Merge remote-tracking branch 'upstream/develop' into remove-india
diff --git a/.eslintrc b/.eslintrc
index 46fb354..276d6ff 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -5,7 +5,7 @@
 		"es6": true
 	},
 	"parserOptions": {
-		"ecmaVersion": 9,
+		"ecmaVersion": 11,
 		"sourceType": "module"
 	},
 	"extends": "eslint:recommended",
diff --git a/.flake8 b/.flake8
index 5735456..4b852ab 100644
--- a/.flake8
+++ b/.flake8
@@ -29,6 +29,9 @@
     B950,
     W191,
     E124, # closing bracket, irritating while writing QB code
+    E131, # continuation line unaligned for hanging indent
+    E123, # closing bracket does not match indentation of opening bracket's line
+    E101, # ensured by use of black
 
 max-line-length = 200
 exclude=.github/helper/semgrep_rules
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 88049be..3bc22af 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -23,3 +23,9 @@
 
 # removing six compatibility layer
 8fe5feb6a4372bf5f2dfaf65fca41bbcc25c8ce7
+
+# bulk format python code with black
+494bd9ef78313436f0424b918f200dab8fc7c20b
+
+# bulk format python code with black
+baec607ff5905b1c67531096a9cf50ec7ff00a5d
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 418bf3c..607e42d 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -12,10 +12,18 @@
 
 1. This tracker should only be used to report bugs and request features / enhancements to ERPNext
     - For questions and general support, checkout the manual https://erpnext.com/docs/user/manual/en or use https://discuss.erpnext.com
-    - For documentation issues, refer to https://github.com/frappe/erpnext_com
 2. Use the search function before creating a new issue. Duplicates will be closed and directed to
    the original discussion.
-3. When making a feature request, make sure to be as verbose as possible. The better you convey your message, the     greater the drive to make it happen.
+3. When making a feature request, make sure to be as verbose as possible. The better you convey your message, the greater the drive to make it happen.
+
+
+Please keep in mind that we get many many requests and we can't possibly work on all of them, we prioritize development based on the goals of the product and organization. Feature requests are still welcome as it helps us in research when we do decide to work on the requested feature. 
+
+If you're in urgent need to a feature, please try the following channels to get paid developments done quickly:
+1. Certified ERPNext partners: https://erpnext.com/partners 
+2. Developer community on ERPNext forums: https://discuss.erpnext.com/c/developers/5 
+3. Telegram group for ERPNext/Frappe development work: https://t.me/erpnext_opps 
+
 -->
 
 **Is your feature request related to a problem? Please describe.**
diff --git a/.github/helper/install.sh b/.github/helper/install.sh
index 859146b..f0f83b0 100644
--- a/.github/helper/install.sh
+++ b/.github/helper/install.sh
@@ -2,9 +2,16 @@
 
 set -e
 
+# Check for merge conflicts before proceeding
+python -m compileall -f "${GITHUB_WORKSPACE}"
+if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}"
+    then echo "Found merge conflicts"
+    exit 1
+fi
+
 cd ~ || exit
 
-sudo apt-get install redis-server libcups2-dev
+sudo apt update && sudo apt install redis-server libcups2-dev
 
 pip install frappe-bench
 
diff --git a/.github/stale.yml b/.github/stale.yml
index 1c2dcf3..da15d32 100644
--- a/.github/stale.yml
+++ b/.github/stale.yml
@@ -24,14 +24,4 @@
     :) Also, even if it is closed, you can always reopen the PR when you're
     ready. Thank you for contributing.
 
-issues:
-  daysUntilStale: 60
-  daysUntilClose: 7
-  exemptLabels:
-    - valid
-    - to-validate
-    - QA
-  markComment: >
-    This issue has been automatically marked as inactive because it has not had
-    recent activity and it wasn't validated by maintainer team. It will be
-    closed within a week if no further activity occurs.
+only: pulls
diff --git a/.github/try-on-f-cloud-button.svg b/.github/try-on-f-cloud-button.svg
index fe0bb2c..2700927 100644
--- a/.github/try-on-f-cloud-button.svg
+++ b/.github/try-on-f-cloud-button.svg
@@ -1,4 +1,4 @@
-<svg width="201" height="60" viewBox="0 0 201 60" fill="none" xmlns="http://www.w3.org/2000/svg">
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="4 2 193 52">
 <g filter="url(#filter0_dd)">
 <rect x="4" y="2" width="193" height="52" rx="6" fill="#2490EF"/>
 <path d="M28 22.2891H32.8786V35.5H36.2088V22.2891H41.0874V19.5H28V22.2891Z" fill="white"/>
diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml
index d05bbbe..2cf4444 100644
--- a/.github/workflows/patch.yml
+++ b/.github/workflows/patch.yml
@@ -4,7 +4,10 @@
   pull_request:
     paths-ignore:
       - '**.js'
+      - '**.css'
       - '**.md'
+      - '**.html'
+      - '**.csv'
   workflow_dispatch:
 
 concurrency:
@@ -112,4 +115,5 @@
           echo "Updating to latest version"
           git -C "apps/frappe" checkout -q -f "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}"
           git -C "apps/erpnext" checkout -q -f "$GITHUB_SHA"
+          bench setup requirements --python
           bench --site test_site migrate
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..5a46002
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,31 @@
+name: Generate Semantic Release
+on:
+  push:
+    branches:
+      - version-13
+jobs:
+  release:
+    name: Release
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout Entire Repository
+        uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+          persist-credentials: false
+      - name: Setup Node.js v14
+        uses: actions/setup-node@v2
+        with:
+          node-version: 14
+      - name: Setup dependencies
+        run: |
+          npm install @semantic-release/git @semantic-release/exec --no-save
+      - name: Create Release
+        env:
+          GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
+          GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
+          GIT_AUTHOR_NAME: "Frappe PR Bot"
+          GIT_AUTHOR_EMAIL: "developers@frappe.io"
+          GIT_COMMITTER_NAME: "Frappe PR Bot"
+          GIT_COMMITTER_EMAIL: "developers@frappe.io"
+        run: npx semantic-release
\ No newline at end of file
diff --git a/.github/workflows/server-tests-mariadb.yml b/.github/workflows/server-tests-mariadb.yml
index 40f9365..cdb6849 100644
--- a/.github/workflows/server-tests-mariadb.yml
+++ b/.github/workflows/server-tests-mariadb.yml
@@ -4,8 +4,10 @@
   pull_request:
     paths-ignore:
       - '**.js'
+      - '**.css'
       - '**.md'
       - '**.html'
+      - '**.csv'
   push:
     branches: [ develop ]
     paths-ignore:
@@ -117,9 +119,25 @@
           ORCHESTRATOR_URL: http://test-orchestrator.frappe.io
 
       - name: Upload coverage data
+        uses: actions/upload-artifact@v3
+        with:
+          name: coverage-${{ matrix.container }}
+          path: /home/runner/frappe-bench/sites/coverage.xml
+
+  coverage:
+    name: Coverage Wrap Up
+    needs: test
+    runs-on: ubuntu-latest
+    steps:
+      - name: Clone
+        uses: actions/checkout@v2
+
+      - name: Download artifacts
+        uses: actions/download-artifact@v3
+
+      - name: Upload coverage data
         uses: codecov/codecov-action@v2
         with:
           name: MariaDB
           fail_ci_if_error: true
-          files: /home/runner/frappe-bench/sites/coverage.xml
           verbose: true
diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml
deleted file mode 100644
index ab6a53b..0000000
--- a/.github/workflows/ui-tests.yml
+++ /dev/null
@@ -1,117 +0,0 @@
-name: UI
-
-on:
-  pull_request:
-    paths-ignore:
-      - '**.md'
-  workflow_dispatch:
-
-concurrency:
-  group: ui-develop-${{ github.event.number }}
-  cancel-in-progress: true
-
-jobs:
-  test:
-    runs-on: ubuntu-latest
-    timeout-minutes: 60
-
-    strategy:
-      fail-fast: false
-
-    name: UI Tests (Cypress)
-
-    services:
-      mysql:
-        image: mariadb:10.3
-        env:
-          MYSQL_ALLOW_EMPTY_PASSWORD: YES
-        ports:
-          - 3306:3306
-        options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
-
-    steps:
-      - name: Clone
-        uses: actions/checkout@v2
-
-      - name: Setup Python
-        uses: actions/setup-python@v2
-        with:
-          python-version: 3.8
-
-      - uses: actions/setup-node@v2
-        with:
-          node-version: 14
-          check-latest: true
-
-      - name: Add to Hosts
-        run: |
-          echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
-
-      - name: Cache pip
-        uses: actions/cache@v2
-        with:
-          path: ~/.cache/pip
-          key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
-          restore-keys: |
-            ${{ runner.os }}-pip-
-            ${{ runner.os }}-
-
-      - name: Cache node modules
-        uses: actions/cache@v2
-        env:
-          cache-name: cache-node-modules
-        with:
-          path: ~/.npm
-          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
-          restore-keys: |
-            ${{ runner.os }}-build-${{ env.cache-name }}-
-            ${{ runner.os }}-build-
-            ${{ runner.os }}-
-
-      - name: Get yarn cache directory path
-        id: yarn-cache-dir-path
-        run: echo "::set-output name=dir::$(yarn cache dir)"
-
-      - uses: actions/cache@v2
-        id: yarn-cache
-        with:
-          path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
-          key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
-          restore-keys: |
-            ${{ runner.os }}-yarn-
-
-      - name: Cache cypress binary
-        uses: actions/cache@v2
-        with:
-          path: ~/.cache
-          key: ${{ runner.os }}-cypress-
-          restore-keys: |
-            ${{ runner.os }}-cypress-
-            ${{ runner.os }}-
-
-      - name: Install
-        run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
-        env:
-          DB: mariadb
-          TYPE: ui
-
-      - name: Site Setup
-        run: cd ~/frappe-bench/ && bench --site test_site execute erpnext.setup.utils.before_tests
-
-      - name: cypress pre-requisites
-        run: cd ~/frappe-bench/apps/frappe && yarn add cypress-file-upload@^5 @testing-library/cypress@^8 --no-lockfile
-
-
-      - name: Build Assets
-        run: cd ~/frappe-bench/ && bench build
-        env:
-          CI: Yes
-
-      - name: UI Tests
-        run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests erpnext --headless
-        env:
-          CYPRESS_RECORD_KEY: 60a8e3bf-08f5-45b1-9269-2b207d7d30cd
-
-      - name: Show bench console if tests failed
-        if: ${{ failure() }}
-        run: cat ~/frappe-bench/bench_run_logs.txt
diff --git a/.mergify.yml b/.mergify.yml
index b7d1df4..d7f82e6 100644
--- a/.mergify.yml
+++ b/.mergify.yml
@@ -7,6 +7,10 @@
           - author!=gavindsouza
           - author!=rohitwaghchaure
           - author!=nabinhait
+          - author!=ankush
+          - author!=deepeshgarg007
+          - author!=mergify[bot]
+
         - or:
           - base=version-13
           - base=version-12
@@ -17,6 +21,16 @@
             @{{author}}, thanks for the contribution, but we do not accept pull requests on a stable branch. Please raise PR on an appropriate hotfix branch.
             https://github.com/frappe/erpnext/wiki/Pull-Request-Checklist#which-branch
 
+  - name: Auto-close PRs on pre-release branch
+    conditions:
+      - base=version-13-pre-release
+    actions:
+      close:
+      comment:
+          message: |
+            @{{author}}, pre-release branch is not maintained anymore. Releases are directly done by merging hotfix branch to stable branches.
+
+
   - name: backport to develop
     conditions:
       - label="backport develop"
@@ -86,3 +100,37 @@
           - version-12-pre-release
         assignees:
           - "{{ author }}"
+
+  - name: Automatic merge on CI success and review
+    conditions:
+      - status-success=linters
+      - status-success=Sider
+      - status-success=Semantic Pull Request
+      - status-success=Patch Test
+      - status-success=Python Unit Tests (1)
+      - status-success=Python Unit Tests (2)
+      - status-success=Python Unit Tests (3)
+      - label!=dont-merge
+      - label!=squash
+      - "#approved-reviews-by>=1"
+    actions:
+      merge:
+        method: merge
+  - name: Automatic squash on CI success and review
+    conditions:
+      - status-success=linters
+      - status-success=Sider
+      - status-success=Patch Test
+      - status-success=Python Unit Tests (1)
+      - status-success=Python Unit Tests (2)
+      - status-success=Python Unit Tests (3)
+      - label!=dont-merge
+      - label=squash
+      - "#approved-reviews-by>=1"
+    actions:
+      merge:
+        method: squash
+        commit_message_template: |
+            {{ title }} (#{{ number }})
+
+            {{ body }}
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index b74d9a6..dc3011f 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -26,12 +26,19 @@
         args: ['--config', '.github/helper/.flake8_strict']
         exclude: ".*setup.py$"
 
+  - repo: https://github.com/adityahase/black
+    rev: 9cb0a69f4d0030cdf687eddf314468b39ed54119
+    hooks:
+      - id: black
+        additional_dependencies: ['click==8.0.4']
+
   - repo: https://github.com/timothycrosley/isort
     rev: 5.9.1
     hooks:
       - id: isort
         exclude: ".*setup.py$"
 
+
 ci:
     autoupdate_schedule: weekly
     skip: []
diff --git a/.releaserc b/.releaserc
new file mode 100644
index 0000000..8a758ed
--- /dev/null
+++ b/.releaserc
@@ -0,0 +1,24 @@
+{
+	"branches": ["version-13"],
+	"plugins": [
+		"@semantic-release/commit-analyzer", {
+			"preset": "angular",
+			"releaseRules": [
+				{"breaking": true, "release": false}
+			]
+		},
+		"@semantic-release/release-notes-generator",
+		[
+			"@semantic-release/exec", {
+				"prepareCmd": 'sed -ir "s/[0-9]*\.[0-9]*\.[0-9]*/${nextRelease.version}/" erpnext/__init__.py'
+			}
+		],
+		[
+			"@semantic-release/git", {
+				"assets": ["erpnext/__init__.py"],
+				"message": "chore(release): Bumped to Version ${nextRelease.version}\n\n${nextRelease.notes}"
+			}
+		],
+		"@semantic-release/github"
+	]
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 9609353..cea3472 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,14 @@
 <div align="center">
-    <img src="https://raw.githubusercontent.com/frappe/erpnext/develop/erpnext/public/images/erpnext-logo.png" height="128">
+    <a href="https://erpnext.com">
+        <img src="https://raw.githubusercontent.com/frappe/erpnext/develop/erpnext/public/images/erpnext-logo.png" height="128">
+    </a>
     <h2>ERPNext</h2>
     <p align="center">
         <p>ERP made simple</p>
     </p>
 
 [![CI](https://github.com/frappe/erpnext/actions/workflows/server-tests.yml/badge.svg?branch=develop)](https://github.com/frappe/erpnext/actions/workflows/server-tests.yml)
+[![UI](https://github.com/erpnext/erpnext_ui_tests/actions/workflows/ui-tests.yml/badge.svg?branch=develop&event=schedule)](https://github.com/erpnext/erpnext_ui_tests/actions/workflows/ui-tests.yml)
 [![Open Source Helpers](https://www.codetriage.com/frappe/erpnext/badges/users.svg)](https://www.codetriage.com/frappe/erpnext)
 [![codecov](https://codecov.io/gh/frappe/erpnext/branch/develop/graph/badge.svg?token=0TwvyUg3I5)](https://codecov.io/gh/frappe/erpnext)
 [![docker pulls](https://img.shields.io/docker/pulls/frappe/erpnext-worker.svg)](https://hub.docker.com/r/frappe/erpnext-worker)
@@ -31,40 +34,39 @@
 1. [Customize ERPNext](https://erpnext.com/docs/user/manual/en/customize-erpnext)
 1. [And More](https://erpnext.com/docs/user/manual/en/)
 
-ERPNext requires MariaDB.
-
 ERPNext is built on the [Frappe Framework](https://github.com/frappe/frappe), a full-stack web app framework built with Python & JavaScript.
 
-- [User Guide](https://erpnext.com/docs/user)
-- [Discussion Forum](https://discuss.erpnext.com/)
+## Installation
 
----
-
-<div align="center">
-    <a href="https://frappecloud.com/deploy?apps=frappe,erpnext&source=erpnext_readme">
+<div align="center" style="max-height: 40px;">
+    <a href="https://frappecloud.com/erpnext/signup">
         <img src=".github/try-on-f-cloud-button.svg" height="40">
     </a>
+    <a href="https://labs.play-with-docker.com/?stack=https://raw.githubusercontent.com/frappe/frappe_docker/main/pwd.yml">
+      <img src="https://raw.githubusercontent.com/play-with-docker/stacks/master/assets/images/button.png" alt="Try in PWD" height="37"/>
+    </a>
 </div>
 
+> Login for the PWD site: (username: Administrator, password: admin)
+
 ### Containerized Installation
 
 Use docker to deploy ERPNext in production or for development of [Frappe](https://github.com/frappe/frappe) apps. See https://github.com/frappe/frappe_docker for more details.
 
-### Full Install
+### Manual Install
 
 The Easy Way: our install script for bench will install all dependencies (e.g. MariaDB). See https://github.com/frappe/bench for more details.
 
 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).
 
----
 
-## License
+## Learning and community
 
-GNU/General Public License (see [license.txt](license.txt))
+1. [Frappe School](https://frappe.school) - Learn Frappe Framework and ERPNext from the various courses by the maintainers or from the community.
+2. [Official documentation](https://docs.erpnext.com/) - Extensive documentation for ERPNext.
+3. [Discussion Forum](https://discuss.erpnext.com/) - Engage with community of ERPNext users and service providers.
+4. [Telegram Group](https://t.me/erpnexthelp) - Get instant help from huge community of users.
 
-The ERPNext code is licensed as GNU General Public License (v3) and the Documentation is licensed as Creative Commons (CC-BY-SA-3.0) and the copyright is owned by Frappe Technologies Pvt Ltd (Frappe) and Contributors.
-
----
 
 ## Contributing
 
@@ -72,49 +74,14 @@
 1. [Report Security Vulnerabilities](https://erpnext.com/security)
 1. [Pull Request Requirements](https://github.com/frappe/erpnext/wiki/Contribution-Guidelines)
 1. [Translations](https://translate.erpnext.com)
-1. [Chart of Accounts](https://charts.erpnext.com)
 
----
 
-## Learning
+## License
 
-1. [Frappe School](https://frappe.school) - Learn Frappe Framework and ERPNext from the various courses by the maintainers or from the community.
+GNU/General Public License (see [license.txt](license.txt))
 
----
+The ERPNext code is licensed as GNU General Public License (v3) and the Documentation is licensed as Creative Commons (CC-BY-SA-3.0) and the copyright is owned by Frappe Technologies Pvt Ltd (Frappe) and Contributors.
 
-## Logo and Trademark
+## Logo and Trademark Policy
 
-The brand name ERPNext and the logo are trademarks of Frappe Technologies Pvt. Ltd.
-
-### Introduction
-
-Frappe Technologies Pvt. Ltd. (Frappe) owns and oversees the trademarks for the ERPNext name and logos. We have developed this trademark usage policy with the following goals in mind:
-
-- We’d like to make it easy for anyone to use the ERPNext name or logo for community-oriented efforts that help spread and improve ERPNext.
-- We’d like to make it clear how ERPNext-related businesses and projects can (and cannot) use the ERPNext name and logo.
-- We’d like to make it hard for anyone to use the ERPNext name and logo to unfairly profit from, trick or confuse people who are looking for official ERPNext resources.
-
-### Frappe Trademark Usage Policy
-
-Permission from Frappe is required to use the ERPNext name or logo as part of any project, product, service, domain or company name.
-
-We will grant permission to use the ERPNext name and logo for projects that meet the following criteria:
-
-- The primary purpose of your project is to promote the spread and improvement of the ERPNext software.
-- Your project is non-commercial in nature (it can make money to cover its costs or contribute to non-profit entities, but it cannot be run as a for-profit project or business).
-Your project neither promotes nor is associated with entities that currently fail to comply with the GPL license under which ERPNext is distributed.
-- If your project meets these criteria, you will be permitted to use the ERPNext name and logo to promote your project in any way you see fit with one exception: Please do not use ERPNext as part of a domain name.
-
-Use of the ERPNext name and logo is additionally allowed in the following situations:
-
-All other ERPNext-related businesses or projects can use the ERPNext name and logo to refer to and explain their services, but they cannot use them as part of a product, project, service, domain, or company name and they cannot use them in any way that suggests an affiliation with or endorsement by ERPNext or Frappe Technologies or the ERPNext open source project. For example, a consulting company can describe its business as “123 Web Services, offering ERPNext consulting for small businesses,” but cannot call its business “The ERPNext Consulting Company.”
-
-Similarly, it’s OK to use the ERPNext logo as part of a page that describes your products or services, but it is not OK to use it as part of your company or product logo or branding itself. Under no circumstances is it permitted to use ERPNext as part of a top-level domain name.
-
-We do not allow the use of the trademark in advertising, including AdSense/AdWords.
-
-Please note that it is not the goal of this policy to limit commercial activity around ERPNext. We encourage ERPNext-based businesses, and we would love to see hundreds of them.
-
-When in doubt about your use of the ERPNext name or logo, please contact Frappe Technologies for clarification.
-
-(inspired by WordPress)
+Please read our [Logo and Trademark Policy](TRADEMARK_POLICY.md).
diff --git a/TRADEMARK_POLICY.md b/TRADEMARK_POLICY.md
new file mode 100644
index 0000000..244c747
--- /dev/null
+++ b/TRADEMARK_POLICY.md
@@ -0,0 +1,36 @@
+## Logo and Trademark Policy
+
+The brand name ERPNext and the logo are trademarks of Frappe Technologies Pvt. Ltd.
+
+### Introduction
+
+Frappe Technologies Pvt. Ltd. (Frappe) owns and oversees the trademarks for the ERPNext name and logos. We have developed this trademark usage policy with the following goals in mind:
+
+- We’d like to make it easy for anyone to use the ERPNext name or logo for community-oriented efforts that help spread and improve ERPNext.
+- We’d like to make it clear how ERPNext-related businesses and projects can (and cannot) use the ERPNext name and logo.
+- We’d like to make it hard for anyone to use the ERPNext name and logo to unfairly profit from, trick or confuse people who are looking for official ERPNext resources.
+
+### Frappe Trademark Usage Policy
+
+Permission from Frappe is required to use the ERPNext name or logo as part of any project, product, service, domain or company name.
+
+We will grant permission to use the ERPNext name and logo for projects that meet the following criteria:
+
+- The primary purpose of your project is to promote the spread and improvement of the ERPNext software.
+- Your project is non-commercial in nature (it can make money to cover its costs or contribute to non-profit entities, but it cannot be run as a for-profit project or business).
+Your project neither promotes nor is associated with entities that currently fail to comply with the GPL license under which ERPNext is distributed.
+- If your project meets these criteria, you will be permitted to use the ERPNext name and logo to promote your project in any way you see fit with one exception: Please do not use ERPNext as part of a domain name.
+
+Use of the ERPNext name and logo is additionally allowed in the following situations:
+
+All other ERPNext-related businesses or projects can use the ERPNext name and logo to refer to and explain their services, but they cannot use them as part of a product, project, service, domain, or company name and they cannot use them in any way that suggests an affiliation with or endorsement by ERPNext or Frappe Technologies or the ERPNext open source project. For example, a consulting company can describe its business as “123 Web Services, offering ERPNext consulting for small businesses,” but cannot call its business “The ERPNext Consulting Company.”
+
+Similarly, it’s OK to use the ERPNext logo as part of a page that describes your products or services, but it is not OK to use it as part of your company or product logo or branding itself. Under no circumstances is it permitted to use ERPNext as part of a top-level domain name.
+
+We do not allow the use of the trademark in advertising, including AdSense/AdWords.
+
+Please note that it is not the goal of this policy to limit commercial activity around ERPNext. We encourage ERPNext-based businesses, and we would love to see hundreds of them.
+
+When in doubt about your use of the ERPNext name or logo, please contact Frappe Technologies for clarification.
+
+(inspired by WordPress)
diff --git a/codecov.yml b/codecov.yml
index 1fa602a..7d9c37d 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -21,7 +21,6 @@
 comment:
   layout: "diff, files"
   require_changes: true
-  after_n_builds: 3
 
 ignore:
   - "erpnext/demo"
diff --git a/cypress.json b/cypress.json
deleted file mode 100644
index 02b10d8..0000000
--- a/cypress.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "baseUrl": "http://test_site:8000/",
-  "projectId": "da59y9",
-  "adminPassword": "admin",
-  "defaultCommandTimeout": 20000,
-  "pageLoadTimeout": 15000,
-  "retries": {
-    "runMode": 2,
-    "openMode": 2
-  }
-}
diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json
deleted file mode 100644
index da18d93..0000000
--- a/cypress/fixtures/example.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "name": "Using fixtures to represent data",
-  "email": "hello@cypress.io",
-  "body": "Fixtures are a great way to mock data for responses to routes"
-}
\ No newline at end of file
diff --git a/cypress/integration/test_bulk_transaction_processing.js b/cypress/integration/test_bulk_transaction_processing.js
deleted file mode 100644
index 428ec51..0000000
--- a/cypress/integration/test_bulk_transaction_processing.js
+++ /dev/null
@@ -1,44 +0,0 @@
-describe("Bulk Transaction Processing", () => {
-	before(() => {
-		cy.login();
-		cy.visit("/app/website");
-	});
-
-	it("Creates To Sales Order", () => {
-		cy.visit("/app/sales-order");
-		cy.url().should("include", "/sales-order");
-		cy.window()
-			.its("frappe.csrf_token")
-			.then((csrf_token) => {
-				return cy
-					.request({
-						url: "/api/method/erpnext.tests.ui_test_bulk_transaction_processing.create_records",
-						method: "POST",
-						headers: {
-							Accept: "application/json",
-							"Content-Type": "application/json",
-							"X-Frappe-CSRF-Token": csrf_token,
-						},
-						timeout: 60000,
-					})
-					.then((res) => {
-						expect(res.status).eq(200);
-					});
-			});
-		cy.wait(5000);
-		cy.get(
-			".list-row-head > .list-header-subject > .list-row-col > .list-check-all"
-		).check({ force: true });
-		cy.wait(3000);
-		cy.get(".actions-btn-group > .btn-primary").click({ force: true });
-		cy.wait(3000);
-		cy.get(".dropdown-menu-right > .user-action > .dropdown-item")
-			.contains("Sales Invoice")
-			.click({ force: true });
-		cy.wait(3000);
-		cy.get(".modal-content > .modal-footer > .standard-actions")
-			.contains("Yes")
-			.click({ force: true });
-		cy.contains("Creation of Sales Invoice successful");
-	});
-});
diff --git a/cypress/integration/test_customer.js b/cypress/integration/test_customer.js
deleted file mode 100644
index 3d6ed5d..0000000
--- a/cypress/integration/test_customer.js
+++ /dev/null
@@ -1,13 +0,0 @@
-
-context('Customer', () => {
-	before(() => {
-		cy.login();
-	});
-	it('Check Customer Group', () => {
-		cy.visit(`app/customer/`);
-		cy.get('.primary-action').click();
-		cy.wait(500);
-		cy.get('.custom-actions > .btn').click();
-		cy.get_field('customer_group', 'Link').should('have.value', 'All Customer Groups');
-	});
-});
diff --git a/cypress/integration/test_item.js b/cypress/integration/test_item.js
deleted file mode 100644
index fcb7533..0000000
--- a/cypress/integration/test_item.js
+++ /dev/null
@@ -1,44 +0,0 @@
-describe("Test Item Dashboard", () => {
-	before(() => {
-		cy.login();
-		cy.visit("/app/item");
-		cy.insert_doc(
-			"Item",
-			{
-				item_code: "e2e_test_item",
-				item_group: "All Item Groups",
-				opening_stock: 42,
-				valuation_rate: 100,
-			},
-			true
-		);
-		cy.go_to_doc("item", "e2e_test_item");
-	});
-
-	it("should show dashboard with correct data on first load", () => {
-		cy.get(".stock-levels").contains("Stock Levels").should("be.visible");
-		cy.get(".stock-levels").contains("e2e_test_item").should("exist");
-
-		// reserved and available qty
-		cy.get(".stock-levels .inline-graph-count")
-			.eq(0)
-			.contains("0")
-			.should("exist");
-		cy.get(".stock-levels .inline-graph-count")
-			.eq(1)
-			.contains("42")
-			.should("exist");
-	});
-
-	it("should persist on field change", () => {
-		cy.get('input[data-fieldname="disabled"]').check();
-		cy.wait(500);
-		cy.get(".stock-levels").contains("Stock Levels").should("be.visible");
-		cy.get(".stock-levels").should("have.length", 1);
-	});
-
-	it("should persist on reload", () => {
-		cy.reload();
-		cy.get(".stock-levels").contains("Stock Levels").should("be.visible");
-	});
-});
diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js
deleted file mode 100644
index 464cce4..0000000
--- a/cypress/integration/test_organizational_chart_desktop.js
+++ /dev/null
@@ -1,116 +0,0 @@
-context('Organizational Chart', () => {
-	before(() => {
-		cy.login();
-		cy.visit('/app/website');
-	});
-
-	it('navigates to org chart', () => {
-		cy.visit('/app');
-		cy.visit('/app/organizational-chart');
-		cy.url().should('include', '/organizational-chart');
-
-		cy.window().its('frappe.csrf_token').then(csrf_token => {
-			return cy.request({
-				url: `/api/method/erpnext.tests.ui_test_helpers.create_employee_records`,
-				method: 'POST',
-				headers: {
-					Accept: 'application/json',
-					'Content-Type': 'application/json',
-					'X-Frappe-CSRF-Token': csrf_token
-				},
-				timeout: 60000
-			}).then(res => {
-				expect(res.status).eq(200);
-				cy.get('.frappe-control[data-fieldname=company] input').focus().as('input');
-				cy.get('@input')
-					.clear({ force: true })
-					.type('Test Org Chart{downarrow}{enter}', { force: true })
-					.blur({ force: true });
-			});
-		});
-	});
-
-	it('renders root nodes and loads children for the first expandable node', () => {
-		// check rendered root nodes and the node name, title, connections
-		cy.get('.hierarchy').find('.root-level ul.node-children').children()
-			.should('have.length', 2)
-			.first()
-			.as('first-child');
-
-		cy.get('@first-child').get('.node-name').contains('Test Employee 1');
-		cy.get('@first-child').get('.node-info').find('.node-title').contains('CEO');
-		cy.get('@first-child').get('.node-info').find('.node-connections').contains('· 2 Connections');
-
-		cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
-			// children of 1st root visible
-			cy.get(`div[data-parent="${employee_records.message[0]}"]`).as('child-node');
-			cy.get('@child-node')
-				.should('have.length', 1)
-				.should('be.visible');
-			cy.get('@child-node').get('.node-name').contains('Test Employee 3');
-
-			// connectors between first root node and immediate child
-			cy.get(`path[data-parent="${employee_records.message[0]}"]`)
-				.should('be.visible')
-				.invoke('attr', 'data-child')
-				.should('equal', employee_records.message[2]);
-		});
-	});
-
-	it('hides active nodes children and connectors on expanding sibling node', () => {
-		cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
-			// click sibling
-			cy.get(`#${employee_records.message[1]}`)
-				.click()
-				.should('have.class', 'active');
-
-			// child nodes and connectors hidden
-			cy.get(`[data-parent="${employee_records.message[0]}"]`).should('not.be.visible');
-			cy.get(`path[data-parent="${employee_records.message[0]}"]`).should('not.be.visible');
-		});
-	});
-
-	it('collapses previous level nodes and refreshes connectors on expanding child node', () => {
-		cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
-			// click child node
-			cy.get(`#${employee_records.message[3]}`)
-				.click()
-				.should('have.class', 'active');
-
-			// previous level nodes: parent should be on active-path; other nodes should be collapsed
-			cy.get(`#${employee_records.message[0]}`).should('have.class', 'collapsed');
-			cy.get(`#${employee_records.message[1]}`).should('have.class', 'active-path');
-
-			// previous level connectors refreshed
-			cy.get(`path[data-parent="${employee_records.message[1]}"]`)
-				.should('have.class', 'collapsed-connector');
-
-			// child node's children and connectors rendered
-			cy.get(`[data-parent="${employee_records.message[3]}"]`).should('be.visible');
-			cy.get(`path[data-parent="${employee_records.message[3]}"]`).should('be.visible');
-		});
-	});
-
-	it('expands previous level nodes', () => {
-		cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
-			cy.get(`#${employee_records.message[0]}`)
-				.click()
-				.should('have.class', 'active');
-
-			cy.get(`[data-parent="${employee_records.message[0]}"]`)
-				.should('be.visible');
-
-			cy.get('ul.hierarchy').children().should('have.length', 2);
-			cy.get(`#connectors`).children().should('have.length', 1);
-		});
-	});
-
-	it('edit node navigates to employee master', () => {
-		cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
-			cy.get(`#${employee_records.message[0]}`).find('.btn-edit-node')
-				.click();
-
-			cy.url().should('include', `/employee/${employee_records.message[0]}`);
-		});
-	});
-});
diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js
deleted file mode 100644
index 971ac6d..0000000
--- a/cypress/integration/test_organizational_chart_mobile.js
+++ /dev/null
@@ -1,195 +0,0 @@
-context('Organizational Chart Mobile', () => {
-	before(() => {
-		cy.login();
-		cy.visit('/app/website');
-	});
-
-	it('navigates to org chart', () => {
-		cy.viewport(375, 667);
-		cy.visit('/app');
-		cy.visit('/app/organizational-chart');
-		cy.url().should('include', '/organizational-chart');
-
-		cy.window().its('frappe.csrf_token').then(csrf_token => {
-			return cy.request({
-				url: `/api/method/erpnext.tests.ui_test_helpers.create_employee_records`,
-				method: 'POST',
-				headers: {
-					Accept: 'application/json',
-					'Content-Type': 'application/json',
-					'X-Frappe-CSRF-Token': csrf_token
-				},
-				timeout: 60000
-			}).then(res => {
-				expect(res.status).eq(200);
-				cy.get('.frappe-control[data-fieldname=company] input').focus().as('input');
-				cy.get('@input')
-					.clear({ force: true })
-					.type('Test Org Chart{downarrow}{enter}', { force: true })
-					.blur({ force: true });
-			});
-		});
-	});
-
-	it('renders root nodes', () => {
-		// check rendered root nodes and the node name, title, connections
-		cy.get('.hierarchy-mobile').find('.root-level').children()
-			.should('have.length', 2)
-			.first()
-			.as('first-child');
-
-		cy.get('@first-child').get('.node-name').contains('Test Employee 1');
-		cy.get('@first-child').get('.node-info').find('.node-title').contains('CEO');
-		cy.get('@first-child').get('.node-info').find('.node-connections').contains('· 2');
-	});
-
-	it('expands root node', () => {
-		cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
-			cy.get(`#${employee_records.message[1]}`)
-				.click()
-				.should('have.class', 'active');
-
-			// other root node removed
-			cy.get(`#${employee_records.message[0]}`).should('not.exist');
-
-			// children of active root node
-			cy.get('.hierarchy-mobile').find('.level').first().find('ul.node-children').children()
-				.should('have.length', 2);
-
-			cy.get(`div[data-parent="${employee_records.message[1]}"]`).first().as('child-node');
-			cy.get('@child-node').should('be.visible');
-
-			cy.get('@child-node')
-				.get('.node-name')
-				.contains('Test Employee 4');
-
-			// connectors between root node and immediate children
-			cy.get(`path[data-parent="${employee_records.message[1]}"]`).as('connectors');
-			cy.get('@connectors')
-				.should('have.length', 2)
-				.should('be.visible');
-
-			cy.get('@connectors')
-				.first()
-				.invoke('attr', 'data-child')
-				.should('eq', employee_records.message[3]);
-		});
-	});
-
-	it('expands child node', () => {
-		cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
-			cy.get(`#${employee_records.message[3]}`)
-				.click()
-				.should('have.class', 'active')
-				.as('expanded_node');
-
-			// 2 levels on screen; 1 on active path; 1 collapsed
-			cy.get('.hierarchy-mobile').children().should('have.length', 2);
-			cy.get(`#${employee_records.message[1]}`).should('have.class', 'active-path');
-
-			// children of expanded node visible
-			cy.get('@expanded_node')
-				.next()
-				.should('have.class', 'node-children')
-				.as('node-children');
-
-			cy.get('@node-children').children().should('have.length', 1);
-			cy.get('@node-children')
-				.first()
-				.get('.node-card')
-				.should('have.class', 'active-child')
-				.contains('Test Employee 7');
-
-			// orphan connectors removed
-			cy.get(`#connectors`).children().should('have.length', 2);
-		});
-	});
-
-	it('renders sibling group', () => {
-		cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
-			// sibling group visible for parent
-			cy.get(`#${employee_records.message[1]}`)
-				.next()
-				.as('sibling_group');
-
-			cy.get('@sibling_group')
-				.should('have.attr', 'data-parent', 'undefined')
-				.should('have.class', 'node-group')
-				.and('have.class', 'collapsed');
-
-			cy.get('@sibling_group').get('.avatar-group').children().as('siblings');
-			cy.get('@siblings').should('have.length', 1);
-			cy.get('@siblings')
-				.first()
-				.should('have.attr', 'title', 'Test Employee 1');
-
-		});
-	});
-
-	it('expands previous level nodes', () => {
-		cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
-			cy.get(`#${employee_records.message[6]}`)
-				.click()
-				.should('have.class', 'active');
-
-			// clicking on previous level node should remove all the nodes ahead
-			// and expand that node
-			cy.get(`#${employee_records.message[3]}`).click();
-			cy.get(`#${employee_records.message[3]}`)
-				.should('have.class', 'active')
-				.should('not.have.class', 'active-path');
-
-			cy.get(`#${employee_records.message[6]}`).should('have.class', 'active-child');
-			cy.get('.hierarchy-mobile').children().should('have.length', 2);
-			cy.get(`#connectors`).children().should('have.length', 2);
-		});
-	});
-
-	it('expands sibling group', () => {
-		cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
-			// sibling group visible for parent
-			cy.get(`#${employee_records.message[6]}`).click();
-
-			cy.get(`#${employee_records.message[3]}`)
-				.next()
-				.click();
-
-			// siblings of parent should be visible
-			cy.get('.hierarchy-mobile').prev().as('sibling_group');
-			cy.get('@sibling_group')
-				.should('exist')
-				.should('have.class', 'sibling-group')
-				.should('not.have.class', 'collapsed');
-
-			cy.get(`#${employee_records.message[1]}`)
-				.should('be.visible')
-				.should('have.class', 'active');
-
-			cy.get(`[data-parent="${employee_records.message[1]}"]`)
-				.should('be.visible')
-				.should('have.length', 2)
-				.should('have.class', 'active-child');
-		});
-	});
-
-	it('goes to the respective level after clicking on non-collapsed sibling group', () => {
-		cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(() => {
-			// click on non-collapsed sibling group
-			cy.get('.hierarchy-mobile')
-				.prev()
-				.click();
-
-			// should take you to that level
-			cy.get('.hierarchy-mobile').find('li.level .node-card').should('have.length', 2);
-		});
-	});
-
-	it('edit node navigates to employee master', () => {
-		cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => {
-			cy.get(`#${employee_records.message[0]}`).find('.btn-edit-node')
-				.click();
-
-			cy.url().should('include', `/employee/${employee_records.message[0]}`);
-		});
-	});
-});
diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js
deleted file mode 100644
index 07d9804..0000000
--- a/cypress/plugins/index.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// ***********************************************************
-// This example plugins/index.js can be used to load plugins
-//
-// You can change the location of this file or turn off loading
-// the plugins file with the 'pluginsFile' configuration option.
-//
-// You can read more here:
-// https://on.cypress.io/plugins-guide
-// ***********************************************************
-
-// This function is called when a project is opened or re-opened (e.g. due to
-// the project's config changing)
-
-module.exports = () => {
-	// `on` is used to hook into various events Cypress emits
-	// `config` is the resolved Cypress config
-};
diff --git a/cypress/support/commands.js b/cypress/support/commands.js
deleted file mode 100644
index 7ddc80a..0000000
--- a/cypress/support/commands.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// ***********************************************
-// This example commands.js shows you how to
-// create various custom commands and overwrite
-// existing commands.
-//
-// For more comprehensive examples of custom
-// commands please read more here:
-// https://on.cypress.io/custom-commands
-// ***********************************************
-//
-//
-// -- This is a parent command --
-// Cypress.Commands.add("login", (email, password) => { ... });
-//
-//
-// -- This is a child command --
-// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... });
-//
-//
-// -- This is a dual command --
-// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... });
-//
-//
-// -- This is will overwrite an existing command --
-// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... });
-
-const slug = (name) => name.toLowerCase().replace(" ", "-");
-
-Cypress.Commands.add("go_to_doc", (doctype, name) => {
-	cy.visit(`/app/${slug(doctype)}/${encodeURIComponent(name)}`);
-});
diff --git a/cypress/support/index.js b/cypress/support/index.js
deleted file mode 100644
index 72070cc..0000000
--- a/cypress/support/index.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// ***********************************************************
-// This example support/index.js is processed and
-// loaded automatically before your test files.
-//
-// This is a great place to put global configuration and
-// behavior that modifies Cypress.
-//
-// You can change the location of this file or turn off
-// automatically serving support files with the
-// 'supportFile' configuration option.
-//
-// You can read more here:
-// https://on.cypress.io/configuration
-// ***********************************************************
-
-// Import commands.js using ES2015 syntax:
-import './commands';
-import '../../../frappe/cypress/support/commands' // eslint-disable-line
-
-
-// Alternatively you can use CommonJS syntax:
-// require('./commands')
-
-Cypress.Cookies.defaults({
-	preserve: 'sid'
-});
diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json
deleted file mode 100644
index d90ebf6..0000000
--- a/cypress/tsconfig.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-    "compilerOptions": {
-        "allowJs": true,
-        "baseUrl": "../node_modules",
-        "types": [
-            "cypress"
-        ]
-    },
-    "include": [
-        "**/*.*"
-    ]
-}
\ No newline at end of file
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index dcfad1f..e0f0c98 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -2,49 +2,57 @@
 
 import frappe
 
-__version__ = '14.0.0-dev'
+__version__ = "14.0.0-dev"
+
 
 def get_default_company(user=None):
-	'''Get default company for user'''
+	"""Get default company for user"""
 	from frappe.defaults import get_user_default_as_list
 
 	if not user:
 		user = frappe.session.user
 
-	companies = get_user_default_as_list(user, 'company')
+	companies = get_user_default_as_list(user, "company")
 	if companies:
 		default_company = companies[0]
 	else:
-		default_company = frappe.db.get_single_value('Global Defaults', 'default_company')
+		default_company = frappe.db.get_single_value("Global Defaults", "default_company")
 
 	return default_company
 
 
 def get_default_currency():
-	'''Returns the currency of the default company'''
+	"""Returns the currency of the default company"""
 	company = get_default_company()
 	if company:
-		return frappe.get_cached_value('Company',  company,  'default_currency')
+		return frappe.get_cached_value("Company", company, "default_currency")
+
 
 def get_default_cost_center(company):
-	'''Returns the default cost center of the company'''
+	"""Returns the default cost center of the company"""
 	if not company:
 		return None
 
 	if not frappe.flags.company_cost_center:
 		frappe.flags.company_cost_center = {}
 	if not company in frappe.flags.company_cost_center:
-		frappe.flags.company_cost_center[company] = frappe.get_cached_value('Company',  company,  'cost_center')
+		frappe.flags.company_cost_center[company] = frappe.get_cached_value(
+			"Company", company, "cost_center"
+		)
 	return frappe.flags.company_cost_center[company]
 
+
 def get_company_currency(company):
-	'''Returns the default company currency'''
+	"""Returns the default company currency"""
 	if not frappe.flags.company_currency:
 		frappe.flags.company_currency = {}
 	if not company in frappe.flags.company_currency:
-		frappe.flags.company_currency[company] = frappe.db.get_value('Company',  company,  'default_currency', cache=True)
+		frappe.flags.company_currency[company] = frappe.db.get_value(
+			"Company", company, "default_currency", cache=True
+		)
 	return frappe.flags.company_currency[company]
 
+
 def set_perpetual_inventory(enable=1, company=None):
 	if not company:
 		company = "_Test Company" if frappe.flags.in_test else get_default_company()
@@ -53,9 +61,10 @@
 	company.enable_perpetual_inventory = enable
 	company.save()
 
+
 def encode_company_abbr(name, company=None, abbr=None):
-	'''Returns name encoded with company abbreviation'''
-	company_abbr = abbr or frappe.get_cached_value('Company',  company,  "abbr")
+	"""Returns name encoded with company abbreviation"""
+	company_abbr = abbr or frappe.get_cached_value("Company", company, "abbr")
 	parts = name.rsplit(" - ", 1)
 
 	if parts[-1].lower() != company_abbr.lower():
@@ -63,62 +72,69 @@
 
 	return " - ".join(parts)
 
+
 def is_perpetual_inventory_enabled(company):
 	if not company:
 		company = "_Test Company" if frappe.flags.in_test else get_default_company()
 
-	if not hasattr(frappe.local, 'enable_perpetual_inventory'):
+	if not hasattr(frappe.local, "enable_perpetual_inventory"):
 		frappe.local.enable_perpetual_inventory = {}
 
 	if not company in frappe.local.enable_perpetual_inventory:
-		frappe.local.enable_perpetual_inventory[company] = frappe.get_cached_value('Company',
-			company,  "enable_perpetual_inventory") or 0
+		frappe.local.enable_perpetual_inventory[company] = (
+			frappe.get_cached_value("Company", company, "enable_perpetual_inventory") or 0
+		)
 
 	return frappe.local.enable_perpetual_inventory[company]
 
+
 def get_default_finance_book(company=None):
 	if not company:
 		company = get_default_company()
 
-	if not hasattr(frappe.local, 'default_finance_book'):
+	if not hasattr(frappe.local, "default_finance_book"):
 		frappe.local.default_finance_book = {}
 
 	if not company in frappe.local.default_finance_book:
-		frappe.local.default_finance_book[company] = frappe.get_cached_value('Company',
-			company,  "default_finance_book")
+		frappe.local.default_finance_book[company] = frappe.get_cached_value(
+			"Company", company, "default_finance_book"
+		)
 
 	return frappe.local.default_finance_book[company]
 
+
 def get_party_account_type(party_type):
-	if not hasattr(frappe.local, 'party_account_types'):
+	if not hasattr(frappe.local, "party_account_types"):
 		frappe.local.party_account_types = {}
 
 	if not party_type in frappe.local.party_account_types:
-		frappe.local.party_account_types[party_type] = frappe.db.get_value("Party Type",
-			party_type, "account_type") or ''
+		frappe.local.party_account_types[party_type] = (
+			frappe.db.get_value("Party Type", party_type, "account_type") or ""
+		)
 
 	return frappe.local.party_account_types[party_type]
 
+
 def get_region(company=None):
-	'''Return the default country based on flag, company or global settings
+	"""Return the default country based on flag, company or global settings
 
 	You can also set global company flag in `frappe.flags.company`
-	'''
+	"""
 	if company or frappe.flags.company:
-		return frappe.get_cached_value('Company',
-			company or frappe.flags.company, 'country')
+		return frappe.get_cached_value("Company", company or frappe.flags.company, "country")
 	elif frappe.flags.country:
 		return frappe.flags.country
 	else:
-		return frappe.get_system_settings('country')
+		return frappe.get_system_settings("country")
+
 
 def allow_regional(fn):
-	'''Decorator to make a function regionally overridable
+	"""Decorator to make a function regionally overridable
 
 	Example:
 	@erpnext.allow_regional
 	def myfunction():
-	  pass'''
+	  pass"""
 
 	def caller(*args, **kwargs):
 		overrides = frappe.get_hooks("regional_overrides", {}).get(get_region())
diff --git a/erpnext/accounts/README.md b/erpnext/accounts/README.md
index da1f201..15f7039 100644
--- a/erpnext/accounts/README.md
+++ b/erpnext/accounts/README.md
@@ -10,4 +10,42 @@
 - Sales Invoice (Itemised)
 - Purchase Invoice (Itemised)
 
-All accounting entries are stored in the `General Ledger`
\ No newline at end of file
+All accounting entries are stored in the `General Ledger`
+
+## Payment Ledger
+Transactions on Receivable and Payable Account types will also be stored in `Payment Ledger`. This is so that payment reconciliation process only requires update on this ledger.
+
+### Key Fields
+| Field                | Description                      |
+|----------------------|----------------------------------|
+| `account_type`       | Receivable/Payable               |
+| `account`            | Accounting head                  |
+| `party`              | Party Name                       |
+| `voucher_no`         | Voucher No                       |
+| `against_voucher_no` | Linked voucher(secondary effect) |
+| `amount`             | can be +ve/-ve                   |
+
+### Design
+`debit` and `credit` have been replaced with `account_type` and `amount`. `against_voucher_no` is populated for all entries. So, outstanding amount can be calculated by summing up amount only using `against_voucher_no`.
+
+Ex:
+1. Consider an invoice for ₹100 and a partial payment of ₹80 against that invoice. Payment Ledger will have following entries.
+
+| voucher_no | against_voucher_no | amount |
+|------------|--------------------|--------|
+| SINV-01    | SINV-01            | 100    |
+| PAY-01     | SINV-01            | -80    |
+
+
+2. Reconcile a Credit Note against an invoice using a Journal Entry
+
+An invoice for ₹100 partially reconciled against a credit of ₹70 using a Journal Entry. Payment Ledger will have the following entries.
+
+| voucher_no | against_voucher_no | amount |
+|------------|--------------------|--------|
+| SINV-01    | SINV-01            | 100    |
+|            |                    |        |
+| CR-NOTE-01 | CR-NOTE-01         | -70    |
+|            |                    |        |
+| JE-01      | CR-NOTE-01         | +70    |
+| JE-01      | SINV-01            | -70    |
diff --git a/erpnext/accounts/custom/address.py b/erpnext/accounts/custom/address.py
index 551048e..775a81f 100644
--- a/erpnext/accounts/custom/address.py
+++ b/erpnext/accounts/custom/address.py
@@ -21,37 +21,39 @@
 		return super(ERPNextAddress, self).link_address()
 
 	def update_compnay_address(self):
-		for link in self.get('links'):
-			if link.link_doctype == 'Company':
+		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"
 		]:
-			frappe.throw(_("Address needs to be linked to a Company. Please add a row for Company in the Links table."),
-				title=_("Company Not Linked"))
+			frappe.throw(
+				_("Address needs to be linked to a Company. Please add a row for Company in the Links table."),
+				title=_("Company Not Linked"),
+			)
 
 	def on_update(self):
 		"""
 		After Address is updated, update the related 'Primary Address' on Customer.
 		"""
 		address_display = get_address_display(self.as_dict())
-		filters = { "customer_primary_address": self.name }
+		filters = {"customer_primary_address": self.name}
 		customers = frappe.db.get_all("Customer", filters=filters, as_list=True)
 		for customer_name in customers:
 			frappe.db.set_value("Customer", customer_name[0], "primary_address", address_display)
 
+
 @frappe.whitelist()
-def get_shipping_address(company, address = None):
+def get_shipping_address(company, address=None):
 	filters = [
 		["Dynamic Link", "link_doctype", "=", "Company"],
 		["Dynamic Link", "link_name", "=", company],
-		["Address", "is_your_company_address", "=", 1]
+		["Address", "is_your_company_address", "=", 1],
 	]
 	fields = ["*"]
-	if address and frappe.db.get_value('Dynamic Link',
-		{'parent': address, 'link_name': company}):
+	if address and frappe.db.get_value("Dynamic Link", {"parent": address, "link_name": company}):
 		filters.append(["Address", "name", "=", address])
 	if not address:
 		filters.append(["Address", "is_shipping_address", "=", 1])
diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
index 1c1364e..fefec0e 100644
--- a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
+++ b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
@@ -12,15 +12,24 @@
 
 @frappe.whitelist()
 @cache_source
-def get(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None,
-	to_date = None, timespan = None, time_interval = None, heatmap_year = None):
+def get(
+	chart_name=None,
+	chart=None,
+	no_cache=None,
+	filters=None,
+	from_date=None,
+	to_date=None,
+	timespan=None,
+	time_interval=None,
+	heatmap_year=None,
+):
 	if chart_name:
-		chart = frappe.get_doc('Dashboard Chart', chart_name)
+		chart = frappe.get_doc("Dashboard Chart", chart_name)
 	else:
 		chart = frappe._dict(frappe.parse_json(chart))
 	timespan = chart.timespan
 
-	if chart.timespan == 'Select Date Range':
+	if chart.timespan == "Select Date Range":
 		from_date = chart.from_date
 		to_date = chart.to_date
 
@@ -31,17 +40,23 @@
 	company = filters.get("company")
 
 	if not account and chart_name:
-		frappe.throw(_("Account is not set for the dashboard chart {0}")
-			.format(get_link_to_form("Dashboard Chart", chart_name)))
+		frappe.throw(
+			_("Account is not set for the dashboard chart {0}").format(
+				get_link_to_form("Dashboard Chart", chart_name)
+			)
+		)
 
 	if not frappe.db.exists("Account", account) and chart_name:
-		frappe.throw(_("Account {0} does not exists in the dashboard chart {1}")
-			.format(account, get_link_to_form("Dashboard Chart", chart_name)))
+		frappe.throw(
+			_("Account {0} does not exists in the dashboard chart {1}").format(
+				account, get_link_to_form("Dashboard Chart", chart_name)
+			)
+		)
 
 	if not to_date:
 		to_date = nowdate()
 	if not from_date:
-		if timegrain in ('Monthly', 'Quarterly'):
+		if timegrain in ("Monthly", "Quarterly"):
 			from_date = get_from_date_from_timespan(to_date, timespan)
 
 	# fetch dates to plot
@@ -54,16 +69,14 @@
 	result = build_result(account, dates, gl_entries)
 
 	return {
-		"labels": [formatdate(r[0].strftime('%Y-%m-%d')) for r in result],
-		"datasets": [{
-			"name": account,
-			"values": [r[1] for r in result]
-		}]
+		"labels": [formatdate(r[0].strftime("%Y-%m-%d")) for r in result],
+		"datasets": [{"name": account, "values": [r[1] for r in result]}],
 	}
 
+
 def build_result(account, dates, gl_entries):
 	result = [[getdate(date), 0.0] for date in dates]
-	root_type = frappe.db.get_value('Account', account, 'root_type')
+	root_type = frappe.db.get_value("Account", account, "root_type")
 
 	# start with the first date
 	date_index = 0
@@ -78,30 +91,34 @@
 		result[date_index][1] += entry.debit - entry.credit
 
 	# if account type is credit, switch balances
-	if root_type not in ('Asset', 'Expense'):
+	if root_type not in ("Asset", "Expense"):
 		for r in result:
 			r[1] = -1 * r[1]
 
 	# for balance sheet accounts, the totals are cumulative
-	if root_type in ('Asset', 'Liability', 'Equity'):
+	if root_type in ("Asset", "Liability", "Equity"):
 		for i, r in enumerate(result):
 			if i > 0:
-				r[1] = r[1] + result[i-1][1]
+				r[1] = r[1] + result[i - 1][1]
 
 	return result
 
+
 def get_gl_entries(account, to_date):
-	child_accounts = get_descendants_of('Account', account, ignore_permissions=True)
+	child_accounts = get_descendants_of("Account", account, ignore_permissions=True)
 	child_accounts.append(account)
 
-	return frappe.db.get_all('GL Entry',
-		fields = ['posting_date', 'debit', 'credit'],
-		filters = [
-			dict(posting_date = ('<', to_date)),
-			dict(account = ('in', child_accounts)),
-			dict(voucher_type = ('!=', 'Period Closing Voucher'))
+	return frappe.db.get_all(
+		"GL Entry",
+		fields=["posting_date", "debit", "credit"],
+		filters=[
+			dict(posting_date=("<", to_date)),
+			dict(account=("in", child_accounts)),
+			dict(voucher_type=("!=", "Period Closing Voucher")),
 		],
-		order_by = 'posting_date asc')
+		order_by="posting_date asc",
+	)
+
 
 def get_dates_from_timegrain(from_date, to_date, timegrain):
 	days = months = years = 0
@@ -116,6 +133,8 @@
 
 	dates = [get_period_ending(from_date, timegrain)]
 	while getdate(dates[-1]) < getdate(to_date):
-		date = get_period_ending(add_to_date(dates[-1], years=years, months=months, days=days), timegrain)
+		date = get_period_ending(
+			add_to_date(dates[-1], years=years, months=months, days=days), timegrain
+		)
 		dates.append(date)
 	return dates
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
index ab1061b..f319003 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -22,20 +22,23 @@
 
 
 def validate_service_stop_date(doc):
-	''' Validates service_stop_date for Purchase Invoice and Sales Invoice '''
+	"""Validates service_stop_date for Purchase Invoice and Sales Invoice"""
 
-	enable_check = "enable_deferred_revenue" \
-		if doc.doctype=="Sales Invoice" else "enable_deferred_expense"
+	enable_check = (
+		"enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
+	)
 
 	old_stop_dates = {}
-	old_doc = frappe.db.get_all("{0} Item".format(doc.doctype),
-		{"parent": doc.name}, ["name", "service_stop_date"])
+	old_doc = frappe.db.get_all(
+		"{0} Item".format(doc.doctype), {"parent": doc.name}, ["name", "service_stop_date"]
+	)
 
 	for d in old_doc:
 		old_stop_dates[d.name] = d.service_stop_date or ""
 
 	for item in doc.items:
-		if not item.get(enable_check): continue
+		if not item.get(enable_check):
+			continue
 
 		if item.service_stop_date:
 			if date_diff(item.service_stop_date, item.service_start_date) < 0:
@@ -44,21 +47,31 @@
 			if date_diff(item.service_stop_date, item.service_end_date) > 0:
 				frappe.throw(_("Service Stop Date cannot be after Service End Date"))
 
-		if old_stop_dates and old_stop_dates.get(item.name) and item.service_stop_date!=old_stop_dates.get(item.name):
+		if (
+			old_stop_dates
+			and old_stop_dates.get(item.name)
+			and item.service_stop_date != old_stop_dates.get(item.name)
+		):
 			frappe.throw(_("Cannot change Service Stop Date for item in row {0}").format(item.idx))
 
+
 def build_conditions(process_type, account, company):
-	conditions=''
-	deferred_account = "item.deferred_revenue_account" if process_type=="Income" else "item.deferred_expense_account"
+	conditions = ""
+	deferred_account = (
+		"item.deferred_revenue_account" if process_type == "Income" else "item.deferred_expense_account"
+	)
 
 	if account:
-		conditions += "AND %s='%s'"%(deferred_account, account)
+		conditions += "AND %s='%s'" % (deferred_account, account)
 	elif company:
 		conditions += f"AND p.company = {frappe.db.escape(company)}"
 
 	return conditions
 
-def convert_deferred_expense_to_expense(deferred_process, start_date=None, end_date=None, conditions=''):
+
+def convert_deferred_expense_to_expense(
+	deferred_process, start_date=None, end_date=None, conditions=""
+):
 	# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
 
 	if not start_date:
@@ -67,14 +80,19 @@
 		end_date = add_days(today(), -1)
 
 	# check for the purchase invoice for which GL entries has to be done
-	invoices = frappe.db.sql_list('''
+	invoices = frappe.db.sql_list(
+		"""
 		select distinct item.parent
 		from `tabPurchase Invoice Item` item, `tabPurchase Invoice` p
 		where item.service_start_date<=%s and item.service_end_date>=%s
 		and item.enable_deferred_expense = 1 and item.parent=p.name
 		and item.docstatus = 1 and ifnull(item.amount, 0) > 0
 		{0}
-	'''.format(conditions), (end_date, start_date)) #nosec
+	""".format(
+			conditions
+		),
+		(end_date, start_date),
+	)  # nosec
 
 	# For each invoice, book deferred expense
 	for invoice in invoices:
@@ -84,7 +102,10 @@
 	if frappe.flags.deferred_accounting_error:
 		send_mail(deferred_process)
 
-def convert_deferred_revenue_to_income(deferred_process, start_date=None, end_date=None, conditions=''):
+
+def convert_deferred_revenue_to_income(
+	deferred_process, start_date=None, end_date=None, conditions=""
+):
 	# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
 
 	if not start_date:
@@ -93,14 +114,19 @@
 		end_date = add_days(today(), -1)
 
 	# check for the sales invoice for which GL entries has to be done
-	invoices = frappe.db.sql_list('''
+	invoices = frappe.db.sql_list(
+		"""
 		select distinct item.parent
 		from `tabSales Invoice Item` item, `tabSales Invoice` p
 		where item.service_start_date<=%s and item.service_end_date>=%s
 		and item.enable_deferred_revenue = 1 and item.parent=p.name
 		and item.docstatus = 1 and ifnull(item.amount, 0) > 0
 		{0}
-	'''.format(conditions), (end_date, start_date)) #nosec
+	""".format(
+			conditions
+		),
+		(end_date, start_date),
+	)  # nosec
 
 	for invoice in invoices:
 		doc = frappe.get_doc("Sales Invoice", invoice)
@@ -109,31 +135,43 @@
 	if frappe.flags.deferred_accounting_error:
 		send_mail(deferred_process)
 
+
 def get_booking_dates(doc, item, posting_date=None):
 	if not posting_date:
 		posting_date = add_days(today(), -1)
 
 	last_gl_entry = False
 
-	deferred_account = "deferred_revenue_account" if doc.doctype=="Sales Invoice" else "deferred_expense_account"
+	deferred_account = (
+		"deferred_revenue_account" if doc.doctype == "Sales Invoice" else "deferred_expense_account"
+	)
 
-	prev_gl_entry = frappe.db.sql('''
+	prev_gl_entry = frappe.db.sql(
+		"""
 		select name, posting_date from `tabGL Entry` where company=%s and account=%s and
 		voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
 		and is_cancelled = 0
 		order by posting_date desc limit 1
-	''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
+	""",
+		(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
+		as_dict=True,
+	)
 
-	prev_gl_via_je = frappe.db.sql('''
+	prev_gl_via_je = frappe.db.sql(
+		"""
 		SELECT p.name, p.posting_date FROM `tabJournal Entry` p, `tabJournal Entry Account` c
 		WHERE p.name = c.parent and p.company=%s and c.account=%s
 		and c.reference_type=%s and c.reference_name=%s
 		and c.reference_detail_no=%s and c.docstatus < 2 order by posting_date desc limit 1
-	''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
+	""",
+		(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
+		as_dict=True,
+	)
 
 	if prev_gl_via_je:
-		if (not prev_gl_entry) or (prev_gl_entry and
-			prev_gl_entry[0].posting_date < prev_gl_via_je[0].posting_date):
+		if (not prev_gl_entry) or (
+			prev_gl_entry and prev_gl_entry[0].posting_date < prev_gl_via_je[0].posting_date
+		):
 			prev_gl_entry = prev_gl_via_je
 
 	if prev_gl_entry:
@@ -157,66 +195,94 @@
 	else:
 		return None, None, None
 
-def calculate_monthly_amount(doc, item, last_gl_entry, start_date, end_date, total_days, total_booking_days, account_currency):
+
+def calculate_monthly_amount(
+	doc, item, last_gl_entry, start_date, end_date, total_days, total_booking_days, account_currency
+):
 	amount, base_amount = 0, 0
 
 	if not last_gl_entry:
-		total_months = (item.service_end_date.year - item.service_start_date.year) * 12 + \
-			(item.service_end_date.month - item.service_start_date.month) + 1
+		total_months = (
+			(item.service_end_date.year - item.service_start_date.year) * 12
+			+ (item.service_end_date.month - item.service_start_date.month)
+			+ 1
+		)
 
-		prorate_factor = flt(date_diff(item.service_end_date, item.service_start_date)) \
-			/ flt(date_diff(get_last_day(item.service_end_date), get_first_day(item.service_start_date)))
+		prorate_factor = flt(date_diff(item.service_end_date, item.service_start_date)) / flt(
+			date_diff(get_last_day(item.service_end_date), get_first_day(item.service_start_date))
+		)
 
 		actual_months = rounded(total_months * prorate_factor, 1)
 
-		already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item)
+		already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
+			doc, item
+		)
 		base_amount = flt(item.base_net_amount / actual_months, item.precision("base_net_amount"))
 
 		if base_amount + already_booked_amount > item.base_net_amount:
 			base_amount = item.base_net_amount - already_booked_amount
 
-		if account_currency==doc.company_currency:
+		if account_currency == doc.company_currency:
 			amount = base_amount
 		else:
-			amount = flt(item.net_amount/actual_months, item.precision("net_amount"))
+			amount = flt(item.net_amount / actual_months, item.precision("net_amount"))
 			if amount + already_booked_amount_in_account_currency > item.net_amount:
 				amount = item.net_amount - already_booked_amount_in_account_currency
 
 		if not (get_first_day(start_date) == start_date and get_last_day(end_date) == end_date):
-			partial_month = flt(date_diff(end_date, start_date)) \
-				/ flt(date_diff(get_last_day(end_date), get_first_day(start_date)))
+			partial_month = flt(date_diff(end_date, start_date)) / flt(
+				date_diff(get_last_day(end_date), get_first_day(start_date))
+			)
 
 			base_amount = rounded(partial_month, 1) * base_amount
 			amount = rounded(partial_month, 1) * amount
 	else:
-		already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item)
-		base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
-		if account_currency==doc.company_currency:
+		already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
+			doc, item
+		)
+		base_amount = flt(
+			item.base_net_amount - already_booked_amount, item.precision("base_net_amount")
+		)
+		if account_currency == doc.company_currency:
 			amount = base_amount
 		else:
-			amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount"))
+			amount = flt(
+				item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount")
+			)
 
 	return amount, base_amount
 
+
 def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency):
 	amount, base_amount = 0, 0
 	if not last_gl_entry:
-		base_amount = flt(item.base_net_amount*total_booking_days/flt(total_days), item.precision("base_net_amount"))
-		if account_currency==doc.company_currency:
+		base_amount = flt(
+			item.base_net_amount * total_booking_days / flt(total_days), item.precision("base_net_amount")
+		)
+		if account_currency == doc.company_currency:
 			amount = base_amount
 		else:
-			amount = flt(item.net_amount*total_booking_days/flt(total_days), item.precision("net_amount"))
+			amount = flt(
+				item.net_amount * total_booking_days / flt(total_days), item.precision("net_amount")
+			)
 	else:
-		already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item)
+		already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
+			doc, item
+		)
 
-		base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
-		if account_currency==doc.company_currency:
+		base_amount = flt(
+			item.base_net_amount - already_booked_amount, item.precision("base_net_amount")
+		)
+		if account_currency == doc.company_currency:
 			amount = base_amount
 		else:
-			amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount"))
+			amount = flt(
+				item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount")
+			)
 
 	return amount, base_amount
 
+
 def get_already_booked_amount(doc, item):
 	if doc.doctype == "Sales Invoice":
 		total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency"
@@ -225,21 +291,31 @@
 		total_credit_debit, total_credit_debit_currency = "credit", "credit_in_account_currency"
 		deferred_account = "deferred_expense_account"
 
-	gl_entries_details = frappe.db.sql('''
+	gl_entries_details = frappe.db.sql(
+		"""
 		select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no
 		from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
 		and is_cancelled = 0
 		group by voucher_detail_no
-	'''.format(total_credit_debit, total_credit_debit_currency),
-		(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
+	""".format(
+			total_credit_debit, total_credit_debit_currency
+		),
+		(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
+		as_dict=True,
+	)
 
-	journal_entry_details = frappe.db.sql('''
+	journal_entry_details = frappe.db.sql(
+		"""
 		SELECT sum(c.{0}) as total_credit, sum(c.{1}) as total_credit_in_account_currency, reference_detail_no
 		FROM `tabJournal Entry` p , `tabJournal Entry Account` c WHERE p.name = c.parent and
 		p.company = %s and c.account=%s and c.reference_type=%s and c.reference_name=%s and c.reference_detail_no=%s
 		and p.docstatus < 2 group by reference_detail_no
-	'''.format(total_credit_debit, total_credit_debit_currency),
-		(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
+	""".format(
+			total_credit_debit, total_credit_debit_currency
+		),
+		(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
+		as_dict=True,
+	)
 
 	already_booked_amount = gl_entries_details[0].total_credit if gl_entries_details else 0
 	already_booked_amount += journal_entry_details[0].total_credit if journal_entry_details else 0
@@ -247,20 +323,29 @@
 	if doc.currency == doc.company_currency:
 		already_booked_amount_in_account_currency = already_booked_amount
 	else:
-		already_booked_amount_in_account_currency = gl_entries_details[0].total_credit_in_account_currency if gl_entries_details else 0
-		already_booked_amount_in_account_currency += journal_entry_details[0].total_credit_in_account_currency if journal_entry_details else 0
+		already_booked_amount_in_account_currency = (
+			gl_entries_details[0].total_credit_in_account_currency if gl_entries_details else 0
+		)
+		already_booked_amount_in_account_currency += (
+			journal_entry_details[0].total_credit_in_account_currency if journal_entry_details else 0
+		)
 
 	return already_booked_amount, already_booked_amount_in_account_currency
 
+
 def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
-	enable_check = "enable_deferred_revenue" \
-		if doc.doctype=="Sales Invoice" else "enable_deferred_expense"
+	enable_check = (
+		"enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
+	)
 
-	accounts_frozen_upto = frappe.get_cached_value('Accounts Settings', 'None', 'acc_frozen_upto')
+	accounts_frozen_upto = frappe.get_cached_value("Accounts Settings", "None", "acc_frozen_upto")
 
-	def _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on):
+	def _book_deferred_revenue_or_expense(
+		item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on
+	):
 		start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date)
-		if not (start_date and end_date): return
+		if not (start_date and end_date):
+			return
 
 		account_currency = get_account_currency(item.expense_account or item.income_account)
 		if doc.doctype == "Sales Invoice":
@@ -273,12 +358,21 @@
 		total_days = date_diff(item.service_end_date, item.service_start_date) + 1
 		total_booking_days = date_diff(end_date, start_date) + 1
 
-		if book_deferred_entries_based_on == 'Months':
-			amount, base_amount = calculate_monthly_amount(doc, item, last_gl_entry,
-				start_date, end_date, total_days, total_booking_days, account_currency)
+		if book_deferred_entries_based_on == "Months":
+			amount, base_amount = calculate_monthly_amount(
+				doc,
+				item,
+				last_gl_entry,
+				start_date,
+				end_date,
+				total_days,
+				total_booking_days,
+				account_currency,
+			)
 		else:
-			amount, base_amount = calculate_amount(doc, item, last_gl_entry,
-				total_days, total_booking_days, account_currency)
+			amount, base_amount = calculate_amount(
+				doc, item, last_gl_entry, total_days, total_booking_days, account_currency
+			)
 
 		if not amount:
 			return
@@ -288,92 +382,155 @@
 			end_date = get_last_day(add_days(accounts_frozen_upto, 1))
 
 		if via_journal_entry:
-			book_revenue_via_journal_entry(doc, credit_account, debit_account, against, amount,
-				base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process, submit_journal_entry)
+			book_revenue_via_journal_entry(
+				doc,
+				credit_account,
+				debit_account,
+				amount,
+				base_amount,
+				end_date,
+				project,
+				account_currency,
+				item.cost_center,
+				item,
+				deferred_process,
+				submit_journal_entry,
+			)
 		else:
-			make_gl_entries(doc, credit_account, debit_account, against,
-				amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process)
+			make_gl_entries(
+				doc,
+				credit_account,
+				debit_account,
+				against,
+				amount,
+				base_amount,
+				end_date,
+				project,
+				account_currency,
+				item.cost_center,
+				item,
+				deferred_process,
+			)
 
 		# Returned in case of any errors because it tries to submit the same record again and again in case of errors
 		if frappe.flags.deferred_accounting_error:
 			return
 
 		if getdate(end_date) < getdate(posting_date) and not last_gl_entry:
-			_book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on)
+			_book_deferred_revenue_or_expense(
+				item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on
+			)
 
-	via_journal_entry = cint(frappe.db.get_singles_value('Accounts Settings', 'book_deferred_entries_via_journal_entry'))
-	submit_journal_entry = cint(frappe.db.get_singles_value('Accounts Settings', 'submit_journal_entries'))
-	book_deferred_entries_based_on = frappe.db.get_singles_value('Accounts Settings', 'book_deferred_entries_based_on')
+	via_journal_entry = cint(
+		frappe.db.get_singles_value("Accounts Settings", "book_deferred_entries_via_journal_entry")
+	)
+	submit_journal_entry = cint(
+		frappe.db.get_singles_value("Accounts Settings", "submit_journal_entries")
+	)
+	book_deferred_entries_based_on = frappe.db.get_singles_value(
+		"Accounts Settings", "book_deferred_entries_based_on"
+	)
 
-	for item in doc.get('items'):
+	for item in doc.get("items"):
 		if item.get(enable_check):
-			_book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on)
+			_book_deferred_revenue_or_expense(
+				item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on
+			)
+
 
 def process_deferred_accounting(posting_date=None):
-	''' Converts deferred income/expense into income/expense
-		Executed via background jobs on every month end '''
+	"""Converts deferred income/expense into income/expense
+	Executed via background jobs on every month end"""
 
 	if not posting_date:
 		posting_date = today()
 
-	if not cint(frappe.db.get_singles_value('Accounts Settings', 'automatically_process_deferred_accounting_entry')):
+	if not cint(
+		frappe.db.get_singles_value(
+			"Accounts Settings", "automatically_process_deferred_accounting_entry"
+		)
+	):
 		return
 
 	start_date = add_months(today(), -1)
 	end_date = add_days(today(), -1)
 
-	companies = frappe.get_all('Company')
+	companies = frappe.get_all("Company")
 
 	for company in companies:
-		for record_type in ('Income', 'Expense'):
-			doc = frappe.get_doc(dict(
-				doctype='Process Deferred Accounting',
-				company=company.name,
-				posting_date=posting_date,
-				start_date=start_date,
-				end_date=end_date,
-				type=record_type
-			))
+		for record_type in ("Income", "Expense"):
+			doc = frappe.get_doc(
+				dict(
+					doctype="Process Deferred Accounting",
+					company=company.name,
+					posting_date=posting_date,
+					start_date=start_date,
+					end_date=end_date,
+					type=record_type,
+				)
+			)
 
 			doc.insert()
 			doc.submit()
 
-def make_gl_entries(doc, credit_account, debit_account, against,
-	amount, base_amount, posting_date, project, account_currency, cost_center, item, deferred_process=None):
+
+def make_gl_entries(
+	doc,
+	credit_account,
+	debit_account,
+	against,
+	amount,
+	base_amount,
+	posting_date,
+	project,
+	account_currency,
+	cost_center,
+	item,
+	deferred_process=None,
+):
 	# GL Entry for crediting the amount in the deferred expense
 	from erpnext.accounts.general_ledger import make_gl_entries
 
-	if amount == 0: return
+	if amount == 0:
+		return
 
 	gl_entries = []
 	gl_entries.append(
-		doc.get_gl_dict({
-			"account": credit_account,
-			"against": against,
-			"credit": base_amount,
-			"credit_in_account_currency": amount,
-			"cost_center": cost_center,
-			"voucher_detail_no": item.name,
-			'posting_date': posting_date,
-			'project': project,
-			'against_voucher_type': 'Process Deferred Accounting',
-			'against_voucher': deferred_process
-		}, account_currency, item=item)
+		doc.get_gl_dict(
+			{
+				"account": credit_account,
+				"against": against,
+				"credit": base_amount,
+				"credit_in_account_currency": amount,
+				"cost_center": cost_center,
+				"voucher_detail_no": item.name,
+				"posting_date": posting_date,
+				"project": project,
+				"against_voucher_type": "Process Deferred Accounting",
+				"against_voucher": deferred_process,
+			},
+			account_currency,
+			item=item,
+		)
 	)
 	# GL Entry to debit the amount from the expense
 	gl_entries.append(
-		doc.get_gl_dict({
-			"account": debit_account,
-			"against": against,
-			"debit": base_amount,
-			"debit_in_account_currency": amount,
-			"cost_center": cost_center,
-			"voucher_detail_no": item.name,
-			'posting_date': posting_date,
-			'project': project,
-			'against_voucher_type': 'Process Deferred Accounting',
-			'against_voucher': deferred_process
-		}, account_currency, item=item)
+		doc.get_gl_dict(
+			{
+				"account": debit_account,
+				"against": against,
+				"debit": base_amount,
+				"debit_in_account_currency": amount,
+				"cost_center": cost_center,
+				"voucher_detail_no": item.name,
+				"posting_date": posting_date,
+				"project": project,
+				"against_voucher_type": "Process Deferred Accounting",
+				"against_voucher": deferred_process,
+			},
+			account_currency,
+			item=item,
+		)
 	)
 
 	if gl_entries:
@@ -382,69 +539,81 @@
 			frappe.db.commit()
 		except Exception as e:
 			if frappe.flags.in_test:
-				traceback = frappe.get_traceback()
-				frappe.log_error(title=_('Error while processing deferred accounting for Invoice {0}').format(doc.name), message=traceback)
+				doc.log_error(f"Error while processing deferred accounting for Invoice {doc.name}")
 				raise e
 			else:
 				frappe.db.rollback()
-				traceback = frappe.get_traceback()
-				frappe.log_error(title=_('Error while processing deferred accounting for Invoice {0}').format(doc.name), message=traceback)
+				doc.log_error(f"Error while processing deferred accounting for Invoice {doc.name}")
 				frappe.flags.deferred_accounting_error = True
 
+
 def send_mail(deferred_process):
 	title = _("Error while processing deferred accounting for {0}").format(deferred_process)
-	link = get_link_to_form('Process Deferred Accounting', deferred_process)
+	link = get_link_to_form("Process Deferred Accounting", deferred_process)
 	content = _("Deferred accounting failed for some invoices:") + "\n"
-	content += _("Please check Process Deferred Accounting {0} and submit manually after resolving errors.").format(link)
+	content += _(
+		"Please check Process Deferred Accounting {0} and submit manually after resolving errors."
+	).format(link)
 	sendmail_to_system_managers(title, content)
 
-def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
-	amount, base_amount, posting_date, project, account_currency, cost_center, item,
-	deferred_process=None, submit='No'):
 
-	if amount == 0: return
+def book_revenue_via_journal_entry(
+	doc,
+	credit_account,
+	debit_account,
+	amount,
+	base_amount,
+	posting_date,
+	project,
+	account_currency,
+	cost_center,
+	item,
+	deferred_process=None,
+	submit="No",
+):
 
-	journal_entry = frappe.new_doc('Journal Entry')
+	if amount == 0:
+		return
+
+	journal_entry = frappe.new_doc("Journal Entry")
 	journal_entry.posting_date = posting_date
 	journal_entry.company = doc.company
-	journal_entry.voucher_type = 'Deferred Revenue' if doc.doctype == 'Sales Invoice' \
-		else 'Deferred Expense'
+	journal_entry.voucher_type = (
+		"Deferred Revenue" if doc.doctype == "Sales Invoice" else "Deferred Expense"
+	)
+	journal_entry.process_deferred_accounting = deferred_process
 
 	debit_entry = {
-		'account': credit_account,
-		'credit': base_amount,
-		'credit_in_account_currency': amount,
-		'account_currency': account_currency,
-		'reference_name': doc.name,
-		'reference_type': doc.doctype,
-		'reference_detail_no': item.name,
-		'cost_center': cost_center,
-		'project': project,
+		"account": credit_account,
+		"credit": base_amount,
+		"credit_in_account_currency": amount,
+		"account_currency": account_currency,
+		"reference_name": doc.name,
+		"reference_type": doc.doctype,
+		"reference_detail_no": item.name,
+		"cost_center": cost_center,
+		"project": project,
 	}
 
 	credit_entry = {
-		'account': debit_account,
-		'debit': base_amount,
-		'debit_in_account_currency': amount,
-		'account_currency': account_currency,
-		'reference_name': doc.name,
-		'reference_type': doc.doctype,
-		'reference_detail_no': item.name,
-		'cost_center': cost_center,
-		'project': project,
+		"account": debit_account,
+		"debit": base_amount,
+		"debit_in_account_currency": amount,
+		"account_currency": account_currency,
+		"reference_name": doc.name,
+		"reference_type": doc.doctype,
+		"reference_detail_no": item.name,
+		"cost_center": cost_center,
+		"project": project,
 	}
 
 	for dimension in get_accounting_dimensions():
-		debit_entry.update({
-			dimension: item.get(dimension)
-		})
+		debit_entry.update({dimension: item.get(dimension)})
 
-		credit_entry.update({
-			dimension: item.get(dimension)
-		})
+		credit_entry.update({dimension: item.get(dimension)})
 
-	journal_entry.append('accounts', debit_entry)
-	journal_entry.append('accounts', credit_entry)
+	journal_entry.append("accounts", debit_entry)
+	journal_entry.append("accounts", credit_entry)
 
 	try:
 		journal_entry.save()
@@ -455,21 +624,26 @@
 		frappe.db.commit()
 	except Exception:
 		frappe.db.rollback()
-		traceback = frappe.get_traceback()
-		frappe.log_error(title=_('Error while processing deferred accounting for Invoice {0}').format(doc.name), message=traceback)
-
+		doc.log_error(f"Error while processing deferred accounting for Invoice {doc.name}")
 		frappe.flags.deferred_accounting_error = True
 
+
 def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr):
 
-	if doctype == 'Sales Invoice':
-		credit_account, debit_account = frappe.db.get_value('Sales Invoice Item', {'name': voucher_detail_no},
-			['income_account', 'deferred_revenue_account'])
+	if doctype == "Sales Invoice":
+		credit_account, debit_account = frappe.db.get_value(
+			"Sales Invoice Item",
+			{"name": voucher_detail_no},
+			["income_account", "deferred_revenue_account"],
+		)
 	else:
-		credit_account, debit_account = frappe.db.get_value('Purchase Invoice Item', {'name': voucher_detail_no},
-			['deferred_expense_account', 'expense_account'])
+		credit_account, debit_account = frappe.db.get_value(
+			"Purchase Invoice Item",
+			{"name": voucher_detail_no},
+			["deferred_expense_account", "expense_account"],
+		)
 
-	if dr_or_cr == 'Debit':
+	if dr_or_cr == "Debit":
 		return debit_account
 	else:
 		return credit_account
diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py
index f8a06c7..2610c86 100644
--- a/erpnext/accounts/doctype/account/account.py
+++ b/erpnext/accounts/doctype/account/account.py
@@ -10,11 +10,17 @@
 import erpnext
 
 
-class RootNotEditable(frappe.ValidationError): pass
-class BalanceMismatchError(frappe.ValidationError): pass
+class RootNotEditable(frappe.ValidationError):
+	pass
+
+
+class BalanceMismatchError(frappe.ValidationError):
+	pass
+
 
 class Account(NestedSet):
-	nsm_parent_field = 'parent_account'
+	nsm_parent_field = "parent_account"
+
 	def on_update(self):
 		if frappe.local.flags.ignore_update_nsm:
 			return
@@ -22,17 +28,20 @@
 			super(Account, self).on_update()
 
 	def onload(self):
-		frozen_accounts_modifier = frappe.db.get_value("Accounts Settings", "Accounts Settings",
-			"frozen_accounts_modifier")
+		frozen_accounts_modifier = frappe.db.get_value(
+			"Accounts Settings", "Accounts Settings", "frozen_accounts_modifier"
+		)
 		if not frozen_accounts_modifier or frozen_accounts_modifier in frappe.get_roles():
 			self.set_onload("can_freeze_account", True)
 
 	def autoname(self):
 		from erpnext.accounts.utils import get_autoname_with_number
+
 		self.name = get_autoname_with_number(self.account_number, self.account_name, None, self.company)
 
 	def validate(self):
 		from erpnext.accounts.utils import validate_field_number
+
 		if frappe.local.flags.allow_unverified_charts:
 			return
 		self.validate_parent()
@@ -49,22 +58,33 @@
 	def validate_parent(self):
 		"""Fetch Parent Details and validate parent account"""
 		if self.parent_account:
-			par = frappe.db.get_value("Account", self.parent_account,
-				["name", "is_group", "company"], as_dict=1)
+			par = frappe.db.get_value(
+				"Account", self.parent_account, ["name", "is_group", "company"], as_dict=1
+			)
 			if not par:
-				throw(_("Account {0}: Parent account {1} does not exist").format(self.name, self.parent_account))
+				throw(
+					_("Account {0}: Parent account {1} does not exist").format(self.name, self.parent_account)
+				)
 			elif par.name == self.name:
 				throw(_("Account {0}: You can not assign itself as parent account").format(self.name))
 			elif not par.is_group:
-				throw(_("Account {0}: Parent account {1} can not be a ledger").format(self.name, self.parent_account))
+				throw(
+					_("Account {0}: Parent account {1} can not be a ledger").format(
+						self.name, self.parent_account
+					)
+				)
 			elif par.company != self.company:
-				throw(_("Account {0}: Parent account {1} does not belong to company: {2}")
-					.format(self.name, self.parent_account, self.company))
+				throw(
+					_("Account {0}: Parent account {1} does not belong to company: {2}").format(
+						self.name, self.parent_account, self.company
+					)
+				)
 
 	def set_root_and_report_type(self):
 		if self.parent_account:
-			par = frappe.db.get_value("Account", self.parent_account,
-				["report_type", "root_type"], as_dict=1)
+			par = frappe.db.get_value(
+				"Account", self.parent_account, ["report_type", "root_type"], as_dict=1
+			)
 
 			if par.report_type:
 				self.report_type = par.report_type
@@ -75,15 +95,20 @@
 			db_value = frappe.db.get_value("Account", self.name, ["report_type", "root_type"], as_dict=1)
 			if db_value:
 				if self.report_type != db_value.report_type:
-					frappe.db.sql("update `tabAccount` set report_type=%s where lft > %s and rgt < %s",
-						(self.report_type, self.lft, self.rgt))
+					frappe.db.sql(
+						"update `tabAccount` set report_type=%s where lft > %s and rgt < %s",
+						(self.report_type, self.lft, self.rgt),
+					)
 				if self.root_type != db_value.root_type:
-					frappe.db.sql("update `tabAccount` set root_type=%s where lft > %s and rgt < %s",
-						(self.root_type, self.lft, self.rgt))
+					frappe.db.sql(
+						"update `tabAccount` set root_type=%s where lft > %s and rgt < %s",
+						(self.root_type, self.lft, self.rgt),
+					)
 
 		if self.root_type and not self.report_type:
-			self.report_type = "Balance Sheet" \
-				if self.root_type in ("Asset", "Liability", "Equity") else "Profit and Loss"
+			self.report_type = (
+				"Balance Sheet" if self.root_type in ("Asset", "Liability", "Equity") else "Profit and Loss"
+			)
 
 	def validate_root_details(self):
 		# does not exists parent
@@ -96,21 +121,26 @@
 
 	def validate_root_company_and_sync_account_to_children(self):
 		# ignore validation while creating new compnay or while syncing to child companies
-		if frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation:
+		if (
+			frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation
+		):
 			return
 		ancestors = get_root_company(self.company)
 		if ancestors:
 			if frappe.get_value("Company", self.company, "allow_account_creation_against_child_company"):
 				return
-			if not frappe.db.get_value("Account",
-				{'account_name': self.account_name, 'company': ancestors[0]}, 'name'):
+			if not frappe.db.get_value(
+				"Account", {"account_name": self.account_name, "company": ancestors[0]}, "name"
+			):
 				frappe.throw(_("Please add the account to root level Company - {}").format(ancestors[0]))
 		elif self.parent_account:
-			descendants = get_descendants_of('Company', self.company)
-			if not descendants: return
+			descendants = get_descendants_of("Company", self.company)
+			if not descendants:
+				return
 			parent_acc_name_map = {}
-			parent_acc_name, parent_acc_number = frappe.db.get_value('Account', self.parent_account, \
-				["account_name", "account_number"])
+			parent_acc_name, parent_acc_number = frappe.db.get_value(
+				"Account", self.parent_account, ["account_name", "account_number"]
+			)
 			filters = {
 				"company": ["in", descendants],
 				"account_name": parent_acc_name,
@@ -118,10 +148,13 @@
 			if parent_acc_number:
 				filters["account_number"] = parent_acc_number
 
-			for d in frappe.db.get_values('Account', filters=filters, fieldname=["company", "name"], as_dict=True):
+			for d in frappe.db.get_values(
+				"Account", filters=filters, fieldname=["company", "name"], as_dict=True
+			):
 				parent_acc_name_map[d["company"]] = d["name"]
 
-			if not parent_acc_name_map: return
+			if not parent_acc_name_map:
+				return
 
 			self.create_account_for_child_company(parent_acc_name_map, descendants, parent_acc_name)
 
@@ -142,26 +175,38 @@
 	def validate_frozen_accounts_modifier(self):
 		old_value = frappe.db.get_value("Account", self.name, "freeze_account")
 		if old_value and old_value != self.freeze_account:
-			frozen_accounts_modifier = frappe.db.get_value('Accounts Settings', None, 'frozen_accounts_modifier')
-			if not frozen_accounts_modifier or \
-				frozen_accounts_modifier not in frappe.get_roles():
-					throw(_("You are not authorized to set Frozen value"))
+			frozen_accounts_modifier = frappe.db.get_value(
+				"Accounts Settings", None, "frozen_accounts_modifier"
+			)
+			if not frozen_accounts_modifier or frozen_accounts_modifier not in frappe.get_roles():
+				throw(_("You are not authorized to set Frozen value"))
 
 	def validate_balance_must_be_debit_or_credit(self):
 		from erpnext.accounts.utils import get_balance_on
+
 		if not self.get("__islocal") and self.balance_must_be:
 			account_balance = get_balance_on(self.name)
 
 			if account_balance > 0 and self.balance_must_be == "Credit":
-				frappe.throw(_("Account balance already in Debit, you are not allowed to set 'Balance Must Be' as 'Credit'"))
+				frappe.throw(
+					_(
+						"Account balance already in Debit, you are not allowed to set 'Balance Must Be' as 'Credit'"
+					)
+				)
 			elif account_balance < 0 and self.balance_must_be == "Debit":
-				frappe.throw(_("Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'"))
+				frappe.throw(
+					_(
+						"Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'"
+					)
+				)
 
 	def validate_account_currency(self):
 		if not self.account_currency:
-			self.account_currency = frappe.get_cached_value('Company',  self.company,  "default_currency")
+			self.account_currency = frappe.get_cached_value("Company", self.company, "default_currency")
 
-		elif self.account_currency != frappe.db.get_value("Account", self.name, "account_currency"):
+		gl_currency = frappe.db.get_value("GL Entry", {"account": self.name}, "account_currency")
+
+		if gl_currency and self.account_currency != gl_currency:
 			if frappe.db.get_value("GL Entry", {"account": self.name}):
 				frappe.throw(_("Currency can not be changed after making entries using some other currency"))
 
@@ -170,45 +215,52 @@
 			company_bold = frappe.bold(company)
 			parent_acc_name_bold = frappe.bold(parent_acc_name)
 			if not parent_acc_name_map.get(company):
-				frappe.throw(_("While creating account for Child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA")
-					.format(company_bold, parent_acc_name_bold), title=_("Account Not Found"))
+				frappe.throw(
+					_(
+						"While creating account for Child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA"
+					).format(company_bold, parent_acc_name_bold),
+					title=_("Account Not Found"),
+				)
 
 			# validate if parent of child company account to be added is a group
-			if (frappe.db.get_value("Account", self.parent_account, "is_group")
-				and not frappe.db.get_value("Account", parent_acc_name_map[company], "is_group")):
-				msg = _("While creating account for Child Company {0}, parent account {1} found as a ledger account.").format(company_bold, parent_acc_name_bold)
+			if frappe.db.get_value("Account", self.parent_account, "is_group") and not frappe.db.get_value(
+				"Account", parent_acc_name_map[company], "is_group"
+			):
+				msg = _(
+					"While creating account for Child Company {0}, parent account {1} found as a ledger account."
+				).format(company_bold, parent_acc_name_bold)
 				msg += "<br><br>"
-				msg += _("Please convert the parent account in corresponding child company to a group account.")
+				msg += _(
+					"Please convert the parent account in corresponding child company to a group account."
+				)
 				frappe.throw(msg, title=_("Invalid Parent Account"))
 
-			filters = {
-				"account_name": self.account_name,
-				"company": company
-			}
+			filters = {"account_name": self.account_name, "company": company}
 
 			if self.account_number:
 				filters["account_number"] = self.account_number
 
-			child_account = frappe.db.get_value("Account", filters, 'name')
+			child_account = frappe.db.get_value("Account", filters, "name")
 			if not child_account:
 				doc = frappe.copy_doc(self)
 				doc.flags.ignore_root_company_validation = True
-				doc.update({
-					"company": company,
-					# parent account's currency should be passed down to child account's curreny
-					# if it is None, it picks it up from default company currency, which might be unintended
-					"account_currency": erpnext.get_company_currency(company),
-					"parent_account": parent_acc_name_map[company]
-				})
+				doc.update(
+					{
+						"company": company,
+						# parent account's currency should be passed down to child account's curreny
+						# if it is None, it picks it up from default company currency, which might be unintended
+						"account_currency": erpnext.get_company_currency(company),
+						"parent_account": parent_acc_name_map[company],
+					}
+				)
 
 				doc.save()
-				frappe.msgprint(_("Account {0} is added in the child company {1}")
-					.format(doc.name, company))
+				frappe.msgprint(_("Account {0} is added in the child company {1}").format(doc.name, company))
 			elif child_account:
 				# update the parent company's value in child companies
 				doc = frappe.get_doc("Account", child_account)
 				parent_value_changed = False
-				for field in ['account_type', 'freeze_account', 'balance_must_be']:
+				for field in ["account_type", "freeze_account", "balance_must_be"]:
 					if doc.get(field) != self.get(field):
 						parent_value_changed = True
 						doc.set(field, self.get(field))
@@ -243,8 +295,11 @@
 		return frappe.db.get_value("GL Entry", {"account": self.name})
 
 	def check_if_child_exists(self):
-		return frappe.db.sql("""select name from `tabAccount` where parent_account = %s
-			and docstatus != 2""", self.name)
+		return frappe.db.sql(
+			"""select name from `tabAccount` where parent_account = %s
+			and docstatus != 2""",
+			self.name,
+		)
 
 	def validate_mandatory(self):
 		if not self.root_type:
@@ -260,73 +315,99 @@
 
 		super(Account, self).on_trash(True)
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_parent_account(doctype, txt, searchfield, start, page_len, filters):
-	return frappe.db.sql("""select name from tabAccount
+	return frappe.db.sql(
+		"""select name from tabAccount
 		where is_group = 1 and docstatus != 2 and company = %s
-		and %s like %s order by name limit %s, %s""" %
-		("%s", searchfield, "%s", "%s", "%s"),
-		(filters["company"], "%%%s%%" % txt, start, page_len), as_list=1)
+		and %s like %s order by name limit %s offset %s"""
+		% ("%s", searchfield, "%s", "%s", "%s"),
+		(filters["company"], "%%%s%%" % txt, page_len, start),
+		as_list=1,
+	)
+
 
 def get_account_currency(account):
 	"""Helper function to get account currency"""
 	if not account:
 		return
+
 	def generator():
-		account_currency, company = frappe.get_cached_value("Account", account, ["account_currency", "company"])
+		account_currency, company = frappe.get_cached_value(
+			"Account", account, ["account_currency", "company"]
+		)
 		if not account_currency:
-			account_currency = frappe.get_cached_value('Company',  company,  "default_currency")
+			account_currency = frappe.get_cached_value("Company", company, "default_currency")
 
 		return account_currency
 
 	return frappe.local_cache("account_currency", account, generator)
 
+
 def on_doctype_update():
 	frappe.db.add_index("Account", ["lft", "rgt"])
 
+
 def get_account_autoname(account_number, account_name, company):
 	# first validate if company exists
-	company = frappe.get_cached_value('Company',  company,  ["abbr", "name"], as_dict=True)
+	company = frappe.get_cached_value("Company", company, ["abbr", "name"], as_dict=True)
 	if not company:
-		frappe.throw(_('Company {0} does not exist').format(company))
+		frappe.throw(_("Company {0} does not exist").format(company))
 
 	parts = [account_name.strip(), company.abbr]
 	if cstr(account_number).strip():
 		parts.insert(0, cstr(account_number).strip())
-	return ' - '.join(parts)
+	return " - ".join(parts)
+
 
 def validate_account_number(name, account_number, company):
 	if account_number:
-		account_with_same_number = frappe.db.get_value("Account",
-			{"account_number": account_number, "company": company, "name": ["!=", name]})
+		account_with_same_number = frappe.db.get_value(
+			"Account", {"account_number": account_number, "company": company, "name": ["!=", name]}
+		)
 		if account_with_same_number:
-			frappe.throw(_("Account Number {0} already used in account {1}")
-				.format(account_number, account_with_same_number))
+			frappe.throw(
+				_("Account Number {0} already used in account {1}").format(
+					account_number, account_with_same_number
+				)
+			)
+
 
 @frappe.whitelist()
 def update_account_number(name, account_name, account_number=None, from_descendant=False):
 	account = frappe.db.get_value("Account", name, "company", as_dict=True)
-	if not account: return
+	if not account:
+		return
 
-	old_acc_name, old_acc_number = frappe.db.get_value('Account', name, \
-				["account_name", "account_number"])
+	old_acc_name, old_acc_number = frappe.db.get_value(
+		"Account", name, ["account_name", "account_number"]
+	)
 
 	# check if account exists in parent company
 	ancestors = get_ancestors_of("Company", account.company)
-	allow_independent_account_creation = frappe.get_value("Company", account.company, "allow_account_creation_against_child_company")
+	allow_independent_account_creation = frappe.get_value(
+		"Company", account.company, "allow_account_creation_against_child_company"
+	)
 
 	if ancestors and not allow_independent_account_creation:
 		for ancestor in ancestors:
-			if frappe.db.get_value("Account", {'account_name': old_acc_name, 'company': ancestor}, 'name'):
+			if frappe.db.get_value("Account", {"account_name": old_acc_name, "company": ancestor}, "name"):
 				# same account in parent company exists
 				allow_child_account_creation = _("Allow Account Creation Against Child Company")
 
-				message = _("Account {0} exists in parent company {1}.").format(frappe.bold(old_acc_name), frappe.bold(ancestor))
+				message = _("Account {0} exists in parent company {1}.").format(
+					frappe.bold(old_acc_name), frappe.bold(ancestor)
+				)
 				message += "<br>"
-				message += _("Renaming it is only allowed via parent company {0}, to avoid mismatch.").format(frappe.bold(ancestor))
+				message += _("Renaming it is only allowed via parent company {0}, to avoid mismatch.").format(
+					frappe.bold(ancestor)
+				)
 				message += "<br><br>"
-				message += _("To overrule this, enable '{0}' in company {1}").format(allow_child_account_creation, frappe.bold(account.company))
+				message += _("To overrule this, enable '{0}' in company {1}").format(
+					allow_child_account_creation, frappe.bold(account.company)
+				)
 
 				frappe.throw(message, title=_("Rename Not Allowed"))
 
@@ -339,42 +420,53 @@
 
 	if not from_descendant:
 		# Update and rename in child company accounts as well
-		descendants = get_descendants_of('Company', account.company)
+		descendants = get_descendants_of("Company", account.company)
 		if descendants:
-			sync_update_account_number_in_child(descendants, old_acc_name, account_name, account_number, old_acc_number)
+			sync_update_account_number_in_child(
+				descendants, old_acc_name, account_name, account_number, old_acc_number
+			)
 
 	new_name = get_account_autoname(account_number, account_name, account.company)
 	if name != new_name:
 		frappe.rename_doc("Account", name, new_name, force=1)
 		return new_name
 
+
 @frappe.whitelist()
 def merge_account(old, new, is_group, root_type, company):
 	# Validate properties before merging
 	if not frappe.db.exists("Account", new):
 		throw(_("Account {0} does not exist").format(new))
 
-	val = list(frappe.db.get_value("Account", new,
-		["is_group", "root_type", "company"]))
+	val = list(frappe.db.get_value("Account", new, ["is_group", "root_type", "company"]))
 
 	if val != [cint(is_group), root_type, company]:
-		throw(_("""Merging is only possible if following properties are same in both records. Is Group, Root Type, Company"""))
+		throw(
+			_(
+				"""Merging is only possible if following properties are same in both records. Is Group, Root Type, Company"""
+			)
+		)
 
 	if is_group and frappe.db.get_value("Account", new, "parent_account") == old:
-		frappe.db.set_value("Account", new, "parent_account",
-			frappe.db.get_value("Account", old, "parent_account"))
+		frappe.db.set_value(
+			"Account", new, "parent_account", frappe.db.get_value("Account", old, "parent_account")
+		)
 
 	frappe.rename_doc("Account", old, new, merge=1, force=1)
 
 	return new
 
+
 @frappe.whitelist()
 def get_root_company(company):
 	# return the topmost company in the hierarchy
-	ancestors = get_ancestors_of('Company', company, "lft asc")
+	ancestors = get_ancestors_of("Company", company, "lft asc")
 	return [ancestors[0]] if ancestors else []
 
-def sync_update_account_number_in_child(descendants, old_acc_name, account_name, account_number=None, old_acc_number=None):
+
+def sync_update_account_number_in_child(
+	descendants, old_acc_name, account_name, account_number=None, old_acc_number=None
+):
 	filters = {
 		"company": ["in", descendants],
 		"account_name": old_acc_name,
@@ -382,5 +474,7 @@
 	if old_acc_number:
 		filters["account_number"] = old_acc_number
 
-	for d in frappe.db.get_values('Account', filters=filters, fieldname=["company", "name"], as_dict=True):
-			update_account_number(d["name"], account_name, account_number, from_descendant=True)
+	for d in frappe.db.get_values(
+		"Account", filters=filters, fieldname=["company", "name"], as_dict=True
+	):
+		update_account_number(d["name"], account_name, account_number, from_descendant=True)
diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js
index a3ef384..8ae90ce 100644
--- a/erpnext/accounts/doctype/account/account_tree.js
+++ b/erpnext/accounts/doctype/account/account_tree.js
@@ -160,7 +160,7 @@
 			let root_company = treeview.page.fields_dict.root_company.get_value();
 
 			if(root_company) {
-				frappe.throw(__("Please add the account to root level Company - ") + root_company);
+				frappe.throw(__("Please add the account to root level Company - {0}"), [root_company]);
 			} else {
 				treeview.new_node();
 			}
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
index a8de06c..947b485 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
@@ -10,7 +10,9 @@
 from unidecode import unidecode
 
 
-def create_charts(company, chart_template=None, existing_company=None, custom_chart=None, from_coa_importer=None):
+def create_charts(
+	company, chart_template=None, existing_company=None, custom_chart=None, from_coa_importer=None
+):
 	chart = custom_chart or get_chart(chart_template, existing_company)
 	if chart:
 		accounts = []
@@ -20,30 +22,41 @@
 				if root_account:
 					root_type = child.get("root_type")
 
-				if account_name not in ["account_name", "account_number", "account_type",
-					"root_type", "is_group", "tax_rate"]:
+				if account_name not in [
+					"account_name",
+					"account_number",
+					"account_type",
+					"root_type",
+					"is_group",
+					"tax_rate",
+				]:
 
 					account_number = cstr(child.get("account_number")).strip()
-					account_name, account_name_in_db = add_suffix_if_duplicate(account_name,
-						account_number, accounts)
+					account_name, account_name_in_db = add_suffix_if_duplicate(
+						account_name, account_number, accounts
+					)
 
 					is_group = identify_is_group(child)
-					report_type = "Balance Sheet" if root_type in ["Asset", "Liability", "Equity"] \
-						else "Profit and Loss"
+					report_type = (
+						"Balance Sheet" if root_type in ["Asset", "Liability", "Equity"] else "Profit and Loss"
+					)
 
-					account = frappe.get_doc({
-						"doctype": "Account",
-						"account_name": child.get('account_name') if from_coa_importer else account_name,
-						"company": company,
-						"parent_account": parent,
-						"is_group": is_group,
-						"root_type": root_type,
-						"report_type": report_type,
-						"account_number": account_number,
-						"account_type": child.get("account_type"),
-						"account_currency": child.get('account_currency') or frappe.db.get_value('Company',  company,  "default_currency"),
-						"tax_rate": child.get("tax_rate")
-					})
+					account = frappe.get_doc(
+						{
+							"doctype": "Account",
+							"account_name": child.get("account_name") if from_coa_importer else account_name,
+							"company": company,
+							"parent_account": parent,
+							"is_group": is_group,
+							"root_type": root_type,
+							"report_type": report_type,
+							"account_number": account_number,
+							"account_type": child.get("account_type"),
+							"account_currency": child.get("account_currency")
+							or frappe.db.get_value("Company", company, "default_currency"),
+							"tax_rate": child.get("tax_rate"),
+						}
+					)
 
 					if root_account or frappe.local.flags.allow_unverified_charts:
 						account.flags.ignore_mandatory = True
@@ -63,10 +76,10 @@
 		rebuild_tree("Account", "parent_account")
 		frappe.local.flags.ignore_update_nsm = False
 
+
 def add_suffix_if_duplicate(account_name, account_number, accounts):
 	if account_number:
-		account_name_in_db = unidecode(" - ".join([account_number,
-			account_name.strip().lower()]))
+		account_name_in_db = unidecode(" - ".join([account_number, account_name.strip().lower()]))
 	else:
 		account_name_in_db = unidecode(account_name.strip().lower())
 
@@ -76,16 +89,21 @@
 
 	return account_name, account_name_in_db
 
+
 def identify_is_group(child):
 	if child.get("is_group"):
 		is_group = child.get("is_group")
-	elif len(set(child.keys()) - set(["account_name", "account_type", "root_type", "is_group", "tax_rate", "account_number"])):
+	elif len(
+		set(child.keys())
+		- set(["account_name", "account_type", "root_type", "is_group", "tax_rate", "account_number"])
+	):
 		is_group = 1
 	else:
 		is_group = 0
 
 	return is_group
 
+
 def get_chart(chart_template, existing_company=None):
 	chart = {}
 	if existing_company:
@@ -95,11 +113,13 @@
 		from erpnext.accounts.doctype.account.chart_of_accounts.verified import (
 			standard_chart_of_accounts,
 		)
+
 		return standard_chart_of_accounts.get()
 	elif chart_template == "Standard with Numbers":
 		from erpnext.accounts.doctype.account.chart_of_accounts.verified import (
 			standard_chart_of_accounts_with_account_number,
 		)
+
 		return standard_chart_of_accounts_with_account_number.get()
 	else:
 		folders = ("verified",)
@@ -115,6 +135,7 @@
 						if chart and json.loads(chart).get("name") == chart_template:
 							return json.loads(chart).get("tree")
 
+
 @frappe.whitelist()
 def get_charts_for_country(country, with_standard=False):
 	charts = []
@@ -122,9 +143,10 @@
 	def _get_chart_name(content):
 		if content:
 			content = json.loads(content)
-			if (content and content.get("disabled", "No") == "No") \
-				or frappe.local.flags.allow_unverified_charts:
-					charts.append(content["name"])
+			if (
+				content and content.get("disabled", "No") == "No"
+			) or frappe.local.flags.allow_unverified_charts:
+				charts.append(content["name"])
 
 	country_code = frappe.db.get_value("Country", country, "code")
 	if country_code:
@@ -151,11 +173,21 @@
 
 
 def get_account_tree_from_existing_company(existing_company):
-	all_accounts = frappe.get_all('Account',
-		filters={'company': existing_company},
-		fields = ["name", "account_name", "parent_account", "account_type",
-			"is_group", "root_type", "tax_rate", "account_number"],
-		order_by="lft, rgt")
+	all_accounts = frappe.get_all(
+		"Account",
+		filters={"company": existing_company},
+		fields=[
+			"name",
+			"account_name",
+			"parent_account",
+			"account_type",
+			"is_group",
+			"root_type",
+			"tax_rate",
+			"account_number",
+		],
+		order_by="lft, rgt",
+	)
 
 	account_tree = {}
 
@@ -164,6 +196,7 @@
 		build_account_tree(account_tree, None, all_accounts)
 	return account_tree
 
+
 def build_account_tree(tree, parent, all_accounts):
 	# find children
 	parent_account = parent.name if parent else ""
@@ -192,27 +225,29 @@
 		# call recursively to build a subtree for current account
 		build_account_tree(tree[child.account_name], child, all_accounts)
 
+
 @frappe.whitelist()
 def validate_bank_account(coa, bank_account):
 	accounts = []
 	chart = get_chart(coa)
 
 	if chart:
+
 		def _get_account_names(account_master):
 			for account_name, child in account_master.items():
-				if account_name not in ["account_number", "account_type",
-					"root_type", "is_group", "tax_rate"]:
+				if account_name not in ["account_number", "account_type", "root_type", "is_group", "tax_rate"]:
 					accounts.append(account_name)
 
 					_get_account_names(child)
 
 		_get_account_names(chart)
 
-	return (bank_account in accounts)
+	return bank_account in accounts
+
 
 @frappe.whitelist()
 def build_tree_from_json(chart_template, chart_data=None, from_coa_importer=False):
-	''' get chart template from its folder and parse the json to be rendered as tree '''
+	"""get chart template from its folder and parse the json to be rendered as tree"""
 	chart = chart_data or get_chart(chart_template)
 
 	# if no template selected, return as it is
@@ -220,22 +255,33 @@
 		return
 
 	accounts = []
+
 	def _import_accounts(children, parent):
-		''' recursively called to form a parent-child based list of dict from chart template '''
+		"""recursively called to form a parent-child based list of dict from chart template"""
 		for account_name, child in children.items():
 			account = {}
-			if account_name in ["account_name", "account_number", "account_type",\
-				"root_type", "is_group", "tax_rate"]: continue
+			if account_name in [
+				"account_name",
+				"account_number",
+				"account_type",
+				"root_type",
+				"is_group",
+				"tax_rate",
+			]:
+				continue
 
 			if from_coa_importer:
-				account_name = child['account_name']
+				account_name = child["account_name"]
 
-			account['parent_account'] = parent
-			account['expandable'] = True if identify_is_group(child) else False
-			account['value'] = (cstr(child.get('account_number')).strip() + ' - ' + account_name) \
-				if child.get('account_number') else account_name
+			account["parent_account"] = parent
+			account["expandable"] = True if identify_is_group(child) else False
+			account["value"] = (
+				(cstr(child.get("account_number")).strip() + " - " + account_name)
+				if child.get("account_number")
+				else account_name
+			)
 			accounts.append(account)
-			_import_accounts(child, account['value'])
+			_import_accounts(child, account["value"])
 
 	_import_accounts(chart, None)
 	return accounts
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/import_from_openerp.py b/erpnext/accounts/doctype/account/chart_of_accounts/import_from_openerp.py
index 79001d7..3f25ada 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/import_from_openerp.py
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/import_from_openerp.py
@@ -20,6 +20,7 @@
 all_account_types = []
 all_roots = {}
 
+
 def go():
 	global accounts, charts
 	default_account_types = get_default_account_types()
@@ -34,14 +35,16 @@
 		accounts, charts = {}, {}
 		country_path = os.path.join(path, country_dir)
 		manifest = ast.literal_eval(open(os.path.join(country_path, "__openerp__.py")).read())
-		data_files = manifest.get("data", []) + manifest.get("init_xml", []) + \
-			manifest.get("update_xml", [])
+		data_files = (
+			manifest.get("data", []) + manifest.get("init_xml", []) + manifest.get("update_xml", [])
+		)
 		files_path = [os.path.join(country_path, d) for d in data_files]
 		xml_roots = get_xml_roots(files_path)
 		csv_content = get_csv_contents(files_path)
 		prefix = country_dir if csv_content else None
-		account_types = get_account_types(xml_roots.get("account.account.type", []),
-			csv_content.get("account.account.type", []), prefix)
+		account_types = get_account_types(
+			xml_roots.get("account.account.type", []), csv_content.get("account.account.type", []), prefix
+		)
 		account_types.update(default_account_types)
 
 		if xml_roots:
@@ -54,12 +57,15 @@
 
 	create_all_roots_file()
 
+
 def get_default_account_types():
 	default_types_root = []
-	default_types_root.append(ET.parse(os.path.join(path, "account", "data",
-			"data_account_type.xml")).getroot())
+	default_types_root.append(
+		ET.parse(os.path.join(path, "account", "data", "data_account_type.xml")).getroot()
+	)
 	return get_account_types(default_types_root, None, prefix="account")
 
+
 def get_xml_roots(files_path):
 	xml_roots = frappe._dict()
 	for filepath in files_path:
@@ -68,64 +74,69 @@
 			tree = ET.parse(filepath)
 			root = tree.getroot()
 			for node in root[0].findall("record"):
-				if node.get("model") in ["account.account.template",
-					"account.chart.template", "account.account.type"]:
+				if node.get("model") in [
+					"account.account.template",
+					"account.chart.template",
+					"account.account.type",
+				]:
 					xml_roots.setdefault(node.get("model"), []).append(root)
 					break
 	return xml_roots
 
+
 def get_csv_contents(files_path):
 	csv_content = {}
 	for filepath in files_path:
 		fname = os.path.basename(filepath)
-		for file_type in ["account.account.template", "account.account.type",
-				"account.chart.template"]:
+		for file_type in ["account.account.template", "account.account.type", "account.chart.template"]:
 			if fname.startswith(file_type) and fname.endswith(".csv"):
 				with open(filepath, "r") as csvfile:
 					try:
-						csv_content.setdefault(file_type, [])\
-							.append(read_csv_content(csvfile.read()))
+						csv_content.setdefault(file_type, []).append(read_csv_content(csvfile.read()))
 					except Exception as e:
 						continue
 	return csv_content
 
+
 def get_account_types(root_list, csv_content, prefix=None):
 	types = {}
 	account_type_map = {
-		'cash': 'Cash',
-		'bank': 'Bank',
-		'tr_cash': 'Cash',
-		'tr_bank': 'Bank',
-		'receivable': 'Receivable',
-		'tr_receivable': 'Receivable',
-		'account rec': 'Receivable',
-		'payable': 'Payable',
-		'tr_payable': 'Payable',
-		'equity': 'Equity',
-		'stocks': 'Stock',
-		'stock': 'Stock',
-		'tax': 'Tax',
-		'tr_tax': 'Tax',
-		'tax-out': 'Tax',
-		'tax-in': 'Tax',
-		'charges_personnel': 'Chargeable',
-		'fixed asset': 'Fixed Asset',
-		'cogs': 'Cost of Goods Sold',
-
+		"cash": "Cash",
+		"bank": "Bank",
+		"tr_cash": "Cash",
+		"tr_bank": "Bank",
+		"receivable": "Receivable",
+		"tr_receivable": "Receivable",
+		"account rec": "Receivable",
+		"payable": "Payable",
+		"tr_payable": "Payable",
+		"equity": "Equity",
+		"stocks": "Stock",
+		"stock": "Stock",
+		"tax": "Tax",
+		"tr_tax": "Tax",
+		"tax-out": "Tax",
+		"tax-in": "Tax",
+		"charges_personnel": "Chargeable",
+		"fixed asset": "Fixed Asset",
+		"cogs": "Cost of Goods Sold",
 	}
 	for root in root_list:
 		for node in root[0].findall("record"):
-			if node.get("model")=="account.account.type":
+			if node.get("model") == "account.account.type":
 				data = {}
 				for field in node.findall("field"):
-					if field.get("name")=="code" and field.text.lower() != "none" \
-						and account_type_map.get(field.text):
-							data["account_type"] = account_type_map[field.text]
+					if (
+						field.get("name") == "code"
+						and field.text.lower() != "none"
+						and account_type_map.get(field.text)
+					):
+						data["account_type"] = account_type_map[field.text]
 
 				node_id = prefix + "." + node.get("id") if prefix else node.get("id")
 				types[node_id] = data
 
-	if csv_content and csv_content[0][0]=="id":
+	if csv_content and csv_content[0][0] == "id":
 		for row in csv_content[1:]:
 			row_dict = dict(zip(csv_content[0], row))
 			data = {}
@@ -136,21 +147,22 @@
 				types[node_id] = data
 	return types
 
+
 def make_maps_for_xml(xml_roots, account_types, country_dir):
 	"""make maps for `charts` and `accounts`"""
 	for model, root_list in xml_roots.items():
 		for root in root_list:
 			for node in root[0].findall("record"):
-				if node.get("model")=="account.account.template":
+				if node.get("model") == "account.account.template":
 					data = {}
 					for field in node.findall("field"):
-						if field.get("name")=="name":
+						if field.get("name") == "name":
 							data["name"] = field.text
-						if field.get("name")=="parent_id":
+						if field.get("name") == "parent_id":
 							parent_id = field.get("ref") or field.get("eval")
 							data["parent_id"] = parent_id
 
-						if field.get("name")=="user_type":
+						if field.get("name") == "user_type":
 							value = field.get("ref")
 							if account_types.get(value, {}).get("account_type"):
 								data["account_type"] = account_types[value]["account_type"]
@@ -160,16 +172,17 @@
 					data["children"] = []
 					accounts[node.get("id")] = data
 
-				if node.get("model")=="account.chart.template":
+				if node.get("model") == "account.chart.template":
 					data = {}
 					for field in node.findall("field"):
-						if field.get("name")=="name":
+						if field.get("name") == "name":
 							data["name"] = field.text
-						if field.get("name")=="account_root_id":
+						if field.get("name") == "account_root_id":
 							data["account_root_id"] = field.get("ref")
 						data["id"] = country_dir
 					charts.setdefault(node.get("id"), {}).update(data)
 
+
 def make_maps_for_csv(csv_content, account_types, country_dir):
 	for content in csv_content.get("account.account.template", []):
 		for row in content[1:]:
@@ -177,7 +190,7 @@
 			account = {
 				"name": data.get("name"),
 				"parent_id": data.get("parent_id:id") or data.get("parent_id/id"),
-				"children": []
+				"children": [],
 			}
 			user_type = data.get("user_type/id") or data.get("user_type:id")
 			if account_types.get(user_type, {}).get("account_type"):
@@ -194,12 +207,14 @@
 		for row in content[1:]:
 			if row:
 				data = dict(zip(content[0], row))
-				charts.setdefault(data.get("id"), {}).update({
-					"account_root_id": data.get("account_root_id:id") or \
-						data.get("account_root_id/id"),
-					"name": data.get("name"),
-					"id": country_dir
-				})
+				charts.setdefault(data.get("id"), {}).update(
+					{
+						"account_root_id": data.get("account_root_id:id") or data.get("account_root_id/id"),
+						"name": data.get("name"),
+						"id": country_dir,
+					}
+				)
+
 
 def make_account_trees():
 	"""build tree hierarchy"""
@@ -218,6 +233,7 @@
 		if "children" in accounts[id] and not accounts[id].get("children"):
 			del accounts[id]["children"]
 
+
 def make_charts():
 	"""write chart files in app/setup/doctype/company/charts"""
 	for chart_id in charts:
@@ -236,34 +252,38 @@
 		chart["country_code"] = src["id"][5:]
 		chart["tree"] = accounts[src["account_root_id"]]
 
-
 		for key, val in chart["tree"].items():
 			if key in ["name", "parent_id"]:
 				chart["tree"].pop(key)
 			if type(val) == dict:
 				val["root_type"] = ""
 		if chart:
-			fpath = os.path.join("erpnext", "erpnext", "accounts", "doctype", "account",
-				"chart_of_accounts", filename + ".json")
+			fpath = os.path.join(
+				"erpnext", "erpnext", "accounts", "doctype", "account", "chart_of_accounts", filename + ".json"
+			)
 
 			with open(fpath, "r") as chartfile:
 				old_content = chartfile.read()
-				if not old_content or (json.loads(old_content).get("is_active", "No") == "No" \
-						and json.loads(old_content).get("disabled", "No") == "No"):
+				if not old_content or (
+					json.loads(old_content).get("is_active", "No") == "No"
+					and json.loads(old_content).get("disabled", "No") == "No"
+				):
 					with open(fpath, "w") as chartfile:
 						chartfile.write(json.dumps(chart, indent=4, sort_keys=True))
 
 					all_roots.setdefault(filename, chart["tree"].keys())
 
+
 def create_all_roots_file():
-	with open('all_roots.txt', 'w') as f:
+	with open("all_roots.txt", "w") as f:
 		for filename, roots in sorted(all_roots.items()):
 			f.write(filename)
-			f.write('\n----------------------\n')
+			f.write("\n----------------------\n")
 			for r in sorted(roots):
-				f.write(r.encode('utf-8'))
-				f.write('\n')
-			f.write('\n\n\n')
+				f.write(r.encode("utf-8"))
+				f.write("\n")
+			f.write("\n\n\n")
 
-if __name__=="__main__":
+
+if __name__ == "__main__":
 	go()
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py b/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py
index 9248ffa..e30ad24 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py
@@ -7,182 +7,103 @@
 
 def get():
 	return {
-	    _("Application of Funds (Assets)"): {
-	        _("Current Assets"): {
-	            _("Accounts Receivable"): {
-	                _("Debtors"): {
-	                    "account_type": "Receivable"
-	                }
-	            },
-	            _("Bank Accounts"): {
-	                "account_type": "Bank",
-					"is_group": 1
-	            },
-	            _("Cash In Hand"): {
-	                _("Cash"): {
-	                    "account_type": "Cash"
-	                },
-	                "account_type": "Cash"
-	            },
-	            _("Loans and Advances (Assets)"): {
-                    	_("Employee Advances"): {
-                    	},
-	            },
-	            _("Securities and Deposits"): {
-	                _("Earnest Money"): {}
-	            },
-	            _("Stock Assets"): {
-	                _("Stock In Hand"): {
-	                    "account_type": "Stock"
-	                },
-	                "account_type": "Stock",
-	            },
-	            _("Tax Assets"): {
-					"is_group": 1
-				}
-	        },
-	        _("Fixed Assets"): {
-	            _("Capital Equipments"): {
-	                "account_type": "Fixed Asset"
-	            },
-	            _("Electronic Equipments"): {
-	                "account_type": "Fixed Asset"
-	            },
-	            _("Furnitures and Fixtures"): {
-	                "account_type": "Fixed Asset"
-	            },
-	            _("Office Equipments"): {
-	                "account_type": "Fixed Asset"
-	            },
-	            _("Plants and Machineries"): {
-	                "account_type": "Fixed Asset"
-	            },
-				_("Buildings"): {
-					"account_type": "Fixed Asset"
+		_("Application of Funds (Assets)"): {
+			_("Current Assets"): {
+				_("Accounts Receivable"): {_("Debtors"): {"account_type": "Receivable"}},
+				_("Bank Accounts"): {"account_type": "Bank", "is_group": 1},
+				_("Cash In Hand"): {_("Cash"): {"account_type": "Cash"}, "account_type": "Cash"},
+				_("Loans and Advances (Assets)"): {
+					_("Employee Advances"): {},
 				},
-				_("Softwares"): {
-					"account_type": "Fixed Asset"
+				_("Securities and Deposits"): {_("Earnest Money"): {}},
+				_("Stock Assets"): {
+					_("Stock In Hand"): {"account_type": "Stock"},
+					"account_type": "Stock",
 				},
-	            _("Accumulated Depreciation"): {
-	            	"account_type": "Accumulated Depreciation"
-	            },
-                _("CWIP Account"): {
-                    "account_type": "Capital Work in Progress",
-                }
-	        },
-	        _("Investments"): {
-	        	"is_group": 1
-	        },
-	        _("Temporary Accounts"): {
-	            _("Temporary Opening"): {
-	            	"account_type": "Temporary"
-	            }
-	        },
-			"root_type": "Asset"
-	    },
-	    _("Expenses"): {
-	        _("Direct Expenses"): {
-	            _("Stock Expenses"): {
-	                _("Cost of Goods Sold"): {
-	                    "account_type": "Cost of Goods Sold"
-	                },
-                    _("Expenses Included In Asset Valuation"): {
-                        "account_type": "Expenses Included In Asset Valuation"
-                    },
-	                _("Expenses Included In Valuation"): {
-	                    "account_type": "Expenses Included In Valuation"
-	                },
-	                _("Stock Adjustment"): {
-	                    "account_type": "Stock Adjustment"
-	                }
-	            },
-	        },
-	        _("Indirect Expenses"): {
-	            _("Administrative Expenses"): {},
-	            _("Commission on Sales"): {},
-	            _("Depreciation"): {
-	                "account_type": "Depreciation"
-	            },
-	            _("Entertainment Expenses"): {},
-	            _("Freight and Forwarding Charges"): {
-	                "account_type": "Chargeable"
-	            },
-	            _("Legal Expenses"): {},
-	            _("Marketing Expenses"): {
-	                "account_type": "Chargeable"
-	            },
-	            _("Miscellaneous Expenses"): {
-	                "account_type": "Chargeable"
-	            },
-	            _("Office Maintenance Expenses"): {},
-	            _("Office Rent"): {},
-	            _("Postal Expenses"): {},
-	            _("Print and Stationery"): {},
-	            _("Round Off"): {
-	                "account_type": "Round Off"
-	            },
-	            _("Salary"): {},
-	            _("Sales Expenses"): {},
-	            _("Telephone Expenses"): {},
-	            _("Travel Expenses"): {},
-	            _("Utility Expenses"): {},
+				_("Tax Assets"): {"is_group": 1},
+			},
+			_("Fixed Assets"): {
+				_("Capital Equipments"): {"account_type": "Fixed Asset"},
+				_("Electronic Equipments"): {"account_type": "Fixed Asset"},
+				_("Furnitures and Fixtures"): {"account_type": "Fixed Asset"},
+				_("Office Equipments"): {"account_type": "Fixed Asset"},
+				_("Plants and Machineries"): {"account_type": "Fixed Asset"},
+				_("Buildings"): {"account_type": "Fixed Asset"},
+				_("Softwares"): {"account_type": "Fixed Asset"},
+				_("Accumulated Depreciation"): {"account_type": "Accumulated Depreciation"},
+				_("CWIP Account"): {
+					"account_type": "Capital Work in Progress",
+				},
+			},
+			_("Investments"): {"is_group": 1},
+			_("Temporary Accounts"): {_("Temporary Opening"): {"account_type": "Temporary"}},
+			"root_type": "Asset",
+		},
+		_("Expenses"): {
+			_("Direct Expenses"): {
+				_("Stock Expenses"): {
+					_("Cost of Goods Sold"): {"account_type": "Cost of Goods Sold"},
+					_("Expenses Included In Asset Valuation"): {
+						"account_type": "Expenses Included In Asset Valuation"
+					},
+					_("Expenses Included In Valuation"): {"account_type": "Expenses Included In Valuation"},
+					_("Stock Adjustment"): {"account_type": "Stock Adjustment"},
+				},
+			},
+			_("Indirect Expenses"): {
+				_("Administrative Expenses"): {},
+				_("Commission on Sales"): {},
+				_("Depreciation"): {"account_type": "Depreciation"},
+				_("Entertainment Expenses"): {},
+				_("Freight and Forwarding Charges"): {"account_type": "Chargeable"},
+				_("Legal Expenses"): {},
+				_("Marketing Expenses"): {"account_type": "Chargeable"},
+				_("Miscellaneous Expenses"): {"account_type": "Chargeable"},
+				_("Office Maintenance Expenses"): {},
+				_("Office Rent"): {},
+				_("Postal Expenses"): {},
+				_("Print and Stationery"): {},
+				_("Round Off"): {"account_type": "Round Off"},
+				_("Salary"): {},
+				_("Sales Expenses"): {},
+				_("Telephone Expenses"): {},
+				_("Travel Expenses"): {},
+				_("Utility Expenses"): {},
 				_("Write Off"): {},
 				_("Exchange Gain/Loss"): {},
-				_("Gain/Loss on Asset Disposal"): {}
-	        },
-			"root_type": "Expense"
-	    },
-	    _("Income"): {
-	        _("Direct Income"): {
-	            _("Sales"): {},
-	            _("Service"): {}
-	        },
-	        _("Indirect Income"): {
-				"is_group": 1
-	        },
-	        "root_type": "Income"
-	    },
-	    _("Source of Funds (Liabilities)"): {
-	        _("Current Liabilities"): {
-			    _("Accounts Payable"): {
-			        _("Creditors"): {
-			            "account_type": "Payable"
-			        },
-			        _("Payroll Payable"): {},
-			    },
-			    _("Stock Liabilities"): {
-				    _("Stock Received But Not Billed"): {
-				        "account_type": "Stock Received But Not Billed"
-				    },
-                    _("Asset Received But Not Billed"): {
-                        "account_type": "Asset Received But Not Billed"
-                    }
-			    },
-				_("Duties and Taxes"): {
-					"account_type": "Tax",
-					"is_group": 1
+				_("Gain/Loss on Asset Disposal"): {},
+			},
+			"root_type": "Expense",
+		},
+		_("Income"): {
+			_("Direct Income"): {_("Sales"): {}, _("Service"): {}},
+			_("Indirect Income"): {"is_group": 1},
+			"root_type": "Income",
+		},
+		_("Source of Funds (Liabilities)"): {
+			_("Current Liabilities"): {
+				_("Accounts Payable"): {
+					_("Creditors"): {"account_type": "Payable"},
+					_("Payroll Payable"): {},
 				},
+				_("Stock Liabilities"): {
+					_("Stock Received But Not Billed"): {"account_type": "Stock Received But Not Billed"},
+					_("Asset Received But Not Billed"): {"account_type": "Asset Received But Not Billed"},
+				},
+				_("Duties and Taxes"): {"account_type": "Tax", "is_group": 1},
 				_("Loans (Liabilities)"): {
 					_("Secured Loans"): {},
 					_("Unsecured Loans"): {},
 					_("Bank Overdraft Account"): {},
 				},
-	        },
-			"root_type": "Liability"
-	    },
+			},
+			"root_type": "Liability",
+		},
 		_("Equity"): {
-	        _("Capital Stock"): {
-	            "account_type": "Equity"
-	        },
-	        _("Dividends Paid"): {
-	            "account_type": "Equity"
-	        },
-	        _("Opening Balance Equity"): {
-	            "account_type": "Equity"
-	        },
-	        _("Retained Earnings"): {
-	            "account_type": "Equity"
-	        },
-			"root_type": "Equity"
-		}
+			_("Capital Stock"): {"account_type": "Equity"},
+			_("Dividends Paid"): {"account_type": "Equity"},
+			_("Opening Balance Equity"): {"account_type": "Equity"},
+			_("Retained Earnings"): {"account_type": "Equity"},
+			"root_type": "Equity",
+		},
 	}
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py b/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py
index 31ae171..0e46f1e 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py
@@ -6,288 +6,153 @@
 
 
 def get():
-    return {
-        _("Application of Funds (Assets)"): {
-            _("Current Assets"): {
-                _("Accounts Receivable"): {
-                    _("Debtors"): {
-                        "account_type": "Receivable",
-                        "account_number": "1310"
-                    },
-                    "account_number": "1300"
-                },
-                _("Bank Accounts"): {
-                    "account_type": "Bank",
-                    "is_group": 1,
-                    "account_number": "1200"
-                },
-                _("Cash In Hand"): {
-                    _("Cash"): {
-                        "account_type": "Cash",
-                        "account_number": "1110"
-                    },
-                    "account_type": "Cash",
-                    "account_number": "1100"
-                },
-                _("Loans and Advances (Assets)"): {
-                    _("Employee Advances"): {
-                        "account_number": "1610"
-                    },
-                    "account_number": "1600"
-                },
-                _("Securities and Deposits"): {
-                    _("Earnest Money"): {
-                        "account_number": "1651"
-                    },
-                    "account_number": "1650"
-                },
-                _("Stock Assets"): {
-                    _("Stock In Hand"): {
-                        "account_type": "Stock",
-                        "account_number": "1410"
-                    },
-                    "account_type": "Stock",
-                    "account_number": "1400"
-                },
-                _("Tax Assets"): {
-                    "is_group": 1,
-                    "account_number": "1500"
-                },
-                "account_number": "1100-1600"
-            },
-            _("Fixed Assets"): {
-                _("Capital Equipments"): {
-                    "account_type": "Fixed Asset",
-                    "account_number": "1710"
-                },
-                _("Electronic Equipments"): {
-                    "account_type": "Fixed Asset",
-                    "account_number": "1720"
-                },
-                _("Furnitures and Fixtures"): {
-                    "account_type": "Fixed Asset",
-                    "account_number": "1730"
-                },
-                _("Office Equipments"): {
-                    "account_type": "Fixed Asset",
-                    "account_number": "1740"
-                },
-                _("Plants and Machineries"): {
-                    "account_type": "Fixed Asset",
-                    "account_number": "1750"
-                },
-                _("Buildings"): {
-                    "account_type": "Fixed Asset",
-                    "account_number": "1760"
-                },
-                _("Softwares"): {
-                    "account_type": "Fixed Asset",
-                    "account_number": "1770"
-                },
-                _("Accumulated Depreciation"): {
-                    "account_type": "Accumulated Depreciation",
-                    "account_number": "1780"
-                },
-                _("CWIP Account"): {
-                    "account_type": "Capital Work in Progress",
-                    "account_number": "1790"
-                },
-                "account_number": "1700"
-            },
-            _("Investments"): {
-                "is_group": 1,
-                "account_number": "1800"
-            },
-            _("Temporary Accounts"): {
-                _("Temporary Opening"): {
-                    "account_type": "Temporary",
-                    "account_number": "1910"
-                },
-                "account_number": "1900"
-            },
-            "root_type": "Asset",
-            "account_number": "1000"
-        },
-        _("Expenses"): {
-            _("Direct Expenses"): {
-                _("Stock Expenses"): {
-                    _("Cost of Goods Sold"): {
-                        "account_type": "Cost of Goods Sold",
-                        "account_number": "5111"
-                    },
-                    _("Expenses Included In Asset Valuation"): {
-                        "account_type": "Expenses Included In Asset Valuation",
-                        "account_number": "5112"
-                    },
-                    _("Expenses Included In Valuation"): {
-                        "account_type": "Expenses Included In Valuation",
-                        "account_number": "5118"
-                    },
-                    _("Stock Adjustment"): {
-                        "account_type": "Stock Adjustment",
-                        "account_number": "5119"
-                    },
-                    "account_number": "5110"
-                },
-                "account_number": "5100"
-            },
-            _("Indirect Expenses"): {
-                _("Administrative Expenses"): {
-                    "account_number": "5201"
-                },
-                _("Commission on Sales"): {
-                    "account_number": "5202"
-                },
-                _("Depreciation"): {
-                    "account_type": "Depreciation",
-                    "account_number": "5203"
-                },
-                _("Entertainment Expenses"): {
-                    "account_number": "5204"
-                },
-                _("Freight and Forwarding Charges"): {
-                    "account_type": "Chargeable",
-                    "account_number": "5205"
-                },
-                _("Legal Expenses"): {
-                    "account_number": "5206"
-                },
-                _("Marketing Expenses"): {
-                    "account_type": "Chargeable",
-                    "account_number": "5207"
-                },
-                _("Office Maintenance Expenses"): {
-                    "account_number": "5208"
-                },
-                _("Office Rent"): {
-                    "account_number": "5209"
-                },
-                _("Postal Expenses"): {
-                    "account_number": "5210"
-                },
-                _("Print and Stationery"): {
-                    "account_number": "5211"
-                },
-                _("Round Off"): {
-                    "account_type": "Round Off",
-                    "account_number": "5212"
-                },
-                _("Salary"): {
-                    "account_number": "5213"
-                },
-                _("Sales Expenses"): {
-                    "account_number": "5214"
-                },
-                _("Telephone Expenses"): {
-                    "account_number": "5215"
-                },
-                _("Travel Expenses"): {
-                    "account_number": "5216"
-                },
-                _("Utility Expenses"): {
-                    "account_number": "5217"
-                },
-                _("Write Off"): {
-                    "account_number": "5218"
-                },
-                _("Exchange Gain/Loss"): {
-                    "account_number": "5219"
-                },
-                _("Gain/Loss on Asset Disposal"): {
-                    "account_number": "5220"
-                },
-                _("Miscellaneous Expenses"): {
-                    "account_type": "Chargeable",
-                    "account_number": "5221"
-                },
-                "account_number": "5200"
-            },
-            "root_type": "Expense",
-            "account_number": "5000"
-        },
-        _("Income"): {
-            _("Direct Income"): {
-                _("Sales"): {
-                    "account_number": "4110"
-                },
-                _("Service"): {
-                    "account_number": "4120"
-                },
-                "account_number": "4100"
-            },
-            _("Indirect Income"): {
-                "is_group": 1,
-                "account_number": "4200"
-            },
-            "root_type": "Income",
-            "account_number": "4000"
-        },
-        _("Source of Funds (Liabilities)"): {
-            _("Current Liabilities"): {
-                _("Accounts Payable"): {
-                    _("Creditors"): {
-                        "account_type": "Payable",
-                        "account_number": "2110"
-                    },
-                    _("Payroll Payable"): {
-                        "account_number": "2120"
-                    },
-                    "account_number": "2100"
-                },
-                _("Stock Liabilities"): {
-                    _("Stock Received But Not Billed"): {
-                        "account_type": "Stock Received But Not Billed",
-                        "account_number": "2210"
-                    },
-                    _("Asset Received But Not Billed"): {
-                        "account_type": "Asset Received But Not Billed",
-                        "account_number": "2211"
-                    },
-                    "account_number": "2200"
-                },
-                _("Duties and Taxes"): {
-                    _("TDS Payable"): {
-                        "account_number": "2310"
-                    },
-                    "account_type": "Tax",
-                    "is_group": 1,
-                    "account_number": "2300"
-                },
-                _("Loans (Liabilities)"): {
-                    _("Secured Loans"): {
-                        "account_number": "2410"
-                    },
-                    _("Unsecured Loans"): {
-                        "account_number": "2420"
-                    },
-                    _("Bank Overdraft Account"): {
-                        "account_number": "2430"
-                    },
-                    "account_number": "2400"
-                },
-                "account_number": "2100-2400"
-            },
-            "root_type": "Liability",
-            "account_number": "2000"
-        },
-        _("Equity"): {
-            _("Capital Stock"): {
-                "account_type": "Equity",
-                "account_number": "3100"
-            },
-            _("Dividends Paid"): {
-                "account_type": "Equity",
-                "account_number": "3200"
-            },
-            _("Opening Balance Equity"): {
-                "account_type": "Equity",
-                "account_number": "3300"
-            },
-            _("Retained Earnings"): {
-                "account_type": "Equity",
-                "account_number": "3400"
-            },
-            "root_type": "Equity",
-            "account_number": "3000"
-        }
-    }
+	return {
+		_("Application of Funds (Assets)"): {
+			_("Current Assets"): {
+				_("Accounts Receivable"): {
+					_("Debtors"): {"account_type": "Receivable", "account_number": "1310"},
+					"account_number": "1300",
+				},
+				_("Bank Accounts"): {"account_type": "Bank", "is_group": 1, "account_number": "1200"},
+				_("Cash In Hand"): {
+					_("Cash"): {"account_type": "Cash", "account_number": "1110"},
+					"account_type": "Cash",
+					"account_number": "1100",
+				},
+				_("Loans and Advances (Assets)"): {
+					_("Employee Advances"): {"account_number": "1610"},
+					"account_number": "1600",
+				},
+				_("Securities and Deposits"): {
+					_("Earnest Money"): {"account_number": "1651"},
+					"account_number": "1650",
+				},
+				_("Stock Assets"): {
+					_("Stock In Hand"): {"account_type": "Stock", "account_number": "1410"},
+					"account_type": "Stock",
+					"account_number": "1400",
+				},
+				_("Tax Assets"): {"is_group": 1, "account_number": "1500"},
+				"account_number": "1100-1600",
+			},
+			_("Fixed Assets"): {
+				_("Capital Equipments"): {"account_type": "Fixed Asset", "account_number": "1710"},
+				_("Electronic Equipments"): {"account_type": "Fixed Asset", "account_number": "1720"},
+				_("Furnitures and Fixtures"): {"account_type": "Fixed Asset", "account_number": "1730"},
+				_("Office Equipments"): {"account_type": "Fixed Asset", "account_number": "1740"},
+				_("Plants and Machineries"): {"account_type": "Fixed Asset", "account_number": "1750"},
+				_("Buildings"): {"account_type": "Fixed Asset", "account_number": "1760"},
+				_("Softwares"): {"account_type": "Fixed Asset", "account_number": "1770"},
+				_("Accumulated Depreciation"): {
+					"account_type": "Accumulated Depreciation",
+					"account_number": "1780",
+				},
+				_("CWIP Account"): {"account_type": "Capital Work in Progress", "account_number": "1790"},
+				"account_number": "1700",
+			},
+			_("Investments"): {"is_group": 1, "account_number": "1800"},
+			_("Temporary Accounts"): {
+				_("Temporary Opening"): {"account_type": "Temporary", "account_number": "1910"},
+				"account_number": "1900",
+			},
+			"root_type": "Asset",
+			"account_number": "1000",
+		},
+		_("Expenses"): {
+			_("Direct Expenses"): {
+				_("Stock Expenses"): {
+					_("Cost of Goods Sold"): {"account_type": "Cost of Goods Sold", "account_number": "5111"},
+					_("Expenses Included In Asset Valuation"): {
+						"account_type": "Expenses Included In Asset Valuation",
+						"account_number": "5112",
+					},
+					_("Expenses Included In Valuation"): {
+						"account_type": "Expenses Included In Valuation",
+						"account_number": "5118",
+					},
+					_("Stock Adjustment"): {"account_type": "Stock Adjustment", "account_number": "5119"},
+					"account_number": "5110",
+				},
+				"account_number": "5100",
+			},
+			_("Indirect Expenses"): {
+				_("Administrative Expenses"): {"account_number": "5201"},
+				_("Commission on Sales"): {"account_number": "5202"},
+				_("Depreciation"): {"account_type": "Depreciation", "account_number": "5203"},
+				_("Entertainment Expenses"): {"account_number": "5204"},
+				_("Freight and Forwarding Charges"): {"account_type": "Chargeable", "account_number": "5205"},
+				_("Legal Expenses"): {"account_number": "5206"},
+				_("Marketing Expenses"): {"account_type": "Chargeable", "account_number": "5207"},
+				_("Office Maintenance Expenses"): {"account_number": "5208"},
+				_("Office Rent"): {"account_number": "5209"},
+				_("Postal Expenses"): {"account_number": "5210"},
+				_("Print and Stationery"): {"account_number": "5211"},
+				_("Round Off"): {"account_type": "Round Off", "account_number": "5212"},
+				_("Salary"): {"account_number": "5213"},
+				_("Sales Expenses"): {"account_number": "5214"},
+				_("Telephone Expenses"): {"account_number": "5215"},
+				_("Travel Expenses"): {"account_number": "5216"},
+				_("Utility Expenses"): {"account_number": "5217"},
+				_("Write Off"): {"account_number": "5218"},
+				_("Exchange Gain/Loss"): {"account_number": "5219"},
+				_("Gain/Loss on Asset Disposal"): {"account_number": "5220"},
+				_("Miscellaneous Expenses"): {"account_type": "Chargeable", "account_number": "5221"},
+				"account_number": "5200",
+			},
+			"root_type": "Expense",
+			"account_number": "5000",
+		},
+		_("Income"): {
+			_("Direct Income"): {
+				_("Sales"): {"account_number": "4110"},
+				_("Service"): {"account_number": "4120"},
+				"account_number": "4100",
+			},
+			_("Indirect Income"): {"is_group": 1, "account_number": "4200"},
+			"root_type": "Income",
+			"account_number": "4000",
+		},
+		_("Source of Funds (Liabilities)"): {
+			_("Current Liabilities"): {
+				_("Accounts Payable"): {
+					_("Creditors"): {"account_type": "Payable", "account_number": "2110"},
+					_("Payroll Payable"): {"account_number": "2120"},
+					"account_number": "2100",
+				},
+				_("Stock Liabilities"): {
+					_("Stock Received But Not Billed"): {
+						"account_type": "Stock Received But Not Billed",
+						"account_number": "2210",
+					},
+					_("Asset Received But Not Billed"): {
+						"account_type": "Asset Received But Not Billed",
+						"account_number": "2211",
+					},
+					"account_number": "2200",
+				},
+				_("Duties and Taxes"): {
+					_("TDS Payable"): {"account_number": "2310"},
+					"account_type": "Tax",
+					"is_group": 1,
+					"account_number": "2300",
+				},
+				_("Loans (Liabilities)"): {
+					_("Secured Loans"): {"account_number": "2410"},
+					_("Unsecured Loans"): {"account_number": "2420"},
+					_("Bank Overdraft Account"): {"account_number": "2430"},
+					"account_number": "2400",
+				},
+				"account_number": "2100-2400",
+			},
+			"root_type": "Liability",
+			"account_number": "2000",
+		},
+		_("Equity"): {
+			_("Capital Stock"): {"account_type": "Equity", "account_number": "3100"},
+			_("Dividends Paid"): {"account_type": "Equity", "account_number": "3200"},
+			_("Opening Balance Equity"): {"account_type": "Equity", "account_number": "3300"},
+			_("Retained Earnings"): {"account_type": "Equity", "account_number": "3400"},
+			"root_type": "Equity",
+			"account_number": "3000",
+		},
+	}
diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py
index 0715823..f9c9173 100644
--- a/erpnext/accounts/doctype/account/test_account.py
+++ b/erpnext/accounts/doctype/account/test_account.py
@@ -20,8 +20,9 @@
 			acc.company = "_Test Company"
 			acc.insert()
 
-		account_number, account_name = frappe.db.get_value("Account", "1210 - Debtors - _TC",
-			["account_number", "account_name"])
+		account_number, account_name = frappe.db.get_value(
+			"Account", "1210 - Debtors - _TC", ["account_number", "account_name"]
+		)
 		self.assertEqual(account_number, "1210")
 		self.assertEqual(account_name, "Debtors")
 
@@ -30,8 +31,12 @@
 
 		update_account_number("1210 - Debtors - _TC", new_account_name, new_account_number)
 
-		new_acc = frappe.db.get_value("Account", "1211-11-4 - 6 - - Debtors 1 - Test - - _TC",
-			["account_name", "account_number"], as_dict=1)
+		new_acc = frappe.db.get_value(
+			"Account",
+			"1211-11-4 - 6 - - Debtors 1 - Test - - _TC",
+			["account_name", "account_number"],
+			as_dict=1,
+		)
 
 		self.assertEqual(new_acc.account_name, "Debtors 1 - Test -")
 		self.assertEqual(new_acc.account_number, "1211-11-4 - 6 -")
@@ -79,7 +84,9 @@
 
 		self.assertEqual(parent, "Securities and Deposits - _TC")
 
-		merge_account("Securities and Deposits - _TC", "Cash In Hand - _TC", doc.is_group, doc.root_type, doc.company)
+		merge_account(
+			"Securities and Deposits - _TC", "Cash In Hand - _TC", doc.is_group, doc.root_type, doc.company
+		)
 		parent = frappe.db.get_value("Account", "Earnest Money - _TC", "parent_account")
 
 		# Parent account of the child account changes after merging
@@ -91,14 +98,28 @@
 		doc = frappe.get_doc("Account", "Current Assets - _TC")
 
 		# Raise error as is_group property doesn't match
-		self.assertRaises(frappe.ValidationError, merge_account, "Current Assets - _TC",\
-			"Accumulated Depreciation - _TC", doc.is_group, doc.root_type, doc.company)
+		self.assertRaises(
+			frappe.ValidationError,
+			merge_account,
+			"Current Assets - _TC",
+			"Accumulated Depreciation - _TC",
+			doc.is_group,
+			doc.root_type,
+			doc.company,
+		)
 
 		doc = frappe.get_doc("Account", "Capital Stock - _TC")
 
 		# Raise error as root_type property doesn't match
-		self.assertRaises(frappe.ValidationError, merge_account, "Capital Stock - _TC",\
-			"Softwares - _TC", doc.is_group, doc.root_type, doc.company)
+		self.assertRaises(
+			frappe.ValidationError,
+			merge_account,
+			"Capital Stock - _TC",
+			"Softwares - _TC",
+			doc.is_group,
+			doc.root_type,
+			doc.company,
+		)
 
 	def test_account_sync(self):
 		frappe.local.flags.pop("ignore_root_company_validation", None)
@@ -109,8 +130,12 @@
 		acc.company = "_Test Company 3"
 		acc.insert()
 
-		acc_tc_4 = frappe.db.get_value('Account', {'account_name': "Test Sync Account", "company": "_Test Company 4"})
-		acc_tc_5 = frappe.db.get_value('Account', {'account_name': "Test Sync Account", "company": "_Test Company 5"})
+		acc_tc_4 = frappe.db.get_value(
+			"Account", {"account_name": "Test Sync Account", "company": "_Test Company 4"}
+		)
+		acc_tc_5 = frappe.db.get_value(
+			"Account", {"account_name": "Test Sync Account", "company": "_Test Company 5"}
+		)
 		self.assertEqual(acc_tc_4, "Test Sync Account - _TC4")
 		self.assertEqual(acc_tc_5, "Test Sync Account - _TC5")
 
@@ -138,8 +163,26 @@
 		update_account_number(acc.name, "Test Rename Sync Account", "1234")
 
 		# Check if renamed in children
-		self.assertTrue(frappe.db.exists("Account", {'account_name': "Test Rename Sync Account", "company": "_Test Company 4", "account_number": "1234"}))
-		self.assertTrue(frappe.db.exists("Account", {'account_name': "Test Rename Sync Account", "company": "_Test Company 5", "account_number": "1234"}))
+		self.assertTrue(
+			frappe.db.exists(
+				"Account",
+				{
+					"account_name": "Test Rename Sync Account",
+					"company": "_Test Company 4",
+					"account_number": "1234",
+				},
+			)
+		)
+		self.assertTrue(
+			frappe.db.exists(
+				"Account",
+				{
+					"account_name": "Test Rename Sync Account",
+					"company": "_Test Company 5",
+					"account_number": "1234",
+				},
+			)
+		)
 
 		frappe.delete_doc("Account", "1234 - Test Rename Sync Account - _TC3")
 		frappe.delete_doc("Account", "1234 - Test Rename Sync Account - _TC4")
@@ -155,25 +198,71 @@
 		acc.company = "_Test Company 3"
 		acc.insert()
 
-		self.assertTrue(frappe.db.exists("Account", {'account_name': "Test Group Account", "company": "_Test Company 4"}))
-		self.assertTrue(frappe.db.exists("Account", {'account_name': "Test Group Account", "company": "_Test Company 5"}))
+		self.assertTrue(
+			frappe.db.exists(
+				"Account", {"account_name": "Test Group Account", "company": "_Test Company 4"}
+			)
+		)
+		self.assertTrue(
+			frappe.db.exists(
+				"Account", {"account_name": "Test Group Account", "company": "_Test Company 5"}
+			)
+		)
 
 		# Try renaming child company account
-		acc_tc_5 = frappe.db.get_value('Account', {'account_name': "Test Group Account", "company": "_Test Company 5"})
-		self.assertRaises(frappe.ValidationError, update_account_number, acc_tc_5, "Test Modified Account")
+		acc_tc_5 = frappe.db.get_value(
+			"Account", {"account_name": "Test Group Account", "company": "_Test Company 5"}
+		)
+		self.assertRaises(
+			frappe.ValidationError, update_account_number, acc_tc_5, "Test Modified Account"
+		)
 
 		# Rename child company account with allow_account_creation_against_child_company enabled
-		frappe.db.set_value("Company", "_Test Company 5", "allow_account_creation_against_child_company", 1)
+		frappe.db.set_value(
+			"Company", "_Test Company 5", "allow_account_creation_against_child_company", 1
+		)
 
 		update_account_number(acc_tc_5, "Test Modified Account")
-		self.assertTrue(frappe.db.exists("Account", {'name': "Test Modified Account - _TC5", "company": "_Test Company 5"}))
+		self.assertTrue(
+			frappe.db.exists(
+				"Account", {"name": "Test Modified Account - _TC5", "company": "_Test Company 5"}
+			)
+		)
 
-		frappe.db.set_value("Company", "_Test Company 5", "allow_account_creation_against_child_company", 0)
+		frappe.db.set_value(
+			"Company", "_Test Company 5", "allow_account_creation_against_child_company", 0
+		)
 
-		to_delete = ["Test Group Account - _TC3", "Test Group Account - _TC4", "Test Modified Account - _TC5"]
+		to_delete = [
+			"Test Group Account - _TC3",
+			"Test Group Account - _TC4",
+			"Test Modified Account - _TC5",
+		]
 		for doc in to_delete:
 			frappe.delete_doc("Account", doc)
 
+	def test_validate_account_currency(self):
+		from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
+
+		if not frappe.db.get_value("Account", "Test Currency Account - _TC"):
+			acc = frappe.new_doc("Account")
+			acc.account_name = "Test Currency Account"
+			acc.parent_account = "Tax Assets - _TC"
+			acc.company = "_Test Company"
+			acc.insert()
+		else:
+			acc = frappe.get_doc("Account", "Test Currency Account - _TC")
+
+		self.assertEqual(acc.account_currency, "INR")
+
+		# Make a JV against this account
+		make_journal_entry(
+			"Test Currency Account - _TC", "Miscellaneous Expenses - _TC", 100, submit=True
+		)
+
+		acc.account_currency = "USD"
+		self.assertRaises(frappe.ValidationError, acc.save)
+
 
 def _make_test_records(verbose=None):
 	from frappe.test_runner import make_test_objects
@@ -184,20 +273,16 @@
 		["_Test Bank USD", "Bank Accounts", 0, "Bank", "USD"],
 		["_Test Bank EUR", "Bank Accounts", 0, "Bank", "EUR"],
 		["_Test Cash", "Cash In Hand", 0, "Cash", None],
-
 		["_Test Account Stock Expenses", "Direct Expenses", 1, None, None],
 		["_Test Account Shipping Charges", "_Test Account Stock Expenses", 0, "Chargeable", None],
 		["_Test Account Customs Duty", "_Test Account Stock Expenses", 0, "Tax", None],
 		["_Test Account Insurance Charges", "_Test Account Stock Expenses", 0, "Chargeable", None],
 		["_Test Account Stock Adjustment", "_Test Account Stock Expenses", 0, "Stock Adjustment", None],
 		["_Test Employee Advance", "Current Liabilities", 0, None, None],
-
 		["_Test Account Tax Assets", "Current Assets", 1, None, None],
 		["_Test Account VAT", "_Test Account Tax Assets", 0, "Tax", None],
 		["_Test Account Service Tax", "_Test Account Tax Assets", 0, "Tax", None],
-
 		["_Test Account Reserves and Surplus", "Current Liabilities", 0, None, None],
-
 		["_Test Account Cost for Goods Sold", "Expenses", 0, None, None],
 		["_Test Account Excise Duty", "_Test Account Tax Assets", 0, "Tax", None],
 		["_Test Account Education Cess", "_Test Account Tax Assets", 0, "Tax", None],
@@ -206,38 +291,45 @@
 		["_Test Account Discount", "Direct Expenses", 0, None, None],
 		["_Test Write Off", "Indirect Expenses", 0, None, None],
 		["_Test Exchange Gain/Loss", "Indirect Expenses", 0, None, None],
-
 		["_Test Account Sales", "Direct Income", 0, None, None],
-
 		# related to Account Inventory Integration
 		["_Test Account Stock In Hand", "Current Assets", 0, None, None],
-
 		# fixed asset depreciation
 		["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None],
 		["_Test Accumulated Depreciations", "Current Assets", 0, "Accumulated Depreciation", None],
 		["_Test Depreciations", "Expenses", 0, None, None],
 		["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None],
-
 		# Receivable / Payable Account
 		["_Test Receivable", "Current Assets", 0, "Receivable", None],
 		["_Test Payable", "Current Liabilities", 0, "Payable", None],
 		["_Test Receivable USD", "Current Assets", 0, "Receivable", "USD"],
-		["_Test Payable USD", "Current Liabilities", 0, "Payable", "USD"]
+		["_Test Payable USD", "Current Liabilities", 0, "Payable", "USD"],
 	]
 
-	for company, abbr in [["_Test Company", "_TC"], ["_Test Company 1", "_TC1"], ["_Test Company with perpetual inventory", "TCP1"]]:
-		test_objects = make_test_objects("Account", [{
-				"doctype": "Account",
-				"account_name": account_name,
-				"parent_account": parent_account + " - " + abbr,
-				"company": company,
-				"is_group": is_group,
-				"account_type": account_type,
-				"account_currency": currency
-			} for account_name, parent_account, is_group, account_type, currency in accounts])
+	for company, abbr in [
+		["_Test Company", "_TC"],
+		["_Test Company 1", "_TC1"],
+		["_Test Company with perpetual inventory", "TCP1"],
+	]:
+		test_objects = make_test_objects(
+			"Account",
+			[
+				{
+					"doctype": "Account",
+					"account_name": account_name,
+					"parent_account": parent_account + " - " + abbr,
+					"company": company,
+					"is_group": is_group,
+					"account_type": account_type,
+					"account_currency": currency,
+				}
+				for account_name, parent_account, is_group, account_type, currency in accounts
+			],
+		)
 
 	return test_objects
 
+
 def get_inventory_account(company, warehouse=None):
 	account = None
 	if warehouse:
@@ -247,19 +339,24 @@
 
 	return account
 
+
 def create_account(**kwargs):
-	account = frappe.db.get_value("Account", filters={"account_name": kwargs.get("account_name"), "company": kwargs.get("company")})
+	account = frappe.db.get_value(
+		"Account", filters={"account_name": kwargs.get("account_name"), "company": kwargs.get("company")}
+	)
 	if account:
 		return account
 	else:
-		account = frappe.get_doc(dict(
-			doctype = "Account",
-			account_name = kwargs.get('account_name'),
-			account_type = kwargs.get('account_type'),
-			parent_account = kwargs.get('parent_account'),
-			company = kwargs.get('company'),
-			account_currency = kwargs.get('account_currency')
-		))
+		account = frappe.get_doc(
+			dict(
+				doctype="Account",
+				account_name=kwargs.get("account_name"),
+				account_type=kwargs.get("account_type"),
+				parent_account=kwargs.get("parent_account"),
+				company=kwargs.get("company"),
+				account_currency=kwargs.get("account_currency"),
+			)
+		)
 
 		account.save()
 		return account.name
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index b6112e0..ce1ed33 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -17,13 +17,21 @@
 		self.set_fieldname_and_label()
 
 	def validate(self):
-		if self.document_type in core_doctypes_list + ('Accounting Dimension', 'Project',
-				'Cost Center', 'Accounting Dimension Detail', 'Company', 'Account') :
+		if self.document_type in core_doctypes_list + (
+			"Accounting Dimension",
+			"Project",
+			"Cost Center",
+			"Accounting Dimension Detail",
+			"Company",
+			"Account",
+		):
 
 			msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
 			frappe.throw(msg)
 
-		exists = frappe.db.get_value("Accounting Dimension", {'document_type': self.document_type}, ['name'])
+		exists = frappe.db.get_value(
+			"Accounting Dimension", {"document_type": self.document_type}, ["name"]
+		)
 
 		if exists and self.is_new():
 			frappe.throw(_("Document Type already used as a dimension"))
@@ -42,13 +50,13 @@
 		if frappe.flags.in_test:
 			make_dimension_in_accounting_doctypes(doc=self)
 		else:
-			frappe.enqueue(make_dimension_in_accounting_doctypes, doc=self, queue='long')
+			frappe.enqueue(make_dimension_in_accounting_doctypes, doc=self, queue="long")
 
 	def on_trash(self):
 		if frappe.flags.in_test:
 			delete_accounting_dimension(doc=self)
 		else:
-			frappe.enqueue(delete_accounting_dimension, doc=self, queue='long')
+			frappe.enqueue(delete_accounting_dimension, doc=self, queue="long")
 
 	def set_fieldname_and_label(self):
 		if not self.label:
@@ -60,6 +68,7 @@
 	def on_update(self):
 		frappe.flags.accounting_dimensions = None
 
+
 def make_dimension_in_accounting_doctypes(doc, doclist=None):
 	if not doclist:
 		doclist = get_doctypes_with_dimensions()
@@ -70,9 +79,9 @@
 	for doctype in doclist:
 
 		if (doc_count + 1) % 2 == 0:
-			insert_after_field = 'dimension_col_break'
+			insert_after_field = "dimension_col_break"
 		else:
-			insert_after_field = 'accounting_dimensions_section'
+			insert_after_field = "accounting_dimensions_section"
 
 		df = {
 			"fieldname": doc.fieldname,
@@ -80,30 +89,33 @@
 			"fieldtype": "Link",
 			"options": doc.document_type,
 			"insert_after": insert_after_field,
-			"owner": "Administrator"
+			"owner": "Administrator",
 		}
 
 		meta = frappe.get_meta(doctype, cached=False)
 		fieldnames = [d.fieldname for d in meta.get("fields")]
 
-		if df['fieldname'] not in fieldnames:
+		if df["fieldname"] not in fieldnames:
 			if doctype == "Budget":
 				add_dimension_to_budget_doctype(df.copy(), doc)
 			else:
-				create_custom_field(doctype, df)
+				create_custom_field(doctype, df, ignore_validate=True)
 
 		count += 1
 
-		frappe.publish_progress(count*100/len(doclist), title = _("Creating Dimensions..."))
+		frappe.publish_progress(count * 100 / len(doclist), title=_("Creating Dimensions..."))
 		frappe.clear_cache(doctype=doctype)
 
-def add_dimension_to_budget_doctype(df, doc):
-	df.update({
-		"insert_after": "cost_center",
-		"depends_on": "eval:doc.budget_against == '{0}'".format(doc.document_type)
-	})
 
-	create_custom_field("Budget", df)
+def add_dimension_to_budget_doctype(df, doc):
+	df.update(
+		{
+			"insert_after": "cost_center",
+			"depends_on": "eval:doc.budget_against == '{0}'".format(doc.document_type),
+		}
+	)
+
+	create_custom_field("Budget", df, ignore_validate=True)
 
 	property_setter = frappe.db.exists("Property Setter", "Budget-budget_against-options")
 
@@ -112,36 +124,44 @@
 		property_setter_doc.value = property_setter_doc.value + "\n" + doc.document_type
 		property_setter_doc.save()
 
-		frappe.clear_cache(doctype='Budget')
+		frappe.clear_cache(doctype="Budget")
 	else:
-		frappe.get_doc({
-			"doctype": "Property Setter",
-			"doctype_or_field": "DocField",
-			"doc_type": "Budget",
-			"field_name": "budget_against",
-			"property": "options",
-			"property_type": "Text",
-			"value": "\nCost Center\nProject\n" + doc.document_type
-		}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "Property Setter",
+				"doctype_or_field": "DocField",
+				"doc_type": "Budget",
+				"field_name": "budget_against",
+				"property": "options",
+				"property_type": "Text",
+				"value": "\nCost Center\nProject\n" + doc.document_type,
+			}
+		).insert(ignore_permissions=True)
 
 
 def delete_accounting_dimension(doc):
 	doclist = get_doctypes_with_dimensions()
 
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		DELETE FROM `tabCustom Field`
 		WHERE fieldname = %s
-		AND dt IN (%s)""" %			#nosec
-		('%s', ', '.join(['%s']* len(doclist))), tuple([doc.fieldname] + doclist))
+		AND dt IN (%s)"""
+		% ("%s", ", ".join(["%s"] * len(doclist))),  # nosec
+		tuple([doc.fieldname] + doclist),
+	)
 
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		DELETE FROM `tabProperty Setter`
 		WHERE field_name = %s
-		AND doc_type IN (%s)""" %		#nosec
-		('%s', ', '.join(['%s']* len(doclist))), tuple([doc.fieldname] + doclist))
+		AND doc_type IN (%s)"""
+		% ("%s", ", ".join(["%s"] * len(doclist))),  # nosec
+		tuple([doc.fieldname] + doclist),
+	)
 
 	budget_against_property = frappe.get_doc("Property Setter", "Budget-budget_against-options")
-	value_list = budget_against_property.value.split('\n')[3:]
+	value_list = budget_against_property.value.split("\n")[3:]
 
 	if doc.document_type in value_list:
 		value_list.remove(doc.document_type)
@@ -152,6 +172,7 @@
 	for doctype in doclist:
 		frappe.clear_cache(doctype=doctype)
 
+
 @frappe.whitelist()
 def disable_dimension(doc):
 	if frappe.flags.in_test:
@@ -159,10 +180,11 @@
 	else:
 		frappe.enqueue(toggle_disabling, doc=doc)
 
+
 def toggle_disabling(doc):
 	doc = json.loads(doc)
 
-	if doc.get('disabled'):
+	if doc.get("disabled"):
 		df = {"read_only": 1}
 	else:
 		df = {"read_only": 0}
@@ -170,7 +192,7 @@
 	doclist = get_doctypes_with_dimensions()
 
 	for doctype in doclist:
-		field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": doc.get('fieldname')})
+		field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": doc.get("fieldname")})
 		if field:
 			custom_field = frappe.get_doc("Custom Field", field)
 			custom_field.update(df)
@@ -178,61 +200,82 @@
 
 		frappe.clear_cache(doctype=doctype)
 
+
 def get_doctypes_with_dimensions():
 	return frappe.get_hooks("accounting_dimension_doctypes")
 
-def get_accounting_dimensions(as_list=True):
+
+def get_accounting_dimensions(as_list=True, filters=None):
+
+	if not filters:
+		filters = {"disabled": 0}
+
 	if frappe.flags.accounting_dimensions is None:
-		frappe.flags.accounting_dimensions = frappe.get_all("Accounting Dimension",
-			fields=["label", "fieldname", "disabled", "document_type"])
+		frappe.flags.accounting_dimensions = frappe.get_all(
+			"Accounting Dimension",
+			fields=["label", "fieldname", "disabled", "document_type"],
+			filters=filters,
+		)
 
 	if as_list:
 		return [d.fieldname for d in frappe.flags.accounting_dimensions]
 	else:
 		return frappe.flags.accounting_dimensions
 
+
 def get_checks_for_pl_and_bs_accounts():
-	dimensions = frappe.db.sql("""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
+	dimensions = frappe.db.sql(
+		"""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
 		FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c
-		WHERE p.name = c.parent""", as_dict=1)
+		WHERE p.name = c.parent""",
+		as_dict=1,
+	)
 
 	return dimensions
 
-def get_dimension_with_children(doctype, dimension):
 
-	if isinstance(dimension, list):
-		dimension = dimension[0]
+def get_dimension_with_children(doctype, dimensions):
+
+	if isinstance(dimensions, str):
+		dimensions = [dimensions]
 
 	all_dimensions = []
-	lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"])
-	children = frappe.get_all(doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}, order_by="lft")
-	all_dimensions += [c.name for c in children]
+
+	for dimension in dimensions:
+		lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"])
+		children = frappe.get_all(
+			doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}, order_by="lft"
+		)
+		all_dimensions += [c.name for c in children]
 
 	return all_dimensions
 
+
 @frappe.whitelist()
 def get_dimensions(with_cost_center_and_project=False):
-	dimension_filters = frappe.db.sql("""
+	dimension_filters = frappe.db.sql(
+		"""
 		SELECT label, fieldname, document_type
 		FROM `tabAccounting Dimension`
 		WHERE disabled = 0
-	""", as_dict=1)
+	""",
+		as_dict=1,
+	)
 
-	default_dimensions = frappe.db.sql("""SELECT p.fieldname, c.company, c.default_dimension
+	default_dimensions = frappe.db.sql(
+		"""SELECT p.fieldname, c.company, c.default_dimension
 		FROM `tabAccounting Dimension Detail` c, `tabAccounting Dimension` p
-		WHERE c.parent = p.name""", as_dict=1)
+		WHERE c.parent = p.name""",
+		as_dict=1,
+	)
 
 	if with_cost_center_and_project:
-		dimension_filters.extend([
-			{
-				'fieldname': 'cost_center',
-				'document_type': 'Cost Center'
-			},
-			{
-				'fieldname': 'project',
-				'document_type': 'Project'
-			}
-		])
+		dimension_filters.extend(
+			[
+				{"fieldname": "cost_center", "document_type": "Cost Center"},
+				{"fieldname": "project", "document_type": "Project"},
+			]
+		)
 
 	default_dimensions_map = {}
 	for dimension in default_dimensions:
diff --git a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
index f781a22..25ef2ea 100644
--- a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
@@ -8,7 +8,8 @@
 from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
 from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
 
-test_dependencies = ['Cost Center', 'Location', 'Warehouse', 'Department']
+test_dependencies = ["Cost Center", "Location", "Warehouse", "Department"]
+
 
 class TestAccountingDimension(unittest.TestCase):
 	def setUp(self):
@@ -18,24 +19,27 @@
 		si = create_sales_invoice(do_not_save=1)
 
 		si.location = "Block 1"
-		si.append("items", {
-			"item_code": "_Test Item",
-			"warehouse": "_Test Warehouse - _TC",
-			"qty": 1,
-			"rate": 100,
-			"income_account": "Sales - _TC",
-			"expense_account": "Cost of Goods Sold - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-			"department": "_Test Department - _TC",
-			"location": "Block 1"
-		})
+		si.append(
+			"items",
+			{
+				"item_code": "_Test Item",
+				"warehouse": "_Test Warehouse - _TC",
+				"qty": 1,
+				"rate": 100,
+				"income_account": "Sales - _TC",
+				"expense_account": "Cost of Goods Sold - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+				"department": "_Test Department - _TC",
+				"location": "Block 1",
+			},
+		)
 
 		si.save()
 		si.submit()
 
 		gle = frappe.get_doc("GL Entry", {"voucher_no": si.name, "account": "Sales - _TC"})
 
-		self.assertEqual(gle.get('department'), "_Test Department - _TC")
+		self.assertEqual(gle.get("department"), "_Test Department - _TC")
 
 	def test_dimension_against_journal_entry(self):
 		je = make_journal_entry("Sales - _TC", "Sales Expenses - _TC", 500, save=False)
@@ -50,21 +54,24 @@
 
 		gle = frappe.get_doc("GL Entry", {"voucher_no": je.name, "account": "Sales - _TC"})
 		gle1 = frappe.get_doc("GL Entry", {"voucher_no": je.name, "account": "Sales Expenses - _TC"})
-		self.assertEqual(gle.get('department'), "_Test Department - _TC")
-		self.assertEqual(gle1.get('department'), "_Test Department - _TC")
+		self.assertEqual(gle.get("department"), "_Test Department - _TC")
+		self.assertEqual(gle1.get("department"), "_Test Department - _TC")
 
 	def test_mandatory(self):
 		si = create_sales_invoice(do_not_save=1)
-		si.append("items", {
-			"item_code": "_Test Item",
-			"warehouse": "_Test Warehouse - _TC",
-			"qty": 1,
-			"rate": 100,
-			"income_account": "Sales - _TC",
-			"expense_account": "Cost of Goods Sold - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-			"location": ""
-		})
+		si.append(
+			"items",
+			{
+				"item_code": "_Test Item",
+				"warehouse": "_Test Warehouse - _TC",
+				"qty": 1,
+				"rate": 100,
+				"income_account": "Sales - _TC",
+				"expense_account": "Cost of Goods Sold - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+				"location": "",
+			},
+		)
 
 		si.save()
 		self.assertRaises(frappe.ValidationError, si.submit)
@@ -72,31 +79,39 @@
 	def tearDown(self):
 		disable_dimension()
 
+
 def create_dimension():
 	frappe.set_user("Administrator")
 
 	if not frappe.db.exists("Accounting Dimension", {"document_type": "Department"}):
-		frappe.get_doc({
-			"doctype": "Accounting Dimension",
-			"document_type": "Department",
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Accounting Dimension",
+				"document_type": "Department",
+			}
+		).insert()
 	else:
 		dimension = frappe.get_doc("Accounting Dimension", "Department")
 		dimension.disabled = 0
 		dimension.save()
 
 	if not frappe.db.exists("Accounting Dimension", {"document_type": "Location"}):
-		dimension1 = frappe.get_doc({
-			"doctype": "Accounting Dimension",
-			"document_type": "Location",
-		})
+		dimension1 = frappe.get_doc(
+			{
+				"doctype": "Accounting Dimension",
+				"document_type": "Location",
+			}
+		)
 
-		dimension1.append("dimension_defaults", {
-			"company": "_Test Company",
-			"reference_document": "Location",
-			"default_dimension": "Block 1",
-			"mandatory_for_bs": 1
-		})
+		dimension1.append(
+			"dimension_defaults",
+			{
+				"company": "_Test Company",
+				"reference_document": "Location",
+				"default_dimension": "Block 1",
+				"mandatory_for_bs": 1,
+			},
+		)
 
 		dimension1.insert()
 		dimension1.save()
@@ -105,6 +120,7 @@
 		dimension1.disabled = 0
 		dimension1.save()
 
+
 def disable_dimension():
 	dimension1 = frappe.get_doc("Accounting Dimension", "Department")
 	dimension1.disabled = 1
diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py
index 7d32bad..80f736f 100644
--- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py
+++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py
@@ -19,17 +19,27 @@
 				WHERE d.name = a.parent
 				and d.name != %s
 				and d.accounting_dimension = %s
-			""", (self.name, self.accounting_dimension), as_dict=1)
+			""",
+			(self.name, self.accounting_dimension),
+			as_dict=1,
+		)
 
 		account_list = [d.account for d in accounts]
 
-		for account in self.get('accounts'):
+		for account in self.get("accounts"):
 			if account.applicable_on_account in account_list:
-				frappe.throw(_("Row {0}: {1} account already applied for Accounting Dimension {2}").format(
-					account.idx, frappe.bold(account.applicable_on_account), frappe.bold(self.accounting_dimension)))
+				frappe.throw(
+					_("Row {0}: {1} account already applied for Accounting Dimension {2}").format(
+						account.idx,
+						frappe.bold(account.applicable_on_account),
+						frappe.bold(self.accounting_dimension),
+					)
+				)
+
 
 def get_dimension_filter_map():
-	filters = frappe.db.sql("""
+	filters = frappe.db.sql(
+		"""
 		SELECT
 			a.applicable_on_account, d.dimension_value, p.accounting_dimension,
 			p.allow_or_restrict, a.is_mandatory
@@ -40,22 +50,30 @@
 			p.name = a.parent
 			AND p.disabled = 0
 			AND p.name = d.parent
-	""", as_dict=1)
+	""",
+		as_dict=1,
+	)
 
 	dimension_filter_map = {}
 
 	for f in filters:
 		f.fieldname = scrub(f.accounting_dimension)
 
-		build_map(dimension_filter_map, f.fieldname, f.applicable_on_account, f.dimension_value,
-			f.allow_or_restrict, f.is_mandatory)
+		build_map(
+			dimension_filter_map,
+			f.fieldname,
+			f.applicable_on_account,
+			f.dimension_value,
+			f.allow_or_restrict,
+			f.is_mandatory,
+		)
 
 	return dimension_filter_map
 
+
 def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory):
-	map_object.setdefault((dimension, account), {
-		'allowed_dimensions': [],
-		'is_mandatory': is_mandatory,
-		'allow_or_restrict': allow_or_restrict
-	})
-	map_object[(dimension, account)]['allowed_dimensions'].append(filter_value)
+	map_object.setdefault(
+		(dimension, account),
+		{"allowed_dimensions": [], "is_mandatory": is_mandatory, "allow_or_restrict": allow_or_restrict},
+	)
+	map_object[(dimension, account)]["allowed_dimensions"].append(filter_value)
diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py
index e2f85ba..f13f2f9 100644
--- a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py
+++ b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py
@@ -12,7 +12,8 @@
 from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
 from erpnext.exceptions import InvalidAccountDimensionError, MandatoryAccountDimensionError
 
-test_dependencies = ['Location', 'Cost Center', 'Department']
+test_dependencies = ["Location", "Cost Center", "Department"]
+
 
 class TestAccountingDimensionFilter(unittest.TestCase):
 	def setUp(self):
@@ -22,9 +23,9 @@
 
 	def test_allowed_dimension_validation(self):
 		si = create_sales_invoice(do_not_save=1)
-		si.items[0].cost_center = 'Main - _TC'
-		si.department = 'Accounts - _TC'
-		si.location = 'Block 1'
+		si.items[0].cost_center = "Main - _TC"
+		si.department = "Accounts - _TC"
+		si.location = "Block 1"
 		si.save()
 
 		self.assertRaises(InvalidAccountDimensionError, si.submit)
@@ -32,12 +33,12 @@
 
 	def test_mandatory_dimension_validation(self):
 		si = create_sales_invoice(do_not_save=1)
-		si.department = ''
-		si.location = 'Block 1'
+		si.department = ""
+		si.location = "Block 1"
 
 		# Test with no department for Sales Account
-		si.items[0].department = ''
-		si.items[0].cost_center = '_Test Cost Center 2 - _TC'
+		si.items[0].department = ""
+		si.items[0].cost_center = "_Test Cost Center 2 - _TC"
 		si.save()
 
 		self.assertRaises(MandatoryAccountDimensionError, si.submit)
@@ -52,53 +53,54 @@
 			if si.docstatus == 1:
 				si.cancel()
 
+
 def create_accounting_dimension_filter():
-	if not frappe.db.get_value('Accounting Dimension Filter',
-		{'accounting_dimension': 'Cost Center'}):
-		frappe.get_doc({
-			'doctype': 'Accounting Dimension Filter',
-			'accounting_dimension': 'Cost Center',
-			'allow_or_restrict': 'Allow',
-			'company': '_Test Company',
-			'accounts': [{
-				'applicable_on_account': 'Sales - _TC',
-			}],
-			'dimensions': [{
-				'accounting_dimension': 'Cost Center',
-				'dimension_value': '_Test Cost Center 2 - _TC'
-			}]
-		}).insert()
+	if not frappe.db.get_value(
+		"Accounting Dimension Filter", {"accounting_dimension": "Cost Center"}
+	):
+		frappe.get_doc(
+			{
+				"doctype": "Accounting Dimension Filter",
+				"accounting_dimension": "Cost Center",
+				"allow_or_restrict": "Allow",
+				"company": "_Test Company",
+				"accounts": [
+					{
+						"applicable_on_account": "Sales - _TC",
+					}
+				],
+				"dimensions": [
+					{"accounting_dimension": "Cost Center", "dimension_value": "_Test Cost Center 2 - _TC"}
+				],
+			}
+		).insert()
 	else:
-		doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Cost Center'})
+		doc = frappe.get_doc("Accounting Dimension Filter", {"accounting_dimension": "Cost Center"})
 		doc.disabled = 0
 		doc.save()
 
-	if not frappe.db.get_value('Accounting Dimension Filter',
-		{'accounting_dimension': 'Department'}):
-		frappe.get_doc({
-			'doctype': 'Accounting Dimension Filter',
-			'accounting_dimension': 'Department',
-			'allow_or_restrict': 'Allow',
-			'company': '_Test Company',
-			'accounts': [{
-				'applicable_on_account': 'Sales - _TC',
-				'is_mandatory': 1
-			}],
-			'dimensions': [{
-				'accounting_dimension': 'Department',
-				'dimension_value': 'Accounts - _TC'
-			}]
-		}).insert()
+	if not frappe.db.get_value("Accounting Dimension Filter", {"accounting_dimension": "Department"}):
+		frappe.get_doc(
+			{
+				"doctype": "Accounting Dimension Filter",
+				"accounting_dimension": "Department",
+				"allow_or_restrict": "Allow",
+				"company": "_Test Company",
+				"accounts": [{"applicable_on_account": "Sales - _TC", "is_mandatory": 1}],
+				"dimensions": [{"accounting_dimension": "Department", "dimension_value": "Accounts - _TC"}],
+			}
+		).insert()
 	else:
-		doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Department'})
+		doc = frappe.get_doc("Accounting Dimension Filter", {"accounting_dimension": "Department"})
 		doc.disabled = 0
 		doc.save()
 
+
 def disable_dimension_filter():
-	doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Cost Center'})
+	doc = frappe.get_doc("Accounting Dimension Filter", {"accounting_dimension": "Cost Center"})
 	doc.disabled = 1
 	doc.save()
 
-	doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Department'})
+	doc = frappe.get_doc("Accounting Dimension Filter", {"accounting_dimension": "Department"})
 	doc.disabled = 1
 	doc.save()
diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.py b/erpnext/accounts/doctype/accounting_period/accounting_period.py
index e294937..0c15d6a 100644
--- a/erpnext/accounts/doctype/accounting_period/accounting_period.py
+++ b/erpnext/accounts/doctype/accounting_period/accounting_period.py
@@ -7,7 +7,9 @@
 from frappe.model.document import Document
 
 
-class OverlapError(frappe.ValidationError): pass
+class OverlapError(frappe.ValidationError):
+	pass
+
 
 class AccountingPeriod(Document):
 	def validate(self):
@@ -17,11 +19,12 @@
 		self.bootstrap_doctypes_for_closing()
 
 	def autoname(self):
-		company_abbr = frappe.get_cached_value('Company',  self.company,  "abbr")
+		company_abbr = frappe.get_cached_value("Company", self.company, "abbr")
 		self.name = " - ".join([self.period_name, company_abbr])
 
 	def validate_overlap(self):
-		existing_accounting_period = frappe.db.sql("""select name from `tabAccounting Period`
+		existing_accounting_period = frappe.db.sql(
+			"""select name from `tabAccounting Period`
 			where (
 				(%(start_date)s between start_date and end_date)
 				or (%(end_date)s between start_date and end_date)
@@ -32,18 +35,29 @@
 				"start_date": self.start_date,
 				"end_date": self.end_date,
 				"name": self.name,
-				"company": self.company
-			}, as_dict=True)
+				"company": self.company,
+			},
+			as_dict=True,
+		)
 
 		if len(existing_accounting_period) > 0:
-			frappe.throw(_("Accounting Period overlaps with {0}")
-				.format(existing_accounting_period[0].get("name")), OverlapError)
+			frappe.throw(
+				_("Accounting Period overlaps with {0}").format(existing_accounting_period[0].get("name")),
+				OverlapError,
+			)
 
 	@frappe.whitelist()
 	def get_doctypes_for_closing(self):
 		docs_for_closing = []
-		doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", \
-			"Bank Clearance", "Asset", "Stock Entry"]
+		doctypes = [
+			"Sales Invoice",
+			"Purchase Invoice",
+			"Journal Entry",
+			"Payroll Entry",
+			"Bank Clearance",
+			"Asset",
+			"Stock Entry",
+		]
 		closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes]
 		for closed_doctype in closed_doctypes:
 			docs_for_closing.append(closed_doctype)
@@ -53,7 +67,7 @@
 	def bootstrap_doctypes_for_closing(self):
 		if len(self.closed_documents) == 0:
 			for doctype_for_closing in self.get_doctypes_for_closing():
-				self.append('closed_documents', {
-					"document_type": doctype_for_closing.document_type,
-					"closed": doctype_for_closing.closed
-				})
+				self.append(
+					"closed_documents",
+					{"document_type": doctype_for_closing.document_type, "closed": doctype_for_closing.closed},
+				)
diff --git a/erpnext/accounts/doctype/accounting_period/test_accounting_period.py b/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
index c06c2e0..85025d1 100644
--- a/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
+++ b/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
@@ -10,29 +10,38 @@
 from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
 from erpnext.accounts.general_ledger import ClosedAccountingPeriod
 
-test_dependencies = ['Item']
+test_dependencies = ["Item"]
+
 
 class TestAccountingPeriod(unittest.TestCase):
 	def test_overlap(self):
-		ap1 = create_accounting_period(start_date = "2018-04-01",
-			end_date = "2018-06-30", company = "Wind Power LLC")
+		ap1 = create_accounting_period(
+			start_date="2018-04-01", end_date="2018-06-30", company="Wind Power LLC"
+		)
 		ap1.save()
 
-		ap2 = create_accounting_period(start_date = "2018-06-30",
-			end_date = "2018-07-10", company = "Wind Power LLC", period_name = "Test Accounting Period 1")
+		ap2 = create_accounting_period(
+			start_date="2018-06-30",
+			end_date="2018-07-10",
+			company="Wind Power LLC",
+			period_name="Test Accounting Period 1",
+		)
 		self.assertRaises(OverlapError, ap2.save)
 
 	def test_accounting_period(self):
-		ap1 = create_accounting_period(period_name = "Test Accounting Period 2")
+		ap1 = create_accounting_period(period_name="Test Accounting Period 2")
 		ap1.save()
 
-		doc = create_sales_invoice(do_not_submit=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC")
+		doc = create_sales_invoice(
+			do_not_submit=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC"
+		)
 		self.assertRaises(ClosedAccountingPeriod, doc.submit)
 
 	def tearDown(self):
 		for d in frappe.get_all("Accounting Period"):
 			frappe.delete_doc("Accounting Period", d.name)
 
+
 def create_accounting_period(**args):
 	args = frappe._dict(args)
 
@@ -41,8 +50,6 @@
 	accounting_period.end_date = args.end_date or add_months(nowdate(), 1)
 	accounting_period.company = args.company or "_Test Company"
 	accounting_period.period_name = args.period_name or "_Test_Period_Name_1"
-	accounting_period.append("closed_documents", {
-		"document_type": 'Sales Invoice', "closed": 1
-	})
+	accounting_period.append("closed_documents", {"document_type": "Sales Invoice", "closed": 1})
 
 	return accounting_period
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index 9a35a24..417611f 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -18,7 +18,6 @@
   "automatically_fetch_payment_terms",
   "column_break_17",
   "enable_common_party_accounting",
-  "enable_discount_accounting",
   "report_setting_section",
   "use_custom_cash_flow",
   "deferred_accounting_settings_section",
@@ -274,13 +273,6 @@
   },
   {
    "default": "0",
-   "description": "If enabled, additional ledger entries will be made for discounts in a separate Discount Account",
-   "fieldname": "enable_discount_accounting",
-   "fieldtype": "Check",
-   "label": "Enable Discount Accounting"
-  },
-  {
-   "default": "0",
    "description": "Learn about <a href=\"https://docs.erpnext.com/docs/v13/user/manual/en/accounts/articles/common_party_accounting#:~:text=Common%20Party%20Accounting%20in%20ERPNext,Invoice%20against%20a%20primary%20Supplier.\">Common Party</a>",
    "fieldname": "enable_common_party_accounting",
    "fieldtype": "Check",
@@ -354,7 +346,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2022-02-04 12:32:36.805652",
+ "modified": "2022-04-08 14:45:06.796418",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounts Settings",
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
index 4839207..3b125a2 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
@@ -18,48 +18,38 @@
 		frappe.clear_cache()
 
 	def validate(self):
-		frappe.db.set_default("add_taxes_from_item_tax_template",
-			self.get("add_taxes_from_item_tax_template", 0))
+		frappe.db.set_default(
+			"add_taxes_from_item_tax_template", self.get("add_taxes_from_item_tax_template", 0)
+		)
 
-		frappe.db.set_default("enable_common_party_accounting",
-			self.get("enable_common_party_accounting", 0))
+		frappe.db.set_default(
+			"enable_common_party_accounting", self.get("enable_common_party_accounting", 0)
+		)
 
 		self.validate_stale_days()
 		self.enable_payment_schedule_in_print()
-		self.toggle_discount_accounting_fields()
 		self.validate_pending_reposts()
 
 	def validate_stale_days(self):
 		if not self.allow_stale and cint(self.stale_days) <= 0:
 			frappe.msgprint(
-				_("Stale Days should start from 1."), title='Error', indicator='red',
-				raise_exception=1)
+				_("Stale Days should start from 1."), title="Error", indicator="red", raise_exception=1
+			)
 
 	def enable_payment_schedule_in_print(self):
 		show_in_print = cint(self.show_payment_schedule_in_print)
 		for doctype in ("Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"):
-			make_property_setter(doctype, "due_date", "print_hide", show_in_print, "Check", validate_fields_for_doctype=False)
-			make_property_setter(doctype, "payment_schedule", "print_hide",  0 if show_in_print else 1, "Check", validate_fields_for_doctype=False)
-
-	def toggle_discount_accounting_fields(self):
-		enable_discount_accounting = cint(self.enable_discount_accounting)
-
-		for doctype in ["Sales Invoice Item", "Purchase Invoice Item"]:
-			make_property_setter(doctype, "discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
-			if enable_discount_accounting:
-				make_property_setter(doctype, "discount_account", "mandatory_depends_on", "eval: doc.discount_amount", "Code", validate_fields_for_doctype=False)
-			else:
-				make_property_setter(doctype, "discount_account", "mandatory_depends_on", "", "Code", validate_fields_for_doctype=False)
-
-		for doctype in ["Sales Invoice", "Purchase Invoice"]:
-			make_property_setter(doctype, "additional_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
-			if enable_discount_accounting:
-				make_property_setter(doctype, "additional_discount_account", "mandatory_depends_on", "eval: doc.discount_amount", "Code", validate_fields_for_doctype=False)
-			else:
-				make_property_setter(doctype, "additional_discount_account", "mandatory_depends_on", "", "Code", validate_fields_for_doctype=False)
-
-		make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
-
+			make_property_setter(
+				doctype, "due_date", "print_hide", show_in_print, "Check", validate_fields_for_doctype=False
+			)
+			make_property_setter(
+				doctype,
+				"payment_schedule",
+				"print_hide",
+				0 if show_in_print else 1,
+				"Check",
+				validate_fields_for_doctype=False,
+			)
 
 	def validate_pending_reposts(self):
 		if self.acc_frozen_upto:
diff --git a/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.py
index bf1e967..a350cc3 100644
--- a/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.py
+++ b/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.py
@@ -7,12 +7,12 @@
 	def tearDown(self):
 		# Just in case `save` method succeeds, we need to take things back to default so that other tests
 		# don't break
-		cur_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
+		cur_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
 		cur_settings.allow_stale = 1
 		cur_settings.save()
 
 	def test_stale_days(self):
-		cur_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
+		cur_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
 		cur_settings.allow_stale = 0
 		cur_settings.stale_days = 0
 
diff --git a/erpnext/accounts/doctype/bank/bank.py b/erpnext/accounts/doctype/bank/bank.py
index f111433..d44be9a 100644
--- a/erpnext/accounts/doctype/bank/bank.py
+++ b/erpnext/accounts/doctype/bank/bank.py
@@ -15,4 +15,4 @@
 		load_address_and_contact(self)
 
 	def on_trash(self):
-		delete_contact_and_address('Bank', self.name)
+		delete_contact_and_address("Bank", self.name)
diff --git a/erpnext/accounts/doctype/bank/bank_dashboard.py b/erpnext/accounts/doctype/bank/bank_dashboard.py
index 36482aa..7e40a1a 100644
--- a/erpnext/accounts/doctype/bank/bank_dashboard.py
+++ b/erpnext/accounts/doctype/bank/bank_dashboard.py
@@ -3,11 +3,6 @@
 
 def get_data():
 	return {
-		'fieldname': 'bank',
-		'transactions': [
-			{
-				'label': _('Bank Details'),
-				'items': ['Bank Account', 'Bank Guarantee']
-			}
-		]
+		"fieldname": "bank",
+		"transactions": [{"label": _("Bank Details"), "items": ["Bank Account", "Bank Guarantee"]}],
 	}
diff --git a/erpnext/accounts/doctype/bank_account/bank_account.json b/erpnext/accounts/doctype/bank_account/bank_account.json
index de67ab1..41d7947 100644
--- a/erpnext/accounts/doctype/bank_account/bank_account.json
+++ b/erpnext/accounts/doctype/bank_account/bank_account.json
@@ -27,7 +27,6 @@
   "bank_account_no",
   "address_and_contact",
   "address_html",
-  "website",
   "column_break_13",
   "contact_html",
   "integration_details_section",
@@ -157,11 +156,6 @@
    "label": "Address HTML"
   },
   {
-   "fieldname": "website",
-   "fieldtype": "Data",
-   "label": "Website"
-  },
-  {
    "fieldname": "column_break_13",
    "fieldtype": "Column Break"
   },
@@ -208,7 +202,7 @@
   }
  ],
  "links": [],
- "modified": "2020-10-23 16:48:06.303658",
+ "modified": "2022-05-04 15:49:42.620630",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Bank Account",
@@ -243,5 +237,6 @@
  "search_fields": "bank,account",
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_account/bank_account.py b/erpnext/accounts/doctype/bank_account/bank_account.py
index f9140c3..addcf62 100644
--- a/erpnext/accounts/doctype/bank_account/bank_account.py
+++ b/erpnext/accounts/doctype/bank_account/bank_account.py
@@ -20,7 +20,7 @@
 		self.name = self.account_name + " - " + self.bank
 
 	def on_trash(self):
-		delete_contact_and_address('BankAccount', self.name)
+		delete_contact_and_address("BankAccount", self.name)
 
 	def validate(self):
 		self.validate_company()
@@ -31,9 +31,9 @@
 			frappe.throw(_("Company is manadatory for company account"))
 
 	def validate_iban(self):
-		'''
+		"""
 		Algorithm: https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN
-		'''
+		"""
 		# IBAN field is optional
 		if not self.iban:
 			return
@@ -43,7 +43,7 @@
 			return str(9 + ord(c) - 64)
 
 		# remove whitespaces, upper case to get the right number from ord()
-		iban = ''.join(self.iban.split(' ')).upper()
+		iban = "".join(self.iban.split(" ")).upper()
 
 		# Move country code and checksum from the start to the end
 		flipped = iban[4:] + iban[:4]
@@ -52,12 +52,12 @@
 		encoded = [encode_char(c) if ord(c) >= 65 and ord(c) <= 90 else c for c in flipped]
 
 		try:
-			to_check = int(''.join(encoded))
+			to_check = int("".join(encoded))
 		except ValueError:
-			frappe.throw(_('IBAN is not valid'))
+			frappe.throw(_("IBAN is not valid"))
 
 		if to_check % 97 != 1:
-			frappe.throw(_('IBAN is not valid'))
+			frappe.throw(_("IBAN is not valid"))
 
 
 @frappe.whitelist()
@@ -69,12 +69,14 @@
 
 	return doc
 
+
 @frappe.whitelist()
 def get_party_bank_account(party_type, party):
-	return frappe.db.get_value(party_type,
-		party, 'default_bank_account')
+	return frappe.db.get_value(party_type, party, "default_bank_account")
+
 
 @frappe.whitelist()
 def get_bank_account_details(bank_account):
-	return frappe.db.get_value("Bank Account",
-		bank_account, ['account', 'bank', 'bank_account_no'], as_dict=1)
+	return frappe.db.get_value(
+		"Bank Account", bank_account, ["account", "bank", "bank_account_no"], as_dict=1
+	)
diff --git a/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py b/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py
index db4d7e5..8bf8d8a 100644
--- a/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py
+++ b/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py
@@ -3,25 +3,18 @@
 
 def get_data():
 	return {
-		'fieldname': 'bank_account',
-		'non_standard_fieldnames': {
-			'Customer': 'default_bank_account',
-			'Supplier': 'default_bank_account',
+		"fieldname": "bank_account",
+		"non_standard_fieldnames": {
+			"Customer": "default_bank_account",
+			"Supplier": "default_bank_account",
 		},
-		'transactions': [
+		"transactions": [
 			{
-				'label': _('Payments'),
-				'items': ['Payment Entry', 'Payment Request', 'Payment Order', 'Payroll Entry']
+				"label": _("Payments"),
+				"items": ["Payment Entry", "Payment Request", "Payment Order", "Payroll Entry"],
 			},
-			{
-				'label': _('Party'),
-				'items': ['Customer', 'Supplier']
-			},
-			{
-				'items': ['Bank Guarantee']
-			},
-			{
-				'items': ['Journal Entry']
-			}
-		]
+			{"label": _("Party"), "items": ["Customer", "Supplier"]},
+			{"items": ["Bank Guarantee"]},
+			{"items": ["Journal Entry"]},
+		],
 	}
diff --git a/erpnext/accounts/doctype/bank_account/test_bank_account.py b/erpnext/accounts/doctype/bank_account/test_bank_account.py
index 5f23f88..8949524 100644
--- a/erpnext/accounts/doctype/bank_account/test_bank_account.py
+++ b/erpnext/accounts/doctype/bank_account/test_bank_account.py
@@ -8,28 +8,28 @@
 
 # test_records = frappe.get_test_records('Bank Account')
 
-class TestBankAccount(unittest.TestCase):
 
+class TestBankAccount(unittest.TestCase):
 	def test_validate_iban(self):
 		valid_ibans = [
-			'GB82 WEST 1234 5698 7654 32',
-			'DE91 1000 0000 0123 4567 89',
-			'FR76 3000 6000 0112 3456 7890 189'
+			"GB82 WEST 1234 5698 7654 32",
+			"DE91 1000 0000 0123 4567 89",
+			"FR76 3000 6000 0112 3456 7890 189",
 		]
 
 		invalid_ibans = [
 			# wrong checksum (3rd place)
-			'GB72 WEST 1234 5698 7654 32',
-			'DE81 1000 0000 0123 4567 89',
-			'FR66 3000 6000 0112 3456 7890 189'
+			"GB72 WEST 1234 5698 7654 32",
+			"DE81 1000 0000 0123 4567 89",
+			"FR66 3000 6000 0112 3456 7890 189",
 		]
 
-		bank_account = frappe.get_doc({'doctype':'Bank Account'})
+		bank_account = frappe.get_doc({"doctype": "Bank Account"})
 
 		try:
 			bank_account.validate_iban()
 		except AttributeError:
-			msg = 'BankAccount.validate_iban() failed for empty IBAN'
+			msg = "BankAccount.validate_iban() failed for empty IBAN"
 			self.fail(msg=msg)
 
 		for iban in valid_ibans:
@@ -37,11 +37,11 @@
 			try:
 				bank_account.validate_iban()
 			except ValidationError:
-				msg = 'BankAccount.validate_iban() failed for valid IBAN {}'.format(iban)
+				msg = "BankAccount.validate_iban() failed for valid IBAN {}".format(iban)
 				self.fail(msg=msg)
 
 		for not_iban in invalid_ibans:
 			bank_account.iban = not_iban
-			msg = 'BankAccount.validate_iban() accepted invalid IBAN {}'.format(not_iban)
+			msg = "BankAccount.validate_iban() accepted invalid IBAN {}".format(not_iban)
 			with self.assertRaises(ValidationError, msg=msg):
 				bank_account.validate_iban()
diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
index a3bbb22..98ba399 100644
--- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
+++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
@@ -5,11 +5,13 @@
 import frappe
 from frappe import _, msgprint
 from frappe.model.document import Document
-from frappe.utils import flt, fmt_money, getdate, nowdate
+from frappe.query_builder.custom import ConstantColumn
+from frappe.utils import flt, fmt_money, getdate
 
-form_grid_templates = {
-	"journal_entries": "templates/form_grid/bank_reconciliation_grid.html"
-}
+import erpnext
+
+form_grid_templates = {"journal_entries": "templates/form_grid/bank_reconciliation_grid.html"}
+
 
 class BankClearance(Document):
 	@frappe.whitelist()
@@ -24,7 +26,8 @@
 		if not self.include_reconciled_entries:
 			condition = "and (clearance_date IS NULL or clearance_date='0000-00-00')"
 
-		journal_entries = frappe.db.sql("""
+		journal_entries = frappe.db.sql(
+			"""
 			select
 				"Journal Entry" as payment_document, t1.name as payment_entry,
 				t1.cheque_no as cheque_number, t1.cheque_date,
@@ -38,12 +41,18 @@
 				and ifnull(t1.is_opening, 'No') = 'No' {condition}
 			group by t2.account, t1.name
 			order by t1.posting_date ASC, t1.name DESC
-		""".format(condition=condition), {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1)
+		""".format(
+				condition=condition
+			),
+			{"account": self.account, "from": self.from_date, "to": self.to_date},
+			as_dict=1,
+		)
 
 		if self.bank_account:
-			condition += 'and bank_account = %(bank_account)s'
+			condition += "and bank_account = %(bank_account)s"
 
-		payment_entries = frappe.db.sql("""
+		payment_entries = frappe.db.sql(
+			"""
 			select
 				"Payment Entry" as payment_document, name as payment_entry,
 				reference_no as cheque_number, reference_date as cheque_date,
@@ -58,12 +67,69 @@
 				{condition}
 			order by
 				posting_date ASC, name DESC
-		""".format(condition=condition), {"account": self.account, "from":self.from_date,
-				"to": self.to_date, "bank_account": self.bank_account}, as_dict=1)
+		""".format(
+				condition=condition
+			),
+			{
+				"account": self.account,
+				"from": self.from_date,
+				"to": self.to_date,
+				"bank_account": self.bank_account,
+			},
+			as_dict=1,
+		)
+
+		loan_disbursement = frappe.qb.DocType("Loan Disbursement")
+
+		loan_disbursements = (
+			frappe.qb.from_(loan_disbursement)
+			.select(
+				ConstantColumn("Loan Disbursement").as_("payment_document"),
+				loan_disbursement.name.as_("payment_entry"),
+				loan_disbursement.disbursed_amount.as_("credit"),
+				ConstantColumn(0).as_("debit"),
+				loan_disbursement.reference_number.as_("cheque_number"),
+				loan_disbursement.reference_date.as_("cheque_date"),
+				loan_disbursement.disbursement_date.as_("posting_date"),
+				loan_disbursement.applicant.as_("against_account"),
+			)
+			.where(loan_disbursement.docstatus == 1)
+			.where(loan_disbursement.disbursement_date >= self.from_date)
+			.where(loan_disbursement.disbursement_date <= self.to_date)
+			.where(loan_disbursement.clearance_date.isnull())
+			.where(loan_disbursement.disbursement_account.isin([self.bank_account, self.account]))
+			.orderby(loan_disbursement.disbursement_date)
+			.orderby(loan_disbursement.name, frappe.qb.desc)
+		).run(as_dict=1)
+
+		loan_repayment = frappe.qb.DocType("Loan Repayment")
+
+		loan_repayments = (
+			frappe.qb.from_(loan_repayment)
+			.select(
+				ConstantColumn("Loan Repayment").as_("payment_document"),
+				loan_repayment.name.as_("payment_entry"),
+				loan_repayment.amount_paid.as_("debit"),
+				ConstantColumn(0).as_("credit"),
+				loan_repayment.reference_number.as_("cheque_number"),
+				loan_repayment.reference_date.as_("cheque_date"),
+				loan_repayment.applicant.as_("against_account"),
+				loan_repayment.posting_date,
+			)
+			.where(loan_repayment.docstatus == 1)
+			.where(loan_repayment.clearance_date.isnull())
+			.where(loan_repayment.repay_from_salary == 0)
+			.where(loan_repayment.posting_date >= self.from_date)
+			.where(loan_repayment.posting_date <= self.to_date)
+			.where(loan_repayment.payment_account.isin([self.bank_account, self.account]))
+			.orderby(loan_repayment.posting_date)
+			.orderby(loan_repayment.name, frappe.qb.desc)
+		).run(as_dict=1)
 
 		pos_sales_invoices, pos_purchase_invoices = [], []
 		if self.include_pos_transactions:
-			pos_sales_invoices = frappe.db.sql("""
+			pos_sales_invoices = frappe.db.sql(
+				"""
 				select
 					"Sales Invoice Payment" as payment_document, sip.name as payment_entry, sip.amount as debit,
 					si.posting_date, si.customer as against_account, sip.clearance_date,
@@ -74,9 +140,13 @@
 					and account.name = sip.account and si.posting_date >= %(from)s and si.posting_date <= %(to)s
 				order by
 					si.posting_date ASC, si.name DESC
-			""", {"account":self.account, "from":self.from_date, "to":self.to_date}, as_dict=1)
+			""",
+				{"account": self.account, "from": self.from_date, "to": self.to_date},
+				as_dict=1,
+			)
 
-			pos_purchase_invoices = frappe.db.sql("""
+			pos_purchase_invoices = frappe.db.sql(
+				"""
 				select
 					"Purchase Invoice" as payment_document, pi.name as payment_entry, pi.paid_amount as credit,
 					pi.posting_date, pi.supplier as against_account, pi.clearance_date,
@@ -87,21 +157,36 @@
 					and pi.posting_date >= %(from)s and pi.posting_date <= %(to)s
 				order by
 					pi.posting_date ASC, pi.name DESC
-			""", {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1)
+			""",
+				{"account": self.account, "from": self.from_date, "to": self.to_date},
+				as_dict=1,
+			)
 
-		entries = sorted(list(payment_entries) + list(journal_entries + list(pos_sales_invoices) + list(pos_purchase_invoices)),
-			key=lambda k: k['posting_date'] or getdate(nowdate()))
+		entries = sorted(
+			list(payment_entries)
+			+ list(journal_entries)
+			+ list(pos_sales_invoices)
+			+ list(pos_purchase_invoices)
+			+ list(loan_disbursements)
+			+ list(loan_repayments),
+			key=lambda k: getdate(k["posting_date"]),
+		)
 
-		self.set('payment_entries', [])
+		self.set("payment_entries", [])
 		self.total_amount = 0.0
+		default_currency = erpnext.get_default_currency()
 
 		for d in entries:
-			row = self.append('payment_entries', {})
+			row = self.append("payment_entries", {})
 
-			amount = flt(d.get('debit', 0)) - flt(d.get('credit', 0))
+			amount = flt(d.get("debit", 0)) - flt(d.get("credit", 0))
+
+			if not d.get("account_currency"):
+				d.account_currency = default_currency
 
 			formatted_amount = fmt_money(abs(amount), 2, d.account_currency)
 			d.amount = formatted_amount + " " + (_("Dr") if amount > 0 else _("Cr"))
+			d.posting_date = getdate(d.posting_date)
 
 			d.pop("credit")
 			d.pop("debit")
@@ -112,21 +197,24 @@
 	@frappe.whitelist()
 	def update_clearance_date(self):
 		clearance_date_updated = False
-		for d in self.get('payment_entries'):
+		for d in self.get("payment_entries"):
 			if d.clearance_date:
 				if not d.payment_document:
 					frappe.throw(_("Row #{0}: Payment document is required to complete the transaction"))
 
 				if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date):
-					frappe.throw(_("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}")
-						.format(d.idx, d.clearance_date, d.cheque_date))
+					frappe.throw(
+						_("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}").format(
+							d.idx, d.clearance_date, d.cheque_date
+						)
+					)
 
 			if d.clearance_date or self.include_reconciled_entries:
 				if not d.clearance_date:
 					d.clearance_date = None
 
 				payment_entry = frappe.get_doc(d.payment_document, d.payment_entry)
-				payment_entry.db_set('clearance_date', d.clearance_date)
+				payment_entry.db_set("clearance_date", d.clearance_date)
 
 				clearance_date_updated = True
 
diff --git a/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py
index 706fbbe..c1e55f6 100644
--- a/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py
+++ b/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py
@@ -1,9 +1,96 @@
 # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 
-# import frappe
 import unittest
 
+import frappe
+from frappe.utils import add_months, getdate
+
+from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
+from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+from erpnext.loan_management.doctype.loan.test_loan import (
+	create_loan,
+	create_loan_accounts,
+	create_loan_type,
+	create_repayment_entry,
+	make_loan_disbursement_entry,
+)
+
 
 class TestBankClearance(unittest.TestCase):
-	pass
+	@classmethod
+	def setUpClass(cls):
+		make_bank_account()
+		create_loan_accounts()
+		create_loan_masters()
+		add_transactions()
+
+	# Basic test case to test if bank clearance tool doesn't break
+	# Detailed test can be added later
+	def test_bank_clearance(self):
+		bank_clearance = frappe.get_doc("Bank Clearance")
+		bank_clearance.account = "_Test Bank Clearance - _TC"
+		bank_clearance.from_date = add_months(getdate(), -1)
+		bank_clearance.to_date = getdate()
+		bank_clearance.get_payment_entries()
+		self.assertEqual(len(bank_clearance.payment_entries), 3)
+
+
+def make_bank_account():
+	if not frappe.db.get_value("Account", "_Test Bank Clearance - _TC"):
+		frappe.get_doc(
+			{
+				"doctype": "Account",
+				"account_type": "Bank",
+				"account_name": "_Test Bank Clearance",
+				"company": "_Test Company",
+				"parent_account": "Bank Accounts - _TC",
+			}
+		).insert()
+
+
+def create_loan_masters():
+	create_loan_type(
+		"Clearance Loan",
+		2000000,
+		13.5,
+		25,
+		0,
+		5,
+		"Cash",
+		"_Test Bank Clearance - _TC",
+		"_Test Bank Clearance - _TC",
+		"Loan Account - _TC",
+		"Interest Income Account - _TC",
+		"Penalty Income Account - _TC",
+	)
+
+
+def add_transactions():
+	make_payment_entry()
+	make_loan()
+
+
+def make_loan():
+	loan = create_loan(
+		"_Test Customer",
+		"Clearance Loan",
+		280000,
+		"Repay Over Number of Periods",
+		20,
+		applicant_type="Customer",
+	)
+	loan.submit()
+	make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=getdate())
+	repayment_entry = create_repayment_entry(loan.name, "_Test Customer", getdate(), loan.loan_amount)
+	repayment_entry.save()
+	repayment_entry.submit()
+
+
+def make_payment_entry():
+	pi = make_purchase_invoice(supplier="_Test Supplier", qty=1, rate=690)
+	pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank Clearance - _TC")
+	pe.reference_no = "Conrad Oct 18"
+	pe.reference_date = "2018-10-24"
+	pe.insert()
+	pe.submit()
diff --git a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py
index cfbcf16..9144a29 100644
--- a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py
+++ b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py
@@ -23,10 +23,16 @@
 		if not self.bank:
 			frappe.throw(_("Enter the name of the bank or lending institution before submittting."))
 
+
 @frappe.whitelist()
 def get_vouchar_detials(column_list, doctype, docname):
 	column_list = json.loads(column_list)
 	for col in column_list:
 		sanitize_searchfield(col)
-	return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
-		.format(columns=", ".join(column_list), doctype=doctype), docname, as_dict=1)[0]
+	return frappe.db.sql(
+		""" select {columns} from `tab{doctype}` where name=%s""".format(
+			columns=", ".join(column_list), doctype=doctype
+		),
+		docname,
+		as_dict=1,
+	)[0]
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
index f3351dd..0efe086 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
@@ -22,48 +22,63 @@
 class BankReconciliationTool(Document):
 	pass
 
+
 @frappe.whitelist()
-def get_bank_transactions(bank_account, from_date = None, to_date = None):
+def get_bank_transactions(bank_account, from_date=None, to_date=None):
 	# returns bank transactions for a bank account
 	filters = []
-	filters.append(['bank_account', '=', bank_account])
-	filters.append(['docstatus', '=', 1])
-	filters.append(['unallocated_amount', '>', 0])
+	filters.append(["bank_account", "=", bank_account])
+	filters.append(["docstatus", "=", 1])
+	filters.append(["unallocated_amount", ">", 0])
 	if to_date:
-		filters.append(['date', '<=', to_date])
+		filters.append(["date", "<=", to_date])
 	if from_date:
-		filters.append(['date', '>=', from_date])
+		filters.append(["date", ">=", from_date])
 	transactions = frappe.get_all(
-		'Bank Transaction',
-		fields = ['date', 'deposit', 'withdrawal', 'currency',
-		'description', 'name', 'bank_account', 'company',
-		'unallocated_amount', 'reference_number', 'party_type', 'party'],
-		filters = filters
+		"Bank Transaction",
+		fields=[
+			"date",
+			"deposit",
+			"withdrawal",
+			"currency",
+			"description",
+			"name",
+			"bank_account",
+			"company",
+			"unallocated_amount",
+			"reference_number",
+			"party_type",
+			"party",
+		],
+		filters=filters,
 	)
 	return transactions
 
+
 @frappe.whitelist()
 def get_account_balance(bank_account, till_date):
 	# returns account balance till the specified date
-	account = frappe.db.get_value('Bank Account', bank_account, 'account')
-	filters = frappe._dict({
-		"account": account,
-		"report_date": till_date,
-		"include_pos_transactions": 1
-	})
+	account = frappe.db.get_value("Bank Account", bank_account, "account")
+	filters = frappe._dict(
+		{"account": account, "report_date": till_date, "include_pos_transactions": 1}
+	)
 	data = get_entries(filters)
 
 	balance_as_per_system = get_balance_on(filters["account"], filters["report_date"])
 
-	total_debit, total_credit = 0,0
+	total_debit, total_credit = 0, 0
 	for d in data:
 		total_debit += flt(d.debit)
 		total_credit += flt(d.credit)
 
 	amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters)
 
-	bank_bal = flt(balance_as_per_system) - flt(total_debit) + flt(total_credit) \
+	bank_bal = (
+		flt(balance_as_per_system)
+		- flt(total_debit)
+		+ flt(total_credit)
 		+ amounts_not_reflected_in_system
+	)
 
 	return bank_bal
 
@@ -76,71 +91,94 @@
 	bank_transaction.party_type = party_type
 	bank_transaction.party = party
 	bank_transaction.save()
-	return frappe.db.get_all('Bank Transaction',
-		filters={
-			'name': bank_transaction_name
-		},
-		fields=['date', 'deposit', 'withdrawal', 'currency',
-			'description', 'name', 'bank_account', 'company',
-			'unallocated_amount', 'reference_number',
-			 'party_type', 'party'],
+	return frappe.db.get_all(
+		"Bank Transaction",
+		filters={"name": bank_transaction_name},
+		fields=[
+			"date",
+			"deposit",
+			"withdrawal",
+			"currency",
+			"description",
+			"name",
+			"bank_account",
+			"company",
+			"unallocated_amount",
+			"reference_number",
+			"party_type",
+			"party",
+		],
 	)[0]
 
 
 @frappe.whitelist()
-def create_journal_entry_bts( bank_transaction_name, reference_number=None, reference_date=None, posting_date=None, entry_type=None,
-	second_account=None, mode_of_payment=None, party_type=None, party=None, allow_edit=None):
+def create_journal_entry_bts(
+	bank_transaction_name,
+	reference_number=None,
+	reference_date=None,
+	posting_date=None,
+	entry_type=None,
+	second_account=None,
+	mode_of_payment=None,
+	party_type=None,
+	party=None,
+	allow_edit=None,
+):
 	# Create a new journal entry based on the bank transaction
 	bank_transaction = frappe.db.get_values(
-		"Bank Transaction", bank_transaction_name,
-		fieldname=["name", "deposit", "withdrawal", "bank_account"] ,
-		as_dict=True
+		"Bank Transaction",
+		bank_transaction_name,
+		fieldname=["name", "deposit", "withdrawal", "bank_account"],
+		as_dict=True,
 	)[0]
 	company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account")
 	account_type = frappe.db.get_value("Account", second_account, "account_type")
 	if account_type in ["Receivable", "Payable"]:
 		if not (party_type and party):
-			frappe.throw(_("Party Type and Party is required for Receivable / Payable account {0}").format( second_account))
+			frappe.throw(
+				_("Party Type and Party is required for Receivable / Payable account {0}").format(
+					second_account
+				)
+			)
 	accounts = []
 	# Multi Currency?
-	accounts.append({
+	accounts.append(
+		{
 			"account": second_account,
-			"credit_in_account_currency": bank_transaction.deposit
-				if  bank_transaction.deposit > 0
-				else 0,
-			"debit_in_account_currency":bank_transaction.withdrawal
-				if  bank_transaction.withdrawal > 0
-				else 0,
-			"party_type":party_type,
-			"party":party,
-		})
+			"credit_in_account_currency": bank_transaction.deposit if bank_transaction.deposit > 0 else 0,
+			"debit_in_account_currency": bank_transaction.withdrawal
+			if bank_transaction.withdrawal > 0
+			else 0,
+			"party_type": party_type,
+			"party": party,
+		}
+	)
 
-	accounts.append({
+	accounts.append(
+		{
 			"account": company_account,
 			"bank_account": bank_transaction.bank_account,
 			"credit_in_account_currency": bank_transaction.withdrawal
-				if  bank_transaction.withdrawal > 0
-				else 0,
-			"debit_in_account_currency":bank_transaction.deposit
-				if  bank_transaction.deposit > 0
-				else 0,
-		})
+			if bank_transaction.withdrawal > 0
+			else 0,
+			"debit_in_account_currency": bank_transaction.deposit if bank_transaction.deposit > 0 else 0,
+		}
+	)
 
 	company = frappe.get_value("Account", company_account, "company")
 
 	journal_entry_dict = {
-		"voucher_type" : entry_type,
-		"company" : company,
-		"posting_date" : posting_date,
-		"cheque_date" : reference_date,
-		"cheque_no" : reference_number,
-		"mode_of_payment" : mode_of_payment
+		"voucher_type": entry_type,
+		"company": company,
+		"posting_date": posting_date,
+		"cheque_date": reference_date,
+		"cheque_no": reference_number,
+		"mode_of_payment": mode_of_payment,
 	}
-	journal_entry = frappe.new_doc('Journal Entry')
+	journal_entry = frappe.new_doc("Journal Entry")
 	journal_entry.update(journal_entry_dict)
 	journal_entry.set("accounts", accounts)
 
-
 	if allow_edit:
 		return journal_entry
 
@@ -152,21 +190,32 @@
 	else:
 		paid_amount = bank_transaction.withdrawal
 
-	vouchers = json.dumps([{
-		"payment_doctype":"Journal Entry",
-		"payment_name":journal_entry.name,
-		"amount":paid_amount}])
+	vouchers = json.dumps(
+		[{"payment_doctype": "Journal Entry", "payment_name": journal_entry.name, "amount": paid_amount}]
+	)
 
 	return reconcile_vouchers(bank_transaction.name, vouchers)
 
+
 @frappe.whitelist()
-def create_payment_entry_bts( bank_transaction_name, reference_number=None, reference_date=None, party_type=None, party=None, posting_date=None,
-	mode_of_payment=None, project=None, cost_center=None, allow_edit=None):
+def create_payment_entry_bts(
+	bank_transaction_name,
+	reference_number=None,
+	reference_date=None,
+	party_type=None,
+	party=None,
+	posting_date=None,
+	mode_of_payment=None,
+	project=None,
+	cost_center=None,
+	allow_edit=None,
+):
 	# Create a new payment entry based on the bank transaction
 	bank_transaction = frappe.db.get_values(
-		"Bank Transaction", bank_transaction_name,
-		fieldname=["name", "unallocated_amount", "deposit", "bank_account"] ,
-		as_dict=True
+		"Bank Transaction",
+		bank_transaction_name,
+		fieldname=["name", "unallocated_amount", "deposit", "bank_account"],
+		as_dict=True,
 	)[0]
 	paid_amount = bank_transaction.unallocated_amount
 	payment_type = "Receive" if bank_transaction.deposit > 0 else "Pay"
@@ -174,27 +223,26 @@
 	company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account")
 	company = frappe.get_value("Account", company_account, "company")
 	payment_entry_dict = {
-		"company" : company,
-		"payment_type" : payment_type,
-		"reference_no" :  reference_number,
-		"reference_date" :  reference_date,
-		"party_type" :  party_type,
-		"party" :  party,
-		"posting_date" :  posting_date,
+		"company": company,
+		"payment_type": payment_type,
+		"reference_no": reference_number,
+		"reference_date": reference_date,
+		"party_type": party_type,
+		"party": party,
+		"posting_date": posting_date,
 		"paid_amount": paid_amount,
-		"received_amount": paid_amount
+		"received_amount": paid_amount,
 	}
 	payment_entry = frappe.new_doc("Payment Entry")
 
-
 	payment_entry.update(payment_entry_dict)
 
 	if mode_of_payment:
-		payment_entry.mode_of_payment =  mode_of_payment
+		payment_entry.mode_of_payment = mode_of_payment
 	if project:
-		payment_entry.project =  project
+		payment_entry.project = project
 	if cost_center:
-		payment_entry.cost_center =  cost_center
+		payment_entry.cost_center = cost_center
 	if payment_type == "Receive":
 		payment_entry.paid_to = company_account
 	else:
@@ -208,84 +256,111 @@
 	payment_entry.insert()
 
 	payment_entry.submit()
-	vouchers = json.dumps([{
-		"payment_doctype":"Payment Entry",
-		"payment_name":payment_entry.name,
-		"amount":paid_amount}])
+	vouchers = json.dumps(
+		[{"payment_doctype": "Payment Entry", "payment_name": payment_entry.name, "amount": paid_amount}]
+	)
 	return reconcile_vouchers(bank_transaction.name, vouchers)
 
+
 @frappe.whitelist()
 def reconcile_vouchers(bank_transaction_name, vouchers):
 	# updated clear date of all the vouchers based on the bank transaction
 	vouchers = json.loads(vouchers)
 	transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
-	company_account = frappe.db.get_value('Bank Account', transaction.bank_account, 'account')
+	company_account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
 
 	if transaction.unallocated_amount == 0:
 		frappe.throw(_("This bank transaction is already fully reconciled"))
 	total_amount = 0
 	for voucher in vouchers:
-		voucher['payment_entry'] = frappe.get_doc(voucher['payment_doctype'], voucher['payment_name'])
-		total_amount += get_paid_amount(frappe._dict({
-			'payment_document': voucher['payment_doctype'],
-			'payment_entry': voucher['payment_name'],
-		}), transaction.currency, company_account)
+		voucher["payment_entry"] = frappe.get_doc(voucher["payment_doctype"], voucher["payment_name"])
+		total_amount += get_paid_amount(
+			frappe._dict(
+				{
+					"payment_document": voucher["payment_doctype"],
+					"payment_entry": voucher["payment_name"],
+				}
+			),
+			transaction.currency,
+			company_account,
+		)
 
 	if total_amount > transaction.unallocated_amount:
-		frappe.throw(_("The Sum Total of Amounts of All Selected Vouchers Should be Less than the Unallocated Amount of the Bank Transaction"))
+		frappe.throw(
+			_(
+				"The sum total of amounts of all selected vouchers should be less than the unallocated amount of the bank transaction"
+			)
+		)
 	account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
 
 	for voucher in vouchers:
-		gl_entry = frappe.db.get_value("GL Entry", dict(account=account, voucher_type=voucher['payment_doctype'], voucher_no=voucher['payment_name']), ['credit', 'debit'], as_dict=1)
-		gl_amount, transaction_amount = (gl_entry.credit, transaction.deposit) if gl_entry.credit > 0 else (gl_entry.debit, transaction.withdrawal)
+		gl_entry = frappe.db.get_value(
+			"GL Entry",
+			dict(
+				account=account, voucher_type=voucher["payment_doctype"], voucher_no=voucher["payment_name"]
+			),
+			["credit", "debit"],
+			as_dict=1,
+		)
+		gl_amount, transaction_amount = (
+			(gl_entry.credit, transaction.deposit)
+			if gl_entry.credit > 0
+			else (gl_entry.debit, transaction.withdrawal)
+		)
 		allocated_amount = gl_amount if gl_amount >= transaction_amount else transaction_amount
 
-		transaction.append("payment_entries", {
-			"payment_document": voucher['payment_entry'].doctype,
-			"payment_entry": voucher['payment_entry'].name,
-			"allocated_amount": allocated_amount
-		})
+		transaction.append(
+			"payment_entries",
+			{
+				"payment_document": voucher["payment_entry"].doctype,
+				"payment_entry": voucher["payment_entry"].name,
+				"allocated_amount": allocated_amount,
+			},
+		)
 
 	transaction.save()
 	transaction.update_allocations()
 	return frappe.get_doc("Bank Transaction", bank_transaction_name)
 
+
 @frappe.whitelist()
-def get_linked_payments(bank_transaction_name, document_types = None):
+def get_linked_payments(bank_transaction_name, document_types=None):
 	# get all matching payments for a bank transaction
 	transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
 	bank_account = frappe.db.get_values(
-		"Bank Account",
-		transaction.bank_account,
-		["account", "company"],
-		as_dict=True)[0]
+		"Bank Account", transaction.bank_account, ["account", "company"], as_dict=True
+	)[0]
 	(account, company) = (bank_account.account, bank_account.company)
 	matching = check_matching(account, company, transaction, document_types)
 	return matching
 
+
 def check_matching(bank_account, company, transaction, document_types):
 	# combine all types of vouchers
 	subquery = get_queries(bank_account, company, transaction, document_types)
 	filters = {
-			"amount": transaction.unallocated_amount,
-			"payment_type" : "Receive" if transaction.deposit > 0 else "Pay",
-			"reference_no": transaction.reference_number,
-			"party_type": transaction.party_type,
-			"party": transaction.party,
-			"bank_account":  bank_account
-		}
+		"amount": transaction.unallocated_amount,
+		"payment_type": "Receive" if transaction.deposit > 0 else "Pay",
+		"reference_no": transaction.reference_number,
+		"party_type": transaction.party_type,
+		"party": transaction.party,
+		"bank_account": bank_account,
+	}
 
 	matching_vouchers = []
 
-	matching_vouchers.extend(get_loan_vouchers(bank_account, transaction,
-		document_types, filters))
+	matching_vouchers.extend(get_loan_vouchers(bank_account, transaction, document_types, filters))
 
 	for query in subquery:
 		matching_vouchers.extend(
-			frappe.db.sql(query, filters,)
+			frappe.db.sql(
+				query,
+				filters,
+			)
 		)
 
-	return sorted(matching_vouchers, key = lambda x: x[0], reverse=True) if matching_vouchers else []
+	return sorted(matching_vouchers, key=lambda x: x[0], reverse=True) if matching_vouchers else []
+
 
 def get_queries(bank_account, company, transaction, document_types):
 	# get queries to get matching vouchers
@@ -302,7 +377,7 @@
 		queries.extend([je_amount_matching])
 
 	if transaction.deposit > 0 and "sales_invoice" in document_types:
-		si_amount_matching =  get_si_matching_query(amount_condition)
+		si_amount_matching = get_si_matching_query(amount_condition)
 		queries.extend([si_amount_matching])
 
 	if transaction.withdrawal > 0:
@@ -316,6 +391,7 @@
 
 	return queries
 
+
 def get_loan_vouchers(bank_account, transaction, document_types, filters):
 	vouchers = []
 	amount_condition = True if "exact_match" in document_types else False
@@ -328,109 +404,91 @@
 
 	return vouchers
 
+
 def get_ld_matching_query(bank_account, amount_condition, filters):
 	loan_disbursement = frappe.qb.DocType("Loan Disbursement")
 	matching_reference = loan_disbursement.reference_number == filters.get("reference_number")
-	matching_party = loan_disbursement.applicant_type == filters.get("party_type") and \
-			loan_disbursement.applicant == filters.get("party")
+	matching_party = loan_disbursement.applicant_type == filters.get(
+		"party_type"
+	) and loan_disbursement.applicant == filters.get("party")
 
-	rank = (
-			frappe.qb.terms.Case()
-			.when(matching_reference, 1)
-			.else_(0)
+	rank = frappe.qb.terms.Case().when(matching_reference, 1).else_(0)
+
+	rank1 = frappe.qb.terms.Case().when(matching_party, 1).else_(0)
+
+	query = (
+		frappe.qb.from_(loan_disbursement)
+		.select(
+			rank + rank1 + 1,
+			ConstantColumn("Loan Disbursement").as_("doctype"),
+			loan_disbursement.name,
+			loan_disbursement.disbursed_amount,
+			loan_disbursement.reference_number,
+			loan_disbursement.reference_date,
+			loan_disbursement.applicant_type,
+			loan_disbursement.disbursement_date,
 		)
-
-	rank1 = (
-			frappe.qb.terms.Case()
-			.when(matching_party, 1)
-			.else_(0)
-		)
-
-	query = frappe.qb.from_(loan_disbursement).select(
-		rank + rank1 + 1,
-		ConstantColumn("Loan Disbursement").as_("doctype"),
-		loan_disbursement.name,
-		loan_disbursement.disbursed_amount,
-		loan_disbursement.reference_number,
-		loan_disbursement.reference_date,
-		loan_disbursement.applicant_type,
-		loan_disbursement.disbursement_date
-	).where(
-		loan_disbursement.docstatus == 1
-	).where(
-		loan_disbursement.clearance_date.isnull()
-	).where(
-		loan_disbursement.disbursement_account == bank_account
+		.where(loan_disbursement.docstatus == 1)
+		.where(loan_disbursement.clearance_date.isnull())
+		.where(loan_disbursement.disbursement_account == bank_account)
 	)
 
 	if amount_condition:
-		query.where(
-			loan_disbursement.disbursed_amount == filters.get('amount')
-		)
+		query.where(loan_disbursement.disbursed_amount == filters.get("amount"))
 	else:
-		query.where(
-			loan_disbursement.disbursed_amount <= filters.get('amount')
-		)
+		query.where(loan_disbursement.disbursed_amount <= filters.get("amount"))
 
 	vouchers = query.run(as_list=True)
 
 	return vouchers
 
+
 def get_lr_matching_query(bank_account, amount_condition, filters):
 	loan_repayment = frappe.qb.DocType("Loan Repayment")
 	matching_reference = loan_repayment.reference_number == filters.get("reference_number")
-	matching_party = loan_repayment.applicant_type == filters.get("party_type") and \
-			loan_repayment.applicant == filters.get("party")
+	matching_party = loan_repayment.applicant_type == filters.get(
+		"party_type"
+	) and loan_repayment.applicant == filters.get("party")
 
-	rank = (
-			frappe.qb.terms.Case()
-			.when(matching_reference, 1)
-			.else_(0)
+	rank = frappe.qb.terms.Case().when(matching_reference, 1).else_(0)
+
+	rank1 = frappe.qb.terms.Case().when(matching_party, 1).else_(0)
+
+	query = (
+		frappe.qb.from_(loan_repayment)
+		.select(
+			rank + rank1 + 1,
+			ConstantColumn("Loan Repayment").as_("doctype"),
+			loan_repayment.name,
+			loan_repayment.amount_paid,
+			loan_repayment.reference_number,
+			loan_repayment.reference_date,
+			loan_repayment.applicant_type,
+			loan_repayment.posting_date,
 		)
-
-	rank1 = (
-			frappe.qb.terms.Case()
-			.when(matching_party, 1)
-			.else_(0)
-		)
-
-	query = frappe.qb.from_(loan_repayment).select(
-		rank + rank1 + 1,
-		ConstantColumn("Loan Repayment").as_("doctype"),
-		loan_repayment.name,
-		loan_repayment.amount_paid,
-		loan_repayment.reference_number,
-		loan_repayment.reference_date,
-		loan_repayment.applicant_type,
-		loan_repayment.posting_date
-	).where(
-		loan_repayment.docstatus == 1
-	).where(
-		loan_repayment.clearance_date.isnull()
-	).where(
-		loan_repayment.payment_account == bank_account
+		.where(loan_repayment.docstatus == 1)
+		.where(loan_repayment.repay_from_salary == 0)
+		.where(loan_repayment.clearance_date.isnull())
+		.where(loan_repayment.payment_account == bank_account)
 	)
 
 	if amount_condition:
-		query.where(
-			loan_repayment.amount_paid == filters.get('amount')
-		)
+		query.where(loan_repayment.amount_paid == filters.get("amount"))
 	else:
-		query.where(
-			loan_repayment.amount_paid <= filters.get('amount')
-		)
+		query.where(loan_repayment.amount_paid <= filters.get("amount"))
 
 	vouchers = query.run()
 
 	return vouchers
 
+
 def get_pe_matching_query(amount_condition, account_from_to, transaction):
 	# get matching payment entries query
 	if transaction.deposit > 0:
 		currency_field = "paid_to_account_currency as currency"
 	else:
 		currency_field = "paid_from_account_currency as currency"
-	return  f"""
+	return f"""
 	SELECT
 		(CASE WHEN reference_no=%(reference_no)s THEN 1 ELSE 0 END
 		+ CASE WHEN (party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0  END
@@ -519,6 +577,7 @@
 			AND si.docstatus = 1
 	"""
 
+
 def get_pi_matching_query(amount_condition):
 	# get matching purchase invoice query
 	return f"""
@@ -544,11 +603,16 @@
 			AND cash_bank_account  = %(bank_account)s
 	"""
 
+
 def get_ec_matching_query(bank_account, company, amount_condition):
 	# get matching Expense Claim query
-	mode_of_payments = [x["parent"] for x in frappe.db.get_all("Mode of Payment Account",
-			filters={"default_account": bank_account}, fields=["parent"])]
-	mode_of_payments = '(\'' + '\', \''.join(mode_of_payments) + '\' )'
+	mode_of_payments = [
+		x["parent"]
+		for x in frappe.db.get_all(
+			"Mode of Payment Account", filters={"default_account": bank_account}, fields=["parent"]
+		)
+	]
+	mode_of_payments = "('" + "', '".join(mode_of_payments) + "' )"
 	company_currency = get_company_currency(company)
 	return f"""
 		SELECT
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
index 990d6d9..a964965 100644
--- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
@@ -200,7 +200,7 @@
 				})
 				.then((result) => {
 					if (result.length > 0) {
-						frm.add_custom_button("Report Error", () => {
+						frm.add_custom_button(__("Report Error"), () => {
 							let fake_xhr = {
 								responseText: JSON.stringify({
 									exc: result[0].error,
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
index 1403303..3f5c064 100644
--- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
@@ -18,6 +18,7 @@
 
 INVALID_VALUES = ("", None)
 
+
 class BankStatementImport(DataImport):
 	def __init__(self, *args, **kwargs):
 		super(BankStatementImport, self).__init__(*args, **kwargs)
@@ -49,16 +50,14 @@
 			self.import_file, self.google_sheets_url
 		)
 
-		if 'Bank Account' not in json.dumps(preview['columns']):
+		if "Bank Account" not in json.dumps(preview["columns"]):
 			frappe.throw(_("Please add the Bank Account column"))
 
 		from frappe.core.page.background_jobs.background_jobs import get_info
 		from frappe.utils.scheduler import is_scheduler_inactive
 
 		if is_scheduler_inactive() and not frappe.flags.in_test:
-			frappe.throw(
-				_("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive")
-			)
+			frappe.throw(_("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive"))
 
 		enqueued_jobs = [d.get("job_name") for d in get_info()]
 
@@ -81,21 +80,25 @@
 
 		return False
 
+
 @frappe.whitelist()
 def get_preview_from_template(data_import, import_file=None, google_sheets_url=None):
 	return frappe.get_doc("Bank Statement Import", data_import).get_preview_from_template(
 		import_file, google_sheets_url
 	)
 
+
 @frappe.whitelist()
 def form_start_import(data_import):
 	return frappe.get_doc("Bank Statement Import", data_import).start_import()
 
+
 @frappe.whitelist()
 def download_errored_template(data_import_name):
 	data_import = frappe.get_doc("Bank Statement Import", data_import_name)
 	data_import.export_errored_rows()
 
+
 def parse_data_from_template(raw_data):
 	data = []
 
@@ -108,7 +111,10 @@
 
 	return data
 
-def start_import(data_import, bank_account, import_file_path, google_sheets_url, bank, template_options):
+
+def start_import(
+	data_import, bank_account, import_file_path, google_sheets_url, bank, template_options
+):
 	"""This method runs in background job"""
 
 	update_mapping_db(bank, template_options)
@@ -116,7 +122,7 @@
 	data_import = frappe.get_doc("Bank Statement Import", data_import)
 	file = import_file_path if import_file_path else google_sheets_url
 
-	import_file = ImportFile("Bank Transaction", file = file, import_type="Insert New Records")
+	import_file = ImportFile("Bank Transaction", file=file, import_type="Insert New Records")
 
 	data = parse_data_from_template(import_file.raw_data)
 
@@ -130,22 +136,24 @@
 	except Exception:
 		frappe.db.rollback()
 		data_import.db_set("status", "Error")
-		frappe.log_error(title=data_import.name)
+		data_import.log_error("Bank Statement Import failed")
 	finally:
 		frappe.flags.in_import = False
 
 	frappe.publish_realtime("data_import_refresh", {"data_import": data_import.name})
 
+
 def update_mapping_db(bank, template_options):
 	bank = frappe.get_doc("Bank", bank)
 	for d in bank.bank_transaction_mapping:
 		d.delete()
 
 	for d in json.loads(template_options)["column_to_field_map"].items():
-		bank.append("bank_transaction_mapping", {"bank_transaction_field":  d[1] ,"file_field": d[0]} )
+		bank.append("bank_transaction_mapping", {"bank_transaction_field": d[1], "file_field": d[0]})
 
 	bank.save()
 
+
 def add_bank_account(data, bank_account):
 	bank_account_loc = None
 	if "Bank Account" not in data[0]:
@@ -161,6 +169,7 @@
 		else:
 			row.append(bank_account)
 
+
 def write_files(import_file, data):
 	full_file_path = import_file.file_doc.get_full_path()
 	parts = import_file.file_doc.get_extension()
@@ -168,11 +177,12 @@
 	extension = extension.lstrip(".")
 
 	if extension == "csv":
-		with open(full_file_path, 'w', newline='') as file:
+		with open(full_file_path, "w", newline="") as file:
 			writer = csv.writer(file)
 			writer.writerows(data)
 	elif extension == "xlsx" or "xls":
-		write_xlsx(data, "trans", file_path = full_file_path)
+		write_xlsx(data, "trans", file_path=full_file_path)
+
 
 def write_xlsx(data, sheet_name, wb=None, column_widths=None, file_path=None):
 	# from xlsx utils with changes
@@ -187,19 +197,19 @@
 			ws.column_dimensions[get_column_letter(i + 1)].width = column_width
 
 	row1 = ws.row_dimensions[1]
-	row1.font = Font(name='Calibri', bold=True)
+	row1.font = Font(name="Calibri", bold=True)
 
 	for row in data:
 		clean_row = []
 		for item in row:
-			if isinstance(item, str) and (sheet_name not in ['Data Import Template', 'Data Export']):
+			if isinstance(item, str) and (sheet_name not in ["Data Import Template", "Data Export"]):
 				value = handle_html(item)
 			else:
 				value = item
 
 			if isinstance(item, str) and next(ILLEGAL_CHARACTERS_RE.finditer(value), None):
 				# Remove illegal characters from the string
-				value = re.sub(ILLEGAL_CHARACTERS_RE, '', value)
+				value = re.sub(ILLEGAL_CHARACTERS_RE, "", value)
 
 			clean_row.append(value)
 
@@ -208,19 +218,20 @@
 	wb.save(file_path)
 	return True
 
+
 @frappe.whitelist()
 def upload_bank_statement(**args):
 	args = frappe._dict(args)
 	bsi = frappe.new_doc("Bank Statement Import")
 
 	if args.company:
-		bsi.update({
-			"company": args.company,
-		})
+		bsi.update(
+			{
+				"company": args.company,
+			}
+		)
 
 	if args.bank_account:
-		bsi.update({
-			"bank_account": args.bank_account
-		})
+		bsi.update({"bank_account": args.bank_account})
 
 	return bsi
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
index 88aa7ef..2bdaa10 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
@@ -134,7 +134,8 @@
   {
    "fieldname": "allocated_amount",
    "fieldtype": "Currency",
-   "label": "Allocated Amount"
+   "label": "Allocated Amount",
+   "options": "currency"
   },
   {
    "fieldname": "amended_from",
@@ -152,7 +153,8 @@
   {
    "fieldname": "unallocated_amount",
    "fieldtype": "Currency",
-   "label": "Unallocated Amount"
+   "label": "Unallocated Amount",
+   "options": "currency"
   },
   {
    "fieldname": "party_section",
@@ -192,10 +194,11 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2021-04-14 17:31:58.963529",
+ "modified": "2022-03-21 19:05:04.208222",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Bank Transaction",
+ "naming_rule": "By \"Naming Series\" field",
  "owner": "Administrator",
  "permissions": [
   {
@@ -242,6 +245,7 @@
  ],
  "sort_field": "date",
  "sort_order": "DESC",
+ "states": [],
  "title_field": "bank_account",
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
index a476cab..9a0891f 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
@@ -29,17 +29,26 @@
 
 	def update_allocations(self):
 		if self.payment_entries:
-			allocated_amount = reduce(lambda x, y: flt(x) + flt(y), [x.allocated_amount for x in self.payment_entries])
+			allocated_amount = reduce(
+				lambda x, y: flt(x) + flt(y), [x.allocated_amount for x in self.payment_entries]
+			)
 		else:
 			allocated_amount = 0
 
 		if allocated_amount:
 			frappe.db.set_value(self.doctype, self.name, "allocated_amount", flt(allocated_amount))
-			frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit)) - flt(allocated_amount))
+			frappe.db.set_value(
+				self.doctype,
+				self.name,
+				"unallocated_amount",
+				abs(flt(self.withdrawal) - flt(self.deposit)) - flt(allocated_amount),
+			)
 
 		else:
 			frappe.db.set_value(self.doctype, self.name, "allocated_amount", 0)
-			frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit)))
+			frappe.db.set_value(
+				self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit))
+			)
 
 		amount = self.deposit or self.withdrawal
 		if amount == self.allocated_amount:
@@ -49,8 +58,14 @@
 
 	def clear_linked_payment_entries(self, for_cancel=False):
 		for payment_entry in self.payment_entries:
-			if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim", "Loan Repayment",
-				"Loan Disbursement"]:
+			if payment_entry.payment_document in [
+				"Payment Entry",
+				"Journal Entry",
+				"Purchase Invoice",
+				"Expense Claim",
+				"Loan Repayment",
+				"Loan Disbursement",
+			]:
 				self.clear_simple_entry(payment_entry, for_cancel=for_cancel)
 
 			elif payment_entry.payment_document == "Sales Invoice":
@@ -58,38 +73,41 @@
 
 	def clear_simple_entry(self, payment_entry, for_cancel=False):
 		if payment_entry.payment_document == "Payment Entry":
-			if frappe.db.get_value("Payment Entry", payment_entry.payment_entry, "payment_type") == "Internal Transfer":
+			if (
+				frappe.db.get_value("Payment Entry", payment_entry.payment_entry, "payment_type")
+				== "Internal Transfer"
+			):
 				if len(get_reconciled_bank_transactions(payment_entry)) < 2:
 					return
 
 		clearance_date = self.date if not for_cancel else None
 		frappe.db.set_value(
-			payment_entry.payment_document, payment_entry.payment_entry,
-			"clearance_date", clearance_date)
+			payment_entry.payment_document, payment_entry.payment_entry, "clearance_date", clearance_date
+		)
 
 	def clear_sales_invoice(self, payment_entry, for_cancel=False):
 		clearance_date = self.date if not for_cancel else None
 		frappe.db.set_value(
 			"Sales Invoice Payment",
-			dict(
-				parenttype=payment_entry.payment_document,
-				parent=payment_entry.payment_entry
-			),
-			"clearance_date", clearance_date)
+			dict(parenttype=payment_entry.payment_document, parent=payment_entry.payment_entry),
+			"clearance_date",
+			clearance_date,
+		)
+
 
 def get_reconciled_bank_transactions(payment_entry):
 	reconciled_bank_transactions = frappe.get_all(
-		'Bank Transaction Payments',
-		filters = {
-			'payment_entry': payment_entry.payment_entry
-		},
-		fields = ['parent']
+		"Bank Transaction Payments",
+		filters={"payment_entry": payment_entry.payment_entry},
+		fields=["parent"],
 	)
 
 	return reconciled_bank_transactions
 
+
 def get_total_allocated_amount(payment_entry):
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		SELECT
 			SUM(btp.allocated_amount) as allocated_amount,
 			bt.name
@@ -102,43 +120,73 @@
 		AND
 			btp.payment_entry = %s
 		AND
-			bt.docstatus = 1""", (payment_entry.payment_document, payment_entry.payment_entry), as_dict=True)
+			bt.docstatus = 1""",
+		(payment_entry.payment_document, payment_entry.payment_entry),
+		as_dict=True,
+	)
+
 
 def get_paid_amount(payment_entry, currency, bank_account):
 	if payment_entry.payment_document in ["Payment Entry", "Sales Invoice", "Purchase Invoice"]:
 
 		paid_amount_field = "paid_amount"
-		if payment_entry.payment_document == 'Payment Entry':
+		if payment_entry.payment_document == "Payment Entry":
 			doc = frappe.get_doc("Payment Entry", payment_entry.payment_entry)
-			paid_amount_field = ("base_paid_amount"
-				if doc.paid_to_account_currency == currency else "paid_amount")
 
-		return frappe.db.get_value(payment_entry.payment_document,
-			payment_entry.payment_entry, paid_amount_field)
+			if doc.payment_type == "Receive":
+				paid_amount_field = (
+					"received_amount" if doc.paid_to_account_currency == currency else "base_received_amount"
+				)
+			elif doc.payment_type == "Pay":
+				paid_amount_field = (
+					"paid_amount" if doc.paid_to_account_currency == currency else "base_paid_amount"
+				)
+
+		return frappe.db.get_value(
+			payment_entry.payment_document, payment_entry.payment_entry, paid_amount_field
+		)
 
 	elif payment_entry.payment_document == "Journal Entry":
-		return frappe.db.get_value('Journal Entry Account', {'parent': payment_entry.payment_entry, 'account': bank_account},
-			"sum(credit_in_account_currency)")
+		return frappe.db.get_value(
+			"Journal Entry Account",
+			{"parent": payment_entry.payment_entry, "account": bank_account},
+			"sum(credit_in_account_currency)",
+		)
 
 	elif payment_entry.payment_document == "Expense Claim":
-		return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "total_amount_reimbursed")
+		return frappe.db.get_value(
+			payment_entry.payment_document, payment_entry.payment_entry, "total_amount_reimbursed"
+		)
 
 	elif payment_entry.payment_document == "Loan Disbursement":
-		return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "disbursed_amount")
+		return frappe.db.get_value(
+			payment_entry.payment_document, payment_entry.payment_entry, "disbursed_amount"
+		)
 
 	elif payment_entry.payment_document == "Loan Repayment":
-		return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "amount_paid")
+		return frappe.db.get_value(
+			payment_entry.payment_document, payment_entry.payment_entry, "amount_paid"
+		)
 
 	else:
-		frappe.throw("Please reconcile {0}: {1} manually".format(payment_entry.payment_document, payment_entry.payment_entry))
+		frappe.throw(
+			"Please reconcile {0}: {1} manually".format(
+				payment_entry.payment_document, payment_entry.payment_entry
+			)
+		)
+
 
 @frappe.whitelist()
 def unclear_reference_payment(doctype, docname):
 	if frappe.db.exists(doctype, docname):
 		doc = frappe.get_doc(doctype, docname)
 		if doctype == "Sales Invoice":
-			frappe.db.set_value("Sales Invoice Payment", dict(parenttype=doc.payment_document,
-				parent=doc.payment_entry), "clearance_date", None)
+			frappe.db.set_value(
+				"Sales Invoice Payment",
+				dict(parenttype=doc.payment_document, parent=doc.payment_entry),
+				"clearance_date",
+				None,
+			)
 		else:
 			frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None)
 
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py
index cca8a88..372c53d 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py
@@ -18,12 +18,14 @@
 		fcontent = frappe.local.uploaded_file
 		fname = frappe.local.uploaded_filename
 
-	if frappe.safe_encode(fname).lower().endswith("csv".encode('utf-8')):
+	if frappe.safe_encode(fname).lower().endswith("csv".encode("utf-8")):
 		from frappe.utils.csvutils import read_csv_content
+
 		rows = read_csv_content(fcontent, False)
 
-	elif frappe.safe_encode(fname).lower().endswith("xlsx".encode('utf-8')):
+	elif frappe.safe_encode(fname).lower().endswith("xlsx".encode("utf-8")):
 		from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
+
 		rows = read_xlsx_file_from_attached_file(fcontent=fcontent)
 
 	columns = rows[0]
@@ -43,12 +45,10 @@
 			continue
 		fields = {}
 		for key, value in header_map.items():
-			fields.update({key: d[int(value)-1]})
+			fields.update({key: d[int(value) - 1]})
 
 		try:
-			bank_transaction = frappe.get_doc({
-				"doctype": "Bank Transaction"
-			})
+			bank_transaction = frappe.get_doc({"doctype": "Bank Transaction"})
 			bank_transaction.update(fields)
 			bank_transaction.date = getdate(parse_date(bank_transaction.date))
 			bank_transaction.bank_account = bank_account
@@ -56,11 +56,12 @@
 			bank_transaction.submit()
 			success += 1
 		except Exception:
-			frappe.log_error(frappe.get_traceback())
+			bank_transaction.log_error("Bank entry creation failed")
 			errors += 1
 
 	return {"success": success, "errors": errors}
 
+
 def get_header_mapping(columns, bank_account):
 	mapping = get_bank_mapping(bank_account)
 
@@ -71,10 +72,11 @@
 
 	return header_map
 
+
 def get_bank_mapping(bank_account):
 	bank_name = frappe.db.get_value("Bank Account", bank_account, "bank")
 	bank = frappe.get_doc("Bank", bank_name)
 
-	mapping = {row.file_field:row.bank_transaction_field for row in bank.bank_transaction_mapping}
+	mapping = {row.file_field: row.bank_transaction_field for row in bank.bank_transaction_mapping}
 
 	return mapping
diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
index d84b8e0..8cbed4c 100644
--- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
@@ -17,6 +17,7 @@
 
 test_dependencies = ["Item", "Cost Center"]
 
+
 class TestBankTransaction(unittest.TestCase):
 	@classmethod
 	def setUpClass(cls):
@@ -41,21 +42,34 @@
 
 	# This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction.
 	def test_linked_payments(self):
-		bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"))
-		linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match'])
+		bank_transaction = frappe.get_doc(
+			"Bank Transaction",
+			dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"),
+		)
+		linked_payments = get_linked_payments(bank_transaction.name, ["payment_entry", "exact_match"])
 		self.assertTrue(linked_payments[0][6] == "Conrad Electronic")
 
 	# This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment
 	def test_reconcile(self):
-		bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G"))
+		bank_transaction = frappe.get_doc(
+			"Bank Transaction",
+			dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G"),
+		)
 		payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1700))
-		vouchers = json.dumps([{
-		"payment_doctype":"Payment Entry",
-		"payment_name":payment.name,
-		"amount":bank_transaction.unallocated_amount}])
+		vouchers = json.dumps(
+			[
+				{
+					"payment_doctype": "Payment Entry",
+					"payment_name": payment.name,
+					"amount": bank_transaction.unallocated_amount,
+				}
+			]
+		)
 		reconcile_vouchers(bank_transaction.name, vouchers)
 
-		unallocated_amount = frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount")
+		unallocated_amount = frappe.db.get_value(
+			"Bank Transaction", bank_transaction.name, "unallocated_amount"
+		)
 		self.assertTrue(unallocated_amount == 0)
 
 		clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
@@ -69,122 +83,177 @@
 
 	# Check if ERPNext can correctly filter a linked payments based on the debit/credit amount
 	def test_debit_credit_output(self):
-		bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"))
-		linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match'])
+		bank_transaction = frappe.get_doc(
+			"Bank Transaction",
+			dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"),
+		)
+		linked_payments = get_linked_payments(bank_transaction.name, ["payment_entry", "exact_match"])
 		self.assertTrue(linked_payments[0][3])
 
 	# Check error if already reconciled
 	def test_already_reconciled(self):
-		bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
+		bank_transaction = frappe.get_doc(
+			"Bank Transaction",
+			dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"),
+		)
 		payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200))
-		vouchers = json.dumps([{
-			"payment_doctype":"Payment Entry",
-			"payment_name":payment.name,
-			"amount":bank_transaction.unallocated_amount}])
+		vouchers = json.dumps(
+			[
+				{
+					"payment_doctype": "Payment Entry",
+					"payment_name": payment.name,
+					"amount": bank_transaction.unallocated_amount,
+				}
+			]
+		)
 		reconcile_vouchers(bank_transaction.name, vouchers)
 
-		bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
+		bank_transaction = frappe.get_doc(
+			"Bank Transaction",
+			dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"),
+		)
 		payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200))
-		vouchers = json.dumps([{
-			"payment_doctype":"Payment Entry",
-			"payment_name":payment.name,
-			"amount":bank_transaction.unallocated_amount}])
-		self.assertRaises(frappe.ValidationError, reconcile_vouchers, bank_transaction_name=bank_transaction.name, vouchers=vouchers)
+		vouchers = json.dumps(
+			[
+				{
+					"payment_doctype": "Payment Entry",
+					"payment_name": payment.name,
+					"amount": bank_transaction.unallocated_amount,
+				}
+			]
+		)
+		self.assertRaises(
+			frappe.ValidationError,
+			reconcile_vouchers,
+			bank_transaction_name=bank_transaction.name,
+			vouchers=vouchers,
+		)
 
 	# Raise an error if debitor transaction vs debitor payment
 	def test_clear_sales_invoice(self):
-		bank_transaction = frappe.get_doc("Bank Transaction", dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio"))
+		bank_transaction = frappe.get_doc(
+			"Bank Transaction",
+			dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio"),
+		)
 		payment = frappe.get_doc("Sales Invoice", dict(customer="Fayva", status=["=", "Paid"]))
-		vouchers = json.dumps([{
-			"payment_doctype":"Sales Invoice",
-			"payment_name":payment.name,
-			"amount":bank_transaction.unallocated_amount}])
+		vouchers = json.dumps(
+			[
+				{
+					"payment_doctype": "Sales Invoice",
+					"payment_name": payment.name,
+					"amount": bank_transaction.unallocated_amount,
+				}
+			]
+		)
 		reconcile_vouchers(bank_transaction.name, vouchers=vouchers)
 
-		self.assertEqual(frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount"), 0)
-		self.assertTrue(frappe.db.get_value("Sales Invoice Payment", dict(parent=payment.name), "clearance_date") is not None)
+		self.assertEqual(
+			frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount"), 0
+		)
+		self.assertTrue(
+			frappe.db.get_value("Sales Invoice Payment", dict(parent=payment.name), "clearance_date")
+			is not None
+		)
+
 
 def create_bank_account(bank_name="Citi Bank", account_name="_Test Bank - _TC"):
 	try:
-		frappe.get_doc({
-			"doctype": "Bank",
-			"bank_name":bank_name,
-		}).insert(ignore_if_duplicate=True)
+		frappe.get_doc(
+			{
+				"doctype": "Bank",
+				"bank_name": bank_name,
+			}
+		).insert(ignore_if_duplicate=True)
 	except frappe.DuplicateEntryError:
 		pass
 
 	try:
-		frappe.get_doc({
-			"doctype": "Bank Account",
-			"account_name":"Checking Account",
-			"bank": bank_name,
-			"account": account_name
-		}).insert(ignore_if_duplicate=True)
+		frappe.get_doc(
+			{
+				"doctype": "Bank Account",
+				"account_name": "Checking Account",
+				"bank": bank_name,
+				"account": account_name,
+			}
+		).insert(ignore_if_duplicate=True)
 	except frappe.DuplicateEntryError:
 		pass
 
+
 def add_transactions():
 	create_bank_account()
 
-	doc = frappe.get_doc({
-		"doctype": "Bank Transaction",
-		"description":"1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G",
-		"date": "2018-10-23",
-		"deposit": 1200,
-		"currency": "INR",
-		"bank_account": "Checking Account - Citi Bank"
-	}).insert()
+	doc = frappe.get_doc(
+		{
+			"doctype": "Bank Transaction",
+			"description": "1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G",
+			"date": "2018-10-23",
+			"deposit": 1200,
+			"currency": "INR",
+			"bank_account": "Checking Account - Citi Bank",
+		}
+	).insert()
 	doc.submit()
 
-	doc = frappe.get_doc({
-		"doctype": "Bank Transaction",
-		"description":"1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G",
-		"date": "2018-10-23",
-		"deposit": 1700,
-		"currency": "INR",
-		"bank_account": "Checking Account - Citi Bank"
-	}).insert()
+	doc = frappe.get_doc(
+		{
+			"doctype": "Bank Transaction",
+			"description": "1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G",
+			"date": "2018-10-23",
+			"deposit": 1700,
+			"currency": "INR",
+			"bank_account": "Checking Account - Citi Bank",
+		}
+	).insert()
 	doc.submit()
 
-	doc = frappe.get_doc({
-		"doctype": "Bank Transaction",
-		"description":"Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic",
-		"date": "2018-10-26",
-		"withdrawal": 690,
-		"currency": "INR",
-		"bank_account": "Checking Account - Citi Bank"
-	}).insert()
+	doc = frappe.get_doc(
+		{
+			"doctype": "Bank Transaction",
+			"description": "Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic",
+			"date": "2018-10-26",
+			"withdrawal": 690,
+			"currency": "INR",
+			"bank_account": "Checking Account - Citi Bank",
+		}
+	).insert()
 	doc.submit()
 
-	doc = frappe.get_doc({
-		"doctype": "Bank Transaction",
-		"description":"Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07",
-		"date": "2018-10-27",
-		"deposit": 3900,
-		"currency": "INR",
-		"bank_account": "Checking Account - Citi Bank"
-	}).insert()
+	doc = frappe.get_doc(
+		{
+			"doctype": "Bank Transaction",
+			"description": "Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07",
+			"date": "2018-10-27",
+			"deposit": 3900,
+			"currency": "INR",
+			"bank_account": "Checking Account - Citi Bank",
+		}
+	).insert()
 	doc.submit()
 
-	doc = frappe.get_doc({
-		"doctype": "Bank Transaction",
-		"description":"I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio",
-		"date": "2018-10-27",
-		"withdrawal": 109080,
-		"currency": "INR",
-		"bank_account": "Checking Account - Citi Bank"
-	}).insert()
+	doc = frappe.get_doc(
+		{
+			"doctype": "Bank Transaction",
+			"description": "I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio",
+			"date": "2018-10-27",
+			"withdrawal": 109080,
+			"currency": "INR",
+			"bank_account": "Checking Account - Citi Bank",
+		}
+	).insert()
 	doc.submit()
 
 
 def add_vouchers():
 	try:
-		frappe.get_doc({
-			"doctype": "Supplier",
-			"supplier_group":"All Supplier Groups",
-			"supplier_type": "Company",
-			"supplier_name": "Conrad Electronic"
-		}).insert(ignore_if_duplicate=True)
+		frappe.get_doc(
+			{
+				"doctype": "Supplier",
+				"supplier_group": "All Supplier Groups",
+				"supplier_type": "Company",
+				"supplier_name": "Conrad Electronic",
+			}
+		).insert(ignore_if_duplicate=True)
 
 	except frappe.DuplicateEntryError:
 		pass
@@ -198,12 +267,14 @@
 	pe.submit()
 
 	try:
-		frappe.get_doc({
-			"doctype": "Supplier",
-			"supplier_group":"All Supplier Groups",
-			"supplier_type": "Company",
-			"supplier_name": "Mr G"
-		}).insert(ignore_if_duplicate=True)
+		frappe.get_doc(
+			{
+				"doctype": "Supplier",
+				"supplier_group": "All Supplier Groups",
+				"supplier_type": "Company",
+				"supplier_name": "Mr G",
+			}
+		).insert(ignore_if_duplicate=True)
 	except frappe.DuplicateEntryError:
 		pass
 
@@ -222,26 +293,30 @@
 	pe.submit()
 
 	try:
-		frappe.get_doc({
-			"doctype": "Supplier",
-			"supplier_group":"All Supplier Groups",
-			"supplier_type": "Company",
-			"supplier_name": "Poore Simon's"
-		}).insert(ignore_if_duplicate=True)
+		frappe.get_doc(
+			{
+				"doctype": "Supplier",
+				"supplier_group": "All Supplier Groups",
+				"supplier_type": "Company",
+				"supplier_name": "Poore Simon's",
+			}
+		).insert(ignore_if_duplicate=True)
 	except frappe.DuplicateEntryError:
 		pass
 
 	try:
-		frappe.get_doc({
-			"doctype": "Customer",
-			"customer_group":"All Customer Groups",
-			"customer_type": "Company",
-			"customer_name": "Poore Simon's"
-		}).insert(ignore_if_duplicate=True)
+		frappe.get_doc(
+			{
+				"doctype": "Customer",
+				"customer_group": "All Customer Groups",
+				"customer_type": "Company",
+				"customer_name": "Poore Simon's",
+			}
+		).insert(ignore_if_duplicate=True)
 	except frappe.DuplicateEntryError:
 		pass
 
-	pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900, is_paid=1, do_not_save =1)
+	pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900, is_paid=1, do_not_save=1)
 	pi.cash_bank_account = "_Test Bank - _TC"
 	pi.insert()
 	pi.submit()
@@ -261,33 +336,31 @@
 	pe.submit()
 
 	try:
-		frappe.get_doc({
-			"doctype": "Customer",
-			"customer_group":"All Customer Groups",
-			"customer_type": "Company",
-			"customer_name": "Fayva"
-		}).insert(ignore_if_duplicate=True)
+		frappe.get_doc(
+			{
+				"doctype": "Customer",
+				"customer_group": "All Customer Groups",
+				"customer_type": "Company",
+				"customer_name": "Fayva",
+			}
+		).insert(ignore_if_duplicate=True)
 	except frappe.DuplicateEntryError:
 		pass
 
-	mode_of_payment = frappe.get_doc({
-		"doctype": "Mode of Payment",
-		"name": "Cash"
-	})
+	mode_of_payment = frappe.get_doc({"doctype": "Mode of Payment", "name": "Cash"})
 
-	if not frappe.db.get_value('Mode of Payment Account', {'company': "_Test Company", 'parent': "Cash"}):
-		mode_of_payment.append("accounts", {
-			"company": "_Test Company",
-			"default_account": "_Test Bank - _TC"
-		})
+	if not frappe.db.get_value(
+		"Mode of Payment Account", {"company": "_Test Company", "parent": "Cash"}
+	):
+		mode_of_payment.append(
+			"accounts", {"company": "_Test Company", "default_account": "_Test Bank - _TC"}
+		)
 		mode_of_payment.save()
 
 	si = create_sales_invoice(customer="Fayva", qty=1, rate=109080, do_not_save=1)
 	si.is_pos = 1
-	si.append("payments", {
-		"mode_of_payment": "Cash",
-		"account": "_Test Bank - _TC",
-		"amount": 109080
-	})
+	si.append(
+		"payments", {"mode_of_payment": "Cash", "account": "_Test Bank - _TC", "amount": 109080}
+	)
 	si.insert()
 	si.submit()
diff --git a/erpnext/accounts/doctype/budget/budget.py b/erpnext/accounts/doctype/budget/budget.py
index 492bb365..5527f9f 100644
--- a/erpnext/accounts/doctype/budget/budget.py
+++ b/erpnext/accounts/doctype/budget/budget.py
@@ -14,13 +14,19 @@
 from erpnext.accounts.utils import get_fiscal_year
 
 
-class BudgetError(frappe.ValidationError): pass
-class DuplicateBudgetError(frappe.ValidationError): pass
+class BudgetError(frappe.ValidationError):
+	pass
+
+
+class DuplicateBudgetError(frappe.ValidationError):
+	pass
+
 
 class Budget(Document):
 	def autoname(self):
-		self.name = make_autoname(self.get(frappe.scrub(self.budget_against))
-			+ "/" + self.fiscal_year + "/.###")
+		self.name = make_autoname(
+			self.get(frappe.scrub(self.budget_against)) + "/" + self.fiscal_year + "/.###"
+		)
 
 	def validate(self):
 		if not self.get(frappe.scrub(self.budget_against)):
@@ -35,34 +41,44 @@
 		budget_against = self.get(budget_against_field)
 
 		accounts = [d.account for d in self.accounts] or []
-		existing_budget = frappe.db.sql("""
+		existing_budget = frappe.db.sql(
+			"""
 			select
 				b.name, ba.account from `tabBudget` b, `tabBudget Account` ba
 			where
 				ba.parent = b.name and b.docstatus < 2 and b.company = %s and %s=%s and
 				b.fiscal_year=%s and b.name != %s and ba.account in (%s) """
-				% ('%s', budget_against_field, '%s', '%s', '%s', ','.join(['%s'] * len(accounts))),
-			(self.company, budget_against, self.fiscal_year, self.name) + tuple(accounts), as_dict=1)
+			% ("%s", budget_against_field, "%s", "%s", "%s", ",".join(["%s"] * len(accounts))),
+			(self.company, budget_against, self.fiscal_year, self.name) + tuple(accounts),
+			as_dict=1,
+		)
 
 		for d in existing_budget:
-			frappe.throw(_("Another Budget record '{0}' already exists against {1} '{2}' and account '{3}' for fiscal year {4}")
-				.format(d.name, self.budget_against, budget_against, d.account, self.fiscal_year), DuplicateBudgetError)
+			frappe.throw(
+				_(
+					"Another Budget record '{0}' already exists against {1} '{2}' and account '{3}' for fiscal year {4}"
+				).format(d.name, self.budget_against, budget_against, d.account, self.fiscal_year),
+				DuplicateBudgetError,
+			)
 
 	def validate_accounts(self):
 		account_list = []
-		for d in self.get('accounts'):
+		for d in self.get("accounts"):
 			if d.account:
-				account_details = frappe.db.get_value("Account", d.account,
-					["is_group", "company", "report_type"], as_dict=1)
+				account_details = frappe.db.get_value(
+					"Account", d.account, ["is_group", "company", "report_type"], as_dict=1
+				)
 
 				if account_details.is_group:
 					frappe.throw(_("Budget cannot be assigned against Group Account {0}").format(d.account))
 				elif account_details.company != self.company:
-					frappe.throw(_("Account {0} does not belongs to company {1}")
-						.format(d.account, self.company))
+					frappe.throw(_("Account {0} does not belongs to company {1}").format(d.account, self.company))
 				elif account_details.report_type != "Profit and Loss":
-					frappe.throw(_("Budget cannot be assigned against {0}, as it's not an Income or Expense account")
-						.format(d.account))
+					frappe.throw(
+						_("Budget cannot be assigned against {0}, as it's not an Income or Expense account").format(
+							d.account
+						)
+					)
 
 				if d.account in account_list:
 					frappe.throw(_("Account {0} has been entered multiple times").format(d.account))
@@ -70,51 +86,66 @@
 					account_list.append(d.account)
 
 	def set_null_value(self):
-		if self.budget_against == 'Cost Center':
+		if self.budget_against == "Cost Center":
 			self.project = None
 		else:
 			self.cost_center = None
 
 	def validate_applicable_for(self):
-		if (self.applicable_on_material_request
-			and not (self.applicable_on_purchase_order and self.applicable_on_booking_actual_expenses)):
-			frappe.throw(_("Please enable Applicable on Purchase Order and Applicable on Booking Actual Expenses"))
+		if self.applicable_on_material_request and not (
+			self.applicable_on_purchase_order and self.applicable_on_booking_actual_expenses
+		):
+			frappe.throw(
+				_("Please enable Applicable on Purchase Order and Applicable on Booking Actual Expenses")
+			)
 
-		elif (self.applicable_on_purchase_order
-			and not (self.applicable_on_booking_actual_expenses)):
+		elif self.applicable_on_purchase_order and not (self.applicable_on_booking_actual_expenses):
 			frappe.throw(_("Please enable Applicable on Booking Actual Expenses"))
 
-		elif not(self.applicable_on_material_request
-			or self.applicable_on_purchase_order or self.applicable_on_booking_actual_expenses):
+		elif not (
+			self.applicable_on_material_request
+			or self.applicable_on_purchase_order
+			or self.applicable_on_booking_actual_expenses
+		):
 			self.applicable_on_booking_actual_expenses = 1
 
+
 def validate_expense_against_budget(args):
 	args = frappe._dict(args)
 
-	if args.get('company') and not args.fiscal_year:
-		args.fiscal_year = get_fiscal_year(args.get('posting_date'), company=args.get('company'))[0]
-		frappe.flags.exception_approver_role = frappe.get_cached_value('Company',
-			args.get('company'),  'exception_budget_approver_role')
+	if args.get("company") and not args.fiscal_year:
+		args.fiscal_year = get_fiscal_year(args.get("posting_date"), company=args.get("company"))[0]
+		frappe.flags.exception_approver_role = frappe.get_cached_value(
+			"Company", args.get("company"), "exception_budget_approver_role"
+		)
 
 	if not args.account:
 		args.account = args.get("expense_account")
 
-	if not (args.get('account') and args.get('cost_center')) and args.item_code:
+	if not (args.get("account") and args.get("cost_center")) and args.item_code:
 		args.cost_center, args.account = get_item_details(args)
 
 	if not args.account:
 		return
 
-	for budget_against in ['project', 'cost_center'] + get_accounting_dimensions():
-		if (args.get(budget_against) and args.account
-				and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})):
+	for budget_against in ["project", "cost_center"] + get_accounting_dimensions():
+		if (
+			args.get(budget_against)
+			and args.account
+			and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})
+		):
 
 			doctype = frappe.unscrub(budget_against)
 
-			if frappe.get_cached_value('DocType', doctype, 'is_tree'):
+			if frappe.get_cached_value("DocType", doctype, "is_tree"):
 				lft, rgt = frappe.db.get_value(doctype, args.get(budget_against), ["lft", "rgt"])
 				condition = """and exists(select name from `tab%s`
-					where lft<=%s and rgt>=%s and name=b.%s)""" % (doctype, lft, rgt, budget_against) #nosec
+					where lft<=%s and rgt>=%s and name=b.%s)""" % (
+					doctype,
+					lft,
+					rgt,
+					budget_against,
+				)  # nosec
 				args.is_tree = True
 			else:
 				condition = "and b.%s=%s" % (budget_against, frappe.db.escape(args.get(budget_against)))
@@ -123,7 +154,8 @@
 			args.budget_against_field = budget_against
 			args.budget_against_doctype = doctype
 
-			budget_records = frappe.db.sql("""
+			budget_records = frappe.db.sql(
+				"""
 				select
 					b.{budget_against_field} as budget_against, ba.budget_amount, b.monthly_distribution,
 					ifnull(b.applicable_on_material_request, 0) as for_material_request,
@@ -138,11 +170,17 @@
 					b.name=ba.parent and b.fiscal_year=%s
 					and ba.account=%s and b.docstatus=1
 					{condition}
-			""".format(condition=condition, budget_against_field=budget_against), (args.fiscal_year, args.account), as_dict=True) #nosec
+			""".format(
+					condition=condition, budget_against_field=budget_against
+				),
+				(args.fiscal_year, args.account),
+				as_dict=True,
+			)  # nosec
 
 			if budget_records:
 				validate_budget_records(args, budget_records)
 
+
 def validate_budget_records(args, budget_records):
 	for budget in budget_records:
 		if flt(budget.budget_amount):
@@ -150,88 +188,118 @@
 			yearly_action, monthly_action = get_actions(args, budget)
 
 			if monthly_action in ["Stop", "Warn"]:
-				budget_amount = get_accumulated_monthly_budget(budget.monthly_distribution,
-					args.posting_date, args.fiscal_year, budget.budget_amount)
+				budget_amount = get_accumulated_monthly_budget(
+					budget.monthly_distribution, args.posting_date, args.fiscal_year, budget.budget_amount
+				)
 
 				args["month_end_date"] = get_last_day(args.posting_date)
 
-				compare_expense_with_budget(args, budget_amount,
-					_("Accumulated Monthly"), monthly_action, budget.budget_against, amount)
+				compare_expense_with_budget(
+					args, budget_amount, _("Accumulated Monthly"), monthly_action, budget.budget_against, amount
+				)
 
-			if yearly_action in ("Stop", "Warn") and monthly_action != "Stop" \
-				and yearly_action != monthly_action:
-				compare_expense_with_budget(args, flt(budget.budget_amount),
-						_("Annual"), yearly_action, budget.budget_against, amount)
+			if (
+				yearly_action in ("Stop", "Warn")
+				and monthly_action != "Stop"
+				and yearly_action != monthly_action
+			):
+				compare_expense_with_budget(
+					args, flt(budget.budget_amount), _("Annual"), yearly_action, budget.budget_against, amount
+				)
+
 
 def compare_expense_with_budget(args, budget_amount, action_for, action, budget_against, amount=0):
 	actual_expense = amount or get_actual_expense(args)
 	if actual_expense > budget_amount:
 		diff = actual_expense - budget_amount
-		currency = frappe.get_cached_value('Company',  args.company,  'default_currency')
+		currency = frappe.get_cached_value("Company", args.company, "default_currency")
 
 		msg = _("{0} Budget for Account {1} against {2} {3} is {4}. It will exceed by {5}").format(
-				_(action_for), frappe.bold(args.account), args.budget_against_field,
-				frappe.bold(budget_against),
-				frappe.bold(fmt_money(budget_amount, currency=currency)),
-				frappe.bold(fmt_money(diff, currency=currency)))
+			_(action_for),
+			frappe.bold(args.account),
+			args.budget_against_field,
+			frappe.bold(budget_against),
+			frappe.bold(fmt_money(budget_amount, currency=currency)),
+			frappe.bold(fmt_money(diff, currency=currency)),
+		)
 
-		if (frappe.flags.exception_approver_role
-			and frappe.flags.exception_approver_role in frappe.get_roles(frappe.session.user)):
+		if (
+			frappe.flags.exception_approver_role
+			and frappe.flags.exception_approver_role in frappe.get_roles(frappe.session.user)
+		):
 			action = "Warn"
 
-		if action=="Stop":
+		if action == "Stop":
 			frappe.throw(msg, BudgetError)
 		else:
-			frappe.msgprint(msg, indicator='orange')
+			frappe.msgprint(msg, indicator="orange")
+
 
 def get_actions(args, budget):
 	yearly_action = budget.action_if_annual_budget_exceeded
 	monthly_action = budget.action_if_accumulated_monthly_budget_exceeded
 
-	if args.get('doctype') == 'Material Request' and budget.for_material_request:
+	if args.get("doctype") == "Material Request" and budget.for_material_request:
 		yearly_action = budget.action_if_annual_budget_exceeded_on_mr
 		monthly_action = budget.action_if_accumulated_monthly_budget_exceeded_on_mr
 
-	elif args.get('doctype') == 'Purchase Order' and budget.for_purchase_order:
+	elif args.get("doctype") == "Purchase Order" and budget.for_purchase_order:
 		yearly_action = budget.action_if_annual_budget_exceeded_on_po
 		monthly_action = budget.action_if_accumulated_monthly_budget_exceeded_on_po
 
 	return yearly_action, monthly_action
 
+
 def get_amount(args, budget):
 	amount = 0
 
-	if args.get('doctype') == 'Material Request' and budget.for_material_request:
-		amount = (get_requested_amount(args, budget)
-			+ get_ordered_amount(args, budget) + get_actual_expense(args))
+	if args.get("doctype") == "Material Request" and budget.for_material_request:
+		amount = (
+			get_requested_amount(args, budget) + get_ordered_amount(args, budget) + get_actual_expense(args)
+		)
 
-	elif args.get('doctype') == 'Purchase Order' and budget.for_purchase_order:
+	elif args.get("doctype") == "Purchase Order" and budget.for_purchase_order:
 		amount = get_ordered_amount(args, budget) + get_actual_expense(args)
 
 	return amount
 
-def get_requested_amount(args, budget):
-	item_code = args.get('item_code')
-	condition = get_other_condition(args, budget, 'Material Request')
 
-	data = frappe.db.sql(""" select ifnull((sum(child.stock_qty - child.ordered_qty) * rate), 0) as amount
+def get_requested_amount(args, budget):
+	item_code = args.get("item_code")
+	condition = get_other_condition(args, budget, "Material Request")
+
+	data = frappe.db.sql(
+		""" select ifnull((sum(child.stock_qty - child.ordered_qty) * rate), 0) as amount
 		from `tabMaterial Request Item` child, `tabMaterial Request` parent where parent.name = child.parent and
 		child.item_code = %s and parent.docstatus = 1 and child.stock_qty > child.ordered_qty and {0} and
-		parent.material_request_type = 'Purchase' and parent.status != 'Stopped'""".format(condition), item_code, as_list=1)
+		parent.material_request_type = 'Purchase' and parent.status != 'Stopped'""".format(
+			condition
+		),
+		item_code,
+		as_list=1,
+	)
 
 	return data[0][0] if data else 0
 
+
 def get_ordered_amount(args, budget):
-	item_code = args.get('item_code')
-	condition = get_other_condition(args, budget, 'Purchase Order')
+	item_code = args.get("item_code")
+	condition = get_other_condition(args, budget, "Purchase Order")
 
-	data = frappe.db.sql(""" select ifnull(sum(child.amount - child.billed_amt), 0) as amount
+	data = frappe.db.sql(
+		""" select ifnull(sum(child.amount - child.billed_amt), 0) as amount
 		from `tabPurchase Order Item` child, `tabPurchase Order` parent where
 		parent.name = child.parent and child.item_code = %s and parent.docstatus = 1 and child.amount > child.billed_amt
-		and parent.status != 'Closed' and {0}""".format(condition), item_code, as_list=1)
+		and parent.status != 'Closed' and {0}""".format(
+			condition
+		),
+		item_code,
+		as_list=1,
+	)
 
 	return data[0][0] if data else 0
 
+
 def get_other_condition(args, budget, for_doc):
 	condition = "expense_account = '%s'" % (args.expense_account)
 	budget_against_field = args.get("budget_against_field")
@@ -239,41 +307,51 @@
 	if budget_against_field and args.get(budget_against_field):
 		condition += " and child.%s = '%s'" % (budget_against_field, args.get(budget_against_field))
 
-	if args.get('fiscal_year'):
-		date_field = 'schedule_date' if for_doc == 'Material Request' else 'transaction_date'
-		start_date, end_date = frappe.db.get_value('Fiscal Year', args.get('fiscal_year'),
-			['year_start_date', 'year_end_date'])
+	if args.get("fiscal_year"):
+		date_field = "schedule_date" if for_doc == "Material Request" else "transaction_date"
+		start_date, end_date = frappe.db.get_value(
+			"Fiscal Year", args.get("fiscal_year"), ["year_start_date", "year_end_date"]
+		)
 
 		condition += """ and parent.%s
-			between '%s' and '%s' """ %(date_field, start_date, end_date)
+			between '%s' and '%s' """ % (
+			date_field,
+			start_date,
+			end_date,
+		)
 
 	return condition
 
+
 def get_actual_expense(args):
 	if not args.budget_against_doctype:
 		args.budget_against_doctype = frappe.unscrub(args.budget_against_field)
 
-	budget_against_field = args.get('budget_against_field')
-	condition1 = " and gle.posting_date <= %(month_end_date)s" \
-		if args.get("month_end_date") else ""
+	budget_against_field = args.get("budget_against_field")
+	condition1 = " and gle.posting_date <= %(month_end_date)s" if args.get("month_end_date") else ""
 
 	if args.is_tree:
-		lft_rgt = frappe.db.get_value(args.budget_against_doctype,
-			args.get(budget_against_field), ["lft", "rgt"], as_dict=1)
+		lft_rgt = frappe.db.get_value(
+			args.budget_against_doctype, args.get(budget_against_field), ["lft", "rgt"], as_dict=1
+		)
 
 		args.update(lft_rgt)
 
 		condition2 = """and exists(select name from `tab{doctype}`
 			where lft>=%(lft)s and rgt<=%(rgt)s
-			and name=gle.{budget_against_field})""".format(doctype=args.budget_against_doctype, #nosec
-			budget_against_field=budget_against_field)
+			and name=gle.{budget_against_field})""".format(
+			doctype=args.budget_against_doctype, budget_against_field=budget_against_field  # nosec
+		)
 	else:
 		condition2 = """and exists(select name from `tab{doctype}`
 		where name=gle.{budget_against} and
-		gle.{budget_against} = %({budget_against})s)""".format(doctype=args.budget_against_doctype,
-		budget_against = budget_against_field)
+		gle.{budget_against} = %({budget_against})s)""".format(
+			doctype=args.budget_against_doctype, budget_against=budget_against_field
+		)
 
-	amount  = flt(frappe.db.sql("""
+	amount = flt(
+		frappe.db.sql(
+			"""
 		select sum(gle.debit) - sum(gle.credit)
 		from `tabGL Entry` gle
 		where gle.account=%(account)s
@@ -282,46 +360,59 @@
 			and gle.company=%(company)s
 			and gle.docstatus=1
 			{condition2}
-	""".format(condition1=condition1, condition2=condition2), (args))[0][0]) #nosec
+	""".format(
+				condition1=condition1, condition2=condition2
+			),
+			(args),
+		)[0][0]
+	)  # nosec
 
 	return amount
 
+
 def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_year, annual_budget):
 	distribution = {}
 	if monthly_distribution:
-		for d in frappe.db.sql("""select mdp.month, mdp.percentage_allocation
+		for d in frappe.db.sql(
+			"""select mdp.month, mdp.percentage_allocation
 			from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
-			where mdp.parent=md.name and md.fiscal_year=%s""", fiscal_year, as_dict=1):
-				distribution.setdefault(d.month, d.percentage_allocation)
+			where mdp.parent=md.name and md.fiscal_year=%s""",
+			fiscal_year,
+			as_dict=1,
+		):
+			distribution.setdefault(d.month, d.percentage_allocation)
 
 	dt = frappe.db.get_value("Fiscal Year", fiscal_year, "year_start_date")
 	accumulated_percentage = 0.0
 
-	while(dt <= getdate(posting_date)):
+	while dt <= getdate(posting_date):
 		if monthly_distribution:
 			accumulated_percentage += distribution.get(getdate(dt).strftime("%B"), 0)
 		else:
-			accumulated_percentage += 100.0/12
+			accumulated_percentage += 100.0 / 12
 
 		dt = add_months(dt, 1)
 
 	return annual_budget * accumulated_percentage / 100
 
+
 def get_item_details(args):
 	cost_center, expense_account = None, None
 
-	if not args.get('company'):
+	if not args.get("company"):
 		return cost_center, expense_account
 
 	if args.item_code:
-		item_defaults = frappe.db.get_value('Item Default',
-			{'parent': args.item_code, 'company': args.get('company')},
-			['buying_cost_center', 'expense_account'])
+		item_defaults = frappe.db.get_value(
+			"Item Default",
+			{"parent": args.item_code, "company": args.get("company")},
+			["buying_cost_center", "expense_account"],
+		)
 		if item_defaults:
 			cost_center, expense_account = item_defaults
 
 	if not (cost_center and expense_account):
-		for doctype in ['Item Group', 'Company']:
+		for doctype in ["Item Group", "Company"]:
 			data = get_expense_cost_center(doctype, args)
 
 			if not cost_center and data:
@@ -335,11 +426,15 @@
 
 	return cost_center, expense_account
 
+
 def get_expense_cost_center(doctype, args):
-	if doctype == 'Item Group':
-		return frappe.db.get_value('Item Default',
-			{'parent': args.get(frappe.scrub(doctype)), 'company': args.get('company')},
-			['buying_cost_center', 'expense_account'])
+	if doctype == "Item Group":
+		return frappe.db.get_value(
+			"Item Default",
+			{"parent": args.get(frappe.scrub(doctype)), "company": args.get("company")},
+			["buying_cost_center", "expense_account"],
+		)
 	else:
-		return frappe.db.get_value(doctype, args.get(frappe.scrub(doctype)),\
-			['cost_center', 'default_expense_account'])
+		return frappe.db.get_value(
+			doctype, args.get(frappe.scrub(doctype)), ["cost_center", "default_expense_account"]
+		)
diff --git a/erpnext/accounts/doctype/budget/test_budget.py b/erpnext/accounts/doctype/budget/test_budget.py
index 9a83a0a..c48c7d9 100644
--- a/erpnext/accounts/doctype/budget/test_budget.py
+++ b/erpnext/accounts/doctype/budget/test_budget.py
@@ -11,7 +11,8 @@
 from erpnext.accounts.utils import get_fiscal_year
 from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
 
-test_dependencies = ['Monthly Distribution']
+test_dependencies = ["Monthly Distribution"]
+
 
 class TestBudget(unittest.TestCase):
 	def test_monthly_budget_crossed_ignore(self):
@@ -19,11 +20,18 @@
 
 		budget = make_budget(budget_against="Cost Center")
 
-		jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
-			"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
+		jv = make_journal_entry(
+			"_Test Account Cost for Goods Sold - _TC",
+			"_Test Bank - _TC",
+			40000,
+			"_Test Cost Center - _TC",
+			posting_date=nowdate(),
+			submit=True,
+		)
 
-		self.assertTrue(frappe.db.get_value("GL Entry",
-			{"voucher_type": "Journal Entry", "voucher_no": jv.name}))
+		self.assertTrue(
+			frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})
+		)
 
 		budget.cancel()
 		jv.cancel()
@@ -33,10 +41,17 @@
 
 		budget = make_budget(budget_against="Cost Center")
 
-		frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
+		frappe.db.set_value(
+			"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
+		)
 
-		jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
-			"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date=nowdate())
+		jv = make_journal_entry(
+			"_Test Account Cost for Goods Sold - _TC",
+			"_Test Bank - _TC",
+			40000,
+			"_Test Cost Center - _TC",
+			posting_date=nowdate(),
+		)
 
 		self.assertRaises(BudgetError, jv.submit)
 
@@ -48,49 +63,65 @@
 
 		budget = make_budget(budget_against="Cost Center")
 
-		frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
+		frappe.db.set_value(
+			"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
+		)
 
-		jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
-			"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date=nowdate())
+		jv = make_journal_entry(
+			"_Test Account Cost for Goods Sold - _TC",
+			"_Test Bank - _TC",
+			40000,
+			"_Test Cost Center - _TC",
+			posting_date=nowdate(),
+		)
 
 		self.assertRaises(BudgetError, jv.submit)
 
-		frappe.db.set_value('Company', budget.company, 'exception_budget_approver_role', 'Accounts User')
+		frappe.db.set_value("Company", budget.company, "exception_budget_approver_role", "Accounts User")
 
 		jv.submit()
-		self.assertEqual(frappe.db.get_value('Journal Entry', jv.name, 'docstatus'), 1)
+		self.assertEqual(frappe.db.get_value("Journal Entry", jv.name, "docstatus"), 1)
 		jv.cancel()
 
-		frappe.db.set_value('Company', budget.company, 'exception_budget_approver_role', '')
+		frappe.db.set_value("Company", budget.company, "exception_budget_approver_role", "")
 
 		budget.load_from_db()
 		budget.cancel()
 
 	def test_monthly_budget_crossed_for_mr(self):
-		budget = make_budget(applicable_on_material_request=1,
-			applicable_on_purchase_order=1, action_if_accumulated_monthly_budget_exceeded_on_mr="Stop",
-			budget_against="Cost Center")
+		budget = make_budget(
+			applicable_on_material_request=1,
+			applicable_on_purchase_order=1,
+			action_if_accumulated_monthly_budget_exceeded_on_mr="Stop",
+			budget_against="Cost Center",
+		)
 
 		fiscal_year = get_fiscal_year(nowdate())[0]
-		frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
+		frappe.db.set_value(
+			"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
+		)
 		frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year)
 
-		mr = frappe.get_doc({
-			"doctype": "Material Request",
-			"material_request_type": "Purchase",
-			"transaction_date": nowdate(),
-			"company": budget.company,
-			"items": [{
-				'item_code': '_Test Item',
-				'qty': 1,
-				'uom': "_Test UOM",
-				'warehouse': '_Test Warehouse - _TC',
-				'schedule_date': nowdate(),
-				'rate': 100000,
-				'expense_account': '_Test Account Cost for Goods Sold - _TC',
-				'cost_center': '_Test Cost Center - _TC'
-			}]
-		})
+		mr = frappe.get_doc(
+			{
+				"doctype": "Material Request",
+				"material_request_type": "Purchase",
+				"transaction_date": nowdate(),
+				"company": budget.company,
+				"items": [
+					{
+						"item_code": "_Test Item",
+						"qty": 1,
+						"uom": "_Test UOM",
+						"warehouse": "_Test Warehouse - _TC",
+						"schedule_date": nowdate(),
+						"rate": 100000,
+						"expense_account": "_Test Account Cost for Goods Sold - _TC",
+						"cost_center": "_Test Cost Center - _TC",
+					}
+				],
+			}
+		)
 
 		mr.set_missing_values()
 
@@ -100,11 +131,16 @@
 		budget.cancel()
 
 	def test_monthly_budget_crossed_for_po(self):
-		budget = make_budget(applicable_on_purchase_order=1,
-			action_if_accumulated_monthly_budget_exceeded_on_po="Stop", budget_against="Cost Center")
+		budget = make_budget(
+			applicable_on_purchase_order=1,
+			action_if_accumulated_monthly_budget_exceeded_on_po="Stop",
+			budget_against="Cost Center",
+		)
 
 		fiscal_year = get_fiscal_year(nowdate())[0]
-		frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
+		frappe.db.set_value(
+			"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
+		)
 		frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year)
 
 		po = create_purchase_order(transaction_date=nowdate(), do_not_submit=True)
@@ -122,12 +158,20 @@
 
 		budget = make_budget(budget_against="Project")
 
-		frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
+		frappe.db.set_value(
+			"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
+		)
 
 		project = frappe.get_value("Project", {"project_name": "_Test Project"})
 
-		jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
-			"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", project=project, posting_date=nowdate())
+		jv = make_journal_entry(
+			"_Test Account Cost for Goods Sold - _TC",
+			"_Test Bank - _TC",
+			40000,
+			"_Test Cost Center - _TC",
+			project=project,
+			posting_date=nowdate(),
+		)
 
 		self.assertRaises(BudgetError, jv.submit)
 
@@ -139,8 +183,13 @@
 
 		budget = make_budget(budget_against="Cost Center")
 
-		jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
-			"_Test Bank - _TC", 250000, "_Test Cost Center - _TC", posting_date=nowdate())
+		jv = make_journal_entry(
+			"_Test Account Cost for Goods Sold - _TC",
+			"_Test Bank - _TC",
+			250000,
+			"_Test Cost Center - _TC",
+			posting_date=nowdate(),
+		)
 
 		self.assertRaises(BudgetError, jv.submit)
 
@@ -153,9 +202,14 @@
 
 		project = frappe.get_value("Project", {"project_name": "_Test Project"})
 
-		jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
-			"_Test Bank - _TC", 250000, "_Test Cost Center - _TC",
-			project=project, posting_date=nowdate())
+		jv = make_journal_entry(
+			"_Test Account Cost for Goods Sold - _TC",
+			"_Test Bank - _TC",
+			250000,
+			"_Test Cost Center - _TC",
+			project=project,
+			posting_date=nowdate(),
+		)
 
 		self.assertRaises(BudgetError, jv.submit)
 
@@ -169,14 +223,23 @@
 		if month > 9:
 			month = 9
 
-		for i in range(month+1):
-			jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
-				"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
+		for i in range(month + 1):
+			jv = make_journal_entry(
+				"_Test Account Cost for Goods Sold - _TC",
+				"_Test Bank - _TC",
+				20000,
+				"_Test Cost Center - _TC",
+				posting_date=nowdate(),
+				submit=True,
+			)
 
-			self.assertTrue(frappe.db.get_value("GL Entry",
-				{"voucher_type": "Journal Entry", "voucher_no": jv.name}))
+			self.assertTrue(
+				frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})
+			)
 
-		frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
+		frappe.db.set_value(
+			"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
+		)
 
 		self.assertRaises(BudgetError, jv.cancel)
 
@@ -193,14 +256,23 @@
 
 		project = frappe.get_value("Project", {"project_name": "_Test Project"})
 		for i in range(month + 1):
-			jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
-				"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True,
-				project=project)
+			jv = make_journal_entry(
+				"_Test Account Cost for Goods Sold - _TC",
+				"_Test Bank - _TC",
+				20000,
+				"_Test Cost Center - _TC",
+				posting_date=nowdate(),
+				submit=True,
+				project=project,
+			)
 
-			self.assertTrue(frappe.db.get_value("GL Entry",
-				{"voucher_type": "Journal Entry", "voucher_no": jv.name}))
+			self.assertTrue(
+				frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})
+			)
 
-		frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
+		frappe.db.set_value(
+			"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
+		)
 
 		self.assertRaises(BudgetError, jv.cancel)
 
@@ -212,10 +284,17 @@
 		set_total_expense_zero(nowdate(), "cost_center", "_Test Cost Center 2 - _TC")
 
 		budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC")
-		frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
+		frappe.db.set_value(
+			"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
+		)
 
-		jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
-			"_Test Bank - _TC", 40000, "_Test Cost Center 2 - _TC", posting_date=nowdate())
+		jv = make_journal_entry(
+			"_Test Account Cost for Goods Sold - _TC",
+			"_Test Bank - _TC",
+			40000,
+			"_Test Cost Center 2 - _TC",
+			posting_date=nowdate(),
+		)
 
 		self.assertRaises(BudgetError, jv.submit)
 
@@ -226,19 +305,28 @@
 		cost_center = "_Test Cost Center 3 - _TC"
 
 		if not frappe.db.exists("Cost Center", cost_center):
-			frappe.get_doc({
-				'doctype': 'Cost Center',
-				'cost_center_name': '_Test Cost Center 3',
-				'parent_cost_center': "_Test Company - _TC",
-				'company': '_Test Company',
-				'is_group': 0
-			}).insert(ignore_permissions=True)
+			frappe.get_doc(
+				{
+					"doctype": "Cost Center",
+					"cost_center_name": "_Test Cost Center 3",
+					"parent_cost_center": "_Test Company - _TC",
+					"company": "_Test Company",
+					"is_group": 0,
+				}
+			).insert(ignore_permissions=True)
 
 		budget = make_budget(budget_against="Cost Center", cost_center=cost_center)
-		frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
+		frappe.db.set_value(
+			"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
+		)
 
-		jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
-			"_Test Bank - _TC", 40000, cost_center, posting_date=nowdate())
+		jv = make_journal_entry(
+			"_Test Account Cost for Goods Sold - _TC",
+			"_Test Bank - _TC",
+			40000,
+			cost_center,
+			posting_date=nowdate(),
+		)
 
 		self.assertRaises(BudgetError, jv.submit)
 
@@ -255,14 +343,16 @@
 
 	fiscal_year = get_fiscal_year(nowdate())[0]
 
-	args = frappe._dict({
-		"account": "_Test Account Cost for Goods Sold - _TC",
-		"cost_center": "_Test Cost Center - _TC",
-		"monthly_end_date": posting_date,
-		"company": "_Test Company",
-		"fiscal_year": fiscal_year,
-		"budget_against_field": budget_against_field,
-	})
+	args = frappe._dict(
+		{
+			"account": "_Test Account Cost for Goods Sold - _TC",
+			"cost_center": "_Test Cost Center - _TC",
+			"monthly_end_date": posting_date,
+			"company": "_Test Company",
+			"fiscal_year": fiscal_year,
+			"budget_against_field": budget_against_field,
+		}
+	)
 
 	if not args.get(budget_against_field):
 		args[budget_against_field] = budget_against
@@ -271,26 +361,42 @@
 
 	if existing_expense:
 		if budget_against_field == "cost_center":
-			make_journal_entry("_Test Account Cost for Goods Sold - _TC",
-			"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
+			make_journal_entry(
+				"_Test Account Cost for Goods Sold - _TC",
+				"_Test Bank - _TC",
+				-existing_expense,
+				"_Test Cost Center - _TC",
+				posting_date=nowdate(),
+				submit=True,
+			)
 		elif budget_against_field == "project":
-			make_journal_entry("_Test Account Cost for Goods Sold - _TC",
-			"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project=budget_against, posting_date=nowdate())
+			make_journal_entry(
+				"_Test Account Cost for Goods Sold - _TC",
+				"_Test Bank - _TC",
+				-existing_expense,
+				"_Test Cost Center - _TC",
+				submit=True,
+				project=budget_against,
+				posting_date=nowdate(),
+			)
+
 
 def make_budget(**args):
 	args = frappe._dict(args)
 
-	budget_against=args.budget_against
-	cost_center=args.cost_center
+	budget_against = args.budget_against
+	cost_center = args.cost_center
 
 	fiscal_year = get_fiscal_year(nowdate())[0]
 
 	if budget_against == "Project":
 		project_name = "{0}%".format("_Test Project/" + fiscal_year)
-		budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", project_name)})
+		budget_list = frappe.get_all("Budget", fields=["name"], filters={"name": ("like", project_name)})
 	else:
 		cost_center_name = "{0}%".format(cost_center or "_Test Cost Center - _TC/" + fiscal_year)
-		budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", cost_center_name)})
+		budget_list = frappe.get_all(
+			"Budget", fields=["name"], filters={"name": ("like", cost_center_name)}
+		)
 	for d in budget_list:
 		frappe.db.sql("delete from `tabBudget` where name = %(name)s", d)
 		frappe.db.sql("delete from `tabBudget Account` where parent = %(name)s", d)
@@ -300,7 +406,7 @@
 	if budget_against == "Project":
 		budget.project = frappe.get_value("Project", {"project_name": "_Test Project"})
 	else:
-		budget.cost_center =cost_center or "_Test Cost Center - _TC"
+		budget.cost_center = cost_center or "_Test Cost Center - _TC"
 
 	monthly_distribution = frappe.get_doc("Monthly Distribution", "_Test Distribution")
 	monthly_distribution.fiscal_year = fiscal_year
@@ -312,20 +418,27 @@
 	budget.action_if_annual_budget_exceeded = "Stop"
 	budget.action_if_accumulated_monthly_budget_exceeded = "Ignore"
 	budget.budget_against = budget_against
-	budget.append("accounts", {
-		"account": "_Test Account Cost for Goods Sold - _TC",
-		"budget_amount": 200000
-	})
+	budget.append(
+		"accounts", {"account": "_Test Account Cost for Goods Sold - _TC", "budget_amount": 200000}
+	)
 
 	if args.applicable_on_material_request:
 		budget.applicable_on_material_request = 1
-		budget.action_if_annual_budget_exceeded_on_mr = args.action_if_annual_budget_exceeded_on_mr or 'Warn'
-		budget.action_if_accumulated_monthly_budget_exceeded_on_mr = args.action_if_accumulated_monthly_budget_exceeded_on_mr or 'Warn'
+		budget.action_if_annual_budget_exceeded_on_mr = (
+			args.action_if_annual_budget_exceeded_on_mr or "Warn"
+		)
+		budget.action_if_accumulated_monthly_budget_exceeded_on_mr = (
+			args.action_if_accumulated_monthly_budget_exceeded_on_mr or "Warn"
+		)
 
 	if args.applicable_on_purchase_order:
 		budget.applicable_on_purchase_order = 1
-		budget.action_if_annual_budget_exceeded_on_po = args.action_if_annual_budget_exceeded_on_po or 'Warn'
-		budget.action_if_accumulated_monthly_budget_exceeded_on_po = args.action_if_accumulated_monthly_budget_exceeded_on_po or 'Warn'
+		budget.action_if_annual_budget_exceeded_on_po = (
+			args.action_if_annual_budget_exceeded_on_po or "Warn"
+		)
+		budget.action_if_accumulated_monthly_budget_exceeded_on_po = (
+			args.action_if_accumulated_monthly_budget_exceeded_on_po or "Warn"
+		)
 
 	budget.insert()
 	budget.submit()
diff --git a/erpnext/accounts/doctype/cash_flow_mapper/default_cash_flow_mapper.py b/erpnext/accounts/doctype/cash_flow_mapper/default_cash_flow_mapper.py
index 6e7b687..79feb2d 100644
--- a/erpnext/accounts/doctype/cash_flow_mapper/default_cash_flow_mapper.py
+++ b/erpnext/accounts/doctype/cash_flow_mapper/default_cash_flow_mapper.py
@@ -1,25 +1,25 @@
 DEFAULT_MAPPERS = [
-    {
-        'doctype': 'Cash Flow Mapper',
-        'section_footer': 'Net cash generated by operating activities',
-        'section_header': 'Cash flows from operating activities',
-        'section_leader': 'Adjustments for',
-        'section_name': 'Operating Activities',
-        'position': 0,
-        'section_subtotal': 'Cash generated from operations',
-    },
-    {
-        'doctype': 'Cash Flow Mapper',
-        'position': 1,
-        'section_footer': 'Net cash used in investing activities',
-        'section_header': 'Cash flows from investing activities',
-        'section_name': 'Investing Activities'
-    },
-    {
-        'doctype': 'Cash Flow Mapper',
-        'position': 2,
-        'section_footer': 'Net cash used in financing activites',
-        'section_header': 'Cash flows from financing activities',
-        'section_name': 'Financing Activities',
-    }
+	{
+		"doctype": "Cash Flow Mapper",
+		"section_footer": "Net cash generated by operating activities",
+		"section_header": "Cash flows from operating activities",
+		"section_leader": "Adjustments for",
+		"section_name": "Operating Activities",
+		"position": 0,
+		"section_subtotal": "Cash generated from operations",
+	},
+	{
+		"doctype": "Cash Flow Mapper",
+		"position": 1,
+		"section_footer": "Net cash used in investing activities",
+		"section_header": "Cash flows from investing activities",
+		"section_name": "Investing Activities",
+	},
+	{
+		"doctype": "Cash Flow Mapper",
+		"position": 2,
+		"section_footer": "Net cash used in financing activites",
+		"section_header": "Cash flows from financing activities",
+		"section_name": "Financing Activities",
+	},
 ]
diff --git a/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py b/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py
index cd8381a..402469f 100644
--- a/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py
+++ b/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py
@@ -3,6 +3,7 @@
 
 
 import frappe
+from frappe import _
 from frappe.model.document import Document
 
 
@@ -11,9 +12,11 @@
 		self.validate_checked_options()
 
 	def validate_checked_options(self):
-		checked_fields = [d for d in self.meta.fields if d.fieldtype == 'Check' and self.get(d.fieldname) == 1]
+		checked_fields = [
+			d for d in self.meta.fields if d.fieldtype == "Check" and self.get(d.fieldname) == 1
+		]
 		if len(checked_fields) > 1:
 			frappe.throw(
-				frappe._('You can only select a maximum of one option from the list of check boxes.'),
-				title='Error'
+				_("You can only select a maximum of one option from the list of check boxes."),
+				title=_("Error"),
 			)
diff --git a/erpnext/accounts/doctype/cash_flow_mapping/test_cash_flow_mapping.py b/erpnext/accounts/doctype/cash_flow_mapping/test_cash_flow_mapping.py
index abb2567..19f2425 100644
--- a/erpnext/accounts/doctype/cash_flow_mapping/test_cash_flow_mapping.py
+++ b/erpnext/accounts/doctype/cash_flow_mapping/test_cash_flow_mapping.py
@@ -9,19 +9,16 @@
 class TestCashFlowMapping(unittest.TestCase):
 	def setUp(self):
 		if frappe.db.exists("Cash Flow Mapping", "Test Mapping"):
-			frappe.delete_doc('Cash Flow Mappping', 'Test Mapping')
+			frappe.delete_doc("Cash Flow Mappping", "Test Mapping")
 
 	def tearDown(self):
-		frappe.delete_doc('Cash Flow Mapping', 'Test Mapping')
+		frappe.delete_doc("Cash Flow Mapping", "Test Mapping")
 
 	def test_multiple_selections_not_allowed(self):
-		doc = frappe.new_doc('Cash Flow Mapping')
-		doc.mapping_name = 'Test Mapping'
-		doc.label = 'Test label'
-		doc.append(
-			'accounts',
-			{'account': 'Accounts Receivable - _TC'}
-		)
+		doc = frappe.new_doc("Cash Flow Mapping")
+		doc.mapping_name = "Test Mapping"
+		doc.label = "Test label"
+		doc.append("accounts", {"account": "Accounts Receivable - _TC"})
 		doc.is_working_capital = 1
 		doc.is_finance_cost = 1
 
diff --git a/erpnext/accounts/doctype/cashier_closing/cashier_closing.py b/erpnext/accounts/doctype/cashier_closing/cashier_closing.py
index 9fbd0c9..6013807 100644
--- a/erpnext/accounts/doctype/cashier_closing/cashier_closing.py
+++ b/erpnext/accounts/doctype/cashier_closing/cashier_closing.py
@@ -17,11 +17,14 @@
 		self.make_calculations()
 
 	def get_outstanding(self):
-		values = frappe.db.sql("""
+		values = frappe.db.sql(
+			"""
 			select sum(outstanding_amount)
 			from `tabSales Invoice`
 			where posting_date=%s and posting_time>=%s and posting_time<=%s and owner=%s
-		""", (self.date, self.from_time, self.time, self.user))
+		""",
+			(self.date, self.from_time, self.time, self.user),
+		)
 		self.outstanding_amount = flt(values[0][0] if values else 0)
 
 	def make_calculations(self):
@@ -29,7 +32,9 @@
 		for i in self.payments:
 			total += flt(i.amount)
 
-		self.net_amount = total + self.outstanding_amount + flt(self.expense) - flt(self.custody) + flt(self.returns)
+		self.net_amount = (
+			total + self.outstanding_amount + flt(self.expense) - flt(self.custody) + flt(self.returns)
+		)
 
 	def validate_time(self):
 		if self.from_time >= self.time:
diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
index aaacce4..01bf1c2 100644
--- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
+++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
@@ -25,33 +25,41 @@
 class ChartofAccountsImporter(Document):
 	def validate(self):
 		if self.import_file:
-			get_coa('Chart of Accounts Importer', 'All Accounts', file_name=self.import_file, for_validate=1)
+			get_coa(
+				"Chart of Accounts Importer", "All Accounts", file_name=self.import_file, for_validate=1
+			)
+
 
 def validate_columns(data):
 	if not data:
-		frappe.throw(_('No data found. Seems like you uploaded a blank file'))
+		frappe.throw(_("No data found. Seems like you uploaded a blank file"))
 
 	no_of_columns = max([len(d) for d in data])
 
 	if no_of_columns > 7:
-		frappe.throw(_('More columns found than expected. Please compare the uploaded file with standard template'),
-			title=(_("Wrong Template")))
+		frappe.throw(
+			_("More columns found than expected. Please compare the uploaded file with standard template"),
+			title=(_("Wrong Template")),
+		)
+
 
 @frappe.whitelist()
 def validate_company(company):
-	parent_company, allow_account_creation_against_child_company = frappe.db.get_value('Company',
-		{'name': company}, ['parent_company',
-		'allow_account_creation_against_child_company'])
+	parent_company, allow_account_creation_against_child_company = frappe.db.get_value(
+		"Company", {"name": company}, ["parent_company", "allow_account_creation_against_child_company"]
+	)
 
 	if parent_company and (not allow_account_creation_against_child_company):
 		msg = _("{} is a child company.").format(frappe.bold(company)) + " "
 		msg += _("Please import accounts against parent company or enable {} in company master.").format(
-			frappe.bold('Allow Account Creation Against Child Company'))
-		frappe.throw(msg, title=_('Wrong Company'))
+			frappe.bold("Allow Account Creation Against Child Company")
+		)
+		frappe.throw(msg, title=_("Wrong Company"))
 
-	if frappe.db.get_all('GL Entry', {"company": company}, "name", limit=1):
+	if frappe.db.get_all("GL Entry", {"company": company}, "name", limit=1):
 		return False
 
+
 @frappe.whitelist()
 def import_coa(file_name, company):
 	# delete existing data for accounts
@@ -60,7 +68,7 @@
 	# create accounts
 	file_doc, extension = get_file(file_name)
 
-	if extension  == 'csv':
+	if extension == "csv":
 		data = generate_data_from_csv(file_doc)
 	else:
 		data = generate_data_from_excel(file_doc, extension)
@@ -72,27 +80,33 @@
 	# trigger on_update for company to reset default accounts
 	set_default_accounts(company)
 
+
 def get_file(file_name):
 	file_doc = frappe.get_doc("File", {"file_url": file_name})
 	parts = file_doc.get_extension()
 	extension = parts[1]
 	extension = extension.lstrip(".")
 
-	if extension not in ('csv',  'xlsx', 'xls'):
-		frappe.throw(_("Only CSV and Excel files can be used to for importing data. Please check the file format you are trying to upload"))
+	if extension not in ("csv", "xlsx", "xls"):
+		frappe.throw(
+			_(
+				"Only CSV and Excel files can be used to for importing data. Please check the file format you are trying to upload"
+			)
+		)
 
-	return  file_doc, extension
+	return file_doc, extension
+
 
 def generate_data_from_csv(file_doc, as_dict=False):
-	''' read csv file and return the generated nested tree '''
+	"""read csv file and return the generated nested tree"""
 
 	file_path = file_doc.get_full_path()
 
 	data = []
-	with open(file_path, 'r') as in_file:
+	with open(file_path, "r") as in_file:
 		csv_reader = list(csv.reader(in_file))
 		headers = csv_reader[0]
-		del csv_reader[0] # delete top row and headers row
+		del csv_reader[0]  # delete top row and headers row
 
 		for row in csv_reader:
 			if as_dict:
@@ -106,6 +120,7 @@
 	# convert csv data
 	return data
 
+
 def generate_data_from_excel(file_doc, extension, as_dict=False):
 	content = file_doc.get_content()
 
@@ -123,20 +138,21 @@
 			data.append({frappe.scrub(header): row[index] for index, header in enumerate(headers)})
 		else:
 			if not row[1]:
-					row[1] = row[0]
-					row[3] = row[2]
+				row[1] = row[0]
+				row[3] = row[2]
 			data.append(row)
 
 	return data
 
+
 @frappe.whitelist()
 def get_coa(doctype, parent, is_root=False, file_name=None, for_validate=0):
-	''' called by tree view (to fetch node's children) '''
+	"""called by tree view (to fetch node's children)"""
 
 	file_doc, extension = get_file(file_name)
-	parent = None if parent==_('All Accounts') else parent
+	parent = None if parent == _("All Accounts") else parent
 
-	if extension  == 'csv':
+	if extension == "csv":
 		data = generate_data_from_csv(file_doc)
 	else:
 		data = generate_data_from_excel(file_doc, extension)
@@ -146,32 +162,33 @@
 
 	if not for_validate:
 		forest = build_forest(data)
-		accounts = build_tree_from_json("", chart_data=forest, from_coa_importer=True) # returns a list of dict in a tree render-able form
+		accounts = build_tree_from_json(
+			"", chart_data=forest, from_coa_importer=True
+		)  # returns a list of dict in a tree render-able form
 
 		# filter out to show data for the selected node only
-		accounts = [d for d in accounts if d['parent_account']==parent]
+		accounts = [d for d in accounts if d["parent_account"] == parent]
 
 		return accounts
 	else:
-		return {
-			'show_import_button': 1
-		}
+		return {"show_import_button": 1}
+
 
 def build_forest(data):
-	'''
-		converts list of list into a nested tree
-		if a = [[1,1], [1,2], [3,2], [4,4], [5,4]]
-		tree = {
-			1: {
-				2: {
-					3: {}
-				}
-			},
-			4: {
-				5: {}
-			}
-		}
-	'''
+	"""
+	converts list of list into a nested tree
+	if a = [[1,1], [1,2], [3,2], [4,4], [5,4]]
+	tree = {
+	        1: {
+	                2: {
+	                        3: {}
+	                }
+	        },
+	        4: {
+	                5: {}
+	        }
+	}
+	"""
 
 	# set the value of nested dictionary
 	def set_nested(d, path, value):
@@ -195,8 +212,11 @@
 			elif account_name == child:
 				parent_account_list = return_parent(data, parent_account)
 				if not parent_account_list and parent_account:
-					frappe.throw(_("The parent account {0} does not exists in the uploaded template").format(
-						frappe.bold(parent_account)))
+					frappe.throw(
+						_("The parent account {0} does not exists in the uploaded template").format(
+							frappe.bold(parent_account)
+						)
+					)
 				return [child] + parent_account_list
 
 	charts_map, paths = {}, []
@@ -205,7 +225,15 @@
 	error_messages = []
 
 	for i in data:
-		account_name, parent_account, account_number, parent_account_number, is_group, account_type, root_type = i
+		(
+			account_name,
+			parent_account,
+			account_number,
+			parent_account_number,
+			is_group,
+			account_type,
+			root_type,
+		) = i
 
 		if not account_name:
 			error_messages.append("Row {0}: Please enter Account Name".format(line_no))
@@ -216,13 +244,17 @@
 			account_name = "{} - {}".format(account_number, account_name)
 
 		charts_map[account_name] = {}
-		charts_map[account_name]['account_name'] = name
-		if account_number: charts_map[account_name]["account_number"] = account_number
-		if cint(is_group) == 1: charts_map[account_name]["is_group"] = is_group
-		if account_type: charts_map[account_name]["account_type"] = account_type
-		if root_type: charts_map[account_name]["root_type"] = root_type
+		charts_map[account_name]["account_name"] = name
+		if account_number:
+			charts_map[account_name]["account_number"] = account_number
+		if cint(is_group) == 1:
+			charts_map[account_name]["is_group"] = is_group
+		if account_type:
+			charts_map[account_name]["account_type"] = account_type
+		if root_type:
+			charts_map[account_name]["root_type"] = root_type
 		path = return_parent(data, account_name)[::-1]
-		paths.append(path) # List of path is created
+		paths.append(path)  # List of path is created
 		line_no += 1
 
 	if error_messages:
@@ -231,27 +263,32 @@
 	out = {}
 	for path in paths:
 		for n, account_name in enumerate(path):
-			set_nested(out, path[:n+1], charts_map[account_name]) # setting the value of nested dictionary.
+			set_nested(
+				out, path[: n + 1], charts_map[account_name]
+			)  # setting the value of nested dictionary.
 
 	return out
 
+
 def build_response_as_excel(writer):
 	filename = frappe.generate_hash("", 10)
-	with open(filename, 'wb') as f:
-		f.write(cstr(writer.getvalue()).encode('utf-8'))
+	with open(filename, "wb") as f:
+		f.write(cstr(writer.getvalue()).encode("utf-8"))
 	f = open(filename)
 	reader = csv.reader(f)
 
 	from frappe.utils.xlsxutils import make_xlsx
+
 	xlsx_file = make_xlsx(reader, "Chart of Accounts Importer Template")
 
 	f.close()
 	os.remove(filename)
 
 	# write out response as a xlsx type
-	frappe.response['filename'] = 'coa_importer_template.xlsx'
-	frappe.response['filecontent'] = xlsx_file.getvalue()
-	frappe.response['type'] = 'binary'
+	frappe.response["filename"] = "coa_importer_template.xlsx"
+	frappe.response["filecontent"] = xlsx_file.getvalue()
+	frappe.response["type"] = "binary"
+
 
 @frappe.whitelist()
 def download_template(file_type, template_type):
@@ -259,34 +296,46 @@
 
 	writer = get_template(template_type)
 
-	if file_type == 'CSV':
+	if file_type == "CSV":
 		# download csv file
-		frappe.response['result'] = cstr(writer.getvalue())
-		frappe.response['type'] = 'csv'
-		frappe.response['doctype'] = 'Chart of Accounts Importer'
+		frappe.response["result"] = cstr(writer.getvalue())
+		frappe.response["type"] = "csv"
+		frappe.response["doctype"] = "Chart of Accounts Importer"
 	else:
 		build_response_as_excel(writer)
 
+
 def get_template(template_type):
 
-	fields = ["Account Name", "Parent Account", "Account Number", "Parent Account Number", "Is Group", "Account Type", "Root Type"]
+	fields = [
+		"Account Name",
+		"Parent Account",
+		"Account Number",
+		"Parent Account Number",
+		"Is Group",
+		"Account Type",
+		"Root Type",
+	]
 	writer = UnicodeWriter()
 	writer.writerow(fields)
 
-	if template_type == 'Blank Template':
-		for root_type in  get_root_types():
-			writer.writerow(['', '', '', 1, '', root_type])
+	if template_type == "Blank Template":
+		for root_type in get_root_types():
+			writer.writerow(["", "", "", 1, "", root_type])
 
 		for account in get_mandatory_group_accounts():
-			writer.writerow(['', '', '', 1, account, "Asset"])
+			writer.writerow(["", "", "", 1, account, "Asset"])
 
 		for account_type in get_mandatory_account_types():
-			writer.writerow(['', '', '', 0, account_type.get('account_type'), account_type.get('root_type')])
+			writer.writerow(
+				["", "", "", 0, account_type.get("account_type"), account_type.get("root_type")]
+			)
 	else:
 		writer = get_sample_template(writer)
 
 	return writer
 
+
 def get_sample_template(writer):
 	template = [
 		["Application Of Funds(Assets)", "", "", "", 1, "", "Asset"],
@@ -316,7 +365,7 @@
 
 @frappe.whitelist()
 def validate_accounts(file_doc, extension):
-	if extension  == 'csv':
+	if extension == "csv":
 		accounts = generate_data_from_csv(file_doc, as_dict=True)
 	else:
 		accounts = generate_data_from_excel(file_doc, extension, as_dict=True)
@@ -325,7 +374,9 @@
 	for account in accounts:
 		accounts_dict.setdefault(account["account_name"], account)
 		if "parent_account" not in account:
-			msg = _("Please make sure the file you are using has 'Parent Account' column present in the header.")
+			msg = _(
+				"Please make sure the file you are using has 'Parent Account' column present in the header."
+			)
 			msg += "<br><br>"
 			msg += _("Alternatively, you can download the template and fill your data in.")
 			frappe.throw(msg, title=_("Parent Account Missing"))
@@ -336,77 +387,106 @@
 
 	return [True, len(accounts)]
 
+
 def validate_root(accounts):
-	roots = [accounts[d] for d in accounts if not accounts[d].get('parent_account')]
+	roots = [accounts[d] for d in accounts if not accounts[d].get("parent_account")]
 	error_messages = []
 
 	for account in roots:
 		if not account.get("root_type") and account.get("account_name"):
-			error_messages.append(_("Please enter Root Type for account- {0}").format(account.get("account_name")))
+			error_messages.append(
+				_("Please enter Root Type for account- {0}").format(account.get("account_name"))
+			)
 		elif account.get("root_type") not in get_root_types() and account.get("account_name"):
-			error_messages.append(_("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity").format(account.get("account_name")))
+			error_messages.append(
+				_("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity").format(
+					account.get("account_name")
+				)
+			)
 
 	validate_missing_roots(roots)
 
 	if error_messages:
 		frappe.throw("<br>".join(error_messages))
 
+
 def validate_missing_roots(roots):
-	root_types_added = set(d.get('root_type') for d in roots)
+	root_types_added = set(d.get("root_type") for d in roots)
 
 	missing = list(set(get_root_types()) - root_types_added)
 
 	if missing:
-		frappe.throw(_("Please add Root Account for - {0}").format(' , '.join(missing)))
+		frappe.throw(_("Please add Root Account for - {0}").format(" , ".join(missing)))
+
 
 def get_root_types():
-	return ('Asset', 'Liability', 'Expense', 'Income', 'Equity')
+	return ("Asset", "Liability", "Expense", "Income", "Equity")
+
 
 def get_report_type(root_type):
-	if root_type in ('Asset', 'Liability', 'Equity'):
-		return 'Balance Sheet'
+	if root_type in ("Asset", "Liability", "Equity"):
+		return "Balance Sheet"
 	else:
-		return 'Profit and Loss'
+		return "Profit and Loss"
+
 
 def get_mandatory_group_accounts():
-	return ('Bank', 'Cash', 'Stock')
+	return ("Bank", "Cash", "Stock")
+
 
 def get_mandatory_account_types():
 	return [
-		{'account_type': 'Cost of Goods Sold', 'root_type': 'Expense'},
-		{'account_type': 'Depreciation', 'root_type': 'Expense'},
-		{'account_type': 'Fixed Asset', 'root_type': 'Asset'},
-		{'account_type': 'Payable', 'root_type': 'Liability'},
-		{'account_type': 'Receivable', 'root_type': 'Asset'},
-		{'account_type': 'Stock Adjustment', 'root_type': 'Expense'},
-		{'account_type': 'Bank', 'root_type': 'Asset'},
-		{'account_type': 'Cash', 'root_type': 'Asset'},
-		{'account_type': 'Stock', 'root_type': 'Asset'}
+		{"account_type": "Cost of Goods Sold", "root_type": "Expense"},
+		{"account_type": "Depreciation", "root_type": "Expense"},
+		{"account_type": "Fixed Asset", "root_type": "Asset"},
+		{"account_type": "Payable", "root_type": "Liability"},
+		{"account_type": "Receivable", "root_type": "Asset"},
+		{"account_type": "Stock Adjustment", "root_type": "Expense"},
+		{"account_type": "Bank", "root_type": "Asset"},
+		{"account_type": "Cash", "root_type": "Asset"},
+		{"account_type": "Stock", "root_type": "Asset"},
 	]
 
+
 def unset_existing_data(company):
-	linked = frappe.db.sql('''select fieldname from tabDocField
-		where fieldtype="Link" and options="Account" and parent="Company"''', as_dict=True)
+	linked = frappe.db.sql(
+		'''select fieldname from tabDocField
+		where fieldtype="Link" and options="Account" and parent="Company"''',
+		as_dict=True,
+	)
 
 	# remove accounts data from company
-	update_values = {d.fieldname: '' for d in linked}
-	frappe.db.set_value('Company', company, update_values, update_values)
+	update_values = {d.fieldname: "" for d in linked}
+	frappe.db.set_value("Company", company, update_values, update_values)
 
 	# remove accounts data from various doctypes
-	for doctype in ["Account", "Party Account", "Mode of Payment Account", "Tax Withholding Account",
-		"Sales Taxes and Charges Template", "Purchase Taxes and Charges Template"]:
-		frappe.db.sql('''delete from `tab{0}` where `company`="%s"''' # nosec
-			.format(doctype) % (company))
+	for doctype in [
+		"Account",
+		"Party Account",
+		"Mode of Payment Account",
+		"Tax Withholding Account",
+		"Sales Taxes and Charges Template",
+		"Purchase Taxes and Charges Template",
+	]:
+		frappe.db.sql(
+			'''delete from `tab{0}` where `company`="%s"'''.format(doctype) % (company)  # nosec
+		)
+
 
 def set_default_accounts(company):
 	from erpnext.setup.doctype.company.company import install_country_fixtures
-	company = frappe.get_doc('Company', company)
-	company.update({
-		"default_receivable_account": frappe.db.get_value("Account",
-			{"company": company.name, "account_type": "Receivable", "is_group": 0}),
-		"default_payable_account": frappe.db.get_value("Account",
-			{"company": company.name, "account_type": "Payable", "is_group": 0})
-	})
+
+	company = frappe.get_doc("Company", company)
+	company.update(
+		{
+			"default_receivable_account": frappe.db.get_value(
+				"Account", {"company": company.name, "account_type": "Receivable", "is_group": 0}
+			),
+			"default_payable_account": frappe.db.get_value(
+				"Account", {"company": company.name, "account_type": "Payable", "is_group": 0}
+			),
+		}
+	)
 
 	company.save()
 	install_country_fixtures(company.name, company.country)
diff --git a/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.py b/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.py
index 20cb42c..f8ac664 100644
--- a/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.py
+++ b/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.py
@@ -10,17 +10,20 @@
 class ChequePrintTemplate(Document):
 	pass
 
+
 @frappe.whitelist()
 def create_or_update_cheque_print_format(template_name):
 	if not frappe.db.exists("Print Format", template_name):
 		cheque_print = frappe.new_doc("Print Format")
-		cheque_print.update({
-			"doc_type": "Payment Entry",
-			"standard": "No",
-			"custom_format": 1,
-			"print_format_type": "Jinja",
-			"name": template_name
-		})
+		cheque_print.update(
+			{
+				"doc_type": "Payment Entry",
+				"standard": "No",
+				"custom_format": 1,
+				"print_format_type": "Jinja",
+				"name": template_name,
+			}
+		)
 	else:
 		cheque_print = frappe.get_doc("Print Format", template_name)
 
@@ -69,10 +72,12 @@
 			{{doc.company}}
 		</span>
 	</div>
-</div>"""%{
-		"starting_position_from_top_edge": doc.starting_position_from_top_edge \
-			if doc.cheque_size == "A4" else 0.0,
-		"cheque_width": doc.cheque_width, "cheque_height": doc.cheque_height,
+</div>""" % {
+		"starting_position_from_top_edge": doc.starting_position_from_top_edge
+		if doc.cheque_size == "A4"
+		else 0.0,
+		"cheque_width": doc.cheque_width,
+		"cheque_height": doc.cheque_height,
 		"acc_pay_dist_from_top_edge": doc.acc_pay_dist_from_top_edge,
 		"acc_pay_dist_from_left_edge": doc.acc_pay_dist_from_left_edge,
 		"message_to_show": doc.message_to_show if doc.message_to_show else _("Account Pay Only"),
@@ -89,7 +94,7 @@
 		"amt_in_figures_from_top_edge": doc.amt_in_figures_from_top_edge,
 		"amt_in_figures_from_left_edge": doc.amt_in_figures_from_left_edge,
 		"signatory_from_top_edge": doc.signatory_from_top_edge,
-		"signatory_from_left_edge": doc.signatory_from_left_edge
+		"signatory_from_left_edge": doc.signatory_from_left_edge,
 	}
 
 	cheque_print.save(ignore_permissions=True)
diff --git a/erpnext/accounts/doctype/cheque_print_template/test_cheque_print_template.py b/erpnext/accounts/doctype/cheque_print_template/test_cheque_print_template.py
index 2b323a9..9b003ce 100644
--- a/erpnext/accounts/doctype/cheque_print_template/test_cheque_print_template.py
+++ b/erpnext/accounts/doctype/cheque_print_template/test_cheque_print_template.py
@@ -5,5 +5,6 @@
 
 # test_records = frappe.get_test_records('Cheque Print Template')
 
+
 class TestChequePrintTemplate(unittest.TestCase):
 	pass
diff --git a/erpnext/accounts/doctype/cost_center/cost_center.py b/erpnext/accounts/doctype/cost_center/cost_center.py
index 07cc076..31055c3 100644
--- a/erpnext/accounts/doctype/cost_center/cost_center.py
+++ b/erpnext/accounts/doctype/cost_center/cost_center.py
@@ -10,11 +10,14 @@
 
 
 class CostCenter(NestedSet):
-	nsm_parent_field = 'parent_cost_center'
+	nsm_parent_field = "parent_cost_center"
 
 	def autoname(self):
 		from erpnext.accounts.utils import get_autoname_with_number
-		self.name = get_autoname_with_number(self.cost_center_number, self.cost_center_name, None, self.company)
+
+		self.name = get_autoname_with_number(
+			self.cost_center_number, self.cost_center_name, None, self.company
+		)
 
 	def validate(self):
 		self.validate_mandatory()
@@ -28,9 +31,12 @@
 
 	def validate_parent_cost_center(self):
 		if self.parent_cost_center:
-			if not frappe.db.get_value('Cost Center', self.parent_cost_center, 'is_group'):
-				frappe.throw(_("{0} is not a group node. Please select a group node as parent cost center").format(
-					frappe.bold(self.parent_cost_center)))
+			if not frappe.db.get_value("Cost Center", self.parent_cost_center, "is_group"):
+				frappe.throw(
+					_("{0} is not a group node. Please select a group node as parent cost center").format(
+						frappe.bold(self.parent_cost_center)
+					)
+				)
 
 	@frappe.whitelist()
 	def convert_group_to_ledger(self):
@@ -48,7 +54,9 @@
 		if self.if_allocation_exists_against_cost_center():
 			frappe.throw(_("Cost Center with Allocation records can not be converted to a group"))
 		if self.check_if_part_of_cost_center_allocation():
-			frappe.throw(_("Cost Center is a part of Cost Center Allocation, hence cannot be converted to a group"))
+			frappe.throw(
+				_("Cost Center is a part of Cost Center Allocation, hence cannot be converted to a group")
+			)
 		if self.check_gle_exists():
 			frappe.throw(_("Cost Center with existing transactions can not be converted to group"))
 		self.is_group = 1
@@ -59,24 +67,26 @@
 		return frappe.db.get_value("GL Entry", {"cost_center": self.name})
 
 	def check_if_child_exists(self):
-		return frappe.db.sql("select name from `tabCost Center` where \
-			parent_cost_center = %s and docstatus != 2", self.name)
+		return frappe.db.sql(
+			"select name from `tabCost Center` where \
+			parent_cost_center = %s and docstatus != 2",
+			self.name,
+		)
 
 	def if_allocation_exists_against_cost_center(self):
-		return frappe.db.get_value("Cost Center Allocation", filters = {
-			"main_cost_center": self.name,
-			"docstatus": 1
-		})
+		return frappe.db.get_value(
+			"Cost Center Allocation", filters={"main_cost_center": self.name, "docstatus": 1}
+		)
 
 	def check_if_part_of_cost_center_allocation(self):
-		return frappe.db.get_value("Cost Center Allocation Percentage", filters = {
-			"cost_center": self.name,
-			"docstatus": 1
-		})
+		return frappe.db.get_value(
+			"Cost Center Allocation Percentage", filters={"cost_center": self.name, "docstatus": 1}
+		)
 
 	def before_rename(self, olddn, newdn, merge=False):
 		# Add company abbr if not provided
 		from erpnext.setup.doctype.company.company import get_name_with_abbr
+
 		new_cost_center = get_name_with_abbr(newdn, self.company)
 
 		# Validate properties before merging
@@ -90,7 +100,9 @@
 		super(CostCenter, self).after_rename(olddn, newdn, merge)
 
 		if not merge:
-			new_cost_center = frappe.db.get_value("Cost Center", newdn, ["cost_center_name", "cost_center_number"], as_dict=1)
+			new_cost_center = frappe.db.get_value(
+				"Cost Center", newdn, ["cost_center_name", "cost_center_number"], as_dict=1
+			)
 
 			# exclude company abbr
 			new_parts = newdn.split(" - ")[:-1]
@@ -99,7 +111,9 @@
 				if len(new_parts) == 1:
 					new_parts = newdn.split(" ")
 				if new_cost_center.cost_center_number != new_parts[0]:
-					validate_field_number("Cost Center", self.name, new_parts[0], self.company, "cost_center_number")
+					validate_field_number(
+						"Cost Center", self.name, new_parts[0], self.company, "cost_center_number"
+					)
 					self.cost_center_number = new_parts[0]
 					self.db_set("cost_center_number", new_parts[0])
 				new_parts = new_parts[1:]
@@ -110,10 +124,12 @@
 				self.cost_center_name = cost_center_name
 				self.db_set("cost_center_name", cost_center_name)
 
+
 def on_doctype_update():
 	frappe.db.add_index("Cost Center", ["lft", "rgt"])
 
+
 def get_name_with_number(new_account, account_number):
 	if account_number and not new_account[0].isdigit():
 		new_account = account_number + " - " + new_account
-	return new_account
\ No newline at end of file
+	return new_account
diff --git a/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py b/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py
index f524803..5059dc3 100644
--- a/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py
+++ b/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py
@@ -3,11 +3,6 @@
 
 def get_data():
 	return {
-		'fieldname': 'cost_center',
-		'reports': [
-			{
-				'label': _('Reports'),
-				'items': ['Budget Variance Report', 'General Ledger']
-			}
-		]
+		"fieldname": "cost_center",
+		"reports": [{"label": _("Reports"), "items": ["Budget Variance Report", "General Ledger"]}],
 	}
diff --git a/erpnext/accounts/doctype/cost_center/test_cost_center.py b/erpnext/accounts/doctype/cost_center/test_cost_center.py
index ff50a21..2ec1609 100644
--- a/erpnext/accounts/doctype/cost_center/test_cost_center.py
+++ b/erpnext/accounts/doctype/cost_center/test_cost_center.py
@@ -5,24 +5,28 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Cost Center')
+test_records = frappe.get_test_records("Cost Center")
+
 
 class TestCostCenter(unittest.TestCase):
 	def test_cost_center_creation_against_child_node(self):
 
-		if not frappe.db.get_value('Cost Center', {'name': '_Test Cost Center 2 - _TC'}):
+		if not frappe.db.get_value("Cost Center", {"name": "_Test Cost Center 2 - _TC"}):
 			frappe.get_doc(test_records[1]).insert()
 
-		cost_center = frappe.get_doc({
-			'doctype': 'Cost Center',
-			'cost_center_name': '_Test Cost Center 3',
-			'parent_cost_center': '_Test Cost Center 2 - _TC',
-			'is_group': 0,
-			'company': '_Test Company'
-		})
+		cost_center = frappe.get_doc(
+			{
+				"doctype": "Cost Center",
+				"cost_center_name": "_Test Cost Center 3",
+				"parent_cost_center": "_Test Cost Center 2 - _TC",
+				"is_group": 0,
+				"company": "_Test Company",
+			}
+		)
 
 		self.assertRaises(frappe.ValidationError, cost_center.save)
 
+
 def create_cost_center(**args):
 	args = frappe._dict(args)
 	if args.cost_center_name:
diff --git a/erpnext/accounts/doctype/cost_center_allocation/cost_center_allocation.py b/erpnext/accounts/doctype/cost_center_allocation/cost_center_allocation.py
index bad3fb4..d25016f 100644
--- a/erpnext/accounts/doctype/cost_center_allocation/cost_center_allocation.py
+++ b/erpnext/accounts/doctype/cost_center_allocation/cost_center_allocation.py
@@ -9,15 +9,24 @@
 
 class MainCostCenterCantBeChild(frappe.ValidationError):
 	pass
+
+
 class InvalidMainCostCenter(frappe.ValidationError):
 	pass
+
+
 class InvalidChildCostCenter(frappe.ValidationError):
 	pass
+
+
 class WrongPercentageAllocation(frappe.ValidationError):
 	pass
+
+
 class InvalidDateError(frappe.ValidationError):
 	pass
 
+
 class CostCenterAllocation(Document):
 	def validate(self):
 		self.validate_total_allocation_percentage()
@@ -30,61 +39,96 @@
 		total_percentage = sum([d.percentage for d in self.get("allocation_percentages", [])])
 
 		if total_percentage != 100:
-			frappe.throw(_("Total percentage against cost centers should be 100"), WrongPercentageAllocation)
+			frappe.throw(
+				_("Total percentage against cost centers should be 100"), WrongPercentageAllocation
+			)
 
 	def validate_from_date_based_on_existing_gle(self):
 		# Check if GLE exists against the main cost center
 		# If exists ensure from date is set after posting date of last GLE
 
-		last_gle_date = frappe.db.get_value("GL Entry",
+		last_gle_date = frappe.db.get_value(
+			"GL Entry",
 			{"cost_center": self.main_cost_center, "is_cancelled": 0},
-			"posting_date", order_by="posting_date desc")
+			"posting_date",
+			order_by="posting_date desc",
+		)
 
 		if last_gle_date:
 			if getdate(self.valid_from) <= getdate(last_gle_date):
-				frappe.throw(_("Valid From must be after {0} as last GL Entry against the cost center {1} posted on this date")
-					.format(last_gle_date, self.main_cost_center), InvalidDateError)
+				frappe.throw(
+					_(
+						"Valid From must be after {0} as last GL Entry against the cost center {1} posted on this date"
+					).format(last_gle_date, self.main_cost_center),
+					InvalidDateError,
+				)
 
 	def validate_backdated_allocation(self):
 		# Check if there are any future existing allocation records against the main cost center
 		# If exists, warn the user about it
 
-		future_allocation = frappe.db.get_value("Cost Center Allocation", filters = {
-			"main_cost_center": self.main_cost_center,
-			"valid_from": (">=", self.valid_from),
-			"name": ("!=", self.name),
-			"docstatus": 1
-		}, fieldname=['valid_from', 'name'], order_by='valid_from', as_dict=1)
+		future_allocation = frappe.db.get_value(
+			"Cost Center Allocation",
+			filters={
+				"main_cost_center": self.main_cost_center,
+				"valid_from": (">=", self.valid_from),
+				"name": ("!=", self.name),
+				"docstatus": 1,
+			},
+			fieldname=["valid_from", "name"],
+			order_by="valid_from",
+			as_dict=1,
+		)
 
 		if future_allocation:
-			frappe.msgprint(_("Another Cost Center Allocation record {0} applicable from {1}, hence this allocation will be applicable upto {2}")
-				.format(frappe.bold(future_allocation.name), frappe.bold(format_date(future_allocation.valid_from)),
-				frappe.bold(format_date(add_days(future_allocation.valid_from, -1)))),
-				title=_("Warning!"), indicator="orange", alert=1
+			frappe.msgprint(
+				_(
+					"Another Cost Center Allocation record {0} applicable from {1}, hence this allocation will be applicable upto {2}"
+				).format(
+					frappe.bold(future_allocation.name),
+					frappe.bold(format_date(future_allocation.valid_from)),
+					frappe.bold(format_date(add_days(future_allocation.valid_from, -1))),
+				),
+				title=_("Warning!"),
+				indicator="orange",
+				alert=1,
 			)
 
 	def validate_main_cost_center(self):
 		# Main cost center itself cannot be entered in child table
 		if self.main_cost_center in [d.cost_center for d in self.allocation_percentages]:
-			frappe.throw(_("Main Cost Center {0} cannot be entered in the child table")
-				.format(self.main_cost_center), MainCostCenterCantBeChild)
+			frappe.throw(
+				_("Main Cost Center {0} cannot be entered in the child table").format(self.main_cost_center),
+				MainCostCenterCantBeChild,
+			)
 
 		# If main cost center is used for allocation under any other cost center,
 		# allocation cannot be done against it
-		parent = frappe.db.get_value("Cost Center Allocation Percentage", filters = {
-			"cost_center": self.main_cost_center,
-			"docstatus": 1
-		}, fieldname='parent')
+		parent = frappe.db.get_value(
+			"Cost Center Allocation Percentage",
+			filters={"cost_center": self.main_cost_center, "docstatus": 1},
+			fieldname="parent",
+		)
 		if parent:
-			frappe.throw(_("{0} cannot be used as a Main Cost Center because it has been used as child in Cost Center Allocation {1}")
-				.format(self.main_cost_center, parent), InvalidMainCostCenter)
+			frappe.throw(
+				_(
+					"{0} cannot be used as a Main Cost Center because it has been used as child in Cost Center Allocation {1}"
+				).format(self.main_cost_center, parent),
+				InvalidMainCostCenter,
+			)
 
 	def validate_child_cost_centers(self):
 		# Check if child cost center is used as main cost center in any existing allocation
-		main_cost_centers = [d.main_cost_center for d in
-			frappe.get_all("Cost Center Allocation", {'docstatus': 1}, 'main_cost_center')]
+		main_cost_centers = [
+			d.main_cost_center
+			for d in frappe.get_all("Cost Center Allocation", {"docstatus": 1}, "main_cost_center")
+		]
 
 		for d in self.allocation_percentages:
 			if d.cost_center in main_cost_centers:
-				frappe.throw(_("Cost Center {0} cannot be used for allocation as it is used as main cost center in other allocation record.")
-					.format(d.cost_center), InvalidChildCostCenter)
\ No newline at end of file
+				frappe.throw(
+					_(
+						"Cost Center {0} cannot be used for allocation as it is used as main cost center in other allocation record."
+					).format(d.cost_center),
+					InvalidChildCostCenter,
+				)
diff --git a/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py b/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py
index 9cf4c00..65784db 100644
--- a/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py
+++ b/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py
@@ -19,33 +19,35 @@
 
 class TestCostCenterAllocation(unittest.TestCase):
 	def setUp(self):
-		cost_centers = ["Main Cost Center 1", "Main Cost Center 2", "Sub Cost Center 1", "Sub Cost Center 2"]
+		cost_centers = [
+			"Main Cost Center 1",
+			"Main Cost Center 2",
+			"Sub Cost Center 1",
+			"Sub Cost Center 2",
+		]
 		for cc in cost_centers:
 			create_cost_center(cost_center_name=cc, company="_Test Company")
 
 	def test_gle_based_on_cost_center_allocation(self):
-		cca = create_cost_center_allocation("_Test Company", "Main Cost Center 1 - _TC",
-			{
-				"Sub Cost Center 1 - _TC": 60,
-				"Sub Cost Center 2 - _TC": 40
-			}
+		cca = create_cost_center_allocation(
+			"_Test Company",
+			"Main Cost Center 1 - _TC",
+			{"Sub Cost Center 1 - _TC": 60, "Sub Cost Center 2 - _TC": 40},
 		)
 
-		jv = make_journal_entry("_Test Cash - _TC", "Sales - _TC", 100,
-			cost_center = "Main Cost Center 1 - _TC", submit=True)
+		jv = make_journal_entry(
+			"_Test Cash - _TC", "Sales - _TC", 100, cost_center="Main Cost Center 1 - _TC", submit=True
+		)
 
-		expected_values = [
-			["Sub Cost Center 1 - _TC", 0.0, 60],
-			["Sub Cost Center 2 - _TC", 0.0, 40]
-		]
+		expected_values = [["Sub Cost Center 1 - _TC", 0.0, 60], ["Sub Cost Center 2 - _TC", 0.0, 40]]
 
 		gle = frappe.qb.DocType("GL Entry")
 		gl_entries = (
 			frappe.qb.from_(gle)
 			.select(gle.cost_center, gle.debit, gle.credit)
-			.where(gle.voucher_type == 'Journal Entry')
+			.where(gle.voucher_type == "Journal Entry")
 			.where(gle.voucher_no == jv.name)
-			.where(gle.account == 'Sales - _TC')
+			.where(gle.account == "Sales - _TC")
 			.orderby(gle.cost_center)
 		).run(as_dict=1)
 
@@ -61,11 +63,11 @@
 
 	def test_main_cost_center_cant_be_child(self):
 		# Main cost center itself cannot be entered in child table
-		cca = create_cost_center_allocation("_Test Company", "Main Cost Center 1 - _TC",
-			{
-				"Sub Cost Center 1 - _TC": 60,
-				"Main Cost Center 1 - _TC": 40
-			}, save=False
+		cca = create_cost_center_allocation(
+			"_Test Company",
+			"Main Cost Center 1 - _TC",
+			{"Sub Cost Center 1 - _TC": 60, "Main Cost Center 1 - _TC": 40},
+			save=False,
 		)
 
 		self.assertRaises(MainCostCenterCantBeChild, cca.save)
@@ -73,17 +75,14 @@
 	def test_invalid_main_cost_center(self):
 		# If main cost center is used for allocation under any other cost center,
 		# allocation cannot be done against it
-		cca1 = create_cost_center_allocation("_Test Company", "Main Cost Center 1 - _TC",
-			{
-				"Sub Cost Center 1 - _TC": 60,
-				"Sub Cost Center 2 - _TC": 40
-			}
+		cca1 = create_cost_center_allocation(
+			"_Test Company",
+			"Main Cost Center 1 - _TC",
+			{"Sub Cost Center 1 - _TC": 60, "Sub Cost Center 2 - _TC": 40},
 		)
 
-		cca2 = create_cost_center_allocation("_Test Company", "Sub Cost Center 1 - _TC",
-			{
-				"Sub Cost Center 2 - _TC": 100
-			}, save=False
+		cca2 = create_cost_center_allocation(
+			"_Test Company", "Sub Cost Center 1 - _TC", {"Sub Cost Center 2 - _TC": 100}, save=False
 		)
 
 		self.assertRaises(InvalidMainCostCenter, cca2.save)
@@ -92,18 +91,17 @@
 
 	def test_if_child_cost_center_has_any_allocation_record(self):
 		# Check if any child cost center is used as main cost center in any other existing allocation
-		cca1 = create_cost_center_allocation("_Test Company", "Main Cost Center 1 - _TC",
-			{
-				"Sub Cost Center 1 - _TC": 60,
-				"Sub Cost Center 2 - _TC": 40
-			}
+		cca1 = create_cost_center_allocation(
+			"_Test Company",
+			"Main Cost Center 1 - _TC",
+			{"Sub Cost Center 1 - _TC": 60, "Sub Cost Center 2 - _TC": 40},
 		)
 
-		cca2 = create_cost_center_allocation("_Test Company", "Main Cost Center 2 - _TC",
-			{
-				"Main Cost Center 1 - _TC": 60,
-				"Sub Cost Center 1 - _TC": 40
-			}, save=False
+		cca2 = create_cost_center_allocation(
+			"_Test Company",
+			"Main Cost Center 2 - _TC",
+			{"Main Cost Center 1 - _TC": 60, "Sub Cost Center 1 - _TC": 40},
+			save=False,
 		)
 
 		self.assertRaises(InvalidChildCostCenter, cca2.save)
@@ -111,46 +109,58 @@
 		cca1.cancel()
 
 	def test_total_percentage(self):
-		cca = create_cost_center_allocation("_Test Company", "Main Cost Center 1 - _TC",
-			{
-				"Sub Cost Center 1 - _TC": 40,
-				"Sub Cost Center 2 - _TC": 40
-			}, save=False
+		cca = create_cost_center_allocation(
+			"_Test Company",
+			"Main Cost Center 1 - _TC",
+			{"Sub Cost Center 1 - _TC": 40, "Sub Cost Center 2 - _TC": 40},
+			save=False,
 		)
 		self.assertRaises(WrongPercentageAllocation, cca.save)
 
 	def test_valid_from_based_on_existing_gle(self):
 		# GLE posted against Sub Cost Center 1 on today
-		jv = make_journal_entry("_Test Cash - _TC", "Sales - _TC", 100,
-			cost_center = "Main Cost Center 1 - _TC", posting_date=today(), submit=True)
+		jv = make_journal_entry(
+			"_Test Cash - _TC",
+			"Sales - _TC",
+			100,
+			cost_center="Main Cost Center 1 - _TC",
+			posting_date=today(),
+			submit=True,
+		)
 
 		# try to set valid from as yesterday
-		cca = create_cost_center_allocation("_Test Company", "Main Cost Center 1 - _TC",
-			{
-				"Sub Cost Center 1 - _TC": 60,
-				"Sub Cost Center 2 - _TC": 40
-			}, valid_from=add_days(today(), -1), save=False
+		cca = create_cost_center_allocation(
+			"_Test Company",
+			"Main Cost Center 1 - _TC",
+			{"Sub Cost Center 1 - _TC": 60, "Sub Cost Center 2 - _TC": 40},
+			valid_from=add_days(today(), -1),
+			save=False,
 		)
 
 		self.assertRaises(InvalidDateError, cca.save)
 
 		jv.cancel()
 
-def create_cost_center_allocation(company, main_cost_center, allocation_percentages,
-		valid_from=None, valid_upto=None, save=True, submit=True):
+
+def create_cost_center_allocation(
+	company,
+	main_cost_center,
+	allocation_percentages,
+	valid_from=None,
+	valid_upto=None,
+	save=True,
+	submit=True,
+):
 	doc = frappe.new_doc("Cost Center Allocation")
 	doc.main_cost_center = main_cost_center
 	doc.company = company
 	doc.valid_from = valid_from or today()
 	doc.valid_upto = valid_upto
 	for cc, percentage in allocation_percentages.items():
-		doc.append("allocation_percentages", {
-			"cost_center": cc,
-			"percentage": percentage
-		})
+		doc.append("allocation_percentages", {"cost_center": cc, "percentage": percentage})
 	if save:
 		doc.save()
 		if submit:
 			doc.submit()
 
-	return doc
\ No newline at end of file
+	return doc
diff --git a/erpnext/accounts/doctype/coupon_code/coupon_code.py b/erpnext/accounts/doctype/coupon_code/coupon_code.py
index ee32de1..6a0cdf9 100644
--- a/erpnext/accounts/doctype/coupon_code/coupon_code.py
+++ b/erpnext/accounts/doctype/coupon_code/coupon_code.py
@@ -15,7 +15,7 @@
 
 		if not self.coupon_code:
 			if self.coupon_type == "Promotional":
-				self.coupon_code =''.join(i for i in self.coupon_name if not i.isdigit())[0:8].upper()
+				self.coupon_code = "".join(i for i in self.coupon_name if not i.isdigit())[0:8].upper()
 			elif self.coupon_type == "Gift Card":
 				self.coupon_code = frappe.generate_hash()[:10].upper()
 
diff --git a/erpnext/accounts/doctype/coupon_code/test_coupon_code.py b/erpnext/accounts/doctype/coupon_code/test_coupon_code.py
index 8dab117..aad341e 100644
--- a/erpnext/accounts/doctype/coupon_code/test_coupon_code.py
+++ b/erpnext/accounts/doctype/coupon_code/test_coupon_code.py
@@ -7,91 +7,109 @@
 
 from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
 
-test_dependencies = ['Item']
+test_dependencies = ["Item"]
+
 
 def test_create_test_data():
 	frappe.set_user("Administrator")
 	# create test item
-	if not frappe.db.exists("Item","_Test Tesla Car"):
-		item = frappe.get_doc({
-		"description": "_Test Tesla Car",
-		"doctype": "Item",
-		"has_batch_no": 0,
-		"has_serial_no": 0,
-		"inspection_required": 0,
-		"is_stock_item": 1,
-		"opening_stock":100,
-		"is_sub_contracted_item": 0,
-		"item_code": "_Test Tesla Car",
-		"item_group": "_Test Item Group",
-		"item_name": "_Test Tesla Car",
-		"apply_warehouse_wise_reorder_level": 0,
-		"warehouse":"Stores - _TC",
-		"valuation_rate": 5000,
-		"standard_rate":5000,
-		"item_defaults": [{
-		"company": "_Test Company",
-		"default_warehouse": "Stores - _TC",
-		"default_price_list":"_Test Price List",
-		"expense_account": "Cost of Goods Sold - _TC",
-		"buying_cost_center": "Main - _TC",
-		"selling_cost_center": "Main - _TC",
-		"income_account": "Sales - _TC"
-		}],
-		})
+	if not frappe.db.exists("Item", "_Test Tesla Car"):
+		item = frappe.get_doc(
+			{
+				"description": "_Test Tesla Car",
+				"doctype": "Item",
+				"has_batch_no": 0,
+				"has_serial_no": 0,
+				"inspection_required": 0,
+				"is_stock_item": 1,
+				"opening_stock": 100,
+				"is_sub_contracted_item": 0,
+				"item_code": "_Test Tesla Car",
+				"item_group": "_Test Item Group",
+				"item_name": "_Test Tesla Car",
+				"apply_warehouse_wise_reorder_level": 0,
+				"warehouse": "Stores - _TC",
+				"valuation_rate": 5000,
+				"standard_rate": 5000,
+				"item_defaults": [
+					{
+						"company": "_Test Company",
+						"default_warehouse": "Stores - _TC",
+						"default_price_list": "_Test Price List",
+						"expense_account": "Cost of Goods Sold - _TC",
+						"buying_cost_center": "Main - _TC",
+						"selling_cost_center": "Main - _TC",
+						"income_account": "Sales - _TC",
+					}
+				],
+			}
+		)
 		item.insert()
 	# create test item price
-	item_price = frappe.get_list('Item Price', filters={'item_code': '_Test Tesla Car', 'price_list': '_Test Price List'}, fields=['name'])
-	if len(item_price)==0:
-		item_price = frappe.get_doc({
-			"doctype": "Item Price",
-			"item_code": "_Test Tesla Car",
-			"price_list": "_Test Price List",
-			"price_list_rate": 5000
-		})
+	item_price = frappe.get_list(
+		"Item Price",
+		filters={"item_code": "_Test Tesla Car", "price_list": "_Test Price List"},
+		fields=["name"],
+	)
+	if len(item_price) == 0:
+		item_price = frappe.get_doc(
+			{
+				"doctype": "Item Price",
+				"item_code": "_Test Tesla Car",
+				"price_list": "_Test Price List",
+				"price_list_rate": 5000,
+			}
+		)
 		item_price.insert()
 	# create test item pricing rule
 	if not frappe.db.exists("Pricing Rule", {"title": "_Test Pricing Rule for _Test Item"}):
-		item_pricing_rule = frappe.get_doc({
-		"doctype": "Pricing Rule",
-		"title": "_Test Pricing Rule for _Test Item",
-		"apply_on": "Item Code",
-		"items": [{
-			"item_code": "_Test Tesla Car"
-		}],
-		"warehouse":"Stores - _TC",
-		"coupon_code_based":1,
-		"selling": 1,
-		"rate_or_discount": "Discount Percentage",
-		"discount_percentage": 30,
-		"company": "_Test Company",
-		"currency":"INR",
-		"for_price_list":"_Test Price List"
-		})
+		item_pricing_rule = frappe.get_doc(
+			{
+				"doctype": "Pricing Rule",
+				"title": "_Test Pricing Rule for _Test Item",
+				"apply_on": "Item Code",
+				"items": [{"item_code": "_Test Tesla Car"}],
+				"warehouse": "Stores - _TC",
+				"coupon_code_based": 1,
+				"selling": 1,
+				"rate_or_discount": "Discount Percentage",
+				"discount_percentage": 30,
+				"company": "_Test Company",
+				"currency": "INR",
+				"for_price_list": "_Test Price List",
+			}
+		)
 		item_pricing_rule.insert()
 	# create test item sales partner
-	if not frappe.db.exists("Sales Partner","_Test Coupon Partner"):
-		sales_partner = frappe.get_doc({
-		"doctype": "Sales Partner",
-		"partner_name":"_Test Coupon Partner",
-		"commission_rate":2,
-		"referral_code": "COPART"
-		})
+	if not frappe.db.exists("Sales Partner", "_Test Coupon Partner"):
+		sales_partner = frappe.get_doc(
+			{
+				"doctype": "Sales Partner",
+				"partner_name": "_Test Coupon Partner",
+				"commission_rate": 2,
+				"referral_code": "COPART",
+			}
+		)
 		sales_partner.insert()
 	# create test item coupon code
 	if not frappe.db.exists("Coupon Code", "SAVE30"):
-		pricing_rule = frappe.db.get_value("Pricing Rule", {"title": "_Test Pricing Rule for _Test Item"}, ['name'])
-		coupon_code = frappe.get_doc({
-			"doctype": "Coupon Code",
-			"coupon_name":"SAVE30",
-			"coupon_code":"SAVE30",
-			"pricing_rule": pricing_rule,
-			"valid_from": "2014-01-01",
-			"maximum_use":1,
-			"used":0
-		})
+		pricing_rule = frappe.db.get_value(
+			"Pricing Rule", {"title": "_Test Pricing Rule for _Test Item"}, ["name"]
+		)
+		coupon_code = frappe.get_doc(
+			{
+				"doctype": "Coupon Code",
+				"coupon_name": "SAVE30",
+				"coupon_code": "SAVE30",
+				"pricing_rule": pricing_rule,
+				"valid_from": "2014-01-01",
+				"maximum_use": 1,
+				"used": 0,
+			}
+		)
 		coupon_code.insert()
 
+
 class TestCouponCode(unittest.TestCase):
 	def setUp(self):
 		test_create_test_data()
@@ -102,15 +120,21 @@
 	def test_sales_order_with_coupon_code(self):
 		frappe.db.set_value("Coupon Code", "SAVE30", "used", 0)
 
-		so = make_sales_order(company='_Test Company', warehouse='Stores - _TC',
-			customer="_Test Customer", selling_price_list="_Test Price List",
-			item_code="_Test Tesla Car", rate=5000, qty=1,
-			do_not_submit=True)
+		so = make_sales_order(
+			company="_Test Company",
+			warehouse="Stores - _TC",
+			customer="_Test Customer",
+			selling_price_list="_Test Price List",
+			item_code="_Test Tesla Car",
+			rate=5000,
+			qty=1,
+			do_not_submit=True,
+		)
 
 		self.assertEqual(so.items[0].rate, 5000)
 
-		so.coupon_code='SAVE30'
-		so.sales_partner='_Test Coupon Partner'
+		so.coupon_code = "SAVE30"
+		so.sales_partner = "_Test Coupon Partner"
 		so.save()
 
 		# check item price after coupon code is applied
diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py
index e16ff3a..d618c5c 100644
--- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py
+++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py
@@ -11,45 +11,40 @@
 class CurrencyExchangeSettings(Document):
 	def validate(self):
 		self.set_parameters_and_result()
+		if frappe.flags.in_test or frappe.flags.in_install or frappe.flags.in_setup_wizard:
+			return
 		response, value = self.validate_parameters()
 		self.validate_result(response, value)
 
 	def set_parameters_and_result(self):
-		if self.service_provider == 'exchangerate.host':
-			self.set('result_key', [])
-			self.set('req_params', [])
+		if self.service_provider == "exchangerate.host":
+			self.set("result_key", [])
+			self.set("req_params", [])
 
 			self.api_endpoint = "https://api.exchangerate.host/convert"
-			self.append('result_key', {'key': 'result'})
-			self.append('req_params', {'key': 'date', 'value': '{transaction_date}'})
-			self.append('req_params', {'key': 'from', 'value': '{from_currency}'})
-			self.append('req_params', {'key': 'to', 'value': '{to_currency}'})
-		elif self.service_provider == 'frankfurter.app':
-			self.set('result_key', [])
-			self.set('req_params', [])
+			self.append("result_key", {"key": "result"})
+			self.append("req_params", {"key": "date", "value": "{transaction_date}"})
+			self.append("req_params", {"key": "from", "value": "{from_currency}"})
+			self.append("req_params", {"key": "to", "value": "{to_currency}"})
+		elif self.service_provider == "frankfurter.app":
+			self.set("result_key", [])
+			self.set("req_params", [])
 
 			self.api_endpoint = "https://frankfurter.app/{transaction_date}"
-			self.append('result_key', {'key': 'rates'})
-			self.append('result_key', {'key': '{to_currency}'})
-			self.append('req_params', {'key': 'base', 'value': '{from_currency}'})
-			self.append('req_params', {'key': 'symbols', 'value': '{to_currency}'})
+			self.append("result_key", {"key": "rates"})
+			self.append("result_key", {"key": "{to_currency}"})
+			self.append("req_params", {"key": "base", "value": "{from_currency}"})
+			self.append("req_params", {"key": "symbols", "value": "{to_currency}"})
 
 	def validate_parameters(self):
-		if frappe.flags.in_test:
-			return None, None
-
 		params = {}
 		for row in self.req_params:
 			params[row.key] = row.value.format(
-				transaction_date=nowdate(),
-				to_currency='INR',
-				from_currency='USD'
+				transaction_date=nowdate(), to_currency="INR", from_currency="USD"
 			)
 
 		api_url = self.api_endpoint.format(
-			transaction_date=nowdate(),
-			to_currency='INR',
-			from_currency='USD'
+			transaction_date=nowdate(), to_currency="INR", from_currency="USD"
 		)
 
 		try:
@@ -63,20 +58,14 @@
 		return response, value
 
 	def validate_result(self, response, value):
-		if frappe.flags.in_test:
-			return
-
 		try:
 			for key in self.result_key:
-				value = value[str(key.key).format(
-					transaction_date=nowdate(),
-					to_currency='INR',
-					from_currency='USD'
-				)]
+				value = value[
+					str(key.key).format(transaction_date=nowdate(), to_currency="INR", from_currency="USD")
+				]
 		except Exception:
-			frappe.throw("Invalid result key. Response: " + response.text)
+			frappe.throw(_("Invalid result key. Response:") + " " + response.text)
 		if not isinstance(value, (int, float)):
 			frappe.throw(_("Returned exchange rate is neither integer not float."))
 
 		self.url = response.url
-		frappe.msgprint("Exchange rate of USD to INR is " + str(value))
diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py
index 5da0077..9874d66 100644
--- a/erpnext/accounts/doctype/dunning/dunning.py
+++ b/erpnext/accounts/doctype/dunning/dunning.py
@@ -19,78 +19,99 @@
 		self.validate_overdue_days()
 		self.validate_amount()
 		if not self.income_account:
-			self.income_account = frappe.db.get_value('Company', self.company, 'default_income_account')
+			self.income_account = frappe.db.get_value("Company", self.company, "default_income_account")
 
 	def validate_overdue_days(self):
 		self.overdue_days = (getdate(self.posting_date) - getdate(self.due_date)).days or 0
 
 	def validate_amount(self):
 		amounts = calculate_interest_and_amount(
-			self.outstanding_amount, self.rate_of_interest, self.dunning_fee, self.overdue_days)
-		if self.interest_amount != amounts.get('interest_amount'):
-			self.interest_amount = flt(amounts.get('interest_amount'), self.precision('interest_amount'))
-		if self.dunning_amount != amounts.get('dunning_amount'):
-			self.dunning_amount = flt(amounts.get('dunning_amount'), self.precision('dunning_amount'))
-		if self.grand_total != amounts.get('grand_total'):
-			self.grand_total = flt(amounts.get('grand_total'), self.precision('grand_total'))
+			self.outstanding_amount, self.rate_of_interest, self.dunning_fee, self.overdue_days
+		)
+		if self.interest_amount != amounts.get("interest_amount"):
+			self.interest_amount = flt(amounts.get("interest_amount"), self.precision("interest_amount"))
+		if self.dunning_amount != amounts.get("dunning_amount"):
+			self.dunning_amount = flt(amounts.get("dunning_amount"), self.precision("dunning_amount"))
+		if self.grand_total != amounts.get("grand_total"):
+			self.grand_total = flt(amounts.get("grand_total"), self.precision("grand_total"))
 
 	def on_submit(self):
 		self.make_gl_entries()
 
 	def on_cancel(self):
 		if self.dunning_amount:
-			self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+			self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
 			make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
 
 	def make_gl_entries(self):
 		if not self.dunning_amount:
 			return
 		gl_entries = []
-		invoice_fields = ["project", "cost_center", "debit_to", "party_account_currency", "conversion_rate", "cost_center"]
+		invoice_fields = [
+			"project",
+			"cost_center",
+			"debit_to",
+			"party_account_currency",
+			"conversion_rate",
+			"cost_center",
+		]
 		inv = frappe.db.get_value("Sales Invoice", self.sales_invoice, invoice_fields, as_dict=1)
 
 		accounting_dimensions = get_accounting_dimensions()
 		invoice_fields.extend(accounting_dimensions)
 
 		dunning_in_company_currency = flt(self.dunning_amount * inv.conversion_rate)
-		default_cost_center = frappe.get_cached_value('Company',  self.company,  'cost_center')
+		default_cost_center = frappe.get_cached_value("Company", self.company, "cost_center")
 
 		gl_entries.append(
-			self.get_gl_dict({
-				"account": inv.debit_to,
-				"party_type": "Customer",
-				"party": self.customer,
-				"due_date": self.due_date,
-				"against": self.income_account,
-				"debit": dunning_in_company_currency,
-				"debit_in_account_currency": self.dunning_amount,
-				"against_voucher": self.name,
-				"against_voucher_type": "Dunning",
-				"cost_center": inv.cost_center or default_cost_center,
-				"project": inv.project
-			}, inv.party_account_currency, item=inv)
+			self.get_gl_dict(
+				{
+					"account": inv.debit_to,
+					"party_type": "Customer",
+					"party": self.customer,
+					"due_date": self.due_date,
+					"against": self.income_account,
+					"debit": dunning_in_company_currency,
+					"debit_in_account_currency": self.dunning_amount,
+					"against_voucher": self.name,
+					"against_voucher_type": "Dunning",
+					"cost_center": inv.cost_center or default_cost_center,
+					"project": inv.project,
+				},
+				inv.party_account_currency,
+				item=inv,
+			)
 		)
 		gl_entries.append(
-			self.get_gl_dict({
-				"account": self.income_account,
-				"against": self.customer,
-				"credit": dunning_in_company_currency,
-				"cost_center": inv.cost_center or default_cost_center,
-				"credit_in_account_currency": self.dunning_amount,
-				"project": inv.project
-			}, item=inv)
+			self.get_gl_dict(
+				{
+					"account": self.income_account,
+					"against": self.customer,
+					"credit": dunning_in_company_currency,
+					"cost_center": inv.cost_center or default_cost_center,
+					"credit_in_account_currency": self.dunning_amount,
+					"project": inv.project,
+				},
+				item=inv,
+			)
 		)
-		make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding="No", merge_entries=False)
+		make_gl_entries(
+			gl_entries, cancel=(self.docstatus == 2), update_outstanding="No", merge_entries=False
+		)
 
 
 def resolve_dunning(doc, state):
 	for reference in doc.references:
-		if reference.reference_doctype == 'Sales Invoice' and reference.outstanding_amount <= 0:
-			dunnings = frappe.get_list('Dunning', filters={
-				'sales_invoice': reference.reference_name, 'status': ('!=', 'Resolved')}, ignore_permissions=True)
+		if reference.reference_doctype == "Sales Invoice" and reference.outstanding_amount <= 0:
+			dunnings = frappe.get_list(
+				"Dunning",
+				filters={"sales_invoice": reference.reference_name, "status": ("!=", "Resolved")},
+				ignore_permissions=True,
+			)
 
 			for dunning in dunnings:
-				frappe.db.set_value("Dunning", dunning.name, "status", 'Resolved')
+				frappe.db.set_value("Dunning", dunning.name, "status", "Resolved")
+
 
 def calculate_interest_and_amount(outstanding_amount, rate_of_interest, dunning_fee, overdue_days):
 	interest_amount = 0
@@ -101,23 +122,26 @@
 		grand_total += flt(interest_amount)
 	dunning_amount = flt(interest_amount) + flt(dunning_fee)
 	return {
-		'interest_amount': interest_amount,
-		'grand_total': grand_total,
-		'dunning_amount': dunning_amount}
+		"interest_amount": interest_amount,
+		"grand_total": grand_total,
+		"dunning_amount": dunning_amount,
+	}
+
 
 @frappe.whitelist()
 def get_dunning_letter_text(dunning_type, doc, language=None):
 	if isinstance(doc, str):
 		doc = json.loads(doc)
 	if language:
-		filters = {'parent': dunning_type, 'language': language}
+		filters = {"parent": dunning_type, "language": language}
 	else:
-		filters = {'parent': dunning_type, 'is_default_language': 1}
-	letter_text = frappe.db.get_value('Dunning Letter Text', filters,
-		['body_text', 'closing_text', 'language'], as_dict=1)
+		filters = {"parent": dunning_type, "is_default_language": 1}
+	letter_text = frappe.db.get_value(
+		"Dunning Letter Text", filters, ["body_text", "closing_text", "language"], as_dict=1
+	)
 	if letter_text:
 		return {
-			'body_text': frappe.render_template(letter_text.body_text, doc),
-			'closing_text': frappe.render_template(letter_text.closing_text, doc),
-			'language': letter_text.language
+			"body_text": frappe.render_template(letter_text.body_text, doc),
+			"closing_text": frappe.render_template(letter_text.closing_text, doc),
+			"language": letter_text.language,
 		}
diff --git a/erpnext/accounts/doctype/dunning/dunning_dashboard.py b/erpnext/accounts/doctype/dunning/dunning_dashboard.py
index a891bd2..d1d4031 100644
--- a/erpnext/accounts/doctype/dunning/dunning_dashboard.py
+++ b/erpnext/accounts/doctype/dunning/dunning_dashboard.py
@@ -3,15 +3,10 @@
 
 def get_data():
 	return {
-		'fieldname': 'dunning',
-		'non_standard_fieldnames': {
-			'Journal Entry': 'reference_name',
-			'Payment Entry': 'reference_name'
+		"fieldname": "dunning",
+		"non_standard_fieldnames": {
+			"Journal Entry": "reference_name",
+			"Payment Entry": "reference_name",
 		},
-		'transactions': [
-			{
-				'label': _('Payment'),
-				'items': ['Payment Entry', 'Journal Entry']
-			}
-		]
+		"transactions": [{"label": _("Payment"), "items": ["Payment Entry", "Journal Entry"]}],
 	}
diff --git a/erpnext/accounts/doctype/dunning/test_dunning.py b/erpnext/accounts/doctype/dunning/test_dunning.py
index b043c5b..e1fd1e9 100644
--- a/erpnext/accounts/doctype/dunning/test_dunning.py
+++ b/erpnext/accounts/doctype/dunning/test_dunning.py
@@ -30,30 +30,35 @@
 	def test_dunning(self):
 		dunning = create_dunning()
 		amounts = calculate_interest_and_amount(
-			dunning.outstanding_amount, dunning.rate_of_interest, dunning.dunning_fee, dunning.overdue_days)
-		self.assertEqual(round(amounts.get('interest_amount'), 2), 0.44)
-		self.assertEqual(round(amounts.get('dunning_amount'), 2), 20.44)
-		self.assertEqual(round(amounts.get('grand_total'), 2), 120.44)
+			dunning.outstanding_amount, dunning.rate_of_interest, dunning.dunning_fee, dunning.overdue_days
+		)
+		self.assertEqual(round(amounts.get("interest_amount"), 2), 0.44)
+		self.assertEqual(round(amounts.get("dunning_amount"), 2), 20.44)
+		self.assertEqual(round(amounts.get("grand_total"), 2), 120.44)
 
 	def test_dunning_with_zero_interest_rate(self):
 		dunning = create_dunning_with_zero_interest_rate()
 		amounts = calculate_interest_and_amount(
-			dunning.outstanding_amount, dunning.rate_of_interest, dunning.dunning_fee, dunning.overdue_days)
-		self.assertEqual(round(amounts.get('interest_amount'), 2), 0)
-		self.assertEqual(round(amounts.get('dunning_amount'), 2), 20)
-		self.assertEqual(round(amounts.get('grand_total'), 2), 120)
+			dunning.outstanding_amount, dunning.rate_of_interest, dunning.dunning_fee, dunning.overdue_days
+		)
+		self.assertEqual(round(amounts.get("interest_amount"), 2), 0)
+		self.assertEqual(round(amounts.get("dunning_amount"), 2), 20)
+		self.assertEqual(round(amounts.get("grand_total"), 2), 120)
 
 	def test_gl_entries(self):
 		dunning = create_dunning()
 		dunning.submit()
-		gl_entries = frappe.db.sql("""select account, debit, credit
+		gl_entries = frappe.db.sql(
+			"""select account, debit, credit
 			from `tabGL Entry` where voucher_type='Dunning' and voucher_no=%s
-			order by account asc""", dunning.name, as_dict=1)
+			order by account asc""",
+			dunning.name,
+			as_dict=1,
+		)
 		self.assertTrue(gl_entries)
-		expected_values = dict((d[0], d) for d in [
-			['Debtors - _TC', 20.44, 0.0],
-			['Sales - _TC',  0.0, 20.44]
-		])
+		expected_values = dict(
+			(d[0], d) for d in [["Debtors - _TC", 20.44, 0.0], ["Sales - _TC", 0.0, 20.44]]
+		)
 		for gle in gl_entries:
 			self.assertEqual(expected_values[gle.account][0], gle.account)
 			self.assertEqual(expected_values[gle.account][1], gle.debit)
@@ -71,7 +76,7 @@
 		pe.target_exchange_rate = 1
 		pe.insert()
 		pe.submit()
-		si_doc = frappe.get_doc('Sales Invoice', dunning.sales_invoice)
+		si_doc = frappe.get_doc("Sales Invoice", dunning.sales_invoice)
 		self.assertEqual(si_doc.outstanding_amount, 0)
 
 
@@ -79,8 +84,9 @@
 	posting_date = add_days(today(), -20)
 	due_date = add_days(today(), -15)
 	sales_invoice = create_sales_invoice_against_cost_center(
-		posting_date=posting_date, due_date=due_date, status='Overdue')
-	dunning_type = frappe.get_doc("Dunning Type", 'First Notice')
+		posting_date=posting_date, due_date=due_date, status="Overdue"
+	)
+	dunning_type = frappe.get_doc("Dunning Type", "First Notice")
 	dunning = frappe.new_doc("Dunning")
 	dunning.sales_invoice = sales_invoice.name
 	dunning.customer_name = sales_invoice.customer_name
@@ -90,18 +96,20 @@
 	dunning.company = sales_invoice.company
 	dunning.posting_date = nowdate()
 	dunning.due_date = sales_invoice.due_date
-	dunning.dunning_type = 'First Notice'
+	dunning.dunning_type = "First Notice"
 	dunning.rate_of_interest = dunning_type.rate_of_interest
 	dunning.dunning_fee = dunning_type.dunning_fee
 	dunning.save()
 	return dunning
 
+
 def create_dunning_with_zero_interest_rate():
 	posting_date = add_days(today(), -20)
 	due_date = add_days(today(), -15)
 	sales_invoice = create_sales_invoice_against_cost_center(
-		posting_date=posting_date, due_date=due_date, status='Overdue')
-	dunning_type = frappe.get_doc("Dunning Type", 'First Notice with 0% Rate of Interest')
+		posting_date=posting_date, due_date=due_date, status="Overdue"
+	)
+	dunning_type = frappe.get_doc("Dunning Type", "First Notice with 0% Rate of Interest")
 	dunning = frappe.new_doc("Dunning")
 	dunning.sales_invoice = sales_invoice.name
 	dunning.customer_name = sales_invoice.customer_name
@@ -111,40 +119,44 @@
 	dunning.company = sales_invoice.company
 	dunning.posting_date = nowdate()
 	dunning.due_date = sales_invoice.due_date
-	dunning.dunning_type = 'First Notice with 0% Rate of Interest'
+	dunning.dunning_type = "First Notice with 0% Rate of Interest"
 	dunning.rate_of_interest = dunning_type.rate_of_interest
 	dunning.dunning_fee = dunning_type.dunning_fee
 	dunning.save()
 	return dunning
 
+
 def create_dunning_type():
 	dunning_type = frappe.new_doc("Dunning Type")
-	dunning_type.dunning_type = 'First Notice'
+	dunning_type.dunning_type = "First Notice"
 	dunning_type.start_day = 10
 	dunning_type.end_day = 20
 	dunning_type.dunning_fee = 20
 	dunning_type.rate_of_interest = 8
 	dunning_type.append(
-		"dunning_letter_text", {
-			'language': 'en',
-			'body_text': 'We have still not received payment for our invoice ',
-			'closing_text': 'We kindly request that you pay the outstanding amount immediately, including interest and late fees.'
-		}
+		"dunning_letter_text",
+		{
+			"language": "en",
+			"body_text": "We have still not received payment for our invoice ",
+			"closing_text": "We kindly request that you pay the outstanding amount immediately, including interest and late fees.",
+		},
 	)
 	dunning_type.save()
 
+
 def create_dunning_type_with_zero_interest_rate():
 	dunning_type = frappe.new_doc("Dunning Type")
-	dunning_type.dunning_type = 'First Notice with 0% Rate of Interest'
+	dunning_type.dunning_type = "First Notice with 0% Rate of Interest"
 	dunning_type.start_day = 10
 	dunning_type.end_day = 20
 	dunning_type.dunning_fee = 20
 	dunning_type.rate_of_interest = 0
 	dunning_type.append(
-		"dunning_letter_text", {
-			'language': 'en',
-			'body_text': 'We have still not received payment for our invoice ',
-			'closing_text': 'We kindly request that you pay the outstanding amount immediately, and late fees.'
-		}
+		"dunning_letter_text",
+		{
+			"language": "en",
+			"body_text": "We have still not received payment for our invoice ",
+			"closing_text": "We kindly request that you pay the outstanding amount immediately, and late fees.",
+		},
 	)
 	dunning_type.save()
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
index 1b13195..2f81c5f 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
@@ -20,8 +20,9 @@
 	def set_total_gain_loss(self):
 		total_gain_loss = 0
 		for d in self.accounts:
-			d.gain_loss = flt(d.new_balance_in_base_currency, d.precision("new_balance_in_base_currency")) \
-				- flt(d.balance_in_base_currency, d.precision("balance_in_base_currency"))
+			d.gain_loss = flt(
+				d.new_balance_in_base_currency, d.precision("new_balance_in_base_currency")
+			) - flt(d.balance_in_base_currency, d.precision("balance_in_base_currency"))
 			total_gain_loss += flt(d.gain_loss, d.precision("gain_loss"))
 		self.total_gain_loss = flt(total_gain_loss, self.precision("total_gain_loss"))
 
@@ -30,15 +31,15 @@
 			frappe.throw(_("Please select Company and Posting Date to getting entries"))
 
 	def on_cancel(self):
-		self.ignore_linked_doctypes = ('GL Entry')
+		self.ignore_linked_doctypes = "GL Entry"
 
 	@frappe.whitelist()
 	def check_journal_entry_condition(self):
-		total_debit = frappe.db.get_value("Journal Entry Account", {
-			'reference_type': 'Exchange Rate Revaluation',
-			'reference_name': self.name,
-			'docstatus': 1
-		}, "sum(debit) as sum")
+		total_debit = frappe.db.get_value(
+			"Journal Entry Account",
+			{"reference_type": "Exchange Rate Revaluation", "reference_name": self.name, "docstatus": 1},
+			"sum(debit) as sum",
+		)
 
 		total_amt = 0
 		for d in self.accounts:
@@ -54,28 +55,33 @@
 		accounts = []
 		self.validate_mandatory()
 		company_currency = erpnext.get_company_currency(self.company)
-		precision = get_field_precision(frappe.get_meta("Exchange Rate Revaluation Account")
-			.get_field("new_balance_in_base_currency"), company_currency)
+		precision = get_field_precision(
+			frappe.get_meta("Exchange Rate Revaluation Account").get_field("new_balance_in_base_currency"),
+			company_currency,
+		)
 
 		account_details = self.get_accounts_from_gle()
 		for d in account_details:
-			current_exchange_rate = d.balance / d.balance_in_account_currency \
-				if d.balance_in_account_currency else 0
+			current_exchange_rate = (
+				d.balance / d.balance_in_account_currency if d.balance_in_account_currency else 0
+			)
 			new_exchange_rate = get_exchange_rate(d.account_currency, company_currency, self.posting_date)
 			new_balance_in_base_currency = flt(d.balance_in_account_currency * new_exchange_rate)
 			gain_loss = flt(new_balance_in_base_currency, precision) - flt(d.balance, precision)
 			if gain_loss:
-				accounts.append({
-					"account": d.account,
-					"party_type": d.party_type,
-					"party": d.party,
-					"account_currency": d.account_currency,
-					"balance_in_base_currency": d.balance,
-					"balance_in_account_currency": d.balance_in_account_currency,
-					"current_exchange_rate": current_exchange_rate,
-					"new_exchange_rate": new_exchange_rate,
-					"new_balance_in_base_currency": new_balance_in_base_currency
-				})
+				accounts.append(
+					{
+						"account": d.account,
+						"party_type": d.party_type,
+						"party": d.party,
+						"account_currency": d.account_currency,
+						"balance_in_base_currency": d.balance,
+						"balance_in_account_currency": d.balance_in_account_currency,
+						"current_exchange_rate": current_exchange_rate,
+						"new_exchange_rate": new_exchange_rate,
+						"new_balance_in_base_currency": new_balance_in_base_currency,
+					}
+				)
 
 		if not accounts:
 			self.throw_invalid_response_message(account_details)
@@ -84,7 +90,8 @@
 
 	def get_accounts_from_gle(self):
 		company_currency = erpnext.get_company_currency(self.company)
-		accounts = frappe.db.sql_list("""
+		accounts = frappe.db.sql_list(
+			"""
 			select name
 			from tabAccount
 			where is_group = 0
@@ -93,11 +100,14 @@
 				and account_type != 'Stock'
 				and company=%s
 				and account_currency != %s
-			order by name""",(self.company, company_currency))
+			order by name""",
+			(self.company, company_currency),
+		)
 
 		account_details = []
 		if accounts:
-			account_details = frappe.db.sql("""
+			account_details = frappe.db.sql(
+				"""
 				select
 					account, party_type, party, account_currency,
 					sum(debit_in_account_currency) - sum(credit_in_account_currency) as balance_in_account_currency,
@@ -109,7 +119,11 @@
 				group by account, NULLIF(party_type,''), NULLIF(party,'')
 				having sum(debit) != sum(credit)
 				order by account
-			""" % (', '.join(['%s']*len(accounts)), '%s'), tuple(accounts + [self.posting_date]), as_dict=1)
+			"""
+				% (", ".join(["%s"] * len(accounts)), "%s"),
+				tuple(accounts + [self.posting_date]),
+				as_dict=1,
+			)
 
 		return account_details
 
@@ -125,77 +139,107 @@
 		if self.total_gain_loss == 0:
 			return
 
-		unrealized_exchange_gain_loss_account = frappe.get_cached_value('Company',  self.company,
-			"unrealized_exchange_gain_loss_account")
+		unrealized_exchange_gain_loss_account = frappe.get_cached_value(
+			"Company", self.company, "unrealized_exchange_gain_loss_account"
+		)
 		if not unrealized_exchange_gain_loss_account:
-			frappe.throw(_("Please set Unrealized Exchange Gain/Loss Account in Company {0}")
-				.format(self.company))
+			frappe.throw(
+				_("Please set Unrealized Exchange Gain/Loss Account in Company {0}").format(self.company)
+			)
 
-		journal_entry = frappe.new_doc('Journal Entry')
-		journal_entry.voucher_type = 'Exchange Rate Revaluation'
+		journal_entry = frappe.new_doc("Journal Entry")
+		journal_entry.voucher_type = "Exchange Rate Revaluation"
 		journal_entry.company = self.company
 		journal_entry.posting_date = self.posting_date
 		journal_entry.multi_currency = 1
 
 		journal_entry_accounts = []
 		for d in self.accounts:
-			dr_or_cr = "debit_in_account_currency" \
-				if d.get("balance_in_account_currency") > 0 else "credit_in_account_currency"
+			dr_or_cr = (
+				"debit_in_account_currency"
+				if d.get("balance_in_account_currency") > 0
+				else "credit_in_account_currency"
+			)
 
-			reverse_dr_or_cr = "debit_in_account_currency" \
-				if dr_or_cr=="credit_in_account_currency" else "credit_in_account_currency"
+			reverse_dr_or_cr = (
+				"debit_in_account_currency"
+				if dr_or_cr == "credit_in_account_currency"
+				else "credit_in_account_currency"
+			)
 
-			journal_entry_accounts.append({
-				"account": d.get("account"),
-				"party_type": d.get("party_type"),
-				"party": d.get("party"),
-				"account_currency": d.get("account_currency"),
-				"balance": flt(d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")),
-				dr_or_cr: flt(abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")),
-				"exchange_rate": flt(d.get("new_exchange_rate"), d.precision("new_exchange_rate")),
+			journal_entry_accounts.append(
+				{
+					"account": d.get("account"),
+					"party_type": d.get("party_type"),
+					"party": d.get("party"),
+					"account_currency": d.get("account_currency"),
+					"balance": flt(
+						d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")
+					),
+					dr_or_cr: flt(
+						abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
+					),
+					"exchange_rate": flt(d.get("new_exchange_rate"), d.precision("new_exchange_rate")),
+					"reference_type": "Exchange Rate Revaluation",
+					"reference_name": self.name,
+				}
+			)
+			journal_entry_accounts.append(
+				{
+					"account": d.get("account"),
+					"party_type": d.get("party_type"),
+					"party": d.get("party"),
+					"account_currency": d.get("account_currency"),
+					"balance": flt(
+						d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")
+					),
+					reverse_dr_or_cr: flt(
+						abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
+					),
+					"exchange_rate": flt(d.get("current_exchange_rate"), d.precision("current_exchange_rate")),
+					"reference_type": "Exchange Rate Revaluation",
+					"reference_name": self.name,
+				}
+			)
+
+		journal_entry_accounts.append(
+			{
+				"account": unrealized_exchange_gain_loss_account,
+				"balance": get_balance_on(unrealized_exchange_gain_loss_account),
+				"debit_in_account_currency": abs(self.total_gain_loss) if self.total_gain_loss < 0 else 0,
+				"credit_in_account_currency": self.total_gain_loss if self.total_gain_loss > 0 else 0,
+				"exchange_rate": 1,
 				"reference_type": "Exchange Rate Revaluation",
 				"reference_name": self.name,
-				})
-			journal_entry_accounts.append({
-				"account": d.get("account"),
-				"party_type": d.get("party_type"),
-				"party": d.get("party"),
-				"account_currency": d.get("account_currency"),
-				"balance": flt(d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")),
-				reverse_dr_or_cr: flt(abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")),
-				"exchange_rate": flt(d.get("current_exchange_rate"), d.precision("current_exchange_rate")),
-				"reference_type": "Exchange Rate Revaluation",
-				"reference_name": self.name
-				})
-
-		journal_entry_accounts.append({
-			"account": unrealized_exchange_gain_loss_account,
-			"balance": get_balance_on(unrealized_exchange_gain_loss_account),
-			"debit_in_account_currency": abs(self.total_gain_loss) if self.total_gain_loss < 0 else 0,
-			"credit_in_account_currency": self.total_gain_loss if self.total_gain_loss > 0 else 0,
-			"exchange_rate": 1,
-			"reference_type": "Exchange Rate Revaluation",
-			"reference_name": self.name,
-			})
+			}
+		)
 
 		journal_entry.set("accounts", journal_entry_accounts)
 		journal_entry.set_amounts_in_company_currency()
 		journal_entry.set_total_debit_credit()
 		return journal_entry.as_dict()
 
+
 @frappe.whitelist()
 def get_account_details(account, company, posting_date, party_type=None, party=None):
-	account_currency, account_type = frappe.db.get_value("Account", account,
-		["account_currency", "account_type"])
+	account_currency, account_type = frappe.db.get_value(
+		"Account", account, ["account_currency", "account_type"]
+	)
 	if account_type in ["Receivable", "Payable"] and not (party_type and party):
 		frappe.throw(_("Party Type and Party is mandatory for {0} account").format(account_type))
 
 	account_details = {}
 	company_currency = erpnext.get_company_currency(company)
-	balance = get_balance_on(account, date=posting_date, party_type=party_type, party=party, in_account_currency=False)
+	balance = get_balance_on(
+		account, date=posting_date, party_type=party_type, party=party, in_account_currency=False
+	)
 	if balance:
-		balance_in_account_currency = get_balance_on(account, date=posting_date, party_type=party_type, party=party)
-		current_exchange_rate = balance / balance_in_account_currency if balance_in_account_currency else 0
+		balance_in_account_currency = get_balance_on(
+			account, date=posting_date, party_type=party_type, party=party
+		)
+		current_exchange_rate = (
+			balance / balance_in_account_currency if balance_in_account_currency else 0
+		)
 		new_exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
 		new_balance_in_base_currency = balance_in_account_currency * new_exchange_rate
 		account_details = {
@@ -204,7 +248,7 @@
 			"balance_in_account_currency": balance_in_account_currency,
 			"current_exchange_rate": current_exchange_rate,
 			"new_exchange_rate": new_exchange_rate,
-			"new_balance_in_base_currency": new_balance_in_base_currency
+			"new_balance_in_base_currency": new_balance_in_base_currency,
 		}
 
 	return account_details
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation_dashboard.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation_dashboard.py
index fe86250..7eca970 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation_dashboard.py
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation_dashboard.py
@@ -1,9 +1,2 @@
 def get_data():
-	return {
-		'fieldname': 'reference_name',
-		'transactions': [
-			{
-				'items': ['Journal Entry']
-			}
-		]
-	}
+	return {"fieldname": "reference_name", "transactions": [{"items": ["Journal Entry"]}]}
diff --git a/erpnext/accounts/doctype/finance_book/finance_book_dashboard.py b/erpnext/accounts/doctype/finance_book/finance_book_dashboard.py
index 57b039d..24e6c0c 100644
--- a/erpnext/accounts/doctype/finance_book/finance_book_dashboard.py
+++ b/erpnext/accounts/doctype/finance_book/finance_book_dashboard.py
@@ -3,21 +3,11 @@
 
 def get_data():
 	return {
-		'fieldname': 'finance_book',
-		'non_standard_fieldnames': {
-			'Asset': 'default_finance_book',
-			'Company': 'default_finance_book'
-		},
-		'transactions': [
-			{
-				'label': _('Assets'),
-				'items': ['Asset', 'Asset Value Adjustment']
-			},
-			{
-				'items': ['Company']
-			},
-			{
-				'items': ['Journal Entry']
-			}
-		]
+		"fieldname": "finance_book",
+		"non_standard_fieldnames": {"Asset": "default_finance_book", "Company": "default_finance_book"},
+		"transactions": [
+			{"label": _("Assets"), "items": ["Asset", "Asset Value Adjustment"]},
+			{"items": ["Company"]},
+			{"items": ["Journal Entry"]},
+		],
 	}
diff --git a/erpnext/accounts/doctype/finance_book/test_finance_book.py b/erpnext/accounts/doctype/finance_book/test_finance_book.py
index 5fb3d0a..7b2575d 100644
--- a/erpnext/accounts/doctype/finance_book/test_finance_book.py
+++ b/erpnext/accounts/doctype/finance_book/test_finance_book.py
@@ -13,30 +13,29 @@
 		finance_book = create_finance_book()
 
 		# create jv entry
-		jv = make_journal_entry("_Test Bank - _TC",
-			"_Test Receivable - _TC", 100, save=False)
+		jv = make_journal_entry("_Test Bank - _TC", "_Test Receivable - _TC", 100, save=False)
 
-		jv.accounts[1].update({
-			"party_type": "Customer",
-			"party": "_Test Customer"
-		})
+		jv.accounts[1].update({"party_type": "Customer", "party": "_Test Customer"})
 
 		jv.finance_book = finance_book.finance_book_name
 		jv.submit()
 
 		# check the Finance Book in the GL Entry
-		gl_entries = frappe.get_all("GL Entry", fields=["name", "finance_book"],
-			filters={"voucher_type": "Journal Entry", "voucher_no": jv.name})
+		gl_entries = frappe.get_all(
+			"GL Entry",
+			fields=["name", "finance_book"],
+			filters={"voucher_type": "Journal Entry", "voucher_no": jv.name},
+		)
 
 		for gl_entry in gl_entries:
 			self.assertEqual(gl_entry.finance_book, finance_book.name)
 
+
 def create_finance_book():
 	if not frappe.db.exists("Finance Book", "_Test Finance Book"):
-		finance_book = frappe.get_doc({
-			"doctype": "Finance Book",
-			"finance_book_name": "_Test Finance Book"
-		}).insert()
+		finance_book = frappe.get_doc(
+			{"doctype": "Finance Book", "finance_book_name": "_Test Finance Book"}
+		).insert()
 	else:
 		finance_book = frappe.get_doc("Finance Book", "_Test Finance Book")
 
diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
index dd893f9..069ab5e 100644
--- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
+++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
@@ -9,7 +9,9 @@
 from frappe.utils import add_days, add_years, cstr, getdate
 
 
-class FiscalYearIncorrectDate(frappe.ValidationError): pass
+class FiscalYearIncorrectDate(frappe.ValidationError):
+	pass
+
 
 class FiscalYear(Document):
 	@frappe.whitelist()
@@ -22,19 +24,33 @@
 		# clear cache
 		frappe.clear_cache()
 
-		msgprint(_("{0} is now the default Fiscal Year. Please refresh your browser for the change to take effect.").format(self.name))
+		msgprint(
+			_(
+				"{0} is now the default Fiscal Year. Please refresh your browser for the change to take effect."
+			).format(self.name)
+		)
 
 	def validate(self):
 		self.validate_dates()
 		self.validate_overlap()
 
 		if not self.is_new():
-			year_start_end_dates = frappe.db.sql("""select year_start_date, year_end_date
-				from `tabFiscal Year` where name=%s""", (self.name))
+			year_start_end_dates = frappe.db.sql(
+				"""select year_start_date, year_end_date
+				from `tabFiscal Year` where name=%s""",
+				(self.name),
+			)
 
 			if year_start_end_dates:
-				if getdate(self.year_start_date) != year_start_end_dates[0][0] or getdate(self.year_end_date) != year_start_end_dates[0][1]:
-					frappe.throw(_("Cannot change Fiscal Year Start Date and Fiscal Year End Date once the Fiscal Year is saved."))
+				if (
+					getdate(self.year_start_date) != year_start_end_dates[0][0]
+					or getdate(self.year_end_date) != year_start_end_dates[0][1]
+				):
+					frappe.throw(
+						_(
+							"Cannot change Fiscal Year Start Date and Fiscal Year End Date once the Fiscal Year is saved."
+						)
+					)
 
 	def validate_dates(self):
 		if self.is_short_year:
@@ -43,14 +59,18 @@
 			return
 
 		if getdate(self.year_start_date) > getdate(self.year_end_date):
-			frappe.throw(_("Fiscal Year Start Date should be one year earlier than Fiscal Year End Date"),
-				FiscalYearIncorrectDate)
+			frappe.throw(
+				_("Fiscal Year Start Date should be one year earlier than Fiscal Year End Date"),
+				FiscalYearIncorrectDate,
+			)
 
 		date = getdate(self.year_start_date) + relativedelta(years=1) - relativedelta(days=1)
 
 		if getdate(self.year_end_date) != date:
-			frappe.throw(_("Fiscal Year End Date should be one year after Fiscal Year Start Date"),
-				FiscalYearIncorrectDate)
+			frappe.throw(
+				_("Fiscal Year End Date should be one year after Fiscal Year Start Date"),
+				FiscalYearIncorrectDate,
+			)
 
 	def on_update(self):
 		check_duplicate_fiscal_year(self)
@@ -59,11 +79,16 @@
 	def on_trash(self):
 		global_defaults = frappe.get_doc("Global Defaults")
 		if global_defaults.current_fiscal_year == self.name:
-			frappe.throw(_("You cannot delete Fiscal Year {0}. Fiscal Year {0} is set as default in Global Settings").format(self.name))
+			frappe.throw(
+				_(
+					"You cannot delete Fiscal Year {0}. Fiscal Year {0} is set as default in Global Settings"
+				).format(self.name)
+			)
 		frappe.cache().delete_value("fiscal_years")
 
 	def validate_overlap(self):
-		existing_fiscal_years = frappe.db.sql("""select name from `tabFiscal Year`
+		existing_fiscal_years = frappe.db.sql(
+			"""select name from `tabFiscal Year`
 			where (
 				(%(year_start_date)s between year_start_date and year_end_date)
 				or (%(year_end_date)s between year_start_date and year_end_date)
@@ -73,13 +98,18 @@
 			{
 				"year_start_date": self.year_start_date,
 				"year_end_date": self.year_end_date,
-				"name": self.name or "No Name"
-			}, as_dict=True)
+				"name": self.name or "No Name",
+			},
+			as_dict=True,
+		)
 
 		if existing_fiscal_years:
 			for existing in existing_fiscal_years:
-				company_for_existing = frappe.db.sql_list("""select company from `tabFiscal Year Company`
-					where parent=%s""", existing.name)
+				company_for_existing = frappe.db.sql_list(
+					"""select company from `tabFiscal Year Company`
+					where parent=%s""",
+					existing.name,
+				)
 
 				overlap = False
 				if not self.get("companies") or not company_for_existing:
@@ -90,20 +120,36 @@
 						overlap = True
 
 				if overlap:
-					frappe.throw(_("Year start date or end date is overlapping with {0}. To avoid please set company")
-						.format(existing.name), frappe.NameError)
+					frappe.throw(
+						_("Year start date or end date is overlapping with {0}. To avoid please set company").format(
+							existing.name
+						),
+						frappe.NameError,
+					)
+
 
 @frappe.whitelist()
 def check_duplicate_fiscal_year(doc):
-	year_start_end_dates = frappe.db.sql("""select name, year_start_date, year_end_date from `tabFiscal Year` where name!=%s""", (doc.name))
+	year_start_end_dates = frappe.db.sql(
+		"""select name, year_start_date, year_end_date from `tabFiscal Year` where name!=%s""",
+		(doc.name),
+	)
 	for fiscal_year, ysd, yed in year_start_end_dates:
-		if (getdate(doc.year_start_date) == ysd and getdate(doc.year_end_date) == yed) and (not frappe.flags.in_test):
-					frappe.throw(_("Fiscal Year Start Date and Fiscal Year End Date are already set in Fiscal Year {0}").format(fiscal_year))
+		if (getdate(doc.year_start_date) == ysd and getdate(doc.year_end_date) == yed) and (
+			not frappe.flags.in_test
+		):
+			frappe.throw(
+				_("Fiscal Year Start Date and Fiscal Year End Date are already set in Fiscal Year {0}").format(
+					fiscal_year
+				)
+			)
 
 
 @frappe.whitelist()
 def auto_create_fiscal_year():
-	for d in frappe.db.sql("""select name from `tabFiscal Year` where year_end_date = date_add(current_date, interval 3 day)"""):
+	for d in frappe.db.sql(
+		"""select name from `tabFiscal Year` where year_end_date = date_add(current_date, interval 3 day)"""
+	):
 		try:
 			current_fy = frappe.get_doc("Fiscal Year", d[0])
 
@@ -114,16 +160,14 @@
 
 			start_year = cstr(new_fy.year_start_date.year)
 			end_year = cstr(new_fy.year_end_date.year)
-			new_fy.year = start_year if start_year==end_year else (start_year + "-" + end_year)
+			new_fy.year = start_year if start_year == end_year else (start_year + "-" + end_year)
 			new_fy.auto_created = 1
 
 			new_fy.insert(ignore_permissions=True)
 		except frappe.NameError:
 			pass
 
+
 def get_from_and_to_date(fiscal_year):
-	fields = [
-		"year_start_date as from_date",
-		"year_end_date as to_date"
-	]
+	fields = ["year_start_date as from_date", "year_end_date as to_date"]
 	return frappe.db.get_value("Fiscal Year", fiscal_year, fields, as_dict=1)
diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year_dashboard.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year_dashboard.py
index 892a2c6..bc96691 100644
--- a/erpnext/accounts/doctype/fiscal_year/fiscal_year_dashboard.py
+++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year_dashboard.py
@@ -3,19 +3,13 @@
 
 def get_data():
 	return {
-		'fieldname': 'fiscal_year',
-		'transactions': [
+		"fieldname": "fiscal_year",
+		"transactions": [
+			{"label": _("Budgets"), "items": ["Budget"]},
+			{"label": _("References"), "items": ["Period Closing Voucher"]},
 			{
-				'label': _('Budgets'),
-				'items': ['Budget']
+				"label": _("Target Details"),
+				"items": ["Sales Person", "Sales Partner", "Territory", "Monthly Distribution"],
 			},
-			{
-				'label': _('References'),
-				'items': ['Period Closing Voucher']
-			},
-			{
-				'label': _('Target Details'),
-				'items': ['Sales Person', 'Sales Partner', 'Territory', 'Monthly Distribution']
-			}
-		]
+		],
 	}
diff --git a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
index 69e13a4..6e946f7 100644
--- a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
+++ b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
@@ -11,43 +11,48 @@
 
 test_ignore = ["Company"]
 
-class TestFiscalYear(unittest.TestCase):
 
+class TestFiscalYear(unittest.TestCase):
 	def test_extra_year(self):
 		if frappe.db.exists("Fiscal Year", "_Test Fiscal Year 2000"):
 			frappe.delete_doc("Fiscal Year", "_Test Fiscal Year 2000")
 
-		fy = frappe.get_doc({
-			"doctype": "Fiscal Year",
-			"year": "_Test Fiscal Year 2000",
-			"year_end_date": "2002-12-31",
-			"year_start_date": "2000-04-01"
-		})
+		fy = frappe.get_doc(
+			{
+				"doctype": "Fiscal Year",
+				"year": "_Test Fiscal Year 2000",
+				"year_end_date": "2002-12-31",
+				"year_start_date": "2000-04-01",
+			}
+		)
 
 		self.assertRaises(FiscalYearIncorrectDate, fy.insert)
 
 
 def test_record_generator():
 	test_records = [
-			{
-				"doctype": "Fiscal Year",
-				"year": "_Test Short Fiscal Year 2011",
-				"is_short_year": 1,
-				"year_end_date": "2011-04-01",
-				"year_start_date": "2011-12-31"
-			 }
+		{
+			"doctype": "Fiscal Year",
+			"year": "_Test Short Fiscal Year 2011",
+			"is_short_year": 1,
+			"year_end_date": "2011-04-01",
+			"year_start_date": "2011-12-31",
+		}
 	]
 
 	start = 2012
 	end = now_datetime().year + 5
 	for year in range(start, end):
-		test_records.append({
-			"doctype": "Fiscal Year",
-			"year": f"_Test Fiscal Year {year}",
-			"year_start_date": f"{year}-01-01",
-			"year_end_date": f"{year}-12-31"
-		})
+		test_records.append(
+			{
+				"doctype": "Fiscal Year",
+				"year": f"_Test Fiscal Year {year}",
+				"year_start_date": f"{year}-01-01",
+				"year_end_date": f"{year}-12-31",
+			}
+		)
 
 	return test_records
 
+
 test_records = test_record_generator()
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.js b/erpnext/accounts/doctype/gl_entry/gl_entry.js
index 491cf4d..4d2a513 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.js
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.js
@@ -3,6 +3,6 @@
 
 frappe.ui.form.on('GL Entry', {
 	refresh: function(frm) {
-
+		frm.page.btn_secondary.hide()
 	}
 });
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 9d1452b..9f71656 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -25,6 +25,8 @@
 )
 
 exclude_from_linked_with = True
+
+
 class GLEntry(Document):
 	def autoname(self):
 		"""
@@ -32,6 +34,8 @@
 		name will be changed using autoname options (in a scheduled job)
 		"""
 		self.name = frappe.generate_hash(txt="", length=10)
+		if self.meta.autoname == "hash":
+			self.to_rename = 0
 
 	def validate(self):
 		self.flags.ignore_submit_comment = True
@@ -54,15 +58,23 @@
 			validate_balance_type(self.account, adv_adj)
 			validate_frozen_account(self.account, adv_adj)
 
-			# Update outstanding amt on against voucher
-			if (self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees']
-				and self.against_voucher and self.flags.update_outstanding == 'Yes'
-				and not frappe.flags.is_reverse_depr_entry):
-					update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type,
-						self.against_voucher)
+			if frappe.db.get_value("Account", self.account, "account_type") not in [
+				"Receivable",
+				"Payable",
+			]:
+				# Update outstanding amt on against voucher
+				if (
+					self.against_voucher_type in ["Journal Entry", "Sales Invoice", "Purchase Invoice", "Fees"]
+					and self.against_voucher
+					and self.flags.update_outstanding == "Yes"
+					and not frappe.flags.is_reverse_depr_entry
+				):
+					update_outstanding_amt(
+						self.account, self.party_type, self.party, self.against_voucher_type, self.against_voucher
+					)
 
 	def check_mandatory(self):
-		mandatory = ['account','voucher_type','voucher_no','company']
+		mandatory = ["account", "voucher_type", "voucher_no", "company"]
 		for k in mandatory:
 			if not self.get(k):
 				frappe.throw(_("{0} is required").format(_(self.meta.get_label(k))))
@@ -70,29 +82,40 @@
 		if not (self.party_type and self.party):
 			account_type = frappe.get_cached_value("Account", self.account, "account_type")
 			if account_type == "Receivable":
-				frappe.throw(_("{0} {1}: Customer is required against Receivable account {2}")
-					.format(self.voucher_type, self.voucher_no, self.account))
+				frappe.throw(
+					_("{0} {1}: Customer is required against Receivable account {2}").format(
+						self.voucher_type, self.voucher_no, self.account
+					)
+				)
 			elif account_type == "Payable":
-				frappe.throw(_("{0} {1}: Supplier is required against Payable account {2}")
-					.format(self.voucher_type, self.voucher_no, self.account))
+				frappe.throw(
+					_("{0} {1}: Supplier is required against Payable account {2}").format(
+						self.voucher_type, self.voucher_no, self.account
+					)
+				)
 
 		# Zero value transaction is not allowed
 		if not (flt(self.debit, self.precision("debit")) or flt(self.credit, self.precision("credit"))):
-			frappe.throw(_("{0} {1}: Either debit or credit amount is required for {2}")
-				.format(self.voucher_type, self.voucher_no, self.account))
+			frappe.throw(
+				_("{0} {1}: Either debit or credit amount is required for {2}").format(
+					self.voucher_type, self.voucher_no, self.account
+				)
+			)
 
 	def pl_must_have_cost_center(self):
 		"""Validate that profit and loss type account GL entries have a cost center."""
 
-		if self.cost_center or self.voucher_type == 'Period Closing Voucher':
+		if self.cost_center or self.voucher_type == "Period Closing Voucher":
 			return
 
 		if frappe.get_cached_value("Account", self.account, "report_type") == "Profit and Loss":
 			msg = _("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}.").format(
-				self.voucher_type, self.voucher_no, self.account)
+				self.voucher_type, self.voucher_no, self.account
+			)
 			msg += " "
-			msg += _("Please set the cost center field in {0} or setup a default Cost Center for the Company.").format(
-				self.voucher_type)
+			msg += _(
+				"Please set the cost center field in {0} or setup a default Cost Center for the Company."
+			).format(self.voucher_type)
 
 			frappe.throw(msg, title=_("Missing Cost Center"))
 
@@ -100,17 +123,31 @@
 		account_type = frappe.db.get_value("Account", self.account, "report_type")
 
 		for dimension in get_checks_for_pl_and_bs_accounts():
-			if account_type == "Profit and Loss" \
-				and self.company == dimension.company and dimension.mandatory_for_pl and not dimension.disabled:
+			if (
+				account_type == "Profit and Loss"
+				and self.company == dimension.company
+				and dimension.mandatory_for_pl
+				and not dimension.disabled
+			):
 				if not self.get(dimension.fieldname):
-					frappe.throw(_("Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}.")
-						.format(dimension.label, self.account))
+					frappe.throw(
+						_("Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}.").format(
+							dimension.label, self.account
+						)
+					)
 
-			if account_type == "Balance Sheet" \
-				and self.company == dimension.company and dimension.mandatory_for_bs and not dimension.disabled:
+			if (
+				account_type == "Balance Sheet"
+				and self.company == dimension.company
+				and dimension.mandatory_for_bs
+				and not dimension.disabled
+			):
 				if not self.get(dimension.fieldname):
-					frappe.throw(_("Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}.")
-						.format(dimension.label, self.account))
+					frappe.throw(
+						_("Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}.").format(
+							dimension.label, self.account
+						)
+					)
 
 	def validate_allowed_dimensions(self):
 		dimension_filter_map = get_dimension_filter_map()
@@ -119,56 +156,97 @@
 			account = key[1]
 
 			if self.account == account:
-				if value['is_mandatory'] and not self.get(dimension):
-					frappe.throw(_("{0} is mandatory for account {1}").format(
-						frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)), MandatoryAccountDimensionError)
+				if value["is_mandatory"] and not self.get(dimension):
+					frappe.throw(
+						_("{0} is mandatory for account {1}").format(
+							frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)
+						),
+						MandatoryAccountDimensionError,
+					)
 
-				if value['allow_or_restrict'] == 'Allow':
-					if self.get(dimension) and self.get(dimension) not in value['allowed_dimensions']:
-						frappe.throw(_("Invalid value {0} for {1} against account {2}").format(
-							frappe.bold(self.get(dimension)), frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)), InvalidAccountDimensionError)
+				if value["allow_or_restrict"] == "Allow":
+					if self.get(dimension) and self.get(dimension) not in value["allowed_dimensions"]:
+						frappe.throw(
+							_("Invalid value {0} for {1} against account {2}").format(
+								frappe.bold(self.get(dimension)),
+								frappe.bold(frappe.unscrub(dimension)),
+								frappe.bold(self.account),
+							),
+							InvalidAccountDimensionError,
+						)
 				else:
-					if self.get(dimension) and self.get(dimension) in value['allowed_dimensions']:
-						frappe.throw(_("Invalid value {0} for {1} against account {2}").format(
-							frappe.bold(self.get(dimension)), frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)), InvalidAccountDimensionError)
+					if self.get(dimension) and self.get(dimension) in value["allowed_dimensions"]:
+						frappe.throw(
+							_("Invalid value {0} for {1} against account {2}").format(
+								frappe.bold(self.get(dimension)),
+								frappe.bold(frappe.unscrub(dimension)),
+								frappe.bold(self.account),
+							),
+							InvalidAccountDimensionError,
+						)
 
 	def check_pl_account(self):
-		if self.is_opening=='Yes' and \
-				frappe.db.get_value("Account", self.account, "report_type")=="Profit and Loss":
-			frappe.throw(_("{0} {1}: 'Profit and Loss' type account {2} not allowed in Opening Entry")
-				.format(self.voucher_type, self.voucher_no, self.account))
+		if (
+			self.is_opening == "Yes"
+			and frappe.db.get_value("Account", self.account, "report_type") == "Profit and Loss"
+			and not self.is_cancelled
+		):
+			frappe.throw(
+				_("{0} {1}: 'Profit and Loss' type account {2} not allowed in Opening Entry").format(
+					self.voucher_type, self.voucher_no, self.account
+				)
+			)
 
 	def validate_account_details(self, adv_adj):
 		"""Account must be ledger, active and not freezed"""
 
-		ret = frappe.db.sql("""select is_group, docstatus, company
-			from tabAccount where name=%s""", self.account, as_dict=1)[0]
+		ret = frappe.db.sql(
+			"""select is_group, docstatus, company
+			from tabAccount where name=%s""",
+			self.account,
+			as_dict=1,
+		)[0]
 
-		if ret.is_group==1:
-			frappe.throw(_('''{0} {1}: Account {2} is a Group Account and group accounts cannot be used in transactions''')
-				.format(self.voucher_type, self.voucher_no, self.account))
+		if ret.is_group == 1:
+			frappe.throw(
+				_(
+					"""{0} {1}: Account {2} is a Group Account and group accounts cannot be used in transactions"""
+				).format(self.voucher_type, self.voucher_no, self.account)
+			)
 
-		if ret.docstatus==2:
-			frappe.throw(_("{0} {1}: Account {2} is inactive")
-				.format(self.voucher_type, self.voucher_no, self.account))
+		if ret.docstatus == 2:
+			frappe.throw(
+				_("{0} {1}: Account {2} is inactive").format(self.voucher_type, self.voucher_no, self.account)
+			)
 
 		if ret.company != self.company:
-			frappe.throw(_("{0} {1}: Account {2} does not belong to Company {3}")
-				.format(self.voucher_type, self.voucher_no, self.account, self.company))
+			frappe.throw(
+				_("{0} {1}: Account {2} does not belong to Company {3}").format(
+					self.voucher_type, self.voucher_no, self.account, self.company
+				)
+			)
 
 	def validate_cost_center(self):
-		if not self.cost_center: return
+		if not self.cost_center:
+			return
 
-		is_group, company = frappe.get_cached_value('Cost Center',
-			self.cost_center, ['is_group', 'company'])
+		is_group, company = frappe.get_cached_value(
+			"Cost Center", self.cost_center, ["is_group", "company"]
+		)
 
 		if company != self.company:
-			frappe.throw(_("{0} {1}: Cost Center {2} does not belong to Company {3}")
-				.format(self.voucher_type, self.voucher_no, self.cost_center, self.company))
+			frappe.throw(
+				_("{0} {1}: Cost Center {2} does not belong to Company {3}").format(
+					self.voucher_type, self.voucher_no, self.cost_center, self.company
+				)
+			)
 
-		if (self.voucher_type != 'Period Closing Voucher' and is_group):
-			frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions""").format(
-				self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
+		if self.voucher_type != "Period Closing Voucher" and is_group:
+			frappe.throw(
+				_(
+					"""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions"""
+				).format(self.voucher_type, self.voucher_no, frappe.bold(self.cost_center))
+			)
 
 	def validate_party(self):
 		validate_party_frozen_disabled(self.party_type, self.party)
@@ -181,9 +259,12 @@
 			self.account_currency = account_currency or company_currency
 
 		if account_currency != self.account_currency:
-			frappe.throw(_("{0} {1}: Accounting Entry for {2} can only be made in currency: {3}")
-				.format(self.voucher_type, self.voucher_no, self.account,
-				(account_currency or company_currency)), InvalidAccountCurrency)
+			frappe.throw(
+				_("{0} {1}: Accounting Entry for {2} can only be made in currency: {3}").format(
+					self.voucher_type, self.voucher_no, self.account, (account_currency or company_currency)
+				),
+				InvalidAccountCurrency,
+			)
 
 		if self.party_type and self.party:
 			validate_party_gle_currency(self.party_type, self.party, self.company, self.account_currency)
@@ -192,51 +273,85 @@
 		if not self.fiscal_year:
 			self.fiscal_year = get_fiscal_year(self.posting_date, company=self.company)[0]
 
+	def on_cancel(self):
+		msg = _("Individual GL Entry cannot be cancelled.")
+		msg += "<br>" + _("Please cancel related transaction.")
+		frappe.throw(msg)
+
+
 def validate_balance_type(account, adv_adj=False):
 	if not adv_adj and account:
 		balance_must_be = frappe.db.get_value("Account", account, "balance_must_be")
 		if balance_must_be:
-			balance = frappe.db.sql("""select sum(debit) - sum(credit)
-				from `tabGL Entry` where account = %s""", account)[0][0]
+			balance = frappe.db.sql(
+				"""select sum(debit) - sum(credit)
+				from `tabGL Entry` where account = %s""",
+				account,
+			)[0][0]
 
-			if (balance_must_be=="Debit" and flt(balance) < 0) or \
-				(balance_must_be=="Credit" and flt(balance) > 0):
-				frappe.throw(_("Balance for Account {0} must always be {1}").format(account, _(balance_must_be)))
+			if (balance_must_be == "Debit" and flt(balance) < 0) or (
+				balance_must_be == "Credit" and flt(balance) > 0
+			):
+				frappe.throw(
+					_("Balance for Account {0} must always be {1}").format(account, _(balance_must_be))
+				)
 
-def update_outstanding_amt(account, party_type, party, against_voucher_type, against_voucher, on_cancel=False):
+
+def update_outstanding_amt(
+	account, party_type, party, against_voucher_type, against_voucher, on_cancel=False
+):
 	if party_type and party:
-		party_condition = " and party_type={0} and party={1}"\
-			.format(frappe.db.escape(party_type), frappe.db.escape(party))
+		party_condition = " and party_type={0} and party={1}".format(
+			frappe.db.escape(party_type), frappe.db.escape(party)
+		)
 	else:
 		party_condition = ""
 
 	if against_voucher_type == "Sales Invoice":
 		party_account = frappe.db.get_value(against_voucher_type, against_voucher, "debit_to")
-		account_condition = "and account in ({0}, {1})".format(frappe.db.escape(account), frappe.db.escape(party_account))
+		account_condition = "and account in ({0}, {1})".format(
+			frappe.db.escape(account), frappe.db.escape(party_account)
+		)
 	else:
 		account_condition = " and account = {0}".format(frappe.db.escape(account))
 
 	# get final outstanding amt
-	bal = flt(frappe.db.sql("""
+	bal = flt(
+		frappe.db.sql(
+			"""
 		select sum(debit_in_account_currency) - sum(credit_in_account_currency)
 		from `tabGL Entry`
 		where against_voucher_type=%s and against_voucher=%s
 		and voucher_type != 'Invoice Discounting'
-		{0} {1}""".format(party_condition, account_condition),
-		(against_voucher_type, against_voucher))[0][0] or 0.0)
+		{0} {1}""".format(
+				party_condition, account_condition
+			),
+			(against_voucher_type, against_voucher),
+		)[0][0]
+		or 0.0
+	)
 
-	if against_voucher_type == 'Purchase Invoice':
+	if against_voucher_type == "Purchase Invoice":
 		bal = -bal
 	elif against_voucher_type == "Journal Entry":
-		against_voucher_amount = flt(frappe.db.sql("""
+		against_voucher_amount = flt(
+			frappe.db.sql(
+				"""
 			select sum(debit_in_account_currency) - sum(credit_in_account_currency)
 			from `tabGL Entry` where voucher_type = 'Journal Entry' and voucher_no = %s
-			and account = %s and (against_voucher is null or against_voucher='') {0}"""
-			.format(party_condition), (against_voucher, account))[0][0])
+			and account = %s and (against_voucher is null or against_voucher='') {0}""".format(
+					party_condition
+				),
+				(against_voucher, account),
+			)[0][0]
+		)
 
 		if not against_voucher_amount:
-			frappe.throw(_("Against Journal Entry {0} is already adjusted against some other voucher")
-				.format(against_voucher))
+			frappe.throw(
+				_("Against Journal Entry {0} is already adjusted against some other voucher").format(
+					against_voucher
+				)
+			)
 
 		bal = against_voucher_amount + bal
 		if against_voucher_amount < 0:
@@ -244,44 +359,51 @@
 
 		# Validation : Outstanding can not be negative for JV
 		if bal < 0 and not on_cancel:
-			frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal)))
+			frappe.throw(
+				_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal))
+			)
 
 	if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]:
 		ref_doc = frappe.get_doc(against_voucher_type, against_voucher)
 
 		# Didn't use db_set for optimisation purpose
 		ref_doc.outstanding_amount = bal
-		frappe.db.set_value(against_voucher_type, against_voucher, 'outstanding_amount', bal)
+		frappe.db.set_value(against_voucher_type, against_voucher, "outstanding_amount", bal)
 
 		ref_doc.set_status(update=True)
 
 
 def validate_frozen_account(account, adv_adj=None):
 	frozen_account = frappe.get_cached_value("Account", account, "freeze_account")
-	if frozen_account == 'Yes' and not adv_adj:
-		frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,
-			'frozen_accounts_modifier')
+	if frozen_account == "Yes" and not adv_adj:
+		frozen_accounts_modifier = frappe.db.get_value(
+			"Accounts Settings", None, "frozen_accounts_modifier"
+		)
 
 		if not frozen_accounts_modifier:
 			frappe.throw(_("Account {0} is frozen").format(account))
 		elif frozen_accounts_modifier not in frappe.get_roles():
 			frappe.throw(_("Not authorized to edit frozen Account {0}").format(account))
 
+
 def update_against_account(voucher_type, voucher_no):
-	entries = frappe.db.get_all("GL Entry",
+	entries = frappe.db.get_all(
+		"GL Entry",
 		filters={"voucher_type": voucher_type, "voucher_no": voucher_no},
-		fields=["name", "party", "against", "debit", "credit", "account", "company"])
+		fields=["name", "party", "against", "debit", "credit", "account", "company"],
+	)
 
 	if not entries:
 		return
 	company_currency = erpnext.get_company_currency(entries[0].company)
-	precision = get_field_precision(frappe.get_meta("GL Entry")
-			.get_field("debit"), company_currency)
+	precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
 
 	accounts_debited, accounts_credited = [], []
 	for d in entries:
-		if flt(d.debit, precision) > 0: accounts_debited.append(d.party or d.account)
-		if flt(d.credit, precision) > 0: accounts_credited.append(d.party or d.account)
+		if flt(d.debit, precision) > 0:
+			accounts_debited.append(d.party or d.account)
+		if flt(d.credit, precision) > 0:
+			accounts_credited.append(d.party or d.account)
 
 	for d in entries:
 		if flt(d.debit, precision) > 0:
@@ -292,14 +414,17 @@
 		if d.against != new_against:
 			frappe.db.set_value("GL Entry", d.name, "against", new_against)
 
+
 def on_doctype_update():
 	frappe.db.add_index("GL Entry", ["against_voucher_type", "against_voucher"])
 	frappe.db.add_index("GL Entry", ["voucher_type", "voucher_no"])
 
+
 def rename_gle_sle_docs():
 	for doctype in ["GL Entry", "Stock Ledger Entry"]:
 		rename_temporarily_named_docs(doctype)
 
+
 def rename_temporarily_named_docs(doctype):
 	"""Rename temporarily named docs using autoname options"""
 	docs_to_rename = frappe.get_all(doctype, {"to_rename": "1"}, order_by="creation", limit=50000)
@@ -310,5 +435,5 @@
 		frappe.db.sql(
 			"UPDATE `tab{}` SET name = %s, to_rename = 0 where name = %s".format(doctype),
 			(newname, oldname),
-			auto_commit=True
+			auto_commit=True,
 		)
diff --git a/erpnext/accounts/doctype/gl_entry/test_gl_entry.py b/erpnext/accounts/doctype/gl_entry/test_gl_entry.py
index 3de2394..b188b09 100644
--- a/erpnext/accounts/doctype/gl_entry/test_gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/test_gl_entry.py
@@ -14,48 +14,68 @@
 class TestGLEntry(unittest.TestCase):
 	def test_round_off_entry(self):
 		frappe.db.set_value("Company", "_Test Company", "round_off_account", "_Test Write Off - _TC")
-		frappe.db.set_value("Company", "_Test Company", "round_off_cost_center", "_Test Cost Center - _TC")
+		frappe.db.set_value(
+			"Company", "_Test Company", "round_off_cost_center", "_Test Cost Center - _TC"
+		)
 
-		jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
-			"_Test Bank - _TC", 100, "_Test Cost Center - _TC", submit=False)
+		jv = make_journal_entry(
+			"_Test Account Cost for Goods Sold - _TC",
+			"_Test Bank - _TC",
+			100,
+			"_Test Cost Center - _TC",
+			submit=False,
+		)
 
 		jv.get("accounts")[0].debit = 100.01
 		jv.flags.ignore_validate = True
 		jv.submit()
 
-		round_off_entry = frappe.db.sql("""select name from `tabGL Entry`
+		round_off_entry = frappe.db.sql(
+			"""select name from `tabGL Entry`
 			where voucher_type='Journal Entry' and voucher_no = %s
 			and account='_Test Write Off - _TC' and cost_center='_Test Cost Center - _TC'
-			and debit = 0 and credit = '.01'""", jv.name)
+			and debit = 0 and credit = '.01'""",
+			jv.name,
+		)
 
 		self.assertTrue(round_off_entry)
 
 	def test_rename_entries(self):
-		je = make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 100, submit=True)
+		je = make_journal_entry(
+			"_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 100, submit=True
+		)
 		rename_gle_sle_docs()
 		naming_series = parse_naming_series(parts=frappe.get_meta("GL Entry").autoname.split(".")[:-1])
 
-		je = make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 100, submit=True)
+		je = make_journal_entry(
+			"_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 100, submit=True
+		)
 
-		gl_entries = frappe.get_all("GL Entry",
+		gl_entries = frappe.get_all(
+			"GL Entry",
 			fields=["name", "to_rename"],
 			filters={"voucher_type": "Journal Entry", "voucher_no": je.name},
-			order_by="creation"
+			order_by="creation",
 		)
 
 		self.assertTrue(all(entry.to_rename == 1 for entry in gl_entries))
-		old_naming_series_current_value = frappe.db.sql("SELECT current from tabSeries where name = %s", naming_series)[0][0]
+		old_naming_series_current_value = frappe.db.sql(
+			"SELECT current from tabSeries where name = %s", naming_series
+		)[0][0]
 
 		rename_gle_sle_docs()
 
-		new_gl_entries = frappe.get_all("GL Entry",
+		new_gl_entries = frappe.get_all(
+			"GL Entry",
 			fields=["name", "to_rename"],
 			filters={"voucher_type": "Journal Entry", "voucher_no": je.name},
-			order_by="creation"
+			order_by="creation",
 		)
 		self.assertTrue(all(entry.to_rename == 0 for entry in new_gl_entries))
 
 		self.assertTrue(all(new.name != old.name for new, old in zip(gl_entries, new_gl_entries)))
 
-		new_naming_series_current_value = frappe.db.sql("SELECT current from tabSeries where name = %s", naming_series)[0][0]
+		new_naming_series_current_value = frappe.db.sql(
+			"SELECT current from tabSeries where name = %s", naming_series
+		)[0][0]
 		self.assertEqual(old_naming_series_current_value + 2, new_naming_series_current_value)
diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
index 09c389d..5bd4585 100644
--- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
@@ -33,19 +33,32 @@
 			frappe.throw(_("Loan Start Date and Loan Period are mandatory to save the Invoice Discounting"))
 
 	def validate_invoices(self):
-		discounted_invoices = [record.sales_invoice for record in
-			frappe.get_all("Discounted Invoice",fields=["sales_invoice"], filters={"docstatus":1})]
+		discounted_invoices = [
+			record.sales_invoice
+			for record in frappe.get_all(
+				"Discounted Invoice", fields=["sales_invoice"], filters={"docstatus": 1}
+			)
+		]
 
 		for record in self.invoices:
 			if record.sales_invoice in discounted_invoices:
-				frappe.throw(_("Row({0}): {1} is already discounted in {2}")
-					.format(record.idx, frappe.bold(record.sales_invoice), frappe.bold(record.parent)))
+				frappe.throw(
+					_("Row({0}): {1} is already discounted in {2}").format(
+						record.idx, frappe.bold(record.sales_invoice), frappe.bold(record.parent)
+					)
+				)
 
-			actual_outstanding = frappe.db.get_value("Sales Invoice", record.sales_invoice,"outstanding_amount")
-			if record.outstanding_amount > actual_outstanding :
-				frappe.throw(_
-					("Row({0}): Outstanding Amount cannot be greater than actual Outstanding Amount {1} in {2}").format(
-					record.idx, frappe.bold(actual_outstanding), frappe.bold(record.sales_invoice)))
+			actual_outstanding = frappe.db.get_value(
+				"Sales Invoice", record.sales_invoice, "outstanding_amount"
+			)
+			if record.outstanding_amount > actual_outstanding:
+				frappe.throw(
+					_(
+						"Row({0}): Outstanding Amount cannot be greater than actual Outstanding Amount {1} in {2}"
+					).format(
+						record.idx, frappe.bold(actual_outstanding), frappe.bold(record.sales_invoice)
+					)
+				)
 
 	def calculate_total_amount(self):
 		self.total_amount = sum(flt(d.outstanding_amount) for d in self.invoices)
@@ -73,24 +86,21 @@
 				self.status = "Cancelled"
 
 		if cancel:
-			self.db_set('status', self.status, update_modified = True)
+			self.db_set("status", self.status, update_modified=True)
 
 	def update_sales_invoice(self):
 		for d in self.invoices:
 			if self.docstatus == 1:
 				is_discounted = 1
 			else:
-				discounted_invoice = frappe.db.exists({
-					"doctype": "Discounted Invoice",
-					"sales_invoice": d.sales_invoice,
-					"docstatus": 1
-				})
+				discounted_invoice = frappe.db.exists(
+					{"doctype": "Discounted Invoice", "sales_invoice": d.sales_invoice, "docstatus": 1}
+				)
 				is_discounted = 1 if discounted_invoice else 0
 			frappe.db.set_value("Sales Invoice", d.sales_invoice, "is_discounted", is_discounted)
 
 	def make_gl_entries(self):
-		company_currency = frappe.get_cached_value('Company',  self.company, "default_currency")
-
+		company_currency = frappe.get_cached_value("Company", self.company, "default_currency")
 
 		gl_entries = []
 		invoice_fields = ["debit_to", "party_account_currency", "conversion_rate", "cost_center"]
@@ -102,135 +112,182 @@
 			inv = frappe.db.get_value("Sales Invoice", d.sales_invoice, invoice_fields, as_dict=1)
 
 			if d.outstanding_amount:
-				outstanding_in_company_currency = flt(d.outstanding_amount * inv.conversion_rate,
-					d.precision("outstanding_amount"))
-				ar_credit_account_currency = frappe.get_cached_value("Account", self.accounts_receivable_credit, "currency")
+				outstanding_in_company_currency = flt(
+					d.outstanding_amount * inv.conversion_rate, d.precision("outstanding_amount")
+				)
+				ar_credit_account_currency = frappe.get_cached_value(
+					"Account", self.accounts_receivable_credit, "currency"
+				)
 
-				gl_entries.append(self.get_gl_dict({
-					"account": inv.debit_to,
-					"party_type": "Customer",
-					"party": d.customer,
-					"against": self.accounts_receivable_credit,
-					"credit": outstanding_in_company_currency,
-					"credit_in_account_currency": outstanding_in_company_currency \
-						if inv.party_account_currency==company_currency else d.outstanding_amount,
-					"cost_center": inv.cost_center,
-					"against_voucher": d.sales_invoice,
-					"against_voucher_type": "Sales Invoice"
-				}, inv.party_account_currency, item=inv))
+				gl_entries.append(
+					self.get_gl_dict(
+						{
+							"account": inv.debit_to,
+							"party_type": "Customer",
+							"party": d.customer,
+							"against": self.accounts_receivable_credit,
+							"credit": outstanding_in_company_currency,
+							"credit_in_account_currency": outstanding_in_company_currency
+							if inv.party_account_currency == company_currency
+							else d.outstanding_amount,
+							"cost_center": inv.cost_center,
+							"against_voucher": d.sales_invoice,
+							"against_voucher_type": "Sales Invoice",
+						},
+						inv.party_account_currency,
+						item=inv,
+					)
+				)
 
-				gl_entries.append(self.get_gl_dict({
-					"account": self.accounts_receivable_credit,
-					"party_type": "Customer",
-					"party": d.customer,
-					"against": inv.debit_to,
-					"debit": outstanding_in_company_currency,
-					"debit_in_account_currency": outstanding_in_company_currency \
-						if ar_credit_account_currency==company_currency else d.outstanding_amount,
-					"cost_center": inv.cost_center,
-					"against_voucher": d.sales_invoice,
-					"against_voucher_type": "Sales Invoice"
-				}, ar_credit_account_currency, item=inv))
+				gl_entries.append(
+					self.get_gl_dict(
+						{
+							"account": self.accounts_receivable_credit,
+							"party_type": "Customer",
+							"party": d.customer,
+							"against": inv.debit_to,
+							"debit": outstanding_in_company_currency,
+							"debit_in_account_currency": outstanding_in_company_currency
+							if ar_credit_account_currency == company_currency
+							else d.outstanding_amount,
+							"cost_center": inv.cost_center,
+							"against_voucher": d.sales_invoice,
+							"against_voucher_type": "Sales Invoice",
+						},
+						ar_credit_account_currency,
+						item=inv,
+					)
+				)
 
-		make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No')
+		make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding="No")
 
 	@frappe.whitelist()
 	def create_disbursement_entry(self):
 		je = frappe.new_doc("Journal Entry")
-		je.voucher_type = 'Journal Entry'
+		je.voucher_type = "Journal Entry"
 		je.company = self.company
-		je.remark = 'Loan Disbursement entry against Invoice Discounting: ' + self.name
+		je.remark = "Loan Disbursement entry against Invoice Discounting: " + self.name
 
-		je.append("accounts", {
-			"account": self.bank_account,
-			"debit_in_account_currency": flt(self.total_amount) - flt(self.bank_charges),
-			"cost_center": erpnext.get_default_cost_center(self.company)
-		})
+		je.append(
+			"accounts",
+			{
+				"account": self.bank_account,
+				"debit_in_account_currency": flt(self.total_amount) - flt(self.bank_charges),
+				"cost_center": erpnext.get_default_cost_center(self.company),
+			},
+		)
 
 		if self.bank_charges:
-			je.append("accounts", {
-				"account": self.bank_charges_account,
-				"debit_in_account_currency": flt(self.bank_charges),
-				"cost_center": erpnext.get_default_cost_center(self.company)
-			})
+			je.append(
+				"accounts",
+				{
+					"account": self.bank_charges_account,
+					"debit_in_account_currency": flt(self.bank_charges),
+					"cost_center": erpnext.get_default_cost_center(self.company),
+				},
+			)
 
-		je.append("accounts", {
-			"account": self.short_term_loan,
-			"credit_in_account_currency": flt(self.total_amount),
-			"cost_center": erpnext.get_default_cost_center(self.company),
-			"reference_type": "Invoice Discounting",
-			"reference_name": self.name
-		})
+		je.append(
+			"accounts",
+			{
+				"account": self.short_term_loan,
+				"credit_in_account_currency": flt(self.total_amount),
+				"cost_center": erpnext.get_default_cost_center(self.company),
+				"reference_type": "Invoice Discounting",
+				"reference_name": self.name,
+			},
+		)
 		for d in self.invoices:
-			je.append("accounts", {
-				"account": self.accounts_receivable_discounted,
-				"debit_in_account_currency": flt(d.outstanding_amount),
-				"cost_center": erpnext.get_default_cost_center(self.company),
-				"reference_type": "Invoice Discounting",
-				"reference_name": self.name,
-				"party_type": "Customer",
-				"party": d.customer
-			})
+			je.append(
+				"accounts",
+				{
+					"account": self.accounts_receivable_discounted,
+					"debit_in_account_currency": flt(d.outstanding_amount),
+					"cost_center": erpnext.get_default_cost_center(self.company),
+					"reference_type": "Invoice Discounting",
+					"reference_name": self.name,
+					"party_type": "Customer",
+					"party": d.customer,
+				},
+			)
 
-			je.append("accounts", {
-				"account": self.accounts_receivable_credit,
-				"credit_in_account_currency": flt(d.outstanding_amount),
-				"cost_center": erpnext.get_default_cost_center(self.company),
-				"reference_type": "Invoice Discounting",
-				"reference_name": self.name,
-				"party_type": "Customer",
-				"party": d.customer
-			})
+			je.append(
+				"accounts",
+				{
+					"account": self.accounts_receivable_credit,
+					"credit_in_account_currency": flt(d.outstanding_amount),
+					"cost_center": erpnext.get_default_cost_center(self.company),
+					"reference_type": "Invoice Discounting",
+					"reference_name": self.name,
+					"party_type": "Customer",
+					"party": d.customer,
+				},
+			)
 
 		return je
 
 	@frappe.whitelist()
 	def close_loan(self):
 		je = frappe.new_doc("Journal Entry")
-		je.voucher_type = 'Journal Entry'
+		je.voucher_type = "Journal Entry"
 		je.company = self.company
-		je.remark = 'Loan Settlement entry against Invoice Discounting: ' + self.name
+		je.remark = "Loan Settlement entry against Invoice Discounting: " + self.name
 
-		je.append("accounts", {
-			"account": self.short_term_loan,
-			"debit_in_account_currency": flt(self.total_amount),
-			"cost_center": erpnext.get_default_cost_center(self.company),
-			"reference_type": "Invoice Discounting",
-			"reference_name": self.name,
-		})
+		je.append(
+			"accounts",
+			{
+				"account": self.short_term_loan,
+				"debit_in_account_currency": flt(self.total_amount),
+				"cost_center": erpnext.get_default_cost_center(self.company),
+				"reference_type": "Invoice Discounting",
+				"reference_name": self.name,
+			},
+		)
 
-		je.append("accounts", {
-			"account": self.bank_account,
-			"credit_in_account_currency": flt(self.total_amount),
-			"cost_center": erpnext.get_default_cost_center(self.company)
-		})
+		je.append(
+			"accounts",
+			{
+				"account": self.bank_account,
+				"credit_in_account_currency": flt(self.total_amount),
+				"cost_center": erpnext.get_default_cost_center(self.company),
+			},
+		)
 
 		if getdate(self.loan_end_date) > getdate(nowdate()):
 			for d in self.invoices:
-				outstanding_amount = frappe.db.get_value("Sales Invoice", d.sales_invoice, "outstanding_amount")
+				outstanding_amount = frappe.db.get_value(
+					"Sales Invoice", d.sales_invoice, "outstanding_amount"
+				)
 				if flt(outstanding_amount) > 0:
-					je.append("accounts", {
-						"account": self.accounts_receivable_discounted,
-						"credit_in_account_currency": flt(outstanding_amount),
-						"cost_center": erpnext.get_default_cost_center(self.company),
-						"reference_type": "Invoice Discounting",
-						"reference_name": self.name,
-						"party_type": "Customer",
-						"party": d.customer
-					})
+					je.append(
+						"accounts",
+						{
+							"account": self.accounts_receivable_discounted,
+							"credit_in_account_currency": flt(outstanding_amount),
+							"cost_center": erpnext.get_default_cost_center(self.company),
+							"reference_type": "Invoice Discounting",
+							"reference_name": self.name,
+							"party_type": "Customer",
+							"party": d.customer,
+						},
+					)
 
-					je.append("accounts", {
-						"account": self.accounts_receivable_unpaid,
-						"debit_in_account_currency": flt(outstanding_amount),
-						"cost_center": erpnext.get_default_cost_center(self.company),
-						"reference_type": "Invoice Discounting",
-						"reference_name": self.name,
-						"party_type": "Customer",
-						"party": d.customer
-					})
+					je.append(
+						"accounts",
+						{
+							"account": self.accounts_receivable_unpaid,
+							"debit_in_account_currency": flt(outstanding_amount),
+							"cost_center": erpnext.get_default_cost_center(self.company),
+							"reference_type": "Invoice Discounting",
+							"reference_name": self.name,
+							"party_type": "Customer",
+							"party": d.customer,
+						},
+					)
 
 		return je
 
+
 @frappe.whitelist()
 def get_invoices(filters):
 	filters = frappe._dict(json.loads(filters))
@@ -250,7 +307,8 @@
 	if cond:
 		where_condition += " and " + " and ".join(cond)
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select
 			name as sales_invoice,
 			customer,
@@ -264,17 +322,26 @@
 			%s
 			and not exists(select di.name from `tabDiscounted Invoice` di
 				where di.docstatus=1 and di.sales_invoice=si.name)
-	""" % where_condition, filters, as_dict=1)
+	"""
+		% where_condition,
+		filters,
+		as_dict=1,
+	)
+
 
 def get_party_account_based_on_invoice_discounting(sales_invoice):
 	party_account = None
-	invoice_discounting = frappe.db.sql("""
+	invoice_discounting = frappe.db.sql(
+		"""
 		select par.accounts_receivable_discounted, par.accounts_receivable_unpaid, par.status
 		from `tabInvoice Discounting` par, `tabDiscounted Invoice` ch
 		where par.name=ch.parent
 			and par.docstatus=1
 			and ch.sales_invoice = %s
-	""", (sales_invoice), as_dict=1)
+	""",
+		(sales_invoice),
+		as_dict=1,
+	)
 	if invoice_discounting:
 		if invoice_discounting[0].status == "Disbursed":
 			party_account = invoice_discounting[0].accounts_receivable_discounted
diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py
index b748429..a442231 100644
--- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py
@@ -3,18 +3,10 @@
 
 def get_data():
 	return {
-		'fieldname': 'reference_name',
-		'internal_links': {
-			'Sales Invoice': ['invoices', 'sales_invoice']
-		},
-		'transactions': [
-			{
-				'label': _('Reference'),
-				'items': ['Sales Invoice']
-			},
-			{
-				'label': _('Payment'),
-				'items': ['Payment Entry', 'Journal Entry']
-			}
-		]
+		"fieldname": "reference_name",
+		"internal_links": {"Sales Invoice": ["invoices", "sales_invoice"]},
+		"transactions": [
+			{"label": _("Reference"), "items": ["Sales Invoice"]},
+			{"label": _("Payment"), "items": ["Payment Entry", "Journal Entry"]},
+		],
 	}
diff --git a/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py
index d1d4be3..a85fdfc 100644
--- a/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py
+++ b/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py
@@ -14,52 +14,74 @@
 
 class TestInvoiceDiscounting(unittest.TestCase):
 	def setUp(self):
-		self.ar_credit = create_account(account_name="_Test Accounts Receivable Credit", parent_account = "Accounts Receivable - _TC", company="_Test Company")
-		self.ar_discounted = create_account(account_name="_Test Accounts Receivable Discounted", parent_account = "Accounts Receivable - _TC", company="_Test Company")
-		self.ar_unpaid = create_account(account_name="_Test Accounts Receivable Unpaid", parent_account = "Accounts Receivable - _TC", company="_Test Company")
-		self.short_term_loan = create_account(account_name="_Test Short Term Loan", parent_account = "Source of Funds (Liabilities) - _TC", company="_Test Company")
-		self.bank_account = create_account(account_name="_Test Bank 2", parent_account = "Bank Accounts - _TC", company="_Test Company")
-		self.bank_charges_account = create_account(account_name="_Test Bank Charges Account", parent_account = "Expenses - _TC", company="_Test Company")
+		self.ar_credit = create_account(
+			account_name="_Test Accounts Receivable Credit",
+			parent_account="Accounts Receivable - _TC",
+			company="_Test Company",
+		)
+		self.ar_discounted = create_account(
+			account_name="_Test Accounts Receivable Discounted",
+			parent_account="Accounts Receivable - _TC",
+			company="_Test Company",
+		)
+		self.ar_unpaid = create_account(
+			account_name="_Test Accounts Receivable Unpaid",
+			parent_account="Accounts Receivable - _TC",
+			company="_Test Company",
+		)
+		self.short_term_loan = create_account(
+			account_name="_Test Short Term Loan",
+			parent_account="Source of Funds (Liabilities) - _TC",
+			company="_Test Company",
+		)
+		self.bank_account = create_account(
+			account_name="_Test Bank 2", parent_account="Bank Accounts - _TC", company="_Test Company"
+		)
+		self.bank_charges_account = create_account(
+			account_name="_Test Bank Charges Account",
+			parent_account="Expenses - _TC",
+			company="_Test Company",
+		)
 		frappe.db.set_value("Company", "_Test Company", "default_bank_account", self.bank_account)
 
 	def test_total_amount(self):
 		inv1 = create_sales_invoice(rate=200)
 		inv2 = create_sales_invoice(rate=500)
 
-		inv_disc = create_invoice_discounting([inv1.name, inv2.name],
+		inv_disc = create_invoice_discounting(
+			[inv1.name, inv2.name],
 			do_not_submit=True,
 			accounts_receivable_credit=self.ar_credit,
 			accounts_receivable_discounted=self.ar_discounted,
 			accounts_receivable_unpaid=self.ar_unpaid,
 			short_term_loan=self.short_term_loan,
 			bank_charges_account=self.bank_charges_account,
-			bank_account=self.bank_account
-			)
+			bank_account=self.bank_account,
+		)
 		self.assertEqual(inv_disc.total_amount, 700)
 
 	def test_gl_entries_in_base_currency(self):
 		inv = create_sales_invoice(rate=200)
-		inv_disc = create_invoice_discounting([inv.name],
+		inv_disc = create_invoice_discounting(
+			[inv.name],
 			accounts_receivable_credit=self.ar_credit,
 			accounts_receivable_discounted=self.ar_discounted,
 			accounts_receivable_unpaid=self.ar_unpaid,
 			short_term_loan=self.short_term_loan,
 			bank_charges_account=self.bank_charges_account,
-			bank_account=self.bank_account
-			)
+			bank_account=self.bank_account,
+		)
 
 		gle = get_gl_entries("Invoice Discounting", inv_disc.name)
 
-		expected_gle = {
-			inv.debit_to: [0.0, 200],
-			self.ar_credit: [200, 0.0]
-		}
+		expected_gle = {inv.debit_to: [0.0, 200], self.ar_credit: [200, 0.0]}
 		for i, gle in enumerate(gle):
 			self.assertEqual([gle.debit, gle.credit], expected_gle.get(gle.account))
 
 	def test_loan_on_submit(self):
 		inv = create_sales_invoice(rate=300)
-		inv_disc = create_invoice_discounting([inv.name],
+		inv_disc = create_invoice_discounting(
+			[inv.name],
 			accounts_receivable_credit=self.ar_credit,
 			accounts_receivable_discounted=self.ar_discounted,
 			accounts_receivable_unpaid=self.ar_unpaid,
@@ -67,28 +89,33 @@
 			bank_charges_account=self.bank_charges_account,
 			bank_account=self.bank_account,
 			start=nowdate(),
-			period=60
-			)
+			period=60,
+		)
 		self.assertEqual(inv_disc.status, "Sanctioned")
-		self.assertEqual(inv_disc.loan_end_date, add_days(inv_disc.loan_start_date, inv_disc.loan_period))
-
+		self.assertEqual(
+			inv_disc.loan_end_date, add_days(inv_disc.loan_start_date, inv_disc.loan_period)
+		)
 
 	def test_on_disbursed(self):
 		inv = create_sales_invoice(rate=500)
-		inv_disc = create_invoice_discounting([inv.name],
+		inv_disc = create_invoice_discounting(
+			[inv.name],
 			accounts_receivable_credit=self.ar_credit,
 			accounts_receivable_discounted=self.ar_discounted,
 			accounts_receivable_unpaid=self.ar_unpaid,
 			short_term_loan=self.short_term_loan,
 			bank_charges_account=self.bank_charges_account,
 			bank_account=self.bank_account,
-			bank_charges=100
-			)
+			bank_charges=100,
+		)
 
 		je = inv_disc.create_disbursement_entry()
 
 		self.assertEqual(je.accounts[0].account, self.bank_account)
-		self.assertEqual(je.accounts[0].debit_in_account_currency, flt(inv_disc.total_amount) - flt(inv_disc.bank_charges))
+		self.assertEqual(
+			je.accounts[0].debit_in_account_currency,
+			flt(inv_disc.total_amount) - flt(inv_disc.bank_charges),
+		)
 
 		self.assertEqual(je.accounts[1].account, self.bank_charges_account)
 		self.assertEqual(je.accounts[1].debit_in_account_currency, flt(inv_disc.bank_charges))
@@ -102,7 +129,6 @@
 		self.assertEqual(je.accounts[4].account, self.ar_credit)
 		self.assertEqual(je.accounts[4].credit_in_account_currency, flt(inv.outstanding_amount))
 
-
 		je.posting_date = nowdate()
 		je.submit()
 
@@ -114,7 +140,8 @@
 
 	def test_on_close_after_loan_period(self):
 		inv = create_sales_invoice(rate=600)
-		inv_disc = create_invoice_discounting([inv.name],
+		inv_disc = create_invoice_discounting(
+			[inv.name],
 			accounts_receivable_credit=self.ar_credit,
 			accounts_receivable_discounted=self.ar_discounted,
 			accounts_receivable_unpaid=self.ar_unpaid,
@@ -122,8 +149,8 @@
 			bank_charges_account=self.bank_charges_account,
 			bank_account=self.bank_account,
 			start=nowdate(),
-			period=60
-			)
+			period=60,
+		)
 
 		je1 = inv_disc.create_disbursement_entry()
 		je1.posting_date = nowdate()
@@ -151,7 +178,8 @@
 
 	def test_on_close_after_loan_period_after_inv_payment(self):
 		inv = create_sales_invoice(rate=600)
-		inv_disc = create_invoice_discounting([inv.name],
+		inv_disc = create_invoice_discounting(
+			[inv.name],
 			accounts_receivable_credit=self.ar_credit,
 			accounts_receivable_discounted=self.ar_discounted,
 			accounts_receivable_unpaid=self.ar_unpaid,
@@ -159,8 +187,8 @@
 			bank_charges_account=self.bank_charges_account,
 			bank_account=self.bank_account,
 			start=nowdate(),
-			period=60
-			)
+			period=60,
+		)
 
 		je1 = inv_disc.create_disbursement_entry()
 		je1.posting_date = nowdate()
@@ -183,7 +211,8 @@
 
 	def test_on_close_before_loan_period(self):
 		inv = create_sales_invoice(rate=700)
-		inv_disc = create_invoice_discounting([inv.name],
+		inv_disc = create_invoice_discounting(
+			[inv.name],
 			accounts_receivable_credit=self.ar_credit,
 			accounts_receivable_discounted=self.ar_discounted,
 			accounts_receivable_unpaid=self.ar_unpaid,
@@ -191,7 +220,7 @@
 			bank_charges_account=self.bank_charges_account,
 			bank_account=self.bank_account,
 			start=add_days(nowdate(), -80),
-			period=60
+			period=60,
 		)
 
 		je1 = inv_disc.create_disbursement_entry()
@@ -209,16 +238,17 @@
 		self.assertEqual(je2.accounts[1].credit_in_account_currency, flt(inv_disc.total_amount))
 
 	def test_make_payment_before_loan_period(self):
-		#it has problem
+		# it has problem
 		inv = create_sales_invoice(rate=700)
-		inv_disc = create_invoice_discounting([inv.name],
-				accounts_receivable_credit=self.ar_credit,
-				accounts_receivable_discounted=self.ar_discounted,
-				accounts_receivable_unpaid=self.ar_unpaid,
-				short_term_loan=self.short_term_loan,
-				bank_charges_account=self.bank_charges_account,
-				bank_account=self.bank_account
-				)
+		inv_disc = create_invoice_discounting(
+			[inv.name],
+			accounts_receivable_credit=self.ar_credit,
+			accounts_receivable_discounted=self.ar_discounted,
+			accounts_receivable_unpaid=self.ar_unpaid,
+			short_term_loan=self.short_term_loan,
+			bank_charges_account=self.bank_charges_account,
+			bank_account=self.bank_account,
+		)
 		je = inv_disc.create_disbursement_entry()
 		inv_disc.reload()
 		je.posting_date = nowdate()
@@ -232,26 +262,31 @@
 		je_on_payment.submit()
 
 		self.assertEqual(je_on_payment.accounts[0].account, self.ar_discounted)
-		self.assertEqual(je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount))
+		self.assertEqual(
+			je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount)
+		)
 		self.assertEqual(je_on_payment.accounts[1].account, self.bank_account)
-		self.assertEqual(je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount))
+		self.assertEqual(
+			je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount)
+		)
 
 		inv.reload()
 		self.assertEqual(inv.outstanding_amount, 0)
 
 	def test_make_payment_before_after_period(self):
-		#it has problem
+		# it has problem
 		inv = create_sales_invoice(rate=700)
-		inv_disc = create_invoice_discounting([inv.name],
-				accounts_receivable_credit=self.ar_credit,
-				accounts_receivable_discounted=self.ar_discounted,
-				accounts_receivable_unpaid=self.ar_unpaid,
-				short_term_loan=self.short_term_loan,
-				bank_charges_account=self.bank_charges_account,
-				bank_account=self.bank_account,
-				loan_start_date=add_days(nowdate(), -10),
-				period=5
-				)
+		inv_disc = create_invoice_discounting(
+			[inv.name],
+			accounts_receivable_credit=self.ar_credit,
+			accounts_receivable_discounted=self.ar_discounted,
+			accounts_receivable_unpaid=self.ar_unpaid,
+			short_term_loan=self.short_term_loan,
+			bank_charges_account=self.bank_charges_account,
+			bank_account=self.bank_account,
+			loan_start_date=add_days(nowdate(), -10),
+			period=5,
+		)
 		je = inv_disc.create_disbursement_entry()
 		inv_disc.reload()
 		je.posting_date = nowdate()
@@ -269,9 +304,13 @@
 		je_on_payment.submit()
 
 		self.assertEqual(je_on_payment.accounts[0].account, self.ar_unpaid)
-		self.assertEqual(je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount))
+		self.assertEqual(
+			je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount)
+		)
 		self.assertEqual(je_on_payment.accounts[1].account, self.bank_account)
-		self.assertEqual(je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount))
+		self.assertEqual(
+			je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount)
+		)
 
 		inv.reload()
 		self.assertEqual(inv.outstanding_amount, 0)
@@ -287,17 +326,15 @@
 	inv_disc.accounts_receivable_credit = args.accounts_receivable_credit
 	inv_disc.accounts_receivable_discounted = args.accounts_receivable_discounted
 	inv_disc.accounts_receivable_unpaid = args.accounts_receivable_unpaid
-	inv_disc.short_term_loan=args.short_term_loan
-	inv_disc.bank_charges_account=args.bank_charges_account
-	inv_disc.bank_account=args.bank_account
+	inv_disc.short_term_loan = args.short_term_loan
+	inv_disc.bank_charges_account = args.bank_charges_account
+	inv_disc.bank_account = args.bank_account
 	inv_disc.loan_start_date = args.start or nowdate()
 	inv_disc.loan_period = args.period or 30
 	inv_disc.bank_charges = flt(args.bank_charges)
 
 	for d in invoices:
-		inv_disc.append("invoices", {
-			"sales_invoice": d
-		})
+		inv_disc.append("invoices", {"sales_invoice": d})
 	inv_disc.insert()
 
 	if not args.do_not_submit:
diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template.py b/erpnext/accounts/doctype/item_tax_template/item_tax_template.py
index 0ceb6a0..23f36ec 100644
--- a/erpnext/accounts/doctype/item_tax_template/item_tax_template.py
+++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template.py
@@ -13,20 +13,28 @@
 
 	def autoname(self):
 		if self.company and self.title:
-			abbr = frappe.get_cached_value('Company',  self.company,  'abbr')
-			self.name = '{0} - {1}'.format(self.title, abbr)
+			abbr = frappe.get_cached_value("Company", self.company, "abbr")
+			self.name = "{0} - {1}".format(self.title, abbr)
 
 	def validate_tax_accounts(self):
 		"""Check whether Tax Rate is not entered twice for same Tax Type"""
 		check_list = []
-		for d in self.get('taxes'):
+		for d in self.get("taxes"):
 			if d.tax_type:
 				account_type = frappe.db.get_value("Account", d.tax_type, "account_type")
 
-				if account_type not in ['Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation']:
+				if account_type not in [
+					"Tax",
+					"Chargeable",
+					"Income Account",
+					"Expense Account",
+					"Expenses Included In Valuation",
+				]:
 					frappe.throw(
-						_("Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable").format(
-							d.idx))
+						_(
+							"Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable"
+						).format(d.idx)
+					)
 				else:
 					if d.tax_type in check_list:
 						frappe.throw(_("{0} entered twice in Item Tax").format(d.tax_type))
diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template_dashboard.py b/erpnext/accounts/doctype/item_tax_template/item_tax_template_dashboard.py
index af01c57..5a2bd72 100644
--- a/erpnext/accounts/doctype/item_tax_template/item_tax_template_dashboard.py
+++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template_dashboard.py
@@ -3,23 +3,11 @@
 
 def get_data():
 	return {
-		'fieldname': 'item_tax_template',
-		'transactions': [
-			{
-				'label': _('Pre Sales'),
-				'items': ['Quotation', 'Supplier Quotation']
-			},
-			{
-				'label': _('Sales'),
-				'items': ['Sales Invoice', 'Sales Order', 'Delivery Note']
-			},
-			{
-				'label': _('Purchase'),
-				'items': ['Purchase Invoice', 'Purchase Order', 'Purchase Receipt']
-			},
-			{
-				'label': _('Stock'),
-				'items': ['Item Groups', 'Item']
-			}
-		]
+		"fieldname": "item_tax_template",
+		"transactions": [
+			{"label": _("Pre Sales"), "items": ["Quotation", "Supplier Quotation"]},
+			{"label": _("Sales"), "items": ["Sales Invoice", "Sales Order", "Delivery Note"]},
+			{"label": _("Purchase"), "items": ["Purchase Invoice", "Purchase Order", "Purchase Receipt"]},
+			{"label": _("Stock"), "items": ["Item Groups", "Item"]},
+		],
 	}
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json
index 335fd35..4493c72 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.json
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json
@@ -3,7 +3,7 @@
  "allow_auto_repeat": 1,
  "allow_import": 1,
  "autoname": "naming_series:",
- "creation": "2013-03-25 10:53:52",
+ "creation": "2022-01-25 10:29:58.717206",
  "doctype": "DocType",
  "document_type": "Document",
  "engine": "InnoDB",
@@ -13,6 +13,7 @@
   "voucher_type",
   "naming_series",
   "finance_book",
+  "process_deferred_accounting",
   "reversal_of",
   "tax_withholding_category",
   "column_break1",
@@ -524,13 +525,20 @@
    "label": "Reversal Of",
    "options": "Journal Entry",
    "read_only": 1
+  },
+  {
+   "fieldname": "process_deferred_accounting",
+   "fieldtype": "Link",
+   "label": "Process Deferred Accounting",
+   "options": "Process Deferred Accounting",
+   "read_only": 1
   }
  ],
  "icon": "fa fa-file-text",
  "idx": 176,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-01-04 13:39:36.485954",
+ "modified": "2022-04-06 17:18:46.865259",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Journal Entry",
@@ -578,6 +586,7 @@
  "search_fields": "voucher_type,posting_date, due_date, cheque_no",
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "title_field": "title",
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index ac8ab31..787efd2 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -18,7 +18,6 @@
 )
 from erpnext.accounts.party import get_party_account
 from erpnext.accounts.utils import (
-	check_if_stock_and_account_balance_synced,
 	get_account_currency,
 	get_balance_on,
 	get_stock_accounts,
@@ -28,7 +27,9 @@
 from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
 
 
-class StockAccountInvalidTransaction(frappe.ValidationError): pass
+class StockAccountInvalidTransaction(frappe.ValidationError):
+	pass
+
 
 class JournalEntry(AccountsController):
 	def __init__(self, *args, **kwargs):
@@ -38,11 +39,11 @@
 		return self.voucher_type
 
 	def validate(self):
-		if self.voucher_type == 'Opening Entry':
-			self.is_opening = 'Yes'
+		if self.voucher_type == "Opening Entry":
+			self.is_opening = "Yes"
 
 		if not self.is_opening:
-			self.is_opening='No'
+			self.is_opening = "No"
 
 		self.clearance_date = None
 
@@ -86,15 +87,14 @@
 		self.update_inter_company_jv()
 		self.update_invoice_discounting()
 		self.update_status_for_full_and_final_statement()
-		check_if_stock_and_account_balance_synced(self.posting_date,
-			self.company, self.doctype, self.name)
 
 	def on_cancel(self):
 		from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
 		from erpnext.payroll.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip
+
 		unlink_ref_doc_from_payment_entries(self)
 		unlink_ref_doc_from_salary_slip(self.name)
-		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+		self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
 		self.make_gl_entries(1)
 		self.update_advance_paid()
 		self.update_expense_claim()
@@ -127,12 +127,14 @@
 				elif self.docstatus == 2:
 					frappe.db.set_value("Full and Final Statement", entry.reference_name, "status", "Unpaid")
 
-
 	def validate_inter_company_accounts(self):
-		if self.voucher_type == "Inter Company Journal Entry" and self.inter_company_journal_entry_reference:
+		if (
+			self.voucher_type == "Inter Company Journal Entry"
+			and self.inter_company_journal_entry_reference
+		):
 			doc = frappe.get_doc("Journal Entry", self.inter_company_journal_entry_reference)
-			account_currency = frappe.get_cached_value('Company',  self.company,  "default_currency")
-			previous_account_currency = frappe.get_cached_value('Company',  doc.company,  "default_currency")
+			account_currency = frappe.get_cached_value("Company", self.company, "default_currency")
+			previous_account_currency = frappe.get_cached_value("Company", doc.company, "default_currency")
 			if account_currency == previous_account_currency:
 				if self.total_credit != doc.total_debit or self.total_debit != doc.total_credit:
 					frappe.throw(_("Total Credit/ Debit Amount should be same as linked Journal Entry"))
@@ -140,45 +142,63 @@
 	def validate_stock_accounts(self):
 		stock_accounts = get_stock_accounts(self.company, self.doctype, self.name)
 		for account in stock_accounts:
-			account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(account,
-				self.posting_date, self.company)
+			account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(
+				account, self.posting_date, self.company
+			)
 
 			if account_bal == stock_bal:
-				frappe.throw(_("Account: {0} can only be updated via Stock Transactions")
-					.format(account), StockAccountInvalidTransaction)
+				frappe.throw(
+					_("Account: {0} can only be updated via Stock Transactions").format(account),
+					StockAccountInvalidTransaction,
+				)
 
 	def apply_tax_withholding(self):
 		from erpnext.accounts.report.general_ledger.general_ledger import get_account_type_map
 
-		if not self.apply_tds or self.voucher_type not in ('Debit Note', 'Credit Note'):
+		if not self.apply_tds or self.voucher_type not in ("Debit Note", "Credit Note"):
 			return
 
-		parties = [d.party for d in self.get('accounts') if d.party]
+		parties = [d.party for d in self.get("accounts") if d.party]
 		parties = list(set(parties))
 
 		if len(parties) > 1:
 			frappe.throw(_("Cannot apply TDS against multiple parties in one entry"))
 
 		account_type_map = get_account_type_map(self.company)
-		party_type = 'supplier' if self.voucher_type == 'Credit Note' else 'customer'
-		doctype = 'Purchase Invoice' if self.voucher_type == 'Credit Note' else 'Sales Invoice'
-		debit_or_credit = 'debit_in_account_currency' if self.voucher_type == 'Credit Note' else 'credit_in_account_currency'
-		rev_debit_or_credit = 'credit_in_account_currency' if debit_or_credit == 'debit_in_account_currency' else 'debit_in_account_currency'
+		party_type = "supplier" if self.voucher_type == "Credit Note" else "customer"
+		doctype = "Purchase Invoice" if self.voucher_type == "Credit Note" else "Sales Invoice"
+		debit_or_credit = (
+			"debit_in_account_currency"
+			if self.voucher_type == "Credit Note"
+			else "credit_in_account_currency"
+		)
+		rev_debit_or_credit = (
+			"credit_in_account_currency"
+			if debit_or_credit == "debit_in_account_currency"
+			else "debit_in_account_currency"
+		)
 
 		party_account = get_party_account(party_type.title(), parties[0], self.company)
 
-		net_total = sum(d.get(debit_or_credit) for d in self.get('accounts') if account_type_map.get(d.account)
-			not in ('Tax', 'Chargeable'))
+		net_total = sum(
+			d.get(debit_or_credit)
+			for d in self.get("accounts")
+			if account_type_map.get(d.account) not in ("Tax", "Chargeable")
+		)
 
-		party_amount = sum(d.get(rev_debit_or_credit) for d in self.get('accounts') if d.account == party_account)
+		party_amount = sum(
+			d.get(rev_debit_or_credit) for d in self.get("accounts") if d.account == party_account
+		)
 
-		inv = frappe._dict({
-			party_type: parties[0],
-			'doctype': doctype,
-			'company': self.company,
-			'posting_date': self.posting_date,
-			'net_total': net_total
-		})
+		inv = frappe._dict(
+			{
+				party_type: parties[0],
+				"doctype": doctype,
+				"company": self.company,
+				"posting_date": self.posting_date,
+				"net_total": net_total,
+			}
+		)
 
 		tax_withholding_details = get_party_tax_withholding_details(inv, self.tax_withholding_category)
 
@@ -186,45 +206,64 @@
 			return
 
 		accounts = []
-		for d in self.get('accounts'):
-			if d.get('account') == tax_withholding_details.get("account_head"):
-				d.update({
-					'account': tax_withholding_details.get("account_head"),
-					debit_or_credit: tax_withholding_details.get('tax_amount')
-				})
+		for d in self.get("accounts"):
+			if d.get("account") == tax_withholding_details.get("account_head"):
+				d.update(
+					{
+						"account": tax_withholding_details.get("account_head"),
+						debit_or_credit: tax_withholding_details.get("tax_amount"),
+					}
+				)
 
-			accounts.append(d.get('account'))
+			accounts.append(d.get("account"))
 
-			if d.get('account') == party_account:
-				d.update({
-					rev_debit_or_credit: party_amount - tax_withholding_details.get('tax_amount')
-				})
+			if d.get("account") == party_account:
+				d.update({rev_debit_or_credit: party_amount - tax_withholding_details.get("tax_amount")})
 
 		if not accounts or tax_withholding_details.get("account_head") not in accounts:
-			self.append("accounts", {
-				'account': tax_withholding_details.get("account_head"),
-				rev_debit_or_credit: tax_withholding_details.get('tax_amount'),
-				'against_account': parties[0]
-			})
+			self.append(
+				"accounts",
+				{
+					"account": tax_withholding_details.get("account_head"),
+					rev_debit_or_credit: tax_withholding_details.get("tax_amount"),
+					"against_account": parties[0],
+				},
+			)
 
-		to_remove = [d for d in self.get('accounts')
-			if not d.get(rev_debit_or_credit) and d.account == tax_withholding_details.get("account_head")]
+		to_remove = [
+			d
+			for d in self.get("accounts")
+			if not d.get(rev_debit_or_credit) and d.account == tax_withholding_details.get("account_head")
+		]
 
 		for d in to_remove:
 			self.remove(d)
 
 	def update_inter_company_jv(self):
-		if self.voucher_type == "Inter Company Journal Entry" and self.inter_company_journal_entry_reference:
-			frappe.db.set_value("Journal Entry", self.inter_company_journal_entry_reference,\
-				"inter_company_journal_entry_reference", self.name)
+		if (
+			self.voucher_type == "Inter Company Journal Entry"
+			and self.inter_company_journal_entry_reference
+		):
+			frappe.db.set_value(
+				"Journal Entry",
+				self.inter_company_journal_entry_reference,
+				"inter_company_journal_entry_reference",
+				self.name,
+			)
 
 	def update_invoice_discounting(self):
 		def _validate_invoice_discounting_status(inv_disc, id_status, expected_status, row_id):
 			id_link = get_link_to_form("Invoice Discounting", inv_disc)
 			if id_status != expected_status:
-				frappe.throw(_("Row #{0}: Status must be {1} for Invoice Discounting {2}").format(d.idx, expected_status, id_link))
+				frappe.throw(
+					_("Row #{0}: Status must be {1} for Invoice Discounting {2}").format(
+						d.idx, expected_status, id_link
+					)
+				)
 
-		invoice_discounting_list = list(set([d.reference_name for d in self.accounts if d.reference_type=="Invoice Discounting"]))
+		invoice_discounting_list = list(
+			set([d.reference_name for d in self.accounts if d.reference_type == "Invoice Discounting"])
+		)
 		for inv_disc in invoice_discounting_list:
 			inv_disc_doc = frappe.get_doc("Invoice Discounting", inv_disc)
 			status = None
@@ -248,104 +287,147 @@
 			if status:
 				inv_disc_doc.set_status(status=status)
 
-
 	def unlink_advance_entry_reference(self):
 		for d in self.get("accounts"):
 			if d.is_advance == "Yes" and d.reference_type in ("Sales Invoice", "Purchase Invoice"):
 				doc = frappe.get_doc(d.reference_type, d.reference_name)
 				doc.delink_advance_entries(self.name)
-				d.reference_type = ''
-				d.reference_name = ''
+				d.reference_type = ""
+				d.reference_name = ""
 				d.db_update()
 
 	def unlink_asset_reference(self):
 		for d in self.get("accounts"):
-			if d.reference_type=="Asset" and d.reference_name:
+			if d.reference_type == "Asset" and d.reference_name:
 				asset = frappe.get_doc("Asset", d.reference_name)
 				for s in asset.get("schedules"):
 					if s.journal_entry == self.name:
 						s.db_set("journal_entry", None)
 
 						idx = cint(s.finance_book_id) or 1
-						finance_books = asset.get('finance_books')[idx - 1]
+						finance_books = asset.get("finance_books")[idx - 1]
 						finance_books.value_after_depreciation += s.depreciation_amount
 						finance_books.db_update()
 
 						asset.set_status()
 
 	def unlink_inter_company_jv(self):
-		if self.voucher_type == "Inter Company Journal Entry" and self.inter_company_journal_entry_reference:
-			frappe.db.set_value("Journal Entry", self.inter_company_journal_entry_reference,\
-				"inter_company_journal_entry_reference", "")
-			frappe.db.set_value("Journal Entry", self.name,\
-				"inter_company_journal_entry_reference", "")
+		if (
+			self.voucher_type == "Inter Company Journal Entry"
+			and self.inter_company_journal_entry_reference
+		):
+			frappe.db.set_value(
+				"Journal Entry",
+				self.inter_company_journal_entry_reference,
+				"inter_company_journal_entry_reference",
+				"",
+			)
+			frappe.db.set_value("Journal Entry", self.name, "inter_company_journal_entry_reference", "")
 
 	def unlink_asset_adjustment_entry(self):
-		frappe.db.sql(""" update `tabAsset Value Adjustment`
-			set journal_entry = null where journal_entry = %s""", self.name)
+		frappe.db.sql(
+			""" update `tabAsset Value Adjustment`
+			set journal_entry = null where journal_entry = %s""",
+			self.name,
+		)
 
 	def validate_party(self):
 		for d in self.get("accounts"):
 			account_type = frappe.db.get_value("Account", d.account, "account_type")
 			if account_type in ["Receivable", "Payable"]:
 				if not (d.party_type and d.party):
-					frappe.throw(_("Row {0}: Party Type and Party is required for Receivable / Payable account {1}").format(d.idx, d.account))
+					frappe.throw(
+						_("Row {0}: Party Type and Party is required for Receivable / Payable account {1}").format(
+							d.idx, d.account
+						)
+					)
 
 	def check_credit_limit(self):
-		customers = list(set(d.party for d in self.get("accounts")
-			if d.party_type=="Customer" and d.party and flt(d.debit) > 0))
+		customers = list(
+			set(
+				d.party
+				for d in self.get("accounts")
+				if d.party_type == "Customer" and d.party and flt(d.debit) > 0
+			)
+		)
 		if customers:
 			from erpnext.selling.doctype.customer.customer import check_credit_limit
+
 			for customer in customers:
 				check_credit_limit(customer, self.company)
 
 	def validate_cheque_info(self):
-		if self.voucher_type in ['Bank Entry']:
+		if self.voucher_type in ["Bank Entry"]:
 			if not self.cheque_no or not self.cheque_date:
-				msgprint(_("Reference No & Reference Date is required for {0}").format(self.voucher_type),
-					raise_exception=1)
+				msgprint(
+					_("Reference No & Reference Date is required for {0}").format(self.voucher_type),
+					raise_exception=1,
+				)
 
 		if self.cheque_date and not self.cheque_no:
 			msgprint(_("Reference No is mandatory if you entered Reference Date"), raise_exception=1)
 
 	def validate_entries_for_advance(self):
-		for d in self.get('accounts'):
+		for d in self.get("accounts"):
 			if d.reference_type not in ("Sales Invoice", "Purchase Invoice", "Journal Entry"):
-				if (d.party_type == 'Customer' and flt(d.credit) > 0) or \
-						(d.party_type == 'Supplier' and flt(d.debit) > 0):
-					if d.is_advance=="No":
-						msgprint(_("Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.").format(d.idx, d.account), alert=True)
+				if (d.party_type == "Customer" and flt(d.credit) > 0) or (
+					d.party_type == "Supplier" and flt(d.debit) > 0
+				):
+					if d.is_advance == "No":
+						msgprint(
+							_(
+								"Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry."
+							).format(d.idx, d.account),
+							alert=True,
+						)
 					elif d.reference_type in ("Sales Order", "Purchase Order") and d.is_advance != "Yes":
-						frappe.throw(_("Row {0}: Payment against Sales/Purchase Order should always be marked as advance").format(d.idx))
+						frappe.throw(
+							_(
+								"Row {0}: Payment against Sales/Purchase Order should always be marked as advance"
+							).format(d.idx)
+						)
 
 				if d.is_advance == "Yes":
-					if d.party_type == 'Customer' and flt(d.debit) > 0:
+					if d.party_type == "Customer" and flt(d.debit) > 0:
 						frappe.throw(_("Row {0}: Advance against Customer must be credit").format(d.idx))
-					elif d.party_type == 'Supplier' and flt(d.credit) > 0:
+					elif d.party_type == "Supplier" and flt(d.credit) > 0:
 						frappe.throw(_("Row {0}: Advance against Supplier must be debit").format(d.idx))
 
 	def validate_against_jv(self):
-		for d in self.get('accounts'):
-			if d.reference_type=="Journal Entry":
+		for d in self.get("accounts"):
+			if d.reference_type == "Journal Entry":
 				account_root_type = frappe.db.get_value("Account", d.account, "root_type")
 				if account_root_type == "Asset" and flt(d.debit) > 0:
-					frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets credited")
-						.format(d.idx, d.account))
+					frappe.throw(
+						_(
+							"Row #{0}: For {1}, you can select reference document only if account gets credited"
+						).format(d.idx, d.account)
+					)
 				elif account_root_type == "Liability" and flt(d.credit) > 0:
-					frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets debited")
-						.format(d.idx, d.account))
+					frappe.throw(
+						_(
+							"Row #{0}: For {1}, you can select reference document only if account gets debited"
+						).format(d.idx, d.account)
+					)
 
 				if d.reference_name == self.name:
 					frappe.throw(_("You can not enter current voucher in 'Against Journal Entry' column"))
 
-				against_entries = frappe.db.sql("""select * from `tabJournal Entry Account`
+				against_entries = frappe.db.sql(
+					"""select * from `tabJournal Entry Account`
 					where account = %s and docstatus = 1 and parent = %s
-					and (reference_type is null or reference_type in ("", "Sales Order", "Purchase Order"))
-					""", (d.account, d.reference_name), as_dict=True)
+					and (reference_type is null or reference_type in ('', 'Sales Order', 'Purchase Order'))
+					""",
+					(d.account, d.reference_name),
+					as_dict=True,
+				)
 
 				if not against_entries:
-					frappe.throw(_("Journal Entry {0} does not have account {1} or already matched against other voucher")
-						.format(d.reference_name, d.account))
+					frappe.throw(
+						_(
+							"Journal Entry {0} does not have account {1} or already matched against other voucher"
+						).format(d.reference_name, d.account)
+					)
 				else:
 					dr_or_cr = "debit" if d.credit > 0 else "credit"
 					valid = False
@@ -353,16 +435,19 @@
 						if flt(jvd[dr_or_cr]) > 0:
 							valid = True
 					if not valid:
-						frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
-							.format(d.reference_name, dr_or_cr))
+						frappe.throw(
+							_("Against Journal Entry {0} does not have any unmatched {1} entry").format(
+								d.reference_name, dr_or_cr
+							)
+						)
 
 	def validate_reference_doc(self):
 		"""Validates reference document"""
 		field_dict = {
-			'Sales Invoice': ["Customer", "Debit To"],
-			'Purchase Invoice': ["Supplier", "Credit To"],
-			'Sales Order': ["Customer"],
-			'Purchase Order': ["Supplier"]
+			"Sales Invoice": ["Customer", "Debit To"],
+			"Purchase Invoice": ["Supplier", "Credit To"],
+			"Sales Order": ["Customer"],
+			"Purchase Order": ["Supplier"],
 		}
 
 		self.reference_totals = {}
@@ -375,56 +460,76 @@
 			if not d.reference_name:
 				d.reference_type = None
 			if d.reference_type and d.reference_name and (d.reference_type in list(field_dict)):
-				dr_or_cr = "credit_in_account_currency" \
-					if d.reference_type in ("Sales Order", "Sales Invoice") else "debit_in_account_currency"
+				dr_or_cr = (
+					"credit_in_account_currency"
+					if d.reference_type in ("Sales Order", "Sales Invoice")
+					else "debit_in_account_currency"
+				)
 
 				# check debit or credit type Sales / Purchase Order
-				if d.reference_type=="Sales Order" and flt(d.debit) > 0:
-					frappe.throw(_("Row {0}: Debit entry can not be linked with a {1}").format(d.idx, d.reference_type))
+				if d.reference_type == "Sales Order" and flt(d.debit) > 0:
+					frappe.throw(
+						_("Row {0}: Debit entry can not be linked with a {1}").format(d.idx, d.reference_type)
+					)
 
 				if d.reference_type == "Purchase Order" and flt(d.credit) > 0:
-					frappe.throw(_("Row {0}: Credit entry can not be linked with a {1}").format(d.idx, d.reference_type))
+					frappe.throw(
+						_("Row {0}: Credit entry can not be linked with a {1}").format(d.idx, d.reference_type)
+					)
 
 				# set totals
 				if not d.reference_name in self.reference_totals:
 					self.reference_totals[d.reference_name] = 0.0
 
-				if self.voucher_type not in ('Deferred Revenue', 'Deferred Expense'):
+				if self.voucher_type not in ("Deferred Revenue", "Deferred Expense"):
 					self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr))
 
 				self.reference_types[d.reference_name] = d.reference_type
 				self.reference_accounts[d.reference_name] = d.account
 
-				against_voucher = frappe.db.get_value(d.reference_type, d.reference_name,
-					[scrub(dt) for dt in field_dict.get(d.reference_type)])
+				against_voucher = frappe.db.get_value(
+					d.reference_type, d.reference_name, [scrub(dt) for dt in field_dict.get(d.reference_type)]
+				)
 
 				if not against_voucher:
 					frappe.throw(_("Row {0}: Invalid reference {1}").format(d.idx, d.reference_name))
 
 				# check if party and account match
 				if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
-					if self.voucher_type in ('Deferred Revenue', 'Deferred Expense') and d.reference_detail_no:
-						debit_or_credit = 'Debit' if d.debit else 'Credit'
-						party_account = get_deferred_booking_accounts(d.reference_type, d.reference_detail_no,
-							debit_or_credit)
-						against_voucher = ['', against_voucher[1]]
+					if self.voucher_type in ("Deferred Revenue", "Deferred Expense") and d.reference_detail_no:
+						debit_or_credit = "Debit" if d.debit else "Credit"
+						party_account = get_deferred_booking_accounts(
+							d.reference_type, d.reference_detail_no, debit_or_credit
+						)
+						against_voucher = ["", against_voucher[1]]
 					else:
 						if d.reference_type == "Sales Invoice":
-							party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1]
+							party_account = (
+								get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1]
+							)
 						else:
 							party_account = against_voucher[1]
 
-					if (against_voucher[0] != cstr(d.party) or party_account != d.account):
-						frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}")
-							.format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1],
-								d.reference_type, d.reference_name))
+					if against_voucher[0] != cstr(d.party) or party_account != d.account:
+						frappe.throw(
+							_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}").format(
+								d.idx,
+								field_dict.get(d.reference_type)[0],
+								field_dict.get(d.reference_type)[1],
+								d.reference_type,
+								d.reference_name,
+							)
+						)
 
 				# check if party matches for Sales / Purchase Order
 				if d.reference_type in ("Sales Order", "Purchase Order"):
 					# set totals
 					if against_voucher != d.party:
-						frappe.throw(_("Row {0}: {1} {2} does not match with {3}") \
-							.format(d.idx, d.party_type, d.party, d.reference_type))
+						frappe.throw(
+							_("Row {0}: {1} {2} does not match with {3}").format(
+								d.idx, d.party_type, d.party, d.reference_type
+							)
+						)
 
 		self.validate_orders()
 		self.validate_invoices()
@@ -450,62 +555,79 @@
 				account_currency = get_account_currency(account)
 				if account_currency == self.company_currency:
 					voucher_total = order.base_grand_total
-					formatted_voucher_total = fmt_money(voucher_total, order.precision("base_grand_total"),
-						currency=account_currency)
+					formatted_voucher_total = fmt_money(
+						voucher_total, order.precision("base_grand_total"), currency=account_currency
+					)
 				else:
 					voucher_total = order.grand_total
-					formatted_voucher_total = fmt_money(voucher_total, order.precision("grand_total"),
-						currency=account_currency)
+					formatted_voucher_total = fmt_money(
+						voucher_total, order.precision("grand_total"), currency=account_currency
+					)
 
 				if flt(voucher_total) < (flt(order.advance_paid) + total):
-					frappe.throw(_("Advance paid against {0} {1} cannot be greater than Grand Total {2}").format(reference_type, reference_name, formatted_voucher_total))
+					frappe.throw(
+						_("Advance paid against {0} {1} cannot be greater than Grand Total {2}").format(
+							reference_type, reference_name, formatted_voucher_total
+						)
+					)
 
 	def validate_invoices(self):
 		"""Validate totals and docstatus for invoices"""
 		for reference_name, total in self.reference_totals.items():
 			reference_type = self.reference_types[reference_name]
 
-			if (reference_type in ("Sales Invoice", "Purchase Invoice") and
-				self.voucher_type not in ['Debit Note', 'Credit Note']):
-				invoice = frappe.db.get_value(reference_type, reference_name,
-					["docstatus", "outstanding_amount"], as_dict=1)
+			if reference_type in ("Sales Invoice", "Purchase Invoice") and self.voucher_type not in [
+				"Debit Note",
+				"Credit Note",
+			]:
+				invoice = frappe.db.get_value(
+					reference_type, reference_name, ["docstatus", "outstanding_amount"], as_dict=1
+				)
 
 				if invoice.docstatus != 1:
 					frappe.throw(_("{0} {1} is not submitted").format(reference_type, reference_name))
 
 				if total and flt(invoice.outstanding_amount) < total:
-					frappe.throw(_("Payment against {0} {1} cannot be greater than Outstanding Amount {2}")
-						.format(reference_type, reference_name, invoice.outstanding_amount))
+					frappe.throw(
+						_("Payment against {0} {1} cannot be greater than Outstanding Amount {2}").format(
+							reference_type, reference_name, invoice.outstanding_amount
+						)
+					)
 
 	def set_against_account(self):
 		accounts_debited, accounts_credited = [], []
-		if self.voucher_type in ('Deferred Revenue', 'Deferred Expense'):
-			for d in self.get('accounts'):
-				if d.reference_type == 'Sales Invoice':
-					field = 'customer'
+		if self.voucher_type in ("Deferred Revenue", "Deferred Expense"):
+			for d in self.get("accounts"):
+				if d.reference_type == "Sales Invoice":
+					field = "customer"
 				else:
-					field = 'supplier'
+					field = "supplier"
 
 				d.against_account = frappe.db.get_value(d.reference_type, d.reference_name, field)
 		else:
 			for d in self.get("accounts"):
-				if flt(d.debit > 0): accounts_debited.append(d.party or d.account)
-				if flt(d.credit) > 0: accounts_credited.append(d.party or d.account)
+				if flt(d.debit > 0):
+					accounts_debited.append(d.party or d.account)
+				if flt(d.credit) > 0:
+					accounts_credited.append(d.party or d.account)
 
 			for d in self.get("accounts"):
-				if flt(d.debit > 0): d.against_account = ", ".join(list(set(accounts_credited)))
-				if flt(d.credit > 0): d.against_account = ", ".join(list(set(accounts_debited)))
+				if flt(d.debit > 0):
+					d.against_account = ", ".join(list(set(accounts_credited)))
+				if flt(d.credit > 0):
+					d.against_account = ", ".join(list(set(accounts_debited)))
 
 	def validate_debit_credit_amount(self):
-		for d in self.get('accounts'):
+		for d in self.get("accounts"):
 			if not flt(d.debit) and not flt(d.credit):
 				frappe.throw(_("Row {0}: Both Debit and Credit values cannot be zero").format(d.idx))
 
 	def validate_total_debit_and_credit(self):
 		self.set_total_debit_credit()
 		if self.difference:
-			frappe.throw(_("Total Debit must be equal to Total Credit. The difference is {0}")
-				.format(self.difference))
+			frappe.throw(
+				_("Total Debit must be equal to Total Credit. The difference is {0}").format(self.difference)
+			)
 
 	def set_total_debit_credit(self):
 		self.total_debit, self.total_credit, self.difference = 0, 0, 0
@@ -516,13 +638,16 @@
 			self.total_debit = flt(self.total_debit) + flt(d.debit, d.precision("debit"))
 			self.total_credit = flt(self.total_credit) + flt(d.credit, d.precision("credit"))
 
-		self.difference = flt(self.total_debit, self.precision("total_debit")) - \
-			flt(self.total_credit, self.precision("total_credit"))
+		self.difference = flt(self.total_debit, self.precision("total_debit")) - flt(
+			self.total_credit, self.precision("total_credit")
+		)
 
 	def validate_multi_currency(self):
 		alternate_currency = []
 		for d in self.get("accounts"):
-			account = frappe.db.get_value("Account", d.account, ["account_currency", "account_type"], as_dict=1)
+			account = frappe.db.get_value(
+				"Account", d.account, ["account_currency", "account_type"], as_dict=1
+			)
 			if account:
 				d.account_currency = account.account_currency
 				d.account_type = account.account_type
@@ -541,8 +666,12 @@
 
 	def set_amounts_in_company_currency(self):
 		for d in self.get("accounts"):
-			d.debit_in_account_currency = flt(d.debit_in_account_currency, d.precision("debit_in_account_currency"))
-			d.credit_in_account_currency = flt(d.credit_in_account_currency, d.precision("credit_in_account_currency"))
+			d.debit_in_account_currency = flt(
+				d.debit_in_account_currency, d.precision("debit_in_account_currency")
+			)
+			d.credit_in_account_currency = flt(
+				d.credit_in_account_currency, d.precision("credit_in_account_currency")
+			)
 
 			d.debit = flt(d.debit_in_account_currency * flt(d.exchange_rate), d.precision("debit"))
 			d.credit = flt(d.credit_in_account_currency * flt(d.exchange_rate), d.precision("credit"))
@@ -551,13 +680,28 @@
 		for d in self.get("accounts"):
 			if d.account_currency == self.company_currency:
 				d.exchange_rate = 1
-			elif not d.exchange_rate or d.exchange_rate == 1 or \
-				(d.reference_type in ("Sales Invoice", "Purchase Invoice")
-				and d.reference_name and self.posting_date):
+			elif (
+				not d.exchange_rate
+				or d.exchange_rate == 1
+				or (
+					d.reference_type in ("Sales Invoice", "Purchase Invoice")
+					and d.reference_name
+					and self.posting_date
+				)
+			):
 
-					# Modified to include the posting date for which to retreive the exchange rate
-					d.exchange_rate = get_exchange_rate(self.posting_date, d.account, d.account_currency,
-						self.company, d.reference_type, d.reference_name, d.debit, d.credit, d.exchange_rate)
+				# Modified to include the posting date for which to retreive the exchange rate
+				d.exchange_rate = get_exchange_rate(
+					self.posting_date,
+					d.account,
+					d.account_currency,
+					self.company,
+					d.reference_type,
+					d.reference_name,
+					d.debit,
+					d.credit,
+					d.exchange_rate,
+				)
 
 			if not d.exchange_rate:
 				frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
@@ -570,55 +714,76 @@
 
 		if self.cheque_no:
 			if self.cheque_date:
-				r.append(_('Reference #{0} dated {1}').format(self.cheque_no, formatdate(self.cheque_date)))
+				r.append(_("Reference #{0} dated {1}").format(self.cheque_no, formatdate(self.cheque_date)))
 			else:
 				msgprint(_("Please enter Reference date"), raise_exception=frappe.MandatoryError)
 
-		for d in self.get('accounts'):
-			if d.reference_type=="Sales Invoice" and d.credit:
-				r.append(_("{0} against Sales Invoice {1}").format(fmt_money(flt(d.credit), currency = self.company_currency), \
-					d.reference_name))
+		for d in self.get("accounts"):
+			if d.reference_type == "Sales Invoice" and d.credit:
+				r.append(
+					_("{0} against Sales Invoice {1}").format(
+						fmt_money(flt(d.credit), currency=self.company_currency), d.reference_name
+					)
+				)
 
-			if d.reference_type=="Sales Order" and d.credit:
-				r.append(_("{0} against Sales Order {1}").format(fmt_money(flt(d.credit), currency = self.company_currency), \
-					d.reference_name))
+			if d.reference_type == "Sales Order" and d.credit:
+				r.append(
+					_("{0} against Sales Order {1}").format(
+						fmt_money(flt(d.credit), currency=self.company_currency), d.reference_name
+					)
+				)
 
 			if d.reference_type == "Purchase Invoice" and d.debit:
-				bill_no = frappe.db.sql("""select bill_no, bill_date
-					from `tabPurchase Invoice` where name=%s""", d.reference_name)
-				if bill_no and bill_no[0][0] and bill_no[0][0].lower().strip() \
-						not in ['na', 'not applicable', 'none']:
-					r.append(_('{0} against Bill {1} dated {2}').format(fmt_money(flt(d.debit), currency=self.company_currency), bill_no[0][0],
-						bill_no[0][1] and formatdate(bill_no[0][1].strftime('%Y-%m-%d'))))
+				bill_no = frappe.db.sql(
+					"""select bill_no, bill_date
+					from `tabPurchase Invoice` where name=%s""",
+					d.reference_name,
+				)
+				if (
+					bill_no
+					and bill_no[0][0]
+					and bill_no[0][0].lower().strip() not in ["na", "not applicable", "none"]
+				):
+					r.append(
+						_("{0} against Bill {1} dated {2}").format(
+							fmt_money(flt(d.debit), currency=self.company_currency),
+							bill_no[0][0],
+							bill_no[0][1] and formatdate(bill_no[0][1].strftime("%Y-%m-%d")),
+						)
+					)
 
 			if d.reference_type == "Purchase Order" and d.debit:
-				r.append(_("{0} against Purchase Order {1}").format(fmt_money(flt(d.credit), currency = self.company_currency), \
-					d.reference_name))
+				r.append(
+					_("{0} against Purchase Order {1}").format(
+						fmt_money(flt(d.credit), currency=self.company_currency), d.reference_name
+					)
+				)
 
 		if r:
-			self.remark = ("\n").join(r) #User Remarks is not mandatory
+			self.remark = ("\n").join(r)  # User Remarks is not mandatory
 
 	def set_print_format_fields(self):
 		bank_amount = party_amount = total_amount = 0.0
-		currency = bank_account_currency = party_account_currency = pay_to_recd_from= None
+		currency = bank_account_currency = party_account_currency = pay_to_recd_from = None
 		party_type = None
-		for d in self.get('accounts'):
-			if d.party_type in ['Customer', 'Supplier'] and d.party:
+		for d in self.get("accounts"):
+			if d.party_type in ["Customer", "Supplier"] and d.party:
 				party_type = d.party_type
 				if not pay_to_recd_from:
 					pay_to_recd_from = d.party
 
 				if pay_to_recd_from and pay_to_recd_from == d.party:
-					party_amount += (d.debit_in_account_currency or d.credit_in_account_currency)
+					party_amount += d.debit_in_account_currency or d.credit_in_account_currency
 					party_account_currency = d.account_currency
 
 			elif frappe.db.get_value("Account", d.account, "account_type") in ["Bank", "Cash"]:
-				bank_amount += (d.debit_in_account_currency or d.credit_in_account_currency)
+				bank_amount += d.debit_in_account_currency or d.credit_in_account_currency
 				bank_account_currency = d.account_currency
 
 		if party_type and pay_to_recd_from:
-			self.pay_to_recd_from = frappe.db.get_value(party_type, pay_to_recd_from,
-				"customer_name" if party_type=="Customer" else "supplier_name")
+			self.pay_to_recd_from = frappe.db.get_value(
+				party_type, pay_to_recd_from, "customer_name" if party_type == "Customer" else "supplier_name"
+			)
 			if bank_amount:
 				total_amount = bank_amount
 				currency = bank_account_currency
@@ -632,11 +797,10 @@
 		self.total_amount = amt
 		self.total_amount_currency = currency
 		from frappe.utils import money_in_words
+
 		self.total_amount_in_words = money_in_words(amt, currency)
 
-	def make_gl_entries(self, cancel=0, adv_adj=0):
-		from erpnext.accounts.general_ledger import make_gl_entries
-
+	def build_gl_map(self):
 		gl_map = []
 		for d in self.get("accounts"):
 			if d.debit or d.credit:
@@ -645,38 +809,50 @@
 				remarks = "\n".join(r)
 
 				gl_map.append(
-					self.get_gl_dict({
-						"account": d.account,
-						"party_type": d.party_type,
-						"due_date": self.due_date,
-						"party": d.party,
-						"against": d.against_account,
-						"debit": flt(d.debit, d.precision("debit")),
-						"credit": flt(d.credit, d.precision("credit")),
-						"account_currency": d.account_currency,
-						"debit_in_account_currency": flt(d.debit_in_account_currency, d.precision("debit_in_account_currency")),
-						"credit_in_account_currency": flt(d.credit_in_account_currency, d.precision("credit_in_account_currency")),
-						"against_voucher_type": d.reference_type,
-						"against_voucher": d.reference_name,
-						"remarks": remarks,
-						"voucher_detail_no": d.reference_detail_no,
-						"cost_center": d.cost_center,
-						"project": d.project,
-						"finance_book": self.finance_book
-					}, item=d)
+					self.get_gl_dict(
+						{
+							"account": d.account,
+							"party_type": d.party_type,
+							"due_date": self.due_date,
+							"party": d.party,
+							"against": d.against_account,
+							"debit": flt(d.debit, d.precision("debit")),
+							"credit": flt(d.credit, d.precision("credit")),
+							"account_currency": d.account_currency,
+							"debit_in_account_currency": flt(
+								d.debit_in_account_currency, d.precision("debit_in_account_currency")
+							),
+							"credit_in_account_currency": flt(
+								d.credit_in_account_currency, d.precision("credit_in_account_currency")
+							),
+							"against_voucher_type": d.reference_type,
+							"against_voucher": d.reference_name,
+							"remarks": remarks,
+							"voucher_detail_no": d.reference_detail_no,
+							"cost_center": d.cost_center,
+							"project": d.project,
+							"finance_book": self.finance_book,
+						},
+						item=d,
+					)
 				)
+		return gl_map
 
-		if self.voucher_type in ('Deferred Revenue', 'Deferred Expense'):
-			update_outstanding = 'No'
+	def make_gl_entries(self, cancel=0, adv_adj=0):
+		from erpnext.accounts.general_ledger import make_gl_entries
+
+		gl_map = self.build_gl_map()
+		if self.voucher_type in ("Deferred Revenue", "Deferred Expense"):
+			update_outstanding = "No"
 		else:
-			update_outstanding = 'Yes'
+			update_outstanding = "Yes"
 
 		if gl_map:
 			make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj, update_outstanding=update_outstanding)
 
 	@frappe.whitelist()
 	def get_balance(self):
-		if not self.get('accounts'):
+		if not self.get("accounts"):
 			msgprint(_("'Entries' cannot be empty"), raise_exception=True)
 		else:
 			self.total_debit, self.total_credit = 0, 0
@@ -685,18 +861,18 @@
 			# If any row without amount, set the diff on that row
 			if diff:
 				blank_row = None
-				for d in self.get('accounts'):
+				for d in self.get("accounts"):
 					if not d.credit_in_account_currency and not d.debit_in_account_currency and diff != 0:
 						blank_row = d
 
 				if not blank_row:
-					blank_row = self.append('accounts', {})
+					blank_row = self.append("accounts", {})
 
 				blank_row.exchange_rate = 1
-				if diff>0:
+				if diff > 0:
 					blank_row.credit_in_account_currency = diff
 					blank_row.credit = diff
-				elif diff<0:
+				elif diff < 0:
 					blank_row.debit_in_account_currency = abs(diff)
 					blank_row.debit = abs(diff)
 
@@ -704,76 +880,100 @@
 
 	@frappe.whitelist()
 	def get_outstanding_invoices(self):
-		self.set('accounts', [])
+		self.set("accounts", [])
 		total = 0
 		for d in self.get_values():
 			total += flt(d.outstanding_amount, self.precision("credit", "accounts"))
-			jd1 = self.append('accounts', {})
+			jd1 = self.append("accounts", {})
 			jd1.account = d.account
 			jd1.party = d.party
 
-			if self.write_off_based_on == 'Accounts Receivable':
+			if self.write_off_based_on == "Accounts Receivable":
 				jd1.party_type = "Customer"
-				jd1.credit_in_account_currency = flt(d.outstanding_amount, self.precision("credit", "accounts"))
+				jd1.credit_in_account_currency = flt(
+					d.outstanding_amount, self.precision("credit", "accounts")
+				)
 				jd1.reference_type = "Sales Invoice"
 				jd1.reference_name = cstr(d.name)
-			elif self.write_off_based_on == 'Accounts Payable':
+			elif self.write_off_based_on == "Accounts Payable":
 				jd1.party_type = "Supplier"
 				jd1.debit_in_account_currency = flt(d.outstanding_amount, self.precision("debit", "accounts"))
 				jd1.reference_type = "Purchase Invoice"
 				jd1.reference_name = cstr(d.name)
 
-		jd2 = self.append('accounts', {})
-		if self.write_off_based_on == 'Accounts Receivable':
+		jd2 = self.append("accounts", {})
+		if self.write_off_based_on == "Accounts Receivable":
 			jd2.debit_in_account_currency = total
-		elif self.write_off_based_on == 'Accounts Payable':
+		elif self.write_off_based_on == "Accounts Payable":
 			jd2.credit_in_account_currency = total
 
 		self.validate_total_debit_and_credit()
 
-
 	def get_values(self):
-		cond = " and outstanding_amount <= {0}".format(self.write_off_amount) \
-			if flt(self.write_off_amount) > 0 else ""
+		cond = (
+			" and outstanding_amount <= {0}".format(self.write_off_amount)
+			if flt(self.write_off_amount) > 0
+			else ""
+		)
 
-		if self.write_off_based_on == 'Accounts Receivable':
-			return frappe.db.sql("""select name, debit_to as account, customer as party, outstanding_amount
+		if self.write_off_based_on == "Accounts Receivable":
+			return frappe.db.sql(
+				"""select name, debit_to as account, customer as party, outstanding_amount
 				from `tabSales Invoice` where docstatus = 1 and company = %s
-				and outstanding_amount > 0 %s""" % ('%s', cond), self.company, as_dict=True)
-		elif self.write_off_based_on == 'Accounts Payable':
-			return frappe.db.sql("""select name, credit_to as account, supplier as party, outstanding_amount
+				and outstanding_amount > 0 %s"""
+				% ("%s", cond),
+				self.company,
+				as_dict=True,
+			)
+		elif self.write_off_based_on == "Accounts Payable":
+			return frappe.db.sql(
+				"""select name, credit_to as account, supplier as party, outstanding_amount
 				from `tabPurchase Invoice` where docstatus = 1 and company = %s
-				and outstanding_amount > 0 %s""" % ('%s', cond), self.company, as_dict=True)
+				and outstanding_amount > 0 %s"""
+				% ("%s", cond),
+				self.company,
+				as_dict=True,
+			)
 
 	def update_expense_claim(self):
 		for d in self.accounts:
-			if d.reference_type=="Expense Claim" and d.reference_name:
+			if d.reference_type == "Expense Claim" and d.reference_name:
 				doc = frappe.get_doc("Expense Claim", d.reference_name)
 				if self.docstatus == 2:
 					update_reimbursed_amount(doc, -1 * d.debit)
 				else:
 					update_reimbursed_amount(doc, d.debit)
 
-
 	def validate_expense_claim(self):
 		for d in self.accounts:
-			if d.reference_type=="Expense Claim":
-				sanctioned_amount, reimbursed_amount = frappe.db.get_value("Expense Claim",
-					d.reference_name, ("total_sanctioned_amount", "total_amount_reimbursed"))
+			if d.reference_type == "Expense Claim":
+				sanctioned_amount, reimbursed_amount = frappe.db.get_value(
+					"Expense Claim", d.reference_name, ("total_sanctioned_amount", "total_amount_reimbursed")
+				)
 				pending_amount = flt(sanctioned_amount) - flt(reimbursed_amount)
 				if d.debit > pending_amount:
-					frappe.throw(_("Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2}").format(d.idx, d.reference_name, pending_amount))
+					frappe.throw(
+						_(
+							"Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2}"
+						).format(d.idx, d.reference_name, pending_amount)
+					)
 
 	def validate_credit_debit_note(self):
 		if self.stock_entry:
 			if frappe.db.get_value("Stock Entry", self.stock_entry, "docstatus") != 1:
 				frappe.throw(_("Stock Entry {0} is not submitted").format(self.stock_entry))
 
-			if frappe.db.exists({"doctype": "Journal Entry", "stock_entry": self.stock_entry, "docstatus":1}):
-				frappe.msgprint(_("Warning: Another {0} # {1} exists against stock entry {2}").format(self.voucher_type, self.name, self.stock_entry))
+			if frappe.db.exists(
+				{"doctype": "Journal Entry", "stock_entry": self.stock_entry, "docstatus": 1}
+			):
+				frappe.msgprint(
+					_("Warning: Another {0} # {1} exists against stock entry {2}").format(
+						self.voucher_type, self.name, self.stock_entry
+					)
+				)
 
 	def validate_empty_accounts_table(self):
-		if not self.get('accounts'):
+		if not self.get("accounts"):
 			frappe.throw(_("Accounts table cannot be blank."))
 
 	def set_account_and_party_balance(self):
@@ -784,54 +984,66 @@
 				account_balance[d.account] = get_balance_on(account=d.account, date=self.posting_date)
 
 			if (d.party_type, d.party) not in party_balance:
-				party_balance[(d.party_type, d.party)] = get_balance_on(party_type=d.party_type,
-					party=d.party, date=self.posting_date, company=self.company)
+				party_balance[(d.party_type, d.party)] = get_balance_on(
+					party_type=d.party_type, party=d.party, date=self.posting_date, company=self.company
+				)
 
 			d.account_balance = account_balance[d.account]
 			d.party_balance = party_balance[(d.party_type, d.party)]
 
+
 @frappe.whitelist()
 def get_default_bank_cash_account(company, account_type=None, mode_of_payment=None, account=None):
 	from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
+
 	if mode_of_payment:
 		account = get_bank_cash_account(mode_of_payment, company).get("account")
 
 	if not account:
-		'''
-			Set the default account first. If the user hasn't set any default account then, he doesn't
-			want us to set any random account. In this case set the account only if there is single
-			account (of that type), otherwise return empty dict.
-		'''
-		if account_type=="Bank":
-			account = frappe.get_cached_value('Company',  company,  "default_bank_account")
+		"""
+		Set the default account first. If the user hasn't set any default account then, he doesn't
+		want us to set any random account. In this case set the account only if there is single
+		account (of that type), otherwise return empty dict.
+		"""
+		if account_type == "Bank":
+			account = frappe.get_cached_value("Company", company, "default_bank_account")
 			if not account:
-				account_list = frappe.get_all("Account", filters = {"company": company,
-					"account_type": "Bank", "is_group": 0})
+				account_list = frappe.get_all(
+					"Account", filters={"company": company, "account_type": "Bank", "is_group": 0}
+				)
 				if len(account_list) == 1:
 					account = account_list[0].name
 
-		elif account_type=="Cash":
-			account = frappe.get_cached_value('Company',  company,  "default_cash_account")
+		elif account_type == "Cash":
+			account = frappe.get_cached_value("Company", company, "default_cash_account")
 			if not account:
-				account_list = frappe.get_all("Account", filters = {"company": company,
-					"account_type": "Cash", "is_group": 0})
+				account_list = frappe.get_all(
+					"Account", filters={"company": company, "account_type": "Cash", "is_group": 0}
+				)
 				if len(account_list) == 1:
 					account = account_list[0].name
 
 	if account:
-		account_details = frappe.db.get_value("Account", account,
-			["account_currency", "account_type"], as_dict=1)
+		account_details = frappe.db.get_value(
+			"Account", account, ["account_currency", "account_type"], as_dict=1
+		)
 
-		return frappe._dict({
-			"account": account,
-			"balance": get_balance_on(account),
-			"account_currency": account_details.account_currency,
-			"account_type": account_details.account_type
-		})
-	else: return frappe._dict()
+		return frappe._dict(
+			{
+				"account": account,
+				"balance": get_balance_on(account),
+				"account_currency": account_details.account_currency,
+				"account_type": account_details.account_type,
+			}
+		)
+	else:
+		return frappe._dict()
+
 
 @frappe.whitelist()
-def get_payment_entry_against_order(dt, dn, amount=None, debit_in_account_currency=None, journal_entry=False, bank_account=None):
+def get_payment_entry_against_order(
+	dt, dn, amount=None, debit_in_account_currency=None, journal_entry=False, bank_account=None
+):
 	ref_doc = frappe.get_doc(dt, dn)
 
 	if flt(ref_doc.per_billed, 2) > 0:
@@ -855,22 +1067,28 @@
 		else:
 			amount = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid)
 
-	return get_payment_entry(ref_doc, {
-		"party_type": party_type,
-		"party_account": party_account,
-		"party_account_currency": party_account_currency,
-		"amount_field_party": amount_field_party,
-		"amount_field_bank": amount_field_bank,
-		"amount": amount,
-		"debit_in_account_currency": debit_in_account_currency,
-		"remarks": 'Advance Payment received against {0} {1}'.format(dt, dn),
-		"is_advance": "Yes",
-		"bank_account": bank_account,
-		"journal_entry": journal_entry
-	})
+	return get_payment_entry(
+		ref_doc,
+		{
+			"party_type": party_type,
+			"party_account": party_account,
+			"party_account_currency": party_account_currency,
+			"amount_field_party": amount_field_party,
+			"amount_field_bank": amount_field_bank,
+			"amount": amount,
+			"debit_in_account_currency": debit_in_account_currency,
+			"remarks": "Advance Payment received against {0} {1}".format(dt, dn),
+			"is_advance": "Yes",
+			"bank_account": bank_account,
+			"journal_entry": journal_entry,
+		},
+	)
+
 
 @frappe.whitelist()
-def get_payment_entry_against_invoice(dt, dn, amount=None,  debit_in_account_currency=None, journal_entry=False, bank_account=None):
+def get_payment_entry_against_invoice(
+	dt, dn, amount=None, debit_in_account_currency=None, journal_entry=False, bank_account=None
+):
 	ref_doc = frappe.get_doc(dt, dn)
 	if dt == "Sales Invoice":
 		party_type = "Customer"
@@ -879,73 +1097,91 @@
 		party_type = "Supplier"
 		party_account = ref_doc.credit_to
 
-	if (dt == "Sales Invoice" and ref_doc.outstanding_amount > 0) \
-		or (dt == "Purchase Invoice" and ref_doc.outstanding_amount < 0):
-			amount_field_party = "credit_in_account_currency"
-			amount_field_bank = "debit_in_account_currency"
+	if (dt == "Sales Invoice" and ref_doc.outstanding_amount > 0) or (
+		dt == "Purchase Invoice" and ref_doc.outstanding_amount < 0
+	):
+		amount_field_party = "credit_in_account_currency"
+		amount_field_bank = "debit_in_account_currency"
 	else:
 		amount_field_party = "debit_in_account_currency"
 		amount_field_bank = "credit_in_account_currency"
 
-	return get_payment_entry(ref_doc, {
-		"party_type": party_type,
-		"party_account": party_account,
-		"party_account_currency": ref_doc.party_account_currency,
-		"amount_field_party": amount_field_party,
-		"amount_field_bank": amount_field_bank,
-		"amount": amount if amount else abs(ref_doc.outstanding_amount),
-		"debit_in_account_currency": debit_in_account_currency,
-		"remarks": 'Payment received against {0} {1}. {2}'.format(dt, dn, ref_doc.remarks),
-		"is_advance": "No",
-		"bank_account": bank_account,
-		"journal_entry": journal_entry
-	})
+	return get_payment_entry(
+		ref_doc,
+		{
+			"party_type": party_type,
+			"party_account": party_account,
+			"party_account_currency": ref_doc.party_account_currency,
+			"amount_field_party": amount_field_party,
+			"amount_field_bank": amount_field_bank,
+			"amount": amount if amount else abs(ref_doc.outstanding_amount),
+			"debit_in_account_currency": debit_in_account_currency,
+			"remarks": "Payment received against {0} {1}. {2}".format(dt, dn, ref_doc.remarks),
+			"is_advance": "No",
+			"bank_account": bank_account,
+			"journal_entry": journal_entry,
+		},
+	)
+
 
 def get_payment_entry(ref_doc, args):
-	cost_center = ref_doc.get("cost_center") or frappe.get_cached_value('Company',  ref_doc.company,  "cost_center")
+	cost_center = ref_doc.get("cost_center") or frappe.get_cached_value(
+		"Company", ref_doc.company, "cost_center"
+	)
 	exchange_rate = 1
 	if args.get("party_account"):
 		# Modified to include the posting date for which the exchange rate is required.
 		# Assumed to be the posting date in the reference document
-		exchange_rate = get_exchange_rate(ref_doc.get("posting_date") or ref_doc.get("transaction_date"),
-			args.get("party_account"), args.get("party_account_currency"),
-			ref_doc.company, ref_doc.doctype, ref_doc.name)
+		exchange_rate = get_exchange_rate(
+			ref_doc.get("posting_date") or ref_doc.get("transaction_date"),
+			args.get("party_account"),
+			args.get("party_account_currency"),
+			ref_doc.company,
+			ref_doc.doctype,
+			ref_doc.name,
+		)
 
 	je = frappe.new_doc("Journal Entry")
-	je.update({
-		"voucher_type": "Bank Entry",
-		"company": ref_doc.company,
-		"remark": args.get("remarks")
-	})
+	je.update(
+		{"voucher_type": "Bank Entry", "company": ref_doc.company, "remark": args.get("remarks")}
+	)
 
-	party_row = je.append("accounts", {
-		"account": args.get("party_account"),
-		"party_type": args.get("party_type"),
-		"party": ref_doc.get(args.get("party_type").lower()),
-		"cost_center": cost_center,
-		"account_type": frappe.db.get_value("Account", args.get("party_account"), "account_type"),
-		"account_currency": args.get("party_account_currency") or \
-							get_account_currency(args.get("party_account")),
-		"balance": get_balance_on(args.get("party_account")),
-		"party_balance": get_balance_on(party=args.get("party"), party_type=args.get("party_type")),
-		"exchange_rate": exchange_rate,
-		args.get("amount_field_party"): args.get("amount"),
-		"is_advance": args.get("is_advance"),
-		"reference_type": ref_doc.doctype,
-		"reference_name": ref_doc.name
-	})
+	party_row = je.append(
+		"accounts",
+		{
+			"account": args.get("party_account"),
+			"party_type": args.get("party_type"),
+			"party": ref_doc.get(args.get("party_type").lower()),
+			"cost_center": cost_center,
+			"account_type": frappe.db.get_value("Account", args.get("party_account"), "account_type"),
+			"account_currency": args.get("party_account_currency")
+			or get_account_currency(args.get("party_account")),
+			"balance": get_balance_on(args.get("party_account")),
+			"party_balance": get_balance_on(party=args.get("party"), party_type=args.get("party_type")),
+			"exchange_rate": exchange_rate,
+			args.get("amount_field_party"): args.get("amount"),
+			"is_advance": args.get("is_advance"),
+			"reference_type": ref_doc.doctype,
+			"reference_name": ref_doc.name,
+		},
+	)
 
 	bank_row = je.append("accounts")
 
 	# Make it bank_details
-	bank_account = get_default_bank_cash_account(ref_doc.company, "Bank", account=args.get("bank_account"))
+	bank_account = get_default_bank_cash_account(
+		ref_doc.company, "Bank", account=args.get("bank_account")
+	)
 	if bank_account:
 		bank_row.update(bank_account)
 		# Modified to include the posting date for which the exchange rate is required.
 		# Assumed to be the posting date of the reference date
-		bank_row.exchange_rate = get_exchange_rate(ref_doc.get("posting_date")
-			or ref_doc.get("transaction_date"), bank_account["account"],
-			bank_account["account_currency"], ref_doc.company)
+		bank_row.exchange_rate = get_exchange_rate(
+			ref_doc.get("posting_date") or ref_doc.get("transaction_date"),
+			bank_account["account"],
+			bank_account["account_currency"],
+			ref_doc.company,
+		)
 
 	bank_row.cost_center = cost_center
 
@@ -957,9 +1193,10 @@
 		bank_row.set(args.get("amount_field_bank"), amount * exchange_rate)
 
 	# Multi currency check again
-	if party_row.account_currency != ref_doc.company_currency \
-		or (bank_row.account_currency and bank_row.account_currency != ref_doc.company_currency):
-			je.multi_currency = 1
+	if party_row.account_currency != ref_doc.company_currency or (
+		bank_row.account_currency and bank_row.account_currency != ref_doc.company_currency
+	):
+		je.multi_currency = 1
 
 	je.set_amounts_in_company_currency()
 	je.set_total_debit_credit()
@@ -970,13 +1207,17 @@
 @frappe.whitelist()
 def get_opening_accounts(company):
 	"""get all balance sheet accounts for opening entry"""
-	accounts = frappe.db.sql_list("""select
+	accounts = frappe.db.sql_list(
+		"""select
 			name from tabAccount
 		where
 			is_group=0 and report_type='Balance Sheet' and company={0} and
 			name not in (select distinct account from tabWarehouse where
 			account is not null and account != '')
-		order by name asc""".format(frappe.db.escape(company)))
+		order by name asc""".format(
+			frappe.db.escape(company)
+		)
+	)
 
 	return [{"account": a, "balance": get_balance_on(a)} for a in accounts]
 
@@ -984,10 +1225,11 @@
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
-	if not frappe.db.has_column('Journal Entry', searchfield):
+	if not frappe.db.has_column("Journal Entry", searchfield):
 		return []
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		SELECT jv.name, jv.posting_date, jv.user_remark
 		FROM `tabJournal Entry` jv, `tabJournal Entry Account` jv_detail
 		WHERE jv_detail.parent = jv.name
@@ -1000,15 +1242,18 @@
 			AND jv.docstatus = 1
 			AND jv.`{0}` LIKE %(txt)s
 		ORDER BY jv.name DESC
-		LIMIT %(offset)s, %(limit)s
-		""".format(searchfield), dict(
-				account=filters.get("account"),
-				party=cstr(filters.get("party")),
-				txt="%{0}%".format(txt),
-				offset=start,
-				limit=page_len
-			)
-		)
+		LIMIT %(limit)s offset %(offset)s
+		""".format(
+			searchfield
+		),
+		dict(
+			account=filters.get("account"),
+			party=cstr(filters.get("party")),
+			txt="%{0}%".format(txt),
+			offset=start,
+			limit=page_len,
+		),
+	)
 
 
 @frappe.whitelist()
@@ -1024,37 +1269,55 @@
 	if args.get("doctype") == "Journal Entry":
 		condition = " and party=%(party)s" if args.get("party") else ""
 
-		against_jv_amount = frappe.db.sql("""
+		against_jv_amount = frappe.db.sql(
+			"""
 			select sum(debit_in_account_currency) - sum(credit_in_account_currency)
 			from `tabJournal Entry Account` where parent=%(docname)s and account=%(account)s {0}
-			and (reference_type is null or reference_type = '')""".format(condition), args)
+			and (reference_type is null or reference_type = '')""".format(
+				condition
+			),
+			args,
+		)
 
 		against_jv_amount = flt(against_jv_amount[0][0]) if against_jv_amount else 0
-		amount_field = "credit_in_account_currency" if against_jv_amount > 0 else "debit_in_account_currency"
-		return {
-			amount_field: abs(against_jv_amount)
-		}
+		amount_field = (
+			"credit_in_account_currency" if against_jv_amount > 0 else "debit_in_account_currency"
+		)
+		return {amount_field: abs(against_jv_amount)}
 	elif args.get("doctype") in ("Sales Invoice", "Purchase Invoice"):
 		party_type = "Customer" if args.get("doctype") == "Sales Invoice" else "Supplier"
-		invoice = frappe.db.get_value(args["doctype"], args["docname"],
-			["outstanding_amount", "conversion_rate", scrub(party_type)], as_dict=1)
+		invoice = frappe.db.get_value(
+			args["doctype"],
+			args["docname"],
+			["outstanding_amount", "conversion_rate", scrub(party_type)],
+			as_dict=1,
+		)
 
-		exchange_rate = invoice.conversion_rate if (args.get("account_currency") != company_currency) else 1
+		exchange_rate = (
+			invoice.conversion_rate if (args.get("account_currency") != company_currency) else 1
+		)
 
 		if args["doctype"] == "Sales Invoice":
-			amount_field = "credit_in_account_currency" \
-				if flt(invoice.outstanding_amount) > 0 else "debit_in_account_currency"
+			amount_field = (
+				"credit_in_account_currency"
+				if flt(invoice.outstanding_amount) > 0
+				else "debit_in_account_currency"
+			)
 		else:
-			amount_field = "debit_in_account_currency" \
-				if flt(invoice.outstanding_amount) > 0 else "credit_in_account_currency"
+			amount_field = (
+				"debit_in_account_currency"
+				if flt(invoice.outstanding_amount) > 0
+				else "credit_in_account_currency"
+			)
 
 		return {
 			amount_field: abs(flt(invoice.outstanding_amount)),
 			"exchange_rate": exchange_rate,
 			"party_type": party_type,
-			"party": invoice.get(scrub(party_type))
+			"party": invoice.get(scrub(party_type)),
 		}
 
+
 @frappe.whitelist()
 def get_party_account_and_balance(company, party_type, party, cost_center=None):
 	if not frappe.has_permission("Account"):
@@ -1063,24 +1326,30 @@
 	account = get_party_account(party_type, party, company)
 
 	account_balance = get_balance_on(account=account, cost_center=cost_center)
-	party_balance = get_balance_on(party_type=party_type, party=party, company=company, cost_center=cost_center)
+	party_balance = get_balance_on(
+		party_type=party_type, party=party, company=company, cost_center=cost_center
+	)
 
 	return {
 		"account": account,
 		"balance": account_balance,
 		"party_balance": party_balance,
-		"account_currency": frappe.db.get_value("Account", account, "account_currency")
+		"account_currency": frappe.db.get_value("Account", account, "account_currency"),
 	}
 
 
 @frappe.whitelist()
-def get_account_balance_and_party_type(account, date, company, debit=None, credit=None, exchange_rate=None, cost_center=None):
+def get_account_balance_and_party_type(
+	account, date, company, debit=None, credit=None, exchange_rate=None, cost_center=None
+):
 	"""Returns dict of account balance and party type to be set in Journal Entry on selection of account."""
 	if not frappe.has_permission("Account"):
 		frappe.msgprint(_("No Permission"), raise_exception=1)
 
 	company_currency = erpnext.get_company_currency(company)
-	account_details = frappe.db.get_value("Account", account, ["account_type", "account_currency"], as_dict=1)
+	account_details = frappe.db.get_value(
+		"Account", account, ["account_type", "account_currency"], as_dict=1
+	)
 
 	if not account_details:
 		return
@@ -1097,11 +1366,17 @@
 		"party_type": party_type,
 		"account_type": account_details.account_type,
 		"account_currency": account_details.account_currency or company_currency,
-
 		# The date used to retreive the exchange rate here is the date passed in
 		# as an argument to this function. It is assumed to be the date on which the balance is sought
-		"exchange_rate": get_exchange_rate(date, account, account_details.account_currency,
-			company, debit=debit, credit=credit, exchange_rate=exchange_rate)
+		"exchange_rate": get_exchange_rate(
+			date,
+			account,
+			account_details.account_currency,
+			company,
+			debit=debit,
+			credit=credit,
+			exchange_rate=exchange_rate,
+		),
 	}
 
 	# un-set party if not party type
@@ -1112,11 +1387,22 @@
 
 
 @frappe.whitelist()
-def get_exchange_rate(posting_date, account=None, account_currency=None, company=None,
-		reference_type=None, reference_name=None, debit=None, credit=None, exchange_rate=None):
+def get_exchange_rate(
+	posting_date,
+	account=None,
+	account_currency=None,
+	company=None,
+	reference_type=None,
+	reference_name=None,
+	debit=None,
+	credit=None,
+	exchange_rate=None,
+):
 	from erpnext.setup.utils import get_exchange_rate
-	account_details = frappe.db.get_value("Account", account,
-		["account_type", "root_type", "account_currency", "company"], as_dict=1)
+
+	account_details = frappe.db.get_value(
+		"Account", account, ["account_type", "root_type", "account_currency", "company"], as_dict=1
+	)
 
 	if not account_details:
 		frappe.throw(_("Please select correct account"))
@@ -1135,7 +1421,7 @@
 
 		# The date used to retreive the exchange rate here is the date passed
 		# in as an argument to this function.
-		elif (not exchange_rate or flt(exchange_rate)==1) and account_currency and posting_date:
+		elif (not exchange_rate or flt(exchange_rate) == 1) and account_currency and posting_date:
 			exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
 	else:
 		exchange_rate = 1
@@ -1154,15 +1440,17 @@
 
 	return exchange_rate
 
+
 @frappe.whitelist()
 def make_inter_company_journal_entry(name, voucher_type, company):
-	journal_entry = frappe.new_doc('Journal Entry')
+	journal_entry = frappe.new_doc("Journal Entry")
 	journal_entry.voucher_type = voucher_type
 	journal_entry.company = company
 	journal_entry.posting_date = nowdate()
 	journal_entry.inter_company_journal_entry_reference = name
 	return journal_entry.as_dict()
 
+
 @frappe.whitelist()
 def make_reverse_journal_entry(source_name, target_doc=None):
 	from frappe.model.mapper import get_mapped_doc
@@ -1170,24 +1458,25 @@
 	def post_process(source, target):
 		target.reversal_of = source.name
 
-	doclist = get_mapped_doc("Journal Entry", source_name, {
-		"Journal Entry": {
-			"doctype": "Journal Entry",
-			"validation": {
-				"docstatus": ["=", 1]
-			}
+	doclist = get_mapped_doc(
+		"Journal Entry",
+		source_name,
+		{
+			"Journal Entry": {"doctype": "Journal Entry", "validation": {"docstatus": ["=", 1]}},
+			"Journal Entry Account": {
+				"doctype": "Journal Entry Account",
+				"field_map": {
+					"account_currency": "account_currency",
+					"exchange_rate": "exchange_rate",
+					"debit_in_account_currency": "credit_in_account_currency",
+					"debit": "credit",
+					"credit_in_account_currency": "debit_in_account_currency",
+					"credit": "debit",
+				},
+			},
 		},
-		"Journal Entry Account": {
-			"doctype": "Journal Entry Account",
-			"field_map": {
-				"account_currency": "account_currency",
-				"exchange_rate": "exchange_rate",
-				"debit_in_account_currency": "credit_in_account_currency",
-				"debit": "credit",
-				"credit_in_account_currency": "debit_in_account_currency",
-				"credit": "debit",
-			}
-		},
-	}, target_doc, post_process)
+		target_doc,
+		post_process,
+	)
 
 	return doclist
diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
index 481462b..2cc5378 100644
--- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
@@ -39,14 +39,25 @@
 		test_voucher.submit()
 
 		if test_voucher.doctype == "Journal Entry":
-			self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
+			self.assertTrue(
+				frappe.db.sql(
+					"""select name from `tabJournal Entry Account`
 				where account = %s and docstatus = 1 and parent = %s""",
-				("_Test Receivable - _TC", test_voucher.name)))
+					("_Test Receivable - _TC", test_voucher.name),
+				)
+			)
 
-		self.assertFalse(frappe.db.sql("""select name from `tabJournal Entry Account`
-			where reference_type = %s and reference_name = %s""", (test_voucher.doctype, test_voucher.name)))
+		self.assertFalse(
+			frappe.db.sql(
+				"""select name from `tabJournal Entry Account`
+			where reference_type = %s and reference_name = %s""",
+				(test_voucher.doctype, test_voucher.name),
+			)
+		)
 
-		base_jv.get("accounts")[0].is_advance = "Yes" if (test_voucher.doctype in ["Sales Order", "Purchase Order"]) else "No"
+		base_jv.get("accounts")[0].is_advance = (
+			"Yes" if (test_voucher.doctype in ["Sales Order", "Purchase Order"]) else "No"
+		)
 		base_jv.get("accounts")[0].set("reference_type", test_voucher.doctype)
 		base_jv.get("accounts")[0].set("reference_name", test_voucher.name)
 		base_jv.insert()
@@ -54,18 +65,28 @@
 
 		submitted_voucher = frappe.get_doc(test_voucher.doctype, test_voucher.name)
 
-		self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
-			where reference_type = %s and reference_name = %s and {0}=400""".format(dr_or_cr),
-				(submitted_voucher.doctype, submitted_voucher.name)))
+		self.assertTrue(
+			frappe.db.sql(
+				"""select name from `tabJournal Entry Account`
+			where reference_type = %s and reference_name = %s and {0}=400""".format(
+					dr_or_cr
+				),
+				(submitted_voucher.doctype, submitted_voucher.name),
+			)
+		)
 
 		if base_jv.get("accounts")[0].is_advance == "Yes":
 			self.advance_paid_testcase(base_jv, submitted_voucher, dr_or_cr)
 		self.cancel_against_voucher_testcase(submitted_voucher)
 
 	def advance_paid_testcase(self, base_jv, test_voucher, dr_or_cr):
-		#Test advance paid field
-		advance_paid = frappe.db.sql("""select advance_paid from `tab%s`
-					where name=%s""" % (test_voucher.doctype, '%s'), (test_voucher.name))
+		# Test advance paid field
+		advance_paid = frappe.db.sql(
+			"""select advance_paid from `tab%s`
+					where name=%s"""
+			% (test_voucher.doctype, "%s"),
+			(test_voucher.name),
+		)
 		payment_against_order = base_jv.get("accounts")[0].get(dr_or_cr)
 
 		self.assertTrue(flt(advance_paid[0][0]) == flt(payment_against_order))
@@ -74,13 +95,19 @@
 		if test_voucher.doctype == "Journal Entry":
 			# if test_voucher is a Journal Entry, test cancellation of test_voucher
 			test_voucher.cancel()
-			self.assertFalse(frappe.db.sql("""select name from `tabJournal Entry Account`
-				where reference_type='Journal Entry' and reference_name=%s""", test_voucher.name))
+			self.assertFalse(
+				frappe.db.sql(
+					"""select name from `tabJournal Entry Account`
+				where reference_type='Journal Entry' and reference_name=%s""",
+					test_voucher.name,
+				)
+			)
 
 		elif test_voucher.doctype in ["Sales Order", "Purchase Order"]:
 			# if test_voucher is a Sales Order/Purchase Order, test error on cancellation of test_voucher
-			frappe.db.set_value("Accounts Settings", "Accounts Settings",
-				"unlink_advance_payment_on_cancelation_of_order", 0)
+			frappe.db.set_value(
+				"Accounts Settings", "Accounts Settings", "unlink_advance_payment_on_cancelation_of_order", 0
+			)
 			submitted_voucher = frappe.get_doc(test_voucher.doctype, test_voucher.name)
 			self.assertRaises(frappe.LinkExistsError, submitted_voucher.cancel)
 
@@ -89,7 +116,10 @@
 		stock_account = get_inventory_account(company)
 
 		from erpnext.accounts.utils import get_stock_and_account_balance
-		account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(stock_account, nowdate(), company)
+
+		account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(
+			stock_account, nowdate(), company
+		)
 		diff = flt(account_bal) - flt(stock_bal)
 
 		if not diff:
@@ -98,19 +128,25 @@
 		jv = frappe.new_doc("Journal Entry")
 		jv.company = company
 		jv.posting_date = nowdate()
-		jv.append("accounts", {
-			"account": stock_account,
-			"cost_center": "Main - TCP1",
-			"debit_in_account_currency": 0 if diff > 0 else abs(diff),
-			"credit_in_account_currency": diff if diff > 0 else 0
-		})
+		jv.append(
+			"accounts",
+			{
+				"account": stock_account,
+				"cost_center": "Main - TCP1",
+				"debit_in_account_currency": 0 if diff > 0 else abs(diff),
+				"credit_in_account_currency": diff if diff > 0 else 0,
+			},
+		)
 
-		jv.append("accounts", {
-			"account": "Stock Adjustment - TCP1",
-			"cost_center": "Main - TCP1",
-			"debit_in_account_currency": diff if diff > 0 else 0,
-			"credit_in_account_currency": 0 if diff > 0 else abs(diff)
-		})
+		jv.append(
+			"accounts",
+			{
+				"account": "Stock Adjustment - TCP1",
+				"cost_center": "Main - TCP1",
+				"debit_in_account_currency": diff if diff > 0 else 0,
+				"credit_in_account_currency": 0 if diff > 0 else abs(diff),
+			},
+		)
 		jv.insert()
 
 		if account_bal == stock_bal:
@@ -121,16 +157,21 @@
 			jv.cancel()
 
 	def test_multi_currency(self):
-		jv = make_journal_entry("_Test Bank USD - _TC",
-			"_Test Bank - _TC", 100, exchange_rate=50, save=False)
+		jv = make_journal_entry(
+			"_Test Bank USD - _TC", "_Test Bank - _TC", 100, exchange_rate=50, save=False
+		)
 
 		jv.get("accounts")[1].credit_in_account_currency = 5000
 		jv.submit()
 
-		gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
+		gl_entries = frappe.db.sql(
+			"""select account, account_currency, debit, credit,
 			debit_in_account_currency, credit_in_account_currency
 			from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
-			order by account asc""", jv.name, as_dict=1)
+			order by account asc""",
+			jv.name,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
@@ -140,33 +181,42 @@
 				"debit": 5000,
 				"debit_in_account_currency": 100,
 				"credit": 0,
-				"credit_in_account_currency": 0
+				"credit_in_account_currency": 0,
 			},
 			"_Test Bank - _TC": {
 				"account_currency": "INR",
 				"debit": 0,
 				"debit_in_account_currency": 0,
 				"credit": 5000,
-				"credit_in_account_currency": 5000
-			}
+				"credit_in_account_currency": 5000,
+			},
 		}
 
-		for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
+		for field in (
+			"account_currency",
+			"debit",
+			"debit_in_account_currency",
+			"credit",
+			"credit_in_account_currency",
+		):
 			for i, gle in enumerate(gl_entries):
 				self.assertEqual(expected_values[gle.account][field], gle[field])
 
 		# cancel
 		jv.cancel()
 
-		gle = frappe.db.sql("""select name from `tabGL Entry`
-			where voucher_type='Sales Invoice' and voucher_no=%s""", jv.name)
+		gle = frappe.db.sql(
+			"""select name from `tabGL Entry`
+			where voucher_type='Sales Invoice' and voucher_no=%s""",
+			jv.name,
+		)
 
 		self.assertFalse(gle)
 
 	def test_reverse_journal_entry(self):
 		from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
-		jv = make_journal_entry("_Test Bank USD - _TC",
-			"Sales - _TC", 100, exchange_rate=50, save=False)
+
+		jv = make_journal_entry("_Test Bank USD - _TC", "Sales - _TC", 100, exchange_rate=50, save=False)
 
 		jv.get("accounts")[1].credit_in_account_currency = 5000
 		jv.get("accounts")[1].exchange_rate = 1
@@ -176,15 +226,17 @@
 		rjv.posting_date = nowdate()
 		rjv.submit()
 
-
-		gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
+		gl_entries = frappe.db.sql(
+			"""select account, account_currency, debit, credit,
 			debit_in_account_currency, credit_in_account_currency
 			from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
-			order by account asc""", rjv.name, as_dict=1)
+			order by account asc""",
+			rjv.name,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
-
 		expected_values = {
 			"_Test Bank USD - _TC": {
 				"account_currency": "USD",
@@ -199,44 +251,38 @@
 				"debit_in_account_currency": 5000,
 				"credit": 0,
 				"credit_in_account_currency": 0,
-			}
+			},
 		}
 
-		for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
+		for field in (
+			"account_currency",
+			"debit",
+			"debit_in_account_currency",
+			"credit",
+			"credit_in_account_currency",
+		):
 			for i, gle in enumerate(gl_entries):
 				self.assertEqual(expected_values[gle.account][field], gle[field])
 
 	def test_disallow_change_in_account_currency_for_a_party(self):
 		# create jv in USD
-		jv = make_journal_entry("_Test Bank USD - _TC",
-			"_Test Receivable USD - _TC", 100, save=False)
+		jv = make_journal_entry("_Test Bank USD - _TC", "_Test Receivable USD - _TC", 100, save=False)
 
-		jv.accounts[1].update({
-			"party_type": "Customer",
-			"party": "_Test Customer USD"
-		})
+		jv.accounts[1].update({"party_type": "Customer", "party": "_Test Customer USD"})
 
 		jv.submit()
 
 		# create jv in USD, but account currency in INR
-		jv = make_journal_entry("_Test Bank - _TC",
-			"_Test Receivable - _TC", 100, save=False)
+		jv = make_journal_entry("_Test Bank - _TC", "_Test Receivable - _TC", 100, save=False)
 
-		jv.accounts[1].update({
-			"party_type": "Customer",
-			"party": "_Test Customer USD"
-		})
+		jv.accounts[1].update({"party_type": "Customer", "party": "_Test Customer USD"})
 
 		self.assertRaises(InvalidAccountCurrency, jv.submit)
 
 		# back in USD
-		jv = make_journal_entry("_Test Bank USD - _TC",
-			"_Test Receivable USD - _TC", 100, save=False)
+		jv = make_journal_entry("_Test Bank USD - _TC", "_Test Receivable USD - _TC", 100, save=False)
 
-		jv.accounts[1].update({
-			"party_type": "Customer",
-			"party": "_Test Customer USD"
-		})
+		jv.accounts[1].update({"party_type": "Customer", "party": "_Test Customer USD"})
 
 		jv.submit()
 
@@ -245,13 +291,27 @@
 		frappe.db.set_value("Account", "Buildings - _TC", "inter_company_account", 1)
 		frappe.db.set_value("Account", "Sales Expenses - _TC1", "inter_company_account", 1)
 		frappe.db.set_value("Account", "Buildings - _TC1", "inter_company_account", 1)
-		jv = make_journal_entry("Sales Expenses - _TC", "Buildings - _TC", 100, posting_date=nowdate(), cost_center = "Main - _TC", save=False)
+		jv = make_journal_entry(
+			"Sales Expenses - _TC",
+			"Buildings - _TC",
+			100,
+			posting_date=nowdate(),
+			cost_center="Main - _TC",
+			save=False,
+		)
 		jv.voucher_type = "Inter Company Journal Entry"
 		jv.multi_currency = 0
 		jv.insert()
 		jv.submit()
 
-		jv1 = make_journal_entry("Sales Expenses - _TC1", "Buildings - _TC1", 100, posting_date=nowdate(), cost_center = "Main - _TC1", save=False)
+		jv1 = make_journal_entry(
+			"Sales Expenses - _TC1",
+			"Buildings - _TC1",
+			100,
+			posting_date=nowdate(),
+			cost_center="Main - _TC1",
+			save=False,
+		)
 		jv1.inter_company_journal_entry_reference = jv.name
 		jv1.company = "_Test Company 1"
 		jv1.voucher_type = "Inter Company Journal Entry"
@@ -273,9 +333,12 @@
 
 	def test_jv_with_cost_centre(self):
 		from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
+
 		cost_center = "_Test Cost Center for BS Account - _TC"
 		create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
-		jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center = cost_center, save=False)
+		jv = make_journal_entry(
+			"_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center=cost_center, save=False
+		)
 		jv.voucher_type = "Bank Entry"
 		jv.multi_currency = 0
 		jv.cheque_no = "112233"
@@ -284,17 +347,17 @@
 		jv.submit()
 
 		expected_values = {
-			"_Test Cash - _TC": {
-				"cost_center": cost_center
-			},
-			"_Test Bank - _TC": {
-				"cost_center": cost_center
-			}
+			"_Test Cash - _TC": {"cost_center": cost_center},
+			"_Test Bank - _TC": {"cost_center": cost_center},
 		}
 
-		gl_entries = frappe.db.sql("""select account, cost_center, debit, credit
+		gl_entries = frappe.db.sql(
+			"""select account, cost_center, debit, credit
 			from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
-			order by account asc""", jv.name, as_dict=1)
+			order by account asc""",
+			jv.name,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
@@ -305,11 +368,13 @@
 		from erpnext.projects.doctype.project.test_project import make_project
 
 		if not frappe.db.exists("Project", {"project_name": "Journal Entry Project"}):
-			project = make_project({
-				'project_name': 'Journal Entry Project',
-				'project_template_name': 'Test Project Template',
-				'start_date': '2020-01-01'
-			})
+			project = make_project(
+				{
+					"project_name": "Journal Entry Project",
+					"project_template_name": "Test Project Template",
+					"start_date": "2020-01-01",
+				}
+			)
 			project_name = project.name
 		else:
 			project_name = frappe.get_value("Project", {"project_name": "_Test Project"})
@@ -325,17 +390,17 @@
 		jv.submit()
 
 		expected_values = {
-			"_Test Cash - _TC": {
-				"project": project_name
-			},
-			"_Test Bank - _TC": {
-				"project": project_name
-			}
+			"_Test Cash - _TC": {"project": project_name},
+			"_Test Bank - _TC": {"project": project_name},
 		}
 
-		gl_entries = frappe.db.sql("""select account, project, debit, credit
+		gl_entries = frappe.db.sql(
+			"""select account, project, debit, credit
 			from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
-			order by account asc""", jv.name, as_dict=1)
+			order by account asc""",
+			jv.name,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
@@ -345,9 +410,12 @@
 	def test_jv_account_and_party_balance_with_cost_centre(self):
 		from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
 		from erpnext.accounts.utils import get_balance_on
+
 		cost_center = "_Test Cost Center for BS Account - _TC"
 		create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
-		jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center = cost_center, save=False)
+		jv = make_journal_entry(
+			"_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center=cost_center, save=False
+		)
 		account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=cost_center)
 		jv.voucher_type = "Bank Entry"
 		jv.multi_currency = 0
@@ -360,7 +428,18 @@
 		account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=cost_center)
 		self.assertEqual(expected_account_balance, account_balance)
 
-def make_journal_entry(account1, account2, amount, cost_center=None, posting_date=None, exchange_rate=1, save=True, submit=False, project=None):
+
+def make_journal_entry(
+	account1,
+	account2,
+	amount,
+	cost_center=None,
+	posting_date=None,
+	exchange_rate=1,
+	save=True,
+	submit=False,
+	project=None,
+):
 	if not cost_center:
 		cost_center = "_Test Cost Center - _TC"
 
@@ -369,23 +448,27 @@
 	jv.company = "_Test Company"
 	jv.user_remark = "test"
 	jv.multi_currency = 1
-	jv.set("accounts", [
-		{
-			"account": account1,
-			"cost_center": cost_center,
-			"project": project,
-			"debit_in_account_currency": amount if amount > 0 else 0,
-			"credit_in_account_currency": abs(amount) if amount < 0 else 0,
-			"exchange_rate": exchange_rate
-		}, {
-			"account": account2,
-			"cost_center": cost_center,
-			"project": project,
-			"credit_in_account_currency": amount if amount > 0 else 0,
-			"debit_in_account_currency": abs(amount) if amount < 0 else 0,
-			"exchange_rate": exchange_rate
-		}
-	])
+	jv.set(
+		"accounts",
+		[
+			{
+				"account": account1,
+				"cost_center": cost_center,
+				"project": project,
+				"debit_in_account_currency": amount if amount > 0 else 0,
+				"credit_in_account_currency": abs(amount) if amount < 0 else 0,
+				"exchange_rate": exchange_rate,
+			},
+			{
+				"account": account2,
+				"cost_center": cost_center,
+				"project": project,
+				"credit_in_account_currency": amount if amount > 0 else 0,
+				"debit_in_account_currency": abs(amount) if amount < 0 else 0,
+				"exchange_rate": exchange_rate,
+			},
+		],
+	)
 	if save or submit:
 		jv.insert()
 
@@ -394,4 +477,5 @@
 
 	return jv
 
-test_records = frappe.get_test_records('Journal Entry')
+
+test_records = frappe.get_test_records("Journal Entry")
diff --git a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py
index 2da72c2..b8ef354 100644
--- a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py
+++ b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py
@@ -9,6 +9,7 @@
 class JournalEntryTemplate(Document):
 	pass
 
+
 @frappe.whitelist()
 def get_naming_series():
 	return frappe.get_meta("Journal Entry").get_field("naming_series").options
diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.py b/erpnext/accounts/doctype/ledger_merge/ledger_merge.py
index 830ad37..18e5a1a 100644
--- a/erpnext/accounts/doctype/ledger_merge/ledger_merge.py
+++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.py
@@ -15,9 +15,7 @@
 		from frappe.utils.scheduler import is_scheduler_inactive
 
 		if is_scheduler_inactive() and not frappe.flags.in_test:
-			frappe.throw(
-				_("Scheduler is inactive. Cannot merge accounts."), title=_("Scheduler Inactive")
-			)
+			frappe.throw(_("Scheduler is inactive. Cannot merge accounts."), title=_("Scheduler Inactive"))
 
 		enqueued_jobs = [d.get("job_name") for d in get_info()]
 
@@ -35,10 +33,12 @@
 
 		return False
 
+
 @frappe.whitelist()
 def form_start_merge(docname):
 	return frappe.get_doc("Ledger Merge", docname).start_merge()
 
+
 def start_merge(docname):
 	ledger_merge = frappe.get_doc("Ledger Merge", docname)
 	successful_merges = 0
@@ -51,26 +51,24 @@
 					ledger_merge.account,
 					ledger_merge.is_group,
 					ledger_merge.root_type,
-					ledger_merge.company
+					ledger_merge.company,
 				)
-				row.db_set('merged', 1)
+				row.db_set("merged", 1)
 				frappe.db.commit()
 				successful_merges += 1
-				frappe.publish_realtime("ledger_merge_progress", {
-						"ledger_merge": ledger_merge.name,
-						"current": successful_merges,
-						"total": total
-					}
+				frappe.publish_realtime(
+					"ledger_merge_progress",
+					{"ledger_merge": ledger_merge.name, "current": successful_merges, "total": total},
 				)
 			except Exception:
 				frappe.db.rollback()
-				frappe.log_error(title=ledger_merge.name)
+				ledger_merge.log_error("Ledger merge failed")
 			finally:
 				if successful_merges == total:
-					ledger_merge.db_set('status', 'Success')
+					ledger_merge.db_set("status", "Success")
 				elif successful_merges > 0:
-					ledger_merge.db_set('status', 'Partial Success')
+					ledger_merge.db_set("status", "Partial Success")
 				else:
-					ledger_merge.db_set('status', 'Error')
+					ledger_merge.db_set("status", "Error")
 
 	frappe.publish_realtime("ledger_merge_refresh", {"ledger_merge": ledger_merge.name})
diff --git a/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py b/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py
index f731536..992ce9e 100644
--- a/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py
+++ b/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py
@@ -31,18 +31,17 @@
 			acc.company = "_Test Company"
 			acc.insert()
 
-		doc = frappe.get_doc({
-			"doctype": "Ledger Merge",
-			"company": "_Test Company",
-			"root_type": frappe.db.get_value("Account", "Indirect Test Expenses - _TC", "root_type"),
-			"account": "Indirect Expenses - _TC",
-			"merge_accounts": [
-				{
-					"account": "Indirect Test Expenses - _TC",
-					"account_name": "Indirect Expenses"
-				}
-			]
-		}).insert(ignore_permissions=True)
+		doc = frappe.get_doc(
+			{
+				"doctype": "Ledger Merge",
+				"company": "_Test Company",
+				"root_type": frappe.db.get_value("Account", "Indirect Test Expenses - _TC", "root_type"),
+				"account": "Indirect Expenses - _TC",
+				"merge_accounts": [
+					{"account": "Indirect Test Expenses - _TC", "account_name": "Indirect Expenses"}
+				],
+			}
+		).insert(ignore_permissions=True)
 
 		parent = frappe.db.get_value("Account", "Administrative Test Expenses - _TC", "parent_account")
 		self.assertEqual(parent, "Indirect Test Expenses - _TC")
@@ -76,22 +75,18 @@
 			acc.company = "_Test Company"
 			acc.insert()
 
-		doc = frappe.get_doc({
-			"doctype": "Ledger Merge",
-			"company": "_Test Company",
-			"root_type": frappe.db.get_value("Account", "Indirect Income - _TC", "root_type"),
-			"account": "Indirect Income - _TC",
-			"merge_accounts": [
-				{
-					"account": "Indirect Test Income - _TC",
-					"account_name": "Indirect Test Income"
-				},
-				{
-					"account": "Administrative Test Income - _TC",
-					"account_name": "Administrative Test Income"
-				}
-			]
-		}).insert(ignore_permissions=True)
+		doc = frappe.get_doc(
+			{
+				"doctype": "Ledger Merge",
+				"company": "_Test Company",
+				"root_type": frappe.db.get_value("Account", "Indirect Income - _TC", "root_type"),
+				"account": "Indirect Income - _TC",
+				"merge_accounts": [
+					{"account": "Indirect Test Income - _TC", "account_name": "Indirect Test Income"},
+					{"account": "Administrative Test Income - _TC", "account_name": "Administrative Test Income"},
+				],
+			}
+		).insert(ignore_permissions=True)
 
 		parent = frappe.db.get_value("Account", "Administrative Test Income - _TC", "parent_account")
 		self.assertEqual(parent, "Indirect Test Income - _TC")
@@ -112,7 +107,7 @@
 			"Indirect Test Expenses - _TC",
 			"Administrative Test Expenses - _TC",
 			"Indirect Test Income - _TC",
-			"Administrative Test Income - _TC"
+			"Administrative Test Income - _TC",
 		]
 		for account in test_accounts:
 			frappe.delete_doc_if_exists("Account", account)
diff --git a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
index f460b9f..dcb43fb 100644
--- a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
+++ b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
@@ -8,6 +8,7 @@
 
 exclude_from_linked_with = True
 
+
 class LoyaltyPointEntry(Document):
 	pass
 
@@ -16,18 +17,28 @@
 	if not expiry_date:
 		expiry_date = today()
 
-	return frappe.db.sql('''
+	return frappe.db.sql(
+		"""
 		select name, loyalty_points, expiry_date, loyalty_program_tier, invoice_type, invoice
 		from `tabLoyalty Point Entry`
 		where customer=%s and loyalty_program=%s
 			and expiry_date>=%s and loyalty_points>0 and company=%s
 		order by expiry_date
-	''', (customer, loyalty_program, expiry_date, company), as_dict=1)
+	""",
+		(customer, loyalty_program, expiry_date, company),
+		as_dict=1,
+	)
+
 
 def get_redemption_details(customer, loyalty_program, company):
-	return frappe._dict(frappe.db.sql('''
+	return frappe._dict(
+		frappe.db.sql(
+			"""
 		select redeem_against, sum(loyalty_points)
 		from `tabLoyalty Point Entry`
 		where customer=%s and loyalty_program=%s and loyalty_points<0 and company=%s
 		group by redeem_against
-	''', (customer, loyalty_program, company)))
+	""",
+			(customer, loyalty_program, company),
+		)
+	)
diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py
index 70da03b..48a25ad 100644
--- a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py
+++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py
@@ -12,39 +12,61 @@
 	pass
 
 
-def get_loyalty_details(customer, loyalty_program, expiry_date=None, company=None, include_expired_entry=False):
+def get_loyalty_details(
+	customer, loyalty_program, expiry_date=None, company=None, include_expired_entry=False
+):
 	if not expiry_date:
 		expiry_date = today()
 
-	condition = ''
+	condition = ""
 	if company:
 		condition = " and company=%s " % frappe.db.escape(company)
 	if not include_expired_entry:
 		condition += " and expiry_date>='%s' " % expiry_date
 
-	loyalty_point_details = frappe.db.sql('''select sum(loyalty_points) as loyalty_points,
+	loyalty_point_details = frappe.db.sql(
+		"""select sum(loyalty_points) as loyalty_points,
 		sum(purchase_amount) as total_spent from `tabLoyalty Point Entry`
 		where customer=%s and loyalty_program=%s and posting_date <= %s
 		{condition}
-		group by customer'''.format(condition=condition),
-		(customer, loyalty_program, expiry_date), as_dict=1)
+		group by customer""".format(
+			condition=condition
+		),
+		(customer, loyalty_program, expiry_date),
+		as_dict=1,
+	)
 
 	if loyalty_point_details:
 		return loyalty_point_details[0]
 	else:
 		return {"loyalty_points": 0, "total_spent": 0}
 
-@frappe.whitelist()
-def get_loyalty_program_details_with_points(customer, loyalty_program=None, expiry_date=None, company=None, \
-		silent=False, include_expired_entry=False, current_transaction_amount=0):
-	lp_details = get_loyalty_program_details(customer, loyalty_program, company=company, silent=silent)
-	loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program)
-	lp_details.update(get_loyalty_details(customer, loyalty_program.name, expiry_date, company, include_expired_entry))
 
-	tier_spent_level = sorted([d.as_dict() for d in loyalty_program.collection_rules],
-		key=lambda rule:rule.min_spent, reverse=True)
+@frappe.whitelist()
+def get_loyalty_program_details_with_points(
+	customer,
+	loyalty_program=None,
+	expiry_date=None,
+	company=None,
+	silent=False,
+	include_expired_entry=False,
+	current_transaction_amount=0,
+):
+	lp_details = get_loyalty_program_details(
+		customer, loyalty_program, company=company, silent=silent
+	)
+	loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program)
+	lp_details.update(
+		get_loyalty_details(customer, loyalty_program.name, expiry_date, company, include_expired_entry)
+	)
+
+	tier_spent_level = sorted(
+		[d.as_dict() for d in loyalty_program.collection_rules],
+		key=lambda rule: rule.min_spent,
+		reverse=True,
+	)
 	for i, d in enumerate(tier_spent_level):
-		if i==0 or (lp_details.total_spent+current_transaction_amount) <= d.min_spent:
+		if i == 0 or (lp_details.total_spent + current_transaction_amount) <= d.min_spent:
 			lp_details.tier_name = d.tier_name
 			lp_details.collection_factor = d.collection_factor
 		else:
@@ -52,8 +74,16 @@
 
 	return lp_details
 
+
 @frappe.whitelist()
-def get_loyalty_program_details(customer, loyalty_program=None, expiry_date=None, company=None, silent=False, include_expired_entry=False):
+def get_loyalty_program_details(
+	customer,
+	loyalty_program=None,
+	expiry_date=None,
+	company=None,
+	silent=False,
+	include_expired_entry=False,
+):
 	lp_details = frappe._dict()
 
 	if not loyalty_program:
@@ -72,6 +102,7 @@
 	lp_details.update(loyalty_program.as_dict())
 	return lp_details
 
+
 @frappe.whitelist()
 def get_redeemption_factor(loyalty_program=None, customer=None):
 	customer_loyalty_program = None
@@ -98,13 +129,16 @@
 	else:
 		loyalty_program = frappe.db.get_value("Customer", ref_doc.customer, ["loyalty_program"])
 
-	if loyalty_program and frappe.db.get_value("Loyalty Program", loyalty_program, ["company"]) !=\
-		ref_doc.company:
+	if (
+		loyalty_program
+		and frappe.db.get_value("Loyalty Program", loyalty_program, ["company"]) != ref_doc.company
+	):
 		frappe.throw(_("The Loyalty Program isn't valid for the selected company"))
 
 	if loyalty_program and points_to_redeem:
-		loyalty_program_details = get_loyalty_program_details_with_points(ref_doc.customer, loyalty_program,
-			posting_date, ref_doc.company)
+		loyalty_program_details = get_loyalty_program_details_with_points(
+			ref_doc.customer, loyalty_program, posting_date, ref_doc.company
+		)
 
 		if points_to_redeem > loyalty_program_details.loyalty_points:
 			frappe.throw(_("You don't have enought Loyalty Points to redeem"))
diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program_dashboard.py b/erpnext/accounts/doctype/loyalty_program/loyalty_program_dashboard.py
index 25328e5..3a4f908 100644
--- a/erpnext/accounts/doctype/loyalty_program/loyalty_program_dashboard.py
+++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program_dashboard.py
@@ -1,9 +1,5 @@
 def get_data():
 	return {
-		'fieldname': 'loyalty_program',
-		'transactions': [
-			{
-				'items': ['Sales Invoice', 'Customer']
-			}
-		]
+		"fieldname": "loyalty_program",
+		"transactions": [{"items": ["Sales Invoice", "Customer"]}],
 	}
diff --git a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
index 82c1432..3641ac4 100644
--- a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
+++ b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
@@ -19,19 +19,28 @@
 		create_records()
 
 	def test_loyalty_points_earned_single_tier(self):
-		frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
+		frappe.db.set_value(
+			"Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty"
+		)
 		# create a new sales invoice
 		si_original = create_sales_invoice_record()
 		si_original.insert()
 		si_original.submit()
 
-		customer = frappe.get_doc('Customer', {"customer_name": "Test Loyalty Customer"})
+		customer = frappe.get_doc("Customer", {"customer_name": "Test Loyalty Customer"})
 		earned_points = get_points_earned(si_original)
 
-		lpe = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer})
+		lpe = frappe.get_doc(
+			"Loyalty Point Entry",
+			{
+				"invoice_type": "Sales Invoice",
+				"invoice": si_original.name,
+				"customer": si_original.customer,
+			},
+		)
 
-		self.assertEqual(si_original.get('loyalty_program'), customer.loyalty_program)
-		self.assertEqual(lpe.get('loyalty_program_tier'), customer.loyalty_program_tier)
+		self.assertEqual(si_original.get("loyalty_program"), customer.loyalty_program)
+		self.assertEqual(lpe.get("loyalty_program_tier"), customer.loyalty_program_tier)
 		self.assertEqual(lpe.loyalty_points, earned_points)
 
 		# add redemption point
@@ -43,21 +52,31 @@
 
 		earned_after_redemption = get_points_earned(si_redeem)
 
-		lpe_redeem = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'redeem_against': lpe.name})
-		lpe_earn = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'name': ['!=', lpe_redeem.name]})
+		lpe_redeem = frappe.get_doc(
+			"Loyalty Point Entry",
+			{"invoice_type": "Sales Invoice", "invoice": si_redeem.name, "redeem_against": lpe.name},
+		)
+		lpe_earn = frappe.get_doc(
+			"Loyalty Point Entry",
+			{"invoice_type": "Sales Invoice", "invoice": si_redeem.name, "name": ["!=", lpe_redeem.name]},
+		)
 
 		self.assertEqual(lpe_earn.loyalty_points, earned_after_redemption)
-		self.assertEqual(lpe_redeem.loyalty_points, (-1*earned_points))
+		self.assertEqual(lpe_redeem.loyalty_points, (-1 * earned_points))
 
 		# cancel and delete
 		for d in [si_redeem, si_original]:
 			d.cancel()
 
 	def test_loyalty_points_earned_multiple_tier(self):
-		frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Multiple Loyalty")
+		frappe.db.set_value(
+			"Customer", "Test Loyalty Customer", "loyalty_program", "Test Multiple Loyalty"
+		)
 		# assign multiple tier program to the customer
-		customer = frappe.get_doc('Customer', {"customer_name": "Test Loyalty Customer"})
-		customer.loyalty_program = frappe.get_doc('Loyalty Program', {'loyalty_program_name': 'Test Multiple Loyalty'}).name
+		customer = frappe.get_doc("Customer", {"customer_name": "Test Loyalty Customer"})
+		customer.loyalty_program = frappe.get_doc(
+			"Loyalty Program", {"loyalty_program_name": "Test Multiple Loyalty"}
+		).name
 		customer.save()
 
 		# create a new sales invoice
@@ -67,10 +86,17 @@
 
 		earned_points = get_points_earned(si_original)
 
-		lpe = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer})
+		lpe = frappe.get_doc(
+			"Loyalty Point Entry",
+			{
+				"invoice_type": "Sales Invoice",
+				"invoice": si_original.name,
+				"customer": si_original.customer,
+			},
+		)
 
-		self.assertEqual(si_original.get('loyalty_program'), customer.loyalty_program)
-		self.assertEqual(lpe.get('loyalty_program_tier'), customer.loyalty_program_tier)
+		self.assertEqual(si_original.get("loyalty_program"), customer.loyalty_program)
+		self.assertEqual(lpe.get("loyalty_program_tier"), customer.loyalty_program_tier)
 		self.assertEqual(lpe.loyalty_points, earned_points)
 
 		# add redemption point
@@ -80,14 +106,20 @@
 		si_redeem.insert()
 		si_redeem.submit()
 
-		customer = frappe.get_doc('Customer', {"customer_name": "Test Loyalty Customer"})
+		customer = frappe.get_doc("Customer", {"customer_name": "Test Loyalty Customer"})
 		earned_after_redemption = get_points_earned(si_redeem)
 
-		lpe_redeem = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'redeem_against': lpe.name})
-		lpe_earn = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'name': ['!=', lpe_redeem.name]})
+		lpe_redeem = frappe.get_doc(
+			"Loyalty Point Entry",
+			{"invoice_type": "Sales Invoice", "invoice": si_redeem.name, "redeem_against": lpe.name},
+		)
+		lpe_earn = frappe.get_doc(
+			"Loyalty Point Entry",
+			{"invoice_type": "Sales Invoice", "invoice": si_redeem.name, "name": ["!=", lpe_redeem.name]},
+		)
 
 		self.assertEqual(lpe_earn.loyalty_points, earned_after_redemption)
-		self.assertEqual(lpe_redeem.loyalty_points, (-1*earned_points))
+		self.assertEqual(lpe_redeem.loyalty_points, (-1 * earned_points))
 		self.assertEqual(lpe_earn.loyalty_program_tier, customer.loyalty_program_tier)
 
 		# cancel and delete
@@ -95,23 +127,30 @@
 			d.cancel()
 
 	def test_cancel_sales_invoice(self):
-		''' cancelling the sales invoice should cancel the earned points'''
-		frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
+		"""cancelling the sales invoice should cancel the earned points"""
+		frappe.db.set_value(
+			"Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty"
+		)
 		# create a new sales invoice
 		si = create_sales_invoice_record()
 		si.insert()
 		si.submit()
 
-		lpe = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si.name, 'customer': si.customer})
+		lpe = frappe.get_doc(
+			"Loyalty Point Entry",
+			{"invoice_type": "Sales Invoice", "invoice": si.name, "customer": si.customer},
+		)
 		self.assertEqual(True, not (lpe is None))
 
 		# cancelling sales invoice
 		si.cancel()
-		lpe = frappe.db.exists('Loyalty Point Entry', lpe.name)
+		lpe = frappe.db.exists("Loyalty Point Entry", lpe.name)
 		self.assertEqual(True, (lpe is None))
 
 	def test_sales_invoice_return(self):
-		frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
+		frappe.db.set_value(
+			"Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty"
+		)
 		# create a new sales invoice
 		si_original = create_sales_invoice_record(2)
 		si_original.conversion_rate = flt(1)
@@ -119,7 +158,14 @@
 		si_original.submit()
 
 		earned_points = get_points_earned(si_original)
-		lpe_original = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer})
+		lpe_original = frappe.get_doc(
+			"Loyalty Point Entry",
+			{
+				"invoice_type": "Sales Invoice",
+				"invoice": si_original.name,
+				"customer": si_original.customer,
+			},
+		)
 		self.assertEqual(lpe_original.loyalty_points, earned_points)
 
 		# create sales invoice return
@@ -131,10 +177,17 @@
 		si_return.submit()
 
 		# fetch original invoice again as its status would have been updated
-		si_original = frappe.get_doc('Sales Invoice', lpe_original.invoice)
+		si_original = frappe.get_doc("Sales Invoice", lpe_original.invoice)
 
 		earned_points = get_points_earned(si_original)
-		lpe_after_return = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer})
+		lpe_after_return = frappe.get_doc(
+			"Loyalty Point Entry",
+			{
+				"invoice_type": "Sales Invoice",
+				"invoice": si_original.name,
+				"customer": si_original.customer,
+			},
+		)
 		self.assertEqual(lpe_after_return.loyalty_points, earned_points)
 		self.assertEqual(True, (lpe_original.loyalty_points > lpe_after_return.loyalty_points))
 
@@ -143,144 +196,164 @@
 			try:
 				d.cancel()
 			except frappe.TimestampMismatchError:
-				frappe.get_doc('Sales Invoice', d.name).cancel()
+				frappe.get_doc("Sales Invoice", d.name).cancel()
 
 	def test_loyalty_points_for_dashboard(self):
-		doc = frappe.get_doc('Customer', 'Test Loyalty Customer')
+		doc = frappe.get_doc("Customer", "Test Loyalty Customer")
 		company_wise_info = get_dashboard_info("Customer", doc.name, doc.loyalty_program)
 
 		for d in company_wise_info:
 			self.assertTrue(d.get("loyalty_points"))
 
+
 def get_points_earned(self):
 	def get_returned_amount():
-		returned_amount = frappe.db.sql("""
+		returned_amount = frappe.db.sql(
+			"""
 			select sum(grand_total)
 			from `tabSales Invoice`
 			where docstatus=1 and is_return=1 and ifnull(return_against, '')=%s
-		""", self.name)
+		""",
+			self.name,
+		)
 		return abs(flt(returned_amount[0][0])) if returned_amount else 0
 
-	lp_details = get_loyalty_program_details_with_points(self.customer, company=self.company,
-		loyalty_program=self.loyalty_program, expiry_date=self.posting_date, include_expired_entry=True)
-	if lp_details and getdate(lp_details.from_date) <= getdate(self.posting_date) and \
-		(not lp_details.to_date or getdate(lp_details.to_date) >= getdate(self.posting_date)):
+	lp_details = get_loyalty_program_details_with_points(
+		self.customer,
+		company=self.company,
+		loyalty_program=self.loyalty_program,
+		expiry_date=self.posting_date,
+		include_expired_entry=True,
+	)
+	if (
+		lp_details
+		and getdate(lp_details.from_date) <= getdate(self.posting_date)
+		and (not lp_details.to_date or getdate(lp_details.to_date) >= getdate(self.posting_date))
+	):
 		returned_amount = get_returned_amount()
 		eligible_amount = flt(self.grand_total) - cint(self.loyalty_amount) - returned_amount
-		points_earned = cint(eligible_amount/lp_details.collection_factor)
+		points_earned = cint(eligible_amount / lp_details.collection_factor)
 
 	return points_earned or 0
 
+
 def create_sales_invoice_record(qty=1):
 	# return sales invoice doc object
-	return frappe.get_doc({
-		"doctype": "Sales Invoice",
-		"customer": frappe.get_doc('Customer', {"customer_name": "Test Loyalty Customer"}).name,
-		"company": '_Test Company',
-		"due_date": today(),
-		"posting_date": today(),
-		"currency": "INR",
-		"taxes_and_charges": "",
-		"debit_to": "Debtors - _TC",
-		"taxes": [],
-		"items": [{
-			'doctype': 'Sales Invoice Item',
-			'item_code': frappe.get_doc('Item', {'item_name': 'Loyal Item'}).name,
-			'qty': qty,
-			"rate": 10000,
-			'income_account': 'Sales - _TC',
-			'cost_center': 'Main - _TC',
-			'expense_account': 'Cost of Goods Sold - _TC'
-		}]
-	})
+	return frappe.get_doc(
+		{
+			"doctype": "Sales Invoice",
+			"customer": frappe.get_doc("Customer", {"customer_name": "Test Loyalty Customer"}).name,
+			"company": "_Test Company",
+			"due_date": today(),
+			"posting_date": today(),
+			"currency": "INR",
+			"taxes_and_charges": "",
+			"debit_to": "Debtors - _TC",
+			"taxes": [],
+			"items": [
+				{
+					"doctype": "Sales Invoice Item",
+					"item_code": frappe.get_doc("Item", {"item_name": "Loyal Item"}).name,
+					"qty": qty,
+					"rate": 10000,
+					"income_account": "Sales - _TC",
+					"cost_center": "Main - _TC",
+					"expense_account": "Cost of Goods Sold - _TC",
+				}
+			],
+		}
+	)
+
 
 def create_records():
 	# create a new loyalty Account
 	if not frappe.db.exists("Account", "Loyalty - _TC"):
-		frappe.get_doc({
-			"doctype": "Account",
-			"account_name": "Loyalty",
-			"parent_account": "Direct Expenses - _TC",
-			"company": "_Test Company",
-			"is_group": 0,
-			"account_type": "Expense Account",
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Account",
+				"account_name": "Loyalty",
+				"parent_account": "Direct Expenses - _TC",
+				"company": "_Test Company",
+				"is_group": 0,
+				"account_type": "Expense Account",
+			}
+		).insert()
 
 	# create a new loyalty program Single tier
-	if not frappe.db.exists("Loyalty Program","Test Single Loyalty"):
-		frappe.get_doc({
-			"doctype": "Loyalty Program",
-			"loyalty_program_name": "Test Single Loyalty",
-			"auto_opt_in": 1,
-			"from_date": today(),
-			"loyalty_program_type": "Single Tier Program",
-			"conversion_factor": 1,
-			"expiry_duration": 10,
-			"company": "_Test Company",
-			"cost_center": "Main - _TC",
-			"expense_account": "Loyalty - _TC",
-			"collection_rules": [{
-				'tier_name': 'Silver',
-				'collection_factor': 1000,
-				'min_spent': 1000
-			}]
-		}).insert()
+	if not frappe.db.exists("Loyalty Program", "Test Single Loyalty"):
+		frappe.get_doc(
+			{
+				"doctype": "Loyalty Program",
+				"loyalty_program_name": "Test Single Loyalty",
+				"auto_opt_in": 1,
+				"from_date": today(),
+				"loyalty_program_type": "Single Tier Program",
+				"conversion_factor": 1,
+				"expiry_duration": 10,
+				"company": "_Test Company",
+				"cost_center": "Main - _TC",
+				"expense_account": "Loyalty - _TC",
+				"collection_rules": [{"tier_name": "Silver", "collection_factor": 1000, "min_spent": 1000}],
+			}
+		).insert()
 
 	# create a new customer
-	if not frappe.db.exists("Customer","Test Loyalty Customer"):
-		frappe.get_doc({
-			"customer_group": "_Test Customer Group",
-			"customer_name": "Test Loyalty Customer",
-			"customer_type": "Individual",
-			"doctype": "Customer",
-			"territory": "_Test Territory"
-		}).insert()
+	if not frappe.db.exists("Customer", "Test Loyalty Customer"):
+		frappe.get_doc(
+			{
+				"customer_group": "_Test Customer Group",
+				"customer_name": "Test Loyalty Customer",
+				"customer_type": "Individual",
+				"doctype": "Customer",
+				"territory": "_Test Territory",
+			}
+		).insert()
 
 	# create a new loyalty program Multiple tier
-	if not frappe.db.exists("Loyalty Program","Test Multiple Loyalty"):
-		frappe.get_doc({
-			"doctype": "Loyalty Program",
-			"loyalty_program_name": "Test Multiple Loyalty",
-			"auto_opt_in": 1,
-			"from_date": today(),
-			"loyalty_program_type": "Multiple Tier Program",
-			"conversion_factor": 1,
-			"expiry_duration": 10,
-			"company": "_Test Company",
-			"cost_center": "Main - _TC",
-			"expense_account": "Loyalty - _TC",
-			"collection_rules": [
-				{
-					'tier_name': 'Silver',
-					'collection_factor': 1000,
-					'min_spent': 10000
-				},
-				{
-					'tier_name': 'Gold',
-					'collection_factor': 1000,
-					'min_spent': 19000
-				}
-			]
-		}).insert()
+	if not frappe.db.exists("Loyalty Program", "Test Multiple Loyalty"):
+		frappe.get_doc(
+			{
+				"doctype": "Loyalty Program",
+				"loyalty_program_name": "Test Multiple Loyalty",
+				"auto_opt_in": 1,
+				"from_date": today(),
+				"loyalty_program_type": "Multiple Tier Program",
+				"conversion_factor": 1,
+				"expiry_duration": 10,
+				"company": "_Test Company",
+				"cost_center": "Main - _TC",
+				"expense_account": "Loyalty - _TC",
+				"collection_rules": [
+					{"tier_name": "Silver", "collection_factor": 1000, "min_spent": 10000},
+					{"tier_name": "Gold", "collection_factor": 1000, "min_spent": 19000},
+				],
+			}
+		).insert()
 
 	# create an item
 	if not frappe.db.exists("Item", "Loyal Item"):
-		frappe.get_doc({
-			"doctype": "Item",
-			"item_code": "Loyal Item",
-			"item_name": "Loyal Item",
-			"item_group": "All Item Groups",
-			"company": "_Test Company",
-			"is_stock_item": 1,
-			"opening_stock": 100,
-			"valuation_rate": 10000,
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Item",
+				"item_code": "Loyal Item",
+				"item_name": "Loyal Item",
+				"item_group": "All Item Groups",
+				"company": "_Test Company",
+				"is_stock_item": 1,
+				"opening_stock": 100,
+				"valuation_rate": 10000,
+			}
+		).insert()
 
 	# create item price
-	if not frappe.db.exists("Item Price", {"price_list": "Standard Selling", "item_code": "Loyal Item"}):
-		frappe.get_doc({
-			"doctype": "Item Price",
-			"price_list": "Standard Selling",
-			"item_code": "Loyal Item",
-			"price_list_rate": 10000
-		}).insert()
+	if not frappe.db.exists(
+		"Item Price", {"price_list": "Standard Selling", "item_code": "Loyal Item"}
+	):
+		frappe.get_doc(
+			{
+				"doctype": "Item Price",
+				"price_list": "Standard Selling",
+				"item_code": "Loyal Item",
+				"price_list_rate": 10000,
+			}
+		).insert()
diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py
index f21d1b9..ed35d1e 100644
--- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py
+++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py
@@ -19,23 +19,30 @@
 		for entry in self.accounts:
 			accounts_list.append(entry.company)
 
-		if len(accounts_list)!= len(set(accounts_list)):
+		if len(accounts_list) != len(set(accounts_list)):
 			frappe.throw(_("Same Company is entered more than once"))
 
 	def validate_accounts(self):
 		for entry in self.accounts:
 			"""Error when Company of Ledger account doesn't match with Company Selected"""
 			if frappe.db.get_value("Account", entry.default_account, "company") != entry.company:
-				frappe.throw(_("Account {0} does not match with Company {1} in Mode of Account: {2}")
-					.format(entry.default_account, entry.company, self.name))
+				frappe.throw(
+					_("Account {0} does not match with Company {1} in Mode of Account: {2}").format(
+						entry.default_account, entry.company, self.name
+					)
+				)
 
 	def validate_pos_mode_of_payment(self):
 		if not self.enabled:
-			pos_profiles = frappe.db.sql("""SELECT sip.parent FROM `tabSales Invoice Payment` sip
-				WHERE sip.parenttype = 'POS Profile' and sip.mode_of_payment = %s""", (self.name))
+			pos_profiles = frappe.db.sql(
+				"""SELECT sip.parent FROM `tabSales Invoice Payment` sip
+				WHERE sip.parenttype = 'POS Profile' and sip.mode_of_payment = %s""",
+				(self.name),
+			)
 			pos_profiles = list(map(lambda x: x[0], pos_profiles))
 
 			if pos_profiles:
-				message = "POS Profile " + frappe.bold(", ".join(pos_profiles)) + " contains \
-					Mode of Payment " + frappe.bold(str(self.name)) + ". Please remove them to disable this mode."
-				frappe.throw(_(message), title="Not Allowed")
+				message = _(
+					"POS Profile {} contains Mode of Payment {}. Please remove them to disable this mode."
+				).format(frappe.bold(", ".join(pos_profiles)), frappe.bold(str(self.name)))
+				frappe.throw(message, title=_("Not Allowed"))
diff --git a/erpnext/accounts/doctype/mode_of_payment/test_mode_of_payment.py b/erpnext/accounts/doctype/mode_of_payment/test_mode_of_payment.py
index 2ff02a7..9733fb8 100644
--- a/erpnext/accounts/doctype/mode_of_payment/test_mode_of_payment.py
+++ b/erpnext/accounts/doctype/mode_of_payment/test_mode_of_payment.py
@@ -5,5 +5,6 @@
 
 # test_records = frappe.get_test_records('Mode of Payment')
 
+
 class TestModeofPayment(unittest.TestCase):
 	pass
diff --git a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py
index a8c5f68..1d19708 100644
--- a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py
+++ b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py
@@ -11,13 +11,25 @@
 class MonthlyDistribution(Document):
 	@frappe.whitelist()
 	def get_months(self):
-		month_list = ['January','February','March','April','May','June','July','August','September',
-		'October','November','December']
-		idx =1
+		month_list = [
+			"January",
+			"February",
+			"March",
+			"April",
+			"May",
+			"June",
+			"July",
+			"August",
+			"September",
+			"October",
+			"November",
+			"December",
+		]
+		idx = 1
 		for m in month_list:
-			mnth = self.append('percentages')
+			mnth = self.append("percentages")
 			mnth.month = m
-			mnth.percentage_allocation = 100.0/12
+			mnth.percentage_allocation = 100.0 / 12
 			mnth.idx = idx
 			idx += 1
 
@@ -25,18 +37,15 @@
 		total = sum(flt(d.percentage_allocation) for d in self.get("percentages"))
 
 		if flt(total, 2) != 100.0:
-			frappe.throw(_("Percentage Allocation should be equal to 100%") + \
-				" ({0}%)".format(str(flt(total, 2))))
+			frappe.throw(
+				_("Percentage Allocation should be equal to 100%") + " ({0}%)".format(str(flt(total, 2)))
+			)
+
 
 def get_periodwise_distribution_data(distribution_id, period_list, periodicity):
-	doc = frappe.get_doc('Monthly Distribution', distribution_id)
+	doc = frappe.get_doc("Monthly Distribution", distribution_id)
 
-	months_to_add = {
-		"Yearly": 12,
-		"Half-Yearly": 6,
-		"Quarterly": 3,
-		"Monthly": 1
-	}[periodicity]
+	months_to_add = {"Yearly": 12, "Half-Yearly": 6, "Quarterly": 3, "Monthly": 1}[periodicity]
 
 	period_dict = {}
 
@@ -45,6 +54,7 @@
 
 	return period_dict
 
+
 def get_percentage(doc, start_date, period):
 	percentage = 0
 	months = [start_date.strftime("%B").title()]
diff --git a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py
index 96008c4..ba2cb67 100644
--- a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py
+++ b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py
@@ -3,19 +3,14 @@
 
 def get_data():
 	return {
-		'fieldname': 'monthly_distribution',
-		'non_standard_fieldnames': {
-			'Sales Person': 'distribution_id',
-			'Territory': 'distribution_id',
-			'Sales Partner': 'distribution_id',
+		"fieldname": "monthly_distribution",
+		"non_standard_fieldnames": {
+			"Sales Person": "distribution_id",
+			"Territory": "distribution_id",
+			"Sales Partner": "distribution_id",
 		},
-		'transactions': [
-			{
-				'label': _('Target Details'),
-				'items': ['Sales Person', 'Territory', 'Sales Partner']
-			},
-			{
-				'items': ['Budget']
-			}
-		]
+		"transactions": [
+			{"label": _("Target Details"), "items": ["Sales Person", "Territory", "Sales Partner"]},
+			{"items": ["Budget"]},
+		],
 	}
diff --git a/erpnext/accounts/doctype/monthly_distribution/test_monthly_distribution.py b/erpnext/accounts/doctype/monthly_distribution/test_monthly_distribution.py
index 4a878b2..848b1f9 100644
--- a/erpnext/accounts/doctype/monthly_distribution/test_monthly_distribution.py
+++ b/erpnext/accounts/doctype/monthly_distribution/test_monthly_distribution.py
@@ -6,7 +6,8 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Monthly Distribution')
+test_records = frappe.get_test_records("Monthly Distribution")
+
 
 class TestMonthlyDistribution(unittest.TestCase):
 	pass
diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py
index 6e7b80e..9937742 100644
--- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py
+++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py
@@ -2,9 +2,6 @@
 # For license information, please see license.txt
 
 
-import traceback
-from json import dumps
-
 import frappe
 from frappe import _, scrub
 from frappe.model.document import Document
@@ -20,9 +17,9 @@
 	def onload(self):
 		"""Load the Opening Invoice summary"""
 		summary, max_count = self.get_opening_invoice_summary()
-		self.set_onload('opening_invoices_summary', summary)
-		self.set_onload('max_count', max_count)
-		self.set_onload('temporary_opening_account', get_temporary_opening_account(self.company))
+		self.set_onload("opening_invoices_summary", summary)
+		self.set_onload("max_count", max_count)
+		self.set_onload("temporary_opening_account", get_temporary_opening_account(self.company))
 
 	def get_opening_invoice_summary(self):
 		def prepare_invoice_summary(doctype, invoices):
@@ -32,10 +29,7 @@
 			for invoice in invoices:
 				company = invoice.pop("company")
 				_summary = invoices_summary.get(company, {})
-				_summary.update({
-					"currency": company_wise_currency.get(company),
-					doctype: invoice
-				})
+				_summary.update({"currency": company_wise_currency.get(company), doctype: invoice})
 				invoices_summary.update({company: _summary})
 
 				if invoice.paid_amount:
@@ -44,17 +38,21 @@
 					outstanding_amount.append(invoice.outstanding_amount)
 
 			if paid_amount or outstanding_amount:
-				max_count.update({
-					doctype: {
-						"max_paid": max(paid_amount) if paid_amount else 0.0,
-						"max_due": max(outstanding_amount) if outstanding_amount else 0.0
+				max_count.update(
+					{
+						doctype: {
+							"max_paid": max(paid_amount) if paid_amount else 0.0,
+							"max_due": max(outstanding_amount) if outstanding_amount else 0.0,
+						}
 					}
-				})
+				)
 
 		invoices_summary = {}
 		max_count = {}
 		fields = [
-			"company", "count(name) as total_invoices", "sum(outstanding_amount) as outstanding_amount"
+			"company",
+			"count(name) as total_invoices",
+			"sum(outstanding_amount) as outstanding_amount",
 		]
 		companies = frappe.get_all("Company", fields=["name as company", "default_currency as currency"])
 		if not companies:
@@ -62,8 +60,9 @@
 
 		company_wise_currency = {row.company: row.currency for row in companies}
 		for doctype in ["Sales Invoice", "Purchase Invoice"]:
-			invoices = frappe.get_all(doctype, filters=dict(is_opening="Yes", docstatus=1),
-				fields=fields, group_by="company")
+			invoices = frappe.get_all(
+				doctype, filters=dict(is_opening="Yes", docstatus=1), fields=fields, group_by="company"
+			)
 			prepare_invoice_summary(doctype, invoices)
 
 		return invoices_summary, max_count
@@ -74,7 +73,9 @@
 
 	def set_missing_values(self, row):
 		row.qty = row.qty or 1.0
-		row.temporary_opening_account = row.temporary_opening_account or get_temporary_opening_account(self.company)
+		row.temporary_opening_account = row.temporary_opening_account or get_temporary_opening_account(
+			self.company
+		)
 		row.party_type = "Customer" if self.invoice_type == "Sales" else "Supplier"
 		row.item_name = row.item_name or _("Opening Invoice Item")
 		row.posting_date = row.posting_date or nowdate()
@@ -85,7 +86,11 @@
 			if self.create_missing_party:
 				self.add_party(row.party_type, row.party)
 			else:
-				frappe.throw(_("Row #{}: {} {} does not exist.").format(row.idx, frappe.bold(row.party_type), frappe.bold(row.party)))
+				frappe.throw(
+					_("Row #{}: {} {} does not exist.").format(
+						row.idx, frappe.bold(row.party_type), frappe.bold(row.party)
+					)
+				)
 
 		mandatory_error_msg = _("Row #{0}: {1} is required to create the Opening {2} Invoices")
 		for d in ("Party", "Outstanding Amount", "Temporary Opening Account"):
@@ -100,12 +105,22 @@
 			self.set_missing_values(row)
 			self.validate_mandatory_invoice_fields(row)
 			invoice = self.get_invoice_dict(row)
-			company_details = frappe.get_cached_value('Company', self.company, ["default_currency", "default_letter_head"], as_dict=1) or {}
+			company_details = (
+				frappe.get_cached_value(
+					"Company", self.company, ["default_currency", "default_letter_head"], as_dict=1
+				)
+				or {}
+			)
+
+			default_currency = frappe.db.get_value(row.party_type, row.party, "default_currency")
+
 			if company_details:
-				invoice.update({
-					"currency": company_details.get("default_currency"),
-					"letter_head": company_details.get("default_letter_head")
-				})
+				invoice.update(
+					{
+						"currency": default_currency or company_details.get("default_currency"),
+						"letter_head": company_details.get("default_letter_head"),
+					}
+				)
 			invoices.append(invoice)
 
 		return invoices
@@ -127,55 +142,61 @@
 
 	def get_invoice_dict(self, row=None):
 		def get_item_dict():
-			cost_center = row.get('cost_center') or frappe.get_cached_value('Company', self.company,  "cost_center")
+			cost_center = row.get("cost_center") or frappe.get_cached_value(
+				"Company", self.company, "cost_center"
+			)
 			if not cost_center:
-				frappe.throw(_("Please set the Default Cost Center in {0} company.").format(frappe.bold(self.company)))
+				frappe.throw(
+					_("Please set the Default Cost Center in {0} company.").format(frappe.bold(self.company))
+				)
 
-			income_expense_account_field = "income_account" if row.party_type == "Customer" else "expense_account"
+			income_expense_account_field = (
+				"income_account" if row.party_type == "Customer" else "expense_account"
+			)
 			default_uom = frappe.db.get_single_value("Stock Settings", "stock_uom") or _("Nos")
 			rate = flt(row.outstanding_amount) / flt(row.qty)
 
-			item_dict = frappe._dict({
-				"uom": default_uom,
-				"rate": rate or 0.0,
-				"qty": row.qty,
-				"conversion_factor": 1.0,
-				"item_name": row.item_name or "Opening Invoice Item",
-				"description": row.item_name or "Opening Invoice Item",
-				income_expense_account_field: row.temporary_opening_account,
-				"cost_center": cost_center
-			})
+			item_dict = frappe._dict(
+				{
+					"uom": default_uom,
+					"rate": rate or 0.0,
+					"qty": row.qty,
+					"conversion_factor": 1.0,
+					"item_name": row.item_name or "Opening Invoice Item",
+					"description": row.item_name or "Opening Invoice Item",
+					income_expense_account_field: row.temporary_opening_account,
+					"cost_center": cost_center,
+				}
+			)
 
 			for dimension in get_accounting_dimensions():
-				item_dict.update({
-					dimension: row.get(dimension)
-				})
+				item_dict.update({dimension: row.get(dimension)})
 
 			return item_dict
 
 		item = get_item_dict()
 
-		invoice = frappe._dict({
-			"items": [item],
-			"is_opening": "Yes",
-			"set_posting_time": 1,
-			"company": self.company,
-			"cost_center": self.cost_center,
-			"due_date": row.due_date,
-			"posting_date": row.posting_date,
-			frappe.scrub(row.party_type): row.party,
-			"is_pos": 0,
-			"doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice",
-			"update_stock": 0,   # important: https://github.com/frappe/erpnext/pull/23559
-			"invoice_number": row.invoice_number,
-			"disable_rounded_total": 1
-		})
+		invoice = frappe._dict(
+			{
+				"items": [item],
+				"is_opening": "Yes",
+				"set_posting_time": 1,
+				"company": self.company,
+				"cost_center": self.cost_center,
+				"due_date": row.due_date,
+				"posting_date": row.posting_date,
+				frappe.scrub(row.party_type): row.party,
+				"is_pos": 0,
+				"doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice",
+				"update_stock": 0,  # important: https://github.com/frappe/erpnext/pull/23559
+				"invoice_number": row.invoice_number,
+				"disable_rounded_total": 1,
+			}
+		)
 
 		accounting_dimension = get_accounting_dimensions()
 		for dimension in accounting_dimension:
-			invoice.update({
-				dimension: self.get(dimension) or item.get(dimension)
-			})
+			invoice.update({dimension: self.get(dimension) or item.get(dimension)})
 
 		return invoice
 
@@ -201,9 +222,10 @@
 					event="opening_invoice_creation",
 					job_name=self.name,
 					invoices=invoices,
-					now=frappe.conf.developer_mode or frappe.flags.in_test
+					now=frappe.conf.developer_mode or frappe.flags.in_test,
 				)
 
+
 def start_import(invoices):
 	errors = 0
 	names = []
@@ -222,14 +244,18 @@
 		except Exception:
 			errors += 1
 			frappe.db.rollback()
-			message = "\n".join(["Data:", dumps(d, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()])
-			frappe.log_error(title="Error while creating Opening Invoice", message=message)
-			frappe.db.commit()
+			doc.log_error("Opening invoice creation failed")
 	if errors:
-		frappe.msgprint(_("You had {} errors while creating opening invoices. Check {} for more details")
-			.format(errors, "<a href='/app/List/Error Log' class='variant-click'>Error Log</a>"), indicator="red", title=_("Error Occured"))
+		frappe.msgprint(
+			_("You had {} errors while creating opening invoices. Check {} for more details").format(
+				errors, "<a href='/app/List/Error Log' class='variant-click'>Error Log</a>"
+			),
+			indicator="red",
+			title=_("Error Occured"),
+		)
 	return names
 
+
 def publish(index, total, doctype):
 	if total < 50:
 		return
@@ -237,21 +263,20 @@
 		"opening_invoice_creation_progress",
 		dict(
 			title=_("Opening Invoice Creation In Progress"),
-			message=_('Creating {} out of {} {}').format(index + 1, total, doctype),
+			message=_("Creating {} out of {} {}").format(index + 1, total, doctype),
 			user=frappe.session.user,
-			count=index+1,
-			total=total
-		))
+			count=index + 1,
+			total=total,
+		),
+	)
+
 
 @frappe.whitelist()
 def get_temporary_opening_account(company=None):
 	if not company:
 		return
 
-	accounts = frappe.get_all("Account", filters={
-		'company': company,
-		'account_type': 'Temporary'
-	})
+	accounts = frappe.get_all("Account", filters={"company": company, "account_type": "Temporary"})
 	if not accounts:
 		frappe.throw(_("Please add a Temporary Opening account in Chart of Accounts"))
 
diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py
index 77d54a6..1e22c64 100644
--- a/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py
+++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py
@@ -14,6 +14,7 @@
 
 test_dependencies = ["Customer", "Supplier", "Accounting Dimension"]
 
+
 class TestOpeningInvoiceCreationTool(FrappeTestCase):
 	@classmethod
 	def setUpClass(self):
@@ -22,10 +23,24 @@
 		create_dimension()
 		return super().setUpClass()
 
-	def make_invoices(self, invoice_type="Sales", company=None, party_1=None, party_2=None, invoice_number=None, department=None):
+	def make_invoices(
+		self,
+		invoice_type="Sales",
+		company=None,
+		party_1=None,
+		party_2=None,
+		invoice_number=None,
+		department=None,
+	):
 		doc = frappe.get_single("Opening Invoice Creation Tool")
-		args = get_opening_invoice_creation_dict(invoice_type=invoice_type, company=company,
-			party_1=party_1, party_2=party_2, invoice_number=invoice_number, department=department)
+		args = get_opening_invoice_creation_dict(
+			invoice_type=invoice_type,
+			company=company,
+			party_1=party_1,
+			party_2=party_2,
+			invoice_number=invoice_number,
+			department=department,
+		)
 		doc.update(args)
 		return doc.make_invoices()
 
@@ -68,15 +83,30 @@
 		company = "_Test Opening Invoice Company"
 		party_1, party_2 = make_customer("Customer A"), make_customer("Customer B")
 
-		old_default_receivable_account = frappe.db.get_value("Company", company, "default_receivable_account")
+		old_default_receivable_account = frappe.db.get_value(
+			"Company", company, "default_receivable_account"
+		)
 		frappe.db.set_value("Company", company, "default_receivable_account", "")
 
 		if not frappe.db.exists("Cost Center", "_Test Opening Invoice Company - _TOIC"):
-			cc = frappe.get_doc({"doctype": "Cost Center", "cost_center_name": "_Test Opening Invoice Company",
-				"is_group": 1, "company": "_Test Opening Invoice Company"})
+			cc = frappe.get_doc(
+				{
+					"doctype": "Cost Center",
+					"cost_center_name": "_Test Opening Invoice Company",
+					"is_group": 1,
+					"company": "_Test Opening Invoice Company",
+				}
+			)
 			cc.insert(ignore_mandatory=True)
-			cc2 = frappe.get_doc({"doctype": "Cost Center", "cost_center_name": "Main", "is_group": 0,
-				"company": "_Test Opening Invoice Company", "parent_cost_center": cc.name})
+			cc2 = frappe.get_doc(
+				{
+					"doctype": "Cost Center",
+					"cost_center_name": "Main",
+					"is_group": 0,
+					"company": "_Test Opening Invoice Company",
+					"parent_cost_center": cc.name,
+				}
+			)
 			cc2.insert()
 
 		frappe.db.set_value("Company", company, "cost_center", "Main - _TOIC")
@@ -84,28 +114,37 @@
 		self.make_invoices(company="_Test Opening Invoice Company", party_1=party_1, party_2=party_2)
 
 		# Check if missing debit account error raised
-		error_log = frappe.db.exists("Error Log", {"error": ["like", "%erpnext.controllers.accounts_controller.AccountMissingError%"]})
+		error_log = frappe.db.exists(
+			"Error Log",
+			{"error": ["like", "%erpnext.controllers.accounts_controller.AccountMissingError%"]},
+		)
 		self.assertTrue(error_log)
 
 		# teardown
-		frappe.db.set_value("Company", company, "default_receivable_account", old_default_receivable_account)
+		frappe.db.set_value(
+			"Company", company, "default_receivable_account", old_default_receivable_account
+		)
 
 	def test_renaming_of_invoice_using_invoice_number_field(self):
 		company = "_Test Opening Invoice Company"
 		party_1, party_2 = make_customer("Customer A"), make_customer("Customer B")
-		self.make_invoices(company=company, party_1=party_1, party_2=party_2, invoice_number="TEST-NEW-INV-11")
+		self.make_invoices(
+			company=company, party_1=party_1, party_2=party_2, invoice_number="TEST-NEW-INV-11"
+		)
 
-		sales_inv1 = frappe.get_all('Sales Invoice', filters={'customer':'Customer A'})[0].get("name")
-		sales_inv2 = frappe.get_all('Sales Invoice', filters={'customer':'Customer B'})[0].get("name")
+		sales_inv1 = frappe.get_all("Sales Invoice", filters={"customer": "Customer A"})[0].get("name")
+		sales_inv2 = frappe.get_all("Sales Invoice", filters={"customer": "Customer B"})[0].get("name")
 		self.assertEqual(sales_inv1, "TEST-NEW-INV-11")
 
-		#teardown
+		# teardown
 		for inv in [sales_inv1, sales_inv2]:
-			doc = frappe.get_doc('Sales Invoice', inv)
+			doc = frappe.get_doc("Sales Invoice", inv)
 			doc.cancel()
 
 	def test_opening_invoice_with_accounting_dimension(self):
-		invoices = self.make_invoices(invoice_type="Sales", company="_Test Opening Invoice Company", department='Sales - _TOIC')
+		invoices = self.make_invoices(
+			invoice_type="Sales", company="_Test Opening Invoice Company", department="Sales - _TOIC"
+		)
 
 		expected_value = {
 			"keys": ["customer", "outstanding_amount", "status", "department"],
@@ -117,40 +156,44 @@
 	def tearDown(self):
 		disable_dimension()
 
+
 def get_opening_invoice_creation_dict(**args):
 	party = "Customer" if args.get("invoice_type", "Sales") == "Sales" else "Supplier"
 	company = args.get("company", "_Test Company")
 
-	invoice_dict = frappe._dict({
-		"company": company,
-		"invoice_type": args.get("invoice_type", "Sales"),
-		"invoices": [
-			{
-				"qty": 1.0,
-				"outstanding_amount": 300,
-				"party": args.get("party_1") or "_Test {0}".format(party),
-				"item_name": "Opening Item",
-				"due_date": "2016-09-10",
-				"posting_date": "2016-09-05",
-				"temporary_opening_account": get_temporary_opening_account(company),
-				"invoice_number": args.get("invoice_number")
-			},
-			{
-				"qty": 2.0,
-				"outstanding_amount": 250,
-				"party": args.get("party_2") or "_Test {0} 1".format(party),
-				"item_name": "Opening Item",
-				"due_date": "2016-09-10",
-				"posting_date": "2016-09-05",
-				"temporary_opening_account": get_temporary_opening_account(company),
-				"invoice_number": None
-			}
-		]
-	})
+	invoice_dict = frappe._dict(
+		{
+			"company": company,
+			"invoice_type": args.get("invoice_type", "Sales"),
+			"invoices": [
+				{
+					"qty": 1.0,
+					"outstanding_amount": 300,
+					"party": args.get("party_1") or "_Test {0}".format(party),
+					"item_name": "Opening Item",
+					"due_date": "2016-09-10",
+					"posting_date": "2016-09-05",
+					"temporary_opening_account": get_temporary_opening_account(company),
+					"invoice_number": args.get("invoice_number"),
+				},
+				{
+					"qty": 2.0,
+					"outstanding_amount": 250,
+					"party": args.get("party_2") or "_Test {0} 1".format(party),
+					"item_name": "Opening Item",
+					"due_date": "2016-09-10",
+					"posting_date": "2016-09-05",
+					"temporary_opening_account": get_temporary_opening_account(company),
+					"invoice_number": None,
+				},
+			],
+		}
+	)
 
 	invoice_dict.update(args)
 	return invoice_dict
 
+
 def make_company():
 	if frappe.db.exists("Company", "_Test Opening Invoice Company"):
 		return frappe.get_doc("Company", "_Test Opening Invoice Company")
@@ -163,15 +206,18 @@
 	company.insert()
 	return company
 
+
 def make_customer(customer=None):
 	customer_name = customer or "Opening Customer"
-	customer = frappe.get_doc({
-		"doctype": "Customer",
-		"customer_name": customer_name,
-		"customer_group": "All Customer Groups",
-		"customer_type": "Company",
-		"territory": "All Territories"
-	})
+	customer = frappe.get_doc(
+		{
+			"doctype": "Customer",
+			"customer_name": customer_name,
+			"customer_group": "All Customer Groups",
+			"customer_type": "Company",
+			"territory": "All Territories",
+		}
+	)
 	if not frappe.db.exists("Customer", customer_name):
 		customer.insert(ignore_permissions=True)
 		return customer.name
diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json b/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json
index 5c19091..ed8ff7c 100644
--- a/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json
+++ b/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json
@@ -110,13 +110,12 @@
    "description": "Reference number of the invoice from the previous system",
    "fieldname": "invoice_number",
    "fieldtype": "Data",
-   "in_list_view": 1,
    "label": "Invoice Number"
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2021-12-17 19:25:06.053187",
+ "modified": "2022-03-21 19:31:45.382656",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Opening Invoice Creation Tool Item",
@@ -125,5 +124,6 @@
  "quick_entry": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/party_account/party_account.json b/erpnext/accounts/doctype/party_account/party_account.json
index c9f15a6..6933057 100644
--- a/erpnext/accounts/doctype/party_account/party_account.json
+++ b/erpnext/accounts/doctype/party_account/party_account.json
@@ -3,6 +3,7 @@
  "creation": "2014-08-29 16:02:39.740505",
  "doctype": "DocType",
  "editable_grid": 1,
+ "engine": "InnoDB",
  "field_order": [
   "company",
   "account"
@@ -11,6 +12,7 @@
   {
    "fieldname": "company",
    "fieldtype": "Link",
+   "ignore_user_permissions": 1,
    "in_list_view": 1,
    "label": "Company",
    "options": "Company",
@@ -27,7 +29,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-04-07 18:13:08.833822",
+ "modified": "2022-04-04 12:31:02.994197",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Party Account",
@@ -35,5 +37,6 @@
  "permissions": [],
  "quick_entry": 1,
  "sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "states": []
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/party_link/party_link.py b/erpnext/accounts/doctype/party_link/party_link.py
index 031a9fa..312cfd2 100644
--- a/erpnext/accounts/doctype/party_link/party_link.py
+++ b/erpnext/accounts/doctype/party_link/party_link.py
@@ -8,45 +8,55 @@
 
 class PartyLink(Document):
 	def validate(self):
-		if self.primary_role not in ['Customer', 'Supplier']:
-			frappe.throw(_("Allowed primary roles are 'Customer' and 'Supplier'. Please select one of these roles only."),
-				title=_("Invalid Primary Role"))
+		if self.primary_role not in ["Customer", "Supplier"]:
+			frappe.throw(
+				_(
+					"Allowed primary roles are 'Customer' and 'Supplier'. Please select one of these roles only."
+				),
+				title=_("Invalid Primary Role"),
+			)
 
-		existing_party_link = frappe.get_all('Party Link', {
-			'primary_party': self.primary_party,
-			'secondary_party': self.secondary_party
-		}, pluck="primary_role")
+		existing_party_link = frappe.get_all(
+			"Party Link",
+			{"primary_party": self.primary_party, "secondary_party": self.secondary_party},
+			pluck="primary_role",
+		)
 		if existing_party_link:
-			frappe.throw(_('{} {} is already linked with {} {}')
-				.format(
-					self.primary_role, bold(self.primary_party),
-					self.secondary_role, bold(self.secondary_party)
-				))
+			frappe.throw(
+				_("{} {} is already linked with {} {}").format(
+					self.primary_role, bold(self.primary_party), self.secondary_role, bold(self.secondary_party)
+				)
+			)
 
-		existing_party_link = frappe.get_all('Party Link', {
-			'primary_party': self.secondary_party
-		}, pluck="primary_role")
+		existing_party_link = frappe.get_all(
+			"Party Link", {"primary_party": self.secondary_party}, pluck="primary_role"
+		)
 		if existing_party_link:
-			frappe.throw(_('{} {} is already linked with another {}')
-				.format(self.secondary_role, self.secondary_party, existing_party_link[0]))
+			frappe.throw(
+				_("{} {} is already linked with another {}").format(
+					self.secondary_role, self.secondary_party, existing_party_link[0]
+				)
+			)
 
-		existing_party_link = frappe.get_all('Party Link', {
-			'secondary_party': self.primary_party
-		}, pluck="primary_role")
+		existing_party_link = frappe.get_all(
+			"Party Link", {"secondary_party": self.primary_party}, pluck="primary_role"
+		)
 		if existing_party_link:
-			frappe.throw(_('{} {} is already linked with another {}')
-				.format(self.primary_role, self.primary_party, existing_party_link[0]))
+			frappe.throw(
+				_("{} {} is already linked with another {}").format(
+					self.primary_role, self.primary_party, existing_party_link[0]
+				)
+			)
 
 
 @frappe.whitelist()
 def create_party_link(primary_role, primary_party, secondary_party):
-	party_link = frappe.new_doc('Party Link')
+	party_link = frappe.new_doc("Party Link")
 	party_link.primary_role = primary_role
 	party_link.primary_party = primary_party
-	party_link.secondary_role = 'Customer' if primary_role == 'Supplier' else 'Supplier'
+	party_link.secondary_role = "Customer" if primary_role == "Supplier" else "Supplier"
 	party_link.secondary_party = secondary_party
 
 	party_link.save(ignore_permissions=True)
 
 	return party_link
-
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index cc32a6c..d7fae6d 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -112,8 +112,6 @@
 				var doctypes = ["Purchase Order", "Purchase Invoice", "Journal Entry"];
 			} else if (frm.doc.party_type == "Employee") {
 				var doctypes = ["Expense Claim", "Journal Entry"];
-			} else if (frm.doc.party_type == "Student") {
-				var doctypes = ["Fees"];
 			} else {
 				var doctypes = ["Journal Entry"];
 			}
@@ -224,10 +222,7 @@
 			(frm.doc.total_allocated_amount > party_amount)));
 
 		frm.toggle_display("set_exchange_gain_loss",
-			(frm.doc.paid_amount && frm.doc.received_amount && frm.doc.difference_amount &&
-				((frm.doc.paid_from_account_currency != company_currency ||
-					frm.doc.paid_to_account_currency != company_currency) &&
-					frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency)));
+			frm.doc.paid_amount && frm.doc.received_amount && frm.doc.difference_amount);
 
 		frm.refresh_fields();
 	},
@@ -346,6 +341,8 @@
 			}
 			frm.set_party_account_based_on_party = true;
 
+			let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
+
 			return frappe.call({
 				method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_party_details",
 				args: {
@@ -379,7 +376,11 @@
 								if (r.message.bank_account) {
 									frm.set_value("bank_account", r.message.bank_account);
 								}
-							}
+							},
+							() => frm.events.set_current_exchange_rate(frm, "source_exchange_rate",
+									frm.doc.paid_from_account_currency, company_currency),
+							() => frm.events.set_current_exchange_rate(frm, "target_exchange_rate",
+									frm.doc.paid_to_account_currency, company_currency)
 						]);
 					}
 				}
@@ -483,14 +484,14 @@
 	},
 
 	paid_from_account_currency: function(frm) {
-		if(!frm.doc.paid_from_account_currency) return;
-		var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
+		if(!frm.doc.paid_from_account_currency || !frm.doc.company) return;
+		let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
 
 		if (frm.doc.paid_from_account_currency == company_currency) {
 			frm.set_value("source_exchange_rate", 1);
 		} else if (frm.doc.paid_from){
 			if (in_list(["Internal Transfer", "Pay"], frm.doc.payment_type)) {
-				var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
+				let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
 				frappe.call({
 					method: "erpnext.setup.utils.get_exchange_rate",
 					args: {
@@ -510,8 +511,8 @@
 	},
 
 	paid_to_account_currency: function(frm) {
-		if(!frm.doc.paid_to_account_currency) return;
-		var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
+		if(!frm.doc.paid_to_account_currency || !frm.doc.company) return;
+		let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
 
 		frm.events.set_current_exchange_rate(frm, "target_exchange_rate",
 			frm.doc.paid_to_account_currency, company_currency);
@@ -526,7 +527,8 @@
 				to_currency: to_currency
 			},
 			callback: function(r, rt) {
-				frm.set_value(exchange_rate_field, r.message);
+				const ex_rate = flt(r.message, frm.get_field(exchange_rate_field).get_precision());
+				frm.set_value(exchange_rate_field, ex_rate);
 			}
 		})
 	},
@@ -751,8 +753,7 @@
 					if(
 						(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") ||
 						(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Supplier")  ||
-						(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Employee") ||
-						(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student")
+						(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Employee")
 					) {
 						if(total_positive_outstanding > total_negative_outstanding)
 							if (!frm.doc.paid_amount)
@@ -794,8 +795,7 @@
 		if (
 				(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") ||
 				(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Supplier") ||
-				(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Employee") ||
-				(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student")
+				(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Employee")
 			) {
 				if(total_positive_outstanding_including_order > paid_amount) {
 					var remaining_outstanding = total_positive_outstanding_including_order - paid_amount;
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index f9f3350..d8af9db 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -6,7 +6,7 @@
 from functools import reduce
 
 import frappe
-from frappe import ValidationError, _, scrub, throw
+from frappe import ValidationError, _, qb, scrub, throw
 from frappe.utils import cint, comma_or, flt, getdate, nowdate
 
 import erpnext
@@ -95,7 +95,7 @@
 		self.set_status()
 
 	def on_cancel(self):
-		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+		self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
 		self.make_gl_entries(cancel=1)
 		self.update_expense_claim()
 		self.update_outstanding_amounts()
@@ -107,6 +107,7 @@
 
 	def set_payment_req_status(self):
 		from erpnext.accounts.doctype.payment_request.payment_request import update_payment_req_status
+
 		update_payment_req_status(self, None)
 
 	def update_outstanding_amounts(self):
@@ -116,8 +117,11 @@
 		reference_names = []
 		for d in self.get("references"):
 			if (d.reference_doctype, d.reference_name, d.payment_term) in reference_names:
-				frappe.throw(_("Row #{0}: Duplicate entry in References {1} {2}")
-					.format(d.idx, d.reference_doctype, d.reference_name))
+				frappe.throw(
+					_("Row #{0}: Duplicate entry in References {1} {2}").format(
+						d.idx, d.reference_doctype, d.reference_name
+					)
+				)
 			reference_names.append((d.reference_doctype, d.reference_name, d.payment_term))
 
 	def set_bank_account_data(self):
@@ -133,20 +137,27 @@
 				self.set(field, bank_data.account)
 
 	def validate_payment_type_with_outstanding(self):
-		total_outstanding = sum(d.allocated_amount for d in self.get('references'))
-		if total_outstanding < 0 and self.party_type == 'Customer' and self.payment_type == 'Receive':
-			frappe.throw(_("Cannot receive from customer against negative outstanding"), title=_("Incorrect Payment Type"))
+		total_outstanding = sum(d.allocated_amount for d in self.get("references"))
+		if total_outstanding < 0 and self.party_type == "Customer" and self.payment_type == "Receive":
+			frappe.throw(
+				_("Cannot receive from customer against negative outstanding"),
+				title=_("Incorrect Payment Type"),
+			)
 
 	def validate_allocated_amount(self):
 		for d in self.get("references"):
-			if (flt(d.allocated_amount))> 0:
+			if (flt(d.allocated_amount)) > 0:
 				if flt(d.allocated_amount) > flt(d.outstanding_amount):
-					frappe.throw(_("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx))
+					frappe.throw(
+						_("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx)
+					)
 
 			# Check for negative outstanding invoices as well
 			if flt(d.allocated_amount) < 0:
 				if flt(d.allocated_amount) < flt(d.outstanding_amount):
-					frappe.throw(_("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx))
+					frappe.throw(
+						_("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx)
+					)
 
 	def delink_advance_entry_references(self):
 		for reference in self.references:
@@ -156,9 +167,14 @@
 
 	def set_missing_values(self):
 		if self.payment_type == "Internal Transfer":
-			for field in ("party", "party_balance", "total_allocated_amount",
-				"base_total_allocated_amount", "unallocated_amount"):
-					self.set(field, None)
+			for field in (
+				"party",
+				"party_balance",
+				"total_allocated_amount",
+				"base_total_allocated_amount",
+				"unallocated_amount",
+			):
+				self.set(field, None)
 			self.references = []
 		else:
 			if not self.party_type:
@@ -167,13 +183,14 @@
 			if not self.party:
 				frappe.throw(_("Party is mandatory"))
 
-			_party_name = "title" if self.party_type in ("Student", "Shareholder") else self.party_type.lower() + "_name"
+			_party_name = "title" if self.party_type == "Shareholder" else self.party_type.lower() + "_name"
 			self.party_name = frappe.db.get_value(self.party_type, self.party, _party_name)
 
 		if self.party:
 			if not self.party_balance:
-				self.party_balance = get_balance_on(party_type=self.party_type,
-					party=self.party, date=self.posting_date, company=self.company)
+				self.party_balance = get_balance_on(
+					party_type=self.party_type, party=self.party, date=self.posting_date, company=self.company
+				)
 
 			if not self.party_account:
 				party_account = get_party_account(self.party_type, self.party, self.company)
@@ -190,16 +207,20 @@
 			self.paid_to_account_currency = acc.account_currency
 			self.paid_to_account_balance = acc.account_balance
 
-		self.party_account_currency = self.paid_from_account_currency \
-			if self.payment_type=="Receive" else self.paid_to_account_currency
+		self.party_account_currency = (
+			self.paid_from_account_currency
+			if self.payment_type == "Receive"
+			else self.paid_to_account_currency
+		)
 
 		self.set_missing_ref_details()
 
 	def set_missing_ref_details(self, force=False):
 		for d in self.get("references"):
 			if d.allocated_amount:
-				ref_details = get_reference_details(d.reference_doctype,
-					d.reference_name, self.party_account_currency)
+				ref_details = get_reference_details(
+					d.reference_doctype, d.reference_name, self.party_account_currency
+				)
 
 				for field, value in ref_details.items():
 					if d.exchange_gain_loss:
@@ -209,7 +230,7 @@
 						# refer -> `update_reference_in_payment_entry()` in utils.py
 						continue
 
-					if field == 'exchange_rate' or not d.get(field) or force:
+					if field == "exchange_rate" or not d.get(field) or force:
 						d.db_set(field, value)
 
 	def validate_payment_type(self):
@@ -222,8 +243,9 @@
 				frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party))
 
 			if self.party_account and self.party_type in ("Customer", "Supplier"):
-				self.validate_account_type(self.party_account,
-					[erpnext.get_party_account_type(self.party_type)])
+				self.validate_account_type(
+					self.party_account, [erpnext.get_party_account_type(self.party_type)]
+				)
 
 	def validate_bank_accounts(self):
 		if self.payment_type in ("Pay", "Internal Transfer"):
@@ -251,8 +273,9 @@
 						self.source_exchange_rate = ref_doc.get("exchange_rate")
 
 			if not self.source_exchange_rate:
-					self.source_exchange_rate = get_exchange_rate(self.paid_from_account_currency,
-						self.company_currency, self.posting_date)
+				self.source_exchange_rate = get_exchange_rate(
+					self.paid_from_account_currency, self.company_currency, self.posting_date
+				)
 
 	def set_target_exchange_rate(self, ref_doc=None):
 		if self.paid_from_account_currency == self.paid_to_account_currency:
@@ -263,8 +286,9 @@
 					self.target_exchange_rate = ref_doc.get("exchange_rate")
 
 			if not self.target_exchange_rate:
-				self.target_exchange_rate = get_exchange_rate(self.paid_to_account_currency,
-					self.company_currency, self.posting_date)
+				self.target_exchange_rate = get_exchange_rate(
+					self.paid_to_account_currency, self.company_currency, self.posting_date
+				)
 
 	def validate_mandatory(self):
 		for field in ("paid_amount", "received_amount", "source_exchange_rate", "target_exchange_rate"):
@@ -272,23 +296,22 @@
 				frappe.throw(_("{0} is mandatory").format(self.meta.get_label(field)))
 
 	def validate_reference_documents(self):
-		if self.party_type == "Student":
-			valid_reference_doctypes = ("Fees")
-		elif self.party_type == "Customer":
+		if self.party_type == "Customer":
 			valid_reference_doctypes = ("Sales Order", "Sales Invoice", "Journal Entry", "Dunning")
 		elif self.party_type == "Supplier":
 			valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry")
 		elif self.party_type == "Employee":
 			valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance", "Gratuity")
 		elif self.party_type == "Shareholder":
-			valid_reference_doctypes = ("Journal Entry")
+			valid_reference_doctypes = "Journal Entry"
 
 		for d in self.get("references"):
 			if not d.allocated_amount:
 				continue
 			if d.reference_doctype not in valid_reference_doctypes:
-				frappe.throw(_("Reference Doctype must be one of {0}")
-					.format(comma_or(valid_reference_doctypes)))
+				frappe.throw(
+					_("Reference Doctype must be one of {0}").format(comma_or(valid_reference_doctypes))
+				)
 
 			elif d.reference_name:
 				if not frappe.db.exists(d.reference_doctype, d.reference_name):
@@ -298,28 +321,39 @@
 
 					if d.reference_doctype != "Journal Entry":
 						if self.party != ref_doc.get(scrub(self.party_type)):
-							frappe.throw(_("{0} {1} is not associated with {2} {3}")
-								.format(d.reference_doctype, d.reference_name, self.party_type, self.party))
+							frappe.throw(
+								_("{0} {1} is not associated with {2} {3}").format(
+									d.reference_doctype, d.reference_name, self.party_type, self.party
+								)
+							)
 					else:
 						self.validate_journal_entry()
 
 					if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim", "Fees"):
 						if self.party_type == "Customer":
-							ref_party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or ref_doc.debit_to
-						elif self.party_type == "Student":
-							ref_party_account = ref_doc.receivable_account
-						elif self.party_type=="Supplier":
+							ref_party_account = (
+								get_party_account_based_on_invoice_discounting(d.reference_name) or ref_doc.debit_to
+							)
+						elif self.party_type == "Supplier":
 							ref_party_account = ref_doc.credit_to
-						elif self.party_type=="Employee":
+						elif self.party_type == "Employee":
 							ref_party_account = ref_doc.payable_account
 
 						if ref_party_account != self.party_account:
-								frappe.throw(_("{0} {1} is associated with {2}, but Party Account is {3}")
-									.format(d.reference_doctype, d.reference_name, ref_party_account, self.party_account))
+							frappe.throw(
+								_("{0} {1} is associated with {2}, but Party Account is {3}").format(
+									d.reference_doctype, d.reference_name, ref_party_account, self.party_account
+								)
+							)
+
+						if ref_doc.doctype == "Purchase Invoice" and ref_doc.get("on_hold"):
+							frappe.throw(
+								_("{0} {1} is on hold").format(d.reference_doctype, d.reference_name),
+								title=_("Invalid Invoice"),
+							)
 
 					if ref_doc.docstatus != 1:
-						frappe.throw(_("{0} {1} must be submitted")
-							.format(d.reference_doctype, d.reference_name))
+						frappe.throw(_("{0} {1} must be submitted").format(d.reference_doctype, d.reference_name))
 
 	def validate_paid_invoices(self):
 		no_oustanding_refs = {}
@@ -329,28 +363,45 @@
 				continue
 
 			if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Fees"):
-				outstanding_amount, is_return = frappe.get_cached_value(d.reference_doctype, d.reference_name, ["outstanding_amount", "is_return"])
+				outstanding_amount, is_return = frappe.get_cached_value(
+					d.reference_doctype, d.reference_name, ["outstanding_amount", "is_return"]
+				)
 				if outstanding_amount <= 0 and not is_return:
 					no_oustanding_refs.setdefault(d.reference_doctype, []).append(d)
 
 		for k, v in no_oustanding_refs.items():
 			frappe.msgprint(
-				_("{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry.")
-					.format(_(k), frappe.bold(", ".join(d.reference_name for d in v)), frappe.bold(_("negative outstanding amount")))
-				+ "<br><br>" + _("If this is undesirable please cancel the corresponding Payment Entry."),
-				title=_("Warning"), indicator="orange")
+				_(
+					"{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry."
+				).format(
+					_(k),
+					frappe.bold(", ".join(d.reference_name for d in v)),
+					frappe.bold(_("negative outstanding amount")),
+				)
+				+ "<br><br>"
+				+ _("If this is undesirable please cancel the corresponding Payment Entry."),
+				title=_("Warning"),
+				indicator="orange",
+			)
 
 	def validate_journal_entry(self):
 		for d in self.get("references"):
 			if d.allocated_amount and d.reference_doctype == "Journal Entry":
-				je_accounts = frappe.db.sql("""select debit, credit from `tabJournal Entry Account`
+				je_accounts = frappe.db.sql(
+					"""select debit, credit from `tabJournal Entry Account`
 					where account = %s and party=%s and docstatus = 1 and parent = %s
 					and (reference_type is null or reference_type in ("", "Sales Order", "Purchase Order"))
-					""", (self.party_account, self.party, d.reference_name), as_dict=True)
+					""",
+					(self.party_account, self.party, d.reference_name),
+					as_dict=True,
+				)
 
 				if not je_accounts:
-					frappe.throw(_("Row #{0}: Journal Entry {1} does not have account {2} or already matched against another voucher")
-						.format(d.idx, d.reference_name, self.party_account))
+					frappe.throw(
+						_(
+							"Row #{0}: Journal Entry {1} does not have account {2} or already matched against another voucher"
+						).format(d.idx, d.reference_name, self.party_account)
+					)
 				else:
 					dr_or_cr = "debit" if self.payment_type == "Receive" else "credit"
 					valid = False
@@ -358,14 +409,17 @@
 						if flt(jvd[dr_or_cr]) > 0:
 							valid = True
 					if not valid:
-						frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
-							.format(d.reference_name, dr_or_cr))
+						frappe.throw(
+							_("Against Journal Entry {0} does not have any unmatched {1} entry").format(
+								d.reference_name, dr_or_cr
+							)
+						)
 
 	def update_payment_schedule(self, cancel=0):
 		invoice_payment_amount_map = {}
 		invoice_paid_amount_map = {}
 
-		for ref in self.get('references'):
+		for ref in self.get("references"):
 			if ref.payment_term and ref.reference_name:
 				key = (ref.payment_term, ref.reference_name)
 				invoice_payment_amount_map.setdefault(key, 0.0)
@@ -373,58 +427,68 @@
 
 				if not invoice_paid_amount_map.get(key):
 					payment_schedule = frappe.get_all(
-						'Payment Schedule',
-						filters={'parent': ref.reference_name},
-						fields=['paid_amount', 'payment_amount', 'payment_term', 'discount', 'outstanding']
+						"Payment Schedule",
+						filters={"parent": ref.reference_name},
+						fields=["paid_amount", "payment_amount", "payment_term", "discount", "outstanding"],
 					)
 					for term in payment_schedule:
 						invoice_key = (term.payment_term, ref.reference_name)
 						invoice_paid_amount_map.setdefault(invoice_key, {})
-						invoice_paid_amount_map[invoice_key]['outstanding'] = term.outstanding
-						invoice_paid_amount_map[invoice_key]['discounted_amt'] = ref.total_amount * (term.discount / 100)
+						invoice_paid_amount_map[invoice_key]["outstanding"] = term.outstanding
+						invoice_paid_amount_map[invoice_key]["discounted_amt"] = ref.total_amount * (
+							term.discount / 100
+						)
 
 		for idx, (key, allocated_amount) in enumerate(invoice_payment_amount_map.items(), 1):
 			if not invoice_paid_amount_map.get(key):
-				frappe.throw(_('Payment term {0} not used in {1}').format(key[0], key[1]))
+				frappe.throw(_("Payment term {0} not used in {1}").format(key[0], key[1]))
 
-			outstanding = flt(invoice_paid_amount_map.get(key, {}).get('outstanding'))
-			discounted_amt = flt(invoice_paid_amount_map.get(key, {}).get('discounted_amt'))
+			outstanding = flt(invoice_paid_amount_map.get(key, {}).get("outstanding"))
+			discounted_amt = flt(invoice_paid_amount_map.get(key, {}).get("discounted_amt"))
 
 			if cancel:
-				frappe.db.sql("""
+				frappe.db.sql(
+					"""
 					UPDATE `tabPayment Schedule`
 					SET
 						paid_amount = `paid_amount` - %s,
 						discounted_amount = `discounted_amount` - %s,
 						outstanding = `outstanding` + %s
 					WHERE parent = %s and payment_term = %s""",
-					(allocated_amount - discounted_amt, discounted_amt, allocated_amount, key[1], key[0]))
+					(allocated_amount - discounted_amt, discounted_amt, allocated_amount, key[1], key[0]),
+				)
 			else:
 				if allocated_amount > outstanding:
-					frappe.throw(_('Row #{0}: Cannot allocate more than {1} against payment term {2}').format(idx, outstanding, key[0]))
+					frappe.throw(
+						_("Row #{0}: Cannot allocate more than {1} against payment term {2}").format(
+							idx, outstanding, key[0]
+						)
+					)
 
 				if allocated_amount and outstanding:
-					frappe.db.sql("""
+					frappe.db.sql(
+						"""
 						UPDATE `tabPayment Schedule`
 						SET
 							paid_amount = `paid_amount` + %s,
 							discounted_amount = `discounted_amount` + %s,
 							outstanding = `outstanding` - %s
 						WHERE parent = %s and payment_term = %s""",
-					(allocated_amount - discounted_amt, discounted_amt, allocated_amount, key[1], key[0]))
+						(allocated_amount - discounted_amt, discounted_amt, allocated_amount, key[1], key[0]),
+					)
 
 	def set_status(self):
 		if self.docstatus == 2:
-			self.status = 'Cancelled'
+			self.status = "Cancelled"
 		elif self.docstatus == 1:
-			self.status = 'Submitted'
+			self.status = "Submitted"
 		else:
-			self.status = 'Draft'
+			self.status = "Draft"
 
-		self.db_set('status', self.status, update_modified = True)
+		self.db_set("status", self.status, update_modified=True)
 
 	def set_tax_withholding(self):
-		if not self.party_type == 'Supplier':
+		if not self.party_type == "Supplier":
 			return
 
 		if not self.apply_tax_withholding_amount:
@@ -433,22 +497,24 @@
 		net_total = self.paid_amount
 
 		# Adding args as purchase invoice to get TDS amount
-		args = frappe._dict({
-			'company': self.company,
-			'doctype': 'Payment Entry',
-			'supplier': self.party,
-			'posting_date': self.posting_date,
-			'net_total': net_total
-		})
+		args = frappe._dict(
+			{
+				"company": self.company,
+				"doctype": "Payment Entry",
+				"supplier": self.party,
+				"posting_date": self.posting_date,
+				"net_total": net_total,
+			}
+		)
 
 		tax_withholding_details = get_party_tax_withholding_details(args, self.tax_withholding_category)
 
 		if not tax_withholding_details:
 			return
 
-		tax_withholding_details.update({
-			'cost_center': self.cost_center or erpnext.get_default_cost_center(self.company)
-		})
+		tax_withholding_details.update(
+			{"cost_center": self.cost_center or erpnext.get_default_cost_center(self.company)}
+		)
 
 		accounts = []
 		for d in self.taxes:
@@ -456,7 +522,7 @@
 
 				# Preserve user updated included in paid amount
 				if d.included_in_paid_amount:
-					tax_withholding_details.update({'included_in_paid_amount': d.included_in_paid_amount})
+					tax_withholding_details.update({"included_in_paid_amount": d.included_in_paid_amount})
 
 				d.update(tax_withholding_details)
 			accounts.append(d.account_head)
@@ -464,8 +530,11 @@
 		if not accounts or tax_withholding_details.get("account_head") not in accounts:
 			self.append("taxes", tax_withholding_details)
 
-		to_remove = [d for d in self.taxes
-			if not d.tax_amount and d.account_head == tax_withholding_details.get("account_head")]
+		to_remove = [
+			d
+			for d in self.taxes
+			if not d.tax_amount and d.account_head == tax_withholding_details.get("account_head")
+		]
 
 		for d in to_remove:
 			self.remove(d)
@@ -492,40 +561,53 @@
 
 	def set_received_amount(self):
 		self.base_received_amount = self.base_paid_amount
-		if self.paid_from_account_currency == self.paid_to_account_currency \
-			and not self.payment_type == 'Internal Transfer':
+		if (
+			self.paid_from_account_currency == self.paid_to_account_currency
+			and not self.payment_type == "Internal Transfer"
+		):
 			self.received_amount = self.paid_amount
 
 	def set_amounts_after_tax(self):
 		applicable_tax = 0
 		base_applicable_tax = 0
-		for tax in self.get('taxes'):
+		for tax in self.get("taxes"):
 			if not tax.included_in_paid_amount:
-				amount = -1 * tax.tax_amount if tax.add_deduct_tax == 'Deduct' else tax.tax_amount
-				base_amount = -1 * tax.base_tax_amount if tax.add_deduct_tax == 'Deduct' else tax.base_tax_amount
+				amount = -1 * tax.tax_amount if tax.add_deduct_tax == "Deduct" else tax.tax_amount
+				base_amount = (
+					-1 * tax.base_tax_amount if tax.add_deduct_tax == "Deduct" else tax.base_tax_amount
+				)
 
 				applicable_tax += amount
 				base_applicable_tax += base_amount
 
-		self.paid_amount_after_tax = flt(flt(self.paid_amount) + flt(applicable_tax),
-			self.precision("paid_amount_after_tax"))
-		self.base_paid_amount_after_tax = flt(flt(self.paid_amount_after_tax) * flt(self.source_exchange_rate),
-			self.precision("base_paid_amount_after_tax"))
+		self.paid_amount_after_tax = flt(
+			flt(self.paid_amount) + flt(applicable_tax), self.precision("paid_amount_after_tax")
+		)
+		self.base_paid_amount_after_tax = flt(
+			flt(self.paid_amount_after_tax) * flt(self.source_exchange_rate),
+			self.precision("base_paid_amount_after_tax"),
+		)
 
-		self.received_amount_after_tax = flt(flt(self.received_amount) + flt(applicable_tax),
-			self.precision("paid_amount_after_tax"))
-		self.base_received_amount_after_tax = flt(flt(self.received_amount_after_tax) * flt(self.target_exchange_rate),
-			self.precision("base_paid_amount_after_tax"))
+		self.received_amount_after_tax = flt(
+			flt(self.received_amount) + flt(applicable_tax), self.precision("paid_amount_after_tax")
+		)
+		self.base_received_amount_after_tax = flt(
+			flt(self.received_amount_after_tax) * flt(self.target_exchange_rate),
+			self.precision("base_paid_amount_after_tax"),
+		)
 
 	def set_amounts_in_company_currency(self):
 		self.base_paid_amount, self.base_received_amount, self.difference_amount = 0, 0, 0
 		if self.paid_amount:
-			self.base_paid_amount = flt(flt(self.paid_amount) * flt(self.source_exchange_rate),
-				self.precision("base_paid_amount"))
+			self.base_paid_amount = flt(
+				flt(self.paid_amount) * flt(self.source_exchange_rate), self.precision("base_paid_amount")
+			)
 
 		if self.received_amount:
-			self.base_received_amount = flt(flt(self.received_amount) * flt(self.target_exchange_rate),
-				self.precision("base_received_amount"))
+			self.base_received_amount = flt(
+				flt(self.received_amount) * flt(self.target_exchange_rate),
+				self.precision("base_received_amount"),
+			)
 
 	def set_total_allocated_amount(self):
 		if self.payment_type == "Internal Transfer":
@@ -535,8 +617,9 @@
 		for d in self.get("references"):
 			if d.allocated_amount:
 				total_allocated_amount += flt(d.allocated_amount)
-				base_total_allocated_amount += flt(flt(d.allocated_amount) * flt(d.exchange_rate),
-					self.precision("base_paid_amount"))
+				base_total_allocated_amount += flt(
+					flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("base_paid_amount")
+				)
 
 		self.total_allocated_amount = abs(total_allocated_amount)
 		self.base_total_allocated_amount = abs(base_total_allocated_amount)
@@ -546,22 +629,33 @@
 		if self.party:
 			total_deductions = sum(flt(d.amount) for d in self.get("deductions"))
 			included_taxes = self.get_included_taxes()
-			if self.payment_type == "Receive" \
-				and self.base_total_allocated_amount < self.base_received_amount + total_deductions \
-				and self.total_allocated_amount < self.paid_amount + (total_deductions / self.source_exchange_rate):
-				self.unallocated_amount = (self.base_received_amount + total_deductions -
-					self.base_total_allocated_amount) / self.source_exchange_rate
+			if (
+				self.payment_type == "Receive"
+				and self.base_total_allocated_amount < self.base_received_amount + total_deductions
+				and self.total_allocated_amount
+				< self.paid_amount + (total_deductions / self.source_exchange_rate)
+			):
+				self.unallocated_amount = (
+					self.base_received_amount + total_deductions - self.base_total_allocated_amount
+				) / self.source_exchange_rate
 				self.unallocated_amount -= included_taxes
-			elif self.payment_type == "Pay" \
-				and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions) \
-				and self.total_allocated_amount < self.received_amount + (total_deductions / self.target_exchange_rate):
-				self.unallocated_amount = (self.base_paid_amount - (total_deductions +
-					self.base_total_allocated_amount)) / self.target_exchange_rate
+			elif (
+				self.payment_type == "Pay"
+				and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions)
+				and self.total_allocated_amount
+				< self.received_amount + (total_deductions / self.target_exchange_rate)
+			):
+				self.unallocated_amount = (
+					self.base_paid_amount - (total_deductions + self.base_total_allocated_amount)
+				) / self.target_exchange_rate
 				self.unallocated_amount -= included_taxes
 
 	def set_difference_amount(self):
-		base_unallocated_amount = flt(self.unallocated_amount) * (flt(self.source_exchange_rate)
-			if self.payment_type == "Receive" else flt(self.target_exchange_rate))
+		base_unallocated_amount = flt(self.unallocated_amount) * (
+			flt(self.source_exchange_rate)
+			if self.payment_type == "Receive"
+			else flt(self.target_exchange_rate)
+		)
 
 		base_party_amount = flt(self.base_total_allocated_amount) + flt(base_unallocated_amount)
 
@@ -575,14 +669,15 @@
 		total_deductions = sum(flt(d.amount) for d in self.get("deductions"))
 		included_taxes = self.get_included_taxes()
 
-		self.difference_amount = flt(self.difference_amount - total_deductions - included_taxes,
-			self.precision("difference_amount"))
+		self.difference_amount = flt(
+			self.difference_amount - total_deductions - included_taxes, self.precision("difference_amount")
+		)
 
 	def get_included_taxes(self):
 		included_taxes = 0
-		for tax in self.get('taxes'):
+		for tax in self.get("taxes"):
 			if tax.included_in_paid_amount:
-				if tax.add_deduct_tax == 'Add':
+				if tax.add_deduct_tax == "Add":
 					included_taxes += tax.base_tax_amount
 				else:
 					included_taxes -= tax.base_tax_amount
@@ -593,27 +688,41 @@
 	# Clear the reference document which doesn't have allocated amount on validate so that form can be loaded fast
 	def clear_unallocated_reference_document_rows(self):
 		self.set("references", self.get("references", {"allocated_amount": ["not in", [0, None, ""]]}))
-		frappe.db.sql("""delete from `tabPayment Entry Reference`
-			where parent = %s and allocated_amount = 0""", self.name)
+		frappe.db.sql(
+			"""delete from `tabPayment Entry Reference`
+			where parent = %s and allocated_amount = 0""",
+			self.name,
+		)
 
 	def validate_payment_against_negative_invoice(self):
-		if ((self.payment_type=="Pay" and self.party_type=="Customer")
-				or (self.payment_type=="Receive" and self.party_type=="Supplier")):
+		if (self.payment_type == "Pay" and self.party_type == "Customer") or (
+			self.payment_type == "Receive" and self.party_type == "Supplier"
+		):
 
-			total_negative_outstanding = sum(abs(flt(d.outstanding_amount))
-				for d in self.get("references") if flt(d.outstanding_amount) < 0)
+			total_negative_outstanding = sum(
+				abs(flt(d.outstanding_amount)) for d in self.get("references") if flt(d.outstanding_amount) < 0
+			)
 
-			paid_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount
+			paid_amount = self.paid_amount if self.payment_type == "Receive" else self.received_amount
 			additional_charges = sum([flt(d.amount) for d in self.deductions])
 
 			if not total_negative_outstanding:
-				frappe.throw(_("Cannot {0} {1} {2} without any negative outstanding invoice")
-					.format(_(self.payment_type), (_("to") if self.party_type=="Customer" else _("from")),
-						self.party_type), InvalidPaymentEntry)
+				frappe.throw(
+					_("Cannot {0} {1} {2} without any negative outstanding invoice").format(
+						_(self.payment_type),
+						(_("to") if self.party_type == "Customer" else _("from")),
+						self.party_type,
+					),
+					InvalidPaymentEntry,
+				)
 
 			elif paid_amount - additional_charges > total_negative_outstanding:
-				frappe.throw(_("Paid Amount cannot be greater than total negative outstanding amount {0}")
-					.format(total_negative_outstanding), InvalidPaymentEntry)
+				frappe.throw(
+					_("Paid Amount cannot be greater than total negative outstanding amount {0}").format(
+						total_negative_outstanding
+					),
+					InvalidPaymentEntry,
+				)
 
 	def set_title(self):
 		if frappe.flags.in_import and self.title:
@@ -634,37 +743,49 @@
 				frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction"))
 
 	def set_remarks(self):
-		if self.custom_remarks: return
+		if self.custom_remarks:
+			return
 
-		if self.payment_type=="Internal Transfer":
-			remarks = [_("Amount {0} {1} transferred from {2} to {3}")
-				.format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)]
+		if self.payment_type == "Internal Transfer":
+			remarks = [
+				_("Amount {0} {1} transferred from {2} to {3}").format(
+					self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to
+				)
+			]
 		else:
 
-			remarks = [_("Amount {0} {1} {2} {3}").format(
-				self.party_account_currency,
-				self.paid_amount if self.payment_type=="Receive" else self.received_amount,
-				_("received from") if self.payment_type=="Receive" else _("to"), self.party
-			)]
+			remarks = [
+				_("Amount {0} {1} {2} {3}").format(
+					self.party_account_currency,
+					self.paid_amount if self.payment_type == "Receive" else self.received_amount,
+					_("received from") if self.payment_type == "Receive" else _("to"),
+					self.party,
+				)
+			]
 
 		if self.reference_no:
-			remarks.append(_("Transaction reference no {0} dated {1}")
-				.format(self.reference_no, self.reference_date))
+			remarks.append(
+				_("Transaction reference no {0} dated {1}").format(self.reference_no, self.reference_date)
+			)
 
 		if self.payment_type in ["Receive", "Pay"]:
 			for d in self.get("references"):
 				if d.allocated_amount:
-					remarks.append(_("Amount {0} {1} against {2} {3}").format(self.party_account_currency,
-						d.allocated_amount, d.reference_doctype, d.reference_name))
+					remarks.append(
+						_("Amount {0} {1} against {2} {3}").format(
+							self.party_account_currency, d.allocated_amount, d.reference_doctype, d.reference_name
+						)
+					)
 
 		for d in self.get("deductions"):
 			if d.amount:
-				remarks.append(_("Amount {0} {1} deducted against {2}")
-					.format(self.company_currency, d.amount, d.account))
+				remarks.append(
+					_("Amount {0} {1} deducted against {2}").format(self.company_currency, d.amount, d.account)
+				)
 
 		self.set("remarks", "\n".join(remarks))
 
-	def make_gl_entries(self, cancel=0, adv_adj=0):
+	def build_gl_map(self):
 		if self.payment_type in ("Receive", "Pay") and not self.get("party_account_field"):
 			self.setup_party_account_field()
 
@@ -673,98 +794,119 @@
 		self.add_bank_gl_entries(gl_entries)
 		self.add_deductions_gl_entries(gl_entries)
 		self.add_tax_gl_entries(gl_entries)
+		return gl_entries
 
+	def make_gl_entries(self, cancel=0, adv_adj=0):
+		gl_entries = self.build_gl_map()
 		gl_entries = process_gl_map(gl_entries)
 		make_gl_entries(gl_entries, cancel=cancel, adv_adj=adv_adj)
 
 	def add_party_gl_entries(self, gl_entries):
 		if self.party_account:
-			if self.payment_type=="Receive":
+			if self.payment_type == "Receive":
 				against_account = self.paid_to
 			else:
 				against_account = self.paid_from
 
-			party_gl_dict = self.get_gl_dict({
-				"account": self.party_account,
-				"party_type": self.party_type,
-				"party": self.party,
-				"against": against_account,
-				"account_currency": self.party_account_currency,
-				"cost_center": self.cost_center
-			}, item=self)
+			party_gl_dict = self.get_gl_dict(
+				{
+					"account": self.party_account,
+					"party_type": self.party_type,
+					"party": self.party,
+					"against": against_account,
+					"account_currency": self.party_account_currency,
+					"cost_center": self.cost_center,
+				},
+				item=self,
+			)
 
-			dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit"
+			dr_or_cr = (
+				"credit" if erpnext.get_party_account_type(self.party_type) == "Receivable" else "debit"
+			)
 
 			for d in self.get("references"):
 				cost_center = self.cost_center
 				if d.reference_doctype == "Sales Invoice" and not cost_center:
 					cost_center = frappe.db.get_value(d.reference_doctype, d.reference_name, "cost_center")
 				gle = party_gl_dict.copy()
-				gle.update({
-					"against_voucher_type": d.reference_doctype,
-					"against_voucher": d.reference_name,
-					"cost_center": cost_center
-				})
+				gle.update(
+					{
+						"against_voucher_type": d.reference_doctype,
+						"against_voucher": d.reference_name,
+						"cost_center": cost_center,
+					}
+				)
 
-				allocated_amount_in_company_currency = flt(flt(d.allocated_amount) * flt(d.exchange_rate),
-					self.precision("paid_amount"))
+				allocated_amount_in_company_currency = flt(
+					flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("paid_amount")
+				)
 
-				gle.update({
-					dr_or_cr + "_in_account_currency": d.allocated_amount,
-					dr_or_cr: allocated_amount_in_company_currency
-				})
+				gle.update(
+					{
+						dr_or_cr + "_in_account_currency": d.allocated_amount,
+						dr_or_cr: allocated_amount_in_company_currency,
+					}
+				)
 
 				gl_entries.append(gle)
 
 			if self.unallocated_amount:
 				exchange_rate = self.get_exchange_rate()
-				base_unallocated_amount = (self.unallocated_amount * exchange_rate)
+				base_unallocated_amount = self.unallocated_amount * exchange_rate
 
 				gle = party_gl_dict.copy()
 
-				gle.update({
-					dr_or_cr + "_in_account_currency": self.unallocated_amount,
-					dr_or_cr: base_unallocated_amount
-				})
+				gle.update(
+					{
+						dr_or_cr + "_in_account_currency": self.unallocated_amount,
+						dr_or_cr: base_unallocated_amount,
+					}
+				)
 
 				gl_entries.append(gle)
 
 	def add_bank_gl_entries(self, gl_entries):
 		if self.payment_type in ("Pay", "Internal Transfer"):
 			gl_entries.append(
-				self.get_gl_dict({
-					"account": self.paid_from,
-					"account_currency": self.paid_from_account_currency,
-					"against": self.party if self.payment_type=="Pay" else self.paid_to,
-					"credit_in_account_currency": self.paid_amount,
-					"credit": self.base_paid_amount,
-					"cost_center": self.cost_center,
-					"post_net_value": True
-				}, item=self)
+				self.get_gl_dict(
+					{
+						"account": self.paid_from,
+						"account_currency": self.paid_from_account_currency,
+						"against": self.party if self.payment_type == "Pay" else self.paid_to,
+						"credit_in_account_currency": self.paid_amount,
+						"credit": self.base_paid_amount,
+						"cost_center": self.cost_center,
+						"post_net_value": True,
+					},
+					item=self,
+				)
 			)
 		if self.payment_type in ("Receive", "Internal Transfer"):
 			gl_entries.append(
-				self.get_gl_dict({
-					"account": self.paid_to,
-					"account_currency": self.paid_to_account_currency,
-					"against": self.party if self.payment_type=="Receive" else self.paid_from,
-					"debit_in_account_currency": self.received_amount,
-					"debit": self.base_received_amount,
-					"cost_center": self.cost_center
-				}, item=self)
+				self.get_gl_dict(
+					{
+						"account": self.paid_to,
+						"account_currency": self.paid_to_account_currency,
+						"against": self.party if self.payment_type == "Receive" else self.paid_from,
+						"debit_in_account_currency": self.received_amount,
+						"debit": self.base_received_amount,
+						"cost_center": self.cost_center,
+					},
+					item=self,
+				)
 			)
 
 	def add_tax_gl_entries(self, gl_entries):
-		for d in self.get('taxes'):
+		for d in self.get("taxes"):
 			account_currency = get_account_currency(d.account_head)
 			if account_currency != self.company_currency:
 				frappe.throw(_("Currency for {0} must be {1}").format(d.account_head, self.company_currency))
 
-			if self.payment_type in ('Pay', 'Internal Transfer'):
+			if self.payment_type in ("Pay", "Internal Transfer"):
 				dr_or_cr = "debit" if d.add_deduct_tax == "Add" else "credit"
 				rev_dr_or_cr = "credit" if dr_or_cr == "debit" else "debit"
 				against = self.party or self.paid_from
-			elif self.payment_type == 'Receive':
+			elif self.payment_type == "Receive":
 				dr_or_cr = "credit" if d.add_deduct_tax == "Add" else "debit"
 				rev_dr_or_cr = "credit" if dr_or_cr == "debit" else "debit"
 				against = self.party or self.paid_to
@@ -774,29 +916,39 @@
 			base_tax_amount = d.base_tax_amount
 
 			gl_entries.append(
-				self.get_gl_dict({
-					"account": d.account_head,
-					"against": against,
-					dr_or_cr: tax_amount,
-					dr_or_cr + "_in_account_currency": base_tax_amount
-					if account_currency==self.company_currency
-					else d.tax_amount,
-					"cost_center": d.cost_center,
-					"post_net_value": True,
-				}, account_currency, item=d))
+				self.get_gl_dict(
+					{
+						"account": d.account_head,
+						"against": against,
+						dr_or_cr: tax_amount,
+						dr_or_cr + "_in_account_currency": base_tax_amount
+						if account_currency == self.company_currency
+						else d.tax_amount,
+						"cost_center": d.cost_center,
+						"post_net_value": True,
+					},
+					account_currency,
+					item=d,
+				)
+			)
 
 			if not d.included_in_paid_amount:
 				gl_entries.append(
-					self.get_gl_dict({
-						"account": payment_account,
-						"against": against,
-						rev_dr_or_cr: tax_amount,
-						rev_dr_or_cr + "_in_account_currency": base_tax_amount
-						if account_currency==self.company_currency
-						else d.tax_amount,
-						"cost_center": self.cost_center,
-						"post_net_value": True,
-					}, account_currency, item=d))
+					self.get_gl_dict(
+						{
+							"account": payment_account,
+							"against": against,
+							rev_dr_or_cr: tax_amount,
+							rev_dr_or_cr + "_in_account_currency": base_tax_amount
+							if account_currency == self.company_currency
+							else d.tax_amount,
+							"cost_center": self.cost_center,
+							"post_net_value": True,
+						},
+						account_currency,
+						item=d,
+					)
+				)
 
 	def add_deductions_gl_entries(self, gl_entries):
 		for d in self.get("deductions"):
@@ -806,33 +958,40 @@
 					frappe.throw(_("Currency for {0} must be {1}").format(d.account, self.company_currency))
 
 				gl_entries.append(
-					self.get_gl_dict({
-						"account": d.account,
-						"account_currency": account_currency,
-						"against": self.party or self.paid_from,
-						"debit_in_account_currency": d.amount,
-						"debit": d.amount,
-						"cost_center": d.cost_center
-					}, item=d)
+					self.get_gl_dict(
+						{
+							"account": d.account,
+							"account_currency": account_currency,
+							"against": self.party or self.paid_from,
+							"debit_in_account_currency": d.amount,
+							"debit": d.amount,
+							"cost_center": d.cost_center,
+						},
+						item=d,
+					)
 				)
 
 	def get_party_account_for_taxes(self):
-		if self.payment_type == 'Receive':
+		if self.payment_type == "Receive":
 			return self.paid_to
-		elif self.payment_type in ('Pay', 'Internal Transfer'):
+		elif self.payment_type in ("Pay", "Internal Transfer"):
 			return self.paid_from
 
 	def update_advance_paid(self):
 		if self.payment_type in ("Receive", "Pay") and self.party:
 			for d in self.get("references"):
-				if d.allocated_amount \
-					and d.reference_doctype in ("Sales Order", "Purchase Order", "Employee Advance", "Gratuity"):
-						frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid()
+				if d.allocated_amount and d.reference_doctype in (
+					"Sales Order",
+					"Purchase Order",
+					"Employee Advance",
+					"Gratuity",
+				):
+					frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid()
 
 	def update_expense_claim(self):
 		if self.payment_type in ("Pay") and self.party:
 			for d in self.get("references"):
-				if d.reference_doctype=="Expense Claim" and d.reference_name:
+				if d.reference_doctype == "Expense Claim" and d.reference_name:
 					doc = frappe.get_doc("Expense Claim", d.reference_name)
 					if self.docstatus == 2:
 						update_reimbursed_amount(doc, -1 * d.allocated_amount)
@@ -845,31 +1004,29 @@
 
 	def calculate_deductions(self, tax_details):
 		return {
-			"account": tax_details['tax']['account_head'],
-			"cost_center": frappe.get_cached_value('Company',  self.company,  "cost_center"),
-			"amount": self.total_allocated_amount * (tax_details['tax']['rate'] / 100)
+			"account": tax_details["tax"]["account_head"],
+			"cost_center": frappe.get_cached_value("Company", self.company, "cost_center"),
+			"amount": self.total_allocated_amount * (tax_details["tax"]["rate"] / 100),
 		}
 
 	def set_gain_or_loss(self, account_details=None):
 		if not self.difference_amount:
 			self.set_difference_amount()
 
-		row = {
-			'amount': self.difference_amount
-		}
+		row = {"amount": self.difference_amount}
 
 		if account_details:
 			row.update(account_details)
 
-		if not row.get('amount'):
+		if not row.get("amount"):
 			# if no difference amount
 			return
 
-		self.append('deductions', row)
+		self.append("deductions", row)
 		self.set_unallocated_amount()
 
 	def get_exchange_rate(self):
-		return self.source_exchange_rate if self.payment_type=="Receive" else self.target_exchange_rate
+		return self.source_exchange_rate if self.payment_type == "Receive" else self.target_exchange_rate
 
 	def initialize_taxes(self):
 		for tax in self.get("taxes"):
@@ -893,25 +1050,31 @@
 		cumulated_tax_fraction = 0
 		for i, tax in enumerate(self.get("taxes")):
 			tax.tax_fraction_for_current_item = self.get_current_tax_fraction(tax)
-			if i==0:
+			if i == 0:
 				tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
 			else:
-				tax.grand_total_fraction_for_current_item = \
-					self.get("taxes")[i-1].grand_total_fraction_for_current_item \
+				tax.grand_total_fraction_for_current_item = (
+					self.get("taxes")[i - 1].grand_total_fraction_for_current_item
 					+ tax.tax_fraction_for_current_item
+				)
 
 			cumulated_tax_fraction += tax.tax_fraction_for_current_item
 
-		self.paid_amount_after_tax = flt(self.paid_amount/(1+cumulated_tax_fraction))
+		self.paid_amount_after_tax = flt(self.paid_amount / (1 + cumulated_tax_fraction))
 
 	def calculate_taxes(self):
 		self.total_taxes_and_charges = 0.0
 		self.base_total_taxes_and_charges = 0.0
 
-		actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
-			for tax in self.get("taxes") if tax.charge_type == "Actual"])
+		actual_tax_dict = dict(
+			[
+				[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
+				for tax in self.get("taxes")
+				if tax.charge_type == "Actual"
+			]
+		)
 
-		for i, tax in enumerate(self.get('taxes')):
+		for i, tax in enumerate(self.get("taxes")):
 			current_tax_amount = self.get_current_tax_amount(tax)
 
 			if tax.charge_type == "Actual":
@@ -930,19 +1093,21 @@
 			if i == 0:
 				tax.total = flt(self.paid_amount_after_tax + current_tax_amount, self.precision("total", tax))
 			else:
-				tax.total = flt(self.get('taxes')[i-1].total + current_tax_amount, self.precision("total", tax))
+				tax.total = flt(
+					self.get("taxes")[i - 1].total + current_tax_amount, self.precision("total", tax)
+				)
 
 			tax.base_total = tax.total * self.source_exchange_rate
 
-			if self.payment_type == 'Pay':
+			if self.payment_type == "Pay":
 				self.base_total_taxes_and_charges += flt(current_tax_amount / self.source_exchange_rate)
 				self.total_taxes_and_charges += flt(current_tax_amount / self.target_exchange_rate)
 			else:
 				self.base_total_taxes_and_charges += flt(current_tax_amount / self.target_exchange_rate)
 				self.total_taxes_and_charges += flt(current_tax_amount / self.source_exchange_rate)
 
-		if self.get('taxes'):
-			self.paid_amount_after_tax = self.get('taxes')[-1].base_total
+		if self.get("taxes"):
+			self.paid_amount_after_tax = self.get("taxes")[-1].base_total
 
 	def get_current_tax_amount(self, tax):
 		tax_rate = tax.rate
@@ -950,7 +1115,11 @@
 		# To set row_id by default as previous row.
 		if tax.charge_type in ["On Previous Row Amount", "On Previous Row Total"]:
 			if tax.idx == 1:
-				frappe.throw(_("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row"))
+				frappe.throw(
+					_(
+						"Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row"
+					)
+				)
 
 			if not tax.row_id:
 				tax.row_id = tax.idx - 1
@@ -960,12 +1129,10 @@
 		elif tax.charge_type == "On Paid Amount":
 			current_tax_amount = (tax_rate / 100.0) * self.paid_amount_after_tax
 		elif tax.charge_type == "On Previous Row Amount":
-			current_tax_amount = (tax_rate / 100.0) * \
-				self.get('taxes')[cint(tax.row_id) - 1].tax_amount
+			current_tax_amount = (tax_rate / 100.0) * self.get("taxes")[cint(tax.row_id) - 1].tax_amount
 
 		elif tax.charge_type == "On Previous Row Total":
-			current_tax_amount = (tax_rate / 100.0) * \
-				self.get('taxes')[cint(tax.row_id) - 1].total
+			current_tax_amount = (tax_rate / 100.0) * self.get("taxes")[cint(tax.row_id) - 1].total
 
 		return current_tax_amount
 
@@ -978,83 +1145,115 @@
 			if tax.charge_type == "On Paid Amount":
 				current_tax_fraction = tax_rate / 100.0
 			elif tax.charge_type == "On Previous Row Amount":
-				current_tax_fraction = (tax_rate / 100.0) * \
-					self.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item
+				current_tax_fraction = (tax_rate / 100.0) * self.get("taxes")[
+					cint(tax.row_id) - 1
+				].tax_fraction_for_current_item
 			elif tax.charge_type == "On Previous Row Total":
-				current_tax_fraction = (tax_rate / 100.0) * \
-					self.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
+				current_tax_fraction = (tax_rate / 100.0) * self.get("taxes")[
+					cint(tax.row_id) - 1
+				].grand_total_fraction_for_current_item
 
 		if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
 			current_tax_fraction *= -1.0
 
 		return current_tax_fraction
 
+
 def validate_inclusive_tax(tax, doc):
 	def _on_previous_row_error(row_range):
-		throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx, row_range))
+		throw(
+			_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(
+				tax.idx, row_range
+			)
+		)
 
 	if cint(getattr(tax, "included_in_paid_amount", None)):
 		if tax.charge_type == "Actual":
 			# inclusive tax cannot be of type Actual
-			throw(_("Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount").format(tax.idx))
-		elif tax.charge_type == "On Previous Row Amount" and \
-				not cint(doc.get("taxes")[cint(tax.row_id) - 1].included_in_paid_amount):
+			throw(
+				_("Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount").format(
+					tax.idx
+				)
+			)
+		elif tax.charge_type == "On Previous Row Amount" and not cint(
+			doc.get("taxes")[cint(tax.row_id) - 1].included_in_paid_amount
+		):
 			# referred row should also be inclusive
 			_on_previous_row_error(tax.row_id)
-		elif tax.charge_type == "On Previous Row Total" and \
-				not all([cint(t.included_in_paid_amount for t in doc.get("taxes")[:cint(tax.row_id) - 1])]):
+		elif tax.charge_type == "On Previous Row Total" and not all(
+			[cint(t.included_in_paid_amount for t in doc.get("taxes")[: cint(tax.row_id) - 1])]
+		):
 			# all rows about the referred tax should be inclusive
 			_on_previous_row_error("1 - %d" % (cint(tax.row_id),))
 		elif tax.get("category") == "Valuation":
 			frappe.throw(_("Valuation type charges can not be marked as Inclusive"))
 
+
 @frappe.whitelist()
 def get_outstanding_reference_documents(args):
 
 	if isinstance(args, str):
 		args = json.loads(args)
 
-	if args.get('party_type') == 'Member':
+	if args.get("party_type") == "Member":
 		return
 
+	ple = qb.DocType("Payment Ledger Entry")
+	common_filter = []
+
 	# confirm that Supplier is not blocked
-	if args.get('party_type') == 'Supplier':
-		supplier_status = get_supplier_block_status(args['party'])
-		if supplier_status['on_hold']:
-			if supplier_status['hold_type'] == 'All':
+	if args.get("party_type") == "Supplier":
+		supplier_status = get_supplier_block_status(args["party"])
+		if supplier_status["on_hold"]:
+			if supplier_status["hold_type"] == "All":
 				return []
-			elif supplier_status['hold_type'] == 'Payments':
-				if not supplier_status['release_date'] or getdate(nowdate()) <= supplier_status['release_date']:
+			elif supplier_status["hold_type"] == "Payments":
+				if (
+					not supplier_status["release_date"] or getdate(nowdate()) <= supplier_status["release_date"]
+				):
 					return []
 
 	party_account_currency = get_account_currency(args.get("party_account"))
-	company_currency = frappe.get_cached_value('Company',  args.get("company"),  "default_currency")
+	company_currency = frappe.get_cached_value("Company", args.get("company"), "default_currency")
 
 	# Get positive outstanding sales /purchase invoices/ Fees
 	condition = ""
 	if args.get("voucher_type") and args.get("voucher_no"):
-		condition = " and voucher_type={0} and voucher_no={1}"\
-			.format(frappe.db.escape(args["voucher_type"]), frappe.db.escape(args["voucher_no"]))
+		condition = " and voucher_type={0} and voucher_no={1}".format(
+			frappe.db.escape(args["voucher_type"]), frappe.db.escape(args["voucher_no"])
+		)
+		common_filter.append(ple.voucher_type == args["voucher_type"])
+		common_filter.append(ple.voucher_no == args["voucher_no"])
 
 	# Add cost center condition
 	if args.get("cost_center"):
 		condition += " and cost_center='%s'" % args.get("cost_center")
+		common_filter.append(ple.cost_center == args.get("cost_center"))
 
 	date_fields_dict = {
-		'posting_date': ['from_posting_date', 'to_posting_date'],
-		'due_date': ['from_due_date', 'to_due_date']
+		"posting_date": ["from_posting_date", "to_posting_date"],
+		"due_date": ["from_due_date", "to_due_date"],
 	}
 
 	for fieldname, date_fields in date_fields_dict.items():
 		if args.get(date_fields[0]) and args.get(date_fields[1]):
-			condition += " and {0} between '{1}' and '{2}'".format(fieldname,
-				args.get(date_fields[0]), args.get(date_fields[1]))
+			condition += " and {0} between '{1}' and '{2}'".format(
+				fieldname, args.get(date_fields[0]), args.get(date_fields[1])
+			)
+			common_filter.append(ple[fieldname][args.get(date_fields[0]) : args.get(date_fields[1])])
 
 	if args.get("company"):
 		condition += " and company = {0}".format(frappe.db.escape(args.get("company")))
+		common_filter.append(ple.company == args.get("company"))
 
-	outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"),
-		args.get("party_account"), filters=args, condition=condition)
+	outstanding_invoices = get_outstanding_invoices(
+		args.get("party_type"),
+		args.get("party"),
+		args.get("party_account"),
+		common_filter=common_filter,
+		min_outstanding=args.get("outstanding_amt_greater_than"),
+		max_outstanding=args.get("outstanding_amt_less_than"),
+	)
 
 	outstanding_invoices = split_invoices_based_on_payment_terms(outstanding_invoices)
 
@@ -1065,28 +1264,43 @@
 				d["exchange_rate"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "conversion_rate")
 			elif d.voucher_type == "Journal Entry":
 				d["exchange_rate"] = get_exchange_rate(
-					party_account_currency,	company_currency, d.posting_date
+					party_account_currency, company_currency, d.posting_date
 				)
 		if d.voucher_type in ("Purchase Invoice"):
 			d["bill_no"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "bill_no")
 
 	# Get all SO / PO which are not fully billed or against which full advance not paid
 	orders_to_be_billed = []
-	if (args.get("party_type") != "Student"):
-		orders_to_be_billed =  get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"),
-			args.get("party"), args.get("company"), party_account_currency, company_currency, filters=args)
+	orders_to_be_billed = get_orders_to_be_billed(
+		args.get("posting_date"),
+		args.get("party_type"),
+		args.get("party"),
+		args.get("company"),
+		party_account_currency,
+		company_currency,
+		filters=args,
+	)
 
 	# Get negative outstanding sales /purchase invoices
 	negative_outstanding_invoices = []
-	if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"):
-		negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), args.get("party"),
-			args.get("party_account"), party_account_currency, company_currency, condition=condition)
+	if args.get("party_type") != "Employee" and not args.get("voucher_no"):
+		negative_outstanding_invoices = get_negative_outstanding_invoices(
+			args.get("party_type"),
+			args.get("party"),
+			args.get("party_account"),
+			party_account_currency,
+			company_currency,
+			condition=condition,
+		)
 
 	data = negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed
 
 	if not data:
-		frappe.msgprint(_("No outstanding invoices found for the {0} {1} which qualify the filters you have specified.")
-			.format(_(args.get("party_type")).lower(), frappe.bold(args.get("party"))))
+		frappe.msgprint(
+			_(
+				"No outstanding invoices found for the {0} {1} which qualify the filters you have specified."
+			).format(_(args.get("party_type")).lower(), frappe.bold(args.get("party")))
+		)
 
 	return data
 
@@ -1094,53 +1308,75 @@
 def split_invoices_based_on_payment_terms(outstanding_invoices):
 	invoice_ref_based_on_payment_terms = {}
 	for idx, d in enumerate(outstanding_invoices):
-		if d.voucher_type in ['Sales Invoice', 'Purchase Invoice']:
-			payment_term_template = frappe.db.get_value(d.voucher_type, d.voucher_no, 'payment_terms_template')
+		if d.voucher_type in ["Sales Invoice", "Purchase Invoice"]:
+			payment_term_template = frappe.db.get_value(
+				d.voucher_type, d.voucher_no, "payment_terms_template"
+			)
 			if payment_term_template:
 				allocate_payment_based_on_payment_terms = frappe.db.get_value(
-					'Payment Terms Template', payment_term_template, 'allocate_payment_based_on_payment_terms')
+					"Payment Terms Template", payment_term_template, "allocate_payment_based_on_payment_terms"
+				)
 				if allocate_payment_based_on_payment_terms:
-					payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': d.voucher_no}, fields=["*"])
+					payment_schedule = frappe.get_all(
+						"Payment Schedule", filters={"parent": d.voucher_no}, fields=["*"]
+					)
 
 					for payment_term in payment_schedule:
 						if payment_term.outstanding > 0.1:
 							invoice_ref_based_on_payment_terms.setdefault(idx, [])
-							invoice_ref_based_on_payment_terms[idx].append(frappe._dict({
-								'due_date': d.due_date,
-								'currency': d.currency,
-								'voucher_no': d.voucher_no,
-								'voucher_type': d.voucher_type,
-								'posting_date': d.posting_date,
-								'invoice_amount': flt(d.invoice_amount),
-								'outstanding_amount': flt(d.outstanding_amount),
-								'payment_amount': payment_term.payment_amount,
-								'payment_term': payment_term.payment_term
-							}))
+							invoice_ref_based_on_payment_terms[idx].append(
+								frappe._dict(
+									{
+										"due_date": d.due_date,
+										"currency": d.currency,
+										"voucher_no": d.voucher_no,
+										"voucher_type": d.voucher_type,
+										"posting_date": d.posting_date,
+										"invoice_amount": flt(d.invoice_amount),
+										"outstanding_amount": flt(d.outstanding_amount),
+										"payment_amount": payment_term.payment_amount,
+										"payment_term": payment_term.payment_term,
+									}
+								)
+							)
 
 	outstanding_invoices_after_split = []
 	if invoice_ref_based_on_payment_terms:
 		for idx, ref in invoice_ref_based_on_payment_terms.items():
-			voucher_no = ref[0]['voucher_no']
-			voucher_type = ref[0]['voucher_type']
+			voucher_no = ref[0]["voucher_no"]
+			voucher_type = ref[0]["voucher_type"]
 
-			frappe.msgprint(_("Spliting {} {} into {} row(s) as per Payment Terms").format(
-				voucher_type, voucher_no, len(ref)), alert=True)
+			frappe.msgprint(
+				_("Spliting {} {} into {} row(s) as per Payment Terms").format(
+					voucher_type, voucher_no, len(ref)
+				),
+				alert=True,
+			)
 
 			outstanding_invoices_after_split += invoice_ref_based_on_payment_terms[idx]
 
-			existing_row = list(filter(lambda x: x.get('voucher_no') == voucher_no, outstanding_invoices))
+			existing_row = list(filter(lambda x: x.get("voucher_no") == voucher_no, outstanding_invoices))
 			index = outstanding_invoices.index(existing_row[0])
 			outstanding_invoices.pop(index)
 
 	outstanding_invoices_after_split += outstanding_invoices
 	return outstanding_invoices_after_split
 
-def get_orders_to_be_billed(posting_date, party_type, party,
-	company, party_account_currency, company_currency, cost_center=None, filters=None):
+
+def get_orders_to_be_billed(
+	posting_date,
+	party_type,
+	party,
+	company,
+	party_account_currency,
+	company_currency,
+	cost_center=None,
+	filters=None,
+):
 	if party_type == "Customer":
-		voucher_type = 'Sales Order'
+		voucher_type = "Sales Order"
 	elif party_type == "Supplier":
-		voucher_type = 'Purchase Order'
+		voucher_type = "Purchase Order"
 	elif party_type == "Employee":
 		voucher_type = None
 
@@ -1148,7 +1384,7 @@
 	if voucher_type:
 		doc = frappe.get_doc({"doctype": voucher_type})
 		condition = ""
-		if doc and hasattr(doc, 'cost_center'):
+		if doc and hasattr(doc, "cost_center"):
 			condition = " and cost_center='%s'" % cost_center
 
 	orders = []
@@ -1160,7 +1396,8 @@
 			grand_total_field = "grand_total"
 			rounded_total_field = "rounded_total"
 
-		orders = frappe.db.sql("""
+		orders = frappe.db.sql(
+			"""
 			select
 				name as voucher_no,
 				if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
@@ -1178,18 +1415,25 @@
 				{condition}
 			order by
 				transaction_date, name
-		""".format(**{
-			"rounded_total_field": rounded_total_field,
-			"grand_total_field": grand_total_field,
-			"voucher_type": voucher_type,
-			"party_type": scrub(party_type),
-			"condition": condition
-		}), (party, company), as_dict=True)
+		""".format(
+				**{
+					"rounded_total_field": rounded_total_field,
+					"grand_total_field": grand_total_field,
+					"voucher_type": voucher_type,
+					"party_type": scrub(party_type),
+					"condition": condition,
+				}
+			),
+			(party, company),
+			as_dict=True,
+		)
 
 	order_list = []
 	for d in orders:
-		if not (flt(d.outstanding_amount) >= flt(filters.get("outstanding_amt_greater_than"))
-			and flt(d.outstanding_amount) <= flt(filters.get("outstanding_amt_less_than"))):
+		if not (
+			flt(d.outstanding_amount) >= flt(filters.get("outstanding_amt_greater_than"))
+			and flt(d.outstanding_amount) <= flt(filters.get("outstanding_amt_less_than"))
+		):
 			continue
 
 		d["voucher_type"] = voucher_type
@@ -1199,12 +1443,20 @@
 
 	return order_list
 
-def get_negative_outstanding_invoices(party_type, party, party_account,
-	party_account_currency, company_currency, cost_center=None, condition=None):
+
+def get_negative_outstanding_invoices(
+	party_type,
+	party,
+	party_account,
+	party_account_currency,
+	company_currency,
+	cost_center=None,
+	condition=None,
+):
 	voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
 	supplier_condition = ""
 	if voucher_type == "Purchase Invoice":
-		supplier_condition = "and (release_date is null or release_date <= CURDATE())"
+		supplier_condition = "and (release_date is null or release_date <= CURRENT_DATE)"
 	if party_account_currency == company_currency:
 		grand_total_field = "base_grand_total"
 		rounded_total_field = "base_rounded_total"
@@ -1212,7 +1464,8 @@
 		grand_total_field = "grand_total"
 		rounded_total_field = "rounded_total"
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select
 			"{voucher_type}" as voucher_type, name as voucher_no,
 			if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
@@ -1227,21 +1480,26 @@
 			{condition}
 		order by
 			posting_date, name
-		""".format(**{
-			"supplier_condition": supplier_condition,
-			"condition": condition,
-			"rounded_total_field": rounded_total_field,
-			"grand_total_field": grand_total_field,
-			"voucher_type": voucher_type,
-			"party_type": scrub(party_type),
-			"party_account": "debit_to" if party_type == "Customer" else "credit_to",
-			"cost_center": cost_center
-		}), (party, party_account), as_dict=True)
+		""".format(
+			**{
+				"supplier_condition": supplier_condition,
+				"condition": condition,
+				"rounded_total_field": rounded_total_field,
+				"grand_total_field": grand_total_field,
+				"voucher_type": voucher_type,
+				"party_type": scrub(party_type),
+				"party_account": "debit_to" if party_type == "Customer" else "credit_to",
+				"cost_center": cost_center,
+			}
+		),
+		(party, party_account),
+		as_dict=True,
+	)
 
 
 @frappe.whitelist()
 def get_party_details(company, party_type, party, date, cost_center=None):
-	bank_account = ''
+	bank_account = ""
 	if not frappe.db.exists(party_type, party):
 		frappe.throw(_("Invalid {0}: {1}").format(party_type, party))
 
@@ -1249,7 +1507,7 @@
 
 	account_currency = get_account_currency(party_account)
 	account_balance = get_balance_on(party_account, date, cost_center=cost_center)
-	_party_name = "title" if party_type in ("Student", "Shareholder") else party_type.lower() + "_name"
+	_party_name = "title" if party_type == "Shareholder" else party_type.lower() + "_name"
 	party_name = frappe.db.get_value(party_type, party, _party_name)
 	party_balance = get_balance_on(party_type=party_type, party=party, cost_center=cost_center)
 	if party_type in ["Customer", "Supplier"]:
@@ -1261,61 +1519,68 @@
 		"party_account_currency": account_currency,
 		"party_balance": party_balance,
 		"account_balance": account_balance,
-		"bank_account": bank_account
+		"bank_account": bank_account,
 	}
 
 
 @frappe.whitelist()
 def get_account_details(account, date, cost_center=None):
-	frappe.has_permission('Payment Entry', throw=True)
+	frappe.has_permission("Payment Entry", throw=True)
 
 	# to check if the passed account is accessible under reference doctype Payment Entry
-	account_list = frappe.get_list('Account', {
-		'name': account
-	}, reference_doctype='Payment Entry', limit=1)
+	account_list = frappe.get_list(
+		"Account", {"name": account}, reference_doctype="Payment Entry", limit=1
+	)
 
 	# There might be some user permissions which will allow account under certain doctypes
 	# except for Payment Entry, only in such case we should throw permission error
 	if not account_list:
-		frappe.throw(_('Account: {0} is not permitted under Payment Entry').format(account))
+		frappe.throw(_("Account: {0} is not permitted under Payment Entry").format(account))
 
-	account_balance = get_balance_on(account, date, cost_center=cost_center,
-		ignore_account_permission=True)
+	account_balance = get_balance_on(
+		account, date, cost_center=cost_center, ignore_account_permission=True
+	)
 
-	return frappe._dict({
-		"account_currency": get_account_currency(account),
-		"account_balance": account_balance,
-		"account_type": frappe.db.get_value("Account", account, "account_type")
-	})
+	return frappe._dict(
+		{
+			"account_currency": get_account_currency(account),
+			"account_balance": account_balance,
+			"account_type": frappe.db.get_value("Account", account, "account_type"),
+		}
+	)
 
 
 @frappe.whitelist()
 def get_company_defaults(company):
 	fields = ["write_off_account", "exchange_gain_loss_account", "cost_center"]
-	ret = frappe.get_cached_value('Company',  company,  fields, as_dict=1)
+	ret = frappe.get_cached_value("Company", company, fields, as_dict=1)
 
 	for fieldname in fields:
 		if not ret[fieldname]:
-			frappe.throw(_("Please set default {0} in Company {1}")
-				.format(frappe.get_meta("Company").get_label(fieldname), company))
+			frappe.throw(
+				_("Please set default {0} in Company {1}").format(
+					frappe.get_meta("Company").get_label(fieldname), company
+				)
+			)
 
 	return ret
 
 
 def get_outstanding_on_journal_entry(name):
 	res = frappe.db.sql(
-			'SELECT '
-			'CASE WHEN party_type IN ("Customer", "Student") '
-			'THEN ifnull(sum(debit_in_account_currency - credit_in_account_currency), 0) '
-			'ELSE ifnull(sum(credit_in_account_currency - debit_in_account_currency), 0) '
-			'END as outstanding_amount '
-			'FROM `tabGL Entry` WHERE (voucher_no=%s OR against_voucher=%s) '
-			'AND party_type IS NOT NULL '
-			'AND party_type != ""',
-			(name, name), as_dict=1
-		)
+		"SELECT "
+		'CASE WHEN party_type IN ("Customer") '
+		"THEN ifnull(sum(debit_in_account_currency - credit_in_account_currency), 0) "
+		"ELSE ifnull(sum(credit_in_account_currency - debit_in_account_currency), 0) "
+		"END as outstanding_amount "
+		"FROM `tabGL Entry` WHERE (voucher_no=%s OR against_voucher=%s) "
+		"AND party_type IS NOT NULL "
+		'AND party_type != ""',
+		(name, name),
+		as_dict=1,
+	)
 
-	outstanding_amount = res[0].get('outstanding_amount', 0) if res else 0
+	outstanding_amount = res[0].get("outstanding_amount", 0) if res else 0
 
 	return outstanding_amount
 
@@ -1324,7 +1589,9 @@
 def get_reference_details(reference_doctype, reference_name, party_account_currency):
 	total_amount = outstanding_amount = exchange_rate = bill_no = None
 	ref_doc = frappe.get_doc(reference_doctype, reference_name)
-	company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency(ref_doc.company)
+	company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency(
+		ref_doc.company
+	)
 
 	if reference_doctype == "Fees":
 		total_amount = ref_doc.get("grand_total")
@@ -1337,20 +1604,22 @@
 	elif reference_doctype == "Journal Entry" and ref_doc.docstatus == 1:
 		total_amount = ref_doc.get("total_amount")
 		if ref_doc.multi_currency:
-			exchange_rate = get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
+			exchange_rate = get_exchange_rate(
+				party_account_currency, company_currency, ref_doc.posting_date
+			)
 		else:
 			exchange_rate = 1
 			outstanding_amount = get_outstanding_on_journal_entry(reference_name)
 	elif reference_doctype != "Journal Entry":
 		if ref_doc.doctype == "Expense Claim":
-				total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
+			total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
 		elif ref_doc.doctype == "Employee Advance":
 			total_amount = ref_doc.advance_amount
 			exchange_rate = ref_doc.get("exchange_rate")
 			if party_account_currency != ref_doc.currency:
 				total_amount = flt(total_amount) * flt(exchange_rate)
 		elif ref_doc.doctype == "Gratuity":
-				total_amount = ref_doc.amount
+			total_amount = ref_doc.amount
 		if not total_amount:
 			if party_account_currency == company_currency:
 				total_amount = ref_doc.base_grand_total
@@ -1360,16 +1629,21 @@
 		if not exchange_rate:
 			# Get the exchange rate from the original ref doc
 			# or get it based on the posting date of the ref doc.
-			exchange_rate = ref_doc.get("conversion_rate") or \
-				get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
+			exchange_rate = ref_doc.get("conversion_rate") or get_exchange_rate(
+				party_account_currency, company_currency, ref_doc.posting_date
+			)
 		if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
 			outstanding_amount = ref_doc.get("outstanding_amount")
 			bill_no = ref_doc.get("bill_no")
 		elif reference_doctype == "Expense Claim":
-			outstanding_amount = flt(ref_doc.get("total_sanctioned_amount")) + flt(ref_doc.get("total_taxes_and_charges"))\
-				- flt(ref_doc.get("total_amount_reimbursed")) - flt(ref_doc.get("total_advance_amount"))
+			outstanding_amount = (
+				flt(ref_doc.get("total_sanctioned_amount"))
+				+ flt(ref_doc.get("total_taxes_and_charges"))
+				- flt(ref_doc.get("total_amount_reimbursed"))
+				- flt(ref_doc.get("total_advance_amount"))
+			)
 		elif reference_doctype == "Employee Advance":
-			outstanding_amount = (flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount))
+			outstanding_amount = flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount)
 			if party_account_currency != ref_doc.currency:
 				outstanding_amount = flt(outstanding_amount) * flt(exchange_rate)
 				if party_account_currency == company_currency:
@@ -1380,18 +1654,22 @@
 			outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid)
 	else:
 		# Get the exchange rate based on the posting date of the ref doc.
-		exchange_rate = get_exchange_rate(party_account_currency,
-			company_currency, ref_doc.posting_date)
+		exchange_rate = get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
 
-	return frappe._dict({
-		"due_date": ref_doc.get("due_date"),
-		"total_amount": flt(total_amount),
-		"outstanding_amount": flt(outstanding_amount),
-		"exchange_rate": flt(exchange_rate),
-		"bill_no": bill_no
-	})
+	return frappe._dict(
+		{
+			"due_date": ref_doc.get("due_date"),
+			"total_amount": flt(total_amount),
+			"outstanding_amount": flt(outstanding_amount),
+			"exchange_rate": flt(exchange_rate),
+			"bill_no": bill_no,
+		}
+	)
 
-def get_amounts_based_on_reference_doctype(reference_doctype, ref_doc, party_account_currency, company_currency, reference_name):
+
+def get_amounts_based_on_reference_doctype(
+	reference_doctype, ref_doc, party_account_currency, company_currency, reference_name
+):
 	total_amount = outstanding_amount = exchange_rate = None
 	if reference_doctype == "Fees":
 		total_amount = ref_doc.get("grand_total")
@@ -1404,35 +1682,46 @@
 	elif reference_doctype == "Journal Entry" and ref_doc.docstatus == 1:
 		total_amount = ref_doc.get("total_amount")
 		if ref_doc.multi_currency:
-			exchange_rate = get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
+			exchange_rate = get_exchange_rate(
+				party_account_currency, company_currency, ref_doc.posting_date
+			)
 		else:
 			exchange_rate = 1
 			outstanding_amount = get_outstanding_on_journal_entry(reference_name)
 
 	return total_amount, outstanding_amount, exchange_rate
 
-def get_amounts_based_on_ref_doc(reference_doctype, ref_doc, party_account_currency, company_currency):
+
+def get_amounts_based_on_ref_doc(
+	reference_doctype, ref_doc, party_account_currency, company_currency
+):
 	total_amount = outstanding_amount = exchange_rate = None
 	if ref_doc.doctype == "Expense Claim":
-			total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
+		total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
 	elif ref_doc.doctype == "Employee Advance":
-		total_amount, exchange_rate = get_total_amount_exchange_rate_for_employee_advance(party_account_currency, ref_doc)
+		total_amount, exchange_rate = get_total_amount_exchange_rate_for_employee_advance(
+			party_account_currency, ref_doc
+		)
 
 	if not total_amount:
 		total_amount, exchange_rate = get_total_amount_exchange_rate_base_on_currency(
-			party_account_currency, company_currency, ref_doc)
+			party_account_currency, company_currency, ref_doc
+		)
 
 	if not exchange_rate:
 		# Get the exchange rate from the original ref doc
 		# or get it based on the posting date of the ref doc
-		exchange_rate = ref_doc.get("conversion_rate") or \
-			get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
+		exchange_rate = ref_doc.get("conversion_rate") or get_exchange_rate(
+			party_account_currency, company_currency, ref_doc.posting_date
+		)
 
 	outstanding_amount, exchange_rate, bill_no = get_bill_no_and_update_amounts(
-		reference_doctype, ref_doc, total_amount, exchange_rate, party_account_currency, company_currency)
+		reference_doctype, ref_doc, total_amount, exchange_rate, party_account_currency, company_currency
+	)
 
 	return total_amount, outstanding_amount, exchange_rate, bill_no
 
+
 def get_total_amount_exchange_rate_for_employee_advance(party_account_currency, ref_doc):
 	total_amount = ref_doc.advance_amount
 	exchange_rate = ref_doc.get("exchange_rate")
@@ -1441,7 +1730,10 @@
 
 	return total_amount, exchange_rate
 
-def get_total_amount_exchange_rate_base_on_currency(party_account_currency, company_currency, ref_doc):
+
+def get_total_amount_exchange_rate_base_on_currency(
+	party_account_currency, company_currency, ref_doc
+):
 	exchange_rate = None
 	if party_account_currency == company_currency:
 		total_amount = ref_doc.base_grand_total
@@ -1451,16 +1743,23 @@
 
 	return total_amount, exchange_rate
 
-def get_bill_no_and_update_amounts(reference_doctype, ref_doc, total_amount, exchange_rate, party_account_currency, company_currency):
+
+def get_bill_no_and_update_amounts(
+	reference_doctype, ref_doc, total_amount, exchange_rate, party_account_currency, company_currency
+):
 	outstanding_amount = bill_no = None
 	if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
 		outstanding_amount = ref_doc.get("outstanding_amount")
 		bill_no = ref_doc.get("bill_no")
 	elif reference_doctype == "Expense Claim":
-		outstanding_amount = flt(ref_doc.get("total_sanctioned_amount")) + flt(ref_doc.get("total_taxes_and_charges"))\
-			- flt(ref_doc.get("total_amount_reimbursed")) - flt(ref_doc.get("total_advance_amount"))
+		outstanding_amount = (
+			flt(ref_doc.get("total_sanctioned_amount"))
+			+ flt(ref_doc.get("total_taxes_and_charges"))
+			- flt(ref_doc.get("total_amount_reimbursed"))
+			- flt(ref_doc.get("total_advance_amount"))
+		)
 	elif reference_doctype == "Employee Advance":
-		outstanding_amount = (flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount))
+		outstanding_amount = flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount)
 		if party_account_currency != ref_doc.currency:
 			outstanding_amount = flt(outstanding_amount) * flt(exchange_rate)
 			if party_account_currency == company_currency:
@@ -1482,15 +1781,20 @@
 	party_account = set_party_account(dt, dn, doc, party_type)
 	party_account_currency = set_party_account_currency(dt, party_account, doc)
 	payment_type = set_payment_type(dt, doc)
-	grand_total, outstanding_amount = set_grand_total_and_outstanding_amount(party_amount, dt, party_account_currency, doc)
+	grand_total, outstanding_amount = set_grand_total_and_outstanding_amount(
+		party_amount, dt, party_account_currency, doc
+	)
 
 	# bank or cash
 	bank = get_bank_cash_account(doc, bank_account)
 
 	paid_amount, received_amount = set_paid_amount_and_received_amount(
-		dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc)
+		dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc
+	)
 
-	paid_amount, received_amount, discount_amount = apply_early_payment_discount(paid_amount, received_amount, doc)
+	paid_amount, received_amount, discount_amount = apply_early_payment_discount(
+		paid_amount, received_amount, doc
+	)
 
 	pe = frappe.new_doc("Payment Entry")
 	pe.payment_type = payment_type
@@ -1504,18 +1808,22 @@
 	pe.contact_email = doc.get("contact_email")
 	pe.ensure_supplier_is_not_blocked()
 
-	pe.paid_from = party_account if payment_type=="Receive" else bank.account
-	pe.paid_to = party_account if payment_type=="Pay" else bank.account
-	pe.paid_from_account_currency = party_account_currency \
-		if payment_type=="Receive" else bank.account_currency
-	pe.paid_to_account_currency = party_account_currency if payment_type=="Pay" else bank.account_currency
+	pe.paid_from = party_account if payment_type == "Receive" else bank.account
+	pe.paid_to = party_account if payment_type == "Pay" else bank.account
+	pe.paid_from_account_currency = (
+		party_account_currency if payment_type == "Receive" else bank.account_currency
+	)
+	pe.paid_to_account_currency = (
+		party_account_currency if payment_type == "Pay" else bank.account_currency
+	)
 	pe.paid_amount = paid_amount
 	pe.received_amount = received_amount
 	pe.letter_head = doc.get("letter_head")
 
-	if dt in ['Purchase Order', 'Sales Order', 'Sales Invoice', 'Purchase Invoice']:
-		pe.project = (doc.get('project') or
-			reduce(lambda prev,cur: prev or cur, [x.get('project') for x in doc.get('items')], None)) # get first non-empty project from items
+	if dt in ["Purchase Order", "Sales Order", "Sales Invoice", "Purchase Invoice"]:
+		pe.project = doc.get("project") or reduce(
+			lambda prev, cur: prev or cur, [x.get("project") for x in doc.get("items")], None
+		)  # get first non-empty project from items
 
 	if pe.party_type in ["Customer", "Supplier"]:
 		bank_account = get_party_bank_account(pe.party_type, pe.party)
@@ -1524,44 +1832,57 @@
 
 	# only Purchase Invoice can be blocked individually
 	if doc.doctype == "Purchase Invoice" and doc.invoice_is_blocked():
-		frappe.msgprint(_('{0} is on hold till {1}').format(doc.name, doc.release_date))
+		frappe.msgprint(_("{0} is on hold till {1}").format(doc.name, doc.release_date))
 	else:
-		if (doc.doctype in ('Sales Invoice', 'Purchase Invoice')
-			and frappe.get_value('Payment Terms Template',
-			{'name': doc.payment_terms_template}, 'allocate_payment_based_on_payment_terms')):
+		if doc.doctype in ("Sales Invoice", "Purchase Invoice") and frappe.get_value(
+			"Payment Terms Template",
+			{"name": doc.payment_terms_template},
+			"allocate_payment_based_on_payment_terms",
+		):
 
-			for reference in get_reference_as_per_payment_terms(doc.payment_schedule, dt, dn, doc, grand_total, outstanding_amount):
-				pe.append('references', reference)
+			for reference in get_reference_as_per_payment_terms(
+				doc.payment_schedule, dt, dn, doc, grand_total, outstanding_amount
+			):
+				pe.append("references", reference)
 		else:
 			if dt == "Dunning":
-				pe.append("references", {
-					'reference_doctype': 'Sales Invoice',
-					'reference_name': doc.get('sales_invoice'),
-					"bill_no": doc.get("bill_no"),
-					"due_date": doc.get("due_date"),
-					'total_amount': doc.get('outstanding_amount'),
-					'outstanding_amount': doc.get('outstanding_amount'),
-					'allocated_amount': doc.get('outstanding_amount')
-				})
-				pe.append("references", {
-					'reference_doctype': dt,
-					'reference_name': dn,
-					"bill_no": doc.get("bill_no"),
-					"due_date": doc.get("due_date"),
-					'total_amount': doc.get('dunning_amount'),
-					'outstanding_amount': doc.get('dunning_amount'),
-					'allocated_amount': doc.get('dunning_amount')
-				})
+				pe.append(
+					"references",
+					{
+						"reference_doctype": "Sales Invoice",
+						"reference_name": doc.get("sales_invoice"),
+						"bill_no": doc.get("bill_no"),
+						"due_date": doc.get("due_date"),
+						"total_amount": doc.get("outstanding_amount"),
+						"outstanding_amount": doc.get("outstanding_amount"),
+						"allocated_amount": doc.get("outstanding_amount"),
+					},
+				)
+				pe.append(
+					"references",
+					{
+						"reference_doctype": dt,
+						"reference_name": dn,
+						"bill_no": doc.get("bill_no"),
+						"due_date": doc.get("due_date"),
+						"total_amount": doc.get("dunning_amount"),
+						"outstanding_amount": doc.get("dunning_amount"),
+						"allocated_amount": doc.get("dunning_amount"),
+					},
+				)
 			else:
-				pe.append("references", {
-					'reference_doctype': dt,
-					'reference_name': dn,
-					"bill_no": doc.get("bill_no"),
-					"due_date": doc.get("due_date"),
-					'total_amount': grand_total,
-					'outstanding_amount': outstanding_amount,
-					'allocated_amount': outstanding_amount
-				})
+				pe.append(
+					"references",
+					{
+						"reference_doctype": dt,
+						"reference_name": dn,
+						"bill_no": doc.get("bill_no"),
+						"due_date": doc.get("due_date"),
+						"total_amount": grand_total,
+						"outstanding_amount": outstanding_amount,
+						"allocated_amount": outstanding_amount,
+					},
+				)
 
 	pe.setup_party_account_field()
 	pe.set_missing_values()
@@ -1572,25 +1893,32 @@
 		pe.set_exchange_rate(ref_doc=reference_doc)
 		pe.set_amounts()
 		if discount_amount:
-			pe.set_gain_or_loss(account_details={
-				'account': frappe.get_cached_value('Company', pe.company, "default_discount_account"),
-				'cost_center': pe.cost_center or frappe.get_cached_value('Company', pe.company, "cost_center"),
-				'amount': discount_amount * (-1 if payment_type == "Pay" else 1)
-			})
+			pe.set_gain_or_loss(
+				account_details={
+					"account": frappe.get_cached_value("Company", pe.company, "default_discount_account"),
+					"cost_center": pe.cost_center
+					or frappe.get_cached_value("Company", pe.company, "cost_center"),
+					"amount": discount_amount * (-1 if payment_type == "Pay" else 1),
+				}
+			)
 			pe.set_difference_amount()
 
 	return pe
 
+
 def get_bank_cash_account(doc, bank_account):
-	bank = get_default_bank_cash_account(doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"),
-		account=bank_account)
+	bank = get_default_bank_cash_account(
+		doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"), account=bank_account
+	)
 
 	if not bank:
-		bank = get_default_bank_cash_account(doc.company, "Cash", mode_of_payment=doc.get("mode_of_payment"),
-			account=bank_account)
+		bank = get_default_bank_cash_account(
+			doc.company, "Cash", mode_of_payment=doc.get("mode_of_payment"), account=bank_account
+		)
 
 	return bank
 
+
 def set_party_type(dt):
 	if dt in ("Sales Invoice", "Sales Order", "Dunning"):
 		party_type = "Customer"
@@ -1598,10 +1926,9 @@
 		party_type = "Supplier"
 	elif dt in ("Expense Claim", "Employee Advance", "Gratuity"):
 		party_type = "Employee"
-	elif dt == "Fees":
-		party_type = "Student"
 	return party_type
 
+
 def set_party_account(dt, dn, doc, party_type):
 	if dt == "Sales Invoice":
 		party_account = get_party_account_based_on_invoice_discounting(dn) or doc.debit_to
@@ -1619,6 +1946,7 @@
 		party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
 	return party_account
 
+
 def set_party_account_currency(dt, party_account, doc):
 	if dt not in ("Sales Invoice", "Purchase Invoice"):
 		party_account_currency = get_account_currency(party_account)
@@ -1626,14 +1954,18 @@
 		party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account)
 	return party_account_currency
 
+
 def set_payment_type(dt, doc):
-	if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees", "Dunning") and doc.outstanding_amount > 0)) \
-		or (dt=="Purchase Invoice" and doc.outstanding_amount < 0):
-			payment_type = "Receive"
+	if (
+		dt == "Sales Order"
+		or (dt in ("Sales Invoice", "Fees", "Dunning") and doc.outstanding_amount > 0)
+	) or (dt == "Purchase Invoice" and doc.outstanding_amount < 0):
+		payment_type = "Receive"
 	else:
 		payment_type = "Pay"
 	return payment_type
 
+
 def set_grand_total_and_outstanding_amount(party_amount, dt, party_account_currency, doc):
 	grand_total = outstanding_amount = 0
 	if party_amount:
@@ -1646,8 +1978,7 @@
 		outstanding_amount = doc.outstanding_amount
 	elif dt in ("Expense Claim"):
 		grand_total = doc.total_sanctioned_amount + doc.total_taxes_and_charges
-		outstanding_amount = doc.grand_total \
-			- doc.total_amount_reimbursed
+		outstanding_amount = doc.grand_total - doc.total_amount_reimbursed
 	elif dt == "Employee Advance":
 		grand_total = flt(doc.advance_amount)
 		outstanding_amount = flt(doc.advance_amount) - flt(doc.paid_amount)
@@ -1671,7 +2002,10 @@
 		outstanding_amount = grand_total - flt(doc.advance_paid)
 	return grand_total, outstanding_amount
 
-def set_paid_amount_and_received_amount(dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc):
+
+def set_paid_amount_and_received_amount(
+	dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc
+):
 	paid_amount = received_amount = 0
 	if party_account_currency == bank.account_currency:
 		paid_amount = received_amount = abs(outstanding_amount)
@@ -1680,37 +2014,38 @@
 		if bank_amount:
 			received_amount = bank_amount
 		else:
-			received_amount = paid_amount * doc.get('conversion_rate', 1)
+			received_amount = paid_amount * doc.get("conversion_rate", 1)
 			if dt == "Employee Advance":
-				received_amount = paid_amount * doc.get('exchange_rate', 1)
+				received_amount = paid_amount * doc.get("exchange_rate", 1)
 	else:
 		received_amount = abs(outstanding_amount)
 		if bank_amount:
 			paid_amount = bank_amount
 		else:
 			# if party account currency and bank currency is different then populate paid amount as well
-			paid_amount = received_amount * doc.get('conversion_rate', 1)
+			paid_amount = received_amount * doc.get("conversion_rate", 1)
 			if dt == "Employee Advance":
-				paid_amount = received_amount * doc.get('exchange_rate', 1)
+				paid_amount = received_amount * doc.get("exchange_rate", 1)
 
 	return paid_amount, received_amount
 
+
 def apply_early_payment_discount(paid_amount, received_amount, doc):
 	total_discount = 0
-	eligible_for_payments = ['Sales Order', 'Sales Invoice', 'Purchase Order', 'Purchase Invoice']
-	has_payment_schedule = hasattr(doc, 'payment_schedule') and doc.payment_schedule
+	eligible_for_payments = ["Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"]
+	has_payment_schedule = hasattr(doc, "payment_schedule") and doc.payment_schedule
 
 	if doc.doctype in eligible_for_payments and has_payment_schedule:
 		for term in doc.payment_schedule:
 			if not term.discounted_amount and term.discount and getdate(nowdate()) <= term.discount_date:
-				if term.discount_type == 'Percentage':
-					discount_amount = flt(doc.get('grand_total')) * (term.discount / 100)
+				if term.discount_type == "Percentage":
+					discount_amount = flt(doc.get("grand_total")) * (term.discount / 100)
 				else:
 					discount_amount = term.discount
 
-				discount_amount_in_foreign_currency = discount_amount * doc.get('conversion_rate', 1)
+				discount_amount_in_foreign_currency = discount_amount * doc.get("conversion_rate", 1)
 
-				if doc.doctype == 'Sales Invoice':
+				if doc.doctype == "Sales Invoice":
 					paid_amount -= discount_amount
 					received_amount -= discount_amount_in_foreign_currency
 				else:
@@ -1720,38 +2055,46 @@
 				total_discount += discount_amount
 
 		if total_discount:
-			money = frappe.utils.fmt_money(total_discount, currency=doc.get('currency'))
+			money = frappe.utils.fmt_money(total_discount, currency=doc.get("currency"))
 			frappe.msgprint(_("Discount of {} applied as per Payment Term").format(money), alert=1)
 
 	return paid_amount, received_amount, total_discount
 
-def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount):
+
+def get_reference_as_per_payment_terms(
+	payment_schedule, dt, dn, doc, grand_total, outstanding_amount
+):
 	references = []
 	for payment_term in payment_schedule:
-		payment_term_outstanding = flt(payment_term.payment_amount - payment_term.paid_amount,
-				payment_term.precision('payment_amount'))
+		payment_term_outstanding = flt(
+			payment_term.payment_amount - payment_term.paid_amount, payment_term.precision("payment_amount")
+		)
 
 		if payment_term_outstanding:
-			references.append({
-				'reference_doctype': dt,
-				'reference_name': dn,
-				'bill_no': doc.get('bill_no'),
-				'due_date': doc.get('due_date'),
-				'total_amount': grand_total,
-				'outstanding_amount': outstanding_amount,
-				'payment_term': payment_term.payment_term,
-				'allocated_amount': payment_term_outstanding
-			})
+			references.append(
+				{
+					"reference_doctype": dt,
+					"reference_name": dn,
+					"bill_no": doc.get("bill_no"),
+					"due_date": doc.get("due_date"),
+					"total_amount": grand_total,
+					"outstanding_amount": outstanding_amount,
+					"payment_term": payment_term.payment_term,
+					"allocated_amount": payment_term_outstanding,
+				}
+			)
 
 	return references
 
+
 def get_paid_amount(dt, dn, party_type, party, account, due_date):
-	if party_type=="Customer":
+	if party_type == "Customer":
 		dr_or_cr = "credit_in_account_currency - debit_in_account_currency"
 	else:
 		dr_or_cr = "debit_in_account_currency - credit_in_account_currency"
 
-	paid_amount = frappe.db.sql("""
+	paid_amount = frappe.db.sql(
+		"""
 		select ifnull(sum({dr_or_cr}), 0) as paid_amount
 		from `tabGL Entry`
 		where against_voucher_type = %s
@@ -1761,41 +2104,58 @@
 			and account = %s
 			and due_date = %s
 			and {dr_or_cr} > 0
-	""".format(dr_or_cr=dr_or_cr), (dt, dn, party_type, party, account, due_date))
+	""".format(
+			dr_or_cr=dr_or_cr
+		),
+		(dt, dn, party_type, party, account, due_date),
+	)
 
 	return paid_amount[0][0] if paid_amount else 0
 
+
 @frappe.whitelist()
-def get_party_and_account_balance(company, date, paid_from=None, paid_to=None, ptype=None, pty=None, cost_center=None):
-	return frappe._dict({
-		"party_balance": get_balance_on(party_type=ptype, party=pty, cost_center=cost_center),
-		"paid_from_account_balance": get_balance_on(paid_from, date, cost_center=cost_center),
-		"paid_to_account_balance": get_balance_on(paid_to, date=date, cost_center=cost_center)
-	})
+def get_party_and_account_balance(
+	company, date, paid_from=None, paid_to=None, ptype=None, pty=None, cost_center=None
+):
+	return frappe._dict(
+		{
+			"party_balance": get_balance_on(party_type=ptype, party=pty, cost_center=cost_center),
+			"paid_from_account_balance": get_balance_on(paid_from, date, cost_center=cost_center),
+			"paid_to_account_balance": get_balance_on(paid_to, date=date, cost_center=cost_center),
+		}
+	)
+
 
 @frappe.whitelist()
 def make_payment_order(source_name, target_doc=None):
 	from frappe.model.mapper import get_mapped_doc
+
 	def set_missing_values(source, target):
 		target.payment_order_type = "Payment Entry"
-		target.append('references', dict(
-			reference_doctype="Payment Entry",
-			reference_name=source.name,
-			bank_account=source.party_bank_account,
-			amount=source.paid_amount,
-			account=source.paid_to,
-			supplier=source.party,
-			mode_of_payment=source.mode_of_payment,
-		))
+		target.append(
+			"references",
+			dict(
+				reference_doctype="Payment Entry",
+				reference_name=source.name,
+				bank_account=source.party_bank_account,
+				amount=source.paid_amount,
+				account=source.paid_to,
+				supplier=source.party,
+				mode_of_payment=source.mode_of_payment,
+			),
+		)
 
-	doclist = get_mapped_doc("Payment Entry", source_name, {
-		"Payment Entry": {
-			"doctype": "Payment Order",
-			"validation": {
-				"docstatus": ["=", 1]
-			},
-		}
-
-	}, target_doc, set_missing_values)
+	doclist = get_mapped_doc(
+		"Payment Entry",
+		source_name,
+		{
+			"Payment Entry": {
+				"doctype": "Payment Order",
+				"validation": {"docstatus": ["=", 1]},
+			}
+		},
+		target_doc,
+		set_missing_values,
+	)
 
 	return doclist
diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
index 349b8bb..9aa1a18 100644
--- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
@@ -4,6 +4,7 @@
 import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import flt, nowdate
 
 from erpnext.accounts.doctype.payment_entry.payment_entry import (
@@ -24,7 +25,10 @@
 test_dependencies = ["Item"]
 
 
-class TestPaymentEntry(unittest.TestCase):
+class TestPaymentEntry(FrappeTestCase):
+	def tearDown(self):
+		frappe.db.rollback()
+
 	def test_payment_entry_against_order(self):
 		so = make_sales_order()
 		pe = get_payment_entry("Sales Order", so.name, bank_account="_Test Cash - _TC")
@@ -32,10 +36,9 @@
 		pe.insert()
 		pe.submit()
 
-		expected_gle = dict((d[0], d) for d in [
-			["Debtors - _TC", 0, 1000, so.name],
-			["_Test Cash - _TC", 1000.0, 0, None]
-		])
+		expected_gle = dict(
+			(d[0], d) for d in [["Debtors - _TC", 0, 1000, so.name], ["_Test Cash - _TC", 1000.0, 0, None]]
+		)
 
 		self.validate_gl_entries(pe.name, expected_gle)
 
@@ -48,9 +51,9 @@
 		self.assertEqual(so_advance_paid, 0)
 
 	def test_payment_entry_for_blocked_supplier_invoice(self):
-		supplier = frappe.get_doc('Supplier', '_Test Supplier')
+		supplier = frappe.get_doc("Supplier", "_Test Supplier")
 		supplier.on_hold = 1
-		supplier.hold_type = 'Invoices'
+		supplier.hold_type = "Invoices"
 		supplier.save()
 
 		self.assertRaises(frappe.ValidationError, make_purchase_invoice)
@@ -59,32 +62,40 @@
 		supplier.save()
 
 	def test_payment_entry_for_blocked_supplier_payments(self):
-		supplier = frappe.get_doc('Supplier', '_Test Supplier')
+		supplier = frappe.get_doc("Supplier", "_Test Supplier")
 		supplier.on_hold = 1
-		supplier.hold_type = 'Payments'
+		supplier.hold_type = "Payments"
 		supplier.save()
 
 		pi = make_purchase_invoice()
 
 		self.assertRaises(
-			frappe.ValidationError, get_payment_entry, dt='Purchase Invoice', dn=pi.name,
-			bank_account="_Test Bank - _TC")
+			frappe.ValidationError,
+			get_payment_entry,
+			dt="Purchase Invoice",
+			dn=pi.name,
+			bank_account="_Test Bank - _TC",
+		)
 
 		supplier.on_hold = 0
 		supplier.save()
 
 	def test_payment_entry_for_blocked_supplier_payments_today_date(self):
-		supplier = frappe.get_doc('Supplier', '_Test Supplier')
+		supplier = frappe.get_doc("Supplier", "_Test Supplier")
 		supplier.on_hold = 1
-		supplier.hold_type = 'Payments'
+		supplier.hold_type = "Payments"
 		supplier.release_date = nowdate()
 		supplier.save()
 
 		pi = make_purchase_invoice()
 
 		self.assertRaises(
-			frappe.ValidationError, get_payment_entry, dt='Purchase Invoice', dn=pi.name,
-			bank_account="_Test Bank - _TC")
+			frappe.ValidationError,
+			get_payment_entry,
+			dt="Purchase Invoice",
+			dn=pi.name,
+			bank_account="_Test Bank - _TC",
+		)
 
 		supplier.on_hold = 0
 		supplier.save()
@@ -93,15 +104,15 @@
 		# this test is meant to fail only if something fails in the try block
 		with self.assertRaises(Exception):
 			try:
-				supplier = frappe.get_doc('Supplier', '_Test Supplier')
+				supplier = frappe.get_doc("Supplier", "_Test Supplier")
 				supplier.on_hold = 1
-				supplier.hold_type = 'Payments'
-				supplier.release_date = '2018-03-01'
+				supplier.hold_type = "Payments"
+				supplier.release_date = "2018-03-01"
 				supplier.save()
 
 				pi = make_purchase_invoice()
 
-				get_payment_entry('Purchase Invoice', pi.name, bank_account="_Test Bank - _TC")
+				get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
 
 				supplier.on_hold = 0
 				supplier.save()
@@ -111,8 +122,12 @@
 				raise Exception
 
 	def test_payment_entry_against_si_usd_to_usd(self):
-		si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
-			currency="USD", conversion_rate=50)
+		si = create_sales_invoice(
+			customer="_Test Customer USD",
+			debit_to="_Test Receivable USD - _TC",
+			currency="USD",
+			conversion_rate=50,
+		)
 		pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
 		pe.reference_no = "1"
 		pe.reference_date = "2016-01-01"
@@ -120,10 +135,13 @@
 		pe.insert()
 		pe.submit()
 
-		expected_gle = dict((d[0], d) for d in [
-			["_Test Receivable USD - _TC", 0, 5000, si.name],
-			["_Test Bank USD - _TC", 5000.0, 0, None]
-		])
+		expected_gle = dict(
+			(d[0], d)
+			for d in [
+				["_Test Receivable USD - _TC", 0, 5000, si.name],
+				["_Test Bank USD - _TC", 5000.0, 0, None],
+			]
+		)
 
 		self.validate_gl_entries(pe.name, expected_gle)
 
@@ -136,8 +154,12 @@
 		self.assertEqual(outstanding_amount, 100)
 
 	def test_payment_entry_against_pi(self):
-		pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC",
-			currency="USD", conversion_rate=50)
+		pi = make_purchase_invoice(
+			supplier="_Test Supplier USD",
+			debit_to="_Test Payable USD - _TC",
+			currency="USD",
+			conversion_rate=50,
+		)
 		pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank USD - _TC")
 		pe.reference_no = "1"
 		pe.reference_date = "2016-01-01"
@@ -145,20 +167,26 @@
 		pe.insert()
 		pe.submit()
 
-		expected_gle = dict((d[0], d) for d in [
-			["_Test Payable USD - _TC", 12500, 0, pi.name],
-			["_Test Bank USD - _TC", 0, 12500, None]
-		])
+		expected_gle = dict(
+			(d[0], d)
+			for d in [
+				["_Test Payable USD - _TC", 12500, 0, pi.name],
+				["_Test Bank USD - _TC", 0, 12500, None],
+			]
+		)
 
 		self.validate_gl_entries(pe.name, expected_gle)
 
 		outstanding_amount = flt(frappe.db.get_value("Sales Invoice", pi.name, "outstanding_amount"))
 		self.assertEqual(outstanding_amount, 0)
 
-
 	def test_payment_against_sales_invoice_to_check_status(self):
-		si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
-			currency="USD", conversion_rate=50)
+		si = create_sales_invoice(
+			customer="_Test Customer USD",
+			debit_to="_Test Receivable USD - _TC",
+			currency="USD",
+			conversion_rate=50,
+		)
 
 		pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
 		pe.reference_no = "1"
@@ -167,28 +195,35 @@
 		pe.insert()
 		pe.submit()
 
-		outstanding_amount, status = frappe.db.get_value("Sales Invoice", si.name, ["outstanding_amount", "status"])
+		outstanding_amount, status = frappe.db.get_value(
+			"Sales Invoice", si.name, ["outstanding_amount", "status"]
+		)
 		self.assertEqual(flt(outstanding_amount), 0)
-		self.assertEqual(status, 'Paid')
+		self.assertEqual(status, "Paid")
 
 		pe.cancel()
 
-		outstanding_amount, status = frappe.db.get_value("Sales Invoice", si.name, ["outstanding_amount", "status"])
+		outstanding_amount, status = frappe.db.get_value(
+			"Sales Invoice", si.name, ["outstanding_amount", "status"]
+		)
 		self.assertEqual(flt(outstanding_amount), 100)
-		self.assertEqual(status, 'Unpaid')
+		self.assertEqual(status, "Unpaid")
 
 	def test_payment_entry_against_payment_terms(self):
 		si = create_sales_invoice(do_not_save=1, qty=1, rate=200)
 		create_payment_terms_template()
-		si.payment_terms_template = 'Test Receivable Template'
+		si.payment_terms_template = "Test Receivable Template"
 
-		si.append('taxes', {
-			"charge_type": "On Net Total",
-			"account_head": "_Test Account Service Tax - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "Service Tax",
-			"rate": 18
-		})
+		si.append(
+			"taxes",
+			{
+				"charge_type": "On Net Total",
+				"account_head": "_Test Account Service Tax - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "Service Tax",
+				"rate": 18,
+			},
+		)
 		si.save()
 
 		si.submit()
@@ -197,25 +232,28 @@
 		pe.submit()
 		si.load_from_db()
 
-		self.assertEqual(pe.references[0].payment_term, 'Basic Amount Receivable')
-		self.assertEqual(pe.references[1].payment_term, 'Tax Receivable')
+		self.assertEqual(pe.references[0].payment_term, "Basic Amount Receivable")
+		self.assertEqual(pe.references[1].payment_term, "Tax Receivable")
 		self.assertEqual(si.payment_schedule[0].paid_amount, 200.0)
 		self.assertEqual(si.payment_schedule[1].paid_amount, 36.0)
 
 	def test_payment_entry_against_payment_terms_with_discount(self):
 		si = create_sales_invoice(do_not_save=1, qty=1, rate=200)
 		create_payment_terms_template_with_discount()
-		si.payment_terms_template = 'Test Discount Template'
+		si.payment_terms_template = "Test Discount Template"
 
-		frappe.db.set_value('Company', si.company, 'default_discount_account', 'Write Off - _TC')
+		frappe.db.set_value("Company", si.company, "default_discount_account", "Write Off - _TC")
 
-		si.append('taxes', {
-			"charge_type": "On Net Total",
-			"account_head": "_Test Account Service Tax - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "Service Tax",
-			"rate": 18
-		})
+		si.append(
+			"taxes",
+			{
+				"charge_type": "On Net Total",
+				"account_head": "_Test Account Service Tax - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "Service Tax",
+				"rate": 18,
+			},
+		)
 		si.save()
 
 		si.submit()
@@ -224,16 +262,19 @@
 		pe.submit()
 		si.load_from_db()
 
-		self.assertEqual(pe.references[0].payment_term, '30 Credit Days with 10% Discount')
+		self.assertEqual(pe.references[0].payment_term, "30 Credit Days with 10% Discount")
 		self.assertEqual(si.payment_schedule[0].payment_amount, 236.0)
 		self.assertEqual(si.payment_schedule[0].paid_amount, 212.40)
 		self.assertEqual(si.payment_schedule[0].outstanding, 0)
 		self.assertEqual(si.payment_schedule[0].discounted_amount, 23.6)
 
-
 	def test_payment_against_purchase_invoice_to_check_status(self):
-		pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC",
-			currency="USD", conversion_rate=50)
+		pi = make_purchase_invoice(
+			supplier="_Test Supplier USD",
+			debit_to="_Test Payable USD - _TC",
+			currency="USD",
+			conversion_rate=50,
+		)
 
 		pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank USD - _TC")
 		pe.reference_no = "1"
@@ -242,21 +283,27 @@
 		pe.insert()
 		pe.submit()
 
-		outstanding_amount, status = frappe.db.get_value("Purchase Invoice", pi.name, ["outstanding_amount", "status"])
+		outstanding_amount, status = frappe.db.get_value(
+			"Purchase Invoice", pi.name, ["outstanding_amount", "status"]
+		)
 		self.assertEqual(flt(outstanding_amount), 0)
-		self.assertEqual(status, 'Paid')
+		self.assertEqual(status, "Paid")
 
 		pe.cancel()
 
-		outstanding_amount, status = frappe.db.get_value("Purchase Invoice", pi.name, ["outstanding_amount", "status"])
+		outstanding_amount, status = frappe.db.get_value(
+			"Purchase Invoice", pi.name, ["outstanding_amount", "status"]
+		)
 		self.assertEqual(flt(outstanding_amount), 250)
-		self.assertEqual(status, 'Unpaid')
+		self.assertEqual(status, "Unpaid")
 
 	def test_payment_entry_against_ec(self):
 
-		payable = frappe.get_cached_value('Company',  "_Test Company",  'default_payable_account')
+		payable = frappe.get_cached_value("Company", "_Test Company", "default_payable_account")
 		ec = make_expense_claim(payable, 300, 300, "_Test Company", "Travel Expenses - _TC")
-		pe = get_payment_entry("Expense Claim", ec.name, bank_account="_Test Bank USD - _TC", bank_amount=300)
+		pe = get_payment_entry(
+			"Expense Claim", ec.name, bank_account="_Test Bank USD - _TC", bank_amount=300
+		)
 		pe.reference_no = "1"
 		pe.reference_date = "2016-01-01"
 		pe.source_exchange_rate = 1
@@ -264,68 +311,87 @@
 		pe.insert()
 		pe.submit()
 
-		expected_gle = dict((d[0], d) for d in [
-			[payable, 300, 0, ec.name],
-			["_Test Bank USD - _TC", 0, 300, None]
-		])
+		expected_gle = dict(
+			(d[0], d) for d in [[payable, 300, 0, ec.name], ["_Test Bank USD - _TC", 0, 300, None]]
+		)
 
 		self.validate_gl_entries(pe.name, expected_gle)
 
-		outstanding_amount = flt(frappe.db.get_value("Expense Claim", ec.name, "total_sanctioned_amount")) - \
-			flt(frappe.db.get_value("Expense Claim", ec.name, "total_amount_reimbursed"))
+		outstanding_amount = flt(
+			frappe.db.get_value("Expense Claim", ec.name, "total_sanctioned_amount")
+		) - flt(frappe.db.get_value("Expense Claim", ec.name, "total_amount_reimbursed"))
 		self.assertEqual(outstanding_amount, 0)
 
 	def test_payment_entry_against_si_usd_to_inr(self):
-		si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
-			currency="USD", conversion_rate=50)
-		pe = get_payment_entry("Sales Invoice", si.name, party_amount=20,
-			bank_account="_Test Bank - _TC", bank_amount=900)
+		si = create_sales_invoice(
+			customer="_Test Customer USD",
+			debit_to="_Test Receivable USD - _TC",
+			currency="USD",
+			conversion_rate=50,
+		)
+		pe = get_payment_entry(
+			"Sales Invoice", si.name, party_amount=20, bank_account="_Test Bank - _TC", bank_amount=900
+		)
 		pe.reference_no = "1"
 		pe.reference_date = "2016-01-01"
 
 		self.assertEqual(pe.difference_amount, 100)
 
-		pe.append("deductions", {
-			"account": "_Test Exchange Gain/Loss - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-			"amount": 100
-		})
+		pe.append(
+			"deductions",
+			{
+				"account": "_Test Exchange Gain/Loss - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+				"amount": 100,
+			},
+		)
 		pe.insert()
 		pe.submit()
 
-		expected_gle = dict((d[0], d) for d in [
-			["_Test Receivable USD - _TC", 0, 1000, si.name],
-			["_Test Bank - _TC", 900, 0, None],
-			["_Test Exchange Gain/Loss - _TC", 100.0, 0, None],
-		])
+		expected_gle = dict(
+			(d[0], d)
+			for d in [
+				["_Test Receivable USD - _TC", 0, 1000, si.name],
+				["_Test Bank - _TC", 900, 0, None],
+				["_Test Exchange Gain/Loss - _TC", 100.0, 0, None],
+			]
+		)
 
 		self.validate_gl_entries(pe.name, expected_gle)
 
 		outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
 		self.assertEqual(outstanding_amount, 80)
 
-	def test_payment_entry_against_si_usd_to_usd_with_deduction_in_base_currency (self):
-		si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
-			currency="USD", conversion_rate=50, do_not_save=1)
+	def test_payment_entry_against_si_usd_to_usd_with_deduction_in_base_currency(self):
+		si = create_sales_invoice(
+			customer="_Test Customer USD",
+			debit_to="_Test Receivable USD - _TC",
+			currency="USD",
+			conversion_rate=50,
+			do_not_save=1,
+		)
 
 		si.plc_conversion_rate = 50
 		si.save()
 		si.submit()
 
-		pe = get_payment_entry("Sales Invoice", si.name, party_amount=20,
-			bank_account="_Test Bank USD - _TC", bank_amount=900)
+		pe = get_payment_entry(
+			"Sales Invoice", si.name, party_amount=20, bank_account="_Test Bank USD - _TC", bank_amount=900
+		)
 
 		pe.source_exchange_rate = 45.263
 		pe.target_exchange_rate = 45.263
 		pe.reference_no = "1"
 		pe.reference_date = "2016-01-01"
 
-
-		pe.append("deductions", {
-			"account": "_Test Exchange Gain/Loss - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-			"amount": 94.80
-		})
+		pe.append(
+			"deductions",
+			{
+				"account": "_Test Exchange Gain/Loss - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+				"amount": 94.80,
+			},
+		)
 
 		pe.save()
 
@@ -359,8 +425,7 @@
 		pe.set_amounts()
 
 		self.assertEqual(
-			pe.source_exchange_rate, 65.1,
-			"{0} is not equal to {1}".format(pe.source_exchange_rate, 65.1)
+			pe.source_exchange_rate, 65.1, "{0} is not equal to {1}".format(pe.source_exchange_rate, 65.1)
 		)
 
 	def test_internal_transfer_usd_to_inr(self):
@@ -382,20 +447,26 @@
 
 		self.assertEqual(pe.difference_amount, 500)
 
-		pe.append("deductions", {
-			"account": "_Test Exchange Gain/Loss - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-			"amount": 500
-		})
+		pe.append(
+			"deductions",
+			{
+				"account": "_Test Exchange Gain/Loss - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+				"amount": 500,
+			},
+		)
 
 		pe.insert()
 		pe.submit()
 
-		expected_gle = dict((d[0], d) for d in [
-			["_Test Bank USD - _TC", 0, 5000, None],
-			["_Test Bank - _TC", 4500, 0, None],
-			["_Test Exchange Gain/Loss - _TC", 500.0, 0, None],
-		])
+		expected_gle = dict(
+			(d[0], d)
+			for d in [
+				["_Test Bank USD - _TC", 0, 5000, None],
+				["_Test Bank - _TC", 4500, 0, None],
+				["_Test Exchange Gain/Loss - _TC", 500.0, 0, None],
+			]
+		)
 
 		self.validate_gl_entries(pe.name, expected_gle)
 
@@ -435,10 +506,9 @@
 		pe3.insert()
 		pe3.submit()
 
-		expected_gle = dict((d[0], d) for d in [
-			["Debtors - _TC", 100, 0, si1.name],
-			["_Test Cash - _TC", 0, 100, None]
-		])
+		expected_gle = dict(
+			(d[0], d) for d in [["Debtors - _TC", 100, 0, si1.name], ["_Test Cash - _TC", 0, 100, None]]
+		)
 
 		self.validate_gl_entries(pe3.name, expected_gle)
 
@@ -462,12 +532,16 @@
 			self.assertEqual(expected_gle[gle.account][3], gle.against_voucher)
 
 	def get_gle(self, voucher_no):
-		return frappe.db.sql("""select account, debit, credit, against_voucher
+		return frappe.db.sql(
+			"""select account, debit, credit, against_voucher
 			from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
-			order by account asc""", voucher_no, as_dict=1)
+			order by account asc""",
+			voucher_no,
+			as_dict=1,
+		)
 
 	def test_payment_entry_write_off_difference(self):
-		si =  create_sales_invoice()
+		si = create_sales_invoice()
 		pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Cash - _TC")
 		pe.reference_no = "1"
 		pe.reference_date = "2016-01-01"
@@ -477,11 +551,10 @@
 		self.assertEqual(pe.unallocated_amount, 10)
 
 		pe.received_amount = pe.paid_amount = 95
-		pe.append("deductions", {
-			"account": "_Test Write Off - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-			"amount": 5
-		})
+		pe.append(
+			"deductions",
+			{"account": "_Test Write Off - _TC", "cost_center": "_Test Cost Center - _TC", "amount": 5},
+		)
 		pe.save()
 
 		self.assertEqual(pe.unallocated_amount, 0)
@@ -489,27 +562,37 @@
 
 		pe.submit()
 
-		expected_gle = dict((d[0], d) for d in [
-			["Debtors - _TC", 0, 100, si.name],
-			["_Test Cash - _TC", 95, 0, None],
-			["_Test Write Off - _TC", 5, 0, None]
-		])
+		expected_gle = dict(
+			(d[0], d)
+			for d in [
+				["Debtors - _TC", 0, 100, si.name],
+				["_Test Cash - _TC", 95, 0, None],
+				["_Test Write Off - _TC", 5, 0, None],
+			]
+		)
 
 		self.validate_gl_entries(pe.name, expected_gle)
 
 	def test_payment_entry_exchange_gain_loss(self):
-		si =  create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
-			currency="USD", conversion_rate=50)
+		si = create_sales_invoice(
+			customer="_Test Customer USD",
+			debit_to="_Test Receivable USD - _TC",
+			currency="USD",
+			conversion_rate=50,
+		)
 		pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
 		pe.reference_no = "1"
 		pe.reference_date = "2016-01-01"
 		pe.source_exchange_rate = 55
 
-		pe.append("deductions", {
-			"account": "_Test Exchange Gain/Loss - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-			"amount": -500
-		})
+		pe.append(
+			"deductions",
+			{
+				"account": "_Test Exchange Gain/Loss - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+				"amount": -500,
+			},
+		)
 		pe.save()
 
 		self.assertEqual(pe.unallocated_amount, 0)
@@ -517,11 +600,14 @@
 
 		pe.submit()
 
-		expected_gle = dict((d[0], d) for d in [
-			["_Test Receivable USD - _TC", 0, 5000, si.name],
-			["_Test Bank USD - _TC", 5500, 0, None],
-			["_Test Exchange Gain/Loss - _TC", 0, 500, None],
-		])
+		expected_gle = dict(
+			(d[0], d)
+			for d in [
+				["_Test Receivable USD - _TC", 0, 5000, si.name],
+				["_Test Bank USD - _TC", 5500, 0, None],
+				["_Test Exchange Gain/Loss - _TC", 0, 500, None],
+			]
+		)
 
 		self.validate_gl_entries(pe.name, expected_gle)
 
@@ -530,10 +616,11 @@
 
 	def test_payment_entry_against_sales_invoice_with_cost_centre(self):
 		from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
+
 		cost_center = "_Test Cost Center for BS Account - _TC"
 		create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
 
-		si =  create_sales_invoice_against_cost_center(cost_center=cost_center, debit_to="Debtors - _TC")
+		si = create_sales_invoice_against_cost_center(cost_center=cost_center, debit_to="Debtors - _TC")
 
 		pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
 		self.assertEqual(pe.cost_center, si.cost_center)
@@ -546,18 +633,18 @@
 		pe.submit()
 
 		expected_values = {
-			"_Test Bank - _TC": {
-				"cost_center": cost_center
-			},
-			"Debtors - _TC": {
-				"cost_center": cost_center
-			}
+			"_Test Bank - _TC": {"cost_center": cost_center},
+			"Debtors - _TC": {"cost_center": cost_center},
 		}
 
-		gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit,
+		gl_entries = frappe.db.sql(
+			"""select account, cost_center, account_currency, debit, credit,
 			debit_in_account_currency, credit_in_account_currency
 			from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
-			order by account asc""", pe.name, as_dict=1)
+			order by account asc""",
+			pe.name,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
@@ -566,10 +653,13 @@
 
 	def test_payment_entry_against_purchase_invoice_with_cost_center(self):
 		from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
+
 		cost_center = "_Test Cost Center for BS Account - _TC"
 		create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
 
-		pi =  make_purchase_invoice_against_cost_center(cost_center=cost_center, credit_to="Creditors - _TC")
+		pi = make_purchase_invoice_against_cost_center(
+			cost_center=cost_center, credit_to="Creditors - _TC"
+		)
 
 		pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
 		self.assertEqual(pe.cost_center, pi.cost_center)
@@ -582,18 +672,18 @@
 		pe.submit()
 
 		expected_values = {
-			"_Test Bank - _TC": {
-				"cost_center": cost_center
-			},
-			"Creditors - _TC": {
-				"cost_center": cost_center
-			}
+			"_Test Bank - _TC": {"cost_center": cost_center},
+			"Creditors - _TC": {"cost_center": cost_center},
 		}
 
-		gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit,
+		gl_entries = frappe.db.sql(
+			"""select account, cost_center, account_currency, debit, credit,
 			debit_in_account_currency, credit_in_account_currency
 			from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
-			order by account asc""", pe.name, as_dict=1)
+			order by account asc""",
+			pe.name,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
@@ -603,13 +693,16 @@
 	def test_payment_entry_account_and_party_balance_with_cost_center(self):
 		from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
 		from erpnext.accounts.utils import get_balance_on
+
 		cost_center = "_Test Cost Center for BS Account - _TC"
 		create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
 
-		si =  create_sales_invoice_against_cost_center(cost_center=cost_center, debit_to="Debtors - _TC")
+		si = create_sales_invoice_against_cost_center(cost_center=cost_center, debit_to="Debtors - _TC")
 
 		account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=si.cost_center)
-		party_balance = get_balance_on(party_type="Customer", party=si.customer, cost_center=si.cost_center)
+		party_balance = get_balance_on(
+			party_type="Customer", party=si.customer, cost_center=si.cost_center
+		)
 		party_account_balance = get_balance_on(si.debit_to, cost_center=si.cost_center)
 
 		pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
@@ -634,94 +727,124 @@
 		self.assertEqual(flt(expected_party_account_balance), party_account_balance)
 
 	def test_multi_currency_payment_entry_with_taxes(self):
-		payment_entry = create_payment_entry(party='_Test Supplier USD', paid_to = '_Test Payable USD - _TC',
-			save=True)
-		payment_entry.append('taxes', {
-			'account_head': '_Test Account Service Tax - _TC',
-			'charge_type': 'Actual',
-			'tax_amount': 10,
-			'add_deduct_tax': 'Add',
-			'description': 'Test'
-		})
+		payment_entry = create_payment_entry(
+			party="_Test Supplier USD", paid_to="_Test Payable USD - _TC", save=True
+		)
+		payment_entry.append(
+			"taxes",
+			{
+				"account_head": "_Test Account Service Tax - _TC",
+				"charge_type": "Actual",
+				"tax_amount": 10,
+				"add_deduct_tax": "Add",
+				"description": "Test",
+			},
+		)
 
 		payment_entry.save()
 		self.assertEqual(payment_entry.base_total_taxes_and_charges, 10)
-		self.assertEqual(flt(payment_entry.total_taxes_and_charges, 2), flt(10 / payment_entry.target_exchange_rate, 2))
+		self.assertEqual(
+			flt(payment_entry.total_taxes_and_charges, 2), flt(10 / payment_entry.target_exchange_rate, 2)
+		)
+
+	def test_payment_entry_against_onhold_purchase_invoice(self):
+		pi = make_purchase_invoice()
+
+		pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank USD - _TC")
+		pe.reference_no = "1"
+		pe.reference_date = "2016-01-01"
+
+		# block invoice after creating payment entry
+		# since `get_payment_entry` will not attach blocked invoice to payment
+		pi.block_invoice()
+		with self.assertRaises(frappe.ValidationError) as err:
+			pe.save()
+
+		self.assertTrue("is on hold" in str(err.exception).lower())
+
 
 def create_payment_entry(**args):
-	payment_entry = frappe.new_doc('Payment Entry')
-	payment_entry.company = args.get('company') or '_Test Company'
-	payment_entry.payment_type = args.get('payment_type') or 'Pay'
-	payment_entry.party_type = args.get('party_type') or 'Supplier'
-	payment_entry.party = args.get('party') or '_Test Supplier'
-	payment_entry.paid_from = args.get('paid_from') or '_Test Bank - _TC'
-	payment_entry.paid_to = args.get('paid_to') or 'Creditors - _TC'
-	payment_entry.paid_amount = args.get('paid_amount') or 1000
+	payment_entry = frappe.new_doc("Payment Entry")
+	payment_entry.company = args.get("company") or "_Test Company"
+	payment_entry.payment_type = args.get("payment_type") or "Pay"
+	payment_entry.party_type = args.get("party_type") or "Supplier"
+	payment_entry.party = args.get("party") or "_Test Supplier"
+	payment_entry.paid_from = args.get("paid_from") or "_Test Bank - _TC"
+	payment_entry.paid_to = args.get("paid_to") or "Creditors - _TC"
+	payment_entry.paid_amount = args.get("paid_amount") or 1000
 
 	payment_entry.setup_party_account_field()
 	payment_entry.set_missing_values()
 	payment_entry.set_exchange_rate()
 	payment_entry.received_amount = payment_entry.paid_amount / payment_entry.target_exchange_rate
-	payment_entry.reference_no = 'Test001'
+	payment_entry.reference_no = "Test001"
 	payment_entry.reference_date = nowdate()
 
-	if args.get('save'):
+	if args.get("save"):
 		payment_entry.save()
-		if args.get('submit'):
+		if args.get("submit"):
 			payment_entry.submit()
 
 	return payment_entry
 
+
 def create_payment_terms_template():
 
-	create_payment_term('Basic Amount Receivable')
-	create_payment_term('Tax Receivable')
+	create_payment_term("Basic Amount Receivable")
+	create_payment_term("Tax Receivable")
 
-	if not frappe.db.exists('Payment Terms Template', 'Test Receivable Template'):
-		payment_term_template = frappe.get_doc({
-			'doctype': 'Payment Terms Template',
-			'template_name': 'Test Receivable Template',
-			'allocate_payment_based_on_payment_terms': 1,
-			'terms': [{
-				'doctype': 'Payment Terms Template Detail',
-				'payment_term': 'Basic Amount Receivable',
-				'invoice_portion': 84.746,
-				'credit_days_based_on': 'Day(s) after invoice date',
-				'credit_days': 1
-			},
+	if not frappe.db.exists("Payment Terms Template", "Test Receivable Template"):
+		payment_term_template = frappe.get_doc(
 			{
-				'doctype': 'Payment Terms Template Detail',
-				'payment_term': 'Tax Receivable',
-				'invoice_portion': 15.254,
-				'credit_days_based_on': 'Day(s) after invoice date',
-				'credit_days': 2
-			}]
-		}).insert()
+				"doctype": "Payment Terms Template",
+				"template_name": "Test Receivable Template",
+				"allocate_payment_based_on_payment_terms": 1,
+				"terms": [
+					{
+						"doctype": "Payment Terms Template Detail",
+						"payment_term": "Basic Amount Receivable",
+						"invoice_portion": 84.746,
+						"credit_days_based_on": "Day(s) after invoice date",
+						"credit_days": 1,
+					},
+					{
+						"doctype": "Payment Terms Template Detail",
+						"payment_term": "Tax Receivable",
+						"invoice_portion": 15.254,
+						"credit_days_based_on": "Day(s) after invoice date",
+						"credit_days": 2,
+					},
+				],
+			}
+		).insert()
+
 
 def create_payment_terms_template_with_discount():
 
-	create_payment_term('30 Credit Days with 10% Discount')
+	create_payment_term("30 Credit Days with 10% Discount")
 
-	if not frappe.db.exists('Payment Terms Template', 'Test Discount Template'):
-		payment_term_template = frappe.get_doc({
-			'doctype': 'Payment Terms Template',
-			'template_name': 'Test Discount Template',
-			'allocate_payment_based_on_payment_terms': 1,
-			'terms': [{
-				'doctype': 'Payment Terms Template Detail',
-				'payment_term': '30 Credit Days with 10% Discount',
-				'invoice_portion': 100,
-				'credit_days_based_on': 'Day(s) after invoice date',
-				'credit_days': 2,
-				'discount': 10,
-				'discount_validity_based_on': 'Day(s) after invoice date',
-				'discount_validity': 1
-			}]
-		}).insert()
+	if not frappe.db.exists("Payment Terms Template", "Test Discount Template"):
+		payment_term_template = frappe.get_doc(
+			{
+				"doctype": "Payment Terms Template",
+				"template_name": "Test Discount Template",
+				"allocate_payment_based_on_payment_terms": 1,
+				"terms": [
+					{
+						"doctype": "Payment Terms Template Detail",
+						"payment_term": "30 Credit Days with 10% Discount",
+						"invoice_portion": 100,
+						"credit_days_based_on": "Day(s) after invoice date",
+						"credit_days": 2,
+						"discount": 10,
+						"discount_validity_based_on": "Day(s) after invoice date",
+						"discount_validity": 1,
+					}
+				],
+			}
+		).insert()
+
 
 def create_payment_term(name):
-	if not frappe.db.exists('Payment Term', name):
-		frappe.get_doc({
-			'doctype': 'Payment Term',
-			'payment_term_name': name
-		}).insert()
+	if not frappe.db.exists("Payment Term", name):
+		frappe.get_doc({"doctype": "Payment Term", "payment_term_name": name}).insert()
diff --git a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py
index 25dc4e6..ab47b61 100644
--- a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py
+++ b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py
@@ -18,10 +18,13 @@
 
 	def update_default_payment_gateway(self):
 		if self.is_default:
-			frappe.db.sql("""update `tabPayment Gateway Account` set is_default = 0
-				where is_default = 1 """)
+			frappe.db.sql(
+				"""update `tabPayment Gateway Account` set is_default = 0
+				where is_default = 1 """
+			)
 
 	def set_as_default_if_not_set(self):
-		if not frappe.db.get_value("Payment Gateway Account",
-			{"is_default": 1, "name": ("!=", self.name)}, "name"):
+		if not frappe.db.get_value(
+			"Payment Gateway Account", {"is_default": 1, "name": ("!=", self.name)}, "name"
+		):
 			self.is_default = 1
diff --git a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account_dashboard.py b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account_dashboard.py
index 3996892..d0aaee8 100644
--- a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account_dashboard.py
+++ b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account_dashboard.py
@@ -1,15 +1,6 @@
 def get_data():
 	return {
-		'fieldname': 'payment_gateway_account',
-		'non_standard_fieldnames': {
-			'Subscription Plan': 'payment_gateway'
-		},
-		'transactions': [
-			{
-				'items': ['Payment Request']
-			},
-			{
-				'items': ['Subscription Plan']
-			}
-		]
+		"fieldname": "payment_gateway_account",
+		"non_standard_fieldnames": {"Subscription Plan": "payment_gateway"},
+		"transactions": [{"items": ["Payment Request"]}, {"items": ["Subscription Plan"]}],
 	}
diff --git a/erpnext/accounts/doctype/payment_gateway_account/test_payment_gateway_account.py b/erpnext/accounts/doctype/payment_gateway_account/test_payment_gateway_account.py
index 1895c12..7a8cdf7 100644
--- a/erpnext/accounts/doctype/payment_gateway_account/test_payment_gateway_account.py
+++ b/erpnext/accounts/doctype/payment_gateway_account/test_payment_gateway_account.py
@@ -5,5 +5,6 @@
 
 # test_records = frappe.get_test_records('Payment Gateway Account')
 
+
 class TestPaymentGatewayAccount(unittest.TestCase):
 	pass
diff --git a/erpnext/education/doctype/content_activity/__init__.py b/erpnext/accounts/doctype/payment_ledger_entry/__init__.py
similarity index 100%
rename from erpnext/education/doctype/content_activity/__init__.py
rename to erpnext/accounts/doctype/payment_ledger_entry/__init__.py
diff --git a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.js b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.js
new file mode 100644
index 0000000..5a7be8e
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Payment Ledger Entry', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json
new file mode 100644
index 0000000..39e9042
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json
@@ -0,0 +1,178 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2022-05-09 19:35:03.334361",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "posting_date",
+  "company",
+  "account_type",
+  "account",
+  "party_type",
+  "party",
+  "due_date",
+  "cost_center",
+  "finance_book",
+  "voucher_type",
+  "voucher_no",
+  "against_voucher_type",
+  "against_voucher_no",
+  "amount",
+  "account_currency",
+  "amount_in_account_currency",
+  "delinked"
+ ],
+ "fields": [
+  {
+   "fieldname": "posting_date",
+   "fieldtype": "Date",
+   "label": "Posting Date"
+  },
+  {
+   "fieldname": "account_type",
+   "fieldtype": "Select",
+   "label": "Account Type",
+   "options": "Receivable\nPayable"
+  },
+  {
+   "fieldname": "account",
+   "fieldtype": "Link",
+   "label": "Account",
+   "options": "Account"
+  },
+  {
+   "fieldname": "party_type",
+   "fieldtype": "Link",
+   "label": "Party Type",
+   "options": "DocType"
+  },
+  {
+   "fieldname": "party",
+   "fieldtype": "Dynamic Link",
+   "label": "Party",
+   "options": "party_type"
+  },
+  {
+   "fieldname": "voucher_type",
+   "fieldtype": "Link",
+   "in_standard_filter": 1,
+   "label": "Voucher Type",
+   "options": "DocType"
+  },
+  {
+   "fieldname": "voucher_no",
+   "fieldtype": "Dynamic Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Voucher No",
+   "options": "voucher_type"
+  },
+  {
+   "fieldname": "against_voucher_type",
+   "fieldtype": "Link",
+   "in_standard_filter": 1,
+   "label": "Against Voucher Type",
+   "options": "DocType"
+  },
+  {
+   "fieldname": "against_voucher_no",
+   "fieldtype": "Dynamic Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Against Voucher No",
+   "options": "against_voucher_type"
+  },
+  {
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Amount",
+   "options": "Company:company:default_currency"
+  },
+  {
+   "fieldname": "account_currency",
+   "fieldtype": "Link",
+   "label": "Currency",
+   "options": "Currency"
+  },
+  {
+   "fieldname": "amount_in_account_currency",
+   "fieldtype": "Currency",
+   "label": "Amount in Account Currency",
+   "options": "account_currency"
+  },
+  {
+   "default": "0",
+   "fieldname": "delinked",
+   "fieldtype": "Check",
+   "in_list_view": 1,
+   "label": "DeLinked"
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company"
+  },
+  {
+   "fieldname": "cost_center",
+   "fieldtype": "Link",
+   "label": "Cost Center",
+   "options": "Cost Center"
+  },
+  {
+   "fieldname": "due_date",
+   "fieldtype": "Date",
+   "label": "Due Date"
+  },
+  {
+   "fieldname": "finance_book",
+   "fieldtype": "Link",
+   "label": "Finance Book",
+   "options": "Finance Book"
+  }
+ ],
+ "in_create": 1,
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2022-05-30 19:04:55.532171",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payment Ledger Entry",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Accounts User",
+   "share": 1
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Accounts Manager",
+   "share": 1
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Auditor",
+   "share": 1
+  }
+ ],
+ "search_fields": "voucher_no, against_voucher_no",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py
new file mode 100644
index 0000000..52df923
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py
@@ -0,0 +1,149 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+
+import frappe
+from frappe import _
+from frappe.model.document import Document
+
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+	get_checks_for_pl_and_bs_accounts,
+)
+from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import (
+	get_dimension_filter_map,
+)
+from erpnext.accounts.doctype.gl_entry.gl_entry import (
+	validate_balance_type,
+	validate_frozen_account,
+)
+from erpnext.accounts.utils import update_voucher_outstanding
+from erpnext.exceptions import InvalidAccountDimensionError, MandatoryAccountDimensionError
+
+
+class PaymentLedgerEntry(Document):
+	def validate_account(self):
+		valid_account = frappe.db.get_list(
+			"Account",
+			"name",
+			filters={"name": self.account, "account_type": self.account_type, "company": self.company},
+			ignore_permissions=True,
+		)
+		if not valid_account:
+			frappe.throw(_("{0} account is not of type {1}").format(self.account, self.account_type))
+
+	def validate_account_details(self):
+		"""Account must be ledger, active and not freezed"""
+
+		ret = frappe.db.sql(
+			"""select is_group, docstatus, company
+			from tabAccount where name=%s""",
+			self.account,
+			as_dict=1,
+		)[0]
+
+		if ret.is_group == 1:
+			frappe.throw(
+				_(
+					"""{0} {1}: Account {2} is a Group Account and group accounts cannot be used in transactions"""
+				).format(self.voucher_type, self.voucher_no, self.account)
+			)
+
+		if ret.docstatus == 2:
+			frappe.throw(
+				_("{0} {1}: Account {2} is inactive").format(self.voucher_type, self.voucher_no, self.account)
+			)
+
+		if ret.company != self.company:
+			frappe.throw(
+				_("{0} {1}: Account {2} does not belong to Company {3}").format(
+					self.voucher_type, self.voucher_no, self.account, self.company
+				)
+			)
+
+	def validate_allowed_dimensions(self):
+		dimension_filter_map = get_dimension_filter_map()
+		for key, value in dimension_filter_map.items():
+			dimension = key[0]
+			account = key[1]
+
+			if self.account == account:
+				if value["is_mandatory"] and not self.get(dimension):
+					frappe.throw(
+						_("{0} is mandatory for account {1}").format(
+							frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)
+						),
+						MandatoryAccountDimensionError,
+					)
+
+				if value["allow_or_restrict"] == "Allow":
+					if self.get(dimension) and self.get(dimension) not in value["allowed_dimensions"]:
+						frappe.throw(
+							_("Invalid value {0} for {1} against account {2}").format(
+								frappe.bold(self.get(dimension)),
+								frappe.bold(frappe.unscrub(dimension)),
+								frappe.bold(self.account),
+							),
+							InvalidAccountDimensionError,
+						)
+				else:
+					if self.get(dimension) and self.get(dimension) in value["allowed_dimensions"]:
+						frappe.throw(
+							_("Invalid value {0} for {1} against account {2}").format(
+								frappe.bold(self.get(dimension)),
+								frappe.bold(frappe.unscrub(dimension)),
+								frappe.bold(self.account),
+							),
+							InvalidAccountDimensionError,
+						)
+
+	def validate_dimensions_for_pl_and_bs(self):
+		account_type = frappe.db.get_value("Account", self.account, "report_type")
+
+		for dimension in get_checks_for_pl_and_bs_accounts():
+			if (
+				account_type == "Profit and Loss"
+				and self.company == dimension.company
+				and dimension.mandatory_for_pl
+				and not dimension.disabled
+			):
+				if not self.get(dimension.fieldname):
+					frappe.throw(
+						_("Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}.").format(
+							dimension.label, self.account
+						)
+					)
+
+			if (
+				account_type == "Balance Sheet"
+				and self.company == dimension.company
+				and dimension.mandatory_for_bs
+				and not dimension.disabled
+			):
+				if not self.get(dimension.fieldname):
+					frappe.throw(
+						_("Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}.").format(
+							dimension.label, self.account
+						)
+					)
+
+	def validate(self):
+		self.validate_account()
+
+	def on_update(self):
+		adv_adj = self.flags.adv_adj
+		if not self.flags.from_repost:
+			self.validate_account_details()
+			self.validate_dimensions_for_pl_and_bs()
+			self.validate_allowed_dimensions()
+			validate_balance_type(self.account, adv_adj)
+			validate_frozen_account(self.account, adv_adj)
+
+		# update outstanding amount
+		if (
+			self.against_voucher_type in ["Journal Entry", "Sales Invoice", "Purchase Invoice", "Fees"]
+			and self.flags.update_outstanding == "Yes"
+			and not frappe.flags.is_reverse_depr_entry
+		):
+			update_voucher_outstanding(
+				self.against_voucher_type, self.against_voucher_no, self.account, self.party_type, self.party
+			)
diff --git a/erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py b/erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py
new file mode 100644
index 0000000..a71b19e
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py
@@ -0,0 +1,408 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+import frappe
+from frappe import qb
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import nowdate
+
+from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
+from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+from erpnext.stock.doctype.item.test_item import create_item
+
+
+class TestPaymentLedgerEntry(FrappeTestCase):
+	def setUp(self):
+		self.ple = qb.DocType("Payment Ledger Entry")
+		self.create_company()
+		self.create_item()
+		self.create_customer()
+		self.clear_old_entries()
+
+	def tearDown(self):
+		frappe.db.rollback()
+
+	def create_company(self):
+		company_name = "_Test Payment Ledger"
+		company = None
+		if frappe.db.exists("Company", company_name):
+			company = frappe.get_doc("Company", company_name)
+		else:
+			company = frappe.get_doc(
+				{
+					"doctype": "Company",
+					"company_name": company_name,
+					"country": "India",
+					"default_currency": "INR",
+					"create_chart_of_accounts_based_on": "Standard Template",
+					"chart_of_accounts": "Standard",
+				}
+			)
+			company = company.save()
+
+		self.company = company.name
+		self.cost_center = company.cost_center
+		self.warehouse = "All Warehouses - _PL"
+		self.income_account = "Sales - _PL"
+		self.expense_account = "Cost of Goods Sold - _PL"
+		self.debit_to = "Debtors - _PL"
+		self.creditors = "Creditors - _PL"
+
+		# create bank account
+		if frappe.db.exists("Account", "HDFC - _PL"):
+			self.bank = "HDFC - _PL"
+		else:
+			bank_acc = frappe.get_doc(
+				{
+					"doctype": "Account",
+					"account_name": "HDFC",
+					"parent_account": "Bank Accounts - _PL",
+					"company": self.company,
+				}
+			)
+			bank_acc.save()
+			self.bank = bank_acc.name
+
+	def create_item(self):
+		item_name = "_Test PL Item"
+		item = create_item(
+			item_code=item_name, is_stock_item=0, company=self.company, warehouse=self.warehouse
+		)
+		self.item = item if isinstance(item, str) else item.item_code
+
+	def create_customer(self):
+		name = "_Test PL Customer"
+		if frappe.db.exists("Customer", name):
+			self.customer = name
+		else:
+			customer = frappe.new_doc("Customer")
+			customer.customer_name = name
+			customer.type = "Individual"
+			customer.save()
+			self.customer = customer.name
+
+	def create_sales_invoice(
+		self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False
+	):
+		"""
+		Helper function to populate default values in sales invoice
+		"""
+		sinv = create_sales_invoice(
+			qty=qty,
+			rate=rate,
+			company=self.company,
+			customer=self.customer,
+			item_code=self.item,
+			item_name=self.item,
+			cost_center=self.cost_center,
+			warehouse=self.warehouse,
+			debit_to=self.debit_to,
+			parent_cost_center=self.cost_center,
+			update_stock=0,
+			currency="INR",
+			is_pos=0,
+			is_return=0,
+			return_against=None,
+			income_account=self.income_account,
+			expense_account=self.expense_account,
+			do_not_save=do_not_save,
+			do_not_submit=do_not_submit,
+		)
+		return sinv
+
+	def create_payment_entry(self, amount=100, posting_date=nowdate()):
+		"""
+		Helper function to populate default values in payment entry
+		"""
+		payment = create_payment_entry(
+			company=self.company,
+			payment_type="Receive",
+			party_type="Customer",
+			party=self.customer,
+			paid_from=self.debit_to,
+			paid_to=self.bank,
+			paid_amount=amount,
+		)
+		payment.posting_date = posting_date
+		return payment
+
+	def clear_old_entries(self):
+		doctype_list = [
+			"GL Entry",
+			"Payment Ledger Entry",
+			"Sales Invoice",
+			"Purchase Invoice",
+			"Payment Entry",
+			"Journal Entry",
+		]
+		for doctype in doctype_list:
+			qb.from_(qb.DocType(doctype)).delete().where(qb.DocType(doctype).company == self.company).run()
+
+	def create_journal_entry(
+		self, acc1=None, acc2=None, amount=0, posting_date=None, cost_center=None
+	):
+		je = frappe.new_doc("Journal Entry")
+		je.posting_date = posting_date or nowdate()
+		je.company = self.company
+		je.user_remark = "test"
+		if not cost_center:
+			cost_center = self.cost_center
+		je.set(
+			"accounts",
+			[
+				{
+					"account": acc1,
+					"cost_center": cost_center,
+					"debit_in_account_currency": amount if amount > 0 else 0,
+					"credit_in_account_currency": abs(amount) if amount < 0 else 0,
+				},
+				{
+					"account": acc2,
+					"cost_center": cost_center,
+					"credit_in_account_currency": amount if amount > 0 else 0,
+					"debit_in_account_currency": abs(amount) if amount < 0 else 0,
+				},
+			],
+		)
+		return je
+
+	def test_payment_against_invoice(self):
+		transaction_date = nowdate()
+		amount = 100
+		ple = self.ple
+
+		# full payment using PE
+		si1 = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
+		pe1 = get_payment_entry(si1.doctype, si1.name).save().submit()
+
+		pl_entries = (
+			qb.from_(ple)
+			.select(
+				ple.voucher_type,
+				ple.voucher_no,
+				ple.against_voucher_type,
+				ple.against_voucher_no,
+				ple.amount,
+				ple.delinked,
+			)
+			.where((ple.against_voucher_type == si1.doctype) & (ple.against_voucher_no == si1.name))
+			.orderby(ple.creation)
+			.run(as_dict=True)
+		)
+
+		expected_values = [
+			{
+				"voucher_type": si1.doctype,
+				"voucher_no": si1.name,
+				"against_voucher_type": si1.doctype,
+				"against_voucher_no": si1.name,
+				"amount": amount,
+				"delinked": 0,
+			},
+			{
+				"voucher_type": pe1.doctype,
+				"voucher_no": pe1.name,
+				"against_voucher_type": si1.doctype,
+				"against_voucher_no": si1.name,
+				"amount": -amount,
+				"delinked": 0,
+			},
+		]
+		self.assertEqual(pl_entries[0], expected_values[0])
+		self.assertEqual(pl_entries[1], expected_values[1])
+
+	def test_partial_payment_against_invoice(self):
+		ple = self.ple
+		transaction_date = nowdate()
+		amount = 100
+
+		# partial payment of invoice using PE
+		si2 = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
+		pe2 = get_payment_entry(si2.doctype, si2.name)
+		pe2.get("references")[0].allocated_amount = 50
+		pe2.get("references")[0].outstanding_amount = 50
+		pe2 = pe2.save().submit()
+
+		pl_entries = (
+			qb.from_(ple)
+			.select(
+				ple.voucher_type,
+				ple.voucher_no,
+				ple.against_voucher_type,
+				ple.against_voucher_no,
+				ple.amount,
+				ple.delinked,
+			)
+			.where((ple.against_voucher_type == si2.doctype) & (ple.against_voucher_no == si2.name))
+			.orderby(ple.creation)
+			.run(as_dict=True)
+		)
+
+		expected_values = [
+			{
+				"voucher_type": si2.doctype,
+				"voucher_no": si2.name,
+				"against_voucher_type": si2.doctype,
+				"against_voucher_no": si2.name,
+				"amount": amount,
+				"delinked": 0,
+			},
+			{
+				"voucher_type": pe2.doctype,
+				"voucher_no": pe2.name,
+				"against_voucher_type": si2.doctype,
+				"against_voucher_no": si2.name,
+				"amount": -50,
+				"delinked": 0,
+			},
+		]
+		self.assertEqual(pl_entries[0], expected_values[0])
+		self.assertEqual(pl_entries[1], expected_values[1])
+
+	def test_cr_note_against_invoice(self):
+		ple = self.ple
+		transaction_date = nowdate()
+		amount = 100
+
+		# reconcile against return invoice
+		si3 = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
+		cr_note1 = self.create_sales_invoice(
+			qty=-1, rate=amount, posting_date=transaction_date, do_not_save=True, do_not_submit=True
+		)
+		cr_note1.is_return = 1
+		cr_note1.return_against = si3.name
+		cr_note1 = cr_note1.save().submit()
+
+		pl_entries = (
+			qb.from_(ple)
+			.select(
+				ple.voucher_type,
+				ple.voucher_no,
+				ple.against_voucher_type,
+				ple.against_voucher_no,
+				ple.amount,
+				ple.delinked,
+			)
+			.where((ple.against_voucher_type == si3.doctype) & (ple.against_voucher_no == si3.name))
+			.orderby(ple.creation)
+			.run(as_dict=True)
+		)
+
+		expected_values = [
+			{
+				"voucher_type": si3.doctype,
+				"voucher_no": si3.name,
+				"against_voucher_type": si3.doctype,
+				"against_voucher_no": si3.name,
+				"amount": amount,
+				"delinked": 0,
+			},
+			{
+				"voucher_type": cr_note1.doctype,
+				"voucher_no": cr_note1.name,
+				"against_voucher_type": si3.doctype,
+				"against_voucher_no": si3.name,
+				"amount": -amount,
+				"delinked": 0,
+			},
+		]
+		self.assertEqual(pl_entries[0], expected_values[0])
+		self.assertEqual(pl_entries[1], expected_values[1])
+
+	def test_je_against_inv_and_note(self):
+		ple = self.ple
+		transaction_date = nowdate()
+		amount = 100
+
+		# reconcile against return invoice using JE
+		si4 = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
+		cr_note2 = self.create_sales_invoice(
+			qty=-1, rate=amount, posting_date=transaction_date, do_not_save=True, do_not_submit=True
+		)
+		cr_note2.is_return = 1
+		cr_note2 = cr_note2.save().submit()
+		je1 = self.create_journal_entry(
+			self.debit_to, self.debit_to, amount, posting_date=transaction_date
+		)
+		je1.get("accounts")[0].party_type = je1.get("accounts")[1].party_type = "Customer"
+		je1.get("accounts")[0].party = je1.get("accounts")[1].party = self.customer
+		je1.get("accounts")[0].reference_type = cr_note2.doctype
+		je1.get("accounts")[0].reference_name = cr_note2.name
+		je1.get("accounts")[1].reference_type = si4.doctype
+		je1.get("accounts")[1].reference_name = si4.name
+		je1 = je1.save().submit()
+
+		pl_entries_for_invoice = (
+			qb.from_(ple)
+			.select(
+				ple.voucher_type,
+				ple.voucher_no,
+				ple.against_voucher_type,
+				ple.against_voucher_no,
+				ple.amount,
+				ple.delinked,
+			)
+			.where((ple.against_voucher_type == si4.doctype) & (ple.against_voucher_no == si4.name))
+			.orderby(ple.creation)
+			.run(as_dict=True)
+		)
+
+		expected_values = [
+			{
+				"voucher_type": si4.doctype,
+				"voucher_no": si4.name,
+				"against_voucher_type": si4.doctype,
+				"against_voucher_no": si4.name,
+				"amount": amount,
+				"delinked": 0,
+			},
+			{
+				"voucher_type": je1.doctype,
+				"voucher_no": je1.name,
+				"against_voucher_type": si4.doctype,
+				"against_voucher_no": si4.name,
+				"amount": -amount,
+				"delinked": 0,
+			},
+		]
+		self.assertEqual(pl_entries_for_invoice[0], expected_values[0])
+		self.assertEqual(pl_entries_for_invoice[1], expected_values[1])
+
+		pl_entries_for_crnote = (
+			qb.from_(ple)
+			.select(
+				ple.voucher_type,
+				ple.voucher_no,
+				ple.against_voucher_type,
+				ple.against_voucher_no,
+				ple.amount,
+				ple.delinked,
+			)
+			.where(
+				(ple.against_voucher_type == cr_note2.doctype) & (ple.against_voucher_no == cr_note2.name)
+			)
+			.orderby(ple.creation)
+			.run(as_dict=True)
+		)
+
+		expected_values = [
+			{
+				"voucher_type": cr_note2.doctype,
+				"voucher_no": cr_note2.name,
+				"against_voucher_type": cr_note2.doctype,
+				"against_voucher_no": cr_note2.name,
+				"amount": -amount,
+				"delinked": 0,
+			},
+			{
+				"voucher_type": je1.doctype,
+				"voucher_no": je1.name,
+				"against_voucher_type": cr_note2.doctype,
+				"against_voucher_no": cr_note2.name,
+				"amount": amount,
+				"delinked": 0,
+			},
+		]
+		self.assertEqual(pl_entries_for_crnote[0], expected_values[0])
+		self.assertEqual(pl_entries_for_crnote[1], expected_values[1])
diff --git a/erpnext/accounts/doctype/payment_order/payment_order.js b/erpnext/accounts/doctype/payment_order/payment_order.js
index 9074def..7d85d89 100644
--- a/erpnext/accounts/doctype/payment_order/payment_order.js
+++ b/erpnext/accounts/doctype/payment_order/payment_order.js
@@ -12,7 +12,6 @@
 		});
 
 		frm.set_df_property('references', 'cannot_add_rows', true);
-		frm.set_df_property('references', 'cannot_delete_rows', true);
 	},
 	refresh: function(frm) {
 		if (frm.doc.docstatus == 0) {
diff --git a/erpnext/accounts/doctype/payment_order/payment_order.py b/erpnext/accounts/doctype/payment_order/payment_order.py
index 50a58b8..ff9615d 100644
--- a/erpnext/accounts/doctype/payment_order/payment_order.py
+++ b/erpnext/accounts/doctype/payment_order/payment_order.py
@@ -18,9 +18,9 @@
 		self.update_payment_status(cancel=True)
 
 	def update_payment_status(self, cancel=False):
-		status = 'Payment Ordered'
+		status = "Payment Ordered"
 		if cancel:
-			status = 'Initiated'
+			status = "Initiated"
 
 		if self.payment_order_type == "Payment Request":
 			ref_field = "status"
@@ -32,67 +32,67 @@
 		for d in self.references:
 			frappe.db.set_value(self.payment_order_type, d.get(ref_doc_field), ref_field, status)
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_mop_query(doctype, txt, searchfield, start, page_len, filters):
-	return frappe.db.sql(""" select mode_of_payment from `tabPayment Order Reference`
+	return frappe.db.sql(
+		""" select mode_of_payment from `tabPayment Order Reference`
 		where parent = %(parent)s and mode_of_payment like %(txt)s
-		limit %(start)s, %(page_len)s""", {
-			'parent': filters.get("parent"),
-			'start': start,
-			'page_len': page_len,
-			'txt': "%%%s%%" % txt
-		})
+		limit %(page_len)s offset %(start)s""",
+		{"parent": filters.get("parent"), "start": start, "page_len": page_len, "txt": "%%%s%%" % txt},
+	)
+
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_supplier_query(doctype, txt, searchfield, start, page_len, filters):
-	return frappe.db.sql(""" select supplier from `tabPayment Order Reference`
+	return frappe.db.sql(
+		""" select supplier from `tabPayment Order Reference`
 		where parent = %(parent)s and supplier like %(txt)s and
 		(payment_reference is null or payment_reference='')
-		limit %(start)s, %(page_len)s""", {
-			'parent': filters.get("parent"),
-			'start': start,
-			'page_len': page_len,
-			'txt': "%%%s%%" % txt
-		})
+		limit %(page_len)s offset %(start)s""",
+		{"parent": filters.get("parent"), "start": start, "page_len": page_len, "txt": "%%%s%%" % txt},
+	)
+
 
 @frappe.whitelist()
 def make_payment_records(name, supplier, mode_of_payment=None):
-	doc = frappe.get_doc('Payment Order', name)
+	doc = frappe.get_doc("Payment Order", name)
 	make_journal_entry(doc, supplier, mode_of_payment)
 
+
 def make_journal_entry(doc, supplier, mode_of_payment=None):
-	je = frappe.new_doc('Journal Entry')
+	je = frappe.new_doc("Journal Entry")
 	je.payment_order = doc.name
 	je.posting_date = nowdate()
-	mode_of_payment_type = frappe._dict(frappe.get_all('Mode of Payment',
-		fields = ["name", "type"], as_list=1))
+	mode_of_payment_type = frappe._dict(
+		frappe.get_all("Mode of Payment", fields=["name", "type"], as_list=1)
+	)
 
-	je.voucher_type = 'Bank Entry'
-	if mode_of_payment and mode_of_payment_type.get(mode_of_payment) == 'Cash':
+	je.voucher_type = "Bank Entry"
+	if mode_of_payment and mode_of_payment_type.get(mode_of_payment) == "Cash":
 		je.voucher_type = "Cash Entry"
 
 	paid_amt = 0
-	party_account = get_party_account('Supplier', supplier, doc.company)
+	party_account = get_party_account("Supplier", supplier, doc.company)
 	for d in doc.references:
-		if (d.supplier == supplier
-			and (not mode_of_payment or mode_of_payment == d.mode_of_payment)):
-			je.append('accounts', {
-				'account': party_account,
-				'debit_in_account_currency': d.amount,
-				'party_type': 'Supplier',
-				'party': supplier,
-				'reference_type': d.reference_doctype,
-				'reference_name': d.reference_name
-			})
+		if d.supplier == supplier and (not mode_of_payment or mode_of_payment == d.mode_of_payment):
+			je.append(
+				"accounts",
+				{
+					"account": party_account,
+					"debit_in_account_currency": d.amount,
+					"party_type": "Supplier",
+					"party": supplier,
+					"reference_type": d.reference_doctype,
+					"reference_name": d.reference_name,
+				},
+			)
 
 			paid_amt += d.amount
 
-	je.append('accounts', {
-		'account': doc.account,
-		'credit_in_account_currency': paid_amt
-	})
+	je.append("accounts", {"account": doc.account, "credit_in_account_currency": paid_amt})
 
 	je.flags.ignore_mandatory = True
 	je.save()
diff --git a/erpnext/accounts/doctype/payment_order/payment_order_dashboard.py b/erpnext/accounts/doctype/payment_order/payment_order_dashboard.py
index 37bbaec..f82886e 100644
--- a/erpnext/accounts/doctype/payment_order/payment_order_dashboard.py
+++ b/erpnext/accounts/doctype/payment_order/payment_order_dashboard.py
@@ -1,9 +1,5 @@
 def get_data():
 	return {
-		'fieldname': 'payment_order',
-		'transactions': [
-			{
-				'items': ['Payment Entry', 'Journal Entry']
-			}
-		]
+		"fieldname": "payment_order",
+		"transactions": [{"items": ["Payment Entry", "Journal Entry"]}],
 	}
diff --git a/erpnext/accounts/doctype/payment_order/test_payment_order.py b/erpnext/accounts/doctype/payment_order/test_payment_order.py
index 3f4d89b..0dcb179 100644
--- a/erpnext/accounts/doctype/payment_order/test_payment_order.py
+++ b/erpnext/accounts/doctype/payment_order/test_payment_order.py
@@ -26,7 +26,9 @@
 
 	def test_payment_order_creation_against_payment_entry(self):
 		purchase_invoice = make_purchase_invoice()
-		payment_entry = get_payment_entry("Purchase Invoice", purchase_invoice.name, bank_account="_Test Bank - _TC")
+		payment_entry = get_payment_entry(
+			"Purchase Invoice", purchase_invoice.name, bank_account="_Test Bank - _TC"
+		)
 		payment_entry.reference_no = "_Test_Payment_Order"
 		payment_entry.reference_date = getdate()
 		payment_entry.party_bank_account = "Checking Account - Citi Bank"
@@ -40,13 +42,16 @@
 		self.assertEqual(reference_doc.supplier, "_Test Supplier")
 		self.assertEqual(reference_doc.amount, 250)
 
+
 def create_payment_order_against_payment_entry(ref_doc, order_type):
-	payment_order = frappe.get_doc(dict(
-		doctype="Payment Order",
-		company="_Test Company",
-		payment_order_type=order_type,
-		company_bank_account="Checking Account - Citi Bank"
-	))
+	payment_order = frappe.get_doc(
+		dict(
+			doctype="Payment Order",
+			company="_Test Company",
+			payment_order_type=order_type,
+			company_bank_account="Checking Account - Citi Bank",
+		)
+	)
 	doc = make_payment_order(ref_doc.name, payment_order)
 	doc.save()
 	doc.submit()
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
index ad5a840..0b334ae 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
@@ -38,6 +38,15 @@
 				]
 			};
 		});
+
+		this.frm.set_query("cost_center", () => {
+			return {
+				"filters": {
+					"company": this.frm.doc.company,
+					"is_group": 0
+				}
+			}
+		});
 	}
 
 	refresh() {
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
index eb0c20f..18d3485 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
@@ -24,6 +24,7 @@
   "invoice_limit",
   "payment_limit",
   "bank_cash_account",
+  "cost_center",
   "sec_break1",
   "invoices",
   "column_break_15",
@@ -178,13 +179,19 @@
   {
    "fieldname": "column_break_11",
    "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "cost_center",
+   "fieldtype": "Link",
+   "label": "Cost Center",
+   "options": "Cost Center"
   }
  ],
  "hide_toolbar": 1,
  "icon": "icon-resize-horizontal",
  "issingle": 1,
  "links": [],
- "modified": "2021-10-04 20:27:11.114194",
+ "modified": "2022-04-29 15:37:10.246831",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Reconciliation",
@@ -209,5 +216,6 @@
  ],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index 548571d..5b2b526 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -3,16 +3,26 @@
 
 
 import frappe
-from frappe import _, msgprint
+from frappe import _, msgprint, qb
 from frappe.model.document import Document
+from frappe.query_builder.custom import ConstantColumn
+from frappe.query_builder.functions import IfNull
 from frappe.utils import flt, getdate, nowdate, today
 
 import erpnext
-from erpnext.accounts.utils import get_outstanding_invoices, reconcile_against_document
+from erpnext.accounts.utils import (
+	QueryPaymentLedger,
+	get_outstanding_invoices,
+	reconcile_against_document,
+)
 from erpnext.controllers.accounts_controller import get_advance_payment_entries
 
 
 class PaymentReconciliation(Document):
+	def __init__(self, *args, **kwargs):
+		super(PaymentReconciliation, self).__init__(*args, **kwargs)
+		self.common_filter_conditions = []
+
 	@frappe.whitelist()
 	def get_unreconciled_entries(self):
 		self.get_nonreconciled_payment_entries()
@@ -32,30 +42,43 @@
 		non_reconciled_payments = payment_entries + journal_entries + dr_or_cr_notes
 
 		if self.payment_limit:
-			non_reconciled_payments = non_reconciled_payments[:self.payment_limit]
+			non_reconciled_payments = non_reconciled_payments[: self.payment_limit]
 
-		non_reconciled_payments = sorted(non_reconciled_payments, key=lambda k: k['posting_date'] or getdate(nowdate()))
+		non_reconciled_payments = sorted(
+			non_reconciled_payments, key=lambda k: k["posting_date"] or getdate(nowdate())
+		)
 
 		self.add_payment_entries(non_reconciled_payments)
 
 	def get_payment_entries(self):
-		order_doctype = "Sales Order" if self.party_type=="Customer" else "Purchase Order"
+		order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order"
 		condition = self.get_conditions(get_payments=True)
-		payment_entries = get_advance_payment_entries(self.party_type, self.party,
-			self.receivable_payable_account, order_doctype, against_all_orders=True, limit=self.payment_limit,
-			condition=condition)
+		payment_entries = get_advance_payment_entries(
+			self.party_type,
+			self.party,
+			self.receivable_payable_account,
+			order_doctype,
+			against_all_orders=True,
+			limit=self.payment_limit,
+			condition=condition,
+		)
 
 		return payment_entries
 
 	def get_jv_entries(self):
 		condition = self.get_conditions()
-		dr_or_cr = ("credit_in_account_currency" if erpnext.get_party_account_type(self.party_type) == 'Receivable'
-			else "debit_in_account_currency")
+		dr_or_cr = (
+			"credit_in_account_currency"
+			if erpnext.get_party_account_type(self.party_type) == "Receivable"
+			else "debit_in_account_currency"
+		)
 
-		bank_account_condition = "t2.against_account like %(bank_cash_account)s" \
-				if self.bank_cash_account else "1=1"
+		bank_account_condition = (
+			"t2.against_account like %(bank_cash_account)s" if self.bank_cash_account else "1=1"
+		)
 
-		journal_entries = frappe.db.sql("""
+		journal_entries = frappe.db.sql(
+			"""
 			select
 				"Journal Entry" as reference_type, t1.name as reference_name,
 				t1.posting_date, t1.remark as remarks, t2.name as reference_row,
@@ -76,147 +99,176 @@
 					ELSE {bank_account_condition}
 				END)
 			order by t1.posting_date
-			""".format(**{
-				"dr_or_cr": dr_or_cr,
-				"bank_account_condition": bank_account_condition,
-				"condition": condition
-			}), {
+			""".format(
+				**{
+					"dr_or_cr": dr_or_cr,
+					"bank_account_condition": bank_account_condition,
+					"condition": condition,
+				}
+			),
+			{
 				"party_type": self.party_type,
 				"party": self.party,
 				"account": self.receivable_payable_account,
-				"bank_cash_account": "%%%s%%" % self.bank_cash_account
-			}, as_dict=1)
+				"bank_cash_account": "%%%s%%" % self.bank_cash_account,
+			},
+			as_dict=1,
+		)
 
 		return list(journal_entries)
 
 	def get_dr_or_cr_notes(self):
-		condition = self.get_conditions(get_return_invoices=True)
-		dr_or_cr = ("credit_in_account_currency"
-			if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit_in_account_currency")
 
-		reconciled_dr_or_cr =  ("debit_in_account_currency"
-			if dr_or_cr == "credit_in_account_currency" else "credit_in_account_currency")
+		self.build_qb_filter_conditions(get_return_invoices=True)
 
-		voucher_type = ('Sales Invoice'
-			if self.party_type == 'Customer' else "Purchase Invoice")
+		ple = qb.DocType("Payment Ledger Entry")
+		voucher_type = "Sales Invoice" if self.party_type == "Customer" else "Purchase Invoice"
 
-		return frappe.db.sql(""" SELECT doc.name as reference_name, %(voucher_type)s as reference_type,
-				(sum(gl.{dr_or_cr}) - sum(gl.{reconciled_dr_or_cr})) as amount, doc.posting_date,
-				account_currency as currency
-			FROM `tab{doc}` doc, `tabGL Entry` gl
-			WHERE
-				(doc.name = gl.against_voucher or doc.name = gl.voucher_no)
-				and doc.{party_type_field} = %(party)s
-				and doc.is_return = 1 and ifnull(doc.return_against, "") = ""
-				and gl.against_voucher_type = %(voucher_type)s
-				and doc.docstatus = 1 and gl.party = %(party)s
-				and gl.party_type = %(party_type)s and gl.account = %(account)s
-				and gl.is_cancelled = 0 {condition}
-			GROUP BY doc.name
-			Having
-				amount > 0
-			ORDER BY doc.posting_date
-		""".format(
-			doc=voucher_type,
-			dr_or_cr=dr_or_cr,
-			reconciled_dr_or_cr=reconciled_dr_or_cr,
-			party_type_field=frappe.scrub(self.party_type),
-			condition=condition or ""),
-			{
-				'party': self.party,
-				'party_type': self.party_type,
-				'voucher_type': voucher_type,
-				'account': self.receivable_payable_account
-			}, as_dict=1)
+		if erpnext.get_party_account_type(self.party_type) == "Receivable":
+			self.common_filter_conditions.append(ple.account_type == "Receivable")
+		else:
+			self.common_filter_conditions.append(ple.account_type == "Payable")
+		self.common_filter_conditions.append(ple.account == self.receivable_payable_account)
+
+		# get return invoices
+		doc = qb.DocType(voucher_type)
+		return_invoices = (
+			qb.from_(doc)
+			.select(ConstantColumn(voucher_type).as_("voucher_type"), doc.name.as_("voucher_no"))
+			.where(
+				(doc.docstatus == 1)
+				& (doc[frappe.scrub(self.party_type)] == self.party)
+				& (doc.is_return == 1)
+				& (IfNull(doc.return_against, "") == "")
+			)
+			.run(as_dict=True)
+		)
+
+		outstanding_dr_or_cr = []
+		if return_invoices:
+			ple_query = QueryPaymentLedger()
+			return_outstanding = ple_query.get_voucher_outstandings(
+				vouchers=return_invoices,
+				common_filter=self.common_filter_conditions,
+				min_outstanding=-(self.minimum_payment_amount) if self.minimum_payment_amount else None,
+				max_outstanding=-(self.maximum_payment_amount) if self.maximum_payment_amount else None,
+				get_payments=True,
+			)
+
+			for inv in return_outstanding:
+				if inv.outstanding != 0:
+					outstanding_dr_or_cr.append(
+						frappe._dict(
+							{
+								"reference_type": inv.voucher_type,
+								"reference_name": inv.voucher_no,
+								"amount": -(inv.outstanding),
+								"posting_date": inv.posting_date,
+								"currency": inv.currency,
+							}
+						)
+					)
+		return outstanding_dr_or_cr
 
 	def add_payment_entries(self, non_reconciled_payments):
-		self.set('payments', [])
+		self.set("payments", [])
 
 		for payment in non_reconciled_payments:
-			row = self.append('payments', {})
+			row = self.append("payments", {})
 			row.update(payment)
 
 	def get_invoice_entries(self):
-		#Fetch JVs, Sales and Purchase Invoices for 'invoices' to reconcile against
+		# Fetch JVs, Sales and Purchase Invoices for 'invoices' to reconcile against
 
-		condition = self.get_conditions(get_invoices=True)
+		self.build_qb_filter_conditions(get_invoices=True)
 
-		non_reconciled_invoices = get_outstanding_invoices(self.party_type, self.party,
-			self.receivable_payable_account, condition=condition)
+		non_reconciled_invoices = get_outstanding_invoices(
+			self.party_type,
+			self.party,
+			self.receivable_payable_account,
+			common_filter=self.common_filter_conditions,
+			min_outstanding=self.minimum_invoice_amount if self.minimum_invoice_amount else None,
+			max_outstanding=self.maximum_invoice_amount if self.maximum_invoice_amount else None,
+		)
 
 		if self.invoice_limit:
-			non_reconciled_invoices = non_reconciled_invoices[:self.invoice_limit]
+			non_reconciled_invoices = non_reconciled_invoices[: self.invoice_limit]
 
 		self.add_invoice_entries(non_reconciled_invoices)
 
 	def add_invoice_entries(self, non_reconciled_invoices):
-		#Populate 'invoices' with JVs and Invoices to reconcile against
-		self.set('invoices', [])
+		# Populate 'invoices' with JVs and Invoices to reconcile against
+		self.set("invoices", [])
 
 		for entry in non_reconciled_invoices:
-			inv = self.append('invoices', {})
-			inv.invoice_type = entry.get('voucher_type')
-			inv.invoice_number = entry.get('voucher_no')
-			inv.invoice_date = entry.get('posting_date')
-			inv.amount = flt(entry.get('invoice_amount'))
-			inv.currency = entry.get('currency')
-			inv.outstanding_amount = flt(entry.get('outstanding_amount'))
+			inv = self.append("invoices", {})
+			inv.invoice_type = entry.get("voucher_type")
+			inv.invoice_number = entry.get("voucher_no")
+			inv.invoice_date = entry.get("posting_date")
+			inv.amount = flt(entry.get("invoice_amount"))
+			inv.currency = entry.get("currency")
+			inv.outstanding_amount = flt(entry.get("outstanding_amount"))
 
 	@frappe.whitelist()
 	def allocate_entries(self, args):
 		self.validate_entries()
 		entries = []
-		for pay in args.get('payments'):
-			pay.update({'unreconciled_amount': pay.get('amount')})
-			for inv in args.get('invoices'):
-				if pay.get('amount') >= inv.get('outstanding_amount'):
-					res = self.get_allocated_entry(pay, inv, inv['outstanding_amount'])
-					pay['amount'] = flt(pay.get('amount')) - flt(inv.get('outstanding_amount'))
-					inv['outstanding_amount'] = 0
+		for pay in args.get("payments"):
+			pay.update({"unreconciled_amount": pay.get("amount")})
+			for inv in args.get("invoices"):
+				if pay.get("amount") >= inv.get("outstanding_amount"):
+					res = self.get_allocated_entry(pay, inv, inv["outstanding_amount"])
+					pay["amount"] = flt(pay.get("amount")) - flt(inv.get("outstanding_amount"))
+					inv["outstanding_amount"] = 0
 				else:
-					res = self.get_allocated_entry(pay, inv, pay['amount'])
-					inv['outstanding_amount'] = flt(inv.get('outstanding_amount')) - flt(pay.get('amount'))
-					pay['amount'] = 0
-				if pay.get('amount') == 0:
+					res = self.get_allocated_entry(pay, inv, pay["amount"])
+					inv["outstanding_amount"] = flt(inv.get("outstanding_amount")) - flt(pay.get("amount"))
+					pay["amount"] = 0
+				if pay.get("amount") == 0:
 					entries.append(res)
 					break
-				elif inv.get('outstanding_amount') == 0:
+				elif inv.get("outstanding_amount") == 0:
 					entries.append(res)
 					continue
 			else:
 				break
 
-		self.set('allocation', [])
+		self.set("allocation", [])
 		for entry in entries:
-			if entry['allocated_amount'] != 0:
-				row = self.append('allocation', {})
+			if entry["allocated_amount"] != 0:
+				row = self.append("allocation", {})
 				row.update(entry)
 
 	def get_allocated_entry(self, pay, inv, allocated_amount):
-		return frappe._dict({
-			'reference_type': pay.get('reference_type'),
-			'reference_name': pay.get('reference_name'),
-			'reference_row': pay.get('reference_row'),
-			'invoice_type': inv.get('invoice_type'),
-			'invoice_number': inv.get('invoice_number'),
-			'unreconciled_amount': pay.get('unreconciled_amount'),
-			'amount': pay.get('amount'),
-			'allocated_amount': allocated_amount,
-			'difference_amount': pay.get('difference_amount')
-		})
+		return frappe._dict(
+			{
+				"reference_type": pay.get("reference_type"),
+				"reference_name": pay.get("reference_name"),
+				"reference_row": pay.get("reference_row"),
+				"invoice_type": inv.get("invoice_type"),
+				"invoice_number": inv.get("invoice_number"),
+				"unreconciled_amount": pay.get("unreconciled_amount"),
+				"amount": pay.get("amount"),
+				"allocated_amount": allocated_amount,
+				"difference_amount": pay.get("difference_amount"),
+			}
+		)
 
 	@frappe.whitelist()
 	def reconcile(self):
 		self.validate_allocation()
-		dr_or_cr = ("credit_in_account_currency"
-			if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit_in_account_currency")
+		dr_or_cr = (
+			"credit_in_account_currency"
+			if erpnext.get_party_account_type(self.party_type) == "Receivable"
+			else "debit_in_account_currency"
+		)
 
 		entry_list = []
 		dr_or_cr_notes = []
-		for row in self.get('allocation'):
+		for row in self.get("allocation"):
 			reconciled_entry = []
 			if row.invoice_number and row.allocated_amount:
-				if row.reference_type in ['Sales Invoice', 'Purchase Invoice']:
+				if row.reference_type in ["Sales Invoice", "Purchase Invoice"]:
 					reconciled_entry = dr_or_cr_notes
 				else:
 					reconciled_entry = entry_list
@@ -233,23 +285,25 @@
 		self.get_unreconciled_entries()
 
 	def get_payment_details(self, row, dr_or_cr):
-		return frappe._dict({
-			'voucher_type': row.get('reference_type'),
-			'voucher_no' : row.get('reference_name'),
-			'voucher_detail_no' : row.get('reference_row'),
-			'against_voucher_type' : row.get('invoice_type'),
-			'against_voucher'  : row.get('invoice_number'),
-			'account' : self.receivable_payable_account,
-			'party_type': self.party_type,
-			'party': self.party,
-			'is_advance' : row.get('is_advance'),
-			'dr_or_cr' : dr_or_cr,
-			'unreconciled_amount': flt(row.get('unreconciled_amount')),
-			'unadjusted_amount' : flt(row.get('amount')),
-			'allocated_amount' : flt(row.get('allocated_amount')),
-			'difference_amount': flt(row.get('difference_amount')),
-			'difference_account': row.get('difference_account')
-		})
+		return frappe._dict(
+			{
+				"voucher_type": row.get("reference_type"),
+				"voucher_no": row.get("reference_name"),
+				"voucher_detail_no": row.get("reference_row"),
+				"against_voucher_type": row.get("invoice_type"),
+				"against_voucher": row.get("invoice_number"),
+				"account": self.receivable_payable_account,
+				"party_type": self.party_type,
+				"party": self.party,
+				"is_advance": row.get("is_advance"),
+				"dr_or_cr": dr_or_cr,
+				"unreconciled_amount": flt(row.get("unreconciled_amount")),
+				"unadjusted_amount": flt(row.get("amount")),
+				"allocated_amount": flt(row.get("allocated_amount")),
+				"difference_amount": flt(row.get("difference_amount")),
+				"difference_account": row.get("difference_account"),
+			}
+		)
 
 	def check_mandatory_to_fetch(self):
 		for fieldname in ["company", "party_type", "party", "receivable_payable_account"]:
@@ -267,7 +321,9 @@
 		unreconciled_invoices = frappe._dict()
 
 		for inv in self.get("invoices"):
-			unreconciled_invoices.setdefault(inv.invoice_type, {}).setdefault(inv.invoice_number, inv.outstanding_amount)
+			unreconciled_invoices.setdefault(inv.invoice_type, {}).setdefault(
+				inv.invoice_number, inv.outstanding_amount
+			)
 
 		invoices_to_reconcile = []
 		for row in self.get("allocation"):
@@ -275,93 +331,121 @@
 				invoices_to_reconcile.append(row.invoice_number)
 
 				if flt(row.amount) - flt(row.allocated_amount) < 0:
-					frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equal to remaining payment amount {2}")
-						.format(row.idx, row.allocated_amount, row.amount))
+					frappe.throw(
+						_(
+							"Row {0}: Allocated amount {1} must be less than or equal to remaining payment amount {2}"
+						).format(row.idx, row.allocated_amount, row.amount)
+					)
 
 				invoice_outstanding = unreconciled_invoices.get(row.invoice_type, {}).get(row.invoice_number)
 				if flt(row.allocated_amount) - invoice_outstanding > 0.009:
-					frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equal to invoice outstanding amount {2}")
-						.format(row.idx, row.allocated_amount, invoice_outstanding))
+					frappe.throw(
+						_(
+							"Row {0}: Allocated amount {1} must be less than or equal to invoice outstanding amount {2}"
+						).format(row.idx, row.allocated_amount, invoice_outstanding)
+					)
 
 		if not invoices_to_reconcile:
 			frappe.throw(_("No records found in Allocation table"))
 
-	def get_conditions(self, get_invoices=False, get_payments=False, get_return_invoices=False):
-		condition = " and company = '{0}' ".format(self.company)
+	def build_qb_filter_conditions(self, get_invoices=False, get_return_invoices=False):
+		self.common_filter_conditions.clear()
+		ple = qb.DocType("Payment Ledger Entry")
+
+		self.common_filter_conditions.append(ple.company == self.company)
+
+		if self.get("cost_center") and (get_invoices or get_return_invoices):
+			self.common_filter_conditions.append(ple.cost_center == self.cost_center)
 
 		if get_invoices:
-			condition += " and posting_date >= {0}".format(frappe.db.escape(self.from_invoice_date)) if self.from_invoice_date else ""
-			condition += " and posting_date <= {0}".format(frappe.db.escape(self.to_invoice_date)) if self.to_invoice_date else ""
-			dr_or_cr = ("debit_in_account_currency" if erpnext.get_party_account_type(self.party_type) == 'Receivable'
-				else "credit_in_account_currency")
-
-			if self.minimum_invoice_amount:
-				condition += " and `{0}` >= {1}".format(dr_or_cr, flt(self.minimum_invoice_amount))
-			if self.maximum_invoice_amount:
-				condition += " and `{0}` <= {1}".format(dr_or_cr, flt(self.maximum_invoice_amount))
+			if self.from_invoice_date:
+				self.common_filter_conditions.append(ple.posting_date.gte(self.from_invoice_date))
+			if self.to_invoice_date:
+				self.common_filter_conditions.append(ple.posting_date.lte(self.to_invoice_date))
 
 		elif get_return_invoices:
-			condition = " and doc.company = '{0}' ".format(self.company)
-			condition += " and doc.posting_date >= {0}".format(frappe.db.escape(self.from_payment_date)) if self.from_payment_date else ""
-			condition += " and doc.posting_date <= {0}".format(frappe.db.escape(self.to_payment_date)) if self.to_payment_date else ""
-			dr_or_cr = ("gl.debit_in_account_currency" if erpnext.get_party_account_type(self.party_type) == 'Receivable'
-				else "gl.credit_in_account_currency")
+			if self.from_payment_date:
+				self.common_filter_conditions.append(ple.posting_date.gte(self.from_payment_date))
+			if self.to_payment_date:
+				self.common_filter_conditions.append(ple.posting_date.lte(self.to_payment_date))
 
-			if self.minimum_invoice_amount:
-				condition += " and `{0}` >= {1}".format(dr_or_cr, flt(self.minimum_payment_amount))
-			if self.maximum_invoice_amount:
-				condition += " and `{0}` <= {1}".format(dr_or_cr, flt(self.maximum_payment_amount))
+	def get_conditions(self, get_payments=False):
+		condition = " and company = '{0}' ".format(self.company)
 
-		else:
-			condition += " and posting_date >= {0}".format(frappe.db.escape(self.from_payment_date)) if self.from_payment_date else ""
-			condition += " and posting_date <= {0}".format(frappe.db.escape(self.to_payment_date)) if self.to_payment_date else ""
+		if self.get("cost_center") and get_payments:
+			condition = " and cost_center = '{0}' ".format(self.cost_center)
 
-			if self.minimum_payment_amount:
-				condition += " and unallocated_amount >= {0}".format(flt(self.minimum_payment_amount)) if get_payments \
-					else " and total_debit >= {0}".format(flt(self.minimum_payment_amount))
-			if self.maximum_payment_amount:
-				condition += " and unallocated_amount <= {0}".format(flt(self.maximum_payment_amount)) if get_payments \
-					else " and total_debit <= {0}".format(flt(self.maximum_payment_amount))
+		condition += (
+			" and posting_date >= {0}".format(frappe.db.escape(self.from_payment_date))
+			if self.from_payment_date
+			else ""
+		)
+		condition += (
+			" and posting_date <= {0}".format(frappe.db.escape(self.to_payment_date))
+			if self.to_payment_date
+			else ""
+		)
+
+		if self.minimum_payment_amount:
+			condition += (
+				" and unallocated_amount >= {0}".format(flt(self.minimum_payment_amount))
+				if get_payments
+				else " and total_debit >= {0}".format(flt(self.minimum_payment_amount))
+			)
+		if self.maximum_payment_amount:
+			condition += (
+				" and unallocated_amount <= {0}".format(flt(self.maximum_payment_amount))
+				if get_payments
+				else " and total_debit <= {0}".format(flt(self.maximum_payment_amount))
+			)
 
 		return condition
 
+
 def reconcile_dr_cr_note(dr_cr_notes, company):
 	for inv in dr_cr_notes:
-		voucher_type = ('Credit Note'
-			if inv.voucher_type == 'Sales Invoice' else 'Debit Note')
+		voucher_type = "Credit Note" if inv.voucher_type == "Sales Invoice" else "Debit Note"
 
-		reconcile_dr_or_cr = ('debit_in_account_currency'
-			if inv.dr_or_cr == 'credit_in_account_currency' else 'credit_in_account_currency')
+		reconcile_dr_or_cr = (
+			"debit_in_account_currency"
+			if inv.dr_or_cr == "credit_in_account_currency"
+			else "credit_in_account_currency"
+		)
 
 		company_currency = erpnext.get_company_currency(company)
 
-		jv = frappe.get_doc({
-			"doctype": "Journal Entry",
-			"voucher_type": voucher_type,
-			"posting_date": today(),
-			"company": company,
-			"multi_currency": 1 if inv.currency != company_currency else 0,
-			"accounts": [
-				{
-					'account': inv.account,
-					'party': inv.party,
-					'party_type': inv.party_type,
-					inv.dr_or_cr: abs(inv.allocated_amount),
-					'reference_type': inv.against_voucher_type,
-					'reference_name': inv.against_voucher,
-					'cost_center': erpnext.get_default_cost_center(company)
-				},
-				{
-					'account': inv.account,
-					'party': inv.party,
-					'party_type': inv.party_type,
-					reconcile_dr_or_cr: (abs(inv.allocated_amount)
-						if abs(inv.unadjusted_amount) > abs(inv.allocated_amount) else abs(inv.unadjusted_amount)),
-					'reference_type': inv.voucher_type,
-					'reference_name': inv.voucher_no,
-					'cost_center': erpnext.get_default_cost_center(company)
-				}
-			]
-		})
+		jv = frappe.get_doc(
+			{
+				"doctype": "Journal Entry",
+				"voucher_type": voucher_type,
+				"posting_date": today(),
+				"company": company,
+				"multi_currency": 1 if inv.currency != company_currency else 0,
+				"accounts": [
+					{
+						"account": inv.account,
+						"party": inv.party,
+						"party_type": inv.party_type,
+						inv.dr_or_cr: abs(inv.allocated_amount),
+						"reference_type": inv.against_voucher_type,
+						"reference_name": inv.against_voucher,
+						"cost_center": erpnext.get_default_cost_center(company),
+					},
+					{
+						"account": inv.account,
+						"party": inv.party,
+						"party_type": inv.party_type,
+						reconcile_dr_or_cr: (
+							abs(inv.allocated_amount)
+							if abs(inv.unadjusted_amount) > abs(inv.allocated_amount)
+							else abs(inv.unadjusted_amount)
+						),
+						"reference_type": inv.voucher_type,
+						"reference_name": inv.voucher_no,
+						"cost_center": erpnext.get_default_cost_center(company),
+					},
+				],
+			}
+		)
 		jv.flags.ignore_mandatory = True
 		jv.submit()
diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
index 2271f48..575ac74 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
@@ -1,9 +1,456 @@
 # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 
-# import frappe
 import unittest
 
+import frappe
+from frappe import qb
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import add_days, nowdate
 
-class TestPaymentReconciliation(unittest.TestCase):
-	pass
+from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+from erpnext.accounts.party import get_party_account
+from erpnext.stock.doctype.item.test_item import create_item
+
+
+class TestPaymentReconciliation(FrappeTestCase):
+	def setUp(self):
+		self.create_company()
+		self.create_item()
+		self.create_customer()
+		self.clear_old_entries()
+
+	def tearDown(self):
+		frappe.db.rollback()
+
+	def create_company(self):
+		company = None
+		if frappe.db.exists("Company", "_Test Payment Reconciliation"):
+			company = frappe.get_doc("Company", "_Test Payment Reconciliation")
+		else:
+			company = frappe.get_doc(
+				{
+					"doctype": "Company",
+					"company_name": "_Test Payment Reconciliation",
+					"country": "India",
+					"default_currency": "INR",
+					"create_chart_of_accounts_based_on": "Standard Template",
+					"chart_of_accounts": "Standard",
+				}
+			)
+			company = company.save()
+
+		self.company = company.name
+		self.cost_center = company.cost_center
+		self.warehouse = "All Warehouses - _PR"
+		self.income_account = "Sales - _PR"
+		self.expense_account = "Cost of Goods Sold - _PR"
+		self.debit_to = "Debtors - _PR"
+		self.creditors = "Creditors - _PR"
+
+		# create bank account
+		if frappe.db.exists("Account", "HDFC - _PR"):
+			self.bank = "HDFC - _PR"
+		else:
+			bank_acc = frappe.get_doc(
+				{
+					"doctype": "Account",
+					"account_name": "HDFC",
+					"parent_account": "Bank Accounts - _PR",
+					"company": self.company,
+				}
+			)
+			bank_acc.save()
+			self.bank = bank_acc.name
+
+	def create_item(self):
+		item = create_item(
+			item_code="_Test PR Item", is_stock_item=0, company=self.company, warehouse=self.warehouse
+		)
+		self.item = item if isinstance(item, str) else item.item_code
+
+	def create_customer(self):
+		if frappe.db.exists("Customer", "_Test PR Customer"):
+			self.customer = "_Test PR Customer"
+		else:
+			customer = frappe.new_doc("Customer")
+			customer.customer_name = "_Test PR Customer"
+			customer.type = "Individual"
+			customer.save()
+			self.customer = customer.name
+
+		if frappe.db.exists("Customer", "_Test PR Customer 2"):
+			self.customer2 = "_Test PR Customer 2"
+		else:
+			customer = frappe.new_doc("Customer")
+			customer.customer_name = "_Test PR Customer 2"
+			customer.type = "Individual"
+			customer.save()
+			self.customer2 = customer.name
+
+	def create_sales_invoice(
+		self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False
+	):
+		"""
+		Helper function to populate default values in sales invoice
+		"""
+		sinv = create_sales_invoice(
+			qty=qty,
+			rate=rate,
+			company=self.company,
+			customer=self.customer,
+			item_code=self.item,
+			item_name=self.item,
+			cost_center=self.cost_center,
+			warehouse=self.warehouse,
+			debit_to=self.debit_to,
+			parent_cost_center=self.cost_center,
+			update_stock=0,
+			currency="INR",
+			is_pos=0,
+			is_return=0,
+			return_against=None,
+			income_account=self.income_account,
+			expense_account=self.expense_account,
+			do_not_save=do_not_save,
+			do_not_submit=do_not_submit,
+		)
+		return sinv
+
+	def create_payment_entry(self, amount=100, posting_date=nowdate()):
+		"""
+		Helper function to populate default values in payment entry
+		"""
+		payment = create_payment_entry(
+			company=self.company,
+			payment_type="Receive",
+			party_type="Customer",
+			party=self.customer,
+			paid_from=self.debit_to,
+			paid_to=self.bank,
+			paid_amount=amount,
+		)
+		payment.posting_date = posting_date
+		return payment
+
+	def clear_old_entries(self):
+		doctype_list = [
+			"GL Entry",
+			"Payment Ledger Entry",
+			"Sales Invoice",
+			"Purchase Invoice",
+			"Payment Entry",
+			"Journal Entry",
+		]
+		for doctype in doctype_list:
+			qb.from_(qb.DocType(doctype)).delete().where(qb.DocType(doctype).company == self.company).run()
+
+	def create_payment_reconciliation(self):
+		pr = frappe.new_doc("Payment Reconciliation")
+		pr.company = self.company
+		pr.party_type = "Customer"
+		pr.party = self.customer
+		pr.receivable_payable_account = get_party_account(pr.party_type, pr.party, pr.company)
+		pr.from_invoice_date = pr.to_invoice_date = pr.from_payment_date = pr.to_payment_date = nowdate()
+		return pr
+
+	def create_journal_entry(
+		self, acc1=None, acc2=None, amount=0, posting_date=None, cost_center=None
+	):
+		je = frappe.new_doc("Journal Entry")
+		je.posting_date = posting_date or nowdate()
+		je.company = self.company
+		je.user_remark = "test"
+		if not cost_center:
+			cost_center = self.cost_center
+		je.set(
+			"accounts",
+			[
+				{
+					"account": acc1,
+					"cost_center": cost_center,
+					"debit_in_account_currency": amount if amount > 0 else 0,
+					"credit_in_account_currency": abs(amount) if amount < 0 else 0,
+				},
+				{
+					"account": acc2,
+					"cost_center": cost_center,
+					"credit_in_account_currency": amount if amount > 0 else 0,
+					"debit_in_account_currency": abs(amount) if amount < 0 else 0,
+				},
+			],
+		)
+		return je
+
+	def test_filter_min_max(self):
+		# check filter condition minimum and maximum amount
+		self.create_sales_invoice(qty=1, rate=300)
+		self.create_sales_invoice(qty=1, rate=400)
+		self.create_sales_invoice(qty=1, rate=500)
+		self.create_payment_entry(amount=300).save().submit()
+		self.create_payment_entry(amount=400).save().submit()
+		self.create_payment_entry(amount=500).save().submit()
+
+		pr = self.create_payment_reconciliation()
+		pr.minimum_invoice_amount = 400
+		pr.maximum_invoice_amount = 500
+		pr.minimum_payment_amount = 300
+		pr.maximum_payment_amount = 600
+		pr.get_unreconciled_entries()
+		self.assertEqual(len(pr.get("invoices")), 2)
+		self.assertEqual(len(pr.get("payments")), 3)
+
+		pr.minimum_invoice_amount = 300
+		pr.maximum_invoice_amount = 600
+		pr.minimum_payment_amount = 400
+		pr.maximum_payment_amount = 500
+		pr.get_unreconciled_entries()
+		self.assertEqual(len(pr.get("invoices")), 3)
+		self.assertEqual(len(pr.get("payments")), 2)
+
+		pr.minimum_invoice_amount = (
+			pr.maximum_invoice_amount
+		) = pr.minimum_payment_amount = pr.maximum_payment_amount = 0
+		pr.get_unreconciled_entries()
+		self.assertEqual(len(pr.get("invoices")), 3)
+		self.assertEqual(len(pr.get("payments")), 3)
+
+	def test_filter_posting_date(self):
+		# check filter condition using transaction date
+		date1 = nowdate()
+		date2 = add_days(nowdate(), -1)
+		amount = 100
+		self.create_sales_invoice(qty=1, rate=amount, posting_date=date1)
+		si2 = self.create_sales_invoice(
+			qty=1, rate=amount, posting_date=date2, do_not_save=True, do_not_submit=True
+		)
+		si2.set_posting_time = 1
+		si2.posting_date = date2
+		si2.save().submit()
+		self.create_payment_entry(amount=amount, posting_date=date1).save().submit()
+		self.create_payment_entry(amount=amount, posting_date=date2).save().submit()
+
+		pr = self.create_payment_reconciliation()
+		pr.from_invoice_date = pr.to_invoice_date = date1
+		pr.from_payment_date = pr.to_payment_date = date1
+
+		pr.get_unreconciled_entries()
+		# assert only si and pe are fetched
+		self.assertEqual(len(pr.get("invoices")), 1)
+		self.assertEqual(len(pr.get("payments")), 1)
+
+		pr.from_invoice_date = date2
+		pr.to_invoice_date = date1
+		pr.from_payment_date = date2
+		pr.to_payment_date = date1
+
+		pr.get_unreconciled_entries()
+		# assert only si and pe are fetched
+		self.assertEqual(len(pr.get("invoices")), 2)
+		self.assertEqual(len(pr.get("payments")), 2)
+
+	def test_filter_invoice_limit(self):
+		# check filter condition - invoice limit
+		transaction_date = nowdate()
+		rate = 100
+		invoices = []
+		payments = []
+		for i in range(5):
+			invoices.append(self.create_sales_invoice(qty=1, rate=rate, posting_date=transaction_date))
+			pe = self.create_payment_entry(amount=rate, posting_date=transaction_date).save().submit()
+			payments.append(pe)
+
+		pr = self.create_payment_reconciliation()
+		pr.from_invoice_date = pr.to_invoice_date = transaction_date
+		pr.from_payment_date = pr.to_payment_date = transaction_date
+		pr.invoice_limit = 2
+		pr.payment_limit = 3
+		pr.get_unreconciled_entries()
+
+		self.assertEqual(len(pr.get("invoices")), 2)
+		self.assertEqual(len(pr.get("payments")), 3)
+
+	def test_payment_against_invoice(self):
+		si = self.create_sales_invoice(qty=1, rate=200)
+		pe = self.create_payment_entry(amount=55).save().submit()
+		# second payment entry
+		self.create_payment_entry(amount=35).save().submit()
+
+		pr = self.create_payment_reconciliation()
+
+		# reconcile multiple payments against invoice
+		pr.get_unreconciled_entries()
+		invoices = [x.as_dict() for x in pr.get("invoices")]
+		payments = [x.as_dict() for x in pr.get("payments")]
+		pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+		pr.reconcile()
+
+		si.reload()
+		self.assertEqual(si.status, "Partly Paid")
+		# check PR tool output post reconciliation
+		self.assertEqual(len(pr.get("invoices")), 1)
+		self.assertEqual(pr.get("invoices")[0].get("outstanding_amount"), 110)
+		self.assertEqual(pr.get("payments"), [])
+
+		# cancel one PE
+		pe.reload()
+		pe.cancel()
+		pr.get_unreconciled_entries()
+		# check PR tool output
+		self.assertEqual(len(pr.get("invoices")), 1)
+		self.assertEqual(len(pr.get("payments")), 0)
+		self.assertEqual(pr.get("invoices")[0].get("outstanding_amount"), 165)
+
+	def test_payment_against_journal(self):
+		transaction_date = nowdate()
+
+		sales = "Sales - _PR"
+		amount = 921
+		# debit debtors account to record an invoice
+		je = self.create_journal_entry(self.debit_to, sales, amount, transaction_date)
+		je.accounts[0].party_type = "Customer"
+		je.accounts[0].party = self.customer
+		je.save()
+		je.submit()
+
+		self.create_payment_entry(amount=amount, posting_date=transaction_date).save().submit()
+
+		pr = self.create_payment_reconciliation()
+		pr.minimum_invoice_amount = pr.maximum_invoice_amount = amount
+		pr.from_invoice_date = pr.to_invoice_date = transaction_date
+		pr.from_payment_date = pr.to_payment_date = transaction_date
+
+		pr.get_unreconciled_entries()
+		invoices = [x.as_dict() for x in pr.get("invoices")]
+		payments = [x.as_dict() for x in pr.get("payments")]
+		pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+		pr.reconcile()
+
+		# check PR tool output
+		self.assertEqual(len(pr.get("invoices")), 0)
+		self.assertEqual(len(pr.get("payments")), 0)
+
+	def test_journal_against_invoice(self):
+		transaction_date = nowdate()
+		amount = 100
+		si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
+
+		# credit debtors account to record a payment
+		je = self.create_journal_entry(self.bank, self.debit_to, amount, transaction_date)
+		je.accounts[1].party_type = "Customer"
+		je.accounts[1].party = self.customer
+		je.save()
+		je.submit()
+
+		pr = self.create_payment_reconciliation()
+
+		pr.get_unreconciled_entries()
+		invoices = [x.as_dict() for x in pr.get("invoices")]
+		payments = [x.as_dict() for x in pr.get("payments")]
+		pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+		pr.reconcile()
+
+		# assert outstanding
+		si.reload()
+		self.assertEqual(si.status, "Paid")
+		self.assertEqual(si.outstanding_amount, 0)
+
+		# check PR tool output
+		self.assertEqual(len(pr.get("invoices")), 0)
+		self.assertEqual(len(pr.get("payments")), 0)
+
+	def test_journal_against_journal(self):
+		transaction_date = nowdate()
+		sales = "Sales - _PR"
+		amount = 100
+
+		# debit debtors account to simulate a invoice
+		je1 = self.create_journal_entry(self.debit_to, sales, amount, transaction_date)
+		je1.accounts[0].party_type = "Customer"
+		je1.accounts[0].party = self.customer
+		je1.save()
+		je1.submit()
+
+		# credit debtors account to simulate a payment
+		je2 = self.create_journal_entry(self.bank, self.debit_to, amount, transaction_date)
+		je2.accounts[1].party_type = "Customer"
+		je2.accounts[1].party = self.customer
+		je2.save()
+		je2.submit()
+
+		pr = self.create_payment_reconciliation()
+
+		pr.get_unreconciled_entries()
+		invoices = [x.as_dict() for x in pr.get("invoices")]
+		payments = [x.as_dict() for x in pr.get("payments")]
+		pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+		pr.reconcile()
+
+		self.assertEqual(pr.get("invoices"), [])
+		self.assertEqual(pr.get("payments"), [])
+
+	def test_cr_note_against_invoice(self):
+		transaction_date = nowdate()
+		amount = 100
+
+		si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
+
+		cr_note = self.create_sales_invoice(
+			qty=-1, rate=amount, posting_date=transaction_date, do_not_save=True, do_not_submit=True
+		)
+		cr_note.is_return = 1
+		cr_note = cr_note.save().submit()
+
+		pr = self.create_payment_reconciliation()
+
+		pr.get_unreconciled_entries()
+		invoices = [x.as_dict() for x in pr.get("invoices")]
+		payments = [x.as_dict() for x in pr.get("payments")]
+		pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+		pr.reconcile()
+
+		pr.get_unreconciled_entries()
+		# check reconciliation tool output
+		# reconciled invoice and credit note shouldn't show up in selection
+		self.assertEqual(pr.get("invoices"), [])
+		self.assertEqual(pr.get("payments"), [])
+
+		# assert outstanding
+		si.reload()
+		self.assertEqual(si.status, "Paid")
+		self.assertEqual(si.outstanding_amount, 0)
+
+	def test_cr_note_partial_against_invoice(self):
+		transaction_date = nowdate()
+		amount = 100
+		allocated_amount = 80
+
+		si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
+
+		cr_note = self.create_sales_invoice(
+			qty=-1, rate=amount, posting_date=transaction_date, do_not_save=True, do_not_submit=True
+		)
+		cr_note.is_return = 1
+		cr_note = cr_note.save().submit()
+
+		pr = self.create_payment_reconciliation()
+
+		pr.get_unreconciled_entries()
+		invoices = [x.as_dict() for x in pr.get("invoices")]
+		payments = [x.as_dict() for x in pr.get("payments")]
+		pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+		pr.allocation[0].allocated_amount = allocated_amount
+		pr.reconcile()
+
+		# assert outstanding
+		si.reload()
+		self.assertEqual(si.status, "Partly Paid")
+		self.assertEqual(si.outstanding_amount, 20)
+
+		pr.get_unreconciled_entries()
+		# check reconciliation tool output
+		self.assertEqual(len(pr.get("invoices")), 1)
+		self.assertEqual(len(pr.get("payments")), 1)
+		self.assertEqual(pr.get("invoices")[0].outstanding_amount, 20)
+		self.assertEqual(pr.get("payments")[0].amount, 20)
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index d72d8f7..5264987 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -24,7 +24,7 @@
 class PaymentRequest(Document):
 	def validate(self):
 		if self.get("__islocal"):
-			self.status = 'Draft'
+			self.status = "Draft"
 		self.validate_reference_document()
 		self.validate_payment_request_amount()
 		self.validate_currency()
@@ -35,51 +35,67 @@
 			frappe.throw(_("To create a Payment Request reference document is required"))
 
 	def validate_payment_request_amount(self):
-		existing_payment_request_amount = \
-			get_existing_payment_request_amount(self.reference_doctype, self.reference_name)
+		existing_payment_request_amount = get_existing_payment_request_amount(
+			self.reference_doctype, self.reference_name
+		)
 
 		if existing_payment_request_amount:
 			ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
-			if (hasattr(ref_doc, "order_type") \
-					and getattr(ref_doc, "order_type") != "Shopping Cart"):
+			if hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") != "Shopping Cart":
 				ref_amount = get_amount(ref_doc, self.payment_account)
 
-				if existing_payment_request_amount + flt(self.grand_total)> ref_amount:
-					frappe.throw(_("Total Payment Request amount cannot be greater than {0} amount")
-						.format(self.reference_doctype))
+				if existing_payment_request_amount + flt(self.grand_total) > ref_amount:
+					frappe.throw(
+						_("Total Payment Request amount cannot be greater than {0} amount").format(
+							self.reference_doctype
+						)
+					)
 
 	def validate_currency(self):
 		ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
-		if self.payment_account and ref_doc.currency != frappe.db.get_value("Account", self.payment_account, "account_currency"):
+		if self.payment_account and ref_doc.currency != frappe.db.get_value(
+			"Account", self.payment_account, "account_currency"
+		):
 			frappe.throw(_("Transaction currency must be same as Payment Gateway currency"))
 
 	def validate_subscription_details(self):
 		if self.is_a_subscription:
 			amount = 0
 			for subscription_plan in self.subscription_plans:
-				payment_gateway = frappe.db.get_value("Subscription Plan", subscription_plan.plan, "payment_gateway")
+				payment_gateway = frappe.db.get_value(
+					"Subscription Plan", subscription_plan.plan, "payment_gateway"
+				)
 				if payment_gateway != self.payment_gateway_account:
-					frappe.throw(_('The payment gateway account in plan {0} is different from the payment gateway account in this payment request').format(subscription_plan.name))
+					frappe.throw(
+						_(
+							"The payment gateway account in plan {0} is different from the payment gateway account in this payment request"
+						).format(subscription_plan.name)
+					)
 
 				rate = get_plan_rate(subscription_plan.plan, quantity=subscription_plan.qty)
 
 				amount += rate
 
 			if amount != self.grand_total:
-				frappe.msgprint(_("The amount of {0} set in this payment request is different from the calculated amount of all payment plans: {1}. Make sure this is correct before submitting the document.").format(self.grand_total, amount))
+				frappe.msgprint(
+					_(
+						"The amount of {0} set in this payment request is different from the calculated amount of all payment plans: {1}. Make sure this is correct before submitting the document."
+					).format(self.grand_total, amount)
+				)
 
 	def on_submit(self):
-		if self.payment_request_type == 'Outward':
-			self.db_set('status', 'Initiated')
+		if self.payment_request_type == "Outward":
+			self.db_set("status", "Initiated")
 			return
-		elif self.payment_request_type == 'Inward':
-			self.db_set('status', 'Requested')
+		elif self.payment_request_type == "Inward":
+			self.db_set("status", "Requested")
 
 		send_mail = self.payment_gateway_validation() if self.payment_gateway else None
 		ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
 
-		if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart") \
-			or self.flags.mute_email:
+		if (
+			hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart"
+		) or self.flags.mute_email:
 			send_mail = False
 
 		if send_mail and self.payment_channel != "Phone":
@@ -101,23 +117,27 @@
 			request_amount=request_amount,
 			sender=self.email_to,
 			currency=self.currency,
-			payment_gateway=self.payment_gateway
+			payment_gateway=self.payment_gateway,
 		)
 
 		controller.validate_transaction_currency(self.currency)
 		controller.request_for_payment(**payment_record)
 
 	def get_request_amount(self):
-		data_of_completed_requests = frappe.get_all("Integration Request", filters={
-			'reference_doctype': self.doctype,
-			'reference_docname': self.name,
-			'status': 'Completed'
-		}, pluck="data")
+		data_of_completed_requests = frappe.get_all(
+			"Integration Request",
+			filters={
+				"reference_doctype": self.doctype,
+				"reference_docname": self.name,
+				"status": "Completed",
+			},
+			pluck="data",
+		)
 
 		if not data_of_completed_requests:
 			return self.grand_total
 
-		request_amounts = sum(json.loads(d).get('request_amount') for d in data_of_completed_requests)
+		request_amounts = sum(json.loads(d).get("request_amount") for d in data_of_completed_requests)
 		return request_amounts
 
 	def on_cancel(self):
@@ -126,8 +146,9 @@
 
 	def make_invoice(self):
 		ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
-		if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart"):
+		if hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart":
 			from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
+
 			si = make_sales_invoice(self.reference_name, ignore_permissions=True)
 			si.allocate_advances_automatically = True
 			si = si.insert(ignore_permissions=True)
@@ -136,7 +157,7 @@
 	def payment_gateway_validation(self):
 		try:
 			controller = get_payment_gateway_controller(self.payment_gateway)
-			if hasattr(controller, 'on_payment_request_submission'):
+			if hasattr(controller, "on_payment_request_submission"):
 				return controller.on_payment_request_submission(self)
 			else:
 				return True
@@ -148,36 +169,45 @@
 			self.payment_url = self.get_payment_url()
 
 		if self.payment_url:
-			self.db_set('payment_url', self.payment_url)
+			self.db_set("payment_url", self.payment_url)
 
-		if self.payment_url or not self.payment_gateway_account \
-			or (self.payment_gateway_account and self.payment_channel == "Phone"):
-			self.db_set('status', 'Initiated')
+		if (
+			self.payment_url
+			or not self.payment_gateway_account
+			or (self.payment_gateway_account and self.payment_channel == "Phone")
+		):
+			self.db_set("status", "Initiated")
 
 	def get_payment_url(self):
 		if self.reference_doctype != "Fees":
-			data = frappe.db.get_value(self.reference_doctype, self.reference_name, ["company", "customer_name"], as_dict=1)
+			data = frappe.db.get_value(
+				self.reference_doctype, self.reference_name, ["company", "customer_name"], as_dict=1
+			)
 		else:
-			data = frappe.db.get_value(self.reference_doctype, self.reference_name, ["student_name"], as_dict=1)
+			data = frappe.db.get_value(
+				self.reference_doctype, self.reference_name, ["student_name"], as_dict=1
+			)
 			data.update({"company": frappe.defaults.get_defaults().company})
 
 		controller = get_payment_gateway_controller(self.payment_gateway)
 		controller.validate_transaction_currency(self.currency)
 
-		if hasattr(controller, 'validate_minimum_transaction_amount'):
+		if hasattr(controller, "validate_minimum_transaction_amount"):
 			controller.validate_minimum_transaction_amount(self.currency, self.grand_total)
 
-		return controller.get_payment_url(**{
-			"amount": flt(self.grand_total, self.precision("grand_total")),
-			"title": data.company.encode("utf-8"),
-			"description": self.subject.encode("utf-8"),
-			"reference_doctype": "Payment Request",
-			"reference_docname": self.name,
-			"payer_email": self.email_to or frappe.session.user,
-			"payer_name": frappe.safe_encode(data.customer_name),
-			"order_id": self.name,
-			"currency": self.currency
-		})
+		return controller.get_payment_url(
+			**{
+				"amount": flt(self.grand_total, self.precision("grand_total")),
+				"title": data.company.encode("utf-8"),
+				"description": self.subject.encode("utf-8"),
+				"reference_doctype": "Payment Request",
+				"reference_docname": self.name,
+				"payer_email": self.email_to or frappe.session.user,
+				"payer_name": frappe.safe_encode(data.customer_name),
+				"order_id": self.name,
+				"currency": self.currency,
+			}
+		)
 
 	def set_as_paid(self):
 		if self.payment_channel == "Phone":
@@ -202,32 +232,47 @@
 		else:
 			party_account = get_party_account("Customer", ref_doc.get("customer"), ref_doc.company)
 
-		party_account_currency = ref_doc.get("party_account_currency") or get_account_currency(party_account)
+		party_account_currency = ref_doc.get("party_account_currency") or get_account_currency(
+			party_account
+		)
 
 		bank_amount = self.grand_total
-		if party_account_currency == ref_doc.company_currency and party_account_currency != self.currency:
+		if (
+			party_account_currency == ref_doc.company_currency and party_account_currency != self.currency
+		):
 			party_amount = ref_doc.base_grand_total
 		else:
 			party_amount = self.grand_total
 
-		payment_entry = get_payment_entry(self.reference_doctype, self.reference_name, party_amount=party_amount,
-			bank_account=self.payment_account, bank_amount=bank_amount)
+		payment_entry = get_payment_entry(
+			self.reference_doctype,
+			self.reference_name,
+			party_amount=party_amount,
+			bank_account=self.payment_account,
+			bank_amount=bank_amount,
+		)
 
-		payment_entry.update({
-			"reference_no": self.name,
-			"reference_date": nowdate(),
-			"remarks": "Payment Entry against {0} {1} via Payment Request {2}".format(self.reference_doctype,
-				self.reference_name, self.name)
-		})
+		payment_entry.update(
+			{
+				"reference_no": self.name,
+				"reference_date": nowdate(),
+				"remarks": "Payment Entry against {0} {1} via Payment Request {2}".format(
+					self.reference_doctype, self.reference_name, self.name
+				),
+			}
+		)
 
 		if payment_entry.difference_amount:
 			company_details = get_company_defaults(ref_doc.company)
 
-			payment_entry.append("deductions", {
-				"account": company_details.exchange_gain_loss_account,
-				"cost_center": company_details.cost_center,
-				"amount": payment_entry.difference_amount
-			})
+			payment_entry.append(
+				"deductions",
+				{
+					"account": company_details.exchange_gain_loss_account,
+					"cost_center": company_details.cost_center,
+					"amount": payment_entry.difference_amount,
+				},
+			)
 
 		if submit:
 			payment_entry.insert(ignore_permissions=True)
@@ -243,16 +288,23 @@
 			"subject": self.subject,
 			"message": self.get_message(),
 			"now": True,
-			"attachments": [frappe.attach_print(self.reference_doctype, self.reference_name,
-				file_name=self.reference_name, print_format=self.print_format)]}
-		enqueue(method=frappe.sendmail, queue='short', timeout=300, is_async=True, **email_args)
+			"attachments": [
+				frappe.attach_print(
+					self.reference_doctype,
+					self.reference_name,
+					file_name=self.reference_name,
+					print_format=self.print_format,
+				)
+			],
+		}
+		enqueue(method=frappe.sendmail, queue="short", timeout=300, is_async=True, **email_args)
 
 	def get_message(self):
 		"""return message with payment gateway link"""
 
 		context = {
 			"doc": frappe.get_doc(self.reference_doctype, self.reference_name),
-			"payment_url": self.payment_url
+			"payment_url": self.payment_url,
 		}
 
 		if self.message:
@@ -266,22 +318,26 @@
 
 	def check_if_payment_entry_exists(self):
 		if self.status == "Paid":
-			if frappe.get_all("Payment Entry Reference",
+			if frappe.get_all(
+				"Payment Entry Reference",
 				filters={"reference_name": self.reference_name, "docstatus": ["<", 2]},
 				fields=["parent"],
-				limit=1):
-				frappe.throw(_("Payment Entry already exists"), title=_('Error'))
+				limit=1,
+			):
+				frappe.throw(_("Payment Entry already exists"), title=_("Error"))
 
 	def make_communication_entry(self):
 		"""Make communication entry"""
-		comm = frappe.get_doc({
-			"doctype":"Communication",
-			"subject": self.subject,
-			"content": self.get_message(),
-			"sent_or_received": "Sent",
-			"reference_doctype": self.reference_doctype,
-			"reference_name": self.reference_name
-		})
+		comm = frappe.get_doc(
+			{
+				"doctype": "Communication",
+				"subject": self.subject,
+				"content": self.get_message(),
+				"sent_or_received": "Sent",
+				"reference_doctype": self.reference_doctype,
+				"reference_name": self.reference_name,
+			}
+		)
 		comm.insert(ignore_permissions=True)
 
 	def get_payment_success_url(self):
@@ -298,16 +354,17 @@
 			self.set_as_paid()
 
 			# if shopping cart enabled and in session
-			if (shopping_cart_settings.enabled and hasattr(frappe.local, "session")
-				and frappe.local.session.user != "Guest") and self.payment_channel != "Phone":
+			if (
+				shopping_cart_settings.enabled
+				and hasattr(frappe.local, "session")
+				and frappe.local.session.user != "Guest"
+			) and self.payment_channel != "Phone":
 
 				success_url = shopping_cart_settings.payment_success_url
 				if success_url:
-					redirect_to = ({
-						"Orders": "/orders",
-						"Invoices": "/invoices",
-						"My Account": "/me"
-					}).get(success_url, "/me")
+					redirect_to = ({"Orders": "/orders", "Invoices": "/invoices", "My Account": "/me"}).get(
+						success_url, "/me"
+					)
 				else:
 					redirect_to = get_url("/orders/{0}".format(self.reference_name))
 
@@ -317,6 +374,7 @@
 		if payment_provider == "stripe":
 			return create_stripe_subscription(gateway_controller, data)
 
+
 @frappe.whitelist(allow_guest=True)
 def make_payment_request(**args):
 	"""Make payment request"""
@@ -329,49 +387,62 @@
 	grand_total = get_amount(ref_doc, gateway_account.get("payment_account"))
 	if args.loyalty_points and args.dt == "Sales Order":
 		from erpnext.accounts.doctype.loyalty_program.loyalty_program import validate_loyalty_points
+
 		loyalty_amount = validate_loyalty_points(ref_doc, int(args.loyalty_points))
-		frappe.db.set_value("Sales Order", args.dn, "loyalty_points", int(args.loyalty_points), update_modified=False)
-		frappe.db.set_value("Sales Order", args.dn, "loyalty_amount", loyalty_amount, update_modified=False)
+		frappe.db.set_value(
+			"Sales Order", args.dn, "loyalty_points", int(args.loyalty_points), update_modified=False
+		)
+		frappe.db.set_value(
+			"Sales Order", args.dn, "loyalty_amount", loyalty_amount, update_modified=False
+		)
 		grand_total = grand_total - loyalty_amount
 
-	bank_account = (get_party_bank_account(args.get('party_type'), args.get('party'))
-		if args.get('party_type') else '')
+	bank_account = (
+		get_party_bank_account(args.get("party_type"), args.get("party"))
+		if args.get("party_type")
+		else ""
+	)
 
 	existing_payment_request = None
 	if args.order_type == "Shopping Cart":
-		existing_payment_request = frappe.db.get_value("Payment Request",
-			{"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": ("!=", 2)})
+		existing_payment_request = frappe.db.get_value(
+			"Payment Request",
+			{"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": ("!=", 2)},
+		)
 
 	if existing_payment_request:
-		frappe.db.set_value("Payment Request", existing_payment_request, "grand_total", grand_total, update_modified=False)
+		frappe.db.set_value(
+			"Payment Request", existing_payment_request, "grand_total", grand_total, update_modified=False
+		)
 		pr = frappe.get_doc("Payment Request", existing_payment_request)
 	else:
 		if args.order_type != "Shopping Cart":
-			existing_payment_request_amount = \
-				get_existing_payment_request_amount(args.dt, args.dn)
+			existing_payment_request_amount = get_existing_payment_request_amount(args.dt, args.dn)
 
 			if existing_payment_request_amount:
 				grand_total -= existing_payment_request_amount
 
 		pr = frappe.new_doc("Payment Request")
-		pr.update({
-			"payment_gateway_account": gateway_account.get("name"),
-			"payment_gateway": gateway_account.get("payment_gateway"),
-			"payment_account": gateway_account.get("payment_account"),
-			"payment_channel": gateway_account.get("payment_channel"),
-			"payment_request_type": args.get("payment_request_type"),
-			"currency": ref_doc.currency,
-			"grand_total": grand_total,
-			"mode_of_payment": args.mode_of_payment,
-			"email_to": args.recipient_id or ref_doc.owner,
-			"subject": _("Payment Request for {0}").format(args.dn),
-			"message": gateway_account.get("message") or get_dummy_message(ref_doc),
-			"reference_doctype": args.dt,
-			"reference_name": args.dn,
-			"party_type": args.get("party_type") or "Customer",
-			"party": args.get("party") or ref_doc.get("customer"),
-			"bank_account": bank_account
-		})
+		pr.update(
+			{
+				"payment_gateway_account": gateway_account.get("name"),
+				"payment_gateway": gateway_account.get("payment_gateway"),
+				"payment_account": gateway_account.get("payment_account"),
+				"payment_channel": gateway_account.get("payment_channel"),
+				"payment_request_type": args.get("payment_request_type"),
+				"currency": ref_doc.currency,
+				"grand_total": grand_total,
+				"mode_of_payment": args.mode_of_payment,
+				"email_to": args.recipient_id or ref_doc.owner,
+				"subject": _("Payment Request for {0}").format(args.dn),
+				"message": gateway_account.get("message") or get_dummy_message(ref_doc),
+				"reference_doctype": args.dt,
+				"reference_name": args.dn,
+				"party_type": args.get("party_type") or "Customer",
+				"party": args.get("party") or ref_doc.get("customer"),
+				"bank_account": bank_account,
+			}
+		)
 
 		if args.order_type == "Shopping Cart" or args.mute_email:
 			pr.flags.mute_email = True
@@ -390,6 +461,7 @@
 
 	return pr.as_dict()
 
+
 def get_amount(ref_doc, payment_account=None):
 	"""get amount based on doctype"""
 	dt = ref_doc.doctype
@@ -411,18 +483,20 @@
 	elif dt == "Fees":
 		grand_total = ref_doc.outstanding_amount
 
-	if grand_total > 0 :
+	if grand_total > 0:
 		return grand_total
 
 	else:
 		frappe.throw(_("Payment Entry is already created"))
 
+
 def get_existing_payment_request_amount(ref_dt, ref_dn):
 	"""
 	Get the existing payment request which are unpaid or partially paid for payment channel other than Phone
 	and get the summation of existing paid payment request for Phone payment channel.
 	"""
-	existing_payment_request_amount = frappe.db.sql("""
+	existing_payment_request_amount = frappe.db.sql(
+		"""
 		select sum(grand_total)
 		from `tabPayment Request`
 		where
@@ -432,10 +506,13 @@
 			and (status != 'Paid'
 			or (payment_channel = 'Phone'
 				and status = 'Paid'))
-	""", (ref_dt, ref_dn))
+	""",
+		(ref_dt, ref_dn),
+	)
 	return flt(existing_payment_request_amount[0][0]) if existing_payment_request_amount else 0
 
-def get_gateway_details(args): # nosemgrep
+
+def get_gateway_details(args):  # nosemgrep
 	"""return gateway and payment account of default payment gateway"""
 	if args.get("payment_gateway_account"):
 		return get_payment_gateway_account(args.get("payment_gateway_account"))
@@ -448,58 +525,74 @@
 
 	return gateway_account
 
+
 def get_payment_gateway_account(args):
-	return frappe.db.get_value("Payment Gateway Account", args,
+	return frappe.db.get_value(
+		"Payment Gateway Account",
+		args,
 		["name", "payment_gateway", "payment_account", "message"],
-			as_dict=1)
+		as_dict=1,
+	)
+
 
 @frappe.whitelist()
 def get_print_format_list(ref_doctype):
 	print_format_list = ["Standard"]
 
-	print_format_list.extend([p.name for p in frappe.get_all("Print Format",
-		filters={"doc_type": ref_doctype})])
+	print_format_list.extend(
+		[p.name for p in frappe.get_all("Print Format", filters={"doc_type": ref_doctype})]
+	)
 
-	return {
-		"print_format": print_format_list
-	}
+	return {"print_format": print_format_list}
+
 
 @frappe.whitelist(allow_guest=True)
 def resend_payment_email(docname):
 	return frappe.get_doc("Payment Request", docname).send_email()
 
+
 @frappe.whitelist()
 def make_payment_entry(docname):
 	doc = frappe.get_doc("Payment Request", docname)
 	return doc.create_payment_entry(submit=False).as_dict()
 
+
 def update_payment_req_status(doc, method):
 	from erpnext.accounts.doctype.payment_entry.payment_entry import get_reference_details
 
 	for ref in doc.references:
-		payment_request_name = frappe.db.get_value("Payment Request",
-			{"reference_doctype": ref.reference_doctype, "reference_name": ref.reference_name,
-			"docstatus": 1})
+		payment_request_name = frappe.db.get_value(
+			"Payment Request",
+			{
+				"reference_doctype": ref.reference_doctype,
+				"reference_name": ref.reference_name,
+				"docstatus": 1,
+			},
+		)
 
 		if payment_request_name:
-			ref_details = get_reference_details(ref.reference_doctype, ref.reference_name, doc.party_account_currency)
-			pay_req_doc = frappe.get_doc('Payment Request', payment_request_name)
+			ref_details = get_reference_details(
+				ref.reference_doctype, ref.reference_name, doc.party_account_currency
+			)
+			pay_req_doc = frappe.get_doc("Payment Request", payment_request_name)
 			status = pay_req_doc.status
 
 			if status != "Paid" and not ref_details.outstanding_amount:
-				status = 'Paid'
+				status = "Paid"
 			elif status != "Partially Paid" and ref_details.outstanding_amount != ref_details.total_amount:
-				status = 'Partially Paid'
+				status = "Partially Paid"
 			elif ref_details.outstanding_amount == ref_details.total_amount:
-				if pay_req_doc.payment_request_type == 'Outward':
-					status = 'Initiated'
-				elif pay_req_doc.payment_request_type == 'Inward':
-					status = 'Requested'
+				if pay_req_doc.payment_request_type == "Outward":
+					status = "Initiated"
+				elif pay_req_doc.payment_request_type == "Inward":
+					status = "Requested"
 
-			pay_req_doc.db_set('status', status)
+			pay_req_doc.db_set("status", status)
+
 
 def get_dummy_message(doc):
-	return frappe.render_template("""{% if doc.contact_person -%}
+	return frappe.render_template(
+		"""{% if doc.contact_person -%}
 <p>Dear {{ doc.contact_person }},</p>
 {%- else %}<p>Hello,</p>{% endif %}
 
@@ -511,12 +604,19 @@
 <p>{{ _("If you have any questions, please get back to us.") }}</p>
 
 <p>{{ _("Thank you for your business!") }}</p>
-""", dict(doc=doc, payment_url = '{{ payment_url }}'))
+""",
+		dict(doc=doc, payment_url="{{ payment_url }}"),
+	)
+
 
 @frappe.whitelist()
 def get_subscription_details(reference_doctype, reference_name):
 	if reference_doctype == "Sales Invoice":
-		subscriptions = frappe.db.sql("""SELECT parent as sub_name FROM `tabSubscription Invoice` WHERE invoice=%s""",reference_name, as_dict=1)
+		subscriptions = frappe.db.sql(
+			"""SELECT parent as sub_name FROM `tabSubscription Invoice` WHERE invoice=%s""",
+			reference_name,
+			as_dict=1,
+		)
 		subscription_plans = []
 		for subscription in subscriptions:
 			plans = frappe.get_doc("Subscription", subscription.sub_name).plans
@@ -524,38 +624,50 @@
 				subscription_plans.append(plan)
 		return subscription_plans
 
+
 @frappe.whitelist()
 def make_payment_order(source_name, target_doc=None):
 	from frappe.model.mapper import get_mapped_doc
+
 	def set_missing_values(source, target):
 		target.payment_order_type = "Payment Request"
-		target.append('references', {
-			'reference_doctype': source.reference_doctype,
-			'reference_name': source.reference_name,
-			'amount': source.grand_total,
-			'supplier': source.party,
-			'payment_request': source_name,
-			'mode_of_payment': source.mode_of_payment,
-			'bank_account': source.bank_account,
-			'account': source.account
-		})
+		target.append(
+			"references",
+			{
+				"reference_doctype": source.reference_doctype,
+				"reference_name": source.reference_name,
+				"amount": source.grand_total,
+				"supplier": source.party,
+				"payment_request": source_name,
+				"mode_of_payment": source.mode_of_payment,
+				"bank_account": source.bank_account,
+				"account": source.account,
+			},
+		)
 
-	doclist = get_mapped_doc("Payment Request", source_name,	{
-		"Payment Request": {
-			"doctype": "Payment Order",
-		}
-	}, target_doc, set_missing_values)
+	doclist = get_mapped_doc(
+		"Payment Request",
+		source_name,
+		{
+			"Payment Request": {
+				"doctype": "Payment Order",
+			}
+		},
+		target_doc,
+		set_missing_values,
+	)
 
 	return doclist
 
+
 def validate_payment(doc, method=None):
 	if doc.reference_doctype != "Payment Request" or (
-		frappe.db.get_value(doc.reference_doctype, doc.reference_docname, 'status')
-		!= "Paid"
+		frappe.db.get_value(doc.reference_doctype, doc.reference_docname, "status") != "Paid"
 	):
 		return
 
 	frappe.throw(
-		_("The Payment Request {0} is already paid, cannot process payment twice")
-		.format(doc.reference_docname)
+		_("The Payment Request {0} is already paid, cannot process payment twice").format(
+			doc.reference_docname
+		)
 	)
diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py
index f679ccf..477c726 100644
--- a/erpnext/accounts/doctype/payment_request/test_payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py
@@ -12,10 +12,7 @@
 
 test_dependencies = ["Currency Exchange", "Journal Entry", "Contact", "Address"]
 
-payment_gateway = {
-	"doctype": "Payment Gateway",
-	"gateway": "_Test Gateway"
-}
+payment_gateway = {"doctype": "Payment Gateway", "gateway": "_Test Gateway"}
 
 payment_method = [
 	{
@@ -23,30 +20,38 @@
 		"is_default": 1,
 		"payment_gateway": "_Test Gateway",
 		"payment_account": "_Test Bank - _TC",
-		"currency": "INR"
+		"currency": "INR",
 	},
 	{
 		"doctype": "Payment Gateway Account",
 		"payment_gateway": "_Test Gateway",
 		"payment_account": "_Test Bank USD - _TC",
-		"currency": "USD"
-	}
+		"currency": "USD",
+	},
 ]
 
+
 class TestPaymentRequest(unittest.TestCase):
 	def setUp(self):
 		if not frappe.db.get_value("Payment Gateway", payment_gateway["gateway"], "name"):
 			frappe.get_doc(payment_gateway).insert(ignore_permissions=True)
 
 		for method in payment_method:
-			if not frappe.db.get_value("Payment Gateway Account", {"payment_gateway": method["payment_gateway"],
-				"currency": method["currency"]}, "name"):
+			if not frappe.db.get_value(
+				"Payment Gateway Account",
+				{"payment_gateway": method["payment_gateway"], "currency": method["currency"]},
+				"name",
+			):
 				frappe.get_doc(method).insert(ignore_permissions=True)
 
 	def test_payment_request_linkings(self):
 		so_inr = make_sales_order(currency="INR")
-		pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com",
-			payment_gateway_account="_Test Gateway - INR")
+		pr = make_payment_request(
+			dt="Sales Order",
+			dn=so_inr.name,
+			recipient_id="saurabh@erpnext.com",
+			payment_gateway_account="_Test Gateway - INR",
+		)
 
 		self.assertEqual(pr.reference_doctype, "Sales Order")
 		self.assertEqual(pr.reference_name, so_inr.name)
@@ -55,45 +60,75 @@
 		conversion_rate = get_exchange_rate("USD", "INR")
 
 		si_usd = create_sales_invoice(currency="USD", conversion_rate=conversion_rate)
-		pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com",
-			payment_gateway_account="_Test Gateway - USD")
+		pr = make_payment_request(
+			dt="Sales Invoice",
+			dn=si_usd.name,
+			recipient_id="saurabh@erpnext.com",
+			payment_gateway_account="_Test Gateway - USD",
+		)
 
 		self.assertEqual(pr.reference_doctype, "Sales Invoice")
 		self.assertEqual(pr.reference_name, si_usd.name)
 		self.assertEqual(pr.currency, "USD")
 
 	def test_payment_entry(self):
-		frappe.db.set_value("Company", "_Test Company",
-			"exchange_gain_loss_account", "_Test Exchange Gain/Loss - _TC")
+		frappe.db.set_value(
+			"Company", "_Test Company", "exchange_gain_loss_account", "_Test Exchange Gain/Loss - _TC"
+		)
 		frappe.db.set_value("Company", "_Test Company", "write_off_account", "_Test Write Off - _TC")
 		frappe.db.set_value("Company", "_Test Company", "cost_center", "_Test Cost Center - _TC")
 
 		so_inr = make_sales_order(currency="INR")
-		pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com",
-			mute_email=1, payment_gateway_account="_Test Gateway - INR", submit_doc=1, return_doc=1)
+		pr = make_payment_request(
+			dt="Sales Order",
+			dn=so_inr.name,
+			recipient_id="saurabh@erpnext.com",
+			mute_email=1,
+			payment_gateway_account="_Test Gateway - INR",
+			submit_doc=1,
+			return_doc=1,
+		)
 		pe = pr.set_as_paid()
 
 		so_inr = frappe.get_doc("Sales Order", so_inr.name)
 
 		self.assertEqual(so_inr.advance_paid, 1000)
 
-		si_usd = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
-			currency="USD", conversion_rate=50)
+		si_usd = create_sales_invoice(
+			customer="_Test Customer USD",
+			debit_to="_Test Receivable USD - _TC",
+			currency="USD",
+			conversion_rate=50,
+		)
 
-		pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com",
-			mute_email=1, payment_gateway_account="_Test Gateway - USD", submit_doc=1, return_doc=1)
+		pr = make_payment_request(
+			dt="Sales Invoice",
+			dn=si_usd.name,
+			recipient_id="saurabh@erpnext.com",
+			mute_email=1,
+			payment_gateway_account="_Test Gateway - USD",
+			submit_doc=1,
+			return_doc=1,
+		)
 
 		pe = pr.set_as_paid()
 
-		expected_gle = dict((d[0], d) for d in [
-			["_Test Receivable USD - _TC", 0, 5000, si_usd.name],
-			[pr.payment_account, 6290.0, 0, None],
-			["_Test Exchange Gain/Loss - _TC", 0, 1290, None]
-		])
+		expected_gle = dict(
+			(d[0], d)
+			for d in [
+				["_Test Receivable USD - _TC", 0, 5000, si_usd.name],
+				[pr.payment_account, 6290.0, 0, None],
+				["_Test Exchange Gain/Loss - _TC", 0, 1290, None],
+			]
+		)
 
-		gl_entries = frappe.db.sql("""select account, debit, credit, against_voucher
+		gl_entries = frappe.db.sql(
+			"""select account, debit, credit, against_voucher
 			from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
-			order by account asc""", pe.name, as_dict=1)
+			order by account asc""",
+			pe.name,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
@@ -104,35 +139,48 @@
 			self.assertEqual(expected_gle[gle.account][3], gle.against_voucher)
 
 	def test_status(self):
-		si_usd = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
-			currency="USD", conversion_rate=50)
+		si_usd = create_sales_invoice(
+			customer="_Test Customer USD",
+			debit_to="_Test Receivable USD - _TC",
+			currency="USD",
+			conversion_rate=50,
+		)
 
-		pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com",
-			mute_email=1, payment_gateway_account="_Test Gateway - USD", submit_doc=1, return_doc=1)
+		pr = make_payment_request(
+			dt="Sales Invoice",
+			dn=si_usd.name,
+			recipient_id="saurabh@erpnext.com",
+			mute_email=1,
+			payment_gateway_account="_Test Gateway - USD",
+			submit_doc=1,
+			return_doc=1,
+		)
 
 		pe = pr.create_payment_entry()
 		pr.load_from_db()
 
-		self.assertEqual(pr.status, 'Paid')
+		self.assertEqual(pr.status, "Paid")
 
 		pe.cancel()
 		pr.load_from_db()
 
-		self.assertEqual(pr.status, 'Requested')
+		self.assertEqual(pr.status, "Requested")
 
 	def test_multiple_payment_entries_against_sales_order(self):
 		# Make Sales Order, grand_total = 1000
 		so = make_sales_order()
 
 		# Payment Request amount = 200
-		pr1 = make_payment_request(dt="Sales Order", dn=so.name,
-			recipient_id="nabin@erpnext.com", return_doc=1)
+		pr1 = make_payment_request(
+			dt="Sales Order", dn=so.name, recipient_id="nabin@erpnext.com", return_doc=1
+		)
 		pr1.grand_total = 200
 		pr1.submit()
 
 		# Make a 2nd Payment Request
-		pr2 = make_payment_request(dt="Sales Order", dn=so.name,
-			recipient_id="nabin@erpnext.com", return_doc=1)
+		pr2 = make_payment_request(
+			dt="Sales Order", dn=so.name, recipient_id="nabin@erpnext.com", return_doc=1
+		)
 
 		self.assertEqual(pr2.grand_total, 800)
 
diff --git a/erpnext/accounts/doctype/payment_term/payment_term_dashboard.py b/erpnext/accounts/doctype/payment_term/payment_term_dashboard.py
index ac80b79..8df97bf 100644
--- a/erpnext/accounts/doctype/payment_term/payment_term_dashboard.py
+++ b/erpnext/accounts/doctype/payment_term/payment_term_dashboard.py
@@ -3,18 +3,10 @@
 
 def get_data():
 	return {
-		'fieldname': 'payment_term',
-		'transactions': [
-			{
-				'label': _('Sales'),
-				'items': ['Sales Invoice', 'Sales Order', 'Quotation']
-			},
-			{
-				'label': _('Purchase'),
-				'items': ['Purchase Invoice', 'Purchase Order']
-			},
-			{
-				'items': ['Payment Terms Template']
-			}
-		]
+		"fieldname": "payment_term",
+		"transactions": [
+			{"label": _("Sales"), "items": ["Sales Invoice", "Sales Order", "Quotation"]},
+			{"label": _("Purchase"), "items": ["Purchase Invoice", "Purchase Order"]},
+			{"items": ["Payment Terms Template"]},
+		],
 	}
diff --git a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py
index 3a6999c..ea3b76c 100644
--- a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py
+++ b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py
@@ -16,10 +16,12 @@
 	def validate_invoice_portion(self):
 		total_portion = 0
 		for term in self.terms:
-			total_portion += flt(term.get('invoice_portion', 0))
+			total_portion += flt(term.get("invoice_portion", 0))
 
 		if flt(total_portion, 2) != 100.00:
-			frappe.msgprint(_('Combined invoice portion must equal 100%'), raise_exception=1, indicator='red')
+			frappe.msgprint(
+				_("Combined invoice portion must equal 100%"), raise_exception=1, indicator="red"
+			)
 
 	def check_duplicate_terms(self):
 		terms = []
@@ -27,8 +29,9 @@
 			term_info = (term.payment_term, term.credit_days, term.credit_months, term.due_date_based_on)
 			if term_info in terms:
 				frappe.msgprint(
-					_('The Payment Term at row {0} is possibly a duplicate.').format(term.idx),
-					raise_exception=1, indicator='red'
+					_("The Payment Term at row {0} is possibly a duplicate.").format(term.idx),
+					raise_exception=1,
+					indicator="red",
 				)
 			else:
 				terms.append(term_info)
diff --git a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template_dashboard.py b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template_dashboard.py
index 2cf7a6c..34ac773 100644
--- a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template_dashboard.py
+++ b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template_dashboard.py
@@ -3,29 +3,17 @@
 
 def get_data():
 	return {
-		'fieldname': 'payment_terms_template',
-		'non_standard_fieldnames': {
-			'Customer Group': 'payment_terms',
-			'Supplier Group': 'payment_terms',
-			'Supplier': 'payment_terms',
-			'Customer': 'payment_terms'
+		"fieldname": "payment_terms_template",
+		"non_standard_fieldnames": {
+			"Customer Group": "payment_terms",
+			"Supplier Group": "payment_terms",
+			"Supplier": "payment_terms",
+			"Customer": "payment_terms",
 		},
-		'transactions': [
-			{
-				'label': _('Sales'),
-				'items': ['Sales Invoice', 'Sales Order', 'Quotation']
-			},
-			{
-				'label': _('Purchase'),
-				'items': ['Purchase Invoice', 'Purchase Order']
-			},
-			{
-				'label': _('Party'),
-				'items': ['Customer', 'Supplier']
-			},
-			{
-				'label': _('Group'),
-				'items': ['Customer Group', 'Supplier Group']
-			}
-		]
+		"transactions": [
+			{"label": _("Sales"), "items": ["Sales Invoice", "Sales Order", "Quotation"]},
+			{"label": _("Purchase"), "items": ["Purchase Invoice", "Purchase Order"]},
+			{"label": _("Party"), "items": ["Customer", "Supplier"]},
+			{"label": _("Group"), "items": ["Customer Group", "Supplier Group"]},
+		],
 	}
diff --git a/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.py b/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.py
index 8529ef5..9717f20 100644
--- a/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.py
+++ b/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.py
@@ -8,64 +8,76 @@
 
 class TestPaymentTermsTemplate(unittest.TestCase):
 	def tearDown(self):
-		frappe.delete_doc('Payment Terms Template', '_Test Payment Terms Template For Test', force=1)
+		frappe.delete_doc("Payment Terms Template", "_Test Payment Terms Template For Test", force=1)
 
 	def test_create_template(self):
-		template = frappe.get_doc({
-			'doctype': 'Payment Terms Template',
-			'template_name': '_Test Payment Terms Template For Test',
-			'terms': [{
-				'doctype': 'Payment Terms Template Detail',
-				'invoice_portion': 50.00,
-				'credit_days_based_on': 'Day(s) after invoice date',
-				'credit_days': 30
-			}]
-		})
+		template = frappe.get_doc(
+			{
+				"doctype": "Payment Terms Template",
+				"template_name": "_Test Payment Terms Template For Test",
+				"terms": [
+					{
+						"doctype": "Payment Terms Template Detail",
+						"invoice_portion": 50.00,
+						"credit_days_based_on": "Day(s) after invoice date",
+						"credit_days": 30,
+					}
+				],
+			}
+		)
 
 		self.assertRaises(frappe.ValidationError, template.insert)
 
-		template.append('terms', {
-			'doctype': 'Payment Terms Template Detail',
-			'invoice_portion': 50.00,
-			'credit_days_based_on': 'Day(s) after invoice date',
-			'credit_days': 0
-		})
+		template.append(
+			"terms",
+			{
+				"doctype": "Payment Terms Template Detail",
+				"invoice_portion": 50.00,
+				"credit_days_based_on": "Day(s) after invoice date",
+				"credit_days": 0,
+			},
+		)
 
 		template.insert()
 
 	def test_credit_days(self):
-		template = frappe.get_doc({
-			'doctype': 'Payment Terms Template',
-			'template_name': '_Test Payment Terms Template For Test',
-			'terms': [{
-				'doctype': 'Payment Terms Template Detail',
-				'invoice_portion': 100.00,
-				'credit_days_based_on': 'Day(s) after invoice date',
-				'credit_days': -30
-			}]
-		})
+		template = frappe.get_doc(
+			{
+				"doctype": "Payment Terms Template",
+				"template_name": "_Test Payment Terms Template For Test",
+				"terms": [
+					{
+						"doctype": "Payment Terms Template Detail",
+						"invoice_portion": 100.00,
+						"credit_days_based_on": "Day(s) after invoice date",
+						"credit_days": -30,
+					}
+				],
+			}
+		)
 
 		self.assertRaises(frappe.ValidationError, template.insert)
 
 	def test_duplicate_terms(self):
-		template = frappe.get_doc({
-			'doctype': 'Payment Terms Template',
-			'template_name': '_Test Payment Terms Template For Test',
-			'terms': [
-				{
-					'doctype': 'Payment Terms Template Detail',
-					'invoice_portion': 50.00,
-					'credit_days_based_on': 'Day(s) after invoice date',
-					'credit_days': 30
-				},
-				{
-					'doctype': 'Payment Terms Template Detail',
-					'invoice_portion': 50.00,
-					'credit_days_based_on': 'Day(s) after invoice date',
-					'credit_days': 30
-				}
-
-			]
-		})
+		template = frappe.get_doc(
+			{
+				"doctype": "Payment Terms Template",
+				"template_name": "_Test Payment Terms Template For Test",
+				"terms": [
+					{
+						"doctype": "Payment Terms Template Detail",
+						"invoice_portion": 50.00,
+						"credit_days_based_on": "Day(s) after invoice date",
+						"credit_days": 30,
+					},
+					{
+						"doctype": "Payment Terms Template Detail",
+						"invoice_portion": 50.00,
+						"credit_days_based_on": "Day(s) after invoice date",
+						"credit_days": 30,
+					},
+				],
+			}
+		)
 
 		self.assertRaises(frappe.ValidationError, template.insert)
diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
index d0e555e..5a86376 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
+++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
@@ -23,40 +23,52 @@
 		self.make_gl_entries()
 
 	def on_cancel(self):
-		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+		self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
 		from erpnext.accounts.general_ledger import make_reverse_gl_entries
+
 		make_reverse_gl_entries(voucher_type="Period Closing Voucher", voucher_no=self.name)
 
 	def validate_account_head(self):
 		closing_account_type = frappe.db.get_value("Account", self.closing_account_head, "root_type")
 
 		if closing_account_type not in ["Liability", "Equity"]:
-			frappe.throw(_("Closing Account {0} must be of type Liability / Equity")
-				.format(self.closing_account_head))
+			frappe.throw(
+				_("Closing Account {0} must be of type Liability / Equity").format(self.closing_account_head)
+			)
 
 		account_currency = get_account_currency(self.closing_account_head)
-		company_currency = frappe.get_cached_value('Company',  self.company,  "default_currency")
+		company_currency = frappe.get_cached_value("Company", self.company, "default_currency")
 		if account_currency != company_currency:
 			frappe.throw(_("Currency of the Closing Account must be {0}").format(company_currency))
 
 	def validate_posting_date(self):
 		from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year
 
-		validate_fiscal_year(self.posting_date, self.fiscal_year, self.company, label=_("Posting Date"), doc=self)
+		validate_fiscal_year(
+			self.posting_date, self.fiscal_year, self.company, label=_("Posting Date"), doc=self
+		)
 
-		self.year_start_date = get_fiscal_year(self.posting_date, self.fiscal_year, company=self.company)[1]
+		self.year_start_date = get_fiscal_year(
+			self.posting_date, self.fiscal_year, company=self.company
+		)[1]
 
-		pce = frappe.db.sql("""select name from `tabPeriod Closing Voucher`
-			where posting_date > %s and fiscal_year = %s and docstatus = 1""",
-			(self.posting_date, self.fiscal_year))
+		pce = frappe.db.sql(
+			"""select name from `tabPeriod Closing Voucher`
+			where posting_date > %s and fiscal_year = %s and docstatus = 1 and company = %s""",
+			(self.posting_date, self.fiscal_year, self.company),
+		)
 		if pce and pce[0][0]:
-			frappe.throw(_("Another Period Closing Entry {0} has been made after {1}")
-				.format(pce[0][0], self.posting_date))
+			frappe.throw(
+				_("Another Period Closing Entry {0} has been made after {1}").format(
+					pce[0][0], self.posting_date
+				)
+			)
 
 	def make_gl_entries(self):
 		gl_entries = self.get_gl_entries()
 		if gl_entries:
 			from erpnext.accounts.general_ledger import make_gl_entries
+
 			make_gl_entries(gl_entries)
 
 	def get_gl_entries(self):
@@ -65,16 +77,29 @@
 
 		for acc in pl_accounts:
 			if flt(acc.bal_in_company_currency):
-				gl_entries.append(self.get_gl_dict({
-					"account": acc.account,
-					"cost_center": acc.cost_center,
-					"finance_book": acc.finance_book,
-					"account_currency": acc.account_currency,
-					"debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) < 0 else 0,
-					"debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0,
-					"credit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) > 0 else 0,
-					"credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0
-				}, item=acc))
+				gl_entries.append(
+					self.get_gl_dict(
+						{
+							"account": acc.account,
+							"cost_center": acc.cost_center,
+							"finance_book": acc.finance_book,
+							"account_currency": acc.account_currency,
+							"debit_in_account_currency": abs(flt(acc.bal_in_account_currency))
+							if flt(acc.bal_in_account_currency) < 0
+							else 0,
+							"debit": abs(flt(acc.bal_in_company_currency))
+							if flt(acc.bal_in_company_currency) < 0
+							else 0,
+							"credit_in_account_currency": abs(flt(acc.bal_in_account_currency))
+							if flt(acc.bal_in_account_currency) > 0
+							else 0,
+							"credit": abs(flt(acc.bal_in_company_currency))
+							if flt(acc.bal_in_company_currency) > 0
+							else 0,
+						},
+						item=acc,
+					)
+				)
 
 		if gl_entries:
 			gle_for_net_pl_bal = self.get_pnl_gl_entry(pl_accounts)
@@ -89,16 +114,27 @@
 		for acc in pl_accounts:
 			if flt(acc.bal_in_company_currency):
 				cost_center = acc.cost_center if self.cost_center_wise_pnl else company_cost_center
-				gl_entry = self.get_gl_dict({
-					"account": self.closing_account_head,
-					"cost_center": cost_center,
-					"finance_book": acc.finance_book,
-					"account_currency": acc.account_currency,
-					"debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) > 0 else 0,
-					"debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0,
-					"credit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) < 0 else 0,
-					"credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0
-				}, item=acc)
+				gl_entry = self.get_gl_dict(
+					{
+						"account": self.closing_account_head,
+						"cost_center": cost_center,
+						"finance_book": acc.finance_book,
+						"account_currency": acc.account_currency,
+						"debit_in_account_currency": abs(flt(acc.bal_in_account_currency))
+						if flt(acc.bal_in_account_currency) > 0
+						else 0,
+						"debit": abs(flt(acc.bal_in_company_currency))
+						if flt(acc.bal_in_company_currency) > 0
+						else 0,
+						"credit_in_account_currency": abs(flt(acc.bal_in_account_currency))
+						if flt(acc.bal_in_account_currency) < 0
+						else 0,
+						"credit": abs(flt(acc.bal_in_company_currency))
+						if flt(acc.bal_in_company_currency) < 0
+						else 0,
+					},
+					item=acc,
+				)
 
 				self.update_default_dimensions(gl_entry)
 
@@ -112,20 +148,19 @@
 
 		_, default_dimensions = get_dimensions()
 		for dimension in self.accounting_dimensions:
-			gl_entry.update({
-				dimension: default_dimensions.get(self.company, {}).get(dimension)
-			})
+			gl_entry.update({dimension: default_dimensions.get(self.company, {}).get(dimension)})
 
 	def get_pl_balances(self):
 		"""Get balance for dimension-wise pl accounts"""
 
-		dimension_fields = ['t1.cost_center', 't1.finance_book']
+		dimension_fields = ["t1.cost_center", "t1.finance_book"]
 
 		self.accounting_dimensions = get_accounting_dimensions()
 		for dimension in self.accounting_dimensions:
-			dimension_fields.append('t1.{0}'.format(dimension))
+			dimension_fields.append("t1.{0}".format(dimension))
 
-		return frappe.db.sql("""
+		return frappe.db.sql(
+			"""
 			select
 				t1.account, t2.account_currency, {dimension_fields},
 				sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as bal_in_account_currency,
@@ -135,4 +170,9 @@
 			and t2.docstatus < 2 and t2.company = %s
 			and t1.posting_date between %s and %s
 			group by t1.account, {dimension_fields}
-		""".format(dimension_fields = ', '.join(dimension_fields)), (self.company, self.get("year_start_date"), self.posting_date), as_dict=1)
+		""".format(
+				dimension_fields=", ".join(dimension_fields)
+			),
+			(self.company, self.get("year_start_date"), self.posting_date),
+			as_dict=1,
+		)
diff --git a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
index 030b4ca..3b938ea 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
+++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
@@ -2,7 +2,6 @@
 # License: GNU General Public License v3. See license.txt
 
 
-
 import unittest
 
 import frappe
@@ -19,7 +18,7 @@
 		frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
 
 		company = create_company()
-		cost_center = create_cost_center('Test Cost Center 1')
+		cost_center = create_cost_center("Test Cost Center 1")
 
 		jv1 = make_journal_entry(
 			amount=400,
@@ -27,7 +26,7 @@
 			account2="Sales - TPC",
 			cost_center=cost_center,
 			posting_date=now(),
-			save=False
+			save=False,
 		)
 		jv1.company = company
 		jv1.save()
@@ -39,7 +38,7 @@
 			account2="Cash - TPC",
 			cost_center=cost_center,
 			posting_date=now(),
-			save=False
+			save=False,
 		)
 		jv2.company = company
 		jv2.save()
@@ -49,14 +48,17 @@
 		surplus_account = pcv.closing_account_head
 
 		expected_gle = (
-			('Cost of Goods Sold - TPC', 0.0, 600.0),
+			("Cost of Goods Sold - TPC", 0.0, 600.0),
 			(surplus_account, 600.0, 400.0),
-			('Sales - TPC', 400.0, 0.0)
+			("Sales - TPC", 400.0, 0.0),
 		)
 
-		pcv_gle = frappe.db.sql("""
+		pcv_gle = frappe.db.sql(
+			"""
 			select account, debit, credit from `tabGL Entry` where voucher_no=%s order by account
-		""", (pcv.name))
+		""",
+			(pcv.name),
+		)
 
 		self.assertEqual(pcv_gle, expected_gle)
 
@@ -75,7 +77,9 @@
 			income_account="Sales - TPC",
 			expense_account="Cost of Goods Sold - TPC",
 			rate=400,
-			debit_to="Debtors - TPC"
+			debit_to="Debtors - TPC",
+			currency="USD",
+			customer="_Test Customer USD",
 		)
 		create_sales_invoice(
 			company=company,
@@ -83,7 +87,9 @@
 			income_account="Sales - TPC",
 			expense_account="Cost of Goods Sold - TPC",
 			rate=200,
-			debit_to="Debtors - TPC"
+			debit_to="Debtors - TPC",
+			currency="USD",
+			customer="_Test Customer USD",
 		)
 
 		pcv = self.make_period_closing_voucher(submit=False)
@@ -95,15 +101,18 @@
 		expected_gle = (
 			(surplus_account, 0.0, 400.0, cost_center1),
 			(surplus_account, 0.0, 200.0, cost_center2),
-			('Sales - TPC', 400.0, 0.0, cost_center1),
-			('Sales - TPC', 200.0, 0.0, cost_center2),
+			("Sales - TPC", 400.0, 0.0, cost_center1),
+			("Sales - TPC", 200.0, 0.0, cost_center2),
 		)
 
-		pcv_gle = frappe.db.sql("""
+		pcv_gle = frappe.db.sql(
+			"""
 			select account, debit, credit, cost_center
 			from `tabGL Entry` where voucher_no=%s
 			order by account, cost_center
-		""", (pcv.name))
+		""",
+			(pcv.name),
+		)
 
 		self.assertEqual(pcv_gle, expected_gle)
 
@@ -114,20 +123,23 @@
 		surplus_account = create_account()
 		cost_center = create_cost_center("Test Cost Center 1")
 
-		create_sales_invoice(
+		si = create_sales_invoice(
 			company=company,
 			income_account="Sales - TPC",
 			expense_account="Cost of Goods Sold - TPC",
 			cost_center=cost_center,
 			rate=400,
-			debit_to="Debtors - TPC"
+			debit_to="Debtors - TPC",
+			currency="USD",
+			customer="_Test Customer USD",
 		)
+
 		jv = make_journal_entry(
 			account1="Cash - TPC",
 			account2="Sales - TPC",
 			amount=400,
 			cost_center=cost_center,
-			posting_date=now()
+			posting_date=now(),
 		)
 		jv.company = company
 		jv.finance_book = create_finance_book().name
@@ -140,69 +152,84 @@
 		expected_gle = (
 			(surplus_account, 0.0, 400.0, None),
 			(surplus_account, 0.0, 400.0, jv.finance_book),
-			('Sales - TPC', 400.0, 0.0, None),
-			('Sales - TPC', 400.0, 0.0, jv.finance_book)
+			("Sales - TPC", 400.0, 0.0, None),
+			("Sales - TPC", 400.0, 0.0, jv.finance_book),
 		)
 
-		pcv_gle = frappe.db.sql("""
+		pcv_gle = frappe.db.sql(
+			"""
 			select account, debit, credit, finance_book
 			from `tabGL Entry` where voucher_no=%s
 			order by account, finance_book
-		""", (pcv.name))
+		""",
+			(pcv.name),
+		)
 
 		self.assertEqual(pcv_gle, expected_gle)
 
 	def make_period_closing_voucher(self, submit=True):
 		surplus_account = create_account()
 		cost_center = create_cost_center("Test Cost Center 1")
-		pcv = frappe.get_doc({
-			"doctype": "Period Closing Voucher",
-			"transaction_date": today(),
-			"posting_date": today(),
-			"company": "Test PCV Company",
-			"fiscal_year": get_fiscal_year(today(), company="Test PCV Company")[0],
-			"cost_center": cost_center,
-			"closing_account_head": surplus_account,
-			"remarks": "test"
-		})
+		pcv = frappe.get_doc(
+			{
+				"doctype": "Period Closing Voucher",
+				"transaction_date": today(),
+				"posting_date": today(),
+				"company": "Test PCV Company",
+				"fiscal_year": get_fiscal_year(today(), company="Test PCV Company")[0],
+				"cost_center": cost_center,
+				"closing_account_head": surplus_account,
+				"remarks": "test",
+			}
+		)
 		pcv.insert()
 		if submit:
 			pcv.submit()
 
 		return pcv
 
+
 def create_company():
-	company = frappe.get_doc({
-		'doctype': 'Company',
-		'company_name': "Test PCV Company",
-		'country': 'United States',
-		'default_currency': 'USD'
-	})
-	company.insert(ignore_if_duplicate = True)
+	company = frappe.get_doc(
+		{
+			"doctype": "Company",
+			"company_name": "Test PCV Company",
+			"country": "United States",
+			"default_currency": "USD",
+		}
+	)
+	company.insert(ignore_if_duplicate=True)
 	return company.name
 
+
 def create_account():
-	account = frappe.get_doc({
-		"account_name": "Reserve and Surplus",
-		"is_group": 0,
-		"company": "Test PCV Company",
-		"root_type": "Liability",
-		"report_type": "Balance Sheet",
-		"account_currency": "USD",
-		"parent_account": "Current Liabilities - TPC",
-		"doctype": "Account"
-	}).insert(ignore_if_duplicate = True)
+	account = frappe.get_doc(
+		{
+			"account_name": "Reserve and Surplus",
+			"is_group": 0,
+			"company": "Test PCV Company",
+			"root_type": "Liability",
+			"report_type": "Balance Sheet",
+			"account_currency": "USD",
+			"parent_account": "Current Liabilities - TPC",
+			"doctype": "Account",
+		}
+	).insert(ignore_if_duplicate=True)
 	return account.name
 
+
 def create_cost_center(cc_name):
-	costcenter = frappe.get_doc({
-		"company": "Test PCV Company",
-		"cost_center_name": cc_name,
-		"doctype": "Cost Center",
-		"parent_cost_center": "Test PCV Company - TPC"
-	})
-	costcenter.insert(ignore_if_duplicate = True)
+	costcenter = frappe.get_doc(
+		{
+			"company": "Test PCV Company",
+			"cost_center_name": cc_name,
+			"doctype": "Cost Center",
+			"parent_cost_center": "Test PCV Company - TPC",
+		}
+	)
+	costcenter.insert(ignore_if_duplicate=True)
 	return costcenter.name
 
+
 test_dependencies = ["Customer", "Cost Center"]
 test_records = frappe.get_test_records("Period Closing Voucher")
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
index 264d4a6..98f3420 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
@@ -64,13 +64,15 @@
 	pos_opening_entry(frm) {
 		if (frm.doc.pos_opening_entry && frm.doc.period_start_date && frm.doc.period_end_date && frm.doc.user) {
 			reset_values(frm);
-			frm.trigger("set_opening_amounts");
-			frm.trigger("get_pos_invoices");
+			frappe.run_serially([
+				() => frm.trigger("set_opening_amounts"),
+				() => frm.trigger("get_pos_invoices")
+			]);
 		}
 	},
 
 	set_opening_amounts(frm) {
-		frappe.db.get_doc("POS Opening Entry", frm.doc.pos_opening_entry)
+		return frappe.db.get_doc("POS Opening Entry", frm.doc.pos_opening_entry)
 			.then(({ balance_details }) => {
 				balance_details.forEach(detail => {
 					frm.add_child("payment_reconciliation", {
@@ -83,7 +85,7 @@
 	},
 
 	get_pos_invoices(frm) {
-		frappe.call({
+		return frappe.call({
 			method: 'erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry.get_pos_invoices',
 			args: {
 				start: frappe.datetime.get_datetime_as_string(frm.doc.period_start_date),
@@ -100,7 +102,9 @@
 		});
 	},
 
-	before_save: function(frm) {
+	before_save: async function(frm) {
+		frappe.dom.freeze(__('Processing Sales! Please Wait...'));
+
 		frm.set_value("grand_total", 0);
 		frm.set_value("net_total", 0);
 		frm.set_value("total_quantity", 0);
@@ -110,17 +114,23 @@
 			row.expected_amount = row.opening_amount;
 		}
 
-		for (let row of frm.doc.pos_transactions) {
-			frappe.db.get_doc("POS Invoice", row.pos_invoice).then(doc => {
-				frm.doc.grand_total += flt(doc.grand_total);
-				frm.doc.net_total += flt(doc.net_total);
-				frm.doc.total_quantity += flt(doc.total_qty);
-				refresh_payments(doc, frm);
-				refresh_taxes(doc, frm);
-				refresh_fields(frm);
-				set_html_data(frm);
-			});
+		const pos_inv_promises = frm.doc.pos_transactions.map(
+			row => frappe.db.get_doc("POS Invoice", row.pos_invoice)
+		);
+
+		const pos_invoices = await Promise.all(pos_inv_promises);
+
+		for (let doc of pos_invoices) {
+			frm.doc.grand_total += flt(doc.grand_total);
+			frm.doc.net_total += flt(doc.net_total);
+			frm.doc.total_quantity += flt(doc.total_qty);
+			refresh_payments(doc, frm);
+			refresh_taxes(doc, frm);
+			refresh_fields(frm);
+			set_html_data(frm);
 		}
+
+		frappe.dom.unfreeze();
 	}
 });
 
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
index 07059cb..49aab0d 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
@@ -23,21 +23,33 @@
 	def validate_pos_invoices(self):
 		invalid_rows = []
 		for d in self.pos_transactions:
-			invalid_row = {'idx': d.idx}
-			pos_invoice = frappe.db.get_values("POS Invoice", d.pos_invoice,
-				["consolidated_invoice", "pos_profile", "docstatus", "owner"], as_dict=1)[0]
+			invalid_row = {"idx": d.idx}
+			pos_invoice = frappe.db.get_values(
+				"POS Invoice",
+				d.pos_invoice,
+				["consolidated_invoice", "pos_profile", "docstatus", "owner"],
+				as_dict=1,
+			)[0]
 			if pos_invoice.consolidated_invoice:
-				invalid_row.setdefault('msg', []).append(_('POS Invoice is {}').format(frappe.bold("already consolidated")))
+				invalid_row.setdefault("msg", []).append(
+					_("POS Invoice is {}").format(frappe.bold("already consolidated"))
+				)
 				invalid_rows.append(invalid_row)
 				continue
 			if pos_invoice.pos_profile != self.pos_profile:
-				invalid_row.setdefault('msg', []).append(_("POS Profile doesn't matches {}").format(frappe.bold(self.pos_profile)))
+				invalid_row.setdefault("msg", []).append(
+					_("POS Profile doesn't matches {}").format(frappe.bold(self.pos_profile))
+				)
 			if pos_invoice.docstatus != 1:
-				invalid_row.setdefault('msg', []).append(_('POS Invoice is not {}').format(frappe.bold("submitted")))
+				invalid_row.setdefault("msg", []).append(
+					_("POS Invoice is not {}").format(frappe.bold("submitted"))
+				)
 			if pos_invoice.owner != self.user:
-				invalid_row.setdefault('msg', []).append(_("POS Invoice isn't created by user {}").format(frappe.bold(self.owner)))
+				invalid_row.setdefault("msg", []).append(
+					_("POS Invoice isn't created by user {}").format(frappe.bold(self.owner))
+				)
 
-			if invalid_row.get('msg'):
+			if invalid_row.get("msg"):
 				invalid_rows.append(invalid_row)
 
 		if not invalid_rows:
@@ -45,16 +57,18 @@
 
 		error_list = []
 		for row in invalid_rows:
-			for msg in row.get('msg'):
-				error_list.append(_("Row #{}: {}").format(row.get('idx'), msg))
+			for msg in row.get("msg"):
+				error_list.append(_("Row #{}: {}").format(row.get("idx"), msg))
 
 		frappe.throw(error_list, title=_("Invalid POS Invoices"), as_list=True)
 
 	@frappe.whitelist()
 	def get_payment_reconciliation_details(self):
-		currency = frappe.get_cached_value('Company', self.company,  "default_currency")
-		return frappe.render_template("erpnext/accounts/doctype/pos_closing_entry/closing_voucher_details.html",
-			{"data": self, "currency": currency})
+		currency = frappe.get_cached_value("Company", self.company, "default_currency")
+		return frappe.render_template(
+			"erpnext/accounts/doctype/pos_closing_entry/closing_voucher_details.html",
+			{"data": self, "currency": currency},
+		)
 
 	def on_submit(self):
 		consolidate_pos_invoices(closing_entry=self)
@@ -72,29 +86,38 @@
 		opening_entry.set_status()
 		opening_entry.save()
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_cashiers(doctype, txt, searchfield, start, page_len, filters):
-	cashiers_list = frappe.get_all("POS Profile User", filters=filters, fields=['user'], as_list=1)
+	cashiers_list = frappe.get_all("POS Profile User", filters=filters, fields=["user"], as_list=1)
 	return [c for c in cashiers_list]
 
+
 @frappe.whitelist()
 def get_pos_invoices(start, end, pos_profile, user):
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 	select
 		name, timestamp(posting_date, posting_time) as "timestamp"
 	from
 		`tabPOS Invoice`
 	where
 		owner = %s and docstatus = 1 and pos_profile = %s and ifnull(consolidated_invoice,'') = ''
-	""", (user, pos_profile), as_dict=1)
+	""",
+		(user, pos_profile),
+		as_dict=1,
+	)
 
-	data = list(filter(lambda d: get_datetime(start) <= get_datetime(d.timestamp) <= get_datetime(end), data))
+	data = list(
+		filter(lambda d: get_datetime(start) <= get_datetime(d.timestamp) <= get_datetime(end), data)
+	)
 	# need to get taxes and payments so can't avoid get_doc
 	data = [frappe.get_doc("POS Invoice", d.name).as_dict() for d in data]
 
 	return data
 
+
 def make_closing_entry_from_opening(opening_entry):
 	closing_entry = frappe.new_doc("POS Closing Entry")
 	closing_entry.pos_opening_entry = opening_entry.name
@@ -107,26 +130,38 @@
 	closing_entry.net_total = 0
 	closing_entry.total_quantity = 0
 
-	invoices = get_pos_invoices(closing_entry.period_start_date, closing_entry.period_end_date,
-		closing_entry.pos_profile, closing_entry.user)
+	invoices = get_pos_invoices(
+		closing_entry.period_start_date,
+		closing_entry.period_end_date,
+		closing_entry.pos_profile,
+		closing_entry.user,
+	)
 
 	pos_transactions = []
 	taxes = []
 	payments = []
 	for detail in opening_entry.balance_details:
-		payments.append(frappe._dict({
-			'mode_of_payment': detail.mode_of_payment,
-			'opening_amount': detail.opening_amount,
-			'expected_amount': detail.opening_amount
-		}))
+		payments.append(
+			frappe._dict(
+				{
+					"mode_of_payment": detail.mode_of_payment,
+					"opening_amount": detail.opening_amount,
+					"expected_amount": detail.opening_amount,
+				}
+			)
+		)
 
 	for d in invoices:
-		pos_transactions.append(frappe._dict({
-			'pos_invoice': d.name,
-			'posting_date': d.posting_date,
-			'grand_total': d.grand_total,
-			'customer': d.customer
-		}))
+		pos_transactions.append(
+			frappe._dict(
+				{
+					"pos_invoice": d.name,
+					"posting_date": d.posting_date,
+					"grand_total": d.grand_total,
+					"customer": d.customer,
+				}
+			)
+		)
 		closing_entry.grand_total += flt(d.grand_total)
 		closing_entry.net_total += flt(d.net_total)
 		closing_entry.total_quantity += flt(d.total_qty)
@@ -134,24 +169,22 @@
 		for t in d.taxes:
 			existing_tax = [tx for tx in taxes if tx.account_head == t.account_head and tx.rate == t.rate]
 			if existing_tax:
-				existing_tax[0].amount += flt(t.tax_amount);
+				existing_tax[0].amount += flt(t.tax_amount)
 			else:
-				taxes.append(frappe._dict({
-					'account_head': t.account_head,
-					'rate': t.rate,
-					'amount': t.tax_amount
-				}))
+				taxes.append(
+					frappe._dict({"account_head": t.account_head, "rate": t.rate, "amount": t.tax_amount})
+				)
 
 		for p in d.payments:
 			existing_pay = [pay for pay in payments if pay.mode_of_payment == p.mode_of_payment]
 			if existing_pay:
-				existing_pay[0].expected_amount += flt(p.amount);
+				existing_pay[0].expected_amount += flt(p.amount)
 			else:
-				payments.append(frappe._dict({
-					'mode_of_payment': p.mode_of_payment,
-					'opening_amount': 0,
-					'expected_amount': p.amount
-				}))
+				payments.append(
+					frappe._dict(
+						{"mode_of_payment": p.mode_of_payment, "opening_amount": 0, "expected_amount": p.amount}
+					)
+				)
 
 	closing_entry.set("pos_transactions", pos_transactions)
 	closing_entry.set("payment_reconciliation", payments)
diff --git a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py
index 626bee0..8eb28df 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py
+++ b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py
@@ -28,24 +28,20 @@
 		opening_entry = create_opening_entry(pos_profile, test_user.name)
 
 		pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1)
-		pos_inv1.append('payments', {
-			'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3500
-		})
+		pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
 		pos_inv1.submit()
 
 		pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
-		pos_inv2.append('payments', {
-			'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3200
-		})
+		pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
 		pos_inv2.submit()
 
 		pcv_doc = make_closing_entry_from_opening(opening_entry)
 		payment = pcv_doc.payment_reconciliation[0]
 
-		self.assertEqual(payment.mode_of_payment, 'Cash')
+		self.assertEqual(payment.mode_of_payment, "Cash")
 
 		for d in pcv_doc.payment_reconciliation:
-			if d.mode_of_payment == 'Cash':
+			if d.mode_of_payment == "Cash":
 				d.closing_amount = 6700
 
 		pcv_doc.submit()
@@ -58,24 +54,20 @@
 		opening_entry = create_opening_entry(pos_profile, test_user.name)
 
 		pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1)
-		pos_inv1.append('payments', {
-			'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3500
-		})
+		pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
 		pos_inv1.submit()
 
 		pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
-		pos_inv2.append('payments', {
-			'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3200
-		})
+		pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
 		pos_inv2.submit()
 
 		pcv_doc = make_closing_entry_from_opening(opening_entry)
 		payment = pcv_doc.payment_reconciliation[0]
 
-		self.assertEqual(payment.mode_of_payment, 'Cash')
+		self.assertEqual(payment.mode_of_payment, "Cash")
 
 		for d in pcv_doc.payment_reconciliation:
-			if d.mode_of_payment == 'Cash':
+			if d.mode_of_payment == "Cash":
 				d.closing_amount = 6700
 
 		pcv_doc.submit()
@@ -90,29 +82,25 @@
 		pcv_doc.cancel()
 
 		cancelled_invoice = frappe.db.get_value(
-			'POS Invoice Merge Log', {'pos_closing_entry': pcv_doc.name},
-			'consolidated_invoice'
+			"POS Invoice Merge Log", {"pos_closing_entry": pcv_doc.name}, "consolidated_invoice"
 		)
-		docstatus = frappe.db.get_value("Sales Invoice", cancelled_invoice, 'docstatus')
+		docstatus = frappe.db.get_value("Sales Invoice", cancelled_invoice, "docstatus")
 		self.assertEqual(docstatus, 2)
 
 		pos_inv1.load_from_db()
-		self.assertEqual(pos_inv1.status, 'Paid')
+		self.assertEqual(pos_inv1.status, "Paid")
 
 
 def init_user_and_profile(**args):
-	user = 'test@example.com'
-	test_user = frappe.get_doc('User', user)
+	user = "test@example.com"
+	test_user = frappe.get_doc("User", user)
 
 	roles = ("Accounts Manager", "Accounts User", "Sales Manager")
 	test_user.add_roles(*roles)
 	frappe.set_user(user)
 
 	pos_profile = make_pos_profile(**args)
-	pos_profile.append('applicable_for_users', {
-		'default': 1,
-		'user': user
-	})
+	pos_profile.append("applicable_for_users", {"default": 1, "user": user})
 
 	pos_profile.save()
 
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
index c68c2e1..b126d57 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
@@ -262,7 +262,6 @@
    "print_hide": 1
   },
   {
-   "allow_on_submit": 1,
    "default": "0",
    "fieldname": "is_return",
    "fieldtype": "Check",
@@ -1554,7 +1553,7 @@
  "icon": "fa fa-file-text",
  "is_submittable": 1,
  "links": [],
- "modified": "2022-02-19 06:25:00.983936",
+ "modified": "2022-03-22 13:00:24.166684",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Invoice",
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index 9b3b3aa..9649f80 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -16,7 +16,11 @@
 )
 from erpnext.accounts.party import get_due_date, get_party_account
 from erpnext.stock.doctype.batch.batch import get_batch_qty, get_pos_reserved_batch_qty
-from erpnext.stock.doctype.serial_no.serial_no import get_pos_reserved_serial_nos, get_serial_nos
+from erpnext.stock.doctype.serial_no.serial_no import (
+	get_delivered_serial_nos,
+	get_pos_reserved_serial_nos,
+	get_serial_nos,
+)
 
 
 class POSInvoice(SalesInvoice):
@@ -25,7 +29,9 @@
 
 	def validate(self):
 		if not cint(self.is_pos):
-			frappe.throw(_("POS Invoice should have {} field checked.").format(frappe.bold("Include Payment")))
+			frappe.throw(
+				_("POS Invoice should have {} field checked.").format(frappe.bold("Include Payment"))
+			)
 
 		# run on validate method of selling controller
 		super(SalesInvoice, self).validate()
@@ -49,11 +55,12 @@
 		self.validate_loyalty_transaction()
 		if self.coupon_code:
 			from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code
+
 			validate_coupon_code(self.coupon_code)
 
 	def on_submit(self):
 		# create the loyalty point ledger entry if the customer is enrolled in any loyalty program
-		if self.loyalty_program:
+		if not self.is_return and self.loyalty_program:
 			self.make_loyalty_point_entry()
 		elif self.is_return and self.return_against and self.loyalty_program:
 			against_psi_doc = frappe.get_doc("POS Invoice", self.return_against)
@@ -66,28 +73,33 @@
 
 		if self.coupon_code:
 			from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count
-			update_coupon_code_count(self.coupon_code,'used')
+
+			update_coupon_code_count(self.coupon_code, "used")
 
 	def before_cancel(self):
-		if self.consolidated_invoice and frappe.db.get_value('Sales Invoice', self.consolidated_invoice, 'docstatus') == 1:
+		if (
+			self.consolidated_invoice
+			and frappe.db.get_value("Sales Invoice", self.consolidated_invoice, "docstatus") == 1
+		):
 			pos_closing_entry = frappe.get_all(
 				"POS Invoice Reference",
 				ignore_permissions=True,
-				filters={ 'pos_invoice': self.name },
+				filters={"pos_invoice": self.name},
 				pluck="parent",
-				limit=1
+				limit=1,
 			)
 			frappe.throw(
-				_('You need to cancel POS Closing Entry {} to be able to cancel this document.').format(
+				_("You need to cancel POS Closing Entry {} to be able to cancel this document.").format(
 					get_link_to_form("POS Closing Entry", pos_closing_entry[0])
 				),
-				title=_('Not Allowed')
+				title=_("Not Allowed"),
 			)
 
 	def on_cancel(self):
+		self.ignore_linked_doctypes = "Payment Ledger Entry"
 		# run on cancel method of selling controller
 		super(SalesInvoice, self).on_cancel()
-		if self.loyalty_program:
+		if not self.is_return and self.loyalty_program:
 			self.delete_loyalty_point_entry()
 		elif self.is_return and self.return_against and self.loyalty_program:
 			against_psi_doc = frappe.get_doc("POS Invoice", self.return_against)
@@ -96,16 +108,22 @@
 
 		if self.coupon_code:
 			from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count
-			update_coupon_code_count(self.coupon_code,'cancelled')
+
+			update_coupon_code_count(self.coupon_code, "cancelled")
 
 	def check_phone_payments(self):
 		for pay in self.payments:
 			if pay.type == "Phone" and pay.amount >= 0:
-				paid_amt = frappe.db.get_value("Payment Request",
+				paid_amt = frappe.db.get_value(
+					"Payment Request",
 					filters=dict(
-						reference_doctype="POS Invoice", reference_name=self.name,
-						mode_of_payment=pay.mode_of_payment, status="Paid"),
-					fieldname="grand_total")
+						reference_doctype="POS Invoice",
+						reference_name=self.name,
+						mode_of_payment=pay.mode_of_payment,
+						status="Paid",
+					),
+					fieldname="grand_total",
+				)
 
 				if paid_amt and pay.amount != paid_amt:
 					return frappe.throw(_("Payment related to {0} is not completed").format(pay.mode_of_payment))
@@ -119,52 +137,73 @@
 		reserved_serial_nos = get_pos_reserved_serial_nos(filters)
 		invalid_serial_nos = [s for s in serial_nos if s in reserved_serial_nos]
 
-		bold_invalid_serial_nos = frappe.bold(', '.join(invalid_serial_nos))
+		bold_invalid_serial_nos = frappe.bold(", ".join(invalid_serial_nos))
 		if len(invalid_serial_nos) == 1:
-			frappe.throw(_("Row #{}: Serial No. {} has already been transacted into another POS Invoice. Please select valid serial no.")
-						.format(item.idx, bold_invalid_serial_nos), title=_("Item Unavailable"))
+			frappe.throw(
+				_(
+					"Row #{}: Serial No. {} has already been transacted into another POS Invoice. Please select valid serial no."
+				).format(item.idx, bold_invalid_serial_nos),
+				title=_("Item Unavailable"),
+			)
 		elif invalid_serial_nos:
-			frappe.throw(_("Row #{}: Serial Nos. {} have already been transacted into another POS Invoice. Please select valid serial no.")
-						.format(item.idx, bold_invalid_serial_nos), title=_("Item Unavailable"))
+			frappe.throw(
+				_(
+					"Row #{}: Serial Nos. {} have already been transacted into another POS Invoice. Please select valid serial no."
+				).format(item.idx, bold_invalid_serial_nos),
+				title=_("Item Unavailable"),
+			)
 
 	def validate_pos_reserved_batch_qty(self, item):
-		filters = {"item_code": item.item_code, "warehouse": item.warehouse, "batch_no":item.batch_no}
+		filters = {"item_code": item.item_code, "warehouse": item.warehouse, "batch_no": item.batch_no}
 
 		available_batch_qty = get_batch_qty(item.batch_no, item.warehouse, item.item_code)
 		reserved_batch_qty = get_pos_reserved_batch_qty(filters)
 
 		bold_item_name = frappe.bold(item.item_name)
-		bold_extra_batch_qty_needed = frappe.bold(abs(available_batch_qty - reserved_batch_qty - item.qty))
+		bold_extra_batch_qty_needed = frappe.bold(
+			abs(available_batch_qty - reserved_batch_qty - item.qty)
+		)
 		bold_invalid_batch_no = frappe.bold(item.batch_no)
 
 		if (available_batch_qty - reserved_batch_qty) == 0:
-			frappe.throw(_("Row #{}: Batch No. {} of item {} has no stock available. Please select valid batch no.")
-						.format(item.idx, bold_invalid_batch_no, bold_item_name), title=_("Item Unavailable"))
+			frappe.throw(
+				_(
+					"Row #{}: Batch No. {} of item {} has no stock available. Please select valid batch no."
+				).format(item.idx, bold_invalid_batch_no, bold_item_name),
+				title=_("Item Unavailable"),
+			)
 		elif (available_batch_qty - reserved_batch_qty - item.qty) < 0:
-			frappe.throw(_("Row #{}: Batch No. {} of item {} has less than required stock available, {} more required")
-						.format(item.idx, bold_invalid_batch_no, bold_item_name, bold_extra_batch_qty_needed), title=_("Item Unavailable"))
+			frappe.throw(
+				_(
+					"Row #{}: Batch No. {} of item {} has less than required stock available, {} more required"
+				).format(
+					item.idx, bold_invalid_batch_no, bold_item_name, bold_extra_batch_qty_needed
+				),
+				title=_("Item Unavailable"),
+			)
 
 	def validate_delivered_serial_nos(self, item):
-		serial_nos = get_serial_nos(item.serial_no)
-		delivered_serial_nos = frappe.db.get_list('Serial No', {
-			'item_code': item.item_code,
-			'name': ['in', serial_nos],
-			'sales_invoice': ['is', 'set']
-		}, pluck='name')
+		delivered_serial_nos = get_delivered_serial_nos(item.serial_no)
 
 		if delivered_serial_nos:
-			bold_delivered_serial_nos = frappe.bold(', '.join(delivered_serial_nos))
-			frappe.throw(_("Row #{}: Serial No. {} has already been transacted into another Sales Invoice. Please select valid serial no.")
-						.format(item.idx, bold_delivered_serial_nos), title=_("Item Unavailable"))
+			bold_delivered_serial_nos = frappe.bold(", ".join(delivered_serial_nos))
+			frappe.throw(
+				_(
+					"Row #{}: Serial No. {} has already been transacted into another Sales Invoice. Please select valid serial no."
+				).format(item.idx, bold_delivered_serial_nos),
+				title=_("Item Unavailable"),
+			)
 
 	def validate_invalid_serial_nos(self, item):
 		serial_nos = get_serial_nos(item.serial_no)
 		error_msg = []
 		invalid_serials, msg = "", ""
 		for serial_no in serial_nos:
-			if not frappe.db.exists('Serial No', serial_no):
+			if not frappe.db.exists("Serial No", serial_no):
 				invalid_serials = invalid_serials + (", " if invalid_serials else "") + serial_no
-		msg = (_("Row #{}: Following Serial numbers for item {} are <b>Invalid</b>: {}").format(item.idx, frappe.bold(item.get("item_code")), frappe.bold(invalid_serials)))
+		msg = _("Row #{}: Following Serial numbers for item {} are <b>Invalid</b>: {}").format(
+			item.idx, frappe.bold(item.get("item_code")), frappe.bold(invalid_serials)
+		)
 		if invalid_serials:
 			error_msg.append(msg)
 
@@ -172,12 +211,18 @@
 			frappe.throw(error_msg, title=_("Invalid Item"), as_list=True)
 
 	def validate_stock_availablility(self):
+		if self.is_return:
+			return
+
+		if self.docstatus.is_draft() and not frappe.db.get_value(
+			"POS Profile", self.pos_profile, "validate_stock_on_save"
+		):
+			return
+
 		from erpnext.stock.stock_ledger import is_negative_stock_allowed
 
-		if self.is_return or self.docstatus != 1:
-			return
-		for d in self.get('items'):
-			is_service_item = not (frappe.db.get_value('Item', d.get('item_code'), 'is_stock_item'))
+		for d in self.get("items"):
+			is_service_item = not (frappe.db.get_value("Item", d.get("item_code"), "is_stock_item"))
 			if is_service_item:
 				return
 			if d.serial_no:
@@ -192,13 +237,25 @@
 
 				available_stock, is_stock_item = get_stock_availability(d.item_code, d.warehouse)
 
-				item_code, warehouse, qty = frappe.bold(d.item_code), frappe.bold(d.warehouse), frappe.bold(d.qty)
+				item_code, warehouse, qty = (
+					frappe.bold(d.item_code),
+					frappe.bold(d.warehouse),
+					frappe.bold(d.qty),
+				)
 				if flt(available_stock) <= 0:
-					frappe.throw(_('Row #{}: Item Code: {} is not available under warehouse {}.')
-								.format(d.idx, item_code, warehouse), title=_("Item Unavailable"))
+					frappe.throw(
+						_("Row #{}: Item Code: {} is not available under warehouse {}.").format(
+							d.idx, item_code, warehouse
+						),
+						title=_("Item Unavailable"),
+					)
 				elif flt(available_stock) < flt(d.qty):
-					frappe.throw(_('Row #{}: Stock quantity not enough for Item Code: {} under warehouse {}. Available quantity {}.')
-								.format(d.idx, item_code, warehouse, available_stock), title=_("Item Unavailable"))
+					frappe.throw(
+						_(
+							"Row #{}: Stock quantity not enough for Item Code: {} under warehouse {}. Available quantity {}."
+						).format(d.idx, item_code, warehouse, available_stock),
+						title=_("Item Unavailable"),
+					)
 
 	def validate_serialised_or_batched_item(self):
 		error_msg = []
@@ -212,16 +269,21 @@
 			item_code = frappe.bold(d.item_code)
 			serial_nos = get_serial_nos(d.serial_no)
 			if serialized and batched and (no_batch_selected or no_serial_selected):
-				msg = (_('Row #{}: Please select a serial no and batch against item: {} or remove it to complete transaction.')
-							.format(d.idx, item_code))
+				msg = _(
+					"Row #{}: Please select a serial no and batch against item: {} or remove it to complete transaction."
+				).format(d.idx, item_code)
 			elif serialized and no_serial_selected:
-				msg = (_('Row #{}: No serial number selected against item: {}. Please select one or remove it to complete transaction.')
-							.format(d.idx, item_code))
+				msg = _(
+					"Row #{}: No serial number selected against item: {}. Please select one or remove it to complete transaction."
+				).format(d.idx, item_code)
 			elif batched and no_batch_selected:
-				msg = (_('Row #{}: No batch selected against item: {}. Please select a batch or remove it to complete transaction.')
-							.format(d.idx, item_code))
+				msg = _(
+					"Row #{}: No batch selected against item: {}. Please select a batch or remove it to complete transaction."
+				).format(d.idx, item_code)
 			elif serialized and not no_serial_selected and len(serial_nos) != d.qty:
-				msg = (_("Row #{}: You must select {} serial numbers for item {}.").format(d.idx, frappe.bold(cint(d.qty)), item_code))
+				msg = _("Row #{}: You must select {} serial numbers for item {}.").format(
+					d.idx, frappe.bold(cint(d.qty)), item_code
+				)
 
 			if msg:
 				error_msg.append(msg)
@@ -230,18 +292,22 @@
 			frappe.throw(error_msg, title=_("Invalid Item"), as_list=True)
 
 	def validate_return_items_qty(self):
-		if not self.get("is_return"): return
+		if not self.get("is_return"):
+			return
 
 		for d in self.get("items"):
 			if d.get("qty") > 0:
 				frappe.throw(
-					_("Row #{}: You cannot add postive quantities in a return invoice. Please remove item {} to complete the return.")
-					.format(d.idx, frappe.bold(d.item_code)), title=_("Invalid Item")
+					_(
+						"Row #{}: You cannot add postive quantities in a return invoice. Please remove item {} to complete the return."
+					).format(d.idx, frappe.bold(d.item_code)),
+					title=_("Invalid Item"),
 				)
 			if d.get("serial_no"):
 				serial_nos = get_serial_nos(d.serial_no)
 				for sr in serial_nos:
-					serial_no_exists = frappe.db.sql("""
+					serial_no_exists = frappe.db.sql(
+						"""
 						SELECT name
 						FROM `tabPOS Invoice Item`
 						WHERE
@@ -251,14 +317,17 @@
 								or serial_no like %s
 								or serial_no like %s
 							)
-					""", (self.return_against, sr, sr+'\n%', '%\n'+sr, '%\n'+sr+'\n%'))
+					""",
+						(self.return_against, sr, sr + "\n%", "%\n" + sr, "%\n" + sr + "\n%"),
+					)
 
 					if not serial_no_exists:
 						bold_return_against = frappe.bold(self.return_against)
 						bold_serial_no = frappe.bold(sr)
 						frappe.throw(
-							_("Row #{}: Serial No {} cannot be returned since it was not transacted in original invoice {}")
-							.format(d.idx, bold_serial_no, bold_return_against)
+							_(
+								"Row #{}: Serial No {} cannot be returned since it was not transacted in original invoice {}"
+							).format(d.idx, bold_serial_no, bold_return_against)
 						)
 
 	def validate_mode_of_payment(self):
@@ -266,16 +335,25 @@
 			frappe.throw(_("At least one mode of payment is required for POS invoice."))
 
 	def validate_change_account(self):
-		if self.change_amount and self.account_for_change_amount and \
-			frappe.db.get_value("Account", self.account_for_change_amount, "company") != self.company:
-			frappe.throw(_("The selected change account {} doesn't belongs to Company {}.").format(self.account_for_change_amount, self.company))
+		if (
+			self.change_amount
+			and self.account_for_change_amount
+			and frappe.db.get_value("Account", self.account_for_change_amount, "company") != self.company
+		):
+			frappe.throw(
+				_("The selected change account {} doesn't belongs to Company {}.").format(
+					self.account_for_change_amount, self.company
+				)
+			)
 
 	def validate_change_amount(self):
 		grand_total = flt(self.rounded_total) or flt(self.grand_total)
 		base_grand_total = flt(self.base_rounded_total) or flt(self.base_grand_total)
 		if not flt(self.change_amount) and grand_total < flt(self.paid_amount):
 			self.change_amount = flt(self.paid_amount - grand_total + flt(self.write_off_amount))
-			self.base_change_amount = flt(self.base_paid_amount) - base_grand_total + flt(self.base_write_off_amount)
+			self.base_change_amount = (
+				flt(self.base_paid_amount) - base_grand_total + flt(self.base_write_off_amount)
+			)
 
 		if flt(self.change_amount) and not self.account_for_change_amount:
 			frappe.msgprint(_("Please enter Account for Change Amount"), raise_exception=1)
@@ -295,8 +373,12 @@
 				frappe.throw(_("Total payments amount can't be greater than {}").format(-invoice_total))
 
 	def validate_loyalty_transaction(self):
-		if self.redeem_loyalty_points and (not self.loyalty_redemption_account or not self.loyalty_redemption_cost_center):
-			expense_account, cost_center = frappe.db.get_value('Loyalty Program', self.loyalty_program, ["expense_account", "cost_center"])
+		if self.redeem_loyalty_points and (
+			not self.loyalty_redemption_account or not self.loyalty_redemption_cost_center
+		):
+			expense_account, cost_center = frappe.db.get_value(
+				"Loyalty Program", self.loyalty_program, ["expense_account", "cost_center"]
+			)
 			if not self.loyalty_redemption_account:
 				self.loyalty_redemption_account = expense_account
 			if not self.loyalty_redemption_cost_center:
@@ -307,8 +389,8 @@
 
 	def set_status(self, update=False, status=None, update_modified=True):
 		if self.is_new():
-			if self.get('amended_from'):
-				self.status = 'Draft'
+			if self.get("amended_from"):
+				self.status = "Draft"
 			return
 
 		if not status:
@@ -317,19 +399,35 @@
 			elif self.docstatus == 1:
 				if self.consolidated_invoice:
 					self.status = "Consolidated"
-				elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) < getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed':
+				elif (
+					flt(self.outstanding_amount) > 0
+					and getdate(self.due_date) < getdate(nowdate())
+					and self.is_discounted
+					and self.get_discounting_status() == "Disbursed"
+				):
 					self.status = "Overdue and Discounted"
 				elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) < getdate(nowdate()):
 					self.status = "Overdue"
-				elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed':
+				elif (
+					flt(self.outstanding_amount) > 0
+					and getdate(self.due_date) >= getdate(nowdate())
+					and self.is_discounted
+					and self.get_discounting_status() == "Disbursed"
+				):
 					self.status = "Unpaid and Discounted"
 				elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) >= getdate(nowdate()):
 					self.status = "Unpaid"
-				elif flt(self.outstanding_amount) <= 0 and self.is_return == 0 and frappe.db.get_value('POS Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
+				elif (
+					flt(self.outstanding_amount) <= 0
+					and self.is_return == 0
+					and frappe.db.get_value(
+						"POS Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1}
+					)
+				):
 					self.status = "Credit Note Issued"
 				elif self.is_return == 1:
 					self.status = "Return"
-				elif flt(self.outstanding_amount)<=0:
+				elif flt(self.outstanding_amount) <= 0:
 					self.status = "Paid"
 				else:
 					self.status = "Submitted"
@@ -337,22 +435,23 @@
 				self.status = "Draft"
 
 		if update:
-			self.db_set('status', self.status, update_modified = update_modified)
+			self.db_set("status", self.status, update_modified=update_modified)
 
 	def set_pos_fields(self, for_validate=False):
 		"""Set retail related fields from POS Profiles"""
 		from erpnext.stock.get_item_details import get_pos_profile, get_pos_profile_item_details
+
 		if not self.pos_profile:
 			pos_profile = get_pos_profile(self.company) or {}
 			if not pos_profile:
 				frappe.throw(_("No POS Profile found. Please create a New POS Profile first"))
-			self.pos_profile = pos_profile.get('name')
+			self.pos_profile = pos_profile.get("name")
 
 		profile = {}
 		if self.pos_profile:
-			profile = frappe.get_doc('POS Profile', self.pos_profile)
+			profile = frappe.get_doc("POS Profile", self.pos_profile)
 
-		if not self.get('payments') and not for_validate:
+		if not self.get("payments") and not for_validate:
 			update_multi_mode_option(self, profile)
 
 		if self.is_return and not for_validate:
@@ -362,35 +461,55 @@
 			if not for_validate and not self.customer:
 				self.customer = profile.customer
 
-			self.account_for_change_amount = profile.get('account_for_change_amount') or self.account_for_change_amount
-			self.set_warehouse = profile.get('warehouse') or self.set_warehouse
+			self.account_for_change_amount = (
+				profile.get("account_for_change_amount") or self.account_for_change_amount
+			)
+			self.set_warehouse = profile.get("warehouse") or self.set_warehouse
 
-			for fieldname in ('currency', 'letter_head', 'tc_name',
-				'company', 'select_print_heading', 'write_off_account', 'taxes_and_charges',
-				'write_off_cost_center', 'apply_discount_on', 'cost_center', 'tax_category',
-				'ignore_pricing_rule', 'company_address', 'update_stock'):
-					if not for_validate:
-						self.set(fieldname, profile.get(fieldname))
+			for fieldname in (
+				"currency",
+				"letter_head",
+				"tc_name",
+				"company",
+				"select_print_heading",
+				"write_off_account",
+				"taxes_and_charges",
+				"write_off_cost_center",
+				"apply_discount_on",
+				"cost_center",
+				"tax_category",
+				"ignore_pricing_rule",
+				"company_address",
+				"update_stock",
+			):
+				if not for_validate:
+					self.set(fieldname, profile.get(fieldname))
 
 			if self.customer:
 				customer_price_list, customer_group, customer_currency = frappe.db.get_value(
-					"Customer", self.customer, ['default_price_list', 'customer_group', 'default_currency']
+					"Customer", self.customer, ["default_price_list", "customer_group", "default_currency"]
 				)
-				customer_group_price_list = frappe.db.get_value("Customer Group", customer_group, 'default_price_list')
-				selling_price_list = customer_price_list or customer_group_price_list or profile.get('selling_price_list')
-				if customer_currency != profile.get('currency'):
-					self.set('currency', customer_currency)
+				customer_group_price_list = frappe.db.get_value(
+					"Customer Group", customer_group, "default_price_list"
+				)
+				selling_price_list = (
+					customer_price_list or customer_group_price_list or profile.get("selling_price_list")
+				)
+				if customer_currency != profile.get("currency"):
+					self.set("currency", customer_currency)
 
 			else:
-				selling_price_list = profile.get('selling_price_list')
+				selling_price_list = profile.get("selling_price_list")
 
 			if selling_price_list:
-				self.set('selling_price_list', selling_price_list)
+				self.set("selling_price_list", selling_price_list)
 
 			# set pos values in items
 			for item in self.get("items"):
-				if item.get('item_code'):
-					profile_details = get_pos_profile_item_details(profile.get("company"), frappe._dict(item.as_dict()), profile)
+				if item.get("item_code"):
+					profile_details = get_pos_profile_item_details(
+						profile.get("company"), frappe._dict(item.as_dict()), profile
+					)
 					for fname, val in profile_details.items():
 						if (not for_validate) or (for_validate and not item.get(fname)):
 							item.set(fname, val)
@@ -404,7 +523,9 @@
 				self.set_taxes()
 
 		if not self.account_for_change_amount:
-			self.account_for_change_amount = frappe.get_cached_value('Company',  self.company,  'default_cash_account')
+			self.account_for_change_amount = frappe.get_cached_value(
+				"Company", self.company, "default_cash_account"
+			)
 
 		return profile
 
@@ -414,27 +535,29 @@
 
 		if not self.debit_to:
 			self.debit_to = get_party_account("Customer", self.customer, self.company)
-			self.party_account_currency = frappe.db.get_value("Account", self.debit_to, "account_currency", cache=True)
+			self.party_account_currency = frappe.db.get_value(
+				"Account", self.debit_to, "account_currency", cache=True
+			)
 		if not self.due_date and self.customer:
 			self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company)
 
 		super(SalesInvoice, self).set_missing_values(for_validate)
 
 		print_format = profile.get("print_format") if profile else None
-		if not print_format and not cint(frappe.db.get_value('Print Format', 'POS Invoice', 'disabled')):
-			print_format = 'POS Invoice'
+		if not print_format and not cint(frappe.db.get_value("Print Format", "POS Invoice", "disabled")):
+			print_format = "POS Invoice"
 
 		if profile:
 			return {
 				"print_format": print_format,
 				"campaign": profile.get("campaign"),
-				"allow_print_before_pay": profile.get("allow_print_before_pay")
+				"allow_print_before_pay": profile.get("allow_print_before_pay"),
 			}
 
 	@frappe.whitelist()
 	def reset_mode_of_payments(self):
 		if self.pos_profile:
-			pos_profile = frappe.get_cached_doc('POS Profile', self.pos_profile)
+			pos_profile = frappe.get_cached_doc("POS Profile", self.pos_profile)
 			update_multi_mode_option(self, pos_profile)
 			self.paid_amount = 0
 
@@ -463,9 +586,13 @@
 				return pay_req
 
 	def get_new_payment_request(self, mop):
-		payment_gateway_account = frappe.db.get_value("Payment Gateway Account", {
-			"payment_account": mop.account,
-		}, ["name"])
+		payment_gateway_account = frappe.db.get_value(
+			"Payment Gateway Account",
+			{
+				"payment_account": mop.account,
+			},
+			["name"],
+		)
 
 		args = {
 			"dt": "POS Invoice",
@@ -476,36 +603,40 @@
 			"payment_request_type": "Inward",
 			"party_type": "Customer",
 			"party": self.customer,
-			"return_doc": True
+			"return_doc": True,
 		}
 		return make_payment_request(**args)
 
 	def get_existing_payment_request(self, pay):
-		payment_gateway_account = frappe.db.get_value("Payment Gateway Account", {
-			"payment_account": pay.account,
-		}, ["name"])
+		payment_gateway_account = frappe.db.get_value(
+			"Payment Gateway Account",
+			{
+				"payment_account": pay.account,
+			},
+			["name"],
+		)
 
-		args = {
-			'doctype': 'Payment Request',
-			'reference_doctype': 'POS Invoice',
-			'reference_name': self.name,
-			'payment_gateway_account': payment_gateway_account,
-			'email_to': self.contact_mobile
+		filters = {
+			"reference_doctype": "POS Invoice",
+			"reference_name": self.name,
+			"payment_gateway_account": payment_gateway_account,
+			"email_to": self.contact_mobile,
 		}
-		pr = frappe.db.exists(args)
+		pr = frappe.db.get_value("Payment Request", filters=filters)
 		if pr:
-			return frappe.get_doc('Payment Request', pr[0][0])
+			return frappe.get_doc("Payment Request", pr)
+
 
 @frappe.whitelist()
 def get_stock_availability(item_code, warehouse):
-	if frappe.db.get_value('Item', item_code, 'is_stock_item'):
+	if frappe.db.get_value("Item", item_code, "is_stock_item"):
 		is_stock_item = True
 		bin_qty = get_bin_qty(item_code, warehouse)
 		pos_sales_qty = get_pos_reserved_qty(item_code, warehouse)
 		return bin_qty - pos_sales_qty, is_stock_item
 	else:
 		is_stock_item = False
-		if frappe.db.exists('Product Bundle', item_code):
+		if frappe.db.exists("Product Bundle", item_code):
 			return get_bundle_availability(item_code, warehouse), is_stock_item
 		else:
 			# Is a service item
@@ -513,7 +644,7 @@
 
 
 def get_bundle_availability(bundle_item_code, warehouse):
-	product_bundle = frappe.get_doc('Product Bundle', bundle_item_code)
+	product_bundle = frappe.get_doc("Product Bundle", bundle_item_code)
 
 	bundle_bin_qty = 1000000
 	for item in product_bundle.items:
@@ -528,68 +659,87 @@
 	pos_sales_qty = get_pos_reserved_qty(bundle_item_code, warehouse)
 	return bundle_bin_qty - pos_sales_qty
 
+
 def get_bin_qty(item_code, warehouse):
-	bin_qty = frappe.db.sql("""select actual_qty from `tabBin`
+	bin_qty = frappe.db.sql(
+		"""select actual_qty from `tabBin`
 		where item_code = %s and warehouse = %s
-		limit 1""", (item_code, warehouse), as_dict=1)
+		limit 1""",
+		(item_code, warehouse),
+		as_dict=1,
+	)
 
 	return bin_qty[0].actual_qty or 0 if bin_qty else 0
 
+
 def get_pos_reserved_qty(item_code, warehouse):
-	reserved_qty = frappe.db.sql("""select sum(p_item.qty) as qty
+	reserved_qty = frappe.db.sql(
+		"""select sum(p_item.qty) as qty
 		from `tabPOS Invoice` p, `tabPOS Invoice Item` p_item
 		where p.name = p_item.parent
 		and ifnull(p.consolidated_invoice, '') = ''
 		and p_item.docstatus = 1
 		and p_item.item_code = %s
 		and p_item.warehouse = %s
-		""", (item_code, warehouse), as_dict=1)
+		""",
+		(item_code, warehouse),
+		as_dict=1,
+	)
 
 	return reserved_qty[0].qty or 0 if reserved_qty else 0
 
+
 @frappe.whitelist()
 def make_sales_return(source_name, target_doc=None):
 	from erpnext.controllers.sales_and_purchase_return import make_return_doc
+
 	return make_return_doc("POS Invoice", source_name, target_doc)
 
+
 @frappe.whitelist()
 def make_merge_log(invoices):
 	import json
 
-
 	if isinstance(invoices, str):
 		invoices = json.loads(invoices)
 
 	if len(invoices) == 0:
-		frappe.throw(_('Atleast one invoice has to be selected.'))
+		frappe.throw(_("Atleast one invoice has to be selected."))
 
 	merge_log = frappe.new_doc("POS Invoice Merge Log")
 	merge_log.posting_date = getdate(nowdate())
 	for inv in invoices:
-		inv_data = frappe.db.get_values("POS Invoice", inv.get('name'),
-			["customer", "posting_date", "grand_total"], as_dict=1)[0]
+		inv_data = frappe.db.get_values(
+			"POS Invoice", inv.get("name"), ["customer", "posting_date", "grand_total"], as_dict=1
+		)[0]
 		merge_log.customer = inv_data.customer
-		merge_log.append("pos_invoices", {
-			'pos_invoice': inv.get('name'),
-			'customer': inv_data.customer,
-			'posting_date': inv_data.posting_date,
-			'grand_total': inv_data.grand_total
-		})
+		merge_log.append(
+			"pos_invoices",
+			{
+				"pos_invoice": inv.get("name"),
+				"customer": inv_data.customer,
+				"posting_date": inv_data.posting_date,
+				"grand_total": inv_data.grand_total,
+			},
+		)
 
-	if merge_log.get('pos_invoices'):
+	if merge_log.get("pos_invoices"):
 		return merge_log.as_dict()
 
+
 def add_return_modes(doc, pos_profile):
 	def append_payment(payment_mode):
-		payment = doc.append('payments', {})
+		payment = doc.append("payments", {})
 		payment.default = payment_mode.default
 		payment.mode_of_payment = payment_mode.parent
 		payment.account = payment_mode.default_account
 		payment.type = payment_mode.type
 
-	for pos_payment_method in pos_profile.get('payments'):
+	for pos_payment_method in pos_profile.get("payments"):
 		pos_payment_method = pos_payment_method.as_dict()
 		mode_of_payment = pos_payment_method.mode_of_payment
-		if pos_payment_method.allow_in_returns and not [d for d in doc.get('payments') if d.mode_of_payment == mode_of_payment]:
+		if pos_payment_method.allow_in_returns and not [
+			d for d in doc.get("payments") if d.mode_of_payment == mode_of_payment
+		]:
 			payment_mode = get_mode_of_payment_info(mode_of_payment, doc.company)
 			append_payment(payment_mode[0])
diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
index cf8affd..70f128e 100644
--- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
@@ -35,29 +35,34 @@
 		w2 = frappe.get_doc(w.doctype, w.name)
 
 		import time
+
 		time.sleep(1)
 		w.save()
 
 		import time
+
 		time.sleep(1)
 		self.assertRaises(frappe.TimestampMismatchError, w2.save)
 
 	def test_change_naming_series(self):
 		inv = create_pos_invoice(do_not_submit=1)
-		inv.naming_series = 'TEST-'
+		inv.naming_series = "TEST-"
 
 		self.assertRaises(frappe.CannotChangeConstantError, inv.save)
 
 	def test_discount_and_inclusive_tax(self):
 		inv = create_pos_invoice(qty=100, rate=50, do_not_save=1)
-		inv.append("taxes", {
-			"charge_type": "On Net Total",
-			"account_head": "_Test Account Service Tax - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "Service Tax",
-			"rate": 14,
-			'included_in_print_rate': 1
-		})
+		inv.append(
+			"taxes",
+			{
+				"charge_type": "On Net Total",
+				"account_head": "_Test Account Service Tax - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "Service Tax",
+				"rate": 14,
+				"included_in_print_rate": 1,
+			},
+		)
 		inv.insert()
 
 		self.assertEqual(inv.net_total, 4385.96)
@@ -66,7 +71,7 @@
 		inv.reload()
 
 		inv.discount_amount = 100
-		inv.apply_discount_on = 'Net Total'
+		inv.apply_discount_on = "Net Total"
 		inv.payment_schedule = []
 
 		inv.save()
@@ -77,7 +82,7 @@
 		inv.reload()
 
 		inv.discount_amount = 100
-		inv.apply_discount_on = 'Grand Total'
+		inv.apply_discount_on = "Grand Total"
 		inv.payment_schedule = []
 
 		inv.save()
@@ -93,14 +98,17 @@
 			item_row_copy.qty = qty
 			inv.append("items", item_row_copy)
 
-		inv.append("taxes", {
-			"account_head": "_Test Account VAT - _TC",
-			"charge_type": "On Net Total",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "VAT",
-			"doctype": "Sales Taxes and Charges",
-			"rate": 19
-		})
+		inv.append(
+			"taxes",
+			{
+				"account_head": "_Test Account VAT - _TC",
+				"charge_type": "On Net Total",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "VAT",
+				"doctype": "Sales Taxes and Charges",
+				"rate": 19,
+			},
+		)
 		inv.insert()
 
 		self.assertEqual(inv.net_total, 4600)
@@ -115,10 +123,10 @@
 		item_row = inv.get("items")[0]
 
 		add_items = [
-			(54, '_Test Account Excise Duty @ 12 - _TC'),
-			(288, '_Test Account Excise Duty @ 15 - _TC'),
-			(144, '_Test Account Excise Duty @ 20 - _TC'),
-			(430, '_Test Item Tax Template 1 - _TC')
+			(54, "_Test Account Excise Duty @ 12 - _TC"),
+			(288, "_Test Account Excise Duty @ 15 - _TC"),
+			(144, "_Test Account Excise Duty @ 20 - _TC"),
+			(430, "_Test Item Tax Template 1 - _TC"),
 		]
 		for qty, item_tax_template in add_items:
 			item_row_copy = copy.deepcopy(item_row)
@@ -126,30 +134,39 @@
 			item_row_copy.item_tax_template = item_tax_template
 			inv.append("items", item_row_copy)
 
-		inv.append("taxes", {
-			"account_head": "_Test Account Excise Duty - _TC",
-			"charge_type": "On Net Total",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "Excise Duty",
-			"doctype": "Sales Taxes and Charges",
-			"rate": 11
-		})
-		inv.append("taxes", {
-			"account_head": "_Test Account Education Cess - _TC",
-			"charge_type": "On Net Total",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "Education Cess",
-			"doctype": "Sales Taxes and Charges",
-			"rate": 0
-		})
-		inv.append("taxes", {
-			"account_head": "_Test Account S&H Education Cess - _TC",
-			"charge_type": "On Net Total",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "S&H Education Cess",
-			"doctype": "Sales Taxes and Charges",
-			"rate": 3
-		})
+		inv.append(
+			"taxes",
+			{
+				"account_head": "_Test Account Excise Duty - _TC",
+				"charge_type": "On Net Total",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "Excise Duty",
+				"doctype": "Sales Taxes and Charges",
+				"rate": 11,
+			},
+		)
+		inv.append(
+			"taxes",
+			{
+				"account_head": "_Test Account Education Cess - _TC",
+				"charge_type": "On Net Total",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "Education Cess",
+				"doctype": "Sales Taxes and Charges",
+				"rate": 0,
+			},
+		)
+		inv.append(
+			"taxes",
+			{
+				"account_head": "_Test Account S&H Education Cess - _TC",
+				"charge_type": "On Net Total",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "S&H Education Cess",
+				"doctype": "Sales Taxes and Charges",
+				"rate": 3,
+			},
+		)
 		inv.insert()
 
 		self.assertEqual(inv.net_total, 4600)
@@ -179,14 +196,17 @@
 		inv.apply_discount_on = "Net Total"
 		inv.discount_amount = 75.0
 
-		inv.append("taxes", {
-			"account_head": "_Test Account VAT - _TC",
-			"charge_type": "On Net Total",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "VAT",
-			"doctype": "Sales Taxes and Charges",
-			"rate": 24
-		})
+		inv.append(
+			"taxes",
+			{
+				"account_head": "_Test Account VAT - _TC",
+				"charge_type": "On Net Total",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "VAT",
+				"doctype": "Sales Taxes and Charges",
+				"rate": 24,
+			},
+		)
 		inv.insert()
 
 		self.assertEqual(inv.total, 975)
@@ -198,11 +218,15 @@
 		self.assertEqual(inv.grand_total, 1116.0)
 
 	def test_pos_returns_with_repayment(self):
-		pos = create_pos_invoice(qty = 10, do_not_save=True)
+		pos = create_pos_invoice(qty=10, do_not_save=True)
 
-		pos.set('payments', [])
-		pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 500})
-		pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 500, 'default': 1})
+		pos.set("payments", [])
+		pos.append(
+			"payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - _TC", "amount": 500}
+		)
+		pos.append(
+			"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 500, "default": 1}
+		)
 		pos.insert()
 		pos.submit()
 
@@ -211,25 +235,39 @@
 		pos_return.insert()
 		pos_return.submit()
 
-		self.assertEqual(pos_return.get('payments')[0].amount, -500)
-		self.assertEqual(pos_return.get('payments')[1].amount, -500)
+		self.assertEqual(pos_return.get("payments")[0].amount, -500)
+		self.assertEqual(pos_return.get("payments")[1].amount, -500)
 
 	def test_pos_return_for_serialized_item(self):
 		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
 
-		se = make_serialized_item(company='_Test Company',
-			target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC')
+		se = make_serialized_item(
+			company="_Test Company",
+			target_warehouse="Stores - _TC",
+			cost_center="Main - _TC",
+			expense_account="Cost of Goods Sold - _TC",
+		)
 
 		serial_nos = get_serial_nos(se.get("items")[0].serial_no)
 
-		pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC',
-			account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
-			expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
-			item=se.get("items")[0].item_code, rate=1000, do_not_save=1)
+		pos = create_pos_invoice(
+			company="_Test Company",
+			debit_to="Debtors - _TC",
+			account_for_change_amount="Cash - _TC",
+			warehouse="Stores - _TC",
+			income_account="Sales - _TC",
+			expense_account="Cost of Goods Sold - _TC",
+			cost_center="Main - _TC",
+			item=se.get("items")[0].item_code,
+			rate=1000,
+			do_not_save=1,
+		)
 
 		pos.get("items")[0].serial_no = serial_nos[0]
-		pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 1000, 'default': 1})
+		pos.append(
+			"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 1000, "default": 1}
+		)
 
 		pos.insert()
 		pos.submit()
@@ -238,24 +276,39 @@
 
 		pos_return.insert()
 		pos_return.submit()
-		self.assertEqual(pos_return.get('items')[0].serial_no, serial_nos[0])
+		self.assertEqual(pos_return.get("items")[0].serial_no, serial_nos[0])
 
 	def test_partial_pos_returns(self):
 		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
 
-		se = make_serialized_item(company='_Test Company',
-			target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC')
+		se = make_serialized_item(
+			company="_Test Company",
+			target_warehouse="Stores - _TC",
+			cost_center="Main - _TC",
+			expense_account="Cost of Goods Sold - _TC",
+		)
 
 		serial_nos = get_serial_nos(se.get("items")[0].serial_no)
 
-		pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC',
-			account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
-			expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
-			item=se.get("items")[0].item_code, qty=2, rate=1000, do_not_save=1)
+		pos = create_pos_invoice(
+			company="_Test Company",
+			debit_to="Debtors - _TC",
+			account_for_change_amount="Cash - _TC",
+			warehouse="Stores - _TC",
+			income_account="Sales - _TC",
+			expense_account="Cost of Goods Sold - _TC",
+			cost_center="Main - _TC",
+			item=se.get("items")[0].item_code,
+			qty=2,
+			rate=1000,
+			do_not_save=1,
+		)
 
 		pos.get("items")[0].serial_no = serial_nos[0] + "\n" + serial_nos[1]
-		pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 1000, 'default': 1})
+		pos.append(
+			"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 1000, "default": 1}
+		)
 
 		pos.insert()
 		pos.submit()
@@ -263,24 +316,34 @@
 		pos_return1 = make_sales_return(pos.name)
 
 		# partial return 1
-		pos_return1.get('items')[0].qty = -1
-		pos_return1.get('items')[0].serial_no = serial_nos[0]
+		pos_return1.get("items")[0].qty = -1
+		pos_return1.get("items")[0].serial_no = serial_nos[0]
 		pos_return1.insert()
 		pos_return1.submit()
 
 		# partial return 2
 		pos_return2 = make_sales_return(pos.name)
-		self.assertEqual(pos_return2.get('items')[0].qty, -1)
-		self.assertEqual(pos_return2.get('items')[0].serial_no, serial_nos[1])
+		self.assertEqual(pos_return2.get("items")[0].qty, -1)
+		self.assertEqual(pos_return2.get("items")[0].serial_no, serial_nos[1])
 
 	def test_pos_change_amount(self):
-		pos = create_pos_invoice(company= "_Test Company", debit_to="Debtors - _TC",
-			income_account = "Sales - _TC", expense_account = "Cost of Goods Sold - _TC", rate=105,
-			cost_center = "Main - _TC", do_not_save=True)
+		pos = create_pos_invoice(
+			company="_Test Company",
+			debit_to="Debtors - _TC",
+			income_account="Sales - _TC",
+			expense_account="Cost of Goods Sold - _TC",
+			rate=105,
+			cost_center="Main - _TC",
+			do_not_save=True,
+		)
 
-		pos.set('payments', [])
-		pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 50})
-		pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 60, 'default': 1})
+		pos.set("payments", [])
+		pos.append(
+			"payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - _TC", "amount": 50}
+		)
+		pos.append(
+			"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 60, "default": 1}
+		)
 
 		pos.insert()
 		pos.submit()
@@ -298,29 +361,53 @@
 		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
 
-		se = make_serialized_item(company='_Test Company',
-			target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC')
+		se = make_serialized_item(
+			company="_Test Company",
+			target_warehouse="Stores - _TC",
+			cost_center="Main - _TC",
+			expense_account="Cost of Goods Sold - _TC",
+		)
 
 		serial_nos = get_serial_nos(se.get("items")[0].serial_no)
 
-		pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC',
-			account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
-			expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
-			item=se.get("items")[0].item_code, rate=1000, do_not_save=1)
+		pos = create_pos_invoice(
+			company="_Test Company",
+			debit_to="Debtors - _TC",
+			account_for_change_amount="Cash - _TC",
+			warehouse="Stores - _TC",
+			income_account="Sales - _TC",
+			expense_account="Cost of Goods Sold - _TC",
+			cost_center="Main - _TC",
+			item=se.get("items")[0].item_code,
+			rate=1000,
+			do_not_save=1,
+		)
 
 		pos.get("items")[0].serial_no = serial_nos[0]
-		pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000})
+		pos.append(
+			"payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - _TC", "amount": 1000}
+		)
 
 		pos.insert()
 		pos.submit()
 
-		pos2 = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC',
-			account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
-			expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
-			item=se.get("items")[0].item_code, rate=1000, do_not_save=1)
+		pos2 = create_pos_invoice(
+			company="_Test Company",
+			debit_to="Debtors - _TC",
+			account_for_change_amount="Cash - _TC",
+			warehouse="Stores - _TC",
+			income_account="Sales - _TC",
+			expense_account="Cost of Goods Sold - _TC",
+			cost_center="Main - _TC",
+			item=se.get("items")[0].item_code,
+			rate=1000,
+			do_not_save=1,
+		)
 
 		pos2.get("items")[0].serial_no = serial_nos[0]
-		pos2.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000})
+		pos2.append(
+			"payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - _TC", "amount": 1000}
+		)
 
 		pos2.insert()
 		self.assertRaises(frappe.ValidationError, pos2.submit)
@@ -329,27 +416,50 @@
 		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
 
-		se = make_serialized_item(company='_Test Company',
-			target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC')
+		se = make_serialized_item(
+			company="_Test Company",
+			target_warehouse="Stores - _TC",
+			cost_center="Main - _TC",
+			expense_account="Cost of Goods Sold - _TC",
+		)
 
 		serial_nos = get_serial_nos(se.get("items")[0].serial_no)
 
-		si = create_sales_invoice(company='_Test Company', debit_to='Debtors - _TC',
-			account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
-			expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
-			item=se.get("items")[0].item_code, rate=1000, do_not_save=1)
+		si = create_sales_invoice(
+			company="_Test Company",
+			debit_to="Debtors - _TC",
+			account_for_change_amount="Cash - _TC",
+			warehouse="Stores - _TC",
+			income_account="Sales - _TC",
+			expense_account="Cost of Goods Sold - _TC",
+			cost_center="Main - _TC",
+			item=se.get("items")[0].item_code,
+			rate=1000,
+			do_not_save=1,
+		)
 
 		si.get("items")[0].serial_no = serial_nos[0]
+		si.update_stock = 1
 		si.insert()
 		si.submit()
 
-		pos2 = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC',
-			account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
-			expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
-			item=se.get("items")[0].item_code, rate=1000, do_not_save=1)
+		pos2 = create_pos_invoice(
+			company="_Test Company",
+			debit_to="Debtors - _TC",
+			account_for_change_amount="Cash - _TC",
+			warehouse="Stores - _TC",
+			income_account="Sales - _TC",
+			expense_account="Cost of Goods Sold - _TC",
+			cost_center="Main - _TC",
+			item=se.get("items")[0].item_code,
+			rate=1000,
+			do_not_save=1,
+		)
 
 		pos2.get("items")[0].serial_no = serial_nos[0]
-		pos2.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000})
+		pos2.append(
+			"payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - _TC", "amount": 1000}
+		)
 
 		pos2.insert()
 		self.assertRaises(frappe.ValidationError, pos2.submit)
@@ -357,17 +467,30 @@
 	def test_invalid_serial_no_validation(self):
 		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
 
-		se = make_serialized_item(company='_Test Company',
-			target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC')
-		serial_nos = se.get("items")[0].serial_no + 'wrong'
+		se = make_serialized_item(
+			company="_Test Company",
+			target_warehouse="Stores - _TC",
+			cost_center="Main - _TC",
+			expense_account="Cost of Goods Sold - _TC",
+		)
+		serial_nos = se.get("items")[0].serial_no + "wrong"
 
-		pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC',
-			account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
-			expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
-			item=se.get("items")[0].item_code, rate=1000, qty=2, do_not_save=1)
+		pos = create_pos_invoice(
+			company="_Test Company",
+			debit_to="Debtors - _TC",
+			account_for_change_amount="Cash - _TC",
+			warehouse="Stores - _TC",
+			income_account="Sales - _TC",
+			expense_account="Cost of Goods Sold - _TC",
+			cost_center="Main - _TC",
+			item=se.get("items")[0].item_code,
+			rate=1000,
+			qty=2,
+			do_not_save=1,
+		)
 
-		pos.get('items')[0].has_serial_no = 1
-		pos.get('items')[0].serial_no = serial_nos
+		pos.get("items")[0].has_serial_no = 1
+		pos.get("items")[0].serial_no = serial_nos
 		pos.insert()
 
 		self.assertRaises(frappe.ValidationError, pos.submit)
@@ -379,20 +502,31 @@
 		from erpnext.accounts.doctype.loyalty_program.test_loyalty_program import create_records
 
 		create_records()
-		frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
-		before_lp_details = get_loyalty_program_details_with_points("Test Loyalty Customer", company="_Test Company", loyalty_program="Test Single Loyalty")
+		frappe.db.set_value(
+			"Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty"
+		)
+		before_lp_details = get_loyalty_program_details_with_points(
+			"Test Loyalty Customer", company="_Test Company", loyalty_program="Test Single Loyalty"
+		)
 
 		inv = create_pos_invoice(customer="Test Loyalty Customer", rate=10000)
 
-		lpe = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'POS Invoice', 'invoice': inv.name, 'customer': inv.customer})
-		after_lp_details = get_loyalty_program_details_with_points(inv.customer, company=inv.company, loyalty_program=inv.loyalty_program)
+		lpe = frappe.get_doc(
+			"Loyalty Point Entry",
+			{"invoice_type": "POS Invoice", "invoice": inv.name, "customer": inv.customer},
+		)
+		after_lp_details = get_loyalty_program_details_with_points(
+			inv.customer, company=inv.company, loyalty_program=inv.loyalty_program
+		)
 
-		self.assertEqual(inv.get('loyalty_program'), "Test Single Loyalty")
+		self.assertEqual(inv.get("loyalty_program"), "Test Single Loyalty")
 		self.assertEqual(lpe.loyalty_points, 10)
 		self.assertEqual(after_lp_details.loyalty_points, before_lp_details.loyalty_points + 10)
 
 		inv.cancel()
-		after_cancel_lp_details = get_loyalty_program_details_with_points(inv.customer, company=inv.company, loyalty_program=inv.loyalty_program)
+		after_cancel_lp_details = get_loyalty_program_details_with_points(
+			inv.customer, company=inv.company, loyalty_program=inv.loyalty_program
+		)
 		self.assertEqual(after_cancel_lp_details.loyalty_points, before_lp_details.loyalty_points)
 
 	def test_loyalty_points_redeemption(self):
@@ -403,17 +537,24 @@
 		# add 10 loyalty points
 		create_pos_invoice(customer="Test Loyalty Customer", rate=10000)
 
-		before_lp_details = get_loyalty_program_details_with_points("Test Loyalty Customer", company="_Test Company", loyalty_program="Test Single Loyalty")
+		before_lp_details = get_loyalty_program_details_with_points(
+			"Test Loyalty Customer", company="_Test Company", loyalty_program="Test Single Loyalty"
+		)
 
 		inv = create_pos_invoice(customer="Test Loyalty Customer", rate=10000, do_not_save=1)
 		inv.redeem_loyalty_points = 1
 		inv.loyalty_points = before_lp_details.loyalty_points
 		inv.loyalty_amount = inv.loyalty_points * before_lp_details.conversion_factor
-		inv.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 10000 - inv.loyalty_amount})
+		inv.append(
+			"payments",
+			{"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 10000 - inv.loyalty_amount},
+		)
 		inv.paid_amount = 10000
 		inv.submit()
 
-		after_redeem_lp_details = get_loyalty_program_details_with_points(inv.customer, company=inv.company, loyalty_program=inv.loyalty_program)
+		after_redeem_lp_details = get_loyalty_program_details_with_points(
+			inv.customer, company=inv.company, loyalty_program=inv.loyalty_program
+		)
 		self.assertEqual(after_redeem_lp_details.loyalty_points, 9)
 
 	def test_merging_into_sales_invoice_with_discount(self):
@@ -427,21 +568,19 @@
 		frappe.db.sql("delete from `tabPOS Invoice`")
 		test_user, pos_profile = init_user_and_profile()
 		pos_inv = create_pos_invoice(rate=300, additional_discount_percentage=10, do_not_submit=1)
-		pos_inv.append('payments', {
-			'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 270
-		})
+		pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 270})
 		pos_inv.submit()
 
 		pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
-		pos_inv2.append('payments', {
-			'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3200
-		})
+		pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
 		pos_inv2.submit()
 
 		consolidate_pos_invoices()
 
 		pos_inv.load_from_db()
-		rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
+		rounded_total = frappe.db.get_value(
+			"Sales Invoice", pos_inv.consolidated_invoice, "rounded_total"
+		)
 		self.assertEqual(rounded_total, 3470)
 
 	def test_merging_into_sales_invoice_with_discount_and_inclusive_tax(self):
@@ -455,38 +594,42 @@
 		frappe.db.sql("delete from `tabPOS Invoice`")
 		test_user, pos_profile = init_user_and_profile()
 		pos_inv = create_pos_invoice(rate=300, do_not_submit=1)
-		pos_inv.append('payments', {
-			'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
-		})
-		pos_inv.append('taxes', {
-			"charge_type": "On Net Total",
-			"account_head": "_Test Account Service Tax - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "Service Tax",
-			"rate": 14,
-			'included_in_print_rate': 1
-		})
+		pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 300})
+		pos_inv.append(
+			"taxes",
+			{
+				"charge_type": "On Net Total",
+				"account_head": "_Test Account Service Tax - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "Service Tax",
+				"rate": 14,
+				"included_in_print_rate": 1,
+			},
+		)
 		pos_inv.submit()
 
 		pos_inv2 = create_pos_invoice(rate=300, qty=2, do_not_submit=1)
 		pos_inv2.additional_discount_percentage = 10
-		pos_inv2.append('payments', {
-			'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 540
-		})
-		pos_inv2.append('taxes', {
-			"charge_type": "On Net Total",
-			"account_head": "_Test Account Service Tax - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "Service Tax",
-			"rate": 14,
-			'included_in_print_rate': 1
-		})
+		pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 540})
+		pos_inv2.append(
+			"taxes",
+			{
+				"charge_type": "On Net Total",
+				"account_head": "_Test Account Service Tax - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "Service Tax",
+				"rate": 14,
+				"included_in_print_rate": 1,
+			},
+		)
 		pos_inv2.submit()
 
 		consolidate_pos_invoices()
 
 		pos_inv.load_from_db()
-		rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
+		rounded_total = frappe.db.get_value(
+			"Sales Invoice", pos_inv.consolidated_invoice, "rounded_total"
+		)
 		self.assertEqual(rounded_total, 840)
 
 	def test_merging_with_validate_selling_price(self):
@@ -506,64 +649,75 @@
 		frappe.db.sql("delete from `tabPOS Invoice`")
 		test_user, pos_profile = init_user_and_profile()
 		pos_inv = create_pos_invoice(item=item, rate=300, do_not_submit=1)
-		pos_inv.append('payments', {
-			'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
-		})
-		pos_inv.append('taxes', {
-			"charge_type": "On Net Total",
-			"account_head": "_Test Account Service Tax - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "Service Tax",
-			"rate": 14,
-			'included_in_print_rate': 1
-		})
+		pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 300})
+		pos_inv.append(
+			"taxes",
+			{
+				"charge_type": "On Net Total",
+				"account_head": "_Test Account Service Tax - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "Service Tax",
+				"rate": 14,
+				"included_in_print_rate": 1,
+			},
+		)
 		self.assertRaises(frappe.ValidationError, pos_inv.submit)
 
 		pos_inv2 = create_pos_invoice(item=item, rate=400, do_not_submit=1)
-		pos_inv2.append('payments', {
-			'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 400
-		})
-		pos_inv2.append('taxes', {
-			"charge_type": "On Net Total",
-			"account_head": "_Test Account Service Tax - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "Service Tax",
-			"rate": 14,
-			'included_in_print_rate': 1
-		})
+		pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 400})
+		pos_inv2.append(
+			"taxes",
+			{
+				"charge_type": "On Net Total",
+				"account_head": "_Test Account Service Tax - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "Service Tax",
+				"rate": 14,
+				"included_in_print_rate": 1,
+			},
+		)
 		pos_inv2.submit()
 
 		consolidate_pos_invoices()
 
 		pos_inv2.load_from_db()
-		rounded_total = frappe.db.get_value("Sales Invoice", pos_inv2.consolidated_invoice, "rounded_total")
+		rounded_total = frappe.db.get_value(
+			"Sales Invoice", pos_inv2.consolidated_invoice, "rounded_total"
+		)
 		self.assertEqual(rounded_total, 400)
 
 	def test_pos_batch_item_qty_validation(self):
 		from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
 			create_batch_item_with_batch,
 		)
-		create_batch_item_with_batch('_BATCH ITEM', 'TestBatch 01')
-		item = frappe.get_doc('Item', '_BATCH ITEM')
-		batch = frappe.get_doc('Batch', 'TestBatch 01')
+
+		create_batch_item_with_batch("_BATCH ITEM", "TestBatch 01")
+		item = frappe.get_doc("Item", "_BATCH ITEM")
+		batch = frappe.get_doc("Batch", "TestBatch 01")
 		batch.submit()
-		item.batch_no = 'TestBatch 01'
+		item.batch_no = "TestBatch 01"
 		item.save()
 
-		se = make_stock_entry(target="_Test Warehouse - _TC", item_code="_BATCH ITEM", qty=2, basic_rate=100, batch_no='TestBatch 01')
+		se = make_stock_entry(
+			target="_Test Warehouse - _TC",
+			item_code="_BATCH ITEM",
+			qty=2,
+			basic_rate=100,
+			batch_no="TestBatch 01",
+		)
 
 		pos_inv1 = create_pos_invoice(item=item.name, rate=300, qty=1, do_not_submit=1)
-		pos_inv1.items[0].batch_no = 'TestBatch 01'
+		pos_inv1.items[0].batch_no = "TestBatch 01"
 		pos_inv1.save()
 		pos_inv1.submit()
 
 		pos_inv2 = create_pos_invoice(item=item.name, rate=300, qty=2, do_not_submit=1)
-		pos_inv2.items[0].batch_no = 'TestBatch 01'
+		pos_inv2.items[0].batch_no = "TestBatch 01"
 		pos_inv2.save()
 
 		self.assertRaises(frappe.ValidationError, pos_inv2.submit)
 
-		#teardown
+		# teardown
 		pos_inv1.reload()
 		pos_inv1.cancel()
 		pos_inv1.delete()
@@ -577,12 +731,14 @@
 	def test_ignore_pricing_rule(self):
 		from erpnext.accounts.doctype.pricing_rule.test_pricing_rule import make_pricing_rule
 
-		item_price = frappe.get_doc({
-			'doctype': 'Item Price',
-			'item_code': '_Test Item',
-			'price_list': '_Test Price List',
-			'price_list_rate': '450',
-		})
+		item_price = frappe.get_doc(
+			{
+				"doctype": "Item Price",
+				"item_code": "_Test Item",
+				"price_list": "_Test Price List",
+				"price_list_rate": "450",
+			}
+		)
 		item_price.insert()
 		pr = make_pricing_rule(selling=1, priority=5, discount_percentage=10)
 		pr.save()
@@ -610,6 +766,76 @@
 			pos_inv.delete()
 			pr.delete()
 
+	def test_delivered_serial_no_case(self):
+		from erpnext.accounts.doctype.pos_invoice_merge_log.test_pos_invoice_merge_log import (
+			init_user_and_profile,
+		)
+		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+		from erpnext.stock.doctype.serial_no.test_serial_no import get_serial_nos
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
+
+		frappe.db.savepoint("before_test_delivered_serial_no_case")
+		try:
+			se = make_serialized_item()
+			serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]
+
+			dn = create_delivery_note(item_code="_Test Serialized Item With Series", serial_no=serial_no)
+
+			delivery_document_no = frappe.db.get_value("Serial No", serial_no, "delivery_document_no")
+			self.assertEquals(delivery_document_no, dn.name)
+
+			init_user_and_profile()
+
+			pos_inv = create_pos_invoice(
+				item_code="_Test Serialized Item With Series",
+				serial_no=serial_no,
+				qty=1,
+				rate=100,
+				do_not_submit=True,
+			)
+
+			self.assertRaises(frappe.ValidationError, pos_inv.submit)
+
+		finally:
+			frappe.db.rollback(save_point="before_test_delivered_serial_no_case")
+			frappe.set_user("Administrator")
+
+	def test_returned_serial_no_case(self):
+		from erpnext.accounts.doctype.pos_invoice_merge_log.test_pos_invoice_merge_log import (
+			init_user_and_profile,
+		)
+		from erpnext.stock.doctype.serial_no.serial_no import get_pos_reserved_serial_nos
+		from erpnext.stock.doctype.serial_no.test_serial_no import get_serial_nos
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
+
+		frappe.db.savepoint("before_test_returned_serial_no_case")
+		try:
+			se = make_serialized_item()
+			serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]
+
+			init_user_and_profile()
+
+			pos_inv = create_pos_invoice(
+				item_code="_Test Serialized Item With Series",
+				serial_no=serial_no,
+				qty=1,
+				rate=100,
+			)
+
+			pos_return = make_sales_return(pos_inv.name)
+			pos_return.flags.ignore_validate = True
+			pos_return.insert()
+			pos_return.submit()
+
+			pos_reserved_serial_nos = get_pos_reserved_serial_nos(
+				{"item_code": "_Test Serialized Item With Series", "warehouse": "_Test Warehouse - _TC"}
+			)
+			self.assertTrue(serial_no not in pos_reserved_serial_nos)
+
+		finally:
+			frappe.db.rollback(save_point="before_test_returned_serial_no_case")
+			frappe.set_user("Administrator")
+
 
 def create_pos_invoice(**args):
 	args = frappe._dict(args)
@@ -633,23 +859,26 @@
 	pos_inv.debit_to = args.debit_to or "Debtors - _TC"
 	pos_inv.is_return = args.is_return
 	pos_inv.return_against = args.return_against
-	pos_inv.currency=args.currency or "INR"
+	pos_inv.currency = args.currency or "INR"
 	pos_inv.conversion_rate = args.conversion_rate or 1
 	pos_inv.account_for_change_amount = args.account_for_change_amount or "Cash - _TC"
 
 	pos_inv.set_missing_values()
 
-	pos_inv.append("items", {
-		"item_code": args.item or args.item_code or "_Test Item",
-		"warehouse": args.warehouse or "_Test Warehouse - _TC",
-		"qty": args.qty or 1,
-		"rate": args.rate if args.get("rate") is not None else 100,
-		"income_account": args.income_account or "Sales - _TC",
-		"expense_account": args.expense_account or "Cost of Goods Sold - _TC",
-		"cost_center": args.cost_center or "_Test Cost Center - _TC",
-		"serial_no": args.serial_no,
-		"batch_no": args.batch_no
-	})
+	pos_inv.append(
+		"items",
+		{
+			"item_code": args.item or args.item_code or "_Test Item",
+			"warehouse": args.warehouse or "_Test Warehouse - _TC",
+			"qty": args.qty or 1,
+			"rate": args.rate if args.get("rate") is not None else 100,
+			"income_account": args.income_account or "Sales - _TC",
+			"expense_account": args.expense_account or "Cost of Goods Sold - _TC",
+			"cost_center": args.cost_center or "_Test Cost Center - _TC",
+			"serial_no": args.serial_no,
+			"batch_no": args.batch_no,
+		},
+	)
 
 	if not args.do_not_save:
 		pos_inv.insert()
@@ -662,7 +891,9 @@
 
 	return pos_inv
 
+
 def make_batch_item(item_name):
 	from erpnext.stock.doctype.item.test_item import make_item
+
 	if not frappe.db.exists(item_name):
-		return make_item(item_name, dict(has_batch_no = 1, create_new_batch = 1, is_stock_item=1))
\ No newline at end of file
+		return make_item(item_name, dict(has_batch_no=1, create_new_batch=1, is_stock_item=1))
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 ddca68a..d3a81fe 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
@@ -20,32 +20,44 @@
 		self.validate_pos_invoice_status()
 
 	def validate_customer(self):
-		if self.merge_invoices_based_on == 'Customer Group':
+		if self.merge_invoices_based_on == "Customer Group":
 			return
 
 		for d in self.pos_invoices:
 			if d.customer != self.customer:
-				frappe.throw(_("Row #{}: POS Invoice {} is not against customer {}").format(d.idx, d.pos_invoice, self.customer))
+				frappe.throw(
+					_("Row #{}: POS Invoice {} is not against customer {}").format(
+						d.idx, d.pos_invoice, self.customer
+					)
+				)
 
 	def validate_pos_invoice_status(self):
 		for d in self.pos_invoices:
 			status, docstatus, is_return, return_against = frappe.db.get_value(
-				'POS Invoice', d.pos_invoice, ['status', 'docstatus', 'is_return', 'return_against'])
+				"POS Invoice", d.pos_invoice, ["status", "docstatus", "is_return", "return_against"]
+			)
 
 			bold_pos_invoice = frappe.bold(d.pos_invoice)
 			bold_status = frappe.bold(status)
 			if docstatus != 1:
 				frappe.throw(_("Row #{}: POS Invoice {} is not submitted yet").format(d.idx, bold_pos_invoice))
 			if status == "Consolidated":
-				frappe.throw(_("Row #{}: POS Invoice {} has been {}").format(d.idx, bold_pos_invoice, bold_status))
-			if is_return and return_against and return_against not in [d.pos_invoice for d in self.pos_invoices]:
+				frappe.throw(
+					_("Row #{}: POS Invoice {} has been {}").format(d.idx, bold_pos_invoice, bold_status)
+				)
+			if (
+				is_return
+				and return_against
+				and return_against not in [d.pos_invoice for d in self.pos_invoices]
+			):
 				bold_return_against = frappe.bold(return_against)
-				return_against_status = frappe.db.get_value('POS Invoice', return_against, "status")
+				return_against_status = frappe.db.get_value("POS Invoice", return_against, "status")
 				if return_against_status != "Consolidated":
 					# if return entry is not getting merged in the current pos closing and if it is not consolidated
 					bold_unconsolidated = frappe.bold("not Consolidated")
-					msg = (_("Row #{}: Original Invoice {} of return invoice {} is {}.")
-								.format(d.idx, bold_return_against, bold_pos_invoice, bold_unconsolidated))
+					msg = _("Row #{}: Original Invoice {} of return invoice {} is {}.").format(
+						d.idx, bold_return_against, bold_pos_invoice, bold_unconsolidated
+					)
 					msg += " "
 					msg += _("Original invoice should be consolidated before or along with the return invoice.")
 					msg += "<br><br>"
@@ -53,10 +65,12 @@
 					frappe.throw(msg)
 
 	def on_submit(self):
-		pos_invoice_docs = [frappe.get_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
+		pos_invoice_docs = [
+			frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices
+		]
 
-		returns = [d for d in pos_invoice_docs if d.get('is_return') == 1]
-		sales = [d for d in pos_invoice_docs if d.get('is_return') == 0]
+		returns = [d for d in pos_invoice_docs if d.get("is_return") == 1]
+		sales = [d for d in pos_invoice_docs if d.get("is_return") == 0]
 
 		sales_invoice, credit_note = "", ""
 		if returns:
@@ -65,12 +79,14 @@
 		if sales:
 			sales_invoice = self.process_merging_into_sales_invoice(sales)
 
-		self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log
+		self.save()  # save consolidated_sales_invoice & consolidated_credit_note ref in merge log
 
 		self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note)
 
 	def on_cancel(self):
-		pos_invoice_docs = [frappe.get_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
+		pos_invoice_docs = [
+			frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices
+		]
 
 		self.update_pos_invoices(pos_invoice_docs)
 		self.cancel_linked_invoices()
@@ -84,20 +100,12 @@
 		sales_invoice.set_posting_time = 1
 		sales_invoice.posting_date = getdate(self.posting_date)
 		sales_invoice.save()
-		self.write_off_fractional_amount(sales_invoice, data)
 		sales_invoice.submit()
 
 		self.consolidated_invoice = sales_invoice.name
 
 		return sales_invoice.name
 
-	def write_off_fractional_amount(self, invoice, data):
-		pos_invoice_grand_total = sum(d.grand_total for d in data)
-
-		if abs(pos_invoice_grand_total - invoice.grand_total) < 1:
-			invoice.write_off_amount += -1 * (pos_invoice_grand_total - invoice.grand_total)
-			invoice.save()
-
 	def process_merging_into_credit_note(self, data):
 		credit_note = self.get_new_sales_invoice()
 		credit_note.is_return = 1
@@ -110,7 +118,6 @@
 		# TODO: return could be against multiple sales invoice which could also have been consolidated?
 		# credit_note.return_against = self.consolidated_invoice
 		credit_note.save()
-		self.write_off_fractional_amount(credit_note, data)
 		credit_note.submit()
 
 		self.consolidated_credit_note = credit_note.name
@@ -127,9 +134,8 @@
 
 		loyalty_amount_sum, loyalty_points_sum, idx = 0, 0, 1
 
-
 		for doc in data:
-			map_doc(doc, invoice, table_map={ "doctype": invoice.doctype })
+			map_doc(doc, invoice, table_map={"doctype": invoice.doctype})
 
 			if doc.redeem_loyalty_points:
 				invoice.loyalty_redemption_account = doc.loyalty_redemption_account
@@ -137,11 +143,17 @@
 				loyalty_points_sum += doc.loyalty_points
 				loyalty_amount_sum += doc.loyalty_amount
 
-			for item in doc.get('items'):
+			for item in doc.get("items"):
 				found = False
 				for i in items:
-					if (i.item_code == item.item_code and not i.serial_no and not i.batch_no and
-						i.uom == item.uom and i.net_rate == item.net_rate and i.warehouse == item.warehouse):
+					if (
+						i.item_code == item.item_code
+						and not i.serial_no
+						and not i.batch_no
+						and i.uom == item.uom
+						and i.net_rate == item.net_rate
+						and i.warehouse == item.warehouse
+					):
 						found = True
 						i.qty = i.qty + item.qty
 						i.amount = i.amount + item.net_amount
@@ -157,7 +169,7 @@
 					si_item = map_child_doc(item, invoice, {"doctype": "Sales Invoice Item"})
 					items.append(si_item)
 
-			for tax in doc.get('taxes'):
+			for tax in doc.get("taxes"):
 				found = False
 				for t in taxes:
 					if t.account_head == tax.account_head and t.cost_center == tax.cost_center:
@@ -166,7 +178,7 @@
 						update_item_wise_tax_detail(t, tax)
 						found = True
 				if not found:
-					tax.charge_type = 'Actual'
+					tax.charge_type = "Actual"
 					tax.idx = idx
 					idx += 1
 					tax.included_in_print_rate = 0
@@ -175,7 +187,7 @@
 					tax.item_wise_tax_detail = tax.item_wise_tax_detail
 					taxes.append(tax)
 
-			for payment in doc.get('payments'):
+			for payment in doc.get("payments"):
 				found = False
 				for pay in payments:
 					if pay.account == payment.account and pay.mode_of_payment == payment.mode_of_payment:
@@ -190,52 +202,59 @@
 			base_rounding_adjustment += doc.base_rounding_adjustment
 			base_rounded_total += doc.base_rounded_total
 
-
 		if loyalty_points_sum:
 			invoice.redeem_loyalty_points = 1
 			invoice.loyalty_points = loyalty_points_sum
 			invoice.loyalty_amount = loyalty_amount_sum
 
-		invoice.set('items', items)
-		invoice.set('payments', payments)
-		invoice.set('taxes', taxes)
-		invoice.set('rounding_adjustment',rounding_adjustment)
-		invoice.set('base_rounding_adjustment',base_rounding_adjustment)
-		invoice.set('rounded_total',rounded_total)
-		invoice.set('base_rounded_total',base_rounded_total)
+		invoice.set("items", items)
+		invoice.set("payments", payments)
+		invoice.set("taxes", taxes)
+		invoice.set("rounding_adjustment", rounding_adjustment)
+		invoice.set("base_rounding_adjustment", base_rounding_adjustment)
+		invoice.set("rounded_total", rounded_total)
+		invoice.set("base_rounded_total", base_rounded_total)
 		invoice.additional_discount_percentage = 0
 		invoice.discount_amount = 0.0
 		invoice.taxes_and_charges = None
 		invoice.ignore_pricing_rule = 1
 		invoice.customer = self.customer
 
-		if self.merge_invoices_based_on == 'Customer Group':
+		if self.merge_invoices_based_on == "Customer Group":
 			invoice.flags.ignore_pos_profile = True
-			invoice.pos_profile = ''
+			invoice.pos_profile = ""
 
 		return invoice
 
 	def get_new_sales_invoice(self):
-		sales_invoice = frappe.new_doc('Sales Invoice')
+		sales_invoice = frappe.new_doc("Sales Invoice")
 		sales_invoice.customer = self.customer
 		sales_invoice.is_pos = 1
 
 		return sales_invoice
 
-	def update_pos_invoices(self, invoice_docs, sales_invoice='', credit_note=''):
+	def update_pos_invoices(self, invoice_docs, sales_invoice="", credit_note=""):
 		for doc in invoice_docs:
 			doc.load_from_db()
-			doc.update({ 'consolidated_invoice': None if self.docstatus==2 else (credit_note if doc.is_return else sales_invoice) })
+			doc.update(
+				{
+					"consolidated_invoice": None
+					if self.docstatus == 2
+					else (credit_note if doc.is_return else sales_invoice)
+				}
+			)
 			doc.set_status(update=True)
 			doc.save()
 
 	def cancel_linked_invoices(self):
 		for si_name in [self.consolidated_invoice, self.consolidated_credit_note]:
-			if not si_name: continue
-			si = frappe.get_doc('Sales Invoice', si_name)
+			if not si_name:
+				continue
+			si = frappe.get_doc("Sales Invoice", si_name)
 			si.flags.ignore_validate = True
 			si.cancel()
 
+
 def update_item_wise_tax_detail(consolidate_tax_row, tax_row):
 	consolidated_tax_detail = json.loads(consolidate_tax_row.item_wise_tax_detail)
 	tax_row_detail = json.loads(tax_row.item_wise_tax_detail)
@@ -246,78 +265,150 @@
 	for item_code, tax_data in tax_row_detail.items():
 		if consolidated_tax_detail.get(item_code):
 			consolidated_tax_data = consolidated_tax_detail.get(item_code)
-			consolidated_tax_detail.update({
-				item_code: [consolidated_tax_data[0], consolidated_tax_data[1] + tax_data[1]]
-			})
+			consolidated_tax_detail.update(
+				{item_code: [consolidated_tax_data[0], consolidated_tax_data[1] + tax_data[1]]}
+			)
 		else:
-			consolidated_tax_detail.update({
-				item_code: [tax_data[0], tax_data[1]]
-			})
+			consolidated_tax_detail.update({item_code: [tax_data[0], tax_data[1]]})
 
-	consolidate_tax_row.item_wise_tax_detail = json.dumps(consolidated_tax_detail, separators=(',', ':'))
+	consolidate_tax_row.item_wise_tax_detail = json.dumps(
+		consolidated_tax_detail, separators=(",", ":")
+	)
+
 
 def get_all_unconsolidated_invoices():
 	filters = {
-		'consolidated_invoice': [ 'in', [ '', None ]],
-		'status': ['not in', ['Consolidated']],
-		'docstatus': 1
+		"consolidated_invoice": ["in", ["", None]],
+		"status": ["not in", ["Consolidated"]],
+		"docstatus": 1,
 	}
-	pos_invoices = frappe.db.get_all('POS Invoice', filters=filters,
-		fields=["name as pos_invoice", 'posting_date', 'grand_total', 'customer'])
+	pos_invoices = frappe.db.get_all(
+		"POS Invoice",
+		filters=filters,
+		fields=[
+			"name as pos_invoice",
+			"posting_date",
+			"grand_total",
+			"customer",
+			"is_return",
+			"return_against",
+		],
+	)
 
 	return pos_invoices
 
+
 def get_invoice_customer_map(pos_invoices):
 	# pos_invoice_customer_map = { 'Customer 1': [{}, {}, {}], 'Customer 2' : [{}] }
 	pos_invoice_customer_map = {}
 	for invoice in pos_invoices:
-		customer = invoice.get('customer')
+		customer = invoice.get("customer")
 		pos_invoice_customer_map.setdefault(customer, [])
 		pos_invoice_customer_map[customer].append(invoice)
 
 	return pos_invoice_customer_map
 
+
 def consolidate_pos_invoices(pos_invoices=None, closing_entry=None):
-	invoices = pos_invoices or (closing_entry and closing_entry.get('pos_transactions'))
+	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:
-		closing_entry.set_status(update=True, status='Queued')
-		enqueue_job(create_merge_logs, invoice_by_customer=invoice_by_customer, closing_entry=closing_entry)
+		closing_entry.set_status(update=True, status="Queued")
+		enqueue_job(
+			create_merge_logs, invoice_by_customer=invoice_by_customer, closing_entry=closing_entry
+		)
 	else:
 		create_merge_logs(invoice_by_customer, closing_entry)
 
+
 def unconsolidate_pos_invoices(closing_entry):
 	merge_logs = frappe.get_all(
-		'POS Invoice Merge Log',
-		filters={ 'pos_closing_entry': closing_entry.name },
-		pluck='name'
+		"POS Invoice Merge Log", filters={"pos_closing_entry": closing_entry.name}, pluck="name"
 	)
 
 	if len(merge_logs) >= 10:
-		closing_entry.set_status(update=True, status='Queued')
+		closing_entry.set_status(update=True, status="Queued")
 		enqueue_job(cancel_merge_logs, merge_logs=merge_logs, closing_entry=closing_entry)
 	else:
 		cancel_merge_logs(merge_logs, closing_entry)
 
+
+def split_invoices(invoices):
+	"""
+	Splits invoices into multiple groups
+	Use-case:
+	If a serial no is sold and later it is returned
+	then split the invoices such that the selling entry is merged first and then the return entry
+	"""
+	# Input
+	# invoices = [
+	# 	{'pos_invoice': 'Invoice with SR#1 & SR#2', 'is_return': 0},
+	# 	{'pos_invoice': 'Invoice with SR#1', 'is_return': 1},
+	# 	{'pos_invoice': 'Invoice with SR#2', 'is_return': 0}
+	# ]
+	# Output
+	# _invoices = [
+	# 	[{'pos_invoice': 'Invoice with SR#1 & SR#2', 'is_return': 0}],
+	# 	[{'pos_invoice': 'Invoice with SR#1', 'is_return': 1}, {'pos_invoice': 'Invoice with SR#2', 'is_return': 0}],
+	# ]
+
+	_invoices = []
+	special_invoices = []
+	pos_return_docs = [
+		frappe.get_cached_doc("POS Invoice", d.pos_invoice)
+		for d in invoices
+		if d.is_return and d.return_against
+	]
+	for pos_invoice in pos_return_docs:
+		for item in pos_invoice.items:
+			if not item.serial_no:
+				continue
+
+			return_against_is_added = any(
+				d for d in _invoices if d.pos_invoice == pos_invoice.return_against
+			)
+			if return_against_is_added:
+				break
+
+			return_against_is_consolidated = (
+				frappe.db.get_value("POS Invoice", pos_invoice.return_against, "status", cache=True)
+				== "Consolidated"
+			)
+			if return_against_is_consolidated:
+				break
+
+			pos_invoice_row = [d for d in invoices if d.pos_invoice == pos_invoice.return_against]
+			_invoices.append(pos_invoice_row)
+			special_invoices.append(pos_invoice.return_against)
+			break
+
+	_invoices.append([d for d in invoices if d.pos_invoice not in special_invoices])
+
+	return _invoices
+
+
 def create_merge_logs(invoice_by_customer, closing_entry=None):
 	try:
 		for customer, invoices in invoice_by_customer.items():
-			merge_log = frappe.new_doc('POS Invoice Merge Log')
-			merge_log.posting_date = getdate(closing_entry.get('posting_date')) if closing_entry else nowdate()
-			merge_log.customer = customer
-			merge_log.pos_closing_entry = closing_entry.get('name') if closing_entry else None
+			for _invoices in split_invoices(invoices):
+				merge_log = frappe.new_doc("POS Invoice Merge Log")
+				merge_log.posting_date = (
+					getdate(closing_entry.get("posting_date")) if closing_entry else nowdate()
+				)
+				merge_log.customer = customer
+				merge_log.pos_closing_entry = closing_entry.get("name") if closing_entry else None
 
-			merge_log.set('pos_invoices', invoices)
-			merge_log.save(ignore_permissions=True)
-			merge_log.submit()
+				merge_log.set("pos_invoices", _invoices)
+				merge_log.save(ignore_permissions=True)
+				merge_log.submit()
 
 		if closing_entry:
-			closing_entry.set_status(update=True, status='Submitted')
-			closing_entry.db_set('error_message', '')
+			closing_entry.set_status(update=True, status="Submitted")
+			closing_entry.db_set("error_message", "")
 			closing_entry.update_opening_entry()
 
 	except Exception as e:
@@ -326,24 +417,25 @@
 		error_message = safe_load_json(message_log)
 
 		if closing_entry:
-			closing_entry.set_status(update=True, status='Failed')
-			closing_entry.db_set('error_message', error_message)
+			closing_entry.set_status(update=True, status="Failed")
+			closing_entry.db_set("error_message", error_message)
 		raise
 
 	finally:
 		frappe.db.commit()
-		frappe.publish_realtime('closing_process_complete', {'user': frappe.session.user})
+		frappe.publish_realtime("closing_process_complete", {"user": frappe.session.user})
+
 
 def cancel_merge_logs(merge_logs, closing_entry=None):
 	try:
 		for log in merge_logs:
-			merge_log = frappe.get_doc('POS Invoice Merge Log', log)
+			merge_log = frappe.get_doc("POS Invoice Merge Log", log)
 			merge_log.flags.ignore_permissions = True
 			merge_log.cancel()
 
 		if closing_entry:
-			closing_entry.set_status(update=True, status='Cancelled')
-			closing_entry.db_set('error_message', '')
+			closing_entry.set_status(update=True, status="Cancelled")
+			closing_entry.db_set("error_message", "")
 			closing_entry.update_opening_entry(for_cancel=True)
 
 	except Exception as e:
@@ -352,18 +444,19 @@
 		error_message = safe_load_json(message_log)
 
 		if closing_entry:
-			closing_entry.set_status(update=True, status='Submitted')
-			closing_entry.db_set('error_message', error_message)
+			closing_entry.set_status(update=True, status="Submitted")
+			closing_entry.db_set("error_message", error_message)
 		raise
 
 	finally:
 		frappe.db.commit()
-		frappe.publish_realtime('closing_process_complete', {'user': frappe.session.user})
+		frappe.publish_realtime("closing_process_complete", {"user": frappe.session.user})
+
 
 def enqueue_job(job, **kwargs):
 	check_scheduler_status()
 
-	closing_entry = kwargs.get('closing_entry') or {}
+	closing_entry = kwargs.get("closing_entry") or {}
 
 	job_name = closing_entry.get("name")
 	if not job_already_enqueued(job_name):
@@ -378,24 +471,27 @@
 		)
 
 		if job == create_merge_logs:
-			msg = _('POS Invoices will be consolidated in a background process')
+			msg = _("POS Invoices will be consolidated in a background process")
 		else:
-			msg = _('POS Invoices will be unconsolidated in a background process')
+			msg = _("POS Invoices will be unconsolidated in a background process")
 
 		frappe.msgprint(msg, alert=1)
 
+
 def check_scheduler_status():
 	if is_scheduler_inactive() and not frappe.flags.in_test:
 		frappe.throw(_("Scheduler is inactive. Cannot enqueue job."), title=_("Scheduler Inactive"))
 
+
 def job_already_enqueued(job_name):
 	enqueued_jobs = [d.get("job_name") for d in get_info()]
 	if job_name in enqueued_jobs:
 		return True
 
+
 def safe_load_json(message):
 	try:
-		json_message = json.loads(message).get('message')
+		json_message = json.loads(message).get("message")
 	except Exception:
 		json_message = message
 
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py
index 5930aa0..9e696f1 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py
@@ -5,6 +5,7 @@
 import unittest
 
 import frappe
+from frappe.tests.utils import change_settings
 
 from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
 from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
@@ -23,21 +24,19 @@
 			test_user, pos_profile = init_user_and_profile()
 
 			pos_inv = create_pos_invoice(rate=300, do_not_submit=1)
-			pos_inv.append('payments', {
-				'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
-			})
+			pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 300})
 			pos_inv.submit()
 
 			pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
-			pos_inv2.append('payments', {
-				'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3200
-			})
+			pos_inv2.append(
+				"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200}
+			)
 			pos_inv2.submit()
 
 			pos_inv3 = create_pos_invoice(customer="_Test Customer 2", rate=2300, do_not_submit=1)
-			pos_inv3.append('payments', {
-				'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 2300
-			})
+			pos_inv3.append(
+				"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 2300}
+			)
 			pos_inv3.submit()
 
 			consolidate_pos_invoices()
@@ -62,28 +61,29 @@
 			test_user, pos_profile = init_user_and_profile()
 
 			pos_inv = create_pos_invoice(rate=300, do_not_submit=1)
-			pos_inv.append('payments', {
-				'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
-			})
+			pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 300})
 			pos_inv.submit()
 
 			pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
-			pos_inv2.append('payments', {
-				'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3200
-			})
+			pos_inv2.append(
+				"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200}
+			)
 			pos_inv2.submit()
 
 			pos_inv3 = create_pos_invoice(customer="_Test Customer 2", rate=2300, do_not_submit=1)
-			pos_inv3.append('payments', {
-				'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 2300
-			})
+			pos_inv3.append(
+				"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 2300}
+			)
 			pos_inv3.submit()
 
 			pos_inv_cn = make_sales_return(pos_inv.name)
 			pos_inv_cn.set("payments", [])
-			pos_inv_cn.append('payments', {
-				'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': -300
-			})
+			pos_inv_cn.append(
+				"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": -100}
+			)
+			pos_inv_cn.append(
+				"payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - _TC", "amount": -200}
+			)
 			pos_inv_cn.paid_amount = -300
 			pos_inv_cn.submit()
 
@@ -97,7 +97,12 @@
 
 			pos_inv_cn.load_from_db()
 			self.assertTrue(frappe.db.exists("Sales Invoice", pos_inv_cn.consolidated_invoice))
-			self.assertTrue(frappe.db.get_value("Sales Invoice", pos_inv_cn.consolidated_invoice, "is_return"))
+			consolidated_credit_note = frappe.get_doc("Sales Invoice", pos_inv_cn.consolidated_invoice)
+			self.assertEqual(consolidated_credit_note.is_return, 1)
+			self.assertEqual(consolidated_credit_note.payments[0].mode_of_payment, "Cash")
+			self.assertEqual(consolidated_credit_note.payments[0].amount, -100)
+			self.assertEqual(consolidated_credit_note.payments[1].mode_of_payment, "Bank Draft")
+			self.assertEqual(consolidated_credit_note.payments[1].amount, -200)
 
 		finally:
 			frappe.set_user("Administrator")
@@ -110,41 +115,47 @@
 		try:
 			inv = create_pos_invoice(qty=1, rate=100, do_not_save=True)
 
-			inv.append("taxes", {
-				"account_head": "_Test Account VAT - _TC",
-				"charge_type": "On Net Total",
-				"cost_center": "_Test Cost Center - _TC",
-				"description": "VAT",
-				"doctype": "Sales Taxes and Charges",
-				"rate": 9
-			})
+			inv.append(
+				"taxes",
+				{
+					"account_head": "_Test Account VAT - _TC",
+					"charge_type": "On Net Total",
+					"cost_center": "_Test Cost Center - _TC",
+					"description": "VAT",
+					"doctype": "Sales Taxes and Charges",
+					"rate": 9,
+				},
+			)
 			inv.insert()
 			inv.submit()
 
 			inv2 = create_pos_invoice(qty=1, rate=100, do_not_save=True)
-			inv2.get('items')[0].item_code = '_Test Item 2'
-			inv2.append("taxes", {
-				"account_head": "_Test Account VAT - _TC",
-				"charge_type": "On Net Total",
-				"cost_center": "_Test Cost Center - _TC",
-				"description": "VAT",
-				"doctype": "Sales Taxes and Charges",
-				"rate": 5
-			})
+			inv2.get("items")[0].item_code = "_Test Item 2"
+			inv2.append(
+				"taxes",
+				{
+					"account_head": "_Test Account VAT - _TC",
+					"charge_type": "On Net Total",
+					"cost_center": "_Test Cost Center - _TC",
+					"description": "VAT",
+					"doctype": "Sales Taxes and Charges",
+					"rate": 5,
+				},
+			)
 			inv2.insert()
 			inv2.submit()
 
 			consolidate_pos_invoices()
 			inv.load_from_db()
 
-			consolidated_invoice = frappe.get_doc('Sales Invoice', inv.consolidated_invoice)
-			item_wise_tax_detail = json.loads(consolidated_invoice.get('taxes')[0].item_wise_tax_detail)
+			consolidated_invoice = frappe.get_doc("Sales Invoice", inv.consolidated_invoice)
+			item_wise_tax_detail = json.loads(consolidated_invoice.get("taxes")[0].item_wise_tax_detail)
 
-			tax_rate, amount = item_wise_tax_detail.get('_Test Item')
+			tax_rate, amount = item_wise_tax_detail.get("_Test Item")
 			self.assertEqual(tax_rate, 9)
 			self.assertEqual(amount, 9)
 
-			tax_rate2, amount2 = item_wise_tax_detail.get('_Test Item 2')
+			tax_rate2, amount2 = item_wise_tax_detail.get("_Test Item 2")
 			self.assertEqual(tax_rate2, 5)
 			self.assertEqual(amount2, 5)
 		finally:
@@ -152,11 +163,10 @@
 			frappe.db.sql("delete from `tabPOS Profile`")
 			frappe.db.sql("delete from `tabPOS Invoice`")
 
-
 	def test_consolidation_round_off_error_1(self):
-		'''
+		"""
 		Test round off error in consolidated invoice creation if POS Invoice has inclusive tax
-		'''
+		"""
 
 		frappe.db.sql("delete from `tabPOS Invoice`")
 
@@ -171,43 +181,45 @@
 			init_user_and_profile()
 
 			inv = create_pos_invoice(qty=3, rate=10000, do_not_save=True)
-			inv.append("taxes", {
-				"account_head": "_Test Account VAT - _TC",
-				"charge_type": "On Net Total",
-				"cost_center": "_Test Cost Center - _TC",
-				"description": "VAT",
-				"doctype": "Sales Taxes and Charges",
-				"rate": 7.5,
-				"included_in_print_rate": 1
-			})
-			inv.append('payments', {
-				'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 30000
-			})
+			inv.append(
+				"taxes",
+				{
+					"account_head": "_Test Account VAT - _TC",
+					"charge_type": "On Net Total",
+					"cost_center": "_Test Cost Center - _TC",
+					"description": "VAT",
+					"doctype": "Sales Taxes and Charges",
+					"rate": 7.5,
+					"included_in_print_rate": 1,
+				},
+			)
+			inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 30000})
 			inv.insert()
 			inv.submit()
 
 			inv2 = create_pos_invoice(qty=3, rate=10000, do_not_save=True)
-			inv2.append("taxes", {
-				"account_head": "_Test Account VAT - _TC",
-				"charge_type": "On Net Total",
-				"cost_center": "_Test Cost Center - _TC",
-				"description": "VAT",
-				"doctype": "Sales Taxes and Charges",
-				"rate": 7.5,
-				"included_in_print_rate": 1
-			})
-			inv2.append('payments', {
-				'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 30000
-			})
+			inv2.append(
+				"taxes",
+				{
+					"account_head": "_Test Account VAT - _TC",
+					"charge_type": "On Net Total",
+					"cost_center": "_Test Cost Center - _TC",
+					"description": "VAT",
+					"doctype": "Sales Taxes and Charges",
+					"rate": 7.5,
+					"included_in_print_rate": 1,
+				},
+			)
+			inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 30000})
 			inv2.insert()
 			inv2.submit()
 
 			consolidate_pos_invoices()
 
 			inv.load_from_db()
-			consolidated_invoice = frappe.get_doc('Sales Invoice', inv.consolidated_invoice)
+			consolidated_invoice = frappe.get_doc("Sales Invoice", inv.consolidated_invoice)
 			self.assertEqual(consolidated_invoice.outstanding_amount, 0)
-			self.assertEqual(consolidated_invoice.status, 'Paid')
+			self.assertEqual(consolidated_invoice.status, "Paid")
 
 		finally:
 			frappe.set_user("Administrator")
@@ -215,9 +227,9 @@
 			frappe.db.sql("delete from `tabPOS Invoice`")
 
 	def test_consolidation_round_off_error_2(self):
-		'''
+		"""
 		Test the same case as above but with an Unpaid POS Invoice
-		'''
+		"""
 		frappe.db.sql("delete from `tabPOS Invoice`")
 
 		try:
@@ -231,50 +243,207 @@
 			init_user_and_profile()
 
 			inv = create_pos_invoice(qty=6, rate=10000, do_not_save=True)
-			inv.append("taxes", {
-				"account_head": "_Test Account VAT - _TC",
-				"charge_type": "On Net Total",
-				"cost_center": "_Test Cost Center - _TC",
-				"description": "VAT",
-				"doctype": "Sales Taxes and Charges",
-				"rate": 7.5,
-				"included_in_print_rate": 1
-			})
-			inv.append('payments', {
-				'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 60000
-			})
+			inv.append(
+				"taxes",
+				{
+					"account_head": "_Test Account VAT - _TC",
+					"charge_type": "On Net Total",
+					"cost_center": "_Test Cost Center - _TC",
+					"description": "VAT",
+					"doctype": "Sales Taxes and Charges",
+					"rate": 7.5,
+					"included_in_print_rate": 1,
+				},
+			)
+			inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 60000})
 			inv.insert()
 			inv.submit()
 
 			inv2 = create_pos_invoice(qty=6, rate=10000, do_not_save=True)
-			inv2.append("taxes", {
-				"account_head": "_Test Account VAT - _TC",
-				"charge_type": "On Net Total",
-				"cost_center": "_Test Cost Center - _TC",
-				"description": "VAT",
-				"doctype": "Sales Taxes and Charges",
-				"rate": 7.5,
-				"included_in_print_rate": 1
-			})
-			inv2.append('payments', {
-				'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 60000
-			})
+			inv2.append(
+				"taxes",
+				{
+					"account_head": "_Test Account VAT - _TC",
+					"charge_type": "On Net Total",
+					"cost_center": "_Test Cost Center - _TC",
+					"description": "VAT",
+					"doctype": "Sales Taxes and Charges",
+					"rate": 7.5,
+					"included_in_print_rate": 1,
+				},
+			)
+			inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 60000})
 			inv2.insert()
 			inv2.submit()
 
 			inv3 = create_pos_invoice(qty=3, rate=600, do_not_save=True)
-			inv3.append('payments', {
-				'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 1000
-			})
+			inv3.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 1000})
 			inv3.insert()
 			inv3.submit()
 
 			consolidate_pos_invoices()
 
 			inv.load_from_db()
-			consolidated_invoice = frappe.get_doc('Sales Invoice', inv.consolidated_invoice)
+			consolidated_invoice = frappe.get_doc("Sales Invoice", inv.consolidated_invoice)
 			self.assertEqual(consolidated_invoice.outstanding_amount, 800)
-			self.assertNotEqual(consolidated_invoice.status, 'Paid')
+			self.assertNotEqual(consolidated_invoice.status, "Paid")
+
+		finally:
+			frappe.set_user("Administrator")
+			frappe.db.sql("delete from `tabPOS Profile`")
+			frappe.db.sql("delete from `tabPOS Invoice`")
+
+	@change_settings(
+		"System Settings", {"number_format": "#,###.###", "currency_precision": 3, "float_precision": 3}
+	)
+	def test_consolidation_round_off_error_3(self):
+		frappe.db.sql("delete from `tabPOS Invoice`")
+
+		try:
+			make_stock_entry(
+				to_warehouse="_Test Warehouse - _TC",
+				item_code="_Test Item",
+				rate=8000,
+				qty=10,
+			)
+			init_user_and_profile()
+
+			item_rates = [69, 59, 29]
+			for i in [1, 2]:
+				inv = create_pos_invoice(is_return=1, do_not_save=1)
+				inv.items = []
+				for rate in item_rates:
+					inv.append(
+						"items",
+						{
+							"item_code": "_Test Item",
+							"warehouse": "_Test Warehouse - _TC",
+							"qty": -1,
+							"rate": rate,
+							"income_account": "Sales - _TC",
+							"expense_account": "Cost of Goods Sold - _TC",
+							"cost_center": "_Test Cost Center - _TC",
+						},
+					)
+				inv.append(
+					"taxes",
+					{
+						"account_head": "_Test Account VAT - _TC",
+						"charge_type": "On Net Total",
+						"cost_center": "_Test Cost Center - _TC",
+						"description": "VAT",
+						"doctype": "Sales Taxes and Charges",
+						"rate": 15,
+						"included_in_print_rate": 1,
+					},
+				)
+				inv.payments = []
+				inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": -157})
+				inv.paid_amount = -157
+				inv.save()
+				inv.submit()
+
+			consolidate_pos_invoices()
+
+			inv.load_from_db()
+			consolidated_invoice = frappe.get_doc("Sales Invoice", inv.consolidated_invoice)
+			self.assertEqual(consolidated_invoice.status, "Return")
+			self.assertEqual(consolidated_invoice.rounding_adjustment, -0.001)
+
+		finally:
+			frappe.set_user("Administrator")
+			frappe.db.sql("delete from `tabPOS Profile`")
+			frappe.db.sql("delete from `tabPOS Invoice`")
+
+	def test_consolidation_rounding_adjustment(self):
+		"""
+		Test if the rounding adjustment is calculated correctly
+		"""
+		frappe.db.sql("delete from `tabPOS Invoice`")
+
+		try:
+			make_stock_entry(
+				to_warehouse="_Test Warehouse - _TC",
+				item_code="_Test Item",
+				rate=8000,
+				qty=10,
+			)
+
+			init_user_and_profile()
+
+			inv = create_pos_invoice(qty=1, rate=69.5, do_not_save=True)
+			inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 70})
+			inv.insert()
+			inv.submit()
+
+			inv2 = create_pos_invoice(qty=1, rate=59.5, do_not_save=True)
+			inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 60})
+			inv2.insert()
+			inv2.submit()
+
+			consolidate_pos_invoices()
+
+			inv.load_from_db()
+			consolidated_invoice = frappe.get_doc("Sales Invoice", inv.consolidated_invoice)
+			self.assertEqual(consolidated_invoice.rounding_adjustment, 1)
+
+		finally:
+			frappe.set_user("Administrator")
+			frappe.db.sql("delete from `tabPOS Profile`")
+			frappe.db.sql("delete from `tabPOS Invoice`")
+
+	def test_serial_no_case_1(self):
+		"""
+		Create a POS Invoice with serial no
+		Create a Return Invoice with serial no
+		Create a POS Invoice with serial no again
+		Consolidate the invoices
+
+		The first POS Invoice should be consolidated with a separate single Merge Log
+		The second and third POS Invoice should be consolidated with a single Merge Log
+		"""
+
+		from erpnext.stock.doctype.serial_no.test_serial_no import get_serial_nos
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
+
+		frappe.db.sql("delete from `tabPOS Invoice`")
+
+		try:
+			se = make_serialized_item()
+			serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]
+
+			init_user_and_profile()
+
+			pos_inv = create_pos_invoice(
+				item_code="_Test Serialized Item With Series",
+				serial_no=serial_no,
+				qty=1,
+				rate=100,
+				do_not_submit=1,
+			)
+			pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100})
+			pos_inv.submit()
+
+			pos_inv_cn = make_sales_return(pos_inv.name)
+			pos_inv_cn.paid_amount = -100
+			pos_inv_cn.submit()
+
+			pos_inv2 = create_pos_invoice(
+				item_code="_Test Serialized Item With Series",
+				serial_no=serial_no,
+				qty=1,
+				rate=100,
+				do_not_submit=1,
+			)
+			pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100})
+			pos_inv2.submit()
+
+			consolidate_pos_invoices()
+
+			pos_inv.load_from_db()
+			pos_inv2.load_from_db()
+
+			self.assertNotEqual(pos_inv.consolidated_invoice, pos_inv2.consolidated_invoice)
 
 		finally:
 			frappe.set_user("Administrator")
diff --git a/erpnext/accounts/doctype/pos_invoice_reference/pos_invoice_reference.json b/erpnext/accounts/doctype/pos_invoice_reference/pos_invoice_reference.json
index 205c4ed..387c4b0 100644
--- a/erpnext/accounts/doctype/pos_invoice_reference/pos_invoice_reference.json
+++ b/erpnext/accounts/doctype/pos_invoice_reference/pos_invoice_reference.json
@@ -9,7 +9,9 @@
   "posting_date",
   "column_break_3",
   "customer",
-  "grand_total"
+  "grand_total",
+  "is_return",
+  "return_against"
  ],
  "fields": [
   {
@@ -48,11 +50,27 @@
    "in_list_view": 1,
    "label": "Amount",
    "reqd": 1
+  },
+  {
+   "default": "0",
+   "fetch_from": "pos_invoice.is_return",
+   "fieldname": "is_return",
+   "fieldtype": "Check",
+   "label": "Is Return",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "pos_invoice.return_against",
+   "fieldname": "return_against",
+   "fieldtype": "Link",
+   "label": "Return Against",
+   "options": "POS Invoice",
+   "read_only": 1
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2020-05-29 15:08:42.194979",
+ "modified": "2022-03-24 13:32:02.366257",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Invoice Reference",
@@ -61,5 +79,6 @@
  "quick_entry": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py
index 0b2e045..3cd1426 100644
--- a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py
+++ b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py
@@ -17,7 +17,9 @@
 
 	def validate_pos_profile_and_cashier(self):
 		if self.company != frappe.db.get_value("POS Profile", self.pos_profile, "company"):
-			frappe.throw(_("POS Profile {} does not belongs to company {}").format(self.pos_profile, self.company))
+			frappe.throw(
+				_("POS Profile {} does not belongs to company {}").format(self.pos_profile, self.company)
+			)
 
 		if not cint(frappe.db.get_value("User", self.user, "enabled")):
 			frappe.throw(_("User {} is disabled. Please select valid user/cashier").format(self.user))
@@ -26,8 +28,11 @@
 		invalid_modes = []
 		for d in self.balance_details:
 			if d.mode_of_payment:
-				account = frappe.db.get_value("Mode of Payment Account",
-					{"parent": d.mode_of_payment, "company": self.company}, "default_account")
+				account = frappe.db.get_value(
+					"Mode of Payment Account",
+					{"parent": d.mode_of_payment, "company": self.company},
+					"default_account",
+				)
 				if not account:
 					invalid_modes.append(get_link_to_form("Mode of Payment", d.mode_of_payment))
 
diff --git a/erpnext/accounts/doctype/pos_opening_entry/test_pos_opening_entry.py b/erpnext/accounts/doctype/pos_opening_entry/test_pos_opening_entry.py
index 105d53d..64c658a 100644
--- a/erpnext/accounts/doctype/pos_opening_entry/test_pos_opening_entry.py
+++ b/erpnext/accounts/doctype/pos_opening_entry/test_pos_opening_entry.py
@@ -9,6 +9,7 @@
 class TestPOSOpeningEntry(unittest.TestCase):
 	pass
 
+
 def create_opening_entry(pos_profile, user):
 	entry = frappe.new_doc("POS Opening Entry")
 	entry.pos_profile = pos_profile.name
@@ -16,11 +17,9 @@
 	entry.company = pos_profile.company
 	entry.period_start_date = frappe.utils.get_datetime()
 
-	balance_details = [];
+	balance_details = []
 	for d in pos_profile.payments:
-		balance_details.append(frappe._dict({
-			'mode_of_payment': d.mode_of_payment
-		}))
+		balance_details.append(frappe._dict({"mode_of_payment": d.mode_of_payment}))
 
 	entry.set("balance_details", balance_details)
 	entry.submit()
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json
index 9c9f37b..11646a6 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.json
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json
@@ -22,6 +22,7 @@
   "hide_images",
   "hide_unavailable_items",
   "auto_add_item_to_cart",
+  "validate_stock_on_save",
   "column_break_16",
   "update_stock",
   "ignore_pricing_rule",
@@ -351,6 +352,12 @@
   {
    "fieldname": "column_break_25",
    "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "fieldname": "validate_stock_on_save",
+   "fieldtype": "Check",
+   "label": "Validate Stock on Save"
   }
  ],
  "icon": "icon-cog",
@@ -378,10 +385,11 @@
    "link_fieldname": "pos_profile"
   }
  ],
- "modified": "2021-10-14 14:17:00.469298",
+ "modified": "2022-03-21 13:29:28.480533",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Profile",
+ "naming_rule": "Set by user",
  "owner": "Administrator",
  "permissions": [
   {
@@ -404,5 +412,6 @@
   }
  ],
  "sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "states": []
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.py b/erpnext/accounts/doctype/pos_profile/pos_profile.py
index 1d49c3d..e8aee73 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.py
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.py
@@ -17,29 +17,42 @@
 
 	def validate_default_profile(self):
 		for row in self.applicable_for_users:
-			res = frappe.db.sql("""select pf.name
+			res = frappe.db.sql(
+				"""select pf.name
 				from
 					`tabPOS Profile User` pfu, `tabPOS Profile` pf
 				where
 					pf.name = pfu.parent and pfu.user = %s and pf.name != %s and pf.company = %s
-					and pfu.default=1 and pf.disabled = 0""", (row.user, self.name, self.company))
+					and pfu.default=1 and pf.disabled = 0""",
+				(row.user, self.name, self.company),
+			)
 
 			if row.default and res:
-				msgprint(_("Already set default in pos profile {0} for user {1}, kindly disabled default")
-					.format(res[0][0], row.user), raise_exception=1)
+				msgprint(
+					_("Already set default in pos profile {0} for user {1}, kindly disabled default").format(
+						res[0][0], row.user
+					),
+					raise_exception=1,
+				)
 			elif not row.default and not res:
-				msgprint(_("User {0} doesn't have any default POS Profile. Check Default at Row {1} for this User.")
-					.format(row.user, row.idx))
+				msgprint(
+					_(
+						"User {0} doesn't have any default POS Profile. Check Default at Row {1} for this User."
+					).format(row.user, row.idx)
+				)
 
 	def validate_all_link_fields(self):
-		accounts = {"Account": [self.income_account,
-			self.expense_account], "Cost Center": [self.cost_center],
-			"Warehouse": [self.warehouse]}
+		accounts = {
+			"Account": [self.income_account, self.expense_account],
+			"Cost Center": [self.cost_center],
+			"Warehouse": [self.warehouse],
+		}
 
 		for link_dt, dn_list in accounts.items():
 			for link_dn in dn_list:
-				if link_dn and not frappe.db.exists({"doctype": link_dt,
-						"company": self.company, "name": link_dn}):
+				if link_dn and not frappe.db.exists(
+					{"doctype": link_dt, "company": self.company, "name": link_dn}
+				):
 					frappe.throw(_("{0} does not belong to Company {1}").format(link_dn, self.company))
 
 	def validate_duplicate_groups(self):
@@ -47,10 +60,15 @@
 		customer_groups = [d.customer_group for d in self.customer_groups]
 
 		if len(item_groups) != len(set(item_groups)):
-			frappe.throw(_("Duplicate item group found in the item group table"), title = "Duplicate Item Group")
+			frappe.throw(
+				_("Duplicate item group found in the item group table"), title=_("Duplicate Item Group")
+			)
 
 		if len(customer_groups) != len(set(customer_groups)):
-			frappe.throw(_("Duplicate customer group found in the cutomer group table"), title = "Duplicate Customer Group")
+			frappe.throw(
+				_("Duplicate customer group found in the cutomer group table"),
+				title=_("Duplicate Customer Group"),
+			)
 
 	def validate_payment_methods(self):
 		if not self.payments:
@@ -68,7 +86,7 @@
 			account = frappe.db.get_value(
 				"Mode of Payment Account",
 				{"parent": d.mode_of_payment, "company": self.company},
-				"default_account"
+				"default_account",
 			)
 
 			if not account:
@@ -91,12 +109,16 @@
 		frappe.defaults.clear_default("is_pos")
 
 		if not include_current_pos:
-			condition = " where pfu.name != '%s' and pfu.default = 1 " % self.name.replace("'", "\'")
+			condition = " where pfu.name != '%s' and pfu.default = 1 " % self.name.replace("'", "'")
 		else:
 			condition = " where pfu.default = 1 "
 
-		pos_view_users = frappe.db.sql_list("""select pfu.user
-			from `tabPOS Profile User` as pfu {0}""".format(condition))
+		pos_view_users = frappe.db.sql_list(
+			"""select pfu.user
+			from `tabPOS Profile User` as pfu {0}""".format(
+				condition
+			)
+		)
 
 		for user in pos_view_users:
 			if user:
@@ -104,48 +126,62 @@
 			else:
 				frappe.defaults.set_global_default("is_pos", 1)
 
+
 def get_item_groups(pos_profile):
 	item_groups = []
-	pos_profile = frappe.get_cached_doc('POS Profile', pos_profile)
+	pos_profile = frappe.get_cached_doc("POS Profile", pos_profile)
 
-	if pos_profile.get('item_groups'):
+	if pos_profile.get("item_groups"):
 		# Get items based on the item groups defined in the POS profile
-		for data in pos_profile.get('item_groups'):
-			item_groups.extend(["%s" % frappe.db.escape(d.name) for d in get_child_nodes('Item Group', data.item_group)])
+		for data in pos_profile.get("item_groups"):
+			item_groups.extend(
+				["%s" % frappe.db.escape(d.name) for d in get_child_nodes("Item Group", data.item_group)]
+			)
 
 	return list(set(item_groups))
 
+
 def get_child_nodes(group_type, root):
 	lft, rgt = frappe.db.get_value(group_type, root, ["lft", "rgt"])
-	return frappe.db.sql(""" Select name, lft, rgt from `tab{tab}` where
-			lft >= {lft} and rgt <= {rgt} order by lft""".format(tab=group_type, lft=lft, rgt=rgt), as_dict=1)
+	return frappe.db.sql(
+		""" Select name, lft, rgt from `tab{tab}` where
+			lft >= {lft} and rgt <= {rgt} order by lft""".format(
+			tab=group_type, lft=lft, rgt=rgt
+		),
+		as_dict=1,
+	)
+
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def pos_profile_query(doctype, txt, searchfield, start, page_len, filters):
-	user = frappe.session['user']
-	company = filters.get('company') or frappe.defaults.get_user_default('company')
+	user = frappe.session["user"]
+	company = filters.get("company") or frappe.defaults.get_user_default("company")
 
 	args = {
-		'user': user,
-		'start': start,
-		'company': company,
-		'page_len': page_len,
-		'txt': '%%%s%%' % txt
+		"user": user,
+		"start": start,
+		"company": company,
+		"page_len": page_len,
+		"txt": "%%%s%%" % txt,
 	}
 
-	pos_profile = frappe.db.sql("""select pf.name
+	pos_profile = frappe.db.sql(
+		"""select pf.name
 		from
 			`tabPOS Profile` pf, `tabPOS Profile User` pfu
 		where
 			pfu.parent = pf.name and pfu.user = %(user)s and pf.company = %(company)s
 			and (pf.name like %(txt)s)
-			and pf.disabled = 0 limit %(start)s, %(page_len)s""", args)
+			and pf.disabled = 0 limit %(page_len)s offset %(start)s""",
+		args,
+	)
 
 	if not pos_profile:
-		del args['user']
+		del args["user"]
 
-		pos_profile = frappe.db.sql("""select pf.name
+		pos_profile = frappe.db.sql(
+			"""select pf.name
 			from
 				`tabPOS Profile` pf left join `tabPOS Profile User` pfu
 			on
@@ -154,26 +190,37 @@
 				ifnull(pfu.user, '') = ''
 				and pf.company = %(company)s
 				and pf.name like %(txt)s
-				and pf.disabled = 0""", args)
+				and pf.disabled = 0""",
+			args,
+		)
 
 	return pos_profile
 
+
 @frappe.whitelist()
 def set_default_profile(pos_profile, company):
 	modified = now()
 	user = frappe.session.user
 
 	if pos_profile and company:
-		frappe.db.sql(""" update `tabPOS Profile User` pfu, `tabPOS Profile` pf
+		frappe.db.sql(
+			""" update `tabPOS Profile User` pfu, `tabPOS Profile` pf
 			set
 				pfu.default = 0, pf.modified = %s, pf.modified_by = %s
 			where
 				pfu.user = %s and pf.name = pfu.parent and pf.company = %s
-				and pfu.default = 1""", (modified, user, user, company), auto_commit=1)
+				and pfu.default = 1""",
+			(modified, user, user, company),
+			auto_commit=1,
+		)
 
-		frappe.db.sql(""" update `tabPOS Profile User` pfu, `tabPOS Profile` pf
+		frappe.db.sql(
+			""" update `tabPOS Profile User` pfu, `tabPOS Profile` pf
 			set
 				pfu.default = 1, pf.modified = %s, pf.modified_by = %s
 			where
 				pfu.user = %s and pf.name = pfu.parent and pf.company = %s and pf.name = %s
-			""", (modified, user, user, company, pos_profile), auto_commit=1)
+			""",
+			(modified, user, user, company, pos_profile),
+			auto_commit=1,
+		)
diff --git a/erpnext/accounts/doctype/pos_profile/test_pos_profile.py b/erpnext/accounts/doctype/pos_profile/test_pos_profile.py
index c8cf0d2..788aa62 100644
--- a/erpnext/accounts/doctype/pos_profile/test_pos_profile.py
+++ b/erpnext/accounts/doctype/pos_profile/test_pos_profile.py
@@ -8,7 +8,8 @@
 from erpnext.accounts.doctype.pos_profile.pos_profile import get_child_nodes
 from erpnext.stock.get_item_details import get_pos_profile
 
-test_dependencies = ['Item']
+test_dependencies = ["Item"]
+
 
 class TestPOSProfile(unittest.TestCase):
 	def test_pos_profile(self):
@@ -17,46 +18,64 @@
 		pos_profile = get_pos_profile("_Test Company") or {}
 		if pos_profile:
 			doc = frappe.get_doc("POS Profile", pos_profile.get("name"))
-			doc.append('item_groups', {'item_group': '_Test Item Group'})
-			doc.append('customer_groups', {'customer_group': '_Test Customer Group'})
+			doc.append("item_groups", {"item_group": "_Test Item Group"})
+			doc.append("customer_groups", {"customer_group": "_Test Customer Group"})
 			doc.save()
 			items = get_items_list(doc, doc.company)
 			customers = get_customers_list(doc)
 
-			products_count = frappe.db.sql(""" select count(name) from tabItem where item_group = '_Test Item Group'""", as_list=1)
-			customers_count = frappe.db.sql(""" select count(name) from tabCustomer where customer_group = '_Test Customer Group'""")
+			products_count = frappe.db.sql(
+				""" select count(name) from tabItem where item_group = '_Test Item Group'""", as_list=1
+			)
+			customers_count = frappe.db.sql(
+				""" select count(name) from tabCustomer where customer_group = '_Test Customer Group'"""
+			)
 
 			self.assertEqual(len(items), products_count[0][0])
 			self.assertEqual(len(customers), customers_count[0][0])
 
 		frappe.db.sql("delete from `tabPOS Profile`")
 
+
 def get_customers_list(pos_profile=None):
 	if pos_profile is None:
 		pos_profile = {}
 	cond = "1=1"
 	customer_groups = []
-	if pos_profile.get('customer_groups'):
+	if pos_profile.get("customer_groups"):
 		# Get customers based on the customer groups defined in the POS profile
-		for d in pos_profile.get('customer_groups'):
-			customer_groups.extend([d.get('name') for d in get_child_nodes('Customer Group', d.get('customer_group'))])
-		cond = "customer_group in (%s)" % (', '.join(['%s'] * len(customer_groups)))
+		for d in pos_profile.get("customer_groups"):
+			customer_groups.extend(
+				[d.get("name") for d in get_child_nodes("Customer Group", d.get("customer_group"))]
+			)
+		cond = "customer_group in (%s)" % (", ".join(["%s"] * len(customer_groups)))
 
-	return frappe.db.sql(""" select name, customer_name, customer_group,
+	return (
+		frappe.db.sql(
+			""" select name, customer_name, customer_group,
 		territory, customer_pos_id from tabCustomer where disabled = 0
-		and {cond}""".format(cond=cond), tuple(customer_groups), as_dict=1) or {}
+		and {cond}""".format(
+				cond=cond
+			),
+			tuple(customer_groups),
+			as_dict=1,
+		)
+		or {}
+	)
+
 
 def get_items_list(pos_profile, company):
 	cond = ""
 	args_list = []
-	if pos_profile.get('item_groups'):
+	if pos_profile.get("item_groups"):
 		# Get items based on the item groups defined in the POS profile
-		for d in pos_profile.get('item_groups'):
-			args_list.extend([d.name for d in get_child_nodes('Item Group', d.item_group)])
+		for d in pos_profile.get("item_groups"):
+			args_list.extend([d.name for d in get_child_nodes("Item Group", d.item_group)])
 		if args_list:
-			cond = "and i.item_group in (%s)" % (', '.join(['%s'] * len(args_list)))
+			cond = "and i.item_group in (%s)" % (", ".join(["%s"] * len(args_list)))
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select
 			i.name, i.item_code, i.item_name, i.description, i.item_group, i.has_batch_no,
 			i.has_serial_no, i.is_stock_item, i.brand, i.stock_uom, i.image,
@@ -69,7 +88,13 @@
 		where
 			i.disabled = 0 and i.has_variants = 0 and i.is_sales_item = 1 and i.is_fixed_asset = 0
 			{cond}
-		""".format(cond=cond), tuple([company] + args_list), as_dict=1)
+		""".format(
+			cond=cond
+		),
+		tuple([company] + args_list),
+		as_dict=1,
+	)
+
 
 def make_pos_profile(**args):
 	frappe.db.sql("delete from `tabPOS Payment Method`")
@@ -77,38 +102,34 @@
 
 	args = frappe._dict(args)
 
-	pos_profile = frappe.get_doc({
-		"company": args.company or "_Test Company",
-		"cost_center": args.cost_center or "_Test Cost Center - _TC",
-		"currency": args.currency or "INR",
-		"doctype": "POS Profile",
-		"expense_account": args.expense_account or "_Test Account Cost for Goods Sold - _TC",
-		"income_account":  args.income_account or "Sales - _TC",
-		"name":  args.name or "_Test POS Profile",
-		"naming_series": "_T-POS Profile-",
-		"selling_price_list":  args.selling_price_list or "_Test Price List",
-		"territory": args.territory or  "_Test Territory",
-		"customer_group": frappe.db.get_value('Customer Group', {'is_group': 0}, 'name'),
-		"warehouse":  args.warehouse or "_Test Warehouse - _TC",
-		"write_off_account":  args.write_off_account or "_Test Write Off - _TC",
-		"write_off_cost_center":  args.write_off_cost_center or "_Test Write Off Cost Center - _TC"
-	})
+	pos_profile = frappe.get_doc(
+		{
+			"company": args.company or "_Test Company",
+			"cost_center": args.cost_center or "_Test Cost Center - _TC",
+			"currency": args.currency or "INR",
+			"doctype": "POS Profile",
+			"expense_account": args.expense_account or "_Test Account Cost for Goods Sold - _TC",
+			"income_account": args.income_account or "Sales - _TC",
+			"name": args.name or "_Test POS Profile",
+			"naming_series": "_T-POS Profile-",
+			"selling_price_list": args.selling_price_list or "_Test Price List",
+			"territory": args.territory or "_Test Territory",
+			"customer_group": frappe.db.get_value("Customer Group", {"is_group": 0}, "name"),
+			"warehouse": args.warehouse or "_Test Warehouse - _TC",
+			"write_off_account": args.write_off_account or "_Test Write Off - _TC",
+			"write_off_cost_center": args.write_off_cost_center or "_Test Write Off Cost Center - _TC",
+		}
+	)
 
 	mode_of_payment = frappe.get_doc("Mode of Payment", "Cash")
 	company = args.company or "_Test Company"
 	default_account = args.income_account or "Sales - _TC"
 
 	if not frappe.db.get_value("Mode of Payment Account", {"company": company, "parent": "Cash"}):
-		mode_of_payment.append("accounts", {
-			"company": company,
-			"default_account": default_account
-		})
+		mode_of_payment.append("accounts", {"company": company, "default_account": default_account})
 		mode_of_payment.save()
 
-	pos_profile.append("payments", {
-		'mode_of_payment': 'Cash',
-		'default': 1
-	})
+	pos_profile.append("payments", {"mode_of_payment": "Cash", "default": 1})
 
 	if not frappe.db.exists("POS Profile", args.name or "_Test POS Profile"):
 		pos_profile.insert()
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index 933fda8..98e0a9b 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -12,11 +12,11 @@
 from frappe.model.document import Document
 from frappe.utils import cint, flt, getdate
 
-apply_on_dict = {"Item Code": "items",
-	"Item Group": "item_groups", "Brand": "brands"}
+apply_on_dict = {"Item Code": "items", "Item Group": "item_groups", "Brand": "brands"}
 
 other_fields = ["other_item_code", "other_item_group", "other_brand"]
 
+
 class PricingRule(Document):
 	def validate(self):
 		self.validate_mandatory()
@@ -31,13 +31,19 @@
 		self.validate_dates()
 		self.validate_condition()
 
-		if not self.margin_type: self.margin_rate_or_amount = 0.0
+		if not self.margin_type:
+			self.margin_rate_or_amount = 0.0
 
 	def validate_duplicate_apply_on(self):
-		field = apply_on_dict.get(self.apply_on)
-		values = [d.get(frappe.scrub(self.apply_on)) for d in self.get(field) if field]
-		if len(values) != len(set(values)):
-			frappe.throw(_("Duplicate {0} found in the table").format(self.apply_on))
+		if self.apply_on != "Transaction":
+			apply_on_table = apply_on_dict.get(self.apply_on)
+			if not apply_on_table:
+				return
+
+			apply_on_field = frappe.scrub(self.apply_on)
+			values = [d.get(apply_on_field) for d in self.get(apply_on_table) if d.get(apply_on_field)]
+			if len(values) != len(set(values)):
+				frappe.throw(_("Duplicate {0} found in the table").format(self.apply_on))
 
 	def validate_mandatory(self):
 		for apply_on, field in apply_on_dict.items():
@@ -49,36 +55,51 @@
 			throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError)
 
 		if self.apply_rule_on_other:
-			o_field = 'other_' + frappe.scrub(self.apply_rule_on_other)
+			o_field = "other_" + frappe.scrub(self.apply_rule_on_other)
 			if not self.get(o_field) and o_field in other_fields:
-				frappe.throw(_("For the 'Apply Rule On Other' condition the field {0} is mandatory")
-					.format(frappe.bold(self.apply_rule_on_other)))
+				frappe.throw(
+					_("For the 'Apply Rule On Other' condition the field {0} is mandatory").format(
+						frappe.bold(self.apply_rule_on_other)
+					)
+				)
 
-
-		if self.price_or_product_discount == 'Price' and not self.rate_or_discount:
+		if self.price_or_product_discount == "Price" and not self.rate_or_discount:
 			throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError)
 
 		if self.apply_discount_on_rate:
 			if not self.priority:
-				throw(_("As the field {0} is enabled, the field {1} is mandatory.")
-					.format(frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority")))
+				throw(
+					_("As the field {0} is enabled, the field {1} is mandatory.").format(
+						frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority")
+					)
+				)
 
 			if self.priority and cint(self.priority) == 1:
-				throw(_("As the field {0} is enabled, the value of the field {1} should be more than 1.")
-					.format(frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority")))
+				throw(
+					_("As the field {0} is enabled, the value of the field {1} should be more than 1.").format(
+						frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority")
+					)
+				)
 
 	def validate_applicable_for_selling_or_buying(self):
 		if not self.selling and not self.buying:
 			throw(_("Atleast one of the Selling or Buying must be selected"))
 
-		if not self.selling and self.applicable_for in ["Customer", "Customer Group",
-				"Territory", "Sales Partner", "Campaign"]:
-			throw(_("Selling must be checked, if Applicable For is selected as {0}")
-				.format(self.applicable_for))
+		if not self.selling and self.applicable_for in [
+			"Customer",
+			"Customer Group",
+			"Territory",
+			"Sales Partner",
+			"Campaign",
+		]:
+			throw(
+				_("Selling must be checked, if Applicable For is selected as {0}").format(self.applicable_for)
+			)
 
 		if not self.buying and self.applicable_for in ["Supplier", "Supplier Group"]:
-			throw(_("Buying must be checked, if Applicable For is selected as {0}")
-				.format(self.applicable_for))
+			throw(
+				_("Buying must be checked, if Applicable For is selected as {0}").format(self.applicable_for)
+			)
 
 	def validate_min_max_qty(self):
 		if self.min_qty and self.max_qty and flt(self.min_qty) > flt(self.max_qty):
@@ -95,11 +116,12 @@
 			# reset all values except for the logic field
 			options = (self.meta.get_options(logic_field) or "").split("\n")
 			for f in options:
-				if not f: continue
+				if not f:
+					continue
 
 				scrubbed_f = frappe.scrub(f)
 
-				if logic_field == 'apply_on':
+				if logic_field == "apply_on":
 					apply_on_f = apply_on_dict.get(f, f)
 				else:
 					apply_on_f = scrubbed_f
@@ -112,8 +134,11 @@
 
 		apply_rule_on_other = frappe.scrub(self.apply_rule_on_other or "")
 
-		cleanup_other_fields = (other_fields if not apply_rule_on_other
-			else [o_field for o_field in other_fields if o_field != 'other_' + apply_rule_on_other])
+		cleanup_other_fields = (
+			other_fields
+			if not apply_rule_on_other
+			else [o_field for o_field in other_fields if o_field != "other_" + apply_rule_on_other]
+		)
 
 		for other_field in cleanup_other_fields:
 			self.set(other_field, None)
@@ -123,7 +148,7 @@
 			if flt(self.get(frappe.scrub(field))) < 0:
 				throw(_("{0} can not be negative").format(field))
 
-		if self.price_or_product_discount == 'Product' and not self.free_item:
+		if self.price_or_product_discount == "Product" and not self.free_item:
 			if self.mixed_conditions:
 				frappe.throw(_("Free item code is not selected"))
 			else:
@@ -150,31 +175,37 @@
 			frappe.throw(_("Valid from date must be less than valid upto date"))
 
 	def validate_condition(self):
-		if self.condition and ("=" in self.condition) and re.match(r'[\w\.:_]+\s*={1}\s*[\w\.@\'"]+', self.condition):
+		if (
+			self.condition
+			and ("=" in self.condition)
+			and re.match(r'[\w\.:_]+\s*={1}\s*[\w\.@\'"]+', self.condition)
+		):
 			frappe.throw(_("Invalid condition expression"))
 
-#--------------------------------------------------------------------------------
+
+# --------------------------------------------------------------------------------
+
 
 @frappe.whitelist()
 def apply_pricing_rule(args, doc=None):
 	"""
-		args = {
-			"items": [{"doctype": "", "name": "", "item_code": "", "brand": "", "item_group": ""}, ...],
-			"customer": "something",
-			"customer_group": "something",
-			"territory": "something",
-			"supplier": "something",
-			"supplier_group": "something",
-			"currency": "something",
-			"conversion_rate": "something",
-			"price_list": "something",
-			"plc_conversion_rate": "something",
-			"company": "something",
-			"transaction_date": "something",
-			"campaign": "something",
-			"sales_partner": "something",
-			"ignore_pricing_rule": "something"
-		}
+	args = {
+	        "items": [{"doctype": "", "name": "", "item_code": "", "brand": "", "item_group": ""}, ...],
+	        "customer": "something",
+	        "customer_group": "something",
+	        "territory": "something",
+	        "supplier": "something",
+	        "supplier_group": "something",
+	        "currency": "something",
+	        "conversion_rate": "something",
+	        "price_list": "something",
+	        "plc_conversion_rate": "something",
+	        "company": "something",
+	        "transaction_date": "something",
+	        "campaign": "something",
+	        "sales_partner": "something",
+	        "ignore_pricing_rule": "something"
+	}
 	"""
 
 	if isinstance(args, str):
@@ -188,16 +219,23 @@
 	# list of dictionaries
 	out = []
 
-	if args.get("doctype") == "Material Request": return out
+	if args.get("doctype") == "Material Request":
+		return out
 
 	item_list = args.get("items")
 	args.pop("items")
 
-	set_serial_nos_based_on_fifo = frappe.db.get_single_value("Stock Settings",
-		"automatically_set_serial_nos_based_on_fifo")
+	set_serial_nos_based_on_fifo = frappe.db.get_single_value(
+		"Stock Settings", "automatically_set_serial_nos_based_on_fifo"
+	)
 
-	item_code_list = tuple(item.get('item_code') for item in item_list)
-	query_items = frappe.get_all('Item', fields=['item_code','has_serial_no'], filters=[['item_code','in',item_code_list]],as_list=1)
+	item_code_list = tuple(item.get("item_code") for item in item_list)
+	query_items = frappe.get_all(
+		"Item",
+		fields=["item_code", "has_serial_no"],
+		filters=[["item_code", "in", item_code_list]],
+		as_list=1,
+	)
 	serialized_items = dict()
 	for item_code, val in query_items:
 		serialized_items.setdefault(item_code, val)
@@ -205,26 +243,31 @@
 	for item in item_list:
 		args_copy = copy.deepcopy(args)
 		args_copy.update(item)
-		data = get_pricing_rule_for_item(args_copy, item.get('price_list_rate'), doc=doc)
+		data = get_pricing_rule_for_item(args_copy, item.get("price_list_rate"), doc=doc)
 		out.append(data)
 
-		if serialized_items.get(item.get('item_code')) and not item.get("serial_no") and set_serial_nos_based_on_fifo and not args.get('is_return'):
+		if (
+			serialized_items.get(item.get("item_code"))
+			and not item.get("serial_no")
+			and set_serial_nos_based_on_fifo
+			and not args.get("is_return")
+		):
 			out[0].update(get_serial_no_for_item(args_copy))
 
 	return out
 
+
 def get_serial_no_for_item(args):
 	from erpnext.stock.get_item_details import get_serial_no
 
-	item_details = frappe._dict({
-		"doctype": args.doctype,
-		"name": args.name,
-		"serial_no": args.serial_no
-	})
+	item_details = frappe._dict(
+		{"doctype": args.doctype, "name": args.name, "serial_no": args.serial_no}
+	)
 	if args.get("parenttype") in ("Sales Invoice", "Delivery Note") and flt(args.stock_qty) > 0:
 		item_details.serial_no = get_serial_no(args)
 	return item_details
 
+
 def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False):
 	from erpnext.accounts.doctype.pricing_rule.utils import (
 		get_applied_pricing_rules,
@@ -239,18 +282,20 @@
 	if doc:
 		doc = frappe.get_doc(doc)
 
-	if (args.get('is_free_item') or
-		args.get("parenttype") == "Material Request"): return {}
+	if args.get("is_free_item") or args.get("parenttype") == "Material Request":
+		return {}
 
-	item_details = frappe._dict({
-		"doctype": args.doctype,
-		"has_margin": False,
-		"name": args.name,
-		"free_item_data": [],
-		"parent": args.parent,
-		"parenttype": args.parenttype,
-		"child_docname": args.get('child_docname'),
-	})
+	item_details = frappe._dict(
+		{
+			"doctype": args.doctype,
+			"has_margin": False,
+			"name": args.name,
+			"free_item_data": [],
+			"parent": args.parent,
+			"parenttype": args.parenttype,
+			"child_docname": args.get("child_docname"),
+		}
+	)
 
 	if args.ignore_pricing_rule or not args.item_code:
 		if frappe.db.exists(args.doctype, args.name) and args.get("pricing_rules"):
@@ -264,20 +309,25 @@
 
 	update_args_for_pricing_rule(args)
 
-	pricing_rules = (get_applied_pricing_rules(args.get('pricing_rules'))
-		if for_validate and args.get("pricing_rules") else get_pricing_rules(args, doc))
+	pricing_rules = (
+		get_applied_pricing_rules(args.get("pricing_rules"))
+		if for_validate and args.get("pricing_rules")
+		else get_pricing_rules(args, doc)
+	)
 
 	if pricing_rules:
 		rules = []
 
 		for pricing_rule in pricing_rules:
-			if not pricing_rule: continue
+			if not pricing_rule:
+				continue
 
 			if isinstance(pricing_rule, str):
 				pricing_rule = frappe.get_cached_doc("Pricing Rule", pricing_rule)
 				pricing_rule.apply_rule_on_other_items = get_pricing_rule_items(pricing_rule)
 
-			if pricing_rule.get('suggestion'): continue
+			if pricing_rule.get("suggestion"):
+				continue
 
 			item_details.validate_applied_rule = pricing_rule.get("validate_applied_rule", 0)
 			item_details.price_or_product_discount = pricing_rule.get("price_or_product_discount")
@@ -285,14 +335,19 @@
 			rules.append(get_pricing_rule_details(args, pricing_rule))
 
 			if pricing_rule.mixed_conditions or pricing_rule.apply_rule_on_other:
-				item_details.update({
-					'apply_rule_on_other_items': json.dumps(pricing_rule.apply_rule_on_other_items),
-					'price_or_product_discount': pricing_rule.price_or_product_discount,
-					'apply_rule_on': (frappe.scrub(pricing_rule.apply_rule_on_other)
-						if pricing_rule.apply_rule_on_other else frappe.scrub(pricing_rule.get('apply_on')))
-				})
+				item_details.update(
+					{
+						"apply_rule_on_other_items": json.dumps(pricing_rule.apply_rule_on_other_items),
+						"price_or_product_discount": pricing_rule.price_or_product_discount,
+						"apply_rule_on": (
+							frappe.scrub(pricing_rule.apply_rule_on_other)
+							if pricing_rule.apply_rule_on_other
+							else frappe.scrub(pricing_rule.get("apply_on"))
+						),
+					}
+				)
 
-			if pricing_rule.coupon_code_based==1 and args.coupon_code==None:
+			if pricing_rule.coupon_code_based == 1 and args.coupon_code == None:
 				return item_details
 
 			if not pricing_rule.validate_applied_rule:
@@ -309,7 +364,8 @@
 
 		item_details.pricing_rules = frappe.as_json([d.pricing_rule for d in rules])
 
-		if not doc: return item_details
+		if not doc:
+			return item_details
 
 	elif args.get("pricing_rules"):
 		item_details = remove_pricing_rule_for_item(
@@ -321,19 +377,22 @@
 
 	return item_details
 
+
 def update_args_for_pricing_rule(args):
 	if not (args.item_group and args.brand):
-		try:
-			args.item_group, args.brand = frappe.get_cached_value("Item", args.item_code, ["item_group", "brand"])
-		except frappe.DoesNotExistError:
+		item = frappe.get_cached_value("Item", args.item_code, ("item_group", "brand"))
+		if not item:
 			return
+
+		args.item_group, args.brand = item
+
 		if not args.item_group:
 			frappe.throw(_("Item Group not mentioned in item master for item {0}").format(args.item_code))
 
-	if args.transaction_type=="selling":
+	if args.transaction_type == "selling":
 		if args.customer and not (args.customer_group and args.territory):
 
-			if args.quotation_to and args.quotation_to != 'Customer':
+			if args.quotation_to and args.quotation_to != "Customer":
 				customer = frappe._dict()
 			else:
 				customer = frappe.get_cached_value("Customer", args.customer, ["customer_group", "territory"])
@@ -347,20 +406,25 @@
 		args.supplier_group = frappe.get_cached_value("Supplier", args.supplier, "supplier_group")
 		args.customer = args.customer_group = args.territory = None
 
+
 def get_pricing_rule_details(args, pricing_rule):
-	return frappe._dict({
-		'pricing_rule': pricing_rule.name,
-		'rate_or_discount': pricing_rule.rate_or_discount,
-		'margin_type': pricing_rule.margin_type,
-		'item_code': args.get("item_code"),
-		'child_docname': args.get('child_docname')
-	})
+	return frappe._dict(
+		{
+			"pricing_rule": pricing_rule.name,
+			"rate_or_discount": pricing_rule.rate_or_discount,
+			"margin_type": pricing_rule.margin_type,
+			"item_code": args.get("item_code"),
+			"child_docname": args.get("child_docname"),
+		}
+	)
+
 
 def apply_price_discount_rule(pricing_rule, item_details, args):
 	item_details.pricing_rule_for = pricing_rule.rate_or_discount
 
-	if ((pricing_rule.margin_type in ['Amount', 'Percentage'] and pricing_rule.currency == args.currency)
-			or (pricing_rule.margin_type == 'Percentage')):
+	if (
+		pricing_rule.margin_type in ["Amount", "Percentage"] and pricing_rule.currency == args.currency
+	) or (pricing_rule.margin_type == "Percentage"):
 		item_details.margin_type = pricing_rule.margin_type
 		item_details.has_margin = True
 
@@ -369,7 +433,7 @@
 		else:
 			item_details.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
 
-	if pricing_rule.rate_or_discount == 'Rate':
+	if pricing_rule.rate_or_discount == "Rate":
 		pricing_rule_rate = 0.0
 		if pricing_rule.currency == args.currency:
 			pricing_rule_rate = pricing_rule.rate
@@ -377,63 +441,71 @@
 		if pricing_rule_rate:
 			# Override already set price list rate (from item price)
 			# if pricing_rule_rate > 0
-			item_details.update({
-				"price_list_rate": pricing_rule_rate * args.get("conversion_factor", 1),
-			})
-		item_details.update({
-			"discount_percentage": 0.0
-		})
+			item_details.update(
+				{
+					"price_list_rate": pricing_rule_rate * args.get("conversion_factor", 1),
+				}
+			)
+		item_details.update({"discount_percentage": 0.0})
 
-	for apply_on in ['Discount Amount', 'Discount Percentage']:
-		if pricing_rule.rate_or_discount != apply_on: continue
+	for apply_on in ["Discount Amount", "Discount Percentage"]:
+		if pricing_rule.rate_or_discount != apply_on:
+			continue
 
 		field = frappe.scrub(apply_on)
 		if pricing_rule.apply_discount_on_rate and item_details.get("discount_percentage"):
 			# Apply discount on discounted rate
-			item_details[field] += ((100 - item_details[field]) * (pricing_rule.get(field, 0) / 100))
+			item_details[field] += (100 - item_details[field]) * (pricing_rule.get(field, 0) / 100)
 		else:
 			if field not in item_details:
 				item_details.setdefault(field, 0)
 
-			item_details[field] += (pricing_rule.get(field, 0)
-				if pricing_rule else args.get(field, 0))
+			item_details[field] += pricing_rule.get(field, 0) if pricing_rule else args.get(field, 0)
+
 
 def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None, rate=None):
 	from erpnext.accounts.doctype.pricing_rule.utils import (
 		get_applied_pricing_rules,
 		get_pricing_rule_items,
 	)
-	for d in get_applied_pricing_rules(pricing_rules):
-		if not d or not frappe.db.exists("Pricing Rule", d): continue
-		pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
 
-		if pricing_rule.price_or_product_discount == 'Price':
-			if pricing_rule.rate_or_discount == 'Discount Percentage':
+	for d in get_applied_pricing_rules(pricing_rules):
+		if not d or not frappe.db.exists("Pricing Rule", d):
+			continue
+		pricing_rule = frappe.get_cached_doc("Pricing Rule", d)
+
+		if pricing_rule.price_or_product_discount == "Price":
+			if pricing_rule.rate_or_discount == "Discount Percentage":
 				item_details.discount_percentage = 0.0
 				item_details.discount_amount = 0.0
 				item_details.rate = rate or 0.0
 
-			if pricing_rule.rate_or_discount == 'Discount Amount':
+			if pricing_rule.rate_or_discount == "Discount Amount":
 				item_details.discount_amount = 0.0
 
-			if pricing_rule.margin_type in ['Percentage', 'Amount']:
+			if pricing_rule.margin_type in ["Percentage", "Amount"]:
 				item_details.margin_rate_or_amount = 0.0
 				item_details.margin_type = None
-		elif pricing_rule.get('free_item'):
-			item_details.remove_free_item = (item_code if pricing_rule.get('same_item')
-				else pricing_rule.get('free_item'))
+		elif pricing_rule.get("free_item"):
+			item_details.remove_free_item = (
+				item_code if pricing_rule.get("same_item") else pricing_rule.get("free_item")
+			)
 
 		if pricing_rule.get("mixed_conditions") or pricing_rule.get("apply_rule_on_other"):
 			items = get_pricing_rule_items(pricing_rule)
-			item_details.apply_on = (frappe.scrub(pricing_rule.apply_rule_on_other)
-				if pricing_rule.apply_rule_on_other else frappe.scrub(pricing_rule.get('apply_on')))
-			item_details.applied_on_items = ','.join(items)
+			item_details.apply_on = (
+				frappe.scrub(pricing_rule.apply_rule_on_other)
+				if pricing_rule.apply_rule_on_other
+				else frappe.scrub(pricing_rule.get("apply_on"))
+			)
+			item_details.applied_on_items = ",".join(items)
 
-	item_details.pricing_rules = ''
+	item_details.pricing_rules = ""
 	item_details.pricing_rule_removed = True
 
 	return item_details
 
+
 @frappe.whitelist()
 def remove_pricing_rules(item_list):
 	if isinstance(item_list, str):
@@ -451,19 +523,26 @@
 
 	return out
 
+
 def set_transaction_type(args):
 	if args.transaction_type:
 		return
 	if args.doctype in ("Opportunity", "Quotation", "Sales Order", "Delivery Note", "Sales Invoice"):
 		args.transaction_type = "selling"
-	elif args.doctype in ("Material Request", "Supplier Quotation", "Purchase Order",
-		"Purchase Receipt", "Purchase Invoice"):
-			args.transaction_type = "buying"
+	elif args.doctype in (
+		"Material Request",
+		"Supplier Quotation",
+		"Purchase Order",
+		"Purchase Receipt",
+		"Purchase Invoice",
+	):
+		args.transaction_type = "buying"
 	elif args.customer:
 		args.transaction_type = "selling"
 	else:
 		args.transaction_type = "buying"
 
+
 @frappe.whitelist()
 def make_pricing_rule(doctype, docname):
 	doc = frappe.new_doc("Pricing Rule")
@@ -474,15 +553,18 @@
 
 	return doc
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_item_uoms(doctype, txt, searchfield, start, page_len, filters):
-	items = [filters.get('value')]
-	if filters.get('apply_on') != 'Item Code':
-		field = frappe.scrub(filters.get('apply_on'))
-		items = [d.name for d in frappe.db.get_all("Item", filters={field: filters.get('value')})]
+	items = [filters.get("value")]
+	if filters.get("apply_on") != "Item Code":
+		field = frappe.scrub(filters.get("apply_on"))
+		items = [d.name for d in frappe.db.get_all("Item", filters={field: filters.get("value")})]
 
-	return frappe.get_all('UOM Conversion Detail', filters={
-			'parent': ('in', items),
-			'uom': ("like", "{0}%".format(txt))
-		}, fields = ["distinct uom"], as_list=1)
+	return frappe.get_all(
+		"UOM Conversion Detail",
+		filters={"parent": ("in", items), "uom": ("like", "{0}%".format(txt))},
+		fields=["distinct uom"],
+		as_list=1,
+	)
diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
index 8338a5b0..3bd0cd2 100644
--- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
@@ -2,7 +2,6 @@
 # License: GNU General Public License v3. See license.txt
 
 
-
 import unittest
 
 import frappe
@@ -30,31 +29,31 @@
 			"doctype": "Pricing Rule",
 			"title": "_Test Pricing Rule",
 			"apply_on": "Item Code",
-			"items": [{
-				"item_code": "_Test Item"
-			}],
+			"items": [{"item_code": "_Test Item"}],
 			"currency": "USD",
 			"selling": 1,
 			"rate_or_discount": "Discount Percentage",
 			"rate": 0,
 			"discount_percentage": 10,
-			"company": "_Test Company"
+			"company": "_Test Company",
 		}
 		frappe.get_doc(test_record.copy()).insert()
 
-		args = frappe._dict({
-			"item_code": "_Test Item",
-			"company": "_Test Company",
-			"price_list": "_Test Price List",
-			"currency": "_Test Currency",
-			"doctype": "Sales Order",
-			"conversion_rate": 1,
-			"price_list_currency": "_Test Currency",
-			"plc_conversion_rate": 1,
-			"order_type": "Sales",
-			"customer": "_Test Customer",
-			"name": None
-		})
+		args = frappe._dict(
+			{
+				"item_code": "_Test Item",
+				"company": "_Test Company",
+				"price_list": "_Test Price List",
+				"currency": "_Test Currency",
+				"doctype": "Sales Order",
+				"conversion_rate": 1,
+				"price_list_currency": "_Test Currency",
+				"plc_conversion_rate": 1,
+				"order_type": "Sales",
+				"customer": "_Test Customer",
+				"name": None,
+			}
+		)
 		details = get_item_details(args)
 		self.assertEqual(details.get("discount_percentage"), 10)
 
@@ -73,9 +72,7 @@
 		prule = frappe.get_doc(test_record.copy())
 		prule.apply_on = "Item Group"
 		prule.items = []
-		prule.append('item_groups', {
-			'item_group': "All Item Groups"
-		})
+		prule.append("item_groups", {"item_group": "All Item Groups"})
 		prule.title = "_Test Pricing Rule for Item Group"
 		prule.discount_percentage = 15
 		prule.insert()
@@ -98,6 +95,7 @@
 
 		frappe.db.sql("update `tabPricing Rule` set priority=NULL where campaign='_Test Campaign'")
 		from erpnext.accounts.doctype.pricing_rule.utils import MultiplePricingRuleConflict
+
 		self.assertRaises(MultiplePricingRuleConflict, get_item_details, args)
 
 		args.item_code = "_Test Item 2"
@@ -113,41 +111,47 @@
 			"doctype": "Pricing Rule",
 			"title": "_Test Pricing Rule",
 			"apply_on": "Item Code",
-			"items": [{
-				"item_code": "_Test FG Item 2",
-			}],
+			"items": [
+				{
+					"item_code": "_Test FG Item 2",
+				}
+			],
 			"selling": 1,
 			"currency": "USD",
 			"rate_or_discount": "Discount Percentage",
 			"rate": 0,
 			"margin_type": "Percentage",
 			"margin_rate_or_amount": 10,
-			"company": "_Test Company"
+			"company": "_Test Company",
 		}
 		frappe.get_doc(test_record.copy()).insert()
 
-		item_price = frappe.get_doc({
-			"doctype": "Item Price",
-			"price_list": "_Test Price List 2",
-			"item_code": "_Test FG Item 2",
-			"price_list_rate": 100
-		})
+		item_price = frappe.get_doc(
+			{
+				"doctype": "Item Price",
+				"price_list": "_Test Price List 2",
+				"item_code": "_Test FG Item 2",
+				"price_list_rate": 100,
+			}
+		)
 
 		item_price.insert(ignore_permissions=True)
 
-		args = frappe._dict({
-			"item_code": "_Test FG Item 2",
-			"company": "_Test Company",
-			"price_list": "_Test Price List",
-			"currency": "_Test Currency",
-			"doctype": "Sales Order",
-			"conversion_rate": 1,
-			"price_list_currency": "_Test Currency",
-			"plc_conversion_rate": 1,
-			"order_type": "Sales",
-			"customer": "_Test Customer",
-			"name": None
-		})
+		args = frappe._dict(
+			{
+				"item_code": "_Test FG Item 2",
+				"company": "_Test Company",
+				"price_list": "_Test Price List",
+				"currency": "_Test Currency",
+				"doctype": "Sales Order",
+				"conversion_rate": 1,
+				"price_list_currency": "_Test Currency",
+				"plc_conversion_rate": 1,
+				"order_type": "Sales",
+				"customer": "_Test Customer",
+				"name": None,
+			}
+		)
 		details = get_item_details(args)
 		self.assertEqual(details.get("margin_type"), "Percentage")
 		self.assertEqual(details.get("margin_rate_or_amount"), 10)
@@ -176,25 +180,27 @@
 			"discount_percentage": 10,
 			"applicable_for": "Customer Group",
 			"customer_group": "All Customer Groups",
-			"company": "_Test Company"
+			"company": "_Test Company",
 		}
 		frappe.get_doc(test_record.copy()).insert()
 
-		args = frappe._dict({
-			"item_code": "Mixed Cond Item 1",
-			"item_group": "Products",
-			"company": "_Test Company",
-			"price_list": "_Test Price List",
-			"currency": "_Test Currency",
-			"doctype": "Sales Order",
-			"conversion_rate": 1,
-			"price_list_currency": "_Test Currency",
-			"plc_conversion_rate": 1,
-			"order_type": "Sales",
-			"customer": "_Test Customer",
-			"customer_group": "_Test Customer Group",
-			"name": None
-		})
+		args = frappe._dict(
+			{
+				"item_code": "Mixed Cond Item 1",
+				"item_group": "Products",
+				"company": "_Test Company",
+				"price_list": "_Test Price List",
+				"currency": "_Test Currency",
+				"doctype": "Sales Order",
+				"conversion_rate": 1,
+				"price_list_currency": "_Test Currency",
+				"plc_conversion_rate": 1,
+				"order_type": "Sales",
+				"customer": "_Test Customer",
+				"customer_group": "_Test Customer Group",
+				"name": None,
+			}
+		)
 		details = get_item_details(args)
 		self.assertEqual(details.get("discount_percentage"), 10)
 
@@ -204,72 +210,79 @@
 		from erpnext.stock.get_item_details import get_item_details
 
 		if not frappe.db.exists("Item", "Test Variant PRT"):
-			frappe.get_doc({
-				"doctype": "Item",
-				"item_code": "Test Variant PRT",
-				"item_name": "Test Variant PRT",
-				"description": "Test Variant PRT",
-				"item_group": "_Test Item Group",
-				"is_stock_item": 1,
-				"variant_of": "_Test Variant Item",
-				"default_warehouse": "_Test Warehouse - _TC",
-				"stock_uom": "_Test UOM",
-				"attributes": [
+			frappe.get_doc(
+				{
+					"doctype": "Item",
+					"item_code": "Test Variant PRT",
+					"item_name": "Test Variant PRT",
+					"description": "Test Variant PRT",
+					"item_group": "_Test Item Group",
+					"is_stock_item": 1,
+					"variant_of": "_Test Variant Item",
+					"default_warehouse": "_Test Warehouse - _TC",
+					"stock_uom": "_Test UOM",
+					"attributes": [{"attribute": "Test Size", "attribute_value": "Medium"}],
+				}
+			).insert()
+
+		frappe.get_doc(
+			{
+				"doctype": "Pricing Rule",
+				"title": "_Test Pricing Rule 1",
+				"apply_on": "Item Code",
+				"currency": "USD",
+				"items": [
 					{
-					  "attribute": "Test Size",
-					  "attribute_value": "Medium"
+						"item_code": "_Test Variant Item",
 					}
 				],
-			}).insert()
+				"selling": 1,
+				"rate_or_discount": "Discount Percentage",
+				"rate": 0,
+				"discount_percentage": 7.5,
+				"company": "_Test Company",
+			}
+		).insert()
 
-		frappe.get_doc({
-			"doctype": "Pricing Rule",
-			"title": "_Test Pricing Rule 1",
-			"apply_on": "Item Code",
-			"currency": "USD",
-			"items": [{
-				"item_code": "_Test Variant Item",
-			}],
-			"selling": 1,
-			"rate_or_discount": "Discount Percentage",
-			"rate": 0,
-			"discount_percentage": 7.5,
-			"company": "_Test Company"
-		}).insert()
-
-		args = frappe._dict({
-			"item_code": "Test Variant PRT",
-			"company": "_Test Company",
-			"price_list": "_Test Price List",
-			"currency": "_Test Currency",
-			"doctype": "Sales Order",
-			"conversion_rate": 1,
-			"price_list_currency": "_Test Currency",
-			"plc_conversion_rate": 1,
-			"order_type": "Sales",
-			"customer": "_Test Customer",
-			"name": None
-		})
+		args = frappe._dict(
+			{
+				"item_code": "Test Variant PRT",
+				"company": "_Test Company",
+				"price_list": "_Test Price List",
+				"currency": "_Test Currency",
+				"doctype": "Sales Order",
+				"conversion_rate": 1,
+				"price_list_currency": "_Test Currency",
+				"plc_conversion_rate": 1,
+				"order_type": "Sales",
+				"customer": "_Test Customer",
+				"name": None,
+			}
+		)
 
 		details = get_item_details(args)
 		self.assertEqual(details.get("discount_percentage"), 7.5)
 
 		# add a new pricing rule for that item code, it should take priority
-		frappe.get_doc({
-			"doctype": "Pricing Rule",
-			"title": "_Test Pricing Rule 2",
-			"apply_on": "Item Code",
-			"items": [{
-				"item_code": "Test Variant PRT",
-			}],
-			"currency": "USD",
-			"selling": 1,
-			"rate_or_discount": "Discount Percentage",
-			"rate": 0,
-			"discount_percentage": 17.5,
-			"priority": 1,
-			"company": "_Test Company"
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Pricing Rule",
+				"title": "_Test Pricing Rule 2",
+				"apply_on": "Item Code",
+				"items": [
+					{
+						"item_code": "Test Variant PRT",
+					}
+				],
+				"currency": "USD",
+				"selling": 1,
+				"rate_or_discount": "Discount Percentage",
+				"rate": 0,
+				"discount_percentage": 17.5,
+				"priority": 1,
+				"company": "_Test Company",
+			}
+		).insert()
 
 		details = get_item_details(args)
 		self.assertEqual(details.get("discount_percentage"), 17.5)
@@ -280,33 +293,31 @@
 			"title": "_Test Pricing Rule",
 			"apply_on": "Item Code",
 			"currency": "USD",
-			"items": [{
-				"item_code": "_Test Item",
-			}],
+			"items": [
+				{
+					"item_code": "_Test Item",
+				}
+			],
 			"selling": 1,
 			"rate_or_discount": "Discount Percentage",
 			"rate": 0,
 			"min_qty": 5,
 			"max_qty": 7,
 			"discount_percentage": 17.5,
-			"company": "_Test Company"
+			"company": "_Test Company",
 		}
 		frappe.get_doc(test_record.copy()).insert()
 
-		if not frappe.db.get_value('UOM Conversion Detail',
-			{'parent': '_Test Item', 'uom': 'box'}):
-			item = frappe.get_doc('Item', '_Test Item')
-			item.append('uoms', {
-				'uom': 'Box',
-				'conversion_factor': 5
-			})
+		if not frappe.db.get_value("UOM Conversion Detail", {"parent": "_Test Item", "uom": "box"}):
+			item = frappe.get_doc("Item", "_Test Item")
+			item.append("uoms", {"uom": "Box", "conversion_factor": 5})
 			item.save(ignore_permissions=True)
 
 		# With pricing rule
 		so = make_sales_order(item_code="_Test Item", qty=1, uom="Box", do_not_submit=True)
 		so.items[0].price_list_rate = 100
 		so.submit()
-		so = frappe.get_doc('Sales Order', so.name)
+		so = frappe.get_doc("Sales Order", so.name)
 		self.assertEqual(so.items[0].discount_percentage, 17.5)
 		self.assertEqual(so.items[0].rate, 82.5)
 
@@ -314,13 +325,15 @@
 		so = make_sales_order(item_code="_Test Item", qty=2, uom="Box", do_not_submit=True)
 		so.items[0].price_list_rate = 100
 		so.submit()
-		so = frappe.get_doc('Sales Order', so.name)
+		so = frappe.get_doc("Sales Order", so.name)
 		self.assertEqual(so.items[0].discount_percentage, 0)
 		self.assertEqual(so.items[0].rate, 100)
 
 	def test_pricing_rule_with_margin_and_discount(self):
-		frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
-		make_pricing_rule(selling=1, margin_type="Percentage", margin_rate_or_amount=10, discount_percentage=10)
+		frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
+		make_pricing_rule(
+			selling=1, margin_type="Percentage", margin_rate_or_amount=10, discount_percentage=10
+		)
 		si = create_sales_invoice(do_not_save=True)
 		si.items[0].price_list_rate = 1000
 		si.payment_schedule = []
@@ -334,9 +347,14 @@
 		self.assertEqual(item.rate, 990)
 
 	def test_pricing_rule_with_margin_and_discount_amount(self):
-		frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
-		make_pricing_rule(selling=1, margin_type="Percentage", margin_rate_or_amount=10,
-			rate_or_discount="Discount Amount", discount_amount=110)
+		frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
+		make_pricing_rule(
+			selling=1,
+			margin_type="Percentage",
+			margin_rate_or_amount=10,
+			rate_or_discount="Discount Amount",
+			discount_amount=110,
+		)
 		si = create_sales_invoice(do_not_save=True)
 		si.items[0].price_list_rate = 1000
 		si.payment_schedule = []
@@ -349,15 +367,17 @@
 		self.assertEqual(item.rate, 990)
 
 	def test_pricing_rule_for_product_discount_on_same_item(self):
-		frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
+		frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
 		test_record = {
 			"doctype": "Pricing Rule",
 			"title": "_Test Pricing Rule",
 			"apply_on": "Item Code",
 			"currency": "USD",
-			"items": [{
-				"item_code": "_Test Item",
-			}],
+			"items": [
+				{
+					"item_code": "_Test Item",
+				}
+			],
 			"selling": 1,
 			"rate_or_discount": "Discount Percentage",
 			"rate": 0,
@@ -367,7 +387,7 @@
 			"price_or_product_discount": "Product",
 			"same_item": 1,
 			"free_qty": 1,
-			"company": "_Test Company"
+			"company": "_Test Company",
 		}
 		frappe.get_doc(test_record.copy()).insert()
 
@@ -377,17 +397,18 @@
 		self.assertEqual(so.items[1].is_free_item, 1)
 		self.assertEqual(so.items[1].item_code, "_Test Item")
 
-
 	def test_pricing_rule_for_product_discount_on_different_item(self):
-		frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
+		frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
 		test_record = {
 			"doctype": "Pricing Rule",
 			"title": "_Test Pricing Rule",
 			"apply_on": "Item Code",
 			"currency": "USD",
-			"items": [{
-				"item_code": "_Test Item",
-			}],
+			"items": [
+				{
+					"item_code": "_Test Item",
+				}
+			],
 			"selling": 1,
 			"rate_or_discount": "Discount Percentage",
 			"rate": 0,
@@ -398,7 +419,7 @@
 			"same_item": 0,
 			"free_item": "_Test Item 2",
 			"free_qty": 1,
-			"company": "_Test Company"
+			"company": "_Test Company",
 		}
 		frappe.get_doc(test_record.copy()).insert()
 
@@ -409,15 +430,17 @@
 		self.assertEqual(so.items[1].item_code, "_Test Item 2")
 
 	def test_cumulative_pricing_rule(self):
-		frappe.delete_doc_if_exists('Pricing Rule', '_Test Cumulative Pricing Rule')
+		frappe.delete_doc_if_exists("Pricing Rule", "_Test Cumulative Pricing Rule")
 		test_record = {
 			"doctype": "Pricing Rule",
 			"title": "_Test Cumulative Pricing Rule",
 			"apply_on": "Item Code",
 			"currency": "USD",
-			"items": [{
-				"item_code": "_Test Item",
-			}],
+			"items": [
+				{
+					"item_code": "_Test Item",
+				}
+			],
 			"is_cumulative": 1,
 			"selling": 1,
 			"applicable_for": "Customer",
@@ -430,24 +453,26 @@
 			"price_or_product_discount": "Price",
 			"company": "_Test Company",
 			"valid_from": frappe.utils.nowdate(),
-			"valid_upto": frappe.utils.nowdate()
+			"valid_upto": frappe.utils.nowdate(),
 		}
 		frappe.get_doc(test_record.copy()).insert()
 
-		args = frappe._dict({
-			"item_code": "_Test Item",
-			"company": "_Test Company",
-			"price_list": "_Test Price List",
-			"currency": "_Test Currency",
-			"doctype": "Sales Invoice",
-			"conversion_rate": 1,
-			"price_list_currency": "_Test Currency",
-			"plc_conversion_rate": 1,
-			"order_type": "Sales",
-			"customer": "_Test Customer",
-			"name": None,
-			"transaction_date": frappe.utils.nowdate()
-		})
+		args = frappe._dict(
+			{
+				"item_code": "_Test Item",
+				"company": "_Test Company",
+				"price_list": "_Test Price List",
+				"currency": "_Test Currency",
+				"doctype": "Sales Invoice",
+				"conversion_rate": 1,
+				"price_list_currency": "_Test Currency",
+				"plc_conversion_rate": 1,
+				"order_type": "Sales",
+				"customer": "_Test Customer",
+				"name": None,
+				"transaction_date": frappe.utils.nowdate(),
+			}
+		)
 		details = get_item_details(args)
 
 		self.assertTrue(details)
@@ -455,8 +480,12 @@
 	def test_pricing_rule_for_condition(self):
 		frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
 
-		make_pricing_rule(selling=1, margin_type="Percentage", \
-			condition="customer=='_Test Customer 1' and is_return==0", discount_percentage=10)
+		make_pricing_rule(
+			selling=1,
+			margin_type="Percentage",
+			condition="customer=='_Test Customer 1' and is_return==0",
+			discount_percentage=10,
+		)
 
 		# Incorrect Customer and Correct is_return value
 		si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 2", is_return=0)
@@ -480,10 +509,20 @@
 		self.assertEqual(item.rate, 900)
 
 	def test_multiple_pricing_rules(self):
-		make_pricing_rule(discount_percentage=20, selling=1, priority=1, apply_multiple_pricing_rules=1,
-			title="_Test Pricing Rule 1")
-		make_pricing_rule(discount_percentage=10, selling=1, title="_Test Pricing Rule 2", priority=2,
-			apply_multiple_pricing_rules=1)
+		make_pricing_rule(
+			discount_percentage=20,
+			selling=1,
+			priority=1,
+			apply_multiple_pricing_rules=1,
+			title="_Test Pricing Rule 1",
+		)
+		make_pricing_rule(
+			discount_percentage=10,
+			selling=1,
+			title="_Test Pricing Rule 2",
+			priority=2,
+			apply_multiple_pricing_rules=1,
+		)
 		si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1)
 		self.assertEqual(si.items[0].discount_percentage, 30)
 		si.delete()
@@ -494,10 +533,21 @@
 	def test_multiple_pricing_rules_with_apply_discount_on_discounted_rate(self):
 		frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
 
-		make_pricing_rule(discount_percentage=20, selling=1, priority=1, apply_multiple_pricing_rules=1,
-			title="_Test Pricing Rule 1")
-		make_pricing_rule(discount_percentage=10, selling=1, priority=2,
-			apply_discount_on_rate=1, title="_Test Pricing Rule 2", apply_multiple_pricing_rules=1)
+		make_pricing_rule(
+			discount_percentage=20,
+			selling=1,
+			priority=1,
+			apply_multiple_pricing_rules=1,
+			title="_Test Pricing Rule 1",
+		)
+		make_pricing_rule(
+			discount_percentage=10,
+			selling=1,
+			priority=2,
+			apply_discount_on_rate=1,
+			title="_Test Pricing Rule 2",
+			apply_multiple_pricing_rules=1,
+		)
 
 		si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1)
 		self.assertEqual(si.items[0].discount_percentage, 28)
@@ -514,16 +564,18 @@
 			"doctype": "Pricing Rule",
 			"title": "_Test Water Flask Rule",
 			"apply_on": "Item Code",
-			"items": [{
-				"item_code": "Water Flask",
-			}],
+			"items": [
+				{
+					"item_code": "Water Flask",
+				}
+			],
 			"selling": 1,
 			"currency": "INR",
 			"rate_or_discount": "Rate",
 			"rate": 0,
 			"margin_type": "Percentage",
 			"margin_rate_or_amount": 2,
-			"company": "_Test Company"
+			"company": "_Test Company",
 		}
 		rule = frappe.get_doc(pricing_rule_record)
 		rule.insert()
@@ -550,9 +602,11 @@
 			"doctype": "Pricing Rule",
 			"title": "_Test Sanitizer Rule",
 			"apply_on": "Item Code",
-			"items": [{
-				"item_code": "Test Sanitizer Item",
-			}],
+			"items": [
+				{
+					"item_code": "Test Sanitizer Item",
+				}
+			],
 			"selling": 1,
 			"currency": "INR",
 			"rate_or_discount": "Rate",
@@ -560,63 +614,73 @@
 			"priority": 2,
 			"margin_type": "Percentage",
 			"margin_rate_or_amount": 0.0,
-			"company": "_Test Company"
+			"company": "_Test Company",
 		}
 
 		rule = frappe.get_doc(pricing_rule_record)
-		rule.rate_or_discount = 'Rate'
+		rule.rate_or_discount = "Rate"
 		rule.rate = 100.0
 		rule.insert()
 
 		rule1 = frappe.get_doc(pricing_rule_record)
-		rule1.currency = 'USD'
-		rule1.rate_or_discount = 'Rate'
+		rule1.currency = "USD"
+		rule1.rate_or_discount = "Rate"
 		rule1.rate = 2.0
 		rule1.priority = 1
 		rule1.insert()
 
-		args = frappe._dict({
-			"item_code": "Test Sanitizer Item",
-			"company": "_Test Company",
-			"price_list": "_Test Price List",
-			"currency": "USD",
-			"doctype": "Sales Invoice",
-			"conversion_rate": 1,
-			"price_list_currency": "_Test Currency",
-			"plc_conversion_rate": 1,
-			"order_type": "Sales",
-			"customer": "_Test Customer",
-			"name": None,
-			"transaction_date": frappe.utils.nowdate()
-		})
+		args = frappe._dict(
+			{
+				"item_code": "Test Sanitizer Item",
+				"company": "_Test Company",
+				"price_list": "_Test Price List",
+				"currency": "USD",
+				"doctype": "Sales Invoice",
+				"conversion_rate": 1,
+				"price_list_currency": "_Test Currency",
+				"plc_conversion_rate": 1,
+				"order_type": "Sales",
+				"customer": "_Test Customer",
+				"name": None,
+				"transaction_date": frappe.utils.nowdate(),
+			}
+		)
 
 		details = get_item_details(args)
 		self.assertEqual(details.price_list_rate, 2.0)
 
-
-		args = frappe._dict({
-			"item_code": "Test Sanitizer Item",
-			"company": "_Test Company",
-			"price_list": "_Test Price List",
-			"currency": "INR",
-			"doctype": "Sales Invoice",
-			"conversion_rate": 1,
-			"price_list_currency": "_Test Currency",
-			"plc_conversion_rate": 1,
-			"order_type": "Sales",
-			"customer": "_Test Customer",
-			"name": None,
-			"transaction_date": frappe.utils.nowdate()
-		})
+		args = frappe._dict(
+			{
+				"item_code": "Test Sanitizer Item",
+				"company": "_Test Company",
+				"price_list": "_Test Price List",
+				"currency": "INR",
+				"doctype": "Sales Invoice",
+				"conversion_rate": 1,
+				"price_list_currency": "_Test Currency",
+				"plc_conversion_rate": 1,
+				"order_type": "Sales",
+				"customer": "_Test Customer",
+				"name": None,
+				"transaction_date": frappe.utils.nowdate(),
+			}
+		)
 
 		details = get_item_details(args)
 		self.assertEqual(details.price_list_rate, 100.0)
 
 	def test_pricing_rule_for_transaction(self):
 		make_item("Water Flask 1")
-		frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
-		make_pricing_rule(selling=1, min_qty=5, price_or_product_discount="Product",
-			apply_on="Transaction", free_item="Water Flask 1", free_qty=1, free_item_rate=10)
+		frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
+		make_pricing_rule(
+			selling=1,
+			min_qty=5,
+			price_or_product_discount="Product",
+			apply_on="Transaction",
+			free_item="Water Flask 1",
+			free_qty=1,
+			free_item_rate=10,
+		)
 
 		si = create_sales_invoice(qty=5, do_not_submit=True)
 		self.assertEqual(len(si.items), 2)
@@ -637,14 +701,16 @@
 			"title": "_Test Water Flask Rule",
 			"apply_on": "Item Code",
 			"price_or_product_discount": "Price",
-			"items": [{
-				"item_code": "Water Flask",
-			}],
+			"items": [
+				{
+					"item_code": "Water Flask",
+				}
+			],
 			"selling": 1,
 			"currency": "INR",
 			"rate_or_discount": "Discount Percentage",
 			"discount_percentage": 20,
-			"company": "_Test Company"
+			"company": "_Test Company",
 		}
 		rule = frappe.get_doc(pricing_rule_record)
 		rule.insert()
@@ -669,12 +735,24 @@
 		item.delete()
 
 	def test_multiple_pricing_rules_with_min_qty(self):
-		make_pricing_rule(discount_percentage=20, selling=1, priority=1, min_qty=4,
-			apply_multiple_pricing_rules=1, title="_Test Pricing Rule with Min Qty - 1")
-		make_pricing_rule(discount_percentage=10, selling=1, priority=2, min_qty=4,
-			apply_multiple_pricing_rules=1, title="_Test Pricing Rule with Min Qty - 2")
+		make_pricing_rule(
+			discount_percentage=20,
+			selling=1,
+			priority=1,
+			min_qty=4,
+			apply_multiple_pricing_rules=1,
+			title="_Test Pricing Rule with Min Qty - 1",
+		)
+		make_pricing_rule(
+			discount_percentage=10,
+			selling=1,
+			priority=2,
+			min_qty=4,
+			apply_multiple_pricing_rules=1,
+			title="_Test Pricing Rule with Min Qty - 2",
+		)
 
-		si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1, currency="USD")
+		si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1)
 		item = si.items[0]
 		item.stock_qty = 1
 		si.save()
@@ -691,73 +769,86 @@
 
 test_dependencies = ["Campaign"]
 
+
 def make_pricing_rule(**args):
 	args = frappe._dict(args)
 
-	doc = frappe.get_doc({
-		"doctype": "Pricing Rule",
-		"title": args.title or "_Test Pricing Rule",
-		"company": args.company or "_Test Company",
-		"apply_on": args.apply_on or "Item Code",
-		"applicable_for": args.applicable_for,
-		"selling": args.selling or 0,
-		"currency": "USD",
-		"apply_discount_on_rate": args.apply_discount_on_rate or 0,
-		"buying": args.buying or 0,
-		"min_qty": args.min_qty or 0.0,
-		"max_qty": args.max_qty or 0.0,
-		"rate_or_discount": args.rate_or_discount or "Discount Percentage",
-		"discount_percentage": args.discount_percentage or 0.0,
-		"rate": args.rate or 0.0,
-		"margin_rate_or_amount": args.margin_rate_or_amount or 0.0,
-		"condition": args.condition or '',
-		"priority": args.priority or 1,
-		"discount_amount": args.discount_amount or 0.0,
-		"apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0
-	})
+	doc = frappe.get_doc(
+		{
+			"doctype": "Pricing Rule",
+			"title": args.title or "_Test Pricing Rule",
+			"company": args.company or "_Test Company",
+			"apply_on": args.apply_on or "Item Code",
+			"applicable_for": args.applicable_for,
+			"selling": args.selling or 0,
+			"currency": "USD",
+			"apply_discount_on_rate": args.apply_discount_on_rate or 0,
+			"buying": args.buying or 0,
+			"min_qty": args.min_qty or 0.0,
+			"max_qty": args.max_qty or 0.0,
+			"rate_or_discount": args.rate_or_discount or "Discount Percentage",
+			"discount_percentage": args.discount_percentage or 0.0,
+			"rate": args.rate or 0.0,
+			"margin_rate_or_amount": args.margin_rate_or_amount or 0.0,
+			"condition": args.condition or "",
+			"priority": args.priority or 1,
+			"discount_amount": args.discount_amount or 0.0,
+			"apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0,
+		}
+	)
 
-	for field in ["free_item", "free_qty", "free_item_rate", "priority",
-		"margin_type", "price_or_product_discount"]:
+	for field in [
+		"free_item",
+		"free_qty",
+		"free_item_rate",
+		"priority",
+		"margin_type",
+		"price_or_product_discount",
+	]:
 		if args.get(field):
 			doc.set(field, args.get(field))
 
-	apply_on = doc.apply_on.replace(' ', '_').lower()
-	child_table = {'Item Code': 'items', 'Item Group': 'item_groups', 'Brand': 'brands'}
+	apply_on = doc.apply_on.replace(" ", "_").lower()
+	child_table = {"Item Code": "items", "Item Group": "item_groups", "Brand": "brands"}
 
 	if doc.apply_on != "Transaction":
-		doc.append(child_table.get(doc.apply_on), {
-			apply_on: args.get(apply_on) or "_Test Item"
-		})
+		doc.append(child_table.get(doc.apply_on), {apply_on: args.get(apply_on) or "_Test Item"})
 
 	doc.insert(ignore_permissions=True)
 	if args.get(apply_on) and apply_on != "item_code":
 		doc.db_set(apply_on, args.get(apply_on))
 
-	applicable_for = doc.applicable_for.replace(' ', '_').lower()
+	applicable_for = doc.applicable_for.replace(" ", "_").lower()
 	if args.get(applicable_for):
 		doc.db_set(applicable_for, args.get(applicable_for))
 
 	return doc
 
+
 def setup_pricing_rule_data():
-	if not frappe.db.exists('Campaign', '_Test Campaign'):
-		frappe.get_doc({
-			'doctype': 'Campaign',
-			'campaign_name': '_Test Campaign',
-			'name': '_Test Campaign'
-		}).insert()
+	if not frappe.db.exists("Campaign", "_Test Campaign"):
+		frappe.get_doc(
+			{"doctype": "Campaign", "campaign_name": "_Test Campaign", "name": "_Test Campaign"}
+		).insert()
+
 
 def delete_existing_pricing_rules():
-	for doctype in ["Pricing Rule", "Pricing Rule Item Code",
-		"Pricing Rule Item Group", "Pricing Rule Brand"]:
+	for doctype in [
+		"Pricing Rule",
+		"Pricing Rule Item Code",
+		"Pricing Rule Item Group",
+		"Pricing Rule Brand",
+	]:
 
 		frappe.db.sql("delete from `tab{0}`".format(doctype))
 
 
 def make_item_price(item, price_list_name, item_price):
-	frappe.get_doc({
-		'doctype': 'Item Price',
-		'price_list': price_list_name,
-		'item_code': item,
-		'price_list_rate': item_price
-	}).insert(ignore_permissions=True, ignore_mandatory=True)
+	frappe.get_doc(
+		{
+			"doctype": "Item Price",
+			"price_list": price_list_name,
+			"item_code": item,
+			"price_list_rate": item_price,
+		}
+	).insert(ignore_permissions=True, ignore_mandatory=True)
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index 7792590..70926cf 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -16,22 +16,21 @@
 from erpnext.stock.get_item_details import get_conversion_factor
 
 
-class MultiplePricingRuleConflict(frappe.ValidationError): pass
+class MultiplePricingRuleConflict(frappe.ValidationError):
+	pass
 
-apply_on_table = {
-	'Item Code': 'items',
-	'Item Group': 'item_groups',
-	'Brand': 'brands'
-}
+
+apply_on_table = {"Item Code": "items", "Item Group": "item_groups", "Brand": "brands"}
+
 
 def get_pricing_rules(args, doc=None):
 	pricing_rules = []
-	values =  {}
+	values = {}
 
-	if not frappe.db.exists('Pricing Rule', {'disable': 0, args.transaction_type: 1}):
+	if not frappe.db.exists("Pricing Rule", {"disable": 0, args.transaction_type: 1}):
 		return
 
-	for apply_on in ['Item Code', 'Item Group', 'Brand']:
+	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):
 			break
@@ -40,7 +39,8 @@
 
 	pricing_rules = filter_pricing_rule_based_on_condition(pricing_rules, doc)
 
-	if not pricing_rules: return []
+	if not pricing_rules:
+		return []
 
 	if apply_multiple_pricing_rules(pricing_rules):
 		pricing_rules = sorted_by_priority(pricing_rules, args, doc)
@@ -56,6 +56,7 @@
 
 	return rules
 
+
 def sorted_by_priority(pricing_rules, args, doc=None):
 	# If more than one pricing rules, then sort by priority
 	pricing_rules_list = []
@@ -64,10 +65,10 @@
 	for pricing_rule in pricing_rules:
 		pricing_rule = filter_pricing_rules(args, pricing_rule, doc)
 		if pricing_rule:
-			if not pricing_rule.get('priority'):
-				pricing_rule['priority'] = 1
+			if not pricing_rule.get("priority"):
+				pricing_rule["priority"] = 1
 
-			if pricing_rule.get('apply_multiple_pricing_rules'):
+			if pricing_rule.get("apply_multiple_pricing_rules"):
 				pricing_rule_dict.setdefault(cint(pricing_rule.get("priority")), []).append(pricing_rule)
 
 	for key in sorted(pricing_rule_dict):
@@ -75,6 +76,7 @@
 
 	return pricing_rules_list
 
+
 def filter_pricing_rule_based_on_condition(pricing_rules, doc=None):
 	filtered_pricing_rules = []
 	if doc:
@@ -92,40 +94,48 @@
 
 	return filtered_pricing_rules
 
+
 def _get_pricing_rules(apply_on, args, values):
 	apply_on_field = frappe.scrub(apply_on)
 
-	if not args.get(apply_on_field): return []
+	if not args.get(apply_on_field):
+		return []
 
-	child_doc = '`tabPricing Rule {0}`'.format(apply_on)
+	child_doc = "`tabPricing Rule {0}`".format(apply_on)
 
 	conditions = item_variant_condition = item_conditions = ""
 	values[apply_on_field] = args.get(apply_on_field)
-	if apply_on_field in ['item_code', 'brand']:
-		item_conditions = "{child_doc}.{apply_on_field}= %({apply_on_field})s".format(child_doc=child_doc,
-			apply_on_field = apply_on_field)
+	if apply_on_field in ["item_code", "brand"]:
+		item_conditions = "{child_doc}.{apply_on_field}= %({apply_on_field})s".format(
+			child_doc=child_doc, apply_on_field=apply_on_field
+		)
 
-		if apply_on_field == 'item_code':
+		if apply_on_field == "item_code":
 			if "variant_of" not in args:
 				args.variant_of = frappe.get_cached_value("Item", args.item_code, "variant_of")
 
 			if args.variant_of:
-				item_variant_condition = ' or {child_doc}.item_code=%(variant_of)s '.format(child_doc=child_doc)
-				values['variant_of'] = args.variant_of
-	elif apply_on_field == 'item_group':
+				item_variant_condition = " or {child_doc}.item_code=%(variant_of)s ".format(
+					child_doc=child_doc
+				)
+				values["variant_of"] = args.variant_of
+	elif apply_on_field == "item_group":
 		item_conditions = _get_tree_conditions(args, "Item Group", child_doc, False)
 
 	conditions += get_other_conditions(conditions, values, args)
-	warehouse_conditions = _get_tree_conditions(args, "Warehouse", '`tabPricing Rule`')
+	warehouse_conditions = _get_tree_conditions(args, "Warehouse", "`tabPricing Rule`")
 	if warehouse_conditions:
 		warehouse_conditions = " and {0}".format(warehouse_conditions)
 
-	if not args.price_list: args.price_list = None
+	if not args.price_list:
+		args.price_list = None
 
 	conditions += " and ifnull(`tabPricing Rule`.for_price_list, '') in (%(price_list)s, '')"
 	values["price_list"] = args.get("price_list")
 
-	pricing_rules = frappe.db.sql("""select `tabPricing Rule`.*,
+	pricing_rules = (
+		frappe.db.sql(
+			"""select `tabPricing Rule`.*,
 			{child_doc}.{apply_on_field}, {child_doc}.uom
 		from `tabPricing Rule`, {child_doc}
 		where ({item_conditions} or (`tabPricing Rule`.apply_rule_on_other is not null
@@ -135,25 +145,35 @@
 			`tabPricing Rule`.{transaction_type} = 1 {warehouse_cond} {conditions}
 		order by `tabPricing Rule`.priority desc,
 			`tabPricing Rule`.name desc""".format(
-			child_doc = child_doc,
-			apply_on_field = apply_on_field,
-			item_conditions = item_conditions,
-			item_variant_condition = item_variant_condition,
-			transaction_type = args.transaction_type,
-			warehouse_cond = warehouse_conditions,
-			apply_on_other_field = "other_{0}".format(apply_on_field),
-			conditions = conditions), values, as_dict=1) or []
+				child_doc=child_doc,
+				apply_on_field=apply_on_field,
+				item_conditions=item_conditions,
+				item_variant_condition=item_variant_condition,
+				transaction_type=args.transaction_type,
+				warehouse_cond=warehouse_conditions,
+				apply_on_other_field="other_{0}".format(apply_on_field),
+				conditions=conditions,
+			),
+			values,
+			as_dict=1,
+		)
+		or []
+	)
 
 	return pricing_rules
 
-def apply_multiple_pricing_rules(pricing_rules):
-	apply_multiple_rule = [d.apply_multiple_pricing_rules
-		for d in pricing_rules if d.apply_multiple_pricing_rules]
 
-	if not apply_multiple_rule: return False
+def apply_multiple_pricing_rules(pricing_rules):
+	apply_multiple_rule = [
+		d.apply_multiple_pricing_rules for d in pricing_rules if d.apply_multiple_pricing_rules
+	]
+
+	if not apply_multiple_rule:
+		return False
 
 	return True
 
+
 def _get_tree_conditions(args, parenttype, table, allow_blank=True):
 	field = frappe.scrub(parenttype)
 	condition = ""
@@ -169,28 +189,37 @@
 		except TypeError:
 			frappe.throw(_("Invalid {0}").format(args.get(field)))
 
-		parent_groups = frappe.db.sql_list("""select name from `tab%s`
-			where lft<=%s and rgt>=%s""" % (parenttype, '%s', '%s'), (lft, rgt))
+		parent_groups = frappe.db.sql_list(
+			"""select name from `tab%s`
+			where lft<=%s and rgt>=%s"""
+			% (parenttype, "%s", "%s"),
+			(lft, rgt),
+		)
 
 		if parenttype in ["Customer Group", "Item Group", "Territory"]:
 			parent_field = "parent_{0}".format(frappe.scrub(parenttype))
-			root_name = frappe.db.get_list(parenttype,
-				{"is_group": 1, parent_field: ("is", "not set")}, "name", as_list=1, ignore_permissions=True)
+			root_name = frappe.db.get_list(
+				parenttype,
+				{"is_group": 1, parent_field: ("is", "not set")},
+				"name",
+				as_list=1,
+				ignore_permissions=True,
+			)
 
 			if root_name and root_name[0][0]:
 				parent_groups.append(root_name[0][0])
 
 		if parent_groups:
-			if allow_blank: parent_groups.append('')
+			if allow_blank:
+				parent_groups.append("")
 			condition = "ifnull({table}.{field}, '') in ({parent_groups})".format(
-				table=table,
-				field=field,
-				parent_groups=", ".join(frappe.db.escape(d) for d in parent_groups)
+				table=table, field=field, parent_groups=", ".join(frappe.db.escape(d) for d in parent_groups)
 			)
 
 			frappe.flags.tree_conditions[key] = condition
 	return condition
 
+
 def get_other_conditions(conditions, values, args):
 	for field in ["company", "customer", "supplier", "campaign", "sales_partner"]:
 		if args.get(field):
@@ -200,17 +229,18 @@
 			conditions += " and ifnull(`tabPricing Rule`.{0}, '') = ''".format(field)
 
 	for parenttype in ["Customer Group", "Territory", "Supplier Group"]:
-		group_condition = _get_tree_conditions(args, parenttype, '`tabPricing Rule`')
+		group_condition = _get_tree_conditions(args, parenttype, "`tabPricing Rule`")
 		if group_condition:
 			conditions += " and " + group_condition
 
 	if args.get("transaction_date"):
 		conditions += """ and %(transaction_date)s between ifnull(`tabPricing Rule`.valid_from, '2000-01-01')
 			and ifnull(`tabPricing Rule`.valid_upto, '2500-12-31')"""
-		values['transaction_date'] = args.get('transaction_date')
+		values["transaction_date"] = args.get("transaction_date")
 
 	return conditions
 
+
 def filter_pricing_rules(args, pricing_rules, doc=None):
 	if not isinstance(pricing_rules, list):
 		pricing_rules = [pricing_rules]
@@ -219,15 +249,16 @@
 
 	# filter for qty
 	if pricing_rules:
-		stock_qty = flt(args.get('stock_qty'))
-		amount = flt(args.get('price_list_rate')) * flt(args.get('qty'))
+		stock_qty = flt(args.get("stock_qty"))
+		amount = flt(args.get("price_list_rate")) * flt(args.get("qty"))
 
 		if pricing_rules[0].apply_rule_on_other:
 			field = frappe.scrub(pricing_rules[0].apply_rule_on_other)
 
-			if (field and pricing_rules[0].get('other_' + field) != args.get(field)): return
+			if field and pricing_rules[0].get("other_" + field) != args.get(field):
+				return
 
-		pr_doc = frappe.get_cached_doc('Pricing Rule', pricing_rules[0].name)
+		pr_doc = frappe.get_cached_doc("Pricing Rule", pricing_rules[0].name)
 
 		if pricing_rules[0].mixed_conditions and doc:
 			stock_qty, amount, items = get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args)
@@ -235,7 +266,7 @@
 				pricing_rule_args.apply_rule_on_other_items = items
 
 		elif pricing_rules[0].is_cumulative:
-			items = [args.get(frappe.scrub(pr_doc.get('apply_on')))]
+			items = [args.get(frappe.scrub(pr_doc.get("apply_on")))]
 			data = get_qty_amount_data_for_cumulative(pr_doc, args, items)
 
 			if data:
@@ -249,13 +280,15 @@
 
 		if not pricing_rules:
 			for d in original_pricing_rule:
-				if not d.threshold_percentage: continue
+				if not d.threshold_percentage:
+					continue
 
-				msg = validate_quantity_and_amount_for_suggestion(d, stock_qty,
-					amount, args.get('item_code'), args.get('transaction_type'))
+				msg = validate_quantity_and_amount_for_suggestion(
+					d, stock_qty, amount, args.get("item_code"), args.get("transaction_type")
+				)
 
 				if msg:
-					return {'suggestion': msg, 'item_code': args.get('item_code')}
+					return {"suggestion": msg, "item_code": args.get("item_code")}
 
 		# add variant_of property in pricing rule
 		for p in pricing_rules:
@@ -265,7 +298,7 @@
 				p.variant_of = None
 
 	if len(pricing_rules) > 1:
-		filtered_rules = list(filter(lambda x: x.currency==args.get('currency'), pricing_rules))
+		filtered_rules = list(filter(lambda x: x.currency == args.get("currency"), pricing_rules))
 		if filtered_rules:
 			pricing_rules = filtered_rules
 
@@ -273,7 +306,7 @@
 	if pricing_rules:
 		max_priority = max(cint(p.priority) for p in pricing_rules)
 		if max_priority:
-			pricing_rules = list(filter(lambda x: cint(x.priority)==max_priority, pricing_rules))
+			pricing_rules = list(filter(lambda x: cint(x.priority) == max_priority, pricing_rules))
 
 	if pricing_rules and not isinstance(pricing_rules, list):
 		pricing_rules = list(pricing_rules)
@@ -281,42 +314,61 @@
 	if len(pricing_rules) > 1:
 		rate_or_discount = list(set(d.rate_or_discount for d in pricing_rules))
 		if len(rate_or_discount) == 1 and rate_or_discount[0] == "Discount Percentage":
-			pricing_rules = list(filter(lambda x: x.for_price_list==args.price_list, pricing_rules)) \
-				or pricing_rules
+			pricing_rules = (
+				list(filter(lambda x: x.for_price_list == args.price_list, pricing_rules)) or pricing_rules
+			)
 
 	if len(pricing_rules) > 1 and not args.for_shopping_cart:
-		frappe.throw(_("Multiple Price Rules exists with same criteria, please resolve conflict by assigning priority. Price Rules: {0}")
-			.format("\n".join(d.name for d in pricing_rules)), MultiplePricingRuleConflict)
+		frappe.throw(
+			_(
+				"Multiple Price Rules exists with same criteria, please resolve conflict by assigning priority. Price Rules: {0}"
+			).format("\n".join(d.name for d in pricing_rules)),
+			MultiplePricingRuleConflict,
+		)
 	elif pricing_rules:
 		return pricing_rules[0]
 
-def validate_quantity_and_amount_for_suggestion(args, qty, amount, item_code, transaction_type):
-	fieldname, msg = '', ''
-	type_of_transaction = 'purchase' if transaction_type == 'buying' else 'sale'
 
-	for field, value in {'min_qty': qty, 'min_amt': amount}.items():
-		if (args.get(field) and value < args.get(field)
-			and (args.get(field) - cint(args.get(field) * args.threshold_percentage * 0.01)) <= value):
+def validate_quantity_and_amount_for_suggestion(args, qty, amount, item_code, transaction_type):
+	fieldname, msg = "", ""
+	type_of_transaction = "purchase" if transaction_type == "buying" else "sale"
+
+	for field, value in {"min_qty": qty, "min_amt": amount}.items():
+		if (
+			args.get(field)
+			and value < args.get(field)
+			and (args.get(field) - cint(args.get(field) * args.threshold_percentage * 0.01)) <= value
+		):
 			fieldname = field
 
-	for field, value in {'max_qty': qty, 'max_amt': amount}.items():
-		if (args.get(field) and value > args.get(field)
-			and (args.get(field) + cint(args.get(field) * args.threshold_percentage * 0.01)) >= value):
+	for field, value in {"max_qty": qty, "max_amt": amount}.items():
+		if (
+			args.get(field)
+			and value > args.get(field)
+			and (args.get(field) + cint(args.get(field) * args.threshold_percentage * 0.01)) >= value
+		):
 			fieldname = field
 
 	if fieldname:
-		msg = (_("If you {0} {1} quantities of the item {2}, the scheme {3} will be applied on the item.")
-			.format(type_of_transaction, args.get(fieldname), bold(item_code), bold(args.rule_description)))
+		msg = _(
+			"If you {0} {1} quantities of the item {2}, the scheme {3} will be applied on the item."
+		).format(
+			type_of_transaction, args.get(fieldname), bold(item_code), bold(args.rule_description)
+		)
 
-		if fieldname in ['min_amt', 'max_amt']:
-			msg = (_("If you {0} {1} worth item {2}, the scheme {3} will be applied on the item.")
-				.format(type_of_transaction, fmt_money(args.get(fieldname), currency=args.get("currency")),
-					bold(item_code), bold(args.rule_description)))
+		if fieldname in ["min_amt", "max_amt"]:
+			msg = _("If you {0} {1} worth item {2}, the scheme {3} will be applied on the item.").format(
+				type_of_transaction,
+				fmt_money(args.get(fieldname), currency=args.get("currency")),
+				bold(item_code),
+				bold(args.rule_description),
+			)
 
 		frappe.msgprint(msg)
 
 	return msg
 
+
 def filter_pricing_rules_for_qty_amount(qty, rate, pricing_rules, args=None):
 	rules = []
 
@@ -327,16 +379,19 @@
 		if rule.get("uom"):
 			conversion_factor = get_conversion_factor(rule.item_code, rule.uom).get("conversion_factor", 1)
 
-		if (flt(qty) >= (flt(rule.min_qty) * conversion_factor)
-			and (flt(qty)<= (rule.max_qty * conversion_factor) if rule.max_qty else True)):
+		if flt(qty) >= (flt(rule.min_qty) * conversion_factor) and (
+			flt(qty) <= (rule.max_qty * conversion_factor) if rule.max_qty else True
+		):
 			status = True
 
 		# if user has created item price against the transaction UOM
 		if args and rule.get("uom") == args.get("uom"):
 			conversion_factor = 1.0
 
-		if status and (flt(rate) >= (flt(rule.min_amt) * conversion_factor)
-			and (flt(rate)<= (rule.max_amt * conversion_factor) if rule.max_amt else True)):
+		if status and (
+			flt(rate) >= (flt(rule.min_amt) * conversion_factor)
+			and (flt(rate) <= (rule.max_amt * conversion_factor) if rule.max_amt else True)
+		):
 			status = True
 		else:
 			status = False
@@ -346,6 +401,7 @@
 
 	return rules
 
+
 def if_all_rules_same(pricing_rules, fields):
 	all_rules_same = True
 	val = [pricing_rules[0].get(k) for k in fields]
@@ -356,30 +412,34 @@
 
 	return all_rules_same
 
+
 def apply_internal_priority(pricing_rules, field_set, args):
 	filtered_rules = []
 	for field in field_set:
 		if args.get(field):
 			# filter function always returns a filter object even if empty
 			# list conversion is necessary to check for an empty result
-			filtered_rules = list(filter(lambda x: x.get(field)==args.get(field), pricing_rules))
-			if filtered_rules: break
+			filtered_rules = list(filter(lambda x: x.get(field) == args.get(field), pricing_rules))
+			if filtered_rules:
+				break
 
 	return filtered_rules or pricing_rules
 
+
 def get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args):
 	sum_qty, sum_amt = [0, 0]
 	items = get_pricing_rule_items(pr_doc) or []
-	apply_on = frappe.scrub(pr_doc.get('apply_on'))
+	apply_on = frappe.scrub(pr_doc.get("apply_on"))
 
 	if items and doc.get("items"):
-		for row in doc.get('items'):
-			if (row.get(apply_on) or args.get(apply_on)) not in items: continue
+		for row in doc.get("items"):
+			if (row.get(apply_on) or args.get(apply_on)) not in items:
+				continue
 
 			if pr_doc.mixed_conditions:
-				amt = args.get('qty') * args.get("price_list_rate")
+				amt = args.get("qty") * args.get("price_list_rate")
 				if args.get("item_code") != row.get("item_code"):
-					amt = flt(row.get('qty')) * flt(row.get("price_list_rate") or args.get("rate"))
+					amt = flt(row.get("qty")) * flt(row.get("price_list_rate") or args.get("rate"))
 
 				sum_qty += flt(row.get("stock_qty")) or flt(args.get("stock_qty")) or flt(args.get("qty"))
 				sum_amt += amt
@@ -393,28 +453,33 @@
 
 	return sum_qty, sum_amt, items
 
+
 def get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules):
 	items = get_pricing_rule_items(pr_doc)
 
 	for row in doc.items:
 		if row.get(frappe.scrub(pr_doc.apply_rule_on_other)) in items:
-			pricing_rules = filter_pricing_rules_for_qty_amount(row.get("stock_qty"),
-				row.get("amount"), pricing_rules, row)
+			pricing_rules = filter_pricing_rules_for_qty_amount(
+				row.get("stock_qty"), row.get("amount"), pricing_rules, row
+			)
 
 			if pricing_rules and pricing_rules[0]:
 				pricing_rules[0].apply_rule_on_other_items = items
 				return pricing_rules
 
+
 def get_qty_amount_data_for_cumulative(pr_doc, doc, items=None):
 	if items is None:
 		items = []
 	sum_qty, sum_amt = [0, 0]
-	doctype = doc.get('parenttype') or doc.doctype
+	doctype = doc.get("parenttype") or doc.doctype
 
-	date_field = 'transaction_date' if frappe.get_meta(doctype).has_field('transaction_date') else 'posting_date'
+	date_field = (
+		"transaction_date" if frappe.get_meta(doctype).has_field("transaction_date") else "posting_date"
+	)
 
-	child_doctype = '{0} Item'.format(doctype)
-	apply_on = frappe.scrub(pr_doc.get('apply_on'))
+	child_doctype = "{0} Item".format(doctype)
+	apply_on = frappe.scrub(pr_doc.get("apply_on"))
 
 	values = [pr_doc.valid_from, pr_doc.valid_upto]
 	condition = ""
@@ -423,72 +488,86 @@
 		warehouses = get_child_warehouses(pr_doc.warehouse)
 
 		condition += """ and `tab{child_doc}`.warehouse in ({warehouses})
-			""".format(child_doc=child_doctype, warehouses = ','.join(['%s'] * len(warehouses)))
+			""".format(
+			child_doc=child_doctype, warehouses=",".join(["%s"] * len(warehouses))
+		)
 
 		values.extend(warehouses)
 
 	if items:
-		condition = " and `tab{child_doc}`.{apply_on} in ({items})".format(child_doc = child_doctype,
-			apply_on = apply_on, items = ','.join(['%s'] * len(items)))
+		condition = " and `tab{child_doc}`.{apply_on} in ({items})".format(
+			child_doc=child_doctype, apply_on=apply_on, items=",".join(["%s"] * len(items))
+		)
 
 		values.extend(items)
 
-	data_set = frappe.db.sql(""" SELECT `tab{child_doc}`.stock_qty,
+	data_set = frappe.db.sql(
+		""" SELECT `tab{child_doc}`.stock_qty,
 			`tab{child_doc}`.amount
 		FROM `tab{child_doc}`, `tab{parent_doc}`
 		WHERE
 			`tab{child_doc}`.parent = `tab{parent_doc}`.name and `tab{parent_doc}`.{date_field}
 			between %s and %s and `tab{parent_doc}`.docstatus = 1
 			{condition} group by `tab{child_doc}`.name
-	""".format(parent_doc = doctype,
-		child_doc = child_doctype,
-		condition = condition,
-		date_field = date_field
-	), tuple(values), as_dict=1)
+	""".format(
+			parent_doc=doctype, child_doc=child_doctype, condition=condition, date_field=date_field
+		),
+		tuple(values),
+		as_dict=1,
+	)
 
 	for data in data_set:
-		sum_qty += data.get('stock_qty')
-		sum_amt += data.get('amount')
+		sum_qty += data.get("stock_qty")
+		sum_amt += data.get("amount")
 
 	return [sum_qty, sum_amt]
 
+
 def apply_pricing_rule_on_transaction(doc):
 	conditions = "apply_on = 'Transaction'"
 
 	values = {}
 	conditions = get_other_conditions(conditions, values, doc)
 
-	pricing_rules = frappe.db.sql(""" Select `tabPricing Rule`.* from `tabPricing Rule`
+	pricing_rules = frappe.db.sql(
+		""" Select `tabPricing Rule`.* from `tabPricing Rule`
 		where  {conditions} and `tabPricing Rule`.disable = 0
-	""".format(conditions = conditions), values, as_dict=1)
+	""".format(
+			conditions=conditions
+		),
+		values,
+		as_dict=1,
+	)
 
 	if pricing_rules:
-		pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty,
-			doc.total, pricing_rules)
+		pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty, doc.total, pricing_rules)
 
 		if not pricing_rules:
 			remove_free_item(doc)
 
 		for d in pricing_rules:
-			if d.price_or_product_discount == 'Price':
+			if d.price_or_product_discount == "Price":
 				if d.apply_discount_on:
-					doc.set('apply_discount_on', d.apply_discount_on)
+					doc.set("apply_discount_on", d.apply_discount_on)
 
-				for field in ['additional_discount_percentage', 'discount_amount']:
-					pr_field = ('discount_percentage'
-						if field == 'additional_discount_percentage' else field)
+				for field in ["additional_discount_percentage", "discount_amount"]:
+					pr_field = "discount_percentage" if field == "additional_discount_percentage" else field
 
-					if not d.get(pr_field): continue
+					if not d.get(pr_field):
+						continue
 
-					if d.validate_applied_rule and doc.get(field) is not None and doc.get(field) < d.get(pr_field):
-						frappe.msgprint(_("User has not applied rule on the invoice {0}")
-							.format(doc.name))
+					if (
+						d.validate_applied_rule and doc.get(field) is not None and doc.get(field) < d.get(pr_field)
+					):
+						frappe.msgprint(_("User has not applied rule on the invoice {0}").format(doc.name))
 					else:
 						if not d.coupon_code_based:
 							doc.set(field, d.get(pr_field))
-						elif doc.get('coupon_code'):
+						elif doc.get("coupon_code"):
 							# coupon code based pricing rule
-							coupon_code_pricing_rule = frappe.db.get_value('Coupon Code', doc.get('coupon_code'), 'pricing_rule')
+							coupon_code_pricing_rule = frappe.db.get_value(
+								"Coupon Code", doc.get("coupon_code"), "pricing_rule"
+							)
 							if coupon_code_pricing_rule == d.name:
 								# if selected coupon code is linked with pricing rule
 								doc.set(field, d.get(pr_field))
@@ -500,83 +579,93 @@
 							doc.set(field, 0)
 
 				doc.calculate_taxes_and_totals()
-			elif d.price_or_product_discount == 'Product':
-				item_details = frappe._dict({'parenttype': doc.doctype, 'free_item_data': []})
+			elif d.price_or_product_discount == "Product":
+				item_details = frappe._dict({"parenttype": doc.doctype, "free_item_data": []})
 				get_product_discount_rule(d, item_details, doc=doc)
 				apply_pricing_rule_for_free_items(doc, item_details.free_item_data)
 				doc.set_missing_values()
 				doc.calculate_taxes_and_totals()
 
+
 def remove_free_item(doc):
 	for d in doc.items:
 		if d.is_free_item:
 			doc.remove(d)
 
+
 def get_applied_pricing_rules(pricing_rules):
 	if pricing_rules:
-		if pricing_rules.startswith('['):
+		if pricing_rules.startswith("["):
 			return json.loads(pricing_rules)
 		else:
-			return pricing_rules.split(',')
+			return pricing_rules.split(",")
 
 	return []
 
+
 def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
 	free_item = pricing_rule.free_item
-	if pricing_rule.same_item and pricing_rule.get("apply_on") != 'Transaction':
+	if pricing_rule.same_item and pricing_rule.get("apply_on") != "Transaction":
 		free_item = item_details.item_code or args.item_code
 
 	if not free_item:
-		frappe.throw(_("Free item not set in the pricing rule {0}")
-			.format(get_link_to_form("Pricing Rule", pricing_rule.name)))
+		frappe.throw(
+			_("Free item not set in the pricing rule {0}").format(
+				get_link_to_form("Pricing Rule", pricing_rule.name)
+			)
+		)
 
 	qty = pricing_rule.free_qty or 1
 	if pricing_rule.is_recursive:
-		transaction_qty = args.get('qty') if args else doc.total_qty
+		transaction_qty = args.get("qty") if args else doc.total_qty
 		if transaction_qty:
 			qty = flt(transaction_qty) * qty
 
 	free_item_data_args = {
-		'item_code': free_item,
-		'qty': qty,
-		'pricing_rules': pricing_rule.name,
-		'rate': pricing_rule.free_item_rate or 0,
-		'price_list_rate': pricing_rule.free_item_rate or 0,
-		'is_free_item': 1
+		"item_code": free_item,
+		"qty": qty,
+		"pricing_rules": pricing_rule.name,
+		"rate": pricing_rule.free_item_rate or 0,
+		"price_list_rate": pricing_rule.free_item_rate or 0,
+		"is_free_item": 1,
 	}
 
-	item_data = frappe.get_cached_value('Item', free_item, ['item_name',
-		'description', 'stock_uom'], as_dict=1)
+	item_data = frappe.get_cached_value(
+		"Item", free_item, ["item_name", "description", "stock_uom"], as_dict=1
+	)
 
 	free_item_data_args.update(item_data)
-	free_item_data_args['uom'] = pricing_rule.free_item_uom or item_data.stock_uom
-	free_item_data_args['conversion_factor'] = get_conversion_factor(free_item,
-		free_item_data_args['uom']).get("conversion_factor", 1)
+	free_item_data_args["uom"] = pricing_rule.free_item_uom or item_data.stock_uom
+	free_item_data_args["conversion_factor"] = get_conversion_factor(
+		free_item, free_item_data_args["uom"]
+	).get("conversion_factor", 1)
 
-	if item_details.get("parenttype") == 'Purchase Order':
-		free_item_data_args['schedule_date'] = doc.schedule_date if doc else today()
+	if item_details.get("parenttype") == "Purchase Order":
+		free_item_data_args["schedule_date"] = doc.schedule_date if doc else today()
 
-	if item_details.get("parenttype") == 'Sales Order':
-		free_item_data_args['delivery_date'] = doc.delivery_date if doc else today()
+	if item_details.get("parenttype") == "Sales Order":
+		free_item_data_args["delivery_date"] = doc.delivery_date if doc else today()
 
 	item_details.free_item_data.append(free_item_data_args)
 
+
 def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False):
 	if pricing_rule_args:
 		items = tuple((d.item_code, d.pricing_rules) for d in doc.items if d.is_free_item)
 
 		for args in pricing_rule_args:
-			if not items or (args.get('item_code'), args.get('pricing_rules')) not in items:
-				doc.append('items', args)
+			if not items or (args.get("item_code"), args.get("pricing_rules")) not in items:
+				doc.append("items", args)
+
 
 def get_pricing_rule_items(pr_doc):
 	apply_on_data = []
-	apply_on = frappe.scrub(pr_doc.get('apply_on'))
+	apply_on = frappe.scrub(pr_doc.get("apply_on"))
 
-	pricing_rule_apply_on = apply_on_table.get(pr_doc.get('apply_on'))
+	pricing_rule_apply_on = apply_on_table.get(pr_doc.get("apply_on"))
 
 	for d in pr_doc.get(pricing_rule_apply_on):
-		if apply_on == 'item_group':
+		if apply_on == "item_group":
 			apply_on_data.extend(get_child_item_groups(d.get(apply_on)))
 		else:
 			apply_on_data.append(d.get(apply_on))
@@ -587,6 +676,7 @@
 
 	return list(set(apply_on_data))
 
+
 def validate_coupon_code(coupon_name):
 	coupon = frappe.get_doc("Coupon Code", coupon_name)
 
@@ -599,16 +689,21 @@
 	elif coupon.used >= coupon.maximum_use:
 		frappe.throw(_("Sorry, this coupon code is no longer valid"))
 
-def update_coupon_code_count(coupon_name,transaction_type):
-	coupon=frappe.get_doc("Coupon Code",coupon_name)
+
+def update_coupon_code_count(coupon_name, transaction_type):
+	coupon = frappe.get_doc("Coupon Code", coupon_name)
 	if coupon:
-		if transaction_type=='used':
-			if coupon.used<coupon.maximum_use:
-				coupon.used=coupon.used+1
+		if transaction_type == "used":
+			if coupon.used < coupon.maximum_use:
+				coupon.used = coupon.used + 1
 				coupon.save(ignore_permissions=True)
 			else:
-				frappe.throw(_("{0} Coupon used are {1}. Allowed quantity is exhausted").format(coupon.coupon_code,coupon.used))
-		elif transaction_type=='cancelled':
-			if coupon.used>0:
-				coupon.used=coupon.used-1
+				frappe.throw(
+					_("{0} Coupon used are {1}. Allowed quantity is exhausted").format(
+						coupon.coupon_code, coupon.used
+					)
+				)
+		elif transaction_type == "cancelled":
+			if coupon.used > 0:
+				coupon.used = coupon.used - 1
 				coupon.save(ignore_permissions=True)
diff --git a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py
index d544f97..8ec726b 100644
--- a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py
+++ b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py
@@ -11,7 +11,7 @@
 	convert_deferred_expense_to_expense,
 	convert_deferred_revenue_to_income,
 )
-from erpnext.accounts.general_ledger import make_reverse_gl_entries
+from erpnext.accounts.general_ledger import make_gl_entries
 
 
 class ProcessDeferredAccounting(Document):
@@ -21,17 +21,17 @@
 
 	def on_submit(self):
 		conditions = build_conditions(self.type, self.account, self.company)
-		if self.type == 'Income':
+		if self.type == "Income":
 			convert_deferred_revenue_to_income(self.name, self.start_date, self.end_date, conditions)
 		else:
 			convert_deferred_expense_to_expense(self.name, self.start_date, self.end_date, conditions)
 
 	def on_cancel(self):
-		self.ignore_linked_doctypes = ['GL Entry']
-		gl_entries = frappe.get_all('GL Entry', fields = ['*'],
-			filters={
-				'against_voucher_type': self.doctype,
-				'against_voucher': self.name
-			})
+		self.ignore_linked_doctypes = ["GL Entry"]
+		gl_entries = frappe.get_all(
+			"GL Entry",
+			fields=["*"],
+			filters={"against_voucher_type": self.doctype, "against_voucher": self.name},
+		)
 
-		make_reverse_gl_entries(gl_entries=gl_entries)
+		make_gl_entries(gl_entries=gl_entries, cancel=1)
diff --git a/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py b/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py
index 757d0fa..164ba6a 100644
--- a/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py
+++ b/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py
@@ -15,9 +15,12 @@
 
 class TestProcessDeferredAccounting(unittest.TestCase):
 	def test_creation_of_ledger_entry_on_submit(self):
-		''' test creation of gl entries on submission of document '''
-		deferred_account = create_account(account_name="Deferred Revenue",
-			parent_account="Current Liabilities - _TC", company="_Test Company")
+		"""test creation of gl entries on submission of document"""
+		deferred_account = create_account(
+			account_name="Deferred Revenue",
+			parent_account="Current Liabilities - _TC",
+			company="_Test Company",
+		)
 
 		item = create_item("_Test Item for Deferred Accounting")
 		item.enable_deferred_revenue = 1
@@ -25,7 +28,9 @@
 		item.no_of_months = 12
 		item.save()
 
-		si = create_sales_invoice(item=item.name, update_stock=0, posting_date="2019-01-10", do_not_submit=True)
+		si = create_sales_invoice(
+			item=item.name, update_stock=0, posting_date="2019-01-10", do_not_submit=True
+		)
 		si.items[0].enable_deferred_revenue = 1
 		si.items[0].service_start_date = "2019-01-10"
 		si.items[0].service_end_date = "2019-03-15"
@@ -33,20 +38,22 @@
 		si.save()
 		si.submit()
 
-		process_deferred_accounting = doc = frappe.get_doc(dict(
-			doctype='Process Deferred Accounting',
-			posting_date="2019-01-01",
-			start_date="2019-01-01",
-			end_date="2019-01-31",
-			type="Income"
-		))
+		process_deferred_accounting = doc = frappe.get_doc(
+			dict(
+				doctype="Process Deferred Accounting",
+				posting_date="2019-01-01",
+				start_date="2019-01-01",
+				end_date="2019-01-31",
+				type="Income",
+			)
+		)
 
 		process_deferred_accounting.insert()
 		process_deferred_accounting.submit()
 
 		expected_gle = [
 			[deferred_account, 33.85, 0.0, "2019-01-31"],
-			["Sales - _TC", 0.0, 33.85, "2019-01-31"]
+			["Sales - _TC", 0.0, 33.85, "2019-01-31"],
 		]
 
 		check_gl_entries(self, si.name, expected_gle, "2019-01-10")
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html
index f8d191c..82705a9 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html
@@ -64,10 +64,10 @@
 				<td></td>
 				<td><b>{{ frappe.format(row.account, {fieldtype: "Link"}) or "&nbsp;" }}</b></td>
 				<td style="text-align: right">
-					{{ row.account and frappe.utils.fmt_money(row.debit, currency=filters.presentation_currency) }}
+					{{ row.get('account', '') and frappe.utils.fmt_money(row.debit, currency=filters.presentation_currency) }}
 				</td>
 				<td style="text-align: right">
-					{{ row.account and frappe.utils.fmt_money(row.credit, currency=filters.presentation_currency) }}
+					{{ row.get('account', '') and frappe.utils.fmt_money(row.credit, currency=filters.presentation_currency) }}
 				</td>
 			{% endif %}
 				<td style="text-align: right">
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js
index 088c190..7dd77fb 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js
@@ -8,7 +8,7 @@
 	},
 	refresh: function(frm){
 		if(!frm.doc.__islocal) {
-			frm.add_custom_button('Send Emails',function(){
+			frm.add_custom_button(__('Send Emails'), function(){
 				frappe.call({
 					method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_emails",
 					args: {
@@ -24,7 +24,7 @@
 					}
 				});
 			});
-			frm.add_custom_button('Download',function(){
+			frm.add_custom_button(__('Download'), function(){
 				var url = frappe.urllib.get_full_url(
 					'/api/method/erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.download_statements?'
 					+ 'document_name='+encodeURIComponent(frm.doc.name))
@@ -51,6 +51,13 @@
 				}
 			}
 		});
+		frm.set_query("account", function() {
+			return {
+				filters: {
+					'company': frm.doc.company
+				}
+			};
+		});
 		if(frm.doc.__islocal){
 			frm.set_value('from_date', frappe.datetime.add_months(frappe.datetime.get_today(), -1));
 			frm.set_value('to_date', frappe.datetime.get_today());
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
index 1b34d6d..01f716d 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
@@ -23,130 +23,166 @@
 class ProcessStatementOfAccounts(Document):
 	def validate(self):
 		if not self.subject:
-			self.subject = 'Statement Of Accounts for {{ customer.name }}'
+			self.subject = "Statement Of Accounts for {{ customer.name }}"
 		if not self.body:
-			self.body = 'Hello {{ customer.name }},<br>PFA your Statement Of Accounts from {{ doc.from_date }} to {{ doc.to_date }}.'
+			self.body = "Hello {{ customer.name }},<br>PFA your Statement Of Accounts from {{ doc.from_date }} to {{ doc.to_date }}."
 
 		validate_template(self.subject)
 		validate_template(self.body)
 
 		if not self.customers:
-			frappe.throw(_('Customers not selected.'))
+			frappe.throw(_("Customers not selected."))
 
 		if self.enable_auto_email:
-			self.to_date = self.start_date
-			self.from_date = add_months(self.to_date, -1 * self.filter_duration)
+			if self.start_date and getdate(self.start_date) >= getdate(today()):
+				self.to_date = self.start_date
+				self.from_date = add_months(self.to_date, -1 * self.filter_duration)
 
 
 def get_report_pdf(doc, consolidated=True):
 	statement_dict = {}
-	ageing = ''
+	ageing = ""
 	base_template_path = "frappe/www/printview.html"
-	template_path = "erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html"
+	template_path = (
+		"erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html"
+	)
 
 	for entry in doc.customers:
 		if doc.include_ageing:
-			ageing_filters = frappe._dict({
-				'company': doc.company,
-				'report_date': doc.to_date,
-				'ageing_based_on': doc.ageing_based_on,
-				'range1': 30,
-				'range2': 60,
-				'range3': 90,
-				'range4': 120,
-				'customer': entry.customer
-			})
+			ageing_filters = frappe._dict(
+				{
+					"company": doc.company,
+					"report_date": doc.to_date,
+					"ageing_based_on": doc.ageing_based_on,
+					"range1": 30,
+					"range2": 60,
+					"range3": 90,
+					"range4": 120,
+					"customer": entry.customer,
+				}
+			)
 			col1, ageing = get_ageing(ageing_filters)
 
 			if ageing:
-				ageing[0]['ageing_based_on'] = doc.ageing_based_on
+				ageing[0]["ageing_based_on"] = doc.ageing_based_on
 
-		tax_id = frappe.get_doc('Customer', entry.customer).tax_id
-		presentation_currency = get_party_account_currency('Customer', entry.customer, doc.company) \
-				or doc.currency or get_company_currency(doc.company)
+		tax_id = frappe.get_doc("Customer", entry.customer).tax_id
+		presentation_currency = (
+			get_party_account_currency("Customer", entry.customer, doc.company)
+			or doc.currency
+			or get_company_currency(doc.company)
+		)
 		if doc.letter_head:
 			from frappe.www.printview import get_letter_head
+
 			letter_head = get_letter_head(doc, 0)
 
-		filters= frappe._dict({
-			'from_date': doc.from_date,
-			'to_date': doc.to_date,
-			'company': doc.company,
-			'finance_book': doc.finance_book if doc.finance_book else None,
-			'account': [doc.account] if doc.account else None,
-			'party_type': 'Customer',
-			'party': [entry.customer],
-			'presentation_currency': presentation_currency,
-			'group_by': doc.group_by,
-			'currency': doc.currency,
-			'cost_center': [cc.cost_center_name for cc in doc.cost_center],
-			'project': [p.project_name for p in doc.project],
-			'show_opening_entries': 0,
-			'include_default_book_entries': 0,
-			'tax_id': tax_id if tax_id else None
-		})
+		filters = frappe._dict(
+			{
+				"from_date": doc.from_date,
+				"to_date": doc.to_date,
+				"company": doc.company,
+				"finance_book": doc.finance_book if doc.finance_book else None,
+				"account": [doc.account] if doc.account else None,
+				"party_type": "Customer",
+				"party": [entry.customer],
+				"presentation_currency": presentation_currency,
+				"group_by": doc.group_by,
+				"currency": doc.currency,
+				"cost_center": [cc.cost_center_name for cc in doc.cost_center],
+				"project": [p.project_name for p in doc.project],
+				"show_opening_entries": 0,
+				"include_default_book_entries": 0,
+				"tax_id": tax_id if tax_id else None,
+			}
+		)
 		col, res = get_soa(filters)
 
 		for x in [0, -2, -1]:
-			res[x]['account'] = res[x]['account'].replace("'","")
+			res[x]["account"] = res[x]["account"].replace("'", "")
 
 		if len(res) == 3:
 			continue
 
-		html = frappe.render_template(template_path, \
-			{"filters": filters, "data": res, "ageing": ageing[0] if (doc.include_ageing and ageing) else None,
+		html = frappe.render_template(
+			template_path,
+			{
+				"filters": filters,
+				"data": res,
+				"ageing": ageing[0] if (doc.include_ageing and ageing) else None,
 				"letter_head": letter_head if doc.letter_head else None,
-				"terms_and_conditions": frappe.db.get_value('Terms and Conditions', doc.terms_and_conditions, 'terms')
-					if doc.terms_and_conditions else None})
+				"terms_and_conditions": frappe.db.get_value(
+					"Terms and Conditions", doc.terms_and_conditions, "terms"
+				)
+				if doc.terms_and_conditions
+				else None,
+			},
+		)
 
-		html = frappe.render_template(base_template_path, {"body": html, \
-			"css": get_print_style(), "title": "Statement For " + entry.customer})
+		html = frappe.render_template(
+			base_template_path,
+			{"body": html, "css": get_print_style(), "title": "Statement For " + entry.customer},
+		)
 		statement_dict[entry.customer] = html
 
 	if not bool(statement_dict):
 		return False
 	elif consolidated:
-		result = ''.join(list(statement_dict.values()))
-		return get_pdf(result, {'orientation': doc.orientation})
+		result = "".join(list(statement_dict.values()))
+		return get_pdf(result, {"orientation": doc.orientation})
 	else:
 		for customer, statement_html in statement_dict.items():
-			statement_dict[customer]=get_pdf(statement_html, {'orientation': doc.orientation})
+			statement_dict[customer] = get_pdf(statement_html, {"orientation": doc.orientation})
 		return statement_dict
 
+
 def get_customers_based_on_territory_or_customer_group(customer_collection, collection_name):
 	fields_dict = {
-		'Customer Group': 'customer_group',
-		'Territory': 'territory',
+		"Customer Group": "customer_group",
+		"Territory": "territory",
 	}
 	collection = frappe.get_doc(customer_collection, collection_name)
-	selected = [customer.name for customer in frappe.get_list(customer_collection, filters=[
-			['lft', '>=', collection.lft],
-			['rgt', '<=', collection.rgt]
-		],
-		fields=['name'],
-		order_by='lft asc, rgt desc'
-	)]
-	return frappe.get_list('Customer', fields=['name', 'email_id'], \
-		filters=[[fields_dict[customer_collection], 'IN', selected]])
+	selected = [
+		customer.name
+		for customer in frappe.get_list(
+			customer_collection,
+			filters=[["lft", ">=", collection.lft], ["rgt", "<=", collection.rgt]],
+			fields=["name"],
+			order_by="lft asc, rgt desc",
+		)
+	]
+	return frappe.get_list(
+		"Customer",
+		fields=["name", "email_id"],
+		filters=[[fields_dict[customer_collection], "IN", selected]],
+	)
+
 
 def get_customers_based_on_sales_person(sales_person):
-	lft, rgt = frappe.db.get_value("Sales Person",
-		sales_person, ["lft", "rgt"])
-	records = frappe.db.sql("""
+	lft, rgt = frappe.db.get_value("Sales Person", sales_person, ["lft", "rgt"])
+	records = frappe.db.sql(
+		"""
 		select distinct parent, parenttype
 		from `tabSales Team` steam
 		where parenttype = 'Customer'
 			and exists(select name from `tabSales Person` where lft >= %s and rgt <= %s and name = steam.sales_person)
-	""", (lft, rgt), as_dict=1)
+	""",
+		(lft, rgt),
+		as_dict=1,
+	)
 	sales_person_records = frappe._dict()
 	for d in records:
 		sales_person_records.setdefault(d.parenttype, set()).add(d.parent)
-	if sales_person_records.get('Customer'):
-		return frappe.get_list('Customer', fields=['name', 'email_id'], \
-			filters=[['name', 'in', list(sales_person_records['Customer'])]])
+	if sales_person_records.get("Customer"):
+		return frappe.get_list(
+			"Customer",
+			fields=["name", "email_id"],
+			filters=[["name", "in", list(sales_person_records["Customer"])]],
+		)
 	else:
 		return []
 
+
 def get_recipients_and_cc(customer, doc):
 	recipients = []
 	for clist in doc.customers:
@@ -155,65 +191,72 @@
 			if doc.primary_mandatory and clist.primary_email:
 				recipients.append(clist.primary_email)
 	cc = []
-	if doc.cc_to != '':
+	if doc.cc_to != "":
 		try:
-			cc=[frappe.get_value('User', doc.cc_to, 'email')]
+			cc = [frappe.get_value("User", doc.cc_to, "email")]
 		except Exception:
 			pass
 
 	return recipients, cc
 
+
 def get_context(customer, doc):
 	template_doc = copy.deepcopy(doc)
 	del template_doc.customers
 	template_doc.from_date = format_date(template_doc.from_date)
 	template_doc.to_date = format_date(template_doc.to_date)
 	return {
-		'doc': template_doc,
-		'customer': frappe.get_doc('Customer', customer),
-		'frappe': frappe.utils
+		"doc": template_doc,
+		"customer": frappe.get_doc("Customer", customer),
+		"frappe": frappe.utils,
 	}
 
+
 @frappe.whitelist()
 def fetch_customers(customer_collection, collection_name, primary_mandatory):
 	customer_list = []
 	customers = []
 
-	if customer_collection == 'Sales Person':
+	if customer_collection == "Sales Person":
 		customers = get_customers_based_on_sales_person(collection_name)
 		if not bool(customers):
-			frappe.throw(_('No Customers found with selected options.'))
+			frappe.throw(_("No Customers found with selected options."))
 	else:
-		if customer_collection == 'Sales Partner':
-			customers = frappe.get_list('Customer', fields=['name', 'email_id'], \
-				filters=[['default_sales_partner', '=', collection_name]])
+		if customer_collection == "Sales Partner":
+			customers = frappe.get_list(
+				"Customer",
+				fields=["name", "email_id"],
+				filters=[["default_sales_partner", "=", collection_name]],
+			)
 		else:
-			customers = get_customers_based_on_territory_or_customer_group(customer_collection, collection_name)
+			customers = get_customers_based_on_territory_or_customer_group(
+				customer_collection, collection_name
+			)
 
 	for customer in customers:
-		primary_email = customer.get('email_id') or ''
+		primary_email = customer.get("email_id") or ""
 		billing_email = get_customer_emails(customer.name, 1, billing_and_primary=False)
 
 		if int(primary_mandatory):
-			if (primary_email == ''):
+			if primary_email == "":
 				continue
-		elif (billing_email == '') and (primary_email == ''):
+		elif (billing_email == "") and (primary_email == ""):
 			continue
 
-		customer_list.append({
-			'name': customer.name,
-			'primary_email': primary_email,
-			'billing_email': billing_email
-		})
+		customer_list.append(
+			{"name": customer.name, "primary_email": primary_email, "billing_email": billing_email}
+		)
 	return customer_list
 
+
 @frappe.whitelist()
 def get_customer_emails(customer_name, primary_mandatory, billing_and_primary=True):
-	""" Returns first email from Contact Email table as a Billing email
-		when Is Billing Contact checked
-		and Primary email- email with Is Primary checked """
+	"""Returns first email from Contact Email table as a Billing email
+	when Is Billing Contact checked
+	and Primary email- email with Is Primary checked"""
 
-	billing_email = frappe.db.sql("""
+	billing_email = frappe.db.sql(
+		"""
 		SELECT
 			email.email_id
 		FROM
@@ -231,42 +274,43 @@
 			and link.link_name=%s
 			and contact.is_billing_contact=1
 		ORDER BY
-			contact.creation desc""", customer_name)
+			contact.creation desc""",
+		customer_name,
+	)
 
 	if len(billing_email) == 0 or (billing_email[0][0] is None):
 		if billing_and_primary:
 			frappe.throw(_("No billing email found for customer: {0}").format(customer_name))
 		else:
-			return ''
+			return ""
 
 	if billing_and_primary:
-		primary_email =  frappe.get_value('Customer', customer_name, 'email_id')
+		primary_email = frappe.get_value("Customer", customer_name, "email_id")
 		if primary_email is None and int(primary_mandatory):
 			frappe.throw(_("No primary email found for customer: {0}").format(customer_name))
-		return [primary_email or '', billing_email[0][0]]
+		return [primary_email or "", billing_email[0][0]]
 	else:
-		return billing_email[0][0] or ''
+		return billing_email[0][0] or ""
+
 
 @frappe.whitelist()
 def download_statements(document_name):
-	doc = frappe.get_doc('Process Statement Of Accounts', document_name)
+	doc = frappe.get_doc("Process Statement Of Accounts", document_name)
 	report = get_report_pdf(doc)
 	if report:
-		frappe.local.response.filename = doc.name + '.pdf'
+		frappe.local.response.filename = doc.name + ".pdf"
 		frappe.local.response.filecontent = report
 		frappe.local.response.type = "download"
 
+
 @frappe.whitelist()
 def send_emails(document_name, from_scheduler=False):
-	doc = frappe.get_doc('Process Statement Of Accounts', document_name)
+	doc = frappe.get_doc("Process Statement Of Accounts", document_name)
 	report = get_report_pdf(doc, consolidated=False)
 
 	if report:
 		for customer, report_pdf in report.items():
-			attachments = [{
-				'fname': customer + '.pdf',
-				'fcontent': report_pdf
-			}]
+			attachments = [{"fname": customer + ".pdf", "fcontent": report_pdf}]
 
 			recipients, cc = get_recipients_and_cc(customer, doc)
 			context = get_context(customer, doc)
@@ -274,7 +318,7 @@
 			message = frappe.render_template(doc.body, context)
 
 			frappe.enqueue(
-				queue='short',
+				queue="short",
 				method=frappe.sendmail,
 				recipients=recipients,
 				sender=frappe.session.user,
@@ -282,28 +326,34 @@
 				subject=subject,
 				message=message,
 				now=True,
-				reference_doctype='Process Statement Of Accounts',
+				reference_doctype="Process Statement Of Accounts",
 				reference_name=document_name,
-				attachments=attachments
+				attachments=attachments,
 			)
 
 		if doc.enable_auto_email and from_scheduler:
 			new_to_date = getdate(today())
-			if doc.frequency == 'Weekly':
+			if doc.frequency == "Weekly":
 				new_to_date = add_days(new_to_date, 7)
 			else:
-				new_to_date = add_months(new_to_date, 1 if doc.frequency == 'Monthly' else 3)
+				new_to_date = add_months(new_to_date, 1 if doc.frequency == "Monthly" else 3)
 			new_from_date = add_months(new_to_date, -1 * doc.filter_duration)
-			doc.add_comment('Comment', 'Emails sent on: ' + frappe.utils.format_datetime(frappe.utils.now()))
-			doc.db_set('to_date', new_to_date, commit=True)
-			doc.db_set('from_date', new_from_date, commit=True)
+			doc.add_comment(
+				"Comment", "Emails sent on: " + frappe.utils.format_datetime(frappe.utils.now())
+			)
+			doc.db_set("to_date", new_to_date, commit=True)
+			doc.db_set("from_date", new_from_date, commit=True)
 		return True
 	else:
 		return False
 
+
 @frappe.whitelist()
 def send_auto_email():
-	selected = frappe.get_list('Process Statement Of Accounts', filters={'to_date': format_date(today()), 'enable_auto_email': 1})
+	selected = frappe.get_list(
+		"Process Statement Of Accounts",
+		filters={"to_date": format_date(today()), "enable_auto_email": 1},
+	)
 	for entry in selected:
 		send_emails(entry.name, from_scheduler=True)
 	return True
diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py
index 5fbe93e..fac9be7 100644
--- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py
+++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py
@@ -6,29 +6,73 @@
 from frappe import _
 from frappe.model.document import Document
 
-pricing_rule_fields = ['apply_on', 'mixed_conditions', 'is_cumulative', 'other_item_code', 'other_item_group',
-	'apply_rule_on_other', 'other_brand', 'selling', 'buying', 'applicable_for', 'valid_from',
-	'valid_upto', 'customer', 'customer_group', 'territory', 'sales_partner', 'campaign', 'supplier',
-	'supplier_group', 'company', 'currency', 'apply_multiple_pricing_rules']
+pricing_rule_fields = [
+	"apply_on",
+	"mixed_conditions",
+	"is_cumulative",
+	"other_item_code",
+	"other_item_group",
+	"apply_rule_on_other",
+	"other_brand",
+	"selling",
+	"buying",
+	"applicable_for",
+	"valid_from",
+	"valid_upto",
+	"customer",
+	"customer_group",
+	"territory",
+	"sales_partner",
+	"campaign",
+	"supplier",
+	"supplier_group",
+	"company",
+	"currency",
+	"apply_multiple_pricing_rules",
+]
 
-other_fields = ['min_qty', 'max_qty', 'min_amt',
-	'max_amt', 'priority','warehouse', 'threshold_percentage', 'rule_description']
+other_fields = [
+	"min_qty",
+	"max_qty",
+	"min_amt",
+	"max_amt",
+	"priority",
+	"warehouse",
+	"threshold_percentage",
+	"rule_description",
+]
 
-price_discount_fields = ['rate_or_discount', 'apply_discount_on', 'apply_discount_on_rate',
-	'rate', 'discount_amount', 'discount_percentage', 'validate_applied_rule', 'apply_multiple_pricing_rules']
+price_discount_fields = [
+	"rate_or_discount",
+	"apply_discount_on",
+	"apply_discount_on_rate",
+	"rate",
+	"discount_amount",
+	"discount_percentage",
+	"validate_applied_rule",
+	"apply_multiple_pricing_rules",
+]
 
-product_discount_fields = ['free_item', 'free_qty', 'free_item_uom',
-	'free_item_rate', 'same_item', 'is_recursive', 'apply_multiple_pricing_rules']
+product_discount_fields = [
+	"free_item",
+	"free_qty",
+	"free_item_uom",
+	"free_item_rate",
+	"same_item",
+	"is_recursive",
+	"apply_multiple_pricing_rules",
+]
+
 
 class TransactionExists(frappe.ValidationError):
 	pass
 
+
 class PromotionalScheme(Document):
 	def validate(self):
 		if not self.selling and not self.buying:
 			frappe.throw(_("Either 'Selling' or 'Buying' must be selected"), title=_("Mandatory"))
-		if not (self.price_discount_slabs
-			or self.product_discount_slabs):
+		if not (self.price_discount_slabs or self.product_discount_slabs):
 			frappe.throw(_("Price or product discount slabs are required"))
 
 		self.validate_applicable_for()
@@ -39,7 +83,7 @@
 			applicable_for = frappe.scrub(self.applicable_for)
 
 			if not self.get(applicable_for):
-				msg = (f'The field {frappe.bold(self.applicable_for)} is required')
+				msg = f"The field {frappe.bold(self.applicable_for)} is required"
 				frappe.throw(_(msg))
 
 	def validate_pricing_rules(self):
@@ -53,28 +97,28 @@
 		if self._doc_before_save.applicable_for == self.applicable_for:
 			return
 
-		docnames = frappe.get_all('Pricing Rule',
-			filters= {'promotional_scheme': self.name})
+		docnames = frappe.get_all("Pricing Rule", filters={"promotional_scheme": self.name})
 
 		for docname in docnames:
-			if frappe.db.exists('Pricing Rule Detail',
-				{'pricing_rule': docname.name, 'docstatus': ('<', 2)}):
+			if frappe.db.exists(
+				"Pricing Rule Detail", {"pricing_rule": docname.name, "docstatus": ("<", 2)}
+			):
 				raise_for_transaction_exists(self.name)
 
 		if docnames and not transaction_exists:
 			for docname in docnames:
-				frappe.delete_doc('Pricing Rule', docname.name)
+				frappe.delete_doc("Pricing Rule", docname.name)
 
 	def on_update(self):
-		pricing_rules = frappe.get_all(
-			'Pricing Rule',
-			fields = ["promotional_scheme_id", "name", "creation"],
-			filters = {
-				'promotional_scheme': self.name,
-				'applicable_for': self.applicable_for
-			},
-			order_by = 'creation asc',
-		) or {}
+		pricing_rules = (
+			frappe.get_all(
+				"Pricing Rule",
+				fields=["promotional_scheme_id", "name", "creation"],
+				filters={"promotional_scheme": self.name, "applicable_for": self.applicable_for},
+				order_by="creation asc",
+			)
+			or {}
+		)
 		self.update_pricing_rules(pricing_rules)
 
 	def update_pricing_rules(self, pricing_rules):
@@ -83,7 +127,7 @@
 		names = []
 		for rule in pricing_rules:
 			names.append(rule.name)
-			rules[rule.get('promotional_scheme_id')] = names
+			rules[rule.get("promotional_scheme_id")] = names
 
 		docs = get_pricing_rules(self, rules)
 
@@ -100,34 +144,38 @@
 			frappe.msgprint(_("New {0} pricing rules are created").format(count))
 
 	def on_trash(self):
-		for rule in frappe.get_all('Pricing Rule',
-			{'promotional_scheme': self.name}):
-			frappe.delete_doc('Pricing Rule', rule.name)
+		for rule in frappe.get_all("Pricing Rule", {"promotional_scheme": self.name}):
+			frappe.delete_doc("Pricing Rule", rule.name)
+
 
 def raise_for_transaction_exists(name):
-	msg = (f"""You can't change the {frappe.bold(_('Applicable For'))}
-		because transactions are present against the Promotional Scheme {frappe.bold(name)}. """)
-	msg += 'Kindly disable this Promotional Scheme and create new for new Applicable For.'
+	msg = f"""You can't change the {frappe.bold(_('Applicable For'))}
+		because transactions are present against the Promotional Scheme {frappe.bold(name)}. """
+	msg += "Kindly disable this Promotional Scheme and create new for new Applicable For."
 
 	frappe.throw(_(msg), TransactionExists)
 
+
 def get_pricing_rules(doc, rules=None):
 	if rules is None:
 		rules = {}
 	new_doc = []
-	for child_doc, fields in {'price_discount_slabs': price_discount_fields,
-		'product_discount_slabs': product_discount_fields}.items():
+	for child_doc, fields in {
+		"price_discount_slabs": price_discount_fields,
+		"product_discount_slabs": product_discount_fields,
+	}.items():
 		if doc.get(child_doc):
 			new_doc.extend(_get_pricing_rules(doc, child_doc, fields, rules))
 
 	return new_doc
 
+
 def _get_pricing_rules(doc, child_doc, discount_fields, rules=None):
 	if rules is None:
 		rules = {}
 	new_doc = []
 	args = get_args_for_pricing_rule(doc)
-	applicable_for = frappe.scrub(doc.get('applicable_for'))
+	applicable_for = frappe.scrub(doc.get("applicable_for"))
 
 	for idx, d in enumerate(doc.get(child_doc)):
 		if d.name in rules:
@@ -138,15 +186,23 @@
 			else:
 				for applicable_for_value in args.get(applicable_for):
 					docname = get_pricing_rule_docname(d, applicable_for, applicable_for_value)
-					pr = prepare_pricing_rule(args, doc, child_doc, discount_fields,
-						d, docname, applicable_for, applicable_for_value)
+					pr = prepare_pricing_rule(
+						args, doc, child_doc, discount_fields, d, docname, applicable_for, applicable_for_value
+					)
 					new_doc.append(pr)
 
 		elif args.get(applicable_for):
 			applicable_for_values = args.get(applicable_for) or []
 			for applicable_for_value in applicable_for_values:
-				pr = prepare_pricing_rule(args, doc, child_doc, discount_fields,
-					d, applicable_for=applicable_for, value= applicable_for_value)
+				pr = prepare_pricing_rule(
+					args,
+					doc,
+					child_doc,
+					discount_fields,
+					d,
+					applicable_for=applicable_for,
+					value=applicable_for_value,
+				)
 
 				new_doc.append(pr)
 		else:
@@ -155,20 +211,24 @@
 
 	return new_doc
 
-def get_pricing_rule_docname(row: dict, applicable_for: str = None, applicable_for_value: str = None) -> str:
-	fields = ['promotional_scheme_id', 'name']
-	filters = {
-		'promotional_scheme_id': row.name
-	}
+
+def get_pricing_rule_docname(
+	row: dict, applicable_for: str = None, applicable_for_value: str = None
+) -> str:
+	fields = ["promotional_scheme_id", "name"]
+	filters = {"promotional_scheme_id": row.name}
 
 	if applicable_for:
 		fields.append(applicable_for)
 		filters[applicable_for] = applicable_for_value
 
-	docname = frappe.get_all('Pricing Rule', fields = fields, filters = filters)
-	return docname[0].name if docname else ''
+	docname = frappe.get_all("Pricing Rule", fields=fields, filters=filters)
+	return docname[0].name if docname else ""
 
-def prepare_pricing_rule(args, doc, child_doc, discount_fields, d, docname=None, applicable_for=None, value=None):
+
+def prepare_pricing_rule(
+	args, doc, child_doc, discount_fields, d, docname=None, applicable_for=None, value=None
+):
 	if docname:
 		pr = frappe.get_doc("Pricing Rule", docname)
 	else:
@@ -182,33 +242,31 @@
 
 	return set_args(temp_args, pr, doc, child_doc, discount_fields, d)
 
+
 def set_args(args, pr, doc, child_doc, discount_fields, child_doc_fields):
 	pr.update(args)
-	for field in (other_fields + discount_fields):
+	for field in other_fields + discount_fields:
 		pr.set(field, child_doc_fields.get(field))
 
 	pr.promotional_scheme_id = child_doc_fields.name
 	pr.promotional_scheme = doc.name
 	pr.disable = child_doc_fields.disable if child_doc_fields.disable else doc.disable
-	pr.price_or_product_discount = ('Price'
-		if child_doc == 'price_discount_slabs' else 'Product')
+	pr.price_or_product_discount = "Price" if child_doc == "price_discount_slabs" else "Product"
 
-	for field in ['items', 'item_groups', 'brands']:
+	for field in ["items", "item_groups", "brands"]:
 		if doc.get(field):
 			pr.set(field, [])
 
-		apply_on = frappe.scrub(doc.get('apply_on'))
+		apply_on = frappe.scrub(doc.get("apply_on"))
 		for d in doc.get(field):
-			pr.append(field, {
-				apply_on: d.get(apply_on),
-				'uom': d.uom
-			})
+			pr.append(field, {apply_on: d.get(apply_on), "uom": d.uom})
 
 	return pr
 
+
 def get_args_for_pricing_rule(doc):
-	args = { 'promotional_scheme': doc.name }
-	applicable_for = frappe.scrub(doc.get('applicable_for'))
+	args = {"promotional_scheme": doc.name}
+	applicable_for = frappe.scrub(doc.get("applicable_for"))
 
 	for d in pricing_rule_fields:
 		if d == applicable_for:
diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme_dashboard.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme_dashboard.py
index 6d07924..70e8740 100644
--- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme_dashboard.py
+++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme_dashboard.py
@@ -3,11 +3,6 @@
 
 def get_data():
 	return {
-		'fieldname': 'promotional_scheme',
-		'transactions': [
-			{
-				'label': _('Reference'),
-				'items': ['Pricing Rule']
-			}
-		]
+		"fieldname": "promotional_scheme",
+		"transactions": [{"label": _("Reference"), "items": ["Pricing Rule"]}],
 	}
diff --git a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py
index 49192a4..b3b9d7b 100644
--- a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py
+++ b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py
@@ -11,96 +11,111 @@
 
 class TestPromotionalScheme(unittest.TestCase):
 	def setUp(self):
-		if frappe.db.exists('Promotional Scheme', '_Test Scheme'):
-			frappe.delete_doc('Promotional Scheme', '_Test Scheme')
+		if frappe.db.exists("Promotional Scheme", "_Test Scheme"):
+			frappe.delete_doc("Promotional Scheme", "_Test Scheme")
 
 	def test_promotional_scheme(self):
-		ps = make_promotional_scheme(applicable_for='Customer', customer='_Test Customer')
-		price_rules = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name", "creation"],
-			filters = {'promotional_scheme': ps.name})
-		self.assertTrue(len(price_rules),1)
-		price_doc_details = frappe.db.get_value('Pricing Rule', price_rules[0].name, ['customer', 'min_qty', 'discount_percentage'], as_dict = 1)
-		self.assertTrue(price_doc_details.customer, '_Test Customer')
+		ps = make_promotional_scheme(applicable_for="Customer", customer="_Test Customer")
+		price_rules = frappe.get_all(
+			"Pricing Rule",
+			fields=["promotional_scheme_id", "name", "creation"],
+			filters={"promotional_scheme": ps.name},
+		)
+		self.assertTrue(len(price_rules), 1)
+		price_doc_details = frappe.db.get_value(
+			"Pricing Rule", price_rules[0].name, ["customer", "min_qty", "discount_percentage"], as_dict=1
+		)
+		self.assertTrue(price_doc_details.customer, "_Test Customer")
 		self.assertTrue(price_doc_details.min_qty, 4)
 		self.assertTrue(price_doc_details.discount_percentage, 20)
 
 		ps.price_discount_slabs[0].min_qty = 6
-		ps.append('customer', {
-			'customer': "_Test Customer 2"})
+		ps.append("customer", {"customer": "_Test Customer 2"})
 		ps.save()
-		price_rules = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name"],
-			filters = {'promotional_scheme': ps.name})
+		price_rules = frappe.get_all(
+			"Pricing Rule",
+			fields=["promotional_scheme_id", "name"],
+			filters={"promotional_scheme": ps.name},
+		)
 		self.assertTrue(len(price_rules), 2)
 
-		price_doc_details = frappe.db.get_value('Pricing Rule', price_rules[1].name, ['customer', 'min_qty', 'discount_percentage'], as_dict = 1)
-		self.assertTrue(price_doc_details.customer, '_Test Customer 2')
+		price_doc_details = frappe.db.get_value(
+			"Pricing Rule", price_rules[1].name, ["customer", "min_qty", "discount_percentage"], as_dict=1
+		)
+		self.assertTrue(price_doc_details.customer, "_Test Customer 2")
 		self.assertTrue(price_doc_details.min_qty, 6)
 		self.assertTrue(price_doc_details.discount_percentage, 20)
 
-		price_doc_details = frappe.db.get_value('Pricing Rule', price_rules[0].name, ['customer', 'min_qty', 'discount_percentage'], as_dict = 1)
-		self.assertTrue(price_doc_details.customer, '_Test Customer')
+		price_doc_details = frappe.db.get_value(
+			"Pricing Rule", price_rules[0].name, ["customer", "min_qty", "discount_percentage"], as_dict=1
+		)
+		self.assertTrue(price_doc_details.customer, "_Test Customer")
 		self.assertTrue(price_doc_details.min_qty, 6)
 
-		frappe.delete_doc('Promotional Scheme', ps.name)
-		price_rules = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name"],
-			filters = {'promotional_scheme': ps.name})
+		frappe.delete_doc("Promotional Scheme", ps.name)
+		price_rules = frappe.get_all(
+			"Pricing Rule",
+			fields=["promotional_scheme_id", "name"],
+			filters={"promotional_scheme": ps.name},
+		)
 		self.assertEqual(price_rules, [])
 
 	def test_promotional_scheme_without_applicable_for(self):
 		ps = make_promotional_scheme()
-		price_rules = frappe.get_all('Pricing Rule', filters = {'promotional_scheme': ps.name})
+		price_rules = frappe.get_all("Pricing Rule", filters={"promotional_scheme": ps.name})
 
 		self.assertTrue(len(price_rules), 1)
-		frappe.delete_doc('Promotional Scheme', ps.name)
+		frappe.delete_doc("Promotional Scheme", ps.name)
 
-		price_rules = frappe.get_all('Pricing Rule', filters = {'promotional_scheme': ps.name})
+		price_rules = frappe.get_all("Pricing Rule", filters={"promotional_scheme": ps.name})
 		self.assertEqual(price_rules, [])
 
 	def test_change_applicable_for_in_promotional_scheme(self):
 		ps = make_promotional_scheme()
-		price_rules = frappe.get_all('Pricing Rule', filters = {'promotional_scheme': ps.name})
+		price_rules = frappe.get_all("Pricing Rule", filters={"promotional_scheme": ps.name})
 		self.assertTrue(len(price_rules), 1)
 
-		so = make_sales_order(qty=5, currency='USD', do_not_save=True)
+		so = make_sales_order(qty=5, currency="USD", do_not_save=True)
 		so.set_missing_values()
 		so.save()
 		self.assertEqual(price_rules[0].name, so.pricing_rules[0].pricing_rule)
 
-		ps.applicable_for = 'Customer'
-		ps.append('customer', {
-			'customer': '_Test Customer'
-		})
+		ps.applicable_for = "Customer"
+		ps.append("customer", {"customer": "_Test Customer"})
 
 		self.assertRaises(TransactionExists, ps.save)
 
-		frappe.delete_doc('Sales Order', so.name)
-		frappe.delete_doc('Promotional Scheme', ps.name)
-		price_rules = frappe.get_all('Pricing Rule', filters = {'promotional_scheme': ps.name})
+		frappe.delete_doc("Sales Order", so.name)
+		frappe.delete_doc("Promotional Scheme", ps.name)
+		price_rules = frappe.get_all("Pricing Rule", filters={"promotional_scheme": ps.name})
 		self.assertEqual(price_rules, [])
 
+
 def make_promotional_scheme(**args):
 	args = frappe._dict(args)
 
-	ps = frappe.new_doc('Promotional Scheme')
-	ps.name = '_Test Scheme'
-	ps.append('items',{
-		'item_code': '_Test Item'
-	})
+	ps = frappe.new_doc("Promotional Scheme")
+	ps.name = "_Test Scheme"
+	ps.append("items", {"item_code": "_Test Item"})
 
 	ps.selling = 1
-	ps.append('price_discount_slabs',{
-		'min_qty': 4,
-		'validate_applied_rule': 0,
-		'discount_percentage': 20,
-		'rule_description': 'Test'
-	})
+	ps.append(
+		"price_discount_slabs",
+		{
+			"min_qty": 4,
+			"validate_applied_rule": 0,
+			"discount_percentage": 20,
+			"rule_description": "Test",
+		},
+	)
 
-	ps.company = '_Test Company'
+	ps.company = "_Test Company"
 	if args.applicable_for:
 		ps.applicable_for = args.applicable_for
-		ps.append(frappe.scrub(args.applicable_for), {
-			frappe.scrub(args.applicable_for): args.get(frappe.scrub(args.applicable_for))
-		})
+		ps.append(
+			frappe.scrub(args.applicable_for),
+			{frappe.scrub(args.applicable_for): args.get(frappe.scrub(args.applicable_for))},
+		)
 
 	ps.save()
 
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 1a398ab..7e3597e 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -30,6 +30,9 @@
 	onload() {
 		super.onload();
 
+		// Ignore linked advances
+		this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry'];
+
 		if(!this.frm.doc.__islocal) {
 			// show credit_to in print format
 			if(!this.frm.doc.supplier && this.frm.doc.credit_to) {
@@ -42,8 +45,6 @@
 		if (this.frm.doc.supplier && this.frm.doc.__islocal) {
 			this.frm.trigger('supplier');
 		}
-
-		erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
 	}
 
 	refresh(doc) {
@@ -141,7 +142,7 @@
 				})
 			}, __("Get Items From"));
 		}
-		this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted==="Yes");
+		this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted);
 
 		if (doc.docstatus == 1 && !doc.inter_company_invoice_reference) {
 			frappe.model.with_doc("Supplier", me.frm.doc.supplier, function() {
@@ -276,6 +277,8 @@
 		if(this.frm.updating_party_details || this.frm.doc.inter_company_invoice_reference)
 			return;
 
+		if (this.frm.doc.__onload && this.frm.doc.__onload.load_after_mapping) return;
+
 		erpnext.utils.get_party_details(this.frm, "erpnext.accounts.party.get_party_details",
 			{
 				posting_date: this.frm.doc.posting_date,
@@ -569,10 +572,10 @@
 	},
 
 	is_subcontracted: function(frm) {
-		if (frm.doc.is_subcontracted === "Yes") {
+		if (frm.doc.is_subcontracted) {
 			erpnext.buying.get_default_bom(frm);
 		}
-		frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted==="Yes");
+		frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted);
 	},
 
 	update_stock: function(frm) {
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index bd01164..9f87c5a 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -543,11 +543,10 @@
    "fieldtype": "Column Break"
   },
   {
-   "default": "No",
+   "default": "0",
    "fieldname": "is_subcontracted",
-   "fieldtype": "Select",
-   "label": "Raw Materials Supplied",
-   "options": "No\nYes",
+   "fieldtype": "Check",
+   "label": "Is Subcontracted",
    "print_hide": 1
   },
   {
@@ -1366,7 +1365,7 @@
    "width": "50px"
   },
   {
-   "depends_on": "eval:doc.update_stock && doc.is_subcontracted==\"Yes\"",
+   "depends_on": "eval:doc.update_stock && doc.is_subcontracted",
    "fieldname": "supplier_warehouse",
    "fieldtype": "Link",
    "label": "Supplier Warehouse",
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 2c31561..23ad223 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -32,6 +32,7 @@
 from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_enabled
 from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
 from erpnext.buying.utils import check_on_hold_or_closed_status
+from erpnext.controllers.accounts_controller import validate_account_head
 from erpnext.controllers.buying_controller import BuyingController
 from erpnext.stock import get_warehouse_account_map
 from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
@@ -40,27 +41,30 @@
 )
 
 
-class WarehouseMissingError(frappe.ValidationError): pass
+class WarehouseMissingError(frappe.ValidationError):
+	pass
 
-form_grid_templates = {
-	"items": "templates/form_grid/item_grid.html"
-}
+
+form_grid_templates = {"items": "templates/form_grid/item_grid.html"}
+
 
 class PurchaseInvoice(BuyingController):
 	def __init__(self, *args, **kwargs):
 		super(PurchaseInvoice, self).__init__(*args, **kwargs)
-		self.status_updater = [{
-			'source_dt': 'Purchase Invoice Item',
-			'target_dt': 'Purchase Order Item',
-			'join_field': 'po_detail',
-			'target_field': 'billed_amt',
-			'target_parent_dt': 'Purchase Order',
-			'target_parent_field': 'per_billed',
-			'target_ref_field': 'amount',
-			'source_field': 'amount',
-			'percent_join_field': 'purchase_order',
-			'overflow_type': 'billing'
-		}]
+		self.status_updater = [
+			{
+				"source_dt": "Purchase Invoice Item",
+				"target_dt": "Purchase Order Item",
+				"join_field": "po_detail",
+				"target_field": "billed_amt",
+				"target_parent_dt": "Purchase Order",
+				"target_parent_field": "per_billed",
+				"target_ref_field": "amount",
+				"source_field": "amount",
+				"percent_join_field": "purchase_order",
+				"overflow_type": "billing",
+			}
+		]
 
 	def onload(self):
 		super(PurchaseInvoice, self).onload()
@@ -69,15 +73,14 @@
 
 	def before_save(self):
 		if not self.on_hold:
-			self.release_date = ''
-
+			self.release_date = ""
 
 	def invoice_is_blocked(self):
 		return self.on_hold and (not self.release_date or self.release_date > getdate(nowdate()))
 
 	def validate(self):
 		if not self.is_opening:
-			self.is_opening = 'No'
+			self.is_opening = "No"
 
 		self.validate_posting_time()
 
@@ -89,14 +92,14 @@
 			self.validate_supplier_invoice()
 
 		# validate cash purchase
-		if (self.is_paid == 1):
+		if self.is_paid == 1:
 			self.validate_cash()
 
 		# validate service stop date to lie in between start and end date
 		validate_service_stop_date(self)
 
-		if self._action=="submit" and self.update_stock:
-			self.make_batches('warehouse')
+		if self._action == "submit" and self.update_stock:
+			self.make_batches("warehouse")
 
 		self.validate_release_date()
 		self.check_conversion_rate()
@@ -107,45 +110,53 @@
 		self.validate_uom_is_integer("uom", "qty")
 		self.validate_uom_is_integer("stock_uom", "stock_qty")
 		self.set_expense_account(for_validate=True)
+		self.validate_expense_account()
 		self.set_against_expense_account()
 		self.validate_write_off_account()
 		self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount", "items")
 		self.create_remarks()
 		self.set_status()
 		self.validate_purchase_receipt_if_update_stock()
-		validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_invoice_reference)
+		validate_inter_company_party(
+			self.doctype, self.supplier, self.company, self.inter_company_invoice_reference
+		)
 		self.reset_default_field_value("set_warehouse", "items", "warehouse")
 		self.reset_default_field_value("rejected_warehouse", "items", "rejected_warehouse")
 		self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
 
 	def validate_release_date(self):
 		if self.release_date and getdate(nowdate()) >= getdate(self.release_date):
-			frappe.throw(_('Release date must be in the future'))
+			frappe.throw(_("Release date must be in the future"))
 
 	def validate_cash(self):
 		if not self.cash_bank_account and flt(self.paid_amount):
 			frappe.throw(_("Cash or Bank Account is mandatory for making payment entry"))
 
-		if (flt(self.paid_amount) + flt(self.write_off_amount)
-			- flt(self.get("rounded_total") or self.grand_total)
-			> 1/(10**(self.precision("base_grand_total") + 1))):
+		if flt(self.paid_amount) + flt(self.write_off_amount) - flt(
+			self.get("rounded_total") or self.grand_total
+		) > 1 / (10 ** (self.precision("base_grand_total") + 1)):
 
 			frappe.throw(_("""Paid amount + Write Off Amount can not be greater than Grand Total"""))
 
 	def create_remarks(self):
 		if not self.remarks:
 			if self.bill_no and self.bill_date:
-				self.remarks = _("Against Supplier Invoice {0} dated {1}").format(self.bill_no,
-					formatdate(self.bill_date))
+				self.remarks = _("Against Supplier Invoice {0} dated {1}").format(
+					self.bill_no, formatdate(self.bill_date)
+				)
 			else:
 				self.remarks = _("No Remarks")
 
 	def set_missing_values(self, for_validate=False):
 		if not self.credit_to:
 			self.credit_to = get_party_account("Supplier", self.supplier, self.company)
-			self.party_account_currency = frappe.db.get_value("Account", self.credit_to, "account_currency", cache=True)
+			self.party_account_currency = frappe.db.get_value(
+				"Account", self.credit_to, "account_currency", cache=True
+			)
 		if not self.due_date:
-			self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier, self.company,  self.bill_date)
+			self.due_date = get_due_date(
+				self.posting_date, "Supplier", self.supplier, self.company, self.bill_date
+			)
 
 		tds_category = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")
 		if tds_category and not for_validate:
@@ -157,8 +168,12 @@
 	def check_conversion_rate(self):
 		default_currency = erpnext.get_company_currency(self.company)
 		if not default_currency:
-			throw(_('Please enter default currency in Company Master'))
-		if (self.currency == default_currency and flt(self.conversion_rate) != 1.00) or not self.conversion_rate or (self.currency != default_currency and flt(self.conversion_rate) == 1.00):
+			throw(_("Please enter default currency in Company Master"))
+		if (
+			(self.currency == default_currency and flt(self.conversion_rate) != 1.00)
+			or not self.conversion_rate
+			or (self.currency != default_currency and flt(self.conversion_rate) == 1.00)
+		):
 			throw(_("Conversion rate cannot be 0 or 1"))
 
 	def validate_credit_to_acc(self):
@@ -167,19 +182,24 @@
 			if not self.credit_to:
 				self.raise_missing_debit_credit_account_error("Supplier", self.supplier)
 
-		account = frappe.db.get_value("Account", self.credit_to,
-			["account_type", "report_type", "account_currency"], as_dict=True)
+		account = frappe.db.get_value(
+			"Account", self.credit_to, ["account_type", "report_type", "account_currency"], as_dict=True
+		)
 
 		if account.report_type != "Balance Sheet":
 			frappe.throw(
-				_("Please ensure {} account is a Balance Sheet account. You can change the parent account to a Balance Sheet account or select a different account.")
-				.format(frappe.bold("Credit To")), title=_("Invalid Account")
+				_(
+					"Please ensure {} account is a Balance Sheet account. You can change the parent account to a Balance Sheet account or select a different account."
+				).format(frappe.bold("Credit To")),
+				title=_("Invalid Account"),
 			)
 
 		if self.supplier and account.account_type != "Payable":
 			frappe.throw(
-				_("Please ensure {} account {} is a Payable account. Change the account type to Payable or select a different account.")
-				.format(frappe.bold("Credit To"), frappe.bold(self.credit_to)), title=_("Invalid Account")
+				_(
+					"Please ensure {} account {} is a Payable account. Change the account type to Payable or select a different account."
+				).format(frappe.bold("Credit To"), frappe.bold(self.credit_to)),
+				title=_("Invalid Account"),
 			)
 
 		self.party_account_currency = account.account_currency
@@ -187,51 +207,62 @@
 	def check_on_hold_or_closed_status(self):
 		check_list = []
 
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if d.purchase_order and not d.purchase_order in check_list and not d.purchase_receipt:
 				check_list.append(d.purchase_order)
-				check_on_hold_or_closed_status('Purchase Order', d.purchase_order)
+				check_on_hold_or_closed_status("Purchase Order", d.purchase_order)
 
 	def validate_with_previous_doc(self):
-		super(PurchaseInvoice, self).validate_with_previous_doc({
-			"Purchase Order": {
-				"ref_dn_field": "purchase_order",
-				"compare_fields": [["supplier", "="], ["company", "="], ["currency", "="]],
-			},
-			"Purchase Order Item": {
-				"ref_dn_field": "po_detail",
-				"compare_fields": [["project", "="], ["item_code", "="], ["uom", "="]],
-				"is_child_table": True,
-				"allow_duplicate_prev_row_id": True
-			},
-			"Purchase Receipt": {
-				"ref_dn_field": "purchase_receipt",
-				"compare_fields": [["supplier", "="], ["company", "="], ["currency", "="]],
-			},
-			"Purchase Receipt Item": {
-				"ref_dn_field": "pr_detail",
-				"compare_fields": [["project", "="], ["item_code", "="], ["uom", "="]],
-				"is_child_table": True
+		super(PurchaseInvoice, self).validate_with_previous_doc(
+			{
+				"Purchase Order": {
+					"ref_dn_field": "purchase_order",
+					"compare_fields": [["supplier", "="], ["company", "="], ["currency", "="]],
+				},
+				"Purchase Order Item": {
+					"ref_dn_field": "po_detail",
+					"compare_fields": [["project", "="], ["item_code", "="], ["uom", "="]],
+					"is_child_table": True,
+					"allow_duplicate_prev_row_id": True,
+				},
+				"Purchase Receipt": {
+					"ref_dn_field": "purchase_receipt",
+					"compare_fields": [["supplier", "="], ["company", "="], ["currency", "="]],
+				},
+				"Purchase Receipt Item": {
+					"ref_dn_field": "pr_detail",
+					"compare_fields": [["project", "="], ["item_code", "="], ["uom", "="]],
+					"is_child_table": True,
+				},
 			}
-		})
+		)
 
-		if cint(frappe.db.get_single_value('Buying Settings', 'maintain_same_rate')) and not self.is_return:
-			self.validate_rate_with_reference_doc([
-				["Purchase Order", "purchase_order", "po_detail"],
-				["Purchase Receipt", "purchase_receipt", "pr_detail"]
-			])
+		if (
+			cint(frappe.db.get_single_value("Buying Settings", "maintain_same_rate")) and not self.is_return
+		):
+			self.validate_rate_with_reference_doc(
+				[
+					["Purchase Order", "purchase_order", "po_detail"],
+					["Purchase Receipt", "purchase_receipt", "pr_detail"],
+				]
+			)
 
 	def validate_warehouse(self, for_validate=True):
 		if self.update_stock and for_validate:
-			for d in self.get('items'):
-				if not d.warehouse:
-					frappe.throw(_("Row No {0}: Warehouse is required. Please set a Default Warehouse for Item {1} and Company {2}").
-						format(d.idx, d.item_code, self.company), exc=WarehouseMissingError)
+			stock_items = self.get_stock_items()
+			for d in self.get("items"):
+				if not d.warehouse and d.item_code in stock_items:
+					frappe.throw(
+						_(
+							"Row No {0}: Warehouse is required. Please set a Default Warehouse for Item {1} and Company {2}"
+						).format(d.idx, d.item_code, self.company),
+						exc=WarehouseMissingError,
+					)
 
 		super(PurchaseInvoice, self).validate_warehouse()
 
 	def validate_item_code(self):
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if not d.item_code:
 				frappe.msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True)
 
@@ -259,51 +290,82 @@
 			if item.item_code:
 				asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category")
 
-			if auto_accounting_for_stock and item.item_code in stock_items \
-				and self.is_opening == 'No' and not item.is_fixed_asset \
-				and (not item.po_detail or
-					not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")):
+			if (
+				auto_accounting_for_stock
+				and item.item_code in stock_items
+				and self.is_opening == "No"
+				and not item.is_fixed_asset
+				and (
+					not item.po_detail
+					or not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")
+				)
+			):
 
 				if self.update_stock and item.warehouse and (not item.from_warehouse):
-					if for_validate and item.expense_account and item.expense_account != warehouse_account[item.warehouse]["account"]:
-						msg = _("Row {0}: Expense Head changed to {1} because account {2} is not linked to warehouse {3} or it is not the default inventory account").format(
-							item.idx, frappe.bold(warehouse_account[item.warehouse]["account"]), frappe.bold(item.expense_account), frappe.bold(item.warehouse))
+					if (
+						for_validate
+						and item.expense_account
+						and item.expense_account != warehouse_account[item.warehouse]["account"]
+					):
+						msg = _(
+							"Row {0}: Expense Head changed to {1} because account {2} is not linked to warehouse {3} or it is not the default inventory account"
+						).format(
+							item.idx,
+							frappe.bold(warehouse_account[item.warehouse]["account"]),
+							frappe.bold(item.expense_account),
+							frappe.bold(item.warehouse),
+						)
 						frappe.msgprint(msg, title=_("Expense Head Changed"))
 					item.expense_account = warehouse_account[item.warehouse]["account"]
 				else:
 					# check if 'Stock Received But Not Billed' account is credited in Purchase receipt or not
 					if item.purchase_receipt:
-						negative_expense_booked_in_pr = frappe.db.sql("""select name from `tabGL Entry`
+						negative_expense_booked_in_pr = frappe.db.sql(
+							"""select name from `tabGL Entry`
 							where voucher_type='Purchase Receipt' and voucher_no=%s and account = %s""",
-							(item.purchase_receipt, stock_not_billed_account))
+							(item.purchase_receipt, stock_not_billed_account),
+						)
 
 						if negative_expense_booked_in_pr:
-							if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
-								msg = _("Row {0}: Expense Head changed to {1} because expense is booked against this account in Purchase Receipt {2}").format(
-									item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.purchase_receipt))
+							if (
+								for_validate and item.expense_account and item.expense_account != stock_not_billed_account
+							):
+								msg = _(
+									"Row {0}: Expense Head changed to {1} because expense is booked against this account in Purchase Receipt {2}"
+								).format(
+									item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.purchase_receipt)
+								)
 								frappe.msgprint(msg, title=_("Expense Head Changed"))
 
 							item.expense_account = stock_not_billed_account
 					else:
 						# If no purchase receipt present then book expense in 'Stock Received But Not Billed'
 						# This is done in cases when Purchase Invoice is created before Purchase Receipt
-						if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
-							msg = _("Row {0}: Expense Head changed to {1} as no Purchase Receipt is created against Item {2}.").format(
-								item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.item_code))
+						if (
+							for_validate and item.expense_account and item.expense_account != stock_not_billed_account
+						):
+							msg = _(
+								"Row {0}: Expense Head changed to {1} as no Purchase Receipt is created against Item {2}."
+							).format(
+								item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.item_code)
+							)
 							msg += "<br>"
-							msg += _("This is done to handle accounting for cases when Purchase Receipt is created after Purchase Invoice")
+							msg += _(
+								"This is done to handle accounting for cases when Purchase Receipt is created after Purchase Invoice"
+							)
 							frappe.msgprint(msg, title=_("Expense Head Changed"))
 
 						item.expense_account = stock_not_billed_account
 
 			elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):
-				asset_category_account = get_asset_category_account('fixed_asset_account', item=item.item_code,
-					company = self.company)
+				asset_category_account = get_asset_category_account(
+					"fixed_asset_account", item=item.item_code, company=self.company
+				)
 				if not asset_category_account:
-					form_link = get_link_to_form('Asset Category', asset_category)
+					form_link = get_link_to_form("Asset Category", asset_category)
 					throw(
 						_("Please set Fixed Asset Account in {} against {}.").format(form_link, self.company),
-						title=_("Missing Account")
+						title=_("Missing Account"),
 					)
 				item.expense_account = asset_category_account
 			elif item.is_fixed_asset and item.pr_detail:
@@ -311,6 +373,10 @@
 			elif not item.expense_account and for_validate:
 				throw(_("Expense account is mandatory for item {0}").format(item.item_code or item.item_name))
 
+	def validate_expense_account(self):
+		for item in self.get("items"):
+			validate_account_head(item.idx, item.expense_account, self.company, "Expense")
+
 	def set_against_expense_account(self):
 		against_accounts = []
 		for item in self.get("items"):
@@ -320,32 +386,44 @@
 		self.against_expense_account = ",".join(against_accounts)
 
 	def po_required(self):
-		if frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes':
+		if frappe.db.get_value("Buying Settings", None, "po_required") == "Yes":
 
-			if frappe.get_value('Supplier', self.supplier, 'allow_purchase_invoice_creation_without_purchase_order'):
+			if frappe.get_value(
+				"Supplier", self.supplier, "allow_purchase_invoice_creation_without_purchase_order"
+			):
 				return
 
-			for d in self.get('items'):
+			for d in self.get("items"):
 				if not d.purchase_order:
 					msg = _("Purchase Order Required for item {}").format(frappe.bold(d.item_code))
 					msg += "<br><br>"
 					msg += _("To submit the invoice without purchase order please set {0} as {1} in {2}").format(
-						frappe.bold(_('Purchase Order Required')), frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings'))
+						frappe.bold(_("Purchase Order Required")),
+						frappe.bold("No"),
+						get_link_to_form("Buying Settings", "Buying Settings", "Buying Settings"),
+					)
 					throw(msg, title=_("Mandatory Purchase Order"))
 
 	def pr_required(self):
 		stock_items = self.get_stock_items()
-		if frappe.db.get_value("Buying Settings", None, "pr_required") == 'Yes':
+		if frappe.db.get_value("Buying Settings", None, "pr_required") == "Yes":
 
-			if frappe.get_value('Supplier', self.supplier, 'allow_purchase_invoice_creation_without_purchase_receipt'):
+			if frappe.get_value(
+				"Supplier", self.supplier, "allow_purchase_invoice_creation_without_purchase_receipt"
+			):
 				return
 
-			for d in self.get('items'):
+			for d in self.get("items"):
 				if not d.purchase_receipt and d.item_code in stock_items:
 					msg = _("Purchase Receipt Required for item {}").format(frappe.bold(d.item_code))
 					msg += "<br><br>"
-					msg += _("To submit the invoice without purchase receipt please set {0} as {1} in {2}").format(
-						frappe.bold(_('Purchase Receipt Required')), frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings'))
+					msg += _(
+						"To submit the invoice without purchase receipt please set {0} as {1} in {2}"
+					).format(
+						frappe.bold(_("Purchase Receipt Required")),
+						frappe.bold("No"),
+						get_link_to_form("Buying Settings", "Buying Settings", "Buying Settings"),
+					)
 					throw(msg, title=_("Mandatory Purchase Receipt"))
 
 	def validate_write_off_account(self):
@@ -353,56 +431,65 @@
 			throw(_("Please enter Write Off Account"))
 
 	def check_prev_docstatus(self):
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if d.purchase_order:
-				submitted = frappe.db.sql("select name from `tabPurchase Order` where docstatus = 1 and name = %s", d.purchase_order)
+				submitted = frappe.db.sql(
+					"select name from `tabPurchase Order` where docstatus = 1 and name = %s", d.purchase_order
+				)
 				if not submitted:
 					frappe.throw(_("Purchase Order {0} is not submitted").format(d.purchase_order))
 			if d.purchase_receipt:
-				submitted = frappe.db.sql("select name from `tabPurchase Receipt` where docstatus = 1 and name = %s", d.purchase_receipt)
+				submitted = frappe.db.sql(
+					"select name from `tabPurchase Receipt` where docstatus = 1 and name = %s", d.purchase_receipt
+				)
 				if not submitted:
 					frappe.throw(_("Purchase Receipt {0} is not submitted").format(d.purchase_receipt))
 
 	def update_status_updater_args(self):
 		if cint(self.update_stock):
-			self.status_updater.append({
-				'source_dt': 'Purchase Invoice Item',
-				'target_dt': 'Purchase Order Item',
-				'join_field': 'po_detail',
-				'target_field': 'received_qty',
-				'target_parent_dt': 'Purchase Order',
-				'target_parent_field': 'per_received',
-				'target_ref_field': 'qty',
-				'source_field': 'received_qty',
-				'second_source_dt': 'Purchase Receipt Item',
-				'second_source_field': 'received_qty',
-				'second_join_field': 'purchase_order_item',
-				'percent_join_field':'purchase_order',
-				'overflow_type': 'receipt',
-				'extra_cond': """ and exists(select name from `tabPurchase Invoice`
-					where name=`tabPurchase Invoice Item`.parent and update_stock = 1)"""
-			})
+			self.status_updater.append(
+				{
+					"source_dt": "Purchase Invoice Item",
+					"target_dt": "Purchase Order Item",
+					"join_field": "po_detail",
+					"target_field": "received_qty",
+					"target_parent_dt": "Purchase Order",
+					"target_parent_field": "per_received",
+					"target_ref_field": "qty",
+					"source_field": "received_qty",
+					"second_source_dt": "Purchase Receipt Item",
+					"second_source_field": "received_qty",
+					"second_join_field": "purchase_order_item",
+					"percent_join_field": "purchase_order",
+					"overflow_type": "receipt",
+					"extra_cond": """ and exists(select name from `tabPurchase Invoice`
+					where name=`tabPurchase Invoice Item`.parent and update_stock = 1)""",
+				}
+			)
 			if cint(self.is_return):
-				self.status_updater.append({
-					'source_dt': 'Purchase Invoice Item',
-					'target_dt': 'Purchase Order Item',
-					'join_field': 'po_detail',
-					'target_field': 'returned_qty',
-					'source_field': '-1 * qty',
-					'second_source_dt': 'Purchase Receipt Item',
-					'second_source_field': '-1 * qty',
-					'second_join_field': 'purchase_order_item',
-					'overflow_type': 'receipt',
-					'extra_cond': """ and exists (select name from `tabPurchase Invoice`
-						where name=`tabPurchase Invoice Item`.parent and update_stock=1 and is_return=1)"""
-				})
+				self.status_updater.append(
+					{
+						"source_dt": "Purchase Invoice Item",
+						"target_dt": "Purchase Order Item",
+						"join_field": "po_detail",
+						"target_field": "returned_qty",
+						"source_field": "-1 * qty",
+						"second_source_dt": "Purchase Receipt Item",
+						"second_source_field": "-1 * qty",
+						"second_join_field": "purchase_order_item",
+						"overflow_type": "receipt",
+						"extra_cond": """ and exists (select name from `tabPurchase Invoice`
+						where name=`tabPurchase Invoice Item`.parent and update_stock=1 and is_return=1)""",
+					}
+				)
 
 	def validate_purchase_receipt_if_update_stock(self):
 		if self.update_stock:
 			for item in self.get("items"):
 				if item.purchase_receipt:
-					frappe.throw(_("Stock cannot be updated against Purchase Receipt {0}")
-						.format(item.purchase_receipt))
+					frappe.throw(
+						_("Stock cannot be updated against Purchase Receipt {0}").format(item.purchase_receipt)
+					)
 
 	def on_submit(self):
 		super(PurchaseInvoice, self).on_submit()
@@ -411,8 +498,9 @@
 		self.update_status_updater_args()
 		self.update_prevdoc_status()
 
-		frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
-			self.company, self.base_grand_total)
+		frappe.get_doc("Authorization Control").validate_approving_authority(
+			self.doctype, self.company, self.base_grand_total
+		)
 
 		if not self.is_return:
 			self.update_against_document_in_jv()
@@ -427,6 +515,7 @@
 			self.update_stock_ledger()
 			self.set_consumed_qty_in_po()
 			from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
+
 			update_serial_nos_after_submit(self, "items")
 
 		# this sequence because outstanding may get -negative
@@ -449,13 +538,32 @@
 			update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
 
 			if self.docstatus == 1:
-				make_gl_entries(gl_entries, update_outstanding=update_outstanding, merge_entries=False, from_repost=from_repost)
+				make_gl_entries(
+					gl_entries,
+					update_outstanding=update_outstanding,
+					merge_entries=False,
+					from_repost=from_repost,
+				)
 			elif self.docstatus == 2:
+				provisional_entries = [a for a in gl_entries if a.voucher_type == "Purchase Receipt"]
 				make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
+				if provisional_entries:
+					for entry in provisional_entries:
+						frappe.db.set_value(
+							"GL Entry",
+							{"voucher_type": "Purchase Receipt", "voucher_detail_no": entry.voucher_detail_no},
+							"is_cancelled",
+							1,
+						)
 
 			if update_outstanding == "No":
-				update_outstanding_amt(self.credit_to, "Supplier", self.supplier,
-					self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name)
+				update_outstanding_amt(
+					self.credit_to,
+					"Supplier",
+					self.supplier,
+					self.doctype,
+					self.return_against if cint(self.is_return) and self.return_against else self.name,
+				)
 
 		elif self.docstatus == 2 and cint(self.update_stock) and self.auto_accounting_for_stock:
 			make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
@@ -504,28 +612,41 @@
 	def make_supplier_gl_entry(self, gl_entries):
 		# Checked both rounding_adjustment and rounded_total
 		# because rounded_total had value even before introcution of posting GLE based on rounded total
-		grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
-		base_grand_total = flt(self.base_rounded_total if (self.base_rounding_adjustment and self.base_rounded_total)
-			else self.base_grand_total, self.precision("base_grand_total"))
+		grand_total = (
+			self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
+		)
+		base_grand_total = flt(
+			self.base_rounded_total
+			if (self.base_rounding_adjustment and self.base_rounded_total)
+			else self.base_grand_total,
+			self.precision("base_grand_total"),
+		)
 
 		if grand_total and not self.is_internal_transfer():
-				# Did not use base_grand_total to book rounding loss gle
-				gl_entries.append(
-					self.get_gl_dict({
+			# Did not use base_grand_total to book rounding loss gle
+			gl_entries.append(
+				self.get_gl_dict(
+					{
 						"account": self.credit_to,
 						"party_type": "Supplier",
 						"party": self.supplier,
 						"due_date": self.due_date,
 						"against": self.against_expense_account,
 						"credit": base_grand_total,
-						"credit_in_account_currency": base_grand_total \
-							if self.party_account_currency==self.company_currency else grand_total,
-						"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
+						"credit_in_account_currency": base_grand_total
+						if self.party_account_currency == self.company_currency
+						else grand_total,
+						"against_voucher": self.return_against
+						if cint(self.is_return) and self.return_against
+						else self.name,
 						"against_voucher_type": self.doctype,
 						"project": self.project,
-						"cost_center": self.cost_center
-					}, self.party_account_currency, item=self)
+						"cost_center": self.cost_center,
+					},
+					self.party_account_currency,
+					item=self,
 				)
+			)
 
 	def make_item_gl_entries(self, gl_entries):
 		# item gl entries
@@ -537,22 +658,33 @@
 
 		voucher_wise_stock_value = {}
 		if self.update_stock:
-			stock_ledger_entries = frappe.get_all("Stock Ledger Entry",
-				fields = ["voucher_detail_no", "stock_value_difference", "warehouse"],
-				filters={"voucher_no": self.name, "voucher_type": self.doctype, "is_cancelled": 0}
+			stock_ledger_entries = frappe.get_all(
+				"Stock Ledger Entry",
+				fields=["voucher_detail_no", "stock_value_difference", "warehouse"],
+				filters={"voucher_no": self.name, "voucher_type": self.doctype, "is_cancelled": 0},
 			)
 			for d in stock_ledger_entries:
-				voucher_wise_stock_value.setdefault((d.voucher_detail_no, d.warehouse), d.stock_value_difference)
+				voucher_wise_stock_value.setdefault(
+					(d.voucher_detail_no, d.warehouse), d.stock_value_difference
+				)
 
-		valuation_tax_accounts = [d.account_head for d in self.get("taxes")
-			if d.category in ('Valuation', 'Total and Valuation')
-			and flt(d.base_tax_amount_after_discount_amount)]
+		valuation_tax_accounts = [
+			d.account_head
+			for d in self.get("taxes")
+			if d.category in ("Valuation", "Total and Valuation")
+			and flt(d.base_tax_amount_after_discount_amount)
+		]
 
 		exchange_rate_map, net_rate_map = get_purchase_document_details(self)
 
-		enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting'))
-		provisional_accounting_for_non_stock_items = cint(frappe.db.get_value('Company', self.company, \
-			'enable_provisional_accounting_for_non_stock_items'))
+		enable_discount_accounting = cint(
+			frappe.db.get_single_value("Buying Settings", "enable_discount_accounting")
+		)
+		provisional_accounting_for_non_stock_items = cint(
+			frappe.db.get_value(
+				"Company", self.company, "enable_provisional_accounting_for_non_stock_items"
+			)
+		)
 
 		purchase_receipt_doc_map = {}
 
@@ -564,86 +696,122 @@
 
 				if self.update_stock and self.auto_accounting_for_stock and item.item_code in stock_items:
 					# warehouse account
-					warehouse_debit_amount = self.make_stock_adjustment_entry(gl_entries,
-						item, voucher_wise_stock_value, account_currency)
+					warehouse_debit_amount = self.make_stock_adjustment_entry(
+						gl_entries, item, voucher_wise_stock_value, account_currency
+					)
 
 					if item.from_warehouse:
-						gl_entries.append(self.get_gl_dict({
-							"account":  warehouse_account[item.warehouse]['account'],
-							"against": warehouse_account[item.from_warehouse]["account"],
-							"cost_center": item.cost_center,
-							"project": item.project or self.project,
-							"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-							"debit": warehouse_debit_amount,
-						}, warehouse_account[item.warehouse]["account_currency"], item=item))
+						gl_entries.append(
+							self.get_gl_dict(
+								{
+									"account": warehouse_account[item.warehouse]["account"],
+									"against": warehouse_account[item.from_warehouse]["account"],
+									"cost_center": item.cost_center,
+									"project": item.project or self.project,
+									"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+									"debit": warehouse_debit_amount,
+								},
+								warehouse_account[item.warehouse]["account_currency"],
+								item=item,
+							)
+						)
 
 						# Intentionally passed negative debit amount to avoid incorrect GL Entry validation
-						gl_entries.append(self.get_gl_dict({
-							"account":  warehouse_account[item.from_warehouse]['account'],
-							"against": warehouse_account[item.warehouse]["account"],
-							"cost_center": item.cost_center,
-							"project": item.project or self.project,
-							"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-							"debit": -1 * flt(item.base_net_amount, item.precision("base_net_amount")),
-						}, warehouse_account[item.from_warehouse]["account_currency"], item=item))
+						gl_entries.append(
+							self.get_gl_dict(
+								{
+									"account": warehouse_account[item.from_warehouse]["account"],
+									"against": warehouse_account[item.warehouse]["account"],
+									"cost_center": item.cost_center,
+									"project": item.project or self.project,
+									"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+									"debit": -1 * flt(item.base_net_amount, item.precision("base_net_amount")),
+								},
+								warehouse_account[item.from_warehouse]["account_currency"],
+								item=item,
+							)
+						)
 
 						# Do not book expense for transfer within same company transfer
 						if not self.is_internal_transfer():
 							gl_entries.append(
-								self.get_gl_dict({
-									"account": item.expense_account,
-									"against": self.supplier,
-									"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
-									"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-									"cost_center": item.cost_center,
-									"project": item.project
-								}, account_currency, item=item)
+								self.get_gl_dict(
+									{
+										"account": item.expense_account,
+										"against": self.supplier,
+										"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
+										"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+										"cost_center": item.cost_center,
+										"project": item.project,
+									},
+									account_currency,
+									item=item,
+								)
 							)
 
 					else:
 						if not self.is_internal_transfer():
 							gl_entries.append(
-								self.get_gl_dict({
-									"account": item.expense_account,
-									"against": self.supplier,
-									"debit": warehouse_debit_amount,
-									"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-									"cost_center": item.cost_center,
-									"project": item.project or self.project
-								}, account_currency, item=item)
+								self.get_gl_dict(
+									{
+										"account": item.expense_account,
+										"against": self.supplier,
+										"debit": warehouse_debit_amount,
+										"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+										"cost_center": item.cost_center,
+										"project": item.project or self.project,
+									},
+									account_currency,
+									item=item,
+								)
 							)
 
 					# Amount added through landed-cost-voucher
 					if landed_cost_entries:
 						for account, amount in landed_cost_entries[(item.item_code, item.name)].items():
-							gl_entries.append(self.get_gl_dict({
-								"account": account,
-								"against": item.expense_account,
-								"cost_center": item.cost_center,
-								"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-								"credit": flt(amount["base_amount"]),
-								"credit_in_account_currency": flt(amount["amount"]),
-								"project": item.project or self.project
-							}, item=item))
+							gl_entries.append(
+								self.get_gl_dict(
+									{
+										"account": account,
+										"against": item.expense_account,
+										"cost_center": item.cost_center,
+										"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+										"credit": flt(amount["base_amount"]),
+										"credit_in_account_currency": flt(amount["amount"]),
+										"project": item.project or self.project,
+									},
+									item=item,
+								)
+							)
 
 					# sub-contracting warehouse
 					if flt(item.rm_supp_cost):
 						supplier_warehouse_account = warehouse_account[self.supplier_warehouse]["account"]
 						if not supplier_warehouse_account:
-							frappe.throw(_("Please set account in Warehouse {0}")
-								.format(self.supplier_warehouse))
-						gl_entries.append(self.get_gl_dict({
-							"account": supplier_warehouse_account,
-							"against": item.expense_account,
-							"cost_center": item.cost_center,
-							"project": item.project or self.project,
-							"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-							"credit": flt(item.rm_supp_cost)
-						}, warehouse_account[self.supplier_warehouse]["account_currency"], item=item))
+							frappe.throw(_("Please set account in Warehouse {0}").format(self.supplier_warehouse))
+						gl_entries.append(
+							self.get_gl_dict(
+								{
+									"account": supplier_warehouse_account,
+									"against": item.expense_account,
+									"cost_center": item.cost_center,
+									"project": item.project or self.project,
+									"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+									"credit": flt(item.rm_supp_cost),
+								},
+								warehouse_account[self.supplier_warehouse]["account_currency"],
+								item=item,
+							)
+						)
 
-				elif not item.is_fixed_asset or (item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category)):
-					expense_account = (item.expense_account
-						if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account)
+				elif not item.is_fixed_asset or (
+					item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category)
+				):
+					expense_account = (
+						item.expense_account
+						if (not item.enable_deferred_expense or self.is_return)
+						else item.deferred_expense_account
+					)
 
 					if not item.is_fixed_asset:
 						dummy, amount = self.get_amount_and_base_amount(item, enable_discount_accounting)
@@ -652,7 +820,9 @@
 
 					if provisional_accounting_for_non_stock_items:
 						if item.purchase_receipt:
-							provisional_account = self.get_company_default("default_provisional_account")
+							provisional_account = frappe.db.get_value(
+								"Purchase Receipt Item", item.pr_detail, "provisional_expense_account"
+							) or self.get_company_default("default_provisional_account")
 							purchase_receipt_doc = purchase_receipt_doc_map.get(item.purchase_receipt)
 
 							if not purchase_receipt_doc:
@@ -660,103 +830,154 @@
 								purchase_receipt_doc_map[item.purchase_receipt] = purchase_receipt_doc
 
 							# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
-							expense_booked_in_pr = frappe.db.get_value('GL Entry', {'is_cancelled': 0,
-								'voucher_type': 'Purchase Receipt', 'voucher_no': item.purchase_receipt, 'voucher_detail_no': item.pr_detail,
-								'account':provisional_account}, ['name'])
+							expense_booked_in_pr = frappe.db.get_value(
+								"GL Entry",
+								{
+									"is_cancelled": 0,
+									"voucher_type": "Purchase Receipt",
+									"voucher_no": item.purchase_receipt,
+									"voucher_detail_no": item.pr_detail,
+									"account": provisional_account,
+								},
+								["name"],
+							)
 
 							if expense_booked_in_pr:
 								# Intentionally passing purchase invoice item to handle partial billing
-								purchase_receipt_doc.add_provisional_gl_entry(item, gl_entries, self.posting_date, reverse=1)
+								purchase_receipt_doc.add_provisional_gl_entry(
+									item, gl_entries, self.posting_date, provisional_account, reverse=1
+								)
 
 					if not self.is_internal_transfer():
-						gl_entries.append(self.get_gl_dict({
-								"account": expense_account,
-								"against": self.supplier,
-								"debit": amount,
-								"cost_center": item.cost_center,
-								"project": item.project or self.project
-							}, account_currency, item=item))
+						gl_entries.append(
+							self.get_gl_dict(
+								{
+									"account": expense_account,
+									"against": self.supplier,
+									"debit": amount,
+									"cost_center": item.cost_center,
+									"project": item.project or self.project,
+								},
+								account_currency,
+								item=item,
+							)
+						)
 
 						# check if the exchange rate has changed
-						if item.get('purchase_receipt'):
-							if exchange_rate_map[item.purchase_receipt] and \
-								self.conversion_rate != exchange_rate_map[item.purchase_receipt] and \
-								item.net_rate == net_rate_map[item.pr_detail]:
+						if item.get("purchase_receipt"):
+							if (
+								exchange_rate_map[item.purchase_receipt]
+								and self.conversion_rate != exchange_rate_map[item.purchase_receipt]
+								and item.net_rate == net_rate_map[item.pr_detail]
+							):
 
-								discrepancy_caused_by_exchange_rate_difference = (item.qty * item.net_rate) * \
-									(exchange_rate_map[item.purchase_receipt] - self.conversion_rate)
+								discrepancy_caused_by_exchange_rate_difference = (item.qty * item.net_rate) * (
+									exchange_rate_map[item.purchase_receipt] - self.conversion_rate
+								)
 
 								gl_entries.append(
-									self.get_gl_dict({
-										"account": expense_account,
-										"against": self.supplier,
-										"debit": discrepancy_caused_by_exchange_rate_difference,
-										"cost_center": item.cost_center,
-										"project": item.project or self.project
-									}, account_currency, item=item)
+									self.get_gl_dict(
+										{
+											"account": expense_account,
+											"against": self.supplier,
+											"debit": discrepancy_caused_by_exchange_rate_difference,
+											"cost_center": item.cost_center,
+											"project": item.project or self.project,
+										},
+										account_currency,
+										item=item,
+									)
 								)
 								gl_entries.append(
-									self.get_gl_dict({
-										"account": self.get_company_default("exchange_gain_loss_account"),
-										"against": self.supplier,
-										"credit": discrepancy_caused_by_exchange_rate_difference,
-										"cost_center": item.cost_center,
-										"project": item.project or self.project
-									}, account_currency, item=item)
+									self.get_gl_dict(
+										{
+											"account": self.get_company_default("exchange_gain_loss_account"),
+											"against": self.supplier,
+											"credit": discrepancy_caused_by_exchange_rate_difference,
+											"cost_center": item.cost_center,
+											"project": item.project or self.project,
+										},
+										account_currency,
+										item=item,
+									)
 								)
 
 					# If asset is bought through this document and not linked to PR
 					if self.update_stock and item.landed_cost_voucher_amount:
-						expenses_included_in_asset_valuation = self.get_company_default("expenses_included_in_asset_valuation")
+						expenses_included_in_asset_valuation = self.get_company_default(
+							"expenses_included_in_asset_valuation"
+						)
 						# Amount added through landed-cost-voucher
-						gl_entries.append(self.get_gl_dict({
-							"account": expenses_included_in_asset_valuation,
-							"against": expense_account,
-							"cost_center": item.cost_center,
-							"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-							"credit": flt(item.landed_cost_voucher_amount),
-							"project": item.project or self.project
-						}, item=item))
+						gl_entries.append(
+							self.get_gl_dict(
+								{
+									"account": expenses_included_in_asset_valuation,
+									"against": expense_account,
+									"cost_center": item.cost_center,
+									"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+									"credit": flt(item.landed_cost_voucher_amount),
+									"project": item.project or self.project,
+								},
+								item=item,
+							)
+						)
 
-						gl_entries.append(self.get_gl_dict({
-							"account": expense_account,
-							"against": expenses_included_in_asset_valuation,
-							"cost_center": item.cost_center,
-							"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-							"debit": flt(item.landed_cost_voucher_amount),
-							"project": item.project or self.project
-						}, item=item))
+						gl_entries.append(
+							self.get_gl_dict(
+								{
+									"account": expense_account,
+									"against": expenses_included_in_asset_valuation,
+									"cost_center": item.cost_center,
+									"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+									"debit": flt(item.landed_cost_voucher_amount),
+									"project": item.project or self.project,
+								},
+								item=item,
+							)
+						)
 
 						# update gross amount of asset bought through this document
-						assets = frappe.db.get_all('Asset',
-							filters={ 'purchase_invoice': self.name, 'item_code': item.item_code }
+						assets = frappe.db.get_all(
+							"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
 						)
 						for asset in assets:
 							frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
-							frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate))
+							frappe.db.set_value(
+								"Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)
+							)
 
-			if self.auto_accounting_for_stock and self.is_opening == "No" and \
-				item.item_code in stock_items and item.item_tax_amount:
-					# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
-					if item.purchase_receipt and valuation_tax_accounts:
-						negative_expense_booked_in_pr = frappe.db.sql("""select name from `tabGL Entry`
+			if (
+				self.auto_accounting_for_stock
+				and self.is_opening == "No"
+				and item.item_code in stock_items
+				and item.item_tax_amount
+			):
+				# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
+				if item.purchase_receipt and valuation_tax_accounts:
+					negative_expense_booked_in_pr = frappe.db.sql(
+						"""select name from `tabGL Entry`
 							where voucher_type='Purchase Receipt' and voucher_no=%s and account in %s""",
-							(item.purchase_receipt, valuation_tax_accounts))
+						(item.purchase_receipt, valuation_tax_accounts),
+					)
 
-						if not negative_expense_booked_in_pr:
-							gl_entries.append(
-								self.get_gl_dict({
+					if not negative_expense_booked_in_pr:
+						gl_entries.append(
+							self.get_gl_dict(
+								{
 									"account": self.stock_received_but_not_billed,
 									"against": self.supplier,
 									"debit": flt(item.item_tax_amount, item.precision("item_tax_amount")),
 									"remarks": self.remarks or _("Accounting Entry for Stock"),
 									"cost_center": self.cost_center,
-									"project": item.project or self.project
-								}, item=item)
+									"project": item.project or self.project,
+								},
+								item=item,
 							)
+						)
 
-							self.negative_expense_to_be_booked += flt(item.item_tax_amount, \
-								item.precision("item_tax_amount"))
+						self.negative_expense_to_be_booked += flt(
+							item.item_tax_amount, item.precision("item_tax_amount")
+						)
 
 	def get_asset_gl_entry(self, gl_entries):
 		arbnb_account = self.get_company_default("asset_received_but_not_billed")
@@ -764,125 +985,179 @@
 
 		for item in self.get("items"):
 			if item.is_fixed_asset:
-				asset_amount = flt(item.net_amount) + flt(item.item_tax_amount/self.conversion_rate)
+				asset_amount = flt(item.net_amount) + flt(item.item_tax_amount / self.conversion_rate)
 				base_asset_amount = flt(item.base_net_amount + item.item_tax_amount)
 
-				item_exp_acc_type = frappe.db.get_value('Account', item.expense_account, 'account_type')
-				if (not item.expense_account or item_exp_acc_type not in ['Asset Received But Not Billed', 'Fixed Asset']):
+				item_exp_acc_type = frappe.db.get_value("Account", item.expense_account, "account_type")
+				if not item.expense_account or item_exp_acc_type not in [
+					"Asset Received But Not Billed",
+					"Fixed Asset",
+				]:
 					item.expense_account = arbnb_account
 
 				if not self.update_stock:
 					arbnb_currency = get_account_currency(item.expense_account)
-					gl_entries.append(self.get_gl_dict({
-						"account": item.expense_account,
-						"against": self.supplier,
-						"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
-						"debit": base_asset_amount,
-						"debit_in_account_currency": (base_asset_amount
-							if arbnb_currency == self.company_currency else asset_amount),
-						"cost_center": item.cost_center,
-						"project": item.project or self.project
-					}, item=item))
+					gl_entries.append(
+						self.get_gl_dict(
+							{
+								"account": item.expense_account,
+								"against": self.supplier,
+								"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
+								"debit": base_asset_amount,
+								"debit_in_account_currency": (
+									base_asset_amount if arbnb_currency == self.company_currency else asset_amount
+								),
+								"cost_center": item.cost_center,
+								"project": item.project or self.project,
+							},
+							item=item,
+						)
+					)
 
 					if item.item_tax_amount:
 						asset_eiiav_currency = get_account_currency(eiiav_account)
-						gl_entries.append(self.get_gl_dict({
-							"account": eiiav_account,
-							"against": self.supplier,
-							"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
-							"cost_center": item.cost_center,
-							"project": item.project or self.project,
-							"credit": item.item_tax_amount,
-							"credit_in_account_currency": (item.item_tax_amount
-								if asset_eiiav_currency == self.company_currency else
-									item.item_tax_amount / self.conversion_rate)
-						}, item=item))
+						gl_entries.append(
+							self.get_gl_dict(
+								{
+									"account": eiiav_account,
+									"against": self.supplier,
+									"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
+									"cost_center": item.cost_center,
+									"project": item.project or self.project,
+									"credit": item.item_tax_amount,
+									"credit_in_account_currency": (
+										item.item_tax_amount
+										if asset_eiiav_currency == self.company_currency
+										else item.item_tax_amount / self.conversion_rate
+									),
+								},
+								item=item,
+							)
+						)
 				else:
-					cwip_account = get_asset_account("capital_work_in_progress_account",
-						asset_category=item.asset_category,company=self.company)
+					cwip_account = get_asset_account(
+						"capital_work_in_progress_account", asset_category=item.asset_category, company=self.company
+					)
 
 					cwip_account_currency = get_account_currency(cwip_account)
-					gl_entries.append(self.get_gl_dict({
-						"account": cwip_account,
-						"against": self.supplier,
-						"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
-						"debit": base_asset_amount,
-						"debit_in_account_currency": (base_asset_amount
-							if cwip_account_currency == self.company_currency else asset_amount),
-						"cost_center": self.cost_center,
-						"project": item.project or self.project
-					}, item=item))
+					gl_entries.append(
+						self.get_gl_dict(
+							{
+								"account": cwip_account,
+								"against": self.supplier,
+								"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
+								"debit": base_asset_amount,
+								"debit_in_account_currency": (
+									base_asset_amount if cwip_account_currency == self.company_currency else asset_amount
+								),
+								"cost_center": self.cost_center,
+								"project": item.project or self.project,
+							},
+							item=item,
+						)
+					)
 
 					if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
 						asset_eiiav_currency = get_account_currency(eiiav_account)
-						gl_entries.append(self.get_gl_dict({
-							"account": eiiav_account,
-							"against": self.supplier,
-							"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
-							"cost_center": item.cost_center,
-							"credit": item.item_tax_amount,
-							"project": item.project or self.project,
-							"credit_in_account_currency": (item.item_tax_amount
-								if asset_eiiav_currency == self.company_currency else
-									item.item_tax_amount / self.conversion_rate)
-						}, item=item))
+						gl_entries.append(
+							self.get_gl_dict(
+								{
+									"account": eiiav_account,
+									"against": self.supplier,
+									"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
+									"cost_center": item.cost_center,
+									"credit": item.item_tax_amount,
+									"project": item.project or self.project,
+									"credit_in_account_currency": (
+										item.item_tax_amount
+										if asset_eiiav_currency == self.company_currency
+										else item.item_tax_amount / self.conversion_rate
+									),
+								},
+								item=item,
+							)
+						)
 
 					# When update stock is checked
 					# Assets are bought through this document then it will be linked to this document
 					if self.update_stock:
 						if flt(item.landed_cost_voucher_amount):
-							gl_entries.append(self.get_gl_dict({
-								"account": eiiav_account,
-								"against": cwip_account,
-								"cost_center": item.cost_center,
-								"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-								"credit": flt(item.landed_cost_voucher_amount),
-								"project": item.project or self.project
-							}, item=item))
+							gl_entries.append(
+								self.get_gl_dict(
+									{
+										"account": eiiav_account,
+										"against": cwip_account,
+										"cost_center": item.cost_center,
+										"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+										"credit": flt(item.landed_cost_voucher_amount),
+										"project": item.project or self.project,
+									},
+									item=item,
+								)
+							)
 
-							gl_entries.append(self.get_gl_dict({
-								"account": cwip_account,
-								"against": eiiav_account,
-								"cost_center": item.cost_center,
-								"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-								"debit": flt(item.landed_cost_voucher_amount),
-								"project": item.project or self.project
-							}, item=item))
+							gl_entries.append(
+								self.get_gl_dict(
+									{
+										"account": cwip_account,
+										"against": eiiav_account,
+										"cost_center": item.cost_center,
+										"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+										"debit": flt(item.landed_cost_voucher_amount),
+										"project": item.project or self.project,
+									},
+									item=item,
+								)
+							)
 
 						# update gross amount of assets bought through this document
-						assets = frappe.db.get_all('Asset',
-							filters={ 'purchase_invoice': self.name, 'item_code': item.item_code }
+						assets = frappe.db.get_all(
+							"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
 						)
 						for asset in assets:
 							frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
-							frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate))
+							frappe.db.set_value(
+								"Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)
+							)
 
 		return gl_entries
 
-	def make_stock_adjustment_entry(self, gl_entries, item, voucher_wise_stock_value, account_currency):
+	def make_stock_adjustment_entry(
+		self, gl_entries, item, voucher_wise_stock_value, account_currency
+	):
 		net_amt_precision = item.precision("base_net_amount")
 		val_rate_db_precision = 6 if cint(item.precision("valuation_rate")) <= 6 else 9
 
-		warehouse_debit_amount = flt(flt(item.valuation_rate, val_rate_db_precision)
-			* flt(item.qty)	* flt(item.conversion_factor), net_amt_precision)
+		warehouse_debit_amount = flt(
+			flt(item.valuation_rate, val_rate_db_precision) * flt(item.qty) * flt(item.conversion_factor),
+			net_amt_precision,
+		)
 
 		# Stock ledger value is not matching with the warehouse amount
-		if (self.update_stock and voucher_wise_stock_value.get(item.name) and
-			warehouse_debit_amount != flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)):
+		if (
+			self.update_stock
+			and voucher_wise_stock_value.get((item.name, item.warehouse))
+			and warehouse_debit_amount
+			!= flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)
+		):
 
 			cost_of_goods_sold_account = self.get_company_default("default_expense_account")
 			stock_amount = flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)
 			stock_adjustment_amt = warehouse_debit_amount - stock_amount
 
 			gl_entries.append(
-				self.get_gl_dict({
-					"account": cost_of_goods_sold_account,
-					"against": item.expense_account,
-					"debit": stock_adjustment_amt,
-					"remarks": self.get("remarks") or _("Stock Adjustment"),
-					"cost_center": item.cost_center,
-					"project": item.project or self.project
-				}, account_currency, item=item)
+				self.get_gl_dict(
+					{
+						"account": cost_of_goods_sold_account,
+						"against": item.expense_account,
+						"debit": stock_adjustment_amt,
+						"remarks": self.get("remarks") or _("Stock Adjustment"),
+						"cost_center": item.cost_center,
+						"project": item.project or self.project,
+					},
+					account_currency,
+					item=item,
+				)
 			)
 
 			warehouse_debit_amount = stock_amount
@@ -892,7 +1167,9 @@
 	def make_tax_gl_entries(self, gl_entries):
 		# tax table gl entries
 		valuation_tax = {}
-		enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting'))
+		enable_discount_accounting = cint(
+			frappe.db.get_single_value("Buying Settings", "enable_discount_accounting")
+		)
 
 		for tax in self.get("taxes"):
 			amount, base_amount = self.get_tax_amounts(tax, enable_discount_accounting)
@@ -902,24 +1179,35 @@
 				dr_or_cr = "debit" if tax.add_deduct_tax == "Add" else "credit"
 
 				gl_entries.append(
-					self.get_gl_dict({
-						"account": tax.account_head,
-						"against": self.supplier,
-						dr_or_cr: base_amount,
-						dr_or_cr + "_in_account_currency": base_amount
-							if account_currency==self.company_currency
+					self.get_gl_dict(
+						{
+							"account": tax.account_head,
+							"against": self.supplier,
+							dr_or_cr: base_amount,
+							dr_or_cr + "_in_account_currency": base_amount
+							if account_currency == self.company_currency
 							else amount,
-						"cost_center": tax.cost_center
-					}, account_currency, item=tax)
+							"cost_center": tax.cost_center,
+						},
+						account_currency,
+						item=tax,
+					)
 				)
 			# accumulate valuation tax
-			if self.is_opening == "No" and tax.category in ("Valuation", "Valuation and Total") and flt(base_amount) \
-				and not self.is_internal_transfer():
+			if (
+				self.is_opening == "No"
+				and tax.category in ("Valuation", "Valuation and Total")
+				and flt(base_amount)
+				and not self.is_internal_transfer()
+			):
 				if self.auto_accounting_for_stock and not tax.cost_center:
-					frappe.throw(_("Cost Center is required in row {0} in Taxes table for type {1}").format(tax.idx, _(tax.category)))
+					frappe.throw(
+						_("Cost Center is required in row {0} in Taxes table for type {1}").format(
+							tax.idx, _(tax.category)
+						)
+					)
 				valuation_tax.setdefault(tax.name, 0)
-				valuation_tax[tax.name] += \
-					(tax.add_deduct_tax == "Add" and 1 or -1) * flt(base_amount)
+				valuation_tax[tax.name] += (tax.add_deduct_tax == "Add" and 1 or -1) * flt(base_amount)
 
 		if self.is_opening == "No" and self.negative_expense_to_be_booked and valuation_tax:
 			# credit valuation tax amount in "Expenses Included In Valuation"
@@ -933,17 +1221,22 @@
 					if i == len(valuation_tax):
 						applicable_amount = amount_including_divisional_loss
 					else:
-						applicable_amount = self.negative_expense_to_be_booked * (valuation_tax[tax.name] / total_valuation_amount)
+						applicable_amount = self.negative_expense_to_be_booked * (
+							valuation_tax[tax.name] / total_valuation_amount
+						)
 						amount_including_divisional_loss -= applicable_amount
 
 					gl_entries.append(
-						self.get_gl_dict({
-							"account": tax.account_head,
-							"cost_center": tax.cost_center,
-							"against": self.supplier,
-							"credit": applicable_amount,
-							"remarks": self.remarks or _("Accounting Entry for Stock"),
-						}, item=tax)
+						self.get_gl_dict(
+							{
+								"account": tax.account_head,
+								"cost_center": tax.cost_center,
+								"against": self.supplier,
+								"credit": applicable_amount,
+								"remarks": self.remarks or _("Accounting Entry for Stock"),
+							},
+							item=tax,
+						)
 					)
 
 					i += 1
@@ -952,18 +1245,24 @@
 			for tax in self.get("taxes"):
 				if valuation_tax.get(tax.name):
 					gl_entries.append(
-						self.get_gl_dict({
-							"account": tax.account_head,
-							"cost_center": tax.cost_center,
-							"against": self.supplier,
-							"credit": valuation_tax[tax.name],
-							"remarks": self.remarks or _("Accounting Entry for Stock")
-						}, item=tax))
+						self.get_gl_dict(
+							{
+								"account": tax.account_head,
+								"cost_center": tax.cost_center,
+								"against": self.supplier,
+								"credit": valuation_tax[tax.name],
+								"remarks": self.remarks or _("Accounting Entry for Stock"),
+							},
+							item=tax,
+						)
+					)
 
 	@property
 	def enable_discount_accounting(self):
 		if not hasattr(self, "_enable_discount_accounting"):
-			self._enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting'))
+			self._enable_discount_accounting = cint(
+				frappe.db.get_single_value("Buying Settings", "enable_discount_accounting")
+			)
 
 		return self._enable_discount_accounting
 
@@ -971,13 +1270,18 @@
 		if self.is_internal_transfer() and flt(self.base_total_taxes_and_charges):
 			account_currency = get_account_currency(self.unrealized_profit_loss_account)
 			gl_entries.append(
-				self.get_gl_dict({
-					"account": self.unrealized_profit_loss_account,
-					"against": self.supplier,
-					"credit": flt(self.total_taxes_and_charges),
-					"credit_in_account_currency": flt(self.base_total_taxes_and_charges),
-					"cost_center": self.cost_center
-				}, account_currency, item=self))
+				self.get_gl_dict(
+					{
+						"account": self.unrealized_profit_loss_account,
+						"against": self.supplier,
+						"credit": flt(self.total_taxes_and_charges),
+						"credit_in_account_currency": flt(self.base_total_taxes_and_charges),
+						"cost_center": self.cost_center,
+					},
+					account_currency,
+					item=self,
+				)
+			)
 
 	def make_payment_gl_entries(self, gl_entries):
 		# Make Cash GL Entries
@@ -985,30 +1289,42 @@
 			bank_account_currency = get_account_currency(self.cash_bank_account)
 			# CASH, make payment entries
 			gl_entries.append(
-				self.get_gl_dict({
-					"account": self.credit_to,
-					"party_type": "Supplier",
-					"party": self.supplier,
-					"against": self.cash_bank_account,
-					"debit": self.base_paid_amount,
-					"debit_in_account_currency": self.base_paid_amount \
-						if self.party_account_currency==self.company_currency else self.paid_amount,
-					"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
-					"against_voucher_type": self.doctype,
-					"cost_center": self.cost_center,
-					"project": self.project
-				}, self.party_account_currency, item=self)
+				self.get_gl_dict(
+					{
+						"account": self.credit_to,
+						"party_type": "Supplier",
+						"party": self.supplier,
+						"against": self.cash_bank_account,
+						"debit": self.base_paid_amount,
+						"debit_in_account_currency": self.base_paid_amount
+						if self.party_account_currency == self.company_currency
+						else self.paid_amount,
+						"against_voucher": self.return_against
+						if cint(self.is_return) and self.return_against
+						else self.name,
+						"against_voucher_type": self.doctype,
+						"cost_center": self.cost_center,
+						"project": self.project,
+					},
+					self.party_account_currency,
+					item=self,
+				)
 			)
 
 			gl_entries.append(
-				self.get_gl_dict({
-					"account": self.cash_bank_account,
-					"against": self.supplier,
-					"credit": self.base_paid_amount,
-					"credit_in_account_currency": self.base_paid_amount \
-						if bank_account_currency==self.company_currency else self.paid_amount,
-					"cost_center": self.cost_center
-				}, bank_account_currency, item=self)
+				self.get_gl_dict(
+					{
+						"account": self.cash_bank_account,
+						"against": self.supplier,
+						"credit": self.base_paid_amount,
+						"credit_in_account_currency": self.base_paid_amount
+						if bank_account_currency == self.company_currency
+						else self.paid_amount,
+						"cost_center": self.cost_center,
+					},
+					bank_account_currency,
+					item=self,
+				)
 			)
 
 	def make_write_off_gl_entry(self, gl_entries):
@@ -1018,48 +1334,66 @@
 			write_off_account_currency = get_account_currency(self.write_off_account)
 
 			gl_entries.append(
-				self.get_gl_dict({
-					"account": self.credit_to,
-					"party_type": "Supplier",
-					"party": self.supplier,
-					"against": self.write_off_account,
-					"debit": self.base_write_off_amount,
-					"debit_in_account_currency": self.base_write_off_amount \
-						if self.party_account_currency==self.company_currency else self.write_off_amount,
-					"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
-					"against_voucher_type": self.doctype,
-					"cost_center": self.cost_center,
-					"project": self.project
-				}, self.party_account_currency, item=self)
+				self.get_gl_dict(
+					{
+						"account": self.credit_to,
+						"party_type": "Supplier",
+						"party": self.supplier,
+						"against": self.write_off_account,
+						"debit": self.base_write_off_amount,
+						"debit_in_account_currency": self.base_write_off_amount
+						if self.party_account_currency == self.company_currency
+						else self.write_off_amount,
+						"against_voucher": self.return_against
+						if cint(self.is_return) and self.return_against
+						else self.name,
+						"against_voucher_type": self.doctype,
+						"cost_center": self.cost_center,
+						"project": self.project,
+					},
+					self.party_account_currency,
+					item=self,
+				)
 			)
 			gl_entries.append(
-				self.get_gl_dict({
-					"account": self.write_off_account,
-					"against": self.supplier,
-					"credit": flt(self.base_write_off_amount),
-					"credit_in_account_currency": self.base_write_off_amount \
-						if write_off_account_currency==self.company_currency else self.write_off_amount,
-					"cost_center": self.cost_center or self.write_off_cost_center
-				}, item=self)
+				self.get_gl_dict(
+					{
+						"account": self.write_off_account,
+						"against": self.supplier,
+						"credit": flt(self.base_write_off_amount),
+						"credit_in_account_currency": self.base_write_off_amount
+						if write_off_account_currency == self.company_currency
+						else self.write_off_amount,
+						"cost_center": self.cost_center or self.write_off_cost_center,
+					},
+					item=self,
+				)
 			)
 
 	def make_gle_for_rounding_adjustment(self, gl_entries):
 		# if rounding adjustment in small and conversion rate is also small then
 		# base_rounding_adjustment may become zero due to small precision
 		# eg: rounding_adjustment = 0.01 and exchange rate = 0.05 and precision of base_rounding_adjustment is 2
-		#	then base_rounding_adjustment becomes zero and error is thrown in GL Entry
-		if not self.is_internal_transfer() and self.rounding_adjustment and self.base_rounding_adjustment:
-			round_off_account, round_off_cost_center = \
-				get_round_off_account_and_cost_center(self.company)
+		# 	then base_rounding_adjustment becomes zero and error is thrown in GL Entry
+		if (
+			not self.is_internal_transfer() and self.rounding_adjustment and self.base_rounding_adjustment
+		):
+			round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
+				self.company, "Purchase Invoice", self.name
+			)
 
 			gl_entries.append(
-				self.get_gl_dict({
-					"account": round_off_account,
-					"against": self.supplier,
-					"debit_in_account_currency": self.rounding_adjustment,
-					"debit": self.base_rounding_adjustment,
-					"cost_center": self.cost_center or round_off_cost_center,
-				}, item=self))
+				self.get_gl_dict(
+					{
+						"account": round_off_account,
+						"against": self.supplier,
+						"debit_in_account_currency": self.rounding_adjustment,
+						"debit": self.base_rounding_adjustment,
+						"cost_center": self.cost_center or round_off_cost_center,
+					},
+					item=self,
+				)
+			)
 
 	def on_cancel(self):
 		check_if_return_invoice_linked_with_payment_entry(self)
@@ -1090,10 +1424,15 @@
 			self.repost_future_sle_and_gle()
 
 		self.update_project()
-		frappe.db.set(self, 'status', 'Cancelled')
+		frappe.db.set(self, "status", "Cancelled")
 
 		unlink_inter_company_doc(self.doctype, self.name, self.inter_company_invoice_reference)
-		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
+		self.ignore_linked_doctypes = (
+			"GL Entry",
+			"Stock Ledger Entry",
+			"Repost Item Valuation",
+			"Payment Ledger Entry",
+		)
 		self.update_advance_tax_references(cancel=1)
 
 	def update_project(self):
@@ -1114,19 +1453,22 @@
 			if cint(frappe.db.get_single_value("Accounts Settings", "check_supplier_invoice_uniqueness")):
 				fiscal_year = get_fiscal_year(self.posting_date, company=self.company, as_dict=True)
 
-				pi = frappe.db.sql('''select name from `tabPurchase Invoice`
+				pi = frappe.db.sql(
+					"""select name from `tabPurchase Invoice`
 					where
 						bill_no = %(bill_no)s
 						and supplier = %(supplier)s
 						and name != %(name)s
 						and docstatus < 2
-						and posting_date between %(year_start_date)s and %(year_end_date)s''', {
-							"bill_no": self.bill_no,
-							"supplier": self.supplier,
-							"name": self.name,
-							"year_start_date": fiscal_year.year_start_date,
-							"year_end_date": fiscal_year.year_end_date
-						})
+						and posting_date between %(year_start_date)s and %(year_end_date)s""",
+					{
+						"bill_no": self.bill_no,
+						"supplier": self.supplier,
+						"name": self.name,
+						"year_start_date": fiscal_year.year_start_date,
+						"year_end_date": fiscal_year.year_end_date,
+					},
+				)
 
 				if pi:
 					pi = pi[0][0]
@@ -1136,16 +1478,26 @@
 		updated_pr = []
 		for d in self.get("items"):
 			if d.pr_detail:
-				billed_amt = frappe.db.sql("""select sum(amount) from `tabPurchase Invoice Item`
-					where pr_detail=%s and docstatus=1""", d.pr_detail)
+				billed_amt = frappe.db.sql(
+					"""select sum(amount) from `tabPurchase Invoice Item`
+					where pr_detail=%s and docstatus=1""",
+					d.pr_detail,
+				)
 				billed_amt = billed_amt and billed_amt[0][0] or 0
-				frappe.db.set_value("Purchase Receipt Item", d.pr_detail, "billed_amt", billed_amt, update_modified=update_modified)
+				frappe.db.set_value(
+					"Purchase Receipt Item",
+					d.pr_detail,
+					"billed_amt",
+					billed_amt,
+					update_modified=update_modified,
+				)
 				updated_pr.append(d.purchase_receipt)
 			elif d.po_detail:
 				updated_pr += update_billed_amount_based_on_po(d.po_detail, update_modified)
 
 		for pr in set(updated_pr):
 			from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billing_percentage
+
 			pr_doc = frappe.get_doc("Purchase Receipt", pr)
 			update_billing_percentage(pr_doc, update_modified=update_modified)
 
@@ -1153,25 +1505,29 @@
 		self.due_date = None
 
 	def block_invoice(self, hold_comment=None, release_date=None):
-		self.db_set('on_hold', 1)
-		self.db_set('hold_comment', cstr(hold_comment))
-		self.db_set('release_date', release_date)
+		self.db_set("on_hold", 1)
+		self.db_set("hold_comment", cstr(hold_comment))
+		self.db_set("release_date", release_date)
 
 	def unblock_invoice(self):
-		self.db_set('on_hold', 0)
-		self.db_set('release_date', None)
+		self.db_set("on_hold", 0)
+		self.db_set("release_date", None)
 
 	def set_tax_withholding(self):
 		if not self.apply_tds:
 			return
 
-		if self.apply_tds and not self.get('tax_withholding_category'):
-			self.tax_withholding_category = frappe.db.get_value('Supplier', self.supplier, 'tax_withholding_category')
+		if self.apply_tds and not self.get("tax_withholding_category"):
+			self.tax_withholding_category = frappe.db.get_value(
+				"Supplier", self.supplier, "tax_withholding_category"
+			)
 
 		if not self.tax_withholding_category:
 			return
 
-		tax_withholding_details, advance_taxes = get_party_tax_withholding_details(self, self.tax_withholding_category)
+		tax_withholding_details, advance_taxes = get_party_tax_withholding_details(
+			self, self.tax_withholding_category
+		)
 
 		# Adjust TDS paid on advances
 		self.allocate_advance_tds(tax_withholding_details, advance_taxes)
@@ -1189,8 +1545,11 @@
 		if not accounts or tax_withholding_details.get("account_head") not in accounts:
 			self.append("taxes", tax_withholding_details)
 
-		to_remove = [d for d in self.taxes
-			if not d.tax_amount and d.account_head == tax_withholding_details.get("account_head")]
+		to_remove = [
+			d
+			for d in self.taxes
+			if not d.tax_amount and d.account_head == tax_withholding_details.get("account_head")
+		]
 
 		for d in to_remove:
 			self.remove(d)
@@ -1199,27 +1558,33 @@
 		self.calculate_taxes_and_totals()
 
 	def allocate_advance_tds(self, tax_withholding_details, advance_taxes):
-		self.set('advance_tax', [])
+		self.set("advance_tax", [])
 		for tax in advance_taxes:
 			allocated_amount = 0
 			pending_amount = flt(tax.tax_amount - tax.allocated_amount)
-			if flt(tax_withholding_details.get('tax_amount')) >= pending_amount:
-				tax_withholding_details['tax_amount'] -= pending_amount
+			if flt(tax_withholding_details.get("tax_amount")) >= pending_amount:
+				tax_withholding_details["tax_amount"] -= pending_amount
 				allocated_amount = pending_amount
-			elif flt(tax_withholding_details.get('tax_amount')) and flt(tax_withholding_details.get('tax_amount')) < pending_amount:
-				allocated_amount = tax_withholding_details['tax_amount']
-				tax_withholding_details['tax_amount'] = 0
+			elif (
+				flt(tax_withholding_details.get("tax_amount"))
+				and flt(tax_withholding_details.get("tax_amount")) < pending_amount
+			):
+				allocated_amount = tax_withholding_details["tax_amount"]
+				tax_withholding_details["tax_amount"] = 0
 
-			self.append('advance_tax', {
-				'reference_type': 'Payment Entry',
-				'reference_name': tax.parent,
-				'reference_detail': tax.name,
-				'account_head': tax.account_head,
-				'allocated_amount': allocated_amount
-			})
+			self.append(
+				"advance_tax",
+				{
+					"reference_type": "Payment Entry",
+					"reference_name": tax.parent,
+					"reference_detail": tax.name,
+					"account_head": tax.account_head,
+					"allocated_amount": allocated_amount,
+				},
+			)
 
 	def update_advance_tax_references(self, cancel=0):
-		for tax in self.get('advance_tax'):
+		for tax in self.get("advance_tax"):
 			at = frappe.qb.DocType("Advance Taxes and Charges").as_("at")
 
 			if cancel:
@@ -1233,8 +1598,8 @@
 
 	def set_status(self, update=False, status=None, update_modified=True):
 		if self.is_new():
-			if self.get('amended_from'):
-				self.status = 'Draft'
+			if self.get("amended_from"):
+				self.status = "Draft"
 			return
 
 		outstanding_amount = flt(self.outstanding_amount, self.precision("outstanding_amount"))
@@ -1245,19 +1610,25 @@
 				status = "Cancelled"
 			elif self.docstatus == 1:
 				if self.is_internal_transfer():
-					self.status = 'Internal Transfer'
+					self.status = "Internal Transfer"
 				elif is_overdue(self, total):
 					self.status = "Overdue"
 				elif 0 < outstanding_amount < total:
 					self.status = "Partly Paid"
 				elif outstanding_amount > 0 and getdate(self.due_date) >= getdate():
 					self.status = "Unpaid"
-				#Check if outstanding amount is 0 due to debit note issued against invoice
-				elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
+				# Check if outstanding amount is 0 due to debit note issued against invoice
+				elif (
+					outstanding_amount <= 0
+					and self.is_return == 0
+					and frappe.db.get_value(
+						"Purchase Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1}
+					)
+				):
 					self.status = "Debit Note Issued"
 				elif self.is_return == 1:
 					self.status = "Return"
-				elif outstanding_amount<=0:
+				elif outstanding_amount <= 0:
 					self.status = "Paid"
 				else:
 					self.status = "Submitted"
@@ -1265,106 +1636,126 @@
 				self.status = "Draft"
 
 		if update:
-			self.db_set('status', self.status, update_modified = update_modified)
+			self.db_set("status", self.status, update_modified=update_modified)
+
 
 # to get details of purchase invoice/receipt from which this doc was created for exchange rate difference handling
 def get_purchase_document_details(doc):
-	if doc.doctype == 'Purchase Invoice':
-		doc_reference = 'purchase_receipt'
-		items_reference = 'pr_detail'
-		parent_doctype = 'Purchase Receipt'
-		child_doctype = 'Purchase Receipt Item'
+	if doc.doctype == "Purchase Invoice":
+		doc_reference = "purchase_receipt"
+		items_reference = "pr_detail"
+		parent_doctype = "Purchase Receipt"
+		child_doctype = "Purchase Receipt Item"
 	else:
-		doc_reference = 'purchase_invoice'
-		items_reference = 'purchase_invoice_item'
-		parent_doctype = 'Purchase Invoice'
-		child_doctype = 'Purchase Invoice Item'
+		doc_reference = "purchase_invoice"
+		items_reference = "purchase_invoice_item"
+		parent_doctype = "Purchase Invoice"
+		child_doctype = "Purchase Invoice Item"
 
 	purchase_receipts_or_invoices = []
 	items = []
 
-	for item in doc.get('items'):
+	for item in doc.get("items"):
 		if item.get(doc_reference):
 			purchase_receipts_or_invoices.append(item.get(doc_reference))
 		if item.get(items_reference):
 			items.append(item.get(items_reference))
 
-	exchange_rate_map = frappe._dict(frappe.get_all(parent_doctype, filters={'name': ('in',
-		purchase_receipts_or_invoices)}, fields=['name', 'conversion_rate'], as_list=1))
+	exchange_rate_map = frappe._dict(
+		frappe.get_all(
+			parent_doctype,
+			filters={"name": ("in", purchase_receipts_or_invoices)},
+			fields=["name", "conversion_rate"],
+			as_list=1,
+		)
+	)
 
-	net_rate_map = frappe._dict(frappe.get_all(child_doctype, filters={'name': ('in',
-		items)}, fields=['name', 'net_rate'], as_list=1))
+	net_rate_map = frappe._dict(
+		frappe.get_all(
+			child_doctype, filters={"name": ("in", items)}, fields=["name", "net_rate"], as_list=1
+		)
+	)
 
 	return exchange_rate_map, net_rate_map
 
+
 def get_list_context(context=None):
 	from erpnext.controllers.website_list_for_contact import get_list_context
+
 	list_context = get_list_context(context)
-	list_context.update({
-		'show_sidebar': True,
-		'show_search': True,
-		'no_breadcrumbs': True,
-		'title': _('Purchase Invoices'),
-	})
+	list_context.update(
+		{
+			"show_sidebar": True,
+			"show_search": True,
+			"no_breadcrumbs": True,
+			"title": _("Purchase Invoices"),
+		}
+	)
 	return list_context
 
+
 @erpnext.allow_regional
 def make_regional_gl_entries(gl_entries, doc):
 	return gl_entries
 
+
 @frappe.whitelist()
 def make_debit_note(source_name, target_doc=None):
 	from erpnext.controllers.sales_and_purchase_return import make_return_doc
+
 	return make_return_doc("Purchase Invoice", source_name, target_doc)
 
+
 @frappe.whitelist()
 def make_stock_entry(source_name, target_doc=None):
-	doc = get_mapped_doc("Purchase Invoice", source_name, {
-		"Purchase Invoice": {
-			"doctype": "Stock Entry",
-			"validation": {
-				"docstatus": ["=", 1]
-			}
-		},
-		"Purchase Invoice Item": {
-			"doctype": "Stock Entry Detail",
-			"field_map": {
-				"stock_qty": "transfer_qty",
-				"batch_no": "batch_no"
+	doc = get_mapped_doc(
+		"Purchase Invoice",
+		source_name,
+		{
+			"Purchase Invoice": {"doctype": "Stock Entry", "validation": {"docstatus": ["=", 1]}},
+			"Purchase Invoice Item": {
+				"doctype": "Stock Entry Detail",
+				"field_map": {"stock_qty": "transfer_qty", "batch_no": "batch_no"},
 			},
-		}
-	}, target_doc)
+		},
+		target_doc,
+	)
 
 	return doc
 
+
 @frappe.whitelist()
 def change_release_date(name, release_date=None):
-	if frappe.db.exists('Purchase Invoice', name):
-		pi = frappe.get_doc('Purchase Invoice', name)
-		pi.db_set('release_date', release_date)
+	if frappe.db.exists("Purchase Invoice", name):
+		pi = frappe.get_doc("Purchase Invoice", name)
+		pi.db_set("release_date", release_date)
 
 
 @frappe.whitelist()
 def unblock_invoice(name):
-	if frappe.db.exists('Purchase Invoice', name):
-		pi = frappe.get_doc('Purchase Invoice', name)
+	if frappe.db.exists("Purchase Invoice", name):
+		pi = frappe.get_doc("Purchase Invoice", name)
 		pi.unblock_invoice()
 
 
 @frappe.whitelist()
 def block_invoice(name, release_date, hold_comment=None):
-	if frappe.db.exists('Purchase Invoice', name):
-		pi = frappe.get_doc('Purchase Invoice', name)
+	if frappe.db.exists("Purchase Invoice", name):
+		pi = frappe.get_doc("Purchase Invoice", name)
 		pi.block_invoice(hold_comment, release_date)
 
+
 @frappe.whitelist()
 def make_inter_company_sales_invoice(source_name, target_doc=None):
 	from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction
+
 	return make_inter_company_transaction("Purchase Invoice", source_name, target_doc)
 
+
 def on_doctype_update():
 	frappe.db.add_index("Purchase Invoice", ["supplier", "is_return", "return_against"])
 
+
 @frappe.whitelist()
 def make_purchase_receipt(source_name, target_doc=None):
 	def update_item(obj, target, source_parent):
@@ -1372,33 +1763,37 @@
 		target.received_qty = flt(obj.qty) - flt(obj.received_qty)
 		target.stock_qty = (flt(obj.qty) - flt(obj.received_qty)) * flt(obj.conversion_factor)
 		target.amount = (flt(obj.qty) - flt(obj.received_qty)) * flt(obj.rate)
-		target.base_amount = (flt(obj.qty) - flt(obj.received_qty)) * \
-			flt(obj.rate) * flt(source_parent.conversion_rate)
+		target.base_amount = (
+			(flt(obj.qty) - flt(obj.received_qty)) * flt(obj.rate) * flt(source_parent.conversion_rate)
+		)
 
-	doc = get_mapped_doc("Purchase Invoice", source_name, {
-		"Purchase Invoice": {
-			"doctype": "Purchase Receipt",
-			"validation": {
-				"docstatus": ["=", 1],
-			}
-		},
-		"Purchase Invoice Item": {
-			"doctype": "Purchase Receipt Item",
-			"field_map": {
-				"name": "purchase_invoice_item",
-				"parent": "purchase_invoice",
-				"bom": "bom",
-				"purchase_order": "purchase_order",
-				"po_detail": "purchase_order_item",
-				"material_request": "material_request",
-				"material_request_item": "material_request_item"
+	doc = get_mapped_doc(
+		"Purchase Invoice",
+		source_name,
+		{
+			"Purchase Invoice": {
+				"doctype": "Purchase Receipt",
+				"validation": {
+					"docstatus": ["=", 1],
+				},
 			},
-			"postprocess": update_item,
-			"condition": lambda doc: abs(doc.received_qty) < abs(doc.qty)
+			"Purchase Invoice Item": {
+				"doctype": "Purchase Receipt Item",
+				"field_map": {
+					"name": "purchase_invoice_item",
+					"parent": "purchase_invoice",
+					"bom": "bom",
+					"purchase_order": "purchase_order",
+					"po_detail": "purchase_order_item",
+					"material_request": "material_request",
+					"material_request_item": "material_request_item",
+				},
+				"postprocess": update_item,
+				"condition": lambda doc: abs(doc.received_qty) < abs(doc.qty),
+			},
+			"Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges"},
 		},
-		"Purchase Taxes and Charges": {
-			"doctype": "Purchase Taxes and Charges"
-		}
-	}, target_doc)
+		target_doc,
+	)
 
 	return doc
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py
index 76c9fcd..10dd0ef 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py
@@ -3,35 +3,26 @@
 
 def get_data():
 	return {
-		'fieldname': 'purchase_invoice',
-		'non_standard_fieldnames': {
-			'Journal Entry': 'reference_name',
-			'Payment Entry': 'reference_name',
-			'Payment Request': 'reference_name',
-			'Landed Cost Voucher': 'receipt_document',
-			'Purchase Invoice': 'return_against',
-			'Auto Repeat': 'reference_document'
+		"fieldname": "purchase_invoice",
+		"non_standard_fieldnames": {
+			"Journal Entry": "reference_name",
+			"Payment Entry": "reference_name",
+			"Payment Request": "reference_name",
+			"Landed Cost Voucher": "receipt_document",
+			"Purchase Invoice": "return_against",
+			"Auto Repeat": "reference_document",
 		},
-		'internal_links': {
-			'Purchase Order': ['items', 'purchase_order'],
-			'Purchase Receipt': ['items', 'purchase_receipt'],
+		"internal_links": {
+			"Purchase Order": ["items", "purchase_order"],
+			"Purchase Receipt": ["items", "purchase_receipt"],
 		},
-		'transactions': [
+		"transactions": [
+			{"label": _("Payment"), "items": ["Payment Entry", "Payment Request", "Journal Entry"]},
 			{
-				'label': _('Payment'),
-				'items': ['Payment Entry', 'Payment Request', 'Journal Entry']
+				"label": _("Reference"),
+				"items": ["Purchase Order", "Purchase Receipt", "Asset", "Landed Cost Voucher"],
 			},
-			{
-				'label': _('Reference'),
-				'items': ['Purchase Order', 'Purchase Receipt', 'Asset', 'Landed Cost Voucher']
-			},
-			{
-				'label': _('Returns'),
-				'items': ['Purchase Invoice']
-			},
-			{
-				'label': _('Subscription'),
-				'items': ['Auto Repeat']
-			},
-		]
+			{"label": _("Returns"), "items": ["Purchase Invoice"]},
+			{"label": _("Subscription"), "items": ["Auto Repeat"]},
+		],
 	}
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index d51a008..6412da7 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -2,10 +2,10 @@
 # License: GNU General Public License v3. See license.txt
 
 
-
 import unittest
 
 import frappe
+from frappe.tests.utils import change_settings
 from frappe.utils import add_days, cint, flt, getdate, nowdate, today
 
 import erpnext
@@ -27,11 +27,13 @@
 	make_purchase_receipt,
 )
 from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction
+from erpnext.stock.tests.test_utils import StockTestMixin
 
 test_dependencies = ["Item", "Cost Center", "Payment Term", "Payment Terms Template"]
 test_ignore = ["Serial No"]
 
-class TestPurchaseInvoice(unittest.TestCase):
+
+class TestPurchaseInvoice(unittest.TestCase, StockTestMixin):
 	@classmethod
 	def setUpClass(self):
 		unlink_payment_on_cancel_of_invoice()
@@ -43,16 +45,18 @@
 
 	def test_purchase_invoice_received_qty(self):
 		"""
-			1. Test if received qty is validated against accepted + rejected
-			2. Test if received qty is auto set on save
+		1. Test if received qty is validated against accepted + rejected
+		2. Test if received qty is auto set on save
 		"""
 		pi = make_purchase_invoice(
 			qty=1,
 			rejected_qty=1,
 			received_qty=3,
 			item_code="_Test Item Home Desktop 200",
-			rejected_warehouse = "_Test Rejected Warehouse - _TC",
-			update_stock=True, do_not_save=True)
+			rejected_warehouse="_Test Rejected Warehouse - _TC",
+			update_stock=True,
+			do_not_save=True,
+		)
 		self.assertRaises(QtyMismatchError, pi.save)
 
 		pi.items[0].received_qty = 0
@@ -79,18 +83,26 @@
 			"_Test Account CST - _TC": [29.88, 0],
 			"_Test Account VAT - _TC": [156.25, 0],
 			"_Test Account Discount - _TC": [0, 168.03],
-			"Round Off - _TC": [0, 0.3]
+			"Round Off - _TC": [0, 0.3],
 		}
-		gl_entries = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
-			where voucher_type = 'Purchase Invoice' and voucher_no = %s""", pi.name, as_dict=1)
+		gl_entries = frappe.db.sql(
+			"""select account, debit, credit from `tabGL Entry`
+			where voucher_type = 'Purchase Invoice' and voucher_no = %s""",
+			pi.name,
+			as_dict=1,
+		)
 		for d in gl_entries:
 			self.assertEqual([d.debit, d.credit], expected_gl_entries.get(d.account))
 
 	def test_gl_entries_with_perpetual_inventory(self):
-		pi = make_purchase_invoice(company="_Test Company with perpetual inventory",
-			warehouse= "Stores - TCP1", cost_center = "Main - TCP1",
-			expense_account ="_Test Account Cost for Goods Sold - TCP1",
-			get_taxes_and_charges=True, qty=10)
+		pi = make_purchase_invoice(
+			company="_Test Company with perpetual inventory",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+			expense_account="_Test Account Cost for Goods Sold - TCP1",
+			get_taxes_and_charges=True,
+			qty=10,
+		)
 
 		self.assertTrue(cint(erpnext.is_perpetual_inventory_enabled(pi.company)), 1)
 
@@ -104,6 +116,7 @@
 
 	def test_payment_entry_unlink_against_purchase_invoice(self):
 		from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
+
 		unlink_payment_on_cancel_of_invoice(0)
 
 		pi_doc = make_purchase_invoice()
@@ -119,7 +132,7 @@
 		pe.save(ignore_permissions=True)
 		pe.submit()
 
-		pi_doc = frappe.get_doc('Purchase Invoice', pi_doc.name)
+		pi_doc = frappe.get_doc("Purchase Invoice", pi_doc.name)
 		pi_doc.load_from_db()
 		self.assertTrue(pi_doc.status, "Paid")
 
@@ -127,7 +140,7 @@
 		unlink_payment_on_cancel_of_invoice()
 
 	def test_purchase_invoice_for_blocked_supplier(self):
-		supplier = frappe.get_doc('Supplier', '_Test Supplier')
+		supplier = frappe.get_doc("Supplier", "_Test Supplier")
 		supplier.on_hold = 1
 		supplier.save()
 
@@ -137,9 +150,9 @@
 		supplier.save()
 
 	def test_purchase_invoice_for_blocked_supplier_invoice(self):
-		supplier = frappe.get_doc('Supplier', '_Test Supplier')
+		supplier = frappe.get_doc("Supplier", "_Test Supplier")
 		supplier.on_hold = 1
-		supplier.hold_type = 'Invoices'
+		supplier.hold_type = "Invoices"
 		supplier.save()
 
 		self.assertRaises(frappe.ValidationError, make_purchase_invoice)
@@ -148,31 +161,40 @@
 		supplier.save()
 
 	def test_purchase_invoice_for_blocked_supplier_payment(self):
-		supplier = frappe.get_doc('Supplier', '_Test Supplier')
+		supplier = frappe.get_doc("Supplier", "_Test Supplier")
 		supplier.on_hold = 1
-		supplier.hold_type = 'Payments'
+		supplier.hold_type = "Payments"
 		supplier.save()
 
 		pi = make_purchase_invoice()
 
 		self.assertRaises(
-			frappe.ValidationError, get_payment_entry, dt='Purchase Invoice', dn=pi.name, bank_account="_Test Bank - _TC")
+			frappe.ValidationError,
+			get_payment_entry,
+			dt="Purchase Invoice",
+			dn=pi.name,
+			bank_account="_Test Bank - _TC",
+		)
 
 		supplier.on_hold = 0
 		supplier.save()
 
 	def test_purchase_invoice_for_blocked_supplier_payment_today_date(self):
-		supplier = frappe.get_doc('Supplier', '_Test Supplier')
+		supplier = frappe.get_doc("Supplier", "_Test Supplier")
 		supplier.on_hold = 1
-		supplier.hold_type = 'Payments'
+		supplier.hold_type = "Payments"
 		supplier.release_date = nowdate()
 		supplier.save()
 
 		pi = make_purchase_invoice()
 
 		self.assertRaises(
-			frappe.ValidationError, get_payment_entry, dt='Purchase Invoice', dn=pi.name,
-			bank_account="_Test Bank - _TC")
+			frappe.ValidationError,
+			get_payment_entry,
+			dt="Purchase Invoice",
+			dn=pi.name,
+			bank_account="_Test Bank - _TC",
+		)
 
 		supplier.on_hold = 0
 		supplier.save()
@@ -181,15 +203,15 @@
 		# this test is meant to fail only if something fails in the try block
 		with self.assertRaises(Exception):
 			try:
-				supplier = frappe.get_doc('Supplier', '_Test Supplier')
+				supplier = frappe.get_doc("Supplier", "_Test Supplier")
 				supplier.on_hold = 1
-				supplier.hold_type = 'Payments'
-				supplier.release_date = '2018-03-01'
+				supplier.hold_type = "Payments"
+				supplier.release_date = "2018-03-01"
 				supplier.save()
 
 				pi = make_purchase_invoice()
 
-				get_payment_entry('Purchase Invoice', dn=pi.name, bank_account="_Test Bank - _TC")
+				get_payment_entry("Purchase Invoice", dn=pi.name, bank_account="_Test Bank - _TC")
 
 				supplier.on_hold = 0
 				supplier.save()
@@ -203,7 +225,7 @@
 		pi.release_date = nowdate()
 
 		self.assertRaises(frappe.ValidationError, pi.save)
-		pi.release_date = ''
+		pi.release_date = ""
 		pi.save()
 
 	def test_purchase_invoice_temporary_blocked(self):
@@ -212,7 +234,7 @@
 		pi.save()
 		pi.submit()
 
-		pe = get_payment_entry('Purchase Invoice', dn=pi.name, bank_account="_Test Bank - _TC")
+		pe = get_payment_entry("Purchase Invoice", dn=pi.name, bank_account="_Test Bank - _TC")
 
 		self.assertRaises(frappe.ValidationError, pe.save)
 
@@ -228,9 +250,24 @@
 
 	def test_gl_entries_with_perpetual_inventory_against_pr(self):
 
-		pr = make_purchase_receipt(company="_Test Company with perpetual inventory", supplier_warehouse="Work In Progress - TCP1", warehouse= "Stores - TCP1", cost_center = "Main - TCP1", get_taxes_and_charges=True,)
+		pr = make_purchase_receipt(
+			company="_Test Company with perpetual inventory",
+			supplier_warehouse="Work In Progress - TCP1",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+			get_taxes_and_charges=True,
+		)
 
-		pi = make_purchase_invoice(company="_Test Company with perpetual inventory", supplier_warehouse="Work In Progress - TCP1", warehouse= "Stores - TCP1", cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1", get_taxes_and_charges=True, qty=10,do_not_save= "True")
+		pi = make_purchase_invoice(
+			company="_Test Company with perpetual inventory",
+			supplier_warehouse="Work In Progress - TCP1",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+			expense_account="_Test Account Cost for Goods Sold - TCP1",
+			get_taxes_and_charges=True,
+			qty=10,
+			do_not_save="True",
+		)
 
 		for d in pi.items:
 			d.purchase_receipt = pr.name
@@ -243,18 +280,25 @@
 		self.check_gle_for_pi(pi.name)
 
 	def check_gle_for_pi(self, pi):
-		gl_entries = frappe.db.sql("""select account, sum(debit) as debit, sum(credit) as credit
+		gl_entries = frappe.db.sql(
+			"""select account, sum(debit) as debit, sum(credit) as credit
 			from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
-			group by account""", pi, as_dict=1)
+			group by account""",
+			pi,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
-		expected_values = dict((d[0], d) for d in [
-			["Creditors - TCP1", 0, 720],
-			["Stock Received But Not Billed - TCP1", 500.0, 0],
-			["_Test Account Shipping Charges - TCP1", 100.0, 0.0],
-			["_Test Account VAT - TCP1", 120.0, 0]
-		])
+		expected_values = dict(
+			(d[0], d)
+			for d in [
+				["Creditors - TCP1", 0, 720],
+				["Stock Received But Not Billed - TCP1", 500.0, 0],
+				["_Test Account Shipping Charges - TCP1", 100.0, 0.0],
+				["_Test Account VAT - TCP1", 120.0, 0],
+			]
+		)
 
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_values[gle.account][0], gle.account)
@@ -266,8 +310,12 @@
 			make_purchase_invoice as create_purchase_invoice,
 		)
 
-		pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse='Stores - TCP1',
-			currency = "USD", conversion_rate = 70)
+		pr = make_purchase_receipt(
+			company="_Test Company with perpetual inventory",
+			warehouse="Stores - TCP1",
+			currency="USD",
+			conversion_rate=70,
+		)
 
 		pi = create_purchase_invoice(pr.name)
 		pi.conversion_rate = 80
@@ -276,54 +324,69 @@
 		pi.submit()
 
 		# Get exchnage gain and loss account
-		exchange_gain_loss_account = frappe.db.get_value('Company', pi.company, 'exchange_gain_loss_account')
+		exchange_gain_loss_account = frappe.db.get_value(
+			"Company", pi.company, "exchange_gain_loss_account"
+		)
 
 		# fetching the latest GL Entry with exchange gain and loss account account
-		amount = frappe.db.get_value('GL Entry', {'account': exchange_gain_loss_account, 'voucher_no': pi.name}, 'debit')
-		discrepancy_caused_by_exchange_rate_diff = abs(pi.items[0].base_net_amount - pr.items[0].base_net_amount)
+		amount = frappe.db.get_value(
+			"GL Entry", {"account": exchange_gain_loss_account, "voucher_no": pi.name}, "debit"
+		)
+		discrepancy_caused_by_exchange_rate_diff = abs(
+			pi.items[0].base_net_amount - pr.items[0].base_net_amount
+		)
 
 		self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount)
 
+	@change_settings("Buying Settings", {"enable_discount_accounting": 1})
 	def test_purchase_invoice_with_discount_accounting_enabled(self):
-		enable_discount_accounting()
 
-		discount_account = create_account(account_name="Discount Account",
-			parent_account="Indirect Expenses - _TC", company="_Test Company")
+		discount_account = create_account(
+			account_name="Discount Account",
+			parent_account="Indirect Expenses - _TC",
+			company="_Test Company",
+		)
 		pi = make_purchase_invoice(discount_account=discount_account, rate=45)
 
 		expected_gle = [
 			["_Test Account Cost for Goods Sold - _TC", 250.0, 0.0, nowdate()],
 			["Creditors - _TC", 0.0, 225.0, nowdate()],
-			["Discount Account - _TC", 0.0, 25.0, nowdate()]
+			["Discount Account - _TC", 0.0, 25.0, nowdate()],
 		]
 
 		check_gl_entries(self, pi.name, expected_gle, nowdate())
-		enable_discount_accounting(enable=0)
 
+	@change_settings("Buying Settings", {"enable_discount_accounting": 1})
 	def test_additional_discount_for_purchase_invoice_with_discount_accounting_enabled(self):
-		enable_discount_accounting()
-		additional_discount_account = create_account(account_name="Discount Account",
-			parent_account="Indirect Expenses - _TC", company="_Test Company")
+
+		additional_discount_account = create_account(
+			account_name="Discount Account",
+			parent_account="Indirect Expenses - _TC",
+			company="_Test Company",
+		)
 
 		pi = make_purchase_invoice(do_not_save=1, parent_cost_center="Main - _TC")
 		pi.apply_discount_on = "Grand Total"
 		pi.additional_discount_account = additional_discount_account
 		pi.additional_discount_percentage = 10
 		pi.disable_rounded_total = 1
-		pi.append("taxes", {
-			"charge_type": "On Net Total",
-			"account_head": "_Test Account VAT - _TC",
-			"cost_center": "Main - _TC",
-			"description": "Test",
-			"rate": 10
-		})
+		pi.append(
+			"taxes",
+			{
+				"charge_type": "On Net Total",
+				"account_head": "_Test Account VAT - _TC",
+				"cost_center": "Main - _TC",
+				"description": "Test",
+				"rate": 10,
+			},
+		)
 		pi.submit()
 
 		expected_gle = [
 			["_Test Account Cost for Goods Sold - _TC", 250.0, 0.0, nowdate()],
 			["_Test Account VAT - _TC", 25.0, 0.0, nowdate()],
 			["Creditors - _TC", 0.0, 247.5, nowdate()],
-			["Discount Account - _TC", 0.0, 27.5, nowdate()]
+			["Discount Account - _TC", 0.0, 27.5, nowdate()],
 		]
 
 		check_gl_entries(self, pi.name, expected_gle, nowdate())
@@ -331,7 +394,7 @@
 	def test_purchase_invoice_change_naming_series(self):
 		pi = frappe.copy_doc(test_records[1])
 		pi.insert()
-		pi.naming_series = 'TEST-'
+		pi.naming_series = "TEST-"
 
 		self.assertRaises(frappe.CannotChangeConstantError, pi.save)
 
@@ -340,25 +403,33 @@
 		pi.load_from_db()
 
 		self.assertTrue(pi.status, "Draft")
-		pi.naming_series = 'TEST-'
+		pi.naming_series = "TEST-"
 
 		self.assertRaises(frappe.CannotChangeConstantError, pi.save)
 
 	def test_gl_entries_for_non_stock_items_with_perpetual_inventory(self):
-		pi = make_purchase_invoice(item_code = "_Test Non Stock Item",
-			company = "_Test Company with perpetual inventory", warehouse= "Stores - TCP1",
-			cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1")
+		pi = make_purchase_invoice(
+			item_code="_Test Non Stock Item",
+			company="_Test Company with perpetual inventory",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+			expense_account="_Test Account Cost for Goods Sold - TCP1",
+		)
 
 		self.assertTrue(pi.status, "Unpaid")
 
-		gl_entries = frappe.db.sql("""select account, debit, credit
+		gl_entries = frappe.db.sql(
+			"""select account, debit, credit
 			from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
-			order by account asc""", pi.name, as_dict=1)
+			order by account asc""",
+			pi.name,
+			as_dict=1,
+		)
 		self.assertTrue(gl_entries)
 
 		expected_values = [
 			["_Test Account Cost for Goods Sold - TCP1", 250.0, 0],
-			["Creditors - TCP1", 0, 250]
+			["Creditors - TCP1", 0, 250],
 		]
 
 		for i, gle in enumerate(gl_entries):
@@ -373,7 +444,7 @@
 
 		expected_values = [
 			["_Test Item Home Desktop 100", 90, 59],
-			["_Test Item Home Desktop 200", 135, 177]
+			["_Test Item Home Desktop 200", 135, 177],
 		]
 		for i, item in enumerate(pi.get("items")):
 			self.assertEqual(item.item_code, expected_values[i][0])
@@ -405,10 +476,7 @@
 		wrapper.insert()
 		wrapper.load_from_db()
 
-		expected_values = [
-			["_Test FG Item", 90, 59],
-			["_Test Item Home Desktop 200", 135, 177]
-		]
+		expected_values = [["_Test FG Item", 90, 59], ["_Test Item Home Desktop 200", 135, 177]]
 		for i, item in enumerate(wrapper.get("items")):
 			self.assertEqual(item.item_code, expected_values[i][0])
 			self.assertEqual(item.item_tax_amount, expected_values[i][1])
@@ -445,14 +513,17 @@
 		pi = frappe.copy_doc(test_records[0])
 		pi.disable_rounded_total = 1
 		pi.allocate_advances_automatically = 0
-		pi.append("advances", {
-			"reference_type": "Journal Entry",
-			"reference_name": jv.name,
-			"reference_row": jv.get("accounts")[0].name,
-			"advance_amount": 400,
-			"allocated_amount": 300,
-			"remarks": jv.remark
-		})
+		pi.append(
+			"advances",
+			{
+				"reference_type": "Journal Entry",
+				"reference_name": jv.name,
+				"reference_row": jv.get("accounts")[0].name,
+				"advance_amount": 400,
+				"allocated_amount": 300,
+				"remarks": jv.remark,
+			},
+		)
 		pi.insert()
 
 		self.assertEqual(pi.outstanding_amount, 1212.30)
@@ -465,14 +536,24 @@
 		pi.submit()
 		pi.load_from_db()
 
-		self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
+		self.assertTrue(
+			frappe.db.sql(
+				"""select name from `tabJournal Entry Account`
 			where reference_type='Purchase Invoice'
-			and reference_name=%s and debit_in_account_currency=300""", pi.name))
+			and reference_name=%s and debit_in_account_currency=300""",
+				pi.name,
+			)
+		)
 
 		pi.cancel()
 
-		self.assertFalse(frappe.db.sql("""select name from `tabJournal Entry Account`
-			where reference_type='Purchase Invoice' and reference_name=%s""", pi.name))
+		self.assertFalse(
+			frappe.db.sql(
+				"""select name from `tabJournal Entry Account`
+			where reference_type='Purchase Invoice' and reference_name=%s""",
+				pi.name,
+			)
+		)
 
 	def test_invoice_with_advance_and_multi_payment_terms(self):
 		from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
@@ -486,20 +567,26 @@
 		pi = frappe.copy_doc(test_records[0])
 		pi.disable_rounded_total = 1
 		pi.allocate_advances_automatically = 0
-		pi.append("advances", {
-			"reference_type": "Journal Entry",
-			"reference_name": jv.name,
-			"reference_row": jv.get("accounts")[0].name,
-			"advance_amount": 400,
-			"allocated_amount": 300,
-			"remarks": jv.remark
-		})
+		pi.append(
+			"advances",
+			{
+				"reference_type": "Journal Entry",
+				"reference_name": jv.name,
+				"reference_row": jv.get("accounts")[0].name,
+				"advance_amount": 400,
+				"allocated_amount": 300,
+				"remarks": jv.remark,
+			},
+		)
 		pi.insert()
 
-		pi.update({
-			"payment_schedule": get_payment_terms("_Test Payment Term Template",
-				pi.posting_date, pi.grand_total, pi.base_grand_total)
-		})
+		pi.update(
+			{
+				"payment_schedule": get_payment_terms(
+					"_Test Payment Term Template", pi.posting_date, pi.grand_total, pi.base_grand_total
+				)
+			}
+		)
 
 		pi.save()
 		pi.submit()
@@ -513,7 +600,9 @@
 		self.assertTrue(
 			frappe.db.sql(
 				"select name from `tabJournal Entry Account` where reference_type='Purchase Invoice' and "
-				"reference_name=%s and debit_in_account_currency=300", pi.name)
+				"reference_name=%s and debit_in_account_currency=300",
+				pi.name,
+			)
 		)
 
 		self.assertEqual(pi.outstanding_amount, 1212.30)
@@ -523,49 +612,76 @@
 		self.assertFalse(
 			frappe.db.sql(
 				"select name from `tabJournal Entry Account` where reference_type='Purchase Invoice' and "
-				"reference_name=%s", pi.name)
+				"reference_name=%s",
+				pi.name,
+			)
 		)
 
 	def test_total_purchase_cost_for_project(self):
 		if not frappe.db.exists("Project", {"project_name": "_Test Project for Purchase"}):
-			project = make_project({'project_name':'_Test Project for Purchase'})
+			project = make_project({"project_name": "_Test Project for Purchase"})
 		else:
 			project = frappe.get_doc("Project", {"project_name": "_Test Project for Purchase"})
 
-		existing_purchase_cost = frappe.db.sql("""select sum(base_net_amount)
+		existing_purchase_cost = frappe.db.sql(
+			"""select sum(base_net_amount)
 			from `tabPurchase Invoice Item`
 			where project = '{0}'
-			and docstatus=1""".format(project.name))
+			and docstatus=1""".format(
+				project.name
+			)
+		)
 		existing_purchase_cost = existing_purchase_cost and existing_purchase_cost[0][0] or 0
 
 		pi = make_purchase_invoice(currency="USD", conversion_rate=60, project=project.name)
-		self.assertEqual(frappe.db.get_value("Project", project.name, "total_purchase_cost"),
-			existing_purchase_cost + 15000)
+		self.assertEqual(
+			frappe.db.get_value("Project", project.name, "total_purchase_cost"),
+			existing_purchase_cost + 15000,
+		)
 
 		pi1 = make_purchase_invoice(qty=10, project=project.name)
-		self.assertEqual(frappe.db.get_value("Project", project.name, "total_purchase_cost"),
-			existing_purchase_cost + 15500)
+		self.assertEqual(
+			frappe.db.get_value("Project", project.name, "total_purchase_cost"),
+			existing_purchase_cost + 15500,
+		)
 
 		pi1.cancel()
-		self.assertEqual(frappe.db.get_value("Project", project.name, "total_purchase_cost"),
-			existing_purchase_cost + 15000)
+		self.assertEqual(
+			frappe.db.get_value("Project", project.name, "total_purchase_cost"),
+			existing_purchase_cost + 15000,
+		)
 
 		pi.cancel()
-		self.assertEqual(frappe.db.get_value("Project", project.name, "total_purchase_cost"), existing_purchase_cost)
+		self.assertEqual(
+			frappe.db.get_value("Project", project.name, "total_purchase_cost"), existing_purchase_cost
+		)
 
 	def test_return_purchase_invoice_with_perpetual_inventory(self):
-		pi = make_purchase_invoice(company = "_Test Company with perpetual inventory", warehouse= "Stores - TCP1",
-			cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1")
+		pi = make_purchase_invoice(
+			company="_Test Company with perpetual inventory",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+			expense_account="_Test Account Cost for Goods Sold - TCP1",
+		)
 
-		return_pi = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2,
-			company = "_Test Company with perpetual inventory", warehouse= "Stores - TCP1",
-			cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1")
-
+		return_pi = make_purchase_invoice(
+			is_return=1,
+			return_against=pi.name,
+			qty=-2,
+			company="_Test Company with perpetual inventory",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+			expense_account="_Test Account Cost for Goods Sold - TCP1",
+		)
 
 		# check gl entries for return
-		gl_entries = frappe.db.sql("""select account, debit, credit
+		gl_entries = frappe.db.sql(
+			"""select account, debit, credit
 			from `tabGL Entry` where voucher_type=%s and voucher_no=%s
-			order by account desc""", ("Purchase Invoice", return_pi.name), as_dict=1)
+			order by account desc""",
+			("Purchase Invoice", return_pi.name),
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
@@ -578,14 +694,96 @@
 			self.assertEqual(expected_values[gle.account][0], gle.debit)
 			self.assertEqual(expected_values[gle.account][1], gle.credit)
 
-	def test_multi_currency_gle(self):
-		pi = make_purchase_invoice(supplier="_Test Supplier USD", credit_to="_Test Payable USD - _TC",
-			currency="USD", conversion_rate=50)
+	def test_standalone_return_using_pi(self):
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 
-		gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
+		item = self.make_item().name
+		company = "_Test Company with perpetual inventory"
+		warehouse = "Stores - TCP1"
+
+		make_stock_entry(item_code=item, target=warehouse, qty=50, rate=120)
+
+		return_pi = make_purchase_invoice(
+			is_return=1,
+			item=item,
+			qty=-10,
+			update_stock=1,
+			rate=100,
+			company=company,
+			warehouse=warehouse,
+			cost_center="Main - TCP1",
+		)
+
+		# assert that stock consumption is with actual rate
+		self.assertGLEs(
+			return_pi,
+			[{"credit": 1200, "debit": 0}],
+			gle_filters={"account": "Stock In Hand - TCP1"},
+		)
+
+		# assert loss booked in COGS
+		self.assertGLEs(
+			return_pi,
+			[{"credit": 0, "debit": 200}],
+			gle_filters={"account": "Cost of Goods Sold - TCP1"},
+		)
+
+	def test_return_with_lcv(self):
+		from erpnext.controllers.sales_and_purchase_return import make_return_doc
+		from erpnext.stock.doctype.landed_cost_voucher.test_landed_cost_voucher import (
+			create_landed_cost_voucher,
+		)
+
+		item = self.make_item().name
+		company = "_Test Company with perpetual inventory"
+		warehouse = "Stores - TCP1"
+		cost_center = "Main - TCP1"
+
+		pi = make_purchase_invoice(
+			item=item,
+			company=company,
+			warehouse=warehouse,
+			cost_center=cost_center,
+			update_stock=1,
+			qty=10,
+			rate=100,
+		)
+
+		# Create landed cost voucher - will increase valuation of received item by 10
+		create_landed_cost_voucher("Purchase Invoice", pi.name, pi.company, charges=100)
+		return_pi = make_return_doc(pi.doctype, pi.name)
+		return_pi.save().submit()
+
+		# assert that stock consumption is with actual in rate
+		self.assertGLEs(
+			return_pi,
+			[{"credit": 1100, "debit": 0}],
+			gle_filters={"account": "Stock In Hand - TCP1"},
+		)
+
+		# assert loss booked in COGS
+		self.assertGLEs(
+			return_pi,
+			[{"credit": 0, "debit": 100}],
+			gle_filters={"account": "Cost of Goods Sold - TCP1"},
+		)
+
+	def test_multi_currency_gle(self):
+		pi = make_purchase_invoice(
+			supplier="_Test Supplier USD",
+			credit_to="_Test Payable USD - _TC",
+			currency="USD",
+			conversion_rate=50,
+		)
+
+		gl_entries = frappe.db.sql(
+			"""select account, account_currency, debit, credit,
 			debit_in_account_currency, credit_in_account_currency
 			from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
-			order by account asc""", pi.name, as_dict=1)
+			order by account asc""",
+			pi.name,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
@@ -595,53 +793,74 @@
 				"debit": 0,
 				"debit_in_account_currency": 0,
 				"credit": 12500,
-				"credit_in_account_currency": 250
+				"credit_in_account_currency": 250,
 			},
 			"_Test Account Cost for Goods Sold - _TC": {
 				"account_currency": "INR",
 				"debit": 12500,
 				"debit_in_account_currency": 12500,
 				"credit": 0,
-				"credit_in_account_currency": 0
-			}
+				"credit_in_account_currency": 0,
+			},
 		}
 
-		for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
+		for field in (
+			"account_currency",
+			"debit",
+			"debit_in_account_currency",
+			"credit",
+			"credit_in_account_currency",
+		):
 			for i, gle in enumerate(gl_entries):
 				self.assertEqual(expected_values[gle.account][field], gle[field])
 
-
 		# Check for valid currency
-		pi1 = make_purchase_invoice(supplier="_Test Supplier USD", credit_to="_Test Payable USD - _TC",
-			do_not_save=True)
+		pi1 = make_purchase_invoice(
+			supplier="_Test Supplier USD", credit_to="_Test Payable USD - _TC", do_not_save=True
+		)
 
 		self.assertRaises(InvalidCurrency, pi1.save)
 
 		# cancel
 		pi.cancel()
 
-		gle = frappe.db.sql("""select name from `tabGL Entry`
-			where voucher_type='Sales Invoice' and voucher_no=%s""", pi.name)
+		gle = frappe.db.sql(
+			"""select name from `tabGL Entry`
+			where voucher_type='Sales Invoice' and voucher_no=%s""",
+			pi.name,
+		)
 
 		self.assertFalse(gle)
 
 	def test_purchase_invoice_update_stock_gl_entry_with_perpetual_inventory(self):
 
-		pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(),
-			posting_time=frappe.utils.nowtime(), cash_bank_account="Cash - TCP1", company="_Test Company with perpetual inventory", supplier_warehouse="Work In Progress - TCP1", warehouse= "Stores - TCP1", cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1")
+		pi = make_purchase_invoice(
+			update_stock=1,
+			posting_date=frappe.utils.nowdate(),
+			posting_time=frappe.utils.nowtime(),
+			cash_bank_account="Cash - TCP1",
+			company="_Test Company with perpetual inventory",
+			supplier_warehouse="Work In Progress - TCP1",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+			expense_account="_Test Account Cost for Goods Sold - TCP1",
+		)
 
-		gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
+		gl_entries = frappe.db.sql(
+			"""select account, account_currency, debit, credit,
 			debit_in_account_currency, credit_in_account_currency
 			from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
-			order by account asc""", pi.name, as_dict=1)
+			order by account asc""",
+			pi.name,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 		stock_in_hand_account = get_inventory_account(pi.company, pi.get("items")[0].warehouse)
 
-		expected_gl_entries = dict((d[0], d) for d in [
-			[pi.credit_to, 0.0, 250.0],
-			[stock_in_hand_account, 250.0, 0.0]
-		])
+		expected_gl_entries = dict(
+			(d[0], d) for d in [[pi.credit_to, 0.0, 250.0], [stock_in_hand_account, 250.0, 0.0]]
+		)
 
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_gl_entries[gle.account][0], gle.account)
@@ -650,22 +869,39 @@
 
 	def test_purchase_invoice_for_is_paid_and_update_stock_gl_entry_with_perpetual_inventory(self):
 
-		pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(),
-			posting_time=frappe.utils.nowtime(), cash_bank_account="Cash - TCP1", is_paid=1, company="_Test Company with perpetual inventory", supplier_warehouse="Work In Progress - TCP1", warehouse= "Stores - TCP1", cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1")
+		pi = make_purchase_invoice(
+			update_stock=1,
+			posting_date=frappe.utils.nowdate(),
+			posting_time=frappe.utils.nowtime(),
+			cash_bank_account="Cash - TCP1",
+			is_paid=1,
+			company="_Test Company with perpetual inventory",
+			supplier_warehouse="Work In Progress - TCP1",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+			expense_account="_Test Account Cost for Goods Sold - TCP1",
+		)
 
-		gl_entries = frappe.db.sql("""select account, account_currency, sum(debit) as debit,
+		gl_entries = frappe.db.sql(
+			"""select account, account_currency, sum(debit) as debit,
 				sum(credit) as credit, debit_in_account_currency, credit_in_account_currency
 			from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
-			group by account, voucher_no order by account asc;""", pi.name, as_dict=1)
+			group by account, voucher_no order by account asc;""",
+			pi.name,
+			as_dict=1,
+		)
 
 		stock_in_hand_account = get_inventory_account(pi.company, pi.get("items")[0].warehouse)
 		self.assertTrue(gl_entries)
 
-		expected_gl_entries = dict((d[0], d) for d in [
-			[pi.credit_to, 250.0, 250.0],
-			[stock_in_hand_account, 250.0, 0.0],
-			["Cash - TCP1", 0.0, 250.0]
-		])
+		expected_gl_entries = dict(
+			(d[0], d)
+			for d in [
+				[pi.credit_to, 250.0, 250.0],
+				[stock_in_hand_account, 250.0, 0.0],
+				["Cash - TCP1", 0.0, 250.0],
+			]
+		)
 
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_gl_entries[gle.account][0], gle.account)
@@ -673,31 +909,36 @@
 			self.assertEqual(expected_gl_entries[gle.account][2], gle.credit)
 
 	def test_auto_batch(self):
-		item_code = frappe.db.get_value('Item',
-			{'has_batch_no': 1, 'create_new_batch':1}, 'name')
+		item_code = frappe.db.get_value("Item", {"has_batch_no": 1, "create_new_batch": 1}, "name")
 
 		if not item_code:
-			doc = frappe.get_doc({
-				'doctype': 'Item',
-				'is_stock_item': 1,
-				'item_code': 'test batch item',
-				'item_group': 'Products',
-				'has_batch_no': 1,
-				'create_new_batch': 1
-			}).insert(ignore_permissions=True)
+			doc = frappe.get_doc(
+				{
+					"doctype": "Item",
+					"is_stock_item": 1,
+					"item_code": "test batch item",
+					"item_group": "Products",
+					"has_batch_no": 1,
+					"create_new_batch": 1,
+				}
+			).insert(ignore_permissions=True)
 			item_code = doc.name
 
-		pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(),
-			posting_time=frappe.utils.nowtime(), item_code=item_code)
+		pi = make_purchase_invoice(
+			update_stock=1,
+			posting_date=frappe.utils.nowdate(),
+			posting_time=frappe.utils.nowtime(),
+			item_code=item_code,
+		)
 
-		self.assertTrue(frappe.db.get_value('Batch',
-			{'item': item_code, 'reference_name': pi.name}))
+		self.assertTrue(frappe.db.get_value("Batch", {"item": item_code, "reference_name": pi.name}))
 
 	def test_update_stock_and_purchase_return(self):
 		actual_qty_0 = get_qty_after_transaction()
 
-		pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(),
-			posting_time=frappe.utils.nowtime())
+		pi = make_purchase_invoice(
+			update_stock=1, posting_date=frappe.utils.nowdate(), posting_time=frappe.utils.nowtime()
+		)
 
 		actual_qty_1 = get_qty_after_transaction()
 		self.assertEqual(actual_qty_0 + 5, actual_qty_1)
@@ -724,13 +965,20 @@
 		from erpnext.buying.doctype.purchase_order.test_purchase_order import update_backflush_based_on
 		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 
-		update_backflush_based_on('BOM')
-		make_stock_entry(item_code="_Test Item", target="_Test Warehouse 1 - _TC", qty=100, basic_rate=100)
-		make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse 1 - _TC",
-			qty=100, basic_rate=100)
+		update_backflush_based_on("BOM")
+		make_stock_entry(
+			item_code="_Test Item", target="_Test Warehouse 1 - _TC", qty=100, basic_rate=100
+		)
+		make_stock_entry(
+			item_code="_Test Item Home Desktop 100",
+			target="_Test Warehouse 1 - _TC",
+			qty=100,
+			basic_rate=100,
+		)
 
-		pi = make_purchase_invoice(item_code="_Test FG Item", qty=10, rate=500,
-			update_stock=1, is_subcontracted="Yes")
+		pi = make_purchase_invoice(
+			item_code="_Test FG Item", qty=10, rate=500, update_stock=1, is_subcontracted=1
+		)
 
 		self.assertEqual(len(pi.get("supplied_items")), 2)
 
@@ -738,15 +986,26 @@
 		self.assertEqual(flt(pi.get("items")[0].rm_supp_cost, 2), flt(rm_supp_cost, 2))
 
 	def test_rejected_serial_no(self):
-		pi = make_purchase_invoice(item_code="_Test Serialized Item With Series", received_qty=2, qty=1,
-			rejected_qty=1, rate=500, update_stock=1, rejected_warehouse = "_Test Rejected Warehouse - _TC",
-			allow_zero_valuation_rate=1)
+		pi = make_purchase_invoice(
+			item_code="_Test Serialized Item With Series",
+			received_qty=2,
+			qty=1,
+			rejected_qty=1,
+			rate=500,
+			update_stock=1,
+			rejected_warehouse="_Test Rejected Warehouse - _TC",
+			allow_zero_valuation_rate=1,
+		)
 
-		self.assertEqual(frappe.db.get_value("Serial No", pi.get("items")[0].serial_no, "warehouse"),
-			pi.get("items")[0].warehouse)
+		self.assertEqual(
+			frappe.db.get_value("Serial No", pi.get("items")[0].serial_no, "warehouse"),
+			pi.get("items")[0].warehouse,
+		)
 
-		self.assertEqual(frappe.db.get_value("Serial No", pi.get("items")[0].rejected_serial_no,
-			"warehouse"), pi.get("items")[0].rejected_warehouse)
+		self.assertEqual(
+			frappe.db.get_value("Serial No", pi.get("items")[0].rejected_serial_no, "warehouse"),
+			pi.get("items")[0].rejected_warehouse,
+		)
 
 	def test_outstanding_amount_after_advance_jv_cancelation(self):
 		from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
@@ -754,85 +1013,95 @@
 		)
 
 		jv = frappe.copy_doc(jv_test_records[1])
-		jv.accounts[0].is_advance = 'Yes'
+		jv.accounts[0].is_advance = "Yes"
 		jv.insert()
 		jv.submit()
 
 		pi = frappe.copy_doc(test_records[0])
-		pi.append("advances", {
-			"reference_type": "Journal Entry",
-			"reference_name": jv.name,
-			"reference_row": jv.get("accounts")[0].name,
-			"advance_amount": 400,
-			"allocated_amount": 300,
-			"remarks": jv.remark
-		})
+		pi.append(
+			"advances",
+			{
+				"reference_type": "Journal Entry",
+				"reference_name": jv.name,
+				"reference_row": jv.get("accounts")[0].name,
+				"advance_amount": 400,
+				"allocated_amount": 300,
+				"remarks": jv.remark,
+			},
+		)
 		pi.insert()
 		pi.submit()
 		pi.load_from_db()
 
-		#check outstanding after advance allocation
+		# check outstanding after advance allocation
 		self.assertEqual(flt(pi.outstanding_amount), flt(pi.rounded_total - pi.total_advance))
 
-		#added to avoid Document has been modified exception
+		# added to avoid Document has been modified exception
 		jv = frappe.get_doc("Journal Entry", jv.name)
 		jv.cancel()
 
 		pi.load_from_db()
-		#check outstanding after advance cancellation
+		# check outstanding after advance cancellation
 		self.assertEqual(flt(pi.outstanding_amount), flt(pi.rounded_total + pi.total_advance))
 
 	def test_outstanding_amount_after_advance_payment_entry_cancelation(self):
-		pe = frappe.get_doc({
-			"doctype": "Payment Entry",
-			"payment_type": "Pay",
-			"party_type": "Supplier",
-			"party": "_Test Supplier",
-			"company": "_Test Company",
-			"paid_from_account_currency": "INR",
-			"paid_to_account_currency": "INR",
-			"source_exchange_rate": 1,
-			"target_exchange_rate": 1,
-			"reference_no": "1",
-			"reference_date": nowdate(),
-			"received_amount": 300,
-			"paid_amount": 300,
-			"paid_from": "_Test Cash - _TC",
-			"paid_to": "_Test Payable - _TC"
-		})
+		pe = frappe.get_doc(
+			{
+				"doctype": "Payment Entry",
+				"payment_type": "Pay",
+				"party_type": "Supplier",
+				"party": "_Test Supplier",
+				"company": "_Test Company",
+				"paid_from_account_currency": "INR",
+				"paid_to_account_currency": "INR",
+				"source_exchange_rate": 1,
+				"target_exchange_rate": 1,
+				"reference_no": "1",
+				"reference_date": nowdate(),
+				"received_amount": 300,
+				"paid_amount": 300,
+				"paid_from": "_Test Cash - _TC",
+				"paid_to": "_Test Payable - _TC",
+			}
+		)
 		pe.insert()
 		pe.submit()
 
 		pi = frappe.copy_doc(test_records[0])
 		pi.is_pos = 0
-		pi.append("advances", {
-			"doctype": "Purchase Invoice Advance",
-			"reference_type": "Payment Entry",
-			"reference_name": pe.name,
-			"advance_amount": 300,
-			"allocated_amount": 300,
-			"remarks": pe.remarks
-		})
+		pi.append(
+			"advances",
+			{
+				"doctype": "Purchase Invoice Advance",
+				"reference_type": "Payment Entry",
+				"reference_name": pe.name,
+				"advance_amount": 300,
+				"allocated_amount": 300,
+				"remarks": pe.remarks,
+			},
+		)
 		pi.insert()
 		pi.submit()
 
 		pi.load_from_db()
 
-		#check outstanding after advance allocation
+		# check outstanding after advance allocation
 		self.assertEqual(flt(pi.outstanding_amount), flt(pi.rounded_total - pi.total_advance))
 
-		#added to avoid Document has been modified exception
+		# added to avoid Document has been modified exception
 		pe = frappe.get_doc("Payment Entry", pe.name)
 		pe.cancel()
 
 		pi.load_from_db()
-		#check outstanding after advance cancellation
+		# check outstanding after advance cancellation
 		self.assertEqual(flt(pi.outstanding_amount), flt(pi.rounded_total + pi.total_advance))
 
 	def test_purchase_invoice_with_shipping_rule(self):
 		from erpnext.accounts.doctype.shipping_rule.test_shipping_rule import create_shipping_rule
 
-		shipping_rule = create_shipping_rule(shipping_rule_type = "Buying", shipping_rule_name = "Shipping Rule - Purchase Invoice Test")
+		shipping_rule = create_shipping_rule(
+			shipping_rule_type="Buying", shipping_rule_name="Shipping Rule - Purchase Invoice Test"
+		)
 
 		pi = frappe.copy_doc(test_records[0])
 
@@ -848,16 +1117,20 @@
 	def test_make_pi_without_terms(self):
 		pi = make_purchase_invoice(do_not_save=1)
 
-		self.assertFalse(pi.get('payment_schedule'))
+		self.assertFalse(pi.get("payment_schedule"))
 
 		pi.insert()
 
-		self.assertTrue(pi.get('payment_schedule'))
+		self.assertTrue(pi.get("payment_schedule"))
 
 	def test_duplicate_due_date_in_terms(self):
 		pi = make_purchase_invoice(do_not_save=1)
-		pi.append('payment_schedule', dict(due_date='2017-01-01', invoice_portion=50.00, payment_amount=50))
-		pi.append('payment_schedule', dict(due_date='2017-01-01', invoice_portion=50.00, payment_amount=50))
+		pi.append(
+			"payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50)
+		)
+		pi.append(
+			"payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50)
+		)
 
 		self.assertRaises(frappe.ValidationError, pi.insert)
 
@@ -865,12 +1138,13 @@
 		from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
 		from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import get_outstanding_amount
 
-		pi = make_purchase_invoice(item_code = "_Test Item", qty = (5 * -1), rate=500, is_return = 1)
+		pi = make_purchase_invoice(item_code="_Test Item", qty=(5 * -1), rate=500, is_return=1)
 		pi.load_from_db()
 		self.assertTrue(pi.status, "Return")
 
-		outstanding_amount = get_outstanding_amount(pi.doctype,
-			pi.name, "Creditors - _TC", pi.supplier, "Supplier")
+		outstanding_amount = get_outstanding_amount(
+			pi.doctype, pi.name, "Creditors - _TC", pi.supplier, "Supplier"
+		)
 
 		self.assertEqual(pi.outstanding_amount, outstanding_amount)
 
@@ -885,30 +1159,33 @@
 		pe.insert()
 		pe.submit()
 
-		pi_doc = frappe.get_doc('Purchase Invoice', pi.name)
+		pi_doc = frappe.get_doc("Purchase Invoice", pi.name)
 		self.assertEqual(pi_doc.outstanding_amount, 0)
 
 	def test_purchase_invoice_with_cost_center(self):
 		from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
+
 		cost_center = "_Test Cost Center for BS Account - _TC"
 		create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
 
-		pi =  make_purchase_invoice_against_cost_center(cost_center=cost_center, credit_to="Creditors - _TC")
+		pi = make_purchase_invoice_against_cost_center(
+			cost_center=cost_center, credit_to="Creditors - _TC"
+		)
 		self.assertEqual(pi.cost_center, cost_center)
 
 		expected_values = {
-			"Creditors - _TC": {
-				"cost_center": cost_center
-			},
-			"_Test Account Cost for Goods Sold - _TC": {
-				"cost_center": cost_center
-			}
+			"Creditors - _TC": {"cost_center": cost_center},
+			"_Test Account Cost for Goods Sold - _TC": {"cost_center": cost_center},
 		}
 
-		gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit,
+		gl_entries = frappe.db.sql(
+			"""select account, cost_center, account_currency, debit, credit,
 			debit_in_account_currency, credit_in_account_currency
 			from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
-			order by account asc""", pi.name, as_dict=1)
+			order by account asc""",
+			pi.name,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
@@ -917,21 +1194,21 @@
 
 	def test_purchase_invoice_without_cost_center(self):
 		cost_center = "_Test Cost Center - _TC"
-		pi =  make_purchase_invoice(credit_to="Creditors - _TC")
+		pi = make_purchase_invoice(credit_to="Creditors - _TC")
 
 		expected_values = {
-			"Creditors - _TC": {
-				"cost_center": None
-			},
-			"_Test Account Cost for Goods Sold - _TC": {
-				"cost_center": cost_center
-			}
+			"Creditors - _TC": {"cost_center": None},
+			"_Test Account Cost for Goods Sold - _TC": {"cost_center": cost_center},
 		}
 
-		gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit,
+		gl_entries = frappe.db.sql(
+			"""select account, cost_center, account_currency, debit, credit,
 			debit_in_account_currency, credit_in_account_currency
 			from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
-			order by account asc""", pi.name, as_dict=1)
+			order by account asc""",
+			pi.name,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
@@ -939,36 +1216,40 @@
 			self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
 
 	def test_purchase_invoice_with_project_link(self):
-		project = make_project({
-			'project_name': 'Purchase Invoice Project',
-			'project_template_name': 'Test Project Template',
-			'start_date': '2020-01-01'
-		})
-		item_project = make_project({
-			'project_name': 'Purchase Invoice Item Project',
-			'project_template_name': 'Test Project Template',
-			'start_date': '2019-06-01'
-		})
+		project = make_project(
+			{
+				"project_name": "Purchase Invoice Project",
+				"project_template_name": "Test Project Template",
+				"start_date": "2020-01-01",
+			}
+		)
+		item_project = make_project(
+			{
+				"project_name": "Purchase Invoice Item Project",
+				"project_template_name": "Test Project Template",
+				"start_date": "2019-06-01",
+			}
+		)
 
-		pi = make_purchase_invoice(credit_to="Creditors - _TC" ,do_not_save=1)
+		pi = make_purchase_invoice(credit_to="Creditors - _TC", do_not_save=1)
 		pi.items[0].project = item_project.name
 		pi.project = project.name
 
 		pi.submit()
 
 		expected_values = {
-			"Creditors - _TC": {
-				"project": project.name
-			},
-			"_Test Account Cost for Goods Sold - _TC": {
-				"project": item_project.name
-			}
+			"Creditors - _TC": {"project": project.name},
+			"_Test Account Cost for Goods Sold - _TC": {"project": item_project.name},
 		}
 
-		gl_entries = frappe.db.sql("""select account, cost_center, project, account_currency, debit, credit,
+		gl_entries = frappe.db.sql(
+			"""select account, cost_center, project, account_currency, debit, credit,
 			debit_in_account_currency, credit_in_account_currency
 			from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
-			order by account asc""", pi.name, as_dict=1)
+			order by account asc""",
+			pi.name,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
@@ -976,10 +1257,11 @@
 			self.assertEqual(expected_values[gle.account]["project"], gle.project)
 
 	def test_deferred_expense_via_journal_entry(self):
-		deferred_account = create_account(account_name="Deferred Expense",
-			parent_account="Current Assets - _TC", company="_Test Company")
+		deferred_account = create_account(
+			account_name="Deferred Expense", parent_account="Current Assets - _TC", company="_Test Company"
+		)
 
-		acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
+		acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
 		acc_settings.book_deferred_entries_via_journal_entry = 1
 		acc_settings.submit_journal_entries = 1
 		acc_settings.save()
@@ -991,7 +1273,7 @@
 
 		pi = make_purchase_invoice(item=item.name, qty=1, rate=100, do_not_save=True)
 		pi.set_posting_time = 1
-		pi.posting_date = '2019-01-10'
+		pi.posting_date = "2019-01-10"
 		pi.items[0].enable_deferred_expense = 1
 		pi.items[0].service_start_date = "2019-01-10"
 		pi.items[0].service_end_date = "2019-03-15"
@@ -999,14 +1281,16 @@
 		pi.save()
 		pi.submit()
 
-		pda1 = frappe.get_doc(dict(
-			doctype='Process Deferred Accounting',
-			posting_date=nowdate(),
-			start_date="2019-01-01",
-			end_date="2019-03-31",
-			type="Expense",
-			company="_Test Company"
-		))
+		pda1 = frappe.get_doc(
+			dict(
+				doctype="Process Deferred Accounting",
+				posting_date=nowdate(),
+				start_date="2019-01-01",
+				end_date="2019-03-31",
+				type="Expense",
+				company="_Test Company",
+			)
+		)
 
 		pda1.insert()
 		pda1.submit()
@@ -1017,13 +1301,17 @@
 			["_Test Account Cost for Goods Sold - _TC", 0.0, 43.08, "2019-02-28"],
 			[deferred_account, 43.08, 0.0, "2019-02-28"],
 			["_Test Account Cost for Goods Sold - _TC", 0.0, 23.07, "2019-03-15"],
-			[deferred_account, 23.07, 0.0, "2019-03-15"]
+			[deferred_account, 23.07, 0.0, "2019-03-15"],
 		]
 
-		gl_entries = gl_entries = frappe.db.sql("""select account, debit, credit, posting_date
+		gl_entries = gl_entries = frappe.db.sql(
+			"""select account, debit, credit, posting_date
 			from `tabGL Entry`
 			where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s
-			order by posting_date asc, account asc""", (pi.items[0].name, pi.posting_date), as_dict=1)
+			order by posting_date asc, account asc""",
+			(pi.items[0].name, pi.posting_date),
+			as_dict=1,
+		)
 
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_gle[i][0], gle.account)
@@ -1031,108 +1319,139 @@
 			self.assertEqual(expected_gle[i][2], gle.debit)
 			self.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
 
-		acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
+		acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
 		acc_settings.book_deferred_entries_via_journal_entry = 0
 		acc_settings.submit_journal_entriessubmit_journal_entries = 0
 		acc_settings.save()
 
 	def test_gain_loss_with_advance_entry(self):
 		unlink_enabled = frappe.db.get_value(
-			"Accounts Settings", "Accounts Settings",
-			"unlink_payment_on_cancel_of_invoice")
+			"Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice"
+		)
 
 		frappe.db.set_value(
-			"Accounts Settings", "Accounts Settings",
-			"unlink_payment_on_cancel_of_invoice", 1)
+			"Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice", 1
+		)
 
 		original_account = frappe.db.get_value("Company", "_Test Company", "exchange_gain_loss_account")
-		frappe.db.set_value("Company", "_Test Company", "exchange_gain_loss_account", "Exchange Gain/Loss - _TC")
+		frappe.db.set_value(
+			"Company", "_Test Company", "exchange_gain_loss_account", "Exchange Gain/Loss - _TC"
+		)
 
-		pay = frappe.get_doc({
-			'doctype': 'Payment Entry',
-			'company': '_Test Company',
-			'payment_type': 'Pay',
-			'party_type': 'Supplier',
-			'party': '_Test Supplier USD',
-			'paid_to': '_Test Payable USD - _TC',
-			'paid_from': 'Cash - _TC',
-			'paid_amount': 70000,
-			'target_exchange_rate': 70,
-			'received_amount': 1000,
-		})
+		pay = frappe.get_doc(
+			{
+				"doctype": "Payment Entry",
+				"company": "_Test Company",
+				"payment_type": "Pay",
+				"party_type": "Supplier",
+				"party": "_Test Supplier USD",
+				"paid_to": "_Test Payable USD - _TC",
+				"paid_from": "Cash - _TC",
+				"paid_amount": 70000,
+				"target_exchange_rate": 70,
+				"received_amount": 1000,
+			}
+		)
 		pay.insert()
 		pay.submit()
 
-		pi = make_purchase_invoice(supplier='_Test Supplier USD', currency="USD",
-			conversion_rate=75, rate=500, do_not_save=1, qty=1)
+		pi = make_purchase_invoice(
+			supplier="_Test Supplier USD",
+			currency="USD",
+			conversion_rate=75,
+			rate=500,
+			do_not_save=1,
+			qty=1,
+		)
 		pi.cost_center = "_Test Cost Center - _TC"
 		pi.advances = []
-		pi.append("advances", {
-			"reference_type": "Payment Entry",
-			"reference_name": pay.name,
-			"advance_amount": 1000,
-			"remarks": pay.remarks,
-			"allocated_amount": 500,
-			"ref_exchange_rate": 70
-		})
+		pi.append(
+			"advances",
+			{
+				"reference_type": "Payment Entry",
+				"reference_name": pay.name,
+				"advance_amount": 1000,
+				"remarks": pay.remarks,
+				"allocated_amount": 500,
+				"ref_exchange_rate": 70,
+			},
+		)
 		pi.save()
 		pi.submit()
 
 		expected_gle = [
 			["_Test Account Cost for Goods Sold - _TC", 37500.0],
 			["_Test Payable USD - _TC", -35000.0],
-			["Exchange Gain/Loss - _TC", -2500.0]
+			["Exchange Gain/Loss - _TC", -2500.0],
 		]
 
-		gl_entries = frappe.db.sql("""
+		gl_entries = frappe.db.sql(
+			"""
 			select account, sum(debit - credit) as balance from `tabGL Entry`
 			where voucher_no=%s
 			group by account
-			order by account asc""", (pi.name), as_dict=1)
+			order by account asc""",
+			(pi.name),
+			as_dict=1,
+		)
 
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_gle[i][0], gle.account)
 			self.assertEqual(expected_gle[i][1], gle.balance)
 
-		pi_2 = make_purchase_invoice(supplier='_Test Supplier USD', currency="USD",
-			conversion_rate=73, rate=500, do_not_save=1, qty=1)
+		pi_2 = make_purchase_invoice(
+			supplier="_Test Supplier USD",
+			currency="USD",
+			conversion_rate=73,
+			rate=500,
+			do_not_save=1,
+			qty=1,
+		)
 		pi_2.cost_center = "_Test Cost Center - _TC"
 		pi_2.advances = []
-		pi_2.append("advances", {
-			"reference_type": "Payment Entry",
-			"reference_name": pay.name,
-			"advance_amount": 500,
-			"remarks": pay.remarks,
-			"allocated_amount": 500,
-			"ref_exchange_rate": 70
-		})
+		pi_2.append(
+			"advances",
+			{
+				"reference_type": "Payment Entry",
+				"reference_name": pay.name,
+				"advance_amount": 500,
+				"remarks": pay.remarks,
+				"allocated_amount": 500,
+				"ref_exchange_rate": 70,
+			},
+		)
 		pi_2.save()
 		pi_2.submit()
 
 		expected_gle = [
 			["_Test Account Cost for Goods Sold - _TC", 36500.0],
 			["_Test Payable USD - _TC", -35000.0],
-			["Exchange Gain/Loss - _TC", -1500.0]
+			["Exchange Gain/Loss - _TC", -1500.0],
 		]
 
-		gl_entries = frappe.db.sql("""
+		gl_entries = frappe.db.sql(
+			"""
 			select account, sum(debit - credit) as balance from `tabGL Entry`
 			where voucher_no=%s
-			group by account order by account asc""", (pi_2.name), as_dict=1)
+			group by account order by account asc""",
+			(pi_2.name),
+			as_dict=1,
+		)
 
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_gle[i][0], gle.account)
 			self.assertEqual(expected_gle[i][1], gle.balance)
 
-		expected_gle = [
-			["_Test Payable USD - _TC", 70000.0],
-			["Cash - _TC", -70000.0]
-		]
+		expected_gle = [["_Test Payable USD - _TC", 70000.0], ["Cash - _TC", -70000.0]]
 
-		gl_entries = frappe.db.sql("""
+		gl_entries = frappe.db.sql(
+			"""
 			select account, sum(debit - credit) as balance from `tabGL Entry`
 			where voucher_no=%s and is_cancelled=0
-			group by account order by account asc""", (pay.name), as_dict=1)
+			group by account order by account asc""",
+			(pay.name),
+			as_dict=1,
+		)
 
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_gle[i][0], gle.account)
@@ -1147,44 +1466,57 @@
 		pay.reload()
 		pay.cancel()
 
-		frappe.db.set_value("Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice", unlink_enabled)
+		frappe.db.set_value(
+			"Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice", unlink_enabled
+		)
 		frappe.db.set_value("Company", "_Test Company", "exchange_gain_loss_account", original_account)
 
 	def test_purchase_invoice_advance_taxes(self):
 		from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
 
 		# create a new supplier to test
-		supplier = create_supplier(supplier_name = '_Test TDS Advance Supplier',
-			tax_withholding_category = 'TDS - 194 - Dividends - Individual')
+		supplier = create_supplier(
+			supplier_name="_Test TDS Advance Supplier",
+			tax_withholding_category="TDS - 194 - Dividends - Individual",
+		)
 
 		# Update tax withholding category with current fiscal year and rate details
-		update_tax_witholding_category('_Test Company', 'TDS Payable - _TC')
+		update_tax_witholding_category("_Test Company", "TDS Payable - _TC")
 
 		# Create Purchase Order with TDS applied
-		po = create_purchase_order(do_not_save=1, supplier=supplier.name, rate=3000, item='_Test Non Stock Item',
-			posting_date='2021-09-15')
+		po = create_purchase_order(
+			do_not_save=1,
+			supplier=supplier.name,
+			rate=3000,
+			item="_Test Non Stock Item",
+			posting_date="2021-09-15",
+		)
 		po.save()
 		po.submit()
 
 		# Create Payment Entry Against the order
-		payment_entry = get_payment_entry(dt='Purchase Order', dn=po.name)
-		payment_entry.paid_from = 'Cash - _TC'
+		payment_entry = get_payment_entry(dt="Purchase Order", dn=po.name)
+		payment_entry.paid_from = "Cash - _TC"
 		payment_entry.apply_tax_withholding_amount = 1
-		payment_entry.tax_withholding_category = 'TDS - 194 - Dividends - Individual'
+		payment_entry.tax_withholding_category = "TDS - 194 - Dividends - Individual"
 		payment_entry.save()
 		payment_entry.submit()
 
 		# Check GLE for Payment Entry
 		expected_gle = [
-			['Cash - _TC', 0, 27000],
-			['Creditors - _TC', 30000, 0],
-			['TDS Payable - _TC', 0, 3000],
+			["Cash - _TC", 0, 27000],
+			["Creditors - _TC", 30000, 0],
+			["TDS Payable - _TC", 0, 3000],
 		]
 
-		gl_entries = frappe.db.sql("""select account, debit, credit
+		gl_entries = frappe.db.sql(
+			"""select account, debit, credit
 			from `tabGL Entry`
 			where voucher_type='Payment Entry' and voucher_no=%s
-			order by account asc""", (payment_entry.name), as_dict=1)
+			order by account asc""",
+			(payment_entry.name),
+			as_dict=1,
+		)
 
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_gle[i][0], gle.account)
@@ -1194,23 +1526,24 @@
 		# Create Purchase Invoice against Purchase Order
 		purchase_invoice = get_mapped_purchase_invoice(po.name)
 		purchase_invoice.allocate_advances_automatically = 1
-		purchase_invoice.items[0].item_code = '_Test Non Stock Item'
-		purchase_invoice.items[0].expense_account = '_Test Account Cost for Goods Sold - _TC'
+		purchase_invoice.items[0].item_code = "_Test Non Stock Item"
+		purchase_invoice.items[0].expense_account = "_Test Account Cost for Goods Sold - _TC"
 		purchase_invoice.save()
 		purchase_invoice.submit()
 
 		# Check GLE for Purchase Invoice
 		# Zero net effect on final TDS Payable on invoice
-		expected_gle = [
-			['_Test Account Cost for Goods Sold - _TC', 30000],
-			['Creditors - _TC', -30000]
-		]
+		expected_gle = [["_Test Account Cost for Goods Sold - _TC", 30000], ["Creditors - _TC", -30000]]
 
-		gl_entries = frappe.db.sql("""select account, sum(debit - credit) as amount
+		gl_entries = frappe.db.sql(
+			"""select account, sum(debit - credit) as amount
 			from `tabGL Entry`
 			where voucher_type='Purchase Invoice' and voucher_no=%s
 			group by account
-			order by account asc""", (purchase_invoice.name), as_dict=1)
+			order by account asc""",
+			(purchase_invoice.name),
+			as_dict=1,
+		)
 
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_gle[i][0], gle.account)
@@ -1225,28 +1558,36 @@
 		self.assertEqual(payment_entry.taxes[0].allocated_amount, 0)
 
 	def test_provisional_accounting_entry(self):
-		item = create_item("_Test Non Stock Item", is_stock_item=0)
-		provisional_account = create_account(account_name="Provision Account",
-			parent_account="Current Liabilities - _TC", company="_Test Company")
+		create_item("_Test Non Stock Item", is_stock_item=0)
 
-		company = frappe.get_doc('Company', '_Test Company')
+		provisional_account = create_account(
+			account_name="Provision Account",
+			parent_account="Current Liabilities - _TC",
+			company="_Test Company",
+		)
+
+		company = frappe.get_doc("Company", "_Test Company")
 		company.enable_provisional_accounting_for_non_stock_items = 1
 		company.default_provisional_account = provisional_account
 		company.save()
 
-		pr = make_purchase_receipt(item_code="_Test Non Stock Item", posting_date=add_days(nowdate(), -2))
+		pr = make_purchase_receipt(
+			item_code="_Test Non Stock Item", posting_date=add_days(nowdate(), -2)
+		)
 
 		pi = create_purchase_invoice_from_receipt(pr.name)
 		pi.set_posting_time = 1
 		pi.posting_date = add_days(pr.posting_date, -1)
-		pi.items[0].expense_account = 'Cost of Goods Sold - _TC'
+		pi.items[0].expense_account = "Cost of Goods Sold - _TC"
 		pi.save()
 		pi.submit()
 
+		self.assertEquals(pr.items[0].provisional_expense_account, "Provision Account - _TC")
+
 		# Check GLE for Purchase Invoice
 		expected_gle = [
-			['Cost of Goods Sold - _TC', 250, 0, add_days(pr.posting_date, -1)],
-			['Creditors - _TC', 0, 250, add_days(pr.posting_date, -1)]
+			["Cost of Goods Sold - _TC", 250, 0, add_days(pr.posting_date, -1)],
+			["Creditors - _TC", 0, 250, add_days(pr.posting_date, -1)],
 		]
 
 		check_gl_entries(self, pi.name, expected_gle, pi.posting_date)
@@ -1255,19 +1596,56 @@
 			["Provision Account - _TC", 250, 0, pr.posting_date],
 			["_Test Account Cost for Goods Sold - _TC", 0, 250, pr.posting_date],
 			["Provision Account - _TC", 0, 250, pi.posting_date],
-			["_Test Account Cost for Goods Sold - _TC", 250, 0, pi.posting_date]
+			["_Test Account Cost for Goods Sold - _TC", 250, 0, pi.posting_date],
 		]
 
 		check_gl_entries(self, pr.name, expected_gle_for_purchase_receipt, pr.posting_date)
 
+		# Cancel purchase invoice to check reverse provisional entry cancellation
+		pi.cancel()
+
+		expected_gle_for_purchase_receipt_post_pi_cancel = [
+			["Provision Account - _TC", 0, 250, pi.posting_date],
+			["_Test Account Cost for Goods Sold - _TC", 250, 0, pi.posting_date],
+		]
+
+		check_gl_entries(
+			self, pr.name, expected_gle_for_purchase_receipt_post_pi_cancel, pr.posting_date
+		)
+
 		company.enable_provisional_accounting_for_non_stock_items = 0
 		company.save()
 
+	def test_item_less_defaults(self):
+
+		pi = frappe.new_doc("Purchase Invoice")
+		pi.supplier = "_Test Supplier"
+		pi.company = "_Test Company"
+		pi.append(
+			"items",
+			{
+				"item_name": "Opening item",
+				"qty": 1,
+				"uom": "Tonne",
+				"stock_uom": "Kg",
+				"rate": 1000,
+				"expense_account": "Stock Received But Not Billed - _TC",
+			},
+		)
+
+		pi.save()
+		self.assertEqual(pi.items[0].conversion_factor, 1000)
+
+
 def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
-	gl_entries = frappe.db.sql("""select account, debit, credit, posting_date
+	gl_entries = frappe.db.sql(
+		"""select account, debit, credit, posting_date
 		from `tabGL Entry`
 		where voucher_type='Purchase Invoice' and voucher_no=%s and posting_date >= %s
-		order by posting_date asc, account asc""", (voucher_no, posting_date), as_dict=1)
+		order by posting_date asc, account asc""",
+		(voucher_no, posting_date),
+		as_dict=1,
+	)
 
 	for i, gle in enumerate(gl_entries):
 		doc.assertEqual(expected_gle[i][0], gle.account)
@@ -1275,44 +1653,48 @@
 		doc.assertEqual(expected_gle[i][2], gle.credit)
 		doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
 
+
 def update_tax_witholding_category(company, account):
 	from erpnext.accounts.utils import get_fiscal_year
 
 	fiscal_year = get_fiscal_year(date=nowdate())
 
-	if not frappe.db.get_value('Tax Withholding Rate',
-		{'parent': 'TDS - 194 - Dividends - Individual', 'from_date': ('>=', fiscal_year[1]),
-			'to_date': ('<=', fiscal_year[2])}):
-		tds_category = frappe.get_doc('Tax Withholding Category', 'TDS - 194 - Dividends - Individual')
-		tds_category.set('rates', [])
+	if not frappe.db.get_value(
+		"Tax Withholding Rate",
+		{
+			"parent": "TDS - 194 - Dividends - Individual",
+			"from_date": (">=", fiscal_year[1]),
+			"to_date": ("<=", fiscal_year[2]),
+		},
+	):
+		tds_category = frappe.get_doc("Tax Withholding Category", "TDS - 194 - Dividends - Individual")
+		tds_category.set("rates", [])
 
-		tds_category.append('rates', {
-			'from_date': fiscal_year[1],
-			'to_date': fiscal_year[2],
-			'tax_withholding_rate': 10,
-			'single_threshold': 2500,
-			'cumulative_threshold': 0
-		})
+		tds_category.append(
+			"rates",
+			{
+				"from_date": fiscal_year[1],
+				"to_date": fiscal_year[2],
+				"tax_withholding_rate": 10,
+				"single_threshold": 2500,
+				"cumulative_threshold": 0,
+			},
+		)
 		tds_category.save()
 
-	if not frappe.db.get_value('Tax Withholding Account',
-		{'parent': 'TDS - 194 - Dividends - Individual', 'account': account}):
-		tds_category = frappe.get_doc('Tax Withholding Category', 'TDS - 194 - Dividends - Individual')
-		tds_category.append('accounts', {
-			'company': company,
-			'account': account
-		})
+	if not frappe.db.get_value(
+		"Tax Withholding Account", {"parent": "TDS - 194 - Dividends - Individual", "account": account}
+	):
+		tds_category = frappe.get_doc("Tax Withholding Category", "TDS - 194 - Dividends - Individual")
+		tds_category.append("accounts", {"company": company, "account": account})
 		tds_category.save()
 
+
 def unlink_payment_on_cancel_of_invoice(enable=1):
 	accounts_settings = frappe.get_doc("Accounts Settings")
 	accounts_settings.unlink_payment_on_cancellation_of_invoice = enable
 	accounts_settings.save()
 
-def enable_discount_accounting(enable=1):
-	accounts_settings = frappe.get_doc("Accounts Settings")
-	accounts_settings.enable_discount_accounting = enable
-	accounts_settings.save()
 
 def make_purchase_invoice(**args):
 	pi = frappe.new_doc("Purchase Invoice")
@@ -1326,7 +1708,7 @@
 		pi.is_paid = 1
 
 	if args.cash_bank_account:
-		pi.cash_bank_account=args.cash_bank_account
+		pi.cash_bank_account = args.cash_bank_account
 
 	pi.company = args.company or "_Test Company"
 	pi.supplier = args.supplier or "_Test Supplier"
@@ -1334,31 +1716,34 @@
 	pi.conversion_rate = args.conversion_rate or 1
 	pi.is_return = args.is_return
 	pi.return_against = args.return_against
-	pi.is_subcontracted = args.is_subcontracted or "No"
+	pi.is_subcontracted = args.is_subcontracted or 0
 	pi.supplier_warehouse = args.supplier_warehouse or "_Test Warehouse 1 - _TC"
 	pi.cost_center = args.parent_cost_center
 
-	pi.append("items", {
-		"item_code": args.item or args.item_code or "_Test Item",
-		"warehouse": args.warehouse or "_Test Warehouse - _TC",
-		"qty": args.qty or 5,
-		"received_qty": args.received_qty or 0,
-		"rejected_qty": args.rejected_qty or 0,
-		"rate": args.rate or 50,
-		"price_list_rate": args.price_list_rate or 50,
-		"expense_account": args.expense_account or '_Test Account Cost for Goods Sold - _TC',
-		"discount_account": args.discount_account or None,
-		"discount_amount": args.discount_amount or 0,
-		"conversion_factor": 1.0,
-		"serial_no": args.serial_no,
-		"stock_uom": args.uom or "_Test UOM",
-		"cost_center": args.cost_center or "_Test Cost Center - _TC",
-		"project": args.project,
-		"rejected_warehouse": args.rejected_warehouse or "",
-		"rejected_serial_no": args.rejected_serial_no or "",
-		"asset_location": args.location or "",
-		"allow_zero_valuation_rate": args.get("allow_zero_valuation_rate") or 0
-	})
+	pi.append(
+		"items",
+		{
+			"item_code": args.item or args.item_code or "_Test Item",
+			"warehouse": args.warehouse or "_Test Warehouse - _TC",
+			"qty": args.qty or 5,
+			"received_qty": args.received_qty or 0,
+			"rejected_qty": args.rejected_qty or 0,
+			"rate": args.rate or 50,
+			"price_list_rate": args.price_list_rate or 50,
+			"expense_account": args.expense_account or "_Test Account Cost for Goods Sold - _TC",
+			"discount_account": args.discount_account or None,
+			"discount_amount": args.discount_amount or 0,
+			"conversion_factor": 1.0,
+			"serial_no": args.serial_no,
+			"stock_uom": args.uom or "_Test UOM",
+			"cost_center": args.cost_center or "_Test Cost Center - _TC",
+			"project": args.project,
+			"rejected_warehouse": args.rejected_warehouse or "",
+			"rejected_serial_no": args.rejected_serial_no or "",
+			"asset_location": args.location or "",
+			"allow_zero_valuation_rate": args.get("allow_zero_valuation_rate") or 0,
+		},
+	)
 
 	if args.get_taxes_and_charges:
 		taxes = get_taxes()
@@ -1371,6 +1756,7 @@
 			pi.submit()
 	return pi
 
+
 def make_purchase_invoice_against_cost_center(**args):
 	pi = frappe.new_doc("Purchase Invoice")
 	args = frappe._dict(args)
@@ -1383,7 +1769,7 @@
 		pi.is_paid = 1
 
 	if args.cash_bank_account:
-		pi.cash_bank_account=args.cash_bank_account
+		pi.cash_bank_account = args.cash_bank_account
 
 	pi.company = args.company or "_Test Company"
 	pi.cost_center = args.cost_center or "_Test Cost Center - _TC"
@@ -1393,29 +1779,33 @@
 	pi.is_return = args.is_return
 	pi.is_return = args.is_return
 	pi.credit_to = args.return_against or "Creditors - _TC"
-	pi.is_subcontracted = args.is_subcontracted or "No"
+	pi.is_subcontracted = args.is_subcontracted or 0
 	if args.supplier_warehouse:
 		pi.supplier_warehouse = "_Test Warehouse 1 - _TC"
 
-	pi.append("items", {
-		"item_code": args.item or args.item_code or "_Test Item",
-		"warehouse": args.warehouse or "_Test Warehouse - _TC",
-		"qty": args.qty or 5,
-		"received_qty": args.received_qty or 0,
-		"rejected_qty": args.rejected_qty or 0,
-		"rate": args.rate or 50,
-		"conversion_factor": 1.0,
-		"serial_no": args.serial_no,
-		"stock_uom": "_Test UOM",
-		"cost_center": args.cost_center or "_Test Cost Center - _TC",
-		"project": args.project,
-		"rejected_warehouse": args.rejected_warehouse or "",
-		"rejected_serial_no": args.rejected_serial_no or ""
-	})
+	pi.append(
+		"items",
+		{
+			"item_code": args.item or args.item_code or "_Test Item",
+			"warehouse": args.warehouse or "_Test Warehouse - _TC",
+			"qty": args.qty or 5,
+			"received_qty": args.received_qty or 0,
+			"rejected_qty": args.rejected_qty or 0,
+			"rate": args.rate or 50,
+			"conversion_factor": 1.0,
+			"serial_no": args.serial_no,
+			"stock_uom": "_Test UOM",
+			"cost_center": args.cost_center or "_Test Cost Center - _TC",
+			"project": args.project,
+			"rejected_warehouse": args.rejected_warehouse or "",
+			"rejected_serial_no": args.rejected_serial_no or "",
+		},
+	)
 	if not args.do_not_save:
 		pi.insert()
 		if not args.do_not_submit:
 			pi.submit()
 	return pi
 
-test_records = frappe.get_test_records('Purchase Invoice')
+
+test_records = frappe.get_test_records("Purchase Invoice")
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index f9b2efd..1f79d47 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -195,6 +195,7 @@
    "label": "Rejected Qty"
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "stock_uom",
    "fieldtype": "Link",
    "label": "Stock UOM",
@@ -214,6 +215,7 @@
    "reqd": 1
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "conversion_factor",
    "fieldtype": "Float",
    "label": "UOM Conversion Factor",
@@ -222,6 +224,7 @@
    "reqd": 1
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "stock_qty",
    "fieldtype": "Float",
    "label": "Accepted Qty in Stock UOM",
@@ -623,7 +626,7 @@
   },
   {
    "default": "0",
-   "depends_on": "eval:parent.is_subcontracted == 'Yes'",
+   "depends_on": "eval:parent.is_subcontracted",
    "fieldname": "include_exploded_items",
    "fieldtype": "Check",
    "label": "Include Exploded Items",
@@ -871,7 +874,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-11-15 17:04:07.191013",
+ "modified": "2022-06-17 05:31:10.520171",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice Item",
@@ -879,5 +882,6 @@
  "owner": "Administrator",
  "permissions": [],
  "sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "states": []
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json
index b46d2e3..c36efb8 100644
--- a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json
+++ b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json
@@ -1,10 +1,12 @@
 {
+ "actions": [],
  "allow_import": 1,
  "allow_rename": 1,
  "creation": "2013-01-10 16:34:08",
  "description": "Standard tax template that can be applied to all Purchase Transactions. This template can contain list of tax heads and also other expense heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n    - This can be on **Net Total** (that is the sum of basic amount).\n    - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n    - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Consider Tax or Charge for: In this section you can specify if the tax / charge is only for valuation (not a part of total) or only for total (does not add value to the item) or for both.\n10. Add or Deduct: Whether you want to add or deduct the tax.",
  "doctype": "DocType",
  "document_type": "Setup",
+ "engine": "InnoDB",
  "field_order": [
   "title",
   "is_default",
@@ -74,7 +76,8 @@
  ],
  "icon": "fa fa-money",
  "idx": 1,
- "modified": "2019-11-25 13:05:26.220275",
+ "links": [],
+ "modified": "2022-05-16 16:15:29.059370",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Taxes and Charges Template",
@@ -103,6 +106,10 @@
    "role": "Purchase User"
   }
  ],
+ "show_title_field_in_link": 1,
+ "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
+ "title_field": "title",
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.py b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.py
index f5eb404..70d29bf 100644
--- a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.py
+++ b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.py
@@ -16,5 +16,5 @@
 
 	def autoname(self):
 		if self.company and self.title:
-			abbr = frappe.get_cached_value('Company',  self.company,  'abbr')
-			self.name = '{0} - {1}'.format(self.title, abbr)
+			abbr = frappe.get_cached_value("Company", self.company, "abbr")
+			self.name = "{0} - {1}".format(self.title, abbr)
diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template_dashboard.py b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template_dashboard.py
index 3176556..1f0ea21 100644
--- a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template_dashboard.py
+++ b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template_dashboard.py
@@ -3,18 +3,15 @@
 
 def get_data():
 	return {
-		'fieldname': 'taxes_and_charges',
-		'non_standard_fieldnames': {
-			'Tax Rule': 'purchase_tax_template',
+		"fieldname": "taxes_and_charges",
+		"non_standard_fieldnames": {
+			"Tax Rule": "purchase_tax_template",
 		},
-		'transactions': [
+		"transactions": [
 			{
-				'label': _('Transactions'),
-				'items': ['Purchase Invoice', 'Purchase Order', 'Purchase Receipt']
+				"label": _("Transactions"),
+				"items": ["Purchase Invoice", "Purchase Order", "Purchase Receipt"],
 			},
-			{
-				'label': _('References'),
-				'items': ['Supplier Quotation', 'Tax Rule']
-			}
-		]
+			{"label": _("References"), "items": ["Supplier Quotation", "Tax Rule"]},
+		],
 	}
diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.py b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.py
index b5b4a67..1d02f05 100644
--- a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.py
+++ b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.py
@@ -5,5 +5,6 @@
 
 # test_records = frappe.get_test_records('Purchase Taxes and Charges Template')
 
+
 class TestPurchaseTaxesandChargesTemplate(unittest.TestCase):
 	pass
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 42f88ce..8eccd2b 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -33,7 +33,9 @@
 		var me = this;
 		super.onload();
 
-		this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet', 'POS Invoice Merge Log', 'POS Closing Entry'];
+		this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet', 'POS Invoice Merge Log',
+			'POS Closing Entry', 'Journal Entry', 'Payment Entry'];
+
 		if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) {
 			// show debit_to in print format
 			this.frm.set_df_property("debit_to", "print_hide", 0);
@@ -50,7 +52,6 @@
 			me.frm.refresh_fields();
 		}
 		erpnext.queries.setup_warehouse_query(this.frm);
-		erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
 	}
 
 	refresh(doc, dt, dn) {
@@ -280,6 +281,9 @@
 		}
 		var me = this;
 		if(this.frm.updating_party_details) return;
+
+		if (this.frm.doc.__onload && this.frm.doc.__onload.load_after_mapping) return;
+
 		erpnext.utils.get_party_details(this.frm,
 			"erpnext.accounts.party.get_party_details", {
 				posting_date: this.frm.doc.posting_date,
@@ -815,29 +819,6 @@
 		}
 	},
 
-	// Healthcare
-	patient: function(frm) {
-		if (frappe.boot.active_domains.includes("Healthcare")){
-			if(frm.doc.patient){
-				frappe.call({
-					method: "frappe.client.get_value",
-					args:{
-						doctype: "Patient",
-						filters: {
-							"name": frm.doc.patient
-						},
-						fieldname: "customer"
-					},
-					callback:function(r) {
-						if(r && r.message.customer){
-							frm.set_value("customer", r.message.customer);
-						}
-					}
-				});
-			}
-		}
-	},
-
 	project: function(frm) {
 		if (frm.doc.project) {
 			frm.events.add_timesheet_data(frm, {
@@ -875,27 +856,44 @@
 
 	set_timesheet_data: function(frm, timesheets) {
 		frm.clear_table("timesheets")
-		timesheets.forEach(timesheet => {
+		timesheets.forEach(async (timesheet) => {
 			if (frm.doc.currency != timesheet.currency) {
-				frappe.call({
-					method: "erpnext.setup.utils.get_exchange_rate",
-					args: {
-						from_currency: timesheet.currency,
-						to_currency: frm.doc.currency
-					},
-					callback: function(r) {
-						if (r.message) {
-							exchange_rate = r.message;
-							frm.events.append_time_log(frm, timesheet, exchange_rate);
-						}
-					}
-				});
+				const exchange_rate = await frm.events.get_exchange_rate(
+					frm, timesheet.currency, frm.doc.currency
+				)
+				frm.events.append_time_log(frm, timesheet, exchange_rate)
 			} else {
 				frm.events.append_time_log(frm, timesheet, 1.0);
 			}
 		});
 	},
 
+	async get_exchange_rate(frm, from_currency, to_currency) {
+		if (
+			frm.exchange_rates
+			&& frm.exchange_rates[from_currency]
+			&& frm.exchange_rates[from_currency][to_currency]
+		) {
+			return frm.exchange_rates[from_currency][to_currency];
+		}
+
+		return frappe.call({
+			method: "erpnext.setup.utils.get_exchange_rate",
+			args: {
+				from_currency,
+				to_currency
+			},
+			callback: function(r) {
+				if (r.message) {
+					// cache exchange rates
+					frm.exchange_rates = frm.exchange_rates || {};
+					frm.exchange_rates[from_currency] = frm.exchange_rates[from_currency] || {};
+					frm.exchange_rates[from_currency][to_currency] = r.message;
+				}
+			}
+		});
+	},
+
 	append_time_log: function(frm, time_log, exchange_rate) {
 		const row = frm.add_child("timesheets");
 		row.activity_type = time_log.activity_type;
@@ -906,7 +904,7 @@
 		row.billing_hours = time_log.billing_hours;
 		row.billing_amount = flt(time_log.billing_amount) * flt(exchange_rate);
 		row.timesheet_detail = time_log.name;
-    row.project_name = time_log.project_name;
+		row.project_name = time_log.project_name;
 
 		frm.refresh_field("timesheets");
 		frm.trigger("calculate_timesheet_totals");
@@ -967,25 +965,6 @@
 		if (frm.doc.is_debit_note) {
 			frm.set_df_property('return_against', 'label', __('Adjustment Against'));
 		}
-
-		if (frappe.boot.active_domains.includes("Healthcare")) {
-			frm.set_df_property("patient", "hidden", 0);
-			frm.set_df_property("patient_name", "hidden", 0);
-			frm.set_df_property("ref_practitioner", "hidden", 0);
-			if (cint(frm.doc.docstatus==0) && cur_frm.page.current_view_name!=="pos" && !frm.doc.is_return) {
-				frm.add_custom_button(__('Healthcare Services'), function() {
-					get_healthcare_services_to_invoice(frm);
-				},__("Get Items From"));
-				frm.add_custom_button(__('Prescriptions'), function() {
-					get_drugs_to_invoice(frm);
-				},__("Get Items From"));
-			}
-		}
-		else {
-			frm.set_df_property("patient", "hidden", 1);
-			frm.set_df_property("patient_name", "hidden", 1);
-			frm.set_df_property("ref_practitioner", "hidden", 1);
-		}
 	},
 
 	create_invoice_discounting: function(frm) {
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index ef3028c..6361b4c 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -2,7 +2,7 @@
  "actions": [],
  "allow_import": 1,
  "autoname": "naming_series:",
- "creation": "2013-05-24 19:29:05",
+ "creation": "2022-01-25 10:29:57.771398",
  "doctype": "DocType",
  "engine": "InnoDB",
  "field_order": [
@@ -1766,6 +1766,8 @@
    "width": "50%"
   },
   {
+   "fetch_from": "sales_partner.commission_rate",
+   "fetch_if_empty": 1,
    "fieldname": "commission_rate",
    "fieldtype": "Float",
    "hide_days": 1,
@@ -1949,9 +1951,10 @@
   },
   {
    "default": "0",
+   "description": "Issue a debit note with 0 qty against an existing Sales Invoice",
    "fieldname": "is_debit_note",
    "fieldtype": "Check",
-   "label": "Is Debit Note"
+   "label": "Is Rate Adjustment Entry (Debit Note)"
   },
   {
    "default": "0",
@@ -2013,7 +2016,7 @@
    "link_fieldname": "consolidated_invoice"
   }
  ],
- "modified": "2022-02-19 06:25:10.079943",
+ "modified": "2022-06-10 03:52:51.409913",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 99f42b7..0c8b662 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -37,35 +37,40 @@
 	get_gl_entries_on_asset_regain,
 	make_depreciation_entry,
 )
+from erpnext.controllers.accounts_controller import validate_account_head
 from erpnext.controllers.selling_controller import SellingController
 from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data
 from erpnext.setup.doctype.company.company import update_company_current_month_sales
 from erpnext.stock.doctype.batch.batch import set_batch_nos
 from erpnext.stock.doctype.delivery_note.delivery_note import update_billed_amount_based_on_so
-from erpnext.stock.doctype.serial_no.serial_no import get_delivery_note_serial_no, get_serial_nos
-from erpnext.stock.utils import calculate_mapped_packed_items_return
+from erpnext.stock.doctype.serial_no.serial_no import (
+	get_delivery_note_serial_no,
+	get_serial_nos,
+	update_serial_nos_after_submit,
+)
 
-form_grid_templates = {
-	"items": "templates/form_grid/item_grid.html"
-}
+form_grid_templates = {"items": "templates/form_grid/item_grid.html"}
+
 
 class SalesInvoice(SellingController):
 	def __init__(self, *args, **kwargs):
 		super(SalesInvoice, self).__init__(*args, **kwargs)
-		self.status_updater = [{
-			'source_dt': 'Sales Invoice Item',
-			'target_field': 'billed_amt',
-			'target_ref_field': 'amount',
-			'target_dt': 'Sales Order Item',
-			'join_field': 'so_detail',
-			'target_parent_dt': 'Sales Order',
-			'target_parent_field': 'per_billed',
-			'source_field': 'amount',
-			'percent_join_field': 'sales_order',
-			'status_field': 'billing_status',
-			'keyword': 'Billed',
-			'overflow_type': 'billing'
-		}]
+		self.status_updater = [
+			{
+				"source_dt": "Sales Invoice Item",
+				"target_field": "billed_amt",
+				"target_ref_field": "amount",
+				"target_dt": "Sales Order Item",
+				"join_field": "so_detail",
+				"target_parent_dt": "Sales Order",
+				"target_parent_field": "per_billed",
+				"source_field": "amount",
+				"percent_join_field": "sales_order",
+				"status_field": "billing_status",
+				"keyword": "Billed",
+				"overflow_type": "billing",
+			}
+		]
 
 	def set_indicator(self):
 		"""Set indicator for portal"""
@@ -108,7 +113,11 @@
 		self.validate_fixed_asset()
 		self.set_income_account_for_fixed_assets()
 		self.validate_item_cost_centers()
-		validate_inter_company_party(self.doctype, self.customer, self.company, self.inter_company_invoice_reference)
+		self.validate_income_account()
+
+		validate_inter_company_party(
+			self.doctype, self.customer, self.company, self.inter_company_invoice_reference
+		)
 
 		if cint(self.is_pos):
 			self.validate_pos()
@@ -124,15 +133,21 @@
 		validate_service_stop_date(self)
 
 		if not self.is_opening:
-			self.is_opening = 'No'
+			self.is_opening = "No"
 
-		if self._action != 'submit' and self.update_stock and not self.is_return:
-			set_batch_nos(self, 'warehouse', True)
+		if self._action != "submit" and self.update_stock and not self.is_return:
+			set_batch_nos(self, "warehouse", True)
 
 		if self.redeem_loyalty_points:
-			lp = frappe.get_doc('Loyalty Program', self.loyalty_program)
-			self.loyalty_redemption_account = lp.expense_account if not self.loyalty_redemption_account else self.loyalty_redemption_account
-			self.loyalty_redemption_cost_center = lp.cost_center if not self.loyalty_redemption_cost_center else self.loyalty_redemption_cost_center
+			lp = frappe.get_doc("Loyalty Program", self.loyalty_program)
+			self.loyalty_redemption_account = (
+				lp.expense_account if not self.loyalty_redemption_account else self.loyalty_redemption_account
+			)
+			self.loyalty_redemption_cost_center = (
+				lp.cost_center
+				if not self.loyalty_redemption_cost_center
+				else self.loyalty_redemption_cost_center
+			)
 
 		self.set_against_income_account()
 		self.validate_time_sheets_are_submitted()
@@ -148,11 +163,16 @@
 		if self.is_pos and not self.is_return:
 			self.verify_payment_amount_is_positive()
 
-		#validate amount in mode of payments for returned invoices for pos must be negative
+		# validate amount in mode of payments for returned invoices for pos must be negative
 		if self.is_pos and self.is_return:
 			self.verify_payment_amount_is_negative()
 
-		if self.redeem_loyalty_points and self.loyalty_program and self.loyalty_points and not self.is_consolidated:
+		if (
+			self.redeem_loyalty_points
+			and self.loyalty_program
+			and self.loyalty_points
+			and not self.is_consolidated
+		):
 			validate_loyalty_points(self, self.loyalty_points)
 
 		self.reset_default_field_value("set_warehouse", "items", "warehouse")
@@ -165,14 +185,28 @@
 					if self.update_stock:
 						frappe.throw(_("'Update Stock' cannot be checked for fixed asset sale"))
 
-					elif asset.status in ("Scrapped", "Cancelled") or (asset.status == "Sold" and not self.is_return):
-						frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}").format(d.idx, d.asset, asset.status))
+					elif asset.status in ("Scrapped", "Cancelled") or (
+						asset.status == "Sold" and not self.is_return
+					):
+						frappe.throw(
+							_("Row #{0}: Asset {1} cannot be submitted, it is already {2}").format(
+								d.idx, d.asset, asset.status
+							)
+						)
 
 	def validate_item_cost_centers(self):
 		for item in self.items:
 			cost_center_company = frappe.get_cached_value("Cost Center", item.cost_center, "company")
 			if cost_center_company != self.company:
-				frappe.throw(_("Row #{0}: Cost Center {1} does not belong to company {2}").format(frappe.bold(item.idx), frappe.bold(item.cost_center), frappe.bold(self.company)))
+				frappe.throw(
+					_("Row #{0}: Cost Center {1} does not belong to company {2}").format(
+						frappe.bold(item.idx), frappe.bold(item.cost_center), frappe.bold(self.company)
+					)
+				)
+
+	def validate_income_account(self):
+		for item in self.get("items"):
+			validate_account_head(item.idx, item.income_account, self.company, "Income")
 
 	def set_tax_withholding(self):
 		tax_withholding_details = get_party_tax_withholding_details(self)
@@ -191,8 +225,11 @@
 		if not accounts or tax_withholding_account not in accounts:
 			self.append("taxes", tax_withholding_details)
 
-		to_remove = [d for d in self.taxes
-			if not d.tax_amount and d.charge_type == "Actual" and d.account_head == tax_withholding_account]
+		to_remove = [
+			d
+			for d in self.taxes
+			if not d.tax_amount and d.charge_type == "Actual" and d.account_head == tax_withholding_account
+		]
 
 		for d in to_remove:
 			self.remove(d)
@@ -207,8 +244,9 @@
 		self.validate_pos_paid_amount()
 
 		if not self.auto_repeat:
-			frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
-				self.company, self.base_grand_total, self)
+			frappe.get_doc("Authorization Control").validate_approving_authority(
+				self.doctype, self.company, self.base_grand_total, self
+			)
 
 		self.check_prev_docstatus()
 
@@ -225,6 +263,8 @@
 		# because updating reserved qty in bin depends upon updated delivered qty in SO
 		if self.update_stock == 1:
 			self.update_stock_ledger()
+		if self.is_return and self.update_stock:
+			update_serial_nos_after_submit(self, "items")
 
 		# this sequence because outstanding may get -ve
 		self.make_gl_entries()
@@ -244,7 +284,9 @@
 
 		self.update_time_sheet(self.name)
 
-		if frappe.db.get_single_value('Selling Settings', 'sales_update_frequency') == "Each Transaction":
+		if (
+			frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction"
+		):
 			update_company_current_month_sales(self.company)
 			self.update_project()
 		update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
@@ -252,7 +294,9 @@
 		# create the loyalty point ledger entry if the customer is enrolled in any loyalty program
 		if not self.is_return and not self.is_consolidated and self.loyalty_program:
 			self.make_loyalty_point_entry()
-		elif self.is_return and self.return_against and not self.is_consolidated and self.loyalty_program:
+		elif (
+			self.is_return and self.return_against and not self.is_consolidated and self.loyalty_program
+		):
 			against_si_doc = frappe.get_doc("Sales Invoice", self.return_against)
 			against_si_doc.delete_loyalty_point_entry()
 			against_si_doc.make_loyalty_point_entry()
@@ -262,6 +306,9 @@
 		self.process_common_party_accounting()
 
 	def validate_pos_return(self):
+		if self.is_consolidated:
+			# pos return is already validated in pos invoice
+			return
 
 		if self.is_pos and self.is_return:
 			total_amount_in_payments = 0
@@ -278,16 +325,16 @@
 	def check_if_consolidated_invoice(self):
 		# since POS Invoice extends Sales Invoice, we explicitly check if doctype is Sales Invoice
 		if self.doctype == "Sales Invoice" and self.is_consolidated:
-			invoice_or_credit_note = "consolidated_credit_note" if self.is_return else "consolidated_invoice"
+			invoice_or_credit_note = (
+				"consolidated_credit_note" if self.is_return else "consolidated_invoice"
+			)
 			pos_closing_entry = frappe.get_all(
-				"POS Invoice Merge Log",
-				filters={ invoice_or_credit_note: self.name },
-				pluck="pos_closing_entry"
+				"POS Invoice Merge Log", filters={invoice_or_credit_note: self.name}, pluck="pos_closing_entry"
 			)
 			if pos_closing_entry and pos_closing_entry[0]:
 				msg = _("To cancel a {} you need to cancel the POS Closing Entry {}.").format(
 					frappe.bold("Consolidated Sales Invoice"),
-					get_link_to_form("POS Closing Entry", pos_closing_entry[0])
+					get_link_to_form("POS Closing Entry", pos_closing_entry[0]),
 				)
 				frappe.throw(msg, title=_("Not Allowed"))
 
@@ -327,14 +374,18 @@
 		if self.update_stock == 1:
 			self.repost_future_sle_and_gle()
 
-		frappe.db.set(self, 'status', 'Cancelled')
+		frappe.db.set(self, "status", "Cancelled")
 
-		if frappe.db.get_single_value('Selling Settings', 'sales_update_frequency') == "Each Transaction":
+		if (
+			frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction"
+		):
 			update_company_current_month_sales(self.company)
 			self.update_project()
 		if not self.is_return and not self.is_consolidated and self.loyalty_program:
 			self.delete_loyalty_point_entry()
-		elif self.is_return and self.return_against and not self.is_consolidated and self.loyalty_program:
+		elif (
+			self.is_return and self.return_against and not self.is_consolidated and self.loyalty_program
+		):
 			against_si_doc = frappe.get_doc("Sales Invoice", self.return_against)
 			against_si_doc.delete_loyalty_point_entry()
 			against_si_doc.make_loyalty_point_entry()
@@ -342,50 +393,61 @@
 		unlink_inter_company_doc(self.doctype, self.name, self.inter_company_invoice_reference)
 
 		self.unlink_sales_invoice_from_timesheets()
-		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
+		self.ignore_linked_doctypes = (
+			"GL Entry",
+			"Stock Ledger Entry",
+			"Repost Item Valuation",
+			"Payment Ledger Entry",
+		)
 
 	def update_status_updater_args(self):
 		if cint(self.update_stock):
-			self.status_updater.append({
-				'source_dt':'Sales Invoice Item',
-				'target_dt':'Sales Order Item',
-				'target_parent_dt':'Sales Order',
-				'target_parent_field':'per_delivered',
-				'target_field':'delivered_qty',
-				'target_ref_field':'qty',
-				'source_field':'qty',
-				'join_field':'so_detail',
-				'percent_join_field':'sales_order',
-				'status_field':'delivery_status',
-				'keyword':'Delivered',
-				'second_source_dt': 'Delivery Note Item',
-				'second_source_field': 'qty',
-				'second_join_field': 'so_detail',
-				'overflow_type': 'delivery',
-				'extra_cond': """ and exists(select name from `tabSales Invoice`
-					where name=`tabSales Invoice Item`.parent and update_stock = 1)"""
-			})
+			self.status_updater.append(
+				{
+					"source_dt": "Sales Invoice Item",
+					"target_dt": "Sales Order Item",
+					"target_parent_dt": "Sales Order",
+					"target_parent_field": "per_delivered",
+					"target_field": "delivered_qty",
+					"target_ref_field": "qty",
+					"source_field": "qty",
+					"join_field": "so_detail",
+					"percent_join_field": "sales_order",
+					"status_field": "delivery_status",
+					"keyword": "Delivered",
+					"second_source_dt": "Delivery Note Item",
+					"second_source_field": "qty",
+					"second_join_field": "so_detail",
+					"overflow_type": "delivery",
+					"extra_cond": """ and exists(select name from `tabSales Invoice`
+					where name=`tabSales Invoice Item`.parent and update_stock = 1)""",
+				}
+			)
 			if cint(self.is_return):
-				self.status_updater.append({
-					'source_dt': 'Sales Invoice Item',
-					'target_dt': 'Sales Order Item',
-					'join_field': 'so_detail',
-					'target_field': 'returned_qty',
-					'target_parent_dt': 'Sales Order',
-					'source_field': '-1 * qty',
-					'second_source_dt': 'Delivery Note Item',
-					'second_source_field': '-1 * qty',
-					'second_join_field': 'so_detail',
-					'extra_cond': """ and exists (select name from `tabSales Invoice` where name=`tabSales Invoice Item`.parent and update_stock=1 and is_return=1)"""
-				})
+				self.status_updater.append(
+					{
+						"source_dt": "Sales Invoice Item",
+						"target_dt": "Sales Order Item",
+						"join_field": "so_detail",
+						"target_field": "returned_qty",
+						"target_parent_dt": "Sales Order",
+						"source_field": "-1 * qty",
+						"second_source_dt": "Delivery Note Item",
+						"second_source_field": "-1 * qty",
+						"second_join_field": "so_detail",
+						"extra_cond": """ and exists (select name from `tabSales Invoice` where name=`tabSales Invoice Item`.parent and update_stock=1 and is_return=1)""",
+					}
+				)
 
 	def check_credit_limit(self):
 		from erpnext.selling.doctype.customer.customer import check_credit_limit
 
 		validate_against_credit_limit = False
-		bypass_credit_limit_check_at_sales_order = frappe.db.get_value("Customer Credit Limit",
-			filters={'parent': self.customer, 'parenttype': 'Customer', 'company': self.company},
-			fieldname=["bypass_credit_limit_check"])
+		bypass_credit_limit_check_at_sales_order = frappe.db.get_value(
+			"Customer Credit Limit",
+			filters={"parent": self.customer, "parenttype": "Customer", "company": self.company},
+			fieldname=["bypass_credit_limit_check"],
+		)
 
 		if bypass_credit_limit_check_at_sales_order:
 			validate_against_credit_limit = True
@@ -399,7 +461,7 @@
 
 	def unlink_sales_invoice_from_timesheets(self):
 		for row in self.timesheets:
-			timesheet = frappe.get_doc('Timesheet', row.time_sheet)
+			timesheet = frappe.get_doc("Timesheet", row.time_sheet)
 			for time_log in timesheet.time_logs:
 				if time_log.sales_invoice == self.name:
 					time_log.sales_invoice = None
@@ -415,15 +477,17 @@
 
 		if not self.debit_to:
 			self.debit_to = get_party_account("Customer", self.customer, self.company)
-			self.party_account_currency = frappe.db.get_value("Account", self.debit_to, "account_currency", cache=True)
+			self.party_account_currency = frappe.db.get_value(
+				"Account", self.debit_to, "account_currency", cache=True
+			)
 		if not self.due_date and self.customer:
 			self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company)
 
 		super(SalesInvoice, self).set_missing_values(for_validate)
 
 		print_format = pos.get("print_format") if pos else None
-		if not print_format and not cint(frappe.db.get_value('Print Format', 'POS Invoice', 'disabled')):
-			print_format = 'POS Invoice'
+		if not print_format and not cint(frappe.db.get_value("Print Format", "POS Invoice", "disabled")):
+			print_format = "POS Invoice"
 
 		if pos:
 			return {
@@ -431,7 +495,7 @@
 				"allow_edit_rate": pos.get("allow_user_to_edit_rate"),
 				"allow_edit_discount": pos.get("allow_user_to_edit_discount"),
 				"campaign": pos.get("campaign"),
-				"allow_print_before_pay": pos.get("allow_print_before_pay")
+				"allow_print_before_pay": pos.get("allow_print_before_pay"),
 			}
 
 	def update_time_sheet(self, sales_invoice):
@@ -447,9 +511,11 @@
 
 	def update_time_sheet_detail(self, timesheet, args, sales_invoice):
 		for data in timesheet.time_logs:
-			if (self.project and args.timesheet_detail == data.name) or \
-				(not self.project and not data.sales_invoice) or \
-				(not sales_invoice and data.sales_invoice == self.name):
+			if (
+				(self.project and args.timesheet_detail == data.name)
+				or (not self.project and not data.sales_invoice)
+				or (not sales_invoice and data.sales_invoice == self.name)
+			):
 				data.sales_invoice = sales_invoice
 
 	def on_update(self):
@@ -459,7 +525,7 @@
 		paid_amount = 0.0
 		base_paid_amount = 0.0
 		for data in self.payments:
-			data.base_amount = flt(data.amount*self.conversion_rate, self.precision("base_paid_amount"))
+			data.base_amount = flt(data.amount * self.conversion_rate, self.precision("base_paid_amount"))
 			paid_amount += data.amount
 			base_paid_amount += data.base_amount
 
@@ -470,7 +536,7 @@
 		for data in self.timesheets:
 			if data.time_sheet:
 				status = frappe.db.get_value("Timesheet", data.time_sheet, "status")
-				if status not in ['Submitted', 'Payslip']:
+				if status not in ["Submitted", "Payslip"]:
 					frappe.throw(_("Timesheet {0} is already completed or cancelled").format(data.time_sheet))
 
 	def set_pos_fields(self, for_validate=False):
@@ -479,20 +545,23 @@
 			return
 
 		if not self.account_for_change_amount:
-			self.account_for_change_amount = frappe.get_cached_value('Company',  self.company,  'default_cash_account')
+			self.account_for_change_amount = frappe.get_cached_value(
+				"Company", self.company, "default_cash_account"
+			)
 
 		from erpnext.stock.get_item_details import get_pos_profile, get_pos_profile_item_details
+
 		if not self.pos_profile and not self.flags.ignore_pos_profile:
 			pos_profile = get_pos_profile(self.company) or {}
 			if not pos_profile:
 				return
-			self.pos_profile = pos_profile.get('name')
+			self.pos_profile = pos_profile.get("name")
 
 		pos = {}
 		if self.pos_profile:
-			pos = frappe.get_doc('POS Profile', self.pos_profile)
+			pos = frappe.get_doc("POS Profile", self.pos_profile)
 
-		if not self.get('payments') and not for_validate:
+		if not self.get("payments") and not for_validate:
 			update_multi_mode_option(self, pos)
 
 		if pos:
@@ -505,35 +574,52 @@
 			if not for_validate:
 				self.ignore_pricing_rule = pos.ignore_pricing_rule
 
-			if pos.get('account_for_change_amount'):
-				self.account_for_change_amount = pos.get('account_for_change_amount')
+			if pos.get("account_for_change_amount"):
+				self.account_for_change_amount = pos.get("account_for_change_amount")
 
-			for fieldname in ('currency', 'letter_head', 'tc_name',
-				'company', 'select_print_heading', 'write_off_account', 'taxes_and_charges',
-				'write_off_cost_center', 'apply_discount_on', 'cost_center'):
-					if (not for_validate) or (for_validate and not self.get(fieldname)):
-						self.set(fieldname, pos.get(fieldname))
+			for fieldname in (
+				"currency",
+				"letter_head",
+				"tc_name",
+				"company",
+				"select_print_heading",
+				"write_off_account",
+				"taxes_and_charges",
+				"write_off_cost_center",
+				"apply_discount_on",
+				"cost_center",
+			):
+				if (not for_validate) or (for_validate and not self.get(fieldname)):
+					self.set(fieldname, pos.get(fieldname))
 
 			if pos.get("company_address"):
 				self.company_address = pos.get("company_address")
 
 			if self.customer:
-				customer_price_list, customer_group = frappe.get_value("Customer", self.customer, ['default_price_list', 'customer_group'])
-				customer_group_price_list = frappe.get_value("Customer Group", customer_group, 'default_price_list')
-				selling_price_list = customer_price_list or customer_group_price_list or pos.get('selling_price_list')
+				customer_price_list, customer_group = frappe.get_value(
+					"Customer", self.customer, ["default_price_list", "customer_group"]
+				)
+				customer_group_price_list = frappe.get_value(
+					"Customer Group", customer_group, "default_price_list"
+				)
+				selling_price_list = (
+					customer_price_list or customer_group_price_list or pos.get("selling_price_list")
+				)
 			else:
-				selling_price_list = pos.get('selling_price_list')
+				selling_price_list = pos.get("selling_price_list")
 
 			if selling_price_list:
-				self.set('selling_price_list', selling_price_list)
+				self.set("selling_price_list", selling_price_list)
 
 			if not for_validate:
 				self.update_stock = cint(pos.get("update_stock"))
 
 			# set pos values in items
 			for item in self.get("items"):
-				if item.get('item_code'):
-					profile_details = get_pos_profile_item_details(pos, frappe._dict(item.as_dict()), pos, update_data=True)
+				if item.get("item_code"):
+					profile_details = get_pos_profile_item_details(
+						pos, frappe._dict(item.as_dict()), pos, update_data=True
+					)
 					for fname, val in profile_details.items():
 						if (not for_validate) or (for_validate and not item.get(fname)):
 							item.set(fname, val)
@@ -557,22 +643,29 @@
 			if not self.debit_to:
 				self.raise_missing_debit_credit_account_error("Customer", self.customer)
 
-		account = frappe.get_cached_value("Account", self.debit_to,
-			["account_type", "report_type", "account_currency"], as_dict=True)
+		account = frappe.get_cached_value(
+			"Account", self.debit_to, ["account_type", "report_type", "account_currency"], as_dict=True
+		)
 
 		if not account:
 			frappe.throw(_("Debit To is required"), title=_("Account Missing"))
 
 		if account.report_type != "Balance Sheet":
-			msg = _("Please ensure {} account is a Balance Sheet account.").format(frappe.bold("Debit To")) + " "
-			msg += _("You can change the parent account to a Balance Sheet account or select a different account.")
+			msg = (
+				_("Please ensure {} account is a Balance Sheet account.").format(frappe.bold("Debit To")) + " "
+			)
+			msg += _(
+				"You can change the parent account to a Balance Sheet account or select a different account."
+			)
 			frappe.throw(msg, title=_("Invalid Account"))
 
 		if self.customer and account.account_type != "Receivable":
-			msg = _("Please ensure {} account {} is a Receivable account.").format(
-				frappe.bold("Debit To"),
-				frappe.bold(self.debit_to)
-			) + " "
+			msg = (
+				_("Please ensure {} account {} is a Receivable account.").format(
+					frappe.bold("Debit To"), frappe.bold(self.debit_to)
+				)
+				+ " "
+			)
 			msg += _("Change the account type to Receivable or select a different account.")
 			frappe.throw(msg, title=_("Invalid Account"))
 
@@ -581,52 +674,60 @@
 	def clear_unallocated_mode_of_payments(self):
 		self.set("payments", self.get("payments", {"amount": ["not in", [0, None, ""]]}))
 
-		frappe.db.sql("""delete from `tabSales Invoice Payment` where parent = %s
-			and amount = 0""", self.name)
+		frappe.db.sql(
+			"""delete from `tabSales Invoice Payment` where parent = %s
+			and amount = 0""",
+			self.name,
+		)
 
 	def validate_with_previous_doc(self):
-		super(SalesInvoice, self).validate_with_previous_doc({
-			"Sales Order": {
-				"ref_dn_field": "sales_order",
-				"compare_fields": [["customer", "="], ["company", "="], ["project", "="], ["currency", "="]]
-			},
-			"Sales Order Item": {
-				"ref_dn_field": "so_detail",
-				"compare_fields": [["item_code", "="], ["uom", "="], ["conversion_factor", "="]],
-				"is_child_table": True,
-				"allow_duplicate_prev_row_id": True
-			},
-			"Delivery Note": {
-				"ref_dn_field": "delivery_note",
-				"compare_fields": [["customer", "="], ["company", "="], ["project", "="], ["currency", "="]]
-			},
-			"Delivery Note Item": {
-				"ref_dn_field": "dn_detail",
-				"compare_fields": [["item_code", "="], ["uom", "="], ["conversion_factor", "="]],
-				"is_child_table": True,
-				"allow_duplicate_prev_row_id": True
-			},
-		})
+		super(SalesInvoice, self).validate_with_previous_doc(
+			{
+				"Sales Order": {
+					"ref_dn_field": "sales_order",
+					"compare_fields": [["customer", "="], ["company", "="], ["project", "="], ["currency", "="]],
+				},
+				"Sales Order Item": {
+					"ref_dn_field": "so_detail",
+					"compare_fields": [["item_code", "="], ["uom", "="], ["conversion_factor", "="]],
+					"is_child_table": True,
+					"allow_duplicate_prev_row_id": True,
+				},
+				"Delivery Note": {
+					"ref_dn_field": "delivery_note",
+					"compare_fields": [["customer", "="], ["company", "="], ["project", "="], ["currency", "="]],
+				},
+				"Delivery Note Item": {
+					"ref_dn_field": "dn_detail",
+					"compare_fields": [["item_code", "="], ["uom", "="], ["conversion_factor", "="]],
+					"is_child_table": True,
+					"allow_duplicate_prev_row_id": True,
+				},
+			}
+		)
 
-		if cint(frappe.db.get_single_value('Selling Settings', 'maintain_same_sales_rate')) and not self.is_return:
-			self.validate_rate_with_reference_doc([
-				["Sales Order", "sales_order", "so_detail"],
-				["Delivery Note", "delivery_note", "dn_detail"]
-			])
+		if (
+			cint(frappe.db.get_single_value("Selling Settings", "maintain_same_sales_rate"))
+			and not self.is_return
+		):
+			self.validate_rate_with_reference_doc(
+				[["Sales Order", "sales_order", "so_detail"], ["Delivery Note", "delivery_note", "dn_detail"]]
+			)
 
 	def set_against_income_account(self):
 		"""Set against account for debit to account"""
 		against_acc = []
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if d.income_account and d.income_account not in against_acc:
 				against_acc.append(d.income_account)
-		self.against_income_account = ','.join(against_acc)
+		self.against_income_account = ",".join(against_acc)
 
 	def add_remarks(self):
 		if not self.remarks:
 			if self.po_no and self.po_date:
-				self.remarks = _("Against Customer Order {0} dated {1}").format(self.po_no,
-					formatdate(self.po_date))
+				self.remarks = _("Against Customer Order {0} dated {1}").format(
+					self.po_no, formatdate(self.po_date)
+				)
 			else:
 				self.remarks = _("No Remarks")
 
@@ -642,36 +743,41 @@
 		if self.is_return:
 			return
 
-		prev_doc_field_map = {'Sales Order': ['so_required', 'is_pos'],'Delivery Note': ['dn_required', 'update_stock']}
+		prev_doc_field_map = {
+			"Sales Order": ["so_required", "is_pos"],
+			"Delivery Note": ["dn_required", "update_stock"],
+		}
 		for key, value in prev_doc_field_map.items():
-			if frappe.db.get_single_value('Selling Settings', value[0]) == 'Yes':
+			if frappe.db.get_single_value("Selling Settings", value[0]) == "Yes":
 
-				if frappe.get_value('Customer', self.customer, value[0]):
+				if frappe.get_value("Customer", self.customer, value[0]):
 					continue
 
-				for d in self.get('items'):
-					if (d.item_code and not d.get(key.lower().replace(' ', '_')) and not self.get(value[1])):
+				for d in self.get("items"):
+					if d.item_code and not d.get(key.lower().replace(" ", "_")) and not self.get(value[1]):
 						msgprint(_("{0} is mandatory for Item {1}").format(key, d.item_code), raise_exception=1)
 
-
 	def validate_proj_cust(self):
 		"""check for does customer belong to same project as entered.."""
 		if self.project and self.customer:
-			res = frappe.db.sql("""select name from `tabProject`
+			res = frappe.db.sql(
+				"""select name from `tabProject`
 				where name = %s and (customer = %s or customer is null or customer = '')""",
-				(self.project, self.customer))
+				(self.project, self.customer),
+			)
 			if not res:
-				throw(_("Customer {0} does not belong to project {1}").format(self.customer,self.project))
+				throw(_("Customer {0} does not belong to project {1}").format(self.customer, self.project))
 
 	def validate_pos(self):
 		if self.is_return:
 			invoice_total = self.rounded_total or self.grand_total
-			if flt(self.paid_amount) + flt(self.write_off_amount) - flt(invoice_total) > \
-				1.0/(10.0**(self.precision("grand_total") + 1.0)):
-					frappe.throw(_("Paid amount + Write Off Amount can not be greater than Grand Total"))
+			if flt(self.paid_amount) + flt(self.write_off_amount) - flt(invoice_total) > 1.0 / (
+				10.0 ** (self.precision("grand_total") + 1.0)
+			):
+				frappe.throw(_("Paid amount + Write Off Amount can not be greater than Grand Total"))
 
 	def validate_item_code(self):
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if not d.item_code and self.is_opening == "No":
 				msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True)
 
@@ -679,17 +785,24 @@
 		super(SalesInvoice, self).validate_warehouse()
 
 		for d in self.get_item_list():
-			if not d.warehouse and d.item_code and frappe.get_cached_value("Item", d.item_code, "is_stock_item"):
+			if (
+				not d.warehouse
+				and d.item_code
+				and frappe.get_cached_value("Item", d.item_code, "is_stock_item")
+			):
 				frappe.throw(_("Warehouse required for stock Item {0}").format(d.item_code))
 
 	def validate_delivery_note(self):
 		for d in self.get("items"):
 			if d.delivery_note:
-				msgprint(_("Stock cannot be updated against Delivery Note {0}").format(d.delivery_note), raise_exception=1)
+				msgprint(
+					_("Stock cannot be updated against Delivery Note {0}").format(d.delivery_note),
+					raise_exception=1,
+				)
 
 	def validate_write_off_account(self):
 		if flt(self.write_off_amount) and not self.write_off_account:
-			self.write_off_account = frappe.get_cached_value('Company',  self.company,  'write_off_account')
+			self.write_off_account = frappe.get_cached_value("Company", self.company, "write_off_account")
 
 		if flt(self.write_off_amount) and not self.write_off_account:
 			msgprint(_("Please enter Write Off Account"), raise_exception=1)
@@ -705,30 +818,36 @@
 					frappe.throw(_("Could not update stock, invoice contains drop shipping item."))
 
 	def update_current_stock(self):
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if d.item_code and d.warehouse:
-				bin = frappe.db.sql("select actual_qty from `tabBin` where item_code = %s and warehouse = %s", (d.item_code, d.warehouse), as_dict = 1)
-				d.actual_qty = bin and flt(bin[0]['actual_qty']) or 0
+				bin = frappe.db.sql(
+					"select actual_qty from `tabBin` where item_code = %s and warehouse = %s",
+					(d.item_code, d.warehouse),
+					as_dict=1,
+				)
+				d.actual_qty = bin and flt(bin[0]["actual_qty"]) or 0
 
-		for d in self.get('packed_items'):
-			bin = frappe.db.sql("select actual_qty, projected_qty from `tabBin` where item_code =	%s and warehouse = %s", (d.item_code, d.warehouse), as_dict = 1)
-			d.actual_qty = bin and flt(bin[0]['actual_qty']) or 0
-			d.projected_qty = bin and flt(bin[0]['projected_qty']) or 0
+		for d in self.get("packed_items"):
+			bin = frappe.db.sql(
+				"select actual_qty, projected_qty from `tabBin` where item_code =	%s and warehouse = %s",
+				(d.item_code, d.warehouse),
+				as_dict=1,
+			)
+			d.actual_qty = bin and flt(bin[0]["actual_qty"]) or 0
+			d.projected_qty = bin and flt(bin[0]["projected_qty"]) or 0
 
 	def update_packing_list(self):
 		if cint(self.update_stock) == 1:
-			if cint(self.is_return) and self.return_against:
-				calculate_mapped_packed_items_return(self)
-			else:
-				from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
-				make_packing_list(self)
+			from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
+
+			make_packing_list(self)
 		else:
-			self.set('packed_items', [])
+			self.set("packed_items", [])
 
 	def set_billing_hours_and_amount(self):
 		if not self.project:
 			for timesheet in self.timesheets:
-				ts_doc = frappe.get_doc('Timesheet', timesheet.time_sheet)
+				ts_doc = frappe.get_doc("Timesheet", timesheet.time_sheet)
 				if not timesheet.billing_hours and ts_doc.total_billable_hours:
 					timesheet.billing_hours = ts_doc.total_billable_hours
 
@@ -743,17 +862,20 @@
 
 	@frappe.whitelist()
 	def add_timesheet_data(self):
-		self.set('timesheets', [])
+		self.set("timesheets", [])
 		if self.project:
 			for data in get_projectwise_timesheet_data(self.project):
-				self.append('timesheets', {
-						'time_sheet': data.time_sheet,
-						'billing_hours': data.billing_hours,
-						'billing_amount': data.billing_amount,
-						'timesheet_detail': data.name,
-						'activity_type': data.activity_type,
-						'description': data.description
-					})
+				self.append(
+					"timesheets",
+					{
+						"time_sheet": data.time_sheet,
+						"billing_hours": data.billing_hours,
+						"billing_amount": data.billing_amount,
+						"timesheet_detail": data.name,
+						"activity_type": data.activity_type,
+						"description": data.description,
+					},
+				)
 
 			self.calculate_billing_amount_for_timesheet()
 
@@ -765,13 +887,19 @@
 		self.total_billing_hours = timesheet_sum("billing_hours")
 
 	def get_warehouse(self):
-		user_pos_profile = frappe.db.sql("""select name, warehouse from `tabPOS Profile`
-			where ifnull(user,'') = %s and company = %s""", (frappe.session['user'], self.company))
+		user_pos_profile = frappe.db.sql(
+			"""select name, warehouse from `tabPOS Profile`
+			where ifnull(user,'') = %s and company = %s""",
+			(frappe.session["user"], self.company),
+		)
 		warehouse = user_pos_profile[0][1] if user_pos_profile else None
 
 		if not warehouse:
-			global_pos_profile = frappe.db.sql("""select name, warehouse from `tabPOS Profile`
-				where (user is null or user = '') and company = %s""", self.company)
+			global_pos_profile = frappe.db.sql(
+				"""select name, warehouse from `tabPOS Profile`
+				where (user is null or user = '') and company = %s""",
+				self.company,
+			)
 
 			if global_pos_profile:
 				warehouse = global_pos_profile[0][1]
@@ -785,14 +913,16 @@
 		for d in self.get("items"):
 			if d.is_fixed_asset:
 				if not disposal_account:
-					disposal_account, depreciation_cost_center = get_disposal_account_and_cost_center(self.company)
+					disposal_account, depreciation_cost_center = get_disposal_account_and_cost_center(
+						self.company
+					)
 
 				d.income_account = disposal_account
 				if not d.cost_center:
 					d.cost_center = depreciation_cost_center
 
 	def check_prev_docstatus(self):
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if d.sales_order and frappe.db.get_value("Sales Order", d.sales_order, "docstatus") != 1:
 				frappe.throw(_("Sales Order {0} is not submitted").format(d.sales_order))
 
@@ -808,22 +938,35 @@
 
 		if gl_entries:
 			# if POS and amount is written off, updating outstanding amt after posting all gl entries
-			update_outstanding = "No" if (cint(self.is_pos) or self.write_off_account or
-				cint(self.redeem_loyalty_points)) else "Yes"
+			update_outstanding = (
+				"No"
+				if (cint(self.is_pos) or self.write_off_account or cint(self.redeem_loyalty_points))
+				else "Yes"
+			)
 
 			if self.docstatus == 1:
-				make_gl_entries(gl_entries, update_outstanding=update_outstanding, merge_entries=False, from_repost=from_repost)
+				make_gl_entries(
+					gl_entries,
+					update_outstanding=update_outstanding,
+					merge_entries=False,
+					from_repost=from_repost,
+				)
 			elif self.docstatus == 2:
 				make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
 
 			if update_outstanding == "No":
 				from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
-				update_outstanding_amt(self.debit_to, "Customer", self.customer,
-					self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name)
 
-		elif self.docstatus == 2 and cint(self.update_stock) \
-			and cint(auto_accounting_for_stock):
-				make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
+				update_outstanding_amt(
+					self.debit_to,
+					"Customer",
+					self.customer,
+					self.doctype,
+					self.return_against if cint(self.is_return) and self.return_against else self.name,
+				)
+
+		elif self.docstatus == 2 and cint(self.update_stock) and cint(auto_accounting_for_stock):
+			make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
 
 	def get_gl_entries(self, warehouse_account=None):
 		from erpnext.accounts.general_ledger import merge_similar_entries
@@ -853,31 +996,46 @@
 	def make_customer_gl_entry(self, gl_entries):
 		# Checked both rounding_adjustment and rounded_total
 		# because rounded_total had value even before introcution of posting GLE based on rounded total
-		grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
-		base_grand_total = flt(self.base_rounded_total if (self.base_rounding_adjustment and self.base_rounded_total)
-			else self.base_grand_total, self.precision("base_grand_total"))
+		grand_total = (
+			self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
+		)
+		base_grand_total = flt(
+			self.base_rounded_total
+			if (self.base_rounding_adjustment and self.base_rounded_total)
+			else self.base_grand_total,
+			self.precision("base_grand_total"),
+		)
 
 		if grand_total and not self.is_internal_transfer():
 			# Didnot use base_grand_total to book rounding loss gle
 			gl_entries.append(
-				self.get_gl_dict({
-					"account": self.debit_to,
-					"party_type": "Customer",
-					"party": self.customer,
-					"due_date": self.due_date,
-					"against": self.against_income_account,
-					"debit": base_grand_total,
-					"debit_in_account_currency": base_grand_total \
-						if self.party_account_currency==self.company_currency else grand_total,
-					"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
-					"against_voucher_type": self.doctype,
-					"cost_center": self.cost_center,
-					"project": self.project
-				}, self.party_account_currency, item=self)
+				self.get_gl_dict(
+					{
+						"account": self.debit_to,
+						"party_type": "Customer",
+						"party": self.customer,
+						"due_date": self.due_date,
+						"against": self.against_income_account,
+						"debit": base_grand_total,
+						"debit_in_account_currency": base_grand_total
+						if self.party_account_currency == self.company_currency
+						else grand_total,
+						"against_voucher": self.return_against
+						if cint(self.is_return) and self.return_against
+						else self.name,
+						"against_voucher_type": self.doctype,
+						"cost_center": self.cost_center,
+						"project": self.project,
+					},
+					self.party_account_currency,
+					item=self,
+				)
 			)
 
 	def make_tax_gl_entries(self, gl_entries):
-		enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting'))
+		enable_discount_accounting = cint(
+			frappe.db.get_single_value("Selling Settings", "enable_discount_accounting")
+		)
 
 		for tax in self.get("taxes"):
 			amount, base_amount = self.get_tax_amounts(tax, enable_discount_accounting)
@@ -885,33 +1043,45 @@
 			if flt(tax.base_tax_amount_after_discount_amount):
 				account_currency = get_account_currency(tax.account_head)
 				gl_entries.append(
-					self.get_gl_dict({
-						"account": tax.account_head,
-						"against": self.customer,
-						"credit": flt(base_amount,
-							tax.precision("tax_amount_after_discount_amount")),
-						"credit_in_account_currency": (flt(base_amount,
-							tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else
-							flt(amount, tax.precision("tax_amount_after_discount_amount"))),
-						"cost_center": tax.cost_center
-					}, account_currency, item=tax)
+					self.get_gl_dict(
+						{
+							"account": tax.account_head,
+							"against": self.customer,
+							"credit": flt(base_amount, tax.precision("tax_amount_after_discount_amount")),
+							"credit_in_account_currency": (
+								flt(base_amount, tax.precision("base_tax_amount_after_discount_amount"))
+								if account_currency == self.company_currency
+								else flt(amount, tax.precision("tax_amount_after_discount_amount"))
+							),
+							"cost_center": tax.cost_center,
+						},
+						account_currency,
+						item=tax,
+					)
 				)
 
 	def make_internal_transfer_gl_entries(self, gl_entries):
 		if self.is_internal_transfer() and flt(self.base_total_taxes_and_charges):
 			account_currency = get_account_currency(self.unrealized_profit_loss_account)
 			gl_entries.append(
-				self.get_gl_dict({
-					"account": self.unrealized_profit_loss_account,
-					"against": self.customer,
-					"debit": flt(self.total_taxes_and_charges),
-					"debit_in_account_currency": flt(self.base_total_taxes_and_charges),
-					"cost_center": self.cost_center
-				}, account_currency, item=self))
+				self.get_gl_dict(
+					{
+						"account": self.unrealized_profit_loss_account,
+						"against": self.customer,
+						"debit": flt(self.total_taxes_and_charges),
+						"debit_in_account_currency": flt(self.base_total_taxes_and_charges),
+						"cost_center": self.cost_center,
+					},
+					account_currency,
+					item=self,
+				)
+			)
 
 	def make_item_gl_entries(self, gl_entries):
 		# income account gl entries
-		enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting'))
+		enable_discount_accounting = cint(
+			frappe.db.get_single_value("Selling Settings", "enable_discount_accounting")
+		)
 
 		for item in self.get("items"):
 			if flt(item.base_net_amount, item.precision("base_net_amount")):
@@ -919,8 +1089,9 @@
 					asset = self.get_asset(item)
 
 					if self.is_return:
-						fixed_asset_gl_entries = get_gl_entries_on_asset_regain(asset,
-							item.base_net_amount, item.finance_book)
+						fixed_asset_gl_entries = get_gl_entries_on_asset_regain(
+							asset, item.base_net_amount, item.finance_book
+						)
 						asset.db_set("disposal_date", None)
 
 						if asset.calculate_depreciation:
@@ -928,8 +1099,9 @@
 							self.reset_depreciation_schedule(asset)
 
 					else:
-						fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset,
-							item.base_net_amount, item.finance_book)
+						fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(
+							asset, item.base_net_amount, item.finance_book
+						)
 						asset.db_set("disposal_date", self.posting_date)
 
 						if asset.calculate_depreciation:
@@ -944,47 +1116,57 @@
 				else:
 					# Do not book income for transfer within same company
 					if not self.is_internal_transfer():
-						income_account = (item.income_account
-							if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account)
+						income_account = (
+							item.income_account
+							if (not item.enable_deferred_revenue or self.is_return)
+							else item.deferred_revenue_account
+						)
 
 						amount, base_amount = self.get_amount_and_base_amount(item, enable_discount_accounting)
 
 						account_currency = get_account_currency(income_account)
 						gl_entries.append(
-							self.get_gl_dict({
-								"account": income_account,
-								"against": self.customer,
-								"credit": flt(base_amount, item.precision("base_net_amount")),
-								"credit_in_account_currency": (flt(base_amount, item.precision("base_net_amount"))
-									if account_currency==self.company_currency
-									else flt(amount, item.precision("net_amount"))),
-								"cost_center": item.cost_center,
-								"project": item.project or self.project
-							}, account_currency, item=item)
+							self.get_gl_dict(
+								{
+									"account": income_account,
+									"against": self.customer,
+									"credit": flt(base_amount, item.precision("base_net_amount")),
+									"credit_in_account_currency": (
+										flt(base_amount, item.precision("base_net_amount"))
+										if account_currency == self.company_currency
+										else flt(amount, item.precision("net_amount"))
+									),
+									"cost_center": item.cost_center,
+									"project": item.project or self.project,
+								},
+								account_currency,
+								item=item,
+							)
 						)
 
 		# expense account gl entries
-		if cint(self.update_stock) and \
-			erpnext.is_perpetual_inventory_enabled(self.company):
+		if cint(self.update_stock) and erpnext.is_perpetual_inventory_enabled(self.company):
 			gl_entries += super(SalesInvoice, self).get_gl_entries()
 
 	def get_asset(self, item):
-		if item.get('asset'):
+		if item.get("asset"):
 			asset = frappe.get_doc("Asset", item.asset)
 		else:
-			frappe.throw(_(
-				"Row #{0}: You must select an Asset for Item {1}.").format(item.idx, item.item_name),
-				title=_("Missing Asset")
+			frappe.throw(
+				_("Row #{0}: You must select an Asset for Item {1}.").format(item.idx, item.item_name),
+				title=_("Missing Asset"),
 			)
 
 		self.check_finance_books(item, asset)
 		return asset
 
 	def check_finance_books(self, item, asset):
-		if (len(asset.finance_books) > 1 and not item.finance_book
-			and asset.finance_books[0].finance_book):
-			frappe.throw(_("Select finance book for the item {0} at row {1}")
-				.format(item.item_code, item.idx))
+		if (
+			len(asset.finance_books) > 1 and not item.finance_book and asset.finance_books[0].finance_book
+		):
+			frappe.throw(
+				_("Select finance book for the item {0} at row {1}").format(item.item_code, item.idx)
+			)
 
 	def depreciate_asset(self, asset):
 		asset.flags.ignore_validate_update_after_submit = True
@@ -1004,14 +1186,12 @@
 
 	def modify_depreciation_schedule_for_asset_repairs(self, asset):
 		asset_repairs = frappe.get_all(
-			'Asset Repair',
-			filters = {'asset': asset.name},
-			fields = ['name', 'increase_in_asset_life']
+			"Asset Repair", filters={"asset": asset.name}, fields=["name", "increase_in_asset_life"]
 		)
 
 		for repair in asset_repairs:
 			if repair.increase_in_asset_life:
-				asset_repair = frappe.get_doc('Asset Repair', repair.name)
+				asset_repair = frappe.get_doc("Asset Repair", repair.name)
 				asset_repair.modify_depreciation_schedule()
 				asset.prepare_depreciation_data()
 
@@ -1021,8 +1201,8 @@
 		posting_date_of_original_invoice = self.get_posting_date_of_sales_invoice()
 
 		row = -1
-		finance_book = asset.get('schedules')[0].get('finance_book')
-		for schedule in asset.get('schedules'):
+		finance_book = asset.get("schedules")[0].get("finance_book")
+		for schedule in asset.get("schedules"):
 			if schedule.finance_book != finance_book:
 				row = 0
 				finance_book = schedule.finance_book
@@ -1030,8 +1210,9 @@
 				row += 1
 
 			if schedule.schedule_date == posting_date_of_original_invoice:
-				if not self.sale_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_invoice) \
-					or self.sale_happens_in_the_future(posting_date_of_original_invoice):
+				if not self.sale_was_made_on_original_schedule_date(
+					asset, schedule, row, posting_date_of_original_invoice
+				) or self.sale_happens_in_the_future(posting_date_of_original_invoice):
 
 					reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry)
 					reverse_journal_entry.posting_date = nowdate()
@@ -1046,14 +1227,17 @@
 					asset.save()
 
 	def get_posting_date_of_sales_invoice(self):
-		return frappe.db.get_value('Sales Invoice', self.return_against, 'posting_date')
+		return frappe.db.get_value("Sales Invoice", self.return_against, "posting_date")
 
 	# if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone
-	def sale_was_made_on_original_schedule_date(self, asset, schedule, row, posting_date_of_original_invoice):
-		for finance_book in asset.get('finance_books'):
+	def sale_was_made_on_original_schedule_date(
+		self, asset, schedule, row, posting_date_of_original_invoice
+	):
+		for finance_book in asset.get("finance_books"):
 			if schedule.finance_book == finance_book.finance_book:
-				orginal_schedule_date = add_months(finance_book.depreciation_start_date,
-					row * cint(finance_book.frequency_of_depreciation))
+				orginal_schedule_date = add_months(
+					finance_book.depreciation_start_date, row * cint(finance_book.frequency_of_depreciation)
+				)
 
 				if orginal_schedule_date == posting_date_of_original_invoice:
 					return True
@@ -1074,7 +1258,9 @@
 	@property
 	def enable_discount_accounting(self):
 		if not hasattr(self, "_enable_discount_accounting"):
-			self._enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting'))
+			self._enable_discount_accounting = cint(
+				frappe.db.get_single_value("Selling Settings", "enable_discount_accounting")
+			)
 
 		return self._enable_discount_accounting
 
@@ -1082,36 +1268,46 @@
 		if self.is_return:
 			asset.set_status()
 		else:
-			asset.set_status("Sold" if self.docstatus==1 else None)
+			asset.set_status("Sold" if self.docstatus == 1 else None)
 
 	def make_loyalty_point_redemption_gle(self, gl_entries):
 		if cint(self.redeem_loyalty_points):
 			gl_entries.append(
-				self.get_gl_dict({
-					"account": self.debit_to,
-					"party_type": "Customer",
-					"party": self.customer,
-					"against": "Expense account - " + cstr(self.loyalty_redemption_account) + " for the Loyalty Program",
-					"credit": self.loyalty_amount,
-					"against_voucher": self.return_against if cint(self.is_return) else self.name,
-					"against_voucher_type": self.doctype,
-					"cost_center": self.cost_center
-				}, item=self)
+				self.get_gl_dict(
+					{
+						"account": self.debit_to,
+						"party_type": "Customer",
+						"party": self.customer,
+						"against": "Expense account - "
+						+ cstr(self.loyalty_redemption_account)
+						+ " for the Loyalty Program",
+						"credit": self.loyalty_amount,
+						"against_voucher": self.return_against if cint(self.is_return) else self.name,
+						"against_voucher_type": self.doctype,
+						"cost_center": self.cost_center,
+					},
+					item=self,
+				)
 			)
 			gl_entries.append(
-				self.get_gl_dict({
-					"account": self.loyalty_redemption_account,
-					"cost_center": self.cost_center or self.loyalty_redemption_cost_center,
-					"against": self.customer,
-					"debit": self.loyalty_amount,
-					"remark": "Loyalty Points redeemed by the customer"
-				}, item=self)
+				self.get_gl_dict(
+					{
+						"account": self.loyalty_redemption_account,
+						"cost_center": self.cost_center or self.loyalty_redemption_cost_center,
+						"against": self.customer,
+						"debit": self.loyalty_amount,
+						"remark": "Loyalty Points redeemed by the customer",
+					},
+					item=self,
+				)
 			)
 
 	def make_pos_gl_entries(self, gl_entries):
 		if cint(self.is_pos):
 
-			skip_change_gl_entries = not cint(frappe.db.get_single_value('Accounts Settings', 'post_change_gl_entries'))
+			skip_change_gl_entries = not cint(
+				frappe.db.get_single_value("Accounts Settings", "post_change_gl_entries")
+			)
 
 			for payment_mode in self.payments:
 				if skip_change_gl_entries and payment_mode.account == self.account_for_change_amount:
@@ -1120,32 +1316,42 @@
 				if payment_mode.amount:
 					# POS, make payment entries
 					gl_entries.append(
-						self.get_gl_dict({
-							"account": self.debit_to,
-							"party_type": "Customer",
-							"party": self.customer,
-							"against": payment_mode.account,
-							"credit": payment_mode.base_amount,
-							"credit_in_account_currency": payment_mode.base_amount \
-								if self.party_account_currency==self.company_currency \
+						self.get_gl_dict(
+							{
+								"account": self.debit_to,
+								"party_type": "Customer",
+								"party": self.customer,
+								"against": payment_mode.account,
+								"credit": payment_mode.base_amount,
+								"credit_in_account_currency": payment_mode.base_amount
+								if self.party_account_currency == self.company_currency
 								else payment_mode.amount,
-							"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
-							"against_voucher_type": self.doctype,
-							"cost_center": self.cost_center
-						}, self.party_account_currency, item=self)
+								"against_voucher": self.return_against
+								if cint(self.is_return) and self.return_against
+								else self.name,
+								"against_voucher_type": self.doctype,
+								"cost_center": self.cost_center,
+							},
+							self.party_account_currency,
+							item=self,
+						)
 					)
 
 					payment_mode_account_currency = get_account_currency(payment_mode.account)
 					gl_entries.append(
-						self.get_gl_dict({
-							"account": payment_mode.account,
-							"against": self.customer,
-							"debit": payment_mode.base_amount,
-							"debit_in_account_currency": payment_mode.base_amount \
-								if payment_mode_account_currency==self.company_currency \
+						self.get_gl_dict(
+							{
+								"account": payment_mode.account,
+								"against": self.customer,
+								"debit": payment_mode.base_amount,
+								"debit_in_account_currency": payment_mode.base_amount
+								if payment_mode_account_currency == self.company_currency
 								else payment_mode.amount,
-							"cost_center": self.cost_center
-						}, payment_mode_account_currency, item=self)
+								"cost_center": self.cost_center,
+							},
+							payment_mode_account_currency,
+							item=self,
+						)
 					)
 
 			if not skip_change_gl_entries:
@@ -1155,94 +1361,129 @@
 		if self.change_amount:
 			if self.account_for_change_amount:
 				gl_entries.append(
-					self.get_gl_dict({
-						"account": self.debit_to,
-						"party_type": "Customer",
-						"party": self.customer,
-						"against": self.account_for_change_amount,
-						"debit": flt(self.base_change_amount),
-						"debit_in_account_currency": flt(self.base_change_amount) \
-							if self.party_account_currency==self.company_currency else flt(self.change_amount),
-						"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
-						"against_voucher_type": self.doctype,
-						"cost_center": self.cost_center,
-						"project": self.project
-					}, self.party_account_currency, item=self)
+					self.get_gl_dict(
+						{
+							"account": self.debit_to,
+							"party_type": "Customer",
+							"party": self.customer,
+							"against": self.account_for_change_amount,
+							"debit": flt(self.base_change_amount),
+							"debit_in_account_currency": flt(self.base_change_amount)
+							if self.party_account_currency == self.company_currency
+							else flt(self.change_amount),
+							"against_voucher": self.return_against
+							if cint(self.is_return) and self.return_against
+							else self.name,
+							"against_voucher_type": self.doctype,
+							"cost_center": self.cost_center,
+							"project": self.project,
+						},
+						self.party_account_currency,
+						item=self,
+					)
 				)
 
 				gl_entries.append(
-					self.get_gl_dict({
-						"account": self.account_for_change_amount,
-						"against": self.customer,
-						"credit": self.base_change_amount,
-						"cost_center": self.cost_center
-					}, item=self)
+					self.get_gl_dict(
+						{
+							"account": self.account_for_change_amount,
+							"against": self.customer,
+							"credit": self.base_change_amount,
+							"cost_center": self.cost_center,
+						},
+						item=self,
+					)
 				)
 			else:
-				frappe.throw(_("Select change amount account"), title="Mandatory Field")
+				frappe.throw(_("Select change amount account"), title=_("Mandatory Field"))
 
 	def make_write_off_gl_entry(self, gl_entries):
 		# write off entries, applicable if only pos
 		if self.write_off_account and flt(self.write_off_amount, self.precision("write_off_amount")):
 			write_off_account_currency = get_account_currency(self.write_off_account)
-			default_cost_center = frappe.get_cached_value('Company',  self.company,  'cost_center')
+			default_cost_center = frappe.get_cached_value("Company", self.company, "cost_center")
 
 			gl_entries.append(
-				self.get_gl_dict({
-					"account": self.debit_to,
-					"party_type": "Customer",
-					"party": self.customer,
-					"against": self.write_off_account,
-					"credit": flt(self.base_write_off_amount, self.precision("base_write_off_amount")),
-					"credit_in_account_currency": (flt(self.base_write_off_amount,
-						self.precision("base_write_off_amount")) if self.party_account_currency==self.company_currency
-						else flt(self.write_off_amount, self.precision("write_off_amount"))),
-					"against_voucher": self.return_against if cint(self.is_return) else self.name,
-					"against_voucher_type": self.doctype,
-					"cost_center": self.cost_center,
-					"project": self.project
-				}, self.party_account_currency, item=self)
+				self.get_gl_dict(
+					{
+						"account": self.debit_to,
+						"party_type": "Customer",
+						"party": self.customer,
+						"against": self.write_off_account,
+						"credit": flt(self.base_write_off_amount, self.precision("base_write_off_amount")),
+						"credit_in_account_currency": (
+							flt(self.base_write_off_amount, self.precision("base_write_off_amount"))
+							if self.party_account_currency == self.company_currency
+							else flt(self.write_off_amount, self.precision("write_off_amount"))
+						),
+						"against_voucher": self.return_against if cint(self.is_return) else self.name,
+						"against_voucher_type": self.doctype,
+						"cost_center": self.cost_center,
+						"project": self.project,
+					},
+					self.party_account_currency,
+					item=self,
+				)
 			)
 			gl_entries.append(
-				self.get_gl_dict({
-					"account": self.write_off_account,
-					"against": self.customer,
-					"debit": flt(self.base_write_off_amount, self.precision("base_write_off_amount")),
-					"debit_in_account_currency": (flt(self.base_write_off_amount,
-						self.precision("base_write_off_amount")) if write_off_account_currency==self.company_currency
-						else flt(self.write_off_amount, self.precision("write_off_amount"))),
-					"cost_center": self.cost_center or self.write_off_cost_center or default_cost_center
-				}, write_off_account_currency, item=self)
+				self.get_gl_dict(
+					{
+						"account": self.write_off_account,
+						"against": self.customer,
+						"debit": flt(self.base_write_off_amount, self.precision("base_write_off_amount")),
+						"debit_in_account_currency": (
+							flt(self.base_write_off_amount, self.precision("base_write_off_amount"))
+							if write_off_account_currency == self.company_currency
+							else flt(self.write_off_amount, self.precision("write_off_amount"))
+						),
+						"cost_center": self.cost_center or self.write_off_cost_center or default_cost_center,
+					},
+					write_off_account_currency,
+					item=self,
+				)
 			)
 
 	def make_gle_for_rounding_adjustment(self, gl_entries):
-		if flt(self.rounding_adjustment, self.precision("rounding_adjustment")) and self.base_rounding_adjustment \
-			and not self.is_internal_transfer():
-			round_off_account, round_off_cost_center = \
-				get_round_off_account_and_cost_center(self.company)
+		if (
+			flt(self.rounding_adjustment, self.precision("rounding_adjustment"))
+			and self.base_rounding_adjustment
+			and not self.is_internal_transfer()
+		):
+			round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
+				self.company, "Sales Invoice", self.name
+			)
 
 			gl_entries.append(
-				self.get_gl_dict({
-					"account": round_off_account,
-					"against": self.customer,
-					"credit_in_account_currency": flt(self.rounding_adjustment,
-						self.precision("rounding_adjustment")),
-					"credit": flt(self.base_rounding_adjustment,
-						self.precision("base_rounding_adjustment")),
-					"cost_center": self.cost_center or round_off_cost_center,
-				}, item=self))
+				self.get_gl_dict(
+					{
+						"account": round_off_account,
+						"against": self.customer,
+						"credit_in_account_currency": flt(
+							self.rounding_adjustment, self.precision("rounding_adjustment")
+						),
+						"credit": flt(self.base_rounding_adjustment, self.precision("base_rounding_adjustment")),
+						"cost_center": self.cost_center or round_off_cost_center,
+					},
+					item=self,
+				)
+			)
 
 	def update_billing_status_in_dn(self, update_modified=True):
 		updated_delivery_notes = []
 		for d in self.get("items"):
-			if d.so_detail:
-				updated_delivery_notes += update_billed_amount_based_on_so(d.so_detail, update_modified)
-			elif d.dn_detail:
-				billed_amt = frappe.db.sql("""select sum(amount) from `tabSales Invoice Item`
-					where dn_detail=%s and docstatus=1""", d.dn_detail)
+			if d.dn_detail:
+				billed_amt = frappe.db.sql(
+					"""select sum(amount) from `tabSales Invoice Item`
+					where dn_detail=%s and docstatus=1""",
+					d.dn_detail,
+				)
 				billed_amt = billed_amt and billed_amt[0][0] or 0
-				frappe.db.set_value("Delivery Note Item", d.dn_detail, "billed_amt", billed_amt, update_modified=update_modified)
+				frappe.db.set_value(
+					"Delivery Note Item", d.dn_detail, "billed_amt", billed_amt, update_modified=update_modified
+				)
 				updated_delivery_notes.append(d.delivery_note)
+			elif d.so_detail:
+				updated_delivery_notes += update_billed_amount_based_on_so(d.so_detail, update_modified)
 
 		for dn in set(updated_delivery_notes):
 			frappe.get_doc("Delivery Note", dn).update_billing_percentage(update_modified=update_modified)
@@ -1252,7 +1493,7 @@
 		self.due_date = None
 
 	def update_serial_no(self, in_cancel=False):
-		""" update Sales Invoice refrence in Serial No """
+		"""update Sales Invoice refrence in Serial No"""
 		invoice = None if (in_cancel or self.is_return) else self.name
 		if in_cancel and self.is_return:
 			invoice = self.return_against
@@ -1262,26 +1503,25 @@
 				continue
 
 			for serial_no in get_serial_nos(item.serial_no):
-				if serial_no and frappe.db.get_value('Serial No', serial_no, 'item_code') == item.item_code:
-					frappe.db.set_value('Serial No', serial_no, 'sales_invoice', invoice)
+				if serial_no and frappe.db.get_value("Serial No", serial_no, "item_code") == item.item_code:
+					frappe.db.set_value("Serial No", serial_no, "sales_invoice", invoice)
 
 	def validate_serial_numbers(self):
 		"""
-			validate serial number agains Delivery Note and Sales Invoice
+		validate serial number agains Delivery Note and Sales Invoice
 		"""
 		self.set_serial_no_against_delivery_note()
 		self.validate_serial_against_delivery_note()
 
 	def set_serial_no_against_delivery_note(self):
 		for item in self.items:
-			if item.serial_no and item.delivery_note and \
-				item.qty != len(get_serial_nos(item.serial_no)):
+			if item.serial_no and item.delivery_note and item.qty != len(get_serial_nos(item.serial_no)):
 				item.serial_no = get_delivery_note_serial_no(item.item_code, item.qty, item.delivery_note)
 
 	def validate_serial_against_delivery_note(self):
 		"""
-			validate if the serial numbers in Sales Invoice Items are same as in
-			Delivery Note Item
+		validate if the serial numbers in Sales Invoice Items are same as in
+		Delivery Note Item
 		"""
 
 		for item in self.items:
@@ -1300,14 +1540,18 @@
 				serial_no_msg = ", ".join(frappe.bold(d) for d in serial_no_diff)
 
 				msg = _("Row #{0}: The following Serial Nos are not present in Delivery Note {1}:").format(
-					item.idx, dn_link)
+					item.idx, dn_link
+				)
 				msg += " " + serial_no_msg
 
 				frappe.throw(msg=msg, title=_("Serial Nos Mismatch"))
 
 			if item.serial_no and cint(item.qty) != len(si_serial_nos):
-				frappe.throw(_("Row #{0}: {1} Serial numbers required for Item {2}. You have provided {3}.").format(
-					item.idx, item.qty, item.item_code, len(si_serial_nos)))
+				frappe.throw(
+					_("Row #{0}: {1} Serial numbers required for Item {2}. You have provided {3}.").format(
+						item.idx, item.qty, item.item_code, len(si_serial_nos)
+					)
+				)
 
 	def update_project(self):
 		if self.project:
@@ -1315,7 +1559,6 @@
 			project.update_billed_amount()
 			project.db_update()
 
-
 	def verify_payment_amount_is_positive(self):
 		for entry in self.payments:
 			if entry.amount < 0:
@@ -1331,63 +1574,90 @@
 		returned_amount = self.get_returned_amount()
 		current_amount = flt(self.grand_total) - cint(self.loyalty_amount)
 		eligible_amount = current_amount - returned_amount
-		lp_details = get_loyalty_program_details_with_points(self.customer, company=self.company,
-			current_transaction_amount=current_amount, loyalty_program=self.loyalty_program,
-			expiry_date=self.posting_date, include_expired_entry=True)
-		if lp_details and getdate(lp_details.from_date) <= getdate(self.posting_date) and \
-			(not lp_details.to_date or getdate(lp_details.to_date) >= getdate(self.posting_date)):
+		lp_details = get_loyalty_program_details_with_points(
+			self.customer,
+			company=self.company,
+			current_transaction_amount=current_amount,
+			loyalty_program=self.loyalty_program,
+			expiry_date=self.posting_date,
+			include_expired_entry=True,
+		)
+		if (
+			lp_details
+			and getdate(lp_details.from_date) <= getdate(self.posting_date)
+			and (not lp_details.to_date or getdate(lp_details.to_date) >= getdate(self.posting_date))
+		):
 
 			collection_factor = lp_details.collection_factor if lp_details.collection_factor else 1.0
-			points_earned = cint(eligible_amount/collection_factor)
+			points_earned = cint(eligible_amount / collection_factor)
 
-			doc = frappe.get_doc({
-				"doctype": "Loyalty Point Entry",
-				"company": self.company,
-				"loyalty_program": lp_details.loyalty_program,
-				"loyalty_program_tier": lp_details.tier_name,
-				"customer": self.customer,
-				"invoice_type": self.doctype,
-				"invoice": self.name,
-				"loyalty_points": points_earned,
-				"purchase_amount": eligible_amount,
-				"expiry_date": add_days(self.posting_date, lp_details.expiry_duration),
-				"posting_date": self.posting_date
-			})
+			doc = frappe.get_doc(
+				{
+					"doctype": "Loyalty Point Entry",
+					"company": self.company,
+					"loyalty_program": lp_details.loyalty_program,
+					"loyalty_program_tier": lp_details.tier_name,
+					"customer": self.customer,
+					"invoice_type": self.doctype,
+					"invoice": self.name,
+					"loyalty_points": points_earned,
+					"purchase_amount": eligible_amount,
+					"expiry_date": add_days(self.posting_date, lp_details.expiry_duration),
+					"posting_date": self.posting_date,
+				}
+			)
 			doc.flags.ignore_permissions = 1
 			doc.save()
 			self.set_loyalty_program_tier()
 
 	# valdite the redemption and then delete the loyalty points earned on cancel of the invoice
 	def delete_loyalty_point_entry(self):
-		lp_entry = frappe.db.sql("select name from `tabLoyalty Point Entry` where invoice=%s",
-			(self.name), as_dict=1)
+		lp_entry = frappe.db.sql(
+			"select name from `tabLoyalty Point Entry` where invoice=%s", (self.name), as_dict=1
+		)
 
-		if not lp_entry: return
-		against_lp_entry = frappe.db.sql('''select name, invoice from `tabLoyalty Point Entry`
-			where redeem_against=%s''', (lp_entry[0].name), as_dict=1)
+		if not lp_entry:
+			return
+		against_lp_entry = frappe.db.sql(
+			"""select name, invoice from `tabLoyalty Point Entry`
+			where redeem_against=%s""",
+			(lp_entry[0].name),
+			as_dict=1,
+		)
 		if against_lp_entry:
 			invoice_list = ", ".join([d.invoice for d in against_lp_entry])
 			frappe.throw(
-				_('''{} can't be cancelled since the Loyalty Points earned has been redeemed. First cancel the {} No {}''')
-				.format(self.doctype, self.doctype, invoice_list)
+				_(
+					"""{} can't be cancelled since the Loyalty Points earned has been redeemed. First cancel the {} No {}"""
+				).format(self.doctype, self.doctype, invoice_list)
 			)
 		else:
-			frappe.db.sql('''delete from `tabLoyalty Point Entry` where invoice=%s''', (self.name))
+			frappe.db.sql("""delete from `tabLoyalty Point Entry` where invoice=%s""", (self.name))
 			# Set loyalty program
 			self.set_loyalty_program_tier()
 
 	def set_loyalty_program_tier(self):
-		lp_details = get_loyalty_program_details_with_points(self.customer, company=self.company,
-				loyalty_program=self.loyalty_program, include_expired_entry=True)
+		lp_details = get_loyalty_program_details_with_points(
+			self.customer,
+			company=self.company,
+			loyalty_program=self.loyalty_program,
+			include_expired_entry=True,
+		)
 		frappe.db.set_value("Customer", self.customer, "loyalty_program_tier", lp_details.tier_name)
 
 	def get_returned_amount(self):
-		returned_amount = frappe.db.sql("""
-			select sum(grand_total)
-			from `tabSales Invoice`
-			where docstatus=1 and is_return=1 and ifnull(return_against, '')=%s
-		""", self.name)
-		return abs(flt(returned_amount[0][0])) if returned_amount else 0
+		from frappe.query_builder.functions import Coalesce, Sum
+
+		doc = frappe.qb.DocType(self.doctype)
+		returned_amount = (
+			frappe.qb.from_(doc)
+			.select(Sum(doc.grand_total))
+			.where(
+				(doc.docstatus == 1) & (doc.is_return == 1) & (Coalesce(doc.return_against, "") == self.name)
+			)
+		).run()
+
+		return abs(returned_amount[0][0]) if returned_amount[0][0] else 0
 
 	# redeem the loyalty points.
 	def apply_loyalty_points(self):
@@ -1395,7 +1665,10 @@
 			get_loyalty_point_entries,
 			get_redemption_details,
 		)
-		loyalty_point_entries = get_loyalty_point_entries(self.customer, self.loyalty_program, self.company, self.posting_date)
+
+		loyalty_point_entries = get_loyalty_point_entries(
+			self.customer, self.loyalty_program, self.company, self.posting_date
+		)
 		redemption_details = get_redemption_details(self.customer, self.loyalty_program, self.company)
 
 		points_to_redeem = self.loyalty_points
@@ -1409,30 +1682,32 @@
 				redeemed_points = points_to_redeem
 			else:
 				redeemed_points = available_points
-			doc = frappe.get_doc({
-				"doctype": "Loyalty Point Entry",
-				"company": self.company,
-				"loyalty_program": self.loyalty_program,
-				"loyalty_program_tier": lp_entry.loyalty_program_tier,
-				"customer": self.customer,
-				"invoice_type": self.doctype,
-				"invoice": self.name,
-				"redeem_against": lp_entry.name,
-				"loyalty_points": -1*redeemed_points,
-				"purchase_amount": self.grand_total,
-				"expiry_date": lp_entry.expiry_date,
-				"posting_date": self.posting_date
-			})
+			doc = frappe.get_doc(
+				{
+					"doctype": "Loyalty Point Entry",
+					"company": self.company,
+					"loyalty_program": self.loyalty_program,
+					"loyalty_program_tier": lp_entry.loyalty_program_tier,
+					"customer": self.customer,
+					"invoice_type": self.doctype,
+					"invoice": self.name,
+					"redeem_against": lp_entry.name,
+					"loyalty_points": -1 * redeemed_points,
+					"purchase_amount": self.grand_total,
+					"expiry_date": lp_entry.expiry_date,
+					"posting_date": self.posting_date,
+				}
+			)
 			doc.flags.ignore_permissions = 1
 			doc.save()
 			points_to_redeem -= redeemed_points
-			if points_to_redeem < 1: # since points_to_redeem is integer
+			if points_to_redeem < 1:  # since points_to_redeem is integer
 				break
 
 	def set_status(self, update=False, status=None, update_modified=True):
 		if self.is_new():
-			if self.get('amended_from'):
-				self.status = 'Draft'
+			if self.get("amended_from"):
+				self.status = "Draft"
 			return
 
 		outstanding_amount = flt(self.outstanding_amount, self.precision("outstanding_amount"))
@@ -1443,7 +1718,7 @@
 				status = "Cancelled"
 			elif self.docstatus == 1:
 				if self.is_internal_transfer():
-					self.status = 'Internal Transfer'
+					self.status = "Internal Transfer"
 				elif is_overdue(self, total):
 					self.status = "Overdue"
 				elif 0 < outstanding_amount < total:
@@ -1451,11 +1726,17 @@
 				elif outstanding_amount > 0 and getdate(self.due_date) >= getdate():
 					self.status = "Unpaid"
 				# Check if outstanding amount is 0 due to credit note issued against invoice
-				elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
+				elif (
+					outstanding_amount <= 0
+					and self.is_return == 0
+					and frappe.db.get_value(
+						"Sales Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1}
+					)
+				):
 					self.status = "Credit Note Issued"
 				elif self.is_return == 1:
 					self.status = "Return"
-				elif outstanding_amount<=0:
+				elif outstanding_amount <= 0:
 					self.status = "Paid"
 				else:
 					self.status = "Submitted"
@@ -1471,34 +1752,29 @@
 				self.status = "Draft"
 
 		if update:
-			self.db_set('status', self.status, update_modified = update_modified)
+			self.db_set("status", self.status, update_modified=update_modified)
 
 
 def get_total_in_party_account_currency(doc):
-	total_fieldname = (
-		"grand_total"
-		if doc.disable_rounded_total
-		else "rounded_total"
-	)
+	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
 
 	today = getdate()
-	if doc.get('is_pos') or not doc.get('payment_schedule'):
+	if doc.get("is_pos") or not doc.get("payment_schedule"):
 		return getdate(doc.due_date) < today
 
 	# calculate payable amount till date
 	payment_amount_field = (
-		"base_payment_amount"
-		if doc.party_account_currency != doc.currency
-		else "payment_amount"
+		"base_payment_amount" if doc.party_account_currency != doc.currency else "payment_amount"
 	)
 
 	payable_amount = sum(
@@ -1513,7 +1789,8 @@
 def get_discounting_status(sales_invoice):
 	status = None
 
-	invoice_discounting_list = frappe.db.sql("""
+	invoice_discounting_list = frappe.db.sql(
+		"""
 		select status
 		from `tabInvoice Discounting` id, `tabDiscounted Invoice` d
 		where
@@ -1521,7 +1798,9 @@
 			and d.sales_invoice=%s
 			and id.docstatus=1
 			and status in ('Disbursed', 'Settled')
-	""", sales_invoice)
+	""",
+		sales_invoice,
+	)
 
 	for d in invoice_discounting_list:
 		status = d[0]
@@ -1530,6 +1809,7 @@
 
 	return status
 
+
 def validate_inter_company_party(doctype, party, company, inter_company_reference):
 	if not party:
 		return
@@ -1558,10 +1838,19 @@
 			frappe.throw(_("Invalid Company for Inter Company Transaction."))
 
 	elif frappe.db.get_value(partytype, {"name": party, internal: 1}, "name") == party:
-		companies = frappe.get_all("Allowed To Transact With", fields=["company"], filters={"parenttype": partytype, "parent": party})
+		companies = frappe.get_all(
+			"Allowed To Transact With",
+			fields=["company"],
+			filters={"parenttype": partytype, "parent": party},
+		)
 		companies = [d.company for d in companies]
 		if not company in companies:
-			frappe.throw(_("{0} not allowed to transact with {1}. Please change the Company.").format(partytype, company))
+			frappe.throw(
+				_("{0} not allowed to transact with {1}. Please change the Company.").format(
+					partytype, company
+				)
+			)
+
 
 def update_linked_doc(doctype, name, inter_company_reference):
 
@@ -1571,8 +1860,8 @@
 		ref_field = "inter_company_order_reference"
 
 	if inter_company_reference:
-		frappe.db.set_value(doctype, inter_company_reference,\
-			ref_field, name)
+		frappe.db.set_value(doctype, inter_company_reference, ref_field, name)
+
 
 def unlink_inter_company_doc(doctype, name, inter_company_reference):
 
@@ -1587,48 +1876,57 @@
 		frappe.db.set_value(doctype, name, ref_field, "")
 		frappe.db.set_value(ref_doc, inter_company_reference, ref_field, "")
 
+
 def get_list_context(context=None):
 	from erpnext.controllers.website_list_for_contact import get_list_context
+
 	list_context = get_list_context(context)
-	list_context.update({
-		'show_sidebar': True,
-		'show_search': True,
-		'no_breadcrumbs': True,
-		'title': _('Invoices'),
-	})
+	list_context.update(
+		{
+			"show_sidebar": True,
+			"show_search": True,
+			"no_breadcrumbs": True,
+			"title": _("Invoices"),
+		}
+	)
 	return list_context
 
+
 @frappe.whitelist()
 def get_bank_cash_account(mode_of_payment, company):
-	account = frappe.db.get_value("Mode of Payment Account",
-		{"parent": mode_of_payment, "company": company}, "default_account")
+	account = frappe.db.get_value(
+		"Mode of Payment Account", {"parent": mode_of_payment, "company": company}, "default_account"
+	)
 	if not account:
-		frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
-			.format(get_link_to_form("Mode of Payment", mode_of_payment)), title=_("Missing Account"))
-	return {
-		"account": account
-	}
+		frappe.throw(
+			_("Please set default Cash or Bank account in Mode of Payment {0}").format(
+				get_link_to_form("Mode of Payment", mode_of_payment)
+			),
+			title=_("Missing Account"),
+		)
+	return {"account": account}
+
 
 @frappe.whitelist()
 def make_maintenance_schedule(source_name, target_doc=None):
-	doclist = get_mapped_doc("Sales Invoice", source_name, 	{
-		"Sales Invoice": {
-			"doctype": "Maintenance Schedule",
-			"validation": {
-				"docstatus": ["=", 1]
-			}
+	doclist = get_mapped_doc(
+		"Sales Invoice",
+		source_name,
+		{
+			"Sales Invoice": {"doctype": "Maintenance Schedule", "validation": {"docstatus": ["=", 1]}},
+			"Sales Invoice Item": {
+				"doctype": "Maintenance Schedule Item",
+			},
 		},
-		"Sales Invoice Item": {
-			"doctype": "Maintenance Schedule Item",
-		},
-	}, target_doc)
+		target_doc,
+	)
 
 	return doclist
 
+
 @frappe.whitelist()
 def make_delivery_note(source_name, target_doc=None):
 	def set_missing_values(source, target):
-		target.ignore_pricing_rule = 1
 		target.run_method("set_missing_values")
 		target.run_method("set_po_nos")
 		target.run_method("calculate_taxes_and_totals")
@@ -1640,82 +1938,104 @@
 		target_doc.base_amount = target_doc.qty * flt(source_doc.base_rate)
 		target_doc.amount = target_doc.qty * flt(source_doc.rate)
 
-	doclist = get_mapped_doc("Sales Invoice", source_name, 	{
-		"Sales Invoice": {
-			"doctype": "Delivery Note",
-			"validation": {
-				"docstatus": ["=", 1]
-			}
-		},
-		"Sales Invoice Item": {
-			"doctype": "Delivery Note Item",
-			"field_map": {
-				"name": "si_detail",
-				"parent": "against_sales_invoice",
-				"serial_no": "serial_no",
-				"sales_order": "against_sales_order",
-				"so_detail": "so_detail",
-				"cost_center": "cost_center"
+	doclist = get_mapped_doc(
+		"Sales Invoice",
+		source_name,
+		{
+			"Sales Invoice": {"doctype": "Delivery Note", "validation": {"docstatus": ["=", 1]}},
+			"Sales Invoice Item": {
+				"doctype": "Delivery Note Item",
+				"field_map": {
+					"name": "si_detail",
+					"parent": "against_sales_invoice",
+					"serial_no": "serial_no",
+					"sales_order": "against_sales_order",
+					"so_detail": "so_detail",
+					"cost_center": "cost_center",
+				},
+				"postprocess": update_item,
+				"condition": lambda doc: doc.delivered_by_supplier != 1,
 			},
-			"postprocess": update_item,
-			"condition": lambda doc: doc.delivered_by_supplier!=1
-		},
-		"Sales Taxes and Charges": {
-			"doctype": "Sales Taxes and Charges",
-			"add_if_empty": True
-		},
-		"Sales Team": {
-			"doctype": "Sales Team",
-			"field_map": {
-				"incentives": "incentives"
+			"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True},
+			"Sales Team": {
+				"doctype": "Sales Team",
+				"field_map": {"incentives": "incentives"},
+				"add_if_empty": True,
 			},
-			"add_if_empty": True
-		}
-	}, target_doc, set_missing_values)
+		},
+		target_doc,
+		set_missing_values,
+	)
 
+	doclist.set_onload("ignore_price_list", True)
 	return doclist
 
+
 @frappe.whitelist()
 def make_sales_return(source_name, target_doc=None):
 	from erpnext.controllers.sales_and_purchase_return import make_return_doc
+
 	return make_return_doc("Sales Invoice", source_name, target_doc)
 
+
 def set_account_for_mode_of_payment(self):
 	for data in self.payments:
 		if not data.account:
 			data.account = get_bank_cash_account(data.mode_of_payment, self.company).get("account")
 
+
 def get_inter_company_details(doc, doctype):
 	if doctype in ["Sales Invoice", "Sales Order", "Delivery Note"]:
-		parties = frappe.db.get_all("Supplier", fields=["name"], filters={"disabled": 0, "is_internal_supplier": 1, "represents_company": doc.company})
+		parties = frappe.db.get_all(
+			"Supplier",
+			fields=["name"],
+			filters={"disabled": 0, "is_internal_supplier": 1, "represents_company": doc.company},
+		)
 		company = frappe.get_cached_value("Customer", doc.customer, "represents_company")
 
 		if not parties:
-			frappe.throw(_('No Supplier found for Inter Company Transactions which represents company {0}').format(frappe.bold(doc.company)))
+			frappe.throw(
+				_("No Supplier found for Inter Company Transactions which represents company {0}").format(
+					frappe.bold(doc.company)
+				)
+			)
 
 		party = get_internal_party(parties, "Supplier", doc)
 	else:
-		parties = frappe.db.get_all("Customer", fields=["name"], filters={"disabled": 0, "is_internal_customer": 1, "represents_company": doc.company})
+		parties = frappe.db.get_all(
+			"Customer",
+			fields=["name"],
+			filters={"disabled": 0, "is_internal_customer": 1, "represents_company": doc.company},
+		)
 		company = frappe.get_cached_value("Supplier", doc.supplier, "represents_company")
 
 		if not parties:
-			frappe.throw(_('No Customer found for Inter Company Transactions which represents company {0}').format(frappe.bold(doc.company)))
+			frappe.throw(
+				_("No Customer found for Inter Company Transactions which represents company {0}").format(
+					frappe.bold(doc.company)
+				)
+			)
 
 		party = get_internal_party(parties, "Customer", doc)
 
-	return {
-		"party": party,
-		"company": company
-	}
+	return {"party": party, "company": company}
+
 
 def get_internal_party(parties, link_doctype, doc):
 	if len(parties) == 1:
-			party = parties[0].name
+		party = parties[0].name
 	else:
 		# If more than one Internal Supplier/Customer, get supplier/customer on basis of address
-		if doc.get('company_address') or doc.get('shipping_address'):
-			party = frappe.db.get_value("Dynamic Link", {"parent": doc.get('company_address') or doc.get('shipping_address'),
-			"parenttype": "Address", "link_doctype": link_doctype}, "link_name")
+		if doc.get("company_address") or doc.get("shipping_address"):
+			party = frappe.db.get_value(
+				"Dynamic Link",
+				{
+					"parent": doc.get("company_address") or doc.get("shipping_address"),
+					"parenttype": "Address",
+					"link_doctype": link_doctype,
+				},
+				"link_name",
+			)
 
 			if not party:
 				party = parties[0].name
@@ -1724,11 +2044,18 @@
 
 	return party
 
+
 def validate_inter_company_transaction(doc, doctype):
 
 	details = get_inter_company_details(doc, doctype)
-	price_list = doc.selling_price_list if doctype in ["Sales Invoice", "Sales Order", "Delivery Note"] else doc.buying_price_list
-	valid_price_list = frappe.db.get_value("Price List", {"name": price_list, "buying": 1, "selling": 1})
+	price_list = (
+		doc.selling_price_list
+		if doctype in ["Sales Invoice", "Sales Order", "Delivery Note"]
+		else doc.buying_price_list
+	)
+	valid_price_list = frappe.db.get_value(
+		"Price List", {"name": price_list, "buying": 1, "selling": 1}
+	)
 	if not valid_price_list and not doc.is_internal_transfer():
 		frappe.throw(_("Selected Price List should have buying and selling fields checked."))
 
@@ -1738,28 +2065,32 @@
 		frappe.throw(_("No {0} found for Inter Company Transactions.").format(partytype))
 
 	company = details.get("company")
-	default_currency = frappe.get_cached_value('Company', company, "default_currency")
+	default_currency = frappe.get_cached_value("Company", company, "default_currency")
 	if default_currency != doc.currency:
-		frappe.throw(_("Company currencies of both the companies should match for Inter Company Transactions."))
+		frappe.throw(
+			_("Company currencies of both the companies should match for Inter Company Transactions.")
+		)
 
 	return
 
+
 @frappe.whitelist()
 def make_inter_company_purchase_invoice(source_name, target_doc=None):
 	return make_inter_company_transaction("Sales Invoice", source_name, target_doc)
 
+
 def make_inter_company_transaction(doctype, source_name, target_doc=None):
 	if doctype in ["Sales Invoice", "Sales Order"]:
 		source_doc = frappe.get_doc(doctype, source_name)
 		target_doctype = "Purchase Invoice" if doctype == "Sales Invoice" else "Purchase Order"
 		target_detail_field = "sales_invoice_item" if doctype == "Sales Invoice" else "sales_order_item"
-		source_document_warehouse_field = 'target_warehouse'
-		target_document_warehouse_field = 'from_warehouse'
+		source_document_warehouse_field = "target_warehouse"
+		target_document_warehouse_field = "from_warehouse"
 	else:
 		source_doc = frappe.get_doc(doctype, source_name)
 		target_doctype = "Sales Invoice" if doctype == "Purchase Invoice" else "Sales Order"
-		source_document_warehouse_field = 'from_warehouse'
-		target_document_warehouse_field = 'target_warehouse'
+		source_document_warehouse_field = "from_warehouse"
+		target_document_warehouse_field = "target_warehouse"
 
 	validate_inter_company_transaction(source_doc, doctype)
 	details = get_inter_company_details(source_doc, doctype)
@@ -1771,7 +2102,7 @@
 	def update_details(source_doc, target_doc, source_parent):
 		target_doc.inter_company_invoice_reference = source_doc.name
 		if target_doc.doctype in ["Purchase Invoice", "Purchase Order"]:
-			currency = frappe.db.get_value('Supplier', details.get('party'), 'default_currency')
+			currency = frappe.db.get_value("Supplier", details.get("party"), "default_currency")
 			target_doc.company = details.get("company")
 			target_doc.supplier = details.get("party")
 			target_doc.is_internal_supplier = 1
@@ -1779,130 +2110,176 @@
 			target_doc.buying_price_list = source_doc.selling_price_list
 
 			# Invert Addresses
-			update_address(target_doc, 'supplier_address', 'address_display', source_doc.company_address)
-			update_address(target_doc, 'shipping_address', 'shipping_address_display', source_doc.customer_address)
+			update_address(target_doc, "supplier_address", "address_display", source_doc.company_address)
+			update_address(
+				target_doc, "shipping_address", "shipping_address_display", source_doc.customer_address
+			)
 
 			if currency:
 				target_doc.currency = currency
 
-			update_taxes(target_doc, party=target_doc.supplier, party_type='Supplier', company=target_doc.company,
-				doctype=target_doc.doctype, party_address=target_doc.supplier_address,
-				company_address=target_doc.shipping_address)
+			update_taxes(
+				target_doc,
+				party=target_doc.supplier,
+				party_type="Supplier",
+				company=target_doc.company,
+				doctype=target_doc.doctype,
+				party_address=target_doc.supplier_address,
+				company_address=target_doc.shipping_address,
+			)
 
 		else:
-			currency = frappe.db.get_value('Customer', details.get('party'), 'default_currency')
+			currency = frappe.db.get_value("Customer", details.get("party"), "default_currency")
 			target_doc.company = details.get("company")
 			target_doc.customer = details.get("party")
 			target_doc.selling_price_list = source_doc.buying_price_list
 
-			update_address(target_doc, 'company_address', 'company_address_display', source_doc.supplier_address)
-			update_address(target_doc, 'shipping_address_name', 'shipping_address', source_doc.shipping_address)
-			update_address(target_doc, 'customer_address', 'address_display', source_doc.shipping_address)
+			update_address(
+				target_doc, "company_address", "company_address_display", source_doc.supplier_address
+			)
+			update_address(
+				target_doc, "shipping_address_name", "shipping_address", source_doc.shipping_address
+			)
+			update_address(target_doc, "customer_address", "address_display", source_doc.shipping_address)
 
 			if currency:
 				target_doc.currency = currency
 
-			update_taxes(target_doc, party=target_doc.customer, party_type='Customer', company=target_doc.company,
-				doctype=target_doc.doctype, party_address=target_doc.customer_address,
-				company_address=target_doc.company_address, shipping_address_name=target_doc.shipping_address_name)
+			update_taxes(
+				target_doc,
+				party=target_doc.customer,
+				party_type="Customer",
+				company=target_doc.company,
+				doctype=target_doc.doctype,
+				party_address=target_doc.customer_address,
+				company_address=target_doc.company_address,
+				shipping_address_name=target_doc.shipping_address_name,
+			)
 
 	item_field_map = {
 		"doctype": target_doctype + " Item",
-		"field_no_map": [
-			"income_account",
-			"expense_account",
-			"cost_center",
-			"warehouse"
-		],
+		"field_no_map": ["income_account", "expense_account", "cost_center", "warehouse"],
 		"field_map": {
-			'rate': 'rate',
-		}
+			"rate": "rate",
+		},
 	}
 
 	if doctype in ["Sales Invoice", "Sales Order"]:
-		item_field_map["field_map"].update({
-			"name": target_detail_field,
-		})
+		item_field_map["field_map"].update(
+			{
+				"name": target_detail_field,
+			}
+		)
 
-	if source_doc.get('update_stock'):
-		item_field_map["field_map"].update({
-			source_document_warehouse_field: target_document_warehouse_field,
-			'batch_no': 'batch_no',
-			'serial_no': 'serial_no'
-		})
+	if source_doc.get("update_stock"):
+		item_field_map["field_map"].update(
+			{
+				source_document_warehouse_field: target_document_warehouse_field,
+				"batch_no": "batch_no",
+				"serial_no": "serial_no",
+			}
+		)
 
-	doclist = get_mapped_doc(doctype, source_name,	{
-		doctype: {
-			"doctype": target_doctype,
-			"postprocess": update_details,
-			"set_target_warehouse": "set_from_warehouse",
-			"field_no_map": [
-				"taxes_and_charges",
-				"set_warehouse",
-				"shipping_address"
-			]
+	doclist = get_mapped_doc(
+		doctype,
+		source_name,
+		{
+			doctype: {
+				"doctype": target_doctype,
+				"postprocess": update_details,
+				"set_target_warehouse": "set_from_warehouse",
+				"field_no_map": ["taxes_and_charges", "set_warehouse", "shipping_address"],
+			},
+			doctype + " Item": item_field_map,
 		},
-		doctype +" Item": item_field_map
-
-	}, target_doc, set_missing_values)
+		target_doc,
+		set_missing_values,
+	)
 
 	return doclist
 
+
 def set_purchase_references(doc):
 	# add internal PO or PR links if any
 	if doc.is_internal_transfer():
-		if doc.doctype == 'Purchase Receipt':
+		if doc.doctype == "Purchase Receipt":
 			so_item_map = get_delivery_note_details(doc.inter_company_invoice_reference)
 
 			if so_item_map:
-				pd_item_map, parent_child_map, warehouse_map = \
-					get_pd_details('Purchase Order Item', so_item_map, 'sales_order_item')
+				pd_item_map, parent_child_map, warehouse_map = get_pd_details(
+					"Purchase Order Item", so_item_map, "sales_order_item"
+				)
 
 				update_pr_items(doc, so_item_map, pd_item_map, parent_child_map, warehouse_map)
 
-		elif doc.doctype == 'Purchase Invoice':
+		elif doc.doctype == "Purchase Invoice":
 			dn_item_map, so_item_map = get_sales_invoice_details(doc.inter_company_invoice_reference)
 			# First check for Purchase receipt
 			if list(dn_item_map.values()):
-				pd_item_map, parent_child_map, warehouse_map = \
-					get_pd_details('Purchase Receipt Item', dn_item_map, 'delivery_note_item')
+				pd_item_map, parent_child_map, warehouse_map = get_pd_details(
+					"Purchase Receipt Item", dn_item_map, "delivery_note_item"
+				)
 
-				update_pi_items(doc, 'pr_detail', 'purchase_receipt',
-					dn_item_map, pd_item_map, parent_child_map, warehouse_map)
+				update_pi_items(
+					doc,
+					"pr_detail",
+					"purchase_receipt",
+					dn_item_map,
+					pd_item_map,
+					parent_child_map,
+					warehouse_map,
+				)
 
 			if list(so_item_map.values()):
-				pd_item_map, parent_child_map, warehouse_map = \
-					get_pd_details('Purchase Order Item', so_item_map, 'sales_order_item')
+				pd_item_map, parent_child_map, warehouse_map = get_pd_details(
+					"Purchase Order Item", so_item_map, "sales_order_item"
+				)
 
-				update_pi_items(doc, 'po_detail', 'purchase_order',
-					so_item_map, pd_item_map, parent_child_map, warehouse_map)
+				update_pi_items(
+					doc, "po_detail", "purchase_order", so_item_map, pd_item_map, parent_child_map, warehouse_map
+				)
 
-def update_pi_items(doc, detail_field, parent_field, sales_item_map,
-	purchase_item_map, parent_child_map, warehouse_map):
-	for item in doc.get('items'):
+
+def update_pi_items(
+	doc,
+	detail_field,
+	parent_field,
+	sales_item_map,
+	purchase_item_map,
+	parent_child_map,
+	warehouse_map,
+):
+	for item in doc.get("items"):
 		item.set(detail_field, purchase_item_map.get(sales_item_map.get(item.sales_invoice_item)))
 		item.set(parent_field, parent_child_map.get(sales_item_map.get(item.sales_invoice_item)))
 		if doc.update_stock:
 			item.warehouse = warehouse_map.get(sales_item_map.get(item.sales_invoice_item))
 
+
 def update_pr_items(doc, sales_item_map, purchase_item_map, parent_child_map, warehouse_map):
-	for item in doc.get('items'):
+	for item in doc.get("items"):
 		item.purchase_order_item = purchase_item_map.get(sales_item_map.get(item.delivery_note_item))
 		item.warehouse = warehouse_map.get(sales_item_map.get(item.delivery_note_item))
 		item.purchase_order = parent_child_map.get(sales_item_map.get(item.delivery_note_item))
 
+
 def get_delivery_note_details(internal_reference):
-	si_item_details = frappe.get_all('Delivery Note Item', fields=['name', 'so_detail'],
-		filters={'parent': internal_reference})
+	si_item_details = frappe.get_all(
+		"Delivery Note Item", fields=["name", "so_detail"], filters={"parent": internal_reference}
+	)
 
 	return {d.name: d.so_detail for d in si_item_details if d.so_detail}
 
+
 def get_sales_invoice_details(internal_reference):
 	dn_item_map = {}
 	so_item_map = {}
 
-	si_item_details = frappe.get_all('Sales Invoice Item', fields=['name', 'so_detail',
-		'dn_detail'], filters={'parent': internal_reference})
+	si_item_details = frappe.get_all(
+		"Sales Invoice Item",
+		fields=["name", "so_detail", "dn_detail"],
+		filters={"parent": internal_reference},
+	)
 
 	for d in si_item_details:
 		if d.dn_detail:
@@ -1912,13 +2289,17 @@
 
 	return dn_item_map, so_item_map
 
+
 def get_pd_details(doctype, sd_detail_map, sd_detail_field):
 	pd_item_map = {}
 	accepted_warehouse_map = {}
 	parent_child_map = {}
 
-	pd_item_details = frappe.get_all(doctype,
-		fields=[sd_detail_field, 'name', 'warehouse', 'parent'], filters={sd_detail_field: ('in', list(sd_detail_map.values()))})
+	pd_item_details = frappe.get_all(
+		doctype,
+		fields=[sd_detail_field, "name", "warehouse", "parent"],
+		filters={sd_detail_field: ("in", list(sd_detail_map.values()))},
+	)
 
 	for d in pd_item_details:
 		pd_item_map.setdefault(d.get(sd_detail_field), d.name)
@@ -1927,16 +2308,33 @@
 
 	return pd_item_map, parent_child_map, accepted_warehouse_map
 
-def update_taxes(doc, party=None, party_type=None, company=None, doctype=None, party_address=None,
-	company_address=None, shipping_address_name=None, master_doctype=None):
+
+def update_taxes(
+	doc,
+	party=None,
+	party_type=None,
+	company=None,
+	doctype=None,
+	party_address=None,
+	company_address=None,
+	shipping_address_name=None,
+	master_doctype=None,
+):
 	# Update Party Details
-	party_details = get_party_details(party=party, party_type=party_type, company=company,
-		doctype=doctype, party_address=party_address, company_address=company_address,
-		shipping_address=shipping_address_name)
+	party_details = get_party_details(
+		party=party,
+		party_type=party_type,
+		company=company,
+		doctype=doctype,
+		party_address=party_address,
+		company_address=company_address,
+		shipping_address=shipping_address_name,
+	)
 
 	# Update taxes and charges if any
-	doc.taxes_and_charges = party_details.get('taxes_and_charges')
-	doc.set('taxes', party_details.get('taxes'))
+	doc.taxes_and_charges = party_details.get("taxes_and_charges")
+	doc.set("taxes", party_details.get("taxes"))
+
 
 def update_address(doc, address_field, address_display_field, address_name):
 	doc.set(address_field, address_name)
@@ -1947,53 +2345,61 @@
 
 	doc.set(address_display_field, get_address_display(doc.get(address_field)))
 
+
 @frappe.whitelist()
 def get_loyalty_programs(customer):
-	''' sets applicable loyalty program to the customer or returns a list of applicable programs '''
+	"""sets applicable loyalty program to the customer or returns a list of applicable programs"""
 	from erpnext.selling.doctype.customer.customer import get_loyalty_programs
 
-	customer = frappe.get_doc('Customer', customer)
-	if customer.loyalty_program: return [customer.loyalty_program]
+	customer = frappe.get_doc("Customer", customer)
+	if customer.loyalty_program:
+		return [customer.loyalty_program]
 
 	lp_details = get_loyalty_programs(customer)
 
 	if len(lp_details) == 1:
-		frappe.db.set(customer, 'loyalty_program', lp_details[0])
+		frappe.db.set(customer, "loyalty_program", lp_details[0])
 		return lp_details
 	else:
 		return lp_details
 
+
 def on_doctype_update():
 	frappe.db.add_index("Sales Invoice", ["customer", "is_return", "return_against"])
 
+
 @frappe.whitelist()
 def create_invoice_discounting(source_name, target_doc=None):
 	invoice = frappe.get_doc("Sales Invoice", source_name)
 	invoice_discounting = frappe.new_doc("Invoice Discounting")
 	invoice_discounting.company = invoice.company
-	invoice_discounting.append("invoices", {
-		"sales_invoice": source_name,
-		"customer": invoice.customer,
-		"posting_date": invoice.posting_date,
-		"outstanding_amount": invoice.outstanding_amount
-	})
+	invoice_discounting.append(
+		"invoices",
+		{
+			"sales_invoice": source_name,
+			"customer": invoice.customer,
+			"posting_date": invoice.posting_date,
+			"outstanding_amount": invoice.outstanding_amount,
+		},
+	)
 
 	return invoice_discounting
 
+
 def update_multi_mode_option(doc, pos_profile):
 	def append_payment(payment_mode):
-		payment = doc.append('payments', {})
+		payment = doc.append("payments", {})
 		payment.default = payment_mode.default
 		payment.mode_of_payment = payment_mode.mop
 		payment.account = payment_mode.default_account
 		payment.type = payment_mode.type
 
-	doc.set('payments', [])
+	doc.set("payments", [])
 	invalid_modes = []
-	mode_of_payments = [d.mode_of_payment for d in pos_profile.get('payments')]
+	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)
 
-	for row in pos_profile.get('payments'):
+	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", row.mode_of_payment))
@@ -2009,12 +2415,17 @@
 			msg = _("Please set default Cash or Bank account in Mode of Payments {}")
 		frappe.throw(msg.format(", ".join(invalid_modes)), title=_("Missing Account"))
 
+
 def get_all_mode_of_payments(doc):
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select mpa.default_account, mpa.parent, mp.type as type
 		from `tabMode of Payment Account` mpa,`tabMode of Payment` mp
 		where mpa.parent = mp.name and mpa.company = %(company)s and mp.enabled = 1""",
-	{'company': doc.company}, as_dict=1)
+		{"company": doc.company},
+		as_dict=1,
+	)
+
 
 def get_mode_of_payments_info(mode_of_payments, company):
 	data = frappe.db.sql(
@@ -2030,16 +2441,24 @@
 			mp.name in %s
 		group by
 			mp.name
-		""", (company, mode_of_payments), as_dict=1)
+		""",
+		(company, mode_of_payments),
+		as_dict=1,
+	)
 
-	return {row.get('mop'): row for row in data}
+	return {row.get("mop"): row for row in data}
+
 
 def get_mode_of_payment_info(mode_of_payment, company):
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select mpa.default_account, mpa.parent, 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 = %s""",
-	(company, mode_of_payment), as_dict=1)
+		(company, mode_of_payment),
+		as_dict=1,
+	)
+
 
 @frappe.whitelist()
 def create_dunning(source_name, target_doc=None):
@@ -2049,41 +2468,58 @@
 		calculate_interest_and_amount,
 		get_dunning_letter_text,
 	)
+
 	def set_missing_values(source, target):
 		target.sales_invoice = source_name
 		target.outstanding_amount = source.outstanding_amount
 		overdue_days = (getdate(target.posting_date) - getdate(source.due_date)).days
 		target.overdue_days = overdue_days
-		if frappe.db.exists('Dunning Type', {'start_day': [
-	                                '<', overdue_days], 'end_day': ['>=', overdue_days]}):
-			dunning_type = frappe.get_doc('Dunning Type', {'start_day': [
-	                                '<', overdue_days], 'end_day': ['>=', overdue_days]})
+		if frappe.db.exists(
+			"Dunning Type", {"start_day": ["<", overdue_days], "end_day": [">=", overdue_days]}
+		):
+			dunning_type = frappe.get_doc(
+				"Dunning Type", {"start_day": ["<", overdue_days], "end_day": [">=", overdue_days]}
+			)
 			target.dunning_type = dunning_type.name
 			target.rate_of_interest = dunning_type.rate_of_interest
 			target.dunning_fee = dunning_type.dunning_fee
-			letter_text = get_dunning_letter_text(dunning_type = dunning_type.name, doc = target.as_dict())
+			letter_text = get_dunning_letter_text(dunning_type=dunning_type.name, doc=target.as_dict())
 			if letter_text:
-				target.body_text = letter_text.get('body_text')
-				target.closing_text = letter_text.get('closing_text')
-				target.language = letter_text.get('language')
-			amounts = calculate_interest_and_amount(target.posting_date, target.outstanding_amount,
-				target.rate_of_interest, target.dunning_fee, target.overdue_days)
-			target.interest_amount = amounts.get('interest_amount')
-			target.dunning_amount = amounts.get('dunning_amount')
-			target.grand_total = amounts.get('grand_total')
+				target.body_text = letter_text.get("body_text")
+				target.closing_text = letter_text.get("closing_text")
+				target.language = letter_text.get("language")
+			amounts = calculate_interest_and_amount(
+				target.posting_date,
+				target.outstanding_amount,
+				target.rate_of_interest,
+				target.dunning_fee,
+				target.overdue_days,
+			)
+			target.interest_amount = amounts.get("interest_amount")
+			target.dunning_amount = amounts.get("dunning_amount")
+			target.grand_total = amounts.get("grand_total")
 
-	doclist = get_mapped_doc("Sales Invoice", source_name,	{
-		"Sales Invoice": {
-			"doctype": "Dunning",
-		}
-	}, target_doc, set_missing_values)
+	doclist = get_mapped_doc(
+		"Sales Invoice",
+		source_name,
+		{
+			"Sales Invoice": {
+				"doctype": "Dunning",
+			}
+		},
+		target_doc,
+		set_missing_values,
+	)
 	return doclist
 
+
 def check_if_return_invoice_linked_with_payment_entry(self):
 	# If a Return invoice is linked with payment entry along with other invoices,
 	# the cancellation of the Return causes allocated amount to be greater than paid
 
-	if not frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
+	if not frappe.db.get_single_value(
+		"Accounts Settings", "unlink_payment_on_cancellation_of_invoice"
+	):
 		return
 
 	payment_entries = []
@@ -2092,7 +2528,8 @@
 	else:
 		invoice = self.name
 
-	payment_entries = frappe.db.sql_list("""
+	payment_entries = frappe.db.sql_list(
+		"""
 		SELECT
 			t1.name
 		FROM
@@ -2102,7 +2539,9 @@
 			and t1.docstatus = 1
 			and t2.reference_name = %s
 			and t2.allocated_amount < 0
-		""", invoice)
+		""",
+		invoice,
+	)
 
 	links_to_pe = []
 	if payment_entries:
@@ -2111,7 +2550,9 @@
 			if len(payment_entry.references) > 1:
 				links_to_pe.append(payment_entry.name)
 		if links_to_pe:
-			payment_entries_link = [get_link_to_form('Payment Entry', name, label=name) for name in links_to_pe]
+			payment_entries_link = [
+				get_link_to_form("Payment Entry", name, label=name) for name in links_to_pe
+			]
 			message = _("Please cancel and amend the Payment Entry")
 			message += " " + ", ".join(payment_entries_link) + " "
 			message += _("to unallocate the amount of this Return Invoice before cancelling it.")
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py
index 5cdc8da..c0005f7 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py
@@ -3,34 +3,32 @@
 
 def get_data():
 	return {
-		'fieldname': 'sales_invoice',
-		'non_standard_fieldnames': {
-			'Delivery Note': 'against_sales_invoice',
-			'Journal Entry': 'reference_name',
-			'Payment Entry': 'reference_name',
-			'Payment Request': 'reference_name',
-			'Sales Invoice': 'return_against',
-			'Auto Repeat': 'reference_document',
+		"fieldname": "sales_invoice",
+		"non_standard_fieldnames": {
+			"Delivery Note": "against_sales_invoice",
+			"Journal Entry": "reference_name",
+			"Payment Entry": "reference_name",
+			"Payment Request": "reference_name",
+			"Sales Invoice": "return_against",
+			"Auto Repeat": "reference_document",
 		},
-		'internal_links': {
-			'Sales Order': ['items', 'sales_order']
+		"internal_links": {
+			"Sales Order": ["items", "sales_order"],
+			"Timesheet": ["timesheets", "time_sheet"],
 		},
-		'transactions': [
+		"transactions": [
 			{
-				'label': _('Payment'),
-				'items': ['Payment Entry', 'Payment Request', 'Journal Entry', 'Invoice Discounting', 'Dunning']
+				"label": _("Payment"),
+				"items": [
+					"Payment Entry",
+					"Payment Request",
+					"Journal Entry",
+					"Invoice Discounting",
+					"Dunning",
+				],
 			},
-			{
-				'label': _('Reference'),
-				'items': ['Timesheet', 'Delivery Note', 'Sales Order']
-			},
-			{
-				'label': _('Returns'),
-				'items': ['Sales Invoice']
-			},
-			{
-				'label': _('Subscription'),
-				'items': ['Auto Repeat']
-			},
-		]
+			{"label": _("Reference"), "items": ["Timesheet", "Delivery Note", "Sales Order"]},
+			{"label": _("Returns"), "items": ["Sales Invoice"]},
+			{"label": _("Subscription"), "items": ["Auto Repeat"]},
+		],
 	}
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 90f3a47..8287755 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -7,6 +7,7 @@
 import frappe
 from frappe.model.dynamic_links import get_dynamic_link_map
 from frappe.model.naming import make_autoname
+from frappe.tests.utils import change_settings
 from frappe.utils import add_days, flt, getdate, nowdate
 
 import erpnext
@@ -58,23 +59,25 @@
 		w2 = frappe.get_doc(w.doctype, w.name)
 
 		import time
+
 		time.sleep(1)
 		w.save()
 
 		import time
+
 		time.sleep(1)
 		self.assertRaises(frappe.TimestampMismatchError, w2.save)
 
 	def test_sales_invoice_change_naming_series(self):
 		si = frappe.copy_doc(test_records[2])
 		si.insert()
-		si.naming_series = 'TEST-'
+		si.naming_series = "TEST-"
 
 		self.assertRaises(frappe.CannotChangeConstantError, si.save)
 
 		si = frappe.copy_doc(test_records[1])
 		si.insert()
-		si.naming_series = 'TEST-'
+		si.naming_series = "TEST-"
 
 		self.assertRaises(frappe.CannotChangeConstantError, si.save)
 
@@ -90,15 +93,21 @@
 		si.insert()
 
 		expected_values = {
-			"keys": ["price_list_rate", "discount_percentage", "rate", "amount",
-				"base_price_list_rate", "base_rate", "base_amount"],
+			"keys": [
+				"price_list_rate",
+				"discount_percentage",
+				"rate",
+				"amount",
+				"base_price_list_rate",
+				"base_rate",
+				"base_amount",
+			],
 			"_Test Item Home Desktop 100": [50, 0, 50, 500, 50, 50, 500],
 			"_Test Item Home Desktop 200": [150, 0, 150, 750, 150, 150, 750],
 		}
 
 		# check if children are saved
-		self.assertEqual(len(si.get("items")),
-			len(expected_values)-1)
+		self.assertEqual(len(si.get("items")), len(expected_values) - 1)
 
 		# check if item values are calculated
 		for d in si.get("items"):
@@ -119,7 +128,7 @@
 			"_Test Account S&H Education Cess - _TC": [1.4, 1619.2],
 			"_Test Account CST - _TC": [32.38, 1651.58],
 			"_Test Account VAT - _TC": [156.25, 1807.83],
-			"_Test Account Discount - _TC": [-180.78, 1627.05]
+			"_Test Account Discount - _TC": [-180.78, 1627.05],
 		}
 
 		for d in si.get("taxes"):
@@ -149,7 +158,7 @@
 		pe.submit()
 
 		unlink_payment_on_cancel_of_invoice(0)
-		si = frappe.get_doc('Sales Invoice', si.name)
+		si = frappe.get_doc("Sales Invoice", si.name)
 		self.assertRaises(frappe.LinkExistsError, si.cancel)
 		unlink_payment_on_cancel_of_invoice()
 
@@ -160,25 +169,30 @@
 		si2 = create_sales_invoice(rate=300)
 		si3 = create_sales_invoice(qty=-1, rate=300, is_return=1)
 
-
 		pe = get_payment_entry("Sales Invoice", si1.name, bank_account="_Test Bank - _TC")
-		pe.append('references', {
-			'reference_doctype': 'Sales Invoice',
-			'reference_name': si2.name,
-			'total_amount': si2.grand_total,
-			'outstanding_amount': si2.outstanding_amount,
-			'allocated_amount': si2.outstanding_amount
-		})
+		pe.append(
+			"references",
+			{
+				"reference_doctype": "Sales Invoice",
+				"reference_name": si2.name,
+				"total_amount": si2.grand_total,
+				"outstanding_amount": si2.outstanding_amount,
+				"allocated_amount": si2.outstanding_amount,
+			},
+		)
 
-		pe.append('references', {
-			'reference_doctype': 'Sales Invoice',
-			'reference_name': si3.name,
-			'total_amount': si3.grand_total,
-			'outstanding_amount': si3.outstanding_amount,
-			'allocated_amount': si3.outstanding_amount
-		})
+		pe.append(
+			"references",
+			{
+				"reference_doctype": "Sales Invoice",
+				"reference_name": si3.name,
+				"total_amount": si3.grand_total,
+				"outstanding_amount": si3.outstanding_amount,
+				"allocated_amount": si3.outstanding_amount,
+			},
+		)
 
-		pe.reference_no = 'Test001'
+		pe.reference_no = "Test001"
 		pe.reference_date = nowdate()
 		pe.save()
 		pe.submit()
@@ -189,7 +203,6 @@
 		si1.load_from_db()
 		self.assertRaises(PaymentEntryUnlinkError, si1.cancel)
 
-
 	def test_sales_invoice_calculation_export_currency(self):
 		si = frappe.copy_doc(test_records[2])
 		si.currency = "USD"
@@ -204,14 +217,21 @@
 		si.insert()
 
 		expected_values = {
-			"keys": ["price_list_rate", "discount_percentage", "rate", "amount",
-				"base_price_list_rate", "base_rate", "base_amount"],
+			"keys": [
+				"price_list_rate",
+				"discount_percentage",
+				"rate",
+				"amount",
+				"base_price_list_rate",
+				"base_rate",
+				"base_amount",
+			],
 			"_Test Item Home Desktop 100": [1, 0, 1, 10, 50, 50, 500],
 			"_Test Item Home Desktop 200": [3, 0, 3, 15, 150, 150, 750],
 		}
 
 		# check if children are saved
-		self.assertEqual(len(si.get("items")), len(expected_values)-1)
+		self.assertEqual(len(si.get("items")), len(expected_values) - 1)
 
 		# check if item values are calculated
 		for d in si.get("items"):
@@ -234,7 +254,7 @@
 			"_Test Account S&H Education Cess - _TC": [1.5, 1619.5, 0.03, 32.39],
 			"_Test Account CST - _TC": [32.5, 1652, 0.65, 33.04],
 			"_Test Account VAT - _TC": [156.5, 1808.5, 3.13, 36.17],
-			"_Test Account Discount - _TC": [-181.0, 1627.5, -3.62, 32.55]
+			"_Test Account Discount - _TC": [-181.0, 1627.5, -3.62, 32.55],
 		}
 
 		for d in si.get("taxes"):
@@ -246,22 +266,28 @@
 
 	def test_sales_invoice_with_discount_and_inclusive_tax(self):
 		si = create_sales_invoice(qty=100, rate=50, do_not_save=True)
-		si.append("taxes", {
-			"charge_type": "On Net Total",
-			"account_head": "_Test Account Service Tax - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "Service Tax",
-			"rate": 14,
-			'included_in_print_rate': 1
-		})
-		si.append("taxes", {
-			"charge_type": "On Item Quantity",
-			"account_head": "_Test Account Education Cess - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "CESS",
-			"rate": 5,
-			'included_in_print_rate': 1
-		})
+		si.append(
+			"taxes",
+			{
+				"charge_type": "On Net Total",
+				"account_head": "_Test Account Service Tax - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "Service Tax",
+				"rate": 14,
+				"included_in_print_rate": 1,
+			},
+		)
+		si.append(
+			"taxes",
+			{
+				"charge_type": "On Item Quantity",
+				"account_head": "_Test Account Education Cess - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "CESS",
+				"rate": 5,
+				"included_in_print_rate": 1,
+			},
+		)
 		si.insert()
 
 		# with inclusive tax
@@ -273,7 +299,7 @@
 
 		# additional discount
 		si.discount_amount = 100
-		si.apply_discount_on = 'Net Total'
+		si.apply_discount_on = "Net Total"
 		si.payment_schedule = []
 
 		si.save()
@@ -286,7 +312,7 @@
 
 		# additional discount on grand total
 		si.discount_amount = 100
-		si.apply_discount_on = 'Grand Total'
+		si.apply_discount_on = "Grand Total"
 		si.payment_schedule = []
 
 		si.save()
@@ -298,14 +324,17 @@
 	def test_sales_invoice_discount_amount(self):
 		si = frappe.copy_doc(test_records[3])
 		si.discount_amount = 104.94
-		si.append("taxes", {
-			"charge_type": "On Previous Row Amount",
-			"account_head": "_Test Account Service Tax - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "Service Tax",
-			"rate": 10,
-			"row_id": 8,
-		})
+		si.append(
+			"taxes",
+			{
+				"charge_type": "On Previous Row Amount",
+				"account_head": "_Test Account Service Tax - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "Service Tax",
+				"rate": 10,
+				"row_id": 8,
+			},
+		)
 		si.insert()
 
 		expected_values = [
@@ -321,7 +350,7 @@
 				"net_rate": 46.54,
 				"net_amount": 465.37,
 				"base_net_rate": 46.54,
-				"base_net_amount": 465.37
+				"base_net_amount": 465.37,
 			},
 			{
 				"item_code": "_Test Item Home Desktop 200",
@@ -335,12 +364,12 @@
 				"net_rate": 139.62,
 				"net_amount": 698.08,
 				"base_net_rate": 139.62,
-				"base_net_amount": 698.08
-			}
+				"base_net_amount": 698.08,
+			},
 		]
 
 		# check if children are saved
-		self.assertEqual(len(si.get("items")),	len(expected_values))
+		self.assertEqual(len(si.get("items")), len(expected_values))
 
 		# check if item values are calculated
 		for i, d in enumerate(si.get("items")):
@@ -362,7 +391,7 @@
 			"_Test Account Customs Duty - _TC": [125, 116.35, 1585.40],
 			"_Test Account Shipping Charges - _TC": [100, 100, 1685.40],
 			"_Test Account Discount - _TC": [-180.33, -168.54, 1516.86],
-			"_Test Account Service Tax - _TC": [-18.03, -16.85, 1500.01]
+			"_Test Account Service Tax - _TC": [-18.03, -16.85, 1500.01],
 		}
 
 		for d in si.get("taxes"):
@@ -377,38 +406,48 @@
 		frappe.db.set_value("Company", "_Test Company", "round_off_account", "Round Off - _TC")
 		si = frappe.copy_doc(test_records[3])
 		si.discount_amount = 104.94
-		si.append("taxes", {
-			"doctype": "Sales Taxes and Charges",
-			"charge_type": "On Previous Row Amount",
-			"account_head": "_Test Account Service Tax - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "Service Tax",
-			"rate": 10,
-			"row_id": 8
-		})
+		si.append(
+			"taxes",
+			{
+				"doctype": "Sales Taxes and Charges",
+				"charge_type": "On Previous Row Amount",
+				"account_head": "_Test Account Service Tax - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "Service Tax",
+				"rate": 10,
+				"row_id": 8,
+			},
+		)
 		si.insert()
 		si.submit()
 
-		gl_entries = frappe.db.sql("""select account, debit, credit
+		gl_entries = frappe.db.sql(
+			"""select account, debit, credit
 			from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
-			order by account asc""", si.name, as_dict=1)
+			order by account asc""",
+			si.name,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
-		expected_values = dict((d[0], d) for d in [
-			[si.debit_to, 1500, 0.0],
-			[test_records[3]["items"][0]["income_account"], 0.0, 1163.45],
-			[test_records[3]["taxes"][0]["account_head"], 0.0, 130.31],
-			[test_records[3]["taxes"][1]["account_head"], 0.0, 2.61],
-			[test_records[3]["taxes"][2]["account_head"], 0.0, 1.30],
-			[test_records[3]["taxes"][3]["account_head"], 0.0, 25.95],
-			[test_records[3]["taxes"][4]["account_head"], 0.0, 145.43],
-			[test_records[3]["taxes"][5]["account_head"], 0.0, 116.35],
-			[test_records[3]["taxes"][6]["account_head"], 0.0, 100],
-			[test_records[3]["taxes"][7]["account_head"], 168.54, 0.0],
-			["_Test Account Service Tax - _TC", 16.85, 0.0],
-			["Round Off - _TC", 0.01, 0.0]
-		])
+		expected_values = dict(
+			(d[0], d)
+			for d in [
+				[si.debit_to, 1500, 0.0],
+				[test_records[3]["items"][0]["income_account"], 0.0, 1163.45],
+				[test_records[3]["taxes"][0]["account_head"], 0.0, 130.31],
+				[test_records[3]["taxes"][1]["account_head"], 0.0, 2.61],
+				[test_records[3]["taxes"][2]["account_head"], 0.0, 1.30],
+				[test_records[3]["taxes"][3]["account_head"], 0.0, 25.95],
+				[test_records[3]["taxes"][4]["account_head"], 0.0, 145.43],
+				[test_records[3]["taxes"][5]["account_head"], 0.0, 116.35],
+				[test_records[3]["taxes"][6]["account_head"], 0.0, 100],
+				[test_records[3]["taxes"][7]["account_head"], 168.54, 0.0],
+				["_Test Account Service Tax - _TC", 16.85, 0.0],
+				["Round Off - _TC", 0.01, 0.0],
+			]
+		)
 
 		for gle in gl_entries:
 			self.assertEqual(expected_values[gle.account][0], gle.account)
@@ -418,8 +457,11 @@
 		# cancel
 		si.cancel()
 
-		gle = frappe.db.sql("""select * from `tabGL Entry`
-			where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
+		gle = frappe.db.sql(
+			"""select * from `tabGL Entry`
+			where voucher_type='Sales Invoice' and voucher_no=%s""",
+			si.name,
+		)
 
 		self.assertTrue(gle)
 
@@ -431,14 +473,17 @@
 			item_row_copy.qty = qty
 			si.append("items", item_row_copy)
 
-		si.append("taxes", {
-			"account_head": "_Test Account VAT - _TC",
-			"charge_type": "On Net Total",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "VAT",
-			"doctype": "Sales Taxes and Charges",
-			"rate": 19
-		})
+		si.append(
+			"taxes",
+			{
+				"account_head": "_Test Account VAT - _TC",
+				"charge_type": "On Net Total",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "VAT",
+				"doctype": "Sales Taxes and Charges",
+				"rate": 19,
+			},
+		)
 		si.insert()
 
 		self.assertEqual(si.net_total, 4600)
@@ -453,10 +498,10 @@
 		item_row = si.get("items")[0]
 
 		add_items = [
-			(54, '_Test Account Excise Duty @ 12 - _TC'),
-			(288, '_Test Account Excise Duty @ 15 - _TC'),
-			(144, '_Test Account Excise Duty @ 20 - _TC'),
-			(430, '_Test Item Tax Template 1 - _TC')
+			(54, "_Test Account Excise Duty @ 12 - _TC"),
+			(288, "_Test Account Excise Duty @ 15 - _TC"),
+			(144, "_Test Account Excise Duty @ 20 - _TC"),
+			(430, "_Test Item Tax Template 1 - _TC"),
 		]
 		for qty, item_tax_template in add_items:
 			item_row_copy = copy.deepcopy(item_row)
@@ -464,30 +509,39 @@
 			item_row_copy.item_tax_template = item_tax_template
 			si.append("items", item_row_copy)
 
-		si.append("taxes", {
-			"account_head": "_Test Account Excise Duty - _TC",
-			"charge_type": "On Net Total",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "Excise Duty",
-			"doctype": "Sales Taxes and Charges",
-			"rate": 11
-		})
-		si.append("taxes", {
-			"account_head": "_Test Account Education Cess - _TC",
-			"charge_type": "On Net Total",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "Education Cess",
-			"doctype": "Sales Taxes and Charges",
-			"rate": 0
-		})
-		si.append("taxes", {
-			"account_head": "_Test Account S&H Education Cess - _TC",
-			"charge_type": "On Net Total",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "S&H Education Cess",
-			"doctype": "Sales Taxes and Charges",
-			"rate": 3
-		})
+		si.append(
+			"taxes",
+			{
+				"account_head": "_Test Account Excise Duty - _TC",
+				"charge_type": "On Net Total",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "Excise Duty",
+				"doctype": "Sales Taxes and Charges",
+				"rate": 11,
+			},
+		)
+		si.append(
+			"taxes",
+			{
+				"account_head": "_Test Account Education Cess - _TC",
+				"charge_type": "On Net Total",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "Education Cess",
+				"doctype": "Sales Taxes and Charges",
+				"rate": 0,
+			},
+		)
+		si.append(
+			"taxes",
+			{
+				"account_head": "_Test Account S&H Education Cess - _TC",
+				"charge_type": "On Net Total",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "S&H Education Cess",
+				"doctype": "Sales Taxes and Charges",
+				"rate": 3,
+			},
+		)
 		si.insert()
 
 		self.assertEqual(si.net_total, 4600)
@@ -517,14 +571,17 @@
 		si.apply_discount_on = "Net Total"
 		si.discount_amount = 75.0
 
-		si.append("taxes", {
-			"account_head": "_Test Account VAT - _TC",
-			"charge_type": "On Net Total",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "VAT",
-			"doctype": "Sales Taxes and Charges",
-			"rate": 24
-		})
+		si.append(
+			"taxes",
+			{
+				"account_head": "_Test Account VAT - _TC",
+				"charge_type": "On Net Total",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "VAT",
+				"doctype": "Sales Taxes and Charges",
+				"rate": 24,
+			},
+		)
 		si.insert()
 
 		self.assertEqual(si.total, 975)
@@ -538,7 +595,7 @@
 	def test_inclusive_rate_validations(self):
 		si = frappe.copy_doc(test_records[2])
 		for i, tax in enumerate(si.get("taxes")):
-			tax.idx = i+1
+			tax.idx = i + 1
 
 		si.get("items")[0].price_list_rate = 62.5
 		si.get("items")[0].price_list_rate = 191
@@ -558,14 +615,43 @@
 		si.insert()
 
 		expected_values = {
-			"keys": ["price_list_rate", "discount_percentage", "rate", "amount",
-				"base_price_list_rate", "base_rate", "base_amount", "net_rate", "net_amount"],
-			"_Test Item Home Desktop 100": [62.5, 0, 62.5, 625.0, 62.5, 62.5, 625.0, 50, 499.97600115194473],
-			"_Test Item Home Desktop 200": [190.66, 0, 190.66, 953.3, 190.66, 190.66, 953.3, 150, 749.9968530500239],
+			"keys": [
+				"price_list_rate",
+				"discount_percentage",
+				"rate",
+				"amount",
+				"base_price_list_rate",
+				"base_rate",
+				"base_amount",
+				"net_rate",
+				"net_amount",
+			],
+			"_Test Item Home Desktop 100": [
+				62.5,
+				0,
+				62.5,
+				625.0,
+				62.5,
+				62.5,
+				625.0,
+				50,
+				499.97600115194473,
+			],
+			"_Test Item Home Desktop 200": [
+				190.66,
+				0,
+				190.66,
+				953.3,
+				190.66,
+				190.66,
+				953.3,
+				150,
+				749.9968530500239,
+			],
 		}
 
 		# check if children are saved
-		self.assertEqual(len(si.get("items")), len(expected_values)-1)
+		self.assertEqual(len(si.get("items")), len(expected_values) - 1)
 
 		# check if item values are calculated
 		for d in si.get("items"):
@@ -586,7 +672,7 @@
 			"_Test Account VAT - _TC": [156.25, 1578.30],
 			"_Test Account Customs Duty - _TC": [125, 1703.30],
 			"_Test Account Shipping Charges - _TC": [100, 1803.30],
-			"_Test Account Discount - _TC": [-180.33, 1622.97]
+			"_Test Account Discount - _TC": [-180.33, 1622.97],
 		}
 
 		for d in si.get("taxes"):
@@ -624,7 +710,7 @@
 				"net_rate": 40,
 				"net_amount": 399.9808009215558,
 				"base_net_rate": 2000,
-				"base_net_amount": 19999
+				"base_net_amount": 19999,
 			},
 			{
 				"item_code": "_Test Item Home Desktop 200",
@@ -638,8 +724,8 @@
 				"net_rate": 118.01,
 				"net_amount": 590.0531205155963,
 				"base_net_rate": 5900.5,
-				"base_net_amount": 29502.5
-			}
+				"base_net_amount": 29502.5,
+			},
 		]
 
 		# check if children are saved
@@ -664,8 +750,8 @@
 			"_Test Account CST - _TC": [1104, 56312.0, 22.08, 1126.24],
 			"_Test Account VAT - _TC": [6187.5, 62499.5, 123.75, 1249.99],
 			"_Test Account Customs Duty - _TC": [4950.0, 67449.5, 99.0, 1348.99],
-			"_Test Account Shipping Charges - _TC": [ 100, 67549.5, 2, 1350.99],
-			"_Test Account Discount - _TC": [ -6755, 60794.5, -135.10, 1215.89]
+			"_Test Account Shipping Charges - _TC": [100, 67549.5, 2, 1350.99],
+			"_Test Account Discount - _TC": [-6755, 60794.5, -135.10, 1215.89],
 		}
 
 		for d in si.get("taxes"):
@@ -677,7 +763,6 @@
 		self.assertEqual(si.rounding_adjustment, 0.01)
 		self.assertEqual(si.base_rounding_adjustment, 0.50)
 
-
 	def test_outstanding(self):
 		w = self.make()
 		self.assertEqual(w.outstanding_amount, w.base_rounded_total)
@@ -697,11 +782,11 @@
 
 		self.assertEqual(frappe.db.get_value("Sales Invoice", w.name, "outstanding_amount"), 162.0)
 
-		link_data = get_dynamic_link_map().get('Sales Invoice', [])
+		link_data = get_dynamic_link_map().get("Sales Invoice", [])
 		link_doctypes = [d.parent for d in link_data]
 
 		# test case for dynamic link order
-		self.assertTrue(link_doctypes.index('GL Entry') > link_doctypes.index('Journal Entry Account'))
+		self.assertTrue(link_doctypes.index("GL Entry") > link_doctypes.index("Journal Entry Account"))
 
 		jv.cancel()
 		self.assertEqual(frappe.db.get_value("Sales Invoice", w.name, "outstanding_amount"), 562.0)
@@ -711,18 +796,25 @@
 		si.insert()
 		si.submit()
 
-		gl_entries = frappe.db.sql("""select account, debit, credit
+		gl_entries = frappe.db.sql(
+			"""select account, debit, credit
 			from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
-			order by account asc""", si.name, as_dict=1)
+			order by account asc""",
+			si.name,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
-		expected_values = dict((d[0], d) for d in [
-			[si.debit_to, 630.0, 0.0],
-			[test_records[1]["items"][0]["income_account"], 0.0, 500.0],
-			[test_records[1]["taxes"][0]["account_head"], 0.0, 80.0],
-			[test_records[1]["taxes"][1]["account_head"], 0.0, 50.0],
-		])
+		expected_values = dict(
+			(d[0], d)
+			for d in [
+				[si.debit_to, 630.0, 0.0],
+				[test_records[1]["items"][0]["income_account"], 0.0, 500.0],
+				[test_records[1]["taxes"][0]["account_head"], 0.0, 80.0],
+				[test_records[1]["taxes"][1]["account_head"], 0.0, 50.0],
+			]
+		)
 
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_values[gle.account][0], gle.account)
@@ -732,25 +824,49 @@
 		# cancel
 		si.cancel()
 
-		gle = frappe.db.sql("""select * from `tabGL Entry`
-			where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
+		gle = frappe.db.sql(
+			"""select * from `tabGL Entry`
+			where voucher_type='Sales Invoice' and voucher_no=%s""",
+			si.name,
+		)
 
 		self.assertTrue(gle)
 
 	def test_pos_gl_entry_with_perpetual_inventory(self):
-		make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
-			expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1")
+		make_pos_profile(
+			company="_Test Company with perpetual inventory",
+			income_account="Sales - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+			write_off_account="_Test Write Off - TCP1",
+		)
 
-		pr = make_purchase_receipt(company= "_Test Company with perpetual inventory", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
+		pr = make_purchase_receipt(
+			company="_Test Company with perpetual inventory",
+			item_code="_Test FG Item",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+		)
 
-		pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1",
-			income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
+		pos = create_sales_invoice(
+			company="_Test Company with perpetual inventory",
+			debit_to="Debtors - TCP1",
+			item_code="_Test FG Item",
+			warehouse="Stores - TCP1",
+			income_account="Sales - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			cost_center="Main - TCP1",
+			do_not_save=True,
+		)
 
 		pos.is_pos = 1
 		pos.update_stock = 1
 
-		pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 50})
-		pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - TCP1', 'amount': 50})
+		pos.append(
+			"payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - TCP1", "amount": 50}
+		)
+		pos.append("payments", {"mode_of_payment": "Cash", "account": "Cash - TCP1", "amount": 50})
 
 		taxes = get_taxes_and_charges()
 		pos.taxes = []
@@ -770,20 +886,19 @@
 		pos_profile = make_pos_profile()
 
 		pos_profile.payments = []
-		pos_profile.append('payments', {
-			'default': 1,
-			'mode_of_payment': 'Cash'
-		})
+		pos_profile.append("payments", {"default": 1, "mode_of_payment": "Cash"})
 
 		pos_profile.save()
 
-		pos = create_sales_invoice(qty = 10, do_not_save=True)
+		pos = create_sales_invoice(qty=10, do_not_save=True)
 
 		pos.is_pos = 1
 		pos.pos_profile = pos_profile.name
 
-		pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 500})
-		pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 500})
+		pos.append(
+			"payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - _TC", "amount": 500}
+		)
+		pos.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 500})
 		pos.insert()
 		pos.submit()
 
@@ -792,46 +907,123 @@
 		pos_return.insert()
 		pos_return.submit()
 
-		self.assertEqual(pos_return.get('payments')[0].amount, -1000)
+		self.assertEqual(pos_return.get("payments")[0].amount, -1000)
 
 	def test_pos_change_amount(self):
-		make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
-			expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1")
+		make_pos_profile(
+			company="_Test Company with perpetual inventory",
+			income_account="Sales - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+			write_off_account="_Test Write Off - TCP1",
+		)
 
-		make_purchase_receipt(company= "_Test Company with perpetual inventory",
-			item_code= "_Test FG Item",warehouse= "Stores - TCP1", cost_center= "Main - TCP1")
+		make_purchase_receipt(
+			company="_Test Company with perpetual inventory",
+			item_code="_Test FG Item",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+		)
 
-		pos = create_sales_invoice(company= "_Test Company with perpetual inventory",
-			debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1",
-			income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1",
-			cost_center = "Main - TCP1", do_not_save=True)
+		pos = create_sales_invoice(
+			company="_Test Company with perpetual inventory",
+			debit_to="Debtors - TCP1",
+			item_code="_Test FG Item",
+			warehouse="Stores - TCP1",
+			income_account="Sales - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			cost_center="Main - TCP1",
+			do_not_save=True,
+		)
 
 		pos.is_pos = 1
 		pos.update_stock = 1
 
-		pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 50})
-		pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - TCP1', 'amount': 60})
+		pos.append(
+			"payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - TCP1", "amount": 50}
+		)
+		pos.append("payments", {"mode_of_payment": "Cash", "account": "Cash - TCP1", "amount": 60})
 
-		pos.change_amount = 5.0
+		pos.write_off_outstanding_amount_automatically = 1
 		pos.insert()
 		pos.submit()
 
 		self.assertEqual(pos.grand_total, 100.0)
-		self.assertEqual(pos.write_off_amount, -5)
+		self.assertEqual(pos.write_off_amount, 0)
+
+	def test_auto_write_off_amount(self):
+		make_pos_profile(
+			company="_Test Company with perpetual inventory",
+			income_account="Sales - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+			write_off_account="_Test Write Off - TCP1",
+		)
+
+		make_purchase_receipt(
+			company="_Test Company with perpetual inventory",
+			item_code="_Test FG Item",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+		)
+
+		pos = create_sales_invoice(
+			company="_Test Company with perpetual inventory",
+			debit_to="Debtors - TCP1",
+			item_code="_Test FG Item",
+			warehouse="Stores - TCP1",
+			income_account="Sales - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			cost_center="Main - TCP1",
+			do_not_save=True,
+		)
+
+		pos.is_pos = 1
+		pos.update_stock = 1
+
+		pos.append(
+			"payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - TCP1", "amount": 50}
+		)
+		pos.append("payments", {"mode_of_payment": "Cash", "account": "Cash - TCP1", "amount": 40})
+
+		pos.write_off_outstanding_amount_automatically = 1
+		pos.insert()
+		pos.submit()
+
+		self.assertEqual(pos.grand_total, 100.0)
+		self.assertEqual(pos.write_off_amount, 10)
 
 	def test_pos_with_no_gl_entry_for_change_amount(self):
-		frappe.db.set_value('Accounts Settings', None, 'post_change_gl_entries', 0)
+		frappe.db.set_value("Accounts Settings", None, "post_change_gl_entries", 0)
 
-		make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
-			expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1")
+		make_pos_profile(
+			company="_Test Company with perpetual inventory",
+			income_account="Sales - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+			write_off_account="_Test Write Off - TCP1",
+		)
 
-		make_purchase_receipt(company= "_Test Company with perpetual inventory",
-			item_code= "_Test FG Item",warehouse= "Stores - TCP1", cost_center= "Main - TCP1")
+		make_purchase_receipt(
+			company="_Test Company with perpetual inventory",
+			item_code="_Test FG Item",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+		)
 
-		pos = create_sales_invoice(company= "_Test Company with perpetual inventory",
-			debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1",
-			income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1",
-			cost_center = "Main - TCP1", do_not_save=True)
+		pos = create_sales_invoice(
+			company="_Test Company with perpetual inventory",
+			debit_to="Debtors - TCP1",
+			item_code="_Test FG Item",
+			warehouse="Stores - TCP1",
+			income_account="Sales - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			cost_center="Main - TCP1",
+			do_not_save=True,
+		)
 
 		pos.is_pos = 1
 		pos.update_stock = 1
@@ -841,8 +1033,10 @@
 		for tax in taxes:
 			pos.append("taxes", tax)
 
-		pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 50})
-		pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - TCP1', 'amount': 60})
+		pos.append(
+			"payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - TCP1", "amount": 50}
+		)
+		pos.append("payments", {"mode_of_payment": "Cash", "account": "Cash - TCP1", "amount": 60})
 
 		pos.insert()
 		pos.submit()
@@ -852,40 +1046,50 @@
 
 		self.validate_pos_gl_entry(pos, pos, 60, validate_without_change_gle=True)
 
-		frappe.db.set_value('Accounts Settings', None, 'post_change_gl_entries', 1)
+		frappe.db.set_value("Accounts Settings", None, "post_change_gl_entries", 1)
 
 	def validate_pos_gl_entry(self, si, pos, cash_amount, validate_without_change_gle=False):
 		if validate_without_change_gle:
 			cash_amount -= pos.change_amount
 
 		# check stock ledger entries
-		sle = frappe.db.sql("""select * from `tabStock Ledger Entry`
+		sle = frappe.db.sql(
+			"""select * from `tabStock Ledger Entry`
 			where voucher_type = 'Sales Invoice' and voucher_no = %s""",
-			si.name, as_dict=1)[0]
+			si.name,
+			as_dict=1,
+		)[0]
 		self.assertTrue(sle)
-		self.assertEqual([sle.item_code, sle.warehouse, sle.actual_qty],
-			['_Test FG Item', 'Stores - TCP1', -1.0])
+		self.assertEqual(
+			[sle.item_code, sle.warehouse, sle.actual_qty], ["_Test FG Item", "Stores - TCP1", -1.0]
+		)
 
 		# check gl entries
-		gl_entries = frappe.db.sql("""select account, debit, credit
+		gl_entries = frappe.db.sql(
+			"""select account, debit, credit
 			from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
-			order by account asc, debit asc, credit asc""", si.name, as_dict=1)
+			order by account asc, debit asc, credit asc""",
+			si.name,
+			as_dict=1,
+		)
 		self.assertTrue(gl_entries)
 
-		stock_in_hand = get_inventory_account('_Test Company with perpetual inventory')
-		expected_gl_entries = sorted([
-			[si.debit_to, 100.0, 0.0],
-			[pos.items[0].income_account, 0.0, 89.09],
-			['Round Off - TCP1', 0.0, 0.01],
-			[pos.taxes[0].account_head, 0.0, 10.69],
-			[pos.taxes[1].account_head, 0.0, 0.21],
-			[stock_in_hand, 0.0, abs(sle.stock_value_difference)],
-			[pos.items[0].expense_account, abs(sle.stock_value_difference), 0.0],
-			[si.debit_to, 0.0, 50.0],
-			[si.debit_to, 0.0, cash_amount],
-			["_Test Bank - TCP1", 50, 0.0],
-			["Cash - TCP1", cash_amount, 0.0]
-		])
+		stock_in_hand = get_inventory_account("_Test Company with perpetual inventory")
+		expected_gl_entries = sorted(
+			[
+				[si.debit_to, 100.0, 0.0],
+				[pos.items[0].income_account, 0.0, 89.09],
+				["Round Off - TCP1", 0.0, 0.01],
+				[pos.taxes[0].account_head, 0.0, 10.69],
+				[pos.taxes[1].account_head, 0.0, 0.21],
+				[stock_in_hand, 0.0, abs(sle.stock_value_difference)],
+				[pos.items[0].expense_account, abs(sle.stock_value_difference), 0.0],
+				[si.debit_to, 0.0, 50.0],
+				[si.debit_to, 0.0, cash_amount],
+				["_Test Bank - TCP1", 50, 0.0],
+				["Cash - TCP1", cash_amount, 0.0],
+			]
+		)
 
 		for i, gle in enumerate(sorted(gl_entries, key=lambda gle: gle.account)):
 			self.assertEqual(expected_gl_entries[i][0], gle.account)
@@ -893,8 +1097,11 @@
 			self.assertEqual(expected_gl_entries[i][2], gle.credit)
 
 		si.cancel()
-		gle = frappe.db.sql("""select * from `tabGL Entry`
-			where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
+		gle = frappe.db.sql(
+			"""select * from `tabGL Entry`
+			where voucher_type='Sales Invoice' and voucher_no=%s""",
+			si.name,
+		)
 
 		self.assertTrue(gle)
 
@@ -914,21 +1121,29 @@
 		self.assertRaises(frappe.ValidationError, si.submit)
 
 	def test_sales_invoice_gl_entry_with_perpetual_inventory_no_item_code(self):
-		si = create_sales_invoice(company="_Test Company with perpetual inventory", debit_to = "Debtors - TCP1",
-			income_account="Sales - TCP1", cost_center = "Main - TCP1", do_not_save=True)
+		si = create_sales_invoice(
+			company="_Test Company with perpetual inventory",
+			debit_to="Debtors - TCP1",
+			income_account="Sales - TCP1",
+			cost_center="Main - TCP1",
+			do_not_save=True,
+		)
 		si.get("items")[0].item_code = None
 		si.insert()
 		si.submit()
 
-		gl_entries = frappe.db.sql("""select account, debit, credit
+		gl_entries = frappe.db.sql(
+			"""select account, debit, credit
 			from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
-			order by account asc""", si.name, as_dict=1)
+			order by account asc""",
+			si.name,
+			as_dict=1,
+		)
 		self.assertTrue(gl_entries)
 
-		expected_values = dict((d[0], d) for d in [
-			["Debtors - TCP1", 100.0, 0.0],
-			["Sales - TCP1", 0.0, 100.0]
-		])
+		expected_values = dict(
+			(d[0], d) for d in [["Debtors - TCP1", 100.0, 0.0], ["Sales - TCP1", 0.0, 100.0]]
+		)
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_values[gle.account][0], gle.account)
 			self.assertEqual(expected_values[gle.account][1], gle.debit)
@@ -937,25 +1152,32 @@
 	def test_sales_invoice_gl_entry_with_perpetual_inventory_non_stock_item(self):
 		si = create_sales_invoice(item="_Test Non Stock Item")
 
-		gl_entries = frappe.db.sql("""select account, debit, credit
+		gl_entries = frappe.db.sql(
+			"""select account, debit, credit
 			from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
-			order by account asc""", si.name, as_dict=1)
+			order by account asc""",
+			si.name,
+			as_dict=1,
+		)
 		self.assertTrue(gl_entries)
 
-		expected_values = dict((d[0], d) for d in [
-			[si.debit_to, 100.0, 0.0],
-			[test_records[1]["items"][0]["income_account"], 0.0, 100.0]
-		])
+		expected_values = dict(
+			(d[0], d)
+			for d in [
+				[si.debit_to, 100.0, 0.0],
+				[test_records[1]["items"][0]["income_account"], 0.0, 100.0],
+			]
+		)
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_values[gle.account][0], gle.account)
 			self.assertEqual(expected_values[gle.account][1], gle.debit)
 			self.assertEqual(expected_values[gle.account][2], gle.credit)
 
-
 	def _insert_purchase_receipt(self):
 		from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import (
 			test_records as pr_test_records,
 		)
+
 		pr = frappe.copy_doc(pr_test_records[0])
 		pr.naming_series = "_T-Purchase Receipt-"
 		pr.insert()
@@ -965,6 +1187,7 @@
 		from erpnext.stock.doctype.delivery_note.test_delivery_note import (
 			test_records as dn_test_records,
 		)
+
 		dn = frappe.copy_doc(dn_test_records[0])
 		dn.naming_series = "_T-Delivery Note-"
 		dn.insert()
@@ -982,24 +1205,37 @@
 
 		si = frappe.copy_doc(test_records[0])
 		si.allocate_advances_automatically = 0
-		si.append("advances", {
-			"doctype": "Sales Invoice Advance",
-			"reference_type": "Journal Entry",
-			"reference_name": jv.name,
-			"reference_row": jv.get("accounts")[0].name,
-			"advance_amount": 400,
-			"allocated_amount": 300,
-			"remarks": jv.remark
-		})
+		si.append(
+			"advances",
+			{
+				"doctype": "Sales Invoice Advance",
+				"reference_type": "Journal Entry",
+				"reference_name": jv.name,
+				"reference_row": jv.get("accounts")[0].name,
+				"advance_amount": 400,
+				"allocated_amount": 300,
+				"remarks": jv.remark,
+			},
+		)
 		si.insert()
 		si.submit()
 		si.load_from_db()
 
-		self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
-			where reference_name=%s""", si.name))
+		self.assertTrue(
+			frappe.db.sql(
+				"""select name from `tabJournal Entry Account`
+			where reference_name=%s""",
+				si.name,
+			)
+		)
 
-		self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
-			where reference_name=%s and credit_in_account_currency=300""", si.name))
+		self.assertTrue(
+			frappe.db.sql(
+				"""select name from `tabJournal Entry Account`
+			where reference_name=%s and credit_in_account_currency=300""",
+				si.name,
+			)
+		)
 
 		self.assertEqual(si.outstanding_amount, 262.0)
 
@@ -1021,29 +1257,34 @@
 		si.submit()
 
 		self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"))
-		self.assertEqual(frappe.db.get_value("Serial No", serial_nos[0],
-			"delivery_document_no"), si.name)
+		self.assertEqual(
+			frappe.db.get_value("Serial No", serial_nos[0], "delivery_document_no"), si.name
+		)
 
 		return si
 
 	def test_serialized_cancel(self):
 		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
 		si = self.test_serialized()
 		si.cancel()
 
 		serial_nos = get_serial_nos(si.get("items")[0].serial_no)
 
-		self.assertEqual(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"), "_Test Warehouse - _TC")
-		self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0],
-			"delivery_document_no"))
+		self.assertEqual(
+			frappe.db.get_value("Serial No", serial_nos[0], "warehouse"), "_Test Warehouse - _TC"
+		)
+		self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], "delivery_document_no"))
 		self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], "sales_invoice"))
 
 	def test_serialize_status(self):
-		serial_no = frappe.get_doc({
-			"doctype": "Serial No",
-			"item_code": "_Test Serialized Item With Series",
-			"serial_no": make_autoname("SR", "Serial No")
-		})
+		serial_no = frappe.get_doc(
+			{
+				"doctype": "Serial No",
+				"item_code": "_Test Serialized Item With Series",
+				"serial_no": make_autoname("SR", "Serial No"),
+			}
+		)
 		serial_no.save()
 
 		si = frappe.copy_doc(test_records[0])
@@ -1057,8 +1298,8 @@
 
 	def test_serial_numbers_against_delivery_note(self):
 		"""
-			check if the sales invoice item serial numbers and the delivery note items
-			serial numbers are same
+		check if the sales invoice item serial numbers and the delivery note items
+		serial numbers are same
 		"""
 		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
 		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@@ -1078,40 +1319,84 @@
 	def test_return_sales_invoice(self):
 		make_stock_entry(item_code="_Test Item", target="Stores - TCP1", qty=50, basic_rate=100)
 
-		actual_qty_0 = get_qty_after_transaction(item_code = "_Test Item", warehouse = "Stores - TCP1")
+		actual_qty_0 = get_qty_after_transaction(item_code="_Test Item", warehouse="Stores - TCP1")
 
-		si = create_sales_invoice(qty = 5, rate=500, update_stock=1, company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1")
+		si = create_sales_invoice(
+			qty=5,
+			rate=500,
+			update_stock=1,
+			company="_Test Company with perpetual inventory",
+			debit_to="Debtors - TCP1",
+			item_code="_Test Item",
+			warehouse="Stores - TCP1",
+			income_account="Sales - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			cost_center="Main - TCP1",
+		)
 
-
-		actual_qty_1 = get_qty_after_transaction(item_code = "_Test Item", warehouse = "Stores - TCP1")
+		actual_qty_1 = get_qty_after_transaction(item_code="_Test Item", warehouse="Stores - TCP1")
 
 		self.assertEqual(actual_qty_0 - 5, actual_qty_1)
 
 		# outgoing_rate
-		outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Sales Invoice",
-			"voucher_no": si.name}, "stock_value_difference") / 5
+		outgoing_rate = (
+			frappe.db.get_value(
+				"Stock Ledger Entry",
+				{"voucher_type": "Sales Invoice", "voucher_no": si.name},
+				"stock_value_difference",
+			)
+			/ 5
+		)
 
 		# return entry
-		si1 = create_sales_invoice(is_return=1, return_against=si.name, qty=-2, rate=500, update_stock=1, company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1")
+		si1 = create_sales_invoice(
+			is_return=1,
+			return_against=si.name,
+			qty=-2,
+			rate=500,
+			update_stock=1,
+			company="_Test Company with perpetual inventory",
+			debit_to="Debtors - TCP1",
+			item_code="_Test Item",
+			warehouse="Stores - TCP1",
+			income_account="Sales - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			cost_center="Main - TCP1",
+		)
 
-		actual_qty_2 = get_qty_after_transaction(item_code = "_Test Item", warehouse = "Stores - TCP1")
+		actual_qty_2 = get_qty_after_transaction(item_code="_Test Item", warehouse="Stores - TCP1")
 		self.assertEqual(actual_qty_1 + 2, actual_qty_2)
 
-		incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
+		incoming_rate, stock_value_difference = frappe.db.get_value(
+			"Stock Ledger Entry",
 			{"voucher_type": "Sales Invoice", "voucher_no": si1.name},
-			["incoming_rate", "stock_value_difference"])
+			["incoming_rate", "stock_value_difference"],
+		)
 
 		self.assertEqual(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3)))
-		stock_in_hand_account = get_inventory_account('_Test Company with perpetual inventory', si1.items[0].warehouse)
+		stock_in_hand_account = get_inventory_account(
+			"_Test Company with perpetual inventory", si1.items[0].warehouse
+		)
 
 		# Check gl entry
-		gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice",
-			"voucher_no": si1.name, "account": stock_in_hand_account}, "debit")
+		gle_warehouse_amount = frappe.db.get_value(
+			"GL Entry",
+			{"voucher_type": "Sales Invoice", "voucher_no": si1.name, "account": stock_in_hand_account},
+			"debit",
+		)
 
 		self.assertEqual(gle_warehouse_amount, stock_value_difference)
 
-		party_credited = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice",
-			"voucher_no": si1.name, "account": "Debtors - TCP1", "party": "_Test Customer"}, "credit")
+		party_credited = frappe.db.get_value(
+			"GL Entry",
+			{
+				"voucher_type": "Sales Invoice",
+				"voucher_no": si1.name,
+				"account": "Debtors - TCP1",
+				"party": "_Test Customer",
+			},
+			"credit",
+		)
 
 		self.assertEqual(party_credited, 1000)
 
@@ -1124,40 +1409,54 @@
 		asset = create_asset(item_code="Macbook Pro")
 
 		si = create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000)
-		return_si = create_sales_invoice(is_return=1, return_against=si.name, item_code="Macbook Pro", asset=asset.name, qty=-1, rate=90000)
+		return_si = create_sales_invoice(
+			is_return=1,
+			return_against=si.name,
+			item_code="Macbook Pro",
+			asset=asset.name,
+			qty=-1,
+			rate=90000,
+		)
 
 		disposal_account = frappe.get_cached_value("Company", "_Test Company", "disposal_account")
 
 		# Asset value is 100,000 but it was sold for 90,000, so there should be a loss of 10,000
 		loss_for_si = frappe.get_all(
 			"GL Entry",
-			filters = {
-				"voucher_no": si.name,
-				"account": disposal_account
-			},
-			fields = ["credit", "debit"]
+			filters={"voucher_no": si.name, "account": disposal_account},
+			fields=["credit", "debit"],
 		)[0]
 
 		loss_for_return_si = frappe.get_all(
 			"GL Entry",
-			filters = {
-				"voucher_no": return_si.name,
-				"account": disposal_account
-			},
-			fields = ["credit", "debit"]
+			filters={"voucher_no": return_si.name, "account": disposal_account},
+			fields=["credit", "debit"],
 		)[0]
 
-		self.assertEqual(loss_for_si['credit'], loss_for_return_si['debit'])
-		self.assertEqual(loss_for_si['debit'], loss_for_return_si['credit'])
+		self.assertEqual(loss_for_si["credit"], loss_for_return_si["debit"])
+		self.assertEqual(loss_for_si["debit"], loss_for_return_si["credit"])
 
 	def test_incoming_rate_for_stand_alone_credit_note(self):
-		return_si = create_sales_invoice(is_return=1, update_stock=1, qty=-1, rate=90000, incoming_rate=10,
-			company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', debit_to='Debtors - TCP1',
-			income_account='Sales - TCP1', expense_account='Cost of Goods Sold - TCP1', cost_center='Main - TCP1')
+		return_si = create_sales_invoice(
+			is_return=1,
+			update_stock=1,
+			qty=-1,
+			rate=90000,
+			incoming_rate=10,
+			company="_Test Company with perpetual inventory",
+			warehouse="Stores - TCP1",
+			debit_to="Debtors - TCP1",
+			income_account="Sales - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			cost_center="Main - TCP1",
+		)
 
-		incoming_rate = frappe.db.get_value('Stock Ledger Entry', {'voucher_no': return_si.name}, 'incoming_rate')
-		debit_amount = frappe.db.get_value('GL Entry',
-			{'voucher_no': return_si.name, 'account': 'Stock In Hand - TCP1'}, 'debit')
+		incoming_rate = frappe.db.get_value(
+			"Stock Ledger Entry", {"voucher_no": return_si.name}, "incoming_rate"
+		)
+		debit_amount = frappe.db.get_value(
+			"GL Entry", {"voucher_no": return_si.name, "account": "Stock In Hand - TCP1"}, "debit"
+		)
 
 		self.assertEqual(debit_amount, 10.0)
 		self.assertEqual(incoming_rate, 10.0)
@@ -1169,16 +1468,25 @@
 		si.insert()
 
 		expected_values = {
-			"keys": ["price_list_rate", "discount_percentage", "rate", "amount",
-				"base_price_list_rate", "base_rate", "base_amount",
-				"net_rate", "base_net_rate", "net_amount", "base_net_amount"],
+			"keys": [
+				"price_list_rate",
+				"discount_percentage",
+				"rate",
+				"amount",
+				"base_price_list_rate",
+				"base_rate",
+				"base_amount",
+				"net_rate",
+				"base_net_rate",
+				"net_amount",
+				"base_net_amount",
+			],
 			"_Test Item Home Desktop 100": [50, 0, 50, 500, 50, 50, 500, 25, 25, 250, 250],
 			"_Test Item Home Desktop 200": [150, 0, 150, 750, 150, 150, 750, 75, 75, 375, 375],
 		}
 
 		# check if children are saved
-		self.assertEqual(len(si.get("items")),
-			len(expected_values)-1)
+		self.assertEqual(len(si.get("items")), len(expected_values) - 1)
 
 		# check if item values are calculated
 		for d in si.get("items"):
@@ -1193,16 +1501,19 @@
 
 		# check tax calculation
 		expected_values = {
-			"keys": ["tax_amount", "tax_amount_after_discount_amount",
-				"base_tax_amount_after_discount_amount"],
+			"keys": [
+				"tax_amount",
+				"tax_amount_after_discount_amount",
+				"base_tax_amount_after_discount_amount",
+			],
 			"_Test Account Shipping Charges - _TC": [100, 100, 100],
 			"_Test Account Customs Duty - _TC": [62.5, 62.5, 62.5],
 			"_Test Account Excise Duty - _TC": [70, 70, 70],
 			"_Test Account Education Cess - _TC": [1.4, 1.4, 1.4],
-			"_Test Account S&H Education Cess - _TC": [.7, 0.7, 0.7],
+			"_Test Account S&H Education Cess - _TC": [0.7, 0.7, 0.7],
 			"_Test Account CST - _TC": [17.19, 17.19, 17.19],
 			"_Test Account VAT - _TC": [78.13, 78.13, 78.13],
-			"_Test Account Discount - _TC": [-95.49, -95.49, -95.49]
+			"_Test Account Discount - _TC": [-95.49, -95.49, -95.49],
 		}
 
 		for d in si.get("taxes"):
@@ -1210,19 +1521,26 @@
 				if expected_values.get(d.account_head):
 					self.assertEqual(d.get(k), expected_values[d.account_head][i])
 
-
 		self.assertEqual(si.total_taxes_and_charges, 234.43)
 		self.assertEqual(si.base_grand_total, 859.43)
 		self.assertEqual(si.grand_total, 859.43)
 
 	def test_multi_currency_gle(self):
-		si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
-			currency="USD", conversion_rate=50)
+		si = create_sales_invoice(
+			customer="_Test Customer USD",
+			debit_to="_Test Receivable USD - _TC",
+			currency="USD",
+			conversion_rate=50,
+		)
 
-		gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
+		gl_entries = frappe.db.sql(
+			"""select account, account_currency, debit, credit,
 			debit_in_account_currency, credit_in_account_currency
 			from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
-			order by account asc""", si.name, as_dict=1)
+			order by account asc""",
+			si.name,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
@@ -1232,26 +1550,35 @@
 				"debit": 5000,
 				"debit_in_account_currency": 100,
 				"credit": 0,
-				"credit_in_account_currency": 0
+				"credit_in_account_currency": 0,
 			},
 			"Sales - _TC": {
 				"account_currency": "INR",
 				"debit": 0,
 				"debit_in_account_currency": 0,
 				"credit": 5000,
-				"credit_in_account_currency": 5000
-			}
+				"credit_in_account_currency": 5000,
+			},
 		}
 
-		for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
+		for field in (
+			"account_currency",
+			"debit",
+			"debit_in_account_currency",
+			"credit",
+			"credit_in_account_currency",
+		):
 			for i, gle in enumerate(gl_entries):
 				self.assertEqual(expected_values[gle.account][field], gle[field])
 
 		# cancel
 		si.cancel()
 
-		gle = frappe.db.sql("""select name from `tabGL Entry`
-			where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
+		gle = frappe.db.sql(
+			"""select name from `tabGL Entry`
+			where voucher_type='Sales Invoice' and voucher_no=%s""",
+			si.name,
+		)
 
 		self.assertTrue(gle)
 
@@ -1259,32 +1586,52 @@
 		# Customer currency = USD
 
 		# Transaction currency cannot be INR
-		si1 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
-			do_not_save=True)
+		si1 = create_sales_invoice(
+			customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC", do_not_save=True
+		)
 
 		self.assertRaises(InvalidCurrency, si1.save)
 
 		# Transaction currency cannot be EUR
-		si2 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
-			currency="EUR", conversion_rate=80, do_not_save=True)
+		si2 = create_sales_invoice(
+			customer="_Test Customer USD",
+			debit_to="_Test Receivable USD - _TC",
+			currency="EUR",
+			conversion_rate=80,
+			do_not_save=True,
+		)
 
 		self.assertRaises(InvalidCurrency, si2.save)
 
 		# Transaction currency only allowed in USD
-		si3 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
-			currency="USD", conversion_rate=50)
+		si3 = create_sales_invoice(
+			customer="_Test Customer USD",
+			debit_to="_Test Receivable USD - _TC",
+			currency="USD",
+			conversion_rate=50,
+		)
 
 		# Party Account currency must be in USD, as there is existing GLE with USD
-		si4 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable - _TC",
-			currency="USD", conversion_rate=50, do_not_submit=True)
+		si4 = create_sales_invoice(
+			customer="_Test Customer USD",
+			debit_to="_Test Receivable - _TC",
+			currency="USD",
+			conversion_rate=50,
+			do_not_submit=True,
+		)
 
 		self.assertRaises(InvalidAccountCurrency, si4.submit)
 
 		# Party Account currency must be in USD, force customer currency as there is no GLE
 
 		si3.cancel()
-		si5 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable - _TC",
-			currency="USD", conversion_rate=50, do_not_submit=True)
+		si5 = create_sales_invoice(
+			customer="_Test Customer USD",
+			debit_to="_Test Receivable - _TC",
+			currency="USD",
+			conversion_rate=50,
+			do_not_submit=True,
+		)
 
 		self.assertRaises(InvalidAccountCurrency, si5.submit)
 
@@ -1292,12 +1639,12 @@
 		si = create_sales_invoice(item_code="_Test Item", qty=1, do_not_submit=True)
 		price_list_rate = flt(100) * flt(si.plc_conversion_rate)
 		si.items[0].price_list_rate = price_list_rate
-		si.items[0].margin_type = 'Percentage'
+		si.items[0].margin_type = "Percentage"
 		si.items[0].margin_rate_or_amount = 25
 		si.items[0].discount_amount = 0.0
 		si.items[0].discount_percentage = 0.0
 		si.save()
-		self.assertEqual(si.get("items")[0].rate, flt((price_list_rate*25)/100 + price_list_rate))
+		self.assertEqual(si.get("items")[0].rate, flt((price_list_rate * 25) / 100 + price_list_rate))
 
 	def test_outstanding_amount_after_advance_jv_cancelation(self):
 		from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
@@ -1305,89 +1652,107 @@
 		)
 
 		jv = frappe.copy_doc(jv_test_records[0])
-		jv.accounts[0].is_advance = 'Yes'
+		jv.accounts[0].is_advance = "Yes"
 		jv.insert()
 		jv.submit()
 
 		si = frappe.copy_doc(test_records[0])
-		si.append("advances", {
-			"doctype": "Sales Invoice Advance",
-			"reference_type": "Journal Entry",
-			"reference_name": jv.name,
-			"reference_row": jv.get("accounts")[0].name,
-			"advance_amount": 400,
-			"allocated_amount": 300,
-			"remarks": jv.remark
-		})
+		si.append(
+			"advances",
+			{
+				"doctype": "Sales Invoice Advance",
+				"reference_type": "Journal Entry",
+				"reference_name": jv.name,
+				"reference_row": jv.get("accounts")[0].name,
+				"advance_amount": 400,
+				"allocated_amount": 300,
+				"remarks": jv.remark,
+			},
+		)
 		si.insert()
 		si.submit()
 		si.load_from_db()
 
-		#check outstanding after advance allocation
-		self.assertEqual(flt(si.outstanding_amount),
-			flt(si.rounded_total - si.total_advance, si.precision("outstanding_amount")))
+		# check outstanding after advance allocation
+		self.assertEqual(
+			flt(si.outstanding_amount),
+			flt(si.rounded_total - si.total_advance, si.precision("outstanding_amount")),
+		)
 
-		#added to avoid Document has been modified exception
+		# added to avoid Document has been modified exception
 		jv = frappe.get_doc("Journal Entry", jv.name)
 		jv.cancel()
 
 		si.load_from_db()
-		#check outstanding after advance cancellation
-		self.assertEqual(flt(si.outstanding_amount),
-			flt(si.rounded_total + si.total_advance, si.precision("outstanding_amount")))
+		# check outstanding after advance cancellation
+		self.assertEqual(
+			flt(si.outstanding_amount),
+			flt(si.rounded_total + si.total_advance, si.precision("outstanding_amount")),
+		)
 
 	def test_outstanding_amount_after_advance_payment_entry_cancelation(self):
-		pe = frappe.get_doc({
-			"doctype": "Payment Entry",
-			"payment_type": "Receive",
-			"party_type": "Customer",
-			"party": "_Test Customer",
-			"company": "_Test Company",
-			"paid_from_account_currency": "INR",
-			"paid_to_account_currency": "INR",
-			"source_exchange_rate": 1,
-			"target_exchange_rate": 1,
-			"reference_no": "1",
-			"reference_date": nowdate(),
-			"received_amount": 300,
-			"paid_amount": 300,
-			"paid_from": "_Test Receivable - _TC",
-			"paid_to": "_Test Cash - _TC"
-		})
+		pe = frappe.get_doc(
+			{
+				"doctype": "Payment Entry",
+				"payment_type": "Receive",
+				"party_type": "Customer",
+				"party": "_Test Customer",
+				"company": "_Test Company",
+				"paid_from_account_currency": "INR",
+				"paid_to_account_currency": "INR",
+				"source_exchange_rate": 1,
+				"target_exchange_rate": 1,
+				"reference_no": "1",
+				"reference_date": nowdate(),
+				"received_amount": 300,
+				"paid_amount": 300,
+				"paid_from": "_Test Receivable - _TC",
+				"paid_to": "_Test Cash - _TC",
+			}
+		)
 		pe.insert()
 		pe.submit()
 
 		si = frappe.copy_doc(test_records[0])
 		si.is_pos = 0
-		si.append("advances", {
-			"doctype": "Sales Invoice Advance",
-			"reference_type": "Payment Entry",
-			"reference_name": pe.name,
-			"advance_amount": 300,
-			"allocated_amount": 300,
-			"remarks": pe.remarks
-		})
+		si.append(
+			"advances",
+			{
+				"doctype": "Sales Invoice Advance",
+				"reference_type": "Payment Entry",
+				"reference_name": pe.name,
+				"advance_amount": 300,
+				"allocated_amount": 300,
+				"remarks": pe.remarks,
+			},
+		)
 		si.insert()
 		si.submit()
 
 		si.load_from_db()
 
-		#check outstanding after advance allocation
-		self.assertEqual(flt(si.outstanding_amount),
-			flt(si.rounded_total - si.total_advance, si.precision("outstanding_amount")))
+		# check outstanding after advance allocation
+		self.assertEqual(
+			flt(si.outstanding_amount),
+			flt(si.rounded_total - si.total_advance, si.precision("outstanding_amount")),
+		)
 
-		#added to avoid Document has been modified exception
+		# added to avoid Document has been modified exception
 		pe = frappe.get_doc("Payment Entry", pe.name)
 		pe.cancel()
 
 		si.load_from_db()
-		#check outstanding after advance cancellation
-		self.assertEqual(flt(si.outstanding_amount),
-			flt(si.rounded_total + si.total_advance, si.precision("outstanding_amount")))
+		# check outstanding after advance cancellation
+		self.assertEqual(
+			flt(si.outstanding_amount),
+			flt(si.rounded_total + si.total_advance, si.precision("outstanding_amount")),
+		)
 
 	def test_multiple_uom_in_selling(self):
-		frappe.db.sql("""delete from `tabItem Price`
-			where price_list='_Test Price List' and item_code='_Test Item'""")
+		frappe.db.sql(
+			"""delete from `tabItem Price`
+			where price_list='_Test Price List' and item_code='_Test Item'"""
+		)
 		item_price = frappe.new_doc("Item Price")
 		item_price.price_list = "_Test Price List"
 		item_price.item_code = "_Test Item"
@@ -1401,9 +1766,18 @@
 		si.save()
 
 		expected_values = {
-			"keys": ["price_list_rate", "stock_uom", "uom", "conversion_factor", "rate", "amount",
-				"base_price_list_rate", "base_rate", "base_amount"],
-			"_Test Item": [1000, "_Test UOM", "_Test UOM 1", 10.0, 1000, 1000, 1000, 1000, 1000]
+			"keys": [
+				"price_list_rate",
+				"stock_uom",
+				"uom",
+				"conversion_factor",
+				"rate",
+				"amount",
+				"base_price_list_rate",
+				"base_rate",
+				"base_amount",
+			],
+			"_Test Item": [1000, "_Test UOM", "_Test UOM 1", 10.0, 1000, 1000, 1000, 1000, 1000],
 		}
 
 		# check if the conversion_factor and price_list_rate is calculated according to uom
@@ -1419,23 +1793,10 @@
 		itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(si)
 
 		expected_itemised_tax = {
-			"_Test Item": {
-				"Service Tax": {
-					"tax_rate": 10.0,
-					"tax_amount": 1000.0
-				}
-			},
-			"_Test Item 2": {
-				"Service Tax": {
-					"tax_rate": 10.0,
-					"tax_amount": 500.0
-				}
-			}
+			"_Test Item": {"Service Tax": {"tax_rate": 10.0, "tax_amount": 1000.0}},
+			"_Test Item 2": {"Service Tax": {"tax_rate": 10.0, "tax_amount": 500.0}},
 		}
-		expected_itemised_taxable_amount = {
-			"_Test Item": 10000.0,
-			"_Test Item 2": 5000.0
-		}
+		expected_itemised_taxable_amount = {"_Test Item": 10000.0, "_Test Item 2": 5000.0}
 
 		self.assertEqual(itemised_tax, expected_itemised_tax)
 		self.assertEqual(itemised_taxable_amount, expected_itemised_taxable_amount)
@@ -1444,57 +1805,71 @@
 
 	def create_si_to_test_tax_breakup(self):
 		si = create_sales_invoice(qty=100, rate=50, do_not_save=True)
-		si.append("items", {
-			"item_code": "_Test Item",
-			"warehouse": "_Test Warehouse - _TC",
-			"qty": 100,
-			"rate": 50,
-			"income_account": "Sales - _TC",
-			"expense_account": "Cost of Goods Sold - _TC",
-			"cost_center": "_Test Cost Center - _TC"
-		})
-		si.append("items", {
-			"item_code": "_Test Item 2",
-			"warehouse": "_Test Warehouse - _TC",
-			"qty": 100,
-			"rate": 50,
-			"income_account": "Sales - _TC",
-			"expense_account": "Cost of Goods Sold - _TC",
-			"cost_center": "_Test Cost Center - _TC"
-		})
+		si.append(
+			"items",
+			{
+				"item_code": "_Test Item",
+				"warehouse": "_Test Warehouse - _TC",
+				"qty": 100,
+				"rate": 50,
+				"income_account": "Sales - _TC",
+				"expense_account": "Cost of Goods Sold - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+			},
+		)
+		si.append(
+			"items",
+			{
+				"item_code": "_Test Item 2",
+				"warehouse": "_Test Warehouse - _TC",
+				"qty": 100,
+				"rate": 50,
+				"income_account": "Sales - _TC",
+				"expense_account": "Cost of Goods Sold - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+			},
+		)
 
-		si.append("taxes", {
-			"charge_type": "On Net Total",
-			"account_head": "_Test Account Service Tax - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "Service Tax",
-			"rate": 10
-		})
+		si.append(
+			"taxes",
+			{
+				"charge_type": "On Net Total",
+				"account_head": "_Test Account Service Tax - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "Service Tax",
+				"rate": 10,
+			},
+		)
 		si.insert()
 		return si
 
 	def test_company_monthly_sales(self):
-		existing_current_month_sales = frappe.get_cached_value('Company',  "_Test Company",  "total_monthly_sales")
+		existing_current_month_sales = frappe.get_cached_value(
+			"Company", "_Test Company", "total_monthly_sales"
+		)
 
 		si = create_sales_invoice()
-		current_month_sales = frappe.get_cached_value('Company',  "_Test Company",  "total_monthly_sales")
+		current_month_sales = frappe.get_cached_value("Company", "_Test Company", "total_monthly_sales")
 		self.assertEqual(current_month_sales, existing_current_month_sales + si.base_grand_total)
 
 		si.cancel()
-		current_month_sales = frappe.get_cached_value('Company',  "_Test Company",  "total_monthly_sales")
+		current_month_sales = frappe.get_cached_value("Company", "_Test Company", "total_monthly_sales")
 		self.assertEqual(current_month_sales, existing_current_month_sales)
 
 	def test_rounding_adjustment(self):
 		si = create_sales_invoice(rate=24900, do_not_save=True)
 		for tax in ["Tax 1", "Tax2"]:
-			si.append("taxes", {
-				"charge_type": "On Net Total",
-				"account_head": "_Test Account Service Tax - _TC",
-				"description": tax,
-				"rate": 14,
-				"cost_center": "_Test Cost Center - _TC",
-				"included_in_print_rate": 1
-			})
+			si.append(
+				"taxes",
+				{
+					"charge_type": "On Net Total",
+					"account_head": "_Test Account Service Tax - _TC",
+					"description": tax,
+					"rate": 14,
+					"cost_center": "_Test Cost Center - _TC",
+					"included_in_print_rate": 1,
+				},
+			)
 		si.save()
 		si.submit()
 		self.assertEqual(si.net_total, 19453.13)
@@ -1502,16 +1877,23 @@
 		self.assertEqual(si.total_taxes_and_charges, 5446.88)
 		self.assertEqual(si.rounding_adjustment, -0.01)
 
-		expected_values = dict((d[0], d) for d in [
-			[si.debit_to, 24900, 0.0],
-			["_Test Account Service Tax - _TC", 0.0, 5446.88],
-			["Sales - _TC", 0.0, 19453.13],
-			["Round Off - _TC", 0.01, 0.0]
-		])
+		expected_values = dict(
+			(d[0], d)
+			for d in [
+				[si.debit_to, 24900, 0.0],
+				["_Test Account Service Tax - _TC", 0.0, 5446.88],
+				["Sales - _TC", 0.0, 19453.13],
+				["Round Off - _TC", 0.01, 0.0],
+			]
+		)
 
-		gl_entries = frappe.db.sql("""select account, debit, credit
+		gl_entries = frappe.db.sql(
+			"""select account, debit, credit
 			from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
-			order by account asc""", si.name, as_dict=1)
+			order by account asc""",
+			si.name,
+			as_dict=1,
+		)
 
 		for gle in gl_entries:
 			self.assertEqual(expected_values[gle.account][0], gle.account)
@@ -1521,23 +1903,29 @@
 	def test_rounding_adjustment_2(self):
 		si = create_sales_invoice(rate=400, do_not_save=True)
 		for rate in [400, 600, 100]:
-			si.append("items", {
-				"item_code": "_Test Item",
-				"warehouse": "_Test Warehouse - _TC",
-				"qty": 1,
-				"rate": rate,
-				"income_account": "Sales - _TC",
-				"cost_center": "_Test Cost Center - _TC"
-			})
+			si.append(
+				"items",
+				{
+					"item_code": "_Test Item",
+					"warehouse": "_Test Warehouse - _TC",
+					"qty": 1,
+					"rate": rate,
+					"income_account": "Sales - _TC",
+					"cost_center": "_Test Cost Center - _TC",
+				},
+			)
 		for tax_account in ["_Test Account VAT - _TC", "_Test Account Service Tax - _TC"]:
-			si.append("taxes", {
-				"charge_type": "On Net Total",
-				"account_head": tax_account,
-				"description": tax_account,
-				"rate": 9,
-				"cost_center": "_Test Cost Center - _TC",
-				"included_in_print_rate": 1
-			})
+			si.append(
+				"taxes",
+				{
+					"charge_type": "On Net Total",
+					"account_head": tax_account,
+					"description": tax_account,
+					"rate": 9,
+					"cost_center": "_Test Cost Center - _TC",
+					"included_in_print_rate": 1,
+				},
+			)
 		si.save()
 		si.submit()
 		self.assertEqual(si.net_total, 1271.19)
@@ -1545,16 +1933,23 @@
 		self.assertEqual(si.total_taxes_and_charges, 228.82)
 		self.assertEqual(si.rounding_adjustment, -0.01)
 
-		expected_values = dict((d[0], d) for d in [
-			[si.debit_to, 1500, 0.0],
-			["_Test Account Service Tax - _TC", 0.0, 114.41],
-			["_Test Account VAT - _TC", 0.0, 114.41],
-			["Sales - _TC", 0.0, 1271.18]
-		])
+		expected_values = dict(
+			(d[0], d)
+			for d in [
+				[si.debit_to, 1500, 0.0],
+				["_Test Account Service Tax - _TC", 0.0, 114.41],
+				["_Test Account VAT - _TC", 0.0, 114.41],
+				["Sales - _TC", 0.0, 1271.18],
+			]
+		)
 
-		gl_entries = frappe.db.sql("""select account, debit, credit
+		gl_entries = frappe.db.sql(
+			"""select account, debit, credit
 			from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
-			order by account asc""", si.name, as_dict=1)
+			order by account asc""",
+			si.name,
+			as_dict=1,
+		)
 
 		for gle in gl_entries:
 			self.assertEqual(expected_values[gle.account][0], gle.account)
@@ -1562,27 +1957,44 @@
 			self.assertEqual(expected_values[gle.account][2], gle.credit)
 
 	def test_rounding_adjustment_3(self):
+		from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import (
+			create_dimension,
+			disable_dimension,
+		)
+
+		create_dimension()
+
 		si = create_sales_invoice(do_not_save=True)
 		si.items = []
 		for d in [(1122, 2), (1122.01, 1), (1122.01, 1)]:
-			si.append("items", {
-				"item_code": "_Test Item",
-				"gst_hsn_code": "999800",
-				"warehouse": "_Test Warehouse - _TC",
-				"qty": d[1],
-				"rate": d[0],
-				"income_account": "Sales - _TC",
-				"cost_center": "_Test Cost Center - _TC"
-			})
+			si.append(
+				"items",
+				{
+					"item_code": "_Test Item",
+					"gst_hsn_code": "999800",
+					"warehouse": "_Test Warehouse - _TC",
+					"qty": d[1],
+					"rate": d[0],
+					"income_account": "Sales - _TC",
+					"cost_center": "_Test Cost Center - _TC",
+				},
+			)
 		for tax_account in ["_Test Account VAT - _TC", "_Test Account Service Tax - _TC"]:
-			si.append("taxes", {
-				"charge_type": "On Net Total",
-				"account_head": tax_account,
-				"description": tax_account,
-				"rate": 6,
-				"cost_center": "_Test Cost Center - _TC",
-				"included_in_print_rate": 1
-			})
+			si.append(
+				"taxes",
+				{
+					"charge_type": "On Net Total",
+					"account_head": tax_account,
+					"description": tax_account,
+					"rate": 6,
+					"cost_center": "_Test Cost Center - _TC",
+					"included_in_print_rate": 1,
+				},
+			)
+
+		si.cost_center = "_Test Cost Center 2 - _TC"
+		si.location = "Block 1"
+
 		si.save()
 		si.submit()
 		self.assertEqual(si.net_total, 4007.16)
@@ -1590,31 +2002,52 @@
 		self.assertEqual(si.total_taxes_and_charges, 480.86)
 		self.assertEqual(si.rounding_adjustment, -0.02)
 
-		expected_values = dict((d[0], d) for d in [
-			[si.debit_to, 4488.0, 0.0],
-			["_Test Account Service Tax - _TC", 0.0, 240.43],
-			["_Test Account VAT - _TC", 0.0, 240.43],
-			["Sales - _TC", 0.0, 4007.15],
-			["Round Off - _TC", 0.01, 0]
-		])
+		expected_values = dict(
+			(d[0], d)
+			for d in [
+				[si.debit_to, 4488.0, 0.0],
+				["_Test Account Service Tax - _TC", 0.0, 240.43],
+				["_Test Account VAT - _TC", 0.0, 240.43],
+				["Sales - _TC", 0.0, 4007.15],
+				["Round Off - _TC", 0.01, 0],
+			]
+		)
 
-		gl_entries = frappe.db.sql("""select account, debit, credit
+		gl_entries = frappe.db.sql(
+			"""select account, debit, credit
 			from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
-			order by account asc""", si.name, as_dict=1)
+			order by account asc""",
+			si.name,
+			as_dict=1,
+		)
 
 		debit_credit_diff = 0
 		for gle in gl_entries:
 			self.assertEqual(expected_values[gle.account][0], gle.account)
 			self.assertEqual(expected_values[gle.account][1], gle.debit)
 			self.assertEqual(expected_values[gle.account][2], gle.credit)
-			debit_credit_diff += (gle.debit - gle.credit)
+			debit_credit_diff += gle.debit - gle.credit
 
 		self.assertEqual(debit_credit_diff, 0)
 
+		round_off_gle = frappe.db.get_value(
+			"GL Entry",
+			{"voucher_type": "Sales Invoice", "voucher_no": si.name, "account": "Round Off - _TC"},
+			["cost_center", "location"],
+			as_dict=1,
+		)
+
+		self.assertEqual(round_off_gle.cost_center, "_Test Cost Center 2 - _TC")
+		self.assertEqual(round_off_gle.location, "Block 1")
+
+		disable_dimension()
+
 	def test_sales_invoice_with_shipping_rule(self):
 		from erpnext.accounts.doctype.shipping_rule.test_shipping_rule import create_shipping_rule
 
-		shipping_rule = create_shipping_rule(shipping_rule_type = "Selling", shipping_rule_name = "Shipping Rule - Sales Invoice Test")
+		shipping_rule = create_shipping_rule(
+			shipping_rule_type="Selling", shipping_rule_name="Shipping Rule - Sales Invoice Test"
+		)
 
 		si = frappe.copy_doc(test_records[2])
 
@@ -1627,29 +2060,32 @@
 		self.assertEqual(si.total_taxes_and_charges, 468.85)
 		self.assertEqual(si.grand_total, 1718.85)
 
-
-
 	def test_create_invoice_without_terms(self):
 		si = create_sales_invoice(do_not_save=1)
-		self.assertFalse(si.get('payment_schedule'))
+		self.assertFalse(si.get("payment_schedule"))
 
 		si.insert()
-		self.assertTrue(si.get('payment_schedule'))
+		self.assertTrue(si.get("payment_schedule"))
 
 	def test_duplicate_due_date_in_terms(self):
 		si = create_sales_invoice(do_not_save=1)
-		si.append('payment_schedule', dict(due_date='2017-01-01', invoice_portion=50.00, payment_amount=50))
-		si.append('payment_schedule', dict(due_date='2017-01-01', invoice_portion=50.00, payment_amount=50))
+		si.append(
+			"payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50)
+		)
+		si.append(
+			"payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50)
+		)
 
 		self.assertRaises(frappe.ValidationError, si.insert)
 
 	def test_credit_note(self):
 		from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
 
-		si = create_sales_invoice(item_code = "_Test Item", qty = (5 * -1), rate=500, is_return = 1)
+		si = create_sales_invoice(item_code="_Test Item", qty=(5 * -1), rate=500, is_return=1)
 
-		outstanding_amount = get_outstanding_amount(si.doctype,
-			si.name, "Debtors - _TC", si.customer, "Customer")
+		outstanding_amount = get_outstanding_amount(
+			si.doctype, si.name, "Debtors - _TC", si.customer, "Customer"
+		)
 
 		self.assertEqual(si.outstanding_amount, outstanding_amount)
 
@@ -1664,30 +2100,31 @@
 		pe.insert()
 		pe.submit()
 
-		si_doc = frappe.get_doc('Sales Invoice', si.name)
+		si_doc = frappe.get_doc("Sales Invoice", si.name)
 		self.assertEqual(si_doc.outstanding_amount, 0)
 
 	def test_sales_invoice_with_cost_center(self):
 		from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
+
 		cost_center = "_Test Cost Center for BS Account - _TC"
 		create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
 
-		si =  create_sales_invoice_against_cost_center(cost_center=cost_center, debit_to="Debtors - _TC")
+		si = create_sales_invoice_against_cost_center(cost_center=cost_center, debit_to="Debtors - _TC")
 		self.assertEqual(si.cost_center, cost_center)
 
 		expected_values = {
-			"Debtors - _TC": {
-				"cost_center": cost_center
-			},
-			"Sales - _TC": {
-				"cost_center": cost_center
-			}
+			"Debtors - _TC": {"cost_center": cost_center},
+			"Sales - _TC": {"cost_center": cost_center},
 		}
 
-		gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit,
+		gl_entries = frappe.db.sql(
+			"""select account, cost_center, account_currency, debit, credit,
 			debit_in_account_currency, credit_in_account_currency
 			from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
-			order by account asc""", si.name, as_dict=1)
+			order by account asc""",
+			si.name,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
@@ -1697,16 +2134,20 @@
 	def test_sales_invoice_with_project_link(self):
 		from erpnext.projects.doctype.project.test_project import make_project
 
-		project = make_project({
-			'project_name': 'Sales Invoice Project',
-			'project_template_name': 'Test Project Template',
-			'start_date': '2020-01-01'
-		})
-		item_project = make_project({
-			'project_name': 'Sales Invoice Item Project',
-			'project_template_name': 'Test Project Template',
-			'start_date': '2019-06-01'
-		})
+		project = make_project(
+			{
+				"project_name": "Sales Invoice Project",
+				"project_template_name": "Test Project Template",
+				"start_date": "2020-01-01",
+			}
+		)
+		item_project = make_project(
+			{
+				"project_name": "Sales Invoice Item Project",
+				"project_template_name": "Test Project Template",
+				"start_date": "2019-06-01",
+			}
+		)
 
 		sales_invoice = create_sales_invoice(do_not_save=1)
 		sales_invoice.items[0].project = item_project.name
@@ -1715,18 +2156,18 @@
 		sales_invoice.submit()
 
 		expected_values = {
-			"Debtors - _TC": {
-				"project": project.name
-			},
-			"Sales - _TC": {
-				"project": item_project.name
-			}
+			"Debtors - _TC": {"project": project.name},
+			"Sales - _TC": {"project": item_project.name},
 		}
 
-		gl_entries = frappe.db.sql("""select account, cost_center, project, account_currency, debit, credit,
+		gl_entries = frappe.db.sql(
+			"""select account, cost_center, project, account_currency, debit, credit,
 			debit_in_account_currency, credit_in_account_currency
 			from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
-			order by account asc""", sales_invoice.name, as_dict=1)
+			order by account asc""",
+			sales_invoice.name,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
@@ -1735,21 +2176,21 @@
 
 	def test_sales_invoice_without_cost_center(self):
 		cost_center = "_Test Cost Center - _TC"
-		si =  create_sales_invoice(debit_to="Debtors - _TC")
+		si = create_sales_invoice(debit_to="Debtors - _TC")
 
 		expected_values = {
-			"Debtors - _TC": {
-				"cost_center": None
-			},
-			"Sales - _TC": {
-				"cost_center": cost_center
-			}
+			"Debtors - _TC": {"cost_center": None},
+			"Sales - _TC": {"cost_center": cost_center},
 		}
 
-		gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit,
+		gl_entries = frappe.db.sql(
+			"""select account, cost_center, account_currency, debit, credit,
 			debit_in_account_currency, credit_in_account_currency
 			from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
-			order by account asc""", si.name, as_dict=1)
+			order by account asc""",
+			si.name,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
@@ -1757,8 +2198,11 @@
 			self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
 
 	def test_deferred_revenue(self):
-		deferred_account = create_account(account_name="Deferred Revenue",
-			parent_account="Current Liabilities - _TC", company="_Test Company")
+		deferred_account = create_account(
+			account_name="Deferred Revenue",
+			parent_account="Current Liabilities - _TC",
+			company="_Test Company",
+		)
 
 		item = create_item("_Test Item for Deferred Accounting")
 		item.enable_deferred_revenue = 1
@@ -1774,14 +2218,16 @@
 		si.save()
 		si.submit()
 
-		pda1 = frappe.get_doc(dict(
-			doctype='Process Deferred Accounting',
-			posting_date=nowdate(),
-			start_date="2019-01-01",
-			end_date="2019-03-31",
-			type="Income",
-			company="_Test Company"
-		))
+		pda1 = frappe.get_doc(
+			dict(
+				doctype="Process Deferred Accounting",
+				posting_date=nowdate(),
+				start_date="2019-01-01",
+				end_date="2019-03-31",
+				type="Income",
+				company="_Test Company",
+			)
+		)
 
 		pda1.insert()
 		pda1.submit()
@@ -1792,17 +2238,28 @@
 			[deferred_account, 43.08, 0.0, "2019-02-28"],
 			["Sales - _TC", 0.0, 43.08, "2019-02-28"],
 			[deferred_account, 23.07, 0.0, "2019-03-15"],
-			["Sales - _TC", 0.0, 23.07, "2019-03-15"]
+			["Sales - _TC", 0.0, 23.07, "2019-03-15"],
 		]
 
 		check_gl_entries(self, si.name, expected_gle, "2019-01-30")
 
-	def test_fixed_deferred_revenue(self):
-		deferred_account = create_account(account_name="Deferred Revenue",
-			parent_account="Current Liabilities - _TC", company="_Test Company")
+	def test_deferred_revenue_missing_account(self):
+		si = create_sales_invoice(posting_date="2019-01-10", do_not_submit=True)
+		si.items[0].enable_deferred_revenue = 1
+		si.items[0].service_start_date = "2019-01-10"
+		si.items[0].service_end_date = "2019-03-15"
 
-		acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
-		acc_settings.book_deferred_entries_based_on = 'Months'
+		self.assertRaises(frappe.ValidationError, si.save)
+
+	def test_fixed_deferred_revenue(self):
+		deferred_account = create_account(
+			account_name="Deferred Revenue",
+			parent_account="Current Liabilities - _TC",
+			company="_Test Company",
+		)
+
+		acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
+		acc_settings.book_deferred_entries_based_on = "Months"
 		acc_settings.save()
 
 		item = create_item("_Test Item for Deferred Accounting")
@@ -1811,7 +2268,9 @@
 		item.no_of_months = 12
 		item.save()
 
-		si = create_sales_invoice(item=item.name, posting_date="2019-01-16", rate=50000, do_not_submit=True)
+		si = create_sales_invoice(
+			item=item.name, posting_date="2019-01-16", rate=50000, do_not_submit=True
+		)
 		si.items[0].enable_deferred_revenue = 1
 		si.items[0].service_start_date = "2019-01-16"
 		si.items[0].service_end_date = "2019-03-31"
@@ -1819,14 +2278,16 @@
 		si.save()
 		si.submit()
 
-		pda1 = frappe.get_doc(dict(
-			doctype='Process Deferred Accounting',
-			posting_date='2019-03-31',
-			start_date="2019-01-01",
-			end_date="2019-03-31",
-			type="Income",
-			company="_Test Company"
-		))
+		pda1 = frappe.get_doc(
+			dict(
+				doctype="Process Deferred Accounting",
+				posting_date="2019-03-31",
+				start_date="2019-01-01",
+				end_date="2019-03-31",
+				type="Income",
+				company="_Test Company",
+			)
+		)
 
 		pda1.insert()
 		pda1.submit()
@@ -1837,13 +2298,13 @@
 			[deferred_account, 20000.0, 0.0, "2019-02-28"],
 			["Sales - _TC", 0.0, 20000.0, "2019-02-28"],
 			[deferred_account, 20000.0, 0.0, "2019-03-31"],
-			["Sales - _TC", 0.0, 20000.0, "2019-03-31"]
+			["Sales - _TC", 0.0, 20000.0, "2019-03-31"],
 		]
 
 		check_gl_entries(self, si.name, expected_gle, "2019-01-30")
 
-		acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
-		acc_settings.book_deferred_entries_based_on = 'Days'
+		acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
+		acc_settings.book_deferred_entries_based_on = "Days"
 		acc_settings.save()
 
 	def test_inter_company_transaction(self):
@@ -1852,45 +2313,47 @@
 		create_internal_customer(
 			customer_name="_Test Internal Customer",
 			represents_company="_Test Company 1",
-			allowed_to_interact_with="Wind Power LLC"
+			allowed_to_interact_with="Wind Power LLC",
 		)
 
 		if not frappe.db.exists("Supplier", "_Test Internal Supplier"):
-			supplier = frappe.get_doc({
-				"supplier_group": "_Test Supplier Group",
-				"supplier_name": "_Test Internal Supplier",
-				"doctype": "Supplier",
-				"is_internal_supplier": 1,
-				"represents_company": "Wind Power LLC"
-			})
+			supplier = frappe.get_doc(
+				{
+					"supplier_group": "_Test Supplier Group",
+					"supplier_name": "_Test Internal Supplier",
+					"doctype": "Supplier",
+					"is_internal_supplier": 1,
+					"represents_company": "Wind Power LLC",
+				}
+			)
 
-			supplier.append("companies", {
-				"company": "_Test Company 1"
-			})
+			supplier.append("companies", {"company": "_Test Company 1"})
 
 			supplier.insert()
 
 		si = create_sales_invoice(
-			company = "Wind Power LLC",
-			customer = "_Test Internal Customer",
-			debit_to = "Debtors - WP",
-			warehouse = "Stores - WP",
-			income_account = "Sales - WP",
-			expense_account = "Cost of Goods Sold - WP",
-			cost_center = "Main - WP",
-			currency = "USD",
-			do_not_save = 1
+			company="Wind Power LLC",
+			customer="_Test Internal Customer",
+			debit_to="Debtors - WP",
+			warehouse="Stores - WP",
+			income_account="Sales - WP",
+			expense_account="Cost of Goods Sold - WP",
+			cost_center="Main - WP",
+			currency="USD",
+			do_not_save=1,
 		)
 
 		si.selling_price_list = "_Test Price List Rest of the World"
 		si.submit()
 
 		target_doc = make_inter_company_transaction("Sales Invoice", si.name)
-		target_doc.items[0].update({
-			"expense_account": "Cost of Goods Sold - _TC1",
-			"cost_center": "Main - _TC1",
-			"warehouse": "Stores - _TC1"
-		})
+		target_doc.items[0].update(
+			{
+				"expense_account": "Cost of Goods Sold - _TC1",
+				"cost_center": "Main - _TC1",
+				"warehouse": "Stores - _TC1",
+			}
+		)
 		target_doc.submit()
 
 		self.assertEqual(target_doc.company, "_Test Company 1")
@@ -1902,57 +2365,66 @@
 		old_negative_stock = frappe.db.get_single_value("Stock Settings", "allow_negative_stock")
 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
 
-		old_perpetual_inventory = erpnext.is_perpetual_inventory_enabled('_Test Company 1')
-		frappe.local.enable_perpetual_inventory['_Test Company 1'] = 1
+		old_perpetual_inventory = erpnext.is_perpetual_inventory_enabled("_Test Company 1")
+		frappe.local.enable_perpetual_inventory["_Test Company 1"] = 1
 
-		frappe.db.set_value("Company", '_Test Company 1', "stock_received_but_not_billed", "Stock Received But Not Billed - _TC1")
-		frappe.db.set_value("Company", '_Test Company 1', "expenses_included_in_valuation", "Expenses Included In Valuation - _TC1")
-
+		frappe.db.set_value(
+			"Company",
+			"_Test Company 1",
+			"stock_received_but_not_billed",
+			"Stock Received But Not Billed - _TC1",
+		)
+		frappe.db.set_value(
+			"Company",
+			"_Test Company 1",
+			"expenses_included_in_valuation",
+			"Expenses Included In Valuation - _TC1",
+		)
 
 		if not frappe.db.exists("Customer", "_Test Internal Customer"):
-			customer = frappe.get_doc({
-				"customer_group": "_Test Customer Group",
-				"customer_name": "_Test Internal Customer",
-				"customer_type": "Individual",
-				"doctype": "Customer",
-				"territory": "_Test Territory",
-				"is_internal_customer": 1,
-				"represents_company": "_Test Company 1"
-			})
+			customer = frappe.get_doc(
+				{
+					"customer_group": "_Test Customer Group",
+					"customer_name": "_Test Internal Customer",
+					"customer_type": "Individual",
+					"doctype": "Customer",
+					"territory": "_Test Territory",
+					"is_internal_customer": 1,
+					"represents_company": "_Test Company 1",
+				}
+			)
 
-			customer.append("companies", {
-				"company": "Wind Power LLC"
-			})
+			customer.append("companies", {"company": "Wind Power LLC"})
 
 			customer.insert()
 
 		if not frappe.db.exists("Supplier", "_Test Internal Supplier"):
-			supplier = frappe.get_doc({
-				"supplier_group": "_Test Supplier Group",
-				"supplier_name": "_Test Internal Supplier",
-				"doctype": "Supplier",
-				"is_internal_supplier": 1,
-				"represents_company": "Wind Power LLC"
-			})
+			supplier = frappe.get_doc(
+				{
+					"supplier_group": "_Test Supplier Group",
+					"supplier_name": "_Test Internal Supplier",
+					"doctype": "Supplier",
+					"is_internal_supplier": 1,
+					"represents_company": "Wind Power LLC",
+				}
+			)
 
-			supplier.append("companies", {
-				"company": "_Test Company 1"
-			})
+			supplier.append("companies", {"company": "_Test Company 1"})
 
 			supplier.insert()
 
 		# begin test
 		si = create_sales_invoice(
-			company = "Wind Power LLC",
-			customer = "_Test Internal Customer",
-			debit_to = "Debtors - WP",
-			warehouse = "Stores - WP",
-			income_account = "Sales - WP",
-			expense_account = "Cost of Goods Sold - WP",
-			cost_center = "Main - WP",
-			currency = "USD",
-			update_stock = 1,
-			do_not_save = 1
+			company="Wind Power LLC",
+			customer="_Test Internal Customer",
+			debit_to="Debtors - WP",
+			warehouse="Stores - WP",
+			income_account="Sales - WP",
+			expense_account="Cost of Goods Sold - WP",
+			cost_center="Main - WP",
+			currency="USD",
+			update_stock=1,
+			do_not_save=1,
 		)
 		si.selling_price_list = "_Test Price List Rest of the World"
 		si.submit()
@@ -1973,19 +2445,19 @@
 		target_doc.save()
 
 		# after warehouse is set, linked account or default inventory account is set
-		self.assertEqual(target_doc.items[0].expense_account, 'Stock In Hand - _TC1')
+		self.assertEqual(target_doc.items[0].expense_account, "Stock In Hand - _TC1")
 
 		# tear down
-		frappe.local.enable_perpetual_inventory['_Test Company 1'] = old_perpetual_inventory
+		frappe.local.enable_perpetual_inventory["_Test Company 1"] = old_perpetual_inventory
 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", old_negative_stock)
 
 	def test_sle_for_target_warehouse(self):
 		se = make_stock_entry(
 			item_code="138-CMS Shoe",
 			target="Finished Goods - _TC",
-			company = "_Test Company",
+			company="_Test Company",
 			qty=1,
-			basic_rate=500
+			basic_rate=500,
 		)
 
 		si = frappe.copy_doc(test_records[0])
@@ -1997,8 +2469,9 @@
 		si.insert()
 		si.submit()
 
-		sles = frappe.get_all("Stock Ledger Entry", filters={"voucher_no": si.name},
-			fields=["name", "actual_qty"])
+		sles = frappe.get_all(
+			"Stock Ledger Entry", filters={"voucher_no": si.name}, fields=["name", "actual_qty"]
+		)
 
 		# check if both SLEs are created
 		self.assertEqual(len(sles), 2)
@@ -2012,82 +2485,92 @@
 		## Create internal transfer account
 		from erpnext.selling.doctype.customer.test_customer import create_internal_customer
 
-		account = create_account(account_name="Unrealized Profit",
-			parent_account="Current Liabilities - TCP1", company="_Test Company with perpetual inventory")
+		account = create_account(
+			account_name="Unrealized Profit",
+			parent_account="Current Liabilities - TCP1",
+			company="_Test Company with perpetual inventory",
+		)
 
-		frappe.db.set_value('Company', '_Test Company with perpetual inventory',
-			'unrealized_profit_loss_account', account)
+		frappe.db.set_value(
+			"Company", "_Test Company with perpetual inventory", "unrealized_profit_loss_account", account
+		)
 
-		customer = create_internal_customer("_Test Internal Customer 2", "_Test Company with perpetual inventory",
-			"_Test Company with perpetual inventory")
+		customer = create_internal_customer(
+			"_Test Internal Customer 2",
+			"_Test Company with perpetual inventory",
+			"_Test Company with perpetual inventory",
+		)
 
-		create_internal_supplier("_Test Internal Supplier 2", "_Test Company with perpetual inventory",
-			"_Test Company with perpetual inventory")
+		create_internal_supplier(
+			"_Test Internal Supplier 2",
+			"_Test Company with perpetual inventory",
+			"_Test Company with perpetual inventory",
+		)
 
 		si = create_sales_invoice(
-			company = "_Test Company with perpetual inventory",
-			customer = customer,
-			debit_to = "Debtors - TCP1",
-			warehouse = "Stores - TCP1",
-			income_account = "Sales - TCP1",
-			expense_account = "Cost of Goods Sold - TCP1",
-			cost_center = "Main - TCP1",
-			currency = "INR",
-			do_not_save = 1
+			company="_Test Company with perpetual inventory",
+			customer=customer,
+			debit_to="Debtors - TCP1",
+			warehouse="Stores - TCP1",
+			income_account="Sales - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			cost_center="Main - TCP1",
+			currency="INR",
+			do_not_save=1,
 		)
 
 		si.selling_price_list = "_Test Price List Rest of the World"
 		si.update_stock = 1
-		si.items[0].target_warehouse = 'Work In Progress - TCP1'
+		si.items[0].target_warehouse = "Work In Progress - TCP1"
 
 		# Add stock to stores for succesful stock transfer
 		make_stock_entry(
-			target="Stores - TCP1",
-			company = "_Test Company with perpetual inventory",
-			qty=1,
-			basic_rate=100
+			target="Stores - TCP1", company="_Test Company with perpetual inventory", qty=1, basic_rate=100
 		)
 
 		add_taxes(si)
 		si.save()
 
 		rate = 0.0
-		for d in si.get('items'):
-			rate = get_incoming_rate({
-				"item_code": d.item_code,
-				"warehouse": d.warehouse,
-				"posting_date": si.posting_date,
-				"posting_time": si.posting_time,
-				"qty": -1 * flt(d.get('stock_qty')),
-				"serial_no": d.serial_no,
-				"company": si.company,
-				"voucher_type": 'Sales Invoice',
-				"voucher_no": si.name,
-				"allow_zero_valuation": d.get("allow_zero_valuation")
-			}, raise_error_if_no_rate=False)
+		for d in si.get("items"):
+			rate = get_incoming_rate(
+				{
+					"item_code": d.item_code,
+					"warehouse": d.warehouse,
+					"posting_date": si.posting_date,
+					"posting_time": si.posting_time,
+					"qty": -1 * flt(d.get("stock_qty")),
+					"serial_no": d.serial_no,
+					"company": si.company,
+					"voucher_type": "Sales Invoice",
+					"voucher_no": si.name,
+					"allow_zero_valuation": d.get("allow_zero_valuation"),
+				},
+				raise_error_if_no_rate=False,
+			)
 
 			rate = flt(rate, 2)
 
 		si.submit()
 
 		target_doc = make_inter_company_transaction("Sales Invoice", si.name)
-		target_doc.company = '_Test Company with perpetual inventory'
-		target_doc.items[0].warehouse = 'Finished Goods - TCP1'
+		target_doc.company = "_Test Company with perpetual inventory"
+		target_doc.items[0].warehouse = "Finished Goods - TCP1"
 		add_taxes(target_doc)
 		target_doc.save()
 		target_doc.submit()
 
-		tax_amount = flt(rate * (12/100), 2)
+		tax_amount = flt(rate * (12 / 100), 2)
 		si_gl_entries = [
 			["_Test Account Excise Duty - TCP1", 0.0, tax_amount, nowdate()],
-			["Unrealized Profit - TCP1", tax_amount, 0.0, nowdate()]
+			["Unrealized Profit - TCP1", tax_amount, 0.0, nowdate()],
 		]
 
 		check_gl_entries(self, si.name, si_gl_entries, add_days(nowdate(), -1))
 
 		pi_gl_entries = [
-			["_Test Account Excise Duty - TCP1", tax_amount , 0.0, nowdate()],
-			["Unrealized Profit - TCP1", 0.0, tax_amount, nowdate()]
+			["_Test Account Excise Duty - TCP1", tax_amount, 0.0, nowdate()],
+			["Unrealized Profit - TCP1", 0.0, tax_amount, nowdate()],
 		]
 
 		# Sale and Purchase both should be at valuation rate
@@ -2099,98 +2582,110 @@
 	def test_item_tax_net_range(self):
 		item = create_item("T Shirt")
 
-		item.set('taxes', [])
-		item.append("taxes", {
-			"item_tax_template": "_Test Account Excise Duty @ 10 - _TC",
-			"minimum_net_rate": 0,
-			"maximum_net_rate": 500
-		})
+		item.set("taxes", [])
+		item.append(
+			"taxes",
+			{
+				"item_tax_template": "_Test Account Excise Duty @ 10 - _TC",
+				"minimum_net_rate": 0,
+				"maximum_net_rate": 500,
+			},
+		)
 
-		item.append("taxes", {
-			"item_tax_template": "_Test Account Excise Duty @ 12 - _TC",
-			"minimum_net_rate": 501,
-			"maximum_net_rate": 1000
-		})
+		item.append(
+			"taxes",
+			{
+				"item_tax_template": "_Test Account Excise Duty @ 12 - _TC",
+				"minimum_net_rate": 501,
+				"maximum_net_rate": 1000,
+			},
+		)
 
 		item.save()
 
-		sales_invoice = create_sales_invoice(item = "T Shirt", rate=700, do_not_submit=True)
-		self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 12 - _TC")
-
-		# Apply discount
-		sales_invoice.apply_discount_on = 'Net Total'
-		sales_invoice.discount_amount = 300
-		sales_invoice.save()
-		self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC")
-
-	def test_sales_invoice_with_discount_accounting_enabled(self):
-		from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import (
-			enable_discount_accounting,
+		sales_invoice = create_sales_invoice(item="T Shirt", rate=700, do_not_submit=True)
+		self.assertEqual(
+			sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 12 - _TC"
 		)
 
-		enable_discount_accounting()
+		# Apply discount
+		sales_invoice.apply_discount_on = "Net Total"
+		sales_invoice.discount_amount = 300
+		sales_invoice.save()
+		self.assertEqual(
+			sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC"
+		)
 
-		discount_account = create_account(account_name="Discount Account",
-			parent_account="Indirect Expenses - _TC", company="_Test Company")
+	@change_settings("Selling Settings", {"enable_discount_accounting": 1})
+	def test_sales_invoice_with_discount_accounting_enabled(self):
+
+		discount_account = create_account(
+			account_name="Discount Account",
+			parent_account="Indirect Expenses - _TC",
+			company="_Test Company",
+		)
 		si = create_sales_invoice(discount_account=discount_account, discount_percentage=10, rate=90)
 
 		expected_gle = [
 			["Debtors - _TC", 90.0, 0.0, nowdate()],
 			["Discount Account - _TC", 10.0, 0.0, nowdate()],
-			["Sales - _TC", 0.0, 100.0, nowdate()]
+			["Sales - _TC", 0.0, 100.0, nowdate()],
 		]
 
 		check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1))
-		enable_discount_accounting(enable=0)
 
+	@change_settings("Selling Settings", {"enable_discount_accounting": 1})
 	def test_additional_discount_for_sales_invoice_with_discount_accounting_enabled(self):
-		from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import (
-			enable_discount_accounting,
+
+		additional_discount_account = create_account(
+			account_name="Discount Account",
+			parent_account="Indirect Expenses - _TC",
+			company="_Test Company",
 		)
 
-		enable_discount_accounting()
-		additional_discount_account = create_account(account_name="Discount Account",
-			parent_account="Indirect Expenses - _TC", company="_Test Company")
-
-		si = create_sales_invoice(parent_cost_center='Main - _TC', do_not_save=1)
+		si = create_sales_invoice(parent_cost_center="Main - _TC", do_not_save=1)
 		si.apply_discount_on = "Grand Total"
 		si.additional_discount_account = additional_discount_account
 		si.additional_discount_percentage = 20
-		si.append("taxes", {
-			"charge_type": "On Net Total",
-			"account_head": "_Test Account VAT - _TC",
-			"cost_center": "Main - _TC",
-			"description": "Test",
-			"rate": 10
-		})
+		si.append(
+			"taxes",
+			{
+				"charge_type": "On Net Total",
+				"account_head": "_Test Account VAT - _TC",
+				"cost_center": "Main - _TC",
+				"description": "Test",
+				"rate": 10,
+			},
+		)
 		si.submit()
 
 		expected_gle = [
 			["_Test Account VAT - _TC", 0.0, 10.0, nowdate()],
 			["Debtors - _TC", 88, 0.0, nowdate()],
 			["Discount Account - _TC", 22.0, 0.0, nowdate()],
-			["Sales - _TC", 0.0, 100.0, nowdate()]
+			["Sales - _TC", 0.0, 100.0, nowdate()],
 		]
 
 		check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1))
-		enable_discount_accounting(enable=0)
 
 	def test_asset_depreciation_on_sale_with_pro_rata(self):
 		"""
-			Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on its date of sale.
+		Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on its date of sale.
 		"""
 
 		create_asset_data()
 		asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, submit=1)
 		post_depreciation_entries(getdate("2021-09-30"))
 
-		create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000, posting_date=getdate("2021-09-30"))
+		create_sales_invoice(
+			item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000, posting_date=getdate("2021-09-30")
+		)
 		asset.load_from_db()
 
 		expected_values = [
 			["2020-06-30", 1366.12, 1366.12],
 			["2021-06-30", 20000.0, 21366.12],
-			["2021-09-30", 5041.1, 26407.22]
+			["2021-09-30", 5041.1, 26407.22],
 		]
 
 		for i, schedule in enumerate(asset.schedules):
@@ -2201,23 +2696,28 @@
 
 	def test_asset_depreciation_on_sale_without_pro_rata(self):
 		"""
-			Tests if an Asset set to depreciate yearly on Dec 31, that gets sold on Dec 31 after two years, created an additional depreciation entry on its date of sale.
+		Tests if an Asset set to depreciate yearly on Dec 31, that gets sold on Dec 31 after two years, created an additional depreciation entry on its date of sale.
 		"""
 
 		create_asset_data()
-		asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1,
-			available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3,
-			expected_value_after_useful_life=10000, depreciation_start_date=getdate("2020-12-31"), submit=1)
+		asset = create_asset(
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			available_for_use_date=getdate("2019-12-31"),
+			total_number_of_depreciations=3,
+			expected_value_after_useful_life=10000,
+			depreciation_start_date=getdate("2020-12-31"),
+			submit=1,
+		)
 
 		post_depreciation_entries(getdate("2021-09-30"))
 
-		create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000, posting_date=getdate("2021-12-31"))
+		create_sales_invoice(
+			item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000, posting_date=getdate("2021-12-31")
+		)
 		asset.load_from_db()
 
-		expected_values = [
-			["2020-12-31", 30000, 30000],
-			["2021-12-31", 30000, 60000]
-		]
+		expected_values = [["2020-12-31", 30000, 30000], ["2021-12-31", 30000, 60000]]
 
 		for i, schedule in enumerate(asset.schedules):
 			self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date)
@@ -2232,7 +2732,9 @@
 		asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, submit=1)
 		post_depreciation_entries(getdate("2021-09-30"))
 
-		si = create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000, posting_date=getdate("2021-09-30"))
+		si = create_sales_invoice(
+			item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000, posting_date=getdate("2021-09-30")
+		)
 		return_si = make_return_doc("Sales Invoice", si.name)
 		return_si.submit()
 		asset.load_from_db()
@@ -2242,8 +2744,8 @@
 			["2021-06-30", 20000.0, 21366.12, True],
 			["2022-06-30", 20000.0, 41366.12, False],
 			["2023-06-30", 20000.0, 61366.12, False],
-			["2024-06-30",  20000.0, 81366.12,  False],
-			["2025-06-06",  18633.88,  100000.0, False]
+			["2024-06-30", 20000.0, 81366.12, False],
+			["2025-06-06", 18633.88, 100000.0, False],
 		]
 
 		for i, schedule in enumerate(asset.schedules):
@@ -2268,30 +2770,34 @@
 		party_link = create_party_link("Supplier", supplier, customer)
 
 		# enable common party accounting
-		frappe.db.set_value('Accounts Settings', None, 'enable_common_party_accounting', 1)
+		frappe.db.set_value("Accounts Settings", None, "enable_common_party_accounting", 1)
 
 		# create a sales invoice
 		si = create_sales_invoice(customer=customer, parent_cost_center="_Test Cost Center - _TC")
 
 		# check outstanding of sales invoice
 		si.reload()
-		self.assertEqual(si.status, 'Paid')
+		self.assertEqual(si.status, "Paid")
 		self.assertEqual(flt(si.outstanding_amount), 0.0)
 
 		# check creation of journal entry
-		jv = frappe.get_all('Journal Entry Account', {
-			'account': si.debit_to,
-			'party_type': 'Customer',
-			'party': si.customer,
-			'reference_type': si.doctype,
-			'reference_name': si.name
-		}, pluck='credit_in_account_currency')
+		jv = frappe.get_all(
+			"Journal Entry Account",
+			{
+				"account": si.debit_to,
+				"party_type": "Customer",
+				"party": si.customer,
+				"reference_type": si.doctype,
+				"reference_name": si.name,
+			},
+			pluck="credit_in_account_currency",
+		)
 
 		self.assertTrue(jv)
 		self.assertEqual(jv[0], si.grand_total)
 
 		party_link.delete()
-		frappe.db.set_value('Accounts Settings', None, 'enable_common_party_accounting', 0)
+		frappe.db.set_value("Accounts Settings", None, "enable_common_party_accounting", 0)
 
 	def test_payment_statuses(self):
 		from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
@@ -2301,16 +2807,14 @@
 		# Test Overdue
 		si = create_sales_invoice(do_not_submit=True)
 		si.payment_schedule = []
-		si.append("payment_schedule", {
-			"due_date": add_days(today, -5),
-			"invoice_portion": 50,
-			"payment_amount": si.grand_total / 2
-		})
-		si.append("payment_schedule", {
-			"due_date": add_days(today, 5),
-			"invoice_portion": 50,
-			"payment_amount": si.grand_total / 2
-		})
+		si.append(
+			"payment_schedule",
+			{"due_date": add_days(today, -5), "invoice_portion": 50, "payment_amount": si.grand_total / 2},
+		)
+		si.append(
+			"payment_schedule",
+			{"due_date": add_days(today, 5), "invoice_portion": 50, "payment_amount": si.grand_total / 2},
+		)
 		si.submit()
 		self.assertEqual(si.status, "Overdue")
 
@@ -2349,21 +2853,23 @@
 
 		# Sales Invoice with Payment Schedule
 		si_with_payment_schedule = create_sales_invoice(do_not_submit=True)
-		si_with_payment_schedule.extend("payment_schedule", [
-			{
-				"due_date": add_days(today, -5),
-				"invoice_portion": 50,
-				"payment_amount": si_with_payment_schedule.grand_total / 2
-			},
-			{
-				"due_date": add_days(today, 5),
-				"invoice_portion": 50,
-				"payment_amount": si_with_payment_schedule.grand_total / 2
-			}
-		])
+		si_with_payment_schedule.extend(
+			"payment_schedule",
+			[
+				{
+					"due_date": add_days(today, -5),
+					"invoice_portion": 50,
+					"payment_amount": si_with_payment_schedule.grand_total / 2,
+				},
+				{
+					"due_date": add_days(today, 5),
+					"invoice_portion": 50,
+					"payment_amount": si_with_payment_schedule.grand_total / 2,
+				},
+			],
+		)
 		si_with_payment_schedule.submit()
 
-
 		for invoice in (si, si_with_payment_schedule):
 			invoice.db_set("status", "Unpaid")
 			update_invoice_status()
@@ -2375,24 +2881,27 @@
 			invoice.reload()
 			self.assertEqual(invoice.status, "Overdue and Discounted")
 
-
 	def test_sales_commission(self):
 		si = frappe.copy_doc(test_records[2])
 
-		frappe.db.set_value('Item', si.get('items')[0].item_code, 'grant_commission', 1)
-		frappe.db.set_value('Item', si.get('items')[1].item_code, 'grant_commission', 0)
+		frappe.db.set_value("Item", si.get("items")[0].item_code, "grant_commission", 1)
+		frappe.db.set_value("Item", si.get("items")[1].item_code, "grant_commission", 0)
 
-		item = copy.deepcopy(si.get('items')[0])
-		item.update({
-			"qty": 1,
-			"rate": 500,
-		})
+		item = copy.deepcopy(si.get("items")[0])
+		item.update(
+			{
+				"qty": 1,
+				"rate": 500,
+			}
+		)
 
-		item = copy.deepcopy(si.get('items')[1])
-		item.update({
-			"qty": 1,
-			"rate": 500,
-		})
+		item = copy.deepcopy(si.get("items")[1])
+		item.update(
+			{
+				"qty": 1,
+				"rate": 500,
+			}
+		)
 
 		# Test valid values
 		for commission_rate, total_commission in ((0, 0), (10, 50), (100, 500)):
@@ -2408,7 +2917,7 @@
 			self.assertRaises(frappe.ValidationError, si.save)
 
 	def test_sales_invoice_submission_post_account_freezing_date(self):
-		frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', add_days(getdate(), 1))
+		frappe.db.set_value("Accounts Settings", None, "acc_frozen_upto", add_days(getdate(), 1))
 		si = create_sales_invoice(do_not_save=True)
 		si.posting_date = add_days(getdate(), 1)
 		si.save()
@@ -2417,17 +2926,19 @@
 		si.posting_date = getdate()
 		si.submit()
 
-		frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
+		frappe.db.set_value("Accounts Settings", None, "acc_frozen_upto", None)
 
 	def test_over_billing_case_against_delivery_note(self):
-		'''
-			Test a case where duplicating the item with qty = 1 in the invoice
-			allows overbilling even if it is disabled
-		'''
+		"""
+		Test a case where duplicating the item with qty = 1 in the invoice
+		allows overbilling even if it is disabled
+		"""
 		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
 
-		over_billing_allowance = frappe.db.get_single_value('Accounts Settings', 'over_billing_allowance')
-		frappe.db.set_value('Accounts Settings', None, 'over_billing_allowance', 0)
+		over_billing_allowance = frappe.db.get_single_value(
+			"Accounts Settings", "over_billing_allowance"
+		)
+		frappe.db.set_value("Accounts Settings", None, "over_billing_allowance", 0)
 
 		dn = create_delivery_note()
 		dn.submit()
@@ -2435,7 +2946,7 @@
 		si = make_sales_invoice(dn.name)
 		# make a copy of first item and add it to invoice
 		item_copy = frappe.copy_doc(si.items[0])
-		si.append('items', item_copy)
+		si.append("items", item_copy)
 		si.save()
 
 		with self.assertRaises(frappe.ValidationError) as err:
@@ -2443,13 +2954,16 @@
 
 		self.assertTrue("cannot overbill" in str(err.exception).lower())
 
-		frappe.db.set_value('Accounts Settings', None, 'over_billing_allowance', over_billing_allowance)
+		frappe.db.set_value("Accounts Settings", None, "over_billing_allowance", over_billing_allowance)
 
 	def test_multi_currency_deferred_revenue_via_journal_entry(self):
-		deferred_account = create_account(account_name="Deferred Revenue",
-			parent_account="Current Liabilities - _TC", company="_Test Company")
+		deferred_account = create_account(
+			account_name="Deferred Revenue",
+			parent_account="Current Liabilities - _TC",
+			company="_Test Company",
+		)
 
-		acc_settings = frappe.get_single('Accounts Settings')
+		acc_settings = frappe.get_single("Accounts Settings")
 		acc_settings.book_deferred_entries_via_journal_entry = 1
 		acc_settings.submit_journal_entries = 1
 		acc_settings.save()
@@ -2459,12 +2973,19 @@
 		item.deferred_revenue_account = deferred_account
 		item.save()
 
-		si = create_sales_invoice(customer='_Test Customer USD', currency='USD',
-			item=item.name, qty=1, rate=100, conversion_rate=60, do_not_save=True)
+		si = create_sales_invoice(
+			customer="_Test Customer USD",
+			currency="USD",
+			item=item.name,
+			qty=1,
+			rate=100,
+			conversion_rate=60,
+			do_not_save=True,
+		)
 
 		si.set_posting_time = 1
-		si.posting_date = '2019-01-01'
-		si.debit_to = '_Test Receivable USD - _TC'
+		si.posting_date = "2019-01-01"
+		si.debit_to = "_Test Receivable USD - _TC"
 		si.items[0].enable_deferred_revenue = 1
 		si.items[0].service_start_date = "2019-01-01"
 		si.items[0].service_end_date = "2019-03-30"
@@ -2472,16 +2993,18 @@
 		si.save()
 		si.submit()
 
-		frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', getdate('2019-01-31'))
+		frappe.db.set_value("Accounts Settings", None, "acc_frozen_upto", getdate("2019-01-31"))
 
-		pda1 = frappe.get_doc(dict(
-			doctype='Process Deferred Accounting',
-			posting_date=nowdate(),
-			start_date="2019-01-01",
-			end_date="2019-03-31",
-			type="Income",
-			company="_Test Company"
-		))
+		pda1 = frappe.get_doc(
+			dict(
+				doctype="Process Deferred Accounting",
+				posting_date=nowdate(),
+				start_date="2019-01-01",
+				end_date="2019-03-31",
+				type="Income",
+				company="_Test Company",
+			)
+		)
 
 		pda1.insert()
 		pda1.submit()
@@ -2492,13 +3015,17 @@
 			["Sales - _TC", 0.0, 1887.64, "2019-02-28"],
 			[deferred_account, 1887.64, 0.0, "2019-02-28"],
 			["Sales - _TC", 0.0, 2022.47, "2019-03-15"],
-			[deferred_account, 2022.47, 0.0, "2019-03-15"]
+			[deferred_account, 2022.47, 0.0, "2019-03-15"],
 		]
 
-		gl_entries = gl_entries = frappe.db.sql("""select account, debit, credit, posting_date
+		gl_entries = gl_entries = frappe.db.sql(
+			"""select account, debit, credit, posting_date
 			from `tabGL Entry`
 			where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s
-			order by posting_date asc, account asc""", (si.items[0].name, si.posting_date), as_dict=1)
+			order by posting_date asc, account asc""",
+			(si.items[0].name, si.posting_date),
+			as_dict=1,
+		)
 
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_gle[i][0], gle.account)
@@ -2506,46 +3033,154 @@
 			self.assertEqual(expected_gle[i][2], gle.debit)
 			self.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
 
-		acc_settings = frappe.get_single('Accounts Settings')
+		acc_settings = frappe.get_single("Accounts Settings")
 		acc_settings.book_deferred_entries_via_journal_entry = 0
-		acc_settings.submit_journal_entriessubmit_journal_entries = 0
+		acc_settings.submit_journal_entries = 0
 		acc_settings.save()
 
-		frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
+		frappe.db.set_value("Accounts Settings", None, "acc_frozen_upto", None)
+
+	def test_standalone_serial_no_return(self):
+		si = create_sales_invoice(
+			item_code="_Test Serialized Item With Series", update_stock=True, is_return=True, qty=-1
+		)
+		si.reload()
+		self.assertTrue(si.items[0].serial_no)
+
+	def test_sales_invoice_with_disabled_account(self):
+		try:
+			account = frappe.get_doc("Account", "VAT 5% - _TC")
+			account.disabled = 1
+			account.save()
+
+			si = create_sales_invoice(do_not_save=True)
+			si.posting_date = add_days(getdate(), 1)
+			si.taxes = []
+
+			si.append(
+				"taxes",
+				{
+					"charge_type": "On Net Total",
+					"account_head": "VAT 5% - _TC",
+					"cost_center": "Main - _TC",
+					"description": "VAT @ 5.0",
+					"rate": 9,
+				},
+			)
+			si.save()
+
+			with self.assertRaises(frappe.ValidationError) as err:
+				si.submit()
+
+			self.assertTrue(
+				"Cannot create accounting entries against disabled accounts" in str(err.exception)
+			)
+
+		finally:
+			account.disabled = 0
+			account.save()
+
+	def test_gain_loss_with_advance_entry(self):
+		from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
+
+		unlink_enabled = frappe.db.get_value(
+			"Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice"
+		)
+
+		frappe.db.set_value(
+			"Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice", 1
+		)
+
+		jv = make_journal_entry("_Test Receivable USD - _TC", "_Test Bank - _TC", -7000, save=False)
+
+		jv.accounts[0].exchange_rate = 70
+		jv.accounts[0].credit_in_account_currency = 100
+		jv.accounts[0].party_type = "Customer"
+		jv.accounts[0].party = "_Test Customer USD"
+
+		jv.save()
+		jv.submit()
+
+		si = create_sales_invoice(
+			customer="_Test Customer USD",
+			debit_to="_Test Receivable USD - _TC",
+			currency="USD",
+			conversion_rate=75,
+			do_not_save=1,
+			rate=100,
+		)
+
+		si.append(
+			"advances",
+			{
+				"reference_type": "Journal Entry",
+				"reference_name": jv.name,
+				"reference_row": jv.accounts[0].name,
+				"advance_amount": 100,
+				"allocated_amount": 100,
+				"ref_exchange_rate": 70,
+			},
+		)
+		si.save()
+		si.submit()
+
+		expected_gle = [
+			["_Test Receivable USD - _TC", 7500.0, 500],
+			["Exchange Gain/Loss - _TC", 500.0, 0.0],
+			["Sales - _TC", 0.0, 7500.0],
+		]
+
+		check_gl_entries(self, si.name, expected_gle, nowdate())
+
+		frappe.db.set_value(
+			"Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice", unlink_enabled
+		)
+
 
 def get_sales_invoice_for_e_invoice():
 	si = make_sales_invoice_for_ewaybill()
-	si.naming_series = 'INV-2020-.#####'
+	si.naming_series = "INV-2020-.#####"
 	si.items = []
-	si.append("items", {
-		"item_code": "_Test Item",
-		"uom": "Nos",
-		"warehouse": "_Test Warehouse - _TC",
-		"qty": 2000,
-		"rate": 12,
-		"income_account": "Sales - _TC",
-		"expense_account": "Cost of Goods Sold - _TC",
-		"cost_center": "_Test Cost Center - _TC",
-	})
+	si.append(
+		"items",
+		{
+			"item_code": "_Test Item",
+			"uom": "Nos",
+			"warehouse": "_Test Warehouse - _TC",
+			"qty": 2000,
+			"rate": 12,
+			"income_account": "Sales - _TC",
+			"expense_account": "Cost of Goods Sold - _TC",
+			"cost_center": "_Test Cost Center - _TC",
+		},
+	)
 
-	si.append("items", {
-		"item_code": "_Test Item 2",
-		"uom": "Nos",
-		"warehouse": "_Test Warehouse - _TC",
-		"qty": 420,
-		"rate": 15,
-		"income_account": "Sales - _TC",
-		"expense_account": "Cost of Goods Sold - _TC",
-		"cost_center": "_Test Cost Center - _TC",
-	})
+	si.append(
+		"items",
+		{
+			"item_code": "_Test Item 2",
+			"uom": "Nos",
+			"warehouse": "_Test Warehouse - _TC",
+			"qty": 420,
+			"rate": 15,
+			"income_account": "Sales - _TC",
+			"expense_account": "Cost of Goods Sold - _TC",
+			"cost_center": "_Test Cost Center - _TC",
+		},
+	)
 
 	return si
 
+
 def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
-	gl_entries = frappe.db.sql("""select account, debit, credit, posting_date
+	gl_entries = frappe.db.sql(
+		"""select account, debit, credit, posting_date
 		from `tabGL Entry`
 		where voucher_type='Sales Invoice' and voucher_no=%s and posting_date > %s
-		order by posting_date asc, account asc""", (voucher_no, posting_date), as_dict=1)
+		order by posting_date asc, account asc""",
+		(voucher_no, posting_date),
+		as_dict=1,
+	)
 
 	for i, gle in enumerate(gl_entries):
 		doc.assertEqual(expected_gle[i][0], gle.account)
@@ -2553,6 +3188,7 @@
 		doc.assertEqual(expected_gle[i][2], gle.credit)
 		doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
 
+
 def create_sales_invoice(**args):
 	si = frappe.new_doc("Sales Invoice")
 	args = frappe._dict(args)
@@ -2567,31 +3203,34 @@
 	si.is_pos = args.is_pos
 	si.is_return = args.is_return
 	si.return_against = args.return_against
-	si.currency=args.currency or "INR"
+	si.currency = args.currency or "INR"
 	si.conversion_rate = args.conversion_rate or 1
 	si.naming_series = args.naming_series or "T-SINV-"
 	si.cost_center = args.parent_cost_center
 
-	si.append("items", {
-		"item_code": args.item or args.item_code or "_Test Item",
-		"item_name": args.item_name or "_Test Item",
-		"description": args.description or "_Test Item",
-		"warehouse": args.warehouse or "_Test Warehouse - _TC",
-		"qty": args.qty or 1,
-		"uom": args.uom or "Nos",
-		"stock_uom": args.uom or "Nos",
-		"rate": args.rate if args.get("rate") is not None else 100,
-		"price_list_rate": args.price_list_rate if args.get("price_list_rate") is not None else 100,
-		"income_account": args.income_account or "Sales - _TC",
-		"expense_account": args.expense_account or "Cost of Goods Sold - _TC",
-		"discount_account": args.discount_account or None,
-		"discount_amount": args.discount_amount or 0,
-		"asset": args.asset or None,
-		"cost_center": args.cost_center or "_Test Cost Center - _TC",
-		"serial_no": args.serial_no,
-		"conversion_factor": 1,
-		"incoming_rate": args.incoming_rate or 0
-	})
+	si.append(
+		"items",
+		{
+			"item_code": args.item or args.item_code or "_Test Item",
+			"item_name": args.item_name or "_Test Item",
+			"description": args.description or "_Test Item",
+			"warehouse": args.warehouse or "_Test Warehouse - _TC",
+			"qty": args.qty or 1,
+			"uom": args.uom or "Nos",
+			"stock_uom": args.uom or "Nos",
+			"rate": args.rate if args.get("rate") is not None else 100,
+			"price_list_rate": args.price_list_rate if args.get("price_list_rate") is not None else 100,
+			"income_account": args.income_account or "Sales - _TC",
+			"expense_account": args.expense_account or "Cost of Goods Sold - _TC",
+			"discount_account": args.discount_account or None,
+			"discount_amount": args.discount_amount or 0,
+			"asset": args.asset or None,
+			"cost_center": args.cost_center or "_Test Cost Center - _TC",
+			"serial_no": args.serial_no,
+			"conversion_factor": 1,
+			"incoming_rate": args.incoming_rate or 0,
+		},
+	)
 
 	if not args.do_not_save:
 		si.insert()
@@ -2604,6 +3243,7 @@
 
 	return si
 
+
 def create_sales_invoice_against_cost_center(**args):
 	si = frappe.new_doc("Sales Invoice")
 	args = frappe._dict(args)
@@ -2619,19 +3259,22 @@
 	si.is_pos = args.is_pos
 	si.is_return = args.is_return
 	si.return_against = args.return_against
-	si.currency=args.currency or "INR"
+	si.currency = args.currency or "INR"
 	si.conversion_rate = args.conversion_rate or 1
 
-	si.append("items", {
-		"item_code": args.item or args.item_code or "_Test Item",
-		"warehouse": args.warehouse or "_Test Warehouse - _TC",
-		"qty": args.qty or 1,
-		"rate": args.rate or 100,
-		"income_account": "Sales - _TC",
-		"expense_account": "Cost of Goods Sold - _TC",
-		"cost_center": args.cost_center or "_Test Cost Center - _TC",
-		"serial_no": args.serial_no
-	})
+	si.append(
+		"items",
+		{
+			"item_code": args.item or args.item_code or "_Test Item",
+			"warehouse": args.warehouse or "_Test Warehouse - _TC",
+			"qty": args.qty or 1,
+			"rate": args.rate or 100,
+			"income_account": "Sales - _TC",
+			"expense_account": "Cost of Goods Sold - _TC",
+			"cost_center": args.cost_center or "_Test Cost Center - _TC",
+			"serial_no": args.serial_no,
+		},
+	)
 
 	if not args.do_not_save:
 		si.insert()
@@ -2646,59 +3289,69 @@
 
 
 test_dependencies = ["Journal Entry", "Contact", "Address"]
-test_records = frappe.get_test_records('Sales Invoice')
+test_records = frappe.get_test_records("Sales Invoice")
+
 
 def get_outstanding_amount(against_voucher_type, against_voucher, account, party, party_type):
-	bal = flt(frappe.db.sql("""
+	bal = flt(
+		frappe.db.sql(
+			"""
 		select sum(debit_in_account_currency) - sum(credit_in_account_currency)
 		from `tabGL Entry`
 		where against_voucher_type=%s and against_voucher=%s
 		and account = %s and party = %s and party_type = %s""",
-		(against_voucher_type, against_voucher, account, party, party_type))[0][0] or 0.0)
+			(against_voucher_type, against_voucher, account, party, party_type),
+		)[0][0]
+		or 0.0
+	)
 
-	if against_voucher_type == 'Purchase Invoice':
+	if against_voucher_type == "Purchase Invoice":
 		bal = bal * -1
 
 	return bal
 
+
 def get_taxes_and_charges():
-	return [{
-	"account_head": "_Test Account Excise Duty - TCP1",
-	"charge_type": "On Net Total",
-	"cost_center": "Main - TCP1",
-	"description": "Excise Duty",
-	"doctype": "Sales Taxes and Charges",
-	"idx": 1,
-	"included_in_print_rate": 1,
-	"parentfield": "taxes",
-	"rate": 12
-	},
-	{
-	"account_head": "_Test Account Education Cess - TCP1",
-	"charge_type": "On Previous Row Amount",
-	"cost_center": "Main - TCP1",
-	"description": "Education Cess",
-	"doctype": "Sales Taxes and Charges",
-	"idx": 2,
-	"included_in_print_rate": 1,
-	"parentfield": "taxes",
-	"rate": 2,
-	"row_id": 1
-	}]
+	return [
+		{
+			"account_head": "_Test Account Excise Duty - TCP1",
+			"charge_type": "On Net Total",
+			"cost_center": "Main - TCP1",
+			"description": "Excise Duty",
+			"doctype": "Sales Taxes and Charges",
+			"idx": 1,
+			"included_in_print_rate": 1,
+			"parentfield": "taxes",
+			"rate": 12,
+		},
+		{
+			"account_head": "_Test Account Education Cess - TCP1",
+			"charge_type": "On Previous Row Amount",
+			"cost_center": "Main - TCP1",
+			"description": "Education Cess",
+			"doctype": "Sales Taxes and Charges",
+			"idx": 2,
+			"included_in_print_rate": 1,
+			"parentfield": "taxes",
+			"rate": 2,
+			"row_id": 1,
+		},
+	]
+
 
 def create_internal_supplier(supplier_name, represents_company, allowed_to_interact_with):
 	if not frappe.db.exists("Supplier", supplier_name):
-		supplier = frappe.get_doc({
-			"supplier_group": "_Test Supplier Group",
-			"supplier_name": supplier_name,
-			"doctype": "Supplier",
-			"is_internal_supplier": 1,
-			"represents_company": represents_company
-		})
+		supplier = frappe.get_doc(
+			{
+				"supplier_group": "_Test Supplier Group",
+				"supplier_name": supplier_name,
+				"doctype": "Supplier",
+				"is_internal_supplier": 1,
+				"represents_company": represents_company,
+			}
+		)
 
-		supplier.append("companies", {
-			"company": allowed_to_interact_with
-		})
+		supplier.append("companies", {"company": allowed_to_interact_with})
 
 		supplier.insert()
 		supplier_name = supplier.name
@@ -2707,11 +3360,15 @@
 
 	return supplier_name
 
+
 def add_taxes(doc):
-	doc.append('taxes', {
-		'account_head': '_Test Account Excise Duty - TCP1',
-		"charge_type": "On Net Total",
-		"cost_center": "Main - TCP1",
-		"description": "Excise Duty",
-		"rate": 12
-	})
+	doc.append(
+		"taxes",
+		{
+			"account_head": "_Test Account Excise Duty - TCP1",
+			"charge_type": "On Net Total",
+			"cost_center": "Main - TCP1",
+			"description": "Excise Duty",
+			"rate": 12,
+		},
+	)
diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
index 2901cf0..b417c7d 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -182,6 +182,7 @@
    "oldfieldtype": "Currency"
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "stock_uom",
    "fieldtype": "Link",
    "label": "Stock UOM",
@@ -200,6 +201,7 @@
    "reqd": 1
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "conversion_factor",
    "fieldtype": "Float",
    "label": "UOM Conversion Factor",
@@ -207,6 +209,7 @@
    "reqd": 1
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "stock_qty",
    "fieldtype": "Float",
    "label": "Qty as per Stock UOM",
@@ -813,6 +816,7 @@
    "fieldtype": "Currency",
    "label": "Incoming Rate (Costing)",
    "no_copy": 1,
+   "options": "Company:company:default_currency",
    "print_hide": 1
   },
   {
@@ -842,7 +846,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2022-02-24 14:41:36.392560",
+ "modified": "2022-06-17 05:33:15.335912",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice Item",
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json
index 19781bd..408ecbf 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "allow_import": 1,
  "allow_rename": 1,
  "creation": "2013-01-10 16:34:09",
@@ -77,7 +78,8 @@
  ],
  "icon": "fa fa-money",
  "idx": 1,
- "modified": "2019-11-25 13:06:03.279099",
+ "links": [],
+ "modified": "2022-05-16 16:14:52.061672",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Taxes and Charges Template",
@@ -113,7 +115,10 @@
    "write": 1
   }
  ],
+ "show_title_field_in_link": 1,
  "sort_field": "modified",
  "sort_order": "ASC",
+ "states": [],
+ "title_field": "title",
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py
index 8043a1b..d9009ba 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py
@@ -21,13 +21,14 @@
 
 	def autoname(self):
 		if self.company and self.title:
-			abbr = frappe.get_cached_value('Company',  self.company,  'abbr')
-			self.name = '{0} - {1}'.format(self.title, abbr)
+			abbr = frappe.get_cached_value("Company", self.company, "abbr")
+			self.name = "{0} - {1}".format(self.title, abbr)
 
 	def set_missing_values(self):
 		for data in self.taxes:
-			if data.charge_type == 'On Net Total' and flt(data.rate) == 0.0:
-				data.rate = frappe.db.get_value('Account', data.account_head, 'tax_rate')
+			if data.charge_type == "On Net Total" and flt(data.rate) == 0.0:
+				data.rate = frappe.db.get_value("Account", data.account_head, "tax_rate")
+
 
 def valdiate_taxes_and_charges_template(doc):
 	# default should not be disabled
@@ -35,9 +36,13 @@
 	# 	doc.is_default = 1
 
 	if doc.is_default == 1:
-		frappe.db.sql("""update `tab{0}` set is_default = 0
-			where is_default = 1 and name != %s and company = %s""".format(doc.doctype),
-			(doc.name, doc.company))
+		frappe.db.sql(
+			"""update `tab{0}` set is_default = 0
+			where is_default = 1 and name != %s and company = %s""".format(
+				doc.doctype
+			),
+			(doc.name, doc.company),
+		)
 
 	validate_disabled(doc)
 
@@ -50,13 +55,27 @@
 		validate_cost_center(tax, doc)
 		validate_inclusive_tax(tax, doc)
 
+
 def validate_disabled(doc):
 	if doc.is_default and doc.disabled:
 		frappe.throw(_("Disabled template must not be default template"))
 
+
 def validate_for_tax_category(doc):
 	if not doc.tax_category:
 		return
 
-	if frappe.db.exists(doc.doctype, {"company": doc.company, "tax_category": doc.tax_category, "disabled": 0, "name": ["!=", doc.name]}):
-		frappe.throw(_("A template with tax category {0} already exists. Only one template is allowed with each tax category").format(frappe.bold(doc.tax_category)))
+	if frappe.db.exists(
+		doc.doctype,
+		{
+			"company": doc.company,
+			"tax_category": doc.tax_category,
+			"disabled": 0,
+			"name": ["!=", doc.name],
+		},
+	):
+		frappe.throw(
+			_(
+				"A template with tax category {0} already exists. Only one template is allowed with each tax category"
+			).format(frappe.bold(doc.tax_category))
+		)
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template_dashboard.py b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template_dashboard.py
index bc1fd8e..6432aca 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template_dashboard.py
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template_dashboard.py
@@ -3,20 +3,14 @@
 
 def get_data():
 	return {
-		'fieldname': 'taxes_and_charges',
-		'non_standard_fieldnames': {
-			'Tax Rule': 'sales_tax_template',
-			'Subscription': 'sales_tax_template',
-			'Restaurant': 'default_tax_template'
+		"fieldname": "taxes_and_charges",
+		"non_standard_fieldnames": {
+			"Tax Rule": "sales_tax_template",
+			"Subscription": "sales_tax_template",
+			"Restaurant": "default_tax_template",
 		},
-		'transactions': [
-			{
-				'label': _('Transactions'),
-				'items': ['Sales Invoice', 'Sales Order', 'Delivery Note']
-			},
-			{
-				'label': _('References'),
-				'items': ['POS Profile', 'Subscription', 'Restaurant', 'Tax Rule']
-			}
-		]
+		"transactions": [
+			{"label": _("Transactions"), "items": ["Sales Invoice", "Sales Order", "Delivery Note"]},
+			{"label": _("References"), "items": ["POS Profile", "Subscription", "Restaurant", "Tax Rule"]},
+		],
 	}
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.py b/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.py
index 7b13c6c..972b773 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.py
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.py
@@ -5,7 +5,8 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Sales Taxes and Charges Template')
+test_records = frappe.get_test_records("Sales Taxes and Charges Template")
+
 
 class TestSalesTaxesandChargesTemplate(unittest.TestCase):
 	pass
diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.py b/erpnext/accounts/doctype/share_transfer/share_transfer.py
index b543ad8..4f49843 100644
--- a/erpnext/accounts/doctype/share_transfer/share_transfer.py
+++ b/erpnext/accounts/doctype/share_transfer/share_transfer.py
@@ -10,95 +10,115 @@
 from frappe.utils import nowdate
 
 
-class ShareDontExists(ValidationError): pass
+class ShareDontExists(ValidationError):
+	pass
+
 
 class ShareTransfer(Document):
 	def on_submit(self):
-		if self.transfer_type == 'Issue':
+		if self.transfer_type == "Issue":
 			shareholder = self.get_company_shareholder()
-			shareholder.append('share_balance', {
-				'share_type': self.share_type,
-				'from_no': self.from_no,
-				'to_no': self.to_no,
-				'rate': self.rate,
-				'amount': self.amount,
-				'no_of_shares': self.no_of_shares,
-				'is_company': 1,
-				'current_state': 'Issued'
-			})
+			shareholder.append(
+				"share_balance",
+				{
+					"share_type": self.share_type,
+					"from_no": self.from_no,
+					"to_no": self.to_no,
+					"rate": self.rate,
+					"amount": self.amount,
+					"no_of_shares": self.no_of_shares,
+					"is_company": 1,
+					"current_state": "Issued",
+				},
+			)
 			shareholder.save()
 
 			doc = self.get_shareholder_doc(self.to_shareholder)
-			doc.append('share_balance', {
-				'share_type': self.share_type,
-				'from_no': self.from_no,
-				'to_no': self.to_no,
-				'rate': self.rate,
-				'amount': self.amount,
-				'no_of_shares': self.no_of_shares
-			})
+			doc.append(
+				"share_balance",
+				{
+					"share_type": self.share_type,
+					"from_no": self.from_no,
+					"to_no": self.to_no,
+					"rate": self.rate,
+					"amount": self.amount,
+					"no_of_shares": self.no_of_shares,
+				},
+			)
 			doc.save()
 
-		elif self.transfer_type == 'Purchase':
+		elif self.transfer_type == "Purchase":
 			self.remove_shares(self.from_shareholder)
 			self.remove_shares(self.get_company_shareholder().name)
 
-		elif self.transfer_type == 'Transfer':
+		elif self.transfer_type == "Transfer":
 			self.remove_shares(self.from_shareholder)
 			doc = self.get_shareholder_doc(self.to_shareholder)
-			doc.append('share_balance', {
-				'share_type': self.share_type,
-				'from_no': self.from_no,
-				'to_no': self.to_no,
-				'rate': self.rate,
-				'amount': self.amount,
-				'no_of_shares': self.no_of_shares
-			})
+			doc.append(
+				"share_balance",
+				{
+					"share_type": self.share_type,
+					"from_no": self.from_no,
+					"to_no": self.to_no,
+					"rate": self.rate,
+					"amount": self.amount,
+					"no_of_shares": self.no_of_shares,
+				},
+			)
 			doc.save()
 
 	def on_cancel(self):
-		if self.transfer_type == 'Issue':
+		if self.transfer_type == "Issue":
 			compnay_shareholder = self.get_company_shareholder()
 			self.remove_shares(compnay_shareholder.name)
 			self.remove_shares(self.to_shareholder)
 
-		elif self.transfer_type == 'Purchase':
+		elif self.transfer_type == "Purchase":
 			compnay_shareholder = self.get_company_shareholder()
 			from_shareholder = self.get_shareholder_doc(self.from_shareholder)
 
-			from_shareholder.append('share_balance', {
-				'share_type': self.share_type,
-				'from_no': self.from_no,
-				'to_no': self.to_no,
-				'rate': self.rate,
-				'amount': self.amount,
-				'no_of_shares': self.no_of_shares
-			})
+			from_shareholder.append(
+				"share_balance",
+				{
+					"share_type": self.share_type,
+					"from_no": self.from_no,
+					"to_no": self.to_no,
+					"rate": self.rate,
+					"amount": self.amount,
+					"no_of_shares": self.no_of_shares,
+				},
+			)
 
 			from_shareholder.save()
 
-			compnay_shareholder.append('share_balance', {
-				'share_type': self.share_type,
-				'from_no': self.from_no,
-				'to_no': self.to_no,
-				'rate': self.rate,
-				'amount': self.amount,
-				'no_of_shares': self.no_of_shares
-			})
+			compnay_shareholder.append(
+				"share_balance",
+				{
+					"share_type": self.share_type,
+					"from_no": self.from_no,
+					"to_no": self.to_no,
+					"rate": self.rate,
+					"amount": self.amount,
+					"no_of_shares": self.no_of_shares,
+				},
+			)
 
 			compnay_shareholder.save()
 
-		elif self.transfer_type == 'Transfer':
+		elif self.transfer_type == "Transfer":
 			self.remove_shares(self.to_shareholder)
 			from_shareholder = self.get_shareholder_doc(self.from_shareholder)
-			from_shareholder.append('share_balance', {
-				'share_type': self.share_type,
-				'from_no': self.from_no,
-				'to_no': self.to_no,
-				'rate': self.rate,
-				'amount': self.amount,
-				'no_of_shares': self.no_of_shares
-			})
+			from_shareholder.append(
+				"share_balance",
+				{
+					"share_type": self.share_type,
+					"from_no": self.from_no,
+					"to_no": self.to_no,
+					"rate": self.rate,
+					"amount": self.amount,
+					"no_of_shares": self.no_of_shares,
+				},
+			)
 			from_shareholder.save()
 
 	def validate(self):
@@ -106,90 +126,96 @@
 		self.basic_validations()
 		self.folio_no_validation()
 
-		if self.transfer_type == 'Issue':
+		if self.transfer_type == "Issue":
 			# validate share doesn't exist in company
 			ret_val = self.share_exists(self.get_company_shareholder().name)
-			if ret_val in ('Complete', 'Partial'):
-				frappe.throw(_('The shares already exist'), frappe.DuplicateEntryError)
+			if ret_val in ("Complete", "Partial"):
+				frappe.throw(_("The shares already exist"), frappe.DuplicateEntryError)
 		else:
 			# validate share exists with from_shareholder
 			ret_val = self.share_exists(self.from_shareholder)
-			if ret_val in ('Outside', 'Partial'):
-				frappe.throw(_("The shares don't exist with the {0}")
-					.format(self.from_shareholder), ShareDontExists)
+			if ret_val in ("Outside", "Partial"):
+				frappe.throw(
+					_("The shares don't exist with the {0}").format(self.from_shareholder), ShareDontExists
+				)
 
 	def basic_validations(self):
-		if self.transfer_type == 'Purchase':
-			self.to_shareholder = ''
+		if self.transfer_type == "Purchase":
+			self.to_shareholder = ""
 			if not self.from_shareholder:
-				frappe.throw(_('The field From Shareholder cannot be blank'))
+				frappe.throw(_("The field From Shareholder cannot be blank"))
 			if not self.from_folio_no:
 				self.to_folio_no = self.autoname_folio(self.to_shareholder)
 			if not self.asset_account:
-				frappe.throw(_('The field Asset Account cannot be blank'))
-		elif (self.transfer_type == 'Issue'):
-			self.from_shareholder = ''
+				frappe.throw(_("The field Asset Account cannot be blank"))
+		elif self.transfer_type == "Issue":
+			self.from_shareholder = ""
 			if not self.to_shareholder:
-				frappe.throw(_('The field To Shareholder cannot be blank'))
+				frappe.throw(_("The field To Shareholder cannot be blank"))
 			if not self.to_folio_no:
 				self.to_folio_no = self.autoname_folio(self.to_shareholder)
 			if not self.asset_account:
-				frappe.throw(_('The field Asset Account cannot be blank'))
+				frappe.throw(_("The field Asset Account cannot be blank"))
 		else:
 			if not self.from_shareholder or not self.to_shareholder:
-				frappe.throw(_('The fields From Shareholder and To Shareholder cannot be blank'))
+				frappe.throw(_("The fields From Shareholder and To Shareholder cannot be blank"))
 			if not self.to_folio_no:
 				self.to_folio_no = self.autoname_folio(self.to_shareholder)
 		if not self.equity_or_liability_account:
-				frappe.throw(_('The field Equity/Liability Account cannot be blank'))
+			frappe.throw(_("The field Equity/Liability Account cannot be blank"))
 		if self.from_shareholder == self.to_shareholder:
-			frappe.throw(_('The seller and the buyer cannot be the same'))
+			frappe.throw(_("The seller and the buyer cannot be the same"))
 		if self.no_of_shares != self.to_no - self.from_no + 1:
-			frappe.throw(_('The number of shares and the share numbers are inconsistent'))
+			frappe.throw(_("The number of shares and the share numbers are inconsistent"))
 		if not self.amount:
 			self.amount = self.rate * self.no_of_shares
 		if self.amount != self.rate * self.no_of_shares:
-			frappe.throw(_('There are inconsistencies between the rate, no of shares and the amount calculated'))
+			frappe.throw(
+				_("There are inconsistencies between the rate, no of shares and the amount calculated")
+			)
 
 	def share_exists(self, shareholder):
 		doc = self.get_shareholder_doc(shareholder)
 		for entry in doc.share_balance:
-			if entry.share_type != self.share_type or \
-				entry.from_no > self.to_no or \
-				entry.to_no < self.from_no:
-				continue # since query lies outside bounds
-			elif entry.from_no <= self.from_no and entry.to_no >= self.to_no: #both inside
-				return 'Complete' # absolute truth!
+			if (
+				entry.share_type != self.share_type or entry.from_no > self.to_no or entry.to_no < self.from_no
+			):
+				continue  # since query lies outside bounds
+			elif entry.from_no <= self.from_no and entry.to_no >= self.to_no:  # both inside
+				return "Complete"  # absolute truth!
 			elif entry.from_no <= self.from_no <= self.to_no:
-				return 'Partial'
+				return "Partial"
 			elif entry.from_no <= self.to_no <= entry.to_no:
-				return 'Partial'
+				return "Partial"
 
-		return 'Outside'
+		return "Outside"
 
 	def folio_no_validation(self):
-		shareholder_fields = ['from_shareholder', 'to_shareholder']
+		shareholder_fields = ["from_shareholder", "to_shareholder"]
 		for shareholder_field in shareholder_fields:
 			shareholder_name = self.get(shareholder_field)
 			if not shareholder_name:
 				continue
 			doc = self.get_shareholder_doc(shareholder_name)
 			if doc.company != self.company:
-				frappe.throw(_('The shareholder does not belong to this company'))
+				frappe.throw(_("The shareholder does not belong to this company"))
 			if not doc.folio_no:
-				doc.folio_no = self.from_folio_no \
-					if (shareholder_field == 'from_shareholder') else self.to_folio_no
+				doc.folio_no = (
+					self.from_folio_no if (shareholder_field == "from_shareholder") else self.to_folio_no
+				)
 				doc.save()
 			else:
-				if doc.folio_no and doc.folio_no != (self.from_folio_no if (shareholder_field == 'from_shareholder') else self.to_folio_no):
-					frappe.throw(_('The folio numbers are not matching'))
+				if doc.folio_no and doc.folio_no != (
+					self.from_folio_no if (shareholder_field == "from_shareholder") else self.to_folio_no
+				):
+					frappe.throw(_("The folio numbers are not matching"))
 
 	def autoname_folio(self, shareholder, is_company=False):
 		if is_company:
 			doc = self.get_company_shareholder()
 		else:
 			doc = self.get_shareholder_doc(shareholder)
-		doc.folio_no = make_autoname('FN.#####')
+		doc.folio_no = make_autoname("FN.#####")
 		doc.save()
 		return doc.folio_no
 
@@ -197,106 +223,120 @@
 		# query = {'from_no': share_starting_no, 'to_no': share_ending_no}
 		# Shares exist for sure
 		# Iterate over all entries and modify entry if in entry
-		doc = frappe.get_doc('Shareholder', shareholder)
+		doc = frappe.get_doc("Shareholder", shareholder)
 		current_entries = doc.share_balance
 		new_entries = []
 
 		for entry in current_entries:
 			# use spaceage logic here
-			if entry.share_type != self.share_type or \
-				entry.from_no > self.to_no or \
-				entry.to_no < self.from_no:
+			if (
+				entry.share_type != self.share_type or entry.from_no > self.to_no or entry.to_no < self.from_no
+			):
 				new_entries.append(entry)
-				continue # since query lies outside bounds
+				continue  # since query lies outside bounds
 			elif entry.from_no <= self.from_no and entry.to_no >= self.to_no:
-				#split
+				# split
 				if entry.from_no == self.from_no:
 					if entry.to_no == self.to_no:
-						pass #nothing to append
+						pass  # nothing to append
 					else:
-						new_entries.append(self.return_share_balance_entry(self.to_no+1, entry.to_no, entry.rate))
+						new_entries.append(self.return_share_balance_entry(self.to_no + 1, entry.to_no, entry.rate))
 				else:
 					if entry.to_no == self.to_no:
-						new_entries.append(self.return_share_balance_entry(entry.from_no, self.from_no-1, entry.rate))
+						new_entries.append(
+							self.return_share_balance_entry(entry.from_no, self.from_no - 1, entry.rate)
+						)
 					else:
-						new_entries.append(self.return_share_balance_entry(entry.from_no, self.from_no-1, entry.rate))
-						new_entries.append(self.return_share_balance_entry(self.to_no+1, entry.to_no, entry.rate))
+						new_entries.append(
+							self.return_share_balance_entry(entry.from_no, self.from_no - 1, entry.rate)
+						)
+						new_entries.append(self.return_share_balance_entry(self.to_no + 1, entry.to_no, entry.rate))
 			elif entry.from_no >= self.from_no and entry.to_no <= self.to_no:
 				# split and check
-				pass #nothing to append
+				pass  # nothing to append
 			elif self.from_no <= entry.from_no <= self.to_no and entry.to_no >= self.to_no:
-				new_entries.append(self.return_share_balance_entry(self.to_no+1, entry.to_no, entry.rate))
+				new_entries.append(self.return_share_balance_entry(self.to_no + 1, entry.to_no, entry.rate))
 			elif self.from_no <= entry.to_no <= self.to_no and entry.from_no <= self.from_no:
-				new_entries.append(self.return_share_balance_entry(entry.from_no, self.from_no-1, entry.rate))
+				new_entries.append(
+					self.return_share_balance_entry(entry.from_no, self.from_no - 1, entry.rate)
+				)
 			else:
 				new_entries.append(entry)
 
 		doc.share_balance = []
 		for entry in new_entries:
-			doc.append('share_balance', entry)
+			doc.append("share_balance", entry)
 		doc.save()
 
 	def return_share_balance_entry(self, from_no, to_no, rate):
 		# return an entry as a dict
 		return {
-			'share_type'   : self.share_type,
-			'from_no'	   : from_no,
-			'to_no'		   : to_no,
-			'rate'		   : rate,
-			'amount'	   : self.rate * (to_no - from_no + 1),
-			'no_of_shares' : to_no - from_no + 1
+			"share_type": self.share_type,
+			"from_no": from_no,
+			"to_no": to_no,
+			"rate": rate,
+			"amount": self.rate * (to_no - from_no + 1),
+			"no_of_shares": to_no - from_no + 1,
 		}
 
 	def get_shareholder_doc(self, shareholder):
 		# Get Shareholder doc based on the Shareholder name
 		if shareholder:
-			query_filters = {'name': shareholder}
+			query_filters = {"name": shareholder}
 
-		name = frappe.db.get_value('Shareholder', {'name': shareholder}, 'name')
+		name = frappe.db.get_value("Shareholder", {"name": shareholder}, "name")
 
-		return frappe.get_doc('Shareholder', name)
+		return frappe.get_doc("Shareholder", name)
 
 	def get_company_shareholder(self):
 		# Get company doc or create one if not present
-		company_shareholder = frappe.db.get_value('Shareholder',
-			{
-				'company': self.company,
-				'is_company': 1
-			}, 'name')
+		company_shareholder = frappe.db.get_value(
+			"Shareholder", {"company": self.company, "is_company": 1}, "name"
+		)
 
 		if company_shareholder:
-			return frappe.get_doc('Shareholder', company_shareholder)
+			return frappe.get_doc("Shareholder", company_shareholder)
 		else:
-			shareholder = frappe.get_doc({
-					'doctype': 'Shareholder',
-					'title': self.company,
-					'company': self.company,
-					'is_company': 1
-				})
+			shareholder = frappe.get_doc(
+				{"doctype": "Shareholder", "title": self.company, "company": self.company, "is_company": 1}
+			)
 			shareholder.insert()
 
 			return shareholder
 
+
 @frappe.whitelist()
-def make_jv_entry( company, account, amount, payment_account,\
-	credit_applicant_type, credit_applicant, debit_applicant_type, debit_applicant):
-	journal_entry = frappe.new_doc('Journal Entry')
-	journal_entry.voucher_type = 'Journal Entry'
+def make_jv_entry(
+	company,
+	account,
+	amount,
+	payment_account,
+	credit_applicant_type,
+	credit_applicant,
+	debit_applicant_type,
+	debit_applicant,
+):
+	journal_entry = frappe.new_doc("Journal Entry")
+	journal_entry.voucher_type = "Journal Entry"
 	journal_entry.company = company
 	journal_entry.posting_date = nowdate()
 	account_amt_list = []
 
-	account_amt_list.append({
-		"account": account,
-		"debit_in_account_currency": amount,
-		"party_type": debit_applicant_type,
-		"party": debit_applicant,
-		})
-	account_amt_list.append({
-		"account": payment_account,
-		"credit_in_account_currency": amount,
-		"party_type": credit_applicant_type,
-		"party": credit_applicant,
-		})
+	account_amt_list.append(
+		{
+			"account": account,
+			"debit_in_account_currency": amount,
+			"party_type": debit_applicant_type,
+			"party": debit_applicant,
+		}
+	)
+	account_amt_list.append(
+		{
+			"account": payment_account,
+			"credit_in_account_currency": amount,
+			"party_type": credit_applicant_type,
+			"party": credit_applicant,
+		}
+	)
 	journal_entry.set("accounts", account_amt_list)
 	return journal_entry.as_dict()
diff --git a/erpnext/accounts/doctype/share_transfer/test_share_transfer.py b/erpnext/accounts/doctype/share_transfer/test_share_transfer.py
index bc3a521..9731074 100644
--- a/erpnext/accounts/doctype/share_transfer/test_share_transfer.py
+++ b/erpnext/accounts/doctype/share_transfer/test_share_transfer.py
@@ -9,6 +9,7 @@
 
 test_dependencies = ["Share Type", "Shareholder"]
 
+
 class TestShareTransfer(unittest.TestCase):
 	def setUp(self):
 		frappe.db.sql("delete from `tabShare Transfer`")
@@ -26,7 +27,7 @@
 				"rate": 10,
 				"company": "_Test Company",
 				"asset_account": "Cash - _TC",
-				"equity_or_liability_account": "Creditors - _TC"
+				"equity_or_liability_account": "Creditors - _TC",
 			},
 			{
 				"doctype": "Share Transfer",
@@ -40,7 +41,7 @@
 				"no_of_shares": 100,
 				"rate": 15,
 				"company": "_Test Company",
-				"equity_or_liability_account": "Creditors - _TC"
+				"equity_or_liability_account": "Creditors - _TC",
 			},
 			{
 				"doctype": "Share Transfer",
@@ -54,7 +55,7 @@
 				"no_of_shares": 300,
 				"rate": 20,
 				"company": "_Test Company",
-				"equity_or_liability_account": "Creditors - _TC"
+				"equity_or_liability_account": "Creditors - _TC",
 			},
 			{
 				"doctype": "Share Transfer",
@@ -68,7 +69,7 @@
 				"no_of_shares": 200,
 				"rate": 15,
 				"company": "_Test Company",
-				"equity_or_liability_account": "Creditors - _TC"
+				"equity_or_liability_account": "Creditors - _TC",
 			},
 			{
 				"doctype": "Share Transfer",
@@ -82,42 +83,46 @@
 				"rate": 25,
 				"company": "_Test Company",
 				"asset_account": "Cash - _TC",
-				"equity_or_liability_account": "Creditors - _TC"
-			}
+				"equity_or_liability_account": "Creditors - _TC",
+			},
 		]
 		for d in share_transfers:
 			st = frappe.get_doc(d)
 			st.submit()
 
 	def test_invalid_share_transfer(self):
-		doc = frappe.get_doc({
-			"doctype": "Share Transfer",
-			"transfer_type": "Transfer",
-			"date": "2018-01-05",
-			"from_shareholder": "SH-00003",
-			"to_shareholder": "SH-00002",
-			"share_type": "Equity",
-			"from_no": 1,
-			"to_no": 100,
-			"no_of_shares": 100,
-			"rate": 15,
-			"company": "_Test Company",
-			"equity_or_liability_account": "Creditors - _TC"
-		})
+		doc = frappe.get_doc(
+			{
+				"doctype": "Share Transfer",
+				"transfer_type": "Transfer",
+				"date": "2018-01-05",
+				"from_shareholder": "SH-00003",
+				"to_shareholder": "SH-00002",
+				"share_type": "Equity",
+				"from_no": 1,
+				"to_no": 100,
+				"no_of_shares": 100,
+				"rate": 15,
+				"company": "_Test Company",
+				"equity_or_liability_account": "Creditors - _TC",
+			}
+		)
 		self.assertRaises(ShareDontExists, doc.insert)
 
-		doc = frappe.get_doc({
-			"doctype": "Share Transfer",
-			"transfer_type": "Purchase",
-			"date": "2018-01-02",
-			"from_shareholder": "SH-00001",
-			"share_type": "Equity",
-			"from_no": 1,
-			"to_no": 200,
-			"no_of_shares": 200,
-			"rate": 15,
-			"company": "_Test Company",
-			"asset_account": "Cash - _TC",
-			"equity_or_liability_account": "Creditors - _TC"
-		})
+		doc = frappe.get_doc(
+			{
+				"doctype": "Share Transfer",
+				"transfer_type": "Purchase",
+				"date": "2018-01-02",
+				"from_shareholder": "SH-00001",
+				"share_type": "Equity",
+				"from_no": 1,
+				"to_no": 200,
+				"no_of_shares": 200,
+				"rate": 15,
+				"company": "_Test Company",
+				"asset_account": "Cash - _TC",
+				"equity_or_liability_account": "Creditors - _TC",
+			}
+		)
 		self.assertRaises(ShareDontExists, doc.insert)
diff --git a/erpnext/accounts/doctype/share_type/share_type_dashboard.py b/erpnext/accounts/doctype/share_type/share_type_dashboard.py
index d5551d1..19604b3 100644
--- a/erpnext/accounts/doctype/share_type/share_type_dashboard.py
+++ b/erpnext/accounts/doctype/share_type/share_type_dashboard.py
@@ -3,11 +3,6 @@
 
 def get_data():
 	return {
-		'fieldname': 'share_type',
-		'transactions': [
-			{
-				'label': _('References'),
-				'items': ['Share Transfer', 'Shareholder']
-			}
-		]
+		"fieldname": "share_type",
+		"transactions": [{"label": _("References"), "items": ["Share Transfer", "Shareholder"]}],
 	}
diff --git a/erpnext/accounts/doctype/shareholder/shareholder.py b/erpnext/accounts/doctype/shareholder/shareholder.py
index 8a0fa85..b0e2493 100644
--- a/erpnext/accounts/doctype/shareholder/shareholder.py
+++ b/erpnext/accounts/doctype/shareholder/shareholder.py
@@ -15,7 +15,7 @@
 		load_address_and_contact(self)
 
 	def on_trash(self):
-		delete_contact_and_address('Shareholder', self.name)
+		delete_contact_and_address("Shareholder", self.name)
 
 	def before_save(self):
 		for entry in self.share_balance:
diff --git a/erpnext/accounts/doctype/shareholder/shareholder_dashboard.py b/erpnext/accounts/doctype/shareholder/shareholder_dashboard.py
index c01ac23..fa9d431 100644
--- a/erpnext/accounts/doctype/shareholder/shareholder_dashboard.py
+++ b/erpnext/accounts/doctype/shareholder/shareholder_dashboard.py
@@ -1,12 +1,6 @@
 def get_data():
 	return {
-		'fieldname': 'shareholder',
-		'non_standard_fieldnames': {
-			'Share Transfer': 'to_shareholder'
-		},
-		'transactions': [
-			{
-				'items': ['Share Transfer']
-			}
-		]
+		"fieldname": "shareholder",
+		"non_standard_fieldnames": {"Share Transfer": "to_shareholder"},
+		"transactions": [{"items": ["Share Transfer"]}],
 	}
diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py
index 792e7d2..1d79503 100644
--- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py
+++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py
@@ -12,9 +12,17 @@
 import erpnext
 
 
-class OverlappingConditionError(frappe.ValidationError): pass
-class FromGreaterThanToError(frappe.ValidationError): pass
-class ManyBlankToValuesError(frappe.ValidationError): pass
+class OverlappingConditionError(frappe.ValidationError):
+	pass
+
+
+class FromGreaterThanToError(frappe.ValidationError):
+	pass
+
+
+class ManyBlankToValuesError(frappe.ValidationError):
+	pass
+
 
 class ShippingRule(Document):
 	def validate(self):
@@ -35,15 +43,19 @@
 			if not d.to_value:
 				zero_to_values.append(d)
 			elif d.from_value >= d.to_value:
-				throw(_("From value must be less than to value in row {0}").format(d.idx), FromGreaterThanToError)
+				throw(
+					_("From value must be less than to value in row {0}").format(d.idx), FromGreaterThanToError
+				)
 
 		# check if more than two or more rows has To Value = 0
 		if len(zero_to_values) >= 2:
-			throw(_('There can only be one Shipping Rule Condition with 0 or blank value for "To Value"'),
-				ManyBlankToValuesError)
+			throw(
+				_('There can only be one Shipping Rule Condition with 0 or blank value for "To Value"'),
+				ManyBlankToValuesError,
+			)
 
 	def apply(self, doc):
-		'''Apply shipping rule on given doc. Called from accounts controller'''
+		"""Apply shipping rule on given doc. Called from accounts controller"""
 
 		shipping_amount = 0.0
 		by_value = False
@@ -52,15 +64,15 @@
 			# validate country only if there is address
 			self.validate_countries(doc)
 
-		if self.calculate_based_on == 'Net Total':
+		if self.calculate_based_on == "Net Total":
 			value = doc.base_net_total
 			by_value = True
 
-		elif self.calculate_based_on == 'Net Weight':
+		elif self.calculate_based_on == "Net Weight":
 			value = doc.total_net_weight
 			by_value = True
 
-		elif self.calculate_based_on == 'Fixed':
+		elif self.calculate_based_on == "Fixed":
 			shipping_amount = self.shipping_amount
 
 		# shipping amount by value, apply conditions
@@ -71,12 +83,13 @@
 		if doc.currency != doc.company_currency:
 			shipping_amount = flt(shipping_amount / doc.conversion_rate, 2)
 
-		if shipping_amount:
-			self.add_shipping_rule_to_tax_table(doc, shipping_amount)
+		self.add_shipping_rule_to_tax_table(doc, shipping_amount)
 
 	def get_shipping_amount_from_rules(self, value):
 		for condition in self.get("conditions"):
-			if not condition.to_value or (flt(condition.from_value) <= flt(value) <= flt(condition.to_value)):
+			if not condition.to_value or (
+				flt(condition.from_value) <= flt(value) <= flt(condition.to_value)
+			):
 				return condition.shipping_amount
 
 		return 0.0
@@ -84,27 +97,31 @@
 	def validate_countries(self, doc):
 		# validate applicable countries
 		if self.countries:
-			shipping_country = doc.get_shipping_address().get('country')
+			shipping_country = doc.get_shipping_address().get("country")
 			if not shipping_country:
-				frappe.throw(_('Shipping Address does not have country, which is required for this Shipping Rule'))
+				frappe.throw(
+					_("Shipping Address does not have country, which is required for this Shipping Rule")
+				)
 			if shipping_country not in [d.country for d in self.countries]:
-				frappe.throw(_('Shipping rule not applicable for country {0} in Shipping Address').format(shipping_country))
+				frappe.throw(
+					_("Shipping rule not applicable for country {0} in Shipping Address").format(shipping_country)
+				)
 
 	def add_shipping_rule_to_tax_table(self, doc, shipping_amount):
 		shipping_charge = {
 			"charge_type": "Actual",
 			"account_head": self.account,
-			"cost_center": self.cost_center
+			"cost_center": self.cost_center,
 		}
 		if self.shipping_rule_type == "Selling":
 			# check if not applied on purchase
-			if not doc.meta.get_field('taxes').options == 'Sales Taxes and Charges':
-				frappe.throw(_('Shipping rule only applicable for Selling'))
+			if not doc.meta.get_field("taxes").options == "Sales Taxes and Charges":
+				frappe.throw(_("Shipping rule only applicable for Selling"))
 			shipping_charge["doctype"] = "Sales Taxes and Charges"
 		else:
 			# check if not applied on sales
-			if not doc.meta.get_field('taxes').options == 'Purchase Taxes and Charges':
-				frappe.throw(_('Shipping rule only applicable for Buying'))
+			if not doc.meta.get_field("taxes").options == "Purchase Taxes and Charges":
+				frappe.throw(_("Shipping rule only applicable for Buying"))
 
 			shipping_charge["doctype"] = "Purchase Taxes and Charges"
 			shipping_charge["category"] = "Valuation and Total"
@@ -128,19 +145,19 @@
 	def validate_overlapping_shipping_rule_conditions(self):
 		def overlap_exists_between(num_range1, num_range2):
 			"""
-				num_range1 and num_range2 are two ranges
-				ranges are represented as a tuple e.g. range 100 to 300 is represented as (100, 300)
-				if condition num_range1 = 100 to 300
-				then condition num_range2 can only be like 50 to 99 or 301 to 400
-				hence, non-overlapping condition = (x1 <= x2 < y1 <= y2) or (y1 <= y2 < x1 <= x2)
+			num_range1 and num_range2 are two ranges
+			ranges are represented as a tuple e.g. range 100 to 300 is represented as (100, 300)
+			if condition num_range1 = 100 to 300
+			then condition num_range2 can only be like 50 to 99 or 301 to 400
+			hence, non-overlapping condition = (x1 <= x2 < y1 <= y2) or (y1 <= y2 < x1 <= x2)
 			"""
 			(x1, x2), (y1, y2) = num_range1, num_range2
 			separate = (x1 <= x2 <= y1 <= y2) or (y1 <= y2 <= x1 <= x2)
-			return (not separate)
+			return not separate
 
 		overlaps = []
 		for i in range(0, len(self.conditions)):
-			for j in range(i+1, len(self.conditions)):
+			for j in range(i + 1, len(self.conditions)):
 				d1, d2 = self.conditions[i], self.conditions[j]
 				if d1.as_dict() != d2.as_dict():
 					# in our case, to_value can be zero, hence pass the from_value if so
@@ -154,7 +171,12 @@
 			msgprint(_("Overlapping conditions found between:"))
 			messages = []
 			for d1, d2 in overlaps:
-				messages.append("%s-%s = %s " % (d1.from_value, d1.to_value, fmt_money(d1.shipping_amount, currency=company_currency)) +
-					_("and") + " %s-%s = %s" % (d2.from_value, d2.to_value, fmt_money(d2.shipping_amount, currency=company_currency)))
+				messages.append(
+					"%s-%s = %s "
+					% (d1.from_value, d1.to_value, fmt_money(d1.shipping_amount, currency=company_currency))
+					+ _("and")
+					+ " %s-%s = %s"
+					% (d2.from_value, d2.to_value, fmt_money(d2.shipping_amount, currency=company_currency))
+				)
 
 			msgprint("\n".join(messages), raise_exception=OverlappingConditionError)
diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule_dashboard.py b/erpnext/accounts/doctype/shipping_rule/shipping_rule_dashboard.py
index fc70621..60ce120 100644
--- a/erpnext/accounts/doctype/shipping_rule/shipping_rule_dashboard.py
+++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule_dashboard.py
@@ -3,22 +3,11 @@
 
 def get_data():
 	return {
-		'fieldname': 'shipping_rule',
-		'non_standard_fieldnames': {
-			'Payment Entry': 'party_name'
-		},
-		'transactions': [
-			{
-				'label': _('Pre Sales'),
-				'items': ['Quotation', 'Supplier Quotation']
-			},
-			{
-				'label': _('Sales'),
-				'items': ['Sales Order', 'Delivery Note', 'Sales Invoice']
-			},
-			{
-				'label': _('Purchase'),
-				'items': ['Purchase Invoice', 'Purchase Order', 'Purchase Receipt']
-			}
-		]
+		"fieldname": "shipping_rule",
+		"non_standard_fieldnames": {"Payment Entry": "party_name"},
+		"transactions": [
+			{"label": _("Pre Sales"), "items": ["Quotation", "Supplier Quotation"]},
+			{"label": _("Sales"), "items": ["Sales Order", "Delivery Note", "Sales Invoice"]},
+			{"label": _("Purchase"), "items": ["Purchase Invoice", "Purchase Order", "Purchase Receipt"]},
+		],
 	}
diff --git a/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.py b/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.py
index c06dae0..a24e834 100644
--- a/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.py
+++ b/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.py
@@ -11,18 +11,19 @@
 	OverlappingConditionError,
 )
 
-test_records = frappe.get_test_records('Shipping Rule')
+test_records = frappe.get_test_records("Shipping Rule")
+
 
 class TestShippingRule(unittest.TestCase):
 	def test_from_greater_than_to(self):
 		shipping_rule = frappe.copy_doc(test_records[0])
-		shipping_rule.name = test_records[0].get('name')
+		shipping_rule.name = test_records[0].get("name")
 		shipping_rule.get("conditions")[0].from_value = 101
 		self.assertRaises(FromGreaterThanToError, shipping_rule.insert)
 
 	def test_many_zero_to_values(self):
 		shipping_rule = frappe.copy_doc(test_records[0])
-		shipping_rule.name = test_records[0].get('name')
+		shipping_rule.name = test_records[0].get("name")
 		shipping_rule.get("conditions")[0].to_value = 0
 		self.assertRaises(ManyBlankToValuesError, shipping_rule.insert)
 
@@ -35,48 +36,58 @@
 			((50, 150), (50, 150)),
 		]:
 			shipping_rule = frappe.copy_doc(test_records[0])
-			shipping_rule.name = test_records[0].get('name')
+			shipping_rule.name = test_records[0].get("name")
 			shipping_rule.get("conditions")[0].from_value = range_a[0]
 			shipping_rule.get("conditions")[0].to_value = range_a[1]
 			shipping_rule.get("conditions")[1].from_value = range_b[0]
 			shipping_rule.get("conditions")[1].to_value = range_b[1]
 			self.assertRaises(OverlappingConditionError, shipping_rule.insert)
 
+
 def create_shipping_rule(shipping_rule_type, shipping_rule_name):
 
 	if frappe.db.exists("Shipping Rule", shipping_rule_name):
 		return frappe.get_doc("Shipping Rule", shipping_rule_name)
 
 	sr = frappe.new_doc("Shipping Rule")
-	sr.account =  "_Test Account Shipping Charges - _TC"
-	sr.calculate_based_on =  "Net Total"
+	sr.account = "_Test Account Shipping Charges - _TC"
+	sr.calculate_based_on = "Net Total"
 	sr.company = "_Test Company"
 	sr.cost_center = "_Test Cost Center - _TC"
 	sr.label = shipping_rule_name
 	sr.name = shipping_rule_name
 	sr.shipping_rule_type = shipping_rule_type
 
-	sr.append("conditions", {
+	sr.append(
+		"conditions",
+		{
 			"doctype": "Shipping Rule Condition",
 			"from_value": 0,
 			"parentfield": "conditions",
 			"shipping_amount": 50.0,
-			"to_value": 100
-		})
-	sr.append("conditions", {
+			"to_value": 100,
+		},
+	)
+	sr.append(
+		"conditions",
+		{
 			"doctype": "Shipping Rule Condition",
 			"from_value": 101,
 			"parentfield": "conditions",
 			"shipping_amount": 100.0,
-			"to_value": 200
-		})
-	sr.append("conditions", {
+			"to_value": 200,
+		},
+	)
+	sr.append(
+		"conditions",
+		{
 			"doctype": "Shipping Rule Condition",
 			"from_value": 201,
 			"parentfield": "conditions",
 			"shipping_amount": 200.0,
-			"to_value": 2000
-		})
+			"to_value": 2000,
+		},
+	)
 	sr.insert(ignore_permissions=True)
 	sr.submit()
 	return sr
diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py
index 467d4a1..9dab4e9 100644
--- a/erpnext/accounts/doctype/subscription/subscription.py
+++ b/erpnext/accounts/doctype/subscription/subscription.py
@@ -60,7 +60,11 @@
 		"""
 		_current_invoice_start = None
 
-		if self.is_new_subscription() and self.trial_period_end and getdate(self.trial_period_end) > getdate(self.start_date):
+		if (
+			self.is_new_subscription()
+			and self.trial_period_end
+			and getdate(self.trial_period_end) > getdate(self.start_date)
+		):
 			_current_invoice_start = add_days(self.trial_period_end, 1)
 		elif self.trial_period_start and self.is_trialling():
 			_current_invoice_start = self.trial_period_start
@@ -102,7 +106,7 @@
 
 			if self.follow_calendar_months:
 				billing_info = self.get_billing_cycle_and_interval()
-				billing_interval_count = billing_info[0]['billing_interval_count']
+				billing_interval_count = billing_info[0]["billing_interval_count"]
 				calendar_months = get_calendar_months(billing_interval_count)
 				calendar_month = 0
 				current_invoice_end_month = getdate(_current_invoice_end).month
@@ -112,12 +116,13 @@
 					if month <= current_invoice_end_month:
 						calendar_month = month
 
-				if cint(calendar_month - billing_interval_count) <= 0 and \
-					getdate(date).month != 1:
+				if cint(calendar_month - billing_interval_count) <= 0 and getdate(date).month != 1:
 					calendar_month = 12
 					current_invoice_end_year -= 1
 
-				_current_invoice_end = get_last_day(cstr(current_invoice_end_year) + '-' + cstr(calendar_month) + '-01')
+				_current_invoice_end = get_last_day(
+					cstr(current_invoice_end_year) + "-" + cstr(calendar_month) + "-01"
+				)
 
 			if self.end_date and getdate(_current_invoice_end) > getdate(self.end_date):
 				_current_invoice_end = self.end_date
@@ -131,7 +136,7 @@
 		same billing interval
 		"""
 		if billing_cycle_data and len(billing_cycle_data) != 1:
-			frappe.throw(_('You can only have Plans with the same billing cycle in a Subscription'))
+			frappe.throw(_("You can only have Plans with the same billing cycle in a Subscription"))
 
 	def get_billing_cycle_and_interval(self):
 		"""
@@ -140,12 +145,14 @@
 		You shouldn't need to call this directly. Use `get_billing_cycle` instead.
 		"""
 		plan_names = [plan.plan for plan in self.plans]
-		billing_info = frappe.db.sql(
-			'select distinct `billing_interval`, `billing_interval_count` '
-			'from `tabSubscription Plan` '
-			'where name in %s',
-			(plan_names,), as_dict=1
-		)
+
+		subscription_plan = frappe.qb.DocType("Subscription Plan")
+		billing_info = (
+			frappe.qb.from_(subscription_plan)
+			.select(subscription_plan.billing_interval, subscription_plan.billing_interval_count)
+			.distinct()
+			.where(subscription_plan.name.isin(plan_names))
+		).run(as_dict=1)
 
 		return billing_info
 
@@ -161,19 +168,19 @@
 
 		if billing_info:
 			data = dict()
-			interval = billing_info[0]['billing_interval']
-			interval_count = billing_info[0]['billing_interval_count']
-			if interval not in ['Day', 'Week']:
-				data['days'] = -1
-			if interval == 'Day':
-				data['days'] = interval_count - 1
-			elif interval == 'Month':
-				data['months'] = interval_count
-			elif interval == 'Year':
-				data['years'] = interval_count
+			interval = billing_info[0]["billing_interval"]
+			interval_count = billing_info[0]["billing_interval_count"]
+			if interval not in ["Day", "Week"]:
+				data["days"] = -1
+			if interval == "Day":
+				data["days"] = interval_count - 1
+			elif interval == "Month":
+				data["months"] = interval_count
+			elif interval == "Year":
+				data["years"] = interval_count
 			# todo: test week
-			elif interval == 'Week':
-				data['days'] = interval_count * 7 - 1
+			elif interval == "Week":
+				data["days"] = interval_count * 7 - 1
 
 			return data
 
@@ -184,27 +191,27 @@
 		Used when the `Subscription` needs to decide what to do after the current generated
 		invoice is past it's due date and grace period.
 		"""
-		subscription_settings = frappe.get_single('Subscription Settings')
-		if self.status == 'Past Due Date' and self.is_past_grace_period():
-			self.status = 'Cancelled' if cint(subscription_settings.cancel_after_grace) else 'Unpaid'
+		subscription_settings = frappe.get_single("Subscription Settings")
+		if self.status == "Past Due Date" and self.is_past_grace_period():
+			self.status = "Cancelled" if cint(subscription_settings.cancel_after_grace) else "Unpaid"
 
 	def set_subscription_status(self):
 		"""
 		Sets the status of the `Subscription`
 		"""
 		if self.is_trialling():
-			self.status = 'Trialling'
-		elif self.status == 'Active' and self.end_date and getdate() > getdate(self.end_date):
-			self.status = 'Completed'
+			self.status = "Trialling"
+		elif self.status == "Active" and self.end_date and getdate() > getdate(self.end_date):
+			self.status = "Completed"
 		elif self.is_past_grace_period():
-			subscription_settings = frappe.get_single('Subscription Settings')
-			self.status = 'Cancelled' if cint(subscription_settings.cancel_after_grace) else 'Unpaid'
+			subscription_settings = frappe.get_single("Subscription Settings")
+			self.status = "Cancelled" if cint(subscription_settings.cancel_after_grace) else "Unpaid"
 		elif self.current_invoice_is_past_due() and not self.is_past_grace_period():
-			self.status = 'Past Due Date'
+			self.status = "Past Due Date"
 		elif not self.has_outstanding_invoice():
-			self.status = 'Active'
+			self.status = "Active"
 		elif self.is_new_subscription():
-			self.status = 'Active'
+			self.status = "Active"
 		self.save()
 
 	def is_trialling(self):
@@ -231,7 +238,7 @@
 		"""
 		current_invoice = self.get_current_invoice()
 		if self.current_invoice_is_past_due(current_invoice):
-			subscription_settings = frappe.get_single('Subscription Settings')
+			subscription_settings = frappe.get_single("Subscription Settings")
 			grace_period = cint(subscription_settings.grace_period)
 
 			return getdate() > add_days(current_invoice.due_date, grace_period)
@@ -252,15 +259,15 @@
 		"""
 		Returns the most recent generated invoice.
 		"""
-		doctype = 'Sales Invoice' if self.party_type == 'Customer' else 'Purchase Invoice'
+		doctype = "Sales Invoice" if self.party_type == "Customer" else "Purchase Invoice"
 
 		if len(self.invoices):
 			current = self.invoices[-1]
-			if frappe.db.exists(doctype, current.get('invoice')):
-				doc = frappe.get_doc(doctype, current.get('invoice'))
+			if frappe.db.exists(doctype, current.get("invoice")):
+				doc = frappe.get_doc(doctype, current.get("invoice"))
 				return doc
 			else:
-				frappe.throw(_('Invoice {0} no longer exists').format(current.get('invoice')))
+				frappe.throw(_("Invoice {0} no longer exists").format(current.get("invoice")))
 
 	def is_new_subscription(self):
 		"""
@@ -273,7 +280,7 @@
 		self.validate_plans_billing_cycle(self.get_billing_cycle_and_interval())
 		self.validate_end_date()
 		self.validate_to_follow_calendar_months()
-		self.cost_center = erpnext.get_default_cost_center(self.get('company'))
+		self.cost_center = erpnext.get_default_cost_center(self.get("company"))
 
 	def validate_trial_period(self):
 		"""
@@ -281,30 +288,34 @@
 		"""
 		if self.trial_period_start and self.trial_period_end:
 			if getdate(self.trial_period_end) < getdate(self.trial_period_start):
-				frappe.throw(_('Trial Period End Date Cannot be before Trial Period Start Date'))
+				frappe.throw(_("Trial Period End Date Cannot be before Trial Period Start Date"))
 
 		if self.trial_period_start and not self.trial_period_end:
-			frappe.throw(_('Both Trial Period Start Date and Trial Period End Date must be set'))
+			frappe.throw(_("Both Trial Period Start Date and Trial Period End Date must be set"))
 
 		if self.trial_period_start and getdate(self.trial_period_start) > getdate(self.start_date):
-			frappe.throw(_('Trial Period Start date cannot be after Subscription Start Date'))
+			frappe.throw(_("Trial Period Start date cannot be after Subscription Start Date"))
 
 	def validate_end_date(self):
 		billing_cycle_info = self.get_billing_cycle_data()
 		end_date = add_to_date(self.start_date, **billing_cycle_info)
 
 		if self.end_date and getdate(self.end_date) <= getdate(end_date):
-			frappe.throw(_('Subscription End Date must be after {0} as per the subscription plan').format(end_date))
+			frappe.throw(
+				_("Subscription End Date must be after {0} as per the subscription plan").format(end_date)
+			)
 
 	def validate_to_follow_calendar_months(self):
 		if self.follow_calendar_months:
 			billing_info = self.get_billing_cycle_and_interval()
 
 			if not self.end_date:
-				frappe.throw(_('Subscription End Date is mandatory to follow calendar months'))
+				frappe.throw(_("Subscription End Date is mandatory to follow calendar months"))
 
-			if billing_info[0]['billing_interval'] != 'Month':
-				frappe.throw(_('Billing Interval in Subscription Plan must be Month to follow calendar months'))
+			if billing_info[0]["billing_interval"] != "Month":
+				frappe.throw(
+					_("Billing Interval in Subscription Plan must be Month to follow calendar months")
+				)
 
 	def after_insert(self):
 		# todo: deal with users who collect prepayments. Maybe a new Subscription Invoice doctype?
@@ -316,13 +327,10 @@
 		saves the `Subscription`.
 		"""
 
-		doctype = 'Sales Invoice' if self.party_type == 'Customer' else 'Purchase Invoice'
+		doctype = "Sales Invoice" if self.party_type == "Customer" else "Purchase Invoice"
 
 		invoice = self.create_invoice(prorate)
-		self.append('invoices', {
-			'document_type': doctype,
-			'invoice': invoice.name
-		})
+		self.append("invoices", {"document_type": doctype, "invoice": invoice.name})
 
 		self.save()
 
@@ -332,28 +340,33 @@
 		"""
 		Creates a `Invoice`, submits it and returns it
 		"""
-		doctype = 'Sales Invoice' if self.party_type == 'Customer' else 'Purchase Invoice'
+		doctype = "Sales Invoice" if self.party_type == "Customer" else "Purchase Invoice"
 
 		invoice = frappe.new_doc(doctype)
 
 		# For backward compatibility
 		# Earlier subscription didn't had any company field
-		company = self.get('company') or get_default_company()
+		company = self.get("company") or get_default_company()
 		if not company:
-			frappe.throw(_("Company is mandatory was generating invoice. Please set default company in Global Defaults"))
+			frappe.throw(
+				_("Company is mandatory was generating invoice. Please set default company in Global Defaults")
+			)
 
 		invoice.company = company
 		invoice.set_posting_time = 1
-		invoice.posting_date = self.current_invoice_start if self.generate_invoice_at_period_start \
+		invoice.posting_date = (
+			self.current_invoice_start
+			if self.generate_invoice_at_period_start
 			else self.current_invoice_end
+		)
 
 		invoice.cost_center = self.cost_center
 
-		if doctype == 'Sales Invoice':
+		if doctype == "Sales Invoice":
 			invoice.customer = self.party
 		else:
 			invoice.supplier = self.party
-			if frappe.db.get_value('Supplier', self.party, 'tax_withholding_category'):
+			if frappe.db.get_value("Supplier", self.party, "tax_withholding_category"):
 				invoice.apply_tds = 1
 
 		### Add party currency to invoice
@@ -364,23 +377,21 @@
 
 		for dimension in accounting_dimensions:
 			if self.get(dimension):
-				invoice.update({
-					dimension: self.get(dimension)
-				})
+				invoice.update({dimension: self.get(dimension)})
 
 		# Subscription is better suited for service items. I won't update `update_stock`
 		# for that reason
 		items_list = self.get_items_from_plans(self.plans, prorate)
 		for item in items_list:
-			item['cost_center'] = self.cost_center
-			invoice.append('items', item)
+			item["cost_center"] = self.cost_center
+			invoice.append("items", item)
 
 		# Taxes
-		tax_template = ''
+		tax_template = ""
 
-		if doctype == 'Sales Invoice' and self.sales_tax_template:
+		if doctype == "Sales Invoice" and self.sales_tax_template:
 			tax_template = self.sales_tax_template
-		if doctype == 'Purchase Invoice' and self.purchase_tax_template:
+		if doctype == "Purchase Invoice" and self.purchase_tax_template:
 			tax_template = self.purchase_tax_template
 
 		if tax_template:
@@ -390,11 +401,11 @@
 		# Due date
 		if self.days_until_due:
 			invoice.append(
-				'payment_schedule',
+				"payment_schedule",
 				{
-					'due_date': add_days(invoice.posting_date, cint(self.days_until_due)),
-					'invoice_portion': 100
-				}
+					"due_date": add_days(invoice.posting_date, cint(self.days_until_due)),
+					"invoice_portion": 100,
+				},
 			)
 
 		# Discounts
@@ -409,7 +420,7 @@
 
 			if self.additional_discount_percentage or self.additional_discount_amount:
 				discount_on = self.apply_additional_discount
-				invoice.apply_discount_on = discount_on if discount_on else 'Grand Total'
+				invoice.apply_discount_on = discount_on if discount_on else "Grand Total"
 
 		# Subscription period
 		invoice.from_date = self.current_invoice_start
@@ -430,44 +441,62 @@
 		Returns the `Item`s linked to `Subscription Plan`
 		"""
 		if prorate:
-			prorate_factor = get_prorata_factor(self.current_invoice_end, self.current_invoice_start,
-				self.generate_invoice_at_period_start)
+			prorate_factor = get_prorata_factor(
+				self.current_invoice_end, self.current_invoice_start, self.generate_invoice_at_period_start
+			)
 
 		items = []
 		party = self.party
 		for plan in plans:
-			plan_doc = frappe.get_doc('Subscription Plan', plan.plan)
+			plan_doc = frappe.get_doc("Subscription Plan", plan.plan)
 
 			item_code = plan_doc.item
 
-			if self.party == 'Customer':
-				deferred_field = 'enable_deferred_revenue'
+			if self.party == "Customer":
+				deferred_field = "enable_deferred_revenue"
 			else:
-				deferred_field = 'enable_deferred_expense'
+				deferred_field = "enable_deferred_expense"
 
-			deferred = frappe.db.get_value('Item', item_code, deferred_field)
+			deferred = frappe.db.get_value("Item", item_code, deferred_field)
 
 			if not prorate:
-				item = {'item_code': item_code, 'qty': plan.qty, 'rate': get_plan_rate(plan.plan, plan.qty, party,
-					self.current_invoice_start, self.current_invoice_end), 'cost_center': plan_doc.cost_center}
+				item = {
+					"item_code": item_code,
+					"qty": plan.qty,
+					"rate": get_plan_rate(
+						plan.plan, plan.qty, party, self.current_invoice_start, self.current_invoice_end
+					),
+					"cost_center": plan_doc.cost_center,
+				}
 			else:
-				item = {'item_code': item_code, 'qty': plan.qty, 'rate': get_plan_rate(plan.plan, plan.qty, party,
-					self.current_invoice_start, self.current_invoice_end, prorate_factor), 'cost_center': plan_doc.cost_center}
+				item = {
+					"item_code": item_code,
+					"qty": plan.qty,
+					"rate": get_plan_rate(
+						plan.plan,
+						plan.qty,
+						party,
+						self.current_invoice_start,
+						self.current_invoice_end,
+						prorate_factor,
+					),
+					"cost_center": plan_doc.cost_center,
+				}
 
 			if deferred:
-				item.update({
-					deferred_field: deferred,
-					'service_start_date': self.current_invoice_start,
-					'service_end_date': self.current_invoice_end
-				})
+				item.update(
+					{
+						deferred_field: deferred,
+						"service_start_date": self.current_invoice_start,
+						"service_end_date": self.current_invoice_end,
+					}
+				)
 
 			accounting_dimensions = get_accounting_dimensions()
 
 			for dimension in accounting_dimensions:
 				if plan_doc.get(dimension):
-					item.update({
-						dimension: plan_doc.get(dimension)
-					})
+					item.update({dimension: plan_doc.get(dimension)})
 
 			items.append(item)
 
@@ -480,9 +509,9 @@
 		1. `process_for_active`
 		2. `process_for_past_due`
 		"""
-		if self.status == 'Active':
+		if self.status == "Active":
 			self.process_for_active()
-		elif self.status in ['Past Due Date', 'Unpaid']:
+		elif self.status in ["Past Due Date", "Unpaid"]:
 			self.process_for_past_due_date()
 
 		self.set_subscription_status()
@@ -490,8 +519,10 @@
 		self.save()
 
 	def is_postpaid_to_invoice(self):
-		return getdate() > getdate(self.current_invoice_end) or \
-			(getdate() >= getdate(self.current_invoice_end) and getdate(self.current_invoice_end) == getdate(self.current_invoice_start))
+		return getdate() > getdate(self.current_invoice_end) or (
+			getdate() >= getdate(self.current_invoice_end)
+			and getdate(self.current_invoice_end) == getdate(self.current_invoice_start)
+		)
 
 	def is_prepaid_to_invoice(self):
 		if not self.generate_invoice_at_period_start:
@@ -507,9 +538,13 @@
 		invoice = self.get_current_invoice()
 
 		if not (_current_start_date and _current_end_date):
-			_current_start_date, _current_end_date = self.update_subscription_period(date=add_days(self.current_invoice_end, 1), return_date=True)
+			_current_start_date, _current_end_date = self.update_subscription_period(
+				date=add_days(self.current_invoice_end, 1), return_date=True
+			)
 
-		if invoice and getdate(_current_start_date) <= getdate(invoice.posting_date) <= getdate(_current_end_date):
+		if invoice and getdate(_current_start_date) <= getdate(invoice.posting_date) <= getdate(
+			_current_end_date
+		):
 			return True
 
 		return False
@@ -524,10 +559,11 @@
 		3. Change the `Subscription` status to 'Cancelled'
 		"""
 
-		if not self.is_current_invoice_generated(self.current_invoice_start, self.current_invoice_end) \
-			and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()):
+		if not self.is_current_invoice_generated(
+			self.current_invoice_start, self.current_invoice_end
+		) and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()):
 
-			prorate = frappe.db.get_single_value('Subscription Settings', 'prorate')
+			prorate = frappe.db.get_single_value("Subscription Settings", "prorate")
 			self.generate_invoice(prorate)
 
 		if getdate() > getdate(self.current_invoice_end) and self.is_prepaid_to_invoice():
@@ -543,7 +579,7 @@
 		if self.end_date and getdate() < getdate(self.end_date):
 			return
 
-		self.status = 'Cancelled'
+		self.status = "Cancelled"
 		if not self.cancelation_date:
 			self.cancelation_date = nowdate()
 
@@ -558,10 +594,10 @@
 		"""
 		current_invoice = self.get_current_invoice()
 		if not current_invoice:
-			frappe.throw(_('Current invoice {0} is missing').format(current_invoice.invoice))
+			frappe.throw(_("Current invoice {0} is missing").format(current_invoice.invoice))
 		else:
 			if not self.has_outstanding_invoice():
-				self.status = 'Active'
+				self.status = "Active"
 			else:
 				self.set_status_grace_period()
 
@@ -569,31 +605,33 @@
 				self.update_subscription_period(add_days(self.current_invoice_end, 1))
 
 			# Generate invoices periodically even if current invoice are unpaid
-			if self.generate_new_invoices_past_due_date and not \
-				self.is_current_invoice_generated(self.current_invoice_start, self.current_invoice_end) \
-				and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()):
+			if (
+				self.generate_new_invoices_past_due_date
+				and not self.is_current_invoice_generated(self.current_invoice_start, self.current_invoice_end)
+				and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice())
+			):
 
-				prorate = frappe.db.get_single_value('Subscription Settings', 'prorate')
+				prorate = frappe.db.get_single_value("Subscription Settings", "prorate")
 				self.generate_invoice(prorate)
 
-
 	@staticmethod
 	def is_paid(invoice):
 		"""
 		Return `True` if the given invoice is paid
 		"""
-		return invoice.status == 'Paid'
+		return invoice.status == "Paid"
 
 	def has_outstanding_invoice(self):
 		"""
 		Returns `True` if the most recent invoice for the `Subscription` is not paid
 		"""
-		doctype = 'Sales Invoice' if self.party_type == 'Customer' else 'Purchase Invoice'
+		doctype = "Sales Invoice" if self.party_type == "Customer" else "Purchase Invoice"
 		current_invoice = self.get_current_invoice()
 		invoice_list = [d.invoice for d in self.invoices]
 
-		outstanding_invoices = frappe.get_all(doctype, fields=['name'],
-			filters={'status': ('!=', 'Paid'), 'name': ('in', invoice_list)})
+		outstanding_invoices = frappe.get_all(
+			doctype, fields=["name"], filters={"status": ("!=", "Paid"), "name": ("in", invoice_list)}
+		)
 
 		if outstanding_invoices:
 			return True
@@ -605,10 +643,12 @@
 		This sets the subscription as cancelled. It will stop invoices from being generated
 		but it will not affect already created invoices.
 		"""
-		if self.status != 'Cancelled':
-			to_generate_invoice = True if self.status == 'Active' and not self.generate_invoice_at_period_start else False
-			to_prorate = frappe.db.get_single_value('Subscription Settings', 'prorate')
-			self.status = 'Cancelled'
+		if self.status != "Cancelled":
+			to_generate_invoice = (
+				True if self.status == "Active" and not self.generate_invoice_at_period_start else False
+			)
+			to_prorate = frappe.db.get_single_value("Subscription Settings", "prorate")
+			self.status = "Cancelled"
 			self.cancelation_date = nowdate()
 			if to_generate_invoice:
 				self.generate_invoice(prorate=to_prorate)
@@ -620,19 +660,20 @@
 		subscription and the `Subscription` will lose all the history of generated invoices
 		it has.
 		"""
-		if self.status == 'Cancelled':
-			self.status = 'Active'
-			self.db_set('start_date', nowdate())
+		if self.status == "Cancelled":
+			self.status = "Active"
+			self.db_set("start_date", nowdate())
 			self.update_subscription_period(nowdate())
 			self.invoices = []
 			self.save()
 		else:
-			frappe.throw(_('You cannot restart a Subscription that is not cancelled.'))
+			frappe.throw(_("You cannot restart a Subscription that is not cancelled."))
 
 	def get_precision(self):
 		invoice = self.get_current_invoice()
 		if invoice:
-			return invoice.precision('grand_total')
+			return invoice.precision("grand_total")
+
 
 def get_calendar_months(billing_interval):
 	calendar_months = []
@@ -643,6 +684,7 @@
 
 	return calendar_months
 
+
 def get_prorata_factor(period_end, period_start, is_prepaid):
 	if is_prepaid:
 		prorate_factor = 1
@@ -667,7 +709,7 @@
 	"""
 	Returns all `Subscription` documents
 	"""
-	return frappe.db.get_all('Subscription', {'status': ('!=','Cancelled')})
+	return frappe.db.get_all("Subscription", {"status": ("!=", "Cancelled")})
 
 
 def process(data):
@@ -676,14 +718,12 @@
 	"""
 	if data:
 		try:
-			subscription = frappe.get_doc('Subscription', data['name'])
+			subscription = frappe.get_doc("Subscription", data["name"])
 			subscription.process()
 			frappe.db.commit()
 		except frappe.ValidationError:
 			frappe.db.rollback()
-			frappe.db.begin()
-			frappe.log_error(frappe.get_traceback())
-			frappe.db.commit()
+			subscription.log_error("Subscription failed")
 
 
 @frappe.whitelist()
@@ -692,7 +732,7 @@
 	Cancels a `Subscription`. This will stop the `Subscription` from further invoicing the
 	`Subscriber` but all already outstanding invoices will not be affected.
 	"""
-	subscription = frappe.get_doc('Subscription', name)
+	subscription = frappe.get_doc("Subscription", name)
 	subscription.cancel_subscription()
 
 
@@ -702,7 +742,7 @@
 	Restarts a cancelled `Subscription`. The `Subscription` will 'forget' the history of
 	all invoices it has generated
 	"""
-	subscription = frappe.get_doc('Subscription', name)
+	subscription = frappe.get_doc("Subscription", name)
 	subscription.restart_subscription()
 
 
@@ -711,5 +751,5 @@
 	"""
 	Use this to get the latest state of the given `Subscription`
 	"""
-	subscription = frappe.get_doc('Subscription', name)
+	subscription = frappe.get_doc("Subscription", name)
 	subscription.process()
diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py
index 6f67bc5..eb17daa 100644
--- a/erpnext/accounts/doctype/subscription/test_subscription.py
+++ b/erpnext/accounts/doctype/subscription/test_subscription.py
@@ -18,104 +18,111 @@
 
 test_dependencies = ("UOM", "Item Group", "Item")
 
+
 def create_plan():
-	if not frappe.db.exists('Subscription Plan', '_Test Plan Name'):
-		plan = frappe.new_doc('Subscription Plan')
-		plan.plan_name = '_Test Plan Name'
-		plan.item = '_Test Non Stock Item'
+	if not frappe.db.exists("Subscription Plan", "_Test Plan Name"):
+		plan = frappe.new_doc("Subscription Plan")
+		plan.plan_name = "_Test Plan Name"
+		plan.item = "_Test Non Stock Item"
 		plan.price_determination = "Fixed Rate"
 		plan.cost = 900
-		plan.billing_interval = 'Month'
+		plan.billing_interval = "Month"
 		plan.billing_interval_count = 1
 		plan.insert()
 
-	if not frappe.db.exists('Subscription Plan', '_Test Plan Name 2'):
-		plan = frappe.new_doc('Subscription Plan')
-		plan.plan_name = '_Test Plan Name 2'
-		plan.item = '_Test Non Stock Item'
+	if not frappe.db.exists("Subscription Plan", "_Test Plan Name 2"):
+		plan = frappe.new_doc("Subscription Plan")
+		plan.plan_name = "_Test Plan Name 2"
+		plan.item = "_Test Non Stock Item"
 		plan.price_determination = "Fixed Rate"
 		plan.cost = 1999
-		plan.billing_interval = 'Month'
+		plan.billing_interval = "Month"
 		plan.billing_interval_count = 1
 		plan.insert()
 
-	if not frappe.db.exists('Subscription Plan', '_Test Plan Name 3'):
-		plan = frappe.new_doc('Subscription Plan')
-		plan.plan_name = '_Test Plan Name 3'
-		plan.item = '_Test Non Stock Item'
+	if not frappe.db.exists("Subscription Plan", "_Test Plan Name 3"):
+		plan = frappe.new_doc("Subscription Plan")
+		plan.plan_name = "_Test Plan Name 3"
+		plan.item = "_Test Non Stock Item"
 		plan.price_determination = "Fixed Rate"
 		plan.cost = 1999
-		plan.billing_interval = 'Day'
+		plan.billing_interval = "Day"
 		plan.billing_interval_count = 14
 		plan.insert()
 
 	# Defined a quarterly Subscription Plan
-	if not frappe.db.exists('Subscription Plan', '_Test Plan Name 4'):
-		plan = frappe.new_doc('Subscription Plan')
-		plan.plan_name = '_Test Plan Name 4'
-		plan.item = '_Test Non Stock Item'
+	if not frappe.db.exists("Subscription Plan", "_Test Plan Name 4"):
+		plan = frappe.new_doc("Subscription Plan")
+		plan.plan_name = "_Test Plan Name 4"
+		plan.item = "_Test Non Stock Item"
 		plan.price_determination = "Monthly Rate"
 		plan.cost = 20000
-		plan.billing_interval = 'Month'
+		plan.billing_interval = "Month"
 		plan.billing_interval_count = 3
 		plan.insert()
 
-	if not frappe.db.exists('Subscription Plan', '_Test Plan Multicurrency'):
-		plan = frappe.new_doc('Subscription Plan')
-		plan.plan_name = '_Test Plan Multicurrency'
-		plan.item = '_Test Non Stock Item'
+	if not frappe.db.exists("Subscription Plan", "_Test Plan Multicurrency"):
+		plan = frappe.new_doc("Subscription Plan")
+		plan.plan_name = "_Test Plan Multicurrency"
+		plan.item = "_Test Non Stock Item"
 		plan.price_determination = "Fixed Rate"
 		plan.cost = 50
-		plan.currency = 'USD'
-		plan.billing_interval = 'Month'
+		plan.currency = "USD"
+		plan.billing_interval = "Month"
 		plan.billing_interval_count = 1
 		plan.insert()
 
+
 def create_parties():
-	if not frappe.db.exists('Supplier', '_Test Supplier'):
-		supplier = frappe.new_doc('Supplier')
-		supplier.supplier_name = '_Test Supplier'
-		supplier.supplier_group = 'All Supplier Groups'
+	if not frappe.db.exists("Supplier", "_Test Supplier"):
+		supplier = frappe.new_doc("Supplier")
+		supplier.supplier_name = "_Test Supplier"
+		supplier.supplier_group = "All Supplier Groups"
 		supplier.insert()
 
-	if not frappe.db.exists('Customer', '_Test Subscription Customer'):
-		customer = frappe.new_doc('Customer')
-		customer.customer_name = '_Test Subscription Customer'
-		customer.billing_currency = 'USD'
-		customer.append('accounts', {
-			'company': '_Test Company',
-			'account': '_Test Receivable USD - _TC'
-		})
+	if not frappe.db.exists("Customer", "_Test Subscription Customer"):
+		customer = frappe.new_doc("Customer")
+		customer.customer_name = "_Test Subscription Customer"
+		customer.billing_currency = "USD"
+		customer.append(
+			"accounts", {"company": "_Test Company", "account": "_Test Receivable USD - _TC"}
+		)
 		customer.insert()
 
+
 class TestSubscription(unittest.TestCase):
 	def setUp(self):
 		create_plan()
 		create_parties()
 
 	def test_create_subscription_with_trial_with_correct_period(self):
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
 		subscription.trial_period_start = nowdate()
 		subscription.trial_period_end = add_months(nowdate(), 1)
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
 		subscription.save()
 
 		self.assertEqual(subscription.trial_period_start, nowdate())
 		self.assertEqual(subscription.trial_period_end, add_months(nowdate(), 1))
-		self.assertEqual(add_days(subscription.trial_period_end, 1), get_date_str(subscription.current_invoice_start))
-		self.assertEqual(add_to_date(subscription.current_invoice_start, months=1, days=-1), get_date_str(subscription.current_invoice_end))
+		self.assertEqual(
+			add_days(subscription.trial_period_end, 1), get_date_str(subscription.current_invoice_start)
+		)
+		self.assertEqual(
+			add_to_date(subscription.current_invoice_start, months=1, days=-1),
+			get_date_str(subscription.current_invoice_end),
+		)
 		self.assertEqual(subscription.invoices, [])
-		self.assertEqual(subscription.status, 'Trialling')
+		self.assertEqual(subscription.status, "Trialling")
 
 		subscription.delete()
 
 	def test_create_subscription_without_trial_with_correct_period(self):
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
 		subscription.save()
 
 		self.assertEqual(subscription.trial_period_start, None)
@@ -124,190 +131,190 @@
 		self.assertEqual(subscription.current_invoice_end, add_to_date(nowdate(), months=1, days=-1))
 		# No invoice is created
 		self.assertEqual(len(subscription.invoices), 0)
-		self.assertEqual(subscription.status, 'Active')
+		self.assertEqual(subscription.status, "Active")
 
 		subscription.delete()
 
 	def test_create_subscription_trial_with_wrong_dates(self):
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
 		subscription.trial_period_end = nowdate()
 		subscription.trial_period_start = add_days(nowdate(), 30)
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
 
 		self.assertRaises(frappe.ValidationError, subscription.save)
 		subscription.delete()
 
 	def test_create_subscription_multi_with_different_billing_fails(self):
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
 		subscription.trial_period_end = nowdate()
 		subscription.trial_period_start = add_days(nowdate(), 30)
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
-		subscription.append('plans', {'plan': '_Test Plan Name 3', 'qty': 1})
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
+		subscription.append("plans", {"plan": "_Test Plan Name 3", "qty": 1})
 
 		self.assertRaises(frappe.ValidationError, subscription.save)
 		subscription.delete()
 
 	def test_invoice_is_generated_at_end_of_billing_period(self):
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
-		subscription.start_date = '2018-01-01'
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
+		subscription.start_date = "2018-01-01"
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
 		subscription.insert()
 
-		self.assertEqual(subscription.status, 'Active')
-		self.assertEqual(subscription.current_invoice_start, '2018-01-01')
-		self.assertEqual(subscription.current_invoice_end, '2018-01-31')
+		self.assertEqual(subscription.status, "Active")
+		self.assertEqual(subscription.current_invoice_start, "2018-01-01")
+		self.assertEqual(subscription.current_invoice_end, "2018-01-31")
 		subscription.process()
 
 		self.assertEqual(len(subscription.invoices), 1)
-		self.assertEqual(subscription.current_invoice_start, '2018-01-01')
+		self.assertEqual(subscription.current_invoice_start, "2018-01-01")
 		subscription.process()
-		self.assertEqual(subscription.status, 'Unpaid')
+		self.assertEqual(subscription.status, "Unpaid")
 		subscription.delete()
 
 	def test_status_goes_back_to_active_after_invoice_is_paid(self):
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
-		subscription.start_date = '2018-01-01'
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
+		subscription.start_date = "2018-01-01"
 		subscription.insert()
-		subscription.process()	# generate first invoice
+		subscription.process()  # generate first invoice
 		self.assertEqual(len(subscription.invoices), 1)
 
 		# Status is unpaid as Days until Due is zero and grace period is Zero
-		self.assertEqual(subscription.status, 'Unpaid')
+		self.assertEqual(subscription.status, "Unpaid")
 
 		subscription.get_current_invoice()
 		current_invoice = subscription.get_current_invoice()
 
 		self.assertIsNotNone(current_invoice)
 
-		current_invoice.db_set('outstanding_amount', 0)
-		current_invoice.db_set('status', 'Paid')
+		current_invoice.db_set("outstanding_amount", 0)
+		current_invoice.db_set("status", "Paid")
 		subscription.process()
 
-		self.assertEqual(subscription.status, 'Active')
+		self.assertEqual(subscription.status, "Active")
 		self.assertEqual(subscription.current_invoice_start, add_months(subscription.start_date, 1))
 		self.assertEqual(len(subscription.invoices), 1)
 
 		subscription.delete()
 
 	def test_subscription_cancel_after_grace_period(self):
-		settings = frappe.get_single('Subscription Settings')
+		settings = frappe.get_single("Subscription Settings")
 		default_grace_period_action = settings.cancel_after_grace
 		settings.cancel_after_grace = 1
 		settings.save()
 
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
-		subscription.start_date = '2018-01-01'
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
+		subscription.start_date = "2018-01-01"
 		subscription.insert()
 
-		self.assertEqual(subscription.status, 'Active')
+		self.assertEqual(subscription.status, "Active")
 
-		subscription.process()		# generate first invoice
+		subscription.process()  # generate first invoice
 		# This should change status to Cancelled since grace period is 0
 		# And is backdated subscription so subscription will be cancelled after processing
-		self.assertEqual(subscription.status, 'Cancelled')
+		self.assertEqual(subscription.status, "Cancelled")
 
 		settings.cancel_after_grace = default_grace_period_action
 		settings.save()
 		subscription.delete()
 
 	def test_subscription_unpaid_after_grace_period(self):
-		settings = frappe.get_single('Subscription Settings')
+		settings = frappe.get_single("Subscription Settings")
 		default_grace_period_action = settings.cancel_after_grace
 		settings.cancel_after_grace = 0
 		settings.save()
 
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
-		subscription.start_date = '2018-01-01'
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
+		subscription.start_date = "2018-01-01"
 		subscription.insert()
-		subscription.process()		# generate first invoice
+		subscription.process()  # generate first invoice
 
 		# Status is unpaid as Days until Due is zero and grace period is Zero
-		self.assertEqual(subscription.status, 'Unpaid')
+		self.assertEqual(subscription.status, "Unpaid")
 
 		settings.cancel_after_grace = default_grace_period_action
 		settings.save()
 		subscription.delete()
 
 	def test_subscription_invoice_days_until_due(self):
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
 		subscription.days_until_due = 10
 		subscription.start_date = add_months(nowdate(), -1)
 		subscription.insert()
-		subscription.process()		# generate first invoice
+		subscription.process()  # generate first invoice
 		self.assertEqual(len(subscription.invoices), 1)
-		self.assertEqual(subscription.status, 'Active')
+		self.assertEqual(subscription.status, "Active")
 
 		subscription.delete()
 
 	def test_subscription_is_past_due_doesnt_change_within_grace_period(self):
-		settings = frappe.get_single('Subscription Settings')
+		settings = frappe.get_single("Subscription Settings")
 		grace_period = settings.grace_period
 		settings.grace_period = 1000
 		settings.save()
 
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
 		subscription.start_date = add_days(nowdate(), -1000)
 		subscription.insert()
-		subscription.process()		# generate first invoice
+		subscription.process()  # generate first invoice
 
-		self.assertEqual(subscription.status, 'Past Due Date')
+		self.assertEqual(subscription.status, "Past Due Date")
 
 		subscription.process()
 		# Grace period is 1000 days so status should remain as Past Due Date
-		self.assertEqual(subscription.status, 'Past Due Date')
+		self.assertEqual(subscription.status, "Past Due Date")
 
 		subscription.process()
-		self.assertEqual(subscription.status, 'Past Due Date')
+		self.assertEqual(subscription.status, "Past Due Date")
 
 		subscription.process()
-		self.assertEqual(subscription.status, 'Past Due Date')
+		self.assertEqual(subscription.status, "Past Due Date")
 
 		settings.grace_period = grace_period
 		settings.save()
 		subscription.delete()
 
 	def test_subscription_remains_active_during_invoice_period(self):
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
 		subscription.save()
-		subscription.process()		# no changes expected
+		subscription.process()  # no changes expected
 
-		self.assertEqual(subscription.status, 'Active')
+		self.assertEqual(subscription.status, "Active")
 		self.assertEqual(subscription.current_invoice_start, nowdate())
 		self.assertEqual(subscription.current_invoice_end, add_to_date(nowdate(), months=1, days=-1))
 		self.assertEqual(len(subscription.invoices), 0)
 
-		subscription.process()		# no changes expected still
-		self.assertEqual(subscription.status, 'Active')
+		subscription.process()  # no changes expected still
+		self.assertEqual(subscription.status, "Active")
 		self.assertEqual(subscription.current_invoice_start, nowdate())
 		self.assertEqual(subscription.current_invoice_end, add_to_date(nowdate(), months=1, days=-1))
 		self.assertEqual(len(subscription.invoices), 0)
 
-		subscription.process()		# no changes expected yet still
-		self.assertEqual(subscription.status, 'Active')
+		subscription.process()  # no changes expected yet still
+		self.assertEqual(subscription.status, "Active")
 		self.assertEqual(subscription.current_invoice_start, nowdate())
 		self.assertEqual(subscription.current_invoice_end, add_to_date(nowdate(), months=1, days=-1))
 		self.assertEqual(len(subscription.invoices), 0)
@@ -315,30 +322,30 @@
 		subscription.delete()
 
 	def test_subscription_cancelation(self):
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
 		subscription.save()
 		subscription.cancel_subscription()
 
-		self.assertEqual(subscription.status, 'Cancelled')
+		self.assertEqual(subscription.status, "Cancelled")
 
 		subscription.delete()
 
 	def test_subscription_cancellation_invoices(self):
-		settings = frappe.get_single('Subscription Settings')
+		settings = frappe.get_single("Subscription Settings")
 		to_prorate = settings.prorate
 		settings.prorate = 1
 		settings.save()
 
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
 		subscription.save()
 
-		self.assertEqual(subscription.status, 'Active')
+		self.assertEqual(subscription.status, "Active")
 
 		subscription.cancel_subscription()
 		# Invoice must have been generated
@@ -346,33 +353,39 @@
 
 		invoice = subscription.get_current_invoice()
 		diff = flt(date_diff(nowdate(), subscription.current_invoice_start) + 1)
-		plan_days = flt(date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1)
-		prorate_factor = flt(diff/plan_days)
+		plan_days = flt(
+			date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1
+		)
+		prorate_factor = flt(diff / plan_days)
 
 		self.assertEqual(
 			flt(
-				get_prorata_factor(subscription.current_invoice_end, subscription.current_invoice_start,
-					subscription.generate_invoice_at_period_start),
-				2),
-			flt(prorate_factor, 2)
+				get_prorata_factor(
+					subscription.current_invoice_end,
+					subscription.current_invoice_start,
+					subscription.generate_invoice_at_period_start,
+				),
+				2,
+			),
+			flt(prorate_factor, 2),
 		)
 		self.assertEqual(flt(invoice.grand_total, 2), flt(prorate_factor * 900, 2))
-		self.assertEqual(subscription.status, 'Cancelled')
+		self.assertEqual(subscription.status, "Cancelled")
 
 		subscription.delete()
 		settings.prorate = to_prorate
 		settings.save()
 
 	def test_subscription_cancellation_invoices_with_prorata_false(self):
-		settings = frappe.get_single('Subscription Settings')
+		settings = frappe.get_single("Subscription Settings")
 		to_prorate = settings.prorate
 		settings.prorate = 0
 		settings.save()
 
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
 		subscription.save()
 		subscription.cancel_subscription()
 		invoice = subscription.get_current_invoice()
@@ -385,21 +398,23 @@
 		subscription.delete()
 
 	def test_subscription_cancellation_invoices_with_prorata_true(self):
-		settings = frappe.get_single('Subscription Settings')
+		settings = frappe.get_single("Subscription Settings")
 		to_prorate = settings.prorate
 		settings.prorate = 1
 		settings.save()
 
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
 		subscription.save()
 		subscription.cancel_subscription()
 
 		invoice = subscription.get_current_invoice()
 		diff = flt(date_diff(nowdate(), subscription.current_invoice_start) + 1)
-		plan_days = flt(date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1)
+		plan_days = flt(
+			date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1
+		)
 		prorate_factor = flt(diff / plan_days)
 
 		self.assertEqual(flt(invoice.grand_total, 2), flt(prorate_factor * 900, 2))
@@ -410,30 +425,30 @@
 		subscription.delete()
 
 	def test_subcription_cancellation_and_process(self):
-		settings = frappe.get_single('Subscription Settings')
+		settings = frappe.get_single("Subscription Settings")
 		default_grace_period_action = settings.cancel_after_grace
 		settings.cancel_after_grace = 1
 		settings.save()
 
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
-		subscription.start_date = '2018-01-01'
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
+		subscription.start_date = "2018-01-01"
 		subscription.insert()
-		subscription.process()	# generate first invoice
+		subscription.process()  # generate first invoice
 		invoices = len(subscription.invoices)
 
 		subscription.cancel_subscription()
-		self.assertEqual(subscription.status, 'Cancelled')
+		self.assertEqual(subscription.status, "Cancelled")
 		self.assertEqual(len(subscription.invoices), invoices)
 
 		subscription.process()
-		self.assertEqual(subscription.status, 'Cancelled')
+		self.assertEqual(subscription.status, "Cancelled")
 		self.assertEqual(len(subscription.invoices), invoices)
 
 		subscription.process()
-		self.assertEqual(subscription.status, 'Cancelled')
+		self.assertEqual(subscription.status, "Cancelled")
 		self.assertEqual(len(subscription.invoices), invoices)
 
 		settings.cancel_after_grace = default_grace_period_action
@@ -441,36 +456,36 @@
 		subscription.delete()
 
 	def test_subscription_restart_and_process(self):
-		settings = frappe.get_single('Subscription Settings')
+		settings = frappe.get_single("Subscription Settings")
 		default_grace_period_action = settings.cancel_after_grace
 		settings.grace_period = 0
 		settings.cancel_after_grace = 0
 		settings.save()
 
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
-		subscription.start_date = '2018-01-01'
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
+		subscription.start_date = "2018-01-01"
 		subscription.insert()
-		subscription.process()		# generate first invoice
+		subscription.process()  # generate first invoice
 
 		# Status is unpaid as Days until Due is zero and grace period is Zero
-		self.assertEqual(subscription.status, 'Unpaid')
+		self.assertEqual(subscription.status, "Unpaid")
 
 		subscription.cancel_subscription()
-		self.assertEqual(subscription.status, 'Cancelled')
+		self.assertEqual(subscription.status, "Cancelled")
 
 		subscription.restart_subscription()
-		self.assertEqual(subscription.status, 'Active')
+		self.assertEqual(subscription.status, "Active")
 		self.assertEqual(len(subscription.invoices), 0)
 
 		subscription.process()
-		self.assertEqual(subscription.status, 'Active')
+		self.assertEqual(subscription.status, "Active")
 		self.assertEqual(len(subscription.invoices), 0)
 
 		subscription.process()
-		self.assertEqual(subscription.status, 'Active')
+		self.assertEqual(subscription.status, "Active")
 		self.assertEqual(len(subscription.invoices), 0)
 
 		settings.cancel_after_grace = default_grace_period_action
@@ -478,42 +493,42 @@
 		subscription.delete()
 
 	def test_subscription_unpaid_back_to_active(self):
-		settings = frappe.get_single('Subscription Settings')
+		settings = frappe.get_single("Subscription Settings")
 		default_grace_period_action = settings.cancel_after_grace
 		settings.cancel_after_grace = 0
 		settings.save()
 
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
-		subscription.start_date = '2018-01-01'
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
+		subscription.start_date = "2018-01-01"
 		subscription.insert()
 
-		subscription.process()		# generate first invoice
+		subscription.process()  # generate first invoice
 		# This should change status to Unpaid since grace period is 0
-		self.assertEqual(subscription.status, 'Unpaid')
+		self.assertEqual(subscription.status, "Unpaid")
 
 		invoice = subscription.get_current_invoice()
-		invoice.db_set('outstanding_amount', 0)
-		invoice.db_set('status', 'Paid')
+		invoice.db_set("outstanding_amount", 0)
+		invoice.db_set("status", "Paid")
 
 		subscription.process()
-		self.assertEqual(subscription.status, 'Active')
+		self.assertEqual(subscription.status, "Active")
 
 		# A new invoice is generated
 		subscription.process()
-		self.assertEqual(subscription.status, 'Unpaid')
+		self.assertEqual(subscription.status, "Unpaid")
 
 		settings.cancel_after_grace = default_grace_period_action
 		settings.save()
 		subscription.delete()
 
 	def test_restart_active_subscription(self):
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
 		subscription.save()
 
 		self.assertRaises(frappe.ValidationError, subscription.restart_subscription)
@@ -521,44 +536,44 @@
 		subscription.delete()
 
 	def test_subscription_invoice_discount_percentage(self):
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
 		subscription.additional_discount_percentage = 10
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
 		subscription.save()
 		subscription.cancel_subscription()
 
 		invoice = subscription.get_current_invoice()
 
 		self.assertEqual(invoice.additional_discount_percentage, 10)
-		self.assertEqual(invoice.apply_discount_on, 'Grand Total')
+		self.assertEqual(invoice.apply_discount_on, "Grand Total")
 
 		subscription.delete()
 
 	def test_subscription_invoice_discount_amount(self):
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
 		subscription.additional_discount_amount = 11
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
 		subscription.save()
 		subscription.cancel_subscription()
 
 		invoice = subscription.get_current_invoice()
 
 		self.assertEqual(invoice.discount_amount, 11)
-		self.assertEqual(invoice.apply_discount_on, 'Grand Total')
+		self.assertEqual(invoice.apply_discount_on, "Grand Total")
 
 		subscription.delete()
 
 	def test_prepaid_subscriptions(self):
 		# Create a non pre-billed subscription, processing should not create
 		# invoices.
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
 		subscription.save()
 		subscription.process()
 
@@ -573,16 +588,16 @@
 		self.assertEqual(len(subscription.invoices), 1)
 
 	def test_prepaid_subscriptions_with_prorate_true(self):
-		settings = frappe.get_single('Subscription Settings')
+		settings = frappe.get_single("Subscription Settings")
 		to_prorate = settings.prorate
 		settings.prorate = 1
 		settings.save()
 
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Customer'
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Customer"
 		subscription.generate_invoice_at_period_start = True
-		subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
+		subscription.append("plans", {"plan": "_Test Plan Name", "qty": 1})
 		subscription.save()
 		subscription.process()
 		subscription.cancel_subscription()
@@ -602,38 +617,38 @@
 		subscription.delete()
 
 	def test_subscription_with_follow_calendar_months(self):
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Supplier'
-		subscription.party = '_Test Supplier'
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Supplier"
+		subscription.party = "_Test Supplier"
 		subscription.generate_invoice_at_period_start = 1
 		subscription.follow_calendar_months = 1
 
 		# select subscription start date as '2018-01-15'
-		subscription.start_date = '2018-01-15'
-		subscription.end_date = '2018-07-15'
-		subscription.append('plans', {'plan': '_Test Plan Name 4', 'qty': 1})
+		subscription.start_date = "2018-01-15"
+		subscription.end_date = "2018-07-15"
+		subscription.append("plans", {"plan": "_Test Plan Name 4", "qty": 1})
 		subscription.save()
 
 		# even though subscription starts at '2018-01-15' and Billing interval is Month and count 3
 		# First invoice will end at '2018-03-31' instead of '2018-04-14'
-		self.assertEqual(get_date_str(subscription.current_invoice_end), '2018-03-31')
+		self.assertEqual(get_date_str(subscription.current_invoice_end), "2018-03-31")
 
 	def test_subscription_generate_invoice_past_due(self):
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Supplier'
-		subscription.party = '_Test Supplier'
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Supplier"
+		subscription.party = "_Test Supplier"
 		subscription.generate_invoice_at_period_start = 1
 		subscription.generate_new_invoices_past_due_date = 1
 		# select subscription start date as '2018-01-15'
-		subscription.start_date = '2018-01-01'
-		subscription.append('plans', {'plan': '_Test Plan Name 4', 'qty': 1})
+		subscription.start_date = "2018-01-01"
+		subscription.append("plans", {"plan": "_Test Plan Name 4", "qty": 1})
 		subscription.save()
 
 		# Process subscription and create first invoice
 		# Subscription status will be unpaid since due date has already passed
 		subscription.process()
 		self.assertEqual(len(subscription.invoices), 1)
-		self.assertEqual(subscription.status, 'Unpaid')
+		self.assertEqual(subscription.status, "Unpaid")
 
 		# Now the Subscription is unpaid
 		# Even then new invoice should be created as we have enabled `generate_new_invoices_past_due_date` in
@@ -643,39 +658,39 @@
 		self.assertEqual(len(subscription.invoices), 2)
 
 	def test_subscription_without_generate_invoice_past_due(self):
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Supplier'
-		subscription.party = '_Test Supplier'
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Supplier"
+		subscription.party = "_Test Supplier"
 		subscription.generate_invoice_at_period_start = 1
 		# select subscription start date as '2018-01-15'
-		subscription.start_date = '2018-01-01'
-		subscription.append('plans', {'plan': '_Test Plan Name 4', 'qty': 1})
+		subscription.start_date = "2018-01-01"
+		subscription.append("plans", {"plan": "_Test Plan Name 4", "qty": 1})
 		subscription.save()
 
 		# Process subscription and create first invoice
 		# Subscription status will be unpaid since due date has already passed
 		subscription.process()
 		self.assertEqual(len(subscription.invoices), 1)
-		self.assertEqual(subscription.status, 'Unpaid')
+		self.assertEqual(subscription.status, "Unpaid")
 
 		subscription.process()
 		self.assertEqual(len(subscription.invoices), 1)
 
 	def test_multicurrency_subscription(self):
-		subscription = frappe.new_doc('Subscription')
-		subscription.party_type = 'Customer'
-		subscription.party = '_Test Subscription Customer'
+		subscription = frappe.new_doc("Subscription")
+		subscription.party_type = "Customer"
+		subscription.party = "_Test Subscription Customer"
 		subscription.generate_invoice_at_period_start = 1
-		subscription.company = '_Test Company'
+		subscription.company = "_Test Company"
 		# select subscription start date as '2018-01-15'
-		subscription.start_date = '2018-01-01'
-		subscription.append('plans', {'plan': '_Test Plan Multicurrency', 'qty': 1})
+		subscription.start_date = "2018-01-01"
+		subscription.append("plans", {"plan": "_Test Plan Multicurrency", "qty": 1})
 		subscription.save()
 
 		subscription.process()
 		self.assertEqual(len(subscription.invoices), 1)
-		self.assertEqual(subscription.status, 'Unpaid')
+		self.assertEqual(subscription.status, "Unpaid")
 
 		# Check the currency of the created invoice
-		currency = frappe.db.get_value('Sales Invoice', subscription.invoices[0].invoice, 'currency')
-		self.assertEqual(currency, 'USD')
\ No newline at end of file
+		currency = frappe.db.get_value("Sales Invoice", subscription.invoices[0].invoice, "currency")
+		self.assertEqual(currency, "USD")
diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py
index 1285343..a95e0a9 100644
--- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py
+++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py
@@ -16,10 +16,13 @@
 
 	def validate_interval_count(self):
 		if self.billing_interval_count < 1:
-			frappe.throw(_('Billing Interval Count cannot be less than 1'))
+			frappe.throw(_("Billing Interval Count cannot be less than 1"))
+
 
 @frappe.whitelist()
-def get_plan_rate(plan, quantity=1, customer=None, start_date=None, end_date=None, prorate_factor=1):
+def get_plan_rate(
+	plan, quantity=1, customer=None, start_date=None, end_date=None, prorate_factor=1
+):
 	plan = frappe.get_doc("Subscription Plan", plan)
 	if plan.price_determination == "Fixed Rate":
 		return plan.cost * prorate_factor
@@ -30,13 +33,19 @@
 		else:
 			customer_group = None
 
-		price = get_price(item_code=plan.item, price_list=plan.price_list, customer_group=customer_group, company=None, qty=quantity)
+		price = get_price(
+			item_code=plan.item,
+			price_list=plan.price_list,
+			customer_group=customer_group,
+			company=None,
+			qty=quantity,
+		)
 		if not price:
 			return 0
 		else:
 			return price.price_list_rate * prorate_factor
 
-	elif plan.price_determination == 'Monthly Rate':
+	elif plan.price_determination == "Monthly Rate":
 		start_date = getdate(start_date)
 		end_date = getdate(end_date)
 
@@ -44,15 +53,21 @@
 		cost = plan.cost * no_of_months
 
 		# Adjust cost if start or end date is not month start or end
-		prorate = frappe.db.get_single_value('Subscription Settings', 'prorate')
+		prorate = frappe.db.get_single_value("Subscription Settings", "prorate")
 
 		if prorate:
-			prorate_factor = flt(date_diff(start_date, get_first_day(start_date)) / date_diff(
-				get_last_day(start_date), get_first_day(start_date)), 1)
+			prorate_factor = flt(
+				date_diff(start_date, get_first_day(start_date))
+				/ date_diff(get_last_day(start_date), get_first_day(start_date)),
+				1,
+			)
 
-			prorate_factor += flt(date_diff(get_last_day(end_date), end_date) / date_diff(
-				get_last_day(end_date), get_first_day(end_date)), 1)
+			prorate_factor += flt(
+				date_diff(get_last_day(end_date), end_date)
+				/ date_diff(get_last_day(end_date), get_first_day(end_date)),
+				1,
+			)
 
-			cost -= (plan.cost * prorate_factor)
+			cost -= plan.cost * prorate_factor
 
 		return cost
diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan_dashboard.py b/erpnext/accounts/doctype/subscription_plan/subscription_plan_dashboard.py
index d076e39..7df76cd 100644
--- a/erpnext/accounts/doctype/subscription_plan/subscription_plan_dashboard.py
+++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan_dashboard.py
@@ -3,15 +3,7 @@
 
 def get_data():
 	return {
-		'fieldname': 'subscription_plan',
-		'non_standard_fieldnames': {
-			'Payment Request': 'plan',
-			'Subscription': 'plan'
-		},
-		'transactions': [
-			{
-				'label': _('References'),
-				'items': ['Payment Request', 'Subscription']
-			}
-		]
+		"fieldname": "subscription_plan",
+		"non_standard_fieldnames": {"Payment Request": "plan", "Subscription": "plan"},
+		"transactions": [{"label": _("References"), "items": ["Payment Request", "Subscription"]}],
 	}
diff --git a/erpnext/accounts/doctype/tax_category/tax_category_dashboard.py b/erpnext/accounts/doctype/tax_category/tax_category_dashboard.py
index 4bdb70a..17a275e 100644
--- a/erpnext/accounts/doctype/tax_category/tax_category_dashboard.py
+++ b/erpnext/accounts/doctype/tax_category/tax_category_dashboard.py
@@ -3,27 +3,12 @@
 
 def get_data():
 	return {
-		'fieldname': 'tax_category',
-		'transactions': [
-			{
-				'label': _('Pre Sales'),
-				'items': ['Quotation', 'Supplier Quotation']
-			},
-			{
-				'label': _('Sales'),
-				'items': ['Sales Invoice', 'Delivery Note', 'Sales Order']
-			},
-			{
-				'label': _('Purchase'),
-				'items': ['Purchase Invoice', 'Purchase Receipt']
-			},
-			{
-				'label': _('Party'),
-				'items': ['Customer', 'Supplier']
-			},
-			{
-				'label': _('Taxes'),
-				'items': ['Item', 'Tax Rule']
-			}
-		]
+		"fieldname": "tax_category",
+		"transactions": [
+			{"label": _("Pre Sales"), "items": ["Quotation", "Supplier Quotation"]},
+			{"label": _("Sales"), "items": ["Sales Invoice", "Delivery Note", "Sales Order"]},
+			{"label": _("Purchase"), "items": ["Purchase Invoice", "Purchase Receipt"]},
+			{"label": _("Party"), "items": ["Customer", "Supplier"]},
+			{"label": _("Taxes"), "items": ["Item", "Tax Rule"]},
+		],
 	}
diff --git a/erpnext/accounts/doctype/tax_rule/tax_rule.py b/erpnext/accounts/doctype/tax_rule/tax_rule.py
index 2d94bc3..4d20129 100644
--- a/erpnext/accounts/doctype/tax_rule/tax_rule.py
+++ b/erpnext/accounts/doctype/tax_rule/tax_rule.py
@@ -14,9 +14,17 @@
 from erpnext.setup.doctype.customer_group.customer_group import get_parent_customer_groups
 
 
-class IncorrectCustomerGroup(frappe.ValidationError): pass
-class IncorrectSupplierType(frappe.ValidationError): pass
-class ConflictingTaxRule(frappe.ValidationError): pass
+class IncorrectCustomerGroup(frappe.ValidationError):
+	pass
+
+
+class IncorrectSupplierType(frappe.ValidationError):
+	pass
+
+
+class ConflictingTaxRule(frappe.ValidationError):
+	pass
+
 
 class TaxRule(Document):
 	def __setup__(self):
@@ -29,7 +37,7 @@
 		self.validate_use_for_shopping_cart()
 
 	def validate_tax_template(self):
-		if self.tax_type== "Sales":
+		if self.tax_type == "Sales":
 			self.purchase_tax_template = self.supplier = self.supplier_group = None
 			if self.customer:
 				self.customer_group = None
@@ -49,28 +57,28 @@
 
 	def validate_filters(self):
 		filters = {
-			"tax_type":			self.tax_type,
-			"customer": 		self.customer,
-			"customer_group": 	self.customer_group,
-			"supplier":			self.supplier,
-			"supplier_group":	self.supplier_group,
-			"item":				self.item,
-			"item_group":		self.item_group,
-			"billing_city":		self.billing_city,
-			"billing_county":	self.billing_county,
-			"billing_state": 	self.billing_state,
-			"billing_zipcode":	self.billing_zipcode,
-			"billing_country":	self.billing_country,
-			"shipping_city":	self.shipping_city,
-			"shipping_county":	self.shipping_county,
-			"shipping_state":	self.shipping_state,
-			"shipping_zipcode":	self.shipping_zipcode,
-			"shipping_country":	self.shipping_country,
-			"tax_category":		self.tax_category,
-			"company":			self.company
+			"tax_type": self.tax_type,
+			"customer": self.customer,
+			"customer_group": self.customer_group,
+			"supplier": self.supplier,
+			"supplier_group": self.supplier_group,
+			"item": self.item,
+			"item_group": self.item_group,
+			"billing_city": self.billing_city,
+			"billing_county": self.billing_county,
+			"billing_state": self.billing_state,
+			"billing_zipcode": self.billing_zipcode,
+			"billing_country": self.billing_country,
+			"shipping_city": self.shipping_city,
+			"shipping_county": self.shipping_county,
+			"shipping_state": self.shipping_state,
+			"shipping_zipcode": self.shipping_zipcode,
+			"shipping_country": self.shipping_country,
+			"tax_category": self.tax_category,
+			"company": self.company,
 		}
 
-		conds=""
+		conds = ""
 		for d in filters:
 			if conds:
 				conds += " and "
@@ -80,85 +88,115 @@
 			conds += """ and ((from_date > '{from_date}' and from_date < '{to_date}') or
 					(to_date > '{from_date}' and to_date < '{to_date}') or
 					('{from_date}' > from_date and '{from_date}' < to_date) or
-					('{from_date}' = from_date and '{to_date}' = to_date))""".format(from_date=self.from_date, to_date=self.to_date)
+					('{from_date}' = from_date and '{to_date}' = to_date))""".format(
+				from_date=self.from_date, to_date=self.to_date
+			)
 
 		elif self.from_date and not self.to_date:
-			conds += """ and to_date > '{from_date}'""".format(from_date = self.from_date)
+			conds += """ and to_date > '{from_date}'""".format(from_date=self.from_date)
 
 		elif self.to_date and not self.from_date:
-			conds += """ and from_date < '{to_date}'""".format(to_date = self.to_date)
+			conds += """ and from_date < '{to_date}'""".format(to_date=self.to_date)
 
-		tax_rule = frappe.db.sql("select name, priority \
-			from `tabTax Rule` where {0} and name != '{1}'".format(conds, self.name), as_dict=1)
+		tax_rule = frappe.db.sql(
+			"select name, priority \
+			from `tabTax Rule` where {0} and name != '{1}'".format(
+				conds, self.name
+			),
+			as_dict=1,
+		)
 
 		if tax_rule:
 			if tax_rule[0].priority == self.priority:
 				frappe.throw(_("Tax Rule Conflicts with {0}").format(tax_rule[0].name), ConflictingTaxRule)
 
 	def validate_use_for_shopping_cart(self):
-		'''If shopping cart is enabled and no tax rule exists for shopping cart, enable this one'''
-		if (not self.use_for_shopping_cart
-			and cint(frappe.db.get_single_value('E Commerce Settings', 'enabled'))
-			and not frappe.db.get_value('Tax Rule', {'use_for_shopping_cart': 1, 'name': ['!=', self.name]})):
+		"""If shopping cart is enabled and no tax rule exists for shopping cart, enable this one"""
+		if (
+			not self.use_for_shopping_cart
+			and cint(frappe.db.get_single_value("E Commerce Settings", "enabled"))
+			and not frappe.db.get_value("Tax Rule", {"use_for_shopping_cart": 1, "name": ["!=", self.name]})
+		):
 
 			self.use_for_shopping_cart = 1
-			frappe.msgprint(_("Enabling 'Use for Shopping Cart', as Shopping Cart is enabled and there should be at least one Tax Rule for Shopping Cart"))
+			frappe.msgprint(
+				_(
+					"Enabling 'Use for Shopping Cart', as Shopping Cart is enabled and there should be at least one Tax Rule for Shopping Cart"
+				)
+			)
+
 
 @frappe.whitelist()
 def get_party_details(party, party_type, args=None):
 	out = {}
 	billing_address, shipping_address = None, None
 	if args:
-		if args.get('billing_address'):
-			billing_address = frappe.get_doc('Address', args.get('billing_address'))
-		if args.get('shipping_address'):
-			shipping_address = frappe.get_doc('Address', args.get('shipping_address'))
+		if args.get("billing_address"):
+			billing_address = frappe.get_doc("Address", args.get("billing_address"))
+		if args.get("shipping_address"):
+			shipping_address = frappe.get_doc("Address", args.get("shipping_address"))
 	else:
 		billing_address_name = get_default_address(party_type, party)
-		shipping_address_name = get_default_address(party_type, party, 'is_shipping_address')
+		shipping_address_name = get_default_address(party_type, party, "is_shipping_address")
 		if billing_address_name:
-			billing_address = frappe.get_doc('Address', billing_address_name)
+			billing_address = frappe.get_doc("Address", billing_address_name)
 		if shipping_address_name:
-			shipping_address = frappe.get_doc('Address', shipping_address_name)
+			shipping_address = frappe.get_doc("Address", shipping_address_name)
 
 	if billing_address:
-		out["billing_city"]= billing_address.city
-		out["billing_county"]= billing_address.county
-		out["billing_state"]= billing_address.state
-		out["billing_zipcode"]= billing_address.pincode
-		out["billing_country"]= billing_address.country
+		out["billing_city"] = billing_address.city
+		out["billing_county"] = billing_address.county
+		out["billing_state"] = billing_address.state
+		out["billing_zipcode"] = billing_address.pincode
+		out["billing_country"] = billing_address.country
 
 	if shipping_address:
-		out["shipping_city"]= shipping_address.city
-		out["shipping_county"]= shipping_address.county
-		out["shipping_state"]= shipping_address.state
-		out["shipping_zipcode"]= shipping_address.pincode
-		out["shipping_country"]= shipping_address.country
+		out["shipping_city"] = shipping_address.city
+		out["shipping_county"] = shipping_address.county
+		out["shipping_state"] = shipping_address.state
+		out["shipping_zipcode"] = shipping_address.pincode
+		out["shipping_country"] = shipping_address.country
 
 	return out
 
+
 def get_tax_template(posting_date, args):
 	"""Get matching tax rule"""
 	args = frappe._dict(args)
-	conditions = ["""(from_date is null or from_date <= '{0}')
-		and (to_date is null or to_date >= '{0}')""".format(posting_date)]
+	conditions = []
 
-	conditions.append("ifnull(tax_category, '') = {0}".format(frappe.db.escape(cstr(args.get("tax_category")))))
-	if 'tax_category' in args.keys():
-		del args['tax_category']
+	if posting_date:
+		conditions.append(
+			f"""(from_date is null or from_date <= '{posting_date}')
+			and (to_date is null or to_date >= '{posting_date}')"""
+		)
+	else:
+		conditions.append("(from_date is null) and (to_date is null)")
+
+	conditions.append(
+		"ifnull(tax_category, '') = {0}".format(frappe.db.escape(cstr(args.get("tax_category"))))
+	)
+	if "tax_category" in args.keys():
+		del args["tax_category"]
 
 	for key, value in args.items():
-		if key=="use_for_shopping_cart":
+		if key == "use_for_shopping_cart":
 			conditions.append("use_for_shopping_cart = {0}".format(1 if value else 0))
-		elif key == 'customer_group':
-			if not value: value = get_root_of("Customer Group")
+		elif key == "customer_group":
+			if not value:
+				value = get_root_of("Customer Group")
 			customer_group_condition = get_customer_group_condition(value)
 			conditions.append("ifnull({0}, '') in ('', {1})".format(key, customer_group_condition))
 		else:
 			conditions.append("ifnull({0}, '') in ('', {1})".format(key, frappe.db.escape(cstr(value))))
 
-	tax_rule = frappe.db.sql("""select * from `tabTax Rule`
-		where {0}""".format(" and ".join(conditions)), as_dict = True)
+	tax_rule = frappe.db.sql(
+		"""select * from `tabTax Rule`
+		where {0}""".format(
+			" and ".join(conditions)
+		),
+		as_dict=True,
+	)
 
 	if not tax_rule:
 		return None
@@ -166,28 +204,34 @@
 	for rule in tax_rule:
 		rule.no_of_keys_matched = 0
 		for key in args:
-			if rule.get(key): rule.no_of_keys_matched += 1
+			if rule.get(key):
+				rule.no_of_keys_matched += 1
 
 	def cmp(a, b):
 		# refernce: https://docs.python.org/3.0/whatsnew/3.0.html#ordering-comparisons
 		return int(a > b) - int(a < b)
 
-	rule = sorted(tax_rule,
-		key = functools.cmp_to_key(lambda b, a:
-		cmp(a.no_of_keys_matched, b.no_of_keys_matched) or
-		cmp(a.priority, b.priority)))[0]
+	rule = sorted(
+		tax_rule,
+		key=functools.cmp_to_key(
+			lambda b, a: cmp(a.no_of_keys_matched, b.no_of_keys_matched) or cmp(a.priority, b.priority)
+		),
+	)[0]
 
 	tax_template = rule.sales_tax_template or rule.purchase_tax_template
 	doctype = "{0} Taxes and Charges Template".format(rule.tax_type)
 
-	if frappe.db.get_value(doctype, tax_template, 'disabled')==1:
+	if frappe.db.get_value(doctype, tax_template, "disabled") == 1:
 		return None
 
 	return tax_template
 
+
 def get_customer_group_condition(customer_group):
 	condition = ""
-	customer_groups = ["%s"%(frappe.db.escape(d.name)) for d in get_parent_customer_groups(customer_group)]
+	customer_groups = [
+		"%s" % (frappe.db.escape(d.name)) for d in get_parent_customer_groups(customer_group)
+	]
 	if customer_groups:
-		condition = ",".join(['%s'] * len(customer_groups))%(tuple(customer_groups))
+		condition = ",".join(["%s"] * len(customer_groups)) % (tuple(customer_groups))
 	return condition
diff --git a/erpnext/accounts/doctype/tax_rule/test_tax_rule.py b/erpnext/accounts/doctype/tax_rule/test_tax_rule.py
index d5ac9b2..848e054 100644
--- a/erpnext/accounts/doctype/tax_rule/test_tax_rule.py
+++ b/erpnext/accounts/doctype/tax_rule/test_tax_rule.py
@@ -9,8 +9,7 @@
 from erpnext.crm.doctype.opportunity.opportunity import make_quotation
 from erpnext.crm.doctype.opportunity.test_opportunity import make_opportunity
 
-test_records = frappe.get_test_records('Tax Rule')
-
+test_records = frappe.get_test_records("Tax Rule")
 
 
 class TestTaxRule(unittest.TestCase):
@@ -26,40 +25,70 @@
 		frappe.db.sql("delete from `tabTax Rule`")
 
 	def test_conflict(self):
-		tax_rule1 = make_tax_rule(customer= "_Test Customer",
-			sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", priority = 1)
+		tax_rule1 = make_tax_rule(
+			customer="_Test Customer",
+			sales_tax_template="_Test Sales Taxes and Charges Template - _TC",
+			priority=1,
+		)
 		tax_rule1.save()
 
-		tax_rule2 = make_tax_rule(customer= "_Test Customer",
-			sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", priority = 1)
+		tax_rule2 = make_tax_rule(
+			customer="_Test Customer",
+			sales_tax_template="_Test Sales Taxes and Charges Template - _TC",
+			priority=1,
+		)
 
 		self.assertRaises(ConflictingTaxRule, tax_rule2.save)
 
 	def test_conflict_with_non_overlapping_dates(self):
-		tax_rule1 = make_tax_rule(customer= "_Test Customer",
-			sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", priority = 1, from_date = "2015-01-01")
+		tax_rule1 = make_tax_rule(
+			customer="_Test Customer",
+			sales_tax_template="_Test Sales Taxes and Charges Template - _TC",
+			priority=1,
+			from_date="2015-01-01",
+		)
 		tax_rule1.save()
 
-		tax_rule2 = make_tax_rule(customer= "_Test Customer",
-			sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", priority = 1, to_date = "2013-01-01")
+		tax_rule2 = make_tax_rule(
+			customer="_Test Customer",
+			sales_tax_template="_Test Sales Taxes and Charges Template - _TC",
+			priority=1,
+			to_date="2013-01-01",
+		)
 
 		tax_rule2.save()
 		self.assertTrue(tax_rule2.name)
 
 	def test_for_parent_customer_group(self):
-		tax_rule1 = make_tax_rule(customer_group= "All Customer Groups",
-			sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", priority = 1, from_date = "2015-01-01")
+		tax_rule1 = make_tax_rule(
+			customer_group="All Customer Groups",
+			sales_tax_template="_Test Sales Taxes and Charges Template - _TC",
+			priority=1,
+			from_date="2015-01-01",
+		)
 		tax_rule1.save()
-		self.assertEqual(get_tax_template("2015-01-01", {"customer_group" : "Commercial", "use_for_shopping_cart":1}),
-			"_Test Sales Taxes and Charges Template - _TC")
+		self.assertEqual(
+			get_tax_template("2015-01-01", {"customer_group": "Commercial", "use_for_shopping_cart": 1}),
+			"_Test Sales Taxes and Charges Template - _TC",
+		)
 
 	def test_conflict_with_overlapping_dates(self):
-		tax_rule1 = make_tax_rule(customer= "_Test Customer",
-			sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", priority = 1, from_date = "2015-01-01", to_date = "2015-01-05")
+		tax_rule1 = make_tax_rule(
+			customer="_Test Customer",
+			sales_tax_template="_Test Sales Taxes and Charges Template - _TC",
+			priority=1,
+			from_date="2015-01-01",
+			to_date="2015-01-05",
+		)
 		tax_rule1.save()
 
-		tax_rule2 = make_tax_rule(customer= "_Test Customer",
-			sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", priority = 1, from_date = "2015-01-03", to_date = "2015-01-09")
+		tax_rule2 = make_tax_rule(
+			customer="_Test Customer",
+			sales_tax_template="_Test Sales Taxes and Charges Template - _TC",
+			priority=1,
+			from_date="2015-01-03",
+			to_date="2015-01-09",
+		)
 
 		self.assertRaises(ConflictingTaxRule, tax_rule2.save)
 
@@ -67,93 +96,186 @@
 		tax_rule = make_tax_rule()
 		self.assertEqual(tax_rule.purchase_tax_template, None)
 
-
 	def test_select_tax_rule_based_on_customer(self):
-		make_tax_rule(customer= "_Test Customer",
-			sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", save=1)
+		make_tax_rule(
+			customer="_Test Customer",
+			sales_tax_template="_Test Sales Taxes and Charges Template - _TC",
+			save=1,
+		)
 
-		make_tax_rule(customer= "_Test Customer 1",
-			sales_tax_template = "_Test Sales Taxes and Charges Template 1 - _TC", save=1)
+		make_tax_rule(
+			customer="_Test Customer 1",
+			sales_tax_template="_Test Sales Taxes and Charges Template 1 - _TC",
+			save=1,
+		)
 
-		make_tax_rule(customer= "_Test Customer 2",
-			sales_tax_template = "_Test Sales Taxes and Charges Template 2 - _TC", save=1)
+		make_tax_rule(
+			customer="_Test Customer 2",
+			sales_tax_template="_Test Sales Taxes and Charges Template 2 - _TC",
+			save=1,
+		)
 
-		self.assertEqual(get_tax_template("2015-01-01", {"customer":"_Test Customer 2"}),
-			"_Test Sales Taxes and Charges Template 2 - _TC")
+		self.assertEqual(
+			get_tax_template("2015-01-01", {"customer": "_Test Customer 2"}),
+			"_Test Sales Taxes and Charges Template 2 - _TC",
+		)
 
 	def test_select_tax_rule_based_on_tax_category(self):
-		make_tax_rule(customer="_Test Customer", tax_category="_Test Tax Category 1",
-			sales_tax_template="_Test Sales Taxes and Charges Template 1 - _TC", save=1)
+		make_tax_rule(
+			customer="_Test Customer",
+			tax_category="_Test Tax Category 1",
+			sales_tax_template="_Test Sales Taxes and Charges Template 1 - _TC",
+			save=1,
+		)
 
-		make_tax_rule(customer="_Test Customer", tax_category="_Test Tax Category 2",
-			sales_tax_template="_Test Sales Taxes and Charges Template 2 - _TC", save=1)
+		make_tax_rule(
+			customer="_Test Customer",
+			tax_category="_Test Tax Category 2",
+			sales_tax_template="_Test Sales Taxes and Charges Template 2 - _TC",
+			save=1,
+		)
 
 		self.assertFalse(get_tax_template("2015-01-01", {"customer": "_Test Customer"}))
 
-		self.assertEqual(get_tax_template("2015-01-01", {"customer": "_Test Customer", "tax_category": "_Test Tax Category 1"}),
-			"_Test Sales Taxes and Charges Template 1 - _TC")
-		self.assertEqual(get_tax_template("2015-01-01", {"customer": "_Test Customer", "tax_category": "_Test Tax Category 2"}),
-			"_Test Sales Taxes and Charges Template 2 - _TC")
+		self.assertEqual(
+			get_tax_template(
+				"2015-01-01", {"customer": "_Test Customer", "tax_category": "_Test Tax Category 1"}
+			),
+			"_Test Sales Taxes and Charges Template 1 - _TC",
+		)
+		self.assertEqual(
+			get_tax_template(
+				"2015-01-01", {"customer": "_Test Customer", "tax_category": "_Test Tax Category 2"}
+			),
+			"_Test Sales Taxes and Charges Template 2 - _TC",
+		)
 
-		make_tax_rule(customer="_Test Customer", tax_category="",
-			sales_tax_template="_Test Sales Taxes and Charges Template - _TC", save=1)
+		make_tax_rule(
+			customer="_Test Customer",
+			tax_category="",
+			sales_tax_template="_Test Sales Taxes and Charges Template - _TC",
+			save=1,
+		)
 
-		self.assertEqual(get_tax_template("2015-01-01", {"customer": "_Test Customer"}),
-			"_Test Sales Taxes and Charges Template - _TC")
+		self.assertEqual(
+			get_tax_template("2015-01-01", {"customer": "_Test Customer"}),
+			"_Test Sales Taxes and Charges Template - _TC",
+		)
 
 	def test_select_tax_rule_based_on_better_match(self):
-		make_tax_rule(customer= "_Test Customer", billing_city = "Test City", billing_state = "Test State",
-			sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", save=1)
+		make_tax_rule(
+			customer="_Test Customer",
+			billing_city="Test City",
+			billing_state="Test State",
+			sales_tax_template="_Test Sales Taxes and Charges Template - _TC",
+			save=1,
+		)
 
-		make_tax_rule(customer= "_Test Customer",  billing_city = "Test City1", billing_state = "Test State",
-			sales_tax_template = "_Test Sales Taxes and Charges Template 1 - _TC", save=1)
+		make_tax_rule(
+			customer="_Test Customer",
+			billing_city="Test City1",
+			billing_state="Test State",
+			sales_tax_template="_Test Sales Taxes and Charges Template 1 - _TC",
+			save=1,
+		)
 
-		self.assertEqual(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City", "billing_state": "Test State"}),
-			"_Test Sales Taxes and Charges Template - _TC")
+		self.assertEqual(
+			get_tax_template(
+				"2015-01-01",
+				{"customer": "_Test Customer", "billing_city": "Test City", "billing_state": "Test State"},
+			),
+			"_Test Sales Taxes and Charges Template - _TC",
+		)
 
 	def test_select_tax_rule_based_on_state_match(self):
-		make_tax_rule(customer= "_Test Customer", shipping_state = "Test State",
-			sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", save=1)
+		make_tax_rule(
+			customer="_Test Customer",
+			shipping_state="Test State",
+			sales_tax_template="_Test Sales Taxes and Charges Template - _TC",
+			save=1,
+		)
 
-		make_tax_rule(customer= "_Test Customer", shipping_state = "Test State12",
-			sales_tax_template = "_Test Sales Taxes and Charges Template 1 - _TC", priority=2, save=1)
+		make_tax_rule(
+			customer="_Test Customer",
+			shipping_state="Test State12",
+			sales_tax_template="_Test Sales Taxes and Charges Template 1 - _TC",
+			priority=2,
+			save=1,
+		)
 
-		self.assertEqual(get_tax_template("2015-01-01", {"customer":"_Test Customer", "shipping_state": "Test State"}),
-			"_Test Sales Taxes and Charges Template - _TC")
+		self.assertEqual(
+			get_tax_template("2015-01-01", {"customer": "_Test Customer", "shipping_state": "Test State"}),
+			"_Test Sales Taxes and Charges Template - _TC",
+		)
 
 	def test_select_tax_rule_based_on_better_priority(self):
-		make_tax_rule(customer= "_Test Customer", billing_city = "Test City",
-			sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", priority=1, save=1)
+		make_tax_rule(
+			customer="_Test Customer",
+			billing_city="Test City",
+			sales_tax_template="_Test Sales Taxes and Charges Template - _TC",
+			priority=1,
+			save=1,
+		)
 
-		make_tax_rule(customer= "_Test Customer", billing_city = "Test City",
-			sales_tax_template = "_Test Sales Taxes and Charges Template 1 - _TC", priority=2, save=1)
+		make_tax_rule(
+			customer="_Test Customer",
+			billing_city="Test City",
+			sales_tax_template="_Test Sales Taxes and Charges Template 1 - _TC",
+			priority=2,
+			save=1,
+		)
 
-		self.assertEqual(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City"}),
-			"_Test Sales Taxes and Charges Template 1 - _TC")
+		self.assertEqual(
+			get_tax_template("2015-01-01", {"customer": "_Test Customer", "billing_city": "Test City"}),
+			"_Test Sales Taxes and Charges Template 1 - _TC",
+		)
 
 	def test_select_tax_rule_based_cross_matching_keys(self):
-		make_tax_rule(customer= "_Test Customer", billing_city = "Test City",
-			sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", save=1)
+		make_tax_rule(
+			customer="_Test Customer",
+			billing_city="Test City",
+			sales_tax_template="_Test Sales Taxes and Charges Template - _TC",
+			save=1,
+		)
 
-		make_tax_rule(customer= "_Test Customer 1", billing_city = "Test City 1",
-			sales_tax_template = "_Test Sales Taxes and Charges Template 1 - _TC", save=1)
+		make_tax_rule(
+			customer="_Test Customer 1",
+			billing_city="Test City 1",
+			sales_tax_template="_Test Sales Taxes and Charges Template 1 - _TC",
+			save=1,
+		)
 
-		self.assertEqual(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City 1"}),
-			None)
+		self.assertEqual(
+			get_tax_template("2015-01-01", {"customer": "_Test Customer", "billing_city": "Test City 1"}),
+			None,
+		)
 
 	def test_select_tax_rule_based_cross_partially_keys(self):
-		make_tax_rule(customer= "_Test Customer", billing_city = "Test City",
-			sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", save=1)
+		make_tax_rule(
+			customer="_Test Customer",
+			billing_city="Test City",
+			sales_tax_template="_Test Sales Taxes and Charges Template - _TC",
+			save=1,
+		)
 
-		make_tax_rule(billing_city = "Test City 1",
-			sales_tax_template = "_Test Sales Taxes and Charges Template 1 - _TC", save=1)
+		make_tax_rule(
+			billing_city="Test City 1",
+			sales_tax_template="_Test Sales Taxes and Charges Template 1 - _TC",
+			save=1,
+		)
 
-		self.assertEqual(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City 1"}),
-			"_Test Sales Taxes and Charges Template 1 - _TC")
+		self.assertEqual(
+			get_tax_template("2015-01-01", {"customer": "_Test Customer", "billing_city": "Test City 1"}),
+			"_Test Sales Taxes and Charges Template 1 - _TC",
+		)
 
 	def test_taxes_fetch_via_tax_rule(self):
-		make_tax_rule(customer= "_Test Customer", billing_city = "_Test City",
-			sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", save=1)
+		make_tax_rule(
+			customer="_Test Customer",
+			billing_city="_Test City",
+			sales_tax_template="_Test Sales Taxes and Charges Template - _TC",
+			save=1,
+		)
 
 		# create opportunity for customer
 		opportunity = make_opportunity(with_items=1)
@@ -168,7 +290,6 @@
 		self.assertTrue(len(quotation.taxes) > 0)
 
 
-
 def make_tax_rule(**args):
 	args = frappe._dict(args)
 
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 5bb9b93..a519d8b 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -16,7 +16,7 @@
 
 	def validate_dates(self):
 		last_date = None
-		for d in self.get('rates'):
+		for d in self.get("rates"):
 			if getdate(d.from_date) >= getdate(d.to_date):
 				frappe.throw(_("Row #{0}: From Date cannot be before To Date").format(d.idx))
 
@@ -26,25 +26,32 @@
 
 	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'))))
+		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'))
+			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:
-				frappe.throw(_("Row #{0}: Cumulative threshold cannot be less than Single Transaction threshold").format(d.idx))
+		for d in self.get("rates"):
+			if (
+				d.cumulative_threshold and d.single_threshold and d.cumulative_threshold < d.single_threshold
+			):
+				frappe.throw(
+					_("Row #{0}: Cumulative threshold cannot be less than Single Transaction threshold").format(
+						d.idx
+					)
+				)
+
 
 def get_party_details(inv):
-	party_type, party = '', ''
+	party_type, party = "", ""
 
-	if inv.doctype == 'Sales Invoice':
-		party_type = 'Customer'
+	if inv.doctype == "Sales Invoice":
+		party_type = "Customer"
 		party = inv.customer
 	else:
-		party_type = 'Supplier'
+		party_type = "Supplier"
 		party = inv.supplier
 
 	if not party:
@@ -52,65 +59,71 @@
 
 	return party_type, party
 
+
 def get_party_tax_withholding_details(inv, tax_withholding_category=None):
-	pan_no = ''
+	pan_no = ""
 	parties = []
 	party_type, party = get_party_details(inv)
 	has_pan_field = frappe.get_meta(party_type).has_field("pan")
 
 	if not tax_withholding_category:
 		if has_pan_field:
-			fields = ['tax_withholding_category', 'pan']
+			fields = ["tax_withholding_category", "pan"]
 		else:
-			fields = ['tax_withholding_category']
+			fields = ["tax_withholding_category"]
 
 		tax_withholding_details = frappe.db.get_value(party_type, party, fields, as_dict=1)
 
-		tax_withholding_category = tax_withholding_details.get('tax_withholding_category')
-		pan_no = tax_withholding_details.get('pan')
+		tax_withholding_category = tax_withholding_details.get("tax_withholding_category")
+		pan_no = tax_withholding_details.get("pan")
 
 	if not tax_withholding_category:
 		return
 
 	# if tax_withholding_category passed as an argument but not pan_no
 	if not pan_no and has_pan_field:
-		pan_no = frappe.db.get_value(party_type, party, 'pan')
+		pan_no = frappe.db.get_value(party_type, party, "pan")
 
 	# Get others suppliers with the same PAN No
 	if pan_no:
-		parties = frappe.get_all(party_type, filters={ 'pan': pan_no }, pluck='name')
+		parties = frappe.get_all(party_type, filters={"pan": pan_no}, pluck="name")
 
 	if not parties:
 		parties.append(party)
 
-	posting_date = inv.get('posting_date') or inv.get('transaction_date')
+	posting_date = inv.get("posting_date") or inv.get("transaction_date")
 	tax_details = get_tax_withholding_details(tax_withholding_category, posting_date, inv.company)
 
 	if not tax_details:
-		frappe.throw(_('Please set associated account in Tax Withholding Category {0} against Company {1}')
-			.format(tax_withholding_category, inv.company))
+		frappe.throw(
+			_("Please set associated account in Tax Withholding Category {0} against Company {1}").format(
+				tax_withholding_category, inv.company
+			)
+		)
 
-	if party_type == 'Customer' and not tax_details.cumulative_threshold:
+	if party_type == "Customer" and not tax_details.cumulative_threshold:
 		# TCS is only chargeable on sum of invoiced value
-		frappe.throw(_('Tax Withholding Category {} against Company {} for Customer {} should have Cumulative Threshold value.')
-			.format(tax_withholding_category, inv.company, party))
+		frappe.throw(
+			_(
+				"Tax Withholding Category {} against Company {} for Customer {} should have Cumulative Threshold value."
+			).format(tax_withholding_category, inv.company, party)
+		)
 
 	tax_amount, tax_deducted, tax_deducted_on_advances = get_tax_amount(
-		party_type, parties,
-		inv, tax_details,
-		posting_date, pan_no
+		party_type, parties, inv, tax_details, posting_date, pan_no
 	)
 
-	if party_type == 'Supplier':
+	if party_type == "Supplier":
 		tax_row = get_tax_row_for_tds(tax_details, tax_amount)
 	else:
 		tax_row = get_tax_row_for_tcs(inv, tax_details, tax_amount, tax_deducted)
 
-	if inv.doctype == 'Purchase Invoice':
+	if inv.doctype == "Purchase Invoice":
 		return tax_row, tax_deducted_on_advances
 	else:
 		return tax_row
 
+
 def get_tax_withholding_details(tax_withholding_category, posting_date, company):
 	tax_withholding = frappe.get_doc("Tax Withholding Category", tax_withholding_category)
 
@@ -118,19 +131,24 @@
 
 	for account_detail in tax_withholding.accounts:
 		if company == account_detail.company:
-			return frappe._dict({
-				"tax_withholding_category": tax_withholding_category,
-				"account_head": account_detail.account,
-				"rate": tax_rate_detail.tax_withholding_rate,
-				"from_date": tax_rate_detail.from_date,
-				"to_date": tax_rate_detail.to_date,
-				"threshold": tax_rate_detail.single_threshold,
-				"cumulative_threshold": tax_rate_detail.cumulative_threshold,
-				"description": tax_withholding.category_name if tax_withholding.category_name else tax_withholding_category,
-				"consider_party_ledger_amount": tax_withholding.consider_party_ledger_amount,
-				"tax_on_excess_amount": tax_withholding.tax_on_excess_amount,
-				"round_off_tax_amount": tax_withholding.round_off_tax_amount
-			})
+			return frappe._dict(
+				{
+					"tax_withholding_category": tax_withholding_category,
+					"account_head": account_detail.account,
+					"rate": tax_rate_detail.tax_withholding_rate,
+					"from_date": tax_rate_detail.from_date,
+					"to_date": tax_rate_detail.to_date,
+					"threshold": tax_rate_detail.single_threshold,
+					"cumulative_threshold": tax_rate_detail.cumulative_threshold,
+					"description": tax_withholding.category_name
+					if tax_withholding.category_name
+					else tax_withholding_category,
+					"consider_party_ledger_amount": tax_withholding.consider_party_ledger_amount,
+					"tax_on_excess_amount": tax_withholding.tax_on_excess_amount,
+					"round_off_tax_amount": tax_withholding.round_off_tax_amount,
+				}
+			)
+
 
 def get_tax_withholding_rates(tax_withholding, posting_date):
 	# returns the row that matches with the fiscal year from posting date
@@ -140,13 +158,14 @@
 
 	frappe.throw(_("No Tax Withholding data found for the current posting date."))
 
+
 def get_tax_row_for_tcs(inv, tax_details, tax_amount, tax_deducted):
 	row = {
 		"category": "Total",
 		"charge_type": "Actual",
 		"tax_amount": tax_amount,
 		"description": tax_details.description,
-		"account_head": tax_details.account_head
+		"account_head": tax_details.account_head,
 	}
 
 	if tax_deducted:
@@ -156,20 +175,20 @@
 		taxes_excluding_tcs = [d for d in inv.taxes if d.account_head != tax_details.account_head]
 		if taxes_excluding_tcs:
 			# chargeable amount is the total amount after other charges are applied
-			row.update({
-				"charge_type": "On Previous Row Total",
-				"row_id": len(taxes_excluding_tcs),
-				"rate": tax_details.rate
-			})
+			row.update(
+				{
+					"charge_type": "On Previous Row Total",
+					"row_id": len(taxes_excluding_tcs),
+					"rate": tax_details.rate,
+				}
+			)
 		else:
 			# if only TCS is to be charged, then net total is chargeable amount
-			row.update({
-				"charge_type": "On Net Total",
-				"rate": tax_details.rate
-			})
+			row.update({"charge_type": "On Net Total", "rate": tax_details.rate})
 
 	return row
 
+
 def get_tax_row_for_tds(tax_details, tax_amount):
 	return {
 		"category": "Total",
@@ -177,29 +196,39 @@
 		"tax_amount": tax_amount,
 		"add_deduct_tax": "Deduct",
 		"description": tax_details.description,
-		"account_head": tax_details.account_head
+		"account_head": tax_details.account_head,
 	}
 
+
 def get_lower_deduction_certificate(tax_details, pan_no):
-	ldc_name = frappe.db.get_value('Lower Deduction Certificate',
+	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')
+			"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",
+	)
 
 	if ldc_name:
-		return frappe.get_doc('Lower Deduction Certificate', ldc_name)
+		return frappe.get_doc("Lower Deduction Certificate", ldc_name)
+
 
 def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=None):
 	vouchers = get_invoice_vouchers(parties, tax_details, inv.company, party_type=party_type)
-	advance_vouchers = get_advance_vouchers(parties, company=inv.company, from_date=tax_details.from_date,
-		to_date=tax_details.to_date, party_type=party_type)
+	advance_vouchers = get_advance_vouchers(
+		parties,
+		company=inv.company,
+		from_date=tax_details.from_date,
+		to_date=tax_details.to_date,
+		party_type=party_type,
+	)
 	taxable_vouchers = vouchers + advance_vouchers
 	tax_deducted_on_advances = 0
 
-	if inv.doctype == 'Purchase Invoice':
+	if inv.doctype == "Purchase Invoice":
 		tax_deducted_on_advances = get_taxes_deducted_on_advances_allocated(inv, tax_details)
 
 	tax_deducted = 0
@@ -207,18 +236,20 @@
 		tax_deducted = get_deducted_tax(taxable_vouchers, tax_details)
 
 	tax_amount = 0
-	if party_type == 'Supplier':
+	if party_type == "Supplier":
 		ldc = get_lower_deduction_certificate(tax_details, pan_no)
 		if tax_deducted:
 			net_total = inv.net_total
 			if ldc:
-				tax_amount = get_tds_amount_from_ldc(ldc, parties, pan_no, tax_details, posting_date, net_total)
+				tax_amount = get_tds_amount_from_ldc(
+					ldc, parties, pan_no, tax_details, posting_date, net_total
+				)
 			else:
 				tax_amount = net_total * tax_details.rate / 100 if net_total > 0 else 0
 		else:
 			tax_amount = get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers)
 
-	elif party_type == 'Customer':
+	elif party_type == "Customer":
 		if tax_deducted:
 			# if already TCS is charged, then amount will be calculated based on 'Previous Row Total'
 			tax_amount = 0
@@ -232,27 +263,28 @@
 
 	return tax_amount, tax_deducted, tax_deducted_on_advances
 
-def get_invoice_vouchers(parties, tax_details, company, party_type='Supplier'):
-	dr_or_cr = 'credit' if party_type == 'Supplier' else 'debit'
-	doctype = 'Purchase Invoice' if party_type == 'Supplier' else 'Sales Invoice'
+
+def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
+	dr_or_cr = "credit" if party_type == "Supplier" else "debit"
+	doctype = "Purchase Invoice" if party_type == "Supplier" else "Sales Invoice"
 
 	filters = {
-		'company': company,
-		frappe.scrub(party_type): ['in', parties],
-		'posting_date': ['between', (tax_details.from_date, tax_details.to_date)],
-		'is_opening': 'No',
-		'docstatus': 1
+		"company": company,
+		frappe.scrub(party_type): ["in", parties],
+		"posting_date": ["between", (tax_details.from_date, tax_details.to_date)],
+		"is_opening": "No",
+		"docstatus": 1,
 	}
 
-	if not tax_details.get('consider_party_ledger_amount') and doctype != "Sales Invoice":
-		filters.update({
-			'apply_tds': 1,
-			'tax_withholding_category': tax_details.get('tax_withholding_category')
-		})
+	if not tax_details.get("consider_party_ledger_amount") and doctype != "Sales Invoice":
+		filters.update(
+			{"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")}
+		)
 
 	invoices = frappe.get_all(doctype, filters=filters, pluck="name") or [""]
 
-	journal_entries = frappe.db.sql("""
+	journal_entries = frappe.db.sql(
+		"""
 		SELECT j.name
 			FROM `tabJournal Entry` j, `tabJournal Entry Account` ja
 		WHERE
@@ -261,52 +293,60 @@
 			AND j.posting_date between %s and %s
 			AND ja.{dr_or_cr} > 0
 			AND ja.party in %s
-	""".format(dr_or_cr=dr_or_cr), (tax_details.from_date, tax_details.to_date, tuple(parties)), as_list=1)
+	""".format(
+			dr_or_cr=dr_or_cr
+		),
+		(tax_details.from_date, tax_details.to_date, tuple(parties)),
+		as_list=1,
+	)
 
 	if journal_entries:
 		journal_entries = journal_entries[0]
 
 	return invoices + journal_entries
 
-def get_advance_vouchers(parties, company=None, from_date=None, to_date=None, party_type='Supplier'):
+
+def get_advance_vouchers(
+	parties, company=None, from_date=None, to_date=None, party_type="Supplier"
+):
 	# for advance vouchers, debit and credit is reversed
-	dr_or_cr = 'debit' if party_type == 'Supplier' else 'credit'
+	dr_or_cr = "debit" if party_type == "Supplier" else "credit"
 
 	filters = {
-		dr_or_cr: ['>', 0],
-		'is_opening': 'No',
-		'is_cancelled': 0,
-		'party_type': party_type,
-		'party': ['in', parties],
-		'against_voucher': ['is', 'not set']
+		dr_or_cr: [">", 0],
+		"is_opening": "No",
+		"is_cancelled": 0,
+		"party_type": party_type,
+		"party": ["in", parties],
+		"against_voucher": ["is", "not set"],
 	}
 
 	if company:
-		filters['company'] = company
+		filters["company"] = company
 	if from_date and to_date:
-		filters['posting_date'] = ['between', (from_date, to_date)]
+		filters["posting_date"] = ["between", (from_date, to_date)]
 
-	return frappe.get_all('GL Entry', filters=filters, distinct=1, pluck='voucher_no') or [""]
+	return frappe.get_all("GL Entry", filters=filters, distinct=1, pluck="voucher_no") or [""]
+
 
 def get_taxes_deducted_on_advances_allocated(inv, tax_details):
-	advances = [d.reference_name for d in inv.get('advances')]
+	advances = [d.reference_name for d in inv.get("advances")]
 	tax_info = []
 
 	if advances:
 		pe = frappe.qb.DocType("Payment Entry").as_("pe")
 		at = frappe.qb.DocType("Advance Taxes and Charges").as_("at")
 
-		tax_info = frappe.qb.from_(at).inner_join(pe).on(
-			pe.name == at.parent
-		).select(
-			at.parent, at.name, at.tax_amount, at.allocated_amount
-		).where(
-			pe.tax_withholding_category == tax_details.get('tax_withholding_category')
-		).where(
-			at.parent.isin(advances)
-		).where(
-			at.account_head == tax_details.account_head
-		).run(as_dict=True)
+		tax_info = (
+			frappe.qb.from_(at)
+			.inner_join(pe)
+			.on(pe.name == at.parent)
+			.select(at.parent, at.name, at.tax_amount, at.allocated_amount)
+			.where(pe.tax_withholding_category == tax_details.get("tax_withholding_category"))
+			.where(at.parent.isin(advances))
+			.where(at.account_head == tax_details.account_head)
+			.run(as_dict=True)
+		)
 
 	return tax_info
 
@@ -314,59 +354,74 @@
 def get_deducted_tax(taxable_vouchers, tax_details):
 	# check if TDS / TCS account is already charged on taxable vouchers
 	filters = {
-		'is_cancelled': 0,
-		'credit': ['>', 0],
-		'posting_date': ['between', (tax_details.from_date, tax_details.to_date)],
-		'account': tax_details.account_head,
-		'voucher_no': ['in', taxable_vouchers],
+		"is_cancelled": 0,
+		"credit": [">", 0],
+		"posting_date": ["between", (tax_details.from_date, tax_details.to_date)],
+		"account": tax_details.account_head,
+		"voucher_no": ["in", taxable_vouchers],
 	}
 	field = "credit"
 
-	entries = frappe.db.get_all('GL Entry', filters, pluck=field)
+	entries = frappe.db.get_all("GL Entry", filters, pluck=field)
 	return sum(entries)
 
+
 def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
 	tds_amount = 0
-	invoice_filters = {
-		'name': ('in', vouchers),
-		'docstatus': 1,
-		'apply_tds': 1
-	}
+	invoice_filters = {"name": ("in", vouchers), "docstatus": 1, "apply_tds": 1}
 
-	field = 'sum(net_total)'
+	field = "sum(net_total)"
 
 	if cint(tax_details.consider_party_ledger_amount):
-		invoice_filters.pop('apply_tds', None)
-		field = 'sum(grand_total)'
+		invoice_filters.pop("apply_tds", None)
+		field = "sum(grand_total)"
 
-	supp_credit_amt = frappe.db.get_value('Purchase Invoice', invoice_filters, field) or 0.0
+	supp_credit_amt = frappe.db.get_value("Purchase Invoice", invoice_filters, field) or 0.0
 
-	supp_jv_credit_amt = frappe.db.get_value('Journal Entry Account', {
-		'parent': ('in', vouchers), 'docstatus': 1,
-		'party': ('in', parties), 'reference_type': ('!=', 'Purchase Invoice')
-	}, 'sum(credit_in_account_currency)') or 0.0
+	supp_jv_credit_amt = (
+		frappe.db.get_value(
+			"Journal Entry Account",
+			{
+				"parent": ("in", vouchers),
+				"docstatus": 1,
+				"party": ("in", parties),
+				"reference_type": ("!=", "Purchase Invoice"),
+			},
+			"sum(credit_in_account_currency)",
+		)
+		or 0.0
+	)
 
 	supp_credit_amt += supp_jv_credit_amt
 	supp_credit_amt += inv.net_total
 
-	debit_note_amount = get_debit_note_amount(parties, tax_details.from_date, tax_details.to_date, inv.company)
+	debit_note_amount = get_debit_note_amount(
+		parties, tax_details.from_date, tax_details.to_date, inv.company
+	)
 	supp_credit_amt -= debit_note_amount
 
-	threshold = tax_details.get('threshold', 0)
-	cumulative_threshold = tax_details.get('cumulative_threshold', 0)
+	threshold = tax_details.get("threshold", 0)
+	cumulative_threshold = tax_details.get("cumulative_threshold", 0)
 
-	if ((threshold and inv.net_total >= threshold) or (cumulative_threshold and supp_credit_amt >= cumulative_threshold)):
-		if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint(tax_details.tax_on_excess_amount):
+	if (threshold and inv.net_total >= threshold) or (
+		cumulative_threshold and supp_credit_amt >= cumulative_threshold
+	):
+		if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint(
+			tax_details.tax_on_excess_amount
+		):
 			# Get net total again as TDS is calculated on net total
 			# Grand is used to just check for threshold breach
-			net_total = frappe.db.get_value('Purchase Invoice', invoice_filters, 'sum(net_total)') or 0.0
+			net_total = frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(net_total)") or 0.0
 			net_total += inv.net_total
 			supp_credit_amt = net_total - cumulative_threshold
 
 		if ldc and is_valid_certificate(
-			ldc.valid_from, ldc.valid_upto,
-			inv.get('posting_date') or inv.get('transaction_date'), tax_deducted,
-			inv.net_total, ldc.certificate_limit
+			ldc.valid_from,
+			ldc.valid_upto,
+			inv.get("posting_date") or inv.get("transaction_date"),
+			tax_deducted,
+			inv.net_total,
+			ldc.certificate_limit,
 		):
 			tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details)
 		else:
@@ -374,98 +429,127 @@
 
 	return tds_amount
 
+
 def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers):
 	tcs_amount = 0
 
 	# sum of debit entries made from sales invoices
-	invoiced_amt = frappe.db.get_value('GL Entry', {
-		'is_cancelled': 0,
-		'party': ['in', parties],
-		'company': inv.company,
-		'voucher_no': ['in', vouchers],
-	}, 'sum(debit)') or 0.0
+	invoiced_amt = (
+		frappe.db.get_value(
+			"GL Entry",
+			{
+				"is_cancelled": 0,
+				"party": ["in", parties],
+				"company": inv.company,
+				"voucher_no": ["in", vouchers],
+			},
+			"sum(debit)",
+		)
+		or 0.0
+	)
 
 	# sum of credit entries made from PE / JV with unset 'against voucher'
-	advance_amt = frappe.db.get_value('GL Entry', {
-		'is_cancelled': 0,
-		'party': ['in', parties],
-		'company': inv.company,
-		'voucher_no': ['in', adv_vouchers],
-	}, 'sum(credit)') or 0.0
+	advance_amt = (
+		frappe.db.get_value(
+			"GL Entry",
+			{
+				"is_cancelled": 0,
+				"party": ["in", parties],
+				"company": inv.company,
+				"voucher_no": ["in", adv_vouchers],
+			},
+			"sum(credit)",
+		)
+		or 0.0
+	)
 
 	# sum of credit entries made from sales invoice
-	credit_note_amt = sum(frappe.db.get_all('GL Entry', {
-		'is_cancelled': 0,
-		'credit': ['>', 0],
-		'party': ['in', parties],
-		'posting_date': ['between', (tax_details.from_date, tax_details.to_date)],
-		'company': inv.company,
-		'voucher_type': 'Sales Invoice',
-	}, pluck='credit'))
+	credit_note_amt = sum(
+		frappe.db.get_all(
+			"GL Entry",
+			{
+				"is_cancelled": 0,
+				"credit": [">", 0],
+				"party": ["in", parties],
+				"posting_date": ["between", (tax_details.from_date, tax_details.to_date)],
+				"company": inv.company,
+				"voucher_type": "Sales Invoice",
+			},
+			pluck="credit",
+		)
+	)
 
-	cumulative_threshold = tax_details.get('cumulative_threshold', 0)
+	cumulative_threshold = tax_details.get("cumulative_threshold", 0)
 
 	current_invoice_total = get_invoice_total_without_tcs(inv, tax_details)
 	total_invoiced_amt = current_invoice_total + invoiced_amt + advance_amt - credit_note_amt
 
-	if (cumulative_threshold and total_invoiced_amt >= cumulative_threshold):
+	if cumulative_threshold and total_invoiced_amt >= cumulative_threshold:
 		chargeable_amt = total_invoiced_amt - cumulative_threshold
 		tcs_amount = chargeable_amt * tax_details.rate / 100 if chargeable_amt > 0 else 0
 
 	return tcs_amount
 
+
 def get_invoice_total_without_tcs(inv, tax_details):
 	tcs_tax_row = [d for d in inv.taxes if d.account_head == tax_details.account_head]
 	tcs_tax_row_amount = tcs_tax_row[0].base_tax_amount if tcs_tax_row else 0
 
 	return inv.grand_total - tcs_tax_row_amount
 
+
 def get_tds_amount_from_ldc(ldc, parties, pan_no, tax_details, posting_date, net_total):
 	tds_amount = 0
-	limit_consumed = frappe.db.get_value('Purchase Invoice', {
-		'supplier': ('in', parties),
-		'apply_tds': 1,
-		'docstatus': 1
-	}, 'sum(net_total)')
+	limit_consumed = frappe.db.get_value(
+		"Purchase Invoice",
+		{"supplier": ("in", parties), "apply_tds": 1, "docstatus": 1},
+		"sum(net_total)",
+	)
 
 	if is_valid_certificate(
-		ldc.valid_from, ldc.valid_upto,
-		posting_date, limit_consumed,
-		net_total, ldc.certificate_limit
+		ldc.valid_from, ldc.valid_upto, posting_date, limit_consumed, net_total, ldc.certificate_limit
 	):
-		tds_amount = get_ltds_amount(net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details)
+		tds_amount = get_ltds_amount(
+			net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details
+		)
 
 	return tds_amount
 
+
 def get_debit_note_amount(suppliers, from_date, to_date, company=None):
 
 	filters = {
-		'supplier': ['in', suppliers],
-		'is_return': 1,
-		'docstatus': 1,
-		'posting_date': ['between', (from_date, to_date)]
+		"supplier": ["in", suppliers],
+		"is_return": 1,
+		"docstatus": 1,
+		"posting_date": ["between", (from_date, to_date)],
 	}
-	fields = ['abs(sum(net_total)) as net_total']
+	fields = ["abs(sum(net_total)) as net_total"]
 
 	if company:
-		filters['company'] = company
+		filters["company"] = company
 
-	return frappe.get_all('Purchase Invoice', filters, fields)[0].get('net_total') or 0.0
+	return frappe.get_all("Purchase Invoice", filters, fields)[0].get("net_total") or 0.0
+
 
 def get_ltds_amount(current_amount, deducted_amount, certificate_limit, rate, tax_details):
 	if current_amount < (certificate_limit - deducted_amount):
-		return current_amount * rate/100
+		return current_amount * rate / 100
 	else:
-		ltds_amount = (certificate_limit - deducted_amount)
+		ltds_amount = certificate_limit - deducted_amount
 		tds_amount = current_amount - ltds_amount
 
-		return ltds_amount * rate/100 + tds_amount * tax_details.rate/100
+		return ltds_amount * rate / 100 + tds_amount * tax_details.rate / 100
 
-def is_valid_certificate(valid_from, valid_upto, posting_date, deducted_amount, current_amount, certificate_limit):
+
+def is_valid_certificate(
+	valid_from, valid_upto, posting_date, deducted_amount, current_amount, certificate_limit
+):
 	valid = False
 
-	if ((getdate(valid_from) <= getdate(posting_date) <= getdate(valid_upto)) and
-			certificate_limit > deducted_amount):
+	if (
+		getdate(valid_from) <= getdate(posting_date) <= getdate(valid_upto)
+	) and certificate_limit > deducted_amount:
 		valid = True
 
 	return valid
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category_dashboard.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category_dashboard.py
index 256d4ac..8a510ea 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category_dashboard.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category_dashboard.py
@@ -1,9 +1,2 @@
 def get_data():
-	return {
-		'fieldname': 'tax_withholding_category',
-		'transactions': [
-			{
-				'items': ['Supplier']
-			}
-		]
-	}
+	return {"fieldname": "tax_withholding_category", "transactions": [{"items": ["Supplier"]}]}
diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
index a3fcf7d..3059f8d 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
@@ -10,6 +10,7 @@
 
 test_dependencies = ["Supplier Group", "Customer Group"]
 
+
 class TestTaxWithholdingCategory(unittest.TestCase):
 	@classmethod
 	def setUpClass(self):
@@ -21,18 +22,20 @@
 		cancel_invoices()
 
 	def test_cumulative_threshold_tds(self):
-		frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "Cumulative Threshold TDS")
+		frappe.db.set_value(
+			"Supplier", "Test TDS Supplier", "tax_withholding_category", "Cumulative Threshold TDS"
+		)
 		invoices = []
 
 		# create invoices for lower than single threshold tax rate
 		for _ in range(2):
-			pi = create_purchase_invoice(supplier = "Test TDS Supplier")
+			pi = create_purchase_invoice(supplier="Test TDS Supplier")
 			pi.submit()
 			invoices.append(pi)
 
 		# create another invoice whose total when added to previously created invoice,
 		# surpasses cumulative threshhold
-		pi = create_purchase_invoice(supplier = "Test TDS Supplier")
+		pi = create_purchase_invoice(supplier="Test TDS Supplier")
 		pi.submit()
 
 		# assert equal tax deduction on total invoice amount uptil now
@@ -41,21 +44,23 @@
 		invoices.append(pi)
 
 		# TDS is already deducted, so from onward system will deduct the TDS on every invoice
-		pi = create_purchase_invoice(supplier = "Test TDS Supplier", rate=5000)
+		pi = create_purchase_invoice(supplier="Test TDS Supplier", rate=5000)
 		pi.submit()
 
 		# assert equal tax deduction on total invoice amount uptil now
 		self.assertEqual(pi.taxes_and_charges_deducted, 500)
 		invoices.append(pi)
 
-		#delete invoices to avoid clashing
+		# delete invoices to avoid clashing
 		for d in invoices:
 			d.cancel()
 
 	def test_single_threshold_tds(self):
 		invoices = []
-		frappe.db.set_value("Supplier", "Test TDS Supplier1", "tax_withholding_category", "Single Threshold TDS")
-		pi = create_purchase_invoice(supplier = "Test TDS Supplier1", rate = 20000)
+		frappe.db.set_value(
+			"Supplier", "Test TDS Supplier1", "tax_withholding_category", "Single Threshold TDS"
+		)
+		pi = create_purchase_invoice(supplier="Test TDS Supplier1", rate=20000)
 		pi.submit()
 		invoices.append(pi)
 
@@ -63,7 +68,7 @@
 		self.assertEqual(pi.grand_total, 18000)
 
 		# check gl entry for the purchase invoice
-		gl_entries = frappe.db.get_all('GL Entry', filters={'voucher_no': pi.name}, fields=["*"])
+		gl_entries = frappe.db.get_all("GL Entry", filters={"voucher_no": pi.name}, fields=["*"])
 		self.assertEqual(len(gl_entries), 3)
 		for d in gl_entries:
 			if d.account == pi.credit_to:
@@ -75,7 +80,7 @@
 			else:
 				raise ValueError("Account head does not match.")
 
-		pi = create_purchase_invoice(supplier = "Test TDS Supplier1")
+		pi = create_purchase_invoice(supplier="Test TDS Supplier1")
 		pi.submit()
 		invoices.append(pi)
 
@@ -88,17 +93,19 @@
 
 	def test_tax_withholding_category_checks(self):
 		invoices = []
-		frappe.db.set_value("Supplier", "Test TDS Supplier3", "tax_withholding_category", "New TDS Category")
+		frappe.db.set_value(
+			"Supplier", "Test TDS Supplier3", "tax_withholding_category", "New TDS Category"
+		)
 
 		# First Invoice with no tds check
-		pi = create_purchase_invoice(supplier = "Test TDS Supplier3", rate = 20000, do_not_save=True)
+		pi = create_purchase_invoice(supplier="Test TDS Supplier3", rate=20000, do_not_save=True)
 		pi.apply_tds = 0
 		pi.save()
 		pi.submit()
 		invoices.append(pi)
 
 		# Second Invoice will apply TDS checked
-		pi1 = create_purchase_invoice(supplier = "Test TDS Supplier3", rate = 20000)
+		pi1 = create_purchase_invoice(supplier="Test TDS Supplier3", rate=20000)
 		pi1.submit()
 		invoices.append(pi1)
 
@@ -110,82 +117,89 @@
 		for d in invoices:
 			d.cancel()
 
-
 	def test_cumulative_threshold_tcs(self):
-		frappe.db.set_value("Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS")
+		frappe.db.set_value(
+			"Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS"
+		)
 		invoices = []
 
 		# create invoices for lower than single threshold tax rate
 		for _ in range(2):
-			si = create_sales_invoice(customer = "Test TCS Customer")
+			si = create_sales_invoice(customer="Test TCS Customer")
 			si.submit()
 			invoices.append(si)
 
 		# create another invoice whose total when added to previously created invoice,
 		# surpasses cumulative threshhold
-		si = create_sales_invoice(customer = "Test TCS Customer", rate=12000)
+		si = create_sales_invoice(customer="Test TCS Customer", rate=12000)
 		si.submit()
 
 		# assert tax collection on total invoice amount created until now
-		tcs_charged = sum([d.base_tax_amount for d in si.taxes if d.account_head == 'TCS - _TC'])
+		tcs_charged = sum([d.base_tax_amount for d in si.taxes if d.account_head == "TCS - _TC"])
 		self.assertEqual(tcs_charged, 200)
 		self.assertEqual(si.grand_total, 12200)
 		invoices.append(si)
 
 		# TCS is already collected once, so going forward system will collect TCS on every invoice
-		si = create_sales_invoice(customer = "Test TCS Customer", rate=5000)
+		si = create_sales_invoice(customer="Test TCS Customer", rate=5000)
 		si.submit()
 
-		tcs_charged = sum(d.base_tax_amount for d in si.taxes if d.account_head == 'TCS - _TC')
+		tcs_charged = sum(d.base_tax_amount for d in si.taxes if d.account_head == "TCS - _TC")
 		self.assertEqual(tcs_charged, 500)
 		invoices.append(si)
 
-		#delete invoices to avoid clashing
+		# delete invoices to avoid clashing
 		for d in invoices:
 			d.cancel()
 
 	def test_tds_calculation_on_net_total(self):
-		frappe.db.set_value("Supplier", "Test TDS Supplier4", "tax_withholding_category", "Cumulative Threshold TDS")
+		frappe.db.set_value(
+			"Supplier", "Test TDS Supplier4", "tax_withholding_category", "Cumulative Threshold TDS"
+		)
 		invoices = []
 
-		pi = create_purchase_invoice(supplier = "Test TDS Supplier4", rate = 20000, do_not_save=True)
-		pi.append('taxes', {
-			"category": "Total",
-			"charge_type": "Actual",
-			"account_head": '_Test Account VAT - _TC',
-			"cost_center": 'Main - _TC',
-			"tax_amount": 1000,
-			"description": "Test",
-			"add_deduct_tax": "Add"
-
-		})
+		pi = create_purchase_invoice(supplier="Test TDS Supplier4", rate=20000, do_not_save=True)
+		pi.append(
+			"taxes",
+			{
+				"category": "Total",
+				"charge_type": "Actual",
+				"account_head": "_Test Account VAT - _TC",
+				"cost_center": "Main - _TC",
+				"tax_amount": 1000,
+				"description": "Test",
+				"add_deduct_tax": "Add",
+			},
+		)
 		pi.save()
 		pi.submit()
 		invoices.append(pi)
 
 		# Second Invoice will apply TDS checked
-		pi1 = create_purchase_invoice(supplier = "Test TDS Supplier4", rate = 20000)
+		pi1 = create_purchase_invoice(supplier="Test TDS Supplier4", rate=20000)
 		pi1.submit()
 		invoices.append(pi1)
 
 		self.assertEqual(pi1.taxes[0].tax_amount, 4000)
 
-		#delete invoices to avoid clashing
+		# delete invoices to avoid clashing
 		for d in invoices:
 			d.cancel()
 
 	def test_multi_category_single_supplier(self):
-		frappe.db.set_value("Supplier", "Test TDS Supplier5", "tax_withholding_category", "Test Service Category")
+		frappe.db.set_value(
+			"Supplier", "Test TDS Supplier5", "tax_withholding_category", "Test Service Category"
+		)
 		invoices = []
 
-		pi = create_purchase_invoice(supplier = "Test TDS Supplier5", rate = 500, do_not_save=True)
+		pi = create_purchase_invoice(supplier="Test TDS Supplier5", rate=500, do_not_save=True)
 		pi.tax_withholding_category = "Test Service Category"
 		pi.save()
 		pi.submit()
 		invoices.append(pi)
 
 		# Second Invoice will apply TDS checked
-		pi1 = create_purchase_invoice(supplier = "Test TDS Supplier5", rate = 2500, do_not_save=True)
+		pi1 = create_purchase_invoice(supplier="Test TDS Supplier5", rate=2500, do_not_save=True)
 		pi1.tax_withholding_category = "Test Goods Category"
 		pi1.save()
 		pi1.submit()
@@ -193,258 +207,294 @@
 
 		self.assertEqual(pi1.taxes[0].tax_amount, 250)
 
-		#delete invoices to avoid clashing
+		# delete invoices to avoid clashing
 		for d in invoices:
 			d.cancel()
 
-def cancel_invoices():
-	purchase_invoices = frappe.get_all("Purchase Invoice", {
-		'supplier': ['in', ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']],
-		'docstatus': 1
-	}, pluck="name")
 
-	sales_invoices = frappe.get_all("Sales Invoice", {
-		'customer': 'Test TCS Customer',
-		'docstatus': 1
-	}, pluck="name")
+def cancel_invoices():
+	purchase_invoices = frappe.get_all(
+		"Purchase Invoice",
+		{
+			"supplier": ["in", ["Test TDS Supplier", "Test TDS Supplier1", "Test TDS Supplier2"]],
+			"docstatus": 1,
+		},
+		pluck="name",
+	)
+
+	sales_invoices = frappe.get_all(
+		"Sales Invoice", {"customer": "Test TCS Customer", "docstatus": 1}, pluck="name"
+	)
 
 	for d in purchase_invoices:
-		frappe.get_doc('Purchase Invoice', d).cancel()
+		frappe.get_doc("Purchase Invoice", d).cancel()
 
 	for d in sales_invoices:
-		frappe.get_doc('Sales Invoice', d).cancel()
+		frappe.get_doc("Sales Invoice", d).cancel()
+
 
 def create_purchase_invoice(**args):
 	# return sales invoice doc object
-	item = frappe.db.get_value('Item', {'item_name': 'TDS Item'}, "name")
+	item = frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name")
 
 	args = frappe._dict(args)
-	pi = frappe.get_doc({
-		"doctype": "Purchase Invoice",
-		"posting_date": today(),
-		"apply_tds": 0 if args.do_not_apply_tds else 1,
-		"supplier": args.supplier,
-		"company": '_Test Company',
-		"taxes_and_charges": "",
-		"currency": "INR",
-		"credit_to": "Creditors - _TC",
-		"taxes": [],
-		"items": [{
-			'doctype': 'Purchase Invoice Item',
-			'item_code': item,
-			'qty': args.qty or 1,
-			'rate': args.rate or 10000,
-			'cost_center': 'Main - _TC',
-			'expense_account': 'Stock Received But Not Billed - _TC'
-		}]
-	})
+	pi = frappe.get_doc(
+		{
+			"doctype": "Purchase Invoice",
+			"posting_date": today(),
+			"apply_tds": 0 if args.do_not_apply_tds else 1,
+			"supplier": args.supplier,
+			"company": "_Test Company",
+			"taxes_and_charges": "",
+			"currency": "INR",
+			"credit_to": "Creditors - _TC",
+			"taxes": [],
+			"items": [
+				{
+					"doctype": "Purchase Invoice Item",
+					"item_code": item,
+					"qty": args.qty or 1,
+					"rate": args.rate or 10000,
+					"cost_center": "Main - _TC",
+					"expense_account": "Stock Received But Not Billed - _TC",
+				}
+			],
+		}
+	)
 
 	pi.save()
 	return pi
 
+
 def create_sales_invoice(**args):
 	# return sales invoice doc object
-	item = frappe.db.get_value('Item', {'item_name': 'TCS Item'}, "name")
+	item = frappe.db.get_value("Item", {"item_name": "TCS Item"}, "name")
 
 	args = frappe._dict(args)
-	si = frappe.get_doc({
-		"doctype": "Sales Invoice",
-		"posting_date": today(),
-		"customer": args.customer,
-		"company": '_Test Company',
-		"taxes_and_charges": "",
-		"currency": "INR",
-		"debit_to": "Debtors - _TC",
-		"taxes": [],
-		"items": [{
-			'doctype': 'Sales Invoice Item',
-			'item_code': item,
-			'qty': args.qty or 1,
-			'rate': args.rate or 10000,
-			'cost_center': 'Main - _TC',
-			'expense_account': 'Cost of Goods Sold - _TC',
-			'warehouse': args.warehouse or '_Test Warehouse - _TC'
-		}]
-	})
+	si = frappe.get_doc(
+		{
+			"doctype": "Sales Invoice",
+			"posting_date": today(),
+			"customer": args.customer,
+			"company": "_Test Company",
+			"taxes_and_charges": "",
+			"currency": "INR",
+			"debit_to": "Debtors - _TC",
+			"taxes": [],
+			"items": [
+				{
+					"doctype": "Sales Invoice Item",
+					"item_code": item,
+					"qty": args.qty or 1,
+					"rate": args.rate or 10000,
+					"cost_center": "Main - _TC",
+					"expense_account": "Cost of Goods Sold - _TC",
+					"warehouse": args.warehouse or "_Test Warehouse - _TC",
+				}
+			],
+		}
+	)
 
 	si.save()
 	return si
 
+
 def create_records():
 	# create a new suppliers
-	for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2', 'Test TDS Supplier3',
-		'Test TDS Supplier4', 'Test TDS Supplier5']:
-		if frappe.db.exists('Supplier', name):
+	for name in [
+		"Test TDS Supplier",
+		"Test TDS Supplier1",
+		"Test TDS Supplier2",
+		"Test TDS Supplier3",
+		"Test TDS Supplier4",
+		"Test TDS Supplier5",
+	]:
+		if frappe.db.exists("Supplier", name):
 			continue
 
-		frappe.get_doc({
-			"supplier_group": "_Test Supplier Group",
-			"supplier_name": name,
-			"doctype": "Supplier",
-		}).insert()
+		frappe.get_doc(
+			{
+				"supplier_group": "_Test Supplier Group",
+				"supplier_name": name,
+				"doctype": "Supplier",
+			}
+		).insert()
 
-	for name in ['Test TCS Customer']:
-		if frappe.db.exists('Customer', name):
+	for name in ["Test TCS Customer"]:
+		if frappe.db.exists("Customer", name):
 			continue
 
-		frappe.get_doc({
-			"customer_group": "_Test Customer Group",
-			"customer_name": name,
-			"doctype": "Customer"
-		}).insert()
+		frappe.get_doc(
+			{"customer_group": "_Test Customer Group", "customer_name": name, "doctype": "Customer"}
+		).insert()
 
 	# create item
-	if not frappe.db.exists('Item', "TDS Item"):
-		frappe.get_doc({
-			"doctype": "Item",
-			"item_code": "TDS Item",
-			"item_name": "TDS Item",
-			"item_group": "All Item Groups",
-			"is_stock_item": 0,
-		}).insert()
+	if not frappe.db.exists("Item", "TDS Item"):
+		frappe.get_doc(
+			{
+				"doctype": "Item",
+				"item_code": "TDS Item",
+				"item_name": "TDS Item",
+				"item_group": "All Item Groups",
+				"is_stock_item": 0,
+			}
+		).insert()
 
-	if not frappe.db.exists('Item', "TCS Item"):
-		frappe.get_doc({
-			"doctype": "Item",
-			"item_code": "TCS Item",
-			"item_name": "TCS Item",
-			"item_group": "All Item Groups",
-			"is_stock_item": 1
-		}).insert()
+	if not frappe.db.exists("Item", "TCS Item"):
+		frappe.get_doc(
+			{
+				"doctype": "Item",
+				"item_code": "TCS Item",
+				"item_name": "TCS Item",
+				"item_group": "All Item Groups",
+				"is_stock_item": 1,
+			}
+		).insert()
 
 	# create tds account
 	if not frappe.db.exists("Account", "TDS - _TC"):
-		frappe.get_doc({
-			'doctype': 'Account',
-			'company': '_Test Company',
-			'account_name': 'TDS',
-			'parent_account': 'Tax Assets - _TC',
-			'report_type': 'Balance Sheet',
-			'root_type': 'Asset'
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Account",
+				"company": "_Test Company",
+				"account_name": "TDS",
+				"parent_account": "Tax Assets - _TC",
+				"report_type": "Balance Sheet",
+				"root_type": "Asset",
+			}
+		).insert()
 
 	# create tcs account
 	if not frappe.db.exists("Account", "TCS - _TC"):
-		frappe.get_doc({
-			'doctype': 'Account',
-			'company': '_Test Company',
-			'account_name': 'TCS',
-			'parent_account': 'Duties and Taxes - _TC',
-			'report_type': 'Balance Sheet',
-			'root_type': 'Liability'
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Account",
+				"company": "_Test Company",
+				"account_name": "TCS",
+				"parent_account": "Duties and Taxes - _TC",
+				"report_type": "Balance Sheet",
+				"root_type": "Liability",
+			}
+		).insert()
+
 
 def create_tax_with_holding_category():
 	fiscal_year = get_fiscal_year(today(), company="_Test Company")
 	# Cumulative threshold
 	if not frappe.db.exists("Tax Withholding Category", "Cumulative Threshold TDS"):
-		frappe.get_doc({
-			"doctype": "Tax Withholding Category",
-			"name": "Cumulative Threshold TDS",
-			"category_name": "10% TDS",
-			"rates": [{
-				'from_date': fiscal_year[1],
-				'to_date': fiscal_year[2],
-				'tax_withholding_rate': 10,
-				'single_threshold': 0,
-				'cumulative_threshold': 30000.00
-			}],
-			"accounts": [{
-				'company': '_Test Company',
-				'account': 'TDS - _TC'
-			}]
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Tax Withholding Category",
+				"name": "Cumulative Threshold TDS",
+				"category_name": "10% TDS",
+				"rates": [
+					{
+						"from_date": fiscal_year[1],
+						"to_date": fiscal_year[2],
+						"tax_withholding_rate": 10,
+						"single_threshold": 0,
+						"cumulative_threshold": 30000.00,
+					}
+				],
+				"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
+			}
+		).insert()
 
 	if not frappe.db.exists("Tax Withholding Category", "Cumulative Threshold TCS"):
-		frappe.get_doc({
-			"doctype": "Tax Withholding Category",
-			"name": "Cumulative Threshold TCS",
-			"category_name": "10% TCS",
-			"rates": [{
-				'from_date': fiscal_year[1],
-				'to_date': fiscal_year[2],
-				'tax_withholding_rate': 10,
-				'single_threshold': 0,
-				'cumulative_threshold': 30000.00
-			}],
-			"accounts": [{
-				'company': '_Test Company',
-				'account': 'TCS - _TC'
-			}]
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Tax Withholding Category",
+				"name": "Cumulative Threshold TCS",
+				"category_name": "10% TCS",
+				"rates": [
+					{
+						"from_date": fiscal_year[1],
+						"to_date": fiscal_year[2],
+						"tax_withholding_rate": 10,
+						"single_threshold": 0,
+						"cumulative_threshold": 30000.00,
+					}
+				],
+				"accounts": [{"company": "_Test Company", "account": "TCS - _TC"}],
+			}
+		).insert()
 
 	# Single thresold
 	if not frappe.db.exists("Tax Withholding Category", "Single Threshold TDS"):
-		frappe.get_doc({
-			"doctype": "Tax Withholding Category",
-			"name": "Single Threshold TDS",
-			"category_name": "10% TDS",
-			"rates": [{
-				'from_date': fiscal_year[1],
-				'to_date': fiscal_year[2],
-				'tax_withholding_rate': 10,
-				'single_threshold': 20000.00,
-				'cumulative_threshold': 0
-			}],
-			"accounts": [{
-				'company': '_Test Company',
-				'account': 'TDS - _TC'
-			}]
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Tax Withholding Category",
+				"name": "Single Threshold TDS",
+				"category_name": "10% TDS",
+				"rates": [
+					{
+						"from_date": fiscal_year[1],
+						"to_date": fiscal_year[2],
+						"tax_withholding_rate": 10,
+						"single_threshold": 20000.00,
+						"cumulative_threshold": 0,
+					}
+				],
+				"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
+			}
+		).insert()
 
 	if not frappe.db.exists("Tax Withholding Category", "New TDS Category"):
-		frappe.get_doc({
-			"doctype": "Tax Withholding Category",
-			"name": "New TDS Category",
-			"category_name": "New TDS Category",
-			"round_off_tax_amount": 1,
-			"consider_party_ledger_amount": 1,
-			"tax_on_excess_amount": 1,
-			"rates": [{
-				'from_date': fiscal_year[1],
-				'to_date': fiscal_year[2],
-				'tax_withholding_rate': 10,
-				'single_threshold': 0,
-				'cumulative_threshold': 30000
-			}],
-			"accounts": [{
-				'company': '_Test Company',
-				'account': 'TDS - _TC'
-			}]
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Tax Withholding Category",
+				"name": "New TDS Category",
+				"category_name": "New TDS Category",
+				"round_off_tax_amount": 1,
+				"consider_party_ledger_amount": 1,
+				"tax_on_excess_amount": 1,
+				"rates": [
+					{
+						"from_date": fiscal_year[1],
+						"to_date": fiscal_year[2],
+						"tax_withholding_rate": 10,
+						"single_threshold": 0,
+						"cumulative_threshold": 30000,
+					}
+				],
+				"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
+			}
+		).insert()
 
 	if not frappe.db.exists("Tax Withholding Category", "Test Service Category"):
-		frappe.get_doc({
-			"doctype": "Tax Withholding Category",
-			"name": "Test Service Category",
-			"category_name": "Test Service Category",
-			"rates": [{
-				'from_date': fiscal_year[1],
-				'to_date': fiscal_year[2],
-				'tax_withholding_rate': 10,
-				'single_threshold': 2000,
-				'cumulative_threshold': 2000
-			}],
-			"accounts": [{
-				'company': '_Test Company',
-				'account': 'TDS - _TC'
-			}]
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Tax Withholding Category",
+				"name": "Test Service Category",
+				"category_name": "Test Service Category",
+				"rates": [
+					{
+						"from_date": fiscal_year[1],
+						"to_date": fiscal_year[2],
+						"tax_withholding_rate": 10,
+						"single_threshold": 2000,
+						"cumulative_threshold": 2000,
+					}
+				],
+				"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
+			}
+		).insert()
 
 	if not frappe.db.exists("Tax Withholding Category", "Test Goods Category"):
-		frappe.get_doc({
-			"doctype": "Tax Withholding Category",
-			"name": "Test Goods Category",
-			"category_name": "Test Goods Category",
-			"rates": [{
-				'from_date': fiscal_year[1],
-				'to_date': fiscal_year[2],
-				'tax_withholding_rate': 10,
-				'single_threshold': 2000,
-				'cumulative_threshold': 2000
-			}],
-			"accounts": [{
-				'company': '_Test Company',
-				'account': 'TDS - _TC'
-			}]
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Tax Withholding Category",
+				"name": "Test Goods Category",
+				"category_name": "Test Goods Category",
+				"rates": [
+					{
+						"from_date": fiscal_year[1],
+						"to_date": fiscal_year[2],
+						"tax_withholding_rate": 10,
+						"single_threshold": 2000,
+						"cumulative_threshold": 2000,
+					}
+				],
+				"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
+			}
+		).insert()
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index 0cd5e86..8146804 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -14,25 +14,69 @@
 	get_accounting_dimensions,
 )
 from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
+from erpnext.accounts.utils import create_payment_ledger_entry
 
 
-class ClosedAccountingPeriod(frappe.ValidationError): pass
+class ClosedAccountingPeriod(frappe.ValidationError):
+	pass
 
-def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, update_outstanding='Yes', from_repost=False):
+
+def make_gl_entries(
+	gl_map,
+	cancel=False,
+	adv_adj=False,
+	merge_entries=True,
+	update_outstanding="Yes",
+	from_repost=False,
+):
 	if gl_map:
 		if not cancel:
 			validate_accounting_period(gl_map)
+			validate_disabled_accounts(gl_map)
 			gl_map = process_gl_map(gl_map, merge_entries)
 			if gl_map and len(gl_map) > 1:
+				create_payment_ledger_entry(
+					gl_map,
+					cancel=0,
+					adv_adj=adv_adj,
+					update_outstanding=update_outstanding,
+					from_repost=from_repost,
+				)
 				save_entries(gl_map, adv_adj, update_outstanding, from_repost)
 			# Post GL Map proccess there may no be any GL Entries
 			elif gl_map:
-				frappe.throw(_("Incorrect number of General Ledger Entries found. You might have selected a wrong Account in the transaction."))
+				frappe.throw(
+					_(
+						"Incorrect number of General Ledger Entries found. You might have selected a wrong Account in the transaction."
+					)
+				)
 		else:
 			make_reverse_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding)
 
+
+def validate_disabled_accounts(gl_map):
+	accounts = [d.account for d in gl_map if d.account]
+
+	Account = frappe.qb.DocType("Account")
+
+	disabled_accounts = (
+		frappe.qb.from_(Account)
+		.where(Account.name.isin(accounts) & Account.disabled == 1)
+		.select(Account.name, Account.disabled)
+	).run(as_dict=True)
+
+	if disabled_accounts:
+		account_list = "<br>"
+		account_list += ", ".join([frappe.bold(d.name) for d in disabled_accounts])
+		frappe.throw(
+			_("Cannot create accounting entries against disabled accounts: {0}").format(account_list),
+			title=_("Disabled Account Selected"),
+		)
+
+
 def validate_accounting_period(gl_map):
-	accounting_periods = frappe.db.sql(""" SELECT
+	accounting_periods = frappe.db.sql(
+		""" SELECT
 			ap.name as name
 		FROM
 			`tabAccounting Period` ap, `tabClosed Document` cd
@@ -42,15 +86,23 @@
 			AND cd.closed = 1
 			AND cd.document_type = %(voucher_type)s
 			AND %(date)s between ap.start_date and ap.end_date
-			""", {
-				'date': gl_map[0].posting_date,
-				'company': gl_map[0].company,
-				'voucher_type': gl_map[0].voucher_type
-			}, as_dict=1)
+			""",
+		{
+			"date": gl_map[0].posting_date,
+			"company": gl_map[0].company,
+			"voucher_type": gl_map[0].voucher_type,
+		},
+		as_dict=1,
+	)
 
 	if accounting_periods:
-		frappe.throw(_("You cannot create or cancel any accounting entries with in the closed Accounting Period {0}")
-			.format(frappe.bold(accounting_periods[0].name)), ClosedAccountingPeriod)
+		frappe.throw(
+			_(
+				"You cannot create or cancel any accounting entries with in the closed Accounting Period {0}"
+			).format(frappe.bold(accounting_periods[0].name)),
+			ClosedAccountingPeriod,
+		)
+
 
 def process_gl_map(gl_map, merge_entries=True, precision=None):
 	if not gl_map:
@@ -65,8 +117,11 @@
 
 	return gl_map
 
+
 def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None):
-	cost_center_allocation = get_cost_center_allocation_data(gl_map[0]["company"], gl_map[0]["posting_date"])
+	cost_center_allocation = get_cost_center_allocation_data(
+		gl_map[0]["company"], gl_map[0]["posting_date"]
+	)
 	if not cost_center_allocation:
 		return gl_map
 
@@ -85,12 +140,15 @@
 
 	return new_gl_map
 
+
 def get_cost_center_allocation_data(company, posting_date):
 	par = frappe.qb.DocType("Cost Center Allocation")
 	child = frappe.qb.DocType("Cost Center Allocation Percentage")
 
 	records = (
-		frappe.qb.from_(par).inner_join(child).on(par.name == child.parent)
+		frappe.qb.from_(par)
+		.inner_join(child)
+		.on(par.name == child.parent)
 		.select(par.main_cost_center, child.cost_center, child.percentage)
 		.where(par.docstatus == 1)
 		.where(par.company == company)
@@ -100,11 +158,13 @@
 
 	cc_allocation = frappe._dict()
 	for d in records:
-		cc_allocation.setdefault(d.main_cost_center, frappe._dict())\
-			.setdefault(d.cost_center, d.percentage)
+		cc_allocation.setdefault(d.main_cost_center, frappe._dict()).setdefault(
+			d.cost_center, d.percentage
+		)
 
 	return cc_allocation
 
+
 def merge_similar_entries(gl_map, precision=None):
 	merged_gl_map = []
 	accounting_dimensions = get_accounting_dimensions()
@@ -113,12 +173,14 @@
 		# to that entry
 		same_head = check_if_in_list(entry, merged_gl_map, accounting_dimensions)
 		if same_head:
-			same_head.debit	= flt(same_head.debit) + flt(entry.debit)
-			same_head.debit_in_account_currency	= \
-				flt(same_head.debit_in_account_currency) + flt(entry.debit_in_account_currency)
+			same_head.debit = flt(same_head.debit) + flt(entry.debit)
+			same_head.debit_in_account_currency = flt(same_head.debit_in_account_currency) + flt(
+				entry.debit_in_account_currency
+			)
 			same_head.credit = flt(same_head.credit) + flt(entry.credit)
-			same_head.credit_in_account_currency = \
-				flt(same_head.credit_in_account_currency) + flt(entry.credit_in_account_currency)
+			same_head.credit_in_account_currency = flt(same_head.credit_in_account_currency) + flt(
+				entry.credit_in_account_currency
+			)
 		else:
 			merged_gl_map.append(entry)
 
@@ -129,14 +191,25 @@
 		precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
 
 	# filter zero debit and credit entries
-	merged_gl_map = filter(lambda x: flt(x.debit, precision)!=0 or flt(x.credit, precision)!=0, merged_gl_map)
+	merged_gl_map = filter(
+		lambda x: flt(x.debit, precision) != 0 or flt(x.credit, precision) != 0, merged_gl_map
+	)
 	merged_gl_map = list(merged_gl_map)
 
 	return merged_gl_map
 
+
 def check_if_in_list(gle, gl_map, dimensions=None):
-	account_head_fieldnames = ['voucher_detail_no', 'party', 'against_voucher',
-			'cost_center', 'against_voucher_type', 'party_type', 'project', 'finance_book']
+	account_head_fieldnames = [
+		"voucher_detail_no",
+		"party",
+		"against_voucher",
+		"cost_center",
+		"against_voucher_type",
+		"party_type",
+		"project",
+		"finance_book",
+	]
 
 	if dimensions:
 		account_head_fieldnames = account_head_fieldnames + dimensions
@@ -155,6 +228,7 @@
 		if same_head:
 			return e
 
+
 def toggle_debit_credit_if_negative(gl_map):
 	for entry in gl_map:
 		# toggle debit, credit if negative entry
@@ -163,8 +237,9 @@
 			entry.debit = 0.0
 
 		if flt(entry.debit_in_account_currency) < 0:
-			entry.credit_in_account_currency = \
-				flt(entry.credit_in_account_currency) - flt(entry.debit_in_account_currency)
+			entry.credit_in_account_currency = flt(entry.credit_in_account_currency) - flt(
+				entry.debit_in_account_currency
+			)
 			entry.debit_in_account_currency = 0.0
 
 		if flt(entry.credit) < 0:
@@ -172,37 +247,42 @@
 			entry.credit = 0.0
 
 		if flt(entry.credit_in_account_currency) < 0:
-			entry.debit_in_account_currency = \
-				flt(entry.debit_in_account_currency) - flt(entry.credit_in_account_currency)
+			entry.debit_in_account_currency = flt(entry.debit_in_account_currency) - flt(
+				entry.credit_in_account_currency
+			)
 			entry.credit_in_account_currency = 0.0
 
 		update_net_values(entry)
 
 	return gl_map
 
+
 def update_net_values(entry):
 	# In some scenarios net value needs to be shown in the ledger
 	# This method updates net values as debit or credit
 	if entry.post_net_value and entry.debit and entry.credit:
 		if entry.debit > entry.credit:
 			entry.debit = entry.debit - entry.credit
-			entry.debit_in_account_currency = entry.debit_in_account_currency \
-				- entry.credit_in_account_currency
+			entry.debit_in_account_currency = (
+				entry.debit_in_account_currency - entry.credit_in_account_currency
+			)
 			entry.credit = 0
 			entry.credit_in_account_currency = 0
 		else:
 			entry.credit = entry.credit - entry.debit
-			entry.credit_in_account_currency = entry.credit_in_account_currency \
-				- entry.debit_in_account_currency
+			entry.credit_in_account_currency = (
+				entry.credit_in_account_currency - entry.debit_in_account_currency
+			)
 
 			entry.debit = 0
 			entry.debit_in_account_currency = 0
 
+
 def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
 	if not from_repost:
 		validate_cwip_accounts(gl_map)
 
-	round_off_debit_credit(gl_map)
+	process_debit_credit_difference(gl_map)
 
 	if gl_map:
 		check_freezing_date(gl_map[0]["posting_date"], adv_adj)
@@ -210,37 +290,70 @@
 	for entry in gl_map:
 		make_entry(entry, adv_adj, update_outstanding, from_repost)
 
+
 def make_entry(args, adv_adj, update_outstanding, from_repost=False):
 	gle = frappe.new_doc("GL Entry")
 	gle.update(args)
 	gle.flags.ignore_permissions = 1
 	gle.flags.from_repost = from_repost
 	gle.flags.adv_adj = adv_adj
-	gle.flags.update_outstanding = update_outstanding or 'Yes'
+	gle.flags.update_outstanding = update_outstanding or "Yes"
 	gle.submit()
 
 	if not from_repost:
 		validate_expense_against_budget(args)
 
+
 def validate_cwip_accounts(gl_map):
 	"""Validate that CWIP account are not used in Journal Entry"""
 	if gl_map and gl_map[0].voucher_type != "Journal Entry":
 		return
 
-	cwip_enabled = any(cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category", "enable_cwip_accounting"))
+	cwip_enabled = any(
+		cint(ac.enable_cwip_accounting)
+		for ac in frappe.db.get_all("Asset Category", "enable_cwip_accounting")
+	)
 	if cwip_enabled:
-		cwip_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount
-			where account_type = 'Capital Work in Progress' and is_group=0""")]
+		cwip_accounts = [
+			d[0]
+			for d in frappe.db.sql(
+				"""select name from tabAccount
+			where account_type = 'Capital Work in Progress' and is_group=0"""
+			)
+		]
 
 		for entry in gl_map:
 			if entry.account in cwip_accounts:
 				frappe.throw(
-					_("Account: <b>{0}</b> is capital Work in progress and can not be updated by Journal Entry").format(entry.account))
+					_(
+						"Account: <b>{0}</b> is capital Work in progress and can not be updated by Journal Entry"
+					).format(entry.account)
+				)
 
-def round_off_debit_credit(gl_map):
-	precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"),
-		currency=frappe.get_cached_value('Company',  gl_map[0].company,  "default_currency"))
 
+def process_debit_credit_difference(gl_map):
+	precision = get_field_precision(
+		frappe.get_meta("GL Entry").get_field("debit"),
+		currency=frappe.get_cached_value("Company", gl_map[0].company, "default_currency"),
+	)
+
+	voucher_type = gl_map[0].voucher_type
+	voucher_no = gl_map[0].voucher_no
+	allowance = get_debit_credit_allowance(voucher_type, precision)
+
+	debit_credit_diff = get_debit_credit_difference(gl_map, precision)
+	if abs(debit_credit_diff) > allowance:
+		raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no)
+
+	elif abs(debit_credit_diff) >= (1.0 / (10**precision)):
+		make_round_off_gle(gl_map, debit_credit_diff, precision)
+
+	debit_credit_diff = get_debit_credit_difference(gl_map, precision)
+	if abs(debit_credit_diff) > allowance:
+		raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no)
+
+
+def get_debit_credit_difference(gl_map, precision):
 	debit_credit_diff = 0.0
 	for entry in gl_map:
 		entry.debit = flt(entry.debit, precision)
@@ -249,20 +362,30 @@
 
 	debit_credit_diff = flt(debit_credit_diff, precision)
 
-	if gl_map[0]["voucher_type"] in ("Journal Entry", "Payment Entry"):
+	return debit_credit_diff
+
+
+def get_debit_credit_allowance(voucher_type, precision):
+	if voucher_type in ("Journal Entry", "Payment Entry"):
 		allowance = 5.0 / (10**precision)
 	else:
-		allowance = .5
+		allowance = 0.5
 
-	if abs(debit_credit_diff) > allowance:
-		frappe.throw(_("Debit and Credit not equal for {0} #{1}. Difference is {2}.")
-			.format(gl_map[0].voucher_type, gl_map[0].voucher_no, debit_credit_diff))
+	return allowance
 
-	elif abs(debit_credit_diff) >= (1.0 / (10**precision)):
-		make_round_off_gle(gl_map, debit_credit_diff, precision)
+
+def raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no):
+	frappe.throw(
+		_("Debit and Credit not equal for {0} #{1}. Difference is {2}.").format(
+			voucher_type, voucher_no, debit_credit_diff
+		)
+	)
+
 
 def make_round_off_gle(gl_map, debit_credit_diff, precision):
-	round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(gl_map[0].company)
+	round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
+		gl_map[0].company, gl_map[0].voucher_type, gl_map[0].voucher_no
+	)
 	round_off_account_exists = False
 	round_off_gle = frappe._dict()
 	for d in gl_map:
@@ -279,30 +402,62 @@
 		return
 
 	if not round_off_gle:
-		for k in ["voucher_type", "voucher_no", "company",
-			"posting_date", "remarks"]:
-				round_off_gle[k] = gl_map[0][k]
+		for k in ["voucher_type", "voucher_no", "company", "posting_date", "remarks"]:
+			round_off_gle[k] = gl_map[0][k]
 
-	round_off_gle.update({
-		"account": round_off_account,
-		"debit_in_account_currency": abs(debit_credit_diff) if debit_credit_diff < 0 else 0,
-		"credit_in_account_currency": debit_credit_diff if debit_credit_diff > 0 else 0,
-		"debit": abs(debit_credit_diff) if debit_credit_diff < 0 else 0,
-		"credit": debit_credit_diff if debit_credit_diff > 0 else 0,
-		"cost_center": round_off_cost_center,
-		"party_type": None,
-		"party": None,
-		"is_opening": "No",
-		"against_voucher_type": None,
-		"against_voucher": None
-	})
+	round_off_gle.update(
+		{
+			"account": round_off_account,
+			"debit_in_account_currency": abs(debit_credit_diff) if debit_credit_diff < 0 else 0,
+			"credit_in_account_currency": debit_credit_diff if debit_credit_diff > 0 else 0,
+			"debit": abs(debit_credit_diff) if debit_credit_diff < 0 else 0,
+			"credit": debit_credit_diff if debit_credit_diff > 0 else 0,
+			"cost_center": round_off_cost_center,
+			"party_type": None,
+			"party": None,
+			"is_opening": "No",
+			"against_voucher_type": None,
+			"against_voucher": None,
+		}
+	)
+
+	update_accounting_dimensions(round_off_gle)
 
 	if not round_off_account_exists:
 		gl_map.append(round_off_gle)
 
-def get_round_off_account_and_cost_center(company):
-	round_off_account, round_off_cost_center = frappe.get_cached_value('Company',  company,
-		["round_off_account", "round_off_cost_center"]) or [None, None]
+
+def update_accounting_dimensions(round_off_gle):
+	dimensions = get_accounting_dimensions()
+	meta = frappe.get_meta(round_off_gle["voucher_type"])
+	has_all_dimensions = True
+
+	for dimension in dimensions:
+		if not meta.has_field(dimension):
+			has_all_dimensions = False
+
+	if dimensions and has_all_dimensions:
+		dimension_values = frappe.db.get_value(
+			round_off_gle["voucher_type"], round_off_gle["voucher_no"], dimensions, as_dict=1
+		)
+
+		for dimension in dimensions:
+			round_off_gle[dimension] = dimension_values.get(dimension)
+
+
+def get_round_off_account_and_cost_center(company, voucher_type, voucher_no):
+	round_off_account, round_off_cost_center = frappe.get_cached_value(
+		"Company", company, ["round_off_account", "round_off_cost_center"]
+	) or [None, None]
+
+	meta = frappe.get_meta(voucher_type)
+
+	# Give first preference to parent cost center for round off GLE
+	if meta.has_field("cost_center"):
+		parent_cost_center = frappe.db.get_value(voucher_type, voucher_no, "cost_center")
+		if parent_cost_center:
+			round_off_cost_center = parent_cost_center
+
 	if not round_off_account:
 		frappe.throw(_("Please mention Round Off Account in Company"))
 
@@ -311,74 +466,87 @@
 
 	return round_off_account, round_off_cost_center
 
-def make_reverse_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
-	adv_adj=False, update_outstanding="Yes"):
+
+def make_reverse_gl_entries(
+	gl_entries=None, voucher_type=None, voucher_no=None, adv_adj=False, update_outstanding="Yes"
+):
 	"""
-		Get original gl entries of the voucher
-		and make reverse gl entries by swapping debit and credit
+	Get original gl entries of the voucher
+	and make reverse gl entries by swapping debit and credit
 	"""
 
 	if not gl_entries:
 		gl_entry = frappe.qb.DocType("GL Entry")
-		gl_entries = (frappe.qb.from_(
-			gl_entry
-		).select(
-			'*'
-		).where(
-			gl_entry.voucher_type == voucher_type
-		).where(
-			gl_entry.voucher_no == voucher_no
-		).where(
-			gl_entry.is_cancelled == 0
-		).for_update()).run(as_dict=1)
+		gl_entries = (
+			frappe.qb.from_(gl_entry)
+			.select("*")
+			.where(gl_entry.voucher_type == voucher_type)
+			.where(gl_entry.voucher_no == voucher_no)
+			.where(gl_entry.is_cancelled == 0)
+			.for_update()
+		).run(as_dict=1)
 
 	if gl_entries:
+		create_payment_ledger_entry(gl_entries, cancel=1)
+		create_payment_ledger_entry(
+			gl_entries, cancel=1, adv_adj=adv_adj, update_outstanding=update_outstanding
+		)
 		validate_accounting_period(gl_entries)
 		check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
-		set_as_cancel(gl_entries[0]['voucher_type'], gl_entries[0]['voucher_no'])
+		set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"])
 
 		for entry in gl_entries:
 			new_gle = copy.deepcopy(entry)
-			new_gle['name'] = None
-			debit = new_gle.get('debit', 0)
-			credit = new_gle.get('credit', 0)
+			new_gle["name"] = None
+			debit = new_gle.get("debit", 0)
+			credit = new_gle.get("credit", 0)
 
-			debit_in_account_currency = new_gle.get('debit_in_account_currency', 0)
-			credit_in_account_currency = new_gle.get('credit_in_account_currency', 0)
+			debit_in_account_currency = new_gle.get("debit_in_account_currency", 0)
+			credit_in_account_currency = new_gle.get("credit_in_account_currency", 0)
 
-			new_gle['debit'] = credit
-			new_gle['credit'] = debit
-			new_gle['debit_in_account_currency'] = credit_in_account_currency
-			new_gle['credit_in_account_currency'] = debit_in_account_currency
+			new_gle["debit"] = credit
+			new_gle["credit"] = debit
+			new_gle["debit_in_account_currency"] = credit_in_account_currency
+			new_gle["credit_in_account_currency"] = debit_in_account_currency
 
-			new_gle['remarks'] = "On cancellation of " + new_gle['voucher_no']
-			new_gle['is_cancelled'] = 1
+			new_gle["remarks"] = "On cancellation of " + new_gle["voucher_no"]
+			new_gle["is_cancelled"] = 1
 
-			if new_gle['debit'] or new_gle['credit']:
+			if new_gle["debit"] or new_gle["credit"]:
 				make_entry(new_gle, adv_adj, "Yes")
 
 
 def check_freezing_date(posting_date, adv_adj=False):
 	"""
-		Nobody can do GL Entries where posting date is before freezing date
-		except authorized person
+	Nobody can do GL Entries where posting date is before freezing date
+	except authorized person
 
-		Administrator has all the roles so this check will be bypassed if any role is allowed to post
-		Hence stop admin to bypass if accounts are freezed
+	Administrator has all the roles so this check will be bypassed if any role is allowed to post
+	Hence stop admin to bypass if accounts are freezed
 	"""
 	if not adv_adj:
-		acc_frozen_upto = frappe.db.get_value('Accounts Settings', None, 'acc_frozen_upto')
+		acc_frozen_upto = frappe.db.get_value("Accounts Settings", None, "acc_frozen_upto")
 		if acc_frozen_upto:
-			frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier')
-			if getdate(posting_date) <= getdate(acc_frozen_upto) \
-					and (frozen_accounts_modifier not in frappe.get_roles() or frappe.session.user == 'Administrator'):
-				frappe.throw(_("You are not authorized to add or update entries before {0}").format(formatdate(acc_frozen_upto)))
+			frozen_accounts_modifier = frappe.db.get_value(
+				"Accounts Settings", None, "frozen_accounts_modifier"
+			)
+			if getdate(posting_date) <= getdate(acc_frozen_upto) and (
+				frozen_accounts_modifier not in frappe.get_roles() or frappe.session.user == "Administrator"
+			):
+				frappe.throw(
+					_("You are not authorized to add or update entries before {0}").format(
+						formatdate(acc_frozen_upto)
+					)
+				)
+
 
 def set_as_cancel(voucher_type, voucher_no):
 	"""
-		Set is_cancelled=1 in all original gl entries for the voucher
+	Set is_cancelled=1 in all original gl entries for the voucher
 	"""
-	frappe.db.sql("""UPDATE `tabGL Entry` SET is_cancelled = 1,
+	frappe.db.sql(
+		"""UPDATE `tabGL Entry` SET is_cancelled = 1,
 		modified=%s, modified_by=%s
 		where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
-		(now(), frappe.session.user, voucher_type, voucher_no))
+		(now(), frappe.session.user, voucher_type, voucher_no),
+	)
diff --git a/erpnext/accounts/module_onboarding/accounts/accounts.json b/erpnext/accounts/module_onboarding/accounts/accounts.json
index aa7cdf7..9916d16 100644
--- a/erpnext/accounts/module_onboarding/accounts/accounts.json
+++ b/erpnext/accounts/module_onboarding/accounts/accounts.json
@@ -13,7 +13,7 @@
  "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/accounts",
  "idx": 0,
  "is_complete": 0,
- "modified": "2022-01-18 18:35:52.326688",
+ "modified": "2022-06-14 17:38:24.967834",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounts",
diff --git a/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json b/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json
index 67553ba..0973ab3 100644
--- a/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json
+++ b/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json
@@ -1,8 +1,8 @@
 {
- "action": "Watch Video",
+ "action": "Go to Page",
  "action_label": "Learn more about Chart of Accounts",
  "callback_message": "You can continue with the onboarding after exploring this page",
- "callback_title": "Awesome Work",
+ "callback_title": "Explore Chart of Accounts",
  "creation": "2020-05-13 19:58:20.928127",
  "description": "# Chart Of Accounts\n\nERPNext sets up a simple chart of accounts for each Company you create, but you can modify it according to business and legal requirements.",
  "docstatus": 0,
@@ -12,7 +12,7 @@
  "is_complete": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2021-08-13 11:46:25.878506",
+ "modified": "2022-06-07 14:21:26.264769",
  "modified_by": "Administrator",
  "name": "Chart of Accounts",
  "owner": "Administrator",
diff --git a/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json b/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json
index 9f4c873..e323f6c 100644
--- a/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json
+++ b/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json
@@ -2,14 +2,14 @@
  "action": "Create Entry",
  "action_label": "Manage Sales Tax Templates",
  "creation": "2020-05-13 19:29:43.844463",
- "description": "# Setting up Taxes\n\nERPNext lets you configure your taxes so that they are automatically applied in your buying and selling transactions. You can configure them globally or even on Items. ERPNext taxes are pre-configured for most regions.\n",
+ "description": "# Setting up Taxes\n\nERPNext lets you configure your taxes so that they are automatically applied in your buying and selling transactions. You can configure them globally or even on Items. ERPNext taxes are pre-configured for most regions.",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2021-08-13 11:48:37.238610",
+ "modified": "2022-06-14 17:37:56.694261",
  "modified_by": "Administrator",
  "name": "Setup Taxes",
  "owner": "Administrator",
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index d6f6c5b..e39f22b 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -33,125 +33,230 @@
 from erpnext.exceptions import InvalidAccountCurrency, PartyDisabled, PartyFrozen
 
 
-class DuplicatePartyAccountError(frappe.ValidationError): pass
+class DuplicatePartyAccountError(frappe.ValidationError):
+	pass
+
 
 @frappe.whitelist()
-def get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None,
-	bill_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False, fetch_payment_terms_template=True,
-	party_address=None, company_address=None, shipping_address=None, pos_profile=None):
+def get_party_details(
+	party=None,
+	account=None,
+	party_type="Customer",
+	company=None,
+	posting_date=None,
+	bill_date=None,
+	price_list=None,
+	currency=None,
+	doctype=None,
+	ignore_permissions=False,
+	fetch_payment_terms_template=True,
+	party_address=None,
+	company_address=None,
+	shipping_address=None,
+	pos_profile=None,
+):
 
 	if not party:
 		return {}
 	if not frappe.db.exists(party_type, party):
 		frappe.throw(_("{0}: {1} does not exists").format(party_type, party))
-	return _get_party_details(party, account, party_type,
-		company, posting_date, bill_date, price_list, currency, doctype, ignore_permissions,
-		fetch_payment_terms_template, party_address, company_address, shipping_address, pos_profile)
+	return _get_party_details(
+		party,
+		account,
+		party_type,
+		company,
+		posting_date,
+		bill_date,
+		price_list,
+		currency,
+		doctype,
+		ignore_permissions,
+		fetch_payment_terms_template,
+		party_address,
+		company_address,
+		shipping_address,
+		pos_profile,
+	)
 
-def _get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None,
-	bill_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False,
-	fetch_payment_terms_template=True, party_address=None, company_address=None, shipping_address=None, pos_profile=None):
-	party_details = frappe._dict(set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype))
+
+def _get_party_details(
+	party=None,
+	account=None,
+	party_type="Customer",
+	company=None,
+	posting_date=None,
+	bill_date=None,
+	price_list=None,
+	currency=None,
+	doctype=None,
+	ignore_permissions=False,
+	fetch_payment_terms_template=True,
+	party_address=None,
+	company_address=None,
+	shipping_address=None,
+	pos_profile=None,
+):
+	party_details = frappe._dict(
+		set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype)
+	)
 	party = party_details[party_type.lower()]
 
-	if not ignore_permissions and not (frappe.has_permission(party_type, "read", party) or frappe.has_permission(party_type, "select", party)):
+	if not ignore_permissions and not (
+		frappe.has_permission(party_type, "read", party)
+		or frappe.has_permission(party_type, "select", party)
+	):
 		frappe.throw(_("Not permitted for {0}").format(party), frappe.PermissionError)
 
 	party = frappe.get_doc(party_type, party)
 	currency = party.get("default_currency") or currency or get_company_currency(company)
 
-	party_address, shipping_address = set_address_details(party_details, party, party_type, doctype, company, party_address, company_address, shipping_address)
+	party_address, shipping_address = set_address_details(
+		party_details,
+		party,
+		party_type,
+		doctype,
+		company,
+		party_address,
+		company_address,
+		shipping_address,
+	)
 	set_contact_details(party_details, party, party_type)
 	set_other_values(party_details, party, party_type)
 	set_price_list(party_details, party, party_type, price_list, pos_profile)
 
-	party_details["tax_category"] = get_address_tax_category(party.get("tax_category"),
-		party_address, shipping_address if party_type != "Supplier" else party_address)
+	party_details["tax_category"] = get_address_tax_category(
+		party.get("tax_category"),
+		party_address,
+		shipping_address if party_type != "Supplier" else party_address,
+	)
 
-	tax_template = set_taxes(party.name, party_type, posting_date, company,
-		customer_group=party_details.customer_group, supplier_group=party_details.supplier_group, tax_category=party_details.tax_category,
-		billing_address=party_address, shipping_address=shipping_address)
+	tax_template = set_taxes(
+		party.name,
+		party_type,
+		posting_date,
+		company,
+		customer_group=party_details.customer_group,
+		supplier_group=party_details.supplier_group,
+		tax_category=party_details.tax_category,
+		billing_address=party_address,
+		shipping_address=shipping_address,
+	)
 
 	if tax_template:
-		party_details['taxes_and_charges'] = tax_template
+		party_details["taxes_and_charges"] = tax_template
 
 	if cint(fetch_payment_terms_template):
-		party_details["payment_terms_template"] = get_payment_terms_template(party.name, party_type, company)
+		party_details["payment_terms_template"] = get_payment_terms_template(
+			party.name, party_type, company
+		)
 
 	if not party_details.get("currency"):
 		party_details["currency"] = currency
 
 	# sales team
-	if party_type=="Customer":
-		party_details["sales_team"] = [{
-			"sales_person": d.sales_person,
-			"allocated_percentage": d.allocated_percentage or None,
-			"commission_rate": d.commission_rate
-		} for d in party.get("sales_team")]
+	if party_type == "Customer":
+		party_details["sales_team"] = [
+			{
+				"sales_person": d.sales_person,
+				"allocated_percentage": d.allocated_percentage or None,
+				"commission_rate": d.commission_rate,
+			}
+			for d in party.get("sales_team")
+		]
 
 	# supplier tax withholding category
 	if party_type == "Supplier" and party:
-		party_details["supplier_tds"] = frappe.get_value(party_type, party.name, "tax_withholding_category")
+		party_details["supplier_tds"] = frappe.get_value(
+			party_type, party.name, "tax_withholding_category"
+		)
 
 	return party_details
 
-def set_address_details(party_details, party, party_type, doctype=None, company=None, party_address=None, company_address=None, shipping_address=None):
-	billing_address_field = "customer_address" if party_type == "Lead" \
-		else party_type.lower() + "_address"
-	party_details[billing_address_field] = party_address or get_default_address(party_type, party.name)
+
+def set_address_details(
+	party_details,
+	party,
+	party_type,
+	doctype=None,
+	company=None,
+	party_address=None,
+	company_address=None,
+	shipping_address=None,
+):
+	billing_address_field = (
+		"customer_address" if party_type == "Lead" else party_type.lower() + "_address"
+	)
+	party_details[billing_address_field] = party_address or get_default_address(
+		party_type, party.name
+	)
 	if doctype:
-		party_details.update(get_fetch_values(doctype, billing_address_field, party_details[billing_address_field]))
+		party_details.update(
+			get_fetch_values(doctype, billing_address_field, party_details[billing_address_field])
+		)
 	# address display
 	party_details.address_display = get_address_display(party_details[billing_address_field])
 	# shipping address
 	if party_type in ["Customer", "Lead"]:
-		party_details.shipping_address_name = shipping_address or get_party_shipping_address(party_type, party.name)
+		party_details.shipping_address_name = shipping_address or get_party_shipping_address(
+			party_type, party.name
+		)
 		party_details.shipping_address = get_address_display(party_details["shipping_address_name"])
 		if doctype:
-			party_details.update(get_fetch_values(doctype, 'shipping_address_name', party_details.shipping_address_name))
+			party_details.update(
+				get_fetch_values(doctype, "shipping_address_name", party_details.shipping_address_name)
+			)
 
 	if company_address:
-		party_details.update({'company_address': company_address})
+		party_details.update({"company_address": company_address})
 	else:
 		party_details.update(get_company_address(company))
 
-	if doctype and doctype in ['Delivery Note', 'Sales Invoice', 'Sales Order']:
+	if doctype and doctype in ["Delivery Note", "Sales Invoice", "Sales Order", "Quotation"]:
 		if party_details.company_address:
-			party_details.update(get_fetch_values(doctype, 'company_address', party_details.company_address))
+			party_details.update(
+				get_fetch_values(doctype, "company_address", party_details.company_address)
+			)
 		get_regional_address_details(party_details, doctype, company)
 
 	elif doctype and doctype in ["Purchase Invoice", "Purchase Order", "Purchase Receipt"]:
 		if party_details.company_address:
 			party_details["shipping_address"] = shipping_address or party_details["company_address"]
 			party_details.shipping_address_display = get_address_display(party_details["shipping_address"])
-			party_details.update(get_fetch_values(doctype, 'shipping_address', party_details.shipping_address))
+			party_details.update(
+				get_fetch_values(doctype, "shipping_address", party_details.shipping_address)
+			)
 		get_regional_address_details(party_details, doctype, company)
 
 	return party_details.get(billing_address_field), party_details.shipping_address_name
 
+
 @erpnext.allow_regional
 def get_regional_address_details(party_details, doctype, company):
 	pass
 
+
 def set_contact_details(party_details, party, party_type):
 	party_details.contact_person = get_default_contact(party_type, party.name)
 
 	if not party_details.contact_person:
-		party_details.update({
-			"contact_person": None,
-			"contact_display": None,
-			"contact_email": None,
-			"contact_mobile": None,
-			"contact_phone": None,
-			"contact_designation": None,
-			"contact_department": None
-		})
+		party_details.update(
+			{
+				"contact_person": None,
+				"contact_display": None,
+				"contact_email": None,
+				"contact_mobile": None,
+				"contact_phone": None,
+				"contact_designation": None,
+				"contact_department": None,
+			}
+		)
 	else:
 		party_details.update(get_contact_details(party_details.contact_person))
 
+
 def set_other_values(party_details, party, party_type):
 	# copy
-	if party_type=="Customer":
+	if party_type == "Customer":
 		to_copy = ["customer_name", "customer_group", "territory", "language"]
 	else:
 		to_copy = ["supplier_name", "supplier_group", "language"]
@@ -159,112 +264,121 @@
 		party_details[f] = party.get(f)
 
 	# fields prepended with default in Customer doctype
-	for f in ['currency'] \
-		+ (['sales_partner', 'commission_rate'] if party_type=="Customer" else []):
+	for f in ["currency"] + (
+		["sales_partner", "commission_rate"] if party_type == "Customer" else []
+	):
 		if party.get("default_" + f):
 			party_details[f] = party.get("default_" + f)
 
+
 def get_default_price_list(party):
 	"""Return default price list for party (Document object)"""
 	if party.get("default_price_list"):
 		return party.default_price_list
 
 	if party.doctype == "Customer":
-		price_list =  frappe.get_cached_value("Customer Group",
-			party.customer_group, "default_price_list")
-		if price_list:
-			return price_list
+		return frappe.db.get_value("Customer Group", party.customer_group, "default_price_list")
 
-	return None
 
 def set_price_list(party_details, party, party_type, given_price_list, pos=None):
 	# price list
-	price_list = get_permitted_documents('Price List')
+	price_list = get_permitted_documents("Price List")
 
 	# if there is only one permitted document based on user permissions, set it
 	if price_list and len(price_list) == 1:
 		price_list = price_list[0]
-	elif pos and party_type == 'Customer':
-		customer_price_list = frappe.get_value('Customer', party.name, 'default_price_list')
+	elif pos and party_type == "Customer":
+		customer_price_list = frappe.get_value("Customer", party.name, "default_price_list")
 
 		if customer_price_list:
 			price_list = customer_price_list
 		else:
-			pos_price_list = frappe.get_value('POS Profile', pos, 'selling_price_list')
+			pos_price_list = frappe.get_value("POS Profile", pos, "selling_price_list")
 			price_list = pos_price_list or given_price_list
 	else:
 		price_list = get_default_price_list(party) or given_price_list
 
 	if price_list:
-		party_details.price_list_currency = frappe.db.get_value("Price List", price_list, "currency", cache=True)
+		party_details.price_list_currency = frappe.db.get_value(
+			"Price List", price_list, "currency", cache=True
+		)
 
-	party_details["selling_price_list" if party.doctype=="Customer" else "buying_price_list"] = price_list
+	party_details[
+		"selling_price_list" if party.doctype == "Customer" else "buying_price_list"
+	] = price_list
 
 
-def set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype):
+def set_account_and_due_date(
+	party, account, party_type, company, posting_date, bill_date, doctype
+):
 	if doctype not in ["POS Invoice", "Sales Invoice", "Purchase Invoice"]:
 		# not an invoice
-		return {
-			party_type.lower(): party
-		}
+		return {party_type.lower(): party}
 
 	if party:
 		account = get_party_account(party_type, party, company)
 
-	account_fieldname = "debit_to" if party_type=="Customer" else "credit_to"
+	account_fieldname = "debit_to" if party_type == "Customer" else "credit_to"
 	out = {
 		party_type.lower(): party,
-		account_fieldname : account,
-		"due_date": get_due_date(posting_date, party_type, party, company, bill_date)
+		account_fieldname: account,
+		"due_date": get_due_date(posting_date, party_type, party, company, bill_date),
 	}
 
 	return out
 
+
 @frappe.whitelist()
 def get_party_account(party_type, party=None, company=None):
 	"""Returns the account for the given `party`.
-		Will first search in party (Customer / Supplier) record, if not found,
-		will search in group (Customer Group / Supplier Group),
-		finally will return default."""
+	Will first search in party (Customer / Supplier) record, if not found,
+	will search in group (Customer Group / Supplier Group),
+	finally will return default."""
 	if not company:
 		frappe.throw(_("Please select a Company"))
 
-	if not party and party_type in ['Customer', 'Supplier']:
-		default_account_name = "default_receivable_account" \
-			if party_type=="Customer" else "default_payable_account"
+	if not party and party_type in ["Customer", "Supplier"]:
+		default_account_name = (
+			"default_receivable_account" if party_type == "Customer" else "default_payable_account"
+		)
 
-		return frappe.get_cached_value('Company',  company,  default_account_name)
+		return frappe.get_cached_value("Company", company, default_account_name)
 
-	account = frappe.db.get_value("Party Account",
-		{"parenttype": party_type, "parent": party, "company": company}, "account")
+	account = frappe.db.get_value(
+		"Party Account", {"parenttype": party_type, "parent": party, "company": company}, "account"
+	)
 
-	if not account and party_type in ['Customer', 'Supplier']:
-		party_group_doctype = "Customer Group" if party_type=="Customer" else "Supplier Group"
+	if not account and party_type in ["Customer", "Supplier"]:
+		party_group_doctype = "Customer Group" if party_type == "Customer" else "Supplier Group"
 		group = frappe.get_cached_value(party_type, party, scrub(party_group_doctype))
-		account = frappe.db.get_value("Party Account",
-			{"parenttype": party_group_doctype, "parent": group, "company": company}, "account")
+		account = frappe.db.get_value(
+			"Party Account",
+			{"parenttype": party_group_doctype, "parent": group, "company": company},
+			"account",
+		)
 
-	if not account and party_type in ['Customer', 'Supplier']:
-		default_account_name = "default_receivable_account" \
-			if party_type=="Customer" else "default_payable_account"
-		account = frappe.get_cached_value('Company',  company,  default_account_name)
+	if not account and party_type in ["Customer", "Supplier"]:
+		default_account_name = (
+			"default_receivable_account" if party_type == "Customer" else "default_payable_account"
+		)
+		account = frappe.get_cached_value("Company", company, default_account_name)
 
 	existing_gle_currency = get_party_gle_currency(party_type, party, company)
 	if existing_gle_currency:
 		if account:
 			account_currency = frappe.db.get_value("Account", account, "account_currency", cache=True)
 		if (account and account_currency != existing_gle_currency) or not account:
-				account = get_party_gle_account(party_type, party, company)
+			account = get_party_gle_account(party_type, party, company)
 
 	return account
 
+
 @frappe.whitelist()
 def get_party_bank_account(party_type, party):
-	return frappe.db.get_value('Bank Account', {
-		'party_type': party_type,
-		'party': party,
-		'is_default': 1
-	})
+	return frappe.db.get_value(
+		"Bank Account", {"party_type": party_type, "party": party, "is_default": 1}
+	)
+
 
 def get_party_account_currency(party_type, party, company):
 	def generator():
@@ -273,27 +387,38 @@
 
 	return frappe.local_cache("party_account_currency", (party_type, party, company), generator)
 
+
 def get_party_gle_currency(party_type, party, company):
 	def generator():
-		existing_gle_currency = frappe.db.sql("""select account_currency from `tabGL Entry`
+		existing_gle_currency = frappe.db.sql(
+			"""select account_currency from `tabGL Entry`
 			where docstatus=1 and company=%(company)s and party_type=%(party_type)s and party=%(party)s
-			limit 1""", { "company": company, "party_type": party_type, "party": party })
+			limit 1""",
+			{"company": company, "party_type": party_type, "party": party},
+		)
 
 		return existing_gle_currency[0][0] if existing_gle_currency else None
 
-	return frappe.local_cache("party_gle_currency", (party_type, party, company), generator,
-		regenerate_if_none=True)
+	return frappe.local_cache(
+		"party_gle_currency", (party_type, party, company), generator, regenerate_if_none=True
+	)
+
 
 def get_party_gle_account(party_type, party, company):
 	def generator():
-		existing_gle_account = frappe.db.sql("""select account from `tabGL Entry`
+		existing_gle_account = frappe.db.sql(
+			"""select account from `tabGL Entry`
 			where docstatus=1 and company=%(company)s and party_type=%(party_type)s and party=%(party)s
-			limit 1""", { "company": company, "party_type": party_type, "party": party })
+			limit 1""",
+			{"company": company, "party_type": party_type, "party": party},
+		)
 
 		return existing_gle_account[0][0] if existing_gle_account else None
 
-	return frappe.local_cache("party_gle_account", (party_type, party, company), generator,
-		regenerate_if_none=True)
+	return frappe.local_cache(
+		"party_gle_account", (party_type, party, company), generator, regenerate_if_none=True
+	)
+
 
 def validate_party_gle_currency(party_type, party, company, party_account_currency=None):
 	"""Validate party account currency with existing GL Entry's currency"""
@@ -303,32 +428,55 @@
 	existing_gle_currency = get_party_gle_currency(party_type, party, company)
 
 	if existing_gle_currency and party_account_currency != existing_gle_currency:
-		frappe.throw(_("{0} {1} has accounting entries in currency {2} for company {3}. Please select a receivable or payable account with currency {2}.")
-			.format(frappe.bold(party_type), frappe.bold(party), frappe.bold(existing_gle_currency), frappe.bold(company)), InvalidAccountCurrency)
+		frappe.throw(
+			_(
+				"{0} {1} has accounting entries in currency {2} for company {3}. Please select a receivable or payable account with currency {2}."
+			).format(
+				frappe.bold(party_type),
+				frappe.bold(party),
+				frappe.bold(existing_gle_currency),
+				frappe.bold(company),
+			),
+			InvalidAccountCurrency,
+		)
+
 
 def validate_party_accounts(doc):
 	from erpnext.controllers.accounts_controller import validate_account_head
+
 	companies = []
 
 	for account in doc.get("accounts"):
 		if account.company in companies:
-			frappe.throw(_("There can only be 1 Account per Company in {0} {1}")
-				.format(doc.doctype, doc.name), DuplicatePartyAccountError)
+			frappe.throw(
+				_("There can only be 1 Account per Company in {0} {1}").format(doc.doctype, doc.name),
+				DuplicatePartyAccountError,
+			)
 		else:
 			companies.append(account.company)
 
-		party_account_currency = frappe.db.get_value("Account", account.account, "account_currency", cache=True)
+		party_account_currency = frappe.db.get_value(
+			"Account", account.account, "account_currency", cache=True
+		)
 		if frappe.db.get_default("Company"):
-			company_default_currency = frappe.get_cached_value('Company',
-				frappe.db.get_default("Company"),  "default_currency")
+			company_default_currency = frappe.get_cached_value(
+				"Company", frappe.db.get_default("Company"), "default_currency"
+			)
 		else:
-			company_default_currency = frappe.db.get_value('Company', account.company, "default_currency")
+			company_default_currency = frappe.db.get_value("Company", account.company, "default_currency")
 
 		validate_party_gle_currency(doc.doctype, doc.name, account.company, party_account_currency)
 
 		if doc.get("default_currency") and party_account_currency and company_default_currency:
-			if doc.default_currency != party_account_currency and doc.default_currency != company_default_currency:
-				frappe.throw(_("Billing currency must be equal to either default company's currency or party account currency"))
+			if (
+				doc.default_currency != party_account_currency
+				and doc.default_currency != company_default_currency
+			):
+				frappe.throw(
+					_(
+						"Billing currency must be equal to either default company's currency or party account currency"
+					)
+				)
 
 		# validate if account is mapped for same company
 		validate_account_head(account.idx, account.account, account.company)
@@ -343,18 +491,23 @@
 		template_name = get_payment_terms_template(party, party_type, company)
 
 		if template_name:
-			due_date = get_due_date_from_template(template_name, posting_date, bill_date).strftime("%Y-%m-%d")
+			due_date = get_due_date_from_template(template_name, posting_date, bill_date).strftime(
+				"%Y-%m-%d"
+			)
 		else:
 			if party_type == "Supplier":
 				supplier_group = frappe.get_cached_value(party_type, party, "supplier_group")
 				template_name = frappe.get_cached_value("Supplier Group", supplier_group, "payment_terms")
 				if template_name:
-					due_date = get_due_date_from_template(template_name, posting_date, bill_date).strftime("%Y-%m-%d")
+					due_date = get_due_date_from_template(template_name, posting_date, bill_date).strftime(
+						"%Y-%m-%d"
+					)
 	# If due date is calculated from bill_date, check this condition
 	if getdate(due_date) < getdate(posting_date):
 		due_date = posting_date
 	return due_date
 
+
 def get_due_date_from_template(template_name, posting_date, bill_date):
 	"""
 	Inspects all `Payment Term`s from the a `Payment Terms Template` and returns the due
@@ -364,40 +517,55 @@
 	"""
 	due_date = getdate(bill_date or posting_date)
 
-	template = frappe.get_doc('Payment Terms Template', template_name)
+	template = frappe.get_doc("Payment Terms Template", template_name)
 
 	for term in template.terms:
-		if term.due_date_based_on == 'Day(s) after invoice date':
+		if term.due_date_based_on == "Day(s) after invoice date":
 			due_date = max(due_date, add_days(due_date, term.credit_days))
-		elif term.due_date_based_on == 'Day(s) after the end of the invoice month':
+		elif term.due_date_based_on == "Day(s) after the end of the invoice month":
 			due_date = max(due_date, add_days(get_last_day(due_date), term.credit_days))
 		else:
 			due_date = max(due_date, add_months(get_last_day(due_date), term.credit_months))
 	return due_date
 
-def validate_due_date(posting_date, due_date, party_type, party, company=None, bill_date=None, template_name=None):
+
+def validate_due_date(
+	posting_date, due_date, party_type, party, company=None, bill_date=None, template_name=None
+):
 	if getdate(due_date) < getdate(posting_date):
 		frappe.throw(_("Due Date cannot be before Posting / Supplier Invoice Date"))
 	else:
-		if not template_name: return
+		if not template_name:
+			return
 
-		default_due_date = get_due_date_from_template(template_name, posting_date, bill_date).strftime("%Y-%m-%d")
+		default_due_date = get_due_date_from_template(template_name, posting_date, bill_date).strftime(
+			"%Y-%m-%d"
+		)
 
 		if not default_due_date:
 			return
 
 		if default_due_date != posting_date and getdate(due_date) > getdate(default_due_date):
-			is_credit_controller = frappe.db.get_single_value("Accounts Settings", "credit_controller") in frappe.get_roles()
+			is_credit_controller = (
+				frappe.db.get_single_value("Accounts Settings", "credit_controller") in frappe.get_roles()
+			)
 			if is_credit_controller:
-				msgprint(_("Note: Due / Reference Date exceeds allowed customer credit days by {0} day(s)")
-					.format(date_diff(due_date, default_due_date)))
+				msgprint(
+					_("Note: Due / Reference Date exceeds allowed customer credit days by {0} day(s)").format(
+						date_diff(due_date, default_due_date)
+					)
+				)
 			else:
-				frappe.throw(_("Due / Reference Date cannot be after {0}")
-					.format(formatdate(default_due_date)))
+				frappe.throw(
+					_("Due / Reference Date cannot be after {0}").format(formatdate(default_due_date))
+				)
+
 
 @frappe.whitelist()
 def get_address_tax_category(tax_category=None, billing_address=None, shipping_address=None):
-	addr_tax_category_from = frappe.db.get_single_value("Accounts Settings", "determine_address_tax_category_from")
+	addr_tax_category_from = frappe.db.get_single_value(
+		"Accounts Settings", "determine_address_tax_category_from"
+	)
 	if addr_tax_category_from == "Shipping Address":
 		if shipping_address:
 			tax_category = frappe.db.get_value("Address", shipping_address, "tax_category") or tax_category
@@ -407,36 +575,48 @@
 
 	return cstr(tax_category)
 
+
 @frappe.whitelist()
-def set_taxes(party, party_type, posting_date, company, customer_group=None, supplier_group=None, tax_category=None,
-	billing_address=None, shipping_address=None, use_for_shopping_cart=None):
+def set_taxes(
+	party,
+	party_type,
+	posting_date,
+	company,
+	customer_group=None,
+	supplier_group=None,
+	tax_category=None,
+	billing_address=None,
+	shipping_address=None,
+	use_for_shopping_cart=None,
+):
 	from erpnext.accounts.doctype.tax_rule.tax_rule import get_party_details, get_tax_template
-	args = {
-		party_type.lower(): party,
-		"company": company
-	}
+
+	args = {party_type.lower(): party, "company": company}
 
 	if tax_category:
-		args['tax_category'] = tax_category
+		args["tax_category"] = tax_category
 
 	if customer_group:
-		args['customer_group'] = customer_group
+		args["customer_group"] = customer_group
 
 	if supplier_group:
-		args['supplier_group'] = supplier_group
+		args["supplier_group"] = supplier_group
 
 	if billing_address or shipping_address:
-		args.update(get_party_details(party, party_type, {"billing_address": billing_address, \
-			"shipping_address": shipping_address }))
+		args.update(
+			get_party_details(
+				party, party_type, {"billing_address": billing_address, "shipping_address": shipping_address}
+			)
+		)
 	else:
 		args.update(get_party_details(party, party_type))
 
 	if party_type in ("Customer", "Lead"):
 		args.update({"tax_type": "Sales"})
 
-		if party_type=='Lead':
-			args['customer'] = None
-			del args['lead']
+		if party_type == "Lead":
+			args["customer"] = None
+			del args["lead"]
 	else:
 		args.update({"tax_type": "Purchase"})
 
@@ -451,25 +631,27 @@
 	if party_type not in ("Customer", "Supplier"):
 		return
 	template = None
-	if party_type == 'Customer':
-		customer = frappe.get_cached_value("Customer", party_name,
-			fieldname=['payment_terms', "customer_group"], as_dict=1)
+	if party_type == "Customer":
+		customer = frappe.get_cached_value(
+			"Customer", party_name, fieldname=["payment_terms", "customer_group"], as_dict=1
+		)
 		template = customer.payment_terms
 
 		if not template and customer.customer_group:
-			template = frappe.get_cached_value("Customer Group",
-				customer.customer_group, 'payment_terms')
+			template = frappe.get_cached_value("Customer Group", customer.customer_group, "payment_terms")
 	else:
-		supplier = frappe.get_cached_value("Supplier", party_name,
-			fieldname=['payment_terms', "supplier_group"], as_dict=1)
+		supplier = frappe.get_cached_value(
+			"Supplier", party_name, fieldname=["payment_terms", "supplier_group"], as_dict=1
+		)
 		template = supplier.payment_terms
 		if not template and supplier.supplier_group:
-			template = frappe.get_cached_value("Supplier Group", supplier.supplier_group, 'payment_terms')
+			template = frappe.get_cached_value("Supplier Group", supplier.supplier_group, "payment_terms")
 
 	if not template and company:
-		template = frappe.get_cached_value('Company',  company,  fieldname='payment_terms')
+		template = frappe.get_cached_value("Company", company, fieldname="payment_terms")
 	return template
 
+
 def validate_party_frozen_disabled(party_type, party_name):
 
 	if frappe.flags.ignore_party_validation:
@@ -481,7 +663,9 @@
 			if party.disabled:
 				frappe.throw(_("{0} {1} is disabled").format(party_type, party_name), PartyDisabled)
 			elif party.get("is_frozen"):
-				frozen_accounts_modifier = frappe.db.get_single_value( 'Accounts Settings', 'frozen_accounts_modifier')
+				frozen_accounts_modifier = frappe.db.get_single_value(
+					"Accounts Settings", "frozen_accounts_modifier"
+				)
 				if not frozen_accounts_modifier in frappe.get_roles():
 					frappe.throw(_("{0} {1} is frozen").format(party_type, party_name), PartyFrozen)
 
@@ -489,99 +673,124 @@
 			if frappe.db.get_value("Employee", party_name, "status") != "Active":
 				frappe.msgprint(_("{0} {1} is not active").format(party_type, party_name), alert=True)
 
+
 def get_timeline_data(doctype, name):
-	'''returns timeline data for the past one year'''
+	"""returns timeline data for the past one year"""
 	from frappe.desk.form.load import get_communication_data
 
 	out = {}
-	fields = 'creation, count(*)'
-	after = add_years(None, -1).strftime('%Y-%m-%d')
-	group_by='group by Date(creation)'
+	fields = "creation, count(*)"
+	after = add_years(None, -1).strftime("%Y-%m-%d")
+	group_by = "group by Date(creation)"
 
-	data = get_communication_data(doctype, name, after=after, group_by='group by creation',
-		fields='C.creation as creation, count(C.name)',as_dict=False)
+	data = get_communication_data(
+		doctype,
+		name,
+		after=after,
+		group_by="group by creation",
+		fields="C.creation as creation, count(C.name)",
+		as_dict=False,
+	)
 
 	# fetch and append data from Activity Log
-	data += frappe.db.sql("""select {fields}
+	data += frappe.db.sql(
+		"""select {fields}
 		from `tabActivity Log`
 		where (reference_doctype=%(doctype)s and reference_name=%(name)s)
 		or (timeline_doctype in (%(doctype)s) and timeline_name=%(name)s)
 		or (reference_doctype in ("Quotation", "Opportunity") and timeline_name=%(name)s)
 		and status!='Success' and creation > {after}
 		{group_by} order by creation desc
-		""".format(fields=fields, group_by=group_by, after=after), {
-			"doctype": doctype,
-			"name": name
-		}, as_dict=False)
+		""".format(
+			fields=fields, group_by=group_by, after=after
+		),
+		{"doctype": doctype, "name": name},
+		as_dict=False,
+	)
 
 	timeline_items = dict(data)
 
 	for date, count in timeline_items.items():
 		timestamp = get_timestamp(date)
-		out.update({ timestamp: count })
+		out.update({timestamp: count})
 
 	return out
 
+
 def get_dashboard_info(party_type, party, loyalty_program=None):
 	current_fiscal_year = get_fiscal_year(nowdate(), as_dict=True)
 
-	doctype = "Sales Invoice" if party_type=="Customer" else "Purchase Invoice"
+	doctype = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
 
-	companies = frappe.get_all(doctype, filters={
-		'docstatus': 1,
-		party_type.lower(): party
-	}, distinct=1, fields=['company'])
+	companies = frappe.get_all(
+		doctype, filters={"docstatus": 1, party_type.lower(): party}, distinct=1, fields=["company"]
+	)
 
 	company_wise_info = []
 
-	company_wise_grand_total = frappe.get_all(doctype,
+	company_wise_grand_total = frappe.get_all(
+		doctype,
 		filters={
-			'docstatus': 1,
+			"docstatus": 1,
 			party_type.lower(): party,
-			'posting_date': ('between', [current_fiscal_year.year_start_date, current_fiscal_year.year_end_date])
-			},
-			group_by="company",
-			fields=["company", "sum(grand_total) as grand_total", "sum(base_grand_total) as base_grand_total"]
-		)
+			"posting_date": (
+				"between",
+				[current_fiscal_year.year_start_date, current_fiscal_year.year_end_date],
+			),
+		},
+		group_by="company",
+		fields=[
+			"company",
+			"sum(grand_total) as grand_total",
+			"sum(base_grand_total) as base_grand_total",
+		],
+	)
 
 	loyalty_point_details = []
 
 	if party_type == "Customer":
-		loyalty_point_details = frappe._dict(frappe.get_all("Loyalty Point Entry",
-			filters={
-				'customer': party,
-				'expiry_date': ('>=', getdate()),
+		loyalty_point_details = frappe._dict(
+			frappe.get_all(
+				"Loyalty Point Entry",
+				filters={
+					"customer": party,
+					"expiry_date": (">=", getdate()),
 				},
 				group_by="company",
 				fields=["company", "sum(loyalty_points) as loyalty_points"],
-				as_list =1
-			))
+				as_list=1,
+			)
+		)
 
 	company_wise_billing_this_year = frappe._dict()
 
 	for d in company_wise_grand_total:
 		company_wise_billing_this_year.setdefault(
-			d.company,{
-				"grand_total": d.grand_total,
-				"base_grand_total": d.base_grand_total
-			})
+			d.company, {"grand_total": d.grand_total, "base_grand_total": d.base_grand_total}
+		)
 
-
-	company_wise_total_unpaid = frappe._dict(frappe.db.sql("""
+	company_wise_total_unpaid = frappe._dict(
+		frappe.db.sql(
+			"""
 		select company, sum(debit_in_account_currency) - sum(credit_in_account_currency)
 		from `tabGL Entry`
 		where party_type = %s and party=%s
 		and is_cancelled = 0
-		group by company""", (party_type, party)))
+		group by company""",
+			(party_type, party),
+		)
+	)
 
 	for d in companies:
-		company_default_currency = frappe.db.get_value("Company", d.company, 'default_currency')
+		company_default_currency = frappe.db.get_value("Company", d.company, "default_currency")
 		party_account_currency = get_party_account_currency(party_type, party, d.company)
 
-		if party_account_currency==company_default_currency:
-			billing_this_year = flt(company_wise_billing_this_year.get(d.company,{}).get("base_grand_total"))
+		if party_account_currency == company_default_currency:
+			billing_this_year = flt(
+				company_wise_billing_this_year.get(d.company, {}).get("base_grand_total")
+			)
 		else:
-			billing_this_year = flt(company_wise_billing_this_year.get(d.company,{}).get("grand_total"))
+			billing_this_year = flt(company_wise_billing_this_year.get(d.company, {}).get("grand_total"))
 
 		total_unpaid = flt(company_wise_total_unpaid.get(d.company))
 
@@ -604,6 +813,7 @@
 
 	return company_wise_info
 
+
 def get_party_shipping_address(doctype, name):
 	"""
 	Returns an Address name (best guess) for the given doctype and name for which `address_type == 'Shipping'` is true.
@@ -616,59 +826,70 @@
 	:return: String
 	"""
 	out = frappe.db.sql(
-		'SELECT dl.parent '
-		'from `tabDynamic Link` dl join `tabAddress` ta on dl.parent=ta.name '
-		'where '
-		'dl.link_doctype=%s '
-		'and dl.link_name=%s '
-		'and dl.parenttype="Address" '
-		'and ifnull(ta.disabled, 0) = 0 and'
-		'(ta.address_type="Shipping" or ta.is_shipping_address=1) '
-		'order by ta.is_shipping_address desc, ta.address_type desc limit 1',
-		(doctype, name)
+		"SELECT dl.parent "
+		"from `tabDynamic Link` dl join `tabAddress` ta on dl.parent=ta.name "
+		"where "
+		"dl.link_doctype=%s "
+		"and dl.link_name=%s "
+		"and dl.parenttype='Address' "
+		"and ifnull(ta.disabled, 0) = 0 and"
+		"(ta.address_type='Shipping' or ta.is_shipping_address=1) "
+		"order by ta.is_shipping_address desc, ta.address_type desc limit 1",
+		(doctype, name),
 	)
 	if out:
 		return out[0][0]
 	else:
-		return ''
+		return ""
 
-def get_partywise_advanced_payment_amount(party_type, posting_date = None, future_payment=0, company=None):
+
+def get_partywise_advanced_payment_amount(
+	party_type, posting_date=None, future_payment=0, company=None
+):
 	cond = "1=1"
 	if posting_date:
 		if future_payment:
-			cond = "posting_date <= '{0}' OR DATE(creation) <= '{0}' """.format(posting_date)
+			cond = "posting_date <= '{0}' OR DATE(creation) <= '{0}' " "".format(posting_date)
 		else:
 			cond = "posting_date <= '{0}'".format(posting_date)
 
 	if company:
 		cond += "and company = {0}".format(frappe.db.escape(company))
 
-	data = frappe.db.sql(""" SELECT party, sum({0}) as amount
+	data = frappe.db.sql(
+		""" SELECT party, sum({0}) as amount
 		FROM `tabGL Entry`
 		WHERE
 			party_type = %s and against_voucher is null
 			and is_cancelled = 0
-			and {1} GROUP BY party"""
-		.format(("credit") if party_type == "Customer" else "debit", cond) , party_type)
+			and {1} GROUP BY party""".format(
+			("credit") if party_type == "Customer" else "debit", cond
+		),
+		party_type,
+	)
 
 	if data:
 		return frappe._dict(data)
 
+
 def get_default_contact(doctype, name):
 	"""
-		Returns default contact for the given doctype and name.
-		Can be ordered by `contact_type` to either is_primary_contact or is_billing_contact.
+	Returns default contact for the given doctype and name.
+	Can be ordered by `contact_type` to either is_primary_contact or is_billing_contact.
 	"""
-	out = frappe.db.sql("""
+	out = frappe.db.sql(
+		"""
 			SELECT dl.parent, c.is_primary_contact, c.is_billing_contact
 			FROM `tabDynamic Link` dl
-			INNER JOIN tabContact c ON c.name = dl.parent
+			INNER JOIN `tabContact` c ON c.name = dl.parent
 			WHERE
 				dl.link_doctype=%s AND
 				dl.link_name=%s AND
-				dl.parenttype = "Contact"
+				dl.parenttype = 'Contact'
 			ORDER BY is_primary_contact DESC, is_billing_contact DESC
-		""", (doctype, name))
+		""",
+		(doctype, name),
+	)
 	if out:
 		try:
 			return out[0][0]
@@ -676,3 +897,18 @@
 			return None
 	else:
 		return None
+
+
+def add_party_account(party_type, party, company, account):
+	doc = frappe.get_doc(party_type, party)
+	account_exists = False
+	for d in doc.get("accounts"):
+		if d.account == account:
+			account_exists = True
+
+	if not account_exists:
+		accounts = {"company": company, "account": account}
+
+		doc.append("accounts", accounts)
+
+		doc.save()
diff --git a/erpnext/accounts/report/account_balance/account_balance.py b/erpnext/accounts/report/account_balance/account_balance.py
index a2c70a4..824a965 100644
--- a/erpnext/accounts/report/account_balance/account_balance.py
+++ b/erpnext/accounts/report/account_balance/account_balance.py
@@ -14,6 +14,7 @@
 	data = get_data(filters)
 	return columns, data
 
+
 def get_columns(filters):
 	columns = [
 		{
@@ -21,7 +22,7 @@
 			"fieldtype": "Link",
 			"fieldname": "account",
 			"options": "Account",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Currency"),
@@ -29,19 +30,20 @@
 			"fieldname": "currency",
 			"options": "Currency",
 			"hidden": 1,
-			"width": 50
+			"width": 50,
 		},
 		{
 			"label": _("Balance"),
 			"fieldtype": "Currency",
 			"fieldname": "balance",
 			"options": "currency",
-			"width": 100
-		}
+			"width": 100,
+		},
 	]
 
 	return columns
 
+
 def get_conditions(filters):
 	conditions = {}
 
@@ -57,12 +59,14 @@
 
 	return conditions
 
+
 def get_data(filters):
 
 	data = []
 	conditions = get_conditions(filters)
-	accounts = frappe.db.get_all("Account", fields=["name", "account_currency"],
-		filters=conditions, order_by='name')
+	accounts = frappe.db.get_all(
+		"Account", fields=["name", "account_currency"], filters=conditions, order_by="name"
+	)
 
 	for d in accounts:
 		balance = get_balance_on(d.name, date=filters.report_date)
diff --git a/erpnext/accounts/report/account_balance/test_account_balance.py b/erpnext/accounts/report/account_balance/test_account_balance.py
index 73370e4..13fa05d 100644
--- a/erpnext/accounts/report/account_balance/test_account_balance.py
+++ b/erpnext/accounts/report/account_balance/test_account_balance.py
@@ -13,9 +13,9 @@
 		frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 2'")
 
 		filters = {
-			'company': '_Test Company 2',
-			'report_date': getdate(),
-			'root_type': 'Income',
+			"company": "_Test Company 2",
+			"report_date": getdate(),
+			"root_type": "Income",
 		}
 
 		make_sales_invoice()
@@ -24,42 +24,45 @@
 
 		expected_data = [
 			{
-				"account": 'Direct Income - _TC2',
-				"currency": 'EUR',
+				"account": "Direct Income - _TC2",
+				"currency": "EUR",
 				"balance": -100.0,
 			},
 			{
-				"account": 'Income - _TC2',
-				"currency": 'EUR',
+				"account": "Income - _TC2",
+				"currency": "EUR",
 				"balance": -100.0,
 			},
 			{
-				"account": 'Indirect Income - _TC2',
-				"currency": 'EUR',
+				"account": "Indirect Income - _TC2",
+				"currency": "EUR",
 				"balance": 0.0,
 			},
 			{
-				"account": 'Sales - _TC2',
-				"currency": 'EUR',
+				"account": "Sales - _TC2",
+				"currency": "EUR",
 				"balance": -100.0,
 			},
 			{
-				"account": 'Service - _TC2',
-				"currency": 'EUR',
+				"account": "Service - _TC2",
+				"currency": "EUR",
 				"balance": 0.0,
-			}
+			},
 		]
 
 		self.assertEqual(expected_data, report[1])
 
+
 def make_sales_invoice():
 	frappe.set_user("Administrator")
 
-	create_sales_invoice(company="_Test Company 2",
-		customer = '_Test Customer 2',
-		currency = 'EUR',
-		warehouse = 'Finished Goods - _TC2',
-		debit_to = 'Debtors - _TC2',
-		income_account = 'Sales - _TC2',
-		expense_account = 'Cost of Goods Sold - _TC2',
-		cost_center = 'Main - _TC2')
+	create_sales_invoice(
+		company="_Test Company 2",
+		customer="_Test Customer 2",
+		currency="EUR",
+		warehouse="Finished Goods - _TC2",
+		debit_to="Debtors - _TC2",
+		income_account="Sales - _TC2",
+		expense_account="Cost of Goods Sold - _TC2",
+		cost_center="Main - _TC2",
+	)
diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js
index 81c60bb..f6961eb 100644
--- a/erpnext/accounts/report/accounts_payable/accounts_payable.js
+++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js
@@ -54,6 +54,22 @@
 			}
 		},
 		{
+			"fieldname": "party_account",
+			"label": __("Payable Account"),
+			"fieldtype": "Link",
+			"options": "Account",
+			get_query: () => {
+				var company = frappe.query_report.get_filter_value('company');
+				return {
+					filters: {
+						'company': company,
+						'account_type': 'Payable',
+						'is_group': 0
+					}
+				};
+			}
+		},
+		{
 			"fieldname": "ageing_based_on",
 			"label": __("Ageing Based On"),
 			"fieldtype": "Select",
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html
index f4fd06b..f2bf942 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html
@@ -42,7 +42,7 @@
 
 	{% if(filters.show_future_payments) { %}
 		{% var balance_row = data.slice(-1).pop();
-			var start = filters.based_on_payment_terms ? 13 : 11;
+			var start = report.columns.findIndex((elem) => (elem.fieldname == 'age'));
 			var range1 = report.columns[start].label;
 			var range2 = report.columns[start+1].label;
 			var range3 = report.columns[start+2].label;
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
index 5700298..0238711 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
@@ -67,6 +67,22 @@
 			}
 		},
 		{
+			"fieldname": "party_account",
+			"label": __("Receivable Account"),
+			"fieldtype": "Link",
+			"options": "Account",
+			get_query: () => {
+				var company = frappe.query_report.get_filter_value('company');
+				return {
+					filters: {
+						'company': company,
+						'account_type': 'Receivable',
+						'is_group': 0
+					}
+				};
+			}
+		},
+		{
 			"fieldname": "ageing_based_on",
 			"label": __("Ageing Based On"),
 			"fieldtype": "Select",
@@ -157,11 +173,6 @@
 			"fieldtype": "Check",
 		},
 		{
-			"fieldname": "show_remarks",
-			"label": __("Show Remarks"),
-			"fieldtype": "Check",
-		},
-		{
 			"fieldname": "tax_id",
 			"label": __("Tax Id"),
 			"fieldtype": "Data",
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index a990f23..1911152 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -5,7 +5,9 @@
 from collections import OrderedDict
 
 import frappe
-from frappe import _, scrub
+from frappe import _, qb, scrub
+from frappe.query_builder import Criterion
+from frappe.query_builder.functions import Date
 from frappe.utils import cint, cstr, flt, getdate, nowdate
 
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
@@ -29,6 +31,7 @@
 #  9. Report amounts are in "Party Currency" if party is selected, or company currency for multi-party
 # 10. This reports is based on all GL Entries that are made against account_type "Receivable" or "Payable"
 
+
 def execute(filters=None):
 	args = {
 		"party_type": "Customer",
@@ -36,18 +39,25 @@
 	}
 	return ReceivablePayableReport(filters).run(args)
 
+
 class ReceivablePayableReport(object):
 	def __init__(self, filters=None):
 		self.filters = frappe._dict(filters or {})
+		self.qb_selection_filter = []
+		self.ple = qb.DocType("Payment Ledger Entry")
 		self.filters.report_date = getdate(self.filters.report_date or nowdate())
-		self.age_as_on = getdate(nowdate()) \
-			if self.filters.report_date > getdate(nowdate()) \
+		self.age_as_on = (
+			getdate(nowdate())
+			if self.filters.report_date > getdate(nowdate())
 			else self.filters.report_date
+		)
 
 	def run(self, args):
 		self.filters.update(args)
 		self.set_defaults()
-		self.party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1])
+		self.party_naming_by = frappe.db.get_value(
+			args.get("naming_by")[0], None, args.get("naming_by")[1]
+		)
 		self.get_columns()
 		self.get_data()
 		self.get_chart_data()
@@ -55,8 +65,10 @@
 
 	def set_defaults(self):
 		if not self.filters.get("company"):
-			self.filters.company = frappe.db.get_single_value('Global Defaults', 'default_company')
-		self.company_currency = frappe.get_cached_value('Company',  self.filters.get("company"), "default_currency")
+			self.filters.company = frappe.db.get_single_value("Global Defaults", "default_company")
+		self.company_currency = frappe.get_cached_value(
+			"Company", self.filters.get("company"), "default_currency"
+		)
 		self.currency_precision = get_currency_precision() or 2
 		self.dr_or_cr = "debit" if self.filters.party_type == "Customer" else "credit"
 		self.party_type = self.filters.party_type
@@ -64,16 +76,16 @@
 		self.invoices = set()
 		self.skip_total_row = 0
 
-		if self.filters.get('group_by_party'):
-			self.previous_party=''
+		if self.filters.get("group_by_party"):
+			self.previous_party = ""
 			self.total_row_map = {}
 			self.skip_total_row = 1
 
 	def get_data(self):
-		self.get_gl_entries()
+		self.get_ple_entries()
 		self.get_sales_invoices_or_customers_based_on_sales_person()
 		self.voucher_balance = OrderedDict()
-		self.init_voucher_balance() # invoiced, paid, credit_note, outstanding
+		self.init_voucher_balance()  # invoiced, paid, credit_note, outstanding
 
 		# Build delivery note map against all sales invoices
 		self.build_delivery_note_map()
@@ -88,96 +100,114 @@
 		self.get_return_entries()
 
 		self.data = []
-		for gle in self.gl_entries:
-			self.update_voucher_balance(gle)
+
+		for ple in self.ple_entries:
+			self.update_voucher_balance(ple)
 
 		self.build_data()
 
 	def init_voucher_balance(self):
 		# build all keys, since we want to exclude vouchers beyond the report date
-		for gle in self.gl_entries:
+		for ple in self.ple_entries:
 			# get the balance object for voucher_type
-			key = (gle.voucher_type, gle.voucher_no, gle.party)
+			key = (ple.voucher_type, ple.voucher_no, ple.party)
 			if not key in self.voucher_balance:
 				self.voucher_balance[key] = frappe._dict(
-					voucher_type = gle.voucher_type,
-					voucher_no = gle.voucher_no,
-					party = gle.party,
-					posting_date = gle.posting_date,
-					account_currency = gle.account_currency,
-					remarks = gle.remarks if self.filters.get("show_remarks") else None,
-					invoiced = 0.0,
-					paid = 0.0,
-					credit_note = 0.0,
-					outstanding = 0.0,
-					invoiced_in_account_currency = 0.0,
-					paid_in_account_currency = 0.0,
-					credit_note_in_account_currency = 0.0,
-					outstanding_in_account_currency = 0.0
+					voucher_type=ple.voucher_type,
+					voucher_no=ple.voucher_no,
+					party=ple.party,
+					party_account=ple.account,
+					posting_date=ple.posting_date,
+					account_currency=ple.account_currency,
+					invoiced=0.0,
+					paid=0.0,
+					credit_note=0.0,
+					outstanding=0.0,
+					invoiced_in_account_currency=0.0,
+					paid_in_account_currency=0.0,
+					credit_note_in_account_currency=0.0,
+					outstanding_in_account_currency=0.0,
 				)
-			self.get_invoices(gle)
 
-			if self.filters.get('group_by_party'):
-				self.init_subtotal_row(gle.party)
+			if self.filters.get("group_by_party"):
+				self.init_subtotal_row(ple.party)
 
-		if self.filters.get('group_by_party'):
-			self.init_subtotal_row('Total')
+		if self.filters.get("group_by_party"):
+			self.init_subtotal_row("Total")
 
-	def get_invoices(self, gle):
-		if gle.voucher_type in ('Sales Invoice', 'Purchase Invoice'):
+	def get_invoices(self, ple):
+		if ple.voucher_type in ("Sales Invoice", "Purchase Invoice"):
 			if self.filters.get("sales_person"):
-				if gle.voucher_no in self.sales_person_records.get("Sales Invoice", []) \
-					or gle.party in self.sales_person_records.get("Customer", []):
-						self.invoices.add(gle.voucher_no)
+				if ple.voucher_no in self.sales_person_records.get(
+					"Sales Invoice", []
+				) or ple.party in self.sales_person_records.get("Customer", []):
+					self.invoices.add(ple.voucher_no)
 			else:
-				self.invoices.add(gle.voucher_no)
+				self.invoices.add(ple.voucher_no)
 
 	def init_subtotal_row(self, party):
 		if not self.total_row_map.get(party):
-			self.total_row_map.setdefault(party, {
-				'party': party,
-				'bold': 1
-			})
+			self.total_row_map.setdefault(party, {"party": party, "bold": 1})
 
 			for field in self.get_currency_fields():
 				self.total_row_map[party][field] = 0.0
 
 	def get_currency_fields(self):
-		return ['invoiced', 'paid', 'credit_note', 'outstanding', 'range1',
-			'range2', 'range3', 'range4', 'range5']
+		return [
+			"invoiced",
+			"paid",
+			"credit_note",
+			"outstanding",
+			"range1",
+			"range2",
+			"range3",
+			"range4",
+			"range5",
+		]
 
-	def update_voucher_balance(self, gle):
+	def get_voucher_balance(self, ple):
+		if self.filters.get("sales_person"):
+			if not (
+				ple.party in self.sales_person_records.get("Customer", [])
+				or ple.against_voucher_no in self.sales_person_records.get("Sales Invoice", [])
+			):
+				return
+
+		key = (ple.against_voucher_type, ple.against_voucher_no, ple.party)
+		row = self.voucher_balance.get(key)
+		return row
+
+	def update_voucher_balance(self, ple):
 		# get the row where this balance needs to be updated
 		# if its a payment, it will return the linked invoice or will be considered as advance
-		row = self.get_voucher_balance(gle)
-		if not row: return
-		# gle_balance will be the total "debit - credit" for receivable type reports and
-		# and vice-versa for payable type reports
-		gle_balance = self.get_gle_balance(gle)
-		gle_balance_in_account_currency = self.get_gle_balance_in_account_currency(gle)
+		row = self.get_voucher_balance(ple)
+		if not row:
+			return
 
-		if gle_balance > 0:
-			if gle.voucher_type in ('Journal Entry', 'Payment Entry') and gle.against_voucher:
-				# debit against sales / purchase invoice
-				row.paid -= gle_balance
-				row.paid_in_account_currency -= gle_balance_in_account_currency
+		amount = ple.amount
+		amount_in_account_currency = ple.amount_in_account_currency
+
+		# update voucher
+		if ple.amount > 0:
+			if (
+				ple.voucher_type in ["Journal Entry", "Payment Entry"]
+				and ple.voucher_no != ple.against_voucher_no
+			):
+				row.paid -= amount
+				row.paid_in_account_currency -= amount_in_account_currency
 			else:
-				# invoice
-				row.invoiced += gle_balance
-				row.invoiced_in_account_currency += gle_balance_in_account_currency
+				row.invoiced += amount
+				row.invoiced_in_account_currency += amount_in_account_currency
 		else:
-			# payment or credit note for receivables
-			if self.is_invoice(gle):
-				# stand alone debit / credit note
-				row.credit_note -= gle_balance
-				row.credit_note_in_account_currency -= gle_balance_in_account_currency
+			if self.is_invoice(ple):
+				row.credit_note -= amount
+				row.credit_note_in_account_currency -= amount_in_account_currency
 			else:
-				# advance / unlinked payment or other adjustment
-				row.paid -= gle_balance
-				row.paid_in_account_currency -= gle_balance_in_account_currency
+				row.paid -= amount
+				row.paid_in_account_currency -= amount_in_account_currency
 
-		if gle.cost_center:
-			row.cost_center =  str(gle.cost_center)
+		if ple.cost_center:
+			row.cost_center = str(ple.cost_center)
 
 	def update_sub_total_row(self, row, party):
 		total_row = self.total_row_map.get(party)
@@ -191,49 +221,26 @@
 		if sub_total_row:
 			self.data.append(sub_total_row)
 			self.data.append({})
-			self.update_sub_total_row(sub_total_row, 'Total')
-
-	def get_voucher_balance(self, gle):
-		if self.filters.get("sales_person"):
-			against_voucher = gle.against_voucher or gle.voucher_no
-			if not (gle.party in self.sales_person_records.get("Customer", []) or \
-				against_voucher in self.sales_person_records.get("Sales Invoice", [])):
-					return
-
-		voucher_balance = None
-		if gle.against_voucher:
-			# find invoice
-			against_voucher = gle.against_voucher
-
-			# If payment is made against credit note
-			# and credit note is made against a Sales Invoice
-			# then consider the payment against original sales invoice.
-			if gle.against_voucher_type in ('Sales Invoice', 'Purchase Invoice'):
-				if gle.against_voucher in self.return_entries:
-					return_against = self.return_entries.get(gle.against_voucher)
-					if return_against:
-						against_voucher = return_against
-
-			voucher_balance = self.voucher_balance.get((gle.against_voucher_type, against_voucher, gle.party))
-
-		if not voucher_balance:
-			# no invoice, this is an invoice / stand-alone payment / credit note
-			voucher_balance = self.voucher_balance.get((gle.voucher_type, gle.voucher_no, gle.party))
-
-		return voucher_balance
+			self.update_sub_total_row(sub_total_row, "Total")
 
 	def build_data(self):
 		# set outstanding for all the accumulated balances
 		# as we can use this to filter out invoices without outstanding
 		for key, row in self.voucher_balance.items():
 			row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision)
-			row.outstanding_in_account_currency = flt(row.invoiced_in_account_currency - row.paid_in_account_currency - \
-				row.credit_note_in_account_currency, self.currency_precision)
+			row.outstanding_in_account_currency = flt(
+				row.invoiced_in_account_currency
+				- row.paid_in_account_currency
+				- row.credit_note_in_account_currency,
+				self.currency_precision,
+			)
 
 			row.invoice_grand_total = row.invoiced
 
-			if (abs(row.outstanding) > 1.0/10 ** self.currency_precision) and \
-				(abs(row.outstanding_in_account_currency) > 1.0/10 ** self.currency_precision):
+			if (abs(row.outstanding) > 1.0 / 10**self.currency_precision) and (
+				abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision
+			):
+
 				# non-zero oustanding, we must consider this row
 
 				if self.is_invoice(row) and self.filters.based_on_payment_terms:
@@ -254,10 +261,10 @@
 				else:
 					self.append_row(row)
 
-		if self.filters.get('group_by_party'):
+		if self.filters.get("group_by_party"):
 			self.append_subtotal_row(self.previous_party)
 			if self.data:
-				self.data.append(self.total_row_map.get('Total'))
+				self.data.append(self.total_row_map.get("Total"))
 
 	def append_row(self, row):
 		self.allocate_future_payments(row)
@@ -265,7 +272,7 @@
 		self.set_party_details(row)
 		self.set_ageing(row)
 
-		if self.filters.get('group_by_party'):
+		if self.filters.get("group_by_party"):
 			self.update_sub_total_row(row, row.party)
 			if self.previous_party and (self.previous_party != row.party):
 				self.append_subtotal_row(self.previous_party)
@@ -279,39 +286,49 @@
 			invoice_details.pop("due_date", None)
 		row.update(invoice_details)
 
-		if row.voucher_type == 'Sales Invoice':
+		if row.voucher_type == "Sales Invoice":
 			if self.filters.show_delivery_notes:
 				self.set_delivery_notes(row)
 
 			if self.filters.show_sales_person and row.sales_team:
 				row.sales_person = ", ".join(row.sales_team)
-				del row['sales_team']
+				del row["sales_team"]
 
 	def set_delivery_notes(self, row):
 		delivery_notes = self.delivery_notes.get(row.voucher_no, [])
 		if delivery_notes:
-			row.delivery_notes = ', '.join(delivery_notes)
+			row.delivery_notes = ", ".join(delivery_notes)
 
 	def build_delivery_note_map(self):
 		if self.invoices and self.filters.show_delivery_notes:
 			self.delivery_notes = frappe._dict()
 
 			# delivery note link inside sales invoice
-			si_against_dn = frappe.db.sql("""
+			si_against_dn = frappe.db.sql(
+				"""
 				select parent, delivery_note
 				from `tabSales Invoice Item`
 				where docstatus=1 and parent in (%s)
-			""" % (','.join(['%s'] * len(self.invoices))), tuple(self.invoices), as_dict=1)
+			"""
+				% (",".join(["%s"] * len(self.invoices))),
+				tuple(self.invoices),
+				as_dict=1,
+			)
 
 			for d in si_against_dn:
 				if d.delivery_note:
 					self.delivery_notes.setdefault(d.parent, set()).add(d.delivery_note)
 
-			dn_against_si = frappe.db.sql("""
+			dn_against_si = frappe.db.sql(
+				"""
 				select distinct parent, against_sales_invoice
 				from `tabDelivery Note Item`
 				where against_sales_invoice in (%s)
-			""" % (','.join(['%s'] * len(self.invoices))), tuple(self.invoices) , as_dict=1)
+			"""
+				% (",".join(["%s"] * len(self.invoices))),
+				tuple(self.invoices),
+				as_dict=1,
+			)
 
 			for d in dn_against_si:
 				self.delivery_notes.setdefault(d.against_sales_invoice, set()).add(d.parent)
@@ -319,39 +336,55 @@
 	def get_invoice_details(self):
 		self.invoice_details = frappe._dict()
 		if self.party_type == "Customer":
-			si_list = frappe.db.sql("""
+			si_list = frappe.db.sql(
+				"""
 				select name, due_date, po_no
 				from `tabSales Invoice`
 				where posting_date <= %s
-			""",self.filters.report_date, as_dict=1)
+			""",
+				self.filters.report_date,
+				as_dict=1,
+			)
 			for d in si_list:
 				self.invoice_details.setdefault(d.name, d)
 
 			# Get Sales Team
 			if self.filters.show_sales_person:
-				sales_team = frappe.db.sql("""
+				sales_team = frappe.db.sql(
+					"""
 					select parent, sales_person
 					from `tabSales Team`
 					where parenttype = 'Sales Invoice'
-				""", as_dict=1)
+				""",
+					as_dict=1,
+				)
 				for d in sales_team:
-					self.invoice_details.setdefault(d.parent, {})\
-						.setdefault('sales_team', []).append(d.sales_person)
+					self.invoice_details.setdefault(d.parent, {}).setdefault("sales_team", []).append(
+						d.sales_person
+					)
 
 		if self.party_type == "Supplier":
-			for pi in frappe.db.sql("""
+			for pi in frappe.db.sql(
+				"""
 				select name, due_date, bill_no, bill_date
 				from `tabPurchase Invoice`
 				where posting_date <= %s
-			""", self.filters.report_date, as_dict=1):
+			""",
+				self.filters.report_date,
+				as_dict=1,
+			):
 				self.invoice_details.setdefault(pi.name, pi)
 
 		# Invoices booked via Journal Entries
-		journal_entries = frappe.db.sql("""
+		journal_entries = frappe.db.sql(
+			"""
 			select name, due_date, bill_no, bill_date
 			from `tabJournal Entry`
 			where posting_date <= %s
-		""", self.filters.report_date, as_dict=1)
+		""",
+			self.filters.report_date,
+			as_dict=1,
+		)
 
 		for je in journal_entries:
 			if je.bill_no:
@@ -372,17 +405,18 @@
 
 			# update "paid" and "oustanding" for this term
 			if not term.paid:
-				self.allocate_closing_to_term(row, term, 'paid')
+				self.allocate_closing_to_term(row, term, "paid")
 
 			# update "credit_note" and "oustanding" for this term
 			if term.outstanding:
-				self.allocate_closing_to_term(row, term, 'credit_note')
+				self.allocate_closing_to_term(row, term, "credit_note")
 
-		row.payment_terms = sorted(row.payment_terms, key=lambda x: x['due_date'])
+		row.payment_terms = sorted(row.payment_terms, key=lambda x: x["due_date"])
 
 	def get_payment_terms(self, row):
 		# build payment_terms for row
-		payment_terms_details = frappe.db.sql("""
+		payment_terms_details = frappe.db.sql(
+			"""
 			select
 				si.name, si.party_account_currency, si.currency, si.conversion_rate,
 				ps.due_date, ps.payment_term, ps.payment_amount, ps.description, ps.paid_amount, ps.discounted_amount
@@ -391,8 +425,12 @@
 				si.name = ps.parent and
 				si.name = %s
 			order by ps.paid_amount desc, due_date
-		""".format(row.voucher_type), row.voucher_no, as_dict = 1)
-
+		""".format(
+				row.voucher_type
+			),
+			row.voucher_no,
+			as_dict=1,
+		)
 
 		original_row = frappe._dict(row)
 		row.payment_terms = []
@@ -406,23 +444,29 @@
 			self.append_payment_term(row, d, term)
 
 	def append_payment_term(self, row, d, term):
-		if (self.filters.get("customer") or self.filters.get("supplier")) and d.currency == d.party_account_currency:
+		if (
+			self.filters.get("customer") or self.filters.get("supplier")
+		) and d.currency == d.party_account_currency:
 			invoiced = d.payment_amount
 		else:
 			invoiced = flt(flt(d.payment_amount) * flt(d.conversion_rate), self.currency_precision)
 
-		row.payment_terms.append(term.update({
-			"due_date": d.due_date,
-			"invoiced": invoiced,
-			"invoice_grand_total": row.invoiced,
-			"payment_term": d.description or d.payment_term,
-			"paid": d.paid_amount + d.discounted_amount,
-			"credit_note": 0.0,
-			"outstanding": invoiced - d.paid_amount - d.discounted_amount
-		}))
+		row.payment_terms.append(
+			term.update(
+				{
+					"due_date": d.due_date,
+					"invoiced": invoiced,
+					"invoice_grand_total": row.invoiced,
+					"payment_term": d.description or d.payment_term,
+					"paid": d.paid_amount + d.discounted_amount,
+					"credit_note": 0.0,
+					"outstanding": invoiced - d.paid_amount - d.discounted_amount,
+				}
+			)
+		)
 
 		if d.paid_amount:
-			row['paid'] -= d.paid_amount + d.discounted_amount
+			row["paid"] -= d.paid_amount + d.discounted_amount
 
 	def allocate_closing_to_term(self, row, term, key):
 		if row[key]:
@@ -437,7 +481,7 @@
 	def allocate_extra_payments_or_credits(self, row):
 		# allocate extra payments / credits
 		additional_row = None
-		for key in ('paid', 'credit_note'):
+		for key in ("paid", "credit_note"):
 			if row[key] > 0:
 				if not additional_row:
 					additional_row = frappe._dict(row)
@@ -445,7 +489,9 @@
 				additional_row[key] = row[key]
 
 		if additional_row:
-			additional_row.outstanding = additional_row.invoiced - additional_row.paid - additional_row.credit_note
+			additional_row.outstanding = (
+				additional_row.invoiced - additional_row.paid - additional_row.credit_note
+			)
 			self.append_row(additional_row)
 
 	def get_future_payments(self):
@@ -459,7 +505,8 @@
 						self.future_payments.setdefault((d.invoice_no, d.party), []).append(d)
 
 	def get_future_payments_from_payment_entry(self):
-		return frappe.db.sql("""
+		return frappe.db.sql(
+			"""
 			select
 				ref.reference_name as invoice_no,
 				payment_entry.party,
@@ -475,16 +522,23 @@
 				payment_entry.docstatus < 2
 				and payment_entry.posting_date > %s
 				and payment_entry.party_type = %s
-			""", (self.filters.report_date, self.party_type), as_dict=1)
+			""",
+			(self.filters.report_date, self.party_type),
+			as_dict=1,
+		)
 
 	def get_future_payments_from_journal_entry(self):
-		if self.filters.get('party'):
-			amount_field = ("jea.debit_in_account_currency - jea.credit_in_account_currency"
-				if self.party_type == 'Supplier' else "jea.credit_in_account_currency - jea.debit_in_account_currency")
+		if self.filters.get("party"):
+			amount_field = (
+				"jea.debit_in_account_currency - jea.credit_in_account_currency"
+				if self.party_type == "Supplier"
+				else "jea.credit_in_account_currency - jea.debit_in_account_currency"
+			)
 		else:
-			amount_field = ("jea.debit - " if self.party_type == 'Supplier' else "jea.credit")
+			amount_field = "jea.debit - " if self.party_type == "Supplier" else "jea.credit"
 
-		return frappe.db.sql("""
+		return frappe.db.sql(
+			"""
 			select
 				jea.reference_name as invoice_no,
 				jea.party,
@@ -503,7 +557,12 @@
 				and jea.reference_name is not null and jea.reference_name != ''
 			group by je.name, jea.reference_name
 			having future_amount > 0
-			""".format(amount_field), (self.filters.report_date, self.party_type), as_dict=1)
+			""".format(
+				amount_field
+			),
+			(self.filters.report_date, self.party_type),
+			as_dict=1,
+		)
 
 	def allocate_future_payments(self, row):
 		# future payments are captured in additional columns
@@ -525,22 +584,21 @@
 					future.future_amount = 0
 					row.remaining_balance = row.outstanding - row.future_amount
 
-				row.setdefault('future_ref', []).append(cstr(future.future_ref) + '/' + cstr(future.future_date))
+				row.setdefault("future_ref", []).append(
+					cstr(future.future_ref) + "/" + cstr(future.future_date)
+				)
 
 		if row.future_ref:
-			row.future_ref = ', '.join(row.future_ref)
+			row.future_ref = ", ".join(row.future_ref)
 
 	def get_return_entries(self):
 		doctype = "Sales Invoice" if self.party_type == "Customer" else "Purchase Invoice"
-		filters={
-			'is_return': 1,
-			'docstatus': 1
-		}
+		filters = {"is_return": 1, "docstatus": 1}
 		party_field = scrub(self.filters.party_type)
 		if self.filters.get(party_field):
 			filters.update({party_field: self.filters.get(party_field)})
 		self.return_entries = frappe._dict(
-			frappe.get_all(doctype, filters, ['name', 'return_against'], as_list=1)
+			frappe.get_all(doctype, filters, ["name", "return_against"], as_list=1)
 		)
 
 	def set_ageing(self, row):
@@ -571,292 +629,373 @@
 		row.age = (getdate(self.age_as_on) - getdate(entry_date)).days or 0
 		index = None
 
-		if not (self.filters.range1 and self.filters.range2 and self.filters.range3 and self.filters.range4):
-			self.filters.range1, self.filters.range2, self.filters.range3, self.filters.range4 = 30, 60, 90, 120
+		if not (
+			self.filters.range1 and self.filters.range2 and self.filters.range3 and self.filters.range4
+		):
+			self.filters.range1, self.filters.range2, self.filters.range3, self.filters.range4 = (
+				30,
+				60,
+				90,
+				120,
+			)
 
-		for i, days in enumerate([self.filters.range1, self.filters.range2, self.filters.range3, self.filters.range4]):
+		for i, days in enumerate(
+			[self.filters.range1, self.filters.range2, self.filters.range3, self.filters.range4]
+		):
 			if cint(row.age) <= cint(days):
 				index = i
 				break
 
-		if index is None: index = 4
-		row['range' + str(index+1)] = row.outstanding
+		if index is None:
+			index = 4
+		row["range" + str(index + 1)] = row.outstanding
 
-	def get_gl_entries(self):
+	def get_ple_entries(self):
 		# get all the GL entries filtered by the given filters
 
-		conditions, values = self.prepare_conditions()
-		order_by = self.get_order_by_condition()
+		self.prepare_conditions()
 
 		if self.filters.show_future_payments:
-			values.insert(2, self.filters.report_date)
-
-			date_condition = """AND (posting_date <= %s
-				OR (against_voucher IS NULL AND DATE(creation) <= %s))"""
+			self.qb_selection_filter.append(
+				(
+					self.ple.posting_date.lte(self.filters.report_date)
+					| (
+						(self.ple.voucher_no == self.ple.against_voucher_no)
+						& (Date(self.ple.creation).lte(self.filters.report_date))
+					)
+				)
+			)
 		else:
-			date_condition = "AND posting_date <=%s"
+			self.qb_selection_filter.append(self.ple.posting_date.lte(self.filters.report_date))
 
-		if self.filters.get(scrub(self.party_type)):
-			select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit"
+		ple = qb.DocType("Payment Ledger Entry")
+		query = (
+			qb.from_(ple)
+			.select(
+				ple.account,
+				ple.voucher_type,
+				ple.voucher_no,
+				ple.against_voucher_type,
+				ple.against_voucher_no,
+				ple.party_type,
+				ple.cost_center,
+				ple.party,
+				ple.posting_date,
+				ple.due_date,
+				ple.account_currency.as_("currency"),
+				ple.amount,
+				ple.amount_in_account_currency,
+			)
+			.where(ple.delinked == 0)
+			.where(Criterion.all(self.qb_selection_filter))
+		)
+
+		if self.filters.get("group_by_party"):
+			query = query.orderby(self.ple.party, self.ple.posting_date)
 		else:
-			select_fields = "debit, credit"
+			query = query.orderby(self.ple.posting_date, self.ple.party)
 
-		doc_currency_fields = "debit_in_account_currency, credit_in_account_currency"
-
-		remarks = ", remarks" if self.filters.get("show_remarks") else ""
-
-		self.gl_entries = frappe.db.sql("""
-			select
-				name, posting_date, account, party_type, party, voucher_type, voucher_no, cost_center,
-				against_voucher_type, against_voucher, account_currency, {0}, {1} {remarks}
-			from
-				`tabGL Entry`
-			where
-				docstatus < 2
-				and is_cancelled = 0
-				and party_type=%s
-				and (party is not null and party != '')
-				{2} {3} {4}"""
-			.format(select_fields, doc_currency_fields, date_condition, conditions, order_by, remarks=remarks), values, as_dict=True)
+		self.ple_entries = query.run(as_dict=True)
 
 	def get_sales_invoices_or_customers_based_on_sales_person(self):
 		if self.filters.get("sales_person"):
-			lft, rgt = frappe.db.get_value("Sales Person",
-				self.filters.get("sales_person"), ["lft", "rgt"])
+			lft, rgt = frappe.db.get_value("Sales Person", self.filters.get("sales_person"), ["lft", "rgt"])
 
-			records = frappe.db.sql("""
+			records = frappe.db.sql(
+				"""
 				select distinct parent, parenttype
 				from `tabSales Team` steam
 				where parenttype in ('Customer', 'Sales Invoice')
 					and exists(select name from `tabSales Person` where lft >= %s and rgt <= %s and name = steam.sales_person)
-			""", (lft, rgt), as_dict=1)
+			""",
+				(lft, rgt),
+				as_dict=1,
+			)
 
 			self.sales_person_records = frappe._dict()
 			for d in records:
 				self.sales_person_records.setdefault(d.parenttype, set()).add(d.parent)
 
 	def prepare_conditions(self):
-		conditions = [""]
-		values = [self.party_type, self.filters.report_date]
+		self.qb_selection_filter = []
 		party_type_field = scrub(self.party_type)
 
-		self.add_common_filters(conditions, values, party_type_field)
+		self.add_common_filters(party_type_field=party_type_field)
 
-		if party_type_field=="customer":
-			self.add_customer_filters(conditions, values)
+		if party_type_field == "customer":
+			self.add_customer_filters()
 
-		elif party_type_field=="supplier":
-			self.add_supplier_filters(conditions, values)
+		elif party_type_field == "supplier":
+			self.add_supplier_filters()
 
 		if self.filters.cost_center:
-			self.get_cost_center_conditions(conditions)
+			self.get_cost_center_conditions()
 
-		self.add_accounting_dimensions_filters(conditions, values)
-		return " and ".join(conditions), values
+		self.add_accounting_dimensions_filters()
 
 	def get_cost_center_conditions(self, conditions):
 		lft, rgt = frappe.db.get_value("Cost Center", self.filters.cost_center, ["lft", "rgt"])
-		cost_center_list = [center.name for center in frappe.get_list("Cost Center", filters = {'lft': (">=", lft), 'rgt': ("<=", rgt)})]
+		cost_center_list = [
+			center.name
+			for center in frappe.get_list("Cost Center", filters={"lft": (">=", lft), "rgt": ("<=", rgt)})
+		]
+		self.qb_selection_filter.append(self.ple.cost_center.isin(cost_center_list))
 
-		cost_center_string = '", "'.join(cost_center_list)
-		conditions.append('cost_center in ("{0}")'.format(cost_center_string))
-
-	def get_order_by_condition(self):
-		if self.filters.get('group_by_party'):
-			return "order by party, posting_date"
-		else:
-			return "order by posting_date, party"
-
-	def add_common_filters(self, conditions, values, party_type_field):
+	def add_common_filters(self, party_type_field):
 		if self.filters.company:
-			conditions.append("company=%s")
-			values.append(self.filters.company)
+			self.qb_selection_filter.append(self.ple.company == self.filters.company)
 
 		if self.filters.finance_book:
-			conditions.append("ifnull(finance_book, '') in (%s, '')")
-			values.append(self.filters.finance_book)
+			self.qb_selection_filter.append(self.ple.finance_book == self.filters.finance_book)
 
 		if self.filters.get(party_type_field):
-			conditions.append("party=%s")
-			values.append(self.filters.get(party_type_field))
+			self.qb_selection_filter.append(self.ple.party == self.filters.get(party_type_field))
 
-		# get GL with "receivable" or "payable" account_type
-		account_type = "Receivable" if self.party_type == "Customer" else "Payable"
-		accounts = [d.name for d in frappe.get_all("Account",
-			filters={"account_type": account_type, "company": self.filters.company})]
+		if self.filters.party_account:
+			self.qb_selection_filter.append(self.ple.account == self.filters.party_account)
+		else:
+			# get GL with "receivable" or "payable" account_type
+			account_type = "Receivable" if self.party_type == "Customer" else "Payable"
+			accounts = [
+				d.name
+				for d in frappe.get_all(
+					"Account", filters={"account_type": account_type, "company": self.filters.company}
+				)
+			]
 
-		if accounts:
-			conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts)))
-			values += accounts
+			if accounts:
+				self.qb_selection_filter.append(self.ple.account.isin(accounts))
 
-	def add_customer_filters(self, conditions, values):
+	def add_customer_filters(
+		self,
+	):
+		self.customter = qb.DocType("Customer")
+
 		if self.filters.get("customer_group"):
-			conditions.append(self.get_hierarchical_filters('Customer Group', 'customer_group'))
+			self.get_hierarchical_filters("Customer Group", "customer_group")
 
 		if self.filters.get("territory"):
-			conditions.append(self.get_hierarchical_filters('Territory', 'territory'))
+			self.get_hierarchical_filters("Territory", "territory")
 
 		if self.filters.get("payment_terms_template"):
-			conditions.append("party in (select name from tabCustomer where payment_terms=%s)")
-			values.append(self.filters.get("payment_terms_template"))
+			self.qb_selection_filter.append(
+				self.ple.party_isin(
+					qb.from_(self.customer).where(
+						self.customer.payment_terms == self.filters.get("payment_terms_template")
+					)
+				)
+			)
 
 		if self.filters.get("sales_partner"):
-			conditions.append("party in (select name from tabCustomer where default_sales_partner=%s)")
-			values.append(self.filters.get("sales_partner"))
+			self.qb_selection_filter.append(
+				self.ple.party_isin(
+					qb.from_(self.customer).where(
+						self.customer.default_sales_partner == self.filters.get("payment_terms_template")
+					)
+				)
+			)
 
-	def add_supplier_filters(self, conditions, values):
+	def add_supplier_filters(self):
+		supplier = qb.DocType("Supplier")
 		if self.filters.get("supplier_group"):
-			conditions.append("""party in (select name from tabSupplier
-				where supplier_group=%s)""")
-			values.append(self.filters.get("supplier_group"))
+			self.qb_selection_filter.append(
+				self.ple.party.isin(
+					qb.from_(supplier)
+					.select(supplier.name)
+					.where(supplier.supplier_group == self.filters.get("supplier_group"))
+				)
+			)
 
 		if self.filters.get("payment_terms_template"):
-			conditions.append("party in (select name from tabSupplier where payment_terms=%s)")
-			values.append(self.filters.get("payment_terms_template"))
+			self.qb_selection_filter.append(
+				self.ple.party.isin(
+					qb.from_(supplier)
+					.select(supplier.name)
+					.where(supplier.payment_terms == self.filters.get("supplier_group"))
+				)
+			)
 
 	def get_hierarchical_filters(self, doctype, key):
 		lft, rgt = frappe.db.get_value(doctype, self.filters.get(key), ["lft", "rgt"])
 
-		return """party in (select name from tabCustomer
-			where exists(select name from `tab{doctype}` where lft >= {lft} and rgt <= {rgt}
-				and name=tabCustomer.{key}))""".format(
-					doctype=doctype, lft=lft, rgt=rgt, key=key)
+		doc = qb.DocType(doctype)
+		ple = self.ple
+		customer = self.customer
+		groups = qb.from_(doc).select(doc.name).where((doc.lft >= lft) & (doc.rgt <= rgt))
+		customers = qb.from_(customer).select(customer.name).where(customer[key].isin(groups))
+		self.qb_selection_filter.append(ple.isin(ple.party.isin(customers)))
 
-	def add_accounting_dimensions_filters(self, conditions, values):
+	def add_accounting_dimensions_filters(self):
 		accounting_dimensions = get_accounting_dimensions(as_list=False)
 
 		if accounting_dimensions:
 			for dimension in accounting_dimensions:
 				if self.filters.get(dimension.fieldname):
-					if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
-						self.filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
-							self.filters.get(dimension.fieldname))
-					conditions.append("{0} in %s".format(dimension.fieldname))
-					values.append(tuple(self.filters.get(dimension.fieldname)))
+					if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"):
+						self.filters[dimension.fieldname] = get_dimension_with_children(
+							dimension.document_type, self.filters.get(dimension.fieldname)
+						)
+						self.qb_selection_filter.append(
+							self.ple[dimension.fieldname].isin(self.filters[dimension.fieldname])
+						)
+					else:
+						self.qb_selection_filter.append(
+							self.ple[dimension.fieldname] == self.filters[dimension.fieldname]
+						)
 
-	def get_gle_balance(self, gle):
-		# get the balance of the GL (debit - credit) or reverse balance based on report type
-		return gle.get(self.dr_or_cr) - self.get_reverse_balance(gle)
-
-	def get_gle_balance_in_account_currency(self, gle):
-		# get the balance of the GL (debit - credit) or reverse balance based on report type
-		return gle.get(self.dr_or_cr + '_in_account_currency') - self.get_reverse_balance_in_account_currency(gle)
-
-	def get_reverse_balance_in_account_currency(self, gle):
-		return gle.get('debit_in_account_currency' if self.dr_or_cr=='credit' else 'credit_in_account_currency')
-
-	def get_reverse_balance(self, gle):
-		# get "credit" balance if report type is "debit" and vice versa
-		return gle.get('debit' if self.dr_or_cr=='credit' else 'credit')
-
-	def is_invoice(self, gle):
-		if gle.voucher_type in ('Sales Invoice', 'Purchase Invoice'):
+	def is_invoice(self, ple):
+		if ple.voucher_type in ("Sales Invoice", "Purchase Invoice"):
 			return True
 
 	def get_party_details(self, party):
 		if not party in self.party_details:
-			if self.party_type == 'Customer':
-				self.party_details[party] = frappe.db.get_value('Customer', party, ['customer_name',
-					'territory', 'customer_group', 'customer_primary_contact'], as_dict=True)
+			if self.party_type == "Customer":
+				self.party_details[party] = frappe.db.get_value(
+					"Customer",
+					party,
+					["customer_name", "territory", "customer_group", "customer_primary_contact"],
+					as_dict=True,
+				)
 			else:
-				self.party_details[party] = frappe.db.get_value('Supplier', party, ['supplier_name',
-					'supplier_group'], as_dict=True)
+				self.party_details[party] = frappe.db.get_value(
+					"Supplier", party, ["supplier_name", "supplier_group"], as_dict=True
+				)
 
 		return self.party_details[party]
 
-
 	def get_columns(self):
 		self.columns = []
-		self.add_column('Posting Date', fieldtype='Date')
-		self.add_column(label=_(self.party_type), fieldname='party',
-			fieldtype='Link', options=self.party_type, width=180)
+		self.add_column("Posting Date", fieldtype="Date")
+		self.add_column(
+			label=_(self.party_type),
+			fieldname="party",
+			fieldtype="Link",
+			options=self.party_type,
+			width=180,
+		)
+		self.add_column(
+			label="Receivable Account" if self.party_type == "Customer" else "Payable Account",
+			fieldname="party_account",
+			fieldtype="Link",
+			options="Account",
+			width=180,
+		)
 
 		if self.party_naming_by == "Naming Series":
-			self.add_column(_('{0} Name').format(self.party_type),
-				fieldname = scrub(self.party_type) + '_name', fieldtype='Data')
+			self.add_column(
+				_("{0} Name").format(self.party_type),
+				fieldname=scrub(self.party_type) + "_name",
+				fieldtype="Data",
+			)
 
-		if self.party_type == 'Customer':
-			self.add_column(_("Customer Contact"), fieldname='customer_primary_contact',
-				fieldtype='Link', options='Contact')
+		if self.party_type == "Customer":
+			self.add_column(
+				_("Customer Contact"),
+				fieldname="customer_primary_contact",
+				fieldtype="Link",
+				options="Contact",
+			)
 
-		self.add_column(label=_('Cost Center'), fieldname='cost_center', fieldtype='Data')
-		self.add_column(label=_('Voucher Type'), fieldname='voucher_type', fieldtype='Data')
-		self.add_column(label=_('Voucher No'), fieldname='voucher_no', fieldtype='Dynamic Link',
-			options='voucher_type', width=180)
+		self.add_column(label=_("Cost Center"), fieldname="cost_center", fieldtype="Data")
+		self.add_column(label=_("Voucher Type"), fieldname="voucher_type", fieldtype="Data")
+		self.add_column(
+			label=_("Voucher No"),
+			fieldname="voucher_no",
+			fieldtype="Dynamic Link",
+			options="voucher_type",
+			width=180,
+		)
 
-		if self.filters.show_remarks:
-			self.add_column(label=_('Remarks'), fieldname='remarks', fieldtype='Text', width=200),
-
-		self.add_column(label='Due Date', fieldtype='Date')
+		self.add_column(label="Due Date", fieldtype="Date")
 
 		if self.party_type == "Supplier":
-			self.add_column(label=_('Bill No'), fieldname='bill_no', fieldtype='Data')
-			self.add_column(label=_('Bill Date'), fieldname='bill_date', fieldtype='Date')
+			self.add_column(label=_("Bill No"), fieldname="bill_no", fieldtype="Data")
+			self.add_column(label=_("Bill Date"), fieldname="bill_date", fieldtype="Date")
 
 		if self.filters.based_on_payment_terms:
-			self.add_column(label=_('Payment Term'), fieldname='payment_term', fieldtype='Data')
-			self.add_column(label=_('Invoice Grand Total'), fieldname='invoice_grand_total')
+			self.add_column(label=_("Payment Term"), fieldname="payment_term", fieldtype="Data")
+			self.add_column(label=_("Invoice Grand Total"), fieldname="invoice_grand_total")
 
-		self.add_column(_('Invoiced Amount'), fieldname='invoiced')
-		self.add_column(_('Paid Amount'), fieldname='paid')
+		self.add_column(_("Invoiced Amount"), fieldname="invoiced")
+		self.add_column(_("Paid Amount"), fieldname="paid")
 		if self.party_type == "Customer":
-			self.add_column(_('Credit Note'), fieldname='credit_note')
+			self.add_column(_("Credit Note"), fieldname="credit_note")
 		else:
 			# note: fieldname is still `credit_note`
-			self.add_column(_('Debit Note'), fieldname='credit_note')
-		self.add_column(_('Outstanding Amount'), fieldname='outstanding')
+			self.add_column(_("Debit Note"), fieldname="credit_note")
+		self.add_column(_("Outstanding Amount"), fieldname="outstanding")
 
 		self.setup_ageing_columns()
 
-		self.add_column(label=_('Currency'), fieldname='currency', fieldtype='Link', options='Currency', width=80)
+		self.add_column(
+			label=_("Currency"), fieldname="currency", fieldtype="Link", options="Currency", width=80
+		)
 
 		if self.filters.show_future_payments:
-			self.add_column(label=_('Future Payment Ref'), fieldname='future_ref', fieldtype='Data')
-			self.add_column(label=_('Future Payment Amount'), fieldname='future_amount')
-			self.add_column(label=_('Remaining Balance'), fieldname='remaining_balance')
+			self.add_column(label=_("Future Payment Ref"), fieldname="future_ref", fieldtype="Data")
+			self.add_column(label=_("Future Payment Amount"), fieldname="future_amount")
+			self.add_column(label=_("Remaining Balance"), fieldname="remaining_balance")
 
-		if self.filters.party_type == 'Customer':
-			self.add_column(label=_('Customer LPO'), fieldname='po_no', fieldtype='Data')
+		if self.filters.party_type == "Customer":
+			self.add_column(label=_("Customer LPO"), fieldname="po_no", fieldtype="Data")
 
 			# comma separated list of linked delivery notes
 			if self.filters.show_delivery_notes:
-				self.add_column(label=_('Delivery Notes'), fieldname='delivery_notes', fieldtype='Data')
-			self.add_column(label=_('Territory'), fieldname='territory', fieldtype='Link',
-				options='Territory')
-			self.add_column(label=_('Customer Group'), fieldname='customer_group', fieldtype='Link',
-				options='Customer Group')
+				self.add_column(label=_("Delivery Notes"), fieldname="delivery_notes", fieldtype="Data")
+			self.add_column(
+				label=_("Territory"), fieldname="territory", fieldtype="Link", options="Territory"
+			)
+			self.add_column(
+				label=_("Customer Group"),
+				fieldname="customer_group",
+				fieldtype="Link",
+				options="Customer Group",
+			)
 			if self.filters.show_sales_person:
-				self.add_column(label=_('Sales Person'), fieldname='sales_person', fieldtype='Data')
+				self.add_column(label=_("Sales Person"), fieldname="sales_person", fieldtype="Data")
 
 		if self.filters.party_type == "Supplier":
-			self.add_column(label=_('Supplier Group'), fieldname='supplier_group', fieldtype='Link',
-				options='Supplier Group')
+			self.add_column(
+				label=_("Supplier Group"),
+				fieldname="supplier_group",
+				fieldtype="Link",
+				options="Supplier Group",
+			)
 
-	def add_column(self, label, fieldname=None, fieldtype='Currency', options=None, width=120):
-		if not fieldname: fieldname = scrub(label)
-		if fieldtype=='Currency': options='currency'
-		if fieldtype=='Date': width = 90
+	def add_column(self, label, fieldname=None, fieldtype="Currency", options=None, width=120):
+		if not fieldname:
+			fieldname = scrub(label)
+		if fieldtype == "Currency":
+			options = "currency"
+		if fieldtype == "Date":
+			width = 90
 
-		self.columns.append(dict(
-			label=label,
-			fieldname=fieldname,
-			fieldtype=fieldtype,
-			options=options,
-			width=width
-		))
+		self.columns.append(
+			dict(label=label, fieldname=fieldname, fieldtype=fieldtype, options=options, width=width)
+		)
 
 	def setup_ageing_columns(self):
 		# for charts
 		self.ageing_column_labels = []
-		self.add_column(label=_('Age (Days)'), fieldname='age', fieldtype='Int', width=80)
+		self.add_column(label=_("Age (Days)"), fieldname="age", fieldtype="Int", width=80)
 
-		for i, label in enumerate(["0-{range1}".format(range1=self.filters["range1"]),
-			"{range1}-{range2}".format(range1=cint(self.filters["range1"])+ 1, range2=self.filters["range2"]),
-			"{range2}-{range3}".format(range2=cint(self.filters["range2"])+ 1, range3=self.filters["range3"]),
-			"{range3}-{range4}".format(range3=cint(self.filters["range3"])+ 1, range4=self.filters["range4"]),
-			"{range4}-{above}".format(range4=cint(self.filters["range4"])+ 1, above=_("Above"))]):
-				self.add_column(label=label, fieldname='range' + str(i+1))
-				self.ageing_column_labels.append(label)
+		for i, label in enumerate(
+			[
+				"0-{range1}".format(range1=self.filters["range1"]),
+				"{range1}-{range2}".format(
+					range1=cint(self.filters["range1"]) + 1, range2=self.filters["range2"]
+				),
+				"{range2}-{range3}".format(
+					range2=cint(self.filters["range2"]) + 1, range3=self.filters["range3"]
+				),
+				"{range3}-{range4}".format(
+					range3=cint(self.filters["range3"]) + 1, range4=self.filters["range4"]
+				),
+				"{range4}-{above}".format(range4=cint(self.filters["range4"]) + 1, above=_("Above")),
+			]
+		):
+			self.add_column(label=label, fieldname="range" + str(i + 1))
+			self.ageing_column_labels.append(label)
 
 	def get_chart_data(self):
 		rows = []
@@ -865,14 +1004,9 @@
 			if not cint(row.bold):
 				values = [row.range1, row.range2, row.range3, row.range4, row.range5]
 				precision = cint(frappe.db.get_default("float_precision")) or 2
-				rows.append({
-					'values': [flt(val, precision) for val in values]
-				})
+				rows.append({"values": [flt(val, precision) for val in values]})
 
 		self.chart = {
-			"data": {
-				'labels': self.ageing_column_labels,
-				'datasets': rows
-			},
-			"type": 'percentage'
+			"data": {"labels": self.ageing_column_labels, "datasets": rows},
+			"type": "percentage",
 		}
diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
index ab95c93..edddbbc 100644
--- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
@@ -12,15 +12,16 @@
 	def test_accounts_receivable(self):
 		frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company 2'")
 		frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 2'")
+		frappe.db.sql("delete from `tabPayment Ledger Entry` where company='_Test Company 2'")
 
 		filters = {
-			'company': '_Test Company 2',
-			'based_on_payment_terms': 1,
-			'report_date': today(),
-			'range1': 30,
-			'range2': 60,
-			'range3': 90,
-			'range4': 120
+			"company": "_Test Company 2",
+			"based_on_payment_terms": 1,
+			"report_date": today(),
+			"range1": 30,
+			"range2": 60,
+			"range3": 90,
+			"range4": 120,
 		}
 
 		# check invoice grand total and invoiced column's value for 3 payment terms
@@ -30,8 +31,8 @@
 		expected_data = [[100, 30], [100, 50], [100, 20]]
 
 		for i in range(3):
-			row = report[1][i-1]
-			self.assertEqual(expected_data[i-1], [row.invoice_grand_total, row.invoiced])
+			row = report[1][i - 1]
+			self.assertEqual(expected_data[i - 1], [row.invoice_grand_total, row.invoiced])
 
 		# check invoice grand total, invoiced, paid and outstanding column's value after payment
 		make_payment(name)
@@ -40,41 +41,65 @@
 		expected_data_after_payment = [[100, 50, 10, 40], [100, 20, 0, 20]]
 
 		for i in range(2):
-			row = report[1][i-1]
-			self.assertEqual(expected_data_after_payment[i-1],
-				[row.invoice_grand_total, row.invoiced, row.paid, row.outstanding])
+			row = report[1][i - 1]
+			self.assertEqual(
+				expected_data_after_payment[i - 1],
+				[row.invoice_grand_total, row.invoiced, row.paid, row.outstanding],
+			)
 
 		# check invoice grand total, invoiced, paid and outstanding column's value after credit note
 		make_credit_note(name)
 		report = execute(filters)
 
-		expected_data_after_credit_note = [100, 0, 0, 40, -40]
+		expected_data_after_credit_note = [100, 0, 0, 40, -40, "Debtors - _TC2"]
 
 		row = report[1][0]
-		self.assertEqual(expected_data_after_credit_note,
-			[row.invoice_grand_total, row.invoiced, row.paid, row.credit_note, row.outstanding])
+		self.assertEqual(
+			expected_data_after_credit_note,
+			[
+				row.invoice_grand_total,
+				row.invoiced,
+				row.paid,
+				row.credit_note,
+				row.outstanding,
+				row.party_account,
+			],
+		)
+
 
 def make_sales_invoice():
 	frappe.set_user("Administrator")
 
-	si = create_sales_invoice(company="_Test Company 2",
-			customer = '_Test Customer 2',
-			currency = 'EUR',
-			warehouse = 'Finished Goods - _TC2',
-			debit_to = 'Debtors - _TC2',
-			income_account = 'Sales - _TC2',
-			expense_account = 'Cost of Goods Sold - _TC2',
-			cost_center = 'Main - _TC2',
-			do_not_save=1)
+	si = create_sales_invoice(
+		company="_Test Company 2",
+		customer="_Test Customer 2",
+		currency="EUR",
+		warehouse="Finished Goods - _TC2",
+		debit_to="Debtors - _TC2",
+		income_account="Sales - _TC2",
+		expense_account="Cost of Goods Sold - _TC2",
+		cost_center="Main - _TC2",
+		do_not_save=1,
+	)
 
-	si.append('payment_schedule', dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30))
-	si.append('payment_schedule', dict(due_date=getdate(add_days(today(), 60)), invoice_portion=50.00, payment_amount=50))
-	si.append('payment_schedule', dict(due_date=getdate(add_days(today(), 90)), invoice_portion=20.00, payment_amount=20))
+	si.append(
+		"payment_schedule",
+		dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30),
+	)
+	si.append(
+		"payment_schedule",
+		dict(due_date=getdate(add_days(today(), 60)), invoice_portion=50.00, payment_amount=50),
+	)
+	si.append(
+		"payment_schedule",
+		dict(due_date=getdate(add_days(today(), 90)), invoice_portion=20.00, payment_amount=20),
+	)
 
 	si.submit()
 
 	return si.name
 
+
 def make_payment(docname):
 	pe = get_payment_entry("Sales Invoice", docname, bank_account="Cash - _TC2", party_amount=40)
 	pe.paid_from = "Debtors - _TC2"
@@ -83,14 +108,16 @@
 
 
 def make_credit_note(docname):
-	create_sales_invoice(company="_Test Company 2",
-		customer = '_Test Customer 2',
-		currency = 'EUR',
-		qty = -1,
-		warehouse = 'Finished Goods - _TC2',
-		debit_to = 'Debtors - _TC2',
-		income_account = 'Sales - _TC2',
-		expense_account = 'Cost of Goods Sold - _TC2',
-		cost_center = 'Main - _TC2',
-		is_return = 1,
-		return_against = docname)
+	create_sales_invoice(
+		company="_Test Company 2",
+		customer="_Test Customer 2",
+		currency="EUR",
+		qty=-1,
+		warehouse="Finished Goods - _TC2",
+		debit_to="Debtors - _TC2",
+		income_account="Sales - _TC2",
+		expense_account="Cost of Goods Sold - _TC2",
+		cost_center="Main - _TC2",
+		is_return=1,
+		return_against=docname,
+	)
diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
index 8e3bd8b..889f5a2 100644
--- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
+++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
@@ -18,10 +18,13 @@
 
 	return AccountsReceivableSummary(filters).run(args)
 
+
 class AccountsReceivableSummary(ReceivablePayableReport):
 	def run(self, args):
-		self.party_type = args.get('party_type')
-		self.party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1])
+		self.party_type = args.get("party_type")
+		self.party_naming_by = frappe.db.get_value(
+			args.get("naming_by")[0], None, args.get("naming_by")[1]
+		)
 		self.get_columns()
 		self.get_data(args)
 		return self.columns, self.data
@@ -33,8 +36,15 @@
 
 		self.get_party_total(args)
 
-		party_advance_amount = get_partywise_advanced_payment_amount(self.party_type,
-			self.filters.report_date, self.filters.show_future_payments, self.filters.company) or {}
+		party_advance_amount = (
+			get_partywise_advanced_payment_amount(
+				self.party_type,
+				self.filters.report_date,
+				self.filters.show_future_payments,
+				self.filters.company,
+			)
+			or {}
+		)
 
 		if self.filters.show_gl_balance:
 			gl_balance_map = get_gl_balance(self.filters.report_date)
@@ -47,7 +57,9 @@
 
 			row.party = party
 			if self.party_naming_by == "Naming Series":
-				row.party_name = frappe.get_cached_value(self.party_type, party, scrub(self.party_type) + "_name")
+				row.party_name = frappe.get_cached_value(
+					self.party_type, party, scrub(self.party_type) + "_name"
+				)
 
 			row.update(party_dict)
 
@@ -80,24 +92,29 @@
 			self.set_party_details(d)
 
 	def init_party_total(self, row):
-		self.party_total.setdefault(row.party, frappe._dict({
-			"invoiced": 0.0,
-			"paid": 0.0,
-			"credit_note": 0.0,
-			"outstanding": 0.0,
-			"range1": 0.0,
-			"range2": 0.0,
-			"range3": 0.0,
-			"range4": 0.0,
-			"range5": 0.0,
-			"total_due": 0.0,
-			"sales_person": []
-		}))
+		self.party_total.setdefault(
+			row.party,
+			frappe._dict(
+				{
+					"invoiced": 0.0,
+					"paid": 0.0,
+					"credit_note": 0.0,
+					"outstanding": 0.0,
+					"range1": 0.0,
+					"range2": 0.0,
+					"range3": 0.0,
+					"range4": 0.0,
+					"range5": 0.0,
+					"total_due": 0.0,
+					"sales_person": [],
+				}
+			),
+		)
 
 	def set_party_details(self, row):
 		self.party_total[row.party].currency = row.currency
 
-		for key in ('territory', 'customer_group', 'supplier_group'):
+		for key in ("territory", "customer_group", "supplier_group"):
 			if row.get(key):
 				self.party_total[row.party][key] = row.get(key)
 
@@ -106,52 +123,84 @@
 
 	def get_columns(self):
 		self.columns = []
-		self.add_column(label=_(self.party_type), fieldname='party',
-			fieldtype='Link', options=self.party_type, width=180)
+		self.add_column(
+			label=_(self.party_type),
+			fieldname="party",
+			fieldtype="Link",
+			options=self.party_type,
+			width=180,
+		)
 
 		if self.party_naming_by == "Naming Series":
-			self.add_column(_('{0} Name').format(self.party_type),
-				fieldname = 'party_name', fieldtype='Data')
+			self.add_column(_("{0} Name").format(self.party_type), fieldname="party_name", fieldtype="Data")
 
-		credit_debit_label = "Credit Note" if self.party_type == 'Customer' else "Debit Note"
+		credit_debit_label = "Credit Note" if self.party_type == "Customer" else "Debit Note"
 
-		self.add_column(_('Advance Amount'), fieldname='advance')
-		self.add_column(_('Invoiced Amount'), fieldname='invoiced')
-		self.add_column(_('Paid Amount'), fieldname='paid')
-		self.add_column(_(credit_debit_label), fieldname='credit_note')
-		self.add_column(_('Outstanding Amount'), fieldname='outstanding')
+		self.add_column(_("Advance Amount"), fieldname="advance")
+		self.add_column(_("Invoiced Amount"), fieldname="invoiced")
+		self.add_column(_("Paid Amount"), fieldname="paid")
+		self.add_column(_(credit_debit_label), fieldname="credit_note")
+		self.add_column(_("Outstanding Amount"), fieldname="outstanding")
 
 		if self.filters.show_gl_balance:
-			self.add_column(_('GL Balance'), fieldname='gl_balance')
-			self.add_column(_('Difference'), fieldname='diff')
+			self.add_column(_("GL Balance"), fieldname="gl_balance")
+			self.add_column(_("Difference"), fieldname="diff")
 
 		self.setup_ageing_columns()
 
 		if self.party_type == "Customer":
-			self.add_column(label=_('Territory'), fieldname='territory', fieldtype='Link',
-				options='Territory')
-			self.add_column(label=_('Customer Group'), fieldname='customer_group', fieldtype='Link',
-				options='Customer Group')
+			self.add_column(
+				label=_("Territory"), fieldname="territory", fieldtype="Link", options="Territory"
+			)
+			self.add_column(
+				label=_("Customer Group"),
+				fieldname="customer_group",
+				fieldtype="Link",
+				options="Customer Group",
+			)
 			if self.filters.show_sales_person:
-				self.add_column(label=_('Sales Person'), fieldname='sales_person', fieldtype='Data')
+				self.add_column(label=_("Sales Person"), fieldname="sales_person", fieldtype="Data")
 		else:
-			self.add_column(label=_('Supplier Group'), fieldname='supplier_group', fieldtype='Link',
-				options='Supplier Group')
+			self.add_column(
+				label=_("Supplier Group"),
+				fieldname="supplier_group",
+				fieldtype="Link",
+				options="Supplier Group",
+			)
 
-		self.add_column(label=_('Currency'), fieldname='currency', fieldtype='Link',
-			options='Currency', width=80)
+		self.add_column(
+			label=_("Currency"), fieldname="currency", fieldtype="Link", options="Currency", width=80
+		)
 
 	def setup_ageing_columns(self):
-		for i, label in enumerate(["0-{range1}".format(range1=self.filters["range1"]),
-			"{range1}-{range2}".format(range1=cint(self.filters["range1"])+ 1, range2=self.filters["range2"]),
-			"{range2}-{range3}".format(range2=cint(self.filters["range2"])+ 1, range3=self.filters["range3"]),
-			"{range3}-{range4}".format(range3=cint(self.filters["range3"])+ 1, range4=self.filters["range4"]),
-			"{range4}-{above}".format(range4=cint(self.filters["range4"])+ 1, above=_("Above"))]):
-				self.add_column(label=label, fieldname='range' + str(i+1))
+		for i, label in enumerate(
+			[
+				"0-{range1}".format(range1=self.filters["range1"]),
+				"{range1}-{range2}".format(
+					range1=cint(self.filters["range1"]) + 1, range2=self.filters["range2"]
+				),
+				"{range2}-{range3}".format(
+					range2=cint(self.filters["range2"]) + 1, range3=self.filters["range3"]
+				),
+				"{range3}-{range4}".format(
+					range3=cint(self.filters["range3"]) + 1, range4=self.filters["range4"]
+				),
+				"{range4}-{above}".format(range4=cint(self.filters["range4"]) + 1, above=_("Above")),
+			]
+		):
+			self.add_column(label=label, fieldname="range" + str(i + 1))
 
 		# Add column for total due amount
-		self.add_column(label="Total Amount Due", fieldname='total_due')
+		self.add_column(label="Total Amount Due", fieldname="total_due")
+
 
 def get_gl_balance(report_date):
-	return frappe._dict(frappe.db.get_all("GL Entry", fields=['party', 'sum(debit -  credit)'],
-		filters={'posting_date': ("<=", report_date), 'is_cancelled': 0}, group_by='party', as_list=1))
+	return frappe._dict(
+		frappe.db.get_all(
+			"GL Entry",
+			fields=["party", "sum(debit -  credit)"],
+			filters={"posting_date": ("<=", report_date), "is_cancelled": 0},
+			group_by="party",
+			as_list=1,
+		)
+	)
diff --git a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py
index 98f5b74..57d8049 100644
--- a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py
+++ b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py
@@ -11,34 +11,44 @@
 	columns, data = get_columns(), get_data(filters)
 	return columns, data
 
+
 def get_data(filters):
 	data = []
-	depreciation_accounts = frappe.db.sql_list(""" select name from tabAccount
-		where ifnull(account_type, '') = 'Depreciation' """)
+	depreciation_accounts = frappe.db.sql_list(
+		""" select name from tabAccount
+		where ifnull(account_type, '') = 'Depreciation' """
+	)
 
-	filters_data = [["company", "=", filters.get('company')],
-		["posting_date", ">=", filters.get('from_date')],
-		["posting_date", "<=", filters.get('to_date')],
+	filters_data = [
+		["company", "=", filters.get("company")],
+		["posting_date", ">=", filters.get("from_date")],
+		["posting_date", "<=", filters.get("to_date")],
 		["against_voucher_type", "=", "Asset"],
-		["account", "in", depreciation_accounts]]
+		["account", "in", depreciation_accounts],
+	]
 
 	if filters.get("asset"):
 		filters_data.append(["against_voucher", "=", filters.get("asset")])
 
 	if filters.get("asset_category"):
 
-		assets = frappe.db.sql_list("""select name from tabAsset
-			where asset_category = %s and docstatus=1""", filters.get("asset_category"))
+		assets = frappe.db.sql_list(
+			"""select name from tabAsset
+			where asset_category = %s and docstatus=1""",
+			filters.get("asset_category"),
+		)
 
 		filters_data.append(["against_voucher", "in", assets])
 
 	if filters.get("finance_book"):
-		filters_data.append(["finance_book", "in", ['', filters.get('finance_book')]])
+		filters_data.append(["finance_book", "in", ["", filters.get("finance_book")]])
 
-	gl_entries = frappe.get_all('GL Entry',
-		filters= filters_data,
-		fields = ["against_voucher", "debit_in_account_currency as debit", "voucher_no", "posting_date"],
-		order_by= "against_voucher, posting_date")
+	gl_entries = frappe.get_all(
+		"GL Entry",
+		filters=filters_data,
+		fields=["against_voucher", "debit_in_account_currency as debit", "voucher_no", "posting_date"],
+		order_by="against_voucher, posting_date",
+	)
 
 	if not gl_entries:
 		return data
@@ -55,29 +65,40 @@
 				asset_data.accumulated_depreciation_amount += d.debit
 
 			row = frappe._dict(asset_data)
-			row.update({
-				"depreciation_amount": d.debit,
-				"depreciation_date": d.posting_date,
-				"amount_after_depreciation": (flt(row.gross_purchase_amount) -
-					flt(row.accumulated_depreciation_amount)),
-				"depreciation_entry": d.voucher_no
-			})
+			row.update(
+				{
+					"depreciation_amount": d.debit,
+					"depreciation_date": d.posting_date,
+					"amount_after_depreciation": (
+						flt(row.gross_purchase_amount) - flt(row.accumulated_depreciation_amount)
+					),
+					"depreciation_entry": d.voucher_no,
+				}
+			)
 
 			data.append(row)
 
 	return data
 
+
 def get_assets_details(assets):
 	assets_details = {}
 
-	fields = ["name as asset", "gross_purchase_amount",
-		"asset_category", "status", "depreciation_method", "purchase_date"]
+	fields = [
+		"name as asset",
+		"gross_purchase_amount",
+		"asset_category",
+		"status",
+		"depreciation_method",
+		"purchase_date",
+	]
 
-	for d in frappe.get_all("Asset", fields = fields, filters = {'name': ('in', assets)}):
+	for d in frappe.get_all("Asset", fields=fields, filters={"name": ("in", assets)}):
 		assets_details.setdefault(d.asset, d)
 
 	return assets_details
 
+
 def get_columns():
 	return [
 		{
@@ -85,68 +106,58 @@
 			"fieldname": "asset",
 			"fieldtype": "Link",
 			"options": "Asset",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("Depreciation Date"),
 			"fieldname": "depreciation_date",
 			"fieldtype": "Date",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("Purchase Amount"),
 			"fieldname": "gross_purchase_amount",
 			"fieldtype": "Currency",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("Depreciation Amount"),
 			"fieldname": "depreciation_amount",
 			"fieldtype": "Currency",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Accumulated Depreciation Amount"),
 			"fieldname": "accumulated_depreciation_amount",
 			"fieldtype": "Currency",
-			"width": 210
+			"width": 210,
 		},
 		{
 			"label": _("Amount After Depreciation"),
 			"fieldname": "amount_after_depreciation",
 			"fieldtype": "Currency",
-			"width": 180
+			"width": 180,
 		},
 		{
 			"label": _("Depreciation Entry"),
 			"fieldname": "depreciation_entry",
 			"fieldtype": "Link",
 			"options": "Journal Entry",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Asset Category"),
 			"fieldname": "asset_category",
 			"fieldtype": "Link",
 			"options": "Asset Category",
-			"width": 120
+			"width": 120,
 		},
-		{
-			"label": _("Current Status"),
-			"fieldname": "status",
-			"fieldtype": "Data",
-			"width": 120
-		},
+		{"label": _("Current Status"), "fieldname": "status", "fieldtype": "Data", "width": 120},
 		{
 			"label": _("Depreciation Method"),
 			"fieldname": "depreciation_method",
 			"fieldtype": "Data",
-			"width": 130
+			"width": 130,
 		},
-		{
-			"label": _("Purchase Date"),
-			"fieldname": "purchase_date",
-			"fieldtype": "Date",
-			"width": 120
-		}
+		{"label": _("Purchase Date"), "fieldname": "purchase_date", "fieldtype": "Date", "width": 120},
 	]
diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py
index 0f9435f..ad9b1ba 100644
--- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py
+++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py
@@ -24,18 +24,33 @@
 		# row.asset_category = asset_category
 		row.update(asset_category)
 
-		row.cost_as_on_to_date = (flt(row.cost_as_on_from_date) + flt(row.cost_of_new_purchase) -
-				flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset))
+		row.cost_as_on_to_date = (
+			flt(row.cost_as_on_from_date)
+			+ flt(row.cost_of_new_purchase)
+			- flt(row.cost_of_sold_asset)
+			- flt(row.cost_of_scrapped_asset)
+		)
 
-		row.update(next(asset for asset in assets if asset["asset_category"] == asset_category.get("asset_category", "")))
-		row.accumulated_depreciation_as_on_to_date = (flt(row.accumulated_depreciation_as_on_from_date) +
-				flt(row.depreciation_amount_during_the_period) - flt(row.depreciation_eliminated_during_the_period))
+		row.update(
+			next(
+				asset
+				for asset in assets
+				if asset["asset_category"] == asset_category.get("asset_category", "")
+			)
+		)
+		row.accumulated_depreciation_as_on_to_date = (
+			flt(row.accumulated_depreciation_as_on_from_date)
+			+ flt(row.depreciation_amount_during_the_period)
+			- flt(row.depreciation_eliminated_during_the_period)
+		)
 
-		row.net_asset_value_as_on_from_date = (flt(row.cost_as_on_from_date) -
-				flt(row.accumulated_depreciation_as_on_from_date))
+		row.net_asset_value_as_on_from_date = flt(row.cost_as_on_from_date) - flt(
+			row.accumulated_depreciation_as_on_from_date
+		)
 
-		row.net_asset_value_as_on_to_date = (flt(row.cost_as_on_to_date) -
-				flt(row.accumulated_depreciation_as_on_to_date))
+		row.net_asset_value_as_on_to_date = flt(row.cost_as_on_to_date) - flt(
+			row.accumulated_depreciation_as_on_to_date
+		)
 
 		data.append(row)
 
@@ -43,7 +58,8 @@
 
 
 def get_asset_categories(filters):
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		SELECT asset_category,
 			   ifnull(sum(case when purchase_date < %(from_date)s then
 							   case when ifnull(disposal_date, 0) = 0 or disposal_date >= %(from_date)s then
@@ -84,10 +100,15 @@
 		from `tabAsset`
 		where docstatus=1 and company=%(company)s and purchase_date <= %(to_date)s
 		group by asset_category
-	""", {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, as_dict=1)
+	""",
+		{"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company},
+		as_dict=1,
+	)
+
 
 def get_assets(filters):
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		SELECT results.asset_category,
 			   sum(results.accumulated_depreciation_as_on_from_date) as accumulated_depreciation_as_on_from_date,
 			   sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
@@ -130,7 +151,10 @@
 			where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s
 			group by a.asset_category) as results
 		group by results.asset_category
-		""", {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, as_dict=1)
+		""",
+		{"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company},
+		as_dict=1,
+	)
 
 
 def get_columns(filters):
@@ -140,72 +164,72 @@
 			"fieldname": "asset_category",
 			"fieldtype": "Link",
 			"options": "Asset Category",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("Cost as on") + " " + formatdate(filters.day_before_from_date),
 			"fieldname": "cost_as_on_from_date",
 			"fieldtype": "Currency",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Cost of New Purchase"),
 			"fieldname": "cost_of_new_purchase",
 			"fieldtype": "Currency",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Cost of Sold Asset"),
 			"fieldname": "cost_of_sold_asset",
 			"fieldtype": "Currency",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Cost of Scrapped Asset"),
 			"fieldname": "cost_of_scrapped_asset",
 			"fieldtype": "Currency",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Cost as on") + " " + formatdate(filters.to_date),
 			"fieldname": "cost_as_on_to_date",
 			"fieldtype": "Currency",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Accumulated Depreciation as on") + " " + formatdate(filters.day_before_from_date),
 			"fieldname": "accumulated_depreciation_as_on_from_date",
 			"fieldtype": "Currency",
-			"width": 270
+			"width": 270,
 		},
 		{
 			"label": _("Depreciation Amount during the period"),
 			"fieldname": "depreciation_amount_during_the_period",
 			"fieldtype": "Currency",
-			"width": 240
+			"width": 240,
 		},
 		{
 			"label": _("Depreciation Eliminated due to disposal of assets"),
 			"fieldname": "depreciation_eliminated_during_the_period",
 			"fieldtype": "Currency",
-			"width": 300
+			"width": 300,
 		},
 		{
 			"label": _("Accumulated Depreciation as on") + " " + formatdate(filters.to_date),
 			"fieldname": "accumulated_depreciation_as_on_to_date",
 			"fieldtype": "Currency",
-			"width": 270
+			"width": 270,
 		},
 		{
 			"label": _("Net Asset value as on") + " " + formatdate(filters.day_before_from_date),
 			"fieldname": "net_asset_value_as_on_from_date",
 			"fieldtype": "Currency",
-			"width": 200
+			"width": 200,
 		},
 		{
 			"label": _("Net Asset value as on") + " " + formatdate(filters.to_date),
 			"fieldname": "net_asset_value_as_on_to_date",
 			"fieldtype": "Currency",
-			"width": 200
-		}
+			"width": 200,
+		},
 	]
diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py
index f10a5ea..07552e3 100644
--- a/erpnext/accounts/report/balance_sheet/balance_sheet.py
+++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py
@@ -15,26 +15,53 @@
 
 
 def execute(filters=None):
-	period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
-		filters.period_start_date, filters.period_end_date, filters.filter_based_on,
-		filters.periodicity, company=filters.company)
+	period_list = get_period_list(
+		filters.from_fiscal_year,
+		filters.to_fiscal_year,
+		filters.period_start_date,
+		filters.period_end_date,
+		filters.filter_based_on,
+		filters.periodicity,
+		company=filters.company,
+	)
 
-	currency = filters.presentation_currency or frappe.get_cached_value('Company',  filters.company,  "default_currency")
+	currency = filters.presentation_currency or frappe.get_cached_value(
+		"Company", filters.company, "default_currency"
+	)
 
-	asset = get_data(filters.company, "Asset", "Debit", period_list,
-		only_current_fiscal_year=False, filters=filters,
-		accumulated_values=filters.accumulated_values)
+	asset = get_data(
+		filters.company,
+		"Asset",
+		"Debit",
+		period_list,
+		only_current_fiscal_year=False,
+		filters=filters,
+		accumulated_values=filters.accumulated_values,
+	)
 
-	liability = get_data(filters.company, "Liability", "Credit", period_list,
-		only_current_fiscal_year=False, filters=filters,
-		accumulated_values=filters.accumulated_values)
+	liability = get_data(
+		filters.company,
+		"Liability",
+		"Credit",
+		period_list,
+		only_current_fiscal_year=False,
+		filters=filters,
+		accumulated_values=filters.accumulated_values,
+	)
 
-	equity = get_data(filters.company, "Equity", "Credit", period_list,
-		only_current_fiscal_year=False, filters=filters,
-		accumulated_values=filters.accumulated_values)
+	equity = get_data(
+		filters.company,
+		"Equity",
+		"Credit",
+		period_list,
+		only_current_fiscal_year=False,
+		filters=filters,
+		accumulated_values=filters.accumulated_values,
+	)
 
-	provisional_profit_loss, total_credit = get_provisional_profit_loss(asset, liability, equity,
-		period_list, filters.company, currency)
+	provisional_profit_loss, total_credit = get_provisional_profit_loss(
+		asset, liability, equity, period_list, filters.company, currency
+	)
 
 	message, opening_balance = check_opening_balance(asset, liability, equity)
 
@@ -42,19 +69,19 @@
 	data.extend(asset or [])
 	data.extend(liability or [])
 	data.extend(equity or [])
-	if opening_balance and round(opening_balance,2) !=0:
-		unclosed ={
+	if opening_balance and round(opening_balance, 2) != 0:
+		unclosed = {
 			"account_name": "'" + _("Unclosed Fiscal Years Profit / Loss (Credit)") + "'",
 			"account": "'" + _("Unclosed Fiscal Years Profit / Loss (Credit)") + "'",
 			"warn_if_negative": True,
-			"currency": currency
+			"currency": currency,
 		}
 		for period in period_list:
 			unclosed[period.key] = opening_balance
 			if provisional_profit_loss:
 				provisional_profit_loss[period.key] = provisional_profit_loss[period.key] - opening_balance
 
-		unclosed["total"]=opening_balance
+		unclosed["total"] = opening_balance
 		data.append(unclosed)
 
 	if provisional_profit_loss:
@@ -62,26 +89,32 @@
 	if total_credit:
 		data.append(total_credit)
 
-	columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, company=filters.company)
+	columns = get_columns(
+		filters.periodicity, period_list, filters.accumulated_values, company=filters.company
+	)
 
 	chart = get_chart_data(filters, columns, asset, liability, equity)
 
-	report_summary = get_report_summary(period_list, asset, liability, equity, provisional_profit_loss,
-		total_credit, currency, filters)
+	report_summary = get_report_summary(
+		period_list, asset, liability, equity, provisional_profit_loss, total_credit, currency, filters
+	)
 
 	return columns, data, message, chart, report_summary
 
-def get_provisional_profit_loss(asset, liability, equity, period_list, company, currency=None, consolidated=False):
+
+def get_provisional_profit_loss(
+	asset, liability, equity, period_list, company, currency=None, consolidated=False
+):
 	provisional_profit_loss = {}
 	total_row = {}
 	if asset and (liability or equity):
-		total = total_row_total=0
-		currency = currency or frappe.get_cached_value('Company',  company,  "default_currency")
+		total = total_row_total = 0
+		currency = currency or frappe.get_cached_value("Company", company, "default_currency")
 		total_row = {
 			"account_name": "'" + _("Total (Credit)") + "'",
 			"account": "'" + _("Total (Credit)") + "'",
 			"warn_if_negative": True,
-			"currency": currency
+			"currency": currency,
 		}
 		has_value = False
 
@@ -106,15 +139,18 @@
 			total_row["total"] = total_row_total
 
 		if has_value:
-			provisional_profit_loss.update({
-				"account_name": "'" + _("Provisional Profit / Loss (Credit)") + "'",
-				"account": "'" + _("Provisional Profit / Loss (Credit)") + "'",
-				"warn_if_negative": True,
-				"currency": currency
-			})
+			provisional_profit_loss.update(
+				{
+					"account_name": "'" + _("Provisional Profit / Loss (Credit)") + "'",
+					"account": "'" + _("Provisional Profit / Loss (Credit)") + "'",
+					"warn_if_negative": True,
+					"currency": currency,
+				}
+			)
 
 	return provisional_profit_loss, total_row
 
+
 def check_opening_balance(asset, liability, equity):
 	# Check if previous year balance sheet closed
 	opening_balance = 0
@@ -128,19 +164,29 @@
 
 	opening_balance = flt(opening_balance, float_precision)
 	if opening_balance:
-		return _("Previous Financial Year is not closed"),opening_balance
-	return None,None
+		return _("Previous Financial Year is not closed"), opening_balance
+	return None, None
 
-def get_report_summary(period_list, asset, liability, equity, provisional_profit_loss, total_credit, currency,
-	filters, consolidated=False):
+
+def get_report_summary(
+	period_list,
+	asset,
+	liability,
+	equity,
+	provisional_profit_loss,
+	total_credit,
+	currency,
+	filters,
+	consolidated=False,
+):
 
 	net_asset, net_liability, net_equity, net_provisional_profit_loss = 0.0, 0.0, 0.0, 0.0
 
-	if filters.get('accumulated_values'):
+	if filters.get("accumulated_values"):
 		period_list = [period_list[-1]]
 
 	# from consolidated financial statement
-	if filters.get('accumulated_in_group_company'):
+	if filters.get("accumulated_in_group_company"):
 		period_list = get_filtered_list_for_consolidated_report(filters, period_list)
 
 	for period in period_list:
@@ -155,33 +201,24 @@
 			net_provisional_profit_loss += provisional_profit_loss.get(key)
 
 	return [
-		{
-			"value": net_asset,
-			"label": "Total Asset",
-			"datatype": "Currency",
-			"currency": currency
-		},
+		{"value": net_asset, "label": _("Total Asset"), "datatype": "Currency", "currency": currency},
 		{
 			"value": net_liability,
-			"label": "Total Liability",
+			"label": _("Total Liability"),
 			"datatype": "Currency",
-			"currency": currency
+			"currency": currency,
 		},
-		{
-			"value": net_equity,
-			"label": "Total Equity",
-			"datatype": "Currency",
-			"currency": currency
-		},
+		{"value": net_equity, "label": _("Total Equity"), "datatype": "Currency", "currency": currency},
 		{
 			"value": net_provisional_profit_loss,
-			"label": "Provisional Profit / Loss (Credit)",
+			"label": _("Provisional Profit / Loss (Credit)"),
 			"indicator": "Green" if net_provisional_profit_loss > 0 else "Red",
 			"datatype": "Currency",
-			"currency": currency
-		}
+			"currency": currency,
+		},
 	]
 
+
 def get_chart_data(filters, columns, asset, liability, equity):
 	labels = [d.get("label") for d in columns[2:]]
 
@@ -197,18 +234,13 @@
 
 	datasets = []
 	if asset_data:
-		datasets.append({'name': _('Assets'), 'values': asset_data})
+		datasets.append({"name": _("Assets"), "values": asset_data})
 	if liability_data:
-		datasets.append({'name': _('Liabilities'), 'values': liability_data})
+		datasets.append({"name": _("Liabilities"), "values": liability_data})
 	if equity_data:
-		datasets.append({'name': _('Equity'), 'values': equity_data})
+		datasets.append({"name": _("Equity"), "values": equity_data})
 
-	chart = {
-		"data": {
-			'labels': labels,
-			'datasets': datasets
-		}
-	}
+	chart = {"data": {"labels": labels, "datasets": datasets}}
 
 	if not filters.accumulated_values:
 		chart["type"] = "bar"
diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
index b456e89..20f7643 100644
--- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
+++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
@@ -8,86 +8,88 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	columns = get_columns()
 	data = get_entries(filters)
 
 	return columns, data
 
+
 def get_columns():
-	columns = [{
+	columns = [
+		{
 			"label": _("Payment Document Type"),
 			"fieldname": "payment_document_type",
 			"fieldtype": "Link",
 			"options": "Doctype",
-			"width": 130
+			"width": 130,
 		},
 		{
 			"label": _("Payment Entry"),
 			"fieldname": "payment_entry",
 			"fieldtype": "Dynamic Link",
 			"options": "payment_document_type",
-			"width": 140
+			"width": 140,
 		},
-		{
-			"label": _("Posting Date"),
-			"fieldname": "posting_date",
-			"fieldtype": "Date",
-			"width": 100
-		},
-		{
-			"label": _("Cheque/Reference No"),
-			"fieldname": "cheque_no",
-			"width": 120
-		},
-		{
-			"label": _("Clearance Date"),
-			"fieldname": "clearance_date",
-			"fieldtype": "Date",
-			"width": 100
-		},
+		{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 100},
+		{"label": _("Cheque/Reference No"), "fieldname": "cheque_no", "width": 120},
+		{"label": _("Clearance Date"), "fieldname": "clearance_date", "fieldtype": "Date", "width": 100},
 		{
 			"label": _("Against Account"),
 			"fieldname": "against",
 			"fieldtype": "Link",
 			"options": "Account",
-			"width": 170
+			"width": 170,
 		},
-		{
-			"label": _("Amount"),
-			"fieldname": "amount",
-			"width": 120
-		}]
+		{"label": _("Amount"), "fieldname": "amount", "width": 120},
+	]
 
 	return columns
 
+
 def get_conditions(filters):
 	conditions = ""
 
-	if filters.get("from_date"): conditions += " and posting_date>=%(from_date)s"
-	if filters.get("to_date"): conditions += " and posting_date<=%(to_date)s"
+	if filters.get("from_date"):
+		conditions += " and posting_date>=%(from_date)s"
+	if filters.get("to_date"):
+		conditions += " and posting_date<=%(to_date)s"
 
 	return conditions
 
+
 def get_entries(filters):
 	conditions = get_conditions(filters)
-	journal_entries =  frappe.db.sql("""SELECT
+	journal_entries = frappe.db.sql(
+		"""SELECT
 			"Journal Entry", jv.name, jv.posting_date, jv.cheque_no,
 			jv.clearance_date, jvd.against_account, jvd.debit - jvd.credit
 		FROM
 			`tabJournal Entry Account` jvd, `tabJournal Entry` jv
 		WHERE
 			jvd.parent = jv.name and jv.docstatus=1 and jvd.account = %(account)s {0}
-			order by posting_date DESC, jv.name DESC""".format(conditions), filters, as_list=1)
+			order by posting_date DESC, jv.name DESC""".format(
+			conditions
+		),
+		filters,
+		as_list=1,
+	)
 
-	payment_entries =  frappe.db.sql("""SELECT
+	payment_entries = frappe.db.sql(
+		"""SELECT
 			"Payment Entry", name, posting_date, reference_no, clearance_date, party,
 			if(paid_from=%(account)s, paid_amount * -1, received_amount)
 		FROM
 			`tabPayment Entry`
 		WHERE
 			docstatus=1 and (paid_from = %(account)s or paid_to = %(account)s) {0}
-			order by posting_date DESC, name DESC""".format(conditions), filters, as_list=1)
+			order by posting_date DESC, name DESC""".format(
+			conditions
+		),
+		filters,
+		as_list=1,
+	)
 
 	return sorted(journal_entries + payment_entries, key=lambda k: k[2] or getdate(nowdate()))
diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
index b72d266..c41d0d1 100644
--- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
+++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
@@ -13,11 +13,13 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	columns = get_columns()
 
-	if not filters.get("account"): return columns, []
+	if not filters.get("account"):
+		return columns, []
 
 	account_currency = frappe.db.get_value("Account", filters.account, "account_currency")
 
@@ -25,102 +27,91 @@
 
 	balance_as_per_system = get_balance_on(filters["account"], filters["report_date"])
 
-	total_debit, total_credit = 0,0
+	total_debit, total_credit = 0, 0
 	for d in data:
 		total_debit += flt(d.debit)
 		total_credit += flt(d.credit)
 
 	amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters)
 
-	bank_bal = flt(balance_as_per_system) - flt(total_debit) + flt(total_credit) \
+	bank_bal = (
+		flt(balance_as_per_system)
+		- flt(total_debit)
+		+ flt(total_credit)
 		+ amounts_not_reflected_in_system
+	)
 
 	data += [
-		get_balance_row(_("Bank Statement balance as per General Ledger"), balance_as_per_system, account_currency),
+		get_balance_row(
+			_("Bank Statement balance as per General Ledger"), balance_as_per_system, account_currency
+		),
 		{},
 		{
 			"payment_entry": _("Outstanding Cheques and Deposits to clear"),
 			"debit": total_debit,
 			"credit": total_credit,
-			"account_currency": account_currency
+			"account_currency": account_currency,
 		},
-		get_balance_row(_("Cheques and Deposits incorrectly cleared"), amounts_not_reflected_in_system,
-			account_currency),
+		get_balance_row(
+			_("Cheques and Deposits incorrectly cleared"), amounts_not_reflected_in_system, account_currency
+		),
 		{},
-		get_balance_row(_("Calculated Bank Statement balance"), bank_bal, account_currency)
+		get_balance_row(_("Calculated Bank Statement balance"), bank_bal, account_currency),
 	]
 
 	return columns, data
 
+
 def get_columns():
 	return [
-		{
-			"fieldname": "posting_date",
-			"label": _("Posting Date"),
-			"fieldtype": "Date",
-			"width": 90
-		},
+		{"fieldname": "posting_date", "label": _("Posting Date"), "fieldtype": "Date", "width": 90},
 		{
 			"fieldname": "payment_document",
 			"label": _("Payment Document Type"),
 			"fieldtype": "Data",
-			"width": 220
+			"width": 220,
 		},
 		{
 			"fieldname": "payment_entry",
 			"label": _("Payment Document"),
 			"fieldtype": "Dynamic Link",
 			"options": "payment_document",
-			"width": 220
+			"width": 220,
 		},
 		{
 			"fieldname": "debit",
 			"label": _("Debit"),
 			"fieldtype": "Currency",
 			"options": "account_currency",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"fieldname": "credit",
 			"label": _("Credit"),
 			"fieldtype": "Currency",
 			"options": "account_currency",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"fieldname": "against_account",
 			"label": _("Against Account"),
 			"fieldtype": "Link",
 			"options": "Account",
-			"width": 200
+			"width": 200,
 		},
-		{
-			"fieldname": "reference_no",
-			"label": _("Reference"),
-			"fieldtype": "Data",
-			"width": 100
-		},
-		{
-			"fieldname": "ref_date",
-			"label": _("Ref Date"),
-			"fieldtype": "Date",
-			"width": 110
-		},
-		{
-			"fieldname": "clearance_date",
-			"label": _("Clearance Date"),
-			"fieldtype": "Date",
-			"width": 110
-		},
+		{"fieldname": "reference_no", "label": _("Reference"), "fieldtype": "Data", "width": 100},
+		{"fieldname": "ref_date", "label": _("Ref Date"), "fieldtype": "Date", "width": 110},
+		{"fieldname": "clearance_date", "label": _("Clearance Date"), "fieldtype": "Date", "width": 110},
 		{
 			"fieldname": "account_currency",
 			"label": _("Currency"),
 			"fieldtype": "Link",
 			"options": "Currency",
-			"width": 100
-		}
+			"width": 100,
+		},
 	]
 
+
 def get_entries(filters):
 	journal_entries = get_journal_entries(filters)
 
@@ -132,11 +123,15 @@
 	if filters.include_pos_transactions:
 		pos_entries = get_pos_entries(filters)
 
-	return sorted(list(payment_entries)+list(journal_entries+list(pos_entries) + list(loan_entries)),
-			key=lambda k: getdate(k['posting_date']))
+	return sorted(
+		list(payment_entries) + list(journal_entries + list(pos_entries) + list(loan_entries)),
+		key=lambda k: getdate(k["posting_date"]),
+	)
+
 
 def get_journal_entries(filters):
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select "Journal Entry" as payment_document, jv.posting_date,
 			jv.name as payment_entry, jvd.debit_in_account_currency as debit,
 			jvd.credit_in_account_currency as credit, jvd.against_account,
@@ -146,10 +141,15 @@
 		where jvd.parent = jv.name and jv.docstatus=1
 			and jvd.account = %(account)s and jv.posting_date <= %(report_date)s
 			and ifnull(jv.clearance_date, '4000-01-01') > %(report_date)s
-			and ifnull(jv.is_opening, 'No') = 'No'""", filters, as_dict=1)
+			and ifnull(jv.is_opening, 'No') = 'No'""",
+		filters,
+		as_dict=1,
+	)
+
 
 def get_payment_entries(filters):
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select
 			"Payment Entry" as payment_document, name as payment_entry,
 			reference_no, reference_date as ref_date,
@@ -162,10 +162,15 @@
 			(paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
 			and posting_date <= %(report_date)s
 			and ifnull(clearance_date, '4000-01-01') > %(report_date)s
-	""", filters, as_dict=1)
+	""",
+		filters,
+		as_dict=1,
+	)
+
 
 def get_pos_entries(filters):
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 			select
 				"Sales Invoice Payment" as payment_document, sip.name as payment_entry, sip.amount as debit,
 				si.posting_date, si.debit_to as against_account, sip.clearance_date,
@@ -177,60 +182,73 @@
 				ifnull(sip.clearance_date, '4000-01-01') > %(report_date)s
 			order by
 				si.posting_date ASC, si.name DESC
-		""", filters, as_dict=1)
+		""",
+		filters,
+		as_dict=1,
+	)
+
 
 def get_loan_entries(filters):
 	loan_docs = []
 	for doctype in ["Loan Disbursement", "Loan Repayment"]:
 		loan_doc = frappe.qb.DocType(doctype)
-		ifnull = CustomFunction('IFNULL', ['value', 'default'])
+		ifnull = CustomFunction("IFNULL", ["value", "default"])
 
 		if doctype == "Loan Disbursement":
 			amount_field = (loan_doc.disbursed_amount).as_("credit")
 			posting_date = (loan_doc.disbursement_date).as_("posting_date")
 			account = loan_doc.disbursement_account
+			salary_condition = loan_doc.docstatus == 1
 		else:
 			amount_field = (loan_doc.amount_paid).as_("debit")
 			posting_date = (loan_doc.posting_date).as_("posting_date")
 			account = loan_doc.payment_account
+			salary_condition = loan_doc.repay_from_salary == 0
 
-		entries = frappe.qb.from_(loan_doc).select(
-			ConstantColumn(doctype).as_("payment_document"),
-			(loan_doc.name).as_("payment_entry"),
-			(loan_doc.reference_number).as_("reference_no"),
-			(loan_doc.reference_date).as_("ref_date"),
-			amount_field,
-			posting_date,
-		).where(
-			loan_doc.docstatus == 1
-		).where(
-			account == filters.get('account')
-		).where(
-			posting_date <= getdate(filters.get('report_date'))
-		).where(
-			ifnull(loan_doc.clearance_date, '4000-01-01') > getdate(filters.get('report_date'))
-		).run(as_dict=1)
+		query = (
+			frappe.qb.from_(loan_doc)
+			.select(
+				ConstantColumn(doctype).as_("payment_document"),
+				(loan_doc.name).as_("payment_entry"),
+				(loan_doc.reference_number).as_("reference_no"),
+				(loan_doc.reference_date).as_("ref_date"),
+				amount_field,
+				posting_date,
+			)
+			.where(loan_doc.docstatus == 1)
+			.where(salary_condition)
+			.where(account == filters.get("account"))
+			.where(posting_date <= getdate(filters.get("report_date")))
+			.where(ifnull(loan_doc.clearance_date, "4000-01-01") > getdate(filters.get("report_date")))
+		)
 
+		entries = query.run(as_dict=1)
 		loan_docs.extend(entries)
 
 	return loan_docs
 
 
 def get_amounts_not_reflected_in_system(filters):
-	je_amount = frappe.db.sql("""
+	je_amount = frappe.db.sql(
+		"""
 		select sum(jvd.debit_in_account_currency - jvd.credit_in_account_currency)
 		from `tabJournal Entry Account` jvd, `tabJournal Entry` jv
 		where jvd.parent = jv.name and jv.docstatus=1 and jvd.account=%(account)s
 		and jv.posting_date > %(report_date)s and jv.clearance_date <= %(report_date)s
-		and ifnull(jv.is_opening, 'No') = 'No' """, filters)
+		and ifnull(jv.is_opening, 'No') = 'No' """,
+		filters,
+	)
 
 	je_amount = flt(je_amount[0][0]) if je_amount else 0.0
 
-	pe_amount = frappe.db.sql("""
+	pe_amount = frappe.db.sql(
+		"""
 		select sum(if(paid_from=%(account)s, paid_amount, received_amount))
 		from `tabPayment Entry`
 		where (paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
-		and posting_date > %(report_date)s and clearance_date <= %(report_date)s""", filters)
+		and posting_date > %(report_date)s and clearance_date <= %(report_date)s""",
+		filters,
+	)
 
 	pe_amount = flt(pe_amount[0][0]) if pe_amount else 0.0
 
@@ -238,36 +256,38 @@
 
 	return je_amount + pe_amount + loan_amount
 
+
 def get_loan_amount(filters):
 	total_amount = 0
 	for doctype in ["Loan Disbursement", "Loan Repayment"]:
 		loan_doc = frappe.qb.DocType(doctype)
-		ifnull = CustomFunction('IFNULL', ['value', 'default'])
+		ifnull = CustomFunction("IFNULL", ["value", "default"])
 
 		if doctype == "Loan Disbursement":
 			amount_field = Sum(loan_doc.disbursed_amount)
 			posting_date = (loan_doc.disbursement_date).as_("posting_date")
 			account = loan_doc.disbursement_account
+			salary_condition = loan_doc.docstatus == 1
 		else:
 			amount_field = Sum(loan_doc.amount_paid)
 			posting_date = (loan_doc.posting_date).as_("posting_date")
 			account = loan_doc.payment_account
-
-		amount = frappe.qb.from_(loan_doc).select(
-			amount_field
-		).where(
-			loan_doc.docstatus == 1
-		).where(
-			account == filters.get('account')
-		).where(
-			posting_date > getdate(filters.get('report_date'))
-		).where(
-			ifnull(loan_doc.clearance_date, '4000-01-01') <= getdate(filters.get('report_date'))
-		).run()[0][0]
+			salary_condition = loan_doc.repay_from_salary == 0
+		amount = (
+			frappe.qb.from_(loan_doc)
+			.select(amount_field)
+			.where(loan_doc.docstatus == 1)
+			.where(salary_condition)
+			.where(account == filters.get("account"))
+			.where(posting_date > getdate(filters.get("report_date")))
+			.where(ifnull(loan_doc.clearance_date, "4000-01-01") <= getdate(filters.get("report_date")))
+			.run()[0][0]
+		)
 
 		total_amount += flt(amount)
 
-	return amount
+	return total_amount
+
 
 def get_balance_row(label, amount, account_currency):
 	if amount > 0:
@@ -275,12 +295,12 @@
 			"payment_entry": label,
 			"debit": amount,
 			"credit": 0,
-			"account_currency": account_currency
+			"account_currency": account_currency,
 		}
 	else:
 		return {
 			"payment_entry": label,
 			"debit": 0,
 			"credit": abs(amount),
-			"account_currency": account_currency
+			"account_currency": account_currency,
 		}
diff --git a/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py b/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py
index 1d7463c..62bee82 100644
--- a/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py
+++ b/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py
@@ -12,97 +12,70 @@
 
 	return columns, data
 
+
 def get_data(report_filters):
 	filters = get_report_filters(report_filters)
 	fields = get_report_fields()
 
-	return frappe.get_all('Purchase Invoice',
-		fields= fields, filters=filters)
+	return frappe.get_all("Purchase Invoice", fields=fields, filters=filters)
+
 
 def get_report_filters(report_filters):
-	filters = [['Purchase Invoice','company','=',report_filters.get('company')],
-		['Purchase Invoice','posting_date','<=',report_filters.get('posting_date')], ['Purchase Invoice','docstatus','=',1],
-		['Purchase Invoice','per_received','<',100], ['Purchase Invoice','update_stock','=',0]]
+	filters = [
+		["Purchase Invoice", "company", "=", report_filters.get("company")],
+		["Purchase Invoice", "posting_date", "<=", report_filters.get("posting_date")],
+		["Purchase Invoice", "docstatus", "=", 1],
+		["Purchase Invoice", "per_received", "<", 100],
+		["Purchase Invoice", "update_stock", "=", 0],
+	]
 
-	if report_filters.get('purchase_invoice'):
-		filters.append(['Purchase Invoice','per_received','in',[report_filters.get('purchase_invoice')]])
+	if report_filters.get("purchase_invoice"):
+		filters.append(
+			["Purchase Invoice", "per_received", "in", [report_filters.get("purchase_invoice")]]
+		)
 
 	return filters
 
+
 def get_report_fields():
 	fields = []
-	for p_field in ['name', 'supplier', 'company', 'posting_date', 'currency']:
-		fields.append('`tabPurchase Invoice`.`{}`'.format(p_field))
+	for p_field in ["name", "supplier", "company", "posting_date", "currency"]:
+		fields.append("`tabPurchase Invoice`.`{}`".format(p_field))
 
-	for c_field in ['item_code', 'item_name', 'uom', 'qty', 'received_qty', 'rate', 'amount']:
-		fields.append('`tabPurchase Invoice Item`.`{}`'.format(c_field))
+	for c_field in ["item_code", "item_name", "uom", "qty", "received_qty", "rate", "amount"]:
+		fields.append("`tabPurchase Invoice Item`.`{}`".format(c_field))
 
 	return fields
 
+
 def get_columns():
 	return [
 		{
-			'label': _('Purchase Invoice'),
-			'fieldname': 'name',
-			'fieldtype': 'Link',
-			'options': 'Purchase Invoice',
-			'width': 170
+			"label": _("Purchase Invoice"),
+			"fieldname": "name",
+			"fieldtype": "Link",
+			"options": "Purchase Invoice",
+			"width": 170,
 		},
 		{
-			'label': _('Supplier'),
-			'fieldname': 'supplier',
-			'fieldtype': 'Link',
-			'options': 'Supplier',
-			'width': 120
+			"label": _("Supplier"),
+			"fieldname": "supplier",
+			"fieldtype": "Link",
+			"options": "Supplier",
+			"width": 120,
 		},
+		{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 100},
 		{
-			'label': _('Posting Date'),
-			'fieldname': 'posting_date',
-			'fieldtype': 'Date',
-			'width': 100
+			"label": _("Item Code"),
+			"fieldname": "item_code",
+			"fieldtype": "Link",
+			"options": "Item",
+			"width": 100,
 		},
-		{
-			'label': _('Item Code'),
-			'fieldname': 'item_code',
-			'fieldtype': 'Link',
-			'options': 'Item',
-			'width': 100
-		},
-		{
-			'label': _('Item Name'),
-			'fieldname': 'item_name',
-			'fieldtype': 'Data',
-			'width': 100
-		},
-		{
-			'label': _('UOM'),
-			'fieldname': 'uom',
-			'fieldtype': 'Link',
-			'options': 'UOM',
-			'width': 100
-		},
-		{
-			'label': _('Invoiced Qty'),
-			'fieldname': 'qty',
-			'fieldtype': 'Float',
-			'width': 100
-		},
-		{
-			'label': _('Received Qty'),
-			'fieldname': 'received_qty',
-			'fieldtype': 'Float',
-			'width': 100
-		},
-		{
-			'label': _('Rate'),
-			'fieldname': 'rate',
-			'fieldtype': 'Currency',
-			'width': 100
-		},
-		{
-			'label': _('Amount'),
-			'fieldname': 'amount',
-			'fieldtype': 'Currency',
-			'width': 100
-		}
+		{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 100},
+		{"label": _("UOM"), "fieldname": "uom", "fieldtype": "Link", "options": "UOM", "width": 100},
+		{"label": _("Invoiced Qty"), "fieldname": "qty", "fieldtype": "Float", "width": 100},
+		{"label": _("Received Qty"), "fieldname": "received_qty", "fieldtype": "Float", "width": 100},
+		{"label": _("Rate"), "fieldname": "rate", "fieldtype": "Currency", "width": 100},
+		{"label": _("Amount"), "fieldname": "amount", "fieldtype": "Currency", "width": 100},
 	]
diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
index 56ee500..7b774ba 100644
--- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
+++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
@@ -34,6 +34,7 @@
 
 	return columns, data, None, chart
 
+
 def get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation):
 	for account, monthwise_data in dimension_items.items():
 		row = [dimension, account]
@@ -53,16 +54,16 @@
 				period_data[0] += last_total
 
 				if DCC_allocation:
-					period_data[0] = period_data[0]*(DCC_allocation/100)
-					period_data[1] = period_data[1]*(DCC_allocation/100)
+					period_data[0] = period_data[0] * (DCC_allocation / 100)
+					period_data[1] = period_data[1] * (DCC_allocation / 100)
 
-				if(filters.get("show_cumulative")):
+				if filters.get("show_cumulative"):
 					last_total = period_data[0] - period_data[1]
 
 				period_data[2] = period_data[0] - period_data[1]
 				row += period_data
 		totals[2] = totals[0] - totals[1]
-		if filters["period"] != "Yearly" :
+		if filters["period"] != "Yearly":
 			row += totals
 		data.append(row)
 
@@ -72,19 +73,19 @@
 def get_columns(filters):
 	columns = [
 		{
-			'label': _(filters.get("budget_against")),
-			'fieldtype': 'Link',
-			'fieldname': 'budget_against',
-			'options': filters.get('budget_against'),
-			'width': 150
+			"label": _(filters.get("budget_against")),
+			"fieldtype": "Link",
+			"fieldname": "budget_against",
+			"options": filters.get("budget_against"),
+			"width": 150,
 		},
 		{
-			'label': _('Account'),
-			'fieldname': 'Account',
-			'fieldtype': 'Link',
-			'options': 'Account',
-			'width': 150
-		}
+			"label": _("Account"),
+			"fieldname": "Account",
+			"fieldtype": "Link",
+			"options": "Account",
+			"width": 150,
+		},
 	]
 
 	group_months = False if filters["period"] == "Monthly" else True
@@ -96,46 +97,35 @@
 			if filters["period"] == "Yearly":
 				labels = [
 					_("Budget") + " " + str(year[0]),
-					_("Actual ") + " " + str(year[0]),
-					_("Variance ") + " " + str(year[0])
+					_("Actual") + " " + str(year[0]),
+					_("Variance") + " " + str(year[0]),
 				]
 				for label in labels:
-					columns.append({
-						'label': label,
-						'fieldtype': 'Float',
-						'fieldname': frappe.scrub(label),
-						'width': 150
-					})
+					columns.append(
+						{"label": label, "fieldtype": "Float", "fieldname": frappe.scrub(label), "width": 150}
+					)
 			else:
 				for label in [
 					_("Budget") + " (%s)" + " " + str(year[0]),
 					_("Actual") + " (%s)" + " " + str(year[0]),
-					_("Variance") + " (%s)" + " " + str(year[0])
+					_("Variance") + " (%s)" + " " + str(year[0]),
 				]:
 					if group_months:
 						label = label % (
-							formatdate(from_date, format_string="MMM")
-							+ "-"
-							+ formatdate(to_date, format_string="MMM")
+							formatdate(from_date, format_string="MMM") + "-" + formatdate(to_date, format_string="MMM")
 						)
 					else:
 						label = label % formatdate(from_date, format_string="MMM")
 
-					columns.append({
-						'label': label,
-						'fieldtype': 'Float',
-						'fieldname': frappe.scrub(label),
-						'width': 150
-					})
+					columns.append(
+						{"label": label, "fieldtype": "Float", "fieldname": frappe.scrub(label), "width": 150}
+					)
 
 	if filters["period"] != "Yearly":
 		for label in [_("Total Budget"), _("Total Actual"), _("Total Variance")]:
-			columns.append({
-				'label': label,
-				'fieldtype': 'Float',
-				'fieldname': frappe.scrub(label),
-				'width': 150
-			})
+			columns.append(
+				{"label": label, "fieldtype": "Float", "fieldname": frappe.scrub(label), "width": 150}
+			)
 
 		return columns
 	else:
@@ -157,8 +147,11 @@
 				where
 					company = %s
 				{order_by}
-			""".format(tab=filters.get("budget_against"), order_by=order_by),
-			filters.get("company"))
+			""".format(
+				tab=filters.get("budget_against"), order_by=order_by
+			),
+			filters.get("company"),
+		)
 	else:
 		return frappe.db.sql_list(
 			"""
@@ -166,7 +159,10 @@
 					name
 				from
 					`tab{tab}`
-			""".format(tab=filters.get("budget_against")))  # nosec
+			""".format(
+				tab=filters.get("budget_against")
+			)
+		)  # nosec
 
 
 # Get dimension & target details
@@ -174,8 +170,9 @@
 	budget_against = frappe.scrub(filters.get("budget_against"))
 	cond = ""
 	if filters.get("budget_against_filter"):
-		cond += """ and b.{budget_against} in (%s)""".format(
-			budget_against=budget_against) % ", ".join(["%s"] * len(filters.get("budget_against_filter")))
+		cond += """ and b.{budget_against} in (%s)""".format(budget_against=budget_against) % ", ".join(
+			["%s"] * len(filters.get("budget_against_filter"))
+		)
 
 	return frappe.db.sql(
 		"""
@@ -209,7 +206,9 @@
 				filters.company,
 			]
 			+ (filters.get("budget_against_filter") or [])
-		), as_dict=True)
+		),
+		as_dict=True,
+	)
 
 
 # Get target distribution details of accounts of cost center
@@ -230,13 +229,14 @@
 			order by
 				md.fiscal_year
 		""",
-		(filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1):
-		target_details.setdefault(d.name, {}).setdefault(
-			d.month, flt(d.percentage_allocation)
-		)
+		(filters.from_fiscal_year, filters.to_fiscal_year),
+		as_dict=1,
+	):
+		target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation))
 
 	return target_details
 
+
 # Get actual details from gl entry
 def get_actual_details(name, filters):
 	budget_against = frappe.scrub(filters.get("budget_against"))
@@ -247,7 +247,9 @@
 		cond = """
 				and lft >= "{lft}"
 				and rgt <= "{rgt}"
-			""".format(lft=cc_lft, rgt=cc_rgt)
+			""".format(
+			lft=cc_lft, rgt=cc_rgt
+		)
 
 	ac_details = frappe.db.sql(
 		"""
@@ -281,8 +283,12 @@
 				group by
 					gl.name
 				order by gl.fiscal_year
-		""".format(tab=filters.budget_against, budget_against=budget_against, cond=cond),
-		(filters.from_fiscal_year, filters.to_fiscal_year, name), as_dict=1)
+		""".format(
+			tab=filters.budget_against, budget_against=budget_against, cond=cond
+		),
+		(filters.from_fiscal_year, filters.to_fiscal_year, name),
+		as_dict=1,
+	)
 
 	cc_actual_details = {}
 	for d in ac_details:
@@ -290,6 +296,7 @@
 
 	return cc_actual_details
 
+
 def get_dimension_account_month_map(filters):
 	dimension_target_details = get_dimension_target_details(filters)
 	tdd = get_target_distribution_details(filters)
@@ -301,17 +308,13 @@
 
 		for month_id in range(1, 13):
 			month = datetime.date(2013, month_id, 1).strftime("%B")
-			cam_map.setdefault(ccd.budget_against, {}).setdefault(
-				ccd.account, {}
-			).setdefault(ccd.fiscal_year, {}).setdefault(
-				month, frappe._dict({"target": 0.0, "actual": 0.0})
-			)
+			cam_map.setdefault(ccd.budget_against, {}).setdefault(ccd.account, {}).setdefault(
+				ccd.fiscal_year, {}
+			).setdefault(month, frappe._dict({"target": 0.0, "actual": 0.0}))
 
 			tav_dict = cam_map[ccd.budget_against][ccd.account][ccd.fiscal_year][month]
 			month_percentage = (
-				tdd.get(ccd.monthly_distribution, {}).get(month, 0)
-				if ccd.monthly_distribution
-				else 100.0 / 12
+				tdd.get(ccd.monthly_distribution, {}).get(month, 0) if ccd.monthly_distribution else 100.0 / 12
 			)
 
 			tav_dict.target = flt(ccd.budget_amount) * month_percentage / 100
@@ -334,13 +337,12 @@
 			where
 				name between %(from_fiscal_year)s and %(to_fiscal_year)s
 		""",
-		{
-			"from_fiscal_year": filters["from_fiscal_year"],
-			"to_fiscal_year": filters["to_fiscal_year"]
-		})
+		{"from_fiscal_year": filters["from_fiscal_year"], "to_fiscal_year": filters["to_fiscal_year"]},
+	)
 
 	return fiscal_year
 
+
 def get_chart_data(filters, columns, data):
 
 	if not data:
@@ -353,12 +355,13 @@
 
 	for year in fiscal_year:
 		for from_date, to_date in get_period_date_ranges(filters["period"], year[0]):
-			if filters['period'] == 'Yearly':
+			if filters["period"] == "Yearly":
 				labels.append(year[0])
 			else:
 				if group_months:
-					label = formatdate(from_date, format_string="MMM") + "-" \
-						+ formatdate(to_date, format_string="MMM")
+					label = (
+						formatdate(from_date, format_string="MMM") + "-" + formatdate(to_date, format_string="MMM")
+					)
 					labels.append(label)
 				else:
 					label = formatdate(from_date, format_string="MMM")
@@ -373,16 +376,16 @@
 
 		for i in range(no_of_columns):
 			budget_values[i] += values[index]
-			actual_values[i] += values[index+1]
+			actual_values[i] += values[index + 1]
 			index += 3
 
 	return {
-		'data': {
-			'labels': labels,
-			'datasets': [
-				{'name': 'Budget', 'chartType': 'bar', 'values': budget_values},
-				{'name': 'Actual Expense', 'chartType': 'bar', 'values': actual_values}
-			]
+		"data": {
+			"labels": labels,
+			"datasets": [
+				{"name": "Budget", "chartType": "bar", "values": budget_values},
+				{"name": "Actual Expense", "chartType": "bar", "values": actual_values},
+			],
 		},
-		'type' : 'bar'
+		"type": "bar",
 	}
diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py
index 15041f2..75e983a 100644
--- a/erpnext/accounts/report/cash_flow/cash_flow.py
+++ b/erpnext/accounts/report/cash_flow/cash_flow.py
@@ -19,65 +19,103 @@
 
 
 def execute(filters=None):
-	if cint(frappe.db.get_single_value('Accounts Settings', 'use_custom_cash_flow')):
+	if cint(frappe.db.get_single_value("Accounts Settings", "use_custom_cash_flow")):
 		from erpnext.accounts.report.cash_flow.custom_cash_flow import execute as execute_custom
+
 		return execute_custom(filters=filters)
 
-	period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
-		filters.period_start_date, filters.period_end_date, filters.filter_based_on,
-		filters.periodicity, company=filters.company)
+	period_list = get_period_list(
+		filters.from_fiscal_year,
+		filters.to_fiscal_year,
+		filters.period_start_date,
+		filters.period_end_date,
+		filters.filter_based_on,
+		filters.periodicity,
+		company=filters.company,
+	)
 
 	cash_flow_accounts = get_cash_flow_accounts()
 
 	# compute net profit / loss
-	income = get_data(filters.company, "Income", "Credit", period_list, filters=filters,
-		accumulated_values=filters.accumulated_values, ignore_closing_entries=True, ignore_accumulated_values_for_fy= True)
-	expense = get_data(filters.company, "Expense", "Debit", period_list, filters=filters,
-		accumulated_values=filters.accumulated_values, ignore_closing_entries=True, ignore_accumulated_values_for_fy= True)
+	income = get_data(
+		filters.company,
+		"Income",
+		"Credit",
+		period_list,
+		filters=filters,
+		accumulated_values=filters.accumulated_values,
+		ignore_closing_entries=True,
+		ignore_accumulated_values_for_fy=True,
+	)
+	expense = get_data(
+		filters.company,
+		"Expense",
+		"Debit",
+		period_list,
+		filters=filters,
+		accumulated_values=filters.accumulated_values,
+		ignore_closing_entries=True,
+		ignore_accumulated_values_for_fy=True,
+	)
 
 	net_profit_loss = get_net_profit_loss(income, expense, period_list, filters.company)
 
 	data = []
 	summary_data = {}
-	company_currency = frappe.get_cached_value('Company',  filters.company,  "default_currency")
+	company_currency = frappe.get_cached_value("Company", filters.company, "default_currency")
 
 	for cash_flow_account in cash_flow_accounts:
 		section_data = []
-		data.append({
-			"account_name": cash_flow_account['section_header'],
-			"parent_account": None,
-			"indent": 0.0,
-			"account": cash_flow_account['section_header']
-		})
+		data.append(
+			{
+				"account_name": cash_flow_account["section_header"],
+				"parent_account": None,
+				"indent": 0.0,
+				"account": cash_flow_account["section_header"],
+			}
+		)
 
 		if len(data) == 1:
 			# add first net income in operations section
 			if net_profit_loss:
-				net_profit_loss.update({
-					"indent": 1,
-					"parent_account": cash_flow_accounts[0]['section_header']
-				})
+				net_profit_loss.update(
+					{"indent": 1, "parent_account": cash_flow_accounts[0]["section_header"]}
+				)
 				data.append(net_profit_loss)
 				section_data.append(net_profit_loss)
 
-		for account in cash_flow_account['account_types']:
-			account_data = get_account_type_based_data(filters.company,
-				account['account_type'], period_list, filters.accumulated_values, filters)
-			account_data.update({
-				"account_name": account['label'],
-				"account": account['label'],
-				"indent": 1,
-				"parent_account": cash_flow_account['section_header'],
-				"currency": company_currency
-			})
+		for account in cash_flow_account["account_types"]:
+			account_data = get_account_type_based_data(
+				filters.company, account["account_type"], period_list, filters.accumulated_values, filters
+			)
+			account_data.update(
+				{
+					"account_name": account["label"],
+					"account": account["label"],
+					"indent": 1,
+					"parent_account": cash_flow_account["section_header"],
+					"currency": company_currency,
+				}
+			)
 			data.append(account_data)
 			section_data.append(account_data)
 
-		add_total_row_account(data, section_data, cash_flow_account['section_footer'],
-			period_list, company_currency, summary_data, filters)
+		add_total_row_account(
+			data,
+			section_data,
+			cash_flow_account["section_footer"],
+			period_list,
+			company_currency,
+			summary_data,
+			filters,
+		)
 
-	add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency, summary_data, filters)
-	columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company)
+	add_total_row_account(
+		data, data, _("Net Change in Cash"), period_list, company_currency, summary_data, filters
+	)
+	columns = get_columns(
+		filters.periodicity, period_list, filters.accumulated_values, filters.company
+	)
 
 	chart = get_chart_data(columns, data)
 
@@ -85,6 +123,7 @@
 
 	return columns, data, None, chart, report_summary
 
+
 def get_cash_flow_accounts():
 	operation_accounts = {
 		"section_name": "Operations",
@@ -94,39 +133,37 @@
 			{"account_type": "Depreciation", "label": _("Depreciation")},
 			{"account_type": "Receivable", "label": _("Net Change in Accounts Receivable")},
 			{"account_type": "Payable", "label": _("Net Change in Accounts Payable")},
-			{"account_type": "Stock", "label": _("Net Change in Inventory")}
-		]
+			{"account_type": "Stock", "label": _("Net Change in Inventory")},
+		],
 	}
 
 	investing_accounts = {
 		"section_name": "Investing",
 		"section_footer": _("Net Cash from Investing"),
 		"section_header": _("Cash Flow from Investing"),
-		"account_types": [
-			{"account_type": "Fixed Asset", "label": _("Net Change in Fixed Asset")}
-		]
+		"account_types": [{"account_type": "Fixed Asset", "label": _("Net Change in Fixed Asset")}],
 	}
 
 	financing_accounts = {
 		"section_name": "Financing",
 		"section_footer": _("Net Cash from Financing"),
 		"section_header": _("Cash Flow from Financing"),
-		"account_types": [
-			{"account_type": "Equity", "label": _("Net Change in Equity")}
-		]
+		"account_types": [{"account_type": "Equity", "label": _("Net Change in Equity")}],
 	}
 
 	# combine all cash flow accounts for iteration
 	return [operation_accounts, investing_accounts, financing_accounts]
 
+
 def get_account_type_based_data(company, account_type, period_list, accumulated_values, filters):
 	data = {}
 	total = 0
 	for period in period_list:
 		start_date = get_start_date(period, accumulated_values, company)
 
-		amount = get_account_type_based_gl_data(company, start_date,
-			period['to_date'], account_type, filters)
+		amount = get_account_type_based_gl_data(
+			company, start_date, period["to_date"], account_type, filters
+		)
 
 		if amount and account_type == "Depreciation":
 			amount *= -1
@@ -137,31 +174,42 @@
 	data["total"] = total
 	return data
 
+
 def get_account_type_based_gl_data(company, start_date, end_date, account_type, filters=None):
 	cond = ""
 	filters = frappe._dict(filters or {})
 
 	if filters.include_default_book_entries:
-		company_fb = frappe.db.get_value("Company", company, 'default_finance_book')
+		company_fb = frappe.db.get_value("Company", company, "default_finance_book")
 		cond = """ AND (finance_book in (%s, %s, '') OR finance_book IS NULL)
-			""" %(frappe.db.escape(filters.finance_book), frappe.db.escape(company_fb))
+			""" % (
+			frappe.db.escape(filters.finance_book),
+			frappe.db.escape(company_fb),
+		)
 	else:
-		cond = " AND (finance_book in (%s, '') OR finance_book IS NULL)" %(frappe.db.escape(cstr(filters.finance_book)))
+		cond = " AND (finance_book in (%s, '') OR finance_book IS NULL)" % (
+			frappe.db.escape(cstr(filters.finance_book))
+		)
 
-
-	gl_sum = frappe.db.sql_list("""
+	gl_sum = frappe.db.sql_list(
+		"""
 		select sum(credit) - sum(debit)
 		from `tabGL Entry`
 		where company=%s and posting_date >= %s and posting_date <= %s
 			and voucher_type != 'Period Closing Voucher'
 			and account in ( SELECT name FROM tabAccount WHERE account_type = %s) {cond}
-	""".format(cond=cond), (company, start_date, end_date, account_type))
+	""".format(
+			cond=cond
+		),
+		(company, start_date, end_date, account_type),
+	)
 
 	return gl_sum[0] if gl_sum and gl_sum[0] else 0
 
+
 def get_start_date(period, accumulated_values, company):
-	if not accumulated_values and period.get('from_date'):
-		return period['from_date']
+	if not accumulated_values and period.get("from_date"):
+		return period["from_date"]
 
 	start_date = period["year_start_date"]
 	if accumulated_values:
@@ -169,23 +217,26 @@
 
 	return start_date
 
-def add_total_row_account(out, data, label, period_list, currency, summary_data, filters, consolidated=False):
+
+def add_total_row_account(
+	out, data, label, period_list, currency, summary_data, filters, consolidated=False
+):
 	total_row = {
 		"account_name": "'" + _("{0}").format(label) + "'",
 		"account": "'" + _("{0}").format(label) + "'",
-		"currency": currency
+		"currency": currency,
 	}
 
 	summary_data[label] = 0
 
 	# from consolidated financial statement
-	if filters.get('accumulated_in_group_company'):
+	if filters.get("accumulated_in_group_company"):
 		period_list = get_filtered_list_for_consolidated_report(filters, period_list)
 
 	for row in data:
 		if row.get("parent_account"):
 			for period in period_list:
-				key = period if consolidated else period['key']
+				key = period if consolidated else period["key"]
 				total_row.setdefault(key, 0.0)
 				total_row[key] += row.get(key, 0.0)
 				summary_data[label] += row.get(key)
@@ -202,12 +253,7 @@
 
 	for label, value in summary_data.items():
 		report_summary.append(
-			{
-				"value": value,
-				"label": label,
-				"datatype": "Currency",
-				"currency": currency
-			}
+			{"value": value, "label": label, "datatype": "Currency", "currency": currency}
 		)
 
 	return report_summary
@@ -215,16 +261,17 @@
 
 def get_chart_data(columns, data):
 	labels = [d.get("label") for d in columns[2:]]
-	datasets = [{'name':account.get('account').replace("'", ""), 'values': [account.get('total')]}  for account in data if account.get('parent_account') == None and account.get('currency')]
+	datasets = [
+		{
+			"name": account.get("account").replace("'", ""),
+			"values": [account.get(d.get("fieldname")) for d in columns[2:]],
+		}
+		for account in data
+		if account.get("parent_account") == None and account.get("currency")
+	]
 	datasets = datasets[:-1]
 
-	chart = {
-		"data": {
-			'labels': labels,
-			'datasets': datasets
-		},
-		"type": "bar"
-	}
+	chart = {"data": {"labels": labels, "datasets": datasets}, "type": "bar"}
 
 	chart["fieldtype"] = "Currency"
 
diff --git a/erpnext/accounts/report/cash_flow/custom_cash_flow.py b/erpnext/accounts/report/cash_flow/custom_cash_flow.py
index 45d147e..b165c88 100644
--- a/erpnext/accounts/report/cash_flow/custom_cash_flow.py
+++ b/erpnext/accounts/report/cash_flow/custom_cash_flow.py
@@ -4,7 +4,8 @@
 
 import frappe
 from frappe import _
-from frappe.utils import add_to_date
+from frappe.query_builder.functions import Sum
+from frappe.utils import add_to_date, flt, get_date_str
 
 from erpnext.accounts.report.financial_statements import get_columns, get_data, get_period_list
 from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import (
@@ -13,41 +14,59 @@
 
 
 def get_mapper_for(mappers, position):
-	mapper_list = list(filter(lambda x: x['position'] == position, mappers))
+	mapper_list = list(filter(lambda x: x["position"] == position, mappers))
 	return mapper_list[0] if mapper_list else []
 
 
 def get_mappers_from_db():
 	return frappe.get_all(
-		'Cash Flow Mapper',
+		"Cash Flow Mapper",
 		fields=[
-			'section_name', 'section_header', 'section_leader', 'section_subtotal',
-			'section_footer', 'name', 'position'],
-		order_by='position'
+			"section_name",
+			"section_header",
+			"section_leader",
+			"section_subtotal",
+			"section_footer",
+			"name",
+			"position",
+		],
+		order_by="position",
 	)
 
 
 def get_accounts_in_mappers(mapping_names):
-	return frappe.db.sql('''
-		select cfma.name, cfm.label, cfm.is_working_capital, cfm.is_income_tax_liability,
-		cfm.is_income_tax_expense, cfm.is_finance_cost, cfm.is_finance_cost_adjustment
-		from `tabCash Flow Mapping Accounts` cfma
-		join `tabCash Flow Mapping` cfm on cfma.parent=cfm.name
-		where cfma.parent in (%s)
-		order by cfm.is_working_capital
-	''', (', '.join('"%s"' % d for d in mapping_names)))
+	cfm = frappe.qb.DocType("Cash Flow Mapping")
+	cfma = frappe.qb.DocType("Cash Flow Mapping Accounts")
+	result = (
+		frappe.qb.select(
+			cfma.name,
+			cfm.label,
+			cfm.is_working_capital,
+			cfm.is_income_tax_liability,
+			cfm.is_income_tax_expense,
+			cfm.is_finance_cost,
+			cfm.is_finance_cost_adjustment,
+			cfma.account,
+		)
+		.from_(cfm)
+		.join(cfma)
+		.on(cfm.name == cfma.parent)
+		.where(cfma.parent.isin(mapping_names))
+	).run()
+
+	return result
 
 
 def setup_mappers(mappers):
 	cash_flow_accounts = []
 
 	for mapping in mappers:
-		mapping['account_types'] = []
-		mapping['tax_liabilities'] = []
-		mapping['tax_expenses'] = []
-		mapping['finance_costs'] = []
-		mapping['finance_costs_adjustments'] = []
-		doc = frappe.get_doc('Cash Flow Mapper', mapping['name'])
+		mapping["account_types"] = []
+		mapping["tax_liabilities"] = []
+		mapping["tax_expenses"] = []
+		mapping["finance_costs"] = []
+		mapping["finance_costs_adjustments"] = []
+		doc = frappe.get_doc("Cash Flow Mapper", mapping["name"])
 		mapping_names = [item.name for item in doc.accounts]
 
 		if not mapping_names:
@@ -57,96 +76,123 @@
 
 		account_types = [
 			dict(
-				name=account[0], label=account[1], is_working_capital=account[2],
-				is_income_tax_liability=account[3], is_income_tax_expense=account[4]
-			) for account in accounts if not account[3]]
+				name=account[0],
+				account_name=account[7],
+				label=account[1],
+				is_working_capital=account[2],
+				is_income_tax_liability=account[3],
+				is_income_tax_expense=account[4],
+			)
+			for account in accounts
+			if not account[3]
+		]
 
 		finance_costs_adjustments = [
 			dict(
-				name=account[0], label=account[1], is_finance_cost=account[5],
-				is_finance_cost_adjustment=account[6]
-			) for account in accounts if account[6]]
+				name=account[0],
+				account_name=account[7],
+				label=account[1],
+				is_finance_cost=account[5],
+				is_finance_cost_adjustment=account[6],
+			)
+			for account in accounts
+			if account[6]
+		]
 
 		tax_liabilities = [
 			dict(
-				name=account[0], label=account[1], is_income_tax_liability=account[3],
-				is_income_tax_expense=account[4]
-			) for account in accounts if account[3]]
+				name=account[0],
+				account_name=account[7],
+				label=account[1],
+				is_income_tax_liability=account[3],
+				is_income_tax_expense=account[4],
+			)
+			for account in accounts
+			if account[3]
+		]
 
 		tax_expenses = [
 			dict(
-				name=account[0], label=account[1], is_income_tax_liability=account[3],
-				is_income_tax_expense=account[4]
-			) for account in accounts if account[4]]
+				name=account[0],
+				account_name=account[7],
+				label=account[1],
+				is_income_tax_liability=account[3],
+				is_income_tax_expense=account[4],
+			)
+			for account in accounts
+			if account[4]
+		]
 
 		finance_costs = [
-			dict(
-				name=account[0], label=account[1], is_finance_cost=account[5])
-			for account in accounts if account[5]]
+			dict(name=account[0], account_name=account[7], label=account[1], is_finance_cost=account[5])
+			for account in accounts
+			if account[5]
+		]
 
 		account_types_labels = sorted(
 			set(
-				(d['label'], d['is_working_capital'], d['is_income_tax_liability'], d['is_income_tax_expense'])
-					for d in account_types
+				(d["label"], d["is_working_capital"], d["is_income_tax_liability"], d["is_income_tax_expense"])
+				for d in account_types
 			),
-			key=lambda x: x[1]
+			key=lambda x: x[1],
 		)
 
 		fc_adjustment_labels = sorted(
 			set(
-				[(d['label'], d['is_finance_cost'], d['is_finance_cost_adjustment'])
-					for d in finance_costs_adjustments if d['is_finance_cost_adjustment']]
+				[
+					(d["label"], d["is_finance_cost"], d["is_finance_cost_adjustment"])
+					for d in finance_costs_adjustments
+					if d["is_finance_cost_adjustment"]
+				]
 			),
-			key=lambda x: x[2]
+			key=lambda x: x[2],
 		)
 
 		unique_liability_labels = sorted(
 			set(
-				[(d['label'], d['is_income_tax_liability'], d['is_income_tax_expense'])
-					for d in tax_liabilities]
+				[
+					(d["label"], d["is_income_tax_liability"], d["is_income_tax_expense"])
+					for d in tax_liabilities
+				]
 			),
-			key=lambda x: x[0]
+			key=lambda x: x[0],
 		)
 
 		unique_expense_labels = sorted(
 			set(
-				[(d['label'], d['is_income_tax_liability'], d['is_income_tax_expense'])
-					for d in tax_expenses]
+				[(d["label"], d["is_income_tax_liability"], d["is_income_tax_expense"]) for d in tax_expenses]
 			),
-			key=lambda x: x[0]
+			key=lambda x: x[0],
 		)
 
 		unique_finance_costs_labels = sorted(
-			set(
-				[(d['label'], d['is_finance_cost']) for d in finance_costs]
-			),
-			key=lambda x: x[0]
+			set([(d["label"], d["is_finance_cost"]) for d in finance_costs]), key=lambda x: x[0]
 		)
 
 		for label in account_types_labels:
-			names = [d['name'] for d in account_types if d['label'] == label[0]]
+			names = [d["account_name"] for d in account_types if d["label"] == label[0]]
 			m = dict(label=label[0], names=names, is_working_capital=label[1])
-			mapping['account_types'].append(m)
+			mapping["account_types"].append(m)
 
 		for label in fc_adjustment_labels:
-			names = [d['name'] for d in finance_costs_adjustments if d['label'] == label[0]]
+			names = [d["account_name"] for d in finance_costs_adjustments if d["label"] == label[0]]
 			m = dict(label=label[0], names=names)
-			mapping['finance_costs_adjustments'].append(m)
+			mapping["finance_costs_adjustments"].append(m)
 
 		for label in unique_liability_labels:
-			names = [d['name'] for d in tax_liabilities if d['label'] == label[0]]
+			names = [d["account_name"] for d in tax_liabilities if d["label"] == label[0]]
 			m = dict(label=label[0], names=names, tax_liability=label[1], tax_expense=label[2])
-			mapping['tax_liabilities'].append(m)
+			mapping["tax_liabilities"].append(m)
 
 		for label in unique_expense_labels:
-			names = [d['name'] for d in tax_expenses if d['label'] == label[0]]
+			names = [d["account_name"] for d in tax_expenses if d["label"] == label[0]]
 			m = dict(label=label[0], names=names, tax_liability=label[1], tax_expense=label[2])
-			mapping['tax_expenses'].append(m)
+			mapping["tax_expenses"].append(m)
 
 		for label in unique_finance_costs_labels:
-			names = [d['name'] for d in finance_costs if d['label'] == label[0]]
+			names = [d["account_name"] for d in finance_costs if d["label"] == label[0]]
 			m = dict(label=label[0], names=names, is_finance_cost=label[1])
-			mapping['finance_costs'].append(m)
+			mapping["finance_costs"].append(m)
 
 		cash_flow_accounts.append(mapping)
 
@@ -154,119 +200,145 @@
 
 
 def add_data_for_operating_activities(
-	filters, company_currency, profit_data, period_list, light_mappers, mapper, data):
+	filters, company_currency, profit_data, period_list, light_mappers, mapper, data
+):
 	has_added_working_capital_header = False
 	section_data = []
 
-	data.append({
-		"account_name": mapper['section_header'],
-		"parent_account": None,
-		"indent": 0.0,
-		"account": mapper['section_header']
-	})
+	data.append(
+		{
+			"account_name": mapper["section_header"],
+			"parent_account": None,
+			"indent": 0.0,
+			"account": mapper["section_header"],
+		}
+	)
 
 	if profit_data:
-		profit_data.update({
-			"indent": 1,
-			"parent_account": get_mapper_for(light_mappers, position=1)['section_header']
-		})
+		profit_data.update(
+			{"indent": 1, "parent_account": get_mapper_for(light_mappers, position=1)["section_header"]}
+		)
 		data.append(profit_data)
 		section_data.append(profit_data)
 
-		data.append({
-			"account_name": mapper["section_leader"],
-			"parent_account": None,
-			"indent": 1.0,
-			"account": mapper["section_leader"]
-		})
-
-	for account in mapper['account_types']:
-		if account['is_working_capital'] and not has_added_working_capital_header:
-			data.append({
-				"account_name": 'Movement in working capital',
+		data.append(
+			{
+				"account_name": mapper["section_leader"],
 				"parent_account": None,
 				"indent": 1.0,
-				"account": ""
-			})
+				"account": mapper["section_leader"],
+			}
+		)
+
+	for account in mapper["account_types"]:
+		if account["is_working_capital"] and not has_added_working_capital_header:
+			data.append(
+				{
+					"account_name": "Movement in working capital",
+					"parent_account": None,
+					"indent": 1.0,
+					"account": "",
+				}
+			)
 			has_added_working_capital_header = True
 
 		account_data = _get_account_type_based_data(
-			filters, account['names'], period_list, filters.accumulated_values)
+			filters, account["names"], period_list, filters.accumulated_values
+		)
 
-		if not account['is_working_capital']:
+		if not account["is_working_capital"]:
 			for key in account_data:
-				if key != 'total':
+				if key != "total":
 					account_data[key] *= -1
 
-		if account_data['total'] != 0:
-			account_data.update({
-				"account_name": account['label'],
-				"account": account['names'],
-				"indent": 1.0,
-				"parent_account": mapper['section_header'],
-				"currency": company_currency
-			})
+		if account_data["total"] != 0:
+			account_data.update(
+				{
+					"account_name": account["label"],
+					"account": account["names"],
+					"indent": 1.0,
+					"parent_account": mapper["section_header"],
+					"currency": company_currency,
+				}
+			)
 			data.append(account_data)
 			section_data.append(account_data)
 
 	_add_total_row_account(
-		data, section_data, mapper['section_subtotal'], period_list, company_currency, indent=1)
+		data, section_data, mapper["section_subtotal"], period_list, company_currency, indent=1
+	)
 
 	# calculate adjustment for tax paid and add to data
-	if not mapper['tax_liabilities']:
-		mapper['tax_liabilities'] = [
-			dict(label='Income tax paid', names=[''], tax_liability=1, tax_expense=0)]
+	if not mapper["tax_liabilities"]:
+		mapper["tax_liabilities"] = [
+			dict(label="Income tax paid", names=[""], tax_liability=1, tax_expense=0)
+		]
 
-	for account in mapper['tax_liabilities']:
+	for account in mapper["tax_liabilities"]:
 		tax_paid = calculate_adjustment(
-			filters, mapper['tax_liabilities'], mapper['tax_expenses'],
-			filters.accumulated_values, period_list)
+			filters,
+			mapper["tax_liabilities"],
+			mapper["tax_expenses"],
+			filters.accumulated_values,
+			period_list,
+		)
 
 		if tax_paid:
-			tax_paid.update({
-				'parent_account': mapper['section_header'],
-				'currency': company_currency,
-				'account_name': account['label'],
-				'indent': 1.0
-			})
+			tax_paid.update(
+				{
+					"parent_account": mapper["section_header"],
+					"currency": company_currency,
+					"account_name": account["label"],
+					"indent": 1.0,
+				}
+			)
 			data.append(tax_paid)
 			section_data.append(tax_paid)
 
-	if not mapper['finance_costs_adjustments']:
-		mapper['finance_costs_adjustments'] = [dict(label='Interest Paid', names=[''])]
+	if not mapper["finance_costs_adjustments"]:
+		mapper["finance_costs_adjustments"] = [dict(label="Interest Paid", names=[""])]
 
-	for account in mapper['finance_costs_adjustments']:
+	for account in mapper["finance_costs_adjustments"]:
 		interest_paid = calculate_adjustment(
-			filters, mapper['finance_costs_adjustments'], mapper['finance_costs'],
-			filters.accumulated_values, period_list
+			filters,
+			mapper["finance_costs_adjustments"],
+			mapper["finance_costs"],
+			filters.accumulated_values,
+			period_list,
 		)
 
 		if interest_paid:
-			interest_paid.update({
-				'parent_account': mapper['section_header'],
-				'currency': company_currency,
-				'account_name': account['label'],
-				'indent': 1.0
-			})
+			interest_paid.update(
+				{
+					"parent_account": mapper["section_header"],
+					"currency": company_currency,
+					"account_name": account["label"],
+					"indent": 1.0,
+				}
+			)
 			data.append(interest_paid)
 			section_data.append(interest_paid)
 
 	_add_total_row_account(
-		data, section_data, mapper['section_footer'], period_list, company_currency)
+		data, section_data, mapper["section_footer"], period_list, company_currency
+	)
 
 
-def calculate_adjustment(filters, non_expense_mapper, expense_mapper, use_accumulated_values, period_list):
-	liability_accounts = [d['names'] for d in non_expense_mapper]
-	expense_accounts = [d['names'] for d in expense_mapper]
+def calculate_adjustment(
+	filters, non_expense_mapper, expense_mapper, use_accumulated_values, period_list
+):
+	liability_accounts = [d["names"] for d in non_expense_mapper]
+	expense_accounts = [d["names"] for d in expense_mapper]
 
-	non_expense_closing = _get_account_type_based_data(
-		filters, liability_accounts, period_list, 0)
+	non_expense_closing = _get_account_type_based_data(filters, liability_accounts, period_list, 0)
 
 	non_expense_opening = _get_account_type_based_data(
-		filters, liability_accounts, period_list, use_accumulated_values, opening_balances=1)
+		filters, liability_accounts, period_list, use_accumulated_values, opening_balances=1
+	)
 
 	expense_data = _get_account_type_based_data(
-		filters, expense_accounts, period_list, use_accumulated_values)
+		filters, expense_accounts, period_list, use_accumulated_values
+	)
 
 	data = _calculate_adjustment(non_expense_closing, non_expense_opening, expense_data)
 	return data
@@ -276,7 +348,9 @@
 	account_data = {}
 	for month in non_expense_opening.keys():
 		if non_expense_opening[month] and non_expense_closing[month]:
-			account_data[month] = non_expense_opening[month] - expense_data[month] + non_expense_closing[month]
+			account_data[month] = (
+				non_expense_opening[month] - expense_data[month] + non_expense_closing[month]
+			)
 		elif expense_data[month]:
 			account_data[month] = expense_data[month]
 
@@ -284,32 +358,39 @@
 
 
 def add_data_for_other_activities(
-	filters, company_currency, profit_data, period_list, light_mappers, mapper_list, data):
+	filters, company_currency, profit_data, period_list, light_mappers, mapper_list, data
+):
 	for mapper in mapper_list:
 		section_data = []
-		data.append({
-			"account_name": mapper['section_header'],
-			"parent_account": None,
-			"indent": 0.0,
-			"account": mapper['section_header']
-		})
+		data.append(
+			{
+				"account_name": mapper["section_header"],
+				"parent_account": None,
+				"indent": 0.0,
+				"account": mapper["section_header"],
+			}
+		)
 
-		for account in mapper['account_types']:
-			account_data = _get_account_type_based_data(filters,
-				account['names'], period_list, filters.accumulated_values)
-			if account_data['total'] != 0:
-				account_data.update({
-					"account_name": account['label'],
-					"account": account['names'],
-					"indent": 1,
-					"parent_account": mapper['section_header'],
-					"currency": company_currency
-				})
+		for account in mapper["account_types"]:
+			account_data = _get_account_type_based_data(
+				filters, account["names"], period_list, filters.accumulated_values
+			)
+			if account_data["total"] != 0:
+				account_data.update(
+					{
+						"account_name": account["label"],
+						"account": account["names"],
+						"indent": 1,
+						"parent_account": mapper["section_header"],
+						"currency": company_currency,
+					}
+				)
 				data.append(account_data)
 				section_data.append(account_data)
 
-		_add_total_row_account(data, section_data, mapper['section_footer'],
-			period_list, company_currency)
+		_add_total_row_account(
+			data, section_data, mapper["section_footer"], period_list, company_currency
+		)
 
 
 def compute_data(filters, company_currency, profit_data, period_list, light_mappers, full_mapper):
@@ -318,13 +399,18 @@
 	operating_activities_mapper = get_mapper_for(light_mappers, position=1)
 	other_mappers = [
 		get_mapper_for(light_mappers, position=2),
-		get_mapper_for(light_mappers, position=3)
+		get_mapper_for(light_mappers, position=3),
 	]
 
 	if operating_activities_mapper:
 		add_data_for_operating_activities(
-			filters, company_currency, profit_data, period_list, light_mappers,
-			operating_activities_mapper, data
+			filters,
+			company_currency,
+			profit_data,
+			period_list,
+			light_mappers,
+			operating_activities_mapper,
+			data,
 		)
 
 	if all(other_mappers):
@@ -336,10 +422,17 @@
 
 
 def execute(filters=None):
-	if not filters.periodicity: filters.periodicity = "Monthly"
-	period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
-		filters.period_start_date, filters.period_end_date, filters.filter_based_on,
-		filters.periodicity, company=filters.company)
+	if not filters.periodicity:
+		filters.periodicity = "Monthly"
+	period_list = get_period_list(
+		filters.from_fiscal_year,
+		filters.to_fiscal_year,
+		filters.period_start_date,
+		filters.period_end_date,
+		filters.filter_based_on,
+		filters.periodicity,
+		company=filters.company,
+	)
 
 	mappers = get_mappers_from_db()
 
@@ -347,43 +440,72 @@
 
 	# compute net profit / loss
 	income = get_data(
-		filters.company, "Income", "Credit", period_list, filters=filters,
-		accumulated_values=filters.accumulated_values, ignore_closing_entries=True,
-		ignore_accumulated_values_for_fy=True
+		filters.company,
+		"Income",
+		"Credit",
+		period_list,
+		filters=filters,
+		accumulated_values=filters.accumulated_values,
+		ignore_closing_entries=True,
+		ignore_accumulated_values_for_fy=True,
 	)
 
 	expense = get_data(
-		filters.company, "Expense", "Debit", period_list, filters=filters,
-		accumulated_values=filters.accumulated_values, ignore_closing_entries=True,
-		ignore_accumulated_values_for_fy=True
+		filters.company,
+		"Expense",
+		"Debit",
+		period_list,
+		filters=filters,
+		accumulated_values=filters.accumulated_values,
+		ignore_closing_entries=True,
+		ignore_accumulated_values_for_fy=True,
 	)
 
 	net_profit_loss = get_net_profit_loss(income, expense, period_list, filters.company)
 
-	company_currency = frappe.get_cached_value('Company',  filters.company,  "default_currency")
+	company_currency = frappe.get_cached_value("Company", filters.company, "default_currency")
 
-	data = compute_data(filters, company_currency, net_profit_loss, period_list, mappers, cash_flow_accounts)
+	data = compute_data(
+		filters, company_currency, net_profit_loss, period_list, mappers, cash_flow_accounts
+	)
 
 	_add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency)
-	columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company)
+	columns = get_columns(
+		filters.periodicity, period_list, filters.accumulated_values, filters.company
+	)
 
 	return columns, data
 
 
-def _get_account_type_based_data(filters, account_names, period_list, accumulated_values, opening_balances=0):
+def _get_account_type_based_data(
+	filters, account_names, period_list, accumulated_values, opening_balances=0
+):
+	if not account_names or not account_names[0] or not type(account_names[0]) == str:
+		# only proceed if account_names is a list of account names
+		return {}
+
 	from erpnext.accounts.report.cash_flow.cash_flow import get_start_date
 
 	company = filters.company
 	data = {}
 	total = 0
+	GLEntry = frappe.qb.DocType("GL Entry")
+	Account = frappe.qb.DocType("Account")
+
 	for period in period_list:
 		start_date = get_start_date(period, accumulated_values, company)
-		accounts = ', '.join('"%s"' % d for d in account_names)
+
+		account_subquery = (
+			frappe.qb.from_(Account)
+			.where((Account.name.isin(account_names)) | (Account.parent_account.isin(account_names)))
+			.select(Account.name)
+			.as_("account_subquery")
+		)
 
 		if opening_balances:
 			date_info = dict(date=start_date)
-			months_map = {'Monthly': -1, 'Quarterly': -3, 'Half-Yearly': -6}
-			years_map = {'Yearly': -1}
+			months_map = {"Monthly": -1, "Quarterly": -3, "Half-Yearly": -6}
+			years_map = {"Yearly": -1}
 
 			if months_map.get(filters.periodicity):
 				date_info.update(months=months_map[filters.periodicity])
@@ -391,36 +513,35 @@
 				date_info.update(years=years_map[filters.periodicity])
 
 			if accumulated_values:
-				start, end = add_to_date(start_date, years=-1), add_to_date(period['to_date'], years=-1)
+				start, end = add_to_date(start_date, years=-1), add_to_date(period["to_date"], years=-1)
 			else:
 				start, end = add_to_date(**date_info), add_to_date(**date_info)
 
-			gl_sum = frappe.db.sql_list("""
-				select sum(credit) - sum(debit)
-				from `tabGL Entry`
-				where company=%s and posting_date >= %s and posting_date <= %s
-					and voucher_type != 'Period Closing Voucher'
-					and account in ( SELECT name FROM tabAccount WHERE name IN (%s)
-					OR parent_account IN (%s))
-			""", (company, start, end, accounts, accounts))
-		else:
-			gl_sum = frappe.db.sql_list("""
-				select sum(credit) - sum(debit)
-				from `tabGL Entry`
-				where company=%s and posting_date >= %s and posting_date <= %s
-					and voucher_type != 'Period Closing Voucher'
-					and account in ( SELECT name FROM tabAccount WHERE name IN (%s)
-					OR parent_account IN (%s))
-			""", (company, start_date if accumulated_values else period['from_date'],
-				period['to_date'], accounts, accounts))
+			start, end = get_date_str(start), get_date_str(end)
 
-		if gl_sum and gl_sum[0]:
-			amount = gl_sum[0]
 		else:
-			amount = 0
+			start, end = start_date if accumulated_values else period["from_date"], period["to_date"]
+			start, end = get_date_str(start), get_date_str(end)
 
-		total += amount
-		data.setdefault(period["key"], amount)
+		result = (
+			frappe.qb.from_(GLEntry)
+			.select(Sum(GLEntry.credit) - Sum(GLEntry.debit))
+			.where(
+				(GLEntry.company == company)
+				& (GLEntry.posting_date >= start)
+				& (GLEntry.posting_date <= end)
+				& (GLEntry.voucher_type != "Period Closing Voucher")
+				& (GLEntry.account.isin(account_subquery))
+			)
+		).run()
+
+		if result and result[0]:
+			gl_sum = result[0][0]
+		else:
+			gl_sum = 0
+
+		total += flt(gl_sum)
+		data.setdefault(period["key"], flt(gl_sum))
 
 	data["total"] = total
 	return data
@@ -431,7 +552,7 @@
 		"indent": indent,
 		"account_name": "'" + _("{0}").format(label) + "'",
 		"account": "'" + _("{0}").format(label) + "'",
-		"currency": currency
+		"currency": currency,
 	}
 	for row in data:
 		if row.get("parent_account"):
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
index d3e836a..dd965a9 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
@@ -50,7 +50,15 @@
 				"fieldtype": "Link",
 				"options": "Fiscal Year",
 				"default": frappe.defaults.get_user_default("fiscal_year"),
-				"reqd": 1
+				"reqd": 1,
+				on_change: () => {
+					frappe.model.with_doc("Fiscal Year", frappe.query_report.get_filter_value('from_fiscal_year'), function(r) {
+						let year_start_date = frappe.model.get_value("Fiscal Year", frappe.query_report.get_filter_value('from_fiscal_year'), "year_start_date");
+						frappe.query_report.set_filter_value({
+							period_start_date: year_start_date
+						});
+					});
+				}
 			},
 			{
 				"fieldname":"to_fiscal_year",
@@ -58,7 +66,15 @@
 				"fieldtype": "Link",
 				"options": "Fiscal Year",
 				"default": frappe.defaults.get_user_default("fiscal_year"),
-				"reqd": 1
+				"reqd": 1,
+				on_change: () => {
+					frappe.model.with_doc("Fiscal Year", frappe.query_report.get_filter_value('to_fiscal_year'), function(r) {
+						let year_end_date = frappe.model.get_value("Fiscal Year", frappe.query_report.get_filter_value('to_fiscal_year'), "year_end_date");
+						frappe.query_report.set_filter_value({
+							period_end_date: year_end_date
+						});
+					});
+				}
 			},
 			{
 				"fieldname":"finance_book",
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 1e20f7b..98dbbf6 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -42,26 +42,32 @@
 def execute(filters=None):
 	columns, data, message, chart = [], [], [], []
 
-	if not filters.get('company'):
+	if not filters.get("company"):
 		return columns, data, message, chart
 
-	fiscal_year = get_fiscal_year_data(filters.get('from_fiscal_year'), filters.get('to_fiscal_year'))
+	fiscal_year = get_fiscal_year_data(filters.get("from_fiscal_year"), filters.get("to_fiscal_year"))
 	companies_column, companies = get_companies(filters)
 	columns = get_columns(companies_column, filters)
 
-	if filters.get('report') == "Balance Sheet":
-		data, message, chart, report_summary = get_balance_sheet_data(fiscal_year, companies, columns, filters)
-	elif filters.get('report') == "Profit and Loss Statement":
-		data, message, chart, report_summary = get_profit_loss_data(fiscal_year, companies, columns, filters)
+	if filters.get("report") == "Balance Sheet":
+		data, message, chart, report_summary = get_balance_sheet_data(
+			fiscal_year, companies, columns, filters
+		)
+	elif filters.get("report") == "Profit and Loss Statement":
+		data, message, chart, report_summary = get_profit_loss_data(
+			fiscal_year, companies, columns, filters
+		)
 	else:
-		if cint(frappe.db.get_single_value('Accounts Settings', 'use_custom_cash_flow')):
+		if cint(frappe.db.get_single_value("Accounts Settings", "use_custom_cash_flow")):
 			from erpnext.accounts.report.cash_flow.custom_cash_flow import execute as execute_custom
+
 			return execute_custom(filters=filters)
 
 		data, report_summary = get_cash_flow_data(fiscal_year, companies, filters)
 
 	return columns, data, message, chart, report_summary
 
+
 def get_balance_sheet_data(fiscal_year, companies, columns, filters):
 	asset = get_data(companies, "Asset", "Debit", fiscal_year, filters=filters)
 
@@ -75,24 +81,27 @@
 	data.extend(equity or [])
 
 	company_currency = get_company_currency(filters)
-	provisional_profit_loss, total_credit = get_provisional_profit_loss(asset, liability, equity,
-		companies, filters.get('company'), company_currency, True)
+	provisional_profit_loss, total_credit = get_provisional_profit_loss(
+		asset, liability, equity, companies, filters.get("company"), company_currency, True
+	)
 
-	message, opening_balance = prepare_companywise_opening_balance(asset, liability, equity, companies)
+	message, opening_balance = prepare_companywise_opening_balance(
+		asset, liability, equity, companies
+	)
 
 	if opening_balance:
 		unclosed = {
 			"account_name": "'" + _("Unclosed Fiscal Years Profit / Loss (Credit)") + "'",
 			"account": "'" + _("Unclosed Fiscal Years Profit / Loss (Credit)") + "'",
 			"warn_if_negative": True,
-			"currency": company_currency
+			"currency": company_currency,
 		}
 
 		for company in companies:
 			unclosed[company] = opening_balance.get(company)
 			if provisional_profit_loss and provisional_profit_loss.get(company):
-				provisional_profit_loss[company] = (
-					flt(provisional_profit_loss[company]) - flt(opening_balance.get(company))
+				provisional_profit_loss[company] = flt(provisional_profit_loss[company]) - flt(
+					opening_balance.get(company)
 				)
 
 		unclosed["total"] = opening_balance.get(company)
@@ -103,13 +112,23 @@
 	if total_credit:
 		data.append(total_credit)
 
-	report_summary = get_bs_summary(companies, asset, liability, equity, provisional_profit_loss, total_credit,
-		company_currency, filters, True)
+	report_summary = get_bs_summary(
+		companies,
+		asset,
+		liability,
+		equity,
+		provisional_profit_loss,
+		total_credit,
+		company_currency,
+		filters,
+		True,
+	)
 
 	chart = get_chart_data(filters, columns, asset, liability, equity)
 
 	return data, message, chart, report_summary
 
+
 def prepare_companywise_opening_balance(asset_data, liability_data, equity_data, companies):
 	opening_balance = {}
 	for company in companies:
@@ -119,29 +138,36 @@
 		for data in [asset_data, liability_data, equity_data]:
 			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_value += get_opening_balance(account_name, data, company) or 0.0
 
 		opening_balance[company] = opening_value
 
 	if opening_balance:
 		return _("Previous Financial Year is not closed"), opening_balance
 
-	return '', {}
+	return "", {}
+
 
 def get_opening_balance(account_name, data, company):
 	for row in data:
-		if row.get('account_name') == account_name:
-			return row.get('company_wise_opening_bal', {}).get(company, 0.0)
+		if row.get("account_name") == account_name:
+			return row.get("company_wise_opening_bal", {}).get(company, 0.0)
+
 
 def get_root_account_name(root_type, company):
 	return frappe.get_all(
-		'Account',
-		fields=['account_name'],
-		filters = {'root_type': root_type, 'is_group': 1,
-			'company': company, 'parent_account': ('is', 'not set')},
-		as_list=1
+		"Account",
+		fields=["account_name"],
+		filters={
+			"root_type": root_type,
+			"is_group": 1,
+			"company": company,
+			"parent_account": ("is", "not set"),
+		},
+		as_list=1,
 	)[0][0]
 
+
 def get_profit_loss_data(fiscal_year, companies, columns, filters):
 	income, expense, net_profit_loss = get_income_expense_data(companies, fiscal_year, filters)
 	company_currency = get_company_currency(filters)
@@ -154,20 +180,26 @@
 
 	chart = get_pl_chart_data(filters, columns, income, expense, net_profit_loss)
 
-	report_summary = get_pl_summary(companies, '', income, expense, net_profit_loss, company_currency, filters, True)
+	report_summary = get_pl_summary(
+		companies, "", income, expense, net_profit_loss, company_currency, filters, True
+	)
 
 	return data, None, chart, report_summary
 
+
 def get_income_expense_data(companies, fiscal_year, filters):
 	company_currency = get_company_currency(filters)
 	income = get_data(companies, "Income", "Credit", fiscal_year, filters, True)
 
 	expense = get_data(companies, "Expense", "Debit", fiscal_year, filters, True)
 
-	net_profit_loss = get_net_profit_loss(income, expense, companies, filters.company, company_currency, True)
+	net_profit_loss = get_net_profit_loss(
+		income, expense, companies, filters.company, company_currency, True
+	)
 
 	return income, expense, net_profit_loss
 
+
 def get_cash_flow_data(fiscal_year, companies, filters):
 	cash_flow_accounts = get_cash_flow_accounts()
 
@@ -179,50 +211,67 @@
 
 	for cash_flow_account in cash_flow_accounts:
 		section_data = []
-		data.append({
-			"account_name": cash_flow_account['section_header'],
-			"parent_account": None,
-			"indent": 0.0,
-			"account": cash_flow_account['section_header']
-		})
+		data.append(
+			{
+				"account_name": cash_flow_account["section_header"],
+				"parent_account": None,
+				"indent": 0.0,
+				"account": cash_flow_account["section_header"],
+			}
+		)
 
 		if len(data) == 1:
 			# add first net income in operations section
 			if net_profit_loss:
-				net_profit_loss.update({
-					"indent": 1,
-					"parent_account": cash_flow_accounts[0]['section_header']
-				})
+				net_profit_loss.update(
+					{"indent": 1, "parent_account": cash_flow_accounts[0]["section_header"]}
+				)
 				data.append(net_profit_loss)
 				section_data.append(net_profit_loss)
 
-		for account in cash_flow_account['account_types']:
-			account_data = get_account_type_based_data(account['account_type'], companies, fiscal_year, filters)
-			account_data.update({
-				"account_name": account['label'],
-				"account": account['label'],
-				"indent": 1,
-				"parent_account": cash_flow_account['section_header'],
-				"currency": company_currency
-			})
+		for account in cash_flow_account["account_types"]:
+			account_data = get_account_type_based_data(
+				account["account_type"], companies, fiscal_year, filters
+			)
+			account_data.update(
+				{
+					"account_name": account["label"],
+					"account": account["label"],
+					"indent": 1,
+					"parent_account": cash_flow_account["section_header"],
+					"currency": company_currency,
+				}
+			)
 			data.append(account_data)
 			section_data.append(account_data)
 
-		add_total_row_account(data, section_data, cash_flow_account['section_footer'],
-			companies, company_currency, summary_data, filters, True)
+		add_total_row_account(
+			data,
+			section_data,
+			cash_flow_account["section_footer"],
+			companies,
+			company_currency,
+			summary_data,
+			filters,
+			True,
+		)
 
-	add_total_row_account(data, data, _("Net Change in Cash"), companies, company_currency, summary_data, filters, True)
+	add_total_row_account(
+		data, data, _("Net Change in Cash"), companies, company_currency, summary_data, filters, True
+	)
 
 	report_summary = get_cash_flow_summary(summary_data, company_currency)
 
 	return data, report_summary
 
+
 def get_account_type_based_data(account_type, companies, fiscal_year, filters):
 	data = {}
 	total = 0
 	for company in companies:
-		amount = get_account_type_based_gl_data(company,
-			fiscal_year.year_start_date, fiscal_year.year_end_date, account_type, filters)
+		amount = get_account_type_based_gl_data(
+			company, fiscal_year.year_start_date, fiscal_year.year_end_date, account_type, filters
+		)
 
 		if amount and account_type == "Depreciation":
 			amount *= -1
@@ -233,6 +282,7 @@
 	data["total"] = total
 	return data
 
+
 def get_columns(companies, filters):
 	columns = [
 		{
@@ -240,14 +290,15 @@
 			"label": _("Account"),
 			"fieldtype": "Link",
 			"options": "Account",
-			"width": 300
-		}, {
+			"width": 300,
+		},
+		{
 			"fieldname": "currency",
 			"label": _("Currency"),
 			"fieldtype": "Link",
 			"options": "Currency",
-			"hidden": 1
-		}
+			"hidden": 1,
+		},
 	]
 
 	for company in companies:
@@ -256,69 +307,96 @@
 		if not currency:
 			currency = erpnext.get_company_currency(company)
 
-		columns.append({
-			"fieldname": company,
-			"label": f'{company} ({currency})',
-			"fieldtype": "Currency",
-			"options": "currency",
-			"width": 150,
-			"apply_currency_formatter": apply_currency_formatter,
-			"company_name": company
-		})
+		columns.append(
+			{
+				"fieldname": company,
+				"label": f"{company} ({currency})",
+				"fieldtype": "Currency",
+				"options": "currency",
+				"width": 150,
+				"apply_currency_formatter": apply_currency_formatter,
+				"company_name": company,
+			}
+		)
 
 	return columns
 
-def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, ignore_closing_entries=False):
-	accounts, accounts_by_name, parent_children_map = get_account_heads(root_type,
-		companies, filters)
 
-	if not accounts: return []
+def get_data(
+	companies, root_type, balance_must_be, fiscal_year, filters=None, ignore_closing_entries=False
+):
+	accounts, accounts_by_name, parent_children_map = get_account_heads(root_type, companies, filters)
+
+	if not accounts:
+		return []
 
 	company_currency = get_company_currency(filters)
 
-	if filters.filter_based_on == 'Fiscal Year':
-		start_date = fiscal_year.year_start_date if filters.report != 'Balance Sheet' else None
+	if filters.filter_based_on == "Fiscal Year":
+		start_date = fiscal_year.year_start_date if filters.report != "Balance Sheet" else None
 		end_date = fiscal_year.year_end_date
 	else:
-		start_date = filters.period_start_date if filters.report != 'Balance Sheet' else None
+		start_date = filters.period_start_date if filters.report != "Balance Sheet" else None
 		end_date = filters.period_end_date
 
 	filters.end_date = end_date
 
 	gl_entries_by_account = {}
-	for root in frappe.db.sql("""select lft, rgt from tabAccount
-			where root_type=%s and ifnull(parent_account, '') = ''""", root_type, as_dict=1):
+	for root in frappe.db.sql(
+		"""select lft, rgt from tabAccount
+			where root_type=%s and ifnull(parent_account, '') = ''""",
+		root_type,
+		as_dict=1,
+	):
 
-		set_gl_entries_by_account(start_date,
-			end_date, root.lft, root.rgt, filters,
-			gl_entries_by_account, accounts_by_name, accounts, ignore_closing_entries=False)
+		set_gl_entries_by_account(
+			start_date,
+			end_date,
+			root.lft,
+			root.rgt,
+			filters,
+			gl_entries_by_account,
+			accounts_by_name,
+			accounts,
+			ignore_closing_entries=False,
+		)
 
 	calculate_values(accounts_by_name, gl_entries_by_account, companies, filters, fiscal_year)
 	accumulate_values_into_parents(accounts, accounts_by_name, companies)
 
-	out = prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency, filters)
+	out = prepare_data(
+		accounts, start_date, end_date, balance_must_be, companies, company_currency, filters
+	)
 
-	out = filter_out_zero_value_rows(out, parent_children_map, show_zero_values=filters.get("show_zero_values"))
+	out = filter_out_zero_value_rows(
+		out, parent_children_map, show_zero_values=filters.get("show_zero_values")
+	)
 
 	if out:
 		add_total_row(out, root_type, balance_must_be, companies, company_currency)
 
 	return out
 
+
 def get_company_currency(filters=None):
-	return (filters.get('presentation_currency')
-		or frappe.get_cached_value('Company',  filters.company,  "default_currency"))
+	return filters.get("presentation_currency") or frappe.get_cached_value(
+		"Company", filters.company, "default_currency"
+	)
+
 
 def calculate_values(accounts_by_name, gl_entries_by_account, companies, filters, fiscal_year):
-	start_date = (fiscal_year.year_start_date
-		if filters.filter_based_on == 'Fiscal Year' else filters.period_start_date)
+	start_date = (
+		fiscal_year.year_start_date
+		if filters.filter_based_on == "Fiscal Year"
+		else filters.period_start_date
+	)
 
 	for entries in gl_entries_by_account.values():
 		for entry in entries:
 			if entry.account_number:
-				account_name = entry.account_number + ' - ' + entry.account_name
+				account_name = entry.account_number + " - " + entry.account_name
 			else:
-				account_name =  entry.account_name
+				account_name = entry.account_name
 
 			d = accounts_by_name.get(account_name)
 
@@ -326,28 +404,34 @@
 				debit, credit = 0, 0
 				for company in companies:
 					# check if posting date is within the period
-					if (entry.company == company or (filters.get('accumulated_in_group_company'))
-						and entry.company in companies.get(company)):
+					if (
+						entry.company == company
+						or (filters.get("accumulated_in_group_company"))
+						and entry.company in companies.get(company)
+					):
 						parent_company_currency = erpnext.get_company_currency(d.company)
 						child_company_currency = erpnext.get_company_currency(entry.company)
 
 						debit, credit = flt(entry.debit), flt(entry.credit)
 
-						if (not filters.get('presentation_currency')
+						if (
+							not filters.get("presentation_currency")
 							and entry.company != company
 							and parent_company_currency != child_company_currency
-							and filters.get('accumulated_in_group_company')):
+							and filters.get("accumulated_in_group_company")
+						):
 							debit = convert(debit, parent_company_currency, child_company_currency, filters.end_date)
 							credit = convert(credit, parent_company_currency, child_company_currency, filters.end_date)
 
 						d[company] = d.get(company, 0.0) + flt(debit) - flt(credit)
 
 						if entry.posting_date < getdate(start_date):
-							d['company_wise_opening_bal'][company] += (flt(debit) - flt(credit))
+							d["company_wise_opening_bal"][company] += flt(debit) - flt(credit)
 
 				if entry.posting_date < getdate(start_date):
 					d["opening_balance"] = d.get("opening_balance", 0.0) + flt(debit) - flt(credit)
 
+
 def accumulate_values_into_parents(accounts, accounts_by_name, companies):
 	"""accumulate children's values in parent accounts"""
 	for d in reversed(accounts):
@@ -355,13 +439,18 @@
 			account = d.parent_account_name
 
 			for company in companies:
-				accounts_by_name[account][company] = \
-					accounts_by_name[account].get(company, 0.0) + d.get(company, 0.0)
+				accounts_by_name[account][company] = accounts_by_name[account].get(company, 0.0) + d.get(
+					company, 0.0
+				)
 
-				accounts_by_name[account]['company_wise_opening_bal'][company] += d.get('company_wise_opening_bal', {}).get(company, 0.0)
+				accounts_by_name[account]["company_wise_opening_bal"][company] += d.get(
+					"company_wise_opening_bal", {}
+				).get(company, 0.0)
 
-			accounts_by_name[account]["opening_balance"] = \
-				accounts_by_name[account].get("opening_balance", 0.0) + d.get("opening_balance", 0.0)
+			accounts_by_name[account]["opening_balance"] = accounts_by_name[account].get(
+				"opening_balance", 0.0
+			) + d.get("opening_balance", 0.0)
+
 
 def get_account_heads(root_type, companies, filters):
 	accounts = get_accounts(root_type, companies)
@@ -375,20 +464,21 @@
 
 	return accounts, accounts_by_name, parent_children_map
 
+
 def update_parent_account_names(accounts):
 	"""Update parent_account_name in accounts list.
 
-		parent_name is `name` of parent account which could have other prefix
-		of account_number and suffix of company abbr. This function adds key called
-		`parent_account_name` which does not have such prefix/suffix.
+	parent_name is `name` of parent account which could have other prefix
+	of account_number and suffix of company abbr. This function adds key called
+	`parent_account_name` which does not have such prefix/suffix.
 	"""
 	name_to_account_map = {}
 
 	for d in accounts:
 		if d.account_number:
-			account_name = d.account_number + ' - ' + d.account_name
+			account_name = d.account_number + " - " + d.account_name
 		else:
-			account_name =  d.account_name
+			account_name = d.account_name
 		name_to_account_map[d.name] = account_name
 
 	for account in accounts:
@@ -397,10 +487,11 @@
 
 	return accounts
 
+
 def get_companies(filters):
 	companies = {}
-	all_companies = get_subsidiary_companies(filters.get('company'))
-	companies.setdefault(filters.get('company'), all_companies)
+	all_companies = get_subsidiary_companies(filters.get("company"))
+	companies.setdefault(filters.get("company"), all_companies)
 
 	for d in all_companies:
 		if d not in companies:
@@ -409,47 +500,73 @@
 
 	return all_companies, companies
 
-def get_subsidiary_companies(company):
-	lft, rgt = frappe.get_cached_value('Company',
-		company,  ["lft", "rgt"])
 
-	return frappe.db.sql_list("""select name from `tabCompany`
-		where lft >= {0} and rgt <= {1} order by lft, rgt""".format(lft, rgt))
+def get_subsidiary_companies(company):
+	lft, rgt = frappe.get_cached_value("Company", company, ["lft", "rgt"])
+
+	return frappe.db.sql_list(
+		"""select name from `tabCompany`
+		where lft >= {0} and rgt <= {1} order by lft, rgt""".format(
+			lft, rgt
+		)
+	)
+
 
 def get_accounts(root_type, companies):
 	accounts = []
 	added_accounts = []
 
 	for company in companies:
-		for account in frappe.get_all("Account", fields=["name", "is_group", "company",
-			"parent_account", "lft", "rgt", "root_type", "report_type", "account_name", "account_number"],
-			filters={"company": company, "root_type": root_type}):
+		for account in frappe.get_all(
+			"Account",
+			fields=[
+				"name",
+				"is_group",
+				"company",
+				"parent_account",
+				"lft",
+				"rgt",
+				"root_type",
+				"report_type",
+				"account_name",
+				"account_number",
+			],
+			filters={"company": company, "root_type": root_type},
+		):
 			if account.account_name not in added_accounts:
 				accounts.append(account)
 				added_accounts.append(account.account_name)
 
 	return accounts
 
-def prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency, filters):
+
+def prepare_data(
+	accounts, start_date, end_date, balance_must_be, companies, company_currency, filters
+):
 	data = []
 
 	for d in accounts:
 		# add to output
 		has_value = False
 		total = 0
-		row = frappe._dict({
-			"account_name": ('%s - %s' %(_(d.account_number), _(d.account_name))
-				if d.account_number else _(d.account_name)),
-			"account": _(d.name),
-			"parent_account": _(d.parent_account),
-			"indent": flt(d.indent),
-			"year_start_date": start_date,
-			"root_type": d.root_type,
-			"year_end_date": end_date,
-			"currency": filters.presentation_currency,
-			"company_wise_opening_bal": d.company_wise_opening_bal,
-			"opening_balance": d.get("opening_balance", 0.0) * (1 if balance_must_be == "Debit" else -1)
-		})
+		row = frappe._dict(
+			{
+				"account_name": (
+					"%s - %s" % (_(d.account_number), _(d.account_name))
+					if d.account_number
+					else _(d.account_name)
+				),
+				"account": _(d.name),
+				"parent_account": _(d.parent_account),
+				"indent": flt(d.indent),
+				"year_start_date": start_date,
+				"root_type": d.root_type,
+				"year_end_date": end_date,
+				"currency": filters.presentation_currency,
+				"company_wise_opening_bal": d.company_wise_opening_bal,
+				"opening_balance": d.get("opening_balance", 0.0) * (1 if balance_must_be == "Debit" else -1),
+			}
+		)
 
 		for company in companies:
 			if d.get(company) and balance_must_be == "Credit":
@@ -470,32 +587,49 @@
 
 	return data
 
-def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, gl_entries_by_account,
-	accounts_by_name, accounts, ignore_closing_entries=False):
+
+def set_gl_entries_by_account(
+	from_date,
+	to_date,
+	root_lft,
+	root_rgt,
+	filters,
+	gl_entries_by_account,
+	accounts_by_name,
+	accounts,
+	ignore_closing_entries=False,
+):
 	"""Returns a dict like { "account": [gl entries], ... }"""
 
-	company_lft, company_rgt = frappe.get_cached_value('Company',
-		filters.get('company'),  ["lft", "rgt"])
+	company_lft, company_rgt = frappe.get_cached_value(
+		"Company", filters.get("company"), ["lft", "rgt"]
+	)
 
 	additional_conditions = get_additional_conditions(from_date, ignore_closing_entries, filters)
-	companies = frappe.db.sql(""" select name, default_currency from `tabCompany`
-		where lft >= %(company_lft)s and rgt <= %(company_rgt)s""", {
+	companies = frappe.db.sql(
+		""" select name, default_currency from `tabCompany`
+		where lft >= %(company_lft)s and rgt <= %(company_rgt)s""",
+		{
 			"company_lft": company_lft,
 			"company_rgt": company_rgt,
-		}, as_dict=1)
+		},
+		as_dict=1,
+	)
 
-	currency_info = frappe._dict({
-		'report_date': to_date,
-		'presentation_currency': filters.get('presentation_currency')
-	})
+	currency_info = frappe._dict(
+		{"report_date": to_date, "presentation_currency": filters.get("presentation_currency")}
+	)
 
 	for d in companies:
-		gl_entries = frappe.db.sql("""select gl.posting_date, gl.account, gl.debit, gl.credit, gl.is_opening, gl.company,
+		gl_entries = frappe.db.sql(
+			"""select gl.posting_date, gl.account, gl.debit, gl.credit, gl.is_opening, gl.company,
 			gl.fiscal_year, gl.debit_in_account_currency, gl.credit_in_account_currency, gl.account_currency,
 			acc.account_name, acc.account_number
 			from `tabGL Entry` gl, `tabAccount` acc where acc.name = gl.account and gl.company = %(company)s and gl.is_cancelled = 0
 			{additional_conditions} and gl.posting_date <= %(to_date)s and acc.lft >= %(lft)s and acc.rgt <= %(rgt)s
-			order by gl.account, gl.posting_date""".format(additional_conditions=additional_conditions),
+			order by gl.account, gl.posting_date""".format(
+				additional_conditions=additional_conditions
+			),
 			{
 				"from_date": from_date,
 				"to_date": to_date,
@@ -503,29 +637,47 @@
 				"rgt": root_rgt,
 				"company": d.name,
 				"finance_book": filters.get("finance_book"),
-				"company_fb": frappe.db.get_value("Company", d.name, 'default_finance_book')
+				"company_fb": frappe.db.get_value("Company", d.name, "default_finance_book"),
 			},
-			as_dict=True)
+			as_dict=True,
+		)
 
-		if filters and filters.get('presentation_currency') != d.default_currency:
-			currency_info['company'] = d.name
-			currency_info['company_currency'] = d.default_currency
-			convert_to_presentation_currency(gl_entries, currency_info, filters.get('company'))
+		if filters and filters.get("presentation_currency") != d.default_currency:
+			currency_info["company"] = d.name
+			currency_info["company_currency"] = d.default_currency
+			convert_to_presentation_currency(gl_entries, currency_info, filters.get("company"))
 
 		for entry in gl_entries:
 			if entry.account_number:
-				account_name = entry.account_number + ' - ' + entry.account_name
+				account_name = entry.account_number + " - " + entry.account_name
 			else:
-				account_name =  entry.account_name
+				account_name = entry.account_name
 
 			validate_entries(account_name, entry, accounts_by_name, accounts)
 			gl_entries_by_account.setdefault(account_name, []).append(entry)
 
 	return gl_entries_by_account
 
+
 def get_account_details(account):
-	return frappe.get_cached_value('Account', account, ['name', 'report_type', 'root_type', 'company',
-		'is_group', 'account_name', 'account_number', 'parent_account', 'lft', 'rgt'], as_dict=1)
+	return frappe.get_cached_value(
+		"Account",
+		account,
+		[
+			"name",
+			"report_type",
+			"root_type",
+			"company",
+			"is_group",
+			"account_name",
+			"account_number",
+			"parent_account",
+			"lft",
+			"rgt",
+		],
+		as_dict=1,
+	)
+
 
 def validate_entries(key, entry, accounts_by_name, accounts):
 	# If an account present in the child company and not in the parent company
@@ -535,15 +687,17 @@
 		if args.parent_account:
 			parent_args = get_account_details(args.parent_account)
 
-			args.update({
-				'lft': parent_args.lft + 1,
-				'rgt': parent_args.rgt - 1,
-				'indent': 3,
-				'root_type': parent_args.root_type,
-				'report_type': parent_args.report_type,
-				'parent_account_name': parent_args.account_name,
-				'company_wise_opening_bal': defaultdict(float)
-			})
+			args.update(
+				{
+					"lft": parent_args.lft + 1,
+					"rgt": parent_args.rgt - 1,
+					"indent": 3,
+					"root_type": parent_args.root_type,
+					"report_type": parent_args.report_type,
+					"parent_account_name": parent_args.account_name,
+					"company_wise_opening_bal": defaultdict(float),
+				}
+			)
 
 		accounts_by_name.setdefault(key, args)
 
@@ -554,7 +708,8 @@
 				idx = index
 				break
 
-		accounts.insert(idx+1, args)
+		accounts.insert(idx + 1, args)
+
 
 def get_additional_conditions(from_date, ignore_closing_entries, filters):
 	additional_conditions = []
@@ -566,17 +721,20 @@
 		additional_conditions.append("gl.posting_date >= %(from_date)s")
 
 	if filters.get("include_default_book_entries"):
-		additional_conditions.append("(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)")
+		additional_conditions.append(
+			"(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)"
+		)
 	else:
 		additional_conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)")
 
 	return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else ""
 
+
 def add_total_row(out, root_type, balance_must_be, companies, company_currency):
 	total_row = {
 		"account_name": "'" + _("Total {0} ({1})").format(_(root_type), _(balance_must_be)) + "'",
 		"account": "'" + _("Total {0} ({1})").format(_(root_type), _(balance_must_be)) + "'",
-		"currency": company_currency
+		"currency": company_currency,
 	}
 
 	for row in out:
@@ -595,15 +753,16 @@
 		# blank row after Total
 		out.append({})
 
+
 def filter_accounts(accounts, depth=10):
 	parent_children_map = {}
 	accounts_by_name = {}
 	for d in accounts:
 		if d.account_number:
-			account_name = d.account_number + ' - ' + d.account_name
+			account_name = d.account_number + " - " + d.account_name
 		else:
-			account_name =  d.account_name
-		d['company_wise_opening_bal'] = defaultdict(float)
+			account_name = d.account_name
+		d["company_wise_opening_bal"] = defaultdict(float)
 		accounts_by_name[account_name] = d
 
 		parent_children_map.setdefault(d.parent_account or None, []).append(d)
@@ -613,7 +772,7 @@
 	def add_to_list(parent, level):
 		if level < depth:
 			children = parent_children_map.get(parent) or []
-			sort_accounts(children, is_root=True if parent==None else False)
+			sort_accounts(children, is_root=True if parent == None else False)
 
 			for child in children:
 				child.indent = level
diff --git a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py
index 56db841..cafe95b 100644
--- a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py
+++ b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py
@@ -14,14 +14,16 @@
 		self.filters.to_date = getdate(self.filters.to_date or nowdate())
 
 		if not self.filters.get("company"):
-			self.filters["company"] = frappe.db.get_single_value('Global Defaults', 'default_company')
+			self.filters["company"] = frappe.db.get_single_value("Global Defaults", "default_company")
 
 	def run(self, args):
 		if self.filters.from_date > self.filters.to_date:
 			frappe.throw(_("From Date must be before To Date"))
 
 		self.filters.party_type = args.get("party_type")
-		self.party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1])
+		self.party_naming_by = frappe.db.get_value(
+			args.get("naming_by")[0], None, args.get("naming_by")[1]
+		)
 
 		self.get_gl_entries()
 		self.get_return_invoices()
@@ -32,21 +34,25 @@
 		return columns, data
 
 	def get_columns(self):
-		columns = [{
-			"label": _(self.filters.party_type),
-			"fieldtype": "Link",
-			"fieldname": "party",
-			"options": self.filters.party_type,
-			"width": 200
-		}]
+		columns = [
+			{
+				"label": _(self.filters.party_type),
+				"fieldtype": "Link",
+				"fieldname": "party",
+				"options": self.filters.party_type,
+				"width": 200,
+			}
+		]
 
 		if self.party_naming_by == "Naming Series":
-			columns.append({
-				"label": _(self.filters.party_type + "Name"),
-				"fieldtype": "Data",
-				"fieldname": "party_name",
-				"width": 110
-			})
+			columns.append(
+				{
+					"label": _(self.filters.party_type + "Name"),
+					"fieldtype": "Data",
+					"fieldname": "party_name",
+					"width": 110,
+				}
+			)
 
 		credit_or_debit_note = "Credit Note" if self.filters.party_type == "Customer" else "Debit Note"
 
@@ -56,40 +62,42 @@
 				"fieldname": "opening_balance",
 				"fieldtype": "Currency",
 				"options": "currency",
-				"width": 120
+				"width": 120,
 			},
 			{
 				"label": _("Invoiced Amount"),
 				"fieldname": "invoiced_amount",
 				"fieldtype": "Currency",
 				"options": "currency",
-				"width": 120
+				"width": 120,
 			},
 			{
 				"label": _("Paid Amount"),
 				"fieldname": "paid_amount",
 				"fieldtype": "Currency",
 				"options": "currency",
-				"width": 120
+				"width": 120,
 			},
 			{
 				"label": _(credit_or_debit_note),
 				"fieldname": "return_amount",
 				"fieldtype": "Currency",
 				"options": "currency",
-				"width": 120
+				"width": 120,
 			},
 		]
 
 		for account in self.party_adjustment_accounts:
-			columns.append({
-				"label": account,
-				"fieldname": "adj_" + scrub(account),
-				"fieldtype": "Currency",
-				"options": "currency",
-				"width": 120,
-				"is_adjustment": 1
-			})
+			columns.append(
+				{
+					"label": account,
+					"fieldname": "adj_" + scrub(account),
+					"fieldtype": "Currency",
+					"options": "currency",
+					"width": 120,
+					"is_adjustment": 1,
+				}
+			)
 
 		columns += [
 			{
@@ -97,36 +105,43 @@
 				"fieldname": "closing_balance",
 				"fieldtype": "Currency",
 				"options": "currency",
-				"width": 120
+				"width": 120,
 			},
 			{
 				"label": _("Currency"),
 				"fieldname": "currency",
 				"fieldtype": "Link",
 				"options": "Currency",
-				"width": 50
-			}
+				"width": 50,
+			},
 		]
 
 		return columns
 
 	def get_data(self):
-		company_currency = frappe.get_cached_value('Company',  self.filters.get("company"),  "default_currency")
+		company_currency = frappe.get_cached_value(
+			"Company", self.filters.get("company"), "default_currency"
+		)
 		invoice_dr_or_cr = "debit" if self.filters.party_type == "Customer" else "credit"
 		reverse_dr_or_cr = "credit" if self.filters.party_type == "Customer" else "debit"
 
 		self.party_data = frappe._dict({})
 		for gle in self.gl_entries:
-			self.party_data.setdefault(gle.party, frappe._dict({
-				"party": gle.party,
-				"party_name": gle.party_name,
-				"opening_balance": 0,
-				"invoiced_amount": 0,
-				"paid_amount": 0,
-				"return_amount": 0,
-				"closing_balance": 0,
-				"currency": company_currency
-			}))
+			self.party_data.setdefault(
+				gle.party,
+				frappe._dict(
+					{
+						"party": gle.party,
+						"party_name": gle.party_name,
+						"opening_balance": 0,
+						"invoiced_amount": 0,
+						"paid_amount": 0,
+						"return_amount": 0,
+						"closing_balance": 0,
+						"currency": company_currency,
+					}
+				),
+			)
 
 			amount = gle.get(invoice_dr_or_cr) - gle.get(reverse_dr_or_cr)
 			self.party_data[gle.party].closing_balance += amount
@@ -143,8 +158,16 @@
 
 		out = []
 		for party, row in self.party_data.items():
-			if row.opening_balance or row.invoiced_amount or row.paid_amount or row.return_amount or row.closing_amount:
-				total_party_adjustment = sum(amount for amount in self.party_adjustment_details.get(party, {}).values())
+			if (
+				row.opening_balance
+				or row.invoiced_amount
+				or row.paid_amount
+				or row.return_amount
+				or row.closing_amount
+			):
+				total_party_adjustment = sum(
+					amount for amount in self.party_adjustment_details.get(party, {}).values()
+				)
 				row.paid_amount -= total_party_adjustment
 
 				adjustments = self.party_adjustment_details.get(party, {})
@@ -165,7 +188,8 @@
 			join_field = ", p.supplier_name as party_name"
 			join = "left join `tabSupplier` p on gle.party = p.name"
 
-		self.gl_entries = frappe.db.sql("""
+		self.gl_entries = frappe.db.sql(
+			"""
 			select
 				gle.posting_date, gle.party, gle.voucher_type, gle.voucher_no, gle.against_voucher_type,
 				gle.against_voucher, gle.debit, gle.credit, gle.is_opening {join_field}
@@ -175,7 +199,12 @@
 				gle.docstatus < 2 and gle.is_cancelled = 0 and gle.party_type=%(party_type)s and ifnull(gle.party, '') != ''
 				and gle.posting_date <= %(to_date)s {conditions}
 			order by gle.posting_date
-		""".format(join=join, join_field=join_field, conditions=conditions), self.filters, as_dict=True)
+		""".format(
+				join=join, join_field=join_field, conditions=conditions
+			),
+			self.filters,
+			as_dict=True,
+		)
 
 	def prepare_conditions(self):
 		conditions = [""]
@@ -191,57 +220,88 @@
 
 		if self.filters.party_type == "Customer":
 			if self.filters.get("customer_group"):
-				lft, rgt = frappe.db.get_value("Customer Group",
-					self.filters.get("customer_group"), ["lft", "rgt"])
+				lft, rgt = frappe.db.get_value(
+					"Customer Group", self.filters.get("customer_group"), ["lft", "rgt"]
+				)
 
-				conditions.append("""party in (select name from tabCustomer
+				conditions.append(
+					"""party in (select name from tabCustomer
 					where exists(select name from `tabCustomer Group` where lft >= {0} and rgt <= {1}
-						and name=tabCustomer.customer_group))""".format(lft, rgt))
+						and name=tabCustomer.customer_group))""".format(
+						lft, rgt
+					)
+				)
 
 			if self.filters.get("territory"):
-				lft, rgt = frappe.db.get_value("Territory",
-					self.filters.get("territory"), ["lft", "rgt"])
+				lft, rgt = frappe.db.get_value("Territory", self.filters.get("territory"), ["lft", "rgt"])
 
-				conditions.append("""party in (select name from tabCustomer
+				conditions.append(
+					"""party in (select name from tabCustomer
 					where exists(select name from `tabTerritory` where lft >= {0} and rgt <= {1}
-						and name=tabCustomer.territory))""".format(lft, rgt))
+						and name=tabCustomer.territory))""".format(
+						lft, rgt
+					)
+				)
 
 			if self.filters.get("payment_terms_template"):
-				conditions.append("party in (select name from tabCustomer where payment_terms=%(payment_terms_template)s)")
+				conditions.append(
+					"party in (select name from tabCustomer where payment_terms=%(payment_terms_template)s)"
+				)
 
 			if self.filters.get("sales_partner"):
-				conditions.append("party in (select name from tabCustomer where default_sales_partner=%(sales_partner)s)")
+				conditions.append(
+					"party in (select name from tabCustomer where default_sales_partner=%(sales_partner)s)"
+				)
 
 			if self.filters.get("sales_person"):
-				lft, rgt = frappe.db.get_value("Sales Person",
-					self.filters.get("sales_person"), ["lft", "rgt"])
+				lft, rgt = frappe.db.get_value(
+					"Sales Person", self.filters.get("sales_person"), ["lft", "rgt"]
+				)
 
-				conditions.append("""exists(select name from `tabSales Team` steam where
+				conditions.append(
+					"""exists(select name from `tabSales Team` steam where
 					steam.sales_person in (select name from `tabSales Person` where lft >= {0} and rgt <= {1})
 					and ((steam.parent = voucher_no and steam.parenttype = voucher_type)
 						or (steam.parent = against_voucher and steam.parenttype = against_voucher_type)
-						or (steam.parent = party and steam.parenttype = 'Customer')))""".format(lft, rgt))
+						or (steam.parent = party and steam.parenttype = 'Customer')))""".format(
+						lft, rgt
+					)
+				)
 
 		if self.filters.party_type == "Supplier":
 			if self.filters.get("supplier_group"):
-				conditions.append("""party in (select name from tabSupplier
-					where supplier_group=%(supplier_group)s)""")
+				conditions.append(
+					"""party in (select name from tabSupplier
+					where supplier_group=%(supplier_group)s)"""
+				)
 
 		return " and ".join(conditions)
 
 	def get_return_invoices(self):
 		doctype = "Sales Invoice" if self.filters.party_type == "Customer" else "Purchase Invoice"
-		self.return_invoices = [d.name for d in frappe.get_all(doctype, filters={"is_return": 1, "docstatus": 1,
-			"posting_date": ["between", [self.filters.from_date, self.filters.to_date]]})]
+		self.return_invoices = [
+			d.name
+			for d in frappe.get_all(
+				doctype,
+				filters={
+					"is_return": 1,
+					"docstatus": 1,
+					"posting_date": ["between", [self.filters.from_date, self.filters.to_date]],
+				},
+			)
+		]
 
 	def get_party_adjustment_amounts(self):
 		conditions = self.prepare_conditions()
-		income_or_expense = "Expense Account" if self.filters.party_type == "Customer" else "Income Account"
+		income_or_expense = (
+			"Expense Account" if self.filters.party_type == "Customer" else "Income Account"
+		)
 		invoice_dr_or_cr = "debit" if self.filters.party_type == "Customer" else "credit"
 		reverse_dr_or_cr = "credit" if self.filters.party_type == "Customer" else "debit"
-		round_off_account = frappe.get_cached_value('Company', self.filters.company, "round_off_account")
+		round_off_account = frappe.get_cached_value("Company", self.filters.company, "round_off_account")
 
-		gl_entries = frappe.db.sql("""
+		gl_entries = frappe.db.sql(
+			"""
 			select
 				posting_date, account, party, voucher_type, voucher_no, debit, credit
 			from
@@ -257,7 +317,12 @@
 					where gle.party_type=%(party_type)s and ifnull(party, '') != ''
 					and gle.posting_date between %(from_date)s and %(to_date)s and gle.docstatus < 2 {conditions}
 				)
-		""".format(conditions=conditions, income_or_expense=income_or_expense), self.filters, as_dict=True)
+		""".format(
+				conditions=conditions, income_or_expense=income_or_expense
+			),
+			self.filters,
+			as_dict=True,
+		)
 
 		self.party_adjustment_details = {}
 		self.party_adjustment_accounts = set()
@@ -299,6 +364,7 @@
 						self.party_adjustment_details[party].setdefault(account, 0)
 						self.party_adjustment_details[party][account] += amount
 
+
 def execute(filters=None):
 	args = {
 		"party_type": "Customer",
diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py
index 3a51db8..1eb257a 100644
--- a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py
+++ b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py
@@ -361,7 +361,8 @@
 					"fieldname": period.key,
 					"fieldtype": "Currency",
 					"read_only": 1,
-				})
+				}
+			)
 		return columns
 
 	def generate_report_data(self):
@@ -408,11 +409,9 @@
 		}
 
 		if self.filters.with_upcoming_postings:
-			chart["data"]["datasets"].append({
-				"name": "Expected",
-				"chartType": "line",
-				"values": [x.total for x in self.period_total]
-			})
+			chart["data"]["datasets"].append(
+				{"name": "Expected", "chartType": "line", "values": [x.total for x in self.period_total]}
+			)
 
 		return chart
 
diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py
index 86eb213..023ff22 100644
--- a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py
+++ b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py
@@ -88,10 +88,12 @@
 			posting_date="2021-05-01",
 			parent_cost_center="Main - _CD",
 			cost_center="Main - _CD",
-			do_not_submit=True,
+			do_not_save=True,
 			rate=300,
 			price_list_rate=300,
 		)
+
+		si.items[0].income_account = "Sales - _CD"
 		si.items[0].enable_deferred_revenue = 1
 		si.items[0].service_start_date = "2021-05-01"
 		si.items[0].service_end_date = "2021-08-01"
@@ -269,11 +271,13 @@
 			posting_date="2021-05-01",
 			parent_cost_center="Main - _CD",
 			cost_center="Main - _CD",
-			do_not_submit=True,
+			do_not_save=True,
 			rate=300,
 			price_list_rate=300,
 		)
+
 		si.items[0].enable_deferred_revenue = 1
+		si.items[0].income_account = "Sales - _CD"
 		si.items[0].deferred_revenue_account = deferred_revenue_account
 		si.items[0].income_account = "Sales - _CD"
 		si.save()
@@ -318,6 +322,7 @@
 		]
 		self.assertEqual(report.period_total, expected)
 
+
 def create_company():
 	company = frappe.db.exists("Company", "_Test Company DR")
 	if not company:
diff --git a/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py b/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py
index 004d092..59914dc 100644
--- a/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py
+++ b/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py
@@ -13,6 +13,7 @@
 	data = get_ordered_to_be_billed_data(args)
 	return columns, data
 
+
 def get_column():
 	return [
 		{
@@ -20,90 +21,76 @@
 			"fieldname": "name",
 			"fieldtype": "Link",
 			"options": "Delivery Note",
-			"width": 160
+			"width": 160,
 		},
-		{
-			"label": _("Date"),
-			"fieldname": "date",
-			"fieldtype": "Date",
-			"width": 100
-		},
+		{"label": _("Date"), "fieldname": "date", "fieldtype": "Date", "width": 100},
 		{
 			"label": _("Customer"),
 			"fieldname": "customer",
 			"fieldtype": "Link",
 			"options": "Customer",
-			"width": 120
+			"width": 120,
 		},
-		{
-			"label": _("Customer Name"),
-			"fieldname": "customer_name",
-			"fieldtype": "Data",
-			"width": 120
-		},
+		{"label": _("Customer Name"), "fieldname": "customer_name", "fieldtype": "Data", "width": 120},
 		{
 			"label": _("Item Code"),
 			"fieldname": "item_code",
 			"fieldtype": "Link",
 			"options": "Item",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("Amount"),
 			"fieldname": "amount",
 			"fieldtype": "Currency",
 			"width": 100,
-			"options": "Company:company:default_currency"
+			"options": "Company:company:default_currency",
 		},
 		{
 			"label": _("Billed Amount"),
 			"fieldname": "billed_amount",
 			"fieldtype": "Currency",
 			"width": 100,
-			"options": "Company:company:default_currency"
+			"options": "Company:company:default_currency",
 		},
 		{
 			"label": _("Returned Amount"),
 			"fieldname": "returned_amount",
 			"fieldtype": "Currency",
 			"width": 120,
-			"options": "Company:company:default_currency"
+			"options": "Company:company:default_currency",
 		},
 		{
 			"label": _("Pending Amount"),
 			"fieldname": "pending_amount",
 			"fieldtype": "Currency",
 			"width": 120,
-			"options": "Company:company:default_currency"
+			"options": "Company:company:default_currency",
 		},
-		{
-			"label": _("Item Name"),
-			"fieldname": "item_name",
-			"fieldtype": "Data",
-			"width": 120
-		},
-		{
-			"label": _("Description"),
-			"fieldname": "description",
-			"fieldtype": "Data",
-			"width": 120
-		},
+		{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 120},
+		{"label": _("Description"), "fieldname": "description", "fieldtype": "Data", "width": 120},
 		{
 			"label": _("Project"),
 			"fieldname": "project",
 			"fieldtype": "Link",
 			"options": "Project",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("Company"),
 			"fieldname": "company",
 			"fieldtype": "Link",
 			"options": "Company",
-			"width": 120
-		}
+			"width": 120,
+		},
 	]
 
+
 def get_args():
-	return {'doctype': 'Delivery Note', 'party': 'customer',
-		'date': 'posting_date', 'order': 'name', 'order_by': 'desc'}
+	return {
+		"doctype": "Delivery Note",
+		"party": "customer",
+		"date": "posting_date",
+		"order": "name",
+		"order_by": "desc",
+	}
diff --git a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js
index 6a03948..ea05a35 100644
--- a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js
+++ b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js
@@ -39,12 +39,14 @@
 				"label": __("From Date"),
 				"fieldtype": "Date",
 				"default": frappe.defaults.get_user_default("year_start_date"),
+				"reqd": 1
 			},
 			{
 				"fieldname": "to_date",
 				"label": __("To Date"),
 				"fieldtype": "Date",
 				"default": frappe.defaults.get_user_default("year_end_date"),
+				"reqd": 1
 			},
 			{
 				"fieldname": "finance_book",
@@ -56,6 +58,7 @@
 				"fieldname": "dimension",
 				"label": __("Select Dimension"),
 				"fieldtype": "Select",
+				"default": "Cost Center",
 				"options": get_accounting_dimension_options(),
 				"reqd": 1,
 			},
@@ -70,7 +73,7 @@
 });
 
 function get_accounting_dimension_options() {
-	let options =["", "Cost Center", "Project"];
+	let options =["Cost Center", "Project"];
 	frappe.db.get_list('Accounting Dimension',
 		{fields:['document_type']}).then((res) => {
 			res.forEach((dimension) => {
diff --git a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.py b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.py
index d547470..ecad9f1 100644
--- a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.py
+++ b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.py
@@ -15,21 +15,24 @@
 
 
 def execute(filters=None):
-	validate_filters(filters)
-	dimension_items_list = get_dimension_items_list(filters.dimension, filters.company)
 
-	if not dimension_items_list:
+	validate_filters(filters)
+	dimension_list = get_dimensions(filters)
+
+	if not dimension_list:
 		return [], []
 
-	dimension_items_list = [''.join(d) for d in dimension_items_list]
-	columns = get_columns(dimension_items_list)
-	data = get_data(filters, dimension_items_list)
+	columns = get_columns(dimension_list)
+	data = get_data(filters, dimension_list)
 
 	return columns, data
 
-def get_data(filters, dimension_items_list):
+
+def get_data(filters, dimension_list):
 	company_currency = erpnext.get_company_currency(filters.company)
-	acc = frappe.db.sql("""
+
+	acc = frappe.db.sql(
+		"""
 		select
 			name, account_number, parent_account, lft, rgt, root_type,
 			report_type, account_name, include_in_gross, account_type, is_group
@@ -37,88 +40,104 @@
 			`tabAccount`
 		where
 			company=%s
-			order by lft""", (filters.company), as_dict=True)
+			order by lft""",
+		(filters.company),
+		as_dict=True,
+	)
 
 	if not acc:
 		return None
 
 	accounts, accounts_by_name, parent_children_map = filter_accounts(acc)
 
-	min_lft, max_rgt = frappe.db.sql("""select min(lft), max(rgt) from `tabAccount`
-		where company=%s""", (filters.company))[0]
+	min_lft, max_rgt = frappe.db.sql(
+		"""select min(lft), max(rgt) from `tabAccount`
+		where company=%s""",
+		(filters.company),
+	)[0]
 
-	account = frappe.db.sql_list("""select name from `tabAccount`
-		where lft >= %s and rgt <= %s and company = %s""", (min_lft, max_rgt, filters.company))
+	account = frappe.db.sql_list(
+		"""select name from `tabAccount`
+		where lft >= %s and rgt <= %s and company = %s""",
+		(min_lft, max_rgt, filters.company),
+	)
 
 	gl_entries_by_account = {}
-	set_gl_entries_by_account(dimension_items_list, filters, account, gl_entries_by_account)
-	format_gl_entries(gl_entries_by_account, accounts_by_name, dimension_items_list)
-	accumulate_values_into_parents(accounts, accounts_by_name, dimension_items_list)
-	out = prepare_data(accounts, filters, parent_children_map, company_currency, dimension_items_list)
+	set_gl_entries_by_account(dimension_list, filters, account, gl_entries_by_account)
+	format_gl_entries(
+		gl_entries_by_account, accounts_by_name, dimension_list, frappe.scrub(filters.get("dimension"))
+	)
+	accumulate_values_into_parents(accounts, accounts_by_name, dimension_list)
+	out = prepare_data(accounts, filters, company_currency, dimension_list)
 	out = filter_out_zero_value_rows(out, parent_children_map)
 
 	return out
 
-def set_gl_entries_by_account(dimension_items_list, filters, account, gl_entries_by_account):
-	for item in dimension_items_list:
-		condition = get_condition(filters.from_date, item, filters.dimension)
-		if account:
-			condition += " and account in ({})"\
-				.format(", ".join([frappe.db.escape(d) for d in account]))
 
-		gl_filters = {
-			"company": filters.get("company"),
-			"from_date": filters.get("from_date"),
-			"to_date": filters.get("to_date"),
-			"finance_book": cstr(filters.get("finance_book"))
-		}
+def set_gl_entries_by_account(dimension_list, filters, account, gl_entries_by_account):
+	condition = get_condition(filters.get("dimension"))
 
-		gl_filters['item'] = ''.join(item)
+	if account:
+		condition += " and account in ({})".format(", ".join([frappe.db.escape(d) for d in account]))
 
-		if filters.get("include_default_book_entries"):
-			gl_filters["company_fb"] = frappe.db.get_value("Company",
-				filters.company, 'default_finance_book')
+	gl_filters = {
+		"company": filters.get("company"),
+		"from_date": filters.get("from_date"),
+		"to_date": filters.get("to_date"),
+		"finance_book": cstr(filters.get("finance_book")),
+	}
 
-		for key, value in filters.items():
-			if value:
-				gl_filters.update({
-					key: value
-				})
+	gl_filters["dimensions"] = set(dimension_list)
 
-		gl_entries = frappe.db.sql("""
+	if filters.get("include_default_book_entries"):
+		gl_filters["company_fb"] = frappe.db.get_value(
+			"Company", filters.company, "default_finance_book"
+		)
+
+	gl_entries = frappe.db.sql(
+		"""
 		select
-			posting_date, account, debit, credit, is_opening, fiscal_year,
+			posting_date, account, {dimension}, debit, credit, is_opening, fiscal_year,
 			debit_in_account_currency, credit_in_account_currency, account_currency
 		from
 			`tabGL Entry`
 		where
 			company=%(company)s
 		{condition}
+		and posting_date >= %(from_date)s
 		and posting_date <= %(to_date)s
 		and is_cancelled = 0
 		order by account, posting_date""".format(
-			condition=condition),
-			gl_filters, as_dict=True) #nosec
+			dimension=frappe.scrub(filters.get("dimension")), condition=condition
+		),
+		gl_filters,
+		as_dict=True,
+	)  # nosec
 
-		for entry in gl_entries:
-			entry['dimension_item'] = ''.join(item)
-			gl_entries_by_account.setdefault(entry.account, []).append(entry)
+	for entry in gl_entries:
+		gl_entries_by_account.setdefault(entry.account, []).append(entry)
 
-def format_gl_entries(gl_entries_by_account, accounts_by_name, dimension_items_list):
+
+def format_gl_entries(gl_entries_by_account, accounts_by_name, dimension_list, dimension_type):
 
 	for entries in gl_entries_by_account.values():
 		for entry in entries:
 			d = accounts_by_name.get(entry.account)
 			if not d:
 				frappe.msgprint(
-					_("Could not retrieve information for {0}.").format(entry.account), title="Error",
-					raise_exception=1
+					_("Could not retrieve information for {0}.").format(entry.account),
+					title="Error",
+					raise_exception=1,
 				)
-			for item in dimension_items_list:
-				if item == entry.dimension_item:
-					d[frappe.scrub(item)] = d.get(frappe.scrub(item), 0.0) + flt(entry.debit) - flt(entry.credit)
 
-def prepare_data(accounts, filters, parent_children_map, company_currency, dimension_items_list):
+			for dimension in dimension_list:
+				if dimension == entry.get(dimension_type):
+					d[frappe.scrub(dimension)] = (
+						d.get(frappe.scrub(dimension), 0.0) + flt(entry.debit) - flt(entry.credit)
+					)
+
+
+def prepare_data(accounts, filters, company_currency, dimension_list):
 	data = []
 
 	for d in accounts:
@@ -131,17 +150,18 @@
 			"from_date": filters.from_date,
 			"to_date": filters.to_date,
 			"currency": company_currency,
-			"account_name": ('{} - {}'.format(d.account_number, d.account_name)
-				if d.account_number else d.account_name)
+			"account_name": (
+				"{} - {}".format(d.account_number, d.account_name) if d.account_number else d.account_name
+			),
 		}
 
-		for item in dimension_items_list:
-			row[frappe.scrub(item)] = flt(d.get(frappe.scrub(item), 0.0), 3)
+		for dimension in dimension_list:
+			row[frappe.scrub(dimension)] = flt(d.get(frappe.scrub(dimension), 0.0), 3)
 
-			if abs(row[frappe.scrub(item)]) >= 0.005:
+			if abs(row[frappe.scrub(dimension)]) >= 0.005:
 				# ignore zero values
 				has_value = True
-				total += flt(d.get(frappe.scrub(item), 0.0), 3)
+				total += flt(d.get(frappe.scrub(dimension), 0.0), 3)
 
 		row["has_value"] = has_value
 		row["total"] = total
@@ -149,68 +169,72 @@
 
 	return data
 
-def accumulate_values_into_parents(accounts, accounts_by_name, dimension_items_list):
+
+def accumulate_values_into_parents(accounts, accounts_by_name, dimension_list):
 	"""accumulate children's values in parent accounts"""
 	for d in reversed(accounts):
 		if d.parent_account:
-			for item in dimension_items_list:
-				accounts_by_name[d.parent_account][frappe.scrub(item)] = \
-					accounts_by_name[d.parent_account].get(frappe.scrub(item), 0.0) + d.get(frappe.scrub(item), 0.0)
+			for dimension in dimension_list:
+				accounts_by_name[d.parent_account][frappe.scrub(dimension)] = accounts_by_name[
+					d.parent_account
+				].get(frappe.scrub(dimension), 0.0) + d.get(frappe.scrub(dimension), 0.0)
 
-def get_condition(from_date, item, dimension):
+
+def get_condition(dimension):
 	conditions = []
 
-	if from_date:
-		conditions.append("posting_date >= %(from_date)s")
-	if dimension:
-		if dimension not in ['Cost Center', 'Project']:
-			if dimension in ['Customer', 'Supplier']:
-				dimension = 'Party'
-			else:
-				dimension = 'Voucher No'
-		txt = "{0} = %(item)s".format(frappe.scrub(dimension))
-		conditions.append(txt)
+	conditions.append("{0} in %(dimensions)s".format(frappe.scrub(dimension)))
 
 	return " and {}".format(" and ".join(conditions)) if conditions else ""
 
-def get_dimension_items_list(dimension, company):
-	meta = frappe.get_meta(dimension, cached=False)
-	fieldnames = [d.fieldname for d in meta.get("fields")]
-	filters = {}
-	if 'company' in fieldnames:
-		filters['company'] = company
-	return frappe.get_all(dimension, filters, as_list=True)
 
-def get_columns(dimension_items_list, accumulated_values=1, company=None):
-	columns = [{
-		"fieldname": "account",
-		"label": _("Account"),
-		"fieldtype": "Link",
-		"options": "Account",
-		"width": 300
-	}]
-	if company:
-		columns.append({
+def get_dimensions(filters):
+	meta = frappe.get_meta(filters.get("dimension"), cached=False)
+	query_filters = {}
+
+	if meta.has_field("company"):
+		query_filters = {"company": filters.get("company")}
+
+	return frappe.get_all(filters.get("dimension"), filters=query_filters, pluck="name")
+
+
+def get_columns(dimension_list):
+	columns = [
+		{
+			"fieldname": "account",
+			"label": _("Account"),
+			"fieldtype": "Link",
+			"options": "Account",
+			"width": 300,
+		},
+		{
 			"fieldname": "currency",
 			"label": _("Currency"),
 			"fieldtype": "Link",
 			"options": "Currency",
-			"hidden": 1
-		})
-	for item in dimension_items_list:
-		columns.append({
-			"fieldname": frappe.scrub(item),
-			"label": item,
-			"fieldtype": "Currency",
-			"options": "currency",
-			"width": 150
-		})
-	columns.append({
+			"hidden": 1,
+		},
+	]
+
+	for dimension in dimension_list:
+		columns.append(
+			{
+				"fieldname": frappe.scrub(dimension),
+				"label": dimension,
+				"fieldtype": "Currency",
+				"options": "currency",
+				"width": 150,
+			}
+		)
+
+	columns.append(
+		{
 			"fieldname": "total",
-			"label": "Total",
+			"label": _("Total"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 150
-		})
+			"width": 150,
+		}
+	)
 
 	return columns
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index db28cdf..3682577 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -18,12 +18,22 @@
 from erpnext.accounts.utils import get_fiscal_year
 
 
-def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_end_date, filter_based_on, periodicity, accumulated_values=False,
-	company=None, reset_period_on_fy_change=True, ignore_fiscal_year=False):
+def get_period_list(
+	from_fiscal_year,
+	to_fiscal_year,
+	period_start_date,
+	period_end_date,
+	filter_based_on,
+	periodicity,
+	accumulated_values=False,
+	company=None,
+	reset_period_on_fy_change=True,
+	ignore_fiscal_year=False,
+):
 	"""Get a list of dict {"from_date": from_date, "to_date": to_date, "key": key, "label": label}
-		Periodicity can be (Yearly, Quarterly, Monthly)"""
+	Periodicity can be (Yearly, Quarterly, Monthly)"""
 
-	if filter_based_on == 'Fiscal Year':
+	if filter_based_on == "Fiscal Year":
 		fiscal_year = get_fiscal_year_data(from_fiscal_year, to_fiscal_year)
 		validate_fiscal_year(fiscal_year, from_fiscal_year, to_fiscal_year)
 		year_start_date = getdate(fiscal_year.year_start_date)
@@ -33,12 +43,7 @@
 		year_start_date = getdate(period_start_date)
 		year_end_date = getdate(period_end_date)
 
-	months_to_add = {
-		"Yearly": 12,
-		"Half-Yearly": 6,
-		"Quarterly": 3,
-		"Monthly": 1
-	}[periodicity]
+	months_to_add = {"Yearly": 12, "Half-Yearly": 6, "Quarterly": 3, "Monthly": 1}[periodicity]
 
 	period_list = []
 
@@ -46,11 +51,9 @@
 	months = get_months(year_start_date, year_end_date)
 
 	for i in range(cint(math.ceil(months / months_to_add))):
-		period = frappe._dict({
-			"from_date": start_date
-		})
+		period = frappe._dict({"from_date": start_date})
 
-		if i==0 and filter_based_on == 'Date Range':
+		if i == 0 and filter_based_on == "Date Range":
 			to_date = add_months(get_first_day(start_date), months_to_add)
 		else:
 			to_date = add_months(start_date, months_to_add)
@@ -90,32 +93,38 @@
 				else:
 					label = get_label(periodicity, period_list[0].from_date, opts["to_date"])
 
-		opts.update({
-			"key": key.replace(" ", "_").replace("-", "_"),
-			"label": label,
-			"year_start_date": year_start_date,
-			"year_end_date": year_end_date
-		})
+		opts.update(
+			{
+				"key": key.replace(" ", "_").replace("-", "_"),
+				"label": label,
+				"year_start_date": year_start_date,
+				"year_end_date": year_end_date,
+			}
+		)
 
 	return period_list
 
 
 def get_fiscal_year_data(from_fiscal_year, to_fiscal_year):
-	fiscal_year = frappe.db.sql("""select min(year_start_date) as year_start_date,
+	fiscal_year = frappe.db.sql(
+		"""select min(year_start_date) as year_start_date,
 		max(year_end_date) as year_end_date from `tabFiscal Year` where
 		name between %(from_fiscal_year)s and %(to_fiscal_year)s""",
-		{'from_fiscal_year': from_fiscal_year, 'to_fiscal_year': to_fiscal_year}, as_dict=1)
+		{"from_fiscal_year": from_fiscal_year, "to_fiscal_year": to_fiscal_year},
+		as_dict=1,
+	)
 
 	return fiscal_year[0] if fiscal_year else {}
 
 
 def validate_fiscal_year(fiscal_year, from_fiscal_year, to_fiscal_year):
-	if not fiscal_year.get('year_start_date') or not fiscal_year.get('year_end_date'):
+	if not fiscal_year.get("year_start_date") or not fiscal_year.get("year_end_date"):
 		frappe.throw(_("Start Year and End Year are mandatory"))
 
-	if getdate(fiscal_year.get('year_end_date')) < getdate(fiscal_year.get('year_start_date')):
+	if getdate(fiscal_year.get("year_end_date")) < getdate(fiscal_year.get("year_start_date")):
 		frappe.throw(_("End Year cannot be before Start Year"))
 
+
 def validate_dates(from_date, to_date):
 	if not from_date or not to_date:
 		frappe.throw(_("From Date and To Date are mandatory"))
@@ -123,6 +132,7 @@
 	if to_date < from_date:
 		frappe.throw(_("To Date cannot be less than From Date"))
 
+
 def get_months(start_date, end_date):
 	diff = (12 * end_date.year + end_date.month) - (12 * start_date.year + start_date.month)
 	return diff + 1
@@ -141,9 +151,17 @@
 
 
 def get_data(
-		company, root_type, balance_must_be, period_list, filters=None,
-		accumulated_values=1, only_current_fiscal_year=True, ignore_closing_entries=False,
-		ignore_accumulated_values_for_fy=False , total = True):
+	company,
+	root_type,
+	balance_must_be,
+	period_list,
+	filters=None,
+	accumulated_values=1,
+	only_current_fiscal_year=True,
+	ignore_closing_entries=False,
+	ignore_accumulated_values_for_fy=False,
+	total=True,
+):
 
 	accounts = get_accounts(company, root_type)
 	if not accounts:
@@ -154,19 +172,31 @@
 	company_currency = get_appropriate_currency(company, filters)
 
 	gl_entries_by_account = {}
-	for root in frappe.db.sql("""select lft, rgt from tabAccount
-			where root_type=%s and ifnull(parent_account, '') = ''""", root_type, as_dict=1):
+	for root in frappe.db.sql(
+		"""select lft, rgt from tabAccount
+			where root_type=%s and ifnull(parent_account, '') = ''""",
+		root_type,
+		as_dict=1,
+	):
 
 		set_gl_entries_by_account(
 			company,
 			period_list[0]["year_start_date"] if only_current_fiscal_year else None,
 			period_list[-1]["to_date"],
-			root.lft, root.rgt, filters,
-			gl_entries_by_account, ignore_closing_entries=ignore_closing_entries
+			root.lft,
+			root.rgt,
+			filters,
+			gl_entries_by_account,
+			ignore_closing_entries=ignore_closing_entries,
 		)
 
 	calculate_values(
-		accounts_by_name, gl_entries_by_account, period_list, accumulated_values, ignore_accumulated_values_for_fy)
+		accounts_by_name,
+		gl_entries_by_account,
+		period_list,
+		accumulated_values,
+		ignore_accumulated_values_for_fy,
+	)
 	accumulate_values_into_parents(accounts, accounts_by_name, period_list)
 	out = prepare_data(accounts, balance_must_be, period_list, company_currency)
 	out = filter_out_zero_value_rows(out, parent_children_map)
@@ -181,26 +211,32 @@
 	if filters and filters.get("presentation_currency"):
 		return filters["presentation_currency"]
 	else:
-		return frappe.get_cached_value('Company',  company,  "default_currency")
+		return frappe.get_cached_value("Company", company, "default_currency")
 
 
 def calculate_values(
-		accounts_by_name, gl_entries_by_account, period_list, accumulated_values, ignore_accumulated_values_for_fy):
+	accounts_by_name,
+	gl_entries_by_account,
+	period_list,
+	accumulated_values,
+	ignore_accumulated_values_for_fy,
+):
 	for entries in gl_entries_by_account.values():
 		for entry in entries:
 			d = accounts_by_name.get(entry.account)
 			if not d:
 				frappe.msgprint(
-					_("Could not retrieve information for {0}.").format(entry.account), title="Error",
-					raise_exception=1
+					_("Could not retrieve information for {0}.").format(entry.account),
+					title="Error",
+					raise_exception=1,
 				)
 			for period in period_list:
 				# check if posting date is within the period
 
 				if entry.posting_date <= period.to_date:
-					if (accumulated_values or entry.posting_date >= period.from_date) and \
-						(not ignore_accumulated_values_for_fy or
-							entry.fiscal_year == period.to_date_fiscal_year):
+					if (accumulated_values or entry.posting_date >= period.from_date) and (
+						not ignore_accumulated_values_for_fy or entry.fiscal_year == period.to_date_fiscal_year
+					):
 						d[period.key] = d.get(period.key, 0.0) + flt(entry.debit) - flt(entry.credit)
 
 			if entry.posting_date < period_list[0].year_start_date:
@@ -212,11 +248,13 @@
 	for d in reversed(accounts):
 		if d.parent_account:
 			for period in period_list:
-				accounts_by_name[d.parent_account][period.key] = \
-					accounts_by_name[d.parent_account].get(period.key, 0.0) + d.get(period.key, 0.0)
+				accounts_by_name[d.parent_account][period.key] = accounts_by_name[d.parent_account].get(
+					period.key, 0.0
+				) + d.get(period.key, 0.0)
 
-			accounts_by_name[d.parent_account]["opening_balance"] = \
-				accounts_by_name[d.parent_account].get("opening_balance", 0.0) + d.get("opening_balance", 0.0)
+			accounts_by_name[d.parent_account]["opening_balance"] = accounts_by_name[d.parent_account].get(
+				"opening_balance", 0.0
+			) + d.get("opening_balance", 0.0)
 
 
 def prepare_data(accounts, balance_must_be, period_list, company_currency):
@@ -228,20 +266,25 @@
 		# add to output
 		has_value = False
 		total = 0
-		row = frappe._dict({
-			"account": _(d.name),
-			"parent_account": _(d.parent_account) if d.parent_account else '',
-			"indent": flt(d.indent),
-			"year_start_date": year_start_date,
-			"year_end_date": year_end_date,
-			"currency": company_currency,
-			"include_in_gross": d.include_in_gross,
-			"account_type": d.account_type,
-			"is_group": d.is_group,
-			"opening_balance": d.get("opening_balance", 0.0) * (1 if balance_must_be=="Debit" else -1),
-			"account_name": ('%s - %s' %(_(d.account_number), _(d.account_name))
-				if d.account_number else _(d.account_name))
-		})
+		row = frappe._dict(
+			{
+				"account": _(d.name),
+				"parent_account": _(d.parent_account) if d.parent_account else "",
+				"indent": flt(d.indent),
+				"year_start_date": year_start_date,
+				"year_end_date": year_end_date,
+				"currency": company_currency,
+				"include_in_gross": d.include_in_gross,
+				"account_type": d.account_type,
+				"is_group": d.is_group,
+				"opening_balance": d.get("opening_balance", 0.0) * (1 if balance_must_be == "Debit" else -1),
+				"account_name": (
+					"%s - %s" % (_(d.account_number), _(d.account_name))
+					if d.account_number
+					else _(d.account_name)
+				),
+			}
+		)
 		for period in period_list:
 			if d.get(period.key) and balance_must_be == "Credit":
 				# change sign based on Debit or Credit, since calculation is done using (debit - credit)
@@ -283,7 +326,7 @@
 		"account_name": _("Total {0} ({1})").format(_(root_type), _(balance_must_be)),
 		"account": _("Total {0} ({1})").format(_(root_type), _(balance_must_be)),
 		"currency": company_currency,
-		"opening_balance": 0.0
+		"opening_balance": 0.0,
 	}
 
 	for row in out:
@@ -306,10 +349,14 @@
 
 
 def get_accounts(company, root_type):
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select name, account_number, parent_account, lft, rgt, root_type, report_type, account_name, include_in_gross, account_type, is_group, lft, rgt
 		from `tabAccount`
-		where company=%s and root_type=%s order by lft""", (company, root_type), as_dict=True)
+		where company=%s and root_type=%s order by lft""",
+		(company, root_type),
+		as_dict=True,
+	)
 
 
 def filter_accounts(accounts, depth=20):
@@ -324,7 +371,7 @@
 	def add_to_list(parent, level):
 		if level < depth:
 			children = parent_children_map.get(parent) or []
-			sort_accounts(children, is_root=True if parent==None else False)
+			sort_accounts(children, is_root=True if parent == None else False)
 
 			for child in children:
 				child.indent = level
@@ -340,7 +387,7 @@
 	"""Sort root types as Asset, Liability, Equity, Income, Expense"""
 
 	def compare_accounts(a, b):
-		if re.split(r'\W+', a[key])[0].isdigit():
+		if re.split(r"\W+", a[key])[0].isdigit():
 			# if chart of accounts is numbered, then sort by number
 			return int(a[key] > b[key]) - int(a[key] < b[key])
 		elif is_root:
@@ -357,50 +404,64 @@
 			return int(a[key] > b[key]) - int(a[key] < b[key])
 		return 1
 
-	accounts.sort(key = functools.cmp_to_key(compare_accounts))
+	accounts.sort(key=functools.cmp_to_key(compare_accounts))
+
 
 def set_gl_entries_by_account(
-		company, from_date, to_date, root_lft, root_rgt, filters, gl_entries_by_account, ignore_closing_entries=False):
+	company,
+	from_date,
+	to_date,
+	root_lft,
+	root_rgt,
+	filters,
+	gl_entries_by_account,
+	ignore_closing_entries=False,
+):
 	"""Returns a dict like { "account": [gl entries], ... }"""
 
 	additional_conditions = get_additional_conditions(from_date, ignore_closing_entries, filters)
 
-	accounts = frappe.db.sql_list("""select name from `tabAccount`
-		where lft >= %s and rgt <= %s and company = %s""", (root_lft, root_rgt, company))
+	accounts = frappe.db.sql_list(
+		"""select name from `tabAccount`
+		where lft >= %s and rgt <= %s and company = %s""",
+		(root_lft, root_rgt, company),
+	)
 
 	if accounts:
-		additional_conditions += " and account in ({})"\
-			.format(", ".join(frappe.db.escape(d) for d in accounts))
+		additional_conditions += " and account in ({})".format(
+			", ".join(frappe.db.escape(d) for d in accounts)
+		)
 
 		gl_filters = {
 			"company": company,
 			"from_date": from_date,
 			"to_date": to_date,
-			"finance_book": cstr(filters.get("finance_book"))
+			"finance_book": cstr(filters.get("finance_book")),
 		}
 
 		if filters.get("include_default_book_entries"):
-			gl_filters["company_fb"] = frappe.db.get_value("Company",
-				company, 'default_finance_book')
+			gl_filters["company_fb"] = frappe.db.get_value("Company", company, "default_finance_book")
 
 		for key, value in filters.items():
 			if value:
-				gl_filters.update({
-					key: value
-				})
+				gl_filters.update({key: value})
 
-		gl_entries = frappe.db.sql("""
+		gl_entries = frappe.db.sql(
+			"""
 			select posting_date, account, debit, credit, is_opening, fiscal_year,
 				debit_in_account_currency, credit_in_account_currency, account_currency from `tabGL Entry`
 			where company=%(company)s
 			{additional_conditions}
 			and posting_date <= %(to_date)s
 			and is_cancelled = 0""".format(
-			additional_conditions=additional_conditions), gl_filters, as_dict=True
+				additional_conditions=additional_conditions
+			),
+			gl_filters,
+			as_dict=True,
 		)
 
-		if filters and filters.get('presentation_currency'):
-			convert_to_presentation_currency(gl_entries, get_currency(filters), filters.get('company'))
+		if filters and filters.get("presentation_currency"):
+			convert_to_presentation_currency(gl_entries, get_currency(filters), filters.get("company"))
 
 		for entry in gl_entries:
 			gl_entries_by_account.setdefault(entry.account, []).append(entry)
@@ -431,25 +492,29 @@
 			additional_conditions.append("cost_center in %(cost_center)s")
 
 		if filters.get("include_default_book_entries"):
-			additional_conditions.append("(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)")
+			additional_conditions.append(
+				"(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)"
+			)
 		else:
 			additional_conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)")
 
 	if accounting_dimensions:
 		for dimension in accounting_dimensions:
 			if filters.get(dimension.fieldname):
-				if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
-					filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
-						filters.get(dimension.fieldname))
+				if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"):
+					filters[dimension.fieldname] = get_dimension_with_children(
+						dimension.document_type, filters.get(dimension.fieldname)
+					)
 					additional_conditions.append("{0} in %({0})s".format(dimension.fieldname))
 				else:
-					additional_conditions.append("{0} in (%({0})s)".format(dimension.fieldname))
+					additional_conditions.append("{0} in %({0})s".format(dimension.fieldname))
 
 	return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else ""
 
+
 def get_cost_centers_with_children(cost_centers):
 	if not isinstance(cost_centers, list):
-		cost_centers = [d.strip() for d in cost_centers.strip().split(',') if d]
+		cost_centers = [d.strip() for d in cost_centers.strip().split(",") if d]
 
 	all_cost_centers = []
 	for d in cost_centers:
@@ -462,45 +527,50 @@
 
 	return list(set(all_cost_centers))
 
+
 def get_columns(periodicity, period_list, accumulated_values=1, company=None):
-	columns = [{
-		"fieldname": "account",
-		"label": _("Account"),
-		"fieldtype": "Link",
-		"options": "Account",
-		"width": 300
-	}]
-	if company:
-		columns.append({
-			"fieldname": "currency",
-			"label": _("Currency"),
+	columns = [
+		{
+			"fieldname": "account",
+			"label": _("Account"),
 			"fieldtype": "Link",
-			"options": "Currency",
-			"hidden": 1
-		})
+			"options": "Account",
+			"width": 300,
+		}
+	]
+	if company:
+		columns.append(
+			{
+				"fieldname": "currency",
+				"label": _("Currency"),
+				"fieldtype": "Link",
+				"options": "Currency",
+				"hidden": 1,
+			}
+		)
 	for period in period_list:
-		columns.append({
-			"fieldname": period.key,
-			"label": period.label,
-			"fieldtype": "Currency",
-			"options": "currency",
-			"width": 150
-		})
-	if periodicity!="Yearly":
-		if not accumulated_values:
-			columns.append({
-				"fieldname": "total",
-				"label": _("Total"),
+		columns.append(
+			{
+				"fieldname": period.key,
+				"label": period.label,
 				"fieldtype": "Currency",
-				"width": 150
-			})
+				"options": "currency",
+				"width": 150,
+			}
+		)
+	if periodicity != "Yearly":
+		if not accumulated_values:
+			columns.append(
+				{"fieldname": "total", "label": _("Total"), "fieldtype": "Currency", "width": 150}
+			)
 
 	return columns
 
+
 def get_filtered_list_for_consolidated_report(filters, period_list):
 	filtered_summary_list = []
 	for period in period_list:
-		if period == filters.get('company'):
+		if period == filters.get("company"):
 			filtered_summary_list.append(period)
 
 	return filtered_summary_list
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 4ff0297..e4b561e 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -20,20 +20,20 @@
 # to cache translations
 TRANSLATIONS = frappe._dict()
 
+
 def execute(filters=None):
 	if not filters:
 		return [], []
 
 	account_details = {}
 
-	if filters and filters.get('print_in_account_currency') and \
-		not filters.get('account'):
+	if filters and filters.get("print_in_account_currency") and not filters.get("account"):
 		frappe.throw(_("Select an account to print in account currency"))
 
 	for acc in frappe.db.sql("""select name, is_group from tabAccount""", as_dict=1):
 		account_details.setdefault(acc.name, acc)
 
-	if filters.get('party'):
+	if filters.get("party"):
 		filters.party = frappe.parse_json(filters.get("party"))
 
 	validate_filters(filters, account_details)
@@ -50,46 +50,45 @@
 
 	return columns, res
 
+
 def update_translations():
 	TRANSLATIONS.update(
-		dict(
-			OPENING = _('Opening'),
-			TOTAL = _('Total'),
-			CLOSING_TOTAL = _('Closing (Opening + Total)')
-		)
+		dict(OPENING=_("Opening"), TOTAL=_("Total"), CLOSING_TOTAL=_("Closing (Opening + Total)"))
 	)
 
+
 def validate_filters(filters, account_details):
 	if not filters.get("company"):
 		frappe.throw(_("{0} is mandatory").format(_("Company")))
 
 	if not filters.get("from_date") and not filters.get("to_date"):
-		frappe.throw(_("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date"))))
+		frappe.throw(
+			_("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date")))
+		)
 
-	if filters.get('account'):
-		filters.account = frappe.parse_json(filters.get('account'))
+	if filters.get("account"):
+		filters.account = frappe.parse_json(filters.get("account"))
 		for account in filters.account:
 			if not account_details.get(account):
 				frappe.throw(_("Account {0} does not exists").format(account))
 
-	if (filters.get("account") and filters.get("group_by") == 'Group by Account'):
-		filters.account = frappe.parse_json(filters.get('account'))
+	if filters.get("account") and filters.get("group_by") == "Group by Account":
+		filters.account = frappe.parse_json(filters.get("account"))
 		for account in filters.account:
 			if account_details[account].is_group == 0:
 				frappe.throw(_("Can not filter based on Child Account, if grouped by Account"))
 
-	if (filters.get("voucher_no")
-		and filters.get("group_by") in ['Group by Voucher']):
+	if filters.get("voucher_no") and filters.get("group_by") in ["Group by Voucher"]:
 		frappe.throw(_("Can not filter based on Voucher No, if grouped by Voucher"))
 
 	if filters.from_date > filters.to_date:
 		frappe.throw(_("From Date must be before To Date"))
 
-	if filters.get('project'):
-		filters.project = frappe.parse_json(filters.get('project'))
+	if filters.get("project"):
+		filters.project = frappe.parse_json(filters.get("project"))
 
-	if filters.get('cost_center'):
-		filters.cost_center = frappe.parse_json(filters.get('cost_center'))
+	if filters.get("cost_center"):
+		filters.cost_center = frappe.parse_json(filters.get("cost_center"))
 
 
 def validate_party(filters):
@@ -100,9 +99,12 @@
 			if not frappe.db.exists(party_type, d):
 				frappe.throw(_("Invalid {0}: {1}").format(party_type, d))
 
+
 def set_account_currency(filters):
-	if filters.get("account") or (filters.get('party') and len(filters.party) == 1):
-		filters["company_currency"] = frappe.get_cached_value('Company',  filters.company,  "default_currency")
+	if filters.get("account") or (filters.get("party") and len(filters.party) == 1):
+		filters["company_currency"] = frappe.get_cached_value(
+			"Company", filters.company, "default_currency"
+		)
 		account_currency = None
 
 		if filters.get("account"):
@@ -121,17 +123,19 @@
 
 		elif filters.get("party"):
 			gle_currency = frappe.db.get_value(
-				"GL Entry", {
-					"party_type": filters.party_type, "party": filters.party[0], "company": filters.company
-				},
-				"account_currency"
+				"GL Entry",
+				{"party_type": filters.party_type, "party": filters.party[0], "company": filters.company},
+				"account_currency",
 			)
 
 			if gle_currency:
 				account_currency = gle_currency
 			else:
-				account_currency = (None if filters.party_type in ["Employee", "Student", "Shareholder", "Member"] else
-					frappe.db.get_value(filters.party_type, filters.party[0], "default_currency"))
+				account_currency = (
+					None
+					if filters.party_type in ["Employee", "Shareholder", "Member"]
+					else frappe.db.get_value(filters.party_type, filters.party[0], "default_currency")
+				)
 
 		filters["account_currency"] = account_currency or filters.company_currency
 		if filters.account_currency != filters.company_currency and not filters.presentation_currency:
@@ -139,6 +143,7 @@
 
 	return filters
 
+
 def get_result(filters, account_details):
 	accounting_dimensions = []
 	if filters.get("include_dimensions"):
@@ -146,13 +151,13 @@
 
 	gl_entries = get_gl_entries(filters, accounting_dimensions)
 
-	data = get_data_with_opening_closing(filters, account_details,
-		accounting_dimensions, gl_entries)
+	data = get_data_with_opening_closing(filters, account_details, accounting_dimensions, gl_entries)
 
 	result = get_result_as_list(data, filters)
 
 	return result
 
+
 def get_gl_entries(filters, accounting_dimensions):
 	currency_map = get_currency(filters)
 	select_fields = """, debit, credit, debit_in_account_currency,
@@ -169,14 +174,16 @@
 		order_by_statement = "order by account, posting_date, creation"
 
 	if filters.get("include_default_book_entries"):
-		filters['company_fb'] = frappe.db.get_value("Company",
-			filters.get("company"), 'default_finance_book')
+		filters["company_fb"] = frappe.db.get_value(
+			"Company", filters.get("company"), "default_finance_book"
+		)
 
 	dimension_fields = ""
 	if accounting_dimensions:
-		dimension_fields = ', '.join(accounting_dimensions) + ','
+		dimension_fields = ", ".join(accounting_dimensions) + ","
 
-	gl_entries = frappe.db.sql("""
+	gl_entries = frappe.db.sql(
+		"""
 		select
 			name as gl_entry, posting_date, account, party_type, party,
 			voucher_type, voucher_no, {dimension_fields}
@@ -187,12 +194,17 @@
 		where company=%(company)s {conditions}
 		{order_by_statement}
 	""".format(
-		dimension_fields=dimension_fields, select_fields=select_fields,
-		conditions=get_conditions(filters), order_by_statement=order_by_statement
-	), filters, as_dict=1)
+			dimension_fields=dimension_fields,
+			select_fields=select_fields,
+			conditions=get_conditions(filters),
+			order_by_statement=order_by_statement,
+		),
+		filters,
+		as_dict=1,
+	)
 
-	if filters.get('presentation_currency'):
-		return convert_to_presentation_currency(gl_entries, currency_map, filters.get('company'))
+	if filters.get("presentation_currency"):
+		return convert_to_presentation_currency(gl_entries, currency_map, filters.get("company"))
 	else:
 		return gl_entries
 
@@ -220,8 +232,11 @@
 	if filters.get("party"):
 		conditions.append("party in %(party)s")
 
-	if not (filters.get("account") or filters.get("party") or
-		filters.get("group_by") in ["Group by Account", "Group by Party"]):
+	if not (
+		filters.get("account")
+		or filters.get("party")
+		or filters.get("group_by") in ["Group by Account", "Group by Party"]
+	):
 		conditions.append("posting_date >=%(from_date)s")
 
 	conditions.append("(posting_date <=%(to_date)s or is_opening = 'Yes')")
@@ -231,7 +246,9 @@
 
 	if filters.get("finance_book"):
 		if filters.get("include_default_book_entries"):
-			conditions.append("(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)")
+			conditions.append(
+				"(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)"
+			)
 		else:
 			conditions.append("finance_book in (%(finance_book)s)")
 
@@ -239,6 +256,7 @@
 		conditions.append("is_cancelled = 0")
 
 	from frappe.desk.reportview import build_match_conditions
+
 	match_conditions = build_match_conditions("GL Entry")
 
 	if match_conditions:
@@ -251,18 +269,20 @@
 			for dimension in accounting_dimensions:
 				if not dimension.disabled:
 					if filters.get(dimension.fieldname):
-						if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
-							filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
-								filters.get(dimension.fieldname))
+						if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"):
+							filters[dimension.fieldname] = get_dimension_with_children(
+								dimension.document_type, filters.get(dimension.fieldname)
+							)
 							conditions.append("{0} in %({0})s".format(dimension.fieldname))
 						else:
-							conditions.append("{0} in (%({0})s)".format(dimension.fieldname))
+							conditions.append("{0} in %({0})s".format(dimension.fieldname))
 
 	return "and {}".format(" and ".join(conditions)) if conditions else ""
 
+
 def get_accounts_with_children(accounts):
 	if not isinstance(accounts, list):
-		accounts = [d.strip() for d in accounts.strip().split(',') if d]
+		accounts = [d.strip() for d in accounts.strip().split(",") if d]
 
 	all_accounts = []
 	for d in accounts:
@@ -275,6 +295,7 @@
 
 	return list(set(all_accounts))
 
+
 def get_data_with_opening_closing(filters, account_details, accounting_dimensions, gl_entries):
 	data = []
 
@@ -285,7 +306,7 @@
 	# Opening for filtered account
 	data.append(totals.opening)
 
-	if filters.get("group_by") != 'Group by Voucher (Consolidated)':
+	if filters.get("group_by") != "Group by Voucher (Consolidated)":
 		for acc, acc_dict in gle_map.items():
 			# acc
 			if acc_dict.entries:
@@ -314,6 +335,7 @@
 
 	return data
 
+
 def get_totals_dict():
 	def _get_debit_credit_dict(label):
 		return _dict(
@@ -321,25 +343,28 @@
 			debit=0.0,
 			credit=0.0,
 			debit_in_account_currency=0.0,
-			credit_in_account_currency=0.0
+			credit_in_account_currency=0.0,
 		)
+
 	return _dict(
-		opening = _get_debit_credit_dict(TRANSLATIONS.OPENING),
-		total = _get_debit_credit_dict(TRANSLATIONS.TOTAL),
-		closing = _get_debit_credit_dict(TRANSLATIONS.CLOSING_TOTAL)
+		opening=_get_debit_credit_dict(TRANSLATIONS.OPENING),
+		total=_get_debit_credit_dict(TRANSLATIONS.TOTAL),
+		closing=_get_debit_credit_dict(TRANSLATIONS.CLOSING_TOTAL),
 	)
 
+
 def group_by_field(group_by):
-	if group_by == 'Group by Party':
-		return 'party'
-	elif group_by in ['Group by Voucher (Consolidated)', 'Group by Account']:
-		return 'account'
+	if group_by == "Group by Party":
+		return "party"
+	elif group_by in ["Group by Voucher (Consolidated)", "Group by Account"]:
+		return "account"
 	else:
-		return 'voucher_no'
+		return "voucher_no"
+
 
 def initialize_gle_map(gl_entries, filters):
 	gle_map = OrderedDict()
-	group_by = group_by_field(filters.get('group_by'))
+	group_by = group_by_field(filters.get("group_by"))
 
 	for gle in gl_entries:
 		gle_map.setdefault(gle.get(group_by), _dict(totals=get_totals_dict(), entries=[]))
@@ -350,11 +375,11 @@
 	totals = get_totals_dict()
 	entries = []
 	consolidated_gle = OrderedDict()
-	group_by = group_by_field(filters.get('group_by'))
-	group_by_voucher_consolidated = filters.get("group_by") == 'Group by Voucher (Consolidated)'
+	group_by = group_by_field(filters.get("group_by"))
+	group_by_voucher_consolidated = filters.get("group_by") == "Group by Voucher (Consolidated)"
 
-	if filters.get('show_net_values_in_party_account'):
-		account_type_map = get_account_type_map(filters.get('company'))
+	if filters.get("show_net_values_in_party_account"):
+		account_type_map = get_account_type_map(filters.get("company"))
 
 	def update_value_in_dict(data, key, gle):
 		data[key].debit += gle.debit
@@ -363,26 +388,28 @@
 		data[key].debit_in_account_currency += gle.debit_in_account_currency
 		data[key].credit_in_account_currency += gle.credit_in_account_currency
 
-		if filters.get('show_net_values_in_party_account') and \
-			account_type_map.get(data[key].account) in ('Receivable', 'Payable'):
+		if filters.get("show_net_values_in_party_account") and account_type_map.get(
+			data[key].account
+		) in ("Receivable", "Payable"):
 			net_value = data[key].debit - data[key].credit
-			net_value_in_account_currency = data[key].debit_in_account_currency \
-				- data[key].credit_in_account_currency
+			net_value_in_account_currency = (
+				data[key].debit_in_account_currency - data[key].credit_in_account_currency
+			)
 
 			if net_value < 0:
-				dr_or_cr = 'credit'
-				rev_dr_or_cr = 'debit'
+				dr_or_cr = "credit"
+				rev_dr_or_cr = "debit"
 			else:
-				dr_or_cr = 'debit'
-				rev_dr_or_cr = 'credit'
+				dr_or_cr = "debit"
+				rev_dr_or_cr = "credit"
 
 			data[key][dr_or_cr] = abs(net_value)
-			data[key][dr_or_cr+'_in_account_currency'] = abs(net_value_in_account_currency)
+			data[key][dr_or_cr + "_in_account_currency"] = abs(net_value_in_account_currency)
 			data[key][rev_dr_or_cr] = 0
-			data[key][rev_dr_or_cr+'_in_account_currency'] = 0
+			data[key][rev_dr_or_cr + "_in_account_currency"] = 0
 
 		if data[key].against_voucher and gle.against_voucher:
-			data[key].against_voucher += ', ' + gle.against_voucher
+			data[key].against_voucher += ", " + gle.against_voucher
 
 	from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
 	show_opening_entries = filters.get("show_opening_entries")
@@ -390,25 +417,31 @@
 	for gle in gl_entries:
 		group_by_value = gle.get(group_by)
 
-		if (gle.posting_date < from_date or (cstr(gle.is_opening) == "Yes" and not show_opening_entries)):
+		if gle.posting_date < from_date or (cstr(gle.is_opening) == "Yes" and not show_opening_entries):
 			if not group_by_voucher_consolidated:
-				update_value_in_dict(gle_map[group_by_value].totals, 'opening', gle)
-				update_value_in_dict(gle_map[group_by_value].totals, 'closing', gle)
+				update_value_in_dict(gle_map[group_by_value].totals, "opening", gle)
+				update_value_in_dict(gle_map[group_by_value].totals, "closing", gle)
 
-			update_value_in_dict(totals, 'opening', gle)
-			update_value_in_dict(totals, 'closing', gle)
+			update_value_in_dict(totals, "opening", gle)
+			update_value_in_dict(totals, "closing", gle)
 
 		elif gle.posting_date <= to_date:
 			if not group_by_voucher_consolidated:
-				update_value_in_dict(gle_map[group_by_value].totals, 'total', gle)
-				update_value_in_dict(gle_map[group_by_value].totals, 'closing', gle)
-				update_value_in_dict(totals, 'total', gle)
-				update_value_in_dict(totals, 'closing', gle)
+				update_value_in_dict(gle_map[group_by_value].totals, "total", gle)
+				update_value_in_dict(gle_map[group_by_value].totals, "closing", gle)
+				update_value_in_dict(totals, "total", gle)
+				update_value_in_dict(totals, "closing", gle)
 
 				gle_map[group_by_value].entries.append(gle)
 
 			elif group_by_voucher_consolidated:
-				keylist = [gle.get("voucher_type"), gle.get("voucher_no"), gle.get("account")]
+				keylist = [
+					gle.get("voucher_type"),
+					gle.get("voucher_no"),
+					gle.get("account"),
+					gle.get("party_type"),
+					gle.get("party"),
+				]
 				if filters.get("include_dimensions"):
 					for dim in accounting_dimensions:
 						keylist.append(gle.get(dim))
@@ -421,47 +454,58 @@
 					update_value_in_dict(consolidated_gle, key, gle)
 
 	for key, value in consolidated_gle.items():
-		update_value_in_dict(totals, 'total', value)
-		update_value_in_dict(totals, 'closing', value)
+		update_value_in_dict(totals, "total", value)
+		update_value_in_dict(totals, "closing", value)
 		entries.append(value)
 
 	return totals, entries
 
+
 def get_account_type_map(company):
-	account_type_map = frappe._dict(frappe.get_all('Account', fields=['name', 'account_type'],
-		filters={'company': company}, as_list=1))
+	account_type_map = frappe._dict(
+		frappe.get_all(
+			"Account", fields=["name", "account_type"], filters={"company": company}, as_list=1
+		)
+	)
 
 	return account_type_map
 
+
 def get_result_as_list(data, filters):
 	balance, balance_in_account_currency = 0, 0
 	inv_details = get_supplier_invoice_details()
 
 	for d in data:
-		if not d.get('posting_date'):
+		if not d.get("posting_date"):
 			balance, balance_in_account_currency = 0, 0
 
-		balance = get_balance(d, balance, 'debit', 'credit')
-		d['balance'] = balance
+		balance = get_balance(d, balance, "debit", "credit")
+		d["balance"] = balance
 
-		d['account_currency'] = filters.account_currency
-		d['bill_no'] = inv_details.get(d.get('against_voucher'), '')
+		d["account_currency"] = filters.account_currency
+		d["bill_no"] = inv_details.get(d.get("against_voucher"), "")
 
 	return data
 
+
 def get_supplier_invoice_details():
 	inv_details = {}
-	for d in frappe.db.sql(""" select name, bill_no from `tabPurchase Invoice`
-		where docstatus = 1 and bill_no is not null and bill_no != '' """, as_dict=1):
+	for d in frappe.db.sql(
+		""" select name, bill_no from `tabPurchase Invoice`
+		where docstatus = 1 and bill_no is not null and bill_no != '' """,
+		as_dict=1,
+	):
 		inv_details[d.name] = d.bill_no
 
 	return inv_details
 
+
 def get_balance(row, balance, debit_field, credit_field):
-	balance += (row.get(debit_field, 0) -  row.get(credit_field, 0))
+	balance += row.get(debit_field, 0) - row.get(credit_field, 0)
 
 	return balance
 
+
 def get_columns(filters):
 	if filters.get("presentation_currency"):
 		currency = filters["presentation_currency"]
@@ -478,113 +522,70 @@
 			"fieldname": "gl_entry",
 			"fieldtype": "Link",
 			"options": "GL Entry",
-			"hidden": 1
+			"hidden": 1,
 		},
-		{
-			"label": _("Posting Date"),
-			"fieldname": "posting_date",
-			"fieldtype": "Date",
-			"width": 90
-		},
+		{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 90},
 		{
 			"label": _("Account"),
 			"fieldname": "account",
 			"fieldtype": "Link",
 			"options": "Account",
-			"width": 180
+			"width": 180,
 		},
 		{
 			"label": _("Debit ({0})").format(currency),
 			"fieldname": "debit",
 			"fieldtype": "Float",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Credit ({0})").format(currency),
 			"fieldname": "credit",
 			"fieldtype": "Float",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Balance ({0})").format(currency),
 			"fieldname": "balance",
 			"fieldtype": "Float",
-			"width": 130
+			"width": 130,
 		},
-		{
-			"label": _("Voucher Type"),
-			"fieldname": "voucher_type",
-			"width": 120
-		},
+		{"label": _("Voucher Type"), "fieldname": "voucher_type", "width": 120},
 		{
 			"label": _("Voucher No"),
 			"fieldname": "voucher_no",
 			"fieldtype": "Dynamic Link",
 			"options": "voucher_type",
-			"width": 180
+			"width": 180,
 		},
-		{
-			"label": _("Against Account"),
-			"fieldname": "against",
-			"width": 120
-		},
-		{
-			"label": _("Party Type"),
-			"fieldname": "party_type",
-			"width": 100
-		},
-		{
-			"label": _("Party"),
-			"fieldname": "party",
-			"width": 100
-		},
-		{
-			"label": _("Project"),
-			"options": "Project",
-			"fieldname": "project",
-			"width": 100
-		}
+		{"label": _("Against Account"), "fieldname": "against", "width": 120},
+		{"label": _("Party Type"), "fieldname": "party_type", "width": 100},
+		{"label": _("Party"), "fieldname": "party", "width": 100},
+		{"label": _("Project"), "options": "Project", "fieldname": "project", "width": 100},
 	]
 
 	if filters.get("include_dimensions"):
-		for dim in get_accounting_dimensions(as_list = False):
-			columns.append({
-				"label": _(dim.label),
-				"options": dim.label,
-				"fieldname": dim.fieldname,
-				"width": 100
-			})
-		columns.append({
-			"label": _("Cost Center"),
-			"options": "Cost Center",
-			"fieldname": "cost_center",
-			"width": 100
-		})
+		for dim in get_accounting_dimensions(as_list=False):
+			columns.append(
+				{"label": _(dim.label), "options": dim.label, "fieldname": dim.fieldname, "width": 100}
+			)
+		columns.append(
+			{"label": _("Cost Center"), "options": "Cost Center", "fieldname": "cost_center", "width": 100}
+		)
 
-	columns.extend([
-		{
-			"label": _("Against Voucher Type"),
-			"fieldname": "against_voucher_type",
-			"width": 100
-		},
-		{
-			"label": _("Against Voucher"),
-			"fieldname": "against_voucher",
-			"fieldtype": "Dynamic Link",
-			"options": "against_voucher_type",
-			"width": 100
-		},
-		{
-			"label": _("Supplier Invoice No"),
-			"fieldname": "bill_no",
-			"fieldtype": "Data",
-			"width": 100
-		},
-		{
-			"label": _("Remarks"),
-			"fieldname": "remarks",
-			"width": 400
-		}
-	])
+	columns.extend(
+		[
+			{"label": _("Against Voucher Type"), "fieldname": "against_voucher_type", "width": 100},
+			{
+				"label": _("Against Voucher"),
+				"fieldname": "against_voucher",
+				"fieldtype": "Dynamic Link",
+				"options": "against_voucher_type",
+				"width": 100,
+			},
+			{"label": _("Supplier Invoice No"), "fieldname": "bill_no", "fieldtype": "Data", "width": 100},
+			{"label": _("Remarks"), "fieldname": "remarks", "width": 400},
+		]
+	)
 
 	return columns
diff --git a/erpnext/accounts/report/general_ledger/test_general_ledger.py b/erpnext/accounts/report/general_ledger/test_general_ledger.py
new file mode 100644
index 0000000..b10e769
--- /dev/null
+++ b/erpnext/accounts/report/general_ledger/test_general_ledger.py
@@ -0,0 +1,152 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
+# MIT License. See license.txt
+
+import frappe
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import today
+
+from erpnext.accounts.report.general_ledger.general_ledger import execute
+
+
+class TestGeneralLedger(FrappeTestCase):
+	def test_foreign_account_balance_after_exchange_rate_revaluation(self):
+		"""
+		Checks the correctness of balance after exchange rate revaluation
+		"""
+		# create a new account with USD currency
+		account_name = "Test USD Account for Revalutation"
+		company = "_Test Company"
+		account = frappe.get_doc(
+			{
+				"account_name": account_name,
+				"is_group": 0,
+				"company": company,
+				"root_type": "Asset",
+				"report_type": "Balance Sheet",
+				"account_currency": "USD",
+				"inter_company_account": 0,
+				"parent_account": "Bank Accounts - _TC",
+				"account_type": "Bank",
+				"doctype": "Account",
+			}
+		)
+		account.insert(ignore_if_duplicate=True)
+		# create a JV to debit 1000 USD at 75 exchange rate
+		jv = frappe.new_doc("Journal Entry")
+		jv.posting_date = today()
+		jv.company = company
+		jv.multi_currency = 1
+		jv.cost_center = "_Test Cost Center - _TC"
+		jv.set(
+			"accounts",
+			[
+				{
+					"account": account.name,
+					"debit_in_account_currency": 1000,
+					"credit_in_account_currency": 0,
+					"exchange_rate": 75,
+					"cost_center": "_Test Cost Center - _TC",
+				},
+				{
+					"account": "Cash - _TC",
+					"debit_in_account_currency": 0,
+					"credit_in_account_currency": 75000,
+					"cost_center": "_Test Cost Center - _TC",
+				},
+			],
+		)
+		jv.save()
+		jv.submit()
+		# create a JV to credit 900 USD at 100 exchange rate
+		jv = frappe.new_doc("Journal Entry")
+		jv.posting_date = today()
+		jv.company = company
+		jv.multi_currency = 1
+		jv.cost_center = "_Test Cost Center - _TC"
+		jv.set(
+			"accounts",
+			[
+				{
+					"account": account.name,
+					"debit_in_account_currency": 0,
+					"credit_in_account_currency": 900,
+					"exchange_rate": 100,
+					"cost_center": "_Test Cost Center - _TC",
+				},
+				{
+					"account": "Cash - _TC",
+					"debit_in_account_currency": 90000,
+					"credit_in_account_currency": 0,
+					"cost_center": "_Test Cost Center - _TC",
+				},
+			],
+		)
+		jv.save()
+		jv.submit()
+
+		# create an exchange rate revaluation entry at 77 exchange rate
+		revaluation = frappe.new_doc("Exchange Rate Revaluation")
+		revaluation.posting_date = today()
+		revaluation.company = company
+		revaluation.set(
+			"accounts",
+			[
+				{
+					"account": account.name,
+					"account_currency": "USD",
+					"new_exchange_rate": 77,
+					"new_balance_in_base_currency": 7700,
+					"balance_in_base_currency": -15000,
+					"balance_in_account_currency": 100,
+					"current_exchange_rate": -150,
+				}
+			],
+		)
+		revaluation.save()
+		revaluation.submit()
+
+		# post journal entry to revaluate
+		frappe.db.set_value(
+			"Company", company, "unrealized_exchange_gain_loss_account", "_Test Exchange Gain/Loss - _TC"
+		)
+		revaluation_jv = revaluation.make_jv_entry()
+		revaluation_jv = frappe.get_doc(revaluation_jv)
+		revaluation_jv.cost_center = "_Test Cost Center - _TC"
+		for acc in revaluation_jv.get("accounts"):
+			acc.cost_center = "_Test Cost Center - _TC"
+		revaluation_jv.save()
+		revaluation_jv.submit()
+
+		# check the balance of the account
+		balance = frappe.db.sql(
+			"""
+				select sum(debit_in_account_currency) - sum(credit_in_account_currency)
+				from `tabGL Entry`
+				where account = %s
+				group by account
+			""",
+			account.name,
+		)
+
+		self.assertEqual(balance[0][0], 100)
+
+		# check if general ledger shows correct balance
+		columns, data = execute(
+			frappe._dict(
+				{
+					"company": company,
+					"from_date": today(),
+					"to_date": today(),
+					"account": [account.name],
+					"group_by": "Group by Voucher (Consolidated)",
+				}
+			)
+		)
+
+		self.assertEqual(data[1]["account"], account.name)
+		self.assertEqual(data[1]["debit"], 1000)
+		self.assertEqual(data[1]["credit"], 0)
+		self.assertEqual(data[2]["debit"], 0)
+		self.assertEqual(data[2]["credit"], 900)
+		self.assertEqual(data[3]["debit"], 100)
+		self.assertEqual(data[3]["credit"], 100)
diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py
index b18b940..9d56678 100644
--- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py
+++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py
@@ -12,30 +12,57 @@
 
 
 def execute(filters=None):
-	period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, filters.period_start_date,
-		filters.period_end_date, filters.filter_based_on, filters.periodicity, filters.accumulated_values, filters.company)
+	period_list = get_period_list(
+		filters.from_fiscal_year,
+		filters.to_fiscal_year,
+		filters.period_start_date,
+		filters.period_end_date,
+		filters.filter_based_on,
+		filters.periodicity,
+		filters.accumulated_values,
+		filters.company,
+	)
 
 	columns, data = [], []
 
-	income = get_data(filters.company, "Income", "Credit", period_list, filters = filters,
+	income = get_data(
+		filters.company,
+		"Income",
+		"Credit",
+		period_list,
+		filters=filters,
 		accumulated_values=filters.accumulated_values,
-		ignore_closing_entries=True, ignore_accumulated_values_for_fy= True, total= False)
+		ignore_closing_entries=True,
+		ignore_accumulated_values_for_fy=True,
+		total=False,
+	)
 
-	expense = get_data(filters.company, "Expense", "Debit", period_list, filters=filters,
+	expense = get_data(
+		filters.company,
+		"Expense",
+		"Debit",
+		period_list,
+		filters=filters,
 		accumulated_values=filters.accumulated_values,
-		ignore_closing_entries=True, ignore_accumulated_values_for_fy= True, total= False)
+		ignore_closing_entries=True,
+		ignore_accumulated_values_for_fy=True,
+		total=False,
+	)
 
-	columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company)
-
+	columns = get_columns(
+		filters.periodicity, period_list, filters.accumulated_values, filters.company
+	)
 
 	gross_income = get_revenue(income, period_list)
 	gross_expense = get_revenue(expense, period_list)
 
-	if(len(gross_income)==0 and len(gross_expense)== 0):
-		data.append({
-			"account_name": "'" + _("Nothing is included in gross") + "'",
-			"account": "'" + _("Nothing is included in gross") + "'"
-		})
+	if len(gross_income) == 0 and len(gross_expense) == 0:
+		data.append(
+			{
+				"account_name": "'" + _("Nothing is included in gross") + "'",
+				"account": "'" + _("Nothing is included in gross") + "'",
+			}
+		)
 		return columns, data
 
 	# to avoid error eg: gross_income[0] : list index out of range
@@ -44,10 +71,12 @@
 	if not gross_expense:
 		gross_expense = [{}]
 
-	data.append({
-		"account_name": "'" + _("Included in Gross Profit") + "'",
-		"account": "'" + _("Included in Gross Profit") + "'"
-	})
+	data.append(
+		{
+			"account_name": "'" + _("Included in Gross Profit") + "'",
+			"account": "'" + _("Included in Gross Profit") + "'",
+		}
+	)
 
 	data.append({})
 	data.extend(gross_income or [])
@@ -56,7 +85,14 @@
 	data.extend(gross_expense or [])
 
 	data.append({})
-	gross_profit = get_profit(gross_income, gross_expense, period_list, filters.company, 'Gross Profit',filters.presentation_currency)
+	gross_profit = get_profit(
+		gross_income,
+		gross_expense,
+		period_list,
+		filters.company,
+		"Gross Profit",
+		filters.presentation_currency,
+	)
 	data.append(gross_profit)
 
 	non_gross_income = get_revenue(income, period_list, 0)
@@ -67,28 +103,40 @@
 	data.append({})
 	data.extend(non_gross_expense or [])
 
-	net_profit = get_net_profit(non_gross_income, gross_income, gross_expense, non_gross_expense, period_list, filters.company,filters.presentation_currency)
+	net_profit = get_net_profit(
+		non_gross_income,
+		gross_income,
+		gross_expense,
+		non_gross_expense,
+		period_list,
+		filters.company,
+		filters.presentation_currency,
+	)
 	data.append({})
 	data.append(net_profit)
 
 	return columns, data
 
-def get_revenue(data, period_list, include_in_gross=1):
-	revenue = [item for item in data if item['include_in_gross']==include_in_gross or item['is_group']==1]
 
-	data_to_be_removed =True
+def get_revenue(data, period_list, include_in_gross=1):
+	revenue = [
+		item for item in data if item["include_in_gross"] == include_in_gross or item["is_group"] == 1
+	]
+
+	data_to_be_removed = True
 	while data_to_be_removed:
 		revenue, data_to_be_removed = remove_parent_with_no_child(revenue, period_list)
 	revenue = adjust_account(revenue, period_list)
 	return copy.deepcopy(revenue)
 
+
 def remove_parent_with_no_child(data, period_list):
 	data_to_be_removed = False
 	for parent in data:
-		if 'is_group' in parent and parent.get("is_group") == 1:
+		if "is_group" in parent and parent.get("is_group") == 1:
 			have_child = False
 			for child in data:
-				if 'parent_account' in child  and child.get("parent_account") == parent.get("account"):
+				if "parent_account" in child and child.get("parent_account") == parent.get("account"):
 					have_child = True
 					break
 
@@ -98,8 +146,9 @@
 
 	return data, data_to_be_removed
 
-def adjust_account(data, period_list, consolidated= False):
-	leaf_nodes = [item for item in data if item['is_group'] == 0]
+
+def adjust_account(data, period_list, consolidated=False):
+	leaf_nodes = [item for item in data if item["is_group"] == 0]
 	totals = {}
 	for node in leaf_nodes:
 		set_total(node, node["total"], data, totals)
@@ -107,25 +156,30 @@
 		for period in period_list:
 			key = period if consolidated else period.key
 			d[key] = totals[d["account"]]
-			d['total'] = totals[d["account"]]
+			d["total"] = totals[d["account"]]
 	return data
 
+
 def set_total(node, value, complete_list, totals):
-	if not totals.get(node['account']):
+	if not totals.get(node["account"]):
 		totals[node["account"]] = 0
 	totals[node["account"]] += value
 
-	parent = node['parent_account']
-	if not parent == '':
-		return set_total(next(item for item in complete_list if item['account'] == parent), value, complete_list, totals)
+	parent = node["parent_account"]
+	if not parent == "":
+		return set_total(
+			next(item for item in complete_list if item["account"] == parent), value, complete_list, totals
+		)
 
 
-def get_profit(gross_income, gross_expense, period_list, company, profit_type, currency=None, consolidated=False):
+def get_profit(
+	gross_income, gross_expense, period_list, company, profit_type, currency=None, consolidated=False
+):
 	profit_loss = {
 		"account_name": "'" + _(profit_type) + "'",
 		"account": "'" + _(profit_type) + "'",
 		"warn_if_negative": True,
-		"currency": currency or frappe.get_cached_value('Company',  company,  "default_currency")
+		"currency": currency or frappe.get_cached_value("Company", company, "default_currency"),
 	}
 
 	has_value = False
@@ -137,17 +191,27 @@
 		profit_loss[key] = gross_income_for_period - gross_expense_for_period
 
 		if profit_loss[key]:
-			has_value=True
+			has_value = True
 
 	if has_value:
 		return profit_loss
 
-def get_net_profit(non_gross_income, gross_income, gross_expense, non_gross_expense, period_list, company, currency=None, consolidated=False):
+
+def get_net_profit(
+	non_gross_income,
+	gross_income,
+	gross_expense,
+	non_gross_expense,
+	period_list,
+	company,
+	currency=None,
+	consolidated=False,
+):
 	profit_loss = {
 		"account_name": "'" + _("Net Profit") + "'",
 		"account": "'" + _("Net Profit") + "'",
 		"warn_if_negative": True,
-		"currency": currency or frappe.get_cached_value('Company',  company,  "default_currency")
+		"currency": currency or frappe.get_cached_value("Company", company, "default_currency"),
 	}
 
 	has_value = False
@@ -165,7 +229,7 @@
 		profit_loss[key] = flt(total_income) - flt(total_expense)
 
 		if profit_loss[key]:
-			has_value=True
+			has_value = True
 
 	if has_value:
 		return profit_loss
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.js b/erpnext/accounts/report/gross_profit/gross_profit.js
index 158ff4d..3d37b58 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.js
+++ b/erpnext/accounts/report/gross_profit/gross_profit.js
@@ -35,7 +35,7 @@
 			"fieldname":"group_by",
 			"label": __("Group By"),
 			"fieldtype": "Select",
-			"options": "Invoice\nItem Code\nItem Group\nBrand\nWarehouse\nCustomer\nCustomer Group\nTerritory\nSales Person\nProject",
+			"options": "Invoice\nItem Code\nItem Group\nBrand\nWarehouse\nCustomer\nCustomer Group\nTerritory\nSales Person\nProject\nMonthly\nPayment Term",
 			"default": "Invoice"
 		},
 	],
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index b03bb9b..526ea9d 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -4,45 +4,149 @@
 
 import frappe
 from frappe import _, scrub
-from frappe.utils import cint, flt
+from frappe.utils import cint, flt, formatdate
 
 from erpnext.controllers.queries import get_match_cond
 from erpnext.stock.utils import get_incoming_rate
 
 
 def execute(filters=None):
-	if not filters: filters = frappe._dict()
-	filters.currency = frappe.get_cached_value('Company',  filters.company,  "default_currency")
+	if not filters:
+		filters = frappe._dict()
+	filters.currency = frappe.get_cached_value("Company", filters.company, "default_currency")
 
 	gross_profit_data = GrossProfitGenerator(filters)
 
 	data = []
 
-	group_wise_columns = frappe._dict({
-		"invoice": ["invoice_or_item", "customer", "customer_group", "posting_date","item_code", "item_name","item_group", "brand", "description",
-			"warehouse", "qty", "base_rate", "buying_rate", "base_amount",
-			"buying_amount", "gross_profit", "gross_profit_percent", "project"],
-		"item_code": ["item_code", "item_name", "brand", "description", "qty", "base_rate",
-			"buying_rate", "base_amount", "buying_amount", "gross_profit", "gross_profit_percent"],
-		"warehouse": ["warehouse", "qty", "base_rate", "buying_rate", "base_amount", "buying_amount",
-			"gross_profit", "gross_profit_percent"],
-		"brand": ["brand", "qty", "base_rate", "buying_rate", "base_amount", "buying_amount",
-			"gross_profit", "gross_profit_percent"],
-		"item_group": ["item_group", "qty", "base_rate", "buying_rate", "base_amount", "buying_amount",
-			"gross_profit", "gross_profit_percent"],
-		"customer": ["customer", "customer_group", "qty", "base_rate", "buying_rate", "base_amount", "buying_amount",
-			"gross_profit", "gross_profit_percent"],
-		"customer_group": ["customer_group", "qty", "base_rate", "buying_rate", "base_amount", "buying_amount",
-			"gross_profit", "gross_profit_percent"],
-		"sales_person": ["sales_person", "allocated_amount", "qty", "base_rate", "buying_rate", "base_amount", "buying_amount",
-			"gross_profit", "gross_profit_percent"],
-		"project": ["project", "base_amount", "buying_amount", "gross_profit", "gross_profit_percent"],
-		"territory": ["territory", "base_amount", "buying_amount", "gross_profit", "gross_profit_percent"]
-	})
+	group_wise_columns = frappe._dict(
+		{
+			"invoice": [
+				"invoice_or_item",
+				"customer",
+				"customer_group",
+				"posting_date",
+				"item_code",
+				"item_name",
+				"item_group",
+				"brand",
+				"description",
+				"warehouse",
+				"qty",
+				"base_rate",
+				"buying_rate",
+				"base_amount",
+				"buying_amount",
+				"gross_profit",
+				"gross_profit_percent",
+				"project",
+			],
+			"item_code": [
+				"item_code",
+				"item_name",
+				"brand",
+				"description",
+				"qty",
+				"base_rate",
+				"buying_rate",
+				"base_amount",
+				"buying_amount",
+				"gross_profit",
+				"gross_profit_percent",
+			],
+			"warehouse": [
+				"warehouse",
+				"qty",
+				"base_rate",
+				"buying_rate",
+				"base_amount",
+				"buying_amount",
+				"gross_profit",
+				"gross_profit_percent",
+			],
+			"brand": [
+				"brand",
+				"qty",
+				"base_rate",
+				"buying_rate",
+				"base_amount",
+				"buying_amount",
+				"gross_profit",
+				"gross_profit_percent",
+			],
+			"item_group": [
+				"item_group",
+				"qty",
+				"base_rate",
+				"buying_rate",
+				"base_amount",
+				"buying_amount",
+				"gross_profit",
+				"gross_profit_percent",
+			],
+			"customer": [
+				"customer",
+				"customer_group",
+				"qty",
+				"base_rate",
+				"buying_rate",
+				"base_amount",
+				"buying_amount",
+				"gross_profit",
+				"gross_profit_percent",
+			],
+			"customer_group": [
+				"customer_group",
+				"qty",
+				"base_rate",
+				"buying_rate",
+				"base_amount",
+				"buying_amount",
+				"gross_profit",
+				"gross_profit_percent",
+			],
+			"sales_person": [
+				"sales_person",
+				"allocated_amount",
+				"qty",
+				"base_rate",
+				"buying_rate",
+				"base_amount",
+				"buying_amount",
+				"gross_profit",
+				"gross_profit_percent",
+			],
+			"project": ["project", "base_amount", "buying_amount", "gross_profit", "gross_profit_percent"],
+			"territory": [
+				"territory",
+				"base_amount",
+				"buying_amount",
+				"gross_profit",
+				"gross_profit_percent",
+			],
+			"monthly": [
+				"monthly",
+				"qty",
+				"base_rate",
+				"buying_rate",
+				"base_amount",
+				"buying_amount",
+				"gross_profit",
+				"gross_profit_percent",
+			],
+			"payment_term": [
+				"payment_term",
+				"base_amount",
+				"buying_amount",
+				"gross_profit",
+				"gross_profit_percent",
+			],
+		}
+	)
 
 	columns = get_columns(group_wise_columns, filters)
 
-	if filters.group_by == 'Invoice':
+	if filters.group_by == "Invoice":
 		get_data_when_grouped_by_invoice(columns, gross_profit_data, filters, group_wise_columns, data)
 
 	else:
@@ -50,11 +154,14 @@
 
 	return columns, data
 
-def get_data_when_grouped_by_invoice(columns, gross_profit_data, filters, group_wise_columns, data):
+
+def get_data_when_grouped_by_invoice(
+	columns, gross_profit_data, filters, group_wise_columns, data
+):
 	column_names = get_column_names()
 
 	# to display item as Item Code: Item Name
-	columns[0] = 'Sales Invoice:Link/Item:300'
+	columns[0] = "Sales Invoice:Link/Item:300"
 	# removing Item Code and Item Name columns
 	del columns[4:6]
 
@@ -69,6 +176,7 @@
 
 		data.append(row)
 
+
 def get_data_when_not_grouped_by_invoice(gross_profit_data, filters, group_wise_columns, data):
 	for src in gross_profit_data.grouped_data:
 		row = []
@@ -79,69 +187,209 @@
 
 		data.append(row)
 
+
 def get_columns(group_wise_columns, filters):
 	columns = []
-	column_map = frappe._dict({
-		"parent": {"label": _('Sales Invoice'), "fieldname": "parent_invoice", "fieldtype": "Link", "options": "Sales Invoice", "width": 120},
-		"invoice_or_item": {"label": _('Sales Invoice'), "fieldtype": "Link", "options": "Sales Invoice", "width": 120},
-		"posting_date": {"label": _('Posting Date'), "fieldname": "posting_date", "fieldtype": "Date", "width": 100},
-		"posting_time": {"label": _('Posting Time'), "fieldname": "posting_time", "fieldtype": "Data", "width": 100},
-		"item_code": {"label": _('Item Code'), "fieldname": "item_code", "fieldtype": "Link", "options": "Item", "width": 100},
-		"item_name": {"label": _('Item Name'), "fieldname": "item_name", "fieldtype": "Data", "width": 100},
-		"item_group": {"label": _('Item Group'), "fieldname": "item_group", "fieldtype": "Link", "options": "Item Group", "width": 100},
-		"brand": {"label": _('Brand'), "fieldtype": "Link", "options": "Brand", "width": 100},
-		"description": {"label": _('Description'), "fieldname": "description",  "fieldtype": "Data", "width": 100},
-		"warehouse": {"label": _('Warehouse'), "fieldname": "warehouse", "fieldtype": "Link", "options": "warehouse", "width": 100},
-		"qty": {"label": _('Qty'), "fieldname": "qty", "fieldtype": "Float", "width": 80},
-		"base_rate": {"label": _('Avg. Selling Rate'), "fieldname": "avg._selling_rate",  "fieldtype": "Currency", "options": "currency", "width": 100},
-		"buying_rate": {"label": _('Valuation Rate'), "fieldname": "valuation_rate", "fieldtype": "Currency", "options": "currency", "width": 100},
-		"base_amount": {"label": _('Selling Amount'), "fieldname": "selling_amount", "fieldtype": "Currency", "options": "currency", "width": 100},
-		"buying_amount": {"label": _('Buying Amount'), "fieldname": "buying_amount", "fieldtype": "Currency", "options": "currency", "width": 100},
-		"gross_profit": {"label": _('Gross Profit'), "fieldname": "gross_profit", "fieldtype": "Currency", "options": "currency", "width": 100},
-		"gross_profit_percent": {"label": _('Gross Profit Percent'), "fieldname": "gross_profit_%",
-			"fieldtype": "Percent", "width": 100},
-		"project": {"label": _('Project'), "fieldname": "project", "fieldtype": "Link", "options": "Project", "width": 100},
-		"sales_person": {"label": _('Sales Person'), "fieldname": "sales_person", "fieldtype": "Data","width": 100},
-		"allocated_amount": {"label": _('Allocated Amount'), "fieldname": "allocated_amount", "fieldtype": "Currency", "options": "currency", "width": 100},
-		"customer": {"label": _('Customer'), "fieldname": "customer", "fieldtype": "Link", "options": "Customer", "width": 100},
-		"customer_group": {"label": _('Customer Group'), "fieldname": "customer_group", "fieldtype": "Link", "options": "customer", "width": 100},
-		"territory": {"label": _('Territory'), "fieldname": "territory",  "fieldtype": "Link", "options": "territory", "width": 100},
-	})
+	column_map = frappe._dict(
+		{
+			"parent": {
+				"label": _("Sales Invoice"),
+				"fieldname": "parent_invoice",
+				"fieldtype": "Link",
+				"options": "Sales Invoice",
+				"width": 120,
+			},
+			"invoice_or_item": {
+				"label": _("Sales Invoice"),
+				"fieldtype": "Link",
+				"options": "Sales Invoice",
+				"width": 120,
+			},
+			"posting_date": {
+				"label": _("Posting Date"),
+				"fieldname": "posting_date",
+				"fieldtype": "Date",
+				"width": 100,
+			},
+			"posting_time": {
+				"label": _("Posting Time"),
+				"fieldname": "posting_time",
+				"fieldtype": "Data",
+				"width": 100,
+			},
+			"item_code": {
+				"label": _("Item Code"),
+				"fieldname": "item_code",
+				"fieldtype": "Link",
+				"options": "Item",
+				"width": 100,
+			},
+			"item_name": {
+				"label": _("Item Name"),
+				"fieldname": "item_name",
+				"fieldtype": "Data",
+				"width": 100,
+			},
+			"item_group": {
+				"label": _("Item Group"),
+				"fieldname": "item_group",
+				"fieldtype": "Link",
+				"options": "Item Group",
+				"width": 100,
+			},
+			"brand": {"label": _("Brand"), "fieldtype": "Link", "options": "Brand", "width": 100},
+			"description": {
+				"label": _("Description"),
+				"fieldname": "description",
+				"fieldtype": "Data",
+				"width": 100,
+			},
+			"warehouse": {
+				"label": _("Warehouse"),
+				"fieldname": "warehouse",
+				"fieldtype": "Link",
+				"options": "warehouse",
+				"width": 100,
+			},
+			"qty": {"label": _("Qty"), "fieldname": "qty", "fieldtype": "Float", "width": 80},
+			"base_rate": {
+				"label": _("Avg. Selling Rate"),
+				"fieldname": "avg._selling_rate",
+				"fieldtype": "Currency",
+				"options": "currency",
+				"width": 100,
+			},
+			"buying_rate": {
+				"label": _("Valuation Rate"),
+				"fieldname": "valuation_rate",
+				"fieldtype": "Currency",
+				"options": "currency",
+				"width": 100,
+			},
+			"base_amount": {
+				"label": _("Selling Amount"),
+				"fieldname": "selling_amount",
+				"fieldtype": "Currency",
+				"options": "currency",
+				"width": 100,
+			},
+			"buying_amount": {
+				"label": _("Buying Amount"),
+				"fieldname": "buying_amount",
+				"fieldtype": "Currency",
+				"options": "currency",
+				"width": 100,
+			},
+			"gross_profit": {
+				"label": _("Gross Profit"),
+				"fieldname": "gross_profit",
+				"fieldtype": "Currency",
+				"options": "currency",
+				"width": 100,
+			},
+			"gross_profit_percent": {
+				"label": _("Gross Profit Percent"),
+				"fieldname": "gross_profit_%",
+				"fieldtype": "Percent",
+				"width": 100,
+			},
+			"project": {
+				"label": _("Project"),
+				"fieldname": "project",
+				"fieldtype": "Link",
+				"options": "Project",
+				"width": 100,
+			},
+			"sales_person": {
+				"label": _("Sales Person"),
+				"fieldname": "sales_person",
+				"fieldtype": "Data",
+				"width": 100,
+			},
+			"allocated_amount": {
+				"label": _("Allocated Amount"),
+				"fieldname": "allocated_amount",
+				"fieldtype": "Currency",
+				"options": "currency",
+				"width": 100,
+			},
+			"customer": {
+				"label": _("Customer"),
+				"fieldname": "customer",
+				"fieldtype": "Link",
+				"options": "Customer",
+				"width": 100,
+			},
+			"customer_group": {
+				"label": _("Customer Group"),
+				"fieldname": "customer_group",
+				"fieldtype": "Link",
+				"options": "customer",
+				"width": 100,
+			},
+			"territory": {
+				"label": _("Territory"),
+				"fieldname": "territory",
+				"fieldtype": "Link",
+				"options": "territory",
+				"width": 100,
+			},
+			"monthly": {
+				"label": _("Monthly"),
+				"fieldname": "monthly",
+				"fieldtype": "Data",
+				"width": 100,
+			},
+			"payment_term": {
+				"label": _("Payment Term"),
+				"fieldname": "payment_term",
+				"fieldtype": "Link",
+				"options": "Payment Term",
+				"width": 170,
+			},
+		}
+	)
 
 	for col in group_wise_columns.get(scrub(filters.group_by)):
 		columns.append(column_map.get(col))
 
-	columns.append({
-		"fieldname": "currency",
-		"label" : _("Currency"),
-		"fieldtype": "Link",
-		"options": "Currency",
-		"hidden": 1
-	})
+	columns.append(
+		{
+			"fieldname": "currency",
+			"label": _("Currency"),
+			"fieldtype": "Link",
+			"options": "Currency",
+			"hidden": 1,
+		}
+	)
 
 	return columns
 
+
 def get_column_names():
-	return frappe._dict({
-		'invoice_or_item': 'sales_invoice',
-		'customer': 'customer',
-		'customer_group': 'customer_group',
-		'posting_date': 'posting_date',
-		'item_code': 'item_code',
-		'item_name': 'item_name',
-		'item_group': 'item_group',
-		'brand': 'brand',
-		'description': 'description',
-		'warehouse': 'warehouse',
-		'qty': 'qty',
-		'base_rate': 'avg._selling_rate',
-		'buying_rate': 'valuation_rate',
-		'base_amount': 'selling_amount',
-		'buying_amount': 'buying_amount',
-		'gross_profit': 'gross_profit',
-		'gross_profit_percent': 'gross_profit_%',
-		'project': 'project'
-	})
+	return frappe._dict(
+		{
+			"invoice_or_item": "sales_invoice",
+			"customer": "customer",
+			"customer_group": "customer_group",
+			"posting_date": "posting_date",
+			"item_code": "item_code",
+			"item_name": "item_name",
+			"item_group": "item_group",
+			"brand": "brand",
+			"description": "description",
+			"warehouse": "warehouse",
+			"qty": "qty",
+			"base_rate": "avg._selling_rate",
+			"buying_rate": "valuation_rate",
+			"base_amount": "selling_amount",
+			"buying_amount": "buying_amount",
+			"gross_profit": "gross_profit",
+			"gross_profit_percent": "gross_profit_%",
+			"project": "project",
+		}
+	)
+
 
 class GrossProfitGenerator(object):
 	def __init__(self, filters=None):
@@ -150,7 +398,7 @@
 		self.filters = frappe._dict(filters)
 		self.load_invoice_items()
 
-		if filters.group_by == 'Invoice':
+		if filters.group_by == "Invoice":
 			self.group_items_by_invoice()
 
 		self.load_stock_ledger_entries()
@@ -172,6 +420,9 @@
 			buying_amount = 0
 
 		for row in reversed(self.si_list):
+			if self.filters.get("group_by") == "Monthly":
+				row.monthly = formatdate(row.posting_date, "MMM YYYY")
+
 			if self.skip_row(row):
 				continue
 
@@ -181,17 +432,19 @@
 			if row.update_stock:
 				product_bundles = self.product_bundles.get(row.parenttype, {}).get(row.parent, frappe._dict())
 			elif row.dn_detail:
-				product_bundles = self.product_bundles.get("Delivery Note", {})\
-					.get(row.delivery_note, frappe._dict())
+				product_bundles = self.product_bundles.get("Delivery Note", {}).get(
+					row.delivery_note, frappe._dict()
+				)
 				row.item_row = row.dn_detail
 
 			# get buying amount
 			if row.item_code in product_bundles:
-				row.buying_amount = flt(self.get_buying_amount_from_product_bundle(row,
-					product_bundles[row.item_code]), self.currency_precision)
+				row.buying_amount = flt(
+					self.get_buying_amount_from_product_bundle(row, product_bundles[row.item_code]),
+					self.currency_precision,
+				)
 			else:
-				row.buying_amount = flt(self.get_buying_amount(row, row.item_code),
-					self.currency_precision)
+				row.buying_amount = flt(self.get_buying_amount(row, row.item_code), self.currency_precision)
 
 			if grouped_by_invoice:
 				if row.indent == 1.0:
@@ -211,7 +464,9 @@
 			# calculate gross profit
 			row.gross_profit = flt(row.base_amount - row.buying_amount, self.currency_precision)
 			if row.base_amount:
-				row.gross_profit_percent = flt((row.gross_profit / row.base_amount) * 100.0, self.currency_precision)
+				row.gross_profit_percent = flt(
+					(row.gross_profit / row.base_amount) * 100.0, self.currency_precision
+				)
 			else:
 				row.gross_profit_percent = 0.0
 
@@ -223,9 +478,43 @@
 
 	def get_average_rate_based_on_group_by(self):
 		for key in list(self.grouped):
-			if self.filters.get("group_by") != "Invoice":
+			if self.filters.get("group_by") == "Invoice":
 				for i, row in enumerate(self.grouped[key]):
-					if i==0:
+					if row.indent == 1.0:
+						if (
+							row.parent in self.returned_invoices and row.item_code in self.returned_invoices[row.parent]
+						):
+							returned_item_rows = self.returned_invoices[row.parent][row.item_code]
+							for returned_item_row in returned_item_rows:
+								row.qty += flt(returned_item_row.qty)
+								row.base_amount += flt(returned_item_row.base_amount, self.currency_precision)
+							row.buying_amount = flt(flt(row.qty) * flt(row.buying_rate), self.currency_precision)
+						if flt(row.qty) or row.base_amount:
+							row = self.set_average_rate(row)
+							self.grouped_data.append(row)
+			elif self.filters.get("group_by") == "Payment Term":
+				for i, row in enumerate(self.grouped[key]):
+					invoice_portion = 0
+
+					if row.is_return:
+						invoice_portion = 100
+					elif row.invoice_portion:
+						invoice_portion = row.invoice_portion
+					else:
+						invoice_portion = row.payment_amount * 100 / row.base_net_amount
+
+					if i == 0:
+						new_row = row
+						self.set_average_based_on_payment_term_portion(new_row, row, invoice_portion)
+					else:
+						new_row.qty += flt(row.qty)
+						self.set_average_based_on_payment_term_portion(new_row, row, invoice_portion, True)
+
+				new_row = self.set_average_rate(new_row)
+				self.grouped_data.append(new_row)
+			else:
+				for i, row in enumerate(self.grouped[key]):
+					if i == 0:
 						new_row = row
 					else:
 						new_row.qty += flt(row.qty)
@@ -233,36 +522,41 @@
 						new_row.base_amount += flt(row.base_amount, self.currency_precision)
 				new_row = self.set_average_rate(new_row)
 				self.grouped_data.append(new_row)
+
+	def set_average_based_on_payment_term_portion(self, new_row, row, invoice_portion, aggr=False):
+		cols = ["base_amount", "buying_amount", "gross_profit"]
+		for col in cols:
+			if aggr:
+				new_row[col] += row[col] * invoice_portion / 100
 			else:
-				for i, row in enumerate(self.grouped[key]):
-					if row.indent == 1.0:
-						if row.parent in self.returned_invoices \
-								and row.item_code in self.returned_invoices[row.parent]:
-							returned_item_rows = self.returned_invoices[row.parent][row.item_code]
-							for returned_item_row in returned_item_rows:
-								row.qty += flt(returned_item_row.qty)
-								row.base_amount += flt(returned_item_row.base_amount, self.currency_precision)
-							row.buying_amount = flt(flt(row.qty) * flt(row.buying_rate), self.currency_precision)
-						if (flt(row.qty) or row.base_amount):
-							row = self.set_average_rate(row)
-							self.grouped_data.append(row)
+				new_row[col] = row[col] * invoice_portion / 100
 
 	def is_not_invoice_row(self, row):
-		return (self.filters.get("group_by") == "Invoice" and row.indent != 0.0) or self.filters.get("group_by") != "Invoice"
+		return (self.filters.get("group_by") == "Invoice" and row.indent != 0.0) or self.filters.get(
+			"group_by"
+		) != "Invoice"
 
 	def set_average_rate(self, new_row):
 		self.set_average_gross_profit(new_row)
-		new_row.buying_rate = flt(new_row.buying_amount / new_row.qty, self.float_precision) if new_row.qty else 0
-		new_row.base_rate = flt(new_row.base_amount / new_row.qty, self.float_precision) if new_row.qty else 0
+		new_row.buying_rate = (
+			flt(new_row.buying_amount / new_row.qty, self.float_precision) if new_row.qty else 0
+		)
+		new_row.base_rate = (
+			flt(new_row.base_amount / new_row.qty, self.float_precision) if new_row.qty else 0
+		)
 		return new_row
 
 	def set_average_gross_profit(self, new_row):
 		new_row.gross_profit = flt(new_row.base_amount - new_row.buying_amount, self.currency_precision)
-		new_row.gross_profit_percent = flt(((new_row.gross_profit / new_row.base_amount) * 100.0), self.currency_precision) \
-			if new_row.base_amount else 0
+		new_row.gross_profit_percent = (
+			flt(((new_row.gross_profit / new_row.base_amount) * 100.0), self.currency_precision)
+			if new_row.base_amount
+			else 0
+		)
 
 	def get_returned_invoice_items(self):
-		returned_invoices = frappe.db.sql("""
+		returned_invoices = frappe.db.sql(
+			"""
 			select
 				si.name, si_item.item_code, si_item.stock_qty as qty, si_item.base_net_amount as base_amount, si.return_against
 			from
@@ -271,12 +565,15 @@
 				si.name = si_item.parent
 				and si.docstatus = 1
 				and si.is_return = 1
-		""", as_dict=1)
+		""",
+			as_dict=1,
+		)
 
 		self.returned_invoices = frappe._dict()
 		for inv in returned_invoices:
-			self.returned_invoices.setdefault(inv.return_against, frappe._dict())\
-				.setdefault(inv.item_code, []).append(inv)
+			self.returned_invoices.setdefault(inv.return_against, frappe._dict()).setdefault(
+				inv.item_code, []
+			).append(inv)
 
 	def skip_row(self, row):
 		if self.filters.get("group_by") != "Invoice":
@@ -288,7 +585,7 @@
 	def get_buying_amount_from_product_bundle(self, row, product_bundle):
 		buying_amount = 0.0
 		for packed_item in product_bundle:
-			if packed_item.get("parent_detail_docname")==row.item_row:
+			if packed_item.get("parent_detail_docname") == row.item_row:
 				buying_amount += self.get_buying_amount(row, packed_item.item_code)
 
 		return flt(buying_amount, self.currency_precision)
@@ -298,7 +595,7 @@
 		# stock_ledger_entries should already be filtered by item_code and warehouse and
 		# sorted by posting_date desc, posting_time desc
 		if item_code in self.non_stock_items and (row.project or row.cost_center):
-			#Issue 6089-Get last purchasing rate for non-stock item
+			# Issue 6089-Get last purchasing rate for non-stock item
 			item_rate = self.get_last_purchase_rate(item_code, row)
 			return flt(row.qty) * item_rate
 
@@ -311,15 +608,17 @@
 
 				for i, sle in enumerate(my_sle):
 					# find the stock valution rate from stock ledger entry
-					if sle.voucher_type == parenttype and parent == sle.voucher_no and \
-						sle.voucher_detail_no == row.item_row:
-							previous_stock_value = len(my_sle) > i+1 and \
-								flt(my_sle[i+1].stock_value) or 0.0
+					if (
+						sle.voucher_type == parenttype
+						and parent == sle.voucher_no
+						and sle.voucher_detail_no == row.item_row
+					):
+						previous_stock_value = len(my_sle) > i + 1 and flt(my_sle[i + 1].stock_value) or 0.0
 
-							if previous_stock_value:
-								return (previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty))
-							else:
-								return flt(row.qty) * self.get_average_buying_rate(row, item_code)
+						if previous_stock_value:
+							return (previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty))
+						else:
+							return flt(row.qty) * self.get_average_buying_rate(row, item_code)
 			else:
 				return flt(row.qty) * self.get_average_buying_rate(row, item_code)
 
@@ -328,15 +627,17 @@
 	def get_average_buying_rate(self, row, item_code):
 		args = row
 		if not item_code in self.average_buying_rate:
-			args.update({
-				'voucher_type': row.parenttype,
-				'voucher_no': row.parent,
-				'allow_zero_valuation': True,
-				'company': self.filters.company
-			})
+			args.update(
+				{
+					"voucher_type": row.parenttype,
+					"voucher_no": row.parent,
+					"allow_zero_valuation": True,
+					"company": self.filters.company,
+				}
+			)
 
 			average_buying_rate = get_incoming_rate(args)
-			self.average_buying_rate[item_code] =  flt(average_buying_rate)
+			self.average_buying_rate[item_code] = flt(average_buying_rate)
 
 		return self.average_buying_rate[item_code]
 
@@ -344,30 +645,21 @@
 		purchase_invoice = frappe.qb.DocType("Purchase Invoice")
 		purchase_invoice_item = frappe.qb.DocType("Purchase Invoice Item")
 
-		query = (frappe.qb.from_(purchase_invoice_item)
-			.inner_join(
-				purchase_invoice
-			).on(
-				purchase_invoice.name == purchase_invoice_item.parent
-			).select(
-				purchase_invoice_item.base_rate / purchase_invoice_item.conversion_factor
-			).where(
-				purchase_invoice.docstatus == 1
-			).where(
-				purchase_invoice.posting_date <= self.filters.to_date
-			).where(
-				purchase_invoice_item.item_code == item_code
-			))
+		query = (
+			frappe.qb.from_(purchase_invoice_item)
+			.inner_join(purchase_invoice)
+			.on(purchase_invoice.name == purchase_invoice_item.parent)
+			.select(purchase_invoice_item.base_rate / purchase_invoice_item.conversion_factor)
+			.where(purchase_invoice.docstatus == 1)
+			.where(purchase_invoice.posting_date <= self.filters.to_date)
+			.where(purchase_invoice_item.item_code == item_code)
+		)
 
 		if row.project:
-			query.where(
-				purchase_invoice_item.project == row.project
-			)
+			query.where(purchase_invoice_item.project == row.project)
 
 		if row.cost_center:
-			query.where(
-				purchase_invoice_item.cost_center == row.cost_center
-			)
+			query.where(purchase_invoice_item.cost_center == row.cost_center)
 
 		query.orderby(purchase_invoice.posting_date, order=frappe.qb.desc)
 		query.limit(1)
@@ -384,20 +676,35 @@
 		if self.filters.to_date:
 			conditions += " and posting_date <= %(to_date)s"
 
-		if self.filters.group_by=="Sales Person":
+		if self.filters.group_by == "Sales Person":
 			sales_person_cols = ", sales.sales_person, sales.allocated_amount, sales.incentives"
 			sales_team_table = "left join `tabSales Team` sales on sales.parent = `tabSales Invoice`.name"
 		else:
 			sales_person_cols = ""
 			sales_team_table = ""
 
+		if self.filters.group_by == "Payment Term":
+			payment_term_cols = """,if(`tabSales Invoice`.is_return = 1,
+										'{0}',
+										coalesce(schedule.payment_term, '{1}')) as payment_term,
+									schedule.invoice_portion,
+									schedule.payment_amount """.format(
+				_("Sales Return"), _("No Terms")
+			)
+			payment_term_table = """ left join `tabPayment Schedule` schedule on schedule.parent = `tabSales Invoice`.name and
+																				`tabSales Invoice`.is_return = 0 """
+		else:
+			payment_term_cols = ""
+			payment_term_table = ""
+
 		if self.filters.get("sales_invoice"):
 			conditions += " and `tabSales Invoice`.name = %(sales_invoice)s"
 
 		if self.filters.get("item_code"):
 			conditions += " and `tabSales Invoice Item`.item_code = %(item_code)s"
 
-		self.si_list = frappe.db.sql("""
+		self.si_list = frappe.db.sql(
+			"""
 			select
 				`tabSales Invoice Item`.parenttype, `tabSales Invoice Item`.parent,
 				`tabSales Invoice`.posting_date, `tabSales Invoice`.posting_time,
@@ -412,20 +719,30 @@
 				`tabSales Invoice Item`.name as "item_row", `tabSales Invoice`.is_return,
 				`tabSales Invoice Item`.cost_center
 				{sales_person_cols}
+				{payment_term_cols}
 			from
 				`tabSales Invoice` inner join `tabSales Invoice Item`
 					on `tabSales Invoice Item`.parent = `tabSales Invoice`.name
 				{sales_team_table}
+				{payment_term_table}
 			where
 				`tabSales Invoice`.docstatus=1 and `tabSales Invoice`.is_opening!='Yes' {conditions} {match_cond}
 			order by
-				`tabSales Invoice`.posting_date desc, `tabSales Invoice`.posting_time desc"""
-			.format(conditions=conditions, sales_person_cols=sales_person_cols,
-				sales_team_table=sales_team_table, match_cond = get_match_cond('Sales Invoice')), self.filters, as_dict=1)
+				`tabSales Invoice`.posting_date desc, `tabSales Invoice`.posting_time desc""".format(
+				conditions=conditions,
+				sales_person_cols=sales_person_cols,
+				sales_team_table=sales_team_table,
+				payment_term_cols=payment_term_cols,
+				payment_term_table=payment_term_table,
+				match_cond=get_match_cond("Sales Invoice"),
+			),
+			self.filters,
+			as_dict=1,
+		)
 
 	def group_items_by_invoice(self):
 		"""
-			Turns list of Sales Invoice Items to a tree of Sales Invoices with their Items as children.
+		Turns list of Sales Invoice Items to a tree of Sales Invoices with their Items as children.
 		"""
 
 		parents = []
@@ -448,94 +765,96 @@
 					row.parent_invoice = row.parent
 					row.invoice_or_item = row.item_code
 
-					if frappe.db.exists('Product Bundle', row.item_code):
+					if frappe.db.exists("Product Bundle", row.item_code):
 						self.add_bundle_items(row, index)
 
 	def get_invoice_row(self, row):
-		return frappe._dict({
-			'parent_invoice': "",
-			'indent': 0.0,
-			'invoice_or_item': row.parent,
-			'parent': None,
-			'posting_date': row.posting_date,
-			'posting_time': row.posting_time,
-			'project': row.project,
-			'update_stock': row.update_stock,
-			'customer': row.customer,
-			'customer_group': row.customer_group,
-			'item_code': None,
-			'item_name': None,
-			'description': None,
-			'warehouse': None,
-			'item_group': None,
-			'brand': None,
-			'dn_detail': None,
-			'delivery_note': None,
-			'qty': None,
-			'item_row': None,
-			'is_return': row.is_return,
-			'cost_center': row.cost_center,
-			'base_net_amount': frappe.db.get_value('Sales Invoice', row.parent, 'base_net_total')
-		})
+		return frappe._dict(
+			{
+				"parent_invoice": "",
+				"indent": 0.0,
+				"invoice_or_item": row.parent,
+				"parent": None,
+				"posting_date": row.posting_date,
+				"posting_time": row.posting_time,
+				"project": row.project,
+				"update_stock": row.update_stock,
+				"customer": row.customer,
+				"customer_group": row.customer_group,
+				"item_code": None,
+				"item_name": None,
+				"description": None,
+				"warehouse": None,
+				"item_group": None,
+				"brand": None,
+				"dn_detail": None,
+				"delivery_note": None,
+				"qty": None,
+				"item_row": None,
+				"is_return": row.is_return,
+				"cost_center": row.cost_center,
+				"base_net_amount": frappe.db.get_value("Sales Invoice", row.parent, "base_net_total"),
+			}
+		)
 
 	def add_bundle_items(self, product_bundle, index):
 		bundle_items = self.get_bundle_items(product_bundle)
 
 		for i, item in enumerate(bundle_items):
 			bundle_item = self.get_bundle_item_row(product_bundle, item)
-			self.si_list.insert((index+i+1), bundle_item)
+			self.si_list.insert((index + i + 1), bundle_item)
 
 	def get_bundle_items(self, product_bundle):
 		return frappe.get_all(
-			'Product Bundle Item',
-			filters = {
-				'parent': product_bundle.item_code
-			},
-			fields = ['item_code', 'qty']
+			"Product Bundle Item", filters={"parent": product_bundle.item_code}, fields=["item_code", "qty"]
 		)
 
 	def get_bundle_item_row(self, product_bundle, item):
 		item_name, description, item_group, brand = self.get_bundle_item_details(item.item_code)
 
-		return frappe._dict({
-			'parent_invoice': product_bundle.item_code,
-			'indent': product_bundle.indent + 1,
-			'parent': None,
-			'invoice_or_item': item.item_code,
-			'posting_date': product_bundle.posting_date,
-			'posting_time': product_bundle.posting_time,
-			'project': product_bundle.project,
-			'customer': product_bundle.customer,
-			'customer_group': product_bundle.customer_group,
-			'item_code': item.item_code,
-			'item_name': item_name,
-			'description': description,
-			'warehouse': product_bundle.warehouse,
-			'item_group': item_group,
-			'brand': brand,
-			'dn_detail': product_bundle.dn_detail,
-			'delivery_note': product_bundle.delivery_note,
-			'qty': (flt(product_bundle.qty) * flt(item.qty)),
-			'item_row': None,
-			'is_return': product_bundle.is_return,
-			'cost_center': product_bundle.cost_center
-		})
+		return frappe._dict(
+			{
+				"parent_invoice": product_bundle.item_code,
+				"indent": product_bundle.indent + 1,
+				"parent": None,
+				"invoice_or_item": item.item_code,
+				"posting_date": product_bundle.posting_date,
+				"posting_time": product_bundle.posting_time,
+				"project": product_bundle.project,
+				"customer": product_bundle.customer,
+				"customer_group": product_bundle.customer_group,
+				"item_code": item.item_code,
+				"item_name": item_name,
+				"description": description,
+				"warehouse": product_bundle.warehouse,
+				"item_group": item_group,
+				"brand": brand,
+				"dn_detail": product_bundle.dn_detail,
+				"delivery_note": product_bundle.delivery_note,
+				"qty": (flt(product_bundle.qty) * flt(item.qty)),
+				"item_row": None,
+				"is_return": product_bundle.is_return,
+				"cost_center": product_bundle.cost_center,
+			}
+		)
 
 	def get_bundle_item_details(self, item_code):
 		return frappe.db.get_value(
-			'Item',
-			item_code,
-			['item_name', 'description', 'item_group', 'brand']
+			"Item", item_code, ["item_name", "description", "item_group", "brand"]
 		)
 
 	def load_stock_ledger_entries(self):
-		res = frappe.db.sql("""select item_code, voucher_type, voucher_no,
+		res = frappe.db.sql(
+			"""select item_code, voucher_type, voucher_no,
 				voucher_detail_no, stock_value, warehouse, actual_qty as qty
 			from `tabStock Ledger Entry`
 			where company=%(company)s and is_cancelled = 0
 			order by
 				item_code desc, warehouse desc, posting_date desc,
-				posting_time desc, creation desc""", self.filters, as_dict=True)
+				posting_time desc, creation desc""",
+			self.filters,
+			as_dict=True,
+		)
 		self.sle = {}
 		for r in res:
 			if (r.item_code, r.warehouse) not in self.sle:
@@ -546,12 +865,18 @@
 	def load_product_bundle(self):
 		self.product_bundles = {}
 
-		for d in frappe.db.sql("""select parenttype, parent, parent_item,
+		for d in frappe.db.sql(
+			"""select parenttype, parent, parent_item,
 			item_code, warehouse, -1*qty as total_qty, parent_detail_docname
-			from `tabPacked Item` where docstatus=1""", as_dict=True):
-			self.product_bundles.setdefault(d.parenttype, frappe._dict()).setdefault(d.parent,
-				frappe._dict()).setdefault(d.parent_item, []).append(d)
+			from `tabPacked Item` where docstatus=1""",
+			as_dict=True,
+		):
+			self.product_bundles.setdefault(d.parenttype, frappe._dict()).setdefault(
+				d.parent, frappe._dict()
+			).setdefault(d.parent_item, []).append(d)
 
 	def load_non_stock_items(self):
-		self.non_stock_items = frappe.db.sql_list("""select name from tabItem
-			where is_stock_item=0""")
+		self.non_stock_items = frappe.db.sql_list(
+			"""select name from tabItem
+			where is_stock_item=0"""
+		)
diff --git a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
index 2f23c8e..230b18c 100644
--- a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
+++ b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
@@ -12,6 +12,7 @@
 	data = get_data(filters)
 	return columns, data
 
+
 def get_columns():
 	columns = [
 		{
@@ -19,53 +20,36 @@
 			"fieldtype": "Link",
 			"label": _("Territory"),
 			"options": "Territory",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"fieldname": "item_group",
 			"fieldtype": "Link",
 			"label": _("Item Group"),
 			"options": "Item Group",
-			"width": 150
+			"width": 150,
 		},
-		{
-			"fieldname": "item",
-			"fieldtype": "Link",
-			"options": "Item",
-			"label": "Item",
-			"width": 150
-		},
-		{
-			"fieldname": "item_name",
-			"fieldtype": "Data",
-			"label": _("Item Name"),
-			"width": 150
-		},
-
+		{"fieldname": "item", "fieldtype": "Link", "options": "Item", "label": _("Item"), "width": 150},
+		{"fieldname": "item_name", "fieldtype": "Data", "label": _("Item Name"), "width": 150},
 		{
 			"fieldname": "customer",
 			"fieldtype": "Link",
 			"label": _("Customer"),
 			"options": "Customer",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"fieldname": "last_order_date",
 			"fieldtype": "Date",
 			"label": _("Last Order Date"),
-			"width": 100
+			"width": 100,
 		},
-		{
-			"fieldname": "qty",
-			"fieldtype": "Float",
-			"label": _("Quantity"),
-			"width": 100
-		},
+		{"fieldname": "qty", "fieldtype": "Float", "label": _("Quantity"), "width": 100},
 		{
 			"fieldname": "days_since_last_order",
 			"fieldtype": "Int",
 			"label": _("Days Since Last Order"),
-			"width": 100
+			"width": 100,
 		},
 	]
 
@@ -84,19 +68,21 @@
 				"territory": territory.name,
 				"item_group": item.item_group,
 				"item": item.item_code,
-				"item_name": item.item_name
+				"item_name": item.item_name,
 			}
 
-			if sales_invoice_data.get((territory.name,item.item_code)):
-				item_obj = sales_invoice_data[(territory.name,item.item_code)]
-				if item_obj.days_since_last_order > cint(filters['days']):
-					row.update({
-						"territory": item_obj.territory,
-						"customer": item_obj.customer,
-						"last_order_date": item_obj.last_order_date,
-						"qty": item_obj.qty,
-						"days_since_last_order": item_obj.days_since_last_order
-					})
+			if sales_invoice_data.get((territory.name, item.item_code)):
+				item_obj = sales_invoice_data[(territory.name, item.item_code)]
+				if item_obj.days_since_last_order > cint(filters["days"]):
+					row.update(
+						{
+							"territory": item_obj.territory,
+							"customer": item_obj.customer,
+							"last_order_date": item_obj.last_order_date,
+							"qty": item_obj.qty,
+							"days_since_last_order": item_obj.days_since_last_order,
+						}
+					)
 				else:
 					continue
 
@@ -111,45 +97,49 @@
 
 	date_field = "s.transaction_date" if filters["based_on"] == "Sales Order" else "s.posting_date"
 
-	sales_data = frappe.db.sql("""
+	sales_data = frappe.db.sql(
+		"""
 		select s.territory, s.customer, si.item_group, si.item_code, si.qty, {date_field} as last_order_date,
-		DATEDIFF(CURDATE(), {date_field}) as days_since_last_order
+		DATEDIFF(CURRENT_DATE, {date_field}) as days_since_last_order
 		from `tab{doctype}` s, `tab{doctype} Item` si
 		where s.name = si.parent and s.docstatus = 1
-		order by days_since_last_order """ #nosec
-		.format(date_field = date_field, doctype = filters['based_on']), as_dict=1)
+		order by days_since_last_order """.format(  # nosec
+			date_field=date_field, doctype=filters["based_on"]
+		),
+		as_dict=1,
+	)
 
 	for d in sales_data:
-		item_details_map.setdefault((d.territory,d.item_code), d)
+		item_details_map.setdefault((d.territory, d.item_code), d)
 
 	return item_details_map
 
+
 def get_territories(filters):
 
 	filter_dict = {}
 	if filters.get("territory"):
-		filter_dict.update({'name': filters['territory']})
+		filter_dict.update({"name": filters["territory"]})
 
 	territories = frappe.get_all("Territory", fields=["name"], filters=filter_dict)
 
 	return territories
 
+
 def get_items(filters):
-	filters_dict = {
-		"disabled": 0,
-		"is_stock_item": 1
-	}
+	filters_dict = {"disabled": 0, "is_stock_item": 1}
 
 	if filters.get("item_group"):
-		filters_dict.update({
-			"item_group": filters["item_group"]
-		})
+		filters_dict.update({"item_group": filters["item_group"]})
 
 	if filters.get("item"):
-		filters_dict.update({
-			"name": filters["item"]
-		})
+		filters_dict.update({"name": filters["item"]})
 
-	items = frappe.get_all("Item", fields=["name", "item_group", "item_name", "item_code"], filters=filters_dict, order_by="name")
+	items = frappe.get_all(
+		"Item",
+		fields=["name", "item_group", "item_name", "item_code"],
+		filters=filters_dict,
+		order_by="name",
+	)
 
 	return items
diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
index aaed58d..c04b9c7 100644
--- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
+++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
@@ -21,8 +21,10 @@
 def execute(filters=None):
 	return _execute(filters)
 
+
 def _execute(filters=None, additional_table_columns=None, additional_query_columns=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 	columns = get_columns(additional_table_columns, filters)
 
 	company_currency = erpnext.get_company_currency(filters.company)
@@ -30,18 +32,23 @@
 	item_list = get_items(filters, additional_query_columns)
 	aii_account_map = get_aii_accounts()
 	if item_list:
-		itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency,
-			doctype='Purchase Invoice', tax_doctype='Purchase Taxes and Charges')
+		itemised_tax, tax_columns = get_tax_accounts(
+			item_list,
+			columns,
+			company_currency,
+			doctype="Purchase Invoice",
+			tax_doctype="Purchase Taxes and Charges",
+		)
 
 	po_pr_map = get_purchase_receipts_against_purchase_order(item_list)
 
 	data = []
 	total_row_map = {}
 	skip_total_row = 0
-	prev_group_by_value = ''
+	prev_group_by_value = ""
 
-	if filters.get('group_by'):
-		grand_total = get_grand_total(filters, 'Purchase Invoice')
+	if filters.get("group_by"):
+		grand_total = get_grand_total(filters, "Purchase Invoice")
 
 	item_details = get_item_details()
 
@@ -57,71 +64,81 @@
 		elif d.po_detail:
 			purchase_receipt = ", ".join(po_pr_map.get(d.po_detail, []))
 
-		expense_account = d.unrealized_profit_loss_account or d.expense_account \
-			or aii_account_map.get(d.company)
+		expense_account = (
+			d.unrealized_profit_loss_account or d.expense_account or aii_account_map.get(d.company)
+		)
 
 		row = {
-			'item_code': d.item_code,
-			'item_name': item_record.item_name if item_record else d.item_name,
-			'item_group': item_record.item_group if item_record else d.item_group,
-			'description': d.description,
-			'invoice': d.parent,
-			'posting_date': d.posting_date,
-			'supplier': d.supplier,
-			'supplier_name': d.supplier_name
+			"item_code": d.item_code,
+			"item_name": item_record.item_name if item_record else d.item_name,
+			"item_group": item_record.item_group if item_record else d.item_group,
+			"description": d.description,
+			"invoice": d.parent,
+			"posting_date": d.posting_date,
+			"supplier": d.supplier,
+			"supplier_name": d.supplier_name,
 		}
 
 		if additional_query_columns:
 			for col in additional_query_columns:
-				row.update({
-					col: d.get(col)
-				})
+				row.update({col: d.get(col)})
 
-		row.update({
-			'credit_to': d.credit_to,
-			'mode_of_payment': d.mode_of_payment,
-			'project': d.project,
-			'company': d.company,
-			'purchase_order': d.purchase_order,
-			'purchase_receipt': d.purchase_receipt,
-			'expense_account': expense_account,
-			'stock_qty': d.stock_qty,
-			'stock_uom': d.stock_uom,
-			'rate': d.base_net_amount / d.stock_qty,
-			'amount': d.base_net_amount
-		})
+		row.update(
+			{
+				"credit_to": d.credit_to,
+				"mode_of_payment": d.mode_of_payment,
+				"project": d.project,
+				"company": d.company,
+				"purchase_order": d.purchase_order,
+				"purchase_receipt": d.purchase_receipt,
+				"expense_account": expense_account,
+				"stock_qty": d.stock_qty,
+				"stock_uom": d.stock_uom,
+				"rate": d.base_net_amount / d.stock_qty,
+				"amount": d.base_net_amount,
+			}
+		)
 
 		total_tax = 0
 		for tax in tax_columns:
 			item_tax = itemised_tax.get(d.name, {}).get(tax, {})
-			row.update({
-				frappe.scrub(tax + ' Rate'): item_tax.get('tax_rate', 0),
-				frappe.scrub(tax + ' Amount'): item_tax.get('tax_amount', 0),
-			})
-			total_tax += flt(item_tax.get('tax_amount'))
+			row.update(
+				{
+					frappe.scrub(tax + " Rate"): item_tax.get("tax_rate", 0),
+					frappe.scrub(tax + " Amount"): item_tax.get("tax_amount", 0),
+				}
+			)
+			total_tax += flt(item_tax.get("tax_amount"))
 
-		row.update({
-			'total_tax': total_tax,
-			'total': d.base_net_amount + total_tax,
-			'currency': company_currency
-		})
+		row.update(
+			{"total_tax": total_tax, "total": d.base_net_amount + total_tax, "currency": company_currency}
+		)
 
-		if filters.get('group_by'):
-			row.update({'percent_gt': flt(row['total']/grand_total) * 100})
+		if filters.get("group_by"):
+			row.update({"percent_gt": flt(row["total"] / grand_total) * 100})
 			group_by_field, subtotal_display_field = get_group_by_and_display_fields(filters)
-			data, prev_group_by_value = add_total_row(data, filters, prev_group_by_value, d, total_row_map,
-				group_by_field, subtotal_display_field, grand_total, tax_columns)
-			add_sub_total_row(row, total_row_map, d.get(group_by_field, ''), tax_columns)
+			data, prev_group_by_value = add_total_row(
+				data,
+				filters,
+				prev_group_by_value,
+				d,
+				total_row_map,
+				group_by_field,
+				subtotal_display_field,
+				grand_total,
+				tax_columns,
+			)
+			add_sub_total_row(row, total_row_map, d.get(group_by_field, ""), tax_columns)
 
 		data.append(row)
 
-	if filters.get('group_by') and item_list:
-		total_row = total_row_map.get(prev_group_by_value or d.get('item_name'))
-		total_row['percent_gt'] = flt(total_row['total']/grand_total * 100)
+	if filters.get("group_by") and item_list:
+		total_row = total_row_map.get(prev_group_by_value or d.get("item_name"))
+		total_row["percent_gt"] = flt(total_row["total"] / grand_total * 100)
 		data.append(total_row)
 		data.append({})
-		add_sub_total_row(total_row, total_row_map, 'total_row', tax_columns)
-		data.append(total_row_map.get('total_row'))
+		add_sub_total_row(total_row, total_row_map, "total_row", tax_columns)
+		data.append(total_row_map.get("total_row"))
 		skip_total_row = 1
 
 	return columns, data, None, None, None, skip_total_row
@@ -131,195 +148,180 @@
 
 	columns = []
 
-	if filters.get('group_by') != ('Item'):
+	if filters.get("group_by") != ("Item"):
 		columns.extend(
 			[
 				{
-					'label': _('Item Code'),
-					'fieldname': 'item_code',
-					'fieldtype': 'Link',
-					'options': 'Item',
-					'width': 120
+					"label": _("Item Code"),
+					"fieldname": "item_code",
+					"fieldtype": "Link",
+					"options": "Item",
+					"width": 120,
 				},
+				{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 120},
+			]
+		)
+
+	if filters.get("group_by") not in ("Item", "Item Group"):
+		columns.extend(
+			[
 				{
-					'label': _('Item Name'),
-					'fieldname': 'item_name',
-					'fieldtype': 'Data',
-					'width': 120
+					"label": _("Item Group"),
+					"fieldname": "item_group",
+					"fieldtype": "Link",
+					"options": "Item Group",
+					"width": 120,
 				}
 			]
 		)
 
-	if filters.get('group_by') not in ('Item', 'Item Group'):
-		columns.extend([
+	columns.extend(
+		[
+			{"label": _("Description"), "fieldname": "description", "fieldtype": "Data", "width": 150},
 			{
-				'label': _('Item Group'),
-				'fieldname': 'item_group',
-				'fieldtype': 'Link',
-				'options': 'Item Group',
-				'width': 120
-			}
-		])
-
-	columns.extend([
-		{
-			'label': _('Description'),
-			'fieldname': 'description',
-			'fieldtype': 'Data',
-			'width': 150
-		},
-		{
-			'label': _('Invoice'),
-			'fieldname': 'invoice',
-			'fieldtype': 'Link',
-			'options': 'Purchase Invoice',
-			'width': 120
-		},
-		{
-			'label': _('Posting Date'),
-			'fieldname': 'posting_date',
-			'fieldtype': 'Date',
-			'width': 120
-		}
-	])
-
-	if filters.get('group_by') != 'Supplier':
-		columns.extend([
-			{
-				'label': _('Supplier'),
-				'fieldname': 'supplier',
-				'fieldtype': 'Link',
-				'options': 'Supplier',
-				'width': 120
+				"label": _("Invoice"),
+				"fieldname": "invoice",
+				"fieldtype": "Link",
+				"options": "Purchase Invoice",
+				"width": 120,
 			},
-			{
-				'label': _('Supplier Name'),
-				'fieldname': 'supplier_name',
-				'fieldtype': 'Data',
-				'width': 120
-			}
-		])
+			{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 120},
+		]
+	)
+
+	if filters.get("group_by") != "Supplier":
+		columns.extend(
+			[
+				{
+					"label": _("Supplier"),
+					"fieldname": "supplier",
+					"fieldtype": "Link",
+					"options": "Supplier",
+					"width": 120,
+				},
+				{"label": _("Supplier Name"), "fieldname": "supplier_name", "fieldtype": "Data", "width": 120},
+			]
+		)
 
 	if additional_table_columns:
 		columns += additional_table_columns
 
 	columns += [
 		{
-			'label': _('Payable Account'),
-			'fieldname': 'credit_to',
-			'fieldtype': 'Link',
-			'options': 'Account',
-			'width': 80
+			"label": _("Payable Account"),
+			"fieldname": "credit_to",
+			"fieldtype": "Link",
+			"options": "Account",
+			"width": 80,
 		},
 		{
-			'label': _('Mode Of Payment'),
-			'fieldname': 'mode_of_payment',
-			'fieldtype': 'Link',
-			'options': 'Mode of Payment',
-			'width': 120
+			"label": _("Mode Of Payment"),
+			"fieldname": "mode_of_payment",
+			"fieldtype": "Link",
+			"options": "Mode of Payment",
+			"width": 120,
 		},
 		{
-			'label': _('Project'),
-			'fieldname': 'project',
-			'fieldtype': 'Link',
-			'options': 'Project',
-			'width': 80
+			"label": _("Project"),
+			"fieldname": "project",
+			"fieldtype": "Link",
+			"options": "Project",
+			"width": 80,
 		},
 		{
-			'label': _('Company'),
-			'fieldname': 'company',
-			'fieldtype': 'Link',
-			'options': 'Company',
-			'width': 80
+			"label": _("Company"),
+			"fieldname": "company",
+			"fieldtype": "Link",
+			"options": "Company",
+			"width": 80,
 		},
 		{
-			'label': _('Purchase Order'),
-			'fieldname': 'purchase_order',
-			'fieldtype': 'Link',
-			'options': 'Purchase Order',
-			'width': 100
+			"label": _("Purchase Order"),
+			"fieldname": "purchase_order",
+			"fieldtype": "Link",
+			"options": "Purchase Order",
+			"width": 100,
 		},
 		{
-			'label': _("Purchase Receipt"),
-			'fieldname': 'Purchase Receipt',
-			'fieldtype': 'Link',
-			'options': 'Purchase Receipt',
-			'width': 100
+			"label": _("Purchase Receipt"),
+			"fieldname": "Purchase Receipt",
+			"fieldtype": "Link",
+			"options": "Purchase Receipt",
+			"width": 100,
 		},
 		{
-			'label': _('Expense Account'),
-			'fieldname': 'expense_account',
-			'fieldtype': 'Link',
-			'options': 'Account',
-			'width': 100
+			"label": _("Expense Account"),
+			"fieldname": "expense_account",
+			"fieldtype": "Link",
+			"options": "Account",
+			"width": 100,
+		},
+		{"label": _("Stock Qty"), "fieldname": "stock_qty", "fieldtype": "Float", "width": 100},
+		{
+			"label": _("Stock UOM"),
+			"fieldname": "stock_uom",
+			"fieldtype": "Link",
+			"options": "UOM",
+			"width": 100,
 		},
 		{
-			'label': _('Stock Qty'),
-			'fieldname': 'stock_qty',
-			'fieldtype': 'Float',
-			'width': 100
+			"label": _("Rate"),
+			"fieldname": "rate",
+			"fieldtype": "Float",
+			"options": "currency",
+			"width": 100,
 		},
 		{
-			'label': _('Stock UOM'),
-			'fieldname': 'stock_uom',
-			'fieldtype': 'Link',
-			'options': 'UOM',
-			'width': 100
+			"label": _("Amount"),
+			"fieldname": "amount",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 100,
 		},
-		{
-			'label': _('Rate'),
-			'fieldname': 'rate',
-			'fieldtype': 'Float',
-			'options': 'currency',
-			'width': 100
-		},
-		{
-			'label': _('Amount'),
-			'fieldname': 'amount',
-			'fieldtype': 'Currency',
-			'options': 'currency',
-			'width': 100
-		}
 	]
 
-	if filters.get('group_by'):
-		columns.append({
-			'label': _('% Of Grand Total'),
-			'fieldname': 'percent_gt',
-			'fieldtype': 'Float',
-			'width': 80
-		})
+	if filters.get("group_by"):
+		columns.append(
+			{"label": _("% Of Grand Total"), "fieldname": "percent_gt", "fieldtype": "Float", "width": 80}
+		)
 
 	return columns
 
+
 def get_conditions(filters):
 	conditions = ""
 
-	for opts in (("company", " and company=%(company)s"),
+	for opts in (
+		("company", " and company=%(company)s"),
 		("supplier", " and `tabPurchase Invoice`.supplier = %(supplier)s"),
 		("item_code", " and `tabPurchase Invoice Item`.item_code = %(item_code)s"),
 		("from_date", " and `tabPurchase Invoice`.posting_date>=%(from_date)s"),
 		("to_date", " and `tabPurchase Invoice`.posting_date<=%(to_date)s"),
-		("mode_of_payment", " and ifnull(mode_of_payment, '') = %(mode_of_payment)s")):
-			if filters.get(opts[0]):
-				conditions += opts[1]
+		("mode_of_payment", " and ifnull(mode_of_payment, '') = %(mode_of_payment)s"),
+	):
+		if filters.get(opts[0]):
+			conditions += opts[1]
 
 	if not filters.get("group_by"):
-		conditions += "ORDER BY `tabPurchase Invoice`.posting_date desc, `tabPurchase Invoice Item`.item_code desc"
+		conditions += (
+			"ORDER BY `tabPurchase Invoice`.posting_date desc, `tabPurchase Invoice Item`.item_code desc"
+		)
 	else:
-		conditions += get_group_by_conditions(filters, 'Purchase Invoice')
+		conditions += get_group_by_conditions(filters, "Purchase Invoice")
 
 	return conditions
 
+
 def get_items(filters, additional_query_columns):
 	conditions = get_conditions(filters)
 
 	if additional_query_columns:
-		additional_query_columns = ', ' + ', '.join(additional_query_columns)
+		additional_query_columns = ", " + ", ".join(additional_query_columns)
 	else:
-		additional_query_columns = ''
+		additional_query_columns = ""
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select
 			`tabPurchase Invoice Item`.`name`, `tabPurchase Invoice Item`.`parent`,
 			`tabPurchase Invoice`.posting_date, `tabPurchase Invoice`.credit_to, `tabPurchase Invoice`.company,
@@ -335,22 +337,35 @@
 		from `tabPurchase Invoice`, `tabPurchase Invoice Item`
 		where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and
 		`tabPurchase Invoice`.docstatus = 1 %s
-	""".format(additional_query_columns) % (conditions), filters, as_dict=1)
+	""".format(
+			additional_query_columns
+		)
+		% (conditions),
+		filters,
+		as_dict=1,
+	)
+
 
 def get_aii_accounts():
 	return dict(frappe.db.sql("select name, stock_received_but_not_billed from tabCompany"))
 
+
 def get_purchase_receipts_against_purchase_order(item_list):
 	po_pr_map = frappe._dict()
 	po_item_rows = list(set(d.po_detail for d in item_list))
 
 	if po_item_rows:
-		purchase_receipts = frappe.db.sql("""
+		purchase_receipts = frappe.db.sql(
+			"""
 			select parent, purchase_order_item
 			from `tabPurchase Receipt Item`
 			where docstatus=1 and purchase_order_item in (%s)
 			group by purchase_order_item, parent
-		""" % (', '.join(['%s']*len(po_item_rows))), tuple(po_item_rows), as_dict=1)
+		"""
+			% (", ".join(["%s"] * len(po_item_rows))),
+			tuple(po_item_rows),
+			as_dict=1,
+		)
 
 		for pr in purchase_receipts:
 			po_pr_map.setdefault(pr.po_detail, []).append(pr.parent)
diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
index 9b35538..ac70666 100644
--- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
+++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
@@ -18,11 +18,13 @@
 def execute(filters=None):
 	return _execute(filters)
 
+
 def _execute(filters=None, additional_table_columns=None, additional_query_columns=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 	columns = get_columns(additional_table_columns, filters)
 
-	company_currency = frappe.get_cached_value('Company',  filters.get('company'),  'default_currency')
+	company_currency = frappe.get_cached_value("Company", filters.get("company"), "default_currency")
 
 	item_list = get_items(filters, additional_query_columns)
 	if item_list:
@@ -34,10 +36,10 @@
 	data = []
 	total_row_map = {}
 	skip_total_row = 0
-	prev_group_by_value = ''
+	prev_group_by_value = ""
 
-	if filters.get('group_by'):
-		grand_total = get_grand_total(filters, 'Sales Invoice')
+	if filters.get("group_by"):
+		grand_total = get_grand_total(filters, "Sales Invoice")
 
 	customer_details = get_customer_details()
 	item_details = get_item_details()
@@ -56,289 +58,279 @@
 			delivery_note = d.parent
 
 		row = {
-			'item_code': d.item_code,
-			'item_name': item_record.item_name if item_record else d.item_name,
-			'item_group': item_record.item_group if item_record else d.item_group,
-			'description': d.description,
-			'invoice': d.parent,
-			'posting_date': d.posting_date,
-			'customer': d.customer,
-			'customer_name': customer_record.customer_name,
-			'customer_group': customer_record.customer_group,
+			"item_code": d.item_code,
+			"item_name": item_record.item_name if item_record else d.item_name,
+			"item_group": item_record.item_group if item_record else d.item_group,
+			"description": d.description,
+			"invoice": d.parent,
+			"posting_date": d.posting_date,
+			"customer": d.customer,
+			"customer_name": customer_record.customer_name,
+			"customer_group": customer_record.customer_group,
 		}
 
 		if additional_query_columns:
 			for col in additional_query_columns:
-				row.update({
-					col: d.get(col)
-				})
+				row.update({col: d.get(col)})
 
-		row.update({
-			'debit_to': d.debit_to,
-			'mode_of_payment': ", ".join(mode_of_payments.get(d.parent, [])),
-			'territory': d.territory,
-			'project': d.project,
-			'company': d.company,
-			'sales_order': d.sales_order,
-			'delivery_note': d.delivery_note,
-			'income_account': d.unrealized_profit_loss_account if d.is_internal_customer == 1 else d.income_account,
-			'cost_center': d.cost_center,
-			'stock_qty': d.stock_qty,
-			'stock_uom': d.stock_uom
-		})
+		row.update(
+			{
+				"debit_to": d.debit_to,
+				"mode_of_payment": ", ".join(mode_of_payments.get(d.parent, [])),
+				"territory": d.territory,
+				"project": d.project,
+				"company": d.company,
+				"sales_order": d.sales_order,
+				"delivery_note": d.delivery_note,
+				"income_account": d.unrealized_profit_loss_account
+				if d.is_internal_customer == 1
+				else d.income_account,
+				"cost_center": d.cost_center,
+				"stock_qty": d.stock_qty,
+				"stock_uom": d.stock_uom,
+			}
+		)
 
 		if d.stock_uom != d.uom and d.stock_qty:
-			row.update({
-				'rate': (d.base_net_rate * d.qty)/d.stock_qty,
-				'amount': d.base_net_amount
-			})
+			row.update({"rate": (d.base_net_rate * d.qty) / d.stock_qty, "amount": d.base_net_amount})
 		else:
-			row.update({
-				'rate': d.base_net_rate,
-				'amount': d.base_net_amount
-			})
+			row.update({"rate": d.base_net_rate, "amount": d.base_net_amount})
 
 		total_tax = 0
 		for tax in tax_columns:
 			item_tax = itemised_tax.get(d.name, {}).get(tax, {})
-			row.update({
-				frappe.scrub(tax + ' Rate'): item_tax.get('tax_rate', 0),
-				frappe.scrub(tax + ' Amount'): item_tax.get('tax_amount', 0),
-			})
-			total_tax += flt(item_tax.get('tax_amount'))
+			row.update(
+				{
+					frappe.scrub(tax + " Rate"): item_tax.get("tax_rate", 0),
+					frappe.scrub(tax + " Amount"): item_tax.get("tax_amount", 0),
+				}
+			)
+			total_tax += flt(item_tax.get("tax_amount"))
 
-		row.update({
-			'total_tax': total_tax,
-			'total': d.base_net_amount + total_tax,
-			'currency': company_currency
-		})
+		row.update(
+			{"total_tax": total_tax, "total": d.base_net_amount + total_tax, "currency": company_currency}
+		)
 
-		if filters.get('group_by'):
-			row.update({'percent_gt': flt(row['total']/grand_total) * 100})
+		if filters.get("group_by"):
+			row.update({"percent_gt": flt(row["total"] / grand_total) * 100})
 			group_by_field, subtotal_display_field = get_group_by_and_display_fields(filters)
-			data, prev_group_by_value = add_total_row(data, filters, prev_group_by_value, d, total_row_map,
-				group_by_field, subtotal_display_field, grand_total, tax_columns)
-			add_sub_total_row(row, total_row_map, d.get(group_by_field, ''), tax_columns)
+			data, prev_group_by_value = add_total_row(
+				data,
+				filters,
+				prev_group_by_value,
+				d,
+				total_row_map,
+				group_by_field,
+				subtotal_display_field,
+				grand_total,
+				tax_columns,
+			)
+			add_sub_total_row(row, total_row_map, d.get(group_by_field, ""), tax_columns)
 
 		data.append(row)
 
-	if filters.get('group_by') and item_list:
-		total_row = total_row_map.get(prev_group_by_value or d.get('item_name'))
-		total_row['percent_gt'] = flt(total_row['total']/grand_total * 100)
+	if filters.get("group_by") and item_list:
+		total_row = total_row_map.get(prev_group_by_value or d.get("item_name"))
+		total_row["percent_gt"] = flt(total_row["total"] / grand_total * 100)
 		data.append(total_row)
 		data.append({})
-		add_sub_total_row(total_row, total_row_map, 'total_row', tax_columns)
-		data.append(total_row_map.get('total_row'))
+		add_sub_total_row(total_row, total_row_map, "total_row", tax_columns)
+		data.append(total_row_map.get("total_row"))
 		skip_total_row = 1
 
 	return columns, data, None, None, None, skip_total_row
 
+
 def get_columns(additional_table_columns, filters):
 	columns = []
 
-	if filters.get('group_by') != ('Item'):
+	if filters.get("group_by") != ("Item"):
 		columns.extend(
 			[
 				{
-					'label': _('Item Code'),
-					'fieldname': 'item_code',
-					'fieldtype': 'Link',
-					'options': 'Item',
-					'width': 120
+					"label": _("Item Code"),
+					"fieldname": "item_code",
+					"fieldtype": "Link",
+					"options": "Item",
+					"width": 120,
 				},
+				{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 120},
+			]
+		)
+
+	if filters.get("group_by") not in ("Item", "Item Group"):
+		columns.extend(
+			[
 				{
-					'label': _('Item Name'),
-					'fieldname': 'item_name',
-					'fieldtype': 'Data',
-					'width': 120
+					"label": _("Item Group"),
+					"fieldname": "item_group",
+					"fieldtype": "Link",
+					"options": "Item Group",
+					"width": 120,
 				}
 			]
 		)
 
-	if filters.get('group_by') not in ('Item', 'Item Group'):
-		columns.extend([
+	columns.extend(
+		[
+			{"label": _("Description"), "fieldname": "description", "fieldtype": "Data", "width": 150},
 			{
-				'label': _('Item Group'),
-				'fieldname': 'item_group',
-				'fieldtype': 'Link',
-				'options': 'Item Group',
-				'width': 120
-			}
-		])
-
-	columns.extend([
-		{
-			'label': _('Description'),
-			'fieldname': 'description',
-			'fieldtype': 'Data',
-			'width': 150
-		},
-		{
-			'label': _('Invoice'),
-			'fieldname': 'invoice',
-			'fieldtype': 'Link',
-			'options': 'Sales Invoice',
-			'width': 120
-		},
-		{
-			'label': _('Posting Date'),
-			'fieldname': 'posting_date',
-			'fieldtype': 'Date',
-			'width': 120
-		}
-	])
-
-	if filters.get('group_by') != 'Customer':
-		columns.extend([
-			{
-				'label': _('Customer Group'),
-				'fieldname': 'customer_group',
-				'fieldtype': 'Link',
-				'options': 'Customer Group',
-				'width': 120
-			}
-		])
-
-	if filters.get('group_by') not in ('Customer', 'Customer Group'):
-		columns.extend([
-			{
-				'label': _('Customer'),
-				'fieldname': 'customer',
-				'fieldtype': 'Link',
-				'options': 'Customer',
-				'width': 120
+				"label": _("Invoice"),
+				"fieldname": "invoice",
+				"fieldtype": "Link",
+				"options": "Sales Invoice",
+				"width": 120,
 			},
-			{
-				'label': _('Customer Name'),
-				'fieldname': 'customer_name',
-				'fieldtype': 'Data',
-				'width': 120
-			}
-		])
+			{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 120},
+		]
+	)
+
+	if filters.get("group_by") != "Customer":
+		columns.extend(
+			[
+				{
+					"label": _("Customer Group"),
+					"fieldname": "customer_group",
+					"fieldtype": "Link",
+					"options": "Customer Group",
+					"width": 120,
+				}
+			]
+		)
+
+	if filters.get("group_by") not in ("Customer", "Customer Group"):
+		columns.extend(
+			[
+				{
+					"label": _("Customer"),
+					"fieldname": "customer",
+					"fieldtype": "Link",
+					"options": "Customer",
+					"width": 120,
+				},
+				{"label": _("Customer Name"), "fieldname": "customer_name", "fieldtype": "Data", "width": 120},
+			]
+		)
 
 	if additional_table_columns:
 		columns += additional_table_columns
 
 	columns += [
 		{
-			'label': _('Receivable Account'),
-			'fieldname': 'debit_to',
-			'fieldtype': 'Link',
-			'options': 'Account',
-			'width': 80
+			"label": _("Receivable Account"),
+			"fieldname": "debit_to",
+			"fieldtype": "Link",
+			"options": "Account",
+			"width": 80,
 		},
 		{
-			'label': _('Mode Of Payment'),
-			'fieldname': 'mode_of_payment',
-			'fieldtype': 'Data',
-			'width': 120
-		}
+			"label": _("Mode Of Payment"),
+			"fieldname": "mode_of_payment",
+			"fieldtype": "Data",
+			"width": 120,
+		},
 	]
 
-	if filters.get('group_by') != 'Territory':
-		columns.extend([
-			{
-				'label': _('Territory'),
-				'fieldname': 'territory',
-				'fieldtype': 'Link',
-				'options': 'Territory',
-				'width': 80
-			}
-		])
-
+	if filters.get("group_by") != "Territory":
+		columns.extend(
+			[
+				{
+					"label": _("Territory"),
+					"fieldname": "territory",
+					"fieldtype": "Link",
+					"options": "Territory",
+					"width": 80,
+				}
+			]
+		)
 
 	columns += [
 		{
-			'label': _('Project'),
-			'fieldname': 'project',
-			'fieldtype': 'Link',
-			'options': 'Project',
-			'width': 80
+			"label": _("Project"),
+			"fieldname": "project",
+			"fieldtype": "Link",
+			"options": "Project",
+			"width": 80,
 		},
 		{
-			'label': _('Company'),
-			'fieldname': 'company',
-			'fieldtype': 'Link',
-			'options': 'Company',
-			'width': 80
+			"label": _("Company"),
+			"fieldname": "company",
+			"fieldtype": "Link",
+			"options": "Company",
+			"width": 80,
 		},
 		{
-			'label': _('Sales Order'),
-			'fieldname': 'sales_order',
-			'fieldtype': 'Link',
-			'options': 'Sales Order',
-			'width': 100
+			"label": _("Sales Order"),
+			"fieldname": "sales_order",
+			"fieldtype": "Link",
+			"options": "Sales Order",
+			"width": 100,
 		},
 		{
-			'label': _("Delivery Note"),
-			'fieldname': 'delivery_note',
-			'fieldtype': 'Link',
-			'options': 'Delivery Note',
-			'width': 100
+			"label": _("Delivery Note"),
+			"fieldname": "delivery_note",
+			"fieldtype": "Link",
+			"options": "Delivery Note",
+			"width": 100,
 		},
 		{
-			'label': _('Income Account'),
-			'fieldname': 'income_account',
-			'fieldtype': 'Link',
-			'options': 'Account',
-			'width': 100
+			"label": _("Income Account"),
+			"fieldname": "income_account",
+			"fieldtype": "Link",
+			"options": "Account",
+			"width": 100,
 		},
 		{
-			'label': _("Cost Center"),
-			'fieldname': 'cost_center',
-			'fieldtype': 'Link',
-			'options': 'Cost Center',
-			'width': 100
+			"label": _("Cost Center"),
+			"fieldname": "cost_center",
+			"fieldtype": "Link",
+			"options": "Cost Center",
+			"width": 100,
+		},
+		{"label": _("Stock Qty"), "fieldname": "stock_qty", "fieldtype": "Float", "width": 100},
+		{
+			"label": _("Stock UOM"),
+			"fieldname": "stock_uom",
+			"fieldtype": "Link",
+			"options": "UOM",
+			"width": 100,
 		},
 		{
-			'label': _('Stock Qty'),
-			'fieldname': 'stock_qty',
-			'fieldtype': 'Float',
-			'width': 100
+			"label": _("Rate"),
+			"fieldname": "rate",
+			"fieldtype": "Float",
+			"options": "currency",
+			"width": 100,
 		},
 		{
-			'label': _('Stock UOM'),
-			'fieldname': 'stock_uom',
-			'fieldtype': 'Link',
-			'options': 'UOM',
-			'width': 100
+			"label": _("Amount"),
+			"fieldname": "amount",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 100,
 		},
-		{
-			'label': _('Rate'),
-			'fieldname': 'rate',
-			'fieldtype': 'Float',
-			'options': 'currency',
-			'width': 100
-		},
-		{
-			'label': _('Amount'),
-			'fieldname': 'amount',
-			'fieldtype': 'Currency',
-			'options': 'currency',
-			'width': 100
-		}
 	]
 
-	if filters.get('group_by'):
-		columns.append({
-			'label': _('% Of Grand Total'),
-			'fieldname': 'percent_gt',
-			'fieldtype': 'Float',
-			'width': 80
-		})
+	if filters.get("group_by"):
+		columns.append(
+			{"label": _("% Of Grand Total"), "fieldname": "percent_gt", "fieldtype": "Float", "width": 80}
+		)
 
 	return columns
 
+
 def get_conditions(filters):
 	conditions = ""
 
-	for opts in (("company", " and company=%(company)s"),
+	for opts in (
+		("company", " and company=%(company)s"),
 		("customer", " and `tabSales Invoice`.customer = %(customer)s"),
 		("item_code", " and `tabSales Invoice Item`.item_code = %(item_code)s"),
 		("from_date", " and `tabSales Invoice`.posting_date>=%(from_date)s"),
-		("to_date", " and `tabSales Invoice`.posting_date<=%(to_date)s")):
-			if filters.get(opts[0]):
-				conditions += opts[1]
+		("to_date", " and `tabSales Invoice`.posting_date<=%(to_date)s"),
+	):
+		if filters.get(opts[0]):
+			conditions += opts[1]
 
 	if filters.get("mode_of_payment"):
 		conditions += """ and exists(select name from `tabSales Invoice Payment`
@@ -346,41 +338,45 @@
 				and ifnull(`tabSales Invoice Payment`.mode_of_payment, '') = %(mode_of_payment)s)"""
 
 	if filters.get("warehouse"):
-		conditions +=  """and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s"""
-
+		conditions += """and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s"""
 
 	if filters.get("brand"):
-		conditions +=  """and ifnull(`tabSales Invoice Item`.brand, '') = %(brand)s"""
+		conditions += """and ifnull(`tabSales Invoice Item`.brand, '') = %(brand)s"""
 
 	if filters.get("item_group"):
-		conditions +=  """and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s"""
+		conditions += """and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s"""
 
 	if not filters.get("group_by"):
-		conditions += "ORDER BY `tabSales Invoice`.posting_date desc, `tabSales Invoice Item`.item_group desc"
+		conditions += (
+			"ORDER BY `tabSales Invoice`.posting_date desc, `tabSales Invoice Item`.item_group desc"
+		)
 	else:
-		conditions += get_group_by_conditions(filters, 'Sales Invoice')
+		conditions += get_group_by_conditions(filters, "Sales Invoice")
 
 	return conditions
 
+
 def get_group_by_conditions(filters, doctype):
-	if filters.get("group_by") == 'Invoice':
+	if filters.get("group_by") == "Invoice":
 		return "ORDER BY `tab{0} Item`.parent desc".format(doctype)
-	elif filters.get("group_by") == 'Item':
+	elif filters.get("group_by") == "Item":
 		return "ORDER BY `tab{0} Item`.`item_code`".format(doctype)
-	elif filters.get("group_by") == 'Item Group':
-		return "ORDER BY `tab{0} Item`.{1}".format(doctype, frappe.scrub(filters.get('group_by')))
-	elif filters.get("group_by") in ('Customer', 'Customer Group', 'Territory', 'Supplier'):
-		return "ORDER BY `tab{0}`.{1}".format(doctype, frappe.scrub(filters.get('group_by')))
+	elif filters.get("group_by") == "Item Group":
+		return "ORDER BY `tab{0} Item`.{1}".format(doctype, frappe.scrub(filters.get("group_by")))
+	elif filters.get("group_by") in ("Customer", "Customer Group", "Territory", "Supplier"):
+		return "ORDER BY `tab{0}`.{1}".format(doctype, frappe.scrub(filters.get("group_by")))
+
 
 def get_items(filters, additional_query_columns):
 	conditions = get_conditions(filters)
 
 	if additional_query_columns:
-		additional_query_columns = ', ' + ', '.join(additional_query_columns)
+		additional_query_columns = ", " + ", ".join(additional_query_columns)
 	else:
-		additional_query_columns = ''
+		additional_query_columns = ""
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select
 			`tabSales Invoice Item`.name, `tabSales Invoice Item`.parent,
 			`tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to,
@@ -399,47 +395,75 @@
 		from `tabSales Invoice`, `tabSales Invoice Item`
 		where `tabSales Invoice`.name = `tabSales Invoice Item`.parent
 			and `tabSales Invoice`.docstatus = 1 {1}
-		""".format(additional_query_columns or '', conditions), filters, as_dict=1) #nosec
+		""".format(
+			additional_query_columns or "", conditions
+		),
+		filters,
+		as_dict=1,
+	)  # nosec
+
 
 def get_delivery_notes_against_sales_order(item_list):
 	so_dn_map = frappe._dict()
 	so_item_rows = list(set([d.so_detail for d in item_list]))
 
 	if so_item_rows:
-		delivery_notes = frappe.db.sql("""
+		delivery_notes = frappe.db.sql(
+			"""
 			select parent, so_detail
 			from `tabDelivery Note Item`
 			where docstatus=1 and so_detail in (%s)
 			group by so_detail, parent
-		""" % (', '.join(['%s']*len(so_item_rows))), tuple(so_item_rows), as_dict=1)
+		"""
+			% (", ".join(["%s"] * len(so_item_rows))),
+			tuple(so_item_rows),
+			as_dict=1,
+		)
 
 		for dn in delivery_notes:
 			so_dn_map.setdefault(dn.so_detail, []).append(dn.parent)
 
 	return so_dn_map
 
+
 def get_grand_total(filters, doctype):
 
-	return frappe.db.sql(""" SELECT
+	return frappe.db.sql(
+		""" SELECT
 		SUM(`tab{0}`.base_grand_total)
 		FROM `tab{0}`
 		WHERE `tab{0}`.docstatus = 1
 		and posting_date between %s and %s
-	""".format(doctype), (filters.get('from_date'), filters.get('to_date')))[0][0] #nosec
+	""".format(
+			doctype
+		),
+		(filters.get("from_date"), filters.get("to_date")),
+	)[0][
+		0
+	]  # nosec
 
-def get_deducted_taxes():
-	return frappe.db.sql_list("select name from `tabPurchase Taxes and Charges` where add_deduct_tax = 'Deduct'")
 
-def get_tax_accounts(item_list, columns, company_currency,
-		doctype='Sales Invoice', tax_doctype='Sales Taxes and Charges'):
+def get_tax_accounts(
+	item_list,
+	columns,
+	company_currency,
+	doctype="Sales Invoice",
+	tax_doctype="Sales Taxes and Charges",
+):
 	import json
+
 	item_row_map = {}
 	tax_columns = []
 	invoice_item_row = {}
 	itemised_tax = {}
+	add_deduct_tax = "charge_type"
 
-	tax_amount_precision = get_field_precision(frappe.get_meta(tax_doctype).get_field('tax_amount'),
-		currency=company_currency) or 2
+	tax_amount_precision = (
+		get_field_precision(
+			frappe.get_meta(tax_doctype).get_field("tax_amount"), currency=company_currency
+		)
+		or 2
+	)
 
 	for d in item_list:
 		invoice_item_row.setdefault(d.parent, []).append(d)
@@ -448,12 +472,13 @@
 	conditions = ""
 	if doctype == "Purchase Invoice":
 		conditions = " and category in ('Total', 'Valuation and Total') and base_tax_amount_after_discount_amount != 0"
+		add_deduct_tax = "add_deduct_tax"
 
-	deducted_tax = get_deducted_taxes()
-	tax_details = frappe.db.sql("""
+	tax_details = frappe.db.sql(
+		"""
 		select
 			name, parent, description, item_wise_tax_detail,
-			charge_type, base_tax_amount_after_discount_amount
+			charge_type, {add_deduct_tax}, base_tax_amount_after_discount_amount
 		from `tab%s`
 		where
 			parenttype = %s and docstatus = 1
@@ -461,10 +486,22 @@
 			and parent in (%s)
 			%s
 		order by description
-	""" % (tax_doctype, '%s', ', '.join(['%s']*len(invoice_item_row)), conditions),
-		tuple([doctype] + list(invoice_item_row)))
+	""".format(
+			add_deduct_tax=add_deduct_tax
+		)
+		% (tax_doctype, "%s", ", ".join(["%s"] * len(invoice_item_row)), conditions),
+		tuple([doctype] + list(invoice_item_row)),
+	)
 
-	for name, parent, description, item_wise_tax_detail, charge_type, tax_amount in tax_details:
+	for (
+		name,
+		parent,
+		description,
+		item_wise_tax_detail,
+		charge_type,
+		add_deduct_tax,
+		tax_amount,
+	) in tax_details:
 		description = handle_html(description)
 		if description not in tax_columns and tax_amount:
 			# as description is text editor earlier and markup can break the column convention in reports
@@ -483,151 +520,189 @@
 						tax_rate = tax_data
 						tax_amount = 0
 
-					if charge_type == 'Actual' and not tax_rate:
-						tax_rate = 'NA'
+					if charge_type == "Actual" and not tax_rate:
+						tax_rate = "NA"
 
-					item_net_amount = sum([flt(d.base_net_amount)
-						for d in item_row_map.get(parent, {}).get(item_code, [])])
+					item_net_amount = sum(
+						[flt(d.base_net_amount) for d in item_row_map.get(parent, {}).get(item_code, [])]
+					)
 
 					for d in item_row_map.get(parent, {}).get(item_code, []):
-						item_tax_amount = flt((tax_amount * d.base_net_amount) / item_net_amount) \
-							if item_net_amount else 0
+						item_tax_amount = (
+							flt((tax_amount * d.base_net_amount) / item_net_amount) if item_net_amount else 0
+						)
 						if item_tax_amount:
 							tax_value = flt(item_tax_amount, tax_amount_precision)
-							tax_value = (tax_value * -1
-								if (doctype == 'Purchase Invoice' and name in deducted_tax) else tax_value)
+							tax_value = (
+								tax_value * -1
+								if (doctype == "Purchase Invoice" and add_deduct_tax == "Deduct")
+								else tax_value
+							)
 
-							itemised_tax.setdefault(d.name, {})[description] = frappe._dict({
-								'tax_rate': tax_rate,
-								'tax_amount': tax_value
-							})
+							itemised_tax.setdefault(d.name, {})[description] = frappe._dict(
+								{"tax_rate": tax_rate, "tax_amount": tax_value}
+							)
 
 			except ValueError:
 				continue
-		elif charge_type == 'Actual' and tax_amount:
+		elif charge_type == "Actual" and tax_amount:
 			for d in invoice_item_row.get(parent, []):
-				itemised_tax.setdefault(d.name, {})[description] = frappe._dict({
-					'tax_rate': 'NA',
-					'tax_amount': flt((tax_amount * d.base_net_amount) / d.base_net_total,
-						tax_amount_precision)
-				})
+				itemised_tax.setdefault(d.name, {})[description] = frappe._dict(
+					{
+						"tax_rate": "NA",
+						"tax_amount": flt((tax_amount * d.base_net_amount) / d.base_net_total, tax_amount_precision),
+					}
+				)
 
 	tax_columns.sort()
 	for desc in tax_columns:
-		columns.append({
-			'label': _(desc + ' Rate'),
-			'fieldname': frappe.scrub(desc + ' Rate'),
-			'fieldtype': 'Float',
-			'width': 100
-		})
+		columns.append(
+			{
+				"label": _(desc + " Rate"),
+				"fieldname": frappe.scrub(desc + " Rate"),
+				"fieldtype": "Float",
+				"width": 100,
+			}
+		)
 
-		columns.append({
-			'label': _(desc + ' Amount'),
-			'fieldname': frappe.scrub(desc + ' Amount'),
-			'fieldtype': 'Currency',
-			'options': 'currency',
-			'width': 100
-		})
+		columns.append(
+			{
+				"label": _(desc + " Amount"),
+				"fieldname": frappe.scrub(desc + " Amount"),
+				"fieldtype": "Currency",
+				"options": "currency",
+				"width": 100,
+			}
+		)
 
 	columns += [
 		{
-			'label': _('Total Tax'),
-			'fieldname': 'total_tax',
-			'fieldtype': 'Currency',
-			'options': 'currency',
-			'width': 100
+			"label": _("Total Tax"),
+			"fieldname": "total_tax",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 100,
 		},
 		{
-			'label': _('Total'),
-			'fieldname': 'total',
-			'fieldtype': 'Currency',
-			'options': 'currency',
-			'width': 100
+			"label": _("Total"),
+			"fieldname": "total",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 100,
 		},
 		{
-			'fieldname': 'currency',
-			'label': _('Currency'),
-			'fieldtype': 'Currency',
-			'width': 80,
-			'hidden': 1
-		}
+			"fieldname": "currency",
+			"label": _("Currency"),
+			"fieldtype": "Currency",
+			"width": 80,
+			"hidden": 1,
+		},
 	]
 
 	return itemised_tax, tax_columns
 
-def add_total_row(data, filters, prev_group_by_value, item, total_row_map,
-	group_by_field, subtotal_display_field, grand_total, tax_columns):
-	if prev_group_by_value != item.get(group_by_field, ''):
+
+def add_total_row(
+	data,
+	filters,
+	prev_group_by_value,
+	item,
+	total_row_map,
+	group_by_field,
+	subtotal_display_field,
+	grand_total,
+	tax_columns,
+):
+	if prev_group_by_value != item.get(group_by_field, ""):
 		if prev_group_by_value:
 			total_row = total_row_map.get(prev_group_by_value)
 			data.append(total_row)
 			data.append({})
-			add_sub_total_row(total_row, total_row_map, 'total_row', tax_columns)
+			add_sub_total_row(total_row, total_row_map, "total_row", tax_columns)
 
-		prev_group_by_value = item.get(group_by_field, '')
+		prev_group_by_value = item.get(group_by_field, "")
 
-		total_row_map.setdefault(item.get(group_by_field, ''), {
-			subtotal_display_field: get_display_value(filters, group_by_field, item),
-			'stock_qty': 0.0,
-			'amount': 0.0,
-			'bold': 1,
-			'total_tax': 0.0,
-			'total': 0.0,
-			'percent_gt': 0.0
-		})
+		total_row_map.setdefault(
+			item.get(group_by_field, ""),
+			{
+				subtotal_display_field: get_display_value(filters, group_by_field, item),
+				"stock_qty": 0.0,
+				"amount": 0.0,
+				"bold": 1,
+				"total_tax": 0.0,
+				"total": 0.0,
+				"percent_gt": 0.0,
+			},
+		)
 
-		total_row_map.setdefault('total_row', {
-			subtotal_display_field: 'Total',
-			'stock_qty': 0.0,
-			'amount': 0.0,
-			'bold': 1,
-			'total_tax': 0.0,
-			'total': 0.0,
-			'percent_gt': 0.0
-		})
+		total_row_map.setdefault(
+			"total_row",
+			{
+				subtotal_display_field: "Total",
+				"stock_qty": 0.0,
+				"amount": 0.0,
+				"bold": 1,
+				"total_tax": 0.0,
+				"total": 0.0,
+				"percent_gt": 0.0,
+			},
+		)
 
 	return data, prev_group_by_value
 
+
 def get_display_value(filters, group_by_field, item):
-	if filters.get('group_by') == 'Item':
-		if item.get('item_code') != item.get('item_name'):
-			value =  cstr(item.get('item_code')) + "<br><br>" + \
-			"<span style='font-weight: normal'>" + cstr(item.get('item_name')) + "</span>"
+	if filters.get("group_by") == "Item":
+		if item.get("item_code") != item.get("item_name"):
+			value = (
+				cstr(item.get("item_code"))
+				+ "<br><br>"
+				+ "<span style='font-weight: normal'>"
+				+ cstr(item.get("item_name"))
+				+ "</span>"
+			)
 		else:
-			value =  item.get('item_code', '')
-	elif filters.get('group_by') in ('Customer', 'Supplier'):
-		party = frappe.scrub(filters.get('group_by'))
-		if item.get(party) != item.get(party+'_name'):
-			value = item.get(party) + "<br><br>" + \
-			"<span style='font-weight: normal'>" + item.get(party+'_name') + "</span>"
+			value = item.get("item_code", "")
+	elif filters.get("group_by") in ("Customer", "Supplier"):
+		party = frappe.scrub(filters.get("group_by"))
+		if item.get(party) != item.get(party + "_name"):
+			value = (
+				item.get(party)
+				+ "<br><br>"
+				+ "<span style='font-weight: normal'>"
+				+ item.get(party + "_name")
+				+ "</span>"
+			)
 		else:
-			value =  item.get(party)
+			value = item.get(party)
 	else:
 		value = item.get(group_by_field)
 
 	return value
 
+
 def get_group_by_and_display_fields(filters):
-	if filters.get('group_by') == 'Item':
-		group_by_field = 'item_code'
-		subtotal_display_field = 'invoice'
-	elif filters.get('group_by') == 'Invoice':
-		group_by_field = 'parent'
-		subtotal_display_field = 'item_code'
+	if filters.get("group_by") == "Item":
+		group_by_field = "item_code"
+		subtotal_display_field = "invoice"
+	elif filters.get("group_by") == "Invoice":
+		group_by_field = "parent"
+		subtotal_display_field = "item_code"
 	else:
-		group_by_field = frappe.scrub(filters.get('group_by'))
-		subtotal_display_field = 'item_code'
+		group_by_field = frappe.scrub(filters.get("group_by"))
+		subtotal_display_field = "item_code"
 
 	return group_by_field, subtotal_display_field
 
+
 def add_sub_total_row(item, total_row_map, group_by_value, tax_columns):
 	total_row = total_row_map.get(group_by_value)
-	total_row['stock_qty'] += item['stock_qty']
-	total_row['amount'] += item['amount']
-	total_row['total_tax'] += item['total_tax']
-	total_row['total'] += item['total']
-	total_row['percent_gt'] += item['percent_gt']
+	total_row["stock_qty"] += item["stock_qty"]
+	total_row["amount"] += item["amount"]
+	total_row["total_tax"] += item["total_tax"]
+	total_row["total"] += item["total"]
+	total_row["percent_gt"] += item["percent_gt"]
 
 	for tax in tax_columns:
-		total_row.setdefault(frappe.scrub(tax + ' Amount'), 0.0)
-		total_row[frappe.scrub(tax + ' Amount')] += flt(item[frappe.scrub(tax + ' Amount')])
+		total_row.setdefault(frappe.scrub(tax + " Amount"), 0.0)
+		total_row[frappe.scrub(tax + " Amount")] += flt(item[frappe.scrub(tax + " Amount")])
diff --git a/erpnext/accounts/report/non_billed_report.py b/erpnext/accounts/report/non_billed_report.py
index a421bc5..39c5311 100644
--- a/erpnext/accounts/report/non_billed_report.py
+++ b/erpnext/accounts/report/non_billed_report.py
@@ -9,14 +9,19 @@
 
 
 def get_ordered_to_be_billed_data(args):
-	doctype, party = args.get('doctype'), args.get('party')
+	doctype, party = args.get("doctype"), args.get("party")
 	child_tab = doctype + " Item"
-	precision = get_field_precision(frappe.get_meta(child_tab).get_field("billed_amt"),
-		currency=get_default_currency()) or 2
+	precision = (
+		get_field_precision(
+			frappe.get_meta(child_tab).get_field("billed_amt"), currency=get_default_currency()
+		)
+		or 2
+	)
 
 	project_field = get_project_field(doctype, party)
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		Select
 			`{parent_tab}`.name, `{parent_tab}`.{date_field},
 			`{parent_tab}`.{party}, `{parent_tab}`.{party}_name,
@@ -40,9 +45,20 @@
 			(`{child_tab}`.base_rate * ifnull(`{child_tab}`.returned_qty, 0))) > 0
 		order by
 			`{parent_tab}`.{order} {order_by}
-		""".format(parent_tab = 'tab' + doctype, child_tab = 'tab' + child_tab, precision= precision, party = party,
-			date_field = args.get('date'), project_field = project_field, order= args.get('order'), order_by = args.get('order_by')))
+		""".format(
+			parent_tab="tab" + doctype,
+			child_tab="tab" + child_tab,
+			precision=precision,
+			party=party,
+			date_field=args.get("date"),
+			project_field=project_field,
+			order=args.get("order"),
+			order_by=args.get("order_by"),
+		)
+	)
+
 
 def get_project_field(doctype, party):
-	if party == "supplier": doctype = doctype + ' Item'
-	return "`tab%s`.project"%(doctype)
+	if party == "supplier":
+		doctype = doctype + " Item"
+	return "`tab%s`.project" % (doctype)
diff --git a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
index 6c12093..3f178f4 100644
--- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
+++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
@@ -28,21 +28,28 @@
 		else:
 			payment_amount = flt(d.credit) or -1 * flt(d.debit)
 
-		d.update({
-			"range1": 0,
-			"range2": 0,
-			"range3": 0,
-			"range4": 0,
-			"outstanding": payment_amount
-		})
+		d.update({"range1": 0, "range2": 0, "range3": 0, "range4": 0, "outstanding": payment_amount})
 
 		if d.against_voucher:
 			ReceivablePayableReport(filters).get_ageing_data(invoice.posting_date, d)
 
 		row = [
-			d.voucher_type, d.voucher_no, d.party_type, d.party, d.posting_date, d.against_voucher,
-			invoice.posting_date, invoice.due_date, d.debit, d.credit, d.remarks,
-			d.age, d.range1, d.range2, d.range3, d.range4
+			d.voucher_type,
+			d.voucher_no,
+			d.party_type,
+			d.party,
+			d.posting_date,
+			d.against_voucher,
+			invoice.posting_date,
+			invoice.due_date,
+			d.debit,
+			d.credit,
+			d.remarks,
+			d.age,
+			d.range1,
+			d.range2,
+			d.range3,
+			d.range4,
 		]
 
 		if invoice.due_date:
@@ -52,11 +59,17 @@
 
 	return columns, data
 
+
 def validate_filters(filters):
-	if (filters.get("payment_type") == _("Incoming") and filters.get("party_type") == "Supplier") or \
-		(filters.get("payment_type") == _("Outgoing") and filters.get("party_type") == "Customer"):
-			frappe.throw(_("{0} payment entries can not be filtered by {1}")\
-				.format(filters.payment_type, filters.party_type))
+	if (filters.get("payment_type") == _("Incoming") and filters.get("party_type") == "Supplier") or (
+		filters.get("payment_type") == _("Outgoing") and filters.get("party_type") == "Customer"
+	):
+		frappe.throw(
+			_("{0} payment entries can not be filtered by {1}").format(
+				filters.payment_type, filters.party_type
+			)
+		)
+
 
 def get_columns(filters):
 	return [
@@ -64,109 +77,57 @@
 			"fieldname": "payment_document",
 			"label": _("Payment Document Type"),
 			"fieldtype": "Data",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"fieldname": "payment_entry",
 			"label": _("Payment Document"),
 			"fieldtype": "Dynamic Link",
 			"options": "payment_document",
-			"width": 160
+			"width": 160,
 		},
-		{
-			"fieldname": "party_type",
-			"label": _("Party Type"),
-			"fieldtype": "Data",
-			"width": 100
-		},
+		{"fieldname": "party_type", "label": _("Party Type"), "fieldtype": "Data", "width": 100},
 		{
 			"fieldname": "party",
 			"label": _("Party"),
 			"fieldtype": "Dynamic Link",
 			"options": "party_type",
-			"width": 160
+			"width": 160,
 		},
-		{
-			"fieldname": "posting_date",
-			"label": _("Posting Date"),
-			"fieldtype": "Date",
-			"width": 100
-		},
+		{"fieldname": "posting_date", "label": _("Posting Date"), "fieldtype": "Date", "width": 100},
 		{
 			"fieldname": "invoice",
 			"label": _("Invoice"),
 			"fieldtype": "Link",
-			"options": "Purchase Invoice" if filters.get("payment_type") == _("Outgoing") else "Sales Invoice",
-			"width": 160
+			"options": "Purchase Invoice"
+			if filters.get("payment_type") == _("Outgoing")
+			else "Sales Invoice",
+			"width": 160,
 		},
 		{
 			"fieldname": "invoice_posting_date",
 			"label": _("Invoice Posting Date"),
 			"fieldtype": "Date",
-			"width": 100
+			"width": 100,
 		},
+		{"fieldname": "due_date", "label": _("Payment Due Date"), "fieldtype": "Date", "width": 100},
+		{"fieldname": "debit", "label": _("Debit"), "fieldtype": "Currency", "width": 140},
+		{"fieldname": "credit", "label": _("Credit"), "fieldtype": "Currency", "width": 140},
+		{"fieldname": "remarks", "label": _("Remarks"), "fieldtype": "Data", "width": 200},
+		{"fieldname": "age", "label": _("Age"), "fieldtype": "Int", "width": 50},
+		{"fieldname": "range1", "label": _("0-30"), "fieldtype": "Currency", "width": 140},
+		{"fieldname": "range2", "label": _("30-60"), "fieldtype": "Currency", "width": 140},
+		{"fieldname": "range3", "label": _("60-90"), "fieldtype": "Currency", "width": 140},
+		{"fieldname": "range4", "label": _("90 Above"), "fieldtype": "Currency", "width": 140},
 		{
-			"fieldname": "due_date",
-			"label": _("Payment Due Date"),
-			"fieldtype": "Date",
-			"width": 100
-		},
-		{
-			"fieldname": "debit",
-			"label": _("Debit"),
-			"fieldtype": "Currency",
-			"width": 140
-		},
-		{
-			"fieldname": "credit",
-			"label": _("Credit"),
-			"fieldtype": "Currency",
-			"width": 140
-		},
-		{
-			"fieldname": "remarks",
-			"label": _("Remarks"),
-			"fieldtype": "Data",
-			"width": 200
-		},
-		{
-			"fieldname": "age",
-			"label": _("Age"),
-			"fieldtype": "Int",
-			"width": 50
-		},
-		{
-			"fieldname": "range1",
-			"label": "0-30",
-			"fieldtype": "Currency",
-			"width": 140
-		},
-		{
-			"fieldname": "range2",
-			"label": "30-60",
-			"fieldtype": "Currency",
-			"width": 140
-		},
-		{
-			"fieldname": "range3",
-			"label": "60-90",
-			"fieldtype": "Currency",
-			"width": 140
-		},
-		{
-			"fieldname": "range4",
-			"label": _("90 Above"),
-			"fieldtype": "Currency",
-			"width": 140
-		},
-			{
 			"fieldname": "delay_in_payment",
 			"label": _("Delay in payment (Days)"),
 			"fieldtype": "Int",
-			"width": 100
-		}
+			"width": 100,
+		},
 	]
 
+
 def get_conditions(filters):
 	conditions = []
 
@@ -184,7 +145,9 @@
 
 	if filters.party_type:
 		conditions.append("against_voucher_type=%(reference_type)s")
-		filters["reference_type"] = "Sales Invoice" if filters.party_type=="Customer" else "Purchase Invoice"
+		filters["reference_type"] = (
+			"Sales Invoice" if filters.party_type == "Customer" else "Purchase Invoice"
+		)
 
 	if filters.get("from_date"):
 		conditions.append("posting_date >= %(from_date)s")
@@ -194,12 +157,20 @@
 
 	return "and " + " and ".join(conditions) if conditions else ""
 
+
 def get_entries(filters):
-	return frappe.db.sql("""select
+	return frappe.db.sql(
+		"""select
 		voucher_type, voucher_no, party_type, party, posting_date, debit, credit, remarks, against_voucher
 		from `tabGL Entry`
 		where company=%(company)s and voucher_type in ('Journal Entry', 'Payment Entry') {0}
-	""".format(get_conditions(filters)), filters, as_dict=1)
+	""".format(
+			get_conditions(filters)
+		),
+		filters,
+		as_dict=1,
+	)
+
 
 def get_invoice_posting_date_map(filters):
 	invoice_details = {}
diff --git a/erpnext/accounts/report/pos_register/pos_register.py b/erpnext/accounts/report/pos_register/pos_register.py
index 77e7568..9c0aba3 100644
--- a/erpnext/accounts/report/pos_register/pos_register.py
+++ b/erpnext/accounts/report/pos_register/pos_register.py
@@ -37,11 +37,14 @@
 		add_subtotal_row(grouped_data, invoices, group_by_field, key)
 
 	# move group by column to first position
-	column_index = next((index for (index, d) in enumerate(columns) if d["fieldname"] == group_by_field), None)
+	column_index = next(
+		(index for (index, d) in enumerate(columns) if d["fieldname"] == group_by_field), None
+	)
 	columns.insert(0, columns.pop(column_index))
 
 	return columns, grouped_data
 
+
 def get_pos_entries(filters, group_by_field):
 	conditions = get_conditions(filters)
 	order_by = "p.posting_date"
@@ -59,7 +62,7 @@
 		"""
 		SELECT
 			p.posting_date, p.name as pos_invoice, p.pos_profile,
-			p.owner, p.base_grand_total as grand_total, p.base_paid_amount as paid_amount,
+			p.owner, p.base_grand_total as grand_total, p.base_paid_amount - p.change_amount  as paid_amount,
 			p.customer, p.is_return {select_mop_field}
 		FROM
 			`tabPOS Invoice` p {from_sales_invoice_payment}
@@ -74,8 +77,12 @@
 			from_sales_invoice_payment=from_sales_invoice_payment,
 			group_by_mop_condition=group_by_mop_condition,
 			conditions=conditions,
-			order_by=order_by
-		), filters, as_dict=1)
+			order_by=order_by,
+		),
+		filters,
+		as_dict=1,
+	)
+
 
 def concat_mode_of_payments(pos_entries):
 	mode_of_payments = get_mode_of_payments(set(d.pos_invoice for d in pos_entries))
@@ -83,41 +90,50 @@
 		if mode_of_payments.get(entry.pos_invoice):
 			entry.mode_of_payment = ", ".join(mode_of_payments.get(entry.pos_invoice, []))
 
+
 def add_subtotal_row(data, group_invoices, group_by_field, group_by_value):
 	grand_total = sum(d.grand_total for d in group_invoices)
 	paid_amount = sum(d.paid_amount for d in group_invoices)
-	data.append({
-		group_by_field: group_by_value,
-		"grand_total": grand_total,
-		"paid_amount": paid_amount,
-		"bold": 1
-	})
+	data.append(
+		{
+			group_by_field: group_by_value,
+			"grand_total": grand_total,
+			"paid_amount": paid_amount,
+			"bold": 1,
+		}
+	)
 	data.append({})
 
+
 def validate_filters(filters):
 	if not filters.get("company"):
 		frappe.throw(_("{0} is mandatory").format(_("Company")))
 
 	if not filters.get("from_date") and not filters.get("to_date"):
-		frappe.throw(_("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date"))))
+		frappe.throw(
+			_("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date")))
+		)
 
 	if filters.from_date > filters.to_date:
 		frappe.throw(_("From Date must be before To Date"))
 
-	if (filters.get("pos_profile") and filters.get("group_by") == _('POS Profile')):
+	if filters.get("pos_profile") and filters.get("group_by") == _("POS Profile"):
 		frappe.throw(_("Can not filter based on POS Profile, if grouped by POS Profile"))
 
-	if (filters.get("customer") and filters.get("group_by") == _('Customer')):
+	if filters.get("customer") and filters.get("group_by") == _("Customer"):
 		frappe.throw(_("Can not filter based on Customer, if grouped by Customer"))
 
-	if (filters.get("owner") and filters.get("group_by") == _('Cashier')):
+	if filters.get("owner") and filters.get("group_by") == _("Cashier"):
 		frappe.throw(_("Can not filter based on Cashier, if grouped by Cashier"))
 
-	if (filters.get("mode_of_payment") and filters.get("group_by") == _('Payment Method')):
+	if filters.get("mode_of_payment") and filters.get("group_by") == _("Payment Method"):
 		frappe.throw(_("Can not filter based on Payment Method, if grouped by Payment Method"))
 
+
 def get_conditions(filters):
-	conditions = "company = %(company)s AND posting_date >= %(from_date)s AND posting_date <= %(to_date)s"
+	conditions = (
+		"company = %(company)s AND posting_date >= %(from_date)s AND posting_date <= %(to_date)s"
+	)
 
 	if filters.get("pos_profile"):
 		conditions += " AND pos_profile = %(pos_profile)s"
@@ -140,6 +156,7 @@
 
 	return conditions
 
+
 def get_group_by_field(group_by):
 	group_by_field = ""
 
@@ -154,68 +171,59 @@
 
 	return group_by_field
 
+
 def get_columns(filters):
 	columns = [
-		{
-			"label": _("Posting Date"),
-			"fieldname": "posting_date",
-			"fieldtype": "Date",
-			"width": 90
-		},
+		{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 90},
 		{
 			"label": _("POS Invoice"),
 			"fieldname": "pos_invoice",
 			"fieldtype": "Link",
 			"options": "POS Invoice",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("Customer"),
 			"fieldname": "customer",
 			"fieldtype": "Link",
 			"options": "Customer",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("POS Profile"),
 			"fieldname": "pos_profile",
 			"fieldtype": "Link",
 			"options": "POS Profile",
-			"width": 160
+			"width": 160,
 		},
 		{
 			"label": _("Cashier"),
 			"fieldname": "owner",
 			"fieldtype": "Link",
 			"options": "User",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Grand Total"),
 			"fieldname": "grand_total",
 			"fieldtype": "Currency",
 			"options": "company:currency",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("Paid Amount"),
 			"fieldname": "paid_amount",
 			"fieldtype": "Currency",
 			"options": "company:currency",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("Payment Method"),
 			"fieldname": "mode_of_payment",
 			"fieldtype": "Data",
-			"width": 150
+			"width": 150,
 		},
-		{
-			"label": _("Is Return"),
-			"fieldname": "is_return",
-			"fieldtype": "Data",
-			"width": 80
-		},
+		{"label": _("Is Return"), "fieldname": "is_return", "fieldtype": "Data", "width": 80},
 	]
 
 	return columns
diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py
index 882e411..6635335 100644
--- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py
+++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py
@@ -15,19 +15,41 @@
 
 
 def execute(filters=None):
-	period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
-		filters.period_start_date, filters.period_end_date, filters.filter_based_on, filters.periodicity,
-		company=filters.company)
+	period_list = get_period_list(
+		filters.from_fiscal_year,
+		filters.to_fiscal_year,
+		filters.period_start_date,
+		filters.period_end_date,
+		filters.filter_based_on,
+		filters.periodicity,
+		company=filters.company,
+	)
 
-	income = get_data(filters.company, "Income", "Credit", period_list, filters = filters,
+	income = get_data(
+		filters.company,
+		"Income",
+		"Credit",
+		period_list,
+		filters=filters,
 		accumulated_values=filters.accumulated_values,
-		ignore_closing_entries=True, ignore_accumulated_values_for_fy= True)
+		ignore_closing_entries=True,
+		ignore_accumulated_values_for_fy=True,
+	)
 
-	expense = get_data(filters.company, "Expense", "Debit", period_list, filters=filters,
+	expense = get_data(
+		filters.company,
+		"Expense",
+		"Debit",
+		period_list,
+		filters=filters,
 		accumulated_values=filters.accumulated_values,
-		ignore_closing_entries=True, ignore_accumulated_values_for_fy= True)
+		ignore_closing_entries=True,
+		ignore_accumulated_values_for_fy=True,
+	)
 
-	net_profit_loss = get_net_profit_loss(income, expense, period_list, filters.company, filters.presentation_currency)
+	net_profit_loss = get_net_profit_loss(
+		income, expense, period_list, filters.company, filters.presentation_currency
+	)
 
 	data = []
 	data.extend(income or [])
@@ -35,20 +57,29 @@
 	if net_profit_loss:
 		data.append(net_profit_loss)
 
-	columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company)
+	columns = get_columns(
+		filters.periodicity, period_list, filters.accumulated_values, filters.company
+	)
 
 	chart = get_chart_data(filters, columns, income, expense, net_profit_loss)
 
-	currency = filters.presentation_currency or frappe.get_cached_value('Company', filters.company, "default_currency")
-	report_summary = get_report_summary(period_list, filters.periodicity, income, expense, net_profit_loss, currency, filters)
+	currency = filters.presentation_currency or frappe.get_cached_value(
+		"Company", filters.company, "default_currency"
+	)
+	report_summary = get_report_summary(
+		period_list, filters.periodicity, income, expense, net_profit_loss, currency, filters
+	)
 
 	return columns, data, None, chart, report_summary
 
-def get_report_summary(period_list, periodicity, income, expense, net_profit_loss, currency, filters, consolidated=False):
+
+def get_report_summary(
+	period_list, periodicity, income, expense, net_profit_loss, currency, filters, consolidated=False
+):
 	net_income, net_expense, net_profit = 0.0, 0.0, 0.0
 
 	# from consolidated financial statement
-	if filters.get('accumulated_in_group_company'):
+	if filters.get("accumulated_in_group_company"):
 		period_list = get_filtered_list_for_consolidated_report(filters, period_list)
 
 	for period in period_list:
@@ -60,37 +91,27 @@
 		if net_profit_loss:
 			net_profit += net_profit_loss.get(key)
 
-	if (len(period_list) == 1 and periodicity== 'Yearly'):
-			profit_label = _("Profit This Year")
-			income_label = _("Total Income This Year")
-			expense_label = _("Total Expense This Year")
+	if len(period_list) == 1 and periodicity == "Yearly":
+		profit_label = _("Profit This Year")
+		income_label = _("Total Income This Year")
+		expense_label = _("Total Expense This Year")
 	else:
 		profit_label = _("Net Profit")
 		income_label = _("Total Income")
 		expense_label = _("Total Expense")
 
 	return [
-		{
-			"value": net_income,
-			"label": income_label,
-			"datatype": "Currency",
-			"currency": currency
-		},
-		{ "type": "separator", "value": "-"},
-		{
-			"value": net_expense,
-			"label": expense_label,
-			"datatype": "Currency",
-			"currency": currency
-		},
-		{ "type": "separator", "value": "=", "color": "blue"},
+		{"value": net_income, "label": income_label, "datatype": "Currency", "currency": currency},
+		{"type": "separator", "value": "-"},
+		{"value": net_expense, "label": expense_label, "datatype": "Currency", "currency": currency},
+		{"type": "separator", "value": "=", "color": "blue"},
 		{
 			"value": net_profit,
 			"indicator": "Green" if net_profit > 0 else "Red",
 			"label": profit_label,
 			"datatype": "Currency",
-			"currency": currency
-		}
+			"currency": currency,
+		},
 	]
 
 
@@ -100,7 +121,7 @@
 		"account_name": "'" + _("Profit for the year") + "'",
 		"account": "'" + _("Profit for the year") + "'",
 		"warn_if_negative": True,
-		"currency": currency or frappe.get_cached_value('Company',  company,  "default_currency")
+		"currency": currency or frappe.get_cached_value("Company", company, "default_currency"),
 	}
 
 	has_value = False
@@ -113,7 +134,7 @@
 		net_profit_loss[key] = total_income - total_expense
 
 		if net_profit_loss[key]:
-			has_value=True
+			has_value = True
 
 		total += flt(net_profit_loss[key])
 		net_profit_loss["total"] = total
@@ -121,6 +142,7 @@
 	if has_value:
 		return net_profit_loss
 
+
 def get_chart_data(filters, columns, income, expense, net_profit_loss):
 	labels = [d.get("label") for d in columns[2:]]
 
@@ -136,18 +158,13 @@
 
 	datasets = []
 	if income_data:
-		datasets.append({'name': _('Income'), 'values': income_data})
+		datasets.append({"name": _("Income"), "values": income_data})
 	if expense_data:
-		datasets.append({'name': _('Expense'), 'values': expense_data})
+		datasets.append({"name": _("Expense"), "values": expense_data})
 	if net_profit:
-		datasets.append({'name': _('Net Profit/Loss'), 'values': net_profit})
+		datasets.append({"name": _("Net Profit/Loss"), "values": net_profit})
 
-	chart = {
-		"data": {
-			'labels': labels,
-			'datasets': datasets
-		}
-	}
+	chart = {"data": {"labels": labels, "datasets": datasets}}
 
 	if not filters.accumulated_values:
 		chart["type"] = "bar"
diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
index f4b8731..183e279 100644
--- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
+++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
@@ -14,31 +14,39 @@
 
 value_fields = ("income", "expense", "gross_profit_loss")
 
-def execute(filters=None):
-	if not filters.get('based_on'): filters["based_on"] = 'Cost Center'
 
-	based_on = filters.based_on.replace(' ', '_').lower()
+def execute(filters=None):
+	if not filters.get("based_on"):
+		filters["based_on"] = "Cost Center"
+
+	based_on = filters.based_on.replace(" ", "_").lower()
 	validate_filters(filters)
 	accounts = get_accounts_data(based_on, filters.get("company"))
 	data = get_data(accounts, filters, based_on)
 	columns = get_columns(filters)
 	return columns, data
 
+
 def get_accounts_data(based_on, company):
-	if based_on == 'cost_center':
-		return frappe.db.sql("""select name, parent_cost_center as parent_account, cost_center_name as account_name, lft, rgt
-			from `tabCost Center` where company=%s order by name""", company, as_dict=True)
-	elif based_on == 'project':
-		return frappe.get_all('Project', fields = ["name"], filters = {'company': company}, order_by = 'name')
+	if based_on == "cost_center":
+		return frappe.db.sql(
+			"""select name, parent_cost_center as parent_account, cost_center_name as account_name, lft, rgt
+			from `tabCost Center` where company=%s order by name""",
+			company,
+			as_dict=True,
+		)
+	elif based_on == "project":
+		return frappe.get_all("Project", fields=["name"], filters={"company": company}, order_by="name")
 	else:
 		filters = {}
 		doctype = frappe.unscrub(based_on)
-		has_company = frappe.db.has_column(doctype, 'company')
+		has_company = frappe.db.has_column(doctype, "company")
 
 		if has_company:
-			filters.update({'company': company})
+			filters.update({"company": company})
 
-		return frappe.get_all(doctype, fields = ["name"], filters = filters, order_by = 'name')
+		return frappe.get_all(doctype, fields=["name"], filters=filters, order_by="name")
+
 
 def get_data(accounts, filters, based_on):
 	if not accounts:
@@ -48,24 +56,28 @@
 
 	gl_entries_by_account = {}
 
-	set_gl_entries_by_account(filters.get("company"), filters.get("from_date"),
-		filters.get("to_date"), based_on, gl_entries_by_account, ignore_closing_entries=not flt(filters.get("with_period_closing_entry")))
+	set_gl_entries_by_account(
+		filters.get("company"),
+		filters.get("from_date"),
+		filters.get("to_date"),
+		based_on,
+		gl_entries_by_account,
+		ignore_closing_entries=not flt(filters.get("with_period_closing_entry")),
+	)
 
 	total_row = calculate_values(accounts, gl_entries_by_account, filters)
 	accumulate_values_into_parents(accounts, accounts_by_name)
 
 	data = prepare_data(accounts, filters, total_row, parent_children_map, based_on)
-	data = filter_out_zero_value_rows(data, parent_children_map,
-		show_zero_values=filters.get("show_zero_values"))
+	data = filter_out_zero_value_rows(
+		data, parent_children_map, show_zero_values=filters.get("show_zero_values")
+	)
 
 	return data
 
+
 def calculate_values(accounts, gl_entries_by_account, filters):
-	init = {
-		"income": 0.0,
-		"expense": 0.0,
-		"gross_profit_loss": 0.0
-	}
+	init = {"income": 0.0, "expense": 0.0, "gross_profit_loss": 0.0}
 
 	total_row = {
 		"cost_center": None,
@@ -77,7 +89,7 @@
 		"account": "'" + _("Total") + "'",
 		"parent_account": None,
 		"indent": 0,
-		"has_value": True
+		"has_value": True,
 	}
 
 	for d in accounts:
@@ -87,9 +99,9 @@
 
 		for entry in gl_entries_by_account.get(d.name, []):
 			if cstr(entry.is_opening) != "Yes":
-				if entry.type == 'Income':
+				if entry.type == "Income":
 					d["income"] += flt(entry.credit) - flt(entry.debit)
-				if entry.type == 'Expense':
+				if entry.type == "Expense":
 					d["expense"] += flt(entry.debit) - flt(entry.credit)
 
 				d["gross_profit_loss"] = d.get("income") - d.get("expense")
@@ -101,15 +113,17 @@
 
 	return total_row
 
+
 def accumulate_values_into_parents(accounts, accounts_by_name):
 	for d in reversed(accounts):
 		if d.parent_account:
 			for key in value_fields:
 				accounts_by_name[d.parent_account][key] += d[key]
 
+
 def prepare_data(accounts, filters, total_row, parent_children_map, based_on):
 	data = []
-	company_currency = frappe.get_cached_value('Company',  filters.get("company"),  "default_currency")
+	company_currency = frappe.get_cached_value("Company", filters.get("company"), "default_currency")
 
 	for d in accounts:
 		has_value = False
@@ -120,7 +134,7 @@
 			"indent": d.indent,
 			"fiscal_year": filters.get("fiscal_year"),
 			"currency": company_currency,
-			"based_on": based_on
+			"based_on": based_on,
 		}
 
 		for key in value_fields:
@@ -133,10 +147,11 @@
 		row["has_value"] = has_value
 		data.append(row)
 
-	data.extend([{},total_row])
+	data.extend([{}, total_row])
 
 	return data
 
+
 def get_columns(filters):
 	return [
 		{
@@ -144,43 +159,42 @@
 			"label": _(filters.get("based_on")),
 			"fieldtype": "Link",
 			"options": filters.get("based_on"),
-			"width": 300
+			"width": 300,
 		},
 		{
 			"fieldname": "currency",
 			"label": _("Currency"),
 			"fieldtype": "Link",
 			"options": "Currency",
-			"hidden": 1
+			"hidden": 1,
 		},
 		{
 			"fieldname": "income",
 			"label": _("Income"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 305
-
+			"width": 305,
 		},
 		{
 			"fieldname": "expense",
 			"label": _("Expense"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 305
-
+			"width": 305,
 		},
 		{
 			"fieldname": "gross_profit_loss",
 			"label": _("Gross Profit / Loss"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 307
-
-		}
+			"width": 307,
+		},
 	]
 
-def set_gl_entries_by_account(company, from_date, to_date, based_on, gl_entries_by_account,
-		ignore_closing_entries=False):
+
+def set_gl_entries_by_account(
+	company, from_date, to_date, based_on, gl_entries_by_account, ignore_closing_entries=False
+):
 	"""Returns a dict like { "account": [gl entries], ... }"""
 	additional_conditions = []
 
@@ -190,19 +204,20 @@
 	if from_date:
 		additional_conditions.append("and posting_date >= %(from_date)s")
 
-	gl_entries = frappe.db.sql("""select posting_date, {based_on} as based_on, debit, credit,
+	gl_entries = frappe.db.sql(
+		"""select posting_date, {based_on} as based_on, debit, credit,
 		is_opening, (select root_type from `tabAccount` where name = account) as type
 		from `tabGL Entry` where company=%(company)s
 		{additional_conditions}
 		and posting_date <= %(to_date)s
 		and {based_on} is not null
-		order by {based_on}, posting_date""".format(additional_conditions="\n".join(additional_conditions), based_on= based_on),
-		{
-			"company": company,
-			"from_date": from_date,
-			"to_date": to_date
-		},
-		as_dict=True)
+		and is_cancelled = 0
+		order by {based_on}, posting_date""".format(
+			additional_conditions="\n".join(additional_conditions), based_on=based_on
+		),
+		{"company": company, "from_date": from_date, "to_date": to_date},
+		as_dict=True,
+	)
 
 	for entry in gl_entries:
 		gl_entries_by_account.setdefault(entry.based_on, []).append(entry)
diff --git a/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py b/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py
index 406f7a5..8af9bb3 100644
--- a/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py
+++ b/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py
@@ -6,7 +6,8 @@
 
 
 def execute(filters=None):
-	if not filters: filters ={}
+	if not filters:
+		filters = {}
 	data = []
 	conditions = get_columns(filters, "Purchase Invoice")
 	data = get_data(filters, conditions)
diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py
index a9696bd..e8a1e79 100644
--- a/erpnext/accounts/report/purchase_register/purchase_register.py
+++ b/erpnext/accounts/report/purchase_register/purchase_register.py
@@ -15,12 +15,15 @@
 def execute(filters=None):
 	return _execute(filters)
 
+
 def _execute(filters=None, additional_table_columns=None, additional_query_columns=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	invoice_list = get_invoices(filters, additional_query_columns)
-	columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts \
-		= get_columns(invoice_list, additional_table_columns)
+	columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts = get_columns(
+		invoice_list, additional_table_columns
+	)
 
 	if not invoice_list:
 		msgprint(_("No record found"))
@@ -28,13 +31,14 @@
 
 	invoice_expense_map = get_invoice_expense_map(invoice_list)
 	internal_invoice_map = get_internal_invoice_map(invoice_list)
-	invoice_expense_map, invoice_tax_map = get_invoice_tax_map(invoice_list,
-		invoice_expense_map, expense_accounts)
+	invoice_expense_map, invoice_tax_map = get_invoice_tax_map(
+		invoice_list, invoice_expense_map, expense_accounts
+	)
 	invoice_po_pr_map = get_invoice_po_pr_map(invoice_list)
 	suppliers = list(set(d.supplier for d in invoice_list))
 	supplier_details = get_supplier_details(suppliers)
 
-	company_currency = frappe.get_cached_value('Company',  filters.company,  "default_currency")
+	company_currency = frappe.get_cached_value("Company", filters.company, "default_currency")
 
 	data = []
 	for inv in invoice_list:
@@ -50,10 +54,17 @@
 				row.append(inv.get(col))
 
 		row += [
-			supplier_details.get(inv.supplier), # supplier_group
-			inv.tax_id, inv.credit_to, inv.mode_of_payment, ", ".join(project),
-			inv.bill_no, inv.bill_date, inv.remarks,
-			", ".join(purchase_order), ", ".join(purchase_receipt), company_currency
+			supplier_details.get(inv.supplier),  # supplier_group
+			inv.tax_id,
+			inv.credit_to,
+			inv.mode_of_payment,
+			", ".join(project),
+			inv.bill_no,
+			inv.bill_date,
+			inv.remarks,
+			", ".join(purchase_order),
+			", ".join(purchase_receipt),
+			company_currency,
 		]
 
 		# map expense values
@@ -91,85 +102,117 @@
 def get_columns(invoice_list, additional_table_columns):
 	"""return columns based on filters"""
 	columns = [
-		_("Invoice") + ":Link/Purchase Invoice:120", _("Posting Date") + ":Date:80",
-		_("Supplier Id") + "::120", _("Supplier Name") + "::120"]
+		_("Invoice") + ":Link/Purchase Invoice:120",
+		_("Posting Date") + ":Date:80",
+		_("Supplier Id") + "::120",
+		_("Supplier Name") + "::120",
+	]
 
 	if additional_table_columns:
 		columns += additional_table_columns
 
 	columns += [
-		_("Supplier Group") + ":Link/Supplier Group:120", _("Tax Id") + "::80", _("Payable Account") + ":Link/Account:120",
-		_("Mode of Payment") + ":Link/Mode of Payment:80", _("Project") + ":Link/Project:80",
-		_("Bill No") + "::120", _("Bill Date") + ":Date:80", _("Remarks") + "::150",
+		_("Supplier Group") + ":Link/Supplier Group:120",
+		_("Tax Id") + "::80",
+		_("Payable Account") + ":Link/Account:120",
+		_("Mode of Payment") + ":Link/Mode of Payment:80",
+		_("Project") + ":Link/Project:80",
+		_("Bill No") + "::120",
+		_("Bill Date") + ":Date:80",
+		_("Remarks") + "::150",
 		_("Purchase Order") + ":Link/Purchase Order:100",
 		_("Purchase Receipt") + ":Link/Purchase Receipt:100",
-		{
-			"fieldname": "currency",
-			"label": _("Currency"),
-			"fieldtype": "Data",
-			"width": 80
-		}
+		{"fieldname": "currency", "label": _("Currency"), "fieldtype": "Data", "width": 80},
 	]
-	expense_accounts = tax_accounts = expense_columns = tax_columns = unrealized_profit_loss_accounts = \
-		unrealized_profit_loss_account_columns = []
+
+	expense_accounts = []
+	tax_accounts = []
+	unrealized_profit_loss_accounts = []
 
 	if invoice_list:
-		expense_accounts = frappe.db.sql_list("""select distinct expense_account
+		expense_accounts = frappe.db.sql_list(
+			"""select distinct expense_account
 			from `tabPurchase Invoice Item` where docstatus = 1
 			and (expense_account is not null and expense_account != '')
-			and parent in (%s) order by expense_account""" %
-			', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
+			and parent in (%s) order by expense_account"""
+			% ", ".join(["%s"] * len(invoice_list)),
+			tuple([inv.name for inv in invoice_list]),
+		)
 
-		tax_accounts = 	frappe.db.sql_list("""select distinct account_head
+		tax_accounts = frappe.db.sql_list(
+			"""select distinct account_head
 			from `tabPurchase Taxes and Charges` where parenttype = 'Purchase Invoice'
 			and docstatus = 1 and (account_head is not null and account_head != '')
 			and category in ('Total', 'Valuation and Total')
-			and parent in (%s) order by account_head""" %
-			', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list))
+			and parent in (%s) order by account_head"""
+			% ", ".join(["%s"] * len(invoice_list)),
+			tuple(inv.name for inv in invoice_list),
+		)
 
-		unrealized_profit_loss_accounts = frappe.db.sql_list("""SELECT distinct unrealized_profit_loss_account
+		unrealized_profit_loss_accounts = frappe.db.sql_list(
+			"""SELECT distinct unrealized_profit_loss_account
 			from `tabPurchase Invoice` where docstatus = 1 and name in (%s)
 			and ifnull(unrealized_profit_loss_account, '') != ''
-			order by unrealized_profit_loss_account""" %
-			', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list))
+			order by unrealized_profit_loss_account"""
+			% ", ".join(["%s"] * len(invoice_list)),
+			tuple(inv.name for inv in invoice_list),
+		)
 
 	expense_columns = [(account + ":Currency/currency:120") for account in expense_accounts]
-	unrealized_profit_loss_account_columns = [(account + ":Currency/currency:120") for account in unrealized_profit_loss_accounts]
+	unrealized_profit_loss_account_columns = [
+		(account + ":Currency/currency:120") for account in unrealized_profit_loss_accounts
+	]
+	tax_columns = [
+		(account + ":Currency/currency:120")
+		for account in tax_accounts
+		if account not in expense_accounts
+	]
 
-	for account in tax_accounts:
-		if account not in expense_accounts:
-			tax_columns.append(account + ":Currency/currency:120")
-
-	columns = columns + expense_columns + unrealized_profit_loss_account_columns + \
-		[_("Net Total") + ":Currency/currency:120"] + tax_columns + \
-		[_("Total Tax") + ":Currency/currency:120", _("Grand Total") + ":Currency/currency:120",
-			_("Rounded Total") + ":Currency/currency:120", _("Outstanding Amount") + ":Currency/currency:120"]
+	columns = (
+		columns
+		+ expense_columns
+		+ unrealized_profit_loss_account_columns
+		+ [_("Net Total") + ":Currency/currency:120"]
+		+ tax_columns
+		+ [
+			_("Total Tax") + ":Currency/currency:120",
+			_("Grand Total") + ":Currency/currency:120",
+			_("Rounded Total") + ":Currency/currency:120",
+			_("Outstanding Amount") + ":Currency/currency:120",
+		]
+	)
 
 	return columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts
 
+
 def get_conditions(filters):
 	conditions = ""
 
-	if filters.get("company"): conditions += " and company=%(company)s"
-	if filters.get("supplier"): conditions += " and supplier = %(supplier)s"
+	if filters.get("company"):
+		conditions += " and company=%(company)s"
+	if filters.get("supplier"):
+		conditions += " and supplier = %(supplier)s"
 
-	if filters.get("from_date"): conditions += " and posting_date>=%(from_date)s"
-	if filters.get("to_date"): conditions += " and posting_date<=%(to_date)s"
+	if filters.get("from_date"):
+		conditions += " and posting_date>=%(from_date)s"
+	if filters.get("to_date"):
+		conditions += " and posting_date<=%(to_date)s"
 
-	if filters.get("mode_of_payment"): conditions += " and ifnull(mode_of_payment, '') = %(mode_of_payment)s"
+	if filters.get("mode_of_payment"):
+		conditions += " and ifnull(mode_of_payment, '') = %(mode_of_payment)s"
 
 	if filters.get("cost_center"):
-		conditions +=  """ and exists(select name from `tabPurchase Invoice Item`
+		conditions += """ and exists(select name from `tabPurchase Invoice Item`
 			 where parent=`tabPurchase Invoice`.name
 			 	and ifnull(`tabPurchase Invoice Item`.cost_center, '') = %(cost_center)s)"""
 
 	if filters.get("warehouse"):
-		conditions +=  """ and exists(select name from `tabPurchase Invoice Item`
+		conditions += """ and exists(select name from `tabPurchase Invoice Item`
 			 where parent=`tabPurchase Invoice`.name
 			 	and ifnull(`tabPurchase Invoice Item`.warehouse, '') = %(warehouse)s)"""
 
 	if filters.get("item_group"):
-		conditions +=  """ and exists(select name from `tabPurchase Invoice Item`
+		conditions += """ and exists(select name from `tabPurchase Invoice Item`
 			 where parent=`tabPurchase Invoice`.name
 			 	and ifnull(`tabPurchase Invoice Item`.item_group, '') = %(item_group)s)"""
 
@@ -182,38 +225,58 @@
 			"""
 		for dimension in accounting_dimensions:
 			if filters.get(dimension.fieldname):
-				if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
-					filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
-						filters.get(dimension.fieldname))
+				if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"):
+					filters[dimension.fieldname] = get_dimension_with_children(
+						dimension.document_type, filters.get(dimension.fieldname)
+					)
 
-					conditions += common_condition + "and ifnull(`tabPurchase Invoice Item`.{0}, '') in %({0})s)".format(dimension.fieldname)
+					conditions += (
+						common_condition
+						+ "and ifnull(`tabPurchase Invoice Item`.{0}, '') in %({0})s)".format(dimension.fieldname)
+					)
 				else:
-					conditions += common_condition + "and ifnull(`tabPurchase Invoice Item`.{0}, '') in (%({0})s))".format(dimension.fieldname)
+					conditions += (
+						common_condition
+						+ "and ifnull(`tabPurchase Invoice Item`.{0}, '') in %({0})s)".format(dimension.fieldname)
+					)
 
 	return conditions
 
+
 def get_invoices(filters, additional_query_columns):
 	if additional_query_columns:
-		additional_query_columns = ', ' + ', '.join(additional_query_columns)
+		additional_query_columns = ", " + ", ".join(additional_query_columns)
 
 	conditions = get_conditions(filters)
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select
 			name, posting_date, credit_to, supplier, supplier_name, tax_id, bill_no, bill_date,
 			remarks, base_net_total, base_grand_total, outstanding_amount,
 			mode_of_payment {0}
 		from `tabPurchase Invoice`
 		where docstatus = 1 %s
-		order by posting_date desc, name desc""".format(additional_query_columns or '') % conditions, filters, as_dict=1)
+		order by posting_date desc, name desc""".format(
+			additional_query_columns or ""
+		)
+		% conditions,
+		filters,
+		as_dict=1,
+	)
 
 
 def get_invoice_expense_map(invoice_list):
-	expense_details = frappe.db.sql("""
+	expense_details = frappe.db.sql(
+		"""
 		select parent, expense_account, sum(base_net_amount) as amount
 		from `tabPurchase Invoice Item`
 		where parent in (%s)
 		group by parent, expense_account
-	""" % ', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1)
+	"""
+		% ", ".join(["%s"] * len(invoice_list)),
+		tuple(inv.name for inv in invoice_list),
+		as_dict=1,
+	)
 
 	invoice_expense_map = {}
 	for d in expense_details:
@@ -222,11 +285,16 @@
 
 	return invoice_expense_map
 
+
 def get_internal_invoice_map(invoice_list):
-	unrealized_amount_details = frappe.db.sql("""SELECT name, unrealized_profit_loss_account,
+	unrealized_amount_details = frappe.db.sql(
+		"""SELECT name, unrealized_profit_loss_account,
 		base_net_total as amount from `tabPurchase Invoice` where name in (%s)
-		and is_internal_supplier = 1 and company = represents_company""" %
-		', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1)
+		and is_internal_supplier = 1 and company = represents_company"""
+		% ", ".join(["%s"] * len(invoice_list)),
+		tuple(inv.name for inv in invoice_list),
+		as_dict=1,
+	)
 
 	internal_invoice_map = {}
 	for d in unrealized_amount_details:
@@ -235,15 +303,21 @@
 
 	return internal_invoice_map
 
+
 def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts):
-	tax_details = frappe.db.sql("""
+	tax_details = frappe.db.sql(
+		"""
 		select parent, account_head, case add_deduct_tax when "Add" then sum(base_tax_amount_after_discount_amount)
 		else sum(base_tax_amount_after_discount_amount) * -1 end as tax_amount
 		from `tabPurchase Taxes and Charges`
 		where parent in (%s) and category in ('Total', 'Valuation and Total')
 			and base_tax_amount_after_discount_amount != 0
 		group by parent, account_head, add_deduct_tax
-	""" % ', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1)
+	"""
+		% ", ".join(["%s"] * len(invoice_list)),
+		tuple(inv.name for inv in invoice_list),
+		as_dict=1,
+	)
 
 	invoice_tax_map = {}
 	for d in tax_details:
@@ -258,48 +332,71 @@
 
 	return invoice_expense_map, invoice_tax_map
 
+
 def get_invoice_po_pr_map(invoice_list):
-	pi_items = frappe.db.sql("""
+	pi_items = frappe.db.sql(
+		"""
 		select parent, purchase_order, purchase_receipt, po_detail, project
 		from `tabPurchase Invoice Item`
 		where parent in (%s)
-	""" % ', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1)
+	"""
+		% ", ".join(["%s"] * len(invoice_list)),
+		tuple(inv.name for inv in invoice_list),
+		as_dict=1,
+	)
 
 	invoice_po_pr_map = {}
 	for d in pi_items:
 		if d.purchase_order:
-			invoice_po_pr_map.setdefault(d.parent, frappe._dict()).setdefault(
-				"purchase_order", []).append(d.purchase_order)
+			invoice_po_pr_map.setdefault(d.parent, frappe._dict()).setdefault("purchase_order", []).append(
+				d.purchase_order
+			)
 
 		pr_list = None
 		if d.purchase_receipt:
 			pr_list = [d.purchase_receipt]
 		elif d.po_detail:
-			pr_list = frappe.db.sql_list("""select distinct parent from `tabPurchase Receipt Item`
-				where docstatus=1 and purchase_order_item=%s""", d.po_detail)
+			pr_list = frappe.db.sql_list(
+				"""select distinct parent from `tabPurchase Receipt Item`
+				where docstatus=1 and purchase_order_item=%s""",
+				d.po_detail,
+			)
 
 		if pr_list:
 			invoice_po_pr_map.setdefault(d.parent, frappe._dict()).setdefault("purchase_receipt", pr_list)
 
 		if d.project:
-			invoice_po_pr_map.setdefault(d.parent, frappe._dict()).setdefault(
-				"project", []).append(d.project)
+			invoice_po_pr_map.setdefault(d.parent, frappe._dict()).setdefault("project", []).append(
+				d.project
+			)
 
 	return invoice_po_pr_map
 
+
 def get_account_details(invoice_list):
 	account_map = {}
 	accounts = list(set([inv.credit_to for inv in invoice_list]))
-	for acc in frappe.db.sql("""select name, parent_account from tabAccount
-		where name in (%s)""" % ", ".join(["%s"]*len(accounts)), tuple(accounts), as_dict=1):
-			account_map[acc.name] = acc.parent_account
+	for acc in frappe.db.sql(
+		"""select name, parent_account from tabAccount
+		where name in (%s)"""
+		% ", ".join(["%s"] * len(accounts)),
+		tuple(accounts),
+		as_dict=1,
+	):
+		account_map[acc.name] = acc.parent_account
 
 	return account_map
 
+
 def get_supplier_details(suppliers):
 	supplier_details = {}
-	for supp in frappe.db.sql("""select name, supplier_group from `tabSupplier`
-		where name in (%s)""" % ", ".join(["%s"]*len(suppliers)), tuple(suppliers), as_dict=1):
-			supplier_details.setdefault(supp.name, supp.supplier_group)
+	for supp in frappe.db.sql(
+		"""select name, supplier_group from `tabSupplier`
+		where name in (%s)"""
+		% ", ".join(["%s"] * len(suppliers)),
+		tuple(suppliers),
+		as_dict=1,
+	):
+		supplier_details.setdefault(supp.name, supp.supplier_group)
 
 	return supplier_details
diff --git a/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py b/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py
index e88675b..1dcacb9 100644
--- a/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py
+++ b/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py
@@ -13,6 +13,7 @@
 	data = get_ordered_to_be_billed_data(args)
 	return columns, data
 
+
 def get_column():
 	return [
 		{
@@ -20,90 +21,76 @@
 			"fieldname": "name",
 			"fieldtype": "Link",
 			"options": "Purchase Receipt",
-			"width": 160
+			"width": 160,
 		},
-		{
-			"label": _("Date"),
-			"fieldname": "date",
-			"fieldtype": "Date",
-			"width": 100
-		},
+		{"label": _("Date"), "fieldname": "date", "fieldtype": "Date", "width": 100},
 		{
 			"label": _("Supplier"),
 			"fieldname": "supplier",
 			"fieldtype": "Link",
 			"options": "Supplier",
-			"width": 120
+			"width": 120,
 		},
-		{
-			"label": _("Supplier Name"),
-			"fieldname": "supplier_name",
-			"fieldtype": "Data",
-			"width": 120
-		},
+		{"label": _("Supplier Name"), "fieldname": "supplier_name", "fieldtype": "Data", "width": 120},
 		{
 			"label": _("Item Code"),
 			"fieldname": "item_code",
 			"fieldtype": "Link",
 			"options": "Item",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("Amount"),
 			"fieldname": "amount",
 			"fieldtype": "Currency",
 			"width": 100,
-			"options": "Company:company:default_currency"
+			"options": "Company:company:default_currency",
 		},
 		{
 			"label": _("Billed Amount"),
 			"fieldname": "billed_amount",
 			"fieldtype": "Currency",
 			"width": 100,
-			"options": "Company:company:default_currency"
+			"options": "Company:company:default_currency",
 		},
 		{
 			"label": _("Returned Amount"),
 			"fieldname": "returned_amount",
 			"fieldtype": "Currency",
 			"width": 120,
-			"options": "Company:company:default_currency"
+			"options": "Company:company:default_currency",
 		},
 		{
 			"label": _("Pending Amount"),
 			"fieldname": "pending_amount",
 			"fieldtype": "Currency",
 			"width": 120,
-			"options": "Company:company:default_currency"
+			"options": "Company:company:default_currency",
 		},
-		{
-			"label": _("Item Name"),
-			"fieldname": "item_name",
-			"fieldtype": "Data",
-			"width": 120
-		},
-		{
-			"label": _("Description"),
-			"fieldname": "description",
-			"fieldtype": "Data",
-			"width": 120
-		},
+		{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 120},
+		{"label": _("Description"), "fieldname": "description", "fieldtype": "Data", "width": 120},
 		{
 			"label": _("Project"),
 			"fieldname": "project",
 			"fieldtype": "Link",
 			"options": "Project",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("Company"),
 			"fieldname": "company",
 			"fieldtype": "Link",
 			"options": "Company",
-			"width": 120
-		}
+			"width": 120,
+		},
 	]
 
+
 def get_args():
-	return {'doctype': 'Purchase Receipt', 'party': 'supplier',
-		'date': 'posting_date', 'order': 'name', 'order_by': 'desc'}
+	return {
+		"doctype": "Purchase Receipt",
+		"party": "supplier",
+		"date": "posting_date",
+		"order": "name",
+		"order_by": "desc",
+	}
diff --git a/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.py b/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.py
index 966b1d4..483deba 100644
--- a/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.py
+++ b/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.py
@@ -6,7 +6,8 @@
 
 
 def execute(filters=None):
-	if not filters: filters ={}
+	if not filters:
+		filters = {}
 	data = []
 	conditions = get_columns(filters, "Sales Invoice")
 	data = get_data(filters, conditions)
diff --git a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py
index 3b73628..0577214 100644
--- a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py
+++ b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py
@@ -9,7 +9,11 @@
 def execute(filters=None):
 	columns, data = [], []
 	columns = get_columns(filters)
-	data = get_pos_sales_payment_data(filters) if filters.get('is_pos') else get_sales_payment_data(filters, columns)
+	data = (
+		get_pos_sales_payment_data(filters)
+		if filters.get("is_pos")
+		else get_sales_payment_data(filters, columns)
+	)
 	return columns, data
 
 
@@ -22,12 +26,12 @@
 		_("Taxes") + ":Currency/currency:120",
 		_("Payments") + ":Currency/currency:120",
 		_("Warehouse") + ":Data:200",
-		_("Cost Center") + ":Data:200"
+		_("Cost Center") + ":Data:200",
 	]
 
 
 def get_columns(filters):
-	if filters.get('is_pos'):
+	if filters.get("is_pos"):
 		return get_pos_columns()
 	else:
 		return [
@@ -44,15 +48,17 @@
 	sales_invoice_data = get_pos_invoice_data(filters)
 	data = [
 		[
-			row['posting_date'],
-			row['owner'],
-			row['mode_of_payment'],
-			row['net_total'],
-			row['total_taxes'],
-			row['paid_amount'],
-			row['warehouse'],
-			row['cost_center']
-		] for row in sales_invoice_data]
+			row["posting_date"],
+			row["owner"],
+			row["mode_of_payment"],
+			row["net_total"],
+			row["total_taxes"],
+			row["paid_amount"],
+			row["warehouse"],
+			row["cost_center"],
+		]
+		for row in sales_invoice_data
+	]
 
 	return data
 
@@ -71,19 +77,25 @@
 		show_payment_detail = False
 
 	for inv in sales_invoice_data:
-		owner_posting_date = inv["owner"]+cstr(inv["posting_date"])
+		owner_posting_date = inv["owner"] + cstr(inv["posting_date"])
 		if show_payment_detail:
-			row = [inv.posting_date, inv.owner," ",inv.net_total,inv.total_taxes, 0]
+			row = [inv.posting_date, inv.owner, " ", inv.net_total, inv.total_taxes, 0]
 			data.append(row)
-			for mop_detail in mode_of_payment_details.get(owner_posting_date,[]):
-				row = [inv.posting_date, inv.owner,mop_detail[0],0,0,mop_detail[1],0]
+			for mop_detail in mode_of_payment_details.get(owner_posting_date, []):
+				row = [inv.posting_date, inv.owner, mop_detail[0], 0, 0, mop_detail[1], 0]
 				data.append(row)
 		else:
 			total_payment = 0
-			for mop_detail in mode_of_payment_details.get(owner_posting_date,[]):
+			for mop_detail in mode_of_payment_details.get(owner_posting_date, []):
 				total_payment = total_payment + mop_detail[1]
-			row = [inv.posting_date, inv.owner,", ".join(mode_of_payments.get(owner_posting_date, [])),
-			inv.net_total,inv.total_taxes,total_payment]
+			row = [
+				inv.posting_date,
+				inv.owner,
+				", ".join(mode_of_payments.get(owner_posting_date, [])),
+				inv.net_total,
+				inv.total_taxes,
+				total_payment,
+			]
 			data.append(row)
 	return data
 
@@ -107,40 +119,44 @@
 
 def get_pos_invoice_data(filters):
 	conditions = get_conditions(filters)
-	result = frappe.db.sql(''
-							'SELECT '
-							'posting_date, owner, sum(net_total) as "net_total", sum(total_taxes) as "total_taxes", '
-							'sum(paid_amount) as "paid_amount", sum(outstanding_amount) as "outstanding_amount", '
-							'mode_of_payment, warehouse, cost_center '
-							'FROM ('
-							'SELECT '
-							'parent, item_code, sum(amount) as "base_total", warehouse, cost_center '
-							'from `tabSales Invoice Item`  group by parent'
-							') t1 '
-							'left join '
-							'(select parent, mode_of_payment from `tabSales Invoice Payment` group by parent) t3 '
-							'on (t3.parent = t1.parent) '
-							'JOIN ('
-							'SELECT '
-							'docstatus, company, is_pos, name, posting_date, owner, sum(base_total) as "base_total", '
-							'sum(net_total) as "net_total", sum(total_taxes_and_charges) as "total_taxes", '
-							'sum(base_paid_amount) as "paid_amount", sum(outstanding_amount) as "outstanding_amount" '
-							'FROM `tabSales Invoice` '
-							'GROUP BY name'
-							') a '
-							'ON ('
-							't1.parent = a.name and t1.base_total = a.base_total) '
-							'WHERE a.docstatus = 1'
-							' AND {conditions} '
-							'GROUP BY '
-							'owner, posting_date, warehouse'.format(conditions=conditions), filters, as_dict=1
-							)
+	result = frappe.db.sql(
+		""
+		"SELECT "
+		'posting_date, owner, sum(net_total) as "net_total", sum(total_taxes) as "total_taxes", '
+		'sum(paid_amount) as "paid_amount", sum(outstanding_amount) as "outstanding_amount", '
+		"mode_of_payment, warehouse, cost_center "
+		"FROM ("
+		"SELECT "
+		'parent, item_code, sum(amount) as "base_total", warehouse, cost_center '
+		"from `tabSales Invoice Item`  group by parent"
+		") t1 "
+		"left join "
+		"(select parent, mode_of_payment from `tabSales Invoice Payment` group by parent) t3 "
+		"on (t3.parent = t1.parent) "
+		"JOIN ("
+		"SELECT "
+		'docstatus, company, is_pos, name, posting_date, owner, sum(base_total) as "base_total", '
+		'sum(net_total) as "net_total", sum(total_taxes_and_charges) as "total_taxes", '
+		'sum(base_paid_amount) as "paid_amount", sum(outstanding_amount) as "outstanding_amount" '
+		"FROM `tabSales Invoice` "
+		"GROUP BY name"
+		") a "
+		"ON ("
+		"t1.parent = a.name and t1.base_total = a.base_total) "
+		"WHERE a.docstatus = 1"
+		" AND {conditions} "
+		"GROUP BY "
+		"owner, posting_date, warehouse".format(conditions=conditions),
+		filters,
+		as_dict=1,
+	)
 	return result
 
 
 def get_sales_invoice_data(filters):
 	conditions = get_conditions(filters)
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select
 			a.posting_date, a.owner,
 			sum(a.net_total) as "net_total",
@@ -152,15 +168,21 @@
 			and {conditions}
 			group by
 			a.owner, a.posting_date
-	""".format(conditions=conditions), filters, as_dict=1)
+	""".format(
+			conditions=conditions
+		),
+		filters,
+		as_dict=1,
+	)
 
 
 def get_mode_of_payments(filters):
 	mode_of_payments = {}
 	invoice_list = get_invoices(filters)
-	invoice_list_names = ",".join('"' + invoice['name'] + '"' for invoice in invoice_list)
+	invoice_list_names = ",".join("'" + invoice["name"] + "'" for invoice in invoice_list)
 	if invoice_list:
-		inv_mop = frappe.db.sql("""select a.owner,a.posting_date, ifnull(b.mode_of_payment, '') as mode_of_payment
+		inv_mop = frappe.db.sql(
+			"""select a.owner,a.posting_date, ifnull(b.mode_of_payment, '') as mode_of_payment
 			from `tabSales Invoice` a, `tabSales Invoice Payment` b
 			where a.name = b.parent
 			and a.docstatus = 1
@@ -178,70 +200,103 @@
 			from `tabJournal Entry` a, `tabJournal Entry Account` b
 			where a.name = b.parent
 			and a.docstatus = 1
-			and b.reference_type = "Sales Invoice"
+			and b.reference_type = 'Sales Invoice'
 			and b.reference_name in ({invoice_list_names})
-			""".format(invoice_list_names=invoice_list_names), as_dict=1)
+			""".format(
+				invoice_list_names=invoice_list_names
+			),
+			as_dict=1,
+		)
 		for d in inv_mop:
-			mode_of_payments.setdefault(d["owner"]+cstr(d["posting_date"]), []).append(d.mode_of_payment)
+			mode_of_payments.setdefault(d["owner"] + cstr(d["posting_date"]), []).append(d.mode_of_payment)
 	return mode_of_payments
 
 
 def get_invoices(filters):
 	conditions = get_conditions(filters)
-	return frappe.db.sql("""select a.name
+	return frappe.db.sql(
+		"""select a.name
 		from `tabSales Invoice` a
-		where a.docstatus = 1 and {conditions}""".format(conditions=conditions),
-		filters, as_dict=1)
+		where a.docstatus = 1 and {conditions}""".format(
+			conditions=conditions
+		),
+		filters,
+		as_dict=1,
+	)
 
 
 def get_mode_of_payment_details(filters):
 	mode_of_payment_details = {}
 	invoice_list = get_invoices(filters)
-	invoice_list_names = ",".join('"' + invoice['name'] + '"' for invoice in invoice_list)
+	invoice_list_names = ",".join("'" + invoice["name"] + "'" for invoice in invoice_list)
 	if invoice_list:
-		inv_mop_detail = frappe.db.sql("""select a.owner, a.posting_date,
-			ifnull(b.mode_of_payment, '') as mode_of_payment, sum(b.base_amount) as paid_amount
-			from `tabSales Invoice` a, `tabSales Invoice Payment` b
-			where a.name = b.parent
-			and a.docstatus = 1
-			and a.name in ({invoice_list_names})
-			group by a.owner, a.posting_date, mode_of_payment
-			union
-			select a.owner,a.posting_date,
-			ifnull(b.mode_of_payment, '') as mode_of_payment, sum(b.base_paid_amount) as paid_amount
-			from `tabSales Invoice` a, `tabPayment Entry` b,`tabPayment Entry Reference` c
-			where a.name = c.reference_name
-			and b.name = c.parent
-			and b.docstatus = 1
-			and a.name in ({invoice_list_names})
-			group by a.owner, a.posting_date, mode_of_payment
-			union
-			select a.owner, a.posting_date,
-			ifnull(a.voucher_type,'') as mode_of_payment, sum(b.credit)
-			from `tabJournal Entry` a, `tabJournal Entry Account` b
-			where a.name = b.parent
-			and a.docstatus = 1
-			and b.reference_type = "Sales Invoice"
-			and b.reference_name in ({invoice_list_names})
-			group by a.owner, a.posting_date, mode_of_payment
-			""".format(invoice_list_names=invoice_list_names), as_dict=1)
+		inv_mop_detail = frappe.db.sql(
+			"""
+			select t.owner,
+			       t.posting_date,
+				   t.mode_of_payment,
+				   sum(t.paid_amount) as paid_amount
+			from (
+				select a.owner, a.posting_date,
+				ifnull(b.mode_of_payment, '') as mode_of_payment, sum(b.base_amount) as paid_amount
+				from `tabSales Invoice` a, `tabSales Invoice Payment` b
+				where a.name = b.parent
+				and a.docstatus = 1
+				and a.name in ({invoice_list_names})
+				group by a.owner, a.posting_date, mode_of_payment
+				union
+				select a.owner,a.posting_date,
+				ifnull(b.mode_of_payment, '') as mode_of_payment, sum(c.allocated_amount) as paid_amount
+				from `tabSales Invoice` a, `tabPayment Entry` b,`tabPayment Entry Reference` c
+				where a.name = c.reference_name
+				and b.name = c.parent
+				and b.docstatus = 1
+				and a.name in ({invoice_list_names})
+				group by a.owner, a.posting_date, mode_of_payment
+				union
+				select a.owner, a.posting_date,
+				ifnull(a.voucher_type,'') as mode_of_payment, sum(b.credit)
+				from `tabJournal Entry` a, `tabJournal Entry Account` b
+				where a.name = b.parent
+				and a.docstatus = 1
+				and b.reference_type = 'Sales Invoice'
+				and b.reference_name in ({invoice_list_names})
+				group by a.owner, a.posting_date, mode_of_payment
+			) t
+			group by t.owner, t.posting_date, t.mode_of_payment
+			""".format(
+				invoice_list_names=invoice_list_names
+			),
+			as_dict=1,
+		)
 
-		inv_change_amount = frappe.db.sql("""select a.owner, a.posting_date,
+		inv_change_amount = frappe.db.sql(
+			"""select a.owner, a.posting_date,
 			ifnull(b.mode_of_payment, '') as mode_of_payment, sum(a.base_change_amount) as change_amount
 			from `tabSales Invoice` a, `tabSales Invoice Payment` b
 			where a.name = b.parent
 			and a.name in ({invoice_list_names})
-			and b.mode_of_payment = 'Cash'
+			and b.type = 'Cash'
 			and a.base_change_amount > 0
-			group by a.owner, a.posting_date, mode_of_payment""".format(invoice_list_names=invoice_list_names), as_dict=1)
+			group by a.owner, a.posting_date, mode_of_payment""".format(
+				invoice_list_names=invoice_list_names
+			),
+			as_dict=1,
+		)
 
 		for d in inv_change_amount:
 			for det in inv_mop_detail:
-				if det["owner"] == d["owner"] and det["posting_date"] == d["posting_date"] and det["mode_of_payment"] == d["mode_of_payment"]:
+				if (
+					det["owner"] == d["owner"]
+					and det["posting_date"] == d["posting_date"]
+					and det["mode_of_payment"] == d["mode_of_payment"]
+				):
 					paid_amount = det["paid_amount"] - d["change_amount"]
 					det["paid_amount"] = paid_amount
 
 		for d in inv_mop_detail:
-			mode_of_payment_details.setdefault(d["owner"]+cstr(d["posting_date"]), []).append((d.mode_of_payment,d.paid_amount))
+			mode_of_payment_details.setdefault(d["owner"] + cstr(d["posting_date"]), []).append(
+				(d.mode_of_payment, d.paid_amount)
+			)
 
 	return mode_of_payment_details
diff --git a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py
index b3f6c72..3ad0ff2 100644
--- a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py
+++ b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py
@@ -15,6 +15,7 @@
 
 test_dependencies = ["Sales Invoice"]
 
+
 class TestSalesPaymentSummary(unittest.TestCase):
 	@classmethod
 	def setUpClass(self):
@@ -37,7 +38,7 @@
 			si.insert()
 			si.submit()
 
-			if int(si.name[-3:])%2 == 0:
+			if int(si.name[-3:]) % 2 == 0:
 				bank_account = "_Test Cash - _TC"
 				mode_of_payment = "Cash"
 			else:
@@ -52,18 +53,22 @@
 			pe.submit()
 
 		mop = get_mode_of_payments(filters)
-		self.assertTrue('Credit Card' in list(mop.values())[0])
-		self.assertTrue('Cash' in list(mop.values())[0])
+		self.assertTrue("Credit Card" in list(mop.values())[0])
+		self.assertTrue("Cash" in list(mop.values())[0])
 
 		# Cancel all Cash payment entry and check if this mode of payment is still fetched.
-		payment_entries = frappe.get_all("Payment Entry", filters={"mode_of_payment": "Cash", "docstatus": 1}, fields=["name", "docstatus"])
+		payment_entries = frappe.get_all(
+			"Payment Entry",
+			filters={"mode_of_payment": "Cash", "docstatus": 1},
+			fields=["name", "docstatus"],
+		)
 		for payment_entry in payment_entries:
 			pe = frappe.get_doc("Payment Entry", payment_entry.name)
 			pe.cancel()
 
 		mop = get_mode_of_payments(filters)
-		self.assertTrue('Credit Card' in list(mop.values())[0])
-		self.assertTrue('Cash' not in list(mop.values())[0])
+		self.assertTrue("Credit Card" in list(mop.values())[0])
+		self.assertTrue("Cash" not in list(mop.values())[0])
 
 	def test_get_mode_of_payments_details(self):
 		filters = get_filters()
@@ -73,7 +78,7 @@
 			si.insert()
 			si.submit()
 
-			if int(si.name[-3:])%2 == 0:
+			if int(si.name[-3:]) % 2 == 0:
 				bank_account = "_Test Cash - _TC"
 				mode_of_payment = "Cash"
 			else:
@@ -95,7 +100,11 @@
 				cc_init_amount = mopd_value[1]
 
 		# Cancel one Credit Card Payment Entry and check that it is not fetched in mode of payment details.
-		payment_entries = frappe.get_all("Payment Entry", filters={"mode_of_payment": "Credit Card", "docstatus": 1}, fields=["name", "docstatus"])
+		payment_entries = frappe.get_all(
+			"Payment Entry",
+			filters={"mode_of_payment": "Credit Card", "docstatus": 1},
+			fields=["name", "docstatus"],
+		)
 		for payment_entry in payment_entries[:1]:
 			pe = frappe.get_doc("Payment Entry", payment_entry.name)
 			pe.cancel()
@@ -108,63 +117,72 @@
 
 		self.assertTrue(cc_init_amount > cc_final_amount)
 
+
 def get_filters():
-	return {
-		"from_date": "1900-01-01",
-		"to_date": today(),
-		"company": "_Test Company"
-	}
+	return {"from_date": "1900-01-01", "to_date": today(), "company": "_Test Company"}
+
 
 def create_sales_invoice_record(qty=1):
 	# return sales invoice doc object
-	return frappe.get_doc({
-		"doctype": "Sales Invoice",
-		"customer": frappe.get_doc('Customer', {"customer_name": "Prestiga-Biz"}).name,
-		"company": '_Test Company',
-		"due_date": today(),
-		"posting_date": today(),
-		"currency": "INR",
-		"taxes_and_charges": "",
-		"debit_to": "Debtors - _TC",
-		"taxes": [],
-		"items": [{
-			'doctype': 'Sales Invoice Item',
-			'item_code': frappe.get_doc('Item', {'item_name': 'Consulting'}).name,
-			'qty': qty,
-			"rate": 10000,
-			'income_account': 'Sales - _TC',
-			'cost_center': 'Main - _TC',
-			'expense_account': 'Cost of Goods Sold - _TC'
-		}]
-	})
+	return frappe.get_doc(
+		{
+			"doctype": "Sales Invoice",
+			"customer": frappe.get_doc("Customer", {"customer_name": "Prestiga-Biz"}).name,
+			"company": "_Test Company",
+			"due_date": today(),
+			"posting_date": today(),
+			"currency": "INR",
+			"taxes_and_charges": "",
+			"debit_to": "Debtors - _TC",
+			"taxes": [],
+			"items": [
+				{
+					"doctype": "Sales Invoice Item",
+					"item_code": frappe.get_doc("Item", {"item_name": "Consulting"}).name,
+					"qty": qty,
+					"rate": 10000,
+					"income_account": "Sales - _TC",
+					"cost_center": "Main - _TC",
+					"expense_account": "Cost of Goods Sold - _TC",
+				}
+			],
+		}
+	)
+
 
 def create_records():
 	if frappe.db.exists("Customer", "Prestiga-Biz"):
 		return
 
-	#customer
-	frappe.get_doc({
-		"customer_group": "_Test Customer Group",
-		"customer_name": "Prestiga-Biz",
-		"customer_type": "Company",
-		"doctype": "Customer",
-		"territory": "_Test Territory"
-	}).insert()
+	# customer
+	frappe.get_doc(
+		{
+			"customer_group": "_Test Customer Group",
+			"customer_name": "Prestiga-Biz",
+			"customer_type": "Company",
+			"doctype": "Customer",
+			"territory": "_Test Territory",
+		}
+	).insert()
 
 	# item
-	item = frappe.get_doc({
-		"doctype": "Item",
-		"item_code": "Consulting",
-		"item_name": "Consulting",
-		"item_group": "All Item Groups",
-		"company": "_Test Company",
-		"is_stock_item": 0
-	}).insert()
+	item = frappe.get_doc(
+		{
+			"doctype": "Item",
+			"item_code": "Consulting",
+			"item_name": "Consulting",
+			"item_group": "All Item Groups",
+			"company": "_Test Company",
+			"is_stock_item": 0,
+		}
+	).insert()
 
 	# item price
-	frappe.get_doc({
-		"doctype": "Item Price",
-		"price_list": "Standard Selling",
-		"item_code": item.item_code,
-		"price_list_rate": 10000
-	}).insert()
+	frappe.get_doc(
+		{
+			"doctype": "Item Price",
+			"price_list": "Standard Selling",
+			"item_code": item.item_code,
+			"price_list_rate": 10000,
+		}
+	).insert()
diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py
index a9d0081..33bd3c7 100644
--- a/erpnext/accounts/report/sales_register/sales_register.py
+++ b/erpnext/accounts/report/sales_register/sales_register.py
@@ -16,11 +16,15 @@
 def execute(filters=None):
 	return _execute(filters)
 
+
 def _execute(filters, additional_table_columns=None, additional_query_columns=None):
-	if not filters: filters = frappe._dict({})
+	if not filters:
+		filters = frappe._dict({})
 
 	invoice_list = get_invoices(filters, additional_query_columns)
-	columns, income_accounts, tax_accounts, unrealized_profit_loss_accounts = get_columns(invoice_list, additional_table_columns)
+	columns, income_accounts, tax_accounts, unrealized_profit_loss_accounts = get_columns(
+		invoice_list, additional_table_columns
+	)
 
 	if not invoice_list:
 		msgprint(_("No record found"))
@@ -28,12 +32,13 @@
 
 	invoice_income_map = get_invoice_income_map(invoice_list)
 	internal_invoice_map = get_internal_invoice_map(invoice_list)
-	invoice_income_map, invoice_tax_map = get_invoice_tax_map(invoice_list,
-		invoice_income_map, income_accounts)
-	#Cost Center & Warehouse Map
+	invoice_income_map, invoice_tax_map = get_invoice_tax_map(
+		invoice_list, invoice_income_map, income_accounts
+	)
+	# Cost Center & Warehouse Map
 	invoice_cc_wh_map = get_invoice_cc_wh_map(invoice_list)
 	invoice_so_dn_map = get_invoice_so_dn_map(invoice_list)
-	company_currency = frappe.get_cached_value('Company',  filters.get("company"),  "default_currency")
+	company_currency = frappe.get_cached_value("Company", filters.get("company"), "default_currency")
 	mode_of_payments = get_mode_of_payments([inv.name for inv in invoice_list])
 
 	data = []
@@ -45,33 +50,33 @@
 		warehouse = list(set(invoice_cc_wh_map.get(inv.name, {}).get("warehouse", [])))
 
 		row = {
-			'invoice': inv.name,
-			'posting_date': inv.posting_date,
-			'customer': inv.customer,
-			'customer_name': inv.customer_name
+			"invoice": inv.name,
+			"posting_date": inv.posting_date,
+			"customer": inv.customer,
+			"customer_name": inv.customer_name,
 		}
 
 		if additional_query_columns:
 			for col in additional_query_columns:
-				row.update({
-					col: inv.get(col)
-				})
+				row.update({col: inv.get(col)})
 
-		row.update({
-			'customer_group': inv.get("customer_group"),
-			'territory': inv.get("territory"),
-			'tax_id': inv.get("tax_id"),
-			'receivable_account': inv.debit_to,
-			'mode_of_payment':  ", ".join(mode_of_payments.get(inv.name, [])),
-			'project': inv.project,
-			'owner': inv.owner,
-			'remarks': inv.remarks,
-			'sales_order': ", ".join(sales_order),
-			'delivery_note': ", ".join(delivery_note),
-			'cost_center': ", ".join(cost_center),
-			'warehouse': ", ".join(warehouse),
-			'currency': company_currency
-		})
+		row.update(
+			{
+				"customer_group": inv.get("customer_group"),
+				"territory": inv.get("territory"),
+				"tax_id": inv.get("tax_id"),
+				"receivable_account": inv.debit_to,
+				"mode_of_payment": ", ".join(mode_of_payments.get(inv.name, [])),
+				"project": inv.project,
+				"owner": inv.owner,
+				"remarks": inv.remarks,
+				"sales_order": ", ".join(sales_order),
+				"delivery_note": ", ".join(delivery_note),
+				"cost_center": ", ".join(cost_center),
+				"warehouse": ", ".join(warehouse),
+				"currency": company_currency,
+			}
+		)
 
 		# map income values
 		base_net_total = 0
@@ -82,164 +87,138 @@
 				income_amount = flt(invoice_income_map.get(inv.name, {}).get(income_acc))
 
 			base_net_total += income_amount
-			row.update({
-				frappe.scrub(income_acc): income_amount
-			})
+			row.update({frappe.scrub(income_acc): income_amount})
 
 		# Add amount in unrealized account
 		for account in unrealized_profit_loss_accounts:
-			row.update({
-				frappe.scrub(account+"_unrealized"): flt(internal_invoice_map.get((inv.name, account)))
-			})
+			row.update(
+				{frappe.scrub(account + "_unrealized"): flt(internal_invoice_map.get((inv.name, account)))}
+			)
 
 		# net total
-		row.update({'net_total': base_net_total or inv.base_net_total})
+		row.update({"net_total": base_net_total or inv.base_net_total})
 
 		# tax account
 		total_tax = 0
 		for tax_acc in tax_accounts:
 			if tax_acc not in income_accounts:
-				tax_amount_precision = get_field_precision(frappe.get_meta("Sales Taxes and Charges").get_field("tax_amount"), currency=company_currency) or 2
+				tax_amount_precision = (
+					get_field_precision(
+						frappe.get_meta("Sales Taxes and Charges").get_field("tax_amount"), currency=company_currency
+					)
+					or 2
+				)
 				tax_amount = flt(invoice_tax_map.get(inv.name, {}).get(tax_acc), tax_amount_precision)
 				total_tax += tax_amount
-				row.update({
-					frappe.scrub(tax_acc): tax_amount
-				})
+				row.update({frappe.scrub(tax_acc): tax_amount})
 
 		# total tax, grand total, outstanding amount & rounded total
 
-		row.update({
-			'tax_total': total_tax,
-			'grand_total': inv.base_grand_total,
-			'rounded_total': inv.base_rounded_total,
-			'outstanding_amount': inv.outstanding_amount
-		})
+		row.update(
+			{
+				"tax_total": total_tax,
+				"grand_total": inv.base_grand_total,
+				"rounded_total": inv.base_rounded_total,
+				"outstanding_amount": inv.outstanding_amount,
+			}
+		)
 
 		data.append(row)
 
 	return columns, data
 
+
 def get_columns(invoice_list, additional_table_columns):
 	"""return columns based on filters"""
 	columns = [
 		{
-			'label': _("Invoice"),
-			'fieldname': 'invoice',
-			'fieldtype': 'Link',
-			'options': 'Sales Invoice',
-			'width': 120
+			"label": _("Invoice"),
+			"fieldname": "invoice",
+			"fieldtype": "Link",
+			"options": "Sales Invoice",
+			"width": 120,
 		},
+		{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 80},
 		{
-			'label': _("Posting Date"),
-			'fieldname': 'posting_date',
-			'fieldtype': 'Date',
-			'width': 80
+			"label": _("Customer"),
+			"fieldname": "customer",
+			"fieldtype": "Link",
+			"options": "Customer",
+			"width": 120,
 		},
-		{
-			'label': _("Customer"),
-			'fieldname': 'customer',
-			'fieldtype': 'Link',
-			'options': 'Customer',
-			'width': 120
-		},
-		{
-			'label': _("Customer Name"),
-			'fieldname': 'customer_name',
-			'fieldtype': 'Data',
-			'width': 120
-		},
+		{"label": _("Customer Name"), "fieldname": "customer_name", "fieldtype": "Data", "width": 120},
 	]
 
 	if additional_table_columns:
 		columns += additional_table_columns
 
-	columns +=[
+	columns += [
 		{
-			'label': _("Customer Group"),
-			'fieldname': 'customer_group',
-			'fieldtype': 'Link',
-			'options': 'Customer Group',
-			'width': 120
+			"label": _("Customer Group"),
+			"fieldname": "customer_group",
+			"fieldtype": "Link",
+			"options": "Customer Group",
+			"width": 120,
 		},
 		{
-			'label': _("Territory"),
-			'fieldname': 'territory',
-			'fieldtype': 'Link',
-			'options': 'Territory',
-			'width': 80
+			"label": _("Territory"),
+			"fieldname": "territory",
+			"fieldtype": "Link",
+			"options": "Territory",
+			"width": 80,
+		},
+		{"label": _("Tax Id"), "fieldname": "tax_id", "fieldtype": "Data", "width": 120},
+		{
+			"label": _("Receivable Account"),
+			"fieldname": "receivable_account",
+			"fieldtype": "Link",
+			"options": "Account",
+			"width": 80,
 		},
 		{
-			'label': _("Tax Id"),
-			'fieldname': 'tax_id',
-			'fieldtype': 'Data',
-			'width': 120
-		},
-		{
-			'label': _("Receivable Account"),
-			'fieldname': 'receivable_account',
-			'fieldtype': 'Link',
-			'options': 'Account',
-			'width': 80
-		},
-		{
-			'label': _("Mode Of Payment"),
-			'fieldname': 'mode_of_payment',
-			'fieldtype': 'Data',
-			'width': 120
-		},
-		{
-			'label': _("Project"),
-			'fieldname': 'project',
-			'fieldtype': 'Link',
-			'options': 'Project',
-			'width': 80
-		},
-		{
-			'label': _("Owner"),
-			'fieldname': 'owner',
-			'fieldtype': 'Data',
-			'width': 150
-		},
-		{
-			'label': _("Remarks"),
-			'fieldname': 'remarks',
-			'fieldtype': 'Data',
-			'width': 150
-		},
-		{
-			'label': _("Sales Order"),
-			'fieldname': 'sales_order',
-			'fieldtype': 'Link',
-			'options': 'Sales Order',
-			'width': 100
-		},
-		{
-			'label': _("Delivery Note"),
-			'fieldname': 'delivery_note',
-			'fieldtype': 'Link',
-			'options': 'Delivery Note',
-			'width': 100
-		},
-		{
-			'label': _("Cost Center"),
-			'fieldname': 'cost_center',
-			'fieldtype': 'Link',
-			'options': 'Cost Center',
-			'width': 100
-		},
-		{
-			'label': _("Warehouse"),
-			'fieldname': 'warehouse',
-			'fieldtype': 'Link',
-			'options': 'Warehouse',
-			'width': 100
-		},
-		{
-			"fieldname": "currency",
-			"label": _("Currency"),
+			"label": _("Mode Of Payment"),
+			"fieldname": "mode_of_payment",
 			"fieldtype": "Data",
-			"width": 80
-		}
+			"width": 120,
+		},
+		{
+			"label": _("Project"),
+			"fieldname": "project",
+			"fieldtype": "Link",
+			"options": "Project",
+			"width": 80,
+		},
+		{"label": _("Owner"), "fieldname": "owner", "fieldtype": "Data", "width": 150},
+		{"label": _("Remarks"), "fieldname": "remarks", "fieldtype": "Data", "width": 150},
+		{
+			"label": _("Sales Order"),
+			"fieldname": "sales_order",
+			"fieldtype": "Link",
+			"options": "Sales Order",
+			"width": 100,
+		},
+		{
+			"label": _("Delivery Note"),
+			"fieldname": "delivery_note",
+			"fieldtype": "Link",
+			"options": "Delivery Note",
+			"width": 100,
+		},
+		{
+			"label": _("Cost Center"),
+			"fieldname": "cost_center",
+			"fieldtype": "Link",
+			"options": "Cost Center",
+			"width": 100,
+		},
+		{
+			"label": _("Warehouse"),
+			"fieldname": "warehouse",
+			"fieldtype": "Link",
+			"options": "Warehouse",
+			"width": 100,
+		},
+		{"fieldname": "currency", "label": _("Currency"), "fieldtype": "Data", "width": 80},
 	]
 
 	income_accounts = []
@@ -250,133 +229,152 @@
 	unrealized_profit_loss_account_columns = []
 
 	if invoice_list:
-		income_accounts = frappe.db.sql_list("""select distinct income_account
+		income_accounts = frappe.db.sql_list(
+			"""select distinct income_account
 			from `tabSales Invoice Item` where docstatus = 1 and parent in (%s)
-			order by income_account""" %
-			', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list))
+			order by income_account"""
+			% ", ".join(["%s"] * len(invoice_list)),
+			tuple(inv.name for inv in invoice_list),
+		)
 
-		tax_accounts = 	frappe.db.sql_list("""select distinct account_head
+		tax_accounts = frappe.db.sql_list(
+			"""select distinct account_head
 			from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice'
 			and docstatus = 1 and base_tax_amount_after_discount_amount != 0
-			and parent in (%s) order by account_head""" %
-			', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list))
+			and parent in (%s) order by account_head"""
+			% ", ".join(["%s"] * len(invoice_list)),
+			tuple(inv.name for inv in invoice_list),
+		)
 
-		unrealized_profit_loss_accounts = frappe.db.sql_list("""SELECT distinct unrealized_profit_loss_account
+		unrealized_profit_loss_accounts = frappe.db.sql_list(
+			"""SELECT distinct unrealized_profit_loss_account
 			from `tabSales Invoice` where docstatus = 1 and name in (%s)
 			and is_internal_customer = 1
 			and ifnull(unrealized_profit_loss_account, '') != ''
-			order by unrealized_profit_loss_account""" %
-			', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list))
+			order by unrealized_profit_loss_account"""
+			% ", ".join(["%s"] * len(invoice_list)),
+			tuple(inv.name for inv in invoice_list),
+		)
 
 	for account in income_accounts:
-		income_columns.append({
-			"label": account,
-			"fieldname": frappe.scrub(account),
-			"fieldtype": "Currency",
-			"options": "currency",
-			"width": 120
-		})
-
-	for account in tax_accounts:
-		if account not in income_accounts:
-			tax_columns.append({
+		income_columns.append(
+			{
 				"label": account,
 				"fieldname": frappe.scrub(account),
 				"fieldtype": "Currency",
 				"options": "currency",
-				"width": 120
-			})
+				"width": 120,
+			}
+		)
+
+	for account in tax_accounts:
+		if account not in income_accounts:
+			tax_columns.append(
+				{
+					"label": account,
+					"fieldname": frappe.scrub(account),
+					"fieldtype": "Currency",
+					"options": "currency",
+					"width": 120,
+				}
+			)
 
 	for account in unrealized_profit_loss_accounts:
-		unrealized_profit_loss_account_columns.append({
-			"label": account,
-			"fieldname": frappe.scrub(account+"_unrealized"),
+		unrealized_profit_loss_account_columns.append(
+			{
+				"label": account,
+				"fieldname": frappe.scrub(account + "_unrealized"),
+				"fieldtype": "Currency",
+				"options": "currency",
+				"width": 120,
+			}
+		)
+
+	net_total_column = [
+		{
+			"label": _("Net Total"),
+			"fieldname": "net_total",
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 120
-		})
-
-	net_total_column = [{
-		"label": _("Net Total"),
-		"fieldname": "net_total",
-		"fieldtype": "Currency",
-		"options": "currency",
-		"width": 120
-	}]
+			"width": 120,
+		}
+	]
 
 	total_columns = [
 		{
 			"label": _("Tax Total"),
 			"fieldname": "tax_total",
 			"fieldtype": "Currency",
-			"options": 'currency',
-			"width": 120
+			"options": "currency",
+			"width": 120,
 		},
 		{
 			"label": _("Grand Total"),
 			"fieldname": "grand_total",
 			"fieldtype": "Currency",
-			"options": 'currency',
-			"width": 120
+			"options": "currency",
+			"width": 120,
 		},
 		{
 			"label": _("Rounded Total"),
 			"fieldname": "rounded_total",
 			"fieldtype": "Currency",
-			"options": 'currency',
-			"width": 120
+			"options": "currency",
+			"width": 120,
 		},
 		{
 			"label": _("Outstanding Amount"),
 			"fieldname": "outstanding_amount",
 			"fieldtype": "Currency",
-			"options": 'currency',
-			"width": 120
-		}
+			"options": "currency",
+			"width": 120,
+		},
 	]
 
-	columns = columns + income_columns + unrealized_profit_loss_account_columns + \
-		net_total_column + tax_columns + total_columns
+	columns = (
+		columns
+		+ income_columns
+		+ unrealized_profit_loss_account_columns
+		+ net_total_column
+		+ tax_columns
+		+ total_columns
+	)
 
 	return columns, income_accounts, tax_accounts, unrealized_profit_loss_accounts
 
+
 def get_conditions(filters):
 	conditions = ""
 
-	if filters.get("company"): conditions += " and company=%(company)s"
-	if filters.get("customer"): conditions += " and customer = %(customer)s"
+	accounting_dimensions = get_accounting_dimensions(as_list=False) or []
+	accounting_dimensions_list = [d.fieldname for d in accounting_dimensions]
 
-	if filters.get("from_date"): conditions += " and posting_date >= %(from_date)s"
-	if filters.get("to_date"): conditions += " and posting_date <= %(to_date)s"
+	if filters.get("company"):
+		conditions += " and company=%(company)s"
 
-	if filters.get("owner"): conditions += " and owner = %(owner)s"
+	if filters.get("customer") and "customer" not in accounting_dimensions_list:
+		conditions += " and customer = %(customer)s"
 
-	if filters.get("mode_of_payment"):
-		conditions += """ and exists(select name from `tabSales Invoice Payment`
-			 where parent=`tabSales Invoice`.name
-			 	and ifnull(`tabSales Invoice Payment`.mode_of_payment, '') = %(mode_of_payment)s)"""
+	if filters.get("from_date"):
+		conditions += " and posting_date >= %(from_date)s"
+	if filters.get("to_date"):
+		conditions += " and posting_date <= %(to_date)s"
 
-	if filters.get("cost_center"):
-		conditions +=  """ and exists(select name from `tabSales Invoice Item`
-			 where parent=`tabSales Invoice`.name
-			 	and ifnull(`tabSales Invoice Item`.cost_center, '') = %(cost_center)s)"""
+	if filters.get("owner"):
+		conditions += " and owner = %(owner)s"
 
-	if filters.get("warehouse"):
-		conditions +=  """ and exists(select name from `tabSales Invoice Item`
-			 where parent=`tabSales Invoice`.name
-			 	and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s)"""
+	def get_sales_invoice_item_field_condition(field, table="Sales Invoice Item") -> str:
+		if not filters.get(field) or field in accounting_dimensions_list:
+			return ""
+		return f""" and exists(select name from `tab{table}`
+				where parent=`tabSales Invoice`.name
+					and ifnull(`tab{table}`.{field}, '') = %({field})s)"""
 
-	if filters.get("brand"):
-		conditions +=  """ and exists(select name from `tabSales Invoice Item`
-			 where parent=`tabSales Invoice`.name
-			 	and ifnull(`tabSales Invoice Item`.brand, '') = %(brand)s)"""
-
-	if filters.get("item_group"):
-		conditions +=  """ and exists(select name from `tabSales Invoice Item`
-			 where parent=`tabSales Invoice`.name
-			 	and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)"""
-
-	accounting_dimensions = get_accounting_dimensions(as_list=False)
+	conditions += get_sales_invoice_item_field_condition("mode_of_payments", "Sales Invoice Payment")
+	conditions += get_sales_invoice_item_field_condition("cost_center")
+	conditions += get_sales_invoice_item_field_condition("warehouse")
+	conditions += get_sales_invoice_item_field_condition("brand")
+	conditions += get_sales_invoice_item_field_condition("item_group")
 
 	if accounting_dimensions:
 		common_condition = """
@@ -385,34 +383,53 @@
 			"""
 		for dimension in accounting_dimensions:
 			if filters.get(dimension.fieldname):
-				if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
-					filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
-						filters.get(dimension.fieldname))
+				if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"):
+					filters[dimension.fieldname] = get_dimension_with_children(
+						dimension.document_type, filters.get(dimension.fieldname)
+					)
 
-					conditions += common_condition + "and ifnull(`tabSales Invoice Item`.{0}, '') in %({0})s)".format(dimension.fieldname)
+					conditions += (
+						common_condition
+						+ "and ifnull(`tabSales Invoice Item`.{0}, '') in %({0})s)".format(dimension.fieldname)
+					)
 				else:
-					conditions += common_condition + "and ifnull(`tabSales Invoice Item`.{0}, '') in (%({0})s))".format(dimension.fieldname)
+					conditions += (
+						common_condition
+						+ "and ifnull(`tabSales Invoice Item`.{0}, '') in %({0})s)".format(dimension.fieldname)
+					)
 
 	return conditions
 
+
 def get_invoices(filters, additional_query_columns):
 	if additional_query_columns:
-		additional_query_columns = ', ' + ', '.join(additional_query_columns)
+		additional_query_columns = ", " + ", ".join(additional_query_columns)
 
 	conditions = get_conditions(filters)
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select name, posting_date, debit_to, project, customer,
 		customer_name, owner, remarks, territory, tax_id, customer_group,
 		base_net_total, base_grand_total, base_rounded_total, outstanding_amount,
 		is_internal_customer, represents_company, company {0}
 		from `tabSales Invoice`
-		where docstatus = 1 %s order by posting_date desc, name desc""".format(additional_query_columns or '') %
-		conditions, filters, as_dict=1)
+		where docstatus = 1 %s order by posting_date desc, name desc""".format(
+			additional_query_columns or ""
+		)
+		% conditions,
+		filters,
+		as_dict=1,
+	)
+
 
 def get_invoice_income_map(invoice_list):
-	income_details = frappe.db.sql("""select parent, income_account, sum(base_net_amount) as amount
-		from `tabSales Invoice Item` where parent in (%s) group by parent, income_account""" %
-		', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1)
+	income_details = frappe.db.sql(
+		"""select parent, income_account, sum(base_net_amount) as amount
+		from `tabSales Invoice Item` where parent in (%s) group by parent, income_account"""
+		% ", ".join(["%s"] * len(invoice_list)),
+		tuple(inv.name for inv in invoice_list),
+		as_dict=1,
+	)
 
 	invoice_income_map = {}
 	for d in income_details:
@@ -421,11 +438,16 @@
 
 	return invoice_income_map
 
+
 def get_internal_invoice_map(invoice_list):
-	unrealized_amount_details = frappe.db.sql("""SELECT name, unrealized_profit_loss_account,
+	unrealized_amount_details = frappe.db.sql(
+		"""SELECT name, unrealized_profit_loss_account,
 		base_net_total as amount from `tabSales Invoice` where name in (%s)
-		and is_internal_customer = 1 and company = represents_company""" %
-		', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1)
+		and is_internal_customer = 1 and company = represents_company"""
+		% ", ".join(["%s"] * len(invoice_list)),
+		tuple(inv.name for inv in invoice_list),
+		as_dict=1,
+	)
 
 	internal_invoice_map = {}
 	for d in unrealized_amount_details:
@@ -434,11 +456,16 @@
 
 	return internal_invoice_map
 
+
 def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts):
-	tax_details = frappe.db.sql("""select parent, account_head,
+	tax_details = frappe.db.sql(
+		"""select parent, account_head,
 		sum(base_tax_amount_after_discount_amount) as tax_amount
-		from `tabSales Taxes and Charges` where parent in (%s) group by parent, account_head""" %
-		', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1)
+		from `tabSales Taxes and Charges` where parent in (%s) group by parent, account_head"""
+		% ", ".join(["%s"] * len(invoice_list)),
+		tuple(inv.name for inv in invoice_list),
+		as_dict=1,
+	)
 
 	invoice_tax_map = {}
 	for d in tax_details:
@@ -453,54 +480,77 @@
 
 	return invoice_income_map, invoice_tax_map
 
+
 def get_invoice_so_dn_map(invoice_list):
-	si_items = frappe.db.sql("""select parent, sales_order, delivery_note, so_detail
+	si_items = frappe.db.sql(
+		"""select parent, sales_order, delivery_note, so_detail
 		from `tabSales Invoice Item` where parent in (%s)
-		and (ifnull(sales_order, '') != '' or ifnull(delivery_note, '') != '')""" %
-		', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1)
+		and (ifnull(sales_order, '') != '' or ifnull(delivery_note, '') != '')"""
+		% ", ".join(["%s"] * len(invoice_list)),
+		tuple(inv.name for inv in invoice_list),
+		as_dict=1,
+	)
 
 	invoice_so_dn_map = {}
 	for d in si_items:
 		if d.sales_order:
-			invoice_so_dn_map.setdefault(d.parent, frappe._dict()).setdefault(
-				"sales_order", []).append(d.sales_order)
+			invoice_so_dn_map.setdefault(d.parent, frappe._dict()).setdefault("sales_order", []).append(
+				d.sales_order
+			)
 
 		delivery_note_list = None
 		if d.delivery_note:
 			delivery_note_list = [d.delivery_note]
 		elif d.sales_order:
-			delivery_note_list = frappe.db.sql_list("""select distinct parent from `tabDelivery Note Item`
-				where docstatus=1 and so_detail=%s""", d.so_detail)
+			delivery_note_list = frappe.db.sql_list(
+				"""select distinct parent from `tabDelivery Note Item`
+				where docstatus=1 and so_detail=%s""",
+				d.so_detail,
+			)
 
 		if delivery_note_list:
-			invoice_so_dn_map.setdefault(d.parent, frappe._dict()).setdefault("delivery_note", delivery_note_list)
+			invoice_so_dn_map.setdefault(d.parent, frappe._dict()).setdefault(
+				"delivery_note", delivery_note_list
+			)
 
 	return invoice_so_dn_map
 
+
 def get_invoice_cc_wh_map(invoice_list):
-	si_items = frappe.db.sql("""select parent, cost_center, warehouse
+	si_items = frappe.db.sql(
+		"""select parent, cost_center, warehouse
 		from `tabSales Invoice Item` where parent in (%s)
-		and (ifnull(cost_center, '') != '' or ifnull(warehouse, '') != '')""" %
-		', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1)
+		and (ifnull(cost_center, '') != '' or ifnull(warehouse, '') != '')"""
+		% ", ".join(["%s"] * len(invoice_list)),
+		tuple(inv.name for inv in invoice_list),
+		as_dict=1,
+	)
 
 	invoice_cc_wh_map = {}
 	for d in si_items:
 		if d.cost_center:
-			invoice_cc_wh_map.setdefault(d.parent, frappe._dict()).setdefault(
-				"cost_center", []).append(d.cost_center)
+			invoice_cc_wh_map.setdefault(d.parent, frappe._dict()).setdefault("cost_center", []).append(
+				d.cost_center
+			)
 
 		if d.warehouse:
-			invoice_cc_wh_map.setdefault(d.parent, frappe._dict()).setdefault(
-				"warehouse", []).append(d.warehouse)
+			invoice_cc_wh_map.setdefault(d.parent, frappe._dict()).setdefault("warehouse", []).append(
+				d.warehouse
+			)
 
 	return invoice_cc_wh_map
 
+
 def get_mode_of_payments(invoice_list):
 	mode_of_payments = {}
 	if invoice_list:
-		inv_mop = frappe.db.sql("""select parent, mode_of_payment
-			from `tabSales Invoice Payment` where parent in (%s) group by parent, mode_of_payment""" %
-			', '.join(['%s']*len(invoice_list)), tuple(invoice_list), as_dict=1)
+		inv_mop = frappe.db.sql(
+			"""select parent, mode_of_payment
+			from `tabSales Invoice Payment` where parent in (%s) group by parent, mode_of_payment"""
+			% ", ".join(["%s"] * len(invoice_list)),
+			tuple(invoice_list),
+			as_dict=1,
+		)
 
 		for d in inv_mop:
 			mode_of_payments.setdefault(d.parent, []).append(d.mode_of_payment)
diff --git a/erpnext/accounts/report/share_balance/share_balance.py b/erpnext/accounts/report/share_balance/share_balance.py
index 943c4e1..d02f53b 100644
--- a/erpnext/accounts/report/share_balance/share_balance.py
+++ b/erpnext/accounts/report/share_balance/share_balance.py
@@ -7,7 +7,8 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	if not filters.get("date"):
 		frappe.throw(_("Please select date"))
@@ -38,22 +39,29 @@
 					break
 			# new entry
 			if not row:
-				row = [filters.get("shareholder"),
-					share_entry.share_type, share_entry.no_of_shares, share_entry.rate, share_entry.amount]
+				row = [
+					filters.get("shareholder"),
+					share_entry.share_type,
+					share_entry.no_of_shares,
+					share_entry.rate,
+					share_entry.amount,
+				]
 
 				data.append(row)
 
 	return columns, data
 
+
 def get_columns(filters):
 	columns = [
 		_("Shareholder") + ":Link/Shareholder:150",
 		_("Share Type") + "::90",
 		_("No of Shares") + "::90",
 		_("Average Rate") + ":Currency:90",
-		_("Amount") + ":Currency:90"
+		_("Amount") + ":Currency:90",
 	]
 	return columns
 
+
 def get_all_shares(shareholder):
-	return frappe.get_doc('Shareholder', shareholder).share_balance
+	return frappe.get_doc("Shareholder", shareholder).share_balance
diff --git a/erpnext/accounts/report/share_ledger/share_ledger.py b/erpnext/accounts/report/share_ledger/share_ledger.py
index b3ff6e4..d6c3bd0 100644
--- a/erpnext/accounts/report/share_ledger/share_ledger.py
+++ b/erpnext/accounts/report/share_ledger/share_ledger.py
@@ -7,7 +7,8 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	if not filters.get("date"):
 		frappe.throw(_("Please select date"))
@@ -23,19 +24,28 @@
 	else:
 		transfers = get_all_transfers(date, filters.get("shareholder"))
 		for transfer in transfers:
-			if transfer.transfer_type == 'Transfer':
+			if transfer.transfer_type == "Transfer":
 				if transfer.from_shareholder == filters.get("shareholder"):
-					transfer.transfer_type += ' to {}'.format(transfer.to_shareholder)
+					transfer.transfer_type += " to {}".format(transfer.to_shareholder)
 				else:
-					transfer.transfer_type += ' from {}'.format(transfer.from_shareholder)
-			row = [filters.get("shareholder"), transfer.date, transfer.transfer_type,
-				transfer.share_type, transfer.no_of_shares, transfer.rate, transfer.amount,
-				transfer.company, transfer.name]
+					transfer.transfer_type += " from {}".format(transfer.from_shareholder)
+			row = [
+				filters.get("shareholder"),
+				transfer.date,
+				transfer.transfer_type,
+				transfer.share_type,
+				transfer.no_of_shares,
+				transfer.rate,
+				transfer.amount,
+				transfer.company,
+				transfer.name,
+			]
 
 			data.append(row)
 
 	return columns, data
 
+
 def get_columns(filters):
 	columns = [
 		_("Shareholder") + ":Link/Shareholder:150",
@@ -46,16 +56,22 @@
 		_("Rate") + ":Currency:90",
 		_("Amount") + ":Currency:90",
 		_("Company") + "::150",
-		_("Share Transfer") + ":Link/Share Transfer:90"
+		_("Share Transfer") + ":Link/Share Transfer:90",
 	]
 	return columns
 
+
 def get_all_transfers(date, shareholder):
-	condition = ' '
+	condition = " "
 	# if company:
 	# 	condition = 'AND company = %(company)s '
-	return frappe.db.sql("""SELECT * FROM `tabShare Transfer`
+	return frappe.db.sql(
+		"""SELECT * FROM `tabShare Transfer`
 		WHERE (DATE(date) <= %(date)s AND from_shareholder = %(shareholder)s {condition})
 		OR (DATE(date) <= %(date)s AND to_shareholder = %(shareholder)s {condition})
-		ORDER BY date""".format(condition=condition),
-		{'date': date, 'shareholder': shareholder}, as_dict=1)
+		ORDER BY date""".format(
+			condition=condition
+		),
+		{"date": date, "shareholder": shareholder},
+		as_dict=1,
+	)
diff --git a/erpnext/accounts/report/tax_detail/tax_detail.py b/erpnext/accounts/report/tax_detail/tax_detail.py
index eeb8483..ba8d307 100644
--- a/erpnext/accounts/report/tax_detail/tax_detail.py
+++ b/erpnext/accounts/report/tax_detail/tax_detail.py
@@ -15,7 +15,13 @@
 	("GL Entry", 1): ["posting_date"],
 	("Account",): ["root_type", "account_type"],
 	("GL Entry", 2): ["account", "voucher_type", "voucher_no", "debit", "credit"],
-	("Purchase Invoice Item", "Sales Invoice Item"): ["base_net_amount", "item_tax_rate", "item_tax_template", "item_group", "item_name"],
+	("Purchase Invoice Item", "Sales Invoice Item"): [
+		"base_net_amount",
+		"item_tax_rate",
+		"item_tax_template",
+		"item_group",
+		"item_name",
+	],
 	("Purchase Invoice", "Sales Invoice"): ["taxes_and_charges", "tax_category"],
 }
 
@@ -27,7 +33,8 @@
 	fieldlist = required_sql_fields
 	fieldstr = get_fieldstr(fieldlist)
 
-	gl_entries = frappe.db.sql("""
+	gl_entries = frappe.db.sql(
+		"""
 		select {fieldstr}
 		from `tabGL Entry` ge
 		inner join `tabAccount` a on
@@ -45,61 +52,73 @@
 			ge.posting_date>=%(from_date)s and
 			ge.posting_date<=%(to_date)s
 		order by ge.posting_date, ge.voucher_no
-		""".format(fieldstr=fieldstr), filters, as_dict=1)
+		""".format(
+			fieldstr=fieldstr
+		),
+		filters,
+		as_dict=1,
+	)
 
 	report_data = modify_report_data(gl_entries)
 	summary = None
-	if filters['mode'] == 'run' and filters['report_name'] != 'Tax Detail':
-		report_data, summary = run_report(filters['report_name'], report_data)
+	if filters["mode"] == "run" and filters["report_name"] != "Tax Detail":
+		report_data, summary = run_report(filters["report_name"], report_data)
 
 	# return columns, data, message, chart, report_summary
 	return get_columns(fieldlist), report_data, None, None, summary
 
+
 def run_report(report_name, data):
 	"Applies the sections and filters saved in the custom report"
-	report_config = json.loads(frappe.get_doc('Report', report_name).json)
+	report_config = json.loads(frappe.get_doc("Report", report_name).json)
 	# Columns indexed from 1 wrt colno
-	columns = report_config.get('columns')
-	sections = report_config.get('sections', {})
-	show_detail = report_config.get('show_detail', 1)
+	columns = report_config.get("columns")
+	sections = report_config.get("sections", {})
+	show_detail = report_config.get("show_detail", 1)
 	report = {}
 	new_data = []
 	summary = []
 	for section_name, section in sections.items():
-		report[section_name] = {'rows': [], 'subtotal': 0.0}
+		report[section_name] = {"rows": [], "subtotal": 0.0}
 		for component_name, component in section.items():
-			if component['type'] == 'filter':
+			if component["type"] == "filter":
 				for row in data:
 					matched = True
-					for colno, filter_string in component['filters'].items():
-						filter_field = columns[int(colno) - 1]['fieldname']
+					for colno, filter_string in component["filters"].items():
+						filter_field = columns[int(colno) - 1]["fieldname"]
 						if not filter_match(row[filter_field], filter_string):
 							matched = False
 							break
 					if matched:
-						report[section_name]['rows'] += [row]
-						report[section_name]['subtotal'] += row['amount']
-			if component['type'] == 'section':
+						report[section_name]["rows"] += [row]
+						report[section_name]["subtotal"] += row["amount"]
+			if component["type"] == "section":
 				if component_name == section_name:
 					frappe.throw(_("A report component cannot refer to its parent section") + ": " + section_name)
 				try:
-					report[section_name]['rows'] += report[component_name]['rows']
-					report[section_name]['subtotal'] += report[component_name]['subtotal']
+					report[section_name]["rows"] += report[component_name]["rows"]
+					report[section_name]["subtotal"] += report[component_name]["subtotal"]
 				except KeyError:
-					frappe.throw(_("A report component can only refer to an earlier section") + ": " + section_name)
+					frappe.throw(
+						_("A report component can only refer to an earlier section") + ": " + section_name
+					)
 
 		if show_detail:
-			new_data += report[section_name]['rows']
-		new_data += [{'voucher_no': section_name, 'amount': report[section_name]['subtotal']}]
-		summary += [{'label': section_name, 'datatype': 'Currency', 'value': report[section_name]['subtotal']}]
+			new_data += report[section_name]["rows"]
+		new_data += [{"voucher_no": section_name, "amount": report[section_name]["subtotal"]}]
+		summary += [
+			{"label": section_name, "datatype": "Currency", "value": report[section_name]["subtotal"]}
+		]
 		if show_detail:
 			new_data += [{}]
 	return new_data or data, summary or None
 
+
 def filter_match(value, string):
 	"Approximation to datatable filters"
 	import datetime
-	if string == '':
+
+	if string == "":
 		return True
 	if value is None:
 		value = -999999999999999
@@ -109,64 +128,68 @@
 	if isinstance(value, str):
 		value = value.lower()
 		string = string.lower()
-		if string[0] == '<':
+		if string[0] == "<":
 			return True if string[1:].strip() else False
-		elif string[0] == '>':
+		elif string[0] == ">":
 			return False if string[1:].strip() else True
-		elif string[0] == '=':
+		elif string[0] == "=":
 			return string[1:] in value if string[1:] else False
-		elif string[0:2] == '!=':
+		elif string[0:2] == "!=":
 			return string[2:] not in value
-		elif len(string.split(':')) == 2:
-			pre, post = string.split(':')
-			return (True if not pre.strip() and post.strip() in value else False)
+		elif len(string.split(":")) == 2:
+			pre, post = string.split(":")
+			return True if not pre.strip() and post.strip() in value else False
 		else:
 			return string in value
 	else:
-		if string[0] in ['<', '>', '=']:
+		if string[0] in ["<", ">", "="]:
 			operator = string[0]
-			if operator == '=':
-				operator = '=='
+			if operator == "=":
+				operator = "=="
 			string = string[1:].strip()
-		elif string[0:2] == '!=':
-			operator = '!='
+		elif string[0:2] == "!=":
+			operator = "!="
 			string = string[2:].strip()
-		elif len(string.split(':')) == 2:
-			pre, post = string.split(':')
+		elif len(string.split(":")) == 2:
+			pre, post = string.split(":")
 			try:
-				return (True if float(pre) <= value and float(post) >= value else False)
+				return True if float(pre) <= value and float(post) >= value else False
 			except ValueError:
-				return (False if pre.strip() else True)
+				return False if pre.strip() else True
 		else:
 			return string in str(value)
 
 	try:
 		num = float(string) if string.strip() else 0
-		return frappe.safe_eval(f'{value} {operator} {num}')
+		return frappe.safe_eval(f"{value} {operator} {num}")
 	except ValueError:
-		if operator == '<':
+		if operator == "<":
 			return True
 		return False
 
 
 def abbrev(dt):
-	return ''.join(l[0].lower() for l in dt.split(' ')) + '.'
+	return "".join(l[0].lower() for l in dt.split(" ")) + "."
+
 
 def doclist(dt, dfs):
 	return [abbrev(dt) + f for f in dfs]
 
+
 def as_split(fields):
 	for field in fields:
-		split = field.split(' as ')
+		split = field.split(" as ")
 		yield (split[0], split[1] if len(split) > 1 else split[0])
 
+
 def coalesce(doctypes, fields):
 	coalesce = []
 	for name, new_name in as_split(fields):
-		sharedfields = ', '.join(abbrev(dt) + name for dt in doctypes)
-		coalesce += [f'coalesce({sharedfields}) as {new_name}']
+		sharedfields = ", ".join(abbrev(dt) + name for dt in doctypes)
+		coalesce += [f"coalesce({sharedfields}) as {new_name}"]
 	return coalesce
 
+
 def get_fieldstr(fieldlist):
 	fields = []
 	for doctypes, docfields in fieldlist.items():
@@ -174,7 +197,8 @@
 			fields += doclist(doctypes[0], docfields)
 		else:
 			fields += coalesce(doctypes, docfields)
-	return ', '.join(fields)
+	return ", ".join(fields)
+
 
 def get_columns(fieldlist):
 	columns = {}
@@ -186,14 +210,14 @@
 			meta = frappe.get_meta(doctype)
 			# get column field metadata from the db
 			fieldmeta = {}
-			for field in meta.get('fields'):
+			for field in meta.get("fields"):
 				if field.fieldname in fieldmap.keys():
 					new_name = fieldmap[field.fieldname]
 					fieldmeta[new_name] = {
 						"label": _(field.label),
 						"fieldname": new_name,
 						"fieldtype": field.fieldtype,
-						"options": field.options
+						"options": field.options,
 					}
 			# edit the columns to match the modified data
 			for field in fieldmap.values():
@@ -203,6 +227,7 @@
 	# use of a dict ensures duplicate columns are removed
 	return list(columns.values())
 
+
 def modify_report_columns(doctype, field, column):
 	"Because data is rearranged into other columns"
 	if doctype in ["Sales Invoice Item", "Purchase Invoice Item"]:
@@ -216,8 +241,10 @@
 		column.update({"label": _("Taxes and Charges Template")})
 	return column
 
+
 def modify_report_data(data):
 	import json
+
 	new_data = []
 	for line in data:
 		if line.debit:
@@ -249,39 +276,37 @@
 # JS client utilities
 
 custom_report_dict = {
-	'ref_doctype': 'GL Entry',
-	'report_type': 'Custom Report',
-	'reference_report': 'Tax Detail'
+	"ref_doctype": "GL Entry",
+	"report_type": "Custom Report",
+	"reference_report": "Tax Detail",
 }
 
+
 @frappe.whitelist()
 def get_custom_reports(name=None):
 	filters = custom_report_dict.copy()
 	if name:
-		filters['name'] = name
-	reports = frappe.get_list('Report',
-		filters = filters,
-		fields = ['name', 'json'],
-		as_list=False
-	)
-	reports_dict = {rep.pop('name'): rep for rep in reports}
+		filters["name"] = name
+	reports = frappe.get_list("Report", filters=filters, fields=["name", "json"], as_list=False)
+	reports_dict = {rep.pop("name"): rep for rep in reports}
 	# Prevent custom reports with the same name
-	reports_dict['Tax Detail'] = {'json': None}
+	reports_dict["Tax Detail"] = {"json": None}
 	return reports_dict
 
+
 @frappe.whitelist()
 def save_custom_report(reference_report, report_name, data):
-	if reference_report != 'Tax Detail':
+	if reference_report != "Tax Detail":
 		frappe.throw(_("The wrong report is referenced."))
-	if report_name == 'Tax Detail':
+	if report_name == "Tax Detail":
 		frappe.throw(_("The parent report cannot be overwritten."))
 
 	doc = {
-		'doctype': 'Report',
-		'report_name': report_name,
-		'is_standard': 'No',
-		'module': 'Accounts',
-		'json': data
+		"doctype": "Report",
+		"report_name": report_name,
+		"is_standard": "No",
+		"module": "Accounts",
+		"json": data,
 	}
 	doc.update(custom_report_dict)
 
@@ -290,7 +315,7 @@
 		newdoc.insert()
 		frappe.msgprint(_("Report created successfully"))
 	except frappe.exceptions.DuplicateEntryError:
-		dbdoc = frappe.get_doc('Report', report_name)
+		dbdoc = frappe.get_doc("Report", report_name)
 		dbdoc.update(doc)
 		dbdoc.save()
 		frappe.msgprint(_("Report updated successfully"))
diff --git a/erpnext/accounts/report/tax_detail/test_tax_detail.json b/erpnext/accounts/report/tax_detail/test_tax_detail.json
index 3a4b175..e490316 100644
--- a/erpnext/accounts/report/tax_detail/test_tax_detail.json
+++ b/erpnext/accounts/report/tax_detail/test_tax_detail.json
@@ -302,7 +302,7 @@
   "is_opening": "No",
   "is_paid": 0,
   "is_return": 0,
-  "is_subcontracted": "No",
+  "is_subcontracted": 0,
   "items": [
    {
     "allow_zero_valuation_rate": 0,
diff --git a/erpnext/accounts/report/tax_detail/test_tax_detail.py b/erpnext/accounts/report/tax_detail/test_tax_detail.py
index 621de82..869f245 100644
--- a/erpnext/accounts/report/tax_detail/test_tax_detail.py
+++ b/erpnext/accounts/report/tax_detail/test_tax_detail.py
@@ -19,8 +19,9 @@
 class TestTaxDetail(unittest.TestCase):
 	def load_testdocs(self):
 		from erpnext.accounts.utils import FiscalYearError, get_fiscal_year
+
 		datapath, _ = os.path.splitext(os.path.realpath(__file__))
-		with open(datapath + '.json', 'r') as fp:
+		with open(datapath + ".json", "r") as fp:
 			docs = json.load(fp)
 
 		now = getdate()
@@ -30,32 +31,38 @@
 		try:
 			get_fiscal_year(now, company="_T")
 		except FiscalYearError:
-			docs = [{
-				"companies": [{
-					"company": "_T",
-					"parent": "_Test Fiscal",
-					"parentfield": "companies",
-					"parenttype": "Fiscal Year"
-				}],
-				"doctype": "Fiscal Year",
-				"year": "_Test Fiscal",
-				"year_end_date": get_year_ending(now),
-				"year_start_date": get_year_start(now)
-			}] + docs
+			docs = [
+				{
+					"companies": [
+						{
+							"company": "_T",
+							"parent": "_Test Fiscal",
+							"parentfield": "companies",
+							"parenttype": "Fiscal Year",
+						}
+					],
+					"doctype": "Fiscal Year",
+					"year": "_Test Fiscal",
+					"year_end_date": get_year_ending(now),
+					"year_start_date": get_year_start(now),
+				}
+			] + docs
 
-		docs = [{
-			"abbr": "_T",
-			"company_name": "_T",
-			"country": "United Kingdom",
-			"default_currency": "GBP",
-			"doctype": "Company",
-			"name": "_T"
-		}] + docs
+		docs = [
+			{
+				"abbr": "_T",
+				"company_name": "_T",
+				"country": "United Kingdom",
+				"default_currency": "GBP",
+				"doctype": "Company",
+				"name": "_T",
+			}
+		] + docs
 
 		for doc in docs:
 			try:
 				db_doc = frappe.get_doc(doc)
-				if 'Invoice' in db_doc.doctype:
+				if "Invoice" in db_doc.doctype:
 					db_doc.due_date = add_to_date(now, days=1)
 					db_doc.insert()
 					# Create GL Entries:
@@ -66,119 +73,141 @@
 				pass
 
 	def load_defcols(self):
-		self.company = frappe.get_doc('Company', '_T')
-		custom_report = frappe.get_doc('Report', 'Tax Detail')
+		self.company = frappe.get_doc("Company", "_T")
+		custom_report = frappe.get_doc("Report", "Tax Detail")
 		self.default_columns, _ = custom_report.run_query_report(
 			filters={
-				'from_date': '2021-03-01',
-				'to_date': '2021-03-31',
-				'company': self.company.name,
-				'mode': 'run',
-				'report_name': 'Tax Detail'
-			}, user=frappe.session.user)
+				"from_date": "2021-03-01",
+				"to_date": "2021-03-31",
+				"company": self.company.name,
+				"mode": "run",
+				"report_name": "Tax Detail",
+			},
+			user=frappe.session.user,
+		)
 
 	def rm_testdocs(self):
 		"Remove the Company and all data"
 		from erpnext.setup.doctype.company.company import create_transaction_deletion_request
+
 		create_transaction_deletion_request(self.company.name)
 
 	def test_report(self):
 		self.load_testdocs()
 		self.load_defcols()
 		report_name = save_custom_report(
-			'Tax Detail',
-			'_Test Tax Detail',
-			json.dumps({
-				'columns': self.default_columns,
-				'sections': {
-					'Box1':{'Filter0':{'type':'filter','filters':{'4':'VAT on Sales'}}},
-					'Box2':{'Filter0':{'type':'filter','filters':{'4':'Acquisition'}}},
-					'Box3':{'Box1':{'type':'section'},'Box2':{'type':'section'}},
-					'Box4':{'Filter0':{'type':'filter','filters':{'4':'VAT on Purchases'}}},
-					'Box5':{'Box3':{'type':'section'},'Box4':{'type':'section'}},
-					'Box6':{'Filter0':{'type':'filter','filters':{'3':'!=Tax','4':'Sales'}}},
-					'Box7':{'Filter0':{'type':'filter','filters':{'2':'Expense','3':'!=Tax'}}},
-					'Box8':{'Filter0':{'type':'filter','filters':{'3':'!=Tax','4':'Sales','12':'EU'}}},
-					'Box9':{'Filter0':{'type':'filter','filters':{'2':'Expense','3':'!=Tax','12':'EU'}}}
-				},
-				'show_detail': 1
-			}))
-		data = frappe.desk.query_report.run(report_name,
+			"Tax Detail",
+			"_Test Tax Detail",
+			json.dumps(
+				{
+					"columns": self.default_columns,
+					"sections": {
+						"Box1": {"Filter0": {"type": "filter", "filters": {"4": "VAT on Sales"}}},
+						"Box2": {"Filter0": {"type": "filter", "filters": {"4": "Acquisition"}}},
+						"Box3": {"Box1": {"type": "section"}, "Box2": {"type": "section"}},
+						"Box4": {"Filter0": {"type": "filter", "filters": {"4": "VAT on Purchases"}}},
+						"Box5": {"Box3": {"type": "section"}, "Box4": {"type": "section"}},
+						"Box6": {"Filter0": {"type": "filter", "filters": {"3": "!=Tax", "4": "Sales"}}},
+						"Box7": {"Filter0": {"type": "filter", "filters": {"2": "Expense", "3": "!=Tax"}}},
+						"Box8": {"Filter0": {"type": "filter", "filters": {"3": "!=Tax", "4": "Sales", "12": "EU"}}},
+						"Box9": {
+							"Filter0": {"type": "filter", "filters": {"2": "Expense", "3": "!=Tax", "12": "EU"}}
+						},
+					},
+					"show_detail": 1,
+				}
+			),
+		)
+		data = frappe.desk.query_report.run(
+			report_name,
 			filters={
-				'from_date': self.from_date,
-				'to_date': self.to_date,
-				'company': self.company.name,
-				'mode': 'run',
-				'report_name': report_name
-			}, user=frappe.session.user)
+				"from_date": self.from_date,
+				"to_date": self.to_date,
+				"company": self.company.name,
+				"mode": "run",
+				"report_name": report_name,
+			},
+			user=frappe.session.user,
+		)
 
-		self.assertListEqual(data.get('columns'), self.default_columns)
-		expected = (('Box1', 43.25), ('Box2', 0.0), ('Box3', 43.25), ('Box4', -85.28), ('Box5', -42.03),
-			('Box6', 825.0), ('Box7', -426.40), ('Box8', 0.0), ('Box9', 0.0))
+		self.assertListEqual(data.get("columns"), self.default_columns)
+		expected = (
+			("Box1", 43.25),
+			("Box2", 0.0),
+			("Box3", 43.25),
+			("Box4", -85.28),
+			("Box5", -42.03),
+			("Box6", 825.0),
+			("Box7", -426.40),
+			("Box8", 0.0),
+			("Box9", 0.0),
+		)
 		exrow = iter(expected)
-		for row in data.get('result'):
-			if row.get('voucher_no') and not row.get('posting_date'):
+		for row in data.get("result"):
+			if row.get("voucher_no") and not row.get("posting_date"):
 				label, value = next(exrow)
-				self.assertDictEqual(row, {'voucher_no': label, 'amount': value})
-		self.assertListEqual(data.get('report_summary'),
-			[{'label': label, 'datatype': 'Currency', 'value': value} for label, value in expected])
+				self.assertDictEqual(row, {"voucher_no": label, "amount": value})
+		self.assertListEqual(
+			data.get("report_summary"),
+			[{"label": label, "datatype": "Currency", "value": value} for label, value in expected],
+		)
 
 		self.rm_testdocs()
 
 	def test_filter_match(self):
 		# None - treated as -inf number except range
-		self.assertTrue(filter_match(None, '!='))
-		self.assertTrue(filter_match(None, '<'))
-		self.assertTrue(filter_match(None, '<jjj'))
-		self.assertTrue(filter_match(None, '  :  '))
-		self.assertTrue(filter_match(None, ':56'))
-		self.assertTrue(filter_match(None, ':de'))
-		self.assertFalse(filter_match(None, '3.4'))
-		self.assertFalse(filter_match(None, '='))
-		self.assertFalse(filter_match(None, '=3.4'))
-		self.assertFalse(filter_match(None, '>3.4'))
-		self.assertFalse(filter_match(None, '   <'))
-		self.assertFalse(filter_match(None, 'ew'))
-		self.assertFalse(filter_match(None, ' '))
-		self.assertFalse(filter_match(None, ' f :'))
+		self.assertTrue(filter_match(None, "!="))
+		self.assertTrue(filter_match(None, "<"))
+		self.assertTrue(filter_match(None, "<jjj"))
+		self.assertTrue(filter_match(None, "  :  "))
+		self.assertTrue(filter_match(None, ":56"))
+		self.assertTrue(filter_match(None, ":de"))
+		self.assertFalse(filter_match(None, "3.4"))
+		self.assertFalse(filter_match(None, "="))
+		self.assertFalse(filter_match(None, "=3.4"))
+		self.assertFalse(filter_match(None, ">3.4"))
+		self.assertFalse(filter_match(None, "   <"))
+		self.assertFalse(filter_match(None, "ew"))
+		self.assertFalse(filter_match(None, " "))
+		self.assertFalse(filter_match(None, " f :"))
 
 		# Numbers
-		self.assertTrue(filter_match(3.4, '3.4'))
-		self.assertTrue(filter_match(3.4, '.4'))
-		self.assertTrue(filter_match(3.4, '3'))
-		self.assertTrue(filter_match(-3.4, '< -3'))
-		self.assertTrue(filter_match(-3.4, '> -4'))
-		self.assertTrue(filter_match(3.4, '= 3.4 '))
-		self.assertTrue(filter_match(3.4, '!=4.5'))
-		self.assertTrue(filter_match(3.4, ' 3 : 4 '))
-		self.assertTrue(filter_match(0.0, '  :  '))
-		self.assertFalse(filter_match(3.4, '=4.5'))
-		self.assertFalse(filter_match(3.4, ' = 3.4 '))
-		self.assertFalse(filter_match(3.4, '!=3.4'))
-		self.assertFalse(filter_match(3.4, '>6'))
-		self.assertFalse(filter_match(3.4, '<-4.5'))
-		self.assertFalse(filter_match(3.4, '4.5'))
-		self.assertFalse(filter_match(3.4, '5:9'))
+		self.assertTrue(filter_match(3.4, "3.4"))
+		self.assertTrue(filter_match(3.4, ".4"))
+		self.assertTrue(filter_match(3.4, "3"))
+		self.assertTrue(filter_match(-3.4, "< -3"))
+		self.assertTrue(filter_match(-3.4, "> -4"))
+		self.assertTrue(filter_match(3.4, "= 3.4 "))
+		self.assertTrue(filter_match(3.4, "!=4.5"))
+		self.assertTrue(filter_match(3.4, " 3 : 4 "))
+		self.assertTrue(filter_match(0.0, "  :  "))
+		self.assertFalse(filter_match(3.4, "=4.5"))
+		self.assertFalse(filter_match(3.4, " = 3.4 "))
+		self.assertFalse(filter_match(3.4, "!=3.4"))
+		self.assertFalse(filter_match(3.4, ">6"))
+		self.assertFalse(filter_match(3.4, "<-4.5"))
+		self.assertFalse(filter_match(3.4, "4.5"))
+		self.assertFalse(filter_match(3.4, "5:9"))
 
 		# Strings
-		self.assertTrue(filter_match('ACC-SINV-2021-00001', 'SINV'))
-		self.assertTrue(filter_match('ACC-SINV-2021-00001', 'sinv'))
-		self.assertTrue(filter_match('ACC-SINV-2021-00001', '-2021'))
-		self.assertTrue(filter_match(' ACC-SINV-2021-00001', ' acc'))
-		self.assertTrue(filter_match('ACC-SINV-2021-00001', '=2021'))
-		self.assertTrue(filter_match('ACC-SINV-2021-00001', '!=zz'))
-		self.assertTrue(filter_match('ACC-SINV-2021-00001', '<   zzz  '))
-		self.assertTrue(filter_match('ACC-SINV-2021-00001', '  :  sinv  '))
-		self.assertFalse(filter_match('ACC-SINV-2021-00001', '  sinv  :'))
-		self.assertFalse(filter_match('ACC-SINV-2021-00001', ' acc'))
-		self.assertFalse(filter_match('ACC-SINV-2021-00001', '= 2021 '))
-		self.assertFalse(filter_match('ACC-SINV-2021-00001', '!=sinv'))
-		self.assertFalse(filter_match('ACC-SINV-2021-00001', ' >'))
-		self.assertFalse(filter_match('ACC-SINV-2021-00001', '>aa'))
-		self.assertFalse(filter_match('ACC-SINV-2021-00001', ' <'))
-		self.assertFalse(filter_match('ACC-SINV-2021-00001', '<   '))
-		self.assertFalse(filter_match('ACC-SINV-2021-00001', ' ='))
-		self.assertFalse(filter_match('ACC-SINV-2021-00001', '='))
+		self.assertTrue(filter_match("ACC-SINV-2021-00001", "SINV"))
+		self.assertTrue(filter_match("ACC-SINV-2021-00001", "sinv"))
+		self.assertTrue(filter_match("ACC-SINV-2021-00001", "-2021"))
+		self.assertTrue(filter_match(" ACC-SINV-2021-00001", " acc"))
+		self.assertTrue(filter_match("ACC-SINV-2021-00001", "=2021"))
+		self.assertTrue(filter_match("ACC-SINV-2021-00001", "!=zz"))
+		self.assertTrue(filter_match("ACC-SINV-2021-00001", "<   zzz  "))
+		self.assertTrue(filter_match("ACC-SINV-2021-00001", "  :  sinv  "))
+		self.assertFalse(filter_match("ACC-SINV-2021-00001", "  sinv  :"))
+		self.assertFalse(filter_match("ACC-SINV-2021-00001", " acc"))
+		self.assertFalse(filter_match("ACC-SINV-2021-00001", "= 2021 "))
+		self.assertFalse(filter_match("ACC-SINV-2021-00001", "!=sinv"))
+		self.assertFalse(filter_match("ACC-SINV-2021-00001", " >"))
+		self.assertFalse(filter_match("ACC-SINV-2021-00001", ">aa"))
+		self.assertFalse(filter_match("ACC-SINV-2021-00001", " <"))
+		self.assertFalse(filter_match("ACC-SINV-2021-00001", "<   "))
+		self.assertFalse(filter_match("ACC-SINV-2021-00001", " ="))
+		self.assertFalse(filter_match("ACC-SINV-2021-00001", "="))
 
 		# Date - always match
-		self.assertTrue(filter_match(datetime.date(2021, 3, 19), ' kdsjkldfs '))
+		self.assertTrue(filter_match(datetime.date(2021, 3, 19), " kdsjkldfs "))
diff --git a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py
index 07f2e43..08d2008 100644
--- a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py
+++ b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py
@@ -11,7 +11,7 @@
 def execute(filters=None):
 	validate_filters(filters)
 
-	filters.naming_series = frappe.db.get_single_value('Buying Settings', 'supp_master_name')
+	filters.naming_series = frappe.db.get_single_value("Buying Settings", "supp_master_name")
 
 	columns = get_columns(filters)
 	tds_docs, tds_accounts, tax_category_map = get_tds_docs(filters)
@@ -21,8 +21,9 @@
 
 	return columns, final_result
 
+
 def validate_filters(filters):
-	''' Validate if dates are properly set and lie in the same fiscal year'''
+	"""Validate if dates are properly set and lie in the same fiscal year"""
 	if filters.from_date > filters.to_date:
 		frappe.throw(_("From Date must be before To Date"))
 
@@ -33,26 +34,32 @@
 
 	filters["fiscal_year"] = from_year
 
+
 def group_by_supplier_and_category(data):
 	supplier_category_wise_map = {}
 
 	for row in data:
-		supplier_category_wise_map.setdefault((row.get('supplier'), row.get('section_code')), {
-			'pan': row.get('pan'),
-			'supplier': row.get('supplier'),
-			'supplier_name': row.get('supplier_name'),
-			'section_code': row.get('section_code'),
-			'entity_type': row.get('entity_type'),
-			'tds_rate': row.get('tds_rate'),
-			'total_amount_credited': 0.0,
-			'tds_deducted': 0.0
-		})
+		supplier_category_wise_map.setdefault(
+			(row.get("supplier"), row.get("section_code")),
+			{
+				"pan": row.get("pan"),
+				"supplier": row.get("supplier"),
+				"supplier_name": row.get("supplier_name"),
+				"section_code": row.get("section_code"),
+				"entity_type": row.get("entity_type"),
+				"tds_rate": row.get("tds_rate"),
+				"total_amount_credited": 0.0,
+				"tds_deducted": 0.0,
+			},
+		)
 
-		supplier_category_wise_map.get((row.get('supplier'), row.get('section_code')))['total_amount_credited'] += \
-			row.get('total_amount_credited', 0.0)
+		supplier_category_wise_map.get((row.get("supplier"), row.get("section_code")))[
+			"total_amount_credited"
+		] += row.get("total_amount_credited", 0.0)
 
-		supplier_category_wise_map.get((row.get('supplier'), row.get('section_code')))['tds_deducted'] += \
-			row.get('tds_deducted', 0.0)
+		supplier_category_wise_map.get((row.get("supplier"), row.get("section_code")))[
+			"tds_deducted"
+		] += row.get("tds_deducted", 0.0)
 
 	final_result = get_final_result(supplier_category_wise_map)
 
@@ -66,62 +73,48 @@
 
 	return out
 
+
 def get_columns(filters):
 	columns = [
-		{
-			"label": _("PAN"),
-			"fieldname": "pan",
-			"fieldtype": "Data",
-			"width": 90
-		},
+		{"label": _("PAN"), "fieldname": "pan", "fieldtype": "Data", "width": 90},
 		{
 			"label": _("Supplier"),
 			"options": "Supplier",
 			"fieldname": "supplier",
 			"fieldtype": "Link",
-			"width": 180
-		}]
+			"width": 180,
+		},
+	]
 
-	if filters.naming_series == 'Naming Series':
-		columns.append({
-			"label": _("Supplier Name"),
-			"fieldname": "supplier_name",
-			"fieldtype": "Data",
-			"width": 180
-		})
+	if filters.naming_series == "Naming Series":
+		columns.append(
+			{"label": _("Supplier Name"), "fieldname": "supplier_name", "fieldtype": "Data", "width": 180}
+		)
 
-	columns.extend([
-		{
-			"label": _("Section Code"),
-			"options": "Tax Withholding Category",
-			"fieldname": "section_code",
-			"fieldtype": "Link",
-			"width": 180
-		},
-		{
-			"label": _("Entity Type"),
-			"fieldname": "entity_type",
-			"fieldtype": "Data",
-			"width": 180
-		},
-		{
-			"label": _("TDS Rate %"),
-			"fieldname": "tds_rate",
-			"fieldtype": "Percent",
-			"width": 90
-		},
-		{
-			"label": _("Total Amount Credited"),
-			"fieldname": "total_amount_credited",
-			"fieldtype": "Float",
-			"width": 90
-		},
-		{
-			"label": _("Amount of TDS Deducted"),
-			"fieldname": "tds_deducted",
-			"fieldtype": "Float",
-			"width": 90
-		}
-	])
+	columns.extend(
+		[
+			{
+				"label": _("Section Code"),
+				"options": "Tax Withholding Category",
+				"fieldname": "section_code",
+				"fieldtype": "Link",
+				"width": 180,
+			},
+			{"label": _("Entity Type"), "fieldname": "entity_type", "fieldtype": "Data", "width": 180},
+			{"label": _("TDS Rate %"), "fieldname": "tds_rate", "fieldtype": "Percent", "width": 90},
+			{
+				"label": _("Total Amount Credited"),
+				"fieldname": "total_amount_credited",
+				"fieldtype": "Float",
+				"width": 90,
+			},
+			{
+				"label": _("Amount of TDS Deducted"),
+				"fieldname": "tds_deducted",
+				"fieldtype": "Float",
+				"width": 90,
+			},
+		]
+	)
 
 	return columns
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 e6cbff5..30ea5a9 100644
--- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
+++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
@@ -15,11 +15,13 @@
 	res = get_result(filters, tds_docs, tds_accounts, tax_category_map)
 	return columns, res
 
+
 def validate_filters(filters):
-	''' Validate if dates are properly set '''
+	"""Validate if dates are properly set"""
 	if filters.from_date > filters.to_date:
 		frappe.throw(_("From Date must be before To Date"))
 
+
 def get_result(filters, tds_docs, tds_accounts, tax_category_map):
 	supplier_map = get_supplier_pan_map()
 	tax_rate_map = get_tax_rate_map(filters)
@@ -37,57 +39,63 @@
 			voucher_type = entry.voucher_type
 
 			if not tax_withholding_category:
-				tax_withholding_category = supplier_map.get(supplier, {}).get('tax_withholding_category')
+				tax_withholding_category = supplier_map.get(supplier, {}).get("tax_withholding_category")
 				rate = tax_rate_map.get(tax_withholding_category)
 
 			if entry.account in tds_accounts:
-				tds_deducted += (entry.credit - entry.debit)
+				tds_deducted += entry.credit - entry.debit
 
 			total_amount_credited += entry.credit
 
 		if tds_deducted:
 			row = {
-				'pan' if frappe.db.has_column('Supplier', 'pan') else 'tax_id': supplier_map.get(supplier, {}).get('pan'),
-				'supplier': supplier_map.get(supplier, {}).get('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, {}).get('supplier_name')})
+			if filters.naming_series == "Naming Series":
+				row.update({"supplier_name": supplier_map.get(supplier, {}).get("supplier_name")})
 
-			row.update({
-				'section_code': tax_withholding_category,
-				'entity_type': supplier_map.get(supplier, {}).get('supplier_type'),
-				'tds_rate': rate,
-				'total_amount_credited': total_amount_credited,
-				'tds_deducted': tds_deducted,
-				'transaction_date': posting_date,
-				'transaction_type': voucher_type,
-				'ref_no': name
-			})
+			row.update(
+				{
+					"section_code": tax_withholding_category,
+					"entity_type": supplier_map.get(supplier, {}).get("supplier_type"),
+					"tds_rate": rate,
+					"total_amount_credited": total_amount_credited,
+					"tds_deducted": tds_deducted,
+					"transaction_date": posting_date,
+					"transaction_type": voucher_type,
+					"ref_no": name,
+				}
+			)
 
 			out.append(row)
 
 	return out
 
+
 def get_supplier_pan_map():
 	supplier_map = frappe._dict()
-	suppliers = frappe.db.get_all('Supplier', fields=['name', 'pan', 'supplier_type', 'supplier_name', 'tax_withholding_category'])
+	suppliers = frappe.db.get_all(
+		"Supplier", fields=["name", "pan", "supplier_type", "supplier_name", "tax_withholding_category"]
+	)
 
 	for d in suppliers:
 		supplier_map[d.name] = d
 
 	return supplier_map
 
+
 def get_gle_map(documents):
 	# create gle_map of the form
 	# {"purchase_invoice": list of dict of all gle created for this invoice}
 	gle_map = {}
 
-	gle = frappe.db.get_all('GL Entry',
-		{
-			"voucher_no": ["in", documents],
-			"is_cancelled": 0
-		},
+	gle = frappe.db.get_all(
+		"GL Entry",
+		{"voucher_no": ["in", documents], "is_cancelled": 0},
 		["credit", "debit", "account", "voucher_no", "posting_date", "voucher_type", "against", "party"],
 	)
 
@@ -99,85 +107,68 @@
 
 	return gle_map
 
+
 def get_columns(filters):
 	pan = "pan" if frappe.db.has_column("Supplier", "pan") else "tax_id"
 	columns = [
-		{
-			"label": _(frappe.unscrub(pan)),
-			"fieldname": pan,
-			"fieldtype": "Data",
-			"width": 90
-		},
+		{"label": _(frappe.unscrub(pan)), "fieldname": pan, "fieldtype": "Data", "width": 90},
 		{
 			"label": _("Supplier"),
 			"options": "Supplier",
 			"fieldname": "supplier",
 			"fieldtype": "Link",
-			"width": 180
-		}]
+			"width": 180,
+		},
+	]
 
-	if filters.naming_series == 'Naming Series':
-		columns.append({
-			"label": _("Supplier Name"),
-			"fieldname": "supplier_name",
-			"fieldtype": "Data",
-			"width": 180
-		})
+	if filters.naming_series == "Naming Series":
+		columns.append(
+			{"label": _("Supplier Name"), "fieldname": "supplier_name", "fieldtype": "Data", "width": 180}
+		)
 
-	columns.extend([
-		{
-			"label": _("Section Code"),
-			"options": "Tax Withholding Category",
-			"fieldname": "section_code",
-			"fieldtype": "Link",
-			"width": 180
-		},
-		{
-			"label": _("Entity Type"),
-			"fieldname": "entity_type",
-			"fieldtype": "Data",
-			"width": 180
-		},
-		{
-			"label": _("TDS Rate %"),
-			"fieldname": "tds_rate",
-			"fieldtype": "Percent",
-			"width": 90
-		},
-		{
-			"label": _("Total Amount Credited"),
-			"fieldname": "total_amount_credited",
-			"fieldtype": "Float",
-			"width": 90
-		},
-		{
-			"label": _("Amount of TDS Deducted"),
-			"fieldname": "tds_deducted",
-			"fieldtype": "Float",
-			"width": 90
-		},
-		{
-			"label": _("Date of Transaction"),
-			"fieldname": "transaction_date",
-			"fieldtype": "Date",
-			"width": 90
-		},
-		{
-			"label": _("Transaction Type"),
-			"fieldname": "transaction_type",
-			"width": 90
-		},
-		{
-			"label": _("Reference No."),
-			"fieldname": "ref_no",
-			"fieldtype": "Dynamic Link",
-			"options": "transaction_type",
-			"width": 90
-		}
-	])
+	columns.extend(
+		[
+			{
+				"label": _("Section Code"),
+				"options": "Tax Withholding Category",
+				"fieldname": "section_code",
+				"fieldtype": "Link",
+				"width": 180,
+			},
+			{"label": _("Entity Type"), "fieldname": "entity_type", "fieldtype": "Data", "width": 180},
+			{"label": _("TDS Rate %"), "fieldname": "tds_rate", "fieldtype": "Percent", "width": 90},
+			{
+				"label": _("Total Amount Credited"),
+				"fieldname": "total_amount_credited",
+				"fieldtype": "Float",
+				"width": 90,
+			},
+			{
+				"label": _("Amount of TDS Deducted"),
+				"fieldname": "tds_deducted",
+				"fieldtype": "Float",
+				"width": 90,
+			},
+			{
+				"label": _("Date of Transaction"),
+				"fieldname": "transaction_date",
+				"fieldtype": "Date",
+				"width": 90,
+			},
+			{"label": _("Transaction Type"), "fieldname": "transaction_type", "width": 90},
+			{
+				"label": _("Reference No."),
+				"fieldname": "ref_no",
+				"fieldtype": "Dynamic Link",
+				"options": "transaction_type",
+				"width": 90,
+			},
+		]
+	)
 
 	return columns
 
+
 def get_tds_docs(filters):
 	tds_documents = []
 	purchase_invoices = []
@@ -185,27 +176,30 @@
 	journal_entries = []
 	tax_category_map = {}
 	or_filters = {}
-	bank_accounts = frappe.get_all('Account', {'is_group': 0, 'account_type': 'Bank'}, pluck="name")
+	bank_accounts = frappe.get_all("Account", {"is_group": 0, "account_type": "Bank"}, pluck="name")
 
-	tds_accounts = frappe.get_all("Tax Withholding Account", {'company': filters.get('company')},
-		pluck="account")
+	tds_accounts = frappe.get_all(
+		"Tax Withholding Account", {"company": filters.get("company")}, pluck="account"
+	)
 
 	query_filters = {
 		"account": ("in", tds_accounts),
 		"posting_date": ("between", [filters.get("from_date"), filters.get("to_date")]),
 		"is_cancelled": 0,
-		"against": ("not in", bank_accounts)
+		"against": ("not in", bank_accounts),
 	}
 
 	if filters.get("supplier"):
 		del query_filters["account"]
 		del query_filters["against"]
-		or_filters = {
-			"against": filters.get('supplier'),
-			"party": filters.get('supplier')
-		}
+		or_filters = {"against": filters.get("supplier"), "party": filters.get("supplier")}
 
-	tds_docs = frappe.get_all("GL Entry", filters=query_filters, or_filters=or_filters, fields=["voucher_no", "voucher_type", "against", "party"])
+	tds_docs = frappe.get_all(
+		"GL Entry",
+		filters=query_filters,
+		or_filters=or_filters,
+		fields=["voucher_no", "voucher_type", "against", "party"],
+	)
 
 	for d in tds_docs:
 		if d.voucher_type == "Purchase Invoice":
@@ -218,24 +212,39 @@
 		tds_documents.append(d.voucher_no)
 
 	if purchase_invoices:
-		get_tax_category_map(purchase_invoices, 'Purchase Invoice', tax_category_map)
+		get_tax_category_map(purchase_invoices, "Purchase Invoice", tax_category_map)
 
 	if payment_entries:
-		get_tax_category_map(payment_entries, 'Payment Entry', tax_category_map)
+		get_tax_category_map(payment_entries, "Payment Entry", tax_category_map)
 
 	if journal_entries:
-		get_tax_category_map(journal_entries, 'Journal Entry', tax_category_map)
+		get_tax_category_map(journal_entries, "Journal Entry", tax_category_map)
 
 	return tds_documents, tds_accounts, tax_category_map
 
+
 def get_tax_category_map(vouchers, doctype, tax_category_map):
-	tax_category_map.update(frappe._dict(frappe.get_all(doctype,
-		filters = {'name': ('in', vouchers)}, fields=['name', 'tax_withholding_category'], as_list=1)))
+	tax_category_map.update(
+		frappe._dict(
+			frappe.get_all(
+				doctype,
+				filters={"name": ("in", vouchers)},
+				fields=["name", "tax_withholding_category"],
+				as_list=1,
+			)
+		)
+	)
+
 
 def get_tax_rate_map(filters):
-	rate_map = frappe.get_all('Tax Withholding Rate', filters={
-		'from_date': ('<=', filters.get('from_date')),
-		'to_date': ('>=', filters.get('to_date'))
-	}, fields=['parent', 'tax_withholding_rate'], as_list=1)
+	rate_map = frappe.get_all(
+		"Tax Withholding Rate",
+		filters={
+			"from_date": ("<=", filters.get("from_date")),
+			"to_date": (">=", filters.get("to_date")),
+		},
+		fields=["parent", "tax_withholding_rate"],
+		as_list=1,
+	)
 
-	return frappe._dict(rate_map)
\ No newline at end of file
+	return frappe._dict(rate_map)
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py
index bda44f6..6bd08ad 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.py
+++ b/erpnext/accounts/report/trial_balance/trial_balance.py
@@ -17,7 +17,15 @@
 	set_gl_entries_by_account,
 )
 
-value_fields = ("opening_debit", "opening_credit", "debit", "credit", "closing_debit", "closing_credit")
+value_fields = (
+	"opening_debit",
+	"opening_credit",
+	"debit",
+	"credit",
+	"closing_debit",
+	"closing_credit",
+)
+
 
 def execute(filters=None):
 	validate_filters(filters)
@@ -25,11 +33,14 @@
 	columns = get_columns()
 	return columns, data
 
+
 def validate_filters(filters):
 	if not filters.fiscal_year:
 		frappe.throw(_("Fiscal Year {0} is required").format(filters.fiscal_year))
 
-	fiscal_year = frappe.db.get_value("Fiscal Year", filters.fiscal_year, ["year_start_date", "year_end_date"], as_dict=True)
+	fiscal_year = frappe.db.get_value(
+		"Fiscal Year", filters.fiscal_year, ["year_start_date", "year_end_date"], as_dict=True
+	)
 	if not fiscal_year:
 		frappe.throw(_("Fiscal Year {0} does not exist").format(filters.fiscal_year))
 	else:
@@ -49,21 +60,32 @@
 		frappe.throw(_("From Date cannot be greater than To Date"))
 
 	if (filters.from_date < filters.year_start_date) or (filters.from_date > filters.year_end_date):
-		frappe.msgprint(_("From Date should be within the Fiscal Year. Assuming From Date = {0}")\
-			.format(formatdate(filters.year_start_date)))
+		frappe.msgprint(
+			_("From Date should be within the Fiscal Year. Assuming From Date = {0}").format(
+				formatdate(filters.year_start_date)
+			)
+		)
 
 		filters.from_date = filters.year_start_date
 
 	if (filters.to_date < filters.year_start_date) or (filters.to_date > filters.year_end_date):
-		frappe.msgprint(_("To Date should be within the Fiscal Year. Assuming To Date = {0}")\
-			.format(formatdate(filters.year_end_date)))
+		frappe.msgprint(
+			_("To Date should be within the Fiscal Year. Assuming To Date = {0}").format(
+				formatdate(filters.year_end_date)
+			)
+		)
 		filters.to_date = filters.year_end_date
 
+
 def get_data(filters):
 
-	accounts = frappe.db.sql("""select name, account_number, parent_account, account_name, root_type, report_type, lft, rgt
+	accounts = frappe.db.sql(
+		"""select name, account_number, parent_account, account_name, root_type, report_type, lft, rgt
 
-		from `tabAccount` where company=%s order by lft""", filters.company, as_dict=True)
+		from `tabAccount` where company=%s order by lft""",
+		filters.company,
+		as_dict=True,
+	)
 	company_currency = filters.presentation_currency or erpnext.get_company_currency(filters.company)
 
 	if not accounts:
@@ -71,28 +93,44 @@
 
 	accounts, accounts_by_name, parent_children_map = filter_accounts(accounts)
 
-	min_lft, max_rgt = frappe.db.sql("""select min(lft), max(rgt) from `tabAccount`
-		where company=%s""", (filters.company,))[0]
+	min_lft, max_rgt = frappe.db.sql(
+		"""select min(lft), max(rgt) from `tabAccount`
+		where company=%s""",
+		(filters.company,),
+	)[0]
 
 	gl_entries_by_account = {}
 
 	opening_balances = get_opening_balances(filters)
 
-	#add filter inside list so that the query in financial_statements.py doesn't break
+	# add filter inside list so that the query in financial_statements.py doesn't break
 	if filters.project:
 		filters.project = [filters.project]
 
-	set_gl_entries_by_account(filters.company, filters.from_date,
-		filters.to_date, min_lft, max_rgt, filters, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry))
+	set_gl_entries_by_account(
+		filters.company,
+		filters.from_date,
+		filters.to_date,
+		min_lft,
+		max_rgt,
+		filters,
+		gl_entries_by_account,
+		ignore_closing_entries=not flt(filters.with_period_closing_entry),
+	)
 
-	total_row = calculate_values(accounts, gl_entries_by_account, opening_balances, filters, company_currency)
+	total_row = calculate_values(
+		accounts, gl_entries_by_account, opening_balances, filters, company_currency
+	)
 	accumulate_values_into_parents(accounts, accounts_by_name)
 
 	data = prepare_data(accounts, filters, total_row, parent_children_map, company_currency)
-	data = filter_out_zero_value_rows(data, parent_children_map, show_zero_values=filters.get("show_zero_values"))
+	data = filter_out_zero_value_rows(
+		data, parent_children_map, show_zero_values=filters.get("show_zero_values")
+	)
 
 	return data
 
+
 def get_opening_balances(filters):
 	balance_sheet_opening = get_rootwise_opening_balances(filters, "Balance Sheet")
 	pl_opening = get_rootwise_opening_balances(filters, "Profit and Loss")
@@ -104,26 +142,30 @@
 def get_rootwise_opening_balances(filters, report_type):
 	additional_conditions = ""
 	if not filters.show_unclosed_fy_pl_balances:
-		additional_conditions = " and posting_date >= %(year_start_date)s" \
-			if report_type == "Profit and Loss" else ""
+		additional_conditions = (
+			" and posting_date >= %(year_start_date)s" if report_type == "Profit and Loss" else ""
+		)
 
 	if not flt(filters.with_period_closing_entry):
 		additional_conditions += " and ifnull(voucher_type, '')!='Period Closing Voucher'"
 
 	if filters.cost_center:
-		lft, rgt = frappe.db.get_value('Cost Center', filters.cost_center, ['lft', 'rgt'])
+		lft, rgt = frappe.db.get_value("Cost Center", filters.cost_center, ["lft", "rgt"])
 		additional_conditions += """ and cost_center in (select name from `tabCost Center`
-			where lft >= %s and rgt <= %s)""" % (lft, rgt)
+			where lft >= %s and rgt <= %s)""" % (
+			lft,
+			rgt,
+		)
 
 	if filters.project:
 		additional_conditions += " and project = %(project)s"
 
-	if filters.finance_book:
-		fb_conditions = " AND finance_book = %(finance_book)s"
-		if filters.include_default_book_entries:
-			fb_conditions = " AND (finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)"
-
-		additional_conditions += fb_conditions
+	if filters.get("include_default_book_entries"):
+		additional_conditions += (
+			" AND (finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)"
+		)
+	else:
+		additional_conditions += " AND (finance_book in (%(finance_book)s, '') OR finance_book IS NULL)"
 
 	accounting_dimensions = get_accounting_dimensions(as_list=False)
 
@@ -134,24 +176,24 @@
 		"year_start_date": filters.year_start_date,
 		"project": filters.project,
 		"finance_book": filters.finance_book,
-		"company_fb": frappe.db.get_value("Company", filters.company, 'default_finance_book')
+		"company_fb": frappe.db.get_value("Company", filters.company, "default_finance_book"),
 	}
 
 	if accounting_dimensions:
 		for dimension in accounting_dimensions:
 			if filters.get(dimension.fieldname):
-				if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
-					filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
-						filters.get(dimension.fieldname))
-					additional_conditions += "and {0} in %({0})s".format(dimension.fieldname)
+				if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"):
+					filters[dimension.fieldname] = get_dimension_with_children(
+						dimension.document_type, filters.get(dimension.fieldname)
+					)
+					additional_conditions += " and {0} in %({0})s".format(dimension.fieldname)
 				else:
-					additional_conditions += "and {0} in (%({0})s)".format(dimension.fieldname)
+					additional_conditions += " and {0} in %({0})s".format(dimension.fieldname)
 
-				query_filters.update({
-					dimension.fieldname: filters.get(dimension.fieldname)
-				})
+				query_filters.update({dimension.fieldname: filters.get(dimension.fieldname)})
 
-	gle = frappe.db.sql("""
+	gle = frappe.db.sql(
+		"""
 		select
 			account, sum(debit) as opening_debit, sum(credit) as opening_credit
 		from `tabGL Entry`
@@ -161,7 +203,12 @@
 			and (posting_date < %(from_date)s or ifnull(is_opening, 'No') = 'Yes')
 			and account in (select name from `tabAccount` where report_type=%(report_type)s)
 			and is_cancelled = 0
-		group by account""".format(additional_conditions=additional_conditions), query_filters , as_dict=True)
+		group by account""".format(
+			additional_conditions=additional_conditions
+		),
+		query_filters,
+		as_dict=True,
+	)
 
 	opening = frappe._dict()
 	for d in gle:
@@ -169,6 +216,7 @@
 
 	return opening
 
+
 def calculate_values(accounts, gl_entries_by_account, opening_balances, filters, company_currency):
 	init = {
 		"opening_debit": 0.0,
@@ -176,7 +224,7 @@
 		"debit": 0.0,
 		"credit": 0.0,
 		"closing_debit": 0.0,
-		"closing_credit": 0.0
+		"closing_credit": 0.0,
 	}
 
 	total_row = {
@@ -192,7 +240,7 @@
 		"parent_account": None,
 		"indent": 0,
 		"has_value": True,
-		"currency": company_currency
+		"currency": company_currency,
 	}
 
 	for d in accounts:
@@ -217,12 +265,14 @@
 
 	return total_row
 
+
 def accumulate_values_into_parents(accounts, accounts_by_name):
 	for d in reversed(accounts):
 		if d.parent_account:
 			for key in value_fields:
 				accounts_by_name[d.parent_account][key] += d[key]
 
+
 def prepare_data(accounts, filters, total_row, parent_children_map, company_currency):
 	data = []
 
@@ -239,8 +289,9 @@
 			"from_date": filters.from_date,
 			"to_date": filters.to_date,
 			"currency": company_currency,
-			"account_name": ('{} - {}'.format(d.account_number, d.account_name)
-				if d.account_number else d.account_name)
+			"account_name": (
+				"{} - {}".format(d.account_number, d.account_name) if d.account_number else d.account_name
+			),
 		}
 
 		for key in value_fields:
@@ -253,10 +304,11 @@
 		row["has_value"] = has_value
 		data.append(row)
 
-	data.extend([{},total_row])
+	data.extend([{}, total_row])
 
 	return data
 
+
 def get_columns():
 	return [
 		{
@@ -264,59 +316,60 @@
 			"label": _("Account"),
 			"fieldtype": "Link",
 			"options": "Account",
-			"width": 300
+			"width": 300,
 		},
 		{
 			"fieldname": "currency",
 			"label": _("Currency"),
 			"fieldtype": "Link",
 			"options": "Currency",
-			"hidden": 1
+			"hidden": 1,
 		},
 		{
 			"fieldname": "opening_debit",
 			"label": _("Opening (Dr)"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"fieldname": "opening_credit",
 			"label": _("Opening (Cr)"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"fieldname": "debit",
 			"label": _("Debit"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"fieldname": "credit",
 			"label": _("Credit"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"fieldname": "closing_debit",
 			"label": _("Closing (Dr)"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"fieldname": "closing_credit",
 			"label": _("Closing (Cr)"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 120
-		}
+			"width": 120,
+		},
 	]
 
+
 def prepare_opening_closing(row):
 	dr_or_cr = "debit" if row["root_type"] in ["Asset", "Equity", "Expense"] else "credit"
 	reverse_dr_or_cr = "credit" if dr_or_cr == "debit" else "debit"
diff --git a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py
index d843dfd..5fcfdff 100644
--- a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py
+++ b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py
@@ -19,102 +19,99 @@
 
 	return columns, data
 
+
 def get_data(filters, show_party_name):
-	if filters.get('party_type') in ('Customer', 'Supplier', 'Employee', 'Member'):
-		party_name_field = "{0}_name".format(frappe.scrub(filters.get('party_type')))
-	elif filters.get('party_type') == 'Student':
-		party_name_field = 'first_name'
-	elif filters.get('party_type') == 'Shareholder':
-		party_name_field = 'title'
+	if filters.get("party_type") in ("Customer", "Supplier", "Employee", "Member"):
+		party_name_field = "{0}_name".format(frappe.scrub(filters.get("party_type")))
+	elif filters.get("party_type") == "Shareholder":
+		party_name_field = "title"
 	else:
-		party_name_field = 'name'
+		party_name_field = "name"
 
 	party_filters = {"name": filters.get("party")} if filters.get("party") else {}
-	parties = frappe.get_all(filters.get("party_type"), fields = ["name", party_name_field],
-		filters = party_filters, order_by="name")
-	company_currency = frappe.get_cached_value('Company',  filters.company,  "default_currency")
+	parties = frappe.get_all(
+		filters.get("party_type"),
+		fields=["name", party_name_field],
+		filters=party_filters,
+		order_by="name",
+	)
+	company_currency = frappe.get_cached_value("Company", filters.company, "default_currency")
 	opening_balances = get_opening_balances(filters)
 	balances_within_period = get_balances_within_period(filters)
 
 	data = []
 	# total_debit, total_credit = 0, 0
-	total_row = frappe._dict({
-		"opening_debit": 0,
-		"opening_credit": 0,
-		"debit": 0,
-		"credit": 0,
-		"closing_debit": 0,
-		"closing_credit": 0
-	})
+	total_row = frappe._dict(
+		{
+			"opening_debit": 0,
+			"opening_credit": 0,
+			"debit": 0,
+			"credit": 0,
+			"closing_debit": 0,
+			"closing_credit": 0,
+		}
+	)
 	for party in parties:
-		row = { "party": party.name }
+		row = {"party": party.name}
 		if show_party_name:
 			row["party_name"] = party.get(party_name_field)
 
 		# opening
 		opening_debit, opening_credit = opening_balances.get(party.name, [0, 0])
-		row.update({
-			"opening_debit": opening_debit,
-			"opening_credit": opening_credit
-		})
+		row.update({"opening_debit": opening_debit, "opening_credit": opening_credit})
 
 		# within period
 		debit, credit = balances_within_period.get(party.name, [0, 0])
-		row.update({
-			"debit": debit,
-			"credit": credit
-		})
+		row.update({"debit": debit, "credit": credit})
 
 		# closing
-		closing_debit, closing_credit = toggle_debit_credit(opening_debit + debit, opening_credit + credit)
-		row.update({
-			"closing_debit": closing_debit,
-			"closing_credit": closing_credit
-		})
+		closing_debit, closing_credit = toggle_debit_credit(
+			opening_debit + debit, opening_credit + credit
+		)
+		row.update({"closing_debit": closing_debit, "closing_credit": closing_credit})
 
 		# totals
 		for col in total_row:
 			total_row[col] += row.get(col)
 
-		row.update({
-			"currency": company_currency
-		})
+		row.update({"currency": company_currency})
 
 		has_value = False
-		if (opening_debit or opening_credit or debit or credit or closing_debit or closing_credit):
-			has_value  =True
+		if opening_debit or opening_credit or debit or credit or closing_debit or closing_credit:
+			has_value = True
 
 		if cint(filters.show_zero_values) or has_value:
 			data.append(row)
 
 	# Add total row
 
-	total_row.update({
-		"party": "'" + _("Totals") + "'",
-		"currency": company_currency
-	})
+	total_row.update({"party": "'" + _("Totals") + "'", "currency": company_currency})
 	data.append(total_row)
 
 	return data
 
+
 def get_opening_balances(filters):
 
-	account_filter = ''
-	if filters.get('account'):
-		account_filter = "and account = %s" % (frappe.db.escape(filters.get('account')))
+	account_filter = ""
+	if filters.get("account"):
+		account_filter = "and account = %s" % (frappe.db.escape(filters.get("account")))
 
-	gle = frappe.db.sql("""
+	gle = frappe.db.sql(
+		"""
 		select party, sum(debit) as opening_debit, sum(credit) as opening_credit
 		from `tabGL Entry`
 		where company=%(company)s
+			and is_cancelled=0
 			and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
 			and (posting_date < %(from_date)s or ifnull(is_opening, 'No') = 'Yes')
 			{account_filter}
-		group by party""".format(account_filter=account_filter), {
-			"company": filters.company,
-			"from_date": filters.from_date,
-			"party_type": filters.party_type
-		}, as_dict=True)
+		group by party""".format(
+			account_filter=account_filter
+		),
+		{"company": filters.company, "from_date": filters.from_date, "party_type": filters.party_type},
+		as_dict=True,
+	)
 
 	opening = frappe._dict()
 	for d in gle:
@@ -123,26 +120,34 @@
 
 	return opening
 
+
 def get_balances_within_period(filters):
 
-	account_filter = ''
-	if filters.get('account'):
-		account_filter = "and account = %s" % (frappe.db.escape(filters.get('account')))
+	account_filter = ""
+	if filters.get("account"):
+		account_filter = "and account = %s" % (frappe.db.escape(filters.get("account")))
 
-	gle = frappe.db.sql("""
+	gle = frappe.db.sql(
+		"""
 		select party, sum(debit) as debit, sum(credit) as credit
 		from `tabGL Entry`
 		where company=%(company)s
+			and is_cancelled = 0
 			and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
 			and posting_date >= %(from_date)s and posting_date <= %(to_date)s
 			and ifnull(is_opening, 'No') = 'No'
 			{account_filter}
-		group by party""".format(account_filter=account_filter), {
+		group by party""".format(
+			account_filter=account_filter
+		),
+		{
 			"company": filters.company,
 			"from_date": filters.from_date,
 			"to_date": filters.to_date,
-			"party_type": filters.party_type
-		}, as_dict=True)
+			"party_type": filters.party_type,
+		},
+		as_dict=True,
+	)
 
 	balances_within_period = frappe._dict()
 	for d in gle:
@@ -150,6 +155,7 @@
 
 	return balances_within_period
 
+
 def toggle_debit_credit(debit, credit):
 	if flt(debit) > flt(credit):
 		debit = flt(debit) - flt(credit)
@@ -160,6 +166,7 @@
 
 	return debit, credit
 
+
 def get_columns(filters, show_party_name):
 	columns = [
 		{
@@ -167,73 +174,77 @@
 			"label": _(filters.party_type),
 			"fieldtype": "Link",
 			"options": filters.party_type,
-			"width": 200
+			"width": 200,
 		},
 		{
 			"fieldname": "opening_debit",
 			"label": _("Opening (Dr)"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"fieldname": "opening_credit",
 			"label": _("Opening (Cr)"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"fieldname": "debit",
 			"label": _("Debit"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"fieldname": "credit",
 			"label": _("Credit"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"fieldname": "closing_debit",
 			"label": _("Closing (Dr)"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"fieldname": "closing_credit",
 			"label": _("Closing (Cr)"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"fieldname": "currency",
 			"label": _("Currency"),
 			"fieldtype": "Link",
 			"options": "Currency",
-			"hidden": 1
-		}
+			"hidden": 1,
+		},
 	]
 
 	if show_party_name:
-		columns.insert(1, {
-			"fieldname": "party_name",
-			"label": _(filters.party_type) + " Name",
-			"fieldtype": "Data",
-			"width": 200
-		})
+		columns.insert(
+			1,
+			{
+				"fieldname": "party_name",
+				"label": _(filters.party_type) + " Name",
+				"fieldtype": "Data",
+				"width": 200,
+			},
+		)
 
 	return columns
 
+
 def is_party_name_visible(filters):
 	show_party_name = False
 
-	if filters.get('party_type') in ['Customer', 'Supplier']:
+	if filters.get("party_type") in ["Customer", "Supplier"]:
 		if filters.get("party_type") == "Customer":
 			party_naming_by = frappe.db.get_single_value("Selling Settings", "cust_master_name")
 		else:
diff --git a/erpnext/accounts/report/unpaid_expense_claim/unpaid_expense_claim.py b/erpnext/accounts/report/unpaid_expense_claim/unpaid_expense_claim.py
index 26b9389..62b4f63 100644
--- a/erpnext/accounts/report/unpaid_expense_claim/unpaid_expense_claim.py
+++ b/erpnext/accounts/report/unpaid_expense_claim/unpaid_expense_claim.py
@@ -12,16 +12,25 @@
 	data = get_unclaimed_expese_claims(filters)
 	return columns, data
 
+
 def get_columns():
-	return [_("Employee") + ":Link/Employee:120", _("Employee Name") + "::120",_("Expense Claim") + ":Link/Expense Claim:120",
-		_("Sanctioned Amount") + ":Currency:120", _("Paid Amount") + ":Currency:120", _("Outstanding Amount") + ":Currency:150"]
+	return [
+		_("Employee") + ":Link/Employee:120",
+		_("Employee Name") + "::120",
+		_("Expense Claim") + ":Link/Expense Claim:120",
+		_("Sanctioned Amount") + ":Currency:120",
+		_("Paid Amount") + ":Currency:120",
+		_("Outstanding Amount") + ":Currency:150",
+	]
+
 
 def get_unclaimed_expese_claims(filters):
 	cond = "1=1"
 	if filters.get("employee"):
 		cond = "ec.employee = %(employee)s"
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select
 			ec.employee, ec.employee_name, ec.name, ec.total_sanctioned_amount, ec.total_amount_reimbursed,
 			sum(gle.credit_in_account_currency - gle.debit_in_account_currency) as outstanding_amt
@@ -32,4 +41,9 @@
 			and gle.party is not null and ec.docstatus = 1 and ec.is_paid = 0 and {cond} group by ec.name
 		having
 			outstanding_amt > 0
-	""".format(cond=cond), filters, as_list=1)
+	""".format(
+			cond=cond
+		),
+		filters,
+		as_list=1,
+	)
diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py
index c38e4b8..eed5836 100644
--- a/erpnext/accounts/report/utils.py
+++ b/erpnext/accounts/report/utils.py
@@ -7,6 +7,7 @@
 
 __exchange_rates = {}
 
+
 def get_currency(filters):
 	"""
 	Returns a dictionary containing currency information. The keys of the dict are
@@ -23,15 +24,22 @@
 	"""
 	company = get_appropriate_company(filters)
 	company_currency = get_company_currency(company)
-	presentation_currency = filters['presentation_currency'] if filters.get('presentation_currency') else company_currency
+	presentation_currency = (
+		filters["presentation_currency"] if filters.get("presentation_currency") else company_currency
+	)
 
-	report_date = filters.get('to_date')
+	report_date = filters.get("to_date")
 
 	if not report_date:
-		fiscal_year_to_date = get_from_and_to_date(filters.get('to_fiscal_year'))["to_date"]
+		fiscal_year_to_date = get_from_and_to_date(filters.get("to_fiscal_year"))["to_date"]
 		report_date = formatdate(get_datetime_str(fiscal_year_to_date), "dd-MM-yyyy")
 
-	currency_map = dict(company=company, company_currency=company_currency, presentation_currency=presentation_currency, report_date=report_date)
+	currency_map = dict(
+		company=company,
+		company_currency=company_currency,
+		presentation_currency=presentation_currency,
+		report_date=report_date,
+	)
 
 	return currency_map
 
@@ -62,13 +70,14 @@
 	:return: Retrieved exchange rate
 	"""
 
-	rate = __exchange_rates.get('{0}-{1}@{2}'.format(from_currency, to_currency, date))
+	rate = __exchange_rates.get("{0}-{1}@{2}".format(from_currency, to_currency, date))
 	if not rate:
 		rate = get_exchange_rate(from_currency, to_currency, date) or 1
-		__exchange_rates['{0}-{1}@{2}'.format(from_currency, to_currency, date)] = rate
+		__exchange_rates["{0}-{1}@{2}".format(from_currency, to_currency, date)] = rate
 
 	return rate
 
+
 def convert_to_presentation_currency(gl_entries, currency_info, company):
 	"""
 	Take a list of GL Entries and change the 'debit' and 'credit' values to currencies
@@ -78,35 +87,35 @@
 	:return:
 	"""
 	converted_gl_list = []
-	presentation_currency = currency_info['presentation_currency']
-	company_currency = currency_info['company_currency']
+	presentation_currency = currency_info["presentation_currency"]
+	company_currency = currency_info["company_currency"]
 
-	account_currencies = list(set(entry['account_currency'] for entry in gl_entries))
+	account_currencies = list(set(entry["account_currency"] for entry in gl_entries))
 
 	for entry in gl_entries:
-		account = entry['account']
-		debit = flt(entry['debit'])
-		credit = flt(entry['credit'])
-		debit_in_account_currency = flt(entry['debit_in_account_currency'])
-		credit_in_account_currency = flt(entry['credit_in_account_currency'])
-		account_currency = entry['account_currency']
+		account = entry["account"]
+		debit = flt(entry["debit"])
+		credit = flt(entry["credit"])
+		debit_in_account_currency = flt(entry["debit_in_account_currency"])
+		credit_in_account_currency = flt(entry["credit_in_account_currency"])
+		account_currency = entry["account_currency"]
 
 		if len(account_currencies) == 1 and account_currency == presentation_currency:
-			if entry.get('debit'):
-				entry['debit'] = debit_in_account_currency
+			if debit_in_account_currency:
+				entry["debit"] = debit_in_account_currency
 
-			if entry.get('credit'):
-				entry['credit'] = credit_in_account_currency
+			if credit_in_account_currency:
+				entry["credit"] = credit_in_account_currency
 		else:
-			date = currency_info['report_date']
+			date = currency_info["report_date"]
 			converted_debit_value = convert(debit, presentation_currency, company_currency, date)
 			converted_credit_value = convert(credit, presentation_currency, company_currency, date)
 
-			if entry.get('debit'):
-				entry['debit'] = converted_debit_value
+			if entry.get("debit"):
+				entry["debit"] = converted_debit_value
 
-			if entry.get('credit'):
-				entry['credit'] = converted_credit_value
+			if entry.get("credit"):
+				entry["credit"] = converted_credit_value
 
 		converted_gl_list.append(entry)
 
@@ -114,26 +123,29 @@
 
 
 def get_appropriate_company(filters):
-	if filters.get('company'):
-		company = filters['company']
+	if filters.get("company"):
+		company = filters["company"]
 	else:
 		company = get_default_company()
 
 	return company
 
+
 @frappe.whitelist()
-def get_invoiced_item_gross_margin(sales_invoice=None, item_code=None, company=None, with_item_data=False):
+def get_invoiced_item_gross_margin(
+	sales_invoice=None, item_code=None, company=None, with_item_data=False
+):
 	from erpnext.accounts.report.gross_profit.gross_profit import GrossProfitGenerator
 
-	sales_invoice = sales_invoice or frappe.form_dict.get('sales_invoice')
-	item_code = item_code or frappe.form_dict.get('item_code')
-	company = company or frappe.get_cached_value("Sales Invoice", sales_invoice, 'company')
+	sales_invoice = sales_invoice or frappe.form_dict.get("sales_invoice")
+	item_code = item_code or frappe.form_dict.get("item_code")
+	company = company or frappe.get_cached_value("Sales Invoice", sales_invoice, "company")
 
 	filters = {
-		'sales_invoice': sales_invoice,
-		'item_code': item_code,
-		'company': company,
-		'group_by': 'Invoice'
+		"sales_invoice": sales_invoice,
+		"item_code": item_code,
+		"company": company,
+		"group_by": "Invoice",
 	}
 
 	gross_profit_data = GrossProfitGenerator(filters)
diff --git a/erpnext/accounts/test/test_reports.py b/erpnext/accounts/test/test_reports.py
index 4ed966d..3f06c30 100644
--- a/erpnext/accounts/test/test_reports.py
+++ b/erpnext/accounts/test/test_reports.py
@@ -8,18 +8,18 @@
 	"from_date": "2010-01-01",
 	"to_date": "2030-01-01",
 	"period_start_date": "2010-01-01",
-	"period_end_date": "2030-01-01"
+	"period_end_date": "2030-01-01",
 }
 
 
 REPORT_FILTER_TEST_CASES: List[Tuple[ReportName, ReportFilters]] = [
-	("General Ledger", {"group_by": "Group by Voucher (Consolidated)"} ),
-	("General Ledger", {"group_by": "Group by Voucher (Consolidated)", "include_dimensions": 1} ),
+	("General Ledger", {"group_by": "Group by Voucher (Consolidated)"}),
+	("General Ledger", {"group_by": "Group by Voucher (Consolidated)", "include_dimensions": 1}),
 	("Accounts Payable", {"range1": 30, "range2": 60, "range3": 90, "range4": 120}),
 	("Accounts Receivable", {"range1": 30, "range2": 60, "range3": 90, "range4": 120}),
-	("Consolidated Financial Statement", {"report": "Balance Sheet"} ),
-	("Consolidated Financial Statement", {"report": "Profit and Loss Statement"} ),
-	("Consolidated Financial Statement", {"report": "Cash Flow"} ),
+	("Consolidated Financial Statement", {"report": "Balance Sheet"}),
+	("Consolidated Financial Statement", {"report": "Profit and Loss Statement"}),
+	("Consolidated Financial Statement", {"report": "Cash Flow"}),
 	("Gross Profit", {"group_by": "Invoice"}),
 	("Gross Profit", {"group_by": "Item Code"}),
 	("Gross Profit", {"group_by": "Item Group"}),
@@ -28,8 +28,12 @@
 	("Item-wise Sales Register", {}),
 	("Item-wise Purchase Register", {}),
 	("Sales Register", {}),
+	("Sales Register", {"item_group": "All Item Groups"}),
 	("Purchase Register", {}),
-	("Tax Detail", {"mode": "run", "report_name": "Tax Detail"},),
+	(
+		"Tax Detail",
+		{"mode": "run", "report_name": "Tax Detail"},
+	),
 ]
 
 OPTIONAL_FILTERS = {}
diff --git a/erpnext/accounts/test/test_utils.py b/erpnext/accounts/test/test_utils.py
index effc913..882cd69 100644
--- a/erpnext/accounts/test/test_utils.py
+++ b/erpnext/accounts/test/test_utils.py
@@ -1,10 +1,17 @@
 import unittest
 
+import frappe
 from frappe.test_runner import make_test_objects
 
 from erpnext.accounts.party import get_party_shipping_address
-from erpnext.accounts.utils import get_future_stock_vouchers, get_voucherwise_gl_entries
+from erpnext.accounts.utils import (
+	get_future_stock_vouchers,
+	get_voucherwise_gl_entries,
+	sort_stock_vouchers_by_posting_date,
+)
+from erpnext.stock.doctype.item.test_item import make_item
 from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 
 
 class TestUtils(unittest.TestCase):
@@ -43,9 +50,29 @@
 		posting_date = "2021-01-01"
 		gl_entries = get_voucherwise_gl_entries(future_vouchers, posting_date)
 		self.assertTrue(
-			voucher_type_and_no in gl_entries, msg="get_voucherwise_gl_entries not returning expected GLes",
+			voucher_type_and_no in gl_entries,
+			msg="get_voucherwise_gl_entries not returning expected GLes",
 		)
 
+	def test_stock_voucher_sorting(self):
+		vouchers = []
+
+		item = make_item().name
+
+		stock_entry = {"item": item, "to_warehouse": "_Test Warehouse - _TC", "qty": 1, "rate": 10}
+
+		se1 = make_stock_entry(posting_date="2022-01-01", **stock_entry)
+		se3 = make_stock_entry(posting_date="2022-03-01", **stock_entry)
+		se2 = make_stock_entry(posting_date="2022-02-01", **stock_entry)
+
+		for doc in (se1, se2, se3):
+			vouchers.append((doc.doctype, doc.name))
+
+		vouchers.append(("Stock Entry", "Wat"))
+
+		sorted_vouchers = sort_stock_vouchers_by_posting_date(list(reversed(vouchers)))
+		self.assertEqual(sorted_vouchers, vouchers)
+
 
 ADDRESS_RECORDS = [
 	{
diff --git a/erpnext/accounts/test_party.py b/erpnext/accounts/test_party.py
new file mode 100644
index 0000000..9d3de5e
--- /dev/null
+++ b/erpnext/accounts/test_party.py
@@ -0,0 +1,18 @@
+import frappe
+from frappe.tests.utils import FrappeTestCase
+
+from erpnext.accounts.party import get_default_price_list
+
+
+class PartyTestCase(FrappeTestCase):
+	def test_get_default_price_list_should_return_none_for_invalid_group(self):
+		customer = frappe.get_doc(
+			{
+				"doctype": "Customer",
+				"customer_name": "test customer",
+			}
+		).insert(ignore_permissions=True, ignore_mandatory=True)
+		customer.customer_group = None
+		customer.save()
+		price_list = get_default_price_list(customer)
+		assert price_list is None
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index b17b90b..8daff9d 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -3,60 +3,91 @@
 
 
 from json import loads
+from typing import TYPE_CHECKING, List, Optional, Tuple
 
 import frappe
 import frappe.defaults
-from frappe import _, throw
+from frappe import _, qb, throw
 from frappe.model.meta import get_field_precision
-from frappe.utils import cint, cstr, flt, formatdate, get_number_format_info, getdate, now, nowdate
+from frappe.query_builder import AliasedQuery, Criterion, Table
+from frappe.query_builder.functions import Sum
+from frappe.query_builder.utils import DocType
+from frappe.utils import (
+	cint,
+	create_batch,
+	cstr,
+	flt,
+	formatdate,
+	get_number_format_info,
+	getdate,
+	now,
+	nowdate,
+)
+from pypika import Order
+from pypika.terms import ExistsCriterion
 
 import erpnext
 
 # imported to enable erpnext.accounts.utils.get_account_currency
 from erpnext.accounts.doctype.account.account import get_account_currency  # noqa
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_dimensions
 from erpnext.stock import get_warehouse_account_map
 from erpnext.stock.utils import get_stock_value_on
 
+if TYPE_CHECKING:
+	from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import RepostItemValuation
 
-class StockValueAndAccountBalanceOutOfSync(frappe.ValidationError): pass
-class FiscalYearError(frappe.ValidationError): pass
-class PaymentEntryUnlinkError(frappe.ValidationError): pass
+
+class FiscalYearError(frappe.ValidationError):
+	pass
+
+
+class PaymentEntryUnlinkError(frappe.ValidationError):
+	pass
+
+
+GL_REPOSTING_CHUNK = 100
+
 
 @frappe.whitelist()
-def get_fiscal_year(date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False):
+def get_fiscal_year(
+	date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False
+):
 	return get_fiscal_years(date, fiscal_year, label, verbose, company, as_dict=as_dict)[0]
 
-def get_fiscal_years(transaction_date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False):
+
+def get_fiscal_years(
+	transaction_date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False
+):
 	fiscal_years = frappe.cache().hget("fiscal_years", company) or []
 
 	if not fiscal_years:
 		# if year start date is 2012-04-01, year end date should be 2013-03-31 (hence subdate)
-		cond = ""
-		if fiscal_year:
-			cond += " and fy.name = {0}".format(frappe.db.escape(fiscal_year))
-		if company:
-			cond += """
-				and (not exists (select name
-					from `tabFiscal Year Company` fyc
-					where fyc.parent = fy.name)
-				or exists(select company
-					from `tabFiscal Year Company` fyc
-					where fyc.parent = fy.name
-					and fyc.company=%(company)s)
-				)
-			"""
+		FY = DocType("Fiscal Year")
 
-		fiscal_years = frappe.db.sql("""
-			select
-				fy.name, fy.year_start_date, fy.year_end_date
-			from
-				`tabFiscal Year` fy
-			where
-				disabled = 0 {0}
-			order by
-				fy.year_start_date desc""".format(cond), {
-				"company": company
-			}, as_dict=True)
+		query = (
+			frappe.qb.from_(FY)
+			.select(FY.name, FY.year_start_date, FY.year_end_date)
+			.where(FY.disabled == 0)
+		)
+
+		if fiscal_year:
+			query = query.where(FY.name == fiscal_year)
+
+		if company:
+			FYC = DocType("Fiscal Year Company")
+			query = query.where(
+				ExistsCriterion(frappe.qb.from_(FYC).select(FYC.name).where(FYC.parent == FY.name)).negate()
+				| ExistsCriterion(
+					frappe.qb.from_(FYC)
+					.select(FYC.company)
+					.where(FYC.parent == FY.name)
+					.where(FYC.company == company)
+				)
+			)
+
+		query = query.orderby(FY.year_start_date, Order.desc)
+		fiscal_years = query.run(as_dict=True)
 
 		frappe.cache().hset("fiscal_years", company, fiscal_years)
 
@@ -71,8 +102,11 @@
 		if fiscal_year and fy.name == fiscal_year:
 			matched = True
 
-		if (transaction_date and getdate(fy.year_start_date) <= transaction_date
-			and getdate(fy.year_end_date) >= transaction_date):
+		if (
+			transaction_date
+			and getdate(fy.year_start_date) <= transaction_date
+			and getdate(fy.year_end_date) >= transaction_date
+		):
 			matched = True
 
 		if matched:
@@ -81,30 +115,35 @@
 			else:
 				return ((fy.name, fy.year_start_date, fy.year_end_date),)
 
-	error_msg = _("""{0} {1} is not in any active Fiscal Year""").format(label, formatdate(transaction_date))
+	error_msg = _("""{0} {1} is not in any active Fiscal Year""").format(
+		label, formatdate(transaction_date)
+	)
 	if company:
 		error_msg = _("""{0} for {1}""").format(error_msg, frappe.bold(company))
 
-	if verbose==1: frappe.msgprint(error_msg)
+	if verbose == 1:
+		frappe.msgprint(error_msg)
 	raise FiscalYearError(error_msg)
 
+
 @frappe.whitelist()
 def get_fiscal_year_filter_field(company=None):
-	field = {
-		"fieldtype": "Select",
-		"options": [],
-		"operator": "Between",
-		"query_value": True
-	}
+	field = {"fieldtype": "Select", "options": [], "operator": "Between", "query_value": True}
 	fiscal_years = get_fiscal_years(company=company)
 	for fiscal_year in fiscal_years:
-		field["options"].append({
-			"label": fiscal_year.name,
-			"value": fiscal_year.name,
-			"query_value": [fiscal_year.year_start_date.strftime("%Y-%m-%d"), fiscal_year.year_end_date.strftime("%Y-%m-%d")]
-		})
+		field["options"].append(
+			{
+				"label": fiscal_year.name,
+				"value": fiscal_year.name,
+				"query_value": [
+					fiscal_year.year_start_date.strftime("%Y-%m-%d"),
+					fiscal_year.year_end_date.strftime("%Y-%m-%d"),
+				],
+			}
+		)
 	return field
 
+
 def validate_fiscal_year(date, fiscal_year, company, label="Date", doc=None):
 	years = [f[0] for f in get_fiscal_years(date, label=_(label), company=company)]
 	if fiscal_year not in years:
@@ -113,9 +152,18 @@
 		else:
 			throw(_("{0} '{1}' not in Fiscal Year {2}").format(label, formatdate(date), fiscal_year))
 
+
 @frappe.whitelist()
-def get_balance_on(account=None, date=None, party_type=None, party=None, company=None,
-	in_account_currency=True, cost_center=None, ignore_account_permission=False):
+def get_balance_on(
+	account=None,
+	date=None,
+	party_type=None,
+	party=None,
+	company=None,
+	in_account_currency=True,
+	cost_center=None,
+	ignore_account_permission=False,
+):
 	if not account and frappe.form_dict.get("account"):
 		account = frappe.form_dict.get("account")
 	if not date and frappe.form_dict.get("date"):
@@ -127,7 +175,6 @@
 	if not cost_center and frappe.form_dict.get("cost_center"):
 		cost_center = frappe.form_dict.get("cost_center")
 
-
 	cond = ["is_cancelled=0"]
 	if date:
 		cond.append("posting_date <= %s" % frappe.db.escape(cstr(date)))
@@ -155,45 +202,52 @@
 	else:
 		report_type = ""
 
-	if cost_center and report_type == 'Profit and Loss':
+	if cost_center and report_type == "Profit and Loss":
 		cc = frappe.get_doc("Cost Center", cost_center)
 		if cc.is_group:
-			cond.append(""" exists (
+			cond.append(
+				""" exists (
 				select 1 from `tabCost Center` cc where cc.name = gle.cost_center
 				and cc.lft >= %s and cc.rgt <= %s
-			)""" % (cc.lft, cc.rgt))
+			)"""
+				% (cc.lft, cc.rgt)
+			)
 
 		else:
-			cond.append("""gle.cost_center = %s """ % (frappe.db.escape(cost_center, percent=False), ))
-
+			cond.append("""gle.cost_center = %s """ % (frappe.db.escape(cost_center, percent=False),))
 
 	if account:
 
-		if not (frappe.flags.ignore_account_permission
-			or ignore_account_permission):
+		if not (frappe.flags.ignore_account_permission or ignore_account_permission):
 			acc.check_permission("read")
 
-		if report_type == 'Profit and Loss':
+		if report_type == "Profit and Loss":
 			# for pl accounts, get balance within a fiscal year
-			cond.append("posting_date >= '%s' and voucher_type != 'Period Closing Voucher'" \
-				% year_start_date)
+			cond.append(
+				"posting_date >= '%s' and voucher_type != 'Period Closing Voucher'" % year_start_date
+			)
 		# different filter for group and ledger - improved performance
 		if acc.is_group:
-			cond.append("""exists (
+			cond.append(
+				"""exists (
 				select name from `tabAccount` ac where ac.name = gle.account
 				and ac.lft >= %s and ac.rgt <= %s
-			)""" % (acc.lft, acc.rgt))
+			)"""
+				% (acc.lft, acc.rgt)
+			)
 
 			# If group and currency same as company,
 			# always return balance based on debit and credit in company currency
-			if acc.account_currency == frappe.get_cached_value('Company',  acc.company,  "default_currency"):
+			if acc.account_currency == frappe.get_cached_value("Company", acc.company, "default_currency"):
 				in_account_currency = False
 		else:
-			cond.append("""gle.account = %s """ % (frappe.db.escape(account, percent=False), ))
+			cond.append("""gle.account = %s """ % (frappe.db.escape(account, percent=False),))
 
 	if party_type and party:
-		cond.append("""gle.party_type = %s and gle.party = %s """ %
-			(frappe.db.escape(party_type), frappe.db.escape(party, percent=False)))
+		cond.append(
+			"""gle.party_type = %s and gle.party = %s """
+			% (frappe.db.escape(party_type), frappe.db.escape(party, percent=False))
+		)
 
 	if company:
 		cond.append("""gle.company = %s """ % (frappe.db.escape(company, percent=False)))
@@ -203,14 +257,19 @@
 			select_field = "sum(debit_in_account_currency) - sum(credit_in_account_currency)"
 		else:
 			select_field = "sum(debit) - sum(credit)"
-		bal = frappe.db.sql("""
+		bal = frappe.db.sql(
+			"""
 			SELECT {0}
 			FROM `tabGL Entry` gle
-			WHERE {1}""".format(select_field, " and ".join(cond)))[0][0]
+			WHERE {1}""".format(
+				select_field, " and ".join(cond)
+			)
+		)[0][0]
 
 		# if bal is None, return 0
 		return flt(bal)
 
+
 def get_count_on(account, fieldname, date):
 	cond = ["is_cancelled=0"]
 	if date:
@@ -238,53 +297,71 @@
 			acc.check_permission("read")
 
 		# for pl accounts, get balance within a fiscal year
-		if acc.report_type == 'Profit and Loss':
-			cond.append("posting_date >= '%s' and voucher_type != 'Period Closing Voucher'" \
-				% year_start_date)
+		if acc.report_type == "Profit and Loss":
+			cond.append(
+				"posting_date >= '%s' and voucher_type != 'Period Closing Voucher'" % year_start_date
+			)
 
 		# different filter for group and ledger - improved performance
 		if acc.is_group:
-			cond.append("""exists (
+			cond.append(
+				"""exists (
 				select name from `tabAccount` ac where ac.name = gle.account
 				and ac.lft >= %s and ac.rgt <= %s
-			)""" % (acc.lft, acc.rgt))
+			)"""
+				% (acc.lft, acc.rgt)
+			)
 		else:
-			cond.append("""gle.account = %s """ % (frappe.db.escape(account, percent=False), ))
+			cond.append("""gle.account = %s """ % (frappe.db.escape(account, percent=False),))
 
-		entries = frappe.db.sql("""
+		entries = frappe.db.sql(
+			"""
 			SELECT name, posting_date, account, party_type, party,debit,credit,
 				voucher_type, voucher_no, against_voucher_type, against_voucher
 			FROM `tabGL Entry` gle
-			WHERE {0}""".format(" and ".join(cond)), as_dict=True)
+			WHERE {0}""".format(
+				" and ".join(cond)
+			),
+			as_dict=True,
+		)
 
 		count = 0
 		for gle in entries:
-			if fieldname not in ('invoiced_amount','payables'):
+			if fieldname not in ("invoiced_amount", "payables"):
 				count += 1
 			else:
 				dr_or_cr = "debit" if fieldname == "invoiced_amount" else "credit"
 				cr_or_dr = "credit" if fieldname == "invoiced_amount" else "debit"
-				select_fields = "ifnull(sum(credit-debit),0)" \
-					if fieldname == "invoiced_amount" else "ifnull(sum(debit-credit),0)"
+				select_fields = (
+					"ifnull(sum(credit-debit),0)"
+					if fieldname == "invoiced_amount"
+					else "ifnull(sum(debit-credit),0)"
+				)
 
-				if ((not gle.against_voucher) or (gle.against_voucher_type in ["Sales Order", "Purchase Order"]) or
-				(gle.against_voucher==gle.voucher_no and gle.get(dr_or_cr) > 0)):
-					payment_amount = frappe.db.sql("""
+				if (
+					(not gle.against_voucher)
+					or (gle.against_voucher_type in ["Sales Order", "Purchase Order"])
+					or (gle.against_voucher == gle.voucher_no and gle.get(dr_or_cr) > 0)
+				):
+					payment_amount = frappe.db.sql(
+						"""
 						SELECT {0}
 						FROM `tabGL Entry` gle
 						WHERE docstatus < 2 and posting_date <= %(date)s and against_voucher = %(voucher_no)s
-						and party = %(party)s and name != %(name)s"""
-						.format(select_fields),
-						{"date": date, "voucher_no": gle.voucher_no,
-							"party": gle.party, "name": gle.name})[0][0]
+						and party = %(party)s and name != %(name)s""".format(
+							select_fields
+						),
+						{"date": date, "voucher_no": gle.voucher_no, "party": gle.party, "name": gle.name},
+					)[0][0]
 
 					outstanding_amount = flt(gle.get(dr_or_cr)) - flt(gle.get(cr_or_dr)) - payment_amount
 					currency_precision = get_currency_precision() or 2
-					if abs(flt(outstanding_amount)) > 0.1/10**currency_precision:
+					if abs(flt(outstanding_amount)) > 0.1 / 10**currency_precision:
 						count += 1
 
 		return count
 
+
 @frappe.whitelist()
 def add_ac(args=None):
 	from frappe.desk.treeview import make_tree_args
@@ -316,6 +393,7 @@
 
 	return ac.name
 
+
 @frappe.whitelist()
 def add_cc(args=None):
 	from frappe.desk.treeview import make_tree_args
@@ -327,8 +405,9 @@
 	args = make_tree_args(**args)
 
 	if args.parent_cost_center == args.company:
-		args.parent_cost_center = "{0} - {1}".format(args.parent_cost_center,
-			frappe.get_cached_value('Company',  args.company,  'abbr'))
+		args.parent_cost_center = "{0} - {1}".format(
+			args.parent_cost_center, frappe.get_cached_value("Company", args.company, "abbr")
+		)
 
 	cc = frappe.new_doc("Cost Center")
 	cc.update(args)
@@ -340,9 +419,10 @@
 	cc.insert()
 	return cc.name
 
+
 def reconcile_against_document(args):
 	"""
-		Cancel PE or JV, Update against document, split if required and resubmit
+	Cancel PE or JV, Update against document, split if required and resubmit
 	"""
 	# To optimize making GL Entry for PE or JV with multiple references
 	reconciled_entries = {}
@@ -359,7 +439,8 @@
 		# cancel advance entry
 		doc = frappe.get_doc(voucher_type, voucher_no)
 		frappe.flags.ignore_party_validation = True
-		doc.make_gl_entries(cancel=1, adv_adj=1)
+		gl_map = doc.build_gl_map()
+		create_payment_ledger_entry(gl_map, cancel=1, adv_adj=1)
 
 		for entry in entries:
 			check_if_advance_entry_modified(entry)
@@ -374,74 +455,100 @@
 		doc.save(ignore_permissions=True)
 		# re-submit advance entry
 		doc = frappe.get_doc(entry.voucher_type, entry.voucher_no)
-		doc.make_gl_entries(cancel = 0, adv_adj =1)
+		gl_map = doc.build_gl_map()
+		create_payment_ledger_entry(gl_map, cancel=0, adv_adj=1)
+
 		frappe.flags.ignore_party_validation = False
 
-		if entry.voucher_type in ('Payment Entry', 'Journal Entry'):
+		if entry.voucher_type in ("Payment Entry", "Journal Entry"):
 			doc.update_expense_claim()
 
+
 def check_if_advance_entry_modified(args):
 	"""
-		check if there is already a voucher reference
-		check if amount is same
-		check if jv is submitted
+	check if there is already a voucher reference
+	check if amount is same
+	check if jv is submitted
 	"""
-	if not args.get('unreconciled_amount'):
-		args.update({'unreconciled_amount': args.get('unadjusted_amount')})
+	if not args.get("unreconciled_amount"):
+		args.update({"unreconciled_amount": args.get("unadjusted_amount")})
 
 	ret = None
 	if args.voucher_type == "Journal Entry":
-		ret = frappe.db.sql("""
+		ret = frappe.db.sql(
+			"""
 			select t2.{dr_or_cr} from `tabJournal Entry` t1, `tabJournal Entry Account` t2
 			where t1.name = t2.parent and t2.account = %(account)s
 			and t2.party_type = %(party_type)s and t2.party = %(party)s
-			and (t2.reference_type is null or t2.reference_type in ("", "Sales Order", "Purchase Order"))
+			and (t2.reference_type is null or t2.reference_type in ('', 'Sales Order', 'Purchase Order'))
 			and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s
-			and t1.docstatus=1 """.format(dr_or_cr = args.get("dr_or_cr")), args)
+			and t1.docstatus=1 """.format(
+				dr_or_cr=args.get("dr_or_cr")
+			),
+			args,
+		)
 	else:
-		party_account_field = ("paid_from"
-			if erpnext.get_party_account_type(args.party_type) == 'Receivable' else "paid_to")
+		party_account_field = (
+			"paid_from" if erpnext.get_party_account_type(args.party_type) == "Receivable" else "paid_to"
+		)
 
 		if args.voucher_detail_no:
-			ret = frappe.db.sql("""select t1.name
+			ret = frappe.db.sql(
+				"""select t1.name
 				from `tabPayment Entry` t1, `tabPayment Entry Reference` t2
 				where
 					t1.name = t2.parent and t1.docstatus = 1
 					and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s
 					and t1.party_type = %(party_type)s and t1.party = %(party)s and t1.{0} = %(account)s
-					and t2.reference_doctype in ("", "Sales Order", "Purchase Order")
+					and t2.reference_doctype in ('', 'Sales Order', 'Purchase Order')
 					and t2.allocated_amount = %(unreconciled_amount)s
-			""".format(party_account_field), args)
+			""".format(
+					party_account_field
+				),
+				args,
+			)
 		else:
-			ret = frappe.db.sql("""select name from `tabPayment Entry`
+			ret = frappe.db.sql(
+				"""select name from `tabPayment Entry`
 				where
 					name = %(voucher_no)s and docstatus = 1
 					and party_type = %(party_type)s and party = %(party)s and {0} = %(account)s
 					and unallocated_amount = %(unreconciled_amount)s
-			""".format(party_account_field), args)
+			""".format(
+					party_account_field
+				),
+				args,
+			)
 
 	if not ret:
 		throw(_("""Payment Entry has been modified after you pulled it. Please pull it again."""))
 
+
 def validate_allocated_amount(args):
-	precision = args.get('precision') or frappe.db.get_single_value("System Settings", "currency_precision")
+	precision = args.get("precision") or frappe.db.get_single_value(
+		"System Settings", "currency_precision"
+	)
 	if args.get("allocated_amount") < 0:
 		throw(_("Allocated amount cannot be negative"))
 	elif flt(args.get("allocated_amount"), precision) > flt(args.get("unadjusted_amount"), precision):
 		throw(_("Allocated amount cannot be greater than unadjusted amount"))
 
+
 def update_reference_in_journal_entry(d, journal_entry, do_not_save=False):
 	"""
-		Updates against document, if partial amount splits into rows
+	Updates against document, if partial amount splits into rows
 	"""
 	jv_detail = journal_entry.get("accounts", {"name": d["voucher_detail_no"]})[0]
 
-	if flt(d['unadjusted_amount']) - flt(d['allocated_amount']) != 0:
+	if flt(d["unadjusted_amount"]) - flt(d["allocated_amount"]) != 0:
 		# adjust the unreconciled balance
-		amount_in_account_currency = flt(d['unadjusted_amount']) - flt(d['allocated_amount'])
+		amount_in_account_currency = flt(d["unadjusted_amount"]) - flt(d["allocated_amount"])
 		amount_in_company_currency = amount_in_account_currency * flt(jv_detail.exchange_rate)
-		jv_detail.set(d['dr_or_cr'], amount_in_account_currency)
-		jv_detail.set('debit' if d['dr_or_cr'] == 'debit_in_account_currency' else 'credit', amount_in_company_currency)
+		jv_detail.set(d["dr_or_cr"], amount_in_account_currency)
+		jv_detail.set(
+			"debit" if d["dr_or_cr"] == "debit_in_account_currency" else "credit",
+			amount_in_company_currency,
+		)
 	else:
 		journal_entry.remove(jv_detail)
 
@@ -451,12 +558,18 @@
 	new_row.update((frappe.copy_doc(jv_detail)).as_dict())
 
 	new_row.set(d["dr_or_cr"], d["allocated_amount"])
-	new_row.set('debit' if d['dr_or_cr'] == 'debit_in_account_currency' else 'credit',
-		d["allocated_amount"] * flt(jv_detail.exchange_rate))
+	new_row.set(
+		"debit" if d["dr_or_cr"] == "debit_in_account_currency" else "credit",
+		d["allocated_amount"] * flt(jv_detail.exchange_rate),
+	)
 
-	new_row.set('credit_in_account_currency' if d['dr_or_cr'] == 'debit_in_account_currency'
-		else 'debit_in_account_currency', 0)
-	new_row.set('credit' if d['dr_or_cr'] == 'debit_in_account_currency' else 'debit', 0)
+	new_row.set(
+		"credit_in_account_currency"
+		if d["dr_or_cr"] == "debit_in_account_currency"
+		else "debit_in_account_currency",
+		0,
+	)
+	new_row.set("credit" if d["dr_or_cr"] == "debit_in_account_currency" else "debit", 0)
 
 	new_row.set("reference_type", d["against_voucher_type"])
 	new_row.set("reference_name", d["against_voucher"])
@@ -470,6 +583,7 @@
 	if not do_not_save:
 		journal_entry.save(ignore_permissions=True)
 
+
 def update_reference_in_payment_entry(d, payment_entry, do_not_save=False):
 	reference_details = {
 		"reference_doctype": d.against_voucher_type,
@@ -477,8 +591,10 @@
 		"total_amount": d.grand_total,
 		"outstanding_amount": d.outstanding_amount,
 		"allocated_amount": d.allocated_amount,
-		"exchange_rate": d.exchange_rate if not d.exchange_gain_loss else payment_entry.get_exchange_rate(),
-		"exchange_gain_loss": d.exchange_gain_loss # only populated from invoice in case of advance allocation
+		"exchange_rate": d.exchange_rate
+		if not d.exchange_gain_loss
+		else payment_entry.get_exchange_rate(),
+		"exchange_gain_loss": d.exchange_gain_loss,  # only populated from invoice in case of advance allocation
 	}
 
 	if d.voucher_detail_no:
@@ -505,57 +621,75 @@
 
 	if d.difference_amount and d.difference_account:
 		account_details = {
-			'account': d.difference_account,
-			'cost_center': payment_entry.cost_center or frappe.get_cached_value('Company',
-				payment_entry.company, "cost_center")
+			"account": d.difference_account,
+			"cost_center": payment_entry.cost_center
+			or frappe.get_cached_value("Company", payment_entry.company, "cost_center"),
 		}
 		if d.difference_amount:
-			account_details['amount'] = d.difference_amount
+			account_details["amount"] = d.difference_amount
 
 		payment_entry.set_gain_or_loss(account_details=account_details)
 
 	if not do_not_save:
 		payment_entry.save(ignore_permissions=True)
 
+
 def unlink_ref_doc_from_payment_entries(ref_doc):
 	remove_ref_doc_link_from_jv(ref_doc.doctype, ref_doc.name)
 	remove_ref_doc_link_from_pe(ref_doc.doctype, ref_doc.name)
 
-	frappe.db.sql("""update `tabGL Entry`
+	frappe.db.sql(
+		"""update `tabGL Entry`
 		set against_voucher_type=null, against_voucher=null,
 		modified=%s, modified_by=%s
 		where against_voucher_type=%s and against_voucher=%s
 		and voucher_no != ifnull(against_voucher, '')""",
-		(now(), frappe.session.user, ref_doc.doctype, ref_doc.name))
+		(now(), frappe.session.user, ref_doc.doctype, ref_doc.name),
+	)
 
 	if ref_doc.doctype in ("Sales Invoice", "Purchase Invoice"):
 		ref_doc.set("advances", [])
 
-		frappe.db.sql("""delete from `tab{0} Advance` where parent = %s"""
-			.format(ref_doc.doctype), ref_doc.name)
+		frappe.db.sql(
+			"""delete from `tab{0} Advance` where parent = %s""".format(ref_doc.doctype), ref_doc.name
+		)
+
 
 def remove_ref_doc_link_from_jv(ref_type, ref_no):
-	linked_jv = frappe.db.sql_list("""select parent from `tabJournal Entry Account`
-		where reference_type=%s and reference_name=%s and docstatus < 2""", (ref_type, ref_no))
+	linked_jv = frappe.db.sql_list(
+		"""select parent from `tabJournal Entry Account`
+		where reference_type=%s and reference_name=%s and docstatus < 2""",
+		(ref_type, ref_no),
+	)
 
 	if linked_jv:
-		frappe.db.sql("""update `tabJournal Entry Account`
+		frappe.db.sql(
+			"""update `tabJournal Entry Account`
 			set reference_type=null, reference_name = null,
 			modified=%s, modified_by=%s
 			where reference_type=%s and reference_name=%s
-			and docstatus < 2""", (now(), frappe.session.user, ref_type, ref_no))
+			and docstatus < 2""",
+			(now(), frappe.session.user, ref_type, ref_no),
+		)
 
 		frappe.msgprint(_("Journal Entries {0} are un-linked").format("\n".join(linked_jv)))
 
+
 def remove_ref_doc_link_from_pe(ref_type, ref_no):
-	linked_pe = frappe.db.sql_list("""select parent from `tabPayment Entry Reference`
-		where reference_doctype=%s and reference_name=%s and docstatus < 2""", (ref_type, ref_no))
+	linked_pe = frappe.db.sql_list(
+		"""select parent from `tabPayment Entry Reference`
+		where reference_doctype=%s and reference_name=%s and docstatus < 2""",
+		(ref_type, ref_no),
+	)
 
 	if linked_pe:
-		frappe.db.sql("""update `tabPayment Entry Reference`
+		frappe.db.sql(
+			"""update `tabPayment Entry Reference`
 			set allocated_amount=0, modified=%s, modified_by=%s
 			where reference_doctype=%s and reference_name=%s
-			and docstatus < 2""", (now(), frappe.session.user, ref_type, ref_no))
+			and docstatus < 2""",
+			(now(), frappe.session.user, ref_type, ref_no),
+		)
 
 		for pe in linked_pe:
 			try:
@@ -565,42 +699,62 @@
 				pe_doc.validate_payment_type_with_outstanding()
 			except Exception as e:
 				msg = _("There were issues unlinking payment entry {0}.").format(pe_doc.name)
-				msg += '<br>'
+				msg += "<br>"
 				msg += _("Please cancel payment entry manually first")
 				frappe.throw(msg, exc=PaymentEntryUnlinkError, title=_("Payment Unlink Error"))
 
-			frappe.db.sql("""update `tabPayment Entry` set total_allocated_amount=%s,
+			frappe.db.sql(
+				"""update `tabPayment Entry` set total_allocated_amount=%s,
 				base_total_allocated_amount=%s, unallocated_amount=%s, modified=%s, modified_by=%s
-				where name=%s""", (pe_doc.total_allocated_amount, pe_doc.base_total_allocated_amount,
-					pe_doc.unallocated_amount, now(), frappe.session.user, pe))
+				where name=%s""",
+				(
+					pe_doc.total_allocated_amount,
+					pe_doc.base_total_allocated_amount,
+					pe_doc.unallocated_amount,
+					now(),
+					frappe.session.user,
+					pe,
+				),
+			)
 
 		frappe.msgprint(_("Payment Entries {0} are un-linked").format("\n".join(linked_pe)))
 
+
 @frappe.whitelist()
 def get_company_default(company, fieldname, ignore_validation=False):
-	value = frappe.get_cached_value('Company',  company,  fieldname)
+	value = frappe.get_cached_value("Company", company, fieldname)
 
 	if not ignore_validation and not value:
-		throw(_("Please set default {0} in Company {1}")
-			.format(frappe.get_meta("Company").get_label(fieldname), company))
+		throw(
+			_("Please set default {0} in Company {1}").format(
+				frappe.get_meta("Company").get_label(fieldname), company
+			)
+		)
 
 	return value
 
+
 def fix_total_debit_credit():
-	vouchers = frappe.db.sql("""select voucher_type, voucher_no,
+	vouchers = frappe.db.sql(
+		"""select voucher_type, voucher_no,
 		sum(debit) - sum(credit) as diff
 		from `tabGL Entry`
 		group by voucher_type, voucher_no
-		having sum(debit) != sum(credit)""", as_dict=1)
+		having sum(debit) != sum(credit)""",
+		as_dict=1,
+	)
 
 	for d in vouchers:
 		if abs(d.diff) > 0:
 			dr_or_cr = d.voucher_type == "Sales Invoice" and "credit" or "debit"
 
-			frappe.db.sql("""update `tabGL Entry` set %s = %s + %s
-				where voucher_type = %s and voucher_no = %s and %s > 0 limit 1""" %
-				(dr_or_cr, dr_or_cr, '%s', '%s', '%s', dr_or_cr),
-				(d.diff, d.voucher_type, d.voucher_no))
+			frappe.db.sql(
+				"""update `tabGL Entry` set %s = %s + %s
+				where voucher_type = %s and voucher_no = %s and %s > 0 limit 1"""
+				% (dr_or_cr, dr_or_cr, "%s", "%s", "%s", dr_or_cr),
+				(d.diff, d.voucher_type, d.voucher_no),
+			)
+
 
 def get_currency_precision():
 	precision = cint(frappe.db.get_default("currency_precision"))
@@ -610,29 +764,41 @@
 
 	return precision
 
-def get_stock_rbnb_difference(posting_date, company):
-	stock_items = frappe.db.sql_list("""select distinct item_code
-		from `tabStock Ledger Entry` where company=%s""", company)
 
-	pr_valuation_amount = frappe.db.sql("""
+def get_stock_rbnb_difference(posting_date, company):
+	stock_items = frappe.db.sql_list(
+		"""select distinct item_code
+		from `tabStock Ledger Entry` where company=%s""",
+		company,
+	)
+
+	pr_valuation_amount = frappe.db.sql(
+		"""
 		select sum(pr_item.valuation_rate * pr_item.qty * pr_item.conversion_factor)
 		from `tabPurchase Receipt Item` pr_item, `tabPurchase Receipt` pr
 		where pr.name = pr_item.parent and pr.docstatus=1 and pr.company=%s
-		and pr.posting_date <= %s and pr_item.item_code in (%s)""" %
-		('%s', '%s', ', '.join(['%s']*len(stock_items))), tuple([company, posting_date] + stock_items))[0][0]
+		and pr.posting_date <= %s and pr_item.item_code in (%s)"""
+		% ("%s", "%s", ", ".join(["%s"] * len(stock_items))),
+		tuple([company, posting_date] + stock_items),
+	)[0][0]
 
-	pi_valuation_amount = frappe.db.sql("""
+	pi_valuation_amount = frappe.db.sql(
+		"""
 		select sum(pi_item.valuation_rate * pi_item.qty * pi_item.conversion_factor)
 		from `tabPurchase Invoice Item` pi_item, `tabPurchase Invoice` pi
 		where pi.name = pi_item.parent and pi.docstatus=1 and pi.company=%s
-		and pi.posting_date <= %s and pi_item.item_code in (%s)""" %
-		('%s', '%s', ', '.join(['%s']*len(stock_items))), tuple([company, posting_date] + stock_items))[0][0]
+		and pi.posting_date <= %s and pi_item.item_code in (%s)"""
+		% ("%s", "%s", ", ".join(["%s"] * len(stock_items))),
+		tuple([company, posting_date] + stock_items),
+	)[0][0]
 
 	# Balance should be
 	stock_rbnb = flt(pr_valuation_amount, 2) - flt(pi_valuation_amount, 2)
 
 	# Balance as per system
-	stock_rbnb_account = "Stock Received But Not Billed - " + frappe.get_cached_value('Company',  company,  "abbr")
+	stock_rbnb_account = "Stock Received But Not Billed - " + frappe.get_cached_value(
+		"Company", company, "abbr"
+	)
 	sys_bal = get_balance_on(stock_rbnb_account, posting_date, in_account_currency=False)
 
 	# Amount should be credited
@@ -645,152 +811,131 @@
 	"""
 	held_invoices = None
 
-	if party_type == 'Supplier':
+	if party_type == "Supplier":
 		held_invoices = frappe.db.sql(
-			'select name from `tabPurchase Invoice` where release_date IS NOT NULL and release_date > CURDATE()',
-			as_dict=1
+			"select name from `tabPurchase Invoice` where release_date IS NOT NULL and release_date > CURDATE()",
+			as_dict=1,
 		)
-		held_invoices = set(d['name'] for d in held_invoices)
+		held_invoices = set(d["name"] for d in held_invoices)
 
 	return held_invoices
 
 
-def get_outstanding_invoices(party_type, party, account, condition=None, filters=None):
+def get_outstanding_invoices(
+	party_type, party, account, common_filter=None, min_outstanding=None, max_outstanding=None
+):
+
+	ple = qb.DocType("Payment Ledger Entry")
 	outstanding_invoices = []
 	precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2
 
 	if account:
-		root_type, account_type = frappe.get_cached_value("Account", account, ["root_type", "account_type"])
+		root_type, account_type = frappe.get_cached_value(
+			"Account", account, ["root_type", "account_type"]
+		)
 		party_account_type = "Receivable" if root_type == "Asset" else "Payable"
 		party_account_type = account_type or party_account_type
 	else:
 		party_account_type = erpnext.get_party_account_type(party_type)
 
-	if party_account_type == 'Receivable':
-		dr_or_cr = "debit_in_account_currency - credit_in_account_currency"
-		payment_dr_or_cr = "credit_in_account_currency - debit_in_account_currency"
-	else:
-		dr_or_cr = "credit_in_account_currency - debit_in_account_currency"
-		payment_dr_or_cr = "debit_in_account_currency - credit_in_account_currency"
-
 	held_invoices = get_held_invoices(party_type, party)
 
-	invoice_list = frappe.db.sql("""
-		select
-			voucher_no, voucher_type, posting_date, due_date,
-			ifnull(sum({dr_or_cr}), 0) as invoice_amount,
-			account_currency as currency
-		from
-			`tabGL Entry`
-		where
-			party_type = %(party_type)s and party = %(party)s
-			and account = %(account)s and {dr_or_cr} > 0
-			and is_cancelled=0
-			{condition}
-			and ((voucher_type = 'Journal Entry'
-					and (against_voucher = '' or against_voucher is null))
-				or (voucher_type not in ('Journal Entry', 'Payment Entry')))
-		group by voucher_type, voucher_no
-		order by posting_date, name""".format(
-			dr_or_cr=dr_or_cr,
-			condition=condition or ""
-		), {
-			"party_type": party_type,
-			"party": party,
-			"account": account,
-		}, as_dict=True)
+	common_filter = common_filter or []
+	common_filter.append(ple.account_type == party_account_type)
+	common_filter.append(ple.account == account)
+	common_filter.append(ple.party_type == party_type)
+	common_filter.append(ple.party == party)
 
-	payment_entries = frappe.db.sql("""
-		select against_voucher_type, against_voucher,
-			ifnull(sum({payment_dr_or_cr}), 0) as payment_amount
-		from `tabGL Entry`
-		where party_type = %(party_type)s and party = %(party)s
-			and account = %(account)s
-			and {payment_dr_or_cr} > 0
-			and against_voucher is not null and against_voucher != ''
-			and is_cancelled=0
-		group by against_voucher_type, against_voucher
-	""".format(payment_dr_or_cr=payment_dr_or_cr), {
-		"party_type": party_type,
-		"party": party,
-		"account": account
-	}, as_dict=True)
-
-	pe_map = frappe._dict()
-	for d in payment_entries:
-		pe_map.setdefault((d.against_voucher_type, d.against_voucher), d.payment_amount)
+	ple_query = QueryPaymentLedger()
+	invoice_list = ple_query.get_voucher_outstandings(
+		common_filter=common_filter,
+		min_outstanding=min_outstanding,
+		max_outstanding=max_outstanding,
+		get_invoices=True,
+	)
 
 	for d in invoice_list:
-		payment_amount = pe_map.get((d.voucher_type, d.voucher_no), 0)
-		outstanding_amount = flt(d.invoice_amount - payment_amount, precision)
+		payment_amount = d.invoice_amount - d.outstanding
+		outstanding_amount = d.outstanding
 		if outstanding_amount > 0.5 / (10**precision):
-			if (filters and filters.get("outstanding_amt_greater_than") and
-				not (outstanding_amount >= filters.get("outstanding_amt_greater_than") and
-				outstanding_amount <= filters.get("outstanding_amt_less_than"))):
+			if (
+				min_outstanding
+				and max_outstanding
+				and not (outstanding_amount >= min_outstanding and outstanding_amount <= max_outstanding)
+			):
 				continue
 
 			if not d.voucher_type == "Purchase Invoice" or d.voucher_no not in held_invoices:
 				outstanding_invoices.append(
-					frappe._dict({
-						'voucher_no': d.voucher_no,
-						'voucher_type': d.voucher_type,
-						'posting_date': d.posting_date,
-						'invoice_amount': flt(d.invoice_amount),
-						'payment_amount': payment_amount,
-						'outstanding_amount': outstanding_amount,
-						'due_date': d.due_date,
-						'currency': d.currency
-					})
+					frappe._dict(
+						{
+							"voucher_no": d.voucher_no,
+							"voucher_type": d.voucher_type,
+							"posting_date": d.posting_date,
+							"invoice_amount": flt(d.invoice_amount),
+							"payment_amount": payment_amount,
+							"outstanding_amount": outstanding_amount,
+							"due_date": d.due_date,
+							"currency": d.currency,
+						}
+					)
 				)
 
-	outstanding_invoices = sorted(outstanding_invoices, key=lambda k: k['due_date'] or getdate(nowdate()))
+	outstanding_invoices = sorted(
+		outstanding_invoices, key=lambda k: k["due_date"] or getdate(nowdate())
+	)
 	return outstanding_invoices
 
 
-def get_account_name(account_type=None, root_type=None, is_group=None, account_currency=None, company=None):
+def get_account_name(
+	account_type=None, root_type=None, is_group=None, account_currency=None, company=None
+):
 	"""return account based on matching conditions"""
-	return frappe.db.get_value("Account", {
-		"account_type": account_type or '',
-		"root_type": root_type or '',
-		"is_group": is_group or 0,
-		"account_currency": account_currency or frappe.defaults.get_defaults().currency,
-		"company": company or frappe.defaults.get_defaults().company
-	}, "name")
+	return frappe.db.get_value(
+		"Account",
+		{
+			"account_type": account_type or "",
+			"root_type": root_type or "",
+			"is_group": is_group or 0,
+			"account_currency": account_currency or frappe.defaults.get_defaults().currency,
+			"company": company or frappe.defaults.get_defaults().company,
+		},
+		"name",
+	)
+
 
 @frappe.whitelist()
 def get_companies():
 	"""get a list of companies based on permission"""
-	return [d.name for d in frappe.get_list("Company", fields=["name"],
-		order_by="name")]
+	return [d.name for d in frappe.get_list("Company", fields=["name"], order_by="name")]
+
 
 @frappe.whitelist()
 def get_children(doctype, parent, company, is_root=False):
 	from erpnext.accounts.report.financial_statements import sort_accounts
 
-	parent_fieldname = 'parent_' + doctype.lower().replace(' ', '_')
-	fields = [
-		'name as value',
-		'is_group as expandable'
-	]
-	filters = [['docstatus', '<', 2]]
+	parent_fieldname = "parent_" + doctype.lower().replace(" ", "_")
+	fields = ["name as value", "is_group as expandable"]
+	filters = [["docstatus", "<", 2]]
 
-	filters.append(['ifnull(`{0}`,"")'.format(parent_fieldname), '=', '' if is_root else parent])
+	filters.append(['ifnull(`{0}`,"")'.format(parent_fieldname), "=", "" if is_root else parent])
 
 	if is_root:
-		fields += ['root_type', 'report_type', 'account_currency'] if doctype == 'Account' else []
-		filters.append(['company', '=', company])
+		fields += ["root_type", "report_type", "account_currency"] if doctype == "Account" else []
+		filters.append(["company", "=", company])
 
 	else:
-		fields += ['root_type', 'account_currency'] if doctype == 'Account' else []
-		fields += [parent_fieldname + ' as parent']
+		fields += ["root_type", "account_currency"] if doctype == "Account" else []
+		fields += [parent_fieldname + " as parent"]
 
 	acc = frappe.get_list(doctype, fields=fields, filters=filters)
 
-	if doctype == 'Account':
+	if doctype == "Account":
 		sort_accounts(acc, is_root, key="value")
 
 	return acc
 
+
 @frappe.whitelist()
 def get_account_balances(accounts, company):
 
@@ -800,16 +945,19 @@
 	if not accounts:
 		return []
 
-	company_currency = frappe.get_cached_value("Company",  company,  "default_currency")
+	company_currency = frappe.get_cached_value("Company", company, "default_currency")
 
 	for account in accounts:
 		account["company_currency"] = company_currency
-		account["balance"] = flt(get_balance_on(account["value"], in_account_currency=False, company=company))
+		account["balance"] = flt(
+			get_balance_on(account["value"], in_account_currency=False, company=company)
+		)
 		if account["account_currency"] and account["account_currency"] != company_currency:
 			account["balance_in_account_currency"] = flt(get_balance_on(account["value"], company=company))
 
 	return accounts
 
+
 def create_payment_gateway_account(gateway, payment_channel="Email"):
 	from erpnext.setup.setup_wizard.operations.install_fixtures import create_bank_account
 
@@ -818,13 +966,21 @@
 		return
 
 	# NOTE: we translate Payment Gateway account name because that is going to be used by the end user
-	bank_account = frappe.db.get_value("Account", {"account_name": _(gateway), "company": company},
-		["name", 'account_currency'], as_dict=1)
+	bank_account = frappe.db.get_value(
+		"Account",
+		{"account_name": _(gateway), "company": company},
+		["name", "account_currency"],
+		as_dict=1,
+	)
 
 	if not bank_account:
 		# check for untranslated one
-		bank_account = frappe.db.get_value("Account", {"account_name": gateway, "company": company},
-			["name", 'account_currency'], as_dict=1)
+		bank_account = frappe.db.get_value(
+			"Account",
+			{"account_name": gateway, "company": company},
+			["name", "account_currency"],
+			as_dict=1,
+		)
 
 	if not bank_account:
 		# try creating one
@@ -835,30 +991,35 @@
 		return
 
 	# if payment gateway account exists, return
-	if frappe.db.exists("Payment Gateway Account",
-		{"payment_gateway": gateway, "currency": bank_account.account_currency}):
+	if frappe.db.exists(
+		"Payment Gateway Account",
+		{"payment_gateway": gateway, "currency": bank_account.account_currency},
+	):
 		return
 
 	try:
-		frappe.get_doc({
-			"doctype": "Payment Gateway Account",
-			"is_default": 1,
-			"payment_gateway": gateway,
-			"payment_account": bank_account.name,
-			"currency": bank_account.account_currency,
-			"payment_channel": payment_channel
-		}).insert(ignore_permissions=True, ignore_if_duplicate=True)
+		frappe.get_doc(
+			{
+				"doctype": "Payment Gateway Account",
+				"is_default": 1,
+				"payment_gateway": gateway,
+				"payment_account": bank_account.name,
+				"currency": bank_account.account_currency,
+				"payment_channel": payment_channel,
+			}
+		).insert(ignore_permissions=True, ignore_if_duplicate=True)
 
 	except frappe.DuplicateEntryError:
 		# already exists, due to a reinstall?
 		pass
 
+
 @frappe.whitelist()
 def update_cost_center(docname, cost_center_name, cost_center_number, company, merge):
-	'''
-		Renames the document by adding the number as a prefix to the current name and updates
-		all transaction where it was present.
-	'''
+	"""
+	Renames the document by adding the number as a prefix to the current name and updates
+	all transaction where it was present.
+	"""
 	validate_field_number("Cost Center", docname, cost_center_number, company, "cost_center_number")
 
 	if cost_center_number:
@@ -873,8 +1034,9 @@
 		frappe.rename_doc("Cost Center", docname, new_name, force=1, merge=merge)
 		return new_name
 
+
 def validate_field_number(doctype_name, docname, number_value, company, field_name):
-	''' Validate if the number entered isn't already assigned to some other document. '''
+	"""Validate if the number entered isn't already assigned to some other document."""
 	if number_value:
 		filters = {field_name: number_value, "name": ["!=", docname]}
 		if company:
@@ -883,20 +1045,25 @@
 		doctype_with_same_number = frappe.db.get_value(doctype_name, filters)
 
 		if doctype_with_same_number:
-			frappe.throw(_("{0} Number {1} is already used in {2} {3}")
-				.format(doctype_name, number_value, doctype_name.lower(), doctype_with_same_number))
+			frappe.throw(
+				_("{0} Number {1} is already used in {2} {3}").format(
+					doctype_name, number_value, doctype_name.lower(), doctype_with_same_number
+				)
+			)
+
 
 def get_autoname_with_number(number_value, doc_title, name, company):
-	''' append title with prefix as number and suffix as company's abbreviation separated by '-' '''
+	"""append title with prefix as number and suffix as company's abbreviation separated by '-'"""
 	if name:
-		name_split=name.split("-")
-		parts = [doc_title.strip(), name_split[len(name_split)-1].strip()]
+		name_split = name.split("-")
+		parts = [doc_title.strip(), name_split[len(name_split) - 1].strip()]
 	else:
-		abbr = frappe.get_cached_value('Company',  company,  ["abbr"], as_dict=True)
+		abbr = frappe.get_cached_value("Company", company, ["abbr"], as_dict=True)
 		parts = [doc_title.strip(), abbr.abbr]
 	if cstr(number_value).strip():
 		parts.insert(0, cstr(number_value).strip())
-	return ' - '.join(parts)
+	return " - ".join(parts)
+
 
 @frappe.whitelist()
 def get_coa(doctype, parent, is_root, chart=None):
@@ -908,43 +1075,115 @@
 	chart = chart if chart else frappe.flags.chart
 	frappe.flags.chart = chart
 
-	parent = None if parent==_('All Accounts') else parent
-	accounts = build_tree_from_json(chart) # returns alist of dict in a tree render-able form
+	parent = None if parent == _("All Accounts") else parent
+	accounts = build_tree_from_json(chart)  # returns alist of dict in a tree render-able form
 
 	# filter out to show data for the selected node only
-	accounts = [d for d in accounts if d['parent_account']==parent]
+	accounts = [d for d in accounts if d["parent_account"] == parent]
 
 	return accounts
 
-def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
-		warehouse_account=None, company=None):
-	stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items, company)
+
+def update_gl_entries_after(
+	posting_date,
+	posting_time,
+	for_warehouses=None,
+	for_items=None,
+	warehouse_account=None,
+	company=None,
+):
+	stock_vouchers = get_future_stock_vouchers(
+		posting_date, posting_time, for_warehouses, for_items, company
+	)
 	repost_gle_for_stock_vouchers(stock_vouchers, posting_date, company, warehouse_account)
 
 
-def repost_gle_for_stock_vouchers(stock_vouchers, posting_date, company=None, warehouse_account=None):
-	def _delete_gl_entries(voucher_type, voucher_no):
-		frappe.db.sql("""delete from `tabGL Entry`
-			where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
+def repost_gle_for_stock_vouchers(
+	stock_vouchers: List[Tuple[str, str]],
+	posting_date: str,
+	company: Optional[str] = None,
+	warehouse_account=None,
+	repost_doc: Optional["RepostItemValuation"] = None,
+):
+
+	from erpnext.accounts.general_ledger import toggle_debit_credit_if_negative
+
+	if not stock_vouchers:
+		return
 
 	if not warehouse_account:
 		warehouse_account = get_warehouse_account_map(company)
 
+	stock_vouchers = sort_stock_vouchers_by_posting_date(stock_vouchers)
+	if repost_doc and repost_doc.gl_reposting_index:
+		# Restore progress
+		stock_vouchers = stock_vouchers[cint(repost_doc.gl_reposting_index) :]
+
 	precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit")) or 2
 
-	gle = get_voucherwise_gl_entries(stock_vouchers, posting_date)
-	for voucher_type, voucher_no in stock_vouchers:
-		existing_gle = gle.get((voucher_type, voucher_no), [])
-		voucher_obj = frappe.get_cached_doc(voucher_type, voucher_no)
-		expected_gle = voucher_obj.get_gl_entries(warehouse_account)
-		if expected_gle:
-			if not existing_gle or not compare_existing_and_expected_gle(existing_gle, expected_gle, precision):
-				_delete_gl_entries(voucher_type, voucher_no)
-				voucher_obj.make_gl_entries(gl_entries=expected_gle, from_repost=True)
-		else:
-			_delete_gl_entries(voucher_type, voucher_no)
+	for stock_vouchers_chunk in create_batch(stock_vouchers, GL_REPOSTING_CHUNK):
+		gle = get_voucherwise_gl_entries(stock_vouchers_chunk, posting_date)
 
-def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None, company=None):
+		for voucher_type, voucher_no in stock_vouchers_chunk:
+			existing_gle = gle.get((voucher_type, voucher_no), [])
+			voucher_obj = frappe.get_doc(voucher_type, voucher_no)
+			# Some transactions post credit as negative debit, this is handled while posting GLE
+			# but while comparing we need to make sure it's flipped so comparisons are accurate
+			expected_gle = toggle_debit_credit_if_negative(voucher_obj.get_gl_entries(warehouse_account))
+			if expected_gle:
+				if not existing_gle or not compare_existing_and_expected_gle(
+					existing_gle, expected_gle, precision
+				):
+					_delete_gl_entries(voucher_type, voucher_no)
+					voucher_obj.make_gl_entries(gl_entries=expected_gle, from_repost=True)
+			else:
+				_delete_gl_entries(voucher_type, voucher_no)
+
+		if not frappe.flags.in_test:
+			frappe.db.commit()
+
+		if repost_doc:
+			repost_doc.db_set(
+				"gl_reposting_index",
+				cint(repost_doc.gl_reposting_index) + len(stock_vouchers_chunk),
+			)
+
+
+def _delete_gl_entries(voucher_type, voucher_no):
+	frappe.db.sql(
+		"""delete from `tabGL Entry`
+		where voucher_type=%s and voucher_no=%s""",
+		(voucher_type, voucher_no),
+	)
+
+
+def sort_stock_vouchers_by_posting_date(
+	stock_vouchers: List[Tuple[str, str]]
+) -> List[Tuple[str, str]]:
+	sle = frappe.qb.DocType("Stock Ledger Entry")
+	voucher_nos = [v[1] for v in stock_vouchers]
+
+	sles = (
+		frappe.qb.from_(sle)
+		.select(sle.voucher_type, sle.voucher_no, sle.posting_date, sle.posting_time, sle.creation)
+		.where((sle.is_cancelled == 0) & (sle.voucher_no.isin(voucher_nos)))
+		.groupby(sle.voucher_type, sle.voucher_no)
+		.orderby(sle.posting_date)
+		.orderby(sle.posting_time)
+		.orderby(sle.creation)
+	).run(as_dict=True)
+	sorted_vouchers = [(sle.voucher_type, sle.voucher_no) for sle in sles]
+
+	unknown_vouchers = set(stock_vouchers) - set(sorted_vouchers)
+	if unknown_vouchers:
+		sorted_vouchers.extend(unknown_vouchers)
+
+	return sorted_vouchers
+
+
+def get_future_stock_vouchers(
+	posting_date, posting_time, for_warehouses=None, for_items=None, company=None
+):
 
 	values = []
 	condition = ""
@@ -960,25 +1199,31 @@
 		condition += " and company = %s"
 		values.append(company)
 
-	future_stock_vouchers = frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
+	future_stock_vouchers = frappe.db.sql(
+		"""select distinct sle.voucher_type, sle.voucher_no
 		from `tabStock Ledger Entry` sle
 		where
 			timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s)
 			and is_cancelled = 0
 			{condition}
-		order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc for update""".format(condition=condition),
-		tuple([posting_date, posting_time] + values), as_dict=True)
+		order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc for update""".format(
+			condition=condition
+		),
+		tuple([posting_date, posting_time] + values),
+		as_dict=True,
+	)
 
 	return [(d.voucher_type, d.voucher_no) for d in future_stock_vouchers]
 
+
 def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
-	""" Get voucherwise list of GL entries.
+	"""Get voucherwise list of GL entries.
 
 	Only fetches GLE fields required for comparing with new GLE.
 	Check compare_existing_and_expected_gle function below.
 
 	returns:
-		Dict[Tuple[voucher_type, voucher_no], List[GL Entries]]
+	        Dict[Tuple[voucher_type, voucher_no], List[GL Entries]]
 	"""
 	gl_entries = {}
 	if not future_stock_vouchers:
@@ -986,19 +1231,23 @@
 
 	voucher_nos = [d[1] for d in future_stock_vouchers]
 
-	gles = frappe.db.sql("""
+	gles = frappe.db.sql(
+		"""
 		select name, account, credit, debit, cost_center, project, voucher_type, voucher_no
 			from `tabGL Entry`
 		where
-			posting_date >= %s and voucher_no in (%s)""" %
-		('%s', ', '.join(['%s'] * len(voucher_nos))),
-		tuple([posting_date] + voucher_nos), as_dict=1)
+			posting_date >= %s and voucher_no in (%s)"""
+		% ("%s", ", ".join(["%s"] * len(voucher_nos))),
+		tuple([posting_date] + voucher_nos),
+		as_dict=1,
+	)
 
 	for d in gles:
 		gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d)
 
 	return gl_entries
 
+
 def compare_existing_and_expected_gle(existing_gle, expected_gle, precision):
 	if len(existing_gle) != len(expected_gle):
 		return False
@@ -1009,10 +1258,14 @@
 		for e in existing_gle:
 			if entry.account == e.account:
 				account_existed = True
-			if (entry.account == e.account
-					and (not entry.cost_center or not e.cost_center or entry.cost_center == e.cost_center)
-					and ( flt(entry.debit, precision) != flt(e.debit, precision) or
-						flt(entry.credit, precision) != flt(e.credit, precision))):
+			if (
+				entry.account == e.account
+				and (not entry.cost_center or not e.cost_center or entry.cost_center == e.cost_center)
+				and (
+					flt(entry.debit, precision) != flt(e.debit, precision)
+					or flt(entry.credit, precision) != flt(e.credit, precision)
+				)
+			):
 				matched = False
 				break
 		if not account_existed:
@@ -1020,69 +1273,51 @@
 			break
 	return matched
 
-def check_if_stock_and_account_balance_synced(posting_date, company, voucher_type=None, voucher_no=None):
-	if not cint(erpnext.is_perpetual_inventory_enabled(company)):
-		return
-
-	accounts = get_stock_accounts(company, voucher_type, voucher_no)
-	stock_adjustment_account = frappe.db.get_value("Company", company, "stock_adjustment_account")
-
-	for account in accounts:
-		account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(account,
-			posting_date, company)
-
-		if abs(account_bal - stock_bal) > 0.1:
-			precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"),
-				currency=frappe.get_cached_value('Company',  company,  "default_currency"))
-
-			diff = flt(stock_bal - account_bal, precision)
-
-			error_reason = _("Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses as on {3}.").format(
-				stock_bal, account_bal, frappe.bold(account), posting_date)
-			error_resolution = _("Please create an adjustment Journal Entry for amount {0} on {1}")\
-				.format(frappe.bold(diff), frappe.bold(posting_date))
-
-			frappe.msgprint(
-				msg="""{0}<br></br>{1}<br></br>""".format(error_reason, error_resolution),
-				raise_exception=StockValueAndAccountBalanceOutOfSync,
-				title=_('Values Out Of Sync'),
-				primary_action={
-					'label': _('Make Journal Entry'),
-					'client_action': 'erpnext.route_to_adjustment_jv',
-					'args': get_journal_entry(account, stock_adjustment_account, diff)
-				})
 
 def get_stock_accounts(company, voucher_type=None, voucher_no=None):
-	stock_accounts = [d.name for d in frappe.db.get_all("Account", {
-		"account_type": "Stock",
-		"company": company,
-		"is_group": 0
-	})]
+	stock_accounts = [
+		d.name
+		for d in frappe.db.get_all(
+			"Account", {"account_type": "Stock", "company": company, "is_group": 0}
+		)
+	]
 	if voucher_type and voucher_no:
 		if voucher_type == "Journal Entry":
-			stock_accounts = [d.account for d in frappe.db.get_all("Journal Entry Account", {
-				"parent": voucher_no,
-				"account": ["in", stock_accounts]
-			}, "account")]
+			stock_accounts = [
+				d.account
+				for d in frappe.db.get_all(
+					"Journal Entry Account", {"parent": voucher_no, "account": ["in", stock_accounts]}, "account"
+				)
+			]
 
 		else:
-			stock_accounts = [d.account for d in frappe.db.get_all("GL Entry", {
-				"voucher_type": voucher_type,
-				"voucher_no": voucher_no,
-				"account": ["in", stock_accounts]
-			}, "account")]
+			stock_accounts = [
+				d.account
+				for d in frappe.db.get_all(
+					"GL Entry",
+					{"voucher_type": voucher_type, "voucher_no": voucher_no, "account": ["in", stock_accounts]},
+					"account",
+				)
+			]
 
 	return stock_accounts
 
+
 def get_stock_and_account_balance(account=None, posting_date=None, company=None):
-	if not posting_date: posting_date = nowdate()
+	if not posting_date:
+		posting_date = nowdate()
 
 	warehouse_account = get_warehouse_account_map(company)
 
-	account_balance = get_balance_on(account, posting_date, in_account_currency=False, ignore_account_permission=True)
+	account_balance = get_balance_on(
+		account, posting_date, in_account_currency=False, ignore_account_permission=True
+	)
 
-	related_warehouses = [wh for wh, wh_details in warehouse_account.items()
-		if wh_details.account == account and not wh_details.is_group]
+	related_warehouses = [
+		wh
+		for wh, wh_details in warehouse_account.items()
+		if wh_details.account == account and not wh_details.is_group
+	]
 
 	total_stock_value = 0.0
 	for warehouse in related_warehouses:
@@ -1092,27 +1327,353 @@
 	precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency")
 	return flt(account_balance, precision), flt(total_stock_value, precision), related_warehouses
 
+
 def get_journal_entry(account, stock_adjustment_account, amount):
-	db_or_cr_warehouse_account =('credit_in_account_currency' if amount < 0 else 'debit_in_account_currency')
-	db_or_cr_stock_adjustment_account = ('debit_in_account_currency' if amount < 0 else 'credit_in_account_currency')
+	db_or_cr_warehouse_account = (
+		"credit_in_account_currency" if amount < 0 else "debit_in_account_currency"
+	)
+	db_or_cr_stock_adjustment_account = (
+		"debit_in_account_currency" if amount < 0 else "credit_in_account_currency"
+	)
 
 	return {
-		'accounts':[{
-			'account': account,
-			db_or_cr_warehouse_account: abs(amount)
-		}, {
-			'account': stock_adjustment_account,
-			db_or_cr_stock_adjustment_account : abs(amount)
-		}]
+		"accounts": [
+			{"account": account, db_or_cr_warehouse_account: abs(amount)},
+			{"account": stock_adjustment_account, db_or_cr_stock_adjustment_account: abs(amount)},
+		]
 	}
 
+
 def check_and_delete_linked_reports(report):
-	""" Check if reports are referenced in Desktop Icon """
-	icons = frappe.get_all("Desktop Icon",
-						fields = ['name'],
-						filters = {
-							"_report": report
-						})
+	"""Check if reports are referenced in Desktop Icon"""
+	icons = frappe.get_all("Desktop Icon", fields=["name"], filters={"_report": report})
 	if icons:
 		for icon in icons:
 			frappe.delete_doc("Desktop Icon", icon)
+
+
+def create_payment_ledger_entry(
+	gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0
+):
+	if gl_entries:
+		ple = None
+
+		# companies
+		account = qb.DocType("Account")
+		companies = list(set([x.company for x in gl_entries]))
+
+		# receivable/payable account
+		accounts_with_types = (
+			qb.from_(account)
+			.select(account.name, account.account_type)
+			.where(
+				(account.account_type.isin(["Receivable", "Payable"]) & (account.company.isin(companies)))
+			)
+			.run(as_dict=True)
+		)
+		receivable_or_payable_accounts = [y.name for y in accounts_with_types]
+
+		def get_account_type(account):
+			for entry in accounts_with_types:
+				if entry.name == account:
+					return entry.account_type
+
+		dr_or_cr = 0
+		account_type = None
+		for gle in gl_entries:
+			if gle.account in receivable_or_payable_accounts:
+				account_type = get_account_type(gle.account)
+				if account_type == "Receivable":
+					dr_or_cr = gle.debit - gle.credit
+					dr_or_cr_account_currency = gle.debit_in_account_currency - gle.credit_in_account_currency
+				elif account_type == "Payable":
+					dr_or_cr = gle.credit - gle.debit
+					dr_or_cr_account_currency = gle.credit_in_account_currency - gle.debit_in_account_currency
+
+				if cancel:
+					dr_or_cr *= -1
+					dr_or_cr_account_currency *= -1
+
+				ple = frappe.get_doc(
+					{
+						"doctype": "Payment Ledger Entry",
+						"posting_date": gle.posting_date,
+						"company": gle.company,
+						"account_type": account_type,
+						"account": gle.account,
+						"party_type": gle.party_type,
+						"party": gle.party,
+						"cost_center": gle.cost_center,
+						"finance_book": gle.finance_book,
+						"due_date": gle.due_date,
+						"voucher_type": gle.voucher_type,
+						"voucher_no": gle.voucher_no,
+						"against_voucher_type": gle.against_voucher_type
+						if gle.against_voucher_type
+						else gle.voucher_type,
+						"against_voucher_no": gle.against_voucher if gle.against_voucher else gle.voucher_no,
+						"currency": gle.currency,
+						"amount": dr_or_cr,
+						"amount_in_account_currency": dr_or_cr_account_currency,
+						"delinked": True if cancel else False,
+					}
+				)
+
+				dimensions_and_defaults = get_dimensions()
+				if dimensions_and_defaults:
+					for dimension in dimensions_and_defaults[0]:
+						ple.set(dimension.fieldname, gle.get(dimension.fieldname))
+
+				if cancel:
+					delink_original_entry(ple)
+				ple.flags.ignore_permissions = 1
+				ple.flags.adv_adj = adv_adj
+				ple.flags.from_repost = from_repost
+				ple.flags.update_outstanding = update_outstanding
+				ple.submit()
+
+
+def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, party):
+	ple = frappe.qb.DocType("Payment Ledger Entry")
+	vouchers = [frappe._dict({"voucher_type": voucher_type, "voucher_no": voucher_no})]
+	common_filter = []
+	if account:
+		common_filter.append(ple.account == account)
+
+	if party_type:
+		common_filter.append(ple.party_type == party_type)
+
+	if party:
+		common_filter.append(ple.party == party)
+
+	ple_query = QueryPaymentLedger()
+
+	# on cancellation outstanding can be an empty list
+	voucher_outstanding = ple_query.get_voucher_outstandings(vouchers, common_filter=common_filter)
+	if voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"] and voucher_outstanding:
+		outstanding = voucher_outstanding[0]
+		ref_doc = frappe.get_doc(voucher_type, voucher_no)
+
+		# Didn't use db_set for optimisation purpose
+		ref_doc.outstanding_amount = outstanding["outstanding_in_account_currency"]
+		frappe.db.set_value(
+			voucher_type, voucher_no, "outstanding_amount", outstanding["outstanding_in_account_currency"]
+		)
+
+		ref_doc.set_status(update=True)
+
+
+def delink_original_entry(pl_entry):
+	if pl_entry:
+		ple = qb.DocType("Payment Ledger Entry")
+		query = (
+			qb.update(ple)
+			.set(ple.delinked, True)
+			.set(ple.modified, now())
+			.set(ple.modified_by, frappe.session.user)
+			.where(
+				(ple.company == pl_entry.company)
+				& (ple.account_type == pl_entry.account_type)
+				& (ple.account == pl_entry.account)
+				& (ple.party_type == pl_entry.party_type)
+				& (ple.party == pl_entry.party)
+				& (ple.voucher_type == pl_entry.voucher_type)
+				& (ple.voucher_no == pl_entry.voucher_no)
+				& (ple.against_voucher_type == pl_entry.against_voucher_type)
+				& (ple.against_voucher_no == pl_entry.against_voucher_no)
+			)
+		)
+		query.run()
+
+
+class QueryPaymentLedger(object):
+	"""
+	Helper Class for Querying Payment Ledger Entry
+	"""
+
+	def __init__(self):
+		self.ple = qb.DocType("Payment Ledger Entry")
+
+		# query result
+		self.voucher_outstandings = []
+
+		# query filters
+		self.vouchers = []
+		self.common_filter = []
+		self.min_outstanding = None
+		self.max_outstanding = None
+
+	def reset(self):
+		# clear filters
+		self.vouchers.clear()
+		self.common_filter.clear()
+		self.min_outstanding = self.max_outstanding = None
+
+		# clear result
+		self.voucher_outstandings.clear()
+
+	def query_for_outstanding(self):
+		"""
+		Database query to fetch voucher amount and voucher outstanding using Common Table Expression
+		"""
+
+		ple = self.ple
+
+		filter_on_voucher_no = []
+		filter_on_against_voucher_no = []
+		if self.vouchers:
+			voucher_types = set([x.voucher_type for x in self.vouchers])
+			voucher_nos = set([x.voucher_no for x in self.vouchers])
+
+			filter_on_voucher_no.append(ple.voucher_type.isin(voucher_types))
+			filter_on_voucher_no.append(ple.voucher_no.isin(voucher_nos))
+
+			filter_on_against_voucher_no.append(ple.against_voucher_type.isin(voucher_types))
+			filter_on_against_voucher_no.append(ple.against_voucher_no.isin(voucher_nos))
+
+		# build outstanding amount filter
+		filter_on_outstanding_amount = []
+		if self.min_outstanding:
+			if self.min_outstanding > 0:
+				filter_on_outstanding_amount.append(
+					Table("outstanding").amount_in_account_currency >= self.min_outstanding
+				)
+			else:
+				filter_on_outstanding_amount.append(
+					Table("outstanding").amount_in_account_currency <= self.min_outstanding
+				)
+		if self.max_outstanding:
+			if self.max_outstanding > 0:
+				filter_on_outstanding_amount.append(
+					Table("outstanding").amount_in_account_currency <= self.max_outstanding
+				)
+			else:
+				filter_on_outstanding_amount.append(
+					Table("outstanding").amount_in_account_currency >= self.max_outstanding
+				)
+
+		# build query for voucher amount
+		query_voucher_amount = (
+			qb.from_(ple)
+			.select(
+				ple.account,
+				ple.voucher_type,
+				ple.voucher_no,
+				ple.party_type,
+				ple.party,
+				ple.posting_date,
+				ple.due_date,
+				ple.account_currency.as_("currency"),
+				Sum(ple.amount).as_("amount"),
+				Sum(ple.amount_in_account_currency).as_("amount_in_account_currency"),
+			)
+			.where(ple.delinked == 0)
+			.where(Criterion.all(filter_on_voucher_no))
+			.where(Criterion.all(self.common_filter))
+			.groupby(ple.voucher_type, ple.voucher_no, ple.party_type, ple.party)
+		)
+
+		# build query for voucher outstanding
+		query_voucher_outstanding = (
+			qb.from_(ple)
+			.select(
+				ple.account,
+				ple.against_voucher_type.as_("voucher_type"),
+				ple.against_voucher_no.as_("voucher_no"),
+				ple.party_type,
+				ple.party,
+				ple.posting_date,
+				ple.due_date,
+				ple.account_currency.as_("currency"),
+				Sum(ple.amount).as_("amount"),
+				Sum(ple.amount_in_account_currency).as_("amount_in_account_currency"),
+			)
+			.where(ple.delinked == 0)
+			.where(Criterion.all(filter_on_against_voucher_no))
+			.where(Criterion.all(self.common_filter))
+			.groupby(ple.against_voucher_type, ple.against_voucher_no, ple.party_type, ple.party)
+		)
+
+		# build CTE for combining voucher amount and outstanding
+		self.cte_query_voucher_amount_and_outstanding = (
+			qb.with_(query_voucher_amount, "vouchers")
+			.with_(query_voucher_outstanding, "outstanding")
+			.from_(AliasedQuery("vouchers"))
+			.left_join(AliasedQuery("outstanding"))
+			.on(
+				(AliasedQuery("vouchers").account == AliasedQuery("outstanding").account)
+				& (AliasedQuery("vouchers").voucher_type == AliasedQuery("outstanding").voucher_type)
+				& (AliasedQuery("vouchers").voucher_no == AliasedQuery("outstanding").voucher_no)
+				& (AliasedQuery("vouchers").party_type == AliasedQuery("outstanding").party_type)
+				& (AliasedQuery("vouchers").party == AliasedQuery("outstanding").party)
+			)
+			.select(
+				Table("vouchers").account,
+				Table("vouchers").voucher_type,
+				Table("vouchers").voucher_no,
+				Table("vouchers").party_type,
+				Table("vouchers").party,
+				Table("vouchers").posting_date,
+				Table("vouchers").amount.as_("invoice_amount"),
+				Table("vouchers").amount_in_account_currency.as_("invoice_amount_in_account_currency"),
+				Table("outstanding").amount.as_("outstanding"),
+				Table("outstanding").amount_in_account_currency.as_("outstanding_in_account_currency"),
+				(Table("vouchers").amount - Table("outstanding").amount).as_("paid_amount"),
+				(
+					Table("vouchers").amount_in_account_currency - Table("outstanding").amount_in_account_currency
+				).as_("paid_amount_in_account_currency"),
+				Table("vouchers").due_date,
+				Table("vouchers").currency,
+			)
+			.where(Criterion.all(filter_on_outstanding_amount))
+		)
+
+		# build CTE filter
+		# only fetch invoices
+		if self.get_invoices:
+			self.cte_query_voucher_amount_and_outstanding = (
+				self.cte_query_voucher_amount_and_outstanding.having(
+					qb.Field("outstanding_in_account_currency") > 0
+				)
+			)
+		# only fetch payments
+		elif self.get_payments:
+			self.cte_query_voucher_amount_and_outstanding = (
+				self.cte_query_voucher_amount_and_outstanding.having(
+					qb.Field("outstanding_in_account_currency") < 0
+				)
+			)
+
+		# execute SQL
+		self.voucher_outstandings = self.cte_query_voucher_amount_and_outstanding.run(as_dict=True)
+
+	def get_voucher_outstandings(
+		self,
+		vouchers=None,
+		common_filter=None,
+		min_outstanding=None,
+		max_outstanding=None,
+		get_payments=False,
+		get_invoices=False,
+	):
+		"""
+		Fetch voucher amount and outstanding amount from Payment Ledger using Database CTE
+
+		vouchers - dict of vouchers to get
+		common_filter - array of criterions
+		min_outstanding - filter on minimum total outstanding amount
+		max_outstanding - filter on maximum total  outstanding amount
+		get_invoices - only fetch vouchers(ledger entries with +ve outstanding)
+		get_payments - only fetch payments(ledger entries with -ve outstanding)
+		"""
+
+		self.reset()
+		self.vouchers = vouchers
+		self.common_filter = common_filter or []
+		self.min_outstanding = min_outstanding
+		self.max_outstanding = max_outstanding
+		self.get_payments = get_payments
+		self.get_invoices = get_invoices
+		self.query_for_outstanding()
+
+		return self.voucher_outstandings
diff --git a/erpnext/accounts/workspace/accounting/accounting.json b/erpnext/accounts/workspace/accounting/accounting.json
index f7d381a..786d3c8 100644
--- a/erpnext/accounts/workspace/accounting/accounting.json
+++ b/erpnext/accounts/workspace/accounting/accounting.json
@@ -508,18 +508,6 @@
    "dependencies": "GL Entry",
    "hidden": 0,
    "is_query_report": 1,
-   "label": "DATEV Export",
-   "link_count": 0,
-   "link_to": "DATEV",
-   "link_type": "Report",
-   "onboard": 0,
-   "only_for": "Germany",
-   "type": "Link"
-  },
-  {
-   "dependencies": "GL Entry",
-   "hidden": 0,
-   "is_query_report": 1,
    "label": "UAE VAT 201",
    "link_count": 0,
    "link_to": "UAE VAT 201",
@@ -1094,13 +1082,14 @@
    "type": "Link"
   }
  ],
- "modified": "2022-02-19 06:22:18.091827",
+ "modified": "2022-06-10 15:49:42.990860",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounting",
  "owner": "Administrator",
  "parent_page": "",
  "public": 1,
+ "quick_lists": [],
  "restrict_to_domain": "",
  "roles": [],
  "sequence_id": 2.0,
diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py
index 39f0f1a..fc9ba38 100644
--- a/erpnext/assets/dashboard_fixtures.py
+++ b/erpnext/assets/dashboard_fixtures.py
@@ -18,30 +18,36 @@
 	if not fiscal_year:
 		return frappe._dict()
 
-	year_start_date = get_date_str(fiscal_year.get('year_start_date'))
-	year_end_date = get_date_str(fiscal_year.get('year_end_date'))
+	year_start_date = get_date_str(fiscal_year.get("year_start_date"))
+	year_end_date = get_date_str(fiscal_year.get("year_end_date"))
 
-	return frappe._dict({
-		"dashboards": get_dashboards(),
-		"charts": get_charts(fiscal_year, year_start_date, year_end_date),
-		"number_cards": get_number_cards(fiscal_year, year_start_date, year_end_date),
-	})
+	return frappe._dict(
+		{
+			"dashboards": get_dashboards(),
+			"charts": get_charts(fiscal_year, year_start_date, year_end_date),
+			"number_cards": get_number_cards(fiscal_year, year_start_date, year_end_date),
+		}
+	)
+
 
 def get_dashboards():
-	return [{
-		"name": "Asset",
-		"dashboard_name": "Asset",
-        "charts": [
-			{ "chart": "Asset Value Analytics", "width": "Full" },
-            { "chart": "Category-wise Asset Value", "width": "Half" },
-            { "chart": "Location-wise Asset Value", "width": "Half" },
-        ],
-		"cards": [
-			{"card": "Total Assets"},
-			{"card": "New Assets (This Year)"},
-			{"card": "Asset Value"}
-		]
-    }]
+	return [
+		{
+			"name": "Asset",
+			"dashboard_name": "Asset",
+			"charts": [
+				{"chart": "Asset Value Analytics", "width": "Full"},
+				{"chart": "Category-wise Asset Value", "width": "Half"},
+				{"chart": "Location-wise Asset Value", "width": "Half"},
+			],
+			"cards": [
+				{"card": "Total Assets"},
+				{"card": "New Assets (This Year)"},
+				{"card": "Asset Value"},
+			],
+		}
+	]
+
 
 def get_charts(fiscal_year, year_start_date, year_end_date):
 	company = get_company_for_dashboards()
@@ -58,26 +64,30 @@
 			"timespan": "Last Year",
 			"time_interval": "Yearly",
 			"timeseries": 0,
-			"filters_json": json.dumps({
-				"company": company,
-				"status": "In Location",
-				"filter_based_on": "Fiscal Year",
-				"from_fiscal_year": fiscal_year.get('name'),
-				"to_fiscal_year": fiscal_year.get('name'),
-				"period_start_date": year_start_date,
-				"period_end_date": year_end_date,
-				"date_based_on": "Purchase Date",
-				"group_by": "--Select a group--"
-			}),
+			"filters_json": json.dumps(
+				{
+					"company": company,
+					"status": "In Location",
+					"filter_based_on": "Fiscal Year",
+					"from_fiscal_year": fiscal_year.get("name"),
+					"to_fiscal_year": fiscal_year.get("name"),
+					"period_start_date": year_start_date,
+					"period_end_date": year_end_date,
+					"date_based_on": "Purchase Date",
+					"group_by": "--Select a group--",
+				}
+			),
 			"type": "Bar",
-			"custom_options": json.dumps({
-				"type": "bar",
-				"barOptions": { "stacked": 1 },
-				"axisOptions": { "shortenYAxisNumbers": 1 },
-				"tooltipOptions": {}
-			}),
+			"custom_options": json.dumps(
+				{
+					"type": "bar",
+					"barOptions": {"stacked": 1},
+					"axisOptions": {"shortenYAxisNumbers": 1},
+					"tooltipOptions": {},
+				}
+			),
 			"doctype": "Dashboard Chart",
-			"y_axis": []
+			"y_axis": [],
 		},
 		{
 			"name": "Category-wise Asset Value",
@@ -86,12 +96,14 @@
 			"report_name": "Fixed Asset Register",
 			"x_field": "asset_category",
 			"timeseries": 0,
-			"filters_json": json.dumps({
-				"company": company,
-				"status":"In Location",
-				"group_by":"Asset Category",
-				"is_existing_asset":0
-			}),
+			"filters_json": json.dumps(
+				{
+					"company": company,
+					"status": "In Location",
+					"group_by": "Asset Category",
+					"is_existing_asset": 0,
+				}
+			),
 			"type": "Donut",
 			"doctype": "Dashboard Chart",
 			"y_axis": [
@@ -100,14 +112,12 @@
 					"parentfield": "y_axis",
 					"parenttype": "Dashboard Chart",
 					"y_field": "asset_value",
-					"doctype": "Dashboard Chart Field"
+					"doctype": "Dashboard Chart Field",
 				}
 			],
-			"custom_options": json.dumps({
-				"type": "donut",
-				"height": 300,
-				"axisOptions": {"shortenYAxisNumbers": 1}
-			})
+			"custom_options": json.dumps(
+				{"type": "donut", "height": 300, "axisOptions": {"shortenYAxisNumbers": 1}}
+			),
 		},
 		{
 			"name": "Location-wise Asset Value",
@@ -116,12 +126,9 @@
 			"report_name": "Fixed Asset Register",
 			"x_field": "location",
 			"timeseries": 0,
-			"filters_json": json.dumps({
-				"company": company,
-				"status":"In Location",
-				"group_by":"Location",
-				"is_existing_asset":0
-			}),
+			"filters_json": json.dumps(
+				{"company": company, "status": "In Location", "group_by": "Location", "is_existing_asset": 0}
+			),
 			"type": "Donut",
 			"doctype": "Dashboard Chart",
 			"y_axis": [
@@ -130,17 +137,16 @@
 					"parentfield": "y_axis",
 					"parenttype": "Dashboard Chart",
 					"y_field": "asset_value",
-					"doctype": "Dashboard Chart Field"
+					"doctype": "Dashboard Chart Field",
 				}
 			],
-			"custom_options": json.dumps({
-				"type": "donut",
-				"height": 300,
-				"axisOptions": {"shortenYAxisNumbers": 1}
-			})
-		}
+			"custom_options": json.dumps(
+				{"type": "donut", "height": 300, "axisOptions": {"shortenYAxisNumbers": 1}}
+			),
+		},
 	]
 
+
 def get_number_cards(fiscal_year, year_start_date, year_end_date):
 	return [
 		{
@@ -162,9 +168,9 @@
 			"is_public": 1,
 			"show_percentage_stats": 1,
 			"stats_time_interval": "Monthly",
-			"filters_json": json.dumps([
-				['Asset', 'creation', 'between', [year_start_date, year_end_date]]
-			]),
+			"filters_json": json.dumps(
+				[["Asset", "creation", "between", [year_start_date, year_end_date]]]
+			),
 			"doctype": "Number Card",
 		},
 		{
@@ -177,6 +183,6 @@
 			"show_percentage_stats": 1,
 			"stats_time_interval": "Monthly",
 			"filters_json": "[]",
-			"doctype": "Number Card"
-		}
+			"doctype": "Number Card",
+		},
 	]
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index ea473fa..257488d 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -58,21 +58,26 @@
 		self.cancel_movement_entries()
 		self.delete_depreciation_entries()
 		self.set_status()
-		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
-		make_reverse_gl_entries(voucher_type='Asset', voucher_no=self.name)
-		self.db_set('booked_fixed_asset', 0)
+		self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
+		make_reverse_gl_entries(voucher_type="Asset", voucher_no=self.name)
+		self.db_set("booked_fixed_asset", 0)
 
 	def validate_asset_and_reference(self):
 		if self.purchase_invoice or self.purchase_receipt:
-			reference_doc = 'Purchase Invoice' if self.purchase_invoice else 'Purchase Receipt'
+			reference_doc = "Purchase Invoice" if self.purchase_invoice else "Purchase Receipt"
 			reference_name = self.purchase_invoice or self.purchase_receipt
 			reference_doc = frappe.get_doc(reference_doc, reference_name)
-			if reference_doc.get('company') != self.company:
-				frappe.throw(_("Company of asset {0} and purchase document {1} doesn't matches.").format(self.name, reference_doc.get('name')))
-
+			if reference_doc.get("company") != self.company:
+				frappe.throw(
+					_("Company of asset {0} and purchase document {1} doesn't matches.").format(
+						self.name, reference_doc.get("name")
+					)
+				)
 
 		if self.is_existing_asset and self.purchase_invoice:
-			frappe.throw(_("Purchase Invoice cannot be made against an existing asset {0}").format(self.name))
+			frappe.throw(
+				_("Purchase Invoice cannot be made against an existing asset {0}").format(self.name)
+			)
 
 	def prepare_depreciation_data(self, date_of_sale=None, date_of_return=None):
 		if self.calculate_depreciation:
@@ -82,12 +87,14 @@
 			self.set_accumulated_depreciation(date_of_sale, date_of_return)
 		else:
 			self.finance_books = []
-			self.value_after_depreciation = (flt(self.gross_purchase_amount) -
-				flt(self.opening_accumulated_depreciation))
+			self.value_after_depreciation = flt(self.gross_purchase_amount) - flt(
+				self.opening_accumulated_depreciation
+			)
 
 	def validate_item(self):
-		item = frappe.get_cached_value("Item", self.item_code,
-			["is_fixed_asset", "is_stock_item", "disabled"], as_dict=1)
+		item = frappe.get_cached_value(
+			"Item", self.item_code, ["is_fixed_asset", "is_stock_item", "disabled"], as_dict=1
+		)
 		if not item:
 			frappe.throw(_("Item {0} does not exist").format(self.item_code))
 		elif item.disabled:
@@ -98,16 +105,16 @@
 			frappe.throw(_("Item {0} must be a non-stock item").format(self.item_code))
 
 	def validate_cost_center(self):
-		if not self.cost_center: return
+		if not self.cost_center:
+			return
 
-		cost_center_company = frappe.db.get_value('Cost Center', self.cost_center, 'company')
+		cost_center_company = frappe.db.get_value("Cost Center", self.cost_center, "company")
 		if cost_center_company != self.company:
 			frappe.throw(
 				_("Selected Cost Center {} doesn't belongs to {}").format(
-					frappe.bold(self.cost_center),
-					frappe.bold(self.company)
+					frappe.bold(self.cost_center), frappe.bold(self.company)
 				),
-				title=_("Invalid Cost Center")
+				title=_("Invalid Cost Center"),
 			)
 
 	def validate_in_use_date(self):
@@ -116,16 +123,20 @@
 
 		for d in self.finance_books:
 			if d.depreciation_start_date == self.available_for_use_date:
-				frappe.throw(_("Row #{}: Depreciation Posting Date should not be equal to Available for Use Date.").format(d.idx),
-					title=_("Incorrect Date"))
+				frappe.throw(
+					_("Row #{}: Depreciation Posting Date should not be equal to Available for Use Date.").format(
+						d.idx
+					),
+					title=_("Incorrect Date"),
+				)
 
 	def set_missing_values(self):
 		if not self.asset_category:
 			self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category")
 
-		if self.item_code and not self.get('finance_books'):
+		if self.item_code and not self.get("finance_books"):
 			finance_books = get_item_details(self.item_code, self.asset_category)
-			self.set('finance_books', finance_books)
+			self.set("finance_books", finance_books)
 
 	def validate_asset_values(self):
 		if not self.asset_category:
@@ -136,13 +147,20 @@
 
 		if is_cwip_accounting_enabled(self.asset_category):
 			if not self.is_existing_asset and not (self.purchase_receipt or self.purchase_invoice):
-				frappe.throw(_("Please create purchase receipt or purchase invoice for the item {0}").
-					format(self.item_code))
+				frappe.throw(
+					_("Please create purchase receipt or purchase invoice for the item {0}").format(
+						self.item_code
+					)
+				)
 
-			if (not self.purchase_receipt and self.purchase_invoice
-				and not frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock')):
-				frappe.throw(_("Update stock must be enable for the purchase invoice {0}").
-					format(self.purchase_invoice))
+			if (
+				not self.purchase_receipt
+				and self.purchase_invoice
+				and not frappe.db.get_value("Purchase Invoice", self.purchase_invoice, "update_stock")
+			):
+				frappe.throw(
+					_("Update stock must be enable for the purchase invoice {0}").format(self.purchase_invoice)
+				)
 
 		if not self.calculate_depreciation:
 			return
@@ -152,49 +170,63 @@
 		if self.is_existing_asset:
 			return
 
-		if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date):
+		if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(
+			self.purchase_date
+		):
 			frappe.throw(_("Available-for-use Date should be after purchase date"))
 
 	def validate_gross_and_purchase_amount(self):
-		if self.is_existing_asset: return
+		if self.is_existing_asset:
+			return
 
 		if self.gross_purchase_amount and self.gross_purchase_amount != self.purchase_receipt_amount:
-			error_message = _("Gross Purchase Amount should be <b>equal</b> to purchase amount of one single Asset.")
+			error_message = _(
+				"Gross Purchase Amount should be <b>equal</b> to purchase amount of one single Asset."
+			)
 			error_message += "<br>"
 			error_message += _("Please do not book expense of multiple assets against one single Asset.")
 			frappe.throw(error_message, title=_("Invalid Gross Purchase Amount"))
 
 	def make_asset_movement(self):
-		reference_doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice'
+		reference_doctype = "Purchase Receipt" if self.purchase_receipt else "Purchase Invoice"
 		reference_docname = self.purchase_receipt or self.purchase_invoice
 		transaction_date = getdate(self.purchase_date)
 		if reference_docname:
-			posting_date, posting_time = frappe.db.get_value(reference_doctype, reference_docname, ["posting_date", "posting_time"])
+			posting_date, posting_time = frappe.db.get_value(
+				reference_doctype, reference_docname, ["posting_date", "posting_time"]
+			)
 			transaction_date = get_datetime("{} {}".format(posting_date, posting_time))
-		assets = [{
-			'asset': self.name,
-			'asset_name': self.asset_name,
-			'target_location': self.location,
-			'to_employee': self.custodian
-		}]
-		asset_movement = frappe.get_doc({
-			'doctype': 'Asset Movement',
-			'assets': assets,
-			'purpose': 'Receipt',
-			'company': self.company,
-			'transaction_date': transaction_date,
-			'reference_doctype': reference_doctype,
-			'reference_name': reference_docname
-		}).insert()
+		assets = [
+			{
+				"asset": self.name,
+				"asset_name": self.asset_name,
+				"target_location": self.location,
+				"to_employee": self.custodian,
+			}
+		]
+		asset_movement = frappe.get_doc(
+			{
+				"doctype": "Asset Movement",
+				"assets": assets,
+				"purpose": "Receipt",
+				"company": self.company,
+				"transaction_date": transaction_date,
+				"reference_doctype": reference_doctype,
+				"reference_name": reference_docname,
+			}
+		).insert()
 		asset_movement.submit()
 
 	def set_depreciation_rate(self):
 		for d in self.get("finance_books"):
-			d.rate_of_depreciation = flt(self.get_depreciation_rate(d, on_validate=True),
-				d.precision("rate_of_depreciation"))
+			d.rate_of_depreciation = flt(
+				self.get_depreciation_rate(d, on_validate=True), d.precision("rate_of_depreciation")
+			)
 
 	def make_depreciation_schedule(self, date_of_sale):
-		if 'Manual' not in [d.depreciation_method for d in self.finance_books] and not self.get('schedules'):
+		if "Manual" not in [d.depreciation_method for d in self.finance_books] and not self.get(
+			"schedules"
+		):
 			self.schedules = []
 
 		if not self.available_for_use_date:
@@ -202,7 +234,7 @@
 
 		start = self.clear_depreciation_schedule()
 
-		for finance_book in self.get('finance_books'):
+		for finance_book in self.get("finance_books"):
 			self._make_depreciation_schedule(finance_book, start, date_of_sale)
 
 	def _make_depreciation_schedule(self, finance_book, start, date_of_sale):
@@ -211,8 +243,9 @@
 		value_after_depreciation = self._get_value_after_depreciation(finance_book)
 		finance_book.value_after_depreciation = value_after_depreciation
 
-		number_of_pending_depreciations = cint(finance_book.total_number_of_depreciations) - \
-			cint(self.number_of_depreciations_booked)
+		number_of_pending_depreciations = cint(finance_book.total_number_of_depreciations) - cint(
+			self.number_of_depreciations_booked
+		)
 
 		has_pro_rata = self.check_is_pro_rata(finance_book)
 		if has_pro_rata:
@@ -220,70 +253,89 @@
 
 		skip_row = False
 
-		for n in range(start[finance_book.idx-1], number_of_pending_depreciations):
+		for n in range(start[finance_book.idx - 1], number_of_pending_depreciations):
 			# If depreciation is already completed (for double declining balance)
-			if skip_row: continue
+			if skip_row:
+				continue
 
 			depreciation_amount = get_depreciation_amount(self, value_after_depreciation, finance_book)
 
 			if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1:
-				schedule_date = add_months(finance_book.depreciation_start_date,
-					n * cint(finance_book.frequency_of_depreciation))
+				schedule_date = add_months(
+					finance_book.depreciation_start_date, n * cint(finance_book.frequency_of_depreciation)
+				)
 
 				# schedule date will be a year later from start date
 				# so monthly schedule date is calculated by removing 11 months from it
-				monthly_schedule_date = add_months(schedule_date, - finance_book.frequency_of_depreciation + 1)
+				monthly_schedule_date = add_months(schedule_date, -finance_book.frequency_of_depreciation + 1)
 
 			# if asset is being sold
 			if date_of_sale:
 				from_date = self.get_from_date(finance_book.finance_book)
-				depreciation_amount, days, months = self.get_pro_rata_amt(finance_book, depreciation_amount,
-					from_date, date_of_sale)
+				depreciation_amount, days, months = self.get_pro_rata_amt(
+					finance_book, depreciation_amount, from_date, date_of_sale
+				)
 
 				if depreciation_amount > 0:
-					self._add_depreciation_row(date_of_sale, depreciation_amount, finance_book.depreciation_method,
-						finance_book.finance_book, finance_book.idx)
+					self._add_depreciation_row(
+						date_of_sale,
+						depreciation_amount,
+						finance_book.depreciation_method,
+						finance_book.finance_book,
+						finance_book.idx,
+					)
 
 				break
 
 			# For first row
-			if has_pro_rata and not self.opening_accumulated_depreciation and n==0:
-				from_date = add_days(self.available_for_use_date, -1) # needed to calc depr amount for available_for_use_date too
-				depreciation_amount, days, months = self.get_pro_rata_amt(finance_book, depreciation_amount,
-					from_date, finance_book.depreciation_start_date)
+			if has_pro_rata and not self.opening_accumulated_depreciation and n == 0:
+				from_date = add_days(
+					self.available_for_use_date, -1
+				)  # needed to calc depr amount for available_for_use_date too
+				depreciation_amount, days, months = self.get_pro_rata_amt(
+					finance_book, depreciation_amount, from_date, finance_book.depreciation_start_date
+				)
 
 				# For first depr schedule date will be the start date
 				# so monthly schedule date is calculated by removing month difference between use date and start date
-				monthly_schedule_date = add_months(finance_book.depreciation_start_date, - months + 1)
+				monthly_schedule_date = add_months(finance_book.depreciation_start_date, -months + 1)
 
 			# For last row
 			elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1:
 				if not self.flags.increase_in_asset_life:
 					# In case of increase_in_asset_life, the self.to_date is already set on asset_repair submission
-					self.to_date = add_months(self.available_for_use_date,
-						(n + self.number_of_depreciations_booked) * cint(finance_book.frequency_of_depreciation))
+					self.to_date = add_months(
+						self.available_for_use_date,
+						(n + self.number_of_depreciations_booked) * cint(finance_book.frequency_of_depreciation),
+					)
 
 				depreciation_amount_without_pro_rata = depreciation_amount
 
-				depreciation_amount, days, months = self.get_pro_rata_amt(finance_book,
-					depreciation_amount, schedule_date, self.to_date)
+				depreciation_amount, days, months = self.get_pro_rata_amt(
+					finance_book, depreciation_amount, schedule_date, self.to_date
+				)
 
-				depreciation_amount = self.get_adjusted_depreciation_amount(depreciation_amount_without_pro_rata,
-					depreciation_amount, finance_book.finance_book)
+				depreciation_amount = self.get_adjusted_depreciation_amount(
+					depreciation_amount_without_pro_rata, depreciation_amount, finance_book.finance_book
+				)
 
 				monthly_schedule_date = add_months(schedule_date, 1)
 				schedule_date = add_days(schedule_date, days)
 				last_schedule_date = schedule_date
 
-			if not depreciation_amount: continue
-			value_after_depreciation -= flt(depreciation_amount,
-				self.precision("gross_purchase_amount"))
+			if not depreciation_amount:
+				continue
+			value_after_depreciation -= flt(depreciation_amount, self.precision("gross_purchase_amount"))
 
 			# Adjust depreciation amount in the last period based on the expected value after useful life
-			if finance_book.expected_value_after_useful_life and ((n == cint(number_of_pending_depreciations) - 1
-				and value_after_depreciation != finance_book.expected_value_after_useful_life)
-				or value_after_depreciation < finance_book.expected_value_after_useful_life):
-				depreciation_amount += (value_after_depreciation - finance_book.expected_value_after_useful_life)
+			if finance_book.expected_value_after_useful_life and (
+				(
+					n == cint(number_of_pending_depreciations) - 1
+					and value_after_depreciation != finance_book.expected_value_after_useful_life
+				)
+				or value_after_depreciation < finance_book.expected_value_after_useful_life
+			):
+				depreciation_amount += value_after_depreciation - finance_book.expected_value_after_useful_life
 				skip_row = True
 
 			if depreciation_amount > 0:
@@ -291,12 +343,15 @@
 				if self.allow_monthly_depreciation:
 					# month range is 1 to 12
 					# In pro rata case, for first and last depreciation, month range would be different
-					month_range = months \
-						if (has_pro_rata and n==0) or (has_pro_rata and n == cint(number_of_pending_depreciations) - 1) \
+					month_range = (
+						months
+						if (has_pro_rata and n == 0)
+						or (has_pro_rata and n == cint(number_of_pending_depreciations) - 1)
 						else finance_book.frequency_of_depreciation
+					)
 
 					for r in range(month_range):
-						if (has_pro_rata and n == 0):
+						if has_pro_rata and n == 0:
 							# For first entry of monthly depr
 							if r == 0:
 								days_until_first_depr = date_diff(monthly_schedule_date, self.available_for_use_date)
@@ -308,7 +363,9 @@
 							else:
 								date = add_months(monthly_schedule_date, r)
 								amount = depreciation_amount / (month_range - 1)
-						elif (has_pro_rata and n == cint(number_of_pending_depreciations) - 1) and r == cint(month_range) - 1:
+						elif (has_pro_rata and n == cint(number_of_pending_depreciations) - 1) and r == cint(
+							month_range
+						) - 1:
 							# For last entry of monthly depr
 							date = last_schedule_date
 							amount = depreciation_amount / month_range
@@ -316,28 +373,40 @@
 							date = add_months(monthly_schedule_date, r)
 							amount = depreciation_amount / month_range
 
-						self._add_depreciation_row(date, amount, finance_book.depreciation_method,
-							finance_book.finance_book, finance_book.idx)
+						self._add_depreciation_row(
+							date, amount, finance_book.depreciation_method, finance_book.finance_book, finance_book.idx
+						)
 				else:
-					self._add_depreciation_row(schedule_date, depreciation_amount, finance_book.depreciation_method,
-						finance_book.finance_book, finance_book.idx)
+					self._add_depreciation_row(
+						schedule_date,
+						depreciation_amount,
+						finance_book.depreciation_method,
+						finance_book.finance_book,
+						finance_book.idx,
+					)
 
-	def _add_depreciation_row(self, schedule_date, depreciation_amount, depreciation_method, finance_book, finance_book_id):
-		self.append("schedules", {
-			"schedule_date": schedule_date,
-			"depreciation_amount": depreciation_amount,
-			"depreciation_method": depreciation_method,
-			"finance_book": finance_book,
-			"finance_book_id": finance_book_id
-		})
+	def _add_depreciation_row(
+		self, schedule_date, depreciation_amount, depreciation_method, finance_book, finance_book_id
+	):
+		self.append(
+			"schedules",
+			{
+				"schedule_date": schedule_date,
+				"depreciation_amount": depreciation_amount,
+				"depreciation_method": depreciation_method,
+				"finance_book": finance_book,
+				"finance_book_id": finance_book_id,
+			},
+		)
 
 	def _get_value_after_depreciation(self, finance_book):
 		# value_after_depreciation - current Asset value
 		if self.docstatus == 1 and finance_book.value_after_depreciation:
 			value_after_depreciation = flt(finance_book.value_after_depreciation)
 		else:
-			value_after_depreciation = (flt(self.gross_purchase_amount) -
-				flt(self.opening_accumulated_depreciation))
+			value_after_depreciation = flt(self.gross_purchase_amount) - flt(
+				self.opening_accumulated_depreciation
+			)
 
 		return value_after_depreciation
 
@@ -348,7 +417,7 @@
 		num_of_depreciations_completed = 0
 		depr_schedule = []
 
-		for schedule in self.get('schedules'):
+		for schedule in self.get("schedules"):
 			# to update start when there are JEs linked with all the schedule rows corresponding to an FB
 			if len(start) == (int(schedule.finance_book_id) - 2):
 				start.append(num_of_depreciations_completed)
@@ -376,14 +445,14 @@
 		return start
 
 	def get_from_date(self, finance_book):
-		if not self.get('schedules'):
+		if not self.get("schedules"):
 			return self.available_for_use_date
 
 		if len(self.finance_books) == 1:
 			return self.schedules[-1].schedule_date
 
 		from_date = ""
-		for schedule in self.get('schedules'):
+		for schedule in self.get("schedules"):
 			if schedule.finance_book == finance_book:
 				from_date = schedule.schedule_date
 
@@ -412,17 +481,25 @@
 		return has_pro_rata
 
 	def get_modified_available_for_use_date(self, row):
-		return add_months(self.available_for_use_date, (self.number_of_depreciations_booked * row.frequency_of_depreciation))
+		return add_months(
+			self.available_for_use_date,
+			(self.number_of_depreciations_booked * row.frequency_of_depreciation),
+		)
 
 	def validate_asset_finance_books(self, row):
 		if flt(row.expected_value_after_useful_life) >= flt(self.gross_purchase_amount):
-			frappe.throw(_("Row {0}: Expected Value After Useful Life must be less than Gross Purchase Amount")
-				.format(row.idx), title=_("Invalid Schedule"))
+			frappe.throw(
+				_("Row {0}: Expected Value After Useful Life must be less than Gross Purchase Amount").format(
+					row.idx
+				),
+				title=_("Invalid Schedule"),
+			)
 
 		if not row.depreciation_start_date:
 			if not self.available_for_use_date:
-				frappe.throw(_("Row {0}: Depreciation Start Date is required")
-					.format(row.idx), title=_("Invalid Schedule"))
+				frappe.throw(
+					_("Row {0}: Depreciation Start Date is required").format(row.idx), title=_("Invalid Schedule")
+				)
 			row.depreciation_start_date = get_last_day(self.available_for_use_date)
 
 		if not self.is_existing_asset:
@@ -431,8 +508,11 @@
 		else:
 			depreciable_amount = flt(self.gross_purchase_amount) - flt(row.expected_value_after_useful_life)
 			if flt(self.opening_accumulated_depreciation) > depreciable_amount:
-					frappe.throw(_("Opening Accumulated Depreciation must be less than equal to {0}")
-						.format(depreciable_amount))
+				frappe.throw(
+					_("Opening Accumulated Depreciation must be less than equal to {0}").format(
+						depreciable_amount
+					)
+				)
 
 			if self.opening_accumulated_depreciation:
 				if not self.number_of_depreciations_booked:
@@ -441,24 +521,45 @@
 				self.number_of_depreciations_booked = 0
 
 			if flt(row.total_number_of_depreciations) <= cint(self.number_of_depreciations_booked):
-				frappe.throw(_("Row {0}: Total Number of Depreciations cannot be less than or equal to Number of Depreciations Booked")
-					.format(row.idx), title=_("Invalid Schedule"))
+				frappe.throw(
+					_(
+						"Row {0}: Total Number of Depreciations cannot be less than or equal to Number of Depreciations Booked"
+					).format(row.idx),
+					title=_("Invalid Schedule"),
+				)
 
-		if row.depreciation_start_date and getdate(row.depreciation_start_date) < getdate(self.purchase_date):
-			frappe.throw(_("Depreciation Row {0}: Next Depreciation Date cannot be before Purchase Date")
-				.format(row.idx))
+		if row.depreciation_start_date and getdate(row.depreciation_start_date) < getdate(
+			self.purchase_date
+		):
+			frappe.throw(
+				_("Depreciation Row {0}: Next Depreciation Date cannot be before Purchase Date").format(
+					row.idx
+				)
+			)
 
-		if row.depreciation_start_date and getdate(row.depreciation_start_date) < getdate(self.available_for_use_date):
-			frappe.throw(_("Depreciation Row {0}: Next Depreciation Date cannot be before Available-for-use Date")
-				.format(row.idx))
+		if row.depreciation_start_date and getdate(row.depreciation_start_date) < getdate(
+			self.available_for_use_date
+		):
+			frappe.throw(
+				_(
+					"Depreciation Row {0}: Next Depreciation Date cannot be before Available-for-use Date"
+				).format(row.idx)
+			)
 
 	# to ensure that final accumulated depreciation amount is accurate
-	def get_adjusted_depreciation_amount(self, depreciation_amount_without_pro_rata, depreciation_amount_for_last_row, finance_book):
+	def get_adjusted_depreciation_amount(
+		self, depreciation_amount_without_pro_rata, depreciation_amount_for_last_row, finance_book
+	):
 		if not self.opening_accumulated_depreciation:
 			depreciation_amount_for_first_row = self.get_depreciation_amount_for_first_row(finance_book)
 
-			if depreciation_amount_for_first_row + depreciation_amount_for_last_row != depreciation_amount_without_pro_rata:
-				depreciation_amount_for_last_row = depreciation_amount_without_pro_rata - depreciation_amount_for_first_row
+			if (
+				depreciation_amount_for_first_row + depreciation_amount_for_last_row
+				!= depreciation_amount_without_pro_rata
+			):
+				depreciation_amount_for_last_row = (
+					depreciation_amount_without_pro_rata - depreciation_amount_for_first_row
+				)
 
 		return depreciation_amount_for_last_row
 
@@ -474,8 +575,12 @@
 		if len(self.finance_books) == 1:
 			return True
 
-	def set_accumulated_depreciation(self, date_of_sale=None, date_of_return=None, ignore_booked_entry = False):
-		straight_line_idx = [d.idx for d in self.get("schedules") if d.depreciation_method == 'Straight Line']
+	def set_accumulated_depreciation(
+		self, date_of_sale=None, date_of_return=None, ignore_booked_entry=False
+	):
+		straight_line_idx = [
+			d.idx for d in self.get("schedules") if d.depreciation_method == "Straight Line"
+		]
 		finance_books = []
 
 		for i, d in enumerate(self.get("schedules")):
@@ -491,41 +596,64 @@
 			value_after_depreciation -= flt(depreciation_amount)
 
 			# for the last row, if depreciation method = Straight Line
-			if straight_line_idx and i == max(straight_line_idx) - 1 and not date_of_sale and not date_of_return:
-				book = self.get('finance_books')[cint(d.finance_book_id) - 1]
-				depreciation_amount += flt(value_after_depreciation -
-					flt(book.expected_value_after_useful_life), d.precision("depreciation_amount"))
+			if (
+				straight_line_idx
+				and i == max(straight_line_idx) - 1
+				and not date_of_sale
+				and not date_of_return
+			):
+				book = self.get("finance_books")[cint(d.finance_book_id) - 1]
+				depreciation_amount += flt(
+					value_after_depreciation - flt(book.expected_value_after_useful_life),
+					d.precision("depreciation_amount"),
+				)
 
 			d.depreciation_amount = depreciation_amount
 			accumulated_depreciation += d.depreciation_amount
-			d.accumulated_depreciation_amount = flt(accumulated_depreciation,
-				d.precision("accumulated_depreciation_amount"))
+			d.accumulated_depreciation_amount = flt(
+				accumulated_depreciation, d.precision("accumulated_depreciation_amount")
+			)
 
 	def get_value_after_depreciation(self, idx):
-		return flt(self.get('finance_books')[cint(idx)-1].value_after_depreciation)
+		return flt(self.get("finance_books")[cint(idx) - 1].value_after_depreciation)
 
 	def validate_expected_value_after_useful_life(self):
-		for row in self.get('finance_books'):
-			accumulated_depreciation_after_full_schedule = [d.accumulated_depreciation_amount
-				for d in self.get("schedules") if cint(d.finance_book_id) == row.idx]
+		for row in self.get("finance_books"):
+			accumulated_depreciation_after_full_schedule = [
+				d.accumulated_depreciation_amount
+				for d in self.get("schedules")
+				if cint(d.finance_book_id) == row.idx
+			]
 
 			if accumulated_depreciation_after_full_schedule:
-				accumulated_depreciation_after_full_schedule = max(accumulated_depreciation_after_full_schedule)
+				accumulated_depreciation_after_full_schedule = max(
+					accumulated_depreciation_after_full_schedule
+				)
 
 				asset_value_after_full_schedule = flt(
-					flt(self.gross_purchase_amount) -
-					flt(accumulated_depreciation_after_full_schedule), self.precision('gross_purchase_amount'))
+					flt(self.gross_purchase_amount) - flt(accumulated_depreciation_after_full_schedule),
+					self.precision("gross_purchase_amount"),
+				)
 
-				if (row.expected_value_after_useful_life and
-					row.expected_value_after_useful_life < asset_value_after_full_schedule):
-					frappe.throw(_("Depreciation Row {0}: Expected value after useful life must be greater than or equal to {1}")
-						.format(row.idx, asset_value_after_full_schedule))
+				if (
+					row.expected_value_after_useful_life
+					and row.expected_value_after_useful_life < asset_value_after_full_schedule
+				):
+					frappe.throw(
+						_(
+							"Depreciation Row {0}: Expected value after useful life must be greater than or equal to {1}"
+						).format(row.idx, asset_value_after_full_schedule)
+					)
 				elif not row.expected_value_after_useful_life:
 					row.expected_value_after_useful_life = asset_value_after_full_schedule
 
 	def validate_cancellation(self):
 		if self.status in ("In Maintenance", "Out of Order"):
-			frappe.throw(_("There are active maintenance or repairs against the asset. You must complete all of them before cancelling the asset."))
+			frappe.throw(
+				_(
+					"There are active maintenance or repairs against the asset. You must complete all of them before cancelling the asset."
+				)
+			)
 		if self.status not in ("Submitted", "Partially Depreciated", "Fully Depreciated"):
 			frappe.throw(_("Asset cannot be cancelled, as it is already {0}").format(self.status))
 
@@ -533,10 +661,13 @@
 		movements = frappe.db.sql(
 			"""SELECT asm.name, asm.docstatus
 			FROM `tabAsset Movement` asm, `tabAsset Movement Item` asm_item
-			WHERE asm_item.parent=asm.name and asm_item.asset=%s and asm.docstatus=1""", self.name, as_dict=1)
+			WHERE asm_item.parent=asm.name and asm_item.asset=%s and asm.docstatus=1""",
+			self.name,
+			as_dict=1,
+		)
 
 		for movement in movements:
-			movement = frappe.get_doc('Asset Movement', movement.get('name'))
+			movement = frappe.get_doc("Asset Movement", movement.get("name"))
 			movement.cancel()
 
 	def delete_depreciation_entries(self):
@@ -545,17 +676,19 @@
 				frappe.get_doc("Journal Entry", d.journal_entry).cancel()
 				d.db_set("journal_entry", None)
 
-		self.db_set("value_after_depreciation",
-			(flt(self.gross_purchase_amount) - flt(self.opening_accumulated_depreciation)))
+		self.db_set(
+			"value_after_depreciation",
+			(flt(self.gross_purchase_amount) - flt(self.opening_accumulated_depreciation)),
+		)
 
 	def set_status(self, status=None):
-		'''Get and update status'''
+		"""Get and update status"""
 		if not status:
 			status = self.get_status()
 		self.db_set("status", status)
 
 	def get_status(self):
-		'''Returns status based on whether it is draft, submitted, scrapped or depreciated'''
+		"""Returns status based on whether it is draft, submitted, scrapped or depreciated"""
 		if self.docstatus == 0:
 			status = "Draft"
 		elif self.docstatus == 1:
@@ -572,17 +705,17 @@
 				if flt(value_after_depreciation) <= expected_value_after_useful_life:
 					status = "Fully Depreciated"
 				elif flt(value_after_depreciation) < flt(self.gross_purchase_amount):
-					status = 'Partially Depreciated'
+					status = "Partially Depreciated"
 		elif self.docstatus == 2:
 			status = "Cancelled"
 		return status
 
 	def get_default_finance_book_idx(self):
-		if not self.get('default_finance_book') and self.company:
+		if not self.get("default_finance_book") and self.company:
 			self.default_finance_book = erpnext.get_default_finance_book(self.company)
 
-		if self.get('default_finance_book'):
-			for d in self.get('finance_books'):
+		if self.get("default_finance_book"):
+			for d in self.get("finance_books"):
 				if d.finance_book == self.default_finance_book:
 					return cint(d.idx) - 1
 
@@ -591,7 +724,7 @@
 		if not purchase_document:
 			return False
 
-		asset_bought_with_invoice = (purchase_document == self.purchase_invoice)
+		asset_bought_with_invoice = purchase_document == self.purchase_invoice
 		fixed_asset_account = self.get_fixed_asset_account()
 
 		cwip_enabled = is_cwip_accounting_enabled(self.asset_category)
@@ -621,13 +754,17 @@
 			return cwip_booked
 
 	def get_purchase_document(self):
-		asset_bought_with_invoice = self.purchase_invoice and frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock')
+		asset_bought_with_invoice = self.purchase_invoice and frappe.db.get_value(
+			"Purchase Invoice", self.purchase_invoice, "update_stock"
+		)
 		purchase_document = self.purchase_invoice if asset_bought_with_invoice else self.purchase_receipt
 
 		return purchase_document
 
 	def get_fixed_asset_account(self):
-		fixed_asset_account = get_asset_category_account('fixed_asset_account', None, self.name, None, self.asset_category, self.company)
+		fixed_asset_account = get_asset_category_account(
+			"fixed_asset_account", None, self.name, None, self.asset_category, self.company
+		)
 		if not fixed_asset_account:
 			frappe.throw(
 				_("Set {0} in asset category {1} for company {2}").format(
@@ -642,7 +779,9 @@
 	def get_cwip_account(self, cwip_enabled=False):
 		cwip_account = None
 		try:
-			cwip_account = get_asset_account("capital_work_in_progress_account", self.name, self.asset_category, self.company)
+			cwip_account = get_asset_account(
+				"capital_work_in_progress_account", self.name, self.asset_category, self.company
+			)
 		except Exception:
 			# if no cwip account found in category or company and "cwip is enabled" then raise else silently pass
 			if cwip_enabled:
@@ -656,33 +795,45 @@
 		purchase_document = self.get_purchase_document()
 		fixed_asset_account, cwip_account = self.get_fixed_asset_account(), self.get_cwip_account()
 
-		if (purchase_document and self.purchase_receipt_amount and self.available_for_use_date <= nowdate()):
+		if (
+			purchase_document and self.purchase_receipt_amount and self.available_for_use_date <= nowdate()
+		):
 
-			gl_entries.append(self.get_gl_dict({
-				"account": cwip_account,
-				"against": fixed_asset_account,
-				"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
-				"posting_date": self.available_for_use_date,
-				"credit": self.purchase_receipt_amount,
-				"credit_in_account_currency": self.purchase_receipt_amount,
-				"cost_center": self.cost_center
-			}, item=self))
+			gl_entries.append(
+				self.get_gl_dict(
+					{
+						"account": cwip_account,
+						"against": fixed_asset_account,
+						"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
+						"posting_date": self.available_for_use_date,
+						"credit": self.purchase_receipt_amount,
+						"credit_in_account_currency": self.purchase_receipt_amount,
+						"cost_center": self.cost_center,
+					},
+					item=self,
+				)
+			)
 
-			gl_entries.append(self.get_gl_dict({
-				"account": fixed_asset_account,
-				"against": cwip_account,
-				"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
-				"posting_date": self.available_for_use_date,
-				"debit": self.purchase_receipt_amount,
-				"debit_in_account_currency": self.purchase_receipt_amount,
-				"cost_center": self.cost_center
-			}, item=self))
+			gl_entries.append(
+				self.get_gl_dict(
+					{
+						"account": fixed_asset_account,
+						"against": cwip_account,
+						"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
+						"posting_date": self.available_for_use_date,
+						"debit": self.purchase_receipt_amount,
+						"debit_in_account_currency": self.purchase_receipt_amount,
+						"cost_center": self.cost_center,
+					},
+					item=self,
+				)
+			)
 
 		if gl_entries:
 			from erpnext.accounts.general_ledger import make_gl_entries
 
 			make_gl_entries(gl_entries)
-			self.db_set('booked_fixed_asset', 1)
+			self.db_set("booked_fixed_asset", 1)
 
 	@frappe.whitelist()
 	def get_depreciation_rate(self, args, on_validate=False):
@@ -691,18 +842,21 @@
 
 		float_precision = cint(frappe.db.get_default("float_precision")) or 2
 
-		if args.get("depreciation_method") == 'Double Declining Balance':
+		if args.get("depreciation_method") == "Double Declining Balance":
 			return 200.0 / args.get("total_number_of_depreciations")
 
 		if args.get("depreciation_method") == "Written Down Value":
 			if args.get("rate_of_depreciation") and on_validate:
 				return args.get("rate_of_depreciation")
 
-			no_of_years = flt(args.get("total_number_of_depreciations") * flt(args.get("frequency_of_depreciation"))) / 12
+			no_of_years = (
+				flt(args.get("total_number_of_depreciations") * flt(args.get("frequency_of_depreciation")))
+				/ 12
+			)
 			value = flt(args.get("expected_value_after_useful_life")) / flt(self.gross_purchase_amount)
 
 			# square root of flt(salvage_value) / flt(asset_cost)
-			depreciation_rate = math.pow(value, 1.0/flt(no_of_years, 2))
+			depreciation_rate = math.pow(value, 1.0 / flt(no_of_years, 2))
 
 			return 100 * (1 - flt(depreciation_rate, float_precision))
 
@@ -713,93 +867,104 @@
 
 		return (depreciation_amount * flt(days)) / flt(total_days), days, months
 
+
 def update_maintenance_status():
-	assets = frappe.get_all(
-		"Asset", filters={"docstatus": 1, "maintenance_required": 1}
-	)
+	assets = frappe.get_all("Asset", filters={"docstatus": 1, "maintenance_required": 1})
 
 	for asset in assets:
 		asset = frappe.get_doc("Asset", asset.name)
 		if frappe.db.exists("Asset Repair", {"asset_name": asset.name, "repair_status": "Pending"}):
 			asset.set_status("Out of Order")
-		elif frappe.db.exists("Asset Maintenance Task", {"parent": asset.name, "next_due_date": today()}):
+		elif frappe.db.exists(
+			"Asset Maintenance Task", {"parent": asset.name, "next_due_date": today()}
+		):
 			asset.set_status("In Maintenance")
 		else:
 			asset.set_status()
 
+
 def make_post_gl_entry():
 
-	asset_categories = frappe.db.get_all('Asset Category', fields = ['name', 'enable_cwip_accounting'])
+	asset_categories = frappe.db.get_all("Asset Category", fields=["name", "enable_cwip_accounting"])
 
 	for asset_category in asset_categories:
 		if cint(asset_category.enable_cwip_accounting):
-			assets = frappe.db.sql_list(""" select name from `tabAsset`
+			assets = frappe.db.sql_list(
+				""" select name from `tabAsset`
 				where asset_category = %s and ifnull(booked_fixed_asset, 0) = 0
-				and available_for_use_date = %s""", (asset_category.name, nowdate()))
+				and available_for_use_date = %s""",
+				(asset_category.name, nowdate()),
+			)
 
 			for asset in assets:
-				doc = frappe.get_doc('Asset', asset)
+				doc = frappe.get_doc("Asset", asset)
 				doc.make_gl_entries()
 
+
 def get_asset_naming_series():
-	meta = frappe.get_meta('Asset')
+	meta = frappe.get_meta("Asset")
 	return meta.get_field("naming_series").options
 
+
 @frappe.whitelist()
 def make_sales_invoice(asset, item_code, company, serial_no=None):
 	si = frappe.new_doc("Sales Invoice")
 	si.company = company
-	si.currency = frappe.get_cached_value('Company',  company,  "default_currency")
+	si.currency = frappe.get_cached_value("Company", company, "default_currency")
 	disposal_account, depreciation_cost_center = get_disposal_account_and_cost_center(company)
-	si.append("items", {
-		"item_code": item_code,
-		"is_fixed_asset": 1,
-		"asset": asset,
-		"income_account": disposal_account,
-		"serial_no": serial_no,
-		"cost_center": depreciation_cost_center,
-		"qty": 1
-	})
+	si.append(
+		"items",
+		{
+			"item_code": item_code,
+			"is_fixed_asset": 1,
+			"asset": asset,
+			"income_account": disposal_account,
+			"serial_no": serial_no,
+			"cost_center": depreciation_cost_center,
+			"qty": 1,
+		},
+	)
 	si.set_missing_values()
 	return si
 
+
 @frappe.whitelist()
 def create_asset_maintenance(asset, item_code, item_name, asset_category, company):
 	asset_maintenance = frappe.new_doc("Asset Maintenance")
-	asset_maintenance.update({
-		"asset_name": asset,
-		"company": company,
-		"item_code": item_code,
-		"item_name": item_name,
-		"asset_category": asset_category
-	})
+	asset_maintenance.update(
+		{
+			"asset_name": asset,
+			"company": company,
+			"item_code": item_code,
+			"item_name": item_name,
+			"asset_category": asset_category,
+		}
+	)
 	return asset_maintenance
 
+
 @frappe.whitelist()
 def create_asset_repair(asset, asset_name):
 	asset_repair = frappe.new_doc("Asset Repair")
-	asset_repair.update({
-		"asset": asset,
-		"asset_name": asset_name
-	})
+	asset_repair.update({"asset": asset, "asset_name": asset_name})
 	return asset_repair
 
+
 @frappe.whitelist()
 def create_asset_value_adjustment(asset, asset_category, company):
 	asset_value_adjustment = frappe.new_doc("Asset Value Adjustment")
-	asset_value_adjustment.update({
-		"asset": asset,
-		"company": company,
-		"asset_category": asset_category
-	})
+	asset_value_adjustment.update(
+		{"asset": asset, "company": company, "asset_category": asset_category}
+	)
 	return asset_value_adjustment
 
+
 @frappe.whitelist()
 def transfer_asset(args):
 	args = json.loads(args)
 
-	if args.get('serial_no'):
-		args['quantity'] = len(args.get('serial_no').split('\n'))
+	if args.get("serial_no"):
+		args["quantity"] = len(args.get("serial_no").split("\n"))
 
 	movement_entry = frappe.new_doc("Asset Movement")
 	movement_entry.update(args)
@@ -808,52 +973,73 @@
 
 	frappe.db.commit()
 
-	frappe.msgprint(_("Asset Movement record {0} created").format("<a href='/app/Form/Asset Movement/{0}'>{0}</a>").format(movement_entry.name))
+	frappe.msgprint(
+		_("Asset Movement record {0} created")
+		.format("<a href='/app/Form/Asset Movement/{0}'>{0}</a>")
+		.format(movement_entry.name)
+	)
+
 
 @frappe.whitelist()
 def get_item_details(item_code, asset_category):
-	asset_category_doc = frappe.get_doc('Asset Category', asset_category)
+	asset_category_doc = frappe.get_doc("Asset Category", asset_category)
 	books = []
 	for d in asset_category_doc.finance_books:
-		books.append({
-			'finance_book': d.finance_book,
-			'depreciation_method': d.depreciation_method,
-			'total_number_of_depreciations': d.total_number_of_depreciations,
-			'frequency_of_depreciation': d.frequency_of_depreciation,
-			'start_date': nowdate()
-		})
+		books.append(
+			{
+				"finance_book": d.finance_book,
+				"depreciation_method": d.depreciation_method,
+				"total_number_of_depreciations": d.total_number_of_depreciations,
+				"frequency_of_depreciation": d.frequency_of_depreciation,
+				"start_date": nowdate(),
+			}
+		)
 
 	return books
 
+
 def get_asset_account(account_name, asset=None, asset_category=None, company=None):
 	account = None
 	if asset:
-		account = get_asset_category_account(account_name, asset=asset,
-				asset_category = asset_category, company = company)
+		account = get_asset_category_account(
+			account_name, asset=asset, asset_category=asset_category, company=company
+		)
 
 	if not asset and not account:
-		account = get_asset_category_account(account_name, asset_category = asset_category, company = company)
+		account = get_asset_category_account(
+			account_name, asset_category=asset_category, company=company
+		)
 
 	if not account:
-		account = frappe.get_cached_value('Company',  company,  account_name)
+		account = frappe.get_cached_value("Company", company, account_name)
 
 	if not account:
 		if not asset_category:
-			frappe.throw(_("Set {0} in company {1}").format(account_name.replace('_', ' ').title(), company))
+			frappe.throw(
+				_("Set {0} in company {1}").format(account_name.replace("_", " ").title(), company)
+			)
 		else:
-			frappe.throw(_("Set {0} in asset category {1} or company {2}")
-				.format(account_name.replace('_', ' ').title(), asset_category, company))
+			frappe.throw(
+				_("Set {0} in asset category {1} or company {2}").format(
+					account_name.replace("_", " ").title(), asset_category, company
+				)
+			)
 
 	return account
 
+
 @frappe.whitelist()
 def make_journal_entry(asset_name):
 	asset = frappe.get_doc("Asset", asset_name)
-	fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \
-		get_depreciation_accounts(asset)
+	(
+		fixed_asset_account,
+		accumulated_depreciation_account,
+		depreciation_expense_account,
+	) = get_depreciation_accounts(asset)
 
-	depreciation_cost_center, depreciation_series = frappe.db.get_value("Company", asset.company,
-		["depreciation_cost_center", "series_for_depreciation_entry"])
+	depreciation_cost_center, depreciation_series = frappe.db.get_value(
+		"Company", asset.company, ["depreciation_cost_center", "series_for_depreciation_entry"]
+	)
 	depreciation_cost_center = asset.cost_center or depreciation_cost_center
 
 	je = frappe.new_doc("Journal Entry")
@@ -862,21 +1048,28 @@
 	je.company = asset.company
 	je.remark = "Depreciation Entry against asset {0}".format(asset_name)
 
-	je.append("accounts", {
-		"account": depreciation_expense_account,
-		"reference_type": "Asset",
-		"reference_name": asset.name,
-		"cost_center": depreciation_cost_center
-	})
+	je.append(
+		"accounts",
+		{
+			"account": depreciation_expense_account,
+			"reference_type": "Asset",
+			"reference_name": asset.name,
+			"cost_center": depreciation_cost_center,
+		},
+	)
 
-	je.append("accounts", {
-		"account": accumulated_depreciation_account,
-		"reference_type": "Asset",
-		"reference_name": asset.name
-	})
+	je.append(
+		"accounts",
+		{
+			"account": accumulated_depreciation_account,
+			"reference_type": "Asset",
+			"reference_name": asset.name,
+		},
+	)
 
 	return je
 
+
 @frappe.whitelist()
 def make_asset_movement(assets, purpose=None):
 	import json
@@ -885,48 +1078,56 @@
 		assets = json.loads(assets)
 
 	if len(assets) == 0:
-		frappe.throw(_('Atleast one asset has to be selected.'))
+		frappe.throw(_("Atleast one asset has to be selected."))
 
 	asset_movement = frappe.new_doc("Asset Movement")
 	asset_movement.quantity = len(assets)
 	for asset in assets:
-		asset = frappe.get_doc('Asset', asset.get('name'))
-		asset_movement.company = asset.get('company')
-		asset_movement.append("assets", {
-			'asset': asset.get('name'),
-			'source_location': asset.get('location'),
-			'from_employee': asset.get('custodian')
-		})
+		asset = frappe.get_doc("Asset", asset.get("name"))
+		asset_movement.company = asset.get("company")
+		asset_movement.append(
+			"assets",
+			{
+				"asset": asset.get("name"),
+				"source_location": asset.get("location"),
+				"from_employee": asset.get("custodian"),
+			},
+		)
 
-	if asset_movement.get('assets'):
+	if asset_movement.get("assets"):
 		return asset_movement.as_dict()
 
+
 def is_cwip_accounting_enabled(asset_category):
 	return cint(frappe.db.get_value("Asset Category", asset_category, "enable_cwip_accounting"))
 
+
 def get_total_days(date, frequency):
-	period_start_date = add_months(date,
-		cint(frequency) * -1)
+	period_start_date = add_months(date, cint(frequency) * -1)
 
 	return date_diff(date, period_start_date)
 
+
 @erpnext.allow_regional
 def get_depreciation_amount(asset, depreciable_value, row):
 	if row.depreciation_method in ("Straight Line", "Manual"):
 		# if the Depreciation Schedule is being prepared for the first time
 		if not asset.flags.increase_in_asset_life:
-			depreciation_amount = (flt(asset.gross_purchase_amount) -
-				flt(row.expected_value_after_useful_life)) / flt(row.total_number_of_depreciations)
+			depreciation_amount = (
+				flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)
+			) / flt(row.total_number_of_depreciations)
 
 		# if the Depreciation Schedule is being modified after Asset Repair
 		else:
-			depreciation_amount = (flt(row.value_after_depreciation) -
-				flt(row.expected_value_after_useful_life)) / (date_diff(asset.to_date, asset.available_for_use_date) / 365)
+			depreciation_amount = (
+				flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)
+			) / (date_diff(asset.to_date, asset.available_for_use_date) / 365)
 	else:
 		depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100))
 
 	return depreciation_amount
 
+
 @frappe.whitelist()
 def split_asset(asset_name, split_qty):
 	asset = frappe.get_doc("Asset", asset_name)
@@ -942,34 +1143,61 @@
 
 	return new_asset
 
+
 def update_existing_asset(asset, remaining_qty):
-	remaining_gross_purchase_amount = flt((asset.gross_purchase_amount * remaining_qty) / asset.asset_quantity)
-	opening_accumulated_depreciation = flt((asset.opening_accumulated_depreciation * remaining_qty) / asset.asset_quantity)
+	remaining_gross_purchase_amount = flt(
+		(asset.gross_purchase_amount * remaining_qty) / asset.asset_quantity
+	)
+	opening_accumulated_depreciation = flt(
+		(asset.opening_accumulated_depreciation * remaining_qty) / asset.asset_quantity
+	)
 
-	frappe.db.set_value("Asset", asset.name, {
-		'opening_accumulated_depreciation': opening_accumulated_depreciation,
-		'gross_purchase_amount': remaining_gross_purchase_amount,
-		'asset_quantity': remaining_qty
-	})
+	frappe.db.set_value(
+		"Asset",
+		asset.name,
+		{
+			"opening_accumulated_depreciation": opening_accumulated_depreciation,
+			"gross_purchase_amount": remaining_gross_purchase_amount,
+			"asset_quantity": remaining_qty,
+		},
+	)
 
-	for finance_book in asset.get('finance_books'):
-		value_after_depreciation = flt((finance_book.value_after_depreciation * remaining_qty)/asset.asset_quantity)
-		expected_value_after_useful_life = flt((finance_book.expected_value_after_useful_life * remaining_qty)/asset.asset_quantity)
-		frappe.db.set_value('Asset Finance Book', finance_book.name, 'value_after_depreciation', value_after_depreciation)
-		frappe.db.set_value('Asset Finance Book', finance_book.name, 'expected_value_after_useful_life', expected_value_after_useful_life)
+	for finance_book in asset.get("finance_books"):
+		value_after_depreciation = flt(
+			(finance_book.value_after_depreciation * remaining_qty) / asset.asset_quantity
+		)
+		expected_value_after_useful_life = flt(
+			(finance_book.expected_value_after_useful_life * remaining_qty) / asset.asset_quantity
+		)
+		frappe.db.set_value(
+			"Asset Finance Book", finance_book.name, "value_after_depreciation", value_after_depreciation
+		)
+		frappe.db.set_value(
+			"Asset Finance Book",
+			finance_book.name,
+			"expected_value_after_useful_life",
+			expected_value_after_useful_life,
+		)
 
 	accumulated_depreciation = 0
 
-	for term in asset.get('schedules'):
-		depreciation_amount = flt((term.depreciation_amount * remaining_qty)/asset.asset_quantity)
-		frappe.db.set_value('Depreciation Schedule', term.name, 'depreciation_amount', depreciation_amount)
+	for term in asset.get("schedules"):
+		depreciation_amount = flt((term.depreciation_amount * remaining_qty) / asset.asset_quantity)
+		frappe.db.set_value(
+			"Depreciation Schedule", term.name, "depreciation_amount", depreciation_amount
+		)
 		accumulated_depreciation += depreciation_amount
-		frappe.db.set_value('Depreciation Schedule', term.name, 'accumulated_depreciation_amount', accumulated_depreciation)
+		frappe.db.set_value(
+			"Depreciation Schedule", term.name, "accumulated_depreciation_amount", accumulated_depreciation
+		)
+
 
 def create_new_asset_after_split(asset, split_qty):
 	new_asset = frappe.copy_doc(asset)
 	new_gross_purchase_amount = flt((asset.gross_purchase_amount * split_qty) / asset.asset_quantity)
-	opening_accumulated_depreciation = flt((asset.opening_accumulated_depreciation * split_qty) / asset.asset_quantity)
+	opening_accumulated_depreciation = flt(
+		(asset.opening_accumulated_depreciation * split_qty) / asset.asset_quantity
+	)
 
 	new_asset.gross_purchase_amount = new_gross_purchase_amount
 	new_asset.opening_accumulated_depreciation = opening_accumulated_depreciation
@@ -977,12 +1205,16 @@
 	new_asset.split_from = asset.name
 	accumulated_depreciation = 0
 
-	for finance_book in new_asset.get('finance_books'):
-		finance_book.value_after_depreciation = flt((finance_book.value_after_depreciation * split_qty)/asset.asset_quantity)
-		finance_book.expected_value_after_useful_life = flt((finance_book.expected_value_after_useful_life * split_qty)/asset.asset_quantity)
+	for finance_book in new_asset.get("finance_books"):
+		finance_book.value_after_depreciation = flt(
+			(finance_book.value_after_depreciation * split_qty) / asset.asset_quantity
+		)
+		finance_book.expected_value_after_useful_life = flt(
+			(finance_book.expected_value_after_useful_life * split_qty) / asset.asset_quantity
+		)
 
-	for term in new_asset.get('schedules'):
-		depreciation_amount = flt((term.depreciation_amount * split_qty)/asset.asset_quantity)
+	for term in new_asset.get("schedules"):
+		depreciation_amount = flt((term.depreciation_amount * split_qty) / asset.asset_quantity)
 		term.depreciation_amount = depreciation_amount
 		accumulated_depreciation += depreciation_amount
 		term.accumulated_depreciation_amount = accumulated_depreciation
@@ -990,29 +1222,34 @@
 	new_asset.submit()
 	new_asset.set_status()
 
-	for term in new_asset.get('schedules'):
+	for term in new_asset.get("schedules"):
 		# Update references in JV
 		if term.journal_entry:
-			add_reference_in_jv_on_split(term.journal_entry, new_asset.name, asset.name, term.depreciation_amount)
+			add_reference_in_jv_on_split(
+				term.journal_entry, new_asset.name, asset.name, term.depreciation_amount
+			)
 
 	return new_asset
 
-def add_reference_in_jv_on_split(entry_name, new_asset_name, old_asset_name, depreciation_amount):
-	journal_entry = frappe.get_doc('Journal Entry', entry_name)
-	entries_to_add = []
-	idx = len(journal_entry.get('accounts')) + 1
 
-	for account in journal_entry.get('accounts'):
+def add_reference_in_jv_on_split(entry_name, new_asset_name, old_asset_name, depreciation_amount):
+	journal_entry = frappe.get_doc("Journal Entry", entry_name)
+	entries_to_add = []
+	idx = len(journal_entry.get("accounts")) + 1
+
+	for account in journal_entry.get("accounts"):
 		if account.reference_name == old_asset_name:
 			entries_to_add.append(frappe.copy_doc(account).as_dict())
 			if account.credit:
 				account.credit = account.credit - depreciation_amount
-				account.credit_in_account_currency = account.credit_in_account_currency - \
-					account.exchange_rate * depreciation_amount
+				account.credit_in_account_currency = (
+					account.credit_in_account_currency - account.exchange_rate * depreciation_amount
+				)
 			elif account.debit:
 				account.debit = account.debit - depreciation_amount
-				account.debit_in_account_currency = account.debit_in_account_currency - \
-					account.exchange_rate * depreciation_amount
+				account.debit_in_account_currency = (
+					account.debit_in_account_currency - account.exchange_rate * depreciation_amount
+				)
 
 	for entry in entries_to_add:
 		entry.reference_name = new_asset_name
@@ -1026,7 +1263,7 @@
 		entry.idx = idx
 		idx += 1
 
-		journal_entry.append('accounts', entry)
+		journal_entry.append("accounts", entry)
 
 	journal_entry.flags.ignore_validate_update_after_submit = True
 	journal_entry.save()
@@ -1035,4 +1272,4 @@
 	journal_entry.docstatus = 2
 	journal_entry.make_gl_entries(1)
 	journal_entry.docstatus = 1
-	journal_entry.make_gl_entries()
\ No newline at end of file
+	journal_entry.make_gl_entries()
diff --git a/erpnext/assets/doctype/asset/asset_dashboard.py b/erpnext/assets/doctype/asset/asset_dashboard.py
index 00d0847..9a45bcb 100644
--- a/erpnext/assets/doctype/asset/asset_dashboard.py
+++ b/erpnext/assets/doctype/asset/asset_dashboard.py
@@ -1,12 +1,8 @@
+from frappe import _
+
+
 def get_data():
 	return {
-		'non_standard_fieldnames': {
-			'Asset Movement': 'asset'
-		},
-		'transactions': [
-			{
-				'label': ['Movement'],
-				'items': ['Asset Movement']
-			}
-		]
+		"non_standard_fieldnames": {"Asset Movement": "asset"},
+		"transactions": [{"label": _("Movement"), "items": ["Asset Movement"]}],
 	}
diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py
index 874fb63..3f7e945 100644
--- a/erpnext/assets/doctype/asset/depreciation.py
+++ b/erpnext/assets/doctype/asset/depreciation.py
@@ -13,7 +13,9 @@
 
 def post_depreciation_entries(date=None):
 	# Return if automatic booking of asset depreciation is disabled
-	if not cint(frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically")):
+	if not cint(
+		frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically")
+	):
 		return
 
 	if not date:
@@ -22,26 +24,35 @@
 		make_depreciation_entry(asset, date)
 		frappe.db.commit()
 
+
 def get_depreciable_assets(date):
-	return frappe.db.sql_list("""select a.name
+	return frappe.db.sql_list(
+		"""select distinct a.name
 		from tabAsset a, `tabDepreciation Schedule` ds
 		where a.name = ds.parent and a.docstatus=1 and ds.schedule_date<=%s and a.calculate_depreciation = 1
 			and a.status in ('Submitted', 'Partially Depreciated')
-			and ifnull(ds.journal_entry, '')=''""", date)
+			and ifnull(ds.journal_entry, '')=''""",
+		date,
+	)
+
 
 @frappe.whitelist()
 def make_depreciation_entry(asset_name, date=None):
-	frappe.has_permission('Journal Entry', throw=True)
+	frappe.has_permission("Journal Entry", throw=True)
 
 	if not date:
 		date = today()
 
 	asset = frappe.get_doc("Asset", asset_name)
-	fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \
-		get_depreciation_accounts(asset)
+	(
+		fixed_asset_account,
+		accumulated_depreciation_account,
+		depreciation_expense_account,
+	) = get_depreciation_accounts(asset)
 
-	depreciation_cost_center, depreciation_series = frappe.get_cached_value('Company',  asset.company,
-		["depreciation_cost_center", "series_for_depreciation_entry"])
+	depreciation_cost_center, depreciation_series = frappe.get_cached_value(
+		"Company", asset.company, ["depreciation_cost_center", "series_for_depreciation_entry"]
+	)
 
 	depreciation_cost_center = asset.cost_center or depreciation_cost_center
 
@@ -57,14 +68,16 @@
 			je.finance_book = d.finance_book
 			je.remark = "Depreciation Entry against {0} worth {1}".format(asset_name, d.depreciation_amount)
 
-			credit_account, debit_account = get_credit_and_debit_accounts(accumulated_depreciation_account, depreciation_expense_account)
+			credit_account, debit_account = get_credit_and_debit_accounts(
+				accumulated_depreciation_account, depreciation_expense_account
+			)
 
 			credit_entry = {
 				"account": credit_account,
 				"credit_in_account_currency": d.depreciation_amount,
 				"reference_type": "Asset",
 				"reference_name": asset.name,
-				"cost_center": depreciation_cost_center
+				"cost_center": depreciation_cost_center,
 			}
 
 			debit_entry = {
@@ -72,19 +85,25 @@
 				"debit_in_account_currency": d.depreciation_amount,
 				"reference_type": "Asset",
 				"reference_name": asset.name,
-				"cost_center": depreciation_cost_center
+				"cost_center": depreciation_cost_center,
 			}
 
 			for dimension in accounting_dimensions:
-				if (asset.get(dimension['fieldname']) or dimension.get('mandatory_for_bs')):
-					credit_entry.update({
-						dimension['fieldname']: asset.get(dimension['fieldname']) or dimension.get('default_dimension')
-					})
+				if asset.get(dimension["fieldname"]) or dimension.get("mandatory_for_bs"):
+					credit_entry.update(
+						{
+							dimension["fieldname"]: asset.get(dimension["fieldname"])
+							or dimension.get("default_dimension")
+						}
+					)
 
-				if (asset.get(dimension['fieldname']) or dimension.get('mandatory_for_pl')):
-					debit_entry.update({
-						dimension['fieldname']: asset.get(dimension['fieldname']) or dimension.get('default_dimension')
-					})
+				if asset.get(dimension["fieldname"]) or dimension.get("mandatory_for_pl"):
+					debit_entry.update(
+						{
+							dimension["fieldname"]: asset.get(dimension["fieldname"])
+							or dimension.get("default_dimension")
+						}
+					)
 
 			je.append("accounts", credit_entry)
 
@@ -98,7 +117,7 @@
 			d.db_set("journal_entry", je.name)
 
 			idx = cint(d.finance_book_id)
-			finance_books = asset.get('finance_books')[idx - 1]
+			finance_books = asset.get("finance_books")[idx - 1]
 			finance_books.value_after_depreciation -= d.depreciation_amount
 			finance_books.db_update()
 
@@ -106,13 +125,20 @@
 
 	return asset
 
+
 def get_depreciation_accounts(asset):
 	fixed_asset_account = accumulated_depreciation_account = depreciation_expense_account = None
 
-	accounts = frappe.db.get_value("Asset Category Account",
-		filters={'parent': asset.asset_category, 'company_name': asset.company},
-		fieldname = ['fixed_asset_account', 'accumulated_depreciation_account',
-			'depreciation_expense_account'], as_dict=1)
+	accounts = frappe.db.get_value(
+		"Asset Category Account",
+		filters={"parent": asset.asset_category, "company_name": asset.company},
+		fieldname=[
+			"fixed_asset_account",
+			"accumulated_depreciation_account",
+			"depreciation_expense_account",
+		],
+		as_dict=1,
+	)
 
 	if accounts:
 		fixed_asset_account = accounts.fixed_asset_account
@@ -120,20 +146,29 @@
 		depreciation_expense_account = accounts.depreciation_expense_account
 
 	if not accumulated_depreciation_account or not depreciation_expense_account:
-		accounts = frappe.get_cached_value('Company',  asset.company,
-			["accumulated_depreciation_account", "depreciation_expense_account"])
+		accounts = frappe.get_cached_value(
+			"Company", asset.company, ["accumulated_depreciation_account", "depreciation_expense_account"]
+		)
 
 		if not accumulated_depreciation_account:
 			accumulated_depreciation_account = accounts[0]
 		if not depreciation_expense_account:
 			depreciation_expense_account = accounts[1]
 
-	if not fixed_asset_account or not accumulated_depreciation_account or not depreciation_expense_account:
-		frappe.throw(_("Please set Depreciation related Accounts in Asset Category {0} or Company {1}")
-			.format(asset.asset_category, asset.company))
+	if (
+		not fixed_asset_account
+		or not accumulated_depreciation_account
+		or not depreciation_expense_account
+	):
+		frappe.throw(
+			_("Please set Depreciation related Accounts in Asset Category {0} or Company {1}").format(
+				asset.asset_category, asset.company
+			)
+		)
 
 	return fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account
 
+
 def get_credit_and_debit_accounts(accumulated_depreciation_account, depreciation_expense_account):
 	root_type = frappe.get_value("Account", depreciation_expense_account, "root_type")
 
@@ -148,6 +183,7 @@
 
 	return credit_account, debit_account
 
+
 @frappe.whitelist()
 def scrap_asset(asset_name):
 	asset = frappe.get_doc("Asset", asset_name)
@@ -155,9 +191,13 @@
 	if asset.docstatus != 1:
 		frappe.throw(_("Asset {0} must be submitted").format(asset.name))
 	elif asset.status in ("Cancelled", "Sold", "Scrapped"):
-		frappe.throw(_("Asset {0} cannot be scrapped, as it is already {1}").format(asset.name, asset.status))
+		frappe.throw(
+			_("Asset {0} cannot be scrapped, as it is already {1}").format(asset.name, asset.status)
+		)
 
-	depreciation_series = frappe.get_cached_value('Company',  asset.company,  "series_for_depreciation_entry")
+	depreciation_series = frappe.get_cached_value(
+		"Company", asset.company, "series_for_depreciation_entry"
+	)
 
 	je = frappe.new_doc("Journal Entry")
 	je.voucher_type = "Journal Entry"
@@ -167,10 +207,7 @@
 	je.remark = "Scrap Entry for asset {0}".format(asset_name)
 
 	for entry in get_gl_entries_on_asset_disposal(asset):
-		entry.update({
-			"reference_type": "Asset",
-			"reference_name": asset_name
-		})
+		entry.update({"reference_type": "Asset", "reference_name": asset_name})
 		je.append("accounts", entry)
 
 	je.flags.ignore_permissions = True
@@ -182,6 +219,7 @@
 
 	frappe.msgprint(_("Asset scrapped via Journal Entry {0}").format(je.name))
 
+
 @frappe.whitelist()
 def restore_asset(asset_name):
 	asset = frappe.get_doc("Asset", asset_name)
@@ -195,23 +233,31 @@
 
 	asset.set_status()
 
+
 def get_gl_entries_on_asset_regain(asset, selling_amount=0, finance_book=None):
-	fixed_asset_account, asset, depreciation_cost_center, accumulated_depr_account, accumulated_depr_amount, disposal_account, value_after_depreciation = \
-		get_asset_details(asset, finance_book)
+	(
+		fixed_asset_account,
+		asset,
+		depreciation_cost_center,
+		accumulated_depr_account,
+		accumulated_depr_amount,
+		disposal_account,
+		value_after_depreciation,
+	) = get_asset_details(asset, finance_book)
 
 	gl_entries = [
 		{
 			"account": fixed_asset_account,
 			"debit_in_account_currency": asset.gross_purchase_amount,
 			"debit": asset.gross_purchase_amount,
-			"cost_center": depreciation_cost_center
+			"cost_center": depreciation_cost_center,
 		},
 		{
 			"account": accumulated_depr_account,
 			"credit_in_account_currency": accumulated_depr_amount,
 			"credit": accumulated_depr_amount,
-			"cost_center": depreciation_cost_center
-		}
+			"cost_center": depreciation_cost_center,
+		},
 	]
 
 	profit_amount = abs(flt(value_after_depreciation)) - abs(flt(selling_amount))
@@ -220,23 +266,31 @@
 
 	return gl_entries
 
+
 def get_gl_entries_on_asset_disposal(asset, selling_amount=0, finance_book=None):
-	fixed_asset_account, asset, depreciation_cost_center, accumulated_depr_account, accumulated_depr_amount, disposal_account, value_after_depreciation = \
-		get_asset_details(asset, finance_book)
+	(
+		fixed_asset_account,
+		asset,
+		depreciation_cost_center,
+		accumulated_depr_account,
+		accumulated_depr_amount,
+		disposal_account,
+		value_after_depreciation,
+	) = get_asset_details(asset, finance_book)
 
 	gl_entries = [
 		{
 			"account": fixed_asset_account,
 			"credit_in_account_currency": asset.gross_purchase_amount,
 			"credit": asset.gross_purchase_amount,
-			"cost_center": depreciation_cost_center
+			"cost_center": depreciation_cost_center,
 		},
 		{
 			"account": accumulated_depr_account,
 			"debit_in_account_currency": accumulated_depr_amount,
 			"debit": accumulated_depr_amount,
-			"cost_center": depreciation_cost_center
-		}
+			"cost_center": depreciation_cost_center,
+		},
 	]
 
 	profit_amount = flt(selling_amount) - flt(value_after_depreciation)
@@ -245,8 +299,11 @@
 
 	return gl_entries
 
+
 def get_asset_details(asset, finance_book=None):
-	fixed_asset_account, accumulated_depr_account, depr_expense_account = get_depreciation_accounts(asset)
+	fixed_asset_account, accumulated_depr_account, depr_expense_account = get_depreciation_accounts(
+		asset
+	)
 	disposal_account, depreciation_cost_center = get_disposal_account_and_cost_center(asset.company)
 	depreciation_cost_center = asset.cost_center or depreciation_cost_center
 
@@ -257,28 +314,46 @@
 				idx = d.idx
 				break
 
-	value_after_depreciation = (asset.finance_books[idx - 1].value_after_depreciation
-		if asset.calculate_depreciation else asset.value_after_depreciation)
+	value_after_depreciation = (
+		asset.finance_books[idx - 1].value_after_depreciation
+		if asset.calculate_depreciation
+		else asset.value_after_depreciation
+	)
 	accumulated_depr_amount = flt(asset.gross_purchase_amount) - flt(value_after_depreciation)
 
-	return fixed_asset_account, asset, depreciation_cost_center, accumulated_depr_account, accumulated_depr_amount, disposal_account, value_after_depreciation
+	return (
+		fixed_asset_account,
+		asset,
+		depreciation_cost_center,
+		accumulated_depr_account,
+		accumulated_depr_amount,
+		disposal_account,
+		value_after_depreciation,
+	)
+
 
 def get_profit_gl_entries(profit_amount, gl_entries, disposal_account, depreciation_cost_center):
 	debit_or_credit = "debit" if profit_amount < 0 else "credit"
-	gl_entries.append({
-		"account": disposal_account,
-		"cost_center": depreciation_cost_center,
-		debit_or_credit: abs(profit_amount),
-		debit_or_credit + "_in_account_currency": abs(profit_amount)
-	})
+	gl_entries.append(
+		{
+			"account": disposal_account,
+			"cost_center": depreciation_cost_center,
+			debit_or_credit: abs(profit_amount),
+			debit_or_credit + "_in_account_currency": abs(profit_amount),
+		}
+	)
+
 
 @frappe.whitelist()
 def get_disposal_account_and_cost_center(company):
-	disposal_account, depreciation_cost_center = frappe.get_cached_value('Company',  company,
-		["disposal_account", "depreciation_cost_center"])
+	disposal_account, depreciation_cost_center = frappe.get_cached_value(
+		"Company", company, ["disposal_account", "depreciation_cost_center"]
+	)
 
 	if not disposal_account:
-		frappe.throw(_("Please set 'Gain/Loss Account on Asset Disposal' in Company {0}").format(company))
+		frappe.throw(
+			_("Please set 'Gain/Loss Account on Asset Disposal' in Company {0}").format(company)
+		)
 	if not depreciation_cost_center:
 		frappe.throw(_("Please set 'Asset Depreciation Cost Center' in Company {0}").format(company))
 
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index f5486dc..a57a28e 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -32,6 +32,7 @@
 	def tearDownClass(cls):
 		frappe.db.rollback()
 
+
 class TestAsset(AssetSetup):
 	def test_asset_category_is_fetched(self):
 		"""Tests if the Item's Asset Category value is assigned to the Asset, if the field is empty."""
@@ -52,7 +53,7 @@
 		"""Tests if either PI or PR is present if CWIP is enabled and is_existing_asset=0."""
 
 		asset = create_asset(item_code="Macbook Pro", do_not_save=1)
-		asset.is_existing_asset=0
+		asset.is_existing_asset = 0
 
 		self.assertRaises(frappe.ValidationError, asset.save)
 
@@ -67,7 +68,7 @@
 	def test_item_exists(self):
 		asset = create_asset(item_code="MacBook", do_not_save=1)
 
-		self.assertRaises(frappe.DoesNotExistError, asset.save)
+		self.assertRaises(frappe.ValidationError, asset.save)
 
 	def test_validate_item(self):
 		asset = create_asset(item_code="MacBook Pro", do_not_save=1)
@@ -86,11 +87,12 @@
 		self.assertRaises(frappe.ValidationError, asset.save)
 
 	def test_purchase_asset(self):
-		pr = make_purchase_receipt(item_code="Macbook Pro",
-			qty=1, rate=100000.0, location="Test Location")
+		pr = make_purchase_receipt(
+			item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location"
+		)
 
-		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
-		asset = frappe.get_doc('Asset', asset_name)
+		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name")
+		asset = frappe.get_doc("Asset", asset_name)
 		asset.calculate_depreciation = 1
 
 		month_end_date = get_last_day(nowdate())
@@ -98,13 +100,16 @@
 
 		asset.available_for_use_date = purchase_date
 		asset.purchase_date = purchase_date
-		asset.append("finance_books", {
-			"expected_value_after_useful_life": 10000,
-			"depreciation_method": "Straight Line",
-			"total_number_of_depreciations": 3,
-			"frequency_of_depreciation": 10,
-			"depreciation_start_date": month_end_date
-		})
+		asset.append(
+			"finance_books",
+			{
+				"expected_value_after_useful_life": 10000,
+				"depreciation_method": "Straight Line",
+				"total_number_of_depreciations": 3,
+				"frequency_of_depreciation": 10,
+				"depreciation_start_date": month_end_date,
+			},
+		)
 		asset.submit()
 
 		pi = make_invoice(pr.name)
@@ -119,12 +124,15 @@
 
 		expected_gle = (
 			("Asset Received But Not Billed - _TC", 100000.0, 0.0),
-			("Creditors - _TC", 0.0, 100000.0)
+			("Creditors - _TC", 0.0, 100000.0),
 		)
 
-		gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
+		gle = frappe.db.sql(
+			"""select account, debit, credit from `tabGL Entry`
 			where voucher_type='Purchase Invoice' and voucher_no = %s
-			order by account""", pi.name)
+			order by account""",
+			pi.name,
+		)
 		self.assertEqual(gle, expected_gle)
 
 		pi.cancel()
@@ -138,8 +146,8 @@
 		create_fixed_asset_item("Rack", is_grouped_asset=1)
 		pr = make_purchase_receipt(item_code="Rack", qty=3, rate=100000.0, location="Test Location")
 
-		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
-		asset = frappe.get_doc('Asset', asset_name)
+		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name")
+		asset = frappe.get_doc("Asset", asset_name)
 		self.assertEqual(asset.asset_quantity, 3)
 		asset.calculate_depreciation = 1
 
@@ -148,40 +156,39 @@
 
 		asset.available_for_use_date = purchase_date
 		asset.purchase_date = purchase_date
-		asset.append("finance_books", {
-			"expected_value_after_useful_life": 10000,
-			"depreciation_method": "Straight Line",
-			"total_number_of_depreciations": 3,
-			"frequency_of_depreciation": 10,
-			"depreciation_start_date": month_end_date
-		})
+		asset.append(
+			"finance_books",
+			{
+				"expected_value_after_useful_life": 10000,
+				"depreciation_method": "Straight Line",
+				"total_number_of_depreciations": 3,
+				"frequency_of_depreciation": 10,
+				"depreciation_start_date": month_end_date,
+			},
+		)
 		asset.submit()
 
 	def test_is_fixed_asset_set(self):
-		asset = create_asset(is_existing_asset = 1)
-		doc = frappe.new_doc('Purchase Invoice')
-		doc.supplier = '_Test Supplier'
-		doc.append('items', {
-			'item_code': 'Macbook Pro',
-			'qty': 1,
-			'asset': asset.name
-		})
+		asset = create_asset(is_existing_asset=1)
+		doc = frappe.new_doc("Purchase Invoice")
+		doc.supplier = "_Test Supplier"
+		doc.append("items", {"item_code": "Macbook Pro", "qty": 1, "asset": asset.name})
 
 		doc.set_missing_values()
 		self.assertEqual(doc.items[0].is_fixed_asset, 1)
 
 	def test_scrap_asset(self):
 		asset = create_asset(
-			calculate_depreciation = 1,
-			available_for_use_date = '2020-01-01',
-			purchase_date = '2020-01-01',
-			expected_value_after_useful_life = 10000,
-			total_number_of_depreciations = 10,
-			frequency_of_depreciation = 1,
-			submit = 1
+			calculate_depreciation=1,
+			available_for_use_date="2020-01-01",
+			purchase_date="2020-01-01",
+			expected_value_after_useful_life=10000,
+			total_number_of_depreciations=10,
+			frequency_of_depreciation=1,
+			submit=1,
 		)
 
-		post_depreciation_entries(date=add_months('2020-01-01', 4))
+		post_depreciation_entries(date=add_months("2020-01-01", 4))
 
 		scrap_asset(asset.name)
 
@@ -192,12 +199,15 @@
 		expected_gle = (
 			("_Test Accumulated Depreciations - _TC", 36000.0, 0.0),
 			("_Test Fixed Asset - _TC", 0.0, 100000.0),
-			("_Test Gain/Loss on Asset Disposal - _TC", 64000.0, 0.0)
+			("_Test Gain/Loss on Asset Disposal - _TC", 64000.0, 0.0),
 		)
 
-		gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
+		gle = frappe.db.sql(
+			"""select account, debit, credit from `tabGL Entry`
 			where voucher_type='Journal Entry' and voucher_no = %s
-			order by account""", asset.journal_entry_for_scrap)
+			order by account""",
+			asset.journal_entry_for_scrap,
+		)
 		self.assertEqual(gle, expected_gle)
 
 		restore_asset(asset.name)
@@ -208,14 +218,14 @@
 
 	def test_gle_made_by_asset_sale(self):
 		asset = create_asset(
-			calculate_depreciation = 1,
-			available_for_use_date = '2020-06-06',
-			purchase_date = '2020-01-01',
-			expected_value_after_useful_life = 10000,
-			total_number_of_depreciations = 3,
-			frequency_of_depreciation = 10,
-			depreciation_start_date = '2020-12-31',
-			submit = 1
+			calculate_depreciation=1,
+			available_for_use_date="2020-06-06",
+			purchase_date="2020-01-01",
+			expected_value_after_useful_life=10000,
+			total_number_of_depreciations=3,
+			frequency_of_depreciation=10,
+			depreciation_start_date="2020-12-31",
+			submit=1,
 		)
 
 		post_depreciation_entries(date="2021-01-01")
@@ -233,12 +243,15 @@
 			("_Test Accumulated Depreciations - _TC", 20490.2, 0.0),
 			("_Test Fixed Asset - _TC", 0.0, 100000.0),
 			("_Test Gain/Loss on Asset Disposal - _TC", 54509.8, 0.0),
-			("Debtors - _TC", 25000.0, 0.0)
+			("Debtors - _TC", 25000.0, 0.0),
 		)
 
-		gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
+		gle = frappe.db.sql(
+			"""select account, debit, credit from `tabGL Entry`
 			where voucher_type='Sales Invoice' and voucher_no = %s
-			order by account""", si.name)
+			order by account""",
+			si.name,
+		)
 
 		self.assertEqual(gle, expected_gle)
 
@@ -247,18 +260,18 @@
 
 	def test_asset_splitting(self):
 		asset = create_asset(
-			calculate_depreciation = 1,
+			calculate_depreciation=1,
 			asset_quantity=10,
-			available_for_use_date = '2020-01-01',
-			purchase_date = '2020-01-01',
-			expected_value_after_useful_life = 0,
-			total_number_of_depreciations = 6,
-			number_of_depreciations_booked = 1,
-			frequency_of_depreciation = 10,
-			depreciation_start_date = '2021-01-01',
+			available_for_use_date="2020-01-01",
+			purchase_date="2020-01-01",
+			expected_value_after_useful_life=0,
+			total_number_of_depreciations=6,
+			number_of_depreciations_booked=1,
+			frequency_of_depreciation=10,
+			depreciation_start_date="2021-01-01",
 			opening_accumulated_depreciation=20000,
 			gross_purchase_amount=120000,
-			submit = 1
+			submit=1,
 		)
 
 		post_depreciation_entries(date="2021-01-01")
@@ -285,7 +298,7 @@
 
 		journal_entry = asset.schedules[0].journal_entry
 
-		jv = frappe.get_doc('Journal Entry', journal_entry)
+		jv = frappe.get_doc("Journal Entry", journal_entry)
 		self.assertEqual(jv.accounts[0].credit_in_account_currency, 16000)
 		self.assertEqual(jv.accounts[1].debit_in_account_currency, 16000)
 		self.assertEqual(jv.accounts[2].credit_in_account_currency, 4000)
@@ -297,45 +310,56 @@
 		self.assertEqual(jv.accounts[3].reference_name, new_asset.name)
 
 	def test_expense_head(self):
-		pr = make_purchase_receipt(item_code="Macbook Pro",
-			qty=2, rate=200000.0, location="Test Location")
+		pr = make_purchase_receipt(
+			item_code="Macbook Pro", qty=2, rate=200000.0, location="Test Location"
+		)
 		doc = make_invoice(pr.name)
 
-		self.assertEqual('Asset Received But Not Billed - _TC', doc.items[0].expense_account)
+		self.assertEqual("Asset Received But Not Billed - _TC", doc.items[0].expense_account)
 
 	# CWIP: Capital Work In Progress
 	def test_cwip_accounting(self):
-		pr = make_purchase_receipt(item_code="Macbook Pro",
-			qty=1, rate=5000, do_not_submit=True, location="Test Location")
+		pr = make_purchase_receipt(
+			item_code="Macbook Pro", qty=1, rate=5000, do_not_submit=True, location="Test Location"
+		)
 
-		pr.set('taxes', [{
-			'category': 'Total',
-			'add_deduct_tax': 'Add',
-			'charge_type': 'On Net Total',
-			'account_head': '_Test Account Service Tax - _TC',
-			'description': '_Test Account Service Tax',
-			'cost_center': 'Main - _TC',
-			'rate': 5.0
-		}, {
-			'category': 'Valuation and Total',
-			'add_deduct_tax': 'Add',
-			'charge_type': 'On Net Total',
-			'account_head': '_Test Account Shipping Charges - _TC',
-			'description': '_Test Account Shipping Charges',
-			'cost_center': 'Main - _TC',
-			'rate': 5.0
-		}])
+		pr.set(
+			"taxes",
+			[
+				{
+					"category": "Total",
+					"add_deduct_tax": "Add",
+					"charge_type": "On Net Total",
+					"account_head": "_Test Account Service Tax - _TC",
+					"description": "_Test Account Service Tax",
+					"cost_center": "Main - _TC",
+					"rate": 5.0,
+				},
+				{
+					"category": "Valuation and Total",
+					"add_deduct_tax": "Add",
+					"charge_type": "On Net Total",
+					"account_head": "_Test Account Shipping Charges - _TC",
+					"description": "_Test Account Shipping Charges",
+					"cost_center": "Main - _TC",
+					"rate": 5.0,
+				},
+			],
+		)
 
 		pr.submit()
 
 		expected_gle = (
 			("Asset Received But Not Billed - _TC", 0.0, 5250.0),
-			("CWIP Account - _TC", 5250.0, 0.0)
+			("CWIP Account - _TC", 5250.0, 0.0),
 		)
 
-		pr_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
+		pr_gle = frappe.db.sql(
+			"""select account, debit, credit from `tabGL Entry`
 			where voucher_type='Purchase Receipt' and voucher_no = %s
-			order by account""", pr.name)
+			order by account""",
+			pr.name,
+		)
 
 		self.assertEqual(pr_gle, expected_gle)
 
@@ -350,45 +374,53 @@
 			("Expenses Included In Asset Valuation - _TC", 0.0, 250.0),
 		)
 
-		pi_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
+		pi_gle = frappe.db.sql(
+			"""select account, debit, credit from `tabGL Entry`
 			where voucher_type='Purchase Invoice' and voucher_no = %s
-			order by account""", pi.name)
+			order by account""",
+			pi.name,
+		)
 
 		self.assertEqual(pi_gle, expected_gle)
 
-		asset = frappe.db.get_value('Asset',
-			{'purchase_receipt': pr.name, 'docstatus': 0}, 'name')
+		asset = frappe.db.get_value("Asset", {"purchase_receipt": pr.name, "docstatus": 0}, "name")
 
-		asset_doc = frappe.get_doc('Asset', asset)
+		asset_doc = frappe.get_doc("Asset", asset)
 
 		month_end_date = get_last_day(nowdate())
-		asset_doc.available_for_use_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15)
+		asset_doc.available_for_use_date = (
+			nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15)
+		)
 		self.assertEqual(asset_doc.gross_purchase_amount, 5250.0)
 
-		asset_doc.append("finance_books", {
-			"expected_value_after_useful_life": 200,
-			"depreciation_method": "Straight Line",
-			"total_number_of_depreciations": 3,
-			"frequency_of_depreciation": 10,
-			"depreciation_start_date": month_end_date
-		})
+		asset_doc.append(
+			"finance_books",
+			{
+				"expected_value_after_useful_life": 200,
+				"depreciation_method": "Straight Line",
+				"total_number_of_depreciations": 3,
+				"frequency_of_depreciation": 10,
+				"depreciation_start_date": month_end_date,
+			},
+		)
 		asset_doc.submit()
 
-		expected_gle = (
-			("_Test Fixed Asset - _TC", 5250.0, 0.0),
-			("CWIP Account - _TC", 0.0, 5250.0)
-		)
+		expected_gle = (("_Test Fixed Asset - _TC", 5250.0, 0.0), ("CWIP Account - _TC", 0.0, 5250.0))
 
-		gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
+		gle = frappe.db.sql(
+			"""select account, debit, credit from `tabGL Entry`
 			where voucher_type='Asset' and voucher_no = %s
-			order by account""", asset_doc.name)
-
+			order by account""",
+			asset_doc.name,
+		)
 
 		self.assertEqual(gle, expected_gle)
 
 	def test_asset_cwip_toggling_cases(self):
 		cwip = frappe.db.get_value("Asset Category", "Computers", "enable_cwip_accounting")
-		name = frappe.db.get_value("Asset Category Account", filters={"parent": "Computers"}, fieldname=["name"])
+		name = frappe.db.get_value(
+			"Asset Category Account", filters={"parent": "Computers"}, fieldname=["name"]
+		)
 		cwip_acc = "CWIP Account - _TC"
 
 		frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 0)
@@ -396,197 +428,231 @@
 		frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account", "")
 
 		# case 0 -- PI with cwip disable, Asset with cwip disabled, No cwip account set
-		pi = make_purchase_invoice(item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location", update_stock=1)
-		asset = frappe.db.get_value('Asset', {'purchase_invoice': pi.name, 'docstatus': 0}, 'name')
-		asset_doc = frappe.get_doc('Asset', asset)
+		pi = make_purchase_invoice(
+			item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location", update_stock=1
+		)
+		asset = frappe.db.get_value("Asset", {"purchase_invoice": pi.name, "docstatus": 0}, "name")
+		asset_doc = frappe.get_doc("Asset", asset)
 		asset_doc.available_for_use_date = nowdate()
 		asset_doc.calculate_depreciation = 0
 		asset_doc.submit()
-		gle = frappe.db.sql("""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""", asset_doc.name)
+		gle = frappe.db.sql(
+			"""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""",
+			asset_doc.name,
+		)
 		self.assertFalse(gle)
 
 		# case 1 -- PR with cwip disabled, Asset with cwip enabled
-		pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location")
+		pr = make_purchase_receipt(
+			item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location"
+		)
 		frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 1)
 		frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", cwip_acc)
-		asset = frappe.db.get_value('Asset', {'purchase_receipt': pr.name, 'docstatus': 0}, 'name')
-		asset_doc = frappe.get_doc('Asset', asset)
+		asset = frappe.db.get_value("Asset", {"purchase_receipt": pr.name, "docstatus": 0}, "name")
+		asset_doc = frappe.get_doc("Asset", asset)
 		asset_doc.available_for_use_date = nowdate()
 		asset_doc.calculate_depreciation = 0
 		asset_doc.submit()
-		gle = frappe.db.sql("""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""", asset_doc.name)
+		gle = frappe.db.sql(
+			"""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""",
+			asset_doc.name,
+		)
 		self.assertFalse(gle)
 
 		# case 2 -- PR with cwip enabled, Asset with cwip disabled
-		pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location")
+		pr = make_purchase_receipt(
+			item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location"
+		)
 		frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 0)
-		asset = frappe.db.get_value('Asset', {'purchase_receipt': pr.name, 'docstatus': 0}, 'name')
-		asset_doc = frappe.get_doc('Asset', asset)
+		asset = frappe.db.get_value("Asset", {"purchase_receipt": pr.name, "docstatus": 0}, "name")
+		asset_doc = frappe.get_doc("Asset", asset)
 		asset_doc.available_for_use_date = nowdate()
 		asset_doc.calculate_depreciation = 0
 		asset_doc.submit()
-		gle = frappe.db.sql("""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""", asset_doc.name)
+		gle = frappe.db.sql(
+			"""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""",
+			asset_doc.name,
+		)
 		self.assertTrue(gle)
 
 		# case 3 -- PI with cwip disabled, Asset with cwip enabled
-		pi = make_purchase_invoice(item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location", update_stock=1)
+		pi = make_purchase_invoice(
+			item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location", update_stock=1
+		)
 		frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 1)
-		asset = frappe.db.get_value('Asset', {'purchase_invoice': pi.name, 'docstatus': 0}, 'name')
-		asset_doc = frappe.get_doc('Asset', asset)
+		asset = frappe.db.get_value("Asset", {"purchase_invoice": pi.name, "docstatus": 0}, "name")
+		asset_doc = frappe.get_doc("Asset", asset)
 		asset_doc.available_for_use_date = nowdate()
 		asset_doc.calculate_depreciation = 0
 		asset_doc.submit()
-		gle = frappe.db.sql("""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""", asset_doc.name)
+		gle = frappe.db.sql(
+			"""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""",
+			asset_doc.name,
+		)
 		self.assertFalse(gle)
 
 		# case 4 -- PI with cwip enabled, Asset with cwip disabled
-		pi = make_purchase_invoice(item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location", update_stock=1)
+		pi = make_purchase_invoice(
+			item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location", update_stock=1
+		)
 		frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 0)
-		asset = frappe.db.get_value('Asset', {'purchase_invoice': pi.name, 'docstatus': 0}, 'name')
-		asset_doc = frappe.get_doc('Asset', asset)
+		asset = frappe.db.get_value("Asset", {"purchase_invoice": pi.name, "docstatus": 0}, "name")
+		asset_doc = frappe.get_doc("Asset", asset)
 		asset_doc.available_for_use_date = nowdate()
 		asset_doc.calculate_depreciation = 0
 		asset_doc.submit()
-		gle = frappe.db.sql("""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""", asset_doc.name)
+		gle = frappe.db.sql(
+			"""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""",
+			asset_doc.name,
+		)
 		self.assertTrue(gle)
 
 		frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", cwip)
 		frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", cwip_acc)
 		frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account", cwip_acc)
 
+
 class TestDepreciationMethods(AssetSetup):
 	def test_schedule_for_straight_line_method(self):
 		asset = create_asset(
-			calculate_depreciation = 1,
-			available_for_use_date = "2030-01-01",
-			purchase_date = "2030-01-01",
-			expected_value_after_useful_life = 10000,
-			depreciation_start_date = "2030-12-31",
-			total_number_of_depreciations = 3,
-			frequency_of_depreciation = 12
+			calculate_depreciation=1,
+			available_for_use_date="2030-01-01",
+			purchase_date="2030-01-01",
+			expected_value_after_useful_life=10000,
+			depreciation_start_date="2030-12-31",
+			total_number_of_depreciations=3,
+			frequency_of_depreciation=12,
 		)
 
 		self.assertEqual(asset.status, "Draft")
 		expected_schedules = [
 			["2030-12-31", 30000.00, 30000.00],
 			["2031-12-31", 30000.00, 60000.00],
-			["2032-12-31", 30000.00, 90000.00]
+			["2032-12-31", 30000.00, 90000.00],
 		]
 
-		schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
-			for d in asset.get("schedules")]
+		schedules = [
+			[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
+			for d in asset.get("schedules")
+		]
 
 		self.assertEqual(schedules, expected_schedules)
 
 	def test_schedule_for_straight_line_method_for_existing_asset(self):
 		asset = create_asset(
-			calculate_depreciation = 1,
-			available_for_use_date = "2030-06-06",
-			is_existing_asset = 1,
-			number_of_depreciations_booked = 2,
-			opening_accumulated_depreciation = 47095.89,
-			expected_value_after_useful_life = 10000,
-			depreciation_start_date = "2032-12-31",
-			total_number_of_depreciations = 3,
-			frequency_of_depreciation = 12
+			calculate_depreciation=1,
+			available_for_use_date="2030-06-06",
+			is_existing_asset=1,
+			number_of_depreciations_booked=2,
+			opening_accumulated_depreciation=47095.89,
+			expected_value_after_useful_life=10000,
+			depreciation_start_date="2032-12-31",
+			total_number_of_depreciations=3,
+			frequency_of_depreciation=12,
 		)
 
 		self.assertEqual(asset.status, "Draft")
-		expected_schedules = [
-			["2032-12-31", 30000.0, 77095.89],
-			["2033-06-06", 12904.11, 90000.0]
+		expected_schedules = [["2032-12-31", 30000.0, 77095.89], ["2033-06-06", 12904.11, 90000.0]]
+		schedules = [
+			[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
+			for d in asset.get("schedules")
 		]
-		schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
-			for d in asset.get("schedules")]
 
 		self.assertEqual(schedules, expected_schedules)
 
 	def test_schedule_for_double_declining_method(self):
 		asset = create_asset(
-			calculate_depreciation = 1,
-			available_for_use_date = "2030-01-01",
-			purchase_date = "2030-01-01",
-			depreciation_method = "Double Declining Balance",
-			expected_value_after_useful_life = 10000,
-			depreciation_start_date = "2030-12-31",
-			total_number_of_depreciations = 3,
-			frequency_of_depreciation = 12
+			calculate_depreciation=1,
+			available_for_use_date="2030-01-01",
+			purchase_date="2030-01-01",
+			depreciation_method="Double Declining Balance",
+			expected_value_after_useful_life=10000,
+			depreciation_start_date="2030-12-31",
+			total_number_of_depreciations=3,
+			frequency_of_depreciation=12,
 		)
 
 		self.assertEqual(asset.status, "Draft")
 
 		expected_schedules = [
-			['2030-12-31', 66667.00, 66667.00],
-			['2031-12-31', 22222.11, 88889.11],
-			['2032-12-31', 1110.89, 90000.0]
+			["2030-12-31", 66667.00, 66667.00],
+			["2031-12-31", 22222.11, 88889.11],
+			["2032-12-31", 1110.89, 90000.0],
 		]
 
-		schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
-			for d in asset.get("schedules")]
+		schedules = [
+			[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
+			for d in asset.get("schedules")
+		]
 
 		self.assertEqual(schedules, expected_schedules)
 
 	def test_schedule_for_double_declining_method_for_existing_asset(self):
 		asset = create_asset(
-			calculate_depreciation = 1,
-			available_for_use_date = "2030-01-01",
-			is_existing_asset = 1,
-			depreciation_method = "Double Declining Balance",
-			number_of_depreciations_booked = 1,
-			opening_accumulated_depreciation = 50000,
-			expected_value_after_useful_life = 10000,
-			depreciation_start_date = "2030-12-31",
-			total_number_of_depreciations = 3,
-			frequency_of_depreciation = 12
+			calculate_depreciation=1,
+			available_for_use_date="2030-01-01",
+			is_existing_asset=1,
+			depreciation_method="Double Declining Balance",
+			number_of_depreciations_booked=1,
+			opening_accumulated_depreciation=50000,
+			expected_value_after_useful_life=10000,
+			depreciation_start_date="2030-12-31",
+			total_number_of_depreciations=3,
+			frequency_of_depreciation=12,
 		)
 
 		self.assertEqual(asset.status, "Draft")
 
-		expected_schedules = [
-			["2030-12-31", 33333.50, 83333.50],
-			["2031-12-31", 6666.50, 90000.0]
-		]
+		expected_schedules = [["2030-12-31", 33333.50, 83333.50], ["2031-12-31", 6666.50, 90000.0]]
 
-		schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
-			for d in asset.get("schedules")]
+		schedules = [
+			[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
+			for d in asset.get("schedules")
+		]
 
 		self.assertEqual(schedules, expected_schedules)
 
 	def test_schedule_for_prorated_straight_line_method(self):
 		asset = create_asset(
-			calculate_depreciation = 1,
-			available_for_use_date = "2030-01-30",
-			purchase_date = "2030-01-30",
-			depreciation_method = "Straight Line",
-			expected_value_after_useful_life = 10000,
-			depreciation_start_date = "2030-12-31",
-			total_number_of_depreciations = 3,
-			frequency_of_depreciation = 12
+			calculate_depreciation=1,
+			available_for_use_date="2030-01-30",
+			purchase_date="2030-01-30",
+			depreciation_method="Straight Line",
+			expected_value_after_useful_life=10000,
+			depreciation_start_date="2030-12-31",
+			total_number_of_depreciations=3,
+			frequency_of_depreciation=12,
 		)
 
 		expected_schedules = [
-			['2030-12-31', 27616.44, 27616.44],
-			['2031-12-31', 30000.0, 57616.44],
-			['2032-12-31', 30000.0, 87616.44],
-			['2033-01-30', 2383.56, 90000.0]
+			["2030-12-31", 27616.44, 27616.44],
+			["2031-12-31", 30000.0, 57616.44],
+			["2032-12-31", 30000.0, 87616.44],
+			["2033-01-30", 2383.56, 90000.0],
 		]
 
-		schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
-			for d in asset.get("schedules")]
+		schedules = [
+			[
+				cstr(d.schedule_date),
+				flt(d.depreciation_amount, 2),
+				flt(d.accumulated_depreciation_amount, 2),
+			]
+			for d in asset.get("schedules")
+		]
 
 		self.assertEqual(schedules, expected_schedules)
 
 	# WDV: Written Down Value method
 	def test_depreciation_entry_for_wdv_without_pro_rata(self):
 		asset = create_asset(
-			calculate_depreciation = 1,
-			available_for_use_date = "2030-01-01",
-			purchase_date = "2030-01-01",
-			depreciation_method = "Written Down Value",
-			expected_value_after_useful_life = 12500,
-			depreciation_start_date = "2030-12-31",
-			total_number_of_depreciations = 3,
-			frequency_of_depreciation = 12
+			calculate_depreciation=1,
+			available_for_use_date="2030-01-01",
+			purchase_date="2030-01-01",
+			depreciation_method="Written Down Value",
+			expected_value_after_useful_life=12500,
+			depreciation_start_date="2030-12-31",
+			total_number_of_depreciations=3,
+			frequency_of_depreciation=12,
 		)
 
 		self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
@@ -597,54 +663,67 @@
 			["2032-12-31", 12500.0, 87500.0],
 		]
 
-		schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
-			for d in asset.get("schedules")]
+		schedules = [
+			[
+				cstr(d.schedule_date),
+				flt(d.depreciation_amount, 2),
+				flt(d.accumulated_depreciation_amount, 2),
+			]
+			for d in asset.get("schedules")
+		]
 
 		self.assertEqual(schedules, expected_schedules)
 
 	# WDV: Written Down Value method
 	def test_pro_rata_depreciation_entry_for_wdv(self):
 		asset = create_asset(
-			calculate_depreciation = 1,
-			available_for_use_date = "2030-06-06",
-			purchase_date = "2030-01-01",
-			depreciation_method = "Written Down Value",
-			expected_value_after_useful_life = 12500,
-			depreciation_start_date = "2030-12-31",
-			total_number_of_depreciations = 3,
-			frequency_of_depreciation = 12
+			calculate_depreciation=1,
+			available_for_use_date="2030-06-06",
+			purchase_date="2030-01-01",
+			depreciation_method="Written Down Value",
+			expected_value_after_useful_life=12500,
+			depreciation_start_date="2030-12-31",
+			total_number_of_depreciations=3,
+			frequency_of_depreciation=12,
 		)
 
 		self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
 
 		expected_schedules = [
-			['2030-12-31', 28630.14, 28630.14],
-			['2031-12-31', 35684.93, 64315.07],
-			['2032-12-31', 17842.47, 82157.54],
-			['2033-06-06', 5342.46, 87500.0]
+			["2030-12-31", 28630.14, 28630.14],
+			["2031-12-31", 35684.93, 64315.07],
+			["2032-12-31", 17842.47, 82157.54],
+			["2033-06-06", 5342.46, 87500.0],
 		]
 
-		schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
-			for d in asset.get("schedules")]
+		schedules = [
+			[
+				cstr(d.schedule_date),
+				flt(d.depreciation_amount, 2),
+				flt(d.accumulated_depreciation_amount, 2),
+			]
+			for d in asset.get("schedules")
+		]
 
 		self.assertEqual(schedules, expected_schedules)
 
+
 class TestDepreciationBasics(AssetSetup):
 	def test_depreciation_without_pro_rata(self):
 		asset = create_asset(
-			item_code = "Macbook Pro",
-			calculate_depreciation = 1,
-			available_for_use_date = getdate("2019-12-31"),
-			total_number_of_depreciations = 3,
-			expected_value_after_useful_life = 10000,
-			depreciation_start_date = getdate("2020-12-31"),
-			submit = 1
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			available_for_use_date=getdate("2019-12-31"),
+			total_number_of_depreciations=3,
+			expected_value_after_useful_life=10000,
+			depreciation_start_date=getdate("2020-12-31"),
+			submit=1,
 		)
 
 		expected_values = [
 			["2020-12-31", 30000, 30000],
 			["2021-12-31", 30000, 60000],
-			["2022-12-31", 30000, 90000]
+			["2022-12-31", 30000, 90000],
 		]
 
 		for i, schedule in enumerate(asset.schedules):
@@ -654,20 +733,20 @@
 
 	def test_depreciation_with_pro_rata(self):
 		asset = create_asset(
-			item_code = "Macbook Pro",
-			calculate_depreciation = 1,
-			available_for_use_date = getdate("2020-01-01"),
-			total_number_of_depreciations = 3,
-			expected_value_after_useful_life = 10000,
-			depreciation_start_date = getdate("2020-07-01"),
-			submit = 1
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			available_for_use_date=getdate("2020-01-01"),
+			total_number_of_depreciations=3,
+			expected_value_after_useful_life=10000,
+			depreciation_start_date=getdate("2020-07-01"),
+			submit=1,
 		)
 
 		expected_values = [
 			["2020-07-01", 15000, 15000],
 			["2021-07-01", 30000, 45000],
 			["2022-07-01", 30000, 75000],
-			["2023-01-01", 15000, 90000]
+			["2023-01-01", 15000, 90000],
 		]
 
 		for i, schedule in enumerate(asset.schedules):
@@ -680,19 +759,19 @@
 
 		from erpnext.assets.doctype.asset.asset import get_depreciation_amount
 
-		asset = create_asset(
-			item_code = "Macbook Pro",
-			available_for_use_date = "2019-12-31"
-		)
+		asset = create_asset(item_code="Macbook Pro", available_for_use_date="2019-12-31")
 
 		asset.calculate_depreciation = 1
-		asset.append("finance_books", {
-			"depreciation_method": "Straight Line",
-			"frequency_of_depreciation": 12,
-			"total_number_of_depreciations": 3,
-			"expected_value_after_useful_life": 10000,
-			"depreciation_start_date": "2020-12-31"
-		})
+		asset.append(
+			"finance_books",
+			{
+				"depreciation_method": "Straight Line",
+				"frequency_of_depreciation": 12,
+				"total_number_of_depreciations": 3,
+				"expected_value_after_useful_life": 10000,
+				"depreciation_start_date": "2020-12-31",
+			},
+		)
 
 		depreciation_amount = get_depreciation_amount(asset, 100000, asset.finance_books[0])
 		self.assertEqual(depreciation_amount, 30000)
@@ -701,21 +780,17 @@
 		"""Tests if make_depreciation_schedule() returns the right values."""
 
 		asset = create_asset(
-			item_code = "Macbook Pro",
-			calculate_depreciation = 1,
-			available_for_use_date = "2019-12-31",
-			depreciation_method = "Straight Line",
-			frequency_of_depreciation = 12,
-			total_number_of_depreciations = 3,
-			expected_value_after_useful_life = 10000,
-			depreciation_start_date = "2020-12-31"
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			available_for_use_date="2019-12-31",
+			depreciation_method="Straight Line",
+			frequency_of_depreciation=12,
+			total_number_of_depreciations=3,
+			expected_value_after_useful_life=10000,
+			depreciation_start_date="2020-12-31",
 		)
 
-		expected_values = [
-			['2020-12-31', 30000.0],
-			['2021-12-31', 30000.0],
-			['2022-12-31', 30000.0]
-		]
+		expected_values = [["2020-12-31", 30000.0], ["2021-12-31", 30000.0], ["2022-12-31", 30000.0]]
 
 		for i, schedule in enumerate(asset.schedules):
 			self.assertEqual(expected_values[i][0], schedule.schedule_date)
@@ -725,14 +800,14 @@
 		"""Tests if set_accumulated_depreciation() returns the right values."""
 
 		asset = create_asset(
-			item_code = "Macbook Pro",
-			calculate_depreciation = 1,
-			available_for_use_date = "2019-12-31",
-			depreciation_method = "Straight Line",
-			frequency_of_depreciation = 12,
-			total_number_of_depreciations = 3,
-			expected_value_after_useful_life = 10000,
-			depreciation_start_date = "2020-12-31"
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			available_for_use_date="2019-12-31",
+			depreciation_method="Straight Line",
+			frequency_of_depreciation=12,
+			total_number_of_depreciations=3,
+			expected_value_after_useful_life=10000,
+			depreciation_start_date="2020-12-31",
 		)
 
 		expected_values = [30000.0, 60000.0, 90000.0]
@@ -743,32 +818,34 @@
 	def test_check_is_pro_rata(self):
 		"""Tests if check_is_pro_rata() returns the right value(i.e. checks if has_pro_rata is accurate)."""
 
-		asset = create_asset(
-			item_code = "Macbook Pro",
-			available_for_use_date = "2019-12-31",
-			do_not_save = 1
-		)
+		asset = create_asset(item_code="Macbook Pro", available_for_use_date="2019-12-31", do_not_save=1)
 
 		asset.calculate_depreciation = 1
-		asset.append("finance_books", {
-			"depreciation_method": "Straight Line",
-			"frequency_of_depreciation": 12,
-			"total_number_of_depreciations": 3,
-			"expected_value_after_useful_life": 10000,
-			"depreciation_start_date": "2020-12-31"
-		})
+		asset.append(
+			"finance_books",
+			{
+				"depreciation_method": "Straight Line",
+				"frequency_of_depreciation": 12,
+				"total_number_of_depreciations": 3,
+				"expected_value_after_useful_life": 10000,
+				"depreciation_start_date": "2020-12-31",
+			},
+		)
 
 		has_pro_rata = asset.check_is_pro_rata(asset.finance_books[0])
 		self.assertFalse(has_pro_rata)
 
 		asset.finance_books = []
-		asset.append("finance_books", {
-			"depreciation_method": "Straight Line",
-			"frequency_of_depreciation": 12,
-			"total_number_of_depreciations": 3,
-			"expected_value_after_useful_life": 10000,
-			"depreciation_start_date": "2020-07-01"
-		})
+		asset.append(
+			"finance_books",
+			{
+				"depreciation_method": "Straight Line",
+				"frequency_of_depreciation": 12,
+				"total_number_of_depreciations": 3,
+				"expected_value_after_useful_life": 10000,
+				"depreciation_start_date": "2020-07-01",
+			},
+		)
 
 		has_pro_rata = asset.check_is_pro_rata(asset.finance_books[0])
 		self.assertTrue(has_pro_rata)
@@ -777,13 +854,13 @@
 		"""Tests if an error is raised when expected_value_after_useful_life(110,000) > gross_purchase_amount(100,000)."""
 
 		asset = create_asset(
-			item_code = "Macbook Pro",
-			calculate_depreciation = 1,
-			available_for_use_date = "2019-12-31",
-			total_number_of_depreciations = 3,
-			expected_value_after_useful_life = 110000,
-			depreciation_start_date = "2020-07-01",
-			do_not_save = 1
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			available_for_use_date="2019-12-31",
+			total_number_of_depreciations=3,
+			expected_value_after_useful_life=110000,
+			depreciation_start_date="2020-07-01",
+			do_not_save=1,
 		)
 
 		self.assertRaises(frappe.ValidationError, asset.save)
@@ -792,11 +869,11 @@
 		"""Tests if an error is raised when neither depreciation_start_date nor available_for_use_date are specified."""
 
 		asset = create_asset(
-			item_code = "Macbook Pro",
-			calculate_depreciation = 1,
-			total_number_of_depreciations = 3,
-			expected_value_after_useful_life = 110000,
-			do_not_save = 1
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			total_number_of_depreciations=3,
+			expected_value_after_useful_life=110000,
+			do_not_save=1,
 		)
 
 		self.assertRaises(frappe.ValidationError, asset.save)
@@ -805,14 +882,14 @@
 		"""Tests if an error is raised when opening_accumulated_depreciation > (gross_purchase_amount - expected_value_after_useful_life)."""
 
 		asset = create_asset(
-			item_code = "Macbook Pro",
-			calculate_depreciation = 1,
-			available_for_use_date = "2019-12-31",
-			total_number_of_depreciations = 3,
-			expected_value_after_useful_life = 10000,
-			depreciation_start_date = "2020-07-01",
-			opening_accumulated_depreciation = 100000,
-			do_not_save = 1
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			available_for_use_date="2019-12-31",
+			total_number_of_depreciations=3,
+			expected_value_after_useful_life=10000,
+			depreciation_start_date="2020-07-01",
+			opening_accumulated_depreciation=100000,
+			do_not_save=1,
 		)
 
 		self.assertRaises(frappe.ValidationError, asset.save)
@@ -821,14 +898,14 @@
 		"""Tests if an error is raised when number_of_depreciations_booked is not specified when opening_accumulated_depreciation is."""
 
 		asset = create_asset(
-			item_code = "Macbook Pro",
-			calculate_depreciation = 1,
-			available_for_use_date = "2019-12-31",
-			total_number_of_depreciations = 3,
-			expected_value_after_useful_life = 10000,
-			depreciation_start_date = "2020-07-01",
-			opening_accumulated_depreciation = 10000,
-			do_not_save = 1
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			available_for_use_date="2019-12-31",
+			total_number_of_depreciations=3,
+			expected_value_after_useful_life=10000,
+			depreciation_start_date="2020-07-01",
+			opening_accumulated_depreciation=10000,
+			do_not_save=1,
 		)
 
 		self.assertRaises(frappe.ValidationError, asset.save)
@@ -838,56 +915,56 @@
 
 		# number_of_depreciations_booked > total_number_of_depreciations
 		asset = create_asset(
-			item_code = "Macbook Pro",
-			calculate_depreciation = 1,
-			available_for_use_date = "2019-12-31",
-			total_number_of_depreciations = 3,
-			expected_value_after_useful_life = 10000,
-			depreciation_start_date = "2020-07-01",
-			opening_accumulated_depreciation = 10000,
-			number_of_depreciations_booked = 5,
-			do_not_save = 1
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			available_for_use_date="2019-12-31",
+			total_number_of_depreciations=3,
+			expected_value_after_useful_life=10000,
+			depreciation_start_date="2020-07-01",
+			opening_accumulated_depreciation=10000,
+			number_of_depreciations_booked=5,
+			do_not_save=1,
 		)
 
 		self.assertRaises(frappe.ValidationError, asset.save)
 
 		# number_of_depreciations_booked = total_number_of_depreciations
 		asset_2 = create_asset(
-			item_code = "Macbook Pro",
-			calculate_depreciation = 1,
-			available_for_use_date = "2019-12-31",
-			total_number_of_depreciations = 5,
-			expected_value_after_useful_life = 10000,
-			depreciation_start_date = "2020-07-01",
-			opening_accumulated_depreciation = 10000,
-			number_of_depreciations_booked = 5,
-			do_not_save = 1
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			available_for_use_date="2019-12-31",
+			total_number_of_depreciations=5,
+			expected_value_after_useful_life=10000,
+			depreciation_start_date="2020-07-01",
+			opening_accumulated_depreciation=10000,
+			number_of_depreciations_booked=5,
+			do_not_save=1,
 		)
 
 		self.assertRaises(frappe.ValidationError, asset_2.save)
 
 	def test_depreciation_start_date_is_before_purchase_date(self):
 		asset = create_asset(
-			item_code = "Macbook Pro",
-			calculate_depreciation = 1,
-			available_for_use_date = "2019-12-31",
-			total_number_of_depreciations = 3,
-			expected_value_after_useful_life = 10000,
-			depreciation_start_date = "2014-07-01",
-			do_not_save = 1
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			available_for_use_date="2019-12-31",
+			total_number_of_depreciations=3,
+			expected_value_after_useful_life=10000,
+			depreciation_start_date="2014-07-01",
+			do_not_save=1,
 		)
 
 		self.assertRaises(frappe.ValidationError, asset.save)
 
 	def test_depreciation_start_date_is_before_available_for_use_date(self):
 		asset = create_asset(
-			item_code = "Macbook Pro",
-			calculate_depreciation = 1,
-			available_for_use_date = "2019-12-31",
-			total_number_of_depreciations = 3,
-			expected_value_after_useful_life = 10000,
-			depreciation_start_date = "2018-07-01",
-			do_not_save = 1
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			available_for_use_date="2019-12-31",
+			total_number_of_depreciations=3,
+			expected_value_after_useful_life=10000,
+			depreciation_start_date="2018-07-01",
+			do_not_save=1,
 		)
 
 		self.assertRaises(frappe.ValidationError, asset.save)
@@ -902,14 +979,14 @@
 		"""Tests if post_depreciation_entries() works as expected."""
 
 		asset = create_asset(
-			item_code = "Macbook Pro",
-			calculate_depreciation = 1,
-			available_for_use_date = "2019-12-31",
-			depreciation_start_date = "2020-12-31",
-			frequency_of_depreciation = 12,
-			total_number_of_depreciations = 3,
-			expected_value_after_useful_life = 10000,
-			submit = 1
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			available_for_use_date="2019-12-31",
+			depreciation_start_date="2020-12-31",
+			frequency_of_depreciation=12,
+			total_number_of_depreciations=3,
+			expected_value_after_useful_life=10000,
+			submit=1,
 		)
 
 		post_depreciation_entries(date="2021-06-01")
@@ -923,21 +1000,24 @@
 		"""Tests if the Depreciation Expense Account gets debited and the Accumulated Depreciation Account gets credited when the former's an Expense Account."""
 
 		asset = create_asset(
-			item_code = "Macbook Pro",
-			calculate_depreciation = 1,
-			available_for_use_date = "2019-12-31",
-			depreciation_start_date = "2020-12-31",
-			frequency_of_depreciation = 12,
-			total_number_of_depreciations = 3,
-			expected_value_after_useful_life = 10000,
-			submit = 1
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			available_for_use_date="2019-12-31",
+			depreciation_start_date="2020-12-31",
+			frequency_of_depreciation=12,
+			total_number_of_depreciations=3,
+			expected_value_after_useful_life=10000,
+			submit=1,
 		)
 
 		post_depreciation_entries(date="2021-06-01")
 		asset.load_from_db()
 
 		je = frappe.get_doc("Journal Entry", asset.schedules[0].journal_entry)
-		accounting_entries = [{"account": entry.account, "debit": entry.debit, "credit": entry.credit} for entry in je.accounts]
+		accounting_entries = [
+			{"account": entry.account, "debit": entry.debit, "credit": entry.credit}
+			for entry in je.accounts
+		]
 
 		for entry in accounting_entries:
 			if entry["account"] == "_Test Depreciations - _TC":
@@ -956,21 +1036,24 @@
 		depr_expense_account.save()
 
 		asset = create_asset(
-			item_code = "Macbook Pro",
-			calculate_depreciation = 1,
-			available_for_use_date = "2019-12-31",
-			depreciation_start_date = "2020-12-31",
-			frequency_of_depreciation = 12,
-			total_number_of_depreciations = 3,
-			expected_value_after_useful_life = 10000,
-			submit = 1
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			available_for_use_date="2019-12-31",
+			depreciation_start_date="2020-12-31",
+			frequency_of_depreciation=12,
+			total_number_of_depreciations=3,
+			expected_value_after_useful_life=10000,
+			submit=1,
 		)
 
 		post_depreciation_entries(date="2021-06-01")
 		asset.load_from_db()
 
 		je = frappe.get_doc("Journal Entry", asset.schedules[0].journal_entry)
-		accounting_entries = [{"account": entry.account, "debit": entry.debit, "credit": entry.credit} for entry in je.accounts]
+		accounting_entries = [
+			{"account": entry.account, "debit": entry.debit, "credit": entry.credit}
+			for entry in je.accounts
+		]
 
 		for entry in accounting_entries:
 			if entry["account"] == "_Test Depreciations - _TC":
@@ -989,14 +1072,14 @@
 		"""Tests if clear_depreciation_schedule() works as expected."""
 
 		asset = create_asset(
-			item_code = "Macbook Pro",
-			calculate_depreciation = 1,
-			available_for_use_date = "2019-12-31",
-			depreciation_start_date = "2020-12-31",
-			frequency_of_depreciation = 12,
-			total_number_of_depreciations = 3,
-			expected_value_after_useful_life = 10000,
-			submit = 1
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			available_for_use_date="2019-12-31",
+			depreciation_start_date="2020-12-31",
+			frequency_of_depreciation=12,
+			total_number_of_depreciations=3,
+			expected_value_after_useful_life=10000,
+			submit=1,
 		)
 
 		post_depreciation_entries(date="2021-06-01")
@@ -1007,34 +1090,39 @@
 		self.assertEqual(len(asset.schedules), 1)
 
 	def test_clear_depreciation_schedule_for_multiple_finance_books(self):
-		asset = create_asset(
-			item_code = "Macbook Pro",
-			available_for_use_date = "2019-12-31",
-			do_not_save = 1
-		)
+		asset = create_asset(item_code="Macbook Pro", available_for_use_date="2019-12-31", do_not_save=1)
 
 		asset.calculate_depreciation = 1
-		asset.append("finance_books", {
-			"depreciation_method": "Straight Line",
-			"frequency_of_depreciation": 1,
-			"total_number_of_depreciations": 3,
-			"expected_value_after_useful_life": 10000,
-			"depreciation_start_date": "2020-01-31"
-		})
-		asset.append("finance_books", {
-			"depreciation_method": "Straight Line",
-			"frequency_of_depreciation": 1,
-			"total_number_of_depreciations": 6,
-			"expected_value_after_useful_life": 10000,
-			"depreciation_start_date": "2020-01-31"
-		})
-		asset.append("finance_books", {
-			"depreciation_method": "Straight Line",
-			"frequency_of_depreciation": 12,
-			"total_number_of_depreciations": 3,
-			"expected_value_after_useful_life": 10000,
-			"depreciation_start_date": "2020-12-31"
-		})
+		asset.append(
+			"finance_books",
+			{
+				"depreciation_method": "Straight Line",
+				"frequency_of_depreciation": 1,
+				"total_number_of_depreciations": 3,
+				"expected_value_after_useful_life": 10000,
+				"depreciation_start_date": "2020-01-31",
+			},
+		)
+		asset.append(
+			"finance_books",
+			{
+				"depreciation_method": "Straight Line",
+				"frequency_of_depreciation": 1,
+				"total_number_of_depreciations": 6,
+				"expected_value_after_useful_life": 10000,
+				"depreciation_start_date": "2020-01-31",
+			},
+		)
+		asset.append(
+			"finance_books",
+			{
+				"depreciation_method": "Straight Line",
+				"frequency_of_depreciation": 12,
+				"total_number_of_depreciations": 3,
+				"expected_value_after_useful_life": 10000,
+				"depreciation_start_date": "2020-12-31",
+			},
+		)
 		asset.submit()
 
 		post_depreciation_entries(date="2020-04-01")
@@ -1051,27 +1139,29 @@
 				self.assertEqual(schedule.finance_book_id, "2")
 
 	def test_depreciation_schedules_are_set_up_for_multiple_finance_books(self):
-		asset = create_asset(
-			item_code = "Macbook Pro",
-			available_for_use_date = "2019-12-31",
-			do_not_save = 1
-		)
+		asset = create_asset(item_code="Macbook Pro", available_for_use_date="2019-12-31", do_not_save=1)
 
 		asset.calculate_depreciation = 1
-		asset.append("finance_books", {
-			"depreciation_method": "Straight Line",
-			"frequency_of_depreciation": 12,
-			"total_number_of_depreciations": 3,
-			"expected_value_after_useful_life": 10000,
-			"depreciation_start_date": "2020-12-31"
-		})
-		asset.append("finance_books", {
-			"depreciation_method": "Straight Line",
-			"frequency_of_depreciation": 12,
-			"total_number_of_depreciations": 6,
-			"expected_value_after_useful_life": 10000,
-			"depreciation_start_date": "2020-12-31"
-		})
+		asset.append(
+			"finance_books",
+			{
+				"depreciation_method": "Straight Line",
+				"frequency_of_depreciation": 12,
+				"total_number_of_depreciations": 3,
+				"expected_value_after_useful_life": 10000,
+				"depreciation_start_date": "2020-12-31",
+			},
+		)
+		asset.append(
+			"finance_books",
+			{
+				"depreciation_method": "Straight Line",
+				"frequency_of_depreciation": 12,
+				"total_number_of_depreciations": 6,
+				"expected_value_after_useful_life": 10000,
+				"depreciation_start_date": "2020-12-31",
+			},
+		)
 		asset.save()
 
 		self.assertEqual(len(asset.schedules), 9)
@@ -1084,15 +1174,15 @@
 
 	def test_depreciation_entry_cancellation(self):
 		asset = create_asset(
-			item_code = "Macbook Pro",
-			calculate_depreciation = 1,
-			purchase_date = "2020-06-06",
-			available_for_use_date = "2020-06-06",
-			depreciation_start_date = "2020-12-31",
-			frequency_of_depreciation = 10,
-			total_number_of_depreciations = 3,
-			expected_value_after_useful_life = 10000,
-			submit = 1
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			purchase_date="2020-06-06",
+			available_for_use_date="2020-06-06",
+			depreciation_start_date="2020-12-31",
+			frequency_of_depreciation=10,
+			total_number_of_depreciations=3,
+			expected_value_after_useful_life=10000,
+			submit=1,
 		)
 
 		post_depreciation_entries(date="2021-01-01")
@@ -1110,34 +1200,38 @@
 
 	def test_asset_expected_value_after_useful_life(self):
 		asset = create_asset(
-			item_code = "Macbook Pro",
-			calculate_depreciation = 1,
-			available_for_use_date = "2020-06-06",
-			purchase_date = "2020-06-06",
-			frequency_of_depreciation = 10,
-			total_number_of_depreciations = 3,
-			expected_value_after_useful_life = 10000
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			available_for_use_date="2020-06-06",
+			purchase_date="2020-06-06",
+			frequency_of_depreciation=10,
+			total_number_of_depreciations=3,
+			expected_value_after_useful_life=10000,
 		)
 
-		accumulated_depreciation_after_full_schedule = \
-			max(d.accumulated_depreciation_amount for d in asset.get("schedules"))
+		accumulated_depreciation_after_full_schedule = max(
+			d.accumulated_depreciation_amount for d in asset.get("schedules")
+		)
 
-		asset_value_after_full_schedule = (flt(asset.gross_purchase_amount) -
-			flt(accumulated_depreciation_after_full_schedule))
+		asset_value_after_full_schedule = flt(asset.gross_purchase_amount) - flt(
+			accumulated_depreciation_after_full_schedule
+		)
 
-		self.assertTrue(asset.finance_books[0].expected_value_after_useful_life >= asset_value_after_full_schedule)
+		self.assertTrue(
+			asset.finance_books[0].expected_value_after_useful_life >= asset_value_after_full_schedule
+		)
 
 	def test_gle_made_by_depreciation_entries(self):
 		asset = create_asset(
-			item_code = "Macbook Pro",
-			calculate_depreciation = 1,
-			purchase_date = "2020-01-30",
-			available_for_use_date = "2020-01-30",
-			depreciation_start_date = "2020-12-31",
-			frequency_of_depreciation = 10,
-			total_number_of_depreciations = 3,
-			expected_value_after_useful_life = 10000,
-			submit = 1
+			item_code="Macbook Pro",
+			calculate_depreciation=1,
+			purchase_date="2020-01-30",
+			available_for_use_date="2020-01-30",
+			depreciation_start_date="2020-12-31",
+			frequency_of_depreciation=10,
+			total_number_of_depreciations=3,
+			expected_value_after_useful_life=10000,
+			submit=1,
 		)
 
 		self.assertEqual(asset.status, "Submitted")
@@ -1151,20 +1245,23 @@
 
 		expected_gle = (
 			("_Test Accumulated Depreciations - _TC", 0.0, 30000.0),
-			("_Test Depreciations - _TC", 30000.0, 0.0)
+			("_Test Depreciations - _TC", 30000.0, 0.0),
 		)
 
-		gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
+		gle = frappe.db.sql(
+			"""select account, debit, credit from `tabGL Entry`
 			where against_voucher_type='Asset' and against_voucher = %s
-			order by account""", asset.name)
+			order by account""",
+			asset.name,
+		)
 
 		self.assertEqual(gle, expected_gle)
 		self.assertEqual(asset.get("value_after_depreciation"), 0)
 
 	def test_expected_value_change(self):
 		"""
-			tests if changing `expected_value_after_useful_life`
-			affects `value_after_depreciation`
+		tests if changing `expected_value_after_useful_life`
+		affects `value_after_depreciation`
 		"""
 
 		asset = create_asset(calculate_depreciation=1)
@@ -1183,7 +1280,7 @@
 		self.assertEquals(asset.finance_books[0].value_after_depreciation, 98000.0)
 
 	def test_asset_cost_center(self):
-		asset = create_asset(is_existing_asset = 1, do_not_save=1)
+		asset = create_asset(is_existing_asset=1, do_not_save=1)
 		asset.cost_center = "Main - WP"
 
 		self.assertRaises(frappe.ValidationError, asset.submit)
@@ -1191,6 +1288,7 @@
 		asset.cost_center = "Main - _TC"
 		asset.submit()
 
+
 def create_asset_data():
 	if not frappe.db.exists("Asset Category", "Computers"):
 		create_asset_category()
@@ -1199,45 +1297,48 @@
 		create_fixed_asset_item()
 
 	if not frappe.db.exists("Location", "Test Location"):
-		frappe.get_doc({
-			'doctype': 'Location',
-			'location_name': 'Test Location'
-		}).insert()
+		frappe.get_doc({"doctype": "Location", "location_name": "Test Location"}).insert()
+
 
 def create_asset(**args):
 	args = frappe._dict(args)
 
 	create_asset_data()
 
-	asset = frappe.get_doc({
-		"doctype": "Asset",
-		"asset_name": args.asset_name or "Macbook Pro 1",
-		"asset_category": args.asset_category or "Computers",
-		"item_code": args.item_code or "Macbook Pro",
-		"company": args.company or "_Test Company",
-		"purchase_date": args.purchase_date or "2015-01-01",
-		"calculate_depreciation": args.calculate_depreciation or 0,
-		"opening_accumulated_depreciation": args.opening_accumulated_depreciation or 0,
-		"number_of_depreciations_booked": args.number_of_depreciations_booked or 0,
-		"gross_purchase_amount": args.gross_purchase_amount or 100000,
-		"purchase_receipt_amount": args.purchase_receipt_amount or 100000,
-		"warehouse": args.warehouse or "_Test Warehouse - _TC",
-		"available_for_use_date": args.available_for_use_date or "2020-06-06",
-		"location": args.location or "Test Location",
-		"asset_owner": args.asset_owner or "Company",
-		"is_existing_asset": args.is_existing_asset or 1,
-		"asset_quantity": args.get("asset_quantity") or 1
-	})
+	asset = frappe.get_doc(
+		{
+			"doctype": "Asset",
+			"asset_name": args.asset_name or "Macbook Pro 1",
+			"asset_category": args.asset_category or "Computers",
+			"item_code": args.item_code or "Macbook Pro",
+			"company": args.company or "_Test Company",
+			"purchase_date": args.purchase_date or "2015-01-01",
+			"calculate_depreciation": args.calculate_depreciation or 0,
+			"opening_accumulated_depreciation": args.opening_accumulated_depreciation or 0,
+			"number_of_depreciations_booked": args.number_of_depreciations_booked or 0,
+			"gross_purchase_amount": args.gross_purchase_amount or 100000,
+			"purchase_receipt_amount": args.purchase_receipt_amount or 100000,
+			"warehouse": args.warehouse or "_Test Warehouse - _TC",
+			"available_for_use_date": args.available_for_use_date or "2020-06-06",
+			"location": args.location or "Test Location",
+			"asset_owner": args.asset_owner or "Company",
+			"is_existing_asset": args.is_existing_asset or 1,
+			"asset_quantity": args.get("asset_quantity") or 1,
+		}
+	)
 
 	if asset.calculate_depreciation:
-		asset.append("finance_books", {
-			"finance_book": args.finance_book,
-			"depreciation_method": args.depreciation_method or "Straight Line",
-			"frequency_of_depreciation": args.frequency_of_depreciation or 12,
-			"total_number_of_depreciations": args.total_number_of_depreciations or 5,
-			"expected_value_after_useful_life": args.expected_value_after_useful_life or 0,
-			"depreciation_start_date": args.depreciation_start_date
-		})
+		asset.append(
+			"finance_books",
+			{
+				"finance_book": args.finance_book,
+				"depreciation_method": args.depreciation_method or "Straight Line",
+				"frequency_of_depreciation": args.frequency_of_depreciation or 12,
+				"total_number_of_depreciations": args.total_number_of_depreciations or 5,
+				"expected_value_after_useful_life": args.expected_value_after_useful_life or 0,
+				"depreciation_start_date": args.depreciation_start_date,
+			},
+		)
 
 	if not args.do_not_save:
 		try:
@@ -1250,43 +1351,51 @@
 
 	return asset
 
+
 def create_asset_category():
 	asset_category = frappe.new_doc("Asset Category")
 	asset_category.asset_category_name = "Computers"
 	asset_category.total_number_of_depreciations = 3
 	asset_category.frequency_of_depreciation = 3
 	asset_category.enable_cwip_accounting = 1
-	asset_category.append("accounts", {
-		"company_name": "_Test Company",
-		"fixed_asset_account": "_Test Fixed Asset - _TC",
-		"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
-		"depreciation_expense_account": "_Test Depreciations - _TC"
-	})
+	asset_category.append(
+		"accounts",
+		{
+			"company_name": "_Test Company",
+			"fixed_asset_account": "_Test Fixed Asset - _TC",
+			"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
+			"depreciation_expense_account": "_Test Depreciations - _TC",
+		},
+	)
 	asset_category.insert()
 
+
 def create_fixed_asset_item(item_code=None, auto_create_assets=1, is_grouped_asset=0):
-	meta = frappe.get_meta('Asset')
-	naming_series = meta.get_field("naming_series").options.splitlines()[0] or 'ACC-ASS-.YYYY.-'
+	meta = frappe.get_meta("Asset")
+	naming_series = meta.get_field("naming_series").options.splitlines()[0] or "ACC-ASS-.YYYY.-"
 	try:
-		item = frappe.get_doc({
-			"doctype": "Item",
-			"item_code": item_code or "Macbook Pro",
-			"item_name": "Macbook Pro",
-			"description": "Macbook Pro Retina Display",
-			"asset_category": "Computers",
-			"item_group": "All Item Groups",
-			"stock_uom": "Nos",
-			"is_stock_item": 0,
-			"is_fixed_asset": 1,
-			"auto_create_assets": auto_create_assets,
-			"is_grouped_asset": is_grouped_asset,
-			"asset_naming_series": naming_series
-		})
+		item = frappe.get_doc(
+			{
+				"doctype": "Item",
+				"item_code": item_code or "Macbook Pro",
+				"item_name": "Macbook Pro",
+				"description": "Macbook Pro Retina Display",
+				"asset_category": "Computers",
+				"item_group": "All Item Groups",
+				"stock_uom": "Nos",
+				"is_stock_item": 0,
+				"is_fixed_asset": 1,
+				"auto_create_assets": auto_create_assets,
+				"is_grouped_asset": is_grouped_asset,
+				"asset_naming_series": naming_series,
+			}
+		)
 		item.insert(ignore_if_duplicate=True)
 	except frappe.DuplicateEntryError:
 		pass
 	return item
 
+
 def set_depreciation_settings_in_company():
 	company = frappe.get_doc("Company", "_Test Company")
 	company.accumulated_depreciation_account = "_Test Accumulated Depreciations - _TC"
@@ -1298,5 +1407,6 @@
 	# Enable booking asset depreciation entry automatically
 	frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1)
 
+
 def enable_cwip_accounting(asset_category, enable=1):
 	frappe.db.set_value("Asset Category", asset_category, "enable_cwip_accounting", enable)
diff --git a/erpnext/assets/doctype/asset_category/asset_category.py b/erpnext/assets/doctype/asset_category/asset_category.py
index bd573bf..a4d2c82 100644
--- a/erpnext/assets/doctype/asset_category/asset_category.py
+++ b/erpnext/assets/doctype/asset_category/asset_category.py
@@ -18,79 +18,104 @@
 	def validate_finance_books(self):
 		for d in self.finance_books:
 			for field in ("Total Number of Depreciations", "Frequency of Depreciation"):
-				if cint(d.get(frappe.scrub(field)))<1:
-					frappe.throw(_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError)
+				if cint(d.get(frappe.scrub(field))) < 1:
+					frappe.throw(
+						_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError
+					)
 
 	def validate_account_currency(self):
 		account_types = [
-			'fixed_asset_account', 'accumulated_depreciation_account', 'depreciation_expense_account', 'capital_work_in_progress_account'
+			"fixed_asset_account",
+			"accumulated_depreciation_account",
+			"depreciation_expense_account",
+			"capital_work_in_progress_account",
 		]
 		invalid_accounts = []
 		for d in self.accounts:
-			company_currency = frappe.get_value('Company', d.get('company_name'), 'default_currency')
+			company_currency = frappe.get_value("Company", d.get("company_name"), "default_currency")
 			for type_of_account in account_types:
 				if d.get(type_of_account):
 					account_currency = frappe.get_value("Account", d.get(type_of_account), "account_currency")
 					if account_currency != company_currency:
-						invalid_accounts.append(frappe._dict({ 'type': type_of_account, 'idx': d.idx, 'account': d.get(type_of_account) }))
+						invalid_accounts.append(
+							frappe._dict({"type": type_of_account, "idx": d.idx, "account": d.get(type_of_account)})
+						)
 
 		for d in invalid_accounts:
-			frappe.throw(_("Row #{}: Currency of {} - {} doesn't matches company currency.")
-				.format(d.idx, frappe.bold(frappe.unscrub(d.type)), frappe.bold(d.account)),
-				title=_("Invalid Account"))
-
+			frappe.throw(
+				_("Row #{}: Currency of {} - {} doesn't matches company currency.").format(
+					d.idx, frappe.bold(frappe.unscrub(d.type)), frappe.bold(d.account)
+				),
+				title=_("Invalid Account"),
+			)
 
 	def validate_account_types(self):
 		account_type_map = {
-			'fixed_asset_account': {'account_type': ['Fixed Asset']},
-			'accumulated_depreciation_account': {'account_type': ['Accumulated Depreciation']},
-			'depreciation_expense_account': {'root_type': ['Expense', 'Income']},
-			'capital_work_in_progress_account': {'account_type': ['Capital Work in Progress']}
+			"fixed_asset_account": {"account_type": ["Fixed Asset"]},
+			"accumulated_depreciation_account": {"account_type": ["Accumulated Depreciation"]},
+			"depreciation_expense_account": {"root_type": ["Expense", "Income"]},
+			"capital_work_in_progress_account": {"account_type": ["Capital Work in Progress"]},
 		}
 		for d in self.accounts:
 			for fieldname in account_type_map.keys():
 				if d.get(fieldname):
 					selected_account = d.get(fieldname)
-					key_to_match = next(iter(account_type_map.get(fieldname))) # acount_type or root_type
-					selected_key_type = frappe.db.get_value('Account', selected_account, key_to_match)
+					key_to_match = next(iter(account_type_map.get(fieldname)))  # acount_type or root_type
+					selected_key_type = frappe.db.get_value("Account", selected_account, key_to_match)
 					expected_key_types = account_type_map[fieldname][key_to_match]
 
 					if selected_key_type not in expected_key_types:
-						frappe.throw(_("Row #{}: {} of {} should be {}. Please modify the account or select a different account.")
-							.format(d.idx, frappe.unscrub(key_to_match), frappe.bold(selected_account), frappe.bold(expected_key_types)),
-							title=_("Invalid Account"))
+						frappe.throw(
+							_(
+								"Row #{}: {} of {} should be {}. Please modify the account or select a different account."
+							).format(
+								d.idx,
+								frappe.unscrub(key_to_match),
+								frappe.bold(selected_account),
+								frappe.bold(expected_key_types),
+							),
+							title=_("Invalid Account"),
+						)
 
 	def valide_cwip_account(self):
 		if self.enable_cwip_accounting:
 			missing_cwip_accounts_for_company = []
 			for d in self.accounts:
-				if (not d.capital_work_in_progress_account and
-					not frappe.db.get_value("Company", d.company_name, "capital_work_in_progress_account")):
+				if not d.capital_work_in_progress_account and not frappe.db.get_value(
+					"Company", d.company_name, "capital_work_in_progress_account"
+				):
 					missing_cwip_accounts_for_company.append(get_link_to_form("Company", d.company_name))
 
 			if missing_cwip_accounts_for_company:
-				msg = _("""To enable Capital Work in Progress Accounting, """)
+				msg = _("""To enable Capital Work in Progress Accounting,""") + " "
 				msg += _("""you must select Capital Work in Progress Account in accounts table""")
 				msg += "<br><br>"
-				msg += _("You can also set default CWIP account in Company {}").format(", ".join(missing_cwip_accounts_for_company))
+				msg += _("You can also set default CWIP account in Company {}").format(
+					", ".join(missing_cwip_accounts_for_company)
+				)
 				frappe.throw(msg, title=_("Missing Account"))
 
 
 @frappe.whitelist()
-def get_asset_category_account(fieldname, item=None, asset=None, account=None, asset_category = None, company = None):
+def get_asset_category_account(
+	fieldname, item=None, asset=None, account=None, asset_category=None, company=None
+):
 	if item and frappe.db.get_value("Item", item, "is_fixed_asset"):
 		asset_category = frappe.db.get_value("Item", item, ["asset_category"])
 
 	elif not asset_category or not company:
 		if account:
 			if frappe.db.get_value("Account", account, "account_type") != "Fixed Asset":
-				account=None
+				account = None
 
 		if not account:
 			asset_details = frappe.db.get_value("Asset", asset, ["asset_category", "company"])
 			asset_category, company = asset_details or [None, None]
 
-	account = frappe.db.get_value("Asset Category Account",
-		filters={"parent": asset_category, "company_name": company}, fieldname=fieldname)
+	account = frappe.db.get_value(
+		"Asset Category Account",
+		filters={"parent": asset_category, "company_name": company},
+		fieldname=fieldname,
+	)
 
 	return account
diff --git a/erpnext/assets/doctype/asset_category/test_asset_category.py b/erpnext/assets/doctype/asset_category/test_asset_category.py
index 2f52248..2c92656 100644
--- a/erpnext/assets/doctype/asset_category/test_asset_category.py
+++ b/erpnext/assets/doctype/asset_category/test_asset_category.py
@@ -15,12 +15,15 @@
 
 		asset_category.total_number_of_depreciations = 3
 		asset_category.frequency_of_depreciation = 3
-		asset_category.append("accounts", {
-			"company_name": "_Test Company",
-			"fixed_asset_account": "_Test Fixed Asset - _TC",
-			"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
-			"depreciation_expense_account": "_Test Depreciations - _TC"
-		})
+		asset_category.append(
+			"accounts",
+			{
+				"company_name": "_Test Company",
+				"fixed_asset_account": "_Test Fixed Asset - _TC",
+				"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
+				"depreciation_expense_account": "_Test Depreciations - _TC",
+			},
+		)
 
 		try:
 			asset_category.insert(ignore_if_duplicate=True)
@@ -28,7 +31,9 @@
 			pass
 
 	def test_cwip_accounting(self):
-		company_cwip_acc = frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account")
+		company_cwip_acc = frappe.db.get_value(
+			"Company", "_Test Company", "capital_work_in_progress_account"
+		)
 		frappe.db.set_value("Company", "_Test Company", "capital_work_in_progress_account", "")
 
 		asset_category = frappe.new_doc("Asset Category")
@@ -37,11 +42,14 @@
 
 		asset_category.total_number_of_depreciations = 3
 		asset_category.frequency_of_depreciation = 3
-		asset_category.append("accounts", {
-			"company_name": "_Test Company",
-			"fixed_asset_account": "_Test Fixed Asset - _TC",
-			"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
-			"depreciation_expense_account": "_Test Depreciations - _TC"
-		})
+		asset_category.append(
+			"accounts",
+			{
+				"company_name": "_Test Company",
+				"fixed_asset_account": "_Test Fixed Asset - _TC",
+				"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
+				"depreciation_expense_account": "_Test Depreciations - _TC",
+			},
+		)
 
 		self.assertRaises(frappe.ValidationError, asset_category.insert)
diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js
index 52996e9..5c03b98 100644
--- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js
+++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js
@@ -48,7 +48,7 @@
 							<div class='col-sm-3 small'>
 								<a onclick="frappe.set_route('List', 'Asset Maintenance Log',
 									{'asset_name': '${d.asset_name}','maintenance_status': '${d.maintenance_status}' });">
-									${d.maintenance_status} <span class="badge">${d.count}</span>
+									${__(d.maintenance_status)} <span class="badge">${d.count}</span>
 								</a>
 							</div>
 						</div>`).appendTo(rows);
diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
index 4fc4c4c..0028d84 100644
--- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
+++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
@@ -11,7 +11,7 @@
 
 class AssetMaintenance(Document):
 	def validate(self):
-		for task in self.get('asset_maintenance_tasks'):
+		for task in self.get("asset_maintenance_tasks"):
 			if task.end_date and (getdate(task.start_date) >= getdate(task.end_date)):
 				throw(_("Start date should be less than end date for task {0}").format(task.maintenance_task))
 			if getdate(task.next_due_date) < getdate(nowdate()):
@@ -20,83 +20,111 @@
 				throw(_("Row #{}: Please asign task to a member.").format(task.idx))
 
 	def on_update(self):
-		for task in self.get('asset_maintenance_tasks'):
+		for task in self.get("asset_maintenance_tasks"):
 			assign_tasks(self.name, task.assign_to, task.maintenance_task, task.next_due_date)
 		self.sync_maintenance_tasks()
 
 	def sync_maintenance_tasks(self):
 		tasks_names = []
-		for task in self.get('asset_maintenance_tasks'):
+		for task in self.get("asset_maintenance_tasks"):
 			tasks_names.append(task.name)
-			update_maintenance_log(asset_maintenance = self.name, item_code = self.item_code, item_name = self.item_name, task = task)
-		asset_maintenance_logs = frappe.get_all("Asset Maintenance Log", fields=["name"], filters = {"asset_maintenance": self.name,
-			"task": ("not in", tasks_names)})
+			update_maintenance_log(
+				asset_maintenance=self.name, item_code=self.item_code, item_name=self.item_name, task=task
+			)
+		asset_maintenance_logs = frappe.get_all(
+			"Asset Maintenance Log",
+			fields=["name"],
+			filters={"asset_maintenance": self.name, "task": ("not in", tasks_names)},
+		)
 		if asset_maintenance_logs:
 			for asset_maintenance_log in asset_maintenance_logs:
-				maintenance_log = frappe.get_doc('Asset Maintenance Log', asset_maintenance_log.name)
-				maintenance_log.db_set('maintenance_status', 'Cancelled')
+				maintenance_log = frappe.get_doc("Asset Maintenance Log", asset_maintenance_log.name)
+				maintenance_log.db_set("maintenance_status", "Cancelled")
+
 
 @frappe.whitelist()
 def assign_tasks(asset_maintenance_name, assign_to_member, maintenance_task, next_due_date):
-	team_member = frappe.db.get_value('User', assign_to_member, "email")
+	team_member = frappe.db.get_value("User", assign_to_member, "email")
 	args = {
-		'doctype' : 'Asset Maintenance',
-		'assign_to' : [team_member],
-		'name' : asset_maintenance_name,
-		'description' : maintenance_task,
-		'date' : next_due_date
+		"doctype": "Asset Maintenance",
+		"assign_to": team_member,
+		"name": asset_maintenance_name,
+		"description": maintenance_task,
+		"date": next_due_date,
 	}
-	if not frappe.db.sql("""select owner from `tabToDo`
-		where reference_type=%(doctype)s and reference_name=%(name)s and status="Open"
-		and owner=%(assign_to)s""", args):
+	if not frappe.db.sql(
+		"""select owner from `tabToDo`
+		where reference_type=%(doctype)s and reference_name=%(name)s and status='Open'
+		and owner=%(assign_to)s""",
+		args,
+	):
+		# assign_to function expects a list
+		args["assign_to"] = [args["assign_to"]]
 		assign_to.add(args)
 
+
 @frappe.whitelist()
-def calculate_next_due_date(periodicity, start_date = None, end_date = None, last_completion_date = None, next_due_date = None):
+def calculate_next_due_date(
+	periodicity, start_date=None, end_date=None, last_completion_date=None, next_due_date=None
+):
 	if not start_date and not last_completion_date:
 		start_date = frappe.utils.now()
 
-	if last_completion_date and ((start_date and last_completion_date > start_date) or not start_date):
+	if last_completion_date and (
+		(start_date and last_completion_date > start_date) or not start_date
+	):
 		start_date = last_completion_date
-	if periodicity == 'Daily':
+	if periodicity == "Daily":
 		next_due_date = add_days(start_date, 1)
-	if periodicity == 'Weekly':
+	if periodicity == "Weekly":
 		next_due_date = add_days(start_date, 7)
-	if periodicity == 'Monthly':
+	if periodicity == "Monthly":
 		next_due_date = add_months(start_date, 1)
-	if periodicity == 'Yearly':
+	if periodicity == "Yearly":
 		next_due_date = add_years(start_date, 1)
-	if periodicity == '2 Yearly':
+	if periodicity == "2 Yearly":
 		next_due_date = add_years(start_date, 2)
-	if periodicity == 'Quarterly':
+	if periodicity == "Quarterly":
 		next_due_date = add_months(start_date, 3)
-	if end_date and ((start_date and start_date >= end_date) or (last_completion_date and last_completion_date >= end_date) or next_due_date):
+	if end_date and (
+		(start_date and start_date >= end_date)
+		or (last_completion_date and last_completion_date >= end_date)
+		or next_due_date
+	):
 		next_due_date = ""
 	return next_due_date
 
 
 def update_maintenance_log(asset_maintenance, item_code, item_name, task):
-	asset_maintenance_log = frappe.get_value("Asset Maintenance Log", {"asset_maintenance": asset_maintenance,
-		"task": task.name, "maintenance_status": ('in',['Planned','Overdue'])})
+	asset_maintenance_log = frappe.get_value(
+		"Asset Maintenance Log",
+		{
+			"asset_maintenance": asset_maintenance,
+			"task": task.name,
+			"maintenance_status": ("in", ["Planned", "Overdue"]),
+		},
+	)
 
 	if not asset_maintenance_log:
-		asset_maintenance_log = frappe.get_doc({
-			"doctype": "Asset Maintenance Log",
-			"asset_maintenance": asset_maintenance,
-			"asset_name": asset_maintenance,
-			"item_code": item_code,
-			"item_name": item_name,
-			"task": task.name,
-			"has_certificate": task.certificate_required,
-			"description": task.description,
-			"assign_to_name": task.assign_to_name,
-			"periodicity": str(task.periodicity),
-			"maintenance_type": task.maintenance_type,
-			"due_date": task.next_due_date
-		})
+		asset_maintenance_log = frappe.get_doc(
+			{
+				"doctype": "Asset Maintenance Log",
+				"asset_maintenance": asset_maintenance,
+				"asset_name": asset_maintenance,
+				"item_code": item_code,
+				"item_name": item_name,
+				"task": task.name,
+				"has_certificate": task.certificate_required,
+				"description": task.description,
+				"assign_to_name": task.assign_to_name,
+				"periodicity": str(task.periodicity),
+				"maintenance_type": task.maintenance_type,
+				"due_date": task.next_due_date,
+			}
+		)
 		asset_maintenance_log.insert()
 	else:
-		maintenance_log = frappe.get_doc('Asset Maintenance Log', asset_maintenance_log)
+		maintenance_log = frappe.get_doc("Asset Maintenance Log", asset_maintenance_log)
 		maintenance_log.assign_to_name = task.assign_to_name
 		maintenance_log.has_certificate = task.certificate_required
 		maintenance_log.description = task.description
@@ -105,15 +133,22 @@
 		maintenance_log.due_date = task.next_due_date
 		maintenance_log.save()
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_team_members(doctype, txt, searchfield, start, page_len, filters):
-	return frappe.db.get_values('Maintenance Team Member', { 'parent': filters.get("maintenance_team") }, "team_member")
+	return frappe.db.get_values(
+		"Maintenance Team Member", {"parent": filters.get("maintenance_team")}, "team_member"
+	)
+
 
 @frappe.whitelist()
 def get_maintenance_log(asset_name):
-    return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
         select maintenance_status, count(asset_name) as count, asset_name
         from `tabAsset Maintenance Log`
         where asset_name=%s group by maintenance_status""",
-        (asset_name), as_dict=1)
+		(asset_name),
+		as_dict=1,
+	)
diff --git a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py
index 8acb61b..e40a551 100644
--- a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py
+++ b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py
@@ -17,11 +17,12 @@
 		create_maintenance_team()
 
 	def test_create_asset_maintenance(self):
-		pr = make_purchase_receipt(item_code="Photocopier",
-			qty=1, rate=100000.0, location="Test Location")
+		pr = make_purchase_receipt(
+			item_code="Photocopier", qty=1, rate=100000.0, location="Test Location"
+		)
 
-		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
-		asset_doc = frappe.get_doc('Asset', asset_name)
+		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name")
+		asset_doc = frappe.get_doc("Asset", asset_name)
 		month_end_date = get_last_day(nowdate())
 
 		purchase_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15)
@@ -30,66 +31,74 @@
 		asset_doc.purchase_date = purchase_date
 
 		asset_doc.calculate_depreciation = 1
-		asset_doc.append("finance_books", {
-			"expected_value_after_useful_life": 200,
-			"depreciation_method": "Straight Line",
-			"total_number_of_depreciations": 3,
-			"frequency_of_depreciation": 10,
-			"depreciation_start_date": month_end_date
-		})
+		asset_doc.append(
+			"finance_books",
+			{
+				"expected_value_after_useful_life": 200,
+				"depreciation_method": "Straight Line",
+				"total_number_of_depreciations": 3,
+				"frequency_of_depreciation": 10,
+				"depreciation_start_date": month_end_date,
+			},
+		)
 
 		asset_doc.save()
 
 		if not frappe.db.exists("Asset Maintenance", "Photocopier"):
-			asset_maintenance =	frappe.get_doc({
+			asset_maintenance = frappe.get_doc(
+				{
 					"doctype": "Asset Maintenance",
 					"asset_name": "Photocopier",
 					"maintenance_team": "Team Awesome",
 					"company": "_Test Company",
-					"asset_maintenance_tasks": get_maintenance_tasks()
-				}).insert()
+					"asset_maintenance_tasks": get_maintenance_tasks(),
+				}
+			).insert()
 
 			next_due_date = calculate_next_due_date(nowdate(), "Monthly")
 			self.assertEqual(asset_maintenance.asset_maintenance_tasks[0].next_due_date, next_due_date)
 
 	def test_create_asset_maintenance_log(self):
 		if not frappe.db.exists("Asset Maintenance Log", "Photocopier"):
-			asset_maintenance_log =	frappe.get_doc({
+			asset_maintenance_log = frappe.get_doc(
+				{
 					"doctype": "Asset Maintenance Log",
 					"asset_maintenance": "Photocopier",
 					"task": "Change Oil",
 					"completion_date": add_days(nowdate(), 2),
-					"maintenance_status": "Completed"
-				}).insert()
+					"maintenance_status": "Completed",
+				}
+			).insert()
 		asset_maintenance = frappe.get_doc("Asset Maintenance", "Photocopier")
 		next_due_date = calculate_next_due_date(asset_maintenance_log.completion_date, "Monthly")
 		self.assertEqual(asset_maintenance.asset_maintenance_tasks[0].next_due_date, next_due_date)
 
+
 def create_asset_data():
 	if not frappe.db.exists("Asset Category", "Equipment"):
 		create_asset_category()
 
 	if not frappe.db.exists("Location", "Test Location"):
-		frappe.get_doc({
-			'doctype': 'Location',
-			'location_name': 'Test Location'
-		}).insert()
+		frappe.get_doc({"doctype": "Location", "location_name": "Test Location"}).insert()
 
 	if not frappe.db.exists("Item", "Photocopier"):
-		meta = frappe.get_meta('Asset')
+		meta = frappe.get_meta("Asset")
 		naming_series = meta.get_field("naming_series").options
-		frappe.get_doc({
-			"doctype": "Item",
-			"item_code": "Photocopier",
-			"item_name": "Photocopier",
-			"item_group": "All Item Groups",
-			"company": "_Test Company",
-			"is_fixed_asset": 1,
-			"is_stock_item": 0,
-			"asset_category": "Equipment",
-			"auto_create_assets": 1,
-			"asset_naming_series": naming_series
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Item",
+				"item_code": "Photocopier",
+				"item_name": "Photocopier",
+				"item_group": "All Item Groups",
+				"company": "_Test Company",
+				"is_fixed_asset": 1,
+				"is_stock_item": 0,
+				"asset_category": "Equipment",
+				"auto_create_assets": 1,
+				"asset_naming_series": naming_series,
+			}
+		).insert()
+
 
 def create_maintenance_team():
 	user_list = ["marcus@abc.com", "thalia@abc.com", "mathias@abc.com"]
@@ -97,60 +106,73 @@
 		frappe.get_doc({"doctype": "Role", "role_name": "Technician"}).insert()
 	for user in user_list:
 		if not frappe.db.get_value("User", user):
-			frappe.get_doc({
-				"doctype": "User",
-				"email": user,
-				"first_name": user,
-				"new_password": "password",
-				"roles": [{"doctype": "Has Role", "role": "Technician"}]
-			}).insert()
+			frappe.get_doc(
+				{
+					"doctype": "User",
+					"email": user,
+					"first_name": user,
+					"new_password": "password",
+					"roles": [{"doctype": "Has Role", "role": "Technician"}],
+				}
+			).insert()
 
 	if not frappe.db.exists("Asset Maintenance Team", "Team Awesome"):
-		frappe.get_doc({
-			"doctype": "Asset Maintenance Team",
-			"maintenance_manager": "marcus@abc.com",
-			"maintenance_team_name": "Team Awesome",
-			"company": "_Test Company",
-			"maintenance_team_members": get_maintenance_team(user_list)
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Asset Maintenance Team",
+				"maintenance_manager": "marcus@abc.com",
+				"maintenance_team_name": "Team Awesome",
+				"company": "_Test Company",
+				"maintenance_team_members": get_maintenance_team(user_list),
+			}
+		).insert()
+
 
 def get_maintenance_team(user_list):
-	return [{"team_member": user,
-			"full_name": user,
-			"maintenance_role": "Technician"
-			}
-		for user in user_list[1:]]
+	return [
+		{"team_member": user, "full_name": user, "maintenance_role": "Technician"}
+		for user in user_list[1:]
+	]
+
 
 def get_maintenance_tasks():
-	return [{"maintenance_task": "Change Oil",
+	return [
+		{
+			"maintenance_task": "Change Oil",
 			"start_date": nowdate(),
 			"periodicity": "Monthly",
 			"maintenance_type": "Preventive Maintenance",
 			"maintenance_status": "Planned",
-			"assign_to": "marcus@abc.com"
-			},
-			{"maintenance_task": "Check Gears",
+			"assign_to": "marcus@abc.com",
+		},
+		{
+			"maintenance_task": "Check Gears",
 			"start_date": nowdate(),
 			"periodicity": "Yearly",
 			"maintenance_type": "Calibration",
 			"maintenance_status": "Planned",
-			"assign_to": "thalia@abc.com"
-			}
-		]
+			"assign_to": "thalia@abc.com",
+		},
+	]
+
 
 def create_asset_category():
 	asset_category = frappe.new_doc("Asset Category")
 	asset_category.asset_category_name = "Equipment"
 	asset_category.total_number_of_depreciations = 3
 	asset_category.frequency_of_depreciation = 3
-	asset_category.append("accounts", {
-		"company_name": "_Test Company",
-		"fixed_asset_account": "_Test Fixed Asset - _TC",
-		"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
-		"depreciation_expense_account": "_Test Depreciations - _TC"
-	})
+	asset_category.append(
+		"accounts",
+		{
+			"company_name": "_Test Company",
+			"fixed_asset_account": "_Test Fixed Asset - _TC",
+			"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
+			"depreciation_expense_account": "_Test Depreciations - _TC",
+		},
+	)
 	asset_category.insert()
 
+
 def set_depreciation_settings_in_company():
 	company = frappe.get_doc("Company", "_Test Company")
 	company.accumulated_depreciation_account = "_Test Accumulated Depreciations - _TC"
diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py
index 7d3453f..ff791b2 100644
--- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py
+++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py
@@ -12,7 +12,10 @@
 
 class AssetMaintenanceLog(Document):
 	def validate(self):
-		if getdate(self.due_date) < getdate(nowdate()) and self.maintenance_status not in ["Completed", "Cancelled"]:
+		if getdate(self.due_date) < getdate(nowdate()) and self.maintenance_status not in [
+			"Completed",
+			"Cancelled",
+		]:
 			self.maintenance_status = "Overdue"
 
 		if self.maintenance_status == "Completed" and not self.completion_date:
@@ -22,15 +25,17 @@
 			frappe.throw(_("Please select Maintenance Status as Completed or remove Completion Date"))
 
 	def on_submit(self):
-		if self.maintenance_status not in ['Completed', 'Cancelled']:
+		if self.maintenance_status not in ["Completed", "Cancelled"]:
 			frappe.throw(_("Maintenance Status has to be Cancelled or Completed to Submit"))
 		self.update_maintenance_task()
 
 	def update_maintenance_task(self):
-		asset_maintenance_doc = frappe.get_doc('Asset Maintenance Task', self.task)
+		asset_maintenance_doc = frappe.get_doc("Asset Maintenance Task", self.task)
 		if self.maintenance_status == "Completed":
 			if asset_maintenance_doc.last_completion_date != self.completion_date:
-				next_due_date = calculate_next_due_date(periodicity = self.periodicity, last_completion_date = self.completion_date)
+				next_due_date = calculate_next_due_date(
+					periodicity=self.periodicity, last_completion_date=self.completion_date
+				)
 				asset_maintenance_doc.last_completion_date = self.completion_date
 				asset_maintenance_doc.next_due_date = next_due_date
 				asset_maintenance_doc.maintenance_status = "Planned"
@@ -38,11 +43,14 @@
 		if self.maintenance_status == "Cancelled":
 			asset_maintenance_doc.maintenance_status = "Cancelled"
 			asset_maintenance_doc.save()
-		asset_maintenance_doc = frappe.get_doc('Asset Maintenance', self.asset_maintenance)
+		asset_maintenance_doc = frappe.get_doc("Asset Maintenance", self.asset_maintenance)
 		asset_maintenance_doc.save()
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_maintenance_tasks(doctype, txt, searchfield, start, page_len, filters):
-	asset_maintenance_tasks = frappe.db.get_values('Asset Maintenance Task', {'parent':filters.get("asset_maintenance")}, 'maintenance_task')
+	asset_maintenance_tasks = frappe.db.get_values(
+		"Asset Maintenance Task", {"parent": filters.get("asset_maintenance")}, "maintenance_task"
+	)
 	return asset_maintenance_tasks
diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py
index 07bea61..143f215 100644
--- a/erpnext/assets/doctype/asset_movement/asset_movement.py
+++ b/erpnext/assets/doctype/asset_movement/asset_movement.py
@@ -16,7 +16,7 @@
 	def validate_asset(self):
 		for d in self.assets:
 			status, company = frappe.db.get_value("Asset", d.asset, ["status", "company"])
-			if self.purpose == 'Transfer' and status in ("Draft", "Scrapped", "Sold"):
+			if self.purpose == "Transfer" and status in ("Draft", "Scrapped", "Sold"):
 				frappe.throw(_("{0} asset cannot be transferred").format(status))
 
 			if company != self.company:
@@ -27,7 +27,7 @@
 
 	def validate_location(self):
 		for d in self.assets:
-			if self.purpose in ['Transfer', 'Issue']:
+			if self.purpose in ["Transfer", "Issue"]:
 				if not d.source_location:
 					d.source_location = frappe.db.get_value("Asset", d.asset, "location")
 
@@ -38,52 +38,73 @@
 					current_location = frappe.db.get_value("Asset", d.asset, "location")
 
 					if current_location != d.source_location:
-						frappe.throw(_("Asset {0} does not belongs to the location {1}").
-							format(d.asset, d.source_location))
+						frappe.throw(
+							_("Asset {0} does not belongs to the location {1}").format(d.asset, d.source_location)
+						)
 
-			if self.purpose == 'Issue':
+			if self.purpose == "Issue":
 				if d.target_location:
-					frappe.throw(_("Issuing cannot be done to a location. \
-						Please enter employee who has issued Asset {0}").format(d.asset), title="Incorrect Movement Purpose")
+					frappe.throw(
+						_(
+							"Issuing cannot be done to a location. Please enter employee who has issued Asset {0}"
+						).format(d.asset),
+						title=_("Incorrect Movement Purpose"),
+					)
 				if not d.to_employee:
 					frappe.throw(_("Employee is required while issuing Asset {0}").format(d.asset))
 
-			if self.purpose == 'Transfer':
+			if self.purpose == "Transfer":
 				if d.to_employee:
-					frappe.throw(_("Transferring cannot be done to an Employee. \
-						Please enter location where Asset {0} has to be transferred").format(
-							d.asset), title="Incorrect Movement Purpose")
+					frappe.throw(
+						_(
+							"Transferring cannot be done to an Employee. Please enter location where Asset {0} has to be transferred"
+						).format(d.asset),
+						title=_("Incorrect Movement Purpose"),
+					)
 				if not d.target_location:
 					frappe.throw(_("Target Location is required while transferring Asset {0}").format(d.asset))
 				if d.source_location == d.target_location:
 					frappe.throw(_("Source and Target Location cannot be same"))
 
-			if self.purpose == 'Receipt':
+			if self.purpose == "Receipt":
 				# only when asset is bought and first entry is made
 				if not d.source_location and not (d.target_location or d.to_employee):
-					frappe.throw(_("Target Location or To Employee is required while receiving Asset {0}").format(d.asset))
+					frappe.throw(
+						_("Target Location or To Employee is required while receiving Asset {0}").format(d.asset)
+					)
 				elif d.source_location:
 					# when asset is received from an employee
 					if d.target_location and not d.from_employee:
-						frappe.throw(_("From employee is required while receiving Asset {0} to a target location").format(d.asset))
+						frappe.throw(
+							_("From employee is required while receiving Asset {0} to a target location").format(
+								d.asset
+							)
+						)
 					if d.from_employee and not d.target_location:
-						frappe.throw(_("Target Location is required while receiving Asset {0} from an employee").format(d.asset))
+						frappe.throw(
+							_("Target Location is required while receiving Asset {0} from an employee").format(d.asset)
+						)
 					if d.to_employee and d.target_location:
-						frappe.throw(_("Asset {0} cannot be received at a location and \
-							given to employee in a single movement").format(d.asset))
+						frappe.throw(
+							_(
+								"Asset {0} cannot be received at a location and given to employee in a single movement"
+							).format(d.asset)
+						)
 
 	def validate_employee(self):
 		for d in self.assets:
 			if d.from_employee:
-					current_custodian = frappe.db.get_value("Asset", d.asset, "custodian")
+				current_custodian = frappe.db.get_value("Asset", d.asset, "custodian")
 
-					if current_custodian != d.from_employee:
-						frappe.throw(_("Asset {0} does not belongs to the custodian {1}").
-							format(d.asset, d.from_employee))
+				if current_custodian != d.from_employee:
+					frappe.throw(
+						_("Asset {0} does not belongs to the custodian {1}").format(d.asset, d.from_employee)
+					)
 
 			if d.to_employee and frappe.db.get_value("Employee", d.to_employee, "company") != self.company:
-				frappe.throw(_("Employee {0} does not belongs to the company {1}").
-							format(d.to_employee, self.company))
+				frappe.throw(
+					_("Employee {0} does not belongs to the company {1}").format(d.to_employee, self.company)
+				)
 
 	def on_submit(self):
 		self.set_latest_location_in_asset()
@@ -92,14 +113,11 @@
 		self.set_latest_location_in_asset()
 
 	def set_latest_location_in_asset(self):
-		current_location, current_employee = '', ''
+		current_location, current_employee = "", ""
 		cond = "1=1"
 
 		for d in self.assets:
-			args = {
-				'asset': d.asset,
-				'company': self.company
-			}
+			args = {"asset": d.asset, "company": self.company}
 
 			# latest entry corresponds to current document's location, employee when transaction date > previous dates
 			# In case of cancellation it corresponds to previous latest document's location, employee
@@ -114,10 +132,14 @@
 					asm.docstatus=1 and {0}
 				ORDER BY
 					asm.transaction_date desc limit 1
-				""".format(cond), args)
+				""".format(
+					cond
+				),
+				args,
+			)
 			if latest_movement_entry:
 				current_location = latest_movement_entry[0][0]
 				current_employee = latest_movement_entry[0][1]
 
-			frappe.db.set_value('Asset', d.asset, 'location', current_location)
-			frappe.db.set_value('Asset', d.asset, 'custodian', current_employee)
+			frappe.db.set_value("Asset", d.asset, "location", current_location)
+			frappe.db.set_value("Asset", d.asset, "custodian", current_employee)
diff --git a/erpnext/assets/doctype/asset_movement/test_asset_movement.py b/erpnext/assets/doctype/asset_movement/test_asset_movement.py
index 025facc..a5fe52c 100644
--- a/erpnext/assets/doctype/asset_movement/test_asset_movement.py
+++ b/erpnext/assets/doctype/asset_movement/test_asset_movement.py
@@ -13,95 +13,122 @@
 
 class TestAssetMovement(unittest.TestCase):
 	def setUp(self):
-		frappe.db.set_value("Company", "_Test Company", "capital_work_in_progress_account", "CWIP Account - _TC")
+		frappe.db.set_value(
+			"Company", "_Test Company", "capital_work_in_progress_account", "CWIP Account - _TC"
+		)
 		create_asset_data()
 		make_location()
 
 	def test_movement(self):
-		pr = make_purchase_receipt(item_code="Macbook Pro",
-			qty=1, rate=100000.0, location="Test Location")
+		pr = make_purchase_receipt(
+			item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location"
+		)
 
-		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
-		asset = frappe.get_doc('Asset', asset_name)
+		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name")
+		asset = frappe.get_doc("Asset", asset_name)
 		asset.calculate_depreciation = 1
-		asset.available_for_use_date = '2020-06-06'
-		asset.purchase_date = '2020-06-06'
-		asset.append("finance_books", {
-			"expected_value_after_useful_life": 10000,
-			"next_depreciation_date": "2020-12-31",
-			"depreciation_method": "Straight Line",
-			"total_number_of_depreciations": 3,
-			"frequency_of_depreciation": 10
-		})
+		asset.available_for_use_date = "2020-06-06"
+		asset.purchase_date = "2020-06-06"
+		asset.append(
+			"finance_books",
+			{
+				"expected_value_after_useful_life": 10000,
+				"next_depreciation_date": "2020-12-31",
+				"depreciation_method": "Straight Line",
+				"total_number_of_depreciations": 3,
+				"frequency_of_depreciation": 10,
+			},
+		)
 
 		if asset.docstatus == 0:
 			asset.submit()
 
 		# check asset movement is created
 		if not frappe.db.exists("Location", "Test Location 2"):
-			frappe.get_doc({
-				'doctype': 'Location',
-				'location_name': 'Test Location 2'
-			}).insert()
+			frappe.get_doc({"doctype": "Location", "location_name": "Test Location 2"}).insert()
 
-		movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company,
-			assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'target_location': 'Test Location 2'}],
-			reference_doctype = 'Purchase Receipt', reference_name = pr.name)
+		movement1 = create_asset_movement(
+			purpose="Transfer",
+			company=asset.company,
+			assets=[
+				{"asset": asset.name, "source_location": "Test Location", "target_location": "Test Location 2"}
+			],
+			reference_doctype="Purchase Receipt",
+			reference_name=pr.name,
+		)
 		self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2")
 
-		create_asset_movement(purpose = 'Transfer', company = asset.company,
-			assets = [{ 'asset': asset.name , 'source_location': 'Test Location 2', 'target_location': 'Test Location'}],
-			reference_doctype = 'Purchase Receipt', reference_name = pr.name)
+		create_asset_movement(
+			purpose="Transfer",
+			company=asset.company,
+			assets=[
+				{"asset": asset.name, "source_location": "Test Location 2", "target_location": "Test Location"}
+			],
+			reference_doctype="Purchase Receipt",
+			reference_name=pr.name,
+		)
 		self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
 
 		movement1.cancel()
 		self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
 
 		employee = make_employee("testassetmovemp@example.com", company="_Test Company")
-		create_asset_movement(purpose = 'Issue', company = asset.company,
-			assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'to_employee': employee}],
-			reference_doctype = 'Purchase Receipt', reference_name = pr.name)
+		create_asset_movement(
+			purpose="Issue",
+			company=asset.company,
+			assets=[{"asset": asset.name, "source_location": "Test Location", "to_employee": employee}],
+			reference_doctype="Purchase Receipt",
+			reference_name=pr.name,
+		)
 
 		# after issuing asset should belong to an employee not at a location
 		self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), None)
 		self.assertEqual(frappe.db.get_value("Asset", asset.name, "custodian"), employee)
 
 	def test_last_movement_cancellation(self):
-		pr = make_purchase_receipt(item_code="Macbook Pro",
-			qty=1, rate=100000.0, location="Test Location")
+		pr = make_purchase_receipt(
+			item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location"
+		)
 
-		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
-		asset = frappe.get_doc('Asset', asset_name)
+		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name")
+		asset = frappe.get_doc("Asset", asset_name)
 		asset.calculate_depreciation = 1
-		asset.available_for_use_date = '2020-06-06'
-		asset.purchase_date = '2020-06-06'
-		asset.append("finance_books", {
-			"expected_value_after_useful_life": 10000,
-			"next_depreciation_date": "2020-12-31",
-			"depreciation_method": "Straight Line",
-			"total_number_of_depreciations": 3,
-			"frequency_of_depreciation": 10
-		})
+		asset.available_for_use_date = "2020-06-06"
+		asset.purchase_date = "2020-06-06"
+		asset.append(
+			"finance_books",
+			{
+				"expected_value_after_useful_life": 10000,
+				"next_depreciation_date": "2020-12-31",
+				"depreciation_method": "Straight Line",
+				"total_number_of_depreciations": 3,
+				"frequency_of_depreciation": 10,
+			},
+		)
 		if asset.docstatus == 0:
 			asset.submit()
 
 		if not frappe.db.exists("Location", "Test Location 2"):
-			frappe.get_doc({
-				'doctype': 'Location',
-				'location_name': 'Test Location 2'
-			}).insert()
+			frappe.get_doc({"doctype": "Location", "location_name": "Test Location 2"}).insert()
 
-		movement = frappe.get_doc({'doctype': 'Asset Movement', 'reference_name': pr.name })
+		movement = frappe.get_doc({"doctype": "Asset Movement", "reference_name": pr.name})
 		self.assertRaises(frappe.ValidationError, movement.cancel)
 
-		movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company,
-			assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'target_location': 'Test Location 2'}],
-			reference_doctype = 'Purchase Receipt', reference_name = pr.name)
+		movement1 = create_asset_movement(
+			purpose="Transfer",
+			company=asset.company,
+			assets=[
+				{"asset": asset.name, "source_location": "Test Location", "target_location": "Test Location 2"}
+			],
+			reference_doctype="Purchase Receipt",
+			reference_name=pr.name,
+		)
 		self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2")
 
 		movement1.cancel()
 		self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
 
+
 def create_asset_movement(**args):
 	args = frappe._dict(args)
 
@@ -109,24 +136,26 @@
 		args.transaction_date = now()
 
 	movement = frappe.new_doc("Asset Movement")
-	movement.update({
-		"assets": args.assets,
-		"transaction_date": args.transaction_date,
-		"company": args.company,
-		'purpose': args.purpose or 'Receipt',
-		'reference_doctype': args.reference_doctype,
-		'reference_name': args.reference_name
-	})
+	movement.update(
+		{
+			"assets": args.assets,
+			"transaction_date": args.transaction_date,
+			"company": args.company,
+			"purpose": args.purpose or "Receipt",
+			"reference_doctype": args.reference_doctype,
+			"reference_name": args.reference_name,
+		}
+	)
 
 	movement.insert()
 	movement.submit()
 
 	return movement
 
+
 def make_location():
-	for location in ['Pune', 'Mumbai', 'Nagpur']:
-		if not frappe.db.exists('Location', location):
-			frappe.get_doc({
-				'doctype': 'Location',
-				'location_name': location
-			}).insert(ignore_permissions = True)
+	for location in ["Pune", "Mumbai", "Nagpur"]:
+		if not frappe.db.exists("Location", location):
+			frappe.get_doc({"doctype": "Location", "location_name": location}).insert(
+				ignore_permissions=True
+			)
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js
index d554d52..f5e4e72 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair.js
+++ b/erpnext/assets/doctype/asset_repair/asset_repair.js
@@ -32,7 +32,7 @@
 
 	refresh: function(frm) {
 		if (frm.doc.docstatus) {
-			frm.add_custom_button("View General Ledger", function() {
+			frm.add_custom_button(__("View General Ledger"), function() {
 				frappe.route_options = {
 					"voucher_no": frm.doc.name
 				};
@@ -68,6 +68,28 @@
 });
 
 frappe.ui.form.on('Asset Repair Consumed Item', {
+	item_code: function(frm, cdt, cdn) {
+		var item = locals[cdt][cdn];
+
+		let item_args = {
+			'item_code': item.item_code,
+			'warehouse': frm.doc.warehouse,
+			'qty': item.consumed_quantity,
+			'serial_no': item.serial_no,
+			'company': frm.doc.company
+		};
+
+		frappe.call({
+			method: 'erpnext.stock.utils.get_incoming_rate',
+			args: {
+				args: item_args
+			},
+			callback: function(r) {
+				frappe.model.set_value(cdt, cdn, 'valuation_rate', r.message);
+			}
+		});
+	},
+
 	consumed_quantity: function(frm, cdt, cdn) {
 		var row = locals[cdt][cdn];
 		frappe.model.set_value(cdt, cdn, 'total_value', row.consumed_quantity * row.valuation_rate);
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py
index 36848e9..5bf6011 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair.py
+++ b/erpnext/assets/doctype/asset_repair/asset_repair.py
@@ -13,21 +13,21 @@
 
 class AssetRepair(AccountsController):
 	def validate(self):
-		self.asset_doc = frappe.get_doc('Asset', self.asset)
+		self.asset_doc = frappe.get_doc("Asset", self.asset)
 		self.update_status()
 
-		if self.get('stock_items'):
+		if self.get("stock_items"):
 			self.set_total_value()
 		self.calculate_total_repair_cost()
 
 	def update_status(self):
-		if self.repair_status == 'Pending':
-			frappe.db.set_value('Asset', self.asset, 'status', 'Out of Order')
+		if self.repair_status == "Pending":
+			frappe.db.set_value("Asset", self.asset, "status", "Out of Order")
 		else:
 			self.asset_doc.set_status()
 
 	def set_total_value(self):
-		for item in self.get('stock_items'):
+		for item in self.get("stock_items"):
 			item.total_value = flt(item.valuation_rate) * flt(item.consumed_quantity)
 
 	def calculate_total_repair_cost(self):
@@ -39,14 +39,17 @@
 	def before_submit(self):
 		self.check_repair_status()
 
-		if self.get('stock_consumption') or self.get('capitalize_repair_cost'):
+		if self.get("stock_consumption") or self.get("capitalize_repair_cost"):
 			self.increase_asset_value()
-		if self.get('stock_consumption'):
+		if self.get("stock_consumption"):
 			self.check_for_stock_items_and_warehouse()
 			self.decrease_stock_quantity()
-		if self.get('capitalize_repair_cost'):
+		if self.get("capitalize_repair_cost"):
 			self.make_gl_entries()
-			if frappe.db.get_value('Asset', self.asset, 'calculate_depreciation') and self.increase_in_asset_life:
+			if (
+				frappe.db.get_value("Asset", self.asset, "calculate_depreciation")
+				and self.increase_in_asset_life
+			):
 				self.modify_depreciation_schedule()
 
 		self.asset_doc.flags.ignore_validate_update_after_submit = True
@@ -54,16 +57,19 @@
 		self.asset_doc.save()
 
 	def before_cancel(self):
-		self.asset_doc = frappe.get_doc('Asset', self.asset)
+		self.asset_doc = frappe.get_doc("Asset", self.asset)
 
-		if self.get('stock_consumption') or self.get('capitalize_repair_cost'):
+		if self.get("stock_consumption") or self.get("capitalize_repair_cost"):
 			self.decrease_asset_value()
-		if self.get('stock_consumption'):
+		if self.get("stock_consumption"):
 			self.increase_stock_quantity()
-		if self.get('capitalize_repair_cost'):
-			self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+		if self.get("capitalize_repair_cost"):
+			self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
 			self.make_gl_entries(cancel=True)
-			if frappe.db.get_value('Asset', self.asset, 'calculate_depreciation') and self.increase_in_asset_life:
+			if (
+				frappe.db.get_value("Asset", self.asset, "calculate_depreciation")
+				and self.increase_in_asset_life
+			):
 				self.revert_depreciation_schedule_on_cancellation()
 
 		self.asset_doc.flags.ignore_validate_update_after_submit = True
@@ -75,10 +81,15 @@
 			frappe.throw(_("Please update Repair Status."))
 
 	def check_for_stock_items_and_warehouse(self):
-		if not self.get('stock_items'):
-			frappe.throw(_("Please enter Stock Items consumed during the Repair."), title=_("Missing Items"))
+		if not self.get("stock_items"):
+			frappe.throw(
+				_("Please enter Stock Items consumed during the Repair."), title=_("Missing Items")
+			)
 		if not self.warehouse:
-			frappe.throw(_("Please enter Warehouse from which Stock Items consumed during the Repair were taken."), title=_("Missing Warehouse"))
+			frappe.throw(
+				_("Please enter Warehouse from which Stock Items consumed during the Repair were taken."),
+				title=_("Missing Warehouse"),
+			)
 
 	def increase_asset_value(self):
 		total_value_of_stock_consumed = self.get_total_value_of_stock_consumed()
@@ -102,35 +113,36 @@
 
 	def get_total_value_of_stock_consumed(self):
 		total_value_of_stock_consumed = 0
-		if self.get('stock_consumption'):
-			for item in self.get('stock_items'):
+		if self.get("stock_consumption"):
+			for item in self.get("stock_items"):
 				total_value_of_stock_consumed += item.total_value
 
 		return total_value_of_stock_consumed
 
 	def decrease_stock_quantity(self):
-		stock_entry = frappe.get_doc({
-			"doctype": "Stock Entry",
-			"stock_entry_type": "Material Issue",
-			"company": self.company
-		})
+		stock_entry = frappe.get_doc(
+			{"doctype": "Stock Entry", "stock_entry_type": "Material Issue", "company": self.company}
+		)
 
-		for stock_item in self.get('stock_items'):
-			stock_entry.append('items', {
-				"s_warehouse": self.warehouse,
-				"item_code": stock_item.item_code,
-				"qty": stock_item.consumed_quantity,
-				"basic_rate": stock_item.valuation_rate,
-				"serial_no": stock_item.serial_no
-			})
+		for stock_item in self.get("stock_items"):
+			stock_entry.append(
+				"items",
+				{
+					"s_warehouse": self.warehouse,
+					"item_code": stock_item.item_code,
+					"qty": stock_item.consumed_quantity,
+					"basic_rate": stock_item.valuation_rate,
+					"serial_no": stock_item.serial_no,
+				},
+			)
 
 		stock_entry.insert()
 		stock_entry.submit()
 
-		self.db_set('stock_entry', stock_entry.name)
+		self.db_set("stock_entry", stock_entry.name)
 
 	def increase_stock_quantity(self):
-		stock_entry = frappe.get_doc('Stock Entry', self.stock_entry)
+		stock_entry = frappe.get_doc("Stock Entry", self.stock_entry)
 		stock_entry.flags.ignore_links = True
 		stock_entry.cancel()
 
@@ -141,63 +153,78 @@
 
 	def get_gl_entries(self):
 		gl_entries = []
-		repair_and_maintenance_account = frappe.db.get_value('Company', self.company, 'repair_and_maintenance_account')
-		fixed_asset_account = get_asset_account("fixed_asset_account", asset=self.asset, company=self.company)
-		expense_account = frappe.get_doc('Purchase Invoice', self.purchase_invoice).items[0].expense_account
-
-		gl_entries.append(
-			self.get_gl_dict({
-				"account": expense_account,
-				"credit": self.repair_cost,
-				"credit_in_account_currency": self.repair_cost,
-				"against": repair_and_maintenance_account,
-				"voucher_type": self.doctype,
-				"voucher_no": self.name,
-				"cost_center": self.cost_center,
-				"posting_date": getdate(),
-				"company": self.company
-			}, item=self)
+		repair_and_maintenance_account = frappe.db.get_value(
+			"Company", self.company, "repair_and_maintenance_account"
+		)
+		fixed_asset_account = get_asset_account(
+			"fixed_asset_account", asset=self.asset, company=self.company
+		)
+		expense_account = (
+			frappe.get_doc("Purchase Invoice", self.purchase_invoice).items[0].expense_account
 		)
 
-		if self.get('stock_consumption'):
+		gl_entries.append(
+			self.get_gl_dict(
+				{
+					"account": expense_account,
+					"credit": self.repair_cost,
+					"credit_in_account_currency": self.repair_cost,
+					"against": repair_and_maintenance_account,
+					"voucher_type": self.doctype,
+					"voucher_no": self.name,
+					"cost_center": self.cost_center,
+					"posting_date": getdate(),
+					"company": self.company,
+				},
+				item=self,
+			)
+		)
+
+		if self.get("stock_consumption"):
 			# creating GL Entries for each row in Stock Items based on the Stock Entry created for it
-			stock_entry = frappe.get_doc('Stock Entry', self.stock_entry)
+			stock_entry = frappe.get_doc("Stock Entry", self.stock_entry)
 			for item in stock_entry.items:
 				gl_entries.append(
-					self.get_gl_dict({
-						"account": item.expense_account,
-						"credit": item.amount,
-						"credit_in_account_currency": item.amount,
-						"against": repair_and_maintenance_account,
-						"voucher_type": self.doctype,
-						"voucher_no": self.name,
-						"cost_center": self.cost_center,
-						"posting_date": getdate(),
-						"company": self.company
-					}, item=self)
+					self.get_gl_dict(
+						{
+							"account": item.expense_account,
+							"credit": item.amount,
+							"credit_in_account_currency": item.amount,
+							"against": repair_and_maintenance_account,
+							"voucher_type": self.doctype,
+							"voucher_no": self.name,
+							"cost_center": self.cost_center,
+							"posting_date": getdate(),
+							"company": self.company,
+						},
+						item=self,
+					)
 				)
 
 		gl_entries.append(
-			self.get_gl_dict({
-				"account": fixed_asset_account,
-				"debit": self.total_repair_cost,
-				"debit_in_account_currency": self.total_repair_cost,
-				"against": expense_account,
-				"voucher_type": self.doctype,
-				"voucher_no": self.name,
-				"cost_center": self.cost_center,
-				"posting_date": getdate(),
-				"against_voucher_type": "Purchase Invoice",
-				"against_voucher": self.purchase_invoice,
-				"company": self.company
-			}, item=self)
+			self.get_gl_dict(
+				{
+					"account": fixed_asset_account,
+					"debit": self.total_repair_cost,
+					"debit_in_account_currency": self.total_repair_cost,
+					"against": expense_account,
+					"voucher_type": self.doctype,
+					"voucher_no": self.name,
+					"cost_center": self.cost_center,
+					"posting_date": getdate(),
+					"against_voucher_type": "Purchase Invoice",
+					"against_voucher": self.purchase_invoice,
+					"company": self.company,
+				},
+				item=self,
+			)
 		)
 
 		return gl_entries
 
 	def modify_depreciation_schedule(self):
 		for row in self.asset_doc.finance_books:
-			row.total_number_of_depreciations += self.increase_in_asset_life/row.frequency_of_depreciation
+			row.total_number_of_depreciations += self.increase_in_asset_life / row.frequency_of_depreciation
 
 			self.asset_doc.flags.increase_in_asset_life = False
 			extra_months = self.increase_in_asset_life % row.frequency_of_depreciation
@@ -207,26 +234,29 @@
 	# to help modify depreciation schedule when increase_in_asset_life is not a multiple of frequency_of_depreciation
 	def calculate_last_schedule_date(self, asset, row, extra_months):
 		asset.flags.increase_in_asset_life = True
-		number_of_pending_depreciations = cint(row.total_number_of_depreciations) - \
-			cint(asset.number_of_depreciations_booked)
+		number_of_pending_depreciations = cint(row.total_number_of_depreciations) - cint(
+			asset.number_of_depreciations_booked
+		)
 
 		# the Schedule Date in the final row of the old Depreciation Schedule
-		last_schedule_date = asset.schedules[len(asset.schedules)-1].schedule_date
+		last_schedule_date = asset.schedules[len(asset.schedules) - 1].schedule_date
 
 		# the Schedule Date in the final row of the new Depreciation Schedule
 		asset.to_date = add_months(last_schedule_date, extra_months)
 
 		# the latest possible date at which the depreciation can occur, without increasing the Total Number of Depreciations
 		# if depreciations happen yearly and the Depreciation Posting Date is 01-01-2020, this could be 01-01-2021, 01-01-2022...
-		schedule_date = add_months(row.depreciation_start_date,
-			number_of_pending_depreciations * cint(row.frequency_of_depreciation))
+		schedule_date = add_months(
+			row.depreciation_start_date,
+			number_of_pending_depreciations * cint(row.frequency_of_depreciation),
+		)
 
 		if asset.to_date > schedule_date:
 			row.total_number_of_depreciations += 1
 
 	def revert_depreciation_schedule_on_cancellation(self):
 		for row in self.asset_doc.finance_books:
-			row.total_number_of_depreciations -= self.increase_in_asset_life/row.frequency_of_depreciation
+			row.total_number_of_depreciations -= self.increase_in_asset_life / row.frequency_of_depreciation
 
 			self.asset_doc.flags.increase_in_asset_life = False
 			extra_months = self.increase_in_asset_life % row.frequency_of_depreciation
@@ -235,23 +265,27 @@
 
 	def calculate_last_schedule_date_before_modification(self, asset, row, extra_months):
 		asset.flags.increase_in_asset_life = True
-		number_of_pending_depreciations = cint(row.total_number_of_depreciations) - \
-			cint(asset.number_of_depreciations_booked)
+		number_of_pending_depreciations = cint(row.total_number_of_depreciations) - cint(
+			asset.number_of_depreciations_booked
+		)
 
 		# the Schedule Date in the final row of the modified Depreciation Schedule
-		last_schedule_date = asset.schedules[len(asset.schedules)-1].schedule_date
+		last_schedule_date = asset.schedules[len(asset.schedules) - 1].schedule_date
 
 		# the Schedule Date in the final row of the original Depreciation Schedule
 		asset.to_date = add_months(last_schedule_date, -extra_months)
 
 		# the latest possible date at which the depreciation can occur, without decreasing the Total Number of Depreciations
 		# if depreciations happen yearly and the Depreciation Posting Date is 01-01-2020, this could be 01-01-2021, 01-01-2022...
-		schedule_date = add_months(row.depreciation_start_date,
-			(number_of_pending_depreciations - 1) * cint(row.frequency_of_depreciation))
+		schedule_date = add_months(
+			row.depreciation_start_date,
+			(number_of_pending_depreciations - 1) * cint(row.frequency_of_depreciation),
+		)
 
 		if asset.to_date < schedule_date:
 			row.total_number_of_depreciations -= 1
 
+
 @frappe.whitelist()
 def get_downtime(failure_date, completion_date):
 	downtime = time_diff_in_hours(completion_date, failure_date)
diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py
index 7c0d057..4e7cf78 100644
--- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py
+++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py
@@ -25,7 +25,7 @@
 	def test_update_status(self):
 		asset = create_asset(submit=1)
 		initial_status = asset.status
-		asset_repair = create_asset_repair(asset = asset)
+		asset_repair = create_asset_repair(asset=asset)
 
 		if asset_repair.repair_status == "Pending":
 			asset.reload()
@@ -37,14 +37,14 @@
 		self.assertEqual(asset_status, initial_status)
 
 	def test_stock_item_total_value(self):
-		asset_repair = create_asset_repair(stock_consumption = 1)
+		asset_repair = create_asset_repair(stock_consumption=1)
 
 		for item in asset_repair.stock_items:
 			total_value = flt(item.valuation_rate) * flt(item.consumed_quantity)
 			self.assertEqual(item.total_value, total_value)
 
 	def test_total_repair_cost(self):
-		asset_repair = create_asset_repair(stock_consumption = 1)
+		asset_repair = create_asset_repair(stock_consumption=1)
 
 		total_repair_cost = asset_repair.repair_cost
 		self.assertEqual(total_repair_cost, asset_repair.repair_cost)
@@ -54,22 +54,22 @@
 		self.assertEqual(total_repair_cost, asset_repair.total_repair_cost)
 
 	def test_repair_status_after_submit(self):
-		asset_repair = create_asset_repair(submit = 1)
+		asset_repair = create_asset_repair(submit=1)
 		self.assertNotEqual(asset_repair.repair_status, "Pending")
 
 	def test_stock_items(self):
-		asset_repair = create_asset_repair(stock_consumption = 1)
+		asset_repair = create_asset_repair(stock_consumption=1)
 		self.assertTrue(asset_repair.stock_consumption)
 		self.assertTrue(asset_repair.stock_items)
 
 	def test_warehouse(self):
-		asset_repair = create_asset_repair(stock_consumption = 1)
+		asset_repair = create_asset_repair(stock_consumption=1)
 		self.assertTrue(asset_repair.stock_consumption)
 		self.assertTrue(asset_repair.warehouse)
 
 	def test_decrease_stock_quantity(self):
-		asset_repair = create_asset_repair(stock_consumption = 1, submit = 1)
-		stock_entry = frappe.get_last_doc('Stock Entry')
+		asset_repair = create_asset_repair(stock_consumption=1, submit=1)
+		stock_entry = frappe.get_last_doc("Stock Entry")
 
 		self.assertEqual(stock_entry.stock_entry_type, "Material Issue")
 		self.assertEqual(stock_entry.items[0].s_warehouse, asset_repair.warehouse)
@@ -85,58 +85,72 @@
 		serial_no = serial_nos.split("\n")[0]
 
 		# should not raise any error
-		create_asset_repair(stock_consumption = 1, item_code = stock_entry.get("items")[0].item_code,
-			warehouse = "_Test Warehouse - _TC", serial_no = serial_no, submit = 1)
+		create_asset_repair(
+			stock_consumption=1,
+			item_code=stock_entry.get("items")[0].item_code,
+			warehouse="_Test Warehouse - _TC",
+			serial_no=serial_no,
+			submit=1,
+		)
 
 		# should raise error
-		asset_repair = create_asset_repair(stock_consumption = 1, warehouse = "_Test Warehouse - _TC",
-			item_code = stock_entry.get("items")[0].item_code)
+		asset_repair = create_asset_repair(
+			stock_consumption=1,
+			warehouse="_Test Warehouse - _TC",
+			item_code=stock_entry.get("items")[0].item_code,
+		)
 
 		asset_repair.repair_status = "Completed"
 		self.assertRaises(SerialNoRequiredError, asset_repair.submit)
 
 	def test_increase_in_asset_value_due_to_stock_consumption(self):
-		asset = create_asset(calculate_depreciation = 1, submit=1)
+		asset = create_asset(calculate_depreciation=1, submit=1)
 		initial_asset_value = get_asset_value(asset)
-		asset_repair = create_asset_repair(asset= asset, stock_consumption = 1, submit = 1)
+		asset_repair = create_asset_repair(asset=asset, stock_consumption=1, submit=1)
 		asset.reload()
 
 		increase_in_asset_value = get_asset_value(asset) - initial_asset_value
 		self.assertEqual(asset_repair.stock_items[0].total_value, increase_in_asset_value)
 
 	def test_increase_in_asset_value_due_to_repair_cost_capitalisation(self):
-		asset = create_asset(calculate_depreciation = 1, submit=1)
+		asset = create_asset(calculate_depreciation=1, submit=1)
 		initial_asset_value = get_asset_value(asset)
-		asset_repair = create_asset_repair(asset= asset, capitalize_repair_cost = 1, submit = 1)
+		asset_repair = create_asset_repair(asset=asset, capitalize_repair_cost=1, submit=1)
 		asset.reload()
 
 		increase_in_asset_value = get_asset_value(asset) - initial_asset_value
 		self.assertEqual(asset_repair.repair_cost, increase_in_asset_value)
 
 	def test_purchase_invoice(self):
-		asset_repair = create_asset_repair(capitalize_repair_cost = 1, submit = 1)
+		asset_repair = create_asset_repair(capitalize_repair_cost=1, submit=1)
 		self.assertTrue(asset_repair.purchase_invoice)
 
 	def test_gl_entries(self):
-		asset_repair = create_asset_repair(capitalize_repair_cost = 1, submit = 1)
-		gl_entry = frappe.get_last_doc('GL Entry')
+		asset_repair = create_asset_repair(capitalize_repair_cost=1, submit=1)
+		gl_entry = frappe.get_last_doc("GL Entry")
 		self.assertEqual(asset_repair.name, gl_entry.voucher_no)
 
 	def test_increase_in_asset_life(self):
-		asset = create_asset(calculate_depreciation = 1, submit=1)
+		asset = create_asset(calculate_depreciation=1, submit=1)
 		initial_num_of_depreciations = num_of_depreciations(asset)
-		create_asset_repair(asset= asset, capitalize_repair_cost = 1, submit = 1)
+		create_asset_repair(asset=asset, capitalize_repair_cost=1, submit=1)
 		asset.reload()
 
 		self.assertEqual((initial_num_of_depreciations + 1), num_of_depreciations(asset))
-		self.assertEqual(asset.schedules[-1].accumulated_depreciation_amount, asset.finance_books[0].value_after_depreciation)
+		self.assertEqual(
+			asset.schedules[-1].accumulated_depreciation_amount,
+			asset.finance_books[0].value_after_depreciation,
+		)
+
 
 def get_asset_value(asset):
 	return asset.finance_books[0].value_after_depreciation
 
+
 def num_of_depreciations(asset):
 	return asset.finance_books[0].total_number_of_depreciations
 
+
 def create_asset_repair(**args):
 	from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
 	from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
@@ -146,26 +160,33 @@
 	if args.asset:
 		asset = args.asset
 	else:
-		asset = create_asset(is_existing_asset = 1, submit=1)
+		asset = create_asset(is_existing_asset=1, submit=1)
 	asset_repair = frappe.new_doc("Asset Repair")
-	asset_repair.update({
-		"asset": asset.name,
-		"asset_name": asset.asset_name,
-		"failure_date": nowdate(),
-		"description": "Test Description",
-		"repair_cost": 0,
-		"company": asset.company
-	})
+	asset_repair.update(
+		{
+			"asset": asset.name,
+			"asset_name": asset.asset_name,
+			"failure_date": nowdate(),
+			"description": "Test Description",
+			"repair_cost": 0,
+			"company": asset.company,
+		}
+	)
 
 	if args.stock_consumption:
 		asset_repair.stock_consumption = 1
-		asset_repair.warehouse = args.warehouse or create_warehouse("Test Warehouse", company = asset.company)
-		asset_repair.append("stock_items", {
-			"item_code": args.item_code or "_Test Stock Item",
-			"valuation_rate": args.rate if args.get("rate") is not None else 100,
-			"consumed_quantity": args.qty or 1,
-			"serial_no": args.serial_no
-		})
+		asset_repair.warehouse = args.warehouse or create_warehouse(
+			"Test Warehouse", company=asset.company
+		)
+		asset_repair.append(
+			"stock_items",
+			{
+				"item_code": args.item_code or "_Test Stock Item",
+				"valuation_rate": args.rate if args.get("rate") is not None else 100,
+				"consumed_quantity": args.qty or 1,
+				"serial_no": args.serial_no,
+			},
+		)
 
 	asset_repair.insert(ignore_if_duplicate=True)
 
@@ -174,16 +195,17 @@
 		asset_repair.cost_center = "_Test Cost Center - _TC"
 
 		if args.stock_consumption:
-			stock_entry = frappe.get_doc({
-				"doctype": "Stock Entry",
-				"stock_entry_type": "Material Receipt",
-				"company": asset.company
-			})
-			stock_entry.append('items', {
-				"t_warehouse": asset_repair.warehouse,
-				"item_code": asset_repair.stock_items[0].item_code,
-				"qty": asset_repair.stock_items[0].consumed_quantity
-			})
+			stock_entry = frappe.get_doc(
+				{"doctype": "Stock Entry", "stock_entry_type": "Material Receipt", "company": asset.company}
+			)
+			stock_entry.append(
+				"items",
+				{
+					"t_warehouse": asset_repair.warehouse,
+					"item_code": asset_repair.stock_items[0].item_code,
+					"qty": asset_repair.stock_items[0].consumed_quantity,
+				},
+			)
 			stock_entry.submit()
 
 		if args.capitalize_repair_cost:
diff --git a/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json
index f63add1..4685a09 100644
--- a/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json
+++ b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json
@@ -13,12 +13,10 @@
  ],
  "fields": [
   {
-   "fetch_from": "item.valuation_rate",
    "fieldname": "valuation_rate",
    "fieldtype": "Currency",
    "in_list_view": 1,
-   "label": "Valuation Rate",
-   "read_only": 1
+   "label": "Valuation Rate"
   },
   {
    "fieldname": "consumed_quantity",
@@ -49,7 +47,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-11-11 18:23:00.492483",
+ "modified": "2022-02-08 17:37:20.028290",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Asset Repair Consumed Item",
diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
index 494af91..59ab6a9 100644
--- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
+++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
@@ -28,10 +28,14 @@
 		self.reschedule_depreciations(self.current_asset_value)
 
 	def validate_date(self):
-		asset_purchase_date = frappe.db.get_value('Asset', self.asset, 'purchase_date')
+		asset_purchase_date = frappe.db.get_value("Asset", self.asset, "purchase_date")
 		if getdate(self.date) < getdate(asset_purchase_date):
-			frappe.throw(_("Asset Value Adjustment cannot be posted before Asset's purchase date <b>{0}</b>.")
-				.format(formatdate(asset_purchase_date)), title="Incorrect Date")
+			frappe.throw(
+				_("Asset Value Adjustment cannot be posted before Asset's purchase date <b>{0}</b>.").format(
+					formatdate(asset_purchase_date)
+				),
+				title=_("Incorrect Date"),
+			)
 
 	def set_difference_amount(self):
 		self.difference_amount = flt(self.current_asset_value - self.new_asset_value)
@@ -42,11 +46,15 @@
 
 	def make_depreciation_entry(self):
 		asset = frappe.get_doc("Asset", self.asset)
-		fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \
-			get_depreciation_accounts(asset)
+		(
+			fixed_asset_account,
+			accumulated_depreciation_account,
+			depreciation_expense_account,
+		) = get_depreciation_accounts(asset)
 
-		depreciation_cost_center, depreciation_series = frappe.get_cached_value('Company',  asset.company,
-			["depreciation_cost_center", "series_for_depreciation_entry"])
+		depreciation_cost_center, depreciation_series = frappe.get_cached_value(
+			"Company", asset.company, ["depreciation_cost_center", "series_for_depreciation_entry"]
+		)
 
 		je = frappe.new_doc("Journal Entry")
 		je.voucher_type = "Depreciation Entry"
@@ -59,27 +67,33 @@
 		credit_entry = {
 			"account": accumulated_depreciation_account,
 			"credit_in_account_currency": self.difference_amount,
-			"cost_center": depreciation_cost_center or self.cost_center
+			"cost_center": depreciation_cost_center or self.cost_center,
 		}
 
 		debit_entry = {
 			"account": depreciation_expense_account,
 			"debit_in_account_currency": self.difference_amount,
-			"cost_center": depreciation_cost_center or self.cost_center
+			"cost_center": depreciation_cost_center or self.cost_center,
 		}
 
 		accounting_dimensions = get_checks_for_pl_and_bs_accounts()
 
 		for dimension in accounting_dimensions:
-			if dimension.get('mandatory_for_bs'):
-				credit_entry.update({
-					dimension['fieldname']: self.get(dimension['fieldname']) or dimension.get('default_dimension')
-				})
+			if dimension.get("mandatory_for_bs"):
+				credit_entry.update(
+					{
+						dimension["fieldname"]: self.get(dimension["fieldname"])
+						or dimension.get("default_dimension")
+					}
+				)
 
-			if dimension.get('mandatory_for_pl'):
-				debit_entry.update({
-					dimension['fieldname']: self.get(dimension['fieldname']) or dimension.get('default_dimension')
-				})
+			if dimension.get("mandatory_for_pl"):
+				debit_entry.update(
+					{
+						dimension["fieldname"]: self.get(dimension["fieldname"])
+						or dimension.get("default_dimension")
+					}
+				)
 
 		je.append("accounts", credit_entry)
 		je.append("accounts", debit_entry)
@@ -90,8 +104,8 @@
 		self.db_set("journal_entry", je.name)
 
 	def reschedule_depreciations(self, asset_value):
-		asset = frappe.get_doc('Asset', self.asset)
-		country = frappe.get_value('Company', self.company, 'country')
+		asset = frappe.get_doc("Asset", self.asset)
+		country = frappe.get_value("Company", self.company, "country")
 
 		for d in asset.finance_books:
 			d.value_after_depreciation = asset_value
@@ -102,8 +116,11 @@
 				rate_per_day = flt(d.value_after_depreciation) / flt(total_days)
 				from_date = self.date
 			else:
-				no_of_depreciations = len([s.name for s in asset.schedules
-					if (cint(s.finance_book_id) == d.idx and not s.journal_entry)])
+				no_of_depreciations = len(
+					[
+						s.name for s in asset.schedules if (cint(s.finance_book_id) == d.idx and not s.journal_entry)
+					]
+				)
 
 			value_after_depreciation = d.value_after_depreciation
 			for data in asset.schedules:
@@ -126,10 +143,11 @@
 			if not asset_data.journal_entry:
 				asset_data.db_update()
 
+
 @frappe.whitelist()
 def get_current_asset_value(asset, finance_book=None):
-	cond = {'parent': asset, 'parenttype': 'Asset'}
+	cond = {"parent": asset, "parenttype": "Asset"}
 	if finance_book:
-		cond.update({'finance_book': finance_book})
+		cond.update({"finance_book": finance_book})
 
-	return frappe.db.get_value('Asset Finance Book', cond, 'value_after_depreciation')
+	return frappe.db.get_value("Asset Finance Book", cond, "value_after_depreciation")
diff --git a/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py
index ef13c56..ebeb174 100644
--- a/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py
+++ b/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py
@@ -18,11 +18,12 @@
 		create_asset_data()
 
 	def test_current_asset_value(self):
-		pr = make_purchase_receipt(item_code="Macbook Pro",
-			qty=1, rate=100000.0, location="Test Location")
+		pr = make_purchase_receipt(
+			item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location"
+		)
 
-		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
-		asset_doc = frappe.get_doc('Asset', asset_name)
+		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name")
+		asset_doc = frappe.get_doc("Asset", asset_name)
 
 		month_end_date = get_last_day(nowdate())
 		purchase_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15)
@@ -30,24 +31,28 @@
 		asset_doc.available_for_use_date = purchase_date
 		asset_doc.purchase_date = purchase_date
 		asset_doc.calculate_depreciation = 1
-		asset_doc.append("finance_books", {
-			"expected_value_after_useful_life": 200,
-			"depreciation_method": "Straight Line",
-			"total_number_of_depreciations": 3,
-			"frequency_of_depreciation": 10,
-			"depreciation_start_date": month_end_date
-		})
+		asset_doc.append(
+			"finance_books",
+			{
+				"expected_value_after_useful_life": 200,
+				"depreciation_method": "Straight Line",
+				"total_number_of_depreciations": 3,
+				"frequency_of_depreciation": 10,
+				"depreciation_start_date": month_end_date,
+			},
+		)
 		asset_doc.submit()
 
 		current_value = get_current_asset_value(asset_doc.name)
 		self.assertEqual(current_value, 100000.0)
 
 	def test_asset_depreciation_value_adjustment(self):
-		pr = make_purchase_receipt(item_code="Macbook Pro",
-			qty=1, rate=100000.0, location="Test Location")
+		pr = make_purchase_receipt(
+			item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location"
+		)
 
-		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
-		asset_doc = frappe.get_doc('Asset', asset_name)
+		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name")
+		asset_doc = frappe.get_doc("Asset", asset_name)
 		asset_doc.calculate_depreciation = 1
 
 		month_end_date = get_last_day(nowdate())
@@ -56,42 +61,52 @@
 		asset_doc.available_for_use_date = purchase_date
 		asset_doc.purchase_date = purchase_date
 		asset_doc.calculate_depreciation = 1
-		asset_doc.append("finance_books", {
-			"expected_value_after_useful_life": 200,
-			"depreciation_method": "Straight Line",
-			"total_number_of_depreciations": 3,
-			"frequency_of_depreciation": 10,
-			"depreciation_start_date": month_end_date
-		})
+		asset_doc.append(
+			"finance_books",
+			{
+				"expected_value_after_useful_life": 200,
+				"depreciation_method": "Straight Line",
+				"total_number_of_depreciations": 3,
+				"frequency_of_depreciation": 10,
+				"depreciation_start_date": month_end_date,
+			},
+		)
 		asset_doc.submit()
 
 		current_value = get_current_asset_value(asset_doc.name)
-		adj_doc = make_asset_value_adjustment(asset = asset_doc.name,
-			current_asset_value = current_value, new_asset_value = 50000.0)
+		adj_doc = make_asset_value_adjustment(
+			asset=asset_doc.name, current_asset_value=current_value, new_asset_value=50000.0
+		)
 		adj_doc.submit()
 
 		expected_gle = (
 			("_Test Accumulated Depreciations - _TC", 0.0, 50000.0),
-			("_Test Depreciations - _TC", 50000.0, 0.0)
+			("_Test Depreciations - _TC", 50000.0, 0.0),
 		)
 
-		gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
+		gle = frappe.db.sql(
+			"""select account, debit, credit from `tabGL Entry`
 			where voucher_type='Journal Entry' and voucher_no = %s
-			order by account""", adj_doc.journal_entry)
+			order by account""",
+			adj_doc.journal_entry,
+		)
 
 		self.assertEqual(gle, expected_gle)
 
+
 def make_asset_value_adjustment(**args):
 	args = frappe._dict(args)
 
-	doc = frappe.get_doc({
-		"doctype": "Asset Value Adjustment",
-		"company": args.company or "_Test Company",
-		"asset": args.asset,
-		"date": args.date or nowdate(),
-		"new_asset_value": args.new_asset_value,
-		"current_asset_value": args.current_asset_value,
-		"cost_center": args.cost_center or "Main - _TC"
-	}).insert()
+	doc = frappe.get_doc(
+		{
+			"doctype": "Asset Value Adjustment",
+			"company": args.company or "_Test Company",
+			"asset": args.asset,
+			"date": args.date or nowdate(),
+			"new_asset_value": args.new_asset_value,
+			"current_asset_value": args.current_asset_value,
+			"cost_center": args.cost_center or "Main - _TC",
+		}
+	).insert()
 
 	return doc
diff --git a/erpnext/assets/doctype/location/location.py b/erpnext/assets/doctype/location/location.py
index abc7325..0d87bb2 100644
--- a/erpnext/assets/doctype/location/location.py
+++ b/erpnext/assets/doctype/location/location.py
@@ -13,12 +13,12 @@
 
 
 class Location(NestedSet):
-	nsm_parent_field = 'parent_location'
+	nsm_parent_field = "parent_location"
 
 	def validate(self):
 		self.calculate_location_area()
 
-		if not self.is_new() and self.get('parent_location'):
+		if not self.is_new() and self.get("parent_location"):
 			self.update_ancestor_location_features()
 
 	def on_update(self):
@@ -42,7 +42,7 @@
 		if not self.location:
 			return []
 
-		features = json.loads(self.location).get('features')
+		features = json.loads(self.location).get("features")
 
 		if not isinstance(features, list):
 			features = json.loads(features)
@@ -54,15 +54,15 @@
 			self.location = '{"type":"FeatureCollection","features":[]}'
 
 		location = json.loads(self.location)
-		location['features'] = features
+		location["features"] = features
 
-		self.db_set('location', json.dumps(location), commit=True)
+		self.db_set("location", json.dumps(location), commit=True)
 
 	def update_ancestor_location_features(self):
 		self_features = set(self.add_child_property())
 
 		for ancestor in self.get_ancestors():
-			ancestor_doc = frappe.get_doc('Location', ancestor)
+			ancestor_doc = frappe.get_doc("Location", ancestor)
 			child_features, ancestor_features = ancestor_doc.feature_seperator(child_feature=self.name)
 
 			ancestor_features = list(set(ancestor_features))
@@ -84,25 +84,27 @@
 				ancestor_features[index] = json.loads(feature)
 
 			ancestor_doc.set_location_features(features=ancestor_features)
-			ancestor_doc.db_set('area', ancestor_doc.area + self.area_difference, commit=True)
+			ancestor_doc.db_set("area", ancestor_doc.area + self.area_difference, commit=True)
 
 	def remove_ancestor_location_features(self):
 		for ancestor in self.get_ancestors():
-			ancestor_doc = frappe.get_doc('Location', ancestor)
+			ancestor_doc = frappe.get_doc("Location", ancestor)
 			child_features, ancestor_features = ancestor_doc.feature_seperator(child_feature=self.name)
 
 			for index, feature in enumerate(ancestor_features):
 				ancestor_features[index] = json.loads(feature)
 
 			ancestor_doc.set_location_features(features=ancestor_features)
-			ancestor_doc.db_set('area', ancestor_doc.area - self.area, commit=True)
+			ancestor_doc.db_set("area", ancestor_doc.area - self.area, commit=True)
 
 	def add_child_property(self):
 		features = self.get_location_features()
-		filter_features = [feature for feature in features if not feature.get('properties').get('child_feature')]
+		filter_features = [
+			feature for feature in features if not feature.get("properties").get("child_feature")
+		]
 
 		for index, feature in enumerate(filter_features):
-			feature['properties'].update({'child_feature': True, 'feature_of': self.location_name})
+			feature["properties"].update({"child_feature": True, "feature_of": self.location_name})
 			filter_features[index] = json.dumps(filter_features[index])
 
 		return filter_features
@@ -112,7 +114,7 @@
 		features = self.get_location_features()
 
 		for feature in features:
-			if feature.get('properties').get('feature_of') == child_feature:
+			if feature.get("properties").get("feature_of") == child_feature:
 				child_features.extend([json.dumps(feature)])
 			else:
 				non_child_features.extend([json.dumps(feature)])
@@ -126,22 +128,22 @@
 	Reference from https://github.com/scisco/area.
 
 	Args:
-		`features` (list of dict): Features marked on the map as
-			GeoJSON data
+	        `features` (list of dict): Features marked on the map as
+	                GeoJSON data
 
 	Returns:
-		float: The approximate signed geodesic area (in sq. meters)
+	        float: The approximate signed geodesic area (in sq. meters)
 	"""
 
 	layer_area = 0.0
 
 	for feature in features:
-		feature_type = feature.get('geometry', {}).get('type')
+		feature_type = feature.get("geometry", {}).get("type")
 
-		if feature_type == 'Polygon':
-			layer_area += _polygon_area(coords=feature.get('geometry').get('coordinates'))
-		elif feature_type == 'Point' and feature.get('properties').get('point_type') == 'circle':
-			layer_area += math.pi * math.pow(feature.get('properties').get('radius'), 2)
+		if feature_type == "Polygon":
+			layer_area += _polygon_area(coords=feature.get("geometry").get("coordinates"))
+		elif feature_type == "Point" and feature.get("properties").get("point_type") == "circle":
+			layer_area += math.pi * math.pow(feature.get("properties").get("radius"), 2)
 
 	return layer_area
 
@@ -192,7 +194,8 @@
 	if parent is None or parent == "All Locations":
 		parent = ""
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select
 			name as value,
 			is_group as expandable
@@ -201,17 +204,20 @@
 		where
 			ifnull(parent_location, "")={parent}
 		""".format(
-			doctype=doctype,
-			parent=frappe.db.escape(parent)
-		), as_dict=1)
+			doctype=doctype, parent=frappe.db.escape(parent)
+		),
+		as_dict=1,
+	)
+
 
 @frappe.whitelist()
 def add_node():
 	from frappe.desk.treeview import make_tree_args
+
 	args = frappe.form_dict
 	args = make_tree_args(**args)
 
-	if args.parent_location == 'All Locations':
+	if args.parent_location == "All Locations":
 		args.parent_location = None
 
 	frappe.get_doc(args).insert()
diff --git a/erpnext/assets/doctype/location/test_location.py b/erpnext/assets/doctype/location/test_location.py
index 36e1dd4..b8563cb 100644
--- a/erpnext/assets/doctype/location/test_location.py
+++ b/erpnext/assets/doctype/location/test_location.py
@@ -6,29 +6,34 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Location')
+test_records = frappe.get_test_records("Location")
+
 
 class TestLocation(unittest.TestCase):
 	def runTest(self):
-		locations = ['Basil Farm', 'Division 1', 'Field 1', 'Block 1']
+		locations = ["Basil Farm", "Division 1", "Field 1", "Block 1"]
 		area = 0
 		formatted_locations = []
 
 		for location in locations:
-			doc = frappe.get_doc('Location', location)
+			doc = frappe.get_doc("Location", location)
 			doc.save()
 			area += doc.area
 			temp = json.loads(doc.location)
-			temp['features'][0]['properties']['child_feature'] = True
-			temp['features'][0]['properties']['feature_of'] = location
-			formatted_locations.extend(temp['features'])
+			temp["features"][0]["properties"]["child_feature"] = True
+			temp["features"][0]["properties"]["feature_of"] = location
+			formatted_locations.extend(temp["features"])
 
-		test_location = frappe.get_doc('Location', 'Test Location Area')
+		test_location = frappe.get_doc("Location", "Test Location Area")
 		test_location.save()
 
-		test_location_features = json.loads(test_location.get('location'))['features']
-		ordered_test_location_features = sorted(test_location_features, key=lambda x: x['properties']['feature_of'])
-		ordered_formatted_locations = sorted(formatted_locations, key=lambda x: x['properties']['feature_of'])
+		test_location_features = json.loads(test_location.get("location"))["features"]
+		ordered_test_location_features = sorted(
+			test_location_features, key=lambda x: x["properties"]["feature_of"]
+		)
+		ordered_formatted_locations = sorted(
+			formatted_locations, key=lambda x: x["properties"]["feature_of"]
+		)
 
 		self.assertEqual(ordered_formatted_locations, ordered_test_location_features)
-		self.assertEqual(area, test_location.get('area'))
+		self.assertEqual(area, test_location.get("area"))
diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
index db51336..6b14dce 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
@@ -17,16 +17,21 @@
 	filters = frappe._dict(filters or {})
 	columns = get_columns(filters)
 	data = get_data(filters)
-	chart = prepare_chart_data(data, filters) if filters.get("group_by") not in ("Asset Category", "Location") else {}
+	chart = (
+		prepare_chart_data(data, filters)
+		if filters.get("group_by") not in ("Asset Category", "Location")
+		else {}
+	)
 
 	return columns, data, None, chart
 
+
 def get_conditions(filters):
-	conditions = { 'docstatus': 1 }
+	conditions = {"docstatus": 1}
 	status = filters.status
 	date_field = frappe.scrub(filters.date_based_on or "Purchase Date")
 
-	if filters.get('company'):
+	if filters.get("company"):
 		conditions["company"] = filters.company
 	if filters.filter_based_on == "Date Range":
 		conditions[date_field] = ["between", [filters.from_date, filters.to_date]]
@@ -37,23 +42,24 @@
 		filters.year_end_date = getdate(fiscal_year.year_end_date)
 
 		conditions[date_field] = ["between", [filters.year_start_date, filters.year_end_date]]
-	if filters.get('is_existing_asset'):
-		conditions["is_existing_asset"] = filters.get('is_existing_asset')
-	if filters.get('asset_category'):
-		conditions["asset_category"] = filters.get('asset_category')
-	if filters.get('cost_center'):
-		conditions["cost_center"] = filters.get('cost_center')
+	if filters.get("is_existing_asset"):
+		conditions["is_existing_asset"] = filters.get("is_existing_asset")
+	if filters.get("asset_category"):
+		conditions["asset_category"] = filters.get("asset_category")
+	if filters.get("cost_center"):
+		conditions["cost_center"] = filters.get("cost_center")
 
 	if status:
 		# In Store assets are those that are not sold or scrapped
-		operand = 'not in'
-		if status not in 'In Location':
-			operand = 'in'
+		operand = "not in"
+		if status not in "In Location":
+			operand = "in"
 
-		conditions['status'] = (operand, ['Sold', 'Scrapped'])
+		conditions["status"] = (operand, ["Sold", "Scrapped"])
 
 	return conditions
 
+
 def get_data(filters):
 
 	data = []
@@ -74,21 +80,37 @@
 		assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields, group_by=group_by)
 
 	else:
-		fields = ["name as asset_id", "asset_name", "status", "department", "cost_center", "purchase_receipt",
-			"asset_category", "purchase_date", "gross_purchase_amount", "location",
-			"available_for_use_date", "purchase_invoice", "opening_accumulated_depreciation"]
+		fields = [
+			"name as asset_id",
+			"asset_name",
+			"status",
+			"department",
+			"cost_center",
+			"purchase_receipt",
+			"asset_category",
+			"purchase_date",
+			"gross_purchase_amount",
+			"location",
+			"available_for_use_date",
+			"purchase_invoice",
+			"opening_accumulated_depreciation",
+		]
 		assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields)
 
 	for asset in assets_record:
-		asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \
+		asset_value = (
+			asset.gross_purchase_amount
+			- flt(asset.opening_accumulated_depreciation)
 			- flt(depreciation_amount_map.get(asset.name))
+		)
 		row = {
 			"asset_id": asset.asset_id,
 			"asset_name": asset.asset_name,
 			"status": asset.status,
 			"department": asset.department,
 			"cost_center": asset.cost_center,
-			"vendor_name": pr_supplier_map.get(asset.purchase_receipt) or pi_supplier_map.get(asset.purchase_invoice),
+			"vendor_name": pr_supplier_map.get(asset.purchase_receipt)
+			or pi_supplier_map.get(asset.purchase_invoice),
 			"gross_purchase_amount": asset.gross_purchase_amount,
 			"opening_accumulated_depreciation": asset.opening_accumulated_depreciation,
 			"depreciated_amount": depreciation_amount_map.get(asset.asset_id) or 0.0,
@@ -96,21 +118,31 @@
 			"location": asset.location,
 			"asset_category": asset.asset_category,
 			"purchase_date": asset.purchase_date,
-			"asset_value": asset_value
+			"asset_value": asset_value,
 		}
 		data.append(row)
 
 	return data
 
+
 def prepare_chart_data(data, filters):
 	labels_values_map = {}
 	date_field = frappe.scrub(filters.date_based_on)
 
-	period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
-		filters.from_date, filters.to_date, filters.filter_based_on, "Monthly", company=filters.company)
+	period_list = get_period_list(
+		filters.from_fiscal_year,
+		filters.to_fiscal_year,
+		filters.from_date,
+		filters.to_date,
+		filters.filter_based_on,
+		"Monthly",
+		company=filters.company,
+	)
 
 	for d in period_list:
-		labels_values_map.setdefault(d.get('label'), frappe._dict({'asset_value': 0, 'depreciated_amount': 0}))
+		labels_values_map.setdefault(
+			d.get("label"), frappe._dict({"asset_value": 0, "depreciated_amount": 0})
+		)
 
 	for d in data:
 		date = d.get(date_field)
@@ -120,23 +152,30 @@
 		labels_values_map[belongs_to_month].depreciated_amount += d.get("depreciated_amount")
 
 	return {
-		"data" : {
+		"data": {
 			"labels": labels_values_map.keys(),
 			"datasets": [
-				{ 'name': _('Asset Value'), 'values': [d.get("asset_value") for d in labels_values_map.values()] },
-				{ 'name': _('Depreciatied Amount'), 'values': [d.get("depreciated_amount") for d in labels_values_map.values()] }
-			]
+				{
+					"name": _("Asset Value"),
+					"values": [d.get("asset_value") for d in labels_values_map.values()],
+				},
+				{
+					"name": _("Depreciatied Amount"),
+					"values": [d.get("depreciated_amount") for d in labels_values_map.values()],
+				},
+			],
 		},
 		"type": "bar",
-		"barOptions": {
-			"stacked": 1
-		},
+		"barOptions": {"stacked": 1},
 	}
 
+
 def get_finance_book_value_map(filters):
 	date = filters.to_date if filters.filter_based_on == "Date Range" else filters.year_end_date
 
-	return frappe._dict(frappe.db.sql(''' Select
+	return frappe._dict(
+		frappe.db.sql(
+			""" Select
 		parent, SUM(depreciation_amount)
 		FROM `tabDepreciation Schedule`
 		WHERE
@@ -144,27 +183,41 @@
 			AND schedule_date<=%s
 			AND journal_entry IS NOT NULL
 			AND ifnull(finance_book, '')=%s
-		GROUP BY parent''', (date, cstr(filters.finance_book or ''))))
+		GROUP BY parent""",
+			(date, cstr(filters.finance_book or "")),
+		)
+	)
+
 
 def get_purchase_receipt_supplier_map():
-	return frappe._dict(frappe.db.sql(''' Select
+	return frappe._dict(
+		frappe.db.sql(
+			""" Select
 		pr.name, pr.supplier
 		FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri
 		WHERE
 			pri.parent = pr.name
 			AND pri.is_fixed_asset=1
 			AND pr.docstatus=1
-			AND pr.is_return=0'''))
+			AND pr.is_return=0"""
+		)
+	)
+
 
 def get_purchase_invoice_supplier_map():
-	return frappe._dict(frappe.db.sql(''' Select
+	return frappe._dict(
+		frappe.db.sql(
+			""" Select
 		pi.name, pi.supplier
 		FROM `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pii
 		WHERE
 			pii.parent = pi.name
 			AND pii.is_fixed_asset=1
 			AND pi.docstatus=1
-			AND pi.is_return=0'''))
+			AND pi.is_return=0"""
+		)
+	)
+
 
 def get_columns(filters):
 	if filters.get("group_by") in ["Asset Category", "Location"]:
@@ -174,36 +227,36 @@
 				"fieldtype": "Link",
 				"fieldname": frappe.scrub(filters.get("group_by")),
 				"options": filters.get("group_by"),
-				"width": 120
+				"width": 120,
 			},
 			{
 				"label": _("Gross Purchase Amount"),
 				"fieldname": "gross_purchase_amount",
 				"fieldtype": "Currency",
 				"options": "company:currency",
-				"width": 100
+				"width": 100,
 			},
 			{
 				"label": _("Opening Accumulated Depreciation"),
 				"fieldname": "opening_accumulated_depreciation",
 				"fieldtype": "Currency",
 				"options": "company:currency",
-				"width": 90
+				"width": 90,
 			},
 			{
 				"label": _("Depreciated Amount"),
 				"fieldname": "depreciated_amount",
 				"fieldtype": "Currency",
 				"options": "company:currency",
-				"width": 100
+				"width": 100,
 			},
 			{
 				"label": _("Asset Value"),
 				"fieldname": "asset_value",
 				"fieldtype": "Currency",
 				"options": "company:currency",
-				"width": 100
-			}
+				"width": 100,
+			},
 		]
 
 	return [
@@ -212,92 +265,72 @@
 			"fieldtype": "Link",
 			"fieldname": "asset_id",
 			"options": "Asset",
-			"width": 60
+			"width": 60,
 		},
-		{
-			"label": _("Asset Name"),
-			"fieldtype": "Data",
-			"fieldname": "asset_name",
-			"width": 140
-		},
+		{"label": _("Asset Name"), "fieldtype": "Data", "fieldname": "asset_name", "width": 140},
 		{
 			"label": _("Asset Category"),
 			"fieldtype": "Link",
 			"fieldname": "asset_category",
 			"options": "Asset Category",
-			"width": 100
+			"width": 100,
 		},
-		{
-			"label": _("Status"),
-			"fieldtype": "Data",
-			"fieldname": "status",
-			"width": 80
-		},
-		{
-			"label": _("Purchase Date"),
-			"fieldtype": "Date",
-			"fieldname": "purchase_date",
-			"width": 90
-		},
+		{"label": _("Status"), "fieldtype": "Data", "fieldname": "status", "width": 80},
+		{"label": _("Purchase Date"), "fieldtype": "Date", "fieldname": "purchase_date", "width": 90},
 		{
 			"label": _("Available For Use Date"),
 			"fieldtype": "Date",
 			"fieldname": "available_for_use_date",
-			"width": 90
+			"width": 90,
 		},
 		{
 			"label": _("Gross Purchase Amount"),
 			"fieldname": "gross_purchase_amount",
 			"fieldtype": "Currency",
 			"options": "company:currency",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Asset Value"),
 			"fieldname": "asset_value",
 			"fieldtype": "Currency",
 			"options": "company:currency",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Opening Accumulated Depreciation"),
 			"fieldname": "opening_accumulated_depreciation",
 			"fieldtype": "Currency",
 			"options": "company:currency",
-			"width": 90
+			"width": 90,
 		},
 		{
 			"label": _("Depreciated Amount"),
 			"fieldname": "depreciated_amount",
 			"fieldtype": "Currency",
 			"options": "company:currency",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Cost Center"),
 			"fieldtype": "Link",
 			"fieldname": "cost_center",
 			"options": "Cost Center",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Department"),
 			"fieldtype": "Link",
 			"fieldname": "department",
 			"options": "Department",
-			"width": 100
+			"width": 100,
 		},
-		{
-			"label": _("Vendor Name"),
-			"fieldtype": "Data",
-			"fieldname": "vendor_name",
-			"width": 100
-		},
+		{"label": _("Vendor Name"), "fieldtype": "Data", "fieldname": "vendor_name", "width": 100},
 		{
 			"label": _("Location"),
 			"fieldtype": "Link",
 			"fieldname": "location",
 			"options": "Location",
-			"width": 100
+			"width": 100,
 		},
 	]
diff --git a/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.js b/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.js
index a739cc3..0073170 100644
--- a/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.js
+++ b/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.js
@@ -3,15 +3,11 @@
 
 frappe.ui.form.on('Bulk Transaction Log', {
 
-	before_load: function(frm) {
-		query(frm);
-	},
-
 	refresh: function(frm) {
 		frm.disable_save();
 		frm.add_custom_button(__('Retry Failed Transactions'), ()=>{
 			frappe.confirm(__("Retry Failing Transactions ?"), ()=>{
-				query(frm);
+				query(frm, 1);
 			}
 			);
 		});
@@ -25,8 +21,8 @@
 			log_date: frm.doc.log_date
 		}
 	}).then((r) => {
-		if (r.message) {
-			frm.remove_custom_button("Retry Failed Transactions");
+		if (r.message === "No Failed Records") {
+			frappe.show_alert(__(r.message), 5);
 		} else {
 			frappe.show_alert(__("Retrying Failed Transactions"), 5);
 		}
diff --git a/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.py b/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.py
index de7cde5..0596be4 100644
--- a/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.py
+++ b/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.py
@@ -15,6 +15,8 @@
 
 @frappe.whitelist()
 def retry_failing_transaction(log_date=None):
+	if not log_date:
+		log_date = str(date.today())
 	btp = frappe.qb.DocType("Bulk Transaction Log Detail")
 	data = (
 		frappe.qb.from_(btp)
@@ -26,8 +28,6 @@
 	).run(as_dict=True)
 
 	if data:
-		if not log_date:
-			log_date = str(date.today())
 		if len(data) > 10:
 			frappe.enqueue(job, queue="long", job_name="bulk_retry", data=data, log_date=log_date)
 		else:
@@ -35,6 +35,7 @@
 	else:
 		return "No Failed Records"
 
+
 def job(data, log_date):
 	for d in data:
 		failed = []
@@ -51,7 +52,7 @@
 				d.to_doctype,
 				status="Failed",
 				log_date=log_date,
-				restarted=1
+				restarted=1,
 			)
 
 		if not failed:
diff --git a/erpnext/bulk_transaction/doctype/bulk_transaction_log/test_bulk_transaction_log.py b/erpnext/bulk_transaction/doctype/bulk_transaction_log/test_bulk_transaction_log.py
index a78e697..646dba5 100644
--- a/erpnext/bulk_transaction/doctype/bulk_transaction_log/test_bulk_transaction_log.py
+++ b/erpnext/bulk_transaction/doctype/bulk_transaction_log/test_bulk_transaction_log.py
@@ -10,7 +10,6 @@
 
 
 class TestBulkTransactionLog(unittest.TestCase):
-
 	def setUp(self):
 		create_company()
 		create_customer()
@@ -19,7 +18,11 @@
 	def test_for_single_record(self):
 		so_name = create_so()
 		transaction_processing([{"name": so_name}], "Sales Order", "Sales Invoice")
-		data = frappe.db.get_list("Sales Invoice", filters = {"posting_date": date.today(), "customer": "Bulk Customer"}, fields=["*"])
+		data = frappe.db.get_list(
+			"Sales Invoice",
+			filters={"posting_date": date.today(), "customer": "Bulk Customer"},
+			fields=["*"],
+		)
 		if not data:
 			self.fail("No Sales Invoice Created !")
 
@@ -36,32 +39,35 @@
 				self.assertEqual(d.retried, 0)
 
 
-
 def create_company():
-	if not frappe.db.exists('Company', '_Test Company'):
-		frappe.get_doc({
-			'doctype': 'Company',
-			'company_name': '_Test Company',
-			'country': 'India',
-			'default_currency': 'INR'
-		}).insert()
+	if not frappe.db.exists("Company", "_Test Company"):
+		frappe.get_doc(
+			{
+				"doctype": "Company",
+				"company_name": "_Test Company",
+				"country": "India",
+				"default_currency": "INR",
+			}
+		).insert()
+
 
 def create_customer():
-	if not frappe.db.exists('Customer', 'Bulk Customer'):
-		frappe.get_doc({
-			'doctype': 'Customer',
-			'customer_name': 'Bulk Customer'
-		}).insert()
+	if not frappe.db.exists("Customer", "Bulk Customer"):
+		frappe.get_doc({"doctype": "Customer", "customer_name": "Bulk Customer"}).insert()
+
 
 def create_item():
 	if not frappe.db.exists("Item", "MK"):
-		frappe.get_doc({
-			"doctype": "Item",
-			"item_code": "MK",
-			"item_name": "Milk",
-			"description": "Milk",
-			"item_group": "Products"
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Item",
+				"item_code": "MK",
+				"item_name": "Milk",
+				"description": "Milk",
+				"item_group": "Products",
+			}
+		).insert()
+
 
 def create_so(intent=None):
 	so = frappe.new_doc("Sales Order")
@@ -70,12 +76,15 @@
 	so.transaction_date = date.today()
 
 	so.set_warehouse = "Finished Goods - _TC"
-	so.append("items", {
-		"item_code": "MK",
-		"delivery_date": date.today(),
-		"qty": 10,
-		"rate": 80,
-	})
+	so.append(
+		"items",
+		{
+			"item_code": "MK",
+			"delivery_date": date.today(),
+			"qty": 10,
+			"rate": 80,
+		},
+	)
 	so.insert()
 	so.submit()
-	return so.name
\ No newline at end of file
+	return so.name
diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json
index 50321ba..6c18a46 100644
--- a/erpnext/buying/doctype/buying_settings/buying_settings.json
+++ b/erpnext/buying/doctype/buying_settings/buying_settings.json
@@ -20,6 +20,7 @@
   "maintain_same_rate",
   "allow_multiple_items",
   "bill_for_rejected_quantity_in_purchase_invoice",
+  "enable_discount_accounting",
   "subcontract",
   "backflush_raw_materials_of_subcontract_based_on",
   "column_break_11",
@@ -133,6 +134,13 @@
   {
    "fieldname": "column_break_12",
    "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "description": "If enabled, additional ledger entries will be made for discounts in a separate Discount Account",
+   "fieldname": "enable_discount_accounting",
+   "fieldtype": "Check",
+   "label": "Enable Discount Accounting for Buying"
   }
  ],
  "icon": "fa fa-cog",
@@ -140,7 +148,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2022-01-27 17:57:58.367048",
+ "modified": "2022-05-31 19:40:26.103909",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Buying Settings",
@@ -154,6 +162,16 @@
    "role": "System Manager",
    "share": 1,
    "write": 1
+  },
+  {
+   "create": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "role": "Purchase Manager",
+   "share": 1,
+   "write": 1
   }
  ],
  "sort_field": "modified",
diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.py b/erpnext/buying/doctype/buying_settings/buying_settings.py
index 2b6ff43..7b18cdb 100644
--- a/erpnext/buying/doctype/buying_settings/buying_settings.py
+++ b/erpnext/buying/doctype/buying_settings/buying_settings.py
@@ -5,14 +5,81 @@
 
 
 import frappe
+from frappe.custom.doctype.property_setter.property_setter import make_property_setter
 from frappe.model.document import Document
+from frappe.utils import cint
 
 
 class BuyingSettings(Document):
+	def on_update(self):
+		self.toggle_discount_accounting_fields()
+
 	def validate(self):
 		for key in ["supplier_group", "supp_master_name", "maintain_same_rate", "buying_price_list"]:
 			frappe.db.set_default(key, self.get(key, ""))
 
-		from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
-		set_by_naming_series("Supplier", "supplier_name",
-			self.get("supp_master_name")=="Naming Series", hide_name_field=False)
+		from erpnext.utilities.naming import set_by_naming_series
+
+		set_by_naming_series(
+			"Supplier",
+			"supplier_name",
+			self.get("supp_master_name") == "Naming Series",
+			hide_name_field=False,
+		)
+
+	def toggle_discount_accounting_fields(self):
+		enable_discount_accounting = cint(self.enable_discount_accounting)
+
+		make_property_setter(
+			"Purchase Invoice Item",
+			"discount_account",
+			"hidden",
+			not (enable_discount_accounting),
+			"Check",
+			validate_fields_for_doctype=False,
+		)
+		if enable_discount_accounting:
+			make_property_setter(
+				"Purchase Invoice Item",
+				"discount_account",
+				"mandatory_depends_on",
+				"eval: doc.discount_amount",
+				"Code",
+				validate_fields_for_doctype=False,
+			)
+		else:
+			make_property_setter(
+				"Purchase Invoice Item",
+				"discount_account",
+				"mandatory_depends_on",
+				"",
+				"Code",
+				validate_fields_for_doctype=False,
+			)
+
+		make_property_setter(
+			"Purchase Invoice",
+			"additional_discount_account",
+			"hidden",
+			not (enable_discount_accounting),
+			"Check",
+			validate_fields_for_doctype=False,
+		)
+		if enable_discount_accounting:
+			make_property_setter(
+				"Purchase Invoice",
+				"additional_discount_account",
+				"mandatory_depends_on",
+				"eval: doc.discount_amount",
+				"Code",
+				validate_fields_for_doctype=False,
+			)
+		else:
+			make_property_setter(
+				"Purchase Invoice",
+				"additional_discount_account",
+				"mandatory_depends_on",
+				"",
+				"Code",
+				validate_fields_for_doctype=False,
+			)
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index 2005dac..da45610 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -43,8 +43,6 @@
 		erpnext.queries.setup_queries(frm, "Warehouse", function() {
 			return erpnext.queries.warehouse(frm.doc);
 		});
-
-		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
 	},
 
 	apply_tds: function(frm) {
@@ -179,7 +177,7 @@
 				if (doc.status != "On Hold") {
 					if(flt(doc.per_received) < 100 && allow_receipt) {
 						cur_frm.add_custom_button(__('Purchase Receipt'), this.make_purchase_receipt, __('Create'));
-						if(doc.is_subcontracted==="Yes" && me.has_unsupplied_items()) {
+						if(doc.is_subcontracted && me.has_unsupplied_items()) {
 							cur_frm.add_custom_button(__('Material to Supplier'),
 								function() { me.make_stock_entry(); }, __("Transfer"));
 						}
@@ -636,7 +634,7 @@
 frappe.provide("erpnext.buying");
 
 frappe.ui.form.on("Purchase Order", "is_subcontracted", function(frm) {
-	if (frm.doc.is_subcontracted === "Yes") {
+	if (frm.doc.is_subcontracted) {
 		erpnext.buying.get_default_bom(frm);
 	}
 });
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index 896208f..b365a83 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -23,6 +23,10 @@
   "order_confirmation_no",
   "order_confirmation_date",
   "amended_from",
+  "accounting_dimensions_section",
+  "cost_center",
+  "dimension_col_break",
+  "project",
   "drop_ship",
   "customer",
   "customer_name",
@@ -457,16 +461,15 @@
    "fieldtype": "Column Break"
   },
   {
-   "default": "No",
+   "default": "0",
    "fieldname": "is_subcontracted",
-   "fieldtype": "Select",
+   "fieldtype": "Check",
    "in_standard_filter": 1,
-   "label": "Supply Raw Materials",
-   "options": "No\nYes",
+   "label": "Is Subcontracted",
    "print_hide": 1
   },
   {
-   "depends_on": "eval:doc.is_subcontracted==\"Yes\"",
+   "depends_on": "eval:doc.is_subcontracted",
    "fieldname": "supplier_warehouse",
    "fieldtype": "Link",
    "label": "Supplier Warehouse",
@@ -1138,16 +1141,39 @@
    "fieldtype": "Link",
    "label": "Tax Withholding Category",
    "options": "Tax Withholding Category"
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "accounting_dimensions_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Dimensions "
+  },
+  {
+   "fieldname": "cost_center",
+   "fieldtype": "Link",
+   "label": "Cost Center",
+   "options": "Cost Center"
+  },
+  {
+   "fieldname": "dimension_col_break",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "project",
+   "fieldtype": "Link",
+   "label": "Project",
+   "options": "Project"
   }
  ],
  "icon": "fa fa-file-text",
  "idx": 105,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-09-28 13:10:47.955401",
+ "modified": "2022-04-26 12:16:38.694276",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order",
+ "naming_rule": "By \"Naming Series\" field",
  "owner": "Administrator",
  "permissions": [
   {
@@ -1194,6 +1220,7 @@
  "show_name_in_global_search": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "timeline_field": "supplier",
  "title_field": "supplier_name",
  "track_changes": 1
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index 2e7d306..44426ba 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -26,24 +26,25 @@
 from erpnext.stock.stock_balance import get_ordered_qty, update_bin_qty
 from erpnext.stock.utils import get_bin
 
-form_grid_templates = {
-	"items": "templates/form_grid/item_grid.html"
-}
+form_grid_templates = {"items": "templates/form_grid/item_grid.html"}
+
 
 class PurchaseOrder(BuyingController):
 	def __init__(self, *args, **kwargs):
 		super(PurchaseOrder, self).__init__(*args, **kwargs)
-		self.status_updater = [{
-			'source_dt': 'Purchase Order Item',
-			'target_dt': 'Material Request Item',
-			'join_field': 'material_request_item',
-			'target_field': 'ordered_qty',
-			'target_parent_dt': 'Material Request',
-			'target_parent_field': 'per_ordered',
-			'target_ref_field': 'stock_qty',
-			'source_field': 'stock_qty',
-			'percent_join_field': 'material_request'
-		}]
+		self.status_updater = [
+			{
+				"source_dt": "Purchase Order Item",
+				"target_dt": "Material Request Item",
+				"join_field": "material_request_item",
+				"target_field": "ordered_qty",
+				"target_parent_dt": "Material Request",
+				"target_parent_field": "per_ordered",
+				"target_ref_field": "stock_qty",
+				"source_field": "stock_qty",
+				"percent_join_field": "material_request",
+			}
+		]
 
 	def onload(self):
 		supplier_tds = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")
@@ -71,35 +72,44 @@
 		self.validate_bom_for_subcontracting_items()
 		self.create_raw_materials_supplied("supplied_items")
 		self.set_received_qty_for_drop_ship_items()
-		validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_order_reference)
+		validate_inter_company_party(
+			self.doctype, self.supplier, self.company, self.inter_company_order_reference
+		)
 		self.reset_default_field_value("set_warehouse", "items", "warehouse")
 
 	def validate_with_previous_doc(self):
-		super(PurchaseOrder, self).validate_with_previous_doc({
-			"Supplier Quotation": {
-				"ref_dn_field": "supplier_quotation",
-				"compare_fields": [["supplier", "="], ["company", "="], ["currency", "="]],
-			},
-			"Supplier Quotation Item": {
-				"ref_dn_field": "supplier_quotation_item",
-				"compare_fields": [["project", "="], ["item_code", "="],
-					["uom", "="], ["conversion_factor", "="]],
-				"is_child_table": True
-			},
-			"Material Request": {
-				"ref_dn_field": "material_request",
-				"compare_fields": [["company", "="]],
-			},
-			"Material Request Item": {
-				"ref_dn_field": "material_request_item",
-				"compare_fields": [["project", "="], ["item_code", "="]],
-				"is_child_table": True
+		super(PurchaseOrder, self).validate_with_previous_doc(
+			{
+				"Supplier Quotation": {
+					"ref_dn_field": "supplier_quotation",
+					"compare_fields": [["supplier", "="], ["company", "="], ["currency", "="]],
+				},
+				"Supplier Quotation Item": {
+					"ref_dn_field": "supplier_quotation_item",
+					"compare_fields": [
+						["project", "="],
+						["item_code", "="],
+						["uom", "="],
+						["conversion_factor", "="],
+					],
+					"is_child_table": True,
+				},
+				"Material Request": {
+					"ref_dn_field": "material_request",
+					"compare_fields": [["company", "="]],
+				},
+				"Material Request Item": {
+					"ref_dn_field": "material_request_item",
+					"compare_fields": [["project", "="], ["item_code", "="]],
+					"is_child_table": True,
+				},
 			}
-		})
+		)
 
-
-		if cint(frappe.db.get_single_value('Buying Settings', 'maintain_same_rate')):
-			self.validate_rate_with_reference_doc([["Supplier Quotation", "supplier_quotation", "supplier_quotation_item"]])
+		if cint(frappe.db.get_single_value("Buying Settings", "maintain_same_rate")):
+			self.validate_rate_with_reference_doc(
+				[["Supplier Quotation", "supplier_quotation", "supplier_quotation_item"]]
+			)
 
 	def set_tax_withholding(self):
 		if not self.apply_tds:
@@ -119,8 +129,11 @@
 		if not accounts or tax_withholding_details.get("account_head") not in accounts:
 			self.append("taxes", tax_withholding_details)
 
-		to_remove = [d for d in self.taxes
-			if not d.tax_amount and d.account_head == tax_withholding_details.get("account_head")]
+		to_remove = [
+			d
+			for d in self.taxes
+			if not d.tax_amount and d.account_head == tax_withholding_details.get("account_head")
+		]
 
 		for d in to_remove:
 			self.remove(d)
@@ -129,26 +142,43 @@
 		self.calculate_taxes_and_totals()
 
 	def validate_supplier(self):
-		prevent_po = frappe.db.get_value("Supplier", self.supplier, 'prevent_pos')
+		prevent_po = frappe.db.get_value("Supplier", self.supplier, "prevent_pos")
 		if prevent_po:
-			standing = frappe.db.get_value("Supplier Scorecard", self.supplier, 'status')
+			standing = frappe.db.get_value("Supplier Scorecard", self.supplier, "status")
 			if standing:
-				frappe.throw(_("Purchase Orders are not allowed for {0} due to a scorecard standing of {1}.")
-					.format(self.supplier, standing))
+				frappe.throw(
+					_("Purchase Orders are not allowed for {0} due to a scorecard standing of {1}.").format(
+						self.supplier, standing
+					)
+				)
 
-		warn_po = frappe.db.get_value("Supplier", self.supplier, 'warn_pos')
+		warn_po = frappe.db.get_value("Supplier", self.supplier, "warn_pos")
 		if warn_po:
-			standing = frappe.db.get_value("Supplier Scorecard",self.supplier, 'status')
-			frappe.msgprint(_("{0} currently has a {1} Supplier Scorecard standing, and Purchase Orders to this supplier should be issued with caution.").format(self.supplier, standing), title=_("Caution"), indicator='orange')
+			standing = frappe.db.get_value("Supplier Scorecard", self.supplier, "status")
+			frappe.msgprint(
+				_(
+					"{0} currently has a {1} Supplier Scorecard standing, and Purchase Orders to this supplier should be issued with caution."
+				).format(self.supplier, standing),
+				title=_("Caution"),
+				indicator="orange",
+			)
 
 		self.party_account_currency = get_party_account_currency("Supplier", self.supplier, self.company)
 
 	def validate_minimum_order_qty(self):
-		if not self.get("items"): return
+		if not self.get("items"):
+			return
 		items = list(set(d.item_code for d in self.get("items")))
 
-		itemwise_min_order_qty = frappe._dict(frappe.db.sql("""select name, min_order_qty
-			from tabItem where name in ({0})""".format(", ".join(["%s"] * len(items))), items))
+		itemwise_min_order_qty = frappe._dict(
+			frappe.db.sql(
+				"""select name, min_order_qty
+			from tabItem where name in ({0})""".format(
+					", ".join(["%s"] * len(items))
+				),
+				items,
+			)
+		)
 
 		itemwise_qty = frappe._dict()
 		for d in self.get("items"):
@@ -157,36 +187,43 @@
 
 		for item_code, qty in itemwise_qty.items():
 			if flt(qty) < flt(itemwise_min_order_qty.get(item_code)):
-				frappe.throw(_("Item {0}: Ordered qty {1} cannot be less than minimum order qty {2} (defined in Item).").format(item_code,
-					qty, itemwise_min_order_qty.get(item_code)))
+				frappe.throw(
+					_(
+						"Item {0}: Ordered qty {1} cannot be less than minimum order qty {2} (defined in Item)."
+					).format(item_code, qty, itemwise_min_order_qty.get(item_code))
+				)
 
 	def validate_bom_for_subcontracting_items(self):
-		if self.is_subcontracted == "Yes":
+		if self.is_subcontracted:
 			for item in self.items:
 				if not item.bom:
-					frappe.throw(_("BOM is not specified for subcontracting item {0} at row {1}")
-						.format(item.item_code, item.idx))
+					frappe.throw(
+						_("BOM is not specified for subcontracting item {0} at row {1}").format(
+							item.item_code, item.idx
+						)
+					)
 
 	def get_schedule_dates(self):
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if d.material_request_item and not d.schedule_date:
-				d.schedule_date = frappe.db.get_value("Material Request Item",
-						d.material_request_item, "schedule_date")
-
+				d.schedule_date = frappe.db.get_value(
+					"Material Request Item", d.material_request_item, "schedule_date"
+				)
 
 	@frappe.whitelist()
 	def get_last_purchase_rate(self):
 		"""get last purchase rates for all items"""
 
-		conversion_rate = flt(self.get('conversion_rate')) or 1.0
+		conversion_rate = flt(self.get("conversion_rate")) or 1.0
 		for d in self.get("items"):
 			if d.item_code:
 				last_purchase_details = get_last_purchase_details(d.item_code, self.name)
 				if last_purchase_details:
-					d.base_price_list_rate = (last_purchase_details['base_price_list_rate'] *
-						(flt(d.conversion_factor) or 1.0))
-					d.discount_percentage = last_purchase_details['discount_percentage']
-					d.base_rate = last_purchase_details['base_rate'] * (flt(d.conversion_factor) or 1.0)
+					d.base_price_list_rate = last_purchase_details["base_price_list_rate"] * (
+						flt(d.conversion_factor) or 1.0
+					)
+					d.discount_percentage = last_purchase_details["discount_percentage"]
+					d.base_rate = last_purchase_details["base_rate"] * (flt(d.conversion_factor) or 1.0)
 					d.price_list_rate = d.base_price_list_rate / conversion_rate
 					d.rate = d.base_rate / conversion_rate
 					d.last_purchase_rate = d.rate
@@ -194,16 +231,21 @@
 
 					item_last_purchase_rate = frappe.get_cached_value("Item", d.item_code, "last_purchase_rate")
 					if item_last_purchase_rate:
-						d.base_price_list_rate = d.base_rate = d.price_list_rate \
-							= d.rate = d.last_purchase_rate = item_last_purchase_rate
+						d.base_price_list_rate = (
+							d.base_rate
+						) = d.price_list_rate = d.rate = d.last_purchase_rate = item_last_purchase_rate
 
 	# Check for Closed status
 	def check_on_hold_or_closed_status(self):
-		check_list =[]
-		for d in self.get('items'):
-			if d.meta.get_field('material_request') and d.material_request and d.material_request not in check_list:
+		check_list = []
+		for d in self.get("items"):
+			if (
+				d.meta.get_field("material_request")
+				and d.material_request
+				and d.material_request not in check_list
+			):
 				check_list.append(d.material_request)
-				check_on_hold_or_closed_status('Material Request', d.material_request)
+				check_on_hold_or_closed_status("Material Request", d.material_request)
 
 	def update_requested_qty(self):
 		material_request_map = {}
@@ -216,7 +258,9 @@
 				mr_obj = frappe.get_doc("Material Request", mr)
 
 				if mr_obj.status in ["Stopped", "Cancelled"]:
-					frappe.throw(_("Material Request {0} is cancelled or stopped").format(mr), frappe.InvalidStatusError)
+					frappe.throw(
+						_("Material Request {0} is cancelled or stopped").format(mr), frappe.InvalidStatusError
+					)
 
 				mr_obj.update_requested_qty(mr_item_rows)
 
@@ -224,31 +268,33 @@
 		"""update requested qty (before ordered_qty is updated)"""
 		item_wh_list = []
 		for d in self.get("items"):
-			if (not po_item_rows or d.name in po_item_rows) \
-				and [d.item_code, d.warehouse] not in item_wh_list \
-				and frappe.get_cached_value("Item", d.item_code, "is_stock_item") \
-				and d.warehouse and not d.delivered_by_supplier:
-					item_wh_list.append([d.item_code, d.warehouse])
+			if (
+				(not po_item_rows or d.name in po_item_rows)
+				and [d.item_code, d.warehouse] not in item_wh_list
+				and frappe.get_cached_value("Item", d.item_code, "is_stock_item")
+				and d.warehouse
+				and not d.delivered_by_supplier
+			):
+				item_wh_list.append([d.item_code, d.warehouse])
 		for item_code, warehouse in item_wh_list:
-			update_bin_qty(item_code, warehouse, {
-				"ordered_qty": get_ordered_qty(item_code, warehouse)
-			})
+			update_bin_qty(item_code, warehouse, {"ordered_qty": get_ordered_qty(item_code, warehouse)})
 
 	def check_modified_date(self):
-		mod_db = frappe.db.sql("select modified from `tabPurchase Order` where name = %s",
-			self.name)
+		mod_db = frappe.db.sql("select modified from `tabPurchase Order` where name = %s", self.name)
 		date_diff = frappe.db.sql("select '%s' - '%s' " % (mod_db[0][0], cstr(self.modified)))
 
 		if date_diff and date_diff[0][0]:
-			msgprint(_("{0} {1} has been modified. Please refresh.").format(self.doctype, self.name),
-				raise_exception=True)
+			msgprint(
+				_("{0} {1} has been modified. Please refresh.").format(self.doctype, self.name),
+				raise_exception=True,
+			)
 
 	def update_status(self, status):
 		self.check_modified_date()
 		self.set_status(update=True, status=status)
 		self.update_requested_qty()
 		self.update_ordered_qty()
-		if self.is_subcontracted == "Yes":
+		if self.is_subcontracted:
 			self.update_reserved_qty_for_subcontract()
 
 		self.notify_update()
@@ -265,17 +311,19 @@
 		self.update_ordered_qty()
 		self.validate_budget()
 
-		if self.is_subcontracted == "Yes":
+		if self.is_subcontracted:
 			self.update_reserved_qty_for_subcontract()
 
-		frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
-			self.company, self.base_grand_total)
+		frappe.get_doc("Authorization Control").validate_approving_authority(
+			self.doctype, self.company, self.base_grand_total
+		)
 
 		self.update_blanket_order()
 
 		update_linked_doc(self.doctype, self.name, self.inter_company_order_reference)
 
 	def on_cancel(self):
+		self.ignore_linked_doctypes = "Payment Ledger Entry"
 		super(PurchaseOrder, self).on_cancel()
 
 		if self.is_against_so():
@@ -284,12 +332,12 @@
 		if self.has_drop_ship_item():
 			self.update_delivered_qty_in_sales_order()
 
-		if self.is_subcontracted == "Yes":
+		if self.is_subcontracted:
 			self.update_reserved_qty_for_subcontract()
 
 		self.check_on_hold_or_closed_status()
 
-		frappe.db.set(self,'status','Cancelled')
+		frappe.db.set(self, "status", "Cancelled")
 
 		self.update_prevdoc_status()
 
@@ -306,26 +354,30 @@
 		pass
 
 	def update_status_updater(self):
-		self.status_updater.append({
-			'source_dt': 'Purchase Order Item',
-			'target_dt': 'Sales Order Item',
-			'target_field': 'ordered_qty',
-			'target_parent_dt': 'Sales Order',
-			'target_parent_field': '',
-			'join_field': 'sales_order_item',
-			'target_ref_field': 'stock_qty',
-			'source_field': 'stock_qty'
-		})
-		self.status_updater.append({
-			'source_dt': 'Purchase Order Item',
-			'target_dt': 'Packed Item',
-			'target_field': 'ordered_qty',
-			'target_parent_dt': 'Sales Order',
-			'target_parent_field': '',
-			'join_field': 'sales_order_packed_item',
-			'target_ref_field': 'qty',
-			'source_field': 'stock_qty'
-		})
+		self.status_updater.append(
+			{
+				"source_dt": "Purchase Order Item",
+				"target_dt": "Sales Order Item",
+				"target_field": "ordered_qty",
+				"target_parent_dt": "Sales Order",
+				"target_parent_field": "",
+				"join_field": "sales_order_item",
+				"target_ref_field": "stock_qty",
+				"source_field": "stock_qty",
+			}
+		)
+		self.status_updater.append(
+			{
+				"source_dt": "Purchase Order Item",
+				"target_dt": "Packed Item",
+				"target_field": "ordered_qty",
+				"target_parent_dt": "Sales Order",
+				"target_parent_field": "",
+				"join_field": "sales_order_packed_item",
+				"target_ref_field": "qty",
+				"source_field": "stock_qty",
+			}
+		)
 
 	def update_delivered_qty_in_sales_order(self):
 		"""Update delivered qty in Sales Order for drop ship"""
@@ -364,24 +416,28 @@
 			received_qty += item.received_qty
 			total_qty += item.qty
 		if total_qty:
-			self.db_set("per_received", flt(received_qty/total_qty) * 100, update_modified=False)
+			self.db_set("per_received", flt(received_qty / total_qty) * 100, update_modified=False)
 		else:
 			self.db_set("per_received", 0, update_modified=False)
 
-def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor= 1.0):
+
+def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor=1.0):
 	"""get last purchase rate for an item"""
 
 	conversion_rate = flt(conversion_rate) or 1.0
 
-	last_purchase_details =  get_last_purchase_details(item_code, name)
+	last_purchase_details = get_last_purchase_details(item_code, name)
 	if last_purchase_details:
-		last_purchase_rate = (last_purchase_details['base_net_rate'] * (flt(conversion_factor) or 1.0)) / conversion_rate
+		last_purchase_rate = (
+			last_purchase_details["base_net_rate"] * (flt(conversion_factor) or 1.0)
+		) / conversion_rate
 		return last_purchase_rate
 	else:
 		item_last_purchase_rate = frappe.get_cached_value("Item", item_code, "last_purchase_rate")
 		if item_last_purchase_rate:
 			return item_last_purchase_rate
 
+
 @frappe.whitelist()
 def close_or_unclose_purchase_orders(names, status):
 	if not frappe.has_permission("Purchase Order", "write"):
@@ -392,7 +448,7 @@
 		po = frappe.get_doc("Purchase Order", name)
 		if po.docstatus == 1:
 			if status == "Closed":
-				if po.status not in ( "Cancelled", "Closed") and (po.per_received < 100 or po.per_billed < 100):
+				if po.status not in ("Cancelled", "Closed") and (po.per_received < 100 or po.per_billed < 100):
 					po.update_status(status)
 			else:
 				if po.status == "Closed":
@@ -401,69 +457,78 @@
 
 	frappe.local.message_log = []
 
+
 def set_missing_values(source, target):
-	target.ignore_pricing_rule = 1
 	target.run_method("set_missing_values")
 	target.run_method("calculate_taxes_and_totals")
 
+
 @frappe.whitelist()
 def make_purchase_receipt(source_name, target_doc=None):
 	def update_item(obj, target, source_parent):
 		target.qty = flt(obj.qty) - flt(obj.received_qty)
 		target.stock_qty = (flt(obj.qty) - flt(obj.received_qty)) * flt(obj.conversion_factor)
 		target.amount = (flt(obj.qty) - flt(obj.received_qty)) * flt(obj.rate)
-		target.base_amount = (flt(obj.qty) - flt(obj.received_qty)) * \
-			flt(obj.rate) * flt(source_parent.conversion_rate)
+		target.base_amount = (
+			(flt(obj.qty) - flt(obj.received_qty)) * flt(obj.rate) * flt(source_parent.conversion_rate)
+		)
 
-	doc = get_mapped_doc("Purchase Order", source_name,	{
-		"Purchase Order": {
-			"doctype": "Purchase Receipt",
-			"field_map": {
-				"supplier_warehouse":"supplier_warehouse"
+	doc = get_mapped_doc(
+		"Purchase Order",
+		source_name,
+		{
+			"Purchase Order": {
+				"doctype": "Purchase Receipt",
+				"field_map": {"supplier_warehouse": "supplier_warehouse"},
+				"validation": {
+					"docstatus": ["=", 1],
+				},
 			},
-			"validation": {
-				"docstatus": ["=", 1],
-			}
-		},
-		"Purchase Order Item": {
-			"doctype": "Purchase Receipt Item",
-			"field_map": {
-				"name": "purchase_order_item",
-				"parent": "purchase_order",
-				"bom": "bom",
-				"material_request": "material_request",
-				"material_request_item": "material_request_item"
+			"Purchase Order Item": {
+				"doctype": "Purchase Receipt Item",
+				"field_map": {
+					"name": "purchase_order_item",
+					"parent": "purchase_order",
+					"bom": "bom",
+					"material_request": "material_request",
+					"material_request_item": "material_request_item",
+				},
+				"postprocess": update_item,
+				"condition": lambda doc: abs(doc.received_qty) < abs(doc.qty)
+				and doc.delivered_by_supplier != 1,
 			},
-			"postprocess": update_item,
-			"condition": lambda doc: abs(doc.received_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1
+			"Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges", "add_if_empty": True},
 		},
-		"Purchase Taxes and Charges": {
-			"doctype": "Purchase Taxes and Charges",
-			"add_if_empty": True
-		}
-	}, target_doc, set_missing_values)
+		target_doc,
+		set_missing_values,
+	)
+
+	doc.set_onload("ignore_price_list", True)
 
 	return doc
 
+
 @frappe.whitelist()
 def make_purchase_invoice(source_name, target_doc=None):
 	return get_mapped_purchase_invoice(source_name, target_doc)
 
+
 @frappe.whitelist()
 def make_purchase_invoice_from_portal(purchase_order_name):
 	doc = get_mapped_purchase_invoice(purchase_order_name, ignore_permissions=True)
 	if doc.contact_email != frappe.session.user:
-		frappe.throw(_('Not Permitted'), frappe.PermissionError)
+		frappe.throw(_("Not Permitted"), frappe.PermissionError)
 	doc.save()
 	frappe.db.commit()
-	frappe.response['type'] = 'redirect'
-	frappe.response.location = '/purchase-invoices/' + doc.name
+	frappe.response["type"] = "redirect"
+	frappe.response.location = "/purchase-invoices/" + doc.name
+
 
 def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions=False):
 	def postprocess(source, target):
 		target.flags.ignore_permissions = ignore_permissions
 		set_missing_values(source, target)
-		#Get the advance paid Journal Entries in Purchase Invoice Advance
+		# Get the advance paid Journal Entries in Purchase Invoice Advance
 		if target.get("allocate_advances_automatically"):
 			target.set_advances()
 
@@ -472,26 +537,30 @@
 	def update_item(obj, target, source_parent):
 		target.amount = flt(obj.amount) - flt(obj.billed_amt)
 		target.base_amount = target.amount * flt(source_parent.conversion_rate)
-		target.qty = target.amount / flt(obj.rate) if (flt(obj.rate) and flt(obj.billed_amt)) else flt(obj.qty)
+		target.qty = (
+			target.amount / flt(obj.rate) if (flt(obj.rate) and flt(obj.billed_amt)) else flt(obj.qty)
+		)
 
 		item = get_item_defaults(target.item_code, source_parent.company)
 		item_group = get_item_group_defaults(target.item_code, source_parent.company)
-		target.cost_center = (obj.cost_center
+		target.cost_center = (
+			obj.cost_center
 			or frappe.db.get_value("Project", obj.project, "cost_center")
 			or item.get("buying_cost_center")
-			or item_group.get("buying_cost_center"))
+			or item_group.get("buying_cost_center")
+		)
 
 	fields = {
 		"Purchase Order": {
 			"doctype": "Purchase Invoice",
 			"field_map": {
 				"party_account_currency": "party_account_currency",
-				"supplier_warehouse":"supplier_warehouse"
+				"supplier_warehouse": "supplier_warehouse",
 			},
-			"field_no_map" : ["payment_terms_template"],
+			"field_no_map": ["payment_terms_template"],
 			"validation": {
 				"docstatus": ["=", 1],
-			}
+			},
 		},
 		"Purchase Order Item": {
 			"doctype": "Purchase Invoice Item",
@@ -500,19 +569,24 @@
 				"parent": "purchase_order",
 			},
 			"postprocess": update_item,
-			"condition": lambda doc: (doc.base_amount==0 or abs(doc.billed_amt) < abs(doc.amount))
+			"condition": lambda doc: (doc.base_amount == 0 or abs(doc.billed_amt) < abs(doc.amount)),
 		},
-		"Purchase Taxes and Charges": {
-			"doctype": "Purchase Taxes and Charges",
-			"add_if_empty": True
-		},
+		"Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges", "add_if_empty": True},
 	}
 
-	doc = get_mapped_doc("Purchase Order", source_name,	fields,
-		target_doc, postprocess, ignore_permissions=ignore_permissions)
+	doc = get_mapped_doc(
+		"Purchase Order",
+		source_name,
+		fields,
+		target_doc,
+		postprocess,
+		ignore_permissions=ignore_permissions,
+	)
+	doc.set_onload("ignore_price_list", True)
 
 	return doc
 
+
 @frappe.whitelist()
 def make_rm_stock_entry(purchase_order, rm_items):
 	rm_items_list = rm_items
@@ -553,71 +627,90 @@
 						rm_item_code: {
 							"po_detail": rm_item_data.get("name"),
 							"item_name": rm_item_data["item_name"],
-							"description": item_wh.get(rm_item_code, {}).get('description', ""),
-							'qty': rm_item_data["qty"],
-							'from_warehouse': rm_item_data["warehouse"],
-							'stock_uom': rm_item_data["stock_uom"],
-							'serial_no': rm_item_data.get('serial_no'),
-							'batch_no': rm_item_data.get('batch_no'),
-							'main_item_code': rm_item_data["item_code"],
-							'allow_alternative_item': item_wh.get(rm_item_code, {}).get('allow_alternative_item')
+							"description": item_wh.get(rm_item_code, {}).get("description", ""),
+							"qty": rm_item_data["qty"],
+							"from_warehouse": rm_item_data["warehouse"],
+							"stock_uom": rm_item_data["stock_uom"],
+							"serial_no": rm_item_data.get("serial_no"),
+							"batch_no": rm_item_data.get("batch_no"),
+							"main_item_code": rm_item_data["item_code"],
+							"allow_alternative_item": item_wh.get(rm_item_code, {}).get("allow_alternative_item"),
 						}
 					}
 					stock_entry.add_to_stock_entry_detail(items_dict)
+
+		stock_entry.set_missing_values()
 		return stock_entry.as_dict()
 	else:
 		frappe.throw(_("No Items selected for transfer"))
 	return purchase_order.name
 
+
 def get_item_details(items):
 	item_details = {}
-	for d in frappe.db.sql("""select item_code, description, allow_alternative_item from `tabItem`
-		where name in ({0})""".format(", ".join(["%s"] * len(items))), items, as_dict=1):
+	for d in frappe.db.sql(
+		"""select item_code, description, allow_alternative_item from `tabItem`
+		where name in ({0})""".format(
+			", ".join(["%s"] * len(items))
+		),
+		items,
+		as_dict=1,
+	):
 		item_details[d.item_code] = d
 
 	return item_details
 
+
 def get_list_context(context=None):
 	from erpnext.controllers.website_list_for_contact import get_list_context
+
 	list_context = get_list_context(context)
-	list_context.update({
-		'show_sidebar': True,
-		'show_search': True,
-		'no_breadcrumbs': True,
-		'title': _('Purchase Orders'),
-	})
+	list_context.update(
+		{
+			"show_sidebar": True,
+			"show_search": True,
+			"no_breadcrumbs": True,
+			"title": _("Purchase Orders"),
+		}
+	)
 	return list_context
 
+
 @frappe.whitelist()
 def update_status(status, name):
 	po = frappe.get_doc("Purchase Order", name)
 	po.update_status(status)
 	po.update_delivered_qty_in_sales_order()
 
+
 @frappe.whitelist()
 def make_inter_company_sales_order(source_name, target_doc=None):
 	from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction
+
 	return make_inter_company_transaction("Purchase Order", source_name, target_doc)
 
+
 @frappe.whitelist()
 def get_materials_from_supplier(purchase_order, po_details):
 	if isinstance(po_details, str):
 		po_details = json.loads(po_details)
 
-	doc = frappe.get_cached_doc('Purchase Order', purchase_order)
+	doc = frappe.get_cached_doc("Purchase Order", purchase_order)
 	doc.initialized_fields()
 	doc.purchase_orders = [doc.name]
 	doc.get_available_materials()
 
 	if not doc.available_materials:
-		frappe.throw(_('Materials are already received against the purchase order {0}')
-			.format(purchase_order))
+		frappe.throw(
+			_("Materials are already received against the purchase order {0}").format(purchase_order)
+		)
 
 	return make_return_stock_entry_for_subcontract(doc.available_materials, doc, po_details)
 
+
 def make_return_stock_entry_for_subcontract(available_materials, po_doc, po_details):
-	ste_doc = frappe.new_doc('Stock Entry')
-	ste_doc.purpose = 'Material Transfer'
+	ste_doc = frappe.new_doc("Stock Entry")
+	ste_doc.purpose = "Material Transfer"
 	ste_doc.purchase_order = po_doc.name
 	ste_doc.company = po_doc.company
 	ste_doc.is_return = 1
@@ -634,22 +727,25 @@
 			add_items_in_ste(ste_doc, value, value.qty, po_details)
 
 	ste_doc.set_stock_entry_type()
-	ste_doc.calculate_rate_and_amount()
+	ste_doc.set_missing_values()
 
 	return ste_doc
 
+
 def add_items_in_ste(ste_doc, row, qty, po_details, batch_no=None):
-	item = ste_doc.append('items', row.item_details)
+	item = ste_doc.append("items", row.item_details)
 
 	po_detail = list(set(row.po_details).intersection(po_details))
-	item.update({
-		'qty': qty,
-		'batch_no': batch_no,
-		'basic_rate': row.item_details['rate'],
-		'po_detail': po_detail[0] if po_detail else '',
-		's_warehouse': row.item_details['t_warehouse'],
-		't_warehouse': row.item_details['s_warehouse'],
-		'item_code': row.item_details['rm_item_code'],
-		'subcontracted_item': row.item_details['main_item_code'],
-		'serial_no': '\n'.join(row.serial_no) if row.serial_no else ''
-	})
+	item.update(
+		{
+			"qty": qty,
+			"batch_no": batch_no,
+			"basic_rate": row.item_details["rate"],
+			"po_detail": po_detail[0] if po_detail else "",
+			"s_warehouse": row.item_details["t_warehouse"],
+			"t_warehouse": row.item_details["s_warehouse"],
+			"item_code": row.item_details["rm_item_code"],
+			"subcontracted_item": row.item_details["main_item_code"],
+			"serial_no": "\n".join(row.serial_no) if row.serial_no else "",
+		}
+	)
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py b/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py
index d288f88..81f2010 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py
@@ -3,34 +3,25 @@
 
 def get_data():
 	return {
-		'fieldname': 'purchase_order',
-		'non_standard_fieldnames': {
-			'Journal Entry': 'reference_name',
-			'Payment Entry': 'reference_name',
-			'Payment Request': 'reference_name',
-			'Auto Repeat': 'reference_document'
+		"fieldname": "purchase_order",
+		"non_standard_fieldnames": {
+			"Journal Entry": "reference_name",
+			"Payment Entry": "reference_name",
+			"Payment Request": "reference_name",
+			"Auto Repeat": "reference_document",
 		},
-		'internal_links': {
-			'Material Request': ['items', 'material_request'],
-			'Supplier Quotation': ['items', 'supplier_quotation'],
-			'Project': ['items', 'project'],
+		"internal_links": {
+			"Material Request": ["items", "material_request"],
+			"Supplier Quotation": ["items", "supplier_quotation"],
+			"Project": ["items", "project"],
 		},
-		'transactions': [
+		"transactions": [
+			{"label": _("Related"), "items": ["Purchase Receipt", "Purchase Invoice"]},
+			{"label": _("Payment"), "items": ["Payment Entry", "Journal Entry", "Payment Request"]},
 			{
-				'label': _('Related'),
-				'items': ['Purchase Receipt', 'Purchase Invoice']
+				"label": _("Reference"),
+				"items": ["Material Request", "Supplier Quotation", "Project", "Auto Repeat"],
 			},
-			{
-				'label': _('Payment'),
-				'items': ['Payment Entry', 'Journal Entry', 'Payment Request']
-			},
-			{
-				'label': _('Reference'),
-				'items': ['Material Request', 'Supplier Quotation', 'Project', 'Auto Repeat']
-			},
-			{
-				'label': _('Sub-contracting'),
-				'items': ['Stock Entry']
-			},
-		]
+			{"label": _("Sub-contracting"), "items": ["Stock Entry"]},
+		],
 	}
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index efa2ab1..d732b75 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -51,7 +51,7 @@
 		po.load_from_db()
 		self.assertEqual(po.get("items")[0].received_qty, 4)
 
-		frappe.db.set_value('Item', '_Test Item', 'over_delivery_receipt_allowance', 50)
+		frappe.db.set_value("Item", "_Test Item", "over_delivery_receipt_allowance", 50)
 
 		pr = create_pr_against_po(po.name, received_qty=8)
 		self.assertEqual(get_ordered_qty(), existing_ordered_qty)
@@ -71,8 +71,8 @@
 
 		self.assertEqual(get_ordered_qty(), existing_ordered_qty + 10)
 
-		frappe.db.set_value('Item', '_Test Item', 'over_delivery_receipt_allowance', 50)
-		frappe.db.set_value('Item', '_Test Item', 'over_billing_allowance', 20)
+		frappe.db.set_value("Item", "_Test Item", "over_delivery_receipt_allowance", 50)
+		frappe.db.set_value("Item", "_Test Item", "over_billing_allowance", 20)
 
 		pi = make_pi_from_po(po.name)
 		pi.update_stock = 1
@@ -91,8 +91,8 @@
 		po.load_from_db()
 		self.assertEqual(po.get("items")[0].received_qty, 0)
 
-		frappe.db.set_value('Item', '_Test Item', 'over_delivery_receipt_allowance', 0)
-		frappe.db.set_value('Item', '_Test Item', 'over_billing_allowance', 0)
+		frappe.db.set_value("Item", "_Test Item", "over_delivery_receipt_allowance", 0)
+		frappe.db.set_value("Item", "_Test Item", "over_billing_allowance", 0)
 		frappe.db.set_value("Accounts Settings", None, "over_billing_allowance", 0)
 
 	def test_update_remove_child_linked_to_mr(self):
@@ -104,41 +104,41 @@
 		po.submit()
 
 		first_item_of_po = po.get("items")[0]
-		existing_ordered_qty = get_ordered_qty() # 10
-		existing_requested_qty = get_requested_qty() # 0
+		existing_ordered_qty = get_ordered_qty()  # 10
+		existing_requested_qty = get_requested_qty()  # 0
 
 		# decrease ordered qty by 3 (10 -> 7) and add item
-		trans_item = json.dumps([
-			{
-				'item_code': first_item_of_po.item_code,
-				'rate': first_item_of_po.rate,
-				'qty': 7,
-				'docname': first_item_of_po.name
-			},
-			{'item_code' : '_Test Item 2', 'rate' : 200, 'qty' : 2}
-		])
-		update_child_qty_rate('Purchase Order', trans_item, po.name)
+		trans_item = json.dumps(
+			[
+				{
+					"item_code": first_item_of_po.item_code,
+					"rate": first_item_of_po.rate,
+					"qty": 7,
+					"docname": first_item_of_po.name,
+				},
+				{"item_code": "_Test Item 2", "rate": 200, "qty": 2},
+			]
+		)
+		update_child_qty_rate("Purchase Order", trans_item, po.name)
 		mr.reload()
 
 		# requested qty increases as ordered qty decreases
-		self.assertEqual(get_requested_qty(), existing_requested_qty + 3) # 3
+		self.assertEqual(get_requested_qty(), existing_requested_qty + 3)  # 3
 		self.assertEqual(mr.items[0].ordered_qty, 7)
 
-		self.assertEqual(get_ordered_qty(), existing_ordered_qty - 3) # 7
+		self.assertEqual(get_ordered_qty(), existing_ordered_qty - 3)  # 7
 
 		# delete first item linked to Material Request
-		trans_item = json.dumps([
-			{'item_code' : '_Test Item 2', 'rate' : 200, 'qty' : 2}
-		])
-		update_child_qty_rate('Purchase Order', trans_item, po.name)
+		trans_item = json.dumps([{"item_code": "_Test Item 2", "rate": 200, "qty": 2}])
+		update_child_qty_rate("Purchase Order", trans_item, po.name)
 		mr.reload()
 
 		# requested qty increases as ordered qty is 0 (deleted row)
-		self.assertEqual(get_requested_qty(), existing_requested_qty + 10) # 10
+		self.assertEqual(get_requested_qty(), existing_requested_qty + 10)  # 10
 		self.assertEqual(mr.items[0].ordered_qty, 0)
 
 		# ordered qty decreases as ordered qty is 0 (deleted row)
-		self.assertEqual(get_ordered_qty(), existing_ordered_qty - 10) # 0
+		self.assertEqual(get_ordered_qty(), existing_ordered_qty - 10)  # 0
 
 	def test_update_child(self):
 		mr = make_material_request(qty=10)
@@ -155,8 +155,10 @@
 		existing_ordered_qty = get_ordered_qty()
 		existing_requested_qty = get_requested_qty()
 
-		trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.items[0].name}])
-		update_child_qty_rate('Purchase Order', trans_item, po.name)
+		trans_item = json.dumps(
+			[{"item_code": "_Test Item", "rate": 200, "qty": 7, "docname": po.items[0].name}]
+		)
+		update_child_qty_rate("Purchase Order", trans_item, po.name)
 
 		mr.reload()
 		self.assertEqual(mr.items[0].ordered_qty, 7)
@@ -180,20 +182,22 @@
 		existing_ordered_qty = get_ordered_qty()
 		first_item_of_po = po.get("items")[0]
 
-		trans_item = json.dumps([
-			{
-				'item_code': first_item_of_po.item_code,
-				'rate': first_item_of_po.rate,
-				'qty': first_item_of_po.qty,
-				'docname': first_item_of_po.name
-			},
-			{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7}
-		])
-		update_child_qty_rate('Purchase Order', trans_item, po.name)
+		trans_item = json.dumps(
+			[
+				{
+					"item_code": first_item_of_po.item_code,
+					"rate": first_item_of_po.rate,
+					"qty": first_item_of_po.qty,
+					"docname": first_item_of_po.name,
+				},
+				{"item_code": "_Test Item", "rate": 200, "qty": 7},
+			]
+		)
+		update_child_qty_rate("Purchase Order", trans_item, po.name)
 
 		po.reload()
-		self.assertEqual(len(po.get('items')), 2)
-		self.assertEqual(po.status, 'To Receive and Bill')
+		self.assertEqual(len(po.get("items")), 2)
+		self.assertEqual(po.status, "To Receive and Bill")
 		# ordered qty should increase on row addition
 		self.assertEqual(get_ordered_qty(), existing_ordered_qty + 7)
 
@@ -208,15 +212,18 @@
 		first_item_of_po = po.get("items")[0]
 		existing_ordered_qty = get_ordered_qty()
 		# add an item
-		trans_item = json.dumps([
-			{
-				'item_code': first_item_of_po.item_code,
-				'rate': first_item_of_po.rate,
-				'qty': first_item_of_po.qty,
-				'docname': first_item_of_po.name
-			},
-			{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7}])
-		update_child_qty_rate('Purchase Order', trans_item, po.name)
+		trans_item = json.dumps(
+			[
+				{
+					"item_code": first_item_of_po.item_code,
+					"rate": first_item_of_po.rate,
+					"qty": first_item_of_po.qty,
+					"docname": first_item_of_po.name,
+				},
+				{"item_code": "_Test Item", "rate": 200, "qty": 7},
+			]
+		)
+		update_child_qty_rate("Purchase Order", trans_item, po.name)
 
 		po.reload()
 
@@ -224,115 +231,145 @@
 		self.assertEqual(get_ordered_qty(), existing_ordered_qty + 7)
 
 		# check if can remove received item
-		trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.get("items")[1].name}])
-		self.assertRaises(frappe.ValidationError, update_child_qty_rate, 'Purchase Order', trans_item, po.name)
+		trans_item = json.dumps(
+			[{"item_code": "_Test Item", "rate": 200, "qty": 7, "docname": po.get("items")[1].name}]
+		)
+		self.assertRaises(
+			frappe.ValidationError, update_child_qty_rate, "Purchase Order", trans_item, po.name
+		)
 
 		first_item_of_po = po.get("items")[0]
-		trans_item = json.dumps([
-			{
-				'item_code': first_item_of_po.item_code,
-				'rate': first_item_of_po.rate,
-				'qty': first_item_of_po.qty,
-				'docname': first_item_of_po.name
-			}
-		])
-		update_child_qty_rate('Purchase Order', trans_item, po.name)
+		trans_item = json.dumps(
+			[
+				{
+					"item_code": first_item_of_po.item_code,
+					"rate": first_item_of_po.rate,
+					"qty": first_item_of_po.qty,
+					"docname": first_item_of_po.name,
+				}
+			]
+		)
+		update_child_qty_rate("Purchase Order", trans_item, po.name)
 
 		po.reload()
-		self.assertEqual(len(po.get('items')), 1)
-		self.assertEqual(po.status, 'To Receive and Bill')
+		self.assertEqual(len(po.get("items")), 1)
+		self.assertEqual(po.status, "To Receive and Bill")
 
 		# ordered qty should decrease (back to initial) on row deletion
 		self.assertEqual(get_ordered_qty(), existing_ordered_qty)
 
 	def test_update_child_perm(self):
-		po = create_purchase_order(item_code= "_Test Item", qty=4)
+		po = create_purchase_order(item_code="_Test Item", qty=4)
 
-		user = 'test@example.com'
-		test_user = frappe.get_doc('User', user)
+		user = "test@example.com"
+		test_user = frappe.get_doc("User", user)
 		test_user.add_roles("Accounts User")
 		frappe.set_user(user)
 
 		# update qty
-		trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.items[0].name}])
-		self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name)
+		trans_item = json.dumps(
+			[{"item_code": "_Test Item", "rate": 200, "qty": 7, "docname": po.items[0].name}]
+		)
+		self.assertRaises(
+			frappe.ValidationError, update_child_qty_rate, "Purchase Order", trans_item, po.name
+		)
 
 		# add new item
-		trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}])
-		self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name)
+		trans_item = json.dumps([{"item_code": "_Test Item", "rate": 100, "qty": 2}])
+		self.assertRaises(
+			frappe.ValidationError, update_child_qty_rate, "Purchase Order", trans_item, po.name
+		)
 		frappe.set_user("Administrator")
 
 	def test_update_child_with_tax_template(self):
 		"""
-			Test Action: Create a PO with one item having its tax account head already in the PO.
-			Add the same item + new item with tax template via Update Items.
-			Expected result: First Item's tax row is updated. New tax row is added for second Item.
+		Test Action: Create a PO with one item having its tax account head already in the PO.
+		Add the same item + new item with tax template via Update Items.
+		Expected result: First Item's tax row is updated. New tax row is added for second Item.
 		"""
 		if not frappe.db.exists("Item", "Test Item with Tax"):
-			make_item("Test Item with Tax", {
-				'is_stock_item': 1,
-			})
+			make_item(
+				"Test Item with Tax",
+				{
+					"is_stock_item": 1,
+				},
+			)
 
-		if not frappe.db.exists("Item Tax Template", {"title": 'Test Update Items Template'}):
-			frappe.get_doc({
-				'doctype': 'Item Tax Template',
-				'title': 'Test Update Items Template',
-				'company': '_Test Company',
-				'taxes': [
-					{
-						'tax_type': "_Test Account Service Tax - _TC",
-						'tax_rate': 10,
-					}
-				]
-			}).insert()
+		if not frappe.db.exists("Item Tax Template", {"title": "Test Update Items Template"}):
+			frappe.get_doc(
+				{
+					"doctype": "Item Tax Template",
+					"title": "Test Update Items Template",
+					"company": "_Test Company",
+					"taxes": [
+						{
+							"tax_type": "_Test Account Service Tax - _TC",
+							"tax_rate": 10,
+						}
+					],
+				}
+			).insert()
 
 		new_item_with_tax = frappe.get_doc("Item", "Test Item with Tax")
 
-		if not frappe.db.exists("Item Tax",
-			{"item_tax_template": "Test Update Items Template - _TC", "parent": "Test Item with Tax"}):
-			new_item_with_tax.append("taxes", {
-				"item_tax_template": "Test Update Items Template - _TC",
-				"valid_from": nowdate()
-			})
+		if not frappe.db.exists(
+			"Item Tax",
+			{"item_tax_template": "Test Update Items Template - _TC", "parent": "Test Item with Tax"},
+		):
+			new_item_with_tax.append(
+				"taxes", {"item_tax_template": "Test Update Items Template - _TC", "valid_from": nowdate()}
+			)
 			new_item_with_tax.save()
 
 		tax_template = "_Test Account Excise Duty @ 10 - _TC"
-		item =  "_Test Item Home Desktop 100"
-		if not frappe.db.exists("Item Tax", {"parent":item, "item_tax_template":tax_template}):
+		item = "_Test Item Home Desktop 100"
+		if not frappe.db.exists("Item Tax", {"parent": item, "item_tax_template": tax_template}):
 			item_doc = frappe.get_doc("Item", item)
-			item_doc.append("taxes", {
-				"item_tax_template": tax_template,
-				"valid_from": nowdate()
-			})
+			item_doc.append("taxes", {"item_tax_template": tax_template, "valid_from": nowdate()})
 			item_doc.save()
 		else:
 			# update valid from
-			frappe.db.sql("""UPDATE `tabItem Tax` set valid_from = CURDATE()
+			frappe.db.sql(
+				"""UPDATE `tabItem Tax` set valid_from = CURRENT_DATE
 				where parent = %(item)s and item_tax_template = %(tax)s""",
-					{"item": item, "tax": tax_template})
+				{"item": item, "tax": tax_template},
+			)
 
 		po = create_purchase_order(item_code=item, qty=1, do_not_save=1)
 
-		po.append("taxes", {
-			"account_head": "_Test Account Excise Duty - _TC",
-			"charge_type": "On Net Total",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "Excise Duty",
-			"doctype": "Purchase Taxes and Charges",
-			"rate": 10
-		})
+		po.append(
+			"taxes",
+			{
+				"account_head": "_Test Account Excise Duty - _TC",
+				"charge_type": "On Net Total",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "Excise Duty",
+				"doctype": "Purchase Taxes and Charges",
+				"rate": 10,
+			},
+		)
 		po.insert()
 		po.submit()
 
 		self.assertEqual(po.taxes[0].tax_amount, 50)
 		self.assertEqual(po.taxes[0].total, 550)
 
-		items = json.dumps([
-			{'item_code' : item, 'rate' : 500, 'qty' : 1, 'docname': po.items[0].name},
-			{'item_code' : item, 'rate' : 100, 'qty' : 1}, # added item whose tax account head already exists in PO
-			{'item_code' : new_item_with_tax.name, 'rate' : 100, 'qty' : 1} # added item whose tax account head  is missing in PO
-		])
-		update_child_qty_rate('Purchase Order', items, po.name)
+		items = json.dumps(
+			[
+				{"item_code": item, "rate": 500, "qty": 1, "docname": po.items[0].name},
+				{
+					"item_code": item,
+					"rate": 100,
+					"qty": 1,
+				},  # added item whose tax account head already exists in PO
+				{
+					"item_code": new_item_with_tax.name,
+					"rate": 100,
+					"qty": 1,
+				},  # added item whose tax account head  is missing in PO
+			]
+		)
+		update_child_qty_rate("Purchase Order", items, po.name)
 
 		po.reload()
 		self.assertEqual(po.taxes[0].tax_amount, 70)
@@ -342,29 +379,38 @@
 		self.assertEqual(po.taxes[1].total, 840)
 
 		# teardown
-		frappe.db.sql("""UPDATE `tabItem Tax` set valid_from = NULL
-			where parent = %(item)s and item_tax_template = %(tax)s""", {"item": item, "tax": tax_template})
+		frappe.db.sql(
+			"""UPDATE `tabItem Tax` set valid_from = NULL
+			where parent = %(item)s and item_tax_template = %(tax)s""",
+			{"item": item, "tax": tax_template},
+		)
 		po.cancel()
 		po.delete()
 		new_item_with_tax.delete()
 		frappe.get_doc("Item Tax Template", "Test Update Items Template - _TC").delete()
 
 	def test_update_child_uom_conv_factor_change(self):
-		po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes")
+		po = create_purchase_order(item_code="_Test FG Item", is_subcontracted=1)
 		total_reqd_qty = sum([d.get("required_qty") for d in po.as_dict().get("supplied_items")])
 
-		trans_item = json.dumps([{
-			'item_code': po.get("items")[0].item_code,
-			'rate': po.get("items")[0].rate,
-			'qty': po.get("items")[0].qty,
-			'uom': "_Test UOM 1",
-			'conversion_factor': 2,
-			'docname': po.get("items")[0].name
-		}])
-		update_child_qty_rate('Purchase Order', trans_item, po.name)
+		trans_item = json.dumps(
+			[
+				{
+					"item_code": po.get("items")[0].item_code,
+					"rate": po.get("items")[0].rate,
+					"qty": po.get("items")[0].qty,
+					"uom": "_Test UOM 1",
+					"conversion_factor": 2,
+					"docname": po.get("items")[0].name,
+				}
+			]
+		)
+		update_child_qty_rate("Purchase Order", trans_item, po.name)
 		po.reload()
 
-		total_reqd_qty_after_change = sum(d.get("required_qty") for d in po.as_dict().get("supplied_items"))
+		total_reqd_qty_after_change = sum(
+			d.get("required_qty") for d in po.as_dict().get("supplied_items")
+		)
 
 		self.assertEqual(total_reqd_qty_after_change, 2 * total_reqd_qty)
 
@@ -401,8 +447,6 @@
 		po.load_from_db()
 		self.assertEqual(po.get("items")[0].received_qty, 6)
 
-
-
 	def test_return_against_purchase_order(self):
 		po = create_purchase_order()
 
@@ -428,17 +472,20 @@
 			make_purchase_receipt as make_purchase_receipt_return,
 		)
 
-		pr1 = make_purchase_receipt_return(is_return=1, return_against=pr.name, qty=-3, do_not_submit=True)
+		pr1 = make_purchase_receipt_return(
+			is_return=1, return_against=pr.name, qty=-3, do_not_submit=True
+		)
 		pr1.items[0].purchase_order = po.name
 		pr1.items[0].purchase_order_item = po.items[0].name
 		pr1.submit()
 
-		pi1= make_purchase_invoice_return(is_return=1, return_against=pi2.name, qty=-1, update_stock=1, do_not_submit=True)
+		pi1 = make_purchase_invoice_return(
+			is_return=1, return_against=pi2.name, qty=-1, update_stock=1, do_not_submit=True
+		)
 		pi1.items[0].purchase_order = po.name
 		pi1.items[0].po_detail = po.items[0].name
 		pi1.submit()
 
-
 		po.load_from_db()
 		self.assertEqual(po.get("items")[0].received_qty, 5)
 
@@ -484,13 +531,12 @@
 
 	def test_purchase_order_on_hold(self):
 		po = create_purchase_order(item_code="_Test Product Bundle Item")
-		po.db_set('Status', "On Hold")
+		po.db_set("Status", "On Hold")
 		pi = make_pi_from_po(po.name)
 		pr = make_purchase_receipt(po.name)
 		self.assertRaises(frappe.ValidationError, pr.submit)
 		self.assertRaises(frappe.ValidationError, pi.submit)
 
-
 	def test_make_purchase_invoice_with_terms(self):
 		from erpnext.selling.doctype.sales_order.test_sales_order import (
 			automatically_fetch_payment_terms,
@@ -501,9 +547,7 @@
 
 		self.assertRaises(frappe.ValidationError, make_pi_from_po, po.name)
 
-		po.update(
-			{"payment_terms_template": "_Test Payment Term Template"}
-		)
+		po.update({"payment_terms_template": "_Test Payment Term Template"})
 
 		po.save()
 		po.submit()
@@ -511,7 +555,9 @@
 		self.assertEqual(po.payment_schedule[0].payment_amount, 2500.0)
 		self.assertEqual(getdate(po.payment_schedule[0].due_date), getdate(po.transaction_date))
 		self.assertEqual(po.payment_schedule[1].payment_amount, 2500.0)
-		self.assertEqual(getdate(po.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30))
+		self.assertEqual(
+			getdate(po.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30)
+		)
 		pi = make_pi_from_po(po.name)
 		pi.save()
 
@@ -521,75 +567,89 @@
 		self.assertEqual(pi.payment_schedule[0].payment_amount, 2500.0)
 		self.assertEqual(getdate(pi.payment_schedule[0].due_date), getdate(po.transaction_date))
 		self.assertEqual(pi.payment_schedule[1].payment_amount, 2500.0)
-		self.assertEqual(getdate(pi.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30))
+		self.assertEqual(
+			getdate(pi.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30)
+		)
 		automatically_fetch_payment_terms(enable=0)
 
 	def test_subcontracting(self):
-		po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes")
+		po = create_purchase_order(item_code="_Test FG Item", is_subcontracted=1)
 		self.assertEqual(len(po.get("supplied_items")), 2)
 
 	def test_warehouse_company_validation(self):
 		from erpnext.stock.utils import InvalidWarehouseCompany
+
 		po = create_purchase_order(company="_Test Company 1", do_not_save=True)
 		self.assertRaises(InvalidWarehouseCompany, po.insert)
 
 	def test_uom_integer_validation(self):
 		from erpnext.utilities.transaction_base import UOMMustBeIntegerError
+
 		po = create_purchase_order(qty=3.4, do_not_save=True)
 		self.assertRaises(UOMMustBeIntegerError, po.insert)
 
 	def test_ordered_qty_for_closing_po(self):
-		bin = frappe.get_all("Bin", filters={"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"},
-			fields=["ordered_qty"])
+		bin = frappe.get_all(
+			"Bin",
+			filters={"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"},
+			fields=["ordered_qty"],
+		)
 
 		existing_ordered_qty = bin[0].ordered_qty if bin else 0.0
 
-		po = create_purchase_order(item_code= "_Test Item", qty=1)
+		po = create_purchase_order(item_code="_Test Item", qty=1)
 
-		self.assertEqual(get_ordered_qty(item_code= "_Test Item", warehouse="_Test Warehouse - _TC"), existing_ordered_qty+1)
+		self.assertEqual(
+			get_ordered_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"),
+			existing_ordered_qty + 1,
+		)
 
 		po.update_status("Closed")
 
-		self.assertEqual(get_ordered_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"), existing_ordered_qty)
+		self.assertEqual(
+			get_ordered_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"), existing_ordered_qty
+		)
 
 	def test_group_same_items(self):
 		frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
-		frappe.get_doc({
-			"doctype": "Purchase Order",
-			"company": "_Test Company",
-			"supplier" : "_Test Supplier",
-			"is_subcontracted" : "No",
-			"schedule_date": add_days(nowdate(), 1),
-			"currency" : frappe.get_cached_value('Company',  "_Test Company",  "default_currency"),
-			"conversion_factor" : 1,
-			"items" : get_same_items(),
-			"group_same_items": 1
-			}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "Purchase Order",
+				"company": "_Test Company",
+				"supplier": "_Test Supplier",
+				"is_subcontracted": 0,
+				"schedule_date": add_days(nowdate(), 1),
+				"currency": frappe.get_cached_value("Company", "_Test Company", "default_currency"),
+				"conversion_factor": 1,
+				"items": get_same_items(),
+				"group_same_items": 1,
+			}
+		).insert(ignore_permissions=True)
 
 	def test_make_po_without_terms(self):
 		po = create_purchase_order(do_not_save=1)
 
-		self.assertFalse(po.get('payment_schedule'))
+		self.assertFalse(po.get("payment_schedule"))
 
 		po.insert()
 
-		self.assertTrue(po.get('payment_schedule'))
+		self.assertTrue(po.get("payment_schedule"))
 
 	def test_po_for_blocked_supplier_all(self):
-		supplier = frappe.get_doc('Supplier', '_Test Supplier')
+		supplier = frappe.get_doc("Supplier", "_Test Supplier")
 		supplier.on_hold = 1
 		supplier.save()
 
-		self.assertEqual(supplier.hold_type, 'All')
+		self.assertEqual(supplier.hold_type, "All")
 		self.assertRaises(frappe.ValidationError, create_purchase_order)
 
 		supplier.on_hold = 0
 		supplier.save()
 
 	def test_po_for_blocked_supplier_invoices(self):
-		supplier = frappe.get_doc('Supplier', '_Test Supplier')
+		supplier = frappe.get_doc("Supplier", "_Test Supplier")
 		supplier.on_hold = 1
-		supplier.hold_type = 'Invoices'
+		supplier.hold_type = "Invoices"
 		supplier.save()
 
 		self.assertRaises(frappe.ValidationError, create_purchase_order)
@@ -598,30 +658,40 @@
 		supplier.save()
 
 	def test_po_for_blocked_supplier_payments(self):
-		supplier = frappe.get_doc('Supplier', '_Test Supplier')
+		supplier = frappe.get_doc("Supplier", "_Test Supplier")
 		supplier.on_hold = 1
-		supplier.hold_type = 'Payments'
+		supplier.hold_type = "Payments"
 		supplier.save()
 
 		po = create_purchase_order()
 
 		self.assertRaises(
-			frappe.ValidationError, get_payment_entry, dt='Purchase Order', dn=po.name, bank_account="_Test Bank - _TC")
+			frappe.ValidationError,
+			get_payment_entry,
+			dt="Purchase Order",
+			dn=po.name,
+			bank_account="_Test Bank - _TC",
+		)
 
 		supplier.on_hold = 0
 		supplier.save()
 
 	def test_po_for_blocked_supplier_payments_with_today_date(self):
-		supplier = frappe.get_doc('Supplier', '_Test Supplier')
+		supplier = frappe.get_doc("Supplier", "_Test Supplier")
 		supplier.on_hold = 1
 		supplier.release_date = nowdate()
-		supplier.hold_type = 'Payments'
+		supplier.hold_type = "Payments"
 		supplier.save()
 
 		po = create_purchase_order()
 
 		self.assertRaises(
-			frappe.ValidationError, get_payment_entry, dt='Purchase Order', dn=po.name, bank_account="_Test Bank - _TC")
+			frappe.ValidationError,
+			get_payment_entry,
+			dt="Purchase Order",
+			dn=po.name,
+			bank_account="_Test Bank - _TC",
+		)
 
 		supplier.on_hold = 0
 		supplier.save()
@@ -630,14 +700,14 @@
 		# this test is meant to fail only if something fails in the try block
 		with self.assertRaises(Exception):
 			try:
-				supplier = frappe.get_doc('Supplier', '_Test Supplier')
+				supplier = frappe.get_doc("Supplier", "_Test Supplier")
 				supplier.on_hold = 1
-				supplier.hold_type = 'Payments'
-				supplier.release_date = '2018-03-01'
+				supplier.hold_type = "Payments"
+				supplier.release_date = "2018-03-01"
 				supplier.save()
 
 				po = create_purchase_order()
-				get_payment_entry('Purchase Order', po.name, bank_account='_Test Bank - _TC')
+				get_payment_entry("Purchase Order", po.name, bank_account="_Test Bank - _TC")
 
 				supplier.on_hold = 0
 				supplier.save()
@@ -648,88 +718,124 @@
 
 	def test_terms_are_not_copied_if_automatically_fetch_payment_terms_is_unchecked(self):
 		po = create_purchase_order(do_not_save=1)
-		po.payment_terms_template = '_Test Payment Term Template'
+		po.payment_terms_template = "_Test Payment Term Template"
 		po.save()
 		po.submit()
 
-		frappe.db.set_value('Company', '_Test Company', 'payment_terms', '_Test Payment Term Template 1')
+		frappe.db.set_value("Company", "_Test Company", "payment_terms", "_Test Payment Term Template 1")
 		pi = make_pi_from_po(po.name)
 		pi.save()
 
-		self.assertEqual(pi.get('payment_terms_template'), '_Test Payment Term Template 1')
-		frappe.db.set_value('Company', '_Test Company', 'payment_terms', '')
+		self.assertEqual(pi.get("payment_terms_template"), "_Test Payment Term Template 1")
+		frappe.db.set_value("Company", "_Test Company", "payment_terms", "")
 
 	def test_terms_copied(self):
 		po = create_purchase_order(do_not_save=1)
-		po.payment_terms_template = '_Test Payment Term Template'
+		po.payment_terms_template = "_Test Payment Term Template"
 		po.insert()
 		po.submit()
-		self.assertTrue(po.get('payment_schedule'))
+		self.assertTrue(po.get("payment_schedule"))
 
 		pi = make_pi_from_po(po.name)
 		pi.insert()
-		self.assertTrue(pi.get('payment_schedule'))
+		self.assertTrue(pi.get("payment_schedule"))
 
 	def test_reserved_qty_subcontract_po(self):
 		# Make stock available for raw materials
 		make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100)
-		make_stock_entry(target="_Test Warehouse - _TC", item_code="_Test Item Home Desktop 100",
-			qty=20, basic_rate=100)
-		make_stock_entry(target="_Test Warehouse 1 - _TC", item_code="_Test Item",
-			qty=30, basic_rate=100)
-		make_stock_entry(target="_Test Warehouse 1 - _TC", item_code="_Test Item Home Desktop 100",
-			qty=30, basic_rate=100)
+		make_stock_entry(
+			target="_Test Warehouse - _TC", item_code="_Test Item Home Desktop 100", qty=20, basic_rate=100
+		)
+		make_stock_entry(
+			target="_Test Warehouse 1 - _TC", item_code="_Test Item", qty=30, basic_rate=100
+		)
+		make_stock_entry(
+			target="_Test Warehouse 1 - _TC",
+			item_code="_Test Item Home Desktop 100",
+			qty=30,
+			basic_rate=100,
+		)
 
-		bin1 = frappe.db.get_value("Bin",
+		bin1 = frappe.db.get_value(
+			"Bin",
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname=["reserved_qty_for_sub_contract", "projected_qty", "modified"], as_dict=1)
+			fieldname=["reserved_qty_for_sub_contract", "projected_qty", "modified"],
+			as_dict=1,
+		)
 
 		# Submit PO
-		po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes")
+		po = create_purchase_order(item_code="_Test FG Item", is_subcontracted=1)
 
-		bin2 = frappe.db.get_value("Bin",
+		bin2 = frappe.db.get_value(
+			"Bin",
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname=["reserved_qty_for_sub_contract", "projected_qty", "modified"], as_dict=1)
+			fieldname=["reserved_qty_for_sub_contract", "projected_qty", "modified"],
+			as_dict=1,
+		)
 
 		self.assertEqual(bin2.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10)
 		self.assertEqual(bin2.projected_qty, bin1.projected_qty - 10)
 		self.assertNotEqual(bin1.modified, bin2.modified)
 
 		# Create stock transfer
-		rm_item = [{"item_code":"_Test FG Item","rm_item_code":"_Test Item","item_name":"_Test Item",
-					"qty":6,"warehouse":"_Test Warehouse - _TC","rate":100,"amount":600,"stock_uom":"Nos"}]
+		rm_item = [
+			{
+				"item_code": "_Test FG Item",
+				"rm_item_code": "_Test Item",
+				"item_name": "_Test Item",
+				"qty": 6,
+				"warehouse": "_Test Warehouse - _TC",
+				"rate": 100,
+				"amount": 600,
+				"stock_uom": "Nos",
+			}
+		]
 		rm_item_string = json.dumps(rm_item)
 		se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string))
 		se.to_warehouse = "_Test Warehouse 1 - _TC"
 		se.save()
 		se.submit()
 
-		bin3 = frappe.db.get_value("Bin",
+		bin3 = frappe.db.get_value(
+			"Bin",
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname="reserved_qty_for_sub_contract", as_dict=1)
+			fieldname="reserved_qty_for_sub_contract",
+			as_dict=1,
+		)
 
 		self.assertEqual(bin3.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
 
 		# close PO
 		po.update_status("Closed")
-		bin4 = frappe.db.get_value("Bin",
+		bin4 = frappe.db.get_value(
+			"Bin",
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname="reserved_qty_for_sub_contract", as_dict=1)
+			fieldname="reserved_qty_for_sub_contract",
+			as_dict=1,
+		)
 
 		self.assertEqual(bin4.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
 
 		# Re-open PO
 		po.update_status("Submitted")
-		bin5 = frappe.db.get_value("Bin",
+		bin5 = frappe.db.get_value(
+			"Bin",
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname="reserved_qty_for_sub_contract", as_dict=1)
+			fieldname="reserved_qty_for_sub_contract",
+			as_dict=1,
+		)
 
 		self.assertEqual(bin5.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
 
-		make_stock_entry(target="_Test Warehouse 1 - _TC", item_code="_Test Item",
-			qty=40, basic_rate=100)
-		make_stock_entry(target="_Test Warehouse 1 - _TC", item_code="_Test Item Home Desktop 100",
-			qty=40, basic_rate=100)
+		make_stock_entry(
+			target="_Test Warehouse 1 - _TC", item_code="_Test Item", qty=40, basic_rate=100
+		)
+		make_stock_entry(
+			target="_Test Warehouse 1 - _TC",
+			item_code="_Test Item Home Desktop 100",
+			qty=40,
+			basic_rate=100,
+		)
 
 		# make Purchase Receipt against PO
 		pr = make_purchase_receipt(po.name)
@@ -737,17 +843,23 @@
 		pr.save()
 		pr.submit()
 
-		bin6 = frappe.db.get_value("Bin",
+		bin6 = frappe.db.get_value(
+			"Bin",
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname="reserved_qty_for_sub_contract", as_dict=1)
+			fieldname="reserved_qty_for_sub_contract",
+			as_dict=1,
+		)
 
 		self.assertEqual(bin6.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
 
 		# Cancel PR
 		pr.cancel()
-		bin7 = frappe.db.get_value("Bin",
+		bin7 = frappe.db.get_value(
+			"Bin",
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname="reserved_qty_for_sub_contract", as_dict=1)
+			fieldname="reserved_qty_for_sub_contract",
+			as_dict=1,
+		)
 
 		self.assertEqual(bin7.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
 
@@ -757,34 +869,46 @@
 		pi.supplier_warehouse = "_Test Warehouse 1 - _TC"
 		pi.insert()
 		pi.submit()
-		bin8 = frappe.db.get_value("Bin",
+		bin8 = frappe.db.get_value(
+			"Bin",
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname="reserved_qty_for_sub_contract", as_dict=1)
+			fieldname="reserved_qty_for_sub_contract",
+			as_dict=1,
+		)
 
 		self.assertEqual(bin8.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
 
 		# Cancel PR
 		pi.cancel()
-		bin9 = frappe.db.get_value("Bin",
+		bin9 = frappe.db.get_value(
+			"Bin",
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname="reserved_qty_for_sub_contract", as_dict=1)
+			fieldname="reserved_qty_for_sub_contract",
+			as_dict=1,
+		)
 
 		self.assertEqual(bin9.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
 
 		# Cancel Stock Entry
 		se.cancel()
-		bin10 = frappe.db.get_value("Bin",
+		bin10 = frappe.db.get_value(
+			"Bin",
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname="reserved_qty_for_sub_contract", as_dict=1)
+			fieldname="reserved_qty_for_sub_contract",
+			as_dict=1,
+		)
 
 		self.assertEqual(bin10.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10)
 
 		# Cancel PO
 		po.reload()
 		po.cancel()
-		bin11 = frappe.db.get_value("Bin",
+		bin11 = frappe.db.get_value(
+			"Bin",
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname="reserved_qty_for_sub_contract", as_dict=1)
+			fieldname="reserved_qty_for_sub_contract",
+			as_dict=1,
+		)
 
 		self.assertEqual(bin11.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
 
@@ -792,56 +916,101 @@
 		item_code = "_Test Subcontracted FG Item 11"
 		make_subcontracted_item(item_code=item_code)
 
-		po = create_purchase_order(item_code=item_code, qty=1,
-			is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC", include_exploded_items=1)
+		po = create_purchase_order(
+			item_code=item_code,
+			qty=1,
+			is_subcontracted=1,
+			supplier_warehouse="_Test Warehouse 1 - _TC",
+			include_exploded_items=1,
+		)
 
-		name = frappe.db.get_value('BOM', {'item': item_code}, 'name')
-		bom = frappe.get_doc('BOM', name)
+		name = frappe.db.get_value("BOM", {"item": item_code}, "name")
+		bom = frappe.get_doc("BOM", name)
 
-		exploded_items = sorted([d.item_code for d in bom.exploded_items if not d.get('sourced_by_supplier')])
+		exploded_items = sorted(
+			[d.item_code for d in bom.exploded_items if not d.get("sourced_by_supplier")]
+		)
 		supplied_items = sorted([d.rm_item_code for d in po.supplied_items])
 		self.assertEqual(exploded_items, supplied_items)
 
-		po1 = create_purchase_order(item_code=item_code, qty=1,
-			is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC", include_exploded_items=0)
+		po1 = create_purchase_order(
+			item_code=item_code,
+			qty=1,
+			is_subcontracted=1,
+			supplier_warehouse="_Test Warehouse 1 - _TC",
+			include_exploded_items=0,
+		)
 
 		supplied_items1 = sorted([d.rm_item_code for d in po1.supplied_items])
-		bom_items = sorted([d.item_code for d in bom.items if not d.get('sourced_by_supplier')])
+		bom_items = sorted([d.item_code for d in bom.items if not d.get("sourced_by_supplier")])
 
 		self.assertEqual(supplied_items1, bom_items)
 
 	def test_backflush_based_on_stock_entry(self):
 		item_code = "_Test Subcontracted FG Item 1"
 		make_subcontracted_item(item_code=item_code)
-		make_item('Sub Contracted Raw Material 1', {
-			'is_stock_item': 1,
-			'is_sub_contracted_item': 1
-		})
+		make_item("Sub Contracted Raw Material 1", {"is_stock_item": 1, "is_sub_contracted_item": 1})
 
 		update_backflush_based_on("Material Transferred for Subcontract")
 
 		order_qty = 5
-		po = create_purchase_order(item_code=item_code, qty=order_qty,
-			is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC")
+		po = create_purchase_order(
+			item_code=item_code,
+			qty=order_qty,
+			is_subcontracted=1,
+			supplier_warehouse="_Test Warehouse 1 - _TC",
+		)
 
-		make_stock_entry(target="_Test Warehouse - _TC",
-			item_code="_Test Item Home Desktop 100", qty=20, basic_rate=100)
-		make_stock_entry(target="_Test Warehouse - _TC",
-			item_code = "Test Extra Item 1", qty=100, basic_rate=100)
-		make_stock_entry(target="_Test Warehouse - _TC",
-			item_code = "Test Extra Item 2", qty=10, basic_rate=100)
-		make_stock_entry(target="_Test Warehouse - _TC",
-			item_code = "Sub Contracted Raw Material 1", qty=10, basic_rate=100)
+		make_stock_entry(
+			target="_Test Warehouse - _TC", item_code="_Test Item Home Desktop 100", qty=20, basic_rate=100
+		)
+		make_stock_entry(
+			target="_Test Warehouse - _TC", item_code="Test Extra Item 1", qty=100, basic_rate=100
+		)
+		make_stock_entry(
+			target="_Test Warehouse - _TC", item_code="Test Extra Item 2", qty=10, basic_rate=100
+		)
+		make_stock_entry(
+			target="_Test Warehouse - _TC",
+			item_code="Sub Contracted Raw Material 1",
+			qty=10,
+			basic_rate=100,
+		)
 
 		rm_items = [
-			{"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 1","item_name":"_Test Item",
-				"qty":10,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos"},
-			{"item_code":item_code,"rm_item_code":"_Test Item Home Desktop 100","item_name":"_Test Item Home Desktop 100",
-				"qty":20,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos"},
-			{"item_code":item_code,"rm_item_code":"Test Extra Item 1","item_name":"Test Extra Item 1",
-				"qty":10,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos"},
-			{'item_code': item_code, 'rm_item_code': 'Test Extra Item 2', 'stock_uom':'Nos',
-				'qty': 10, 'warehouse': '_Test Warehouse - _TC', 'item_name':'Test Extra Item 2'}]
+			{
+				"item_code": item_code,
+				"rm_item_code": "Sub Contracted Raw Material 1",
+				"item_name": "_Test Item",
+				"qty": 10,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "Nos",
+			},
+			{
+				"item_code": item_code,
+				"rm_item_code": "_Test Item Home Desktop 100",
+				"item_name": "_Test Item Home Desktop 100",
+				"qty": 20,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "Nos",
+			},
+			{
+				"item_code": item_code,
+				"rm_item_code": "Test Extra Item 1",
+				"item_name": "Test Extra Item 1",
+				"qty": 10,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "Nos",
+			},
+			{
+				"item_code": item_code,
+				"rm_item_code": "Test Extra Item 2",
+				"stock_uom": "Nos",
+				"qty": 10,
+				"warehouse": "_Test Warehouse - _TC",
+				"item_name": "Test Extra Item 2",
+			},
+		]
 
 		rm_item_string = json.dumps(rm_items)
 		se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string))
@@ -851,61 +1020,80 @@
 
 		received_qty = 2
 		# partial receipt
-		pr.get('items')[0].qty = received_qty
+		pr.get("items")[0].qty = received_qty
 		pr.save()
 		pr.submit()
 
-		transferred_items = sorted([d.item_code for d in se.get('items') if se.purchase_order == po.name])
-		issued_items = sorted([d.rm_item_code for d in pr.get('supplied_items')])
+		transferred_items = sorted(
+			[d.item_code for d in se.get("items") if se.purchase_order == po.name]
+		)
+		issued_items = sorted([d.rm_item_code for d in pr.get("supplied_items")])
 
 		self.assertEqual(transferred_items, issued_items)
-		self.assertEqual(pr.get('items')[0].rm_supp_cost, 2000)
-
+		self.assertEqual(pr.get("items")[0].rm_supp_cost, 2000)
 
 		transferred_rm_map = frappe._dict()
 		for item in rm_items:
-			transferred_rm_map[item.get('rm_item_code')] = item
+			transferred_rm_map[item.get("rm_item_code")] = item
 
 		update_backflush_based_on("BOM")
 
 	def test_supplied_qty_against_subcontracted_po(self):
 		item_code = "_Test Subcontracted FG Item 5"
-		make_item('Sub Contracted Raw Material 4', {
-			'is_stock_item': 1,
-			'is_sub_contracted_item': 1
-		})
+		make_item("Sub Contracted Raw Material 4", {"is_stock_item": 1, "is_sub_contracted_item": 1})
 
 		make_subcontracted_item(item_code=item_code, raw_materials=["Sub Contracted Raw Material 4"])
 
 		update_backflush_based_on("Material Transferred for Subcontract")
 
 		order_qty = 250
-		po = create_purchase_order(item_code=item_code, qty=order_qty,
-			is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC", do_not_save=True)
+		po = create_purchase_order(
+			item_code=item_code,
+			qty=order_qty,
+			is_subcontracted=1,
+			supplier_warehouse="_Test Warehouse 1 - _TC",
+			do_not_save=True,
+		)
 
 		# Add same subcontracted items multiple times
-		po.append("items", {
-			"item_code": item_code,
-			"qty": order_qty,
-			"schedule_date": add_days(nowdate(), 1),
-			"warehouse": "_Test Warehouse - _TC"
-		})
+		po.append(
+			"items",
+			{
+				"item_code": item_code,
+				"qty": order_qty,
+				"schedule_date": add_days(nowdate(), 1),
+				"warehouse": "_Test Warehouse - _TC",
+			},
+		)
 
 		po.set_missing_values()
 		po.submit()
 
 		# Material receipt entry for the raw materials which will be send to supplier
-		make_stock_entry(target="_Test Warehouse - _TC",
-			item_code = "Sub Contracted Raw Material 4", qty=500, basic_rate=100)
+		make_stock_entry(
+			target="_Test Warehouse - _TC",
+			item_code="Sub Contracted Raw Material 4",
+			qty=500,
+			basic_rate=100,
+		)
 
 		rm_items = [
 			{
-				"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 4","item_name":"_Test Item",
-				"qty":250,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos", "name": po.supplied_items[0].name
+				"item_code": item_code,
+				"rm_item_code": "Sub Contracted Raw Material 4",
+				"item_name": "_Test Item",
+				"qty": 250,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "Nos",
+				"name": po.supplied_items[0].name,
 			},
 			{
-				"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 4","item_name":"_Test Item",
-				"qty":250,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos"
+				"item_code": item_code,
+				"rm_item_code": "Sub Contracted Raw Material 4",
+				"item_name": "_Test Item",
+				"qty": 250,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "Nos",
 			},
 		]
 
@@ -927,8 +1115,10 @@
 
 	def test_advance_payment_entry_unlink_against_purchase_order(self):
 		from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
-		frappe.db.set_value("Accounts Settings", "Accounts Settings",
-			"unlink_advance_payment_on_cancelation_of_order", 1)
+
+		frappe.db.set_value(
+			"Accounts Settings", "Accounts Settings", "unlink_advance_payment_on_cancelation_of_order", 1
+		)
 
 		po_doc = create_purchase_order()
 
@@ -943,24 +1133,23 @@
 		pe.save(ignore_permissions=True)
 		pe.submit()
 
-		po_doc = frappe.get_doc('Purchase Order', po_doc.name)
+		po_doc = frappe.get_doc("Purchase Order", po_doc.name)
 		po_doc.cancel()
 
-		pe_doc = frappe.get_doc('Payment Entry', pe.name)
+		pe_doc = frappe.get_doc("Payment Entry", pe.name)
 		pe_doc.cancel()
 
-		frappe.db.set_value("Accounts Settings", "Accounts Settings",
-			"unlink_advance_payment_on_cancelation_of_order", 0)
+		frappe.db.set_value(
+			"Accounts Settings", "Accounts Settings", "unlink_advance_payment_on_cancelation_of_order", 0
+		)
 
 	def test_schedule_date(self):
 		po = create_purchase_order(do_not_submit=True)
 		po.schedule_date = None
-		po.append("items", {
-			"item_code": "_Test Item",
-			"qty": 1,
-			"rate": 100,
-			"schedule_date": add_days(nowdate(), 5)
-		})
+		po.append(
+			"items",
+			{"item_code": "_Test Item", "qty": 1, "rate": 100, "schedule_date": add_days(nowdate(), 5)},
+		)
 		po.save()
 		self.assertEqual(po.schedule_date, add_days(nowdate(), 1))
 
@@ -968,22 +1157,21 @@
 		po.save()
 		self.assertEqual(po.schedule_date, add_days(nowdate(), 2))
 
-
 	def test_po_optional_blanket_order(self):
 		"""
-			Expected result: Blanket order Ordered Quantity should only be affected on Purchase Order with against_blanket_order = 1.
-			Second Purchase Order should not add on to Blanket Orders Ordered Quantity.
+		Expected result: Blanket order Ordered Quantity should only be affected on Purchase Order with against_blanket_order = 1.
+		Second Purchase Order should not add on to Blanket Orders Ordered Quantity.
 		"""
 
-		bo = make_blanket_order(blanket_order_type = "Purchasing", quantity = 10, rate = 10)
+		bo = make_blanket_order(blanket_order_type="Purchasing", quantity=10, rate=10)
 
-		po = create_purchase_order(item_code= "_Test Item", qty = 5, against_blanket_order = 1)
-		po_doc = frappe.get_doc('Purchase Order', po.get('name'))
+		po = create_purchase_order(item_code="_Test Item", qty=5, against_blanket_order=1)
+		po_doc = frappe.get_doc("Purchase Order", po.get("name"))
 		# To test if the PO has a Blanket Order
 		self.assertTrue(po_doc.items[0].blanket_order)
 
-		po = create_purchase_order(item_code= "_Test Item", qty = 5, against_blanket_order = 0)
-		po_doc = frappe.get_doc('Purchase Order', po.get('name'))
+		po = create_purchase_order(item_code="_Test Item", qty=5, against_blanket_order=0)
+		po_doc = frappe.get_doc("Purchase Order", po.get("name"))
 		# To test if the PO does NOT have a Blanket Order
 		self.assertEqual(po_doc.items[0].blanket_order, None)
 
@@ -1001,7 +1189,7 @@
 
 		po = create_purchase_order(qty=10, rate=100, do_not_save=1)
 		create_payment_terms_template()
-		po.payment_terms_template = 'Test Receivable Template'
+		po.payment_terms_template = "Test Receivable Template"
 		po.submit()
 
 		pi = make_purchase_invoice(qty=10, rate=100, do_not_save=1)
@@ -1014,6 +1202,7 @@
 
 		automatically_fetch_payment_terms(enable=0)
 
+
 def make_pr_against_po(po, received_qty=0):
 	pr = make_purchase_receipt(po)
 	pr.get("items")[0].qty = received_qty or 5
@@ -1021,39 +1210,51 @@
 	pr.submit()
 	return pr
 
+
 def make_subcontracted_item(**args):
 	from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
 
 	args = frappe._dict(args)
 
-	if not frappe.db.exists('Item', args.item_code):
-		make_item(args.item_code, {
-			'is_stock_item': 1,
-			'is_sub_contracted_item': 1,
-			'has_batch_no': args.get("has_batch_no") or 0
-		})
+	if not frappe.db.exists("Item", args.item_code):
+		make_item(
+			args.item_code,
+			{
+				"is_stock_item": 1,
+				"is_sub_contracted_item": 1,
+				"has_batch_no": args.get("has_batch_no") or 0,
+			},
+		)
 
 	if not args.raw_materials:
-		if not frappe.db.exists('Item', "Test Extra Item 1"):
-			make_item("Test Extra Item 1", {
-				'is_stock_item': 1,
-			})
+		if not frappe.db.exists("Item", "Test Extra Item 1"):
+			make_item(
+				"Test Extra Item 1",
+				{
+					"is_stock_item": 1,
+				},
+			)
 
-		if not frappe.db.exists('Item', "Test Extra Item 2"):
-			make_item("Test Extra Item 2", {
-				'is_stock_item': 1,
-			})
+		if not frappe.db.exists("Item", "Test Extra Item 2"):
+			make_item(
+				"Test Extra Item 2",
+				{
+					"is_stock_item": 1,
+				},
+			)
 
-		args.raw_materials = ['_Test FG Item', 'Test Extra Item 1']
+		args.raw_materials = ["_Test FG Item", "Test Extra Item 1"]
 
-	if not frappe.db.get_value('BOM', {'item': args.item_code}, 'name'):
-		make_bom(item = args.item_code, raw_materials = args.get("raw_materials"))
+	if not frappe.db.get_value("BOM", {"item": args.item_code}, "name"):
+		make_bom(item=args.item_code, raw_materials=args.get("raw_materials"))
+
 
 def update_backflush_based_on(based_on):
-	doc = frappe.get_doc('Buying Settings')
+	doc = frappe.get_doc("Buying Settings")
 	doc.backflush_raw_materials_of_subcontract_based_on = based_on
 	doc.save()
 
+
 def get_same_items():
 	return [
 		{
@@ -1061,17 +1262,18 @@
 			"warehouse": "_Test Warehouse - _TC",
 			"qty": 1,
 			"rate": 500,
-			"schedule_date": add_days(nowdate(), 1)
+			"schedule_date": add_days(nowdate(), 1),
 		},
 		{
 			"item_code": "_Test FG Item",
 			"warehouse": "_Test Warehouse - _TC",
 			"qty": 4,
 			"rate": 500,
-			"schedule_date": add_days(nowdate(), 1)
-		}
+			"schedule_date": add_days(nowdate(), 1),
+		},
 	]
 
+
 def create_purchase_order(**args):
 	po = frappe.new_doc("Purchase Order")
 	args = frappe._dict(args)
@@ -1081,8 +1283,8 @@
 	po.schedule_date = add_days(nowdate(), 1)
 	po.company = args.company or "_Test Company"
 	po.supplier = args.supplier or "_Test Supplier"
-	po.is_subcontracted = args.is_subcontracted or "No"
-	po.currency = args.currency or frappe.get_cached_value('Company',  po.company,  "default_currency")
+	po.is_subcontracted = args.is_subcontracted or 0
+	po.currency = args.currency or frappe.get_cached_value("Company", po.company, "default_currency")
 	po.conversion_factor = args.conversion_factor or 1
 	po.supplier_warehouse = args.supplier_warehouse or None
 
@@ -1090,21 +1292,24 @@
 		for row in args.rm_items:
 			po.append("items", row)
 	else:
-		po.append("items", {
-			"item_code": args.item or args.item_code or "_Test Item",
-			"warehouse": args.warehouse or "_Test Warehouse - _TC",
-			"qty": args.qty or 10,
-			"rate": args.rate or 500,
-			"schedule_date": add_days(nowdate(), 1),
-			"include_exploded_items": args.get('include_exploded_items', 1),
-			"against_blanket_order": args.against_blanket_order
-		})
+		po.append(
+			"items",
+			{
+				"item_code": args.item or args.item_code or "_Test Item",
+				"warehouse": args.warehouse or "_Test Warehouse - _TC",
+				"qty": args.qty or 10,
+				"rate": args.rate or 500,
+				"schedule_date": add_days(nowdate(), 1),
+				"include_exploded_items": args.get("include_exploded_items", 1),
+				"against_blanket_order": args.against_blanket_order,
+			},
+		)
 
 	po.set_missing_values()
 	if not args.do_not_save:
 		po.insert()
 		if not args.do_not_submit:
-			if po.is_subcontracted == "Yes":
+			if po.is_subcontracted:
 				supp_items = po.get("supplied_items")
 				for d in supp_items:
 					if not d.reserve_warehouse:
@@ -1113,6 +1318,7 @@
 
 	return po
 
+
 def create_pr_against_po(po, received_qty=4):
 	pr = make_purchase_receipt(po)
 	pr.get("items")[0].qty = received_qty
@@ -1120,14 +1326,19 @@
 	pr.submit()
 	return pr
 
+
 def get_ordered_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"):
-	return flt(frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse},
-		"ordered_qty"))
+	return flt(
+		frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "ordered_qty")
+	)
+
 
 def get_requested_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"):
-	return flt(frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse},
-		"indented_qty"))
+	return flt(
+		frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "indented_qty")
+	)
+
 
 test_dependencies = ["BOM", "Item Price"]
 
-test_records = frappe.get_test_records('Purchase Order')
+test_records = frappe.get_test_records("Purchase Order")
diff --git a/erpnext/buying/doctype/purchase_order/test_records.json b/erpnext/buying/doctype/purchase_order/test_records.json
index 74b8f1b..896050c 100644
--- a/erpnext/buying/doctype/purchase_order/test_records.json
+++ b/erpnext/buying/doctype/purchase_order/test_records.json
@@ -8,7 +8,7 @@
   "doctype": "Purchase Order", 
   "base_grand_total": 5000.0, 
   "grand_total": 5000.0, 
-  "is_subcontracted": "Yes", 
+  "is_subcontracted": 1, 
   "naming_series": "_T-Purchase Order-", 
   "base_net_total": 5000.0, 
   "items": [
@@ -42,7 +42,7 @@
   "doctype": "Purchase Order", 
   "base_grand_total": 5000.0, 
   "grand_total": 5000.0, 
-  "is_subcontracted": "No", 
+  "is_subcontracted": 0, 
   "naming_series": "_T-Purchase Order-", 
   "base_net_total": 5000.0, 
   "items": [
diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
index a18c527..7994b08 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -213,6 +213,7 @@
    "width": "60px"
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "stock_uom",
    "fieldtype": "Link",
    "label": "Stock UOM",
@@ -242,6 +243,7 @@
    "reqd": 1
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "conversion_factor",
    "fieldtype": "Float",
    "label": "UOM Conversion Factor",
@@ -572,7 +574,7 @@
    "read_only": 1
   },
   {
-   "depends_on": "eval:parent.is_subcontracted == 'Yes'",
+   "depends_on": "eval:parent.is_subcontracted",
    "fieldname": "bom",
    "fieldtype": "Link",
    "label": "BOM",
@@ -581,7 +583,7 @@
   },
   {
    "default": "0",
-   "depends_on": "eval:parent.is_subcontracted == 'Yes'",
+   "depends_on": "eval:parent.is_subcontracted",
    "fieldname": "include_exploded_items",
    "fieldtype": "Check",
    "label": "Include Exploded Items",
@@ -593,6 +595,7 @@
    "label": "Billed, Received & Returned"
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "stock_qty",
    "fieldtype": "Float",
    "label": "Qty in Stock UOM",
@@ -851,7 +854,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2022-02-02 13:10:18.398976",
+ "modified": "2022-06-17 05:29:40.602349",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order Item",
diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py
index 0cef0de..a8bafda 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py
@@ -9,5 +9,6 @@
 class PurchaseOrderItem(Document):
 	pass
 
+
 def on_doctype_update():
 	frappe.db.add_index("Purchase Order Item", ["item_code", "warehouse"])
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
index e7049fd..4e29ee5 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
@@ -31,7 +31,7 @@
 		if (frm.doc.docstatus === 1) {
 
 			frm.add_custom_button(__('Supplier Quotation'),
-				function(){ frm.trigger("make_suppplier_quotation") }, __("Create"));
+				function(){ frm.trigger("make_supplier_quotation") }, __("Create"));
 
 
 			frm.add_custom_button(__("Send Emails to Suppliers"), function() {
@@ -87,16 +87,24 @@
 
 	},
 
-	make_suppplier_quotation: function(frm) {
+	make_supplier_quotation: function(frm) {
 		var doc = frm.doc;
 		var dialog = new frappe.ui.Dialog({
 			title: __("Create Supplier Quotation"),
 			fields: [
-				{	"fieldtype": "Select", "label": __("Supplier"),
+				{	"fieldtype": "Link",
+					"label": __("Supplier"),
 					"fieldname": "supplier",
-					"options": doc.suppliers.map(d => d.supplier),
+					"options": 'Supplier',
 					"reqd": 1,
-					"default": doc.suppliers.length === 1 ? doc.suppliers[0].supplier_name : "" },
+					get_query: () => {
+						return {
+							filters: [
+								["Supplier", "name", "in", frm.doc.suppliers.map((row) => {return row.supplier;})]
+							]
+						}
+					}
+				}
 			],
 			primary_action_label: __("Create"),
 			primary_action: (args) => {
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json
index 4993df9..083cab7 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json
@@ -32,7 +32,9 @@
   "terms",
   "printing_settings",
   "select_print_heading",
-  "letter_head"
+  "letter_head",
+  "more_info",
+  "opportunity"
  ],
  "fields": [
   {
@@ -194,6 +196,23 @@
    "print_hide": 1
   },
   {
+   "collapsible": 1,
+   "fieldname": "more_info",
+   "fieldtype": "Section Break",
+   "label": "More Information",
+   "oldfieldtype": "Section Break",
+   "options": "fa fa-file-text",
+   "print_hide": 1
+  },
+  {
+   "fieldname": "opportunity",
+   "fieldtype": "Link",
+   "label": "Opportunity",
+   "options": "Opportunity",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
    "fieldname": "status",
    "fieldtype": "Select",
    "label": "Status",
@@ -258,7 +277,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-11-24 17:47:49.909000",
+ "modified": "2022-04-06 17:47:49.909000",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Request for Quotation",
@@ -327,4 +346,4 @@
  "show_name_in_global_search": 1,
  "sort_field": "modified",
  "sort_order": "DESC"
-}
\ No newline at end of file
+}
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
index 2db750e..67affe7 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
@@ -20,6 +20,7 @@
 
 STANDARD_USERS = ("Guest", "Administrator")
 
+
 class RequestforQuotation(BuyingController):
 	def validate(self):
 		self.validate_duplicate_supplier()
@@ -30,7 +31,7 @@
 
 		if self.docstatus < 1:
 			# after amend and save, status still shows as cancelled, until submit
-			frappe.db.set(self, 'status', 'Draft')
+			frappe.db.set(self, "status", "Draft")
 
 	def validate_duplicate_supplier(self):
 		supplier_list = [d.supplier for d in self.suppliers]
@@ -39,14 +40,24 @@
 
 	def validate_supplier_list(self):
 		for d in self.suppliers:
-			prevent_rfqs = frappe.db.get_value("Supplier", d.supplier, 'prevent_rfqs')
+			prevent_rfqs = frappe.db.get_value("Supplier", d.supplier, "prevent_rfqs")
 			if prevent_rfqs:
-				standing = frappe.db.get_value("Supplier Scorecard",d.supplier, 'status')
-				frappe.throw(_("RFQs are not allowed for {0} due to a scorecard standing of {1}").format(d.supplier, standing))
-			warn_rfqs = frappe.db.get_value("Supplier", d.supplier, 'warn_rfqs')
+				standing = frappe.db.get_value("Supplier Scorecard", d.supplier, "status")
+				frappe.throw(
+					_("RFQs are not allowed for {0} due to a scorecard standing of {1}").format(
+						d.supplier, standing
+					)
+				)
+			warn_rfqs = frappe.db.get_value("Supplier", d.supplier, "warn_rfqs")
 			if warn_rfqs:
-				standing = frappe.db.get_value("Supplier Scorecard",d.supplier, 'status')
-				frappe.msgprint(_("{0} currently has a {1} Supplier Scorecard standing, and RFQs to this supplier should be issued with caution.").format(d.supplier, standing), title=_("Caution"), indicator='orange')
+				standing = frappe.db.get_value("Supplier Scorecard", d.supplier, "status")
+				frappe.msgprint(
+					_(
+						"{0} currently has a {1} Supplier Scorecard standing, and RFQs to this supplier should be issued with caution."
+					).format(d.supplier, standing),
+					title=_("Caution"),
+					indicator="orange",
+				)
 
 	def update_email_id(self):
 		for rfq_supplier in self.suppliers:
@@ -55,17 +66,21 @@
 
 	def validate_email_id(self, args):
 		if not args.email_id:
-			frappe.throw(_("Row {0}: For Supplier {1}, Email Address is Required to send an email").format(args.idx, frappe.bold(args.supplier)))
+			frappe.throw(
+				_("Row {0}: For Supplier {1}, Email Address is Required to send an email").format(
+					args.idx, frappe.bold(args.supplier)
+				)
+			)
 
 	def on_submit(self):
-		frappe.db.set(self, 'status', 'Submitted')
+		frappe.db.set(self, "status", "Submitted")
 		for supplier in self.suppliers:
 			supplier.email_sent = 0
-			supplier.quote_status = 'Pending'
+			supplier.quote_status = "Pending"
 		self.send_to_supplier()
 
 	def on_cancel(self):
-		frappe.db.set(self, 'status', 'Cancelled')
+		frappe.db.set(self, "status", "Cancelled")
 
 	@frappe.whitelist()
 	def get_supplier_email_preview(self, supplier):
@@ -75,7 +90,7 @@
 
 		self.validate_email_id(rfq_supplier)
 
-		message  = self.supplier_rfq_mail(rfq_supplier, '', self.get_link(), True)
+		message = self.supplier_rfq_mail(rfq_supplier, "", self.get_link(), True)
 
 		return message
 
@@ -102,12 +117,13 @@
 	def update_supplier_part_no(self, supplier):
 		self.vendor = supplier
 		for item in self.items:
-			item.supplier_part_no = frappe.db.get_value('Item Supplier',
-				{'parent': item.item_code, 'supplier': supplier}, 'supplier_part_no')
+			item.supplier_part_no = frappe.db.get_value(
+				"Item Supplier", {"parent": item.item_code, "supplier": supplier}, "supplier_part_no"
+			)
 
 	def update_supplier_contact(self, rfq_supplier, link):
-		'''Create a new user for the supplier if not set in contact'''
-		update_password_link, contact = '', ''
+		"""Create a new user for the supplier if not set in contact"""
+		update_password_link, contact = "", ""
 
 		if frappe.db.exists("User", rfq_supplier.email_id):
 			user = frappe.get_doc("User", rfq_supplier.email_id)
@@ -125,14 +141,8 @@
 		else:
 			contact = frappe.new_doc("Contact")
 			contact.first_name = rfq_supplier.supplier_name or rfq_supplier.supplier
-			contact.append('links', {
-				'link_doctype': 'Supplier',
-				'link_name': rfq_supplier.supplier
-			})
-			contact.append('email_ids', {
-				'email_id': user.name,
-				'is_primary': 1
-			})
+			contact.append("links", {"link_doctype": "Supplier", "link_name": rfq_supplier.supplier})
+			contact.append("email_ids", {"email_id": user.name, "is_primary": 1})
 
 		if not contact.email_id and not contact.user:
 			contact.email_id = user.name
@@ -145,39 +155,38 @@
 			return contact.name
 
 	def create_user(self, rfq_supplier, link):
-		user = frappe.get_doc({
-			'doctype': 'User',
-			'send_welcome_email': 0,
-			'email': rfq_supplier.email_id,
-			'first_name': rfq_supplier.supplier_name or rfq_supplier.supplier,
-			'user_type': 'Website User',
-			'redirect_url': link
-		})
+		user = frappe.get_doc(
+			{
+				"doctype": "User",
+				"send_welcome_email": 0,
+				"email": rfq_supplier.email_id,
+				"first_name": rfq_supplier.supplier_name or rfq_supplier.supplier,
+				"user_type": "Website User",
+				"redirect_url": link,
+			}
+		)
 		user.save(ignore_permissions=True)
 		update_password_link = user.reset_password()
 
 		return user, update_password_link
 
 	def supplier_rfq_mail(self, data, update_password_link, rfq_link, preview=False):
-		full_name = get_user_fullname(frappe.session['user'])
+		full_name = get_user_fullname(frappe.session["user"])
 		if full_name == "Guest":
 			full_name = "Administrator"
 
 		# send document dict and some important data from suppliers row
 		# to render message_for_supplier from any template
 		doc_args = self.as_dict()
-		doc_args.update({
-			'supplier': data.get('supplier'),
-			'supplier_name': data.get('supplier_name')
-		})
+		doc_args.update({"supplier": data.get("supplier"), "supplier_name": data.get("supplier_name")})
 
 		args = {
-			'update_password_link': update_password_link,
-			'message': frappe.render_template(self.message_for_supplier, doc_args),
-			'rfq_link': rfq_link,
-			'user_fullname': full_name,
-			'supplier_name' : data.get('supplier_name'),
-			'supplier_salutation' : self.salutation or 'Dear Mx.',
+			"update_password_link": update_password_link,
+			"message": frappe.render_template(self.message_for_supplier, doc_args),
+			"rfq_link": rfq_link,
+			"user_fullname": full_name,
+			"supplier_name": data.get("supplier_name"),
+			"supplier_salutation": self.salutation or "Dear Mx.",
 		}
 
 		subject = self.subject or _("Request for Quotation")
@@ -193,9 +202,16 @@
 		self.send_email(data, sender, subject, message, attachments)
 
 	def send_email(self, data, sender, subject, message, attachments):
-		make(subject = subject, content=message,recipients=data.email_id,
-			sender=sender,attachments = attachments, send_email=True,
-		     	doctype=self.doctype, name=self.name)["name"]
+		make(
+			subject=subject,
+			content=message,
+			recipients=data.email_id,
+			sender=sender,
+			attachments=attachments,
+			send_email=True,
+			doctype=self.doctype,
+			name=self.name,
+		)["name"]
 
 		frappe.msgprint(_("Email Sent to Supplier {0}").format(data.supplier))
 
@@ -207,9 +223,10 @@
 	def update_rfq_supplier_status(self, sup_name=None):
 		for supplier in self.suppliers:
 			if sup_name == None or supplier.supplier == sup_name:
-				quote_status = _('Received')
+				quote_status = _("Received")
 				for item in self.items:
-					sqi_count = frappe.db.sql("""
+					sqi_count = frappe.db.sql(
+						"""
 						SELECT
 							COUNT(sqi.name) as count
 						FROM
@@ -219,42 +236,59 @@
 							AND sqi.docstatus = 1
 							AND sqi.request_for_quotation_item = %(rqi)s
 							AND sqi.parent = sq.name""",
-						{"supplier": supplier.supplier, "rqi": item.name}, as_dict=1)[0]
+						{"supplier": supplier.supplier, "rqi": item.name},
+						as_dict=1,
+					)[0]
 					if (sqi_count.count) == 0:
-						quote_status = _('Pending')
+						quote_status = _("Pending")
 				supplier.quote_status = quote_status
 
 
 @frappe.whitelist()
 def send_supplier_emails(rfq_name):
-	check_portal_enabled('Request for Quotation')
+	check_portal_enabled("Request for Quotation")
 	rfq = frappe.get_doc("Request for Quotation", rfq_name)
-	if rfq.docstatus==1:
+	if rfq.docstatus == 1:
 		rfq.send_to_supplier()
 
+
 def check_portal_enabled(reference_doctype):
-	if not frappe.db.get_value('Portal Menu Item',
-		{'reference_doctype': reference_doctype}, 'enabled'):
-		frappe.throw(_("The Access to Request for Quotation From Portal is Disabled. To Allow Access, Enable it in Portal Settings."))
+	if not frappe.db.get_value(
+		"Portal Menu Item", {"reference_doctype": reference_doctype}, "enabled"
+	):
+		frappe.throw(
+			_(
+				"The Access to Request for Quotation From Portal is Disabled. To Allow Access, Enable it in Portal Settings."
+			)
+		)
+
 
 def get_list_context(context=None):
 	from erpnext.controllers.website_list_for_contact import get_list_context
+
 	list_context = get_list_context(context)
-	list_context.update({
-		'show_sidebar': True,
-		'show_search': True,
-		'no_breadcrumbs': True,
-		'title': _('Request for Quotation'),
-	})
+	list_context.update(
+		{
+			"show_sidebar": True,
+			"show_search": True,
+			"no_breadcrumbs": True,
+			"title": _("Request for Quotation"),
+		}
+	)
 	return list_context
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_supplier_contacts(doctype, txt, searchfield, start, page_len, filters):
-	return frappe.db.sql("""select `tabContact`.name from `tabContact`, `tabDynamic Link`
+	return frappe.db.sql(
+		"""select `tabContact`.name from `tabContact`, `tabDynamic Link`
 		where `tabDynamic Link`.link_doctype = 'Supplier' and (`tabDynamic Link`.link_name=%(name)s
 		and `tabDynamic Link`.link_name like %(txt)s) and `tabContact`.name = `tabDynamic Link`.parent
-		limit %(start)s, %(page_len)s""", {"start": start, "page_len":page_len, "txt": "%%%s%%" % txt, "name": filters.get('supplier')})
+		limit %(page_len)s offset %(start)s""",
+		{"start": start, "page_len": page_len, "txt": "%%%s%%" % txt, "name": filters.get("supplier")},
+	)
+
 
 @frappe.whitelist()
 def make_supplier_quotation_from_rfq(source_name, target_doc=None, for_supplier=None):
@@ -262,28 +296,34 @@
 		if for_supplier:
 			target_doc.supplier = for_supplier
 			args = get_party_details(for_supplier, party_type="Supplier", ignore_permissions=True)
-			target_doc.currency = args.currency or get_party_account_currency('Supplier', for_supplier, source.company)
-			target_doc.buying_price_list = args.buying_price_list or frappe.db.get_value('Buying Settings', None, 'buying_price_list')
+			target_doc.currency = args.currency or get_party_account_currency(
+				"Supplier", for_supplier, source.company
+			)
+			target_doc.buying_price_list = args.buying_price_list or frappe.db.get_value(
+				"Buying Settings", None, "buying_price_list"
+			)
 		set_missing_values(source, target_doc)
 
-	doclist = get_mapped_doc("Request for Quotation", source_name, {
-		"Request for Quotation": {
-			"doctype": "Supplier Quotation",
-			"validation": {
-				"docstatus": ["=", 1]
-			}
-		},
-		"Request for Quotation Item": {
-			"doctype": "Supplier Quotation Item",
-			"field_map": {
-				"name": "request_for_quotation_item",
-				"parent": "request_for_quotation"
+	doclist = get_mapped_doc(
+		"Request for Quotation",
+		source_name,
+		{
+			"Request for Quotation": {
+				"doctype": "Supplier Quotation",
+				"validation": {"docstatus": ["=", 1]},
 			},
-		}
-	}, target_doc, postprocess)
+			"Request for Quotation Item": {
+				"doctype": "Supplier Quotation Item",
+				"field_map": {"name": "request_for_quotation_item", "parent": "request_for_quotation"},
+			},
+		},
+		target_doc,
+		postprocess,
+	)
 
 	return doclist
 
+
 # This method is used to make supplier quotation from supplier's portal.
 @frappe.whitelist()
 def create_supplier_quotation(doc):
@@ -291,15 +331,19 @@
 		doc = json.loads(doc)
 
 	try:
-		sq_doc = frappe.get_doc({
-			"doctype": "Supplier Quotation",
-			"supplier": doc.get('supplier'),
-			"terms": doc.get("terms"),
-			"company": doc.get("company"),
-			"currency": doc.get('currency') or get_party_account_currency('Supplier', doc.get('supplier'), doc.get('company')),
-			"buying_price_list": doc.get('buying_price_list') or frappe.db.get_value('Buying Settings', None, 'buying_price_list')
-		})
-		add_items(sq_doc, doc.get('supplier'), doc.get('items'))
+		sq_doc = frappe.get_doc(
+			{
+				"doctype": "Supplier Quotation",
+				"supplier": doc.get("supplier"),
+				"terms": doc.get("terms"),
+				"company": doc.get("company"),
+				"currency": doc.get("currency")
+				or get_party_account_currency("Supplier", doc.get("supplier"), doc.get("company")),
+				"buying_price_list": doc.get("buying_price_list")
+				or frappe.db.get_value("Buying Settings", None, "buying_price_list"),
+			}
+		)
+		add_items(sq_doc, doc.get("supplier"), doc.get("items"))
 		sq_doc.flags.ignore_permissions = True
 		sq_doc.run_method("set_missing_values")
 		sq_doc.save()
@@ -308,6 +352,7 @@
 	except Exception:
 		return None
 
+
 def add_items(sq_doc, supplier, items):
 	for data in items:
 		if data.get("qty") > 0:
@@ -316,21 +361,36 @@
 
 			create_rfq_items(sq_doc, supplier, data)
 
+
 def create_rfq_items(sq_doc, supplier, data):
 	args = {}
 
-	for field in ['item_code', 'item_name', 'description', 'qty', 'rate', 'conversion_factor',
-		'warehouse', 'material_request', 'material_request_item', 'stock_qty']:
+	for field in [
+		"item_code",
+		"item_name",
+		"description",
+		"qty",
+		"rate",
+		"conversion_factor",
+		"warehouse",
+		"material_request",
+		"material_request_item",
+		"stock_qty",
+	]:
 		args[field] = data.get(field)
 
-	args.update({
-		"request_for_quotation_item": data.name,
-		"request_for_quotation": data.parent,
-		"supplier_part_no": frappe.db.get_value("Item Supplier",
-			{'parent': data.item_code, 'supplier': supplier}, "supplier_part_no")
-	})
+	args.update(
+		{
+			"request_for_quotation_item": data.name,
+			"request_for_quotation": data.parent,
+			"supplier_part_no": frappe.db.get_value(
+				"Item Supplier", {"parent": data.item_code, "supplier": supplier}, "supplier_part_no"
+			),
+		}
+	)
 
-	sq_doc.append('items', args)
+	sq_doc.append("items", args)
+
 
 @frappe.whitelist()
 def get_pdf(doctype, name, supplier):
@@ -338,15 +398,18 @@
 	if doc:
 		download_pdf(doctype, name, doc=doc)
 
+
 def get_rfq_doc(doctype, name, supplier):
 	if supplier:
 		doc = frappe.get_doc(doctype, name)
 		doc.update_supplier_part_no(supplier)
 		return doc
 
+
 @frappe.whitelist()
-def get_item_from_material_requests_based_on_supplier(source_name, target_doc = None):
-	mr_items_list = frappe.db.sql("""
+def get_item_from_material_requests_based_on_supplier(source_name, target_doc=None):
+	mr_items_list = frappe.db.sql(
+		"""
 		SELECT
 			mr.name, mr_item.item_code
 		FROM
@@ -361,52 +424,65 @@
 			AND mr.status != "Stopped"
 			AND mr.material_request_type = "Purchase"
 			AND mr.docstatus = 1
-			AND mr.per_ordered < 99.99""", {"supplier": source_name}, as_dict=1)
+			AND mr.per_ordered < 99.99""",
+		{"supplier": source_name},
+		as_dict=1,
+	)
 
 	material_requests = {}
 	for d in mr_items_list:
 		material_requests.setdefault(d.name, []).append(d.item_code)
 
 	for mr, items in material_requests.items():
-		target_doc = get_mapped_doc("Material Request", mr, {
-			"Material Request": {
-				"doctype": "Request for Quotation",
-				"validation": {
-					"docstatus": ["=", 1],
-					"material_request_type": ["=", "Purchase"],
-				}
+		target_doc = get_mapped_doc(
+			"Material Request",
+			mr,
+			{
+				"Material Request": {
+					"doctype": "Request for Quotation",
+					"validation": {
+						"docstatus": ["=", 1],
+						"material_request_type": ["=", "Purchase"],
+					},
+				},
+				"Material Request Item": {
+					"doctype": "Request for Quotation Item",
+					"condition": lambda row: row.item_code in items,
+					"field_map": [
+						["name", "material_request_item"],
+						["parent", "material_request"],
+						["uom", "uom"],
+					],
+				},
 			},
-			"Material Request Item": {
-				"doctype": "Request for Quotation Item",
-				"condition": lambda row: row.item_code in items,
-				"field_map": [
-					["name", "material_request_item"],
-					["parent", "material_request"],
-					["uom", "uom"]
-				]
-			}
-		}, target_doc)
+			target_doc,
+		)
 
 	return target_doc
 
+
 @frappe.whitelist()
 def get_supplier_tag():
 	filters = {"document_type": "Supplier"}
-	tags = list(set(tag.tag for tag in frappe.get_all("Tag Link", filters=filters, fields=["tag"]) if tag))
+	tags = list(
+		set(tag.tag for tag in frappe.get_all("Tag Link", filters=filters, fields=["tag"]) if tag)
+	)
 
 	return tags
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_rfq_containing_supplier(doctype, txt, searchfield, start, page_len, filters):
 	conditions = ""
 	if txt:
-		conditions += "and rfq.name like '%%"+txt+"%%' "
+		conditions += "and rfq.name like '%%" + txt + "%%' "
 
 	if filters.get("transaction_date"):
 		conditions += "and rfq.transaction_date = '{0}'".format(filters.get("transaction_date"))
 
-	rfq_data = frappe.db.sql("""
+	rfq_data = frappe.db.sql(
+		"""
 		select
 			distinct rfq.name, rfq.transaction_date,
 			rfq.company
@@ -419,8 +495,11 @@
 			and rfq.company = '{1}'
 			{2}
 		order by rfq.transaction_date ASC
-		limit %(page_len)s offset %(start)s """ \
-		.format(filters.get("supplier"), filters.get("company"), conditions),
-			{"page_len": page_len, "start": start}, as_dict=1)
+		limit %(page_len)s offset %(start)s """.format(
+			filters.get("supplier"), filters.get("company"), conditions
+		),
+		{"page_len": page_len, "start": start},
+		as_dict=1,
+	)
 
 	return rfq_data
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation_dashboard.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation_dashboard.py
index dc1cda1..505e3e0 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation_dashboard.py
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation_dashboard.py
@@ -1,10 +1,8 @@
 def get_data():
 	return {
-		'docstatus': 1,
-		'fieldname': 'request_for_quotation',
-		'transactions': [
-			{
-				'items': ['Supplier Quotation']
-			},
-		]
+		"docstatus": 1,
+		"fieldname": "request_for_quotation",
+		"transactions": [
+			{"items": ["Supplier Quotation"]},
+		],
 	}
diff --git a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py
index 5b21124..064b806 100644
--- a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py
@@ -20,36 +20,36 @@
 	def test_quote_status(self):
 		rfq = make_request_for_quotation()
 
-		self.assertEqual(rfq.get('suppliers')[0].quote_status, 'Pending')
-		self.assertEqual(rfq.get('suppliers')[1].quote_status, 'Pending')
+		self.assertEqual(rfq.get("suppliers")[0].quote_status, "Pending")
+		self.assertEqual(rfq.get("suppliers")[1].quote_status, "Pending")
 
 		# Submit the first supplier quotation
-		sq = make_supplier_quotation_from_rfq(rfq.name, for_supplier=rfq.get('suppliers')[0].supplier)
+		sq = make_supplier_quotation_from_rfq(rfq.name, for_supplier=rfq.get("suppliers")[0].supplier)
 		sq.submit()
 
-		rfq.update_rfq_supplier_status() #rfq.get('suppliers')[1].supplier)
+		rfq.update_rfq_supplier_status()  # rfq.get('suppliers')[1].supplier)
 
-		self.assertEqual(rfq.get('suppliers')[0].quote_status, 'Received')
-		self.assertEqual(rfq.get('suppliers')[1].quote_status, 'Pending')
+		self.assertEqual(rfq.get("suppliers")[0].quote_status, "Received")
+		self.assertEqual(rfq.get("suppliers")[1].quote_status, "Pending")
 
 	def test_make_supplier_quotation(self):
 		rfq = make_request_for_quotation()
 
-		sq = make_supplier_quotation_from_rfq(rfq.name, for_supplier=rfq.get('suppliers')[0].supplier)
+		sq = make_supplier_quotation_from_rfq(rfq.name, for_supplier=rfq.get("suppliers")[0].supplier)
 		sq.submit()
 
-		sq1 = make_supplier_quotation_from_rfq(rfq.name, for_supplier=rfq.get('suppliers')[1].supplier)
+		sq1 = make_supplier_quotation_from_rfq(rfq.name, for_supplier=rfq.get("suppliers")[1].supplier)
 		sq1.submit()
 
-		self.assertEqual(sq.supplier, rfq.get('suppliers')[0].supplier)
-		self.assertEqual(sq.get('items')[0].request_for_quotation, rfq.name)
-		self.assertEqual(sq.get('items')[0].item_code, "_Test Item")
-		self.assertEqual(sq.get('items')[0].qty, 5)
+		self.assertEqual(sq.supplier, rfq.get("suppliers")[0].supplier)
+		self.assertEqual(sq.get("items")[0].request_for_quotation, rfq.name)
+		self.assertEqual(sq.get("items")[0].item_code, "_Test Item")
+		self.assertEqual(sq.get("items")[0].qty, 5)
 
-		self.assertEqual(sq1.supplier, rfq.get('suppliers')[1].supplier)
-		self.assertEqual(sq1.get('items')[0].request_for_quotation, rfq.name)
-		self.assertEqual(sq1.get('items')[0].item_code, "_Test Item")
-		self.assertEqual(sq1.get('items')[0].qty, 5)
+		self.assertEqual(sq1.supplier, rfq.get("suppliers")[1].supplier)
+		self.assertEqual(sq1.get("items")[0].request_for_quotation, rfq.name)
+		self.assertEqual(sq1.get("items")[0].item_code, "_Test Item")
+		self.assertEqual(sq1.get("items")[0].qty, 5)
 
 	def test_make_supplier_quotation_with_special_characters(self):
 		frappe.delete_doc_if_exists("Supplier", "_Test Supplier '1", force=1)
@@ -60,52 +60,49 @@
 
 		rfq = make_request_for_quotation(supplier_data=supplier_wt_appos)
 
-		sq = make_supplier_quotation_from_rfq(rfq.name, for_supplier=supplier_wt_appos[0].get("supplier"))
+		sq = make_supplier_quotation_from_rfq(
+			rfq.name, for_supplier=supplier_wt_appos[0].get("supplier")
+		)
 		sq.submit()
 
-		frappe.form_dict = frappe.local("form_dict")
 		frappe.form_dict.name = rfq.name
 
-		self.assertEqual(
-			check_supplier_has_docname_access(supplier_wt_appos[0].get('supplier')),
-			True
-		)
+		self.assertEqual(check_supplier_has_docname_access(supplier_wt_appos[0].get("supplier")), True)
 
 		# reset form_dict
 		frappe.form_dict.name = None
 
 	def test_make_supplier_quotation_from_portal(self):
 		rfq = make_request_for_quotation()
-		rfq.get('items')[0].rate = 100
+		rfq.get("items")[0].rate = 100
 		rfq.supplier = rfq.suppliers[0].supplier
 		supplier_quotation_name = create_supplier_quotation(rfq)
 
-		supplier_quotation_doc = frappe.get_doc('Supplier Quotation', supplier_quotation_name)
+		supplier_quotation_doc = frappe.get_doc("Supplier Quotation", supplier_quotation_name)
 
-		self.assertEqual(supplier_quotation_doc.supplier, rfq.get('suppliers')[0].supplier)
-		self.assertEqual(supplier_quotation_doc.get('items')[0].request_for_quotation, rfq.name)
-		self.assertEqual(supplier_quotation_doc.get('items')[0].item_code, "_Test Item")
-		self.assertEqual(supplier_quotation_doc.get('items')[0].qty, 5)
-		self.assertEqual(supplier_quotation_doc.get('items')[0].amount, 500)
+		self.assertEqual(supplier_quotation_doc.supplier, rfq.get("suppliers")[0].supplier)
+		self.assertEqual(supplier_quotation_doc.get("items")[0].request_for_quotation, rfq.name)
+		self.assertEqual(supplier_quotation_doc.get("items")[0].item_code, "_Test Item")
+		self.assertEqual(supplier_quotation_doc.get("items")[0].qty, 5)
+		self.assertEqual(supplier_quotation_doc.get("items")[0].amount, 500)
 
 	def test_make_multi_uom_supplier_quotation(self):
 		item_code = "_Test Multi UOM RFQ Item"
-		if not frappe.db.exists('Item', item_code):
-			item = make_item(item_code, {'stock_uom': '_Test UOM'})
-			row = item.append('uoms', {
-				'uom': 'Kg',
-				'conversion_factor': 2
-			})
+		if not frappe.db.exists("Item", item_code):
+			item = make_item(item_code, {"stock_uom": "_Test UOM"})
+			row = item.append("uoms", {"uom": "Kg", "conversion_factor": 2})
 			row.db_update()
 
-		rfq = make_request_for_quotation(item_code="_Test Multi UOM RFQ Item", uom="Kg", conversion_factor=2)
-		rfq.get('items')[0].rate = 100
+		rfq = make_request_for_quotation(
+			item_code="_Test Multi UOM RFQ Item", uom="Kg", conversion_factor=2
+		)
+		rfq.get("items")[0].rate = 100
 		rfq.supplier = rfq.suppliers[0].supplier
 
 		self.assertEqual(rfq.items[0].stock_qty, 10)
 
 		supplier_quotation_name = create_supplier_quotation(rfq)
-		supplier_quotation = frappe.get_doc('Supplier Quotation', supplier_quotation_name)
+		supplier_quotation = frappe.get_doc("Supplier Quotation", supplier_quotation_name)
 
 		self.assertEqual(supplier_quotation.items[0].qty, 5)
 		self.assertEqual(supplier_quotation.items[0].stock_qty, 10)
@@ -116,58 +113,62 @@
 		rfq = make_rfq(opportunity.name)
 
 		self.assertEqual(len(rfq.get("items")), len(opportunity.get("items")))
-		rfq.message_for_supplier = 'Please supply the specified items at the best possible rates.'
+		rfq.message_for_supplier = "Please supply the specified items at the best possible rates."
 
 		for item in rfq.items:
 			item.warehouse = "_Test Warehouse - _TC"
 
 		for data in supplier_data:
-			rfq.append('suppliers', data)
+			rfq.append("suppliers", data)
 
-		rfq.status = 'Draft'
+		rfq.status = "Draft"
 		rfq.submit()
 
+
 def make_request_for_quotation(**args):
 	"""
 	:param supplier_data: List containing supplier data
 	"""
 	args = frappe._dict(args)
 	supplier_data = args.get("supplier_data") if args.get("supplier_data") else get_supplier_data()
-	rfq = frappe.new_doc('Request for Quotation')
+	rfq = frappe.new_doc("Request for Quotation")
 	rfq.transaction_date = nowdate()
-	rfq.status = 'Draft'
-	rfq.company = '_Test Company'
-	rfq.message_for_supplier = 'Please supply the specified items at the best possible rates.'
+	rfq.status = "Draft"
+	rfq.company = "_Test Company"
+	rfq.message_for_supplier = "Please supply the specified items at the best possible rates."
 
 	for data in supplier_data:
-		rfq.append('suppliers', data)
+		rfq.append("suppliers", data)
 
-	rfq.append("items", {
-		"item_code": args.item_code or "_Test Item",
-		"description": "_Test Item",
-		"uom": args.uom or "_Test UOM",
-		"stock_uom": args.stock_uom or "_Test UOM",
-		"qty": args.qty or 5,
-		"conversion_factor": args.conversion_factor or 1.0,
-		"warehouse": args.warehouse or "_Test Warehouse - _TC",
-		"schedule_date": nowdate()
-	})
+	rfq.append(
+		"items",
+		{
+			"item_code": args.item_code or "_Test Item",
+			"description": "_Test Item",
+			"uom": args.uom or "_Test UOM",
+			"stock_uom": args.stock_uom or "_Test UOM",
+			"qty": args.qty or 5,
+			"conversion_factor": args.conversion_factor or 1.0,
+			"warehouse": args.warehouse or "_Test Warehouse - _TC",
+			"schedule_date": nowdate(),
+		},
+	)
 
 	rfq.submit()
 
 	return rfq
 
-def get_supplier_data():
-	return [{
-		"supplier": "_Test Supplier",
-		"supplier_name": "_Test Supplier"
-	},
-	{
-		"supplier": "_Test Supplier 1",
-		"supplier_name": "_Test Supplier 1"
-	}]
 
-supplier_wt_appos = [{
-	"supplier": "_Test Supplier '1",
-	"supplier_name": "_Test Supplier '1",
-}]
+def get_supplier_data():
+	return [
+		{"supplier": "_Test Supplier", "supplier_name": "_Test Supplier"},
+		{"supplier": "_Test Supplier 1", "supplier_name": "_Test Supplier 1"},
+	]
+
+
+supplier_wt_appos = [
+	{
+		"supplier": "_Test Supplier '1",
+		"supplier_name": "_Test Supplier '1",
+	}
+]
diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json
index a57d9a9..e0ee658 100644
--- a/erpnext/buying/doctype/supplier/supplier.json
+++ b/erpnext/buying/doctype/supplier/supplier.json
@@ -18,16 +18,16 @@
   "tax_id",
   "tax_category",
   "tax_withholding_category",
-  "is_transporter",
-  "is_internal_supplier",
-  "represents_company",
   "image",
   "column_break0",
   "supplier_group",
   "supplier_type",
   "allow_purchase_invoice_creation_without_purchase_order",
   "allow_purchase_invoice_creation_without_purchase_receipt",
+  "is_internal_supplier",
+  "represents_company",
   "disabled",
+  "is_transporter",
   "warn_rfqs",
   "warn_pos",
   "prevent_rfqs",
@@ -38,12 +38,6 @@
   "default_currency",
   "column_break_10",
   "default_price_list",
-  "section_credit_limit",
-  "payment_terms",
-  "cb_21",
-  "on_hold",
-  "hold_type",
-  "release_date",
   "address_contacts",
   "address_html",
   "column_break1",
@@ -57,6 +51,12 @@
   "primary_address",
   "default_payable_accounts",
   "accounts",
+  "section_credit_limit",
+  "payment_terms",
+  "cb_21",
+  "on_hold",
+  "hold_type",
+  "release_date",
   "default_tax_withholding_config",
   "column_break2",
   "website",
@@ -258,7 +258,7 @@
    "collapsible": 1,
    "fieldname": "section_credit_limit",
    "fieldtype": "Section Break",
-   "label": "Credit Limit"
+   "label": "Payment Terms"
   },
   {
    "fieldname": "payment_terms",
@@ -432,7 +432,7 @@
    "link_fieldname": "party"
   }
  ],
- "modified": "2021-10-20 22:03:33.147249",
+ "modified": "2022-04-16 18:02:27.838623",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Supplier",
@@ -497,6 +497,7 @@
  "show_name_in_global_search": 1,
  "sort_field": "modified",
  "sort_order": "ASC",
+ "states": [],
  "title_field": "supplier_name",
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py
index 4f9ff43..43152e8 100644
--- a/erpnext/buying/doctype/supplier/supplier.py
+++ b/erpnext/buying/doctype/supplier/supplier.py
@@ -30,27 +30,27 @@
 
 	def before_save(self):
 		if not self.on_hold:
-			self.hold_type = ''
-			self.release_date = ''
+			self.hold_type = ""
+			self.release_date = ""
 		elif self.on_hold and not self.hold_type:
-			self.hold_type = 'All'
+			self.hold_type = "All"
 
 	def load_dashboard_info(self):
 		info = get_dashboard_info(self.doctype, self.name)
-		self.set_onload('dashboard_info', info)
+		self.set_onload("dashboard_info", info)
 
 	def autoname(self):
-		supp_master_name = frappe.defaults.get_global_default('supp_master_name')
-		if supp_master_name == 'Supplier Name':
+		supp_master_name = frappe.defaults.get_global_default("supp_master_name")
+		if supp_master_name == "Supplier Name":
 			self.name = self.supplier_name
-		elif supp_master_name == 'Naming Series':
+		elif supp_master_name == "Naming Series":
 			set_name_by_naming_series(self)
 		else:
 			self.name = set_name_from_naming_options(frappe.get_meta(self.doctype).autoname, self)
 
 	def on_update(self):
 		if not self.naming_series:
-			self.naming_series = ''
+			self.naming_series = ""
 
 		self.create_primary_contact()
 		self.create_primary_address()
@@ -59,7 +59,7 @@
 		self.flags.is_new_doc = self.is_new()
 
 		# validation for Naming Series mandatory field...
-		if frappe.defaults.get_global_default('supp_master_name') == 'Naming Series':
+		if frappe.defaults.get_global_default("supp_master_name") == "Naming Series":
 			if not self.naming_series:
 				msgprint(_("Series is mandatory"), raise_exception=1)
 
@@ -68,13 +68,13 @@
 
 	@frappe.whitelist()
 	def get_supplier_group_details(self):
-		doc = frappe.get_doc('Supplier Group', self.supplier_group)
+		doc = frappe.get_doc("Supplier Group", self.supplier_group)
 		self.payment_terms = ""
 		self.accounts = []
 
 		if doc.accounts:
 			for account in doc.accounts:
-				child = self.append('accounts')
+				child = self.append("accounts")
 				child.company = account.company
 				child.account = account.account
 
@@ -84,12 +84,25 @@
 		self.save()
 
 	def validate_internal_supplier(self):
-		internal_supplier = frappe.db.get_value("Supplier",
-			{"is_internal_supplier": 1, "represents_company": self.represents_company, "name": ("!=", self.name)}, "name")
+		if not self.is_internal_supplier:
+			self.represents_company = ""
+
+		internal_supplier = frappe.db.get_value(
+			"Supplier",
+			{
+				"is_internal_supplier": 1,
+				"represents_company": self.represents_company,
+				"name": ("!=", self.name),
+			},
+			"name",
+		)
 
 		if internal_supplier:
-			frappe.throw(_("Internal Supplier for company {0} already exists").format(
-				frappe.bold(self.represents_company)))
+			frappe.throw(
+				_("Internal Supplier for company {0} already exists").format(
+					frappe.bold(self.represents_company)
+				)
+			)
 
 	def create_primary_contact(self):
 		from erpnext.selling.doctype.customer.customer import make_contact
@@ -97,16 +110,16 @@
 		if not self.supplier_primary_contact:
 			if self.mobile_no or self.email_id:
 				contact = make_contact(self)
-				self.db_set('supplier_primary_contact', contact.name)
-				self.db_set('mobile_no', self.mobile_no)
-				self.db_set('email_id', self.email_id)
+				self.db_set("supplier_primary_contact", contact.name)
+				self.db_set("mobile_no", self.mobile_no)
+				self.db_set("email_id", self.email_id)
 
 	def create_primary_address(self):
 		from frappe.contacts.doctype.address.address import get_address_display
 
 		from erpnext.selling.doctype.customer.customer import make_address
 
-		if self.flags.is_new_doc and self.get('address_line1'):
+		if self.flags.is_new_doc and self.get("address_line1"):
 			address = make_address(self)
 			address_display = get_address_display(address.name)
 
@@ -115,7 +128,8 @@
 
 	def on_trash(self):
 		if self.supplier_primary_contact:
-			frappe.db.sql("""
+			frappe.db.sql(
+				"""
 				UPDATE `tabSupplier`
 				SET
 					supplier_primary_contact=null,
@@ -123,19 +137,23 @@
 					mobile_no=null,
 					email_id=null,
 					primary_address=null
-				WHERE name=%(name)s""", {"name": self.name})
+				WHERE name=%(name)s""",
+				{"name": self.name},
+			)
 
-		delete_contact_and_address('Supplier', self.name)
+		delete_contact_and_address("Supplier", self.name)
 
 	def after_rename(self, olddn, newdn, merge=False):
-		if frappe.defaults.get_global_default('supp_master_name') == 'Supplier Name':
+		if frappe.defaults.get_global_default("supp_master_name") == "Supplier Name":
 			frappe.db.set(self, "supplier_name", newdn)
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_supplier_primary_contact(doctype, txt, searchfield, start, page_len, filters):
 	supplier = filters.get("supplier")
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		SELECT
 			`tabContact`.name from `tabContact`,
 			`tabDynamic Link`
@@ -144,7 +162,6 @@
 			and `tabDynamic Link`.link_name = %(supplier)s
 			and `tabDynamic Link`.link_doctype = 'Supplier'
 			and `tabContact`.name like %(txt)s
-		""", {
-			'supplier': supplier,
-			'txt': '%%%s%%' % txt
-		})
+		""",
+		{"supplier": supplier, "txt": "%%%s%%" % txt},
+	)
diff --git a/erpnext/buying/doctype/supplier/supplier_dashboard.py b/erpnext/buying/doctype/supplier/supplier_dashboard.py
index 78efd8e..11bb06e 100644
--- a/erpnext/buying/doctype/supplier/supplier_dashboard.py
+++ b/erpnext/buying/doctype/supplier/supplier_dashboard.py
@@ -3,29 +3,16 @@
 
 def get_data():
 	return {
-		'heatmap': True,
-		'heatmap_message': _('This is based on transactions against this Supplier. See timeline below for details'),
-		'fieldname': 'supplier',
-		'non_standard_fieldnames': {
-			'Payment Entry': 'party_name',
-			'Bank Account': 'party'
-		},
-		'transactions': [
-			{
-				'label': _('Procurement'),
-				'items': ['Request for Quotation', 'Supplier Quotation']
-			},
-			{
-				'label': _('Orders'),
-				'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
-			},
-			{
-				'label': _('Payments'),
-				'items': ['Payment Entry', 'Bank Account']
-			},
-			{
-				'label': _('Pricing'),
-				'items': ['Pricing Rule']
-			}
-		]
+		"heatmap": True,
+		"heatmap_message": _(
+			"This is based on transactions against this Supplier. See timeline below for details"
+		),
+		"fieldname": "supplier",
+		"non_standard_fieldnames": {"Payment Entry": "party_name", "Bank Account": "party"},
+		"transactions": [
+			{"label": _("Procurement"), "items": ["Request for Quotation", "Supplier Quotation"]},
+			{"label": _("Orders"), "items": ["Purchase Order", "Purchase Receipt", "Purchase Invoice"]},
+			{"label": _("Payments"), "items": ["Payment Entry", "Bank Account"]},
+			{"label": _("Pricing"), "items": ["Pricing Rule"]},
+		],
 	}
diff --git a/erpnext/buying/doctype/supplier/test_supplier.py b/erpnext/buying/doctype/supplier/test_supplier.py
index 7358e2a..5572268 100644
--- a/erpnext/buying/doctype/supplier/test_supplier.py
+++ b/erpnext/buying/doctype/supplier/test_supplier.py
@@ -8,8 +8,8 @@
 from erpnext.accounts.party import get_due_date
 from erpnext.exceptions import PartyDisabled
 
-test_dependencies = ['Payment Term', 'Payment Terms Template']
-test_records = frappe.get_test_records('Supplier')
+test_dependencies = ["Payment Term", "Payment Terms Template"]
+test_records = frappe.get_test_records("Supplier")
 
 from frappe.tests.utils import FrappeTestCase
 
@@ -42,7 +42,8 @@
 	def test_supplier_default_payment_terms(self):
 		# Payment Term based on Days after invoice date
 		frappe.db.set_value(
-			"Supplier", "_Test Supplier With Template 1", "payment_terms", "_Test Payment Term Template 3")
+			"Supplier", "_Test Supplier With Template 1", "payment_terms", "_Test Payment Term Template 3"
+		)
 
 		due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier With Template 1")
 		self.assertEqual(due_date, "2016-02-21")
@@ -52,7 +53,8 @@
 
 		# Payment Term based on last day of month
 		frappe.db.set_value(
-			"Supplier", "_Test Supplier With Template 1", "payment_terms", "_Test Payment Term Template 1")
+			"Supplier", "_Test Supplier With Template 1", "payment_terms", "_Test Payment Term Template 1"
+		)
 
 		due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier With Template 1")
 		self.assertEqual(due_date, "2016-02-29")
@@ -63,13 +65,17 @@
 		frappe.db.set_value("Supplier", "_Test Supplier With Template 1", "payment_terms", "")
 
 		# Set credit limit for the supplier group instead of supplier and evaluate the due date
-		frappe.db.set_value("Supplier Group", "_Test Supplier Group", "payment_terms", "_Test Payment Term Template 3")
+		frappe.db.set_value(
+			"Supplier Group", "_Test Supplier Group", "payment_terms", "_Test Payment Term Template 3"
+		)
 
 		due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier With Template 1")
 		self.assertEqual(due_date, "2016-02-21")
 
 		# Payment terms for Supplier Group instead of supplier and evaluate the due date
-		frappe.db.set_value("Supplier Group", "_Test Supplier Group", "payment_terms", "_Test Payment Term Template 1")
+		frappe.db.set_value(
+			"Supplier Group", "_Test Supplier Group", "payment_terms", "_Test Payment Term Template 1"
+		)
 
 		# Leap year
 		due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier With Template 1")
@@ -105,15 +111,15 @@
 
 	def test_supplier_country(self):
 		# Test that country field exists in Supplier DocType
-		supplier = frappe.get_doc('Supplier', '_Test Supplier with Country')
-		self.assertTrue('country' in supplier.as_dict())
+		supplier = frappe.get_doc("Supplier", "_Test Supplier with Country")
+		self.assertTrue("country" in supplier.as_dict())
 
 		# Test if test supplier field record is 'Greece'
 		self.assertEqual(supplier.country, "Greece")
 
 		# Test update Supplier instance country value
-		supplier = frappe.get_doc('Supplier', '_Test Supplier')
-		supplier.country = 'Greece'
+		supplier = frappe.get_doc("Supplier", "_Test Supplier")
+		supplier.country = "Greece"
 		supplier.save()
 		self.assertEqual(supplier.country, "Greece")
 
@@ -126,19 +132,18 @@
 		details = get_party_details("_Test Supplier With Tax Category", party_type="Supplier")
 		self.assertEqual(details.tax_category, "_Test Tax Category 1")
 
-		address = frappe.get_doc(dict(
-			doctype='Address',
-			address_title='_Test Address With Tax Category',
-			tax_category='_Test Tax Category 2',
-			address_type='Billing',
-			address_line1='Station Road',
-			city='_Test City',
-			country='India',
-			links=[dict(
-				link_doctype='Supplier',
-				link_name='_Test Supplier With Tax Category'
-			)]
-		)).insert()
+		address = frappe.get_doc(
+			dict(
+				doctype="Address",
+				address_title="_Test Address With Tax Category",
+				tax_category="_Test Tax Category 2",
+				address_type="Billing",
+				address_line1="Station Road",
+				city="_Test City",
+				country="India",
+				links=[dict(link_doctype="Supplier", link_name="_Test Supplier With Tax Category")],
+			)
+		).insert()
 
 		# Tax Category with Address
 		details = get_party_details("_Test Supplier With Tax Category", party_type="Supplier")
@@ -147,18 +152,21 @@
 		# Rollback
 		address.delete()
 
+
 def create_supplier(**args):
 	args = frappe._dict(args)
 
 	if frappe.db.exists("Supplier", args.supplier_name):
 		return frappe.get_doc("Supplier", args.supplier_name)
 
-	doc = frappe.get_doc({
-		"doctype": "Supplier",
-		"supplier_name": args.supplier_name,
-		"supplier_group": args.supplier_group or "Services",
-		"supplier_type": args.supplier_type or "Company",
-		"tax_withholding_category": args.tax_withholding_category
-	}).insert()
+	doc = frappe.get_doc(
+		{
+			"doctype": "Supplier",
+			"supplier_name": args.supplier_name,
+			"supplier_group": args.supplier_group or "Services",
+			"supplier_type": args.supplier_type or "Company",
+			"tax_withholding_category": args.tax_withholding_category,
+		}
+	).insert()
 
 	return doc
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
index 023c95d..8d1939a 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
@@ -72,8 +72,8 @@
   "section_break_46",
   "base_grand_total",
   "base_rounding_adjustment",
-  "base_in_words",
   "base_rounded_total",
+  "base_in_words",
   "column_break4",
   "grand_total",
   "rounding_adjustment",
@@ -635,6 +635,7 @@
    "fieldname": "rounded_total",
    "fieldtype": "Currency",
    "label": "Rounded Total",
+   "options": "currency",
    "read_only": 1
   },
   {
@@ -772,11 +773,10 @@
    "fieldtype": "Column Break"
   },
   {
-   "default": "No",
+   "default": "0",
    "fieldname": "is_subcontracted",
-   "fieldtype": "Select",
+   "fieldtype": "Check",
    "label": "Is Subcontracted",
-   "options": "\nYes\nNo",
    "print_hide": 1
   },
   {
@@ -810,7 +810,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-12-11 06:43:20.924080",
+ "modified": "2022-03-14 16:13:20.284572",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Supplier Quotation",
@@ -875,6 +875,7 @@
  "show_name_in_global_search": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "timeline_field": "supplier",
  "title_field": "title"
 }
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
index 171de78..c19c1df 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
@@ -10,9 +10,8 @@
 from erpnext.buying.utils import validate_for_items
 from erpnext.controllers.buying_controller import BuyingController
 
-form_grid_templates = {
-	"items": "templates/form_grid/item_grid.html"
-}
+form_grid_templates = {"items": "templates/form_grid/item_grid.html"}
+
 
 class SupplierQuotation(BuyingController):
 	def validate(self):
@@ -22,8 +21,8 @@
 			self.status = "Draft"
 
 		from erpnext.controllers.status_updater import validate_status
-		validate_status(self.status, ["Draft", "Submitted", "Stopped",
-			"Cancelled"])
+
+		validate_status(self.status, ["Draft", "Submitted", "Stopped", "Cancelled"])
 
 		validate_for_items(self)
 		self.validate_with_previous_doc()
@@ -42,17 +41,19 @@
 		pass
 
 	def validate_with_previous_doc(self):
-		super(SupplierQuotation, self).validate_with_previous_doc({
-			"Material Request": {
-				"ref_dn_field": "prevdoc_docname",
-				"compare_fields": [["company", "="]],
-			},
-			"Material Request Item": {
-				"ref_dn_field": "prevdoc_detail_docname",
-				"compare_fields": [["item_code", "="], ["uom", "="]],
-				"is_child_table": True
+		super(SupplierQuotation, self).validate_with_previous_doc(
+			{
+				"Material Request": {
+					"ref_dn_field": "prevdoc_docname",
+					"compare_fields": [["company", "="]],
+				},
+				"Material Request Item": {
+					"ref_dn_field": "prevdoc_detail_docname",
+					"compare_fields": [["item_code", "="], ["uom", "="]],
+					"is_child_table": True,
+				},
 			}
-		})
+		)
 
 	def validate_valid_till(self):
 		if self.valid_till and getdate(self.valid_till) < getdate(self.transaction_date):
@@ -64,18 +65,28 @@
 			if item.request_for_quotation:
 				rfq_list.add(item.request_for_quotation)
 		for rfq in rfq_list:
-			doc = frappe.get_doc('Request for Quotation', rfq)
-			doc_sup = frappe.get_all('Request for Quotation Supplier', filters=
-				{'parent': doc.name, 'supplier': self.supplier}, fields=['name', 'quote_status'])
+			doc = frappe.get_doc("Request for Quotation", rfq)
+			doc_sup = frappe.get_all(
+				"Request for Quotation Supplier",
+				filters={"parent": doc.name, "supplier": self.supplier},
+				fields=["name", "quote_status"],
+			)
 
 			doc_sup = doc_sup[0] if doc_sup else None
 			if not doc_sup:
-				frappe.throw(_("Supplier {0} not found in {1}").format(self.supplier,
-					"<a href='desk/app/Form/Request for Quotation/{0}'> Request for Quotation {0} </a>".format(doc.name)))
+				frappe.throw(
+					_("Supplier {0} not found in {1}").format(
+						self.supplier,
+						"<a href='desk/app/Form/Request for Quotation/{0}'> Request for Quotation {0} </a>".format(
+							doc.name
+						),
+					)
+				)
 
-			quote_status = _('Received')
+			quote_status = _("Received")
 			for item in doc.items:
-				sqi_count = frappe.db.sql("""
+				sqi_count = frappe.db.sql(
+					"""
 					SELECT
 						COUNT(sqi.name) as count
 					FROM
@@ -86,30 +97,41 @@
 						AND sq.name != %(me)s
 						AND sqi.request_for_quotation_item = %(rqi)s
 						AND sqi.parent = sq.name""",
-					{"supplier": self.supplier, "rqi": item.name, 'me': self.name}, as_dict=1)[0]
-				self_count = sum(my_item.request_for_quotation_item == item.name
-					for my_item in self.items) if include_me else 0
+					{"supplier": self.supplier, "rqi": item.name, "me": self.name},
+					as_dict=1,
+				)[0]
+				self_count = (
+					sum(my_item.request_for_quotation_item == item.name for my_item in self.items)
+					if include_me
+					else 0
+				)
 				if (sqi_count.count + self_count) == 0:
-					quote_status = _('Pending')
+					quote_status = _("Pending")
 
-				frappe.db.set_value('Request for Quotation Supplier', doc_sup.name, 'quote_status', quote_status)
+				frappe.db.set_value(
+					"Request for Quotation Supplier", doc_sup.name, "quote_status", quote_status
+				)
+
 
 def get_list_context(context=None):
 	from erpnext.controllers.website_list_for_contact import get_list_context
+
 	list_context = get_list_context(context)
-	list_context.update({
-		'show_sidebar': True,
-		'show_search': True,
-		'no_breadcrumbs': True,
-		'title': _('Supplier Quotation'),
-	})
+	list_context.update(
+		{
+			"show_sidebar": True,
+			"show_search": True,
+			"no_breadcrumbs": True,
+			"title": _("Supplier Quotation"),
+		}
+	)
 
 	return list_context
 
+
 @frappe.whitelist()
 def make_purchase_order(source_name, target_doc=None):
 	def set_missing_values(source, target):
-		target.ignore_pricing_rule = 1
 		target.run_method("set_missing_values")
 		target.run_method("get_schedule_dates")
 		target.run_method("calculate_taxes_and_totals")
@@ -117,73 +139,91 @@
 	def update_item(obj, target, source_parent):
 		target.stock_qty = flt(obj.qty) * flt(obj.conversion_factor)
 
-	doclist = get_mapped_doc("Supplier Quotation", source_name,		{
-		"Supplier Quotation": {
-			"doctype": "Purchase Order",
-			"validation": {
-				"docstatus": ["=", 1],
-			}
+	doclist = get_mapped_doc(
+		"Supplier Quotation",
+		source_name,
+		{
+			"Supplier Quotation": {
+				"doctype": "Purchase Order",
+				"validation": {
+					"docstatus": ["=", 1],
+				},
+			},
+			"Supplier Quotation Item": {
+				"doctype": "Purchase Order Item",
+				"field_map": [
+					["name", "supplier_quotation_item"],
+					["parent", "supplier_quotation"],
+					["material_request", "material_request"],
+					["material_request_item", "material_request_item"],
+					["sales_order", "sales_order"],
+				],
+				"postprocess": update_item,
+			},
+			"Purchase Taxes and Charges": {
+				"doctype": "Purchase Taxes and Charges",
+			},
 		},
-		"Supplier Quotation Item": {
-			"doctype": "Purchase Order Item",
-			"field_map": [
-				["name", "supplier_quotation_item"],
-				["parent", "supplier_quotation"],
-				["material_request", "material_request"],
-				["material_request_item", "material_request_item"],
-				["sales_order", "sales_order"]
-			],
-			"postprocess": update_item
-		},
-		"Purchase Taxes and Charges": {
-			"doctype": "Purchase Taxes and Charges",
-		},
-	}, target_doc, set_missing_values)
+		target_doc,
+		set_missing_values,
+	)
 
+	doclist.set_onload("ignore_price_list", True)
 	return doclist
 
+
 @frappe.whitelist()
 def make_purchase_invoice(source_name, target_doc=None):
-	doc = get_mapped_doc("Supplier Quotation", source_name, {
-		"Supplier Quotation": {
-			"doctype": "Purchase Invoice",
-			"validation": {
-				"docstatus": ["=", 1],
-			}
+	doc = get_mapped_doc(
+		"Supplier Quotation",
+		source_name,
+		{
+			"Supplier Quotation": {
+				"doctype": "Purchase Invoice",
+				"validation": {
+					"docstatus": ["=", 1],
+				},
+			},
+			"Supplier Quotation Item": {"doctype": "Purchase Invoice Item"},
+			"Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges"},
 		},
-		"Supplier Quotation Item": {
-			"doctype": "Purchase Invoice Item"
-		},
-		"Purchase Taxes and Charges": {
-			"doctype": "Purchase Taxes and Charges"
-		}
-	}, target_doc)
+		target_doc,
+	)
 
 	return doc
 
 
 @frappe.whitelist()
 def make_quotation(source_name, target_doc=None):
-	doclist = get_mapped_doc("Supplier Quotation", source_name, {
-		"Supplier Quotation": {
-			"doctype": "Quotation",
-			"field_map": {
-				"name": "supplier_quotation",
-			}
+	doclist = get_mapped_doc(
+		"Supplier Quotation",
+		source_name,
+		{
+			"Supplier Quotation": {
+				"doctype": "Quotation",
+				"field_map": {
+					"name": "supplier_quotation",
+				},
+			},
+			"Supplier Quotation Item": {
+				"doctype": "Quotation Item",
+				"condition": lambda doc: frappe.db.get_value("Item", doc.item_code, "is_sales_item") == 1,
+				"add_if_empty": True,
+			},
 		},
-		"Supplier Quotation Item": {
-			"doctype": "Quotation Item",
-			"condition": lambda doc: frappe.db.get_value("Item", doc.item_code, "is_sales_item")==1,
-			"add_if_empty": True
-		}
-	}, target_doc)
+		target_doc,
+	)
 
 	return doclist
 
+
 def set_expired_status():
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		UPDATE
 			`tabSupplier Quotation` SET `status` = 'Expired'
 		WHERE
 			`status` not in ('Cancelled', 'Stopped') AND `valid_till` < %s
-		""", (nowdate()))
+		""",
+		(nowdate()),
+	)
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation_dashboard.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_dashboard.py
index 236b91a..369fc94 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation_dashboard.py
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_dashboard.py
@@ -3,28 +3,16 @@
 
 def get_data():
 	return {
-		'fieldname': 'supplier_quotation',
-		'non_standard_fieldnames': {
-			'Auto Repeat': 'reference_document'
+		"fieldname": "supplier_quotation",
+		"non_standard_fieldnames": {"Auto Repeat": "reference_document"},
+		"internal_links": {
+			"Material Request": ["items", "material_request"],
+			"Request for Quotation": ["items", "request_for_quotation"],
+			"Project": ["items", "project"],
 		},
-		'internal_links': {
-			'Material Request': ['items', 'material_request'],
-			'Request for Quotation': ['items', 'request_for_quotation'],
-			'Project': ['items', 'project'],
-		},
-		'transactions': [
-			{
-				'label': _('Related'),
-				'items': ['Purchase Order', 'Quotation']
-			},
-			{
-				'label': _('Reference'),
-				'items': ['Material Request', 'Request for Quotation', 'Project']
-			},
-			{
-				'label': _('Subscription'),
-				'items': ['Auto Repeat']
-			},
-		]
-
+		"transactions": [
+			{"label": _("Related"), "items": ["Purchase Order", "Quotation"]},
+			{"label": _("Reference"), "items": ["Material Request", "Request for Quotation", "Project"]},
+			{"label": _("Subscription"), "items": ["Auto Repeat"]},
+		],
 	}
diff --git a/erpnext/buying/doctype/supplier_quotation/test_records.json b/erpnext/buying/doctype/supplier_quotation/test_records.json
index 0f835d2..8acac32 100644
--- a/erpnext/buying/doctype/supplier_quotation/test_records.json
+++ b/erpnext/buying/doctype/supplier_quotation/test_records.json
@@ -7,7 +7,7 @@
   "doctype": "Supplier Quotation", 
   "base_grand_total": 5000.0, 
   "grand_total": 5000.0, 
-  "is_subcontracted": "No", 
+  "is_subcontracted": 0, 
   "naming_series": "_T-Supplier Quotation-", 
   "base_net_total": 5000.0, 
   "items": [
diff --git a/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py
index a4d4597..13c851c 100644
--- a/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py
+++ b/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py
@@ -2,8 +2,6 @@
 # License: GNU General Public License v3. See license.txt
 
 
-
-
 import frappe
 from frappe.tests.utils import FrappeTestCase
 
@@ -14,8 +12,7 @@
 
 		sq = frappe.copy_doc(test_records[0]).insert()
 
-		self.assertRaises(frappe.ValidationError, make_purchase_order,
-			sq.name)
+		self.assertRaises(frappe.ValidationError, make_purchase_order, sq.name)
 
 		sq = frappe.get_doc("Supplier Quotation", sq.name)
 		sq.submit()
@@ -32,4 +29,5 @@
 
 		po.insert()
 
-test_records = frappe.get_test_records('Supplier Quotation')
+
+test_records = frappe.get_test_records("Supplier Quotation")
diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
index 3bcc0de..486bf23 100644
--- a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
+++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
@@ -16,7 +16,6 @@
 
 
 class SupplierScorecard(Document):
-
 	def validate(self):
 		self.validate_standings()
 		self.validate_criteria_weights()
@@ -34,12 +33,16 @@
 		for c1 in self.standings:
 			for c2 in self.standings:
 				if c1 != c2:
-					if (c1.max_grade > c2.min_grade and c1.min_grade < c2.max_grade):
-						throw(_('Overlap in scoring between {0} and {1}').format(c1.standing_name,c2.standing_name))
+					if c1.max_grade > c2.min_grade and c1.min_grade < c2.max_grade:
+						throw(_("Overlap in scoring between {0} and {1}").format(c1.standing_name, c2.standing_name))
 				if c2.min_grade == score:
 					score = c2.max_grade
 		if score < 100:
-			throw(_('Unable to find score starting at {0}. You need to have standing scores covering 0 to 100').format(score))
+			throw(
+				_(
+					"Unable to find score starting at {0}. You need to have standing scores covering 0 to 100"
+				).format(score)
+			)
 
 	def validate_criteria_weights(self):
 
@@ -48,10 +51,11 @@
 			weight += c.weight
 
 		if weight != 100:
-			throw(_('Criteria weights must add up to 100%'))
+			throw(_("Criteria weights must add up to 100%"))
 
 	def calculate_total_score(self):
-		scorecards = frappe.db.sql("""
+		scorecards = frappe.db.sql(
+			"""
 			SELECT
 				scp.name
 			FROM
@@ -61,18 +65,20 @@
 				AND scp.docstatus = 1
 			ORDER BY
 				scp.end_date DESC""",
-				{"sc": self.name}, as_dict=1)
+			{"sc": self.name},
+			as_dict=1,
+		)
 
 		period = 0
 		total_score = 0
 		total_max_score = 0
 		for scp in scorecards:
-			my_sc = frappe.get_doc('Supplier Scorecard Period', scp.name)
+			my_sc = frappe.get_doc("Supplier Scorecard Period", scp.name)
 			my_scp_weight = self.weighting_function
-			my_scp_weight = my_scp_weight.replace('{period_number}', str(period))
+			my_scp_weight = my_scp_weight.replace("{period_number}", str(period))
 
-			my_scp_maxweight = my_scp_weight.replace('{total_score}', '100')
-			my_scp_weight = my_scp_weight.replace('{total_score}', str(my_sc.total_score))
+			my_scp_maxweight = my_scp_weight.replace("{total_score}", "100")
+			my_scp_weight = my_scp_weight.replace("{total_score}", str(my_sc.total_score))
 
 			max_score = my_sc.calculate_weighted_score(my_scp_maxweight)
 			score = my_sc.calculate_weighted_score(my_scp_weight)
@@ -81,24 +87,25 @@
 			total_max_score += max_score
 			period += 1
 		if total_max_score > 0:
-			self.supplier_score = round(100.0 * (total_score / total_max_score) ,1)
+			self.supplier_score = round(100.0 * (total_score / total_max_score), 1)
 		else:
-			self.supplier_score =  100
+			self.supplier_score = 100
 
 	def update_standing(self):
 		# Get the setup document
 
 		for standing in self.standings:
-			if (not standing.min_grade or (standing.min_grade <= self.supplier_score)) and \
-				(not standing.max_grade or (standing.max_grade > self.supplier_score)):
+			if (not standing.min_grade or (standing.min_grade <= self.supplier_score)) and (
+				not standing.max_grade or (standing.max_grade > self.supplier_score)
+			):
 				self.status = standing.standing_name
 				self.indicator_color = standing.standing_color
 				self.notify_supplier = standing.notify_supplier
 				self.notify_employee = standing.notify_employee
 				self.employee_link = standing.employee_link
 
-				#Update supplier standing info
-				for fieldname in ('prevent_pos', 'prevent_rfqs','warn_rfqs','warn_pos'):
+				# Update supplier standing info
+				for fieldname in ("prevent_pos", "prevent_rfqs", "warn_rfqs", "warn_pos"):
 					self.set(fieldname, standing.get(fieldname))
 					frappe.db.set_value("Supplier", self.supplier, fieldname, self.get(fieldname))
 
@@ -109,7 +116,8 @@
 	scs = frappe.get_doc(doctype, name)
 	out = {}
 	timeline_data = {}
-	scorecards = frappe.db.sql("""
+	scorecards = frappe.db.sql(
+		"""
 		SELECT
 			sc.name
 		FROM
@@ -117,39 +125,48 @@
 		WHERE
 			sc.scorecard = %(scs)s
 			AND sc.docstatus = 1""",
-			{"scs": scs.name}, as_dict=1)
+		{"scs": scs.name},
+		as_dict=1,
+	)
 
 	for sc in scorecards:
-		start_date, end_date, total_score = frappe.db.get_value('Supplier Scorecard Period', sc.name, ['start_date', 'end_date', 'total_score'])
+		start_date, end_date, total_score = frappe.db.get_value(
+			"Supplier Scorecard Period", sc.name, ["start_date", "end_date", "total_score"]
+		)
 		for single_date in daterange(start_date, end_date):
-			timeline_data[time.mktime(single_date.timetuple())] =  total_score
+			timeline_data[time.mktime(single_date.timetuple())] = total_score
 
-	out['timeline_data'] = timeline_data
+	out["timeline_data"] = timeline_data
 	return out
 
+
 def daterange(start_date, end_date):
-    for n in range(int ((end_date - start_date).days)+1):
-        yield start_date + timedelta(n)
+	for n in range(int((end_date - start_date).days) + 1):
+		yield start_date + timedelta(n)
+
 
 def refresh_scorecards():
-	scorecards = frappe.db.sql("""
+	scorecards = frappe.db.sql(
+		"""
 		SELECT
 			sc.name
 		FROM
 			`tabSupplier Scorecard` sc""",
-			{}, as_dict=1)
+		{},
+		as_dict=1,
+	)
 	for sc in scorecards:
 		# Check to see if any new scorecard periods are created
 		if make_all_scorecards(sc.name) > 0:
 			# Save the scorecard to update the score and standings
-			frappe.get_doc('Supplier Scorecard', sc.name).save()
+			frappe.get_doc("Supplier Scorecard", sc.name).save()
 
 
 @frappe.whitelist()
 def make_all_scorecards(docname):
 
-	sc = frappe.get_doc('Supplier Scorecard', docname)
-	supplier = frappe.get_doc('Supplier',sc.supplier)
+	sc = frappe.get_doc("Supplier Scorecard", docname)
+	supplier = frappe.get_doc("Supplier", sc.supplier)
 
 	start_date = getdate(supplier.creation)
 	end_date = get_scorecard_date(sc.period, start_date)
@@ -161,7 +178,8 @@
 
 	while (start_date < todays) and (end_date <= todays):
 		# check to make sure there is no scorecard period already created
-		scorecards = frappe.db.sql("""
+		scorecards = frappe.db.sql(
+			"""
 			SELECT
 				scp.name
 			FROM
@@ -177,7 +195,9 @@
 					AND scp.end_date > %(start_date)s))
 			ORDER BY
 				scp.end_date DESC""",
-				{"sc": docname, "start_date": start_date, "end_date": end_date}, as_dict=1)
+			{"sc": docname, "start_date": start_date, "end_date": end_date},
+			as_dict=1,
+		)
 		if len(scorecards) == 0:
 			period_card = make_supplier_scorecard(docname, None)
 			period_card.start_date = start_date
@@ -189,82 +209,179 @@
 				first_start_date = start_date
 			last_end_date = end_date
 
-		start_date = getdate(add_days(end_date,1))
+		start_date = getdate(add_days(end_date, 1))
 		end_date = get_scorecard_date(sc.period, start_date)
 	if scp_count > 0:
-		frappe.msgprint(_("Created {0} scorecards for {1} between: ").format(scp_count, sc.supplier) + str(first_start_date) + " - " + str(last_end_date))
+		frappe.msgprint(
+			_("Created {0} scorecards for {1} between:").format(scp_count, sc.supplier)
+			+ " "
+			+ str(first_start_date)
+			+ " - "
+			+ str(last_end_date)
+		)
 	return scp_count
 
+
 def get_scorecard_date(period, start_date):
-	if period == 'Per Week':
-		end_date = getdate(add_days(start_date,7))
-	elif period == 'Per Month':
+	if period == "Per Week":
+		end_date = getdate(add_days(start_date, 7))
+	elif period == "Per Month":
 		end_date = get_last_day(start_date)
-	elif period == 'Per Year':
-		end_date = add_days(add_years(start_date,1), -1)
+	elif period == "Per Year":
+		end_date = add_days(add_years(start_date, 1), -1)
 	return end_date
 
+
 def make_default_records():
 	install_variable_docs = [
-		{"param_name": "total_accepted_items", "variable_label": "Total Accepted Items", \
-			"path": "get_total_accepted_items"},
-		{"param_name": "total_accepted_amount", "variable_label": "Total Accepted Amount", \
-			"path": "get_total_accepted_amount"},
-		{"param_name": "total_rejected_items", "variable_label": "Total Rejected Items", \
-			"path": "get_total_rejected_items"},
-		{"param_name": "total_rejected_amount", "variable_label": "Total Rejected Amount", \
-			"path": "get_total_rejected_amount"},
-		{"param_name": "total_received_items", "variable_label": "Total Received Items", \
-			"path": "get_total_received_items"},
-		{"param_name": "total_received_amount", "variable_label": "Total Received Amount", \
-			"path": "get_total_received_amount"},
-		{"param_name": "rfq_response_days", "variable_label": "RFQ Response Days", \
-			"path": "get_rfq_response_days"},
-		{"param_name": "sq_total_items", "variable_label": "SQ Total Items", \
-			"path": "get_sq_total_items"},
-		{"param_name": "sq_total_number", "variable_label": "SQ Total Number", \
-			"path": "get_sq_total_number"},
-		{"param_name": "rfq_total_number", "variable_label": "RFQ Total Number", \
-			"path": "get_rfq_total_number"},
-		{"param_name": "rfq_total_items", "variable_label": "RFQ Total Items", \
-			"path": "get_rfq_total_items"},
-		{"param_name": "tot_item_days", "variable_label": "Total Item Days", \
-			"path": "get_item_workdays"},
-		{"param_name": "on_time_shipment_num", "variable_label": "# of On Time Shipments", "path": \
-			"get_on_time_shipments"},
-		{"param_name": "cost_of_delayed_shipments", "variable_label": "Cost of Delayed Shipments", \
-			"path": "get_cost_of_delayed_shipments"},
-		{"param_name": "cost_of_on_time_shipments", "variable_label": "Cost of On Time Shipments", \
-			"path": "get_cost_of_on_time_shipments"},
-		{"param_name": "total_working_days", "variable_label": "Total Working Days", \
-			"path": "get_total_workdays"},
-		{"param_name": "tot_cost_shipments", "variable_label": "Total Cost of Shipments", \
-			"path": "get_total_cost_of_shipments"},
-		{"param_name": "tot_days_late", "variable_label": "Total Days Late", \
-			"path": "get_total_days_late"},
-		{"param_name": "total_shipments", "variable_label": "Total Shipments", \
-			"path": "get_total_shipments"}
+		{
+			"param_name": "total_accepted_items",
+			"variable_label": "Total Accepted Items",
+			"path": "get_total_accepted_items",
+		},
+		{
+			"param_name": "total_accepted_amount",
+			"variable_label": "Total Accepted Amount",
+			"path": "get_total_accepted_amount",
+		},
+		{
+			"param_name": "total_rejected_items",
+			"variable_label": "Total Rejected Items",
+			"path": "get_total_rejected_items",
+		},
+		{
+			"param_name": "total_rejected_amount",
+			"variable_label": "Total Rejected Amount",
+			"path": "get_total_rejected_amount",
+		},
+		{
+			"param_name": "total_received_items",
+			"variable_label": "Total Received Items",
+			"path": "get_total_received_items",
+		},
+		{
+			"param_name": "total_received_amount",
+			"variable_label": "Total Received Amount",
+			"path": "get_total_received_amount",
+		},
+		{
+			"param_name": "rfq_response_days",
+			"variable_label": "RFQ Response Days",
+			"path": "get_rfq_response_days",
+		},
+		{
+			"param_name": "sq_total_items",
+			"variable_label": "SQ Total Items",
+			"path": "get_sq_total_items",
+		},
+		{
+			"param_name": "sq_total_number",
+			"variable_label": "SQ Total Number",
+			"path": "get_sq_total_number",
+		},
+		{
+			"param_name": "rfq_total_number",
+			"variable_label": "RFQ Total Number",
+			"path": "get_rfq_total_number",
+		},
+		{
+			"param_name": "rfq_total_items",
+			"variable_label": "RFQ Total Items",
+			"path": "get_rfq_total_items",
+		},
+		{
+			"param_name": "tot_item_days",
+			"variable_label": "Total Item Days",
+			"path": "get_item_workdays",
+		},
+		{
+			"param_name": "on_time_shipment_num",
+			"variable_label": "# of On Time Shipments",
+			"path": "get_on_time_shipments",
+		},
+		{
+			"param_name": "cost_of_delayed_shipments",
+			"variable_label": "Cost of Delayed Shipments",
+			"path": "get_cost_of_delayed_shipments",
+		},
+		{
+			"param_name": "cost_of_on_time_shipments",
+			"variable_label": "Cost of On Time Shipments",
+			"path": "get_cost_of_on_time_shipments",
+		},
+		{
+			"param_name": "total_working_days",
+			"variable_label": "Total Working Days",
+			"path": "get_total_workdays",
+		},
+		{
+			"param_name": "tot_cost_shipments",
+			"variable_label": "Total Cost of Shipments",
+			"path": "get_total_cost_of_shipments",
+		},
+		{
+			"param_name": "tot_days_late",
+			"variable_label": "Total Days Late",
+			"path": "get_total_days_late",
+		},
+		{
+			"param_name": "total_shipments",
+			"variable_label": "Total Shipments",
+			"path": "get_total_shipments",
+		},
 	]
 	install_standing_docs = [
-		{"min_grade": 0.0, "prevent_rfqs": 1, "notify_supplier": 0, "max_grade": 30.0, "prevent_pos": 1, \
-			"standing_color": "Red", "notify_employee": 0, "standing_name": "Very Poor"},
-		{"min_grade": 30.0, "prevent_rfqs": 1, "notify_supplier": 0, "max_grade": 50.0, "prevent_pos": 0, \
-			"standing_color": "Red", "notify_employee": 0, "standing_name": "Poor"},
-		{"min_grade": 50.0, "prevent_rfqs": 0, "notify_supplier": 0, "max_grade": 80.0, "prevent_pos": 0, \
-			"standing_color": "Green", "notify_employee": 0, "standing_name": "Average"},
-		{"min_grade": 80.0, "prevent_rfqs": 0, "notify_supplier": 0, "max_grade": 100.0, "prevent_pos": 0, \
-			"standing_color": "Blue", "notify_employee": 0, "standing_name": "Excellent"},
+		{
+			"min_grade": 0.0,
+			"prevent_rfqs": 1,
+			"notify_supplier": 0,
+			"max_grade": 30.0,
+			"prevent_pos": 1,
+			"standing_color": "Red",
+			"notify_employee": 0,
+			"standing_name": "Very Poor",
+		},
+		{
+			"min_grade": 30.0,
+			"prevent_rfqs": 1,
+			"notify_supplier": 0,
+			"max_grade": 50.0,
+			"prevent_pos": 0,
+			"standing_color": "Red",
+			"notify_employee": 0,
+			"standing_name": "Poor",
+		},
+		{
+			"min_grade": 50.0,
+			"prevent_rfqs": 0,
+			"notify_supplier": 0,
+			"max_grade": 80.0,
+			"prevent_pos": 0,
+			"standing_color": "Green",
+			"notify_employee": 0,
+			"standing_name": "Average",
+		},
+		{
+			"min_grade": 80.0,
+			"prevent_rfqs": 0,
+			"notify_supplier": 0,
+			"max_grade": 100.0,
+			"prevent_pos": 0,
+			"standing_color": "Blue",
+			"notify_employee": 0,
+			"standing_name": "Excellent",
+		},
 	]
 
 	for d in install_variable_docs:
 		try:
-			d['doctype'] = "Supplier Scorecard Variable"
+			d["doctype"] = "Supplier Scorecard Variable"
 			frappe.get_doc(d).insert()
 		except frappe.NameError:
 			pass
 	for d in install_standing_docs:
 		try:
-			d['doctype'] = "Supplier Scorecard Standing"
+			d["doctype"] = "Supplier Scorecard Standing"
 			frappe.get_doc(d).insert()
 		except frappe.NameError:
 			pass
diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py
index 5d69326..e3557bd 100644
--- a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py
+++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py
@@ -3,14 +3,9 @@
 
 def get_data():
 	return {
-		'heatmap': True,
-		'heatmap_message': _('This covers all scorecards tied to this Setup'),
-		'fieldname': 'supplier',
-		'method' : 'erpnext.buying.doctype.supplier_scorecard.supplier_scorecard.get_timeline_data',
-		'transactions': [
-			{
-				'label': _('Scorecards'),
-				'items': ['Supplier Scorecard Period']
-			}
-		]
+		"heatmap": True,
+		"heatmap_message": _("This covers all scorecards tied to this Setup"),
+		"fieldname": "supplier",
+		"method": "erpnext.buying.doctype.supplier_scorecard.supplier_scorecard.get_timeline_data",
+		"transactions": [{"label": _("Scorecards"), "items": ["Supplier Scorecard Period"]}],
 	}
diff --git a/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py b/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py
index 8ecc2cd..2694f96 100644
--- a/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py
+++ b/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py
@@ -7,7 +7,6 @@
 
 
 class TestSupplierScorecard(FrappeTestCase):
-
 	def test_create_scorecard(self):
 		doc = make_supplier_scorecard().insert()
 		self.assertEqual(doc.name, valid_scorecard[0].get("supplier"))
@@ -17,7 +16,8 @@
 		my_doc = make_supplier_scorecard()
 		for d in my_doc.criteria:
 			d.weight = 0
-		self.assertRaises(frappe.ValidationError,my_doc.insert)
+		self.assertRaises(frappe.ValidationError, my_doc.insert)
+
 
 def make_supplier_scorecard():
 	my_doc = frappe.get_doc(valid_scorecard[0])
@@ -36,95 +36,106 @@
 	my_doc = make_supplier_scorecard()
 	if frappe.db.exists("Supplier Scorecard", my_doc.name):
 		# Delete all the periods, then delete the scorecard
-		frappe.db.sql("""delete from `tabSupplier Scorecard Period` where scorecard = %(scorecard)s""", {'scorecard': my_doc.name})
-		frappe.db.sql("""delete from `tabSupplier Scorecard Scoring Criteria` where parenttype = 'Supplier Scorecard Period'""")
-		frappe.db.sql("""delete from `tabSupplier Scorecard Scoring Standing` where parenttype = 'Supplier Scorecard Period'""")
-		frappe.db.sql("""delete from `tabSupplier Scorecard Scoring Variable` where parenttype = 'Supplier Scorecard Period'""")
+		frappe.db.sql(
+			"""delete from `tabSupplier Scorecard Period` where scorecard = %(scorecard)s""",
+			{"scorecard": my_doc.name},
+		)
+		frappe.db.sql(
+			"""delete from `tabSupplier Scorecard Scoring Criteria` where parenttype = 'Supplier Scorecard Period'"""
+		)
+		frappe.db.sql(
+			"""delete from `tabSupplier Scorecard Scoring Standing` where parenttype = 'Supplier Scorecard Period'"""
+		)
+		frappe.db.sql(
+			"""delete from `tabSupplier Scorecard Scoring Variable` where parenttype = 'Supplier Scorecard Period'"""
+		)
 		frappe.delete_doc(my_doc.doctype, my_doc.name)
 
+
 valid_scorecard = [
 	{
-		"standings":[
+		"standings": [
 			{
-				"min_grade":0.0,"name":"Very Poor",
-				"prevent_rfqs":1,
-				"notify_supplier":0,
-				"doctype":"Supplier Scorecard Scoring Standing",
-				"max_grade":30.0,
-				"prevent_pos":1,
-				"warn_pos":0,
-				"warn_rfqs":0,
-				"standing_color":"Red",
-				"notify_employee":0,
-				"standing_name":"Very Poor",
-				"parenttype":"Supplier Scorecard",
-				"parentfield":"standings"
+				"min_grade": 0.0,
+				"name": "Very Poor",
+				"prevent_rfqs": 1,
+				"notify_supplier": 0,
+				"doctype": "Supplier Scorecard Scoring Standing",
+				"max_grade": 30.0,
+				"prevent_pos": 1,
+				"warn_pos": 0,
+				"warn_rfqs": 0,
+				"standing_color": "Red",
+				"notify_employee": 0,
+				"standing_name": "Very Poor",
+				"parenttype": "Supplier Scorecard",
+				"parentfield": "standings",
 			},
 			{
-				"min_grade":30.0,
-				"name":"Poor",
-				"prevent_rfqs":1,
-				"notify_supplier":0,
-				"doctype":"Supplier Scorecard Scoring Standing",
-				"max_grade":50.0,
-				"prevent_pos":0,
-				"warn_pos":0,
-				"warn_rfqs":0,
-				"standing_color":"Red",
-				"notify_employee":0,
-				"standing_name":"Poor",
-				"parenttype":"Supplier Scorecard",
-				"parentfield":"standings"
+				"min_grade": 30.0,
+				"name": "Poor",
+				"prevent_rfqs": 1,
+				"notify_supplier": 0,
+				"doctype": "Supplier Scorecard Scoring Standing",
+				"max_grade": 50.0,
+				"prevent_pos": 0,
+				"warn_pos": 0,
+				"warn_rfqs": 0,
+				"standing_color": "Red",
+				"notify_employee": 0,
+				"standing_name": "Poor",
+				"parenttype": "Supplier Scorecard",
+				"parentfield": "standings",
 			},
 			{
-				"min_grade":50.0,
-				"name":"Average",
-				"prevent_rfqs":0,
-				"notify_supplier":0,
-				"doctype":"Supplier Scorecard Scoring Standing",
-				"max_grade":80.0,
-				"prevent_pos":0,
-				"warn_pos":0,
-				"warn_rfqs":0,
-				"standing_color":"Green",
-				"notify_employee":0,
-				"standing_name":"Average",
-				"parenttype":"Supplier Scorecard",
-				"parentfield":"standings"
+				"min_grade": 50.0,
+				"name": "Average",
+				"prevent_rfqs": 0,
+				"notify_supplier": 0,
+				"doctype": "Supplier Scorecard Scoring Standing",
+				"max_grade": 80.0,
+				"prevent_pos": 0,
+				"warn_pos": 0,
+				"warn_rfqs": 0,
+				"standing_color": "Green",
+				"notify_employee": 0,
+				"standing_name": "Average",
+				"parenttype": "Supplier Scorecard",
+				"parentfield": "standings",
 			},
 			{
-				"min_grade":80.0,
-				"name":"Excellent",
-				"prevent_rfqs":0,
-				"notify_supplier":0,
-				"doctype":"Supplier Scorecard Scoring Standing",
-				"max_grade":100.0,
-				"prevent_pos":0,
-				"warn_pos":0,
-				"warn_rfqs":0,
-				"standing_color":"Blue",
-				"notify_employee":0,
-				"standing_name":"Excellent",
-				"parenttype":"Supplier Scorecard",
-				"parentfield":"standings"
+				"min_grade": 80.0,
+				"name": "Excellent",
+				"prevent_rfqs": 0,
+				"notify_supplier": 0,
+				"doctype": "Supplier Scorecard Scoring Standing",
+				"max_grade": 100.0,
+				"prevent_pos": 0,
+				"warn_pos": 0,
+				"warn_rfqs": 0,
+				"standing_color": "Blue",
+				"notify_employee": 0,
+				"standing_name": "Excellent",
+				"parenttype": "Supplier Scorecard",
+				"parentfield": "standings",
+			},
+		],
+		"prevent_pos": 0,
+		"period": "Per Month",
+		"doctype": "Supplier Scorecard",
+		"warn_pos": 0,
+		"warn_rfqs": 0,
+		"notify_supplier": 0,
+		"criteria": [
+			{
+				"weight": 100.0,
+				"doctype": "Supplier Scorecard Scoring Criteria",
+				"criteria_name": "Delivery",
+				"formula": "100",
 			}
 		],
-		"prevent_pos":0,
-		"period":"Per Month",
-		"doctype":"Supplier Scorecard",
-		"warn_pos":0,
-		"warn_rfqs":0,
-		"notify_supplier":0,
-		"criteria":[
-			{
-				"weight":100.0,
-				"doctype":"Supplier Scorecard Scoring Criteria",
-				"criteria_name":"Delivery",
-				"formula": "100"
-			}
-		],
-		"supplier":"_Test Supplier",
-		"name":"_Test Supplier",
-		"weighting_function":"{total_score} * max( 0, min ( 1 , (12 - {period_number}) / 12) )"
+		"supplier": "_Test Supplier",
+		"name": "_Test Supplier",
+		"weighting_function": "{total_score} * max( 0, min ( 1 , (12 - {period_number}) / 12) )",
 	}
 ]
diff --git a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.py b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.py
index 7cd18c3..ab7d487 100644
--- a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.py
+++ b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.py
@@ -9,7 +9,9 @@
 from frappe.model.document import Document
 
 
-class InvalidFormulaVariable(frappe.ValidationError): pass
+class InvalidFormulaVariable(frappe.ValidationError):
+	pass
+
 
 class SupplierScorecardCriteria(Document):
 	def validate(self):
@@ -29,28 +31,34 @@
 		mylist = re.finditer(regex, test_formula, re.MULTILINE | re.DOTALL)
 		for dummy1, match in enumerate(mylist):
 			for dummy2 in range(0, len(match.groups())):
-				test_formula = test_formula.replace('{' + match.group(1) + '}', "0")
+				test_formula = test_formula.replace("{" + match.group(1) + "}", "0")
 
 		try:
-			frappe.safe_eval(test_formula,  None, {'max':max, 'min': min})
+			frappe.safe_eval(test_formula, None, {"max": max, "min": min})
 		except Exception:
 			frappe.throw(_("Error evaluating the criteria formula"))
 
+
 @frappe.whitelist()
 def get_criteria_list():
-	criteria = frappe.db.sql("""
+	criteria = frappe.db.sql(
+		"""
 		SELECT
 			scs.name
 		FROM
 			`tabSupplier Scorecard Criteria` scs""",
-			{}, as_dict=1)
+		{},
+		as_dict=1,
+	)
 
 	return criteria
 
+
 def get_variables(criteria_name):
 	criteria = frappe.get_doc("Supplier Scorecard Criteria", criteria_name)
 	return _get_variables(criteria)
 
+
 def _get_variables(criteria):
 	my_variables = []
 	regex = r"\{(.*?)\}"
@@ -59,16 +67,19 @@
 	for dummy1, match in enumerate(mylist):
 		for dummy2 in range(0, len(match.groups())):
 			try:
-				var = frappe.db.sql("""
+				var = frappe.db.sql(
+					"""
 					SELECT
 						scv.variable_label, scv.description, scv.param_name, scv.path
 					FROM
 						`tabSupplier Scorecard Variable` scv
 					WHERE
 						param_name=%(param)s""",
-						{'param':match.group(1)}, as_dict=1)[0]
+					{"param": match.group(1)},
+					as_dict=1,
+				)[0]
 				my_variables.append(var)
 			except Exception:
-				frappe.throw(_('Unable to find variable: ') + str(match.group(1)), InvalidFormulaVariable)
+				frappe.throw(_("Unable to find variable:") + " " + str(match.group(1)), InvalidFormulaVariable)
 
 	return my_variables
diff --git a/erpnext/buying/doctype/supplier_scorecard_criteria/test_supplier_scorecard_criteria.py b/erpnext/buying/doctype/supplier_scorecard_criteria/test_supplier_scorecard_criteria.py
index 7ff84c1..90468d6 100644
--- a/erpnext/buying/doctype/supplier_scorecard_criteria/test_supplier_scorecard_criteria.py
+++ b/erpnext/buying/doctype/supplier_scorecard_criteria/test_supplier_scorecard_criteria.py
@@ -12,19 +12,26 @@
 		for d in test_good_criteria:
 			frappe.get_doc(d).insert()
 
-		self.assertRaises(frappe.ValidationError,frappe.get_doc(test_bad_criteria[0]).insert)
+		self.assertRaises(frappe.ValidationError, frappe.get_doc(test_bad_criteria[0]).insert)
 
 	def test_formula_validate(self):
 		delete_test_scorecards()
-		self.assertRaises(frappe.ValidationError,frappe.get_doc(test_bad_criteria[1]).insert)
-		self.assertRaises(frappe.ValidationError,frappe.get_doc(test_bad_criteria[2]).insert)
+		self.assertRaises(frappe.ValidationError, frappe.get_doc(test_bad_criteria[1]).insert)
+		self.assertRaises(frappe.ValidationError, frappe.get_doc(test_bad_criteria[2]).insert)
+
 
 def delete_test_scorecards():
 	# Delete all the periods so we can delete all the criteria
 	frappe.db.sql("""delete from `tabSupplier Scorecard Period`""")
-	frappe.db.sql("""delete from `tabSupplier Scorecard Scoring Criteria` where parenttype = 'Supplier Scorecard Period'""")
-	frappe.db.sql("""delete from `tabSupplier Scorecard Scoring Standing` where parenttype = 'Supplier Scorecard Period'""")
-	frappe.db.sql("""delete from `tabSupplier Scorecard Scoring Variable` where parenttype = 'Supplier Scorecard Period'""")
+	frappe.db.sql(
+		"""delete from `tabSupplier Scorecard Scoring Criteria` where parenttype = 'Supplier Scorecard Period'"""
+	)
+	frappe.db.sql(
+		"""delete from `tabSupplier Scorecard Scoring Standing` where parenttype = 'Supplier Scorecard Period'"""
+	)
+	frappe.db.sql(
+		"""delete from `tabSupplier Scorecard Scoring Variable` where parenttype = 'Supplier Scorecard Period'"""
+	)
 
 	for d in test_good_criteria:
 		if frappe.db.exists("Supplier Scorecard Criteria", d.get("name")):
@@ -36,40 +43,41 @@
 			# Delete all the periods, then delete the scorecard
 			frappe.delete_doc(d.get("doctype"), d.get("name"))
 
+
 test_good_criteria = [
 	{
-		"name":"Delivery",
-		"weight":40.0,
-		"doctype":"Supplier Scorecard Criteria",
-		"formula":"(({cost_of_on_time_shipments} / {tot_cost_shipments}) if {tot_cost_shipments} > 0 else 1 )* 100",
-		"criteria_name":"Delivery",
-		"max_score":100.0
+		"name": "Delivery",
+		"weight": 40.0,
+		"doctype": "Supplier Scorecard Criteria",
+		"formula": "(({cost_of_on_time_shipments} / {tot_cost_shipments}) if {tot_cost_shipments} > 0 else 1 )* 100",
+		"criteria_name": "Delivery",
+		"max_score": 100.0,
 	},
 ]
 
 test_bad_criteria = [
 	{
-		"name":"Fake Criteria 1",
-		"weight":40.0,
-		"doctype":"Supplier Scorecard Criteria",
-		"formula":"(({fake_variable} / {tot_cost_shipments}) if {tot_cost_shipments} > 0 else 1 )* 100", # Invalid variable name
-		"criteria_name":"Fake Criteria 1",
-		"max_score":100.0
+		"name": "Fake Criteria 1",
+		"weight": 40.0,
+		"doctype": "Supplier Scorecard Criteria",
+		"formula": "(({fake_variable} / {tot_cost_shipments}) if {tot_cost_shipments} > 0 else 1 )* 100",  # Invalid variable name
+		"criteria_name": "Fake Criteria 1",
+		"max_score": 100.0,
 	},
 	{
-		"name":"Fake Criteria 2",
-		"weight":40.0,
-		"doctype":"Supplier Scorecard Criteria",
-		"formula":"(({cost_of_on_time_shipments} / {tot_cost_shipments}))* 100", # Force 0 divided by 0
-		"criteria_name":"Fake Criteria 2",
-		"max_score":100.0
+		"name": "Fake Criteria 2",
+		"weight": 40.0,
+		"doctype": "Supplier Scorecard Criteria",
+		"formula": "(({cost_of_on_time_shipments} / {tot_cost_shipments}))* 100",  # Force 0 divided by 0
+		"criteria_name": "Fake Criteria 2",
+		"max_score": 100.0,
 	},
 	{
-		"name":"Fake Criteria 3",
-		"weight":40.0,
-		"doctype":"Supplier Scorecard Criteria",
-		"formula":"(({cost_of_on_time_shipments} {cost_of_on_time_shipments} / {tot_cost_shipments}))* 100", # Two variables beside eachother
-		"criteria_name":"Fake Criteria 3",
-		"max_score":100.0
+		"name": "Fake Criteria 3",
+		"weight": 40.0,
+		"doctype": "Supplier Scorecard Criteria",
+		"formula": "(({cost_of_on_time_shipments} {cost_of_on_time_shipments} / {tot_cost_shipments}))* 100",  # Two variables beside eachother
+		"criteria_name": "Fake Criteria 3",
+		"max_score": 100.0,
 	},
 ]
diff --git a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py
index c247241..a8b76db 100644
--- a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py
+++ b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py
@@ -14,7 +14,6 @@
 
 
 class SupplierScorecardPeriod(Document):
-
 	def validate(self):
 		self.validate_criteria_weights()
 		self.calculate_variables()
@@ -28,69 +27,84 @@
 			weight += c.weight
 
 		if weight != 100:
-			throw(_('Criteria weights must add up to 100%'))
+			throw(_("Criteria weights must add up to 100%"))
 
 	def calculate_variables(self):
 		for var in self.variables:
-			if '.' in var.path:
+			if "." in var.path:
 				method_to_call = import_string_path(var.path)
 				var.value = method_to_call(self)
 			else:
 				method_to_call = getattr(variable_functions, var.path)
 				var.value = method_to_call(self)
 
-
-
 	def calculate_criteria(self):
 		for crit in self.criteria:
 			try:
-				crit.score = min(crit.max_score, max( 0 ,frappe.safe_eval(self.get_eval_statement(crit.formula),  None, {'max':max, 'min': min})))
+				crit.score = min(
+					crit.max_score,
+					max(
+						0, frappe.safe_eval(self.get_eval_statement(crit.formula), None, {"max": max, "min": min})
+					),
+				)
 			except Exception:
-				frappe.throw(_("Could not solve criteria score function for {0}. Make sure the formula is valid.").format(crit.criteria_name),frappe.ValidationError)
+				frappe.throw(
+					_("Could not solve criteria score function for {0}. Make sure the formula is valid.").format(
+						crit.criteria_name
+					),
+					frappe.ValidationError,
+				)
 				crit.score = 0
 
 	def calculate_score(self):
 		myscore = 0
 		for crit in self.criteria:
-			myscore += crit.score * crit.weight/100.0
+			myscore += crit.score * crit.weight / 100.0
 		self.total_score = myscore
 
 	def calculate_weighted_score(self, weighing_function):
 		try:
-			weighed_score = frappe.safe_eval(self.get_eval_statement(weighing_function),  None, {'max':max, 'min': min})
+			weighed_score = frappe.safe_eval(
+				self.get_eval_statement(weighing_function), None, {"max": max, "min": min}
+			)
 		except Exception:
-			frappe.throw(_("Could not solve weighted score function. Make sure the formula is valid."),frappe.ValidationError)
+			frappe.throw(
+				_("Could not solve weighted score function. Make sure the formula is valid."),
+				frappe.ValidationError,
+			)
 			weighed_score = 0
 		return weighed_score
 
-
 	def get_eval_statement(self, formula):
 		my_eval_statement = formula.replace("\r", "").replace("\n", "")
 
 		for var in self.variables:
-				if var.value:
-					if var.param_name in my_eval_statement:
-						my_eval_statement = my_eval_statement.replace('{' + var.param_name + '}', "{:.2f}".format(var.value))
-				else:
-					if var.param_name in my_eval_statement:
-						my_eval_statement = my_eval_statement.replace('{' + var.param_name + '}', '0.0')
+			if var.value:
+				if var.param_name in my_eval_statement:
+					my_eval_statement = my_eval_statement.replace(
+						"{" + var.param_name + "}", "{:.2f}".format(var.value)
+					)
+			else:
+				if var.param_name in my_eval_statement:
+					my_eval_statement = my_eval_statement.replace("{" + var.param_name + "}", "0.0")
 
 		return my_eval_statement
 
 
 def import_string_path(path):
-    components = path.split('.')
-    mod = __import__(components[0])
-    for comp in components[1:]:
-        mod = getattr(mod, comp)
-    return mod
+	components = path.split(".")
+	mod = __import__(components[0])
+	for comp in components[1:]:
+		mod = getattr(mod, comp)
+	return mod
 
 
 @frappe.whitelist()
 def make_supplier_scorecard(source_name, target_doc=None):
 	def update_criteria_fields(obj, target, source_parent):
-		target.max_score, target.formula = frappe.db.get_value('Supplier Scorecard Criteria',
-			obj.criteria_name, ['max_score', 'formula'])
+		target.max_score, target.formula = frappe.db.get_value(
+			"Supplier Scorecard Criteria", obj.criteria_name, ["max_score", "formula"]
+		)
 
 	def post_process(source, target):
 		variables = []
@@ -99,16 +113,21 @@
 				if var not in variables:
 					variables.append(var)
 
-		target.extend('variables', variables)
+		target.extend("variables", variables)
 
-	doc = get_mapped_doc("Supplier Scorecard", source_name,	{
-		"Supplier Scorecard": {
-			"doctype": "Supplier Scorecard Period"
+	doc = get_mapped_doc(
+		"Supplier Scorecard",
+		source_name,
+		{
+			"Supplier Scorecard": {"doctype": "Supplier Scorecard Period"},
+			"Supplier Scorecard Scoring Criteria": {
+				"doctype": "Supplier Scorecard Scoring Criteria",
+				"postprocess": update_criteria_fields,
+			},
 		},
-		"Supplier Scorecard Scoring Criteria": {
-			"doctype": "Supplier Scorecard Scoring Criteria",
-			"postprocess": update_criteria_fields,
-		}
-	}, target_doc, post_process, ignore_permissions=True)
+		target_doc,
+		post_process,
+		ignore_permissions=True,
+	)
 
 	return doc
diff --git a/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.py b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.py
index 11ebe6d..929e8a3 100644
--- a/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.py
+++ b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.py
@@ -19,11 +19,14 @@
 
 @frappe.whitelist()
 def get_standings_list():
-	standings = frappe.db.sql("""
+	standings = frappe.db.sql(
+		"""
 		SELECT
 			scs.name
 		FROM
 			`tabSupplier Scorecard Standing` scs""",
-			{}, as_dict=1)
+		{},
+		as_dict=1,
+	)
 
 	return standings
diff --git a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py
index 217aadba6..fb8819e 100644
--- a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py
+++ b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py
@@ -10,18 +10,21 @@
 from frappe.utils import getdate
 
 
-class VariablePathNotFound(frappe.ValidationError): pass
+class VariablePathNotFound(frappe.ValidationError):
+	pass
+
 
 class SupplierScorecardVariable(Document):
 	def validate(self):
 		self.validate_path_exists()
 
 	def validate_path_exists(self):
-		if '.' in self.path:
+		if "." in self.path:
 			try:
 				from erpnext.buying.doctype.supplier_scorecard_period.supplier_scorecard_period import (
 					import_string_path,
 				)
+
 				import_string_path(self.path)
 			except AttributeError:
 				frappe.throw(_("Could not find path for " + self.path), VariablePathNotFound)
@@ -30,15 +33,18 @@
 			if not hasattr(sys.modules[__name__], self.path):
 				frappe.throw(_("Could not find path for " + self.path), VariablePathNotFound)
 
+
 def get_total_workdays(scorecard):
-	""" Gets the number of days in this period"""
+	"""Gets the number of days in this period"""
 	delta = getdate(scorecard.end_date) - getdate(scorecard.start_date)
 	return delta.days
 
+
 def get_item_workdays(scorecard):
-	""" Gets the number of days in this period"""
-	supplier = frappe.get_doc('Supplier', scorecard.supplier)
-	total_item_days = frappe.db.sql("""
+	"""Gets the number of days in this period"""
+	supplier = frappe.get_doc("Supplier", scorecard.supplier)
+	total_item_days = frappe.db.sql(
+		"""
 			SELECT
 				SUM(DATEDIFF( %(end_date)s, po_item.schedule_date) * (po_item.qty))
 			FROM
@@ -49,20 +55,22 @@
 				AND po_item.received_qty < po_item.qty
 				AND po_item.schedule_date BETWEEN %(start_date)s AND %(end_date)s
 				AND po_item.parent = po.name""",
-				{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+		{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date},
+		as_dict=0,
+	)[0][0]
 
 	if not total_item_days:
 		total_item_days = 0
 	return total_item_days
 
 
-
 def get_total_cost_of_shipments(scorecard):
-	""" Gets the total cost of all shipments in the period (based on Purchase Orders)"""
-	supplier = frappe.get_doc('Supplier', scorecard.supplier)
+	"""Gets the total cost of all shipments in the period (based on Purchase Orders)"""
+	supplier = frappe.get_doc("Supplier", scorecard.supplier)
 
 	# Look up all PO Items with delivery dates between our dates
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 			SELECT
 				SUM(po_item.base_amount)
 			FROM
@@ -73,24 +81,29 @@
 				AND po_item.schedule_date BETWEEN %(start_date)s AND %(end_date)s
 				AND po_item.docstatus = 1
 				AND po_item.parent = po.name""",
-				{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+		{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date},
+		as_dict=0,
+	)[0][0]
 
 	if data:
 		return data
 	else:
 		return 0
 
+
 def get_cost_of_delayed_shipments(scorecard):
-	""" Gets the total cost of all delayed shipments in the period (based on Purchase Receipts - POs)"""
+	"""Gets the total cost of all delayed shipments in the period (based on Purchase Receipts - POs)"""
 	return get_total_cost_of_shipments(scorecard) - get_cost_of_on_time_shipments(scorecard)
 
+
 def get_cost_of_on_time_shipments(scorecard):
-	""" Gets the total cost of all on_time shipments in the period (based on Purchase Receipts)"""
-	supplier = frappe.get_doc('Supplier', scorecard.supplier)
+	"""Gets the total cost of all on_time shipments in the period (based on Purchase Receipts)"""
+	supplier = frappe.get_doc("Supplier", scorecard.supplier)
 
 	# Look up all PO Items with delivery dates between our dates
 
-	total_delivered_on_time_costs = frappe.db.sql("""
+	total_delivered_on_time_costs = frappe.db.sql(
+		"""
 			SELECT
 				SUM(pr_item.base_amount)
 			FROM
@@ -106,7 +119,9 @@
 				AND pr_item.purchase_order_item = po_item.name
 				AND po_item.parent = po.name
 				AND pr_item.parent = pr.name""",
-				{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+		{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date},
+		as_dict=0,
+	)[0][0]
 
 	if total_delivered_on_time_costs:
 		return total_delivered_on_time_costs
@@ -115,9 +130,10 @@
 
 
 def get_total_days_late(scorecard):
-	""" Gets the number of item days late in the period (based on Purchase Receipts vs POs)"""
-	supplier = frappe.get_doc('Supplier', scorecard.supplier)
-	total_delivered_late_days = frappe.db.sql("""
+	"""Gets the number of item days late in the period (based on Purchase Receipts vs POs)"""
+	supplier = frappe.get_doc("Supplier", scorecard.supplier)
+	total_delivered_late_days = frappe.db.sql(
+		"""
 			SELECT
 				SUM(DATEDIFF(pr.posting_date,po_item.schedule_date)* pr_item.qty)
 			FROM
@@ -133,11 +149,14 @@
 				AND pr_item.purchase_order_item = po_item.name
 				AND po_item.parent = po.name
 				AND pr_item.parent = pr.name""",
-				{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+		{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date},
+		as_dict=0,
+	)[0][0]
 	if not total_delivered_late_days:
 		total_delivered_late_days = 0
 
-	total_missed_late_days = frappe.db.sql("""
+	total_missed_late_days = frappe.db.sql(
+		"""
 			SELECT
 				SUM(DATEDIFF( %(end_date)s, po_item.schedule_date) * (po_item.qty - po_item.received_qty))
 			FROM
@@ -148,19 +167,23 @@
 				AND po_item.received_qty < po_item.qty
 				AND po_item.schedule_date BETWEEN %(start_date)s AND %(end_date)s
 				AND po_item.parent = po.name""",
-				{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+		{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date},
+		as_dict=0,
+	)[0][0]
 
 	if not total_missed_late_days:
 		total_missed_late_days = 0
 	return total_missed_late_days + total_delivered_late_days
 
-def get_on_time_shipments(scorecard):
-	""" Gets the number of late shipments (counting each item) in the period (based on Purchase Receipts vs POs)"""
 
-	supplier = frappe.get_doc('Supplier', scorecard.supplier)
+def get_on_time_shipments(scorecard):
+	"""Gets the number of late shipments (counting each item) in the period (based on Purchase Receipts vs POs)"""
+
+	supplier = frappe.get_doc("Supplier", scorecard.supplier)
 
 	# Look up all PO Items with delivery dates between our dates
-	total_items_delivered_on_time = frappe.db.sql("""
+	total_items_delivered_on_time = frappe.db.sql(
+		"""
 			SELECT
 				COUNT(pr_item.qty)
 			FROM
@@ -177,22 +200,27 @@
 				AND pr_item.purchase_order_item = po_item.name
 				AND po_item.parent = po.name
 				AND pr_item.parent = pr.name""",
-				{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+		{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date},
+		as_dict=0,
+	)[0][0]
 
 	if not total_items_delivered_on_time:
 		total_items_delivered_on_time = 0
 	return total_items_delivered_on_time
 
+
 def get_late_shipments(scorecard):
-	""" Gets the number of late shipments (counting each item) in the period (based on Purchase Receipts vs POs)"""
+	"""Gets the number of late shipments (counting each item) in the period (based on Purchase Receipts vs POs)"""
 	return get_total_shipments(scorecard) - get_on_time_shipments(scorecard)
 
+
 def get_total_received(scorecard):
-	""" Gets the total number of received shipments in the period (based on Purchase Receipts)"""
-	supplier = frappe.get_doc('Supplier', scorecard.supplier)
+	"""Gets the total number of received shipments in the period (based on Purchase Receipts)"""
+	supplier = frappe.get_doc("Supplier", scorecard.supplier)
 
 	# Look up all PO Items with delivery dates between our dates
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 			SELECT
 				COUNT(pr_item.base_amount)
 			FROM
@@ -203,18 +231,22 @@
 				AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s
 				AND pr_item.docstatus = 1
 				AND pr_item.parent = pr.name""",
-				{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+		{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date},
+		as_dict=0,
+	)[0][0]
 
 	if not data:
 		data = 0
 	return data
 
+
 def get_total_received_amount(scorecard):
-	""" Gets the total amount (in company currency) received in the period (based on Purchase Receipts)"""
-	supplier = frappe.get_doc('Supplier', scorecard.supplier)
+	"""Gets the total amount (in company currency) received in the period (based on Purchase Receipts)"""
+	supplier = frappe.get_doc("Supplier", scorecard.supplier)
 
 	# Look up all PO Items with delivery dates between our dates
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 			SELECT
 				SUM(pr_item.received_qty * pr_item.base_rate)
 			FROM
@@ -225,18 +257,22 @@
 				AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s
 				AND pr_item.docstatus = 1
 				AND pr_item.parent = pr.name""",
-				{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+		{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date},
+		as_dict=0,
+	)[0][0]
 
 	if not data:
 		data = 0
 	return data
 
+
 def get_total_received_items(scorecard):
-	""" Gets the total number of received shipments in the period (based on Purchase Receipts)"""
-	supplier = frappe.get_doc('Supplier', scorecard.supplier)
+	"""Gets the total number of received shipments in the period (based on Purchase Receipts)"""
+	supplier = frappe.get_doc("Supplier", scorecard.supplier)
 
 	# Look up all PO Items with delivery dates between our dates
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 			SELECT
 				SUM(pr_item.received_qty)
 			FROM
@@ -247,18 +283,22 @@
 				AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s
 				AND pr_item.docstatus = 1
 				AND pr_item.parent = pr.name""",
-				{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+		{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date},
+		as_dict=0,
+	)[0][0]
 
 	if not data:
 		data = 0
 	return data
 
+
 def get_total_rejected_amount(scorecard):
-	""" Gets the total amount (in company currency) rejected in the period (based on Purchase Receipts)"""
-	supplier = frappe.get_doc('Supplier', scorecard.supplier)
+	"""Gets the total amount (in company currency) rejected in the period (based on Purchase Receipts)"""
+	supplier = frappe.get_doc("Supplier", scorecard.supplier)
 
 	# Look up all PO Items with delivery dates between our dates
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 			SELECT
 				SUM(pr_item.rejected_qty * pr_item.base_rate)
 			FROM
@@ -269,18 +309,22 @@
 				AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s
 				AND pr_item.docstatus = 1
 				AND pr_item.parent = pr.name""",
-				{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+		{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date},
+		as_dict=0,
+	)[0][0]
 
 	if not data:
 		data = 0
 	return data
 
+
 def get_total_rejected_items(scorecard):
-	""" Gets the total number of rejected items in the period (based on Purchase Receipts)"""
-	supplier = frappe.get_doc('Supplier', scorecard.supplier)
+	"""Gets the total number of rejected items in the period (based on Purchase Receipts)"""
+	supplier = frappe.get_doc("Supplier", scorecard.supplier)
 
 	# Look up all PO Items with delivery dates between our dates
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 			SELECT
 				SUM(pr_item.rejected_qty)
 			FROM
@@ -291,18 +335,22 @@
 				AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s
 				AND pr_item.docstatus = 1
 				AND pr_item.parent = pr.name""",
-				{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+		{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date},
+		as_dict=0,
+	)[0][0]
 
 	if not data:
 		data = 0
 	return data
 
+
 def get_total_accepted_amount(scorecard):
-	""" Gets the total amount (in company currency) accepted in the period (based on Purchase Receipts)"""
-	supplier = frappe.get_doc('Supplier', scorecard.supplier)
+	"""Gets the total amount (in company currency) accepted in the period (based on Purchase Receipts)"""
+	supplier = frappe.get_doc("Supplier", scorecard.supplier)
 
 	# Look up all PO Items with delivery dates between our dates
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 			SELECT
 				SUM(pr_item.qty * pr_item.base_rate)
 			FROM
@@ -313,18 +361,22 @@
 				AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s
 				AND pr_item.docstatus = 1
 				AND pr_item.parent = pr.name""",
-				{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+		{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date},
+		as_dict=0,
+	)[0][0]
 
 	if not data:
 		data = 0
 	return data
 
+
 def get_total_accepted_items(scorecard):
-	""" Gets the total number of rejected items in the period (based on Purchase Receipts)"""
-	supplier = frappe.get_doc('Supplier', scorecard.supplier)
+	"""Gets the total number of rejected items in the period (based on Purchase Receipts)"""
+	supplier = frappe.get_doc("Supplier", scorecard.supplier)
 
 	# Look up all PO Items with delivery dates between our dates
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 			SELECT
 				SUM(pr_item.qty)
 			FROM
@@ -335,18 +387,22 @@
 				AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s
 				AND pr_item.docstatus = 1
 				AND pr_item.parent = pr.name""",
-				{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+		{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date},
+		as_dict=0,
+	)[0][0]
 
 	if not data:
 		data = 0
 	return data
 
+
 def get_total_shipments(scorecard):
-	""" Gets the total number of ordered shipments to arrive in the period (based on Purchase Receipts)"""
-	supplier = frappe.get_doc('Supplier', scorecard.supplier)
+	"""Gets the total number of ordered shipments to arrive in the period (based on Purchase Receipts)"""
+	supplier = frappe.get_doc("Supplier", scorecard.supplier)
 
 	# Look up all PO Items with delivery dates between our dates
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 			SELECT
 				COUNT(po_item.base_amount)
 			FROM
@@ -357,18 +413,22 @@
 				AND po_item.schedule_date BETWEEN %(start_date)s AND %(end_date)s
 				AND po_item.docstatus = 1
 				AND po_item.parent = po.name""",
-				{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+		{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date},
+		as_dict=0,
+	)[0][0]
 
 	if not data:
 		data = 0
 	return data
 
+
 def get_rfq_total_number(scorecard):
-	""" Gets the total number of RFQs sent to supplier"""
-	supplier = frappe.get_doc('Supplier', scorecard.supplier)
+	"""Gets the total number of RFQs sent to supplier"""
+	supplier = frappe.get_doc("Supplier", scorecard.supplier)
 
 	# Look up all PO Items with delivery dates between our dates
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 			SELECT
 				COUNT(rfq.name) as total_rfqs
 			FROM
@@ -381,18 +441,22 @@
 				AND rfq_item.docstatus = 1
 				AND rfq_item.parent = rfq.name
 				AND rfq_sup.parent = rfq.name""",
-				{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+		{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date},
+		as_dict=0,
+	)[0][0]
 
 	if not data:
 		data = 0
 	return data
 
+
 def get_rfq_total_items(scorecard):
-	""" Gets the total number of RFQ items sent to supplier"""
-	supplier = frappe.get_doc('Supplier', scorecard.supplier)
+	"""Gets the total number of RFQ items sent to supplier"""
+	supplier = frappe.get_doc("Supplier", scorecard.supplier)
 
 	# Look up all PO Items with delivery dates between our dates
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 			SELECT
 				COUNT(rfq_item.name) as total_rfqs
 			FROM
@@ -405,18 +469,21 @@
 				AND rfq_item.docstatus = 1
 				AND rfq_item.parent = rfq.name
 				AND rfq_sup.parent = rfq.name""",
-				{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+		{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date},
+		as_dict=0,
+	)[0][0]
 	if not data:
 		data = 0
 	return data
 
 
 def get_sq_total_number(scorecard):
-	""" Gets the total number of RFQ items sent to supplier"""
-	supplier = frappe.get_doc('Supplier', scorecard.supplier)
+	"""Gets the total number of RFQ items sent to supplier"""
+	supplier = frappe.get_doc("Supplier", scorecard.supplier)
 
 	# Look up all PO Items with delivery dates between our dates
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 			SELECT
 				COUNT(sq.name) as total_sqs
 			FROM
@@ -435,17 +502,21 @@
 				AND sq_item.parent = sq.name
 				AND rfq_item.parent = rfq.name
 				AND rfq_sup.parent = rfq.name""",
-				{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+		{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date},
+		as_dict=0,
+	)[0][0]
 	if not data:
 		data = 0
 	return data
 
+
 def get_sq_total_items(scorecard):
-	""" Gets the total number of RFQ items sent to supplier"""
-	supplier = frappe.get_doc('Supplier', scorecard.supplier)
+	"""Gets the total number of RFQ items sent to supplier"""
+	supplier = frappe.get_doc("Supplier", scorecard.supplier)
 
 	# Look up all PO Items with delivery dates between our dates
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 			SELECT
 				COUNT(sq_item.name) as total_sqs
 			FROM
@@ -464,15 +535,19 @@
 				AND rfq_item.docstatus = 1
 				AND rfq_item.parent = rfq.name
 				AND rfq_sup.parent = rfq.name""",
-				{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+		{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date},
+		as_dict=0,
+	)[0][0]
 	if not data:
 		data = 0
 	return data
 
+
 def get_rfq_response_days(scorecard):
-	""" Gets the total number of days it has taken a supplier to respond to rfqs in the period"""
-	supplier = frappe.get_doc('Supplier', scorecard.supplier)
-	total_sq_days = frappe.db.sql("""
+	"""Gets the total number of days it has taken a supplier to respond to rfqs in the period"""
+	supplier = frappe.get_doc("Supplier", scorecard.supplier)
+	total_sq_days = frappe.db.sql(
+		"""
 			SELECT
 				SUM(DATEDIFF(sq.transaction_date, rfq.transaction_date))
 			FROM
@@ -491,9 +566,10 @@
 				AND rfq_item.docstatus = 1
 				AND rfq_item.parent = rfq.name
 				AND rfq_sup.parent = rfq.name""",
-				{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+		{"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date},
+		as_dict=0,
+	)[0][0]
 	if not total_sq_days:
 		total_sq_days = 0
 
-
 	return total_sq_days
diff --git a/erpnext/buying/doctype/supplier_scorecard_variable/test_supplier_scorecard_variable.py b/erpnext/buying/doctype/supplier_scorecard_variable/test_supplier_scorecard_variable.py
index 32005a3..60d8464 100644
--- a/erpnext/buying/doctype/supplier_scorecard_variable/test_supplier_scorecard_variable.py
+++ b/erpnext/buying/doctype/supplier_scorecard_variable/test_supplier_scorecard_variable.py
@@ -14,9 +14,9 @@
 	def test_variable_exist(self):
 		for d in test_existing_variables:
 			my_doc = frappe.get_doc("Supplier Scorecard Variable", d.get("name"))
-			self.assertEqual(my_doc.param_name, d.get('param_name'))
-			self.assertEqual(my_doc.variable_label, d.get('variable_label'))
-			self.assertEqual(my_doc.path, d.get('path'))
+			self.assertEqual(my_doc.param_name, d.get("param_name"))
+			self.assertEqual(my_doc.variable_label, d.get("variable_label"))
+			self.assertEqual(my_doc.path, d.get("path"))
 
 	def test_path_exists(self):
 		for d in test_good_variables:
@@ -25,34 +25,35 @@
 			frappe.get_doc(d).insert()
 
 		for d in test_bad_variables:
-			self.assertRaises(VariablePathNotFound,frappe.get_doc(d).insert)
+			self.assertRaises(VariablePathNotFound, frappe.get_doc(d).insert)
+
 
 test_existing_variables = [
 	{
-		"param_name":"total_accepted_items",
-		"name":"Total Accepted Items",
-		"doctype":"Supplier Scorecard Variable",
-		"variable_label":"Total Accepted Items",
-		"path":"get_total_accepted_items"
+		"param_name": "total_accepted_items",
+		"name": "Total Accepted Items",
+		"doctype": "Supplier Scorecard Variable",
+		"variable_label": "Total Accepted Items",
+		"path": "get_total_accepted_items",
 	},
 ]
 
 test_good_variables = [
 	{
-		"param_name":"good_variable1",
-		"name":"Good Variable 1",
-		"doctype":"Supplier Scorecard Variable",
-		"variable_label":"Good Variable 1",
-		"path":"get_total_accepted_items"
+		"param_name": "good_variable1",
+		"name": "Good Variable 1",
+		"doctype": "Supplier Scorecard Variable",
+		"variable_label": "Good Variable 1",
+		"path": "get_total_accepted_items",
 	},
 ]
 
 test_bad_variables = [
 	{
-		"param_name":"fake_variable1",
-		"name":"Fake Variable 1",
-		"doctype":"Supplier Scorecard Variable",
-		"variable_label":"Fake Variable 1",
-		"path":"get_fake_variable1"
+		"param_name": "fake_variable1",
+		"name": "Fake Variable 1",
+		"doctype": "Supplier Scorecard Variable",
+		"variable_label": "Fake Variable 1",
+		"path": "get_fake_variable1",
 	},
 ]
diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py
index 295a19d..d70ac46 100644
--- a/erpnext/buying/report/procurement_tracker/procurement_tracker.py
+++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py
@@ -12,152 +12,143 @@
 	data = get_data(filters)
 	return columns, data
 
+
 def get_columns(filters):
 	columns = [
 		{
 			"label": _("Material Request Date"),
 			"fieldname": "material_request_date",
 			"fieldtype": "Date",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Material Request No"),
 			"options": "Material Request",
 			"fieldname": "material_request_no",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Cost Center"),
 			"options": "Cost Center",
 			"fieldname": "cost_center",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Project"),
 			"options": "Project",
 			"fieldname": "project",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Requesting Site"),
 			"options": "Warehouse",
 			"fieldname": "requesting_site",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Requestor"),
 			"options": "Employee",
 			"fieldname": "requestor",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Item"),
 			"fieldname": "item_code",
 			"fieldtype": "Link",
 			"options": "Item",
-			"width": 150
+			"width": 150,
 		},
-		{
-			"label": _("Quantity"),
-			"fieldname": "quantity",
-			"fieldtype": "Float",
-			"width": 140
-		},
+		{"label": _("Quantity"), "fieldname": "quantity", "fieldtype": "Float", "width": 140},
 		{
 			"label": _("Unit of Measure"),
 			"options": "UOM",
 			"fieldname": "unit_of_measurement",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
-		{
-			"label": _("Status"),
-			"fieldname": "status",
-			"fieldtype": "data",
-			"width": 140
-		},
+		{"label": _("Status"), "fieldname": "status", "fieldtype": "data", "width": 140},
 		{
 			"label": _("Purchase Order Date"),
 			"fieldname": "purchase_order_date",
 			"fieldtype": "Date",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Purchase Order"),
 			"options": "Purchase Order",
 			"fieldname": "purchase_order",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Supplier"),
 			"options": "Supplier",
 			"fieldname": "supplier",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Estimated Cost"),
 			"fieldname": "estimated_cost",
 			"fieldtype": "Float",
-			"width": 140
+			"width": 140,
 		},
-		{
-			"label": _("Actual Cost"),
-			"fieldname": "actual_cost",
-			"fieldtype": "Float",
-			"width": 140
-		},
+		{"label": _("Actual Cost"), "fieldname": "actual_cost", "fieldtype": "Float", "width": 140},
 		{
 			"label": _("Purchase Order Amount"),
 			"fieldname": "purchase_order_amt",
 			"fieldtype": "Float",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Purchase Order Amount(Company Currency)"),
 			"fieldname": "purchase_order_amt_in_company_currency",
 			"fieldtype": "Float",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Expected Delivery Date"),
 			"fieldname": "expected_delivery_date",
 			"fieldtype": "Date",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Actual Delivery Date"),
 			"fieldname": "actual_delivery_date",
 			"fieldtype": "Date",
-			"width": 140
+			"width": 140,
 		},
 	]
 	return columns
 
+
 def get_conditions(filters):
 	conditions = ""
 
 	if filters.get("company"):
-		conditions += " AND parent.company=%s" % frappe.db.escape(filters.get('company'))
+		conditions += " AND parent.company=%s" % frappe.db.escape(filters.get("company"))
 
 	if filters.get("cost_center") or filters.get("project"):
 		conditions += """
 			AND (child.`cost_center`=%s OR child.`project`=%s)
-			""" % (frappe.db.escape(filters.get('cost_center')), frappe.db.escape(filters.get('project')))
+			""" % (
+			frappe.db.escape(filters.get("cost_center")),
+			frappe.db.escape(filters.get("project")),
+		)
 
 	if filters.get("from_date"):
-		conditions += " AND parent.transaction_date>='%s'" % filters.get('from_date')
+		conditions += " AND parent.transaction_date>='%s'" % filters.get("from_date")
 
 	if filters.get("to_date"):
-		conditions += " AND parent.transaction_date<='%s'" % filters.get('to_date')
+		conditions += " AND parent.transaction_date<='%s'" % filters.get("to_date")
 	return conditions
 
+
 def get_data(filters):
 	conditions = get_conditions(filters)
 	purchase_order_entry = get_po_entries(conditions)
@@ -165,14 +156,14 @@
 	pr_records = get_mapped_pr_records()
 	pi_records = get_mapped_pi_records()
 
-	procurement_record=[]
+	procurement_record = []
 	if procurement_record_against_mr:
 		procurement_record += procurement_record_against_mr
 	for po in purchase_order_entry:
 		# fetch material records linked to the purchase order item
 		mr_record = mr_records.get(po.material_request_item, [{}])[0]
 		procurement_detail = {
-			"material_request_date": mr_record.get('transaction_date'),
+			"material_request_date": mr_record.get("transaction_date"),
 			"cost_center": po.cost_center,
 			"project": po.project,
 			"requesting_site": po.warehouse,
@@ -185,19 +176,21 @@
 			"purchase_order_date": po.transaction_date,
 			"purchase_order": po.parent,
 			"supplier": po.supplier,
-			"estimated_cost": flt(mr_record.get('amount')),
+			"estimated_cost": flt(mr_record.get("amount")),
 			"actual_cost": flt(pi_records.get(po.name)),
 			"purchase_order_amt": flt(po.amount),
 			"purchase_order_amt_in_company_currency": flt(po.base_amount),
 			"expected_delivery_date": po.schedule_date,
-			"actual_delivery_date": pr_records.get(po.name)
+			"actual_delivery_date": pr_records.get(po.name),
 		}
 		procurement_record.append(procurement_detail)
 	return procurement_record
 
+
 def get_mapped_mr_details(conditions):
 	mr_records = {}
-	mr_details = frappe.db.sql("""
+	mr_details = frappe.db.sql(
+		"""
 		SELECT
 			parent.transaction_date,
 			parent.per_ordered,
@@ -217,7 +210,11 @@
 			AND parent.name=child.parent
 			AND parent.docstatus=1
 			{conditions}
-		""".format(conditions=conditions), as_dict=1) #nosec
+		""".format(
+			conditions=conditions
+		),
+		as_dict=1,
+	)  # nosec
 
 	procurement_record_against_mr = []
 	for record in mr_details:
@@ -236,14 +233,17 @@
 				actual_cost=0,
 				purchase_order_amt=0,
 				purchase_order_amt_in_company_currency=0,
-				project = record.project,
-				cost_center = record.cost_center
+				project=record.project,
+				cost_center=record.cost_center,
 			)
 			procurement_record_against_mr.append(procurement_record_details)
 	return mr_records, procurement_record_against_mr
 
+
 def get_mapped_pi_records():
-	return frappe._dict(frappe.db.sql("""
+	return frappe._dict(
+		frappe.db.sql(
+			"""
 		SELECT
 			pi_item.po_detail,
 			pi_item.base_amount
@@ -252,12 +252,17 @@
 		ON pi_item.`purchase_order` = po.`name`
 		WHERE
 			pi_item.docstatus = 1
-			AND po.status not in ("Closed","Completed","Cancelled")
+			AND po.status not in ('Closed','Completed','Cancelled')
 			AND pi_item.po_detail IS NOT NULL
-		"""))
+		"""
+		)
+	)
+
 
 def get_mapped_pr_records():
-	return frappe._dict(frappe.db.sql("""
+	return frappe._dict(
+		frappe.db.sql(
+			"""
 		SELECT
 			pr_item.purchase_order_item,
 			pr.posting_date
@@ -266,11 +271,15 @@
 			pr.docstatus=1
 			AND pr.name=pr_item.parent
 			AND pr_item.purchase_order_item IS NOT NULL
-			AND pr.status not in  ("Closed","Completed","Cancelled")
-		"""))
+			AND pr.status not in  ('Closed','Completed','Cancelled')
+		"""
+		)
+	)
+
 
 def get_po_entries(conditions):
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		SELECT
 			child.name,
 			child.parent,
@@ -293,8 +302,12 @@
 		WHERE
 			parent.docstatus = 1
 			AND parent.name = child.parent
-			AND parent.status not in  ("Closed","Completed","Cancelled")
+			AND parent.status not in  ('Closed','Completed','Cancelled')
 			{conditions}
 		GROUP BY
 			parent.name, child.item_code
-		""".format(conditions=conditions), as_dict=1) #nosec
+		""".format(
+			conditions=conditions
+		),
+		as_dict=1,
+	)  # nosec
diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
index 4452452..47a66ad 100644
--- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
+++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
@@ -16,27 +16,28 @@
 
 class TestProcurementTracker(FrappeTestCase):
 	def test_result_for_procurement_tracker(self):
-		filters = {
-			'company': '_Test Procurement Company',
-			'cost_center': 'Main - _TPC'
-		}
+		filters = {"company": "_Test Procurement Company", "cost_center": "Main - _TPC"}
 		expected_data = self.generate_expected_data()
 		report = execute(filters)
 
 		length = len(report[1])
-		self.assertEqual(expected_data, report[1][length-1])
+		self.assertEqual(expected_data, report[1][length - 1])
 
 	def generate_expected_data(self):
 		if not frappe.db.exists("Company", "_Test Procurement Company"):
-			frappe.get_doc(dict(
-				doctype="Company",
-				company_name="_Test Procurement Company",
-				abbr="_TPC",
-				default_currency="INR",
-				country="Pakistan"
-				)).insert()
+			frappe.get_doc(
+				dict(
+					doctype="Company",
+					company_name="_Test Procurement Company",
+					abbr="_TPC",
+					default_currency="INR",
+					country="Pakistan",
+				)
+			).insert()
 		warehouse = create_warehouse("_Test Procurement Warehouse", company="_Test Procurement Company")
-		mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse, cost_center="Main - _TPC")
+		mr = make_material_request(
+			company="_Test Procurement Company", warehouse=warehouse, cost_center="Main - _TPC"
+		)
 		po = make_purchase_order(mr.name)
 		po.supplier = "_Test Supplier"
 		po.get("items")[0].cost_center = "Main - _TPC"
@@ -55,7 +56,7 @@
 			"requesting_site": "_Test Procurement Warehouse - _TPC",
 			"requestor": "Administrator",
 			"material_request_no": mr.name,
-			"item_code": '_Test Item',
+			"item_code": "_Test Item",
 			"quantity": 10.0,
 			"unit_of_measurement": "_Test UOM",
 			"status": "To Bill",
@@ -67,7 +68,7 @@
 			"purchase_order_amt": po.net_total,
 			"purchase_order_amt_in_company_currency": po.base_net_total,
 			"expected_delivery_date": date_obj,
-			"actual_delivery_date": date_obj
+			"actual_delivery_date": date_obj,
 		}
 
 		return expected_data
diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
index 9dd9121..a5c4649 100644
--- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
+++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
@@ -27,6 +27,7 @@
 
 	return columns, data, None, chart_data
 
+
 def validate_filters(filters):
 	from_date, to_date = filters.get("from_date"), filters.get("to_date")
 
@@ -35,25 +36,28 @@
 	elif date_diff(to_date, from_date) < 0:
 		frappe.throw(_("To Date cannot be before From Date."))
 
+
 def get_conditions(filters):
 	conditions = ""
 	if filters.get("from_date") and filters.get("to_date"):
 		conditions += " and po.transaction_date between %(from_date)s and %(to_date)s"
 
-	for field in ['company', 'name']:
+	for field in ["company", "name"]:
 		if filters.get(field):
 			conditions += f" and po.{field} = %({field})s"
 
-	if filters.get('status'):
+	if filters.get("status"):
 		conditions += " and po.status in %(status)s"
 
-	if filters.get('project'):
+	if filters.get("project"):
 		conditions += " and poi.project = %(project)s"
 
 	return conditions
 
+
 def get_data(conditions, filters):
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 		SELECT
 			po.transaction_date as date,
 			poi.schedule_date as required_date,
@@ -81,13 +85,19 @@
 			{0}
 		GROUP BY poi.name
 		ORDER BY po.transaction_date ASC
-	""".format(conditions), filters, as_dict=1)
+	""".format(
+			conditions
+		),
+		filters,
+		as_dict=1,
+	)
 
 	return data
 
+
 def prepare_data(data, filters):
 	completed, pending = 0, 0
-	pending_field =  "pending_amount"
+	pending_field = "pending_amount"
 	completed_field = "billed_amount"
 
 	if filters.get("group_by_po"):
@@ -114,8 +124,17 @@
 				po_row["required_date"] = min(getdate(po_row["required_date"]), getdate(row["required_date"]))
 
 				# sum numeric columns
-				fields = ["qty", "received_qty", "pending_qty", "billed_qty", "qty_to_bill", "amount",
-					"received_qty_amount", "billed_amount", "pending_amount"]
+				fields = [
+					"qty",
+					"received_qty",
+					"pending_qty",
+					"billed_qty",
+					"qty_to_bill",
+					"amount",
+					"received_qty_amount",
+					"billed_amount",
+					"pending_amount",
+				]
 				for field in fields:
 					po_row[field] = flt(row[field]) + flt(po_row[field])
 
@@ -129,152 +148,140 @@
 
 	return data, chart_data
 
+
 def prepare_chart_data(pending, completed):
 	labels = ["Amount to Bill", "Billed Amount"]
 
 	return {
-		"data" : {
-			"labels": labels,
-			"datasets": [
-				{"values": [pending, completed]}
-				]
-		},
-		"type": 'donut',
-		"height": 300
+		"data": {"labels": labels, "datasets": [{"values": [pending, completed]}]},
+		"type": "donut",
+		"height": 300,
 	}
 
+
 def get_columns(filters):
 	columns = [
-		{
-			"label":_("Date"),
-			"fieldname": "date",
-			"fieldtype": "Date",
-			"width": 90
-		},
-		{
-			"label":_("Required By"),
-			"fieldname": "required_date",
-			"fieldtype": "Date",
-			"width": 90
-		},
+		{"label": _("Date"), "fieldname": "date", "fieldtype": "Date", "width": 90},
+		{"label": _("Required By"), "fieldname": "required_date", "fieldtype": "Date", "width": 90},
 		{
 			"label": _("Purchase Order"),
 			"fieldname": "purchase_order",
 			"fieldtype": "Link",
 			"options": "Purchase Order",
-			"width": 160
+			"width": 160,
 		},
-		{
-			"label":_("Status"),
-			"fieldname": "status",
-			"fieldtype": "Data",
-			"width": 130
-		},
+		{"label": _("Status"), "fieldname": "status", "fieldtype": "Data", "width": 130},
 		{
 			"label": _("Supplier"),
 			"fieldname": "supplier",
 			"fieldtype": "Link",
 			"options": "Supplier",
-			"width": 130
-		},{
+			"width": 130,
+		},
+		{
 			"label": _("Project"),
 			"fieldname": "project",
 			"fieldtype": "Link",
 			"options": "Project",
-			"width": 130
-		}]
+			"width": 130,
+		},
+	]
 
 	if not filters.get("group_by_po"):
-		columns.append({
-			"label":_("Item Code"),
-			"fieldname": "item_code",
-			"fieldtype": "Link",
-			"options": "Item",
-			"width": 100
-		})
+		columns.append(
+			{
+				"label": _("Item Code"),
+				"fieldname": "item_code",
+				"fieldtype": "Link",
+				"options": "Item",
+				"width": 100,
+			}
+		)
 
-	columns.extend([
-		{
-			"label": _("Qty"),
-			"fieldname": "qty",
-			"fieldtype": "Float",
-			"width": 120,
-			"convertible": "qty"
-		},
-		{
-			"label": _("Received Qty"),
-			"fieldname": "received_qty",
-			"fieldtype": "Float",
-			"width": 120,
-			"convertible": "qty"
-		},
-		{
-			"label": _("Pending Qty"),
-			"fieldname": "pending_qty",
-			"fieldtype": "Float",
-			"width": 80,
-			"convertible": "qty"
-		},
-		{
-			"label": _("Billed Qty"),
-			"fieldname": "billed_qty",
-			"fieldtype": "Float",
-			"width": 80,
-			"convertible": "qty"
-		},
-		{
-			"label": _("Qty to Bill"),
-			"fieldname": "qty_to_bill",
-			"fieldtype": "Float",
-			"width": 80,
-			"convertible": "qty"
-		},
-		{
-			"label": _("Amount"),
-			"fieldname": "amount",
-			"fieldtype": "Currency",
-			"width": 110,
-			"options": "Company:company:default_currency",
-			"convertible": "rate"
-		},
-		{
-			"label": _("Billed Amount"),
-			"fieldname": "billed_amount",
-			"fieldtype": "Currency",
-			"width": 110,
-			"options": "Company:company:default_currency",
-			"convertible": "rate"
-		},
-		{
-			"label": _("Pending Amount"),
-			"fieldname": "pending_amount",
-			"fieldtype": "Currency",
-			"width": 130,
-			"options": "Company:company:default_currency",
-			"convertible": "rate"
-		},
-		{
-			"label": _("Received Qty Amount"),
-			"fieldname": "received_qty_amount",
-			"fieldtype": "Currency",
-			"width": 130,
-			"options": "Company:company:default_currency",
-			"convertible": "rate"
-		},
-		{
-			"label": _("Warehouse"),
-			"fieldname": "warehouse",
-			"fieldtype": "Link",
-			"options": "Warehouse",
-			"width": 100
-		},
-		{
-			"label": _("Company"),
-			"fieldname": "company",
-			"fieldtype": "Link",
-			"options": "Company",
-			"width": 100
-		}
-	])
+	columns.extend(
+		[
+			{
+				"label": _("Qty"),
+				"fieldname": "qty",
+				"fieldtype": "Float",
+				"width": 120,
+				"convertible": "qty",
+			},
+			{
+				"label": _("Received Qty"),
+				"fieldname": "received_qty",
+				"fieldtype": "Float",
+				"width": 120,
+				"convertible": "qty",
+			},
+			{
+				"label": _("Pending Qty"),
+				"fieldname": "pending_qty",
+				"fieldtype": "Float",
+				"width": 80,
+				"convertible": "qty",
+			},
+			{
+				"label": _("Billed Qty"),
+				"fieldname": "billed_qty",
+				"fieldtype": "Float",
+				"width": 80,
+				"convertible": "qty",
+			},
+			{
+				"label": _("Qty to Bill"),
+				"fieldname": "qty_to_bill",
+				"fieldtype": "Float",
+				"width": 80,
+				"convertible": "qty",
+			},
+			{
+				"label": _("Amount"),
+				"fieldname": "amount",
+				"fieldtype": "Currency",
+				"width": 110,
+				"options": "Company:company:default_currency",
+				"convertible": "rate",
+			},
+			{
+				"label": _("Billed Amount"),
+				"fieldname": "billed_amount",
+				"fieldtype": "Currency",
+				"width": 110,
+				"options": "Company:company:default_currency",
+				"convertible": "rate",
+			},
+			{
+				"label": _("Pending Amount"),
+				"fieldname": "pending_amount",
+				"fieldtype": "Currency",
+				"width": 130,
+				"options": "Company:company:default_currency",
+				"convertible": "rate",
+			},
+			{
+				"label": _("Received Qty Amount"),
+				"fieldname": "received_qty_amount",
+				"fieldtype": "Currency",
+				"width": 130,
+				"options": "Company:company:default_currency",
+				"convertible": "rate",
+			},
+			{
+				"label": _("Warehouse"),
+				"fieldname": "warehouse",
+				"fieldtype": "Link",
+				"options": "Warehouse",
+				"width": 100,
+			},
+			{
+				"label": _("Company"),
+				"fieldname": "company",
+				"fieldtype": "Link",
+				"options": "Company",
+				"width": 100,
+			},
+		]
+	)
 
 	return columns
diff --git a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py
index 21643a8..dbdc62e 100644
--- a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py
+++ b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py
@@ -8,7 +8,8 @@
 
 
 def execute(filters=None):
-	if not filters: filters ={}
+	if not filters:
+		filters = {}
 	data = []
 	conditions = get_columns(filters, "Purchase Order")
 	data = get_data(filters, conditions)
@@ -16,6 +17,7 @@
 
 	return conditions["columns"], data, None, chart_data
 
+
 def get_chart_data(data, conditions, filters):
 	if not (data and conditions):
 		return []
@@ -28,32 +30,27 @@
 
 	# fetch only periodic columns as labels
 	columns = conditions.get("columns")[start:-2][1::2]
-	labels = [column.split(':')[0] for column in columns]
+	labels = [column.split(":")[0] for column in columns]
 	datapoints = [0] * len(labels)
 
 	for row in data:
 		# If group by filter, don't add first row of group (it's already summed)
-		if not row[start-1]:
+		if not row[start - 1]:
 			continue
 		# Remove None values and compute only periodic data
 		row = [x if x else 0 for x in row[start:-2]]
-		row  = row[1::2]
+		row = row[1::2]
 
 		for i in range(len(row)):
 			datapoints[i] += row[i]
 
 	return {
-		"data" : {
-			"labels" : labels,
-			"datasets" : [
-				{
-					"name" : _("{0}").format(filters.get("period")) + _(" Purchase Value"),
-					"values" : datapoints
-				}
-			]
+		"data": {
+			"labels": labels,
+			"datasets": [
+				{"name": _(filters.get("period")) + " " + _("Purchase Value"), "values": datapoints}
+			],
 		},
-		"type" : "line",
-		"lineOptions": {
-			"regionFill": 1
-		}
+		"type": "line",
+		"lineOptions": {"regionFill": 1},
 	}
diff --git a/erpnext/buying/report/requested_items_to_order_and_receive/requested_items_to_order_and_receive.py b/erpnext/buying/report/requested_items_to_order_and_receive/requested_items_to_order_and_receive.py
index f98e5f1..21241e0 100644
--- a/erpnext/buying/report/requested_items_to_order_and_receive/requested_items_to_order_and_receive.py
+++ b/erpnext/buying/report/requested_items_to_order_and_receive/requested_items_to_order_and_receive.py
@@ -6,26 +6,25 @@
 
 import frappe
 from frappe import _
+from frappe.query_builder.functions import Coalesce, Sum
 from frappe.utils import date_diff, flt, getdate
 
 
 def execute(filters=None):
 	if not filters:
-		return [],[]
+		return [], []
 
 	validate_filters(filters)
 
 	columns = get_columns(filters)
-	conditions = get_conditions(filters)
+	data = get_data(filters)
 
-	#get queried data
-	data = get_data(filters, conditions)
-
-	#prepare data for report and chart views
+	# prepare data for report and chart views
 	data, chart_data = prepare_data(data, filters)
 
 	return columns, data, None, chart_data
 
+
 def validate_filters(filters):
 	from_date, to_date = filters.get("from_date"), filters.get("to_date")
 
@@ -34,59 +33,74 @@
 	elif date_diff(to_date, from_date) < 0:
 		frappe.throw(_("To Date cannot be before From Date."))
 
-def get_conditions(filters):
-	conditions = ''
 
+def get_data(filters):
+	mr = frappe.qb.DocType("Material Request")
+	mr_item = frappe.qb.DocType("Material Request Item")
+
+	query = (
+		frappe.qb.from_(mr)
+		.join(mr_item)
+		.on(mr_item.parent == mr.name)
+		.select(
+			mr.name.as_("material_request"),
+			mr.transaction_date.as_("date"),
+			mr_item.schedule_date.as_("required_date"),
+			mr_item.item_code.as_("item_code"),
+			Sum(Coalesce(mr_item.stock_qty, 0)).as_("qty"),
+			Coalesce(mr_item.stock_uom, "").as_("uom"),
+			Sum(Coalesce(mr_item.ordered_qty, 0)).as_("ordered_qty"),
+			Sum(Coalesce(mr_item.received_qty, 0)).as_("received_qty"),
+			(Sum(Coalesce(mr_item.stock_qty, 0)) - Sum(Coalesce(mr_item.received_qty, 0))).as_(
+				"qty_to_receive"
+			),
+			Sum(Coalesce(mr_item.received_qty, 0)).as_("received_qty"),
+			(Sum(Coalesce(mr_item.stock_qty, 0)) - Sum(Coalesce(mr_item.ordered_qty, 0))).as_(
+				"qty_to_order"
+			),
+			mr_item.item_name,
+			mr_item.description,
+			mr.company,
+		)
+		.where(
+			(mr.material_request_type == "Purchase")
+			& (mr.docstatus == 1)
+			& (mr.status != "Stopped")
+			& (mr.per_received < 100)
+		)
+	)
+
+	query = get_conditions(filters, query, mr, mr_item)  # add conditional conditions
+
+	query = query.groupby(mr.name, mr_item.item_code).orderby(mr.transaction_date, mr.schedule_date)
+	data = query.run(as_dict=True)
+	return data
+
+
+def get_conditions(filters, query, mr, mr_item):
 	if filters.get("from_date") and filters.get("to_date"):
-		conditions += " and mr.transaction_date between '{0}' and '{1}'".format(filters.get("from_date"),filters.get("to_date"))
-
+		query = query.where(
+			(mr.transaction_date >= filters.get("from_date"))
+			& (mr.transaction_date <= filters.get("to_date"))
+		)
 	if filters.get("company"):
-		conditions += " and mr.company = '{0}'".format(filters.get("company"))
+		query = query.where(mr.company == filters.get("company"))
 
 	if filters.get("material_request"):
-		conditions += " and mr.name = '{0}'".format(filters.get("material_request"))
+		query = query.where(mr.name == filters.get("material_request"))
 
 	if filters.get("item_code"):
-		conditions += " and mr_item.item_code = '{0}'".format(filters.get("item_code"))
+		query = query.where(mr_item.item_code == filters.get("item_code"))
 
-	return conditions
+	return query
 
-def get_data(filters, conditions):
-	data = frappe.db.sql("""
-		select
-			mr.name as material_request,
-			mr.transaction_date as date,
-			mr_item.schedule_date as required_date,
-			mr_item.item_code as item_code,
-			sum(ifnull(mr_item.stock_qty, 0)) as qty,
-			ifnull(mr_item.stock_uom, '') as uom,
-			sum(ifnull(mr_item.ordered_qty, 0)) as ordered_qty,
-			sum(ifnull(mr_item.received_qty, 0)) as received_qty,
-			(sum(ifnull(mr_item.stock_qty, 0)) - sum(ifnull(mr_item.received_qty, 0))) as qty_to_receive,
-			(sum(ifnull(mr_item.stock_qty, 0)) - sum(ifnull(mr_item.ordered_qty, 0))) as qty_to_order,
-			mr_item.item_name as item_name,
-			mr_item.description as "description",
-			mr.company as company
-		from
-			`tabMaterial Request` mr, `tabMaterial Request Item` mr_item
-		where
-			mr_item.parent = mr.name
-			and mr.material_request_type = "Purchase"
-			and mr.docstatus = 1
-			and mr.status != "Stopped"
-			{conditions}
-		group by mr.name, mr_item.item_code
-		having
-			sum(ifnull(mr_item.ordered_qty, 0)) < sum(ifnull(mr_item.stock_qty, 0))
-		order by mr.transaction_date, mr.schedule_date""".format(conditions=conditions), as_dict=1)
-
-	return data
 
 def update_qty_columns(row_to_update, data_row):
 	fields = ["qty", "ordered_qty", "received_qty", "qty_to_receive", "qty_to_order"]
 	for field in fields:
 		row_to_update[field] += flt(data_row[field])
 
+
 def prepare_data(data, filters):
 	"""Prepare consolidated Report data and Chart data"""
 	material_request_map, item_qty_map = {}, {}
@@ -95,11 +109,11 @@
 		# item wise map for charts
 		if not row["item_code"] in item_qty_map:
 			item_qty_map[row["item_code"]] = {
-				"qty" : row["qty"],
-				"ordered_qty" : row["ordered_qty"],
+				"qty": row["qty"],
+				"ordered_qty": row["ordered_qty"],
 				"received_qty": row["received_qty"],
 				"qty_to_receive": row["qty_to_receive"],
-				"qty_to_order" : row["qty_to_order"],
+				"qty_to_order": row["qty_to_order"],
 			}
 		else:
 			item_entry = item_qty_map[row["item_code"]]
@@ -115,19 +129,20 @@
 				mr_row = material_request_map[row["material_request"]]
 				mr_row["required_date"] = min(getdate(mr_row["required_date"]), getdate(row["required_date"]))
 
-				#sum numeric columns
+				# sum numeric columns
 				update_qty_columns(mr_row, row)
 
 	chart_data = prepare_chart_data(item_qty_map)
 
 	if filters.get("group_by_mr"):
-		data =[]
+		data = []
 		for mr in material_request_map:
 			data.append(material_request_map[mr])
 		return data, chart_data
 
 	return data, chart_data
 
+
 def prepare_chart_data(item_data):
 	labels, qty_to_order, ordered_qty, received_qty, qty_to_receive = [], [], [], [], []
 
@@ -143,35 +158,22 @@
 		qty_to_receive.append(mr_row["qty_to_receive"])
 
 	chart_data = {
-		"data" : {
+		"data": {
 			"labels": labels,
 			"datasets": [
-				{
-					'name': _('Qty to Order'),
-					'values': qty_to_order
-				},
-				{
-					'name': _('Ordered Qty'),
-					'values': ordered_qty
-				},
-				{
-					'name': _('Received Qty'),
-					'values': received_qty
-				},
-				{
-					'name': _('Qty to Receive'),
-					'values': qty_to_receive
-				}
-			]
+				{"name": _("Qty to Order"), "values": qty_to_order},
+				{"name": _("Ordered Qty"), "values": ordered_qty},
+				{"name": _("Received Qty"), "values": received_qty},
+				{"name": _("Qty to Receive"), "values": qty_to_receive},
+			],
 		},
 		"type": "bar",
-		"barOptions": {
-			"stacked": 1
-		},
+		"barOptions": {"stacked": 1},
 	}
 
 	return chart_data
 
+
 def get_columns(filters):
 	columns = [
 		{
@@ -179,92 +181,78 @@
 			"fieldname": "material_request",
 			"fieldtype": "Link",
 			"options": "Material Request",
-			"width": 150
+			"width": 150,
 		},
-		{
-			"label":_("Date"),
-			"fieldname": "date",
-			"fieldtype": "Date",
-			"width": 90
-		},
-		{
-			"label":_("Required By"),
-			"fieldname": "required_date",
-			"fieldtype": "Date",
-			"width": 100
-		}
+		{"label": _("Date"), "fieldname": "date", "fieldtype": "Date", "width": 90},
+		{"label": _("Required By"), "fieldname": "required_date", "fieldtype": "Date", "width": 100},
 	]
 
 	if not filters.get("group_by_mr"):
-		columns.extend([{
-			"label":_("Item Code"),
-			"fieldname": "item_code",
-			"fieldtype": "Link",
-			"options": "Item",
-			"width": 100
-		},
-		{
-			"label":_("Item Name"),
-			"fieldname": "item_name",
-			"fieldtype": "Data",
-			"width": 100
-		},
-		{
-			"label": _("Description"),
-			"fieldname": "description",
-			"fieldtype": "Data",
-			"width": 200
-		},
-		{
-			"label": _("Stock UOM"),
-			"fieldname": "uom",
-			"fieldtype": "Data",
-			"width": 100,
-		}])
+		columns.extend(
+			[
+				{
+					"label": _("Item Code"),
+					"fieldname": "item_code",
+					"fieldtype": "Link",
+					"options": "Item",
+					"width": 100,
+				},
+				{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 100},
+				{"label": _("Description"), "fieldname": "description", "fieldtype": "Data", "width": 200},
+				{
+					"label": _("Stock UOM"),
+					"fieldname": "uom",
+					"fieldtype": "Data",
+					"width": 100,
+				},
+			]
+		)
 
-	columns.extend([
-		{
-			"label": _("Stock Qty"),
-			"fieldname": "qty",
-			"fieldtype": "Float",
-			"width": 120,
-			"convertible": "qty"
-		},
-		{
-			"label": _("Ordered Qty"),
-			"fieldname": "ordered_qty",
-			"fieldtype": "Float",
-			"width": 120,
-			"convertible": "qty"
-		},
-		{
-			"label": _("Received Qty"),
-			"fieldname": "received_qty",
-			"fieldtype": "Float",
-			"width": 120,
-			"convertible": "qty"
-		},
-		{
-			"label": _("Qty to Receive"),
-			"fieldname": "qty_to_receive",
-			"fieldtype": "Float",
-			"width": 120,
-			"convertible": "qty"
-		},
-		{
-			"label": _("Qty to Order"),
-			"fieldname": "qty_to_order",
-			"fieldtype": "Float",
-			"width": 120,
-			"convertible": "qty"
-		},
-		{
-			"label": _("Company"),
-			"fieldname": "company",
-			"fieldtype": "Link",
-			"options": "Company",
-			"width": 100
-		}
-	])
+	columns.extend(
+		[
+			{
+				"label": _("Stock Qty"),
+				"fieldname": "qty",
+				"fieldtype": "Float",
+				"width": 120,
+				"convertible": "qty",
+			},
+			{
+				"label": _("Ordered Qty"),
+				"fieldname": "ordered_qty",
+				"fieldtype": "Float",
+				"width": 120,
+				"convertible": "qty",
+			},
+			{
+				"label": _("Received Qty"),
+				"fieldname": "received_qty",
+				"fieldtype": "Float",
+				"width": 120,
+				"convertible": "qty",
+			},
+			{
+				"label": _("Qty to Receive"),
+				"fieldname": "qty_to_receive",
+				"fieldtype": "Float",
+				"width": 120,
+				"convertible": "qty",
+			},
+			{
+				"label": _("Qty to Order"),
+				"fieldname": "qty_to_order",
+				"fieldtype": "Float",
+				"width": 120,
+				"convertible": "qty",
+			},
+			{
+				"label": _("Company"),
+				"fieldname": "company",
+				"fieldtype": "Link",
+				"options": "Company",
+				"width": 100,
+			},
+		]
+	)
 
 	return columns
diff --git a/erpnext/buying/report/requested_items_to_order_and_receive/test_requested_items_to_order_and_receive.py b/erpnext/buying/report/requested_items_to_order_and_receive/test_requested_items_to_order_and_receive.py
new file mode 100644
index 0000000..5b84113
--- /dev/null
+++ b/erpnext/buying/report/requested_items_to_order_and_receive/test_requested_items_to_order_and_receive.py
@@ -0,0 +1,69 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+import frappe
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import add_days, today
+
+from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
+from erpnext.buying.report.requested_items_to_order_and_receive.requested_items_to_order_and_receive import (
+	get_data,
+)
+from erpnext.stock.doctype.item.test_item import create_item
+from erpnext.stock.doctype.material_request.material_request import make_purchase_order
+
+
+class TestRequestedItemsToOrderAndReceive(FrappeTestCase):
+	def setUp(self) -> None:
+		create_item("Test MR Report Item")
+		self.setup_material_request()  # to order and receive
+		self.setup_material_request(order=True, days=1)  # to receive (ordered)
+		self.setup_material_request(order=True, receive=True, days=2)  # complete (ordered & received)
+
+		self.filters = frappe._dict(
+			company="_Test Company",
+			from_date=today(),
+			to_date=add_days(today(), 30),
+			item_code="Test MR Report Item",
+		)
+
+	def tearDown(self) -> None:
+		frappe.db.rollback()
+
+	def test_date_range(self):
+		data = get_data(self.filters)
+		self.assertEqual(len(data), 2)  # MRs today should be fetched
+
+		data = get_data(self.filters.update({"from_date": add_days(today(), 10)}))
+		self.assertEqual(len(data), 0)  # MRs today should not be fetched as from date is in future
+
+	def test_ordered_received_material_requests(self):
+		data = get_data(self.filters)
+
+		# from the 3 MRs made, only 2 (to receive) should be fetched
+		self.assertEqual(len(data), 2)
+		self.assertEqual(data[0].ordered_qty, 0.0)
+		self.assertEqual(data[1].ordered_qty, 57.0)
+
+	def setup_material_request(self, order=False, receive=False, days=0):
+		po = None
+		test_records = frappe.get_test_records("Material Request")
+
+		mr = frappe.copy_doc(test_records[0])
+		mr.transaction_date = add_days(today(), days)
+		mr.schedule_date = add_days(mr.transaction_date, 1)
+		for row in mr.items:
+			row.item_code = "Test MR Report Item"
+			row.item_name = "Test MR Report Item"
+			row.description = "Test MR Report Item"
+			row.uom = "Nos"
+			row.schedule_date = mr.schedule_date
+		mr.submit()
+
+		if order or receive:
+			po = make_purchase_order(mr.name)
+			po.supplier = "_Test Supplier"
+			po.submit()
+			if receive:
+				pr = make_purchase_receipt(po.name)
+				pr.submit()
diff --git a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js
index 5ba52f1..6889322 100644
--- a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js
+++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js
@@ -35,7 +35,7 @@
 				return {
 					filters: {
 						docstatus: 1,
-						is_subcontracted: 'Yes',
+						is_subcontracted: 1,
 						company: frappe.query_report.get_filter_value('company')
 					}
 				}
diff --git a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py
index 8e5c2f9..3d66637 100644
--- a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py
+++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py
@@ -13,6 +13,7 @@
 
 	return columns, data
 
+
 def get_data(report_filters):
 	data = []
 	orders = get_subcontracted_orders(report_filters)
@@ -24,57 +25,85 @@
 
 	return data
 
+
 def get_subcontracted_orders(report_filters):
-	fields = ['`tabPurchase Order Item`.`parent` as po_id', '`tabPurchase Order Item`.`item_code`',
-		'`tabPurchase Order Item`.`item_name`', '`tabPurchase Order Item`.`qty`', '`tabPurchase Order Item`.`name`',
-		'`tabPurchase Order Item`.`received_qty`', '`tabPurchase Order`.`status`']
+	fields = [
+		"`tabPurchase Order Item`.`parent` as po_id",
+		"`tabPurchase Order Item`.`item_code`",
+		"`tabPurchase Order Item`.`item_name`",
+		"`tabPurchase Order Item`.`qty`",
+		"`tabPurchase Order Item`.`name`",
+		"`tabPurchase Order Item`.`received_qty`",
+		"`tabPurchase Order`.`status`",
+	]
 
 	filters = get_filters(report_filters)
 
-	return frappe.get_all('Purchase Order', fields = fields, filters=filters) or []
+	return frappe.get_all("Purchase Order", fields=fields, filters=filters) or []
+
 
 def get_filters(report_filters):
-	filters = [['Purchase Order', 'docstatus', '=', 1], ['Purchase Order', 'is_subcontracted', '=', 'Yes'],
-		['Purchase Order', 'transaction_date', 'between', (report_filters.from_date, report_filters.to_date)]]
+	filters = [
+		["Purchase Order", "docstatus", "=", 1],
+		["Purchase Order", "is_subcontracted", "=", 1],
+		[
+			"Purchase Order",
+			"transaction_date",
+			"between",
+			(report_filters.from_date, report_filters.to_date),
+		],
+	]
 
-	for field in ['name', 'company']:
+	for field in ["name", "company"]:
 		if report_filters.get(field):
-			filters.append(['Purchase Order', field, '=', report_filters.get(field)])
+			filters.append(["Purchase Order", field, "=", report_filters.get(field)])
 
 	return filters
 
+
 def get_supplied_items(orders, report_filters):
 	if not orders:
 		return []
 
-	fields = ['parent', 'main_item_code', 'rm_item_code', 'required_qty',
-		'supplied_qty', 'returned_qty', 'total_supplied_qty', 'consumed_qty', 'reference_name']
+	fields = [
+		"parent",
+		"main_item_code",
+		"rm_item_code",
+		"required_qty",
+		"supplied_qty",
+		"returned_qty",
+		"total_supplied_qty",
+		"consumed_qty",
+		"reference_name",
+	]
 
-	filters = {'parent': ('in', [d.po_id for d in orders]), 'docstatus': 1}
+	filters = {"parent": ("in", [d.po_id for d in orders]), "docstatus": 1}
 
 	supplied_items = {}
-	for row in frappe.get_all('Purchase Order Item Supplied', fields = fields, filters=filters):
+	for row in frappe.get_all("Purchase Order Item Supplied", fields=fields, filters=filters):
 		new_key = (row.parent, row.reference_name, row.main_item_code)
 
 		supplied_items.setdefault(new_key, []).append(row)
 
 	return supplied_items
 
+
 def prepare_subcontracted_data(orders, supplied_items):
 	po_details = {}
 	for row in orders:
 		key = (row.po_id, row.name, row.item_code)
 		if key not in po_details:
-			po_details.setdefault(key, frappe._dict({'po_item': row, 'supplied_items': []}))
+			po_details.setdefault(key, frappe._dict({"po_item": row, "supplied_items": []}))
 
 		details = po_details[key]
 
 		if supplied_items.get(key):
 			for supplied_item in supplied_items[key]:
-				details['supplied_items'].append(supplied_item)
+				details["supplied_items"].append(supplied_item)
 
 	return po_details
 
+
 def get_subcontracted_data(po_details, data):
 	for key, details in po_details.items():
 		res = details.po_item
@@ -85,6 +114,7 @@
 			res.update(row)
 			data.append(res)
 
+
 def get_columns():
 	return [
 		{
@@ -92,62 +122,27 @@
 			"fieldname": "po_id",
 			"fieldtype": "Link",
 			"options": "Purchase Order",
-			"width": 100
+			"width": 100,
 		},
-		{
-			"label": _("Status"),
-			"fieldname": "status",
-			"fieldtype": "Data",
-			"width": 80
-		},
+		{"label": _("Status"), "fieldname": "status", "fieldtype": "Data", "width": 80},
 		{
 			"label": _("Subcontracted Item"),
 			"fieldname": "item_code",
 			"fieldtype": "Link",
 			"options": "Item",
-			"width": 160
+			"width": 160,
 		},
-		{
-			"label": _("Order Qty"),
-			"fieldname": "qty",
-			"fieldtype": "Float",
-			"width": 90
-		},
-		{
-			"label": _("Received Qty"),
-			"fieldname": "received_qty",
-			"fieldtype": "Float",
-			"width": 110
-		},
+		{"label": _("Order Qty"), "fieldname": "qty", "fieldtype": "Float", "width": 90},
+		{"label": _("Received Qty"), "fieldname": "received_qty", "fieldtype": "Float", "width": 110},
 		{
 			"label": _("Supplied Item"),
 			"fieldname": "rm_item_code",
 			"fieldtype": "Link",
 			"options": "Item",
-			"width": 160
+			"width": 160,
 		},
-		{
-			"label": _("Required Qty"),
-			"fieldname": "required_qty",
-			"fieldtype": "Float",
-			"width": 110
-		},
-		{
-			"label": _("Supplied Qty"),
-			"fieldname": "supplied_qty",
-			"fieldtype": "Float",
-			"width": 110
-		},
-		{
-			"label": _("Consumed Qty"),
-			"fieldname": "consumed_qty",
-			"fieldtype": "Float",
-			"width": 120
-		},
-		{
-			"label": _("Returned Qty"),
-			"fieldname": "returned_qty",
-			"fieldtype": "Float",
-			"width": 110
-		}
+		{"label": _("Required Qty"), "fieldname": "required_qty", "fieldtype": "Float", "width": 110},
+		{"label": _("Supplied Qty"), "fieldname": "supplied_qty", "fieldtype": "Float", "width": 110},
+		{"label": _("Consumed Qty"), "fieldname": "consumed_qty", "fieldtype": "Float", "width": 120},
+		{"label": _("Returned Qty"), "fieldname": "returned_qty", "fieldtype": "Float", "width": 110},
 	]
diff --git a/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.py b/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.py
index 67e275f..2e90de6 100644
--- a/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.py
+++ b/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.py
@@ -12,9 +12,10 @@
 
 	data = []
 	columns = get_columns()
-	get_data(data , filters)
+	get_data(data, filters)
 	return columns, data
 
+
 def get_columns():
 	return [
 		{
@@ -22,54 +23,39 @@
 			"fieldtype": "Link",
 			"fieldname": "purchase_order",
 			"options": "Purchase Order",
-			"width": 150
+			"width": 150,
 		},
-		{
-			"label": _("Date"),
-			"fieldtype": "Date",
-			"fieldname": "date",
-			"hidden": 1,
-			"width": 150
-		},
+		{"label": _("Date"), "fieldtype": "Date", "fieldname": "date", "hidden": 1, "width": 150},
 		{
 			"label": _("Supplier"),
 			"fieldtype": "Link",
 			"fieldname": "supplier",
 			"options": "Supplier",
-			"width": 150
+			"width": 150,
 		},
 		{
 			"label": _("Finished Good Item Code"),
 			"fieldtype": "Data",
 			"fieldname": "fg_item_code",
-			"width": 100
+			"width": 100,
 		},
-		{
-			"label": _("Item name"),
-			"fieldtype": "Data",
-			"fieldname": "item_name",
-			"width": 100
-		},
+		{"label": _("Item name"), "fieldtype": "Data", "fieldname": "item_name", "width": 100},
 		{
 			"label": _("Required Quantity"),
 			"fieldtype": "Float",
 			"fieldname": "required_qty",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Received Quantity"),
 			"fieldtype": "Float",
 			"fieldname": "received_qty",
-			"width": 100
+			"width": 100,
 		},
-		{
-			"label": _("Pending Quantity"),
-			"fieldtype": "Float",
-			"fieldname": "pending_qty",
-			"width": 100
-		}
+		{"label": _("Pending Quantity"), "fieldtype": "Float", "fieldname": "pending_qty", "width": 100},
 	]
 
+
 def get_data(data, filters):
 	po = get_po(filters)
 	po_name = [v.name for v in po]
@@ -77,29 +63,35 @@
 	for item in sub_items:
 		for order in po:
 			if order.name == item.parent and item.received_qty < item.qty:
-				row ={
-					'purchase_order': item.parent,
-					'date': order.transaction_date,
-					'supplier': order.supplier,
-					'fg_item_code': item.item_code,
-					'item_name': item.item_name,
-					'required_qty': item.qty,
-					'received_qty':item.received_qty,
-					'pending_qty':item.qty - item.received_qty
+				row = {
+					"purchase_order": item.parent,
+					"date": order.transaction_date,
+					"supplier": order.supplier,
+					"fg_item_code": item.item_code,
+					"item_name": item.item_name,
+					"required_qty": item.qty,
+					"received_qty": item.received_qty,
+					"pending_qty": item.qty - item.received_qty,
 				}
 				data.append(row)
 
+
 def get_po(filters):
 	record_filters = [
-			["is_subcontracted", "=", "Yes"],
-			["supplier", "=", filters.supplier],
-			["transaction_date", "<=", filters.to_date],
-			["transaction_date", ">=", filters.from_date],
-			["docstatus", "=", 1]
-		]
-	return frappe.get_all("Purchase Order", filters=record_filters, fields=["name", "transaction_date", "supplier"])
+		["is_subcontracted", "=", 1],
+		["supplier", "=", filters.supplier],
+		["transaction_date", "<=", filters.to_date],
+		["transaction_date", ">=", filters.from_date],
+		["docstatus", "=", 1],
+	]
+	return frappe.get_all(
+		"Purchase Order", filters=record_filters, fields=["name", "transaction_date", "supplier"]
+	)
+
 
 def get_purchase_order_item_supplied(po):
-	return frappe.get_all("Purchase Order Item", filters=[
-			('parent', 'IN', po)
-	], fields=["parent", "item_code", "item_name", "qty", "received_qty"])
+	return frappe.get_all(
+		"Purchase Order Item",
+		filters=[("parent", "IN", po)],
+		fields=["parent", "item_code", "item_name", "qty", "received_qty"],
+	)
diff --git a/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py b/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py
index c2b38d3..57f8741 100644
--- a/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py
+++ b/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py
@@ -16,26 +16,40 @@
 
 
 class TestSubcontractedItemToBeReceived(FrappeTestCase):
-
 	def test_pending_and_received_qty(self):
-		po = create_purchase_order(item_code='_Test FG Item', is_subcontracted='Yes')
+		po = create_purchase_order(item_code="_Test FG Item", is_subcontracted=1)
 		transfer_param = []
-		make_stock_entry(item_code='_Test Item', target='_Test Warehouse 1 - _TC', qty=100, basic_rate=100)
-		make_stock_entry(item_code='_Test Item Home Desktop 100', target='_Test Warehouse 1 - _TC', qty=100, basic_rate=100)
+		make_stock_entry(
+			item_code="_Test Item", target="_Test Warehouse 1 - _TC", qty=100, basic_rate=100
+		)
+		make_stock_entry(
+			item_code="_Test Item Home Desktop 100",
+			target="_Test Warehouse 1 - _TC",
+			qty=100,
+			basic_rate=100,
+		)
 		make_purchase_receipt_against_po(po.name)
 		po.reload()
-		col, data = execute(filters=frappe._dict({'supplier': po.supplier,
-		   'from_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=-10)),
-		   'to_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=10))}))
-		self.assertEqual(data[0]['pending_qty'], 5)
-		self.assertEqual(data[0]['received_qty'], 5)
-		self.assertEqual(data[0]['purchase_order'], po.name)
-		self.assertEqual(data[0]['supplier'], po.supplier)
+		col, data = execute(
+			filters=frappe._dict(
+				{
+					"supplier": po.supplier,
+					"from_date": frappe.utils.get_datetime(
+						frappe.utils.add_to_date(po.transaction_date, days=-10)
+					),
+					"to_date": frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=10)),
+				}
+			)
+		)
+		self.assertEqual(data[0]["pending_qty"], 5)
+		self.assertEqual(data[0]["received_qty"], 5)
+		self.assertEqual(data[0]["purchase_order"], po.name)
+		self.assertEqual(data[0]["supplier"], po.supplier)
 
 
 def make_purchase_receipt_against_po(po, quantity=5):
 	pr = make_purchase_receipt(po)
 	pr.items[0].qty = quantity
-	pr.supplier_warehouse = '_Test Warehouse 1 - _TC'
+	pr.supplier_warehouse = "_Test Warehouse 1 - _TC"
 	pr.insert()
 	pr.submit()
diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py
index 6b605ad..6b8a3b1 100644
--- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py
+++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py
@@ -15,6 +15,7 @@
 
 	return columns, data or []
 
+
 def get_columns():
 	return [
 		{
@@ -22,47 +23,28 @@
 			"fieldtype": "Link",
 			"fieldname": "purchase_order",
 			"options": "Purchase Order",
-			"width": 200
+			"width": 200,
 		},
-		{
-			"label": _("Date"),
-			"fieldtype": "Date",
-			"fieldname": "date",
-			"width": 150
-		},
+		{"label": _("Date"), "fieldtype": "Date", "fieldname": "date", "width": 150},
 		{
 			"label": _("Supplier"),
 			"fieldtype": "Link",
 			"fieldname": "supplier",
 			"options": "Supplier",
-			"width": 150
+			"width": 150,
 		},
-		{
-			"label": _("Item Code"),
-			"fieldtype": "Data",
-			"fieldname": "rm_item_code",
-			"width": 150
-		},
-		{
-			"label": _("Required Quantity"),
-			"fieldtype": "Float",
-			"fieldname": "reqd_qty",
-			"width": 150
-		},
+		{"label": _("Item Code"), "fieldtype": "Data", "fieldname": "rm_item_code", "width": 150},
+		{"label": _("Required Quantity"), "fieldtype": "Float", "fieldname": "reqd_qty", "width": 150},
 		{
 			"label": _("Transferred Quantity"),
 			"fieldtype": "Float",
 			"fieldname": "transferred_qty",
-			"width": 200
+			"width": 200,
 		},
-		{
-			"label": _("Pending Quantity"),
-			"fieldtype": "Float",
-			"fieldname": "p_qty",
-			"width": 150
-		}
+		{"label": _("Pending Quantity"), "fieldtype": "Float", "fieldname": "p_qty", "width": 150},
 	]
 
+
 def get_data(filters):
 	po_rm_item_details = get_po_items_to_supply(filters)
 
@@ -76,6 +58,7 @@
 
 	return data
 
+
 def get_po_items_to_supply(filters):
 	return frappe.db.get_all(
 		"Purchase Order",
@@ -85,14 +68,14 @@
 			"supplier as supplier",
 			"`tabPurchase Order Item Supplied`.rm_item_code as rm_item_code",
 			"`tabPurchase Order Item Supplied`.required_qty as reqd_qty",
-			"`tabPurchase Order Item Supplied`.supplied_qty as transferred_qty"
+			"`tabPurchase Order Item Supplied`.supplied_qty as transferred_qty",
 		],
-		filters = [
+		filters=[
 			["Purchase Order", "per_received", "<", "100"],
-			["Purchase Order", "is_subcontracted", "=", "Yes"],
+			["Purchase Order", "is_subcontracted", "=", 1],
 			["Purchase Order", "supplier", "=", filters.supplier],
 			["Purchase Order", "transaction_date", "<=", filters.to_date],
 			["Purchase Order", "transaction_date", ">=", filters.from_date],
-			["Purchase Order", "docstatus", "=", 1]
-		]
+			["Purchase Order", "docstatus", "=", 1],
+		],
 	)
diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
index fc9acab..2791a26 100644
--- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
+++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
@@ -17,82 +17,87 @@
 
 
 class TestSubcontractedItemToBeTransferred(FrappeTestCase):
-
 	def test_pending_and_transferred_qty(self):
-		po = create_purchase_order(item_code='_Test FG Item', is_subcontracted='Yes', supplier_warehouse="_Test Warehouse 1 - _TC")
+		po = create_purchase_order(
+			item_code="_Test FG Item", is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
+		)
 
 		# Material Receipt of RMs
-		make_stock_entry(item_code='_Test Item', target='_Test Warehouse - _TC', qty=100, basic_rate=100)
-		make_stock_entry(item_code='_Test Item Home Desktop 100', target='_Test Warehouse - _TC', qty=100, basic_rate=100)
+		make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=100, basic_rate=100)
+		make_stock_entry(
+			item_code="_Test Item Home Desktop 100", target="_Test Warehouse - _TC", qty=100, basic_rate=100
+		)
 
 		se = transfer_subcontracted_raw_materials(po)
 
-		col, data = execute(filters=frappe._dict(
-			{
-				'supplier': po.supplier,
-				'from_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=-10)),
-				'to_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=10))
-			}
-		))
+		col, data = execute(
+			filters=frappe._dict(
+				{
+					"supplier": po.supplier,
+					"from_date": frappe.utils.get_datetime(
+						frappe.utils.add_to_date(po.transaction_date, days=-10)
+					),
+					"to_date": frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=10)),
+				}
+			)
+		)
 		po.reload()
 
-		po_data = [row for row in data if row.get('purchase_order') == po.name]
+		po_data = [row for row in data if row.get("purchase_order") == po.name]
 		# Alphabetically sort to be certain of order
-		po_data = sorted(po_data, key = lambda i: i['rm_item_code'])
+		po_data = sorted(po_data, key=lambda i: i["rm_item_code"])
 
 		self.assertEqual(len(po_data), 2)
-		self.assertEqual(po_data[0]['purchase_order'], po.name)
+		self.assertEqual(po_data[0]["purchase_order"], po.name)
 
-		self.assertEqual(po_data[0]['rm_item_code'], '_Test Item')
-		self.assertEqual(po_data[0]['p_qty'], 8)
-		self.assertEqual(po_data[0]['transferred_qty'], 2)
+		self.assertEqual(po_data[0]["rm_item_code"], "_Test Item")
+		self.assertEqual(po_data[0]["p_qty"], 8)
+		self.assertEqual(po_data[0]["transferred_qty"], 2)
 
-		self.assertEqual(po_data[1]['rm_item_code'], '_Test Item Home Desktop 100')
-		self.assertEqual(po_data[1]['p_qty'], 19)
-		self.assertEqual(po_data[1]['transferred_qty'], 1)
+		self.assertEqual(po_data[1]["rm_item_code"], "_Test Item Home Desktop 100")
+		self.assertEqual(po_data[1]["p_qty"], 19)
+		self.assertEqual(po_data[1]["transferred_qty"], 1)
 
 		se.cancel()
 		po.cancel()
 
+
 def transfer_subcontracted_raw_materials(po):
 	# Order of supplied items fetched in PO is flaky
-	transfer_qty_map = {
-		'_Test Item': 2,
-		'_Test Item Home Desktop 100': 1
-	}
+	transfer_qty_map = {"_Test Item": 2, "_Test Item Home Desktop 100": 1}
 
 	item_1 = po.supplied_items[0].rm_item_code
 	item_2 = po.supplied_items[1].rm_item_code
 
 	rm_item = [
 		{
-			'name': po.supplied_items[0].name,
-			'item_code': item_1,
-			'rm_item_code': item_1,
-			'item_name': item_1,
-			'qty': transfer_qty_map[item_1],
-			'warehouse': '_Test Warehouse - _TC',
-			'rate': 100,
-			'amount': 100 * transfer_qty_map[item_1],
-			'stock_uom': 'Nos'
+			"name": po.supplied_items[0].name,
+			"item_code": item_1,
+			"rm_item_code": item_1,
+			"item_name": item_1,
+			"qty": transfer_qty_map[item_1],
+			"warehouse": "_Test Warehouse - _TC",
+			"rate": 100,
+			"amount": 100 * transfer_qty_map[item_1],
+			"stock_uom": "Nos",
 		},
 		{
-			'name': po.supplied_items[1].name,
-			'item_code': item_2,
-			'rm_item_code': item_2,
-			'item_name': item_2,
-			'qty': transfer_qty_map[item_2],
-			'warehouse': '_Test Warehouse - _TC',
-			'rate': 100,
-			'amount': 100 * transfer_qty_map[item_2],
-			'stock_uom': 'Nos'
-		}
+			"name": po.supplied_items[1].name,
+			"item_code": item_2,
+			"rm_item_code": item_2,
+			"item_name": item_2,
+			"qty": transfer_qty_map[item_2],
+			"warehouse": "_Test Warehouse - _TC",
+			"rate": 100,
+			"amount": 100 * transfer_qty_map[item_2],
+			"stock_uom": "Nos",
+		},
 	]
 	rm_item_string = json.dumps(rm_item)
 	se = frappe.get_doc(make_rm_stock_entry(po.name, rm_item_string))
-	se.from_warehouse = '_Test Warehouse - _TC'
-	se.to_warehouse = '_Test Warehouse - _TC'
-	se.stock_entry_type = 'Send to Subcontractor'
+	se.from_warehouse = "_Test Warehouse - _TC"
+	se.to_warehouse = "_Test Warehouse - _TC"
+	se.stock_entry_type = "Send to Subcontractor"
 	se.save()
 	se.submit()
 	return se
diff --git a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js
index 7a8d08d..fd73b87 100644
--- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js
+++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js
@@ -114,11 +114,11 @@
 	onload: (report) => {
 		// Create a button for setting the default supplier
 		report.page.add_inner_button(__("Select Default Supplier"), () => {
-			let reporter = frappe.query_reports["Quoted Item Comparison"];
+			let reporter = frappe.query_reports["Supplier Quotation Comparison"];
 
 			//Always make a new one so that the latest values get updated
 			reporter.make_default_supplier_dialog(report);
-		}, 'Tools');
+		}, __("Tools"));
 
 	},
 	make_default_supplier_dialog: (report) => {
@@ -126,7 +126,7 @@
 		if(!report.data) return;
 
 		let filters = report.get_values();
-		let item_code = filters.item;
+		let item_code = filters.item_code;
 
 		// Get a list of the suppliers (with a blank as well) for the user to select
 		let suppliers = $.map(report.data, (row, idx)=>{ return row.supplier_name })
@@ -152,7 +152,7 @@
 			]
 		});
 
-		dialog.set_primary_action("Set Default Supplier", () => {
+		dialog.set_primary_action(__("Set Default Supplier"), () => {
 			let values = dialog.get_values();
 			if(values) {
 				// Set the default_supplier field of the appropriate Item to the selected supplier
diff --git a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py
index 65f9ce3..3013b6d 100644
--- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py
+++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py
@@ -24,6 +24,7 @@
 
 	return columns, data, message, chart_data
 
+
 def get_conditions(filters):
 	conditions = ""
 	if filters.get("item_code"):
@@ -43,8 +44,10 @@
 
 	return conditions
 
+
 def get_data(filters, conditions):
-	supplier_quotation_data = frappe.db.sql("""
+	supplier_quotation_data = frappe.db.sql(
+		"""
 		SELECT
 			sqi.parent, sqi.item_code,
 			sqi.qty, sqi.stock_qty, sqi.amount,
@@ -60,23 +63,33 @@
 			AND sq.company = %(company)s
 			AND sq.transaction_date between %(from_date)s and %(to_date)s
 			{0}
-			order by sq.transaction_date, sqi.item_code""".format(conditions), filters, as_dict=1)
+			order by sq.transaction_date, sqi.item_code""".format(
+			conditions
+		),
+		filters,
+		as_dict=1,
+	)
 
 	return supplier_quotation_data
 
+
 def prepare_data(supplier_quotation_data, filters):
 	out, groups, qty_list, suppliers, chart_data = [], [], [], [], []
 	group_wise_map = defaultdict(list)
 	supplier_qty_price_map = {}
 
-	group_by_field = "supplier_name" if filters.get("group_by") == "Group by Supplier" else "item_code"
+	group_by_field = (
+		"supplier_name" if filters.get("group_by") == "Group by Supplier" else "item_code"
+	)
 	company_currency = frappe.db.get_default("currency")
 	float_precision = cint(frappe.db.get_default("float_precision")) or 2
 
 	for data in supplier_quotation_data:
-		group = data.get(group_by_field) # get item or supplier value for this row
+		group = data.get(group_by_field)  # get item or supplier value for this row
 
-		supplier_currency = frappe.db.get_value("Supplier", data.get("supplier_name"), "default_currency")
+		supplier_currency = frappe.db.get_value(
+			"Supplier", data.get("supplier_name"), "default_currency"
+		)
 
 		if supplier_currency:
 			exchange_rate = get_exchange_rate(supplier_currency, company_currency)
@@ -84,16 +97,18 @@
 			exchange_rate = 1
 
 		row = {
-			"item_code":  "" if group_by_field=="item_code" else data.get("item_code"), # leave blank if group by field
-			"supplier_name": "" if group_by_field=="supplier_name" else data.get("supplier_name"),
+			"item_code": ""
+			if group_by_field == "item_code"
+			else data.get("item_code"),  # leave blank if group by field
+			"supplier_name": "" if group_by_field == "supplier_name" else data.get("supplier_name"),
 			"quotation": data.get("parent"),
 			"qty": data.get("qty"),
 			"price": flt(data.get("amount") * exchange_rate, float_precision),
 			"uom": data.get("uom"),
-			"stock_uom": data.get('stock_uom'),
+			"stock_uom": data.get("stock_uom"),
 			"request_for_quotation": data.get("request_for_quotation"),
-			"valid_till": data.get('valid_till'),
-			"lead_time_days": data.get('lead_time_days')
+			"valid_till": data.get("valid_till"),
+			"lead_time_days": data.get("lead_time_days"),
 		}
 		row["price_per_unit"] = flt(row["price"]) / (flt(data.get("stock_qty")) or 1)
 
@@ -119,8 +134,8 @@
 
 	# final data format for report view
 	for group in groups:
-		group_entries = group_wise_map[group] # all entries pertaining to item/supplier
-		group_entries[0].update({group_by_field : group}) # Add item/supplier name in first group row
+		group_entries = group_wise_map[group]  # all entries pertaining to item/supplier
+		group_entries[0].update({group_by_field: group})  # Add item/supplier name in first group row
 
 		if highlight_min_price:
 			prices = [group_entry["price_per_unit"] for group_entry in group_entries]
@@ -137,6 +152,7 @@
 
 	return out, chart_data
 
+
 def prepare_chart_data(suppliers, qty_list, supplier_qty_price_map):
 	data_points_map = {}
 	qty_list.sort()
@@ -157,107 +173,89 @@
 	for qty in qty_list:
 		datapoints = {
 			"name": currency_symbol + " (Qty " + str(qty) + " )",
-			"values": data_points_map[qty]
+			"values": data_points_map[qty],
 		}
 		dataset.append(datapoints)
 
-	chart_data = {
-		"data": {
-			"labels": suppliers,
-			"datasets": dataset
-		},
-		"type": "bar"
-	}
+	chart_data = {"data": {"labels": suppliers, "datasets": dataset}, "type": "bar"}
 
 	return chart_data
 
+
 def get_columns(filters):
 	group_by_columns = [
-	{
-		"fieldname": "supplier_name",
-		"label": _("Supplier"),
-		"fieldtype": "Link",
-		"options": "Supplier",
-		"width": 150
-	},
-	{
-		"fieldname": "item_code",
-		"label": _("Item"),
-		"fieldtype": "Link",
-		"options": "Item",
-		"width": 150
-	}]
+		{
+			"fieldname": "supplier_name",
+			"label": _("Supplier"),
+			"fieldtype": "Link",
+			"options": "Supplier",
+			"width": 150,
+		},
+		{
+			"fieldname": "item_code",
+			"label": _("Item"),
+			"fieldtype": "Link",
+			"options": "Item",
+			"width": 150,
+		},
+	]
 
 	columns = [
-	{
-		"fieldname": "uom",
-		"label": _("UOM"),
-		"fieldtype": "Link",
-		"options": "UOM",
-		"width": 90
-	},
-	{
-		"fieldname": "qty",
-		"label": _("Quantity"),
-		"fieldtype": "Float",
-		"width": 80
-	},
-	{
-		"fieldname": "price",
-		"label": _("Price"),
-		"fieldtype": "Currency",
-		"options": "Company:company:default_currency",
-		"width": 110
-	},
-	{
-		"fieldname": "stock_uom",
-		"label": _("Stock UOM"),
-		"fieldtype": "Link",
-		"options": "UOM",
-		"width": 90
-	},
-	{
-		"fieldname": "price_per_unit",
-		"label": _("Price per Unit (Stock UOM)"),
-		"fieldtype": "Currency",
-		"options": "Company:company:default_currency",
-		"width": 120
-	},
-	{
-		"fieldname": "quotation",
-		"label": _("Supplier Quotation"),
-		"fieldtype": "Link",
-		"options": "Supplier Quotation",
-		"width": 200
-	},
-	{
-		"fieldname": "valid_till",
-		"label": _("Valid Till"),
-		"fieldtype": "Date",
-		"width": 100
-	},
-	{
-		"fieldname": "lead_time_days",
-		"label": _("Lead Time (Days)"),
-		"fieldtype": "Int",
-		"width": 100
-	},
-	{
-		"fieldname": "request_for_quotation",
-		"label": _("Request for Quotation"),
-		"fieldtype": "Link",
-		"options": "Request for Quotation",
-		"width": 150
-	}]
+		{"fieldname": "uom", "label": _("UOM"), "fieldtype": "Link", "options": "UOM", "width": 90},
+		{"fieldname": "qty", "label": _("Quantity"), "fieldtype": "Float", "width": 80},
+		{
+			"fieldname": "price",
+			"label": _("Price"),
+			"fieldtype": "Currency",
+			"options": "Company:company:default_currency",
+			"width": 110,
+		},
+		{
+			"fieldname": "stock_uom",
+			"label": _("Stock UOM"),
+			"fieldtype": "Link",
+			"options": "UOM",
+			"width": 90,
+		},
+		{
+			"fieldname": "price_per_unit",
+			"label": _("Price per Unit (Stock UOM)"),
+			"fieldtype": "Currency",
+			"options": "Company:company:default_currency",
+			"width": 120,
+		},
+		{
+			"fieldname": "quotation",
+			"label": _("Supplier Quotation"),
+			"fieldtype": "Link",
+			"options": "Supplier Quotation",
+			"width": 200,
+		},
+		{"fieldname": "valid_till", "label": _("Valid Till"), "fieldtype": "Date", "width": 100},
+		{
+			"fieldname": "lead_time_days",
+			"label": _("Lead Time (Days)"),
+			"fieldtype": "Int",
+			"width": 100,
+		},
+		{
+			"fieldname": "request_for_quotation",
+			"label": _("Request for Quotation"),
+			"fieldtype": "Link",
+			"options": "Request for Quotation",
+			"width": 150,
+		},
+	]
 
 	if filters.get("group_by") == "Group by Item":
 		group_by_columns.reverse()
 
-	columns[0:0] = group_by_columns # add positioned group by columns to the report
+	columns[0:0] = group_by_columns  # add positioned group by columns to the report
 	return columns
 
+
 def get_message():
-	return  """<span class="indicator">
+	return """<span class="indicator">
 		Valid till : &nbsp;&nbsp;
 		</span>
 		<span class="indicator orange">
diff --git a/erpnext/buying/utils.py b/erpnext/buying/utils.py
index 66c60d5..e904af0 100644
--- a/erpnext/buying/utils.py
+++ b/erpnext/buying/utils.py
@@ -3,18 +3,19 @@
 
 
 import json
+from typing import Dict
 
 import frappe
 from frappe import _
-from frappe.utils import cint, cstr, flt
+from frappe.utils import cint, cstr, flt, getdate
 
 from erpnext.stock.doctype.item.item import get_last_purchase_details, validate_end_of_life
 
 
-def update_last_purchase_rate(doc, is_submit):
+def update_last_purchase_rate(doc, is_submit) -> None:
 	"""updates last_purchase_rate in item table for each item"""
-	import frappe.utils
-	this_purchase_date = frappe.utils.getdate(doc.get('posting_date') or doc.get('transaction_date'))
+
+	this_purchase_date = getdate(doc.get("posting_date") or doc.get("transaction_date"))
 
 	for d in doc.get("items"):
 		# get last purchase details
@@ -22,9 +23,10 @@
 
 		# compare last purchase date and this transaction's date
 		last_purchase_rate = None
-		if last_purchase_details and \
-				(doc.get('docstatus') == 2 or last_purchase_details.purchase_date > this_purchase_date):
-			last_purchase_rate = last_purchase_details['base_net_rate']
+		if last_purchase_details and (
+			doc.get("docstatus") == 2 or last_purchase_details.purchase_date > this_purchase_date
+		):
+			last_purchase_rate = last_purchase_details["base_net_rate"]
 		elif is_submit == 1:
 			# even if this transaction is the latest one, it should be submitted
 			# for it to be considered for latest purchase rate
@@ -36,12 +38,10 @@
 				frappe.throw(_("UOM Conversion factor is required in row {0}").format(d.idx))
 
 		# update last purchsae rate
-		frappe.db.set_value('Item', d.item_code, 'last_purchase_rate', flt(last_purchase_rate))
+		frappe.db.set_value("Item", d.item_code, "last_purchase_rate", flt(last_purchase_rate))
 
 
-
-
-def validate_for_items(doc):
+def validate_for_items(doc) -> None:
 	items = []
 	for d in doc.get("items"):
 		if not d.qty:
@@ -49,45 +49,87 @@
 				continue
 			frappe.throw(_("Please enter quantity for Item {0}").format(d.item_code))
 
-		# update with latest quantities
-		bin = frappe.db.sql("""select projected_qty from `tabBin` where
-			item_code = %s and warehouse = %s""", (d.item_code, d.warehouse), as_dict=1)
-
-		f_lst ={'projected_qty': bin and flt(bin[0]['projected_qty']) or 0, 'ordered_qty': 0, 'received_qty' : 0}
-		if d.doctype in ('Purchase Receipt Item', 'Purchase Invoice Item'):
-			f_lst.pop('received_qty')
-		for x in f_lst :
-			if d.meta.get_field(x):
-				d.set(x, f_lst[x])
-
-		item = frappe.db.sql("""select is_stock_item,
-			is_sub_contracted_item, end_of_life, disabled from `tabItem` where name=%s""",
-			d.item_code, as_dict=1)[0]
-
+		set_stock_levels(row=d)  # update with latest quantities
+		item = validate_item_and_get_basic_data(row=d)
+		validate_stock_item_warehouse(row=d, item=item)
 		validate_end_of_life(d.item_code, item.end_of_life, item.disabled)
 
-		# validate stock item
-		if item.is_stock_item==1 and d.qty and not d.warehouse and not d.get("delivered_by_supplier"):
-			frappe.throw(_("Warehouse is mandatory for stock Item {0} in row {1}").format(d.item_code, d.idx))
-
 		items.append(cstr(d.item_code))
 
-	if items and len(items) != len(set(items)) and \
-		not cint(frappe.db.get_single_value("Buying Settings", "allow_multiple_items") or 0):
+	if (
+		items
+		and len(items) != len(set(items))
+		and not cint(frappe.db.get_single_value("Buying Settings", "allow_multiple_items") or 0)
+	):
 		frappe.throw(_("Same item cannot be entered multiple times."))
 
-def check_on_hold_or_closed_status(doctype, docname):
+
+def set_stock_levels(row) -> None:
+	projected_qty = frappe.db.get_value(
+		"Bin",
+		{
+			"item_code": row.item_code,
+			"warehouse": row.warehouse,
+		},
+		"projected_qty",
+	)
+
+	qty_data = {
+		"projected_qty": flt(projected_qty),
+		"ordered_qty": 0,
+		"received_qty": 0,
+	}
+	if row.doctype in ("Purchase Receipt Item", "Purchase Invoice Item"):
+		qty_data.pop("received_qty")
+
+	for field in qty_data:
+		if row.meta.get_field(field):
+			row.set(field, qty_data[field])
+
+
+def validate_item_and_get_basic_data(row) -> Dict:
+	item = frappe.db.get_values(
+		"Item",
+		filters={"name": row.item_code},
+		fieldname=["is_stock_item", "is_sub_contracted_item", "end_of_life", "disabled"],
+		as_dict=1,
+	)
+	if not item:
+		frappe.throw(_("Row #{0}: Item {1} does not exist").format(row.idx, frappe.bold(row.item_code)))
+
+	return item[0]
+
+
+def validate_stock_item_warehouse(row, item) -> None:
+	if (
+		item.is_stock_item == 1
+		and row.qty
+		and not row.warehouse
+		and not row.get("delivered_by_supplier")
+	):
+		frappe.throw(
+			_("Row #{1}: Warehouse is mandatory for stock Item {0}").format(
+				frappe.bold(row.item_code), row.idx
+			)
+		)
+
+
+def check_on_hold_or_closed_status(doctype, docname) -> None:
 	status = frappe.db.get_value(doctype, docname, "status")
 
 	if status in ("Closed", "On Hold"):
-		frappe.throw(_("{0} {1} status is {2}").format(doctype, docname, status), frappe.InvalidStatusError)
+		frappe.throw(
+			_("{0} {1} status is {2}").format(doctype, docname, status), frappe.InvalidStatusError
+		)
+
 
 @frappe.whitelist()
 def get_linked_material_requests(items):
 	items = json.loads(items)
 	mr_list = []
 	for item in items:
-		material_request = frappe.db.sql("""SELECT distinct mr.name AS mr_name,
+		material_request = frappe.db.sql(
+			"""SELECT distinct mr.name AS mr_name,
 				(mr_item.qty - mr_item.ordered_qty) AS qty,
 				mr_item.item_code AS item_code,
 				mr_item.name AS mr_item
@@ -98,7 +140,10 @@
 				AND mr.per_ordered < 99.99
 				AND mr.docstatus = 1
 				AND mr.status != 'Stopped'
-                        ORDER BY mr_item.item_code ASC""",{"item": item}, as_dict=1)
+                        ORDER BY mr_item.item_code ASC""",
+			{"item": item},
+			as_dict=1,
+		)
 		if material_request:
 			mr_list.append(material_request)
 
diff --git a/erpnext/commands/__init__.py b/erpnext/commands/__init__.py
index 8e12fad..ddf0acc 100644
--- a/erpnext/commands/__init__.py
+++ b/erpnext/commands/__init__.py
@@ -7,4 +7,5 @@
 def call_command(cmd, context):
 	return click.Context(cmd, obj=context).forward(cmd)
 
+
 commands = []
diff --git a/erpnext/config/education.py b/erpnext/config/education.py
deleted file mode 100644
index d718a94..0000000
--- a/erpnext/config/education.py
+++ /dev/null
@@ -1,299 +0,0 @@
-from frappe import _
-
-
-def get_data():
-	return [
-		{
-			"label": _("Student"),
-			"items": [
-				{
-					"type": "doctype",
-					"name": "Student",
-					"onboard": 1,
-				},
-				{
-					"type": "doctype",
-					"name": "Guardian"
-				},
-				{
-					"type": "doctype",
-					"name": "Student Log"
-				},
-				{
-					"type": "doctype",
-					"name": "Student Group"
-				}
-			]
-		},
-		{
-			"label": _("Admission"),
-			"items": [
-
-				{
-					"type": "doctype",
-					"name": "Student Applicant"
-				},
-				{
-					"type": "doctype",
-					"name": "Web Academy Applicant"
-				},
-				{
-					"type": "doctype",
-					"name": "Student Admission"
-				},
-				{
-					"type": "doctype",
-					"name": "Program Enrollment"
-				}
-			]
-		},
-		{
-			"label": _("Attendance"),
-			"items": [
-				{
-					"type": "doctype",
-					"name": "Student Attendance"
-				},
-				{
-					"type": "doctype",
-					"name": "Student Leave Application"
-				},
-				{
-					"type": "report",
-					"is_query_report": True,
-					"name": "Absent Student Report",
-					"doctype": "Student Attendance"
-				},
-				{
-					"type": "report",
-					"is_query_report": True,
-					"name": "Student Batch-Wise Attendance",
-					"doctype": "Student Attendance"
-				},
-			]
-		},
-		{
-			"label": _("Tools"),
-			"items": [
-				{
-					"type": "doctype",
-					"name": "Student Attendance Tool"
-				},
-				{
-					"type": "doctype",
-					"name": "Assessment Result Tool"
-				},
-				{
-					"type": "doctype",
-					"name": "Student Group Creation Tool"
-				},
-				{
-					"type": "doctype",
-					"name": "Program Enrollment Tool"
-				},
-				{
-					"type": "doctype",
-					"name": "Course Scheduling Tool"
-				}
-			]
-		},
-		{
-			"label": _("Assessment"),
-			"items": [
-				{
-					"type": "doctype",
-					"name": "Assessment Plan"
-				},
-				{
-					"type": "doctype",
-					"name": "Assessment Group",
-					"link": "Tree/Assessment Group",
-				},
-				{
-					"type": "doctype",
-					"name": "Assessment Result"
-				},
-				{
-					"type": "doctype",
-					"name": "Assessment Criteria"
-				}
-			]
-		},
-		{
-			"label": _("Assessment Reports"),
-			"items": [
-				{
-					"type": "report",
-					"is_query_report": True,
-					"name": "Course wise Assessment Report",
-					"doctype": "Assessment Result"
-				},
-				{
-					"type": "report",
-					"is_query_report": True,
-					"name": "Final Assessment Grades",
-					"doctype": "Assessment Result"
-				},
-				{
-					"type": "report",
-					"is_query_report": True,
-					"name": "Assessment Plan Status",
-					"doctype": "Assessment Plan"
-				},
-				{
-					"type": "doctype",
-					"name": "Student Report Generation Tool"
-				}
-			]
-		},
-		{
-			"label": _("Fees"),
-			"items": [
-				{
-					"type": "doctype",
-					"name": "Fees"
-				},
-				{
-					"type": "doctype",
-					"name": "Fee Schedule"
-				},
-				{
-					"type": "doctype",
-					"name": "Fee Structure"
-				},
-				{
-					"type": "doctype",
-					"name": "Fee Category"
-				}
-			]
-		},
-		{
-			"label": _("Schedule"),
-			"items": [
-				{
-					"type": "doctype",
-					"name": "Course Schedule",
-					"route": "/app/List/Course Schedule/Calendar"
-				},
-				{
-					"type": "doctype",
-					"name": "Course Scheduling Tool"
-				}
-			]
-		},
-		{
-			"label": _("Masters"),
-			"items": [
-				{
-					"type": "doctype",
-					"name": "Program",
-				},
-				{
-					"type": "doctype",
-					"name": "Course",
-					"onboard": 1,
-				},
-				{
-					"type": "doctype",
-					"name": "Topic",
-				},
-				{
-					"type": "doctype",
-					"name": "Instructor",
-					"onboard": 1,
-				},
-				{
-					"type": "doctype",
-					"name": "Room",
-					"onboard": 1,
-				}
-			]
-		},
-		{
-			"label": _("Content Masters"),
-			"items": [
-				{
-					"type": "doctype",
-					"name": "Article"
-				},
-				{
-					"type": "doctype",
-					"name": "Video"
-				},
-				{
-					"type": "doctype",
-					"name": "Quiz"
-				}
-			]
-		},
-		{
-			"label": _("LMS Activity"),
-			"items": [
-				{
-					"type": "doctype",
-					"name": "Course Enrollment"
-				},
-				{
-					"type": "doctype",
-					"name": "Course Activity"
-				},
-				{
-					"type": "doctype",
-					"name": "Quiz Activity"
-				}
-			]
-		},
-		{
-			"label": _("Settings"),
-			"items": [
-				{
-					"type": "doctype",
-					"name": "Student Category"
-				},
-				{
-					"type": "doctype",
-					"name": "Student Batch Name"
-				},
-				{
-					"type": "doctype",
-					"name": "Grading Scale",
-					"onboard": 1,
-				},
-				{
-					"type": "doctype",
-					"name": "Academic Term"
-				},
-				{
-					"type": "doctype",
-					"name": "Academic Year"
-				},
-				{
-					"type": "doctype",
-					"name": "Education Settings"
-				}
-			]
-		},
-		{
-			"label": _("Other Reports"),
-			"items": [
-				{
-					"type": "report",
-					"is_query_report": True,
-					"name": "Student and Guardian Contact Details",
-					"doctype": "Program Enrollment"
-				},
-				{
-					"type": "report",
-					"is_query_report": True,
-					"name": "Student Monthly Attendance Sheet",
-					"doctype": "Student Attendance"
-				},
-				{
-					"type": "report",
-					"name": "Student Fee Collection",
-					"doctype": "Fees",
-					"is_query_report": True
-				}
-			]
-		}
-	]
diff --git a/erpnext/config/projects.py b/erpnext/config/projects.py
index f4675e7..6186a4e 100644
--- a/erpnext/config/projects.py
+++ b/erpnext/config/projects.py
@@ -44,7 +44,7 @@
 					"description": _("Project Update."),
 					"dependencies": ["Project"],
 				},
-			]
+			],
 		},
 		{
 			"label": _("Time Tracking"),
@@ -67,7 +67,7 @@
 					"description": _("Cost of various activities"),
 					"dependencies": ["Activity Type"],
 				},
-			]
+			],
 		},
 		{
 			"label": _("Reports"),
@@ -95,7 +95,6 @@
 					"doctype": "Project",
 					"dependencies": ["Project"],
 				},
-			]
+			],
 		},
-
 	]
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index a94af10..ded9a30 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -34,6 +34,7 @@
 from erpnext.accounts.party import (
 	get_party_account,
 	get_party_account_currency,
+	get_party_gle_currency,
 	validate_party_frozen_disabled,
 )
 from erpnext.accounts.utils import get_account_currency, get_fiscal_years, validate_fiscal_year
@@ -45,6 +46,7 @@
 from erpnext.controllers.sales_and_purchase_return import validate_return
 from erpnext.exceptions import InvalidCurrency
 from erpnext.setup.utils import get_exchange_rate
+from erpnext.stock.doctype.item.item import get_uom_conv_factor
 from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
 from erpnext.stock.get_item_details import (
 	_get_item_tax_template,
@@ -56,10 +58,22 @@
 from erpnext.utilities.transaction_base import TransactionBase
 
 
-class AccountMissingError(frappe.ValidationError): pass
+class AccountMissingError(frappe.ValidationError):
+	pass
 
-force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate",
-	"pricing_rules", "weight_per_unit", "weight_uom", "total_weight")
+
+force_item_fields = (
+	"item_group",
+	"brand",
+	"stock_uom",
+	"is_fixed_asset",
+	"item_tax_rate",
+	"pricing_rules",
+	"weight_per_unit",
+	"weight_uom",
+	"total_weight",
+)
+
 
 class AccountsController(TransactionBase):
 	def __init__(self, *args, **kwargs):
@@ -67,14 +81,14 @@
 
 	def get_print_settings(self):
 		print_setting_fields = []
-		items_field = self.meta.get_field('items')
+		items_field = self.meta.get_field("items")
 
-		if items_field and items_field.fieldtype == 'Table':
-			print_setting_fields += ['compact_item_print', 'print_uom_after_quantity']
+		if items_field and items_field.fieldtype == "Table":
+			print_setting_fields += ["compact_item_print", "print_uom_after_quantity"]
 
-		taxes_field = self.meta.get_field('taxes')
-		if taxes_field and taxes_field.fieldtype == 'Table':
-			print_setting_fields += ['print_taxes_with_zero_amount']
+		taxes_field = self.meta.get_field("taxes")
+		if taxes_field and taxes_field.fieldtype == "Table":
+			print_setting_fields += ["print_taxes_with_zero_amount"]
 
 		return print_setting_fields
 
@@ -86,34 +100,44 @@
 		return self.__company_currency
 
 	def onload(self):
-		self.set_onload("make_payment_via_journal_entry",
-			frappe.db.get_single_value('Accounts Settings', 'make_payment_via_journal_entry'))
+		self.set_onload(
+			"make_payment_via_journal_entry",
+			frappe.db.get_single_value("Accounts Settings", "make_payment_via_journal_entry"),
+		)
 
 		if self.is_new():
-			relevant_docs = ("Quotation", "Purchase Order", "Sales Order",
-							 "Purchase Invoice", "Sales Invoice")
+			relevant_docs = (
+				"Quotation",
+				"Purchase Order",
+				"Sales Order",
+				"Purchase Invoice",
+				"Sales Invoice",
+			)
 			if self.doctype in relevant_docs:
 				self.set_payment_schedule()
 
 	def ensure_supplier_is_not_blocked(self):
-		is_supplier_payment = self.doctype == 'Payment Entry' and self.party_type == 'Supplier'
-		is_buying_invoice = self.doctype in ['Purchase Invoice', 'Purchase Order']
+		is_supplier_payment = self.doctype == "Payment Entry" and self.party_type == "Supplier"
+		is_buying_invoice = self.doctype in ["Purchase Invoice", "Purchase Order"]
 		supplier = None
 		supplier_name = None
 
 		if is_buying_invoice or is_supplier_payment:
 			supplier_name = self.supplier if is_buying_invoice else self.party
-			supplier = frappe.get_doc('Supplier', supplier_name)
+			supplier = frappe.get_doc("Supplier", supplier_name)
 
 		if supplier and supplier_name and supplier.on_hold:
-			if (is_buying_invoice and supplier.hold_type in ['All', 'Invoices']) or \
-					(is_supplier_payment and supplier.hold_type in ['All', 'Payments']):
+			if (is_buying_invoice and supplier.hold_type in ["All", "Invoices"]) or (
+				is_supplier_payment and supplier.hold_type in ["All", "Payments"]
+			):
 				if not supplier.release_date or getdate(nowdate()) <= supplier.release_date:
 					frappe.msgprint(
-						_('{0} is blocked so this transaction cannot proceed').format(supplier_name), raise_exception=1)
+						_("{0} is blocked so this transaction cannot proceed").format(supplier_name),
+						raise_exception=1,
+					)
 
 	def validate(self):
-		if not self.get('is_return') and not self.get('is_debit_note'):
+		if not self.get("is_return") and not self.get("is_debit_note"):
 			self.validate_qty_is_not_zero()
 
 		if self.get("_action") and self._action != "update_after_submit":
@@ -126,6 +150,7 @@
 
 		self.validate_inter_company_reference()
 
+		self.disable_pricing_rule_on_internal_transfer()
 		self.set_incoming_rate()
 
 		if self.meta.get_field("currency"):
@@ -145,9 +170,10 @@
 
 		self.validate_party()
 		self.validate_currency()
+		self.validate_party_account_currency()
 
-		if self.doctype in ['Purchase Invoice', 'Sales Invoice']:
-			pos_check_field = "is_pos" if self.doctype=="Sales Invoice" else "is_paid"
+		if self.doctype in ["Purchase Invoice", "Sales Invoice"]:
+			pos_check_field = "is_pos" if self.doctype == "Sales Invoice" else "is_paid"
 			if cint(self.allocate_advances_automatically) and not cint(self.get(pos_check_field)):
 				self.set_advances()
 
@@ -158,9 +184,10 @@
 			else:
 				self.validate_deferred_start_and_end_date()
 
+			self.validate_deferred_income_expense_account()
 			self.set_inter_company_account()
 
-		if self.doctype == 'Purchase Invoice':
+		if self.doctype == "Purchase Invoice":
 			self.calculate_paid_amount()
 			# apply tax withholding only if checked and applicable
 			self.set_tax_withholding()
@@ -169,7 +196,7 @@
 
 		validate_einvoice_fields(self)
 
-		if self.doctype != 'Material Request':
+		if self.doctype != "Material Request":
 			apply_pricing_rule_on_transaction(self)
 
 	def before_cancel(self):
@@ -177,26 +204,58 @@
 
 	def on_trash(self):
 		# delete sl and gl entries on deletion of transaction
-		if frappe.db.get_single_value('Accounts Settings', 'delete_linked_ledger_entries'):
-			frappe.db.sql("delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s", (self.doctype, self.name))
-			frappe.db.sql("delete from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s", (self.doctype, self.name))
+		if frappe.db.get_single_value("Accounts Settings", "delete_linked_ledger_entries"):
+			frappe.db.sql(
+				"delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s", (self.doctype, self.name)
+			)
+			frappe.db.sql(
+				"delete from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s",
+				(self.doctype, self.name),
+			)
+
+	def validate_deferred_income_expense_account(self):
+		field_map = {
+			"Sales Invoice": "deferred_revenue_account",
+			"Purchase Invoice": "deferred_expense_account",
+		}
+
+		for item in self.get("items"):
+			if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"):
+				if not item.get(field_map.get(self.doctype)):
+					default_deferred_account = frappe.db.get_value(
+						"Company", self.company, "default_" + field_map.get(self.doctype)
+					)
+					if not default_deferred_account:
+						frappe.throw(
+							_(
+								"Row #{0}: Please update deferred revenue/expense account in item row or default account in company master"
+							).format(item.idx)
+						)
+					else:
+						item.set(field_map.get(self.doctype), default_deferred_account)
 
 	def validate_deferred_start_and_end_date(self):
 		for d in self.items:
 			if d.get("enable_deferred_revenue") or d.get("enable_deferred_expense"):
 				if not (d.service_start_date and d.service_end_date):
-					frappe.throw(_("Row #{0}: Service Start and End Date is required for deferred accounting").format(d.idx))
+					frappe.throw(
+						_("Row #{0}: Service Start and End Date is required for deferred accounting").format(d.idx)
+					)
 				elif getdate(d.service_start_date) > getdate(d.service_end_date):
-					frappe.throw(_("Row #{0}: Service Start Date cannot be greater than Service End Date").format(d.idx))
+					frappe.throw(
+						_("Row #{0}: Service Start Date cannot be greater than Service End Date").format(d.idx)
+					)
 				elif getdate(self.posting_date) > getdate(d.service_end_date):
-					frappe.throw(_("Row #{0}: Service End Date cannot be before Invoice Posting Date").format(d.idx))
+					frappe.throw(
+						_("Row #{0}: Service End Date cannot be before Invoice Posting Date").format(d.idx)
+					)
 
 	def validate_invoice_documents_schedule(self):
 		self.validate_payment_schedule_dates()
 		self.set_due_date()
 		self.set_payment_schedule()
 		self.validate_payment_schedule_amount()
-		if not self.get('ignore_default_payment_terms_template'):
+		if not self.get("ignore_default_payment_terms_template"):
 			self.validate_due_date()
 		self.validate_advance_entries()
 
@@ -212,8 +271,16 @@
 			self.validate_non_invoice_documents_schedule()
 
 	def before_print(self, settings=None):
-		if self.doctype in ['Purchase Order', 'Sales Order', 'Sales Invoice', 'Purchase Invoice',
-							'Supplier Quotation', 'Purchase Receipt', 'Delivery Note', 'Quotation']:
+		if self.doctype in [
+			"Purchase Order",
+			"Sales Order",
+			"Sales Invoice",
+			"Purchase Invoice",
+			"Supplier Quotation",
+			"Purchase Receipt",
+			"Delivery Note",
+			"Quotation",
+		]:
 			if self.get("group_same_items"):
 				self.group_similar_items()
 
@@ -234,7 +301,9 @@
 			if is_paid:
 				if not self.cash_bank_account:
 					# show message that the amount is not paid
-					frappe.throw(_("Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified"))
+					frappe.throw(
+						_("Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified")
+					)
 
 				if cint(self.is_return) and self.grand_total > self.paid_amount:
 					self.paid_amount = flt(flt(self.grand_total), self.precision("paid_amount"))
@@ -242,8 +311,9 @@
 				elif not flt(self.paid_amount) and flt(self.outstanding_amount) > 0:
 					self.paid_amount = flt(flt(self.outstanding_amount), self.precision("paid_amount"))
 
-				self.base_paid_amount = flt(self.paid_amount * self.conversion_rate,
-										self.precision("base_paid_amount"))
+				self.base_paid_amount = flt(
+					self.paid_amount * self.conversion_rate, self.precision("base_paid_amount")
+				)
 
 	def set_missing_values(self, for_validate=False):
 		if frappe.flags.in_test:
@@ -254,13 +324,14 @@
 
 	def calculate_taxes_and_totals(self):
 		from erpnext.controllers.taxes_and_totals import calculate_taxes_and_totals
+
 		calculate_taxes_and_totals(self)
 
 		if self.doctype in (
-			'Sales Order',
-			'Delivery Note',
-			'Sales Invoice',
-			'POS Invoice',
+			"Sales Order",
+			"Delivery Note",
+			"Sales Invoice",
+			"POS Invoice",
 		):
 			self.calculate_commission()
 			self.calculate_contribution()
@@ -274,50 +345,83 @@
 				date_field = "transaction_date"
 
 			if date_field and self.get(date_field):
-				validate_fiscal_year(self.get(date_field), self.fiscal_year, self.company,
-									 self.meta.get_label(date_field), self)
+				validate_fiscal_year(
+					self.get(date_field), self.fiscal_year, self.company, self.meta.get_label(date_field), self
+				)
 
 	def validate_party_accounts(self):
-		if self.doctype not in ('Sales Invoice', 'Purchase Invoice'):
+		if self.doctype not in ("Sales Invoice", "Purchase Invoice"):
 			return
 
-		if self.doctype == 'Sales Invoice':
-			party_account_field = 'debit_to'
-			item_field = 'income_account'
+		if self.doctype == "Sales Invoice":
+			party_account_field = "debit_to"
+			item_field = "income_account"
 		else:
-			party_account_field = 'credit_to'
-			item_field = 'expense_account'
+			party_account_field = "credit_to"
+			item_field = "expense_account"
 
-		for item in self.get('items'):
+		for item in self.get("items"):
 			if item.get(item_field) == self.get(party_account_field):
-				frappe.throw(_("Row {0}: {1} {2} cannot be same as {3} (Party Account) {4}").format(item.idx,
-					frappe.bold(frappe.unscrub(item_field)), item.get(item_field),
-					frappe.bold(frappe.unscrub(party_account_field)), self.get(party_account_field)))
+				frappe.throw(
+					_("Row {0}: {1} {2} cannot be same as {3} (Party Account) {4}").format(
+						item.idx,
+						frappe.bold(frappe.unscrub(item_field)),
+						item.get(item_field),
+						frappe.bold(frappe.unscrub(party_account_field)),
+						self.get(party_account_field),
+					)
+				)
 
 	def validate_inter_company_reference(self):
-		if self.doctype not in ('Purchase Invoice', 'Purchase Receipt', 'Purchase Order'):
+		if self.doctype not in ("Purchase Invoice", "Purchase Receipt", "Purchase Order"):
 			return
 
 		if self.is_internal_transfer():
-			if not (self.get('inter_company_reference') or self.get('inter_company_invoice_reference')
-				or self.get('inter_company_order_reference')):
+			if not (
+				self.get("inter_company_reference")
+				or self.get("inter_company_invoice_reference")
+				or self.get("inter_company_order_reference")
+			):
 				msg = _("Internal Sale or Delivery Reference missing.")
 				msg += _("Please create purchase from internal sale or delivery document itself")
 				frappe.throw(msg, title=_("Internal Sales Reference Missing"))
 
+	def disable_pricing_rule_on_internal_transfer(self):
+		if not self.get("ignore_pricing_rule") and self.is_internal_transfer():
+			self.ignore_pricing_rule = 1
+			frappe.msgprint(
+				_("Disabled pricing rules since this {} is an internal transfer").format(self.doctype),
+				alert=1,
+			)
+
 	def validate_due_date(self):
-		if self.get('is_pos'): return
+		if self.get("is_pos"):
+			return
 
 		from erpnext.accounts.party import validate_due_date
+
 		if self.doctype == "Sales Invoice":
 			if not self.due_date:
 				frappe.throw(_("Due Date is mandatory"))
 
-			validate_due_date(self.posting_date, self.due_date,
-				"Customer", self.customer, self.company, self.payment_terms_template)
+			validate_due_date(
+				self.posting_date,
+				self.due_date,
+				"Customer",
+				self.customer,
+				self.company,
+				self.payment_terms_template,
+			)
 		elif self.doctype == "Purchase Invoice":
-			validate_due_date(self.bill_date or self.posting_date, self.due_date,
-				"Supplier", self.supplier, self.company, self.bill_date, self.payment_terms_template)
+			validate_due_date(
+				self.bill_date or self.posting_date,
+				self.due_date,
+				"Supplier",
+				self.supplier,
+				self.company,
+				self.bill_date,
+				self.payment_terms_template,
+			)
 
 	def set_price_list_currency(self, buying_or_selling):
 		if self.meta.get_field("posting_date"):
@@ -335,15 +439,15 @@
 				args = "for_buying"
 
 			if self.meta.get_field(fieldname) and self.get(fieldname):
-				self.price_list_currency = frappe.db.get_value("Price List",
-															   self.get(fieldname), "currency")
+				self.price_list_currency = frappe.db.get_value("Price List", self.get(fieldname), "currency")
 
 				if self.price_list_currency == self.company_currency:
 					self.plc_conversion_rate = 1.0
 
 				elif not self.plc_conversion_rate:
-					self.plc_conversion_rate = get_exchange_rate(self.price_list_currency,
-																 self.company_currency, transaction_date, args)
+					self.plc_conversion_rate = get_exchange_rate(
+						self.price_list_currency, self.company_currency, transaction_date, args
+					)
 
 			# currency
 			if not self.currency:
@@ -352,8 +456,9 @@
 			elif self.currency == self.company_currency:
 				self.conversion_rate = 1.0
 			elif not self.conversion_rate:
-				self.conversion_rate = get_exchange_rate(self.currency,
-														 self.company_currency, transaction_date, args)
+				self.conversion_rate = get_exchange_rate(
+					self.currency, self.company_currency, transaction_date, args
+				)
 
 	def set_missing_item_details(self, for_validate=False):
 		"""set missing item values"""
@@ -369,7 +474,11 @@
 				parent_dict.update({"document_type": document_type})
 
 			# party_name field used for customer in quotation
-			if self.doctype == "Quotation" and self.quotation_to == "Customer" and parent_dict.get("party_name"):
+			if (
+				self.doctype == "Quotation"
+				and self.quotation_to == "Customer"
+				and parent_dict.get("party_name")
+			):
 				parent_dict.update({"customer": parent_dict.get("party_name")})
 
 			self.pricing_rules = []
@@ -381,7 +490,9 @@
 					args["doctype"] = self.doctype
 					args["name"] = self.name
 					args["child_docname"] = item.name
-					args["ignore_pricing_rule"] = self.ignore_pricing_rule if hasattr(self, 'ignore_pricing_rule') else 0
+					args["ignore_pricing_rule"] = (
+						self.ignore_pricing_rule if hasattr(self, "ignore_pricing_rule") else 0
+					)
 
 					if not args.get("transaction_date"):
 						args["transaction_date"] = args.get("posting_date")
@@ -393,10 +504,10 @@
 
 					for fieldname, value in ret.items():
 						if item.meta.get_field(fieldname) and value is not None:
-							if (item.get(fieldname) is None or fieldname in force_item_fields):
+							if item.get(fieldname) is None or fieldname in force_item_fields:
 								item.set(fieldname, value)
 
-							elif fieldname in ['cost_center', 'conversion_factor'] and not item.get(fieldname):
+							elif fieldname in ["cost_center", "conversion_factor"] and not item.get(fieldname):
 								item.set(fieldname, value)
 
 							elif fieldname == "serial_no":
@@ -404,7 +515,7 @@
 								item_conversion_factor = item.get("conversion_factor") or 1.0
 								item_qty = abs(item.get("qty")) * item_conversion_factor
 
-								if item_qty != len(get_serial_nos(item.get('serial_no'))):
+								if item_qty != len(get_serial_nos(item.get("serial_no"))):
 									item.set(fieldname, value)
 
 							elif (
@@ -423,17 +534,30 @@
 								# reset pricing rule fields if pricing_rule_removed
 								item.set(fieldname, value)
 
-					if self.doctype in ["Purchase Invoice", "Sales Invoice"] and item.meta.get_field('is_fixed_asset'):
-						item.set('is_fixed_asset', ret.get('is_fixed_asset', 0))
+					if self.doctype in ["Purchase Invoice", "Sales Invoice"] and item.meta.get_field(
+						"is_fixed_asset"
+					):
+						item.set("is_fixed_asset", ret.get("is_fixed_asset", 0))
 
 					# Double check for cost center
 					# Items add via promotional scheme may not have cost center set
-					if hasattr(item, 'cost_center') and not item.get('cost_center'):
-						item.set('cost_center', self.get('cost_center') or erpnext.get_default_cost_center(self.company))
+					if hasattr(item, "cost_center") and not item.get("cost_center"):
+						item.set(
+							"cost_center", self.get("cost_center") or erpnext.get_default_cost_center(self.company)
+						)
 
 					if ret.get("pricing_rules"):
 						self.apply_pricing_rule_on_items(item, ret)
 						self.set_pricing_rule_details(item, ret)
+				else:
+					# Transactions line item without item code
+
+					uom = item.get("uom")
+					stock_uom = item.get("stock_uom")
+					if bool(uom) != bool(stock_uom):  # xor
+						item.stock_uom = item.uom = uom or stock_uom
+
+					item.conversion_factor = get_uom_conv_factor(item.get("uom"), item.get("stock_uom"))
 
 			if self.doctype == "Purchase Invoice":
 				self.set_expense_account(for_validate)
@@ -441,7 +565,7 @@
 	def apply_pricing_rule_on_items(self, item, pricing_rule_args):
 		if not pricing_rule_args.get("validate_applied_rule", 0):
 			# if user changed the discount percentage then set user's discount percentage ?
-			if pricing_rule_args.get("price_or_product_discount") == 'Price':
+			if pricing_rule_args.get("price_or_product_discount") == "Price":
 				item.set("pricing_rules", pricing_rule_args.get("pricing_rules"))
 				item.set("discount_percentage", pricing_rule_args.get("discount_percentage"))
 				item.set("discount_amount", pricing_rule_args.get("discount_amount"))
@@ -449,39 +573,48 @@
 					item.set("price_list_rate", pricing_rule_args.get("price_list_rate"))
 
 				if item.get("price_list_rate"):
-					item.rate = flt(item.price_list_rate *
-						(1.0 - (flt(item.discount_percentage) / 100.0)), item.precision("rate"))
+					item.rate = flt(
+						item.price_list_rate * (1.0 - (flt(item.discount_percentage) / 100.0)),
+						item.precision("rate"),
+					)
 
-					if item.get('discount_amount'):
+					if item.get("discount_amount"):
 						item.rate = item.price_list_rate - item.discount_amount
 
 				if item.get("apply_discount_on_discounted_rate") and pricing_rule_args.get("rate"):
 					item.rate = pricing_rule_args.get("rate")
 
-			elif pricing_rule_args.get('free_item_data'):
-				apply_pricing_rule_for_free_items(self, pricing_rule_args.get('free_item_data'))
+			elif pricing_rule_args.get("free_item_data"):
+				apply_pricing_rule_for_free_items(self, pricing_rule_args.get("free_item_data"))
 
 		elif pricing_rule_args.get("validate_applied_rule"):
-			for pricing_rule in get_applied_pricing_rules(item.get('pricing_rules')):
+			for pricing_rule in get_applied_pricing_rules(item.get("pricing_rules")):
 				pricing_rule_doc = frappe.get_cached_doc("Pricing Rule", pricing_rule)
-				for field in ['discount_percentage', 'discount_amount', 'rate']:
+				for field in ["discount_percentage", "discount_amount", "rate"]:
 					if item.get(field) < pricing_rule_doc.get(field):
 						title = get_link_to_form("Pricing Rule", pricing_rule)
 
-						frappe.msgprint(_("Row {0}: user has not applied the rule {1} on the item {2}")
-							.format(item.idx, frappe.bold(title), frappe.bold(item.item_code)))
+						frappe.msgprint(
+							_("Row {0}: user has not applied the rule {1} on the item {2}").format(
+								item.idx, frappe.bold(title), frappe.bold(item.item_code)
+							)
+						)
 
 	def set_pricing_rule_details(self, item_row, args):
 		pricing_rules = get_applied_pricing_rules(args.get("pricing_rules"))
-		if not pricing_rules: return
+		if not pricing_rules:
+			return
 
 		for pricing_rule in pricing_rules:
-			self.append("pricing_rules", {
-				"pricing_rule": pricing_rule,
-				"item_code": item_row.item_code,
-				"child_docname": item_row.name,
-				"rule_applied": True
-			})
+			self.append(
+				"pricing_rules",
+				{
+					"pricing_rule": pricing_rule,
+					"item_code": item_row.item_code,
+					"child_docname": item_row.name,
+					"rule_applied": True,
+				},
+			)
 
 	def set_taxes(self):
 		if not self.meta.get_field("taxes"):
@@ -492,14 +625,18 @@
 		if (self.is_new() or self.is_pos_profile_changed()) and not self.get("taxes"):
 			if self.company and not self.get("taxes_and_charges"):
 				# get the default tax master
-				self.taxes_and_charges = frappe.db.get_value(tax_master_doctype,
-															 {"is_default": 1, 'company': self.company})
+				self.taxes_and_charges = frappe.db.get_value(
+					tax_master_doctype, {"is_default": 1, "company": self.company}
+				)
 
 			self.append_taxes_from_master(tax_master_doctype)
 
 	def is_pos_profile_changed(self):
-		if (self.doctype == 'Sales Invoice' and self.is_pos and
-				self.pos_profile != frappe.db.get_value('Sales Invoice', self.name, 'pos_profile')):
+		if (
+			self.doctype == "Sales Invoice"
+			and self.is_pos
+			and self.pos_profile != frappe.db.get_value("Sales Invoice", self.name, "pos_profile")
+		):
 			return True
 
 	def append_taxes_from_master(self, tax_master_doctype=None):
@@ -516,44 +653,54 @@
 	def validate_enabled_taxes_and_charges(self):
 		taxes_and_charges_doctype = self.meta.get_options("taxes_and_charges")
 		if frappe.db.get_value(taxes_and_charges_doctype, self.taxes_and_charges, "disabled"):
-			frappe.throw(_("{0} '{1}' is disabled").format(taxes_and_charges_doctype, self.taxes_and_charges))
+			frappe.throw(
+				_("{0} '{1}' is disabled").format(taxes_and_charges_doctype, self.taxes_and_charges)
+			)
 
 	def validate_tax_account_company(self):
 		for d in self.get("taxes"):
 			if d.account_head:
 				tax_account_company = frappe.db.get_value("Account", d.account_head, "company")
 				if tax_account_company != self.company:
-					frappe.throw(_("Row #{0}: Account {1} does not belong to company {2}")
-								 .format(d.idx, d.account_head, self.company))
+					frappe.throw(
+						_("Row #{0}: Account {1} does not belong to company {2}").format(
+							d.idx, d.account_head, self.company
+						)
+					)
 
 	def get_gl_dict(self, args, account_currency=None, item=None):
 		"""this method populates the common properties of a gl entry record"""
 
-		posting_date = args.get('posting_date') or self.get('posting_date')
+		posting_date = args.get("posting_date") or self.get("posting_date")
 		fiscal_years = get_fiscal_years(posting_date, company=self.company)
 		if len(fiscal_years) > 1:
-			frappe.throw(_("Multiple fiscal years exist for the date {0}. Please set company in Fiscal Year").format(
-				formatdate(posting_date)))
+			frappe.throw(
+				_("Multiple fiscal years exist for the date {0}. Please set company in Fiscal Year").format(
+					formatdate(posting_date)
+				)
+			)
 		else:
 			fiscal_year = fiscal_years[0][0]
 
-		gl_dict = frappe._dict({
-			'company': self.company,
-			'posting_date': posting_date,
-			'fiscal_year': fiscal_year,
-			'voucher_type': self.doctype,
-			'voucher_no': self.name,
-			'remarks': self.get("remarks") or self.get("remark"),
-			'debit': 0,
-			'credit': 0,
-			'debit_in_account_currency': 0,
-			'credit_in_account_currency': 0,
-			'is_opening': self.get("is_opening") or "No",
-			'party_type': None,
-			'party': None,
-			'project': self.get("project"),
-			'post_net_value': args.get('post_net_value')
-		})
+		gl_dict = frappe._dict(
+			{
+				"company": self.company,
+				"posting_date": posting_date,
+				"fiscal_year": fiscal_year,
+				"voucher_type": self.doctype,
+				"voucher_no": self.name,
+				"remarks": self.get("remarks") or self.get("remark"),
+				"debit": 0,
+				"credit": 0,
+				"debit_in_account_currency": 0,
+				"credit_in_account_currency": 0,
+				"is_opening": self.get("is_opening") or "No",
+				"party_type": None,
+				"party": None,
+				"project": self.get("project"),
+				"post_net_value": args.get("post_net_value"),
+			}
+		)
 
 		accounting_dimensions = get_accounting_dimensions()
 		dimension_dict = frappe._dict()
@@ -569,13 +716,24 @@
 		if not account_currency:
 			account_currency = get_account_currency(gl_dict.account)
 
-		if gl_dict.account and self.doctype not in ["Journal Entry",
-			"Period Closing Voucher", "Payment Entry", "Purchase Receipt", "Purchase Invoice", "Stock Entry"]:
+		if gl_dict.account and self.doctype not in [
+			"Journal Entry",
+			"Period Closing Voucher",
+			"Payment Entry",
+			"Purchase Receipt",
+			"Purchase Invoice",
+			"Stock Entry",
+		]:
 			self.validate_account_currency(gl_dict.account, account_currency)
 
-		if gl_dict.account and self.doctype not in ["Journal Entry", "Period Closing Voucher", "Payment Entry"]:
-			set_balance_in_account_currency(gl_dict, account_currency, self.get("conversion_rate"),
-											self.company_currency)
+		if gl_dict.account and self.doctype not in [
+			"Journal Entry",
+			"Period Closing Voucher",
+			"Payment Entry",
+		]:
+			set_balance_in_account_currency(
+				gl_dict, account_currency, self.get("conversion_rate"), self.company_currency
+			)
 
 		return gl_dict
 
@@ -591,14 +749,21 @@
 			valid_currency.append(self.currency)
 
 		if account_currency not in valid_currency:
-			frappe.throw(_("Account {0} is invalid. Account Currency must be {1}")
-				.format(account, (' ' + _("or") + ' ').join(valid_currency)))
+			frappe.throw(
+				_("Account {0} is invalid. Account Currency must be {1}").format(
+					account, (" " + _("or") + " ").join(valid_currency)
+				)
+			)
 
 	def clear_unallocated_advances(self, childtype, parentfield):
 		self.set(parentfield, self.get(parentfield, {"allocated_amount": ["not in", [0, None, ""]]}))
 
-		frappe.db.sql("""delete from `tab%s` where parentfield=%s and parent = %s
-			and allocated_amount = 0""" % (childtype, '%s', '%s'), (parentfield, self.name))
+		frappe.db.sql(
+			"""delete from `tab%s` where parentfield=%s and parent = %s
+			and allocated_amount = 0"""
+			% (childtype, "%s", "%s"),
+			(parentfield, self.name),
+		)
 
 	@frappe.whitelist()
 	def apply_shipping_rule(self):
@@ -608,16 +773,16 @@
 			self.calculate_taxes_and_totals()
 
 	def get_shipping_address(self):
-		'''Returns Address object from shipping address fields if present'''
+		"""Returns Address object from shipping address fields if present"""
 
 		# shipping address fields can be `shipping_address_name` or `shipping_address`
 		# try getting value from both
 
-		for fieldname in ('shipping_address_name', 'shipping_address'):
+		for fieldname in ("shipping_address_name", "shipping_address"):
 			shipping_field = self.meta.get_field(fieldname)
-			if shipping_field and shipping_field.fieldtype == 'Link':
+			if shipping_field and shipping_field.fieldtype == "Link":
 				if self.get(fieldname):
-					return frappe.get_doc('Address', self.get(fieldname))
+					return frappe.get_doc("Address", self.get(fieldname))
 
 		return {}
 
@@ -633,10 +798,10 @@
 			if d.against_order:
 				allocated_amount = flt(d.amount)
 			else:
-				if self.get('party_account_currency') == self.company_currency:
-					amount = self.get('base_rounded_total') or self.base_grand_total
+				if self.get("party_account_currency") == self.company_currency:
+					amount = self.get("base_rounded_total") or self.base_grand_total
 				else:
-					amount = self.get('rounded_total') or self.grand_total
+					amount = self.get("rounded_total") or self.grand_total
 
 				allocated_amount = min(amount - advance_allocated, d.amount)
 			advance_allocated += flt(allocated_amount)
@@ -649,7 +814,7 @@
 				"remarks": d.remarks,
 				"advance_amount": flt(d.amount),
 				"allocated_amount": allocated_amount,
-				"ref_exchange_rate": flt(d.exchange_rate) # exchange_rate of advance entry
+				"ref_exchange_rate": flt(d.exchange_rate),  # exchange_rate of advance entry
 			}
 
 			self.append("advances", advance_row)
@@ -670,21 +835,24 @@
 			order_field = "purchase_order"
 			order_doctype = "Purchase Order"
 
-		order_list = list(set(d.get(order_field)
-			for d in self.get("items") if d.get(order_field)))
+		order_list = list(set(d.get(order_field) for d in self.get("items") if d.get(order_field)))
 
-		journal_entries = get_advance_journal_entries(party_type, party, party_account,
-			amount_field, order_doctype, order_list, include_unallocated)
+		journal_entries = get_advance_journal_entries(
+			party_type, party, party_account, amount_field, order_doctype, order_list, include_unallocated
+		)
 
-		payment_entries = get_advance_payment_entries(party_type, party, party_account,
-			order_doctype, order_list, include_unallocated)
+		payment_entries = get_advance_payment_entries(
+			party_type, party, party_account, order_doctype, order_list, include_unallocated
+		)
 
 		res = journal_entries + payment_entries
 
 		return res
 
 	def is_inclusive_tax(self):
-		is_inclusive = cint(frappe.db.get_single_value("Accounts Settings", "show_inclusive_tax_in_print"))
+		is_inclusive = cint(
+			frappe.db.get_single_value("Accounts Settings", "show_inclusive_tax_in_print")
+		)
 
 		if is_inclusive:
 			is_inclusive = 0
@@ -695,10 +863,10 @@
 
 	def validate_advance_entries(self):
 		order_field = "sales_order" if self.doctype == "Sales Invoice" else "purchase_order"
-		order_list = list(set(d.get(order_field)
-			for d in self.get("items") if d.get(order_field)))
+		order_list = list(set(d.get(order_field) for d in self.get("items") if d.get(order_field)))
 
-		if not order_list: return
+		if not order_list:
+			return
 
 		advance_entries = self.get_advance_entries(include_unallocated=False)
 
@@ -706,22 +874,24 @@
 			advance_entries_against_si = [d.reference_name for d in self.get("advances")]
 			for d in advance_entries:
 				if not advance_entries_against_si or d.reference_name not in advance_entries_against_si:
-					frappe.msgprint(_(
-						"Payment Entry {0} is linked against Order {1}, check if it should be pulled as advance in this invoice.")
-							.format(d.reference_name, d.against_order))
+					frappe.msgprint(
+						_(
+							"Payment Entry {0} is linked against Order {1}, check if it should be pulled as advance in this invoice."
+						).format(d.reference_name, d.against_order)
+					)
 
 	def set_advance_gain_or_loss(self):
-		if self.get('conversion_rate') == 1 or not self.get("advances"):
+		if self.get("conversion_rate") == 1 or not self.get("advances"):
 			return
 
-		is_purchase_invoice = self.doctype == 'Purchase Invoice'
+		is_purchase_invoice = self.doctype == "Purchase Invoice"
 		party_account = self.credit_to if is_purchase_invoice else self.debit_to
 		if get_account_currency(party_account) != self.currency:
 			return
 
 		for d in self.get("advances"):
 			advance_exchange_rate = d.ref_exchange_rate
-			if (d.allocated_amount and self.conversion_rate != advance_exchange_rate):
+			if d.allocated_amount and self.conversion_rate != advance_exchange_rate:
 
 				base_allocated_amount_in_ref_rate = advance_exchange_rate * d.allocated_amount
 				base_allocated_amount_in_inv_rate = self.conversion_rate * d.allocated_amount
@@ -730,61 +900,71 @@
 				d.exchange_gain_loss = difference
 
 	def make_exchange_gain_loss_gl_entries(self, gl_entries):
-		if self.get('doctype') in ['Purchase Invoice', 'Sales Invoice']:
+		if self.get("doctype") in ["Purchase Invoice", "Sales Invoice"]:
 			for d in self.get("advances"):
 				if d.exchange_gain_loss:
-					is_purchase_invoice = self.get('doctype') == 'Purchase Invoice'
+					is_purchase_invoice = self.get("doctype") == "Purchase Invoice"
 					party = self.supplier if is_purchase_invoice else self.customer
 					party_account = self.credit_to if is_purchase_invoice else self.debit_to
 					party_type = "Supplier" if is_purchase_invoice else "Customer"
 
-					gain_loss_account = frappe.db.get_value('Company', self.company, 'exchange_gain_loss_account')
+					gain_loss_account = frappe.db.get_value("Company", self.company, "exchange_gain_loss_account")
 					if not gain_loss_account:
-						frappe.throw(_("Please set default Exchange Gain/Loss Account in Company {}")
-							.format(self.get('company')))
+						frappe.throw(
+							_("Please set default Exchange Gain/Loss Account in Company {}").format(self.get("company"))
+						)
 					account_currency = get_account_currency(gain_loss_account)
 					if account_currency != self.company_currency:
-						frappe.throw(_("Currency for {0} must be {1}").format(gain_loss_account, self.company_currency))
+						frappe.throw(
+							_("Currency for {0} must be {1}").format(gain_loss_account, self.company_currency)
+						)
 
 					# for purchase
-					dr_or_cr = 'debit' if d.exchange_gain_loss > 0 else 'credit'
+					dr_or_cr = "debit" if d.exchange_gain_loss > 0 else "credit"
 					if not is_purchase_invoice:
 						# just reverse for sales?
-						dr_or_cr = 'debit' if dr_or_cr == 'credit' else 'credit'
+						dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
 
 					gl_entries.append(
-						self.get_gl_dict({
-							"account": gain_loss_account,
-							"account_currency": account_currency,
-							"against": party,
-							dr_or_cr + "_in_account_currency": abs(d.exchange_gain_loss),
-							dr_or_cr: abs(d.exchange_gain_loss),
-							"cost_center": self.cost_center or erpnext.get_default_cost_center(self.company),
-							"project": self.project
-						}, item=d)
+						self.get_gl_dict(
+							{
+								"account": gain_loss_account,
+								"account_currency": account_currency,
+								"against": party,
+								dr_or_cr + "_in_account_currency": abs(d.exchange_gain_loss),
+								dr_or_cr: abs(d.exchange_gain_loss),
+								"cost_center": self.cost_center or erpnext.get_default_cost_center(self.company),
+								"project": self.project,
+							},
+							item=d,
+						)
 					)
 
-					dr_or_cr = 'debit' if dr_or_cr == 'credit' else 'credit'
+					dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
 
 					gl_entries.append(
-						self.get_gl_dict({
-							"account": party_account,
-							"party_type": party_type,
-							"party": party,
-							"against": gain_loss_account,
-							dr_or_cr + "_in_account_currency": flt(abs(d.exchange_gain_loss) / self.conversion_rate),
-							dr_or_cr: abs(d.exchange_gain_loss),
-							"cost_center": self.cost_center,
-							"project": self.project
-						}, self.party_account_currency, item=self)
+						self.get_gl_dict(
+							{
+								"account": party_account,
+								"party_type": party_type,
+								"party": party,
+								"against": gain_loss_account,
+								dr_or_cr + "_in_account_currency": flt(abs(d.exchange_gain_loss) / self.conversion_rate),
+								dr_or_cr: abs(d.exchange_gain_loss),
+								"cost_center": self.cost_center,
+								"project": self.project,
+							},
+							self.party_account_currency,
+							item=self,
+						)
 					)
 
 	def update_against_document_in_jv(self):
 		"""
-			Links invoice and advance voucher:
-				1. cancel advance voucher
-				2. split into multiple rows if partially adjusted, assign against voucher
-				3. submit advance voucher
+		Links invoice and advance voucher:
+		        1. cancel advance voucher
+		        2. split into multiple rows if partially adjusted, assign against voucher
+		        3. submit advance voucher
 		"""
 
 		if self.doctype == "Sales Invoice":
@@ -799,45 +979,56 @@
 			dr_or_cr = "debit_in_account_currency"
 
 		lst = []
-		for d in self.get('advances'):
+		for d in self.get("advances"):
 			if flt(d.allocated_amount) > 0:
-				args = frappe._dict({
-					'voucher_type': d.reference_type,
-					'voucher_no': d.reference_name,
-					'voucher_detail_no': d.reference_row,
-					'against_voucher_type': self.doctype,
-					'against_voucher': self.name,
-					'account': party_account,
-					'party_type': party_type,
-					'party': party,
-					'is_advance': 'Yes',
-					'dr_or_cr': dr_or_cr,
-					'unadjusted_amount': flt(d.advance_amount),
-					'allocated_amount': flt(d.allocated_amount),
-					'precision': d.precision('advance_amount'),
-					'exchange_rate': (self.conversion_rate
-						if self.party_account_currency != self.company_currency else 1),
-					'grand_total': (self.base_grand_total
-						if self.party_account_currency == self.company_currency else self.grand_total),
-					'outstanding_amount': self.outstanding_amount,
-					'difference_account': frappe.db.get_value('Company', self.company, 'exchange_gain_loss_account'),
-					'exchange_gain_loss': flt(d.get('exchange_gain_loss'))
-				})
+				args = frappe._dict(
+					{
+						"voucher_type": d.reference_type,
+						"voucher_no": d.reference_name,
+						"voucher_detail_no": d.reference_row,
+						"against_voucher_type": self.doctype,
+						"against_voucher": self.name,
+						"account": party_account,
+						"party_type": party_type,
+						"party": party,
+						"is_advance": "Yes",
+						"dr_or_cr": dr_or_cr,
+						"unadjusted_amount": flt(d.advance_amount),
+						"allocated_amount": flt(d.allocated_amount),
+						"precision": d.precision("advance_amount"),
+						"exchange_rate": (
+							self.conversion_rate if self.party_account_currency != self.company_currency else 1
+						),
+						"grand_total": (
+							self.base_grand_total
+							if self.party_account_currency == self.company_currency
+							else self.grand_total
+						),
+						"outstanding_amount": self.outstanding_amount,
+						"difference_account": frappe.db.get_value(
+							"Company", self.company, "exchange_gain_loss_account"
+						),
+						"exchange_gain_loss": flt(d.get("exchange_gain_loss")),
+					}
+				)
 				lst.append(args)
 
 		if lst:
 			from erpnext.accounts.utils import reconcile_against_document
+
 			reconcile_against_document(lst)
 
 	def on_cancel(self):
 		from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
 
 		if self.doctype in ["Sales Invoice", "Purchase Invoice"]:
-			if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
+			if frappe.db.get_single_value("Accounts Settings", "unlink_payment_on_cancellation_of_invoice"):
 				unlink_ref_doc_from_payment_entries(self)
 
 		elif self.doctype in ["Sales Order", "Purchase Order"]:
-			if frappe.db.get_single_value('Accounts Settings', 'unlink_advance_payment_on_cancelation_of_order'):
+			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":
@@ -848,33 +1039,32 @@
 		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'
-		)))
+		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
-				}
+				"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'):
+		for tax in self.get("taxes"):
 			tax_map.setdefault(tax.account_head, 0.0)
 			tax_map[tax.account_head] += tax.tax_amount
 
@@ -884,7 +1074,11 @@
 		amount = item.net_amount
 		base_amount = item.base_net_amount
 
-		if enable_discount_accounting and self.get('discount_amount') and self.get('additional_discount_account'):
+		if (
+			enable_discount_accounting
+			and self.get("discount_amount")
+			and self.get("additional_discount_account")
+		):
 			amount = item.amount
 			base_amount = item.base_amount
 
@@ -894,15 +1088,26 @@
 		amount = tax.tax_amount_after_discount_amount
 		base_amount = tax.base_tax_amount_after_discount_amount
 
-		if enable_discount_accounting and self.get('discount_amount') and self.get('additional_discount_account') \
-			and self.get('apply_discount_on') == 'Grand Total':
+		if (
+			enable_discount_accounting
+			and self.get("discount_amount")
+			and self.get("additional_discount_account")
+			and self.get("apply_discount_on") == "Grand Total"
+		):
 			amount = tax.tax_amount
 			base_amount = tax.base_tax_amount
 
 		return amount, base_amount
 
 	def make_discount_gl_entries(self, gl_entries):
-		enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting'))
+		if self.doctype == "Purchase Invoice":
+			enable_discount_accounting = cint(
+				frappe.db.get_single_value("Buying Settings", "enable_discount_accounting")
+			)
+		elif self.doctype == "Sales Invoice":
+			enable_discount_accounting = cint(
+				frappe.db.get_single_value("Selling Settings", "enable_discount_accounting")
+			)
 
 		if enable_discount_accounting:
 			if self.doctype == "Purchase Invoice":
@@ -916,61 +1121,80 @@
 				supplier_or_customer = self.customer
 
 			for item in self.get("items"):
-				if item.get('discount_amount') and item.get('discount_account'):
+				if item.get("discount_amount") and item.get("discount_account"):
 					discount_amount = item.discount_amount * item.qty
 					if self.doctype == "Purchase Invoice":
-						income_or_expense_account = (item.expense_account
+						income_or_expense_account = (
+							item.expense_account
 							if (not item.enable_deferred_expense or self.is_return)
-							else item.deferred_expense_account)
+							else item.deferred_expense_account
+						)
 					else:
-						income_or_expense_account = (item.income_account
+						income_or_expense_account = (
+							item.income_account
 							if (not item.enable_deferred_revenue or self.is_return)
-							else item.deferred_revenue_account)
+							else item.deferred_revenue_account
+						)
 
 					account_currency = get_account_currency(item.discount_account)
 					gl_entries.append(
-						self.get_gl_dict({
-							"account": item.discount_account,
-							"against": supplier_or_customer,
-							dr_or_cr: flt(discount_amount, item.precision('discount_amount')),
-							dr_or_cr + "_in_account_currency": flt(discount_amount * self.get('conversion_rate'),
-								item.precision('discount_amount')),
-							"cost_center": item.cost_center,
-							"project": item.project
-						}, account_currency, item=item)
+						self.get_gl_dict(
+							{
+								"account": item.discount_account,
+								"against": supplier_or_customer,
+								dr_or_cr: flt(
+									discount_amount * self.get("conversion_rate"), item.precision("discount_amount")
+								),
+								dr_or_cr + "_in_account_currency": flt(discount_amount, item.precision("discount_amount")),
+								"cost_center": item.cost_center,
+								"project": item.project,
+							},
+							account_currency,
+							item=item,
+						)
 					)
 
 					account_currency = get_account_currency(income_or_expense_account)
 					gl_entries.append(
-						self.get_gl_dict({
-							"account": income_or_expense_account,
-							"against": supplier_or_customer,
-							rev_dr_cr: flt(discount_amount, item.precision('discount_amount')),
-							rev_dr_cr + "_in_account_currency": flt(discount_amount * self.get('conversion_rate'),
-								item.precision('discount_amount')),
-							"cost_center": item.cost_center,
-							"project": item.project or self.project
-						}, account_currency, item=item)
+						self.get_gl_dict(
+							{
+								"account": income_or_expense_account,
+								"against": supplier_or_customer,
+								rev_dr_cr: flt(
+									discount_amount * self.get("conversion_rate"), item.precision("discount_amount")
+								),
+								rev_dr_cr
+								+ "_in_account_currency": flt(discount_amount, item.precision("discount_amount")),
+								"cost_center": item.cost_center,
+								"project": item.project or self.project,
+							},
+							account_currency,
+							item=item,
+						)
 					)
 
-			if self.get('discount_amount') and self.get('additional_discount_account'):
+			if self.get("discount_amount") and self.get("additional_discount_account"):
 				gl_entries.append(
-					self.get_gl_dict({
-						"account": self.additional_discount_account,
-						"against": supplier_or_customer,
-						dr_or_cr: self.discount_amount,
-						"cost_center": self.cost_center
-					}, item=self)
+					self.get_gl_dict(
+						{
+							"account": self.additional_discount_account,
+							"against": supplier_or_customer,
+							dr_or_cr: self.discount_amount,
+							"cost_center": self.cost_center,
+						},
+						item=self,
+					)
 				)
 
-
 	def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
 		from erpnext.controllers.status_updater import get_allowance_for
 
 		item_allowance = {}
 		global_qty_allowance, global_amount_allowance = None, None
 
-		role_allowed_to_over_bill = frappe.db.get_single_value('Accounts Settings', 'role_allowed_to_over_bill')
+		role_allowed_to_over_bill = frappe.db.get_single_value(
+			"Accounts Settings", "role_allowed_to_over_bill"
+		)
 		user_roles = frappe.get_roles()
 
 		total_overbilled_amt = 0.0
@@ -979,21 +1203,29 @@
 			if not item.get(item_ref_dn):
 				continue
 
-			ref_amt = flt(frappe.db.get_value(ref_dt + " Item",
-				item.get(item_ref_dn), based_on), self.precision(based_on, item))
+			ref_amt = flt(
+				frappe.db.get_value(ref_dt + " Item", item.get(item_ref_dn), based_on),
+				self.precision(based_on, item),
+			)
 			if not ref_amt:
 				frappe.msgprint(
-					_("System will not check overbilling since amount for Item {0} in {1} is zero")
-						.format(item.item_code, ref_dt), title=_("Warning"), indicator="orange")
+					_("System will not check overbilling since amount for Item {0} in {1} is zero").format(
+						item.item_code, ref_dt
+					),
+					title=_("Warning"),
+					indicator="orange",
+				)
 				continue
 
 			already_billed = self.get_billed_amount_for_item(item, item_ref_dn, based_on)
 
-			total_billed_amt = flt(flt(already_billed) + flt(item.get(based_on)),
-				self.precision(based_on, item))
+			total_billed_amt = flt(
+				flt(already_billed) + flt(item.get(based_on)), self.precision(based_on, item)
+			)
 
-			allowance, item_allowance, global_qty_allowance, global_amount_allowance = \
-				get_allowance_for(item.item_code, item_allowance, global_qty_allowance, global_amount_allowance, "amount")
+			allowance, item_allowance, global_qty_allowance, global_amount_allowance = get_allowance_for(
+				item.item_code, item_allowance, global_qty_allowance, global_amount_allowance, "amount"
+			)
 
 			max_allowed_amt = flt(ref_amt * (100 + allowance) / 100)
 
@@ -1008,20 +1240,29 @@
 			if overbill_amt > 0.01 and role_allowed_to_over_bill not in user_roles:
 				if self.doctype != "Purchase Invoice":
 					self.throw_overbill_exception(item, max_allowed_amt)
-				elif not cint(frappe.db.get_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice")):
+				elif not cint(
+					frappe.db.get_single_value(
+						"Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice"
+					)
+				):
 					self.throw_overbill_exception(item, max_allowed_amt)
 
 		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), indicator="orange", alert=True)
+			frappe.msgprint(
+				_("Overbilling of {} ignored because you have {} role.").format(
+					total_overbilled_amt, role_allowed_to_over_bill
+				),
+				indicator="orange",
+				alert=True,
+			)
 
 	def get_billed_amount_for_item(self, item, item_ref_dn, based_on):
-		'''
-			Returns Sum of Amount of
-			Sales/Purchase Invoice Items
-			that are linked to `item_ref_dn` (`dn_detail` / `pr_detail`)
-			that are submitted OR not submitted but are under current invoice
-		'''
+		"""
+		Returns Sum of Amount of
+		Sales/Purchase Invoice Items
+		that are linked to `item_ref_dn` (`dn_detail` / `pr_detail`)
+		that are submitted OR not submitted but are under current invoice
+		"""
 
 		from frappe.query_builder import Criterion
 		from frappe.query_builder.functions import Sum
@@ -1033,41 +1274,49 @@
 		result = (
 			frappe.qb.from_(item_doctype)
 			.select(Sum(based_on_field))
+			.where(join_field == item.get(item_ref_dn))
 			.where(
-				join_field == item.get(item_ref_dn)
-			).where(
-				Criterion.any([ # select all items from other invoices OR current invoices
-					Criterion.all([ # for selecting items from other invoices
-						item_doctype.docstatus == 1,
-						item_doctype.parent != self.name
-					]),
-					Criterion.all([ # for selecting items from current invoice, that are linked to same reference
-						item_doctype.docstatus == 0,
-						item_doctype.parent == self.name,
-						item_doctype.name != item.name
-					])
-				])
+				Criterion.any(
+					[  # select all items from other invoices OR current invoices
+						Criterion.all(
+							[  # for selecting items from other invoices
+								item_doctype.docstatus == 1,
+								item_doctype.parent != self.name,
+							]
+						),
+						Criterion.all(
+							[  # for selecting items from current invoice, that are linked to same reference
+								item_doctype.docstatus == 0,
+								item_doctype.parent == self.name,
+								item_doctype.name != item.name,
+							]
+						),
+					]
+				)
 			)
 		).run()
 
 		return result[0][0] if result else 0
 
 	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")
-			.format(item.item_code, item.idx, 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"
+			).format(item.item_code, item.idx, max_allowed_amt)
+		)
 
 	def get_company_default(self, fieldname, ignore_validation=False):
 		from erpnext.accounts.utils import get_company_default
+
 		return get_company_default(self.company, fieldname, ignore_validation=ignore_validation)
 
 	def get_stock_items(self):
 		stock_items = []
 		item_codes = list(set(item.item_code for item in self.get("items")))
 		if item_codes:
-			stock_items = [r[0] for r in frappe.db.sql("""
-				select name from `tabItem`
-				where name in (%s) and is_stock_item=1
-			""" % (", ".join(["%s"] * len(item_codes)),), item_codes)]
+			stock_items = frappe.db.get_values(
+				"Item", {"name": ["in", item_codes], "is_stock_item": 1}, pluck="name", cache=True
+			)
 
 		return stock_items
 
@@ -1081,7 +1330,8 @@
 			rev_dr_or_cr = "credit_in_account_currency"
 			party = self.supplier
 
-		advance = frappe.db.sql("""
+		advance = frappe.db.sql(
+			"""
 			select
 				account_currency, sum({dr_or_cr}) - sum({rev_dr_cr}) as amount
 			from
@@ -1089,17 +1339,22 @@
 			where
 				against_voucher_type = %s and against_voucher = %s and party=%s
 				and docstatus = 1
-		""".format(dr_or_cr=dr_or_cr, rev_dr_cr=rev_dr_or_cr), (self.doctype, self.name, party), as_dict=1) #nosec
+		""".format(
+				dr_or_cr=dr_or_cr, rev_dr_cr=rev_dr_or_cr
+			),
+			(self.doctype, self.name, party),
+			as_dict=1,
+		)  # nosec
 
 		if advance:
 			advance = advance[0]
 
 			advance_paid = flt(advance.amount, self.precision("advance_paid"))
-			formatted_advance_paid = fmt_money(advance_paid, precision=self.precision("advance_paid"),
-											   currency=advance.account_currency)
+			formatted_advance_paid = fmt_money(
+				advance_paid, precision=self.precision("advance_paid"), currency=advance.account_currency
+			)
 
-			frappe.db.set_value(self.doctype, self.name, "party_account_currency",
-								advance.account_currency)
+			frappe.db.set_value(self.doctype, self.name, "party_account_currency", advance.account_currency)
 
 			if advance.account_currency == self.currency:
 				order_total = self.get("rounded_total") or self.grand_total
@@ -1108,34 +1363,50 @@
 				order_total = self.get("base_rounded_total") or self.base_grand_total
 				precision = "base_rounded_total" if self.get("base_rounded_total") else "base_grand_total"
 
-			formatted_order_total = fmt_money(order_total, precision=self.precision(precision),
-											  currency=advance.account_currency)
+			formatted_order_total = fmt_money(
+				order_total, precision=self.precision(precision), currency=advance.account_currency
+			)
 
 			if self.currency == self.company_currency and advance_paid > order_total:
-				frappe.throw(_("Total advance ({0}) against Order {1} cannot be greater than the Grand Total ({2})")
-							 .format(formatted_advance_paid, self.name, formatted_order_total))
+				frappe.throw(
+					_(
+						"Total advance ({0}) against Order {1} cannot be greater than the Grand Total ({2})"
+					).format(formatted_advance_paid, self.name, formatted_order_total)
+				)
 
 			frappe.db.set_value(self.doctype, self.name, "advance_paid", advance_paid)
 
 	@property
 	def company_abbr(self):
 		if not hasattr(self, "_abbr"):
-			self._abbr = frappe.db.get_value('Company',  self.company,  "abbr")
+			self._abbr = frappe.db.get_value("Company", self.company, "abbr")
 
 		return self._abbr
 
 	def raise_missing_debit_credit_account_error(self, party_type, party):
 		"""Raise an error if debit to/credit to account does not exist."""
-		db_or_cr = frappe.bold("Debit To") if self.doctype == "Sales Invoice" else frappe.bold("Credit To")
+		db_or_cr = (
+			frappe.bold("Debit To") if self.doctype == "Sales Invoice" else frappe.bold("Credit To")
+		)
 		rec_or_pay = "Receivable" if self.doctype == "Sales Invoice" else "Payable"
 
 		link_to_party = frappe.utils.get_link_to_form(party_type, party)
 		link_to_company = frappe.utils.get_link_to_form("Company", self.company)
 
-		message = _("{0} Account not found against Customer {1}.").format(db_or_cr, frappe.bold(party) or '')
+		message = _("{0} Account not found against Customer {1}.").format(
+			db_or_cr, frappe.bold(party) or ""
+		)
 		message += "<br>" + _("Please set one of the following:") + "<br>"
-		message += "<br><ul><li>" + _("'Account' in the Accounting section of Customer {0}").format(link_to_party) + "</li>"
-		message += "<li>" + _("'Default {0} Account' in Company {1}").format(rec_or_pay, link_to_company) + "</li></ul>"
+		message += (
+			"<br><ul><li>"
+			+ _("'Account' in the Accounting section of Customer {0}").format(link_to_party)
+			+ "</li>"
+		)
+		message += (
+			"<li>"
+			+ _("'Default {0} Account' in Company {1}").format(rec_or_pay, link_to_company)
+			+ "</li></ul>"
+		)
 
 		frappe.throw(message, title=_("Account Missing"), exc=AccountMissingError)
 
@@ -1146,10 +1417,15 @@
 	def get_party(self):
 		party_type = None
 		if self.doctype in ("Opportunity", "Quotation", "Sales Order", "Delivery Note", "Sales Invoice"):
-			party_type = 'Customer'
+			party_type = "Customer"
 
-		elif self.doctype in ("Supplier Quotation", "Purchase Order", "Purchase Receipt", "Purchase Invoice"):
-			party_type = 'Supplier'
+		elif self.doctype in (
+			"Supplier Quotation",
+			"Purchase Order",
+			"Purchase Receipt",
+			"Purchase Invoice",
+		):
+			party_type = "Supplier"
 
 		elif self.meta.get_field("customer"):
 			party_type = "Customer"
@@ -1167,30 +1443,63 @@
 			if party_type and party:
 				party_account_currency = get_party_account_currency(party_type, party, self.company)
 
-				if (party_account_currency
-						and party_account_currency != self.company_currency
-						and self.currency != party_account_currency):
-					frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}")
-								 .format(party_type, party, party_account_currency), InvalidCurrency)
+				if (
+					party_account_currency
+					and party_account_currency != self.company_currency
+					and self.currency != party_account_currency
+				):
+					frappe.throw(
+						_("Accounting Entry for {0}: {1} can only be made in currency: {2}").format(
+							party_type, party, party_account_currency
+						),
+						InvalidCurrency,
+					)
 
 				# Note: not validating with gle account because we don't have the account
 				# at quotation / sales order level and we shouldn't stop someone
 				# from creating a sales invoice if sales order is already created
 
+	def validate_party_account_currency(self):
+		if self.doctype not in ("Sales Invoice", "Purchase Invoice"):
+			return
+
+		if self.is_opening == "Yes":
+			return
+
+		party_type, party = self.get_party()
+		party_gle_currency = get_party_gle_currency(party_type, party, self.company)
+		party_account = (
+			self.get("debit_to") if self.doctype == "Sales Invoice" else self.get("credit_to")
+		)
+		party_account_currency = get_account_currency(party_account)
+
+		if not party_gle_currency and (party_account_currency != self.currency):
+			frappe.throw(
+				_("Party Account {0} currency ({1}) and document currency ({2}) should be same").format(
+					frappe.bold(party_account), party_account_currency, self.currency
+				)
+			)
+
 	def delink_advance_entries(self, linked_doc_name):
 		total_allocated_amount = 0
 		for adv in self.advances:
 			consider_for_total_advance = True
 			if adv.reference_name == linked_doc_name:
-				frappe.db.sql("""delete from `tab{0} Advance`
-					where name = %s""".format(self.doctype), adv.name)
+				frappe.db.sql(
+					"""delete from `tab{0} Advance`
+					where name = %s""".format(
+						self.doctype
+					),
+					adv.name,
+				)
 				consider_for_total_advance = False
 
 			if consider_for_total_advance:
 				total_allocated_amount += flt(adv.allocated_amount, adv.precision("allocated_amount"))
 
-		frappe.db.set_value(self.doctype, self.name, "total_advance",
-							total_allocated_amount, update_modified=False)
+		frappe.db.set_value(
+			self.doctype, self.name, "total_advance", total_allocated_amount, update_modified=False
+		)
 
 	def group_similar_items(self):
 		group_item_qty = {}
@@ -1222,11 +1531,11 @@
 			self.remove(item)
 
 	def set_payment_schedule(self):
-		if self.doctype == 'Sales Invoice' and self.is_pos:
-			self.payment_terms_template = ''
+		if self.doctype == "Sales Invoice" and self.is_pos:
+			self.payment_terms_template = ""
 			return
 
-		party_account_currency = self.get('party_account_currency')
+		party_account_currency = self.get("party_account_currency")
 		if not party_account_currency:
 			party_type, party = self.get_party()
 
@@ -1244,47 +1553,68 @@
 			base_grand_total = base_grand_total - flt(self.base_write_off_amount)
 			grand_total = grand_total - flt(self.write_off_amount)
 			po_or_so, doctype, fieldname = self.get_order_details()
-			automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms'))
+			automatically_fetch_payment_terms = cint(
+				frappe.db.get_single_value("Accounts Settings", "automatically_fetch_payment_terms")
+			)
 
 		if self.get("total_advance"):
 			if party_account_currency == self.company_currency:
 				base_grand_total -= self.get("total_advance")
-				grand_total = flt(base_grand_total / self.get("conversion_rate"), self.precision("grand_total"))
+				grand_total = flt(
+					base_grand_total / self.get("conversion_rate"), self.precision("grand_total")
+				)
 			else:
 				grand_total -= self.get("total_advance")
-				base_grand_total = flt(grand_total * self.get("conversion_rate"), self.precision("base_grand_total"))
+				base_grand_total = flt(
+					grand_total * self.get("conversion_rate"), self.precision("base_grand_total")
+				)
 
 		if not self.get("payment_schedule"):
-			if self.doctype in ["Sales Invoice", "Purchase Invoice"] and automatically_fetch_payment_terms \
-				and self.linked_order_has_payment_terms(po_or_so, fieldname, doctype):
+			if (
+				self.doctype in ["Sales Invoice", "Purchase Invoice"]
+				and automatically_fetch_payment_terms
+				and self.linked_order_has_payment_terms(po_or_so, fieldname, doctype)
+			):
 				self.fetch_payment_terms_from_order(po_or_so, doctype)
-				if self.get('payment_terms_template'):
+				if self.get("payment_terms_template"):
 					self.ignore_default_payment_terms_template = 1
 			elif self.get("payment_terms_template"):
-				data = get_payment_terms(self.payment_terms_template, posting_date, grand_total, base_grand_total)
+				data = get_payment_terms(
+					self.payment_terms_template, posting_date, grand_total, base_grand_total
+				)
 				for item in data:
 					self.append("payment_schedule", item)
 			elif self.doctype not in ["Purchase Receipt"]:
-				data = dict(due_date=due_date, invoice_portion=100, payment_amount=grand_total, base_payment_amount=base_grand_total)
+				data = dict(
+					due_date=due_date,
+					invoice_portion=100,
+					payment_amount=grand_total,
+					base_payment_amount=base_grand_total,
+				)
 				self.append("payment_schedule", data)
 
 		for d in self.get("payment_schedule"):
 			if d.invoice_portion:
-				d.payment_amount = flt(grand_total * flt(d.invoice_portion / 100), d.precision('payment_amount'))
-				d.base_payment_amount = flt(base_grand_total * flt(d.invoice_portion / 100), d.precision('base_payment_amount'))
+				d.payment_amount = flt(
+					grand_total * flt(d.invoice_portion / 100), d.precision("payment_amount")
+				)
+				d.base_payment_amount = flt(
+					base_grand_total * flt(d.invoice_portion / 100), d.precision("base_payment_amount")
+				)
 				d.outstanding = d.payment_amount
 			elif not d.invoice_portion:
-				d.base_payment_amount = flt(d.payment_amount * self.get("conversion_rate"), d.precision('base_payment_amount'))
-
+				d.base_payment_amount = flt(
+					d.payment_amount * self.get("conversion_rate"), d.precision("base_payment_amount")
+				)
 
 	def get_order_details(self):
 		if self.doctype == "Sales Invoice":
-			po_or_so = self.get('items')[0].get('sales_order')
+			po_or_so = self.get("items")[0].get("sales_order")
 			po_or_so_doctype = "Sales Order"
 			po_or_so_doctype_name = "sales_order"
 
 		else:
-			po_or_so = self.get('items')[0].get('purchase_order')
+			po_or_so = self.get("items")[0].get("purchase_order")
 			po_or_so_doctype = "Purchase Order"
 			po_or_so_doctype_name = "purchase_order"
 
@@ -1300,21 +1630,21 @@
 		return False
 
 	def all_items_have_same_po_or_so(self, po_or_so, fieldname):
-		for item in self.get('items'):
+		for item in self.get("items"):
 			if item.get(fieldname) != po_or_so:
 				return False
 
 		return True
 
 	def linked_order_has_payment_terms_template(self, po_or_so, doctype):
-		return frappe.get_value(doctype, po_or_so, 'payment_terms_template')
+		return frappe.get_value(doctype, po_or_so, "payment_terms_template")
 
 	def linked_order_has_payment_schedule(self, po_or_so):
-		return frappe.get_all('Payment Schedule', filters={'parent': po_or_so})
+		return frappe.get_all("Payment Schedule", filters={"parent": po_or_so})
 
 	def fetch_payment_terms_from_order(self, po_or_so, po_or_so_doctype):
 		"""
-			Fetch Payment Terms from Purchase/Sales Order on creating a new Purchase/Sales Invoice.
+		Fetch Payment Terms from Purchase/Sales Order on creating a new Purchase/Sales Invoice.
 		"""
 		po_or_so = frappe.get_cached_doc(po_or_so_doctype, po_or_so)
 
@@ -1323,19 +1653,19 @@
 
 		for schedule in po_or_so.payment_schedule:
 			payment_schedule = {
-				'payment_term': schedule.payment_term,
-				'due_date': schedule.due_date,
-				'invoice_portion': schedule.invoice_portion,
-				'mode_of_payment': schedule.mode_of_payment,
-				'description': schedule.description
+				"payment_term": schedule.payment_term,
+				"due_date": schedule.due_date,
+				"invoice_portion": schedule.invoice_portion,
+				"mode_of_payment": schedule.mode_of_payment,
+				"description": schedule.description,
 			}
 
-			if schedule.discount_type == 'Percentage':
-				payment_schedule['discount_type'] = schedule.discount_type
-				payment_schedule['discount'] = schedule.discount
+			if schedule.discount_type == "Percentage":
+				payment_schedule["discount_type"] = schedule.discount_type
+				payment_schedule["discount"] = schedule.discount
 
 			if not schedule.invoice_portion:
-				payment_schedule['payment_amount'] = schedule.payment_amount
+				payment_schedule["payment_amount"] = schedule.payment_amount
 
 			self.append("payment_schedule", payment_schedule)
 
@@ -1348,23 +1678,29 @@
 		dates = []
 		li = []
 
-		if self.doctype == 'Sales Invoice' and self.is_pos: return
+		if self.doctype == "Sales Invoice" and self.is_pos:
+			return
 
 		for d in self.get("payment_schedule"):
 			if self.doctype == "Sales Order" and getdate(d.due_date) < getdate(self.transaction_date):
-				frappe.throw(_("Row {0}: Due Date in the Payment Terms table cannot be before Posting Date").format(d.idx))
+				frappe.throw(
+					_("Row {0}: Due Date in the Payment Terms table cannot be before Posting Date").format(d.idx)
+				)
 			elif d.due_date in dates:
 				li.append(_("{0} in row {1}").format(d.due_date, d.idx))
 			dates.append(d.due_date)
 
 		if li:
-			duplicates = '<br>' + '<br>'.join(li)
-			frappe.throw(_("Rows with duplicate due dates in other rows were found: {0}").format(duplicates))
+			duplicates = "<br>" + "<br>".join(li)
+			frappe.throw(
+				_("Rows with duplicate due dates in other rows were found: {0}").format(duplicates)
+			)
 
 	def validate_payment_schedule_amount(self):
-		if self.doctype == 'Sales Invoice' and self.is_pos: return
+		if self.doctype == "Sales Invoice" and self.is_pos:
+			return
 
-		party_account_currency = self.get('party_account_currency')
+		party_account_currency = self.get("party_account_currency")
 		if not party_account_currency:
 			party_type, party = self.get_party()
 
@@ -1388,14 +1724,25 @@
 			if self.get("total_advance"):
 				if party_account_currency == self.company_currency:
 					base_grand_total -= self.get("total_advance")
-					grand_total = flt(base_grand_total / self.get("conversion_rate"), self.precision("grand_total"))
+					grand_total = flt(
+						base_grand_total / self.get("conversion_rate"), self.precision("grand_total")
+					)
 				else:
 					grand_total -= self.get("total_advance")
-					base_grand_total = flt(grand_total * self.get("conversion_rate"), self.precision("base_grand_total"))
+					base_grand_total = flt(
+						grand_total * self.get("conversion_rate"), self.precision("base_grand_total")
+					)
 
-			if flt(total, self.precision("grand_total")) - flt(grand_total, self.precision("grand_total")) > 0.1 or \
-				flt(base_total, self.precision("base_grand_total")) - flt(base_grand_total, self.precision("base_grand_total")) > 0.1:
-				frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total"))
+			if (
+				flt(total, self.precision("grand_total")) - flt(grand_total, self.precision("grand_total"))
+				> 0.1
+				or flt(base_total, self.precision("base_grand_total"))
+				- flt(base_grand_total, self.precision("base_grand_total"))
+				> 0.1
+			):
+				frappe.throw(
+					_("Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total")
+				)
 
 	def is_rounded_total_disabled(self):
 		if self.meta.get_field("disable_rounded_total"):
@@ -1405,30 +1752,35 @@
 
 	def set_inter_company_account(self):
 		"""
-			Set intercompany account for inter warehouse transactions
-			This account will be used in case billing company and internal customer's
-			representation company is same
+		Set intercompany account for inter warehouse transactions
+		This account will be used in case billing company and internal customer's
+		representation company is same
 		"""
 
 		if self.is_internal_transfer() and not self.unrealized_profit_loss_account:
-			unrealized_profit_loss_account = frappe.db.get_value('Company', self.company, 'unrealized_profit_loss_account')
+			unrealized_profit_loss_account = frappe.db.get_value(
+				"Company", self.company, "unrealized_profit_loss_account"
+			)
 
 			if not unrealized_profit_loss_account:
-				msg = _("Please select Unrealized Profit / Loss account or add default Unrealized Profit / Loss account account for company {0}").format(
-						frappe.bold(self.company))
+				msg = _(
+					"Please select Unrealized Profit / Loss account or add default Unrealized Profit / Loss account account for company {0}"
+				).format(frappe.bold(self.company))
 				frappe.throw(msg)
 
 			self.unrealized_profit_loss_account = unrealized_profit_loss_account
 
 	def is_internal_transfer(self):
 		"""
-			It will an internal transfer if its an internal customer and representation
-			company is same as billing company
+		It will an internal transfer if its an internal customer and representation
+		company is same as billing company
 		"""
-		if self.doctype in ('Sales Invoice', 'Delivery Note', 'Sales Order'):
-			internal_party_field = 'is_internal_customer'
-		elif self.doctype in ('Purchase Invoice', 'Purchase Receipt', 'Purchase Order'):
-			internal_party_field = 'is_internal_supplier'
+		if self.doctype in ("Sales Invoice", "Delivery Note", "Sales Order"):
+			internal_party_field = "is_internal_customer"
+		elif self.doctype in ("Purchase Invoice", "Purchase Receipt", "Purchase Order"):
+			internal_party_field = "is_internal_supplier"
+		else:
+			return False
 
 		if self.get(internal_party_field) and (self.represents_company == self.company):
 			return True
@@ -1436,11 +1788,11 @@
 		return False
 
 	def process_common_party_accounting(self):
-		is_invoice = self.doctype in ['Sales Invoice', 'Purchase Invoice']
+		is_invoice = self.doctype in ["Sales Invoice", "Purchase Invoice"]
 		if not is_invoice:
 			return
 
-		if frappe.db.get_single_value('Accounts Settings', 'enable_common_party_accounting'):
+		if frappe.db.get_single_value("Accounts Settings", "enable_common_party_accounting"):
 			party_link = self.get_common_party_link()
 			if party_link and self.outstanding_amount:
 				self.create_advance_and_reconcile(party_link)
@@ -1448,10 +1800,10 @@
 	def get_common_party_link(self):
 		party_type, party = self.get_party()
 		return frappe.db.get_value(
-			doctype='Party Link',
-			filters={'secondary_role': party_type, 'secondary_party': party},
-			fieldname=['primary_role', 'primary_party'],
-			as_dict=True
+			doctype="Party Link",
+			filters={"secondary_role": party_type, "secondary_party": party},
+			fieldname=["primary_role", "primary_party"],
+			as_dict=True,
 		)
 
 	def create_advance_and_reconcile(self, party_link):
@@ -1461,11 +1813,11 @@
 		primary_account = get_party_account(primary_party_type, primary_party, self.company)
 		secondary_account = get_party_account(secondary_party_type, secondary_party, self.company)
 
-		jv = frappe.new_doc('Journal Entry')
-		jv.voucher_type = 'Journal Entry'
+		jv = frappe.new_doc("Journal Entry")
+		jv.voucher_type = "Journal Entry"
 		jv.posting_date = self.posting_date
 		jv.company = self.company
-		jv.remark = 'Adjustment for {} {}'.format(self.doctype, self.name)
+		jv.remark = "Adjustment for {} {}".format(self.doctype, self.name)
 
 		reconcilation_entry = frappe._dict()
 		advance_entry = frappe._dict()
@@ -1481,21 +1833,22 @@
 		advance_entry.party_type = primary_party_type
 		advance_entry.party = primary_party
 		advance_entry.cost_center = self.cost_center
-		advance_entry.is_advance = 'Yes'
+		advance_entry.is_advance = "Yes"
 
-		if self.doctype == 'Sales Invoice':
+		if self.doctype == "Sales Invoice":
 			reconcilation_entry.credit_in_account_currency = self.outstanding_amount
 			advance_entry.debit_in_account_currency = self.outstanding_amount
 		else:
 			advance_entry.credit_in_account_currency = self.outstanding_amount
 			reconcilation_entry.debit_in_account_currency = self.outstanding_amount
 
-		jv.append('accounts', reconcilation_entry)
-		jv.append('accounts', advance_entry)
+		jv.append("accounts", reconcilation_entry)
+		jv.append("accounts", advance_entry)
 
 		jv.save()
 		jv.submit()
 
+
 @frappe.whitelist()
 def get_tax_rate(account_head):
 	return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True)
@@ -1503,7 +1856,8 @@
 
 @frappe.whitelist()
 def get_default_taxes_and_charges(master_doctype, tax_template=None, company=None):
-	if not company: return {}
+	if not company:
+		return {}
 
 	if tax_template and company:
 		tax_template_company = frappe.db.get_value(master_doctype, tax_template, "company")
@@ -1513,8 +1867,8 @@
 	default_tax = frappe.db.get_value(master_doctype, {"is_default": 1, "company": company})
 
 	return {
-		'taxes_and_charges': default_tax,
-		'taxes': get_taxes_and_charges(master_doctype, default_tax)
+		"taxes_and_charges": default_tax,
+		"taxes": get_taxes_and_charges(master_doctype, default_tax),
 	}
 
 
@@ -1522,14 +1876,15 @@
 def get_taxes_and_charges(master_doctype, master_name):
 	if not master_name:
 		return
-	from frappe.model import default_fields
+	from frappe.model import child_table_fields, default_fields
+
 	tax_master = frappe.get_doc(master_doctype, master_name)
 
 	taxes_and_charges = []
 	for i, tax in enumerate(tax_master.get("taxes")):
 		tax = tax.as_dict()
 
-		for fieldname in default_fields:
+		for fieldname in default_fields + child_table_fields:
 			if fieldname in tax:
 				del tax[fieldname]
 
@@ -1541,109 +1896,162 @@
 def validate_conversion_rate(currency, conversion_rate, conversion_rate_label, company):
 	"""common validation for currency and price list currency"""
 
-	company_currency = frappe.get_cached_value('Company',  company,  "default_currency")
+	company_currency = frappe.get_cached_value("Company", company, "default_currency")
 
 	if not conversion_rate:
 		throw(
-			_("{0} is mandatory. Maybe Currency Exchange record is not created for {1} to {2}.")
-			.format(conversion_rate_label, currency, company_currency)
+			_("{0} is mandatory. Maybe Currency Exchange record is not created for {1} to {2}.").format(
+				conversion_rate_label, currency, company_currency
+			)
 		)
 
 
 def validate_taxes_and_charges(tax):
-	if tax.charge_type in ['Actual', 'On Net Total', 'On Paid Amount'] and tax.row_id:
-		frappe.throw(_("Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total'"))
-	elif tax.charge_type in ['On Previous Row Amount', 'On Previous Row Total']:
+	if tax.charge_type in ["Actual", "On Net Total", "On Paid Amount"] and tax.row_id:
+		frappe.throw(
+			_("Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total'")
+		)
+	elif tax.charge_type in ["On Previous Row Amount", "On Previous Row Total"]:
 		if cint(tax.idx) == 1:
 			frappe.throw(
-				_("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row"))
+				_(
+					"Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row"
+				)
+			)
 		elif not tax.row_id:
-			frappe.throw(_("Please specify a valid Row ID for row {0} in table {1}").format(tax.idx, _(tax.doctype)))
+			frappe.throw(
+				_("Please specify a valid Row ID for row {0} in table {1}").format(tax.idx, _(tax.doctype))
+			)
 		elif tax.row_id and cint(tax.row_id) >= cint(tax.idx):
-			frappe.throw(_("Cannot refer row number greater than or equal to current row number for this Charge type"))
+			frappe.throw(
+				_("Cannot refer row number greater than or equal to current row number for this Charge type")
+			)
 
 	if tax.charge_type == "Actual":
 		tax.rate = None
 
 
-def validate_account_head(idx, account, company):
-	account_company = frappe.get_cached_value('Account', account, 'company')
+def validate_account_head(idx, account, company, context=""):
+	account_company = frappe.get_cached_value("Account", account, "company")
 
 	if account_company != company:
-		frappe.throw(_('Row {0}: Account {1} does not belong to Company {2}')
-			.format(idx, frappe.bold(account), frappe.bold(company)), title=_('Invalid Account'))
+		frappe.throw(
+			_("Row {0}: {3} Account {1} does not belong to Company {2}").format(
+				idx, frappe.bold(account), frappe.bold(company), context
+			),
+			title=_("Invalid Account"),
+		)
 
 
 def validate_cost_center(tax, doc):
 	if not tax.cost_center:
 		return
 
-	company = frappe.get_cached_value('Cost Center',
-		tax.cost_center, 'company')
+	company = frappe.get_cached_value("Cost Center", tax.cost_center, "company")
 
 	if company != doc.company:
-		frappe.throw(_('Row {0}: Cost Center {1} does not belong to Company {2}')
-			.format(tax.idx, frappe.bold(tax.cost_center), frappe.bold(doc.company)), title=_('Invalid Cost Center'))
+		frappe.throw(
+			_("Row {0}: Cost Center {1} does not belong to Company {2}").format(
+				tax.idx, frappe.bold(tax.cost_center), frappe.bold(doc.company)
+			),
+			title=_("Invalid Cost Center"),
+		)
 
 
 def validate_inclusive_tax(tax, doc):
 	def _on_previous_row_error(row_range):
-		throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx, row_range))
+		throw(
+			_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(
+				tax.idx, row_range
+			)
+		)
 
 	if cint(getattr(tax, "included_in_print_rate", None)):
 		if tax.charge_type == "Actual":
 			# inclusive tax cannot be of type Actual
-			throw(_("Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount").format(tax.idx))
-		elif tax.charge_type == "On Previous Row Amount" and \
-				not cint(doc.get("taxes")[cint(tax.row_id) - 1].included_in_print_rate):
+			throw(
+				_("Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount").format(
+					tax.idx
+				)
+			)
+		elif tax.charge_type == "On Previous Row Amount" and not cint(
+			doc.get("taxes")[cint(tax.row_id) - 1].included_in_print_rate
+		):
 			# referred row should also be inclusive
 			_on_previous_row_error(tax.row_id)
-		elif tax.charge_type == "On Previous Row Total" and \
-				not all([cint(t.included_in_print_rate) for t in doc.get("taxes")[:cint(tax.row_id) - 1]]):
+		elif tax.charge_type == "On Previous Row Total" and not all(
+			[cint(t.included_in_print_rate) for t in doc.get("taxes")[: cint(tax.row_id) - 1]]
+		):
 			# all rows about the referred tax should be inclusive
 			_on_previous_row_error("1 - %d" % (tax.row_id,))
 		elif tax.get("category") == "Valuation":
 			frappe.throw(_("Valuation type charges can not be marked as Inclusive"))
 
 
-def set_balance_in_account_currency(gl_dict, account_currency=None, conversion_rate=None, company_currency=None):
+def set_balance_in_account_currency(
+	gl_dict, account_currency=None, conversion_rate=None, company_currency=None
+):
 	if (not conversion_rate) and (account_currency != company_currency):
-		frappe.throw(_("Account: {0} with currency: {1} can not be selected")
-					 .format(gl_dict.account, account_currency))
+		frappe.throw(
+			_("Account: {0} with currency: {1} can not be selected").format(
+				gl_dict.account, account_currency
+			)
+		)
 
-	gl_dict["account_currency"] = company_currency if account_currency == company_currency \
-		else account_currency
+	gl_dict["account_currency"] = (
+		company_currency if account_currency == company_currency else account_currency
+	)
 
 	# set debit/credit in account currency if not provided
 	if flt(gl_dict.debit) and not flt(gl_dict.debit_in_account_currency):
-		gl_dict.debit_in_account_currency = gl_dict.debit if account_currency == company_currency \
+		gl_dict.debit_in_account_currency = (
+			gl_dict.debit
+			if account_currency == company_currency
 			else flt(gl_dict.debit / conversion_rate, 2)
+		)
 
 	if flt(gl_dict.credit) and not flt(gl_dict.credit_in_account_currency):
-		gl_dict.credit_in_account_currency = gl_dict.credit if account_currency == company_currency \
+		gl_dict.credit_in_account_currency = (
+			gl_dict.credit
+			if account_currency == company_currency
 			else flt(gl_dict.credit / conversion_rate, 2)
+		)
 
 
-def get_advance_journal_entries(party_type, party, party_account, amount_field,
-								order_doctype, order_list, include_unallocated=True):
-	dr_or_cr = "credit_in_account_currency" if party_type == "Customer" else "debit_in_account_currency"
+def get_advance_journal_entries(
+	party_type,
+	party,
+	party_account,
+	amount_field,
+	order_doctype,
+	order_list,
+	include_unallocated=True,
+):
+	dr_or_cr = (
+		"credit_in_account_currency" if party_type == "Customer" else "debit_in_account_currency"
+	)
 
 	conditions = []
 	if include_unallocated:
 		conditions.append("ifnull(t2.reference_name, '')=''")
 
 	if order_list:
-		order_condition = ', '.join(['%s'] * len(order_list))
-		conditions.append(" (t2.reference_type = '{0}' and ifnull(t2.reference_name, '') in ({1}))" \
-						  .format(order_doctype, order_condition))
+		order_condition = ", ".join(["%s"] * len(order_list))
+		conditions.append(
+			" (t2.reference_type = '{0}' and ifnull(t2.reference_name, '') in ({1}))".format(
+				order_doctype, order_condition
+			)
+		)
 
 	reference_condition = " and (" + " or ".join(conditions) + ")" if conditions else ""
 
-	journal_entries = frappe.db.sql("""
+	# nosemgrep
+	journal_entries = frappe.db.sql(
+		"""
 		select
-			"Journal Entry" as reference_type, t1.name as reference_name,
+			'Journal Entry' as reference_type, t1.name as reference_name,
 			t1.remark as remarks, t2.{0} as amount, t2.name as reference_row,
-			t2.reference_name as against_order
+			t2.reference_name as against_order, t2.exchange_rate
 		from
 			`tabJournal Entry` t1, `tabJournal Entry Account` t2
 		where
@@ -1651,33 +2059,52 @@
 			and t2.party_type = %s and t2.party = %s
 			and t2.is_advance = 'Yes' and t1.docstatus = 1
 			and {1} > 0 {2}
-		order by t1.posting_date""".format(amount_field, dr_or_cr, reference_condition),
-									[party_account, party_type, party] + order_list, as_dict=1)
+		order by t1.posting_date""".format(
+			amount_field, dr_or_cr, reference_condition
+		),
+		[party_account, party_type, party] + order_list,
+		as_dict=1,
+	)
 
 	return list(journal_entries)
 
 
-def get_advance_payment_entries(party_type, party, party_account, order_doctype,
-		order_list=None, include_unallocated=True, against_all_orders=False, limit=None, condition=None):
+def get_advance_payment_entries(
+	party_type,
+	party,
+	party_account,
+	order_doctype,
+	order_list=None,
+	include_unallocated=True,
+	against_all_orders=False,
+	limit=None,
+	condition=None,
+):
 	party_account_field = "paid_from" if party_type == "Customer" else "paid_to"
-	currency_field = "paid_from_account_currency" if party_type == "Customer" else "paid_to_account_currency"
+	currency_field = (
+		"paid_from_account_currency" if party_type == "Customer" else "paid_to_account_currency"
+	)
 	payment_type = "Receive" if party_type == "Customer" else "Pay"
-	exchange_rate_field = "source_exchange_rate" if payment_type == "Receive" else "target_exchange_rate"
+	exchange_rate_field = (
+		"source_exchange_rate" if payment_type == "Receive" else "target_exchange_rate"
+	)
 
 	payment_entries_against_order, unallocated_payment_entries = [], []
 	limit_cond = "limit %s" % limit if limit else ""
 
 	if order_list or against_all_orders:
 		if order_list:
-			reference_condition = " and t2.reference_name in ({0})" \
-				.format(', '.join(['%s'] * len(order_list)))
+			reference_condition = " and t2.reference_name in ({0})".format(
+				", ".join(["%s"] * len(order_list))
+			)
 		else:
 			reference_condition = ""
 			order_list = []
 
-		payment_entries_against_order = frappe.db.sql("""
+		payment_entries_against_order = frappe.db.sql(
+			"""
 			select
-				"Payment Entry" as reference_type, t1.name as reference_name,
+				'Payment Entry' as reference_type, t1.name as reference_name,
 				t1.remarks, t2.allocated_amount as amount, t2.name as reference_row,
 				t2.reference_name as against_order, t1.posting_date,
 				t1.{0} as currency, t1.{4} as exchange_rate
@@ -1687,24 +2114,33 @@
 				and t1.party_type = %s and t1.party = %s and t1.docstatus = 1
 				and t2.reference_doctype = %s {2}
 			order by t1.posting_date {3}
-		""".format(currency_field, party_account_field, reference_condition, limit_cond, exchange_rate_field),
-													  [party_account, payment_type, party_type, party,
-													   order_doctype] + order_list, as_dict=1)
+		""".format(
+				currency_field, party_account_field, reference_condition, limit_cond, exchange_rate_field
+			),
+			[party_account, payment_type, party_type, party, order_doctype] + order_list,
+			as_dict=1,
+		)
 
 	if include_unallocated:
-		unallocated_payment_entries = frappe.db.sql("""
-				select "Payment Entry" as reference_type, name as reference_name, posting_date,
+		unallocated_payment_entries = frappe.db.sql(
+			"""
+				select 'Payment Entry' as reference_type, name as reference_name, posting_date,
 				remarks, unallocated_amount as amount, {2} as exchange_rate, {3} as currency
 				from `tabPayment Entry`
 				where
 					{0} = %s and party_type = %s and party = %s and payment_type = %s
 					and docstatus = 1 and unallocated_amount > 0 {condition}
 				order by posting_date {1}
-			""".format(party_account_field, limit_cond, exchange_rate_field, currency_field, condition=condition or ""),
-			(party_account, party_type, party, payment_type), as_dict=1)
+			""".format(
+				party_account_field, limit_cond, exchange_rate_field, currency_field, condition=condition or ""
+			),
+			(party_account, party_type, party, payment_type),
+			as_dict=1,
+		)
 
 	return list(payment_entries_against_order) + list(unallocated_payment_entries)
 
+
 def update_invoice_status():
 	"""Updates status as Overdue for applicable invoices. Runs daily."""
 	today = getdate()
@@ -1722,10 +2158,7 @@
 		payable_amount = (
 			frappe.qb.from_(payment_schedule)
 			.select(Sum(payment_amount))
-			.where(
-				(payment_schedule.parent == invoice.name)
-				& (payment_schedule.due_date < today)
-			)
+			.where((payment_schedule.parent == invoice.name) & (payment_schedule.due_date < today))
 		)
 
 		total = (
@@ -1740,21 +2173,14 @@
 			.else_(invoice.base_rounded_total)
 		)
 
-		total_amount = (
-			frappe.qb.terms.Case()
-			.when(consider_base_amount, base_total)
-			.else_(total)
-		)
+		total_amount = frappe.qb.terms.Case().when(consider_base_amount, base_total).else_(total)
 
 		is_overdue = total_amount - invoice.outstanding_amount < payable_amount
 
 		conditions = (
 			(invoice.docstatus == 1)
 			& (invoice.outstanding_amount > 0)
-			& (
-				invoice.status.like("Unpaid%")
-				| invoice.status.like("Partly Paid%")
-			)
+			& (invoice.status.like("Unpaid%") | invoice.status.like("Partly Paid%"))
 			& (
 				((invoice.is_pos & invoice.due_date < today) | is_overdue)
 				if doctype == "Sales Invoice"
@@ -1772,7 +2198,9 @@
 
 
 @frappe.whitelist()
-def get_payment_terms(terms_template, posting_date=None, grand_total=None, base_grand_total=None, bill_date=None):
+def get_payment_terms(
+	terms_template, posting_date=None, grand_total=None, base_grand_total=None, bill_date=None
+):
 	if not terms_template:
 		return
 
@@ -1780,14 +2208,18 @@
 
 	schedule = []
 	for d in terms_doc.get("terms"):
-		term_details = get_payment_term_details(d, posting_date, grand_total, base_grand_total, bill_date)
+		term_details = get_payment_term_details(
+			d, posting_date, grand_total, base_grand_total, bill_date
+		)
 		schedule.append(term_details)
 
 	return schedule
 
 
 @frappe.whitelist()
-def get_payment_term_details(term, posting_date=None, grand_total=None, base_grand_total=None, bill_date=None):
+def get_payment_term_details(
+	term, posting_date=None, grand_total=None, base_grand_total=None, bill_date=None
+):
 	term_details = frappe._dict()
 	if isinstance(term, str):
 		term = frappe.get_doc("Payment Term", term)
@@ -1814,6 +2246,7 @@
 
 	return term_details
 
+
 def get_due_date(term, posting_date=None, bill_date=None):
 	due_date = None
 	date = bill_date or posting_date
@@ -1825,6 +2258,7 @@
 		due_date = add_months(get_last_day(date), term.credit_months)
 	return due_date
 
+
 def get_discount_date(term, posting_date=None, bill_date=None):
 	discount_validity = None
 	date = bill_date or posting_date
@@ -1836,64 +2270,73 @@
 		discount_validity = add_months(get_last_day(date), term.discount_validity)
 	return discount_validity
 
+
 def get_supplier_block_status(party_name):
 	"""
 	Returns a dict containing the values of `on_hold`, `release_date` and `hold_type` of
 	a `Supplier`
 	"""
-	supplier = frappe.get_doc('Supplier', party_name)
+	supplier = frappe.get_doc("Supplier", party_name)
 	info = {
-		'on_hold': supplier.on_hold,
-		'release_date': supplier.release_date,
-		'hold_type': supplier.hold_type
+		"on_hold": supplier.on_hold,
+		"release_date": supplier.release_date,
+		"hold_type": supplier.hold_type,
 	}
 	return info
 
+
 def set_child_tax_template_and_map(item, child_item, parent_doc):
 	args = {
-			'item_code': item.item_code,
-			'posting_date': parent_doc.transaction_date,
-			'tax_category': parent_doc.get('tax_category'),
-			'company': parent_doc.get('company')
-		}
+		"item_code": item.item_code,
+		"posting_date": parent_doc.transaction_date,
+		"tax_category": parent_doc.get("tax_category"),
+		"company": parent_doc.get("company"),
+	}
 
 	child_item.item_tax_template = _get_item_tax_template(args, item.taxes)
 	if child_item.get("item_tax_template"):
-		child_item.item_tax_rate = get_item_tax_map(parent_doc.get('company'), child_item.item_tax_template, as_json=True)
+		child_item.item_tax_rate = get_item_tax_map(
+			parent_doc.get("company"), child_item.item_tax_template, as_json=True
+		)
+
 
 def add_taxes_from_tax_template(child_item, parent_doc, db_insert=True):
-	add_taxes_from_item_tax_template = frappe.db.get_single_value("Accounts Settings", "add_taxes_from_item_tax_template")
+	add_taxes_from_item_tax_template = frappe.db.get_single_value(
+		"Accounts Settings", "add_taxes_from_item_tax_template"
+	)
 
 	if child_item.get("item_tax_rate") and add_taxes_from_item_tax_template:
 		tax_map = json.loads(child_item.get("item_tax_rate"))
 		for tax_type in tax_map:
 			tax_rate = flt(tax_map[tax_type])
-			taxes = parent_doc.get('taxes') or []
+			taxes = parent_doc.get("taxes") or []
 			# add new row for tax head only if missing
 			found = any(tax.account_head == tax_type for tax in taxes)
 			if not found:
 				tax_row = parent_doc.append("taxes", {})
-				tax_row.update({
-					"description" : str(tax_type).split(' - ')[0],
-					"charge_type" : "On Net Total",
-					"account_head" : tax_type,
-					"rate" : tax_rate
-				})
+				tax_row.update(
+					{
+						"description": str(tax_type).split(" - ")[0],
+						"charge_type": "On Net Total",
+						"account_head": tax_type,
+						"rate": tax_rate,
+					}
+				)
 				if parent_doc.doctype == "Purchase Order":
-					tax_row.update({
-						"category" : "Total",
-						"add_deduct_tax" : "Add"
-					})
+					tax_row.update({"category": "Total", "add_deduct_tax": "Add"})
 				if db_insert:
 					tax_row.db_insert()
 
-def set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, trans_item):
+
+def set_order_defaults(
+	parent_doctype, parent_doctype_name, child_doctype, child_docname, trans_item
+):
 	"""
 	Returns a Sales/Purchase Order Item child item containing the default values
 	"""
 	p_doc = frappe.get_doc(parent_doctype, parent_doctype_name)
 	child_item = frappe.new_doc(child_doctype, p_doc, child_docname)
-	item = frappe.get_doc("Item", trans_item.get('item_code'))
+	item = frappe.get_doc("Item", trans_item.get("item_code"))
 
 	for field in ("item_code", "item_name", "description", "item_group"):
 		child_item.update({field: item.get(field)})
@@ -1903,8 +2346,10 @@
 	child_item.stock_uom = item.stock_uom
 	child_item.uom = trans_item.get("uom") or item.stock_uom
 	child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
-	conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor"))
-	child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor
+	conversion_factor = flt(
+		get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor")
+	)
+	child_item.conversion_factor = flt(trans_item.get("conversion_factor")) or conversion_factor
 
 	if child_doctype == "Purchase Order Item":
 		# Initialized value will update in parent validation
@@ -1913,28 +2358,53 @@
 	if child_doctype == "Sales Order Item":
 		child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
 		if not child_item.warehouse:
-			frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.")
-				.format(frappe.bold("default warehouse"), frappe.bold(item.item_code)))
+			frappe.throw(
+				_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.").format(
+					frappe.bold("default warehouse"), frappe.bold(item.item_code)
+				)
+			)
 
 	set_child_tax_template_and_map(item, child_item, p_doc)
 	add_taxes_from_tax_template(child_item, p_doc)
 	return child_item
 
+
 def validate_child_on_delete(row, parent):
 	"""Check if partially transacted item (row) is being deleted."""
 	if parent.doctype == "Sales Order":
 		if flt(row.delivered_qty):
-			frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been delivered").format(row.idx, row.item_code))
+			frappe.throw(
+				_("Row #{0}: Cannot delete item {1} which has already been delivered").format(
+					row.idx, row.item_code
+				)
+			)
 		if flt(row.work_order_qty):
-			frappe.throw(_("Row #{0}: Cannot delete item {1} which has work order assigned to it.").format(row.idx, row.item_code))
+			frappe.throw(
+				_("Row #{0}: Cannot delete item {1} which has work order assigned to it.").format(
+					row.idx, row.item_code
+				)
+			)
 		if flt(row.ordered_qty):
-			frappe.throw(_("Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.").format(row.idx, row.item_code))
+			frappe.throw(
+				_("Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.").format(
+					row.idx, row.item_code
+				)
+			)
 
 	if parent.doctype == "Purchase Order" and flt(row.received_qty):
-		frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been received").format(row.idx, row.item_code))
+		frappe.throw(
+			_("Row #{0}: Cannot delete item {1} which has already been received").format(
+				row.idx, row.item_code
+			)
+		)
 
 	if flt(row.billed_amt):
-		frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been billed.").format(row.idx, row.item_code))
+		frappe.throw(
+			_("Row #{0}: Cannot delete item {1} which has already been billed.").format(
+				row.idx, row.item_code
+			)
+		)
+
 
 def update_bin_on_delete(row, doctype):
 	"""Update bin for deleted item (row)."""
@@ -1944,6 +2414,7 @@
 		get_reserved_qty,
 		update_bin_qty,
 	)
+
 	qty_dict = {}
 
 	if doctype == "Sales Order":
@@ -1957,6 +2428,7 @@
 	if row.warehouse:
 		update_bin_qty(row.item_code, row.warehouse, qty_dict)
 
+
 def validate_and_delete_children(parent, data):
 	deleted_children = []
 	updated_item_names = [d.get("docname") for d in data]
@@ -1979,14 +2451,18 @@
 
 @frappe.whitelist()
 def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
-	def check_doc_permissions(doc, perm_type='create'):
+	def check_doc_permissions(doc, perm_type="create"):
 		try:
 			doc.check_permission(perm_type)
 		except frappe.PermissionError:
-			actions = { 'create': 'add', 'write': 'update'}
+			actions = {"create": "add", "write": "update"}
 
-			frappe.throw(_("You do not have permissions to {} items in a {}.")
-				.format(actions[perm_type], parent_doctype), title=_("Insufficient Permissions"))
+			frappe.throw(
+				_("You do not have permissions to {} items in a {}.").format(
+					actions[perm_type], parent_doctype
+				),
+				title=_("Insufficient Permissions"),
+			)
 
 	def validate_workflow_conditions(doc):
 		workflow = get_workflow_name(doc.doctype)
@@ -2006,27 +2482,41 @@
 
 		if not transitions:
 			frappe.throw(
-				_("You are not allowed to update as per the conditions set in {} Workflow.").format(get_link_to_form("Workflow", workflow)),
-				title=_("Insufficient Permissions")
+				_("You are not allowed to update as per the conditions set in {} Workflow.").format(
+					get_link_to_form("Workflow", workflow)
+				),
+				title=_("Insufficient Permissions"),
 			)
 
 	def get_new_child_item(item_row):
 		child_doctype = "Sales Order Item" if parent_doctype == "Sales Order" else "Purchase Order Item"
-		return set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, item_row)
+		return set_order_defaults(
+			parent_doctype, parent_doctype_name, child_doctype, child_docname, item_row
+		)
 
-	def validate_quantity(child_item, d):
-		if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty):
+	def validate_quantity(child_item, new_data):
+		if not flt(new_data.get("qty")):
+			frappe.throw(
+				_("Row # {0}: Quantity for Item {1} cannot be zero").format(
+					new_data.get("idx"), frappe.bold(new_data.get("item_code"))
+				),
+				title=_("Invalid Qty"),
+			)
+
+		if parent_doctype == "Sales Order" and flt(new_data.get("qty")) < flt(child_item.delivered_qty):
 			frappe.throw(_("Cannot set quantity less than delivered quantity"))
 
-		if parent_doctype == "Purchase Order" and flt(d.get("qty")) < flt(child_item.received_qty):
+		if parent_doctype == "Purchase Order" and flt(new_data.get("qty")) < flt(
+			child_item.received_qty
+		):
 			frappe.throw(_("Cannot set quantity less than received quantity"))
 
 	data = json.loads(trans_items)
 
-	sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation']
+	sales_doctypes = ["Sales Order", "Sales Invoice", "Delivery Note", "Quotation"]
 	parent = frappe.get_doc(parent_doctype, parent_doctype_name)
 
-	check_doc_permissions(parent, 'write')
+	check_doc_permissions(parent, "write")
 	validate_and_delete_children(parent, data)
 
 	for d in data:
@@ -2038,28 +2528,38 @@
 
 		if not d.get("docname"):
 			new_child_flag = True
-			check_doc_permissions(parent, 'create')
+			check_doc_permissions(parent, "create")
 			child_item = get_new_child_item(d)
 		else:
-			check_doc_permissions(parent, 'write')
-			child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname"))
+			check_doc_permissions(parent, "write")
+			child_item = frappe.get_doc(parent_doctype + " Item", d.get("docname"))
 
 			prev_rate, new_rate = flt(child_item.get("rate")), flt(d.get("rate"))
 			prev_qty, new_qty = flt(child_item.get("qty")), flt(d.get("qty"))
-			prev_con_fac, new_con_fac = flt(child_item.get("conversion_factor")), flt(d.get("conversion_factor"))
+			prev_con_fac, new_con_fac = flt(child_item.get("conversion_factor")), flt(
+				d.get("conversion_factor")
+			)
 			prev_uom, new_uom = child_item.get("uom"), d.get("uom")
 
-			if parent_doctype == 'Sales Order':
+			if parent_doctype == "Sales Order":
 				prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date")
-			elif parent_doctype == 'Purchase Order':
+			elif parent_doctype == "Purchase Order":
 				prev_date, new_date = child_item.get("schedule_date"), d.get("schedule_date")
 
 			rate_unchanged = prev_rate == new_rate
 			qty_unchanged = prev_qty == new_qty
 			uom_unchanged = prev_uom == new_uom
 			conversion_factor_unchanged = prev_con_fac == new_con_fac
-			date_unchanged = prev_date == getdate(new_date) if prev_date and new_date else False # in case of delivery note etc
-			if rate_unchanged and qty_unchanged and conversion_factor_unchanged and uom_unchanged and date_unchanged:
+			date_unchanged = (
+				prev_date == getdate(new_date) if prev_date and new_date else False
+			)  # in case of delivery note etc
+			if (
+				rate_unchanged
+				and qty_unchanged
+				and conversion_factor_unchanged
+				and uom_unchanged
+				and date_unchanged
+			):
 				continue
 
 		validate_quantity(child_item, d)
@@ -2069,9 +2569,14 @@
 		conv_fac_precision = child_item.precision("conversion_factor") or 2
 		qty_precision = child_item.precision("qty") or 2
 
-		if flt(child_item.billed_amt, rate_precision) > flt(flt(d.get("rate"), rate_precision) * flt(d.get("qty"), qty_precision), rate_precision):
-			frappe.throw(_("Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.")
-						 .format(child_item.idx, child_item.item_code))
+		if flt(child_item.billed_amt, rate_precision) > flt(
+			flt(d.get("rate"), rate_precision) * flt(d.get("qty"), qty_precision), rate_precision
+		):
+			frappe.throw(
+				_("Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.").format(
+					child_item.idx, child_item.item_code
+				)
+			)
 		else:
 			child_item.rate = flt(d.get("rate"), rate_precision)
 
@@ -2079,18 +2584,22 @@
 			if child_item.stock_uom == child_item.uom:
 				child_item.conversion_factor = 1
 			else:
-				child_item.conversion_factor = flt(d.get('conversion_factor'), conv_fac_precision)
+				child_item.conversion_factor = flt(d.get("conversion_factor"), conv_fac_precision)
 
 		if d.get("uom"):
 			child_item.uom = d.get("uom")
-			conversion_factor = flt(get_conversion_factor(child_item.item_code, child_item.uom).get("conversion_factor"))
-			child_item.conversion_factor = flt(d.get('conversion_factor'), conv_fac_precision) or conversion_factor
+			conversion_factor = flt(
+				get_conversion_factor(child_item.item_code, child_item.uom).get("conversion_factor")
+			)
+			child_item.conversion_factor = (
+				flt(d.get("conversion_factor"), conv_fac_precision) or conversion_factor
+			)
 
-		if d.get("delivery_date") and parent_doctype == 'Sales Order':
-			child_item.delivery_date = d.get('delivery_date')
+		if d.get("delivery_date") and parent_doctype == "Sales Order":
+			child_item.delivery_date = d.get("delivery_date")
 
-		if d.get("schedule_date") and parent_doctype == 'Purchase Order':
-			child_item.schedule_date = d.get('schedule_date')
+		if d.get("schedule_date") and parent_doctype == "Purchase Order":
+			child_item.schedule_date = d.get("schedule_date")
 
 		if flt(child_item.price_list_rate):
 			if flt(child_item.rate) > flt(child_item.price_list_rate):
@@ -2100,14 +2609,16 @@
 
 				if parent_doctype in sales_doctypes:
 					child_item.margin_type = "Amount"
-					child_item.margin_rate_or_amount = flt(child_item.rate - child_item.price_list_rate,
-						child_item.precision("margin_rate_or_amount"))
+					child_item.margin_rate_or_amount = flt(
+						child_item.rate - child_item.price_list_rate, child_item.precision("margin_rate_or_amount")
+					)
 					child_item.rate_with_margin = child_item.rate
 			else:
-				child_item.discount_percentage = flt((1 - flt(child_item.rate) / flt(child_item.price_list_rate)) * 100.0,
-					child_item.precision("discount_percentage"))
-				child_item.discount_amount = flt(
-					child_item.price_list_rate) - flt(child_item.rate)
+				child_item.discount_percentage = flt(
+					(1 - flt(child_item.rate) / flt(child_item.price_list_rate)) * 100.0,
+					child_item.precision("discount_percentage"),
+				)
+				child_item.discount_amount = flt(child_item.price_list_rate) - flt(child_item.rate)
 
 				if parent_doctype in sales_doctypes:
 					child_item.margin_type = ""
@@ -2130,11 +2641,12 @@
 	if parent_doctype == "Sales Order":
 		make_packing_list(parent)
 		parent.set_gross_profit()
-	frappe.get_doc('Authorization Control').validate_approving_authority(parent.doctype,
-		parent.company, parent.base_grand_total)
+	frappe.get_doc("Authorization Control").validate_approving_authority(
+		parent.doctype, parent.company, parent.base_grand_total
+	)
 
 	parent.set_payment_schedule()
-	if parent_doctype == 'Purchase Order':
+	if parent_doctype == "Purchase Order":
 		parent.validate_minimum_order_qty()
 		parent.validate_budget()
 		if parent.is_against_so():
@@ -2148,21 +2660,22 @@
 
 	parent.save()
 
-	if parent_doctype == 'Purchase Order':
-		update_last_purchase_rate(parent, is_submit = 1)
+	if parent_doctype == "Purchase Order":
+		update_last_purchase_rate(parent, is_submit=1)
 		parent.update_prevdoc_status()
 		parent.update_requested_qty()
 		parent.update_ordered_qty()
 		parent.update_ordered_and_reserved_qty()
 		parent.update_receiving_percentage()
-		if parent.is_subcontracted == "Yes":
+		if parent.is_subcontracted:
 			parent.update_reserved_qty_for_subcontract()
 			parent.create_raw_materials_supplied("supplied_items")
 			parent.save()
-	else:
+	else:  # Sales Order
+		parent.validate_warehouse()
 		parent.update_reserved_qty()
 		parent.update_project()
-		parent.update_prevdoc_status('submit')
+		parent.update_prevdoc_status("submit")
 		parent.update_delivery_status()
 
 	parent.reload()
@@ -2172,10 +2685,12 @@
 	parent.update_billing_percentage()
 	parent.set_status()
 
+
 @erpnext.allow_regional
 def validate_regional(doc):
 	pass
 
+
 @erpnext.allow_regional
 def validate_einvoice_fields(doc):
 	pass
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index b740476..f28de3b 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -20,12 +20,14 @@
 class QtyMismatchError(ValidationError):
 	pass
 
+
 class BuyingController(StockController, Subcontracting):
+	def __setup__(self):
+		self.flags.ignore_permlevel_for_fields = ["buying_price_list", "price_list_currency"]
 
 	def get_feed(self):
 		if self.get("supplier_name"):
-			return _("From {0} | {1} {2}").format(self.supplier_name, self.currency,
-				self.grand_total)
+			return _("From {0} | {1} {2}").format(self.supplier_name, self.currency, self.grand_total)
 
 	def validate(self):
 		super(BuyingController, self).validate()
@@ -40,16 +42,18 @@
 		self.set_supplier_address()
 		self.validate_asset_return()
 
-		if self.doctype=="Purchase Invoice":
+		if self.doctype == "Purchase Invoice":
 			self.validate_purchase_receipt_if_update_stock()
 
-		if self.doctype=="Purchase Receipt" or (self.doctype=="Purchase Invoice" and self.update_stock):
+		if self.doctype == "Purchase Receipt" or (
+			self.doctype == "Purchase Invoice" and self.update_stock
+		):
 			# self.validate_purchase_return()
 			self.validate_rejected_warehouse()
 			self.validate_accepted_rejected_qty()
 			validate_for_items(self)
 
-			#sub-contracting
+			# sub-contracting
 			self.validate_for_subcontracting()
 			self.create_raw_materials_supplied("supplied_items")
 			self.set_landed_cost_voucher_amount()
@@ -59,8 +63,12 @@
 
 	def onload(self):
 		super(BuyingController, self).onload()
-		self.set_onload("backflush_based_on", frappe.db.get_single_value('Buying Settings',
-			'backflush_raw_materials_of_subcontract_based_on'))
+		self.set_onload(
+			"backflush_based_on",
+			frappe.db.get_single_value(
+				"Buying Settings", "backflush_raw_materials_of_subcontract_based_on"
+			),
+		)
 
 	def set_missing_values(self, for_validate=False):
 		super(BuyingController, self).set_missing_values(for_validate)
@@ -77,9 +85,9 @@
 					doctype=self.doctype,
 					company=self.company,
 					party_address=self.get("supplier_address"),
-					shipping_address=self.get('shipping_address'),
-					fetch_payment_terms_template= not self.get('ignore_default_payment_terms_template'),
-					ignore_permissions=self.flags.ignore_permissions
+					shipping_address=self.get("shipping_address"),
+					fetch_payment_terms_template=not self.get("ignore_default_payment_terms_template"),
+					ignore_permissions=self.flags.ignore_permissions,
 				)
 			)
 
@@ -88,14 +96,16 @@
 	def set_supplier_from_item_default(self):
 		if self.meta.get_field("supplier") and not self.supplier:
 			for d in self.get("items"):
-				supplier = frappe.db.get_value("Item Default",
-					{"parent": d.item_code, "company": self.company}, "default_supplier")
+				supplier = frappe.db.get_value(
+					"Item Default", {"parent": d.item_code, "company": self.company}, "default_supplier"
+				)
 				if supplier:
 					self.supplier = supplier
 				else:
 					item_group = frappe.db.get_value("Item", d.item_code, "item_group")
-					supplier = frappe.db.get_value("Item Default",
-					{"parent": item_group, "company": self.company}, "default_supplier")
+					supplier = frappe.db.get_value(
+						"Item Default", {"parent": item_group, "company": self.company}, "default_supplier"
+					)
 					if supplier:
 						self.supplier = supplier
 					break
@@ -106,55 +116,71 @@
 			self.update_tax_category(msg)
 
 	def update_tax_category(self, msg):
-		tax_for_valuation = [d for d in self.get("taxes")
-				if d.category in ["Valuation", "Valuation and Total"]]
+		tax_for_valuation = [
+			d for d in self.get("taxes") if d.category in ["Valuation", "Valuation and Total"]
+		]
 
 		if tax_for_valuation:
 			for d in tax_for_valuation:
-				d.category = 'Total'
+				d.category = "Total"
 
 			msgprint(msg)
 
 	def validate_asset_return(self):
-		if self.doctype not in ['Purchase Receipt', 'Purchase Invoice'] or not self.is_return:
+		if self.doctype not in ["Purchase Receipt", "Purchase Invoice"] or not self.is_return:
 			return
 
-		purchase_doc_field = 'purchase_receipt' if self.doctype == 'Purchase Receipt' else 'purchase_invoice'
-		not_cancelled_asset = [d.name for d in frappe.db.get_all("Asset", {
-			purchase_doc_field: self.return_against,
-			"docstatus": 1
-		})]
+		purchase_doc_field = (
+			"purchase_receipt" if self.doctype == "Purchase Receipt" else "purchase_invoice"
+		)
+		not_cancelled_asset = [
+			d.name
+			for d in frappe.db.get_all("Asset", {purchase_doc_field: self.return_against, "docstatus": 1})
+		]
 		if self.is_return and len(not_cancelled_asset):
-			frappe.throw(_("{} has submitted assets linked to it. You need to cancel the assets to create purchase return.")
-				.format(self.return_against), title=_("Not Allowed"))
+			frappe.throw(
+				_(
+					"{} has submitted assets linked to it. You need to cancel the assets to create purchase return."
+				).format(self.return_against),
+				title=_("Not Allowed"),
+			)
 
 	def get_asset_items(self):
-		if self.doctype not in ['Purchase Order', 'Purchase Invoice', 'Purchase Receipt']:
+		if self.doctype not in ["Purchase Order", "Purchase Invoice", "Purchase Receipt"]:
 			return []
 
 		return [d.item_code for d in self.items if d.is_fixed_asset]
 
 	def set_landed_cost_voucher_amount(self):
 		for d in self.get("items"):
-			lc_voucher_data = frappe.db.sql("""select sum(applicable_charges), cost_center
+			lc_voucher_data = frappe.db.sql(
+				"""select sum(applicable_charges), cost_center
 				from `tabLanded Cost Item`
-				where docstatus = 1 and purchase_receipt_item = %s""", d.name)
+				where docstatus = 1 and purchase_receipt_item = %s""",
+				d.name,
+			)
 			d.landed_cost_voucher_amount = lc_voucher_data[0][0] if lc_voucher_data else 0.0
 			if not d.cost_center and lc_voucher_data and lc_voucher_data[0][1]:
-				d.db_set('cost_center', lc_voucher_data[0][1])
+				d.db_set("cost_center", lc_voucher_data[0][1])
 
 	def validate_from_warehouse(self):
-		for item in self.get('items'):
-			if item.get('from_warehouse') and (item.get('from_warehouse') == item.get('warehouse')):
-				frappe.throw(_("Row #{0}: Accepted Warehouse and Supplier Warehouse cannot be same").format(item.idx))
+		for item in self.get("items"):
+			if item.get("from_warehouse") and (item.get("from_warehouse") == item.get("warehouse")):
+				frappe.throw(
+					_("Row #{0}: Accepted Warehouse and Supplier Warehouse cannot be same").format(item.idx)
+				)
 
-			if item.get('from_warehouse') and self.get('is_subcontracted') == 'Yes':
-				frappe.throw(_("Row #{0}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor").format(item.idx))
+			if item.get("from_warehouse") and self.get("is_subcontracted"):
+				frappe.throw(
+					_(
+						"Row #{0}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor"
+					).format(item.idx)
+				)
 
 	def set_supplier_address(self):
 		address_dict = {
-			'supplier_address': 'address_display',
-			'shipping_address': 'shipping_address_display'
+			"supplier_address": "address_display",
+			"shipping_address": "shipping_address_display",
 		}
 
 		for address_field, address_display_field in address_dict.items():
@@ -163,6 +189,7 @@
 
 	def set_total_in_words(self):
 		from frappe.utils import money_in_words
+
 		if self.meta.get_field("base_in_words"):
 			if self.meta.get_field("base_rounded_total") and not self.is_rounded_total_disabled():
 				amount = self.base_rounded_total
@@ -181,10 +208,10 @@
 	# update valuation rate
 	def update_valuation_rate(self, reset_outgoing_rate=True):
 		"""
-			item_tax_amount is the total tax amount applied on that item
-			stored for valuation
+		item_tax_amount is the total tax amount applied on that item
+		stored for valuation
 
-			TODO: rename item_tax_amount to valuation_tax_amount
+		TODO: rename item_tax_amount to valuation_tax_amount
 		"""
 		stock_and_asset_items = []
 		stock_and_asset_items = self.get_stock_items() + self.get_asset_items()
@@ -192,36 +219,50 @@
 		stock_and_asset_items_qty, stock_and_asset_items_amount = 0, 0
 		last_item_idx = 1
 		for d in self.get("items"):
-			if (d.item_code and d.item_code in stock_and_asset_items):
+			if d.item_code and d.item_code in stock_and_asset_items:
 				stock_and_asset_items_qty += flt(d.qty)
 				stock_and_asset_items_amount += flt(d.base_net_amount)
 				last_item_idx = d.idx
 
-		total_valuation_amount = sum(flt(d.base_tax_amount_after_discount_amount) for d in self.get("taxes")
-			if d.category in ["Valuation", "Valuation and Total"])
+		total_valuation_amount = sum(
+			flt(d.base_tax_amount_after_discount_amount)
+			for d in self.get("taxes")
+			if d.category in ["Valuation", "Valuation and Total"]
+		)
 
 		valuation_amount_adjustment = total_valuation_amount
 		for i, item in enumerate(self.get("items")):
 			if item.item_code and item.qty and item.item_code in stock_and_asset_items:
-				item_proportion = flt(item.base_net_amount) / stock_and_asset_items_amount if stock_and_asset_items_amount \
+				item_proportion = (
+					flt(item.base_net_amount) / stock_and_asset_items_amount
+					if stock_and_asset_items_amount
 					else flt(item.qty) / stock_and_asset_items_qty
+				)
 
 				if i == (last_item_idx - 1):
-					item.item_tax_amount = flt(valuation_amount_adjustment,
-						self.precision("item_tax_amount", item))
+					item.item_tax_amount = flt(
+						valuation_amount_adjustment, self.precision("item_tax_amount", item)
+					)
 				else:
-					item.item_tax_amount = flt(item_proportion * total_valuation_amount,
-						self.precision("item_tax_amount", item))
+					item.item_tax_amount = flt(
+						item_proportion * total_valuation_amount, self.precision("item_tax_amount", item)
+					)
 					valuation_amount_adjustment -= item.item_tax_amount
 
 				self.round_floats_in(item)
-				if flt(item.conversion_factor)==0.0:
-					item.conversion_factor = get_conversion_factor(item.item_code, item.uom).get("conversion_factor") or 1.0
+				if flt(item.conversion_factor) == 0.0:
+					item.conversion_factor = (
+						get_conversion_factor(item.item_code, item.uom).get("conversion_factor") or 1.0
+					)
 
 				qty_in_stock_uom = flt(item.qty * item.conversion_factor)
 				item.rm_supp_cost = self.get_supplied_items_cost(item.name, reset_outgoing_rate)
-				item.valuation_rate = ((item.base_net_amount + item.item_tax_amount + item.rm_supp_cost
-					 + flt(item.landed_cost_voucher_amount)) / qty_in_stock_uom)
+				item.valuation_rate = (
+					item.base_net_amount
+					+ item.item_tax_amount
+					+ item.rm_supp_cost
+					+ flt(item.landed_cost_voucher_amount)
+				) / qty_in_stock_uom
 			else:
 				item.valuation_rate = 0.0
 
@@ -242,46 +283,56 @@
 				# Get outgoing rate based on original item cost based on valuation method
 
 				if not d.get(frappe.scrub(ref_doctype)):
-					outgoing_rate = get_incoming_rate({
-						"item_code": d.item_code,
-						"warehouse": d.get('from_warehouse'),
-						"posting_date": self.get('posting_date') or self.get('transation_date'),
-						"posting_time": self.get('posting_time'),
-						"qty": -1 * flt(d.get('stock_qty')),
-						"serial_no": d.get('serial_no'),
-						"batch_no": d.get("batch_no"),
-						"company": self.company,
-						"voucher_type": self.doctype,
-						"voucher_no": self.name,
-						"allow_zero_valuation": d.get("allow_zero_valuation")
-					}, raise_error_if_no_rate=False)
+					outgoing_rate = get_incoming_rate(
+						{
+							"item_code": d.item_code,
+							"warehouse": d.get("from_warehouse"),
+							"posting_date": self.get("posting_date") or self.get("transation_date"),
+							"posting_time": self.get("posting_time"),
+							"qty": -1 * flt(d.get("stock_qty")),
+							"serial_no": d.get("serial_no"),
+							"batch_no": d.get("batch_no"),
+							"company": self.company,
+							"voucher_type": self.doctype,
+							"voucher_no": self.name,
+							"allow_zero_valuation": d.get("allow_zero_valuation"),
+						},
+						raise_error_if_no_rate=False,
+					)
 
-					rate = flt(outgoing_rate * d.conversion_factor, d.precision('rate'))
+					rate = flt(outgoing_rate * d.conversion_factor, d.precision("rate"))
 				else:
-					rate = frappe.db.get_value(ref_doctype, d.get(frappe.scrub(ref_doctype)), 'rate')
+					rate = frappe.db.get_value(ref_doctype, d.get(frappe.scrub(ref_doctype)), "rate")
 
 				if self.is_internal_transfer():
 					if rate != d.rate:
 						d.rate = rate
-						d.discount_percentage = 0
-						d.discount_amount = 0
-						frappe.msgprint(_("Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer")
-							.format(d.idx), alert=1)
+						frappe.msgprint(
+							_(
+								"Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer"
+							).format(d.idx),
+							alert=1,
+						)
+					d.discount_percentage = 0.0
+					d.discount_amount = 0.0
+					d.margin_rate_or_amount = 0.0
 
 	def get_supplied_items_cost(self, item_row_id, reset_outgoing_rate=True):
 		supplied_items_cost = 0.0
 		for d in self.get("supplied_items"):
 			if d.reference_name == item_row_id:
-				if reset_outgoing_rate and frappe.get_cached_value('Item', d.rm_item_code, 'is_stock_item'):
-					rate = get_incoming_rate({
-						"item_code": d.rm_item_code,
-						"warehouse": self.supplier_warehouse,
-						"posting_date": self.posting_date,
-						"posting_time": self.posting_time,
-						"qty": -1 * d.consumed_qty,
-						"serial_no": d.serial_no,
-						"batch_no": d.batch_no,
-					})
+				if reset_outgoing_rate and frappe.get_cached_value("Item", d.rm_item_code, "is_stock_item"):
+					rate = get_incoming_rate(
+						{
+							"item_code": d.rm_item_code,
+							"warehouse": self.supplier_warehouse,
+							"posting_date": self.posting_date,
+							"posting_time": self.posting_time,
+							"qty": -1 * d.consumed_qty,
+							"serial_no": d.serial_no,
+							"batch_no": d.batch_no,
+						}
+					)
 
 					if rate > 0:
 						d.rate = rate
@@ -292,10 +343,7 @@
 		return supplied_items_cost
 
 	def validate_for_subcontracting(self):
-		if not self.is_subcontracted and self.sub_contracted_items:
-			frappe.throw(_("Please enter 'Is Subcontracted' as Yes or No"))
-
-		if self.is_subcontracted == "Yes":
+		if self.is_subcontracted:
 			if self.doctype in ["Purchase Receipt", "Purchase Invoice"] and not self.supplier_warehouse:
 				frappe.throw(_("Supplier Warehouse mandatory for sub-contracted {0}").format(self.doctype))
 
@@ -316,27 +364,25 @@
 					item.bom = None
 
 	def create_raw_materials_supplied(self, raw_material_table):
-		if self.is_subcontracted=="Yes":
+		if self.is_subcontracted:
 			self.set_materials_for_subcontracted_items(raw_material_table)
 
 		elif self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
 			for item in self.get("items"):
 				item.rm_supp_cost = 0.0
 
-		if self.is_subcontracted == "No" and self.get("supplied_items"):
-			self.set('supplied_items', [])
+		if not self.is_subcontracted and self.get("supplied_items"):
+			self.set("supplied_items", [])
 
 	@property
 	def sub_contracted_items(self):
 		if not hasattr(self, "_sub_contracted_items"):
 			self._sub_contracted_items = []
-			item_codes = list(set(item.item_code for item in
-				self.get("items")))
+			item_codes = list(set(item.item_code for item in self.get("items")))
 			if item_codes:
-				items = frappe.get_all('Item', filters={
-					'name': ['in', item_codes],
-					'is_sub_contracted_item': 1
-				})
+				items = frappe.get_all(
+					"Item", filters={"name": ["in", item_codes], "is_sub_contracted_item": 1}
+				)
 				self._sub_contracted_items = [item.name for item in items]
 
 		return self._sub_contracted_items
@@ -350,9 +396,11 @@
 					frappe.throw(_("Row {0}: Conversion Factor is mandatory").format(d.idx))
 				d.stock_qty = flt(d.qty) * flt(d.conversion_factor)
 
-				if self.doctype=="Purchase Receipt" and d.meta.get_field("received_stock_qty"):
+				if self.doctype == "Purchase Receipt" and d.meta.get_field("received_stock_qty"):
 					# Set Received Qty in Stock UOM
-					d.received_stock_qty = flt(d.received_qty) * flt(d.conversion_factor, d.precision("conversion_factor"))
+					d.received_stock_qty = flt(d.received_qty) * flt(
+						d.conversion_factor, d.precision("conversion_factor")
+					)
 
 	def validate_purchase_return(self):
 		for d in self.get("items"):
@@ -368,20 +416,26 @@
 					d.rejected_warehouse = self.rejected_warehouse
 
 				if not d.rejected_warehouse:
-					frappe.throw(_("Row #{0}: Rejected Warehouse is mandatory against rejected Item {1}").format(d.idx, d.item_code))
+					frappe.throw(
+						_("Row #{0}: Rejected Warehouse is mandatory against rejected Item {1}").format(
+							d.idx, d.item_code
+						)
+					)
 
 	# validate accepted and rejected qty
 	def validate_accepted_rejected_qty(self):
 		for d in self.get("items"):
-			self.validate_negative_quantity(d, ["received_qty","qty", "rejected_qty"])
+			self.validate_negative_quantity(d, ["received_qty", "qty", "rejected_qty"])
 
 			if not flt(d.received_qty) and (flt(d.qty) or flt(d.rejected_qty)):
 				d.received_qty = flt(d.qty) + flt(d.rejected_qty)
 
 			# Check Received Qty = Accepted Qty + Rejected Qty
 			val = flt(d.qty) + flt(d.rejected_qty)
-			if (flt(val, d.precision("received_qty")) != flt(d.received_qty, d.precision("received_qty"))):
-				message = _("Row #{0}: Received Qty must be equal to Accepted + Rejected Qty for Item {1}").format(d.idx, d.item_code)
+			if flt(val, d.precision("received_qty")) != flt(d.received_qty, d.precision("received_qty")):
+				message = _(
+					"Row #{0}: Received Qty must be equal to Accepted + Rejected Qty for Item {1}"
+				).format(d.idx, d.item_code)
 				frappe.throw(msg=message, title=_("Mismatch"), exc=QtyMismatchError)
 
 	def validate_negative_quantity(self, item_row, field_list):
@@ -391,15 +445,20 @@
 		item_row = item_row.as_dict()
 		for fieldname in field_list:
 			if flt(item_row[fieldname]) < 0:
-				frappe.throw(_("Row #{0}: {1} can not be negative for item {2}").format(item_row['idx'],
-					frappe.get_meta(item_row.doctype).get_label(fieldname), item_row['item_code']))
+				frappe.throw(
+					_("Row #{0}: {1} can not be negative for item {2}").format(
+						item_row["idx"],
+						frappe.get_meta(item_row.doctype).get_label(fieldname),
+						item_row["item_code"],
+					)
+				)
 
 	def check_for_on_hold_or_closed_status(self, ref_doctype, ref_fieldname):
 		for d in self.get("items"):
 			if d.get(ref_fieldname):
 				status = frappe.db.get_value(ref_doctype, d.get(ref_fieldname), "status")
 				if status in ("Closed", "On Hold"):
-					frappe.throw(_("{0} {1} is {2}").format(ref_doctype,d.get(ref_fieldname), status))
+					frappe.throw(_("{0} {1} is {2}").format(ref_doctype, d.get(ref_fieldname), status))
 
 	def update_stock_ledger(self, allow_negative_stock=False, via_landed_cost_voucher=False):
 		self.update_ordered_and_reserved_qty()
@@ -407,76 +466,92 @@
 		sl_entries = []
 		stock_items = self.get_stock_items()
 
-		for d in self.get('items'):
-			if d.item_code in stock_items and d.warehouse:
+		for d in self.get("items"):
+			if d.item_code not in stock_items:
+				continue
+
+			if d.warehouse:
 				pr_qty = flt(d.qty) * flt(d.conversion_factor)
 
 				if pr_qty:
 
-					if d.from_warehouse and ((not cint(self.is_return) and self.docstatus==1)
-						or (cint(self.is_return) and self.docstatus==2)):
-						from_warehouse_sle = self.get_sl_entries(d, {
-							"actual_qty": -1 * pr_qty,
-							"warehouse": d.from_warehouse,
-							"outgoing_rate": d.rate,
-							"recalculate_rate": 1,
-							"dependant_sle_voucher_detail_no": d.name
-						})
+					if d.from_warehouse and (
+						(not cint(self.is_return) and self.docstatus == 1)
+						or (cint(self.is_return) and self.docstatus == 2)
+					):
+						from_warehouse_sle = self.get_sl_entries(
+							d,
+							{
+								"actual_qty": -1 * pr_qty,
+								"warehouse": d.from_warehouse,
+								"outgoing_rate": d.rate,
+								"recalculate_rate": 1,
+								"dependant_sle_voucher_detail_no": d.name,
+							},
+						)
 
 						sl_entries.append(from_warehouse_sle)
 
-					sle = self.get_sl_entries(d, {
-						"actual_qty": flt(pr_qty),
-						"serial_no": cstr(d.serial_no).strip()
-					})
-					if self.is_return:
-						outgoing_rate = get_rate_for_return(self.doctype, self.name, d.item_code, self.return_against, item_row=d)
+					sle = self.get_sl_entries(
+						d, {"actual_qty": flt(pr_qty), "serial_no": cstr(d.serial_no).strip()}
+					)
 
-						sle.update({
-							"outgoing_rate": outgoing_rate,
-							"recalculate_rate": 1
-						})
+					if self.is_return:
+						outgoing_rate = get_rate_for_return(
+							self.doctype, self.name, d.item_code, self.return_against, item_row=d
+						)
+
+						sle.update({"outgoing_rate": outgoing_rate, "recalculate_rate": 1})
 						if d.from_warehouse:
 							sle.dependant_sle_voucher_detail_no = d.name
 					else:
 						val_rate_db_precision = 6 if cint(self.precision("valuation_rate", d)) <= 6 else 9
 						incoming_rate = flt(d.valuation_rate, val_rate_db_precision)
-						sle.update({
-							"incoming_rate": incoming_rate,
-							"recalculate_rate": 1 if (self.is_subcontracted and d.bom) or d.from_warehouse else 0
-						})
+						sle.update(
+							{
+								"incoming_rate": incoming_rate,
+								"recalculate_rate": 1 if (self.is_subcontracted and d.bom) or d.from_warehouse else 0,
+							}
+						)
 					sl_entries.append(sle)
 
-					if d.from_warehouse and ((not cint(self.is_return) and self.docstatus==2)
-						or (cint(self.is_return) and self.docstatus==1)):
-						from_warehouse_sle = self.get_sl_entries(d, {
-							"actual_qty": -1 * pr_qty,
-							"warehouse": d.from_warehouse,
-							"recalculate_rate": 1
-						})
+					if d.from_warehouse and (
+						(not cint(self.is_return) and self.docstatus == 2)
+						or (cint(self.is_return) and self.docstatus == 1)
+					):
+						from_warehouse_sle = self.get_sl_entries(
+							d, {"actual_qty": -1 * pr_qty, "warehouse": d.from_warehouse, "recalculate_rate": 1}
+						)
 
 						sl_entries.append(from_warehouse_sle)
 
-				if flt(d.rejected_qty) != 0:
-					sl_entries.append(self.get_sl_entries(d, {
-						"warehouse": d.rejected_warehouse,
-						"actual_qty": flt(d.rejected_qty) * flt(d.conversion_factor),
-						"serial_no": cstr(d.rejected_serial_no).strip(),
-						"incoming_rate": 0.0
-					}))
+			if flt(d.rejected_qty) != 0:
+				sl_entries.append(
+					self.get_sl_entries(
+						d,
+						{
+							"warehouse": d.rejected_warehouse,
+							"actual_qty": flt(d.rejected_qty) * flt(d.conversion_factor),
+							"serial_no": cstr(d.rejected_serial_no).strip(),
+							"incoming_rate": 0.0,
+						},
+					)
+				)
 
 		self.make_sl_entries_for_supplier_warehouse(sl_entries)
-		self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock,
-			via_landed_cost_voucher=via_landed_cost_voucher)
+		self.make_sl_entries(
+			sl_entries,
+			allow_negative_stock=allow_negative_stock,
+			via_landed_cost_voucher=via_landed_cost_voucher,
+		)
 
 	def update_ordered_and_reserved_qty(self):
 		po_map = {}
 		for d in self.get("items"):
-			if self.doctype=="Purchase Receipt" \
-				and d.purchase_order:
-					po_map.setdefault(d.purchase_order, []).append(d.purchase_order_item)
+			if self.doctype == "Purchase Receipt" and d.purchase_order:
+				po_map.setdefault(d.purchase_order, []).append(d.purchase_order_item)
 
-			elif self.doctype=="Purchase Invoice" and d.purchase_order and d.po_detail:
+			elif self.doctype == "Purchase Invoice" and d.purchase_order and d.po_detail:
 				po_map.setdefault(d.purchase_order, []).append(d.po_detail)
 
 		for po, po_item_rows in po_map.items():
@@ -484,68 +559,78 @@
 				po_obj = frappe.get_doc("Purchase Order", po)
 
 				if po_obj.status in ["Closed", "Cancelled"]:
-					frappe.throw(_("{0} {1} is cancelled or closed").format(_("Purchase Order"), po),
-						frappe.InvalidStatusError)
+					frappe.throw(
+						_("{0} {1} is cancelled or closed").format(_("Purchase Order"), po),
+						frappe.InvalidStatusError,
+					)
 
 				po_obj.update_ordered_qty(po_item_rows)
 				if self.is_subcontracted:
 					po_obj.update_reserved_qty_for_subcontract()
 
 	def make_sl_entries_for_supplier_warehouse(self, sl_entries):
-		if hasattr(self, 'supplied_items'):
-			for d in self.get('supplied_items'):
+		if hasattr(self, "supplied_items"):
+			for d in self.get("supplied_items"):
 				# negative quantity is passed, as raw material qty has to be decreased
 				# when PR is submitted and it has to be increased when PR is cancelled
-				sl_entries.append(self.get_sl_entries(d, {
-					"item_code": d.rm_item_code,
-					"warehouse": self.supplier_warehouse,
-					"actual_qty": -1*flt(d.consumed_qty),
-					"dependant_sle_voucher_detail_no": d.reference_name
-				}))
+				sl_entries.append(
+					self.get_sl_entries(
+						d,
+						{
+							"item_code": d.rm_item_code,
+							"warehouse": self.supplier_warehouse,
+							"actual_qty": -1 * flt(d.consumed_qty),
+							"dependant_sle_voucher_detail_no": d.reference_name,
+						},
+					)
+				)
 
 	def on_submit(self):
-		if self.get('is_return'):
+		if self.get("is_return"):
 			return
 
-		if self.doctype in ['Purchase Receipt', 'Purchase Invoice']:
-			field = 'purchase_invoice' if self.doctype == 'Purchase Invoice' else 'purchase_receipt'
+		if self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
+			field = "purchase_invoice" if self.doctype == "Purchase Invoice" else "purchase_receipt"
 
 			self.process_fixed_asset()
 			self.update_fixed_asset(field)
 
-		if self.doctype in ['Purchase Order', 'Purchase Receipt']:
-			update_last_purchase_rate(self, is_submit = 1)
+		if self.doctype in ["Purchase Order", "Purchase Receipt"]:
+			update_last_purchase_rate(self, is_submit=1)
 
 	def on_cancel(self):
 		super(BuyingController, self).on_cancel()
 
-		if self.get('is_return'):
+		if self.get("is_return"):
 			return
 
-		if self.doctype in ['Purchase Order', 'Purchase Receipt']:
-			update_last_purchase_rate(self, is_submit = 0)
+		if self.doctype in ["Purchase Order", "Purchase Receipt"]:
+			update_last_purchase_rate(self, is_submit=0)
 
-		if self.doctype in ['Purchase Receipt', 'Purchase Invoice']:
-			field = 'purchase_invoice' if self.doctype == 'Purchase Invoice' else 'purchase_receipt'
+		if self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
+			field = "purchase_invoice" if self.doctype == "Purchase Invoice" else "purchase_receipt"
 
 			self.delete_linked_asset()
 			self.update_fixed_asset(field, delete_asset=True)
 
 	def validate_budget(self):
 		if self.docstatus == 1:
-			for data in self.get('items'):
+			for data in self.get("items"):
 				args = data.as_dict()
-				args.update({
-					'doctype': self.doctype,
-					'company': self.company,
-					'posting_date': (self.schedule_date
-						if self.doctype == 'Material Request' else self.transaction_date)
-				})
+				args.update(
+					{
+						"doctype": self.doctype,
+						"company": self.company,
+						"posting_date": (
+							self.schedule_date if self.doctype == "Material Request" else self.transaction_date
+						),
+					}
+				)
 
 				validate_expense_against_budget(args)
 
 	def process_fixed_asset(self):
-		if self.doctype == 'Purchase Invoice' and not self.update_stock:
+		if self.doctype == "Purchase Invoice" and not self.update_stock:
 			return
 
 		asset_items = self.get_asset_items()
@@ -560,12 +645,12 @@
 			if d.is_fixed_asset:
 				item_data = items_data.get(d.item_code)
 
-				if item_data.get('auto_create_assets'):
+				if item_data.get("auto_create_assets"):
 					# If asset has to be auto created
 					# Check for asset naming series
-					if item_data.get('asset_naming_series'):
+					if item_data.get("asset_naming_series"):
 						created_assets = []
-						if item_data.get('is_grouped_asset'):
+						if item_data.get("is_grouped_asset"):
 							asset = self.make_asset(d, is_grouped_asset=True)
 							created_assets.append(asset)
 						else:
@@ -575,21 +660,31 @@
 
 						if len(created_assets) > 5:
 							# dont show asset form links if more than 5 assets are created
-							messages.append(_('{} Assets created for {}').format(len(created_assets), frappe.bold(d.item_code)))
-						else:
-							assets_link = list(map(lambda d: frappe.utils.get_link_to_form('Asset', d), created_assets))
-							assets_link = frappe.bold(','.join(assets_link))
-
-							is_plural = 's' if len(created_assets) != 1 else ''
 							messages.append(
-								_('Asset{} {assets_link} created for {}').format(is_plural, frappe.bold(d.item_code), assets_link=assets_link)
+								_("{} Assets created for {}").format(len(created_assets), frappe.bold(d.item_code))
+							)
+						else:
+							assets_link = list(map(lambda d: frappe.utils.get_link_to_form("Asset", d), created_assets))
+							assets_link = frappe.bold(",".join(assets_link))
+
+							is_plural = "s" if len(created_assets) != 1 else ""
+							messages.append(
+								_("Asset{} {assets_link} created for {}").format(
+									is_plural, frappe.bold(d.item_code), assets_link=assets_link
+								)
 							)
 					else:
-						frappe.throw(_("Row {}: Asset Naming Series is mandatory for the auto creation for item {}")
-							.format(d.idx, frappe.bold(d.item_code)))
+						frappe.throw(
+							_("Row {}: Asset Naming Series is mandatory for the auto creation for item {}").format(
+								d.idx, frappe.bold(d.item_code)
+							)
+						)
 				else:
-					messages.append(_("Assets not created for {0}. You will have to create asset manually.")
-						.format(frappe.bold(d.item_code)))
+					messages.append(
+						_("Assets not created for {0}. You will have to create asset manually.").format(
+							frappe.bold(d.item_code)
+						)
+					)
 
 		for message in messages:
 			frappe.msgprint(message, title="Success", indicator="green")
@@ -598,31 +693,34 @@
 		if not row.asset_location:
 			frappe.throw(_("Row {0}: Enter location for the asset item {1}").format(row.idx, row.item_code))
 
-		item_data = frappe.db.get_value('Item',
-			row.item_code, ['asset_naming_series', 'asset_category'], as_dict=1)
+		item_data = frappe.db.get_value(
+			"Item", row.item_code, ["asset_naming_series", "asset_category"], as_dict=1
+		)
 
 		if is_grouped_asset:
 			purchase_amount = flt(row.base_amount + row.item_tax_amount)
 		else:
 			purchase_amount = flt(row.base_rate + row.item_tax_amount)
 
-		asset = frappe.get_doc({
-			'doctype': 'Asset',
-			'item_code': row.item_code,
-			'asset_name': row.item_name,
-			'naming_series': item_data.get('asset_naming_series') or 'AST',
-			'asset_category': item_data.get('asset_category'),
-			'location': row.asset_location,
-			'company': self.company,
-			'supplier': self.supplier,
-			'purchase_date': self.posting_date,
-			'calculate_depreciation': 1,
-			'purchase_receipt_amount': purchase_amount,
-			'gross_purchase_amount': purchase_amount,
-			'asset_quantity': row.qty if is_grouped_asset else 0,
-			'purchase_receipt': self.name if self.doctype == 'Purchase Receipt' else None,
-			'purchase_invoice': self.name if self.doctype == 'Purchase Invoice' else None
-		})
+		asset = frappe.get_doc(
+			{
+				"doctype": "Asset",
+				"item_code": row.item_code,
+				"asset_name": row.item_name,
+				"naming_series": item_data.get("asset_naming_series") or "AST",
+				"asset_category": item_data.get("asset_category"),
+				"location": row.asset_location,
+				"company": self.company,
+				"supplier": self.supplier,
+				"purchase_date": self.posting_date,
+				"calculate_depreciation": 1,
+				"purchase_receipt_amount": purchase_amount,
+				"gross_purchase_amount": purchase_amount,
+				"asset_quantity": row.qty if is_grouped_asset else 0,
+				"purchase_receipt": self.name if self.doctype == "Purchase Receipt" else None,
+				"purchase_invoice": self.name if self.doctype == "Purchase Invoice" else None,
+			}
+		)
 
 		asset.flags.ignore_validate = True
 		asset.flags.ignore_mandatory = True
@@ -631,22 +729,25 @@
 
 		return asset.name
 
-	def update_fixed_asset(self, field, delete_asset = False):
+	def update_fixed_asset(self, field, delete_asset=False):
 		for d in self.get("items"):
 			if d.is_fixed_asset:
-				is_auto_create_enabled = frappe.db.get_value('Item', d.item_code, 'auto_create_assets')
-				assets = frappe.db.get_all('Asset', filters={ field : self.name, 'item_code' : d.item_code })
+				is_auto_create_enabled = frappe.db.get_value("Item", d.item_code, "auto_create_assets")
+				assets = frappe.db.get_all("Asset", filters={field: self.name, "item_code": d.item_code})
 
 				for asset in assets:
-					asset = frappe.get_doc('Asset', asset.name)
+					asset = frappe.get_doc("Asset", asset.name)
 					if delete_asset and is_auto_create_enabled:
 						# need to delete movements to delete assets otherwise throws link exists error
 						movements = frappe.db.sql(
 							"""SELECT asm.name
 							FROM `tabAsset Movement` asm, `tabAsset Movement Item` asm_item
-							WHERE asm_item.parent=asm.name and asm_item.asset=%s""", asset.name, as_dict=1)
+							WHERE asm_item.parent=asm.name and asm_item.asset=%s""",
+							asset.name,
+							as_dict=1,
+						)
 						for movement in movements:
-							frappe.delete_doc('Asset Movement', movement.name, force=1)
+							frappe.delete_doc("Asset Movement", movement.name, force=1)
 						frappe.delete_doc("Asset", asset.name, force=1)
 						continue
 
@@ -659,8 +760,11 @@
 							asset.set(field, None)
 							asset.supplier = None
 						if asset.docstatus == 1 and delete_asset:
-							frappe.throw(_('Cannot cancel this document as it is linked with submitted asset {0}. Please cancel it to continue.')
-								.format(frappe.utils.get_link_to_form('Asset', asset.name)))
+							frappe.throw(
+								_(
+									"Cannot cancel this document as it is linked with submitted asset {0}. Please cancel it to continue."
+								).format(frappe.utils.get_link_to_form("Asset", asset.name))
+							)
 
 					asset.flags.ignore_validate_update_after_submit = True
 					asset.flags.ignore_mandatory = True
@@ -670,7 +774,7 @@
 					asset.save()
 
 	def delete_linked_asset(self):
-		if self.doctype == 'Purchase Invoice' and not self.get('update_stock'):
+		if self.doctype == "Purchase Invoice" and not self.get("update_stock"):
 			return
 
 		frappe.db.sql("delete from `tabAsset Movement` where reference_name=%s", self.name)
@@ -681,37 +785,47 @@
 
 		if any(d.schedule_date for d in self.get("items")):
 			# Select earliest schedule_date.
-			self.schedule_date = min(d.schedule_date for d in self.get("items")
-							if d.schedule_date is not None)
+			self.schedule_date = min(
+				d.schedule_date for d in self.get("items") if d.schedule_date is not None
+			)
 
 		if self.schedule_date:
-			for d in self.get('items'):
+			for d in self.get("items"):
 				if not d.schedule_date:
 					d.schedule_date = self.schedule_date
 
-				if (d.schedule_date and self.transaction_date and
-					getdate(d.schedule_date) < getdate(self.transaction_date)):
+				if (
+					d.schedule_date
+					and self.transaction_date
+					and getdate(d.schedule_date) < getdate(self.transaction_date)
+				):
 					frappe.throw(_("Row #{0}: Reqd by Date cannot be before Transaction Date").format(d.idx))
 		else:
 			frappe.throw(_("Please enter Reqd by Date"))
 
 	def validate_items(self):
 		# validate items to see if they have is_purchase_item or is_subcontracted_item enabled
-		if self.doctype=="Material Request": return
+		if self.doctype == "Material Request":
+			return
 
-		if hasattr(self, "is_subcontracted") and self.is_subcontracted == 'Yes':
+		if hasattr(self, "is_subcontracted") and self.is_subcontracted:
 			validate_item_type(self, "is_sub_contracted_item", "subcontracted")
 		else:
 			validate_item_type(self, "is_purchase_item", "purchase")
 
+
 def get_asset_item_details(asset_items):
 	asset_items_data = {}
-	for d in frappe.get_all('Item', fields = ["name", "auto_create_assets", "asset_naming_series", "is_grouped_asset"],
-		filters = {'name': ('in', asset_items)}):
+	for d in frappe.get_all(
+		"Item",
+		fields=["name", "auto_create_assets", "asset_naming_series", "is_grouped_asset"],
+		filters={"name": ("in", asset_items)},
+	):
 		asset_items_data.setdefault(d.name, d)
 
 	return asset_items_data
 
+
 def validate_item_type(doc, fieldname, message):
 	# iterate through items and check if they are valid sales or purchase items
 	items = [d.item_code for d in doc.items if d.item_code]
@@ -722,16 +836,28 @@
 
 	item_list = ", ".join(["%s" % frappe.db.escape(d) for d in items])
 
-	invalid_items = [d[0] for d in frappe.db.sql("""
+	invalid_items = [
+		d[0]
+		for d in frappe.db.sql(
+			"""
 		select item_code from tabItem where name in ({0}) and {1}=0
-		""".format(item_list, fieldname), as_list=True)]
+		""".format(
+				item_list, fieldname
+			),
+			as_list=True,
+		)
+	]
 
 	if invalid_items:
 		items = ", ".join([d for d in invalid_items])
 
 		if len(invalid_items) > 1:
-			error_message = _("Following items {0} are not marked as {1} item. You can enable them as {1} item from its Item master").format(items, message)
+			error_message = _(
+				"Following items {0} are not marked as {1} item. You can enable them as {1} item from its Item master"
+			).format(items, message)
 		else:
-			error_message = _("Following item {0} is not marked as {1} item. You can enable them as {1} item from its Item master").format(items, message)
+			error_message = _(
+				"Following item {0} is not marked as {1} item. You can enable them as {1} item from its Item master"
+			).format(items, message)
 
 		frappe.throw(error_message)
diff --git a/erpnext/controllers/employee_boarding_controller.py b/erpnext/controllers/employee_boarding_controller.py
index dd02ce1..8ff397d 100644
--- a/erpnext/controllers/employee_boarding_controller.py
+++ b/erpnext/controllers/employee_boarding_controller.py
@@ -12,34 +12,39 @@
 
 
 class EmployeeBoardingController(Document):
-	'''
-		Create the project and the task for the boarding process
-		Assign to the concerned person and roles as per the onboarding/separation template
-	'''
+	"""
+	Create the project and the task for the boarding process
+	Assign to the concerned person and roles as per the onboarding/separation template
+	"""
+
 	def validate(self):
 		# remove the task if linked before submitting the form
 		if self.amended_from:
 			for activity in self.activities:
-				activity.task = ''
+				activity.task = ""
 
 	def on_submit(self):
 		# create the project for the given employee onboarding
-		project_name = _(self.doctype) + ' : '
-		if self.doctype == 'Employee Onboarding':
+		project_name = _(self.doctype) + " : "
+		if self.doctype == "Employee Onboarding":
 			project_name += self.job_applicant
 		else:
 			project_name += self.employee
 
-		project = frappe.get_doc({
-				'doctype': 'Project',
-				'project_name': project_name,
-				'expected_start_date': self.date_of_joining if self.doctype == 'Employee Onboarding' else self.resignation_letter_date,
-				'department': self.department,
-				'company': self.company
-			}).insert(ignore_permissions=True, ignore_mandatory=True)
+		project = frappe.get_doc(
+			{
+				"doctype": "Project",
+				"project_name": project_name,
+				"expected_start_date": self.date_of_joining
+				if self.doctype == "Employee Onboarding"
+				else self.resignation_letter_date,
+				"department": self.department,
+				"company": self.company,
+			}
+		).insert(ignore_permissions=True, ignore_mandatory=True)
 
-		self.db_set('project', project.name)
-		self.db_set('boarding_status', 'Pending')
+		self.db_set("project", project.name)
+		self.db_set("boarding_status", "Pending")
 		self.reload()
 		self.create_task_and_notify_user()
 
@@ -53,22 +58,25 @@
 
 			dates = self.get_task_dates(activity, holiday_list)
 
-			task = frappe.get_doc({
-				'doctype': 'Task',
-				'project': self.project,
-				'subject': activity.activity_name + ' : ' + self.employee_name,
-				'description': activity.description,
-				'department': self.department,
-				'company': self.company,
-				'task_weight': activity.task_weight,
-				'exp_start_date': dates[0],
-				'exp_end_date': dates[1]
-			}).insert(ignore_permissions=True)
-			activity.db_set('task', task.name)
+			task = frappe.get_doc(
+				{
+					"doctype": "Task",
+					"project": self.project,
+					"subject": activity.activity_name + " : " + self.employee_name,
+					"description": activity.description,
+					"department": self.department,
+					"company": self.company,
+					"task_weight": activity.task_weight,
+					"exp_start_date": dates[0],
+					"exp_end_date": dates[1],
+				}
+			).insert(ignore_permissions=True)
+			activity.db_set("task", task.name)
 
 			users = [activity.user] if activity.user else []
 			if activity.role:
-				user_list = frappe.db.sql_list('''
+				user_list = frappe.db.sql_list(
+					"""
 					SELECT
 						DISTINCT(has_role.parent)
 					FROM
@@ -79,25 +87,27 @@
 						has_role.parenttype = 'User'
 							AND user.enabled = 1
 							AND has_role.role = %s
-				''', activity.role)
+				""",
+					activity.role,
+				)
 				users = unique(users + user_list)
 
-				if 'Administrator' in users:
-					users.remove('Administrator')
+				if "Administrator" in users:
+					users.remove("Administrator")
 
 			# assign the task the users
 			if users:
 				self.assign_task_to_users(task, users)
 
 	def get_holiday_list(self):
-		if self.doctype == 'Employee Separation':
+		if self.doctype == "Employee Separation":
 			return get_holiday_list_for_employee(self.employee)
 		else:
 			if self.employee:
 				return get_holiday_list_for_employee(self.employee)
 			else:
 				if not self.holiday_list:
-					frappe.throw(_('Please set the Holiday List.'), frappe.MandatoryError)
+					frappe.throw(_("Please set the Holiday List."), frappe.MandatoryError)
 				else:
 					return self.holiday_list
 
@@ -122,51 +132,62 @@
 	def assign_task_to_users(self, task, users):
 		for user in users:
 			args = {
-				'assign_to': [user],
-				'doctype': task.doctype,
-				'name': task.name,
-				'description': task.description or task.subject,
-				'notify': self.notify_users_by_email
+				"assign_to": [user],
+				"doctype": task.doctype,
+				"name": task.name,
+				"description": task.description or task.subject,
+				"notify": self.notify_users_by_email,
 			}
 			assign_to.add(args)
 
 	def on_cancel(self):
 		# delete task project
 		project = self.project
-		for task in frappe.get_all('Task', filters={'project': project}):
-			frappe.delete_doc('Task', task.name, force=1)
-		frappe.delete_doc('Project', project, force=1)
-		self.db_set('project', '')
+		for task in frappe.get_all("Task", filters={"project": project}):
+			frappe.delete_doc("Task", task.name, force=1)
+		frappe.delete_doc("Project", project, force=1)
+		self.db_set("project", "")
 		for activity in self.activities:
-			activity.db_set('task', '')
+			activity.db_set("task", "")
 
-		frappe.msgprint(_('Linked Project {} and Tasks deleted.').format(
-			project), alert=True, indicator='blue')
+		frappe.msgprint(
+			_("Linked Project {} and Tasks deleted.").format(project), alert=True, indicator="blue"
+		)
 
 
 @frappe.whitelist()
 def get_onboarding_details(parent, parenttype):
-	return frappe.get_all('Employee Boarding Activity',
-		fields=['activity_name', 'role', 'user', 'required_for_employee_creation',
-			'description', 'task_weight', 'begin_on', 'duration'],
-		filters={'parent': parent, 'parenttype': parenttype},
-		order_by= 'idx')
+	return frappe.get_all(
+		"Employee Boarding Activity",
+		fields=[
+			"activity_name",
+			"role",
+			"user",
+			"required_for_employee_creation",
+			"description",
+			"task_weight",
+			"begin_on",
+			"duration",
+		],
+		filters={"parent": parent, "parenttype": parenttype},
+		order_by="idx",
+	)
 
 
 def update_employee_boarding_status(project):
-	employee_onboarding = frappe.db.exists('Employee Onboarding', {'project': project.name})
-	employee_separation = frappe.db.exists('Employee Separation', {'project': project.name})
+	employee_onboarding = frappe.db.exists("Employee Onboarding", {"project": project.name})
+	employee_separation = frappe.db.exists("Employee Separation", {"project": project.name})
 
 	if not (employee_onboarding or employee_separation):
 		return
 
-	status = 'Pending'
+	status = "Pending"
 	if flt(project.percent_complete) > 0.0 and flt(project.percent_complete) < 100.0:
-		status = 'In Process'
+		status = "In Process"
 	elif flt(project.percent_complete) == 100.0:
-		status = 'Completed'
+		status = "Completed"
 
 	if employee_onboarding:
-		frappe.db.set_value('Employee Onboarding', employee_onboarding, 'boarding_status', status)
+		frappe.db.set_value("Employee Onboarding", employee_onboarding, "boarding_status", status)
 	elif employee_separation:
-		frappe.db.set_value('Employee Separation', employee_separation, 'boarding_status', status)
+		frappe.db.set_value("Employee Separation", employee_separation, "boarding_status", status)
diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py
index 68ad702..e68ee90 100644
--- a/erpnext/controllers/item_variant.py
+++ b/erpnext/controllers/item_variant.py
@@ -10,24 +10,30 @@
 from frappe.utils import cstr, flt
 
 
-class ItemVariantExistsError(frappe.ValidationError): pass
-class InvalidItemAttributeValueError(frappe.ValidationError): pass
-class ItemTemplateCannotHaveStock(frappe.ValidationError): pass
+class ItemVariantExistsError(frappe.ValidationError):
+	pass
+
+
+class InvalidItemAttributeValueError(frappe.ValidationError):
+	pass
+
+
+class ItemTemplateCannotHaveStock(frappe.ValidationError):
+	pass
+
 
 @frappe.whitelist()
-def get_variant(template, args=None, variant=None, manufacturer=None,
-	manufacturer_part_no=None):
+def get_variant(template, args=None, variant=None, manufacturer=None, manufacturer_part_no=None):
 	"""Validates Attributes and their Values, then looks for an exactly
-		matching Item Variant
+	matching Item Variant
 
-		:param item: Template Item
-		:param args: A dictionary with "Attribute" as key and "Attribute Value" as value
+	:param item: Template Item
+	:param args: A dictionary with "Attribute" as key and "Attribute Value" as value
 	"""
-	item_template = frappe.get_doc('Item', template)
+	item_template = frappe.get_doc("Item", template)
 
-	if item_template.variant_based_on=='Manufacturer' and manufacturer:
-		return make_variant_based_on_manufacturer(item_template, manufacturer,
-			manufacturer_part_no)
+	if item_template.variant_based_on == "Manufacturer" and manufacturer:
+		return make_variant_based_on_manufacturer(item_template, manufacturer, manufacturer_part_no)
 	else:
 		if isinstance(args, str):
 			args = json.loads(args)
@@ -36,28 +42,30 @@
 			frappe.throw(_("Please specify at least one attribute in the Attributes table"))
 		return find_variant(template, args, variant)
 
+
 def make_variant_based_on_manufacturer(template, manufacturer, manufacturer_part_no):
-	'''Make and return a new variant based on manufacturer and
-		manufacturer part no'''
+	"""Make and return a new variant based on manufacturer and
+	manufacturer part no"""
 	from frappe.model.naming import append_number_if_name_exists
 
-	variant = frappe.new_doc('Item')
+	variant = frappe.new_doc("Item")
 
 	copy_attributes_to_variant(template, variant)
 
 	variant.manufacturer = manufacturer
 	variant.manufacturer_part_no = manufacturer_part_no
 
-	variant.item_code = append_number_if_name_exists('Item', template.name)
+	variant.item_code = append_number_if_name_exists("Item", template.name)
 
 	return variant
 
+
 def validate_item_variant_attributes(item, args=None):
 	if isinstance(item, str):
-		item = frappe.get_doc('Item', item)
+		item = frappe.get_doc("Item", item)
 
 	if not args:
-		args = {d.attribute.lower():d.attribute_value for d in item.attributes}
+		args = {d.attribute.lower(): d.attribute_value for d in item.attributes}
 
 	attribute_values, numeric_values = get_attribute_values(item)
 
@@ -73,6 +81,7 @@
 			attributes_list = attribute_values.get(attribute.lower(), [])
 			validate_item_attribute_value(attributes_list, attribute, value, item.name, from_variant=True)
 
+
 def validate_is_incremental(numeric_attribute, attribute, value, item):
 	from_range = numeric_attribute.from_range
 	to_range = numeric_attribute.to_range
@@ -84,30 +93,48 @@
 
 	is_in_range = from_range <= flt(value) <= to_range
 	precision = max(len(cstr(v).split(".")[-1].rstrip("0")) for v in (value, increment))
-	#avoid precision error by rounding the remainder
+	# avoid precision error by rounding the remainder
 	remainder = flt((flt(value) - from_range) % increment, precision)
 
-	is_incremental = remainder==0 or remainder==increment
+	is_incremental = remainder == 0 or remainder == increment
 
 	if not (is_in_range and is_incremental):
-		frappe.throw(_("Value for Attribute {0} must be within the range of {1} to {2} in the increments of {3} for Item {4}")\
-			.format(attribute, from_range, to_range, increment, item),
-			InvalidItemAttributeValueError, title=_('Invalid Attribute'))
+		frappe.throw(
+			_(
+				"Value for Attribute {0} must be within the range of {1} to {2} in the increments of {3} for Item {4}"
+			).format(attribute, from_range, to_range, increment, item),
+			InvalidItemAttributeValueError,
+			title=_("Invalid Attribute"),
+		)
 
-def validate_item_attribute_value(attributes_list, attribute, attribute_value, item, from_variant=True):
-	allow_rename_attribute_value = frappe.db.get_single_value('Item Variant Settings', 'allow_rename_attribute_value')
+
+def validate_item_attribute_value(
+	attributes_list, attribute, attribute_value, item, from_variant=True
+):
+	allow_rename_attribute_value = frappe.db.get_single_value(
+		"Item Variant Settings", "allow_rename_attribute_value"
+	)
 	if allow_rename_attribute_value:
 		pass
 	elif attribute_value not in attributes_list:
 		if from_variant:
-			frappe.throw(_("{0} is not a valid Value for Attribute {1} of Item {2}.").format(
-				frappe.bold(attribute_value), frappe.bold(attribute), frappe.bold(item)), InvalidItemAttributeValueError, title=_("Invalid Value"))
+			frappe.throw(
+				_("{0} is not a valid Value for Attribute {1} of Item {2}.").format(
+					frappe.bold(attribute_value), frappe.bold(attribute), frappe.bold(item)
+				),
+				InvalidItemAttributeValueError,
+				title=_("Invalid Value"),
+			)
 		else:
 			msg = _("The value {0} is already assigned to an existing Item {1}.").format(
-				frappe.bold(attribute_value), frappe.bold(item))
-			msg += "<br>" + _("To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings.").format(frappe.bold("Allow Rename Attribute Value"))
+				frappe.bold(attribute_value), frappe.bold(item)
+			)
+			msg += "<br>" + _(
+				"To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings."
+			).format(frappe.bold("Allow Rename Attribute Value"))
 
-			frappe.throw(msg, InvalidItemAttributeValueError, title=_('Edit Not Allowed'))
+			frappe.throw(msg, InvalidItemAttributeValueError, title=_("Edit Not Allowed"))
+
 
 def get_attribute_values(item):
 	if not frappe.flags.attribute_values:
@@ -116,9 +143,11 @@
 		for t in frappe.get_all("Item Attribute Value", fields=["parent", "attribute_value"]):
 			attribute_values.setdefault(t.parent.lower(), []).append(t.attribute_value)
 
-		for t in frappe.get_all('Item Variant Attribute',
+		for t in frappe.get_all(
+			"Item Variant Attribute",
 			fields=["attribute", "from_range", "to_range", "increment"],
-			filters={'numeric_values': 1, 'parent': item.variant_of}):
+			filters={"numeric_values": 1, "parent": item.variant_of},
+		):
 			numeric_values[t.attribute.lower()] = t
 
 		frappe.flags.attribute_values = attribute_values
@@ -126,14 +155,22 @@
 
 	return frappe.flags.attribute_values, frappe.flags.numeric_values
 
+
 def find_variant(template, args, variant_item_code=None):
-	conditions = ["""(iv_attribute.attribute={0} and iv_attribute.attribute_value={1})"""\
-		.format(frappe.db.escape(key), frappe.db.escape(cstr(value))) for key, value in args.items()]
+	conditions = [
+		"""(iv_attribute.attribute={0} and iv_attribute.attribute_value={1})""".format(
+			frappe.db.escape(key), frappe.db.escape(cstr(value))
+		)
+		for key, value in args.items()
+	]
 
 	conditions = " or ".join(conditions)
 
 	from erpnext.e_commerce.variant_selector.utils import get_item_codes_by_attributes
-	possible_variants = [i for i in get_item_codes_by_attributes(args, template) if i != variant_item_code]
+
+	possible_variants = [
+		i for i in get_item_codes_by_attributes(args, template) if i != variant_item_code
+	]
 
 	for variant in possible_variants:
 		variant = frappe.get_doc("Item", variant)
@@ -145,7 +182,7 @@
 
 			for attribute, value in args.items():
 				for row in variant.attributes:
-					if row.attribute==attribute and row.attribute_value== cstr(value):
+					if row.attribute == attribute and row.attribute_value == cstr(value):
 						# this row matches
 						match_count += 1
 						break
@@ -153,6 +190,7 @@
 			if match_count == len(args.keys()):
 				return variant.name
 
+
 @frappe.whitelist()
 def create_variant(item, args):
 	if isinstance(args, str):
@@ -160,14 +198,11 @@
 
 	template = frappe.get_doc("Item", item)
 	variant = frappe.new_doc("Item")
-	variant.variant_based_on = 'Item Attribute'
+	variant.variant_based_on = "Item Attribute"
 	variant_attributes = []
 
 	for d in template.attributes:
-		variant_attributes.append({
-			"attribute": d.attribute,
-			"attribute_value": args.get(d.attribute)
-		})
+		variant_attributes.append({"attribute": d.attribute, "attribute_value": args.get(d.attribute)})
 
 	variant.set("attributes", variant_attributes)
 	copy_attributes_to_variant(template, variant)
@@ -175,6 +210,7 @@
 
 	return variant
 
+
 @frappe.whitelist()
 def enqueue_multiple_variant_creation(item, args):
 	# There can be innumerable attribute combinations, enqueue
@@ -189,9 +225,14 @@
 	if total_variants < 10:
 		return create_multiple_variants(item, args)
 	else:
-		frappe.enqueue("erpnext.controllers.item_variant.create_multiple_variants",
-			item=item, args=args, now=frappe.flags.in_test);
-		return 'queued'
+		frappe.enqueue(
+			"erpnext.controllers.item_variant.create_multiple_variants",
+			item=item,
+			args=args,
+			now=frappe.flags.in_test,
+		)
+		return "queued"
+
 
 def create_multiple_variants(item, args):
 	count = 0
@@ -204,26 +245,27 @@
 		if not get_variant(item, args=attribute_values):
 			variant = create_variant(item, attribute_values)
 			variant.save()
-			count +=1
+			count += 1
 
 	return count
 
+
 def generate_keyed_value_combinations(args):
 	"""
 	From this:
 
-		args = {"attr1": ["a", "b", "c"], "attr2": ["1", "2"], "attr3": ["A"]}
+	        args = {"attr1": ["a", "b", "c"], "attr2": ["1", "2"], "attr3": ["A"]}
 
 	To this:
 
-		[
-			{u'attr1': u'a', u'attr2': u'1', u'attr3': u'A'},
-			{u'attr1': u'b', u'attr2': u'1', u'attr3': u'A'},
-			{u'attr1': u'c', u'attr2': u'1', u'attr3': u'A'},
-			{u'attr1': u'a', u'attr2': u'2', u'attr3': u'A'},
-			{u'attr1': u'b', u'attr2': u'2', u'attr3': u'A'},
-			{u'attr1': u'c', u'attr2': u'2', u'attr3': u'A'}
-		]
+	        [
+	                {u'attr1': u'a', u'attr2': u'1', u'attr3': u'A'},
+	                {u'attr1': u'b', u'attr2': u'1', u'attr3': u'A'},
+	                {u'attr1': u'c', u'attr2': u'1', u'attr3': u'A'},
+	                {u'attr1': u'a', u'attr2': u'2', u'attr3': u'A'},
+	                {u'attr1': u'b', u'attr2': u'2', u'attr3': u'A'},
+	                {u'attr1': u'c', u'attr2': u'2', u'attr3': u'A'}
+	        ]
 
 	"""
 	# Return empty list if empty
@@ -259,17 +301,25 @@
 
 	return results
 
+
 def copy_attributes_to_variant(item, variant):
 	# copy non no-copy fields
 
-	exclude_fields = ["naming_series", "item_code", "item_name", "published_in_website",
-		"opening_stock", "variant_of", "valuation_rate"]
+	exclude_fields = [
+		"naming_series",
+		"item_code",
+		"item_name",
+		"published_in_website",
+		"opening_stock",
+		"variant_of",
+		"valuation_rate",
+	]
 
-	if item.variant_based_on=='Manufacturer':
+	if item.variant_based_on == "Manufacturer":
 		# don't copy manufacturer values if based on part no
-		exclude_fields += ['manufacturer', 'manufacturer_part_no']
+		exclude_fields += ["manufacturer", "manufacturer_part_no"]
 
-	allow_fields = [d.field_name for d in frappe.get_all("Variant Field", fields = ['field_name'])]
+	allow_fields = [d.field_name for d in frappe.get_all("Variant Field", fields=["field_name"])]
 	if "variant_based_on" not in allow_fields:
 		allow_fields.append("variant_based_on")
 	for field in item.meta.fields:
@@ -288,11 +338,11 @@
 
 	variant.variant_of = item.name
 
-	if 'description' not in allow_fields:
+	if "description" not in allow_fields:
 		if not variant.description:
-				variant.description = ""
+			variant.description = ""
 	else:
-		if item.variant_based_on=='Item Attribute':
+		if item.variant_based_on == "Item Attribute":
 			if variant.attributes:
 				attributes_description = item.description + " "
 				for d in variant.attributes:
@@ -301,6 +351,7 @@
 				if attributes_description not in variant.description:
 					variant.description = attributes_description
 
+
 def make_variant_item_code(template_item_code, template_item_name, variant):
 	"""Uses template's item code and abbreviations to make variant's item code"""
 	if variant.item_code:
@@ -308,13 +359,14 @@
 
 	abbreviations = []
 	for attr in variant.attributes:
-		item_attribute = frappe.db.sql("""select i.numeric_values, v.abbr
+		item_attribute = frappe.db.sql(
+			"""select i.numeric_values, v.abbr
 			from `tabItem Attribute` i left join `tabItem Attribute Value` v
 				on (i.name=v.parent)
-			where i.name=%(attribute)s and (v.attribute_value=%(attribute_value)s or i.numeric_values = 1)""", {
-				"attribute": attr.attribute,
-				"attribute_value": attr.attribute_value
-			}, as_dict=True)
+			where i.name=%(attribute)s and (v.attribute_value=%(attribute_value)s or i.numeric_values = 1)""",
+			{"attribute": attr.attribute, "attribute_value": attr.attribute_value},
+			as_dict=True,
+		)
 
 		if not item_attribute:
 			continue
@@ -322,13 +374,16 @@
 			# 	frappe.bold(attr.attribute_value)), title=_('Invalid Attribute'),
 			# 	exc=InvalidItemAttributeValueError)
 
-		abbr_or_value = cstr(attr.attribute_value) if item_attribute[0].numeric_values else item_attribute[0].abbr
+		abbr_or_value = (
+			cstr(attr.attribute_value) if item_attribute[0].numeric_values else item_attribute[0].abbr
+		)
 		abbreviations.append(abbr_or_value)
 
 	if abbreviations:
 		variant.item_code = "{0}-{1}".format(template_item_code, "-".join(abbreviations))
 		variant.item_name = "{0}-{1}".format(template_item_name, "-".join(abbreviations))
 
+
 @frappe.whitelist()
 def create_variant_doc_for_quick_entry(template, args):
 	variant_based_on = frappe.db.get_value("Item", template, "variant_based_on")
diff --git a/erpnext/controllers/print_settings.py b/erpnext/controllers/print_settings.py
index cf9de52..d2c8096 100644
--- a/erpnext/controllers/print_settings.py
+++ b/erpnext/controllers/print_settings.py
@@ -2,7 +2,6 @@
 # License: GNU General Public License v3. See license.txt
 
 
-
 def set_print_templates_for_item_table(doc, settings):
 	doc.print_templates = {
 		"items": "templates/print_formats/includes/items.html",
@@ -20,16 +19,21 @@
 	doc.flags.compact_item_fields = ["description", "qty", "rate", "amount"]
 
 	if settings.compact_item_print:
-		doc.child_print_templates["items"]["description"] =\
-			"templates/print_formats/includes/item_table_description.html"
+		doc.child_print_templates["items"][
+			"description"
+		] = "templates/print_formats/includes/item_table_description.html"
 		doc.flags.format_columns = format_columns
 
+
 def set_print_templates_for_taxes(doc, settings):
 	doc.flags.show_inclusive_tax_in_print = doc.is_inclusive_tax()
-	doc.print_templates.update({
-		"total": "templates/print_formats/includes/total.html",
-		"taxes": "templates/print_formats/includes/taxes.html"
-	})
+	doc.print_templates.update(
+		{
+			"total": "templates/print_formats/includes/total.html",
+			"taxes": "templates/print_formats/includes/taxes.html",
+		}
+	)
+
 
 def format_columns(display_columns, compact_fields):
 	compact_fields = compact_fields + ["image", "item_code", "item_name"]
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index dd9b45c..243ebb6 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -21,28 +21,28 @@
 	conditions = []
 	fields = get_fields("Employee", ["name", "employee_name"])
 
-	return frappe.db.sql("""select {fields} from `tabEmployee`
+	return frappe.db.sql(
+		"""select {fields} from `tabEmployee`
 		where status in ('Active', 'Suspended')
 			and docstatus < 2
 			and ({key} like %(txt)s
 				or employee_name like %(txt)s)
 			{fcond} {mcond}
 		order by
-			if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
-			if(locate(%(_txt)s, employee_name), locate(%(_txt)s, employee_name), 99999),
+			(case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end),
+			(case when locate(%(_txt)s, employee_name) > 0 then locate(%(_txt)s, employee_name) else 99999 end),
 			idx desc,
 			name, employee_name
-		limit %(start)s, %(page_len)s""".format(**{
-			'fields': ", ".join(fields),
-			'key': searchfield,
-			'fcond': get_filters_cond(doctype, filters, conditions),
-			'mcond': get_match_cond(doctype)
-		}), {
-			'txt': "%%%s%%" % txt,
-			'_txt': txt.replace("%", ""),
-			'start': start,
-			'page_len': page_len
-		})
+		limit %(page_len)s offset %(start)s""".format(
+			**{
+				"fields": ", ".join(fields),
+				"key": searchfield,
+				"fcond": get_filters_cond(doctype, filters, conditions),
+				"mcond": get_match_cond(doctype),
+			}
+		),
+		{"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len},
+	)
 
 
 # searches for leads which are not converted
@@ -51,7 +51,8 @@
 def lead_query(doctype, txt, searchfield, start, page_len, filters):
 	fields = get_fields("Lead", ["name", "lead_name", "company_name"])
 
-	return frappe.db.sql("""select {fields} from `tabLead`
+	return frappe.db.sql(
+		"""select {fields} from `tabLead`
 		where docstatus < 2
 			and ifnull(status, '') != 'Converted'
 			and ({key} like %(txt)s
@@ -59,24 +60,20 @@
 				or company_name like %(txt)s)
 			{mcond}
 		order by
-			if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
-			if(locate(%(_txt)s, lead_name), locate(%(_txt)s, lead_name), 99999),
-			if(locate(%(_txt)s, company_name), locate(%(_txt)s, company_name), 99999),
+			(case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end),
+			(case when locate(%(_txt)s, lead_name) > 0 then locate(%(_txt)s, lead_name) else 99999 end),
+			(case when locate(%(_txt)s, company_name) > 0 then locate(%(_txt)s, company_name) else 99999 end),
 			idx desc,
 			name, lead_name
-		limit %(start)s, %(page_len)s""".format(**{
-			'fields': ", ".join(fields),
-			'key': searchfield,
-			'mcond':get_match_cond(doctype)
-		}), {
-			'txt': "%%%s%%" % txt,
-			'_txt': txt.replace("%", ""),
-			'start': start,
-			'page_len': page_len
-		})
+		limit %(page_len)s offset %(start)s""".format(
+			**{"fields": ", ".join(fields), "key": searchfield, "mcond": get_match_cond(doctype)}
+		),
+		{"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len},
+	)
+
+	# searches for customer
 
 
- # searches for customer
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def customer_query(doctype, txt, searchfield, start, page_len, filters):
@@ -93,26 +90,26 @@
 	searchfields = frappe.get_meta("Customer").get_search_fields()
 	searchfields = " or ".join(field + " like %(txt)s" for field in searchfields)
 
-	return frappe.db.sql("""select {fields} from `tabCustomer`
+	return frappe.db.sql(
+		"""select {fields} from `tabCustomer`
 		where docstatus < 2
 			and ({scond}) and disabled=0
 			{fcond} {mcond}
 		order by
-			if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
-			if(locate(%(_txt)s, customer_name), locate(%(_txt)s, customer_name), 99999),
+			(case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end),
+			(case when locate(%(_txt)s, customer_name) > 0 then locate(%(_txt)s, customer_name) else 99999 end),
 			idx desc,
 			name, customer_name
-		limit %(start)s, %(page_len)s""".format(**{
-			"fields": ", ".join(fields),
-			"scond": searchfields,
-			"mcond": get_match_cond(doctype),
-			"fcond": get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
-		}), {
-			'txt': "%%%s%%" % txt,
-			'_txt': txt.replace("%", ""),
-			'start': start,
-			'page_len': page_len
-		})
+		limit %(page_len)s offset %(start)s""".format(
+			**{
+				"fields": ", ".join(fields),
+				"scond": searchfields,
+				"mcond": get_match_cond(doctype),
+				"fcond": get_filters_cond(doctype, filters, conditions).replace("%", "%%"),
+			}
+		),
+		{"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len},
+	)
 
 
 # searches for supplier
@@ -128,64 +125,63 @@
 
 	fields = get_fields("Supplier", fields)
 
-	return frappe.db.sql("""select {field} from `tabSupplier`
+	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
-			and (on_hold = 0 or (on_hold = 1 and CURDATE() > release_date))
+			and (on_hold = 0 or (on_hold = 1 and CURRENT_DATE > release_date))
 			{mcond}
 		order by
-			if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
-			if(locate(%(_txt)s, supplier_name), locate(%(_txt)s, supplier_name), 99999),
+			(case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end),
+			(case when locate(%(_txt)s, supplier_name) > 0 then locate(%(_txt)s, supplier_name) else 99999 end),
 			idx desc,
 			name, supplier_name
-		limit %(start)s, %(page_len)s """.format(**{
-			'field': ', '.join(fields),
-			'key': searchfield,
-			'mcond':get_match_cond(doctype)
-		}), {
-			'txt': "%%%s%%" % txt,
-			'_txt': txt.replace("%", ""),
-			'start': start,
-			'page_len': page_len
-		})
+		limit %(page_len)s offset %(start)s""".format(
+			**{"field": ", ".join(fields), "key": searchfield, "mcond": get_match_cond(doctype)}
+		),
+		{"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len},
+	)
 
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
-	company_currency = erpnext.get_company_currency(filters.get('company'))
+	company_currency = erpnext.get_company_currency(filters.get("company"))
 
 	def get_accounts(with_account_type_filter):
-		account_type_condition = ''
+		account_type_condition = ""
 		if with_account_type_filter:
 			account_type_condition = "AND account_type in %(account_types)s"
 
-		accounts = frappe.db.sql("""
+		accounts = frappe.db.sql(
+			"""
 			SELECT name, parent_account
 			FROM `tabAccount`
 			WHERE `tabAccount`.docstatus!=2
 				{account_type_condition}
 				AND is_group = 0
 				AND company = %(company)s
-				AND account_currency = %(currency)s
+				AND disabled = %(disabled)s
+				AND (account_currency = %(currency)s or ifnull(account_currency, '') = '')
 				AND `{searchfield}` LIKE %(txt)s
 				{mcond}
 			ORDER BY idx DESC, name
-			LIMIT %(offset)s, %(limit)s
+			LIMIT %(limit)s offset %(offset)s
 		""".format(
 				account_type_condition=account_type_condition,
 				searchfield=searchfield,
-				mcond=get_match_cond(doctype)
+				mcond=get_match_cond(doctype),
 			),
 			dict(
 				account_types=filters.get("account_type"),
 				company=filters.get("company"),
+				disabled=filters.get("disabled", 0),
 				currency=company_currency,
 				txt="%{}%".format(txt),
 				offset=start,
-				limit=page_len
-			)
+				limit=page_len,
+			),
 		)
 
 		return accounts
@@ -206,7 +202,7 @@
 	if isinstance(filters, str):
 		filters = json.loads(filters)
 
-	#Get searchfields from meta and use in Item Link field query
+	# Get searchfields from meta and use in Item Link field query
 	meta = frappe.get_meta("Item", cached=True)
 	searchfields = meta.get_search_fields()
 
@@ -216,49 +212,56 @@
 		if ignored_field in searchfields:
 			searchfields.remove(ignored_field)
 
-	columns = ''
-	extra_searchfields = [field for field in searchfields
-		if not field in ["name", "item_group", "description", "item_name"]]
+	columns = ""
+	extra_searchfields = [
+		field
+		for field in searchfields
+		if not field in ["name", "item_group", "description", "item_name"]
+	]
 
 	if extra_searchfields:
 		columns = ", " + ", ".join(extra_searchfields)
 
-	searchfields = searchfields + [field for field in[searchfield or "name", "item_code", "item_group", "item_name"]
-		if not field in searchfields]
+	searchfields = searchfields + [
+		field
+		for field in [searchfield or "name", "item_code", "item_group", "item_name"]
+		if not field in searchfields
+	]
 	searchfields = " or ".join([field + " like %(txt)s" for field in searchfields])
 
 	if filters and isinstance(filters, dict):
-		if filters.get('customer') or filters.get('supplier'):
-			party = filters.get('customer') or filters.get('supplier')
-			item_rules_list = frappe.get_all('Party Specific Item',
-				filters = {'party': party}, fields = ['restrict_based_on', 'based_on_value'])
+		if filters.get("customer") or filters.get("supplier"):
+			party = filters.get("customer") or filters.get("supplier")
+			item_rules_list = frappe.get_all(
+				"Party Specific Item", filters={"party": party}, fields=["restrict_based_on", "based_on_value"]
+			)
 
 			filters_dict = {}
 			for rule in item_rules_list:
-				if rule['restrict_based_on'] == 'Item':
-					rule['restrict_based_on'] = 'name'
+				if rule["restrict_based_on"] == "Item":
+					rule["restrict_based_on"] = "name"
 				filters_dict[rule.restrict_based_on] = []
 
 			for rule in item_rules_list:
 				filters_dict[rule.restrict_based_on].append(rule.based_on_value)
 
 			for filter in filters_dict:
-				filters[scrub(filter)] = ['in', filters_dict[filter]]
+				filters[scrub(filter)] = ["in", filters_dict[filter]]
 
-			if filters.get('customer'):
-				del filters['customer']
+			if filters.get("customer"):
+				del filters["customer"]
 			else:
-				del filters['supplier']
+				del filters["supplier"]
 		else:
-			filters.pop('customer', None)
-			filters.pop('supplier', None)
+			filters.pop("customer", None)
+			filters.pop("supplier", None)
 
-
-	description_cond = ''
-	if frappe.db.count('Item', cache=True) < 50000:
+	description_cond = ""
+	if frappe.db.count("Item", cache=True) < 50000:
 		# scan description only if items are less than 50000
-		description_cond = 'or tabItem.description LIKE %(txt)s'
-	return frappe.db.sql("""select
+		description_cond = "or tabItem.description LIKE %(txt)s"
+	return frappe.db.sql(
+		"""select
 			tabItem.name, tabItem.item_name, tabItem.item_group,
 		if(length(tabItem.description) > 40, \
 			concat(substr(tabItem.description, 1, 40), "..."), description) as description
@@ -279,16 +282,19 @@
 		limit %(start)s, %(page_len)s """.format(
 			columns=columns,
 			scond=searchfields,
-			fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
-			mcond=get_match_cond(doctype).replace('%', '%%'),
-			description_cond = description_cond),
-			{
-				"today": nowdate(),
-				"txt": "%%%s%%" % txt,
-				"_txt": txt.replace("%", ""),
-				"start": start,
-				"page_len": page_len
-			}, as_dict=as_dict)
+			fcond=get_filters_cond(doctype, filters, conditions).replace("%", "%%"),
+			mcond=get_match_cond(doctype).replace("%", "%%"),
+			description_cond=description_cond,
+		),
+		{
+			"today": nowdate(),
+			"txt": "%%%s%%" % txt,
+			"_txt": txt.replace("%", ""),
+			"start": start,
+			"page_len": page_len,
+		},
+		as_dict=as_dict,
+	)
 
 
 @frappe.whitelist()
@@ -297,58 +303,64 @@
 	conditions = []
 	fields = get_fields("BOM", ["name", "item"])
 
-	return frappe.db.sql("""select {fields}
-		from tabBOM
-		where tabBOM.docstatus=1
-			and tabBOM.is_active=1
-			and tabBOM.`{key}` like %(txt)s
+	return frappe.db.sql(
+		"""select {fields}
+		from `tabBOM`
+		where `tabBOM`.docstatus=1
+			and `tabBOM`.is_active=1
+			and `tabBOM`.`{key}` like %(txt)s
 			{fcond} {mcond}
 		order by
-			if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
+			(case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end),
 			idx desc, name
-		limit %(start)s, %(page_len)s """.format(
+		limit %(page_len)s offset %(start)s""".format(
 			fields=", ".join(fields),
-			fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
-			mcond=get_match_cond(doctype).replace('%', '%%'),
-			key=searchfield),
+			fcond=get_filters_cond(doctype, filters, conditions).replace("%", "%%"),
+			mcond=get_match_cond(doctype).replace("%", "%%"),
+			key=searchfield,
+		),
 		{
-			'txt': '%' + txt + '%',
-			'_txt': txt.replace("%", ""),
-			'start': start or 0,
-			'page_len': page_len or 20
-		})
+			"txt": "%" + txt + "%",
+			"_txt": txt.replace("%", ""),
+			"start": start or 0,
+			"page_len": page_len or 20,
+		},
+	)
 
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_project_name(doctype, txt, searchfield, start, page_len, filters):
-	cond = ''
-	if filters and filters.get('customer'):
+	cond = ""
+	if filters and filters.get("customer"):
 		cond = """(`tabProject`.customer = %s or
-			ifnull(`tabProject`.customer,"")="") and""" %(frappe.db.escape(filters.get("customer")))
+			ifnull(`tabProject`.customer,"")="") and""" % (
+			frappe.db.escape(filters.get("customer"))
+		)
 
 	fields = get_fields("Project", ["name", "project_name"])
 	searchfields = frappe.get_meta("Project").get_search_fields()
-	searchfields = " or ".join([field + " like %(txt)s" for field in searchfields])
+	searchfields = " or ".join(["`tabProject`." + field + " like %(txt)s" for field in searchfields])
 
-	return frappe.db.sql("""select {fields} from `tabProject`
+	return frappe.db.sql(
+		"""select {fields} from `tabProject`
 		where
-			`tabProject`.status not in ("Completed", "Cancelled")
+			`tabProject`.status not in ('Completed', 'Cancelled')
 			and {cond} {scond} {match_cond}
 		order by
-			if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
-			idx desc,
+			(case when locate(%(_txt)s, `tabProject`.name) > 0 then locate(%(_txt)s, `tabProject`.name) else 99999 end),
+			`tabProject`.idx desc,
 			`tabProject`.name asc
-		limit {start}, {page_len}""".format(
-			fields=", ".join(['`tabProject`.{0}'.format(f) for f in fields]),
+		limit {page_len} offset {start}""".format(
+			fields=", ".join(["`tabProject`.{0}".format(f) for f in fields]),
 			cond=cond,
 			scond=searchfields,
 			match_cond=get_match_cond(doctype),
 			start=start,
-			page_len=page_len), {
-				"txt": "%{0}%".format(txt),
-				"_txt": txt.replace('%', '')
-			})
+			page_len=page_len,
+		),
+		{"txt": "%{0}%".format(txt), "_txt": txt.replace("%", "")},
+	)
 
 
 @frappe.whitelist()
@@ -356,12 +368,13 @@
 def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, filters, as_dict):
 	fields = get_fields("Delivery Note", ["name", "customer", "posting_date"])
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select %(fields)s
 		from `tabDelivery Note`
 		where `tabDelivery Note`.`%(key)s` like %(txt)s and
 			`tabDelivery Note`.docstatus = 1
-			and status not in ("Stopped", "Closed") %(fcond)s
+			and status not in ('Stopped', 'Closed') %(fcond)s
 			and (
 				(`tabDelivery Note`.is_return = 0 and `tabDelivery Note`.per_billed < 100)
 				or (`tabDelivery Note`.grand_total = 0 and `tabDelivery Note`.per_billed < 100)
@@ -370,16 +383,20 @@
 					and return_against in (select name from `tabDelivery Note` where per_billed < 100)
 				)
 			)
-			%(mcond)s order by `tabDelivery Note`.`%(key)s` asc limit %(start)s, %(page_len)s
-	""" % {
-		"fields": ", ".join(["`tabDelivery Note`.{0}".format(f) for f in fields]),
-		"key": searchfield,
-		"fcond": get_filters_cond(doctype, filters, []),
-		"mcond": get_match_cond(doctype),
-		"start": start,
-		"page_len": page_len,
-		"txt": "%(txt)s"
-	}, {"txt": ("%%%s%%" % txt)}, as_dict=as_dict)
+			%(mcond)s order by `tabDelivery Note`.`%(key)s` asc limit %(page_len)s offset %(start)s
+	"""
+		% {
+			"fields": ", ".join(["`tabDelivery Note`.{0}".format(f) for f in fields]),
+			"key": searchfield,
+			"fcond": get_filters_cond(doctype, filters, []),
+			"mcond": get_match_cond(doctype),
+			"start": start,
+			"page_len": page_len,
+			"txt": "%(txt)s",
+		},
+		{"txt": ("%%%s%%" % txt)},
+		as_dict=as_dict,
+	)
 
 
 @frappe.whitelist()
@@ -391,12 +408,12 @@
 
 	batch_nos = None
 	args = {
-		'item_code': filters.get("item_code"),
-		'warehouse': filters.get("warehouse"),
-		'posting_date': filters.get('posting_date'),
-		'txt': "%{0}%".format(txt),
+		"item_code": filters.get("item_code"),
+		"warehouse": filters.get("warehouse"),
+		"posting_date": filters.get("posting_date"),
+		"txt": "%{0}%".format(txt),
 		"start": start,
-		"page_len": page_len
+		"page_len": page_len,
 	}
 
 	having_clause = "having sum(sle.actual_qty) > 0"
@@ -406,20 +423,21 @@
 	meta = frappe.get_meta("Batch", cached=True)
 	searchfields = meta.get_search_fields()
 
-	search_columns = ''
-	search_cond = ''
+	search_columns = ""
+	search_cond = ""
 
 	if searchfields:
 		search_columns = ", " + ", ".join(searchfields)
 		search_cond = " or " + " or ".join([field + " like %(txt)s" for field in searchfields])
 
-	if args.get('warehouse'):
-		searchfields = ['batch.' + field for field in searchfields]
+	if args.get("warehouse"):
+		searchfields = ["batch." + field for field in searchfields]
 		if searchfields:
 			search_columns = ", " + ", ".join(searchfields)
 			search_cond = " or " + " or ".join([field + " like %(txt)s" for field in searchfields])
 
-		batch_nos = frappe.db.sql("""select sle.batch_no, round(sum(sle.actual_qty),2), sle.stock_uom,
+		batch_nos = frappe.db.sql(
+			"""select sle.batch_no, round(sum(sle.actual_qty),2), sle.stock_uom,
 				concat('MFG-',batch.manufacturing_date), concat('EXP-',batch.expiry_date)
 				{search_columns}
 			from `tabStock Ledger Entry` sle
@@ -438,17 +456,20 @@
 				{match_conditions}
 			group by batch_no {having_clause}
 			order by batch.expiry_date, sle.batch_no desc
-			limit %(start)s, %(page_len)s""".format(
-				search_columns = search_columns,
+			limit %(page_len)s offset %(start)s""".format(
+				search_columns=search_columns,
 				cond=cond,
 				match_conditions=get_match_cond(doctype),
-				having_clause = having_clause,
-				search_cond = search_cond
-			), args)
+				having_clause=having_clause,
+				search_cond=search_cond,
+			),
+			args,
+		)
 
 		return batch_nos
 	else:
-		return frappe.db.sql("""select name, concat('MFG-', manufacturing_date), concat('EXP-',expiry_date)
+		return frappe.db.sql(
+			"""select name, concat('MFG-', manufacturing_date), concat('EXP-',expiry_date)
 			{search_columns}
 			from `tabBatch` batch
 			where batch.disabled = 0
@@ -462,8 +483,14 @@
 			{match_conditions}
 
 			order by expiry_date, name desc
-			limit %(start)s, %(page_len)s""".format(cond, search_columns = search_columns,
-			search_cond = search_cond, match_conditions=get_match_cond(doctype)), args)
+			limit %(page_len)s offset %(start)s""".format(
+				cond,
+				search_columns=search_columns,
+				search_cond=search_cond,
+				match_conditions=get_match_cond(doctype),
+			),
+			args,
+		)
 
 
 @frappe.whitelist()
@@ -486,25 +513,33 @@
 	if searchfield and txt:
 		filter_list.append([doctype, searchfield, "like", "%%%s%%" % txt])
 
-	return frappe.desk.reportview.execute("Account", filters = filter_list,
-		fields = ["name", "parent_account"],
-		limit_start=start, limit_page_length=page_len, as_list=True)
+	return frappe.desk.reportview.execute(
+		"Account",
+		filters=filter_list,
+		fields=["name", "parent_account"],
+		limit_start=start,
+		limit_page_length=page_len,
+		as_list=True,
+	)
+
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_blanket_orders(doctype, txt, searchfield, start, page_len, filters):
-	return frappe.db.sql("""select distinct bo.name, bo.blanket_order_type, bo.to_date
+	return frappe.db.sql(
+		"""select distinct bo.name, bo.blanket_order_type, bo.to_date
 		from `tabBlanket Order` bo, `tabBlanket Order Item` boi
 		where
 			boi.parent = bo.name
 			and boi.item_code = {item_code}
 			and bo.blanket_order_type = '{blanket_order_type}'
 			and bo.company = {company}
-			and bo.docstatus = 1"""
-		.format(item_code = frappe.db.escape(filters.get("item")),
-			blanket_order_type = filters.get("blanket_order_type"),
-			company = frappe.db.escape(filters.get("company"))
-		))
+			and bo.docstatus = 1""".format(
+			item_code=frappe.db.escape(filters.get("item")),
+			blanket_order_type=filters.get("blanket_order_type"),
+			company=frappe.db.escape(filters.get("company")),
+		)
+	)
 
 
 @frappe.whitelist()
@@ -515,23 +550,26 @@
 	# income account can be any Credit account,
 	# but can also be a Asset account with account_type='Income Account' in special circumstances.
 	# Hence the first condition is an "OR"
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	condition = ""
 	if filters.get("company"):
 		condition += "and tabAccount.company = %(company)s"
 
-	return frappe.db.sql("""select tabAccount.name from `tabAccount`
+	return frappe.db.sql(
+		"""select tabAccount.name from `tabAccount`
 			where (tabAccount.report_type = "Profit and Loss"
 					or tabAccount.account_type in ("Income Account", "Temporary"))
 				and tabAccount.is_group=0
 				and tabAccount.`{key}` LIKE %(txt)s
 				{condition} {match_condition}
-			order by idx desc, name"""
-			.format(condition=condition, match_condition=get_match_cond(doctype), key=searchfield), {
-				'txt': '%' + txt + '%',
-				'company': filters.get("company", "")
-			})
+			order by idx desc, name""".format(
+			condition=condition, match_condition=get_match_cond(doctype), key=searchfield
+		),
+		{"txt": "%" + txt + "%", "company": filters.get("company", "")},
+	)
+
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
@@ -539,68 +577,73 @@
 	from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import (
 		get_dimension_filter_map,
 	)
+
 	dimension_filters = get_dimension_filter_map()
-	dimension_filters = dimension_filters.get((filters.get('dimension'),filters.get('account')))
+	dimension_filters = dimension_filters.get((filters.get("dimension"), filters.get("account")))
 	query_filters = []
 	or_filters = []
-	fields = ['name']
+	fields = ["name"]
 
 	searchfields = frappe.get_meta(doctype).get_search_fields()
 
 	meta = frappe.get_meta(doctype)
 	if meta.is_tree:
-		query_filters.append(['is_group', '=', 0])
+		query_filters.append(["is_group", "=", 0])
 
-	if meta.has_field('disabled'):
-		query_filters.append(['disabled', '!=', 1])
+	if meta.has_field("disabled"):
+		query_filters.append(["disabled", "!=", 1])
 
-	if meta.has_field('company'):
-		query_filters.append(['company', '=', filters.get('company')])
+	if meta.has_field("company"):
+		query_filters.append(["company", "=", filters.get("company")])
 
 	for field in searchfields:
-		or_filters.append([field, 'LIKE', "%%%s%%" % txt])
+		or_filters.append([field, "LIKE", "%%%s%%" % txt])
 		fields.append(field)
 
 	if dimension_filters:
-		if dimension_filters['allow_or_restrict'] == 'Allow':
-			query_selector = 'in'
+		if dimension_filters["allow_or_restrict"] == "Allow":
+			query_selector = "in"
 		else:
-			query_selector = 'not in'
+			query_selector = "not in"
 
-		if len(dimension_filters['allowed_dimensions']) == 1:
-			dimensions = tuple(dimension_filters['allowed_dimensions'] * 2)
+		if len(dimension_filters["allowed_dimensions"]) == 1:
+			dimensions = tuple(dimension_filters["allowed_dimensions"] * 2)
 		else:
-			dimensions = tuple(dimension_filters['allowed_dimensions'])
+			dimensions = tuple(dimension_filters["allowed_dimensions"])
 
-		query_filters.append(['name', query_selector, dimensions])
+		query_filters.append(["name", query_selector, dimensions])
 
-	output = frappe.get_list(doctype, fields=fields, filters=query_filters, or_filters=or_filters, as_list=1)
+	output = frappe.get_list(
+		doctype, fields=fields, filters=query_filters, or_filters=or_filters, as_list=1
+	)
 
 	return [tuple(d) for d in set(output)]
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
 	from erpnext.controllers.queries import get_match_cond
 
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	condition = ""
 	if filters.get("company"):
 		condition += "and tabAccount.company = %(company)s"
 
-	return frappe.db.sql("""select tabAccount.name from `tabAccount`
+	return frappe.db.sql(
+		"""select tabAccount.name from `tabAccount`
 		where (tabAccount.report_type = "Profit and Loss"
 				or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary", "Asset Received But Not Billed", "Capital Work in Progress"))
 			and tabAccount.is_group=0
 			and tabAccount.docstatus!=2
 			and tabAccount.{key} LIKE %(txt)s
-			{condition} {match_condition}"""
-		.format(condition=condition, key=searchfield,
-			match_condition=get_match_cond(doctype)), {
-			'company': filters.get("company", ""),
-			'txt': '%' + txt + '%'
-		})
+			{condition} {match_condition}""".format(
+			condition=condition, key=searchfield, match_condition=get_match_cond(doctype)
+		),
+		{"company": filters.get("company", ""), "txt": "%" + txt + "%"},
+	)
 
 
 @frappe.whitelist()
@@ -611,7 +654,7 @@
 	filter_dict = get_doctype_wise_filters(filters)
 
 	query = """select `tabWarehouse`.name,
-		CONCAT_WS(" : ", "Actual Qty", ifnull(round(`tabBin`.actual_qty, 2), 0 )) actual_qty
+		CONCAT_WS(' : ', 'Actual Qty', ifnull(round(`tabBin`.actual_qty, 2), 0 )) actual_qty
 		from `tabWarehouse` left join `tabBin`
 		on `tabBin`.warehouse = `tabWarehouse`.name {bin_conditions}
 		where
@@ -619,16 +662,18 @@
 			{fcond} {mcond}
 		order by ifnull(`tabBin`.actual_qty, 0) desc
 		limit
-			{start}, {page_len}
+			{page_len} offset {start}
 		""".format(
-			bin_conditions=get_filters_cond(doctype, filter_dict.get("Bin"),bin_conditions, ignore_permissions=True),
-			key=searchfield,
-			fcond=get_filters_cond(doctype, filter_dict.get("Warehouse"), conditions),
-			mcond=get_match_cond(doctype),
-			start=start,
-			page_len=page_len,
-			txt=frappe.db.escape('%{0}%'.format(txt))
-		)
+		bin_conditions=get_filters_cond(
+			doctype, filter_dict.get("Bin"), bin_conditions, ignore_permissions=True
+		),
+		key=searchfield,
+		fcond=get_filters_cond(doctype, filter_dict.get("Warehouse"), conditions),
+		mcond=get_match_cond(doctype),
+		start=start,
+		page_len=page_len,
+		txt=frappe.db.escape("%{0}%".format(txt)),
+	)
 
 	return frappe.db.sql(query)
 
@@ -646,11 +691,13 @@
 def get_batch_numbers(doctype, txt, searchfield, start, page_len, filters):
 	query = """select batch_id from `tabBatch`
 			where disabled = 0
-			and (expiry_date >= CURDATE() or expiry_date IS NULL)
-			and name like {txt}""".format(txt = frappe.db.escape('%{0}%'.format(txt)))
+			and (expiry_date >= CURRENT_DATE or expiry_date IS NULL)
+			and name like {txt}""".format(
+		txt=frappe.db.escape("%{0}%".format(txt))
+	)
 
-	if filters and filters.get('item'):
-		query += " and item = {item}".format(item = frappe.db.escape(filters.get('item')))
+	if filters and filters.get("item"):
+		query += " and item = {item}".format(item=frappe.db.escape(filters.get("item")))
 
 	return frappe.db.sql(query, filters)
 
@@ -659,8 +706,8 @@
 @frappe.validate_and_sanitize_search_inputs
 def item_manufacturer_query(doctype, txt, searchfield, start, page_len, filters):
 	item_filters = [
-		['manufacturer', 'like', '%' + txt + '%'],
-		['item_code', '=', filters.get("item_code")]
+		["manufacturer", "like", "%" + txt + "%"],
+		["item_code", "=", filters.get("item_code")],
 	]
 
 	item_manufacturers = frappe.get_all(
@@ -669,7 +716,7 @@
 		filters=item_filters,
 		limit_start=start,
 		limit_page_length=page_len,
-		as_list=1
+		as_list=1,
 	)
 	return item_manufacturers
 
@@ -681,10 +728,14 @@
 		select pr.name
 		from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pritem
 		where pr.docstatus = 1 and pritem.parent = pr.name
-		and pr.name like {txt}""".format(txt = frappe.db.escape('%{0}%'.format(txt)))
+		and pr.name like {txt}""".format(
+		txt=frappe.db.escape("%{0}%".format(txt))
+	)
 
-	if filters and filters.get('item_code'):
-		query += " and pritem.item_code = {item_code}".format(item_code = frappe.db.escape(filters.get('item_code')))
+	if filters and filters.get("item_code"):
+		query += " and pritem.item_code = {item_code}".format(
+			item_code=frappe.db.escape(filters.get("item_code"))
+		)
 
 	return frappe.db.sql(query, filters)
 
@@ -696,10 +747,14 @@
 		select pi.name
 		from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` piitem
 		where pi.docstatus = 1 and piitem.parent = pi.name
-		and pi.name like {txt}""".format(txt = frappe.db.escape('%{0}%'.format(txt)))
+		and pi.name like {txt}""".format(
+		txt=frappe.db.escape("%{0}%".format(txt))
+	)
 
-	if filters and filters.get('item_code'):
-		query += " and piitem.item_code = {item_code}".format(item_code = frappe.db.escape(filters.get('item_code')))
+	if filters and filters.get("item_code"):
+		query += " and piitem.item_code = {item_code}".format(
+			item_code=frappe.db.escape(filters.get("item_code"))
+		)
 
 	return frappe.db.sql(query, filters)
 
@@ -708,27 +763,29 @@
 @frappe.validate_and_sanitize_search_inputs
 def get_tax_template(doctype, txt, searchfield, start, page_len, filters):
 
-	item_doc = frappe.get_cached_doc('Item', filters.get('item_code'))
-	item_group = filters.get('item_group')
-	company = filters.get('company')
+	item_doc = frappe.get_cached_doc("Item", filters.get("item_code"))
+	item_group = filters.get("item_group")
+	company = filters.get("company")
 	taxes = item_doc.taxes or []
 
 	while item_group:
-		item_group_doc = frappe.get_cached_doc('Item Group', item_group)
+		item_group_doc = frappe.get_cached_doc("Item Group", item_group)
 		taxes += item_group_doc.taxes or []
 		item_group = item_group_doc.parent_item_group
 
 	if not taxes:
-		return frappe.get_all('Item Tax Template', filters={'disabled': 0, 'company': company}, as_list=True)
+		return frappe.get_all(
+			"Item Tax Template", filters={"disabled": 0, "company": company}, as_list=True
+		)
 	else:
-		valid_from = filters.get('valid_from')
+		valid_from = filters.get("valid_from")
 		valid_from = valid_from[1] if isinstance(valid_from, list) else valid_from
 
 		args = {
-			'item_code': filters.get('item_code'),
-			'posting_date': valid_from,
-			'tax_category': filters.get('tax_category'),
-			'company': company
+			"item_code": filters.get("item_code"),
+			"posting_date": valid_from,
+			"tax_category": filters.get("tax_category"),
+			"company": company,
 		}
 
 		taxes = _get_item_tax_template(args, taxes, for_validate=True)
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 8c3aab4..d24ac3f 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -11,7 +11,9 @@
 from erpnext.stock.utils import get_incoming_rate
 
 
-class StockOverReturnError(frappe.ValidationError): pass
+class StockOverReturnError(frappe.ValidationError):
+	pass
+
 
 def validate_return(doc):
 	if not doc.meta.get_field("is_return") or not doc.is_return:
@@ -21,32 +23,50 @@
 		validate_return_against(doc)
 		validate_returned_items(doc)
 
+
 def validate_return_against(doc):
 	if not frappe.db.exists(doc.doctype, doc.return_against):
-			frappe.throw(_("Invalid {0}: {1}")
-				.format(doc.meta.get_label("return_against"), doc.return_against))
+		frappe.throw(
+			_("Invalid {0}: {1}").format(doc.meta.get_label("return_against"), doc.return_against)
+		)
 	else:
 		ref_doc = frappe.get_doc(doc.doctype, doc.return_against)
 
 		party_type = "customer" if doc.doctype in ("Sales Invoice", "Delivery Note") else "supplier"
 
-		if ref_doc.company == doc.company and ref_doc.get(party_type) == doc.get(party_type) and ref_doc.docstatus == 1:
+		if (
+			ref_doc.company == doc.company
+			and ref_doc.get(party_type) == doc.get(party_type)
+			and ref_doc.docstatus == 1
+		):
 			# validate posting date time
 			return_posting_datetime = "%s %s" % (doc.posting_date, doc.get("posting_time") or "00:00:00")
-			ref_posting_datetime = "%s %s" % (ref_doc.posting_date, ref_doc.get("posting_time") or "00:00:00")
+			ref_posting_datetime = "%s %s" % (
+				ref_doc.posting_date,
+				ref_doc.get("posting_time") or "00:00:00",
+			)
 
 			if get_datetime(return_posting_datetime) < get_datetime(ref_posting_datetime):
-				frappe.throw(_("Posting timestamp must be after {0}").format(format_datetime(ref_posting_datetime)))
+				frappe.throw(
+					_("Posting timestamp must be after {0}").format(format_datetime(ref_posting_datetime))
+				)
 
 			# validate same exchange rate
 			if doc.conversion_rate != ref_doc.conversion_rate:
-				frappe.throw(_("Exchange Rate must be same as {0} {1} ({2})")
-					.format(doc.doctype, doc.return_against, ref_doc.conversion_rate))
+				frappe.throw(
+					_("Exchange Rate must be same as {0} {1} ({2})").format(
+						doc.doctype, doc.return_against, ref_doc.conversion_rate
+					)
+				)
 
 			# validate update stock
 			if doc.doctype == "Sales Invoice" and doc.update_stock and not ref_doc.update_stock:
-					frappe.throw(_("'Update Stock' can not be checked because items are not delivered via {0}")
-						.format(doc.return_against))
+				frappe.throw(
+					_("'Update Stock' can not be checked because items are not delivered via {0}").format(
+						doc.return_against
+					)
+				)
+
 
 def validate_returned_items(doc):
 	from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@@ -54,43 +74,61 @@
 	valid_items = frappe._dict()
 
 	select_fields = "item_code, qty, stock_qty, rate, parenttype, conversion_factor"
-	if doc.doctype != 'Purchase Invoice':
+	if doc.doctype != "Purchase Invoice":
 		select_fields += ",serial_no, batch_no"
 
-	if doc.doctype in ['Purchase Invoice', 'Purchase Receipt']:
+	if doc.doctype in ["Purchase Invoice", "Purchase Receipt"]:
 		select_fields += ",rejected_qty, received_qty"
 
-	for d in frappe.db.sql("""select {0} from `tab{1} Item` where parent = %s"""
-		.format(select_fields, doc.doctype), doc.return_against, as_dict=1):
-			valid_items = get_ref_item_dict(valid_items, d)
+	for d in frappe.db.sql(
+		"""select {0} from `tab{1} Item` where parent = %s""".format(select_fields, doc.doctype),
+		doc.return_against,
+		as_dict=1,
+	):
+		valid_items = get_ref_item_dict(valid_items, d)
 
 	if doc.doctype in ("Delivery Note", "Sales Invoice"):
-		for d in frappe.db.sql("""select item_code, qty, serial_no, batch_no from `tabPacked Item`
-			where parent = %s""", doc.return_against, as_dict=1):
-				valid_items = get_ref_item_dict(valid_items, d)
+		for d in frappe.db.sql(
+			"""select item_code, qty, serial_no, batch_no from `tabPacked Item`
+			where parent = %s""",
+			doc.return_against,
+			as_dict=1,
+		):
+			valid_items = get_ref_item_dict(valid_items, d)
 
 	already_returned_items = get_already_returned_items(doc)
 
 	# ( not mandatory when it is Purchase Invoice or a Sales Invoice without Update Stock )
-	warehouse_mandatory = not ((doc.doctype=="Purchase Invoice" or doc.doctype=="Sales Invoice") and not doc.update_stock)
+	warehouse_mandatory = not (
+		(doc.doctype == "Purchase Invoice" or doc.doctype == "Sales Invoice") and not doc.update_stock
+	)
 
 	items_returned = False
 	for d in doc.get("items"):
-		if d.item_code and (flt(d.qty) < 0 or flt(d.get('received_qty')) < 0):
+		if d.item_code and (flt(d.qty) < 0 or flt(d.get("received_qty")) < 0):
 			if d.item_code not in valid_items:
-				frappe.throw(_("Row # {0}: Returned Item {1} does not exist in {2} {3}")
-					.format(d.idx, d.item_code, doc.doctype, doc.return_against))
+				frappe.throw(
+					_("Row # {0}: Returned Item {1} does not exist in {2} {3}").format(
+						d.idx, d.item_code, doc.doctype, doc.return_against
+					)
+				)
 			else:
 				ref = valid_items.get(d.item_code, frappe._dict())
 				validate_quantity(doc, d, ref, valid_items, already_returned_items)
 
 				if ref.rate and doc.doctype in ("Delivery Note", "Sales Invoice") and flt(d.rate) > ref.rate:
-					frappe.throw(_("Row # {0}: Rate cannot be greater than the rate used in {1} {2}")
-						.format(d.idx, doc.doctype, doc.return_against))
+					frappe.throw(
+						_("Row # {0}: Rate cannot be greater than the rate used in {1} {2}").format(
+							d.idx, doc.doctype, doc.return_against
+						)
+					)
 
 				elif ref.batch_no and d.batch_no not in ref.batch_no:
-					frappe.throw(_("Row # {0}: Batch No must be same as {1} {2}")
-						.format(d.idx, doc.doctype, doc.return_against))
+					frappe.throw(
+						_("Row # {0}: Batch No must be same as {1} {2}").format(
+							d.idx, doc.doctype, doc.return_against
+						)
+					)
 
 				elif ref.serial_no:
 					if not d.serial_no:
@@ -99,11 +137,16 @@
 						serial_nos = get_serial_nos(d.serial_no)
 						for s in serial_nos:
 							if s not in ref.serial_no:
-								frappe.throw(_("Row # {0}: Serial No {1} does not match with {2} {3}")
-									.format(d.idx, s, doc.doctype, doc.return_against))
+								frappe.throw(
+									_("Row # {0}: Serial No {1} does not match with {2} {3}").format(
+										d.idx, s, doc.doctype, doc.return_against
+									)
+								)
 
-				if (warehouse_mandatory and not d.get("warehouse") and
-					frappe.db.get_value("Item", d.item_code, "is_stock_item")
+				if (
+					warehouse_mandatory
+					and not d.get("warehouse")
+					and frappe.db.get_value("Item", d.item_code, "is_stock_item")
 				):
 					frappe.throw(_("Warehouse is mandatory"))
 
@@ -115,21 +158,23 @@
 	if not items_returned:
 		frappe.throw(_("Atleast one item should be entered with negative quantity in return document"))
 
+
 def validate_quantity(doc, args, ref, valid_items, already_returned_items):
-	fields = ['stock_qty']
-	if doc.doctype in ['Purchase Receipt', 'Purchase Invoice']:
-		fields.extend(['received_qty', 'rejected_qty'])
+	fields = ["stock_qty"]
+	if doc.doctype in ["Purchase Receipt", "Purchase Invoice"]:
+		fields.extend(["received_qty", "rejected_qty"])
 
 	already_returned_data = already_returned_items.get(args.item_code) or {}
 
 	company_currency = erpnext.get_company_currency(doc.company)
-	stock_qty_precision = get_field_precision(frappe.get_meta(doc.doctype + " Item")
-			.get_field("stock_qty"), company_currency)
+	stock_qty_precision = get_field_precision(
+		frappe.get_meta(doc.doctype + " Item").get_field("stock_qty"), company_currency
+	)
 
 	for column in fields:
 		returned_qty = flt(already_returned_data.get(column, 0)) if len(already_returned_data) > 0 else 0
 
-		if column == 'stock_qty':
+		if column == "stock_qty":
 			reference_qty = ref.get(column)
 			current_stock_qty = args.get(column)
 		else:
@@ -137,38 +182,49 @@
 			current_stock_qty = args.get(column) * args.get("conversion_factor", 1.0)
 
 		max_returnable_qty = flt(reference_qty, stock_qty_precision) - returned_qty
-		label = column.replace('_', ' ').title()
+		label = column.replace("_", " ").title()
 
 		if reference_qty:
 			if flt(args.get(column)) > 0:
 				frappe.throw(_("{0} must be negative in return document").format(label))
 			elif returned_qty >= reference_qty and args.get(column):
-				frappe.throw(_("Item {0} has already been returned")
-					.format(args.item_code), StockOverReturnError)
+				frappe.throw(
+					_("Item {0} has already been returned").format(args.item_code), StockOverReturnError
+				)
 			elif abs(flt(current_stock_qty, stock_qty_precision)) > max_returnable_qty:
-				frappe.throw(_("Row # {0}: Cannot return more than {1} for Item {2}")
-					.format(args.idx, max_returnable_qty, args.item_code), StockOverReturnError)
+				frappe.throw(
+					_("Row # {0}: Cannot return more than {1} for Item {2}").format(
+						args.idx, max_returnable_qty, args.item_code
+					),
+					StockOverReturnError,
+				)
+
 
 def get_ref_item_dict(valid_items, ref_item_row):
 	from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 
-	valid_items.setdefault(ref_item_row.item_code, frappe._dict({
-		"qty": 0,
-		"rate": 0,
-		"stock_qty": 0,
-		"rejected_qty": 0,
-		"received_qty": 0,
-		"serial_no": [],
-		"conversion_factor": ref_item_row.get("conversion_factor", 1),
-		"batch_no": []
-	}))
+	valid_items.setdefault(
+		ref_item_row.item_code,
+		frappe._dict(
+			{
+				"qty": 0,
+				"rate": 0,
+				"stock_qty": 0,
+				"rejected_qty": 0,
+				"received_qty": 0,
+				"serial_no": [],
+				"conversion_factor": ref_item_row.get("conversion_factor", 1),
+				"batch_no": [],
+			}
+		),
+	)
 	item_dict = valid_items[ref_item_row.item_code]
 	item_dict["qty"] += ref_item_row.qty
-	item_dict["stock_qty"] += ref_item_row.get('stock_qty', 0)
+	item_dict["stock_qty"] += ref_item_row.get("stock_qty", 0)
 	if ref_item_row.get("rate", 0) > item_dict["rate"]:
 		item_dict["rate"] = ref_item_row.get("rate", 0)
 
-	if ref_item_row.parenttype in ['Purchase Invoice', 'Purchase Receipt']:
+	if ref_item_row.parenttype in ["Purchase Invoice", "Purchase Receipt"]:
 		item_dict["received_qty"] += ref_item_row.received_qty
 		item_dict["rejected_qty"] += ref_item_row.rejected_qty
 
@@ -180,13 +236,15 @@
 
 	return valid_items
 
+
 def get_already_returned_items(doc):
-	column = 'child.item_code, sum(abs(child.qty)) as qty, sum(abs(child.stock_qty)) as stock_qty'
-	if doc.doctype in ['Purchase Invoice', 'Purchase Receipt']:
+	column = "child.item_code, sum(abs(child.qty)) as qty, sum(abs(child.stock_qty)) as stock_qty"
+	if doc.doctype in ["Purchase Invoice", "Purchase Receipt"]:
 		column += """, sum(abs(child.rejected_qty) * child.conversion_factor) as rejected_qty,
 			sum(abs(child.received_qty) * child.conversion_factor) as received_qty"""
 
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 		select {0}
 		from
 			`tab{1} Item` child, `tab{2}` par
@@ -194,60 +252,84 @@
 			child.parent = par.name and par.docstatus = 1
 			and par.is_return = 1 and par.return_against = %s
 		group by item_code
-	""".format(column, doc.doctype, doc.doctype), doc.return_against, as_dict=1)
+	""".format(
+			column, doc.doctype, doc.doctype
+		),
+		doc.return_against,
+		as_dict=1,
+	)
 
 	items = {}
 
 	for d in data:
-		items.setdefault(d.item_code, frappe._dict({
-			"qty": d.get("qty"),
-			"stock_qty": d.get("stock_qty"),
-			"received_qty": d.get("received_qty"),
-			"rejected_qty": d.get("rejected_qty")
-		}))
+		items.setdefault(
+			d.item_code,
+			frappe._dict(
+				{
+					"qty": d.get("qty"),
+					"stock_qty": d.get("stock_qty"),
+					"received_qty": d.get("received_qty"),
+					"rejected_qty": d.get("rejected_qty"),
+				}
+			),
+		)
 
 	return items
 
-def get_returned_qty_map_for_row(row_name, doctype):
+
+def get_returned_qty_map_for_row(return_against, party, row_name, doctype):
 	child_doctype = doctype + " Item"
 	reference_field = "dn_detail" if doctype == "Delivery Note" else frappe.scrub(child_doctype)
 
+	if doctype in ("Purchase Receipt", "Purchase Invoice"):
+		party_type = "supplier"
+	else:
+		party_type = "customer"
+
 	fields = [
 		"sum(abs(`tab{0}`.qty)) as qty".format(child_doctype),
-		"sum(abs(`tab{0}`.stock_qty)) as stock_qty".format(child_doctype)
+		"sum(abs(`tab{0}`.stock_qty)) as stock_qty".format(child_doctype),
 	]
 
 	if doctype in ("Purchase Receipt", "Purchase Invoice"):
 		fields += [
 			"sum(abs(`tab{0}`.rejected_qty)) as rejected_qty".format(child_doctype),
-			"sum(abs(`tab{0}`.received_qty)) as received_qty".format(child_doctype)
+			"sum(abs(`tab{0}`.received_qty)) as received_qty".format(child_doctype),
 		]
 
 		if doctype == "Purchase Receipt":
 			fields += ["sum(abs(`tab{0}`.received_stock_qty)) as received_stock_qty".format(child_doctype)]
 
-	data = frappe.db.get_list(doctype,
-		fields = fields,
-		filters = [
+	# Used retrun against and supplier and is_retrun because there is an index added for it
+	data = frappe.db.get_list(
+		doctype,
+		fields=fields,
+		filters=[
+			[doctype, "return_against", "=", return_against],
+			[doctype, party_type, "=", party],
 			[doctype, "docstatus", "=", 1],
 			[doctype, "is_return", "=", 1],
-			[child_doctype, reference_field, "=", row_name]
-	])
+			[child_doctype, reference_field, "=", row_name],
+		],
+	)
 
 	return data[0]
 
-def make_return_doc(doctype, source_name, target_doc=None):
+
+def make_return_doc(doctype: str, source_name: str, target_doc=None):
 	from frappe.model.mapper import get_mapped_doc
 
 	from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
 	company = frappe.db.get_value("Delivery Note", source_name, "company")
-	default_warehouse_for_sales_return = frappe.db.get_value("Company", company, "default_warehouse_for_sales_return")
+	default_warehouse_for_sales_return = frappe.db.get_value(
+		"Company", company, "default_warehouse_for_sales_return"
+	)
 
 	def set_missing_values(source, target):
 		doc = frappe.get_doc(target)
 		doc.is_return = 1
 		doc.return_against = source.name
-		doc.ignore_pricing_rule = 1
 		doc.set_warehouse = ""
 		if doctype == "Sales Invoice" or doctype == "POS Invoice":
 			doc.is_pos = source.is_pos
@@ -265,29 +347,34 @@
 				tax.tax_amount = -1 * tax.tax_amount
 
 		if doc.get("is_return"):
-			if doc.doctype == 'Sales Invoice' or doc.doctype == 'POS Invoice':
+			if doc.doctype == "Sales Invoice" or doc.doctype == "POS Invoice":
 				doc.consolidated_invoice = ""
-				doc.set('payments', [])
+				doc.set("payments", [])
 				for data in source.payments:
 					paid_amount = 0.00
 					base_paid_amount = 0.00
-					data.base_amount = flt(data.amount*source.conversion_rate, source.precision("base_paid_amount"))
+					data.base_amount = flt(
+						data.amount * source.conversion_rate, source.precision("base_paid_amount")
+					)
 					paid_amount += data.amount
 					base_paid_amount += data.base_amount
-					doc.append('payments', {
-						'mode_of_payment': data.mode_of_payment,
-						'type': data.type,
-						'amount': -1 * paid_amount,
-						'base_amount': -1 * base_paid_amount,
-						'account': data.account,
-						'default': data.default
-					})
+					doc.append(
+						"payments",
+						{
+							"mode_of_payment": data.mode_of_payment,
+							"type": data.type,
+							"amount": -1 * paid_amount,
+							"base_amount": -1 * base_paid_amount,
+							"account": data.account,
+							"default": data.default,
+						},
+					)
 				if doc.is_pos:
 					doc.paid_amount = -1 * source.paid_amount
-			elif doc.doctype == 'Purchase Invoice':
+			elif doc.doctype == "Purchase Invoice":
 				doc.paid_amount = -1 * source.paid_amount
 				doc.base_paid_amount = -1 * source.base_paid_amount
-				doc.payment_terms_template = ''
+				doc.payment_terms_template = ""
 				doc.payment_schedule = []
 
 		if doc.get("is_return") and hasattr(doc, "packed_items"):
@@ -304,16 +391,24 @@
 			returned_serial_nos = get_returned_serial_nos(source_doc, source_parent)
 			serial_nos = list(set(get_serial_nos(source_doc.serial_no)) - set(returned_serial_nos))
 			if serial_nos:
-				target_doc.serial_no = '\n'.join(serial_nos)
+				target_doc.serial_no = "\n".join(serial_nos)
 
 		if doctype == "Purchase Receipt":
-			returned_qty_map = get_returned_qty_map_for_row(source_doc.name, doctype)
-			target_doc.received_qty = -1 * flt(source_doc.received_qty - (returned_qty_map.get('received_qty') or 0))
-			target_doc.rejected_qty = -1 * flt(source_doc.rejected_qty - (returned_qty_map.get('rejected_qty') or 0))
-			target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get('qty') or 0))
+			returned_qty_map = get_returned_qty_map_for_row(
+				source_parent.name, source_parent.supplier, source_doc.name, doctype
+			)
+			target_doc.received_qty = -1 * flt(
+				source_doc.received_qty - (returned_qty_map.get("received_qty") or 0)
+			)
+			target_doc.rejected_qty = -1 * flt(
+				source_doc.rejected_qty - (returned_qty_map.get("rejected_qty") or 0)
+			)
+			target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get("qty") or 0))
 
-			target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (returned_qty_map.get('stock_qty') or 0))
-			target_doc.received_stock_qty = -1 * flt(source_doc.received_stock_qty - (returned_qty_map.get('received_stock_qty') or 0))
+			target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (returned_qty_map.get("stock_qty") or 0))
+			target_doc.received_stock_qty = -1 * flt(
+				source_doc.received_stock_qty - (returned_qty_map.get("received_stock_qty") or 0)
+			)
 
 			target_doc.purchase_order = source_doc.purchase_order
 			target_doc.purchase_order_item = source_doc.purchase_order_item
@@ -321,12 +416,18 @@
 			target_doc.purchase_receipt_item = source_doc.name
 
 		elif doctype == "Purchase Invoice":
-			returned_qty_map = get_returned_qty_map_for_row(source_doc.name, doctype)
-			target_doc.received_qty = -1 * flt(source_doc.received_qty - (returned_qty_map.get('received_qty') or 0))
-			target_doc.rejected_qty = -1 * flt(source_doc.rejected_qty - (returned_qty_map.get('rejected_qty') or 0))
-			target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get('qty') or 0))
+			returned_qty_map = get_returned_qty_map_for_row(
+				source_parent.name, source_parent.supplier, source_doc.name, doctype
+			)
+			target_doc.received_qty = -1 * flt(
+				source_doc.received_qty - (returned_qty_map.get("received_qty") or 0)
+			)
+			target_doc.rejected_qty = -1 * flt(
+				source_doc.rejected_qty - (returned_qty_map.get("rejected_qty") or 0)
+			)
+			target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get("qty") or 0))
 
-			target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (returned_qty_map.get('stock_qty') or 0))
+			target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (returned_qty_map.get("stock_qty") or 0))
 			target_doc.purchase_order = source_doc.purchase_order
 			target_doc.purchase_receipt = source_doc.purchase_receipt
 			target_doc.rejected_warehouse = source_doc.rejected_warehouse
@@ -335,9 +436,11 @@
 			target_doc.purchase_invoice_item = source_doc.name
 
 		elif doctype == "Delivery Note":
-			returned_qty_map = get_returned_qty_map_for_row(source_doc.name, doctype)
-			target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get('qty') or 0))
-			target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (returned_qty_map.get('stock_qty') or 0))
+			returned_qty_map = get_returned_qty_map_for_row(
+				source_parent.name, source_parent.customer, source_doc.name, doctype
+			)
+			target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get("qty") or 0))
+			target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (returned_qty_map.get("stock_qty") or 0))
 
 			target_doc.against_sales_order = source_doc.against_sales_order
 			target_doc.against_sales_invoice = source_doc.against_sales_invoice
@@ -348,9 +451,11 @@
 			if default_warehouse_for_sales_return:
 				target_doc.warehouse = default_warehouse_for_sales_return
 		elif doctype == "Sales Invoice" or doctype == "POS Invoice":
-			returned_qty_map = get_returned_qty_map_for_row(source_doc.name, doctype)
-			target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get('qty') or 0))
-			target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (returned_qty_map.get('stock_qty') or 0))
+			returned_qty_map = get_returned_qty_map_for_row(
+				source_parent.name, source_parent.customer, source_doc.name, doctype
+			)
+			target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get("qty") or 0))
+			target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (returned_qty_map.get("stock_qty") or 0))
 
 			target_doc.sales_order = source_doc.sales_order
 			target_doc.delivery_note = source_doc.delivery_note
@@ -369,39 +474,56 @@
 	def update_terms(source_doc, target_doc, source_parent):
 		target_doc.payment_amount = -source_doc.payment_amount
 
-	doclist = get_mapped_doc(doctype, source_name,	{
-		doctype: {
-			"doctype": doctype,
-
-			"validation": {
-				"docstatus": ["=", 1],
-			}
-		},
-		doctype +" Item": {
-			"doctype": doctype + " Item",
-			"field_map": {
-				"serial_no": "serial_no",
-				"batch_no": "batch_no"
+	doclist = get_mapped_doc(
+		doctype,
+		source_name,
+		{
+			doctype: {
+				"doctype": doctype,
+				"validation": {
+					"docstatus": ["=", 1],
+				},
 			},
-			"postprocess": update_item
+			doctype
+			+ " Item": {
+				"doctype": doctype + " Item",
+				"field_map": {"serial_no": "serial_no", "batch_no": "batch_no"},
+				"postprocess": update_item,
+			},
+			"Payment Schedule": {"doctype": "Payment Schedule", "postprocess": update_terms},
 		},
-		"Payment Schedule": {
-			"doctype": "Payment Schedule",
-			"postprocess": update_terms
-		}
-	}, target_doc, set_missing_values)
+		target_doc,
+		set_missing_values,
+	)
+
+	doclist.set_onload("ignore_price_list", True)
 
 	return doclist
 
-def get_rate_for_return(voucher_type, voucher_no, item_code, return_against=None,
-	item_row=None, voucher_detail_no=None, sle=None):
+
+def get_rate_for_return(
+	voucher_type,
+	voucher_no,
+	item_code,
+	return_against=None,
+	item_row=None,
+	voucher_detail_no=None,
+	sle=None,
+):
 	if not return_against:
 		return_against = frappe.get_cached_value(voucher_type, voucher_no, "return_against")
 
 	return_against_item_field = get_return_against_item_fields(voucher_type)
 
-	filters = get_filters(voucher_type, voucher_no, voucher_detail_no,
-		return_against, item_code, return_against_item_field, item_row)
+	filters = get_filters(
+		voucher_type,
+		voucher_no,
+		voucher_detail_no,
+		return_against,
+		item_code,
+		return_against_item_field,
+		item_row,
+	)
 
 	if voucher_type in ("Purchase Receipt", "Purchase Invoice"):
 		select_field = "incoming_rate"
@@ -409,53 +531,66 @@
 		select_field = "abs(stock_value_difference / actual_qty)"
 
 	rate = flt(frappe.db.get_value("Stock Ledger Entry", filters, select_field))
-	if not (rate and return_against) and voucher_type in ['Sales Invoice', 'Delivery Note']:
-		rate = frappe.db.get_value(f'{voucher_type} Item', voucher_detail_no, 'incoming_rate')
+	if not (rate and return_against) and voucher_type in ["Sales Invoice", "Delivery Note"]:
+		rate = frappe.db.get_value(f"{voucher_type} Item", voucher_detail_no, "incoming_rate")
 
 		if not rate and sle:
-			rate = get_incoming_rate({
-				"item_code": sle.item_code,
-				"warehouse": sle.warehouse,
-				"posting_date": sle.get('posting_date'),
-				"posting_time": sle.get('posting_time'),
-				"qty": sle.actual_qty,
-				"serial_no": sle.get('serial_no'),
-				"batch_no": sle.get("batch_no"),
-				"company": sle.company,
-				"voucher_type": sle.voucher_type,
-				"voucher_no": sle.voucher_no
-			}, raise_error_if_no_rate=False)
+			rate = get_incoming_rate(
+				{
+					"item_code": sle.item_code,
+					"warehouse": sle.warehouse,
+					"posting_date": sle.get("posting_date"),
+					"posting_time": sle.get("posting_time"),
+					"qty": sle.actual_qty,
+					"serial_no": sle.get("serial_no"),
+					"batch_no": sle.get("batch_no"),
+					"company": sle.company,
+					"voucher_type": sle.voucher_type,
+					"voucher_no": sle.voucher_no,
+				},
+				raise_error_if_no_rate=False,
+			)
 
 	return rate
 
+
 def get_return_against_item_fields(voucher_type):
 	return_against_item_fields = {
 		"Purchase Receipt": "purchase_receipt_item",
 		"Purchase Invoice": "purchase_invoice_item",
 		"Delivery Note": "dn_detail",
-		"Sales Invoice": "sales_invoice_item"
+		"Sales Invoice": "sales_invoice_item",
 	}
 	return return_against_item_fields[voucher_type]
 
-def get_filters(voucher_type, voucher_no, voucher_detail_no, return_against, item_code, return_against_item_field, item_row):
-	filters = {
-		"voucher_type": voucher_type,
-		"voucher_no": return_against,
-		"item_code": item_code
-	}
+
+def get_filters(
+	voucher_type,
+	voucher_no,
+	voucher_detail_no,
+	return_against,
+	item_code,
+	return_against_item_field,
+	item_row,
+):
+	filters = {"voucher_type": voucher_type, "voucher_no": return_against, "item_code": item_code}
 
 	if item_row:
 		reference_voucher_detail_no = item_row.get(return_against_item_field)
 	else:
-		reference_voucher_detail_no = frappe.db.get_value(voucher_type + " Item", voucher_detail_no, return_against_item_field)
+		reference_voucher_detail_no = frappe.db.get_value(
+			voucher_type + " Item", voucher_detail_no, return_against_item_field
+		)
 
 	if reference_voucher_detail_no:
 		filters["voucher_detail_no"] = reference_voucher_detail_no
 
 	return filters
 
+
 def get_returned_serial_nos(child_doc, parent_doc):
 	from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
 	return_ref_field = frappe.scrub(child_doc.doctype)
 	if child_doc.doctype == "Delivery Note Item":
 		return_ref_field = "dn_detail"
@@ -464,10 +599,14 @@
 
 	fields = ["`{0}`.`serial_no`".format("tab" + child_doc.doctype)]
 
-	filters = [[parent_doc.doctype, "return_against", "=", parent_doc.name], [parent_doc.doctype, "is_return", "=", 1],
-		[child_doc.doctype, return_ref_field, "=", child_doc.name], [parent_doc.doctype, "docstatus", "=", 1]]
+	filters = [
+		[parent_doc.doctype, "return_against", "=", parent_doc.name],
+		[parent_doc.doctype, "is_return", "=", 1],
+		[child_doc.doctype, return_ref_field, "=", child_doc.name],
+		[parent_doc.doctype, "docstatus", "=", 1],
+	]
 
-	for row in frappe.get_all(parent_doc.doctype, fields = fields, filters=filters):
+	for row in frappe.get_all(parent_doc.doctype, fields=fields, filters=filters):
 		serial_nos.extend(get_serial_nos(row.serial_no))
 
 	return serial_nos
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index e918cde..70e2056 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -16,9 +16,11 @@
 
 
 class SellingController(StockController):
+	def __setup__(self):
+		self.flags.ignore_permlevel_for_fields = ["selling_price_list", "price_list_currency"]
+
 	def get_feed(self):
-		return _("To {0} | {1} {2}").format(self.customer_name, self.currency,
-			self.grand_total)
+		return _("To {0} | {1} {2}").format(self.customer_name, self.currency, self.grand_total)
 
 	def onload(self):
 		super(SellingController, self).onload()
@@ -64,32 +66,43 @@
 
 		if customer:
 			from erpnext.accounts.party import _get_party_details
+
 			fetch_payment_terms_template = False
-			if (self.get("__islocal") or
-				self.company != frappe.db.get_value(self.doctype, self.name, 'company')):
+			if self.get("__islocal") or self.company != frappe.db.get_value(
+				self.doctype, self.name, "company"
+			):
 				fetch_payment_terms_template = True
 
-			party_details = _get_party_details(customer,
+			party_details = _get_party_details(
+				customer,
 				ignore_permissions=self.flags.ignore_permissions,
-				doctype=self.doctype, company=self.company,
-				posting_date=self.get('posting_date'),
+				doctype=self.doctype,
+				company=self.company,
+				posting_date=self.get("posting_date"),
 				fetch_payment_terms_template=fetch_payment_terms_template,
-				party_address=self.customer_address, shipping_address=self.shipping_address_name,
-				company_address=self.get('company_address'))
+				party_address=self.customer_address,
+				shipping_address=self.shipping_address_name,
+				company_address=self.get("company_address"),
+			)
 			if not self.meta.get_field("sales_team"):
 				party_details.pop("sales_team")
 			self.update_if_missing(party_details)
 
 		elif lead:
 			from erpnext.crm.doctype.lead.lead import get_lead_details
-			self.update_if_missing(get_lead_details(lead,
-				posting_date=self.get('transaction_date') or self.get('posting_date'),
-				company=self.company))
 
-		if self.get('taxes_and_charges') and not self.get('taxes') and not for_validate:
-			taxes = get_taxes_and_charges('Sales Taxes and Charges Template', self.taxes_and_charges)
+			self.update_if_missing(
+				get_lead_details(
+					lead,
+					posting_date=self.get("transaction_date") or self.get("posting_date"),
+					company=self.company,
+				)
+			)
+
+		if self.get("taxes_and_charges") and not self.get("taxes") and not for_validate:
+			taxes = get_taxes_and_charges("Sales Taxes and Charges Template", self.taxes_and_charges)
 			for tax in taxes:
-				self.append('taxes', tax)
+				self.append("taxes", tax)
 
 	def set_price_list_and_item_details(self, for_validate=False):
 		self.set_price_list_currency("Selling")
@@ -98,12 +111,15 @@
 	def remove_shipping_charge(self):
 		if self.shipping_rule:
 			shipping_rule = frappe.get_doc("Shipping Rule", self.shipping_rule)
-			existing_shipping_charge = self.get("taxes", {
-				"doctype": "Sales Taxes and Charges",
-				"charge_type": "Actual",
-				"account_head": shipping_rule.account,
-				"cost_center": shipping_rule.cost_center
-			})
+			existing_shipping_charge = self.get(
+				"taxes",
+				{
+					"doctype": "Sales Taxes and Charges",
+					"charge_type": "Actual",
+					"account_head": shipping_rule.account,
+					"cost_center": shipping_rule.cost_center,
+				},
+			)
 			if existing_shipping_charge:
 				self.get("taxes").remove(existing_shipping_charge[-1])
 				self.calculate_taxes_and_totals()
@@ -112,8 +128,9 @@
 		from frappe.utils import money_in_words
 
 		if self.meta.get_field("base_in_words"):
-			base_amount = abs(self.base_grand_total
-				if self.is_rounded_total_disabled() else self.base_rounded_total)
+			base_amount = abs(
+				self.base_grand_total if self.is_rounded_total_disabled() else self.base_rounded_total
+			)
 			self.base_in_words = money_in_words(base_amount, self.company_currency)
 
 		if self.meta.get_field("in_words"):
@@ -124,15 +141,15 @@
 		if not self.meta.get_field("commission_rate"):
 			return
 
-		self.round_floats_in(
-			self, ("amount_eligible_for_commission", "commission_rate")
-		)
+		self.round_floats_in(self, ("amount_eligible_for_commission", "commission_rate"))
 
 		if not (0 <= self.commission_rate <= 100.0):
-			throw("{} {}".format(
-				_(self.meta.get_label("commission_rate")),
-				_("must be between 0 and 100"),
-			))
+			throw(
+				"{} {}".format(
+					_(self.meta.get_label("commission_rate")),
+					_("must be between 0 and 100"),
+				)
+			)
 
 		self.amount_eligible_for_commission = sum(
 			item.base_net_amount for item in self.items if item.grant_commission
@@ -140,7 +157,7 @@
 
 		self.total_commission = flt(
 			self.amount_eligible_for_commission * self.commission_rate / 100.0,
-			self.precision("total_commission")
+			self.precision("total_commission"),
 		)
 
 	def calculate_contribution(self):
@@ -154,12 +171,14 @@
 
 			sales_person.allocated_amount = flt(
 				self.amount_eligible_for_commission * sales_person.allocated_percentage / 100.0,
-				self.precision("allocated_amount", sales_person))
+				self.precision("allocated_amount", sales_person),
+			)
 
 			if sales_person.commission_rate:
 				sales_person.incentives = flt(
 					sales_person.allocated_amount * flt(sales_person.commission_rate) / 100.0,
-					self.precision("incentives", sales_person))
+					self.precision("incentives", sales_person),
+				)
 
 			total += sales_person.allocated_percentage
 
@@ -183,25 +202,29 @@
 
 	def validate_selling_price(self):
 		def throw_message(idx, item_name, rate, ref_rate_field):
-			throw(_("""Row #{0}: Selling rate for item {1} is lower than its {2}.
+			throw(
+				_(
+					"""Row #{0}: Selling rate for item {1} is lower than its {2}.
 					Selling {3} should be atleast {4}.<br><br>Alternatively,
 					you can disable selling price validation in {5} to bypass
-					this validation.""").format(
-				idx,
-				bold(item_name),
-				bold(ref_rate_field),
-				bold("net rate"),
-				bold(rate),
-				get_link_to_form("Selling Settings", "Selling Settings"),
-			), title=_("Invalid Selling Price"))
+					this validation."""
+				).format(
+					idx,
+					bold(item_name),
+					bold(ref_rate_field),
+					bold("net rate"),
+					bold(rate),
+					get_link_to_form("Selling Settings", "Selling Settings"),
+				),
+				title=_("Invalid Selling Price"),
+			)
 
-		if (
-			self.get("is_return")
-			or not frappe.db.get_single_value("Selling Settings", "validate_selling_price")
+		if self.get("is_return") or not frappe.db.get_single_value(
+			"Selling Settings", "validate_selling_price"
 		):
 			return
 
-		is_internal_customer = self.get('is_internal_customer')
+		is_internal_customer = self.get("is_internal_customer")
 		valuation_rate_map = {}
 
 		for item in self.items:
@@ -212,17 +235,10 @@
 				"Item", item.item_code, ("last_purchase_rate", "is_stock_item")
 			)
 
-			last_purchase_rate_in_sales_uom = (
-				last_purchase_rate * (item.conversion_factor or 1)
-			)
+			last_purchase_rate_in_sales_uom = last_purchase_rate * (item.conversion_factor or 1)
 
 			if flt(item.base_net_rate) < flt(last_purchase_rate_in_sales_uom):
-				throw_message(
-					item.idx,
-					item.item_name,
-					last_purchase_rate_in_sales_uom,
-					"last purchase rate"
-				)
+				throw_message(item.idx, item.item_name, last_purchase_rate_in_sales_uom, "last purchase rate")
 
 			if is_internal_customer or not is_stock_item:
 				continue
@@ -238,7 +254,8 @@
 			for valuation_rate in valuation_rate_map
 		)
 
-		valuation_rates = frappe.db.sql(f"""
+		valuation_rates = frappe.db.sql(
+			f"""
 			select
 				item_code, warehouse, valuation_rate
 			from
@@ -246,7 +263,9 @@
 			where
 				({" or ".join(or_conditions)})
 				and valuation_rate > 0
-		""", as_dict=True)
+		""",
+			as_dict=True,
+		)
 
 		for rate in valuation_rates:
 			valuation_rate_map[(rate.item_code, rate.warehouse)] = rate.valuation_rate
@@ -255,24 +274,15 @@
 			if not item.item_code or item.is_free_item:
 				continue
 
-			last_valuation_rate = valuation_rate_map.get(
-				(item.item_code, item.warehouse)
-			)
+			last_valuation_rate = valuation_rate_map.get((item.item_code, item.warehouse))
 
 			if not last_valuation_rate:
 				continue
 
-			last_valuation_rate_in_sales_uom = (
-				last_valuation_rate * (item.conversion_factor or 1)
-			)
+			last_valuation_rate_in_sales_uom = last_valuation_rate * (item.conversion_factor or 1)
 
 			if flt(item.base_net_rate) < flt(last_valuation_rate_in_sales_uom):
-				throw_message(
-					item.idx,
-					item.item_name,
-					last_valuation_rate_in_sales_uom,
-					"valuation rate"
-				)
+				throw_message(item.idx, item.item_name, last_valuation_rate_in_sales_uom, "valuation rate")
 
 	def get_item_list(self):
 		il = []
@@ -284,68 +294,90 @@
 				for p in self.get("packed_items"):
 					if p.parent_detail_docname == d.name and p.parent_item == d.item_code:
 						# the packing details table's qty is already multiplied with parent's qty
-						il.append(frappe._dict({
-							'warehouse': p.warehouse or d.warehouse,
-							'item_code': p.item_code,
-							'qty': flt(p.qty),
-							'uom': p.uom,
-							'batch_no': cstr(p.batch_no).strip(),
-							'serial_no': cstr(p.serial_no).strip(),
-							'name': d.name,
-							'target_warehouse': p.target_warehouse,
-							'company': self.company,
-							'voucher_type': self.doctype,
-							'allow_zero_valuation': d.allow_zero_valuation_rate,
-							'sales_invoice_item': d.get("sales_invoice_item"),
-							'dn_detail': d.get("dn_detail"),
-							'incoming_rate': p.get("incoming_rate")
-						}))
+						il.append(
+							frappe._dict(
+								{
+									"warehouse": p.warehouse or d.warehouse,
+									"item_code": p.item_code,
+									"qty": flt(p.qty),
+									"uom": p.uom,
+									"batch_no": cstr(p.batch_no).strip(),
+									"serial_no": cstr(p.serial_no).strip(),
+									"name": d.name,
+									"target_warehouse": p.target_warehouse,
+									"company": self.company,
+									"voucher_type": self.doctype,
+									"allow_zero_valuation": d.allow_zero_valuation_rate,
+									"sales_invoice_item": d.get("sales_invoice_item"),
+									"dn_detail": d.get("dn_detail"),
+									"incoming_rate": p.get("incoming_rate"),
+								}
+							)
+						)
 			else:
-				il.append(frappe._dict({
-					'warehouse': d.warehouse,
-					'item_code': d.item_code,
-					'qty': d.stock_qty,
-					'uom': d.uom,
-					'stock_uom': d.stock_uom,
-					'conversion_factor': d.conversion_factor,
-					'batch_no': cstr(d.get("batch_no")).strip(),
-					'serial_no': cstr(d.get("serial_no")).strip(),
-					'name': d.name,
-					'target_warehouse': d.target_warehouse,
-					'company': self.company,
-					'voucher_type': self.doctype,
-					'allow_zero_valuation': d.allow_zero_valuation_rate,
-					'sales_invoice_item': d.get("sales_invoice_item"),
-					'dn_detail': d.get("dn_detail"),
-					'incoming_rate': d.get("incoming_rate")
-				}))
+				il.append(
+					frappe._dict(
+						{
+							"warehouse": d.warehouse,
+							"item_code": d.item_code,
+							"qty": d.stock_qty,
+							"uom": d.uom,
+							"stock_uom": d.stock_uom,
+							"conversion_factor": d.conversion_factor,
+							"batch_no": cstr(d.get("batch_no")).strip(),
+							"serial_no": cstr(d.get("serial_no")).strip(),
+							"name": d.name,
+							"target_warehouse": d.target_warehouse,
+							"company": self.company,
+							"voucher_type": self.doctype,
+							"allow_zero_valuation": d.allow_zero_valuation_rate,
+							"sales_invoice_item": d.get("sales_invoice_item"),
+							"dn_detail": d.get("dn_detail"),
+							"incoming_rate": d.get("incoming_rate"),
+						}
+					)
+				)
 		return il
 
 	def has_product_bundle(self, item_code):
-		return frappe.db.sql("""select name from `tabProduct Bundle`
-			where new_item_code=%s and docstatus != 2""", item_code)
+		return frappe.db.sql(
+			"""select name from `tabProduct Bundle`
+			where new_item_code=%s and docstatus != 2""",
+			item_code,
+		)
 
 	def get_already_delivered_qty(self, current_docname, so, so_detail):
-		delivered_via_dn = frappe.db.sql("""select sum(qty) from `tabDelivery Note Item`
+		delivered_via_dn = frappe.db.sql(
+			"""select sum(qty) from `tabDelivery Note Item`
 			where so_detail = %s and docstatus = 1
 			and against_sales_order = %s
-			and parent != %s""", (so_detail, so, current_docname))
+			and parent != %s""",
+			(so_detail, so, current_docname),
+		)
 
-		delivered_via_si = frappe.db.sql("""select sum(si_item.qty)
+		delivered_via_si = frappe.db.sql(
+			"""select sum(si_item.qty)
 			from `tabSales Invoice Item` si_item, `tabSales Invoice` si
 			where si_item.parent = si.name and si.update_stock = 1
 			and si_item.so_detail = %s and si.docstatus = 1
 			and si_item.sales_order = %s
-			and si.name != %s""", (so_detail, so, current_docname))
+			and si.name != %s""",
+			(so_detail, so, current_docname),
+		)
 
-		total_delivered_qty = (flt(delivered_via_dn[0][0]) if delivered_via_dn else 0) \
-			+ (flt(delivered_via_si[0][0]) if delivered_via_si else 0)
+		total_delivered_qty = (flt(delivered_via_dn[0][0]) if delivered_via_dn else 0) + (
+			flt(delivered_via_si[0][0]) if delivered_via_si else 0
+		)
 
 		return total_delivered_qty
 
 	def get_so_qty_and_warehouse(self, so_detail):
-		so_item = frappe.db.sql("""select qty, warehouse from `tabSales Order Item`
-			where name = %s and docstatus = 1""", so_detail, as_dict=1)
+		so_item = frappe.db.sql(
+			"""select qty, warehouse from `tabSales Order Item`
+			where name = %s and docstatus = 1""",
+			so_detail,
+			as_dict=1,
+		)
 		so_qty = so_item and flt(so_item[0]["qty"]) or 0.0
 		so_warehouse = so_item and so_item[0]["warehouse"] or ""
 		return so_qty, so_warehouse
@@ -371,8 +403,9 @@
 				sales_order = frappe.get_doc("Sales Order", so)
 
 				if sales_order.status in ["Closed", "Cancelled"]:
-					frappe.throw(_("{0} {1} is cancelled or closed").format(_("Sales Order"), so),
-						frappe.InvalidStatusError)
+					frappe.throw(
+						_("{0} {1} is cancelled or closed").format(_("Sales Order"), so), frappe.InvalidStatusError
+					)
 
 				sales_order.update_reserved_qty(so_item_rows)
 
@@ -384,43 +417,53 @@
 		for d in items:
 			if not self.get("return_against"):
 				# Get incoming rate based on original item cost based on valuation method
-				qty = flt(d.get('stock_qty') or d.get('actual_qty'))
+				qty = flt(d.get("stock_qty") or d.get("actual_qty"))
 
 				if not (self.get("is_return") and d.incoming_rate):
-					d.incoming_rate = get_incoming_rate({
-						"item_code": d.item_code,
-						"warehouse": d.warehouse,
-						"posting_date": self.get('posting_date') or self.get('transaction_date'),
-						"posting_time": self.get('posting_time') or nowtime(),
-						"qty": qty if cint(self.get("is_return")) else (-1 * qty),
-						"serial_no": d.get('serial_no'),
-						"batch_no": d.get("batch_no"),
-						"company": self.company,
-						"voucher_type": self.doctype,
-						"voucher_no": self.name,
-						"allow_zero_valuation": d.get("allow_zero_valuation")
-					}, raise_error_if_no_rate=False)
+					d.incoming_rate = get_incoming_rate(
+						{
+							"item_code": d.item_code,
+							"warehouse": d.warehouse,
+							"posting_date": self.get("posting_date") or self.get("transaction_date"),
+							"posting_time": self.get("posting_time") or nowtime(),
+							"qty": qty if cint(self.get("is_return")) else (-1 * qty),
+							"serial_no": d.get("serial_no"),
+							"batch_no": d.get("batch_no"),
+							"company": self.company,
+							"voucher_type": self.doctype,
+							"voucher_no": self.name,
+							"allow_zero_valuation": d.get("allow_zero_valuation"),
+						},
+						raise_error_if_no_rate=False,
+					)
 
 				# For internal transfers use incoming rate as the valuation rate
 				if self.is_internal_transfer():
 					if d.doctype == "Packed Item":
-						incoming_rate = flt(d.incoming_rate * d.conversion_factor, d.precision('incoming_rate'))
+						incoming_rate = flt(d.incoming_rate * d.conversion_factor, d.precision("incoming_rate"))
 						if d.incoming_rate != incoming_rate:
 							d.incoming_rate = incoming_rate
 					else:
-						rate = flt(d.incoming_rate * d.conversion_factor, d.precision('rate'))
+						rate = flt(d.incoming_rate * d.conversion_factor, d.precision("rate"))
 						if d.rate != rate:
 							d.rate = rate
+							frappe.msgprint(
+								_(
+									"Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer"
+								).format(d.idx),
+								alert=1,
+							)
 
-						d.discount_percentage = 0
-						d.discount_amount = 0
-						frappe.msgprint(_("Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer")
-							.format(d.idx), alert=1)
+						d.discount_percentage = 0.0
+						d.discount_amount = 0.0
+						d.margin_rate_or_amount = 0.0
 
 			elif self.get("return_against"):
 				# Get incoming rate of return entry from reference document
 				# based on original item cost as per valuation method
-				d.incoming_rate = get_rate_for_return(self.doctype, self.name, d.item_code, self.return_against, item_row=d)
+				d.incoming_rate = get_rate_for_return(
+					self.doctype, self.name, d.item_code, self.return_against, item_row=d
+				)
 
 	def update_stock_ledger(self):
 		self.update_reserved_qty()
@@ -429,63 +472,66 @@
 		# Loop over items and packed items table
 		for d in self.get_item_list():
 			if frappe.get_cached_value("Item", d.item_code, "is_stock_item") == 1 and flt(d.qty):
-				if flt(d.conversion_factor)==0.0:
-					d.conversion_factor = get_conversion_factor(d.item_code, d.uom).get("conversion_factor") or 1.0
+				if flt(d.conversion_factor) == 0.0:
+					d.conversion_factor = (
+						get_conversion_factor(d.item_code, d.uom).get("conversion_factor") or 1.0
+					)
 
 				# On cancellation or return entry submission, make stock ledger entry for
 				# target warehouse first, to update serial no values properly
 
-				if d.warehouse and ((not cint(self.is_return) and self.docstatus==1)
-					or (cint(self.is_return) and self.docstatus==2)):
-						sl_entries.append(self.get_sle_for_source_warehouse(d))
+				if d.warehouse and (
+					(not cint(self.is_return) and self.docstatus == 1)
+					or (cint(self.is_return) and self.docstatus == 2)
+				):
+					sl_entries.append(self.get_sle_for_source_warehouse(d))
 
 				if d.target_warehouse:
 					sl_entries.append(self.get_sle_for_target_warehouse(d))
 
-				if d.warehouse and ((not cint(self.is_return) and self.docstatus==2)
-					or (cint(self.is_return) and self.docstatus==1)):
-						sl_entries.append(self.get_sle_for_source_warehouse(d))
+				if d.warehouse and (
+					(not cint(self.is_return) and self.docstatus == 2)
+					or (cint(self.is_return) and self.docstatus == 1)
+				):
+					sl_entries.append(self.get_sle_for_source_warehouse(d))
 
 		self.make_sl_entries(sl_entries)
 
 	def get_sle_for_source_warehouse(self, item_row):
-		sle = self.get_sl_entries(item_row, {
-			"actual_qty": -1*flt(item_row.qty),
-			"incoming_rate": item_row.incoming_rate,
-			"recalculate_rate": cint(self.is_return)
-		})
+		sle = self.get_sl_entries(
+			item_row,
+			{
+				"actual_qty": -1 * flt(item_row.qty),
+				"incoming_rate": item_row.incoming_rate,
+				"recalculate_rate": cint(self.is_return),
+			},
+		)
 		if item_row.target_warehouse and not cint(self.is_return):
 			sle.dependant_sle_voucher_detail_no = item_row.name
 
 		return sle
 
 	def get_sle_for_target_warehouse(self, item_row):
-		sle = self.get_sl_entries(item_row, {
-			"actual_qty": flt(item_row.qty),
-			"warehouse": item_row.target_warehouse
-		})
+		sle = self.get_sl_entries(
+			item_row, {"actual_qty": flt(item_row.qty), "warehouse": item_row.target_warehouse}
+		)
 
 		if self.docstatus == 1:
 			if not cint(self.is_return):
-				sle.update({
-					"incoming_rate": item_row.incoming_rate,
-					"recalculate_rate": 1
-				})
+				sle.update({"incoming_rate": item_row.incoming_rate, "recalculate_rate": 1})
 			else:
-				sle.update({
-					"outgoing_rate": item_row.incoming_rate
-				})
+				sle.update({"outgoing_rate": item_row.incoming_rate})
 				if item_row.warehouse:
 					sle.dependant_sle_voucher_detail_no = item_row.name
 
 		return sle
 
 	def set_po_nos(self, for_validate=False):
-		if self.doctype == 'Sales Invoice' and hasattr(self, "items"):
+		if self.doctype == "Sales Invoice" and hasattr(self, "items"):
 			if for_validate and self.po_no:
 				return
 			self.set_pos_for_sales_invoice()
-		if self.doctype == 'Delivery Note' and hasattr(self, "items"):
+		if self.doctype == "Delivery Note" and hasattr(self, "items"):
 			if for_validate and self.po_no:
 				return
 			self.set_pos_for_delivery_note()
@@ -494,34 +540,39 @@
 		po_nos = []
 		if self.po_no:
 			po_nos.append(self.po_no)
-		self.get_po_nos('Sales Order', 'sales_order', po_nos)
-		self.get_po_nos('Delivery Note', 'delivery_note', po_nos)
-		self.po_no = ', '.join(list(set(x.strip() for x in ','.join(po_nos).split(','))))
+		self.get_po_nos("Sales Order", "sales_order", po_nos)
+		self.get_po_nos("Delivery Note", "delivery_note", po_nos)
+		self.po_no = ", ".join(list(set(x.strip() for x in ",".join(po_nos).split(","))))
 
 	def set_pos_for_delivery_note(self):
 		po_nos = []
 		if self.po_no:
 			po_nos.append(self.po_no)
-		self.get_po_nos('Sales Order', 'against_sales_order', po_nos)
-		self.get_po_nos('Sales Invoice', 'against_sales_invoice', po_nos)
-		self.po_no = ', '.join(list(set(x.strip() for x in ','.join(po_nos).split(','))))
+		self.get_po_nos("Sales Order", "against_sales_order", po_nos)
+		self.get_po_nos("Sales Invoice", "against_sales_invoice", po_nos)
+		self.po_no = ", ".join(list(set(x.strip() for x in ",".join(po_nos).split(","))))
 
 	def get_po_nos(self, ref_doctype, ref_fieldname, po_nos):
 		doc_list = list(set(d.get(ref_fieldname) for d in self.items if d.get(ref_fieldname)))
 		if doc_list:
-			po_nos += [d.po_no for d in frappe.get_all(ref_doctype, 'po_no', filters = {'name': ('in', doc_list)}) if d.get('po_no')]
+			po_nos += [
+				d.po_no
+				for d in frappe.get_all(ref_doctype, "po_no", filters={"name": ("in", doc_list)})
+				if d.get("po_no")
+			]
 
 	def set_gross_profit(self):
 		if self.doctype in ["Sales Order", "Quotation"]:
 			for item in self.items:
-				item.gross_profit = flt(((item.base_rate - item.valuation_rate) * item.stock_qty), self.precision("amount", item))
-
+				item.gross_profit = flt(
+					((item.base_rate - item.valuation_rate) * item.stock_qty), self.precision("amount", item)
+				)
 
 	def set_customer_address(self):
 		address_dict = {
-			'customer_address': 'address_display',
-			'shipping_address_name': 'shipping_address',
-			'company_address': 'company_address_display'
+			"customer_address": "address_display",
+			"shipping_address_name": "shipping_address",
+			"company_address": "company_address_display",
 		}
 
 		for address_field, address_display_field in address_dict.items():
@@ -537,15 +588,31 @@
 		if self.doctype == "POS Invoice":
 			return
 
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if self.doctype == "Sales Invoice":
-				stock_items = [d.item_code, d.description, d.warehouse, d.sales_order or d.delivery_note, d.batch_no or '']
+				stock_items = [
+					d.item_code,
+					d.description,
+					d.warehouse,
+					d.sales_order or d.delivery_note,
+					d.batch_no or "",
+				]
 				non_stock_items = [d.item_code, d.description, d.sales_order or d.delivery_note]
 			elif self.doctype == "Delivery Note":
-				stock_items = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or '']
-				non_stock_items = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice]
+				stock_items = [
+					d.item_code,
+					d.description,
+					d.warehouse,
+					d.against_sales_order or d.against_sales_invoice,
+					d.batch_no or "",
+				]
+				non_stock_items = [
+					d.item_code,
+					d.description,
+					d.against_sales_order or d.against_sales_invoice,
+				]
 			elif self.doctype in ["Sales Order", "Quotation"]:
-				stock_items = [d.item_code, d.description, d.warehouse, '']
+				stock_items = [d.item_code, d.description, d.warehouse, ""]
 				non_stock_items = [d.item_code, d.description]
 
 			if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1:
@@ -553,7 +620,7 @@
 				duplicate_items_msg += "<br><br>"
 				duplicate_items_msg += _("Please enable {} in {} to allow same item in multiple rows").format(
 					frappe.bold("Allow Item to Be Added Multiple Times in a Transaction"),
-					get_link_to_form("Selling Settings", "Selling Settings")
+					get_link_to_form("Selling Settings", "Selling Settings"),
 				)
 				if stock_items in check_list:
 					frappe.throw(duplicate_items_msg)
@@ -571,22 +638,26 @@
 		for d in items:
 			if d.get("target_warehouse") and d.get("warehouse") == d.get("target_warehouse"):
 				warehouse = frappe.bold(d.get("target_warehouse"))
-				frappe.throw(_("Row {0}: Delivery Warehouse ({1}) and Customer Warehouse ({2}) can not be same")
-					.format(d.idx, warehouse, warehouse))
+				frappe.throw(
+					_("Row {0}: Delivery Warehouse ({1}) and Customer Warehouse ({2}) can not be same").format(
+						d.idx, warehouse, warehouse
+					)
+				)
 
 		if not self.get("is_internal_customer") and any(d.get("target_warehouse") for d in items):
 			msg = _("Target Warehouse is set for some items but the customer is not an internal customer.")
 			msg += " " + _("This {} will be treated as material transfer.").format(_(self.doctype))
 			frappe.msgprint(msg, title="Internal Transfer", alert=True)
 
-
 	def validate_items(self):
 		# validate items to see if they have is_sales_item enabled
 		from erpnext.controllers.buying_controller import validate_item_type
+
 		validate_item_type(self, "is_sales_item", "sales")
 
+
 def set_default_income_account_for_item(obj):
 	for d in obj.get("items"):
 		if d.item_code:
 			if getattr(d, "income_account", None):
-				set_item_default(d.item_code, obj.company, 'income_account', d.income_account)
+				set_item_default(d.item_code, obj.company, "income_account", d.income_account)
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index affde4a..197d2ba 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -8,12 +8,15 @@
 from frappe.utils import comma_or, flt, getdate, now, nowdate
 
 
-class OverAllowanceError(frappe.ValidationError): pass
+class OverAllowanceError(frappe.ValidationError):
+	pass
+
 
 def validate_status(status, options):
 	if status not in options:
 		frappe.throw(_("Status must be one of {0}").format(comma_or(options)))
 
+
 status_map = {
 	"Lead": [
 		["Lost Quotation", "has_lost_quotation"],
@@ -26,31 +29,53 @@
 		["Lost", "has_lost_quotation"],
 		["Quotation", "has_active_quotation"],
 		["Converted", "has_ordered_quotation"],
-		["Closed", "eval:self.status=='Closed'"]
+		["Closed", "eval:self.status=='Closed'"],
 	],
 	"Quotation": [
 		["Draft", None],
 		["Open", "eval:self.docstatus==1"],
 		["Lost", "eval:self.status=='Lost'"],
-		["Ordered", "has_sales_order"],
+		["Partially Ordered", "is_partially_ordered"],
+		["Ordered", "is_fully_ordered"],
 		["Cancelled", "eval:self.docstatus==2"],
 	],
 	"Sales Order": [
 		["Draft", None],
-		["To Deliver and Bill", "eval:self.per_delivered < 100 and self.per_billed < 100 and self.docstatus == 1"],
-		["To Bill", "eval:(self.per_delivered == 100 or self.skip_delivery_note) and self.per_billed < 100 and self.docstatus == 1"],
-		["To Deliver", "eval:self.per_delivered < 100 and self.per_billed == 100 and self.docstatus == 1 and not self.skip_delivery_note"],
-		["Completed", "eval:(self.per_delivered == 100 or self.skip_delivery_note) and self.per_billed == 100 and self.docstatus == 1"],
+		[
+			"To Deliver and Bill",
+			"eval:self.per_delivered < 100 and self.per_billed < 100 and self.docstatus == 1",
+		],
+		[
+			"To Bill",
+			"eval:(self.per_delivered == 100 or self.skip_delivery_note) and self.per_billed < 100 and self.docstatus == 1",
+		],
+		[
+			"To Deliver",
+			"eval:self.per_delivered < 100 and self.per_billed == 100 and self.docstatus == 1 and not self.skip_delivery_note",
+		],
+		[
+			"Completed",
+			"eval:(self.per_delivered == 100 or self.skip_delivery_note) and self.per_billed == 100 and self.docstatus == 1",
+		],
 		["Cancelled", "eval:self.docstatus==2"],
 		["Closed", "eval:self.status=='Closed'"],
 		["On Hold", "eval:self.status=='On Hold'"],
 	],
 	"Purchase Order": [
 		["Draft", None],
-		["To Receive and Bill", "eval:self.per_received < 100 and self.per_billed < 100 and self.docstatus == 1"],
+		[
+			"To Receive and Bill",
+			"eval:self.per_received < 100 and self.per_billed < 100 and self.docstatus == 1",
+		],
 		["To Bill", "eval:self.per_received >= 100 and self.per_billed < 100 and self.docstatus == 1"],
-		["To Receive", "eval:self.per_received < 100 and self.per_billed == 100 and self.docstatus == 1"],
-		["Completed", "eval:self.per_received >= 100 and self.per_billed == 100 and self.docstatus == 1"],
+		[
+			"To Receive",
+			"eval:self.per_received < 100 and self.per_billed == 100 and self.docstatus == 1",
+		],
+		[
+			"Completed",
+			"eval:self.per_received >= 100 and self.per_billed == 100 and self.docstatus == 1",
+		],
 		["Delivered", "eval:self.status=='Delivered'"],
 		["Cancelled", "eval:self.docstatus==2"],
 		["On Hold", "eval:self.status=='On Hold'"],
@@ -77,18 +102,39 @@
 		["Stopped", "eval:self.status == 'Stopped'"],
 		["Cancelled", "eval:self.docstatus == 2"],
 		["Pending", "eval:self.status != 'Stopped' and self.per_ordered == 0 and self.docstatus == 1"],
-		["Ordered", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"],
-		["Transferred", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Transfer'"],
-		["Issued", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Issue'"],
-		["Received", "eval:self.status != 'Stopped' and self.per_received == 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"],
-		["Partially Received", "eval:self.status != 'Stopped' and self.per_received > 0 and self.per_received < 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"],
-		["Partially Ordered", "eval:self.status != 'Stopped' and self.per_ordered < 100 and self.per_ordered > 0 and self.docstatus == 1"],
-		["Manufactured", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Manufacture'"]
+		[
+			"Ordered",
+			"eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'",
+		],
+		[
+			"Transferred",
+			"eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Transfer'",
+		],
+		[
+			"Issued",
+			"eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Issue'",
+		],
+		[
+			"Received",
+			"eval:self.status != 'Stopped' and self.per_received == 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'",
+		],
+		[
+			"Partially Received",
+			"eval:self.status != 'Stopped' and self.per_received > 0 and self.per_received < 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'",
+		],
+		[
+			"Partially Ordered",
+			"eval:self.status != 'Stopped' and self.per_ordered < 100 and self.per_ordered > 0 and self.docstatus == 1",
+		],
+		[
+			"Manufactured",
+			"eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Manufacture'",
+		],
 	],
 	"Bank Transaction": [
 		["Unreconciled", "eval:self.docstatus == 1 and self.unallocated_amount>0"],
 		["Reconciled", "eval:self.docstatus == 1 and self.unallocated_amount<=0"],
-		["Cancelled", "eval:self.docstatus == 2"]
+		["Cancelled", "eval:self.docstatus == 2"],
 	],
 	"POS Opening Entry": [
 		["Draft", None],
@@ -106,15 +152,16 @@
 	"Transaction Deletion Record": [
 		["Draft", None],
 		["Completed", "eval:self.docstatus == 1"],
-	]
+	],
 }
 
+
 class StatusUpdater(Document):
 	"""
-		Updates the status of the calling records
-		Delivery Note: Update Delivered Qty, Update Percent and Validate over delivery
-		Sales Invoice: Update Billed Amt, Update Percent and Validate over billing
-		Installation Note: Update Installed Qty, Update Percent Qty and Validate over installation
+	Updates the status of the calling records
+	Delivery Note: Update Delivered Qty, Update Percent and Validate over delivery
+	Sales Invoice: Update Billed Amt, Update Percent and Validate over billing
+	Installation Note: Update Installed Qty, Update Percent Qty and Validate over installation
 	"""
 
 	def update_prevdoc_status(self):
@@ -123,8 +170,8 @@
 
 	def set_status(self, update=False, status=None, update_modified=True):
 		if self.is_new():
-			if self.get('amended_from'):
-				self.status = 'Draft'
+			if self.get("amended_from"):
+				self.status = "Draft"
 			return
 
 		if self.doctype in status_map:
@@ -139,20 +186,33 @@
 					self.status = s[0]
 					break
 				elif s[1].startswith("eval:"):
-					if frappe.safe_eval(s[1][5:], None, { "self": self.as_dict(), "getdate": getdate,
-							"nowdate": nowdate, "get_value": frappe.db.get_value }):
+					if frappe.safe_eval(
+						s[1][5:],
+						None,
+						{
+							"self": self.as_dict(),
+							"getdate": getdate,
+							"nowdate": nowdate,
+							"get_value": frappe.db.get_value,
+						},
+					):
 						self.status = s[0]
 						break
 				elif getattr(self, s[1])():
 					self.status = s[0]
 					break
 
-			if self.status != _status and self.status not in ("Cancelled", "Partially Ordered",
-																"Ordered", "Issued", "Transferred"):
+			if self.status != _status and self.status not in (
+				"Cancelled",
+				"Partially Ordered",
+				"Ordered",
+				"Issued",
+				"Transferred",
+			):
 				self.add_comment("Label", _(self.status))
 
 			if update:
-				self.db_set('status', self.status, update_modified = update_modified)
+				self.db_set("status", self.status, update_modified=update_modified)
 
 	def validate_qty(self):
 		"""Validates qty at row level"""
@@ -167,57 +227,78 @@
 
 			# get unique transactions to update
 			for d in self.get_all_children():
-				if hasattr(d, 'qty') and d.qty < 0 and not self.get('is_return'):
+				if hasattr(d, "qty") and d.qty < 0 and not self.get("is_return"):
 					frappe.throw(_("For an item {0}, quantity must be positive number").format(d.item_code))
 
-				if hasattr(d, 'qty') and d.qty > 0 and self.get('is_return'):
+				if hasattr(d, "qty") and d.qty > 0 and self.get("is_return"):
 					frappe.throw(_("For an item {0}, quantity must be negative number").format(d.item_code))
 
-				if d.doctype == args['source_dt'] and d.get(args["join_field"]):
-					args['name'] = d.get(args['join_field'])
+				if d.doctype == args["source_dt"] and d.get(args["join_field"]):
+					args["name"] = d.get(args["join_field"])
 
 					# get all qty where qty > target_field
-					item = frappe.db.sql("""select item_code, `{target_ref_field}`,
+					item = frappe.db.sql(
+						"""select item_code, `{target_ref_field}`,
 						`{target_field}`, parenttype, parent from `tab{target_dt}`
 						where `{target_ref_field}` < `{target_field}`
-						and name=%s and docstatus=1""".format(**args),
-						args['name'], as_dict=1)
+						and name=%s and docstatus=1""".format(
+							**args
+						),
+						args["name"],
+						as_dict=1,
+					)
 					if item:
 						item = item[0]
-						item['idx'] = d.idx
-						item['target_ref_field'] = args['target_ref_field'].replace('_', ' ')
+						item["idx"] = d.idx
+						item["target_ref_field"] = args["target_ref_field"].replace("_", " ")
 
 						# if not item[args['target_ref_field']]:
 						# 	msgprint(_("Note: System will not check over-delivery and over-booking for Item {0} as quantity or amount is 0").format(item.item_code))
-						if args.get('no_allowance'):
-							item['reduce_by'] = item[args['target_field']] - item[args['target_ref_field']]
-							if item['reduce_by'] > .01:
+						if args.get("no_allowance"):
+							item["reduce_by"] = item[args["target_field"]] - item[args["target_ref_field"]]
+							if item["reduce_by"] > 0.01:
 								self.limits_crossed_error(args, item, "qty")
 
-						elif item[args['target_ref_field']]:
+						elif item[args["target_ref_field"]]:
 							self.check_overflow_with_allowance(item, args)
 
 	def check_overflow_with_allowance(self, item, args):
 		"""
-			Checks if there is overflow condering a relaxation allowance
+		Checks if there is overflow condering a relaxation allowance
 		"""
-		qty_or_amount = "qty" if "qty" in args['target_ref_field'] else "amount"
+		qty_or_amount = "qty" if "qty" in args["target_ref_field"] else "amount"
 
 		# check if overflow is within allowance
-		allowance, self.item_allowance, self.global_qty_allowance, self.global_amount_allowance = \
-			get_allowance_for(item['item_code'], self.item_allowance,
-				self.global_qty_allowance, self.global_amount_allowance, qty_or_amount)
+		(
+			allowance,
+			self.item_allowance,
+			self.global_qty_allowance,
+			self.global_amount_allowance,
+		) = get_allowance_for(
+			item["item_code"],
+			self.item_allowance,
+			self.global_qty_allowance,
+			self.global_amount_allowance,
+			qty_or_amount,
+		)
 
-		role_allowed_to_over_deliver_receive = frappe.db.get_single_value('Stock Settings', 'role_allowed_to_over_deliver_receive')
-		role_allowed_to_over_bill = frappe.db.get_single_value('Accounts Settings', 'role_allowed_to_over_bill')
-		role = role_allowed_to_over_deliver_receive if qty_or_amount == 'qty' else role_allowed_to_over_bill
+		role_allowed_to_over_deliver_receive = frappe.db.get_single_value(
+			"Stock Settings", "role_allowed_to_over_deliver_receive"
+		)
+		role_allowed_to_over_bill = frappe.db.get_single_value(
+			"Accounts Settings", "role_allowed_to_over_bill"
+		)
+		role = (
+			role_allowed_to_over_deliver_receive if qty_or_amount == "qty" else role_allowed_to_over_bill
+		)
 
-		overflow_percent = ((item[args['target_field']] - item[args['target_ref_field']]) /
-			item[args['target_ref_field']]) * 100
+		overflow_percent = (
+			(item[args["target_field"]] - item[args["target_ref_field"]]) / item[args["target_ref_field"]]
+		) * 100
 
 		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']
+			item["max_allowed"] = flt(item[args["target_ref_field"]] * (100 + allowance) / 100)
+			item["reduce_by"] = item[args["target_field"]] - item["max_allowed"]
 
 			if role not in frappe.get_roles():
 				self.limits_crossed_error(args, item, qty_or_amount)
@@ -225,45 +306,55 @@
 				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'''
+		"""Raise exception for limits crossed"""
 		if qty_or_amount == "qty":
-			action_msg = _('To allow over receipt / delivery, update "Over Receipt/Delivery Allowance" in Stock Settings or the Item.')
+			action_msg = _(
+				'To allow over receipt / delivery, update "Over Receipt/Delivery Allowance" in Stock Settings or the Item.'
+			)
 		else:
-			action_msg = _('To allow over billing, update "Over Billing Allowance" in Accounts Settings or the Item.')
+			action_msg = _(
+				'To allow over billing, update "Over Billing Allowance" in Accounts Settings or the Item.'
+			)
 
-		frappe.throw(_('This document is over limit by {0} {1} for item {4}. Are you making another {3} against the same {2}?')
-			.format(
+		frappe.throw(
+			_(
+				"This document is over limit by {0} {1} for item {4}. Are you making another {3} against the same {2}?"
+			).format(
 				frappe.bold(_(item["target_ref_field"].title())),
 				frappe.bold(item["reduce_by"]),
-				frappe.bold(_(args.get('target_dt'))),
+				frappe.bold(_(args.get("target_dt"))),
 				frappe.bold(_(self.doctype)),
-				frappe.bold(item.get('item_code'))
-			) + '<br><br>' + action_msg, OverAllowanceError, title = _('Limit Crossed'))
+				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)
-				)
+		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
 
-			:param update_modified: If true, updates `modified` and `modified_by` for target parent doc
+		:param update_modified: If true, updates `modified` and `modified_by` for target parent doc
 		"""
 		for args in self.status_updater:
 			# condition to include current record (if submit or no if cancel)
 			if self.docstatus == 1:
-				args['cond'] = ' or parent="%s"' % self.name.replace('"', '\"')
+				args["cond"] = " or parent='%s'" % self.name.replace('"', '"')
 			else:
-				args['cond'] = ' and parent!="%s"' % self.name.replace('"', '\"')
+				args["cond"] = " and parent!='%s'" % self.name.replace('"', '"')
 
 			self._update_children(args, update_modified)
 
@@ -273,56 +364,73 @@
 	def _update_children(self, args, update_modified):
 		"""Update quantities or amount in child table"""
 		for d in self.get_all_children():
-			if d.doctype != args['source_dt']:
+			if d.doctype != args["source_dt"]:
 				continue
 
 			self._update_modified(args, update_modified)
 
 			# updates qty in the child table
-			args['detail_id'] = d.get(args['join_field'])
+			args["detail_id"] = d.get(args["join_field"])
 
-			args['second_source_condition'] = ""
-			if args.get('second_source_dt') and args.get('second_source_field') \
-					and args.get('second_join_field'):
+			args["second_source_condition"] = ""
+			if (
+				args.get("second_source_dt")
+				and args.get("second_source_field")
+				and args.get("second_join_field")
+			):
 				if not args.get("second_source_extra_cond"):
 					args["second_source_extra_cond"] = ""
 
-				args['second_source_condition'] = frappe.db.sql(""" select ifnull((select sum(%(second_source_field)s)
+				args["second_source_condition"] = frappe.db.sql(
+					""" select ifnull((select sum(%(second_source_field)s)
 					from `tab%(second_source_dt)s`
-					where `%(second_join_field)s`="%(detail_id)s"
+					where `%(second_join_field)s`='%(detail_id)s'
 					and (`tab%(second_source_dt)s`.docstatus=1)
-					%(second_source_extra_cond)s), 0) """ % args)[0][0]
+					%(second_source_extra_cond)s), 0) """
+					% args
+				)[0][0]
 
-			if args['detail_id']:
-				if not args.get("extra_cond"): args["extra_cond"] = ""
+			if args["detail_id"]:
+				if not args.get("extra_cond"):
+					args["extra_cond"] = ""
 
-				args["source_dt_value"] = frappe.db.sql("""
+				args["source_dt_value"] = (
+					frappe.db.sql(
+						"""
 						(select ifnull(sum(%(source_field)s), 0)
-							from `tab%(source_dt)s` where `%(join_field)s`="%(detail_id)s"
+							from `tab%(source_dt)s` where `%(join_field)s`='%(detail_id)s'
 							and (docstatus=1 %(cond)s) %(extra_cond)s)
-				""" % args)[0][0] or 0.0
+				"""
+						% args
+					)[0][0]
+					or 0.0
+				)
 
-				if args['second_source_condition']:
-					args["source_dt_value"] += flt(args['second_source_condition'])
+				if args["second_source_condition"]:
+					args["source_dt_value"] += flt(args["second_source_condition"])
 
-				frappe.db.sql("""update `tab%(target_dt)s`
+				frappe.db.sql(
+					"""update `tab%(target_dt)s`
 					set %(target_field)s = %(source_dt_value)s %(update_modified)s
-					where name='%(detail_id)s'""" % args)
+					where name='%(detail_id)s'"""
+					% args
+				)
 
 	def _update_percent_field_in_targets(self, args, update_modified=True):
 		"""Update percent field in parent transaction"""
-		if args.get('percent_join_field_parent'):
+		if args.get("percent_join_field_parent"):
 			# if reference to target doc where % is to be updated, is
 			# in source doc's parent form, consider percent_join_field_parent
-			args['name'] = self.get(args['percent_join_field_parent'])
+			args["name"] = self.get(args["percent_join_field_parent"])
 			self._update_percent_field(args, update_modified)
 		else:
-			distinct_transactions = set(d.get(args['percent_join_field'])
-				for d in self.get_all_children(args['source_dt']))
+			distinct_transactions = set(
+				d.get(args["percent_join_field"]) for d in self.get_all_children(args["source_dt"])
+			)
 
 			for name in distinct_transactions:
 				if name:
-					args['name'] = name
+					args["name"] = name
 					self._update_percent_field(args, update_modified)
 
 	def _update_percent_field(self, args, update_modified=True):
@@ -330,23 +438,29 @@
 
 		self._update_modified(args, update_modified)
 
-		if args.get('target_parent_field'):
-			frappe.db.sql("""update `tab%(target_parent_dt)s`
+		if args.get("target_parent_field"):
+			frappe.db.sql(
+				"""update `tab%(target_parent_dt)s`
 				set %(target_parent_field)s = round(
 					ifnull((select
-						ifnull(sum(if(abs(%(target_ref_field)s) > abs(%(target_field)s), abs(%(target_field)s), abs(%(target_ref_field)s))), 0)
+						ifnull(sum(case when abs(%(target_ref_field)s) > abs(%(target_field)s) then abs(%(target_field)s) else abs(%(target_ref_field)s) end), 0)
 						/ sum(abs(%(target_ref_field)s)) * 100
-					from `tab%(target_dt)s` where parent="%(name)s" having sum(abs(%(target_ref_field)s)) > 0), 0), 6)
+					from `tab%(target_dt)s` where parent='%(name)s' having sum(abs(%(target_ref_field)s)) > 0), 0), 6)
 					%(update_modified)s
-				where name='%(name)s'""" % args)
+				where name='%(name)s'"""
+				% args
+			)
 
 			# update field
-			if args.get('status_field'):
-				frappe.db.sql("""update `tab%(target_parent_dt)s`
-					set %(status_field)s = if(%(target_parent_field)s<0.001,
-						'Not %(keyword)s', if(%(target_parent_field)s>=99.999999,
-						'Fully %(keyword)s', 'Partly %(keyword)s'))
-					where name='%(name)s'""" % args)
+			if args.get("status_field"):
+				frappe.db.sql(
+					"""update `tab%(target_parent_dt)s`
+					set %(status_field)s = (case when %(target_parent_field)s<0.001 then 'Not %(keyword)s'
+					else case when %(target_parent_field)s>=99.999999 then 'Fully %(keyword)s'
+					else 'Partly %(keyword)s' end end)
+					where name='%(name)s'"""
+					% args
+				)
 
 			if update_modified:
 				target = frappe.get_doc(args["target_parent_dt"], args["name"])
@@ -355,22 +469,24 @@
 
 	def _update_modified(self, args, update_modified):
 		if not update_modified:
-			args['update_modified'] = ''
+			args["update_modified"] = ""
 			return
 
-		args['update_modified'] = ', modified = {0}, modified_by = {1}'.format(
-			frappe.db.escape(now()),
-			frappe.db.escape(frappe.session.user)
+		args["update_modified"] = ", modified = {0}, modified_by = {1}".format(
+			frappe.db.escape(now()), frappe.db.escape(frappe.session.user)
 		)
 
 	def update_billing_status_for_zero_amount_refdoc(self, ref_dt):
 		ref_fieldname = frappe.scrub(ref_dt)
 
-		ref_docs = [item.get(ref_fieldname) for item in (self.get('items') or []) if item.get(ref_fieldname)]
+		ref_docs = [
+			item.get(ref_fieldname) for item in (self.get("items") or []) if item.get(ref_fieldname)
+		]
 		if not ref_docs:
 			return
 
-		zero_amount_refdocs = frappe.db.sql_list("""
+		zero_amount_refdocs = frappe.db.sql_list(
+			"""
 			SELECT
 				name
 			from
@@ -379,21 +495,34 @@
 				docstatus = 1
 				and base_net_total = 0
 				and name in %(ref_docs)s
-		""".format(ref_dt=ref_dt), {
-			'ref_docs': ref_docs
-		})
+		""".format(
+				ref_dt=ref_dt
+			),
+			{"ref_docs": ref_docs},
+		)
 
 		if zero_amount_refdocs:
 			self.update_billing_status(zero_amount_refdocs, ref_dt, ref_fieldname)
 
 	def update_billing_status(self, zero_amount_refdoc, ref_dt, ref_fieldname):
 		for ref_dn in zero_amount_refdoc:
-			ref_doc_qty = flt(frappe.db.sql("""select ifnull(sum(qty), 0) from `tab%s Item`
-				where parent=%s""" % (ref_dt, '%s'), (ref_dn))[0][0])
+			ref_doc_qty = flt(
+				frappe.db.sql(
+					"""select ifnull(sum(qty), 0) from `tab%s Item`
+				where parent=%s"""
+					% (ref_dt, "%s"),
+					(ref_dn),
+				)[0][0]
+			)
 
-			billed_qty = flt(frappe.db.sql("""select ifnull(sum(qty), 0)
-				from `tab%s Item` where %s=%s and docstatus=1""" %
-				(self.doctype, ref_fieldname, '%s'), (ref_dn))[0][0])
+			billed_qty = flt(
+				frappe.db.sql(
+					"""select ifnull(sum(qty), 0)
+				from `tab%s Item` where %s=%s and docstatus=1"""
+					% (self.doctype, ref_fieldname, "%s"),
+					(ref_dn),
+				)[0][0]
+			)
 
 			per_billed = (min(ref_doc_qty, billed_qty) / ref_doc_qty) * 100
 
@@ -402,7 +531,7 @@
 			ref_doc.db_set("per_billed", per_billed)
 
 			# set billling status
-			if hasattr(ref_doc, 'billing_status'):
+			if hasattr(ref_doc, "billing_status"):
 				if ref_doc.per_billed < 0.001:
 					ref_doc.db_set("billing_status", "Not Billed")
 				elif ref_doc.per_billed > 99.999999:
@@ -412,29 +541,51 @@
 
 			ref_doc.set_status(update=True)
 
-def get_allowance_for(item_code, item_allowance=None, global_qty_allowance=None, global_amount_allowance=None, qty_or_amount="qty"):
+
+def get_allowance_for(
+	item_code,
+	item_allowance=None,
+	global_qty_allowance=None,
+	global_amount_allowance=None,
+	qty_or_amount="qty",
+):
 	"""
-		Returns the allowance for the item, if not set, returns global allowance
+	Returns the allowance for the item, if not set, returns global allowance
 	"""
 	if item_allowance is None:
 		item_allowance = {}
 	if qty_or_amount == "qty":
 		if item_allowance.get(item_code, frappe._dict()).get("qty"):
-			return item_allowance[item_code].qty, item_allowance, global_qty_allowance, global_amount_allowance
+			return (
+				item_allowance[item_code].qty,
+				item_allowance,
+				global_qty_allowance,
+				global_amount_allowance,
+			)
 	else:
 		if item_allowance.get(item_code, frappe._dict()).get("amount"):
-			return item_allowance[item_code].amount, item_allowance, global_qty_allowance, global_amount_allowance
+			return (
+				item_allowance[item_code].amount,
+				item_allowance,
+				global_qty_allowance,
+				global_amount_allowance,
+			)
 
-	qty_allowance, over_billing_allowance = \
-		frappe.db.get_value('Item', item_code, ['over_delivery_receipt_allowance', 'over_billing_allowance'])
+	qty_allowance, over_billing_allowance = frappe.db.get_value(
+		"Item", item_code, ["over_delivery_receipt_allowance", "over_billing_allowance"]
+	)
 
 	if qty_or_amount == "qty" and not qty_allowance:
 		if global_qty_allowance == None:
-			global_qty_allowance = flt(frappe.db.get_single_value('Stock Settings', 'over_delivery_receipt_allowance'))
+			global_qty_allowance = flt(
+				frappe.db.get_single_value("Stock Settings", "over_delivery_receipt_allowance")
+			)
 		qty_allowance = global_qty_allowance
 	elif qty_or_amount == "amount" and not over_billing_allowance:
 		if global_amount_allowance == None:
-			global_amount_allowance = flt(frappe.db.get_single_value('Accounts Settings', 'over_billing_allowance'))
+			global_amount_allowance = flt(
+				frappe.db.get_single_value("Accounts Settings", "over_billing_allowance")
+			)
 		over_billing_allowance = global_amount_allowance
 
 	if qty_or_amount == "qty":
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 8972c32..feec42f 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -21,14 +21,22 @@
 from erpnext.stock.stock_ledger import get_items_to_be_repost
 
 
-class QualityInspectionRequiredError(frappe.ValidationError): pass
-class QualityInspectionRejectedError(frappe.ValidationError): pass
-class QualityInspectionNotSubmittedError(frappe.ValidationError): pass
+class QualityInspectionRequiredError(frappe.ValidationError):
+	pass
+
+
+class QualityInspectionRejectedError(frappe.ValidationError):
+	pass
+
+
+class QualityInspectionNotSubmittedError(frappe.ValidationError):
+	pass
+
 
 class StockController(AccountsController):
 	def validate(self):
 		super(StockController, self).validate()
-		if not self.get('is_return'):
+		if not self.get("is_return"):
 			self.validate_inspection()
 		self.validate_serialized_batch()
 		self.clean_serial_nos()
@@ -41,44 +49,56 @@
 		if self.docstatus == 2:
 			make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
 
-		provisional_accounting_for_non_stock_items = \
-			cint(frappe.db.get_value('Company', self.company, 'enable_provisional_accounting_for_non_stock_items'))
+		provisional_accounting_for_non_stock_items = cint(
+			frappe.db.get_value(
+				"Company", self.company, "enable_provisional_accounting_for_non_stock_items"
+			)
+		)
 
-		if cint(erpnext.is_perpetual_inventory_enabled(self.company)) or provisional_accounting_for_non_stock_items:
+		if (
+			cint(erpnext.is_perpetual_inventory_enabled(self.company))
+			or provisional_accounting_for_non_stock_items
+		):
 			warehouse_account = get_warehouse_account_map(self.company)
 
-			if self.docstatus==1:
+			if self.docstatus == 1:
 				if not gl_entries:
 					gl_entries = self.get_gl_entries(warehouse_account)
 				make_gl_entries(gl_entries, from_repost=from_repost)
 
-		elif self.doctype in ['Purchase Receipt', 'Purchase Invoice'] and self.docstatus == 1:
+		elif self.doctype in ["Purchase Receipt", "Purchase Invoice"] and self.docstatus == 1:
 			gl_entries = []
 			gl_entries = self.get_asset_gl_entry(gl_entries)
 			make_gl_entries(gl_entries, from_repost=from_repost)
 
 	def validate_serialized_batch(self):
 		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
 		for d in self.get("items"):
-			if hasattr(d, 'serial_no') and hasattr(d, 'batch_no') and d.serial_no and d.batch_no:
-				serial_nos = frappe.get_all("Serial No",
+			if hasattr(d, "serial_no") and hasattr(d, "batch_no") and d.serial_no and d.batch_no:
+				serial_nos = frappe.get_all(
+					"Serial No",
 					fields=["batch_no", "name", "warehouse"],
-					filters={
-						"name": ("in", get_serial_nos(d.serial_no))
-					}
+					filters={"name": ("in", get_serial_nos(d.serial_no))},
 				)
 
 				for row in serial_nos:
 					if row.warehouse and row.batch_no != d.batch_no:
-						frappe.throw(_("Row #{0}: Serial No {1} does not belong to Batch {2}")
-							.format(d.idx, row.name, d.batch_no))
+						frappe.throw(
+							_("Row #{0}: Serial No {1} does not belong to Batch {2}").format(
+								d.idx, row.name, d.batch_no
+							)
+						)
 
 			if flt(d.qty) > 0.0 and d.get("batch_no") and self.get("posting_date") and self.docstatus < 2:
 				expiry_date = frappe.get_cached_value("Batch", d.get("batch_no"), "expiry_date")
 
 				if expiry_date and getdate(expiry_date) < getdate(self.posting_date):
-					frappe.throw(_("Row #{0}: The batch {1} has already expired.")
-						.format(d.idx, get_link_to_form("Batch", d.get("batch_no"))))
+					frappe.throw(
+						_("Row #{0}: The batch {1} has already expired.").format(
+							d.idx, get_link_to_form("Batch", d.get("batch_no"))
+						)
+					)
 
 	def clean_serial_nos(self):
 		from erpnext.stock.doctype.serial_no.serial_no import clean_serial_no_string
@@ -88,13 +108,14 @@
 				# remove extra whitespace and store one serial no on each line
 				row.serial_no = clean_serial_no_string(row.serial_no)
 
-		for row in self.get('packed_items') or []:
+		for row in self.get("packed_items") or []:
 			if hasattr(row, "serial_no") and row.serial_no:
 				# remove extra whitespace and store one serial no on each line
 				row.serial_no = clean_serial_no_string(row.serial_no)
 
-	def get_gl_entries(self, warehouse_account=None, default_expense_account=None,
-			default_cost_center=None):
+	def get_gl_entries(
+		self, warehouse_account=None, default_expense_account=None, default_cost_center=None
+	):
 
 		if not warehouse_account:
 			warehouse_account = get_warehouse_account_map(self.company)
@@ -116,44 +137,61 @@
 						self.check_expense_account(item_row)
 
 						# expense account/ target_warehouse / source_warehouse
-						if item_row.get('target_warehouse'):
-							warehouse = item_row.get('target_warehouse')
+						if item_row.get("target_warehouse"):
+							warehouse = item_row.get("target_warehouse")
 							expense_account = warehouse_account[warehouse]["account"]
 						else:
 							expense_account = item_row.expense_account
 
-						gl_list.append(self.get_gl_dict({
-							"account": warehouse_account[sle.warehouse]["account"],
-							"against": expense_account,
-							"cost_center": item_row.cost_center,
-							"project": item_row.project or self.get('project'),
-							"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-							"debit": flt(sle.stock_value_difference, precision),
-							"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No",
-						}, warehouse_account[sle.warehouse]["account_currency"], item=item_row))
+						gl_list.append(
+							self.get_gl_dict(
+								{
+									"account": warehouse_account[sle.warehouse]["account"],
+									"against": expense_account,
+									"cost_center": item_row.cost_center,
+									"project": item_row.project or self.get("project"),
+									"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+									"debit": flt(sle.stock_value_difference, precision),
+									"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No",
+								},
+								warehouse_account[sle.warehouse]["account_currency"],
+								item=item_row,
+							)
+						)
 
-						gl_list.append(self.get_gl_dict({
-							"account": expense_account,
-							"against": warehouse_account[sle.warehouse]["account"],
-							"cost_center": item_row.cost_center,
-							"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-							"credit": flt(sle.stock_value_difference, precision),
-							"project": item_row.get("project") or self.get("project"),
-							"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No"
-						}, item=item_row))
+						gl_list.append(
+							self.get_gl_dict(
+								{
+									"account": expense_account,
+									"against": warehouse_account[sle.warehouse]["account"],
+									"cost_center": item_row.cost_center,
+									"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+									"credit": flt(sle.stock_value_difference, precision),
+									"project": item_row.get("project") or self.get("project"),
+									"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No",
+								},
+								item=item_row,
+							)
+						)
 					elif sle.warehouse not in warehouse_with_no_account:
 						warehouse_with_no_account.append(sle.warehouse)
 
 		if warehouse_with_no_account:
 			for wh in warehouse_with_no_account:
 				if frappe.db.get_value("Warehouse", wh, "company"):
-					frappe.throw(_("Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}.").format(wh, self.company))
+					frappe.throw(
+						_(
+							"Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}."
+						).format(wh, self.company)
+					)
 
 		return process_gl_map(gl_list, precision=precision)
 
 	def get_debit_field_precision(self):
 		if not frappe.flags.debit_field_precision:
-			frappe.flags.debit_field_precision = frappe.get_precision("GL Entry", "debit_in_account_currency")
+			frappe.flags.debit_field_precision = frappe.get_precision(
+				"GL Entry", "debit_in_account_currency"
+			)
 
 		return frappe.flags.debit_field_precision
 
@@ -163,12 +201,16 @@
 			is_opening = "Yes" if reconciliation_purpose == "Opening Stock" else "No"
 			details = []
 			for voucher_detail_no in sle_map:
-				details.append(frappe._dict({
-					"name": voucher_detail_no,
-					"expense_account": default_expense_account,
-					"cost_center": default_cost_center,
-					"is_opening": is_opening
-				}))
+				details.append(
+					frappe._dict(
+						{
+							"name": voucher_detail_no,
+							"expense_account": default_expense_account,
+							"cost_center": default_cost_center,
+							"is_opening": is_opening,
+						}
+					)
+				)
 			return details
 		else:
 			details = self.get("items")
@@ -207,7 +249,8 @@
 
 	def get_stock_ledger_details(self):
 		stock_ledger = {}
-		stock_ledger_entries = frappe.db.sql("""
+		stock_ledger_entries = frappe.db.sql(
+			"""
 			select
 				name, warehouse, stock_value_difference, valuation_rate,
 				voucher_detail_no, item_code, posting_date, posting_time,
@@ -216,110 +259,154 @@
 				`tabStock Ledger Entry`
 			where
 				voucher_type=%s and voucher_no=%s and is_cancelled = 0
-		""", (self.doctype, self.name), as_dict=True)
+		""",
+			(self.doctype, self.name),
+			as_dict=True,
+		)
 
 		for sle in stock_ledger_entries:
 			stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
 		return stock_ledger
 
 	def make_batches(self, warehouse_field):
-		'''Create batches if required. Called before submit'''
+		"""Create batches if required. Called before submit"""
 		for d in self.items:
 			if d.get(warehouse_field) and not d.batch_no:
-				has_batch_no, create_new_batch = frappe.db.get_value('Item', d.item_code, ['has_batch_no', 'create_new_batch'])
+				has_batch_no, create_new_batch = frappe.db.get_value(
+					"Item", d.item_code, ["has_batch_no", "create_new_batch"]
+				)
 				if has_batch_no and create_new_batch:
-					d.batch_no = frappe.get_doc(dict(
-						doctype='Batch',
-						item=d.item_code,
-						supplier=getattr(self, 'supplier', None),
-						reference_doctype=self.doctype,
-						reference_name=self.name)).insert().name
+					d.batch_no = (
+						frappe.get_doc(
+							dict(
+								doctype="Batch",
+								item=d.item_code,
+								supplier=getattr(self, "supplier", None),
+								reference_doctype=self.doctype,
+								reference_name=self.name,
+							)
+						)
+						.insert()
+						.name
+					)
 
 	def check_expense_account(self, item):
 		if not item.get("expense_account"):
 			msg = _("Please set an Expense Account in the Items table")
-			frappe.throw(_("Row #{0}: Expense Account not set for the Item {1}. {2}")
-				.format(item.idx, frappe.bold(item.item_code), msg), title=_("Expense Account Missing"))
+			frappe.throw(
+				_("Row #{0}: Expense Account not set for the Item {1}. {2}").format(
+					item.idx, frappe.bold(item.item_code), msg
+				),
+				title=_("Expense Account Missing"),
+			)
 
 		else:
-			is_expense_account = frappe.get_cached_value("Account",
-				item.get("expense_account"), "report_type")=="Profit and Loss"
-			if self.doctype not in ("Purchase Receipt", "Purchase Invoice", "Stock Reconciliation", "Stock Entry") and not is_expense_account:
-				frappe.throw(_("Expense / Difference account ({0}) must be a 'Profit or Loss' account")
-					.format(item.get("expense_account")))
+			is_expense_account = (
+				frappe.get_cached_value("Account", item.get("expense_account"), "report_type")
+				== "Profit and Loss"
+			)
+			if (
+				self.doctype
+				not in ("Purchase Receipt", "Purchase Invoice", "Stock Reconciliation", "Stock Entry")
+				and not is_expense_account
+			):
+				frappe.throw(
+					_("Expense / Difference account ({0}) must be a 'Profit or Loss' account").format(
+						item.get("expense_account")
+					)
+				)
 			if is_expense_account and not item.get("cost_center"):
-				frappe.throw(_("{0} {1}: Cost Center is mandatory for Item {2}").format(
-					_(self.doctype), self.name, item.get("item_code")))
+				frappe.throw(
+					_("{0} {1}: Cost Center is mandatory for Item {2}").format(
+						_(self.doctype), self.name, item.get("item_code")
+					)
+				)
 
 	def delete_auto_created_batches(self):
 		for d in self.items:
-			if not d.batch_no: continue
+			if not d.batch_no:
+				continue
 
-			frappe.db.set_value("Serial No", {"batch_no": d.batch_no, "status": "Inactive"}, "batch_no", None)
+			frappe.db.set_value(
+				"Serial No", {"batch_no": d.batch_no, "status": "Inactive"}, "batch_no", None
+			)
 
 			d.batch_no = None
 			d.db_set("batch_no", None)
 
-		for data in frappe.get_all("Batch",
-			{'reference_name': self.name, 'reference_doctype': self.doctype}):
+		for data in frappe.get_all(
+			"Batch", {"reference_name": self.name, "reference_doctype": self.doctype}
+		):
 			frappe.delete_doc("Batch", data.name)
 
 	def get_sl_entries(self, d, args):
-		sl_dict = frappe._dict({
-			"item_code": d.get("item_code", None),
-			"warehouse": d.get("warehouse", None),
-			"posting_date": self.posting_date,
-			"posting_time": self.posting_time,
-			'fiscal_year': get_fiscal_year(self.posting_date, company=self.company)[0],
-			"voucher_type": self.doctype,
-			"voucher_no": self.name,
-			"voucher_detail_no": d.name,
-			"actual_qty": (self.docstatus==1 and 1 or -1)*flt(d.get("stock_qty")),
-			"stock_uom": frappe.db.get_value("Item", args.get("item_code") or d.get("item_code"), "stock_uom"),
-			"incoming_rate": 0,
-			"company": self.company,
-			"batch_no": cstr(d.get("batch_no")).strip(),
-			"serial_no": d.get("serial_no"),
-			"project": d.get("project") or self.get('project'),
-			"is_cancelled": 1 if self.docstatus==2 else 0
-		})
+		sl_dict = frappe._dict(
+			{
+				"item_code": d.get("item_code", None),
+				"warehouse": d.get("warehouse", None),
+				"posting_date": self.posting_date,
+				"posting_time": self.posting_time,
+				"fiscal_year": get_fiscal_year(self.posting_date, company=self.company)[0],
+				"voucher_type": self.doctype,
+				"voucher_no": self.name,
+				"voucher_detail_no": d.name,
+				"actual_qty": (self.docstatus == 1 and 1 or -1) * flt(d.get("stock_qty")),
+				"stock_uom": frappe.db.get_value(
+					"Item", args.get("item_code") or d.get("item_code"), "stock_uom"
+				),
+				"incoming_rate": 0,
+				"company": self.company,
+				"batch_no": cstr(d.get("batch_no")).strip(),
+				"serial_no": d.get("serial_no"),
+				"project": d.get("project") or self.get("project"),
+				"is_cancelled": 1 if self.docstatus == 2 else 0,
+			}
+		)
 
 		sl_dict.update(args)
 		return sl_dict
 
-	def make_sl_entries(self, sl_entries, allow_negative_stock=False,
-			via_landed_cost_voucher=False):
+	def make_sl_entries(self, sl_entries, allow_negative_stock=False, via_landed_cost_voucher=False):
 		from erpnext.stock.stock_ledger import make_sl_entries
+
 		make_sl_entries(sl_entries, allow_negative_stock, via_landed_cost_voucher)
 
 	def make_gl_entries_on_cancel(self):
-		if frappe.db.sql("""select name from `tabGL Entry` where voucher_type=%s
-			and voucher_no=%s""", (self.doctype, self.name)):
-				self.make_gl_entries()
+		if frappe.db.sql(
+			"""select name from `tabGL Entry` where voucher_type=%s
+			and voucher_no=%s""",
+			(self.doctype, self.name),
+		):
+			self.make_gl_entries()
 
 	def get_serialized_items(self):
 		serialized_items = []
 		item_codes = list(set(d.item_code for d in self.get("items")))
 		if item_codes:
-			serialized_items = frappe.db.sql_list("""select name from `tabItem`
-				where has_serial_no=1 and name in ({})""".format(", ".join(["%s"]*len(item_codes))),
-				tuple(item_codes))
+			serialized_items = frappe.db.sql_list(
+				"""select name from `tabItem`
+				where has_serial_no=1 and name in ({})""".format(
+					", ".join(["%s"] * len(item_codes))
+				),
+				tuple(item_codes),
+			)
 
 		return serialized_items
 
 	def validate_warehouse(self):
 		from erpnext.stock.utils import validate_disabled_warehouse, validate_warehouse_company
 
-		warehouses = list(set(d.warehouse for d in
-			self.get("items") if getattr(d, "warehouse", None)))
+		warehouses = list(set(d.warehouse for d in self.get("items") if getattr(d, "warehouse", None)))
 
-		target_warehouses = list(set([d.target_warehouse for d in
-			self.get("items") if getattr(d, "target_warehouse", None)]))
+		target_warehouses = list(
+			set([d.target_warehouse for d in self.get("items") if getattr(d, "target_warehouse", None)])
+		)
 
 		warehouses.extend(target_warehouses)
 
-		from_warehouse = list(set([d.from_warehouse for d in
-			self.get("items") if getattr(d, "from_warehouse", None)]))
+		from_warehouse = list(
+			set([d.from_warehouse for d in self.get("items") if getattr(d, "from_warehouse", None)])
+		)
 
 		warehouses.extend(from_warehouse)
 
@@ -332,14 +419,17 @@
 		if self.doctype == "Delivery Note":
 			target_ref_field = "amount - (returned_qty * rate)"
 
-		self._update_percent_field({
-			"target_dt": self.doctype + " Item",
-			"target_parent_dt": self.doctype,
-			"target_parent_field": "per_billed",
-			"target_ref_field": target_ref_field,
-			"target_field": "billed_amt",
-			"name": self.name,
-		}, update_modified)
+		self._update_percent_field(
+			{
+				"target_dt": self.doctype + " Item",
+				"target_parent_dt": self.doctype,
+				"target_parent_field": "per_billed",
+				"target_ref_field": target_ref_field,
+				"target_field": "billed_amt",
+				"name": self.name,
+			},
+			update_modified,
+		)
 
 	def validate_inspection(self):
 		"""Checks if quality inspection is set/ is valid for Items that require inspection."""
@@ -347,24 +437,28 @@
 			"Purchase Receipt": "inspection_required_before_purchase",
 			"Purchase Invoice": "inspection_required_before_purchase",
 			"Sales Invoice": "inspection_required_before_delivery",
-			"Delivery Note": "inspection_required_before_delivery"
+			"Delivery Note": "inspection_required_before_delivery",
 		}
 		inspection_required_fieldname = inspection_fieldname_map.get(self.doctype)
 
 		# return if inspection is not required on document level
-		if ((not inspection_required_fieldname and self.doctype != "Stock Entry") or
-			(self.doctype == "Stock Entry" and not self.inspection_required) or
-			(self.doctype in ["Sales Invoice", "Purchase Invoice"] and not self.update_stock)):
-				return
+		if (
+			(not inspection_required_fieldname and self.doctype != "Stock Entry")
+			or (self.doctype == "Stock Entry" and not self.inspection_required)
+			or (self.doctype in ["Sales Invoice", "Purchase Invoice"] and not self.update_stock)
+		):
+			return
 
-		for row in self.get('items'):
+		for row in self.get("items"):
 			qi_required = False
-			if (inspection_required_fieldname and frappe.db.get_value("Item", row.item_code, inspection_required_fieldname)):
+			if inspection_required_fieldname and frappe.db.get_value(
+				"Item", row.item_code, inspection_required_fieldname
+			):
 				qi_required = True
 			elif self.doctype == "Stock Entry" and row.t_warehouse:
-				qi_required = True # inward stock needs inspection
+				qi_required = True  # inward stock needs inspection
 
-			if qi_required: # validate row only if inspection is required on item level
+			if qi_required:  # validate row only if inspection is required on item level
 				self.validate_qi_presence(row)
 				if self.docstatus == 1:
 					self.validate_qi_submission(row)
@@ -381,12 +475,16 @@
 
 	def validate_qi_submission(self, row):
 		"""Check if QI is submitted on row level, during submission"""
-		action = frappe.db.get_single_value("Stock Settings", "action_if_quality_inspection_is_not_submitted")
+		action = frappe.db.get_single_value(
+			"Stock Settings", "action_if_quality_inspection_is_not_submitted"
+		)
 		qa_docstatus = frappe.db.get_value("Quality Inspection", row.quality_inspection, "docstatus")
 
 		if not qa_docstatus == 1:
-			link = frappe.utils.get_link_to_form('Quality Inspection', row.quality_inspection)
-			msg = f"Row #{row.idx}: Quality Inspection {link} is not submitted for the item: {row.item_code}"
+			link = frappe.utils.get_link_to_form("Quality Inspection", row.quality_inspection)
+			msg = (
+				f"Row #{row.idx}: Quality Inspection {link} is not submitted for the item: {row.item_code}"
+			)
 			if action == "Stop":
 				frappe.throw(_(msg), title=_("Inspection Submission"), exc=QualityInspectionNotSubmittedError)
 			else:
@@ -398,7 +496,7 @@
 		qa_status = frappe.db.get_value("Quality Inspection", row.quality_inspection, "status")
 
 		if qa_status == "Rejected":
-			link = frappe.utils.get_link_to_form('Quality Inspection', row.quality_inspection)
+			link = frappe.utils.get_link_to_form("Quality Inspection", row.quality_inspection)
 			msg = f"Row #{row.idx}: Quality Inspection {link} was rejected for item {row.item_code}"
 			if action == "Stop":
 				frappe.throw(_(msg), title=_("Inspection Rejected"), exc=QualityInspectionRejectedError)
@@ -411,48 +509,71 @@
 			frappe.get_doc("Blanket Order", blanket_order).update_ordered_qty()
 
 	def validate_customer_provided_item(self):
-		for d in self.get('items'):
+		for d in self.get("items"):
 			# Customer Provided parts will have zero valuation rate
-			if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
+			if frappe.db.get_value("Item", d.item_code, "is_customer_provided_item"):
 				d.allow_zero_valuation_rate = 1
 
 	def set_rate_of_stock_uom(self):
-		if self.doctype in ["Purchase Receipt", "Purchase Invoice", "Purchase Order", "Sales Invoice", "Sales Order", "Delivery Note", "Quotation"]:
+		if self.doctype in [
+			"Purchase Receipt",
+			"Purchase Invoice",
+			"Purchase Order",
+			"Sales Invoice",
+			"Sales Order",
+			"Delivery Note",
+			"Quotation",
+		]:
 			for d in self.get("items"):
 				d.stock_uom_rate = d.rate / (d.conversion_factor or 1)
 
 	def validate_internal_transfer(self):
-		if self.doctype in ('Sales Invoice', 'Delivery Note', 'Purchase Invoice', 'Purchase Receipt') \
-			and self.is_internal_transfer():
+		if (
+			self.doctype in ("Sales Invoice", "Delivery Note", "Purchase Invoice", "Purchase Receipt")
+			and self.is_internal_transfer()
+		):
 			self.validate_in_transit_warehouses()
 			self.validate_multi_currency()
 			self.validate_packed_items()
 
 	def validate_in_transit_warehouses(self):
-		if (self.doctype == 'Sales Invoice' and self.get('update_stock')) or self.doctype == 'Delivery Note':
-			for item in self.get('items'):
+		if (
+			self.doctype == "Sales Invoice" and self.get("update_stock")
+		) or self.doctype == "Delivery Note":
+			for item in self.get("items"):
 				if not item.target_warehouse:
-					frappe.throw(_("Row {0}: Target Warehouse is mandatory for internal transfers").format(item.idx))
+					frappe.throw(
+						_("Row {0}: Target Warehouse is mandatory for internal transfers").format(item.idx)
+					)
 
-		if (self.doctype == 'Purchase Invoice' and self.get('update_stock')) or self.doctype == 'Purchase Receipt':
-			for item in self.get('items'):
+		if (
+			self.doctype == "Purchase Invoice" and self.get("update_stock")
+		) or self.doctype == "Purchase Receipt":
+			for item in self.get("items"):
 				if not item.from_warehouse:
-					frappe.throw(_("Row {0}: From Warehouse is mandatory for internal transfers").format(item.idx))
+					frappe.throw(
+						_("Row {0}: From Warehouse is mandatory for internal transfers").format(item.idx)
+					)
 
 	def validate_multi_currency(self):
 		if self.currency != self.company_currency:
 			frappe.throw(_("Internal transfers can only be done in company's default currency"))
 
 	def validate_packed_items(self):
-		if self.doctype in ('Sales Invoice', 'Delivery Note Item') and self.get('packed_items'):
+		if self.doctype in ("Sales Invoice", "Delivery Note Item") and self.get("packed_items"):
 			frappe.throw(_("Packed Items cannot be transferred internally"))
 
 	def validate_putaway_capacity(self):
 		# if over receipt is attempted while 'apply putaway rule' is disabled
 		# and if rule was applied on the transaction, validate it.
 		from erpnext.stock.doctype.putaway_rule.putaway_rule import get_available_putaway_capacity
-		valid_doctype = self.doctype in ("Purchase Receipt", "Stock Entry", "Purchase Invoice",
-			"Stock Reconciliation")
+
+		valid_doctype = self.doctype in (
+			"Purchase Receipt",
+			"Stock Entry",
+			"Purchase Invoice",
+			"Stock Reconciliation",
+		)
 
 		if self.doctype == "Purchase Invoice" and self.get("update_stock") == 0:
 			valid_doctype = False
@@ -461,14 +582,15 @@
 			rule_map = defaultdict(dict)
 			for item in self.get("items"):
 				warehouse_field = "t_warehouse" if self.doctype == "Stock Entry" else "warehouse"
-				rule = frappe.db.get_value("Putaway Rule",
-					{
-						"item_code": item.get("item_code"),
-						"warehouse": item.get(warehouse_field)
-					},
-					["name", "disable"], as_dict=True)
+				rule = frappe.db.get_value(
+					"Putaway Rule",
+					{"item_code": item.get("item_code"), "warehouse": item.get(warehouse_field)},
+					["name", "disable"],
+					as_dict=True,
+				)
 				if rule:
-					if rule.get("disabled"): continue # dont validate for disabled rule
+					if rule.get("disabled"):
+						continue  # dont validate for disabled rule
 
 					if self.doctype == "Stock Reconciliation":
 						stock_qty = flt(item.qty)
@@ -489,46 +611,55 @@
 					frappe.throw(msg=message, title=_("Over Receipt"))
 
 	def prepare_over_receipt_message(self, rule, values):
-		message = _("{0} qty of Item {1} is being received into Warehouse {2} with capacity {3}.") \
-			.format(
-				frappe.bold(values["qty_put"]), frappe.bold(values["item"]),
-				frappe.bold(values["warehouse"]), frappe.bold(values["capacity"])
-			)
+		message = _(
+			"{0} qty of Item {1} is being received into Warehouse {2} with capacity {3}."
+		).format(
+			frappe.bold(values["qty_put"]),
+			frappe.bold(values["item"]),
+			frappe.bold(values["warehouse"]),
+			frappe.bold(values["capacity"]),
+		)
 		message += "<br><br>"
 		rule_link = frappe.utils.get_link_to_form("Putaway Rule", rule)
 		message += _("Please adjust the qty or edit {0} to proceed.").format(rule_link)
 		return message
 
 	def repost_future_sle_and_gle(self):
-		args = frappe._dict({
-			"posting_date": self.posting_date,
-			"posting_time": self.posting_time,
-			"voucher_type": self.doctype,
-			"voucher_no": self.name,
-			"company": self.company
-		})
+		args = frappe._dict(
+			{
+				"posting_date": self.posting_date,
+				"posting_time": self.posting_time,
+				"voucher_type": self.doctype,
+				"voucher_no": self.name,
+				"company": self.company,
+			}
+		)
 
 		if future_sle_exists(args) or repost_required_for_queue(self):
-			item_based_reposting =  cint(frappe.db.get_single_value("Stock Reposting Settings", "item_based_reposting"))
+			item_based_reposting = cint(
+				frappe.db.get_single_value("Stock Reposting Settings", "item_based_reposting")
+			)
 			if item_based_reposting:
 				create_item_wise_repost_entries(voucher_type=self.doctype, voucher_no=self.name)
 			else:
 				create_repost_item_valuation_entry(args)
 
+
 def repost_required_for_queue(doc: StockController) -> bool:
 	"""check if stock document contains repeated item-warehouse with queue based valuation.
 
 	if queue exists for repeated items then SLEs need to reprocessed in background again.
 	"""
 
-	consuming_sles = frappe.db.get_all("Stock Ledger Entry",
+	consuming_sles = frappe.db.get_all(
+		"Stock Ledger Entry",
 		filters={
 			"voucher_type": doc.doctype,
 			"voucher_no": doc.name,
 			"actual_qty": ("<", 0),
-			"is_cancelled": 0
+			"is_cancelled": 0,
 		},
-		fields=["item_code", "warehouse", "stock_queue"]
+		fields=["item_code", "warehouse", "stock_queue"],
 	)
 	item_warehouses = [(sle.item_code, sle.warehouse) for sle in consuming_sles]
 
@@ -551,32 +682,41 @@
 	inspections = []
 	for item in items:
 		if flt(item.get("sample_size")) > flt(item.get("qty")):
-			frappe.throw(_("{item_name}'s Sample Size ({sample_size}) cannot be greater than the Accepted Quantity ({accepted_quantity})").format(
-				item_name=item.get("item_name"),
-				sample_size=item.get("sample_size"),
-				accepted_quantity=item.get("qty")
-			))
+			frappe.throw(
+				_(
+					"{item_name}'s Sample Size ({sample_size}) cannot be greater than the Accepted Quantity ({accepted_quantity})"
+				).format(
+					item_name=item.get("item_name"),
+					sample_size=item.get("sample_size"),
+					accepted_quantity=item.get("qty"),
+				)
+			)
 
-		quality_inspection = frappe.get_doc({
-			"doctype": "Quality Inspection",
-			"inspection_type": "Incoming",
-			"inspected_by": frappe.session.user,
-			"reference_type": doctype,
-			"reference_name": docname,
-			"item_code": item.get("item_code"),
-			"description": item.get("description"),
-			"sample_size": flt(item.get("sample_size")),
-			"item_serial_no": item.get("serial_no").split("\n")[0] if item.get("serial_no") else None,
-			"batch_no": item.get("batch_no")
-		}).insert()
+		quality_inspection = frappe.get_doc(
+			{
+				"doctype": "Quality Inspection",
+				"inspection_type": "Incoming",
+				"inspected_by": frappe.session.user,
+				"reference_type": doctype,
+				"reference_name": docname,
+				"item_code": item.get("item_code"),
+				"description": item.get("description"),
+				"sample_size": flt(item.get("sample_size")),
+				"item_serial_no": item.get("serial_no").split("\n")[0] if item.get("serial_no") else None,
+				"batch_no": item.get("batch_no"),
+			}
+		).insert()
 		quality_inspection.save()
 		inspections.append(quality_inspection.name)
 
 	return inspections
 
+
 def is_reposting_pending():
-	return frappe.db.exists("Repost Item Valuation",
-		{'docstatus': 1, 'status': ['in', ['Queued','In Progress']]})
+	return frappe.db.exists(
+		"Repost Item Valuation", {"docstatus": 1, "status": ["in", ["Queued", "In Progress"]]}
+	)
+
 
 def future_sle_exists(args, sl_entries=None):
 	key = (args.voucher_type, args.voucher_no)
@@ -593,7 +733,8 @@
 
 	or_conditions = get_conditions_to_validate_future_sle(sl_entries)
 
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 		select item_code, warehouse, count(name) as total_row
 		from `tabStock Ledger Entry` force index (item_warehouse)
 		where
@@ -604,43 +745,55 @@
 			and is_cancelled = 0
 		GROUP BY
 			item_code, warehouse
-		""".format(" or ".join(or_conditions)), args, as_dict=1)
+		""".format(
+			" or ".join(or_conditions)
+		),
+		args,
+		as_dict=1,
+	)
 
 	for d in data:
 		frappe.local.future_sle[key][(d.item_code, d.warehouse)] = d.total_row
 
 	return len(data)
 
-def validate_future_sle_not_exists(args, key, sl_entries=None):
-	item_key = ''
-	if args.get('item_code'):
-		item_key = (args.get('item_code'), args.get('warehouse'))
 
-	if not sl_entries and hasattr(frappe.local, 'future_sle'):
-		if (not frappe.local.future_sle.get(key) or
-			(item_key and item_key not in frappe.local.future_sle.get(key))):
+def validate_future_sle_not_exists(args, key, sl_entries=None):
+	item_key = ""
+	if args.get("item_code"):
+		item_key = (args.get("item_code"), args.get("warehouse"))
+
+	if not sl_entries and hasattr(frappe.local, "future_sle"):
+		if not frappe.local.future_sle.get(key) or (
+			item_key and item_key not in frappe.local.future_sle.get(key)
+		):
 			return True
 
+
 def get_cached_data(args, key):
-	if not hasattr(frappe.local, 'future_sle'):
+	if not hasattr(frappe.local, "future_sle"):
 		frappe.local.future_sle = {}
 
 	if key not in frappe.local.future_sle:
 		frappe.local.future_sle[key] = frappe._dict({})
 
-	if args.get('item_code'):
-		item_key = (args.get('item_code'), args.get('warehouse'))
+	if args.get("item_code"):
+		item_key = (args.get("item_code"), args.get("warehouse"))
 		count = frappe.local.future_sle[key].get(item_key)
 
 		return True if (count or count == 0) else False
 	else:
 		return frappe.local.future_sle[key]
 
+
 def get_sle_entries_against_voucher(args):
-	return frappe.get_all("Stock Ledger Entry",
+	return frappe.get_all(
+		"Stock Ledger Entry",
 		filters={"voucher_type": args.voucher_type, "voucher_no": args.voucher_no},
 		fields=["item_code", "warehouse"],
-		order_by="creation asc")
+		order_by="creation asc",
+	)
+
 
 def get_conditions_to_validate_future_sle(sl_entries):
 	warehouse_items_map = {}
@@ -654,16 +807,18 @@
 	for warehouse, items in warehouse_items_map.items():
 		or_conditions.append(
 			f"""warehouse = {frappe.db.escape(warehouse)}
-				and item_code in ({', '.join(frappe.db.escape(item) for item in items)})""")
+				and item_code in ({', '.join(frappe.db.escape(item) for item in items)})"""
+		)
 
 	return or_conditions
 
+
 def create_repost_item_valuation_entry(args):
 	args = frappe._dict(args)
 	repost_entry = frappe.new_doc("Repost Item Valuation")
 	repost_entry.based_on = args.based_on
 	if not args.based_on:
-		repost_entry.based_on = 'Transaction' if args.voucher_no else "Item and Warehouse"
+		repost_entry.based_on = "Transaction" if args.voucher_no else "Item and Warehouse"
 	repost_entry.voucher_type = args.voucher_type
 	repost_entry.voucher_no = args.voucher_no
 	repost_entry.item_code = args.item_code
diff --git a/erpnext/controllers/subcontracting.py b/erpnext/controllers/subcontracting.py
index c52c688..4bce06f 100644
--- a/erpnext/controllers/subcontracting.py
+++ b/erpnext/controllers/subcontracting.py
@@ -8,9 +8,9 @@
 from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 
 
-class Subcontracting():
+class Subcontracting:
 	def set_materials_for_subcontracted_items(self, raw_material_table):
-		if self.doctype == 'Purchase Invoice' and not self.update_stock:
+		if self.doctype == "Purchase Invoice" and not self.update_stock:
 			return
 
 		self.raw_material_table = raw_material_table
@@ -33,13 +33,14 @@
 		self.__get_backflush_based_on()
 
 	def __get_backflush_based_on(self):
-		self.backflush_based_on = frappe.db.get_single_value("Buying Settings",
-			"backflush_raw_materials_of_subcontract_based_on")
+		self.backflush_based_on = frappe.db.get_single_value(
+			"Buying Settings", "backflush_raw_materials_of_subcontract_based_on"
+		)
 
 	def __get_purchase_orders(self):
 		self.purchase_orders = []
 
-		if self.doctype == 'Purchase Order':
+		if self.doctype == "Purchase Order":
 			return
 
 		self.purchase_orders = [d.purchase_order for d in self.items if d.purchase_order]
@@ -48,7 +49,7 @@
 		self.__changed_name = []
 		self.__reference_name = []
 
-		if self.doctype == 'Purchase Order' or self.is_new():
+		if self.doctype == "Purchase Order" or self.is_new():
 			self.set(self.raw_material_table, [])
 			return
 
@@ -68,20 +69,20 @@
 
 	def __get_data_before_save(self):
 		item_dict = {}
-		if self.doctype in ['Purchase Receipt', 'Purchase Invoice'] and self._doc_before_save:
-			for row in self._doc_before_save.get('items'):
+		if self.doctype in ["Purchase Receipt", "Purchase Invoice"] and self._doc_before_save:
+			for row in self._doc_before_save.get("items"):
 				item_dict[row.name] = (row.item_code, row.qty)
 
 		return item_dict
 
 	def get_available_materials(self):
-		''' Get the available raw materials which has been transferred to the supplier.
-			available_materials = {
-				(item_code, subcontracted_item, purchase_order): {
-					'qty': 1, 'serial_no': [ABC], 'batch_no': {'batch1': 1}, 'data': item_details
-				}
-			}
-		'''
+		"""Get the available raw materials which has been transferred to the supplier.
+		available_materials = {
+		        (item_code, subcontracted_item, purchase_order): {
+		                'qty': 1, 'serial_no': [ABC], 'batch_no': {'batch1': 1}, 'data': item_details
+		        }
+		}
+		"""
 		if not self.purchase_orders:
 			return
 
@@ -89,8 +90,17 @@
 			key = (row.rm_item_code, row.main_item_code, row.purchase_order)
 
 			if key not in self.available_materials:
-				self.available_materials.setdefault(key, frappe._dict({'qty': 0, 'serial_no': [],
-					'batch_no': defaultdict(float), 'item_details': row, 'po_details': []})
+				self.available_materials.setdefault(
+					key,
+					frappe._dict(
+						{
+							"qty": 0,
+							"serial_no": [],
+							"batch_no": defaultdict(float),
+							"item_details": row,
+							"po_details": [],
+						}
+					),
 				)
 
 			details = self.available_materials[key]
@@ -106,17 +116,17 @@
 			self.__set_alternative_item_details(row)
 
 		self.__transferred_items = copy.deepcopy(self.available_materials)
-		for doctype in ['Purchase Receipt', 'Purchase Invoice']:
+		for doctype in ["Purchase Receipt", "Purchase Invoice"]:
 			self.__update_consumed_materials(doctype)
 
 	def __update_consumed_materials(self, doctype, return_consumed_items=False):
-		'''Deduct the consumed materials from the available materials.'''
+		"""Deduct the consumed materials from the available materials."""
 
 		pr_items = self.__get_received_items(doctype)
 		if not pr_items:
 			return ([], {}) if return_consumed_items else None
 
-		pr_items = {d.name: d.get(self.get('po_field') or 'purchase_order') for d in pr_items}
+		pr_items = {d.name: d.get(self.get("po_field") or "purchase_order") for d in pr_items}
 		consumed_materials = self.__get_consumed_items(doctype, pr_items.keys())
 
 		if return_consumed_items:
@@ -127,97 +137,153 @@
 			if not self.available_materials.get(key):
 				continue
 
-			self.available_materials[key]['qty'] -= row.consumed_qty
+			self.available_materials[key]["qty"] -= row.consumed_qty
 			if row.serial_no:
-				self.available_materials[key]['serial_no'] = list(
-					set(self.available_materials[key]['serial_no']) - set(get_serial_nos(row.serial_no))
+				self.available_materials[key]["serial_no"] = list(
+					set(self.available_materials[key]["serial_no"]) - set(get_serial_nos(row.serial_no))
 				)
 
 			if row.batch_no:
-				self.available_materials[key]['batch_no'][row.batch_no] -= row.consumed_qty
+				self.available_materials[key]["batch_no"][row.batch_no] -= row.consumed_qty
 
 	def __get_transferred_items(self):
-		fields = ['`tabStock Entry`.`purchase_order`']
-		alias_dict = {'item_code': 'rm_item_code', 'subcontracted_item': 'main_item_code', 'basic_rate': 'rate'}
+		fields = ["`tabStock Entry`.`purchase_order`"]
+		alias_dict = {
+			"item_code": "rm_item_code",
+			"subcontracted_item": "main_item_code",
+			"basic_rate": "rate",
+		}
 
-		child_table_fields = ['item_code', 'item_name', 'description', 'qty', 'basic_rate', 'amount',
-			'serial_no', 'uom', 'subcontracted_item', 'stock_uom', 'batch_no', 'conversion_factor',
-			's_warehouse', 't_warehouse', 'item_group', 'po_detail']
+		child_table_fields = [
+			"item_code",
+			"item_name",
+			"description",
+			"qty",
+			"basic_rate",
+			"amount",
+			"serial_no",
+			"uom",
+			"subcontracted_item",
+			"stock_uom",
+			"batch_no",
+			"conversion_factor",
+			"s_warehouse",
+			"t_warehouse",
+			"item_group",
+			"po_detail",
+		]
 
-		if self.backflush_based_on == 'BOM':
-			child_table_fields.append('original_item')
+		if self.backflush_based_on == "BOM":
+			child_table_fields.append("original_item")
 
 		for field in child_table_fields:
-			fields.append(f'`tabStock Entry Detail`.`{field}` As {alias_dict.get(field, field)}')
+			fields.append(f"`tabStock Entry Detail`.`{field}` As {alias_dict.get(field, field)}")
 
-		filters = [['Stock Entry', 'docstatus', '=', 1], ['Stock Entry', 'purpose', '=', 'Send to Subcontractor'],
-			['Stock Entry', 'purchase_order', 'in', self.purchase_orders]]
+		filters = [
+			["Stock Entry", "docstatus", "=", 1],
+			["Stock Entry", "purpose", "=", "Send to Subcontractor"],
+			["Stock Entry", "purchase_order", "in", self.purchase_orders],
+		]
 
-		return frappe.get_all('Stock Entry', fields = fields, filters=filters)
+		return frappe.get_all("Stock Entry", fields=fields, filters=filters)
 
 	def __get_received_items(self, doctype):
 		fields = []
-		self.po_field = 'purchase_order'
+		self.po_field = "purchase_order"
 
-		for field in ['name', self.po_field, 'parent']:
-			fields.append(f'`tab{doctype} Item`.`{field}`')
+		for field in ["name", self.po_field, "parent"]:
+			fields.append(f"`tab{doctype} Item`.`{field}`")
 
-		filters = [[doctype, 'docstatus', '=', 1], [f'{doctype} Item', self.po_field, 'in', self.purchase_orders]]
-		if doctype == 'Purchase Invoice':
-			filters.append(['Purchase Invoice', 'update_stock', "=", 1])
+		filters = [
+			[doctype, "docstatus", "=", 1],
+			[f"{doctype} Item", self.po_field, "in", self.purchase_orders],
+		]
+		if doctype == "Purchase Invoice":
+			filters.append(["Purchase Invoice", "update_stock", "=", 1])
 
-		return frappe.get_all(f'{doctype}', fields = fields, filters = filters)
+		return frappe.get_all(f"{doctype}", fields=fields, filters=filters)
 
 	def __get_consumed_items(self, doctype, pr_items):
-		return frappe.get_all('Purchase Receipt Item Supplied',
-			fields = ['serial_no', 'rm_item_code', 'reference_name', 'batch_no', 'consumed_qty', 'main_item_code'],
-			filters = {'docstatus': 1, 'reference_name': ('in', list(pr_items)), 'parenttype': doctype})
+		return frappe.get_all(
+			"Purchase Receipt Item Supplied",
+			fields=[
+				"serial_no",
+				"rm_item_code",
+				"reference_name",
+				"batch_no",
+				"consumed_qty",
+				"main_item_code",
+			],
+			filters={"docstatus": 1, "reference_name": ("in", list(pr_items)), "parenttype": doctype},
+		)
 
 	def __set_alternative_item_details(self, row):
-		if row.get('original_item'):
-			self.alternative_item_details[row.get('original_item')] = row
+		if row.get("original_item"):
+			self.alternative_item_details[row.get("original_item")] = row
 
 	def __get_pending_qty_to_receive(self):
-		'''Get qty to be received against the purchase order.'''
+		"""Get qty to be received against the purchase order."""
 
 		self.qty_to_be_received = defaultdict(float)
 
-		if self.doctype != 'Purchase Order' and self.backflush_based_on != 'BOM' and self.purchase_orders:
-			for row in frappe.get_all('Purchase Order Item',
-				fields = ['item_code', '(qty - received_qty) as qty', 'parent', 'name'],
-				filters = {'docstatus': 1, 'parent': ('in', self.purchase_orders)}):
+		if (
+			self.doctype != "Purchase Order" and self.backflush_based_on != "BOM" and self.purchase_orders
+		):
+			for row in frappe.get_all(
+				"Purchase Order Item",
+				fields=["item_code", "(qty - received_qty) as qty", "parent", "name"],
+				filters={"docstatus": 1, "parent": ("in", self.purchase_orders)},
+			):
 
 				self.qty_to_be_received[(row.item_code, row.parent)] += row.qty
 
 	def __get_materials_from_bom(self, item_code, bom_no, exploded_item=0):
-		doctype = 'BOM Item' if not exploded_item else 'BOM Explosion Item'
-		fields = [f'`tab{doctype}`.`stock_qty` / `tabBOM`.`quantity` as qty_consumed_per_unit']
+		doctype = "BOM Item" if not exploded_item else "BOM Explosion Item"
+		fields = [f"`tab{doctype}`.`stock_qty` / `tabBOM`.`quantity` as qty_consumed_per_unit"]
 
-		alias_dict = {'item_code': 'rm_item_code', 'name': 'bom_detail_no', 'source_warehouse': 'reserve_warehouse'}
-		for field in ['item_code', 'name', 'rate', 'stock_uom',
-			'source_warehouse', 'description', 'item_name', 'stock_uom']:
-			fields.append(f'`tab{doctype}`.`{field}` As {alias_dict.get(field, field)}')
+		alias_dict = {
+			"item_code": "rm_item_code",
+			"name": "bom_detail_no",
+			"source_warehouse": "reserve_warehouse",
+		}
+		for field in [
+			"item_code",
+			"name",
+			"rate",
+			"stock_uom",
+			"source_warehouse",
+			"description",
+			"item_name",
+			"stock_uom",
+		]:
+			fields.append(f"`tab{doctype}`.`{field}` As {alias_dict.get(field, field)}")
 
-		filters = [[doctype, 'parent', '=', bom_no], [doctype, 'docstatus', '=', 1],
-			['BOM', 'item', '=', item_code], [doctype, 'sourced_by_supplier', '=', 0]]
+		filters = [
+			[doctype, "parent", "=", bom_no],
+			[doctype, "docstatus", "=", 1],
+			["BOM", "item", "=", item_code],
+			[doctype, "sourced_by_supplier", "=", 0],
+		]
 
-		return frappe.get_all('BOM', fields = fields, filters=filters, order_by = f'`tab{doctype}`.`idx`') or []
+		return (
+			frappe.get_all("BOM", fields=fields, filters=filters, order_by=f"`tab{doctype}`.`idx`") or []
+		)
 
 	def __remove_changed_rows(self):
 		if not self.__changed_name:
 			return
 
-		i=1
+		i = 1
 		self.set(self.raw_material_table, [])
 		for d in self._doc_before_save.supplied_items:
 			if d.reference_name in self.__changed_name:
 				continue
 
-			if (d.reference_name not in self.__reference_name):
+			if d.reference_name not in self.__reference_name:
 				continue
 
 			d.idx = i
-			self.append('supplied_items', d)
+			self.append("supplied_items", d)
 
 			i += 1
 
@@ -226,31 +292,35 @@
 
 		has_supplied_items = True if self.get(self.raw_material_table) else False
 		for row in self.items:
-			if (self.doctype != 'Purchase Order' and ((self.__changed_name and row.name not in self.__changed_name)
-				or (has_supplied_items and not self.__changed_name))):
+			if self.doctype != "Purchase Order" and (
+				(self.__changed_name and row.name not in self.__changed_name)
+				or (has_supplied_items and not self.__changed_name)
+			):
 				continue
 
-			if self.doctype == 'Purchase Order' or self.backflush_based_on == 'BOM':
-				for bom_item in self.__get_materials_from_bom(row.item_code, row.bom, row.get('include_exploded_items')):
-					qty = (flt(bom_item.qty_consumed_per_unit) * flt(row.qty) * row.conversion_factor)
+			if self.doctype == "Purchase Order" or self.backflush_based_on == "BOM":
+				for bom_item in self.__get_materials_from_bom(
+					row.item_code, row.bom, row.get("include_exploded_items")
+				):
+					qty = flt(bom_item.qty_consumed_per_unit) * flt(row.qty) * row.conversion_factor
 					bom_item.main_item_code = row.item_code
 					self.__update_reserve_warehouse(bom_item, row)
 					self.__set_alternative_item(bom_item)
 					self.__add_supplied_item(row, bom_item, qty)
 
-			elif self.backflush_based_on != 'BOM':
+			elif self.backflush_based_on != "BOM":
 				for key, transfer_item in self.available_materials.items():
 					if (key[1], key[2]) == (row.item_code, row.purchase_order) and transfer_item.qty > 0:
 						qty = self.__get_qty_based_on_material_transfer(row, transfer_item) or 0
 						transfer_item.qty -= qty
-						self.__add_supplied_item(row, transfer_item.get('item_details'), qty)
+						self.__add_supplied_item(row, transfer_item.get("item_details"), qty)
 
 				if self.qty_to_be_received:
 					self.qty_to_be_received[(row.item_code, row.purchase_order)] -= row.qty
 
 	def __update_reserve_warehouse(self, row, item):
-		if self.doctype == 'Purchase Order':
-			row.reserve_warehouse = (self.set_reserve_warehouse or item.warehouse)
+		if self.doctype == "Purchase Order":
+			row.reserve_warehouse = self.set_reserve_warehouse or item.warehouse
 
 	def __get_qty_based_on_material_transfer(self, item_row, transfer_item):
 		key = (item_row.item_code, item_row.purchase_order)
@@ -262,8 +332,9 @@
 			qty = (flt(item_row.qty) * flt(transfer_item.qty)) / flt(self.qty_to_be_received.get(key, 0))
 			transfer_item.item_details.required_qty = transfer_item.qty
 
-			if (transfer_item.serial_no or frappe.get_cached_value('UOM',
-				transfer_item.item_details.stock_uom, 'must_be_whole_number')):
+			if transfer_item.serial_no or frappe.get_cached_value(
+				"UOM", transfer_item.item_details.stock_uom, "must_be_whole_number"
+			):
 				return frappe.utils.ceil(qty)
 
 			return qty
@@ -277,7 +348,7 @@
 		rm_obj = self.append(self.raw_material_table, bom_item)
 		rm_obj.reference_name = item_row.name
 
-		if self.doctype == 'Purchase Order':
+		if self.doctype == "Purchase Order":
 			rm_obj.required_qty = qty
 		else:
 			rm_obj.consumed_qty = 0
@@ -287,12 +358,12 @@
 	def __set_batch_nos(self, bom_item, item_row, rm_obj, qty):
 		key = (rm_obj.rm_item_code, item_row.item_code, item_row.purchase_order)
 
-		if (self.available_materials.get(key) and self.available_materials[key]['batch_no']):
+		if self.available_materials.get(key) and self.available_materials[key]["batch_no"]:
 			new_rm_obj = None
-			for batch_no, batch_qty in self.available_materials[key]['batch_no'].items():
+			for batch_no, batch_qty in self.available_materials[key]["batch_no"].items():
 				if batch_qty >= qty:
 					self.__set_batch_no_as_per_qty(item_row, rm_obj, batch_no, qty)
-					self.available_materials[key]['batch_no'][batch_no] -= qty
+					self.available_materials[key]["batch_no"][batch_no] -= qty
 					return
 
 				elif qty > 0 and batch_qty > 0:
@@ -300,7 +371,7 @@
 					new_rm_obj = self.append(self.raw_material_table, bom_item)
 					new_rm_obj.reference_name = item_row.name
 					self.__set_batch_no_as_per_qty(item_row, new_rm_obj, batch_no, batch_qty)
-					self.available_materials[key]['batch_no'][batch_no] = 0
+					self.available_materials[key]["batch_no"][batch_no] = 0
 
 			if abs(qty) > 0 and not new_rm_obj:
 				self.__set_consumed_qty(rm_obj, qty)
@@ -313,29 +384,35 @@
 		rm_obj.consumed_qty = consumed_qty
 
 	def __set_batch_no_as_per_qty(self, item_row, rm_obj, batch_no, qty):
-		rm_obj.update({'consumed_qty': qty, 'batch_no': batch_no,
-			'required_qty': qty, 'purchase_order': item_row.purchase_order})
+		rm_obj.update(
+			{
+				"consumed_qty": qty,
+				"batch_no": batch_no,
+				"required_qty": qty,
+				"purchase_order": item_row.purchase_order,
+			}
+		)
 
 		self.__set_serial_nos(item_row, rm_obj)
 
 	def __set_serial_nos(self, item_row, rm_obj):
 		key = (rm_obj.rm_item_code, item_row.item_code, item_row.purchase_order)
-		if (self.available_materials.get(key) and self.available_materials[key]['serial_no']):
-			used_serial_nos = self.available_materials[key]['serial_no'][0: cint(rm_obj.consumed_qty)]
-			rm_obj.serial_no = '\n'.join(used_serial_nos)
+		if self.available_materials.get(key) and self.available_materials[key]["serial_no"]:
+			used_serial_nos = self.available_materials[key]["serial_no"][0 : cint(rm_obj.consumed_qty)]
+			rm_obj.serial_no = "\n".join(used_serial_nos)
 
 			# Removed the used serial nos from the list
 			for sn in used_serial_nos:
-				self.available_materials[key]['serial_no'].remove(sn)
+				self.available_materials[key]["serial_no"].remove(sn)
 
 	def set_consumed_qty_in_po(self):
 		# Update consumed qty back in the purchase order
-		if self.is_subcontracted != 'Yes':
+		if not self.is_subcontracted:
 			return
 
 		self.__get_purchase_orders()
 		itemwise_consumed_qty = defaultdict(float)
-		for doctype in ['Purchase Receipt', 'Purchase Invoice']:
+		for doctype in ["Purchase Receipt", "Purchase Invoice"]:
 			consumed_items, pr_items = self.__update_consumed_materials(doctype, return_consumed_items=True)
 
 			for row in consumed_items:
@@ -345,10 +422,12 @@
 		self.__update_consumed_qty_in_po(itemwise_consumed_qty)
 
 	def __update_consumed_qty_in_po(self, itemwise_consumed_qty):
-		fields = ['main_item_code', 'rm_item_code', 'parent', 'supplied_qty', 'name']
-		filters = {'docstatus': 1, 'parent': ('in', self.purchase_orders)}
+		fields = ["main_item_code", "rm_item_code", "parent", "supplied_qty", "name"]
+		filters = {"docstatus": 1, "parent": ("in", self.purchase_orders)}
 
-		for row in frappe.get_all('Purchase Order Item Supplied', fields = fields, filters=filters, order_by='idx'):
+		for row in frappe.get_all(
+			"Purchase Order Item Supplied", fields=fields, filters=filters, order_by="idx"
+		):
 			key = (row.rm_item_code, row.main_item_code, row.parent)
 			consumed_qty = itemwise_consumed_qty.get(key, 0)
 
@@ -356,10 +435,10 @@
 				consumed_qty = row.supplied_qty
 
 			itemwise_consumed_qty[key] -= consumed_qty
-			frappe.db.set_value('Purchase Order Item Supplied', row.name, 'consumed_qty', consumed_qty)
+			frappe.db.set_value("Purchase Order Item Supplied", row.name, "consumed_qty", consumed_qty)
 
 	def __validate_supplied_items(self):
-		if self.doctype not in ['Purchase Invoice', 'Purchase Receipt']:
+		if self.doctype not in ["Purchase Invoice", "Purchase Receipt"]:
 			return
 
 		for row in self.get(self.raw_material_table):
@@ -371,18 +450,20 @@
 			self.__validate_serial_no(row, key)
 
 	def __validate_batch_no(self, row, key):
-		if row.get('batch_no') and row.get('batch_no') not in self.__transferred_items.get(key).get('batch_no'):
-			link = get_link_to_form('Purchase Order', row.purchase_order)
+		if row.get("batch_no") and row.get("batch_no") not in self.__transferred_items.get(key).get(
+			"batch_no"
+		):
+			link = get_link_to_form("Purchase Order", row.purchase_order)
 			msg = f'The Batch No {frappe.bold(row.get("batch_no"))} has not supplied against the Purchase Order {link}'
 			frappe.throw(_(msg), title=_("Incorrect Batch Consumed"))
 
 	def __validate_serial_no(self, row, key):
-		if row.get('serial_no'):
-			serial_nos = get_serial_nos(row.get('serial_no'))
-			incorrect_sn = set(serial_nos).difference(self.__transferred_items.get(key).get('serial_no'))
+		if row.get("serial_no"):
+			serial_nos = get_serial_nos(row.get("serial_no"))
+			incorrect_sn = set(serial_nos).difference(self.__transferred_items.get(key).get("serial_no"))
 
 			if incorrect_sn:
 				incorrect_sn = "\n".join(incorrect_sn)
-				link = get_link_to_form('Purchase Order', row.purchase_order)
-				msg = f'The Serial Nos {incorrect_sn} has not supplied against the Purchase Order {link}'
+				link = get_link_to_form("Purchase Order", row.purchase_order)
+				msg = f"The Serial Nos {incorrect_sn} has not supplied against the Purchase Order {link}"
 				frappe.throw(_(msg), title=_("Incorrect Serial Number Consumed"))
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 2776628..2144055 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -37,6 +37,8 @@
 			self.set_discount_amount()
 			self.apply_discount_amount()
 
+		self.calculate_shipping_charges()
+
 		if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
 			self.calculate_total_advance()
 
@@ -50,7 +52,6 @@
 		self.initialize_taxes()
 		self.determine_exclusive_rate()
 		self.calculate_net_total()
-		self.calculate_shipping_charges()
 		self.calculate_taxes()
 		self.manipulate_grand_total_for_inclusive_tax()
 		self.calculate_totals()
@@ -58,23 +59,23 @@
 		self.calculate_total_net_weight()
 
 	def validate_item_tax_template(self):
-		for item in self.doc.get('items'):
-			if item.item_code and item.get('item_tax_template'):
+		for item in self.doc.get("items"):
+			if item.item_code and item.get("item_tax_template"):
 				item_doc = frappe.get_cached_doc("Item", item.item_code)
 				args = {
-					'net_rate': item.net_rate or item.rate,
-					'tax_category': self.doc.get('tax_category'),
-					'posting_date': self.doc.get('posting_date'),
-					'bill_date': self.doc.get('bill_date'),
-					'transaction_date': self.doc.get('transaction_date'),
-					'company': self.doc.get('company')
+					"net_rate": item.net_rate or item.rate,
+					"tax_category": self.doc.get("tax_category"),
+					"posting_date": self.doc.get("posting_date"),
+					"bill_date": self.doc.get("bill_date"),
+					"transaction_date": self.doc.get("transaction_date"),
+					"company": self.doc.get("company"),
 				}
 
 				item_group = item_doc.item_group
 				item_group_taxes = []
 
 				while item_group:
-					item_group_doc = frappe.get_cached_doc('Item Group', item_group)
+					item_group_doc = frappe.get_cached_doc("Item Group", item_group)
 					item_group_taxes += item_group_doc.taxes or []
 					item_group = item_group_doc.parent_item_group
 
@@ -89,9 +90,11 @@
 				if taxes:
 					if item.item_tax_template not in taxes:
 						item.item_tax_template = taxes[0]
-						frappe.msgprint(_("Row {0}: Item Tax template updated as per validity and rate applied").format(
-							item.idx, frappe.bold(item.item_code)
-						))
+						frappe.msgprint(
+							_("Row {0}: Item Tax template updated as per validity and rate applied").format(
+								item.idx, frappe.bold(item.item_code)
+							)
+						)
 
 	def validate_conversion_rate(self):
 		# validate conversion rate
@@ -100,13 +103,17 @@
 			self.doc.currency = company_currency
 			self.doc.conversion_rate = 1.0
 		else:
-			validate_conversion_rate(self.doc.currency, self.doc.conversion_rate,
-				self.doc.meta.get_label("conversion_rate"), self.doc.company)
+			validate_conversion_rate(
+				self.doc.currency,
+				self.doc.conversion_rate,
+				self.doc.meta.get_label("conversion_rate"),
+				self.doc.company,
+			)
 
 		self.doc.conversion_rate = flt(self.doc.conversion_rate)
 
 	def calculate_item_values(self):
-		if self.doc.get('is_consolidated'):
+		if self.doc.get("is_consolidated"):
 			return
 
 		if not self.discount_amount_applied:
@@ -117,16 +124,30 @@
 					item.rate = 0.0
 				elif item.price_list_rate:
 					if not item.rate or (item.pricing_rules and item.discount_percentage > 0):
-						item.rate = flt(item.price_list_rate *
-							(1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
-						item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
-					elif item.discount_amount and item.pricing_rules:
-						item.rate =  item.price_list_rate - item.discount_amount
+						item.rate = flt(
+							item.price_list_rate * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate")
+						)
 
-				if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item', 'POS Invoice Item', 'Purchase Invoice Item', 'Purchase Order Item', 'Purchase Receipt Item']:
+						item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
+
+					elif item.discount_amount and item.pricing_rules:
+						item.rate = item.price_list_rate - item.discount_amount
+
+				if item.doctype in [
+					"Quotation Item",
+					"Sales Order Item",
+					"Delivery Note Item",
+					"Sales Invoice Item",
+					"POS Invoice Item",
+					"Purchase Invoice Item",
+					"Purchase Order Item",
+					"Purchase Receipt Item",
+				]:
 					item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
 					if flt(item.rate_with_margin) > 0:
-						item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
+						item.rate = flt(
+							item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate")
+						)
 
 						if item.discount_amount and not item.discount_percentage:
 							item.rate = item.rate_with_margin - item.discount_amount
@@ -145,18 +166,22 @@
 				elif not item.qty and self.doc.get("is_debit_note"):
 					item.amount = flt(item.rate, item.precision("amount"))
 				else:
-					item.amount = flt(item.rate * item.qty,	item.precision("amount"))
+					item.amount = flt(item.rate * item.qty, item.precision("amount"))
 
 				item.net_amount = item.amount
 
-				self._set_in_company_currency(item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"])
+				self._set_in_company_currency(
+					item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"]
+				)
 
 				item.item_tax_amount = 0.0
 
 	def _set_in_company_currency(self, doc, fields):
 		"""set values in base currency"""
 		for f in fields:
-			val = flt(flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f))
+			val = flt(
+				flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f)
+			)
 			doc.set("base_" + f, val)
 
 	def initialize_taxes(self):
@@ -165,16 +190,22 @@
 				validate_taxes_and_charges(tax)
 				validate_inclusive_tax(tax, self.doc)
 
-			if not (self.doc.get('is_consolidated') or tax.get("dont_recompute_tax")):
+			if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
 				tax.item_wise_tax_detail = {}
 
-			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"]
+			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",
+			]
 
-			if tax.charge_type != "Actual" and \
-				not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
-					tax_fields.append("tax_amount")
+			if tax.charge_type != "Actual" and not (
+				self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
+			):
+				tax_fields.append("tax_amount")
 
 			for fieldname in tax_fields:
 				tax.set(fieldname, 0.0)
@@ -190,25 +221,32 @@
 			cumulated_tax_fraction = 0
 			total_inclusive_tax_amount_per_qty = 0
 			for i, tax in enumerate(self.doc.get("taxes")):
-				tax.tax_fraction_for_current_item, inclusive_tax_amount_per_qty = self.get_current_tax_fraction(tax, item_tax_map)
+				(
+					tax.tax_fraction_for_current_item,
+					inclusive_tax_amount_per_qty,
+				) = self.get_current_tax_fraction(tax, item_tax_map)
 
-				if i==0:
+				if i == 0:
 					tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
 				else:
-					tax.grand_total_fraction_for_current_item = \
-						self.doc.get("taxes")[i-1].grand_total_fraction_for_current_item \
+					tax.grand_total_fraction_for_current_item = (
+						self.doc.get("taxes")[i - 1].grand_total_fraction_for_current_item
 						+ tax.tax_fraction_for_current_item
+					)
 
 				cumulated_tax_fraction += tax.tax_fraction_for_current_item
 				total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
 
-			if not self.discount_amount_applied and item.qty and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty):
+			if (
+				not self.discount_amount_applied
+				and item.qty
+				and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty)
+			):
 				amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
 
 				item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
 				item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
-				item.discount_percentage = flt(item.discount_percentage,
-					item.precision("discount_percentage"))
+				item.discount_percentage = flt(item.discount_percentage, item.precision("discount_percentage"))
 
 				self._set_in_company_currency(item, ["net_rate", "net_amount"])
 
@@ -217,8 +255,8 @@
 
 	def get_current_tax_fraction(self, tax, item_tax_map):
 		"""
-			Get tax fraction for calculating tax exclusive amount
-			from tax inclusive amount
+		Get tax fraction for calculating tax exclusive amount
+		from tax inclusive amount
 		"""
 		current_tax_fraction = 0
 		inclusive_tax_amount_per_qty = 0
@@ -230,12 +268,14 @@
 				current_tax_fraction = tax_rate / 100.0
 
 			elif tax.charge_type == "On Previous Row Amount":
-				current_tax_fraction = (tax_rate / 100.0) * \
-					self.doc.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item
+				current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
+					cint(tax.row_id) - 1
+				].tax_fraction_for_current_item
 
 			elif tax.charge_type == "On Previous Row Total":
-				current_tax_fraction = (tax_rate / 100.0) * \
-					self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
+				current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
+					cint(tax.row_id) - 1
+				].grand_total_fraction_for_current_item
 
 			elif tax.charge_type == "On Item Quantity":
 				inclusive_tax_amount_per_qty = flt(tax_rate)
@@ -253,7 +293,9 @@
 			return tax.rate
 
 	def calculate_net_total(self):
-		self.doc.total_qty = self.doc.total = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0
+		self.doc.total_qty = (
+			self.doc.total
+		) = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0
 
 		for item in self.doc.get("items"):
 			self.doc.total += item.amount
@@ -265,17 +307,32 @@
 		self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
 
 	def calculate_shipping_charges(self):
+
+		# Do not apply shipping rule for POS
+		if self.doc.get("is_pos"):
+			return
+
 		if hasattr(self.doc, "shipping_rule") and self.doc.shipping_rule:
 			shipping_rule = frappe.get_doc("Shipping Rule", self.doc.shipping_rule)
 			shipping_rule.apply(self.doc)
 
+			self._calculate()
+
 	def calculate_taxes(self):
-		if not self.doc.get('is_consolidated'):
+		rounding_adjustment_computed = self.doc.get("is_consolidated") and self.doc.get(
+			"rounding_adjustment"
+		)
+		if not rounding_adjustment_computed:
 			self.doc.rounding_adjustment = 0
 
 		# maintain actual tax rate based on idx
-		actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
-			for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
+		actual_tax_dict = dict(
+			[
+				[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
+				for tax in self.doc.get("taxes")
+				if tax.charge_type == "Actual"
+			]
+		)
 
 		for n, item in enumerate(self.doc.get("items")):
 			item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
@@ -290,9 +347,10 @@
 						current_tax_amount += actual_tax_dict[tax.idx]
 
 				# accumulate tax amount into tax.tax_amount
-				if tax.charge_type != "Actual" and \
-					not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
-						tax.tax_amount += current_tax_amount
+				if tax.charge_type != "Actual" and not (
+					self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
+				):
+					tax.tax_amount += current_tax_amount
 
 				# store tax_amount for current item as it will be used for
 				# charge type = 'On Previous Row Amount'
@@ -305,17 +363,17 @@
 
 				# note: grand_total_for_current_item contains the contribution of
 				# item's amount, previously applied tax and the current tax on that item
-				if i==0:
+				if i == 0:
 					tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
 				else:
-					tax.grand_total_for_current_item = \
-						flt(self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount)
+					tax.grand_total_for_current_item = flt(
+						self.doc.get("taxes")[i - 1].grand_total_for_current_item + current_tax_amount
+					)
 
 				# set precision in the last item iteration
 				if n == len(self.doc.get("items")) - 1:
 					self.round_off_totals(tax)
-					self._set_in_company_currency(tax,
-						["tax_amount", "tax_amount_after_discount_amount"])
+					self._set_in_company_currency(tax, ["tax_amount", "tax_amount_after_discount_amount"])
 
 					self.round_off_base_values(tax)
 					self.set_cumulative_total(i, tax)
@@ -323,20 +381,29 @@
 					self._set_in_company_currency(tax, ["total"])
 
 					# adjust Discount Amount loss in last tax iteration
-					if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
-						and self.doc.discount_amount \
-						and self.doc.apply_discount_on == "Grand Total" \
-						and not self.doc.get('is_consolidated'):
-							self.doc.rounding_adjustment = flt(self.doc.grand_total
-								- flt(self.doc.discount_amount) - tax.total,
-								self.doc.precision("rounding_adjustment"))
+					if (
+						i == (len(self.doc.get("taxes")) - 1)
+						and self.discount_amount_applied
+						and self.doc.discount_amount
+						and self.doc.apply_discount_on == "Grand Total"
+						and not rounding_adjustment_computed
+					):
+						self.doc.rounding_adjustment = flt(
+							self.doc.grand_total - flt(self.doc.discount_amount) - tax.total,
+							self.doc.precision("rounding_adjustment"),
+						)
 
 	def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
 		# if just for valuation, do not add the tax amount in total
 		# if tax/charges is for deduction, multiply by -1
 		if getattr(tax, "category", None):
 			tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
-			if self.doc.doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt", "Supplier Quotation"]:
+			if self.doc.doctype in [
+				"Purchase Order",
+				"Purchase Invoice",
+				"Purchase Receipt",
+				"Supplier Quotation",
+			]:
 				tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
 		return tax_amount
 
@@ -347,7 +414,7 @@
 		if row_idx == 0:
 			tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
 		else:
-			tax.total = flt(self.doc.get("taxes")[row_idx-1].total + tax_amount, tax.precision("total"))
+			tax.total = flt(self.doc.get("taxes")[row_idx - 1].total + tax_amount, tax.precision("total"))
 
 	def get_current_tax_amount(self, item, tax, item_tax_map):
 		tax_rate = self._get_tax_rate(tax, item_tax_map)
@@ -356,16 +423,20 @@
 		if tax.charge_type == "Actual":
 			# distribute the tax amount proportionally to each item row
 			actual = flt(tax.tax_amount, tax.precision("tax_amount"))
-			current_tax_amount = item.net_amount*actual / self.doc.net_total if self.doc.net_total else 0.0
+			current_tax_amount = (
+				item.net_amount * actual / self.doc.net_total if self.doc.net_total else 0.0
+			)
 
 		elif tax.charge_type == "On Net Total":
 			current_tax_amount = (tax_rate / 100.0) * item.net_amount
 		elif tax.charge_type == "On Previous Row Amount":
-			current_tax_amount = (tax_rate / 100.0) * \
-				self.doc.get("taxes")[cint(tax.row_id) - 1].tax_amount_for_current_item
+			current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
+				cint(tax.row_id) - 1
+			].tax_amount_for_current_item
 		elif tax.charge_type == "On Previous Row Total":
-			current_tax_amount = (tax_rate / 100.0) * \
-				self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
+			current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
+				cint(tax.row_id) - 1
+			].grand_total_for_current_item
 		elif tax.charge_type == "On Item Quantity":
 			current_tax_amount = tax_rate * item.qty
 
@@ -377,11 +448,11 @@
 	def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
 		# store tax breakup for each item
 		key = item.item_code or item.item_name
-		item_wise_tax_amount = current_tax_amount*self.doc.conversion_rate
+		item_wise_tax_amount = current_tax_amount * self.doc.conversion_rate
 		if tax.item_wise_tax_detail.get(key):
 			item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
 
-		tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount)]
+		tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount)]
 
 	def round_off_totals(self, tax):
 		if tax.account_head in frappe.flags.round_off_applicable_accounts:
@@ -389,8 +460,9 @@
 			tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
 
 		tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
-		tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
-			tax.precision("tax_amount"))
+		tax.tax_amount_after_discount_amount = flt(
+			tax.tax_amount_after_discount_amount, tax.precision("tax_amount")
+		)
 
 	def round_off_base_values(self, tax):
 		# Round off to nearest integer based on regional settings
@@ -402,11 +474,15 @@
 		# if fully inclusive taxes and diff
 		if self.doc.get("taxes") and any(cint(t.included_in_print_rate) for t in self.doc.get("taxes")):
 			last_tax = self.doc.get("taxes")[-1]
-			non_inclusive_tax_amount = sum(flt(d.tax_amount_after_discount_amount)
-				for d in self.doc.get("taxes") if not d.included_in_print_rate)
+			non_inclusive_tax_amount = sum(
+				flt(d.tax_amount_after_discount_amount)
+				for d in self.doc.get("taxes")
+				if not d.included_in_print_rate
+			)
 
-			diff = self.doc.total + non_inclusive_tax_amount \
-				- flt(last_tax.total, last_tax.precision("total"))
+			diff = (
+				self.doc.total + non_inclusive_tax_amount - flt(last_tax.total, last_tax.precision("total"))
+			)
 
 			# If discount amount applied, deduct the discount amount
 			# because self.doc.total is always without discount, but last_tax.total is after discount
@@ -415,7 +491,7 @@
 
 			diff = flt(diff, self.doc.precision("rounding_adjustment"))
 
-			if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")):
+			if diff and abs(diff) <= (5.0 / 10 ** last_tax.precision("tax_amount")):
 				self.doc.rounding_adjustment = diff
 
 	def calculate_totals(self):
@@ -425,16 +501,27 @@
 			self.doc.grand_total = flt(self.doc.net_total)
 
 		if self.doc.get("taxes"):
-			self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
-			- flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges"))
+			self.doc.total_taxes_and_charges = flt(
+				self.doc.grand_total - self.doc.net_total - flt(self.doc.rounding_adjustment),
+				self.doc.precision("total_taxes_and_charges"),
+			)
 		else:
 			self.doc.total_taxes_and_charges = 0.0
 
 		self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
 
-		if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"]:
-			self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate, self.doc.precision("base_grand_total")) \
-				if self.doc.total_taxes_and_charges else self.doc.base_net_total
+		if self.doc.doctype in [
+			"Quotation",
+			"Sales Order",
+			"Delivery Note",
+			"Sales Invoice",
+			"POS Invoice",
+		]:
+			self.doc.base_grand_total = (
+				flt(self.doc.grand_total * self.doc.conversion_rate, self.doc.precision("base_grand_total"))
+				if self.doc.total_taxes_and_charges
+				else self.doc.base_net_total
+			)
 		else:
 			self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
 			for tax in self.doc.get("taxes"):
@@ -446,58 +533,70 @@
 
 			self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
 
-			self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
-				if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \
+			self.doc.base_grand_total = (
+				flt(self.doc.grand_total * self.doc.conversion_rate)
+				if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted)
 				else self.doc.base_net_total
+			)
 
-			self._set_in_company_currency(self.doc,
-				["taxes_and_charges_added", "taxes_and_charges_deducted"])
+			self._set_in_company_currency(
+				self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"]
+			)
 
 		self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
 
 		self.set_rounded_total()
 
 	def calculate_total_net_weight(self):
-		if self.doc.meta.get_field('total_net_weight'):
+		if self.doc.meta.get_field("total_net_weight"):
 			self.doc.total_net_weight = 0.0
 			for d in self.doc.items:
 				if d.total_weight:
 					self.doc.total_net_weight += d.total_weight
 
 	def set_rounded_total(self):
-		if not self.doc.get('is_consolidated'):
-			if self.doc.meta.get_field("rounded_total"):
-				if self.doc.is_rounded_total_disabled():
-					self.doc.rounded_total = self.doc.base_rounded_total = 0
-					return
+		if self.doc.get("is_consolidated") and self.doc.get("rounding_adjustment"):
+			return
 
-				self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
-					self.doc.currency, self.doc.precision("rounded_total"))
+		if self.doc.meta.get_field("rounded_total"):
+			if self.doc.is_rounded_total_disabled():
+				self.doc.rounded_total = self.doc.base_rounded_total = 0
+				return
 
-				#if print_in_rate is set, we would have already calculated rounding adjustment
-				self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
-					self.doc.precision("rounding_adjustment"))
+			self.doc.rounded_total = round_based_on_smallest_currency_fraction(
+				self.doc.grand_total, self.doc.currency, self.doc.precision("rounded_total")
+			)
 
-				self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
+			# if print_in_rate is set, we would have already calculated rounding adjustment
+			self.doc.rounding_adjustment += flt(
+				self.doc.rounded_total - self.doc.grand_total, self.doc.precision("rounding_adjustment")
+			)
+
+			self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
 
 	def _cleanup(self):
-		if not self.doc.get('is_consolidated'):
+		if not self.doc.get("is_consolidated"):
 			for tax in self.doc.get("taxes"):
 				if not tax.get("dont_recompute_tax"):
-					tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
+					tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(",", ":"))
 
 	def set_discount_amount(self):
 		if self.doc.additional_discount_percentage:
-			self.doc.discount_amount = flt(flt(self.doc.get(scrub(self.doc.apply_discount_on)))
-				* self.doc.additional_discount_percentage / 100, self.doc.precision("discount_amount"))
+			self.doc.discount_amount = flt(
+				flt(self.doc.get(scrub(self.doc.apply_discount_on)))
+				* self.doc.additional_discount_percentage
+				/ 100,
+				self.doc.precision("discount_amount"),
+			)
 
 	def apply_discount_amount(self):
 		if self.doc.discount_amount:
 			if not self.doc.apply_discount_on:
 				frappe.throw(_("Please select Apply Discount On"))
 
-			self.doc.base_discount_amount = flt(self.doc.discount_amount * self.doc.conversion_rate,
-				self.doc.precision("base_discount_amount"))
+			self.doc.base_discount_amount = flt(
+				self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount")
+			)
 
 			total_for_discount_amount = self.get_total_for_discount_amount()
 			taxes = self.doc.get("taxes")
@@ -506,20 +605,24 @@
 			if total_for_discount_amount:
 				# calculate item amount after Discount Amount
 				for i, item in enumerate(self.doc.get("items")):
-					distributed_amount = flt(self.doc.discount_amount) * \
-						item.net_amount / total_for_discount_amount
+					distributed_amount = (
+						flt(self.doc.discount_amount) * item.net_amount / total_for_discount_amount
+					)
 
 					item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
 					net_total += item.net_amount
 
 					# discount amount rounding loss adjustment if no taxes
-					if (self.doc.apply_discount_on == "Net Total" or not taxes or total_for_discount_amount==self.doc.net_total) \
-						and i == len(self.doc.get("items")) - 1:
-							discount_amount_loss = flt(self.doc.net_total - net_total - self.doc.discount_amount,
-								self.doc.precision("net_total"))
+					if (
+						self.doc.apply_discount_on == "Net Total"
+						or not taxes
+						or total_for_discount_amount == self.doc.net_total
+					) and i == len(self.doc.get("items")) - 1:
+						discount_amount_loss = flt(
+							self.doc.net_total - net_total - self.doc.discount_amount, self.doc.precision("net_total")
+						)
 
-							item.net_amount = flt(item.net_amount + discount_amount_loss,
-								item.precision("net_amount"))
+						item.net_amount = flt(item.net_amount + discount_amount_loss, item.precision("net_amount"))
 
 					item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate")) if item.qty else 0
 
@@ -544,42 +647,56 @@
 					actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
 					actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
 
-			return flt(self.doc.grand_total - sum(actual_taxes_dict.values()),
-				self.doc.precision("grand_total"))
-
+			return flt(
+				self.doc.grand_total - sum(actual_taxes_dict.values()), self.doc.precision("grand_total")
+			)
 
 	def calculate_total_advance(self):
 		if self.doc.docstatus < 2:
-			total_allocated_amount = sum(flt(adv.allocated_amount, adv.precision("allocated_amount"))
-				for adv in self.doc.get("advances"))
+			total_allocated_amount = sum(
+				flt(adv.allocated_amount, adv.precision("allocated_amount"))
+				for adv in self.doc.get("advances")
+			)
 
 			self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
 
 			grand_total = self.doc.rounded_total or self.doc.grand_total
 
 			if self.doc.party_account_currency == self.doc.currency:
-				invoice_total = flt(grand_total - flt(self.doc.write_off_amount),
-					self.doc.precision("grand_total"))
+				invoice_total = flt(
+					grand_total - flt(self.doc.write_off_amount), self.doc.precision("grand_total")
+				)
 			else:
-				base_write_off_amount = flt(flt(self.doc.write_off_amount) * self.doc.conversion_rate,
-					self.doc.precision("base_write_off_amount"))
-				invoice_total = flt(grand_total * self.doc.conversion_rate,
-					self.doc.precision("grand_total")) - base_write_off_amount
+				base_write_off_amount = flt(
+					flt(self.doc.write_off_amount) * self.doc.conversion_rate,
+					self.doc.precision("base_write_off_amount"),
+				)
+				invoice_total = (
+					flt(grand_total * self.doc.conversion_rate, self.doc.precision("grand_total"))
+					- base_write_off_amount
+				)
 
 			if invoice_total > 0 and self.doc.total_advance > invoice_total:
-				frappe.throw(_("Advance amount cannot be greater than {0} {1}")
-					.format(self.doc.party_account_currency, invoice_total))
+				frappe.throw(
+					_("Advance amount cannot be greater than {0} {1}").format(
+						self.doc.party_account_currency, invoice_total
+					)
+				)
 
 			if self.doc.docstatus == 0:
+				if self.doc.get("write_off_outstanding_amount_automatically"):
+					self.doc.write_off_amount = 0
+
 				self.calculate_outstanding_amount()
+				self.calculate_write_off_amount()
 
 	def is_internal_invoice(self):
 		"""
-			Checks if its an internal transfer invoice
-			and decides if to calculate any out standing amount or not
+		Checks if its an internal transfer invoice
+		and decides if to calculate any out standing amount or not
 		"""
 
-		if self.doc.doctype in ('Sales Invoice', 'Purchase Invoice') and self.doc.is_internal_transfer():
+		if self.doc.doctype in ("Sales Invoice", "Purchase Invoice") and self.doc.is_internal_transfer():
 			return True
 
 		return False
@@ -591,39 +708,62 @@
 		if self.doc.doctype == "Sales Invoice":
 			self.calculate_paid_amount()
 
-		if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos') or \
-			self.is_internal_invoice(): return
+		if (
+			self.doc.is_return
+			and self.doc.return_against
+			and not self.doc.get("is_pos")
+			or self.is_internal_invoice()
+		):
+			return
 
 		self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
-		self._set_in_company_currency(self.doc, ['write_off_amount'])
+		self._set_in_company_currency(self.doc, ["write_off_amount"])
 
 		if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
 			grand_total = self.doc.rounded_total or self.doc.grand_total
 			base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
 
 			if self.doc.party_account_currency == self.doc.currency:
-				total_amount_to_pay = flt(grand_total - self.doc.total_advance
-					- flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
+				total_amount_to_pay = flt(
+					grand_total - self.doc.total_advance - flt(self.doc.write_off_amount),
+					self.doc.precision("grand_total"),
+				)
 			else:
-				total_amount_to_pay = flt(flt(base_grand_total, self.doc.precision("base_grand_total")) - self.doc.total_advance
-						- flt(self.doc.base_write_off_amount), self.doc.precision("base_grand_total"))
+				total_amount_to_pay = flt(
+					flt(base_grand_total, self.doc.precision("base_grand_total"))
+					- self.doc.total_advance
+					- flt(self.doc.base_write_off_amount),
+					self.doc.precision("base_grand_total"),
+				)
 
 			self.doc.round_floats_in(self.doc, ["paid_amount"])
 			change_amount = 0
 
-			if self.doc.doctype == "Sales Invoice" and not self.doc.get('is_return'):
-				self.calculate_write_off_amount()
+			if self.doc.doctype == "Sales Invoice" and not self.doc.get("is_return"):
 				self.calculate_change_amount()
-				change_amount = self.doc.change_amount \
-					if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
+				change_amount = (
+					self.doc.change_amount
+					if self.doc.party_account_currency == self.doc.currency
+					else self.doc.base_change_amount
+				)
 
-			paid_amount = self.doc.paid_amount \
-				if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
+			paid_amount = (
+				self.doc.paid_amount
+				if self.doc.party_account_currency == self.doc.currency
+				else self.doc.base_paid_amount
+			)
 
-			self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(change_amount),
-				self.doc.precision("outstanding_amount"))
+			self.doc.outstanding_amount = flt(
+				total_amount_to_pay - flt(paid_amount) + flt(change_amount),
+				self.doc.precision("outstanding_amount"),
+			)
 
-			if self.doc.doctype == 'Sales Invoice' and self.doc.get('is_pos') and self.doc.get('is_return'):
+			if (
+				self.doc.doctype == "Sales Invoice"
+				and self.doc.get("is_pos")
+				and self.doc.get("is_return")
+				and not self.doc.get("is_consolidated")
+			):
 				self.set_total_amount_to_default_mop(total_amount_to_pay)
 				self.calculate_paid_amount()
 
@@ -632,17 +772,17 @@
 		paid_amount = base_paid_amount = 0.0
 
 		if self.doc.is_pos:
-			for payment in self.doc.get('payments'):
+			for payment in self.doc.get("payments"):
 				payment.amount = flt(payment.amount)
 				payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
 				paid_amount += payment.amount
 				base_paid_amount += payment.base_amount
 		elif not self.doc.is_return:
-			self.doc.set('payments', [])
+			self.doc.set("payments", [])
 
 		if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
 			base_paid_amount += self.doc.loyalty_amount
-			paid_amount += (self.doc.loyalty_amount / flt(self.doc.conversion_rate))
+			paid_amount += self.doc.loyalty_amount / flt(self.doc.conversion_rate)
 
 		self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
 		self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
@@ -653,22 +793,31 @@
 		grand_total = self.doc.rounded_total or self.doc.grand_total
 		base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
 
-		if self.doc.doctype == "Sales Invoice" \
-			and self.doc.paid_amount > grand_total and not self.doc.is_return \
-			and any(d.type == "Cash" for d in self.doc.payments):
+		if (
+			self.doc.doctype == "Sales Invoice"
+			and self.doc.paid_amount > grand_total
+			and not self.doc.is_return
+			and any(d.type == "Cash" for d in self.doc.payments)
+		):
+			self.doc.change_amount = flt(
+				self.doc.paid_amount - grand_total, self.doc.precision("change_amount")
+			)
 
-			self.doc.change_amount = flt(self.doc.paid_amount - grand_total +
-				self.doc.write_off_amount, self.doc.precision("change_amount"))
-
-			self.doc.base_change_amount = flt(self.doc.base_paid_amount - base_grand_total +
-				self.doc.base_write_off_amount, self.doc.precision("base_change_amount"))
+			self.doc.base_change_amount = flt(
+				self.doc.base_paid_amount - base_grand_total, self.doc.precision("base_change_amount")
+			)
 
 	def calculate_write_off_amount(self):
-		if flt(self.doc.change_amount) > 0:
-			self.doc.write_off_amount = flt(self.doc.grand_total - self.doc.paid_amount
-				+ self.doc.change_amount, self.doc.precision("write_off_amount"))
-			self.doc.base_write_off_amount = flt(self.doc.write_off_amount * self.doc.conversion_rate,
-				self.doc.precision("base_write_off_amount"))
+		if self.doc.get("write_off_outstanding_amount_automatically"):
+			self.doc.write_off_amount = flt(
+				self.doc.outstanding_amount, self.doc.precision("write_off_amount")
+			)
+			self.doc.base_write_off_amount = flt(
+				self.doc.write_off_amount * self.doc.conversion_rate,
+				self.doc.precision("base_write_off_amount"),
+			)
+
+			self.calculate_outstanding_amount()
 
 	def calculate_margin(self, item):
 		rate_with_margin = 0.0
@@ -677,10 +826,15 @@
 			if item.pricing_rules and not self.doc.ignore_pricing_rule:
 				has_margin = False
 				for d in get_applied_pricing_rules(item.pricing_rules):
-					pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
+					pricing_rule = frappe.get_cached_doc("Pricing Rule", d)
 
-					if pricing_rule.margin_rate_or_amount and ((pricing_rule.currency == self.doc.currency and
-						pricing_rule.margin_type in ['Amount', 'Percentage']) or pricing_rule.margin_type == 'Percentage'):
+					if pricing_rule.margin_rate_or_amount and (
+						(
+							pricing_rule.currency == self.doc.currency
+							and pricing_rule.margin_type in ["Amount", "Percentage"]
+						)
+						or pricing_rule.margin_type == "Percentage"
+					):
 						item.margin_type = pricing_rule.margin_type
 						item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
 						has_margin = True
@@ -691,12 +845,17 @@
 
 			if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
 				item.margin_type = "Amount"
-				item.margin_rate_or_amount = flt(item.rate - item.price_list_rate,
-						item.precision("margin_rate_or_amount"))
+				item.margin_rate_or_amount = flt(
+					item.rate - item.price_list_rate, item.precision("margin_rate_or_amount")
+				)
 				item.rate_with_margin = item.rate
 
 			elif item.margin_type and item.margin_rate_or_amount:
-				margin_value = item.margin_rate_or_amount if item.margin_type == 'Amount' else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
+				margin_value = (
+					item.margin_rate_or_amount
+					if item.margin_type == "Amount"
+					else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
+				)
 				rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
 				base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
 
@@ -706,16 +865,24 @@
 		self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
 
 	def set_total_amount_to_default_mop(self, total_amount_to_pay):
-		default_mode_of_payment = frappe.db.get_value('POS Payment Method',
-			{'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1)
+		default_mode_of_payment = frappe.db.get_value(
+			"POS Payment Method",
+			{"parent": self.doc.pos_profile, "default": 1},
+			["mode_of_payment"],
+			as_dict=1,
+		)
 
 		if default_mode_of_payment:
 			self.doc.payments = []
-			self.doc.append('payments', {
-				'mode_of_payment': default_mode_of_payment.mode_of_payment,
-				'amount': total_amount_to_pay,
-				'default': 1
-			})
+			self.doc.append(
+				"payments",
+				{
+					"mode_of_payment": default_mode_of_payment.mode_of_payment,
+					"amount": total_amount_to_pay,
+					"default": 1,
+				},
+			)
+
 
 def get_itemised_tax_breakup_html(doc):
 	if not doc.taxes:
@@ -725,7 +892,7 @@
 	# get headers
 	tax_accounts = []
 	for tax in doc.taxes:
-		if getattr(tax, "category", None) and tax.category=="Valuation":
+		if getattr(tax, "category", None) and tax.category == "Valuation":
 			continue
 		if tax.description not in tax_accounts:
 			tax_accounts.append(tax.description)
@@ -741,34 +908,40 @@
 	frappe.flags.company = None
 
 	return frappe.render_template(
-		"templates/includes/itemised_tax_breakup.html", dict(
+		"templates/includes/itemised_tax_breakup.html",
+		dict(
 			headers=headers,
 			itemised_tax=itemised_tax,
 			itemised_taxable_amount=itemised_taxable_amount,
 			tax_accounts=tax_accounts,
-			doc=doc
-		)
+			doc=doc,
+		),
 	)
 
+
 @frappe.whitelist()
 def get_round_off_applicable_accounts(company, account_list):
 	account_list = get_regional_round_off_accounts(company, account_list)
 
 	return account_list
 
+
 @erpnext.allow_regional
 def get_regional_round_off_accounts(company, account_list):
 	pass
 
+
 @erpnext.allow_regional
 def update_itemised_tax_data(doc):
-	#Don't delete this method, used for localization
+	# Don't delete this method, used for localization
 	pass
 
+
 @erpnext.allow_regional
 def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
 	return [_("Item"), _("Taxable Amount")] + tax_accounts
 
+
 @erpnext.allow_regional
 def get_itemised_tax_breakup_data(doc):
 	itemised_tax = get_itemised_tax(doc.taxes)
@@ -777,10 +950,11 @@
 
 	return itemised_tax, itemised_taxable_amount
 
+
 def get_itemised_tax(taxes, with_tax_account=False):
 	itemised_tax = {}
 	for tax in taxes:
-		if getattr(tax, "category", None) and tax.category=="Valuation":
+		if getattr(tax, "category", None) and tax.category == "Valuation":
 			continue
 
 		item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
@@ -797,16 +971,16 @@
 				else:
 					tax_rate = flt(tax_data)
 
-				itemised_tax[item_code][tax.description] = frappe._dict(dict(
-					tax_rate = tax_rate,
-					tax_amount = tax_amount
-				))
+				itemised_tax[item_code][tax.description] = frappe._dict(
+					dict(tax_rate=tax_rate, tax_amount=tax_amount)
+				)
 
 				if with_tax_account:
 					itemised_tax[item_code][tax.description].tax_account = tax.account_head
 
 	return itemised_tax
 
+
 def get_itemised_taxable_amount(items):
 	itemised_taxable_amount = frappe._dict()
 	for item in items:
@@ -816,16 +990,18 @@
 
 	return itemised_taxable_amount
 
+
 def get_rounded_tax_amount(itemised_tax, precision):
 	# Rounding based on tax_amount precision
 	for taxes in itemised_tax.values():
 		for tax_account in taxes:
 			taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
 
+
 class init_landed_taxes_and_totals(object):
 	def __init__(self, doc):
 		self.doc = doc
-		self.tax_field = 'taxes' if self.doc.doctype == 'Landed Cost Voucher' else 'additional_costs'
+		self.tax_field = "taxes" if self.doc.doctype == "Landed Cost Voucher" else "additional_costs"
 		self.set_account_currency()
 		self.set_exchange_rate()
 		self.set_amounts_in_company_currency()
@@ -834,7 +1010,7 @@
 		company_currency = erpnext.get_company_currency(self.doc.company)
 		for d in self.doc.get(self.tax_field):
 			if not d.account_currency:
-				account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency')
+				account_currency = frappe.db.get_value("Account", d.expense_account, "account_currency")
 				d.account_currency = account_currency or company_currency
 
 	def set_exchange_rate(self):
@@ -843,8 +1019,12 @@
 			if d.account_currency == company_currency:
 				d.exchange_rate = 1
 			elif not d.exchange_rate:
-				d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
-					account_currency=d.account_currency, company=self.doc.company)
+				d.exchange_rate = get_exchange_rate(
+					self.doc.posting_date,
+					account=d.expense_account,
+					account_currency=d.account_currency,
+					company=self.doc.company,
+				)
 
 			if not d.exchange_rate:
 				frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
diff --git a/erpnext/controllers/tests/test_item_variant.py b/erpnext/controllers/tests/test_item_variant.py
index 5c6e06a..68c8d2c 100644
--- a/erpnext/controllers/tests/test_item_variant.py
+++ b/erpnext/controllers/tests/test_item_variant.py
@@ -12,11 +12,12 @@
 
 class TestItemVariant(unittest.TestCase):
 	def test_tables_in_template_copied_to_variant(self):
-		fields = [{'field_name': 'quality_inspection_template'}]
+		fields = [{"field_name": "quality_inspection_template"}]
 		set_item_variant_settings(fields)
 		variant = make_item_variant()
 		self.assertEqual(variant.get("quality_inspection_template"), "_Test QC Template")
 
+
 def create_variant_with_tables(item, args):
 	if isinstance(args, str):
 		args = json.loads(args)
@@ -27,14 +28,11 @@
 	template.save()
 
 	variant = frappe.new_doc("Item")
-	variant.variant_based_on = 'Item Attribute'
+	variant.variant_based_on = "Item Attribute"
 	variant_attributes = []
 
 	for d in template.attributes:
-		variant_attributes.append({
-			"attribute": d.attribute,
-			"attribute_value": args.get(d.attribute)
-		})
+		variant_attributes.append({"attribute": d.attribute, "attribute_value": args.get(d.attribute)})
 
 	variant.set("attributes", variant_attributes)
 	copy_attributes_to_variant(template, variant)
@@ -42,6 +40,7 @@
 
 	return variant
 
+
 def make_item_variant():
 	frappe.delete_doc_if_exists("Item", "_Test Variant Item-XSL", force=1)
 	variant = create_variant_with_tables("_Test Variant Item", '{"Test Size": "Extra Small"}')
@@ -50,6 +49,7 @@
 	variant.save()
 	return variant
 
+
 def make_quality_inspection_template():
 	qc_template = "_Test QC Template"
 	if frappe.db.exists("Quality Inspection Template", qc_template):
@@ -59,10 +59,13 @@
 	qc.quality_inspection_template_name = qc_template
 
 	create_quality_inspection_parameter("Moisture")
-	qc.append('item_quality_inspection_parameter', {
-		"specification": "Moisture",
-		"value": "&lt; 5%",
-	})
+	qc.append(
+		"item_quality_inspection_parameter",
+		{
+			"specification": "Moisture",
+			"value": "&lt; 5%",
+		},
+	)
 
 	qc.insert()
 	return qc.name
diff --git a/erpnext/controllers/tests/test_mapper.py b/erpnext/controllers/tests/test_mapper.py
index e755876..919bcda 100644
--- a/erpnext/controllers/tests/test_mapper.py
+++ b/erpnext/controllers/tests/test_mapper.py
@@ -10,14 +10,14 @@
 
 class TestMapper(unittest.TestCase):
 	def test_map_docs(self):
-		'''Test mapping of multiple source docs on a single target doc'''
+		"""Test mapping of multiple source docs on a single target doc"""
 
 		make_test_records("Item")
-		items = ['_Test Item', '_Test Item 2', '_Test FG Item']
+		items = ["_Test Item", "_Test Item 2", "_Test FG Item"]
 
 		# Make source docs (quotations) and a target doc (sales order)
-		qtn1, item_list_1 = self.make_quotation(items, '_Test Customer')
-		qtn2, item_list_2 = self.make_quotation(items, '_Test Customer')
+		qtn1, item_list_1 = self.make_quotation(items, "_Test Customer")
+		qtn2, item_list_2 = self.make_quotation(items, "_Test Customer")
 		so, item_list_3 = self.make_sales_order()
 
 		# Map source docs to target with corresponding mapper method
@@ -26,20 +26,20 @@
 
 		# Assert that all inserted items are present in updated sales order
 		src_items = item_list_1 + item_list_2 + item_list_3
-		self.assertEqual(set(d for d in src_items),
-			set(d.item_code for d in updated_so.items))
-
+		self.assertEqual(set(d for d in src_items), set(d.item_code for d in updated_so.items))
 
 	def make_quotation(self, item_list, customer):
 
-		qtn = frappe.get_doc({
-			"doctype": "Quotation",
-			"quotation_to": "Customer",
-			"party_name": customer,
-			"order_type": "Sales",
-			"transaction_date" : nowdate(),
-			"valid_till" : add_months(nowdate(), 1)
-		})
+		qtn = frappe.get_doc(
+			{
+				"doctype": "Quotation",
+				"quotation_to": "Customer",
+				"party_name": customer,
+				"order_type": "Sales",
+				"transaction_date": nowdate(),
+				"valid_till": add_months(nowdate(), 1),
+			}
+		)
 		for item in item_list:
 			qtn.append("items", {"qty": "2", "item_code": item})
 
@@ -47,21 +47,23 @@
 		return qtn, item_list
 
 	def make_sales_order(self):
-		item = frappe.get_doc({
-			"base_amount": 1000.0,
-			"base_rate": 100.0,
-			"description": "CPU",
-			"doctype": "Sales Order Item",
-			"item_code": "_Test Item",
-			"item_name": "CPU",
-			"parentfield": "items",
-			"qty": 10.0,
-			"rate": 100.0,
-			"warehouse": "_Test Warehouse - _TC",
-			"stock_uom": "_Test UOM",
-			"conversion_factor": 1.0,
-			"uom": "_Test UOM"
-		})
-		so = frappe.get_doc(frappe.get_test_records('Sales Order')[0])
+		item = frappe.get_doc(
+			{
+				"base_amount": 1000.0,
+				"base_rate": 100.0,
+				"description": "CPU",
+				"doctype": "Sales Order Item",
+				"item_code": "_Test Item",
+				"item_name": "CPU",
+				"parentfield": "items",
+				"qty": 10.0,
+				"rate": 100.0,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "_Test UOM",
+				"conversion_factor": 1.0,
+				"uom": "_Test UOM",
+			}
+		)
+		so = frappe.get_doc(frappe.get_test_records("Sales Order")[0])
 		so.insert(ignore_permissions=True)
 		return so, [item.item_code]
diff --git a/erpnext/controllers/tests/test_qty_based_taxes.py b/erpnext/controllers/tests/test_qty_based_taxes.py
index 49b844b..2e9dfd2 100644
--- a/erpnext/controllers/tests/test_qty_based_taxes.py
+++ b/erpnext/controllers/tests/test_qty_based_taxes.py
@@ -5,104 +5,131 @@
 
 
 def uuid4():
-    return str(_uuid4())
+	return str(_uuid4())
+
 
 class TestTaxes(unittest.TestCase):
-    def setUp(self):
-        self.company = frappe.get_doc({
-            'doctype': 'Company',
-            'company_name': uuid4(),
-            'abbr': ''.join(s[0] for s in uuid4().split('-')),
-            'default_currency': 'USD',
-            'country': 'United States',
-        }).insert()
-        self.account = frappe.get_doc({
-            'doctype': 'Account',
-            'account_name': uuid4(),
-            'account_type': 'Tax',
-            'company': self.company.name,
-            'parent_account': 'Duties and Taxes - {self.company.abbr}'.format(self=self)
-        }).insert()
-        self.item_group = frappe.get_doc({
-            'doctype': 'Item Group',
-            'item_group_name': uuid4(),
-            'parent_item_group': 'All Item Groups',
-        }).insert()
-        self.item_tax_template = frappe.get_doc({
-            'doctype': 'Item Tax Template',
-            'title': uuid4(),
-            'company': self.company.name,
-            'taxes': [
-                {
-                    'tax_type': self.account.name,
-                    'tax_rate': 2,
-                }
-            ]
-        }).insert()
-        self.item = frappe.get_doc({
-            'doctype': 'Item',
-            'item_code': uuid4(),
-            'item_group': self.item_group.name,
-            'is_stock_item': 0,
-            'taxes': [
-                {
-                    'item_tax_template': self.item_tax_template.name,
-                    'tax_category': '',
-                }
-            ],
-        }).insert()
-        self.customer = frappe.get_doc({
-            'doctype': 'Customer',
-            'customer_name': uuid4(),
-            'customer_group': 'All Customer Groups',
-        }).insert()
-        self.supplier = frappe.get_doc({
-            'doctype': 'Supplier',
-            'supplier_name': uuid4(),
-            'supplier_group': 'All Supplier Groups',
-        }).insert()
+	def setUp(self):
+		self.company = frappe.get_doc(
+			{
+				"doctype": "Company",
+				"company_name": uuid4(),
+				"abbr": "".join(s[0] for s in uuid4().split("-")),
+				"default_currency": "USD",
+				"country": "United States",
+			}
+		).insert()
+		self.account = frappe.get_doc(
+			{
+				"doctype": "Account",
+				"account_name": uuid4(),
+				"account_type": "Tax",
+				"company": self.company.name,
+				"parent_account": "Duties and Taxes - {self.company.abbr}".format(self=self),
+			}
+		).insert()
+		self.item_group = frappe.get_doc(
+			{
+				"doctype": "Item Group",
+				"item_group_name": uuid4(),
+				"parent_item_group": "All Item Groups",
+			}
+		).insert()
+		self.item_tax_template = frappe.get_doc(
+			{
+				"doctype": "Item Tax Template",
+				"title": uuid4(),
+				"company": self.company.name,
+				"taxes": [
+					{
+						"tax_type": self.account.name,
+						"tax_rate": 2,
+					}
+				],
+			}
+		).insert()
+		self.item = frappe.get_doc(
+			{
+				"doctype": "Item",
+				"item_code": uuid4(),
+				"item_group": self.item_group.name,
+				"is_stock_item": 0,
+				"taxes": [
+					{
+						"item_tax_template": self.item_tax_template.name,
+						"tax_category": "",
+					}
+				],
+			}
+		).insert()
+		self.customer = frappe.get_doc(
+			{
+				"doctype": "Customer",
+				"customer_name": uuid4(),
+				"customer_group": "All Customer Groups",
+			}
+		).insert()
+		self.supplier = frappe.get_doc(
+			{
+				"doctype": "Supplier",
+				"supplier_name": uuid4(),
+				"supplier_group": "All Supplier Groups",
+			}
+		).insert()
 
-    def test_taxes(self):
-        self.created_docs = []
-        for dt in ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice',
-                'Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']:
-            doc = frappe.get_doc({
-                'doctype': dt,
-                'company': self.company.name,
-                'supplier': self.supplier.name,
-                'currency': "USD",
-                'schedule_date': frappe.utils.nowdate(),
-                'delivery_date': frappe.utils.nowdate(),
-                'customer': self.customer.name,
-                'buying_price_list' if dt.startswith('Purchase') else 'selling_price_list'
-                : 'Standard Buying' if dt.startswith('Purchase') else 'Standard Selling',
-                'items': [
-                    {
-                        'item_code': self.item.name,
-                        'qty': 300,
-                        'rate': 100,
-                    }
-                ],
-                'taxes': [
-                    {
-                        'charge_type': 'On Item Quantity',
-                        'account_head': self.account.name,
-                        'description': 'N/A',
-                        'rate': 0,
-                    },
-                ],
-            })
-            doc.run_method('set_missing_values')
-            doc.run_method('calculate_taxes_and_totals')
-            doc.insert()
-            self.assertEqual(doc.taxes[0].tax_amount, 600)
-            self.created_docs.append(doc)
+	def test_taxes(self):
+		self.created_docs = []
+		for dt in [
+			"Purchase Order",
+			"Purchase Receipt",
+			"Purchase Invoice",
+			"Quotation",
+			"Sales Order",
+			"Delivery Note",
+			"Sales Invoice",
+		]:
+			doc = frappe.get_doc(
+				{
+					"doctype": dt,
+					"company": self.company.name,
+					"supplier": self.supplier.name,
+					"currency": "USD",
+					"schedule_date": frappe.utils.nowdate(),
+					"delivery_date": frappe.utils.nowdate(),
+					"customer": self.customer.name,
+					"buying_price_list"
+					if dt.startswith("Purchase")
+					else "selling_price_list": "Standard Buying"
+					if dt.startswith("Purchase")
+					else "Standard Selling",
+					"items": [
+						{
+							"item_code": self.item.name,
+							"qty": 300,
+							"rate": 100,
+						}
+					],
+					"taxes": [
+						{
+							"charge_type": "On Item Quantity",
+							"account_head": self.account.name,
+							"description": "N/A",
+							"rate": 0,
+						},
+					],
+				}
+			)
+			doc.run_method("set_missing_values")
+			doc.run_method("calculate_taxes_and_totals")
+			doc.insert()
+			self.assertEqual(doc.taxes[0].tax_amount, 600)
+			self.created_docs.append(doc)
 
-    def tearDown(self):
-        for doc in self.created_docs:
-            doc.delete()
-        self.item.delete()
-        self.item_group.delete()
-        self.item_tax_template.delete()
-        self.account.delete()
-        self.company.delete()
+	def tearDown(self):
+		for doc in self.created_docs:
+			doc.delete()
+		self.item.delete()
+		self.item_group.delete()
+		self.item_tax_template.delete()
+		self.account.delete()
+		self.company.delete()
diff --git a/erpnext/controllers/tests/test_transaction_base.py b/erpnext/controllers/tests/test_transaction_base.py
index f4d3f97..1471543 100644
--- a/erpnext/controllers/tests/test_transaction_base.py
+++ b/erpnext/controllers/tests/test_transaction_base.py
@@ -5,18 +5,28 @@
 
 class TestUtils(unittest.TestCase):
 	def test_reset_default_field_value(self):
-		doc = frappe.get_doc({
-			"doctype": "Purchase Receipt",
-			"set_warehouse": "Warehouse 1",
-		})
+		doc = frappe.get_doc(
+			{
+				"doctype": "Purchase Receipt",
+				"set_warehouse": "Warehouse 1",
+			}
+		)
 
 		# Same values
-		doc.items = [{"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 1"}]
+		doc.items = [
+			{"warehouse": "Warehouse 1"},
+			{"warehouse": "Warehouse 1"},
+			{"warehouse": "Warehouse 1"},
+		]
 		doc.reset_default_field_value("set_warehouse", "items", "warehouse")
 		self.assertEqual(doc.set_warehouse, "Warehouse 1")
 
 		# Mixed values
-		doc.items = [{"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 2"}, {"warehouse": "Warehouse 1"}]
+		doc.items = [
+			{"warehouse": "Warehouse 1"},
+			{"warehouse": "Warehouse 2"},
+			{"warehouse": "Warehouse 1"},
+		]
 		doc.reset_default_field_value("set_warehouse", "items", "warehouse")
 		self.assertEqual(doc.set_warehouse, None)
 
@@ -30,9 +40,13 @@
 			from_warehouse="_Test Warehouse - _TC",
 			to_warehouse="_Test Warehouse 1 - _TC",
 			items=[
-				frappe._dict(item_code="_Test Item", qty=1, basic_rate=200, s_warehouse="_Test Warehouse - _TC"),
-				frappe._dict(item_code="_Test FG Item", qty=4, t_warehouse="_Test Warehouse 1 - _TC", is_finished_item=1)
-			]
+				frappe._dict(
+					item_code="_Test Item", qty=1, basic_rate=200, s_warehouse="_Test Warehouse - _TC"
+				),
+				frappe._dict(
+					item_code="_Test FG Item", qty=4, t_warehouse="_Test Warehouse 1 - _TC", is_finished_item=1
+				),
+			],
 		)
 		se.save()
 
@@ -43,18 +57,20 @@
 		se.delete()
 
 	def test_reset_default_field_value_in_transfer_stock_entry(self):
-		doc = frappe.get_doc({
-			"doctype": "Stock Entry",
-			"purpose": "Material Receipt",
-			"from_warehouse": "Warehouse 1",
-			"to_warehouse": "Warehouse 2",
-		})
+		doc = frappe.get_doc(
+			{
+				"doctype": "Stock Entry",
+				"purpose": "Material Receipt",
+				"from_warehouse": "Warehouse 1",
+				"to_warehouse": "Warehouse 2",
+			}
+		)
 
 		# Same values
 		doc.items = [
 			{"s_warehouse": "Warehouse 1", "t_warehouse": "Warehouse 2"},
 			{"s_warehouse": "Warehouse 1", "t_warehouse": "Warehouse 2"},
-			{"s_warehouse": "Warehouse 1", "t_warehouse": "Warehouse 2"}
+			{"s_warehouse": "Warehouse 1", "t_warehouse": "Warehouse 2"},
 		]
 
 		doc.reset_default_field_value("from_warehouse", "items", "s_warehouse")
@@ -66,10 +82,10 @@
 		doc.items = [
 			{"s_warehouse": "Warehouse 1", "t_warehouse": "Warehouse 2"},
 			{"s_warehouse": "Warehouse 3", "t_warehouse": "Warehouse 2"},
-			{"s_warehouse": "Warehouse 1", "t_warehouse": "Warehouse 2"}
+			{"s_warehouse": "Warehouse 1", "t_warehouse": "Warehouse 2"},
 		]
 
 		doc.reset_default_field_value("from_warehouse", "items", "s_warehouse")
 		doc.reset_default_field_value("to_warehouse", "items", "t_warehouse")
 		self.assertEqual(doc.from_warehouse, None)
-		self.assertEqual(doc.to_warehouse, "Warehouse 2")
\ No newline at end of file
+		self.assertEqual(doc.to_warehouse, "Warehouse 2")
diff --git a/erpnext/controllers/trends.py b/erpnext/controllers/trends.py
index 1cb101f..1d6c5dc 100644
--- a/erpnext/controllers/trends.py
+++ b/erpnext/controllers/trends.py
@@ -17,17 +17,33 @@
 	# get conditions for grouping filter cond
 	group_by_cols = group_wise_column(filters.get("group_by"))
 
-	columns = based_on_details["based_on_cols"] + period_cols + [_("Total(Qty)") + ":Float:120", _("Total(Amt)") + ":Currency:120"]
+	columns = (
+		based_on_details["based_on_cols"]
+		+ period_cols
+		+ [_("Total(Qty)") + ":Float:120", _("Total(Amt)") + ":Currency:120"]
+	)
 	if group_by_cols:
-		columns = based_on_details["based_on_cols"] + group_by_cols + period_cols + \
-			[_("Total(Qty)") + ":Float:120", _("Total(Amt)") + ":Currency:120"]
+		columns = (
+			based_on_details["based_on_cols"]
+			+ group_by_cols
+			+ period_cols
+			+ [_("Total(Qty)") + ":Float:120", _("Total(Amt)") + ":Currency:120"]
+		)
 
-	conditions = {"based_on_select": based_on_details["based_on_select"], "period_wise_select": period_select,
-		"columns": columns, "group_by": based_on_details["based_on_group_by"], "grbc": group_by_cols, "trans": trans,
-		"addl_tables": based_on_details["addl_tables"], "addl_tables_relational_cond": based_on_details.get("addl_tables_relational_cond", "")}
+	conditions = {
+		"based_on_select": based_on_details["based_on_select"],
+		"period_wise_select": period_select,
+		"columns": columns,
+		"group_by": based_on_details["based_on_group_by"],
+		"grbc": group_by_cols,
+		"trans": trans,
+		"addl_tables": based_on_details["addl_tables"],
+		"addl_tables_relational_cond": based_on_details.get("addl_tables_relational_cond", ""),
+	}
 
 	return conditions
 
+
 def validate_filters(filters):
 	for f in ["Fiscal Year", "Based On", "Period", "Company"]:
 		if not filters.get(f.lower().replace(" ", "_")):
@@ -39,153 +55,231 @@
 	if filters.get("based_on") == filters.get("group_by"):
 		frappe.throw(_("'Based On' and 'Group By' can not be same"))
 
+
 def get_data(filters, conditions):
 	data = []
-	inc, cond= '',''
-	query_details =  conditions["based_on_select"] + conditions["period_wise_select"]
+	inc, cond = "", ""
+	query_details = conditions["based_on_select"] + conditions["period_wise_select"]
 
-	posting_date = 't1.transaction_date'
-	if conditions.get('trans') in ['Sales Invoice', 'Purchase Invoice', 'Purchase Receipt', 'Delivery Note']:
-		posting_date = 't1.posting_date'
+	posting_date = "t1.transaction_date"
+	if conditions.get("trans") in [
+		"Sales Invoice",
+		"Purchase Invoice",
+		"Purchase Receipt",
+		"Delivery Note",
+	]:
+		posting_date = "t1.posting_date"
 		if filters.period_based_on:
-			posting_date = 't1.'+filters.period_based_on
+			posting_date = "t1." + filters.period_based_on
 
 	if conditions["based_on_select"] in ["t1.project,", "t2.project,"]:
-		cond = ' and '+ conditions["based_on_select"][:-1] +' IS Not NULL'
-	if conditions.get('trans') in ['Sales Order', 'Purchase Order']:
+		cond = " and " + conditions["based_on_select"][:-1] + " IS Not NULL"
+	if conditions.get("trans") in ["Sales Order", "Purchase Order"]:
 		cond += " and t1.status != 'Closed'"
 
-	if conditions.get('trans') == 'Quotation' and filters.get("group_by") == 'Customer':
+	if conditions.get("trans") == "Quotation" and filters.get("group_by") == "Customer":
 		cond += " and t1.quotation_to = 'Customer'"
 
-	year_start_date, year_end_date = frappe.db.get_value("Fiscal Year",
-		filters.get('fiscal_year'), ["year_start_date", "year_end_date"])
+	year_start_date, year_end_date = frappe.db.get_value(
+		"Fiscal Year", filters.get("fiscal_year"), ["year_start_date", "year_end_date"]
+	)
 
 	if filters.get("group_by"):
-		sel_col = ''
+		sel_col = ""
 		ind = conditions["columns"].index(conditions["grbc"][0])
 
-		if filters.get("group_by") == 'Item':
-			sel_col = 't2.item_code'
-		elif filters.get("group_by") == 'Customer':
-			sel_col = 't1.party_name' if conditions.get('trans') == 'Quotation' else 't1.customer'
-		elif filters.get("group_by") == 'Supplier':
-			sel_col = 't1.supplier'
+		if filters.get("group_by") == "Item":
+			sel_col = "t2.item_code"
+		elif filters.get("group_by") == "Customer":
+			sel_col = "t1.party_name" if conditions.get("trans") == "Quotation" else "t1.customer"
+		elif filters.get("group_by") == "Supplier":
+			sel_col = "t1.supplier"
 
-		if filters.get('based_on') in ['Item','Customer','Supplier']:
+		if filters.get("based_on") in ["Item", "Customer", "Supplier"]:
 			inc = 2
-		else :
+		else:
 			inc = 1
-		data1 = frappe.db.sql(""" select %s from `tab%s` t1, `tab%s Item` t2 %s
+		data1 = frappe.db.sql(
+			""" select %s from `tab%s` t1, `tab%s Item` t2 %s
 					where t2.parent = t1.name and t1.company = %s and %s between %s and %s and
 					t1.docstatus = 1 %s %s
 					group by %s
-				""" % (query_details,  conditions["trans"],  conditions["trans"], conditions["addl_tables"], "%s",
-					posting_date, "%s", "%s", conditions.get("addl_tables_relational_cond"), cond, conditions["group_by"]), (filters.get("company"),
-					year_start_date, year_end_date),as_list=1)
+				"""
+			% (
+				query_details,
+				conditions["trans"],
+				conditions["trans"],
+				conditions["addl_tables"],
+				"%s",
+				posting_date,
+				"%s",
+				"%s",
+				conditions.get("addl_tables_relational_cond"),
+				cond,
+				conditions["group_by"],
+			),
+			(filters.get("company"), year_start_date, year_end_date),
+			as_list=1,
+		)
 
 		for d in range(len(data1)):
-			#to add blanck column
+			# to add blanck column
 			dt = data1[d]
-			dt.insert(ind,'')
+			dt.insert(ind, "")
 			data.append(dt)
 
-			#to get distinct value of col specified by group_by in filter
-			row = frappe.db.sql("""select DISTINCT(%s) from `tab%s` t1, `tab%s Item` t2 %s
+			# to get distinct value of col specified by group_by in filter
+			row = frappe.db.sql(
+				"""select DISTINCT(%s) from `tab%s` t1, `tab%s Item` t2 %s
 						where t2.parent = t1.name and t1.company = %s and %s between %s and %s
 						and t1.docstatus = 1 and %s = %s %s %s
-					""" %
-					(sel_col,  conditions["trans"],  conditions["trans"], conditions["addl_tables"],
-						"%s", posting_date, "%s", "%s", conditions["group_by"], "%s", conditions.get("addl_tables_relational_cond"), cond),
-					(filters.get("company"), year_start_date, year_end_date, data1[d][0]), as_list=1)
+					"""
+				% (
+					sel_col,
+					conditions["trans"],
+					conditions["trans"],
+					conditions["addl_tables"],
+					"%s",
+					posting_date,
+					"%s",
+					"%s",
+					conditions["group_by"],
+					"%s",
+					conditions.get("addl_tables_relational_cond"),
+					cond,
+				),
+				(filters.get("company"), year_start_date, year_end_date, data1[d][0]),
+				as_list=1,
+			)
 
 			for i in range(len(row)):
-				des = ['' for q in range(len(conditions["columns"]))]
+				des = ["" for q in range(len(conditions["columns"]))]
 
-				#get data for group_by filter
-				row1 = frappe.db.sql(""" select %s , %s from `tab%s` t1, `tab%s Item` t2 %s
+				# get data for group_by filter
+				row1 = frappe.db.sql(
+					""" select %s , %s from `tab%s` t1, `tab%s Item` t2 %s
 							where t2.parent = t1.name and t1.company = %s and %s between %s and %s
 							and t1.docstatus = 1 and %s = %s and %s = %s %s %s
-						""" %
-						(sel_col, conditions["period_wise_select"], conditions["trans"],
-							conditions["trans"], conditions["addl_tables"], "%s", posting_date, "%s","%s", sel_col,
-							"%s", conditions["group_by"], "%s", conditions.get("addl_tables_relational_cond"), cond),
-						(filters.get("company"), year_start_date, year_end_date, row[i][0],
-							data1[d][0]), as_list=1)
+						"""
+					% (
+						sel_col,
+						conditions["period_wise_select"],
+						conditions["trans"],
+						conditions["trans"],
+						conditions["addl_tables"],
+						"%s",
+						posting_date,
+						"%s",
+						"%s",
+						sel_col,
+						"%s",
+						conditions["group_by"],
+						"%s",
+						conditions.get("addl_tables_relational_cond"),
+						cond,
+					),
+					(filters.get("company"), year_start_date, year_end_date, row[i][0], data1[d][0]),
+					as_list=1,
+				)
 
 				des[ind] = row[i][0]
 
-				for j in range(1,len(conditions["columns"])-inc):
-					des[j+inc] = row1[0][j]
+				for j in range(1, len(conditions["columns"]) - inc):
+					des[j + inc] = row1[0][j]
 
 				data.append(des)
 	else:
-		data = frappe.db.sql(""" select %s from `tab%s` t1, `tab%s Item` t2 %s
+		data = frappe.db.sql(
+			""" select %s from `tab%s` t1, `tab%s Item` t2 %s
 					where t2.parent = t1.name and t1.company = %s and %s between %s and %s and
 					t1.docstatus = 1 %s %s
 					group by %s
-				""" %
-				(query_details, conditions["trans"], conditions["trans"], conditions["addl_tables"],
-					"%s", posting_date, "%s", "%s", cond, conditions.get("addl_tables_relational_cond", ""), conditions["group_by"]),
-				(filters.get("company"), year_start_date, year_end_date), as_list=1)
+				"""
+			% (
+				query_details,
+				conditions["trans"],
+				conditions["trans"],
+				conditions["addl_tables"],
+				"%s",
+				posting_date,
+				"%s",
+				"%s",
+				cond,
+				conditions.get("addl_tables_relational_cond", ""),
+				conditions["group_by"],
+			),
+			(filters.get("company"), year_start_date, year_end_date),
+			as_list=1,
+		)
 
 	return data
 
+
 def get_mon(dt):
 	return getdate(dt).strftime("%b")
 
+
 def period_wise_columns_query(filters, trans):
-	query_details = ''
+	query_details = ""
 	pwc = []
 	bet_dates = get_period_date_ranges(filters.get("period"), filters.get("fiscal_year"))
 
-	if trans in ['Purchase Receipt', 'Delivery Note', 'Purchase Invoice', 'Sales Invoice']:
-		trans_date = 'posting_date'
+	if trans in ["Purchase Receipt", "Delivery Note", "Purchase Invoice", "Sales Invoice"]:
+		trans_date = "posting_date"
 		if filters.period_based_on:
 			trans_date = filters.period_based_on
 	else:
-		trans_date = 'transaction_date'
+		trans_date = "transaction_date"
 
-	if filters.get("period") != 'Yearly':
+	if filters.get("period") != "Yearly":
 		for dt in bet_dates:
 			get_period_wise_columns(dt, filters.get("period"), pwc)
 			query_details = get_period_wise_query(dt, trans_date, query_details)
 	else:
-		pwc = [_(filters.get("fiscal_year")) + " ("+_("Qty") + "):Float:120",
-			_(filters.get("fiscal_year")) + " ("+ _("Amt") + "):Currency:120"]
+		pwc = [
+			_(filters.get("fiscal_year")) + " (" + _("Qty") + "):Float:120",
+			_(filters.get("fiscal_year")) + " (" + _("Amt") + "):Currency:120",
+		]
 		query_details = " SUM(t2.stock_qty), SUM(t2.base_net_amount),"
 
-	query_details += 'SUM(t2.stock_qty), SUM(t2.base_net_amount)'
+	query_details += "SUM(t2.stock_qty), SUM(t2.base_net_amount)"
 	return pwc, query_details
 
+
 def get_period_wise_columns(bet_dates, period, pwc):
-	if period == 'Monthly':
-		pwc += [_(get_mon(bet_dates[0])) + " (" + _("Qty") + "):Float:120",
-			_(get_mon(bet_dates[0])) + " (" + _("Amt") + "):Currency:120"]
+	if period == "Monthly":
+		pwc += [
+			_(get_mon(bet_dates[0])) + " (" + _("Qty") + "):Float:120",
+			_(get_mon(bet_dates[0])) + " (" + _("Amt") + "):Currency:120",
+		]
 	else:
-		pwc += [_(get_mon(bet_dates[0])) + "-" + _(get_mon(bet_dates[1])) + " (" + _("Qty") + "):Float:120",
-			_(get_mon(bet_dates[0])) + "-" + _(get_mon(bet_dates[1])) + " (" + _("Amt") + "):Currency:120"]
+		pwc += [
+			_(get_mon(bet_dates[0])) + "-" + _(get_mon(bet_dates[1])) + " (" + _("Qty") + "):Float:120",
+			_(get_mon(bet_dates[0])) + "-" + _(get_mon(bet_dates[1])) + " (" + _("Amt") + "):Currency:120",
+		]
+
 
 def get_period_wise_query(bet_dates, trans_date, query_details):
 	query_details += """SUM(IF(t1.%(trans_date)s BETWEEN '%(sd)s' AND '%(ed)s', t2.stock_qty, NULL)),
 					SUM(IF(t1.%(trans_date)s BETWEEN '%(sd)s' AND '%(ed)s', t2.base_net_amount, NULL)),
-				""" % {"trans_date": trans_date, "sd": bet_dates[0],"ed": bet_dates[1]}
+				""" % {
+		"trans_date": trans_date,
+		"sd": bet_dates[0],
+		"ed": bet_dates[1],
+	}
 	return query_details
 
+
 @frappe.whitelist(allow_guest=True)
 def get_period_date_ranges(period, fiscal_year=None, year_start_date=None):
 	from dateutil.relativedelta import relativedelta
 
 	if not year_start_date:
-		year_start_date, year_end_date = frappe.db.get_value("Fiscal Year",
-			fiscal_year, ["year_start_date", "year_end_date"])
+		year_start_date, year_end_date = frappe.db.get_value(
+			"Fiscal Year", fiscal_year, ["year_start_date", "year_end_date"]
+		)
 
-	increment = {
-		"Monthly": 1,
-		"Quarterly": 3,
-		"Half-Yearly": 6,
-		"Yearly": 12
-	}.get(period)
+	increment = {"Monthly": 1, "Quarterly": 3, "Half-Yearly": 6, "Yearly": 12}.get(period)
 
 	period_date_ranges = []
 	for i in range(1, 13, increment):
@@ -199,8 +293,10 @@
 
 	return period_date_ranges
 
+
 def get_period_month_ranges(period, fiscal_year):
 	from dateutil.relativedelta import relativedelta
+
 	period_month_ranges = []
 
 	for start_date, end_date in get_period_date_ranges(period, fiscal_year):
@@ -212,6 +308,7 @@
 
 	return period_month_ranges
 
+
 def based_wise_columns_query(based_on, trans):
 	based_on_details = {}
 
@@ -219,65 +316,74 @@
 	if based_on == "Item":
 		based_on_details["based_on_cols"] = ["Item:Link/Item:120", "Item Name:Data:120"]
 		based_on_details["based_on_select"] = "t2.item_code, t2.item_name,"
-		based_on_details["based_on_group_by"] = 't2.item_code'
-		based_on_details["addl_tables"] = ''
+		based_on_details["based_on_group_by"] = "t2.item_code"
+		based_on_details["addl_tables"] = ""
 
 	elif based_on == "Item Group":
 		based_on_details["based_on_cols"] = ["Item Group:Link/Item Group:120"]
 		based_on_details["based_on_select"] = "t2.item_group,"
-		based_on_details["based_on_group_by"] = 't2.item_group'
-		based_on_details["addl_tables"] = ''
+		based_on_details["based_on_group_by"] = "t2.item_group"
+		based_on_details["addl_tables"] = ""
 
 	elif based_on == "Customer":
-		based_on_details["based_on_cols"] = ["Customer:Link/Customer:120", "Territory:Link/Territory:120"]
+		based_on_details["based_on_cols"] = [
+			"Customer:Link/Customer:120",
+			"Territory:Link/Territory:120",
+		]
 		based_on_details["based_on_select"] = "t1.customer_name, t1.territory, "
-		based_on_details["based_on_group_by"] = 't1.party_name' if trans == 'Quotation' else 't1.customer'
-		based_on_details["addl_tables"] = ''
+		based_on_details["based_on_group_by"] = (
+			"t1.party_name" if trans == "Quotation" else "t1.customer"
+		)
+		based_on_details["addl_tables"] = ""
 
 	elif based_on == "Customer Group":
 		based_on_details["based_on_cols"] = ["Customer Group:Link/Customer Group"]
 		based_on_details["based_on_select"] = "t1.customer_group,"
-		based_on_details["based_on_group_by"] = 't1.customer_group'
-		based_on_details["addl_tables"] = ''
+		based_on_details["based_on_group_by"] = "t1.customer_group"
+		based_on_details["addl_tables"] = ""
 
-	elif based_on == 'Supplier':
-		based_on_details["based_on_cols"] = ["Supplier:Link/Supplier:120", "Supplier Group:Link/Supplier Group:140"]
+	elif based_on == "Supplier":
+		based_on_details["based_on_cols"] = [
+			"Supplier:Link/Supplier:120",
+			"Supplier Group:Link/Supplier Group:140",
+		]
 		based_on_details["based_on_select"] = "t1.supplier, t3.supplier_group,"
-		based_on_details["based_on_group_by"] = 't1.supplier'
-		based_on_details["addl_tables"] = ',`tabSupplier` t3'
+		based_on_details["based_on_group_by"] = "t1.supplier"
+		based_on_details["addl_tables"] = ",`tabSupplier` t3"
 		based_on_details["addl_tables_relational_cond"] = " and t1.supplier = t3.name"
 
-	elif based_on == 'Supplier Group':
+	elif based_on == "Supplier Group":
 		based_on_details["based_on_cols"] = ["Supplier Group:Link/Supplier Group:140"]
 		based_on_details["based_on_select"] = "t3.supplier_group,"
-		based_on_details["based_on_group_by"] = 't3.supplier_group'
-		based_on_details["addl_tables"] = ',`tabSupplier` t3'
+		based_on_details["based_on_group_by"] = "t3.supplier_group"
+		based_on_details["addl_tables"] = ",`tabSupplier` t3"
 		based_on_details["addl_tables_relational_cond"] = " and t1.supplier = t3.name"
 
 	elif based_on == "Territory":
 		based_on_details["based_on_cols"] = ["Territory:Link/Territory:120"]
 		based_on_details["based_on_select"] = "t1.territory,"
-		based_on_details["based_on_group_by"] = 't1.territory'
-		based_on_details["addl_tables"] = ''
+		based_on_details["based_on_group_by"] = "t1.territory"
+		based_on_details["addl_tables"] = ""
 
 	elif based_on == "Project":
-		if trans in ['Sales Invoice', 'Delivery Note', 'Sales Order']:
+		if trans in ["Sales Invoice", "Delivery Note", "Sales Order"]:
 			based_on_details["based_on_cols"] = ["Project:Link/Project:120"]
 			based_on_details["based_on_select"] = "t1.project,"
-			based_on_details["based_on_group_by"] = 't1.project'
-			based_on_details["addl_tables"] = ''
-		elif trans in ['Purchase Order', 'Purchase Invoice', 'Purchase Receipt']:
+			based_on_details["based_on_group_by"] = "t1.project"
+			based_on_details["addl_tables"] = ""
+		elif trans in ["Purchase Order", "Purchase Invoice", "Purchase Receipt"]:
 			based_on_details["based_on_cols"] = ["Project:Link/Project:120"]
 			based_on_details["based_on_select"] = "t2.project,"
-			based_on_details["based_on_group_by"] = 't2.project'
-			based_on_details["addl_tables"] = ''
+			based_on_details["based_on_group_by"] = "t2.project"
+			based_on_details["addl_tables"] = ""
 		else:
 			frappe.throw(_("Project-wise data is not available for Quotation"))
 
 	return based_on_details
 
+
 def group_wise_column(group_by):
 	if group_by:
-		return [group_by+":Link/"+group_by+":120"]
+		return [group_by + ":Link/" + group_by + ":120"]
 	else:
 		return []
diff --git a/erpnext/controllers/website_list_for_contact.py b/erpnext/controllers/website_list_for_contact.py
index 23463ab..4673230 100644
--- a/erpnext/controllers/website_list_for_contact.py
+++ b/erpnext/controllers/website_list_for_contact.py
@@ -15,21 +15,29 @@
 	return {
 		"global_number_format": frappe.db.get_default("number_format") or "#,###.##",
 		"currency": frappe.db.get_default("currency"),
-		"currency_symbols": json.dumps(dict(frappe.db.sql("""select name, symbol
-			from tabCurrency where enabled=1"""))),
+		"currency_symbols": json.dumps(
+			dict(
+				frappe.db.sql(
+					"""select name, symbol
+			from tabCurrency where enabled=1"""
+				)
+			)
+		),
 		"row_template": "templates/includes/transaction_row.html",
-		"get_list": get_transaction_list
+		"get_list": get_transaction_list,
 	}
 
+
 def get_webform_list_context(module):
-	if get_module_app(module) != 'erpnext':
+	if get_module_app(module) != "erpnext":
 		return
-	return {
-		"get_list": get_webform_transaction_list
-	}
+	return {"get_list": get_webform_transaction_list}
 
-def get_webform_transaction_list(doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by="modified"):
-	""" Get List of transactions for custom doctypes """
+
+def get_webform_transaction_list(
+	doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by="modified"
+):
+	"""Get List of transactions for custom doctypes"""
 	from frappe.www.list import get_list
 
 	if not filters:
@@ -38,42 +46,62 @@
 	meta = frappe.get_meta(doctype)
 
 	for d in meta.fields:
-		if d.fieldtype == 'Link' and d.fieldname != 'amended_from':
+		if d.fieldtype == "Link" and d.fieldname != "amended_from":
 			allowed_docs = [d.name for d in get_transaction_list(doctype=d.options, custom=True)]
-			allowed_docs.append('')
-			filters.append((d.fieldname, 'in', allowed_docs))
+			allowed_docs.append("")
+			filters.append((d.fieldname, "in", allowed_docs))
 
-	return get_list(doctype, txt, filters, limit_start, limit_page_length, ignore_permissions=False,
-		fields=None, order_by="modified")
+	return get_list(
+		doctype,
+		txt,
+		filters,
+		limit_start,
+		limit_page_length,
+		ignore_permissions=False,
+		fields=None,
+		order_by="modified",
+	)
 
-def get_transaction_list(doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by="modified", custom=False):
+
+def get_transaction_list(
+	doctype,
+	txt=None,
+	filters=None,
+	limit_start=0,
+	limit_page_length=20,
+	order_by="modified",
+	custom=False,
+):
 	user = frappe.session.user
 	ignore_permissions = False
 
-	if not filters: filters = []
+	if not filters:
+		filters = []
 
-	if doctype in ['Supplier Quotation', 'Purchase Invoice']:
-		filters.append((doctype, 'docstatus', '<', 2))
+	if doctype in ["Supplier Quotation", "Purchase Invoice"]:
+		filters.append((doctype, "docstatus", "<", 2))
 	else:
-		filters.append((doctype, 'docstatus', '=', 1))
+		filters.append((doctype, "docstatus", "=", 1))
 
-	if (user != 'Guest' and is_website_user()) or doctype == 'Request for Quotation':
-		parties_doctype = 'Request for Quotation Supplier' if doctype == 'Request for Quotation' else doctype
+	if (user != "Guest" and is_website_user()) or doctype == "Request for Quotation":
+		parties_doctype = (
+			"Request for Quotation Supplier" if doctype == "Request for Quotation" else doctype
+		)
 		# find party for this contact
 		customers, suppliers = get_customers_suppliers(parties_doctype, user)
 
 		if customers:
-			if doctype == 'Quotation':
-				filters.append(('quotation_to', '=', 'Customer'))
-				filters.append(('party_name', 'in', customers))
+			if doctype == "Quotation":
+				filters.append(("quotation_to", "=", "Customer"))
+				filters.append(("party_name", "in", customers))
 			else:
-				filters.append(('customer', 'in', customers))
+				filters.append(("customer", "in", customers))
 		elif suppliers:
-			filters.append(('supplier', 'in', suppliers))
+			filters.append(("supplier", "in", suppliers))
 		elif not custom:
 			return []
 
-		if doctype == 'Request for Quotation':
+		if doctype == "Request for Quotation":
 			parties = customers or suppliers
 			return rfq_transaction_list(parties_doctype, doctype, parties, limit_start, limit_page_length)
 
@@ -84,49 +112,88 @@
 			ignore_permissions = False
 			filters = []
 
-	transactions = get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_length,
-		fields='name', ignore_permissions=ignore_permissions, order_by='modified desc')
+	transactions = get_list_for_transactions(
+		doctype,
+		txt,
+		filters,
+		limit_start,
+		limit_page_length,
+		fields="name",
+		ignore_permissions=ignore_permissions,
+		order_by="modified desc",
+	)
 
 	if custom:
 		return transactions
 
 	return post_process(doctype, transactions)
 
-def get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_length=20,
-	ignore_permissions=False, fields=None, order_by=None):
-	""" Get List of transactions like Invoices, Orders """
+
+def get_list_for_transactions(
+	doctype,
+	txt,
+	filters,
+	limit_start,
+	limit_page_length=20,
+	ignore_permissions=False,
+	fields=None,
+	order_by=None,
+):
+	"""Get List of transactions like Invoices, Orders"""
 	from frappe.www.list import get_list
+
 	meta = frappe.get_meta(doctype)
 	data = []
 	or_filters = []
 
-	for d in get_list(doctype, txt, filters=filters, fields="name", limit_start=limit_start,
-		limit_page_length=limit_page_length, ignore_permissions=ignore_permissions, order_by="modified desc"):
+	for d in get_list(
+		doctype,
+		txt,
+		filters=filters,
+		fields="name",
+		limit_start=limit_start,
+		limit_page_length=limit_page_length,
+		ignore_permissions=ignore_permissions,
+		order_by="modified desc",
+	):
 		data.append(d)
 
 	if txt:
-		if meta.get_field('items'):
-			if meta.get_field('items').options:
-				child_doctype = meta.get_field('items').options
-				for item in frappe.get_all(child_doctype, {"item_name": ['like', "%" + txt + "%"]}):
+		if meta.get_field("items"):
+			if meta.get_field("items").options:
+				child_doctype = meta.get_field("items").options
+				for item in frappe.get_all(child_doctype, {"item_name": ["like", "%" + txt + "%"]}):
 					child = frappe.get_doc(child_doctype, item.name)
 					or_filters.append([doctype, "name", "=", child.parent])
 
 	if or_filters:
-		for r in frappe.get_list(doctype, fields=fields,filters=filters, or_filters=or_filters,
-			limit_start=limit_start, limit_page_length=limit_page_length,
-			ignore_permissions=ignore_permissions, order_by=order_by):
+		for r in frappe.get_list(
+			doctype,
+			fields=fields,
+			filters=filters,
+			or_filters=or_filters,
+			limit_start=limit_start,
+			limit_page_length=limit_page_length,
+			ignore_permissions=ignore_permissions,
+			order_by=order_by,
+		):
 			data.append(r)
 
 	return data
 
+
 def rfq_transaction_list(parties_doctype, doctype, parties, limit_start, limit_page_length):
-	data = frappe.db.sql("""select distinct parent as name, supplier from `tab{doctype}`
-			where supplier = '{supplier}' and docstatus=1  order by modified desc limit {start}, {len}""".
-			format(doctype=parties_doctype, supplier=parties[0], start=limit_start, len = limit_page_length), as_dict=1)
+	data = frappe.db.sql(
+		"""select distinct parent as name, supplier from `tab{doctype}`
+			where supplier = '{supplier}' and docstatus=1  order by modified desc limit {start}, {len}""".format(
+			doctype=parties_doctype, supplier=parties[0], start=limit_start, len=limit_page_length
+		),
+		as_dict=1,
+	)
 
 	return post_process(doctype, data)
 
+
 def post_process(doctype, data):
 	result = []
 	for d in data:
@@ -137,11 +204,15 @@
 
 		if doc.get("per_billed"):
 			doc.status_percent += flt(doc.per_billed)
-			doc.status_display.append(_("Billed") if doc.per_billed==100 else _("{0}% Billed").format(doc.per_billed))
+			doc.status_display.append(
+				_("Billed") if doc.per_billed == 100 else _("{0}% Billed").format(doc.per_billed)
+			)
 
 		if doc.get("per_delivered"):
 			doc.status_percent += flt(doc.per_delivered)
-			doc.status_display.append(_("Delivered") if doc.per_delivered==100 else _("{0}% Delivered").format(doc.per_delivered))
+			doc.status_display.append(
+				_("Delivered") if doc.per_delivered == 100 else _("{0}% Delivered").format(doc.per_delivered)
+			)
 
 		if hasattr(doc, "set_indicator"):
 			doc.set_indicator()
@@ -152,6 +223,7 @@
 
 	return result
 
+
 def get_customers_suppliers(doctype, user):
 	customers = []
 	suppliers = []
@@ -160,10 +232,11 @@
 	customer_field_name = get_customer_field_name(doctype)
 
 	has_customer_field = meta.has_field(customer_field_name)
-	has_supplier_field = meta.has_field('supplier')
+	has_supplier_field = meta.has_field("supplier")
 
 	if has_common(["Supplier", "Customer"], frappe.get_roles(user)):
-		contacts = frappe.db.sql("""
+		contacts = frappe.db.sql(
+			"""
 			select
 				`tabContact`.email_id,
 				`tabDynamic Link`.link_doctype,
@@ -172,15 +245,18 @@
 				`tabContact`, `tabDynamic Link`
 			where
 				`tabContact`.name=`tabDynamic Link`.parent and `tabContact`.email_id =%s
-			""", user, as_dict=1)
-		customers = [c.link_name for c in contacts if c.link_doctype == 'Customer']
-		suppliers = [c.link_name for c in contacts if c.link_doctype == 'Supplier']
-	elif frappe.has_permission(doctype, 'read', user=user):
+			""",
+			user,
+			as_dict=1,
+		)
+		customers = [c.link_name for c in contacts if c.link_doctype == "Customer"]
+		suppliers = [c.link_name for c in contacts if c.link_doctype == "Supplier"]
+	elif frappe.has_permission(doctype, "read", user=user):
 		customer_list = frappe.get_list("Customer")
 		customers = suppliers = [customer.name for customer in customer_list]
 
-	return customers if has_customer_field else None, \
-		suppliers if has_supplier_field else None
+	return customers if has_customer_field else None, suppliers if has_supplier_field else None
+
 
 def has_website_permission(doc, ptype, user, verbose=False):
 	doctype = doc.doctype
@@ -188,25 +264,24 @@
 	if customers:
 		return frappe.db.exists(doctype, get_customer_filter(doc, customers))
 	elif suppliers:
-		fieldname = 'suppliers' if doctype == 'Request for Quotation' else 'supplier'
-		return frappe.db.exists(doctype, {
-			'name': doc.name,
-			fieldname: ["in", suppliers]
-		})
+		fieldname = "suppliers" if doctype == "Request for Quotation" else "supplier"
+		return frappe.db.exists(doctype, {"name": doc.name, fieldname: ["in", suppliers]})
 	else:
 		return False
 
+
 def get_customer_filter(doc, customers):
 	doctype = doc.doctype
 	filters = frappe._dict()
 	filters.name = doc.name
-	filters[get_customer_field_name(doctype)] = ['in', customers]
-	if doctype == 'Quotation':
-		filters.quotation_to = 'Customer'
+	filters[get_customer_field_name(doctype)] = ["in", customers]
+	if doctype == "Quotation":
+		filters.quotation_to = "Customer"
 	return filters
 
+
 def get_customer_field_name(doctype):
-	if doctype == 'Quotation':
-		return 'party_name'
+	if doctype == "Quotation":
+		return "party_name"
 	else:
-		return 'customer'
+		return "customer"
diff --git a/erpnext/crm/doctype/appointment/appointment.py b/erpnext/crm/doctype/appointment/appointment.py
index 20fb987..5f5923d 100644
--- a/erpnext/crm/doctype/appointment/appointment.py
+++ b/erpnext/crm/doctype/appointment/appointment.py
@@ -12,17 +12,17 @@
 
 
 class Appointment(Document):
-
 	def find_lead_by_email(self):
 		lead_list = frappe.get_list(
-			'Lead', filters={'email_id': self.customer_email}, ignore_permissions=True)
+			"Lead", filters={"email_id": self.customer_email}, ignore_permissions=True
+		)
 		if lead_list:
 			return lead_list[0].name
 		return None
 
 	def find_customer_by_email(self):
 		customer_list = frappe.get_list(
-			'Customer', filters={'email_id': self.customer_email}, ignore_permissions=True
+			"Customer", filters={"email_id": self.customer_email}, ignore_permissions=True
 		)
 		if customer_list:
 			return customer_list[0].name
@@ -30,11 +30,12 @@
 
 	def before_insert(self):
 		number_of_appointments_in_same_slot = frappe.db.count(
-			'Appointment', filters={'scheduled_time': self.scheduled_time})
-		number_of_agents = frappe.db.get_single_value('Appointment Booking Settings', 'number_of_agents')
+			"Appointment", filters={"scheduled_time": self.scheduled_time}
+		)
+		number_of_agents = frappe.db.get_single_value("Appointment Booking Settings", "number_of_agents")
 		if not number_of_agents == 0:
-			if (number_of_appointments_in_same_slot >= number_of_agents):
-				frappe.throw(_('Time slot is not available'))
+			if number_of_appointments_in_same_slot >= number_of_agents:
+				frappe.throw(_("Time slot is not available"))
 		# Link lead
 		if not self.party:
 			lead = self.find_lead_by_email()
@@ -53,45 +54,46 @@
 			self.create_calendar_event()
 		else:
 			# Set status to unverified
-			self.status = 'Unverified'
+			self.status = "Unverified"
 			# Send email to confirm
 			self.send_confirmation_email()
 
 	def send_confirmation_email(self):
 		verify_url = self._get_verify_url()
-		template = 'confirm_appointment'
+		template = "confirm_appointment"
 		args = {
-			"link":verify_url,
-			"site_url":frappe.utils.get_url(),
-			"full_name":self.customer_name,
+			"link": verify_url,
+			"site_url": frappe.utils.get_url(),
+			"full_name": self.customer_name,
 		}
-		frappe.sendmail(recipients=[self.customer_email],
-						template=template,
-						args=args,
-						subject=_('Appointment Confirmation'))
+		frappe.sendmail(
+			recipients=[self.customer_email],
+			template=template,
+			args=args,
+			subject=_("Appointment Confirmation"),
+		)
 		if frappe.session.user == "Guest":
+			frappe.msgprint(_("Please check your email to confirm the appointment"))
+		else:
 			frappe.msgprint(
-				_('Please check your email to confirm the appointment'))
-		else :
-			frappe.msgprint(
-				_('Appointment was created. But no lead was found. Please check the email to confirm'))
+				_("Appointment was created. But no lead was found. Please check the email to confirm")
+			)
 
 	def on_change(self):
 		# Sync Calendar
 		if not self.calendar_event:
 			return
-		cal_event = frappe.get_doc('Event', self.calendar_event)
+		cal_event = frappe.get_doc("Event", self.calendar_event)
 		cal_event.starts_on = self.scheduled_time
 		cal_event.save(ignore_permissions=True)
 
-
 	def set_verified(self, email):
 		if not email == self.customer_email:
-			frappe.throw(_('Email verification failed.'))
+			frappe.throw(_("Email verification failed."))
 		# Create new lead
 		self.create_lead_and_link()
 		# Remove unverified status
-		self.status = 'Open'
+		self.status = "Open"
 		# Create calender event
 		self.auto_assign()
 		self.create_calendar_event()
@@ -102,58 +104,53 @@
 		# Return if already linked
 		if self.party:
 			return
-		lead = frappe.get_doc({
-			'doctype': 'Lead',
-			'lead_name': self.customer_name,
-			'email_id': self.customer_email,
-			'notes': self.customer_details,
-			'phone': self.customer_phone_number,
-		})
+		lead = frappe.get_doc(
+			{
+				"doctype": "Lead",
+				"lead_name": self.customer_name,
+				"email_id": self.customer_email,
+				"notes": self.customer_details,
+				"phone": self.customer_phone_number,
+			}
+		)
 		lead.insert(ignore_permissions=True)
 		# Link lead
 		self.party = lead.name
 
 	def auto_assign(self):
 		from frappe.desk.form.assign_to import add as add_assignemnt
+
 		existing_assignee = self.get_assignee_from_latest_opportunity()
 		if existing_assignee:
 			# If the latest opportunity is assigned to someone
 			# Assign the appointment to the same
-			add_assignemnt({
-				'doctype': self.doctype,
-				'name': self.name,
-				'assign_to': [existing_assignee]
-			})
+			add_assignemnt({"doctype": self.doctype, "name": self.name, "assign_to": [existing_assignee]})
 			return
 		if self._assign:
 			return
-		available_agents = _get_agents_sorted_by_asc_workload(
-			getdate(self.scheduled_time))
+		available_agents = _get_agents_sorted_by_asc_workload(getdate(self.scheduled_time))
 		for agent in available_agents:
-			if(_check_agent_availability(agent, self.scheduled_time)):
+			if _check_agent_availability(agent, self.scheduled_time):
 				agent = agent[0]
-				add_assignemnt({
-					'doctype': self.doctype,
-					'name': self.name,
-					'assign_to': [agent]
-				})
+				add_assignemnt({"doctype": self.doctype, "name": self.name, "assign_to": [agent]})
 			break
 
 	def get_assignee_from_latest_opportunity(self):
 		if not self.party:
 			return None
-		if not frappe.db.exists('Lead', self.party):
+		if not frappe.db.exists("Lead", self.party):
 			return None
 		opporutnities = frappe.get_list(
-			'Opportunity',
+			"Opportunity",
 			filters={
-				'party_name': self.party,
+				"party_name": self.party,
 			},
 			ignore_permissions=True,
-			order_by='creation desc')
+			order_by="creation desc",
+		)
 		if not opporutnities:
 			return None
-		latest_opportunity = frappe.get_doc('Opportunity', opporutnities[0].name )
+		latest_opportunity = frappe.get_doc("Opportunity", opporutnities[0].name)
 		assignee = latest_opportunity._assign
 		if not assignee:
 			return None
@@ -163,35 +160,36 @@
 	def create_calendar_event(self):
 		if self.calendar_event:
 			return
-		appointment_event = frappe.get_doc({
-			'doctype': 'Event',
-			'subject': ' '.join(['Appointment with', self.customer_name]),
-			'starts_on': self.scheduled_time,
-			'status': 'Open',
-			'type': 'Public',
-			'send_reminder': frappe.db.get_single_value('Appointment Booking Settings', 'email_reminders'),
-			'event_participants': [dict(reference_doctype=self.appointment_with, reference_docname=self.party)]
-		})
+		appointment_event = frappe.get_doc(
+			{
+				"doctype": "Event",
+				"subject": " ".join(["Appointment with", self.customer_name]),
+				"starts_on": self.scheduled_time,
+				"status": "Open",
+				"type": "Public",
+				"send_reminder": frappe.db.get_single_value("Appointment Booking Settings", "email_reminders"),
+				"event_participants": [
+					dict(reference_doctype=self.appointment_with, reference_docname=self.party)
+				],
+			}
+		)
 		employee = _get_employee_from_user(self._assign)
 		if employee:
-			appointment_event.append('event_participants', dict(
-				reference_doctype='Employee',
-				reference_docname=employee.name))
+			appointment_event.append(
+				"event_participants", dict(reference_doctype="Employee", reference_docname=employee.name)
+			)
 		appointment_event.insert(ignore_permissions=True)
 		self.calendar_event = appointment_event.name
 		self.save(ignore_permissions=True)
 
 	def _get_verify_url(self):
-		verify_route = '/book_appointment/verify'
-		params = {
-			'email': self.customer_email,
-			'appointment': self.name
-		}
-		return get_url(verify_route + '?' + get_signed_params(params))
+		verify_route = "/book_appointment/verify"
+		params = {"email": self.customer_email, "appointment": self.name}
+		return get_url(verify_route + "?" + get_signed_params(params))
 
 
 def _get_agents_sorted_by_asc_workload(date):
-	appointments = frappe.db.get_list('Appointment', fields='*')
+	appointments = frappe.db.get_list("Appointment", fields="*")
 	agent_list = _get_agent_list_as_strings()
 	if not appointments:
 		return agent_list
@@ -209,7 +207,7 @@
 
 def _get_agent_list_as_strings():
 	agent_list_as_strings = []
-	agent_list = frappe.get_doc('Appointment Booking Settings').agent_list
+	agent_list = frappe.get_doc("Appointment Booking Settings").agent_list
 	for agent in agent_list:
 		agent_list_as_strings.append(agent.user)
 	return agent_list_as_strings
@@ -217,7 +215,8 @@
 
 def _check_agent_availability(agent_email, scheduled_time):
 	appointemnts_at_scheduled_time = frappe.get_list(
-		'Appointment', filters={'scheduled_time': scheduled_time})
+		"Appointment", filters={"scheduled_time": scheduled_time}
+	)
 	for appointment in appointemnts_at_scheduled_time:
 		if appointment._assign == agent_email:
 			return False
@@ -225,9 +224,7 @@
 
 
 def _get_employee_from_user(user):
-	employee_docname = frappe.db.exists(
-		{'doctype': 'Employee', 'user_id': user})
+	employee_docname = frappe.db.get_value("Employee", {"user_id": user})
 	if employee_docname:
-		# frappe.db.exists returns a tuple of a tuple
-		return frappe.get_doc('Employee', employee_docname[0][0])
+		return frappe.get_doc("Employee", employee_docname)
 	return None
diff --git a/erpnext/crm/doctype/appointment/test_appointment.py b/erpnext/crm/doctype/appointment/test_appointment.py
index f4086dc..776e604 100644
--- a/erpnext/crm/doctype/appointment/test_appointment.py
+++ b/erpnext/crm/doctype/appointment/test_appointment.py
@@ -8,50 +8,44 @@
 
 
 def create_test_lead():
-    test_lead = frappe.db.exists({'doctype': 'Lead', 'email_id':'test@example.com'})
-    if test_lead:
-        return frappe.get_doc('Lead', test_lead[0][0])
-    test_lead = frappe.get_doc({
-        'doctype': 'Lead',
-        'lead_name': 'Test Lead',
-        'email_id': 'test@example.com'
-    })
-    test_lead.insert(ignore_permissions=True)
-    return test_lead
+	test_lead = frappe.db.get_value("Lead", {"email_id": "test@example.com"})
+	if test_lead:
+		return frappe.get_doc("Lead", test_lead)
+	test_lead = frappe.get_doc(
+		{"doctype": "Lead", "lead_name": "Test Lead", "email_id": "test@example.com"}
+	)
+	test_lead.insert(ignore_permissions=True)
+	return test_lead
 
 
 def create_test_appointments():
-    test_appointment = frappe.db.exists(
-        {'doctype': 'Appointment', 'scheduled_time':datetime.datetime.now(),'email':'test@example.com'})
-    if test_appointment:
-        return frappe.get_doc('Appointment', test_appointment[0][0])
-    test_appointment = frappe.get_doc({
-        'doctype': 'Appointment',
-        'email': 'test@example.com',
-        'status': 'Open',
-        'customer_name': 'Test Lead',
-        'customer_phone_number': '666',
-        'customer_skype': 'test',
-        'customer_email': 'test@example.com',
-        'scheduled_time': datetime.datetime.now()
-    })
-    test_appointment.insert()
-    return test_appointment
+	test_appointment = frappe.get_doc(
+		{
+			"doctype": "Appointment",
+			"email": "test@example.com",
+			"status": "Open",
+			"customer_name": "Test Lead",
+			"customer_phone_number": "666",
+			"customer_skype": "test",
+			"customer_email": "test@example.com",
+			"scheduled_time": datetime.datetime.now(),
+		}
+	)
+	test_appointment.insert()
+	return test_appointment
 
 
 class TestAppointment(unittest.TestCase):
-    test_appointment = test_lead = None
+	test_appointment = test_lead = None
 
-    def setUp(self):
-        self.test_lead = create_test_lead()
-        self.test_appointment = create_test_appointments()
+	def setUp(self):
+		self.test_lead = create_test_lead()
+		self.test_appointment = create_test_appointments()
 
-    def test_calendar_event_created(self):
-        cal_event = frappe.get_doc(
-            'Event', self.test_appointment.calendar_event)
-        self.assertEqual(cal_event.starts_on,
-                         self.test_appointment.scheduled_time)
+	def test_calendar_event_created(self):
+		cal_event = frappe.get_doc("Event", self.test_appointment.calendar_event)
+		self.assertEqual(cal_event.starts_on, self.test_appointment.scheduled_time)
 
-    def test_lead_linked(self):
-        lead = frappe.get_doc('Lead', self.test_lead.name)
-        self.assertIsNotNone(lead)
+	def test_lead_linked(self):
+		lead = frappe.get_doc("Lead", self.test_lead.name)
+		self.assertIsNotNone(lead)
diff --git a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py
index 1431b03..e43f460 100644
--- a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py
+++ b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py
@@ -10,8 +10,8 @@
 
 
 class AppointmentBookingSettings(Document):
-	agent_list = [] #Hack
-	min_date = '01/01/1970 '
+	agent_list = []  # Hack
+	min_date = "01/01/1970 "
 	format_string = "%d/%m/%Y %H:%M:%S"
 
 	def validate(self):
@@ -23,21 +23,22 @@
 
 	def validate_availability_of_slots(self):
 		for record in self.availability_of_slots:
-			from_time = datetime.datetime.strptime(
-				self.min_date+record.from_time, self.format_string)
-			to_time = datetime.datetime.strptime(
-				self.min_date+record.to_time, self.format_string)
-			timedelta = to_time-from_time
+			from_time = datetime.datetime.strptime(self.min_date + record.from_time, self.format_string)
+			to_time = datetime.datetime.strptime(self.min_date + record.to_time, self.format_string)
+			timedelta = to_time - from_time
 			self.validate_from_and_to_time(from_time, to_time, record)
 			self.duration_is_divisible(from_time, to_time)
 
 	def validate_from_and_to_time(self, from_time, to_time, record):
 		if from_time > to_time:
-			err_msg = _('<b>From Time</b> cannot be later than <b>To Time</b> for {0}').format(record.day_of_week)
+			err_msg = _("<b>From Time</b> cannot be later than <b>To Time</b> for {0}").format(
+				record.day_of_week
+			)
 			frappe.throw(_(err_msg))
 
 	def duration_is_divisible(self, from_time, to_time):
 		timedelta = to_time - from_time
 		if timedelta.total_seconds() % (self.appointment_duration * 60):
 			frappe.throw(
-				_('The difference between from time and To Time must be a multiple of Appointment'))
+				_("The difference between from time and To Time must be a multiple of Appointment")
+			)
diff --git a/erpnext/crm/doctype/campaign/campaign.py b/erpnext/crm/doctype/campaign/campaign.py
index 8b62800..5d06075 100644
--- a/erpnext/crm/doctype/campaign/campaign.py
+++ b/erpnext/crm/doctype/campaign/campaign.py
@@ -8,7 +8,7 @@
 
 class Campaign(Document):
 	def autoname(self):
-		if frappe.defaults.get_global_default('campaign_naming_by') != 'Naming Series':
+		if frappe.defaults.get_global_default("campaign_naming_by") != "Naming Series":
 			self.name = self.campaign_name
 		else:
 			set_name_by_naming_series(self)
diff --git a/erpnext/crm/doctype/contract/contract.py b/erpnext/crm/doctype/contract/contract.py
index e21f46a..1c2470b 100644
--- a/erpnext/crm/doctype/contract/contract.py
+++ b/erpnext/crm/doctype/contract/contract.py
@@ -75,11 +75,11 @@
 	Get a Contract's status based on the start, current and end dates
 
 	Args:
-		start_date (str): The start date of the contract
-		end_date (str): The end date of the contract
+	        start_date (str): The start date of the contract
+	        end_date (str): The end date of the contract
 
 	Returns:
-		str: 'Active' if within range, otherwise 'Inactive'
+	        str: 'Active' if within range, otherwise 'Inactive'
 	"""
 
 	if not end_date:
@@ -98,13 +98,13 @@
 	and submitted Contracts
 	"""
 
-	contracts = frappe.get_all("Contract",
-								filters={"is_signed": True,
-										"docstatus": 1},
-								fields=["name", "start_date", "end_date"])
+	contracts = frappe.get_all(
+		"Contract",
+		filters={"is_signed": True, "docstatus": 1},
+		fields=["name", "start_date", "end_date"],
+	)
 
 	for contract in contracts:
-		status = get_status(contract.get("start_date"),
-							contract.get("end_date"))
+		status = get_status(contract.get("start_date"), contract.get("end_date"))
 
 		frappe.db.set_value("Contract", contract.get("name"), "status", status)
diff --git a/erpnext/crm/doctype/contract/test_contract.py b/erpnext/crm/doctype/contract/test_contract.py
index e685362..1390168 100644
--- a/erpnext/crm/doctype/contract/test_contract.py
+++ b/erpnext/crm/doctype/contract/test_contract.py
@@ -8,7 +8,6 @@
 
 
 class TestContract(unittest.TestCase):
-
 	def setUp(self):
 		frappe.db.sql("delete from `tabContract`")
 		self.contract_doc = get_contract()
@@ -65,10 +64,7 @@
 		# Mark all the terms as fulfilled
 		self.contract_doc.requires_fulfilment = 1
 		fulfilment_terms = []
-		fulfilment_terms.append({
-			"requirement": "This is a test requirement.",
-			"fulfilled": 0
-		})
+		fulfilment_terms.append({"requirement": "This is a test requirement.", "fulfilled": 0})
 		self.contract_doc.set("fulfilment_terms", fulfilment_terms)
 
 		for term in self.contract_doc.fulfilment_terms:
@@ -85,14 +81,8 @@
 		# Mark only the first term as fulfilled
 		self.contract_doc.save()
 		fulfilment_terms = []
-		fulfilment_terms.append({
-			"requirement": "This is a test requirement.",
-			"fulfilled": 0
-		})
-		fulfilment_terms.append({
-			"requirement": "This is another test requirement.",
-			"fulfilled": 0
-		})
+		fulfilment_terms.append({"requirement": "This is a test requirement.", "fulfilled": 0})
+		fulfilment_terms.append({"requirement": "This is another test requirement.", "fulfilled": 0})
 
 		self.contract_doc.set("fulfilment_terms", fulfilment_terms)
 		self.contract_doc.fulfilment_terms[0].fulfilled = 1
@@ -110,6 +100,7 @@
 
 		self.assertEqual(self.contract_doc.fulfilment_status, "Lapsed")
 
+
 def get_contract():
 	doc = frappe.new_doc("Contract")
 	doc.party_type = "Customer"
diff --git a/erpnext/crm/doctype/contract_template/contract_template.py b/erpnext/crm/doctype/contract_template/contract_template.py
index 7439e4c..a5b0ee0 100644
--- a/erpnext/crm/doctype/contract_template/contract_template.py
+++ b/erpnext/crm/doctype/contract_template/contract_template.py
@@ -14,6 +14,7 @@
 		if self.contract_terms:
 			validate_template(self.contract_terms)
 
+
 @frappe.whitelist()
 def get_contract_template(template_name, doc):
 	if isinstance(doc, str):
@@ -25,7 +26,4 @@
 	if contract_template.contract_terms:
 		contract_terms = frappe.render_template(contract_template.contract_terms, doc)
 
-	return {
-		'contract_template': contract_template,
-		'contract_terms': contract_terms
-	}
+	return {"contract_template": contract_template, "contract_terms": contract_terms}
diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.py b/erpnext/crm/doctype/email_campaign/email_campaign.py
index d444432..9ec54ff 100644
--- a/erpnext/crm/doctype/email_campaign/email_campaign.py
+++ b/erpnext/crm/doctype/email_campaign/email_campaign.py
@@ -12,7 +12,7 @@
 class EmailCampaign(Document):
 	def validate(self):
 		self.set_date()
-		#checking if email is set for lead. Not checking for contact as email is a mandatory field for contact.
+		# checking if email is set for lead. Not checking for contact as email is a mandatory field for contact.
 		if self.email_campaign_for == "Lead":
 			self.validate_lead()
 		self.validate_email_campaign_already_exists()
@@ -21,7 +21,7 @@
 	def set_date(self):
 		if getdate(self.start_date) < getdate(today()):
 			frappe.throw(_("Start Date cannot be before the current date"))
-		#set the end date as start date + max(send after days) in campaign schedule
+		# set the end date as start date + max(send after days) in campaign schedule
 		send_after_days = []
 		campaign = frappe.get_doc("Campaign", self.campaign_name)
 		for entry in campaign.get("campaign_schedules"):
@@ -29,23 +29,32 @@
 		try:
 			self.end_date = add_days(getdate(self.start_date), max(send_after_days))
 		except ValueError:
-			frappe.throw(_("Please set up the Campaign Schedule in the Campaign {0}").format(self.campaign_name))
+			frappe.throw(
+				_("Please set up the Campaign Schedule in the Campaign {0}").format(self.campaign_name)
+			)
 
 	def validate_lead(self):
-		lead_email_id = frappe.db.get_value("Lead", self.recipient, 'email_id')
+		lead_email_id = frappe.db.get_value("Lead", self.recipient, "email_id")
 		if not lead_email_id:
-			lead_name = frappe.db.get_value("Lead", self.recipient, 'lead_name')
+			lead_name = frappe.db.get_value("Lead", self.recipient, "lead_name")
 			frappe.throw(_("Please set an email id for the Lead {0}").format(lead_name))
 
 	def validate_email_campaign_already_exists(self):
-		email_campaign_exists = frappe.db.exists("Email Campaign", {
-			"campaign_name": self.campaign_name,
-			"recipient": self.recipient,
-			"status": ("in", ["In Progress", "Scheduled"]),
-			"name": ("!=", self.name)
-		})
+		email_campaign_exists = frappe.db.exists(
+			"Email Campaign",
+			{
+				"campaign_name": self.campaign_name,
+				"recipient": self.recipient,
+				"status": ("in", ["In Progress", "Scheduled"]),
+				"name": ("!=", self.name),
+			},
+		)
 		if email_campaign_exists:
-			frappe.throw(_("The Campaign '{0}' already exists for the {1} '{2}'").format(self.campaign_name, self.email_campaign_for, self.recipient))
+			frappe.throw(
+				_("The Campaign '{0}' already exists for the {1} '{2}'").format(
+					self.campaign_name, self.email_campaign_for, self.recipient
+				)
+			)
 
 	def update_status(self):
 		start_date = getdate(self.start_date)
@@ -58,51 +67,63 @@
 		elif end_date < today_date:
 			self.status = "Completed"
 
-#called through hooks to send campaign mails to leads
+
+# called through hooks to send campaign mails to leads
 def send_email_to_leads_or_contacts():
-	email_campaigns = frappe.get_all("Email Campaign", filters = { 'status': ('not in', ['Unsubscribed', 'Completed', 'Scheduled']) })
+	email_campaigns = frappe.get_all(
+		"Email Campaign", filters={"status": ("not in", ["Unsubscribed", "Completed", "Scheduled"])}
+	)
 	for camp in email_campaigns:
 		email_campaign = frappe.get_doc("Email Campaign", camp.name)
 		campaign = frappe.get_cached_doc("Campaign", email_campaign.campaign_name)
 		for entry in campaign.get("campaign_schedules"):
-			scheduled_date = add_days(email_campaign.get('start_date'), entry.get('send_after_days'))
+			scheduled_date = add_days(email_campaign.get("start_date"), entry.get("send_after_days"))
 			if scheduled_date == getdate(today()):
 				send_mail(entry, email_campaign)
 
+
 def send_mail(entry, email_campaign):
 	recipient_list = []
 	if email_campaign.email_campaign_for == "Email Group":
-		for member in frappe.db.get_list("Email Group Member", filters={"email_group": email_campaign.get("recipient")}, fields=["email"]):
-			recipient_list.append(member['email'])
+		for member in frappe.db.get_list(
+			"Email Group Member", filters={"email_group": email_campaign.get("recipient")}, fields=["email"]
+		):
+			recipient_list.append(member["email"])
 	else:
-		recipient_list.append(frappe.db.get_value(email_campaign.email_campaign_for, email_campaign.get("recipient"), "email_id"))
+		recipient_list.append(
+			frappe.db.get_value(
+				email_campaign.email_campaign_for, email_campaign.get("recipient"), "email_id"
+			)
+		)
 
 	email_template = frappe.get_doc("Email Template", entry.get("email_template"))
 	sender = frappe.db.get_value("User", email_campaign.get("sender"), "email")
 	context = {"doc": frappe.get_doc(email_campaign.email_campaign_for, email_campaign.recipient)}
 	# send mail and link communication to document
 	comm = make(
-		doctype = "Email Campaign",
-		name = email_campaign.name,
-		subject = frappe.render_template(email_template.get("subject"), context),
-		content = frappe.render_template(email_template.get("response"), context),
-		sender = sender,
-		recipients = recipient_list,
-		communication_medium = "Email",
-		sent_or_received = "Sent",
-		send_email = True,
-		email_template = email_template.name
+		doctype="Email Campaign",
+		name=email_campaign.name,
+		subject=frappe.render_template(email_template.get("subject"), context),
+		content=frappe.render_template(email_template.get("response"), context),
+		sender=sender,
+		recipients=recipient_list,
+		communication_medium="Email",
+		sent_or_received="Sent",
+		send_email=True,
+		email_template=email_template.name,
 	)
 	return comm
 
-#called from hooks on doc_event Email Unsubscribe
+
+# called from hooks on doc_event Email Unsubscribe
 def unsubscribe_recipient(unsubscribe, method):
-	if unsubscribe.reference_doctype == 'Email Campaign':
+	if unsubscribe.reference_doctype == "Email Campaign":
 		frappe.db.set_value("Email Campaign", unsubscribe.reference_name, "status", "Unsubscribed")
 
-#called through hooks to update email campaign status daily
+
+# called through hooks to update email campaign status daily
 def set_email_campaign_status():
-	email_campaigns = frappe.get_all("Email Campaign", filters = { 'status': ('!=', 'Unsubscribed')})
+	email_campaigns = frappe.get_all("Email Campaign", filters={"status": ("!=", "Unsubscribed")})
 	for entry in email_campaigns:
 		email_campaign = frappe.get_doc("Email Campaign", entry.name)
 		email_campaign.update_status()
diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py
index c31b068..c9a64ff 100644
--- a/erpnext/crm/doctype/lead/lead.py
+++ b/erpnext/crm/doctype/lead/lead.py
@@ -23,7 +23,7 @@
 
 class Lead(SellingController):
 	def get_feed(self):
-		return '{0}: {1}'.format(_(self.status), self.lead_name)
+		return "{0}: {1}".format(_(self.status), self.lead_name)
 
 	def onload(self):
 		customer = frappe.db.get_value("Customer", {"lead_name": self.name})
@@ -62,8 +62,7 @@
 		if self.contact_date and getdate(self.contact_date) < getdate(nowdate()):
 			frappe.throw(_("Next Contact Date cannot be in the past"))
 
-		if (self.ends_on and self.contact_date and
-			(getdate(self.ends_on) < getdate(self.contact_date))):
+		if self.ends_on and self.contact_date and (getdate(self.ends_on) < getdate(self.contact_date)):
 			frappe.throw(_("Ends On date cannot be before Next Contact Date."))
 
 	def on_update(self):
@@ -72,13 +71,11 @@
 
 	def set_prev(self):
 		if self.is_new():
-			self._prev = frappe._dict({
-				"contact_date": None,
-				"ends_on": None,
-				"contact_by": None
-			})
+			self._prev = frappe._dict({"contact_date": None, "ends_on": None, "contact_by": None})
 		else:
-			self._prev = frappe.db.get_value("Lead", self.name, ["contact_date", "ends_on", "contact_by"], as_dict=1)
+			self._prev = frappe.db.get_value(
+				"Lead", self.name, ["contact_date", "ends_on", "contact_by"], as_dict=1
+			)
 
 	def before_insert(self):
 		self.contact_doc = self.create_contact()
@@ -89,39 +86,47 @@
 	def update_links(self):
 		# update contact links
 		if self.contact_doc:
-			self.contact_doc.append("links", {
-				"link_doctype": "Lead",
-				"link_name": self.name,
-				"link_title": self.lead_name
-			})
+			self.contact_doc.append(
+				"links", {"link_doctype": "Lead", "link_name": self.name, "link_title": self.lead_name}
+			)
 			self.contact_doc.save()
 
 	def add_calendar_event(self, opts=None, force=False):
-		if frappe.db.get_single_value('CRM Settings', 'create_event_on_next_contact_date'):
-			super(Lead, self).add_calendar_event({
-				"owner": self.lead_owner,
-				"starts_on": self.contact_date,
-				"ends_on": self.ends_on or "",
-				"subject": ('Contact ' + cstr(self.lead_name)),
-				"description": ('Contact ' + cstr(self.lead_name)) + (self.contact_by and ('. By : ' + cstr(self.contact_by)) or '')
-			}, force)
+		if frappe.db.get_single_value("CRM Settings", "create_event_on_next_contact_date"):
+			super(Lead, self).add_calendar_event(
+				{
+					"owner": self.lead_owner,
+					"starts_on": self.contact_date,
+					"ends_on": self.ends_on or "",
+					"subject": ("Contact " + cstr(self.lead_name)),
+					"description": ("Contact " + cstr(self.lead_name))
+					+ (self.contact_by and (". By : " + cstr(self.contact_by)) or ""),
+				},
+				force,
+			)
 
 	def update_prospects(self):
-		prospects = frappe.get_all('Prospect Lead', filters={'lead': self.name}, fields=['parent'])
+		prospects = frappe.get_all("Prospect Lead", filters={"lead": self.name}, fields=["parent"])
 		for row in prospects:
-			prospect = frappe.get_doc('Prospect', row.parent)
+			prospect = frappe.get_doc("Prospect", row.parent)
 			prospect.save(ignore_permissions=True)
 
 	def check_email_id_is_unique(self):
 		if self.email_id:
 			# validate email is unique
-			if not frappe.db.get_single_value('CRM Settings', 'allow_lead_duplication_based_on_emails'):
-				duplicate_leads = frappe.get_all("Lead", filters={"email_id": self.email_id, "name": ["!=", self.name]})
-				duplicate_leads = [frappe.bold(get_link_to_form('Lead', lead.name)) for lead in duplicate_leads]
+			if not frappe.db.get_single_value("CRM Settings", "allow_lead_duplication_based_on_emails"):
+				duplicate_leads = frappe.get_all(
+					"Lead", filters={"email_id": self.email_id, "name": ["!=", self.name]}
+				)
+				duplicate_leads = [
+					frappe.bold(get_link_to_form("Lead", lead.name)) for lead in duplicate_leads
+				]
 
 				if duplicate_leads:
-					frappe.throw(_("Email Address must be unique, already exists for {0}")
-						.format(comma_and(duplicate_leads)), frappe.DuplicateEntryError)
+					frappe.throw(
+						_("Email Address must be unique, already exists for {0}").format(comma_and(duplicate_leads)),
+						frappe.DuplicateEntryError,
+					)
 
 	def on_trash(self):
 		frappe.db.sql("""update `tabIssue` set lead='' where lead=%s""", self.name)
@@ -130,16 +135,20 @@
 		self.delete_events()
 
 	def unlink_dynamic_links(self):
-		links = frappe.get_all('Dynamic Link', filters={'link_doctype': self.doctype, 'link_name': self.name}, fields=['parent', 'parenttype'])
+		links = frappe.get_all(
+			"Dynamic Link",
+			filters={"link_doctype": self.doctype, "link_name": self.name},
+			fields=["parent", "parenttype"],
+		)
 
 		for link in links:
-			linked_doc = frappe.get_doc(link['parenttype'], link['parent'])
+			linked_doc = frappe.get_doc(link["parenttype"], link["parent"])
 
-			if len(linked_doc.get('links')) == 1:
+			if len(linked_doc.get("links")) == 1:
 				linked_doc.delete(ignore_permissions=True)
 			else:
 				to_remove = None
-				for d in linked_doc.get('links'):
+				for d in linked_doc.get("links"):
 					if d.link_doctype == self.doctype and d.link_name == self.name:
 						to_remove = d
 				if to_remove:
@@ -153,18 +162,14 @@
 		return frappe.db.get_value("Opportunity", {"party_name": self.name, "status": ["!=", "Lost"]})
 
 	def has_quotation(self):
-		return frappe.db.get_value("Quotation", {
-			"party_name": self.name,
-			"docstatus": 1,
-			"status": ["!=", "Lost"]
-		})
+		return frappe.db.get_value(
+			"Quotation", {"party_name": self.name, "docstatus": 1, "status": ["!=", "Lost"]}
+		)
 
 	def has_lost_quotation(self):
-		return frappe.db.get_value("Quotation", {
-			"party_name": self.name,
-			"docstatus": 1,
-			"status": "Lost"
-		})
+		return frappe.db.get_value(
+			"Quotation", {"party_name": self.name, "docstatus": 1, "status": "Lost"}
+		)
 
 	def set_lead_name(self):
 		if not self.lead_name:
@@ -180,43 +185,38 @@
 		self.title = self.company_name or self.lead_name
 
 	def create_contact(self):
-		if frappe.db.get_single_value('CRM Settings', 'auto_creation_of_contact'):
+		if frappe.db.get_single_value("CRM Settings", "auto_creation_of_contact"):
 			if not self.lead_name:
 				self.set_full_name()
 				self.set_lead_name()
 
 			contact = frappe.new_doc("Contact")
-			contact.update({
-				"first_name": self.first_name or self.lead_name,
-				"last_name": self.last_name,
-				"salutation": self.salutation,
-				"gender": self.gender,
-				"designation": self.designation,
-				"company_name": self.company_name,
-			})
+			contact.update(
+				{
+					"first_name": self.first_name or self.lead_name,
+					"last_name": self.last_name,
+					"salutation": self.salutation,
+					"gender": self.gender,
+					"designation": self.designation,
+					"company_name": self.company_name,
+				}
+			)
 
 			if self.email_id:
-				contact.append("email_ids", {
-					"email_id": self.email_id,
-					"is_primary": 1
-				})
+				contact.append("email_ids", {"email_id": self.email_id, "is_primary": 1})
 
 			if self.phone:
-				contact.append("phone_nos", {
-					"phone": self.phone,
-					"is_primary_phone": 1
-				})
+				contact.append("phone_nos", {"phone": self.phone, "is_primary_phone": 1})
 
 			if self.mobile_no:
-				contact.append("phone_nos", {
-					"phone": self.mobile_no,
-					"is_primary_mobile_no":1
-				})
+				contact.append("phone_nos", {"phone": self.mobile_no, "is_primary_mobile_no": 1})
 
 			contact.insert(ignore_permissions=True)
+			contact.reload()  # load changes by hooks on contact
 
 			return contact
 
+
 @frappe.whitelist()
 def make_customer(source_name, target_doc=None):
 	return _make_customer(source_name, target_doc)
@@ -233,16 +233,24 @@
 
 		target.customer_group = frappe.db.get_default("Customer Group")
 
-	doclist = get_mapped_doc("Lead", source_name,
-		{"Lead": {
-			"doctype": "Customer",
-			"field_map": {
-				"name": "lead_name",
-				"company_name": "customer_name",
-				"contact_no": "phone_1",
-				"fax": "fax_1"
+	doclist = get_mapped_doc(
+		"Lead",
+		source_name,
+		{
+			"Lead": {
+				"doctype": "Customer",
+				"field_map": {
+					"name": "lead_name",
+					"company_name": "customer_name",
+					"contact_no": "phone_1",
+					"fax": "fax_1",
+				},
 			}
-		}}, target_doc, set_missing_values, ignore_permissions=ignore_permissions)
+		},
+		target_doc,
+		set_missing_values,
+		ignore_permissions=ignore_permissions,
+	)
 
 	return doclist
 
@@ -252,19 +260,26 @@
 	def set_missing_values(source, target):
 		_set_missing_values(source, target)
 
-	target_doc = get_mapped_doc("Lead", source_name,
-		{"Lead": {
-			"doctype": "Opportunity",
-			"field_map": {
-				"campaign_name": "campaign",
-				"doctype": "opportunity_from",
-				"name": "party_name",
-				"lead_name": "contact_display",
-				"company_name": "customer_name",
-				"email_id": "contact_email",
-				"mobile_no": "contact_mobile"
+	target_doc = get_mapped_doc(
+		"Lead",
+		source_name,
+		{
+			"Lead": {
+				"doctype": "Opportunity",
+				"field_map": {
+					"campaign_name": "campaign",
+					"doctype": "opportunity_from",
+					"name": "party_name",
+					"lead_name": "contact_display",
+					"company_name": "customer_name",
+					"email_id": "contact_email",
+					"mobile_no": "contact_mobile",
+				},
 			}
-		}}, target_doc, set_missing_values)
+		},
+		target_doc,
+		set_missing_values,
+	)
 
 	return target_doc
 
@@ -274,13 +289,13 @@
 	def set_missing_values(source, target):
 		_set_missing_values(source, target)
 
-	target_doc = get_mapped_doc("Lead", source_name,
-		{"Lead": {
-			"doctype": "Quotation",
-			"field_map": {
-				"name": "party_name"
-			}
-		}}, target_doc, set_missing_values)
+	target_doc = get_mapped_doc(
+		"Lead",
+		source_name,
+		{"Lead": {"doctype": "Quotation", "field_map": {"name": "party_name"}}},
+		target_doc,
+		set_missing_values,
+	)
 
 	target_doc.quotation_to = "Lead"
 	target_doc.run_method("set_missing_values")
@@ -289,18 +304,29 @@
 
 	return target_doc
 
-def _set_missing_values(source, target):
-	address = frappe.get_all('Dynamic Link', {
-			'link_doctype': source.doctype,
-			'link_name': source.name,
-			'parenttype': 'Address',
-		}, ['parent'], limit=1)
 
-	contact = frappe.get_all('Dynamic Link', {
-			'link_doctype': source.doctype,
-			'link_name': source.name,
-			'parenttype': 'Contact',
-		}, ['parent'], limit=1)
+def _set_missing_values(source, target):
+	address = frappe.get_all(
+		"Dynamic Link",
+		{
+			"link_doctype": source.doctype,
+			"link_name": source.name,
+			"parenttype": "Address",
+		},
+		["parent"],
+		limit=1,
+	)
+
+	contact = frappe.get_all(
+		"Dynamic Link",
+		{
+			"link_doctype": source.doctype,
+			"link_name": source.name,
+			"parenttype": "Contact",
+		},
+		["parent"],
+		limit=1,
+	)
 
 	if address:
 		target.customer_address = address[0].parent
@@ -308,39 +334,49 @@
 	if contact:
 		target.contact_person = contact[0].parent
 
+
 @frappe.whitelist()
 def get_lead_details(lead, posting_date=None, company=None):
 	if not lead:
 		return {}
 
 	from erpnext.accounts.party import set_address_details
+
 	out = frappe._dict()
 
 	lead_doc = frappe.get_doc("Lead", lead)
 	lead = lead_doc
 
-	out.update({
-		"territory": lead.territory,
-		"customer_name": lead.company_name or lead.lead_name,
-		"contact_display": " ".join(filter(None, [lead.salutation, lead.lead_name])),
-		"contact_email": lead.email_id,
-		"contact_mobile": lead.mobile_no,
-		"contact_phone": lead.phone,
-	})
+	out.update(
+		{
+			"territory": lead.territory,
+			"customer_name": lead.company_name or lead.lead_name,
+			"contact_display": " ".join(filter(None, [lead.salutation, lead.lead_name])),
+			"contact_email": lead.email_id,
+			"contact_mobile": lead.mobile_no,
+			"contact_phone": lead.phone,
+		}
+	)
 
 	set_address_details(out, lead, "Lead")
 
-	taxes_and_charges = set_taxes(None, 'Lead', posting_date, company,
-		billing_address=out.get('customer_address'), shipping_address=out.get('shipping_address_name'))
+	taxes_and_charges = set_taxes(
+		None,
+		"Lead",
+		posting_date,
+		company,
+		billing_address=out.get("customer_address"),
+		shipping_address=out.get("shipping_address_name"),
+	)
 	if taxes_and_charges:
-		out['taxes_and_charges'] = taxes_and_charges
+		out["taxes_and_charges"] = taxes_and_charges
 
 	return out
 
 
 @frappe.whitelist()
 def make_lead_from_communication(communication, ignore_communication_links=False):
-	""" raise a issue from email """
+	"""raise a issue from email"""
 
 	doc = frappe.get_doc("Communication", communication)
 	lead_name = None
@@ -349,12 +385,14 @@
 	if not lead_name and doc.phone_no:
 		lead_name = frappe.db.get_value("Lead", {"mobile_no": doc.phone_no})
 	if not lead_name:
-		lead = frappe.get_doc({
-			"doctype": "Lead",
-			"lead_name": doc.sender_full_name,
-			"email_id": doc.sender,
-			"mobile_no": doc.phone_no
-		})
+		lead = frappe.get_doc(
+			{
+				"doctype": "Lead",
+				"lead_name": doc.sender_full_name,
+				"email_id": doc.sender,
+				"mobile_no": doc.phone_no,
+			}
+		)
 		lead.flags.ignore_mandatory = True
 		lead.flags.ignore_permissions = True
 		lead.insert()
@@ -364,29 +402,41 @@
 	link_communication_to_document(doc, "Lead", lead_name, ignore_communication_links)
 	return lead_name
 
-def get_lead_with_phone_number(number):
-	if not number: return
 
-	leads = frappe.get_all('Lead', or_filters={
-		'phone': ['like', '%{}'.format(number)],
-		'mobile_no': ['like', '%{}'.format(number)]
-	}, limit=1, order_by="creation DESC")
+def get_lead_with_phone_number(number):
+	if not number:
+		return
+
+	leads = frappe.get_all(
+		"Lead",
+		or_filters={
+			"phone": ["like", "%{}".format(number)],
+			"mobile_no": ["like", "%{}".format(number)],
+		},
+		limit=1,
+		order_by="creation DESC",
+	)
 
 	lead = leads[0].name if leads else None
 
 	return lead
 
+
 def daily_open_lead():
-	leads = frappe.get_all("Lead", filters = [["contact_date", "Between", [nowdate(), nowdate()]]])
+	leads = frappe.get_all("Lead", filters=[["contact_date", "Between", [nowdate(), nowdate()]]])
 	for lead in leads:
 		frappe.db.set_value("Lead", lead.name, "status", "Open")
 
+
 @frappe.whitelist()
 def add_lead_to_prospect(lead, prospect):
-	prospect = frappe.get_doc('Prospect', prospect)
-	prospect.append('prospect_lead', {
-		'lead': lead
-	})
+	prospect = frappe.get_doc("Prospect", prospect)
+	prospect.append("prospect_lead", {"lead": lead})
 	prospect.save(ignore_permissions=True)
-	frappe.msgprint(_('Lead {0} has been added to prospect {1}.').format(frappe.bold(lead), frappe.bold(prospect.name)),
-		title=_('Lead Added'), indicator='green')
+	frappe.msgprint(
+		_("Lead {0} has been added to prospect {1}.").format(
+			frappe.bold(lead), frappe.bold(prospect.name)
+		),
+		title=_("Lead Added"),
+		indicator="green",
+	)
diff --git a/erpnext/crm/doctype/lead/lead_dashboard.py b/erpnext/crm/doctype/lead/lead_dashboard.py
index 017390d..730e8e6 100644
--- a/erpnext/crm/doctype/lead/lead_dashboard.py
+++ b/erpnext/crm/doctype/lead/lead_dashboard.py
@@ -1,16 +1,9 @@
 def get_data():
 	return {
-		'fieldname': 'lead',
-		'non_standard_fieldnames': {
-			'Quotation': 'party_name',
-			'Opportunity': 'party_name'
-		},
-		'dynamic_links': {
-			'party_name': ['Lead', 'quotation_to']
-		},
-		'transactions': [
-			{
-				'items': ['Opportunity', 'Quotation', 'Prospect']
-			},
-		]
+		"fieldname": "lead",
+		"non_standard_fieldnames": {"Quotation": "party_name", "Opportunity": "party_name"},
+		"dynamic_links": {"party_name": ["Lead", "quotation_to"]},
+		"transactions": [
+			{"items": ["Opportunity", "Quotation", "Prospect"]},
+		],
 	}
diff --git a/erpnext/crm/doctype/lead/test_lead.py b/erpnext/crm/doctype/lead/test_lead.py
index 3882974..166ae2c 100644
--- a/erpnext/crm/doctype/lead/test_lead.py
+++ b/erpnext/crm/doctype/lead/test_lead.py
@@ -7,7 +7,8 @@
 import frappe
 from frappe.utils import random_string
 
-test_records = frappe.get_test_records('Lead')
+test_records = frappe.get_test_records("Lead")
+
 
 class TestLead(unittest.TestCase):
 	def test_make_customer(self):
@@ -23,12 +24,16 @@
 		customer.customer_group = "_Test Customer Group"
 		customer.insert()
 
-		#check whether lead contact is carried forward to the customer.
-		contact = frappe.db.get_value('Dynamic Link', {
-			"parenttype": "Contact",
-			"link_doctype": "Lead",
-			"link_name": customer.lead_name,
-		}, "parent")
+		# check whether lead contact is carried forward to the customer.
+		contact = frappe.db.get_value(
+			"Dynamic Link",
+			{
+				"parenttype": "Contact",
+				"link_doctype": "Lead",
+				"link_name": customer.lead_name,
+			},
+			"parent",
+		)
 
 		if contact:
 			contact_doc = frappe.get_doc("Contact", contact)
@@ -46,51 +51,49 @@
 		customer.insert()
 
 	def test_create_lead_and_unlinking_dynamic_links(self):
-		lead_doc = make_lead(first_name = "Lorem", last_name="Ipsum", email_id="lorem_ipsum@example.com")
+		lead_doc = make_lead(first_name="Lorem", last_name="Ipsum", email_id="lorem_ipsum@example.com")
 		lead_doc_1 = make_lead()
-		frappe.get_doc({
-			"doctype": "Address",
-			"address_type": "Billing",
-			"city": "Mumbai",
-			"address_line1": "Vidya Vihar West",
-			"country": "India",
-			"links": [{
-				"link_doctype": "Lead",
-				"link_name": lead_doc.name
-			}]
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Address",
+				"address_type": "Billing",
+				"city": "Mumbai",
+				"address_line1": "Vidya Vihar West",
+				"country": "India",
+				"links": [{"link_doctype": "Lead", "link_name": lead_doc.name}],
+			}
+		).insert()
 
-		address_1 = frappe.get_doc({
-			"doctype": "Address",
-			"address_type": "Billing",
-			"address_line1": "Baner",
-			"city": "Pune",
-			"country": "India",
-			"links": [
-				{
-					"link_doctype": "Lead",
-					"link_name": lead_doc.name
-				},
-				{
-					"link_doctype": "Lead",
-					"link_name": lead_doc_1.name
-				}
-			]
-		}).insert()
+		address_1 = frappe.get_doc(
+			{
+				"doctype": "Address",
+				"address_type": "Billing",
+				"address_line1": "Baner",
+				"city": "Pune",
+				"country": "India",
+				"links": [
+					{"link_doctype": "Lead", "link_name": lead_doc.name},
+					{"link_doctype": "Lead", "link_name": lead_doc_1.name},
+				],
+			}
+		).insert()
 
 		lead_doc.delete()
 		address_1.reload()
-		self.assertEqual(frappe.db.exists("Lead",lead_doc.name), None)
-		self.assertEqual(len(address_1.get('links')), 1)
+		self.assertEqual(frappe.db.exists("Lead", lead_doc.name), None)
+		self.assertEqual(len(address_1.get("links")), 1)
+
 
 def make_lead(**args):
 	args = frappe._dict(args)
 
-	lead_doc = frappe.get_doc({
-		"doctype": "Lead",
-		"first_name": args.first_name or "_Test",
-		"last_name": args.last_name or "Lead",
-		"email_id": args.email_id or "new_lead_{}@example.com".format(random_string(5)),
-	}).insert()
+	lead_doc = frappe.get_doc(
+		{
+			"doctype": "Lead",
+			"first_name": args.first_name or "_Test",
+			"last_name": args.last_name or "Lead",
+			"email_id": args.email_id or "new_lead_{}@example.com".format(random_string(5)),
+		}
+	).insert()
 
 	return lead_doc
diff --git a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js
index 7aa0b77..d532236 100644
--- a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js
+++ b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js
@@ -37,7 +37,7 @@
 			let msg,color;
 
 			if (days>0){
-				msg = __("Your Session will be expire in ") + days + __(" days.");
+				msg = __("Your Session will be expire in {0} days.", [days]);
 				color = "green";
 			}
 			else {
diff --git a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py
index d2ac10a..64b3a01 100644
--- a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py
+++ b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py
@@ -15,12 +15,16 @@
 class LinkedInSettings(Document):
 	@frappe.whitelist()
 	def get_authorization_url(self):
-		params = urlencode({
-			"response_type":"code",
-			"client_id": self.consumer_key,
-			"redirect_uri": "{0}/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?".format(frappe.utils.get_url()),
-			"scope": "r_emailaddress w_organization_social r_basicprofile r_liteprofile r_organization_social rw_organization_admin w_member_social"
-		})
+		params = urlencode(
+			{
+				"response_type": "code",
+				"client_id": self.consumer_key,
+				"redirect_uri": "{0}/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?".format(
+					frappe.utils.get_url()
+				),
+				"scope": "r_emailaddress w_organization_social r_basicprofile r_liteprofile r_organization_social rw_organization_admin w_member_social",
+			}
+		)
 
 		url = "https://www.linkedin.com/oauth/v2/authorization?{}".format(params)
 
@@ -33,11 +37,11 @@
 			"code": code,
 			"client_id": self.consumer_key,
 			"client_secret": self.get_password(fieldname="consumer_secret"),
-			"redirect_uri": "{0}/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?".format(frappe.utils.get_url()),
+			"redirect_uri": "{0}/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?".format(
+				frappe.utils.get_url()
+			),
 		}
-		headers = {
-			"Content-Type": "application/x-www-form-urlencoded"
-		}
+		headers = {"Content-Type": "application/x-www-form-urlencoded"}
 
 		response = self.http_post(url=url, data=body, headers=headers)
 		response = frappe.parse_json(response.content.decode())
@@ -47,11 +51,15 @@
 		response = requests.get(url="https://api.linkedin.com/v2/me", headers=self.get_headers())
 		response = frappe.parse_json(response.content.decode())
 
-		frappe.db.set_value(self.doctype, self.name, {
-			"person_urn": response["id"],
-			"account_name": response["vanityName"],
-			"session_status": "Active"
-		})
+		frappe.db.set_value(
+			self.doctype,
+			self.name,
+			{
+				"person_urn": response["id"],
+				"account_name": response["vanityName"],
+				"session_status": "Active",
+			},
+		)
 		frappe.local.response["type"] = "redirect"
 		frappe.local.response["location"] = get_url_to_form("LinkedIn Settings", "LinkedIn Settings")
 
@@ -64,8 +72,7 @@
 			if media_id:
 				return self.post_text(text, title, media_id=media_id)
 			else:
-				frappe.log_error("Failed to upload media.","LinkedIn Upload Error")
-
+				self.log_error("LinkedIn: Failed to upload media")
 
 	def upload_image(self, media):
 		media = get_file_path(media)
@@ -74,10 +81,9 @@
 			"registerUploadRequest": {
 				"recipes": ["urn:li:digitalmediaRecipe:feedshare-image"],
 				"owner": "urn:li:organization:{0}".format(self.company_id),
-				"serviceRelationships": [{
-					"relationshipType": "OWNER",
-					"identifier": "urn:li:userGeneratedContent"
-				}]
+				"serviceRelationships": [
+					{"relationshipType": "OWNER", "identifier": "urn:li:userGeneratedContent"}
+				],
 			}
 		}
 		headers = self.get_headers()
@@ -86,11 +92,16 @@
 		if response.status_code == 200:
 			response = response.json()
 			asset = response["value"]["asset"]
-			upload_url = response["value"]["uploadMechanism"]["com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest"]["uploadUrl"]
-			headers['Content-Type']='image/jpeg'
-			response = self.http_post(upload_url, headers=headers, data=open(media,"rb"))
+			upload_url = response["value"]["uploadMechanism"][
+				"com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest"
+			]["uploadUrl"]
+			headers["Content-Type"] = "image/jpeg"
+			response = self.http_post(upload_url, headers=headers, data=open(media, "rb"))
 			if response.status_code < 200 and response.status_code > 299:
-				frappe.throw(_("Error While Uploading Image"), title="{0} {1}".format(response.status_code, response.reason))
+				frappe.throw(
+					_("Error While Uploading Image"),
+					title="{0} {1}".format(response.status_code, response.reason),
+				)
 				return None
 			return asset
 
@@ -103,46 +114,26 @@
 		headers["Content-Type"] = "application/json; charset=UTF-8"
 
 		body = {
-			"distribution": {
-				"linkedInDistributionTarget": {}
-			},
-			"owner":"urn:li:organization:{0}".format(self.company_id),
+			"distribution": {"linkedInDistributionTarget": {}},
+			"owner": "urn:li:organization:{0}".format(self.company_id),
 			"subject": title,
-			"text": {
-				"text": text
-			}
+			"text": {"text": text},
 		}
 
 		reference_url = self.get_reference_url(text)
 		if reference_url:
-			body["content"] = {
-				"contentEntities": [
-					{
-						"entityLocation": reference_url
-					}
-				]
-			}
+			body["content"] = {"contentEntities": [{"entityLocation": reference_url}]}
 
 		if media_id:
-			body["content"]= {
-				"contentEntities": [{
-					"entity": media_id
-				}],
-				"shareMediaCategory": "IMAGE"
-			}
+			body["content"] = {"contentEntities": [{"entity": media_id}], "shareMediaCategory": "IMAGE"}
 
 		response = self.http_post(url=url, headers=headers, body=body)
 		return response
 
 	def http_post(self, url, headers=None, body=None, data=None):
 		try:
-			response = requests.post(
-				url = url,
-				json = body,
-				data = data,
-				headers = headers
-			)
-			if response.status_code not in [201,200]:
+			response = requests.post(url=url, json=body, data=data, headers=headers)
+			if response.status_code not in [201, 200]:
 				raise
 
 		except Exception as e:
@@ -151,12 +142,11 @@
 		return response
 
 	def get_headers(self):
-		return {
-			"Authorization": "Bearer {}".format(self.access_token)
-		}
+		return {"Authorization": "Bearer {}".format(self.access_token)}
 
 	def get_reference_url(self, text):
 		import re
+
 		regex_url = r"http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+"
 		urls = re.findall(regex_url, text)
 		if urls:
@@ -164,18 +154,23 @@
 
 	def delete_post(self, post_id):
 		try:
-			response = requests.delete(url="https://api.linkedin.com/v2/shares/urn:li:share:{0}".format(post_id), headers=self.get_headers())
-			if response.status_code !=200:
+			response = requests.delete(
+				url="https://api.linkedin.com/v2/shares/urn:li:share:{0}".format(post_id),
+				headers=self.get_headers(),
+			)
+			if response.status_code != 200:
 				raise
 		except Exception:
 			self.api_error(response)
 
 	def get_post(self, post_id):
-		url = "https://api.linkedin.com/v2/organizationalEntityShareStatistics?q=organizationalEntity&organizationalEntity=urn:li:organization:{0}&shares[0]=urn:li:share:{1}".format(self.company_id, post_id)
+		url = "https://api.linkedin.com/v2/organizationalEntityShareStatistics?q=organizationalEntity&organizationalEntity=urn:li:organization:{0}&shares[0]=urn:li:share:{1}".format(
+			self.company_id, post_id
+		)
 
 		try:
 			response = requests.get(url=url, headers=self.get_headers())
-			if response.status_code !=200:
+			if response.status_code != 200:
 				raise
 
 		except Exception:
@@ -200,6 +195,7 @@
 		else:
 			frappe.throw(response.reason, title=response.status_code)
 
+
 @frappe.whitelist(allow_guest=True)
 def callback(code=None, error=None, error_description=None):
 	if not error:
@@ -209,4 +205,4 @@
 		frappe.db.commit()
 	else:
 		frappe.local.response["type"] = "redirect"
-		frappe.local.response["location"] = get_url_to_form("LinkedIn Settings","LinkedIn Settings")
+		frappe.local.response["location"] = get_url_to_form("LinkedIn Settings", "LinkedIn Settings")
diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py
index 2d53874..c70a4f6 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.py
+++ b/erpnext/crm/doctype/opportunity/opportunity.py
@@ -8,8 +8,9 @@
 from frappe import _
 from frappe.email.inbox import link_communication_to_document
 from frappe.model.mapper import get_mapped_doc
-from frappe.query_builder import DocType
-from frappe.utils import cint, cstr, flt, get_fullname
+from frappe.query_builder import DocType, Interval
+from frappe.query_builder.functions import Now
+from frappe.utils import cint, flt, get_fullname
 
 from erpnext.crm.utils import add_link_in_communication, copy_comments
 from erpnext.setup.utils import get_exchange_rate
@@ -27,12 +28,16 @@
 				add_link_in_communication(self.opportunity_from, self.party_name, self)
 
 	def validate(self):
-		self._prev = frappe._dict({
-			"contact_date": frappe.db.get_value("Opportunity", self.name, "contact_date") if \
-				(not cint(self.get("__islocal"))) else None,
-			"contact_by": frappe.db.get_value("Opportunity", self.name, "contact_by") if \
-				(not cint(self.get("__islocal"))) else None,
-		})
+		self._prev = frappe._dict(
+			{
+				"contact_date": frappe.db.get_value("Opportunity", self.name, "contact_date")
+				if (not cint(self.get("__islocal")))
+				else None,
+				"contact_by": frappe.db.get_value("Opportunity", self.name, "contact_by")
+				if (not cint(self.get("__islocal")))
+				else None,
+			}
+		)
 
 		self.make_new_lead_if_required()
 		self.validate_item_details()
@@ -50,17 +55,17 @@
 			self.calculate_totals()
 
 	def map_fields(self):
-		for field in self.meta.fields:
-			if not self.get(field.fieldname):
+		for field in self.meta.get_valid_columns():
+			if not self.get(field) and frappe.db.field_exists(self.opportunity_from, field):
 				try:
-					value = frappe.db.get_value(self.opportunity_from, self.party_name, field.fieldname)
-					frappe.db.set(self, field.fieldname, value)
+					value = frappe.db.get_value(self.opportunity_from, self.party_name, field)
+					frappe.db.set(self, field, value)
 				except Exception:
 					continue
 
 	def calculate_totals(self):
 		total = base_total = 0
-		for item in self.get('items'):
+		for item in self.get("items"):
 			item.amount = flt(item.rate) * flt(item.qty)
 			item.base_rate = flt(self.conversion_rate * item.rate)
 			item.base_amount = flt(self.conversion_rate * item.amount)
@@ -75,17 +80,18 @@
 		if (not self.get("party_name")) and self.contact_email:
 			# check if customer is already created agains the self.contact_email
 			dynamic_link, contact = DocType("Dynamic Link"), DocType("Contact")
-			customer = frappe.qb.from_(
-				dynamic_link
-			).join(
-				contact
-			).on(
-				(contact.name == dynamic_link.parent)
-				& (dynamic_link.link_doctype == "Customer")
-				& (contact.email_id == self.contact_email)
-			).select(
-				dynamic_link.link_name
-			).distinct().run(as_dict=True)
+			customer = (
+				frappe.qb.from_(dynamic_link)
+				.join(contact)
+				.on(
+					(contact.name == dynamic_link.parent)
+					& (dynamic_link.link_doctype == "Customer")
+					& (contact.email_id == self.contact_email)
+				)
+				.select(dynamic_link.link_name)
+				.distinct()
+				.run(as_dict=True)
+			)
 
 			if customer and customer[0].link_name:
 				self.party_name = customer[0].link_name
@@ -98,19 +104,17 @@
 				if sender_name == self.contact_email:
 					sender_name = None
 
-				if not sender_name and ('@' in self.contact_email):
-					email_name = self.contact_email.split('@')[0]
+				if not sender_name and ("@" in self.contact_email):
+					email_name = self.contact_email.split("@")[0]
 
-					email_split = email_name.split('.')
-					sender_name = ''
+					email_split = email_name.split(".")
+					sender_name = ""
 					for s in email_split:
-						sender_name += s.capitalize() + ' '
+						sender_name += s.capitalize() + " "
 
-				lead = frappe.get_doc({
-					"doctype": "Lead",
-					"email_id": self.contact_email,
-					"lead_name": sender_name or 'Unknown'
-				})
+				lead = frappe.get_doc(
+					{"doctype": "Lead", "email_id": self.contact_email, "lead_name": sender_name or "Unknown"}
+				)
 
 				lead.flags.ignore_email_validation = True
 				lead.insert(ignore_permissions=True)
@@ -122,17 +126,18 @@
 	@frappe.whitelist()
 	def declare_enquiry_lost(self, lost_reasons_list, competitors, detailed_reason=None):
 		if not self.has_active_quotation():
-			self.status = 'Lost'
-			self.lost_reasons = self.competitors = []
+			self.status = "Lost"
+			self.lost_reasons = []
+			self.competitors = []
 
 			if detailed_reason:
 				self.order_lost_reason = detailed_reason
 
 			for reason in lost_reasons_list:
-				self.append('lost_reasons', reason)
+				self.append("lost_reasons", reason)
 
 			for competitor in competitors:
-				self.append('competitors', competitor)
+				self.append("competitors", competitor)
 
 			self.save()
 
@@ -144,85 +149,92 @@
 
 	def has_active_quotation(self):
 		if not self.with_items:
-			return frappe.get_all('Quotation',
-				{
-					'opportunity': self.name,
-					'status': ("not in", ['Lost', 'Closed']),
-					'docstatus': 1
-				}, 'name')
+			return frappe.get_all(
+				"Quotation",
+				{"opportunity": self.name, "status": ("not in", ["Lost", "Closed"]), "docstatus": 1},
+				"name",
+			)
 		else:
-			return frappe.db.sql("""
+			return frappe.db.sql(
+				"""
 				select q.name
 				from `tabQuotation` q, `tabQuotation Item` qi
 				where q.name = qi.parent and q.docstatus=1 and qi.prevdoc_docname =%s
-				and q.status not in ('Lost', 'Closed')""", self.name)
+				and q.status not in ('Lost', 'Closed')""",
+				self.name,
+			)
 
 	def has_ordered_quotation(self):
 		if not self.with_items:
-			return frappe.get_all('Quotation',
-				{
-					'opportunity': self.name,
-					'status': 'Ordered',
-					'docstatus': 1
-				}, 'name')
+			return frappe.get_all(
+				"Quotation", {"opportunity": self.name, "status": "Ordered", "docstatus": 1}, "name"
+			)
 		else:
-			return frappe.db.sql("""
+			return frappe.db.sql(
+				"""
 				select q.name
 				from `tabQuotation` q, `tabQuotation Item` qi
 				where q.name = qi.parent and q.docstatus=1 and qi.prevdoc_docname =%s
-				and q.status = 'Ordered'""", self.name)
+				and q.status = 'Ordered'""",
+				self.name,
+			)
 
 	def has_lost_quotation(self):
-		lost_quotation = frappe.db.sql("""
+		lost_quotation = frappe.db.sql(
+			"""
 			select name
 			from `tabQuotation`
 			where docstatus=1
 				and opportunity =%s and status = 'Lost'
-			""", self.name)
+			""",
+			self.name,
+		)
 		if lost_quotation:
 			if self.has_active_quotation():
 				return False
 			return True
 
 	def validate_cust_name(self):
-		if self.party_name and self.opportunity_from == 'Customer':
+		if self.party_name and self.opportunity_from == "Customer":
 			self.customer_name = frappe.db.get_value("Customer", self.party_name, "customer_name")
-		elif self.party_name and self.opportunity_from == 'Lead':
-			lead_name, company_name = frappe.db.get_value("Lead", self.party_name, ["lead_name", "company_name"])
+		elif self.party_name and self.opportunity_from == "Lead":
+			lead_name, company_name = frappe.db.get_value(
+				"Lead", self.party_name, ["lead_name", "company_name"]
+			)
 			self.customer_name = company_name or lead_name
 
 	def on_update(self):
 		self.add_calendar_event()
 
 	def add_calendar_event(self, opts=None, force=False):
-		if frappe.db.get_single_value('CRM Settings', 'create_event_on_next_contact_date_opportunity'):
+		if frappe.db.get_single_value("CRM Settings", "create_event_on_next_contact_date_opportunity"):
 			if not opts:
 				opts = frappe._dict()
 
 			opts.description = ""
 			opts.contact_date = self.contact_date
 
-			if self.party_name and self.opportunity_from == 'Customer':
+			if self.party_name and self.opportunity_from == "Customer":
 				if self.contact_person:
-					opts.description = 'Contact '+cstr(self.contact_person)
+					opts.description = f"Contact {self.contact_person}"
 				else:
-					opts.description = 'Contact customer '+cstr(self.party_name)
-			elif self.party_name and self.opportunity_from == 'Lead':
+					opts.description = f"Contact customer {self.party_name}"
+			elif self.party_name and self.opportunity_from == "Lead":
 				if self.contact_display:
-					opts.description = 'Contact '+cstr(self.contact_display)
+					opts.description = f"Contact {self.contact_display}"
 				else:
-					opts.description = 'Contact lead '+cstr(self.party_name)
+					opts.description = f"Contact lead {self.party_name}"
 
 			opts.subject = opts.description
-			opts.description += '. By : ' + cstr(self.contact_by)
+			opts.description += f". By : {self.contact_by}"
 
 			if self.to_discuss:
-				opts.description += ' To Discuss : ' + cstr(self.to_discuss)
+				opts.description += f" To Discuss : {frappe.render_template(self.to_discuss, {'doc': self})}"
 
 			super(Opportunity, self).add_calendar_event(opts, force)
 
 	def validate_item_details(self):
-		if not self.get('items'):
+		if not self.get("items"):
 			return
 
 		# set missing values
@@ -234,41 +246,51 @@
 
 			item = frappe.db.get_value("Item", d.item_code, item_fields, as_dict=True)
 			for key in item_fields:
-				if not d.get(key): d.set(key, item.get(key))
+				if not d.get(key):
+					d.set(key, item.get(key))
 
 
 @frappe.whitelist()
 def get_item_details(item_code):
-	item = frappe.db.sql("""select item_name, stock_uom, image, description, item_group, brand
-		from `tabItem` where name = %s""", item_code, as_dict=1)
+	item = frappe.db.sql(
+		"""select item_name, stock_uom, image, description, item_group, brand
+		from `tabItem` where name = %s""",
+		item_code,
+		as_dict=1,
+	)
 	return {
-		'item_name': item and item[0]['item_name'] or '',
-		'uom': item and item[0]['stock_uom'] or '',
-		'description': item and item[0]['description'] or '',
-		'image': item and item[0]['image'] or '',
-		'item_group': item and item[0]['item_group'] or '',
-		'brand': item and item[0]['brand'] or ''
+		"item_name": item and item[0]["item_name"] or "",
+		"uom": item and item[0]["stock_uom"] or "",
+		"description": item and item[0]["description"] or "",
+		"image": item and item[0]["image"] or "",
+		"item_group": item and item[0]["item_group"] or "",
+		"brand": item and item[0]["brand"] or "",
 	}
 
+
 @frappe.whitelist()
 def make_quotation(source_name, target_doc=None):
 	def set_missing_values(source, target):
 		from erpnext.controllers.accounts_controller import get_default_taxes_and_charges
+
 		quotation = frappe.get_doc(target)
 
-		company_currency = frappe.get_cached_value('Company',  quotation.company,  "default_currency")
+		company_currency = frappe.get_cached_value("Company", quotation.company, "default_currency")
 
 		if company_currency == quotation.currency:
 			exchange_rate = 1
 		else:
-			exchange_rate = get_exchange_rate(quotation.currency, company_currency,
-				quotation.transaction_date, args="for_selling")
+			exchange_rate = get_exchange_rate(
+				quotation.currency, company_currency, quotation.transaction_date, args="for_selling"
+			)
 
 		quotation.conversion_rate = exchange_rate
 
 		# get default taxes
-		taxes = get_default_taxes_and_charges("Sales Taxes and Charges Template", company=quotation.company)
-		if taxes.get('taxes'):
+		taxes = get_default_taxes_and_charges(
+			"Sales Taxes and Charges Template", company=quotation.company
+		)
+		if taxes.get("taxes"):
 			quotation.update(taxes)
 
 		quotation.run_method("set_missing_values")
@@ -276,49 +298,53 @@
 		if not source.with_items:
 			quotation.opportunity = source.name
 
-	doclist = get_mapped_doc("Opportunity", source_name, {
-		"Opportunity": {
-			"doctype": "Quotation",
-			"field_map": {
-				"opportunity_from": "quotation_to",
-				"name": "enq_no"
-			}
-		},
-		"Opportunity Item": {
-			"doctype": "Quotation Item",
-			"field_map": {
-				"parent": "prevdoc_docname",
-				"parenttype": "prevdoc_doctype",
-				"uom": "stock_uom"
+	doclist = get_mapped_doc(
+		"Opportunity",
+		source_name,
+		{
+			"Opportunity": {
+				"doctype": "Quotation",
+				"field_map": {"opportunity_from": "quotation_to", "name": "enq_no"},
 			},
-			"add_if_empty": True
-		}
-	}, target_doc, set_missing_values)
+			"Opportunity Item": {
+				"doctype": "Quotation Item",
+				"field_map": {
+					"parent": "prevdoc_docname",
+					"parenttype": "prevdoc_doctype",
+					"uom": "stock_uom",
+				},
+				"add_if_empty": True,
+			},
+		},
+		target_doc,
+		set_missing_values,
+	)
 
 	return doclist
 
+
 @frappe.whitelist()
 def make_request_for_quotation(source_name, target_doc=None):
 	def update_item(obj, target, source_parent):
 		target.conversion_factor = 1.0
 
-	doclist = get_mapped_doc("Opportunity", source_name, {
-		"Opportunity": {
-			"doctype": "Request for Quotation"
+	doclist = get_mapped_doc(
+		"Opportunity",
+		source_name,
+		{
+			"Opportunity": {"doctype": "Request for Quotation"},
+			"Opportunity Item": {
+				"doctype": "Request for Quotation Item",
+				"field_map": [["name", "opportunity_item"], ["parent", "opportunity"], ["uom", "uom"]],
+				"postprocess": update_item,
+			},
 		},
-		"Opportunity Item": {
-			"doctype": "Request for Quotation Item",
-			"field_map": [
-				["name", "opportunity_item"],
-				["parent", "opportunity"],
-				["uom", "uom"]
-			],
-			"postprocess": update_item
-		}
-	}, target_doc)
+		target_doc,
+	)
 
 	return doclist
 
+
 @frappe.whitelist()
 def make_customer(source_name, target_doc=None):
 	def set_missing_values(source, target):
@@ -327,37 +353,37 @@
 		if source.opportunity_from == "Lead":
 			target.lead_name = source.party_name
 
-	doclist = get_mapped_doc("Opportunity", source_name, {
-		"Opportunity": {
-			"doctype": "Customer",
-			"field_map": {
-				"currency": "default_currency",
-				"customer_name": "customer_name"
+	doclist = get_mapped_doc(
+		"Opportunity",
+		source_name,
+		{
+			"Opportunity": {
+				"doctype": "Customer",
+				"field_map": {"currency": "default_currency", "customer_name": "customer_name"},
 			}
-		}
-	}, target_doc, set_missing_values)
+		},
+		target_doc,
+		set_missing_values,
+	)
 
 	return doclist
 
+
 @frappe.whitelist()
 def make_supplier_quotation(source_name, target_doc=None):
-	doclist = get_mapped_doc("Opportunity", source_name, {
-		"Opportunity": {
-			"doctype": "Supplier Quotation",
-			"field_map": {
-				"name": "opportunity"
-			}
+	doclist = get_mapped_doc(
+		"Opportunity",
+		source_name,
+		{
+			"Opportunity": {"doctype": "Supplier Quotation", "field_map": {"name": "opportunity"}},
+			"Opportunity Item": {"doctype": "Supplier Quotation Item", "field_map": {"uom": "stock_uom"}},
 		},
-		"Opportunity Item": {
-			"doctype": "Supplier Quotation Item",
-			"field_map": {
-				"uom": "stock_uom"
-			}
-		}
-	}, target_doc)
+		target_doc,
+	)
 
 	return doclist
 
+
 @frappe.whitelist()
 def set_multiple_status(names, status):
 	names = json.loads(names)
@@ -366,23 +392,34 @@
 		opp.status = status
 		opp.save()
 
-def auto_close_opportunity():
-	""" auto close the `Replied` Opportunities after 7 days """
-	auto_close_after_days = frappe.db.get_single_value("CRM Settings", "close_opportunity_after_days") or 15
 
-	opportunities = frappe.db.sql(""" select name from tabOpportunity where status='Replied' and
-		modified<DATE_SUB(CURDATE(), INTERVAL %s DAY) """, (auto_close_after_days), as_dict=True)
+def auto_close_opportunity():
+	"""auto close the `Replied` Opportunities after 7 days"""
+	auto_close_after_days = (
+		frappe.db.get_single_value("CRM Settings", "close_opportunity_after_days") or 15
+	)
+
+	table = frappe.qb.DocType("Opportunity")
+	opportunities = (
+		frappe.qb.from_(table)
+		.select(table.name)
+		.where(
+			(table.modified < (Now() - Interval(days=auto_close_after_days))) & (table.status == "Replied")
+		)
+	).run(pluck=True)
 
 	for opportunity in opportunities:
-		doc = frappe.get_doc("Opportunity", opportunity.get("name"))
+		doc = frappe.get_doc("Opportunity", opportunity)
 		doc.status = "Closed"
 		doc.flags.ignore_permissions = True
 		doc.flags.ignore_mandatory = True
 		doc.save()
 
+
 @frappe.whitelist()
 def make_opportunity_from_communication(communication, company, ignore_communication_links=False):
 	from erpnext.crm.doctype.lead.lead import make_lead_from_communication
+
 	doc = frappe.get_doc("Communication", communication)
 
 	lead = doc.reference_name if doc.reference_doctype == "Lead" else None
@@ -391,16 +428,20 @@
 
 	opportunity_from = "Lead"
 
-	opportunity = frappe.get_doc({
-		"doctype": "Opportunity",
-		"company": company,
-		"opportunity_from": opportunity_from,
-		"party_name": lead
-	}).insert(ignore_permissions=True)
+	opportunity = frappe.get_doc(
+		{
+			"doctype": "Opportunity",
+			"company": company,
+			"opportunity_from": opportunity_from,
+			"party_name": lead,
+		}
+	).insert(ignore_permissions=True)
 
 	link_communication_to_document(doc, "Opportunity", opportunity.name, ignore_communication_links)
 
 	return opportunity.name
+
+
 @frappe.whitelist()
 def get_events(start, end, filters=None):
 	"""Returns events for Gantt / Calendar view rendering.
@@ -409,9 +450,11 @@
 	:param filters: Filters (JSON).
 	"""
 	from frappe.desk.calendar import get_event_conditions
+
 	conditions = get_event_conditions("Opportunity", filters)
 
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 		select
 			distinct `tabOpportunity`.name, `tabOpportunity`.customer_name, `tabOpportunity`.opportunity_amount,
 			`tabOpportunity`.title, `tabOpportunity`.contact_date
@@ -420,8 +463,11 @@
 		where
 			(`tabOpportunity`.contact_date between %(start)s and %(end)s)
 			{conditions}
-		""".format(conditions=conditions), {
-			"start": start,
-			"end": end
-		}, as_dict=True, update={"allDay": 0})
+		""".format(
+			conditions=conditions
+		),
+		{"start": start, "end": end},
+		as_dict=True,
+		update={"allDay": 0},
+	)
 	return data
diff --git a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py
index 708fb12..d3a10bc 100644
--- a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py
+++ b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py
@@ -1,9 +1,7 @@
 def get_data():
 	return {
-		'fieldname': 'opportunity',
-		'transactions': [
-			{
-				'items': ['Quotation', 'Supplier Quotation']
-			},
-		]
+		"fieldname": "opportunity",
+		"transactions": [
+			{"items": ["Quotation", "Request for Quotation", "Supplier Quotation"]},
+		],
 	}
diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py
index db44b6a..4a18e94 100644
--- a/erpnext/crm/doctype/opportunity/test_opportunity.py
+++ b/erpnext/crm/doctype/opportunity/test_opportunity.py
@@ -4,29 +4,27 @@
 import unittest
 
 import frappe
-from frappe.utils import now_datetime, random_string, today
+from frappe.utils import add_days, now_datetime, random_string, today
 
 from erpnext.crm.doctype.lead.lead import make_customer
 from erpnext.crm.doctype.lead.test_lead import make_lead
 from erpnext.crm.doctype.opportunity.opportunity import make_quotation
 from erpnext.crm.utils import get_linked_communication_list
 
-test_records = frappe.get_test_records('Opportunity')
+test_records = frappe.get_test_records("Opportunity")
+
 
 class TestOpportunity(unittest.TestCase):
 	def test_opportunity_status(self):
 		doc = make_opportunity(with_items=0)
 		quotation = make_quotation(doc.name)
-		quotation.append('items', {
-			"item_code": "_Test Item",
-			"qty": 1
-		})
+		quotation.append("items", {"item_code": "_Test Item", "qty": 1})
 
 		quotation.run_method("set_missing_values")
 		quotation.run_method("calculate_taxes_and_totals")
 		quotation.submit()
 
-		doc = frappe.get_doc('Opportunity', doc.name)
+		doc = frappe.get_doc("Opportunity", doc.name)
 		self.assertEqual(doc.status, "Quotation")
 
 	def test_make_new_lead_if_required(self):
@@ -34,18 +32,19 @@
 
 		self.assertTrue(opp_doc.party_name)
 		self.assertEqual(opp_doc.opportunity_from, "Lead")
-		self.assertEqual(frappe.db.get_value("Lead", opp_doc.party_name, "email_id"), opp_doc.contact_email)
+		self.assertEqual(
+			frappe.db.get_value("Lead", opp_doc.party_name, "email_id"), opp_doc.contact_email
+		)
 
 		# create new customer and create new contact against 'new.opportunity@example.com'
 		customer = make_customer(opp_doc.party_name).insert(ignore_permissions=True)
-		contact = frappe.get_doc({
-			"doctype": "Contact",
-			"first_name": "_Test Opportunity Customer",
-			"links": [{
-				"link_doctype": "Customer",
-				"link_name": customer.name
-			}]
-		})
+		contact = frappe.get_doc(
+			{
+				"doctype": "Contact",
+				"first_name": "_Test Opportunity Customer",
+				"links": [{"link_doctype": "Customer", "link_name": customer.name}],
+			}
+		)
 		contact.add_email(opp_doc.contact_email, is_primary=True)
 		contact.insert(ignore_permissions=True)
 
@@ -54,38 +53,67 @@
 		self.assertEqual(opportunity_doc.total, 2200)
 
 	def test_carry_forward_of_email_and_comments(self):
-		frappe.db.set_value("CRM Settings", "CRM Settings", "carry_forward_communication_and_comments", 1)
+		frappe.db.set_value(
+			"CRM Settings", "CRM Settings", "carry_forward_communication_and_comments", 1
+		)
 		lead_doc = make_lead()
-		lead_doc.add_comment('Comment', text='Test Comment 1')
-		lead_doc.add_comment('Comment', text='Test Comment 2')
+		lead_doc.add_comment("Comment", text="Test Comment 1")
+		lead_doc.add_comment("Comment", text="Test Comment 2")
 		create_communication(lead_doc.doctype, lead_doc.name, lead_doc.email_id)
 		create_communication(lead_doc.doctype, lead_doc.name, lead_doc.email_id)
 
 		opp_doc = make_opportunity(opportunity_from="Lead", lead=lead_doc.name)
-		opportunity_comment_count = frappe.db.count("Comment", {"reference_doctype": opp_doc.doctype, "reference_name": opp_doc.name})
-		opportunity_communication_count = len(get_linked_communication_list(opp_doc.doctype, opp_doc.name))
+		opportunity_comment_count = frappe.db.count(
+			"Comment", {"reference_doctype": opp_doc.doctype, "reference_name": opp_doc.name}
+		)
+		opportunity_communication_count = len(
+			get_linked_communication_list(opp_doc.doctype, opp_doc.name)
+		)
 		self.assertEqual(opportunity_comment_count, 2)
 		self.assertEqual(opportunity_communication_count, 2)
 
-		opp_doc.add_comment('Comment', text='Test Comment 3')
-		opp_doc.add_comment('Comment', text='Test Comment 4')
+		opp_doc.add_comment("Comment", text="Test Comment 3")
+		opp_doc.add_comment("Comment", text="Test Comment 4")
 		create_communication(opp_doc.doctype, opp_doc.name, opp_doc.contact_email)
 		create_communication(opp_doc.doctype, opp_doc.name, opp_doc.contact_email)
 
 		quotation_doc = make_quotation(opp_doc.name)
-		quotation_doc.append('items', {
-			"item_code": "_Test Item",
-			"qty": 1
-		})
+		quotation_doc.append("items", {"item_code": "_Test Item", "qty": 1})
 		quotation_doc.run_method("set_missing_values")
 		quotation_doc.run_method("calculate_taxes_and_totals")
 		quotation_doc.save()
 
-		quotation_comment_count = frappe.db.count("Comment", {"reference_doctype": quotation_doc.doctype, "reference_name": quotation_doc.name, "comment_type": "Comment"})
-		quotation_communication_count = len(get_linked_communication_list(quotation_doc.doctype, quotation_doc.name))
+		quotation_comment_count = frappe.db.count(
+			"Comment",
+			{
+				"reference_doctype": quotation_doc.doctype,
+				"reference_name": quotation_doc.name,
+				"comment_type": "Comment",
+			},
+		)
+		quotation_communication_count = len(
+			get_linked_communication_list(quotation_doc.doctype, quotation_doc.name)
+		)
 		self.assertEqual(quotation_comment_count, 4)
 		self.assertEqual(quotation_communication_count, 4)
 
+	def test_render_template_for_to_discuss(self):
+		doc = make_opportunity(with_items=0, opportunity_from="Lead")
+		doc.contact_by = "test@example.com"
+		doc.contact_date = add_days(today(), days=2)
+		doc.to_discuss = "{{ doc.name }} test data"
+		doc.save()
+
+		event = frappe.get_all(
+			"Event Participants",
+			fields=["parent"],
+			filters={"reference_doctype": doc.doctype, "reference_docname": doc.name},
+		)
+
+		event_description = frappe.db.get_value("Event", event[0].parent, "description")
+		self.assertTrue(doc.name in event_description)
+
+
 def make_opportunity_from_lead():
 	new_lead_email_id = "new{}@example.com".format(random_string(5))
 	args = {
@@ -93,56 +121,67 @@
 		"contact_email": new_lead_email_id,
 		"opportunity_type": "Sales",
 		"with_items": 0,
-		"transaction_date": today()
+		"transaction_date": today(),
 	}
 	# new lead should be created against the new.opportunity@example.com
 	opp_doc = frappe.get_doc(args).insert(ignore_permissions=True)
 
 	return opp_doc
 
+
 def make_opportunity(**args):
 	args = frappe._dict(args)
 
-	opp_doc = frappe.get_doc({
-		"doctype": "Opportunity",
-		"company": args.company or "_Test Company",
-		"opportunity_from": args.opportunity_from or "Customer",
-		"opportunity_type": "Sales",
-		"conversion_rate": 1.0,
-		"with_items": args.with_items or 0,
-		"transaction_date": today()
-	})
+	opp_doc = frappe.get_doc(
+		{
+			"doctype": "Opportunity",
+			"company": args.company or "_Test Company",
+			"opportunity_from": args.opportunity_from or "Customer",
+			"opportunity_type": "Sales",
+			"conversion_rate": 1.0,
+			"with_items": args.with_items or 0,
+			"transaction_date": today(),
+		}
+	)
 
-	if opp_doc.opportunity_from == 'Customer':
-		opp_doc.party_name= args.customer or "_Test Customer"
+	if opp_doc.opportunity_from == "Customer":
+		opp_doc.party_name = args.customer or "_Test Customer"
 
-	if opp_doc.opportunity_from == 'Lead':
+	if opp_doc.opportunity_from == "Lead":
 		opp_doc.party_name = args.lead or "_T-Lead-00001"
 
 	if args.with_items:
-		opp_doc.append('items', {
-			"item_code": args.item_code or "_Test Item",
-			"qty": args.qty or 1,
-			"rate": args.rate or 1000,
-			"uom": "_Test UOM"
-		})
+		opp_doc.append(
+			"items",
+			{
+				"item_code": args.item_code or "_Test Item",
+				"qty": args.qty or 1,
+				"rate": args.rate or 1000,
+				"uom": "_Test UOM",
+			},
+		)
 
 	opp_doc.insert()
 	return opp_doc
 
-def create_communication(reference_doctype, reference_name, sender, sent_or_received=None, creation=None):
-	communication = frappe.get_doc({
-		"doctype": "Communication",
-		"communication_type": "Communication",
-		"communication_medium": "Email",
-		"sent_or_received": sent_or_received or "Sent",
-		"email_status": "Open",
-		"subject": "Test Subject",
-		"sender": sender,
-		"content": "Test",
-		"status": "Linked",
-		"reference_doctype": reference_doctype,
-		"creation": creation or now_datetime(),
-		"reference_name": reference_name
-	})
-	communication.save()
\ No newline at end of file
+
+def create_communication(
+	reference_doctype, reference_name, sender, sent_or_received=None, creation=None
+):
+	communication = frappe.get_doc(
+		{
+			"doctype": "Communication",
+			"communication_type": "Communication",
+			"communication_medium": "Email",
+			"sent_or_received": sent_or_received or "Sent",
+			"email_status": "Open",
+			"subject": "Test Subject",
+			"sender": sender,
+			"content": "Test",
+			"status": "Linked",
+			"reference_doctype": reference_doctype,
+			"creation": creation or now_datetime(),
+			"reference_name": reference_name,
+		}
+	)
+	communication.save()
diff --git a/erpnext/crm/doctype/prospect/prospect.py b/erpnext/crm/doctype/prospect/prospect.py
index cc4c1d3..39436f5 100644
--- a/erpnext/crm/doctype/prospect/prospect.py
+++ b/erpnext/crm/doctype/prospect/prospect.py
@@ -24,13 +24,15 @@
 
 	def after_insert(self):
 		if frappe.db.get_single_value("CRM Settings", "carry_forward_communication_and_comments"):
-			for row in self.get('prospect_lead'):
+			for row in self.get("prospect_lead"):
 				copy_comments("Lead", row.lead, self)
 				add_link_in_communication("Lead", row.lead, self)
 
 	def update_lead_details(self):
-		for row in self.get('prospect_lead'):
-			lead = frappe.get_value('Lead', row.lead, ['lead_name', 'status', 'email_id', 'mobile_no'], as_dict=True)
+		for row in self.get("prospect_lead"):
+			lead = frappe.get_value(
+				"Lead", row.lead, ["lead_name", "status", "email_id", "mobile_no"], as_dict=True
+			)
 			row.lead_name = lead.lead_name
 			row.status = lead.status
 			row.email = lead.email_id
@@ -38,39 +40,45 @@
 
 	def link_with_lead_contact_and_address(self):
 		for row in self.prospect_lead:
-			links = frappe.get_all('Dynamic Link', filters={'link_doctype': 'Lead', 'link_name': row.lead}, fields=['parent', 'parenttype'])
+			links = frappe.get_all(
+				"Dynamic Link",
+				filters={"link_doctype": "Lead", "link_name": row.lead},
+				fields=["parent", "parenttype"],
+			)
 			for link in links:
-				linked_doc = frappe.get_doc(link['parenttype'], link['parent'])
+				linked_doc = frappe.get_doc(link["parenttype"], link["parent"])
 				exists = False
 
-				for d in linked_doc.get('links'):
+				for d in linked_doc.get("links"):
 					if d.link_doctype == self.doctype and d.link_name == self.name:
 						exists = True
 
 				if not exists:
-					linked_doc.append('links', {
-						'link_doctype': self.doctype,
-						'link_name': self.name
-					})
+					linked_doc.append("links", {"link_doctype": self.doctype, "link_name": self.name})
 					linked_doc.save(ignore_permissions=True)
 
 	def unlink_dynamic_links(self):
-		links = frappe.get_all('Dynamic Link', filters={'link_doctype': self.doctype, 'link_name': self.name}, fields=['parent', 'parenttype'])
+		links = frappe.get_all(
+			"Dynamic Link",
+			filters={"link_doctype": self.doctype, "link_name": self.name},
+			fields=["parent", "parenttype"],
+		)
 
 		for link in links:
-			linked_doc = frappe.get_doc(link['parenttype'], link['parent'])
+			linked_doc = frappe.get_doc(link["parenttype"], link["parent"])
 
-			if len(linked_doc.get('links')) == 1:
+			if len(linked_doc.get("links")) == 1:
 				linked_doc.delete(ignore_permissions=True)
 			else:
 				to_remove = None
-				for d in linked_doc.get('links'):
+				for d in linked_doc.get("links"):
 					if d.link_doctype == self.doctype and d.link_name == self.name:
 						to_remove = d
 				if to_remove:
 					linked_doc.remove(to_remove)
 					linked_doc.save(ignore_permissions=True)
 
+
 @frappe.whitelist()
 def make_customer(source_name, target_doc=None):
 	def set_missing_values(source, target):
@@ -78,18 +86,23 @@
 		target.company_name = source.name
 		target.customer_group = source.customer_group or frappe.db.get_default("Customer Group")
 
-	doclist = get_mapped_doc("Prospect", source_name,
-		{"Prospect": {
-			"doctype": "Customer",
-			"field_map": {
-				"company_name": "customer_name",
-				"currency": "default_currency",
-				"fax": "fax"
+	doclist = get_mapped_doc(
+		"Prospect",
+		source_name,
+		{
+			"Prospect": {
+				"doctype": "Customer",
+				"field_map": {"company_name": "customer_name", "currency": "default_currency", "fax": "fax"},
 			}
-		}}, target_doc, set_missing_values, ignore_permissions=False)
+		},
+		target_doc,
+		set_missing_values,
+		ignore_permissions=False,
+	)
 
 	return doclist
 
+
 @frappe.whitelist()
 def make_opportunity(source_name, target_doc=None):
 	def set_missing_values(source, target):
@@ -97,12 +110,20 @@
 		target.customer_name = source.company_name
 		target.customer_group = source.customer_group or frappe.db.get_default("Customer Group")
 
-	doclist = get_mapped_doc("Prospect", source_name,
-		{"Prospect": {
-			"doctype": "Opportunity",
-			"field_map": {
-				"name": "party_name",
+	doclist = get_mapped_doc(
+		"Prospect",
+		source_name,
+		{
+			"Prospect": {
+				"doctype": "Opportunity",
+				"field_map": {
+					"name": "party_name",
+				},
 			}
-		}}, target_doc, set_missing_values, ignore_permissions=False)
+		},
+		target_doc,
+		set_missing_values,
+		ignore_permissions=False,
+	)
 
 	return doclist
diff --git a/erpnext/crm/doctype/prospect/test_prospect.py b/erpnext/crm/doctype/prospect/test_prospect.py
index fa44e20..ddd7b93 100644
--- a/erpnext/crm/doctype/prospect/test_prospect.py
+++ b/erpnext/crm/doctype/prospect/test_prospect.py
@@ -14,43 +14,45 @@
 	def test_add_lead_to_prospect_and_address_linking(self):
 		lead_doc = make_lead()
 		address_doc = make_address(address_title=lead_doc.name)
-		address_doc.append('links', {
-				"link_doctype": lead_doc.doctype,
-				"link_name": lead_doc.name
-			})
+		address_doc.append("links", {"link_doctype": lead_doc.doctype, "link_name": lead_doc.name})
 		address_doc.save()
 		prospect_doc = make_prospect()
 		add_lead_to_prospect(lead_doc.name, prospect_doc.name)
 		prospect_doc.reload()
 		lead_exists_in_prosoect = False
-		for rec in prospect_doc.get('prospect_lead'):
+		for rec in prospect_doc.get("prospect_lead"):
 			if rec.lead == lead_doc.name:
 				lead_exists_in_prosoect = True
 		self.assertEqual(lead_exists_in_prosoect, True)
 		address_doc.reload()
-		self.assertEqual(address_doc.has_link('Prospect', prospect_doc.name), True)
+		self.assertEqual(address_doc.has_link("Prospect", prospect_doc.name), True)
 
 
 def make_prospect(**args):
 	args = frappe._dict(args)
 
-	prospect_doc = frappe.get_doc({
-		"doctype": "Prospect",
-		"company_name": args.company_name or "_Test Company {}".format(random_string(3)),
-	}).insert()
+	prospect_doc = frappe.get_doc(
+		{
+			"doctype": "Prospect",
+			"company_name": args.company_name or "_Test Company {}".format(random_string(3)),
+		}
+	).insert()
 
 	return prospect_doc
 
+
 def make_address(**args):
 	args = frappe._dict(args)
 
-	address_doc = frappe.get_doc({
-		"doctype": "Address",
-		"address_title": args.address_title or "Address Title",
-		"address_type": args.address_type or "Billing",
-		"city": args.city or "Mumbai",
-		"address_line1": args.address_line1 or "Vidya Vihar West",
-		"country": args.country or "India"
-	}).insert()
+	address_doc = frappe.get_doc(
+		{
+			"doctype": "Address",
+			"address_title": args.address_title or "Address Title",
+			"address_type": args.address_type or "Billing",
+			"city": args.city or "Mumbai",
+			"address_line1": args.address_line1 or "Vidya Vihar West",
+			"country": args.country or "India",
+		}
+	).insert()
 
 	return address_doc
diff --git a/erpnext/crm/doctype/social_media_post/social_media_post.js b/erpnext/crm/doctype/social_media_post/social_media_post.js
index 6874caa..d4ac0ba 100644
--- a/erpnext/crm/doctype/social_media_post/social_media_post.js
+++ b/erpnext/crm/doctype/social_media_post/social_media_post.js
@@ -86,7 +86,7 @@
 				frm.trigger('add_post_btn');
 			}
 			if (frm.doc.post_status !='Deleted') {
-				frm.add_custom_button(('Delete Post'), function() {
+				frm.add_custom_button(__('Delete Post'), function() {
 					frappe.confirm(__('Are you sure want to delete the Post from Social Media platforms?'),
 						function() {
 							frappe.call({
diff --git a/erpnext/crm/doctype/social_media_post/social_media_post.py b/erpnext/crm/doctype/social_media_post/social_media_post.py
index 3f63c1d..55db29a 100644
--- a/erpnext/crm/doctype/social_media_post/social_media_post.py
+++ b/erpnext/crm/doctype/social_media_post/social_media_post.py
@@ -11,7 +11,7 @@
 
 class SocialMediaPost(Document):
 	def validate(self):
-		if (not self.twitter and not self.linkedin):
+		if not self.twitter and not self.linkedin:
 			frappe.throw(_("Select atleast one Social Media Platform to Share on."))
 
 		if self.scheduled_time:
@@ -29,7 +29,7 @@
 		super(SocialMediaPost, self).submit()
 
 	def on_cancel(self):
-		self.db_set('post_status', 'Cancelled')
+		self.db_set("post_status", "Cancelled")
 
 	@frappe.whitelist()
 	def delete_post(self):
@@ -41,17 +41,17 @@
 			linkedin = frappe.get_doc("LinkedIn Settings")
 			linkedin.delete_post(self.linkedin_post_id)
 
-		self.db_set('post_status', 'Deleted')
+		self.db_set("post_status", "Deleted")
 
 	@frappe.whitelist()
 	def get_post(self):
 		response = {}
 		if self.linkedin and self.linkedin_post_id:
 			linkedin = frappe.get_doc("LinkedIn Settings")
-			response['linkedin'] = linkedin.get_post(self.linkedin_post_id)
+			response["linkedin"] = linkedin.get_post(self.linkedin_post_id)
 		if self.twitter and self.twitter_post_id:
 			twitter = frappe.get_doc("Twitter Settings")
-			response['twitter'] = twitter.get_tweet(self.twitter_post_id)
+			response["twitter"] = twitter.get_tweet(self.twitter_post_id)
 
 		return response
 
@@ -65,21 +65,25 @@
 			if self.linkedin and not self.linkedin_post_id:
 				linkedin = frappe.get_doc("LinkedIn Settings")
 				linkedin_post = linkedin.post(self.linkedin_post, self.title, self.image)
-				self.db_set("linkedin_post_id", linkedin_post.headers['X-RestLi-Id'])
+				self.db_set("linkedin_post_id", linkedin_post.headers["X-RestLi-Id"])
 			self.db_set("post_status", "Posted")
 
 		except Exception:
 			self.db_set("post_status", "Error")
-			title = _("Error while POSTING {0}").format(self.name)
-			frappe.log_error(message=frappe.get_traceback(), title=title)
+			self.log_error("Social posting failed")
+
 
 def process_scheduled_social_media_posts():
-	posts = frappe.get_list("Social Media Post", filters={"post_status": "Scheduled", "docstatus":1}, fields= ["name", "scheduled_time"])
+	posts = frappe.get_list(
+		"Social Media Post",
+		filters={"post_status": "Scheduled", "docstatus": 1},
+		fields=["name", "scheduled_time"],
+	)
 	start = frappe.utils.now_datetime()
 	end = start + datetime.timedelta(minutes=10)
 	for post in posts:
 		if post.scheduled_time:
 			post_time = frappe.utils.get_datetime(post.scheduled_time)
 			if post_time > start and post_time <= end:
-				sm_post = frappe.get_doc('Social Media Post', post.name)
+				sm_post = frappe.get_doc("Social Media Post", post.name)
 				sm_post.post()
diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.py b/erpnext/crm/doctype/twitter_settings/twitter_settings.py
index be7d914..42874dd 100644
--- a/erpnext/crm/doctype/twitter_settings/twitter_settings.py
+++ b/erpnext/crm/doctype/twitter_settings/twitter_settings.py
@@ -16,22 +16,26 @@
 class TwitterSettings(Document):
 	@frappe.whitelist()
 	def get_authorize_url(self):
-		callback_url = "{0}/api/method/erpnext.crm.doctype.twitter_settings.twitter_settings.callback?".format(frappe.utils.get_url())
-		auth = tweepy.OAuthHandler(self.consumer_key, self.get_password(fieldname="consumer_secret"), callback_url)
+		callback_url = (
+			"{0}/api/method/erpnext.crm.doctype.twitter_settings.twitter_settings.callback?".format(
+				frappe.utils.get_url()
+			)
+		)
+		auth = tweepy.OAuthHandler(
+			self.consumer_key, self.get_password(fieldname="consumer_secret"), callback_url
+		)
 		try:
 			redirect_url = auth.get_authorization_url()
 			return redirect_url
 		except tweepy.TweepError as e:
 			frappe.msgprint(_("Error! Failed to get request token."))
-			frappe.throw(_('Invalid {0} or {1}').format(frappe.bold("Consumer Key"), frappe.bold("Consumer Secret Key")))
-
+			frappe.throw(
+				_("Invalid {0} or {1}").format(frappe.bold("Consumer Key"), frappe.bold("Consumer Secret Key"))
+			)
 
 	def get_access_token(self, oauth_token, oauth_verifier):
 		auth = tweepy.OAuthHandler(self.consumer_key, self.get_password(fieldname="consumer_secret"))
-		auth.request_token = {
-			'oauth_token' : oauth_token,
-			'oauth_token_secret' : oauth_verifier
-		}
+		auth.request_token = {"oauth_token": oauth_token, "oauth_token_secret": oauth_verifier}
 
 		try:
 			auth.get_access_token(oauth_verifier)
@@ -39,21 +43,25 @@
 			self.access_token_secret = auth.access_token_secret
 			api = self.get_api()
 			user = api.me()
-			profile_pic = (user._json["profile_image_url"]).replace("_normal","")
+			profile_pic = (user._json["profile_image_url"]).replace("_normal", "")
 
-			frappe.db.set_value(self.doctype, self.name, {
-				"access_token" : auth.access_token,
-				"access_token_secret" : auth.access_token_secret,
-				"account_name" : user._json["screen_name"],
-				"profile_pic" : profile_pic,
-				"session_status" : "Active"
-			})
+			frappe.db.set_value(
+				self.doctype,
+				self.name,
+				{
+					"access_token": auth.access_token,
+					"access_token_secret": auth.access_token_secret,
+					"account_name": user._json["screen_name"],
+					"profile_pic": profile_pic,
+					"session_status": "Active",
+				},
+			)
 
 			frappe.local.response["type"] = "redirect"
-			frappe.local.response["location"] = get_url_to_form("Twitter Settings","Twitter Settings")
+			frappe.local.response["location"] = get_url_to_form("Twitter Settings", "Twitter Settings")
 		except TweepError as e:
 			frappe.msgprint(_("Error! Failed to get access token."))
-			frappe.throw(_('Invalid Consumer Key or Consumer Secret Key'))
+			frappe.throw(_("Invalid Consumer Key or Consumer Secret Key"))
 
 	def get_api(self):
 		# authentication of consumer key and secret
@@ -82,9 +90,9 @@
 		api = self.get_api()
 		try:
 			if media_id:
-				response = api.update_status(status = text, media_ids = [media_id])
+				response = api.update_status(status=text, media_ids=[media_id])
 			else:
-				response = api.update_status(status = text)
+				response = api.update_status(status=text)
 
 			return response
 
@@ -113,15 +121,18 @@
 		if e.response.status_code == 401:
 			self.db_set("session_status", "Expired")
 			frappe.db.commit()
-		frappe.throw(content["message"],title=_("Twitter Error {0} : {1}").format(e.response.status_code, e.response.reason))
+		frappe.throw(
+			content["message"],
+			title=_("Twitter Error {0} : {1}").format(e.response.status_code, e.response.reason),
+		)
 
 
 @frappe.whitelist(allow_guest=True)
-def callback(oauth_token = None, oauth_verifier = None):
+def callback(oauth_token=None, oauth_verifier=None):
 	if oauth_token and oauth_verifier:
 		twitter_settings = frappe.get_single("Twitter Settings")
-		twitter_settings.get_access_token(oauth_token,oauth_verifier)
+		twitter_settings.get_access_token(oauth_token, oauth_verifier)
 		frappe.db.commit()
 	else:
 		frappe.local.response["type"] = "redirect"
-		frappe.local.response["location"] = get_url_to_form("Twitter Settings","Twitter Settings")
+		frappe.local.response["location"] = get_url_to_form("Twitter Settings", "Twitter Settings")
diff --git a/erpnext/crm/doctype/utils.py b/erpnext/crm/doctype/utils.py
index 9b56170..6bcfcb7 100644
--- a/erpnext/crm/doctype/utils.py
+++ b/erpnext/crm/doctype/utils.py
@@ -4,16 +4,17 @@
 @frappe.whitelist()
 def get_last_interaction(contact=None, lead=None):
 
-	if not contact and not lead: return
+	if not contact and not lead:
+		return
 
 	last_communication = None
 	last_issue = None
 	if contact:
-		query_condition = ''
+		query_condition = ""
 		values = []
-		contact = frappe.get_doc('Contact', contact)
+		contact = frappe.get_doc("Contact", contact)
 		for link in contact.links:
-			if link.link_doctype == 'Customer':
+			if link.link_doctype == "Customer":
 				last_issue = get_last_issue_from_customer(link.link_name)
 			query_condition += "(`reference_doctype`=%s AND `reference_name`=%s) OR"
 			values += [link.link_doctype, link.link_name]
@@ -21,65 +22,82 @@
 		if query_condition:
 			# remove extra appended 'OR'
 			query_condition = query_condition[:-2]
-			last_communication = frappe.db.sql("""
+			last_communication = frappe.db.sql(
+				"""
 				SELECT `name`, `content`
 				FROM `tabCommunication`
 				WHERE `sent_or_received`='Received'
 				AND ({})
 				ORDER BY `modified`
 				LIMIT 1
-			""".format(query_condition), values, as_dict=1)  # nosec
+			""".format(
+					query_condition
+				),
+				values,
+				as_dict=1,
+			)  # nosec
 
 	if lead:
-		last_communication = frappe.get_all('Communication', filters={
-			'reference_doctype': 'Lead',
-			'reference_name': lead,
-			'sent_or_received': 'Received'
-		}, fields=['name', 'content'], order_by='`creation` DESC', limit=1)
+		last_communication = frappe.get_all(
+			"Communication",
+			filters={"reference_doctype": "Lead", "reference_name": lead, "sent_or_received": "Received"},
+			fields=["name", "content"],
+			order_by="`creation` DESC",
+			limit=1,
+		)
 
 	last_communication = last_communication[0] if last_communication else None
 
-	return {
-		'last_communication': last_communication,
-		'last_issue': last_issue
-	}
+	return {"last_communication": last_communication, "last_issue": last_issue}
+
 
 def get_last_issue_from_customer(customer_name):
-	issues = frappe.get_all('Issue', {
-		'customer': customer_name
-	}, ['name', 'subject', 'customer'], order_by='`creation` DESC', limit=1)
+	issues = frappe.get_all(
+		"Issue",
+		{"customer": customer_name},
+		["name", "subject", "customer"],
+		order_by="`creation` DESC",
+		limit=1,
+	)
 
 	return issues[0] if issues else None
 
 
 def get_scheduled_employees_for_popup(communication_medium):
-	if not communication_medium: return []
+	if not communication_medium:
+		return []
 
 	now_time = frappe.utils.nowtime()
 	weekday = frappe.utils.get_weekday()
 
-	available_employee_groups = frappe.get_all("Communication Medium Timeslot", filters={
-		'day_of_week': weekday,
-		'parent': communication_medium,
-		'from_time': ['<=', now_time],
-		'to_time': ['>=', now_time],
-	}, fields=['employee_group'])
+	available_employee_groups = frappe.get_all(
+		"Communication Medium Timeslot",
+		filters={
+			"day_of_week": weekday,
+			"parent": communication_medium,
+			"from_time": ["<=", now_time],
+			"to_time": [">=", now_time],
+		},
+		fields=["employee_group"],
+	)
 
 	available_employee_groups = tuple([emp.employee_group for emp in available_employee_groups])
 
-	employees = frappe.get_all('Employee Group Table', filters={
-		'parent': ['in', available_employee_groups]
-	}, fields=['user_id'])
+	employees = frappe.get_all(
+		"Employee Group Table", filters={"parent": ["in", available_employee_groups]}, fields=["user_id"]
+	)
 
 	employee_emails = set([employee.user_id for employee in employees])
 
 	return employee_emails
 
+
 def strip_number(number):
-	if not number: return
+	if not number:
+		return
 	# strip + and 0 from the start of the number for proper number comparisions
 	# eg. +7888383332 should match with 7888383332
 	# eg. 07888383332 should match with 7888383332
-	number = number.lstrip('+')
-	number = number.lstrip('0')
+	number = number.lstrip("+")
+	number = number.lstrip("0")
 	return number
diff --git a/erpnext/crm/report/campaign_efficiency/campaign_efficiency.py b/erpnext/crm/report/campaign_efficiency/campaign_efficiency.py
index 6f3e311..be7f5ca 100644
--- a/erpnext/crm/report/campaign_efficiency/campaign_efficiency.py
+++ b/erpnext/crm/report/campaign_efficiency/campaign_efficiency.py
@@ -9,77 +9,40 @@
 
 def execute(filters=None):
 	columns, data = [], []
-	columns=get_columns("Campaign Name")
-	data=get_lead_data(filters or {}, "Campaign Name")
+	columns = get_columns("Campaign Name")
+	data = get_lead_data(filters or {}, "Campaign Name")
 	return columns, data
 
+
 def get_columns(based_on):
 	return [
-		{
-			"fieldname": frappe.scrub(based_on),
-			"label": _(based_on),
-			"fieldtype": "Data",
-			"width": 150
-		},
-		{
-			"fieldname": "lead_count",
-			"label": _("Lead Count"),
-			"fieldtype": "Int",
-			"width": 80
-		},
-		{
-			"fieldname": "opp_count",
-			"label": _("Opp Count"),
-			"fieldtype": "Int",
-			"width": 80
-		},
-		{
-			"fieldname": "quot_count",
-			"label": _("Quot Count"),
-			"fieldtype": "Int",
-			"width": 80
-		},
-		{
-			"fieldname": "order_count",
-			"label": _("Order Count"),
-			"fieldtype": "Int",
-			"width": 100
-		},
-		{
-			"fieldname": "order_value",
-			"label": _("Order Value"),
-			"fieldtype": "Float",
-			"width": 100
-		},
-		{
-			"fieldname": "opp_lead",
-			"label": _("Opp/Lead %"),
-			"fieldtype": "Float",
-			"width": 100
-		},
-		{
-			"fieldname": "quot_lead",
-			"label": _("Quot/Lead %"),
-			"fieldtype": "Float",
-			"width": 100
-		},
-		{
-			"fieldname": "order_quot",
-			"label": _("Order/Quot %"),
-			"fieldtype": "Float",
-			"width": 100
-		}
+		{"fieldname": frappe.scrub(based_on), "label": _(based_on), "fieldtype": "Data", "width": 150},
+		{"fieldname": "lead_count", "label": _("Lead Count"), "fieldtype": "Int", "width": 80},
+		{"fieldname": "opp_count", "label": _("Opp Count"), "fieldtype": "Int", "width": 80},
+		{"fieldname": "quot_count", "label": _("Quot Count"), "fieldtype": "Int", "width": 80},
+		{"fieldname": "order_count", "label": _("Order Count"), "fieldtype": "Int", "width": 100},
+		{"fieldname": "order_value", "label": _("Order Value"), "fieldtype": "Float", "width": 100},
+		{"fieldname": "opp_lead", "label": _("Opp/Lead %"), "fieldtype": "Float", "width": 100},
+		{"fieldname": "quot_lead", "label": _("Quot/Lead %"), "fieldtype": "Float", "width": 100},
+		{"fieldname": "order_quot", "label": _("Order/Quot %"), "fieldtype": "Float", "width": 100},
 	]
 
+
 def get_lead_data(filters, based_on):
 	based_on_field = frappe.scrub(based_on)
 	conditions = get_filter_conditions(filters)
 
-	lead_details = frappe.db.sql("""
+	lead_details = frappe.db.sql(
+		"""
 		select {based_on_field}, name
 		from `tabLead`
 		where {based_on_field} is not null and {based_on_field} != '' {conditions}
-	""".format(based_on_field=based_on_field, conditions=conditions), filters, as_dict=1)
+	""".format(
+			based_on_field=based_on_field, conditions=conditions
+		),
+		filters,
+		as_dict=1,
+	)
 
 	lead_map = frappe._dict()
 	for d in lead_details:
@@ -87,11 +50,8 @@
 
 	data = []
 	for based_on_value, leads in lead_map.items():
-		row = {
-			based_on_field: based_on_value,
-			"lead_count": len(leads)
-		}
-		row["quot_count"]= get_lead_quotation_count(leads)
+		row = {based_on_field: based_on_value, "lead_count": len(leads)}
+		row["quot_count"] = get_lead_quotation_count(leads)
 		row["opp_count"] = get_lead_opp_count(leads)
 		row["order_count"] = get_quotation_ordered_count(leads)
 		row["order_value"] = get_order_amount(leads) or 0
@@ -105,8 +65,9 @@
 
 	return data
 
+
 def get_filter_conditions(filters):
-	conditions=""
+	conditions = ""
 	if filters.from_date:
 		conditions += " and date(creation) >= %(from_date)s"
 	if filters.to_date:
@@ -114,23 +75,45 @@
 
 	return conditions
 
+
 def get_lead_quotation_count(leads):
-	return frappe.db.sql("""select count(name) from `tabQuotation`
-		where quotation_to = 'Lead' and party_name in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0] #nosec
+	return frappe.db.sql(
+		"""select count(name) from `tabQuotation`
+		where quotation_to = 'Lead' and party_name in (%s)"""
+		% ", ".join(["%s"] * len(leads)),
+		tuple(leads),
+	)[0][
+		0
+	]  # nosec
+
 
 def get_lead_opp_count(leads):
-	return frappe.db.sql("""select count(name) from `tabOpportunity`
-	where opportunity_from = 'Lead' and party_name in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
+	return frappe.db.sql(
+		"""select count(name) from `tabOpportunity`
+	where opportunity_from = 'Lead' and party_name in (%s)"""
+		% ", ".join(["%s"] * len(leads)),
+		tuple(leads),
+	)[0][0]
+
 
 def get_quotation_ordered_count(leads):
-	return frappe.db.sql("""select count(name)
+	return frappe.db.sql(
+		"""select count(name)
 		from `tabQuotation` where status = 'Ordered' and quotation_to = 'Lead'
-		and party_name in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
+		and party_name in (%s)"""
+		% ", ".join(["%s"] * len(leads)),
+		tuple(leads),
+	)[0][0]
+
 
 def get_order_amount(leads):
-	return frappe.db.sql("""select sum(base_net_amount)
+	return frappe.db.sql(
+		"""select sum(base_net_amount)
 		from `tabSales Order Item`
 		where prevdoc_docname in (
 			select name from `tabQuotation` where status = 'Ordered'
 			and quotation_to = 'Lead' and party_name in (%s)
-		)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
+		)"""
+		% ", ".join(["%s"] * len(leads)),
+		tuple(leads),
+	)[0][0]
diff --git a/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.py b/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.py
index ed6cefb..db36581 100644
--- a/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.py
+++ b/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.py
@@ -3,25 +3,22 @@
 
 
 import frappe
+from frappe import _
 
 
 def execute(filters=None):
 	columns = [
+		{"fieldname": "creation_date", "label": _("Date"), "fieldtype": "Date", "width": 300},
 		{
-			'fieldname': 'creation_date',
-			'label': 'Date',
-			'fieldtype': 'Date',
-			'width': 300
-		},
-		{
-			'fieldname': 'first_response_time',
-			'fieldtype': 'Duration',
-			'label': 'First Response Time',
-			'width': 300
+			"fieldname": "first_response_time",
+			"fieldtype": "Duration",
+			"label": "First Response Time",
+			"width": 300,
 		},
 	]
 
-	data = frappe.db.sql('''
+	data = frappe.db.sql(
+		"""
 		SELECT
 			date(creation) as creation_date,
 			avg(first_response_time) as avg_response_time
@@ -31,6 +28,8 @@
 			and first_response_time > 0
 		GROUP BY creation_date
 		ORDER BY creation_date desc
-	''', (filters.from_date, filters.to_date))
+	""",
+		(filters.from_date, filters.to_date),
+	)
 
 	return columns, data
diff --git a/erpnext/crm/report/lead_conversion_time/lead_conversion_time.py b/erpnext/crm/report/lead_conversion_time/lead_conversion_time.py
index 1f43fa0..d7d964d 100644
--- a/erpnext/crm/report/lead_conversion_time/lead_conversion_time.py
+++ b/erpnext/crm/report/lead_conversion_time/lead_conversion_time.py
@@ -8,7 +8,8 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	communication_list = get_communication_details(filters)
 	columns = get_columns()
@@ -19,8 +20,12 @@
 
 	data = []
 	for communication in communication_list:
-		row = [communication.get('customer'), communication.get('interactions'),\
-			communication.get('duration'), communication.get('support_tickets')]
+		row = [
+			communication.get("customer"),
+			communication.get("interactions"),
+			communication.get("duration"),
+			communication.get("support_tickets"),
+		]
 		data.append(row)
 
 	# add the average row
@@ -32,9 +37,17 @@
 		total_interactions += row[1]
 		total_duration += row[2]
 		total_tickets += row[3]
-	data.append(['Average', total_interactions/len(data), total_duration/len(data), total_tickets/len(data)])
+	data.append(
+		[
+			"Average",
+			total_interactions / len(data),
+			total_duration / len(data),
+			total_tickets / len(data),
+		]
+	)
 	return columns, data
 
+
 def get_columns():
 	return [
 		{
@@ -42,36 +55,37 @@
 			"fieldname": "customer",
 			"fieldtype": "Link",
 			"options": "Customer",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("No of Interactions"),
 			"fieldname": "interactions",
 			"fieldtype": "Float",
-			"width": 120
+			"width": 120,
 		},
-		{
-			"label": _("Duration in Days"),
-			"fieldname": "duration",
-			"fieldtype": "Float",
-			"width": 120
-		},
+		{"label": _("Duration in Days"), "fieldname": "duration", "fieldtype": "Float", "width": 120},
 		{
 			"label": _("Support Tickets"),
 			"fieldname": "support_tickets",
 			"fieldtype": "Float",
-			"width": 120
-		}
+			"width": 120,
+		},
 	]
 
+
 def get_communication_details(filters):
 	communication_count = None
 	communication_list = []
-	opportunities = frappe.db.get_values('Opportunity', {'opportunity_from': 'Lead'},\
-		['name', 'customer_name', 'contact_email'], as_dict=1)
+	opportunities = frappe.db.get_values(
+		"Opportunity",
+		{"opportunity_from": "Lead"},
+		["name", "customer_name", "contact_email"],
+		as_dict=1,
+	)
 
 	for d in opportunities:
-		invoice = frappe.db.sql('''
+		invoice = frappe.db.sql(
+			"""
 				SELECT
 					date(creation)
 				FROM
@@ -81,22 +95,30 @@
 				ORDER BY
 					creation
 				LIMIT 1
-			''', (d.contact_email, filters.from_date, filters.to_date))
+			""",
+			(d.contact_email, filters.from_date, filters.to_date),
+		)
 
-		if not invoice: continue
+		if not invoice:
+			continue
 
-		communication_count = frappe.db.sql('''
+		communication_count = frappe.db.sql(
+			"""
 				SELECT
 					count(*)
 				FROM
 					`tabCommunication`
 				WHERE
 					sender = %s AND date(communication_date) <= %s
-			''', (d.contact_email, invoice))[0][0]
+			""",
+			(d.contact_email, invoice),
+		)[0][0]
 
-		if not communication_count: continue
+		if not communication_count:
+			continue
 
-		first_contact = frappe.db.sql('''
+		first_contact = frappe.db.sql(
+			"""
 				SELECT
 					date(communication_date)
 				FROM
@@ -106,10 +128,19 @@
 				ORDER BY
 					communication_date
 				LIMIT 1
-			''', (d.contact_email))[0][0]
+			""",
+			(d.contact_email),
+		)[0][0]
 
 		duration = flt(date_diff(invoice[0][0], first_contact))
 
-		support_tickets = len(frappe.db.get_all('Issue', {'raised_by': d.contact_email}))
-		communication_list.append({'customer': d.customer_name, 'interactions': communication_count, 'duration': duration, 'support_tickets': support_tickets})
+		support_tickets = len(frappe.db.get_all("Issue", {"raised_by": d.contact_email}))
+		communication_list.append(
+			{
+				"customer": d.customer_name,
+				"interactions": communication_count,
+				"duration": duration,
+				"support_tickets": support_tickets,
+			}
+		)
 	return communication_list
diff --git a/erpnext/crm/report/lead_details/lead_details.py b/erpnext/crm/report/lead_details/lead_details.py
index 09eba7c..8660c73 100644
--- a/erpnext/crm/report/lead_details/lead_details.py
+++ b/erpnext/crm/report/lead_details/lead_details.py
@@ -10,6 +10,7 @@
 	columns, data = get_columns(), get_data(filters)
 	return columns, data
 
+
 def get_columns():
 	columns = [
 		{
@@ -19,101 +20,57 @@
 			"options": "Lead",
 			"width": 150,
 		},
+		{"label": _("Lead Name"), "fieldname": "lead_name", "fieldtype": "Data", "width": 120},
+		{"fieldname": "status", "label": _("Status"), "fieldtype": "Data", "width": 100},
 		{
-			"label": _("Lead Name"),
-			"fieldname": "lead_name",
-			"fieldtype": "Data",
-			"width": 120
-		},
-		{
-			"fieldname":"status",
-			"label": _("Status"),
-			"fieldtype": "Data",
-			"width": 100
-		},
-		{
-			"fieldname":"lead_owner",
+			"fieldname": "lead_owner",
 			"label": _("Lead Owner"),
 			"fieldtype": "Link",
 			"options": "User",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Territory"),
 			"fieldname": "territory",
 			"fieldtype": "Link",
 			"options": "Territory",
-			"width": 100
+			"width": 100,
 		},
-		{
-			"label": _("Source"),
-			"fieldname": "source",
-			"fieldtype": "Data",
-			"width": 120
-		},
-		{
-			"label": _("Email"),
-			"fieldname": "email_id",
-			"fieldtype": "Data",
-			"width": 120
-		},
-		{
-			"label": _("Mobile"),
-			"fieldname": "mobile_no",
-			"fieldtype": "Data",
-			"width": 120
-		},
-		{
-			"label": _("Phone"),
-			"fieldname": "phone",
-			"fieldtype": "Data",
-			"width": 120
-		},
+		{"label": _("Source"), "fieldname": "source", "fieldtype": "Data", "width": 120},
+		{"label": _("Email"), "fieldname": "email_id", "fieldtype": "Data", "width": 120},
+		{"label": _("Mobile"), "fieldname": "mobile_no", "fieldtype": "Data", "width": 120},
+		{"label": _("Phone"), "fieldname": "phone", "fieldtype": "Data", "width": 120},
 		{
 			"label": _("Owner"),
 			"fieldname": "owner",
 			"fieldtype": "Link",
 			"options": "user",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("Company"),
 			"fieldname": "company",
 			"fieldtype": "Link",
 			"options": "Company",
-			"width": 120
+			"width": 120,
 		},
+		{"fieldname": "address", "label": _("Address"), "fieldtype": "Data", "width": 130},
+		{"fieldname": "state", "label": _("State"), "fieldtype": "Data", "width": 100},
+		{"fieldname": "pincode", "label": _("Postal Code"), "fieldtype": "Data", "width": 90},
 		{
-			"fieldname":"address",
-			"label": _("Address"),
-			"fieldtype": "Data",
-			"width": 130
-		},
-		{
-			"fieldname":"state",
-			"label": _("State"),
-			"fieldtype": "Data",
-			"width": 100
-		},
-		{
-			"fieldname":"pincode",
-			"label": _("Postal Code"),
-			"fieldtype": "Data",
-			"width": 90
-		},
-		{
-			"fieldname":"country",
+			"fieldname": "country",
 			"label": _("Country"),
 			"fieldtype": "Link",
 			"options": "Country",
-			"width": 100
+			"width": 100,
 		},
-
 	]
 	return columns
 
+
 def get_data(filters):
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		SELECT
 			`tabLead`.name,
 			`tabLead`.lead_name,
@@ -144,9 +101,15 @@
 			AND `tabLead`.creation BETWEEN %(from_date)s AND %(to_date)s
 			{conditions}
 		ORDER BY
-			`tabLead`.creation asc """.format(conditions=get_conditions(filters)), filters, as_dict=1)
+			`tabLead`.creation asc """.format(
+			conditions=get_conditions(filters)
+		),
+		filters,
+		as_dict=1,
+	)
 
-def get_conditions(filters) :
+
+def get_conditions(filters):
 	conditions = []
 
 	if filters.get("territory"):
diff --git a/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py b/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py
index 2932211..996b1b4 100644
--- a/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py
+++ b/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py
@@ -9,10 +9,11 @@
 
 def execute(filters=None):
 	columns, data = [], []
-	columns=get_columns()
-	data=get_lead_data(filters, "Lead Owner")
+	columns = get_columns()
+	data = get_lead_data(filters, "Lead Owner")
 	return columns, data
 
+
 def get_columns():
 	return [
 		{
@@ -20,54 +21,14 @@
 			"label": _("Lead Owner"),
 			"fieldtype": "Link",
 			"options": "User",
-			"width": "130"
+			"width": "130",
 		},
-		{
-			"fieldname": "lead_count",
-			"label": _("Lead Count"),
-			"fieldtype": "Int",
-			"width": "80"
-		},
-		{
-			"fieldname": "opp_count",
-			"label": _("Opp Count"),
-			"fieldtype": "Int",
-			"width": "80"
-		},
-		{
-			"fieldname": "quot_count",
-			"label": _("Quot Count"),
-			"fieldtype": "Int",
-			"width": "80"
-		},
-		{
-			"fieldname": "order_count",
-			"label": _("Order Count"),
-			"fieldtype": "Int",
-			"width": "100"
-		},
-		{
-			"fieldname": "order_value",
-			"label": _("Order Value"),
-			"fieldtype": "Float",
-			"width": "100"
-		},
-		{
-			"fieldname": "opp_lead",
-			"label": _("Opp/Lead %"),
-			"fieldtype": "Float",
-			"width": "100"
-		},
-		{
-			"fieldname": "quot_lead",
-			"label": _("Quot/Lead %"),
-			"fieldtype": "Float",
-			"width": "100"
-		},
-		{
-			"fieldname": "order_quot",
-			"label": _("Order/Quot %"),
-			"fieldtype": "Float",
-			"width": "100"
-		}
+		{"fieldname": "lead_count", "label": _("Lead Count"), "fieldtype": "Int", "width": "80"},
+		{"fieldname": "opp_count", "label": _("Opp Count"), "fieldtype": "Int", "width": "80"},
+		{"fieldname": "quot_count", "label": _("Quot Count"), "fieldtype": "Int", "width": "80"},
+		{"fieldname": "order_count", "label": _("Order Count"), "fieldtype": "Int", "width": "100"},
+		{"fieldname": "order_value", "label": _("Order Value"), "fieldtype": "Float", "width": "100"},
+		{"fieldname": "opp_lead", "label": _("Opp/Lead %"), "fieldtype": "Float", "width": "100"},
+		{"fieldname": "quot_lead", "label": _("Quot/Lead %"), "fieldtype": "Float", "width": "100"},
+		{"fieldname": "order_quot", "label": _("Order/Quot %"), "fieldtype": "Float", "width": "100"},
 	]
diff --git a/erpnext/crm/report/lost_opportunity/lost_opportunity.py b/erpnext/crm/report/lost_opportunity/lost_opportunity.py
index 60d4be8..a57b44b 100644
--- a/erpnext/crm/report/lost_opportunity/lost_opportunity.py
+++ b/erpnext/crm/report/lost_opportunity/lost_opportunity.py
@@ -10,6 +10,7 @@
 	columns, data = get_columns(), get_data(filters)
 	return columns, data
 
+
 def get_columns():
 	columns = [
 		{
@@ -24,59 +25,56 @@
 			"fieldname": "opportunity_from",
 			"fieldtype": "Link",
 			"options": "DocType",
-			"width": 130
+			"width": 130,
 		},
 		{
 			"label": _("Party"),
-			"fieldname":"party_name",
+			"fieldname": "party_name",
 			"fieldtype": "Dynamic Link",
 			"options": "opportunity_from",
-			"width": 160
+			"width": 160,
 		},
 		{
 			"label": _("Customer/Lead Name"),
-			"fieldname":"customer_name",
+			"fieldname": "customer_name",
 			"fieldtype": "Data",
-			"width": 150
+			"width": 150,
 		},
 		{
 			"label": _("Opportunity Type"),
 			"fieldname": "opportunity_type",
 			"fieldtype": "Data",
-			"width": 130
+			"width": 130,
 		},
-		{
-			"label": _("Lost Reasons"),
-			"fieldname": "lost_reason",
-			"fieldtype": "Data",
-			"width": 220
-		},
+		{"label": _("Lost Reasons"), "fieldname": "lost_reason", "fieldtype": "Data", "width": 220},
 		{
 			"label": _("Sales Stage"),
 			"fieldname": "sales_stage",
 			"fieldtype": "Link",
 			"options": "Sales Stage",
-			"width": 150
+			"width": 150,
 		},
 		{
 			"label": _("Territory"),
 			"fieldname": "territory",
 			"fieldtype": "Link",
 			"options": "Territory",
-			"width": 150
+			"width": 150,
 		},
 		{
 			"label": _("Next Contact By"),
 			"fieldname": "contact_by",
 			"fieldtype": "Link",
 			"options": "User",
-			"width": 150
-		}
+			"width": 150,
+		},
 	]
 	return columns
 
+
 def get_data(filters):
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		SELECT
 			`tabOpportunity`.name,
 			`tabOpportunity`.opportunity_from,
@@ -97,7 +95,12 @@
 		GROUP BY
 			`tabOpportunity`.name
 		ORDER BY
-			`tabOpportunity`.creation asc  """.format(conditions=get_conditions(filters), join=get_join(filters)), filters, as_dict=1)
+			`tabOpportunity`.creation asc  """.format(
+			conditions=get_conditions(filters), join=get_join(filters)
+		),
+		filters,
+		as_dict=1,
+	)
 
 
 def get_conditions(filters):
@@ -117,6 +120,7 @@
 
 	return " ".join(conditions) if conditions else ""
 
+
 def get_join(filters):
 	join = """LEFT JOIN `tabOpportunity Lost Reason Detail`
 			ON `tabOpportunity Lost Reason Detail`.parenttype = 'Opportunity' and
@@ -127,6 +131,8 @@
 			ON `tabOpportunity Lost Reason Detail`.parenttype = 'Opportunity' and
 			`tabOpportunity Lost Reason Detail`.parent = `tabOpportunity`.name and
 			`tabOpportunity Lost Reason Detail`.lost_reason = '{0}'
-			""".format(filters.get("lost_reason"))
+			""".format(
+			filters.get("lost_reason")
+		)
 
 	return join
diff --git a/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.py b/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.py
index f53b5bd..3a46fb0 100644
--- a/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.py
+++ b/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.py
@@ -1,9 +1,9 @@
 # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
 import json
+from itertools import groupby
 
 import frappe
-import pandas
 from frappe import _
 from frappe.utils import flt
 
@@ -13,8 +13,9 @@
 def execute(filters=None):
 	return OpportunitySummaryBySalesStage(filters).run()
 
+
 class OpportunitySummaryBySalesStage(object):
-	def __init__(self,filters=None):
+	def __init__(self, filters=None):
 		self.filters = frappe._dict(filters or {})
 
 	def run(self):
@@ -26,98 +27,95 @@
 	def get_columns(self):
 		self.columns = []
 
-		if self.filters.get('based_on') == 'Opportunity Owner':
-			self.columns.append({
-				'label': _('Opportunity Owner'),
-				'fieldname': 'opportunity_owner',
-				'width': 200
-			})
+		if self.filters.get("based_on") == "Opportunity Owner":
+			self.columns.append(
+				{"label": _("Opportunity Owner"), "fieldname": "opportunity_owner", "width": 200}
+			)
 
-		if self.filters.get('based_on') == 'Source':
-			self.columns.append({
-				'label': _('Source'),
-				'fieldname': 'source',
-				'fieldtype': 'Link',
-				'options': 'Lead Source',
-				'width': 200
-			})
+		if self.filters.get("based_on") == "Source":
+			self.columns.append(
+				{
+					"label": _("Source"),
+					"fieldname": "source",
+					"fieldtype": "Link",
+					"options": "Lead Source",
+					"width": 200,
+				}
+			)
 
-		if self.filters.get('based_on') == 'Opportunity Type':
-			self.columns.append({
-				'label': _('Opportunity Type'),
-				'fieldname': 'opportunity_type',
-				'width': 200
-			})
+		if self.filters.get("based_on") == "Opportunity Type":
+			self.columns.append(
+				{"label": _("Opportunity Type"), "fieldname": "opportunity_type", "width": 200}
+			)
 
 		self.set_sales_stage_columns()
 
 	def set_sales_stage_columns(self):
-		self.sales_stage_list = frappe.db.get_list('Sales Stage', pluck='name')
+		self.sales_stage_list = frappe.db.get_list("Sales Stage", pluck="name")
 
 		for sales_stage in self.sales_stage_list:
-			if self.filters.get('data_based_on') == 'Number':
-				self.columns.append({
-					'label': _(sales_stage),
-					'fieldname': sales_stage,
-					'fieldtype': 'Int',
-					'width': 150
-				})
+			if self.filters.get("data_based_on") == "Number":
+				self.columns.append(
+					{"label": _(sales_stage), "fieldname": sales_stage, "fieldtype": "Int", "width": 150}
+				)
 
-			elif self.filters.get('data_based_on') == 'Amount':
-				self.columns.append({
-					'label': _(sales_stage),
-					'fieldname': sales_stage,
-					'fieldtype': 'Currency',
-					'width': 150
-				})
+			elif self.filters.get("data_based_on") == "Amount":
+				self.columns.append(
+					{"label": _(sales_stage), "fieldname": sales_stage, "fieldtype": "Currency", "width": 150}
+				)
 
 	def get_data(self):
 		self.data = []
 
 		based_on = {
-			'Opportunity Owner': '_assign',
-			'Source': 'source',
-			'Opportunity Type': 'opportunity_type'
-		}[self.filters.get('based_on')]
+			"Opportunity Owner": "_assign",
+			"Source": "source",
+			"Opportunity Type": "opportunity_type",
+		}[self.filters.get("based_on")]
 
 		data_based_on = {
-			'Number': 'count(name) as count',
-			'Amount': 'opportunity_amount as amount',
-		}[self.filters.get('data_based_on')]
+			"Number": "count(name) as count",
+			"Amount": "opportunity_amount as amount",
+		}[self.filters.get("data_based_on")]
 
 		self.get_data_query(based_on, data_based_on)
 
 		self.get_rows()
 
 	def get_data_query(self, based_on, data_based_on):
-		if self.filters.get('data_based_on') == 'Number':
-			group_by = '{},{}'.format('sales_stage', based_on)
-			self.query_result = frappe.db.get_list('Opportunity',
+		if self.filters.get("data_based_on") == "Number":
+			group_by = "{},{}".format("sales_stage", based_on)
+			self.query_result = frappe.db.get_list(
+				"Opportunity",
 				filters=self.get_conditions(),
-				fields=['sales_stage', data_based_on, based_on],
-				group_by=group_by
+				fields=["sales_stage", data_based_on, based_on],
+				group_by=group_by,
 			)
 
-		elif self.filters.get('data_based_on') == 'Amount':
-			self.query_result = frappe.db.get_list('Opportunity',
+		elif self.filters.get("data_based_on") == "Amount":
+			self.query_result = frappe.db.get_list(
+				"Opportunity",
 				filters=self.get_conditions(),
-				fields=['sales_stage', based_on, data_based_on, 'currency']
+				fields=["sales_stage", based_on, data_based_on, "currency"],
 			)
 
 			self.convert_to_base_currency()
 
-			dataframe = pandas.DataFrame.from_records(self.query_result)
-			dataframe.replace(to_replace=[None], value='Not Assigned', inplace=True)
-			result = dataframe.groupby(['sales_stage', based_on], as_index=False)['amount'].sum()
+			for row in self.query_result:
+				if not row.get(based_on):
+					row[based_on] = "Not Assigned"
 
 			self.grouped_data = []
 
-			for i in range(len(result['amount'])):
-				self.grouped_data.append({
-					'sales_stage': result['sales_stage'][i],
-					based_on : result[based_on][i],
-					'amount': result['amount'][i]
-				})
+			grouping_key = lambda o: (o["sales_stage"], o[based_on])  # noqa
+			for (sales_stage, _based_on), rows in groupby(self.query_result, grouping_key):
+				self.grouped_data.append(
+					{
+						"sales_stage": sales_stage,
+						based_on: _based_on,
+						"amount": sum(flt(r["amount"]) for r in rows),
+					}
+				)
 
 			self.query_result = self.grouped_data
 
@@ -125,17 +123,17 @@
 		self.data = []
 		self.get_formatted_data()
 
-		for based_on,data in self.formatted_data.items():
-			row_based_on={
-				'Opportunity Owner': 'opportunity_owner',
-				'Source': 'source',
-				'Opportunity Type': 'opportunity_type'
-			}[self.filters.get('based_on')]
+		for based_on, data in self.formatted_data.items():
+			row_based_on = {
+				"Opportunity Owner": "opportunity_owner",
+				"Source": "source",
+				"Opportunity Type": "opportunity_type",
+			}[self.filters.get("based_on")]
 
 			row = {row_based_on: based_on}
 
 			for d in self.query_result:
-				sales_stage = d.get('sales_stage')
+				sales_stage = d.get("sales_stage")
 				row[sales_stage] = data.get(sales_stage)
 
 			self.data.append(row)
@@ -144,24 +142,21 @@
 		self.formatted_data = frappe._dict()
 
 		for d in self.query_result:
-			data_based_on ={
-				'Number': 'count',
-				'Amount': 'amount'
-			}[self.filters.get('data_based_on')]
+			data_based_on = {"Number": "count", "Amount": "amount"}[self.filters.get("data_based_on")]
 
-			based_on ={
-				'Opportunity Owner': '_assign',
-				'Source': 'source',
-				'Opportunity Type': 'opportunity_type'
-			}[self.filters.get('based_on')]
+			based_on = {
+				"Opportunity Owner": "_assign",
+				"Source": "source",
+				"Opportunity Type": "opportunity_type",
+			}[self.filters.get("based_on")]
 
-			if self.filters.get('based_on') == 'Opportunity Owner':
-				if d.get(based_on) == '[]' or d.get(based_on) is None or d.get(based_on) == 'Not Assigned':
-					assignments = ['Not Assigned']
+			if self.filters.get("based_on") == "Opportunity Owner":
+				if d.get(based_on) == "[]" or d.get(based_on) is None or d.get(based_on) == "Not Assigned":
+					assignments = ["Not Assigned"]
 				else:
 					assignments = json.loads(d.get(based_on))
 
-				sales_stage = d.get('sales_stage')
+				sales_stage = d.get("sales_stage")
 				count = d.get(data_based_on)
 
 				if assignments:
@@ -173,7 +168,7 @@
 						self.set_formatted_data_based_on_sales_stage(assigned_to, sales_stage, count)
 			else:
 				value = d.get(based_on)
-				sales_stage = d.get('sales_stage')
+				sales_stage = d.get("sales_stage")
 				count = d.get(data_based_on)
 				self.set_formatted_data_based_on_sales_stage(value, sales_stage, count)
 
@@ -184,20 +179,22 @@
 	def get_conditions(self):
 		filters = []
 
-		if self.filters.get('company'):
-			filters.append({'company': self.filters.get('company')})
+		if self.filters.get("company"):
+			filters.append({"company": self.filters.get("company")})
 
-		if self.filters.get('opportunity_type'):
-			filters.append({'opportunity_type': self.filters.get('opportunity_type')})
+		if self.filters.get("opportunity_type"):
+			filters.append({"opportunity_type": self.filters.get("opportunity_type")})
 
-		if self.filters.get('opportunity_source'):
-			filters.append({'source': self.filters.get('opportunity_source')})
+		if self.filters.get("opportunity_source"):
+			filters.append({"source": self.filters.get("opportunity_source")})
 
-		if self.filters.get('status'):
-			filters.append({'status': ('in',self.filters.get('status'))})
+		if self.filters.get("status"):
+			filters.append({"status": ("in", self.filters.get("status"))})
 
-		if self.filters.get('from_date') and self.filters.get('to_date'):
-			filters.append(['transaction_date', 'between', [self.filters.get('from_date'), self.filters.get('to_date')]])
+		if self.filters.get("from_date") and self.filters.get("to_date"):
+			filters.append(
+				["transaction_date", "between", [self.filters.get("from_date"), self.filters.get("to_date")]]
+			)
 
 		return filters
 
@@ -209,45 +206,36 @@
 		for sales_stage in self.sales_stage_list:
 			labels.append(sales_stage)
 
-		options = {
-			'Number': 'count',
-			'Amount': 'amount'
-		}[self.filters.get('data_based_on')]
+		options = {"Number": "count", "Amount": "amount"}[self.filters.get("data_based_on")]
 
 		for data in self.query_result:
 			for count in range(len(values)):
-				if data['sales_stage'] == labels[count]:
+				if data["sales_stage"] == labels[count]:
 					values[count] = values[count] + data[options]
 
-		datasets.append({'name':options, 'values':values})
+		datasets.append({"name": options, "values": values})
 
-		self.chart = {
-			'data':{
-				'labels': labels,
-				'datasets': datasets
-			},
-			'type':'line'
-		}
+		self.chart = {"data": {"labels": labels, "datasets": datasets}, "type": "line"}
 
-	def currency_conversion(self,from_currency,to_currency):
+	def currency_conversion(self, from_currency, to_currency):
 		cacheobj = frappe.cache()
 
 		if cacheobj.get(from_currency):
-			return flt(str(cacheobj.get(from_currency),'UTF-8'))
+			return flt(str(cacheobj.get(from_currency), "UTF-8"))
 
 		else:
-			value = get_exchange_rate(from_currency,to_currency)
-			cacheobj.set(from_currency,value)
-			return flt(str(cacheobj.get(from_currency),'UTF-8'))
+			value = get_exchange_rate(from_currency, to_currency)
+			cacheobj.set(from_currency, value)
+			return flt(str(cacheobj.get(from_currency), "UTF-8"))
 
 	def get_default_currency(self):
-		company = self.filters.get('company')
-		return frappe.db.get_value('Company', company, 'default_currency')
+		company = self.filters.get("company")
+		return frappe.db.get_value("Company", company, "default_currency")
 
 	def convert_to_base_currency(self):
 		default_currency = self.get_default_currency()
 		for data in self.query_result:
-			if data.get('currency') != default_currency:
-				opportunity_currency = data.get('currency')
-				value = self.currency_conversion(opportunity_currency,default_currency)
-				data['amount'] = data['amount'] * value
+			if data.get("currency") != default_currency:
+				opportunity_currency = data.get("currency")
+				value = self.currency_conversion(opportunity_currency, default_currency)
+				data["amount"] = data["amount"] * value
diff --git a/erpnext/crm/report/opportunity_summary_by_sales_stage/test_opportunity_summary_by_sales_stage.py b/erpnext/crm/report/opportunity_summary_by_sales_stage/test_opportunity_summary_by_sales_stage.py
index 13859d9..ffc612d 100644
--- a/erpnext/crm/report/opportunity_summary_by_sales_stage/test_opportunity_summary_by_sales_stage.py
+++ b/erpnext/crm/report/opportunity_summary_by_sales_stage/test_opportunity_summary_by_sales_stage.py
@@ -27,68 +27,44 @@
 		self.check_all_filters()
 
 	def check_for_opportunity_owner(self):
-		filters = {
-			'based_on': "Opportunity Owner",
-			'data_based_on': "Number",
-			'company': "Best Test"
-		}
+		filters = {"based_on": "Opportunity Owner", "data_based_on": "Number", "company": "Best Test"}
 
 		report = execute(filters)
 
-		expected_data = [{
-			'opportunity_owner': "Not Assigned",
-			'Prospecting': 1
-		}]
+		expected_data = [{"opportunity_owner": "Not Assigned", "Prospecting": 1}]
 
 		self.assertEqual(expected_data, report[1])
 
 	def check_for_source(self):
-		filters = {
-			'based_on': "Source",
-			'data_based_on': "Number",
-			'company': "Best Test"
-		}
+		filters = {"based_on": "Source", "data_based_on": "Number", "company": "Best Test"}
 
 		report = execute(filters)
 
-		expected_data = [{
-			'source': 'Cold Calling',
-			'Prospecting': 1
-		}]
+		expected_data = [{"source": "Cold Calling", "Prospecting": 1}]
 
 		self.assertEqual(expected_data, report[1])
 
 	def check_for_opportunity_type(self):
-		filters = {
-			'based_on': "Opportunity Type",
-			'data_based_on': "Number",
-			'company': "Best Test"
-		}
+		filters = {"based_on": "Opportunity Type", "data_based_on": "Number", "company": "Best Test"}
 
 		report = execute(filters)
 
-		expected_data = [{
-			'opportunity_type': 'Sales',
-			'Prospecting': 1
-		}]
+		expected_data = [{"opportunity_type": "Sales", "Prospecting": 1}]
 
 		self.assertEqual(expected_data, report[1])
 
 	def check_all_filters(self):
 		filters = {
-			'based_on': "Opportunity Type",
-			'data_based_on': "Number",
-			'company': "Best Test",
-			'opportunity_source': "Cold Calling",
-			'opportunity_type': "Sales",
-			'status': ["Open"]
+			"based_on": "Opportunity Type",
+			"data_based_on": "Number",
+			"company": "Best Test",
+			"opportunity_source": "Cold Calling",
+			"opportunity_type": "Sales",
+			"status": ["Open"],
 		}
 
 		report = execute(filters)
 
-		expected_data = [{
-			'opportunity_type': 'Sales',
-			'Prospecting': 1
-		}]
+		expected_data = [{"opportunity_type": "Sales", "Prospecting": 1}]
 
-		self.assertEqual(expected_data, report[1])
\ No newline at end of file
+		self.assertEqual(expected_data, report[1])
diff --git a/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py b/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py
index 41cb442..50c42ef 100644
--- a/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py
+++ b/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py
@@ -15,62 +15,58 @@
 
 	return columns, data
 
+
 def set_defaut_value_for_filters(filters):
-	if not filters.get('no_of_interaction'): filters["no_of_interaction"] = 1
-	if not filters.get('lead_age'): filters["lead_age"] = 60
+	if not filters.get("no_of_interaction"):
+		filters["no_of_interaction"] = 1
+	if not filters.get("lead_age"):
+		filters["lead_age"] = 60
+
 
 def get_columns():
-	columns = [{
-			"label": _("Lead"),
-			"fieldname": "lead",
-			"fieldtype": "Link",
-			"options": "Lead",
-			"width": 130
-		},
-		{
-			"label": _("Name"),
-			"fieldname": "name",
-			"width": 120
-		},
-		{
-			"label": _("Organization"),
-			"fieldname": "organization",
-			"width": 120
-		},
+	columns = [
+		{"label": _("Lead"), "fieldname": "lead", "fieldtype": "Link", "options": "Lead", "width": 130},
+		{"label": _("Name"), "fieldname": "name", "width": 120},
+		{"label": _("Organization"), "fieldname": "organization", "width": 120},
 		{
 			"label": _("Reference Document Type"),
 			"fieldname": "reference_document_type",
 			"fieldtype": "Link",
 			"options": "Doctype",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Reference Name"),
 			"fieldname": "reference_name",
 			"fieldtype": "Dynamic Link",
 			"options": "reference_document_type",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Last Communication"),
 			"fieldname": "last_communication",
 			"fieldtype": "Data",
-			"width": 200
+			"width": 200,
 		},
 		{
 			"label": _("Last Communication Date"),
 			"fieldname": "last_communication_date",
 			"fieldtype": "Date",
-			"width": 100
-		}]
+			"width": 100,
+		},
+	]
 	return columns
 
+
 def get_data(filters):
 	lead_details = []
 	lead_filters = get_lead_filters(filters)
 
-	for lead in frappe.get_all('Lead', fields = ['name', 'lead_name', 'company_name'], filters=lead_filters):
-		data = frappe.db.sql("""
+	for lead in frappe.get_all(
+		"Lead", fields=["name", "lead_name", "company_name"], filters=lead_filters
+	):
+		data = frappe.db.sql(
+			"""
 			select
 				`tabCommunication`.reference_doctype, `tabCommunication`.reference_name,
 				`tabCommunication`.content, `tabCommunication`.communication_date
@@ -90,7 +86,8 @@
 				`tabCommunication`.sent_or_received = 'Received'
 			order by
 				ref_document.lead, `tabCommunication`.creation desc limit %(limit)s""",
-			{'lead': lead.name, 'limit': filters.get('no_of_interaction')})
+			{"lead": lead.name, "limit": filters.get("no_of_interaction")},
+		)
 
 		for lead_info in data:
 			lead_data = [lead.name, lead.lead_name, lead.company_name] + list(lead_info)
@@ -98,13 +95,15 @@
 
 	return lead_details
 
+
 def get_lead_filters(filters):
 	lead_creation_date = get_creation_date_based_on_lead_age(filters)
 	lead_filters = [["status", "!=", "Converted"], ["creation", ">", lead_creation_date]]
 
-	if filters.get('lead'):
-		lead_filters.append(["name", "=", filters.get('lead')])
+	if filters.get("lead"):
+		lead_filters.append(["name", "=", filters.get("lead")])
 	return lead_filters
 
+
 def get_creation_date_based_on_lead_age(filters):
-	return add_days(now(), (filters.get('lead_age') * -1))
+	return add_days(now(), (filters.get("lead_age") * -1))
diff --git a/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py
index 1c7846b..d23a22a 100644
--- a/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py
+++ b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py
@@ -3,9 +3,9 @@
 
 import json
 from datetime import date
+from itertools import groupby
 
 import frappe
-import pandas
 from dateutil.relativedelta import relativedelta
 from frappe import _
 from frappe.utils import cint, flt
@@ -16,6 +16,7 @@
 def execute(filters=None):
 	return SalesPipelineAnalytics(filters).run()
 
+
 class SalesPipelineAnalytics(object):
 	def __init__(self, filters=None):
 		self.filters = frappe._dict(filters or {})
@@ -34,113 +35,91 @@
 		self.set_pipeline_based_on_column()
 
 	def set_range_columns(self):
-		based_on = {
-			'Number': 'Int',
-			'Amount': 'Currency'
-		}[self.filters.get('based_on')]
+		based_on = {"Number": "Int", "Amount": "Currency"}[self.filters.get("based_on")]
 
-		if self.filters.get('range') == 'Monthly':
+		if self.filters.get("range") == "Monthly":
 			month_list = self.get_month_list()
 
 			for month in month_list:
-				self.columns.append({
-					'fieldname': month,
-					'fieldtype': based_on,
-					'label': month,
-					'width': 200
-				})
+				self.columns.append({"fieldname": month, "fieldtype": based_on, "label": month, "width": 200})
 
-		elif self.filters.get('range') == 'Quarterly':
+		elif self.filters.get("range") == "Quarterly":
 			for quarter in range(1, 5):
-				self.columns.append({
-					'fieldname': f'Q{quarter}',
-					'fieldtype': based_on,
-					'label': f'Q{quarter}',
-					'width': 200
-				})
+				self.columns.append(
+					{"fieldname": f"Q{quarter}", "fieldtype": based_on, "label": f"Q{quarter}", "width": 200}
+				)
 
 	def set_pipeline_based_on_column(self):
-		if self.filters.get('pipeline_by') == 'Owner':
-			self.columns.insert(0, {
-				'fieldname': 'opportunity_owner',
-				'label': _('Opportunity Owner'),
-				'width': 200
-			})
+		if self.filters.get("pipeline_by") == "Owner":
+			self.columns.insert(
+				0, {"fieldname": "opportunity_owner", "label": _("Opportunity Owner"), "width": 200}
+			)
 
-		elif self.filters.get('pipeline_by') == 'Sales Stage':
-			self.columns.insert(0, {
-				'fieldname': 'sales_stage',
-				'label': _('Sales Stage'),
-				'width': 200
-			})
+		elif self.filters.get("pipeline_by") == "Sales Stage":
+			self.columns.insert(0, {"fieldname": "sales_stage", "label": _("Sales Stage"), "width": 200})
 
 	def get_fields(self):
-		self.based_on ={
-			'Owner': '_assign as opportunity_owner',
-			'Sales Stage': 'sales_stage'
-		}[self.filters.get('pipeline_by')]
+		self.based_on = {"Owner": "_assign as opportunity_owner", "Sales Stage": "sales_stage"}[
+			self.filters.get("pipeline_by")
+		]
 
-		self.data_based_on ={
-			'Number': 'count(name) as count',
-			'Amount': 'opportunity_amount as amount'
-		}[self.filters.get('based_on')]
+		self.data_based_on = {
+			"Number": "count(name) as count",
+			"Amount": "opportunity_amount as amount",
+		}[self.filters.get("based_on")]
 
-		self.group_by_based_on = {
-			'Owner': '_assign',
-			'Sales Stage': 'sales_stage'
-		}[self.filters.get('pipeline_by')]
+		self.group_by_based_on = {"Owner": "_assign", "Sales Stage": "sales_stage"}[
+			self.filters.get("pipeline_by")
+		]
 
 		self.group_by_period = {
-			'Monthly': 'month(expected_closing)',
-			'Quarterly': 'QUARTER(expected_closing)'
-		}[self.filters.get('range')]
+			"Monthly": "month(expected_closing)",
+			"Quarterly": "QUARTER(expected_closing)",
+		}[self.filters.get("range")]
 
-		self.pipeline_by = {
-			'Owner': 'opportunity_owner',
-			'Sales Stage': 'sales_stage'
-		}[self.filters.get('pipeline_by')]
+		self.pipeline_by = {"Owner": "opportunity_owner", "Sales Stage": "sales_stage"}[
+			self.filters.get("pipeline_by")
+		]
 
 		self.duration = {
-			'Monthly': 'monthname(expected_closing) as month',
-			'Quarterly': 'QUARTER(expected_closing) as quarter'
-		}[self.filters.get('range')]
+			"Monthly": "monthname(expected_closing) as month",
+			"Quarterly": "QUARTER(expected_closing) as quarter",
+		}[self.filters.get("range")]
 
-		self.period_by = {
-			'Monthly': 'month',
-			'Quarterly': 'quarter'
-		}[self.filters.get('range')]
+		self.period_by = {"Monthly": "month", "Quarterly": "quarter"}[self.filters.get("range")]
 
 	def get_data(self):
 		self.get_fields()
 
-		if self.filters.get('based_on') == 'Number':
-			self.query_result = frappe.db.get_list('Opportunity',
+		if self.filters.get("based_on") == "Number":
+			self.query_result = frappe.db.get_list(
+				"Opportunity",
 				filters=self.get_conditions(),
 				fields=[self.based_on, self.data_based_on, self.duration],
-				group_by='{},{}'.format(self.group_by_based_on, self.group_by_period),
-				order_by=self.group_by_period
+				group_by="{},{}".format(self.group_by_based_on, self.group_by_period),
+				order_by=self.group_by_period,
 			)
 
-		if self.filters.get('based_on') == 'Amount':
-			self.query_result = frappe.db.get_list('Opportunity',
+		if self.filters.get("based_on") == "Amount":
+			self.query_result = frappe.db.get_list(
+				"Opportunity",
 				filters=self.get_conditions(),
-				fields=[self.based_on, self.data_based_on, self.duration, 'currency']
+				fields=[self.based_on, self.data_based_on, self.duration, "currency"],
 			)
 
 			self.convert_to_base_currency()
 
-			dataframe = pandas.DataFrame.from_records(self.query_result)
-			dataframe.replace(to_replace=[None], value='Not Assigned', inplace=True)
-			result = dataframe.groupby([self.pipeline_by, self.period_by], as_index=False)['amount'].sum()
-
 			self.grouped_data = []
 
-			for i in range(len(result['amount'])):
-				self.grouped_data.append({
-					self.pipeline_by : result[self.pipeline_by][i],
-					self.period_by : result[self.period_by][i],
-					'amount': result['amount'][i]
-				})
+			grouping_key = lambda o: (o.get(self.pipeline_by) or "Not Assigned", o[self.period_by])  # noqa
+			for (pipeline_by, period_by), rows in groupby(self.query_result, grouping_key):
+				self.grouped_data.append(
+					{
+						self.pipeline_by: pipeline_by,
+						self.period_by: period_by,
+						"amount": sum(flt(r["amount"]) for r in rows),
+					}
+				)
 
 			self.query_result = self.grouped_data
 
@@ -150,21 +129,22 @@
 	def get_conditions(self):
 		conditions = []
 
-		if self.filters.get('opportunity_source'):
-			conditions.append({'source': self.filters.get('opportunity_source')})
+		if self.filters.get("opportunity_source"):
+			conditions.append({"source": self.filters.get("opportunity_source")})
 
-		if self.filters.get('opportunity_type'):
-			conditions.append({'opportunity_type': self.filters.get('opportunity_type')})
+		if self.filters.get("opportunity_type"):
+			conditions.append({"opportunity_type": self.filters.get("opportunity_type")})
 
-		if self.filters.get('status'):
-			conditions.append({'status': self.filters.get('status')})
+		if self.filters.get("status"):
+			conditions.append({"status": self.filters.get("status")})
 
-		if self.filters.get('company'):
-			conditions.append({'company': self.filters.get('company')})
+		if self.filters.get("company"):
+			conditions.append({"company": self.filters.get("company")})
 
-		if self.filters.get('from_date') and self.filters.get('to_date'):
-			conditions.append(['expected_closing', 'between',
-				[self.filters.get('from_date'), self.filters.get('to_date')]])
+		if self.filters.get("from_date") and self.filters.get("to_date"):
+			conditions.append(
+				["expected_closing", "between", [self.filters.get("from_date"), self.filters.get("to_date")]]
+			)
 
 		return conditions
 
@@ -175,49 +155,36 @@
 		self.append_to_dataset(datasets)
 
 		for column in self.columns:
-			if column['fieldname'] != 'opportunity_owner' and column['fieldname'] != 'sales_stage':
-				labels.append(column['fieldname'])
+			if column["fieldname"] != "opportunity_owner" and column["fieldname"] != "sales_stage":
+				labels.append(column["fieldname"])
 
-		self.chart = {
-			'data':{
-				'labels': labels,
-				'datasets': datasets
-			},
-			'type':'line'
-		}
+		self.chart = {"data": {"labels": labels, "datasets": datasets}, "type": "line"}
 
 		return self.chart
 
 	def get_periodic_data(self):
 		self.periodic_data = frappe._dict()
 
-		based_on = {
-			'Number': 'count',
-			'Amount': 'amount'
-		}[self.filters.get('based_on')]
+		based_on = {"Number": "count", "Amount": "amount"}[self.filters.get("based_on")]
 
-		pipeline_by = {
-			'Owner': 'opportunity_owner',
-			'Sales Stage': 'sales_stage'
-		}[self.filters.get('pipeline_by')]
+		pipeline_by = {"Owner": "opportunity_owner", "Sales Stage": "sales_stage"}[
+			self.filters.get("pipeline_by")
+		]
 
-		frequency = {
-			'Monthly': 'month',
-			'Quarterly': 'quarter'
-		}[self.filters.get('range')]
+		frequency = {"Monthly": "month", "Quarterly": "quarter"}[self.filters.get("range")]
 
 		for info in self.query_result:
-			if self.filters.get('range') == 'Monthly':
+			if self.filters.get("range") == "Monthly":
 				period = info.get(frequency)
-			if self.filters.get('range') == 'Quarterly':
+			if self.filters.get("range") == "Quarterly":
 				period = f'Q{cint(info.get("quarter"))}'
 
 			value = info.get(pipeline_by)
 			count_or_amount = info.get(based_on)
 
-			if self.filters.get('pipeline_by') == 'Owner':
-				if value == 'Not Assigned' or value == '[]' or value is None:
-					assigned_to = ['Not Assigned']
+			if self.filters.get("pipeline_by") == "Owner":
+				if value == "Not Assigned" or value == "[]" or value is None:
+					assigned_to = ["Not Assigned"]
 				else:
 					assigned_to = json.loads(value)
 				self.check_for_assigned_to(period, value, count_or_amount, assigned_to, info)
@@ -228,9 +195,9 @@
 	def set_formatted_data(self, period, value, count_or_amount, assigned_to):
 		if assigned_to:
 			if len(assigned_to) > 1:
-				if self.filters.get('assigned_to'):
+				if self.filters.get("assigned_to"):
 					for user in assigned_to:
-						if self.filters.get('assigned_to') == user:
+						if self.filters.get("assigned_to") == user:
 							value = user
 							self.periodic_data.setdefault(value, frappe._dict()).setdefault(period, 0)
 							self.periodic_data[value][period] += count_or_amount
@@ -249,40 +216,34 @@
 			self.periodic_data[value][period] += count_or_amount
 
 	def check_for_assigned_to(self, period, value, count_or_amount, assigned_to, info):
-		if self.filters.get('assigned_to'):
-			for data in json.loads(info.get('opportunity_owner')):
-				if data == self.filters.get('assigned_to'):
+		if self.filters.get("assigned_to"):
+			for data in json.loads(info.get("opportunity_owner")):
+				if data == self.filters.get("assigned_to"):
 					self.set_formatted_data(period, data, count_or_amount, assigned_to)
 		else:
 			self.set_formatted_data(period, value, count_or_amount, assigned_to)
 
 	def get_month_list(self):
-		month_list= []
+		month_list = []
 		current_date = date.today()
 		month_number = date.today().month
 
-		for month in range(month_number,13):
-			month_list.append(current_date.strftime('%B'))
+		for month in range(month_number, 13):
+			month_list.append(current_date.strftime("%B"))
 			current_date = current_date + relativedelta(months=1)
 
 		return month_list
 
 	def append_to_dataset(self, datasets):
-		range_by = {
-			'Monthly': 'month',
-			'Quarterly': 'quarter'
-		}[self.filters.get('range')]
+		range_by = {"Monthly": "month", "Quarterly": "quarter"}[self.filters.get("range")]
 
-		based_on = {
-			'Amount': 'amount',
-			'Number': 'count'
-		}[self.filters.get('based_on')]
+		based_on = {"Amount": "amount", "Number": "count"}[self.filters.get("based_on")]
 
-		if self.filters.get('range') == 'Quarterly':
-			frequency_list = [1,2,3,4]
+		if self.filters.get("range") == "Quarterly":
+			frequency_list = [1, 2, 3, 4]
 			count = [0] * 4
 
-		if self.filters.get('range') == 'Monthly':
+		if self.filters.get("range") == "Monthly":
 			frequency_list = self.get_month_list()
 			count = [0] * 12
 
@@ -290,43 +251,43 @@
 			for i in range(len(frequency_list)):
 				if info[range_by] == frequency_list[i]:
 					count[i] = count[i] + info[based_on]
-		datasets.append({'name': based_on, 'values': count})
+		datasets.append({"name": based_on, "values": count})
 
 	def append_data(self, pipeline_by, period_by):
 		self.data = []
-		for pipeline,period_data in self.periodic_data.items():
-			row = {pipeline_by : pipeline}
+		for pipeline, period_data in self.periodic_data.items():
+			row = {pipeline_by: pipeline}
 			for info in self.query_result:
-				if self.filters.get('range') == 'Monthly':
+				if self.filters.get("range") == "Monthly":
 					period = info.get(period_by)
 
-				if self.filters.get('range') == 'Quarterly':
-					period = f'Q{cint(info.get(period_by))}'
+				if self.filters.get("range") == "Quarterly":
+					period = f"Q{cint(info.get(period_by))}"
 
-				count = period_data.get(period,0.0)
+				count = period_data.get(period, 0.0)
 				row[period] = count
 
 			self.data.append(row)
 
 	def get_default_currency(self):
-		company = self.filters.get('company')
-		return frappe.db.get_value('Company',company,['default_currency'])
+		company = self.filters.get("company")
+		return frappe.db.get_value("Company", company, ["default_currency"])
 
 	def get_currency_rate(self, from_currency, to_currency):
 		cacheobj = frappe.cache()
 
 		if cacheobj.get(from_currency):
-			return flt(str(cacheobj.get(from_currency),'UTF-8'))
+			return flt(str(cacheobj.get(from_currency), "UTF-8"))
 
 		else:
 			value = get_exchange_rate(from_currency, to_currency)
 			cacheobj.set(from_currency, value)
-			return flt(str(cacheobj.get(from_currency),'UTF-8'))
+			return flt(str(cacheobj.get(from_currency), "UTF-8"))
 
 	def convert_to_base_currency(self):
 		default_currency = self.get_default_currency()
 		for data in self.query_result:
-			if data.get('currency') != default_currency:
-				opportunity_currency = data.get('currency')
-				value = self.get_currency_rate(opportunity_currency,default_currency)
-				data['amount'] = data['amount'] * value
+			if data.get("currency") != default_currency:
+				opportunity_currency = data.get("currency")
+				value = self.get_currency_rate(opportunity_currency, default_currency)
+				data["amount"] = data["amount"] * value
diff --git a/erpnext/crm/report/sales_pipeline_analytics/test_sales_pipeline_analytics.py b/erpnext/crm/report/sales_pipeline_analytics/test_sales_pipeline_analytics.py
index 24c3839..02d82b6 100644
--- a/erpnext/crm/report/sales_pipeline_analytics/test_sales_pipeline_analytics.py
+++ b/erpnext/crm/report/sales_pipeline_analytics/test_sales_pipeline_analytics.py
@@ -22,217 +22,175 @@
 
 	def check_for_monthly_and_number(self):
 		filters = {
-			'pipeline_by':"Owner",
-			'range':"Monthly",
-			'based_on':"Number",
-			'status':"Open",
-			'opportunity_type':"Sales",
-			'company':"Best Test"
+			"pipeline_by": "Owner",
+			"range": "Monthly",
+			"based_on": "Number",
+			"status": "Open",
+			"opportunity_type": "Sales",
+			"company": "Best Test",
 		}
 
 		report = execute(filters)
 
-		expected_data = [
-			{
-				'opportunity_owner':'Not Assigned',
-				'August':1
-			}
-		]
+		expected_data = [{"opportunity_owner": "Not Assigned", "August": 1}]
 
-		self.assertEqual(expected_data,report[1])
+		self.assertEqual(expected_data, report[1])
 
 		filters = {
-			'pipeline_by':"Sales Stage",
-			'range':"Monthly",
-			'based_on':"Number",
-			'status':"Open",
-			'opportunity_type':"Sales",
-			'company':"Best Test"
+			"pipeline_by": "Sales Stage",
+			"range": "Monthly",
+			"based_on": "Number",
+			"status": "Open",
+			"opportunity_type": "Sales",
+			"company": "Best Test",
 		}
 
 		report = execute(filters)
 
-		expected_data = [
-			{
-				'sales_stage':'Prospecting',
-				'August':1
-			}
-		]
+		expected_data = [{"sales_stage": "Prospecting", "August": 1}]
 
-		self.assertEqual(expected_data,report[1])
+		self.assertEqual(expected_data, report[1])
 
 	def check_for_monthly_and_amount(self):
 		filters = {
-			'pipeline_by':"Owner",
-			'range':"Monthly",
-			'based_on':"Amount",
-			'status':"Open",
-			'opportunity_type':"Sales",
-			'company':"Best Test"
+			"pipeline_by": "Owner",
+			"range": "Monthly",
+			"based_on": "Amount",
+			"status": "Open",
+			"opportunity_type": "Sales",
+			"company": "Best Test",
 		}
 
 		report = execute(filters)
 
-		expected_data = [
-			{
-				'opportunity_owner':'Not Assigned',
-				'August':150000
-			}
-		]
+		expected_data = [{"opportunity_owner": "Not Assigned", "August": 150000}]
 
-		self.assertEqual(expected_data,report[1])
+		self.assertEqual(expected_data, report[1])
 
 		filters = {
-			'pipeline_by':"Sales Stage",
-			'range':"Monthly",
-			'based_on':"Amount",
-			'status':"Open",
-			'opportunity_type':"Sales",
-			'company':"Best Test"
+			"pipeline_by": "Sales Stage",
+			"range": "Monthly",
+			"based_on": "Amount",
+			"status": "Open",
+			"opportunity_type": "Sales",
+			"company": "Best Test",
 		}
 
 		report = execute(filters)
 
-		expected_data = [
-			{
-				'sales_stage':'Prospecting',
-				'August':150000
-			}
-		]
+		expected_data = [{"sales_stage": "Prospecting", "August": 150000}]
 
-		self.assertEqual(expected_data,report[1])
+		self.assertEqual(expected_data, report[1])
 
 	def check_for_quarterly_and_number(self):
 		filters = {
-			'pipeline_by':"Owner",
-			'range':"Quarterly",
-			'based_on':"Number",
-			'status':"Open",
-			'opportunity_type':"Sales",
-			'company':"Best Test"
+			"pipeline_by": "Owner",
+			"range": "Quarterly",
+			"based_on": "Number",
+			"status": "Open",
+			"opportunity_type": "Sales",
+			"company": "Best Test",
 		}
 
 		report = execute(filters)
 
-		expected_data = [
-			{
-				'opportunity_owner':'Not Assigned',
-				'Q3':1
-			}
-		]
+		expected_data = [{"opportunity_owner": "Not Assigned", "Q3": 1}]
 
-		self.assertEqual(expected_data,report[1])
+		self.assertEqual(expected_data, report[1])
 
 		filters = {
-			'pipeline_by':"Sales Stage",
-			'range':"Quarterly",
-			'based_on':"Number",
-			'status':"Open",
-			'opportunity_type':"Sales",
-			'company':"Best Test"
+			"pipeline_by": "Sales Stage",
+			"range": "Quarterly",
+			"based_on": "Number",
+			"status": "Open",
+			"opportunity_type": "Sales",
+			"company": "Best Test",
 		}
 
 		report = execute(filters)
 
-		expected_data = [
-			{
-				'sales_stage':'Prospecting',
-				'Q3':1
-			}
-		]
+		expected_data = [{"sales_stage": "Prospecting", "Q3": 1}]
 
-		self.assertEqual(expected_data,report[1])
+		self.assertEqual(expected_data, report[1])
 
 	def check_for_quarterly_and_amount(self):
 		filters = {
-			'pipeline_by':"Owner",
-			'range':"Quarterly",
-			'based_on':"Amount",
-			'status':"Open",
-			'opportunity_type':"Sales",
-			'company':"Best Test"
+			"pipeline_by": "Owner",
+			"range": "Quarterly",
+			"based_on": "Amount",
+			"status": "Open",
+			"opportunity_type": "Sales",
+			"company": "Best Test",
 		}
 
 		report = execute(filters)
 
-		expected_data = [
-			{
-				'opportunity_owner':'Not Assigned',
-				'Q3':150000
-			}
-		]
+		expected_data = [{"opportunity_owner": "Not Assigned", "Q3": 150000}]
 
-		self.assertEqual(expected_data,report[1])
+		self.assertEqual(expected_data, report[1])
 
 		filters = {
-			'pipeline_by':"Sales Stage",
-			'range':"Quarterly",
-			'based_on':"Amount",
-			'status':"Open",
-			'opportunity_type':"Sales",
-			'company':"Best Test"
+			"pipeline_by": "Sales Stage",
+			"range": "Quarterly",
+			"based_on": "Amount",
+			"status": "Open",
+			"opportunity_type": "Sales",
+			"company": "Best Test",
 		}
 
 		report = execute(filters)
 
-		expected_data = [
-			{
-				'sales_stage':'Prospecting',
-				'Q3':150000
-			}
-		]
+		expected_data = [{"sales_stage": "Prospecting", "Q3": 150000}]
 
-		self.assertEqual(expected_data,report[1])
+		self.assertEqual(expected_data, report[1])
 
 	def check_for_all_filters(self):
 		filters = {
-			'pipeline_by':"Owner",
-			'range':"Monthly",
-			'based_on':"Number",
-			'status':"Open",
-			'opportunity_type':"Sales",
-			'company':"Best Test",
-			'opportunity_source':'Cold Calling',
-			'from_date': '2021-08-01',
-			'to_date':'2021-08-31'
+			"pipeline_by": "Owner",
+			"range": "Monthly",
+			"based_on": "Number",
+			"status": "Open",
+			"opportunity_type": "Sales",
+			"company": "Best Test",
+			"opportunity_source": "Cold Calling",
+			"from_date": "2021-08-01",
+			"to_date": "2021-08-31",
 		}
 
 		report = execute(filters)
 
-		expected_data = [
-			{
-				'opportunity_owner':'Not Assigned',
-				'August': 1
-			}
-		]
+		expected_data = [{"opportunity_owner": "Not Assigned", "August": 1}]
 
-		self.assertEqual(expected_data,report[1])
+		self.assertEqual(expected_data, report[1])
+
 
 def create_company():
-	doc = frappe.db.exists('Company','Best Test')
+	doc = frappe.db.exists("Company", "Best Test")
 	if not doc:
-		doc = frappe.new_doc('Company')
-		doc.company_name = 'Best Test'
+		doc = frappe.new_doc("Company")
+		doc.company_name = "Best Test"
 		doc.default_currency = "INR"
 		doc.insert()
 
+
 def create_customer():
-	doc = frappe.db.exists("Customer","_Test NC")
+	doc = frappe.db.exists("Customer", "_Test NC")
 	if not doc:
 		doc = frappe.new_doc("Customer")
-		doc.customer_name = '_Test NC'
+		doc.customer_name = "_Test NC"
 		doc.insert()
 
+
 def create_opportunity():
-	doc = frappe.db.exists({"doctype":"Opportunity","party_name":"_Test NC"})
+	doc = frappe.db.exists({"doctype": "Opportunity", "party_name": "_Test NC"})
 	if not doc:
 		doc = frappe.new_doc("Opportunity")
 		doc.opportunity_from = "Customer"
-		customer_name = frappe.db.get_value("Customer",{"customer_name":'_Test NC'},['customer_name'])
+		customer_name = frappe.db.get_value("Customer", {"customer_name": "_Test NC"}, ["customer_name"])
 		doc.party_name = customer_name
 		doc.opportunity_amount = 150000
 		doc.source = "Cold Calling"
 		doc.currency = "INR"
 		doc.expected_closing = "2021-08-31"
-		doc.company = 'Best Test'
-		doc.insert()
\ No newline at end of file
+		doc.company = "Best Test"
+		doc.insert()
diff --git a/erpnext/crm/utils.py b/erpnext/crm/utils.py
index a4576a2..5783b2c 100644
--- a/erpnext/crm/utils.py
+++ b/erpnext/crm/utils.py
@@ -9,12 +9,16 @@
 
 			if len(contact.phone_nos) > 1:
 				# get the default phone number
-				primary_phones = [phone_doc.phone for phone_doc in contact.phone_nos if phone_doc.is_primary_phone]
+				primary_phones = [
+					phone_doc.phone for phone_doc in contact.phone_nos if phone_doc.is_primary_phone
+				]
 				if primary_phones:
 					phone = primary_phones[0]
 
 				# get the default mobile number
-				primary_mobile_nos = [phone_doc.phone for phone_doc in contact.phone_nos if phone_doc.is_primary_mobile_no]
+				primary_mobile_nos = [
+					phone_doc.phone for phone_doc in contact.phone_nos if phone_doc.is_primary_mobile_no
+				]
 				if primary_mobile_nos:
 					mobile_no = primary_mobile_nos[0]
 
@@ -22,15 +26,21 @@
 			lead.db_set("phone", phone)
 			lead.db_set("mobile_no", mobile_no)
 
+
 def copy_comments(doctype, docname, doc):
-	comments = frappe.db.get_values("Comment", filters={"reference_doctype": doctype, "reference_name": docname, "comment_type": "Comment"}, fieldname="*")
+	comments = frappe.db.get_values(
+		"Comment",
+		filters={"reference_doctype": doctype, "reference_name": docname, "comment_type": "Comment"},
+		fieldname="*",
+	)
 	for comment in comments:
-		comment = frappe.get_doc(comment.update({"doctype":"Comment"}))
+		comment = frappe.get_doc(comment.update({"doctype": "Comment"}))
 		comment.name = None
 		comment.reference_doctype = doc.doctype
 		comment.reference_name = doc.name
 		comment.insert()
 
+
 def add_link_in_communication(doctype, docname, doc):
 	communication_list = get_linked_communication_list(doctype, docname)
 
@@ -38,13 +48,15 @@
 		communication_doc = frappe.get_doc("Communication", communication)
 		communication_doc.add_link(doc.doctype, doc.name, autosave=True)
 
+
 def get_linked_communication_list(doctype, docname):
-	communications = frappe.get_all("Communication", filters={"reference_doctype": doctype, "reference_name": docname}, pluck='name')
-	communication_links = frappe.get_all('Communication Link',
-		{
-			"link_doctype": doctype,
-			"link_name": docname,
-			"parent": ("not in", communications)
-		}, pluck="parent")
+	communications = frappe.get_all(
+		"Communication", filters={"reference_doctype": doctype, "reference_name": docname}, pluck="name"
+	)
+	communication_links = frappe.get_all(
+		"Communication Link",
+		{"link_doctype": doctype, "link_name": docname, "parent": ("not in", communications)},
+		pluck="parent",
+	)
 
 	return communications + communication_links
diff --git a/erpnext/domains/distribution.py b/erpnext/domains/distribution.py
index 020ab3b..5953c4e 100644
--- a/erpnext/domains/distribution.py
+++ b/erpnext/domains/distribution.py
@@ -1,18 +1,16 @@
 data = {
-	'desktop_icons': [
-		'Item',
-		'Customer',
-		'Supplier',
-		'Lead',
-		'Sales Order',
-		'Purchase Order',
-		'Task',
-		'Sales Invoice',
-		'CRM',
-		'ToDo'
+	"desktop_icons": [
+		"Item",
+		"Customer",
+		"Supplier",
+		"Lead",
+		"Sales Order",
+		"Purchase Order",
+		"Task",
+		"Sales Invoice",
+		"CRM",
+		"ToDo",
 	],
-	'set_value': [
-		['Stock Settings', None, 'show_barcode_field', 1]
-	],
-	'default_portal_role': 'Customer'
+	"set_value": [["Stock Settings", None, "show_barcode_field", 1]],
+	"default_portal_role": "Customer",
 }
diff --git a/erpnext/domains/education.py b/erpnext/domains/education.py
deleted file mode 100644
index 11ea9b4..0000000
--- a/erpnext/domains/education.py
+++ /dev/null
@@ -1,27 +0,0 @@
-data = {
-	'desktop_icons': [
-		'Student',
-		'Program',
-		'Course',
-		'Student Group',
-		'Instructor',
-		'Fees',
-		'Task',
-		'ToDo',
-		'Education',
-		'Student Attendance Tool',
-		'Student Applicant'
-	],
-	'default_portal_role': 'Student',
-	'restricted_roles': [
-		'Student',
-		'Instructor',
-		'Academics User',
-		'Education Manager'
-	],
-	'modules': [
-		'Education'
-	],
-	'on_setup': 'erpnext.education.setup.setup_education'
-
-}
diff --git a/erpnext/domains/manufacturing.py b/erpnext/domains/manufacturing.py
index 96ce194..08ed3cf 100644
--- a/erpnext/domains/manufacturing.py
+++ b/erpnext/domains/manufacturing.py
@@ -1,22 +1,25 @@
 data = {
-	'desktop_icons': [
-		'Item',
-		'BOM',
-		'Customer',
-		'Supplier',
-		'Sales Order',
-		'Purchase Order',
-		'Work Order',
-		'Task',
-		'Accounts',
-		'HR',
-		'ToDo'
-	 ],
-	'properties': [
-		{'doctype': 'Item', 'fieldname': 'manufacturing', 'property': 'collapsible_depends_on', 'value': 'is_stock_item'},
+	"desktop_icons": [
+		"Item",
+		"BOM",
+		"Customer",
+		"Supplier",
+		"Sales Order",
+		"Purchase Order",
+		"Work Order",
+		"Task",
+		"Accounts",
+		"HR",
+		"ToDo",
 	],
-	'set_value': [
-		['Stock Settings', None, 'show_barcode_field', 1]
+	"properties": [
+		{
+			"doctype": "Item",
+			"fieldname": "manufacturing",
+			"property": "collapsible_depends_on",
+			"value": "is_stock_item",
+		},
 	],
-	'default_portal_role': 'Customer'
+	"set_value": [["Stock Settings", None, "show_barcode_field", 1]],
+	"default_portal_role": "Customer",
 }
diff --git a/erpnext/domains/retail.py b/erpnext/domains/retail.py
index 07b2e27..8ea85f1 100644
--- a/erpnext/domains/retail.py
+++ b/erpnext/domains/retail.py
@@ -1,16 +1,14 @@
 data = {
-	'desktop_icons': [
-		'POS',
-		'Item',
-		'Customer',
-		'Sales Invoice',
-		'Purchase Order',
-		'Accounts',
-		'Task',
-		'ToDo'
+	"desktop_icons": [
+		"POS",
+		"Item",
+		"Customer",
+		"Sales Invoice",
+		"Purchase Order",
+		"Accounts",
+		"Task",
+		"ToDo",
 	],
-	'set_value': [
-		['Stock Settings', None, 'show_barcode_field', 1]
-	],
-	'default_portal_role': 'Customer'
+	"set_value": [["Stock Settings", None, "show_barcode_field", 1]],
+	"default_portal_role": "Customer",
 }
diff --git a/erpnext/domains/services.py b/erpnext/domains/services.py
index 6035afb..8595c69 100644
--- a/erpnext/domains/services.py
+++ b/erpnext/domains/services.py
@@ -1,19 +1,17 @@
 data = {
-	'desktop_icons': [
-		'Project',
-		'Timesheet',
-		'Customer',
-		'Sales Order',
-		'Sales Invoice',
-		'CRM',
-		'Task',
-		'Expense Claim',
-		'Employee',
-		'HR',
-		'ToDo'
+	"desktop_icons": [
+		"Project",
+		"Timesheet",
+		"Customer",
+		"Sales Order",
+		"Sales Invoice",
+		"CRM",
+		"Task",
+		"Expense Claim",
+		"Employee",
+		"HR",
+		"ToDo",
 	],
-	'set_value': [
-		['Stock Settings', None, 'show_barcode_field', 0]
-	],
-	'default_portal_role': 'Customer'
+	"set_value": [["Stock Settings", None, "show_barcode_field", 0]],
+	"default_portal_role": "Customer",
 }
diff --git a/erpnext/e_commerce/api.py b/erpnext/e_commerce/api.py
index 43cb36c..bfada0f 100644
--- a/erpnext/e_commerce/api.py
+++ b/erpnext/e_commerce/api.py
@@ -15,16 +15,16 @@
 @frappe.whitelist(allow_guest=True)
 def get_product_filter_data(query_args=None):
 	"""
-		Returns filtered products and discount filters.
-		:param query_args (dict): contains filters to get products list
+	Returns filtered products and discount filters.
+	:param query_args (dict): contains filters to get products list
 
-		Query Args filters:
-		search (str): Search Term.
-		field_filters (dict): Keys include item_group, brand, etc.
-		attribute_filters(dict): Keys include Color, Size, etc.
-		start (int): Offset items by
-		item_group (str): Valid Item Group
-		from_filters (bool): Set as True to jump to page 1
+	Query Args filters:
+	search (str): Search Term.
+	field_filters (dict): Keys include item_group, brand, etc.
+	attribute_filters(dict): Keys include Color, Size, etc.
+	start (int): Offset items by
+	item_group (str): Valid Item Group
+	from_filters (bool): Set as True to jump to page 1
 	"""
 	if isinstance(query_args, str):
 		query_args = json.loads(query_args)
@@ -48,21 +48,15 @@
 
 	sub_categories = []
 	if item_group:
-		field_filters['item_group'] = item_group
 		sub_categories = get_child_groups_for_website(item_group, immediate=True)
 
 	engine = ProductQuery()
 	try:
 		result = engine.query(
-			attribute_filters,
-			field_filters,
-			search_term=search,
-			start=start,
-			item_group=item_group
+			attribute_filters, field_filters, search_term=search, start=start, item_group=item_group
 		)
 	except Exception:
-		traceback = frappe.get_traceback()
-		frappe.log_error(traceback, frappe._("Product Engine Error"))
+		frappe.log_error("Product query with filter failed")
 		return {"exc": "Something went wrong!"}
 
 	# discount filter data
@@ -78,9 +72,10 @@
 		"filters": filters,
 		"settings": engine.settings,
 		"sub_categories": sub_categories,
-		"items_count": result["items_count"]
+		"items_count": result["items_count"],
 	}
 
+
 @frappe.whitelist(allow_guest=True)
 def get_guest_redirect_on_action():
-	return frappe.db.get_single_value("E Commerce Settings", "redirect_on_action")
\ No newline at end of file
+	return frappe.db.get_single_value("E Commerce Settings", "redirect_on_action")
diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.js b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.js
index 6302d26..69b9cfa 100644
--- a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.js
+++ b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.js
@@ -24,17 +24,16 @@
 			);
 		}
 
-		frappe.model.with_doctype("Item", () => {
+		frappe.model.with_doctype("Website Item", () => {
 			const web_item_meta = frappe.get_meta('Website Item');
 
-			const valid_fields = web_item_meta.fields.filter(
-				df => ["Link", "Table MultiSelect"].includes(df.fieldtype) && !df.hidden
-			).map(df => ({ label: df.label, value: df.fieldname }));
-
-			frm.fields_dict.filter_fields.grid.update_docfield_property(
-				'fieldname', 'fieldtype', 'Select'
+			const valid_fields = web_item_meta.fields.filter(df =>
+				["Link", "Table MultiSelect"].includes(df.fieldtype) && !df.hidden
+			).map(df =>
+				({ label: df.label, value: df.fieldname })
 			);
-			frm.fields_dict.filter_fields.grid.update_docfield_property(
+
+			frm.get_field("filter_fields").grid.update_docfield_property(
 				'fieldname', 'options', valid_fields
 			);
 		});
diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json
index d5fb969..e6f08f7 100644
--- a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json
+++ b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json
@@ -47,7 +47,7 @@
   "item_search_settings_section",
   "redisearch_warning",
   "search_index_fields",
-  "show_categories_in_search_autocomplete",
+  "is_redisearch_enabled",
   "is_redisearch_loaded",
   "shop_by_category_section",
   "slideshow",
@@ -293,6 +293,7 @@
    "fieldname": "search_index_fields",
    "fieldtype": "Small Text",
    "label": "Search Index Fields",
+   "mandatory_depends_on": "is_redisearch_enabled",
    "read_only_depends_on": "eval:!doc.is_redisearch_loaded"
   },
   {
@@ -302,13 +303,6 @@
    "label": "Item Search Settings"
   },
   {
-   "default": "1",
-   "fieldname": "show_categories_in_search_autocomplete",
-   "fieldtype": "Check",
-   "label": "Show Categories in Search Autocomplete",
-   "read_only_depends_on": "eval:!doc.is_redisearch_loaded"
-  },
-  {
    "default": "0",
    "fieldname": "is_redisearch_loaded",
    "fieldtype": "Check",
@@ -365,12 +359,19 @@
    "fieldname": "show_price_in_quotation",
    "fieldtype": "Check",
    "label": "Show Price in Quotation"
+  },
+  {
+   "default": "0",
+   "fieldname": "is_redisearch_enabled",
+   "fieldtype": "Check",
+   "label": "Enable Redisearch",
+   "read_only_depends_on": "eval:!doc.is_redisearch_loaded"
   }
  ],
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-09-02 14:02:44.785824",
+ "modified": "2022-04-01 18:35:56.106756",
  "modified_by": "Administrator",
  "module": "E-commerce",
  "name": "E Commerce Settings",
@@ -389,5 +390,6 @@
  ],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py
index dd7b114..c27d29a 100644
--- a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py
+++ b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py
@@ -9,20 +9,25 @@
 
 from erpnext.e_commerce.redisearch_utils import (
 	create_website_items_index,
+	define_autocomplete_dictionary,
 	get_indexable_web_fields,
 	is_search_module_loaded,
 )
 
 
-class ShoppingCartSetupError(frappe.ValidationError): pass
+class ShoppingCartSetupError(frappe.ValidationError):
+	pass
+
 
 class ECommerceSettings(Document):
 	def onload(self):
 		self.get("__onload").quotation_series = frappe.get_meta("Quotation").get_options("naming_series")
+
+		# flag >> if redisearch is installed and loaded
 		self.is_redisearch_loaded = is_search_module_loaded()
 
 	def validate(self):
-		self.validate_field_filters()
+		self.validate_field_filters(self.filter_fields, self.enable_field_filters)
 		self.validate_attribute_filters()
 		self.validate_checkout()
 		self.validate_search_index_fields()
@@ -32,16 +37,37 @@
 
 		frappe.clear_document_cache("E Commerce Settings", "E Commerce Settings")
 
-	def validate_field_filters(self):
-		if not (self.enable_field_filters and self.filter_fields):
+		self.is_redisearch_enabled_pre_save = frappe.db.get_single_value(
+			"E Commerce Settings", "is_redisearch_enabled"
+		)
+
+	def after_save(self):
+		self.create_redisearch_indexes()
+
+	def create_redisearch_indexes(self):
+		# if redisearch is enabled (value changed) create indexes and dictionary
+		value_changed = self.is_redisearch_enabled != self.is_redisearch_enabled_pre_save
+		if self.is_redisearch_loaded and self.is_redisearch_enabled and value_changed:
+			define_autocomplete_dictionary()
+			create_website_items_index()
+
+	@staticmethod
+	def validate_field_filters(filter_fields, enable_field_filters):
+		if not (enable_field_filters and filter_fields):
 			return
 
-		item_meta = frappe.get_meta("Item")
-		valid_fields = [df.fieldname for df in item_meta.fields if df.fieldtype in ["Link", "Table MultiSelect"]]
+		web_item_meta = frappe.get_meta("Website Item")
+		valid_fields = [
+			df.fieldname for df in web_item_meta.fields if df.fieldtype in ["Link", "Table MultiSelect"]
+		]
 
-		for f in self.filter_fields:
-			if f.fieldname not in valid_fields:
-				frappe.throw(_("Filter Fields Row #{0}: Fieldname <b>{1}</b> must be of type 'Link' or 'Table MultiSelect'").format(f.idx, f.fieldname))
+		for row in filter_fields:
+			if row.fieldname not in valid_fields:
+				frappe.throw(
+					_(
+						"Filter Fields Row #{0}: Fieldname {1} must be of type 'Link' or 'Table MultiSelect'"
+					).format(row.idx, frappe.bold(row.fieldname))
+				)
 
 	def validate_attribute_filters(self):
 		if not (self.enable_attribute_filters and self.filter_attributes):
@@ -58,8 +84,8 @@
 		if not self.search_index_fields:
 			return
 
-		fields = self.search_index_fields.replace(' ', '')
-		fields = unique(fields.strip(',').split(',')) # Remove extra ',' and remove duplicates
+		fields = self.search_index_fields.replace(" ", "")
+		fields = unique(fields.strip(",").split(","))  # Remove extra ',' and remove duplicates
 
 		# All fields should be indexable
 		allowed_indexable_fields = get_indexable_web_fields()
@@ -70,18 +96,22 @@
 			invalid_fields = comma_and(invalid_fields)
 
 			if num_invalid_fields > 1:
-				frappe.throw(_("{0} are not valid options for Search Index Field.").format(frappe.bold(invalid_fields)))
+				frappe.throw(
+					_("{0} are not valid options for Search Index Field.").format(frappe.bold(invalid_fields))
+				)
 			else:
-				frappe.throw(_("{0} is not a valid option for Search Index Field.").format(frappe.bold(invalid_fields)))
+				frappe.throw(
+					_("{0} is not a valid option for Search Index Field.").format(frappe.bold(invalid_fields))
+				)
 
-		self.search_index_fields = ','.join(fields)
+		self.search_index_fields = ",".join(fields)
 
 	def validate_price_list_exchange_rate(self):
 		"Check if exchange rate exists for Price List currency (to Company's currency)."
 		from erpnext.setup.utils import get_exchange_rate
 
 		if not self.enabled or not self.company or not self.price_list:
-			return # this function is also called from hooks, check values again
+			return  # this function is also called from hooks, check values again
 
 		company_currency = frappe.get_cached_value("Company", self.company, "default_currency")
 		price_list_currency = frappe.db.get_value("Price List", self.price_list, "currency")
@@ -105,12 +135,13 @@
 				frappe.throw(_(msg), title=_("Missing"), exc=ShoppingCartSetupError)
 
 	def validate_tax_rule(self):
-		if not frappe.db.get_value("Tax Rule", {"use_for_shopping_cart" : 1}, "name"):
+		if not frappe.db.get_value("Tax Rule", {"use_for_shopping_cart": 1}, "name"):
 			frappe.throw(frappe._("Set Tax Rule for shopping cart"), ShoppingCartSetupError)
 
 	def get_tax_master(self, billing_territory):
-		tax_master = self.get_name_from_territory(billing_territory, "sales_taxes_and_charges_masters",
-			"sales_taxes_and_charges_master")
+		tax_master = self.get_name_from_territory(
+			billing_territory, "sales_taxes_and_charges_masters", "sales_taxes_and_charges_master"
+		)
 		return tax_master and tax_master[0] or None
 
 	def get_shipping_rules(self, shipping_territory):
@@ -127,25 +158,28 @@
 			if not (new_fields == old_fields):
 				create_website_items_index()
 
+
 def validate_cart_settings(doc=None, method=None):
 	frappe.get_doc("E Commerce Settings", "E Commerce Settings").run_method("validate")
 
-def get_shopping_cart_settings():
-	if not getattr(frappe.local, "shopping_cart_settings", None):
-		frappe.local.shopping_cart_settings = frappe.get_doc("E Commerce Settings", "E Commerce Settings")
 
-	return frappe.local.shopping_cart_settings
+def get_shopping_cart_settings():
+	return frappe.get_cached_doc("E Commerce Settings")
+
 
 @frappe.whitelist(allow_guest=True)
 def is_cart_enabled():
 	return get_shopping_cart_settings().enabled
 
+
 def show_quantity_in_website():
 	return get_shopping_cart_settings().show_quantity_in_website
 
+
 def check_shopping_cart_enabled():
 	if not get_shopping_cart_settings().enabled:
 		frappe.throw(_("You need to enable Shopping Cart"), ShoppingCartSetupError)
 
+
 def show_attachments():
 	return get_shopping_cart_settings().show_attachments
diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/test_e_commerce_settings.py b/erpnext/e_commerce/doctype/e_commerce_settings/test_e_commerce_settings.py
index 86cef30..662db4d 100644
--- a/erpnext/e_commerce/doctype/e_commerce_settings/test_e_commerce_settings.py
+++ b/erpnext/e_commerce/doctype/e_commerce_settings/test_e_commerce_settings.py
@@ -1,5 +1,4 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 import unittest
 
@@ -11,45 +10,36 @@
 
 
 class TestECommerceSettings(unittest.TestCase):
-	def setUp(self):
-		frappe.db.sql("""delete from `tabSingles` where doctype="Shipping Cart Settings" """)
-
-	def get_cart_settings(self):
-		return frappe.get_doc({"doctype": "E Commerce Settings",
-			"company": "_Test Company"})
-
-	# NOTE: Exchangrate API has all enabled currencies that ERPNext supports.
-	# We aren't checking just currency exchange record anymore
-	# while validating price list currency exchange rate to that of company.
-	# The API is being used to fetch the rate which again almost always
-	# gives back a valid value (for valid currencies).
-	# This makes the test obsolete.
-	# Commenting because im not sure if there's a better test we can write
-
-	# def test_exchange_rate_exists(self):
-	# 	frappe.db.sql("""delete from `tabCurrency Exchange`""")
-
-	# 	cart_settings = self.get_cart_settings()
-	# 	cart_settings.price_list = "_Test Price List Rest of the World"
-	# 	self.assertRaises(ShoppingCartSetupError, cart_settings.validate_exchange_rates_exist)
-
-	# 	from erpnext.setup.doctype.currency_exchange.test_currency_exchange import (
-	# 		test_records as currency_exchange_records,
-	# 	)
-	# 	frappe.get_doc(currency_exchange_records[0]).insert()
-	# 	cart_settings.validate_exchange_rates_exist()
+	def tearDown(self):
+		frappe.db.rollback()
 
 	def test_tax_rule_validation(self):
 		frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 0")
-		frappe.db.commit() # nosemgrep
+		frappe.db.commit()  # nosemgrep
 
-		cart_settings = self.get_cart_settings()
+		cart_settings = frappe.get_doc("E Commerce Settings")
 		cart_settings.enabled = 1
 		if not frappe.db.get_value("Tax Rule", {"use_for_shopping_cart": 1}, "name"):
 			self.assertRaises(ShoppingCartSetupError, cart_settings.validate_tax_rule)
 
 		frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 1")
 
+	def test_invalid_filter_fields(self):
+		"Check if Item fields are blocked in E Commerce Settings filter fields."
+		from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+
+		setup_e_commerce_settings({"enable_field_filters": 1})
+
+		create_custom_field(
+			"Item",
+			dict(owner="Administrator", fieldname="test_data", label="Test", fieldtype="Data"),
+		)
+		settings = frappe.get_doc("E Commerce Settings")
+		settings.append("filter_fields", {"fieldname": "test_data"})
+
+		self.assertRaises(frappe.ValidationError, settings.save)
+
+
 def setup_e_commerce_settings(values_dict):
 	"Accepts a dict of values that updates E Commerce Settings."
 	if not values_dict:
@@ -59,4 +49,5 @@
 	doc.update(values_dict)
 	doc.save()
 
+
 test_dependencies = ["Tax Rule"]
diff --git a/erpnext/e_commerce/doctype/item_review/item_review.py b/erpnext/e_commerce/doctype/item_review/item_review.py
index 966ec35..3e540e3 100644
--- a/erpnext/e_commerce/doctype/item_review/item_review.py
+++ b/erpnext/e_commerce/doctype/item_review/item_review.py
@@ -18,6 +18,7 @@
 class UnverifiedReviewer(frappe.ValidationError):
 	pass
 
+
 class ItemReview(Document):
 	def after_insert(self):
 		# regenerate cache on review creation
@@ -54,12 +55,13 @@
 
 	return data
 
+
 def get_queried_reviews(web_item, start=0, end=10, data=None):
 	"""
-		Query Website Item wise reviews and cache if needed.
-		Cache stores only first page of reviews i.e. 10 reviews maximum.
-		Returns:
-			dict: Containing reviews, average ratings, % of reviews per rating and total reviews.
+	Query Website Item wise reviews and cache if needed.
+	Cache stores only first page of reviews i.e. 10 reviews maximum.
+	Returns:
+	        dict: Containing reviews, average ratings, % of reviews per rating and total reviews.
 	"""
 	if not data:
 		data = frappe._dict()
@@ -69,13 +71,13 @@
 		filters={"website_item": web_item},
 		fields=["*"],
 		limit_start=start,
-		limit_page_length=end
+		limit_page_length=end,
 	)
 
 	rating_data = frappe.db.get_all(
 		"Item Review",
 		filters={"website_item": web_item},
-		fields=["avg(rating) as average, count(*) as total"]
+		fields=["avg(rating) as average, count(*) as total"],
 	)[0]
 
 	data.average_rating = flt(rating_data.average, 1)
@@ -83,11 +85,9 @@
 
 	# get % of reviews per rating
 	reviews_per_rating = []
-	for i in range(1,6):
+	for i in range(1, 6):
 		count = frappe.db.get_all(
-			"Item Review",
-			filters={"website_item": web_item, "rating": i},
-			fields=["count(*) as count"]
+			"Item Review", filters={"website_item": web_item, "rating": i}, fields=["count(*) as count"]
 		)[0].count
 
 		percent = flt((count / rating_data.total or 1) * 100, 0) if count else 0
@@ -98,40 +98,45 @@
 
 	return data
 
+
 def set_reviews_in_cache(web_item, reviews_dict):
 	frappe.cache().hset("item_reviews", web_item, reviews_dict)
 
+
 @frappe.whitelist()
 def add_item_review(web_item, title, rating, comment=None):
-	""" Add an Item Review by a user if non-existent. """
+	"""Add an Item Review by a user if non-existent."""
 	if frappe.session.user == "Guest":
 		# guest user should not reach here ideally in the case they do via an API, throw error
 		frappe.throw(_("You are not verified to write a review yet."), exc=UnverifiedReviewer)
 
 	if not frappe.db.exists("Item Review", {"user": frappe.session.user, "website_item": web_item}):
-		doc = frappe.get_doc({
-			"doctype": "Item Review",
-			"user": frappe.session.user,
-			"customer": get_customer(),
-			"website_item": web_item,
-			"item": frappe.db.get_value("Website Item", web_item, "item_code"),
-			"review_title": title,
-			"rating": rating,
-			"comment": comment
-		})
+		doc = frappe.get_doc(
+			{
+				"doctype": "Item Review",
+				"user": frappe.session.user,
+				"customer": get_customer(),
+				"website_item": web_item,
+				"item": frappe.db.get_value("Website Item", web_item, "item_code"),
+				"review_title": title,
+				"rating": rating,
+				"comment": comment,
+			}
+		)
 		doc.published_on = datetime.today().strftime("%d %B %Y")
 		doc.insert()
 
+
 def get_customer(silent=False):
 	"""
-		silent: Return customer if exists else return nothing. Dont throw error.
+	silent: Return customer if exists else return nothing. Dont throw error.
 	"""
 	user = frappe.session.user
 	contact_name = get_contact_name(user)
 	customer = None
 
 	if contact_name:
-		contact = frappe.get_doc('Contact', contact_name)
+		contact = frappe.get_doc("Contact", contact_name)
 		for link in contact.links:
 			if link.link_doctype == "Customer":
 				customer = link.link_name
@@ -143,5 +148,6 @@
 		return None
 	else:
 		# should not reach here unless via an API
-		frappe.throw(_("You are not a verified customer yet. Please contact us to proceed."),
-			exc=UnverifiedReviewer)
+		frappe.throw(
+			_("You are not a verified customer yet. Please contact us to proceed."), exc=UnverifiedReviewer
+		)
diff --git a/erpnext/e_commerce/doctype/website_item/test_website_item.py b/erpnext/e_commerce/doctype/website_item/test_website_item.py
index b39e4df..828c655 100644
--- a/erpnext/e_commerce/doctype/website_item/test_website_item.py
+++ b/erpnext/e_commerce/doctype/website_item/test_website_item.py
@@ -19,17 +19,23 @@
 from erpnext.stock.doctype.item.test_item import make_item
 
 WEBITEM_DESK_TESTS = ("test_website_item_desk_item_sync", "test_publish_variant_and_template")
-WEBITEM_PRICE_TESTS = ('test_website_item_price_for_logged_in_user', 'test_website_item_price_for_guest_user')
+WEBITEM_PRICE_TESTS = (
+	"test_website_item_price_for_logged_in_user",
+	"test_website_item_price_for_guest_user",
+)
+
 
 class TestWebsiteItem(unittest.TestCase):
 	@classmethod
 	def setUpClass(cls):
-		setup_e_commerce_settings({
-			"company": "_Test Company",
-			"enabled": 1,
-			"default_customer_group": "_Test Customer Group",
-			"price_list": "_Test Price List India"
-		})
+		setup_e_commerce_settings(
+			{
+				"company": "_Test Company",
+				"enabled": 1,
+				"default_customer_group": "_Test Customer Group",
+				"price_list": "_Test Price List India",
+			}
+		)
 
 	@classmethod
 	def tearDownClass(cls):
@@ -37,40 +43,42 @@
 
 	def setUp(self):
 		if self._testMethodName in WEBITEM_DESK_TESTS:
-			make_item("Test Web Item", {
-				"has_variant": 1,
-				"variant_based_on": "Item Attribute",
-				"attributes": [
-					{
-						"attribute": "Test Size"
-					}
-				]
-			})
+			make_item(
+				"Test Web Item",
+				{
+					"has_variant": 1,
+					"variant_based_on": "Item Attribute",
+					"attributes": [{"attribute": "Test Size"}],
+				},
+			)
 		elif self._testMethodName in WEBITEM_PRICE_TESTS:
-			create_user_and_customer_if_not_exists("test_contact_customer@example.com", "_Test Contact For _Test Customer")
+			create_user_and_customer_if_not_exists(
+				"test_contact_customer@example.com", "_Test Contact For _Test Customer"
+			)
 			create_regular_web_item()
 			make_web_item_price(item_code="Test Mobile Phone")
 
 			# Note: When testing web item pricing rule logged-in user pricing rule must differ from guest pricing rule or test will falsely pass.
-			#	  This is because make_web_pricing_rule creates a pricing rule "selling": 1, without specifying "applicable_for". Therefor,
-			#	  when testing for logged-in user the test will get the previous pricing rule because "selling" is still true.
+			# 	  This is because make_web_pricing_rule creates a pricing rule "selling": 1, without specifying "applicable_for". Therefor,
+			# 	  when testing for logged-in user the test will get the previous pricing rule because "selling" is still true.
 			#
 			#     I've attempted to mitigate this by setting applicable_for=Customer, and customer=Guest however, this only results in PermissionError failing the test.
 			make_web_pricing_rule(
-				title="Test Pricing Rule for Test Mobile Phone",
-				item_code="Test Mobile Phone",
-				selling=1)
+				title="Test Pricing Rule for Test Mobile Phone", item_code="Test Mobile Phone", selling=1
+			)
 			make_web_pricing_rule(
 				title="Test Pricing Rule for Test Mobile Phone (Customer)",
 				item_code="Test Mobile Phone",
 				selling=1,
 				discount_percentage="25",
 				applicable_for="Customer",
-				customer="_Test Customer")
+				customer="_Test Customer",
+			)
 
 	def test_index_creation(self):
 		"Check if index is getting created in db."
 		from erpnext.e_commerce.doctype.website_item.website_item import on_doctype_update
+
 		on_doctype_update()
 
 		indices = frappe.db.sql("show index from `tabWebsite Item`", as_dict=1)
@@ -84,7 +92,7 @@
 	def test_website_item_desk_item_sync(self):
 		"Check creation/updation/deletion of Website Item and its impact on Item master."
 		web_item = None
-		item = make_item("Test Web Item") # will return item if exists
+		item = make_item("Test Web Item")  # will return item if exists
 		try:
 			web_item = make_website_item(item, save=False)
 			web_item.save()
@@ -97,7 +105,7 @@
 
 		item.reload()
 		self.assertEqual(web_item.published, 1)
-		self.assertEqual(item.published_in_website, 1) # check if item was back updated
+		self.assertEqual(item.published_in_website, 1)  # check if item was back updated
 		self.assertEqual(web_item.item_group, item.item_group)
 
 		# check if changing item data changes it in website item
@@ -170,9 +178,12 @@
 		from erpnext.setup.doctype.item_group.item_group import get_parent_item_groups
 
 		item_code = "Test Breadcrumb Item"
-		item = make_item(item_code, {
-			"item_group": "_Test Item Group B - 1",
-		})
+		item = make_item(
+			item_code,
+			{
+				"item_group": "_Test Item Group B - 1",
+			},
+		)
 
 		if not frappe.db.exists("Website Item", {"item_code": item_code}):
 			web_item = make_website_item(item, save=False)
@@ -187,7 +198,7 @@
 
 		self.assertEqual(breadcrumbs[0]["name"], "Home")
 		self.assertEqual(breadcrumbs[1]["name"], "Shop by Category")
-		self.assertEqual(breadcrumbs[2]["name"], "_Test Item Group B") # parent item group
+		self.assertEqual(breadcrumbs[2]["name"], "_Test Item Group B")  # parent item group
 		self.assertEqual(breadcrumbs[3]["name"], "_Test Item Group B - 1")
 
 		# tear down
@@ -236,10 +247,7 @@
 		item_code = "Test Mobile Phone"
 
 		# show price for guest user in e commerce settings
-		setup_e_commerce_settings({
-			"show_price": 1,
-			"hide_price_for_guest": 0
-		})
+		setup_e_commerce_settings({"show_price": 1, "hide_price_for_guest": 0})
 
 		# price and pricing rule added via setUp
 
@@ -270,11 +278,11 @@
 
 	def test_website_item_stock_when_out_of_stock(self):
 		"""
-			Check if stock details are fetched correctly for empty inventory when:
-			1) Showing stock availability enabled:
-				- Warehouse unset
-				- Warehouse set
-			2) Showing stock availability disabled
+		Check if stock details are fetched correctly for empty inventory when:
+		1) Showing stock availability enabled:
+		        - Warehouse unset
+		        - Warehouse set
+		2) Showing stock availability disabled
 		"""
 		item_code = "Test Mobile Phone"
 		create_regular_web_item()
@@ -288,7 +296,9 @@
 		self.assertFalse(bool(data.product_info["stock_qty"]))
 
 		# set warehouse
-		frappe.db.set_value("Website Item", {"item_code": item_code}, "website_warehouse", "_Test Warehouse - _TC")
+		frappe.db.set_value(
+			"Website Item", {"item_code": item_code}, "website_warehouse", "_Test Warehouse - _TC"
+		)
 
 		# check if stock details are fetched and item not in stock with warehouse set
 		data = get_product_info_for_website(item_code, skip_quotation_creation=True)
@@ -310,11 +320,11 @@
 
 	def test_website_item_stock_when_in_stock(self):
 		"""
-			Check if stock details are fetched correctly for available inventory when:
-			1) Showing stock availability enabled:
-				- Warehouse set
-				- Warehouse unset
-			2) Showing stock availability disabled
+		Check if stock details are fetched correctly for available inventory when:
+		1) Showing stock availability enabled:
+		        - Warehouse set
+		        - Warehouse unset
+		2) Showing stock availability disabled
 		"""
 		from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 
@@ -324,10 +334,14 @@
 		frappe.local.shopping_cart_settings = None
 
 		# set warehouse
-		frappe.db.set_value("Website Item", {"item_code": item_code}, "website_warehouse", "_Test Warehouse - _TC")
+		frappe.db.set_value(
+			"Website Item", {"item_code": item_code}, "website_warehouse", "_Test Warehouse - _TC"
+		)
 
 		# stock up item
-		stock_entry = make_stock_entry(item_code=item_code, target="_Test Warehouse - _TC", qty=2, rate=100)
+		stock_entry = make_stock_entry(
+			item_code=item_code, target="_Test Warehouse - _TC", qty=2, rate=100
+		)
 
 		# check if stock details are fetched and item is in stock with warehouse set
 		data = get_product_info_for_website(item_code, skip_quotation_creation=True)
@@ -362,10 +376,7 @@
 		item_code = "Test Mobile Phone"
 		web_item = create_regular_web_item(item_code)
 
-		setup_e_commerce_settings({
-			"enable_recommendations": 1,
-			"show_price": 1
-		})
+		setup_e_commerce_settings({"enable_recommendations": 1, "show_price": 1})
 
 		# create recommended web item and price for it
 		recommended_web_item = create_regular_web_item("Test Mobile Phone 1")
@@ -383,7 +394,7 @@
 		self.assertEqual(len(recommended_items), 1)
 		recomm_item = recommended_items[0]
 		self.assertEqual(recomm_item.get("website_item_name"), "Test Mobile Phone 1")
-		self.assertTrue(bool(recomm_item.get("price_info"))) # price fetched
+		self.assertTrue(bool(recomm_item.get("price_info")))  # price fetched
 
 		price_info = recomm_item.get("price_info")
 		self.assertEqual(price_info.get("price_list_rate"), 1000)
@@ -397,7 +408,7 @@
 		recommended_items = web_item.get_recommended_items(e_commerce_settings)
 
 		self.assertEqual(len(recommended_items), 1)
-		self.assertFalse(bool(recommended_items[0].get("price_info"))) # price not fetched
+		self.assertFalse(bool(recommended_items[0].get("price_info")))  # price not fetched
 
 		# tear down
 		web_item.delete()
@@ -410,11 +421,9 @@
 		web_item = create_regular_web_item(item_code)
 
 		# price visible to guests
-		setup_e_commerce_settings({
-			"enable_recommendations": 1,
-			"show_price": 1,
-			"hide_price_for_guest": 0
-		})
+		setup_e_commerce_settings(
+			{"enable_recommendations": 1, "show_price": 1, "hide_price_for_guest": 0}
+		)
 
 		# create recommended web item and price for it
 		recommended_web_item = create_regular_web_item("Test Mobile Phone 1")
@@ -432,7 +441,7 @@
 
 		# test results if show price is enabled
 		self.assertEqual(len(recommended_items), 1)
-		self.assertTrue(bool(recommended_items[0].get("price_info"))) # price fetched
+		self.assertTrue(bool(recommended_items[0].get("price_info")))  # price fetched
 
 		# price hidden from guests
 		frappe.set_user("Administrator")
@@ -445,7 +454,7 @@
 
 		# test results if show price is enabled
 		self.assertEqual(len(recommended_items), 1)
-		self.assertFalse(bool(recommended_items[0].get("price_info"))) # price fetched
+		self.assertFalse(bool(recommended_items[0].get("price_info")))  # price fetched
 
 		# tear down
 		frappe.set_user("Administrator")
@@ -453,6 +462,7 @@
 		recommended_web_item.delete()
 		frappe.get_cached_doc("Item", "Test Mobile Phone 1").delete()
 
+
 def create_regular_web_item(item_code=None, item_args=None, web_args=None):
 	"Create Regular Item and Website Item."
 	item_code = item_code or "Test Mobile Phone"
@@ -468,47 +478,51 @@
 
 	return web_item
 
+
 def make_web_item_price(**kwargs):
 	item_code = kwargs.get("item_code")
 	if not item_code:
 		return
 
 	if not frappe.db.exists("Item Price", {"item_code": item_code}):
-		item_price = frappe.get_doc({
-			"doctype": "Item Price",
-			"item_code": item_code,
-			"price_list": kwargs.get("price_list") or "_Test Price List India",
-			"price_list_rate": kwargs.get("price_list_rate") or 1000
-		})
+		item_price = frappe.get_doc(
+			{
+				"doctype": "Item Price",
+				"item_code": item_code,
+				"price_list": kwargs.get("price_list") or "_Test Price List India",
+				"price_list_rate": kwargs.get("price_list_rate") or 1000,
+			}
+		)
 		item_price.insert()
 	else:
 		item_price = frappe.get_cached_doc("Item Price", {"item_code": item_code})
 
 	return item_price
 
+
 def make_web_pricing_rule(**kwargs):
 	title = kwargs.get("title")
 	if not title:
 		return
 
 	if not frappe.db.exists("Pricing Rule", title):
-		pricing_rule = frappe.get_doc({
-			"doctype": "Pricing Rule",
-			"title": title,
-			"apply_on": kwargs.get("apply_on") or "Item Code",
-			"items": [{
-				"item_code": kwargs.get("item_code")
-			}],
-			"selling": kwargs.get("selling") or 0,
-			"buying": kwargs.get("buying") or 0,
-			"rate_or_discount": kwargs.get("rate_or_discount") or "Discount Percentage",
-			"discount_percentage": kwargs.get("discount_percentage") or 10,
-			"company": kwargs.get("company") or "_Test Company",
-			"currency": kwargs.get("currency") or "INR",
-			"for_price_list": kwargs.get("price_list") or "_Test Price List India",
-			"applicable_for": kwargs.get("applicable_for") or "",
-			"customer": kwargs.get("customer") or "",
-		})
+		pricing_rule = frappe.get_doc(
+			{
+				"doctype": "Pricing Rule",
+				"title": title,
+				"apply_on": kwargs.get("apply_on") or "Item Code",
+				"items": [{"item_code": kwargs.get("item_code")}],
+				"selling": kwargs.get("selling") or 0,
+				"buying": kwargs.get("buying") or 0,
+				"rate_or_discount": kwargs.get("rate_or_discount") or "Discount Percentage",
+				"discount_percentage": kwargs.get("discount_percentage") or 10,
+				"company": kwargs.get("company") or "_Test Company",
+				"currency": kwargs.get("currency") or "INR",
+				"for_price_list": kwargs.get("price_list") or "_Test Price List India",
+				"applicable_for": kwargs.get("applicable_for") or "",
+				"customer": kwargs.get("customer") or "",
+			}
+		)
 		pricing_rule.insert()
 	else:
 		pricing_rule = frappe.get_doc("Pricing Rule", {"title": title})
@@ -516,23 +530,26 @@
 	return pricing_rule
 
 
-def create_user_and_customer_if_not_exists(email, first_name = None):
+def create_user_and_customer_if_not_exists(email, first_name=None):
 	if frappe.db.exists("User", email):
 		return
 
-	frappe.get_doc({
-		"doctype": "User",
-		"user_type": "Website User",
-		"email": email,
-		"send_welcome_email": 0,
-		"first_name": first_name or email.split("@")[0]
-	}).insert(ignore_permissions=True)
+	frappe.get_doc(
+		{
+			"doctype": "User",
+			"user_type": "Website User",
+			"email": email,
+			"send_welcome_email": 0,
+			"first_name": first_name or email.split("@")[0],
+		}
+	).insert(ignore_permissions=True)
 
 	contact = frappe.get_last_doc("Contact", filters={"email_id": email})
-	link = contact.append('links', {})
+	link = contact.append("links", {})
 	link.link_doctype = "Customer"
 	link.link_name = "_Test Customer"
 	link.link_title = "_Test Customer"
 	contact.save()
 
+
 test_dependencies = ["Price List", "Item Price", "Customer", "Contact", "Item"]
diff --git a/erpnext/e_commerce/doctype/website_item/website_item.js b/erpnext/e_commerce/doctype/website_item/website_item.js
index 741e78f..7295e4b 100644
--- a/erpnext/e_commerce/doctype/website_item/website_item.js
+++ b/erpnext/e_commerce/doctype/website_item/website_item.js
@@ -2,23 +2,46 @@
 // For license information, please see license.txt
 
 frappe.ui.form.on('Website Item', {
-	onload: function(frm) {
+	onload: (frm) => {
 		// should never check Private
 		frm.fields_dict["website_image"].df.is_private = 0;
+
+		frm.set_query("website_warehouse", () => {
+			return {
+				filters: {"is_group": 0}
+			};
+		});
 	},
 
-	image: function() {
+	refresh: (frm) => {
+		frm.add_custom_button(__("Prices"), function() {
+			frappe.set_route("List", "Item Price", {"item_code": frm.doc.item_code});
+		}, __("View"));
+
+		frm.add_custom_button(__("Stock"), function() {
+			frappe.route_options = {
+				"item_code": frm.doc.item_code
+			};
+			frappe.set_route("query-report", "Stock Balance");
+		}, __("View"));
+
+		frm.add_custom_button(__("E Commerce Settings"), function() {
+			frappe.set_route("Form", "E Commerce Settings");
+		}, __("View"));
+	},
+
+	image: () => {
 		refresh_field("image_view");
 	},
 
-	copy_from_item_group: function(frm) {
+	copy_from_item_group: (frm) => {
 		return frm.call({
 			doc: frm.doc,
 			method: "copy_specification_from_item_group"
 		});
 	},
 
-	set_meta_tags(frm) {
+	set_meta_tags: (frm) => {
 		frappe.utils.set_meta_tag(frm.doc.route);
 	}
 });
diff --git a/erpnext/e_commerce/doctype/website_item/website_item.py b/erpnext/e_commerce/doctype/website_item/website_item.py
index 62f7f49..f6fea72 100644
--- a/erpnext/e_commerce/doctype/website_item/website_item.py
+++ b/erpnext/e_commerce/doctype/website_item/website_item.py
@@ -29,14 +29,12 @@
 		page_title_field="web_item_name",
 		condition_field="published",
 		template="templates/generators/item/item.html",
-		no_cache=1
+		no_cache=1,
 	)
 
 	def autoname(self):
 		# use naming series to accomodate items with same name (different item code)
-		from frappe.model.naming import make_autoname
-
-		from erpnext.setup.doctype.naming_series.naming_series import get_default_naming_series
+		from frappe.model.naming import get_default_naming_series, make_autoname
 
 		naming_series = get_default_naming_series("Website Item")
 		if not self.name and naming_series:
@@ -88,14 +86,17 @@
 
 	def publish_unpublish_desk_item(self, publish=True):
 		if frappe.db.get_value("Item", self.item_code, "published_in_website") and publish:
-			return # if already published don't publish again
+			return  # if already published don't publish again
 		frappe.db.set_value("Item", self.item_code, "published_in_website", publish)
 
 	def make_route(self):
 		"""Called from set_route in WebsiteGenerator."""
 		if not self.route:
-			return cstr(frappe.db.get_value('Item Group', self.item_group,
-					'route')) + '/' + self.scrub((self.item_name if self.item_name else self.item_code) + '-' + random_string(5))
+			return (
+				cstr(frappe.db.get_value("Item Group", self.item_group, "route"))
+				+ "/"
+				+ self.scrub((self.item_name if self.item_name else self.item_code) + "-" + random_string(5))
+			)
 
 	def update_template_item(self):
 		"""Publish Template Item if Variant is published."""
@@ -124,12 +125,10 @@
 		# find if website image url exists as public
 		file_doc = frappe.get_all(
 			"File",
-			filters={
-				"file_url": self.website_image
-			},
+			filters={"file_url": self.website_image},
 			fields=["name", "is_private"],
 			order_by="is_private asc",
-			limit_page_length=1
+			limit_page_length=1,
 		)
 
 		if file_doc:
@@ -137,7 +136,11 @@
 
 		if not file_doc:
 			if not auto_set_website_image:
-				frappe.msgprint(_("Website Image {0} attached to Item {1} cannot be found").format(self.website_image, self.name))
+				frappe.msgprint(
+					_("Website Image {0} attached to Item {1} cannot be found").format(
+						self.website_image, self.name
+					)
+				)
 
 			self.website_image = None
 
@@ -154,18 +157,23 @@
 
 		import requests.exceptions
 
-		if not self.is_new() and self.website_image != frappe.db.get_value(self.doctype, self.name, "website_image"):
+		if not self.is_new() and self.website_image != frappe.db.get_value(
+			self.doctype, self.name, "website_image"
+		):
 			self.thumbnail = None
 
 		if self.website_image and not self.thumbnail:
 			file_doc = None
 
 			try:
-				file_doc = frappe.get_doc("File", {
-					"file_url": self.website_image,
-					"attached_to_doctype": "Website Item",
-					"attached_to_name": self.name
-				})
+				file_doc = frappe.get_doc(
+					"File",
+					{
+						"file_url": self.website_image,
+						"attached_to_doctype": "Website Item",
+						"attached_to_name": self.name,
+					},
+				)
 			except frappe.DoesNotExistError:
 				pass
 				# cleanup
@@ -177,18 +185,21 @@
 
 			except requests.exceptions.SSLError:
 				frappe.msgprint(
-					_("Warning: Invalid SSL certificate on attachment {0}").format(self.website_image))
+					_("Warning: Invalid SSL certificate on attachment {0}").format(self.website_image)
+				)
 				self.website_image = None
 
 			# for CSV import
 			if self.website_image and not file_doc:
 				try:
-					file_doc = frappe.get_doc({
-						"doctype": "File",
-						"file_url": self.website_image,
-						"attached_to_doctype": "Website Item",
-						"attached_to_name": self.name
-					}).save()
+					file_doc = frappe.get_doc(
+						{
+							"doctype": "File",
+							"file_url": self.website_image,
+							"attached_to_doctype": "Website Item",
+							"attached_to_name": self.name,
+						}
+					).save()
 
 				except IOError:
 					self.website_image = None
@@ -204,11 +215,11 @@
 		context.search_link = "/search"
 		context.body_class = "product-page"
 
-		context.parents = get_parent_item_groups(self.item_group, from_item=True) # breadcumbs
+		context.parents = get_parent_item_groups(self.item_group, from_item=True)  # breadcumbs
 		self.attributes = frappe.get_all(
 			"Item Variant Attribute",
 			fields=["attribute", "attribute_value"],
-			filters={"parent": self.item_code}
+			filters={"parent": self.item_code},
 		)
 
 		if self.slideshow:
@@ -227,7 +238,9 @@
 			context.reviews = context.reviews[:4]
 
 		context.wished = False
-		if frappe.db.exists("Wishlist Item", {"item_code": self.item_code, "parent": frappe.session.user}):
+		if frappe.db.exists(
+			"Wishlist Item", {"item_code": self.item_code, "parent": frappe.session.user}
+		):
 			context.wished = True
 
 		context.user_is_customer = check_if_user_is_customer()
@@ -243,11 +256,12 @@
 			variant.attributes = frappe.get_all(
 				"Item Variant Attribute",
 				filters={"parent": variant.name},
-				fields=["attribute", "attribute_value as value"])
+				fields=["attribute", "attribute_value as value"],
+			)
 
 			# make an attribute-value map for easier access in templates
 			variant.attribute_map = frappe._dict(
-				{attr.attribute : attr.value for attr in variant.attributes}
+				{attr.attribute: attr.value for attr in variant.attributes}
 			)
 
 			for attr in variant.attributes:
@@ -267,9 +281,12 @@
 					values.append(val)
 			else:
 				# get list of values defined (for sequence)
-				for attr_value in frappe.db.get_all("Item Attribute Value",
+				for attr_value in frappe.db.get_all(
+					"Item Attribute Value",
 					fields=["attribute_value"],
-					filters={"parent": attr.attribute}, order_by="idx asc"):
+					filters={"parent": attr.attribute},
+					order_by="idx asc",
+				):
 
 					if attr_value.attribute_value in attribute_values_available.get(attr.attribute, []):
 						values.append(attr_value.attribute_value)
@@ -279,10 +296,10 @@
 
 		safe_description = frappe.utils.to_markdown(self.description)
 
-		context.metatags.url = frappe.utils.get_url() + '/' + context.route
+		context.metatags.url = frappe.utils.get_url() + "/" + context.route
 
 		if context.website_image:
-			if context.website_image.startswith('http'):
+			if context.website_image.startswith("http"):
 				url = context.website_image
 			else:
 				url = frappe.utils.get_url() + context.website_image
@@ -292,24 +309,28 @@
 
 		context.metatags.title = self.web_item_name or self.item_name or self.item_code
 
-		context.metatags['og:type'] = 'product'
-		context.metatags['og:site_name'] = 'ERPNext'
+		context.metatags["og:type"] = "product"
+		context.metatags["og:site_name"] = "ERPNext"
 
 	def set_shopping_cart_data(self, context):
 		from erpnext.e_commerce.shopping_cart.product_info import get_product_info_for_website
-		context.shopping_cart = get_product_info_for_website(self.item_code, skip_quotation_creation=True)
+
+		context.shopping_cart = get_product_info_for_website(
+			self.item_code, skip_quotation_creation=True
+		)
 
 	def copy_specification_from_item_group(self):
 		self.set("website_specifications", [])
 		if self.item_group:
-			for label, desc in frappe.db.get_values("Item Website Specification",
-				{"parent": self.item_group}, ["label", "description"]):
+			for label, desc in frappe.db.get_values(
+				"Item Website Specification", {"parent": self.item_group}, ["label", "description"]
+			):
 				row = self.append("website_specifications")
 				row.label = label
 				row.description = desc
 
 	def get_product_details_section(self, context):
-		""" Get section with tabs or website specifications. """
+		"""Get section with tabs or website specifications."""
 		context.show_tabs = self.show_tabbed_section
 		if self.show_tabbed_section and (self.tabs or self.website_specifications):
 			context.tabs = self.get_tabs()
@@ -321,10 +342,8 @@
 		tab_values["tab_1_title"] = "Product Details"
 		tab_values["tab_1_content"] = frappe.render_template(
 			"templates/generators/item/item_specifications.html",
-			{
-				"website_specifications": self.website_specifications,
-				"show_tabs": self.show_tabbed_section
-			})
+			{"website_specifications": self.website_specifications, "show_tabs": self.show_tabbed_section},
+		)
 
 		for row in self.tabs:
 			tab_values[f"tab_{row.idx + 1}_title"] = _(row.label)
@@ -338,15 +357,11 @@
 
 		query = (
 			frappe.qb.from_(ri)
-			.join(wi).on(ri.item_code == wi.item_code)
-			.select(
-				ri.item_code, ri.route,
-				ri.website_item_name,
-				ri.website_item_thumbnail
-			).where(
-				(ri.parent == self.name)
-				& (wi.published == 1)
-			).orderby(ri.idx)
+			.join(wi)
+			.on(ri.item_code == wi.item_code)
+			.select(ri.item_code, ri.route, ri.website_item_name, ri.website_item_thumbnail)
+			.where((ri.parent == self.name) & (wi.published == 1))
+			.orderby(ri.idx)
 		)
 		items = query.run(as_dict=True)
 
@@ -360,22 +375,24 @@
 			selling_price_list = _set_price_list(settings, None)
 			for item in items:
 				item.price_info = get_price(
-					item.item_code,
-					selling_price_list,
-					settings.default_customer_group,
-					settings.company
+					item.item_code, selling_price_list, settings.default_customer_group, settings.company
 				)
 
 		return items
 
+
 def invalidate_cache_for_web_item(doc):
 	"""Invalidate Website Item Group cache and rebuild ItemVariantsCacheManager."""
 	from erpnext.stock.doctype.item.item import invalidate_item_variants_cache_for_website
 
 	invalidate_cache_for(doc, doc.item_group)
 
-	website_item_groups = list(set((doc.get("old_website_item_groups") or [])
-		+ [d.item_group for d in doc.get({"doctype": "Website Item Group"}) if d.item_group]))
+	website_item_groups = list(
+		set(
+			(doc.get("old_website_item_groups") or [])
+			+ [d.item_group for d in doc.get({"doctype": "Website Item Group"}) if d.item_group]
+		)
+	)
 
 	for item_group in website_item_groups:
 		invalidate_cache_for(doc, item_group)
@@ -385,6 +402,7 @@
 
 	invalidate_item_variants_cache_for_website(doc)
 
+
 def on_doctype_update():
 	# since route is a Text column, it needs a length for indexing
 	frappe.db.add_index("Website Item", ["route(500)"])
@@ -392,6 +410,7 @@
 	frappe.db.add_index("Website Item", ["item_group"])
 	frappe.db.add_index("Website Item", ["brand"])
 
+
 def check_if_user_is_customer(user=None):
 	from frappe.contacts.doctype.contact.contact import get_contact_name
 
@@ -402,7 +421,7 @@
 	customer = None
 
 	if contact_name:
-		contact = frappe.get_doc('Contact', contact_name)
+		contact = frappe.get_doc("Contact", contact_name)
 		for link in contact.links:
 			if link.link_doctype == "Customer":
 				customer = link.link_name
@@ -410,6 +429,7 @@
 
 	return True if customer else False
 
+
 @frappe.whitelist()
 def make_website_item(doc, save=True):
 	if not doc:
@@ -425,8 +445,17 @@
 	website_item = frappe.new_doc("Website Item")
 	website_item.web_item_name = doc.get("item_name")
 
-	fields_to_map = ["item_code", "item_name", "item_group", "stock_uom", "brand", "image",
-		"has_variants", "variant_of", "description"]
+	fields_to_map = [
+		"item_code",
+		"item_name",
+		"item_group",
+		"stock_uom",
+		"brand",
+		"image",
+		"has_variants",
+		"variant_of",
+		"description",
+	]
 	for field in fields_to_map:
 		website_item.update({field: doc.get(field)})
 
@@ -438,4 +467,4 @@
 	# Add to search cache
 	insert_item_to_index(website_item)
 
-	return [website_item.name, website_item.web_item_name]
\ No newline at end of file
+	return [website_item.name, website_item.web_item_name]
diff --git a/erpnext/e_commerce/doctype/website_offer/website_offer.py b/erpnext/e_commerce/doctype/website_offer/website_offer.py
index d73c132..8c92f75 100644
--- a/erpnext/e_commerce/doctype/website_offer/website_offer.py
+++ b/erpnext/e_commerce/doctype/website_offer/website_offer.py
@@ -9,6 +9,7 @@
 class WebsiteOffer(Document):
 	pass
 
+
 @frappe.whitelist(allow_guest=True)
 def get_offer_details(offer_id):
-	return frappe.db.get_value('Website Offer', {'name': offer_id}, ['offer_details'])
+	return frappe.db.get_value("Website Offer", {"name": offer_id}, ["offer_details"])
diff --git a/erpnext/e_commerce/doctype/wishlist/test_wishlist.py b/erpnext/e_commerce/doctype/wishlist/test_wishlist.py
index 504bb65..9d27126 100644
--- a/erpnext/e_commerce/doctype/wishlist/test_wishlist.py
+++ b/erpnext/e_commerce/doctype/wishlist/test_wishlist.py
@@ -34,14 +34,16 @@
 
 		# check if wishlist was created and item was added
 		self.assertTrue(frappe.db.exists("Wishlist", {"user": frappe.session.user}))
-		self.assertTrue(frappe.db.exists("Wishlist Item", {"item_code": "Test Phone Series X", "parent": frappe.session.user}))
+		self.assertTrue(
+			frappe.db.exists(
+				"Wishlist Item", {"item_code": "Test Phone Series X", "parent": frappe.session.user}
+			)
+		)
 
 		# add second item to wishlist
 		add_to_wishlist("Test Phone Series Y")
 		wishlist_length = frappe.db.get_value(
-			"Wishlist Item",
-			{"parent": frappe.session.user},
-			"count(*)"
+			"Wishlist Item", {"parent": frappe.session.user}, "count(*)"
 		)
 		self.assertEqual(wishlist_length, 2)
 
@@ -49,9 +51,7 @@
 		remove_from_wishlist("Test Phone Series Y")
 
 		wishlist_length = frappe.db.get_value(
-			"Wishlist Item",
-			{"parent": frappe.session.user},
-			"count(*)"
+			"Wishlist Item", {"parent": frappe.session.user}, "count(*)"
 		)
 		self.assertIsNone(frappe.db.exists("Wishlist Item", {"parent": frappe.session.user}))
 		self.assertEqual(wishlist_length, 0)
@@ -74,29 +74,44 @@
 
 		# check wishlist and its content for users
 		self.assertTrue(frappe.db.exists("Wishlist", {"user": test_user.name}))
-		self.assertTrue(frappe.db.exists("Wishlist Item",
-			{"item_code": "Test Phone Series X", "parent": test_user.name}))
+		self.assertTrue(
+			frappe.db.exists(
+				"Wishlist Item", {"item_code": "Test Phone Series X", "parent": test_user.name}
+			)
+		)
 
 		self.assertTrue(frappe.db.exists("Wishlist", {"user": test_user_1.name}))
-		self.assertTrue(frappe.db.exists("Wishlist Item",
-			{"item_code": "Test Phone Series X", "parent": test_user_1.name}))
+		self.assertTrue(
+			frappe.db.exists(
+				"Wishlist Item", {"item_code": "Test Phone Series X", "parent": test_user_1.name}
+			)
+		)
 
 		# remove item for second user
 		remove_from_wishlist("Test Phone Series X")
 
 		# make sure item was removed for second user and not first
-		self.assertFalse(frappe.db.exists("Wishlist Item",
-			{"item_code": "Test Phone Series X", "parent": test_user_1.name}))
-		self.assertTrue(frappe.db.exists("Wishlist Item",
-			{"item_code": "Test Phone Series X", "parent": test_user.name}))
+		self.assertFalse(
+			frappe.db.exists(
+				"Wishlist Item", {"item_code": "Test Phone Series X", "parent": test_user_1.name}
+			)
+		)
+		self.assertTrue(
+			frappe.db.exists(
+				"Wishlist Item", {"item_code": "Test Phone Series X", "parent": test_user.name}
+			)
+		)
 
 		# remove item for first user
 		frappe.set_user(test_user.name)
 		remove_from_wishlist("Test Phone Series X")
-		self.assertFalse(frappe.db.exists("Wishlist Item",
-			{"item_code": "Test Phone Series X", "parent": test_user.name}))
+		self.assertFalse(
+			frappe.db.exists(
+				"Wishlist Item", {"item_code": "Test Phone Series X", "parent": test_user.name}
+			)
+		)
 
 		# tear down
 		frappe.set_user("Administrator")
 		frappe.get_doc("Wishlist", {"user": test_user.name}).delete()
-		frappe.get_doc("Wishlist", {"user": test_user_1.name}).delete()
\ No newline at end of file
+		frappe.get_doc("Wishlist", {"user": test_user_1.name}).delete()
diff --git a/erpnext/e_commerce/doctype/wishlist/wishlist.py b/erpnext/e_commerce/doctype/wishlist/wishlist.py
index 50e3d3a..5d6ab41 100644
--- a/erpnext/e_commerce/doctype/wishlist/wishlist.py
+++ b/erpnext/e_commerce/doctype/wishlist/wishlist.py
@@ -9,6 +9,7 @@
 class Wishlist(Document):
 	pass
 
+
 @frappe.whitelist()
 def add_to_wishlist(item_code):
 	"""Insert Item into wishlist."""
@@ -20,7 +21,8 @@
 		"Website Item",
 		{"item_code": item_code},
 		["image", "website_warehouse", "name", "web_item_name", "item_name", "item_group", "route"],
-		as_dict=1)
+		as_dict=1,
+	)
 
 	wished_item_dict = {
 		"item_code": item_code,
@@ -30,7 +32,7 @@
 		"web_item_name": web_item_data.get("web_item_name"),
 		"image": web_item_data.get("image"),
 		"warehouse": web_item_data.get("website_warehouse"),
-		"route": web_item_data.get("route")
+		"route": web_item_data.get("route"),
 	}
 
 	if not frappe.db.exists("Wishlist", frappe.session.user):
@@ -41,28 +43,20 @@
 		wishlist.save(ignore_permissions=True)
 	else:
 		wishlist = frappe.get_doc("Wishlist", frappe.session.user)
-		item = wishlist.append('items', wished_item_dict)
+		item = wishlist.append("items", wished_item_dict)
 		item.db_insert()
 
 	if hasattr(frappe.local, "cookie_manager"):
 		frappe.local.cookie_manager.set_cookie("wish_count", str(len(wishlist.items)))
 
+
 @frappe.whitelist()
 def remove_from_wishlist(item_code):
 	if frappe.db.exists("Wishlist Item", {"item_code": item_code, "parent": frappe.session.user}):
-		frappe.db.delete(
-			"Wishlist Item",
-			{
-				"item_code": item_code,
-				"parent": frappe.session.user
-			}
-		)
-		frappe.db.commit() # nosemgrep
+		frappe.db.delete("Wishlist Item", {"item_code": item_code, "parent": frappe.session.user})
+		frappe.db.commit()  # nosemgrep
 
-		wishlist_items = frappe.db.get_values(
-			"Wishlist Item",
-			filters={"parent": frappe.session.user}
-		)
+		wishlist_items = frappe.db.get_values("Wishlist Item", filters={"parent": frappe.session.user})
 
 		if hasattr(frappe.local, "cookie_manager"):
-			frappe.local.cookie_manager.set_cookie("wish_count", str(len(wishlist_items)))
\ No newline at end of file
+			frappe.local.cookie_manager.set_cookie("wish_count", str(len(wishlist_items)))
diff --git a/erpnext/e_commerce/legacy_search.py b/erpnext/e_commerce/legacy_search.py
index 752c33e..ef8e86d 100644
--- a/erpnext/e_commerce/legacy_search.py
+++ b/erpnext/e_commerce/legacy_search.py
@@ -9,8 +9,9 @@
 # TODO: Make obsolete
 INDEX_NAME = "products"
 
+
 class ProductSearch(FullTextSearch):
-	""" Wrapper for WebsiteSearch """
+	"""Wrapper for WebsiteSearch"""
 
 	def get_schema(self):
 		return Schema(
@@ -29,7 +30,7 @@
 		in www/ and routes from published documents
 
 		Returns:
-			self (object): FullTextSearch Instance
+		        self (object): FullTextSearch Instance
 		"""
 		items = get_all_published_items()
 		documents = [self.get_document_to_index(item) for item in items]
@@ -69,12 +70,12 @@
 		"""Search from the current index
 
 		Args:
-			text (str): String to search for
-			scope (str, optional): Scope to limit the search. Defaults to None.
-			limit (int, optional): Limit number of search results. Defaults to 20.
+		        text (str): String to search for
+		        scope (str, optional): Scope to limit the search. Defaults to None.
+		        limit (int, optional): Limit number of search results. Defaults to 20.
 
 		Returns:
-			[List(_dict)]: Search results
+		        [List(_dict)]: Search results
 		"""
 		ix = self.get_index()
 
@@ -111,17 +112,23 @@
 			keyword_highlights=keyword_highlights,
 		)
 
+
 def get_all_published_items():
-	return frappe.get_all("Website Item", filters={"variant_of": "", "published": 1}, pluck="item_code")
+	return frappe.get_all(
+		"Website Item", filters={"variant_of": "", "published": 1}, pluck="item_code"
+	)
+
 
 def update_index_for_path(path):
 	search = ProductSearch(INDEX_NAME)
 	return search.update_index_by_name(path)
 
+
 def remove_document_from_index(path):
 	search = ProductSearch(INDEX_NAME)
 	return search.remove_document_from_index(path)
 
+
 def build_index_for_all_routes():
 	search = ProductSearch(INDEX_NAME)
 	return search.build()
diff --git a/erpnext/e_commerce/product_data_engine/filters.py b/erpnext/e_commerce/product_data_engine/filters.py
index c4a3cb9..e5e5e97 100644
--- a/erpnext/e_commerce/product_data_engine/filters.py
+++ b/erpnext/e_commerce/product_data_engine/filters.py
@@ -14,39 +14,59 @@
 		self.item_group = item_group
 
 	def get_field_filters(self):
+		from erpnext.setup.doctype.item_group.item_group import get_child_groups_for_website
+
 		if not self.item_group and not self.doc.enable_field_filters:
 			return
 
 		fields, filter_data = [], []
-		filter_fields = [row.fieldname for row in self.doc.filter_fields] # fields in settings
+		filter_fields = [row.fieldname for row in self.doc.filter_fields]  # fields in settings
 
-		# filter valid field filters i.e. those that exist in Item
-		item_meta = frappe.get_meta('Item', cached=True)
-		fields = [item_meta.get_field(field) for field in filter_fields if item_meta.has_field(field)]
+		# filter valid field filters i.e. those that exist in Website Item
+		web_item_meta = frappe.get_meta("Website Item", cached=True)
+		fields = [
+			web_item_meta.get_field(field) for field in filter_fields if web_item_meta.has_field(field)
+		]
 
 		for df in fields:
-			item_filters, item_or_filters = {}, []
+			item_filters, item_or_filters = {"published": 1}, []
 			link_doctype_values = self.get_filtered_link_doctype_records(df)
 
 			if df.fieldtype == "Link":
 				if self.item_group:
-					item_or_filters.extend([
-						["item_group", "=", self.item_group],
-						["Website Item Group", "item_group", "=", self.item_group] # consider website item groups
-					])
+					include_child = frappe.db.get_value("Item Group", self.item_group, "include_descendants")
+					if include_child:
+						include_groups = get_child_groups_for_website(self.item_group, include_self=True)
+						include_groups = [x.name for x in include_groups]
+						item_or_filters.extend(
+							[
+								["item_group", "in", include_groups],
+								["Website Item Group", "item_group", "=", self.item_group],  # consider website item groups
+							]
+						)
+					else:
+						item_or_filters.extend(
+							[
+								["item_group", "=", self.item_group],
+								["Website Item Group", "item_group", "=", self.item_group],  # consider website item groups
+							]
+						)
+
+				# exclude variants if mentioned in settings
+				if frappe.db.get_single_value("E Commerce Settings", "hide_variants"):
+					item_filters["variant_of"] = ["is", "not set"]
 
 				# Get link field values attached to published items
-				item_filters['published_in_website'] = 1
 				item_values = frappe.get_all(
-					"Item",
+					"Website Item",
 					fields=[df.fieldname],
 					filters=item_filters,
 					or_filters=item_or_filters,
 					distinct="True",
-					pluck=df.fieldname
+					pluck=df.fieldname,
 				)
 
-				values = list(set(item_values) & link_doctype_values) # intersection of both
+				values = list(set(item_values) & link_doctype_values)  # intersection of both
 			else:
 				# table multiselect
 				values = list(link_doctype_values)
@@ -62,10 +82,10 @@
 
 	def get_filtered_link_doctype_records(self, field):
 		"""
-			Get valid link doctype records depending on filters.
-			Apply enable/disable/show_in_website filter.
-			Returns:
-				set: A set containing valid record names
+		Get valid link doctype records depending on filters.
+		Apply enable/disable/show_in_website filter.
+		Returns:
+		        set: A set containing valid record names
 		"""
 		link_doctype = field.get_link_doctype()
 		meta = frappe.get_meta(link_doctype, cached=True) if link_doctype else None
@@ -81,12 +101,12 @@
 		if not meta:
 			return filters
 
-		if meta.has_field('enabled'):
-			filters['enabled'] = 1
-		if meta.has_field('disabled'):
-			filters['disabled'] = 0
-		if meta.has_field('show_in_website'):
-			filters['show_in_website'] = 1
+		if meta.has_field("enabled"):
+			filters["enabled"] = 1
+		if meta.has_field("disabled"):
+			filters["disabled"] = 0
+		if meta.has_field("show_in_website"):
+			filters["show_in_website"] = 1
 
 		return filters
 
@@ -101,12 +121,9 @@
 
 		result = frappe.get_all(
 			"Item Variant Attribute",
-			filters={
-				"attribute": ["in", attributes],
-				"attribute_value": ["is", "set"]
-			},
+			filters={"attribute": ["in", attributes], "attribute_value": ["is", "set"]},
 			fields=["attribute", "attribute_value"],
-			distinct=True
+			distinct=True,
 		)
 
 		attribute_value_map = {}
@@ -126,11 +143,13 @@
 		# [25, 60] rounded min max
 		min_range_absolute, max_range_absolute = floor(min_discount), floor(max_discount)
 
-		min_range = int(min_discount - (min_range_absolute % 10)) # 20
-		max_range = int(max_discount - (max_range_absolute % 10)) # 60
+		min_range = int(min_discount - (min_range_absolute % 10))  # 20
+		max_range = int(max_discount - (max_range_absolute % 10))  # 60
 
-		min_range = (min_range + 10) if min_range != min_range_absolute else min_range # 30 (upper limit of 25.89 in range of 10)
-		max_range = (max_range + 10) if max_range != max_range_absolute else max_range # 60
+		min_range = (
+			(min_range + 10) if min_range != min_range_absolute else min_range
+		)  # 30 (upper limit of 25.89 in range of 10)
+		max_range = (max_range + 10) if max_range != max_range_absolute else max_range  # 60
 
 		for discount in range(min_range, (max_range + 1), 10):
 			label = f"{discount}% and below"
diff --git a/erpnext/e_commerce/product_data_engine/query.py b/erpnext/e_commerce/product_data_engine/query.py
index cfc3c7b..5a75382 100644
--- a/erpnext/e_commerce/product_data_engine/query.py
+++ b/erpnext/e_commerce/product_data_engine/query.py
@@ -13,12 +13,13 @@
 	"""Query engine for product listing
 
 	Attributes:
-		fields (list): Fields to fetch in query
-		conditions (string): Conditions for query building
-		or_conditions (string): Search conditions
-		page_length (Int): Length of page for the query
-		settings (Document): E Commerce Settings DocType
+	        fields (list): Fields to fetch in query
+	        conditions (string): Conditions for query building
+	        or_conditions (string): Search conditions
+	        page_length (Int): Length of page for the query
+	        settings (Document): E Commerce Settings DocType
 	"""
+
 	def __init__(self):
 		self.settings = frappe.get_doc("E Commerce Settings")
 		self.page_length = self.settings.products_per_page or 20
@@ -26,30 +27,42 @@
 		self.or_filters = []
 		self.filters = [["published", "=", 1]]
 		self.fields = [
-			"web_item_name", "name", "item_name", "item_code", "website_image",
-			"variant_of", "has_variants", "item_group", "image", "web_long_description",
-			"short_description", "route", "website_warehouse", "ranking", "on_backorder"
+			"web_item_name",
+			"name",
+			"item_name",
+			"item_code",
+			"website_image",
+			"variant_of",
+			"has_variants",
+			"item_group",
+			"image",
+			"web_long_description",
+			"short_description",
+			"route",
+			"website_warehouse",
+			"ranking",
+			"on_backorder",
 		]
 
 	def query(self, attributes=None, fields=None, search_term=None, start=0, item_group=None):
 		"""
 		Args:
-			attributes (dict, optional): Item Attribute filters
-			fields (dict, optional): Field level filters
-			search_term (str, optional): Search term to lookup
-			start (int, optional): Page start
+		        attributes (dict, optional): Item Attribute filters
+		        fields (dict, optional): Field level filters
+		        search_term (str, optional): Search term to lookup
+		        start (int, optional): Page start
 
 		Returns:
-			dict: Dict containing items, item count & discount range
+		        dict: Dict containing items, item count & discount range
 		"""
 		# track if discounts included in field filters
 		self.filter_with_discount = bool(fields.get("discount"))
 		result, discount_list, website_item_groups, cart_items, count = [], [], [], [], 0
 
-		website_item_groups = self.get_website_item_group_results(item_group, website_item_groups)
-
 		if fields:
 			self.build_fields_filters(fields)
+		if item_group:
+			self.build_item_group_filters(item_group)
 		if search_term:
 			self.build_search_filters(search_term)
 		if self.settings.hide_variants:
@@ -61,8 +74,6 @@
 		else:
 			result, count = self.query_items(start=start)
 
-		result = self.combine_web_item_group_results(item_group, result, website_item_groups)
-
 		# sort combined results by ranking
 		result = sorted(result, key=lambda x: x.get("ranking"), reverse=True)
 
@@ -77,11 +88,7 @@
 
 		result = self.filter_results_by_discount(fields, result)
 
-		return {
-			"items": result,
-			"items_count": count,
-			"discounts": discounts
-		}
+		return {"items": result, "items_count": count, "discounts": discounts}
 
 	def query_items(self, start=0):
 		"""Build a query to fetch Website Items based on field filters."""
@@ -93,8 +100,9 @@
 			filters=self.filters,
 			or_filters=self.or_filters,
 			limit_page_length=184467440737095516,
-			limit_start=start, # get all items from this offset for total count ahead
-			order_by="ranking desc")
+			limit_start=start,  # get all items from this offset for total count ahead
+			order_by="ranking desc",
+		)
 		count = len(count_items)
 
 		# If discounts included, return all rows.
@@ -110,7 +118,8 @@
 			or_filters=self.or_filters,
 			limit_page_length=page_length,
 			limit_start=start,
-			order_by="ranking desc")
+			order_by="ranking desc",
+		)
 
 		return items, count
 
@@ -129,8 +138,9 @@
 				filters=[
 					["published_in_website", "=", 1],
 					["Item Variant Attribute", "attribute", "=", attribute],
-					["Item Variant Attribute", "attribute_value", "in", values]
-				])
+					["Item Variant Attribute", "attribute_value", "in", values],
+				],
+			)
 			item_codes.append({x.item_code for x in item_code_list})
 
 		if item_codes:
@@ -145,21 +155,21 @@
 		"""Build filters for field values
 
 		Args:
-			filters (dict): Filters
+		        filters (dict): Filters
 		"""
 		for field, values in filters.items():
 			if not values or field == "discount":
 				continue
 
 			# handle multiselect fields in filter addition
-			meta = frappe.get_meta('Website Item', cached=True)
+			meta = frappe.get_meta("Website Item", cached=True)
 			df = meta.get_field(field)
-			if df.fieldtype == 'Table MultiSelect':
+			if df.fieldtype == "Table MultiSelect":
 				child_doctype = df.options
 				child_meta = frappe.get_meta(child_doctype, cached=True)
 				fields = child_meta.get("fields")
 				if fields:
-					self.filters.append([child_doctype, fields[0].fieldname, 'IN', values])
+					self.filters.append([child_doctype, fields[0].fieldname, "IN", values])
 			elif isinstance(values, list):
 				# If value is a list use `IN` query
 				self.filters.append([field, "in", values])
@@ -167,14 +177,34 @@
 				# `=` will be faster than `IN` for most cases
 				self.filters.append([field, "=", values])
 
+	def build_item_group_filters(self, item_group):
+		"Add filters for Item group page and include Website Item Groups."
+		from erpnext.setup.doctype.item_group.item_group import get_child_groups_for_website
+
+		item_group_filters = []
+
+		item_group_filters.append(["Website Item", "item_group", "=", item_group])
+		# Consider Website Item Groups
+		item_group_filters.append(["Website Item Group", "item_group", "=", item_group])
+
+		if frappe.db.get_value("Item Group", item_group, "include_descendants"):
+			# include child item group's items as well
+			# eg. Group Node A, will show items of child 1 and child 2 as well
+			# on it's web page
+			include_groups = get_child_groups_for_website(item_group, include_self=True)
+			include_groups = [x.name for x in include_groups]
+			item_group_filters.append(["Website Item", "item_group", "in", include_groups])
+
+		self.or_filters.extend(item_group_filters)
+
 	def build_search_filters(self, search_term):
 		"""Query search term in specified fields
 
 		Args:
-			search_term (str): Search candidate
+		        search_term (str): Search candidate
 		"""
 		# Default fields to search from
-		default_fields = {'item_code', 'item_name', 'web_long_description', 'item_group'}
+		default_fields = {"item_code", "item_name", "web_long_description", "item_group"}
 
 		# Get meta search fields
 		meta = frappe.get_meta("Website Item")
@@ -182,35 +212,24 @@
 
 		# Join the meta fields and default fields set
 		search_fields = default_fields.union(meta_fields)
-		if frappe.db.count('Website Item', cache=True) > 50000:
-			search_fields.discard('web_long_description')
+		if frappe.db.count("Website Item", cache=True) > 50000:
+			search_fields.discard("web_long_description")
 
 		# Build or filters for query
-		search = '%{}%'.format(search_term)
+		search = "%{}%".format(search_term)
 		for field in search_fields:
 			self.or_filters.append([field, "like", search])
 
-	def get_website_item_group_results(self, item_group, website_item_groups):
-		"""Get Web Items for Item Group Page via Website Item Groups."""
-		if item_group:
-			website_item_groups = frappe.db.get_all(
-				"Website Item",
-				fields=self.fields + ["`tabWebsite Item Group`.parent as wig_parent"],
-				filters=[
-					["Website Item Group", "item_group", "=", item_group],
-					["published", "=", 1]
-				]
-			)
-		return website_item_groups
-
 	def add_display_details(self, result, discount_list, cart_items):
 		"""Add price and availability details in result."""
 		for item in result:
-			product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get('product_info')
+			product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get(
+				"product_info"
+			)
 
-			if product_info and product_info['price']:
+			if product_info and product_info["price"]:
 				# update/mutate item and discount_list objects
-				self.get_price_discount_info(item, product_info['price'], discount_list)
+				self.get_price_discount_info(item, product_info["price"], discount_list)
 
 			if self.settings.show_stock_availability:
 				self.get_stock_availability(item)
@@ -218,7 +237,9 @@
 			item.in_cart = item.item_code in cart_items
 
 			item.wished = False
-			if frappe.db.exists("Wishlist Item", {"item_code": item.item_code, "parent": frappe.session.user}):
+			if frappe.db.exists(
+				"Wishlist Item", {"item_code": item.item_code, "parent": frappe.session.user}
+			):
 				item.wished = True
 
 		return result, discount_list
@@ -229,13 +250,14 @@
 		for field in fields:
 			item[field] = price_object.get(field)
 
-		if price_object.get('discount_percent'):
+		if price_object.get("discount_percent"):
 			item.discount_percent = flt(price_object.discount_percent)
 			discount_list.append(price_object.discount_percent)
 
 		if item.formatted_mrp:
-			item.discount = price_object.get('formatted_discount_percent') or \
-				price_object.get('formatted_discount_rate')
+			item.discount = price_object.get("formatted_discount_percent") or price_object.get(
+				"formatted_discount_rate"
+			)
 
 	def get_stock_availability(self, item):
 		"""Modify item object and add stock details."""
@@ -255,47 +277,46 @@
 		elif warehouse:
 			# stock item and has warehouse
 			actual_qty = frappe.db.get_value(
-				"Bin",
-				{"item_code": item.item_code,"warehouse": item.get("website_warehouse")},
-				"actual_qty")
+				"Bin", {"item_code": item.item_code, "warehouse": item.get("website_warehouse")}, "actual_qty"
+			)
 			item.in_stock = bool(flt(actual_qty))
 
 	def get_cart_items(self):
 		customer = get_customer(silent=True)
 		if customer:
-			quotation = frappe.get_all("Quotation", fields=["name"], filters=
-				{"party_name": customer, "contact_email": frappe.session.user, "order_type": "Shopping Cart", "docstatus": 0},
-				order_by="modified desc", limit_page_length=1)
+			quotation = frappe.get_all(
+				"Quotation",
+				fields=["name"],
+				filters={
+					"party_name": customer,
+					"contact_email": frappe.session.user,
+					"order_type": "Shopping Cart",
+					"docstatus": 0,
+				},
+				order_by="modified desc",
+				limit_page_length=1,
+			)
 			if quotation:
 				items = frappe.get_all(
-					"Quotation Item",
-					fields=["item_code"],
-					filters={
-						"parent": quotation[0].get("name")
-					})
+					"Quotation Item", fields=["item_code"], filters={"parent": quotation[0].get("name")}
+				)
 				items = [row.item_code for row in items]
 				return items
 
 		return []
 
-	def combine_web_item_group_results(self, item_group, result, website_item_groups):
-		"""Combine results with context of website item groups into item results."""
-		if item_group and website_item_groups:
-			items_list = {row.name for row in result}
-			for row in website_item_groups:
-				if row.wig_parent not in items_list:
-					result.append(row)
-
-		return result
-
 	def filter_results_by_discount(self, fields, result):
 		if fields and fields.get("discount"):
 			discount_percent = frappe.utils.flt(fields["discount"][0])
-			result = [row for row in result if row.get("discount_percent") and row.discount_percent <= discount_percent]
+			result = [
+				row
+				for row in result
+				if row.get("discount_percent") and row.discount_percent <= discount_percent
+			]
 
 		if self.filter_with_discount:
 			# no limit was added to results while querying
 			# slice results manually
-			result[:self.page_length]
+			result[: self.page_length]
 
 		return result
diff --git a/erpnext/e_commerce/product_data_engine/test_item_group_product_data_engine.py b/erpnext/e_commerce/product_data_engine/test_item_group_product_data_engine.py
index f0f7918..45bc20e 100644
--- a/erpnext/e_commerce/product_data_engine/test_item_group_product_data_engine.py
+++ b/erpnext/e_commerce/product_data_engine/test_item_group_product_data_engine.py
@@ -10,17 +10,17 @@
 
 test_dependencies = ["Item", "Item Group"]
 
+
 class TestItemGroupProductDataEngine(unittest.TestCase):
 	"Test Products & Sub-Category Querying for Product Listing on Item Group Page."
 
-	@classmethod
-	def setUpClass(cls):
+	def setUp(self):
 		item_codes = [
 			("Test Mobile A", "_Test Item Group B"),
 			("Test Mobile B", "_Test Item Group B"),
 			("Test Mobile C", "_Test Item Group B - 1"),
 			("Test Mobile D", "_Test Item Group B - 1"),
-			("Test Mobile E", "_Test Item Group B - 2")
+			("Test Mobile E", "_Test Item Group B - 2"),
 		]
 		for item in item_codes:
 			item_code = item[0]
@@ -28,18 +28,22 @@
 			if not frappe.db.exists("Website Item", {"item_code": item_code}):
 				create_regular_web_item(item_code, item_args=item_args)
 
-	@classmethod
-	def tearDownClass(cls):
+		frappe.db.set_value("Item Group", "_Test Item Group B - 1", "show_in_website", 1)
+		frappe.db.set_value("Item Group", "_Test Item Group B - 2", "show_in_website", 1)
+
+	def tearDown(self):
 		frappe.db.rollback()
 
 	def test_product_listing_in_item_group(self):
 		"Test if only products belonging to the Item Group are fetched."
-		result = get_product_filter_data(query_args={
-			"field_filters": {},
-			"attribute_filters": {},
-			"start": 0,
-			"item_group": "_Test Item Group B"
-		})
+		result = get_product_filter_data(
+			query_args={
+				"field_filters": {},
+				"attribute_filters": {},
+				"start": 0,
+				"item_group": "_Test Item Group B",
+			}
+		)
 
 		items = result.get("items")
 		item_codes = [item.get("item_code") for item in items]
@@ -53,49 +57,52 @@
 		website_item = frappe.get_doc("Website Item", {"item_code": "Test Mobile E"})
 
 		# show item belonging to '_Test Item Group B - 2' in '_Test Item Group B - 1' as well
-		website_item.append("website_item_groups", {
-			"item_group": "_Test Item Group B - 1"
-		})
+		website_item.append("website_item_groups", {"item_group": "_Test Item Group B - 1"})
 		website_item.save()
 
-		result = get_product_filter_data(query_args={
-			"field_filters": {},
-			"attribute_filters": {},
-			"start": 0,
-			"item_group": "_Test Item Group B - 1"
-		})
+		result = get_product_filter_data(
+			query_args={
+				"field_filters": {},
+				"attribute_filters": {},
+				"start": 0,
+				"item_group": "_Test Item Group B - 1",
+			}
+		)
 
 		items = result.get("items")
 		item_codes = [item.get("item_code") for item in items]
 
 		self.assertEqual(len(items), 3)
-		self.assertIn("Test Mobile E", item_codes) # visible in other item groups
+		self.assertIn("Test Mobile E", item_codes)  # visible in other item groups
 		self.assertIn("Test Mobile C", item_codes)
 		self.assertIn("Test Mobile D", item_codes)
 
-		result = get_product_filter_data(query_args={
-			"field_filters": {},
-			"attribute_filters": {},
-			"start": 0,
-			"item_group": "_Test Item Group B - 2"
-		})
+		result = get_product_filter_data(
+			query_args={
+				"field_filters": {},
+				"attribute_filters": {},
+				"start": 0,
+				"item_group": "_Test Item Group B - 2",
+			}
+		)
 
 		items = result.get("items")
 
 		self.assertEqual(len(items), 1)
-		self.assertEqual(items[0].get("item_code"), "Test Mobile E") # visible in own item group
+		self.assertEqual(items[0].get("item_code"), "Test Mobile E")  # visible in own item group
 
 	def test_item_group_with_sub_groups(self):
 		"Test Valid Sub Item Groups in Item Group Page."
-		frappe.db.set_value("Item Group", "_Test Item Group B - 1", "show_in_website", 1)
 		frappe.db.set_value("Item Group", "_Test Item Group B - 2", "show_in_website", 0)
 
-		result = get_product_filter_data(query_args={
-			"field_filters": {},
-			"attribute_filters": {},
-			"start": 0,
-			"item_group": "_Test Item Group B"
-		})
+		result = get_product_filter_data(
+			query_args={
+				"field_filters": {},
+				"attribute_filters": {},
+				"start": 0,
+				"item_group": "_Test Item Group B",
+			}
+		)
 
 		self.assertTrue(bool(result.get("sub_categories")))
 
@@ -104,14 +111,60 @@
 		self.assertIn("_Test Item Group B - 1", child_groups)
 
 		frappe.db.set_value("Item Group", "_Test Item Group B - 2", "show_in_website", 1)
-		result = get_product_filter_data(query_args={
-			"field_filters": {},
-			"attribute_filters": {},
-			"start": 0,
-			"item_group": "_Test Item Group B"
-		})
+		result = get_product_filter_data(
+			query_args={
+				"field_filters": {},
+				"attribute_filters": {},
+				"start": 0,
+				"item_group": "_Test Item Group B",
+			}
+		)
 		child_groups = [d.name for d in result.get("sub_categories")]
 
 		# check if child group is fetched if shown in website
 		self.assertIn("_Test Item Group B - 1", child_groups)
-		self.assertIn("_Test Item Group B - 2", child_groups)
\ No newline at end of file
+		self.assertIn("_Test Item Group B - 2", child_groups)
+
+	def test_item_group_page_with_descendants_included(self):
+		"""
+		Test if 'include_descendants' pulls Items belonging to descendant Item Groups (Level 2 & 3).
+		> _Test Item Group B [Level 1]
+		        > _Test Item Group B - 1 [Level 2]
+		                > _Test Item Group B - 1 - 1 [Level 3]
+		"""
+		frappe.get_doc(
+			{  # create Level 3 nested child group
+				"doctype": "Item Group",
+				"is_group": 1,
+				"item_group_name": "_Test Item Group B - 1 - 1",
+				"parent_item_group": "_Test Item Group B - 1",
+			}
+		).insert()
+
+		create_regular_web_item(  # create an item belonging to level 3 item group
+			"Test Mobile F", item_args={"item_group": "_Test Item Group B - 1 - 1"}
+		)
+
+		frappe.db.set_value("Item Group", "_Test Item Group B - 1 - 1", "show_in_website", 1)
+
+		# enable 'include descendants' in Level 1
+		frappe.db.set_value("Item Group", "_Test Item Group B", "include_descendants", 1)
+
+		result = get_product_filter_data(
+			query_args={
+				"field_filters": {},
+				"attribute_filters": {},
+				"start": 0,
+				"item_group": "_Test Item Group B",
+			}
+		)
+
+		items = result.get("items")
+		item_codes = [item.get("item_code") for item in items]
+
+		# check if all sub groups' items are pulled
+		self.assertEqual(len(items), 6)
+		self.assertIn("Test Mobile A", item_codes)
+		self.assertIn("Test Mobile C", item_codes)
+		self.assertIn("Test Mobile E", item_codes)
+		self.assertIn("Test Mobile F", item_codes)
diff --git a/erpnext/e_commerce/product_data_engine/test_product_data_engine.py b/erpnext/e_commerce/product_data_engine/test_product_data_engine.py
index 9ec336d..c3b6ed5 100644
--- a/erpnext/e_commerce/product_data_engine/test_product_data_engine.py
+++ b/erpnext/e_commerce/product_data_engine/test_product_data_engine.py
@@ -14,19 +14,20 @@
 
 test_dependencies = ["Item", "Item Group"]
 
+
 class TestProductDataEngine(unittest.TestCase):
 	"Test Products Querying and Filters for Product Listing."
 
 	@classmethod
 	def setUpClass(cls):
 		item_codes = [
-			("Test 11I Laptop", "Products"), # rank 1
-			("Test 12I Laptop", "Products"), # rank 2
-			("Test 13I Laptop", "Products"), # rank 3
-			("Test 14I Laptop", "Raw Material"), # rank 4
-			("Test 15I Laptop", "Raw Material"), # rank 5
-			("Test 16I Laptop", "Raw Material"), # rank 6
-			("Test 17I Laptop", "Products") # rank 7
+			("Test 11I Laptop", "Products"),  # rank 1
+			("Test 12I Laptop", "Products"),  # rank 2
+			("Test 13I Laptop", "Products"),  # rank 3
+			("Test 14I Laptop", "Raw Material"),  # rank 4
+			("Test 15I Laptop", "Raw Material"),  # rank 5
+			("Test 16I Laptop", "Raw Material"),  # rank 6
+			("Test 17I Laptop", "Products"),  # rank 7
 		]
 		for index, item in enumerate(item_codes, start=1):
 			item_code = item[0]
@@ -35,17 +36,19 @@
 			if not frappe.db.exists("Website Item", {"item_code": item_code}):
 				create_regular_web_item(item_code, item_args=item_args, web_args=web_args)
 
-		setup_e_commerce_settings({
-			"products_per_page": 4,
-			"enable_field_filters": 1,
-			"filter_fields": [{"fieldname": "item_group"}],
-			"enable_attribute_filters": 1,
-			"filter_attributes": [{"attribute": "Test Size"}],
-			"company": "_Test Company",
-			"enabled": 1,
-			"default_customer_group": "_Test Customer Group",
-			"price_list": "_Test Price List India"
-		})
+		setup_e_commerce_settings(
+			{
+				"products_per_page": 4,
+				"enable_field_filters": 1,
+				"filter_fields": [{"fieldname": "item_group"}],
+				"enable_attribute_filters": 1,
+				"filter_attributes": [{"attribute": "Test Size"}],
+				"company": "_Test Company",
+				"enabled": 1,
+				"default_customer_group": "_Test Customer Group",
+				"price_list": "_Test Price List India",
+			}
+		)
 		frappe.local.shopping_cart_settings = None
 
 	@classmethod
@@ -55,13 +58,7 @@
 	def test_product_list_ordering_and_paging(self):
 		"Test if website items appear by ranking on different pages."
 		engine = ProductQuery()
-		result = engine.query(
-			attributes={},
-			fields={},
-			search_term=None,
-			start=0,
-			item_group=None
-		)
+		result = engine.query(attributes={}, fields={}, search_term=None, start=0, item_group=None)
 		items = result.get("items")
 
 		self.assertIsNotNone(items)
@@ -75,13 +72,7 @@
 		self.assertEqual(items[3].get("item_code"), "Test 14I Laptop")
 
 		# check next page
-		result = engine.query(
-			attributes={},
-			fields={},
-			search_term=None,
-			start=4,
-			item_group=None
-		)
+		result = engine.query(attributes={}, fields={}, search_term=None, start=4, item_group=None)
 		items = result.get("items")
 
 		# check if items appear as per ranking set in setUpClass on next page
@@ -101,13 +92,7 @@
 		frappe.db.set_value("Website Item", {"item_code": item_code}, "ranking", 10)
 
 		engine = ProductQuery()
-		result = engine.query(
-			attributes={},
-			fields={},
-			search_term=None,
-			start=0,
-			item_group=None
-		)
+		result = engine.query(attributes={}, fields={}, search_term=None, start=0, item_group=None)
 		items = result.get("items")
 
 		# check if item is the first item on the first page
@@ -152,11 +137,7 @@
 
 		engine = ProductQuery()
 		result = engine.query(
-			attributes={},
-			fields=field_filters,
-			search_term=None,
-			start=0,
-			item_group=None
+			attributes={}, fields=field_filters, search_term=None, start=0, item_group=None
 		)
 		items = result.get("items")
 
@@ -188,11 +169,7 @@
 		attribute_filters = {"Test Size": ["Large"]}
 		engine = ProductQuery()
 		result = engine.query(
-			attributes=attribute_filters,
-			fields={},
-			search_term=None,
-			start=0,
-			item_group=None
+			attributes=attribute_filters, fields={}, search_term=None, start=0, item_group=None
 		)
 		items = result.get("items")
 
@@ -209,24 +186,13 @@
 
 		item_code = "Test 12I Laptop"
 		make_web_item_price(item_code=item_code)
-		make_web_pricing_rule(
-			title=f"Test Pricing Rule for {item_code}",
-			item_code=item_code,
-			selling=1
-		)
+		make_web_pricing_rule(title=f"Test Pricing Rule for {item_code}", item_code=item_code, selling=1)
 
 		setup_e_commerce_settings({"show_price": 1})
 		frappe.local.shopping_cart_settings = None
 
-
 		engine = ProductQuery()
-		result = engine.query(
-			attributes={},
-			fields={},
-			search_term=None,
-			start=4,
-			item_group=None
-		)
+		result = engine.query(attributes={}, fields={}, search_term=None, start=4, item_group=None)
 		self.assertTrue(bool(result.get("discounts")))
 
 		filter_engine = ProductFiltersBuilder()
@@ -247,16 +213,16 @@
 
 		make_web_item_price(item_code="Test 12I Laptop")
 		make_web_pricing_rule(
-			title="Test Pricing Rule for Test 12I Laptop", # 10% discount
+			title="Test Pricing Rule for Test 12I Laptop",  # 10% discount
 			item_code="Test 12I Laptop",
-			selling=1
+			selling=1,
 		)
 		make_web_item_price(item_code="Test 13I Laptop")
 		make_web_pricing_rule(
-			title="Test Pricing Rule for Test 13I Laptop", # 15% discount
+			title="Test Pricing Rule for Test 13I Laptop",  # 15% discount
 			item_code="Test 13I Laptop",
 			discount_percentage=15,
-			selling=1
+			selling=1,
 		)
 
 		setup_e_commerce_settings({"show_price": 1})
@@ -264,11 +230,7 @@
 
 		engine = ProductQuery()
 		result = engine.query(
-			attributes={},
-			fields=field_filters,
-			search_term=None,
-			start=0,
-			item_group=None
+			attributes={}, fields=field_filters, search_term=None, start=0, item_group=None
 		)
 		items = result.get("items")
 
@@ -282,15 +244,13 @@
 
 		create_variant_web_item()
 
-		result = get_product_filter_data(query_args={
-			"field_filters": {
-				"item_group": "Products"
-			},
-			"attribute_filters": {
-				"Test Size": ["Large"]
-			},
-			"start": 0
-		})
+		result = get_product_filter_data(
+			query_args={
+				"field_filters": {"item_group": "Products"},
+				"attribute_filters": {"Test Size": ["Large"]},
+				"start": 0,
+			}
+		)
 
 		items = result.get("items")
 
@@ -301,20 +261,13 @@
 		"Test if variants are hideen on hiding variants in settings."
 		create_variant_web_item()
 
-		setup_e_commerce_settings({
-			"enable_attribute_filters": 0,
-			"hide_variants": 1
-		})
+		setup_e_commerce_settings({"enable_attribute_filters": 0, "hide_variants": 1})
 		frappe.local.shopping_cart_settings = None
 
 		attribute_filters = {"Test Size": ["Large"]}
 		engine = ProductQuery()
 		result = engine.query(
-			attributes=attribute_filters,
-			fields={},
-			search_term=None,
-			start=0,
-			item_group=None
+			attributes=attribute_filters, fields={}, search_term=None, start=0, item_group=None
 		)
 		items = result.get("items")
 
@@ -322,10 +275,56 @@
 		self.assertEqual(len(items), 0)
 
 		# tear down
-		setup_e_commerce_settings({
-			"enable_attribute_filters": 1,
-			"hide_variants": 0
-		})
+		setup_e_commerce_settings({"enable_attribute_filters": 1, "hide_variants": 0})
+
+	def test_custom_field_as_filter(self):
+		"Test if custom field functions as filter correctly."
+		from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+
+		create_custom_field(
+			"Website Item",
+			dict(
+				owner="Administrator",
+				fieldname="supplier",
+				label="Supplier",
+				fieldtype="Link",
+				options="Supplier",
+				insert_after="on_backorder",
+			),
+		)
+
+		frappe.db.set_value(
+			"Website Item", {"item_code": "Test 11I Laptop"}, "supplier", "_Test Supplier"
+		)
+		frappe.db.set_value(
+			"Website Item", {"item_code": "Test 12I Laptop"}, "supplier", "_Test Supplier 1"
+		)
+
+		settings = frappe.get_doc("E Commerce Settings")
+		settings.append("filter_fields", {"fieldname": "supplier"})
+		settings.save()
+
+		filter_engine = ProductFiltersBuilder()
+		field_filters = filter_engine.get_field_filters()
+		custom_filter = field_filters[1]
+		filter_values = custom_filter[1]
+
+		self.assertEqual(custom_filter[0].options, "Supplier")
+		self.assertEqual(len(filter_values), 2)
+		self.assertIn("_Test Supplier", filter_values)
+
+		# test if custom filter works in query
+		field_filters = {"supplier": "_Test Supplier 1"}
+		engine = ProductQuery()
+		result = engine.query(
+			attributes={}, fields=field_filters, search_term=None, start=0, item_group=None
+		)
+		items = result.get("items")
+
+		# check if only 'Raw Material' are fetched in the right order
+		self.assertEqual(len(items), 1)
+		self.assertEqual(items[0].get("item_code"), "Test 12I Laptop")
+
 
 def create_variant_web_item():
 	"Create Variant and Template Website Items."
@@ -333,18 +332,17 @@
 	from erpnext.e_commerce.doctype.website_item.website_item import make_website_item
 	from erpnext.stock.doctype.item.test_item import make_item
 
-	make_item("Test Web Item", {
-		"has_variant": 1,
-		"variant_based_on": "Item Attribute",
-		"attributes": [
-			{
-				"attribute": "Test Size"
-			}
-		]
-	})
+	make_item(
+		"Test Web Item",
+		{
+			"has_variant": 1,
+			"variant_based_on": "Item Attribute",
+			"attributes": [{"attribute": "Test Size"}],
+		},
+	)
 	if not frappe.db.exists("Item", "Test Web Item-L"):
 		variant = create_variant("Test Web Item", {"Test Size": "Large"})
 		variant.save()
 
 	if not frappe.db.exists("Website Item", {"variant_of": "Test Web Item"}):
-		make_website_item(variant, save=True)
\ No newline at end of file
+		make_website_item(variant, save=True)
diff --git a/erpnext/e_commerce/product_ui/views.js b/erpnext/e_commerce/product_ui/views.js
index 1b5c440..fb63b21 100644
--- a/erpnext/e_commerce/product_ui/views.js
+++ b/erpnext/e_commerce/product_ui/views.js
@@ -418,6 +418,22 @@
 
 			me.change_route_with_filters();
 		});
+
+		// bind filter lookup input box
+		$('.filter-lookup-input').on('keydown', frappe.utils.debounce((e) => {
+			const $input = $(e.target);
+			const keyword = ($input.val() || '').toLowerCase();
+			const $filter_options = $input.next('.filter-options');
+
+			$filter_options.find('.filter-lookup-wrapper').show();
+			$filter_options.find('.filter-lookup-wrapper').each((i, el) => {
+				const $el = $(el);
+				const value = $el.data('value').toLowerCase();
+				if (!value.includes(keyword)) {
+					$el.hide();
+				}
+			});
+		}, 300));
 	}
 
 	change_route_with_filters() {
@@ -495,7 +511,7 @@
 
 			categories.forEach(category => {
 				sub_group_html += `
-					<a href="${ category.route || '#' }" style="text-decoration: none;">
+					<a href="/${ category.route || '#' }" style="text-decoration: none;">
 						<div class="category-pill">
 							${ category.name }
 						</div>
diff --git a/erpnext/e_commerce/redisearch_utils.py b/erpnext/e_commerce/redisearch_utils.py
index 59c7f32..1f649c7 100644
--- a/erpnext/e_commerce/redisearch_utils.py
+++ b/erpnext/e_commerce/redisearch_utils.py
@@ -1,73 +1,90 @@
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 
+import json
+
 import frappe
+from frappe import _
 from frappe.utils.redis_wrapper import RedisWrapper
+from redis import ResponseError
 from redisearch import AutoCompleter, Client, IndexDefinition, Suggestion, TagField, TextField
 
-WEBSITE_ITEM_INDEX = 'website_items_index'
-WEBSITE_ITEM_KEY_PREFIX = 'website_item:'
-WEBSITE_ITEM_NAME_AUTOCOMPLETE = 'website_items_name_dict'
-WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE = 'website_items_category_dict'
+WEBSITE_ITEM_INDEX = "website_items_index"
+WEBSITE_ITEM_KEY_PREFIX = "website_item:"
+WEBSITE_ITEM_NAME_AUTOCOMPLETE = "website_items_name_dict"
+WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE = "website_items_category_dict"
+
 
 def get_indexable_web_fields():
 	"Return valid fields from Website Item that can be searched for."
 	web_item_meta = frappe.get_meta("Website Item", cached=True)
 	valid_fields = filter(
 		lambda df: df.fieldtype in ("Link", "Table MultiSelect", "Data", "Small Text", "Text Editor"),
-		web_item_meta.fields)
+		web_item_meta.fields,
+	)
 
 	return [df.fieldname for df in valid_fields]
 
+
+def is_redisearch_enabled():
+	"Return True only if redisearch is loaded and enabled."
+	is_redisearch_enabled = frappe.db.get_single_value("E Commerce Settings", "is_redisearch_enabled")
+	return is_search_module_loaded() and is_redisearch_enabled
+
+
 def is_search_module_loaded():
 	try:
 		cache = frappe.cache()
-		out = cache.execute_command('MODULE LIST')
+		out = cache.execute_command("MODULE LIST")
 
 		parsed_output = " ".join(
-			(" ".join([s.decode() for s in o if not isinstance(s, int)]) for o in out)
+			(" ".join([frappe.as_unicode(s) for s in o if not isinstance(s, int)]) for o in out)
 		)
 		return "search" in parsed_output
 	except Exception:
-		return False
+		return False  # handling older redis versions
 
-def if_redisearch_loaded(function):
-	"Decorator to check if Redisearch is loaded."
+
+def if_redisearch_enabled(function):
+	"Decorator to check if Redisearch is enabled."
+
 	def wrapper(*args, **kwargs):
-		if is_search_module_loaded():
+		if is_redisearch_enabled():
 			func = function(*args, **kwargs)
 			return func
 		return
 
 	return wrapper
 
-def make_key(key):
-	return "{0}|{1}".format(frappe.conf.db_name, key).encode('utf-8')
 
-@if_redisearch_loaded
+def make_key(key):
+	return "{0}|{1}".format(frappe.conf.db_name, key).encode("utf-8")
+
+
+@if_redisearch_enabled
 def create_website_items_index():
 	"Creates Index Definition."
 
 	# CREATE index
 	client = Client(make_key(WEBSITE_ITEM_INDEX), conn=frappe.cache())
 
-	# DROP if already exists
 	try:
-		client.drop_index()
-	except Exception:
+		client.drop_index()  # drop if already exists
+	except ResponseError:
+		# will most likely raise a ResponseError if index does not exist
+		# ignore and create index
 		pass
+	except Exception:
+		raise_redisearch_error()
 
 	idx_def = IndexDefinition([make_key(WEBSITE_ITEM_KEY_PREFIX)])
 
-	# Based on e-commerce settings
-	idx_fields = frappe.db.get_single_value(
-		'E Commerce Settings',
-		'search_index_fields'
-	)
-	idx_fields = idx_fields.split(',') if idx_fields else []
+	# Index fields mentioned in e-commerce settings
+	idx_fields = frappe.db.get_single_value("E Commerce Settings", "search_index_fields")
+	idx_fields = idx_fields.split(",") if idx_fields else []
 
-	if 'web_item_name' in idx_fields:
-		idx_fields.remove('web_item_name')
+	if "web_item_name" in idx_fields:
+		idx_fields.remove("web_item_name")
 
 	idx_fields = list(map(to_search_field, idx_fields))
 
@@ -79,45 +96,51 @@
 	reindex_all_web_items()
 	define_autocomplete_dictionary()
 
+
 def to_search_field(field):
 	if field == "tags":
 		return TagField("tags", separator=",")
 
 	return TextField(field)
 
-@if_redisearch_loaded
+
+@if_redisearch_enabled
 def insert_item_to_index(website_item_doc):
 	# Insert item to index
 	key = get_cache_key(website_item_doc.name)
 	cache = frappe.cache()
 	web_item = create_web_item_map(website_item_doc)
 
-	for k, v in web_item.items():
-		super(RedisWrapper, cache).hset(make_key(key), k, v)
+	for field, value in web_item.items():
+		super(RedisWrapper, cache).hset(make_key(key), field, value)
 
 	insert_to_name_ac(website_item_doc.web_item_name, website_item_doc.name)
 
-@if_redisearch_loaded
+
+@if_redisearch_enabled
 def insert_to_name_ac(web_name, doc_name):
 	ac = AutoCompleter(make_key(WEBSITE_ITEM_NAME_AUTOCOMPLETE), conn=frappe.cache())
 	ac.add_suggestions(Suggestion(web_name, payload=doc_name))
 
+
 def create_web_item_map(website_item_doc):
 	fields_to_index = get_fields_indexed()
 	web_item = {}
 
-	for f in fields_to_index:
-		web_item[f] = website_item_doc.get(f) or ''
+	for field in fields_to_index:
+		web_item[field] = website_item_doc.get(field) or ""
 
 	return web_item
 
-@if_redisearch_loaded
+
+@if_redisearch_enabled
 def update_index_for_item(website_item_doc):
 	# Reinsert to Cache
 	insert_item_to_index(website_item_doc)
 	define_autocomplete_dictionary()
 
-@if_redisearch_loaded
+
+@if_redisearch_enabled
 def delete_item_from_index(website_item_doc):
 	cache = frappe.cache()
 	key = get_cache_key(website_item_doc.name)
@@ -125,86 +148,106 @@
 	try:
 		cache.delete(key)
 	except Exception:
-		return False
+		raise_redisearch_error()
 
 	delete_from_ac_dict(website_item_doc)
 	return True
 
-@if_redisearch_loaded
+
+@if_redisearch_enabled
 def delete_from_ac_dict(website_item_doc):
-	'''Removes this items's name from autocomplete dictionary'''
+	"""Removes this items's name from autocomplete dictionary"""
 	cache = frappe.cache()
 	name_ac = AutoCompleter(make_key(WEBSITE_ITEM_NAME_AUTOCOMPLETE), conn=cache)
 	name_ac.delete(website_item_doc.web_item_name)
 
-@if_redisearch_loaded
+
+@if_redisearch_enabled
 def define_autocomplete_dictionary():
-	"""Creates an autocomplete search dictionary for `name`.
-		Also creats autocomplete dictionary for `categories` if
-		checked in E Commerce Settings"""
+	"""
+	Defines/Redefines an autocomplete search dictionary for Website Item Name.
+	Also creats autocomplete dictionary for Published Item Groups.
+	"""
 
 	cache = frappe.cache()
-	name_ac = AutoCompleter(make_key(WEBSITE_ITEM_NAME_AUTOCOMPLETE), conn=cache)
-	cat_ac = AutoCompleter(make_key(WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE), conn=cache)
-
-	ac_categories = frappe.db.get_single_value(
-		'E Commerce Settings',
-		'show_categories_in_search_autocomplete'
-	)
+	item_ac = AutoCompleter(make_key(WEBSITE_ITEM_NAME_AUTOCOMPLETE), conn=cache)
+	item_group_ac = AutoCompleter(make_key(WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE), conn=cache)
 
 	# Delete both autocomplete dicts
 	try:
 		cache.delete(make_key(WEBSITE_ITEM_NAME_AUTOCOMPLETE))
 		cache.delete(make_key(WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE))
 	except Exception:
-		return False
+		raise_redisearch_error()
 
+	create_items_autocomplete_dict(autocompleter=item_ac)
+	create_item_groups_autocomplete_dict(autocompleter=item_group_ac)
+
+
+@if_redisearch_enabled
+def create_items_autocomplete_dict(autocompleter):
+	"Add items as suggestions in Autocompleter."
 	items = frappe.get_all(
-		'Website Item',
-		fields=['web_item_name', 'item_group'],
-		filters={"published": 1}
+		"Website Item", fields=["web_item_name", "item_group"], filters={"published": 1}
 	)
 
 	for item in items:
-		name_ac.add_suggestions(Suggestion(item.web_item_name))
-		if ac_categories and item.item_group:
-			cat_ac.add_suggestions(Suggestion(item.item_group))
+		autocompleter.add_suggestions(Suggestion(item.web_item_name))
 
-	return True
 
-@if_redisearch_loaded
-def reindex_all_web_items():
-	items = frappe.get_all(
-		'Website Item',
-		fields=get_fields_indexed(),
-		filters={"published": True}
+@if_redisearch_enabled
+def create_item_groups_autocomplete_dict(autocompleter):
+	"Add item groups with weightage as suggestions in Autocompleter."
+	published_item_groups = frappe.get_all(
+		"Item Group", fields=["name", "route", "weightage"], filters={"show_in_website": 1}
 	)
+	if not published_item_groups:
+		return
+
+	for item_group in published_item_groups:
+		payload = json.dumps({"name": item_group.name, "route": item_group.route})
+		autocompleter.add_suggestions(
+			Suggestion(
+				string=item_group.name,
+				score=frappe.utils.flt(item_group.weightage) or 1.0,
+				payload=payload,  # additional info that can be retrieved later
+			)
+		)
+
+
+@if_redisearch_enabled
+def reindex_all_web_items():
+	items = frappe.get_all("Website Item", fields=get_fields_indexed(), filters={"published": True})
 
 	cache = frappe.cache()
 	for item in items:
 		web_item = create_web_item_map(item)
 		key = make_key(get_cache_key(item.name))
 
-		for k, v in web_item.items():
-			super(RedisWrapper, cache).hset(key, k, v)
+		for field, value in web_item.items():
+			super(RedisWrapper, cache).hset(key, field, value)
+
 
 def get_cache_key(name):
 	name = frappe.scrub(name)
 	return f"{WEBSITE_ITEM_KEY_PREFIX}{name}"
 
-def get_fields_indexed():
-	fields_to_index = frappe.db.get_single_value(
-		'E Commerce Settings',
-		'search_index_fields'
-	)
-	fields_to_index = fields_to_index.split(',') if fields_to_index else []
 
-	mandatory_fields = ['name', 'web_item_name', 'route', 'thumbnail', 'ranking']
+def get_fields_indexed():
+	fields_to_index = frappe.db.get_single_value("E Commerce Settings", "search_index_fields")
+	fields_to_index = fields_to_index.split(",") if fields_to_index else []
+
+	mandatory_fields = ["name", "web_item_name", "route", "thumbnail", "ranking"]
 	fields_to_index = fields_to_index + mandatory_fields
 
 	return fields_to_index
 
-# TODO: Remove later
-# # Figure out a way to run this at startup
-define_autocomplete_dictionary()
-create_website_items_index()
+
+def raise_redisearch_error():
+	"Create an Error Log and raise error."
+	log = frappe.log_error("Redisearch Error")
+	log_link = frappe.utils.get_link_to_form("Error Log", log.name)
+
+	frappe.throw(
+		msg=_("Something went wrong. Check {0}").format(log_link), title=_("Redisearch Error")
+	)
diff --git a/erpnext/e_commerce/shopping_cart/cart.py b/erpnext/e_commerce/shopping_cart/cart.py
index 372aed0..4c82393 100644
--- a/erpnext/e_commerce/shopping_cart/cart.py
+++ b/erpnext/e_commerce/shopping_cart/cart.py
@@ -19,6 +19,7 @@
 class WebsitePriceListMissingError(frappe.ValidationError):
 	pass
 
+
 def set_cart_count(quotation=None):
 	if cint(frappe.db.get_singles_value("E Commerce Settings", "enabled")):
 		if not quotation:
@@ -28,6 +29,7 @@
 		if hasattr(frappe.local, "cookie_manager"):
 			frappe.local.cookie_manager.set_cookie("cart_count", cart_count)
 
+
 @frappe.whitelist()
 def get_cart_quotation(doc=None):
 	party = get_party()
@@ -47,38 +49,46 @@
 		"shipping_addresses": get_shipping_addresses(party),
 		"billing_addresses": get_billing_addresses(party),
 		"shipping_rules": get_applicable_shipping_rules(party),
-		"cart_settings": frappe.get_cached_doc("E Commerce Settings")
+		"cart_settings": frappe.get_cached_doc("E Commerce Settings"),
 	}
 
+
 @frappe.whitelist()
 def get_shipping_addresses(party=None):
 	if not party:
 		party = get_party()
 	addresses = get_address_docs(party=party)
-	return [{"name": address.name, "title": address.address_title, "display": address.display}
-		for address in addresses if address.address_type == "Shipping"
+	return [
+		{"name": address.name, "title": address.address_title, "display": address.display}
+		for address in addresses
+		if address.address_type == "Shipping"
 	]
 
+
 @frappe.whitelist()
 def get_billing_addresses(party=None):
 	if not party:
 		party = get_party()
 	addresses = get_address_docs(party=party)
-	return [{"name": address.name, "title": address.address_title, "display": address.display}
-		for address in addresses if address.address_type == "Billing"
+	return [
+		{"name": address.name, "title": address.address_title, "display": address.display}
+		for address in addresses
+		if address.address_type == "Billing"
 	]
 
+
 @frappe.whitelist()
 def place_order():
 	quotation = _get_cart_quotation()
-	cart_settings = frappe.db.get_value("E Commerce Settings", None,
-		["company", "allow_items_not_in_stock"], as_dict=1)
+	cart_settings = frappe.db.get_value(
+		"E Commerce Settings", None, ["company", "allow_items_not_in_stock"], as_dict=1
+	)
 	quotation.company = cart_settings.company
 
 	quotation.flags.ignore_permissions = True
 	quotation.submit()
 
-	if quotation.quotation_to == 'Lead' and quotation.party_name:
+	if quotation.quotation_to == "Lead" and quotation.party_name:
 		# company used to create customer accounts
 		frappe.defaults.set_user_default("company", quotation.company)
 
@@ -86,17 +96,14 @@
 		frappe.throw(_("Set Shipping Address or Billing Address"))
 
 	from erpnext.selling.doctype.quotation.quotation import _make_sales_order
+
 	sales_order = frappe.get_doc(_make_sales_order(quotation.name, ignore_permissions=True))
 	sales_order.payment_schedule = []
 
 	if not cint(cart_settings.allow_items_not_in_stock):
 		for item in sales_order.get("items"):
 			item.warehouse = frappe.db.get_value(
-				"Website Item",
-				{
-					"item_code": item.item_code
-				},
-				"website_warehouse"
+				"Website Item", {"item_code": item.item_code}, "website_warehouse"
 			)
 			is_stock_item = frappe.db.get_value("Item", item.item_code, "is_stock_item")
 
@@ -116,13 +123,19 @@
 
 	return sales_order.name
 
+
 @frappe.whitelist()
 def request_for_quotation():
 	quotation = _get_cart_quotation()
 	quotation.flags.ignore_permissions = True
-	quotation.submit()
+
+	if get_shopping_cart_settings().save_quotations_as_draft:
+		quotation.save()
+	else:
+		quotation.submit()
 	return quotation.name
 
+
 @frappe.whitelist()
 def update_cart(item_code, qty, additional_notes=None, with_items=False):
 	quotation = _get_cart_quotation()
@@ -139,12 +152,15 @@
 	else:
 		quotation_items = quotation.get("items", {"item_code": item_code})
 		if not quotation_items:
-			quotation.append("items", {
-				"doctype": "Quotation Item",
-				"item_code": item_code,
-				"qty": qty,
-				"additional_notes": additional_notes
-			})
+			quotation.append(
+				"items",
+				{
+					"doctype": "Quotation Item",
+					"item_code": item_code,
+					"qty": qty,
+					"additional_notes": additional_notes,
+				},
+			)
 		else:
 			quotation_items[0].qty = qty
 			quotation_items[0].additional_notes = additional_notes
@@ -164,73 +180,75 @@
 	if cint(with_items):
 		context = get_cart_quotation(quotation)
 		return {
-			"items": frappe.render_template("templates/includes/cart/cart_items.html",
-				context),
-			"total": frappe.render_template("templates/includes/cart/cart_items_total.html",
-				context),
-			"taxes_and_totals": frappe.render_template("templates/includes/cart/cart_payment_summary.html",
-				context)
+			"items": frappe.render_template("templates/includes/cart/cart_items.html", context),
+			"total": frappe.render_template("templates/includes/cart/cart_items_total.html", context),
+			"taxes_and_totals": frappe.render_template(
+				"templates/includes/cart/cart_payment_summary.html", context
+			),
 		}
 	else:
-		return {
-			'name': quotation.name
-		}
+		return {"name": quotation.name}
+
 
 @frappe.whitelist()
 def get_shopping_cart_menu(context=None):
 	if not context:
 		context = get_cart_quotation()
 
-	return frappe.render_template('templates/includes/cart/cart_dropdown.html', context)
+	return frappe.render_template("templates/includes/cart/cart_dropdown.html", context)
 
 
 @frappe.whitelist()
 def add_new_address(doc):
 	doc = frappe.parse_json(doc)
-	doc.update({
-		'doctype': 'Address'
-	})
+	doc.update({"doctype": "Address"})
 	address = frappe.get_doc(doc)
 	address.save(ignore_permissions=True)
 
 	return address
 
+
 @frappe.whitelist(allow_guest=True)
 def create_lead_for_item_inquiry(lead, subject, message):
 	lead = frappe.parse_json(lead)
-	lead_doc = frappe.new_doc('Lead')
+	lead_doc = frappe.new_doc("Lead")
 	for fieldname in ("lead_name", "company_name", "email_id", "phone"):
 		lead_doc.set(fieldname, lead.get(fieldname))
 
-	lead_doc.set('lead_owner', '')
+	lead_doc.set("lead_owner", "")
 
-	if not frappe.db.exists('Lead Source', 'Product Inquiry'):
-		frappe.get_doc({
-			'doctype': 'Lead Source',
-			'source_name' : 'Product Inquiry'
-		}).insert(ignore_permissions=True)
+	if not frappe.db.exists("Lead Source", "Product Inquiry"):
+		frappe.get_doc({"doctype": "Lead Source", "source_name": "Product Inquiry"}).insert(
+			ignore_permissions=True
+		)
 
-	lead_doc.set('source', 'Product Inquiry')
+	lead_doc.set("source", "Product Inquiry")
 
 	try:
 		lead_doc.save(ignore_permissions=True)
 	except frappe.exceptions.DuplicateEntryError:
 		frappe.clear_messages()
-		lead_doc = frappe.get_doc('Lead', {'email_id': lead['email_id']})
+		lead_doc = frappe.get_doc("Lead", {"email_id": lead["email_id"]})
 
-	lead_doc.add_comment('Comment', text='''
+	lead_doc.add_comment(
+		"Comment",
+		text="""
 		<div>
 			<h5>{subject}</h5>
 			<p>{message}</p>
 		</div>
-	'''.format(subject=subject, message=message))
+	""".format(
+			subject=subject, message=message
+		),
+	)
 
 	return lead_doc
 
 
 @frappe.whitelist()
 def get_terms_and_conditions(terms_name):
-	return frappe.db.get_value('Terms and Conditions', terms_name, 'terms')
+	return frappe.db.get_value("Terms and Conditions", terms_name, "terms")
+
 
 @frappe.whitelist()
 def update_cart_address(address_type, address_name):
@@ -247,31 +265,35 @@
 		quotation.shipping_address_name = address_name
 		quotation.shipping_address = address_display
 		quotation.customer_address = quotation.customer_address or address_name
-		address_doc = next((doc for doc in get_shipping_addresses() if doc["name"] == address_name), None)
+		address_doc = next(
+			(doc for doc in get_shipping_addresses() if doc["name"] == address_name), None
+		)
 	apply_cart_settings(quotation=quotation)
 
 	quotation.flags.ignore_permissions = True
 	quotation.save()
 
 	context = get_cart_quotation(quotation)
-	context['address'] = address_doc
+	context["address"] = address_doc
 
 	return {
-		"taxes": frappe.render_template("templates/includes/order/order_taxes.html",
-			context),
-		"address": frappe.render_template("templates/includes/cart/address_card.html",
-			context)
+		"taxes": frappe.render_template("templates/includes/order/order_taxes.html", context),
+		"address": frappe.render_template("templates/includes/cart/address_card.html", context),
 	}
 
+
 def guess_territory():
 	territory = None
 	geoip_country = frappe.session.get("session_country")
 	if geoip_country:
 		territory = frappe.db.get_value("Territory", geoip_country)
 
-	return territory or \
-		frappe.db.get_value("E Commerce Settings", None, "territory") or \
-			get_root_of("Territory")
+	return (
+		territory
+		or frappe.db.get_value("E Commerce Settings", None, "territory")
+		or get_root_of("Territory")
+	)
+
 
 def decorate_quotation_doc(doc):
 	for d in doc.get("items", []):
@@ -284,50 +306,56 @@
 				"Item",
 				filters={"item_code": item_code},
 				fieldname=["variant_of", "item_name", "image"],
-				as_dict=True
+				as_dict=True,
 			)[0]
 			item_code = variant_data.variant_of
 			fields = fields[1:]
 			d.web_item_name = variant_data.item_name
 
-			if variant_data.image: # get image from variant or template web item
+			if variant_data.image:  # get image from variant or template web item
 				d.thumbnail = variant_data.image
 				fields = fields[2:]
 
-		d.update(frappe.db.get_value(
-			"Website Item",
-			{"item_code": item_code},
-			fields,
-			as_dict=True)
-		)
+		d.update(frappe.db.get_value("Website Item", {"item_code": item_code}, fields, as_dict=True))
 
 	return doc
 
 
 def _get_cart_quotation(party=None):
-	'''Return the open Quotation of type "Shopping Cart" or make a new one'''
+	"""Return the open Quotation of type "Shopping Cart" or make a new one"""
 	if not party:
 		party = get_party()
 
-	quotation = frappe.get_all("Quotation", fields=["name"], filters=
-		{"party_name": party.name, "contact_email": frappe.session.user, "order_type": "Shopping Cart", "docstatus": 0},
-		order_by="modified desc", limit_page_length=1)
+	quotation = frappe.get_all(
+		"Quotation",
+		fields=["name"],
+		filters={
+			"party_name": party.name,
+			"contact_email": frappe.session.user,
+			"order_type": "Shopping Cart",
+			"docstatus": 0,
+		},
+		order_by="modified desc",
+		limit_page_length=1,
+	)
 
 	if quotation:
 		qdoc = frappe.get_doc("Quotation", quotation[0].name)
 	else:
 		company = frappe.db.get_value("E Commerce Settings", None, ["company"])
-		qdoc = frappe.get_doc({
-			"doctype": "Quotation",
-			"naming_series": get_shopping_cart_settings().quotation_series or "QTN-CART-",
-			"quotation_to": party.doctype,
-			"company": company,
-			"order_type": "Shopping Cart",
-			"status": "Draft",
-			"docstatus": 0,
-			"__islocal": 1,
-			"party_name": party.name
-		})
+		qdoc = frappe.get_doc(
+			{
+				"doctype": "Quotation",
+				"naming_series": get_shopping_cart_settings().quotation_series or "QTN-CART-",
+				"quotation_to": party.doctype,
+				"company": company,
+				"order_type": "Shopping Cart",
+				"status": "Draft",
+				"docstatus": 0,
+				"__islocal": 1,
+				"party_name": party.name,
+			}
+		)
 
 		qdoc.contact_person = frappe.db.get_value("Contact", {"email_id": frappe.session.user})
 		qdoc.contact_email = frappe.session.user
@@ -338,6 +366,7 @@
 
 	return qdoc
 
+
 def update_party(fullname, company_name=None, mobile_no=None, phone=None):
 	party = get_party()
 
@@ -365,6 +394,7 @@
 		qdoc.flags.ignore_permissions = True
 		qdoc.save()
 
+
 def apply_cart_settings(party=None, quotation=None):
 	if not party:
 		party = get_party()
@@ -381,14 +411,16 @@
 
 	_apply_shipping_rule(party, quotation, cart_settings)
 
+
 def set_price_list_and_rate(quotation, cart_settings):
 	"""set price list based on billing territory"""
 
 	_set_price_list(cart_settings, quotation)
 
 	# reset values
-	quotation.price_list_currency = quotation.currency = \
-		quotation.plc_conversion_rate = quotation.conversion_rate = None
+	quotation.price_list_currency = (
+		quotation.currency
+	) = quotation.plc_conversion_rate = quotation.conversion_rate = None
 	for item in quotation.get("items"):
 		item.price_list_rate = item.discount_percentage = item.rate = item.amount = None
 
@@ -399,9 +431,11 @@
 		# set it in cookies for using in product page
 		frappe.local.cookie_manager.set_cookie("selling_price_list", quotation.selling_price_list)
 
+
 def _set_price_list(cart_settings, quotation=None):
 	"""Set price list based on customer or shopping cart default"""
 	from erpnext.accounts.party import get_default_price_list
+
 	party_name = quotation.get("party_name") if quotation else get_party().get("name")
 	selling_price_list = None
 
@@ -418,23 +452,33 @@
 
 	return selling_price_list
 
+
 def set_taxes(quotation, cart_settings):
 	"""set taxes based on billing territory"""
 	from erpnext.accounts.party import set_taxes
 
 	customer_group = frappe.db.get_value("Customer", quotation.party_name, "customer_group")
 
-	quotation.taxes_and_charges = set_taxes(quotation.party_name, "Customer",
-		quotation.transaction_date, quotation.company, customer_group=customer_group, supplier_group=None,
-		tax_category=quotation.tax_category, billing_address=quotation.customer_address,
-		shipping_address=quotation.shipping_address_name, use_for_shopping_cart=1)
-#
-# 	# clear table
+	quotation.taxes_and_charges = set_taxes(
+		quotation.party_name,
+		"Customer",
+		quotation.transaction_date,
+		quotation.company,
+		customer_group=customer_group,
+		supplier_group=None,
+		tax_category=quotation.tax_category,
+		billing_address=quotation.customer_address,
+		shipping_address=quotation.shipping_address_name,
+		use_for_shopping_cart=1,
+	)
+	#
+	# 	# clear table
 	quotation.set("taxes", [])
-#
-# 	# append taxes
+	#
+	# 	# append taxes
 	quotation.append_taxes_from_master()
 
+
 def get_party(user=None):
 	if not user:
 		user = frappe.session.user
@@ -443,14 +487,14 @@
 	party = None
 
 	if contact_name:
-		contact = frappe.get_doc('Contact', contact_name)
+		contact = frappe.get_doc("Contact", contact_name)
 		if contact.links:
 			party_doctype = contact.links[0].link_doctype
 			party = contact.links[0].link_name
 
 	cart_settings = frappe.get_doc("E Commerce Settings")
 
-	debtors_account = ''
+	debtors_account = ""
 
 	if cart_settings.enable_checkout:
 		debtors_account = get_debtors_account(cart_settings)
@@ -464,57 +508,62 @@
 			raise frappe.Redirect
 		customer = frappe.new_doc("Customer")
 		fullname = get_fullname(user)
-		customer.update({
-			"customer_name": fullname,
-			"customer_type": "Individual",
-			"customer_group": get_shopping_cart_settings().default_customer_group,
-			"territory": get_root_of("Territory")
-		})
+		customer.update(
+			{
+				"customer_name": fullname,
+				"customer_type": "Individual",
+				"customer_group": get_shopping_cart_settings().default_customer_group,
+				"territory": get_root_of("Territory"),
+			}
+		)
 
 		if debtors_account:
-			customer.update({
-				"accounts": [{
-					"company": cart_settings.company,
-					"account": debtors_account
-				}]
-			})
+			customer.update({"accounts": [{"company": cart_settings.company, "account": debtors_account}]})
 
 		customer.flags.ignore_mandatory = True
 		customer.insert(ignore_permissions=True)
 
 		contact = frappe.new_doc("Contact")
-		contact.update({
-			"first_name": fullname,
-			"email_ids": [{"email_id": user, "is_primary": 1}]
-		})
-		contact.append('links', dict(link_doctype='Customer', link_name=customer.name))
+		contact.update({"first_name": fullname, "email_ids": [{"email_id": user, "is_primary": 1}]})
+		contact.append("links", dict(link_doctype="Customer", link_name=customer.name))
 		contact.flags.ignore_mandatory = True
 		contact.insert(ignore_permissions=True)
 
 		return customer
 
+
 def get_debtors_account(cart_settings):
 	if not cart_settings.payment_gateway_account:
 		frappe.throw(_("Payment Gateway Account not set"), _("Mandatory"))
 
-	payment_gateway_account_currency = \
-		frappe.get_doc("Payment Gateway Account", cart_settings.payment_gateway_account).currency
+	payment_gateway_account_currency = frappe.get_doc(
+		"Payment Gateway Account", cart_settings.payment_gateway_account
+	).currency
 
 	account_name = _("Debtors ({0})").format(payment_gateway_account_currency)
 
-	debtors_account_name = get_account_name("Receivable", "Asset", is_group=0,\
-		account_currency=payment_gateway_account_currency, company=cart_settings.company)
+	debtors_account_name = get_account_name(
+		"Receivable",
+		"Asset",
+		is_group=0,
+		account_currency=payment_gateway_account_currency,
+		company=cart_settings.company,
+	)
 
 	if not debtors_account_name:
-		debtors_account = frappe.get_doc({
-			"doctype": "Account",
-			"account_type": "Receivable",
-			"root_type": "Asset",
-			"is_group": 0,
-			"parent_account": get_account_name(root_type="Asset", is_group=1, company=cart_settings.company),
-			"account_name": account_name,
-			"currency": payment_gateway_account_currency
-		}).insert(ignore_permissions=True)
+		debtors_account = frappe.get_doc(
+			{
+				"doctype": "Account",
+				"account_type": "Receivable",
+				"root_type": "Asset",
+				"is_group": 0,
+				"parent_account": get_account_name(
+					root_type="Asset", is_group=1, company=cart_settings.company
+				),
+				"account_name": account_name,
+				"currency": payment_gateway_account_currency,
+			}
+		).insert(ignore_permissions=True)
 
 		return debtors_account.name
 
@@ -522,26 +571,31 @@
 		return debtors_account_name
 
 
-def get_address_docs(doctype=None, txt=None, filters=None, limit_start=0, limit_page_length=20,
-	party=None):
+def get_address_docs(
+	doctype=None, txt=None, filters=None, limit_start=0, limit_page_length=20, party=None
+):
 	if not party:
 		party = get_party()
 
 	if not party:
 		return []
 
-	address_names = frappe.db.get_all('Dynamic Link', fields=('parent'),
-		filters=dict(parenttype='Address', link_doctype=party.doctype, link_name=party.name))
+	address_names = frappe.db.get_all(
+		"Dynamic Link",
+		fields=("parent"),
+		filters=dict(parenttype="Address", link_doctype=party.doctype, link_name=party.name),
+	)
 
 	out = []
 
 	for a in address_names:
-		address = frappe.get_doc('Address', a.parent)
+		address = frappe.get_doc("Address", a.parent)
 		address.display = get_address_display(address.as_dict())
 		out.append(address)
 
 	return out
 
+
 @frappe.whitelist()
 def apply_shipping_rule(shipping_rule):
 	quotation = _get_cart_quotation()
@@ -555,6 +609,7 @@
 
 	return get_cart_quotation(quotation)
 
+
 def _apply_shipping_rule(party=None, quotation=None, cart_settings=None):
 	if not quotation.shipping_rule:
 		shipping_rules = get_shipping_rules(quotation, cart_settings)
@@ -569,6 +624,7 @@
 		quotation.run_method("apply_shipping_rule")
 		quotation.run_method("calculate_taxes_and_totals")
 
+
 def get_applicable_shipping_rules(party=None, quotation=None):
 	shipping_rules = get_shipping_rules(quotation)
 
@@ -577,6 +633,7 @@
 		# we need this in sorted order as per the position of the rule in the settings page
 		return [[rule, rule] for rule in shipping_rules]
 
+
 def get_shipping_rules(quotation=None, cart_settings=None):
 	if not quotation:
 		quotation = _get_cart_quotation()
@@ -589,26 +646,24 @@
 			sr = frappe.qb.DocType("Shipping Rule")
 			query = (
 				frappe.qb.from_(sr_country)
-				.join(sr).on(sr.name == sr_country.parent)
+				.join(sr)
+				.on(sr.name == sr_country.parent)
 				.select(sr.name)
 				.distinct()
-				.where(
-					(sr_country.country == country)
-					& (sr.disabled != 1)
-				)
+				.where((sr_country.country == country) & (sr.disabled != 1))
 			)
 			result = query.run(as_list=True)
 			shipping_rules = [x[0] for x in result]
 
 	return shipping_rules
 
+
 def get_address_territory(address_name):
 	"""Tries to match city, state and country of address to existing territory"""
 	territory = None
 
 	if address_name:
-		address_fields = frappe.db.get_value("Address", address_name,
-			["city", "state", "country"])
+		address_fields = frappe.db.get_value("Address", address_name, ["city", "state", "country"])
 		for value in address_fields:
 			territory = frappe.db.get_value("Territory", value)
 			if territory:
@@ -616,9 +671,11 @@
 
 	return territory
 
+
 def show_terms(doc):
 	return doc.tc_name
 
+
 @frappe.whitelist(allow_guest=True)
 def apply_coupon_code(applied_code, applied_referral_sales_partner):
 	quotation = True
@@ -626,13 +683,14 @@
 	if not applied_code:
 		frappe.throw(_("Please enter a coupon code"))
 
-	coupon_list = frappe.get_all('Coupon Code', filters={'coupon_code': applied_code})
+	coupon_list = frappe.get_all("Coupon Code", filters={"coupon_code": applied_code})
 	if not coupon_list:
 		frappe.throw(_("Please enter a valid coupon code"))
 
 	coupon_name = coupon_list[0].name
 
 	from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code
+
 	validate_coupon_code(coupon_name)
 	quotation = _get_cart_quotation()
 	quotation.coupon_code = coupon_name
@@ -640,7 +698,9 @@
 	quotation.save()
 
 	if applied_referral_sales_partner:
-		sales_partner_list = frappe.get_all('Sales Partner', filters={'referral_code': applied_referral_sales_partner})
+		sales_partner_list = frappe.get_all(
+			"Sales Partner", filters={"referral_code": applied_referral_sales_partner}
+		)
 		if sales_partner_list:
 			sales_partner_name = sales_partner_list[0].name
 			quotation.referral_sales_partner = sales_partner_name
diff --git a/erpnext/e_commerce/shopping_cart/product_info.py b/erpnext/e_commerce/shopping_cart/product_info.py
index 595fed0..0248ca7 100644
--- a/erpnext/e_commerce/shopping_cart/product_info.py
+++ b/erpnext/e_commerce/shopping_cart/product_info.py
@@ -22,16 +22,17 @@
 	cart_settings = get_shopping_cart_settings()
 	if not cart_settings.enabled:
 		# return settings even if cart is disabled
-		return frappe._dict({
-			"product_info": {},
-			"cart_settings": cart_settings
-		})
+		return frappe._dict({"product_info": {}, "cart_settings": cart_settings})
 
 	cart_quotation = frappe._dict()
 	if not skip_quotation_creation:
 		cart_quotation = _get_cart_quotation()
 
-	selling_price_list = cart_quotation.get("selling_price_list") if cart_quotation else _set_price_list(cart_settings, None)
+	selling_price_list = (
+		cart_quotation.get("selling_price_list")
+		if cart_quotation
+		else _set_price_list(cart_settings, None)
+	)
 
 	price = {}
 	if cart_settings.show_price:
@@ -40,10 +41,7 @@
 		# If not logged in, check if price is hidden for guest.
 		if not is_guest or not cart_settings.hide_price_for_guest:
 			price = get_price(
-				item_code,
-				selling_price_list,
-				cart_settings.default_customer_group,
-				cart_settings.company
+				item_code, selling_price_list, cart_settings.default_customer_group, cart_settings.company
 			)
 
 	stock_status = None
@@ -59,7 +57,7 @@
 		"price": price,
 		"qty": 0,
 		"uom": frappe.db.get_value("Item", item_code, "stock_uom"),
-		"sales_uom": frappe.db.get_value("Item", item_code, "sales_uom")
+		"sales_uom": frappe.db.get_value("Item", item_code, "sales_uom"),
 	}
 
 	if stock_status:
@@ -67,7 +65,11 @@
 			product_info["on_backorder"] = True
 		else:
 			product_info["stock_qty"] = stock_status.stock_qty
-			product_info["in_stock"] = stock_status.in_stock if stock_status.is_stock_item else get_non_stock_item_status(item_code, "website_warehouse")
+			product_info["in_stock"] = (
+				stock_status.in_stock
+				if stock_status.is_stock_item
+				else get_non_stock_item_status(item_code, "website_warehouse")
+			)
 			product_info["show_stock_qty"] = show_quantity_in_website()
 
 	if product_info["price"]:
@@ -76,14 +78,14 @@
 			if item:
 				product_info["qty"] = item[0].qty
 
-	return frappe._dict({
-		"product_info": product_info,
-		"cart_settings": cart_settings
-	})
+	return frappe._dict({"product_info": product_info, "cart_settings": cart_settings})
+
 
 def set_product_info_for_website(item):
 	"""set product price uom for website"""
-	product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get("product_info")
+	product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get(
+		"product_info"
+	)
 
 	if product_info:
 		item.update(product_info)
diff --git a/erpnext/e_commerce/shopping_cart/test_shopping_cart.py b/erpnext/e_commerce/shopping_cart/test_shopping_cart.py
index 9c389d0..f44f8fe 100644
--- a/erpnext/e_commerce/shopping_cart/test_shopping_cart.py
+++ b/erpnext/e_commerce/shopping_cart/test_shopping_cart.py
@@ -6,7 +6,7 @@
 
 import frappe
 from frappe.tests.utils import change_settings
-from frappe.utils import add_months, nowdate
+from frappe.utils import add_months, cint, nowdate
 
 from erpnext.accounts.doctype.tax_rule.tax_rule import ConflictingTaxRule
 from erpnext.e_commerce.doctype.website_item.website_item import make_website_item
@@ -14,45 +14,47 @@
 	_get_cart_quotation,
 	get_cart_quotation,
 	get_party,
+	request_for_quotation,
 	update_cart,
 )
 from erpnext.tests.utils import create_test_contact_and_address
 
-# test_dependencies = ['Payment Terms Template']
 
 class TestShoppingCart(unittest.TestCase):
 	"""
-		Note:
-		Shopping Cart == Quotation
+	Note:
+	Shopping Cart == Quotation
 	"""
 
-	@classmethod
-	def tearDownClass(cls):
-		frappe.db.sql("delete from `tabTax Rule`")
-
 	def setUp(self):
 		frappe.set_user("Administrator")
 		create_test_contact_and_address()
 		self.enable_shopping_cart()
 		if not frappe.db.exists("Website Item", {"item_code": "_Test Item"}):
-			make_website_item(frappe.get_cached_doc("Item",  "_Test Item"))
+			make_website_item(frappe.get_cached_doc("Item", "_Test Item"))
 
 		if not frappe.db.exists("Website Item", {"item_code": "_Test Item 2"}):
-			make_website_item(frappe.get_cached_doc("Item",  "_Test Item 2"))
+			make_website_item(frappe.get_cached_doc("Item", "_Test Item 2"))
 
 	def tearDown(self):
 		frappe.db.rollback()
 		frappe.set_user("Administrator")
 		self.disable_shopping_cart()
 
+	@classmethod
+	def tearDownClass(cls):
+		frappe.db.sql("delete from `tabTax Rule`")
+
 	def test_get_cart_new_user(self):
 		self.login_as_new_user()
 
 		# test if lead is created and quotation with new lead is fetched
 		quotation = _get_cart_quotation()
 		self.assertEqual(quotation.quotation_to, "Customer")
-		self.assertEqual(quotation.contact_person,
-			frappe.db.get_value("Contact", dict(email_id="test_cart_user@example.com")))
+		self.assertEqual(
+			quotation.contact_person,
+			frappe.db.get_value("Contact", dict(email_id="test_cart_user@example.com")),
+		)
 		self.assertEqual(quotation.contact_email, frappe.session.user)
 
 		return quotation
@@ -66,7 +68,9 @@
 			self.assertEqual(quotation.contact_email, frappe.session.user)
 			return quotation
 
-		self.login_as_customer("test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer")
+		self.login_as_customer(
+			"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
+		)
 		validate_quotation()
 
 		self.login_as_customer()
@@ -125,6 +129,7 @@
 		self.assertEqual(quotation.net_total, 20)
 		self.assertEqual(len(quotation.get("items")), 1)
 
+	@unittest.skip("Flaky in CI")
 	def test_tax_rule(self):
 		self.create_tax_rule()
 		self.login_as_customer()
@@ -132,46 +137,55 @@
 
 		from erpnext.accounts.party import set_taxes
 
-		tax_rule_master = set_taxes(quotation.party_name, "Customer",
-			quotation.transaction_date, quotation.company, customer_group=None, supplier_group=None,
-			tax_category=quotation.tax_category, billing_address=quotation.customer_address,
-			shipping_address=quotation.shipping_address_name, use_for_shopping_cart=1)
+		tax_rule_master = set_taxes(
+			quotation.party_name,
+			"Customer",
+			None,
+			quotation.company,
+			customer_group=None,
+			supplier_group=None,
+			tax_category=quotation.tax_category,
+			billing_address=quotation.customer_address,
+			shipping_address=quotation.shipping_address_name,
+			use_for_shopping_cart=1,
+		)
 
 		self.assertEqual(quotation.taxes_and_charges, tax_rule_master)
 		self.assertEqual(quotation.total_taxes_and_charges, 1000.0)
 
 		self.remove_test_quotation(quotation)
 
-	@change_settings("E Commerce Settings",{
-		"company": "_Test Company",
-		"enabled": 1,
-		"default_customer_group": "_Test Customer Group",
-		"price_list": "_Test Price List India",
-		"show_price": 1
-	})
+	@change_settings(
+		"E Commerce Settings",
+		{
+			"company": "_Test Company",
+			"enabled": 1,
+			"default_customer_group": "_Test Customer Group",
+			"price_list": "_Test Price List India",
+			"show_price": 1,
+		},
+	)
 	def test_add_item_variant_without_web_item_to_cart(self):
 		"Test adding Variants having no Website Items in cart via Template Web Item."
 		from erpnext.controllers.item_variant import create_variant
 		from erpnext.e_commerce.doctype.website_item.website_item import make_website_item
 		from erpnext.stock.doctype.item.test_item import make_item
 
-		template_item = make_item("Test-Tshirt-Temp", {
-			"has_variant": 1,
-			"variant_based_on": "Item Attribute",
-			"attributes": [
-				{"attribute": "Test Size"},
-				{"attribute": "Test Colour"}
-			]
-		})
-		variant = create_variant("Test-Tshirt-Temp", {
-			"Test Size": "Small", "Test Colour": "Red"
-		})
+		template_item = make_item(
+			"Test-Tshirt-Temp",
+			{
+				"has_variant": 1,
+				"variant_based_on": "Item Attribute",
+				"attributes": [{"attribute": "Test Size"}, {"attribute": "Test Colour"}],
+			},
+		)
+		variant = create_variant("Test-Tshirt-Temp", {"Test Size": "Small", "Test Colour": "Red"})
 		variant.save()
-		make_website_item(template_item) # publish template not variant
+		make_website_item(template_item)  # publish template not variant
 
 		update_cart("Test-Tshirt-Temp-S-R", 1)
 
-		cart = get_cart_quotation() # test if cart page gets data without errors
+		cart = get_cart_quotation()  # test if cart page gets data without errors
 		doc = cart.get("doc")
 
 		self.assertEqual(doc.get("items")[0].item_name, "Test-Tshirt-Temp-S-R")
@@ -179,6 +193,26 @@
 		# test if items are rendered without error
 		frappe.render_template("templates/includes/cart/cart_items.html", cart)
 
+	@change_settings("E Commerce Settings", {"save_quotations_as_draft": 1})
+	def test_cart_without_checkout_and_draft_quotation(self):
+		"Test impact of 'save_quotations_as_draft' checkbox."
+		frappe.local.shopping_cart_settings = None
+
+		# add item to cart
+		update_cart("_Test Item", 1)
+		quote_name = request_for_quotation()  # Request for Quote
+		quote_doctstatus = cint(frappe.db.get_value("Quotation", quote_name, "docstatus"))
+
+		self.assertEqual(quote_doctstatus, 0)
+
+		frappe.db.set_value("E Commerce Settings", None, "save_quotations_as_draft", 0)
+		frappe.local.shopping_cart_settings = None
+		update_cart("_Test Item", 1)
+		quote_name = request_for_quotation()  # Request for Quote
+		quote_doctstatus = cint(frappe.db.get_value("Quotation", quote_name, "docstatus"))
+
+		self.assertEqual(quote_doctstatus, 1)
+
 	def create_tax_rule(self):
 		tax_rule = frappe.get_test_records("Tax Rule")[0]
 		try:
@@ -198,16 +232,13 @@
 			"contact_email": frappe.session.user,
 			"selling_price_list": "_Test Price List Rest of the World",
 			"currency": "USD",
-			"taxes_and_charges" : "_Test Tax 1 - _TC",
-			"conversion_rate":1,
-			"transaction_date" : nowdate(),
-			"valid_till" : add_months(nowdate(), 1),
-			"items": [{
-				"item_code": "_Test Item",
-				"qty": 1
-			}],
+			"taxes_and_charges": "_Test Tax 1 - _TC",
+			"conversion_rate": 1,
+			"transaction_date": nowdate(),
+			"valid_till": add_months(nowdate(), 1),
+			"items": [{"item_code": "_Test Item", "qty": 1}],
 			"taxes": frappe.get_doc("Sales Taxes and Charges Template", "_Test Tax 1 - _TC").taxes,
-			"company": "_Test Company"
+			"company": "_Test Company",
 		}
 
 		quotation.update(values)
@@ -224,29 +255,36 @@
 	def enable_shopping_cart(self):
 		settings = frappe.get_doc("E Commerce Settings", "E Commerce Settings")
 
-		settings.update({
-			"enabled": 1,
-			"company": "_Test Company",
-			"default_customer_group": "_Test Customer Group",
-			"quotation_series": "_T-Quotation-",
-			"price_list": "_Test Price List India"
-		})
+		settings.update(
+			{
+				"enabled": 1,
+				"company": "_Test Company",
+				"default_customer_group": "_Test Customer Group",
+				"quotation_series": "_T-Quotation-",
+				"price_list": "_Test Price List India",
+			}
+		)
 
 		# insert item price
-		if not frappe.db.get_value("Item Price", {"price_list":  "_Test Price List India",
-			"item_code": "_Test Item"}):
-			frappe.get_doc({
-				"doctype": "Item Price",
-				"price_list": "_Test Price List India",
-				"item_code": "_Test Item",
-				"price_list_rate": 10
-			}).insert()
-			frappe.get_doc({
-				"doctype": "Item Price",
-				"price_list": "_Test Price List India",
-				"item_code": "_Test Item 2",
-				"price_list_rate": 20
-			}).insert()
+		if not frappe.db.get_value(
+			"Item Price", {"price_list": "_Test Price List India", "item_code": "_Test Item"}
+		):
+			frappe.get_doc(
+				{
+					"doctype": "Item Price",
+					"price_list": "_Test Price List India",
+					"item_code": "_Test Item",
+					"price_list_rate": 10,
+				}
+			).insert()
+			frappe.get_doc(
+				{
+					"doctype": "Item Price",
+					"price_list": "_Test Price List India",
+					"item_code": "_Test Item 2",
+					"price_list_rate": 20,
+				}
+			).insert()
 
 		settings.save()
 		frappe.local.shopping_cart_settings = None
@@ -261,31 +299,49 @@
 		self.create_user_if_not_exists("test_cart_user@example.com")
 		frappe.set_user("test_cart_user@example.com")
 
-	def login_as_customer(self, email="test_contact_customer@example.com", name="_Test Contact For _Test Customer"):
+	def login_as_customer(
+		self, email="test_contact_customer@example.com", name="_Test Contact For _Test Customer"
+	):
 		self.create_user_if_not_exists(email, name)
 		frappe.set_user(email)
 
 	def clear_existing_quotations(self):
-		quotations = frappe.get_all("Quotation", filters={
-			"party_name": get_party().name,
-			"order_type": "Shopping Cart",
-			"docstatus": 0
-		}, order_by="modified desc", pluck="name")
+		quotations = frappe.get_all(
+			"Quotation",
+			filters={"party_name": get_party().name, "order_type": "Shopping Cart", "docstatus": 0},
+			order_by="modified desc",
+			pluck="name",
+		)
 
 		for quotation in quotations:
 			frappe.delete_doc("Quotation", quotation, ignore_permissions=True, force=True)
 
-	def create_user_if_not_exists(self, email, first_name = None):
+	def create_user_if_not_exists(self, email, first_name=None):
 		if frappe.db.exists("User", email):
 			return
 
-		frappe.get_doc({
-			"doctype": "User",
-			"user_type": "Website User",
-			"email": email,
-			"send_welcome_email": 0,
-			"first_name": first_name or email.split("@")[0]
-		}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "User",
+				"user_type": "Website User",
+				"email": email,
+				"send_welcome_email": 0,
+				"first_name": first_name or email.split("@")[0],
+			}
+		).insert(ignore_permissions=True)
 
-test_dependencies = ["Sales Taxes and Charges Template", "Price List", "Item Price", "Shipping Rule", "Currency Exchange",
-	"Customer Group", "Lead", "Customer", "Contact", "Address", "Item", "Tax Rule"]
+
+test_dependencies = [
+	"Sales Taxes and Charges Template",
+	"Price List",
+	"Item Price",
+	"Shipping Rule",
+	"Currency Exchange",
+	"Customer Group",
+	"Lead",
+	"Customer",
+	"Contact",
+	"Address",
+	"Item",
+	"Tax Rule",
+]
diff --git a/erpnext/e_commerce/shopping_cart/utils.py b/erpnext/e_commerce/shopping_cart/utils.py
index e9745a4..3d48c28 100644
--- a/erpnext/e_commerce/shopping_cart/utils.py
+++ b/erpnext/e_commerce/shopping_cart/utils.py
@@ -6,12 +6,15 @@
 
 
 def show_cart_count():
-	if (is_cart_enabled() and
-		frappe.db.get_value("User", frappe.session.user, "user_type") == "Website User"):
+	if (
+		is_cart_enabled()
+		and frappe.db.get_value("User", frappe.session.user, "user_type") == "Website User"
+	):
 		return True
 
 	return False
 
+
 def set_cart_count(login_manager):
 	# since this is run only on hooks login event
 	# make sure user is already a customer
@@ -28,21 +31,24 @@
 		# cart count is calculated from this quotation's items
 		set_cart_count()
 
+
 def clear_cart_count(login_manager):
 	if show_cart_count():
 		frappe.local.cookie_manager.delete_cookie("cart_count")
 
+
 def update_website_context(context):
 	cart_enabled = is_cart_enabled()
 	context["shopping_cart_enabled"] = cart_enabled
 
+
 def is_customer():
 	if frappe.session.user and frappe.session.user != "Guest":
 		contact_name = frappe.get_value("Contact", {"email_id": frappe.session.user})
 		if contact_name:
-			contact = frappe.get_doc('Contact', contact_name)
+			contact = frappe.get_doc("Contact", contact_name)
 			for link in contact.links:
-				if link.link_doctype == 'Customer':
+				if link.link_doctype == "Customer":
 					return True
 
 		return False
diff --git a/erpnext/e_commerce/variant_selector/item_variants_cache.py b/erpnext/e_commerce/variant_selector/item_variants_cache.py
index 3107c01..f8439d5 100644
--- a/erpnext/e_commerce/variant_selector/item_variants_cache.py
+++ b/erpnext/e_commerce/variant_selector/item_variants_cache.py
@@ -6,63 +6,60 @@
 		self.item_code = item_code
 
 	def get_item_variants_data(self):
-		val = frappe.cache().hget('item_variants_data', self.item_code)
+		val = frappe.cache().hget("item_variants_data", self.item_code)
 
 		if not val:
 			self.build_cache()
 
-		return frappe.cache().hget('item_variants_data', self.item_code)
-
+		return frappe.cache().hget("item_variants_data", self.item_code)
 
 	def get_attribute_value_item_map(self):
-		val = frappe.cache().hget('attribute_value_item_map', self.item_code)
+		val = frappe.cache().hget("attribute_value_item_map", self.item_code)
 
 		if not val:
 			self.build_cache()
 
-		return frappe.cache().hget('attribute_value_item_map', self.item_code)
-
+		return frappe.cache().hget("attribute_value_item_map", self.item_code)
 
 	def get_item_attribute_value_map(self):
-		val = frappe.cache().hget('item_attribute_value_map', self.item_code)
+		val = frappe.cache().hget("item_attribute_value_map", self.item_code)
 
 		if not val:
 			self.build_cache()
 
-		return frappe.cache().hget('item_attribute_value_map', self.item_code)
-
+		return frappe.cache().hget("item_attribute_value_map", self.item_code)
 
 	def get_optional_attributes(self):
-		val = frappe.cache().hget('optional_attributes', self.item_code)
+		val = frappe.cache().hget("optional_attributes", self.item_code)
 
 		if not val:
 			self.build_cache()
 
-		return frappe.cache().hget('optional_attributes', self.item_code)
+		return frappe.cache().hget("optional_attributes", self.item_code)
 
 	def get_ordered_attribute_values(self):
-		val = frappe.cache().get_value('ordered_attribute_values_map')
-		if val: return val
+		val = frappe.cache().get_value("ordered_attribute_values_map")
+		if val:
+			return val
 
-		all_attribute_values = frappe.get_all('Item Attribute Value',
-			['attribute_value', 'idx', 'parent'], order_by='idx asc')
+		all_attribute_values = frappe.get_all(
+			"Item Attribute Value", ["attribute_value", "idx", "parent"], order_by="idx asc"
+		)
 
 		ordered_attribute_values_map = frappe._dict({})
 		for d in all_attribute_values:
 			ordered_attribute_values_map.setdefault(d.parent, []).append(d.attribute_value)
 
-		frappe.cache().set_value('ordered_attribute_values_map', ordered_attribute_values_map)
+		frappe.cache().set_value("ordered_attribute_values_map", ordered_attribute_values_map)
 		return ordered_attribute_values_map
 
 	def build_cache(self):
 		parent_item_code = self.item_code
 
 		attributes = [
-			a.attribute for a in frappe.get_all(
-				'Item Variant Attribute',
-				{'parent': parent_item_code},
-				['attribute'],
-				order_by='idx asc'
+			a.attribute
+			for a in frappe.get_all(
+				"Item Variant Attribute", {"parent": parent_item_code}, ["attribute"], order_by="idx asc"
 			)
 		]
 
@@ -71,13 +68,11 @@
 		item = frappe.qb.DocType("Item")
 		query = (
 			frappe.qb.from_(iva)
-			.join(item).on(item.name == iva.parent)
-			.select(
-				iva.parent, iva.attribute, iva.attribute_value
-			).where(
-				(iva.variant_of == parent_item_code)
-				& (item.disabled == 0)
-			).orderby(iva.name)
+			.join(item)
+			.on(item.name == iva.parent)
+			.select(iva.parent, iva.attribute, iva.attribute_value)
+			.where((iva.variant_of == parent_item_code) & (item.disabled == 0))
+			.orderby(iva.name)
 		)
 		item_variants_data = query.run()
 
@@ -97,13 +92,18 @@
 				if attribute not in attr_dict:
 					optional_attributes.add(attribute)
 
-		frappe.cache().hset('attribute_value_item_map', parent_item_code, attribute_value_item_map)
-		frappe.cache().hset('item_attribute_value_map', parent_item_code, item_attribute_value_map)
-		frappe.cache().hset('item_variants_data', parent_item_code, item_variants_data)
-		frappe.cache().hset('optional_attributes', parent_item_code, optional_attributes)
+		frappe.cache().hset("attribute_value_item_map", parent_item_code, attribute_value_item_map)
+		frappe.cache().hset("item_attribute_value_map", parent_item_code, item_attribute_value_map)
+		frappe.cache().hset("item_variants_data", parent_item_code, item_variants_data)
+		frappe.cache().hset("optional_attributes", parent_item_code, optional_attributes)
 
 	def clear_cache(self):
-		keys = ['attribute_value_item_map', 'item_attribute_value_map', 'item_variants_data', 'optional_attributes']
+		keys = [
+			"attribute_value_item_map",
+			"item_attribute_value_map",
+			"item_variants_data",
+			"optional_attributes",
+		]
 
 		for key in keys:
 			frappe.cache().hdel(key, self.item_code)
@@ -114,15 +114,17 @@
 
 
 def build_cache(item_code):
-	frappe.cache().hset('item_cache_build_in_progress', item_code, 1)
+	frappe.cache().hset("item_cache_build_in_progress", item_code, 1)
 	i = ItemVariantsCacheManager(item_code)
 	i.build_cache()
-	frappe.cache().hset('item_cache_build_in_progress', item_code, 0)
+	frappe.cache().hset("item_cache_build_in_progress", item_code, 0)
+
 
 def enqueue_build_cache(item_code):
-	if frappe.cache().hget('item_cache_build_in_progress', item_code):
+	if frappe.cache().hget("item_cache_build_in_progress", item_code):
 		return
 	frappe.enqueue(
 		"erpnext.e_commerce.variant_selector.item_variants_cache.build_cache",
-		item_code=item_code, queue='long'
+		item_code=item_code,
+		queue="long",
 	)
diff --git a/erpnext/e_commerce/variant_selector/test_variant_selector.py b/erpnext/e_commerce/variant_selector/test_variant_selector.py
index ee098e1..8eb497c 100644
--- a/erpnext/e_commerce/variant_selector/test_variant_selector.py
+++ b/erpnext/e_commerce/variant_selector/test_variant_selector.py
@@ -11,40 +11,43 @@
 
 test_dependencies = ["Item"]
 
-class TestVariantSelector(FrappeTestCase):
 
+class TestVariantSelector(FrappeTestCase):
 	@classmethod
 	def setUpClass(cls):
-		template_item = make_item("Test-Tshirt-Temp", {
-			"has_variant": 1,
-			"variant_based_on": "Item Attribute",
-			"attributes": [
-				{"attribute": "Test Size"},
-				{"attribute": "Test Colour"}
-			]
-		})
+		super().setUpClass()
+		template_item = make_item(
+			"Test-Tshirt-Temp",
+			{
+				"has_variant": 1,
+				"variant_based_on": "Item Attribute",
+				"attributes": [{"attribute": "Test Size"}, {"attribute": "Test Colour"}],
+			},
+		)
 
 		# create L-R, L-G, M-R, M-G and S-R
-		for size in ("Large", "Medium",):
-			for colour in ("Red", "Green",):
-				variant = create_variant("Test-Tshirt-Temp", {
-					"Test Size": size, "Test Colour": colour
-				})
+		for size in (
+			"Large",
+			"Medium",
+		):
+			for colour in (
+				"Red",
+				"Green",
+			):
+				variant = create_variant("Test-Tshirt-Temp", {"Test Size": size, "Test Colour": colour})
 				variant.save()
 
-		variant = create_variant("Test-Tshirt-Temp", {
-			"Test Size": "Small", "Test Colour": "Red"
-		})
+		variant = create_variant("Test-Tshirt-Temp", {"Test Size": "Small", "Test Colour": "Red"})
 		variant.save()
 
-		make_website_item(template_item) # publish template not variants
+		make_website_item(template_item)  # publish template not variants
 
 	def test_item_attributes(self):
 		"""
-			Test if the right attributes are fetched in the popup.
-			(Attributes must only come from active items)
+		Test if the right attributes are fetched in the popup.
+		(Attributes must only come from active items)
 
-			Attribute selection must not be linked to Website Items.
+		Attribute selection must not be linked to Website Items.
 		"""
 		from erpnext.e_commerce.variant_selector.utils import get_attributes_and_values
 
@@ -52,14 +55,14 @@
 
 		self.assertEqual(attr_data[0]["attribute"], "Test Size")
 		self.assertEqual(attr_data[1]["attribute"], "Test Colour")
-		self.assertEqual(len(attr_data[0]["values"]), 3) # ['Small', 'Medium', 'Large']
-		self.assertEqual(len(attr_data[1]["values"]), 2) # ['Red', 'Green']
+		self.assertEqual(len(attr_data[0]["values"]), 3)  # ['Small', 'Medium', 'Large']
+		self.assertEqual(len(attr_data[1]["values"]), 2)  # ['Red', 'Green']
 
 		# disable small red tshirt, now there are no small tshirts.
 		# but there are some red tshirts
 		small_variant = frappe.get_doc("Item", "Test-Tshirt-Temp-S-R")
 		small_variant.disabled = 1
-		small_variant.save() # trigger cache rebuild
+		small_variant.save()  # trigger cache rebuild
 
 		attr_data = get_attributes_and_values("Test-Tshirt-Temp")
 
@@ -72,14 +75,16 @@
 
 	def test_next_item_variant_values(self):
 		"""
-			Test if on selecting an attribute value, the next possible values
-			are filtered accordingly.
-			Values that dont apply should not be fetched.
-			E.g.
-			There is a ** Small-Red ** Tshirt. No other colour in this size.
-			On selecting ** Small **, only ** Red ** should be selectable next.
+		Test if on selecting an attribute value, the next possible values
+		are filtered accordingly.
+		Values that dont apply should not be fetched.
+		E.g.
+		There is a ** Small-Red ** Tshirt. No other colour in this size.
+		On selecting ** Small **, only ** Red ** should be selectable next.
 		"""
-		next_values = get_next_attribute_and_values("Test-Tshirt-Temp", selected_attributes={"Test Size": "Small"})
+		next_values = get_next_attribute_and_values(
+			"Test-Tshirt-Temp", selected_attributes={"Test Size": "Small"}
+		)
 		next_colours = next_values["valid_options_for_attributes"]["Test Colour"]
 		filtered_items = next_values["filtered_items"]
 
@@ -90,30 +95,31 @@
 
 	def test_exact_match_with_price(self):
 		"""
-			Test price fetching and matching of variant without Website Item
+		Test price fetching and matching of variant without Website Item
 		"""
 		from erpnext.e_commerce.doctype.website_item.test_website_item import make_web_item_price
 
 		frappe.set_user("Administrator")
-		setup_e_commerce_settings({
-			"company": "_Test Company",
-			"enabled": 1,
-			"default_customer_group": "_Test Customer Group",
-			"price_list": "_Test Price List India",
-			"show_price": 1
-		})
+		setup_e_commerce_settings(
+			{
+				"company": "_Test Company",
+				"enabled": 1,
+				"default_customer_group": "_Test Customer Group",
+				"price_list": "_Test Price List India",
+				"show_price": 1,
+			}
+		)
 
 		make_web_item_price(item_code="Test-Tshirt-Temp-S-R", price_list_rate=100)
 
-		frappe.local.shopping_cart_settings = None # clear cached settings values
+		frappe.local.shopping_cart_settings = None  # clear cached settings values
 		next_values = get_next_attribute_and_values(
-			"Test-Tshirt-Temp",
-			selected_attributes={"Test Size": "Small", "Test Colour": "Red"}
+			"Test-Tshirt-Temp", selected_attributes={"Test Size": "Small", "Test Colour": "Red"}
 		)
 		print(">>>>", next_values)
 		price_info = next_values["product_info"]["price"]
 
-		self.assertEqual(next_values["exact_match"][0],"Test-Tshirt-Temp-S-R")
-		self.assertEqual(next_values["exact_match"][0],"Test-Tshirt-Temp-S-R")
+		self.assertEqual(next_values["exact_match"][0], "Test-Tshirt-Temp-S-R")
+		self.assertEqual(next_values["exact_match"][0], "Test-Tshirt-Temp-S-R")
 		self.assertEqual(price_info["price_list_rate"], 100.0)
 		self.assertEqual(price_info["formatted_price_sales_uom"], "₹ 100.00")
diff --git a/erpnext/e_commerce/variant_selector/utils.py b/erpnext/e_commerce/variant_selector/utils.py
index 3380273..df62c23 100644
--- a/erpnext/e_commerce/variant_selector/utils.py
+++ b/erpnext/e_commerce/variant_selector/utils.py
@@ -24,18 +24,18 @@
 		wheres = []
 		query_values = []
 		for attribute_value in attribute_values:
-			wheres.append('( attribute = %s and attribute_value = %s )')
+			wheres.append("( attribute = %s and attribute_value = %s )")
 			query_values += [attribute, attribute_value]
 
-		attribute_query = ' or '.join(wheres)
+		attribute_query = " or ".join(wheres)
 
 		if template_item_code:
-			variant_of_query = 'AND t2.variant_of = %s'
+			variant_of_query = "AND t2.variant_of = %s"
 			query_values.append(template_item_code)
 		else:
-			variant_of_query = ''
+			variant_of_query = ""
 
-		query = '''
+		query = """
 			SELECT
 				t1.parent
 			FROM
@@ -58,20 +58,23 @@
 				t1.parent
 			ORDER BY
 				NULL
-		'''.format(attribute_query=attribute_query, variant_of_query=variant_of_query)
+		""".format(
+			attribute_query=attribute_query, variant_of_query=variant_of_query
+		)
 
-		item_codes = set([r[0] for r in frappe.db.sql(query, query_values)]) # nosemgrep
+		item_codes = set([r[0] for r in frappe.db.sql(query, query_values)])  # nosemgrep
 		items.append(item_codes)
 
 	res = list(set.intersection(*items))
 
 	return res
 
+
 @frappe.whitelist(allow_guest=True)
 def get_attributes_and_values(item_code):
-	'''Build a list of attributes and their possible values.
+	"""Build a list of attributes and their possible values.
 	This will ignore the values upon selection of which there cannot exist one item.
-	'''
+	"""
 	item_cache = ItemVariantsCacheManager(item_code)
 	item_variants_data = item_cache.get_item_variants_data()
 
@@ -83,8 +86,9 @@
 		if attribute in attribute_list:
 			valid_options.setdefault(attribute, set()).add(attribute_value)
 
-	item_attribute_values = frappe.db.get_all('Item Attribute Value',
-		['parent', 'attribute_value', 'idx'], order_by='parent asc, idx asc')
+	item_attribute_values = frappe.db.get_all(
+		"Item Attribute Value", ["parent", "attribute_value", "idx"], order_by="parent asc, idx asc"
+	)
 	ordered_attribute_value_map = frappe._dict()
 	for iv in item_attribute_values:
 		ordered_attribute_value_map.setdefault(iv.parent, []).append(iv.attribute_value)
@@ -93,18 +97,18 @@
 	for attr in attributes:
 		valid_attribute_values = valid_options.get(attr.attribute, [])
 		ordered_values = ordered_attribute_value_map.get(attr.attribute, [])
-		attr['values'] = [v for v in ordered_values if v in valid_attribute_values]
+		attr["values"] = [v for v in ordered_values if v in valid_attribute_values]
 
 	return attributes
 
 
 @frappe.whitelist(allow_guest=True)
 def get_next_attribute_and_values(item_code, selected_attributes):
-	'''Find the count of Items that match the selected attributes.
+	"""Find the count of Items that match the selected attributes.
 	Also, find the attribute values that are not applicable for further searching.
 	If less than equal to 10 items are found, return item_codes of those items.
 	If one item is matched exactly, return item_code of that item.
-	'''
+	"""
 	selected_attributes = frappe.parse_json(selected_attributes)
 
 	item_cache = ItemVariantsCacheManager(item_code)
@@ -133,7 +137,11 @@
 
 	for row in item_variants_data:
 		item_code, attribute, attribute_value = row
-		if item_code in filtered_items and attribute not in selected_attributes and attribute in attribute_list:
+		if (
+			item_code in filtered_items
+			and attribute not in selected_attributes
+			and attribute in attribute_list
+		):
 			valid_options_for_attributes[attribute].add(attribute_value)
 
 	optional_attributes = item_cache.get_optional_attributes()
@@ -159,12 +167,12 @@
 		product_info = None
 
 	return {
-		'next_attribute': next_attribute,
-		'valid_options_for_attributes': valid_options_for_attributes,
-		'filtered_items_count': filtered_items_count,
-		'filtered_items': filtered_items if filtered_items_count < 10 else [],
-		'exact_match': exact_match,
-		'product_info': product_info
+		"next_attribute": next_attribute,
+		"valid_options_for_attributes": valid_options_for_attributes,
+		"filtered_items_count": filtered_items_count,
+		"filtered_items": filtered_items if filtered_items_count < 10 else [],
+		"exact_match": exact_match,
+		"product_info": product_info,
 	}
 
 
@@ -179,16 +187,16 @@
 
 	return set.intersection(*items)
 
+
 # utilities
 
+
 def get_item_attributes(item_code):
-	attributes = frappe.db.get_all('Item Variant Attribute',
-		fields=['attribute'],
-		filters={
-			'parenttype': 'Item',
-			'parent': item_code
-		},
-		order_by='idx asc'
+	attributes = frappe.db.get_all(
+		"Item Variant Attribute",
+		fields=["attribute"],
+		filters={"parenttype": "Item", "parent": item_code},
+		order_by="idx asc",
 	)
 
 	optional_attributes = ItemVariantsCacheManager(item_code).get_optional_attributes()
@@ -199,6 +207,7 @@
 
 	return attributes
 
+
 def get_item_variant_price_dict(item_code, cart_settings):
 	if cart_settings.enabled and cart_settings.show_price:
 		is_guest = frappe.session.user == "Guest"
@@ -207,12 +216,8 @@
 		if not is_guest or not cart_settings.hide_price_for_guest:
 			price_list = _set_price_list(cart_settings, None)
 			price = get_price(
-				item_code,
-				price_list,
-				cart_settings.default_customer_group,
-				cart_settings.company
+				item_code, price_list, cart_settings.default_customer_group, cart_settings.company
 			)
 			return {"price": price}
 
 	return None
-
diff --git a/erpnext/education/__init__.py b/erpnext/education/__init__.py
deleted file mode 100644
index 56c2b29..0000000
--- a/erpnext/education/__init__.py
+++ /dev/null
@@ -1,11 +0,0 @@
-import frappe
-from frappe import _
-
-
-class StudentNotInGroupError(frappe.ValidationError): pass
-
-def validate_student_belongs_to_group(student, student_group):
-	groups = frappe.db.get_all('Student Group Student', ['parent'], dict(student = student, active=1))
-	if not student_group in [d.parent for d in groups]:
-		frappe.throw(_('Student {0} does not belong to group {1}').format(frappe.bold(student), frappe.bold(student_group)),
-			StudentNotInGroupError)
diff --git a/erpnext/education/api.py b/erpnext/education/api.py
deleted file mode 100644
index 636b948..0000000
--- a/erpnext/education/api.py
+++ /dev/null
@@ -1,388 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-import json
-
-import frappe
-from frappe import _
-from frappe.email.doctype.email_group.email_group import add_subscribers
-from frappe.model.mapper import get_mapped_doc
-from frappe.utils import cstr, flt, getdate
-
-
-def get_course(program):
-	'''Return list of courses for a particular program
-	:param program: Program
-	'''
-	courses = frappe.db.sql('''select course, course_name from `tabProgram Course` where parent=%s''',
-			(program), as_dict=1)
-	return courses
-
-
-@frappe.whitelist()
-def enroll_student(source_name):
-	"""Creates a Student Record and returns a Program Enrollment.
-
-	:param source_name: Student Applicant.
-	"""
-	frappe.publish_realtime('enroll_student_progress', {"progress": [1, 4]}, user=frappe.session.user)
-	student = get_mapped_doc("Student Applicant", source_name,
-		{"Student Applicant": {
-			"doctype": "Student",
-			"field_map": {
-				"name": "student_applicant"
-			}
-		}}, ignore_permissions=True)
-	student.save()
-
-	student_applicant = frappe.db.get_value("Student Applicant", source_name,
-		["student_category", "program"], as_dict=True)
-	program_enrollment = frappe.new_doc("Program Enrollment")
-	program_enrollment.student = student.name
-	program_enrollment.student_category = student_applicant.student_category
-	program_enrollment.student_name = student.title
-	program_enrollment.program = student_applicant.program
-	frappe.publish_realtime('enroll_student_progress', {"progress": [2, 4]}, user=frappe.session.user)
-	return program_enrollment
-
-
-@frappe.whitelist()
-def check_attendance_records_exist(course_schedule=None, student_group=None, date=None):
-	"""Check if Attendance Records are made against the specified Course Schedule or Student Group for given date.
-
-	:param course_schedule: Course Schedule.
-	:param student_group: Student Group.
-	:param date: Date.
-	"""
-	if course_schedule:
-		return frappe.get_list("Student Attendance", filters={"course_schedule": course_schedule})
-	else:
-		return frappe.get_list("Student Attendance", filters={"student_group": student_group, "date": date})
-
-
-@frappe.whitelist()
-def mark_attendance(students_present, students_absent, course_schedule=None, student_group=None, date=None):
-	"""Creates Multiple Attendance Records.
-
-	:param students_present: Students Present JSON.
-	:param students_absent: Students Absent JSON.
-	:param course_schedule: Course Schedule.
-	:param student_group: Student Group.
-	:param date: Date.
-	"""
-
-	if student_group:
-		academic_year = frappe.db.get_value('Student Group', student_group, 'academic_year')
-		if academic_year:
-			year_start_date, year_end_date = frappe.db.get_value('Academic Year', academic_year, ['year_start_date', 'year_end_date'])
-			if getdate(date) < getdate(year_start_date) or getdate(date) > getdate(year_end_date):
-				frappe.throw(_('Attendance cannot be marked outside of Academic Year {0}').format(academic_year))
-
-	present = json.loads(students_present)
-	absent = json.loads(students_absent)
-
-	for d in present:
-		make_attendance_records(d["student"], d["student_name"], "Present", course_schedule, student_group, date)
-
-	for d in absent:
-		make_attendance_records(d["student"], d["student_name"], "Absent", course_schedule, student_group, date)
-
-	frappe.db.commit()
-	frappe.msgprint(_("Attendance has been marked successfully."))
-
-
-def make_attendance_records(student, student_name, status, course_schedule=None, student_group=None, date=None):
-	"""Creates/Update Attendance Record.
-
-	:param student: Student.
-	:param student_name: Student Name.
-	:param course_schedule: Course Schedule.
-	:param status: Status (Present/Absent)
-	"""
-	student_attendance = frappe.get_doc({
-		"doctype": "Student Attendance",
-		"student": student,
-		"course_schedule": course_schedule,
-		"student_group": student_group,
-		"date": date
-	})
-	if not student_attendance:
-		student_attendance = frappe.new_doc("Student Attendance")
-	student_attendance.student = student
-	student_attendance.student_name = student_name
-	student_attendance.course_schedule = course_schedule
-	student_attendance.student_group = student_group
-	student_attendance.date = date
-	student_attendance.status = status
-	student_attendance.save()
-	student_attendance.submit()
-
-
-@frappe.whitelist()
-def get_student_guardians(student):
-	"""Returns List of Guardians of a Student.
-
-	:param student: Student.
-	"""
-	guardians = frappe.get_all("Student Guardian", fields=["guardian"] ,
-		filters={"parent": student})
-	return guardians
-
-
-@frappe.whitelist()
-def get_student_group_students(student_group, include_inactive=0):
-	"""Returns List of student, student_name in Student Group.
-
-	:param student_group: Student Group.
-	"""
-	if include_inactive:
-		students = frappe.get_all("Student Group Student", fields=["student", "student_name"] ,
-			filters={"parent": student_group}, order_by= "group_roll_number")
-	else:
-		students = frappe.get_all("Student Group Student", fields=["student", "student_name"] ,
-			filters={"parent": student_group, "active": 1}, order_by= "group_roll_number")
-	return students
-
-
-@frappe.whitelist()
-def get_fee_structure(program, academic_term=None):
-	"""Returns Fee Structure.
-
-	:param program: Program.
-	:param academic_term: Academic Term.
-	"""
-	fee_structure = frappe.db.get_values("Fee Structure", {"program": program,
-		"academic_term": academic_term}, 'name', as_dict=True)
-	return fee_structure[0].name if fee_structure else None
-
-
-@frappe.whitelist()
-def get_fee_components(fee_structure):
-	"""Returns Fee Components.
-
-	:param fee_structure: Fee Structure.
-	"""
-	if fee_structure:
-		fs = frappe.get_all("Fee Component", fields=["fees_category", "description", "amount"] , filters={"parent": fee_structure}, order_by= "idx")
-		return fs
-
-
-@frappe.whitelist()
-def get_fee_schedule(program, student_category=None):
-	"""Returns Fee Schedule.
-
-	:param program: Program.
-	:param student_category: Student Category
-	"""
-	fs = frappe.get_all("Program Fee", fields=["academic_term", "fee_structure", "due_date", "amount"] ,
-		filters={"parent": program, "student_category": student_category }, order_by= "idx")
-	return fs
-
-
-@frappe.whitelist()
-def collect_fees(fees, amt):
-	paid_amount = flt(amt) + flt(frappe.db.get_value("Fees", fees, "paid_amount"))
-	total_amount = flt(frappe.db.get_value("Fees", fees, "total_amount"))
-	frappe.db.set_value("Fees", fees, "paid_amount", paid_amount)
-	frappe.db.set_value("Fees", fees, "outstanding_amount", (total_amount - paid_amount))
-	return paid_amount
-
-
-@frappe.whitelist()
-def get_course_schedule_events(start, end, filters=None):
-	"""Returns events for Course Schedule Calendar view rendering.
-
-	:param start: Start date-time.
-	:param end: End date-time.
-	:param filters: Filters (JSON).
-	"""
-	from frappe.desk.calendar import get_event_conditions
-	conditions = get_event_conditions("Course Schedule", filters)
-
-	data = frappe.db.sql("""select name, course, color,
-			timestamp(schedule_date, from_time) as from_time,
-			timestamp(schedule_date, to_time) as to_time,
-			room, student_group, 0 as 'allDay'
-		from `tabCourse Schedule`
-		where ( schedule_date between %(start)s and %(end)s )
-		{conditions}""".format(conditions=conditions), {
-			"start": start,
-			"end": end
-			}, as_dict=True, update={"allDay": 0})
-
-	return data
-
-
-@frappe.whitelist()
-def get_assessment_criteria(course):
-	"""Returns Assessmemt Criteria and their Weightage from Course Master.
-
-	:param Course: Course
-	"""
-	return frappe.get_all("Course Assessment Criteria",
-		fields=["assessment_criteria", "weightage"], filters={"parent": course}, order_by= "idx")
-
-
-@frappe.whitelist()
-def get_assessment_students(assessment_plan, student_group):
-	student_list = get_student_group_students(student_group)
-	for i, student in enumerate(student_list):
-		result = get_result(student.student, assessment_plan)
-		if result:
-			student_result = {}
-			for d in result.details:
-				student_result.update({d.assessment_criteria: [cstr(d.score), d.grade]})
-			student_result.update({
-				"total_score": [cstr(result.total_score), result.grade],
-				"comment": result.comment
-			})
-			student.update({
-				"assessment_details": student_result,
-				"docstatus": result.docstatus,
-				"name": result.name
-			})
-		else:
-			student.update({'assessment_details': None})
-	return student_list
-
-
-@frappe.whitelist()
-def get_assessment_details(assessment_plan):
-	"""Returns Assessment Criteria  and Maximum Score from Assessment Plan Master.
-
-	:param Assessment Plan: Assessment Plan
-	"""
-	return frappe.get_all("Assessment Plan Criteria",
-		fields=["assessment_criteria", "maximum_score", "docstatus"], filters={"parent": assessment_plan}, order_by= "idx")
-
-
-@frappe.whitelist()
-def get_result(student, assessment_plan):
-	"""Returns Submitted Result of given student for specified Assessment Plan
-
-	:param Student: Student
-	:param Assessment Plan: Assessment Plan
-	"""
-	results = frappe.get_all("Assessment Result", filters={"student": student,
-		"assessment_plan": assessment_plan, "docstatus": ("!=", 2)})
-	if results:
-		return frappe.get_doc("Assessment Result", results[0])
-	else:
-		return None
-
-
-@frappe.whitelist()
-def get_grade(grading_scale, percentage):
-	"""Returns Grade based on the Grading Scale and Score.
-
-	:param Grading Scale: Grading Scale
-	:param Percentage: Score Percentage Percentage
-	"""
-	grading_scale_intervals = {}
-	if not hasattr(frappe.local, 'grading_scale'):
-		grading_scale = frappe.get_all("Grading Scale Interval", fields=["grade_code", "threshold"], filters={"parent": grading_scale})
-		frappe.local.grading_scale = grading_scale
-	for d in frappe.local.grading_scale:
-		grading_scale_intervals.update({d.threshold:d.grade_code})
-	intervals = sorted(grading_scale_intervals.keys(), key=float, reverse=True)
-	for interval in intervals:
-		if flt(percentage) >= interval:
-			grade = grading_scale_intervals.get(interval)
-			break
-		else:
-			grade = ""
-	return grade
-
-
-@frappe.whitelist()
-def mark_assessment_result(assessment_plan, scores):
-	student_score = json.loads(scores);
-	assessment_details = []
-	for criteria in student_score.get("assessment_details"):
-		assessment_details.append({
-			"assessment_criteria": criteria,
-			"score": flt(student_score["assessment_details"][criteria])
-		})
-	assessment_result = get_assessment_result_doc(student_score["student"], assessment_plan)
-	assessment_result.update({
-		"student": student_score.get("student"),
-		"assessment_plan": assessment_plan,
-		"comment": student_score.get("comment"),
-		"total_score":student_score.get("total_score"),
-		"details": assessment_details
-	})
-	assessment_result.save()
-	details = {}
-	for d in assessment_result.details:
-		details.update({d.assessment_criteria: d.grade})
-	assessment_result_dict = {
-		"name": assessment_result.name,
-		"student": assessment_result.student,
-		"total_score": assessment_result.total_score,
-		"grade": assessment_result.grade,
-		"details": details
-	}
-	return assessment_result_dict
-
-
-@frappe.whitelist()
-def submit_assessment_results(assessment_plan, student_group):
-	total_result = 0
-	student_list = get_student_group_students(student_group)
-	for i, student in enumerate(student_list):
-		doc = get_result(student.student, assessment_plan)
-		if doc and doc.docstatus==0:
-			total_result += 1
-			doc.submit()
-	return total_result
-
-
-def get_assessment_result_doc(student, assessment_plan):
-	assessment_result = frappe.get_all("Assessment Result", filters={"student": student,
-			"assessment_plan": assessment_plan, "docstatus": ("!=", 2)})
-	if assessment_result:
-		doc = frappe.get_doc("Assessment Result", assessment_result[0])
-		if doc.docstatus == 0:
-			return doc
-		elif doc.docstatus == 1:
-			frappe.msgprint(_("Result already Submitted"))
-			return None
-	else:
-		return frappe.new_doc("Assessment Result")
-
-
-@frappe.whitelist()
-def update_email_group(doctype, name):
-	if not frappe.db.exists("Email Group", name):
-		email_group = frappe.new_doc("Email Group")
-		email_group.title = name
-		email_group.save()
-	email_list = []
-	students = []
-	if doctype == "Student Group":
-		students = get_student_group_students(name)
-	for stud in students:
-		for guard in get_student_guardians(stud.student):
-			email = frappe.db.get_value("Guardian", guard.guardian, "email_address")
-			if email:
-				email_list.append(email)
-	add_subscribers(name, email_list)
-
-@frappe.whitelist()
-def get_current_enrollment(student, academic_year=None):
-	current_academic_year = academic_year or frappe.defaults.get_defaults().academic_year
-	program_enrollment_list = frappe.db.sql('''
-		select
-			name as program_enrollment, student_name, program, student_batch_name as student_batch,
-			student_category, academic_term, academic_year
-		from
-			`tabProgram Enrollment`
-		where
-			student = %s and academic_year = %s
-		order by creation''', (student, current_academic_year), as_dict=1)
-
-	if program_enrollment_list:
-		return program_enrollment_list[0]
-	else:
-		return None
diff --git a/erpnext/education/dashboard_chart/course_wise_enrollment/course_wise_enrollment.json b/erpnext/education/dashboard_chart/course_wise_enrollment/course_wise_enrollment.json
deleted file mode 100644
index 9c5f784..0000000
--- a/erpnext/education/dashboard_chart/course_wise_enrollment/course_wise_enrollment.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "based_on": "",
- "chart_name": "Course wise Enrollment",
- "chart_type": "Group By",
- "creation": "2020-07-23 18:24:38.214220",
- "docstatus": 0,
- "doctype": "Dashboard Chart",
- "document_type": "Course Enrollment",
- "dynamic_filters_json": "[]",
- "filters_json": "[[\"Course Enrollment\",\"enrollment_date\",\"Timespan\",\"this year\",false]]",
- "group_by_based_on": "course",
- "group_by_type": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "last_synced_on": "2020-07-27 17:50:32.490587",
- "modified": "2020-07-27 17:54:09.829206",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Course wise Enrollment",
- "number_of_groups": 0,
- "owner": "Administrator",
- "source": "",
- "time_interval": "Yearly",
- "timeseries": 0,
- "timespan": "Last Year",
- "type": "Percentage",
- "use_report_chart": 0,
- "value_based_on": "",
- "y_axis": []
-}
\ No newline at end of file
diff --git a/erpnext/education/dashboard_chart/course_wise_student_count/course_wise_student_count.json b/erpnext/education/dashboard_chart/course_wise_student_count/course_wise_student_count.json
deleted file mode 100644
index 5441518..0000000
--- a/erpnext/education/dashboard_chart/course_wise_student_count/course_wise_student_count.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "based_on": "",
- "chart_name": "Course wise Student Count",
- "chart_type": "Group By",
- "creation": "2020-07-27 17:24:39.136163",
- "docstatus": 0,
- "doctype": "Dashboard Chart",
- "document_type": "Course Enrollment",
- "dynamic_filters_json": "[]",
- "filters_json": "[[\"Course Enrollment\",\"enrollment_date\",\"Timespan\",\"this year\",false]]",
- "group_by_based_on": "course",
- "group_by_type": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "last_synced_on": "2020-07-27 17:24:56.184236",
- "modified": "2020-07-27 17:25:46.232846",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Course wise Student Count",
- "number_of_groups": 0,
- "owner": "Administrator",
- "source": "",
- "time_interval": "Yearly",
- "timeseries": 0,
- "timespan": "Last Year",
- "type": "Donut",
- "use_report_chart": 0,
- "value_based_on": "",
- "y_axis": []
-}
\ No newline at end of file
diff --git a/erpnext/education/dashboard_chart/instructor_gender_diversity_ratio/instructor_gender_diversity_ratio.json b/erpnext/education/dashboard_chart/instructor_gender_diversity_ratio/instructor_gender_diversity_ratio.json
deleted file mode 100644
index b7ee509..0000000
--- a/erpnext/education/dashboard_chart/instructor_gender_diversity_ratio/instructor_gender_diversity_ratio.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "based_on": "",
- "chart_name": "Instructor Gender Diversity Ratio",
- "chart_type": "Group By",
- "creation": "2020-07-23 18:35:02.544019",
- "docstatus": 0,
- "doctype": "Dashboard Chart",
- "document_type": "Instructor",
- "dynamic_filters_json": "[]",
- "filters_json": "[[\"Instructor\",\"status\",\"=\",\"Active\",false]]",
- "group_by_based_on": "gender",
- "group_by_type": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "last_synced_on": "2020-07-27 17:50:32.783820",
- "modified": "2020-07-27 17:55:41.595260",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Instructor Gender Diversity Ratio",
- "number_of_groups": 0,
- "owner": "Administrator",
- "source": "",
- "time_interval": "Yearly",
- "timeseries": 0,
- "timespan": "Last Year",
- "type": "Donut",
- "use_report_chart": 0,
- "value_based_on": "",
- "y_axis": []
-}
\ No newline at end of file
diff --git a/erpnext/education/dashboard_chart/program_enrollments/program_enrollments.json b/erpnext/education/dashboard_chart/program_enrollments/program_enrollments.json
deleted file mode 100644
index 2a4a4a3..0000000
--- a/erpnext/education/dashboard_chart/program_enrollments/program_enrollments.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
- "based_on": "enrollment_date",
- "chart_name": "Program Enrollments",
- "chart_type": "Count",
- "creation": "2020-07-23 18:27:53.641616",
- "docstatus": 0,
- "doctype": "Dashboard Chart",
- "document_type": "Program Enrollment",
- "dynamic_filters_json": "[]",
- "filters_json": "[[\"Program Enrollment\",\"docstatus\",\"=\",\"1\",false]]",
- "group_by_type": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "last_synced_on": "2020-07-27 17:50:32.203069",
- "modified": "2020-07-27 17:51:59.022909",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Program Enrollments",
- "number_of_groups": 0,
- "owner": "Administrator",
- "source": "",
- "time_interval": "Daily",
- "timeseries": 1,
- "timespan": "Last Month",
- "type": "Line",
- "use_report_chart": 0,
- "value_based_on": "",
- "y_axis": []
-}
\ No newline at end of file
diff --git a/erpnext/education/dashboard_chart/program_wise_enrollment/program_wise_enrollment.json b/erpnext/education/dashboard_chart/program_wise_enrollment/program_wise_enrollment.json
deleted file mode 100644
index 2ba138e..0000000
--- a/erpnext/education/dashboard_chart/program_wise_enrollment/program_wise_enrollment.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "based_on": "",
- "chart_name": "Program wise Enrollment",
- "chart_type": "Group By",
- "creation": "2020-07-23 18:23:45.192748",
- "docstatus": 0,
- "doctype": "Dashboard Chart",
- "document_type": "Program Enrollment",
- "dynamic_filters_json": "[]",
- "filters_json": "[[\"Program Enrollment\",\"docstatus\",\"=\",\"1\",false],[\"Program Enrollment\",\"enrollment_date\",\"Timespan\",\"this year\",false]]",
- "group_by_based_on": "program",
- "group_by_type": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "last_synced_on": "2020-07-27 17:50:32.629321",
- "modified": "2020-07-27 17:53:36.269098",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Program wise Enrollment",
- "number_of_groups": 0,
- "owner": "Administrator",
- "source": "",
- "time_interval": "Yearly",
- "timeseries": 0,
- "timespan": "Last Year",
- "type": "Percentage",
- "use_report_chart": 0,
- "value_based_on": "",
- "y_axis": []
-}
\ No newline at end of file
diff --git a/erpnext/education/dashboard_chart/program_wise_fee_collection/program_wise_fee_collection.json b/erpnext/education/dashboard_chart/program_wise_fee_collection/program_wise_fee_collection.json
deleted file mode 100644
index 38c1b6d..0000000
--- a/erpnext/education/dashboard_chart/program_wise_fee_collection/program_wise_fee_collection.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "chart_name": "Program wise Fee Collection",
- "chart_type": "Report",
- "creation": "2020-08-05 16:19:53.398335",
- "custom_options": "",
- "docstatus": 0,
- "doctype": "Dashboard Chart",
- "dynamic_filters_json": "{\"from_date\":\"frappe.datetime.add_months(frappe.datetime.get_today(), -1)\",\"to_date\":\"frappe.datetime.nowdate()\"}",
- "filters_json": "{}",
- "group_by_type": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "modified": "2020-08-05 16:20:47.436847",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Program wise Fee Collection",
- "number_of_groups": 0,
- "owner": "Administrator",
- "report_name": "Program wise Fee Collection",
- "time_interval": "Yearly",
- "timeseries": 0,
- "timespan": "Last Year",
- "type": "Bar",
- "use_report_chart": 1,
- "x_field": "",
- "y_axis": []
-}
\ No newline at end of file
diff --git a/erpnext/education/dashboard_chart/student_category_wise_program_enrollments/student_category_wise_program_enrollments.json b/erpnext/education/dashboard_chart/student_category_wise_program_enrollments/student_category_wise_program_enrollments.json
deleted file mode 100644
index 8887145..0000000
--- a/erpnext/education/dashboard_chart/student_category_wise_program_enrollments/student_category_wise_program_enrollments.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "based_on": "",
- "chart_name": "Student Category wise Program Enrollments",
- "chart_type": "Group By",
- "creation": "2020-07-27 17:37:47.116446",
- "docstatus": 0,
- "doctype": "Dashboard Chart",
- "document_type": "Program Enrollment",
- "dynamic_filters_json": "[]",
- "filters_json": "[[\"Program Enrollment\",\"enrollment_date\",\"Timespan\",\"this year\",false],[\"Program Enrollment\",\"docstatus\",\"=\",\"1\",false]]",
- "group_by_based_on": "student_category",
- "group_by_type": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "last_synced_on": "2020-07-27 17:46:54.901911",
- "modified": "2020-07-27 17:47:21.370866",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Student Category wise Program Enrollments",
- "number_of_groups": 0,
- "owner": "Administrator",
- "source": "",
- "time_interval": "Yearly",
- "timeseries": 0,
- "timespan": "Last Year",
- "type": "Donut",
- "use_report_chart": 0,
- "value_based_on": "",
- "y_axis": []
-}
\ No newline at end of file
diff --git a/erpnext/education/dashboard_chart/student_gender_diversity_ratio/student_gender_diversity_ratio.json b/erpnext/education/dashboard_chart/student_gender_diversity_ratio/student_gender_diversity_ratio.json
deleted file mode 100644
index ce602d2..0000000
--- a/erpnext/education/dashboard_chart/student_gender_diversity_ratio/student_gender_diversity_ratio.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
- "based_on": "",
- "chart_name": "Student Gender Diversity Ratio",
- "chart_type": "Group By",
- "creation": "2020-07-23 18:12:15.972123",
- "docstatus": 0,
- "doctype": "Dashboard Chart",
- "document_type": "Student",
- "dynamic_filters_json": "[]",
- "filters_json": "[[\"Student\",\"enabled\",\"=\",1,false]]",
- "group_by_based_on": "gender",
- "group_by_type": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "modified": "2020-07-23 18:12:21.606772",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Student Gender Diversity Ratio",
- "number_of_groups": 0,
- "owner": "Administrator",
- "source": "",
- "time_interval": "Yearly",
- "timeseries": 0,
- "timespan": "Last Year",
- "type": "Donut",
- "use_report_chart": 0,
- "value_based_on": "",
- "y_axis": []
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/academic_term/__init__.py b/erpnext/education/doctype/academic_term/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/academic_term/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/academic_term/academic_term.js b/erpnext/education/doctype/academic_term/academic_term.js
deleted file mode 100644
index 26d66f0..0000000
--- a/erpnext/education/doctype/academic_term/academic_term.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Academic Term', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/academic_term/academic_term.json b/erpnext/education/doctype/academic_term/academic_term.json
deleted file mode 100644
index 06c5b1a..0000000
--- a/erpnext/education/doctype/academic_term/academic_term.json
+++ /dev/null
@@ -1,216 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 1, 
- "allow_rename": 1, 
- "autoname": "field:title", 
- "beta": 0, 
- "creation": "2015-09-08 17:19:19.158228", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Setup", 
- "editable_grid": 0, 
- "engine": "InnoDB", 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "academic_year", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 1, 
-   "label": "Academic Year", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Academic Year", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "term_name", 
-   "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": "Term 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": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "term_start_date", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Term Start Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 1, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "term_end_date", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Term End Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 1, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "title", 
-   "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": "Title", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "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": "2017-11-10 19:05:58.567627", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Academic Term", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [
-  {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Academics User", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "name", 
- "sort_order": "DESC", 
- "title_field": "title", 
- "track_changes": 0, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/academic_term/academic_term.py b/erpnext/education/doctype/academic_term/academic_term.py
deleted file mode 100644
index 93861ca..0000000
--- a/erpnext/education/doctype/academic_term/academic_term.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import getdate
-
-
-class AcademicTerm(Document):
-    def autoname(self):
-        self.name = self.academic_year + " ({})".format(self.term_name) if self.term_name else ""
-
-    def validate(self):
-        #Check if entry with same academic_year and the term_name already exists
-        validate_duplication(self)
-        self.title = self.academic_year + " ({})".format(self.term_name) if self.term_name else ""
-
-        #Check that start of academic year is earlier than end of academic year
-        if self.term_start_date and self.term_end_date \
-				and getdate(self.term_start_date) > getdate(self.term_end_date):
-            frappe.throw(_("The Term End Date cannot be earlier than the Term Start Date. Please correct the dates and try again."))
-
-        # Check that the start of the term is not before the start of the academic year
-		# and end of term is not after the end of the academic year"""
-
-        year = frappe.get_doc("Academic Year",self.academic_year)
-        if self.term_start_date and getdate(year.year_start_date) and (getdate(self.term_start_date) < getdate(year.year_start_date)):
-            frappe.throw(_("The Term Start Date cannot be earlier than the Year Start Date of the Academic Year to which the term is linked (Academic Year {}). Please correct the dates and try again.").format(self.academic_year))
-
-        if self.term_end_date and getdate(year.year_end_date) and (getdate(self.term_end_date) > getdate(year.year_end_date)):
-            frappe.throw(_("The Term End Date cannot be later than the Year End Date of the Academic Year to which the term is linked (Academic Year {}). Please correct the dates and try again.").format(self.academic_year))
-
-
-def validate_duplication(self):
-    term = frappe.db.sql("""select name from `tabAcademic Term` where academic_year= %s and term_name= %s
-    and docstatus<2 and name != %s""", (self.academic_year, self.term_name, self.name))
-    if term:
-        frappe.throw(_("An academic term with this 'Academic Year' {0} and 'Term Name' {1} already exists. Please modify these entries and try again.").format(self.academic_year,self.term_name))
diff --git a/erpnext/education/doctype/academic_term/academic_term_dashboard.py b/erpnext/education/doctype/academic_term/academic_term_dashboard.py
deleted file mode 100644
index c686b09..0000000
--- a/erpnext/education/doctype/academic_term/academic_term_dashboard.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from frappe import _
-
-
-def get_data():
-	return {
-		'fieldname': 'academic_term',
-		'transactions': [
-			{
-				'label': _('Student'),
-				'items': ['Student Applicant', 'Student Group', 'Student Log']
-			},
-			{
-				'label': _('Fee'),
-				'items': ['Fees', 'Fee Schedule', 'Fee Structure']
-			},
-			{
-				'label': _('Program'),
-				'items': ['Program Enrollment']
-			},
-			{
-				'label': _('Assessment'),
-				'items': ['Assessment Plan', 'Assessment Result']
-			}
-		]
-	}
diff --git a/erpnext/education/doctype/academic_term/test_academic_term.py b/erpnext/education/doctype/academic_term/test_academic_term.py
deleted file mode 100644
index 0e39fb0..0000000
--- a/erpnext/education/doctype/academic_term/test_academic_term.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and Contributors
-# See license.txt
-
-import unittest
-
-# test_records = frappe.get_test_records('Academic Term')
-
-class TestAcademicTerm(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/academic_term/test_records.json b/erpnext/education/doctype/academic_term/test_records.json
deleted file mode 100644
index 6bd3655..0000000
--- a/erpnext/education/doctype/academic_term/test_records.json
+++ /dev/null
@@ -1,27 +0,0 @@
-[
-    {
-        "doctype": "Academic Term",
-        "academic_year": "2014-2015",
-        "term_name": "_Test Academic Term"
-    },
-    {
-        "doctype": "Academic Term",
-        "academic_year": "2014-2015",
-        "term_name": "_Test Academic Term 1"
-    },
-    {
-        "doctype": "Academic Term",
-        "academic_year": "2014-2015",
-        "term_name": "_Test Academic Term 2"
-    },
-    {
-        "doctype": "Academic Term",
-        "academic_year": "2017-2018",
-        "term_name": "_Test AT1"
-    },
-    {
-        "doctype": "Academic Term",
-        "academic_year": "2017-2018",
-        "term_name": "_Test AT2"
-    }
-]    
\ No newline at end of file
diff --git a/erpnext/education/doctype/academic_year/__init__.py b/erpnext/education/doctype/academic_year/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/academic_year/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/academic_year/academic_year.js b/erpnext/education/doctype/academic_year/academic_year.js
deleted file mode 100644
index 20e2528..0000000
--- a/erpnext/education/doctype/academic_year/academic_year.js
+++ /dev/null
@@ -1,2 +0,0 @@
-frappe.ui.form.on("Academic Year", {
-});
diff --git a/erpnext/education/doctype/academic_year/academic_year.json b/erpnext/education/doctype/academic_year/academic_year.json
deleted file mode 100644
index 5df89a7..0000000
--- a/erpnext/education/doctype/academic_year/academic_year.json
+++ /dev/null
@@ -1,154 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 1, 
- "allow_rename": 0, 
- "autoname": "field:academic_year_name", 
- "beta": 0, 
- "creation": "2015-09-07 12:49:51.303026", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 0, 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "academic_year_name", 
-   "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": "Academic Year 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": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "year_start_date", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Year Start Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "year_end_date", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Year End Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "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": "2017-11-10 19:06:08.123090", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Academic Year", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [
-  {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Academics User", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "title_field": "", 
- "track_changes": 0, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/academic_year/academic_year.py b/erpnext/education/doctype/academic_year/academic_year.py
deleted file mode 100644
index e2010fb..0000000
--- a/erpnext/education/doctype/academic_year/academic_year.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-
-
-class AcademicYear(Document):
-    def validate(self):
-        #Check that start of academic year is earlier than end of academic year
-        if self.year_start_date and self.year_end_date and self.year_start_date > self.year_end_date:
-            frappe.throw(_("The Year End Date cannot be earlier than the Year Start Date. Please correct the dates and try again."))
diff --git a/erpnext/education/doctype/academic_year/academic_year_dashboard.py b/erpnext/education/doctype/academic_year/academic_year_dashboard.py
deleted file mode 100644
index ede2411..0000000
--- a/erpnext/education/doctype/academic_year/academic_year_dashboard.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from frappe import _
-
-
-def get_data():
-	return {
-		'fieldname': 'academic_year',
-		'transactions': [
-			{
-				'label': _('Student'),
-				'items': ['Student Admission', 'Student Applicant', 'Student Group', 'Student Log']
-			},
-			{
-				'label': _('Fee'),
-				'items': ['Fees', 'Fee Schedule', 'Fee Structure']
-			},
-			{
-				'label': _('Academic Term and Program'),
-				'items': ['Academic Term', 'Program Enrollment']
-			},
-			{
-				'label': _('Assessment'),
-				'items': ['Assessment Plan', 'Assessment Result']
-			}
-		]
-	}
diff --git a/erpnext/education/doctype/academic_year/test_academic_year.py b/erpnext/education/doctype/academic_year/test_academic_year.py
deleted file mode 100644
index 6d33fe6..0000000
--- a/erpnext/education/doctype/academic_year/test_academic_year.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and Contributors
-# See license.txt
-
-import unittest
-
-# test_records = frappe.get_test_records('Academic Year')
-
-class TestAcademicYear(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/academic_year/test_records.json b/erpnext/education/doctype/academic_year/test_records.json
deleted file mode 100644
index 5eb5e2e..0000000
--- a/erpnext/education/doctype/academic_year/test_records.json
+++ /dev/null
@@ -1,18 +0,0 @@
-[
-	{
-        "doctype": "Academic Year",
-		"academic_year_name": "2014-2015"
-	},
-	{
-        "doctype": "Academic Year",
-		"academic_year_name": "2015-2016"
-	},
-	{
-        "doctype": "Academic Year",
-		"academic_year_name": "2016-2017"
-	},
-	{
-        "doctype": "Academic Year",
-		"academic_year_name": "2017-2018"
-	}
-]
\ No newline at end of file
diff --git a/erpnext/education/doctype/article/__init__.py b/erpnext/education/doctype/article/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/article/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/article/article.js b/erpnext/education/doctype/article/article.js
deleted file mode 100644
index 85b387f..0000000
--- a/erpnext/education/doctype/article/article.js
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Article', {
-	refresh: function(frm) {
-		if (!frm.doc.__islocal) {
-			frm.add_custom_button(__('Add to Topics'), function() {
-				frm.trigger('add_article_to_topics');
-			}, __('Action'));
-		}
-	},
-
-	add_article_to_topics: function(frm) {
-		get_topics_without_article(frm.doc.name).then(r => {
-			if (r.message.length) {
-				frappe.prompt([
-					{
-						fieldname: 'topics',
-						label: __('Topics'),
-						fieldtype: 'MultiSelectPills',
-						get_data: function() {
-							return r.message;
-						}
-					}
-				],
-				function(data) {
-					frappe.call({
-						method: 'erpnext.education.doctype.topic.topic.add_content_to_topics',
-						args: {
-							'content_type': 'Article',
-							'content': frm.doc.name,
-							'topics': data.topics,
-						},
-						callback: function(r) {
-							if (!r.exc) {
-								frm.reload_doc();
-							}
-						},
-						freeze: true,
-						freeze_message: __('...Adding Article to Topics')
-					});
-				}, __('Add Article to Topics'), __('Add'));
-			} else {
-				frappe.msgprint(__('This article is already added to the existing topics'));
-			}
-		});
-	}
-});
-
-let get_topics_without_article = function(article) {
-	return frappe.call({
-		type: 'GET',
-		method: 'erpnext.education.doctype.article.article.get_topics_without_article',
-		args: {'article': article}
-	});
-};
diff --git a/erpnext/education/doctype/article/article.json b/erpnext/education/doctype/article/article.json
deleted file mode 100644
index 2fad5af..0000000
--- a/erpnext/education/doctype/article/article.json
+++ /dev/null
@@ -1,81 +0,0 @@
-{
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:title",
- "creation": "2018-10-17 05:45:38.471670",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "title",
-  "author",
-  "content",
-  "publish_date"
- ],
- "fields": [
-  {
-   "fieldname": "title",
-   "fieldtype": "Data",
-   "label": "Title",
-   "unique": 1
-  },
-  {
-   "fieldname": "author",
-   "fieldtype": "Data",
-   "label": "Author"
-  },
-  {
-   "fieldname": "content",
-   "fieldtype": "Text Editor",
-   "label": "Content"
-  },
-  {
-   "fieldname": "publish_date",
-   "fieldtype": "Date",
-   "label": "Publish Date"
-  }
- ],
- "modified": "2019-06-12 12:36:58.740340",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Article",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Instructor",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "LMS User",
-   "share": 1
-  }
- ],
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/article/article.py b/erpnext/education/doctype/article/article.py
deleted file mode 100644
index 8f1a2e3..0000000
--- a/erpnext/education/doctype/article/article.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe.model.document import Document
-
-
-class Article(Document):
-	def get_article(self):
-		pass
-
-@frappe.whitelist()
-def get_topics_without_article(article):
-	data = []
-	for entry in frappe.db.get_all('Topic'):
-		topic = frappe.get_doc('Topic', entry.name)
-		topic_contents = [tc.content for tc in topic.topic_content]
-		if not topic_contents or article not in topic_contents:
-			data.append(topic.name)
-	return data
diff --git a/erpnext/education/doctype/article/test_article.py b/erpnext/education/doctype/article/test_article.py
deleted file mode 100644
index 2ea5c82..0000000
--- a/erpnext/education/doctype/article/test_article.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestArticle(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/assessment_criteria/__init__.py b/erpnext/education/doctype/assessment_criteria/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/assessment_criteria/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/assessment_criteria/assessment_criteria.js b/erpnext/education/doctype/assessment_criteria/assessment_criteria.js
deleted file mode 100644
index 44b9ca3..0000000
--- a/erpnext/education/doctype/assessment_criteria/assessment_criteria.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Assessment Criteria', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/assessment_criteria/assessment_criteria.json b/erpnext/education/doctype/assessment_criteria/assessment_criteria.json
deleted file mode 100644
index 9e228fd..0000000
--- a/erpnext/education/doctype/assessment_criteria/assessment_criteria.json
+++ /dev/null
@@ -1,125 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 1, 
- "allow_rename": 0, 
- "autoname": "field:assessment_criteria", 
- "beta": 0, 
- "creation": "2016-12-14 16:40:15.144115", 
- "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": "assessment_criteria", 
-   "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": "Assessment Criteria", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 1, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "assessment_criteria_group", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Assessment Criteria Group", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Assessment Criteria Group", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2017-11-10 19:08:11.311304", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Assessment Criteria", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [
-  {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Academics User", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/assessment_criteria/assessment_criteria.py b/erpnext/education/doctype/assessment_criteria/assessment_criteria.py
deleted file mode 100644
index 58448ea..0000000
--- a/erpnext/education/doctype/assessment_criteria/assessment_criteria.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-
-STD_CRITERIA = ["total", "total score", "total grade", "maximum score", "score", "grade"]
-
-class AssessmentCriteria(Document):
-	def validate(self):
-		if self.assessment_criteria.lower() in STD_CRITERIA:
-			frappe.throw(_("Can't create standard criteria. Please rename the criteria"))
diff --git a/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.py b/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.py
deleted file mode 100644
index 40ba0e7..0000000
--- a/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-# test_records = frappe.get_test_records('Assessment Criteria')
-
-class TestAssessmentCriteria(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/assessment_criteria/test_records.json b/erpnext/education/doctype/assessment_criteria/test_records.json
deleted file mode 100644
index 7af63b3..0000000
--- a/erpnext/education/doctype/assessment_criteria/test_records.json
+++ /dev/null
@@ -1,8 +0,0 @@
-[
- {
-  "assessment_criteria": "_Test Assessment Criteria"
- }, 
- {
-  "assessment_criteria": "_Test Assessment Criteria 1"
- }
-]
\ No newline at end of file
diff --git a/erpnext/education/doctype/assessment_criteria_group/__init__.py b/erpnext/education/doctype/assessment_criteria_group/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/assessment_criteria_group/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/assessment_criteria_group/assessment_criteria_group.js b/erpnext/education/doctype/assessment_criteria_group/assessment_criteria_group.js
deleted file mode 100644
index 89358d2..0000000
--- a/erpnext/education/doctype/assessment_criteria_group/assessment_criteria_group.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Assessment Criteria Group', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/assessment_criteria_group/assessment_criteria_group.json b/erpnext/education/doctype/assessment_criteria_group/assessment_criteria_group.json
deleted file mode 100644
index 5765b4b..0000000
--- a/erpnext/education/doctype/assessment_criteria_group/assessment_criteria_group.json
+++ /dev/null
@@ -1,94 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 1, 
- "allow_rename": 1, 
- "autoname": "field:assessment_criteria_group", 
- "beta": 0, 
- "creation": "2017-01-27 15:17:38.855910", 
- "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": "assessment_criteria_group", 
-   "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": "Assessment Criteria Group", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2017-11-10 19:11:45.334917", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Assessment Criteria Group", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [
-  {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Academics User", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/assessment_criteria_group/assessment_criteria_group.py b/erpnext/education/doctype/assessment_criteria_group/assessment_criteria_group.py
deleted file mode 100644
index d284db5..0000000
--- a/erpnext/education/doctype/assessment_criteria_group/assessment_criteria_group.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class AssessmentCriteriaGroup(Document):
-	pass
diff --git a/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.py b/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.py
deleted file mode 100644
index ccf82ba..0000000
--- a/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-# test_records = frappe.get_test_records('Assessment Criteria Group')
-
-class TestAssessmentCriteriaGroup(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/assessment_group/__init__.py b/erpnext/education/doctype/assessment_group/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/assessment_group/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/assessment_group/assessment_group.js b/erpnext/education/doctype/assessment_group/assessment_group.js
deleted file mode 100644
index 6be5102..0000000
--- a/erpnext/education/doctype/assessment_group/assessment_group.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Assessment Group', {
-	onload: function(frm) {
-		frm.list_route = "Tree/Assessment Group";
-	}
-});
diff --git a/erpnext/education/doctype/assessment_group/assessment_group.json b/erpnext/education/doctype/assessment_group/assessment_group.json
deleted file mode 100644
index a8ffaf4..0000000
--- a/erpnext/education/doctype/assessment_group/assessment_group.json
+++ /dev/null
@@ -1,90 +0,0 @@
-{
- "actions": [],
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:assessment_group_name",
- "creation": "2016-08-04 04:42:48.319388",
- "doctype": "DocType",
- "editable_grid": 1,
- "field_order": [
-  "assessment_group_name",
-  "is_group",
-  "section_break_2",
-  "parent_assessment_group",
-  "lft",
-  "rgt",
-  "old_parent"
- ],
- "fields": [
-  {
-   "fieldname": "assessment_group_name",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Assessment Group Name",
-   "reqd": 1,
-   "unique": 1
-  },
-  {
-   "default": "0",
-   "fieldname": "is_group",
-   "fieldtype": "Check",
-   "label": "Is Group"
-  },
-  {
-   "fieldname": "section_break_2",
-   "fieldtype": "Section Break",
-   "hidden": 1
-  },
-  {
-   "fieldname": "parent_assessment_group",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Parent Assessment Group",
-   "options": "Assessment Group",
-   "reqd": 1
-  },
-  {
-   "fieldname": "lft",
-   "fieldtype": "Int",
-   "label": "lft"
-  },
-  {
-   "fieldname": "rgt",
-   "fieldtype": "Int",
-   "label": "rgt"
-  },
-  {
-   "fieldname": "old_parent",
-   "fieldtype": "Link",
-   "ignore_user_permissions": 1,
-   "label": "old_parent",
-   "options": "Assessment Group"
-  }
- ],
- "is_tree": 1,
- "links": [],
- "modified": "2020-03-18 18:01:14.710416",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Assessment Group",
- "nsm_parent_field": "parent_assessment_group",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "write": 1
-  }
- ],
- "quick_entry": 1,
- "restrict_to_domain": "Education",
- "sort_field": "modified",
- "sort_order": "DESC"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/assessment_group/assessment_group.py b/erpnext/education/doctype/assessment_group/assessment_group.py
deleted file mode 100644
index d606ffb..0000000
--- a/erpnext/education/doctype/assessment_group/assessment_group.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class AssessmentGroup(Document):
-	pass
diff --git a/erpnext/education/doctype/assessment_group/assessment_group_dashboard.py b/erpnext/education/doctype/assessment_group/assessment_group_dashboard.py
deleted file mode 100644
index 9568091..0000000
--- a/erpnext/education/doctype/assessment_group/assessment_group_dashboard.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from frappe import _
-
-
-def get_data():
-	return {
-		'fieldname': 'assessment_group',
-		'transactions': [
-			{
-				'label': _('Assessment'),
-				'items': ['Assessment Plan', 'Assessment Result']
-			}
-		]
-	}
diff --git a/erpnext/education/doctype/assessment_group/assessment_group_tree.js b/erpnext/education/doctype/assessment_group/assessment_group_tree.js
deleted file mode 100644
index e0dfaa3..0000000
--- a/erpnext/education/doctype/assessment_group/assessment_group_tree.js
+++ /dev/null
@@ -1,3 +0,0 @@
-frappe.treeview_settings["Assessment Group"] = {
-
-}
diff --git a/erpnext/education/doctype/assessment_group/test_assessment_group.py b/erpnext/education/doctype/assessment_group/test_assessment_group.py
deleted file mode 100644
index 6e840aa..0000000
--- a/erpnext/education/doctype/assessment_group/test_assessment_group.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-# test_records = frappe.get_test_records('Assessment Group')
-
-class TestAssessmentGroup(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/assessment_plan/__init__.py b/erpnext/education/doctype/assessment_plan/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/assessment_plan/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/assessment_plan/assessment_plan.js b/erpnext/education/doctype/assessment_plan/assessment_plan.js
deleted file mode 100644
index cf545c4..0000000
--- a/erpnext/education/doctype/assessment_plan/assessment_plan.js
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-
-frappe.ui.form.on('Assessment Plan', {
-	onload: function(frm) {
-		frm.set_query('assessment_group', function(doc, cdt, cdn) {
-			return{
-				filters: {
-					'is_group': 0
-				}
-			};
-		});
-		frm.set_query('grading_scale', function(){
-			return {
-				filters: {
-					docstatus: 1
-				}
-			};
-		});
-	},
-
-	refresh: function(frm) {
-		if (frm.doc.docstatus == 1) {
-			frm.add_custom_button(__('Assessment Result Tool'), function() {
-				frappe.route_options = {
-					assessment_plan: frm.doc.name,
-					student_group: frm.doc.student_group
-				}
-				frappe.set_route('Form', 'Assessment Result Tool');
-			}, __('Tools'));
-		}
-
-		frm.set_query('course', function() {
-			return {
-				query: 'erpnext.education.doctype.program_enrollment.program_enrollment.get_program_courses',
-				filters: {
-					'program': frm.doc.program
-				}
-			};
-		});
-
-		frm.set_query('academic_term', function() {
-			return {
-				filters: {
-					'academic_year': frm.doc.academic_year
-				}
-			};
-		});
-	},
-
-	course: function(frm) {
-		if (frm.doc.course && frm.doc.maximum_assessment_score) {
-			frappe.call({
-				method: 'erpnext.education.api.get_assessment_criteria',
-				args: {
-					course: frm.doc.course
-				},
-				callback: function(r) {
-					if (r.message) {
-						frm.doc.assessment_criteria = [];
-						$.each(r.message, function(i, d) {
-							var row = frappe.model.add_child(frm.doc, 'Assessment Plan Criteria', 'assessment_criteria');
-							row.assessment_criteria = d.assessment_criteria;
-							row.maximum_score = d.weightage / 100 * frm.doc.maximum_assessment_score;
-						});
-					}
-					refresh_field('assessment_criteria');
-
-				}
-			});
-		}
-	},
-
-	maximum_assessment_score: function(frm) {
-		frm.trigger('course');
-	}
-});
diff --git a/erpnext/education/doctype/assessment_plan/assessment_plan.json b/erpnext/education/doctype/assessment_plan/assessment_plan.json
deleted file mode 100644
index 5066fdf..0000000
--- a/erpnext/education/doctype/assessment_plan/assessment_plan.json
+++ /dev/null
@@ -1,227 +0,0 @@
-{
- "actions": [],
- "allow_import": 1,
- "autoname": "EDU-ASP-.YYYY.-.#####",
- "creation": "2015-11-12 16:34:34.658092",
- "doctype": "DocType",
- "document_type": "Setup",
- "engine": "InnoDB",
- "field_order": [
-  "student_group",
-  "assessment_name",
-  "assessment_group",
-  "grading_scale",
-  "column_break_2",
-  "program",
-  "course",
-  "academic_year",
-  "academic_term",
-  "section_break_5",
-  "schedule_date",
-  "room",
-  "examiner",
-  "examiner_name",
-  "column_break_4",
-  "from_time",
-  "to_time",
-  "supervisor",
-  "supervisor_name",
-  "section_break_20",
-  "maximum_assessment_score",
-  "assessment_criteria",
-  "amended_from"
- ],
- "fields": [
-  {
-   "fieldname": "student_group",
-   "fieldtype": "Link",
-   "in_global_search": 1,
-   "in_list_view": 1,
-   "in_standard_filter": 1,
-   "label": "Student Group",
-   "options": "Student Group",
-   "reqd": 1
-  },
-  {
-   "fieldname": "assessment_name",
-   "fieldtype": "Data",
-   "in_global_search": 1,
-   "label": "Assessment Name"
-  },
-  {
-   "fieldname": "assessment_group",
-   "fieldtype": "Link",
-   "in_standard_filter": 1,
-   "label": "Assessment Group",
-   "options": "Assessment Group",
-   "reqd": 1
-  },
-  {
-   "fetch_from": "course.default_grading_scale",
-   "fetch_if_empty": 1,
-   "fieldname": "grading_scale",
-   "fieldtype": "Link",
-   "in_standard_filter": 1,
-   "label": "Grading Scale",
-   "options": "Grading Scale",
-   "reqd": 1
-  },
-  {
-   "fieldname": "column_break_2",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fetch_from": "student_group.course",
-   "fetch_if_empty": 1,
-   "fieldname": "course",
-   "fieldtype": "Link",
-   "in_global_search": 1,
-   "in_standard_filter": 1,
-   "label": "Course",
-   "options": "Course",
-   "reqd": 1
-  },
-  {
-   "fetch_from": "student_group.program",
-   "fieldname": "program",
-   "fieldtype": "Link",
-   "in_global_search": 1,
-   "label": "Program",
-   "options": "Program"
-  },
-  {
-   "fetch_from": "student_group.academic_year",
-   "fieldname": "academic_year",
-   "fieldtype": "Link",
-   "label": "Academic Year",
-   "options": "Academic Year"
-  },
-  {
-   "fetch_from": "student_group.academic_term",
-   "fieldname": "academic_term",
-   "fieldtype": "Link",
-   "label": "Academic Term",
-   "options": "Academic Term"
-  },
-  {
-   "fieldname": "section_break_5",
-   "fieldtype": "Section Break",
-   "label": "Schedule"
-  },
-  {
-   "default": "Today",
-   "fieldname": "schedule_date",
-   "fieldtype": "Date",
-   "in_list_view": 1,
-   "label": "Schedule Date",
-   "no_copy": 1,
-   "reqd": 1
-  },
-  {
-   "fieldname": "room",
-   "fieldtype": "Link",
-   "label": "Room",
-   "options": "Room"
-  },
-  {
-   "fieldname": "examiner",
-   "fieldtype": "Link",
-   "label": "Examiner",
-   "options": "Instructor"
-  },
-  {
-   "fetch_from": "examiner.instructor_name",
-   "fieldname": "examiner_name",
-   "fieldtype": "Data",
-   "label": "Examiner Name",
-   "read_only": 1
-  },
-  {
-   "fieldname": "column_break_4",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "from_time",
-   "fieldtype": "Time",
-   "label": "From Time",
-   "no_copy": 1,
-   "reqd": 1
-  },
-  {
-   "fieldname": "to_time",
-   "fieldtype": "Time",
-   "label": "To Time",
-   "no_copy": 1,
-   "reqd": 1
-  },
-  {
-   "fieldname": "supervisor",
-   "fieldtype": "Link",
-   "label": "Supervisor",
-   "options": "Instructor"
-  },
-  {
-   "fetch_from": "supervisor.instructor_name",
-   "fieldname": "supervisor_name",
-   "fieldtype": "Data",
-   "in_global_search": 1,
-   "label": "Supervisor Name",
-   "read_only": 1
-  },
-  {
-   "fieldname": "section_break_20",
-   "fieldtype": "Section Break",
-   "label": "Evaluate"
-  },
-  {
-   "fieldname": "maximum_assessment_score",
-   "fieldtype": "Float",
-   "label": "Maximum Assessment Score",
-   "reqd": 1
-  },
-  {
-   "fieldname": "assessment_criteria",
-   "fieldtype": "Table",
-   "label": "Assessment Criteria",
-   "options": "Assessment Plan Criteria",
-   "reqd": 1
-  },
-  {
-   "fieldname": "amended_from",
-   "fieldtype": "Link",
-   "label": "Amended From",
-   "no_copy": 1,
-   "options": "Assessment Plan",
-   "print_hide": 1,
-   "read_only": 1
-  }
- ],
- "is_submittable": 1,
- "links": [],
- "modified": "2020-10-23 15:55:35.076251",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Assessment Plan",
- "owner": "Administrator",
- "permissions": [
-  {
-   "amend": 1,
-   "cancel": 1,
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "submit": 1,
-   "write": 1
-  }
- ],
- "restrict_to_domain": "Education",
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "assessment_name"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/assessment_plan/assessment_plan.py b/erpnext/education/doctype/assessment_plan/assessment_plan.py
deleted file mode 100644
index 82a28de..0000000
--- a/erpnext/education/doctype/assessment_plan/assessment_plan.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-
-
-class AssessmentPlan(Document):
-	def validate(self):
-		self.validate_overlap()
-		self.validate_max_score()
-		self.validate_assessment_criteria()
-
-	def validate_overlap(self):
-		"""Validates overlap for Student Group, Instructor, Room"""
-
-		from erpnext.education.utils import validate_overlap_for
-
-		#Validate overlapping course schedules.
-		if self.student_group:
-			validate_overlap_for(self, "Course Schedule", "student_group")
-
-		validate_overlap_for(self, "Course Schedule", "instructor")
-		validate_overlap_for(self, "Course Schedule", "room")
-
-		#validate overlapping assessment schedules.
-		if self.student_group:
-			validate_overlap_for(self, "Assessment Plan", "student_group")
-
-		validate_overlap_for(self, "Assessment Plan", "room")
-		validate_overlap_for(self, "Assessment Plan", "supervisor", self.supervisor)
-
-	def validate_max_score(self):
-		max_score = 0
-		for d in self.assessment_criteria:
-			max_score += d.maximum_score
-		if self.maximum_assessment_score != max_score:
-			frappe.throw(_("Sum of Scores of Assessment Criteria needs to be {0}.").format(self.maximum_assessment_score))
-
-	def validate_assessment_criteria(self):
-		assessment_criteria_list = frappe.db.sql_list(''' select apc.assessment_criteria
-			from `tabAssessment Plan` ap , `tabAssessment Plan Criteria` apc
-			where ap.name = apc.parent and ap.course=%s and ap.student_group=%s and ap.assessment_group=%s
-			and ap.name != %s and ap.docstatus=1''', (self.course, self.student_group, self.assessment_group, self.name))
-		for d in self.assessment_criteria:
-			if d.assessment_criteria in assessment_criteria_list:
-				frappe.throw(_("You have already assessed for the assessment criteria {}.")
-					.format(frappe.bold(d.assessment_criteria)))
diff --git a/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py b/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py
deleted file mode 100644
index 31b9509..0000000
--- a/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from frappe import _
-
-
-def get_data():
-	return {
-		'fieldname': 'assessment_plan',
-		'transactions': [
-			{
-				'label': _('Assessment'),
-				'items': ['Assessment Result']
-			}
-		],
-		'reports': [
-			{
-				'label': _('Report'),
-				'items': ['Assessment Plan Status']
-			}
-		]
-	}
diff --git a/erpnext/education/doctype/assessment_plan/test_assessment_plan.py b/erpnext/education/doctype/assessment_plan/test_assessment_plan.py
deleted file mode 100644
index 9f55a78..0000000
--- a/erpnext/education/doctype/assessment_plan/test_assessment_plan.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-# test_records = frappe.get_test_records('Assessment Plan')
-
-class TestAssessmentPlan(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/assessment_plan_criteria/__init__.py b/erpnext/education/doctype/assessment_plan_criteria/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/assessment_plan_criteria/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/assessment_plan_criteria/assessment_plan_criteria.json b/erpnext/education/doctype/assessment_plan_criteria/assessment_plan_criteria.json
deleted file mode 100644
index d9ad0cb..0000000
--- a/erpnext/education/doctype/assessment_plan_criteria/assessment_plan_criteria.json
+++ /dev/null
@@ -1,134 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "autoname": "", 
- "beta": 0, 
- "creation": "2016-12-14 17:20:27.738226", 
- "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": "assessment_criteria", 
-   "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": "Assessment Criteria", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Assessment Criteria", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_2", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "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, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "maximum_score", 
-   "fieldtype": "Float", 
-   "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": "Maximum Score", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2017-11-10 19:10:50.560006", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Assessment Plan Criteria", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/assessment_plan_criteria/assessment_plan_criteria.py b/erpnext/education/doctype/assessment_plan_criteria/assessment_plan_criteria.py
deleted file mode 100644
index 2cd17d6..0000000
--- a/erpnext/education/doctype/assessment_plan_criteria/assessment_plan_criteria.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class AssessmentPlanCriteria(Document):
-	pass
diff --git a/erpnext/education/doctype/assessment_result/__init__.py b/erpnext/education/doctype/assessment_result/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/assessment_result/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/assessment_result/assessment_result.js b/erpnext/education/doctype/assessment_result/assessment_result.js
deleted file mode 100644
index b6d2881..0000000
--- a/erpnext/education/doctype/assessment_result/assessment_result.js
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Assessment Result', {
-	refresh: function(frm) {
-		if (!frm.doc.__islocal) {
-			frm.trigger('setup_chart');
-		}
-
-		frm.get_field('details').grid.cannot_add_rows = true;
-
-		frm.set_query('course', function() {
-			return {
-				query: 'erpnext.education.doctype.program_enrollment.program_enrollment.get_program_courses',
-				filters: {
-					'program': frm.doc.program
-				}
-			};
-		});
-
-		frm.set_query('academic_term', function() {
-			return {
-				filters: {
-					'academic_year': frm.doc.academic_year
-				}
-			};
-		});
-	},
-
-	onload: function(frm) {
-		frm.set_query('assessment_plan', function() {
-			return {
-				filters: {
-					docstatus: 1
-				}
-			};
-		});
-	},
-
-	assessment_plan: function(frm) {
-		if (frm.doc.assessment_plan) {
-			frappe.call({
-				method: 'erpnext.education.api.get_assessment_details',
-				args: {
-					assessment_plan: frm.doc.assessment_plan
-				},
-				callback: function(r) {
-					if (r.message) {
-						frappe.model.clear_table(frm.doc, 'details');
-						$.each(r.message, function(i, d) {
-							var row = frm.add_child('details');
-							row.assessment_criteria = d.assessment_criteria;
-							row.maximum_score = d.maximum_score;
-						});
-						frm.refresh_field('details');
-					}
-				}
-			});
-		}
-	},
-
-	setup_chart: function(frm) {
-		let labels = [];
-		let maximum_scores = [];
-		let scores = [];
-		$.each(frm.doc.details, function(_i, e) {
-			labels.push(e.assessment_criteria);
-			maximum_scores.push(e.maximum_score);
-			scores.push(e.score);
-		});
-
-		if (labels.length && maximum_scores.length && scores.length) {
-			frm.dashboard.chart_area.empty().removeClass('hidden');
-			new frappe.Chart('.form-graph', {
-				title: 'Assessment Results',
-				data: {
-					labels: labels,
-					datasets: [
-						{
-							name: 'Maximum Score',
-							chartType: 'bar',
-							values: maximum_scores,
-						},
-						{
-							name: 'Score Obtained',
-							chartType: 'bar',
-							values: scores,
-						}
-					]
-				},
-				colors: ['#4CA746', '#98D85B'],
-				type: 'bar'
-			});
-		}
-	}
-});
-
-frappe.ui.form.on('Assessment Result Detail', {
-	score: function(frm, cdt, cdn) {
-		var d  = locals[cdt][cdn];
-
-		if (!d.maximum_score || !frm.doc.grading_scale) {
-			d.score = '';
-			frappe.throw(__('Please fill in all the details to generate Assessment Result.'));
-		}
-
-		if (d.score > d.maximum_score) {
-			frappe.throw(__('Score cannot be greater than Maximum Score'));
-		}
-		else {
-			frappe.call({
-				method: 'erpnext.education.api.get_grade',
-				args: {
-					grading_scale: frm.doc.grading_scale,
-					percentage: ((d.score/d.maximum_score) * 100)
-				},
-				callback: function(r) {
-					if (r.message) {
-						frappe.model.set_value(cdt, cdn, 'grade', r.message);
-					}
-				}
-			});
-		}
-	}
-});
diff --git a/erpnext/education/doctype/assessment_result/assessment_result.json b/erpnext/education/doctype/assessment_result/assessment_result.json
deleted file mode 100644
index 7a893aa..0000000
--- a/erpnext/education/doctype/assessment_result/assessment_result.json
+++ /dev/null
@@ -1,203 +0,0 @@
-{
- "actions": [],
- "allow_import": 1,
- "autoname": "EDU-RES-.YYYY.-.#####",
- "creation": "2015-11-13 17:18:06.468332",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "assessment_plan",
-  "program",
-  "course",
-  "academic_year",
-  "academic_term",
-  "column_break_3",
-  "student",
-  "student_name",
-  "student_group",
-  "assessment_group",
-  "grading_scale",
-  "section_break_5",
-  "details",
-  "section_break_8",
-  "maximum_score",
-  "column_break_11",
-  "total_score",
-  "grade",
-  "section_break_13",
-  "comment",
-  "amended_from"
- ],
- "fields": [
-  {
-   "fieldname": "assessment_plan",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Assessment Plan",
-   "options": "Assessment Plan",
-   "reqd": 1
-  },
-  {
-   "fetch_from": "assessment_plan.program",
-   "fieldname": "program",
-   "fieldtype": "Link",
-   "label": "Program",
-   "options": "Program"
-  },
-  {
-   "fetch_from": "assessment_plan.course",
-   "fieldname": "course",
-   "fieldtype": "Link",
-   "label": "Course",
-   "options": "Course"
-  },
-  {
-   "fetch_from": "assessment_plan.academic_year",
-   "fieldname": "academic_year",
-   "fieldtype": "Link",
-   "label": "Academic Year",
-   "options": "Academic Year"
-  },
-  {
-   "fetch_from": "assessment_plan.academic_term",
-   "fieldname": "academic_term",
-   "fieldtype": "Link",
-   "label": "Academic Term",
-   "options": "Academic Term"
-  },
-  {
-   "fieldname": "column_break_3",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "student",
-   "fieldtype": "Link",
-   "in_global_search": 1,
-   "label": "Student",
-   "options": "Student",
-   "reqd": 1
-  },
-  {
-   "fetch_from": "student.title",
-   "fieldname": "student_name",
-   "fieldtype": "Data",
-   "in_global_search": 1,
-   "in_list_view": 1,
-   "label": "Student Name",
-   "read_only": 1
-  },
-  {
-   "fetch_from": "assessment_plan.student_group",
-   "fieldname": "student_group",
-   "fieldtype": "Link",
-   "label": "Student Group",
-   "options": "Student Group"
-  },
-  {
-   "fetch_from": "assessment_plan.assessment_group",
-   "fieldname": "assessment_group",
-   "fieldtype": "Link",
-   "label": "Assessment Group",
-   "options": "Assessment Group"
-  },
-  {
-   "fetch_from": "assessment_plan.grading_scale",
-   "fieldname": "grading_scale",
-   "fieldtype": "Link",
-   "label": "Grading Scale",
-   "options": "Grading Scale",
-   "read_only": 1
-  },
-  {
-   "fieldname": "section_break_5",
-   "fieldtype": "Section Break",
-   "label": "Result"
-  },
-  {
-   "fieldname": "details",
-   "fieldtype": "Table",
-   "label": "Details",
-   "options": "Assessment Result Detail",
-   "reqd": 1
-  },
-  {
-   "fieldname": "section_break_8",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fetch_from": "assessment_plan.maximum_assessment_score",
-   "fieldname": "maximum_score",
-   "fieldtype": "Float",
-   "label": "Maximum Score",
-   "read_only": 1
-  },
-  {
-   "fetch_from": "assessment_plan.maximum_assessment_score",
-   "fieldname": "column_break_11",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "total_score",
-   "fieldtype": "Float",
-   "in_list_view": 1,
-   "label": "Total Score",
-   "read_only": 1
-  },
-  {
-   "fieldname": "grade",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Grade",
-   "read_only": 1
-  },
-  {
-   "fieldname": "section_break_13",
-   "fieldtype": "Section Break",
-   "label": "Summary"
-  },
-  {
-   "fieldname": "comment",
-   "fieldtype": "Small Text",
-   "label": "Comment"
-  },
-  {
-   "fieldname": "amended_from",
-   "fieldtype": "Link",
-   "label": "Amended From",
-   "no_copy": 1,
-   "options": "Assessment Result",
-   "print_hide": 1,
-   "read_only": 1
-  }
- ],
- "is_submittable": 1,
- "links": [],
- "modified": "2020-08-03 11:47:54.119486",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Assessment Result",
- "owner": "Administrator",
- "permissions": [
-  {
-   "amend": 1,
-   "cancel": 1,
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "submit": 1,
-   "write": 1
-  }
- ],
- "restrict_to_domain": "Education",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "student_name"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/assessment_result/assessment_result.py b/erpnext/education/doctype/assessment_result/assessment_result.py
deleted file mode 100644
index 8278b9e..0000000
--- a/erpnext/education/doctype/assessment_result/assessment_result.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import flt
-from frappe.utils.csvutils import getlink
-
-import erpnext.education
-from erpnext.education.api import get_assessment_details, get_grade
-
-
-class AssessmentResult(Document):
-	def validate(self):
-		erpnext.education.validate_student_belongs_to_group(self.student, self.student_group)
-		self.validate_maximum_score()
-		self.validate_grade()
-		self.validate_duplicate()
-
-	def validate_maximum_score(self):
-		assessment_details = get_assessment_details(self.assessment_plan)
-		max_scores = {}
-		for d in assessment_details:
-			max_scores.update({d.assessment_criteria: d.maximum_score})
-
-		for d in self.details:
-			d.maximum_score = max_scores.get(d.assessment_criteria)
-			if d.score > d.maximum_score:
-				frappe.throw(_("Score cannot be greater than Maximum Score"))
-
-	def validate_grade(self):
-		self.total_score = 0.0
-		for d in self.details:
-			d.grade = get_grade(self.grading_scale, (flt(d.score)/d.maximum_score)*100)
-			self.total_score += d.score
-		self.grade = get_grade(self.grading_scale, (self.total_score/self.maximum_score)*100)
-
-	def validate_duplicate(self):
-		assessment_result = frappe.get_list("Assessment Result", filters={"name": ("not in", [self.name]),
-			"student":self.student, "assessment_plan":self.assessment_plan, "docstatus":("!=", 2)})
-		if assessment_result:
-			frappe.throw(_("Assessment Result record {0} already exists.").format(getlink("Assessment Result",assessment_result[0].name)))
diff --git a/erpnext/education/doctype/assessment_result/assessment_result_dashboard.py b/erpnext/education/doctype/assessment_result/assessment_result_dashboard.py
deleted file mode 100644
index 3b07417..0000000
--- a/erpnext/education/doctype/assessment_result/assessment_result_dashboard.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from frappe import _
-
-
-def get_data():
-	return {
-		'reports': [
-			{
-				'label': _('Reports'),
-				'items': ['Final Assessment Grades', 'Course wise Assessment Report']
-			}
-		]
-	}
diff --git a/erpnext/education/doctype/assessment_result/test_assessment_result.py b/erpnext/education/doctype/assessment_result/test_assessment_result.py
deleted file mode 100644
index c0872df..0000000
--- a/erpnext/education/doctype/assessment_result/test_assessment_result.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-from erpnext.education.api import get_grade
-
-# test_records = frappe.get_test_records('Assessment Result')
-
-class TestAssessmentResult(unittest.TestCase):
-	def test_grade(self):
-		grade = get_grade("_Test Grading Scale", 80)
-		self.assertEqual("A", grade)
-
-		grade = get_grade("_Test Grading Scale", 70)
-		self.assertEqual("B", grade)
diff --git a/erpnext/education/doctype/assessment_result_detail/__init__.py b/erpnext/education/doctype/assessment_result_detail/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/assessment_result_detail/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/assessment_result_detail/assessment_result_detail.json b/erpnext/education/doctype/assessment_result_detail/assessment_result_detail.json
deleted file mode 100644
index 450f41c..0000000
--- a/erpnext/education/doctype/assessment_result_detail/assessment_result_detail.json
+++ /dev/null
@@ -1,66 +0,0 @@
-{
- "actions": [],
- "creation": "2016-12-14 17:44:35.583123",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "assessment_criteria",
-  "maximum_score",
-  "column_break_2",
-  "score",
-  "grade"
- ],
- "fields": [
-  {
-   "columns": 4,
-   "fieldname": "assessment_criteria",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Assessment Criteria",
-   "options": "Assessment Criteria",
-   "read_only": 1,
-   "reqd": 1
-  },
-  {
-   "columns": 2,
-   "fieldname": "maximum_score",
-   "fieldtype": "Float",
-   "in_list_view": 1,
-   "label": "Maximum Score",
-   "read_only": 1
-  },
-  {
-   "fieldname": "column_break_2",
-   "fieldtype": "Column Break"
-  },
-  {
-   "columns": 2,
-   "fieldname": "score",
-   "fieldtype": "Float",
-   "in_list_view": 1,
-   "label": "Score",
-   "reqd": 1
-  },
-  {
-   "columns": 2,
-   "fieldname": "grade",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Grade",
-   "read_only": 1
-  }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-07-31 13:27:17.699022",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Assessment Result Detail",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "restrict_to_domain": "Education",
- "sort_field": "modified",
- "sort_order": "DESC"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/assessment_result_detail/assessment_result_detail.py b/erpnext/education/doctype/assessment_result_detail/assessment_result_detail.py
deleted file mode 100644
index 5ef1129..0000000
--- a/erpnext/education/doctype/assessment_result_detail/assessment_result_detail.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class AssessmentResultDetail(Document):
-	pass
diff --git a/erpnext/education/doctype/assessment_result_tool/__init__.py b/erpnext/education/doctype/assessment_result_tool/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/assessment_result_tool/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.js b/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.js
deleted file mode 100644
index 053f0c2..0000000
--- a/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.js
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-
-frappe.ui.form.on('Assessment Result Tool', {
-	setup: function(frm) {
-		frm.add_fetch("assessment_plan", "student_group", "student_group");
-	},
-
-	refresh: function(frm) {
-		if (frappe.route_options) {
-			frm.set_value("student_group", frappe.route_options.student_group);
-			frm.set_value("assessment_plan", frappe.route_options.assessment_plan);
-			frappe.route_options = null;
-		} else {
-			frm.trigger("assessment_plan");
-		}
-		frm.disable_save();
-		frm.page.clear_indicator();
-	},
-
-	assessment_plan: function(frm) {
-		frm.doc.show_submit = false;
-		if(frm.doc.assessment_plan) {
-			if (!frm.doc.student_group)
-				return
-			frappe.call({
-				method: "erpnext.education.api.get_assessment_students",
-				args: {
-					"assessment_plan": frm.doc.assessment_plan,
-					"student_group": frm.doc.student_group
-				},
-				callback: function(r) {
-					if (r.message) {
-						frm.doc.students = r.message;
-						frm.events.render_table(frm);
-						for (let value of r.message) {
-							if (!value.docstatus) {
-								frm.doc.show_submit = true;
-								break;
-							}
-						}
-						frm.events.submit_result(frm);
-					}
-				}
-			});
-		}
-	},
-
-	render_table: function(frm) {
-		$(frm.fields_dict.result_html.wrapper).empty();
-		let assessment_plan = frm.doc.assessment_plan;
-		frappe.call({
-			method: "erpnext.education.api.get_assessment_details",
-			args: {
-				assessment_plan: assessment_plan
-			},
-			callback: function(r) {
-				frm.events.get_marks(frm, r.message);
-			}
-		});
-	},
-
-	get_marks: function(frm, criteria_list) {
-		let max_total_score = 0;
-		criteria_list.forEach(function(c) {
-			max_total_score += c.maximum_score
-		});
-		var result_table = $(frappe.render_template('assessment_result_tool', {
-			frm: frm,
-			students: frm.doc.students,
-			criteria: criteria_list,
-			max_total_score: max_total_score
-		}));
-		result_table.appendTo(frm.fields_dict.result_html.wrapper);
-
-		result_table.on('change', 'input', function(e) {
-			let $input = $(e.target);
-			let student = $input.data().student;
-			let max_score = $input.data().maxScore;
-			let value = $input.val();
-			if(value < 0) {
-				$input.val(0);
-			} else if(value > max_score) {
-				$input.val(max_score);
-			}
-			let total_score = 0;
-			let student_scores = {};
-			student_scores["assessment_details"] = {}
-			result_table.find(`input[data-student=${student}].student-result-data`)
-				.each(function(el, input) {
-					let $input = $(input);
-					let criteria = $input.data().criteria;
-					let value = parseFloat($input.val());
-					if (!Number.isNaN(value)) {
-						student_scores["assessment_details"][criteria] = value;
-					}
-					total_score += value;
-			});
-			if(!Number.isNaN(total_score)) {
-				result_table.find(`span[data-student=${student}].total-score`).html(total_score);
-			}
-			if (Object.keys(student_scores["assessment_details"]).length === criteria_list.length) {
-				student_scores["student"] = student;
-				student_scores["total_score"] = total_score;
-				result_table.find(`[data-student=${student}].result-comment`)
-					.each(function(el, input){
-					student_scores["comment"] = $(input).val();
-				});
-				frappe.call({
-					method: "erpnext.education.api.mark_assessment_result",
-					args: {
-						"assessment_plan": frm.doc.assessment_plan,
-						"scores": student_scores
-					},
-					callback: function(r) {
-						let assessment_result = r.message;
-						if (!frm.doc.show_submit) {
-							frm.doc.show_submit = true;
-							frm.events.submit_result;
-						}
-						for (var criteria of Object.keys(assessment_result.details)) {
-							result_table.find(`[data-criteria=${criteria}][data-student=${assessment_result
-								.student}].student-result-grade`).each(function(e1, input) {
-									$(input).html(assessment_result.details[criteria]);
-							});
-						}
-						result_table.find(`span[data-student=${assessment_result.student}].total-score-grade`).html(assessment_result.grade);
-						let link_span = result_table.find(`span[data-student=${assessment_result.student}].total-result-link`);
-						$(link_span).css("display", "block");
-						$(link_span).find("a").attr("href", "/app/assessment-result/"+assessment_result.name);
-					}
-				});
-			}
-		});
-	},
-
-	submit_result: function(frm) {
-		if (frm.doc.show_submit) {
-			frm.page.set_primary_action(__("Submit"), function() {
-				frappe.call({
-					method: "erpnext.education.api.submit_assessment_results",
-					args: {
-						"assessment_plan": frm.doc.assessment_plan,
-						"student_group": frm.doc.student_group
-					},
-					callback: function(r) {
-						if (r.message) {
-							frappe.msgprint(__("{0} Result submittted", [r.message]));
-						} else {
-							frappe.msgprint(__("No Result to submit"));
-						}
-						frm.events.assessment_plan(frm);
-					}
-				});
-			});
-		}
-		else {
-			frm.page.clear_primary_action();
-		}
-	}
-});
diff --git a/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.json b/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.json
deleted file mode 100644
index ddef100..0000000
--- a/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.json
+++ /dev/null
@@ -1,235 +0,0 @@
-{
- "allow_copy": 1, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2017-01-05 12:27:48.951036", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "", 
-   "fieldname": "assessment_plan", 
-   "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": "Assessment Plan", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Assessment Plan", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_2", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "student_group", 
-   "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": "Student Group", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student Group", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "assessment_plan", 
-   "fieldname": "section_break_5", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "result_html", 
-   "fieldtype": "HTML", 
-   "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": "Result HTML", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 1, 
- "hide_toolbar": 1, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 1, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2017-12-27 09:36:37.155890", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Assessment Result Tool", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [
-  {
-   "amend": 0, 
-   "apply_user_permissions": 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": "Academics User", 
-   "set_user_permissions": 0, 
-   "share": 0, 
-   "submit": 0, 
-   "write": 1
-  }, 
-  {
-   "amend": 0, 
-   "apply_user_permissions": 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": "Instructor", 
-   "set_user_permissions": 0, 
-   "share": 0, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.py b/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.py
deleted file mode 100644
index 4b953be..0000000
--- a/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class AssessmentResultTool(Document):
-	pass
diff --git a/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.py b/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.py
deleted file mode 100644
index 49e0be0..0000000
--- a/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestAssessmentResultTool(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/content_activity/content_activity.json b/erpnext/education/doctype/content_activity/content_activity.json
deleted file mode 100644
index b4c95da..0000000
--- a/erpnext/education/doctype/content_activity/content_activity.json
+++ /dev/null
@@ -1,141 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2018-10-16 03:55:53.283893", 
- "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": "content", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Content", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Content", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_from": "content.content_type", 
-   "fieldname": "content_type", 
-   "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": "Content Type", 
-   "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": "last_activity", 
-   "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 Activity ", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2018-10-16 03:55:58.202436", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Content Activity", 
- "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/education/doctype/content_activity/content_activity.py b/erpnext/education/doctype/content_activity/content_activity.py
deleted file mode 100644
index f30cb87..0000000
--- a/erpnext/education/doctype/content_activity/content_activity.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class ContentActivity(Document):
-	pass
diff --git a/erpnext/education/doctype/content_question/content_question.js b/erpnext/education/doctype/content_question/content_question.js
deleted file mode 100644
index 7615f5e..0000000
--- a/erpnext/education/doctype/content_question/content_question.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('Content Question', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/content_question/content_question.json b/erpnext/education/doctype/content_question/content_question.json
deleted file mode 100644
index d390e8e..0000000
--- a/erpnext/education/doctype/content_question/content_question.json
+++ /dev/null
@@ -1,76 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2018-10-15 14:35:40.728454", 
- "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": "question_link", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Question Link", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Question", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2018-10-15 14:41:31.729083", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Content Question", 
- "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/education/doctype/content_question/content_question.py b/erpnext/education/doctype/content_question/content_question.py
deleted file mode 100644
index f52f0c8..0000000
--- a/erpnext/education/doctype/content_question/content_question.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class ContentQuestion(Document):
-	pass
diff --git a/erpnext/education/doctype/content_question/test_content_question.py b/erpnext/education/doctype/content_question/test_content_question.py
deleted file mode 100644
index 63a5a96..0000000
--- a/erpnext/education/doctype/content_question/test_content_question.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestContentQuestion(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/course/__init__.py b/erpnext/education/doctype/course/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/course/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/course/course.js b/erpnext/education/doctype/course/course.js
deleted file mode 100644
index bd8d62c..0000000
--- a/erpnext/education/doctype/course/course.js
+++ /dev/null
@@ -1,79 +0,0 @@
-frappe.ui.form.on('Course', {
-	refresh: function(frm) {
-		if (!cur_frm.doc.__islocal) {
-			frm.add_custom_button(__('Add to Programs'), function() {
-				frm.trigger('add_course_to_programs')
-			}, __('Action'));
-		}
-
-		frm.set_query('default_grading_scale', function(){
-			return {
-				filters: {
-					docstatus: 1
-				}
-			}
-		});
-	},
-
-	add_course_to_programs: function(frm) {
-		get_programs_without_course(frm.doc.name).then(r => {
-			if (r.message.length) {
-				frappe.prompt([
-					{
-						fieldname: 'programs',
-						label: __('Programs'),
-						fieldtype: 'MultiSelectPills',
-						get_data: function() {
-							return r.message;
-						}
-					},
-					{
-						fieldtype: 'Check',
-						label: __('Is Mandatory'),
-						fieldname: 'mandatory',
-					}
-				],
-				function(data) {
-					frappe.call({
-						method: 'erpnext.education.doctype.course.course.add_course_to_programs',
-						args: {
-							'course': frm.doc.name,
-							'programs': data.programs,
-							'mandatory': data.mandatory
-						},
-						callback: function(r) {
-							if (!r.exc) {
-								frm.reload_doc();
-							}
-						},
-						freeze: true,
-						freeze_message: __('...Adding Course to Programs')
-					})
-				}, __('Add Course to Programs'), __('Add'));
-			} else {
-				frappe.msgprint(__('This course is already added to the existing programs'));
-			}
-		});
-	}
-});
-
-frappe.ui.form.on('Course Topic', {
-	topics_add: function(frm){
-		frm.fields_dict['topics'].grid.get_field('topic').get_query = function(doc){
-			var topics_list = [];
-			if(!doc.__islocal) topics_list.push(doc.name);
-			$.each(doc.topics, function(idx, val){
-				if (val.topic) topics_list.push(val.topic);
-			});
-			return { filters: [['Topic', 'name', 'not in', topics_list]] };
-		};
-	}
-});
-
-let get_programs_without_course = function(course) {
-	return frappe.call({
-		type: 'GET',
-		method: 'erpnext.education.doctype.course.course.get_programs_without_course',
-		args: {'course': course}
-	});
-}
diff --git a/erpnext/education/doctype/course/course.json b/erpnext/education/doctype/course/course.json
deleted file mode 100644
index da10db1..0000000
--- a/erpnext/education/doctype/course/course.json
+++ /dev/null
@@ -1,137 +0,0 @@
-{
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:course_name",
- "creation": "2015-09-07 12:39:55.181893",
- "doctype": "DocType",
- "engine": "InnoDB",
- "field_order": [
-  "course_name",
-  "department",
-  "section_break_6",
-  "topics",
-  "description",
-  "hero_image",
-  "assessment",
-  "default_grading_scale",
-  "assessment_criteria"
- ],
- "fields": [
-  {
-   "fieldname": "course_name",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Course Name",
-   "reqd": 1,
-   "unique": 1
-  },
-  {
-   "fieldname": "department",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "in_standard_filter": 1,
-   "label": "Department",
-   "options": "Department"
-  },
-  {
-   "fieldname": "section_break_6",
-   "fieldtype": "Section Break",
-   "label": "Portal Settings"
-  },
-  {
-   "fieldname": "topics",
-   "fieldtype": "Table",
-   "label": "Topics",
-   "options": "Course Topic"
-  },
-  {
-   "fieldname": "hero_image",
-   "fieldtype": "Attach Image",
-   "hidden": 1,
-   "label": "Hero Image"
-  },
-  {
-   "fieldname": "assessment",
-   "fieldtype": "Section Break",
-   "label": "Assessment"
-  },
-  {
-   "fieldname": "default_grading_scale",
-   "fieldtype": "Link",
-   "label": "Default Grading Scale",
-   "options": "Grading Scale"
-  },
-  {
-   "fieldname": "assessment_criteria",
-   "fieldtype": "Table",
-   "label": "Assessment Criteria",
-   "options": "Course Assessment Criteria"
-  },
-  {
-   "fieldname": "description",
-   "fieldtype": "Small Text",
-   "label": "Description"
-  }
- ],
- "image_field": "hero_image",
- "modified": "2020-03-29 12:50:27.677589",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Course",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Instructor",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Administrator",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Education Manager",
-   "share": 1,
-   "write": 1
-  }
- ],
- "restrict_to_domain": "Education",
- "search_fields": "course_name",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/course/course.py b/erpnext/education/doctype/course/course.py
deleted file mode 100644
index 2d4f282..0000000
--- a/erpnext/education/doctype/course/course.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-import json
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-
-
-class Course(Document):
-	def validate(self):
-		self.validate_assessment_criteria()
-
-	def validate_assessment_criteria(self):
-		if self.assessment_criteria:
-			total_weightage = 0
-			for criteria in self.assessment_criteria:
-				total_weightage += criteria.weightage or 0
-			if total_weightage != 100:
-				frappe.throw(_('Total Weightage of all Assessment Criteria must be 100%'))
-
-	def get_topics(self):
-		topic_data= []
-		for topic in self.topics:
-			topic_doc = frappe.get_doc('Topic', topic.topic)
-			if topic_doc.topic_content:
-				topic_data.append(topic_doc)
-		return topic_data
-
-
-@frappe.whitelist()
-def add_course_to_programs(course, programs, mandatory=False):
-	programs = json.loads(programs)
-	for entry in programs:
-		program = frappe.get_doc('Program', entry)
-		program.append('courses', {
-			'course': course,
-			'course_name': course,
-			'mandatory': mandatory
-		})
-		program.flags.ignore_mandatory = True
-		program.save()
-	frappe.db.commit()
-	frappe.msgprint(_('Course {0} has been added to all the selected programs successfully.').format(frappe.bold(course)),
-		title=_('Programs updated'), indicator='green')
-
-@frappe.whitelist()
-def get_programs_without_course(course):
-	data = []
-	for entry in frappe.db.get_all('Program'):
-		program = frappe.get_doc('Program', entry.name)
-		courses = [c.course for c in program.courses]
-		if not courses or course not in courses:
-			data.append(program.name)
-	return data
diff --git a/erpnext/education/doctype/course/course_dashboard.py b/erpnext/education/doctype/course/course_dashboard.py
deleted file mode 100644
index 276830f..0000000
--- a/erpnext/education/doctype/course/course_dashboard.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from frappe import _
-
-
-def get_data():
-	return {
-		'fieldname': 'course',
-		'transactions': [
-			{
-				'label': _('Program and Course'),
-				'items': ['Program', 'Course Enrollment', 'Course Schedule']
-			},
-			{
-				'label': _('Student'),
-				'items': ['Student Group']
-			},
-			{
-				'label': _('Assessment'),
-				'items': ['Assessment Plan', 'Assessment Result']
-			},
-		]
-	}
diff --git a/erpnext/education/doctype/course/test_course.py b/erpnext/education/doctype/course/test_course.py
deleted file mode 100644
index 6381cdb..0000000
--- a/erpnext/education/doctype/course/test_course.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and Contributors
-# See license.txt
-
-import unittest
-
-import frappe
-
-from erpnext.education.doctype.topic.test_topic import make_topic, make_topic_and_linked_content
-
-# test_records = frappe.get_test_records('Course')
-
-class TestCourse(unittest.TestCase):
-	def setUp(self):
-		make_topic_and_linked_content("_Test Topic 1", [{"type":"Article", "name": "_Test Article 1"}])
-		make_topic_and_linked_content("_Test Topic 2", [{"type":"Article", "name": "_Test Article 2"}])
-		make_course_and_linked_topic("_Test Course 1", ["_Test Topic 1", "_Test Topic 2"])
-
-	def test_get_topics(self):
-		course = frappe.get_doc("Course", "_Test Course 1")
-		topics = course.get_topics()
-		self.assertEqual(topics[0].name, "_Test Topic 1")
-		self.assertEqual(topics[1].name, "_Test Topic 2")
-		frappe.db.rollback()
-
-def make_course(name):
-	try:
-		course = frappe.get_doc("Course", name)
-	except frappe.DoesNotExistError:
-		course = frappe.get_doc({
-			"doctype": "Course",
-			"course_name": name,
-			"course_code": name
-		}).insert()
-	return course.name
-
-def make_course_and_linked_topic(course_name, topic_name_list):
-	try:
-		course = frappe.get_doc("Course", course_name)
-	except frappe.DoesNotExistError:
-		make_course(course_name)
-		course = frappe.get_doc("Course", course_name)
-	topic_list = [make_topic(topic_name) for topic_name in topic_name_list]
-	for topic in topic_list:
-		course.append("topics", {"topic": topic})
-	course.save()
-	return course
diff --git a/erpnext/education/doctype/course/test_records.json b/erpnext/education/doctype/course/test_records.json
deleted file mode 100644
index 1e7467a..0000000
--- a/erpnext/education/doctype/course/test_records.json
+++ /dev/null
@@ -1,14 +0,0 @@
-[
-	{
-		"course_name": "TC100",
-		"course_abbreviation": "TC"
-	},
-	{
-		"course_name": "TC101",
-		"course_abbreviation": "TC1"
-	},
-	{
-		"course_name": "TC102",
-		"course_abbreviation": "TC2"
-	}
-]
\ No newline at end of file
diff --git a/erpnext/education/doctype/course_activity/__init__.py b/erpnext/education/doctype/course_activity/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/course_activity/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/course_activity/course_activity.js b/erpnext/education/doctype/course_activity/course_activity.js
deleted file mode 100644
index 5115fc4..0000000
--- a/erpnext/education/doctype/course_activity/course_activity.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('Course Activity', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/course_activity/course_activity.json b/erpnext/education/doctype/course_activity/course_activity.json
deleted file mode 100644
index 3e23c90..0000000
--- a/erpnext/education/doctype/course_activity/course_activity.json
+++ /dev/null
@@ -1,301 +0,0 @@
-{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "format:EDU-CA-{YYYY}-{#####}",
- "beta": 1,
- "creation": "2018-10-01 17:35:54.391413",
- "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": "enrollment",
-   "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Course Enrollment",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Course Enrollment",
-   "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": 1,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_from": "enrollment.course",
-   "fieldname": "course",
-   "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": 1,
-   "label": "Course",
-   "length": 0,
-   "no_copy": 0,
-   "options": "",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 1,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 1,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_from": "enrollment.student",
-   "fieldname": "student",
-   "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": 1,
-   "label": "Student",
-   "length": 0,
-   "no_copy": 0,
-   "options": "",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 1,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 1,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "content_type",
-   "fieldtype": "Select",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Content Type",
-   "length": 0,
-   "no_copy": 0,
-   "options": "\nArticle\nVideo",
-   "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": 1,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "content",
-   "fieldtype": "Dynamic Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Content",
-   "length": 0,
-   "no_copy": 0,
-   "options": "content_type",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 1,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "activity_date",
-   "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": "Activity Date",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 1,
-   "translatable": 0,
-   "unique": 0
-  }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-12-06 11:53:08.006123",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Course Activity",
- "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": "Academics User",
-   "set_user_permissions": 0,
-   "share": 1,
-   "submit": 0,
-   "write": 1
-  },
-  {
-   "amend": 0,
-   "cancel": 0,
-   "create": 1,
-   "delete": 0,
-   "email": 1,
-   "export": 1,
-   "if_owner": 1,
-   "import": 0,
-   "permlevel": 0,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "LMS User",
-   "set_user_permissions": 0,
-   "share": 1,
-   "submit": 0,
-   "write": 0
-  },
-  {
-   "amend": 0,
-   "cancel": 0,
-   "create": 0,
-   "delete": 0,
-   "email": 1,
-   "export": 1,
-   "if_owner": 0,
-   "import": 0,
-   "permlevel": 0,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Instructor",
-   "set_user_permissions": 0,
-   "share": 1,
-   "submit": 0,
-   "write": 0
-  }
- ],
- "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
-}
diff --git a/erpnext/education/doctype/course_activity/course_activity.py b/erpnext/education/doctype/course_activity/course_activity.py
deleted file mode 100644
index c1d8242..0000000
--- a/erpnext/education/doctype/course_activity/course_activity.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-
-
-class CourseActivity(Document):
-	def validate(self):
-		self.check_if_enrolled()
-
-
-	def check_if_enrolled(self):
-		if frappe.db.exists("Course Enrollment", self.enrollment):
-			return True
-		else:
-			frappe.throw(_("Course Enrollment {0} does not exists").format(self.enrollment))
diff --git a/erpnext/education/doctype/course_activity/test_course_activity.py b/erpnext/education/doctype/course_activity/test_course_activity.py
deleted file mode 100644
index 9514ff1..0000000
--- a/erpnext/education/doctype/course_activity/test_course_activity.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-import frappe
-
-
-class TestCourseActivity(unittest.TestCase):
-	pass
-
-def make_course_activity(enrollment, content_type, content):
-	activity = frappe.get_all("Course Activity", filters={'enrollment': enrollment, 'content_type': content_type, 'content': content})
-	try:
-		activity = frappe.get_doc("Course Activity", activity[0]['name'])
-	except (IndexError, frappe.DoesNotExistError):
-		activity = frappe.get_doc({
-			"doctype": "Course Activity",
-			"enrollment": enrollment,
-			"content_type": content_type,
-			"content": content,
-			"activity_date": frappe.utils.datetime.datetime.now()
-		}).insert()
-	return activity
diff --git a/erpnext/education/doctype/course_assessment_criteria/__init__.py b/erpnext/education/doctype/course_assessment_criteria/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/course_assessment_criteria/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/course_assessment_criteria/course_assessment_criteria.json b/erpnext/education/doctype/course_assessment_criteria/course_assessment_criteria.json
deleted file mode 100644
index 2d8c0b3..0000000
--- a/erpnext/education/doctype/course_assessment_criteria/course_assessment_criteria.json
+++ /dev/null
@@ -1,134 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "autoname": "", 
- "beta": 0, 
- "creation": "2016-12-14 16:46:46.786353", 
- "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": "assessment_criteria", 
-   "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": "Assessment Criteria", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Assessment Criteria", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_2", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "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, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "weightage", 
-   "fieldtype": "Percent", 
-   "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": "Weightage", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2017-11-10 19:10:44.710837", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Course Assessment Criteria", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/course_assessment_criteria/course_assessment_criteria.py b/erpnext/education/doctype/course_assessment_criteria/course_assessment_criteria.py
deleted file mode 100644
index 4223741..0000000
--- a/erpnext/education/doctype/course_assessment_criteria/course_assessment_criteria.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class CourseAssessmentCriteria(Document):
-	pass
diff --git a/erpnext/education/doctype/course_content/__init__.py b/erpnext/education/doctype/course_content/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/course_content/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/course_content/course_content.js b/erpnext/education/doctype/course_content/course_content.js
deleted file mode 100644
index b9faf99..0000000
--- a/erpnext/education/doctype/course_content/course_content.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('Course Content', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/course_content/course_content.json b/erpnext/education/doctype/course_content/course_content.json
deleted file mode 100644
index 378e560..0000000
--- a/erpnext/education/doctype/course_content/course_content.json
+++ /dev/null
@@ -1,142 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2018-10-01 13:04:09.313771", 
- "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, 
-   "fetch_from": "", 
-   "fieldname": "content_type", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Content Type", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "\nArticle\nVideo\nQuiz", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_2", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "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": "content", 
-   "fieldtype": "Dynamic Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Content", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "content_type", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2018-10-17 07:36:04.029818", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Course Content", 
- "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/education/doctype/course_content/course_content.py b/erpnext/education/doctype/course_content/course_content.py
deleted file mode 100644
index abc370e..0000000
--- a/erpnext/education/doctype/course_content/course_content.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class CourseContent(Document):
-	pass
diff --git a/erpnext/education/doctype/course_content/test_course_content.py b/erpnext/education/doctype/course_content/test_course_content.py
deleted file mode 100644
index 49f042e..0000000
--- a/erpnext/education/doctype/course_content/test_course_content.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestCourseContent(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/course_enrollment/__init__.py b/erpnext/education/doctype/course_enrollment/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/course_enrollment/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/course_enrollment/course_enrollment.js b/erpnext/education/doctype/course_enrollment/course_enrollment.js
deleted file mode 100644
index b5d3cc5..0000000
--- a/erpnext/education/doctype/course_enrollment/course_enrollment.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('Course Enrollment', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/course_enrollment/course_enrollment.json b/erpnext/education/doctype/course_enrollment/course_enrollment.json
deleted file mode 100644
index 6286ec1..0000000
--- a/erpnext/education/doctype/course_enrollment/course_enrollment.json
+++ /dev/null
@@ -1,233 +0,0 @@
-{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "format:EDU-CE-{YYYY}-{#####}",
- "beta": 1,
- "creation": "2018-10-15 15:35:39.375161",
- "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": "program_enrollment",
-   "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Program Enrollment",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Program Enrollment",
-   "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": 1,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "student",
-   "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 1,
-   "label": "Student",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Student",
-   "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": 1,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "course",
-   "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": "Course",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Course",
-   "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": 1,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "enrollment_date",
-   "fieldtype": "Date",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Enrollment Date",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 1,
-   "translatable": 0,
-   "unique": 0
-  }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-11-25 18:59:01.742377",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Course Enrollment",
- "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": "Academics User",
-   "set_user_permissions": 0,
-   "share": 1,
-   "submit": 0,
-   "write": 1
-  },
-  {
-   "amend": 0,
-   "cancel": 0,
-   "create": 1,
-   "delete": 0,
-   "email": 1,
-   "export": 1,
-   "if_owner": 0,
-   "import": 0,
-   "permlevel": 0,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "LMS User",
-   "set_user_permissions": 0,
-   "share": 1,
-   "submit": 0,
-   "write": 1
-  },
-  {
-   "amend": 0,
-   "cancel": 0,
-   "create": 0,
-   "delete": 0,
-   "email": 1,
-   "export": 1,
-   "if_owner": 0,
-   "import": 0,
-   "permlevel": 0,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Instructor",
-   "set_user_permissions": 0,
-   "share": 1,
-   "submit": 0,
-   "write": 0
-  }
- ],
- "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/education/doctype/course_enrollment/course_enrollment.py b/erpnext/education/doctype/course_enrollment/course_enrollment.py
deleted file mode 100644
index 7921284..0000000
--- a/erpnext/education/doctype/course_enrollment/course_enrollment.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from functools import reduce
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import get_link_to_form
-
-
-class CourseEnrollment(Document):
-	def validate(self):
-		self.validate_duplication()
-
-	def get_progress(self, student):
-		"""
-		Returns Progress of given student for a particular course enrollment
-
-			:param self: Course Enrollment Object
-			:param student: Student Object
-		"""
-		course = frappe.get_doc('Course', self.course)
-		topics = course.get_topics()
-		progress = []
-		for topic in topics:
-			progress.append(student.get_topic_progress(self.name, topic))
-		if progress:
-			return reduce(lambda x,y: x+y, progress) # Flatten out the List
-		else:
-			return []
-
-	def validate_duplication(self):
-		enrollment = frappe.db.exists("Course Enrollment", {
-			"student": self.student,
-			"course": self.course,
-			"program_enrollment": self.program_enrollment,
-			"name": ("!=", self.name)
-		})
-		if enrollment:
-			frappe.throw(_("Student is already enrolled via Course Enrollment {0}").format(
-				get_link_to_form("Course Enrollment", enrollment)), title=_('Duplicate Entry'))
-
-	def add_quiz_activity(self, quiz_name, quiz_response, answers, score, status, time_taken):
-		result = {k: ('Correct' if v else 'Wrong') for k,v in answers.items()}
-		result_data = []
-		for key in answers:
-			item = {}
-			item['question'] = key
-			item['quiz_result'] = result[key]
-			try:
-				if not quiz_response[key]:
-					item['selected_option'] = "Unattempted"
-				elif isinstance(quiz_response[key], list):
-					item['selected_option'] = ', '.join(frappe.get_value('Options', res, 'option') for res in quiz_response[key])
-				else:
-					item['selected_option'] = frappe.get_value('Options', quiz_response[key], 'option')
-			except KeyError:
-				item['selected_option'] = "Unattempted"
-			result_data.append(item)
-
-		quiz_activity = frappe.get_doc({
-			"doctype": "Quiz Activity",
-			"enrollment": self.name,
-			"quiz": quiz_name,
-			"activity_date": frappe.utils.datetime.datetime.now(),
-			"result": result_data,
-			"score": score,
-			"status": status,
-			"time_taken": time_taken
-			}).insert(ignore_permissions = True)
-
-	def add_activity(self, content_type, content):
-		activity = check_activity_exists(self.name, content_type, content)
-		if activity:
-			return activity
-		else:
-			activity = frappe.get_doc({
-				"doctype": "Course Activity",
-				"enrollment": self.name,
-				"content_type": content_type,
-				"content": content,
-				"activity_date": frappe.utils.datetime.datetime.now()
-			})
-
-			activity.insert(ignore_permissions=True)
-			return activity.name
-
-def check_activity_exists(enrollment, content_type, content):
-	activity = frappe.get_all("Course Activity", filters={'enrollment': enrollment, 'content_type': content_type, 'content': content})
-	if activity:
-		return activity[0].name
-	else:
-		return None
diff --git a/erpnext/education/doctype/course_enrollment/course_enrollment_dashboard.py b/erpnext/education/doctype/course_enrollment/course_enrollment_dashboard.py
deleted file mode 100644
index 14a7a8f..0000000
--- a/erpnext/education/doctype/course_enrollment/course_enrollment_dashboard.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from frappe import _
-
-
-def get_data():
-	return {
-		'fieldname': 'enrollment',
-		'transactions': [
-			{
-				'label': _('Activity'),
-				'items': ['Course Activity', 'Quiz Activity']
-			}
-		]
-	}
diff --git a/erpnext/education/doctype/course_enrollment/test_course_enrollment.py b/erpnext/education/doctype/course_enrollment/test_course_enrollment.py
deleted file mode 100644
index e74d510..0000000
--- a/erpnext/education/doctype/course_enrollment/test_course_enrollment.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-import frappe
-
-from erpnext.education.doctype.course_activity.test_course_activity import make_course_activity
-from erpnext.education.doctype.program.test_program import setup_program
-from erpnext.education.doctype.student.test_student import create_student, get_student
-
-
-class TestCourseEnrollment(unittest.TestCase):
-	def setUp(self):
-		setup_program()
-		student = create_student({"first_name": "_Test First", "last_name": "_Test Last", "email": "_test_student_1@example.com"})
-		program_enrollment = student.enroll_in_program("_Test Program")
-		course_enrollment = frappe.db.get_value("Course Enrollment",
-			{"course": "_Test Course 1", "student": student.name, "program_enrollment": program_enrollment.name}, 'name')
-		make_course_activity(course_enrollment, "Article", "_Test Article 1-1")
-
-	def test_get_progress(self):
-		student = get_student("_test_student_1@example.com")
-		program_enrollment_name = frappe.get_list("Program Enrollment", filters={"student": student.name, "Program": "_Test Program"})[0].name
-		course_enrollment_name = frappe.get_list("Course Enrollment", filters={"student": student.name, "course": "_Test Course 1", "program_enrollment": program_enrollment_name})[0].name
-		course_enrollment = frappe.get_doc("Course Enrollment", course_enrollment_name)
-		progress = course_enrollment.get_progress(student)
-		finished = {'content': '_Test Article 1-1', 'content_type': 'Article', 'is_complete': True}
-		self.assertTrue(finished in progress)
-		frappe.db.rollback()
-
-	def tearDown(self):
-		for entry in frappe.db.get_all("Course Enrollment"):
-			frappe.delete_doc("Course Enrollment", entry.name)
-
-		for entry in frappe.db.get_all("Program Enrollment"):
-			doc = frappe.get_doc("Program Enrollment", entry.name)
-			doc.cancel()
-			doc.delete()
diff --git a/erpnext/education/doctype/course_schedule/__init__.py b/erpnext/education/doctype/course_schedule/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/course_schedule/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/course_schedule/course_schedule.js b/erpnext/education/doctype/course_schedule/course_schedule.js
deleted file mode 100644
index 366bbd8..0000000
--- a/erpnext/education/doctype/course_schedule/course_schedule.js
+++ /dev/null
@@ -1,16 +0,0 @@
-frappe.provide("education");
-
-cur_frm.add_fetch("student_group", "course", "course")
-frappe.ui.form.on("Course Schedule", {
-	refresh: function(frm) {
-		if (!frm.doc.__islocal) {
-			frm.add_custom_button(__("Mark Attendance"), function() {
-				frappe.route_options = {
-					based_on: "Course Schedule",
-					course_schedule: frm.doc.name
-				}
-				frappe.set_route("Form", "Student Attendance Tool");
-			}).addClass("btn-primary");
-		}
-	}
-});
diff --git a/erpnext/education/doctype/course_schedule/course_schedule.json b/erpnext/education/doctype/course_schedule/course_schedule.json
deleted file mode 100644
index 38d9b50..0000000
--- a/erpnext/education/doctype/course_schedule/course_schedule.json
+++ /dev/null
@@ -1,143 +0,0 @@
-{
- "actions": [],
- "allow_import": 1,
- "autoname": "naming_series:",
- "creation": "2015-09-09 16:34:04.960369",
- "doctype": "DocType",
- "document_type": "Document",
- "engine": "InnoDB",
- "field_order": [
-  "student_group",
-  "instructor",
-  "instructor_name",
-  "column_break_2",
-  "naming_series",
-  "course",
-  "color",
-  "section_break_6",
-  "schedule_date",
-  "room",
-  "column_break_9",
-  "from_time",
-  "to_time",
-  "title"
- ],
- "fields": [
-  {
-   "fieldname": "student_group",
-   "fieldtype": "Link",
-   "in_global_search": 1,
-   "in_standard_filter": 1,
-   "label": "Student Group",
-   "options": "Student Group",
-   "reqd": 1
-  },
-  {
-   "fieldname": "instructor",
-   "fieldtype": "Link",
-   "in_standard_filter": 1,
-   "label": "Instructor",
-   "options": "Instructor",
-   "reqd": 1
-  },
-  {
-   "fetch_from": "instructor.instructor_name",
-   "fieldname": "instructor_name",
-   "fieldtype": "Read Only",
-   "in_global_search": 1,
-   "label": "Instructor Name",
-   "read_only": 1
-  },
-  {
-   "fieldname": "column_break_2",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "naming_series",
-   "fieldtype": "Select",
-   "label": "Naming Series",
-   "options": "EDU-CSH-.YYYY.-",
-   "set_only_once": 1
-  },
-  {
-   "fieldname": "course",
-   "fieldtype": "Link",
-   "in_global_search": 1,
-   "label": "Course",
-   "options": "Course",
-   "reqd": 1
-  },
-  {
-   "fieldname": "color",
-   "fieldtype": "Color",
-   "label": "Color",
-   "print_hide": 1
-  },
-  {
-   "fieldname": "section_break_6",
-   "fieldtype": "Section Break"
-  },
-  {
-   "default": "Today",
-   "fieldname": "schedule_date",
-   "fieldtype": "Date",
-   "label": "Schedule Date"
-  },
-  {
-   "fieldname": "room",
-   "fieldtype": "Link",
-   "label": "Room",
-   "options": "Room",
-   "reqd": 1
-  },
-  {
-   "fieldname": "column_break_9",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "from_time",
-   "fieldtype": "Time",
-   "in_list_view": 1,
-   "label": "From Time",
-   "reqd": 1
-  },
-  {
-   "fieldname": "to_time",
-   "fieldtype": "Time",
-   "in_list_view": 1,
-   "label": "To Time",
-   "reqd": 1
-  },
-  {
-   "fieldname": "title",
-   "fieldtype": "Data",
-   "hidden": 1,
-   "label": "Title"
-  }
- ],
- "links": [],
- "modified": "2021-11-24 11:57:08.164449",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Course Schedule",
- "naming_rule": "By \"Naming Series\" field",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "write": 1
-  }
- ],
- "restrict_to_domain": "Education",
- "sort_field": "schedule_date",
- "sort_order": "DESC",
- "title_field": "title"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/course_schedule/course_schedule.py b/erpnext/education/doctype/course_schedule/course_schedule.py
deleted file mode 100644
index 615d2c4..0000000
--- a/erpnext/education/doctype/course_schedule/course_schedule.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-from datetime import datetime
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-
-
-class CourseSchedule(Document):
-	def validate(self):
-		self.instructor_name = frappe.db.get_value("Instructor", self.instructor, "instructor_name")
-		self.set_title()
-		self.validate_course()
-		self.validate_date()
-		self.validate_overlap()
-
-	def set_title(self):
-		"""Set document Title"""
-		self.title = self.course + " by " + (self.instructor_name if self.instructor_name else self.instructor)
-
-	def validate_course(self):
-		group_based_on, course = frappe.db.get_value("Student Group", self.student_group, ["group_based_on", "course"])
-		if group_based_on == "Course":
-			self.course = course
-
-	def validate_date(self):
-		"""Validates if from_time is greater than to_time"""
-		if self.from_time > self.to_time:
-			frappe.throw(_("From Time cannot be greater than To Time."))
-
-		"""Handles specicfic case to update schedule date in calendar """
-		if isinstance(self.from_time, str):
-			try:
-				datetime_obj = datetime.strptime(self.from_time, '%Y-%m-%d %H:%M:%S')
-				self.schedule_date = datetime_obj
-			except ValueError:
-				pass
-
-	def validate_overlap(self):
-		"""Validates overlap for Student Group, Instructor, Room"""
-
-		from erpnext.education.utils import validate_overlap_for
-
-		#Validate overlapping course schedules.
-		if self.student_group:
-			validate_overlap_for(self, "Course Schedule", "student_group")
-
-		validate_overlap_for(self, "Course Schedule", "instructor")
-		validate_overlap_for(self, "Course Schedule", "room")
-
-		#validate overlapping assessment schedules.
-		if self.student_group:
-			validate_overlap_for(self, "Assessment Plan", "student_group")
-
-		validate_overlap_for(self, "Assessment Plan", "room")
-		validate_overlap_for(self, "Assessment Plan", "supervisor", self.instructor)
\ No newline at end of file
diff --git a/erpnext/education/doctype/course_schedule/course_schedule_calendar.js b/erpnext/education/doctype/course_schedule/course_schedule_calendar.js
deleted file mode 100644
index cacd539..0000000
--- a/erpnext/education/doctype/course_schedule/course_schedule_calendar.js
+++ /dev/null
@@ -1,38 +0,0 @@
-frappe.views.calendar["Course Schedule"] = {
-	field_map: {
-		"start": "from_time",
-		"end": "to_time",
-		"id": "name",
-		"title": "course",
-		"allDay": "allDay",
-	},
-	gantt: false,
-	order_by: "schedule_date",
-	filters: [
-		{
-			"fieldtype": "Link",
-			"fieldname": "student_group",
-			"options": "Student Group",
-			"label": __("Student Group")
-		},
-		{
-			"fieldtype": "Link",
-			"fieldname": "course",
-			"options": "Course",
-			"label": __("Course")
-		},
-		{
-			"fieldtype": "Link",
-			"fieldname": "instructor",
-			"options": "Instructor",
-			"label": __("Instructor")
-		},
-		{
-			"fieldtype": "Link",
-			"fieldname": "room",
-			"options": "Room",
-			"label": __("Room")
-		}
-	],
-	get_events_method: "erpnext.education.api.get_course_schedule_events"
-}
diff --git a/erpnext/education/doctype/course_schedule/course_schedule_dashboard.py b/erpnext/education/doctype/course_schedule/course_schedule_dashboard.py
deleted file mode 100644
index 256e40b..0000000
--- a/erpnext/education/doctype/course_schedule/course_schedule_dashboard.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from frappe import _
-
-
-def get_data():
-	return {
-		'fieldname': 'course_schedule',
-		'transactions': [
-			{
-				'label': _('Attendance'),
-				'items': ['Student Attendance']
-			}
-		]
-	}
diff --git a/erpnext/education/doctype/course_schedule/test_course_schedule.py b/erpnext/education/doctype/course_schedule/test_course_schedule.py
deleted file mode 100644
index 56149af..0000000
--- a/erpnext/education/doctype/course_schedule/test_course_schedule.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and Contributors
-# See license.txt
-
-import datetime
-import unittest
-
-import frappe
-from frappe.utils import to_timedelta, today
-from frappe.utils.data import add_to_date
-
-from erpnext.education.utils import OverlapError
-
-# test_records = frappe.get_test_records('Course Schedule')
-
-class TestCourseSchedule(unittest.TestCase):
-	def test_student_group_conflict(self):
-		cs1 = make_course_schedule_test_record(simulate= True)
-
-		cs2 = make_course_schedule_test_record(schedule_date=cs1.schedule_date, from_time= cs1.from_time,
-			to_time= cs1.to_time, instructor="_Test Instructor 2", room=frappe.get_all("Room")[1].name, do_not_save= 1)
-		self.assertRaises(OverlapError, cs2.save)
-
-	def test_instructor_conflict(self):
-		cs1 = make_course_schedule_test_record(simulate= True)
-
-		cs2 = make_course_schedule_test_record(from_time= cs1.from_time, to_time= cs1.to_time,
-			student_group="Course-TC101-2014-2015 (_Test Academic Term)", room=frappe.get_all("Room")[1].name, do_not_save= 1)
-		self.assertRaises(OverlapError, cs2.save)
-
-	def test_room_conflict(self):
-		cs1 = make_course_schedule_test_record(simulate= True)
-
-		cs2 = make_course_schedule_test_record(from_time= cs1.from_time, to_time= cs1.to_time,
-			student_group="Course-TC101-2014-2015 (_Test Academic Term)", instructor="_Test Instructor 2", do_not_save= 1)
-		self.assertRaises(OverlapError, cs2.save)
-
-	def test_no_conflict(self):
-		cs1 = make_course_schedule_test_record(simulate= True)
-
-		make_course_schedule_test_record(from_time= cs1.from_time, to_time= cs1.to_time,
-			student_group="Course-TC102-2014-2015 (_Test Academic Term)", instructor="_Test Instructor 2", room=frappe.get_all("Room")[1].name)
-
-	def test_update_schedule_date(self):
-		doc = make_course_schedule_test_record(schedule_date= add_to_date(today(), days=1))
-		doc.schedule_date = add_to_date(doc.schedule_date, days=1)
-		doc.save()
-
-def make_course_schedule_test_record(**args):
-	args = frappe._dict(args)
-
-	course_schedule = frappe.new_doc("Course Schedule")
-	course_schedule.student_group = args.student_group or "Course-TC101-2014-2015 (_Test Academic Term)"
-	course_schedule.course = args.course or "TC101"
-	course_schedule.instructor = args.instructor or "_Test Instructor"
-	course_schedule.room = args.room or frappe.get_all("Room")[0].name
-
-	course_schedule.schedule_date = args.schedule_date or today()
-	course_schedule.from_time = args.from_time or to_timedelta("01:00:00")
-	course_schedule.to_time = args.to_time or course_schedule.from_time + datetime.timedelta(hours= 1)
-
-
-	if not args.do_not_save:
-		if args.simulate:
-			while True:
-				try:
-					course_schedule.save()
-					break
-				except OverlapError:
-					course_schedule.from_time = course_schedule.from_time + datetime.timedelta(minutes=10)
-					course_schedule.to_time = course_schedule.from_time + datetime.timedelta(hours= 1)
-		else:
-			course_schedule.save()
-
-	return course_schedule
diff --git a/erpnext/education/doctype/course_scheduling_tool/__init__.py b/erpnext/education/doctype/course_scheduling_tool/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/course_scheduling_tool/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js
deleted file mode 100644
index 7b0e4ab..0000000
--- a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-
-frappe.ui.form.on('Course Scheduling Tool', {
-	setup(frm) {
-		frm.add_fetch('student_group', 'program', 'program');
-		frm.add_fetch('student_group', 'course', 'course');
-		frm.add_fetch('student_group', 'academic_year', 'academic_year');
-		frm.add_fetch('student_group', 'academic_term', 'academic_term');
-	},
-	refresh(frm) {
-		frm.disable_save();
-		frm.page.set_primary_action(__('Schedule Course'), () => {
-			frm.call('schedule_course')
-				.then(r => {
-					if (!r.message) {
-						frappe.throw(__('There were errors creating Course Schedule'));
-					}
-					const { course_schedules } = r.message;
-					if (course_schedules) {
-						const course_schedules_html = course_schedules.map(c => `
-							<tr>
-								<td><a href="/app/course-schedule/${c.name}">${c.name}</a></td>
-								<td>${c.schedule_date}</td>
-							</tr>
-						`).join('');
-
-						const html = `
-							<table class="table table-bordered">
-								<caption>${__('Following course schedules were created')}</caption>
-								<thead><tr><th>${__("Course")}</th><th>${__("Date")}</th></tr></thead>
-								<tbody>
-									${course_schedules_html}
-								</tbody>
-							</table>
-						`;
-
-						frappe.msgprint(html);
-					}
-				});
-		});
-	}
-});
diff --git a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.json b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.json
deleted file mode 100644
index 13dfe38..0000000
--- a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.json
+++ /dev/null
@@ -1,168 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "creation": "2015-09-23 15:37:38.108475",
- "doctype": "DocType",
- "document_type": "Setup",
- "engine": "InnoDB",
- "field_order": [
-  "student_group",
-  "course",
-  "program",
-  "column_break_3",
-  "academic_year",
-  "academic_term",
-  "section_break_6",
-  "instructor",
-  "instructor_name",
-  "column_break_9",
-  "room",
-  "section_break_7",
-  "course_start_date",
-  "course_end_date",
-  "day",
-  "reschedule",
-  "column_break_15",
-  "from_time",
-  "to_time"
- ],
- "fields": [
-  {
-   "fieldname": "student_group",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Student Group",
-   "options": "Student Group",
-   "reqd": 1
-  },
-  {
-   "fieldname": "course",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Course",
-   "options": "Course",
-   "reqd": 1
-  },
-  {
-   "fieldname": "program",
-   "fieldtype": "Link",
-   "label": "Program",
-   "options": "Program",
-   "read_only": 1
-  },
-  {
-   "fieldname": "column_break_3",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "academic_year",
-   "fieldtype": "Link",
-   "label": "Academic Year",
-   "options": "Academic Year",
-   "read_only": 1
-  },
-  {
-   "fieldname": "academic_term",
-   "fieldtype": "Link",
-   "label": "Academic Term",
-   "options": "Academic Term",
-   "read_only": 1
-  },
-  {
-   "fieldname": "section_break_6",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fieldname": "instructor",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Instructor",
-   "options": "Instructor",
-   "reqd": 1
-  },
-  {
-   "fetch_from": "instructor.instructor_name",
-   "fieldname": "instructor_name",
-   "fieldtype": "Read Only",
-   "label": "Instructor Name",
-   "read_only": 1
-  },
-  {
-   "fieldname": "column_break_9",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "room",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Room",
-   "options": "Room",
-   "reqd": 1
-  },
-  {
-   "fieldname": "section_break_7",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fieldname": "from_time",
-   "fieldtype": "Time",
-   "label": "From Time",
-   "reqd": 1
-  },
-  {
-   "fieldname": "course_start_date",
-   "fieldtype": "Date",
-   "label": "Course Start Date",
-   "reqd": 1
-  },
-  {
-   "fieldname": "day",
-   "fieldtype": "Select",
-   "label": "Day",
-   "options": "\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday",
-   "reqd": 1
-  },
-  {
-   "default": "0",
-   "fieldname": "reschedule",
-   "fieldtype": "Check",
-   "label": "Reschedule"
-  },
-  {
-   "fieldname": "column_break_15",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "to_time",
-   "fieldtype": "Time",
-   "label": "To TIme",
-   "reqd": 1
-  },
-  {
-   "fieldname": "course_end_date",
-   "fieldtype": "Date",
-   "label": "Course End Date",
-   "reqd": 1
-  }
- ],
- "hide_toolbar": 1,
- "issingle": 1,
- "links": [],
- "modified": "2021-11-11 09:33:18.874445",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Course Scheduling Tool",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "read": 1,
-   "role": "Academics User",
-   "write": 1
-  }
- ],
- "restrict_to_domain": "Education",
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.py b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.py
deleted file mode 100644
index a309e46..0000000
--- a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.py
+++ /dev/null
@@ -1,116 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import calendar
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import add_days, getdate
-
-from erpnext.education.utils import OverlapError
-
-
-class CourseSchedulingTool(Document):
-
-	@frappe.whitelist()
-	def schedule_course(self):
-		"""Creates course schedules as per specified parameters"""
-
-		course_schedules = []
-		course_schedules_errors = []
-		rescheduled = []
-		reschedule_errors = []
-
-		self.validate_mandatory()
-		self.validate_date()
-		self.instructor_name = frappe.db.get_value(
-			"Instructor", self.instructor, "instructor_name")
-
-		group_based_on, course = frappe.db.get_value(
-			"Student Group", self.student_group, ["group_based_on", "course"])
-
-		if group_based_on == "Course":
-			self.course = course
-
-		if self.reschedule:
-			rescheduled, reschedule_errors = self.delete_course_schedule(
-				rescheduled, reschedule_errors)
-
-		date = self.course_start_date
-		while date < self.course_end_date:
-			if self.day == calendar.day_name[getdate(date).weekday()]:
-				course_schedule = self.make_course_schedule(date)
-				try:
-					print('pass')
-					course_schedule.save()
-				except OverlapError:
-					print('fail')
-					course_schedules_errors.append(date)
-				else:
-					course_schedules.append(course_schedule)
-
-				date = add_days(date, 7)
-			else:
-				date = add_days(date, 1)
-
-		return dict(
-			course_schedules=course_schedules,
-			course_schedules_errors=course_schedules_errors,
-			rescheduled=rescheduled,
-			reschedule_errors=reschedule_errors
-		)
-
-	def validate_mandatory(self):
-		"""Validates all mandatory fields"""
-
-		fields = ['course', 'room', 'instructor', 'from_time',
-				  'to_time', 'course_start_date', 'course_end_date', 'day']
-		for d in fields:
-			if not self.get(d):
-				frappe.throw(_("{0} is mandatory").format(
-					self.meta.get_label(d)))
-
-	def validate_date(self):
-		"""Validates if Course Start Date is greater than Course End Date"""
-		if self.course_start_date > self.course_end_date:
-			frappe.throw(
-				_("Course Start Date cannot be greater than Course End Date."))
-
-	def delete_course_schedule(self, rescheduled, reschedule_errors):
-		"""Delete all course schedule within the Date range and specified filters"""
-
-		schedules = frappe.get_list("Course Schedule",
-			fields=["name", "schedule_date"],
-			filters=[
-				["student_group", "=", self.student_group],
-				["course", "=", self.course],
-				["schedule_date", ">=", self.course_start_date],
-				["schedule_date", "<=", self.course_end_date]
-			]
-		)
-
-		for d in schedules:
-			try:
-				if self.day == calendar.day_name[getdate(d.schedule_date).weekday()]:
-					frappe.delete_doc("Course Schedule", d.name)
-					rescheduled.append(d.name)
-			except Exception:
-				reschedule_errors.append(d.name)
-		return rescheduled, reschedule_errors
-
-	def make_course_schedule(self, date):
-		"""Makes a new Course Schedule.
-		:param date: Date on which Course Schedule will be created."""
-
-		course_schedule = frappe.new_doc("Course Schedule")
-		course_schedule.student_group = self.student_group
-		course_schedule.course = self.course
-		course_schedule.instructor = self.instructor
-		course_schedule.instructor_name = self.instructor_name
-		course_schedule.room = self.room
-		course_schedule.schedule_date = date
-		course_schedule.from_time = self.from_time
-		course_schedule.to_time = self.to_time
-		return course_schedule
diff --git a/erpnext/education/doctype/course_scheduling_tool/test_course_scheduling_tool.py b/erpnext/education/doctype/course_scheduling_tool/test_course_scheduling_tool.py
deleted file mode 100644
index 559214b..0000000
--- a/erpnext/education/doctype/course_scheduling_tool/test_course_scheduling_tool.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestCourseSchedulingTool(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/course_topic/__init__.py b/erpnext/education/doctype/course_topic/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/course_topic/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/course_topic/course_topic.js b/erpnext/education/doctype/course_topic/course_topic.js
deleted file mode 100644
index 7d03ba3..0000000
--- a/erpnext/education/doctype/course_topic/course_topic.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('Course Topic', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/course_topic/course_topic.json b/erpnext/education/doctype/course_topic/course_topic.json
deleted file mode 100644
index 3fcddc1..0000000
--- a/erpnext/education/doctype/course_topic/course_topic.json
+++ /dev/null
@@ -1,109 +0,0 @@
-{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2018-12-12 11:51:25.952740",
- "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": "topic",
-   "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": "Topic",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Topic",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_from": "topic.topic_name",
-   "fieldname": "topic_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": "Topic 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
-  }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2018-12-12 13:01:58.960425",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Course Topic",
- "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/education/doctype/course_topic/course_topic.py b/erpnext/education/doctype/course_topic/course_topic.py
deleted file mode 100644
index 3c1cabf..0000000
--- a/erpnext/education/doctype/course_topic/course_topic.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class CourseTopic(Document):
-	pass
diff --git a/erpnext/education/doctype/course_topic/test_course_topic.py b/erpnext/education/doctype/course_topic/test_course_topic.py
deleted file mode 100644
index a4d370c..0000000
--- a/erpnext/education/doctype/course_topic/test_course_topic.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestCourseTopic(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/education_settings/__init__.py b/erpnext/education/doctype/education_settings/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/education_settings/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/education_settings/education_settings.js b/erpnext/education/doctype/education_settings/education_settings.js
deleted file mode 100644
index 764d8b4..0000000
--- a/erpnext/education/doctype/education_settings/education_settings.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Education Settings', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/education_settings/education_settings.json b/erpnext/education/doctype/education_settings/education_settings.json
deleted file mode 100644
index 0e548db..0000000
--- a/erpnext/education/doctype/education_settings/education_settings.json
+++ /dev/null
@@ -1,149 +0,0 @@
-{
- "actions": [],
- "creation": "2017-04-05 13:33:04.519313",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "current_academic_year",
-  "current_academic_term",
-  "attendance_freeze_date",
-  "column_break_4",
-  "validate_batch",
-  "validate_course",
-  "academic_term_reqd",
-  "user_creation_skip",
-  "section_break_7",
-  "instructor_created_by",
-  "web_academy_settings_section",
-  "enable_lms",
-  "portal_title",
-  "description"
- ],
- "fields": [
-  {
-   "fieldname": "current_academic_year",
-   "fieldtype": "Link",
-   "label": "Current Academic Year",
-   "options": "Academic Year"
-  },
-  {
-   "fieldname": "current_academic_term",
-   "fieldtype": "Link",
-   "label": "Current Academic Term",
-   "options": "Academic Term"
-  },
-  {
-   "fieldname": "attendance_freeze_date",
-   "fieldtype": "Date",
-   "label": "Attendance Freeze Date"
-  },
-  {
-   "fieldname": "column_break_4",
-   "fieldtype": "Column Break"
-  },
-  {
-   "default": "0",
-   "description": "For Batch based Student Group, the Student Batch will be validated for every Student from the Program Enrollment.",
-   "fieldname": "validate_batch",
-   "fieldtype": "Check",
-   "label": "Validate Batch for Students in Student Group"
-  },
-  {
-   "default": "0",
-   "description": "For Course based Student Group, the Course will be validated for every Student from the enrolled Courses in Program Enrollment.",
-   "fieldname": "validate_course",
-   "fieldtype": "Check",
-   "label": "Validate Enrolled Course for Students in Student Group"
-  },
-  {
-   "default": "0",
-   "description": "If enabled, field Academic Term will be Mandatory in Program Enrollment Tool.",
-   "fieldname": "academic_term_reqd",
-   "fieldtype": "Check",
-   "label": "Make Academic Term Mandatory"
-  },
-  {
-   "fieldname": "section_break_7",
-   "fieldtype": "Section Break"
-  },
-  {
-   "default": "Full Name",
-   "fieldname": "instructor_created_by",
-   "fieldtype": "Select",
-   "label": "Instructor Records to be created by",
-   "options": "Full Name\nNaming Series\nEmployee Number"
-  },
-  {
-   "fieldname": "web_academy_settings_section",
-   "fieldtype": "Section Break",
-   "label": "Learning Management System Settings"
-  },
-  {
-   "depends_on": "eval: doc.enable_lms",
-   "fieldname": "portal_title",
-   "fieldtype": "Data",
-   "label": "Learning Management System Title"
-  },
-  {
-   "depends_on": "eval: doc.enable_lms",
-   "fieldname": "description",
-   "fieldtype": "Small Text",
-   "label": "Description"
-  },
-  {
-   "default": "0",
-   "fieldname": "enable_lms",
-   "fieldtype": "Check",
-   "label": "Enable Learning Management System"
-  },
-  {
-   "default": "0",
-   "description": "By default, a new User is created for every new Student. If enabled, no new User will be created when a new Student is created.",
-   "fieldname": "user_creation_skip",
-   "fieldtype": "Check",
-   "label": "Skip User creation for new Student"
-  }
- ],
- "issingle": 1,
- "links": [],
- "modified": "2020-05-07 19:18:10.639356",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Education Settings",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "print": 1,
-   "read": 1,
-   "role": "System Manager",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "print": 1,
-   "read": 1,
-   "role": "Education Manager",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "email": 1,
-   "print": 1,
-   "read": 1,
-   "role": "Guest",
-   "share": 1
-  }
- ],
- "quick_entry": 1,
- "restrict_to_domain": "Education",
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/education_settings/education_settings.py b/erpnext/education/doctype/education_settings/education_settings.py
deleted file mode 100644
index 13123be..0000000
--- a/erpnext/education/doctype/education_settings/education_settings.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-import frappe.defaults
-from frappe.model.document import Document
-
-education_keydict = {
-	# "key in defaults": "key in Global Defaults"
-	"academic_year": "current_academic_year",
-	"academic_term": "current_academic_term",
-	"validate_batch": "validate_batch",
-	"validate_course": "validate_course"
-}
-
-
-class EducationSettings(Document):
-	def on_update(self):
-		"""update defaults"""
-		for key in education_keydict:
-			frappe.db.set_default(key, self.get(education_keydict[key], ''))
-
-		# clear cache
-		frappe.clear_cache()
-
-	def get_defaults(self):
-		return frappe.defaults.get_defaults()
-
-	def validate(self):
-		from frappe.custom.doctype.property_setter.property_setter import make_property_setter
-		if self.get('instructor_created_by')=='Naming Series':
-			make_property_setter('Instructor', "naming_series", "hidden", 0, "Check", validate_fields_for_doctype=False)
-		else:
-			make_property_setter('Instructor', "naming_series", "hidden", 1, "Check", validate_fields_for_doctype=False)
-
-def update_website_context(context):
-	context["lms_enabled"] = frappe.get_doc("Education Settings").enable_lms
diff --git a/erpnext/education/doctype/education_settings/test_education_settings.py b/erpnext/education/doctype/education_settings/test_education_settings.py
deleted file mode 100644
index 223e838..0000000
--- a/erpnext/education/doctype/education_settings/test_education_settings.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestEducationSettings(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/fee_category/__init__.py b/erpnext/education/doctype/fee_category/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/fee_category/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/fee_category/fee_category.js b/erpnext/education/doctype/fee_category/fee_category.js
deleted file mode 100644
index 96fd915..0000000
--- a/erpnext/education/doctype/fee_category/fee_category.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Fee Category', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/fee_category/fee_category.json b/erpnext/education/doctype/fee_category/fee_category.json
deleted file mode 100644
index c1bfa8e..0000000
--- a/erpnext/education/doctype/fee_category/fee_category.json
+++ /dev/null
@@ -1,171 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 1, 
- "allow_rename": 1, 
- "autoname": "field:category_name", 
- "beta": 0, 
- "creation": "2015-09-16 13:01:10.448734", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Setup", 
- "editable_grid": 0, 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "category_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": "Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "earning_name", 
-   "oldfieldtype": "Data", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "description", 
-   "fieldtype": "Small Text", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Description", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "description", 
-   "oldfieldtype": "Small Text", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0, 
-   "width": "300px"
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "icon": "fa fa-flag", 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "menu_index": 0, 
- "modified": "2017-11-10 18:56:33.824534", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Fee Category", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [
-  {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Academics User", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }, 
-  {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Accounts User", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }, 
-  {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Accounts Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "search_fields": "description", 
- "show_name_in_global_search": 1, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/fee_category/fee_category.py b/erpnext/education/doctype/fee_category/fee_category.py
deleted file mode 100644
index 1faa0c5..0000000
--- a/erpnext/education/doctype/fee_category/fee_category.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class FeeCategory(Document):
-	pass
diff --git a/erpnext/education/doctype/fee_category/test_fee_category.py b/erpnext/education/doctype/fee_category/test_fee_category.py
deleted file mode 100644
index 9e74c7d..0000000
--- a/erpnext/education/doctype/fee_category/test_fee_category.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and Contributors
-# See license.txt
-
-import unittest
-
-# test_records = frappe.get_test_records('Fee Category')
-
-class TestFeeCategory(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/fee_category/test_records.json b/erpnext/education/doctype/fee_category/test_records.json
deleted file mode 100644
index 598c1ed..0000000
--- a/erpnext/education/doctype/fee_category/test_records.json
+++ /dev/null
@@ -1,11 +0,0 @@
-[
-	{
-		"category_name": "Admission Fee"
-	},
-	{
-		"category_name": "Tuition Fee"
-	},
-	{
-		"category_name": "Transportation Fee"
-	}
-]
\ No newline at end of file
diff --git a/erpnext/education/doctype/fee_component/__init__.py b/erpnext/education/doctype/fee_component/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/fee_component/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/fee_component/fee_component.json b/erpnext/education/doctype/fee_component/fee_component.json
deleted file mode 100644
index f6e13c4..0000000
--- a/erpnext/education/doctype/fee_component/fee_component.json
+++ /dev/null
@@ -1,169 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 1, 
- "autoname": "", 
- "beta": 0, 
- "creation": "2015-09-16 13:07:27.675453", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Setup", 
- "editable_grid": 1, 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "fees_category", 
-   "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": "Fees Category", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "earning_name", 
-   "oldfieldtype": "Data", 
-   "options": "Fee Category", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "description", 
-   "fieldtype": "Small Text", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Description", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_2", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "amount", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Amount", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "description", 
-   "oldfieldtype": "Small Text", 
-   "options": "", 
-   "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, 
-   "width": "300px"
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "icon": "fa fa-flag", 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2017-11-10 18:58:10.254407", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Fee Component", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/fee_component/fee_component.py b/erpnext/education/doctype/fee_component/fee_component.py
deleted file mode 100644
index c5cf7d9..0000000
--- a/erpnext/education/doctype/fee_component/fee_component.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class FeeComponent(Document):
-	pass
diff --git a/erpnext/education/doctype/fee_schedule/__init__.py b/erpnext/education/doctype/fee_schedule/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/fee_schedule/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.js b/erpnext/education/doctype/fee_schedule/fee_schedule.js
deleted file mode 100644
index 97691a5..0000000
--- a/erpnext/education/doctype/fee_schedule/fee_schedule.js
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.provide("erpnext.accounts.dimensions");
-frappe.ui.form.on('Fee Schedule', {
-	setup: function(frm) {
-		frm.add_fetch('fee_structure', 'receivable_account', 'receivable_account');
-		frm.add_fetch('fee_structure', 'income_account', 'income_account');
-		frm.add_fetch('fee_structure', 'cost_center', 'cost_center');
-	},
-
-	company: function(frm) {
-		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
-	},
-
-	onload: function(frm) {
-		frm.set_query('receivable_account', function(doc) {
-			return {
-				filters: {
-					'account_type': 'Receivable',
-					'is_group': 0,
-					'company': doc.company
-				}
-			};
-		});
-
-		frm.set_query('income_account', function(doc) {
-			return {
-				filters: {
-					'account_type': 'Income Account',
-					'is_group': 0,
-					'company': doc.company
-				}
-			};
-		});
-
-		frm.set_query('student_group', 'student_groups', function() {
-			return {
-				'program': frm.doc.program,
-				'academic_term': frm.doc.academic_term,
-				'academic_year': frm.doc.academic_year,
-				'disabled': 0
-			};
-		});
-
-		frappe.realtime.on('fee_schedule_progress', function(data) {
-			if (data.reload && data.reload === 1) {
-				frm.reload_doc();
-			}
-			if (data.progress) {
-				let progress_bar = $(cur_frm.dashboard.progress_area.body).find('.progress-bar');
-				if (progress_bar) {
-					$(progress_bar).removeClass('progress-bar-danger').addClass('progress-bar-success progress-bar-striped');
-					$(progress_bar).css('width', data.progress+'%');
-				}
-			}
-		});
-
-		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
-	},
-
-	refresh: function(frm) {
-		if (!frm.doc.__islocal && frm.doc.__onload && frm.doc.__onload.dashboard_info &&
-			frm.doc.fee_creation_status === 'Successful') {
-			var info = frm.doc.__onload.dashboard_info;
-			frm.dashboard.add_indicator(__('Total Collected: {0}', [format_currency(info.total_paid,
-				info.currency)]), 'blue');
-			frm.dashboard.add_indicator(__('Total Outstanding: {0}', [format_currency(info.total_unpaid,
-				info.currency)]), info.total_unpaid ? 'orange' : 'green');
-		}
-		if (frm.doc.fee_creation_status === 'In Process') {
-			frm.dashboard.add_progress('Fee Creation Status', '0');
-		}
-		if (frm.doc.docstatus === 1 && !frm.doc.fee_creation_status || frm.doc.fee_creation_status === 'Failed') {
-			frm.add_custom_button(__('Create Fees'), function() {
-				frappe.call({
-					method: 'create_fees',
-					doc: frm.doc,
-					callback: function() {
-						frm.refresh();
-					}
-				});
-			}).addClass('btn-primary');;
-		}
-		if (frm.doc.fee_creation_status === 'Successful') {
-			frm.add_custom_button(__('View Fees Records'), function() {
-				frappe.route_options = {
-					fee_schedule: frm.doc.name
-				};
-				frappe.set_route('List', 'Fees');
-			});
-		}
-
-	},
-
-	fee_structure: function(frm) {
-		if (frm.doc.fee_structure) {
-			frappe.call({
-				method: 'erpnext.education.doctype.fee_schedule.fee_schedule.get_fee_structure',
-				args: {
-					'target_doc': frm.doc.name,
-					'source_name': frm.doc.fee_structure
-				},
-				callback: function(r) {
-					var doc = frappe.model.sync(r.message);
-					frappe.set_route('Form', doc[0].doctype, doc[0].name);
-				}
-			});
-		}
-	}
-});
-
-frappe.ui.form.on('Fee Schedule Student Group', {
-	student_group: function(frm, cdt, cdn) {
-		var row = locals[cdt][cdn];
-		if (row.student_group && frm.doc.academic_year) {
-			frappe.call({
-				method: 'erpnext.education.doctype.fee_schedule.fee_schedule.get_total_students',
-				args: {
-					'student_group': row.student_group,
-					'academic_year': frm.doc.academic_year,
-					'academic_term': frm.doc.academic_term,
-					'student_category': frm.doc.student_category
-				},
-				callback: function(r) {
-					if (!r.exc) {
-						frappe.model.set_value(cdt, cdn, 'total_students', r.message);
-					}
-				}
-			});
-		}
-	}
-})
diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.json b/erpnext/education/doctype/fee_schedule/fee_schedule.json
deleted file mode 100644
index 23b3212..0000000
--- a/erpnext/education/doctype/fee_schedule/fee_schedule.json
+++ /dev/null
@@ -1,327 +0,0 @@
-{
- "actions": [],
- "allow_import": 1,
- "autoname": "naming_series:",
- "creation": "2017-07-18 15:21:21.527136",
- "doctype": "DocType",
- "document_type": "Document",
- "engine": "InnoDB",
- "field_order": [
-  "fee_structure",
-  "posting_date",
-  "due_date",
-  "naming_series",
-  "fee_creation_status",
-  "send_email",
-  "column_break_4",
-  "student_category",
-  "program",
-  "academic_year",
-  "academic_term",
-  "section_break_10",
-  "currency",
-  "student_groups",
-  "section_break_14",
-  "components",
-  "section_break_16",
-  "column_break_18",
-  "total_amount",
-  "grand_total",
-  "grand_total_in_words",
-  "edit_printing_settings",
-  "letter_head",
-  "column_break_32",
-  "select_print_heading",
-  "account",
-  "receivable_account",
-  "income_account",
-  "column_break_39",
-  "company",
-  "amended_from",
-  "accounting_dimensions_section",
-  "cost_center",
-  "dimension_col_break",
-  "section_break_31",
-  "error_log"
- ],
- "fields": [
-  {
-   "fieldname": "fee_structure",
-   "fieldtype": "Link",
-   "in_global_search": 1,
-   "in_list_view": 1,
-   "label": "Fee Structure",
-   "options": "Fee Structure",
-   "reqd": 1
-  },
-  {
-   "fieldname": "due_date",
-   "fieldtype": "Date",
-   "label": "Due Date",
-   "reqd": 1
-  },
-  {
-   "fieldname": "naming_series",
-   "fieldtype": "Select",
-   "label": "Naming Series",
-   "no_copy": 1,
-   "options": "EDU-FSH-.YYYY.-"
-  },
-  {
-   "fieldname": "fee_creation_status",
-   "fieldtype": "Select",
-   "label": "Fee Creation Status",
-   "no_copy": 1,
-   "options": "\nIn Process\nFailed\nSuccessful",
-   "print_hide": 1,
-   "read_only": 1
-  },
-  {
-   "default": "0",
-   "fieldname": "send_email",
-   "fieldtype": "Check",
-   "label": "Send Payment Request Email"
-  },
-  {
-   "fieldname": "column_break_4",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "student_category",
-   "fieldtype": "Link",
-   "label": "Student Category",
-   "options": "Student Category",
-   "read_only": 1
-  },
-  {
-   "fieldname": "program",
-   "fieldtype": "Link",
-   "label": "Program",
-   "options": "Program",
-   "read_only": 1
-  },
-  {
-   "fieldname": "academic_year",
-   "fieldtype": "Link",
-   "label": "Academic Year",
-   "options": "Academic Year",
-   "reqd": 1
-  },
-  {
-   "fieldname": "academic_term",
-   "fieldtype": "Link",
-   "label": "Academic Term",
-   "options": "Academic Term"
-  },
-  {
-   "fieldname": "section_break_10",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fieldname": "currency",
-   "fieldtype": "Link",
-   "hidden": 1,
-   "label": "Currency",
-   "options": "Currency",
-   "read_only": 1
-  },
-  {
-   "fieldname": "student_groups",
-   "fieldtype": "Table",
-   "options": "Fee Schedule Student Group",
-   "reqd": 1
-  },
-  {
-   "fieldname": "section_break_14",
-   "fieldtype": "Section Break",
-   "label": "Fee Breakup for each student",
-   "read_only": 1
-  },
-  {
-   "fieldname": "components",
-   "fieldtype": "Table",
-   "options": "Fee Component",
-   "read_only": 1
-  },
-  {
-   "fieldname": "section_break_16",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fieldname": "column_break_18",
-   "fieldtype": "Column Break"
-  },
-  {
-   "default": "0",
-   "fieldname": "total_amount",
-   "fieldtype": "Currency",
-   "label": "Total Amount per Student",
-   "read_only": 1
-  },
-  {
-   "fieldname": "grand_total",
-   "fieldtype": "Currency",
-   "label": "Grand Total",
-   "read_only": 1
-  },
-  {
-   "fieldname": "grand_total_in_words",
-   "fieldtype": "Data",
-   "label": "In Words",
-   "length": 240,
-   "read_only": 1
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "edit_printing_settings",
-   "fieldtype": "Section Break",
-   "label": "Printing Settings"
-  },
-  {
-   "allow_on_submit": 1,
-   "fieldname": "letter_head",
-   "fieldtype": "Link",
-   "label": "Letter Head",
-   "options": "Letter Head",
-   "print_hide": 1
-  },
-  {
-   "fieldname": "column_break_32",
-   "fieldtype": "Column Break"
-  },
-  {
-   "allow_on_submit": 1,
-   "fieldname": "select_print_heading",
-   "fieldtype": "Link",
-   "label": "Print Heading",
-   "no_copy": 1,
-   "options": "Print Heading",
-   "print_hide": 1,
-   "report_hide": 1
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "account",
-   "fieldtype": "Section Break",
-   "label": "Accounting"
-  },
-  {
-   "fieldname": "receivable_account",
-   "fieldtype": "Link",
-   "label": "Receivable Account",
-   "options": "Account"
-  },
-  {
-   "fieldname": "income_account",
-   "fieldtype": "Link",
-   "label": "Income Account",
-   "options": "Account"
-  },
-  {
-   "fieldname": "column_break_39",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "cost_center",
-   "fieldtype": "Link",
-   "label": "Cost Center",
-   "options": "Cost Center"
-  },
-  {
-   "fieldname": "company",
-   "fieldtype": "Link",
-   "label": "Institution",
-   "options": "Company"
-  },
-  {
-   "fieldname": "amended_from",
-   "fieldtype": "Link",
-   "label": "Amended From",
-   "no_copy": 1,
-   "options": "Fee Schedule",
-   "print_hide": 1,
-   "read_only": 1
-  },
-  {
-   "collapsible": 1,
-   "depends_on": "error_log",
-   "fieldname": "section_break_31",
-   "fieldtype": "Section Break",
-   "label": "Error Log"
-  },
-  {
-   "fieldname": "error_log",
-   "fieldtype": "Read Only",
-   "label": "Error Log"
-  },
-  {
-   "fieldname": "accounting_dimensions_section",
-   "fieldtype": "Section Break",
-   "label": "Accounting Dimensions"
-  },
-  {
-   "fieldname": "dimension_col_break",
-   "fieldtype": "Column Break"
-  },
-  {
-   "default": "Today",
-   "fieldname": "posting_date",
-   "fieldtype": "Date",
-   "label": "Posting Date",
-   "reqd": 1
-  }
- ],
- "is_submittable": 1,
- "links": [],
- "modified": "2020-07-18 05:11:49.905457",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Fee Schedule",
- "owner": "Administrator",
- "permissions": [
-  {
-   "amend": 1,
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "import": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Accounts User",
-   "share": 1,
-   "submit": 1,
-   "write": 1
-  },
-  {
-   "cancel": 1,
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Accounts Manager",
-   "share": 1,
-   "submit": 1,
-   "write": 1
-  }
- ],
- "restrict_to_domain": "Education",
- "sort_field": "modified",
- "sort_order": "DESC"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.py b/erpnext/education/doctype/fee_schedule/fee_schedule.py
deleted file mode 100644
index a122fe8..0000000
--- a/erpnext/education/doctype/fee_schedule/fee_schedule.py
+++ /dev/null
@@ -1,149 +0,0 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.model.mapper import get_mapped_doc
-from frappe.utils import cint, cstr, flt, money_in_words
-from frappe.utils.background_jobs import enqueue
-
-import erpnext
-
-
-class FeeSchedule(Document):
-	def onload(self):
-		info = self.get_dashboard_info()
-		self.set_onload('dashboard_info', info)
-
-	def get_dashboard_info(self):
-		info = {
-			"total_paid": 0,
-			"total_unpaid": 0,
-			"currency": erpnext.get_company_currency(self.company)
-		}
-
-		fees_amount = frappe.db.sql("""select sum(grand_total), sum(outstanding_amount) from tabFees
-			where fee_schedule=%s and docstatus=1""", (self.name))
-
-		if fees_amount:
-			info["total_paid"] = flt(fees_amount[0][0]) - flt(fees_amount[0][1])
-			info["total_unpaid"] = flt(fees_amount[0][1])
-
-		return info
-
-	def validate(self):
-		self.calculate_total_and_program()
-
-	def calculate_total_and_program(self):
-		no_of_students = 0
-		for d in self.student_groups:
-			# if not d.total_students:
-			d.total_students = get_total_students(d.student_group, self.academic_year,
-				self.academic_term, self.student_category)
-			no_of_students += cint(d.total_students)
-
-			# validate the program of fee structure and student groups
-			student_group_program = frappe.db.get_value("Student Group", d.student_group, "program")
-			if self.program and student_group_program and self.program != student_group_program:
-				frappe.msgprint(_("Program in the Fee Structure and Student Group {0} are different.")
-					.format(d.student_group))
-		self.grand_total = no_of_students*self.total_amount
-		self.grand_total_in_words = money_in_words(self.grand_total)
-
-	@frappe.whitelist()
-	def create_fees(self):
-		self.db_set("fee_creation_status", "In Process")
-		frappe.publish_realtime("fee_schedule_progress",
-			{"progress": "0", "reload": 1}, user=frappe.session.user)
-
-		total_records = sum([int(d.total_students) for d in self.student_groups])
-		if total_records > 10:
-			frappe.msgprint(_('''Fee records will be created in the background.
-				In case of any error the error message will be updated in the Schedule.'''))
-			enqueue(generate_fee, queue='default', timeout=6000, event='generate_fee',
-				fee_schedule=self.name)
-		else:
-			generate_fee(self.name)
-
-def generate_fee(fee_schedule):
-	doc = frappe.get_doc("Fee Schedule", fee_schedule)
-	error = False
-	total_records = sum([int(d.total_students) for d in doc.student_groups])
-	created_records = 0
-
-	if not total_records:
-		frappe.throw(_("Please setup Students under Student Groups"))
-
-	for d in doc.student_groups:
-		students = get_students(d.student_group, doc.academic_year, doc.academic_term, doc.student_category)
-		for student in students:
-			try:
-				fees_doc = get_mapped_doc("Fee Schedule", fee_schedule,	{
-					"Fee Schedule": {
-						"doctype": "Fees",
-						"field_map": {
-							"name": "Fee Schedule"
-						}
-					}
-				})
-				fees_doc.posting_date = doc.posting_date
-				fees_doc.student = student.student
-				fees_doc.student_name = student.student_name
-				fees_doc.program = student.program
-				fees_doc.student_batch = student.student_batch_name
-				fees_doc.send_payment_request = doc.send_email
-				fees_doc.save()
-				fees_doc.submit()
-				created_records += 1
-				frappe.publish_realtime("fee_schedule_progress", {"progress": str(int(created_records * 100/total_records))}, user=frappe.session.user)
-
-			except Exception as e:
-				error = True
-				err_msg = frappe.local.message_log and "\n\n".join(frappe.local.message_log) or cstr(e)
-
-	if error:
-		frappe.db.rollback()
-		frappe.db.set_value("Fee Schedule", fee_schedule, "fee_creation_status", "Failed")
-		frappe.db.set_value("Fee Schedule", fee_schedule, "error_log", err_msg)
-
-	else:
-		frappe.db.set_value("Fee Schedule", fee_schedule, "fee_creation_status", "Successful")
-		frappe.db.set_value("Fee Schedule", fee_schedule, "error_log", None)
-
-	frappe.publish_realtime("fee_schedule_progress",
-		{"progress": "100", "reload": 1}, user=frappe.session.user)
-
-
-def get_students(student_group, academic_year, academic_term=None, student_category=None):
-	conditions = ""
-	if student_category:
-		conditions = " and pe.student_category={}".format(frappe.db.escape(student_category))
-	if academic_term:
-		conditions = " and pe.academic_term={}".format(frappe.db.escape(academic_term))
-
-	students = frappe.db.sql("""
-		select pe.student, pe.student_name, pe.program, pe.student_batch_name
-		from `tabStudent Group Student` sgs, `tabProgram Enrollment` pe
-		where
-			pe.student = sgs.student and pe.academic_year = %s
-			and sgs.parent = %s and sgs.active = 1
-			{conditions}
-		""".format(conditions=conditions), (academic_year, student_group), as_dict=1)
-	return students
-
-
-@frappe.whitelist()
-def get_total_students(student_group, academic_year, academic_term=None, student_category=None):
-	total_students = get_students(student_group, academic_year, academic_term, student_category)
-	return len(total_students)
-
-
-@frappe.whitelist()
-def get_fee_structure(source_name,target_doc=None):
-	fee_request = get_mapped_doc("Fee Structure", source_name,
-		{"Fee Structure": {
-			"doctype": "Fee Schedule"
-		}}, ignore_permissions=True)
-	return fee_request
diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule_dashboard.py b/erpnext/education/doctype/fee_schedule/fee_schedule_dashboard.py
deleted file mode 100644
index f5d1dee..0000000
--- a/erpnext/education/doctype/fee_schedule/fee_schedule_dashboard.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-
-def get_data():
-	return {
-		'fieldname': 'fee_schedule',
-		'transactions': [
-			{
-				'items': ['Fees']
-			}
-		]
-	}
diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule_list.js b/erpnext/education/doctype/fee_schedule/fee_schedule_list.js
deleted file mode 100644
index 3039c51..0000000
--- a/erpnext/education/doctype/fee_schedule/fee_schedule_list.js
+++ /dev/null
@@ -1,14 +0,0 @@
-frappe.listview_settings['Fee Schedule'] = {
-	add_fields: ["fee_creation_status", "due_date", "grand_total"],
-	get_indicator: function(doc) {
-		if (doc.fee_creation_status=="Successful") {
-			return [__("Fee Created"), "blue", "fee_creation_status,=,Successful"];
-		} else if(doc.fee_creation_status == "In Process") {
-			return [__("Creating Fees"), "orange", "fee_creation_status,=,In Process"];
-		} else if(doc.fee_creation_status == "Failed") {
-			return [__("Fee Creation Failed"), "red", "fee_creation_status,=,Failed"];
-		} else {
-			return [__("Fee Creation Pending"), "green", "fee_creation_status,=,"];
-		}
-	}
-};
diff --git a/erpnext/education/doctype/fee_schedule/test_fee_schedule.py b/erpnext/education/doctype/fee_schedule/test_fee_schedule.py
deleted file mode 100644
index c291aed..0000000
--- a/erpnext/education/doctype/fee_schedule/test_fee_schedule.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestFeeSchedule(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/fee_schedule_program/__init__.py b/erpnext/education/doctype/fee_schedule_program/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/fee_schedule_program/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/fee_schedule_program/fee_schedule_program.json b/erpnext/education/doctype/fee_schedule_program/fee_schedule_program.json
deleted file mode 100644
index f644dc2..0000000
--- a/erpnext/education/doctype/fee_schedule_program/fee_schedule_program.json
+++ /dev/null
@@ -1,142 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2017-03-23 17:46:55.712169", 
- "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": "program", 
-   "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": "Program", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Program", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "student_batch", 
-   "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": "Student Batch", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student Batch Name", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "total_students", 
-   "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": "Total Students", 
-   "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": "2018-11-04 03:37:57.763134", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Fee Schedule Program", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "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/education/doctype/fee_schedule_program/fee_schedule_program.py b/erpnext/education/doctype/fee_schedule_program/fee_schedule_program.py
deleted file mode 100644
index ad7af3a..0000000
--- a/erpnext/education/doctype/fee_schedule_program/fee_schedule_program.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class FeeScheduleProgram(Document):
-	pass
diff --git a/erpnext/education/doctype/fee_schedule_student_group/__init__.py b/erpnext/education/doctype/fee_schedule_student_group/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/fee_schedule_student_group/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/fee_schedule_student_group/fee_schedule_student_group.json b/erpnext/education/doctype/fee_schedule_student_group/fee_schedule_student_group.json
deleted file mode 100644
index d4e0acb..0000000
--- a/erpnext/education/doctype/fee_schedule_student_group/fee_schedule_student_group.json
+++ /dev/null
@@ -1,109 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2017-03-23 17:55:52.476822", 
- "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": "student_group", 
-   "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": "Student Group", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student Group", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "total_students", 
-   "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": "Total Students", 
-   "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": "2018-11-04 03:38:06.535706", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Fee Schedule Student Group", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "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/education/doctype/fee_schedule_student_group/fee_schedule_student_group.py b/erpnext/education/doctype/fee_schedule_student_group/fee_schedule_student_group.py
deleted file mode 100644
index 24e5404..0000000
--- a/erpnext/education/doctype/fee_schedule_student_group/fee_schedule_student_group.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class FeeScheduleStudentGroup(Document):
-	pass
diff --git a/erpnext/education/doctype/fee_structure/__init__.py b/erpnext/education/doctype/fee_structure/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/fee_structure/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/fee_structure/fee_structure.js b/erpnext/education/doctype/fee_structure/fee_structure.js
deleted file mode 100644
index d9ab99f..0000000
--- a/erpnext/education/doctype/fee_structure/fee_structure.js
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.provide("erpnext.accounts.dimensions");
-
-frappe.ui.form.on('Fee Structure', {
-	setup: function(frm) {
-		frm.add_fetch('company', 'default_receivable_account', 'receivable_account');
-		frm.add_fetch('company', 'default_income_account', 'income_account');
-		frm.add_fetch('company', 'cost_center', 'cost_center');
-	},
-
-	company: function(frm) {
-		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
-	},
-
-	onload: function(frm) {
-		frm.set_query('academic_term', function() {
-			return {
-				'filters': {
-					'academic_year': frm.doc.academic_year
-				}
-			};
-		});
-
-		frm.set_query('receivable_account', function(doc) {
-			return {
-				filters: {
-					'account_type': 'Receivable',
-					'is_group': 0,
-					'company': doc.company
-				}
-			};
-		});
-		frm.set_query('income_account', function(doc) {
-			return {
-				filters: {
-					'account_type': 'Income Account',
-					'is_group': 0,
-					'company': doc.company
-				}
-			};
-		});
-
-		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
-	},
-
-	refresh: function(frm) {
-		if (frm.doc.docstatus === 1) {
-			frm.add_custom_button(__('Create Fee Schedule'), function() {
-				frm.events.make_fee_schedule(frm);
-			}).addClass('btn-primary');
-		}
-	},
-
-	make_fee_schedule: function(frm) {
-		frappe.model.open_mapped_doc({
-			method: 'erpnext.education.doctype.fee_structure.fee_structure.make_fee_schedule',
-			frm: frm
-		});
-	}
-});
-
-frappe.ui.form.on('Fee Component', {
-	amount: function(frm) {
-		var total_amount = 0;
-		for (var i=0;i<frm.doc.components.length;i++) {
-			total_amount += frm.doc.components[i].amount;
-		}
-		frm.set_value('total_amount', total_amount);
-	}
-});
diff --git a/erpnext/education/doctype/fee_structure/fee_structure.json b/erpnext/education/doctype/fee_structure/fee_structure.json
deleted file mode 100644
index 67e4637..0000000
--- a/erpnext/education/doctype/fee_structure/fee_structure.json
+++ /dev/null
@@ -1,215 +0,0 @@
-{
- "actions": [],
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "naming_series:",
- "creation": "2015-09-16 13:03:20.430704",
- "doctype": "DocType",
- "document_type": "Setup",
- "engine": "InnoDB",
- "field_order": [
-  "naming_series",
-  "program",
-  "student_category",
-  "column_break_2",
-  "academic_year",
-  "academic_term",
-  "section_break_4",
-  "components",
-  "section_break_6",
-  "column_break_11",
-  "total_amount",
-  "accounts",
-  "receivable_account",
-  "income_account",
-  "column_break_16",
-  "company",
-  "amended_from",
-  "accounting_dimensions_section",
-  "cost_center",
-  "dimension_col_break"
- ],
- "fields": [
-  {
-   "fieldname": "naming_series",
-   "fieldtype": "Select",
-   "label": "Naming Series",
-   "options": "EDU-FST-.YYYY.-",
-   "set_only_once": 1
-  },
-  {
-   "fieldname": "program",
-   "fieldtype": "Link",
-   "in_global_search": 1,
-   "in_list_view": 1,
-   "in_standard_filter": 1,
-   "label": "Program",
-   "oldfieldname": "earning_name",
-   "oldfieldtype": "Data",
-   "options": "Program",
-   "search_index": 1
-  },
-  {
-   "fieldname": "student_category",
-   "fieldtype": "Link",
-   "label": "Student Category",
-   "options": "Student Category"
-  },
-  {
-   "fieldname": "column_break_2",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "academic_term",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "in_standard_filter": 1,
-   "label": "Academic Term",
-   "oldfieldname": "description",
-   "oldfieldtype": "Small Text",
-   "options": "Academic Term",
-   "search_index": 1,
-   "width": "300px"
-  },
-  {
-   "fieldname": "academic_year",
-   "fieldtype": "Link",
-   "label": "Academic Year",
-   "options": "Academic Year"
-  },
-  {
-   "fieldname": "section_break_4",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fieldname": "components",
-   "fieldtype": "Table",
-   "label": "Components",
-   "options": "Fee Component",
-   "reqd": 1
-  },
-  {
-   "fieldname": "section_break_6",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fieldname": "column_break_11",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "total_amount",
-   "fieldtype": "Currency",
-   "in_list_view": 1,
-   "label": "Total Amount",
-   "read_only": 1
-  },
-  {
-   "fieldname": "accounts",
-   "fieldtype": "Section Break",
-   "label": "Accounts"
-  },
-  {
-   "fieldname": "receivable_account",
-   "fieldtype": "Link",
-   "label": "Receivable Account",
-   "options": "Account",
-   "reqd": 1
-  },
-  {
-   "fieldname": "income_account",
-   "fieldtype": "Link",
-   "label": "Income Account",
-   "options": "Account"
-  },
-  {
-   "fieldname": "column_break_16",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "cost_center",
-   "fieldtype": "Link",
-   "label": "Cost Center",
-   "options": "Cost Center"
-  },
-  {
-   "fieldname": "company",
-   "fieldtype": "Link",
-   "label": "Company",
-   "options": "Company"
-  },
-  {
-   "fieldname": "amended_from",
-   "fieldtype": "Link",
-   "label": "Amended From",
-   "no_copy": 1,
-   "options": "Fee Structure",
-   "print_hide": 1,
-   "read_only": 1
-  },
-  {
-   "fieldname": "accounting_dimensions_section",
-   "fieldtype": "Section Break",
-   "label": "Accounting Dimensions"
-  },
-  {
-   "fieldname": "dimension_col_break",
-   "fieldtype": "Column Break"
-  }
- ],
- "icon": "fa fa-flag",
- "is_submittable": 1,
- "links": [],
- "modified": "2020-06-16 15:34:57.295010",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Fee Structure",
- "owner": "Administrator",
- "permissions": [
-  {
-   "amend": 1,
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "import": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Accounts User",
-   "share": 1,
-   "submit": 1,
-   "write": 1
-  },
-  {
-   "cancel": 1,
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Accounts Manager",
-   "share": 1,
-   "submit": 1,
-   "write": 1
-  }
- ],
- "restrict_to_domain": "Education",
- "search_fields": "program, student_category, academic_year",
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "program"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/fee_structure/fee_structure.py b/erpnext/education/doctype/fee_structure/fee_structure.py
deleted file mode 100644
index 9090a6b..0000000
--- a/erpnext/education/doctype/fee_structure/fee_structure.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe.model.document import Document
-from frappe.model.mapper import get_mapped_doc
-
-
-class FeeStructure(Document):
-	def validate(self):
-		self.calculate_total()
-
-	def calculate_total(self):
-		"""Calculates total amount."""
-		self.total_amount = 0
-		for d in self.components:
-			self.total_amount += d.amount
-
-
-@frappe.whitelist()
-def make_fee_schedule(source_name, target_doc=None):
-	return get_mapped_doc("Fee Structure", source_name,	{
-		"Fee Structure": {
-			"doctype": "Fee Schedule",
-			"validation": {
-				"docstatus": ["=", 1],
-			}
-		},
-		"Fee Component": {
-			"doctype": "Fee Component"
-		}
-	}, target_doc)
diff --git a/erpnext/education/doctype/fee_structure/fee_structure_dashboard.py b/erpnext/education/doctype/fee_structure/fee_structure_dashboard.py
deleted file mode 100644
index 27ce06b..0000000
--- a/erpnext/education/doctype/fee_structure/fee_structure_dashboard.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from frappe import _
-
-
-def get_data():
-	return {
-		'fieldname': 'fee_structure',
-		'transactions': [
-			{
-                'label': _('Fee'),
-				'items': ['Fees', 'Fee Schedule']
-			}
-		]
-	}
diff --git a/erpnext/education/doctype/fee_structure/test_fee_structure.py b/erpnext/education/doctype/fee_structure/test_fee_structure.py
deleted file mode 100644
index 61381a6..0000000
--- a/erpnext/education/doctype/fee_structure/test_fee_structure.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and Contributors
-# See license.txt
-
-import unittest
-
-# test_records = frappe.get_test_records('Fee Structure')
-
-class TestFeeStructure(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/fee_structure/test_records.json b/erpnext/education/doctype/fee_structure/test_records.json
deleted file mode 100644
index cdd00be..0000000
--- a/erpnext/education/doctype/fee_structure/test_records.json
+++ /dev/null
@@ -1,42 +0,0 @@
-[
-	{
-		"doctype": "Fee Structure",
-		"academic_year": "2017-2018",
-		"academic_term": "2017-2018 (_Test AT1)",
-		"components": [
-			{
-				"fees_category": "Tuition Fee",
-				"amount": 40000
-			},
-			{
-				"fees_category": "Transportation Fee",
-				"amount": 10000
-			}
-		],
-		"total_amount": 50000,
-		"receivable_account": "_Test Receivable - _TC",
-		"income_account": "Sales - _TC",
-		"cost_center": "_Test Cost Center - _TC",
-		"company": "_Test Company"
-	},
-	{
-		"doctype": "Fee Structure",
-		"academic_year": "2017-2018",
-		"academic_term": "2017-2018 (_Test AT2)",
-		"components": [
-			{
-				"fees_category": "Tuition Fee",
-				"amount": 40000
-			},
-			{
-				"fees_category": "Transportation Fee",
-				"amount": 10000
-			}
-		],
-		"total_amount": 50000,
-		"receivable_account": "_Test Receivable - _TC",
-		"income_account": "Sales - _TC",
-		"cost_center": "_Test Cost Center - _TC",
-		"company": "_Test Company"
-	}
-]
\ No newline at end of file
diff --git a/erpnext/education/doctype/fees/__init__.py b/erpnext/education/doctype/fees/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/fees/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/fees/fees.js b/erpnext/education/doctype/fees/fees.js
deleted file mode 100644
index ac66acd..0000000
--- a/erpnext/education/doctype/fees/fees.js
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.provide("erpnext.accounts.dimensions");
-
-frappe.ui.form.on("Fees", {
-	setup: function(frm) {
-		frm.add_fetch("fee_structure", "receivable_account", "receivable_account");
-		frm.add_fetch("fee_structure", "income_account", "income_account");
-		frm.add_fetch("fee_structure", "cost_center", "cost_center");
-	},
-
-	company: function(frm) {
-		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
-	},
-
-	onload: function(frm) {
-		frm.set_query("academic_term", function() {
-			return{
-				"filters": {
-					"academic_year": (frm.doc.academic_year)
-				}
-			};
-		});
-		frm.set_query("fee_structure", function() {
-			return{
-				"filters":{
-					"academic_year": (frm.doc.academic_year)
-				}
-			};
-		});
-		frm.set_query("receivable_account", function(doc) {
-			return {
-				filters: {
-					'account_type': 'Receivable',
-					'is_group': 0,
-					'company': doc.company
-				}
-			};
-		});
-		frm.set_query("income_account", function(doc) {
-			return {
-				filters: {
-					'account_type': 'Income Account',
-					'is_group': 0,
-					'company': doc.company
-				}
-			};
-		});
-		if (!frm.doc.posting_date) {
-			frm.doc.posting_date = frappe.datetime.get_today();
-		}
-
-		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
-	},
-
-	refresh: function(frm) {
-		if(frm.doc.docstatus == 0 && frm.doc.set_posting_time) {
-			frm.set_df_property('posting_date', 'read_only', 0);
-			frm.set_df_property('posting_time', 'read_only', 0);
-		} else {
-			frm.set_df_property('posting_date', 'read_only', 1);
-			frm.set_df_property('posting_time', 'read_only', 1);
-		}
-		if(frm.doc.docstatus > 0) {
-			frm.add_custom_button(__('Accounting Ledger'), function() {
-				frappe.route_options = {
-					voucher_no: frm.doc.name,
-					from_date: frm.doc.posting_date,
-					to_date: moment(frm.doc.modified).format('YYYY-MM-DD'),
-					company: frm.doc.company,
-					group_by: '',
-					show_cancelled_entries: frm.doc.docstatus === 2
-				};
-				frappe.set_route("query-report", "General Ledger");
-			}, __("View"));
-			frm.add_custom_button(__("Payments"), function() {
-				frappe.set_route("List", "Payment Entry", {"Payment Entry Reference.reference_name": frm.doc.name});
-			}, __("View"));
-		}
-		if(frm.doc.docstatus===1 && frm.doc.outstanding_amount>0) {
-			frm.add_custom_button(__("Payment Request"), function() {
-				frm.events.make_payment_request(frm);
-			}, __('Create'));
-			frm.page.set_inner_btn_group_as_primary(__('Create'));
-		}
-		if(frm.doc.docstatus===1 && frm.doc.outstanding_amount!=0) {
-			frm.add_custom_button(__("Payment"), function() {
-				frm.events.make_payment_entry(frm);
-			}, __('Create'));
-			frm.page.set_inner_btn_group_as_primary(__('Create'));
-		}
-	},
-
-	student: function(frm) {
-		if (frm.doc.student) {
-			frappe.call({
-				method:"erpnext.education.api.get_current_enrollment",
-				args: {
-					"student": frm.doc.student,
-					"academic_year": frm.doc.academic_year
-				},
-				callback: function(r) {
-					if(r){
-						$.each(r.message, function(i, d) {
-							frm.set_value(i,d);
-						});
-					}
-				}
-			});
-		}
-	},
-
-	make_payment_request: function(frm) {
-		if (!frm.doc.student_email) {
-			frappe.msgprint(__("Please set the Email ID for the Student to send the Payment Request"));
-		} else {
-			frappe.call({
-				method:"erpnext.accounts.doctype.payment_request.payment_request.make_payment_request",
-				args: {
-					"dt": frm.doc.doctype,
-					"dn": frm.doc.name,
-					"party_type": "Student",
-					"party": frm.doc.student,
-					"recipient_id": frm.doc.student_email
-				},
-				callback: function(r) {
-					if(!r.exc){
-						var doc = frappe.model.sync(r.message);
-						frappe.set_route("Form", doc[0].doctype, doc[0].name);
-					}
-				}
-			});
-		}
-	},
-
-	make_payment_entry: function(frm) {
-		return frappe.call({
-			method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry",
-			args: {
-				"dt": frm.doc.doctype,
-				"dn": frm.doc.name
-			},
-			callback: function(r) {
-				var doc = frappe.model.sync(r.message);
-				frappe.set_route("Form", doc[0].doctype, doc[0].name);
-			}
-		});
-	},
-
-	set_posting_time: function(frm) {
-		frm.refresh();
-	},
-
-	academic_term: function() {
-		frappe.ui.form.trigger("Fees", "program");
-	},
-
-	fee_structure: function(frm) {
-		frm.set_value("components" ,"");
-		if (frm.doc.fee_structure) {
-			frappe.call({
-				method: "erpnext.education.api.get_fee_components",
-				args: {
-					"fee_structure": frm.doc.fee_structure
-				},
-				callback: function(r) {
-					if (r.message) {
-						$.each(r.message, function(i, d) {
-							var row = frappe.model.add_child(frm.doc, "Fee Component", "components");
-							row.fees_category = d.fees_category;
-							row.description = d.description;
-							row.amount = d.amount;
-						});
-					}
-					refresh_field("components");
-					frm.trigger("calculate_total_amount");
-				}
-			});
-		}
-	},
-
-	calculate_total_amount: function(frm) {
-		var grand_total = 0;
-		for(var i=0;i<frm.doc.components.length;i++) {
-			grand_total += frm.doc.components[i].amount;
-		}
-		frm.set_value("grand_total", grand_total);
-	}
-});
-
-
-frappe.ui.form.on("Fee Component", {
-	amount: function(frm) {
-		frm.trigger("calculate_total_amount");
-	}
-});
diff --git a/erpnext/education/doctype/fees/fees.json b/erpnext/education/doctype/fees/fees.json
deleted file mode 100644
index 0fb7672..0000000
--- a/erpnext/education/doctype/fees/fees.json
+++ /dev/null
@@ -1,395 +0,0 @@
-{
- "actions": [],
- "allow_import": 1,
- "autoname": "naming_series:",
- "creation": "2015-09-22 16:57:22.143710",
- "doctype": "DocType",
- "document_type": "Document",
- "engine": "InnoDB",
- "field_order": [
-  "naming_series",
-  "student",
-  "student_name",
-  "fee_schedule",
-  "include_payment",
-  "send_payment_request",
-  "column_break_4",
-  "company",
-  "posting_date",
-  "posting_time",
-  "set_posting_time",
-  "due_date",
-  "student_details",
-  "program_enrollment",
-  "program",
-  "student_batch",
-  "student_email",
-  "column_break_16",
-  "student_category",
-  "academic_term",
-  "academic_year",
-  "section_break_7",
-  "currency",
-  "fee_structure",
-  "components",
-  "section_break_10",
-  "amended_from",
-  "column_break_11",
-  "grand_total",
-  "grand_total_in_words",
-  "outstanding_amount",
-  "edit_printing_settings",
-  "letter_head",
-  "column_break_32",
-  "select_print_heading",
-  "account",
-  "receivable_account",
-  "column_break_39",
-  "income_account",
-  "accounting_dimensions_section",
-  "cost_center",
-  "dimension_col_break"
- ],
- "fields": [
-  {
-   "fieldname": "naming_series",
-   "fieldtype": "Select",
-   "label": "Naming Series",
-   "options": "EDU-FEE-.YYYY.-",
-   "print_hide": 1,
-   "set_only_once": 1
-  },
-  {
-   "fieldname": "student",
-   "fieldtype": "Link",
-   "in_global_search": 1,
-   "label": "Student",
-   "options": "Student",
-   "reqd": 1
-  },
-  {
-   "fetch_from": "student.title",
-   "fieldname": "student_name",
-   "fieldtype": "Data",
-   "in_global_search": 1,
-   "label": "Student Name",
-   "read_only": 1
-  },
-  {
-   "fieldname": "fee_schedule",
-   "fieldtype": "Link",
-   "in_global_search": 1,
-   "label": "Fee Schedule",
-   "options": "Fee Schedule",
-   "print_hide": 1,
-   "read_only": 1
-  },
-  {
-   "default": "0",
-   "fieldname": "include_payment",
-   "fieldtype": "Check",
-   "hidden": 1,
-   "label": "Include Payment",
-   "print_hide": 1
-  },
-  {
-   "default": "0",
-   "fieldname": "send_payment_request",
-   "fieldtype": "Check",
-   "label": "Send Payment Request",
-   "no_copy": 1,
-   "print_hide": 1
-  },
-  {
-   "fieldname": "column_break_4",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "company",
-   "fieldtype": "Link",
-   "label": "Institution",
-   "options": "Company",
-   "remember_last_selected_value": 1,
-   "reqd": 1
-  },
-  {
-   "bold": 1,
-   "default": "Today",
-   "fieldname": "posting_date",
-   "fieldtype": "Date",
-   "label": "Date",
-   "no_copy": 1,
-   "reqd": 1,
-   "search_index": 1
-  },
-  {
-   "fieldname": "posting_time",
-   "fieldtype": "Time",
-   "label": "Posting Time",
-   "no_copy": 1
-  },
-  {
-   "default": "0",
-   "depends_on": "eval:doc.docstatus==0",
-   "fieldname": "set_posting_time",
-   "fieldtype": "Check",
-   "label": "Edit Posting Date and Time",
-   "print_hide": 1
-  },
-  {
-   "fieldname": "due_date",
-   "fieldtype": "Date",
-   "label": "Due Date",
-   "reqd": 1
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "student_details",
-   "fieldtype": "Section Break",
-   "label": "Student Details"
-  },
-  {
-   "fieldname": "program_enrollment",
-   "fieldtype": "Link",
-   "label": "Program Enrollment",
-   "options": "Program Enrollment"
-  },
-  {
-   "fieldname": "program",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "in_standard_filter": 1,
-   "label": "Program",
-   "options": "Program",
-   "reqd": 1
-  },
-  {
-   "fieldname": "student_batch",
-   "fieldtype": "Link",
-   "label": "Student Batch",
-   "options": "Student Batch Name",
-   "print_hide": 1
-  },
-  {
-   "allow_on_submit": 1,
-   "fieldname": "student_email",
-   "fieldtype": "Data",
-   "label": "Student Email",
-   "options": "Email",
-   "print_hide": 1
-  },
-  {
-   "fieldname": "column_break_16",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "student_category",
-   "fieldtype": "Link",
-   "label": "Student Category",
-   "options": "Student Category"
-  },
-  {
-   "fieldname": "academic_term",
-   "fieldtype": "Link",
-   "label": "Academic Term",
-   "options": "Academic Term"
-  },
-  {
-   "fieldname": "academic_year",
-   "fieldtype": "Link",
-   "label": "Academic Year",
-   "options": "Academic Year"
-  },
-  {
-   "fieldname": "section_break_7",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fieldname": "currency",
-   "fieldtype": "Link",
-   "hidden": 1,
-   "label": "Currency",
-   "options": "Currency",
-   "print_hide": 1,
-   "read_only": 1
-  },
-  {
-   "fieldname": "fee_structure",
-   "fieldtype": "Link",
-   "label": "Fee Structure",
-   "options": "Fee Structure",
-   "print_hide": 1
-  },
-  {
-   "fieldname": "components",
-   "fieldtype": "Table",
-   "label": "Components",
-   "options": "Fee Component",
-   "reqd": 1
-  },
-  {
-   "fieldname": "section_break_10",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fieldname": "amended_from",
-   "fieldtype": "Link",
-   "label": "Amended From",
-   "no_copy": 1,
-   "options": "Fees",
-   "print_hide": 1,
-   "read_only": 1
-  },
-  {
-   "fieldname": "column_break_11",
-   "fieldtype": "Column Break"
-  },
-  {
-   "default": "0",
-   "fieldname": "grand_total",
-   "fieldtype": "Currency",
-   "label": "Grand Total",
-   "read_only": 1
-  },
-  {
-   "fieldname": "grand_total_in_words",
-   "fieldtype": "Data",
-   "label": "In Words",
-   "length": 240,
-   "read_only": 1
-  },
-  {
-   "default": "0",
-   "fieldname": "outstanding_amount",
-   "fieldtype": "Currency",
-   "label": "Outstanding Amount",
-   "no_copy": 1,
-   "read_only": 1
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "edit_printing_settings",
-   "fieldtype": "Section Break",
-   "label": "Printing Settings",
-   "print_hide": 1
-  },
-  {
-   "allow_on_submit": 1,
-   "fieldname": "letter_head",
-   "fieldtype": "Link",
-   "label": "Letter Head",
-   "options": "Letter Head",
-   "print_hide": 1
-  },
-  {
-   "fieldname": "column_break_32",
-   "fieldtype": "Column Break"
-  },
-  {
-   "allow_on_submit": 1,
-   "fieldname": "select_print_heading",
-   "fieldtype": "Link",
-   "label": "Print Heading",
-   "no_copy": 1,
-   "options": "Print Heading",
-   "print_hide": 1,
-   "report_hide": 1
-  },
-  {
-   "fieldname": "account",
-   "fieldtype": "Section Break",
-   "label": "Accounting",
-   "print_hide": 1
-  },
-  {
-   "fieldname": "receivable_account",
-   "fieldtype": "Link",
-   "label": "Receivable Account",
-   "options": "Account",
-   "print_hide": 1,
-   "reqd": 1
-  },
-  {
-   "fieldname": "income_account",
-   "fieldtype": "Link",
-   "label": "Income Account",
-   "options": "Account",
-   "print_hide": 1
-  },
-  {
-   "fieldname": "column_break_39",
-   "fieldtype": "Column Break",
-   "print_hide": 1
-  },
-  {
-   "fieldname": "cost_center",
-   "fieldtype": "Link",
-   "label": "Cost Center",
-   "options": "Cost Center",
-   "print_hide": 1
-  },
-  {
-   "fieldname": "accounting_dimensions_section",
-   "fieldtype": "Section Break",
-   "label": "Accounting Dimensions"
-  },
-  {
-   "fieldname": "dimension_col_break",
-   "fieldtype": "Column Break"
-  }
- ],
- "is_submittable": 1,
- "links": [],
- "modified": "2020-08-05 14:05:47.728409",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Fees",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Accounts User",
-   "share": 1,
-   "submit": 1,
-   "write": 1
-  },
-  {
-   "cancel": 1,
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Accounts Manager",
-   "share": 1,
-   "submit": 1,
-   "write": 1
-  }
- ],
- "restrict_to_domain": "Education",
- "search_fields": "student, student_name",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "student_name"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/fees/fees.py b/erpnext/education/doctype/fees/fees.py
deleted file mode 100644
index 41d428d..0000000
--- a/erpnext/education/doctype/fees/fees.py
+++ /dev/null
@@ -1,135 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.utils import money_in_words
-from frappe.utils.csvutils import getlink
-
-import erpnext
-from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
-from erpnext.accounts.general_ledger import make_reverse_gl_entries
-from erpnext.controllers.accounts_controller import AccountsController
-
-
-class Fees(AccountsController):
-	def set_indicator(self):
-		"""Set indicator for portal"""
-		if self.outstanding_amount > 0:
-			self.indicator_color = "orange"
-			self.indicator_title = _("Unpaid")
-		else:
-			self.indicator_color = "green"
-			self.indicator_title = _("Paid")
-
-	def validate(self):
-		self.calculate_total()
-		self.set_missing_accounts_and_fields()
-
-	def set_missing_accounts_and_fields(self):
-		if not self.company:
-			self.company = frappe.defaults.get_defaults().company
-		if not self.currency:
-			self.currency = erpnext.get_company_currency(self.company)
-		if not (self.receivable_account and self.income_account and self.cost_center):
-			accounts_details = frappe.get_all("Company",
-				fields=["default_receivable_account", "default_income_account", "cost_center"],
-				filters={"name": self.company})[0]
-		if not self.receivable_account:
-			self.receivable_account = accounts_details.default_receivable_account
-		if not self.income_account:
-			self.income_account = accounts_details.default_income_account
-		if not self.cost_center:
-			self.cost_center = accounts_details.cost_center
-		if not self.student_email:
-			self.student_email = self.get_student_emails()
-
-	def get_student_emails(self):
-		student_emails = frappe.db.sql_list("""
-			select g.email_address
-			from `tabGuardian` g, `tabStudent Guardian` sg
-			where g.name = sg.guardian and sg.parent = %s and sg.parenttype = 'Student'
-			and ifnull(g.email_address, '')!=''
-		""", self.student)
-
-		student_email_id = frappe.db.get_value("Student", self.student, "student_email_id")
-		if student_email_id:
-			student_emails.append(student_email_id)
-		if student_emails:
-			return ", ".join(list(set(student_emails)))
-		else:
-			return None
-
-
-	def calculate_total(self):
-		"""Calculates total amount."""
-		self.grand_total = 0
-		for d in self.components:
-			self.grand_total += d.amount
-		self.outstanding_amount = self.grand_total
-		self.grand_total_in_words = money_in_words(self.grand_total)
-
-	def on_submit(self):
-
-		self.make_gl_entries()
-
-		if self.send_payment_request and self.student_email:
-			pr = make_payment_request(party_type="Student", party=self.student, dt="Fees",
-					dn=self.name, recipient_id=self.student_email,
-					submit_doc=True, use_dummy_message=True)
-			frappe.msgprint(_("Payment request {0} created").format(getlink("Payment Request", pr.name)))
-
-	def on_cancel(self):
-		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
-		make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
-		# frappe.db.set(self, 'status', 'Cancelled')
-
-
-	def make_gl_entries(self):
-		if not self.grand_total:
-			return
-		student_gl_entries =  self.get_gl_dict({
-			"account": self.receivable_account,
-			"party_type": "Student",
-			"party": self.student,
-			"against": self.income_account,
-			"debit": self.grand_total,
-			"debit_in_account_currency": self.grand_total,
-			"against_voucher": self.name,
-			"against_voucher_type": self.doctype
-		}, item=self)
-
-		fee_gl_entry = self.get_gl_dict({
-			"account": self.income_account,
-			"against": self.student,
-			"credit": self.grand_total,
-			"credit_in_account_currency": self.grand_total,
-			"cost_center": self.cost_center
-		}, item=self)
-
-		from erpnext.accounts.general_ledger import make_gl_entries
-		make_gl_entries([student_gl_entries, fee_gl_entry], cancel=(self.docstatus == 2),
-			update_outstanding="Yes", merge_entries=False)
-
-def get_fee_list(doctype, txt, filters, limit_start, limit_page_length=20, order_by="modified"):
-	user = frappe.session.user
-	student = frappe.db.sql("select name from `tabStudent` where student_email_id= %s", user)
-	if student:
-		return frappe. db.sql('''
-			select name, program, due_date, grand_total - outstanding_amount as paid_amount,
-			outstanding_amount, grand_total, currency
-			from `tabFees`
-			where student= %s and docstatus=1
-			order by due_date asc limit {0} , {1}'''
-			.format(limit_start, limit_page_length), student, as_dict = True)
-
-def get_list_context(context=None):
-	return {
-		"show_sidebar": True,
-		"show_search": True,
-		'no_breadcrumbs': True,
-		"title": _("Fees"),
-		"get_list": get_fee_list,
-		"row_template": "templates/includes/fee/fee_row.html"
-	}
diff --git a/erpnext/education/doctype/fees/fees_list.js b/erpnext/education/doctype/fees/fees_list.js
deleted file mode 100644
index ee8e1e3..0000000
--- a/erpnext/education/doctype/fees/fees_list.js
+++ /dev/null
@@ -1,12 +0,0 @@
-frappe.listview_settings['Fees'] = {
-	add_fields: ["grand_total", "outstanding_amount", "due_date"],
-	get_indicator: function(doc) {
-		if(flt(doc.outstanding_amount)==0) {
-			return [__("Paid"), "green", "outstanding_amount,=,0"];
-		} else if (flt(doc.outstanding_amount) > 0 && doc.due_date >= frappe.datetime.get_today()) {
-			return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>,Today"];
-		} else if (flt(doc.outstanding_amount) > 0 && doc.due_date < frappe.datetime.get_today()) {
-			return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<=,Today"];
-		}
-	}
-};
diff --git a/erpnext/education/doctype/fees/test_fees.py b/erpnext/education/doctype/fees/test_fees.py
deleted file mode 100644
index 72f1d11..0000000
--- a/erpnext/education/doctype/fees/test_fees.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and Contributors
-# See license.txt
-
-import unittest
-
-import frappe
-from frappe.utils import nowdate
-from frappe.utils.make_random import get_random
-
-from erpnext.education.doctype.program.test_program import make_program_and_linked_courses
-
-test_dependencies = ['Company']
-class TestFees(unittest.TestCase):
-
-	def test_fees(self):
-		student = get_random("Student")
-		program = make_program_and_linked_courses("_Test Program 1", ["_Test Course 1", "_Test Course 2"])
-		fee = frappe.new_doc("Fees")
-		fee.posting_date = nowdate()
-		fee.due_date = nowdate()
-		fee.student = student
-		fee.receivable_account = "_Test Receivable - _TC"
-		fee.income_account = "Sales - _TC"
-		fee.cost_center = "_Test Cost Center - _TC"
-		fee.company = "_Test Company"
-		fee.program = program.name
-
-		fee.extend("components", [
-			{
-				"fees_category": "Tuition Fee",
-				"amount": 40000
-			},
-			{
-				"fees_category": "Transportation Fee",
-				"amount": 10000
-			}])
-		fee.save()
-		fee.submit()
-
-		gl_entries = frappe.db.sql("""
-			select account, posting_date, party_type, party, cost_center, fiscal_year, voucher_type,
-			voucher_no, against_voucher_type, against_voucher, cost_center, company, credit, debit
-			from `tabGL Entry` where voucher_type=%s and voucher_no=%s""", ("Fees", fee.name), as_dict=True)
-
-		if gl_entries[0].account == "_Test Receivable - _TC":
-			self.assertEqual(gl_entries[0].debit, 50000)
-			self.assertEqual(gl_entries[0].credit, 0)
-			self.assertEqual(gl_entries[1].debit, 0)
-			self.assertEqual(gl_entries[1].credit, 50000)
-		else:
-			self.assertEqual(gl_entries[0].credit, 50000)
-			self.assertEqual(gl_entries[0].debit, 0)
-			self.assertEqual(gl_entries[1].credit, 0)
-			self.assertEqual(gl_entries[1].debit, 50000)
diff --git a/erpnext/education/doctype/grading_scale/__init__.py b/erpnext/education/doctype/grading_scale/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/grading_scale/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/grading_scale/grading_scale.js b/erpnext/education/doctype/grading_scale/grading_scale.js
deleted file mode 100644
index 622388c..0000000
--- a/erpnext/education/doctype/grading_scale/grading_scale.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Grading Scale', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/grading_scale/grading_scale.json b/erpnext/education/doctype/grading_scale/grading_scale.json
deleted file mode 100644
index fb1e2c7..0000000
--- a/erpnext/education/doctype/grading_scale/grading_scale.json
+++ /dev/null
@@ -1,226 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 1,
- "autoname": "field:grading_scale_name",
- "beta": 0,
- "creation": "2016-08-26 03:06:53.922972",
- "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": "grading_scale_name",
-   "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": "Grading Scale 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": 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": "grading_intervals_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": "Grading Scale Intervals",
-   "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": "intervals",
-   "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": "Intervals",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Grading Scale Interval",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "amended_from",
-   "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Amended From",
-   "length": 0,
-   "no_copy": 1,
-   "options": "Grading Scale",
-   "permlevel": 0,
-   "print_hide": 1,
-   "print_hide_if_no_value": 0,
-   "read_only": 1,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "description",
-   "fieldtype": "Small Text",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Description",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
-  }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 1,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-08-30 00:34:03.368432",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Grading Scale",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [
-  {
-   "amend": 0,
-   "cancel": 1,
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "if_owner": 0,
-   "import": 0,
-   "permlevel": 0,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "set_user_permissions": 0,
-   "share": 1,
-   "submit": 1,
-   "write": 1
-  }
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "restrict_to_domain": "Education",
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "",
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/grading_scale/grading_scale.py b/erpnext/education/doctype/grading_scale/grading_scale.py
deleted file mode 100644
index c4bd158..0000000
--- a/erpnext/education/doctype/grading_scale/grading_scale.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import cint
-
-
-class GradingScale(Document):
-	def validate(self):
-		thresholds = []
-		for d in self.intervals:
-			if d.threshold in thresholds:
-				frappe.throw(_("Treshold {0}% appears more than once").format(d.threshold))
-			else:
-				thresholds.append(cint(d.threshold))
-		if 0 not in thresholds:
-			frappe.throw(_("Please define grade for Threshold 0%"))
diff --git a/erpnext/education/doctype/grading_scale/grading_scale_dashboard.py b/erpnext/education/doctype/grading_scale/grading_scale_dashboard.py
deleted file mode 100644
index b8ae8b0..0000000
--- a/erpnext/education/doctype/grading_scale/grading_scale_dashboard.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from frappe import _
-
-
-def get_data():
-	return {
-		'fieldname': 'grading_scale',
-		'non_standard_fieldnames': {
-			'Course': 'default_grading_scale'
-		},
-		'transactions': [
-			{
-				'label': _('Course'),
-				'items': ['Course']
-			},
-			{
-				'label': _('Assessment'),
-				'items': ['Assessment Plan', 'Assessment Result']
-			}
-		]
-	}
diff --git a/erpnext/education/doctype/grading_scale/test_grading_scale.py b/erpnext/education/doctype/grading_scale/test_grading_scale.py
deleted file mode 100644
index 3ebefda..0000000
--- a/erpnext/education/doctype/grading_scale/test_grading_scale.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-# test_records = frappe.get_test_records('Grading Scale')
-
-class TestGradingScale(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/grading_scale/test_records.json b/erpnext/education/doctype/grading_scale/test_records.json
deleted file mode 100644
index 72b6954..0000000
--- a/erpnext/education/doctype/grading_scale/test_records.json
+++ /dev/null
@@ -1,19 +0,0 @@
-[
- {
-  "grading_scale_name": "_Test Grading Scale",
-  "intervals": [
-      {
-         "grade_code": "A",
-         "threshold": 75
-      },
-      {
-          "grade_code": "B",
-          "threshold": 50
-      },
-      {
-          "grade_code": "C",
-          "threshold": 0
-      }
-  ]
- }
-]
\ No newline at end of file
diff --git a/erpnext/education/doctype/grading_scale_interval/__init__.py b/erpnext/education/doctype/grading_scale_interval/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/grading_scale_interval/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/grading_scale_interval/grading_scale_interval.json b/erpnext/education/doctype/grading_scale_interval/grading_scale_interval.json
deleted file mode 100644
index 5574e1d..0000000
--- a/erpnext/education/doctype/grading_scale_interval/grading_scale_interval.json
+++ /dev/null
@@ -1,133 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2016-08-26 03:11:09.591049", 
- "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": "grade_code", 
-   "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": "Grade 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": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "0", 
-   "fieldname": "threshold", 
-   "fieldtype": "Percent", 
-   "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": "Threshold", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "1", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "grade_description", 
-   "fieldtype": "Small Text", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Grade Description", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2017-11-10 19:08:48.083084", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Grading Scale Interval", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "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/education/doctype/grading_scale_interval/grading_scale_interval.py b/erpnext/education/doctype/grading_scale_interval/grading_scale_interval.py
deleted file mode 100644
index b4101bd..0000000
--- a/erpnext/education/doctype/grading_scale_interval/grading_scale_interval.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class GradingScaleInterval(Document):
-	pass
diff --git a/erpnext/education/doctype/guardian/__init__.py b/erpnext/education/doctype/guardian/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/guardian/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/guardian/guardian.js b/erpnext/education/doctype/guardian/guardian.js
deleted file mode 100644
index b7c2f0a..0000000
--- a/erpnext/education/doctype/guardian/guardian.js
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Guardian', {
-	refresh: function(frm) {
-		if(!frm.doc.user && !frm.is_new()) {
-			frm.add_custom_button(__("Invite as User"), function() {
-				return frappe.call({
-					method: "erpnext.education.doctype.guardian.guardian.invite_guardian",
-					args: {
-						guardian: frm.doc.name
-					},
-					callback: function(r) {
-						frm.set_value("user", r.message);
-					}
-				});
-			});
-		}
-	}
-});
diff --git a/erpnext/education/doctype/guardian/guardian.json b/erpnext/education/doctype/guardian/guardian.json
deleted file mode 100644
index 7b8168e..0000000
--- a/erpnext/education/doctype/guardian/guardian.json
+++ /dev/null
@@ -1,580 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 1, 
- "allow_rename": 1, 
- "autoname": "EDU-GRD-.YYYY.-.#####", 
- "beta": 0, 
- "creation": "2016-07-21 15:32:51.163292", 
- "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": "guardian_name", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Guardian 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": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "email_address", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Email Address", 
-   "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": "mobile_number", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Mobile Number", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "alternate_number", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Alternate Number", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "date_of_birth", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Date of Birth", 
-   "length": 0, 
-   "no_copy": 1, 
-   "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": "user", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "User Id", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "User", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_3", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "education", 
-   "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": "Education", 
-   "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": "occupation", 
-   "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": "Occupation", 
-   "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": "designation", 
-   "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": "Designation", 
-   "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": "work_address", 
-   "fieldtype": "Text", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Work Address", 
-   "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", 
-   "fieldtype": "Attach Image", 
-   "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": "Image", 
-   "length": 0, 
-   "no_copy": 1, 
-   "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": "section_break_13", 
-   "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": "Guardian Of ", 
-   "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": "students", 
-   "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": "Students", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Guardian Student", 
-   "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": "section_break_8", 
-   "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": "Guardian Interests", 
-   "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": "interests", 
-   "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": "Interests", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Guardian Interest", 
-   "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_field": "image", 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2018-08-21 16:15:54.050317", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Guardian", 
- "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": "Academics User", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 1, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "title_field": "guardian_name", 
- "track_changes": 0, 
- "track_seen": 0, 
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/guardian/guardian.py b/erpnext/education/doctype/guardian/guardian.py
deleted file mode 100644
index aae651b..0000000
--- a/erpnext/education/doctype/guardian/guardian.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils.csvutils import getlink
-
-
-class Guardian(Document):
-	def __setup__(self):
-		self.onload()
-
-	def onload(self):
-		"""Load Students for quick view"""
-		self.load_students()
-
-	def load_students(self):
-		"""Load `students` from the database"""
-		self.students = []
-		students = frappe.get_all("Student Guardian", filters={"guardian":self.name}, fields=["parent"])
-		for student in students:
-			self.append("students", {
-				"student":student.parent,
-				"student_name": frappe.db.get_value("Student", student.parent, "title")
-			})
-
-	def validate(self):
-		self.students = []
-
-
-@frappe.whitelist()
-def invite_guardian(guardian):
-	guardian_doc = frappe.get_doc("Guardian", guardian)
-	if not guardian_doc.email_address:
-		frappe.throw(_("Please set Email Address"))
-	else:
-		guardian_as_user = frappe.get_value('User', dict(email=guardian_doc.email_address))
-		if guardian_as_user:
-			frappe.msgprint(_("User {0} already exists").format(getlink("User", guardian_as_user)))
-			return guardian_as_user
-		else:
-			user = frappe.get_doc({
-				"doctype": "User",
-				"first_name": guardian_doc.guardian_name,
-				"email": guardian_doc.email_address,
-				"user_type": "Website User",
-				"send_welcome_email": 1
-			}).insert(ignore_permissions = True)
-			frappe.msgprint(_("User {0} created").format(getlink("User", user.name)))
-			return user.name
diff --git a/erpnext/education/doctype/guardian/test_guardian.py b/erpnext/education/doctype/guardian/test_guardian.py
deleted file mode 100644
index f474ed5..0000000
--- a/erpnext/education/doctype/guardian/test_guardian.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-# test_records = frappe.get_test_records('Guardian')
-
-class TestGuardian(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/guardian_interest/__init__.py b/erpnext/education/doctype/guardian_interest/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/guardian_interest/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/guardian_interest/guardian_interest.json b/erpnext/education/doctype/guardian_interest/guardian_interest.json
deleted file mode 100644
index 1995c55..0000000
--- a/erpnext/education/doctype/guardian_interest/guardian_interest.json
+++ /dev/null
@@ -1,72 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2016-07-25 07:19:55.113871", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "interest", 
-   "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": "Interest", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Interest", 
-   "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": "2017-11-10 19:10:22.333454", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Guardian Interest", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/guardian_interest/guardian_interest.py b/erpnext/education/doctype/guardian_interest/guardian_interest.py
deleted file mode 100644
index 6cd1e55..0000000
--- a/erpnext/education/doctype/guardian_interest/guardian_interest.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class GuardianInterest(Document):
-	pass
diff --git a/erpnext/education/doctype/guardian_student/__init__.py b/erpnext/education/doctype/guardian_student/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/guardian_student/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/guardian_student/guardian_student.json b/erpnext/education/doctype/guardian_student/guardian_student.json
deleted file mode 100644
index 2519a54..0000000
--- a/erpnext/education/doctype/guardian_student/guardian_student.json
+++ /dev/null
@@ -1,132 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2017-01-31 11:53:21.580099", 
- "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": "student", 
-   "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": "Student", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_2", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "student_name", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Student Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2017-11-10 19:10:15.786362", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Guardian Student", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "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/education/doctype/guardian_student/guardian_student.py b/erpnext/education/doctype/guardian_student/guardian_student.py
deleted file mode 100644
index 4c29575..0000000
--- a/erpnext/education/doctype/guardian_student/guardian_student.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class GuardianStudent(Document):
-	pass
diff --git a/erpnext/education/doctype/instructor/__init__.py b/erpnext/education/doctype/instructor/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/instructor/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/instructor/instructor.js b/erpnext/education/doctype/instructor/instructor.js
deleted file mode 100644
index 034b0aa..0000000
--- a/erpnext/education/doctype/instructor/instructor.js
+++ /dev/null
@@ -1,64 +0,0 @@
-cur_frm.add_fetch("employee", "department", "department");
-cur_frm.add_fetch("employee", "image", "image");
-
-frappe.ui.form.on("Instructor", {
-	employee: function(frm) {
-		if (!frm.doc.employee) return;
-		frappe.db.get_value("Employee", {name: frm.doc.employee}, "company", (d) => {
-			frm.set_query("department", function() {
-				return {
-					"filters": {
-						"company": d.company,
-					}
-				};
-			});
-			frm.set_query("department", "instructor_log", function() {
-				return {
-					"filters": {
-						"company": d.company,
-					}
-				};
-			});
-		});
-	},
-	refresh: function(frm) {
-		if (!frm.doc.__islocal) {
-			frm.add_custom_button(__("As Examiner"), function() {
-				frappe.new_doc("Assessment Plan", {
-					examiner: frm.doc.name
-				});
-			}, __("Assessment Plan"));
-			frm.add_custom_button(__("As Supervisor"), function() {
-				frappe.new_doc("Assessment Plan", {
-					supervisor: frm.doc.name
-				});
-			}, __("Assessment Plan"));
-		}
-		frm.set_query("employee", function(doc) {
-			return {
-				"filters": {
-					"department": doc.department,
-				}
-			};
-		});
-
-		frm.set_query("academic_term", "instructor_log", function(_doc, cdt, cdn) {
-			let d = locals[cdt][cdn];
-			return {
-				filters: {
-					"academic_year": d.academic_year
-				}
-			};
-		});
-
-		frm.set_query("course", "instructor_log", function(_doc, cdt, cdn) {
-			let d = locals[cdt][cdn];
-			return {
-				query: "erpnext.education.doctype.program_enrollment.program_enrollment.get_program_courses",
-				filters: {
-					"program": d.program
-				}
-			};
-		});
-	}
-});
diff --git a/erpnext/education/doctype/instructor/instructor.json b/erpnext/education/doctype/instructor/instructor.json
deleted file mode 100644
index a417391..0000000
--- a/erpnext/education/doctype/instructor/instructor.json
+++ /dev/null
@@ -1,125 +0,0 @@
-{
- "actions": [],
- "allow_import": 1,
- "autoname": "naming_series:",
- "creation": "2015-11-04 15:56:30.004034",
- "doctype": "DocType",
- "document_type": "Other",
- "engine": "InnoDB",
- "field_order": [
-  "instructor_name",
-  "employee",
-  "gender",
-  "column_break_5",
-  "status",
-  "naming_series",
-  "department",
-  "image",
-  "log_details",
-  "instructor_log"
- ],
- "fields": [
-  {
-   "fieldname": "instructor_name",
-   "fieldtype": "Data",
-   "in_global_search": 1,
-   "label": "Instructor Name",
-   "reqd": 1
-  },
-  {
-   "fieldname": "employee",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Employee",
-   "options": "Employee"
-  },
-  {
-   "fieldname": "column_break_5",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "naming_series",
-   "fieldtype": "Select",
-   "label": "Naming Series",
-   "options": "EDU-INS-.YYYY.-",
-   "set_only_once": 1
-  },
-  {
-   "fetch_from": "employee.department",
-   "fieldname": "department",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "in_standard_filter": 1,
-   "label": "Department",
-   "options": "Department"
-  },
-  {
-   "fieldname": "image",
-   "fieldtype": "Attach Image",
-   "hidden": 1,
-   "label": "Image"
-  },
-  {
-   "fieldname": "log_details",
-   "fieldtype": "Section Break",
-   "label": "Instructor Log"
-  },
-  {
-   "fieldname": "instructor_log",
-   "fieldtype": "Table",
-   "label": "Instructor Log",
-   "options": "Instructor Log"
-  },
-  {
-   "default": "Active",
-   "fieldname": "status",
-   "fieldtype": "Select",
-   "in_list_view": 1,
-   "in_standard_filter": 1,
-   "label": "Status",
-   "options": "Active\nLeft"
-  },
-  {
-   "fetch_from": "employee.gender",
-   "fieldname": "gender",
-   "fieldtype": "Link",
-   "label": "Gender",
-   "options": "Gender",
-   "read_only_depends_on": "employee"
-  }
- ],
- "image_field": "image",
- "links": [],
- "modified": "2020-07-23 18:33:57.904398",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Instructor",
- "owner": "Administrator",
- "permissions": [
-  {
-   "email": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Instructor"
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Education Manager",
-   "set_user_permissions": 1,
-   "share": 1,
-   "write": 1
-  }
- ],
- "restrict_to_domain": "Education",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "instructor_name"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/instructor/instructor.py b/erpnext/education/doctype/instructor/instructor.py
deleted file mode 100644
index 0076240..0000000
--- a/erpnext/education/doctype/instructor/instructor.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.model.naming import set_name_by_naming_series
-
-
-class Instructor(Document):
-	def autoname(self):
-		naming_method = frappe.db.get_value("Education Settings", None, "instructor_created_by")
-		if not naming_method:
-			frappe.throw(_("Please setup Instructor Naming System in Education > Education Settings"))
-		else:
-			if naming_method == 'Naming Series':
-				set_name_by_naming_series(self)
-			elif naming_method == 'Employee Number':
-				if not self.employee:
-					frappe.throw(_("Please select Employee"))
-				self.name = self.employee
-			elif naming_method == 'Full Name':
-				self.name = self.instructor_name
-
-	def validate(self):
-		self.validate_duplicate_employee()
-
-	def validate_duplicate_employee(self):
-		if self.employee and frappe.db.get_value("Instructor", {'employee': self.employee, 'name': ['!=', self.name]}, 'name'):
-			frappe.throw(_("Employee ID is linked with another instructor"))
-
-def get_timeline_data(doctype, name):
-	"""Return timeline for course schedule"""
-	return dict(frappe.db.sql(
-		"""
-			SELECT unix_timestamp(`schedule_date`), count(*)
-			FROM `tabCourse Schedule`
-			WHERE
-				instructor=%s and
-				`schedule_date` > date_sub(curdate(), interval 1 year)
-			GROUP BY schedule_date
-		""", name))
diff --git a/erpnext/education/doctype/instructor/instructor_dashboard.py b/erpnext/education/doctype/instructor/instructor_dashboard.py
deleted file mode 100644
index eae67ac..0000000
--- a/erpnext/education/doctype/instructor/instructor_dashboard.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from frappe import _
-
-
-def get_data():
-	return {
-		'heatmap': True,
-		'heatmap_message': _('This is based on the course schedules of this Instructor'),
-		'fieldname': 'instructor',
-		'non_standard_fieldnames': {
-			'Assessment Plan': 'supervisor'
-		},
-		'transactions': [
-			{
-				'label': _('Course and Assessment'),
-				'items': ['Course Schedule', 'Assessment Plan']
-			},
-			{
-				'label': _('Students'),
-				'items': ['Student Group']
-			}
-		]
-	}
diff --git a/erpnext/education/doctype/instructor/test_instructor.py b/erpnext/education/doctype/instructor/test_instructor.py
deleted file mode 100644
index 4eab37a..0000000
--- a/erpnext/education/doctype/instructor/test_instructor.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and Contributors
-# See license.txt
-
-import unittest
-
-# test_records = frappe.get_test_records('Instructor')
-
-class TestInstructor(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/instructor/test_records.json b/erpnext/education/doctype/instructor/test_records.json
deleted file mode 100644
index 220d84e..0000000
--- a/erpnext/education/doctype/instructor/test_records.json
+++ /dev/null
@@ -1,12 +0,0 @@
-[
-	{
-		"naming_series": "_T-Instructor-",
-		"employee": "_T-Employee-00001",
-		"instructor_name": "_Test Instructor"
-	},
-	{
-		"naming_series": "_T-Instructor-",
-		"employee": "_T-Employee-00002",
-		"instructor_name": "_Test Instructor 2"
-	}
-]
diff --git a/erpnext/education/doctype/instructor_log/instructor_log.json b/erpnext/education/doctype/instructor_log/instructor_log.json
deleted file mode 100644
index 5b9e1f9..0000000
--- a/erpnext/education/doctype/instructor_log/instructor_log.json
+++ /dev/null
@@ -1,88 +0,0 @@
-{
- "actions": [],
- "creation": "2017-12-27 08:55:52.680284",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "academic_year",
-  "academic_term",
-  "department",
-  "column_break_3",
-  "program",
-  "course",
-  "student_group",
-  "section_break_8",
-  "other_details"
- ],
- "fields": [
-  {
-   "fieldname": "academic_year",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Academic Year",
-   "options": "Academic Year",
-   "reqd": 1
-  },
-  {
-   "fieldname": "academic_term",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Academic Term",
-   "options": "Academic Term"
-  },
-  {
-   "fieldname": "department",
-   "fieldtype": "Link",
-   "label": "Department",
-   "options": "Department"
-  },
-  {
-   "fieldname": "column_break_3",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "program",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Program",
-   "options": "Program",
-   "reqd": 1
-  },
-  {
-   "fieldname": "course",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Course",
-   "options": "Course"
-  },
-  {
-   "fieldname": "student_group",
-   "fieldtype": "Link",
-   "label": "Student Group",
-   "options": "Student Group"
-  },
-  {
-   "fieldname": "section_break_8",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fieldname": "other_details",
-   "fieldtype": "Small Text",
-   "label": "Other details"
-  }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-10-23 15:15:50.759657",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Instructor Log",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "restrict_to_domain": "Education",
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/instructor_log/instructor_log.py b/erpnext/education/doctype/instructor_log/instructor_log.py
deleted file mode 100644
index 12d11ba..0000000
--- a/erpnext/education/doctype/instructor_log/instructor_log.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class InstructorLog(Document):
-	pass
diff --git a/erpnext/education/doctype/options/__init__.py b/erpnext/education/doctype/options/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/options/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/options/options.json b/erpnext/education/doctype/options/options.json
deleted file mode 100644
index 59deab7..0000000
--- a/erpnext/education/doctype/options/options.json
+++ /dev/null
@@ -1,107 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2018-10-15 14:05:28.601274", 
- "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": "option", 
-   "fieldtype": "Small Text", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Option", 
-   "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": "is_correct", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Is Correct", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2018-10-15 14:16:18.303156", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Options", 
- "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/education/doctype/options/options.py b/erpnext/education/doctype/options/options.py
deleted file mode 100644
index 968a772..0000000
--- a/erpnext/education/doctype/options/options.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class Options(Document):
-	pass
diff --git a/erpnext/education/doctype/program/__init__.py b/erpnext/education/doctype/program/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/program/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/program/program.js b/erpnext/education/doctype/program/program.js
deleted file mode 100644
index 2d89351..0000000
--- a/erpnext/education/doctype/program/program.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2015, Frappe Technologies and contributors
-// For license information, please see license.txt
-
-cur_frm.add_fetch('fee_structure', 'total_amount', 'amount');
-
-frappe.ui.form.on("Program", "refresh", function(frm) {
-
-});
diff --git a/erpnext/education/doctype/program/program.json b/erpnext/education/doctype/program/program.json
deleted file mode 100644
index 2dfe50b..0000000
--- a/erpnext/education/doctype/program/program.json
+++ /dev/null
@@ -1,177 +0,0 @@
-{
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:program_name",
- "creation": "2015-09-07 12:54:03.609282",
- "doctype": "DocType",
- "engine": "InnoDB",
- "field_order": [
-  "program_name",
-  "department",
-  "column_break_3",
-  "program_abbreviation",
-  "section_break_courses",
-  "courses",
-  "section_break_5",
-  "is_published",
-  "allow_self_enroll",
-  "is_featured",
-  "column_break_11",
-  "intro_video",
-  "hero_image",
-  "description"
- ],
- "fields": [
-  {
-   "fieldname": "program_name",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Program Name",
-   "reqd": 1,
-   "unique": 1
-  },
-  {
-   "fieldname": "department",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "in_standard_filter": 1,
-   "label": "Department",
-   "options": "Department"
-  },
-  {
-   "fieldname": "column_break_3",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "program_abbreviation",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Program Abbreviation"
-  },
-  {
-   "fieldname": "section_break_5",
-   "fieldtype": "Section Break",
-   "label": "Portal Settings"
-  },
-  {
-   "fieldname": "courses",
-   "fieldtype": "Table",
-   "label": "Courses",
-   "options": "Program Course"
-  },
-  {
-   "depends_on": "is_published",
-   "fieldname": "description",
-   "fieldtype": "Small Text",
-   "label": "Description"
-  },
-  {
-   "depends_on": "is_published",
-   "fieldname": "intro_video",
-   "fieldtype": "Data",
-   "label": "Intro Video"
-  },
-  {
-   "fieldname": "hero_image",
-   "fieldtype": "Attach Image",
-   "hidden": 1,
-   "label": "Hero Image"
-  },
-  {
-   "default": "0",
-   "fieldname": "is_published",
-   "fieldtype": "Check",
-   "label": "Is Published"
-  },
-  {
-   "default": "0",
-   "depends_on": "eval: doc.is_published == 1",
-   "fieldname": "is_featured",
-   "fieldtype": "Check",
-   "label": "Is Featured"
-  },
-  {
-   "default": "0",
-   "depends_on": "eval: doc.is_published == 1",
-   "fieldname": "allow_self_enroll",
-   "fieldtype": "Check",
-   "label": "Allow Self Enroll"
-  },
-  {
-   "fieldname": "section_break_courses",
-   "fieldtype": "Section Break",
-   "label": "Courses"
-  },
-  {
-   "fieldname": "column_break_11",
-   "fieldtype": "Column Break"
-  }
- ],
- "image_field": "hero_image",
- "modified": "2019-06-12 12:31:14.999346",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Program",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "System Manager",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Instructor",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Guest",
-   "share": 1
-  },
-  {
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Student",
-   "share": 1
-  }
- ],
- "restrict_to_domain": "Education",
- "search_fields": "program_name",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/program/program.py b/erpnext/education/doctype/program/program.py
deleted file mode 100644
index a9ce644..0000000
--- a/erpnext/education/doctype/program/program.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe.model.document import Document
-
-
-class Program(Document):
-
-	def get_course_list(self):
-		program_course_list = self.courses
-		course_list = [frappe.get_doc("Course", program_course.course) for program_course in program_course_list]
-		return course_list
diff --git a/erpnext/education/doctype/program/program_dashboard.py b/erpnext/education/doctype/program/program_dashboard.py
deleted file mode 100644
index 6696076..0000000
--- a/erpnext/education/doctype/program/program_dashboard.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from frappe import _
-
-
-def get_data():
-	return {
-		'fieldname': 'program',
-		'transactions': [
-			{
-				'label': _('Admission and Enrollment'),
-				'items': ['Student Applicant', 'Program Enrollment']
-			},
-			{
-				'label': _('Student Activity'),
-				'items': ['Student Group', 'Student Log']
-			},
-			{
-				'label': _('Fee'),
-				'items': ['Fees','Fee Structure', 'Fee Schedule']
-			},
-			{
-				'label': _('Assessment'),
-				'items': ['Assessment Plan', 'Assessment Result']
-			}
-		]
-	}
diff --git a/erpnext/education/doctype/program/test_program.py b/erpnext/education/doctype/program/test_program.py
deleted file mode 100644
index cb8926b..0000000
--- a/erpnext/education/doctype/program/test_program.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and Contributors
-# See license.txt
-
-import unittest
-
-import frappe
-
-from erpnext.education.doctype.course.test_course import make_course, make_course_and_linked_topic
-from erpnext.education.doctype.topic.test_topic import make_topic_and_linked_content
-
-test_data = {
-	"program_name": "_Test Program",
-	"description": "_Test Program",
-	"course": [{
-		"course_name": "_Test Course 1",
-		"topic": [{
-				"topic_name": "_Test Topic 1-1",
-				"content": [{
-					"type": "Article",
-					"name": "_Test Article 1-1"
-				}, {
-					"type": "Article",
-					"name": "_Test Article 1-2"
-				}]
-			},
-			{
-				"topic_name": "_Test Topic 1-2",
-				"content": [{
-					"type": "Article",
-					"name": "_Test Article 1-3"
-				}, {
-					"type": "Article",
-					"name": "_Test Article 1-4"
-				}]
-			}
-		]
-	}]
-}
-
-class TestProgram(unittest.TestCase):
-	def setUp(self):
-		make_program_and_linked_courses("_Test Program 1", ["_Test Course 1", "_Test Course 2"])
-
-	def test_get_course_list(self):
-		program = frappe.get_doc("Program", "_Test Program 1")
-		course = program.get_course_list()
-		self.assertEqual(course[0].name, "_Test Course 1")
-		self.assertEqual(course[1].name, "_Test Course 2")
-		frappe.db.rollback()
-
-	def tearDown(self):
-		for dt in ["Program", "Course", "Topic", "Article"]:
-			for entry in frappe.get_all(dt):
-				frappe.delete_doc(dt, entry.program)
-
-def make_program(name):
-	program = frappe.get_doc({
-		"doctype": "Program",
-		"program_name": name,
-		"program_code": name,
-		"description": "_test description",
-		"is_published": True,
-		"is_featured": True,
-	}).insert()
-	return program.name
-
-def make_program_and_linked_courses(program_name, course_name_list):
-	try:
-		program = frappe.get_doc("Program", program_name)
-	except frappe.DoesNotExistError:
-		make_program(program_name)
-		program = frappe.get_doc("Program", program_name)
-	course_list = [make_course(course_name) for course_name in course_name_list]
-	for course in course_list:
-		program.append("courses", {"course": course, "required": 1})
-	program.save()
-	return program
-
-def setup_program():
-	topic_list = [course['topic'] for course in test_data['course']]
-	for topic in topic_list[0]:
-		make_topic_and_linked_content(topic['topic_name'], topic['content'])
-
-	all_courses_list = [{'course': course['course_name'], 'topic': [topic['topic_name'] for topic in course['topic']]} for course in test_data['course']] # returns [{'course': 'Applied Math', 'topic': ['Trignometry', 'Geometry']}]
-	for course in all_courses_list:
-		make_course_and_linked_topic(course['course'], course['topic'])
-
-	course_list = [course['course_name'] for course in test_data['course']]
-	program = make_program_and_linked_courses(test_data['program_name'], course_list)
-	return program
diff --git a/erpnext/education/doctype/program/test_records.json b/erpnext/education/doctype/program/test_records.json
deleted file mode 100644
index 4013695..0000000
--- a/erpnext/education/doctype/program/test_records.json
+++ /dev/null
@@ -1,12 +0,0 @@
-[
-	{
-		"program_name": "_TP1",
-		"description": "Test Description",
-		"program_abbreviation": "TP1"
-	},
-	{
-		"program_name": "_TP2",
-		"description": "Test Description",
-		"program_abbreviation": "TP2"
-	}
-]
\ No newline at end of file
diff --git a/erpnext/education/doctype/program_course/__init__.py b/erpnext/education/doctype/program_course/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/program_course/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/program_course/program_course.json b/erpnext/education/doctype/program_course/program_course.json
deleted file mode 100644
index dc6b3fc..0000000
--- a/erpnext/education/doctype/program_course/program_course.json
+++ /dev/null
@@ -1,50 +0,0 @@
-{
- "actions": [],
- "creation": "2015-09-07 14:37:01.886859",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "course",
-  "course_name",
-  "required"
- ],
- "fields": [
-  {
-   "fieldname": "course",
-   "fieldtype": "Link",
-   "in_global_search": 1,
-   "in_list_view": 1,
-   "label": "Course",
-   "options": "Course",
-   "reqd": 1
-  },
-  {
-   "fetch_from": "course.course_name",
-   "fieldname": "course_name",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Course Name",
-   "read_only": 1
-  },
-  {
-   "default": "1",
-   "fieldname": "required",
-   "fieldtype": "Check",
-   "in_list_view": 1,
-   "label": "Mandatory"
-  }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-09-15 18:14:22.816795",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Program Course",
- "owner": "Administrator",
- "permissions": [],
- "restrict_to_domain": "Education",
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/program_course/program_course.py b/erpnext/education/doctype/program_course/program_course.py
deleted file mode 100644
index dec392c..0000000
--- a/erpnext/education/doctype/program_course/program_course.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class ProgramCourse(Document):
-	pass
diff --git a/erpnext/education/doctype/program_enrollment/__init__.py b/erpnext/education/doctype/program_enrollment/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/program_enrollment/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/program_enrollment/program_enrollment.js b/erpnext/education/doctype/program_enrollment/program_enrollment.js
deleted file mode 100644
index e92d063..0000000
--- a/erpnext/education/doctype/program_enrollment/program_enrollment.js
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (c) 2016, Frappe and contributors
-// For license information, please see license.txt
-
-
-frappe.ui.form.on('Program Enrollment', {
-	setup: function(frm) {
-		frm.add_fetch('fee_structure', 'total_amount', 'amount');
-	},
-
-	onload: function(frm) {
-		frm.set_query('academic_term', function() {
-			return {
-				'filters':{
-					'academic_year': frm.doc.academic_year
-				}
-			};
-		});
-
-		frm.set_query('academic_term', 'fees', function() {
-			return {
-				'filters':{
-					'academic_year': frm.doc.academic_year
-				}
-			};
-		});
-
-		frm.fields_dict['fees'].grid.get_field('fee_structure').get_query = function(doc, cdt, cdn) {
-			var d = locals[cdt][cdn];
-			return {
-				filters: {'academic_term': d.academic_term}
-			}
-		};
-
-		if (frm.doc.program) {
-			frm.set_query('course', 'courses', function() {
-				return {
-					query: 'erpnext.education.doctype.program_enrollment.program_enrollment.get_program_courses',
-					filters: {
-						'program': frm.doc.program
-					}
-				}
-			});
-		}
-
-		frm.set_query('student', function() {
-			return{
-				query: 'erpnext.education.doctype.program_enrollment.program_enrollment.get_students',
-				filters: {
-					'academic_year': frm.doc.academic_year,
-					'academic_term': frm.doc.academic_term
-				}
-			}
-		});
-	},
-
-	program: function(frm) {
-		frm.events.get_courses(frm);
-		if (frm.doc.program) {
-			frappe.call({
-				method: 'erpnext.education.api.get_fee_schedule',
-				args: {
-					'program': frm.doc.program,
-					'student_category': frm.doc.student_category
-				},
-				callback: function(r) {
-					if (r.message) {
-						frm.set_value('fees' ,r.message);
-						frm.events.get_courses(frm);
-					}
-				}
-			});
-		}
-	},
-
-	student_category: function() {
-		frappe.ui.form.trigger('Program Enrollment', 'program');
-	},
-
-	get_courses: function(frm) {
-		frm.set_value('courses',[]);
-		frappe.call({
-			method: 'get_courses',
-			doc:frm.doc,
-			callback: function(r) {
-				if (r.message) {
-					frm.set_value('courses', r.message);
-				}
-			}
-		})
-	}
-});
-
-frappe.ui.form.on('Program Enrollment Course', {
-	courses_add: function(frm){
-		frm.fields_dict['courses'].grid.get_field('course').get_query = function(doc) {
-			var course_list = [];
-			if(!doc.__islocal) course_list.push(doc.name);
-			$.each(doc.courses, function(_idx, val) {
-				if (val.course) course_list.push(val.course);
-			});
-			return { filters: [['Course', 'name', 'not in', course_list]] };
-		};
-	}
-});
diff --git a/erpnext/education/doctype/program_enrollment/program_enrollment.json b/erpnext/education/doctype/program_enrollment/program_enrollment.json
deleted file mode 100644
index 4a00fd0..0000000
--- a/erpnext/education/doctype/program_enrollment/program_enrollment.json
+++ /dev/null
@@ -1,218 +0,0 @@
-{
- "actions": [],
- "allow_import": 1,
- "autoname": "EDU-ENR-.YYYY.-.#####",
- "creation": "2015-12-02 12:58:32.916080",
- "doctype": "DocType",
- "document_type": "Document",
- "engine": "InnoDB",
- "field_order": [
-  "student",
-  "student_name",
-  "student_category",
-  "student_batch_name",
-  "school_house",
-  "column_break_4",
-  "program",
-  "academic_year",
-  "academic_term",
-  "enrollment_date",
-  "boarding_student",
-  "enrolled_courses",
-  "courses",
-  "transportation",
-  "mode_of_transportation",
-  "column_break_13",
-  "vehicle_no",
-  "section_break_7",
-  "fees",
-  "amended_from",
-  "image"
- ],
- "fields": [
-  {
-   "fieldname": "student",
-   "fieldtype": "Link",
-   "in_global_search": 1,
-   "label": "Student",
-   "options": "Student",
-   "reqd": 1
-  },
-  {
-   "fetch_from": "student.title",
-   "fieldname": "student_name",
-   "fieldtype": "Read Only",
-   "in_global_search": 1,
-   "label": "Student Name",
-   "read_only": 1
-  },
-  {
-   "fieldname": "student_category",
-   "fieldtype": "Link",
-   "label": "Student Category",
-   "options": "Student Category"
-  },
-  {
-   "allow_on_submit": 1,
-   "fieldname": "student_batch_name",
-   "fieldtype": "Link",
-   "in_global_search": 1,
-   "label": "Student Batch",
-   "options": "Student Batch Name"
-  },
-  {
-   "allow_on_submit": 1,
-   "fieldname": "school_house",
-   "fieldtype": "Link",
-   "label": "School House",
-   "options": "School House"
-  },
-  {
-   "fieldname": "column_break_4",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "program",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "in_standard_filter": 1,
-   "label": "Program",
-   "options": "Program",
-   "reqd": 1
-  },
-  {
-   "fieldname": "academic_year",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "in_standard_filter": 1,
-   "label": "Academic Year",
-   "options": "Academic Year",
-   "reqd": 1
-  },
-  {
-   "fieldname": "academic_term",
-   "fieldtype": "Link",
-   "label": "Academic Term",
-   "options": "Academic Term"
-  },
-  {
-   "default": "Today",
-   "fieldname": "enrollment_date",
-   "fieldtype": "Date",
-   "label": "Enrollment Date",
-   "reqd": 1
-  },
-  {
-   "default": "0",
-   "description": "Check this if the Student is residing at the Institute's Hostel.",
-   "fieldname": "boarding_student",
-   "fieldtype": "Check",
-   "label": "Boarding Student"
-  },
-  {
-   "collapsible": 1,
-   "collapsible_depends_on": "vehicle_no",
-   "fieldname": "transportation",
-   "fieldtype": "Section Break",
-   "label": "Transportation"
-  },
-  {
-   "allow_on_submit": 1,
-   "fieldname": "mode_of_transportation",
-   "fieldtype": "Select",
-   "label": "Mode of Transportation",
-   "options": "\nWalking\nInstitute's Bus\nPublic Transport\nSelf-Driving Vehicle\nPick/Drop by Guardian"
-  },
-  {
-   "fieldname": "column_break_13",
-   "fieldtype": "Column Break"
-  },
-  {
-   "allow_on_submit": 1,
-   "fieldname": "vehicle_no",
-   "fieldtype": "Data",
-   "label": "Vehicle/Bus Number"
-  },
-  {
-   "fieldname": "enrolled_courses",
-   "fieldtype": "Section Break",
-   "label": "Enrolled courses"
-  },
-  {
-   "allow_on_submit": 1,
-   "fieldname": "courses",
-   "fieldtype": "Table",
-   "label": "Courses",
-   "options": "Program Enrollment Course"
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "section_break_7",
-   "fieldtype": "Section Break",
-   "label": "Fees"
-  },
-  {
-   "fieldname": "fees",
-   "fieldtype": "Table",
-   "label": "Fees",
-   "options": "Program Fee"
-  },
-  {
-   "fieldname": "amended_from",
-   "fieldtype": "Link",
-   "label": "Amended From",
-   "no_copy": 1,
-   "options": "Program Enrollment",
-   "print_hide": 1,
-   "read_only": 1
-  },
-  {
-   "fieldname": "image",
-   "fieldtype": "Attach Image",
-   "hidden": 1,
-   "label": "Image"
-  }
- ],
- "image_field": "image",
- "is_submittable": 1,
- "links": [],
- "modified": "2020-09-15 18:12:11.988565",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Program Enrollment",
- "owner": "Administrator",
- "permissions": [
-  {
-   "amend": 1,
-   "cancel": 1,
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "submit": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "LMS User",
-   "share": 1,
-   "submit": 1,
-   "write": 1
-  }
- ],
- "restrict_to_domain": "Education",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "student_name"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/program_enrollment/program_enrollment.py b/erpnext/education/doctype/program_enrollment/program_enrollment.py
deleted file mode 100644
index 4d0f3a9..0000000
--- a/erpnext/education/doctype/program_enrollment/program_enrollment.py
+++ /dev/null
@@ -1,183 +0,0 @@
-# Copyright (c) 2015, Frappe and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _, msgprint
-from frappe.desk.reportview import get_match_cond
-from frappe.model.document import Document
-from frappe.query_builder.functions import Min
-from frappe.utils import comma_and, get_link_to_form, getdate
-
-
-class ProgramEnrollment(Document):
-	def validate(self):
-		self.validate_duplication()
-		self.validate_academic_year()
-		if self.academic_term:
-			self.validate_academic_term()
-		if not self.student_name:
-			self.student_name = frappe.db.get_value("Student", self.student, "title")
-		if not self.courses:
-			self.extend("courses", self.get_courses())
-
-	def on_submit(self):
-		self.update_student_joining_date()
-		self.make_fee_records()
-		self.create_course_enrollments()
-
-	def validate_academic_year(self):
-		start_date, end_date = frappe.db.get_value("Academic Year", self.academic_year, ["year_start_date", "year_end_date"])
-		if self.enrollment_date:
-			if start_date and getdate(self.enrollment_date) < getdate(start_date):
-				frappe.throw(_("Enrollment Date cannot be before the Start Date of the Academic Year {0}").format(
-					get_link_to_form("Academic Year", self.academic_year)))
-
-			if end_date and getdate(self.enrollment_date) > getdate(end_date):
-				frappe.throw(_("Enrollment Date cannot be after the End Date of the Academic Term {0}").format(
-					get_link_to_form("Academic Year", self.academic_year)))
-
-	def validate_academic_term(self):
-		start_date, end_date = frappe.db.get_value("Academic Term", self.academic_term, ["term_start_date", "term_end_date"])
-		if self.enrollment_date:
-			if start_date and getdate(self.enrollment_date) < getdate(start_date):
-				frappe.throw(_("Enrollment Date cannot be before the Start Date of the Academic Term {0}").format(
-					get_link_to_form("Academic Term", self.academic_term)))
-
-			if end_date and getdate(self.enrollment_date) > getdate(end_date):
-				frappe.throw(_("Enrollment Date cannot be after the End Date of the Academic Term {0}").format(
-					get_link_to_form("Academic Term", self.academic_term)))
-
-	def validate_duplication(self):
-		enrollment = frappe.get_all("Program Enrollment", filters={
-			"student": self.student,
-			"program": self.program,
-			"academic_year": self.academic_year,
-			"academic_term": self.academic_term,
-			"docstatus": ("<", 2),
-			"name": ("!=", self.name)
-		})
-		if enrollment:
-			frappe.throw(_("Student is already enrolled."))
-
-	def update_student_joining_date(self):
-		table = frappe.qb.DocType('Program Enrollment')
-		date = (
-			frappe.qb.from_(table)
-				.select(Min(table.enrollment_date).as_('enrollment_date'))
-				.where(table.student == self.student)
-		).run(as_dict=True)
-
-		if date:
-			frappe.db.set_value("Student", self.student, "joining_date", date[0].enrollment_date)
-
-	def make_fee_records(self):
-		from erpnext.education.api import get_fee_components
-		fee_list = []
-		for d in self.fees:
-			fee_components = get_fee_components(d.fee_structure)
-			if fee_components:
-				fees = frappe.new_doc("Fees")
-				fees.update({
-					"student": self.student,
-					"academic_year": self.academic_year,
-					"academic_term": d.academic_term,
-					"fee_structure": d.fee_structure,
-					"program": self.program,
-					"due_date": d.due_date,
-					"student_name": self.student_name,
-					"program_enrollment": self.name,
-					"components": fee_components
-				})
-
-				fees.save()
-				fees.submit()
-				fee_list.append(fees.name)
-		if fee_list:
-			fee_list = ["""<a href="/app/Form/Fees/%s" target="_blank">%s</a>""" % \
-				(fee, fee) for fee in fee_list]
-			msgprint(_("Fee Records Created - {0}").format(comma_and(fee_list)))
-
-
-	@frappe.whitelist()
-	def get_courses(self):
-		return frappe.db.sql('''select course from `tabProgram Course` where parent = %s and required = 1''', (self.program), as_dict=1)
-
-	def create_course_enrollments(self):
-		student = frappe.get_doc("Student", self.student)
-		course_list = [course.course for course in self.courses]
-		for course_name in course_list:
-			student.enroll_in_course(course_name=course_name, program_enrollment=self.name, enrollment_date=self.enrollment_date)
-
-	def get_all_course_enrollments(self):
-		course_enrollment_names = frappe.get_list("Course Enrollment", filters={'program_enrollment': self.name})
-		return [frappe.get_doc('Course Enrollment', course_enrollment.name) for course_enrollment in course_enrollment_names]
-
-	def get_quiz_progress(self):
-		student = frappe.get_doc("Student", self.student)
-		quiz_progress = frappe._dict()
-		progress_list = []
-		for course_enrollment in self.get_all_course_enrollments():
-			course_progress = course_enrollment.get_progress(student)
-			for progress_item in course_progress:
-				if progress_item['content_type'] == "Quiz":
-					progress_item['course'] = course_enrollment.course
-					progress_list.append(progress_item)
-		if not progress_list:
-			return None
-		quiz_progress.quiz_attempt = progress_list
-		quiz_progress.name = self.program
-		quiz_progress.program = self.program
-		return quiz_progress
-
-@frappe.whitelist()
-@frappe.validate_and_sanitize_search_inputs
-def get_program_courses(doctype, txt, searchfield, start, page_len, filters):
-	if not filters.get('program'):
-		frappe.msgprint(_("Please select a Program first."))
-		return []
-
-	return frappe.db.sql("""select course, course_name from `tabProgram Course`
-		where  parent = %(program)s and course like %(txt)s {match_cond}
-		order by
-			if(locate(%(_txt)s, course), locate(%(_txt)s, course), 99999),
-			idx desc,
-			`tabProgram Course`.course asc
-		limit {start}, {page_len}""".format(
-			match_cond=get_match_cond(doctype),
-			start=start,
-			page_len=page_len), {
-				"txt": "%{0}%".format(txt),
-				"_txt": txt.replace('%', ''),
-				"program": filters['program']
-			})
-
-@frappe.whitelist()
-@frappe.validate_and_sanitize_search_inputs
-def get_students(doctype, txt, searchfield, start, page_len, filters):
-	if not filters.get("academic_term"):
-		filters["academic_term"] = frappe.defaults.get_defaults().academic_term
-
-	if not filters.get("academic_year"):
-		filters["academic_year"] = frappe.defaults.get_defaults().academic_year
-
-	enrolled_students = frappe.get_list("Program Enrollment", filters={
-		"academic_term": filters.get('academic_term'),
-		"academic_year": filters.get('academic_year')
-	}, fields=["student"])
-
-	students = [d.student for d in enrolled_students] if enrolled_students else [""]
-
-	return frappe.db.sql("""select
-			name, title from tabStudent
-		where
-			name not in (%s)
-		and
-			`%s` LIKE %s
-		order by
-			idx desc, name
-		limit %s, %s"""%(
-			", ".join(['%s']*len(students)), searchfield, "%s", "%s", "%s"),
-			tuple(students + ["%%%s%%" % txt, start, page_len]
-		)
-	)
diff --git a/erpnext/education/doctype/program_enrollment/program_enrollment_dashboard.py b/erpnext/education/doctype/program_enrollment/program_enrollment_dashboard.py
deleted file mode 100644
index 14ed95d..0000000
--- a/erpnext/education/doctype/program_enrollment/program_enrollment_dashboard.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from frappe import _
-
-
-def get_data():
-	return {
-		'fieldname': 'program_enrollment',
-		'transactions': [
-			{
-				'label': _('Course and Fee'),
-				'items': ['Course Enrollment', 'Fees']
-			}
-		],
-		'reports': [
-			{
-				'label': _('Report'),
-				'items': ['Student and Guardian Contact Details']
-			}
-		]
-	}
diff --git a/erpnext/education/doctype/program_enrollment/test_program_enrollment.py b/erpnext/education/doctype/program_enrollment/test_program_enrollment.py
deleted file mode 100644
index dda2465..0000000
--- a/erpnext/education/doctype/program_enrollment/test_program_enrollment.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (c) 2015, Frappe and Contributors
-# See license.txt
-
-import unittest
-
-import frappe
-
-from erpnext.education.doctype.program.test_program import make_program_and_linked_courses
-from erpnext.education.doctype.student.test_student import create_student, get_student
-
-
-class TestProgramEnrollment(unittest.TestCase):
-
-	def setUp(self):
-		create_student({"first_name": "_Test Name", "last_name": "_Test Last Name", "email": "_test_student@example.com"})
-		make_program_and_linked_courses("_Test Program 1", ["_Test Course 1", "_Test Course 2"])
-
-	def test_create_course_enrollments(self):
-		student = get_student("_test_student@example.com")
-		enrollment = student.enroll_in_program("_Test Program 1")
-		course_enrollments = student.get_all_course_enrollments()
-		self.assertTrue("_Test Course 1" in course_enrollments.keys())
-		self.assertTrue("_Test Course 2" in course_enrollments.keys())
-		frappe.db.rollback()
-
-	def tearDown(self):
-		for entry in frappe.db.get_all("Course Enrollment"):
-			frappe.delete_doc("Course Enrollment", entry.name)
-
-		for entry in frappe.db.get_all("Program Enrollment"):
-			doc = frappe.get_doc("Program Enrollment", entry.name)
-			doc.cancel()
-			doc.delete()
diff --git a/erpnext/education/doctype/program_enrollment_course/__init__.py b/erpnext/education/doctype/program_enrollment_course/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/program_enrollment_course/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/program_enrollment_course/program_enrollment_course.json b/erpnext/education/doctype/program_enrollment_course/program_enrollment_course.json
deleted file mode 100644
index 502a401..0000000
--- a/erpnext/education/doctype/program_enrollment_course/program_enrollment_course.json
+++ /dev/null
@@ -1,107 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2017-04-10 19:28:19.616308", 
- "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": "course", 
-   "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": "Course", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Course", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0,
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_from": "course.course_name",
-   "fieldname": "course_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": "Course Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "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": "2018-05-16 22:42:39.136276",
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Program Enrollment Course", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "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/education/doctype/program_enrollment_course/program_enrollment_course.py b/erpnext/education/doctype/program_enrollment_course/program_enrollment_course.py
deleted file mode 100644
index 8b2d82c..0000000
--- a/erpnext/education/doctype/program_enrollment_course/program_enrollment_course.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class ProgramEnrollmentCourse(Document):
-	pass
diff --git a/erpnext/education/doctype/program_enrollment_fee/__init__.py b/erpnext/education/doctype/program_enrollment_fee/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/program_enrollment_fee/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/program_enrollment_fee/program_enrollment_fee.json b/erpnext/education/doctype/program_enrollment_fee/program_enrollment_fee.json
deleted file mode 100644
index 0af2815..0000000
--- a/erpnext/education/doctype/program_enrollment_fee/program_enrollment_fee.json
+++ /dev/null
@@ -1,192 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2016-05-19 05:52:56.322125", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "academic_term", 
-   "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": "Academic Term", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Academic Term", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "fee_structure", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Fee Structure", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Fee Structure", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_3", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "due_date", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Due Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "amount", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Amount", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2017-11-10 19:11:07.516632", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Program Enrollment Fee", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/program_enrollment_fee/program_enrollment_fee.py b/erpnext/education/doctype/program_enrollment_fee/program_enrollment_fee.py
deleted file mode 100644
index 17d410f..0000000
--- a/erpnext/education/doctype/program_enrollment_fee/program_enrollment_fee.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class ProgramEnrollmentFee(Document):
-	pass
diff --git a/erpnext/education/doctype/program_enrollment_tool/__init__.py b/erpnext/education/doctype/program_enrollment_tool/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/program_enrollment_tool/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.js b/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.js
deleted file mode 100644
index 06d7598..0000000
--- a/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.js
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) 2016, Frappe and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on("Program Enrollment Tool", {
-	setup: function(frm) {
-		frm.add_fetch("student", "title", "student_name");
-		frm.add_fetch("student_applicant", "title", "student_name");
-		if(frm.doc.__onload && frm.doc.__onload.academic_term_reqd) {
-			frm.toggle_reqd("academic_term", true);
-		}
-	},
-
-	"refresh": function(frm) {
-		frm.disable_save();
-		frm.fields_dict.enroll_students.$input.addClass(' btn btn-primary');
-		frappe.realtime.on("program_enrollment_tool", function(data) {
-			frappe.hide_msgprint(true);
-			frappe.show_progress(__("Enrolling students"), data.progress[0], data.progress[1]);
-		});
-	},
-
-	get_students_from: function(frm) {
-		if (frm.doc.get_students_from == "Student Applicant") {
-			frm.dashboard.add_comment(__('Only the Student Applicant with the status "Approved" will be selected in the table below.'));
-		}
-	},
-
-	"get_students": function(frm) {
-		frm.set_value("students",[]);
-		frappe.call({
-			method: "get_students",
-			doc:frm.doc,
-			callback: function(r) {
-				if(r.message) {
-					frm.set_value("students", r.message);
-				}
-			}
-		});
-	},
-
-	"enroll_students": function(frm) {
-		frappe.call({
-			method: "enroll_students",
-			doc:frm.doc,
-			callback: function(r) {
-				frm.set_value("students", []);
-				frappe.hide_msgprint(true);
-			}
-		});
-	}
-});
diff --git a/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.json b/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.json
deleted file mode 100644
index 35ad98d..0000000
--- a/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.json
+++ /dev/null
@@ -1,553 +0,0 @@
-{
- "allow_copy": 1, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2016-06-10 03:01:05.178956", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 0, 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "get_students_from", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Get Students From", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "\nStudent Applicant\nProgram Enrollment", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "program", 
-   "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": "Program", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Program", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:doc.get_students_from==\"Program Enrollment\"", 
-   "fieldname": "student_batch", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Student Batch", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student Batch Name", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_3", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "academic_year", 
-   "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": "Academic Year", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Academic Year", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "academic_term", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Academic Term", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Academic Term", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_5", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "get_students", 
-   "fieldtype": "Button", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Get Students", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "students", 
-   "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": "Students", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Program Enrollment Tool Student", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_7", 
-   "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": "Enrollment Details", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:doc.get_students_from==\"Program Enrollment\"", 
-   "fieldname": "new_program", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "New Program", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Program", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "new_student_batch", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "New Student Batch", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student Batch Name", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "enroll_students", 
-   "fieldtype": "Button", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Enroll Students", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_12", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:doc.get_students_from==\"Program Enrollment\"", 
-   "fieldname": "new_academic_year", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "New Academic Year", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Academic Year", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:doc.get_students_from==\"Program Enrollment\"", 
-   "fieldname": "new_academic_term", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "New Academic Term", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Academic Term", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 1, 
- "hide_toolbar": 1, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 1, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2018-07-26 04:44:13.232146", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Program Enrollment Tool", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [
-  {
-   "amend": 0, 
-   "apply_user_permissions": 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": "Education Manager", 
-   "set_user_permissions": 0, 
-   "share": 0, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py b/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py
deleted file mode 100644
index 7ffa077..0000000
--- a/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# Copyright (c) 2015, Frappe and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import cint
-
-from erpnext.education.api import enroll_student
-
-
-class ProgramEnrollmentTool(Document):
-	def onload(self):
-		academic_term_reqd = cint(frappe.db.get_single_value('Education Settings', 'academic_term_reqd'))
-		self.set_onload("academic_term_reqd", academic_term_reqd)
-
-	@frappe.whitelist()
-	def get_students(self):
-		students = []
-		if not self.get_students_from:
-			frappe.throw(_("Mandatory field - Get Students From"))
-		elif not self.program:
-			frappe.throw(_("Mandatory field - Program"))
-		elif not self.academic_year:
-			frappe.throw(_("Mandatory field - Academic Year"))
-		else:
-			condition = 'and academic_term=%(academic_term)s' if self.academic_term else " "
-			if self.get_students_from == "Student Applicant":
-				students = frappe.db.sql('''select name as student_applicant, title as student_name from `tabStudent Applicant`
-					where application_status="Approved" and program=%(program)s and academic_year=%(academic_year)s {0}'''
-					.format(condition), self.as_dict(), as_dict=1)
-			elif self.get_students_from == "Program Enrollment":
-				condition2 = 'and student_batch_name=%(student_batch)s' if self.student_batch else " "
-				students = frappe.db.sql('''select student, student_name, student_batch_name, student_category from `tabProgram Enrollment`
-					where program=%(program)s and academic_year=%(academic_year)s {0} {1} and docstatus != 2'''
-					.format(condition, condition2), self.as_dict(), as_dict=1)
-
-				student_list = [d.student for d in students]
-				if student_list:
-					inactive_students = frappe.db.sql('''
-						select name as student, title as student_name from `tabStudent` where name in (%s) and enabled = 0''' %
-						', '.join(['%s']*len(student_list)), tuple(student_list), as_dict=1)
-
-					for student in students:
-						if student.student in [d.student for d in inactive_students]:
-							students.remove(student)
-
-		if students:
-			return students
-		else:
-			frappe.throw(_("No students Found"))
-
-	@frappe.whitelist()
-	def enroll_students(self):
-		total = len(self.students)
-		for i, stud in enumerate(self.students):
-			frappe.publish_realtime("program_enrollment_tool", dict(progress=[i+1, total]), user=frappe.session.user)
-			if stud.student:
-				prog_enrollment = frappe.new_doc("Program Enrollment")
-				prog_enrollment.student = stud.student
-				prog_enrollment.student_name = stud.student_name
-				prog_enrollment.student_category = stud.student_category
-				prog_enrollment.program = self.new_program
-				prog_enrollment.academic_year = self.new_academic_year
-				prog_enrollment.academic_term = self.new_academic_term
-				prog_enrollment.student_batch_name = stud.student_batch_name if stud.student_batch_name else self.new_student_batch
-				prog_enrollment.save()
-			elif stud.student_applicant:
-				prog_enrollment = enroll_student(stud.student_applicant)
-				prog_enrollment.academic_year = self.academic_year
-				prog_enrollment.academic_term = self.academic_term
-				prog_enrollment.student_batch_name = stud.student_batch_name if stud.student_batch_name else self.new_student_batch
-				prog_enrollment.save()
-		frappe.msgprint(_("{0} Students have been enrolled").format(total))
diff --git a/erpnext/education/doctype/program_enrollment_tool/test_program_enrollment_tool.py b/erpnext/education/doctype/program_enrollment_tool/test_program_enrollment_tool.py
deleted file mode 100644
index e806792..0000000
--- a/erpnext/education/doctype/program_enrollment_tool/test_program_enrollment_tool.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestProgramEnrollmentTool(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/program_enrollment_tool_student/__init__.py b/erpnext/education/doctype/program_enrollment_tool_student/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/program_enrollment_tool_student/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/program_enrollment_tool_student/program_enrollment_tool_student.json b/erpnext/education/doctype/program_enrollment_tool_student/program_enrollment_tool_student.json
deleted file mode 100644
index 1d74973..0000000
--- a/erpnext/education/doctype/program_enrollment_tool_student/program_enrollment_tool_student.json
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "actions": [],
- "creation": "2016-06-10 03:29:02.539914",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "student_applicant",
-  "student",
-  "student_name",
-  "column_break_3",
-  "student_batch_name",
-  "student_category"
- ],
- "fields": [
-  {
-   "fieldname": "student_applicant",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Student Applicant",
-   "options": "Student Applicant"
-  },
-  {
-   "fieldname": "student",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Student",
-   "options": "Student"
-  },
-  {
-   "fieldname": "column_break_3",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "student_name",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Student Name",
-   "read_only": 1
-  },
-  {
-   "fieldname": "student_batch_name",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Student Batch Name",
-   "options": "Student Batch Name"
-  },
-  {
-   "fieldname": "student_category",
-   "fieldtype": "Link",
-   "label": "Student Category",
-   "options": "Student Category",
-   "read_only": 1
-  }
- ],
- "istable": 1,
- "links": [],
- "modified": "2021-07-29 18:19:54.471594",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Program Enrollment Tool Student",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "restrict_to_domain": "Education",
- "sort_field": "modified",
- "sort_order": "DESC"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/program_enrollment_tool_student/program_enrollment_tool_student.py b/erpnext/education/doctype/program_enrollment_tool_student/program_enrollment_tool_student.py
deleted file mode 100644
index b37e5d3..0000000
--- a/erpnext/education/doctype/program_enrollment_tool_student/program_enrollment_tool_student.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class ProgramEnrollmentToolStudent(Document):
-	pass
diff --git a/erpnext/education/doctype/program_fee/__init__.py b/erpnext/education/doctype/program_fee/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/program_fee/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/program_fee/program_fee.json b/erpnext/education/doctype/program_fee/program_fee.json
deleted file mode 100644
index d45e4bd..0000000
--- a/erpnext/education/doctype/program_fee/program_fee.json
+++ /dev/null
@@ -1,224 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2016-05-19 05:52:56.322125", 
- "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": "academic_term", 
-   "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": "Academic Term", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Academic Term", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "fee_structure", 
-   "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": "Fee Structure", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Fee Structure", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "student_category", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Student Category", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student Category", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_3", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "due_date", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Due Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "amount", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Amount", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2017-11-10 19:07:10.426335", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Program Fee", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "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/education/doctype/program_fee/program_fee.py b/erpnext/education/doctype/program_fee/program_fee.py
deleted file mode 100644
index e9a0be1..0000000
--- a/erpnext/education/doctype/program_fee/program_fee.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class ProgramFee(Document):
-	pass
diff --git a/erpnext/education/doctype/question/__init__.py b/erpnext/education/doctype/question/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/question/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/question/question.js b/erpnext/education/doctype/question/question.js
deleted file mode 100644
index 01b3091..0000000
--- a/erpnext/education/doctype/question/question.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('Question', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/question/question.json b/erpnext/education/doctype/question/question.json
deleted file mode 100644
index e396760..0000000
--- a/erpnext/education/doctype/question/question.json
+++ /dev/null
@@ -1,80 +0,0 @@
-{
- "allow_import": 1,
- "autoname": "format:QUESTION-{#####}",
- "creation": "2018-10-01 15:58:00.696815",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "question",
-  "options",
-  "question_type"
- ],
- "fields": [
-  {
-   "fieldname": "question",
-   "fieldtype": "Text Editor",
-   "in_list_view": 1,
-   "label": "Question",
-   "reqd": 1
-  },
-  {
-   "fieldname": "options",
-   "fieldtype": "Table",
-   "label": "Options",
-   "options": "Options",
-   "reqd": 1
-  },
-  {
-   "fieldname": "question_type",
-   "fieldtype": "Select",
-   "in_standard_filter": 1,
-   "label": "Type",
-   "options": "\nSingle Correct Answer\nMultiple Correct Answer",
-   "read_only": 1
-  }
- ],
- "modified": "2020-09-24 18:39:21.880974",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Question",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Instructor",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "LMS User",
-   "share": 1
-  }
- ],
- "quick_entry": 1,
- "sort_field": "modified",
- "sort_order": "DESC"
-}
diff --git a/erpnext/education/doctype/question/question.py b/erpnext/education/doctype/question/question.py
deleted file mode 100644
index aa6cf9f..0000000
--- a/erpnext/education/doctype/question/question.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-
-
-class Question(Document):
-
-	def validate(self):
-		self.check_at_least_one_option()
-		self.check_minimum_one_correct_answer()
-		self.set_question_type()
-
-	def check_at_least_one_option(self):
-		if len(self.options) <= 1:
-			frappe.throw(_("A question must have more than one options"))
-		else:
-			pass
-
-	def check_minimum_one_correct_answer(self):
-		correct_options = [option.is_correct for option in self.options]
-		if bool(sum(correct_options)):
-			pass
-		else:
-			frappe.throw(_("A qustion must have at least one correct options"))
-
-	def set_question_type(self):
-		correct_options = [option for option in self.options if option.is_correct]
-		if len(correct_options) > 1:
-			self.question_type = "Multiple Correct Answer"
-		else:
-			self.question_type = "Single Correct Answer"
-
-	def get_answer(self):
-		options = self.options
-		answers = [item.name for item in options if item.is_correct == True]
-		if len(answers) == 0:
-			frappe.throw(_("No correct answer is set for {0}").format(self.name))
-			return None
-		elif len(answers) == 1:
-			return answers[0]
-		else:
-			return answers
diff --git a/erpnext/education/doctype/question/test_question.py b/erpnext/education/doctype/question/test_question.py
deleted file mode 100644
index 7506d84..0000000
--- a/erpnext/education/doctype/question/test_question.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestQuestion(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/quiz/__init__.py b/erpnext/education/doctype/quiz/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/quiz/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/quiz/quiz.js b/erpnext/education/doctype/quiz/quiz.js
deleted file mode 100644
index 320869b..0000000
--- a/erpnext/education/doctype/quiz/quiz.js
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Quiz', {
-	refresh: function(frm) {
-		if (!frm.doc.__islocal) {
-			frm.add_custom_button(__('Add to Topics'), function() {
-				frm.trigger('add_quiz_to_topics');
-			}, __('Action'));
-		}
-	},
-
-	validate: function(frm){
-		frm.events.check_duplicate_question(frm.doc.question);
-	},
-
-	check_duplicate_question: function(questions_data){
-		var questions = [];
-		questions_data.forEach(function(q){
-			questions.push(q.question_link);
-		});
-		var questions_set = new Set(questions);
-		if (questions.length != questions_set.size) {
-			frappe.throw(__('The question cannot be duplicate'));
-		}
-	},
-
-	add_quiz_to_topics: function(frm) {
-		get_topics_without_quiz(frm.doc.name).then(r => {
-			if (r.message.length) {
-				frappe.prompt([
-					{
-						fieldname: 'topics',
-						label: __('Topics'),
-						fieldtype: 'MultiSelectPills',
-						get_data: function() {
-							return r.message;
-						}
-					}
-				],
-				function(data) {
-					frappe.call({
-						method: 'erpnext.education.doctype.topic.topic.add_content_to_topics',
-						args: {
-							'content_type': 'Quiz',
-							'content': frm.doc.name,
-							'topics': data.topics,
-						},
-						callback: function(r) {
-							if (!r.exc) {
-								frm.reload_doc();
-							}
-						},
-						freeze: true,
-						freeze_message: __('...Adding Quiz to Topics')
-					});
-				}, __('Add Quiz to Topics'), __('Add'));
-			} else {
-				frappe.msgprint(__('This quiz is already added to the existing topics'));
-			}
-		});
-	}
-});
-
-let get_topics_without_quiz = function(quiz) {
-	return frappe.call({
-		type: 'GET',
-		method: 'erpnext.education.doctype.quiz.quiz.get_topics_without_quiz',
-		args: {'quiz': quiz}
-	});
-};
diff --git a/erpnext/education/doctype/quiz/quiz.json b/erpnext/education/doctype/quiz/quiz.json
deleted file mode 100644
index 16d7d7e..0000000
--- a/erpnext/education/doctype/quiz/quiz.json
+++ /dev/null
@@ -1,128 +0,0 @@
-{
- "actions": [],
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:title",
- "creation": "2018-10-17 05:52:50.149904",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "title",
-  "question",
-  "quiz_configuration_section",
-  "passing_score",
-  "max_attempts",
-  "grading_basis",
-  "column_break_7",
-  "is_time_bound",
-  "duration"
- ],
- "fields": [
-  {
-   "fieldname": "title",
-   "fieldtype": "Data",
-   "label": "Title",
-   "reqd": 1,
-   "unique": 1
-  },
-  {
-   "fieldname": "question",
-   "fieldtype": "Table",
-   "label": "Question",
-   "options": "Quiz Question",
-   "reqd": 1
-  },
-  {
-   "fieldname": "quiz_configuration_section",
-   "fieldtype": "Section Break",
-   "label": "Quiz Configuration"
-  },
-  {
-   "default": "75",
-   "description": "Score out of 100",
-   "fieldname": "passing_score",
-   "fieldtype": "Float",
-   "in_list_view": 1,
-   "label": "Passing Score",
-   "reqd": 1
-  },
-  {
-   "default": "1",
-   "description": "Enter 0 to waive limit",
-   "fieldname": "max_attempts",
-   "fieldtype": "Int",
-   "in_list_view": 1,
-   "label": "Max Attempts",
-   "reqd": 1
-  },
-  {
-   "default": "Latest Highest Score",
-   "fieldname": "grading_basis",
-   "fieldtype": "Select",
-   "label": "Grading Basis",
-   "options": "Latest Highest Score\nLatest Attempt"
-  },
-  {
-   "default": "0",
-   "fieldname": "is_time_bound",
-   "fieldtype": "Check",
-   "label": "Is Time-Bound"
-  },
-  {
-   "depends_on": "is_time_bound",
-   "fieldname": "duration",
-   "fieldtype": "Duration",
-   "label": "Duration"
-  },
-  {
-   "fieldname": "column_break_7",
-   "fieldtype": "Column Break"
-  }
- ],
- "links": [],
- "modified": "2020-12-24 15:41:35.043262",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Quiz",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "LMS User",
-   "share": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Instructor",
-   "share": 1,
-   "write": 1
-  }
- ],
- "quick_entry": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/quiz/quiz.py b/erpnext/education/doctype/quiz/quiz.py
deleted file mode 100644
index 9ad7252d..0000000
--- a/erpnext/education/doctype/quiz/quiz.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-
-
-class Quiz(Document):
-	def validate(self):
-		if self.passing_score > 100:
-			frappe.throw(_("Passing Score value should be between 0 and 100"))
-
-	def allowed_attempt(self, enrollment, quiz_name):
-		if self.max_attempts ==  0:
-			return True
-
-		try:
-			if len(frappe.get_all("Quiz Activity", {'enrollment': enrollment.name, 'quiz': quiz_name})) >= self.max_attempts:
-				frappe.msgprint(_("Maximum attempts for this quiz reached!"))
-				return False
-			else:
-				return True
-		except Exception as e:
-			return False
-
-
-	def evaluate(self, response_dict, quiz_name):
-		questions = [frappe.get_doc('Question', question.question_link) for question in self.question]
-		answers = {q.name:q.get_answer() for q in questions}
-		result = {}
-		for key in answers:
-			try:
-				if isinstance(response_dict[key], list):
-					is_correct = compare_list_elementwise(response_dict[key], answers[key])
-				else:
-					is_correct = (response_dict[key] == answers[key])
-			except Exception as e:
-				is_correct = False
-			result[key] = is_correct
-		score = (sum(result.values()) * 100 ) / len(answers)
-		if score >= self.passing_score:
-			status = "Pass"
-		else:
-			status = "Fail"
-		return result, score, status
-
-
-	def get_questions(self):
-		return [frappe.get_doc('Question', question.question_link) for question in self.question]
-
-def compare_list_elementwise(*args):
-	try:
-		if all(len(args[0]) == len(_arg) for _arg in args[1:]):
-			return all(all([element in (item) for element in args[0]]) for item in args[1:])
-		else:
-			return False
-	except TypeError:
-		frappe.throw(_("Compare List function takes on list arguments"))
-
-@frappe.whitelist()
-def get_topics_without_quiz(quiz):
-	data = []
-	for entry in frappe.db.get_all('Topic'):
-		topic = frappe.get_doc('Topic', entry.name)
-		topic_contents = [tc.content for tc in topic.topic_content]
-		if not topic_contents or quiz not in topic_contents:
-			data.append(topic.name)
-	return data
diff --git a/erpnext/education/doctype/quiz/test_quiz.py b/erpnext/education/doctype/quiz/test_quiz.py
deleted file mode 100644
index a69a0c1..0000000
--- a/erpnext/education/doctype/quiz/test_quiz.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestQuiz(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/quiz_activity/__init__.py b/erpnext/education/doctype/quiz_activity/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/quiz_activity/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/quiz_activity/quiz_activity.js b/erpnext/education/doctype/quiz_activity/quiz_activity.js
deleted file mode 100644
index f6ba12c..0000000
--- a/erpnext/education/doctype/quiz_activity/quiz_activity.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('Quiz Activity', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/quiz_activity/quiz_activity.json b/erpnext/education/doctype/quiz_activity/quiz_activity.json
deleted file mode 100644
index 742c887..0000000
--- a/erpnext/education/doctype/quiz_activity/quiz_activity.json
+++ /dev/null
@@ -1,163 +0,0 @@
-{
- "actions": [],
- "autoname": "format:EDU-QA-{YYYY}-{#####}",
- "beta": 1,
- "creation": "2018-10-15 15:48:40.482821",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "enrollment",
-  "student",
-  "column_break_3",
-  "course",
-  "section_break_5",
-  "quiz",
-  "column_break_7",
-  "status",
-  "section_break_9",
-  "result",
-  "section_break_11",
-  "activity_date",
-  "score",
-  "column_break_14",
-  "time_taken"
- ],
- "fields": [
-  {
-   "fieldname": "enrollment",
-   "fieldtype": "Link",
-   "label": "Enrollment",
-   "options": "Course Enrollment",
-   "set_only_once": 1
-  },
-  {
-   "fetch_from": "enrollment.student",
-   "fieldname": "student",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Student",
-   "options": "Student",
-   "read_only": 1
-  },
-  {
-   "fieldname": "column_break_3",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fetch_from": "enrollment.course",
-   "fieldname": "course",
-   "fieldtype": "Link",
-   "label": "Course",
-   "options": "Course",
-   "read_only": 1,
-   "set_only_once": 1
-  },
-  {
-   "fieldname": "section_break_5",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fieldname": "quiz",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Quiz",
-   "options": "Quiz",
-   "set_only_once": 1
-  },
-  {
-   "fieldname": "column_break_7",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "status",
-   "fieldtype": "Select",
-   "label": "Status",
-   "options": "\nPass\nFail",
-   "read_only": 1
-  },
-  {
-   "fieldname": "section_break_9",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fieldname": "result",
-   "fieldtype": "Table",
-   "label": "Result",
-   "options": "Quiz Result",
-   "set_only_once": 1
-  },
-  {
-   "fieldname": "activity_date",
-   "fieldtype": "Data",
-   "label": "Activity Date",
-   "set_only_once": 1
-  },
-  {
-   "fieldname": "score",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Score",
-   "set_only_once": 1
-  },
-  {
-   "fieldname": "time_taken",
-   "fieldtype": "Duration",
-   "label": "Time Taken",
-   "set_only_once": 1
-  },
-  {
-   "fieldname": "section_break_11",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fieldname": "column_break_14",
-   "fieldtype": "Column Break"
-  }
- ],
- "links": [],
- "modified": "2020-12-24 15:41:20.085380",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Quiz Activity",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "LMS User",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Instructor",
-   "share": 1
-  }
- ],
- "quick_entry": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/quiz_activity/quiz_activity.py b/erpnext/education/doctype/quiz_activity/quiz_activity.py
deleted file mode 100644
index a67f82f..0000000
--- a/erpnext/education/doctype/quiz_activity/quiz_activity.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class QuizActivity(Document):
-	pass
diff --git a/erpnext/education/doctype/quiz_activity/test_quiz_activity.py b/erpnext/education/doctype/quiz_activity/test_quiz_activity.py
deleted file mode 100644
index 1040c1a..0000000
--- a/erpnext/education/doctype/quiz_activity/test_quiz_activity.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestQuizActivity(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/quiz_question/__init__.py b/erpnext/education/doctype/quiz_question/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/quiz_question/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/quiz_question/quiz_question.json b/erpnext/education/doctype/quiz_question/quiz_question.json
deleted file mode 100644
index aab07a3..0000000
--- a/erpnext/education/doctype/quiz_question/quiz_question.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
- "allow_rename": 1,
- "creation": "2018-10-17 06:13:00.098883",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "question_link",
-  "question"
- ],
- "fields": [
-  {
-   "fieldname": "question_link",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Question Link",
-   "options": "Question",
-   "reqd": 1
-  },
-  {
-   "fetch_from": "question_link.question",
-   "fieldname": "question",
-   "fieldtype": "Text Editor",
-   "in_list_view": 1,
-   "label": "Question",
-   "read_only": 1
-  }
- ],
- "istable": 1,
- "modified": "2020-09-24 12:24:02.312577",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Quiz Question",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
diff --git a/erpnext/education/doctype/quiz_question/quiz_question.py b/erpnext/education/doctype/quiz_question/quiz_question.py
deleted file mode 100644
index 91641eb..0000000
--- a/erpnext/education/doctype/quiz_question/quiz_question.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class QuizQuestion(Document):
-	pass
diff --git a/erpnext/education/doctype/quiz_result/__init__.py b/erpnext/education/doctype/quiz_result/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/quiz_result/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/quiz_result/quiz_result.js b/erpnext/education/doctype/quiz_result/quiz_result.js
deleted file mode 100644
index a018749..0000000
--- a/erpnext/education/doctype/quiz_result/quiz_result.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('Quiz Result', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/quiz_result/quiz_result.json b/erpnext/education/doctype/quiz_result/quiz_result.json
deleted file mode 100644
index 67c7e2d..0000000
--- a/erpnext/education/doctype/quiz_result/quiz_result.json
+++ /dev/null
@@ -1,52 +0,0 @@
-{
- "creation": "2018-10-15 15:52:25.766374",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "question",
-  "selected_option",
-  "quiz_result"
- ],
- "fields": [
-  {
-   "fieldname": "question",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Question",
-   "options": "Question",
-   "read_only": 1,
-   "reqd": 1,
-   "set_only_once": 1
-  },
-  {
-   "fieldname": "selected_option",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Selected Option",
-   "read_only": 1,
-   "set_only_once": 1
-  },
-  {
-   "fieldname": "quiz_result",
-   "fieldtype": "Select",
-   "in_list_view": 1,
-   "label": "Result",
-   "options": "\nCorrect\nWrong",
-   "read_only": 1,
-   "reqd": 1,
-   "set_only_once": 1
-  }
- ],
- "istable": 1,
- "modified": "2019-06-03 12:52:32.267392",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Quiz Result",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/quiz_result/quiz_result.py b/erpnext/education/doctype/quiz_result/quiz_result.py
deleted file mode 100644
index 615281b..0000000
--- a/erpnext/education/doctype/quiz_result/quiz_result.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class QuizResult(Document):
-	pass
diff --git a/erpnext/education/doctype/quiz_result/test_quiz_result.py b/erpnext/education/doctype/quiz_result/test_quiz_result.py
deleted file mode 100644
index 12098a7..0000000
--- a/erpnext/education/doctype/quiz_result/test_quiz_result.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestQuizResult(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/room/__init__.py b/erpnext/education/doctype/room/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/room/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/room/room.js b/erpnext/education/doctype/room/room.js
deleted file mode 100644
index 1263b60..0000000
--- a/erpnext/education/doctype/room/room.js
+++ /dev/null
@@ -1,2 +0,0 @@
-frappe.ui.form.on("Room", {
-});
diff --git a/erpnext/education/doctype/room/room.json b/erpnext/education/doctype/room/room.json
deleted file mode 100644
index 888a0fa..0000000
--- a/erpnext/education/doctype/room/room.json
+++ /dev/null
@@ -1,161 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 1, 
- "allow_rename": 0, 
- "autoname": "HTL-ROOM-.YYYY.-.#####", 
- "beta": 0, 
- "creation": "2015-09-09 16:20:14.613061", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 0, 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "room_name", 
-   "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": "Room 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": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "room_number", 
-   "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": "Room Number", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "seating_capacity", 
-   "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": "Seating Capacity", 
-   "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, 
- "menu_index": 0, 
- "modified": "2018-08-21 16:15:46.096492", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Room", 
- "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": "Academics User", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "title_field": "room_name", 
- "track_changes": 0, 
- "track_seen": 0, 
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/room/room.py b/erpnext/education/doctype/room/room.py
deleted file mode 100644
index a2a8980..0000000
--- a/erpnext/education/doctype/room/room.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class Room(Document):
-	pass
diff --git a/erpnext/education/doctype/room/room_dashboard.py b/erpnext/education/doctype/room/room_dashboard.py
deleted file mode 100644
index b710722..0000000
--- a/erpnext/education/doctype/room/room_dashboard.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from frappe import _
-
-
-def get_data():
-	return {
-		'fieldname': 'room',
-		'transactions': [
-			{
-				'label': _('Course'),
-				'items': ['Course Schedule']
-			},
-			{
-				'label': _('Assessment'),
-				'items': ['Assessment Plan']
-			}
-		]
-	}
diff --git a/erpnext/education/doctype/room/test_records.json b/erpnext/education/doctype/room/test_records.json
deleted file mode 100644
index 6edf076..0000000
--- a/erpnext/education/doctype/room/test_records.json
+++ /dev/null
@@ -1,17 +0,0 @@
-[
-	{
-		"room_name": "_Test Room",
-		"room_number": "1001",
-		"seating_capacity": 100
-	},
-	{
-		"room_name": "_Test Room 1",
-		"room_number": "1002",
-		"seating_capacity": 100
-	},
-	{
-		"room_name": "_Test Room 2",
-		"room_number": "1003",
-		"seating_capacity": 100
-	}
-]
\ No newline at end of file
diff --git a/erpnext/education/doctype/room/test_room.py b/erpnext/education/doctype/room/test_room.py
deleted file mode 100644
index 68c97c7..0000000
--- a/erpnext/education/doctype/room/test_room.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and Contributors
-# See license.txt
-
-import unittest
-
-# test_records = frappe.get_test_records('Room')
-
-class TestRoom(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/school_house/__init__.py b/erpnext/education/doctype/school_house/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/school_house/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/school_house/school_house.js b/erpnext/education/doctype/school_house/school_house.js
deleted file mode 100644
index cf1dcd4..0000000
--- a/erpnext/education/doctype/school_house/school_house.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('School House', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/school_house/school_house.json b/erpnext/education/doctype/school_house/school_house.json
deleted file mode 100644
index 8a653a9..0000000
--- a/erpnext/education/doctype/school_house/school_house.json
+++ /dev/null
@@ -1,94 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "autoname": "field:house_name", 
- "beta": 0, 
- "creation": "2017-03-27 15:19:54.672995", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Setup", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "house_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": "House 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": 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": 0, 
- "max_attachments": 0, 
- "modified": "2017-11-10 19:05:06.419022", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "School House", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [
-  {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Academics User", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/school_house/school_house.py b/erpnext/education/doctype/school_house/school_house.py
deleted file mode 100644
index 52e0508..0000000
--- a/erpnext/education/doctype/school_house/school_house.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class SchoolHouse(Document):
-	pass
diff --git a/erpnext/education/doctype/school_house/test_school_house.py b/erpnext/education/doctype/school_house/test_school_house.py
deleted file mode 100644
index 7fe12d7..0000000
--- a/erpnext/education/doctype/school_house/test_school_house.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestSchoolHouse(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/student/__init__.py b/erpnext/education/doctype/student/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student/student.js b/erpnext/education/doctype/student/student.js
deleted file mode 100644
index aead556..0000000
--- a/erpnext/education/doctype/student/student.js
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Student', {
-	setup: function(frm) {
-		frm.add_fetch('guardian', 'guardian_name', 'guardian_name');
-		frm.add_fetch('student', 'title', 'full_name');
-		frm.add_fetch('student', 'gender', 'gender');
-		frm.add_fetch('student', 'date_of_birth', 'date_of_birth');
-
-		frm.set_query('student', 'siblings', function(doc) {
-			return {
-				'filters': {
-					'name': ['!=', doc.name]
-				}
-			};
-		})
-	},
-	refresh: function(frm) {
-		if(!frm.is_new()) {
-
-			// custom buttons
-			frm.add_custom_button(__('Accounting Ledger'), function() {
-				frappe.set_route('query-report', 'General Ledger',
-					{party_type:'Student', party:frm.doc.name});
-			});
-		}
-
-		frappe.db.get_value('Education Settings', {name: 'Education Settings'}, 'user_creation_skip', (r) => {
-			if (cint(r.user_creation_skip) !== 1) {
-				frm.set_df_property('student_email_id', 'reqd', 1);
-			}
-		});
-	}
-});
-
-frappe.ui.form.on('Student Guardian', {
-	guardians_add: function(frm){
-		frm.fields_dict['guardians'].grid.get_field('guardian').get_query = function(doc){
-			let guardian_list = [];
-			if(!doc.__islocal) guardian_list.push(doc.guardian);
-			$.each(doc.guardians, function(idx, val){
-				if (val.guardian) guardian_list.push(val.guardian);
-			});
-			return { filters: [['Guardian', 'name', 'not in', guardian_list]] };
-		};
-	}
-});
-
-
-frappe.ui.form.on('Student Sibling', {
-	siblings_add: function(frm){
-		frm.fields_dict['siblings'].grid.get_field('student').get_query = function(doc){
-			let sibling_list = [frm.doc.name];
-			$.each(doc.siblings, function(idx, val){
-				if (val.student && val.studying_in_same_institute == 'YES') {
-					sibling_list.push(val.student);
-				}
-			});
-			return { filters: [['Student', 'name', 'not in', sibling_list]] };
-		};
-	}
-});
diff --git a/erpnext/education/doctype/student/student.json b/erpnext/education/doctype/student/student.json
deleted file mode 100644
index 8ba9a7f..0000000
--- a/erpnext/education/doctype/student/student.json
+++ /dev/null
@@ -1,304 +0,0 @@
-{
- "actions": [],
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "naming_series:",
- "creation": "2015-09-07 13:00:55.938280",
- "doctype": "DocType",
- "document_type": "Document",
- "engine": "InnoDB",
- "field_order": [
-  "section_break_1",
-  "enabled",
-  "section_break_3",
-  "first_name",
-  "middle_name",
-  "last_name",
-  "user",
-  "column_break_4",
-  "naming_series",
-  "student_email_id",
-  "student_mobile_number",
-  "joining_date",
-  "image",
-  "section_break_7",
-  "date_of_birth",
-  "blood_group",
-  "column_break_3",
-  "gender",
-  "nationality",
-  "student_applicant",
-  "section_break_22",
-  "address_line_1",
-  "address_line_2",
-  "pincode",
-  "column_break_20",
-  "city",
-  "state",
-  "section_break_18",
-  "guardians",
-  "section_break_20",
-  "siblings",
-  "exit",
-  "date_of_leaving",
-  "leaving_certificate_number",
-  "column_break_31",
-  "reason_for_leaving",
-  "title"
- ],
- "fields": [
-  {
-   "fieldname": "section_break_1",
-   "fieldtype": "Section Break"
-  },
-  {
-   "default": "1",
-   "fieldname": "enabled",
-   "fieldtype": "Check",
-   "label": "Enabled"
-  },
-  {
-   "fieldname": "section_break_3",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fieldname": "first_name",
-   "fieldtype": "Data",
-   "in_global_search": 1,
-   "label": "First Name",
-   "reqd": 1
-  },
-  {
-   "fieldname": "middle_name",
-   "fieldtype": "Data",
-   "label": "Middle Name"
-  },
-  {
-   "fieldname": "last_name",
-   "fieldtype": "Data",
-   "in_global_search": 1,
-   "label": "Last Name"
-  },
-  {
-   "fieldname": "user",
-   "fieldtype": "Link",
-   "label": "User ID",
-   "options": "User"
-  },
-  {
-   "fieldname": "column_break_4",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "naming_series",
-   "fieldtype": "Select",
-   "label": "Naming Series",
-   "no_copy": 1,
-   "options": "EDU-STU-.YYYY.-",
-   "set_only_once": 1
-  },
-  {
-   "fieldname": "student_email_id",
-   "fieldtype": "Data",
-   "in_global_search": 1,
-   "label": "Student Email Address",
-   "unique": 1
-  },
-  {
-   "fieldname": "student_mobile_number",
-   "fieldtype": "Data",
-   "in_global_search": 1,
-   "label": "Student Mobile Number"
-  },
-  {
-   "default": "Today",
-   "fieldname": "joining_date",
-   "fieldtype": "Date",
-   "in_list_view": 1,
-   "label": "Joining Date"
-  },
-  {
-   "fieldname": "image",
-   "fieldtype": "Attach Image",
-   "hidden": 1,
-   "label": "Image",
-   "width": "10"
-  },
-  {
-   "fieldname": "section_break_7",
-   "fieldtype": "Section Break",
-   "label": "Personal Details"
-  },
-  {
-   "fieldname": "date_of_birth",
-   "fieldtype": "Date",
-   "label": "Date of Birth"
-  },
-  {
-   "fieldname": "blood_group",
-   "fieldtype": "Select",
-   "label": "Blood Group",
-   "options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-"
-  },
-  {
-   "fieldname": "column_break_3",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "gender",
-   "fieldtype": "Link",
-   "label": "Gender",
-   "options": "Gender"
-  },
-  {
-   "fieldname": "nationality",
-   "fieldtype": "Data",
-   "label": "Nationality"
-  },
-  {
-   "fieldname": "student_applicant",
-   "fieldtype": "Link",
-   "label": "Student Applicant",
-   "options": "Student Applicant",
-   "read_only": 1
-  },
-  {
-   "fieldname": "section_break_22",
-   "fieldtype": "Section Break",
-   "label": "Home Address"
-  },
-  {
-   "fieldname": "address_line_1",
-   "fieldtype": "Data",
-   "label": "Address Line 1"
-  },
-  {
-   "fieldname": "address_line_2",
-   "fieldtype": "Data",
-   "label": "Address Line 2"
-  },
-  {
-   "fieldname": "pincode",
-   "fieldtype": "Data",
-   "label": "Pincode"
-  },
-  {
-   "fieldname": "column_break_20",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "city",
-   "fieldtype": "Data",
-   "label": "City"
-  },
-  {
-   "fieldname": "state",
-   "fieldtype": "Data",
-   "label": "State"
-  },
-  {
-   "fieldname": "section_break_18",
-   "fieldtype": "Section Break",
-   "label": "Guardian Details"
-  },
-  {
-   "fieldname": "guardians",
-   "fieldtype": "Table",
-   "label": "Guardians",
-   "options": "Student Guardian"
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "section_break_20",
-   "fieldtype": "Section Break",
-   "label": "Sibling Details",
-   "options": "Country"
-  },
-  {
-   "fieldname": "siblings",
-   "fieldtype": "Table",
-   "label": "Siblings",
-   "options": "Student Sibling"
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "exit",
-   "fieldtype": "Section Break",
-   "label": "Exit"
-  },
-  {
-   "fieldname": "date_of_leaving",
-   "fieldtype": "Date",
-   "label": "Date of Leaving"
-  },
-  {
-   "fieldname": "leaving_certificate_number",
-   "fieldtype": "Data",
-   "label": "Leaving Certificate Number"
-  },
-  {
-   "fieldname": "column_break_31",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "reason_for_leaving",
-   "fieldtype": "Text",
-   "label": "Reason For Leaving"
-  },
-  {
-   "fieldname": "title",
-   "fieldtype": "Data",
-   "hidden": 1,
-   "label": "Title"
-  }
- ],
- "image_field": "image",
- "links": [],
- "modified": "2020-09-07 19:28:08.914568",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Student",
- "owner": "Administrator",
- "permissions": [
-  {
-   "read": 1,
-   "role": "Instructor"
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "import": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Student",
-   "share": 1
-  },
-  {
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "LMS User",
-   "share": 1
-  }
- ],
- "restrict_to_domain": "Education",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "title"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/student/student.py b/erpnext/education/doctype/student/student.py
deleted file mode 100644
index 44a3277..0000000
--- a/erpnext/education/doctype/student/student.py
+++ /dev/null
@@ -1,164 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.desk.form.linked_with import get_linked_doctypes
-from frappe.model.document import Document
-from frappe.utils import getdate, today
-
-from erpnext.education.utils import check_content_completion, check_quiz_completion
-
-
-class Student(Document):
-	def validate(self):
-		self.title = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name]))
-		self.validate_dates()
-
-		if self.student_applicant:
-			self.check_unique()
-			self.update_applicant_status()
-
-		if frappe.get_value("Student", self.name, "title") != self.title:
-			self.update_student_name_in_linked_doctype()
-
-	def validate_dates(self):
-		for sibling in self.siblings:
-			if sibling.date_of_birth and getdate(sibling.date_of_birth) > getdate():
-				frappe.throw(_("Row {0}:Sibling Date of Birth cannot be greater than today.").format(sibling.idx))
-
-		if self.date_of_birth and getdate(self.date_of_birth) >= getdate(today()):
-			frappe.throw(_("Date of Birth cannot be greater than today."))
-
-		if self.date_of_birth and getdate(self.date_of_birth) >= getdate(self.joining_date):
-			frappe.throw(_("Date of Birth cannot be greater than Joining Date."))
-
-		if self.joining_date and self.date_of_leaving and getdate(self.joining_date) > getdate(self.date_of_leaving):
-			frappe.throw(_("Joining Date can not be greater than Leaving Date"))
-
-	def update_student_name_in_linked_doctype(self):
-		linked_doctypes = get_linked_doctypes("Student")
-		for d in linked_doctypes:
-			meta = frappe.get_meta(d)
-			if not meta.issingle:
-				if "student_name" in [f.fieldname for f in meta.fields]:
-					frappe.db.sql("""UPDATE `tab{0}` set student_name = %s where {1} = %s"""
-						.format(d, linked_doctypes[d]["fieldname"][0]),(self.title, self.name))
-
-				if "child_doctype" in linked_doctypes[d].keys() and "student_name" in \
-					[f.fieldname for f in frappe.get_meta(linked_doctypes[d]["child_doctype"]).fields]:
-					frappe.db.sql("""UPDATE `tab{0}` set student_name = %s where {1} = %s"""
-						.format(linked_doctypes[d]["child_doctype"], linked_doctypes[d]["fieldname"][0]),(self.title, self.name))
-
-	def check_unique(self):
-		"""Validates if the Student Applicant is Unique"""
-		student = frappe.db.sql("select name from `tabStudent` where student_applicant=%s and name!=%s", (self.student_applicant, self.name))
-		if student:
-			frappe.throw(_("Student {0} exist against student applicant {1}").format(student[0][0], self.student_applicant))
-
-	def after_insert(self):
-		if not frappe.get_single('Education Settings').get('user_creation_skip'):
-			self.create_student_user()
-
-	def create_student_user(self):
-		"""Create a website user for student creation if not already exists"""
-		if not frappe.db.exists("User", self.student_email_id):
-			student_user = frappe.get_doc({
-				'doctype':'User',
-				'first_name': self.first_name,
-				'last_name': self.last_name,
-				'email': self.student_email_id,
-				'gender': self.gender,
-				'send_welcome_email': 1,
-				'user_type': 'Website User'
-				})
-			student_user.flags.ignore_permissions = True
-			student_user.add_roles("Student")
-			student_user.save()
-
-	def update_applicant_status(self):
-		"""Updates Student Applicant status to Admitted"""
-		if self.student_applicant:
-			frappe.db.set_value("Student Applicant", self.student_applicant, "application_status", "Admitted")
-
-	def get_all_course_enrollments(self):
-		"""Returns a list of course enrollments linked with the current student"""
-		course_enrollments = frappe.get_all("Course Enrollment", filters={"student": self.name}, fields=['course', 'name'])
-		if not course_enrollments:
-			return None
-		else:
-			enrollments = {item['course']:item['name'] for item in course_enrollments}
-			return enrollments
-
-	def get_program_enrollments(self):
-		"""Returns a list of course enrollments linked with the current student"""
-		program_enrollments = frappe.get_all("Program Enrollment", filters={"student": self.name}, fields=['program'])
-		if not program_enrollments:
-			return None
-		else:
-			enrollments = [item['program'] for item in program_enrollments]
-			return enrollments
-
-	def get_topic_progress(self, course_enrollment_name, topic):
-		"""
-		Get Progress Dictionary of a student for a particular topic
-			:param self: Student Object
-			:param course_enrollment_name: Name of the Course Enrollment
-			:param topic: Topic DocType Object
-		"""
-		contents = topic.get_contents()
-		progress = []
-		if contents:
-			for content in contents:
-				if content.doctype in ('Article', 'Video'):
-					status = check_content_completion(content.name, content.doctype, course_enrollment_name)
-					progress.append({'content': content.name, 'content_type': content.doctype, 'is_complete': status})
-				elif content.doctype == 'Quiz':
-					status, score, result, time_taken = check_quiz_completion(content, course_enrollment_name)
-					progress.append({'content': content.name, 'content_type': content.doctype, 'is_complete': status, 'score': score, 'result': result})
-		return progress
-
-	def enroll_in_program(self, program_name):
-		try:
-			enrollment = frappe.get_doc({
-					"doctype": "Program Enrollment",
-					"student": self.name,
-					"academic_year": frappe.get_last_doc("Academic Year").name,
-					"program": program_name,
-					"enrollment_date": frappe.utils.datetime.datetime.now()
-				})
-			enrollment.save(ignore_permissions=True)
-		except frappe.exceptions.ValidationError:
-			enrollment_name = frappe.get_list("Program Enrollment", filters={"student": self.name, "Program": program_name})[0].name
-			return frappe.get_doc("Program Enrollment", enrollment_name)
-		else:
-			enrollment.submit()
-			return enrollment
-
-	def enroll_in_course(self, course_name, program_enrollment, enrollment_date=None):
-		if enrollment_date is None:
-			enrollment_date = frappe.utils.datetime.datetime.now()
-		try:
-			enrollment = frappe.get_doc({
-					"doctype": "Course Enrollment",
-					"student": self.name,
-					"course": course_name,
-					"program_enrollment": program_enrollment,
-					"enrollment_date": enrollment_date
-				})
-			enrollment.save(ignore_permissions=True)
-		except frappe.exceptions.ValidationError:
-			enrollment_name = frappe.get_list("Course Enrollment", filters={"student": self.name, "course": course_name, "program_enrollment": program_enrollment})[0].name
-			return frappe.get_doc("Course Enrollment", enrollment_name)
-		else:
-			return enrollment
-
-def get_timeline_data(doctype, name):
-	'''Return timeline for attendance'''
-	return dict(frappe.db.sql('''select unix_timestamp(`date`), count(*)
-		from `tabStudent Attendance` where
-			student=%s
-			and `date` > date_sub(curdate(), interval 1 year)
-			and docstatus = 1 and status = 'Present'
-			group by date''', name))
diff --git a/erpnext/education/doctype/student/student_dashboard.py b/erpnext/education/doctype/student/student_dashboard.py
deleted file mode 100644
index 3ae772d..0000000
--- a/erpnext/education/doctype/student/student_dashboard.py
+++ /dev/null
@@ -1,38 +0,0 @@
-from frappe import _
-
-
-def get_data():
-	return {
-		'heatmap': True,
-		'heatmap_message': _('This is based on the attendance of this Student'),
-		'fieldname': 'student',
-		'non_standard_fieldnames': {
-			'Bank Account': 'party'
-		},
-		'transactions': [
-			{
-				'label': _('Admission'),
-				'items': ['Program Enrollment', 'Course Enrollment']
-			},
-			{
-				'label': _('Student Activity'),
-				'items': ['Student Log', 'Student Group', ]
-			},
-			{
-				'label': _('Assessment'),
-				'items': ['Assessment Result']
-			},
-			{
-				'label': _('Student LMS Activity'),
-				'items': ['Course Activity', 'Quiz Activity' ]
-			},
-			{
-				'label': _('Attendance'),
-				'items': ['Student Attendance', 'Student Leave Application']
-			},
-			{
-				'label': _('Fee'),
-				'items': ['Fees', 'Bank Account']
-			}
-		]
-	}
diff --git a/erpnext/education/doctype/student/student_list.js b/erpnext/education/doctype/student/student_list.js
deleted file mode 100644
index c1bce24..0000000
--- a/erpnext/education/doctype/student/student_list.js
+++ /dev/null
@@ -1,3 +0,0 @@
-frappe.listview_settings['Student'] = {
-	add_fields: [ "image"]
-}
diff --git a/erpnext/education/doctype/student/test_records.json b/erpnext/education/doctype/student/test_records.json
deleted file mode 100644
index 8ad3afa..0000000
--- a/erpnext/education/doctype/student/test_records.json
+++ /dev/null
@@ -1,35 +0,0 @@
-[
-	{
-		"first_name": "_Test",
-		"middle_name": "Student",
-		"last_name": "Name",
-		"program": "TC101",
-		"date_of_birth": "2000-01-01",
-		"gender": "Male",
-		"student_email_id": "_test_student@example.com",
-		"blood_group": "A+"
-
-	},
-	{
-		"first_name": "_Test",
-		"middle_name": "Student",
-		"last_name": "Name 1",
-		"program": "TC101",
-		"date_of_birth": "2000-01-01",
-		"gender": "Male",
-		"student_email_id": "_test_student_1@example.com",
-		"blood_group": "A+"
-
-	},
-	{
-		"first_name": "_Test",
-		"middle_name": "Student",
-		"last_name": "Name 2",
-		"program": "TC101",
-		"date_of_birth": "2000-01-01",
-		"gender": "Female",
-		"student_email_id": "_test_student_2@example.com",
-		"blood_group": "A+"
-
-	}
-]
\ No newline at end of file
diff --git a/erpnext/education/doctype/student/test_student.py b/erpnext/education/doctype/student/test_student.py
deleted file mode 100644
index 0a85708..0000000
--- a/erpnext/education/doctype/student/test_student.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and Contributors
-# See license.txt
-
-import unittest
-
-import frappe
-
-from erpnext.education.doctype.program.test_program import make_program_and_linked_courses
-
-test_records = frappe.get_test_records('Student')
-class TestStudent(unittest.TestCase):
-	def setUp(self):
-		create_student({"first_name": "_Test Name", "last_name": "_Test Last Name", "email": "_test_student@example.com"})
-		make_program_and_linked_courses("_Test Program 1", ["_Test Course 1", "_Test Course 2"])
-
-	def test_create_student_user(self):
-		self.assertTrue(bool(frappe.db.exists("User", "_test_student@example.com")))
-		frappe.db.rollback()
-
-	def test_enroll_in_program(self):
-		student = get_student("_test_student@example.com")
-		enrollment = student.enroll_in_program("_Test Program 1")
-		test_enrollment = frappe.get_all("Program Enrollment", filters={"student": student.name, "Program": "_Test Program 1"})
-		self.assertTrue(len(test_enrollment))
-		self.assertEqual(test_enrollment[0]['name'], enrollment.name)
-		frappe.db.rollback()
-
-	def test_get_program_enrollments(self):
-		student = get_student("_test_student@example.com")
-		enrollment = student.enroll_in_program("_Test Program 1")
-		program_enrollments = student.get_program_enrollments()
-		self.assertTrue("_Test Program 1" in program_enrollments)
-		frappe.db.rollback()
-
-	def test_get_all_course_enrollments(self):
-		student = get_student("_test_student@example.com")
-		enrollment = student.enroll_in_program("_Test Program 1")
-		course_enrollments = student.get_all_course_enrollments()
-		self.assertTrue("_Test Course 1" in course_enrollments.keys())
-		self.assertTrue("_Test Course 2" in course_enrollments.keys())
-		frappe.db.rollback()
-
-	def tearDown(self):
-		for entry in frappe.db.get_all("Course Enrollment"):
-			frappe.delete_doc("Course Enrollment", entry.name)
-
-		for entry in frappe.db.get_all("Program Enrollment"):
-			doc = frappe.get_doc("Program Enrollment", entry.name)
-			doc.cancel()
-			doc.delete()
-
-
-def create_student(student_dict):
-	student = get_student(student_dict['email'])
-	if not student:
-		student = frappe.get_doc({
-			"doctype": "Student",
-			"first_name": student_dict['first_name'],
-			"last_name": student_dict['last_name'],
-			"student_email_id": student_dict['email']
-		}).insert()
-	return student
-
-def get_student(email):
-	try:
-		student_id = frappe.get_all("Student", {"student_email_id": email}, ["name"])[0].name
-		return frappe.get_doc("Student", student_id)
-	except IndexError:
-		return None
diff --git a/erpnext/education/doctype/student_admission/__init__.py b/erpnext/education/doctype/student_admission/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student_admission/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student_admission/student_admission.js b/erpnext/education/doctype/student_admission/student_admission.js
deleted file mode 100644
index 2b62967..0000000
--- a/erpnext/education/doctype/student_admission/student_admission.js
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Student Admission', {
-	program: function(frm) {
-		if (frm.doc.academic_year && frm.doc.program) {
-			frm.doc.route = frappe.model.scrub(frm.doc.program) + "-" + frappe.model.scrub(frm.doc.academic_year)
-			frm.refresh_field("route");
-		}
-	},
-
-	academic_year: function(frm) {
-		frm.trigger("program");
-	},
-
-	admission_end_date: function(frm) {
-		if(frm.doc.admission_end_date && frm.doc.admission_end_date <= frm.doc.admission_start_date){
-			frm.set_value("admission_end_date", "");
-			frappe.throw(__("Admission End Date should be greater than Admission Start Date."));
-		}
-	}
-});
diff --git a/erpnext/education/doctype/student_admission/student_admission.json b/erpnext/education/doctype/student_admission/student_admission.json
deleted file mode 100644
index 75f2162..0000000
--- a/erpnext/education/doctype/student_admission/student_admission.json
+++ /dev/null
@@ -1,122 +0,0 @@
-{
- "actions": [],
- "allow_guest_to_view": 1,
- "allow_rename": 1,
- "creation": "2016-09-13 03:05:27.154713",
- "doctype": "DocType",
- "document_type": "Document",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "title",
-  "route",
-  "column_break_3",
-  "academic_year",
-  "admission_start_date",
-  "admission_end_date",
-  "published",
-  "enable_admission_application",
-  "section_break_5",
-  "program_details",
-  "introduction"
- ],
- "fields": [
-  {
-   "fieldname": "title",
-   "fieldtype": "Data",
-   "label": "Title"
-  },
-  {
-   "fieldname": "route",
-   "fieldtype": "Data",
-   "label": "Route",
-   "no_copy": 1,
-   "unique": 1
-  },
-  {
-   "fieldname": "column_break_3",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "academic_year",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "in_standard_filter": 1,
-   "label": "Academic Year",
-   "no_copy": 1,
-   "options": "Academic Year",
-   "reqd": 1
-  },
-  {
-   "fieldname": "admission_start_date",
-   "fieldtype": "Date",
-   "label": "Admission Start Date",
-   "mandatory_depends_on": "enable_admission_application",
-   "no_copy": 1
-  },
-  {
-   "fieldname": "admission_end_date",
-   "fieldtype": "Date",
-   "label": "Admission End Date",
-   "mandatory_depends_on": "enable_admission_application",
-   "no_copy": 1
-  },
-  {
-   "default": "0",
-   "fieldname": "published",
-   "fieldtype": "Check",
-   "label": "Publish on website"
-  },
-  {
-   "fieldname": "section_break_5",
-   "fieldtype": "Section Break",
-   "label": "Eligibility and Details"
-  },
-  {
-   "fieldname": "program_details",
-   "fieldtype": "Table",
-   "label": "Eligibility and Details",
-   "options": "Student Admission Program"
-  },
-  {
-   "fieldname": "introduction",
-   "fieldtype": "Text Editor",
-   "label": "Introduction"
-  },
-  {
-   "default": "0",
-   "depends_on": "published",
-   "fieldname": "enable_admission_application",
-   "fieldtype": "Check",
-   "label": "Enable Admission Application"
-  }
- ],
- "has_web_view": 1,
- "is_published_field": "published",
- "links": [],
- "modified": "2020-09-18 00:14:54.615321",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Student Admission",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "write": 1
-  }
- ],
- "restrict_to_domain": "Education",
- "route": "admissions",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "title"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_admission/student_admission.py b/erpnext/education/doctype/student_admission/student_admission.py
deleted file mode 100644
index b1fd780..0000000
--- a/erpnext/education/doctype/student_admission/student_admission.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.utils import nowdate
-from frappe.website.website_generator import WebsiteGenerator
-
-
-class StudentAdmission(WebsiteGenerator):
-	def autoname(self):
-		if not self.title:
-			self.title = self.get_title()
-		self.name = self.title
-
-	def validate(self):
-		if not self.route:		#pylint: disable=E0203
-			self.route = "admissions/" + "-".join(self.title.split(" "))
-
-		if self.enable_admission_application and not self.program_details:
-			frappe.throw(_("Please add programs to enable admission application."))
-
-	def get_context(self, context):
-		context.no_cache = 1
-		context.show_sidebar = True
-		context.title = self.title
-		context.parents = [{'name': 'admissions', 'title': _('All Student Admissions'), 'route': 'admissions' }]
-
-	def get_title(self):
-		return _("Admissions for {0}").format(self.academic_year)
-
-
-def get_list_context(context=None):
-	context.update({
-		"show_sidebar": True,
-		"title": _("Student Admissions"),
-		"get_list": get_admission_list,
-		"row_template": "education/doctype/student_admission/templates/student_admission_row.html",
-	})
-
-def get_admission_list(doctype, txt, filters, limit_start, limit_page_length=20, order_by="modified"):
-	return frappe.db.sql('''select name, title, academic_year, modified, admission_start_date, route,
-		admission_end_date from `tabStudent Admission` where published=1 and admission_end_date >= %s
-		order by admission_end_date asc limit {0}, {1}
-		'''.format(limit_start, limit_page_length), [nowdate()], as_dict=1)
diff --git a/erpnext/education/doctype/student_admission/templates/student_admission.html b/erpnext/education/doctype/student_admission/templates/student_admission.html
deleted file mode 100644
index f9ddac0..0000000
--- a/erpnext/education/doctype/student_admission/templates/student_admission.html
+++ /dev/null
@@ -1,77 +0,0 @@
-
-{% extends "templates/web.html" %}
-
-{% block breadcrumbs %}
-	{% include "templates/includes/breadcrumbs.html" %}
-{% endblock %}
-
-{% block header %}
-<h1>{{ title }}</h1>
-{% endblock %}
-
-{% block page_content %}
-	{% set today = frappe.utils.getdate(frappe.utils.nowdate()) %}
-	<div class="row transaction-subheading">
-	    <div class="col-xs-6">
-	        <span class="indicator
-				{% if frappe.utils.getdate(doc.admission_end_date) == today %}
-					red"> Application will be closed soon
-				{% elif frappe.utils.getdate(doc.admission_end_date) > today >= frappe.utils.getdate(doc.admission_start_date)%}
-					green"> Application open
-				{% elif frappe.utils.getdate(doc.admission_start_date) > today %}
-					blue"> Application will open
-				{% else %}
-					gray
-				{% endif  %}
-	        </span>
-		</div>
-	    <div class="col-xs-6 text-muted text-right small">
-	        {{ _("Start on") }}: {{ frappe.format_date(admission_start_date) }}<br>
-	        {{ _("End on") }}: {{ frappe.format_date(admission_end_date) }}
-	    </div>
-	</div><br>
-
-	{%- if introduction -%}
-		<div>{{ introduction }}</div>
-	{% endif %}
-
-	{% if program_details %}
-		<br>
-		<div class="table-responsive">
-			<h3 class="bold">Eligibility and Other Details:</h3>
-			<table class="table table-bordered table-hover">
-				<thead>
-					<tr class="active">
-						<th style="width: 90px">Program/Std.</th>
-						<th style="width: 180px">Description</th>
-						<th style="width: 100px">Minumum Age</th>
-						<th style="width: 100px">Maximum Age</th>
-						<th style="width: 100px">Application Fee</th>
-						{%- if doc.enable_admission_application and frappe.utils.getdate(doc.admission_start_date) <= today -%}
-							<th style="width: 120px"></th>
-						{% endif %}
-					</tr>
-				</thead>
-				<tbody>
-					{% for row in program_details %}
-						<tr>
-							<td>{{ row.program }}</td>
-							<td><div class="text-muted">{{ row.description if row.description else '' }}</div></td>
-							<td>{{ row.min_age }}</td>
-							<td>{{ row.max_age }}</td>
-							<td>{{ row.application_fee }}</td>
-							{%- if doc.enable_admission_application and frappe.utils.getdate(doc.admission_start_date) <= today -%}
-							<td>
-								<a class='btn btn-sm btn-primary' href='/student-applicant?new=1&student_admission={{doc.name}}&program={{row.program}}&academic_year={{academic_year}}'>
-									{{ _("Apply Now") }}
-								</a>
-							</td>
-							{% endif %}
-						</tr>
-					{% endfor %}
-				</tbody>
-			</table>
-		</div>
-	{% endif %}
-
-{% endblock %}
diff --git a/erpnext/education/doctype/student_admission/templates/student_admission_row.html b/erpnext/education/doctype/student_admission/templates/student_admission_row.html
deleted file mode 100644
index 529d651..0000000
--- a/erpnext/education/doctype/student_admission/templates/student_admission_row.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<div class="web-list-item transaction-list-item">
-	{% set today = frappe.utils.getdate(frappe.utils.nowdate()) %}
-	<a href = "{{ doc.route }}/" class="no-underline">
-		<div class="row">
-			<div class="col-sm-4 bold">
-				<span class="indicator
-				{% if frappe.utils.getdate(doc.admission_end_date) == today %}
-					red
-				{% elif frappe.utils.getdate(doc.admission_end_date) > today >= frappe.utils.getdate(doc.admission_start_date)%}
-					green
-				{% elif frappe.utils.getdate(doc.admission_start_date) > today %}
-					blue
-				{% else %}
-					gray
-				{% endif  %}
-				">{{ doc.title }}</span>
-			</div>
-			<div class="col-sm-2 small">
-				<span class="text-muted">
-					Academic Year
-				</span>
-				<div class="text-muted bold">
-					{{ doc.academic_year }}
-				</div>
-			</div>
-			<div class="col-sm-3 small">
-				<span class="text-muted">
-					Starts on
-				</span>
-				<div class="text-muted bold">
-					{{ frappe.format_date(doc.admission_start_date) }}
-				</div>
-			</div>
-			<div class="col-sm-3 small">
-				<span class="text-muted">
-					Ends on
-				</span>
-				<div class=" text-muted bold">
-					{{ frappe.format_date(doc.admission_end_date) }}
-				</div>
-			</div>
-		</div>
-	</a>
-</div>
diff --git a/erpnext/education/doctype/student_admission/test_student_admission.py b/erpnext/education/doctype/student_admission/test_student_admission.py
deleted file mode 100644
index 03867e2..0000000
--- a/erpnext/education/doctype/student_admission/test_student_admission.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-# test_records = frappe.get_test_records('Student Admission')
-
-class TestStudentAdmission(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/student_admission_program/__init__.py b/erpnext/education/doctype/student_admission_program/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student_admission_program/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student_admission_program/student_admission_program.json b/erpnext/education/doctype/student_admission_program/student_admission_program.json
deleted file mode 100644
index d14b9a4..0000000
--- a/erpnext/education/doctype/student_admission_program/student_admission_program.json
+++ /dev/null
@@ -1,73 +0,0 @@
-{
- "actions": [],
- "creation": "2017-09-15 12:59:43.207923",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "program",
-  "min_age",
-  "max_age",
-  "description",
-  "column_break_4",
-  "application_fee",
-  "applicant_naming_series"
- ],
- "fields": [
-  {
-   "fieldname": "program",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Program",
-   "options": "Program"
-  },
-  {
-   "fieldname": "column_break_4",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "application_fee",
-   "fieldtype": "Currency",
-   "in_list_view": 1,
-   "label": "Application Fee"
-  },
-  {
-   "fieldname": "applicant_naming_series",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Naming Series (for Student Applicant)"
-  },
-  {
-   "fieldname": "min_age",
-   "fieldtype": "Int",
-   "in_list_view": 1,
-   "label": "Minimum Age"
-  },
-  {
-   "fieldname": "max_age",
-   "fieldtype": "Int",
-   "in_list_view": 1,
-   "label": "Maximum Age"
-  },
-  {
-   "fetch_from": "program.description",
-   "fetch_if_empty": 1,
-   "fieldname": "description",
-   "fieldtype": "Small Text",
-   "label": "Description"
-  }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-10-05 13:03:42.005985",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Student Admission Program",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "restrict_to_domain": "Education",
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_admission_program/student_admission_program.py b/erpnext/education/doctype/student_admission_program/student_admission_program.py
deleted file mode 100644
index eba2239..0000000
--- a/erpnext/education/doctype/student_admission_program/student_admission_program.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class StudentAdmissionProgram(Document):
-	pass
diff --git a/erpnext/education/doctype/student_applicant/__init__.py b/erpnext/education/doctype/student_applicant/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student_applicant/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student_applicant/student_applicant.js b/erpnext/education/doctype/student_applicant/student_applicant.js
deleted file mode 100644
index 7b41a72..0000000
--- a/erpnext/education/doctype/student_applicant/student_applicant.js
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on("Student Applicant", {
-	setup: function(frm) {
-		frm.add_fetch("guardian", "guardian_name", "guardian_name");
-	},
-
-	refresh: function(frm) {
-		if (frm.doc.application_status==="Applied" && frm.doc.docstatus===1 ) {
-			frm.add_custom_button(__("Approve"), function() {
-				frm.set_value("application_status", "Approved");
-				frm.save_or_update();
-
-			}, 'Actions');
-
-			frm.add_custom_button(__("Reject"), function() {
-				frm.set_value("application_status", "Rejected");
-				frm.save_or_update();
-			}, 'Actions');
-		}
-
-		if (frm.doc.application_status === "Approved" && frm.doc.docstatus === 1) {
-			frm.add_custom_button(__("Enroll"), function() {
-				frm.events.enroll(frm)
-			}).addClass("btn-primary");
-
-			frm.add_custom_button(__("Reject"), function() {
-				frm.set_value("application_status", "Rejected");
-				frm.save_or_update();
-			}, 'Actions');
-		}
-
-		frappe.realtime.on("enroll_student_progress", function(data) {
-			if(data.progress) {
-				frappe.hide_msgprint(true);
-				frappe.show_progress(__("Enrolling student"), data.progress[0],data.progress[1]);
-			}
-		});
-
-		frappe.db.get_value("Education Settings", {name: "Education Settings"}, "user_creation_skip", (r) => {
-			if (cint(r.user_creation_skip) !== 1) {
-				frm.set_df_property("student_email_id", "reqd", 1);
-			}
-		});
-	},
-
-	enroll: function(frm) {
-		frappe.model.open_mapped_doc({
-			method: "erpnext.education.api.enroll_student",
-			frm: frm
-		})
-	}
-});
-
-frappe.ui.form.on('Student Sibling', {
-	setup: function(frm) {
-		frm.add_fetch("student", "title", "full_name");
-		frm.add_fetch("student", "gender", "gender");
-		frm.add_fetch("student", "date_of_birth", "date_of_birth");
-	}
-});
diff --git a/erpnext/education/doctype/student_applicant/student_applicant.json b/erpnext/education/doctype/student_applicant/student_applicant.json
deleted file mode 100644
index 95f9224..0000000
--- a/erpnext/education/doctype/student_applicant/student_applicant.json
+++ /dev/null
@@ -1,300 +0,0 @@
-{
- "actions": [],
- "allow_import": 1,
- "autoname": "naming_series:",
- "creation": "2015-09-11 11:50:09.740807",
- "doctype": "DocType",
- "document_type": "Document",
- "engine": "InnoDB",
- "field_order": [
-  "first_name",
-  "middle_name",
-  "last_name",
-  "program",
-  "student_category",
-  "lms_only",
-  "paid",
-  "column_break_8",
-  "naming_series",
-  "application_status",
-  "application_date",
-  "academic_year",
-  "academic_term",
-  "student_admission",
-  "image",
-  "section_break_4",
-  "date_of_birth",
-  "gender",
-  "blood_group",
-  "column_break_12",
-  "student_email_id",
-  "student_mobile_number",
-  "nationality",
-  "home_address",
-  "address_line_1",
-  "address_line_2",
-  "pincode",
-  "column_break_23",
-  "city",
-  "state",
-  "section_break_20",
-  "guardians",
-  "section_break_21",
-  "siblings",
-  "section_break_23",
-  "title",
-  "amended_from"
- ],
- "fields": [
-  {
-   "fieldname": "first_name",
-   "fieldtype": "Data",
-   "in_global_search": 1,
-   "label": "First Name",
-   "reqd": 1
-  },
-  {
-   "fieldname": "middle_name",
-   "fieldtype": "Data",
-   "label": "Middle Name"
-  },
-  {
-   "fieldname": "last_name",
-   "fieldtype": "Data",
-   "in_global_search": 1,
-   "label": "Last Name"
-  },
-  {
-   "fieldname": "program",
-   "fieldtype": "Link",
-   "in_filter": 1,
-   "in_global_search": 1,
-   "in_list_view": 1,
-   "in_standard_filter": 1,
-   "label": "Program",
-   "options": "Program",
-   "reqd": 1
-  },
-  {
-   "default": "0",
-   "fieldname": "lms_only",
-   "fieldtype": "Check",
-   "label": "LMS Only"
-  },
-  {
-   "default": "0",
-   "fieldname": "paid",
-   "fieldtype": "Check",
-   "label": "Paid"
-  },
-  {
-   "fieldname": "column_break_8",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "naming_series",
-   "fieldtype": "Select",
-   "label": "Naming Series",
-   "no_copy": 1,
-   "options": "EDU-APP-.YYYY.-",
-   "set_only_once": 1
-  },
-  {
-   "allow_on_submit": 1,
-   "depends_on": "eval:doc.docstatus != 0",
-   "fieldname": "application_status",
-   "fieldtype": "Select",
-   "in_filter": 1,
-   "label": "Application Status",
-   "no_copy": 1,
-   "options": "Applied\nApproved\nRejected\nAdmitted"
-  },
-  {
-   "default": "Today",
-   "fieldname": "application_date",
-   "fieldtype": "Date",
-   "label": "Application Date"
-  },
-  {
-   "fieldname": "academic_year",
-   "fieldtype": "Link",
-   "label": "Academic Year",
-   "options": "Academic Year"
-  },
-  {
-   "fieldname": "academic_term",
-   "fieldtype": "Link",
-   "label": "Academic Term",
-   "options": "Academic Term"
-  },
-  {
-   "fieldname": "student_admission",
-   "fieldtype": "Link",
-   "label": "Student Admission",
-   "options": "Student Admission"
-  },
-  {
-   "fieldname": "image",
-   "fieldtype": "Attach Image",
-   "hidden": 1,
-   "label": "Image"
-  },
-  {
-   "fieldname": "section_break_4",
-   "fieldtype": "Section Break",
-   "label": "Personal Details"
-  },
-  {
-   "fieldname": "date_of_birth",
-   "fieldtype": "Date",
-   "label": "Date of Birth"
-  },
-  {
-   "fieldname": "gender",
-   "fieldtype": "Select",
-   "label": "Gender",
-   "options": "\nMale\nFemale"
-  },
-  {
-   "fieldname": "blood_group",
-   "fieldtype": "Select",
-   "label": "Blood Group",
-   "options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-"
-  },
-  {
-   "fieldname": "column_break_12",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "student_email_id",
-   "fieldtype": "Data",
-   "label": "Student Email Address",
-   "options": "Email",
-   "unique": 1
-  },
-  {
-   "fieldname": "student_mobile_number",
-   "fieldtype": "Data",
-   "label": "Student Mobile Number"
-  },
-  {
-   "fieldname": "nationality",
-   "fieldtype": "Data",
-   "label": "Nationality"
-  },
-  {
-   "fieldname": "home_address",
-   "fieldtype": "Section Break",
-   "label": "Home Address"
-  },
-  {
-   "fieldname": "address_line_1",
-   "fieldtype": "Data",
-   "label": "Address Line 1"
-  },
-  {
-   "fieldname": "address_line_2",
-   "fieldtype": "Data",
-   "label": "Address Line 2"
-  },
-  {
-   "fieldname": "pincode",
-   "fieldtype": "Data",
-   "label": "Pincode"
-  },
-  {
-   "fieldname": "column_break_23",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "city",
-   "fieldtype": "Data",
-   "label": "City"
-  },
-  {
-   "fieldname": "state",
-   "fieldtype": "Data",
-   "label": "State"
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "section_break_20",
-   "fieldtype": "Section Break",
-   "label": "Guardian Details"
-  },
-  {
-   "fieldname": "guardians",
-   "fieldtype": "Table",
-   "label": "Guardians",
-   "options": "Student Guardian"
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "section_break_21",
-   "fieldtype": "Section Break",
-   "label": "Sibling Details"
-  },
-  {
-   "fieldname": "siblings",
-   "fieldtype": "Table",
-   "label": "Siblings",
-   "options": "Student Sibling"
-  },
-  {
-   "fieldname": "section_break_23",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fieldname": "title",
-   "fieldtype": "Data",
-   "hidden": 1,
-   "label": "Title"
-  },
-  {
-   "fieldname": "amended_from",
-   "fieldtype": "Link",
-   "label": "Amended From",
-   "no_copy": 1,
-   "options": "Student Applicant",
-   "print_hide": 1,
-   "read_only": 1
-  },
-  {
-   "fieldname": "student_category",
-   "fieldtype": "Link",
-   "label": "Student Category",
-   "options": "Student Category"
-  }
- ],
- "image_field": "image",
- "is_submittable": 1,
- "links": [],
- "modified": "2021-03-01 23:00:25.119241",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Student Applicant",
- "owner": "Administrator",
- "permissions": [
-  {
-   "amend": 1,
-   "cancel": 1,
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "import": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "submit": 1,
-   "write": 1
-  }
- ],
- "restrict_to_domain": "Education",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "title"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_applicant/student_applicant.py b/erpnext/education/doctype/student_applicant/student_applicant.py
deleted file mode 100644
index 5dae9f6..0000000
--- a/erpnext/education/doctype/student_applicant/student_applicant.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import add_years, date_diff, getdate, nowdate
-
-
-class StudentApplicant(Document):
-	def autoname(self):
-		from frappe.model.naming import set_name_by_naming_series
-		if self.student_admission:
-			naming_series = None
-			if self.program:
-				# set the naming series from the student admission if provided.
-				student_admission = get_student_admission_data(self.student_admission, self.program)
-				if student_admission:
-					naming_series = student_admission.get("applicant_naming_series")
-				else:
-					naming_series = None
-			else:
-				frappe.throw(_("Select the program first"))
-
-			if naming_series:
-				self.naming_series = naming_series
-
-		set_name_by_naming_series(self)
-
-	def validate(self):
-		self.validate_dates()
-		self.title = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name]))
-
-		if self.student_admission and self.program and self.date_of_birth:
-			self.validation_from_student_admission()
-
-	def validate_dates(self):
-		if self.date_of_birth and getdate(self.date_of_birth) >= getdate():
-			frappe.throw(_("Date of Birth cannot be greater than today."))
-
-	def on_update_after_submit(self):
-		student = frappe.get_list("Student",  filters= {"student_applicant": self.name})
-		if student:
-			frappe.throw(_("Cannot change status as student {0} is linked with student application {1}").format(student[0].name, self.name))
-
-	def on_submit(self):
-		if self.paid and not self.student_admission:
-			frappe.throw(_("Please select Student Admission which is mandatory for the paid student applicant"))
-
-	def validation_from_student_admission(self):
-
-		student_admission = get_student_admission_data(self.student_admission, self.program)
-
-		if student_admission and student_admission.min_age and \
-			date_diff(nowdate(), add_years(getdate(self.date_of_birth), student_admission.min_age)) < 0:
-				frappe.throw(_("Not eligible for the admission in this program as per Date Of Birth"))
-
-		if student_admission and student_admission.max_age and \
-			date_diff(nowdate(), add_years(getdate(self.date_of_birth), student_admission.max_age)) > 0:
-				frappe.throw(_("Not eligible for the admission in this program as per Date Of Birth"))
-
-
-	def on_payment_authorized(self, *args, **kwargs):
-		self.db_set('paid', 1)
-
-
-def get_student_admission_data(student_admission, program):
-
-	student_admission = frappe.db.sql("""select sa.admission_start_date, sa.admission_end_date,
-		sap.program, sap.min_age, sap.max_age, sap.applicant_naming_series
-		from `tabStudent Admission` sa, `tabStudent Admission Program` sap
-		where sa.name = sap.parent and sa.name = %s and sap.program = %s""", (student_admission, program), as_dict=1)
-
-	if student_admission:
-		return student_admission[0]
-	else:
-		return None
diff --git a/erpnext/education/doctype/student_applicant/student_applicant_list.js b/erpnext/education/doctype/student_applicant/student_applicant_list.js
deleted file mode 100644
index c39d46e..0000000
--- a/erpnext/education/doctype/student_applicant/student_applicant_list.js
+++ /dev/null
@@ -1,21 +0,0 @@
-frappe.listview_settings['Student Applicant'] = {
-	add_fields: [ "application_status", 'paid'],
-	has_indicator_for_draft: 1,
-	get_indicator: function(doc) {
-		if (doc.paid) {
-			return [__("Paid"), "green", "paid,=,Yes"];
-		}
-		else if (doc.application_status=="Applied") {
-			return [__("Applied"), "orange", "application_status,=,Applied"];
-		}
-		else if (doc.application_status=="Approved") {
-			return [__("Approved"), "green", "application_status,=,Approved"];
-		}
-		else if (doc.application_status=="Rejected") {
-			return [__("Rejected"), "red", "application_status,=,Rejected"];
-		}
-		else if (doc.application_status=="Admitted") {
-			return [__("Admitted"), "blue", "application_status,=,Admitted"];
-		}
-	}
-};
diff --git a/erpnext/education/doctype/student_applicant/test_student_applicant.py b/erpnext/education/doctype/student_applicant/test_student_applicant.py
deleted file mode 100644
index ba2e9c1..0000000
--- a/erpnext/education/doctype/student_applicant/test_student_applicant.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and Contributors
-# See license.txt
-
-import unittest
-
-# test_records = frappe.get_test_records('Student Applicant')
-
-class TestStudentApplicant(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/student_attendance/__init__.py b/erpnext/education/doctype/student_attendance/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student_attendance/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student_attendance/student_attendance.js b/erpnext/education/doctype/student_attendance/student_attendance.js
deleted file mode 100644
index 2bbecb9..0000000
--- a/erpnext/education/doctype/student_attendance/student_attendance.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-cur_frm.add_fetch("course_schedule", "schedule_date", "date");
-cur_frm.add_fetch("course_schedule", "student_group", "student_group")
diff --git a/erpnext/education/doctype/student_attendance/student_attendance.json b/erpnext/education/doctype/student_attendance/student_attendance.json
deleted file mode 100644
index e6e46d1..0000000
--- a/erpnext/education/doctype/student_attendance/student_attendance.json
+++ /dev/null
@@ -1,134 +0,0 @@
-{
- "actions": [],
- "allow_import": 1,
- "autoname": "naming_series:",
- "creation": "2015-11-05 15:20:23.045996",
- "doctype": "DocType",
- "document_type": "Document",
- "engine": "InnoDB",
- "field_order": [
-  "naming_series",
-  "student",
-  "student_name",
-  "student_mobile_number",
-  "course_schedule",
-  "student_group",
-  "column_break_3",
-  "date",
-  "status",
-  "leave_application",
-  "amended_from"
- ],
- "fields": [
-  {
-   "fieldname": "student",
-   "fieldtype": "Link",
-   "in_global_search": 1,
-   "in_standard_filter": 1,
-   "label": "Student",
-   "options": "Student",
-   "reqd": 1,
-   "search_index": 1
-  },
-  {
-   "fieldname": "course_schedule",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Course Schedule",
-   "options": "Course Schedule"
-  },
-  {
-   "fieldname": "date",
-   "fieldtype": "Date",
-   "label": "Date",
-   "reqd": 1,
-   "search_index": 1
-  },
-  {
-   "fieldname": "column_break_3",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fetch_from": "student.title",
-   "fieldname": "student_name",
-   "fieldtype": "Read Only",
-   "in_global_search": 1,
-   "label": "Student Name"
-  },
-  {
-   "fieldname": "student_group",
-   "fieldtype": "Link",
-   "in_global_search": 1,
-   "in_standard_filter": 1,
-   "label": "Student Group",
-   "options": "Student Group"
-  },
-  {
-   "default": "Present",
-   "fieldname": "status",
-   "fieldtype": "Select",
-   "in_list_view": 1,
-   "in_standard_filter": 1,
-   "label": "Status",
-   "options": "Present\nAbsent",
-   "reqd": 1
-  },
-  {
-   "fieldname": "leave_application",
-   "fieldtype": "Link",
-   "label": "Leave Application",
-   "options": "Student Leave Application",
-   "read_only": 1
-  },
-  {
-   "fieldname": "naming_series",
-   "fieldtype": "Select",
-   "label": "Series",
-   "options": "EDU-ATT-.YYYY.-"
-  },
-  {
-   "fieldname": "amended_from",
-   "fieldtype": "Link",
-   "label": "Amended From",
-   "no_copy": 1,
-   "options": "Student Attendance",
-   "print_hide": 1,
-   "read_only": 1
-  },
-  {
-   "fetch_from": "student.student_mobile_number",
-   "fieldname": "student_mobile_number",
-   "fieldtype": "Read Only",
-   "label": "Student Mobile Number",
-   "options": "Phone"
-  }
- ],
- "index_web_pages_for_search": 1,
- "is_submittable": 1,
- "links": [],
- "modified": "2021-03-24 00:02:11.005895",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Student Attendance",
- "owner": "Administrator",
- "permissions": [
-  {
-   "cancel": 1,
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "submit": 1,
-   "write": 1
-  }
- ],
- "restrict_to_domain": "Education",
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "student_name"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_attendance/student_attendance.py b/erpnext/education/doctype/student_attendance/student_attendance.py
deleted file mode 100644
index db0fd37..0000000
--- a/erpnext/education/doctype/student_attendance/student_attendance.py
+++ /dev/null
@@ -1,99 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import formatdate, get_link_to_form, getdate
-
-from erpnext import get_default_company
-from erpnext.education.api import get_student_group_students
-from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
-
-
-class StudentAttendance(Document):
-	def validate(self):
-		self.validate_mandatory()
-		self.validate_date()
-		self.set_date()
-		self.set_student_group()
-		self.validate_student()
-		self.validate_duplication()
-		self.validate_is_holiday()
-
-	def set_date(self):
-		if self.course_schedule:
-			self.date = frappe.db.get_value('Course Schedule', self.course_schedule, 'schedule_date')
-
-	def validate_mandatory(self):
-		if not (self.student_group or self.course_schedule):
-			frappe.throw(_('{0} or {1} is mandatory').format(frappe.bold('Student Group'),
-				frappe.bold('Course Schedule')), title=_('Mandatory Fields'))
-
-	def validate_date(self):
-		if not self.leave_application and getdate(self.date) > getdate():
-			frappe.throw(_('Attendance cannot be marked for future dates.'))
-
-		if self.student_group:
-			academic_year = frappe.db.get_value('Student Group', self.student_group, 'academic_year')
-			if academic_year:
-				year_start_date, year_end_date = frappe.db.get_value('Academic Year', academic_year, ['year_start_date', 'year_end_date'])
-				if year_start_date and year_end_date:
-					if getdate(self.date) < getdate(year_start_date) or getdate(self.date) > getdate(year_end_date):
-						frappe.throw(_('Attendance cannot be marked outside of Academic Year {0}').format(academic_year))
-
-	def set_student_group(self):
-		if self.course_schedule:
-			self.student_group = frappe.db.get_value('Course Schedule', self.course_schedule, 'student_group')
-
-	def validate_student(self):
-		if self.course_schedule:
-			student_group = frappe.db.get_value('Course Schedule', self.course_schedule, 'student_group')
-		else:
-			student_group = self.student_group
-		student_group_students = [d.student for d in get_student_group_students(student_group)]
-		if student_group and self.student not in student_group_students:
-			student_group_doc = get_link_to_form('Student Group', student_group)
-			frappe.throw(_('Student {0}: {1} does not belong to Student Group {2}').format(
-				frappe.bold(self.student), self.student_name, frappe.bold(student_group_doc)))
-
-	def validate_duplication(self):
-		"""Check if the Attendance Record is Unique"""
-		attendance_record = None
-		if self.course_schedule:
-			attendance_record = frappe.db.exists('Student Attendance', {
-				'student': self.student,
-				'course_schedule': self.course_schedule,
-				'docstatus': ('!=', 2),
-				'name': ('!=', self.name)
-			})
-		else:
-			attendance_record = frappe.db.exists('Student Attendance', {
-				'student': self.student,
-				'student_group': self.student_group,
-				'date': self.date,
-				'docstatus': ('!=', 2),
-				'name': ('!=', self.name),
-				'course_schedule': ''
-			})
-
-		if attendance_record:
-			record = get_link_to_form('Student Attendance', attendance_record)
-			frappe.throw(_('Student Attendance record {0} already exists against the Student {1}')
-				.format(record, frappe.bold(self.student)), title=_('Duplicate Entry'))
-
-	def validate_is_holiday(self):
-		holiday_list = get_holiday_list()
-		if is_holiday(holiday_list, self.date):
-			frappe.throw(_('Attendance cannot be marked for {0} as it is a holiday.').format(
-				frappe.bold(formatdate(self.date))))
-
-def get_holiday_list(company=None):
-	if not company:
-		company = get_default_company() or frappe.get_all('Company')[0].name
-
-	holiday_list = frappe.get_cached_value('Company', company,  'default_holiday_list')
-	if not holiday_list:
-		frappe.throw(_('Please set a default Holiday List for Company {0}').format(frappe.bold(get_default_company())))
-	return holiday_list
diff --git a/erpnext/education/doctype/student_attendance/student_attendance_dashboard.py b/erpnext/education/doctype/student_attendance/student_attendance_dashboard.py
deleted file mode 100644
index 6758452..0000000
--- a/erpnext/education/doctype/student_attendance/student_attendance_dashboard.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from frappe import _
-
-
-def get_data():
-	return {
-		'reports': [
-			{
-				'label': _('Reports'),
-				'items': ['Student Monthly Attendance Sheet', 'Student Batch-Wise Attendance']
-			}
-		]
-	}
diff --git a/erpnext/education/doctype/student_attendance/student_attendance_list.js b/erpnext/education/doctype/student_attendance/student_attendance_list.js
deleted file mode 100644
index e89b76c..0000000
--- a/erpnext/education/doctype/student_attendance/student_attendance_list.js
+++ /dev/null
@@ -1,11 +0,0 @@
-frappe.listview_settings['Student Attendance'] = {
-	add_fields: [ "status"],
-	get_indicator: function(doc) {
-		if (doc.status=="Absent") {
-			return [__("Absent"), "orange", "status,=,Absent"];
-		}
-		else if (doc.status=="Present") {
-			return [__("Present"), "green", "status,=,Present"];
-		}
-	}
-};
diff --git a/erpnext/education/doctype/student_attendance/test_student_attendance.py b/erpnext/education/doctype/student_attendance/test_student_attendance.py
deleted file mode 100644
index 6a43e30..0000000
--- a/erpnext/education/doctype/student_attendance/test_student_attendance.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and Contributors
-# See license.txt
-
-import unittest
-
-# test_records = frappe.get_test_records('Student Attendance')
-
-class TestStudentAttendance(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/student_attendance_tool/__init__.py b/erpnext/education/doctype/student_attendance_tool/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student_attendance_tool/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.js b/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.js
deleted file mode 100644
index 4526585..0000000
--- a/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.js
+++ /dev/null
@@ -1,196 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-frappe.provide("education");
-
-frappe.ui.form.on('Student Attendance Tool', {
-	setup: (frm) => {
-		frm.students_area = $('<div>')
-			.appendTo(frm.fields_dict.students_html.wrapper);
-	},
-	onload: function(frm) {
-		frm.set_query("student_group", function() {
-			return {
-				"filters": {
-					"group_based_on": frm.doc.group_based_on,
-					"disabled": 0
-				}
-			};
-		});
-	},
-
-	refresh: function(frm) {
-		if (frappe.route_options) {
-			frm.set_value("based_on", frappe.route_options.based_on);
-			frm.set_value("student_group", frappe.route_options.student_group);
-			frm.set_value("course_schedule", frappe.route_options.course_schedule);
-			frappe.route_options = null;
-		}
-		frm.disable_save();
-	},
-
-	based_on: function(frm) {
-		if (frm.doc.based_on == "Student Group") {
-			frm.set_value("course_schedule", "");
-		} else {
-			frm.set_value("student_group", "");
-		}
-	},
-
-	student_group: function(frm) {
-		if ((frm.doc.student_group && frm.doc.date) || frm.doc.course_schedule) {
-			frm.students_area.find('.student-attendance-checks').html(`<div style='padding: 2rem 0'>Fetching...</div>`);
-			var method = "erpnext.education.doctype.student_attendance_tool.student_attendance_tool.get_student_attendance_records";
-
-			frappe.call({
-				method: method,
-				args: {
-					based_on: frm.doc.based_on,
-					student_group: frm.doc.student_group,
-					date: frm.doc.date,
-					course_schedule: frm.doc.course_schedule
-				},
-				callback: function(r) {
-					frm.events.get_students(frm, r.message);
-				}
-			})
-		}
-	},
-
-	date: function(frm) {
-		if (frm.doc.date > frappe.datetime.get_today())
-			frappe.throw(__("Cannot mark attendance for future dates."));
-		frm.trigger("student_group");
-	},
-
-	course_schedule: function(frm) {
-		frm.trigger("student_group");
-	},
-
-	get_students: function(frm, students) {
-		students = students || [];
-		frm.students_editor = new education.StudentsEditor(frm, frm.students_area, students);
-	}
-});
-
-
-education.StudentsEditor = class StudentsEditor {
-	constructor(frm, wrapper, students) {
-		this.wrapper = wrapper;
-		this.frm = frm;
-		if(students.length > 0) {
-			this.make(frm, students);
-		} else {
-			this.show_empty_state();
-		}
-	}
-	make(frm, students) {
-		var me = this;
-
-		$(this.wrapper).empty();
-		var student_toolbar = $('<p>\
-			<button class="btn btn-default btn-add btn-xs" style="margin-right: 5px;"></button>\
-			<button class="btn btn-xs btn-default btn-remove" style="margin-right: 5px;"></button>\
-			<button class="btn btn-default btn-primary btn-mark-att btn-xs"></button></p>').appendTo($(this.wrapper));
-
-		student_toolbar.find(".btn-add")
-			.html(__('Check all'))
-			.on("click", function() {
-				$(me.wrapper).find('input[type="checkbox"]').each(function(i, check) {
-					if (!$(check).prop("disabled")) {
-						check.checked = true;
-					}
-				});
-			});
-
-		student_toolbar.find(".btn-remove")
-			.html(__('Uncheck all'))
-			.on("click", function() {
-				$(me.wrapper).find('input[type="checkbox"]').each(function(i, check) {
-					if (!$(check).prop("disabled")) {
-						check.checked = false;
-					}
-				});
-			});
-
-		student_toolbar.find(".btn-mark-att")
-			.html(__('Mark Attendance'))
-			.on("click", function() {
-				$(me.wrapper.find(".btn-mark-att")).attr("disabled", true);
-				var studs = [];
-				$(me.wrapper.find('input[type="checkbox"]')).each(function(i, check) {
-					var $check = $(check);
-					studs.push({
-						student: $check.data().student,
-						student_name: $check.data().studentName,
-						group_roll_number: $check.data().group_roll_number,
-						disabled: $check.prop("disabled"),
-						checked: $check.is(":checked")
-					});
-				});
-
-				var students_present = studs.filter(function(stud) {
-					return !stud.disabled && stud.checked;
-				});
-
-				var students_absent = studs.filter(function(stud) {
-					return !stud.disabled && !stud.checked;
-				});
-
-				frappe.confirm(__("Do you want to update attendance? <br> Present: {0} <br> Absent: {1}",
-					[students_present.length, students_absent.length]),
-					function() {	//ifyes
-						if(!frappe.request.ajax_count) {
-							frappe.call({
-								method: "erpnext.education.api.mark_attendance",
-								freeze: true,
-								freeze_message: __("Marking attendance"),
-								args: {
-									"students_present": students_present,
-									"students_absent": students_absent,
-									"student_group": frm.doc.student_group,
-									"course_schedule": frm.doc.course_schedule,
-									"date": frm.doc.date
-								},
-								callback: function(r) {
-									$(me.wrapper.find(".btn-mark-att")).attr("disabled", false);
-									frm.trigger("student_group");
-								}
-							});
-						}
-					},
-					function() {	//ifno
-						$(me.wrapper.find(".btn-mark-att")).attr("disabled", false);
-					}
-				);
-			});
-
-		// make html grid of students
-		let student_html = '';
-		for (let student of students) {
-			student_html += `<div class="col-sm-3">
-					<div class="checkbox">
-						<label>
-							<input
-								type="checkbox"
-								data-group_roll_number="${student.group_roll_number}"
-								data-student="${student.student}"
-								data-student-name="${student.student_name}"
-								class="students-check"
-								${student.status==='Present' ? 'checked' : ''}>
-							${student.group_roll_number} - ${student.student_name}
-						</label>
-					</div>
-				</div>`;
-		}
-
-		$(`<div class='student-attendance-checks'>${student_html}</div>`).appendTo(me.wrapper);
-	}
-
-	show_empty_state() {
-		$(this.wrapper).html(
-			`<div class="text-center text-muted" style="line-height: 100px;">
-				${__("No Students in")} ${this.frm.doc.student_group}
-			</div>`
-		);
-	}
-};
diff --git a/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.json b/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.json
deleted file mode 100644
index ee8f484..0000000
--- a/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.json
+++ /dev/null
@@ -1,118 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "creation": "2016-11-16 17:12:46.437539",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "based_on",
-  "group_based_on",
-  "column_break_2",
-  "student_group",
-  "academic_year",
-  "academic_term",
-  "course_schedule",
-  "date",
-  "attendance",
-  "students_html"
- ],
- "fields": [
-  {
-   "fieldname": "based_on",
-   "fieldtype": "Select",
-   "label": "Based On",
-   "options": "Student Group\nCourse Schedule"
-  },
-  {
-   "default": "Batch",
-   "depends_on": "eval:doc.based_on == \"Student Group\"",
-   "fieldname": "group_based_on",
-   "fieldtype": "Select",
-   "label": "Group Based On",
-   "options": "Batch\nCourse\nActivity"
-  },
-  {
-   "fieldname": "column_break_2",
-   "fieldtype": "Column Break"
-  },
-  {
-   "depends_on": "eval:doc.based_on ==\"Student Group\"",
-   "fieldname": "student_group",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Student Group",
-   "options": "Student Group",
-   "reqd": 1
-  },
-  {
-   "depends_on": "eval:doc.based_on ==\"Course Schedule\"",
-   "fieldname": "course_schedule",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Course Schedule",
-   "options": "Course Schedule",
-   "reqd": 1
-  },
-  {
-   "depends_on": "eval:doc.based_on ==\"Student Group\"",
-   "fieldname": "date",
-   "fieldtype": "Date",
-   "in_list_view": 1,
-   "label": "Date",
-   "reqd": 1
-  },
-  {
-   "depends_on": "eval: (doc.course_schedule \n|| (doc.student_group && doc.date))",
-   "fieldname": "attendance",
-   "fieldtype": "Section Break",
-   "label": "Attendance"
-  },
-  {
-   "fieldname": "students_html",
-   "fieldtype": "HTML",
-   "label": "Students HTML"
-  },
-  {
-   "fetch_from": "student_group.academic_year",
-   "fieldname": "academic_year",
-   "fieldtype": "Link",
-   "label": "Academic Year",
-   "options": "Academic Year",
-   "read_only": 1
-  },
-  {
-   "fetch_from": "student_group.academic_term",
-   "fieldname": "academic_term",
-   "fieldtype": "Link",
-   "label": "Academic Term",
-   "options": "Academic Term",
-   "read_only": 1
-  }
- ],
- "hide_toolbar": 1,
- "issingle": 1,
- "links": [],
- "modified": "2020-10-23 17:52:28.078971",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Student Attendance Tool",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "read": 1,
-   "role": "Instructor",
-   "write": 1
-  },
-  {
-   "create": 1,
-   "read": 1,
-   "role": "Academics User",
-   "write": 1
-  }
- ],
- "restrict_to_domain": "Education",
- "sort_field": "modified",
- "sort_order": "DESC"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py b/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py
deleted file mode 100644
index 92bb20c..0000000
--- a/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe.model.document import Document
-
-
-class StudentAttendanceTool(Document):
-	pass
-
-@frappe.whitelist()
-def get_student_attendance_records(based_on, date=None, student_group=None, course_schedule=None):
-	student_list = []
-	student_attendance_list = []
-
-	if based_on == "Course Schedule":
-		student_group = frappe.db.get_value("Course Schedule", course_schedule, "student_group")
-		if student_group:
-			student_list = frappe.get_all("Student Group Student", fields=["student", "student_name", "group_roll_number"],
-			filters={"parent": student_group, "active": 1}, order_by= "group_roll_number")
-
-	if not student_list:
-		student_list = frappe.get_all("Student Group Student", fields=["student", "student_name", "group_roll_number"],
-			filters={"parent": student_group, "active": 1}, order_by= "group_roll_number")
-
-	StudentAttendance = frappe.qb.DocType("Student Attendance")
-
-	if course_schedule:
-		student_attendance_list = (
-			frappe.qb.from_(StudentAttendance)
-				.select(StudentAttendance.student, StudentAttendance.status)
-				.where(
-					(StudentAttendance.course_schedule == course_schedule)
-				)
-			).run(as_dict=True)
-	else:
-		student_attendance_list = (
-			frappe.qb.from_(StudentAttendance)
-				.select(StudentAttendance.student, StudentAttendance.status)
-				.where(
-					(StudentAttendance.student_group == student_group)
-					& (StudentAttendance.date == date)
-					& ((StudentAttendance.course_schedule == "") | (StudentAttendance.course_schedule.isnull()))
-				)
-			).run(as_dict=True)
-
-	for attendance in student_attendance_list:
-		for student in student_list:
-			if student.student == attendance.student:
-				student.status = attendance.status
-
-	return student_list
diff --git a/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.py b/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.py
deleted file mode 100644
index c15036f..0000000
--- a/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestStudentAttendanceTool(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/student_batch_name/__init__.py b/erpnext/education/doctype/student_batch_name/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student_batch_name/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student_batch_name/student_batch_name.js b/erpnext/education/doctype/student_batch_name/student_batch_name.js
deleted file mode 100644
index 7ed3021..0000000
--- a/erpnext/education/doctype/student_batch_name/student_batch_name.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Student Batch Name', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/student_batch_name/student_batch_name.json b/erpnext/education/doctype/student_batch_name/student_batch_name.json
deleted file mode 100644
index abb6436..0000000
--- a/erpnext/education/doctype/student_batch_name/student_batch_name.json
+++ /dev/null
@@ -1,94 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "autoname": "field:batch_name", 
- "beta": 0, 
- "creation": "2016-11-17 18:45:57.965091", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Setup", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "batch_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": "Batch 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": 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": 0, 
- "max_attachments": 0, 
- "modified": "2017-11-10 19:08:17.980349", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Student Batch Name", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [
-  {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Academics User", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_batch_name/student_batch_name.py b/erpnext/education/doctype/student_batch_name/student_batch_name.py
deleted file mode 100644
index ae59291..0000000
--- a/erpnext/education/doctype/student_batch_name/student_batch_name.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class StudentBatchName(Document):
-	pass
diff --git a/erpnext/education/doctype/student_batch_name/test_records.json b/erpnext/education/doctype/student_batch_name/test_records.json
deleted file mode 100644
index bf365c6..0000000
--- a/erpnext/education/doctype/student_batch_name/test_records.json
+++ /dev/null
@@ -1,8 +0,0 @@
-[
-	{
-		"batch_name": "_Batch 1"
-	},
-	{
-		"batch_name": "_Batch 2"
-	}
-]
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_batch_name/test_student_batch_name.py b/erpnext/education/doctype/student_batch_name/test_student_batch_name.py
deleted file mode 100644
index ad9b545..0000000
--- a/erpnext/education/doctype/student_batch_name/test_student_batch_name.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-# test_records = frappe.get_test_records('Student Batch Name')
-
-class TestStudentBatchName(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/student_category/__init__.py b/erpnext/education/doctype/student_category/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student_category/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student_category/student_category.js b/erpnext/education/doctype/student_category/student_category.js
deleted file mode 100644
index 3a264d1..0000000
--- a/erpnext/education/doctype/student_category/student_category.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Student Category', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/student_category/student_category.json b/erpnext/education/doctype/student_category/student_category.json
deleted file mode 100644
index d7d4444..0000000
--- a/erpnext/education/doctype/student_category/student_category.json
+++ /dev/null
@@ -1,93 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "autoname": "field:category", 
- "beta": 0, 
- "creation": "2016-09-05 06:28:33.679415", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "category", 
-   "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": "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": 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": 0, 
- "max_attachments": 0, 
- "modified": "2017-11-10 19:09:45.783401", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Student Category", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [
-  {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Academics User", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_category/student_category.py b/erpnext/education/doctype/student_category/student_category.py
deleted file mode 100644
index 0d71859..0000000
--- a/erpnext/education/doctype/student_category/student_category.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class StudentCategory(Document):
-	pass
diff --git a/erpnext/education/doctype/student_category/student_category_dashboard.py b/erpnext/education/doctype/student_category/student_category_dashboard.py
deleted file mode 100644
index ebb639e..0000000
--- a/erpnext/education/doctype/student_category/student_category_dashboard.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from frappe import _
-
-
-def get_data():
-	return {
-		'fieldname': 'student_category',
-		'transactions': [
-			{
-				'label': _('Fee'),
-				'items': ['Fee Structure', 'Fee Schedule', 'Fees']
-			}
-		]
-	}
diff --git a/erpnext/education/doctype/student_category/test_student_category.py b/erpnext/education/doctype/student_category/test_student_category.py
deleted file mode 100644
index 76469ff..0000000
--- a/erpnext/education/doctype/student_category/test_student_category.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-# test_records = frappe.get_test_records('Student Category')
-
-class TestStudentCategory(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/student_group/__init__.py b/erpnext/education/doctype/student_group/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student_group/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student_group/student_group.js b/erpnext/education/doctype/student_group/student_group.js
deleted file mode 100644
index 39ee9ce..0000000
--- a/erpnext/education/doctype/student_group/student_group.js
+++ /dev/null
@@ -1,145 +0,0 @@
-cur_frm.add_fetch('student', 'title', 'student_name');
-
-frappe.ui.form.on('Student Group', {
-	onload: function(frm) {
-		frm.set_query('academic_term', function() {
-			return {
-				filters: {
-					'academic_year': (frm.doc.academic_year)
-				}
-			};
-		});
-		if (!frm.__islocal) {
-			frm.set_query('student', 'students', function() {
-				return{
-					query: 'erpnext.education.doctype.student_group.student_group.fetch_students',
-					filters: {
-						'academic_year': frm.doc.academic_year,
-						'group_based_on': frm.doc.group_based_on,
-						'academic_term': frm.doc.academic_term,
-						'program': frm.doc.program,
-						'batch': frm.doc.batch,
-						'student_category': frm.doc.student_category,
-						'course': frm.doc.course,
-						'student_group': frm.doc.name
-					}
-				}
-			});
-		}
-	},
-
-	refresh: function(frm) {
-		if (!frm.doc.__islocal) {
-
-			frm.add_custom_button(__('Add Guardians to Email Group'), function() {
-				frappe.call({
-					method: 'erpnext.education.api.update_email_group',
-					args: {
-						'doctype': 'Student Group',
-						'name': frm.doc.name
-					}
-				});
-			}, __('Actions'));
-
-			frm.add_custom_button(__('Student Attendance Tool'), function() {
-				frappe.route_options = {
-					based_on: 'Student Group',
-					student_group: frm.doc.name
-				}
-				frappe.set_route('Form', 'Student Attendance Tool', 'Student Attendance Tool');
-			}, __('Tools'));
-
-			frm.add_custom_button(__('Course Scheduling Tool'), function() {
-				frappe.route_options = {
-					student_group: frm.doc.name
-				}
-				frappe.set_route('Form', 'Course Scheduling Tool', 'Course Scheduling Tool');
-			}, __('Tools'));
-
-			frm.add_custom_button(__('Newsletter'), function() {
-				frappe.route_options = {
-					'Newsletter Email Group.email_group': frm.doc.name
-				}
-				frappe.set_route('List', 'Newsletter');
-			}, __('View'));
-
-		}
-	},
-
-	group_based_on: function(frm) {
-		if (frm.doc.group_based_on == 'Batch') {
-			frm.doc.course = null;
-			frm.set_df_property('program', 'reqd', 1);
-			frm.set_df_property('course', 'reqd', 0);
-		}
-		else if (frm.doc.group_based_on == 'Course') {
-			frm.set_df_property('program', 'reqd', 0);
-			frm.set_df_property('course', 'reqd', 1);
-		}
-		else if (frm.doc.group_based_on == 'Activity') {
-			frm.set_df_property('program', 'reqd', 0);
-			frm.set_df_property('course', 'reqd', 0);
-		}
-	},
-
-	get_students: function(frm) {
-		if (frm.doc.group_based_on == 'Batch' || frm.doc.group_based_on == 'Course') {
-			var student_list = [];
-			var max_roll_no = 0;
-			$.each(frm.doc.students, function(_i,d) {
-				student_list.push(d.student);
-				if (d.group_roll_number>max_roll_no) {
-					max_roll_no = d.group_roll_number;
-				}
-			});
-
-			if (frm.doc.academic_year) {
-				frappe.call({
-					method: 'erpnext.education.doctype.student_group.student_group.get_students',
-					args: {
-						'academic_year': frm.doc.academic_year,
-						'academic_term': frm.doc.academic_term,
-						'group_based_on': frm.doc.group_based_on,
-						'program': frm.doc.program,
-						'batch' : frm.doc.batch,
-						'student_category' : frm.doc.student_category,
-						'course': frm.doc.course
-					},
-					callback: function(r) {
-						if (r.message) {
-							$.each(r.message, function(i, d) {
-								if(!in_list(student_list, d.student)) {
-									var s = frm.add_child('students');
-									s.student = d.student;
-									s.student_name = d.student_name;
-									if (d.active === 0) {
-										s.active = 0;
-									}
-									s.group_roll_number = ++max_roll_no;
-								}
-							});
-							refresh_field('students');
-							frm.save();
-						} else {
-							frappe.msgprint(__('Student Group is already updated.'))
-						}
-					}
-				})
-			}
-		} else {
-			frappe.msgprint(__('Select students manually for the Activity based Group'));
-		}
-	}
-});
-
-frappe.ui.form.on('Student Group Instructor', {
-	instructors_add: function(frm){
-		frm.fields_dict['instructors'].grid.get_field('instructor').get_query = function(doc){
-			let instructor_list = [];
-			$.each(doc.instructors, function(idx, val){
-				instructor_list.push(val.instructor);
-			});
-			return { filters: [['Instructor', 'name', 'not in', instructor_list]] };
-		};
-	}
-});
diff --git a/erpnext/education/doctype/student_group/student_group.json b/erpnext/education/doctype/student_group/student_group.json
deleted file mode 100644
index 44b38b1..0000000
--- a/erpnext/education/doctype/student_group/student_group.json
+++ /dev/null
@@ -1,161 +0,0 @@
-{
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:student_group_name",
- "creation": "2015-09-07 12:55:52.072792",
- "doctype": "DocType",
- "document_type": "Document",
- "engine": "InnoDB",
- "field_order": [
-  "academic_year",
-  "group_based_on",
-  "student_group_name",
-  "max_strength",
-  "column_break_3",
-  "academic_term",
-  "program",
-  "batch",
-  "student_category",
-  "course",
-  "disabled",
-  "section_break_6",
-  "get_students",
-  "students",
-  "section_break_12",
-  "instructors"
- ],
- "fields": [
-  {
-   "fieldname": "academic_year",
-   "fieldtype": "Link",
-   "in_standard_filter": 1,
-   "label": "Academic Year",
-   "options": "Academic Year",
-   "set_only_once": 1
-  },
-  {
-   "fieldname": "group_based_on",
-   "fieldtype": "Select",
-   "in_list_view": 1,
-   "label": "Group Based on",
-   "options": "\nBatch\nCourse\nActivity",
-   "reqd": 1
-  },
-  {
-   "fieldname": "student_group_name",
-   "fieldtype": "Data",
-   "label": "Student Group Name",
-   "reqd": 1,
-   "unique": 1
-  },
-  {
-   "description": "Set 0 for no limit",
-   "fieldname": "max_strength",
-   "fieldtype": "Int",
-   "label": "Max Strength"
-  },
-  {
-   "fieldname": "column_break_3",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "academic_term",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "in_standard_filter": 1,
-   "label": "Academic Term",
-   "options": "Academic Term"
-  },
-  {
-   "fieldname": "program",
-   "fieldtype": "Link",
-   "in_global_search": 1,
-   "label": "Program",
-   "options": "Program"
-  },
-  {
-   "fieldname": "batch",
-   "fieldtype": "Link",
-   "in_global_search": 1,
-   "label": "Batch",
-   "options": "Student Batch Name"
-  },
-  {
-   "depends_on": "eval:doc.group_based_on == 'Course'",
-   "fieldname": "course",
-   "fieldtype": "Link",
-   "in_global_search": 1,
-   "in_standard_filter": 1,
-   "label": "Course",
-   "options": "Course"
-  },
-  {
-   "default": "0",
-   "fieldname": "disabled",
-   "fieldtype": "Check",
-   "label": "Disabled"
-  },
-  {
-   "depends_on": "eval:!doc.__islocal",
-   "fieldname": "section_break_6",
-   "fieldtype": "Section Break",
-   "label": "Students"
-  },
-  {
-   "fieldname": "get_students",
-   "fieldtype": "Button",
-   "label": "Get Students"
-  },
-  {
-   "allow_on_submit": 1,
-   "fieldname": "students",
-   "fieldtype": "Table",
-   "label": "Students",
-   "options": "Student Group Student"
-  },
-  {
-   "fieldname": "section_break_12",
-   "fieldtype": "Section Break",
-   "label": "Instructors"
-  },
-  {
-   "fieldname": "instructors",
-   "fieldtype": "Table",
-   "label": "Instructors",
-   "options": "Student Group Instructor"
-  },
-  {
-   "fieldname": "student_category",
-   "fieldtype": "Link",
-   "label": "Student Category",
-   "options": "Student Category"
-  }
- ],
- "modified": "2019-04-26 10:52:57.303951",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Student Group",
- "owner": "Administrator",
- "permissions": [
-  {
-   "read": 1,
-   "role": "Instructor"
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "write": 1
-  }
- ],
- "restrict_to_domain": "Education",
- "search_fields": "program, batch, course",
- "sort_field": "modified",
- "sort_order": "DESC"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_group/student_group.py b/erpnext/education/doctype/student_group/student_group.py
deleted file mode 100644
index ceae036..0000000
--- a/erpnext/education/doctype/student_group/student_group.py
+++ /dev/null
@@ -1,131 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import cint
-
-from erpnext.education.utils import validate_duplicate_student
-
-
-class StudentGroup(Document):
-	def validate(self):
-		self.validate_mandatory_fields()
-		self.validate_strength()
-		self.validate_students()
-		self.validate_and_set_child_table_fields()
-		validate_duplicate_student(self.students)
-
-	def validate_mandatory_fields(self):
-		if self.group_based_on == "Course" and not self.course:
-			frappe.throw(_("Please select Course"))
-		if self.group_based_on == "Course" and (not self.program and self.batch):
-			frappe.throw(_("Please select Program"))
-		if self.group_based_on == "Batch" and not self.program:
-			frappe.throw(_("Please select Program"))
-
-	def validate_strength(self):
-		if cint(self.max_strength) < 0:
-			frappe.throw(_("""Max strength cannot be less than zero."""))
-		if self.max_strength and len(self.students) > self.max_strength:
-			frappe.throw(_("""Cannot enroll more than {0} students for this student group.""").format(self.max_strength))
-
-	def validate_students(self):
-		program_enrollment = get_program_enrollment(self.academic_year, self.academic_term, self.program, self.batch, self.student_category, self.course)
-		students = [d.student for d in program_enrollment] if program_enrollment else []
-		for d in self.students:
-			if not frappe.db.get_value("Student", d.student, "enabled") and d.active and not self.disabled:
-				frappe.throw(_("{0} - {1} is inactive student").format(d.group_roll_number, d.student_name))
-
-			if (self.group_based_on == "Batch") and cint(frappe.defaults.get_defaults().validate_batch)\
-				and d.student not in students:
-				frappe.throw(_("{0} - {1} is not enrolled in the Batch {2}").format(d.group_roll_number, d.student_name, self.batch))
-
-			if (self.group_based_on == "Course") and cint(frappe.defaults.get_defaults().validate_course)\
-				and (d.student not in students):
-				frappe.throw(_("{0} - {1} is not enrolled in the Course {2}").format(d.group_roll_number, d.student_name, self.course))
-
-	def validate_and_set_child_table_fields(self):
-		roll_numbers = [d.group_roll_number for d in self.students if d.group_roll_number]
-		max_roll_no = max(roll_numbers) if roll_numbers else 0
-		roll_no_list = []
-		for d in self.students:
-			if not d.student_name:
-				d.student_name = frappe.db.get_value("Student", d.student, "title")
-			if not d.group_roll_number:
-				max_roll_no += 1
-				d.group_roll_number = max_roll_no
-			if d.group_roll_number in roll_no_list:
-				frappe.throw(_("Duplicate roll number for student {0}").format(d.student_name))
-			else:
-				roll_no_list.append(d.group_roll_number)
-
-@frappe.whitelist()
-def get_students(academic_year, group_based_on, academic_term=None, program=None, batch=None, student_category=None, course=None):
-	enrolled_students = get_program_enrollment(academic_year, academic_term, program, batch, student_category, course)
-
-	if enrolled_students:
-		student_list = []
-		for s in enrolled_students:
-			if frappe.db.get_value("Student", s.student, "enabled"):
-				s.update({"active": 1})
-			else:
-				s.update({"active": 0})
-			student_list.append(s)
-		return student_list
-	else:
-		frappe.msgprint(_("No students found"))
-		return []
-
-def get_program_enrollment(academic_year, academic_term=None, program=None, batch=None, student_category=None, course=None):
-
-	condition1 = " "
-	condition2 = " "
-	if academic_term:
-		condition1 += " and pe.academic_term = %(academic_term)s"
-	if program:
-		condition1 += " and pe.program = %(program)s"
-	if batch:
-		condition1 += " and pe.student_batch_name = %(batch)s"
-	if student_category:
-		condition1 += " and pe.student_category = %(student_category)s"
-	if course:
-		condition1 += " and pe.name = pec.parent and pec.course = %(course)s"
-		condition2 = ", `tabProgram Enrollment Course` pec"
-
-	return frappe.db.sql('''
-		select
-			pe.student, pe.student_name
-		from
-			`tabProgram Enrollment` pe {condition2}
-		where
-			pe.academic_year = %(academic_year)s  {condition1}
-		order by
-			pe.student_name asc
-		'''.format(condition1=condition1, condition2=condition2),
-                ({"academic_year": academic_year, "academic_term":academic_term, "program": program, "batch": batch, "student_category": student_category, "course": course}), as_dict=1)
-
-
-@frappe.whitelist()
-@frappe.validate_and_sanitize_search_inputs
-def fetch_students(doctype, txt, searchfield, start, page_len, filters):
-	if filters.get("group_based_on") != "Activity":
-		enrolled_students = get_program_enrollment(filters.get('academic_year'), filters.get('academic_term'),
-			filters.get('program'), filters.get('batch'), filters.get('student_category'))
-		student_group_student = frappe.db.sql_list('''select student from `tabStudent Group Student` where parent=%s''',
-			(filters.get('student_group')))
-		students = ([d.student for d in enrolled_students if d.student not in student_group_student]
-			if enrolled_students else [""]) or [""]
-		return frappe.db.sql("""select name, title from tabStudent
-			where name in ({0}) and (`{1}` LIKE %s or title LIKE %s)
-			order by idx desc, name
-			limit %s, %s""".format(", ".join(['%s']*len(students)), searchfield),
-			tuple(students + ["%%%s%%" % txt, "%%%s%%" % txt, start, page_len]))
-	else:
-		return frappe.db.sql("""select name, title from tabStudent
-			where `{0}` LIKE %s or title LIKE %s
-			order by idx desc, name
-			limit %s, %s""".format(searchfield),
-			tuple(["%%%s%%" % txt, "%%%s%%" % txt, start, page_len]))
diff --git a/erpnext/education/doctype/student_group/student_group_dashboard.py b/erpnext/education/doctype/student_group/student_group_dashboard.py
deleted file mode 100644
index d5b9302..0000000
--- a/erpnext/education/doctype/student_group/student_group_dashboard.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from frappe import _
-
-
-def get_data():
-	return {
-		'fieldname': 'student_group',
-		'transactions': [
-			{
-				'label': _('Assessment'),
-				'items': ['Assessment Plan', 'Assessment Result']
-			},
-			{
-				'label': _('Course'),
-				'items': ['Course Schedule']
-			}
-		]
-	}
diff --git a/erpnext/education/doctype/student_group/test_records.json b/erpnext/education/doctype/student_group/test_records.json
deleted file mode 100644
index 4c4e042..0000000
--- a/erpnext/education/doctype/student_group/test_records.json
+++ /dev/null
@@ -1,34 +0,0 @@
-[
-	{
-		"student_group_name": "Batch-_TP1-_Batch 1-2014-2015 (_Test Academic Term)",
-		"group_based_on": "Batch",
-		"program": "_TP1",
-		"batch": "_Batch 1",
-		"academic_year": "2014-2015",
-		"academic_term": "2014-2015 (_Test Academic Term)",
-		"max_strength": 0
-	},
-	{
-		"student_group_name": "Course-TC101-2014-2015 (_Test Academic Term)",
-		"group_based_on": "Course",
-		"course": "TC101",
-		"academic_year": "2014-2015",
-		"academic_term": "2014-2015 (_Test Academic Term)",
-		"max_strength": 0
-	},
-	{
-		"student_group_name": "Course-TC102-2014-2015 (_Test Academic Term)",
-		"group_based_on": "Course",
-		"course": "TC102",
-		"academic_year": "2014-2015",
-		"academic_term": "2014-2015 (_Test Academic Term)",
-		"max_strength": 0
-	},
-	{
-		"student_group_name": "Activity-2014-2015 (_Test Academic Term)",
-		"group_based_on": "Activity",
-		"academic_year": "2014-2015",
-		"academic_term": "2014-2015 (_Test Academic Term)",
-		"max_strength": 0
-	}
-]
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_group/test_student_group.py b/erpnext/education/doctype/student_group/test_student_group.py
deleted file mode 100644
index 807c632..0000000
--- a/erpnext/education/doctype/student_group/test_student_group.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and Contributors
-# See license.txt
-
-import unittest
-
-import frappe
-
-import erpnext.education
-
-
-def get_random_group():
-	doc = frappe.get_doc({
-		"doctype": "Student Group",
-		"student_group_name": "_Test Student Group-" + frappe.generate_hash(length=5),
-		"group_based_on": "Activity"
-	}).insert()
-
-	student_list = frappe.get_all('Student', limit=5)
-
-	doc.extend("students", [{"student":d.name, "active": 1} for d in student_list])
-	doc.save()
-
-	return doc
-
-class TestStudentGroup(unittest.TestCase):
-	def test_student_roll_no(self):
-		doc = get_random_group()
-		self.assertEqual(max([d.group_roll_number for d in doc.students]), len(doc.students))
-
-	def test_in_group(self):
-		doc = get_random_group()
-
-		last_student = doc.students[-1].student
-
-		# remove last student
-		doc.students = doc.students[:-1]
-		doc.save()
-
-		self.assertRaises(erpnext.education.StudentNotInGroupError,
-			erpnext.education.validate_student_belongs_to_group, last_student, doc.name)
-
-		# safe, don't throw validation
-		erpnext.education.validate_student_belongs_to_group(doc.students[0].student, doc.name)
diff --git a/erpnext/education/doctype/student_group_creation_tool/__init__.py b/erpnext/education/doctype/student_group_creation_tool/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student_group_creation_tool/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.js b/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.js
deleted file mode 100644
index c189e27..0000000
--- a/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.js
+++ /dev/null
@@ -1,40 +0,0 @@
-frappe.ui.form.on("Student Group Creation Tool", "refresh", function(frm) {
-	frm.disable_save();
-	frm.page.set_primary_action(__("Create Student Groups"), function() {
-		frappe.call({
-			method: "create_student_groups",
-			doc:frm.doc
-		})
-	});
-	frappe.realtime.on("student_group_creation_progress", function(data) {
-		if(data.progress) {
-			frappe.hide_msgprint(true);
-			frappe.show_progress(__("Creating student groups"), data.progress[0],data.progress[1]);
-		}
-	});
-});
-
-frappe.ui.form.on("Student Group Creation Tool", "get_courses", function(frm) {
-	frm.set_value("courses",[]);
-	if (frm.doc.academic_year && frm.doc.program) {
-		frappe.call({
-			method: "get_courses",
-			doc:frm.doc,
-			callback: function(r) {
-				if(r.message) {
-					frm.set_value("courses", r.message);
-				}
-			}
-		})
-	}
-});
-
-frappe.ui.form.on("Student Group Creation Tool", "onload", function(frm){
-	cur_frm.set_query("academic_term",function(){
-		return{
-			"filters":{
-				"academic_year": (frm.doc.academic_year)
-			}
-		};
-	});
-});
diff --git a/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.json b/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.json
deleted file mode 100644
index fe7cbdb..0000000
--- a/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.json
+++ /dev/null
@@ -1,309 +0,0 @@
-{
- "allow_copy": 1, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2016-01-04 14:45:36.576933", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 0, 
- "engine": "InnoDB", 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "academic_year", 
-   "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": "Academic Year", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Academic Year", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "Leave blank if you make students groups per year", 
-   "fieldname": "academic_term", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Academic Term", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Academic Term", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "get_courses", 
-   "fieldtype": "Button", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Get Courses", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_4", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "", 
-   "fieldname": "program", 
-   "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": "Program", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Program", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "Leave unchecked if you don't want to consider batch while making course based groups. ", 
-   "fieldname": "separate_groups", 
-   "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": "Separate course based Group for every Batch", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_4", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "courses", 
-   "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": "Courses", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student Group Creation Tool Course", 
-   "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": 1, 
- "hide_toolbar": 1, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 1, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2017-12-27 09:35:30.211254", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Student Group Creation Tool", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [
-  {
-   "amend": 0, 
-   "apply_user_permissions": 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": "Education Manager", 
-   "set_user_permissions": 0, 
-   "share": 0, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "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/education/doctype/student_group_creation_tool/student_group_creation_tool.py b/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.py
deleted file mode 100644
index 8fbfcec..0000000
--- a/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# Copyright (c) 2015, Frappe and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-
-from erpnext.education.doctype.student_group.student_group import get_students
-
-
-class StudentGroupCreationTool(Document):
-	@frappe.whitelist()
-	def get_courses(self):
-		group_list = []
-
-		batches = frappe.db.sql('''select name as batch from `tabStudent Batch Name`''', as_dict=1)
-		for batch in batches:
-			group_list.append({"group_based_on":"Batch", "batch":batch.batch})
-
-		courses = frappe.db.sql('''select course, course_name from `tabProgram Course` where parent=%s''',
-			(self.program), as_dict=1)
-		if self.separate_groups:
-			from itertools import product
-			course_list = product(courses,batches)
-			for course in course_list:
-				temp_dict = {}
-				temp_dict.update({"group_based_on":"Course"})
-				temp_dict.update(course[0])
-				temp_dict.update(course[1])
-				group_list.append(temp_dict)
-		else:
-			for course in courses:
-				course.update({"group_based_on":"Course"})
-				group_list.append(course)
-
-		for group in group_list:
-			if group.get("group_based_on") == "Batch":
-				student_group_name = self.program + "/" + group.get("batch") + "/" + (self.academic_term if self.academic_term else self.academic_year)
-				group.update({"student_group_name": student_group_name})
-			elif group.get("group_based_on") == "Course":
-				student_group_name = group.get("course") + "/" + self.program + ("/" + group.get("batch") if group.get("batch") else "") + "/" + (self.academic_term if self.academic_term else self.academic_year)
-				group.update({"student_group_name": student_group_name})
-
-		return group_list
-
-	@frappe.whitelist()
-	def create_student_groups(self):
-		if not self.courses:
-			frappe.throw(_("""No Student Groups created."""))
-
-		l = len(self.courses)
-		for d in self.courses:
-			if not d.student_group_name:
-				frappe.throw(_("""Student Group Name is mandatory in row {0}""".format(d.idx)))
-
-			if d.group_based_on == "Course" and not d.course:
-				frappe.throw(_("""Course is mandatory in row {0}""".format(d.idx)))
-
-			if d.group_based_on == "Batch" and not d.batch:
-				frappe.throw(_("""Batch is mandatory in row {0}""".format(d.idx)))
-
-			frappe.publish_realtime('student_group_creation_progress', {"progress": [d.idx, l]}, user=frappe.session.user)
-
-			student_group = frappe.new_doc("Student Group")
-			student_group.student_group_name = d.student_group_name
-			student_group.group_based_on = d.group_based_on
-			student_group.program = self.program
-			student_group.course = d.course
-			student_group.batch = d.batch
-			student_group.max_strength = d.max_strength
-			student_group.academic_term = self.academic_term
-			student_group.academic_year = self.academic_year
-			student_list = get_students(self.academic_year, d.group_based_on, self.academic_term, self.program, d.batch, d.course)
-
-			for student in student_list:
-				student_group.append('students', student)
-			student_group.save()
-
-		frappe.msgprint(_("{0} Student Groups created.").format(l))
diff --git a/erpnext/education/doctype/student_group_creation_tool/test_student_group_creation_tool.py b/erpnext/education/doctype/student_group_creation_tool/test_student_group_creation_tool.py
deleted file mode 100644
index 8722f97..0000000
--- a/erpnext/education/doctype/student_group_creation_tool/test_student_group_creation_tool.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestStudentGroupCreationTool(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/student_group_creation_tool_course/__init__.py b/erpnext/education/doctype/student_group_creation_tool_course/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student_group_creation_tool_course/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student_group_creation_tool_course/student_group_creation_tool_course.json b/erpnext/education/doctype/student_group_creation_tool_course/student_group_creation_tool_course.json
deleted file mode 100644
index 9f691a1..0000000
--- a/erpnext/education/doctype/student_group_creation_tool_course/student_group_creation_tool_course.json
+++ /dev/null
@@ -1,272 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2016-01-04 15:03:57.940079", 
- "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": "group_based_on", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Group Based On", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "\nBatch\nCourse", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "course", 
-   "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": "Course", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Course", 
-   "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": "batch", 
-   "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": "Batch", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student Batch Name", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_3", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "student_group_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": "Student Group 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": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_from": "course.course_name", 
-   "fieldname": "course_code", 
-   "fieldtype": "Read Only", 
-   "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": "Course Code", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "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": "max_strength", 
-   "fieldtype": "Int", 
-   "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": "Max Strength", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2018-11-04 03:38:52.525155", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Student Group Creation Tool Course", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "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/education/doctype/student_group_creation_tool_course/student_group_creation_tool_course.py b/erpnext/education/doctype/student_group_creation_tool_course/student_group_creation_tool_course.py
deleted file mode 100644
index 78e4541..0000000
--- a/erpnext/education/doctype/student_group_creation_tool_course/student_group_creation_tool_course.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class StudentGroupCreationToolCourse(Document):
-	pass
diff --git a/erpnext/education/doctype/student_group_instructor/__init__.py b/erpnext/education/doctype/student_group_instructor/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student_group_instructor/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student_group_instructor/student_group_instructor.json b/erpnext/education/doctype/student_group_instructor/student_group_instructor.json
deleted file mode 100644
index cb4e527..0000000
--- a/erpnext/education/doctype/student_group_instructor/student_group_instructor.json
+++ /dev/null
@@ -1,142 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2017-04-17 16:06:01.406768", 
- "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": "instructor", 
-   "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": "Instructor", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Instructor", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_2", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_from": "instructor.instructor_name", 
-   "fieldname": "instructor_name", 
-   "fieldtype": "Read Only", 
-   "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": "Instructor Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2018-11-04 03:39:02.413082", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Student Group Instructor", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0, 
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_group_instructor/student_group_instructor.py b/erpnext/education/doctype/student_group_instructor/student_group_instructor.py
deleted file mode 100644
index 05ef6fc..0000000
--- a/erpnext/education/doctype/student_group_instructor/student_group_instructor.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class StudentGroupInstructor(Document):
-	pass
diff --git a/erpnext/education/doctype/student_group_student/__init__.py b/erpnext/education/doctype/student_group_student/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student_group_student/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student_group_student/student_group_student.json b/erpnext/education/doctype/student_group_student/student_group_student.json
deleted file mode 100644
index d55db34..0000000
--- a/erpnext/education/doctype/student_group_student/student_group_student.json
+++ /dev/null
@@ -1,204 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2015-09-11 15:14:58.501830", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "student", 
-   "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": "Student", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "student_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": "Student Name", 
-   "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": "column_break_2", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "group_roll_number", 
-   "fieldtype": "Int", 
-   "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": "Group Roll Number", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "1", 
-   "fieldname": "active", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Active", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2018-11-04 03:38:22.896203", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Student Group Student", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0, 
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_group_student/student_group_student.py b/erpnext/education/doctype/student_group_student/student_group_student.py
deleted file mode 100644
index f9d00ab..0000000
--- a/erpnext/education/doctype/student_group_student/student_group_student.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class StudentGroupStudent(Document):
-	pass
diff --git a/erpnext/education/doctype/student_guardian/__init__.py b/erpnext/education/doctype/student_guardian/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student_guardian/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student_guardian/student_guardian.json b/erpnext/education/doctype/student_guardian/student_guardian.json
deleted file mode 100644
index 3f03a3d..0000000
--- a/erpnext/education/doctype/student_guardian/student_guardian.json
+++ /dev/null
@@ -1,141 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2016-09-01 14:28:39.174471", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "guardian", 
-   "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": "Guardian", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Guardian", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "guardian_name", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Guardian Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "relation", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Relation", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "\nMother\nFather\nOthers", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2018-11-04 03:38:14.211238", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Student Guardian", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0, 
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_guardian/student_guardian.py b/erpnext/education/doctype/student_guardian/student_guardian.py
deleted file mode 100644
index 0843acf..0000000
--- a/erpnext/education/doctype/student_guardian/student_guardian.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class StudentGuardian(Document):
-	pass
diff --git a/erpnext/education/doctype/student_language/__init__.py b/erpnext/education/doctype/student_language/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student_language/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student_language/student_language.js b/erpnext/education/doctype/student_language/student_language.js
deleted file mode 100644
index 6239ed1..0000000
--- a/erpnext/education/doctype/student_language/student_language.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Student Language', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/student_language/student_language.json b/erpnext/education/doctype/student_language/student_language.json
deleted file mode 100644
index fc53cd1..0000000
--- a/erpnext/education/doctype/student_language/student_language.json
+++ /dev/null
@@ -1,98 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "autoname": "field:language_name", 
- "beta": 0, 
- "creation": "2017-02-21 01:55:00.366273", 
- "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": "language_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": "Language 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": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 1
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2018-11-04 03:37:34.712397", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Student Language", 
- "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": "Academics User", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "title_field": "", 
- "track_changes": 1, 
- "track_seen": 0, 
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_language/student_language.py b/erpnext/education/doctype/student_language/student_language.py
deleted file mode 100644
index d578c9a..0000000
--- a/erpnext/education/doctype/student_language/student_language.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class StudentLanguage(Document):
-	pass
diff --git a/erpnext/education/doctype/student_language/test_student_language.py b/erpnext/education/doctype/student_language/test_student_language.py
deleted file mode 100644
index 718896c..0000000
--- a/erpnext/education/doctype/student_language/test_student_language.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-# test_records = frappe.get_test_records('Student Language')
-
-class TestStudentLanguage(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/student_leave_application/__init__.py b/erpnext/education/doctype/student_leave_application/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student_leave_application/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student_leave_application/student_leave_application.js b/erpnext/education/doctype/student_leave_application/student_leave_application.js
deleted file mode 100644
index 4746148..0000000
--- a/erpnext/education/doctype/student_leave_application/student_leave_application.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Student Leave Application', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/student_leave_application/student_leave_application.json b/erpnext/education/doctype/student_leave_application/student_leave_application.json
deleted file mode 100644
index 31b3da2..0000000
--- a/erpnext/education/doctype/student_leave_application/student_leave_application.json
+++ /dev/null
@@ -1,165 +0,0 @@
-{
- "actions": [],
- "autoname": "EDU-SLA-.YYYY.-.#####",
- "creation": "2016-11-28 15:38:54.793854",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "student",
-  "student_name",
-  "column_break_3",
-  "from_date",
-  "to_date",
-  "total_leave_days",
-  "section_break_5",
-  "attendance_based_on",
-  "student_group",
-  "course_schedule",
-  "mark_as_present",
-  "column_break_11",
-  "reason",
-  "amended_from"
- ],
- "fields": [
-  {
-   "fieldname": "student",
-   "fieldtype": "Link",
-   "in_global_search": 1,
-   "label": "Student",
-   "options": "Student",
-   "reqd": 1
-  },
-  {
-   "fetch_from": "student.title",
-   "fieldname": "student_name",
-   "fieldtype": "Read Only",
-   "in_global_search": 1,
-   "label": "Student Name",
-   "read_only": 1
-  },
-  {
-   "fieldname": "column_break_3",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "from_date",
-   "fieldtype": "Date",
-   "in_list_view": 1,
-   "in_standard_filter": 1,
-   "label": "From Date",
-   "reqd": 1
-  },
-  {
-   "fieldname": "to_date",
-   "fieldtype": "Date",
-   "in_list_view": 1,
-   "label": "To Date",
-   "reqd": 1
-  },
-  {
-   "default": "0",
-   "description": "Check this to mark the student as present in case the student is not attending the institute to participate or represent the institute in any event.\n\n",
-   "fieldname": "mark_as_present",
-   "fieldtype": "Check",
-   "label": "Mark as Present"
-  },
-  {
-   "fieldname": "section_break_5",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fieldname": "reason",
-   "fieldtype": "Text",
-   "label": "Reason"
-  },
-  {
-   "fieldname": "amended_from",
-   "fieldtype": "Link",
-   "label": "Amended From",
-   "no_copy": 1,
-   "options": "Student Leave Application",
-   "print_hide": 1,
-   "read_only": 1
-  },
-  {
-   "allow_in_quick_entry": 1,
-   "default": "Student Group",
-   "fieldname": "attendance_based_on",
-   "fieldtype": "Select",
-   "label": "Attendance Based On",
-   "options": "Student Group\nCourse Schedule"
-  },
-  {
-   "allow_in_quick_entry": 1,
-   "depends_on": "eval:doc.attendance_based_on === \"Student Group\";",
-   "fieldname": "student_group",
-   "fieldtype": "Link",
-   "label": "Student Group",
-   "mandatory_depends_on": "eval:doc.attendance_based_on === \"Student Group\";",
-   "options": "Student Group"
-  },
-  {
-   "allow_in_quick_entry": 1,
-   "depends_on": "eval:doc.attendance_based_on === \"Course Schedule\";",
-   "fieldname": "course_schedule",
-   "fieldtype": "Link",
-   "label": "Course Schedule",
-   "mandatory_depends_on": "eval:doc.attendance_based_on === \"Course Schedule\";",
-   "options": "Course Schedule"
-  },
-  {
-   "fieldname": "column_break_11",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "total_leave_days",
-   "fieldtype": "Float",
-   "label": "Total Leave Days",
-   "read_only": 1
-  }
- ],
- "is_submittable": 1,
- "links": [],
- "modified": "2020-09-21 18:10:24.440669",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Student Leave Application",
- "owner": "Administrator",
- "permissions": [
-  {
-   "amend": 1,
-   "cancel": 1,
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Instructor",
-   "submit": 1,
-   "write": 1
-  },
-  {
-   "amend": 1,
-   "cancel": 1,
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Academics User",
-   "share": 1,
-   "submit": 1,
-   "write": 1
-  }
- ],
- "quick_entry": 1,
- "restrict_to_domain": "Education",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "student_name"
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_leave_application/student_leave_application.py b/erpnext/education/doctype/student_leave_application/student_leave_application.py
deleted file mode 100644
index b1eda9a..0000000
--- a/erpnext/education/doctype/student_leave_application/student_leave_application.py
+++ /dev/null
@@ -1,121 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from datetime import timedelta
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import date_diff, flt, get_link_to_form, getdate
-
-from erpnext.education.doctype.student_attendance.student_attendance import get_holiday_list
-from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
-
-
-class StudentLeaveApplication(Document):
-	def validate(self):
-		self.validate_holiday_list()
-		self.validate_duplicate()
-		self.validate_from_to_dates('from_date', 'to_date')
-
-	def on_submit(self):
-		self.update_attendance()
-
-	def on_cancel(self):
-		self.cancel_attendance()
-
-	def validate_duplicate(self):
-		data = frappe.db.sql("""select name from `tabStudent Leave Application`
-			where
-				((%(from_date)s > from_date and %(from_date)s < to_date) or
-				(%(to_date)s > from_date and %(to_date)s < to_date) or
-				(%(from_date)s <= from_date and %(to_date)s >= to_date)) and
-				name != %(name)s and student = %(student)s and docstatus < 2
-		""", {
-			'from_date': self.from_date,
-			'to_date': self.to_date,
-			'student': self.student,
-			'name': self.name
-		}, as_dict=1)
-
-		if data:
-			link = get_link_to_form('Student Leave Application', data[0].name)
-			frappe.throw(_('Leave application {0} already exists against the student {1}')
-				.format(link, frappe.bold(self.student)), title=_('Duplicate Entry'))
-
-	def validate_holiday_list(self):
-		holiday_list = get_holiday_list()
-		self.total_leave_days = get_number_of_leave_days(self.from_date, self.to_date, holiday_list)
-
-	def update_attendance(self):
-		holiday_list = get_holiday_list()
-
-		for dt in daterange(getdate(self.from_date), getdate(self.to_date)):
-			date = dt.strftime('%Y-%m-%d')
-
-			if is_holiday(holiday_list, date):
-				continue
-
-			attendance = frappe.db.exists('Student Attendance', {
-				'student': self.student,
-				'date': date,
-				'docstatus': ('!=', 2)
-			})
-
-			status = 'Present' if self.mark_as_present else 'Absent'
-			if attendance:
-				# update existing attendance record
-				values = dict()
-				values['status'] = status
-				values['leave_application'] = self.name
-				frappe.db.set_value('Student Attendance', attendance, values)
-			else:
-				# make a new attendance record
-				doc = frappe.new_doc('Student Attendance')
-				doc.student = self.student
-				doc.student_name = self.student_name
-				doc.date = date
-				doc.leave_application = self.name
-				doc.status = status
-				if self.attendance_based_on == 'Student Group':
-					doc.student_group = self.student_group
-				else:
-					doc.course_schedule = self.course_schedule
-				doc.insert(ignore_permissions=True, ignore_mandatory=True)
-				doc.submit()
-
-	def cancel_attendance(self):
-		if self.docstatus == 2:
-			attendance = frappe.db.sql("""
-				SELECT name
-				FROM `tabStudent Attendance`
-				WHERE
-					student = %s and
-					(date between %s and %s) and
-					docstatus < 2
-			""", (self.student, self.from_date, self.to_date), as_dict=1)
-
-			for name in attendance:
-				frappe.db.set_value('Student Attendance', name, 'docstatus', 2)
-
-
-def daterange(start_date, end_date):
-	for n in range(int ((end_date - start_date).days)+1):
-		yield start_date + timedelta(n)
-
-def get_number_of_leave_days(from_date, to_date, holiday_list):
-	number_of_days = date_diff(to_date, from_date) + 1
-
-	holidays = frappe.db.sql("""
-		SELECT
-			COUNT(DISTINCT holiday_date)
-		FROM `tabHoliday` h1,`tabHoliday List` h2
-		WHERE
-			h1.parent = h2.name and
-			h1.holiday_date between %s and %s and
-			h2.name = %s""", (from_date, to_date, holiday_list))[0][0]
-
-	number_of_days = flt(number_of_days) - flt(holidays)
-
-	return number_of_days
diff --git a/erpnext/education/doctype/student_leave_application/student_leave_application_dashboard.py b/erpnext/education/doctype/student_leave_application/student_leave_application_dashboard.py
deleted file mode 100644
index d01790d..0000000
--- a/erpnext/education/doctype/student_leave_application/student_leave_application_dashboard.py
+++ /dev/null
@@ -1,9 +0,0 @@
-def get_data():
-	return {
-		'fieldname': 'leave_application',
-		'transactions': [
-			{
-				'items': ['Student Attendance']
-			}
-		]
-	}
diff --git a/erpnext/education/doctype/student_leave_application/test_student_leave_application.py b/erpnext/education/doctype/student_leave_application/test_student_leave_application.py
deleted file mode 100644
index 92e82c5..0000000
--- a/erpnext/education/doctype/student_leave_application/test_student_leave_application.py
+++ /dev/null
@@ -1,116 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-import frappe
-from frappe.utils import add_days, add_months, getdate
-
-from erpnext import get_default_company
-from erpnext.education.doctype.student.test_student import create_student
-from erpnext.education.doctype.student_group.test_student_group import get_random_group
-
-
-class TestStudentLeaveApplication(unittest.TestCase):
-	def setUp(self):
-		frappe.db.sql("""delete from `tabStudent Leave Application`""")
-		create_holiday_list()
-
-	def test_attendance_record_creation(self):
-		leave_application = create_leave_application()
-		attendance_record = frappe.db.exists('Student Attendance', {'leave_application': leave_application.name, 'status': 'Absent'})
-		self.assertTrue(attendance_record)
-
-		# mark as present
-		date = add_days(getdate(), -1)
-		leave_application = create_leave_application(date, date, 1)
-		attendance_record = frappe.db.exists('Student Attendance', {'leave_application': leave_application.name, 'status': 'Present'})
-		self.assertTrue(attendance_record)
-
-	def test_attendance_record_updated(self):
-		attendance = create_student_attendance()
-		create_leave_application()
-		self.assertEqual(frappe.db.get_value('Student Attendance', attendance.name, 'status'), 'Absent')
-
-	def test_attendance_record_cancellation(self):
-		leave_application = create_leave_application()
-		leave_application.cancel()
-		attendance_status = frappe.db.get_value('Student Attendance', {'leave_application': leave_application.name}, 'docstatus')
-		self.assertTrue(attendance_status, 2)
-
-	def test_holiday(self):
-		today = getdate()
-		leave_application = create_leave_application(from_date=today, to_date= add_days(today, 1), submit=0)
-
-		# holiday list validation
-		company = get_default_company() or frappe.get_all('Company')[0].name
-		frappe.db.set_value('Company', company, 'default_holiday_list', '')
-		self.assertRaises(frappe.ValidationError, leave_application.save)
-
-		frappe.db.set_value('Company', company, 'default_holiday_list', 'Test Holiday List for Student')
-		leave_application.save()
-
-		leave_application.reload()
-		self.assertEqual(leave_application.total_leave_days, 1)
-
-		# check no attendance record created for a holiday
-		leave_application.submit()
-		self.assertIsNone(frappe.db.exists('Student Attendance', {'leave_application': leave_application.name, 'date': add_days(today, 1)}))
-
-	def tearDown(self):
-		company = get_default_company() or frappe.get_all('Company')[0].name
-		frappe.db.set_value('Company', company, 'default_holiday_list', '_Test Holiday List')
-
-
-def create_leave_application(from_date=None, to_date=None, mark_as_present=0, submit=1):
-	student = get_student()
-
-	leave_application = frappe.new_doc('Student Leave Application')
-	leave_application.student = student.name
-	leave_application.attendance_based_on = 'Student Group'
-	leave_application.student_group = get_random_group().name
-	leave_application.from_date = from_date if from_date else getdate()
-	leave_application.to_date = from_date if from_date else getdate()
-	leave_application.mark_as_present = mark_as_present
-
-	if submit:
-		leave_application.insert()
-		leave_application.submit()
-
-	return leave_application
-
-def create_student_attendance(date=None, status=None):
-	student = get_student()
-	attendance = frappe.get_doc({
-		'doctype': 'Student Attendance',
-		'student': student.name,
-		'status': status if status else 'Present',
-		'date': date if date else getdate(),
-		'student_group': get_random_group().name
-	}).insert()
-	return attendance
-
-def get_student():
-	return create_student(dict(
-		email='test_student@gmail.com',
-		first_name='Test',
-		last_name='Student'
-	))
-
-def create_holiday_list():
-	holiday_list = 'Test Holiday List for Student'
-	today = getdate()
-	if not frappe.db.exists('Holiday List', holiday_list):
-		frappe.get_doc(dict(
-			doctype = 'Holiday List',
-			holiday_list_name = holiday_list,
-			from_date = add_months(today, -6),
-			to_date = add_months(today, 6),
-			holidays = [
-				dict(holiday_date=add_days(today, 1), description = 'Test')
-			]
-		)).insert()
-
-	company = get_default_company() or frappe.get_all('Company')[0].name
-	frappe.db.set_value('Company', company, 'default_holiday_list', holiday_list)
-	return holiday_list
diff --git a/erpnext/education/doctype/student_log/__init__.py b/erpnext/education/doctype/student_log/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student_log/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student_log/student_log.js b/erpnext/education/doctype/student_log/student_log.js
deleted file mode 100644
index 40e63e9..0000000
--- a/erpnext/education/doctype/student_log/student_log.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Student Log', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/student_log/student_log.json b/erpnext/education/doctype/student_log/student_log.json
deleted file mode 100644
index cdd6aaa..0000000
--- a/erpnext/education/doctype/student_log/student_log.json
+++ /dev/null
@@ -1,423 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "autoname": "EDU-SLOG-.YYYY.-.#####", 
- "beta": 0, 
- "creation": "2016-07-29 03:27:22.451772", 
- "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": "student", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 1, 
-   "label": "Student", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_from": "student.title", 
-   "fieldname": "student_name", 
-   "fieldtype": "Read Only", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Student Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "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": "type", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 1, 
-   "label": "Type", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "General\nAcademic\nMedical\nAchievement", 
-   "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": "date", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_3", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "academic_year", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Academic Year", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Academic Year", 
-   "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": "academic_term", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Academic Term", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Academic Term", 
-   "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": "program", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Program", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Program", 
-   "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": "student_batch", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Student Batch", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student Batch Name", 
-   "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": "section_break_5", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "log", 
-   "fieldtype": "Text Editor", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Log", 
-   "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": "2018-08-21 16:15:47.027645", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Student Log", 
- "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": "Academics User", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "title_field": "student_name", 
- "track_changes": 0, 
- "track_seen": 1, 
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_log/student_log.py b/erpnext/education/doctype/student_log/student_log.py
deleted file mode 100644
index b95f34e..0000000
--- a/erpnext/education/doctype/student_log/student_log.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class StudentLog(Document):
-	pass
diff --git a/erpnext/education/doctype/student_log/test_student_log.py b/erpnext/education/doctype/student_log/test_student_log.py
deleted file mode 100644
index 91fdb3c..0000000
--- a/erpnext/education/doctype/student_log/test_student_log.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-# test_records = frappe.get_test_records('Student Log')
-
-class TestStudentLog(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/student_report_generation_tool/__init__.py b/erpnext/education/doctype/student_report_generation_tool/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student_report_generation_tool/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.html b/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.html
deleted file mode 100644
index a9e84e6..0000000
--- a/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.html
+++ /dev/null
@@ -1,371 +0,0 @@
-<style>
-		@media screen {
-		.print-format-gutter {
-			background-color: #ddd;
-			padding: 15px 0px;
-		}
-		.print-format {
-			background-color: white;
-			box-shadow: 0px 0px 9px rgba(0,0,0,0.5);
-			max-width: 8.3in;
-			min-height: 11.69in;
-			padding: 0.75in;
-			margin: auto;
-		}
-
-		.print-format.landscape {
-			max-width: 11.69in;
-			padding: 0.2in;
-		}
-
-		.page-break {
-			padding: 30px 0px;
-			border-bottom: 1px dashed #888;
-		}
-
-		.page-break:first-child {
-			padding-top: 0px;
-		}
-
-		.page-break:last-child {
-			border-bottom: 0px;
-		}
-
-		/* mozilla hack for images in table */
-		body:last-child .print-format td img {
-			width: 100% !important;
-		}
-
-		@media(max-width: 767px) {
-			.print-format {
-				padding: 0.2in;
-			}
-		}
-	}
-
-	@media print {
-		.print-format p {
-			margin-left: 1px;
-			margin-right: 1px;
-		}
-	}
-
-	.data-field {
-		margin-top: 5px;
-		margin-bottom: 5px;
-	}
-
-	.data-field .value {
-		word-wrap: break-word;
-	}
-
-	.important .value {
-		font-size: 120%;
-		font-weight: bold;
-	}
-
-	.important label {
-		line-height: 1.8;
-		margin: 0px;
-	}
-
-	.table {
-		margin: 20px 0px;
-	}
-
-	.square-image {
-		width: 100%;
-		height: 0;
-		padding: 50% 0;
-		background-size: contain;
-		/*background-size: cover;*/
-		background-repeat: no-repeat !important;
-		background-position: center center;
-		border-radius: 4px;
-	}
-
-	.print-item-image {
-		object-fit: contain;
-	}
-
-	.pdf-variables,
-	.pdf-variable,
-	.visible-pdf {
-		display: none !important;
-	}
-
-	.print-format {
-		font-size: 9pt;
-		font-family: "Helvetica Neue", Helvetica, Arial, "Open Sans", sans-serif;
-		-webkit-print-color-adjust:exact;
-	}
-
-	.page-break {
-		page-break-after: always;
-	}
-
-	.print-heading {
-		border-bottom: 1px solid #aaa;
-		margin-bottom: 10px;
-	}
-
-	.print-heading h2 {
-		margin: 0px;
-	}
-	.print-heading h4 {
-		margin-top: 5px;
-	}
-
-	table.no-border, table.no-border td {
-		border: 0px;
-	}
-
-	.print-format label {
-		/* wkhtmltopdf breaks label into multiple lines when it is inline-block */
-		display: block;
-	}
-
-	.print-format img {
-		max-width: 100%;
-	}
-
-	.print-format table td > .primary:first-child {
-		font-weight: bold;
-	}
-
-	.print-format td, .print-format th {
-		vertical-align: top !important;
-		padding: 6px !important;
-	}
-
-	.print-format p {
-		margin: 3px 0px 3px;
-	}
-
-	table td div {
-
-		/* needed to avoid partial cutting of text between page break in wkhtmltopdf */
-		page-break-inside: avoid !important;
-
-	}
-
-	/* hack for webkit specific browser */
-	@media (-webkit-min-device-pixel-ratio:0) {
-		thead, tfoot { display: table-row-group; }
-	}
-
-	[document-status] {
-		margin-bottom: 5mm;
-	}
-
-	.signature-img {
-		background: #fff;
-		border-radius: 3px;
-		margin-top: 5px;
-		max-height: 150px;
-	}
-
-	.print-heading {
-		text-align: right;
-		text-transform: uppercase;
-		color: #666;
-		padding-bottom: 20px;
-		margin-bottom: 20px;
-		border-bottom: 1px solid #d1d8dd;
-	}
-
-	.print-heading h2 {
-		font-size: 24px;
-	}
-
-	.print-format th {
-		background-color: #eee !important;
-		border-bottom: 0px !important;
-	}
-
-	/* modern format: for-test */
-
-	.pbi_avoid {
-		page-break-inside: avoid !important;
-	}
-	.pb_before {
-		page-break-before: always !important;
-	}
-</style>
-
-
-
-<div class="page-break">
-
-	<div id="header-html" class="hidden-pdf">
-
-		{% if letterhead and add_letterhead %}
-			<div class="">{{ letterhead }}</div>
-		{% endif %}
-		<div class="print-heading">
-			<h2>{{ _("Student Report Card") }}<br>
-				<small>{{ doc.student_name }}</small>
-			</h2>
-		</div>
-	</div>
-
-	<div class="row section-break">
-		<div class="col-xs-6 column-break">
-			<div class="row data-field">
-				<div class="col-xs-5">
-					<label>{{ _("Student ID: ") }}</label>
-				</div>
-				<div class="col-xs-7">
-					{{ doc.students[0] }}
-				</div>
-			</div>
-
-			<div class="row data-field">
-				<div class="col-xs-5">
-					<label>{{ _("Student Name: ") }}</label>
-				</div>
-				<div class="col-xs-7">
-					{{ doc.student_name }}
-				</div>
-			</div>
-
-			<div class="row data-field">
-				<div class="col-xs-5">
-					<label>{{ _("Program: ") }}</label>
-				</div>
-				<div class="col-xs-7">
-					{{ doc.program }}
-				</div>
-			</div>
-
-			<div class="row data-field">
-				<div class="col-xs-5">
-					<label>{{ _("Batch: ") }}</label>
-				</div>
-				<div class="col-xs-7">
-					{{ doc.student_batch }}
-				</div>
-			</div>
-		</div>
-
-		<div class="col-xs-6 column-break">
-			<div class="row data-field">
-				<div class="col-xs-5">
-					<label>{{ _("Academic Year: ") }}</label>
-				</div>
-				<div class="col-xs-7">
-					{{ doc.academic_year }}
-				</div>
-			</div>
-
-			{% if doc.academic_term %}
-			<div class="row data-field">
-				<div class="col-xs-5">
-					<label>{{ _("Academic Term: ") }}</label>
-				</div>
-				<div class="col-xs-7">
-					{{ doc.academic_term }}
-				</div>
-			</div>
-			{% endif %}
-
-			<div class="row data-field">
-				<div class="col-xs-5">
-					<label>{{ _("Assessment Group: ") }}</label>
-				</div>
-				<div class="col-xs-7">
-					{{ doc.assessment_group }}
-				</div>
-			</div>
-		</div>
-	</div>
-
-
-{% if doc.show_marks | int %}
-	{% set result_data = 'score' %}
-{% else %}
-	{% set result_data = 'grade' %}
-{% endif %}
-
-
-{% for course in courses %}
-
-<div class="row section-break pbi_avoid">
-	<div class="col-xs-12 column-break">
-
-		<div>
-		<table class="table table-bordered table-condensed">
-			<caption>
-				<div class="row">
-					<div class="col-xs-1">
-						<label>{{ _("Course: ") }}</label>
-					</div>
-					<div class="col-xs-11">
-						{{ course }} ({{ frappe.db.get_value("Course", course, "course_name") }})
-					</div>
-				</div>
-			</caption>
-			<thead>
-				<tr>
-					<th style="width: {{ 650//(assessment_groups|length + 1) }}px">{{ _("Assessment Criteria") }}</th>
-					{% for assessment_group in assessment_groups %}
-					<th> {{ assessment_group }}</th>
-					{% endfor %}
-				</tr>
-			</thead>
-
-			<tbody>
-				{% for criteria in course_criteria[course] %}
-				<tr>
-					<td>{{ criteria }}</td>
-					{% for assessment_group in assessment_groups %}
-						{% if (assessment_result.get(course) and assessment_result.get(course).get(assessment_group) and assessment_result.get(course).get(assessment_group).get(criteria)) %}
-							<td>
-								{{ assessment_result.get(course).get(assessment_group).get(criteria).get(result_data) }}
-								{% if result_data == 'score' %}
-								({{ assessment_result.get(course).get(assessment_group).get(criteria).get('maximum_score') }})
-								{% endif %}
-							</td>
-						{% else %}
-							<td></td>
-						{% endif %}
-					{% endfor %}
-				</tr>
-				{% endfor %}
-			</tbody>
-
-		</table>
-		</div>
-	</div>
-</div>
-
-{% endfor %}
-
-<br>
-
-<div class="row section-break pbi_avoid">
-	<div class="col-xs-6 column-break">
-		<h4>{{ _("Student Attendance")}}</h4> <br>
-		<div>
-			Present {{ doc.attendance.get("Present") if doc.attendance.get("Present") != None else '0' }} days
-			out of {{ doc.attendance.get("Present") + doc.attendance.get("Absent") }}
-		</div>
-	</div>
-
-	<div class="col-xs-6 column-break">
-		<h4>{{ _("Parents Teacher Meeting Attendance")}}</h4> <br>
-		<div>
-			Present {{ doc.parents_attendance if doc.parents_attendance != None else '0' }}
-			out of {{ doc.parents_meeting if doc.parents_meeting != None else '0' }}
-		</div>
-	</div>
-</div>
-
-{% if doc.assessment_terms %}
-<div class="row section-break pb_before">
-	<div class="col-xs-12">
-		<p> {{ doc.assessment_terms }} </p>
-	</div>
-</div>
-{% endif %}
-</div>
diff --git a/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.js b/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.js
deleted file mode 100644
index 565074d..0000000
--- a/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.js
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Student Report Generation Tool', {
-	onload: function(frm) {
-		frm.set_query("academic_term",function(){
-			return{
-				"filters":{
-					"academic_year": frm.doc.academic_year
-				}
-			};
-		});
-		frm.set_query("assessment_group", function() {
-			return{
-				filters: {
-					"is_group": 1
-				}
-			};
-		});
-	},
-
-	refresh: function(frm) {
-		frm.disable_save();
-		frm.page.clear_indicator();
-		frm.page.set_primary_action(__('Print Report Card'), () => {
-			let url = "/api/method/erpnext.education.doctype.student_report_generation_tool.student_report_generation_tool.preview_report_card";
-			open_url_post(url, {"doc": frm.doc}, true);
-		});
-	},
-
-	student: function(frm) {
-		if (frm.doc.student) {
-			frappe.call({
-				method:"erpnext.education.api.get_current_enrollment",
-				args: {
-					"student": frm.doc.student,
-					"academic_year": frm.doc.academic_year
-				},
-				callback: function(r) {
-					if(r){
-						$.each(r.message, function(i, d) {
-							if (frm.fields_dict.hasOwnProperty(i)) {
-								frm.set_value(i, d);
-							}
-						});
-					}
-				}
-			});
-		}
-	},
-
-	terms: function(frm) {
-		if(frm.doc.terms) {
-			return frappe.call({
-				method: 'erpnext.setup.doctype.terms_and_conditions.terms_and_conditions.get_terms_and_conditions',
-				args: {
-					template_name: frm.doc.terms,
-					doc: frm.doc
-				},
-				callback: function(r) {
-					frm.set_value("assessment_terms", r.message);
-				}
-			});
-		}
-	}
-});
diff --git a/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.json b/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.json
deleted file mode 100644
index a328299..0000000
--- a/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.json
+++ /dev/null
@@ -1,618 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2018-01-15 15:36:32.830069", 
- "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": "student", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Student", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "student_name", 
-   "fieldtype": "Read Only", 
-   "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": "Student 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": "program", 
-   "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": "Program", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Program", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "student_batch", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Batch", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student Batch Name", 
-   "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": "include_all_assessment", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Include All Assessment Group", 
-   "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": "show_marks", 
-   "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": "Show Marks", 
-   "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": "1", 
-   "fieldname": "add_letterhead", 
-   "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": "Add letterhead", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_3", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "assessment_group", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Assessment Group", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Assessment Group", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "academic_year", 
-   "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": "Academic Year", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Academic Year", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "academic_term", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Academic Term", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Academic Term", 
-   "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": "add_letterhead", 
-   "fieldname": "letter_head", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Letter Head", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Letter Head", 
-   "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": "section_break_5", 
-   "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": "Print Section", 
-   "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": "parents_meeting", 
-   "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": "Total Parents Teacher Meeting", 
-   "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": "parents_attendance", 
-   "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": "Attended by Parents", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "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": "terms", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Terms", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Terms and Conditions", 
-   "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": "assessment_terms", 
-   "fieldtype": "Text Editor", 
-   "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": "Assessment Terms", 
-   "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": 1, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 1, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2018-11-04 03:38:42.970869", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Student Report Generation Tool", 
- "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
-  }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "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/education/doctype/student_report_generation_tool/student_report_generation_tool.py b/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.py
deleted file mode 100644
index 43802ab..0000000
--- a/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import json
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils.pdf import get_pdf
-
-from erpnext.education.report.course_wise_assessment_report.course_wise_assessment_report import (
-	get_child_assessment_groups,
-	get_formatted_result,
-)
-
-
-class StudentReportGenerationTool(Document):
-	pass
-
-
-@frappe.whitelist()
-def preview_report_card(doc):
-	doc = frappe._dict(json.loads(doc))
-	doc.students = [doc.student]
-	if not (doc.student_name and doc.student_batch):
-		program_enrollment = frappe.get_all("Program Enrollment", fields=["student_batch_name", "student_name"],
-			filters={"student": doc.student, "docstatus": ('!=', 2), "academic_year": doc.academic_year})
-		if program_enrollment:
-			doc.batch = program_enrollment[0].student_batch_name
-			doc.student_name = program_enrollment[0].student_name
-
-	# get the assessment result of the selected student
-	values = get_formatted_result(doc, get_course=True, get_all_assessment_groups=doc.include_all_assessment)
-	assessment_result = values.get("assessment_result").get(doc.student)
-	courses = values.get("course_dict")
-	course_criteria = get_courses_criteria(courses)
-
-	# get the assessment group as per the user selection
-	if doc.include_all_assessment:
-		assessment_groups = get_child_assessment_groups(doc.assessment_group)
-	else:
-		assessment_groups = [doc.assessment_group]
-
-	# get the attendance of the student for that peroid of time.
-	doc.attendance = get_attendance_count(doc.students[0], doc.academic_year, doc.academic_term)
-
-	template = "erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.html"
-	base_template_path = "frappe/www/printview.html"
-
-	from frappe.www.printview import get_letter_head
-	letterhead = get_letter_head(frappe._dict({"letter_head": doc.letterhead}), not doc.add_letterhead)
-
-	html = frappe.render_template(template,
-		{
-			"doc": doc,
-			"assessment_result": assessment_result,
-			"courses": courses,
-			"assessment_groups": assessment_groups,
-			"course_criteria": course_criteria,
-			"letterhead": letterhead and letterhead.get('content', None),
-			"add_letterhead": doc.add_letterhead if doc.add_letterhead else 0
-		})
-	final_template = frappe.render_template(base_template_path, {"body": html, "title": "Report Card"})
-
-	frappe.response.filename = "Report Card " + doc.students[0] + ".pdf"
-	frappe.response.filecontent = get_pdf(final_template)
-	frappe.response.type = "download"
-
-
-def get_courses_criteria(courses):
-	course_criteria = frappe._dict()
-	for course in courses:
-		course_criteria[course] = [d.assessment_criteria for d in frappe.get_all("Course Assessment Criteria",
-			fields=["assessment_criteria"], filters={"parent": course})]
-	return course_criteria
-
-
-def get_attendance_count(student, academic_year, academic_term=None):
-	if academic_year:
-		from_date, to_date = frappe.db.get_value("Academic Year", academic_year, ["year_start_date", "year_end_date"])
-	elif academic_term:
-		from_date, to_date = frappe.db.get_value("Academic Term", academic_term, ["term_start_date", "term_end_date"])
-	if from_date and to_date:
-		attendance = dict(frappe.db.sql('''select status, count(student) as no_of_days
-			from `tabStudent Attendance` where student = %s and docstatus = 1
-			and date between %s and %s group by status''',
-			(student, from_date, to_date)))
-		if "Absent" not in attendance.keys():
-			attendance["Absent"] = 0
-		if "Present" not in attendance.keys():
-			attendance["Present"] = 0
-		return attendance
-	else:
-		frappe.throw(_("Provide the academic year and set the starting and ending date."))
diff --git a/erpnext/education/doctype/student_report_generation_tool/test_student_report_generation_tool.py b/erpnext/education/doctype/student_report_generation_tool/test_student_report_generation_tool.py
deleted file mode 100644
index e37881f..0000000
--- a/erpnext/education/doctype/student_report_generation_tool/test_student_report_generation_tool.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestStudentReportGenerationTool(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/student_sibling/__init__.py b/erpnext/education/doctype/student_sibling/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student_sibling/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student_sibling/student_sibling.json b/erpnext/education/doctype/student_sibling/student_sibling.json
deleted file mode 100644
index f67fe79..0000000
--- a/erpnext/education/doctype/student_sibling/student_sibling.json
+++ /dev/null
@@ -1,306 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2016-09-01 14:41:23.824083", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 0, 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "studying_in_same_institute", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Studying in Same Institute", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "NO\nYES", 
-   "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": 3, 
-   "fieldname": "full_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": "Full Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 1, 
-   "fieldname": "gender", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Gender", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "\nMale\nFemale", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "", 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_4", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:doc.studying_in_same_institute == \"YES\"", 
-   "fieldname": "student", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Student ID", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 2, 
-   "depends_on": "eval:doc.studying_in_same_institute == \"NO\"", 
-   "fieldname": "institution", 
-   "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": "Institution", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 2, 
-   "fieldname": "program", 
-   "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": "Program", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 2, 
-   "fieldname": "date_of_birth", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Date of Birth", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2018-11-04 03:37:25.881487", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Student Sibling", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0, 
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_sibling/student_sibling.py b/erpnext/education/doctype/student_sibling/student_sibling.py
deleted file mode 100644
index 9ee0667..0000000
--- a/erpnext/education/doctype/student_sibling/student_sibling.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class StudentSibling(Document):
-	pass
diff --git a/erpnext/education/doctype/student_siblings/__init__.py b/erpnext/education/doctype/student_siblings/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/student_siblings/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/student_siblings/student_siblings.json b/erpnext/education/doctype/student_siblings/student_siblings.json
deleted file mode 100644
index 9d91ad2..0000000
--- a/erpnext/education/doctype/student_siblings/student_siblings.json
+++ /dev/null
@@ -1,141 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2016-09-01 14:41:23.824083", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "name1", 
-   "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": "Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "gender", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Gender", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "\nMale\nFemale", 
-   "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": "date_of_birth", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Date of Birth", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2018-11-04 03:37:46.485218", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Student Siblings", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0, 
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/student_siblings/student_siblings.py b/erpnext/education/doctype/student_siblings/student_siblings.py
deleted file mode 100644
index ee89f4f..0000000
--- a/erpnext/education/doctype/student_siblings/student_siblings.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class StudentSiblings(Document):
-	pass
diff --git a/erpnext/education/doctype/topic/__init__.py b/erpnext/education/doctype/topic/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/topic/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/topic/test_topic.py b/erpnext/education/doctype/topic/test_topic.py
deleted file mode 100644
index d1d664b..0000000
--- a/erpnext/education/doctype/topic/test_topic.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-import frappe
-
-
-class TestTopic(unittest.TestCase):
-	def setUp(self):
-		make_topic_and_linked_content("_Test Topic 1", [{"type":"Article", "name": "_Test Article 1"}])
-
-	def test_get_contents(self):
-		topic = frappe.get_doc("Topic", "_Test Topic 1")
-		contents = topic.get_contents()
-		self.assertEqual(contents[0].doctype, "Article")
-		self.assertEqual(contents[0].name, "_Test Article 1")
-		frappe.db.rollback()
-
-def make_topic(name):
-	try:
-		topic = frappe.get_doc("Topic", name)
-	except frappe.DoesNotExistError:
-		topic = frappe.get_doc({
-			"doctype": "Topic",
-			"topic_name": name,
-			"topic_code": name,
-		}).insert()
-	return topic.name
-
-def make_topic_and_linked_content(topic_name, content_dict_list):
-	try:
-		topic = frappe.get_doc("Topic", topic_name)
-	except frappe.DoesNotExistError:
-		make_topic(topic_name)
-		topic = frappe.get_doc("Topic", topic_name)
-	content_list = [make_content(content['type'], content['name']) for content in content_dict_list]
-	for content in content_list:
-		topic.append("topic_content", {"content": content.title, "content_type": content.doctype})
-	topic.save()
-	return topic
-
-
-def make_content(type, name):
-	try:
-		content = frappe.get_doc(type, name)
-	except frappe.DoesNotExistError:
-		content = frappe.get_doc({"doctype": type, "title": name}).insert()
-	return content
diff --git a/erpnext/education/doctype/topic/topic.js b/erpnext/education/doctype/topic/topic.js
deleted file mode 100644
index 0c903c5..0000000
--- a/erpnext/education/doctype/topic/topic.js
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Topic', {
-	refresh: function(frm) {
-		if (!cur_frm.doc.__islocal) {
-			frm.add_custom_button(__('Add to Courses'), function() {
-				frm.trigger('add_topic_to_courses');
-			}, __('Action'));
-		}
-	},
-
-	add_topic_to_courses: function(frm) {
-		get_courses_without_topic(frm.doc.name).then(r => {
-			if (r.message.length) {
-				frappe.prompt([
-					{
-						fieldname: 'courses',
-						label: __('Courses'),
-						fieldtype: 'MultiSelectPills',
-						get_data: function() {
-							return r.message;
-						}
-					}
-				],
-				function(data) {
-					frappe.call({
-						method: 'erpnext.education.doctype.topic.topic.add_topic_to_courses',
-						args: {
-							'topic': frm.doc.name,
-							'courses': data.courses
-						},
-						callback: function(r) {
-							if (!r.exc) {
-								frm.reload_doc();
-							}
-						},
-						freeze: true,
-						freeze_message: __('...Adding Topic to Courses')
-					});
-				}, __('Add Topic to Courses'), __('Add'));
-			} else {
-				frappe.msgprint(__('This topic is already added to the existing courses'));
-			}
-		});
-	}
-});
-
-let get_courses_without_topic = function(topic) {
-	return frappe.call({
-		type: 'GET',
-		method: 'erpnext.education.doctype.topic.topic.get_courses_without_topic',
-		args: {'topic': topic}
-	});
-};
diff --git a/erpnext/education/doctype/topic/topic.json b/erpnext/education/doctype/topic/topic.json
deleted file mode 100644
index 305458b..0000000
--- a/erpnext/education/doctype/topic/topic.json
+++ /dev/null
@@ -1,90 +0,0 @@
-{
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:topic_name",
- "creation": "2018-12-12 11:37:39.917760",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "topic_name",
-  "topic_content",
-  "description",
-  "hero_image"
- ],
- "fields": [
-  {
-   "fieldname": "topic_name",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Name",
-   "reqd": 1,
-   "unique": 1
-  },
-  {
-   "fieldname": "topic_content",
-   "fieldtype": "Table",
-   "label": "Topic Content",
-   "options": "Topic Content"
-  },
-  {
-   "fieldname": "hero_image",
-   "fieldtype": "Attach Image",
-   "hidden": 1,
-   "label": "Hero Image"
-  },
-  {
-   "fieldname": "description",
-   "fieldtype": "Small Text",
-   "label": "Description"
-  }
- ],
- "image_field": "hero_image",
- "modified": "2019-06-12 12:34:49.911300",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Topic",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "System Manager",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Administrator",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Instructor",
-   "share": 1,
-   "write": 1
-  }
- ],
- "quick_entry": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/topic/topic.py b/erpnext/education/doctype/topic/topic.py
deleted file mode 100644
index 146f574..0000000
--- a/erpnext/education/doctype/topic/topic.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import json
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-
-
-class Topic(Document):
-	def get_contents(self):
-		try:
-			topic_content_list = self.topic_content
-			content_data = [frappe.get_doc(topic_content.content_type, topic_content.content) for topic_content in topic_content_list]
-		except Exception as e:
-			frappe.log_error(frappe.get_traceback())
-			return None
-		return content_data
-
-@frappe.whitelist()
-def get_courses_without_topic(topic):
-	data = []
-	for entry in frappe.db.get_all('Course'):
-		course = frappe.get_doc('Course', entry.name)
-		topics = [t.topic for t in course.topics]
-		if not topics or topic not in topics:
-			data.append(course.name)
-	return data
-
-@frappe.whitelist()
-def add_topic_to_courses(topic, courses, mandatory=False):
-	courses = json.loads(courses)
-	for entry in courses:
-		course = frappe.get_doc('Course', entry)
-		course.append('topics', {
-			'topic': topic,
-			'topic_name': topic
-		})
-		course.flags.ignore_mandatory = True
-		course.save()
-	frappe.db.commit()
-	frappe.msgprint(_('Topic {0} has been added to all the selected courses successfully.').format(frappe.bold(topic)),
-		title=_('Courses updated'), indicator='green')
-
-@frappe.whitelist()
-def add_content_to_topics(content_type, content, topics):
-	topics = json.loads(topics)
-	for entry in topics:
-		topic = frappe.get_doc('Topic', entry)
-		topic.append('topic_content', {
-			'content_type': content_type,
-			'content': content,
-		})
-		topic.flags.ignore_mandatory = True
-		topic.save()
-	frappe.db.commit()
-	frappe.msgprint(_('{0} {1} has been added to all the selected topics successfully.').format(content_type, frappe.bold(content)),
-		title=_('Topics updated'), indicator='green')
diff --git a/erpnext/education/doctype/topic_content/__init__.py b/erpnext/education/doctype/topic_content/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/doctype/topic_content/__init__.py
+++ /dev/null
diff --git a/erpnext/education/doctype/topic_content/test_topic_content.py b/erpnext/education/doctype/topic_content/test_topic_content.py
deleted file mode 100644
index 56bb409..0000000
--- a/erpnext/education/doctype/topic_content/test_topic_content.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestTopicContent(unittest.TestCase):
-	pass
diff --git a/erpnext/education/doctype/topic_content/topic_content.js b/erpnext/education/doctype/topic_content/topic_content.js
deleted file mode 100644
index 9cda0ca..0000000
--- a/erpnext/education/doctype/topic_content/topic_content.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('Topic Content', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/education/doctype/topic_content/topic_content.json b/erpnext/education/doctype/topic_content/topic_content.json
deleted file mode 100644
index 444fd1d..0000000
--- a/erpnext/education/doctype/topic_content/topic_content.json
+++ /dev/null
@@ -1,44 +0,0 @@
-{
- "creation": "2018-12-12 11:42:57.987434",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "content_type",
-  "column_break_2",
-  "content"
- ],
- "fields": [
-  {
-   "fieldname": "content_type",
-   "fieldtype": "Select",
-   "in_list_view": 1,
-   "label": "Content Type",
-   "options": "\nArticle\nVideo\nQuiz",
-   "reqd": 1
-  },
-  {
-   "fieldname": "column_break_2",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "content",
-   "fieldtype": "Dynamic Link",
-   "in_list_view": 1,
-   "label": "Content",
-   "options": "content_type",
-   "reqd": 1
-  }
- ],
- "istable": 1,
- "modified": "2019-05-14 11:12:49.153771",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Topic Content",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/topic_content/topic_content.py b/erpnext/education/doctype/topic_content/topic_content.py
deleted file mode 100644
index 88d0eee..0000000
--- a/erpnext/education/doctype/topic_content/topic_content.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class TopicContent(Document):
-	pass
diff --git a/erpnext/education/education_dashboard/education/education.json b/erpnext/education/education_dashboard/education/education.json
deleted file mode 100644
index 41d3375..0000000
--- a/erpnext/education/education_dashboard/education/education.json
+++ /dev/null
@@ -1,62 +0,0 @@
-{
- "cards": [
-  {
-   "card": "Total Students"
-  },
-  {
-   "card": "Total Instructors"
-  },
-  {
-   "card": "Program Enrollments"
-  },
-  {
-   "card": "Student Applicants to Review"
-  }
- ],
- "charts": [
-  {
-   "chart": "Program Enrollments",
-   "width": "Full"
-  },
-  {
-   "chart": "Program wise Enrollment",
-   "width": "Half"
-  },
-  {
-   "chart": "Course wise Enrollment",
-   "width": "Half"
-  },
-  {
-   "chart": "Course wise Student Count",
-   "width": "Half"
-  },
-  {
-   "chart": "Student Category wise Program Enrollments",
-   "width": "Half"
-  },
-  {
-   "chart": "Student Gender Diversity Ratio",
-   "width": "Half"
-  },
-  {
-   "chart": "Instructor Gender Diversity Ratio",
-   "width": "Half"
-  },
-  {
-   "chart": "Program wise Fee Collection",
-   "width": "Full"
-  }
- ],
- "creation": "2020-07-22 18:51:02.195762",
- "dashboard_name": "Education",
- "docstatus": 0,
- "doctype": "Dashboard",
- "idx": 0,
- "is_default": 0,
- "is_standard": 1,
- "modified": "2020-08-05 16:22:17.428101",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Education",
- "owner": "Administrator"
-}
\ No newline at end of file
diff --git a/erpnext/education/module_onboarding/education/education.json b/erpnext/education/module_onboarding/education/education.json
deleted file mode 100644
index e5f0fec..0000000
--- a/erpnext/education/module_onboarding/education/education.json
+++ /dev/null
@@ -1,50 +0,0 @@
-{
- "allow_roles": [
-  {
-   "role": "Education Manager"
-  }
- ],
- "creation": "2020-07-27 19:02:49.561391",
- "docstatus": 0,
- "doctype": "Module Onboarding",
- "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/education",
- "idx": 0,
- "is_complete": 0,
- "modified": "2020-07-27 21:10:46.722961",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Education",
- "owner": "Administrator",
- "steps": [
-  {
-   "step": "Create a Student"
-  },
-  {
-   "step": "Create an Instructor"
-  },
-  {
-   "step": "Introduction to Program and Courses"
-  },
-  {
-   "step": "Create a Topic"
-  },
-  {
-   "step": "Create a Course"
-  },
-  {
-   "step": "Create a Program"
-  },
-  {
-   "step": "Enroll a Student in a Program"
-  },
-  {
-   "step": "Introduction to Student Group"
-  },
-  {
-   "step": "Introduction to Student Attendance"
-  }
- ],
- "subtitle": "Students, Instructors, Programs and more.",
- "success_message": "The Education Module is all set up!",
- "title": "Let's Set Up the Education Module."
-}
diff --git a/erpnext/education/number_card/program_enrollments/program_enrollments.json b/erpnext/education/number_card/program_enrollments/program_enrollments.json
deleted file mode 100644
index 5847679..0000000
--- a/erpnext/education/number_card/program_enrollments/program_enrollments.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "aggregate_function_based_on": "",
- "creation": "2020-07-27 18:26:27.005186",
- "docstatus": 0,
- "doctype": "Number Card",
- "document_type": "Program Enrollment",
- "dynamic_filters_json": "[]",
- "filters_json": "[[\"Program Enrollment\",\"docstatus\",\"=\",\"1\",false],[\"Program Enrollment\",\"enrollment_date\",\"Timespan\",\"this year\",false]]",
- "function": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "label": "Program Enrollments",
- "modified": "2020-07-27 18:26:32.512624",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Program Enrollments",
- "owner": "Administrator",
- "report_function": "Sum",
- "show_percentage_stats": 1,
- "stats_time_interval": "Yearly",
- "type": "Document Type"
-}
\ No newline at end of file
diff --git a/erpnext/education/number_card/student_applicants_to_review/student_applicants_to_review.json b/erpnext/education/number_card/student_applicants_to_review/student_applicants_to_review.json
deleted file mode 100644
index 258667a..0000000
--- a/erpnext/education/number_card/student_applicants_to_review/student_applicants_to_review.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "aggregate_function_based_on": "",
- "creation": "2020-07-27 18:42:33.366862",
- "docstatus": 0,
- "doctype": "Number Card",
- "document_type": "Student Applicant",
- "dynamic_filters_json": "[]",
- "filters_json": "[[\"Student Applicant\",\"application_status\",\"=\",\"Applied\",false]]",
- "function": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "label": "Student Applicants to Review",
- "modified": "2020-07-27 18:42:42.739710",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Student Applicants to Review",
- "owner": "Administrator",
- "report_function": "Sum",
- "show_percentage_stats": 1,
- "stats_time_interval": "Monthly",
- "type": "Document Type"
-}
\ No newline at end of file
diff --git a/erpnext/education/number_card/total_instructors/total_instructors.json b/erpnext/education/number_card/total_instructors/total_instructors.json
deleted file mode 100644
index b8d3cc0..0000000
--- a/erpnext/education/number_card/total_instructors/total_instructors.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "aggregate_function_based_on": "",
- "creation": "2020-07-23 14:19:38.423190",
- "docstatus": 0,
- "doctype": "Number Card",
- "document_type": "Instructor",
- "dynamic_filters_json": "[]",
- "filters_json": "[[\"Instructor\",\"status\",\"=\",\"Active\",false]]",
- "function": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "label": "Total Instructors",
- "modified": "2020-07-23 14:19:47.623306",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Total Instructors",
- "owner": "Administrator",
- "report_function": "Sum",
- "show_percentage_stats": 1,
- "stats_time_interval": "Monthly",
- "type": "Document Type"
-}
\ No newline at end of file
diff --git a/erpnext/education/number_card/total_students/total_students.json b/erpnext/education/number_card/total_students/total_students.json
deleted file mode 100644
index 109c3d8..0000000
--- a/erpnext/education/number_card/total_students/total_students.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "aggregate_function_based_on": "",
- "creation": "2020-07-23 14:18:07.732298",
- "docstatus": 0,
- "doctype": "Number Card",
- "document_type": "Student",
- "dynamic_filters_json": "[]",
- "filters_json": "[[\"Student\",\"enabled\",\"=\",1,false]]",
- "function": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "label": "Total Students",
- "modified": "2020-07-23 14:18:40.603947",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Total Students",
- "owner": "Administrator",
- "report_function": "Sum",
- "show_percentage_stats": 1,
- "stats_time_interval": "Monthly",
- "type": "Document Type"
-}
\ No newline at end of file
diff --git a/erpnext/education/onboarding_step/create_a_course/create_a_course.json b/erpnext/education/onboarding_step/create_a_course/create_a_course.json
deleted file mode 100644
index 02eee14..0000000
--- a/erpnext/education/onboarding_step/create_a_course/create_a_course.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "action": "Create Entry",
- "creation": "2020-07-27 19:09:04.493932",
- "docstatus": 0,
- "doctype": "Onboarding Step",
- "idx": 0,
- "is_complete": 0,
- "is_mandatory": 0,
- "is_single": 0,
- "is_skipped": 0,
- "modified": "2020-07-27 19:09:04.493932",
- "modified_by": "Administrator",
- "name": "Create a Course",
- "owner": "Administrator",
- "reference_document": "Course",
- "show_full_form": 1,
- "title": "Create a Course",
- "validate_action": 1
-}
\ No newline at end of file
diff --git a/erpnext/education/onboarding_step/create_a_program/create_a_program.json b/erpnext/education/onboarding_step/create_a_program/create_a_program.json
deleted file mode 100644
index 6172630..0000000
--- a/erpnext/education/onboarding_step/create_a_program/create_a_program.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "action": "Create Entry",
- "creation": "2020-07-27 19:09:35.451945",
- "docstatus": 0,
- "doctype": "Onboarding Step",
- "idx": 0,
- "is_complete": 0,
- "is_mandatory": 0,
- "is_single": 0,
- "is_skipped": 0,
- "modified": "2020-07-27 19:09:35.451945",
- "modified_by": "Administrator",
- "name": "Create a Program",
- "owner": "Administrator",
- "reference_document": "Program",
- "show_full_form": 1,
- "title": "Create a Program",
- "validate_action": 1
-}
\ No newline at end of file
diff --git a/erpnext/education/onboarding_step/create_a_student/create_a_student.json b/erpnext/education/onboarding_step/create_a_student/create_a_student.json
deleted file mode 100644
index 07c3f73..0000000
--- a/erpnext/education/onboarding_step/create_a_student/create_a_student.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "action": "Create Entry",
- "creation": "2020-07-27 19:17:20.326837",
- "docstatus": 0,
- "doctype": "Onboarding Step",
- "idx": 0,
- "is_complete": 0,
- "is_mandatory": 1,
- "is_single": 0,
- "is_skipped": 0,
- "modified": "2020-07-27 19:49:47.724289",
- "modified_by": "Administrator",
- "name": "Create a Student",
- "owner": "Administrator",
- "reference_document": "Student",
- "show_full_form": 1,
- "title": "Create a Student",
- "validate_action": 1
-}
\ No newline at end of file
diff --git a/erpnext/education/onboarding_step/create_a_topic/create_a_topic.json b/erpnext/education/onboarding_step/create_a_topic/create_a_topic.json
deleted file mode 100644
index 96a5364..0000000
--- a/erpnext/education/onboarding_step/create_a_topic/create_a_topic.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "action": "Create Entry",
- "creation": "2020-07-27 19:08:40.754534",
- "docstatus": 0,
- "doctype": "Onboarding Step",
- "idx": 0,
- "is_complete": 0,
- "is_mandatory": 0,
- "is_single": 0,
- "is_skipped": 0,
- "modified": "2020-07-27 19:09:13.231995",
- "modified_by": "Administrator",
- "name": "Create a Topic",
- "owner": "Administrator",
- "reference_document": "Topic",
- "show_full_form": 1,
- "title": "Create a Topic",
- "validate_action": 1
-}
\ No newline at end of file
diff --git a/erpnext/education/onboarding_step/create_an_instructor/create_an_instructor.json b/erpnext/education/onboarding_step/create_an_instructor/create_an_instructor.json
deleted file mode 100644
index 419d6e0..0000000
--- a/erpnext/education/onboarding_step/create_an_instructor/create_an_instructor.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "action": "Create Entry",
- "creation": "2020-07-27 19:17:39.158037",
- "docstatus": 0,
- "doctype": "Onboarding Step",
- "idx": 0,
- "is_complete": 0,
- "is_mandatory": 1,
- "is_single": 0,
- "is_skipped": 0,
- "modified": "2020-07-27 19:49:47.723494",
- "modified_by": "Administrator",
- "name": "Create an Instructor",
- "owner": "Administrator",
- "reference_document": "Instructor",
- "show_full_form": 1,
- "title": "Create an Instructor",
- "validate_action": 1
-}
\ No newline at end of file
diff --git a/erpnext/education/onboarding_step/enroll_a_student_in_a_program/enroll_a_student_in_a_program.json b/erpnext/education/onboarding_step/enroll_a_student_in_a_program/enroll_a_student_in_a_program.json
deleted file mode 100644
index 61e48cd..0000000
--- a/erpnext/education/onboarding_step/enroll_a_student_in_a_program/enroll_a_student_in_a_program.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "action": "Create Entry",
- "creation": "2020-07-27 19:10:28.530226",
- "docstatus": 0,
- "doctype": "Onboarding Step",
- "idx": 0,
- "is_complete": 0,
- "is_mandatory": 0,
- "is_single": 0,
- "is_skipped": 0,
- "modified": "2020-07-27 19:10:28.530226",
- "modified_by": "Administrator",
- "name": "Enroll a Student in a Program",
- "owner": "Administrator",
- "reference_document": "Program Enrollment",
- "show_full_form": 0,
- "title": "Enroll a Student in a Program",
- "validate_action": 1
-}
\ No newline at end of file
diff --git a/erpnext/education/onboarding_step/introduction_to_program_and_courses/introduction_to_program_and_courses.json b/erpnext/education/onboarding_step/introduction_to_program_and_courses/introduction_to_program_and_courses.json
deleted file mode 100644
index a9ddfc0..0000000
--- a/erpnext/education/onboarding_step/introduction_to_program_and_courses/introduction_to_program_and_courses.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "action": "Watch Video",
- "creation": "2020-07-27 19:05:12.663987",
- "docstatus": 0,
- "doctype": "Onboarding Step",
- "idx": 0,
- "is_complete": 0,
- "is_mandatory": 0,
- "is_single": 0,
- "is_skipped": 0,
- "modified": "2020-07-27 20:18:11.831789",
- "modified_by": "Administrator",
- "name": "Introduction to Program and Courses",
- "owner": "Administrator",
- "show_full_form": 0,
- "title": "Introduction to Program and Courses",
- "validate_action": 1,
- "video_url": "https://www.youtube.com/watch?v=1ueE4seFTp8"
-}
\ No newline at end of file
diff --git a/erpnext/education/onboarding_step/introduction_to_student_attendance/introduction_to_student_attendance.json b/erpnext/education/onboarding_step/introduction_to_student_attendance/introduction_to_student_attendance.json
deleted file mode 100644
index 3de9972..0000000
--- a/erpnext/education/onboarding_step/introduction_to_student_attendance/introduction_to_student_attendance.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "action": "Watch Video",
- "creation": "2020-07-27 19:14:57.176131",
- "docstatus": 0,
- "doctype": "Onboarding Step",
- "idx": 0,
- "is_complete": 0,
- "is_mandatory": 0,
- "is_single": 0,
- "is_skipped": 0,
- "modified": "2020-07-27 19:55:55.411032",
- "modified_by": "Administrator",
- "name": "Introduction to Student Attendance",
- "owner": "Administrator",
- "show_full_form": 0,
- "title": "Introduction to Student Attendance",
- "validate_action": 1,
- "video_url": "https://youtu.be/j9pgkPuyiaI"
-}
\ No newline at end of file
diff --git a/erpnext/education/onboarding_step/introduction_to_student_group/introduction_to_student_group.json b/erpnext/education/onboarding_step/introduction_to_student_group/introduction_to_student_group.json
deleted file mode 100644
index 74bdcd1..0000000
--- a/erpnext/education/onboarding_step/introduction_to_student_group/introduction_to_student_group.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "action": "Watch Video",
- "creation": "2020-07-27 19:12:05.046465",
- "docstatus": 0,
- "doctype": "Onboarding Step",
- "idx": 0,
- "is_complete": 0,
- "is_mandatory": 0,
- "is_single": 0,
- "is_skipped": 0,
- "modified": "2020-07-27 19:42:47.286441",
- "modified_by": "Administrator",
- "name": "Introduction to Student Group",
- "owner": "Administrator",
- "reference_document": "Student Group",
- "show_full_form": 0,
- "title": "Introduction to Student Group",
- "validate_action": 1,
- "video_url": "https://youtu.be/5K_smeeE1Q4"
-}
\ No newline at end of file
diff --git a/erpnext/education/report/__init__.py b/erpnext/education/report/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/report/__init__.py
+++ /dev/null
diff --git a/erpnext/education/report/absent_student_report/__init__.py b/erpnext/education/report/absent_student_report/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/report/absent_student_report/__init__.py
+++ /dev/null
diff --git a/erpnext/education/report/absent_student_report/absent_student_report.js b/erpnext/education/report/absent_student_report/absent_student_report.js
deleted file mode 100644
index bf2d10d..0000000
--- a/erpnext/education/report/absent_student_report/absent_student_report.js
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-// License: GNU General Public License v3. See license.txt
-
-
-frappe.query_reports["Absent Student Report"] = {
-	"filters": [
-		{
-			"fieldname":"date",
-			"label": __("Date"),
-			"fieldtype": "Date",
-			"default": frappe.datetime.get_today(),
-			"reqd": 1
-		}
-	]
-}
diff --git a/erpnext/education/report/absent_student_report/absent_student_report.json b/erpnext/education/report/absent_student_report/absent_student_report.json
deleted file mode 100644
index 92ad860..0000000
--- a/erpnext/education/report/absent_student_report/absent_student_report.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "add_total_row": 0,
- "creation": "2013-05-13 14:04:03",
- "disable_prepared_report": 0,
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 3,
- "is_standard": "Yes",
- "modified": "2020-06-24 17:16:40.251116",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Absent Student Report",
- "owner": "Administrator",
- "prepared_report": 0,
- "ref_doctype": "Student Attendance",
- "report_name": "Absent Student Report",
- "report_type": "Script Report",
- "roles": [
-  {
-   "role": "Academics User"
-  }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/education/report/absent_student_report/absent_student_report.py b/erpnext/education/report/absent_student_report/absent_student_report.py
deleted file mode 100644
index c274d33..0000000
--- a/erpnext/education/report/absent_student_report/absent_student_report.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-
-import frappe
-from frappe import _, msgprint
-from frappe.utils import formatdate
-
-from erpnext.education.doctype.student_attendance.student_attendance import get_holiday_list
-from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
-
-
-def execute(filters=None):
-	if not filters: filters = {}
-
-	if not filters.get("date"):
-		msgprint(_("Please select date"), raise_exception=1)
-
-	columns = get_columns(filters)
-	date = filters.get("date")
-
-	holiday_list = get_holiday_list()
-	if is_holiday(holiday_list, filters.get("date")):
-		msgprint(_("No attendance has been marked for {0} as it is a Holiday").format(frappe.bold(formatdate(filters.get("date")))))
-
-
-	absent_students = get_absent_students(date)
-	leave_applicants = get_leave_applications(date)
-	if absent_students:
-		student_list = [d["student"] for d in absent_students]
-		transportation_details = get_transportation_details(date, student_list)
-
-	data = []
-	for student in absent_students:
-		if not student.student in leave_applicants:
-			row = [student.student, student.student_name, student.student_group]
-			stud_details = frappe.db.get_value("Student", student.student, ['student_email_id', 'student_mobile_number'], as_dict=True)
-
-			if stud_details.student_email_id:
-				row+=[stud_details.student_email_id]
-			else:
-				row+= [""]
-
-			if stud_details.student_mobile_number:
-				row+=[stud_details.student_mobile_number]
-			else:
-				row+= [""]
-			if transportation_details.get(student.student):
-				row += transportation_details.get(student.student)
-
-			data.append(row)
-
-	return columns, data
-
-def get_columns(filters):
-	columns = [
-		_("Student") + ":Link/Student:90",
-		_("Student Name") + "::150",
-		_("Student Group") + "::180",
-		_("Student Email Address") + "::180",
-		_("Student Mobile No.") + "::150",
-		_("Mode of Transportation") + "::150",
-		_("Vehicle/Bus Number") + "::150",
-	]
-	return columns
-
-def get_absent_students(date):
-	absent_students = frappe.db.sql("""
-		SELECT student, student_name, student_group
-		FROM `tabStudent Attendance`
-		WHERE
-			status='Absent' and docstatus=1 and date = %s
-		ORDER BY
-			student_group, student_name""",
-	date, as_dict=1)
-	return absent_students
-
-def get_leave_applications(date):
-	leave_applicants = []
-	leave_applications = frappe.db.sql("""
-		SELECT student
-		FROM
-			`tabStudent Leave Application`
-		WHERE
-			docstatus = 1 and mark_as_present = 1 and
-			from_date <= %s and to_date >= %s
-	""", (date, date))
-	for student in leave_applications:
-		leave_applicants.append(student[0])
-
-	return leave_applicants
-
-def get_transportation_details(date, student_list):
-	academic_year = frappe.get_all("Academic Year", filters=[["year_start_date", "<=", date],["year_end_date", ">=", date]])
-	if academic_year:
-		academic_year = academic_year[0].name
-	elif frappe.defaults.get_defaults().academic_year:
-		academic_year = frappe.defaults.get_defaults().academic_year
-	else:
-		return {}
-
-	transportation_details = frappe.get_all("Program Enrollment", fields=["student", "mode_of_transportation", "vehicle_no"],
-		filters={"student": ("in", student_list), "academic_year": academic_year, "docstatus": ("not in", ["2"])})
-	transportation_map = {}
-	for d in transportation_details:
-		transportation_map[d.student] = [d.mode_of_transportation, d.vehicle_no]
-	return transportation_map
diff --git a/erpnext/education/report/assessment_plan_status/__init__.py b/erpnext/education/report/assessment_plan_status/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/report/assessment_plan_status/__init__.py
+++ /dev/null
diff --git a/erpnext/education/report/assessment_plan_status/assessment_plan_status.js b/erpnext/education/report/assessment_plan_status/assessment_plan_status.js
deleted file mode 100644
index 2d1eb09..0000000
--- a/erpnext/education/report/assessment_plan_status/assessment_plan_status.js
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-/* eslint-disable */
-
-frappe.query_reports["Assessment Plan Status"] = {
-	"filters": [
-		{
-			"fieldname":"assessment_group",
-			"label": __("Assessment Group"),
-			"fieldtype": "Link",
-			"options": "Assessment Group",
-			"get_query": function() {
-				return{
-					filters: {
-						'is_group': 0
-					}
-				};
-			}
-		},
-		{
-			"fieldname":"schedule_date",
-			"label": __("Scheduled Upto"),
-			"fieldtype": "Date",
-			"options": ""
-		}
-
-	]
-}
diff --git a/erpnext/education/report/assessment_plan_status/assessment_plan_status.json b/erpnext/education/report/assessment_plan_status/assessment_plan_status.json
deleted file mode 100644
index cbca648..0000000
--- a/erpnext/education/report/assessment_plan_status/assessment_plan_status.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "add_total_row": 0,
- "creation": "2017-11-09 15:07:30.404428",
- "disable_prepared_report": 0,
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 0,
- "is_standard": "Yes",
- "modified": "2020-06-24 17:16:02.027410",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Assessment Plan Status",
- "owner": "Administrator",
- "prepared_report": 0,
- "ref_doctype": "Assessment Plan",
- "report_name": "Assessment Plan Status",
- "report_type": "Script Report",
- "roles": [
-  {
-   "role": "Academics User"
-  }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/education/report/assessment_plan_status/assessment_plan_status.py b/erpnext/education/report/assessment_plan_status/assessment_plan_status.py
deleted file mode 100644
index 86f2451..0000000
--- a/erpnext/education/report/assessment_plan_status/assessment_plan_status.py
+++ /dev/null
@@ -1,180 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from itertools import groupby
-
-import frappe
-from frappe import _
-from frappe.utils import cint
-
-DOCSTATUS = {
-	0: "saved",
-	1: "submitted",
-}
-
-def execute(filters=None):
-	columns, data = [], []
-
-	args = frappe._dict()
-	args["assessment_group"] = filters.get("assessment_group")
-	args["schedule_date"] = filters.get("schedule_date")
-
-	columns = get_column()
-
-	data, chart = get_assessment_data(args)
-
-	return columns, data, None, chart
-
-
-def get_assessment_data(args=None):
-
-	# [total, saved, submitted, remaining]
-	chart_data = [0, 0, 0, 0]
-
-	condition = ''
-	if args["assessment_group"]:
-		condition += "and assessment_group = %(assessment_group)s"
-	if args["schedule_date"]:
-		condition += "and schedule_date <= %(schedule_date)s"
-
-	assessment_plan = frappe.db.sql('''
-			SELECT
-				ap.name as assessment_plan,
-				ap.assessment_name,
-				ap.student_group,
-				ap.schedule_date,
-				(select count(*) from `tabStudent Group Student` sgs where sgs.parent=ap.student_group)
-					as student_group_strength
-			FROM
-				`tabAssessment Plan` ap
-			WHERE
-				ap.docstatus = 1 {condition}
-			ORDER BY
-				ap.modified desc
-		'''.format(condition=condition), (args), as_dict=1)
-
-	assessment_plan_list = [d.assessment_plan for d in assessment_plan] if assessment_plan else ['']
-	assessment_result = get_assessment_result(assessment_plan_list)
-
-	for d in assessment_plan:
-
-		assessment_plan_details = assessment_result.get(d.assessment_plan)
-		assessment_plan_details = frappe._dict() if not assessment_plan_details else \
-			frappe._dict(assessment_plan_details)
-		if "saved" not in assessment_plan_details:
-			assessment_plan_details.update({"saved": 0})
-		if "submitted" not in assessment_plan_details:
-			assessment_plan_details.update({"submitted": 0})
-
-		# remaining students whose marks not entered
-		remaining_students = cint(d.student_group_strength) - cint(assessment_plan_details.saved) -\
-			cint(assessment_plan_details.submitted)
-		assessment_plan_details.update({"remaining": remaining_students})
-		d.update(assessment_plan_details)
-
-		chart_data[0] += cint(d.student_group_strength)
-		chart_data[1] += assessment_plan_details.saved
-		chart_data[2] += assessment_plan_details.submitted
-		chart_data[3] += assessment_plan_details.remaining
-
-	chart = get_chart(chart_data[1:])
-
-	return assessment_plan, chart
-
-
-def get_assessment_result(assessment_plan_list):
-	assessment_result_dict = frappe._dict()
-
-	assessment_result = frappe.db.sql('''
-		SELECT
-			assessment_plan, docstatus, count(*) as count
-		FROM
-			`tabAssessment Result`
-		WHERE
-			assessment_plan in (%s)
-		GROUP BY
-			assessment_plan, docstatus
-		ORDER BY
-			assessment_plan
-	''' %', '.join(['%s']*len(assessment_plan_list)), tuple(assessment_plan_list), as_dict=1)
-
-	for key, group in groupby(assessment_result, lambda ap: ap["assessment_plan"]):
-		tmp = {}
-		for d in group:
-			if d.docstatus in [0,1]:
-				tmp.update({DOCSTATUS[d.docstatus]:d.count})
-		assessment_result_dict[key] = tmp
-
-	return assessment_result_dict
-
-
-def get_chart(chart_data):
-	return {
-		"data": {
-			"labels": ["Saved", "Submitted", "Remaining"],
-			"datasets": [{
-				"values": chart_data
-			}]
-		},
-		"type": 'percentage',
-	}
-
-
-def get_column():
-	return [{
-		"fieldname": "assessment_plan",
-		"label": _("Assessment Plan"),
-		"fieldtype": "Link",
-		"options": "Assessment Plan",
-		"width": 120
-	},
-	{
-		"fieldname": "assessment_name",
-		"label": _("Assessment Plan Name"),
-		"fieldtype": "Data",
-		"options": "",
-		"width": 200
-	},
-	{
-		"fieldname": "schedule_date",
-		"label": _("Schedule Date"),
-		"fieldtype": "Date",
-		"options": "",
-		"width": 100
-	},
-	{
-		"fieldname": "student_group",
-		"label": _("Student Group"),
-		"fieldtype": "Link",
-		"options": "Student Group",
-		"width": 200
-	},
-	{
-		"fieldname": "student_group_strength",
-		"label": _("Total Student"),
-		"fieldtype": "Data",
-		"options": "",
-		"width": 100
-	},
-	{
-		"fieldname": "submitted",
-		"label": _("Submitted"),
-		"fieldtype": "Data",
-		"options": "",
-		"width": 100
-	},
-	{
-		"fieldname": "saved",
-		"label": _("Saved"),
-		"fieldtype": "Data",
-		"options": "",
-		"width": 100
-	},
-	{
-		"fieldname": "remaining",
-		"label": _("Remaining"),
-		"fieldtype": "Data",
-		"options": "",
-		"width": 100
-	}]
diff --git a/erpnext/education/report/course_wise_assessment_report/__init__.py b/erpnext/education/report/course_wise_assessment_report/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/report/course_wise_assessment_report/__init__.py
+++ /dev/null
diff --git a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.html b/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.html
deleted file mode 100644
index 33e725f..0000000
--- a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.html
+++ /dev/null
@@ -1,50 +0,0 @@
-{%
-	var letterhead = filters.letter_head || (frappe.get_doc(":Company", filters.company) && frappe.get_doc(":Company", filters.company).default_letter_head) || frappe.defaults.get_default("letter_head");
-	var report_columns = report.get_columns_for_print();
-%}
-{% if(letterhead) { %}
-<div style="margin-bottom: 7px;" class="text-center">
-	{%= frappe.boot.letter_heads[letterhead].header %}
-</div>
-{% } %}
-<h4 class="text-center">{%= __("Assessment Report") %}</h4>
-<hr>
-<h5 class="text-center">{%= __("Academic Year: ") %} {%= filters.academic_year %} </h5>
-{% if (filters.academic_term){ %}
-<h5 class="text-center">{%= __("Academic Term: ") %} {%= filters.academic_term %} </h5>
-{% } %}
-<h5 class="text-center">{%= __("Course Code: ") %} {%= filters.course %}</h5>
-<h5 class="text-center">{%= __("Assessment Group: ") %} {%= filters.assessment_group %}</h5>
-{% if (filters.student_group){ %}
-<h5 class="text-center">{%= __("Student Group: ") %} {%= filters.student_group %} </h5>
-{% } %}
-<hr>
-
-<table class="table table-bordered">
-	<thead>
-		<tr>
-			{% for(var i=1, l=report_columns.length; i<l; i++) { %}
-				<th style="text-transform: uppercase; max-width: 100px">{%= report_columns[i].label %}</th>
-			{% } %}
-		</tr>
-	</thead>
-	<tbody>
-		{% for(var j=0, k=data.length; j<k; j++) { %}
-			{%
-				var row = data[j];
-			%}
-			<tr>
-				{% for(var i=1, l=report_columns.length; i<l; i++) { %}
-					<td class="text-center">
-						{% var fieldname = report_columns[i].fieldname; %}
-						{% if (!is_null(row[fieldname])) { %}
-							{%= row[fieldname] %}
-						{% } %}
-					</td>
-				{% } %}
-			</tr>
-		{% } %}
-	</tbody>
-</table>
-
-<p class="text-right text-muted">Printed On {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}</p>
diff --git a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.js b/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.js
deleted file mode 100644
index 8c42d48..0000000
--- a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.js
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.query_reports["Course wise Assessment Report"] = {
-	"filters": [
-		{
-			"fieldname":"academic_year",
-			"label": __("Academic Year"),
-			"fieldtype": "Link",
-			"options": "Academic Year",
-			"reqd": 1
-		},
-		{
-			"fieldname":"academic_term",
-			"label": __("Academic Term"),
-			"fieldtype": "Link",
-			"options": "Academic Term"
-		},
-		{
-			"fieldname":"course",
-			"label": __("Course"),
-			"fieldtype": "Link",
-			"options": "Course",
-			"reqd": 1
-		},
-		{
-			"fieldname":"student_group",
-			"label": __("Student Group"),
-			"fieldtype": "Link",
-			"options": "Student Group"
-		},
-		{
-			"fieldname":"assessment_group",
-			"label": __("Assessment Group"),
-			"fieldtype": "Link",
-			"options": "Assessment Group",
-			"reqd": 1
-		}
-	]
-};
diff --git a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.json b/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.json
deleted file mode 100644
index 416db9d..0000000
--- a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "add_total_row": 0,
- "creation": "2017-05-05 14:46:13.776133",
- "disable_prepared_report": 0,
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 0,
- "is_standard": "Yes",
- "modified": "2020-06-24 17:15:15.477530",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Course wise Assessment Report",
- "owner": "Administrator",
- "prepared_report": 0,
- "query": "",
- "ref_doctype": "Assessment Result",
- "report_name": "Course wise Assessment Report",
- "report_type": "Script Report",
- "roles": [
-  {
-   "role": "Instructor"
-  },
-  {
-   "role": "Education Manager"
-  }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.py b/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.py
deleted file mode 100644
index 38eef68..0000000
--- a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.py
+++ /dev/null
@@ -1,250 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from collections import OrderedDict, defaultdict
-
-import frappe
-from frappe import _
-
-from erpnext.education.api import get_grade
-
-
-def execute(filters=None):
-	data, chart, grades = [], [], []
-	args = frappe._dict()
-	grade_wise_analysis = defaultdict(dict)
-
-	args["academic_year"] = filters.get("academic_year")
-	args["course"] = filters.get("course")
-	args["assessment_group"] = filters.get("assessment_group")
-
-	args["academic_term"] = filters.get("academic_term")
-	args["student_group"] = filters.get("student_group")
-
-	if args["assessment_group"] == "All Assessment Groups":
-		frappe.throw(_("Please select the assessment group other than 'All Assessment Groups'"))
-
-	returned_values = get_formatted_result(args, get_assessment_criteria=True)
-	student_dict = returned_values["student_details"]
-	result_dict = returned_values["assessment_result"]
-	assessment_criteria_dict = returned_values["assessment_criteria"]
-
-	for student in result_dict:
-		student_row = {}
-		student_row["student"] = student
-		student_row["student_name"] = student_dict[student]
-		for criteria in assessment_criteria_dict:
-			scrub_criteria = frappe.scrub(criteria)
-			if criteria in result_dict[student][args.course][args.assessment_group]:
-				student_row[scrub_criteria] = result_dict[student][args.course][args.assessment_group][criteria]["grade"]
-				student_row[scrub_criteria + "_score"] = result_dict[student][args.course][args.assessment_group][criteria]["score"]
-
-				# create the list of possible grades
-				if student_row[scrub_criteria] not in grades:
-					grades.append(student_row[scrub_criteria])
-
-				# create the dict of for gradewise analysis
-				if student_row[scrub_criteria] not in grade_wise_analysis[criteria]:
-					grade_wise_analysis[criteria][student_row[scrub_criteria]] = 1
-				else:
-					grade_wise_analysis[criteria][student_row[scrub_criteria]] += 1
-			else:
-				student_row[frappe.scrub(criteria)] = ""
-				student_row[frappe.scrub(criteria)+ "_score"] = ""
-		data.append(student_row)
-
-	assessment_criteria_list = [d for d in assessment_criteria_dict]
-	columns = get_column(assessment_criteria_dict)
-	chart = get_chart_data(grades, assessment_criteria_list, grade_wise_analysis)
-
-	return columns, data, None, chart
-
-
-def get_formatted_result(args, get_assessment_criteria=False, get_course=False, get_all_assessment_groups=False):
-	cond, cond1, cond2, cond3, cond4 = " ", " ", " ", " ", " "
-	args_list = [args.academic_year]
-
-	if args.course:
-		cond = " and ar.course=%s"
-		args_list.append(args.course)
-
-	if args.academic_term:
-		cond1 = " and ar.academic_term=%s"
-		args_list.append(args.academic_term)
-
-	if args.student_group:
-		cond2 = " and ar.student_group=%s"
-		args_list.append(args.student_group)
-
-	create_total_dict = False
-
-	assessment_groups = get_child_assessment_groups(args.assessment_group)
-	cond3 = " and ar.assessment_group in (%s)"%(', '.join(['%s']*len(assessment_groups)))
-	args_list += assessment_groups
-
-	if args.students:
-		cond4 = " and ar.student in (%s)"%(', '.join(['%s']*len(args.students)))
-		args_list += args.students
-
-	assessment_result = frappe.db.sql('''
-		SELECT
-			ar.student, ar.student_name, ar.academic_year, ar.academic_term, ar.program, ar.course,
-			ar.assessment_plan, ar.grading_scale, ar.assessment_group, ar.student_group,
-			ard.assessment_criteria, ard.maximum_score, ard.grade, ard.score
-		FROM
-			`tabAssessment Result` ar, `tabAssessment Result Detail` ard
-		WHERE
-			ar.name=ard.parent and ar.docstatus=1 and ar.academic_year=%s {0} {1} {2} {3} {4}
-		ORDER BY
-			ard.assessment_criteria'''.format(cond, cond1, cond2, cond3, cond4),
-		tuple(args_list), as_dict=1)
-
-	# create the nested dictionary structure as given below:
-	# <variable_name>.<student_name>.<course>.<assessment_group>.<assessment_criteria>.<grade/score/max_score>
-	# "Final Grade" -> assessment criteria used for totaling and args.assessment_group -> for totaling all the assesments
-
-	student_details = {}
-	formatted_assessment_result = defaultdict(dict)
-	assessment_criteria_dict = OrderedDict()
-	course_dict = OrderedDict()
-	total_maximum_score = None
-	if not (len(assessment_groups) == 1 and assessment_groups[0] == args.assessment_group):
-		create_total_dict = True
-
-	# add the score for a given score and recalculate the grades
-	def add_score_and_recalculate_grade(result, assessment_group, assessment_criteria):
-		formatted_assessment_result[result.student][result.course][assessment_group]\
-			[assessment_criteria]["maximum_score"] += result.maximum_score
-		formatted_assessment_result[result.student][result.course][assessment_group]\
-			[assessment_criteria]["score"] += result.score
-		tmp_grade = get_grade(result.grading_scale, ((formatted_assessment_result[result.student][result.course]
-			[assessment_group][assessment_criteria]["score"])/(formatted_assessment_result[result.student]
-			[result.course][assessment_group][assessment_criteria]["maximum_score"]))*100)
-		formatted_assessment_result[result.student][result.course][assessment_group]\
-			[assessment_criteria]["grade"] = tmp_grade
-
-	# create the assessment criteria "Final Grade" with the sum of all the scores of the assessment criteria in a given assessment group
-	def add_total_score(result, assessment_group):
-		if "Final Grade" not in formatted_assessment_result[result.student][result.course][assessment_group]:
-			formatted_assessment_result[result.student][result.course][assessment_group]["Final Grade"] = frappe._dict({
-				"assessment_criteria": "Final Grade", "maximum_score": result.maximum_score, "score": result.score, "grade": result.grade})
-		else:
-			add_score_and_recalculate_grade(result, assessment_group, "Final Grade")
-
-	for result in assessment_result:
-		if result.student not in student_details:
-			student_details[result.student] = result.student_name
-
-		assessment_criteria_details = frappe._dict({"assessment_criteria": result.assessment_criteria,
-			"maximum_score": result.maximum_score, "score": result.score, "grade": result.grade})
-
-		if not formatted_assessment_result[result.student]:
-			formatted_assessment_result[result.student] = defaultdict(dict)
-		if not formatted_assessment_result[result.student][result.course]:
-			formatted_assessment_result[result.student][result.course] = defaultdict(dict)
-
-		if not create_total_dict:
-			formatted_assessment_result[result.student][result.course][result.assessment_group]\
-				[result.assessment_criteria] = assessment_criteria_details
-			add_total_score(result, result.assessment_group)
-
-		# create the total of all the assessment groups criteria-wise
-		elif create_total_dict:
-			if get_all_assessment_groups:
-				formatted_assessment_result[result.student][result.course][result.assessment_group]\
-					[result.assessment_criteria] = assessment_criteria_details
-			if not formatted_assessment_result[result.student][result.course][args.assessment_group]:
-				formatted_assessment_result[result.student][result.course][args.assessment_group] = defaultdict(dict)
-				formatted_assessment_result[result.student][result.course][args.assessment_group]\
-					[result.assessment_criteria] = assessment_criteria_details
-			elif result.assessment_criteria not in formatted_assessment_result[result.student][result.course][args.assessment_group]:
-				formatted_assessment_result[result.student][result.course][args.assessment_group]\
-					[result.assessment_criteria] = assessment_criteria_details
-			elif result.assessment_criteria in formatted_assessment_result[result.student][result.course][args.assessment_group]:
-				add_score_and_recalculate_grade(result, args.assessment_group, result.assessment_criteria)
-
-			add_total_score(result, args.assessment_group)
-
-		total_maximum_score = formatted_assessment_result[result.student][result.course][args.assessment_group]\
-			["Final Grade"]["maximum_score"]
-		if get_assessment_criteria:
-			assessment_criteria_dict[result.assessment_criteria] = formatted_assessment_result[result.student][result.course]\
-				[args.assessment_group][result.assessment_criteria]["maximum_score"]
-		if get_course:
-			course_dict[result.course] = total_maximum_score
-
-	if get_assessment_criteria and total_maximum_score:
-		assessment_criteria_dict["Final Grade"] = total_maximum_score
-
-	return {
-		"student_details": student_details,
-		"assessment_result": formatted_assessment_result,
-		"assessment_criteria": assessment_criteria_dict,
-		"course_dict": course_dict
-	}
-
-
-def get_column(assessment_criteria):
-	columns = [{
-		"fieldname": "student",
-		"label": _("Student ID"),
-		"fieldtype": "Link",
-		"options": "Student",
-		"width": 90
-	},
-	{
-		"fieldname": "student_name",
-		"label": _("Student Name"),
-		"fieldtype": "Data",
-		"width": 160
-	}]
-	for d in assessment_criteria:
-		columns.append({
-			"fieldname": frappe.scrub(d),
-			"label": d,
-			"fieldtype": "Data",
-			"width": 110
-		})
-		columns.append({
-			"fieldname": frappe.scrub(d) +"_score",
-			"label": "Score(" + str(int(assessment_criteria[d])) + ")",
-			"fieldtype": "Float",
-			"width": 100
-		})
-
-	return columns
-
-
-def get_chart_data(grades, criteria_list, kounter):
-	grades = sorted(grades)
-	datasets = []
-
-	for grade in grades:
-		tmp = frappe._dict({"name": grade, "values":[]})
-		for criteria in criteria_list:
-			if grade in kounter[criteria]:
-				tmp["values"].append(kounter[criteria][grade])
-			else:
-				tmp["values"].append(0)
-		datasets.append(tmp)
-
-	return {
-		"data": {
-			"labels": criteria_list,
-			"datasets": datasets
-		},
-		"type": 'bar',
-	}
-
-
-def get_child_assessment_groups(assessment_group):
-	assessment_groups = []
-	group_type = frappe.get_value("Assessment Group", assessment_group, "is_group")
-	if group_type:
-		from frappe.desk.treeview import get_children
-		assessment_groups = [d.get("value") for d in get_children("Assessment Group",
-			assessment_group) if d.get("value") and not d.get("expandable")]
-	else:
-		assessment_groups = [assessment_group]
-	return assessment_groups
diff --git a/erpnext/education/report/final_assessment_grades/__init__.py b/erpnext/education/report/final_assessment_grades/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/report/final_assessment_grades/__init__.py
+++ /dev/null
diff --git a/erpnext/education/report/final_assessment_grades/final_assessment_grades.js b/erpnext/education/report/final_assessment_grades/final_assessment_grades.js
deleted file mode 100644
index ddff8a8..0000000
--- a/erpnext/education/report/final_assessment_grades/final_assessment_grades.js
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-/* eslint-disable */
-
-frappe.query_reports["Final Assessment Grades"] = {
-	"filters": [
-		{
-			"fieldname":"academic_year",
-			"label": __("Academic Year"),
-			"fieldtype": "Link",
-			"options": "Academic Year",
-			"reqd": 1
-		},
-		{
-			"fieldname":"student_group",
-			"label": __("Student Group"),
-			"fieldtype": "Link",
-			"options": "Student Group",
-			"reqd": 1,
-			"get_query": function() {
-				return{
-					filters: {
-						"group_based_on": "Batch",
-						"academic_year": frappe.query_report.get_filter_value('academic_year')
-					}
-				};
-			}
-		},
-		{
-			"fieldname":"assessment_group",
-			"label": __("Assessment Group"),
-			"fieldtype": "Link",
-			"options": "Assessment Group",
-			"reqd": 1
-		}
-
-	]
-}
diff --git a/erpnext/education/report/final_assessment_grades/final_assessment_grades.json b/erpnext/education/report/final_assessment_grades/final_assessment_grades.json
deleted file mode 100644
index 6a23494..0000000
--- a/erpnext/education/report/final_assessment_grades/final_assessment_grades.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "add_total_row": 0,
- "creation": "2018-01-22 17:04:43.412054",
- "disable_prepared_report": 0,
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 0,
- "is_standard": "Yes",
- "modified": "2020-06-24 17:13:35.373756",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Final Assessment Grades",
- "owner": "Administrator",
- "prepared_report": 0,
- "ref_doctype": "Assessment Result",
- "report_name": "Final Assessment Grades",
- "report_type": "Script Report",
- "roles": [
-  {
-   "role": "Instructor"
-  },
-  {
-   "role": "Education Manager"
-  }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/education/report/final_assessment_grades/final_assessment_grades.py b/erpnext/education/report/final_assessment_grades/final_assessment_grades.py
deleted file mode 100644
index b042867..0000000
--- a/erpnext/education/report/final_assessment_grades/final_assessment_grades.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from collections import defaultdict
-
-import frappe
-from frappe import _
-
-from erpnext.education.report.course_wise_assessment_report.course_wise_assessment_report import (
-	get_chart_data,
-	get_formatted_result,
-)
-
-
-def execute(filters=None):
-	columns, data, grades = [], [], []
-	args = frappe._dict()
-	course_wise_analysis = defaultdict(dict)
-
-	args["academic_year"] = filters.get("academic_year")
-	assessment_group = args["assessment_group"] = filters.get("assessment_group")
-
-	student_group = filters.get("student_group")
-	args.students = frappe.db.sql_list("select student from `tabStudent Group Student` where parent=%s", (student_group))
-
-	values = get_formatted_result(args, get_course=True)
-	student_details = values.get("student_details")
-	assessment_result = values.get("assessment_result")
-	course_dict = values.get("course_dict")
-
-	for student in args.students:
-		if student_details.get(student):
-			student_row = {}
-			student_row["student"] = student
-			student_row["student_name"] = student_details[student]
-			for course in course_dict:
-				scrub_course = frappe.scrub(course)
-				if assessment_group in assessment_result[student][course]:
-					student_row["grade_" + scrub_course] = assessment_result[student][course][assessment_group]["Total Score"]["grade"]
-					student_row["score_" + scrub_course] = assessment_result[student][course][assessment_group]["Total Score"]["score"]
-
-					# create the list of possible grades
-					if student_row["grade_" + scrub_course] not in grades:
-						grades.append(student_row["grade_" + scrub_course])
-
-					# create the dict of for gradewise analysis
-					if student_row["grade_" + scrub_course] not in course_wise_analysis[course]:
-						course_wise_analysis[course][student_row["grade_" + scrub_course]] = 1
-					else:
-						course_wise_analysis[course][student_row["grade_" + scrub_course]] += 1
-
-			data.append(student_row)
-
-	course_list = [d for d in course_dict]
-	columns = get_column(course_dict)
-	chart = get_chart_data(grades, course_list, course_wise_analysis)
-	return columns, data, None, chart
-
-
-def get_column(course_dict):
-	columns = [{
-		"fieldname": "student",
-		"label": _("Student ID"),
-		"fieldtype": "Link",
-		"options": "Student",
-		"width": 90
-	},
-	{
-		"fieldname": "student_name",
-		"label": _("Student Name"),
-		"fieldtype": "Data",
-		"width": 160
-	}]
-	for course in course_dict:
-		columns.append({
-			"fieldname": "grade_" + frappe.scrub(course),
-			"label": course,
-			"fieldtype": "Data",
-			"width": 110
-		})
-		columns.append({
-			"fieldname": "score_" + frappe.scrub(course),
-			"label": "Score(" + str(course_dict[course]) + ")",
-			"fieldtype": "Float",
-			"width": 100
-		})
-
-	return columns
diff --git a/erpnext/education/report/program_wise_fee_collection/__init__.py b/erpnext/education/report/program_wise_fee_collection/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/report/program_wise_fee_collection/__init__.py
+++ /dev/null
diff --git a/erpnext/education/report/program_wise_fee_collection/program_wise_fee_collection.js b/erpnext/education/report/program_wise_fee_collection/program_wise_fee_collection.js
deleted file mode 100644
index 72e8f12..0000000
--- a/erpnext/education/report/program_wise_fee_collection/program_wise_fee_collection.js
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-/* eslint-disable */
-
-frappe.query_reports["Program wise Fee Collection"] = {
-	"filters": [
-		{
-			"fieldname": "from_date",
-			"label": __("From Date"),
-			"fieldtype": "Date",
-			"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
-			"reqd": 1
-		},
-		{
-			"fieldname": "to_date",
-			"label": __("To Date"),
-			"fieldtype": "Date",
-			"default": frappe.datetime.get_today(),
-			"reqd": 1
-		}
-	]
-};
diff --git a/erpnext/education/report/program_wise_fee_collection/program_wise_fee_collection.json b/erpnext/education/report/program_wise_fee_collection/program_wise_fee_collection.json
deleted file mode 100644
index ee5c0de..0000000
--- a/erpnext/education/report/program_wise_fee_collection/program_wise_fee_collection.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "add_total_row": 1,
- "creation": "2020-07-27 16:05:33.263539",
- "disable_prepared_report": 0,
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 0,
- "is_standard": "Yes",
- "json": "{}",
- "modified": "2020-08-05 14:14:12.410515",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Program wise Fee Collection",
- "owner": "Administrator",
- "prepared_report": 0,
- "query": "SELECT \n    FeesCollected.program AS \"Program:Link/Program:200\",\n    FeesCollected.paid_amount AS \"Fees Collected:Currency:150\",\n    FeesCollected.outstanding_amount AS \"Outstanding Amount:Currency:150\",\n    FeesCollected.grand_total \"Grand Total:Currency:150\"\nFROM (\n    SELECT \n        sum(grand_total) - sum(outstanding_amount) AS paid_amount, program,\n        sum(outstanding_amount) AS outstanding_amount,\n        sum(grand_total) AS grand_total\n    FROM `tabFees`\n    WHERE docstatus = 1\n    GROUP BY program\n) AS FeesCollected\nORDER BY FeesCollected.paid_amount DESC",
- "ref_doctype": "Fees",
- "report_name": "Program wise Fee Collection",
- "report_type": "Script Report",
- "roles": [
-  {
-   "role": "Academics User"
-  },
-  {
-   "role": "Accounts User"
-  },
-  {
-   "role": "Accounts Manager"
-  }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/education/report/program_wise_fee_collection/program_wise_fee_collection.py b/erpnext/education/report/program_wise_fee_collection/program_wise_fee_collection.py
deleted file mode 100644
index 0599dad..0000000
--- a/erpnext/education/report/program_wise_fee_collection/program_wise_fee_collection.py
+++ /dev/null
@@ -1,124 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-
-
-def execute(filters=None):
-	if not filters:
-		filters = {}
-
-	columns = get_columns(filters)
-	data = get_data(filters)
-	chart = get_chart_data(data)
-
-	return columns, data, None, chart
-
-def get_columns(filters=None):
-	return [
-		{
-			'label': _('Program'),
-			'fieldname': 'program',
-			'fieldtype': 'Link',
-			'options': 'Program',
-			'width': 300
-		},
-		{
-			'label': _('Fees Collected'),
-			'fieldname': 'fees_collected',
-			'fieldtype': 'Currency',
-			'width': 200
-		},
-		{
-			'label': _('Outstanding Amount'),
-			'fieldname': 'outstanding_amount',
-			'fieldtype': 'Currency',
-			'width': 200
-		},
-		{
-			'label': _('Grand Total'),
-			'fieldname': 'grand_total',
-			'fieldtype': 'Currency',
-			'width': 200
-		}
-	]
-
-
-def get_data(filters=None):
-	data = []
-
-	conditions = get_filter_conditions(filters)
-
-	fee_details = frappe.db.sql(
-		"""
-			SELECT
-				FeesCollected.program,
-				FeesCollected.paid_amount,
-				FeesCollected.outstanding_amount,
-				FeesCollected.grand_total
-			FROM (
-				SELECT
-					sum(grand_total) - sum(outstanding_amount) AS paid_amount, program,
-					sum(outstanding_amount) AS outstanding_amount,
-					sum(grand_total) AS grand_total
-				FROM `tabFees`
-				WHERE
-					docstatus = 1 and
-					program IS NOT NULL
-					%s
-				GROUP BY program
-			) AS FeesCollected
-			ORDER BY FeesCollected.paid_amount DESC
-		""" % (conditions)
-	, as_dict=1)
-
-	for entry in fee_details:
-		data.append({
-			'program': entry.program,
-			'fees_collected': entry.paid_amount,
-			'outstanding_amount': entry.outstanding_amount,
-			'grand_total': entry.grand_total
-		})
-
-	return data
-
-def get_filter_conditions(filters):
-	conditions = ''
-
-	if filters.get('from_date') and filters.get('to_date'):
-		conditions += " and posting_date BETWEEN '%s' and '%s'" % (filters.get('from_date'), filters.get('to_date'))
-
-	return conditions
-
-
-def get_chart_data(data):
-	if not data:
-		return
-
-	labels = []
-	fees_collected = []
-	outstanding_amount = []
-
-	for entry in data:
-		labels.append(entry.get('program'))
-		fees_collected.append(entry.get('fees_collected'))
-		outstanding_amount.append(entry.get('outstanding_amount'))
-
-	return {
-		'data': {
-			'labels': labels,
-			'datasets': [
-				{
-					'name': _('Fees Collected'),
-					'values': fees_collected
-				},
-				{
-					'name': _('Outstanding Amt'),
-					'values': outstanding_amount
-				}
-			]
-		},
-		'type': 'bar'
-	}
diff --git a/erpnext/education/report/student_and_guardian_contact_details/__init__.py b/erpnext/education/report/student_and_guardian_contact_details/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/report/student_and_guardian_contact_details/__init__.py
+++ /dev/null
diff --git a/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.js b/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.js
deleted file mode 100644
index 2734b53..0000000
--- a/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.js
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.query_reports["Student and Guardian Contact Details"] = {
-	"filters": [
-		{
-			"fieldname":"academic_year",
-			"label": __("Academic Year"),
-			"fieldtype": "Link",
-			"options": "Academic Year",
-			"reqd": 1,
-		},
-		{
-			"fieldname":"program",
-			"label": __("Program"),
-			"fieldtype": "Link",
-			"options": "Program",
-			"reqd": 1
-		},
-		{
-			"fieldname":"student_batch_name",
-			"label": __("Batch Name"),
-			"fieldtype": "Link",
-			"options": "Student Batch Name",
-			"reqd": 1
-		},
-
-	]
-}
diff --git a/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.json b/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.json
deleted file mode 100644
index fa9be65..0000000
--- a/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "add_total_row": 0,
- "creation": "2017-03-27 17:47:16.831433",
- "disable_prepared_report": 0,
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 0,
- "is_standard": "Yes",
- "modified": "2020-06-24 17:16:50.639488",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Student and Guardian Contact Details",
- "owner": "Administrator",
- "prepared_report": 0,
- "ref_doctype": "Program Enrollment",
- "report_name": "Student and Guardian Contact Details",
- "report_type": "Script Report",
- "roles": [
-  {
-   "role": "Instructor"
-  },
-  {
-   "role": "Academics User"
-  }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.py b/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.py
deleted file mode 100644
index 7097b80..0000000
--- a/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-
-
-def execute(filters=None):
-	columns, data = [], []
-
-	academic_year = filters.get("academic_year")
-	program = filters.get("program")
-	student_batch_name = filters.get("student_batch_name")
-
-	columns = get_columns()
-
-	program_enrollments = frappe.get_list("Program Enrollment", fields=["student", "student_name"],
-		filters={"academic_year": academic_year, "program": program, "student_batch_name": student_batch_name})
-
-	student_list = [d.student for d in program_enrollments]
-	if not student_list:
-		return  columns, []
-
-	group_roll_no_map = get_student_roll_no(academic_year, program, student_batch_name)
-	student_map = get_student_details(student_list)
-	guardian_map = get_guardian_map(student_list)
-
-	for d in program_enrollments:
-		student_details = student_map.get(d.student)
-		row = [group_roll_no_map.get(d.student), d.student, d.student_name, student_details.get("student_mobile_number"),\
-				student_details.get("student_email_id"), student_details.get("address")]
-
-		student_guardians = guardian_map.get(d.student)
-
-		if student_guardians:
-			for i in range(2):
-				if i < len(student_guardians):
-					g = student_guardians[i]
-					row += [g.guardian_name, g.relation, g.mobile_number, g.email_address]
-
-		data.append(row)
-
-	return columns, data
-
-
-def get_columns():
-	columns = [
-		_("Group Roll No") + "::60",
-		_("Student ID") + ":Link/Student:90",
-		_("Student Name") + "::150",
-		_("Student Mobile No.") + "::110",
-		_("Student Email ID") + "::125",
-		_("Student Address") + "::175",
-		_("Guardian1 Name") + "::150",
-		_("Relation with Guardian1") + "::80",
-		_("Guardian1 Mobile No") + "::125",
-		_("Guardian1 Email ID") + "::125",
-		_("Guardian2 Name") + "::150",
-		_("Relation with Guardian2") + "::80",
-		_("Guardian2 Mobile No") + "::125",
-		_("Guardian2 Email ID") + "::125",
-	]
-	return columns
-
-def get_student_details(student_list):
-	student_map = frappe._dict()
-	student_details = frappe.db.sql('''
-		select name, student_mobile_number, student_email_id, address_line_1, address_line_2, city, state from `tabStudent` where name in (%s)''' %
-		', '.join(['%s']*len(student_list)), tuple(student_list), as_dict=1)
-	for s in student_details:
-		student = frappe._dict()
-		student["student_mobile_number"] = s.student_mobile_number
-		student["student_email_id"] = s.student_email_id
-		student["address"] = ', '.join([d for d in [s.address_line_1, s.address_line_2, s.city, s.state] if d])
-		student_map[s.name] = student
-	return student_map
-
-def get_guardian_map(student_list):
-	guardian_map = frappe._dict()
-	guardian_details = frappe.db.sql('''
-		select  parent, guardian, guardian_name, relation  from `tabStudent Guardian` where parent in (%s)''' %
-		', '.join(['%s']*len(student_list)), tuple(student_list), as_dict=1)
-
-	guardian_list = list(set([g.guardian for g in guardian_details])) or ['']
-
-	guardian_mobile_no = dict(frappe.db.sql("""select name, mobile_number from `tabGuardian`
-			where name in (%s)""" % ", ".join(['%s']*len(guardian_list)), tuple(guardian_list)))
-
-	guardian_email_id = dict(frappe.db.sql("""select name, email_address from `tabGuardian`
-			where name in (%s)""" % ", ".join(['%s']*len(guardian_list)), tuple(guardian_list)))
-
-	for guardian in guardian_details:
-		guardian["mobile_number"] = guardian_mobile_no.get(guardian.guardian)
-		guardian["email_address"] = guardian_email_id.get(guardian.guardian)
-		guardian_map.setdefault(guardian.parent, []).append(guardian)
-
-	return guardian_map
-
-def get_student_roll_no(academic_year, program, batch):
-	student_group = frappe.get_all("Student Group",
-		filters={"academic_year":academic_year, "program":program, "batch":batch, "disabled": 0})
-	if student_group:
-		roll_no_dict = dict(frappe.db.sql('''select student, group_roll_number from `tabStudent Group Student` where parent=%s''',
-			(student_group[0].name)))
-		return roll_no_dict
-	return {}
diff --git a/erpnext/education/report/student_batch_wise_attendance/__init__.py b/erpnext/education/report/student_batch_wise_attendance/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/report/student_batch_wise_attendance/__init__.py
+++ /dev/null
diff --git a/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.js b/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.js
deleted file mode 100644
index 9f1fcbc..0000000
--- a/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.js
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.query_reports["Student Batch-Wise Attendance"] = {
-	"filters": [{
-		"fieldname": "date",
-		"label": __("Date"),
-		"fieldtype": "Date",
-		"default": frappe.datetime.get_today(),
-		"reqd": 1
-	}]
-}
diff --git a/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.json b/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.json
deleted file mode 100644
index 8baf8f9..0000000
--- a/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "add_total_row": 0,
- "creation": "2016-11-28 22:07:03.859124",
- "disable_prepared_report": 0,
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 2,
- "is_standard": "Yes",
- "modified": "2020-06-24 17:16:59.823709",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Student Batch-Wise Attendance",
- "owner": "Administrator",
- "prepared_report": 0,
- "ref_doctype": "Student Attendance",
- "report_name": "Student Batch-Wise Attendance",
- "report_type": "Script Report",
- "roles": [
-  {
-   "role": "Academics User"
-  }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.py b/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.py
deleted file mode 100644
index 52055dc..0000000
--- a/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-
-import frappe
-from frappe import _, msgprint
-from frappe.utils import formatdate
-
-from erpnext.education.doctype.student_attendance.student_attendance import get_holiday_list
-from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
-
-
-def execute(filters=None):
-	if not filters: filters = {}
-
-	if not filters.get("date"):
-		msgprint(_("Please select date"), raise_exception=1)
-
-	holiday_list = get_holiday_list()
-	if is_holiday(holiday_list, filters.get("date")):
-		msgprint(_("No attendance has been marked for {0} as it is a Holiday").format(frappe.bold(formatdate(filters.get("date")))))
-
-	columns = get_columns(filters)
-
-	active_student_group = get_active_student_group()
-
-	data = []
-	for student_group in active_student_group:
-		row = [student_group.name]
-		present_students = 0
-		absent_students = 0
-		student_group_strength = get_student_group_strength(student_group.name)
-		student_attendance = get_student_attendance(student_group.name, filters.get("date"))
-		if student_attendance:
-			for attendance in student_attendance:
-				if attendance.status== "Present":
-					present_students = attendance.count
-				elif attendance.status== "Absent":
-					absent_students = attendance.count
-
-		unmarked_students = student_group_strength - (present_students + absent_students)
-		row+= [student_group_strength, present_students, absent_students, unmarked_students]
-		data.append(row)
-
-	return columns, data
-
-def get_columns(filters):
-	columns = [
-		_("Student Group") + ":Link/Student Group:250",
-		_("Student Group Strength") + "::170",
-		_("Present") + "::90",
-		_("Absent") + "::90",
-		_("Not Marked") + "::90"
-	]
-	return columns
-
-def get_active_student_group():
-	active_student_groups = frappe.db.sql("""select name from `tabStudent Group` where group_based_on = "Batch"
-		and academic_year=%s order by name""", (frappe.defaults.get_defaults().academic_year), as_dict=1)
-	return active_student_groups
-
-def get_student_group_strength(student_group):
-	student_group_strength = frappe.db.sql("""select count(*) from `tabStudent Group Student`
-		where parent = %s and active=1""", student_group)[0][0]
-	return student_group_strength
-
-def get_student_attendance(student_group, date):
-	student_attendance = frappe.db.sql("""select count(*) as count, status from `tabStudent Attendance` where
-				student_group= %s and date= %s and docstatus = 1 and
-				(course_schedule is Null or course_schedule='') group by status""",
-				(student_group, date), as_dict=1)
-	return student_attendance
diff --git a/erpnext/education/report/student_fee_collection/__init__.py b/erpnext/education/report/student_fee_collection/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/report/student_fee_collection/__init__.py
+++ /dev/null
diff --git a/erpnext/education/report/student_fee_collection/student_fee_collection.json b/erpnext/education/report/student_fee_collection/student_fee_collection.json
deleted file mode 100644
index c0229a2..0000000
--- a/erpnext/education/report/student_fee_collection/student_fee_collection.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "add_total_row": 1,
- "creation": "2016-06-22 02:58:41.024538",
- "disable_prepared_report": 0,
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 3,
- "is_standard": "Yes",
- "modified": "2020-06-24 17:14:39.452551",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Student Fee Collection",
- "owner": "Administrator",
- "prepared_report": 0,
- "query": "SELECT\n student as \"Student:Link/Student:200\",\n student_name as \"Student Name::200\",\n sum(grand_total) - sum(outstanding_amount) as \"Paid Amount:Currency:150\",\n sum(outstanding_amount) as \"Outstanding Amount:Currency:150\",\n sum(grand_total) as \"Grand Total:Currency:150\"\nFROM\n `tabFees` \nWHERE\n docstatus=1 \nGROUP BY\n student",
- "ref_doctype": "Fees",
- "report_name": "Student Fee Collection",
- "report_type": "Query Report",
- "roles": [
-  {
-   "role": "Academics User"
-  }
- ]
-}
diff --git a/erpnext/education/report/student_monthly_attendance_sheet/__init__.py b/erpnext/education/report/student_monthly_attendance_sheet/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/report/student_monthly_attendance_sheet/__init__.py
+++ /dev/null
diff --git a/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.js b/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.js
deleted file mode 100644
index 62c9455..0000000
--- a/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.js
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-// License: GNU General Public License v3. See license.txt
-
-
-frappe.query_reports["Student Monthly Attendance Sheet"] = {
-	"filters": [{
-		"fieldname": "month",
-		"label": __("Month"),
-		"fieldtype": "Select",
-		"options": "Jan\nFeb\nMar\nApr\nMay\nJun\nJul\nAug\nSep\nOct\nNov\nDec",
-		"default": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov",
-			"Dec"
-		][frappe.datetime.str_to_obj(frappe.datetime.get_today()).getMonth()],
-	},
-	{
-		"fieldname": "year",
-		"label": __("Year"),
-		"fieldtype": "Select",
-		"reqd": 1
-	},
-	{
-		"fieldname": "student_group",
-		"label": __("Student Group"),
-		"fieldtype": "Link",
-		"options": "Student Group",
-		"reqd": 1
-	}
-	],
-
-	"onload": function() {
-		return frappe.call({
-			method: "erpnext.education.report.student_monthly_attendance_sheet.student_monthly_attendance_sheet.get_attendance_years",
-			callback: function(r) {
-				var year_filter = frappe.query_report.get_filter('year');
-				year_filter.df.options = r.message;
-				year_filter.df.default = r.message.split("\n")[0];
-				year_filter.refresh();
-				year_filter.set_input(year_filter.df.default);
-			}
-		});
-	}
-}
diff --git a/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.json b/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.json
deleted file mode 100644
index 1423d4f..0000000
--- a/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "add_total_row": 0,
- "creation": "2013-05-13 14:04:03",
- "disable_prepared_report": 0,
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 3,
- "is_standard": "Yes",
- "modified": "2020-06-24 17:16:13.307053",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Student Monthly Attendance Sheet",
- "owner": "Administrator",
- "prepared_report": 0,
- "ref_doctype": "Student Attendance",
- "report_name": "Student Monthly Attendance Sheet",
- "report_type": "Script Report",
- "roles": [
-  {
-   "role": "Academics User"
-  }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.py b/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.py
deleted file mode 100644
index 1166a75..0000000
--- a/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.py
+++ /dev/null
@@ -1,135 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-
-import frappe
-from frappe import _
-from frappe.utils import add_days, cstr, date_diff, get_first_day, get_last_day, getdate
-
-from erpnext.education.api import get_student_group_students
-from erpnext.education.doctype.student_attendance.student_attendance import get_holiday_list
-from erpnext.support.doctype.issue.issue import get_holidays
-
-
-def execute(filters=None):
-	if not filters: filters = {}
-
-	from_date = get_first_day(filters["month"] + '-' + filters["year"])
-	to_date = get_last_day(filters["month"] + '-' + filters["year"])
-	total_days_in_month = date_diff(to_date, from_date) +1
-	columns = get_columns(total_days_in_month)
-	students = get_student_group_students(filters.get("student_group"),1)
-	students_list = get_students_list(students)
-	att_map = get_attendance_list(from_date, to_date, filters.get("student_group"), students_list)
-	data = []
-
-	for stud in students:
-		row = [stud.student, stud.student_name]
-		student_status = frappe.db.get_value("Student", stud.student, "enabled")
-		date = from_date
-		total_p = total_a = 0.0
-
-		for day in range(total_days_in_month):
-			status="None"
-
-			if att_map.get(stud.student):
-				status = att_map.get(stud.student).get(date, "None")
-			elif not student_status:
-				status = "Inactive"
-			else:
-				status = "None"
-
-			status_map = {"Present": "P", "Absent": "A", "None": "", "Inactive":"-", "Holiday":"H"}
-			row.append(status_map[status])
-
-			if status == "Present":
-				total_p += 1
-			elif status == "Absent":
-				total_a += 1
-			date = add_days(date, 1)
-
-		row += [total_p, total_a]
-		data.append(row)
-	return columns, data
-
-def get_columns(days_in_month):
-	columns = [ _("Student") + ":Link/Student:90", _("Student Name") + "::150"]
-	for day in range(days_in_month):
-		columns.append(cstr(day+1) +"::20")
-	columns += [_("Total Present") + ":Int:95", _("Total Absent") + ":Int:90"]
-	return columns
-
-def get_students_list(students):
-	student_list = []
-	for stud in students:
-		student_list.append(stud.student)
-	return student_list
-
-def get_attendance_list(from_date, to_date, student_group, students_list):
-	attendance_list = frappe.db.sql('''select student, date, status
-		from `tabStudent Attendance` where student_group = %s
-		and docstatus = 1
-		and date between %s and %s
-		order by student, date''',
-		(student_group, from_date, to_date), as_dict=1)
-
-	att_map = {}
-	students_with_leave_application = get_students_with_leave_application(from_date, to_date, students_list)
-	for d in attendance_list:
-		att_map.setdefault(d.student, frappe._dict()).setdefault(d.date, "")
-
-		if students_with_leave_application.get(d.date) and d.student in students_with_leave_application.get(d.date):
-			att_map[d.student][d.date] = "Present"
-		else:
-			att_map[d.student][d.date] = d.status
-
-	att_map = mark_holidays(att_map, from_date, to_date, students_list)
-
-	return att_map
-
-def get_students_with_leave_application(from_date, to_date, students_list):
-	if not students_list: return
-	leave_applications = frappe.db.sql("""
-		select student, from_date, to_date
-		from `tabStudent Leave Application`
-		where
-			mark_as_present = 1 and docstatus = 1
-			and student in %(students)s
-			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)s between from_date and to_date and %(to_date)s between from_date and to_date)
-			)
-		""", {
-			"students": students_list,
-			"from_date": from_date,
-			"to_date": to_date
-		}, as_dict=True)
-	students_with_leaves= {}
-	for application in leave_applications:
-		for date in daterange(application.from_date, application.to_date):
-			students_with_leaves.setdefault(date, []).append(application.student)
-
-	return students_with_leaves
-
-def daterange(d1, d2):
-	import datetime
-	return (d1 + datetime.timedelta(days=i) for i in range((d2 - d1).days + 1))
-
-@frappe.whitelist()
-def get_attendance_years():
-	year_list = frappe.db.sql_list('''select distinct YEAR(date) from `tabStudent Attendance` ORDER BY YEAR(date) DESC''')
-	if not year_list:
-		year_list = [getdate().year]
-	return "\n".join(str(year) for year in year_list)
-
-def mark_holidays(att_map, from_date, to_date, students_list):
-	holiday_list = get_holiday_list()
-	holidays = get_holidays(holiday_list)
-
-	for dt in daterange(getdate(from_date), getdate(to_date)):
-		if dt in holidays:
-			for student in students_list:
-				att_map.setdefault(student, frappe._dict()).setdefault(dt, "Holiday")
-
-	return att_map
diff --git a/erpnext/education/setup.py b/erpnext/education/setup.py
deleted file mode 100644
index b716926..0000000
--- a/erpnext/education/setup.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from erpnext.setup.utils import insert_record
-
-
-def setup_education():
-	disable_desk_access_for_student_role()
-	if frappe.db.exists("Academic Year", "2015-16"):
-		# already setup
-		return
-	create_academic_sessions()
-
-def create_academic_sessions():
-	data = [
-		{"doctype": "Academic Year", "academic_year_name": "2015-16"},
-		{"doctype": "Academic Year", "academic_year_name": "2016-17"},
-		{"doctype": "Academic Year", "academic_year_name": "2017-18"},
-		{"doctype": "Academic Year", "academic_year_name": "2018-19"},
-		{"doctype": "Academic Term", "academic_year": "2016-17", "term_name": "Semester 1"},
-		{"doctype": "Academic Term", "academic_year": "2016-17", "term_name": "Semester 2"},
-		{"doctype": "Academic Term", "academic_year": "2017-18", "term_name": "Semester 1"},
-		{"doctype": "Academic Term", "academic_year": "2017-18", "term_name": "Semester 2"}
-	]
-	insert_record(data)
-
-def disable_desk_access_for_student_role():
-	try:
-		student_role = frappe.get_doc("Role", "Student")
-	except frappe.DoesNotExistError:
-		create_student_role()
-		return
-
-	student_role.desk_access = 0
-	student_role.save()
-
-def create_student_role():
-	student_role = frappe.get_doc({
-		"doctype": "Role",
-		"role_name": "Student",
-		"desk_access": 0,
-		"restrict_to_domain": "Education"
-	})
-	student_role.insert()
diff --git a/erpnext/education/utils.py b/erpnext/education/utils.py
deleted file mode 100644
index a7a15d1..0000000
--- a/erpnext/education/utils.py
+++ /dev/null
@@ -1,392 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies and contributors
-
-import frappe
-from frappe import _
-
-
-class OverlapError(frappe.ValidationError): pass
-
-def validate_overlap_for(doc, doctype, fieldname, value=None):
-	"""Checks overlap for specified field.
-
-	:param fieldname: Checks Overlap for this field
-	"""
-
-	existing = get_overlap_for(doc, doctype, fieldname, value)
-	if existing:
-		frappe.throw(_("This {0} conflicts with {1} for {2} {3}").format(doc.doctype, existing.name,
-			doc.meta.get_label(fieldname) if not value else fieldname , value or doc.get(fieldname)), OverlapError)
-
-def get_overlap_for(doc, doctype, fieldname, value=None):
-	"""Returns overlaping document for specified field.
-
-	:param fieldname: Checks Overlap for this field
-	"""
-
-	existing = frappe.db.sql("""select name, from_time, to_time from `tab{0}`
-		where `{1}`=%(val)s and schedule_date = %(schedule_date)s and
-		(
-			(from_time > %(from_time)s and from_time < %(to_time)s) or
-			(to_time > %(from_time)s and to_time < %(to_time)s) or
-			(%(from_time)s > from_time and %(from_time)s < to_time) or
-			(%(from_time)s = from_time and %(to_time)s = to_time))
-		and name!=%(name)s and docstatus!=2""".format(doctype, fieldname),
-		{
-			"schedule_date": doc.schedule_date,
-			"val": value or doc.get(fieldname),
-			"from_time": doc.from_time,
-			"to_time": doc.to_time,
-			"name": doc.name or "No Name"
-		}, as_dict=True)
-
-	return existing[0] if existing else None
-
-
-def validate_duplicate_student(students):
-	unique_students= []
-	for stud in students:
-		if stud.student in unique_students:
-			frappe.throw(_("Student {0} - {1} appears Multiple times in row {2} & {3}")
-				.format(stud.student, stud.student_name, unique_students.index(stud.student)+1, stud.idx))
-		else:
-			unique_students.append(stud.student)
-
-		return None
-
-# LMS Utils
-def get_current_student():
-	"""Returns current student from frappe.session.user
-
-	Returns:
-		object: Student Document
-	"""
-	email = frappe.session.user
-	if email in ('Administrator', 'Guest'):
-		return None
-	try:
-		student_id = frappe.get_all("Student", {"student_email_id": email}, ["name"])[0].name
-		return frappe.get_doc("Student", student_id)
-	except (IndexError, frappe.DoesNotExistError):
-		return None
-
-def get_portal_programs():
-	"""Returns a list of all program to be displayed on the portal
-	Programs are returned based on the following logic
-		is_published and (student_is_enrolled or student_can_self_enroll)
-
-	Returns:
-		list of dictionary: List of all programs and to be displayed on the portal along with access rights
-	"""
-	published_programs = frappe.get_all("Program", filters={"is_published": True})
-	if not published_programs:
-		return None
-
-	program_list = [frappe.get_doc("Program", program) for program in published_programs]
-	portal_programs = [{'program': program, 'has_access': allowed_program_access(program.name)} for program in program_list if allowed_program_access(program.name) or program.allow_self_enroll]
-
-	return portal_programs
-
-def allowed_program_access(program, student=None):
-	"""Returns enrollment status for current student
-
-	Args:
-		program (string): Name of the program
-		student (object): instance of Student document
-
-	Returns:
-		bool: Is current user enrolled or not
-	"""
-	if has_super_access():
-		return True
-	if not student:
-		student = get_current_student()
-	if student and get_enrollment('program', program, student.name):
-		return True
-	else:
-		return False
-
-def get_enrollment(master, document, student):
-	"""Gets enrollment for course or program
-
-	Args:
-		master (string): can either be program or course
-		document (string): program or course name
-		student (string): Student ID
-
-	Returns:
-		string: Enrollment Name if exists else returns empty string
-	"""
-	if master == 'program':
-		enrollments = frappe.get_all("Program Enrollment", filters={'student':student, 'program': document, 'docstatus': 1})
-	if master == 'course':
-		enrollments = frappe.get_all("Course Enrollment", filters={'student':student, 'course': document})
-
-	if enrollments:
-		return enrollments[0].name
-	else:
-		return None
-
-@frappe.whitelist()
-def enroll_in_program(program_name, student=None):
-	"""Enroll student in program
-
-	Args:
-		program_name (string): Name of the program to be enrolled into
-		student (string, optional): name of student who has to be enrolled, if not
-			provided, a student will be created from the current user
-
-	Returns:
-		string: name of the program enrollment document
-	"""
-	if has_super_access():
-		return
-
-	if not student == None:
-		student = frappe.get_doc("Student", student)
-	else:
-		# Check if self enrollment in allowed
-		program = frappe.get_doc('Program', program_name)
-		if not program.allow_self_enroll:
-			return frappe.throw(_("You are not allowed to enroll for this course"))
-
-		student = get_current_student()
-		if not student:
-			student = create_student_from_current_user()
-
-	# Check if student is already enrolled in program
-	enrollment = get_enrollment('program', program_name, student.name)
-	if enrollment:
-		return enrollment
-
-	# Check if self enrollment in allowed
-	program = frappe.get_doc('Program', program_name)
-	if not program.allow_self_enroll:
-		return frappe.throw(_("You are not allowed to enroll for this course"))
-
-	# Enroll in program
-	program_enrollment = student.enroll_in_program(program_name)
-	return program_enrollment.name
-
-def has_super_access():
-	"""Check if user has a role that allows full access to LMS
-
-	Returns:
-		bool: true if user has access to all lms content
-	"""
-	current_user = frappe.get_doc('User', frappe.session.user)
-	roles = set([role.role for role in current_user.roles])
-	return bool(roles & {'Administrator', 'Instructor', 'Education Manager', 'System Manager', 'Academic User'})
-
-@frappe.whitelist()
-def add_activity(course, content_type, content, program):
-	if has_super_access():
-		return None
-
-	student = get_current_student()
-	if not student:
-		return frappe.throw(_("Student with email {0} does not exist").format(frappe.session.user), frappe.DoesNotExistError)
-
-	enrollment = get_or_create_course_enrollment(course, program)
-	if content_type == 'Quiz':
-		return
-	else:
-		return enrollment.add_activity(content_type, content)
-
-@frappe.whitelist()
-def evaluate_quiz(quiz_response, quiz_name, course, program, time_taken):
-	import json
-
-	student = get_current_student()
-
-	quiz_response = json.loads(quiz_response)
-	quiz = frappe.get_doc("Quiz", quiz_name)
-	result, score, status = quiz.evaluate(quiz_response, quiz_name)
-
-	if has_super_access():
-		return {'result': result, 'score': score, 'status': status}
-
-	if student:
-		enrollment = get_or_create_course_enrollment(course, program)
-		if quiz.allowed_attempt(enrollment, quiz_name):
-			enrollment.add_quiz_activity(quiz_name, quiz_response, result, score, status, time_taken)
-			return {'result': result, 'score': score, 'status': status}
-		else:
-			return None
-
-@frappe.whitelist()
-def get_quiz(quiz_name, course):
-	try:
-		quiz = frappe.get_doc("Quiz", quiz_name)
-		questions = quiz.get_questions()
-	except Exception:
-		frappe.throw(_("Quiz {0} does not exist").format(quiz_name), frappe.DoesNotExistError)
-		return None
-
-	questions = [{
-		'name': question.name,
-		'question': question.question,
-		'type': question.question_type,
-		'options': [{'name': option.name, 'option': option.option}
-					for option in question.options],
-		} for question in questions]
-
-	if has_super_access():
-		return {
-			'questions': questions,
-			'activity': None,
-			'is_time_bound': quiz.is_time_bound,
-			'duration': quiz.duration
-		}
-
-	student = get_current_student()
-	course_enrollment = get_enrollment("course", course, student.name)
-	status, score, result, time_taken = check_quiz_completion(quiz, course_enrollment)
-	return {
-		'questions': questions,
-		'activity': {'is_complete': status, 'score': score, 'result': result, 'time_taken': time_taken},
-		'is_time_bound': quiz.is_time_bound,
-		'duration': quiz.duration
-	}
-
-def get_topic_progress(topic, course_name, program):
-	"""
-	Return the porgress of a course in a program as well as the content to continue from.
-		:param topic_name:
-		:param course_name:
-	"""
-	student = get_current_student()
-	if not student:
-		return None
-	course_enrollment = get_or_create_course_enrollment(course_name, program)
-	progress = student.get_topic_progress(course_enrollment.name, topic)
-	if not progress:
-		return None
-	count = sum([activity['is_complete'] for activity in progress])
-	if count == 0:
-		return {'completed': False, 'started': False}
-	elif count == len(progress):
-		return {'completed': True, 'started': True}
-	elif count < len(progress):
-		return {'completed': False, 'started': True}
-
-def get_course_progress(course, program):
-	"""
-	Return the porgress of a course in a program as well as the content to continue from.
-		:param topic_name:
-		:param course_name:
-	"""
-	course_progress = []
-	for course_topic in course.topics:
-		topic = frappe.get_doc("Topic", course_topic.topic)
-		progress = get_topic_progress(topic, course.name, program)
-		if progress:
-			course_progress.append(progress)
-	if course_progress:
-		number_of_completed_topics = sum([activity['completed'] for activity in course_progress])
-		total_topics = len(course_progress)
-		if total_topics == 1:
-			return course_progress[0]
-		if number_of_completed_topics == 0:
-			return {'completed': False, 'started': False}
-		if number_of_completed_topics == total_topics:
-			return {'completed': True, 'started': True}
-		if number_of_completed_topics < total_topics:
-			return {'completed': False, 'started': True}
-
-	return None
-
-def get_program_progress(program):
-	program_progress = []
-	if not program.courses:
-		return None
-	for program_course in program.courses:
-		course = frappe.get_doc("Course", program_course.course)
-		progress = get_course_progress(course, program.name)
-		if progress:
-			progress['name'] = course.name
-			progress['course'] = course.course_name
-			program_progress.append(progress)
-
-	if program_progress:
-		return program_progress
-
-	return None
-
-def get_program_completion(program):
-	topics = frappe.db.sql("""select `tabCourse Topic`.topic, `tabCourse Topic`.parent
-	from `tabCourse Topic`,
-		 `tabProgram Course`
-	where `tabCourse Topic`.parent = `tabProgram Course`.course
-			and `tabProgram Course`.parent = %s""", program.name)
-
-	progress = []
-	for topic in topics:
-		topic_doc = frappe.get_doc('Topic', topic[0])
-		topic_progress = get_topic_progress(topic_doc, topic[1], program.name)
-		if topic_progress:
-			progress.append(topic_progress)
-
-	if progress:
-		number_of_completed_topics = sum([activity['completed'] for activity in progress if activity])
-		total_topics = len(progress)
-		try:
-			return int((float(number_of_completed_topics)/total_topics)*100)
-		except ZeroDivisionError:
-			return 0
-
-	return 0
-
-def create_student_from_current_user():
-	user = frappe.get_doc("User", frappe.session.user)
-
-	student = frappe.get_doc({
-		"doctype": "Student",
-		"first_name": user.first_name,
-		"last_name": user.last_name,
-		"student_email_id": user.email,
-		"user": frappe.session.user
-		})
-
-	student.save(ignore_permissions=True)
-	return student
-
-def get_or_create_course_enrollment(course, program):
-	student = get_current_student()
-	course_enrollment = get_enrollment("course", course, student.name)
-	if not course_enrollment:
-		program_enrollment = get_enrollment('program', program.name, student.name)
-		if not program_enrollment:
-			frappe.throw(_("You are not enrolled in program {0}").format(program))
-			return
-		return student.enroll_in_course(course_name=course, program_enrollment=get_enrollment('program', program.name, student.name))
-	else:
-		return frappe.get_doc('Course Enrollment', course_enrollment)
-
-def check_content_completion(content_name, content_type, enrollment_name):
-	activity = frappe.get_all("Course Activity", filters={'enrollment': enrollment_name, 'content_type': content_type, 'content': content_name})
-	if activity:
-		return True
-	else:
-		return False
-
-def check_quiz_completion(quiz, enrollment_name):
-	attempts = frappe.get_all("Quiz Activity",
-		filters={
-			'enrollment': enrollment_name,
-			'quiz': quiz.name
-		},
-		fields=["name", "activity_date", "score", "status", "time_taken"]
-	)
-	status = False if quiz.max_attempts == 0 else bool(len(attempts) >= quiz.max_attempts)
-	score = None
-	result = None
-	time_taken = None
-	if attempts:
-		if quiz.grading_basis == 'Last Highest Score':
-			attempts = sorted(attempts, key = lambda i: int(i.score), reverse=True)
-		score = attempts[0]['score']
-		result = attempts[0]['status']
-		time_taken = attempts[0]['time_taken']
-		if result == 'Pass':
-			status = True
-	return status, score, result, time_taken
diff --git a/erpnext/education/web_form/__init__.py b/erpnext/education/web_form/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/web_form/__init__.py
+++ /dev/null
diff --git a/erpnext/education/web_form/student_applicant/__init__.py b/erpnext/education/web_form/student_applicant/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/education/web_form/student_applicant/__init__.py
+++ /dev/null
diff --git a/erpnext/education/web_form/student_applicant/student_applicant.js b/erpnext/education/web_form/student_applicant/student_applicant.js
deleted file mode 100644
index ffc5e98..0000000
--- a/erpnext/education/web_form/student_applicant/student_applicant.js
+++ /dev/null
@@ -1,3 +0,0 @@
-frappe.ready(function() {
-	// bind events here
-})
diff --git a/erpnext/education/web_form/student_applicant/student_applicant.json b/erpnext/education/web_form/student_applicant/student_applicant.json
deleted file mode 100644
index 7b4eaa1..0000000
--- a/erpnext/education/web_form/student_applicant/student_applicant.json
+++ /dev/null
@@ -1,250 +0,0 @@
-{
- "accept_payment": 0,
- "allow_comments": 0,
- "allow_delete": 0,
- "allow_edit": 1,
- "allow_incomplete": 0,
- "allow_multiple": 1,
- "allow_print": 0,
- "amount": 0.0,
- "amount_based_on_field": 0,
- "apply_document_permissions": 0,
- "creation": "2016-09-22 13:10:10.792735",
- "doc_type": "Student Applicant",
- "docstatus": 0,
- "doctype": "Web Form",
- "idx": 0,
- "is_standard": 1,
- "login_required": 1,
- "max_attachment_size": 0,
- "modified": "2020-10-07 23:13:07.814941",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "student-applicant",
- "owner": "Administrator",
- "payment_button_label": "Buy Now",
- "published": 1,
- "route": "student-applicant",
- "route_to_success_link": 0,
- "show_attachments": 0,
- "show_in_grid": 0,
- "show_sidebar": 1,
- "sidebar_items": [],
- "success_url": "/student-applicant",
- "title": "Student Applicant",
- "web_form_fields": [
-  {
-   "allow_read_on_all_link_options": 0,
-   "fieldname": "first_name",
-   "fieldtype": "Data",
-   "hidden": 0,
-   "label": "First Name",
-   "max_length": 0,
-   "max_value": 0,
-   "read_only": 0,
-   "reqd": 1,
-   "show_in_filter": 0
-  },
-  {
-   "allow_read_on_all_link_options": 0,
-   "fieldname": "middle_name",
-   "fieldtype": "Data",
-   "hidden": 0,
-   "label": "Middle Name",
-   "max_length": 0,
-   "max_value": 0,
-   "read_only": 0,
-   "reqd": 0,
-   "show_in_filter": 0
-  },
-  {
-   "allow_read_on_all_link_options": 0,
-   "fieldname": "last_name",
-   "fieldtype": "Data",
-   "hidden": 0,
-   "label": "Last Name",
-   "max_length": 0,
-   "max_value": 0,
-   "read_only": 0,
-   "reqd": 0,
-   "show_in_filter": 0
-  },
-  {
-   "allow_read_on_all_link_options": 1,
-   "fieldname": "program",
-   "fieldtype": "Link",
-   "hidden": 0,
-   "label": "Program",
-   "max_length": 0,
-   "max_value": 0,
-   "options": "Program",
-   "read_only": 0,
-   "reqd": 1,
-   "show_in_filter": 0
-  },
-  {
-   "allow_read_on_all_link_options": 1,
-   "fieldname": "academic_year",
-   "fieldtype": "Link",
-   "hidden": 0,
-   "label": "Academic Year",
-   "max_length": 0,
-   "max_value": 0,
-   "options": "Academic Year",
-   "read_only": 0,
-   "reqd": 0,
-   "show_in_filter": 0
-  },
-  {
-   "allow_read_on_all_link_options": 1,
-   "fieldname": "academic_term",
-   "fieldtype": "Link",
-   "hidden": 0,
-   "label": "Academic Term",
-   "max_length": 0,
-   "max_value": 0,
-   "options": "Academic Term",
-   "read_only": 0,
-   "reqd": 0,
-   "show_in_filter": 0
-  },
-  {
-   "allow_read_on_all_link_options": 0,
-   "fieldname": "date_of_birth",
-   "fieldtype": "Date",
-   "hidden": 0,
-   "label": "Date of Birth",
-   "max_length": 0,
-   "max_value": 0,
-   "read_only": 0,
-   "reqd": 0,
-   "show_in_filter": 0
-  },
-  {
-   "allow_read_on_all_link_options": 1,
-   "fieldname": "gender",
-   "fieldtype": "Link",
-   "hidden": 0,
-   "label": "Gender",
-   "max_length": 0,
-   "max_value": 0,
-   "options": "Gender",
-   "read_only": 0,
-   "reqd": 0,
-   "show_in_filter": 0
-  },
-  {
-   "allow_read_on_all_link_options": 0,
-   "fieldname": "blood_group",
-   "fieldtype": "Select",
-   "hidden": 0,
-   "label": "Blood Group",
-   "max_length": 0,
-   "max_value": 0,
-   "options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-",
-   "read_only": 0,
-   "reqd": 0,
-   "show_in_filter": 0
-  },
-  {
-   "allow_read_on_all_link_options": 0,
-   "fieldname": "student_email_id",
-   "fieldtype": "Data",
-   "hidden": 0,
-   "label": "Student Email ID",
-   "max_length": 0,
-   "max_value": 0,
-   "read_only": 0,
-   "reqd": 1,
-   "show_in_filter": 0
-  },
-  {
-   "allow_read_on_all_link_options": 0,
-   "fieldname": "student_mobile_number",
-   "fieldtype": "Data",
-   "hidden": 0,
-   "label": "Student Mobile Number",
-   "max_length": 0,
-   "max_value": 0,
-   "read_only": 0,
-   "reqd": 0,
-   "show_in_filter": 0
-  },
-  {
-   "allow_read_on_all_link_options": 0,
-   "default": "",
-   "fieldname": "nationality",
-   "fieldtype": "Data",
-   "hidden": 0,
-   "label": "Nationality",
-   "max_length": 0,
-   "max_value": 0,
-   "options": "",
-   "read_only": 0,
-   "reqd": 0,
-   "show_in_filter": 0
-  },
-  {
-   "allow_read_on_all_link_options": 0,
-   "fieldname": "address_line_1",
-   "fieldtype": "Data",
-   "hidden": 0,
-   "label": "Address Line 1",
-   "max_length": 0,
-   "max_value": 0,
-   "read_only": 0,
-   "reqd": 0,
-   "show_in_filter": 0
-  },
-  {
-   "allow_read_on_all_link_options": 0,
-   "fieldname": "address_line_2",
-   "fieldtype": "Data",
-   "hidden": 0,
-   "label": "Address Line 2",
-   "max_length": 0,
-   "max_value": 0,
-   "read_only": 0,
-   "reqd": 0,
-   "show_in_filter": 0
-  },
-  {
-   "allow_read_on_all_link_options": 0,
-   "fieldname": "pincode",
-   "fieldtype": "Data",
-   "hidden": 0,
-   "label": "Pincode",
-   "max_length": 0,
-   "max_value": 0,
-   "read_only": 0,
-   "reqd": 0,
-   "show_in_filter": 0
-  },
-  {
-   "allow_read_on_all_link_options": 0,
-   "fieldname": "siblings",
-   "fieldtype": "Table",
-   "hidden": 0,
-   "label": "Siblings",
-   "max_length": 0,
-   "max_value": 0,
-   "options": "Student Sibling",
-   "read_only": 0,
-   "reqd": 0,
-   "show_in_filter": 0
-  },
-  {
-   "allow_read_on_all_link_options": 0,
-   "fieldname": "student_admission",
-   "fieldtype": "Link",
-   "hidden": 0,
-   "label": "Student Admission",
-   "max_length": 0,
-   "max_value": 0,
-   "options": "Student Admission",
-   "read_only": 0,
-   "reqd": 0,
-   "show_in_filter": 0
-  }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/education/web_form/student_applicant/student_applicant.py b/erpnext/education/web_form/student_applicant/student_applicant.py
deleted file mode 100644
index 02e3e93..0000000
--- a/erpnext/education/web_form/student_applicant/student_applicant.py
+++ /dev/null
@@ -1,3 +0,0 @@
-def get_context(context):
-	# do your magic here
-	pass
diff --git a/erpnext/education/workspace/education/education.json b/erpnext/education/workspace/education/education.json
deleted file mode 100644
index 0c7f198..0000000
--- a/erpnext/education/workspace/education/education.json
+++ /dev/null
@@ -1,765 +0,0 @@
-{
- "charts": [
-  {
-   "chart_name": "Program Enrollments",
-   "label": "Program Enrollments"
-  }
- ],
- "content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Education\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Program Enrollments\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Student\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Instructor\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Program\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Course\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Fees\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Student Monthly Attendance Sheet\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Course Scheduling Tool\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Student Attendance Tool\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Student and Instructor\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Masters\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Content Masters\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Admission\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Fees\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Schedule\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Attendance\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"LMS Activity\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Assessment\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Assessment Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]",
- "creation": "2020-03-02 17:22:57.066401",
- "docstatus": 0,
- "doctype": "Workspace",
- "for_user": "",
- "hide_custom": 0,
- "icon": "education",
- "idx": 0,
- "label": "Education",
- "links": [
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Student and Instructor",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Student",
-   "link_count": 0,
-   "link_to": "Student",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Instructor",
-   "link_count": 0,
-   "link_to": "Instructor",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Guardian",
-   "link_count": 0,
-   "link_to": "Guardian",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Student Group",
-   "link_count": 0,
-   "link_to": "Student Group",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Student Log",
-   "link_count": 0,
-   "link_to": "Student Log",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Masters",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Program",
-   "link_count": 0,
-   "link_to": "Program",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Course",
-   "link_count": 0,
-   "link_to": "Course",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Topic",
-   "link_count": 0,
-   "link_to": "Topic",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Room",
-   "link_count": 0,
-   "link_to": "Room",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Content Masters",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Article",
-   "link_count": 0,
-   "link_to": "Article",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Video",
-   "link_count": 0,
-   "link_to": "Video",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Quiz",
-   "link_count": 0,
-   "link_to": "Quiz",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Settings",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Education Settings",
-   "link_count": 0,
-   "link_to": "Education Settings",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Student Category",
-   "link_count": 0,
-   "link_to": "Student Category",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Student Batch Name",
-   "link_count": 0,
-   "link_to": "Student Batch Name",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Grading Scale",
-   "link_count": 0,
-   "link_to": "Grading Scale",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Academic Term",
-   "link_count": 0,
-   "link_to": "Academic Term",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Academic Year",
-   "link_count": 0,
-   "link_to": "Academic Year",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Admission",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Student Applicant",
-   "link_count": 0,
-   "link_to": "Student Applicant",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Student Admission",
-   "link_count": 0,
-   "link_to": "Student Admission",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Program Enrollment",
-   "link_count": 0,
-   "link_to": "Program Enrollment",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Course Enrollment",
-   "link_count": 0,
-   "link_to": "Course Enrollment",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Fees",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Fee Structure",
-   "link_count": 0,
-   "link_to": "Fee Structure",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Fee Category",
-   "link_count": 0,
-   "link_to": "Fee Category",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Fee Schedule",
-   "link_count": 0,
-   "link_to": "Fee Schedule",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Fees",
-   "link_count": 0,
-   "link_to": "Fees",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "Fees",
-   "hidden": 0,
-   "is_query_report": 1,
-   "label": "Student Fee Collection Report",
-   "link_count": 0,
-   "link_to": "Student Fee Collection",
-   "link_type": "Report",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "Fees",
-   "hidden": 0,
-   "is_query_report": 1,
-   "label": "Program wise Fee Collection Report",
-   "link_count": 0,
-   "link_to": "Program wise Fee Collection",
-   "link_type": "Report",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Schedule",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Course Schedule",
-   "link_count": 0,
-   "link_to": "Course Schedule",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Course Scheduling Tool",
-   "link_count": 0,
-   "link_to": "Course Scheduling Tool",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Attendance",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Student Attendance",
-   "link_count": 0,
-   "link_to": "Student Attendance",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Student Leave Application",
-   "link_count": 0,
-   "link_to": "Student Leave Application",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "Student Attendance",
-   "hidden": 0,
-   "is_query_report": 1,
-   "label": "Student Monthly Attendance Sheet",
-   "link_count": 0,
-   "link_to": "Student Monthly Attendance Sheet",
-   "link_type": "Report",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "Student Attendance",
-   "hidden": 0,
-   "is_query_report": 1,
-   "label": "Absent Student Report",
-   "link_count": 0,
-   "link_to": "Absent Student Report",
-   "link_type": "Report",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "Student Attendance",
-   "hidden": 0,
-   "is_query_report": 1,
-   "label": "Student Batch-Wise Attendance",
-   "link_count": 0,
-   "link_to": "Student Batch-Wise Attendance",
-   "link_type": "Report",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "LMS Activity",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Course Enrollment",
-   "link_count": 0,
-   "link_to": "Course Enrollment",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Course Activity",
-   "link_count": 0,
-   "link_to": "Course Activity",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Quiz Activity",
-   "link_count": 0,
-   "link_to": "Quiz Activity",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Assessment",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Assessment Plan",
-   "link_count": 0,
-   "link_to": "Assessment Plan",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Assessment Group",
-   "link_count": 0,
-   "link_to": "Assessment Group",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Assessment Result",
-   "link_count": 0,
-   "link_to": "Assessment Result",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Assessment Criteria",
-   "link_count": 0,
-   "link_to": "Assessment Criteria",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Assessment Reports",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "Assessment Result",
-   "hidden": 0,
-   "is_query_report": 1,
-   "label": "Course wise Assessment Report",
-   "link_count": 0,
-   "link_to": "Course wise Assessment Report",
-   "link_type": "Report",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "Assessment Result",
-   "hidden": 0,
-   "is_query_report": 1,
-   "label": "Final Assessment Grades",
-   "link_count": 0,
-   "link_to": "Final Assessment Grades",
-   "link_type": "Report",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "Assessment Plan",
-   "hidden": 0,
-   "is_query_report": 1,
-   "label": "Assessment Plan Status",
-   "link_count": 0,
-   "link_to": "Assessment Plan Status",
-   "link_type": "Report",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Student Report Generation Tool",
-   "link_count": 0,
-   "link_to": "Student Report Generation Tool",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Tools",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Student Attendance Tool",
-   "link_count": 0,
-   "link_to": "Student Attendance Tool",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Assessment Result Tool",
-   "link_count": 0,
-   "link_to": "Assessment Result Tool",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Student Group Creation Tool",
-   "link_count": 0,
-   "link_to": "Student Group Creation Tool",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Program Enrollment Tool",
-   "link_count": 0,
-   "link_to": "Program Enrollment Tool",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Course Scheduling Tool",
-   "link_count": 0,
-   "link_to": "Course Scheduling Tool",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Other Reports",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "Program Enrollment",
-   "hidden": 0,
-   "is_query_report": 1,
-   "label": "Student and Guardian Contact Details",
-   "link_count": 0,
-   "link_to": "Student and Guardian Contact Details",
-   "link_type": "Report",
-   "onboard": 0,
-   "type": "Link"
-  }
- ],
- "modified": "2022-01-13 17:29:13.676542",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Education",
- "owner": "Administrator",
- "parent_page": "",
- "public": 1,
- "restrict_to_domain": "Education",
- "roles": [],
- "sequence_id": 9.0,
- "shortcuts": [
-  {
-   "color": "Grey",
-   "format": "{} Active",
-   "label": "Student",
-   "link_to": "Student",
-   "stats_filter": "{\n    \"enabled\": 1\n}",
-   "type": "DocType"
-  },
-  {
-   "color": "Grey",
-   "format": "{} Active",
-   "label": "Instructor",
-   "link_to": "Instructor",
-   "stats_filter": "{\n    \"status\": \"Active\"\n}",
-   "type": "DocType"
-  },
-  {
-   "color": "",
-   "format": "",
-   "label": "Program",
-   "link_to": "Program",
-   "stats_filter": "",
-   "type": "DocType"
-  },
-  {
-   "label": "Course",
-   "link_to": "Course",
-   "type": "DocType"
-  },
-  {
-   "color": "Grey",
-   "format": "{} Unpaid",
-   "label": "Fees",
-   "link_to": "Fees",
-   "stats_filter": "{\n    \"outstanding_amount\": [\"!=\", 0.0]\n}",
-   "type": "DocType"
-  },
-  {
-   "label": "Student Monthly Attendance Sheet",
-   "link_to": "Student Monthly Attendance Sheet",
-   "type": "Report"
-  },
-  {
-   "label": "Course Scheduling Tool",
-   "link_to": "Course Scheduling Tool",
-   "type": "DocType"
-  },
-  {
-   "label": "Student Attendance Tool",
-   "link_to": "Student Attendance Tool",
-   "type": "DocType"
-  },
-  {
-   "label": "Dashboard",
-   "link_to": "Education",
-   "type": "Dashboard"
-  }
- ],
- "title": "Education"
-}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/connectors/github_connection.py b/erpnext/erpnext_integrations/connectors/github_connection.py
deleted file mode 100644
index f28065e..0000000
--- a/erpnext/erpnext_integrations/connectors/github_connection.py
+++ /dev/null
@@ -1,44 +0,0 @@
-import frappe
-from frappe.data_migration.doctype.data_migration_connector.connectors.base import BaseConnection
-from github import Github
-
-class GithubConnection(BaseConnection):
-	def __init__(self, connector):
-		self.connector = connector
-
-		try:
-			password = self.get_password()
-		except frappe.AuthenticationError:
-			password = None
-
-		if self.connector.username and password:
-			self.connection = Github(self.connector.username, self.get_password())
-		else:
-			self.connection = Github()
-
-		self.name_field = 'id'
-
-	def insert(self, doctype, doc):
-		pass
-
-	def update(self, doctype, doc, migration_id):
-		pass
-
-	def delete(self, doctype, migration_id):
-		pass
-
-	def get(self, remote_objectname, fields=None, filters=None, start=0, page_length=10):
-		repo = filters.get('repo')
-
-		if remote_objectname == 'Milestone':
-			return self.get_milestones(repo, start, page_length)
-		if remote_objectname == 'Issue':
-			return self.get_issues(repo, start, page_length)
-
-	def get_milestones(self, repo, start=0, page_length=10):
-		_repo = self.connection.get_repo(repo)
-		return list(_repo.get_milestones()[start:start+page_length])
-
-	def get_issues(self, repo, start=0, page_length=10):
-		_repo = self.connection.get_repo(repo)
-		return list(_repo.get_issues()[start:start+page_length])
diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py
index 9409485..6d977e0 100644
--- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py
+++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py
@@ -12,26 +12,30 @@
 	woocommerce_settings = frappe.get_doc("Woocommerce Settings")
 	sig = base64.b64encode(
 		hmac.new(
-			woocommerce_settings.secret.encode('utf8'),
-			frappe.request.data,
-			hashlib.sha256
+			woocommerce_settings.secret.encode("utf8"), frappe.request.data, hashlib.sha256
 		).digest()
 	)
 
-	if frappe.request.data and \
-		not sig == frappe.get_request_header("X-Wc-Webhook-Signature", "").encode():
-			frappe.throw(_("Unverified Webhook Data"))
+	if (
+		frappe.request.data
+		and not sig == frappe.get_request_header("X-Wc-Webhook-Signature", "").encode()
+	):
+		frappe.throw(_("Unverified Webhook Data"))
 	frappe.set_user(woocommerce_settings.creation_user)
 
+
 @frappe.whitelist(allow_guest=True)
 def order(*args, **kwargs):
 	try:
 		_order(*args, **kwargs)
 	except Exception:
-		error_message = frappe.get_traceback()+"\n\n Request Data: \n"+json.loads(frappe.request.data).__str__()
-		frappe.log_error(error_message, "WooCommerce Error")
+		error_message = (
+			frappe.get_traceback() + "\n\n Request Data: \n" + json.loads(frappe.request.data).__str__()
+		)
+		frappe.log_error("WooCommerce Error", error_message)
 		raise
 
+
 def _order(*args, **kwargs):
 	woocommerce_settings = frappe.get_doc("Woocommerce Settings")
 	if frappe.flags.woocomm_test_order_data:
@@ -43,7 +47,7 @@
 		try:
 			order = json.loads(frappe.request.data)
 		except ValueError:
-			#woocommerce returns 'webhook_id=value' for the first request which is not JSON
+			# woocommerce returns 'webhook_id=value' for the first request which is not JSON
 			order = frappe.request.data
 		event = frappe.get_request_header("X-Wc-Webhook-Event")
 
@@ -51,7 +55,7 @@
 		return "success"
 
 	if event == "created":
-		sys_lang = frappe.get_single("System Settings").language or 'en'
+		sys_lang = frappe.get_single("System Settings").language or "en"
 		raw_billing_data = order.get("billing")
 		raw_shipping_data = order.get("shipping")
 		customer_name = raw_billing_data.get("first_name") + " " + raw_billing_data.get("last_name")
@@ -59,6 +63,7 @@
 		link_items(order.get("line_items"), woocommerce_settings, sys_lang)
 		create_sales_order(order, woocommerce_settings, customer_name, sys_lang)
 
+
 def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name):
 	customer_woo_com_email = raw_billing_data.get("email")
 	customer_exists = frappe.get_value("Customer", {"woocommerce_email": customer_woo_com_email})
@@ -77,9 +82,14 @@
 
 	if customer_exists:
 		frappe.rename_doc("Customer", old_name, customer_name)
-		for address_type in ("Billing", "Shipping",):
+		for address_type in (
+			"Billing",
+			"Shipping",
+		):
 			try:
-				address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": address_type})
+				address = frappe.get_doc(
+					"Address", {"woocommerce_email": customer_woo_com_email, "address_type": address_type}
+				)
 				rename_address(address, customer)
 			except (
 				frappe.DoesNotExistError,
@@ -92,6 +102,7 @@
 		create_address(raw_shipping_data, customer, "Shipping")
 		create_contact(raw_billing_data, customer)
 
+
 def create_contact(data, customer):
 	email = data.get("email", None)
 	phone = data.get("phone", None)
@@ -111,14 +122,12 @@
 	if email:
 		contact.add_email(email, is_primary=1)
 
-	contact.append("links", {
-		"link_doctype": "Customer",
-		"link_name": customer.name
-	})
+	contact.append("links", {"link_doctype": "Customer", "link_name": customer.name})
 
 	contact.flags.ignore_mandatory = True
 	contact.save()
 
+
 def create_address(raw_data, customer, address_type):
 	address = frappe.new_doc("Address")
 
@@ -132,14 +141,12 @@
 	address.pincode = raw_data.get("postcode")
 	address.phone = raw_data.get("phone")
 	address.email_id = customer.woocommerce_email
-	address.append("links", {
-		"link_doctype": "Customer",
-		"link_name": customer.name
-	})
+	address.append("links", {"link_doctype": "Customer", "link_name": customer.name})
 
 	address.flags.ignore_mandatory = True
 	address.save()
 
+
 def rename_address(address, customer):
 	old_address_title = address.name
 	new_address_title = customer.name + "-" + address.address_type
@@ -148,12 +155,13 @@
 
 	frappe.rename_doc("Address", old_address_title, new_address_title)
 
+
 def link_items(items_list, woocommerce_settings, sys_lang):
 	for item_data in items_list:
 		item_woo_com_id = cstr(item_data.get("product_id"))
 
-		if not frappe.db.get_value("Item", {"woocommerce_id": item_woo_com_id}, 'name'):
-			#Create Item
+		if not frappe.db.get_value("Item", {"woocommerce_id": item_woo_com_id}, "name"):
+			# Create Item
 			item = frappe.new_doc("Item")
 			item.item_code = _("woocommerce - {0}", sys_lang).format(item_woo_com_id)
 			item.stock_uom = woocommerce_settings.uom or _("Nos", sys_lang)
@@ -164,6 +172,7 @@
 			item.flags.ignore_mandatory = True
 			item.save()
 
+
 def create_sales_order(order, woocommerce_settings, customer_name, sys_lang):
 	new_sales_order = frappe.new_doc("Sales Order")
 	new_sales_order.customer = customer_name
@@ -185,12 +194,12 @@
 
 	frappe.db.commit()
 
+
 def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_lang):
-	company_abbr = frappe.db.get_value('Company', woocommerce_settings.company, 'abbr')
+	company_abbr = frappe.db.get_value("Company", woocommerce_settings.company, "abbr")
 
 	default_warehouse = _("Stores - {0}", sys_lang).format(company_abbr)
-	if not frappe.db.exists("Warehouse", default_warehouse) \
-		and not woocommerce_settings.warehouse:
+	if not frappe.db.exists("Warehouse", default_warehouse) and not woocommerce_settings.warehouse:
 		frappe.throw(_("Please set Warehouse in Woocommerce Settings"))
 
 	for item in order.get("line_items"):
@@ -199,28 +208,44 @@
 
 		ordered_items_tax = item.get("total_tax")
 
-		new_sales_order.append("items", {
-			"item_code": found_item.name,
-			"item_name": found_item.item_name,
-			"description": found_item.item_name,
-			"delivery_date": new_sales_order.delivery_date,
-			"uom": woocommerce_settings.uom or _("Nos", sys_lang),
-			"qty": item.get("quantity"),
-			"rate": item.get("price"),
-			"warehouse": woocommerce_settings.warehouse or default_warehouse
-		})
+		new_sales_order.append(
+			"items",
+			{
+				"item_code": found_item.name,
+				"item_name": found_item.item_name,
+				"description": found_item.item_name,
+				"delivery_date": new_sales_order.delivery_date,
+				"uom": woocommerce_settings.uom or _("Nos", sys_lang),
+				"qty": item.get("quantity"),
+				"rate": item.get("price"),
+				"warehouse": woocommerce_settings.warehouse or default_warehouse,
+			},
+		)
 
-		add_tax_details(new_sales_order, ordered_items_tax, "Ordered Item tax", woocommerce_settings.tax_account)
+		add_tax_details(
+			new_sales_order, ordered_items_tax, "Ordered Item tax", woocommerce_settings.tax_account
+		)
 
 	# shipping_details = order.get("shipping_lines") # used for detailed order
 
-	add_tax_details(new_sales_order, order.get("shipping_tax"), "Shipping Tax", woocommerce_settings.f_n_f_account)
-	add_tax_details(new_sales_order, order.get("shipping_total"), "Shipping Total", woocommerce_settings.f_n_f_account)
+	add_tax_details(
+		new_sales_order, order.get("shipping_tax"), "Shipping Tax", woocommerce_settings.f_n_f_account
+	)
+	add_tax_details(
+		new_sales_order,
+		order.get("shipping_total"),
+		"Shipping Total",
+		woocommerce_settings.f_n_f_account,
+	)
+
 
 def add_tax_details(sales_order, price, desc, tax_account_head):
-	sales_order.append("taxes", {
-		"charge_type":"Actual",
-		"account_head": tax_account_head,
-		"tax_amount": price,
-		"description": desc
-	})
+	sales_order.append(
+		"taxes",
+		{
+			"charge_type": "Actual",
+			"account_head": tax_account_head,
+			"tax_amount": price,
+			"description": desc,
+		},
+	)
diff --git a/erpnext/erpnext_integrations/data_migration_mapping/__init__.py b/erpnext/erpnext_integrations/data_migration_mapping/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/erpnext_integrations/data_migration_mapping/__init__.py
+++ /dev/null
diff --git a/erpnext/erpnext_integrations/data_migration_mapping/issue_to_task/__init__.py b/erpnext/erpnext_integrations/data_migration_mapping/issue_to_task/__init__.py
deleted file mode 100644
index 1d0dfa5..0000000
--- a/erpnext/erpnext_integrations/data_migration_mapping/issue_to_task/__init__.py
+++ /dev/null
@@ -1,12 +0,0 @@
-import frappe
-
-
-def pre_process(issue):
-
-	project = frappe.db.get_value('Project', filters={'project_name': issue.milestone})
-	return {
-		'title': issue.title,
-		'body': frappe.utils.md_to_html(issue.body or ''),
-		'state': issue.state.title(),
-		'project': project or ''
-	}
diff --git a/erpnext/erpnext_integrations/data_migration_mapping/issue_to_task/issue_to_task.json b/erpnext/erpnext_integrations/data_migration_mapping/issue_to_task/issue_to_task.json
deleted file mode 100644
index e945ba2..0000000
--- a/erpnext/erpnext_integrations/data_migration_mapping/issue_to_task/issue_to_task.json
+++ /dev/null
@@ -1,36 +0,0 @@
-{
- "condition": "{\"repo\":\"frappe/erpnext\"}", 
- "creation": "2017-10-16 16:03:32.772191", 
- "docstatus": 0, 
- "doctype": "Data Migration Mapping", 
- "fields": [
-  {
-   "is_child_table": 0, 
-   "local_fieldname": "subject", 
-   "remote_fieldname": "title"
-  }, 
-  {
-   "is_child_table": 0, 
-   "local_fieldname": "description", 
-   "remote_fieldname": "body"
-  }, 
-  {
-   "is_child_table": 0, 
-   "local_fieldname": "status", 
-   "remote_fieldname": "state"
-  }
- ], 
- "idx": 0, 
- "local_doctype": "Task", 
- "local_primary_key": "name", 
- "mapping_name": "Issue to Task", 
- "mapping_type": "Pull", 
- "migration_id_field": "github_sync_id", 
- "modified": "2017-10-20 11:48:54.575993", 
- "modified_by": "Administrator", 
- "name": "Issue to Task", 
- "owner": "Administrator", 
- "page_length": 10, 
- "remote_objectname": "Issue", 
- "remote_primary_key": "id"
-}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/data_migration_mapping/milestone_to_project/__init__.py b/erpnext/erpnext_integrations/data_migration_mapping/milestone_to_project/__init__.py
deleted file mode 100644
index 212f81b..0000000
--- a/erpnext/erpnext_integrations/data_migration_mapping/milestone_to_project/__init__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-def pre_process(milestone):
-	return {
-		'title': milestone.title,
-		'description': milestone.description,
-		'state': milestone.state.title()
-	}
diff --git a/erpnext/erpnext_integrations/data_migration_mapping/milestone_to_project/milestone_to_project.json b/erpnext/erpnext_integrations/data_migration_mapping/milestone_to_project/milestone_to_project.json
deleted file mode 100644
index 5a3e07e..0000000
--- a/erpnext/erpnext_integrations/data_migration_mapping/milestone_to_project/milestone_to_project.json
+++ /dev/null
@@ -1,36 +0,0 @@
-{
- "condition": "{\"repo\": \"frappe/erpnext\"}", 
- "creation": "2017-10-13 11:16:49.664925", 
- "docstatus": 0, 
- "doctype": "Data Migration Mapping", 
- "fields": [
-  {
-   "is_child_table": 0, 
-   "local_fieldname": "project_name", 
-   "remote_fieldname": "title"
-  }, 
-  {
-   "is_child_table": 0, 
-   "local_fieldname": "notes", 
-   "remote_fieldname": "description"
-  }, 
-  {
-   "is_child_table": 0, 
-   "local_fieldname": "status", 
-   "remote_fieldname": "state"
-  }
- ], 
- "idx": 0, 
- "local_doctype": "Project", 
- "local_primary_key": "project_name", 
- "mapping_name": "Milestone to Project", 
- "mapping_type": "Pull", 
- "migration_id_field": "github_sync_id", 
- "modified": "2017-10-20 11:48:54.552305", 
- "modified_by": "Administrator", 
- "name": "Milestone to Project", 
- "owner": "Administrator", 
- "page_length": 10, 
- "remote_objectname": "Milestone", 
- "remote_primary_key": "id"
-}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/data_migration_plan/github_sync/github_sync.json b/erpnext/erpnext_integrations/data_migration_plan/github_sync/github_sync.json
deleted file mode 100644
index 20eb387..0000000
--- a/erpnext/erpnext_integrations/data_migration_plan/github_sync/github_sync.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "creation": "2017-10-13 11:16:53.600026", 
- "docstatus": 0, 
- "doctype": "Data Migration Plan", 
- "idx": 0, 
- "mappings": [
-  {
-   "enabled": 1, 
-   "mapping": "Milestone to Project"
-  }, 
-  {
-   "enabled": 1, 
-   "mapping": "Issue to Task"
-  }
- ], 
- "modified": "2017-10-20 11:48:54.496123", 
- "modified_by": "Administrator", 
- "module": "ERPNext Integrations", 
- "name": "GitHub Sync", 
- "owner": "Administrator", 
- "plan_name": "GitHub Sync"
-}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py
index e84093c..4879cb5 100644
--- a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py
+++ b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py
@@ -14,7 +14,9 @@
 
 	def verify_credentials(self):
 		if self.enabled:
-			response = requests.get('https://api.exotel.com/v1/Accounts/{sid}'
-				.format(sid = self.account_sid), auth=(self.api_key, self.api_token))
+			response = requests.get(
+				"https://api.exotel.com/v1/Accounts/{sid}".format(sid=self.account_sid),
+				auth=(self.api_key, self.api_token),
+			)
 			if response.status_code != 200:
 				frappe.throw(_("Invalid credentials"))
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_settings/__init__.py b/erpnext/erpnext_integrations/doctype/gocardless_settings/__init__.py
index bb62c39..65be599 100644
--- a/erpnext/erpnext_integrations/doctype/gocardless_settings/__init__.py
+++ b/erpnext/erpnext_integrations/doctype/gocardless_settings/__init__.py
@@ -23,12 +23,15 @@
 		set_status(event)
 
 	return 200
+
+
 def set_status(event):
 	resource_type = event.get("resource_type", {})
 
 	if resource_type == "mandates":
 		set_mandate_status(event)
 
+
 def set_mandate_status(event):
 	mandates = []
 	if isinstance(event["links"], (list,)):
@@ -37,7 +40,12 @@
 	else:
 		mandates.append(event["links"]["mandate"])
 
-	if event["action"] == "pending_customer_approval" or event["action"] == "pending_submission" or event["action"] == "submitted" or event["action"] == "active":
+	if (
+		event["action"] == "pending_customer_approval"
+		or event["action"] == "pending_submission"
+		or event["action"] == "submitted"
+		or event["action"] == "active"
+	):
 		disabled = 0
 	else:
 		disabled = 1
@@ -45,6 +53,7 @@
 	for mandate in mandates:
 		frappe.db.set_value("GoCardless Mandate", mandate, "disabled", disabled)
 
+
 def authenticate_signature(r):
 	"""Returns True if the received signature matches the generated signature"""
 	received_signature = frappe.get_request_header("Webhook-Signature")
@@ -59,13 +68,22 @@
 
 	return False
 
+
 def get_webhook_keys():
 	def _get_webhook_keys():
-		webhook_keys = [d.webhooks_secret for d in frappe.get_all("GoCardless Settings", fields=["webhooks_secret"],) if d.webhooks_secret]
+		webhook_keys = [
+			d.webhooks_secret
+			for d in frappe.get_all(
+				"GoCardless Settings",
+				fields=["webhooks_secret"],
+			)
+			if d.webhooks_secret
+		]
 
 		return webhook_keys
 
 	return frappe.cache().get_value("gocardless_webhooks_secret", _get_webhook_keys)
 
+
 def clear_cache():
 	frappe.cache().delete_value("gocardless_webhooks_secret")
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.py b/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.py
index f02f76e..55517e4 100644
--- a/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.py
+++ b/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.py
@@ -22,32 +22,35 @@
 		self.environment = self.get_environment()
 		try:
 			self.client = gocardless_pro.Client(
-				access_token=self.access_token,
-				environment=self.environment
-				)
+				access_token=self.access_token, environment=self.environment
+			)
 			return self.client
 		except Exception as e:
 			frappe.throw(e)
 
 	def on_update(self):
-		create_payment_gateway('GoCardless-' + self.gateway_name, settings='GoCardLess Settings', controller=self.gateway_name)
-		call_hook_method('payment_gateway_enabled', gateway='GoCardless-' + self.gateway_name)
+		create_payment_gateway(
+			"GoCardless-" + self.gateway_name, settings="GoCardLess Settings", controller=self.gateway_name
+		)
+		call_hook_method("payment_gateway_enabled", gateway="GoCardless-" + self.gateway_name)
 
 	def on_payment_request_submission(self, data):
 		if data.reference_doctype != "Fees":
-			customer_data = frappe.db.get_value(data.reference_doctype, data.reference_name, ["company", "customer_name"], as_dict=1)
+			customer_data = frappe.db.get_value(
+				data.reference_doctype, data.reference_name, ["company", "customer_name"], as_dict=1
+			)
 
 		data = {
-					"amount": flt(data.grand_total, data.precision("grand_total")),
-					"title": customer_data.company.encode("utf-8"),
-					"description": data.subject.encode("utf-8"),
-					"reference_doctype": data.doctype,
-					"reference_docname": data.name,
-					"payer_email": data.email_to or frappe.session.user,
-					"payer_name": customer_data.customer_name,
-					"order_id": data.name,
-					"currency": data.currency
-				}
+			"amount": flt(data.grand_total, data.precision("grand_total")),
+			"title": customer_data.company.encode("utf-8"),
+			"description": data.subject.encode("utf-8"),
+			"reference_doctype": data.doctype,
+			"reference_docname": data.name,
+			"payer_email": data.email_to or frappe.session.user,
+			"payer_name": customer_data.customer_name,
+			"order_id": data.name,
+			"currency": data.currency,
+		}
 
 		valid_mandate = self.check_mandate_validity(data)
 		if valid_mandate is not None:
@@ -60,12 +63,19 @@
 
 	def check_mandate_validity(self, data):
 
-		if frappe.db.exists("GoCardless Mandate", dict(customer=data.get('payer_name'), disabled=0)):
-			registered_mandate = frappe.db.get_value("GoCardless Mandate", dict(customer=data.get('payer_name'), disabled=0), 'mandate')
+		if frappe.db.exists("GoCardless Mandate", dict(customer=data.get("payer_name"), disabled=0)):
+			registered_mandate = frappe.db.get_value(
+				"GoCardless Mandate", dict(customer=data.get("payer_name"), disabled=0), "mandate"
+			)
 			self.initialize_client()
 			mandate = self.client.mandates.get(registered_mandate)
 
-			if mandate.status=="pending_customer_approval" or mandate.status=="pending_submission" or mandate.status=="submitted" or mandate.status=="active":
+			if (
+				mandate.status == "pending_customer_approval"
+				or mandate.status == "pending_submission"
+				or mandate.status == "submitted"
+				or mandate.status == "active"
+			):
 				return {"mandate": registered_mandate}
 			else:
 				return None
@@ -74,13 +84,17 @@
 
 	def get_environment(self):
 		if self.use_sandbox:
-			return 'sandbox'
+			return "sandbox"
 		else:
-			return 'live'
+			return "live"
 
 	def validate_transaction_currency(self, currency):
 		if currency not in self.supported_currencies:
-			frappe.throw(_("Please select another payment method. Go Cardless does not support transactions in currency '{0}'").format(currency))
+			frappe.throw(
+				_(
+					"Please select another payment method. Go Cardless does not support transactions in currency '{0}'"
+				).format(currency)
+			)
 
 	def get_payment_url(self, **kwargs):
 		return get_url("./integrations/gocardless_checkout?{0}".format(urlencode(kwargs)))
@@ -93,90 +107,107 @@
 			return self.create_charge_on_gocardless()
 
 		except Exception:
-			frappe.log_error(frappe.get_traceback())
-			return{
-				"redirect_to": frappe.redirect_to_message(_('Server Error'), _("There seems to be an issue with the server's GoCardless configuration. Don't worry, in case of failure, the amount will get refunded to your account.")),
-				"status": 401
+			frappe.log_error("Gocardless payment reqeust failed")
+			return {
+				"redirect_to": frappe.redirect_to_message(
+					_("Server Error"),
+					_(
+						"There seems to be an issue with the server's GoCardless configuration. Don't worry, in case of failure, the amount will get refunded to your account."
+					),
+				),
+				"status": 401,
 			}
 
 	def create_charge_on_gocardless(self):
-		redirect_to = self.data.get('redirect_to') or None
-		redirect_message = self.data.get('redirect_message') or None
+		redirect_to = self.data.get("redirect_to") or None
+		redirect_message = self.data.get("redirect_message") or None
 
-		reference_doc = frappe.get_doc(self.data.get('reference_doctype'), self.data.get('reference_docname'))
+		reference_doc = frappe.get_doc(
+			self.data.get("reference_doctype"), self.data.get("reference_docname")
+		)
 		self.initialize_client()
 
 		try:
 			payment = self.client.payments.create(
 				params={
-					"amount" : cint(reference_doc.grand_total * 100),
-					"currency" : reference_doc.currency,
-					"links" : {
-						"mandate": self.data.get('mandate')
-					},
+					"amount": cint(reference_doc.grand_total * 100),
+					"currency": reference_doc.currency,
+					"links": {"mandate": self.data.get("mandate")},
 					"metadata": {
-					  "reference_doctype": reference_doc.doctype,
-					  "reference_document": reference_doc.name
-					}
-				}, headers={
-					'Idempotency-Key' : self.data.get('reference_docname'),
-			})
+						"reference_doctype": reference_doc.doctype,
+						"reference_document": reference_doc.name,
+					},
+				},
+				headers={
+					"Idempotency-Key": self.data.get("reference_docname"),
+				},
+			)
 
-			if payment.status=="pending_submission" or payment.status=="pending_customer_approval" or payment.status=="submitted":
-				self.integration_request.db_set('status', 'Authorized', update_modified=False)
+			if (
+				payment.status == "pending_submission"
+				or payment.status == "pending_customer_approval"
+				or payment.status == "submitted"
+			):
+				self.integration_request.db_set("status", "Authorized", update_modified=False)
 				self.flags.status_changed_to = "Completed"
-				self.integration_request.db_set('output', payment.status, update_modified=False)
+				self.integration_request.db_set("output", payment.status, update_modified=False)
 
-			elif payment.status=="confirmed" or payment.status=="paid_out":
-				self.integration_request.db_set('status', 'Completed', update_modified=False)
+			elif payment.status == "confirmed" or payment.status == "paid_out":
+				self.integration_request.db_set("status", "Completed", update_modified=False)
 				self.flags.status_changed_to = "Completed"
-				self.integration_request.db_set('output', payment.status, update_modified=False)
+				self.integration_request.db_set("output", payment.status, update_modified=False)
 
-			elif payment.status=="cancelled" or payment.status=="customer_approval_denied" or payment.status=="charged_back":
-				self.integration_request.db_set('status', 'Cancelled', update_modified=False)
-				frappe.log_error(_("Payment Cancelled. Please check your GoCardless Account for more details"), "GoCardless Payment Error")
-				self.integration_request.db_set('error', payment.status, update_modified=False)
+			elif (
+				payment.status == "cancelled"
+				or payment.status == "customer_approval_denied"
+				or payment.status == "charged_back"
+			):
+				self.integration_request.db_set("status", "Cancelled", update_modified=False)
+				frappe.log_error("Gocardless payment cancelled")
+				self.integration_request.db_set("error", payment.status, update_modified=False)
 			else:
-				self.integration_request.db_set('status', 'Failed', update_modified=False)
-				frappe.log_error(_("Payment Failed. Please check your GoCardless Account for more details"), "GoCardless Payment Error")
-				self.integration_request.db_set('error', payment.status, update_modified=False)
+				self.integration_request.db_set("status", "Failed", update_modified=False)
+				frappe.log_error("Gocardless payment failed")
+				self.integration_request.db_set("error", payment.status, update_modified=False)
 
 		except Exception as e:
-			frappe.log_error(e, "GoCardless Payment Error")
+			frappe.log_error("GoCardless Payment Error")
 
 		if self.flags.status_changed_to == "Completed":
-			status = 'Completed'
-			if 'reference_doctype' in self.data and 'reference_docname' in self.data:
+			status = "Completed"
+			if "reference_doctype" in self.data and "reference_docname" in self.data:
 				custom_redirect_to = None
 				try:
-					custom_redirect_to = frappe.get_doc(self.data.get('reference_doctype'),
-						self.data.get('reference_docname')).run_method("on_payment_authorized", self.flags.status_changed_to)
+					custom_redirect_to = frappe.get_doc(
+						self.data.get("reference_doctype"), self.data.get("reference_docname")
+					).run_method("on_payment_authorized", self.flags.status_changed_to)
 				except Exception:
-					frappe.log_error(frappe.get_traceback())
+					frappe.log_error("Gocardless redirect failed")
 
 				if custom_redirect_to:
 					redirect_to = custom_redirect_to
 
 			redirect_url = redirect_to
 		else:
-			status = 'Error'
-			redirect_url = 'payment-failed'
+			status = "Error"
+			redirect_url = "payment-failed"
 
 			if redirect_message:
-				redirect_url += '&' + urlencode({'redirect_message': redirect_message})
+				redirect_url += "&" + urlencode({"redirect_message": redirect_message})
 
 			redirect_url = get_url(redirect_url)
 
-		return {
-			"redirect_to": redirect_url,
-			"status": status
-			}
+		return {"redirect_to": redirect_url, "status": status}
+
 
 def get_gateway_controller(doc):
 	payment_request = frappe.get_doc("Payment Request", doc)
-	gateway_controller = frappe.db.get_value("Payment Gateway", payment_request.payment_gateway, "gateway_controller")
+	gateway_controller = frappe.db.get_value(
+		"Payment Gateway", payment_request.payment_gateway, "gateway_controller"
+	)
 	return gateway_controller
 
+
 def gocardless_initialization(doc):
 	gateway_controller = get_gateway_controller(doc)
 	settings = frappe.get_doc("GoCardless Settings", gateway_controller)
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
index 6d46a1c..a577e7f 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
@@ -5,9 +5,15 @@
 from requests.auth import HTTPBasicAuth
 
 
-class MpesaConnector():
-	def __init__(self, env="sandbox", app_key=None, app_secret=None, sandbox_url="https://sandbox.safaricom.co.ke",
-		live_url="https://api.safaricom.co.ke"):
+class MpesaConnector:
+	def __init__(
+		self,
+		env="sandbox",
+		app_key=None,
+		app_secret=None,
+		sandbox_url="https://sandbox.safaricom.co.ke",
+		live_url="https://api.safaricom.co.ke",
+	):
 		"""Setup configuration for Mpesa connector and generate new access token."""
 		self.env = env
 		self.app_key = app_key
@@ -23,36 +29,41 @@
 		This method is used to fetch the access token required by Mpesa.
 
 		Returns:
-			access_token (str): This token is to be used with the Bearer header for further API calls to Mpesa.
+		        access_token (str): This token is to be used with the Bearer header for further API calls to Mpesa.
 		"""
 		authenticate_uri = "/oauth/v1/generate?grant_type=client_credentials"
 		authenticate_url = "{0}{1}".format(self.base_url, authenticate_uri)
-		r = requests.get(
-			authenticate_url,
-			auth=HTTPBasicAuth(self.app_key, self.app_secret)
-		)
-		self.authentication_token = r.json()['access_token']
-		return r.json()['access_token']
+		r = requests.get(authenticate_url, auth=HTTPBasicAuth(self.app_key, self.app_secret))
+		self.authentication_token = r.json()["access_token"]
+		return r.json()["access_token"]
 
-	def get_balance(self, initiator=None, security_credential=None, party_a=None, identifier_type=None,
-					remarks=None, queue_timeout_url=None,result_url=None):
+	def get_balance(
+		self,
+		initiator=None,
+		security_credential=None,
+		party_a=None,
+		identifier_type=None,
+		remarks=None,
+		queue_timeout_url=None,
+		result_url=None,
+	):
 		"""
 		This method uses Mpesa's Account Balance API to to enquire the balance on a M-Pesa BuyGoods (Till Number).
 
 		Args:
-			initiator (str): Username used to authenticate the transaction.
-			security_credential (str): Generate from developer portal.
-			command_id (str): AccountBalance.
-			party_a (int): Till number being queried.
-			identifier_type (int): Type of organization receiving the transaction. (MSISDN/Till Number/Organization short code)
-			remarks (str): Comments that are sent along with the transaction(maximum 100 characters).
-			queue_timeout_url (str): The url that handles information of timed out transactions.
-			result_url (str): The url that receives results from M-Pesa api call.
+		        initiator (str): Username used to authenticate the transaction.
+		        security_credential (str): Generate from developer portal.
+		        command_id (str): AccountBalance.
+		        party_a (int): Till number being queried.
+		        identifier_type (int): Type of organization receiving the transaction. (MSISDN/Till Number/Organization short code)
+		        remarks (str): Comments that are sent along with the transaction(maximum 100 characters).
+		        queue_timeout_url (str): The url that handles information of timed out transactions.
+		        result_url (str): The url that receives results from M-Pesa api call.
 
 		Returns:
-			OriginatorConverstionID (str): The unique request ID for tracking a transaction.
-			ConversationID (str): The unique request ID returned by mpesa for each request made
-			ResponseDescription (str): Response Description message
+		        OriginatorConverstionID (str): The unique request ID for tracking a transaction.
+		        ConversationID (str): The unique request ID returned by mpesa for each request made
+		        ResponseDescription (str): Response Description message
 		"""
 
 		payload = {
@@ -63,43 +74,56 @@
 			"IdentifierType": identifier_type,
 			"Remarks": remarks,
 			"QueueTimeOutURL": queue_timeout_url,
-			"ResultURL": result_url
+			"ResultURL": result_url,
 		}
-		headers = {'Authorization': 'Bearer {0}'.format(self.authentication_token), 'Content-Type': "application/json"}
+		headers = {
+			"Authorization": "Bearer {0}".format(self.authentication_token),
+			"Content-Type": "application/json",
+		}
 		saf_url = "{0}{1}".format(self.base_url, "/mpesa/accountbalance/v1/query")
 		r = requests.post(saf_url, headers=headers, json=payload)
 		return r.json()
 
-	def stk_push(self, business_shortcode=None, passcode=None, amount=None, callback_url=None, reference_code=None,
-				 phone_number=None, description=None):
+	def stk_push(
+		self,
+		business_shortcode=None,
+		passcode=None,
+		amount=None,
+		callback_url=None,
+		reference_code=None,
+		phone_number=None,
+		description=None,
+	):
 		"""
 		This method uses Mpesa's Express API to initiate online payment on behalf of a customer.
 
 		Args:
-			business_shortcode (int): The short code of the organization.
-			passcode (str): Get from developer portal
-			amount (int): The amount being transacted
-			callback_url (str): A CallBack URL is a valid secure URL that is used to receive notifications from M-Pesa API.
-			reference_code(str): Account Reference: This is an Alpha-Numeric parameter that is defined by your system as an Identifier of the transaction for CustomerPayBillOnline transaction type.
-			phone_number(int): The Mobile Number to receive the STK Pin Prompt.
-			description(str): This is any additional information/comment that can be sent along with the request from your system. MAX 13 characters
+		        business_shortcode (int): The short code of the organization.
+		        passcode (str): Get from developer portal
+		        amount (int): The amount being transacted
+		        callback_url (str): A CallBack URL is a valid secure URL that is used to receive notifications from M-Pesa API.
+		        reference_code(str): Account Reference: This is an Alpha-Numeric parameter that is defined by your system as an Identifier of the transaction for CustomerPayBillOnline transaction type.
+		        phone_number(int): The Mobile Number to receive the STK Pin Prompt.
+		        description(str): This is any additional information/comment that can be sent along with the request from your system. MAX 13 characters
 
 		Success Response:
-			CustomerMessage(str): Messages that customers can understand.
-			CheckoutRequestID(str): This is a global unique identifier of the processed checkout transaction request.
-			ResponseDescription(str): Describes Success or failure
-			MerchantRequestID(str): This is a global unique Identifier for any submitted payment request.
-			ResponseCode(int): 0 means success all others are error codes. e.g.404.001.03
+		        CustomerMessage(str): Messages that customers can understand.
+		        CheckoutRequestID(str): This is a global unique identifier of the processed checkout transaction request.
+		        ResponseDescription(str): Describes Success or failure
+		        MerchantRequestID(str): This is a global unique Identifier for any submitted payment request.
+		        ResponseCode(int): 0 means success all others are error codes. e.g.404.001.03
 
 		Error Reponse:
-			requestId(str): This is a unique requestID for the payment request
-			errorCode(str): This is a predefined code that indicates the reason for request failure.
-			errorMessage(str): This is a predefined code that indicates the reason for request failure.
+		        requestId(str): This is a unique requestID for the payment request
+		        errorCode(str): This is a predefined code that indicates the reason for request failure.
+		        errorMessage(str): This is a predefined code that indicates the reason for request failure.
 		"""
 
-		time = str(datetime.datetime.now()).split(".")[0].replace("-", "").replace(" ", "").replace(":", "")
+		time = (
+			str(datetime.datetime.now()).split(".")[0].replace("-", "").replace(" ", "").replace(":", "")
+		)
 		password = "{0}{1}{2}".format(str(business_shortcode), str(passcode), time)
-		encoded = base64.b64encode(bytes(password, encoding='utf8'))
+		encoded = base64.b64encode(bytes(password, encoding="utf8"))
 		payload = {
 			"BusinessShortCode": business_shortcode,
 			"Password": encoded.decode("utf-8"),
@@ -111,9 +135,14 @@
 			"CallBackURL": callback_url,
 			"AccountReference": reference_code,
 			"TransactionDesc": description,
-			"TransactionType": "CustomerPayBillOnline" if self.env == "sandbox" else "CustomerBuyGoodsOnline"
+			"TransactionType": "CustomerPayBillOnline"
+			if self.env == "sandbox"
+			else "CustomerBuyGoodsOnline",
 		}
-		headers = {'Authorization': 'Bearer {0}'.format(self.authentication_token), 'Content-Type': "application/json"}
+		headers = {
+			"Authorization": "Bearer {0}".format(self.authentication_token),
+			"Content-Type": "application/json",
+		}
 
 		saf_url = "{0}{1}".format(self.base_url, "/mpesa/stkpush/v1/processrequest")
 		r = requests.post(saf_url, headers=headers, json=payload)
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py
index 368139b..c92edc5 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py
@@ -11,21 +11,22 @@
 				"label": "Request for Payment",
 				"fieldtype": "Button",
 				"hidden": 1,
-				"insert_after": "contact_email"
+				"insert_after": "contact_email",
 			},
 			{
 				"fieldname": "mpesa_receipt_number",
 				"label": "Mpesa Receipt Number",
 				"fieldtype": "Data",
 				"read_only": 1,
-				"insert_after": "company"
-			}
+				"insert_after": "company",
+			},
 		]
 	}
 	if not frappe.get_meta("POS Invoice").has_field("request_for_payment"):
 		create_custom_fields(pos_field)
 
-	record_dict = [{
+	record_dict = [
+		{
 			"doctype": "POS Field",
 			"fieldname": "contact_mobile",
 			"label": "Mobile No",
@@ -33,7 +34,7 @@
 			"options": "Phone",
 			"parenttype": "POS Settings",
 			"parent": "POS Settings",
-			"parentfield": "invoice_fields"
+			"parentfield": "invoice_fields",
 		},
 		{
 			"doctype": "POS Field",
@@ -42,11 +43,12 @@
 			"fieldtype": "Button",
 			"parenttype": "POS Settings",
 			"parent": "POS Settings",
-			"parentfield": "invoice_fields"
-		}
+			"parentfield": "invoice_fields",
+		},
 	]
 	create_pos_settings(record_dict)
 
+
 def create_pos_settings(record_dict):
 	for record in record_dict:
 		if frappe.db.exists("POS Field", {"fieldname": record.get("fieldname")}):
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
index e7b4a30..e389980 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
@@ -2,7 +2,6 @@
 # For license information, please see license.txt
 
 
-
 from json import dumps, loads
 
 import frappe
@@ -23,16 +22,26 @@
 
 	def validate_transaction_currency(self, currency):
 		if currency not in self.supported_currencies:
-			frappe.throw(_("Please select another payment method. Mpesa does not support transactions in currency '{0}'").format(currency))
+			frappe.throw(
+				_(
+					"Please select another payment method. Mpesa does not support transactions in currency '{0}'"
+				).format(currency)
+			)
 
 	def on_update(self):
 		create_custom_pos_fields()
-		create_payment_gateway('Mpesa-' + self.payment_gateway_name, settings='Mpesa Settings', controller=self.payment_gateway_name)
-		call_hook_method('payment_gateway_enabled', gateway='Mpesa-' + self.payment_gateway_name, payment_channel="Phone")
+		create_payment_gateway(
+			"Mpesa-" + self.payment_gateway_name,
+			settings="Mpesa Settings",
+			controller=self.payment_gateway_name,
+		)
+		call_hook_method(
+			"payment_gateway_enabled", gateway="Mpesa-" + self.payment_gateway_name, payment_channel="Phone"
+		)
 
 		# required to fetch the bank account details from the payment gateway account
 		frappe.db.commit()
-		create_mode_of_payment('Mpesa-' + self.payment_gateway_name, payment_type="Phone")
+		create_mode_of_payment("Mpesa-" + self.payment_gateway_name, payment_type="Phone")
 
 	def request_for_payment(self, **kwargs):
 		args = frappe._dict(kwargs)
@@ -44,6 +53,7 @@
 				from erpnext.erpnext_integrations.doctype.mpesa_settings.test_mpesa_settings import (
 					get_payment_request_response_payload,
 				)
+
 				response = frappe._dict(get_payment_request_response_payload(amount))
 			else:
 				response = frappe._dict(generate_stk_push(**args))
@@ -55,11 +65,15 @@
 		if request_amount > self.transaction_limit:
 			# make multiple requests
 			request_amounts = []
-			requests_to_be_made = frappe.utils.ceil(request_amount / self.transaction_limit) # 480/150 = ceil(3.2) = 4
+			requests_to_be_made = frappe.utils.ceil(
+				request_amount / self.transaction_limit
+			)  # 480/150 = ceil(3.2) = 4
 			for i in range(requests_to_be_made):
 				amount = self.transaction_limit
 				if i == requests_to_be_made - 1:
-					amount = request_amount - (self.transaction_limit * i) # for 4th request, 480 - (150 * 3) = 30
+					amount = request_amount - (
+						self.transaction_limit * i
+					)  # for 4th request, 480 - (150 * 3) = 30
 				request_amounts.append(amount)
 		else:
 			request_amounts = [request_amount]
@@ -69,15 +83,14 @@
 	@frappe.whitelist()
 	def get_account_balance_info(self):
 		payload = dict(
-			reference_doctype="Mpesa Settings",
-			reference_docname=self.name,
-			doc_details=vars(self)
+			reference_doctype="Mpesa Settings", reference_docname=self.name, doc_details=vars(self)
 		)
 
 		if frappe.flags.in_test:
 			from erpnext.erpnext_integrations.doctype.mpesa_settings.test_mpesa_settings import (
 				get_test_account_balance_response,
 			)
+
 			response = frappe._dict(get_test_account_balance_response())
 		else:
 			response = frappe._dict(get_account_balance(payload))
@@ -95,46 +108,62 @@
 			req_name = getattr(response, global_id)
 			error = None
 
-		if not frappe.db.exists('Integration Request', req_name):
+		if not frappe.db.exists("Integration Request", req_name):
 			create_request_log(request_dict, "Host", "Mpesa", req_name, error)
 
 		if error:
 			frappe.throw(_(getattr(response, "errorMessage")), title=_("Transaction Error"))
 
+
 def generate_stk_push(**kwargs):
 	"""Generate stk push by making a API call to the stk push API."""
 	args = frappe._dict(kwargs)
 	try:
-		callback_url = get_request_site_address(True) + "/api/method/erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings.verify_transaction"
+		callback_url = (
+			get_request_site_address(True)
+			+ "/api/method/erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings.verify_transaction"
+		)
 
 		mpesa_settings = frappe.get_doc("Mpesa Settings", args.payment_gateway[6:])
 		env = "production" if not mpesa_settings.sandbox else "sandbox"
 		# for sandbox, business shortcode is same as till number
-		business_shortcode = mpesa_settings.business_shortcode if env == "production" else mpesa_settings.till_number
+		business_shortcode = (
+			mpesa_settings.business_shortcode if env == "production" else mpesa_settings.till_number
+		)
 
-		connector = MpesaConnector(env=env,
+		connector = MpesaConnector(
+			env=env,
 			app_key=mpesa_settings.consumer_key,
-			app_secret=mpesa_settings.get_password("consumer_secret"))
+			app_secret=mpesa_settings.get_password("consumer_secret"),
+		)
 
 		mobile_number = sanitize_mobile_number(args.sender)
 
 		response = connector.stk_push(
-			business_shortcode=business_shortcode, amount=args.request_amount,
+			business_shortcode=business_shortcode,
+			amount=args.request_amount,
 			passcode=mpesa_settings.get_password("online_passkey"),
-			callback_url=callback_url, reference_code=mpesa_settings.till_number,
-			phone_number=mobile_number, description="POS Payment"
+			callback_url=callback_url,
+			reference_code=mpesa_settings.till_number,
+			phone_number=mobile_number,
+			description="POS Payment",
 		)
 
 		return response
 
 	except Exception:
-		frappe.log_error(title=_("Mpesa Express Transaction Error"))
-		frappe.throw(_("Issue detected with Mpesa configuration, check the error logs for more details"), title=_("Mpesa Express Error"))
+		frappe.log_error("Mpesa Express Transaction Error")
+		frappe.throw(
+			_("Issue detected with Mpesa configuration, check the error logs for more details"),
+			title=_("Mpesa Express Error"),
+		)
+
 
 def sanitize_mobile_number(number):
 	"""Add country code and strip leading zeroes from the phone number."""
 	return "254" + str(number).lstrip("0")
 
+
 @frappe.whitelist(allow_guest=True)
 def verify_transaction(**kwargs):
 	"""Verify the transaction result received via callback from stk."""
@@ -146,58 +175,65 @@
 
 	integration_request = frappe.get_doc("Integration Request", checkout_id)
 	transaction_data = frappe._dict(loads(integration_request.data))
-	total_paid = 0 # for multiple integration request made against a pos invoice
-	success = False # for reporting successfull callback to point of sale ui
+	total_paid = 0  # for multiple integration request made against a pos invoice
+	success = False  # for reporting successfull callback to point of sale ui
 
-	if transaction_response['ResultCode'] == 0:
+	if transaction_response["ResultCode"] == 0:
 		if integration_request.reference_doctype and integration_request.reference_docname:
 			try:
 				item_response = transaction_response["CallbackMetadata"]["Item"]
 				amount = fetch_param_value(item_response, "Amount", "Name")
 				mpesa_receipt = fetch_param_value(item_response, "MpesaReceiptNumber", "Name")
-				pr = frappe.get_doc(integration_request.reference_doctype, integration_request.reference_docname)
+				pr = frappe.get_doc(
+					integration_request.reference_doctype, integration_request.reference_docname
+				)
 
 				mpesa_receipts, completed_payments = get_completed_integration_requests_info(
-					integration_request.reference_doctype,
-					integration_request.reference_docname,
-					checkout_id
+					integration_request.reference_doctype, integration_request.reference_docname, checkout_id
 				)
 
 				total_paid = amount + sum(completed_payments)
-				mpesa_receipts = ', '.join(mpesa_receipts + [mpesa_receipt])
+				mpesa_receipts = ", ".join(mpesa_receipts + [mpesa_receipt])
 
 				if total_paid >= pr.grand_total:
-					pr.run_method("on_payment_authorized", 'Completed')
+					pr.run_method("on_payment_authorized", "Completed")
 					success = True
 
 				frappe.db.set_value("POS Invoice", pr.reference_name, "mpesa_receipt_number", mpesa_receipts)
 				integration_request.handle_success(transaction_response)
 			except Exception:
 				integration_request.handle_failure(transaction_response)
-				frappe.log_error(frappe.get_traceback())
+				frappe.log_error("Mpesa: Failed to verify transaction")
 
 	else:
 		integration_request.handle_failure(transaction_response)
 
 	frappe.publish_realtime(
-		event='process_phone_payment',
+		event="process_phone_payment",
 		doctype="POS Invoice",
 		docname=transaction_data.payment_reference,
 		user=integration_request.owner,
 		message={
-			'amount': total_paid,
-			'success': success,
-			'failure_message': transaction_response["ResultDesc"] if transaction_response['ResultCode'] != 0 else ''
+			"amount": total_paid,
+			"success": success,
+			"failure_message": transaction_response["ResultDesc"]
+			if transaction_response["ResultCode"] != 0
+			else "",
 		},
 	)
 
+
 def get_completed_integration_requests_info(reference_doctype, reference_docname, checkout_id):
-	output_of_other_completed_requests = frappe.get_all("Integration Request", filters={
-		'name': ['!=', checkout_id],
-		'reference_doctype': reference_doctype,
-		'reference_docname': reference_docname,
-		'status': 'Completed'
-	}, pluck="output")
+	output_of_other_completed_requests = frappe.get_all(
+		"Integration Request",
+		filters={
+			"name": ["!=", checkout_id],
+			"reference_doctype": reference_doctype,
+			"reference_docname": reference_docname,
+			"status": "Completed",
+		},
+		pluck="output",
+	)
 
 	mpesa_receipts, completed_payments = [], []
 
@@ -211,23 +247,38 @@
 
 	return mpesa_receipts, completed_payments
 
+
 def get_account_balance(request_payload):
 	"""Call account balance API to send the request to the Mpesa Servers."""
 	try:
 		mpesa_settings = frappe.get_doc("Mpesa Settings", request_payload.get("reference_docname"))
 		env = "production" if not mpesa_settings.sandbox else "sandbox"
-		connector = MpesaConnector(env=env,
+		connector = MpesaConnector(
+			env=env,
 			app_key=mpesa_settings.consumer_key,
-			app_secret=mpesa_settings.get_password("consumer_secret"))
+			app_secret=mpesa_settings.get_password("consumer_secret"),
+		)
 
-		callback_url = get_request_site_address(True) + "/api/method/erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings.process_balance_info"
+		callback_url = (
+			get_request_site_address(True)
+			+ "/api/method/erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings.process_balance_info"
+		)
 
-		response = connector.get_balance(mpesa_settings.initiator_name, mpesa_settings.security_credential, mpesa_settings.till_number, 4, mpesa_settings.name, callback_url, callback_url)
+		response = connector.get_balance(
+			mpesa_settings.initiator_name,
+			mpesa_settings.security_credential,
+			mpesa_settings.till_number,
+			4,
+			mpesa_settings.name,
+			callback_url,
+			callback_url,
+		)
 		return response
 	except Exception:
-		frappe.log_error(title=_("Account Balance Processing Error"))
+		frappe.log_error("Mpesa: Failed to get account balance")
 		frappe.throw(_("Please check your configuration and try again"), title=_("Error"))
 
+
 @frappe.whitelist(allow_guest=True)
 def process_balance_info(**kwargs):
 	"""Process and store account balance information received via callback from the account balance API call."""
@@ -255,35 +306,43 @@
 			ref_doc.db_set("account_balance", balance_info)
 
 			request.handle_success(account_balance_response)
-			frappe.publish_realtime("refresh_mpesa_dashboard", doctype="Mpesa Settings",
-				docname=transaction_data.reference_docname, user=transaction_data.owner)
+			frappe.publish_realtime(
+				"refresh_mpesa_dashboard",
+				doctype="Mpesa Settings",
+				docname=transaction_data.reference_docname,
+				user=transaction_data.owner,
+			)
 		except Exception:
 			request.handle_failure(account_balance_response)
-			frappe.log_error(title=_("Mpesa Account Balance Processing Error"), message=account_balance_response)
+			frappe.log_error(
+				title="Mpesa Account Balance Processing Error", message=account_balance_response
+			)
 	else:
 		request.handle_failure(account_balance_response)
 
+
 def format_string_to_json(balance_info):
 	"""
 	Format string to json.
 
 	e.g: '''Working Account|KES|481000.00|481000.00|0.00|0.00'''
 	=> {'Working Account': {'current_balance': '481000.00',
-		'available_balance': '481000.00',
-		'reserved_balance': '0.00',
-		'uncleared_balance': '0.00'}}
+	        'available_balance': '481000.00',
+	        'reserved_balance': '0.00',
+	        'uncleared_balance': '0.00'}}
 	"""
 	balance_dict = frappe._dict()
 	for account_info in balance_info.split("&"):
-		account_info = account_info.split('|')
+		account_info = account_info.split("|")
 		balance_dict[account_info[0]] = dict(
 			current_balance=fmt_money(account_info[2], currency="KES"),
 			available_balance=fmt_money(account_info[3], currency="KES"),
 			reserved_balance=fmt_money(account_info[4], currency="KES"),
-			uncleared_balance=fmt_money(account_info[5], currency="KES")
+			uncleared_balance=fmt_money(account_info[5], currency="KES"),
 		)
 	return dumps(balance_dict)
 
+
 def fetch_param_value(response, key, key_field):
 	"""Fetch the specified key from list of dictionary. Key is identified via the key field."""
 	for param in response:
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
index 3945afa..b526624 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
@@ -22,12 +22,12 @@
 		create_mpesa_settings(payment_gateway_name="Payment")
 
 	def tearDown(self):
-		frappe.db.sql('delete from `tabMpesa Settings`')
-		frappe.db.sql('delete from `tabIntegration Request` where integration_request_service = "Mpesa"')
+		frappe.db.sql("delete from `tabMpesa Settings`")
+		frappe.db.sql("delete from `tabIntegration Request` where integration_request_service = 'Mpesa'")
 
 	def test_creation_of_payment_gateway(self):
-		mode_of_payment = create_mode_of_payment('Mpesa-_Test', payment_type="Phone")
-		self.assertTrue(frappe.db.exists("Payment Gateway Account", {'payment_gateway': "Mpesa-_Test"}))
+		mode_of_payment = create_mode_of_payment("Mpesa-_Test", payment_type="Phone")
+		self.assertTrue(frappe.db.exists("Payment Gateway Account", {"payment_gateway": "Mpesa-_Test"}))
 		self.assertTrue(mode_of_payment.name)
 		self.assertEqual(mode_of_payment.type, "Phone")
 
@@ -45,24 +45,33 @@
 
 		# test formatting of account balance received as string to json with appropriate currency symbol
 		mpesa_doc.reload()
-		self.assertEqual(mpesa_doc.account_balance, dumps({
-			"Working Account": {
-				"current_balance": "Sh 481,000.00",
-				"available_balance": "Sh 481,000.00",
-				"reserved_balance": "Sh 0.00",
-				"uncleared_balance": "Sh 0.00"
-			}
-		}))
+		self.assertEqual(
+			mpesa_doc.account_balance,
+			dumps(
+				{
+					"Working Account": {
+						"current_balance": "Sh 481,000.00",
+						"available_balance": "Sh 481,000.00",
+						"reserved_balance": "Sh 0.00",
+						"uncleared_balance": "Sh 0.00",
+					}
+				}
+			),
+		)
 
 		integration_request.delete()
 
 	def test_processing_of_callback_payload(self):
-		mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
+		mpesa_account = frappe.db.get_value(
+			"Payment Gateway Account", {"payment_gateway": "Mpesa-Payment"}, "payment_account"
+		)
 		frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
 		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES")
 
 		pos_invoice = create_pos_invoice(do_not_submit=1)
-		pos_invoice.append("payments", {'mode_of_payment': 'Mpesa-Payment', 'account': mpesa_account, 'amount': 500})
+		pos_invoice.append(
+			"payments", {"mode_of_payment": "Mpesa-Payment", "account": mpesa_account, "amount": 500}
+		)
 		pos_invoice.contact_mobile = "093456543894"
 		pos_invoice.currency = "KES"
 		pos_invoice.save()
@@ -72,12 +81,18 @@
 		self.assertEqual(pr.payment_gateway, "Mpesa-Payment")
 
 		# submitting payment request creates integration requests with random id
-		integration_req_ids = frappe.get_all("Integration Request", filters={
-			'reference_doctype': pr.doctype,
-			'reference_docname': pr.name,
-		}, pluck="name")
+		integration_req_ids = frappe.get_all(
+			"Integration Request",
+			filters={
+				"reference_doctype": pr.doctype,
+				"reference_docname": pr.name,
+			},
+			pluck="name",
+		)
 
-		callback_response = get_payment_callback_payload(Amount=500, CheckoutRequestID=integration_req_ids[0])
+		callback_response = get_payment_callback_payload(
+			Amount=500, CheckoutRequestID=integration_req_ids[0]
+		)
 		verify_transaction(**callback_response)
 		# test creation of integration request
 		integration_request = frappe.get_doc("Integration Request", integration_req_ids[0])
@@ -99,13 +114,17 @@
 		pos_invoice.delete()
 
 	def test_processing_of_multiple_callback_payload(self):
-		mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
+		mpesa_account = frappe.db.get_value(
+			"Payment Gateway Account", {"payment_gateway": "Mpesa-Payment"}, "payment_account"
+		)
 		frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
 		frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500")
 		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES")
 
 		pos_invoice = create_pos_invoice(do_not_submit=1)
-		pos_invoice.append("payments", {'mode_of_payment': 'Mpesa-Payment', 'account': mpesa_account, 'amount': 1000})
+		pos_invoice.append(
+			"payments", {"mode_of_payment": "Mpesa-Payment", "account": mpesa_account, "amount": 1000}
+		)
 		pos_invoice.contact_mobile = "093456543894"
 		pos_invoice.currency = "KES"
 		pos_invoice.save()
@@ -115,10 +134,14 @@
 		self.assertEqual(pr.payment_gateway, "Mpesa-Payment")
 
 		# submitting payment request creates integration requests with random id
-		integration_req_ids = frappe.get_all("Integration Request", filters={
-			'reference_doctype': pr.doctype,
-			'reference_docname': pr.name,
-		}, pluck="name")
+		integration_req_ids = frappe.get_all(
+			"Integration Request",
+			filters={
+				"reference_doctype": pr.doctype,
+				"reference_docname": pr.name,
+			},
+			pluck="name",
+		)
 
 		# create random receipt nos and send it as response to callback handler
 		mpesa_receipt_numbers = [frappe.utils.random_string(5) for d in integration_req_ids]
@@ -128,7 +151,7 @@
 			callback_response = get_payment_callback_payload(
 				Amount=500,
 				CheckoutRequestID=integration_req_ids[i],
-				MpesaReceiptNumber=mpesa_receipt_numbers[i]
+				MpesaReceiptNumber=mpesa_receipt_numbers[i],
 			)
 			# handle response manually
 			verify_transaction(**callback_response)
@@ -139,7 +162,7 @@
 
 		# check receipt number once all the integration requests are completed
 		pos_invoice.reload()
-		self.assertEqual(pos_invoice.mpesa_receipt_number, ', '.join(mpesa_receipt_numbers))
+		self.assertEqual(pos_invoice.mpesa_receipt_number, ", ".join(mpesa_receipt_numbers))
 
 		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "")
 		[d.delete() for d in integration_requests]
@@ -149,13 +172,17 @@
 		pos_invoice.delete()
 
 	def test_processing_of_only_one_succes_callback_payload(self):
-		mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
+		mpesa_account = frappe.db.get_value(
+			"Payment Gateway Account", {"payment_gateway": "Mpesa-Payment"}, "payment_account"
+		)
 		frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
 		frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500")
 		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES")
 
 		pos_invoice = create_pos_invoice(do_not_submit=1)
-		pos_invoice.append("payments", {'mode_of_payment': 'Mpesa-Payment', 'account': mpesa_account, 'amount': 1000})
+		pos_invoice.append(
+			"payments", {"mode_of_payment": "Mpesa-Payment", "account": mpesa_account, "amount": 1000}
+		)
 		pos_invoice.contact_mobile = "093456543894"
 		pos_invoice.currency = "KES"
 		pos_invoice.save()
@@ -165,10 +192,14 @@
 		self.assertEqual(pr.payment_gateway, "Mpesa-Payment")
 
 		# submitting payment request creates integration requests with random id
-		integration_req_ids = frappe.get_all("Integration Request", filters={
-			'reference_doctype': pr.doctype,
-			'reference_docname': pr.name,
-		}, pluck="name")
+		integration_req_ids = frappe.get_all(
+			"Integration Request",
+			filters={
+				"reference_doctype": pr.doctype,
+				"reference_docname": pr.name,
+			},
+			pluck="name",
+		)
 
 		# create random receipt nos and send it as response to callback handler
 		mpesa_receipt_numbers = [frappe.utils.random_string(5) for d in integration_req_ids]
@@ -176,7 +207,7 @@
 		callback_response = get_payment_callback_payload(
 			Amount=500,
 			CheckoutRequestID=integration_req_ids[0],
-			MpesaReceiptNumber=mpesa_receipt_numbers[0]
+			MpesaReceiptNumber=mpesa_receipt_numbers[0],
 		)
 		# handle response manually
 		verify_transaction(**callback_response)
@@ -188,11 +219,15 @@
 		# second integration request fails
 		# now retrying payment request should make only one integration request again
 		pr = pos_invoice.create_payment_request()
-		new_integration_req_ids = frappe.get_all("Integration Request", filters={
-			'reference_doctype': pr.doctype,
-			'reference_docname': pr.name,
-			'name': ['not in', integration_req_ids]
-		}, pluck="name")
+		new_integration_req_ids = frappe.get_all(
+			"Integration Request",
+			filters={
+				"reference_doctype": pr.doctype,
+				"reference_docname": pr.name,
+				"name": ["not in", integration_req_ids],
+			},
+			pluck="name",
+		)
 
 		self.assertEqual(len(new_integration_req_ids), 1)
 
@@ -203,94 +238,56 @@
 		pr.delete()
 		pos_invoice.delete()
 
+
 def create_mpesa_settings(payment_gateway_name="Express"):
 	if frappe.db.exists("Mpesa Settings", payment_gateway_name):
 		return frappe.get_doc("Mpesa Settings", payment_gateway_name)
 
-	doc = frappe.get_doc(dict( #nosec
-		doctype="Mpesa Settings",
-		sandbox=1,
-		payment_gateway_name=payment_gateway_name,
-		consumer_key="5sMu9LVI1oS3oBGPJfh3JyvLHwZOdTKn",
-		consumer_secret="VI1oS3oBGPJfh3JyvLHw",
-		online_passkey="LVI1oS3oBGPJfh3JyvLHwZOd",
-		till_number="174379"
-	))
+	doc = frappe.get_doc(
+		dict(  # nosec
+			doctype="Mpesa Settings",
+			sandbox=1,
+			payment_gateway_name=payment_gateway_name,
+			consumer_key="5sMu9LVI1oS3oBGPJfh3JyvLHwZOdTKn",
+			consumer_secret="VI1oS3oBGPJfh3JyvLHw",
+			online_passkey="LVI1oS3oBGPJfh3JyvLHwZOd",
+			till_number="174379",
+		)
+	)
 
 	doc.insert(ignore_permissions=True)
 	return doc
 
+
 def get_test_account_balance_response():
 	"""Response received after calling the account balance API."""
 	return {
-		"ResultType":0,
-		"ResultCode":0,
-		"ResultDesc":"The service request has been accepted successfully.",
-		"OriginatorConversationID":"10816-694520-2",
-		"ConversationID":"AG_20200927_00007cdb1f9fb6494315",
-		"TransactionID":"LGR0000000",
-		"ResultParameters":{
-		"ResultParameter":[
-			{
-			"Key":"ReceiptNo",
-			"Value":"LGR919G2AV"
-			},
-			{
-			"Key":"Conversation ID",
-			"Value":"AG_20170727_00004492b1b6d0078fbe"
-			},
-			{
-			"Key":"FinalisedTime",
-			"Value":20170727101415
-			},
-			{
-			"Key":"Amount",
-			"Value":10
-			},
-			{
-			"Key":"TransactionStatus",
-			"Value":"Completed"
-			},
-			{
-			"Key":"ReasonType",
-			"Value":"Salary Payment via API"
-			},
-			{
-			"Key":"TransactionReason"
-			},
-			{
-			"Key":"DebitPartyCharges",
-			"Value":"Fee For B2C Payment|KES|33.00"
-			},
-			{
-			"Key":"DebitAccountType",
-			"Value":"Utility Account"
-			},
-			{
-			"Key":"InitiatedTime",
-			"Value":20170727101415
-			},
-			{
-			"Key":"Originator Conversation ID",
-			"Value":"19455-773836-1"
-			},
-			{
-			"Key":"CreditPartyName",
-			"Value":"254708374149 - John Doe"
-			},
-			{
-			"Key":"DebitPartyName",
-			"Value":"600134 - Safaricom157"
-			}
-		]
-	},
-	"ReferenceData":{
-	"ReferenceItem":{
-		"Key":"Occasion",
-		"Value":"aaaa"
+		"ResultType": 0,
+		"ResultCode": 0,
+		"ResultDesc": "The service request has been accepted successfully.",
+		"OriginatorConversationID": "10816-694520-2",
+		"ConversationID": "AG_20200927_00007cdb1f9fb6494315",
+		"TransactionID": "LGR0000000",
+		"ResultParameters": {
+			"ResultParameter": [
+				{"Key": "ReceiptNo", "Value": "LGR919G2AV"},
+				{"Key": "Conversation ID", "Value": "AG_20170727_00004492b1b6d0078fbe"},
+				{"Key": "FinalisedTime", "Value": 20170727101415},
+				{"Key": "Amount", "Value": 10},
+				{"Key": "TransactionStatus", "Value": "Completed"},
+				{"Key": "ReasonType", "Value": "Salary Payment via API"},
+				{"Key": "TransactionReason"},
+				{"Key": "DebitPartyCharges", "Value": "Fee For B2C Payment|KES|33.00"},
+				{"Key": "DebitAccountType", "Value": "Utility Account"},
+				{"Key": "InitiatedTime", "Value": 20170727101415},
+				{"Key": "Originator Conversation ID", "Value": "19455-773836-1"},
+				{"Key": "CreditPartyName", "Value": "254708374149 - John Doe"},
+				{"Key": "DebitPartyName", "Value": "600134 - Safaricom157"},
+			]
+		},
+		"ReferenceData": {"ReferenceItem": {"Key": "Occasion", "Value": "aaaa"}},
 	}
-	}
-		}
+
 
 def get_payment_request_response_payload(Amount=500):
 	"""Response received after successfully calling the stk push process request API."""
@@ -304,40 +301,44 @@
 		"ResultDesc": "The service request is processed successfully.",
 		"CallbackMetadata": {
 			"Item": [
-				{ "Name": "Amount", "Value": Amount },
-				{ "Name": "MpesaReceiptNumber", "Value": "LGR7OWQX0R" },
-				{ "Name": "TransactionDate", "Value": 20201006113336 },
-				{ "Name": "PhoneNumber", "Value": 254723575670 }
+				{"Name": "Amount", "Value": Amount},
+				{"Name": "MpesaReceiptNumber", "Value": "LGR7OWQX0R"},
+				{"Name": "TransactionDate", "Value": 20201006113336},
+				{"Name": "PhoneNumber", "Value": 254723575670},
 			]
-		}
+		},
 	}
 
-def get_payment_callback_payload(Amount=500, CheckoutRequestID="ws_CO_061020201133231972", MpesaReceiptNumber="LGR7OWQX0R"):
+
+def get_payment_callback_payload(
+	Amount=500, CheckoutRequestID="ws_CO_061020201133231972", MpesaReceiptNumber="LGR7OWQX0R"
+):
 	"""Response received from the server as callback after calling the stkpush process request API."""
 	return {
-		"Body":{
-			"stkCallback":{
-				"MerchantRequestID":"19465-780693-1",
-				"CheckoutRequestID":CheckoutRequestID,
-				"ResultCode":0,
-				"ResultDesc":"The service request is processed successfully.",
-				"CallbackMetadata":{
-					"Item":[
-						{ "Name":"Amount", "Value":Amount },
-						{ "Name":"MpesaReceiptNumber", "Value":MpesaReceiptNumber },
-						{ "Name":"Balance" },
-						{ "Name":"TransactionDate", "Value":20170727154800 },
-						{ "Name":"PhoneNumber", "Value":254721566839 }
+		"Body": {
+			"stkCallback": {
+				"MerchantRequestID": "19465-780693-1",
+				"CheckoutRequestID": CheckoutRequestID,
+				"ResultCode": 0,
+				"ResultDesc": "The service request is processed successfully.",
+				"CallbackMetadata": {
+					"Item": [
+						{"Name": "Amount", "Value": Amount},
+						{"Name": "MpesaReceiptNumber", "Value": MpesaReceiptNumber},
+						{"Name": "Balance"},
+						{"Name": "TransactionDate", "Value": 20170727154800},
+						{"Name": "PhoneNumber", "Value": 254721566839},
 					]
-				}
+				},
 			}
 		}
 	}
 
+
 def get_account_balance_callback_payload():
 	"""Response received from the server as callback after calling the account balance API."""
 	return {
-		"Result":{
+		"Result": {
 			"ResultType": 0,
 			"ResultCode": 0,
 			"ResultDesc": "The service request is processed successfully.",
@@ -346,18 +347,15 @@
 			"TransactionID": "OIR0000000",
 			"ResultParameters": {
 				"ResultParameter": [
-					{
-						"Key": "AccountBalance",
-						"Value": "Working Account|KES|481000.00|481000.00|0.00|0.00"
-					},
-					{ "Key": "BOCompletedTime", "Value": 20200927234123 }
+					{"Key": "AccountBalance", "Value": "Working Account|KES|481000.00|481000.00|0.00|0.00"},
+					{"Key": "BOCompletedTime", "Value": 20200927234123},
 				]
 			},
 			"ReferenceData": {
 				"ReferenceItem": {
 					"Key": "QueueTimeoutURL",
-					"Value": "https://internalsandbox.safaricom.co.ke/mpesa/abresults/v1/submit"
+					"Value": "https://internalsandbox.safaricom.co.ke/mpesa/abresults/v1/submit",
 				}
-			}
+			},
 		}
 	}
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
index 0b552f9..38d6993 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
@@ -8,7 +8,7 @@
 from plaid.errors import APIError, InvalidRequestError, ItemError
 
 
-class PlaidConnector():
+class PlaidConnector:
 	def __init__(self, access_token=None):
 		self.access_token = access_token
 		self.settings = frappe.get_single("Plaid Settings")
@@ -18,36 +18,40 @@
 			client_id=self.settings.plaid_client_id,
 			secret=self.settings.get_password("plaid_secret"),
 			environment=self.settings.plaid_env,
-			api_version="2020-09-14"
+			api_version="2020-09-14",
 		)
 
 	def get_access_token(self, public_token):
 		if public_token is None:
-			frappe.log_error(_("Public token is missing for this bank"), _("Plaid public token error"))
+			frappe.log_error("Plaid: Public token is missing")
 		response = self.client.Item.public_token.exchange(public_token)
 		access_token = response["access_token"]
 		return access_token
 
 	def get_token_request(self, update_mode=False):
-		country_codes = ["US", "CA", "FR", "IE", "NL", "ES", "GB"] if self.settings.enable_european_access else ["US", "CA"]
+		country_codes = (
+			["US", "CA", "FR", "IE", "NL", "ES", "GB"]
+			if self.settings.enable_european_access
+			else ["US", "CA"]
+		)
 		args = {
 			"client_name": self.client_name,
 			# only allow Plaid-supported languages and countries (LAST: Sep-19-2020)
 			"language": frappe.local.lang if frappe.local.lang in ["en", "fr", "es", "nl"] else "en",
 			"country_codes": country_codes,
-			"user": {
-				"client_user_id": frappe.generate_hash(frappe.session.user, length=32)
-			}
+			"user": {"client_user_id": frappe.generate_hash(frappe.session.user, length=32)},
 		}
 
 		if update_mode:
 			args["access_token"] = self.access_token
 		else:
-			args.update({
-				"client_id": self.settings.plaid_client_id,
-				"secret": self.settings.plaid_secret,
-				"products": self.products,
-			})
+			args.update(
+				{
+					"client_id": self.settings.plaid_client_id,
+					"secret": self.settings.plaid_secret,
+					"products": self.products,
+				}
+			)
 
 		return args
 
@@ -57,10 +61,10 @@
 		try:
 			response = self.client.LinkToken.create(token_request)
 		except InvalidRequestError:
-			frappe.log_error(frappe.get_traceback(), _("Plaid invalid request error"))
+			frappe.log_error("Plaid: Invalid request error")
 			frappe.msgprint(_("Please check your Plaid client ID and secret values"))
 		except APIError as e:
-			frappe.log_error(frappe.get_traceback(), _("Plaid authentication error"))
+			frappe.log_error("Plaid: Authentication error")
 			frappe.throw(_(str(e)), title=_("Authentication Failed"))
 		else:
 			return response["link_token"]
@@ -77,16 +81,12 @@
 		except requests.Timeout:
 			pass
 		except Exception as e:
-			frappe.log_error(frappe.get_traceback(), _("Plaid authentication error"))
+			frappe.log_error("Plaid: Authentication error")
 			frappe.throw(_(str(e)), title=_("Authentication Failed"))
 
 	def get_transactions(self, start_date, end_date, account_id=None):
 		self.auth()
-		kwargs = dict(
-			access_token=self.access_token,
-			start_date=start_date,
-			end_date=end_date
-		)
+		kwargs = dict(access_token=self.access_token, start_date=start_date, end_date=end_date)
 		if account_id:
 			kwargs.update(dict(account_ids=[account_id]))
 
@@ -94,10 +94,12 @@
 			response = self.client.Transactions.get(**kwargs)
 			transactions = response["transactions"]
 			while len(transactions) < response["total_transactions"]:
-				response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, offset=len(transactions))
+				response = self.client.Transactions.get(
+					self.access_token, start_date=start_date, end_date=end_date, offset=len(transactions)
+				)
 				transactions.extend(response["transactions"])
 			return transactions
 		except ItemError as e:
 			raise e
 		except Exception:
-			frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
+			frappe.log_error("Plaid: Transactions sync error")
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
index 7e6f146..62ea85f 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
@@ -29,7 +29,7 @@
 		return {
 			"plaid_env": plaid_settings.plaid_env,
 			"link_token": plaid_settings.get_link_token(),
-			"client_name": frappe.local.site
+			"client_name": frappe.local.site,
 		}
 
 	return "disabled"
@@ -45,14 +45,16 @@
 
 	if not frappe.db.exists("Bank", response["institution"]["name"]):
 		try:
-			bank = frappe.get_doc({
-				"doctype": "Bank",
-				"bank_name": response["institution"]["name"],
-				"plaid_access_token": access_token
-			})
+			bank = frappe.get_doc(
+				{
+					"doctype": "Bank",
+					"bank_name": response["institution"]["name"],
+					"plaid_access_token": access_token,
+				}
+			)
 			bank.insert()
 		except Exception:
-			frappe.log_error(frappe.get_traceback(), title=_('Plaid Link Error'))
+			frappe.log_error("Plaid Link Error")
 	else:
 		bank = frappe.get_doc("Bank", response["institution"]["name"])
 		bank.plaid_access_token = access_token
@@ -89,65 +91,71 @@
 
 		if not existing_bank_account:
 			try:
-				new_account = frappe.get_doc({
-					"doctype": "Bank Account",
-					"bank": bank["bank_name"],
-					"account": default_gl_account.account,
-					"account_name": account["name"],
-					"account_type": account.get("type", ""),
-					"account_subtype": account.get("subtype", ""),
-					"mask": account.get("mask", ""),
-					"integration_id": account["id"],
-					"is_company_account": 1,
-					"company": company
-				})
+				new_account = frappe.get_doc(
+					{
+						"doctype": "Bank Account",
+						"bank": bank["bank_name"],
+						"account": default_gl_account.account,
+						"account_name": account["name"],
+						"account_type": account.get("type", ""),
+						"account_subtype": account.get("subtype", ""),
+						"mask": account.get("mask", ""),
+						"integration_id": account["id"],
+						"is_company_account": 1,
+						"company": company,
+					}
+				)
 				new_account.insert()
 
 				result.append(new_account.name)
 			except frappe.UniqueValidationError:
-				frappe.msgprint(_("Bank account {0} already exists and could not be created again").format(account["name"]))
+				frappe.msgprint(
+					_("Bank account {0} already exists and could not be created again").format(account["name"])
+				)
 			except Exception:
-				frappe.log_error(frappe.get_traceback(), title=_("Plaid Link Error"))
-				frappe.throw(_("There was an error creating Bank Account while linking with Plaid."),
-					title=_("Plaid Link Failed"))
+				frappe.log_error("Plaid Link Error")
+				frappe.throw(
+					_("There was an error creating Bank Account while linking with Plaid."),
+					title=_("Plaid Link Failed"),
+				)
 
 		else:
 			try:
-				existing_account = frappe.get_doc('Bank Account', existing_bank_account)
-				existing_account.update({
-					"bank": bank["bank_name"],
-					"account_name": account["name"],
-					"account_type": account.get("type", ""),
-					"account_subtype": account.get("subtype", ""),
-					"mask": account.get("mask", ""),
-					"integration_id": account["id"]
-				})
+				existing_account = frappe.get_doc("Bank Account", existing_bank_account)
+				existing_account.update(
+					{
+						"bank": bank["bank_name"],
+						"account_name": account["name"],
+						"account_type": account.get("type", ""),
+						"account_subtype": account.get("subtype", ""),
+						"mask": account.get("mask", ""),
+						"integration_id": account["id"],
+					}
+				)
 				existing_account.save()
 				result.append(existing_bank_account)
 			except Exception:
-				frappe.log_error(frappe.get_traceback(), title=_("Plaid Link Error"))
-				frappe.throw(_("There was an error updating Bank Account {} while linking with Plaid.").format(
-					existing_bank_account), title=_("Plaid Link Failed"))
+				frappe.log_error("Plaid Link Error")
+				frappe.throw(
+					_("There was an error updating Bank Account {} while linking with Plaid.").format(
+						existing_bank_account
+					),
+					title=_("Plaid Link Failed"),
+				)
 
 	return result
 
 
 def add_account_type(account_type):
 	try:
-		frappe.get_doc({
-			"doctype": "Bank Account Type",
-			"account_type": account_type
-		}).insert()
+		frappe.get_doc({"doctype": "Bank Account Type", "account_type": account_type}).insert()
 	except Exception:
 		frappe.throw(frappe.get_traceback())
 
 
 def add_account_subtype(account_subtype):
 	try:
-		frappe.get_doc({
-			"doctype": "Bank Account Subtype",
-			"account_subtype": account_subtype
-		}).insert()
+		frappe.get_doc({"doctype": "Bank Account Subtype", "account_subtype": account_subtype}).insert()
 	except Exception:
 		frappe.throw(frappe.get_traceback())
 
@@ -164,19 +172,26 @@
 	end_date = formatdate(today(), "YYYY-MM-dd")
 
 	try:
-		transactions = get_transactions(bank=bank, bank_account=bank_account, start_date=start_date, end_date=end_date)
+		transactions = get_transactions(
+			bank=bank, bank_account=bank_account, start_date=start_date, end_date=end_date
+		)
 
 		result = []
 		for transaction in reversed(transactions):
 			result += new_bank_transaction(transaction)
 
 		if result:
-			last_transaction_date = frappe.db.get_value('Bank Transaction', result.pop(), 'date')
+			last_transaction_date = frappe.db.get_value("Bank Transaction", result.pop(), "date")
 
-			frappe.logger().info("Plaid added {} new Bank Transactions from '{}' between {} and {}".format(
-				len(result), bank_account, start_date, end_date))
+			frappe.logger().info(
+				"Plaid added {} new Bank Transactions from '{}' between {} and {}".format(
+					len(result), bank_account, start_date, end_date
+				)
+			)
 
-			frappe.db.set_value("Bank Account", bank_account, "last_integration_date", last_transaction_date)
+			frappe.db.set_value(
+				"Bank Account", bank_account, "last_integration_date", last_transaction_date
+			)
 	except Exception:
 		frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
 
@@ -185,7 +200,9 @@
 	access_token = None
 
 	if bank_account:
-		related_bank = frappe.db.get_values("Bank Account", bank_account, ["bank", "integration_id"], as_dict=True)
+		related_bank = frappe.db.get_values(
+			"Bank Account", bank_account, ["bank", "integration_id"], as_dict=True
+		)
 		access_token = frappe.db.get_value("Bank", related_bank[0].bank, "plaid_access_token")
 		account_id = related_bank[0].integration_id
 	else:
@@ -196,7 +213,9 @@
 
 	transactions = []
 	try:
-		transactions = plaid.get_transactions(start_date=start_date, end_date=end_date, account_id=account_id)
+		transactions = plaid.get_transactions(
+			start_date=start_date, end_date=end_date, account_id=account_id
+		)
 	except ItemError as e:
 		if e.code == "ITEM_LOGIN_REQUIRED":
 			msg = _("There was an error syncing transactions.") + " "
@@ -229,18 +248,20 @@
 
 	if not frappe.db.exists("Bank Transaction", dict(transaction_id=transaction["transaction_id"])):
 		try:
-			new_transaction = frappe.get_doc({
-				"doctype": "Bank Transaction",
-				"date": getdate(transaction["date"]),
-				"status": status,
-				"bank_account": bank_account,
-				"deposit": debit,
-				"withdrawal": credit,
-				"currency": transaction["iso_currency_code"],
-				"transaction_id": transaction["transaction_id"],
-				"reference_number": transaction["payment_meta"]["reference_number"],
-				"description": transaction["name"]
-			})
+			new_transaction = frappe.get_doc(
+				{
+					"doctype": "Bank Transaction",
+					"date": getdate(transaction["date"]),
+					"status": status,
+					"bank_account": bank_account,
+					"deposit": debit,
+					"withdrawal": credit,
+					"currency": transaction["iso_currency_code"],
+					"transaction_id": transaction["transaction_id"],
+					"reference_number": transaction["payment_meta"]["reference_number"],
+					"description": transaction["name"],
+				}
+			)
 			new_transaction.insert()
 			new_transaction.submit()
 
@@ -250,7 +271,7 @@
 			result.append(new_transaction.name)
 
 		except Exception:
-			frappe.throw(title=_('Bank transaction creation error'))
+			frappe.throw(title=_("Bank transaction creation error"))
 
 	return result
 
@@ -260,19 +281,21 @@
 	if settings.enabled == 1 and settings.automatic_sync == 1:
 		enqueue_synchronization()
 
+
 @frappe.whitelist()
 def enqueue_synchronization():
-	plaid_accounts = frappe.get_all("Bank Account",
-		filters={"integration_id": ["!=", ""]},
-		fields=["name", "bank"])
+	plaid_accounts = frappe.get_all(
+		"Bank Account", filters={"integration_id": ["!=", ""]}, fields=["name", "bank"]
+	)
 
 	for plaid_account in plaid_accounts:
 		frappe.enqueue(
 			"erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions",
 			bank=plaid_account.bank,
-			bank_account=plaid_account.name
+			bank_account=plaid_account.name,
 		)
 
+
 @frappe.whitelist()
 def get_link_token_for_update(access_token):
 	plaid = PlaidConnector(access_token)
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py
index 535d7fa..e8dc3e2 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py
@@ -45,111 +45,110 @@
 
 	def test_default_bank_account(self):
 		if not frappe.db.exists("Bank", "Citi"):
-			frappe.get_doc({
-				"doctype": "Bank",
-				"bank_name": "Citi"
-			}).insert()
+			frappe.get_doc({"doctype": "Bank", "bank_name": "Citi"}).insert()
 
 		bank_accounts = {
-			'account': {
-				'subtype': 'checking',
-				'mask': '0000',
-				'type': 'depository',
-				'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
-				'name': 'Plaid Checking'
+			"account": {
+				"subtype": "checking",
+				"mask": "0000",
+				"type": "depository",
+				"id": "6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK",
+				"name": "Plaid Checking",
 			},
-			'account_id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
-			'link_session_id': 'db673d75-61aa-442a-864f-9b3f174f3725',
-			'accounts': [{
-				'type': 'depository',
-				'subtype': 'checking',
-				'mask': '0000',
-				'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
-				'name': 'Plaid Checking'
-			}],
-			'institution': {
-				'institution_id': 'ins_6',
-				'name': 'Citi'
-			}
+			"account_id": "6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK",
+			"link_session_id": "db673d75-61aa-442a-864f-9b3f174f3725",
+			"accounts": [
+				{
+					"type": "depository",
+					"subtype": "checking",
+					"mask": "0000",
+					"id": "6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK",
+					"name": "Plaid Checking",
+				}
+			],
+			"institution": {"institution_id": "ins_6", "name": "Citi"},
 		}
 
 		bank = json.dumps(frappe.get_doc("Bank", "Citi").as_dict(), default=json_handler)
-		company = frappe.db.get_single_value('Global Defaults', 'default_company')
+		company = frappe.db.get_single_value("Global Defaults", "default_company")
 		frappe.db.set_value("Company", company, "default_bank_account", None)
 
-		self.assertRaises(frappe.ValidationError, add_bank_accounts, response=bank_accounts, bank=bank, company=company)
+		self.assertRaises(
+			frappe.ValidationError, add_bank_accounts, response=bank_accounts, bank=bank, company=company
+		)
 
 	def test_new_transaction(self):
 		if not frappe.db.exists("Bank", "Citi"):
-			frappe.get_doc({
-				"doctype": "Bank",
-				"bank_name": "Citi"
-			}).insert()
+			frappe.get_doc({"doctype": "Bank", "bank_name": "Citi"}).insert()
 
 		bank_accounts = {
-			'account': {
-				'subtype': 'checking',
-				'mask': '0000',
-				'type': 'depository',
-				'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
-				'name': 'Plaid Checking'
+			"account": {
+				"subtype": "checking",
+				"mask": "0000",
+				"type": "depository",
+				"id": "6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK",
+				"name": "Plaid Checking",
 			},
-			'account_id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
-			'link_session_id': 'db673d75-61aa-442a-864f-9b3f174f3725',
-			'accounts': [{
-				'type': 'depository',
-				'subtype': 'checking',
-				'mask': '0000',
-				'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
-				'name': 'Plaid Checking'
-			}],
-			'institution': {
-				'institution_id': 'ins_6',
-				'name': 'Citi'
-			}
+			"account_id": "6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK",
+			"link_session_id": "db673d75-61aa-442a-864f-9b3f174f3725",
+			"accounts": [
+				{
+					"type": "depository",
+					"subtype": "checking",
+					"mask": "0000",
+					"id": "6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK",
+					"name": "Plaid Checking",
+				}
+			],
+			"institution": {"institution_id": "ins_6", "name": "Citi"},
 		}
 
 		bank = json.dumps(frappe.get_doc("Bank", "Citi").as_dict(), default=json_handler)
-		company = frappe.db.get_single_value('Global Defaults', 'default_company')
+		company = frappe.db.get_single_value("Global Defaults", "default_company")
 
 		if frappe.db.get_value("Company", company, "default_bank_account") is None:
-			frappe.db.set_value("Company", company, "default_bank_account", get_default_bank_cash_account(company, "Cash").get("account"))
+			frappe.db.set_value(
+				"Company",
+				company,
+				"default_bank_account",
+				get_default_bank_cash_account(company, "Cash").get("account"),
+			)
 
 		add_bank_accounts(bank_accounts, bank, company)
 
 		transactions = {
-			'account_owner': None,
-			'category': ['Food and Drink', 'Restaurants'],
-			'account_id': 'b4Jkp1LJDZiPgojpr1ansXJrj5Q6w9fVmv6ov',
-			'pending_transaction_id': None,
-			'transaction_id': 'x374xPa7DvUewqlR5mjNIeGK8r8rl3Sn647LM',
-			'unofficial_currency_code': None,
-			'name': 'INTRST PYMNT',
-			'transaction_type': 'place',
-			'amount': -4.22,
-			'location': {
-				'city': None,
-				'zip': None,
-				'store_number': None,
-				'lon': None,
-				'state': None,
-				'address': None,
-				'lat': None
+			"account_owner": None,
+			"category": ["Food and Drink", "Restaurants"],
+			"account_id": "b4Jkp1LJDZiPgojpr1ansXJrj5Q6w9fVmv6ov",
+			"pending_transaction_id": None,
+			"transaction_id": "x374xPa7DvUewqlR5mjNIeGK8r8rl3Sn647LM",
+			"unofficial_currency_code": None,
+			"name": "INTRST PYMNT",
+			"transaction_type": "place",
+			"amount": -4.22,
+			"location": {
+				"city": None,
+				"zip": None,
+				"store_number": None,
+				"lon": None,
+				"state": None,
+				"address": None,
+				"lat": None,
 			},
-			'payment_meta': {
-				'reference_number': None,
-				'payer': None,
-				'payment_method': None,
-				'reason': None,
-				'payee': None,
-				'ppd_id': None,
-				'payment_processor': None,
-				'by_order_of': None
+			"payment_meta": {
+				"reference_number": None,
+				"payer": None,
+				"payment_method": None,
+				"reason": None,
+				"payee": None,
+				"ppd_id": None,
+				"payment_processor": None,
+				"by_order_of": None,
 			},
-			'date': '2017-12-22',
-			'category_id': '13005000',
-			'pending': False,
-			'iso_currency_code': 'USD'
+			"date": "2017-12-22",
+			"category_id": "13005000",
+			"pending": False,
+			"iso_currency_code": "USD",
 		}
 
 		new_bank_transaction(transactions)
diff --git a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py
index 5de5682..b93c5c4 100644
--- a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py
+++ b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py
@@ -37,30 +37,27 @@
 	def __init__(self, *args, **kwargs):
 		super(QuickBooksMigrator, self).__init__(*args, **kwargs)
 		self.oauth = OAuth2Session(
-			client_id=self.client_id,
-			redirect_uri=self.redirect_url,
-			scope=self.scope
+			client_id=self.client_id, redirect_uri=self.redirect_url, scope=self.scope
 		)
 		if not self.authorization_url and self.authorization_endpoint:
 			self.authorization_url = self.oauth.authorization_url(self.authorization_endpoint)[0]
 
-
 	def on_update(self):
 		if self.company:
 			# We need a Cost Center corresponding to the selected erpnext Company
-			self.default_cost_center = frappe.db.get_value('Company', self.company, 'cost_center')
-			company_warehouses = frappe.get_all('Warehouse', filters={"company": self.company, "is_group": 0})
+			self.default_cost_center = frappe.db.get_value("Company", self.company, "cost_center")
+			company_warehouses = frappe.get_all(
+				"Warehouse", filters={"company": self.company, "is_group": 0}
+			)
 			if company_warehouses:
 				self.default_warehouse = company_warehouses[0].name
 		if self.authorization_endpoint:
 			self.authorization_url = self.oauth.authorization_url(self.authorization_endpoint)[0]
 
-
 	@frappe.whitelist()
 	def migrate(self):
 		frappe.enqueue_doc("QuickBooks Migrator", "QuickBooks Migrator", "_migrate", queue="long")
 
-
 	def _migrate(self):
 		try:
 			self.set_indicator("In Progress")
@@ -86,18 +83,33 @@
 			# Following entities are directly available from API
 			# Invoice can be an exception sometimes though (as explained above).
 			entities_for_normal_transform = [
-				"Customer", "Item", "Vendor",
+				"Customer",
+				"Item",
+				"Vendor",
 				"Preferences",
-				"JournalEntry", "Purchase", "Deposit",
-				"Invoice", "CreditMemo", "SalesReceipt", "RefundReceipt",
-				"Bill", "VendorCredit",
-				"Payment", "BillPayment",
+				"JournalEntry",
+				"Purchase",
+				"Deposit",
+				"Invoice",
+				"CreditMemo",
+				"SalesReceipt",
+				"RefundReceipt",
+				"Bill",
+				"VendorCredit",
+				"Payment",
+				"BillPayment",
 			]
 			for entity in entities_for_normal_transform:
 				self._migrate_entries(entity)
 
 			# Following entries are not available directly from API, Need to be regenrated from GeneralLedger Report
-			entities_for_gl_transform = ["Advance Payment", "Tax Payment", "Sales Tax Payment", "Purchase Tax Payment", "Inventory Qty Adjust"]
+			entities_for_gl_transform = [
+				"Advance Payment",
+				"Tax Payment",
+				"Sales Tax Payment",
+				"Purchase Tax Payment",
+				"Inventory Qty Adjust",
+			]
 			for entity in entities_for_gl_transform:
 				self._migrate_entries_from_gl(entity)
 			self.set_indicator("Complete")
@@ -107,33 +119,37 @@
 
 		frappe.db.commit()
 
-
 	def get_tokens(self):
 		token = self.oauth.fetch_token(
-			token_url=self.token_endpoint,
-			client_secret=self.client_secret,
-			code=self.code
+			token_url=self.token_endpoint, client_secret=self.client_secret, code=self.code
 		)
 		self.access_token = token["access_token"]
 		self.refresh_token = token["refresh_token"]
 		self.save()
 
-
 	def _refresh_tokens(self):
 		token = self.oauth.refresh_token(
 			token_url=self.token_endpoint,
 			client_id=self.client_id,
 			refresh_token=self.refresh_token,
 			client_secret=self.client_secret,
-			code=self.code
+			code=self.code,
 		)
 		self.access_token = token["access_token"]
 		self.refresh_token = token["refresh_token"]
 		self.save()
 
-
 	def _make_custom_fields(self):
-		doctypes_for_quickbooks_id_field = ["Account", "Customer", "Address", "Item", "Supplier", "Sales Invoice", "Journal Entry", "Purchase Invoice"]
+		doctypes_for_quickbooks_id_field = [
+			"Account",
+			"Customer",
+			"Address",
+			"Item",
+			"Supplier",
+			"Sales Invoice",
+			"Journal Entry",
+			"Purchase Invoice",
+		]
 		for doctype in doctypes_for_quickbooks_id_field:
 			self._make_custom_quickbooks_id_field(doctype)
 
@@ -143,53 +159,60 @@
 
 		frappe.db.commit()
 
-
 	def _make_custom_quickbooks_id_field(self, doctype):
 		if not frappe.get_meta(doctype).has_field("quickbooks_id"):
-			frappe.get_doc({
-				"doctype": "Custom Field",
-				"label": "QuickBooks ID",
-				"dt": doctype,
-				"fieldname": "quickbooks_id",
-				"fieldtype": "Data",
-			}).insert()
-
+			frappe.get_doc(
+				{
+					"doctype": "Custom Field",
+					"label": "QuickBooks ID",
+					"dt": doctype,
+					"fieldname": "quickbooks_id",
+					"fieldtype": "Data",
+				}
+			).insert()
 
 	def _make_custom_company_field(self, doctype):
 		if not frappe.get_meta(doctype).has_field("company"):
-			frappe.get_doc({
-				"doctype": "Custom Field",
-				"label": "Company",
-				"dt": doctype,
-				"fieldname": "company",
-				"fieldtype": "Link",
-				"options": "Company",
-			}).insert()
-
+			frappe.get_doc(
+				{
+					"doctype": "Custom Field",
+					"label": "Company",
+					"dt": doctype,
+					"fieldname": "company",
+					"fieldtype": "Link",
+					"options": "Company",
+				}
+			).insert()
 
 	def _migrate_accounts(self):
 		self._make_root_accounts()
 		for entity in ["Account", "TaxRate", "TaxCode"]:
 			self._migrate_entries(entity)
 
-
 	def _make_root_accounts(self):
 		roots = ["Asset", "Equity", "Expense", "Liability", "Income"]
 		for root in roots:
 			try:
-				if not frappe.db.exists({"doctype": "Account", "name": encode_company_abbr("{} - QB".format(root), self.company), "company": self.company}):
-					frappe.get_doc({
+				if not frappe.db.exists(
+					{
 						"doctype": "Account",
-						"account_name": "{} - QB".format(root),
-						"root_type": root,
-						"is_group": "1",
+						"name": encode_company_abbr("{} - QB".format(root), self.company),
 						"company": self.company,
-					}).insert(ignore_mandatory=True)
+					}
+				):
+					frappe.get_doc(
+						{
+							"doctype": "Account",
+							"account_name": "{} - QB".format(root),
+							"root_type": root,
+							"is_group": "1",
+							"company": self.company,
+						}
+					).insert(ignore_mandatory=True)
 			except Exception as e:
 				self._log_error(e, root)
 		frappe.db.commit()
 
-
 	def _migrate_entries(self, entity):
 		try:
 			query_uri = "{}/company/{}/query".format(
@@ -198,22 +221,19 @@
 			)
 			max_result_count = 1000
 			# Count number of entries
-			response = self._get(query_uri,
-				params={
-					"query": """SELECT COUNT(*) FROM {}""".format(entity)
-				}
-			)
+			response = self._get(query_uri, params={"query": """SELECT COUNT(*) FROM {}""".format(entity)})
 			entry_count = response.json()["QueryResponse"]["totalCount"]
 
 			# fetch pages and accumulate
 			entries = []
 			for start_position in range(1, entry_count + 1, max_result_count):
-				response = self._get(query_uri,
+				response = self._get(
+					query_uri,
 					params={
 						"query": """SELECT * FROM {} STARTPOSITION {} MAXRESULTS {}""".format(
 							entity, start_position, max_result_count
 						)
-					}
+					},
 				)
 				entries.extend(response.json()["QueryResponse"][entity])
 			entries = self._preprocess_entries(entity, entries)
@@ -221,16 +241,18 @@
 		except Exception as e:
 			self._log_error(e, response.text)
 
-
 	def _fetch_general_ledger(self):
 		try:
-			query_uri = "{}/company/{}/reports/GeneralLedger".format(self.api_endpoint, self.quickbooks_company_id)
-			response = self._get(query_uri,
+			query_uri = "{}/company/{}/reports/GeneralLedger".format(
+				self.api_endpoint, self.quickbooks_company_id
+			)
+			response = self._get(
+				query_uri,
 				params={
 					"columns": ",".join(["tx_date", "txn_type", "credit_amt", "debt_amt"]),
 					"date_macro": "All",
 					"minorversion": 3,
-				}
+				},
 			)
 			self.gl_entries = {}
 			for section in response.json()["Rows"]["Row"]:
@@ -250,7 +272,6 @@
 		except Exception as e:
 			self._log_error(e, response.text)
 
-
 	def _create_fiscal_years(self):
 		try:
 			# Assumes that exactly one fiscal year has been created so far
@@ -258,10 +279,12 @@
 			from itertools import chain
 
 			from frappe.utils.data import add_years, getdate
-			smallest_ledger_entry_date = getdate(min(entry["date"] for entry in chain(*self.gl_entries.values()) if entry["date"]))
-			oldest_fiscal_year = frappe.get_all("Fiscal Year",
-				fields=["year_start_date", "year_end_date"],
-				order_by="year_start_date"
+
+			smallest_ledger_entry_date = getdate(
+				min(entry["date"] for entry in chain(*self.gl_entries.values()) if entry["date"])
+			)
+			oldest_fiscal_year = frappe.get_all(
+				"Fiscal Year", fields=["year_start_date", "year_end_date"], order_by="year_start_date"
 			)[0]
 			# Keep on creating fiscal years
 			# until smallest_ledger_entry_date is no longer smaller than the oldest fiscal year's start date
@@ -272,7 +295,9 @@
 				if new_fiscal_year.year_start_date.year == new_fiscal_year.year_end_date.year:
 					new_fiscal_year.year = new_fiscal_year.year_start_date.year
 				else:
-					new_fiscal_year.year = "{}-{}".format(new_fiscal_year.year_start_date.year, new_fiscal_year.year_end_date.year)
+					new_fiscal_year.year = "{}-{}".format(
+						new_fiscal_year.year_start_date.year, new_fiscal_year.year_end_date.year
+					)
 				new_fiscal_year.save()
 				oldest_fiscal_year = new_fiscal_year
 
@@ -280,40 +305,30 @@
 		except Exception as e:
 			self._log_error(e)
 
-
 	def _migrate_entries_from_gl(self, entity):
 		if entity in self.general_ledger:
 			self._save_entries(entity, self.general_ledger[entity].values())
 
-
 	def _save_entries(self, entity, entries):
 		entity_method_map = {
 			"Account": self._save_account,
 			"TaxRate": self._save_tax_rate,
 			"TaxCode": self._save_tax_code,
-
 			"Preferences": self._save_preference,
-
 			"Customer": self._save_customer,
 			"Item": self._save_item,
 			"Vendor": self._save_vendor,
-
 			"Invoice": self._save_invoice,
 			"CreditMemo": self._save_credit_memo,
 			"SalesReceipt": self._save_sales_receipt,
 			"RefundReceipt": self._save_refund_receipt,
-
 			"JournalEntry": self._save_journal_entry,
-
 			"Bill": self._save_bill,
 			"VendorCredit": self._save_vendor_credit,
-
 			"Payment": self._save_payment,
 			"BillPayment": self._save_bill_payment,
-
 			"Purchase": self._save_purchase,
 			"Deposit": self._save_deposit,
-
 			"Advance Payment": self._save_advance_payment,
 			"Tax Payment": self._save_tax_payment,
 			"Sales Tax Payment": self._save_tax_payment,
@@ -322,11 +337,17 @@
 		}
 		total = len(entries)
 		for index, entry in enumerate(entries, start=1):
-			self._publish({"event": "progress", "message": _("Saving {0}").format(entity), "count": index, "total": total})
+			self._publish(
+				{
+					"event": "progress",
+					"message": _("Saving {0}").format(entity),
+					"count": index,
+					"total": total,
+				}
+			)
 			entity_method_map[entity](entry)
 		frappe.db.commit()
 
-
 	def _preprocess_entries(self, entity, entries):
 		entity_method_map = {
 			"Account": self._preprocess_accounts,
@@ -338,7 +359,6 @@
 			entries = preprocessor(entries)
 		return entries
 
-
 	def _get_gl_entries_from_section(self, section, account=None):
 		if "Header" in section:
 			if "id" in section["Header"]["ColData"][0]:
@@ -359,19 +379,20 @@
 		for row in section["Rows"]["Row"]:
 			if row["type"] == "Data":
 				data = row["ColData"]
-				entries.append({
-					"account": account,
-					"date": data[0]["value"],
-					"type": data[1]["value"],
-					"id": data[1].get("id"),
-					"credit": frappe.utils.flt(data[2]["value"]),
-					"debit": frappe.utils.flt(data[3]["value"]),
-				})
+				entries.append(
+					{
+						"account": account,
+						"date": data[0]["value"],
+						"type": data[1]["value"],
+						"id": data[1].get("id"),
+						"credit": frappe.utils.flt(data[2]["value"]),
+						"debit": frappe.utils.flt(data[3]["value"]),
+					}
+				)
 			if row["type"] == "Section":
 				self._get_gl_entries_from_section(row, account)
 		self.gl_entries.setdefault(account, []).extend(entries)
 
-
 	def _preprocess_accounts(self, accounts):
 		self.accounts = {account["Name"]: account for account in accounts}
 		for account in accounts:
@@ -381,7 +402,6 @@
 				account["is_group"] = 0
 		return sorted(accounts, key=lambda account: int(account["Id"]))
 
-
 	def _save_account(self, account):
 		mapping = {
 			"Bank": "Asset",
@@ -389,24 +409,22 @@
 			"Fixed Asset": "Asset",
 			"Other Asset": "Asset",
 			"Accounts Receivable": "Asset",
-
 			"Equity": "Equity",
-
 			"Expense": "Expense",
 			"Other Expense": "Expense",
 			"Cost of Goods Sold": "Expense",
-
 			"Accounts Payable": "Liability",
 			"Credit Card": "Liability",
 			"Long Term Liability": "Liability",
 			"Other Current Liability": "Liability",
-
 			"Income": "Income",
 			"Other Income": "Income",
 		}
 		# Map Quickbooks Account Types to ERPNext root_accunts and and root_type
 		try:
-			if not frappe.db.exists({"doctype": "Account", "quickbooks_id": account["Id"], "company": self.company}):
+			if not frappe.db.exists(
+				{"doctype": "Account", "quickbooks_id": account["Id"], "company": self.company}
+			):
 				is_child = account["SubAccount"]
 				is_group = account["is_group"]
 				# Create Two Accounts for every Group Account
@@ -416,103 +434,125 @@
 					account_id = account["Id"]
 
 				if is_child:
-					parent_account = self._get_account_name_by_id("Group - {}".format(account["ParentRef"]["value"]))
+					parent_account = self._get_account_name_by_id(
+						"Group - {}".format(account["ParentRef"]["value"])
+					)
 				else:
-					parent_account = encode_company_abbr("{} - QB".format(mapping[account["AccountType"]]), self.company)
+					parent_account = encode_company_abbr(
+						"{} - QB".format(mapping[account["AccountType"]]), self.company
+					)
 
-				frappe.get_doc({
-					"doctype": "Account",
-					"quickbooks_id": account_id,
-					"account_name": self._get_unique_account_name(account["Name"]),
-					"root_type": mapping[account["AccountType"]],
-					"account_type": self._get_account_type(account),
-					"account_currency": account["CurrencyRef"]["value"],
-					"parent_account": parent_account,
-					"is_group": is_group,
-					"company": self.company,
-				}).insert()
-
-				if is_group:
-					# Create a Leaf account corresponding to the group account
-					frappe.get_doc({
+				frappe.get_doc(
+					{
 						"doctype": "Account",
-						"quickbooks_id": account["Id"],
+						"quickbooks_id": account_id,
 						"account_name": self._get_unique_account_name(account["Name"]),
 						"root_type": mapping[account["AccountType"]],
 						"account_type": self._get_account_type(account),
 						"account_currency": account["CurrencyRef"]["value"],
-						"parent_account": self._get_account_name_by_id(account_id),
-						"is_group": 0,
+						"parent_account": parent_account,
+						"is_group": is_group,
 						"company": self.company,
-					}).insert()
+					}
+				).insert()
+
+				if is_group:
+					# Create a Leaf account corresponding to the group account
+					frappe.get_doc(
+						{
+							"doctype": "Account",
+							"quickbooks_id": account["Id"],
+							"account_name": self._get_unique_account_name(account["Name"]),
+							"root_type": mapping[account["AccountType"]],
+							"account_type": self._get_account_type(account),
+							"account_currency": account["CurrencyRef"]["value"],
+							"parent_account": self._get_account_name_by_id(account_id),
+							"is_group": 0,
+							"company": self.company,
+						}
+					).insert()
 				if account.get("AccountSubType") == "UndepositedFunds":
 					self.undeposited_funds_account = self._get_account_name_by_id(account["Id"])
 					self.save()
 		except Exception as e:
 			self._log_error(e, account)
 
-
 	def _get_account_type(self, account):
 		account_subtype_mapping = {"UndepositedFunds": "Cash"}
 		account_type = account_subtype_mapping.get(account.get("AccountSubType"))
 		if account_type is None:
-			account_type_mapping = {"Accounts Payable": "Payable", "Accounts Receivable": "Receivable", "Bank": "Bank", "Credit Card": "Bank"}
+			account_type_mapping = {
+				"Accounts Payable": "Payable",
+				"Accounts Receivable": "Receivable",
+				"Bank": "Bank",
+				"Credit Card": "Bank",
+			}
 			account_type = account_type_mapping.get(account["AccountType"])
 		return account_type
 
-
 	def _preprocess_tax_rates(self, tax_rates):
 		self.tax_rates = {tax_rate["Id"]: tax_rate for tax_rate in tax_rates}
 		return tax_rates
 
-
 	def _save_tax_rate(self, tax_rate):
 		try:
-			if not frappe.db.exists({"doctype": "Account", "quickbooks_id": "TaxRate - {}".format(tax_rate["Id"]), "company": self.company}):
-				frappe.get_doc({
+			if not frappe.db.exists(
+				{
 					"doctype": "Account",
 					"quickbooks_id": "TaxRate - {}".format(tax_rate["Id"]),
-					"account_name": "{} - QB".format(tax_rate["Name"]),
-					"root_type": "Liability",
-					"parent_account": encode_company_abbr("{} - QB".format("Liability"), self.company),
-					"is_group": "0",
 					"company": self.company,
-				}).insert()
+				}
+			):
+				frappe.get_doc(
+					{
+						"doctype": "Account",
+						"quickbooks_id": "TaxRate - {}".format(tax_rate["Id"]),
+						"account_name": "{} - QB".format(tax_rate["Name"]),
+						"root_type": "Liability",
+						"parent_account": encode_company_abbr("{} - QB".format("Liability"), self.company),
+						"is_group": "0",
+						"company": self.company,
+					}
+				).insert()
 		except Exception as e:
 			self._log_error(e, tax_rate)
 
-
 	def _preprocess_tax_codes(self, tax_codes):
 		self.tax_codes = {tax_code["Id"]: tax_code for tax_code in tax_codes}
 		return tax_codes
 
-
 	def _save_tax_code(self, tax_code):
 		pass
 
-
 	def _save_customer(self, customer):
 		try:
-			if not frappe.db.exists({"doctype": "Customer", "quickbooks_id": customer["Id"], "company": self.company}):
+			if not frappe.db.exists(
+				{"doctype": "Customer", "quickbooks_id": customer["Id"], "company": self.company}
+			):
 				try:
-					receivable_account = frappe.get_all("Account", filters={
-						"account_type": "Receivable",
-						"account_currency": customer["CurrencyRef"]["value"],
-						"company": self.company,
-					})[0]["name"]
+					receivable_account = frappe.get_all(
+						"Account",
+						filters={
+							"account_type": "Receivable",
+							"account_currency": customer["CurrencyRef"]["value"],
+							"company": self.company,
+						},
+					)[0]["name"]
 				except Exception:
 					receivable_account = None
-				erpcustomer = frappe.get_doc({
-					"doctype": "Customer",
-					"quickbooks_id": customer["Id"],
-					"customer_name": encode_company_abbr(customer["DisplayName"], self.company),
-					"customer_type": "Individual",
-					"customer_group": "Commercial",
-					"default_currency": customer["CurrencyRef"]["value"],
-					"accounts": [{"company": self.company, "account": receivable_account}],
-					"territory": "All Territories",
-					"company": self.company,
-				}).insert()
+				erpcustomer = frappe.get_doc(
+					{
+						"doctype": "Customer",
+						"quickbooks_id": customer["Id"],
+						"customer_name": encode_company_abbr(customer["DisplayName"], self.company),
+						"customer_type": "Individual",
+						"customer_group": "Commercial",
+						"default_currency": customer["CurrencyRef"]["value"],
+						"accounts": [{"company": self.company, "account": receivable_account}],
+						"territory": "All Territories",
+						"company": self.company,
+					}
+				).insert()
 				if "BillAddr" in customer:
 					self._create_address(erpcustomer, "Customer", customer["BillAddr"], "Billing")
 				if "ShipAddr" in customer:
@@ -520,10 +560,11 @@
 		except Exception as e:
 			self._log_error(e, customer)
 
-
 	def _save_item(self, item):
 		try:
-			if not frappe.db.exists({"doctype": "Item", "quickbooks_id": item["Id"], "company": self.company}):
+			if not frappe.db.exists(
+				{"doctype": "Item", "quickbooks_id": item["Id"], "company": self.company}
+			):
 				if item["Type"] in ("Service", "Inventory"):
 					item_dict = {
 						"doctype": "Item",
@@ -533,7 +574,7 @@
 						"is_stock_item": 0,
 						"item_group": "All Item Groups",
 						"company": self.company,
-						"item_defaults": [{"company": self.company, "default_warehouse": self.default_warehouse}]
+						"item_defaults": [{"company": self.company, "default_warehouse": self.default_warehouse}],
 					}
 					if "ExpenseAccountRef" in item:
 						expense_account = self._get_account_name_by_id(item["ExpenseAccountRef"]["value"])
@@ -545,21 +586,23 @@
 		except Exception as e:
 			self._log_error(e, item)
 
-
 	def _allow_fraction_in_unit(self):
 		frappe.db.set_value("UOM", "Unit", "must_be_whole_number", 0)
 
-
 	def _save_vendor(self, vendor):
 		try:
-			if not frappe.db.exists({"doctype": "Supplier", "quickbooks_id": vendor["Id"], "company": self.company}):
-				erpsupplier = frappe.get_doc({
-					"doctype": "Supplier",
-					"quickbooks_id": vendor["Id"],
-					"supplier_name": encode_company_abbr(vendor["DisplayName"], self.company),
-					"supplier_group": "All Supplier Groups",
-					"company": self.company,
-				}).insert()
+			if not frappe.db.exists(
+				{"doctype": "Supplier", "quickbooks_id": vendor["Id"], "company": self.company}
+			):
+				erpsupplier = frappe.get_doc(
+					{
+						"doctype": "Supplier",
+						"quickbooks_id": vendor["Id"],
+						"supplier_name": encode_company_abbr(vendor["DisplayName"], self.company),
+						"supplier_group": "All Supplier Groups",
+						"company": self.company,
+					}
+				).insert()
 				if "BillAddr" in vendor:
 					self._create_address(erpsupplier, "Supplier", vendor["BillAddr"], "Billing")
 				if "ShipAddr" in vendor:
@@ -567,7 +610,6 @@
 		except Exception as e:
 			self._log_error(e)
 
-
 	def _save_preference(self, preference):
 		try:
 			if preference["SalesFormsPrefs"]["AllowShipping"]:
@@ -577,7 +619,6 @@
 		except Exception as e:
 			self._log_error(e, preference)
 
-
 	def _save_invoice(self, invoice):
 		# Invoice can be Linked with Another Transactions
 		# If any of these transactions is a "StatementCharge" or "ReimburseCharge" then in the UI
@@ -585,59 +626,56 @@
 		# Also as of now there is no way of fetching the corresponding transaction from api
 		# We in order to correctly reflect account balance make an equivalent Journal Entry
 		quickbooks_id = "Invoice - {}".format(invoice["Id"])
-		if any(linked["TxnType"] in ("StatementCharge", "ReimburseCharge") for linked in invoice["LinkedTxn"]):
+		if any(
+			linked["TxnType"] in ("StatementCharge", "ReimburseCharge") for linked in invoice["LinkedTxn"]
+		):
 			self._save_invoice_as_journal_entry(invoice, quickbooks_id)
 		else:
 			self._save_sales_invoice(invoice, quickbooks_id)
 
-
 	def _save_credit_memo(self, credit_memo):
 		# Credit Memo is equivalent to a return Sales Invoice
 		quickbooks_id = "Credit Memo - {}".format(credit_memo["Id"])
 		self._save_sales_invoice(credit_memo, quickbooks_id, is_return=True)
 
-
 	def _save_sales_receipt(self, sales_receipt):
 		# Sales Receipt is equivalent to a POS Sales Invoice
 		quickbooks_id = "Sales Receipt - {}".format(sales_receipt["Id"])
 		self._save_sales_invoice(sales_receipt, quickbooks_id, is_pos=True)
 
-
 	def _save_refund_receipt(self, refund_receipt):
 		# Refund Receipt is equivalent to a return POS Sales Invoice
 		quickbooks_id = "Refund Receipt - {}".format(refund_receipt["Id"])
 		self._save_sales_invoice(refund_receipt, quickbooks_id, is_return=True, is_pos=True)
 
-
 	def _save_sales_invoice(self, invoice, quickbooks_id, is_return=False, is_pos=False):
 		try:
-			if not frappe.db.exists({"doctype": "Sales Invoice", "quickbooks_id": quickbooks_id, "company": self.company}):
+			if not frappe.db.exists(
+				{"doctype": "Sales Invoice", "quickbooks_id": quickbooks_id, "company": self.company}
+			):
 				invoice_dict = {
 					"doctype": "Sales Invoice",
 					"quickbooks_id": quickbooks_id,
-
 					# Quickbooks uses ISO 4217 Code
 					# of course this gonna come back to bite me
 					"currency": invoice["CurrencyRef"]["value"],
-
 					# Exchange Rate is provided if multicurrency is enabled
 					# It is not provided if multicurrency is not enabled
 					"conversion_rate": invoice.get("ExchangeRate", 1),
 					"posting_date": invoice["TxnDate"],
-
 					# QuickBooks doesn't make Due Date a mandatory field this is a hack
 					"due_date": invoice.get("DueDate", invoice["TxnDate"]),
-					"customer": frappe.get_all("Customer",
+					"customer": frappe.get_all(
+						"Customer",
 						filters={
 							"quickbooks_id": invoice["CustomerRef"]["value"],
 							"company": self.company,
-						})[0]["name"],
+						},
+					)[0]["name"],
 					"items": self._get_si_items(invoice, is_return=is_return),
 					"taxes": self._get_taxes(invoice),
-
 					# Do not change posting_date upon submission
 					"set_posting_time": 1,
-
 					# QuickBooks doesn't round total
 					"disable_rounded_total": 1,
 					"is_return": is_return,
@@ -659,7 +697,6 @@
 		except Exception as e:
 			self._log_error(e, [invoice, invoice_dict, json.loads(invoice_doc.as_json())])
 
-
 	def _get_si_items(self, invoice, is_return=False):
 		items = []
 		for line in invoice["Line"]:
@@ -672,48 +709,56 @@
 					else:
 						tax_code = "NON"
 				if line["SalesItemLineDetail"]["ItemRef"]["value"] != "SHIPPING_ITEM_ID":
-					item = frappe.db.get_all("Item",
+					item = frappe.db.get_all(
+						"Item",
 						filters={
 							"quickbooks_id": line["SalesItemLineDetail"]["ItemRef"]["value"],
 							"company": self.company,
 						},
-						fields=["name", "stock_uom"]
+						fields=["name", "stock_uom"],
 					)[0]
-					items.append({
-						"item_code": item["name"],
-						"conversion_factor": 1,
-						"uom": item["stock_uom"],
-						"description": line.get("Description", line["SalesItemLineDetail"]["ItemRef"]["name"]),
-						"qty": line["SalesItemLineDetail"]["Qty"],
-						"price_list_rate": line["SalesItemLineDetail"]["UnitPrice"],
-						"cost_center": self.default_cost_center,
-						"warehouse": self.default_warehouse,
-						"item_tax_rate": json.dumps(self._get_item_taxes(tax_code))
-					})
+					items.append(
+						{
+							"item_code": item["name"],
+							"conversion_factor": 1,
+							"uom": item["stock_uom"],
+							"description": line.get("Description", line["SalesItemLineDetail"]["ItemRef"]["name"]),
+							"qty": line["SalesItemLineDetail"]["Qty"],
+							"price_list_rate": line["SalesItemLineDetail"]["UnitPrice"],
+							"cost_center": self.default_cost_center,
+							"warehouse": self.default_warehouse,
+							"item_tax_rate": json.dumps(self._get_item_taxes(tax_code)),
+						}
+					)
 				else:
-					items.append({
-						"item_name": "Shipping",
-						"conversion_factor": 1,
-						"expense_account": self._get_account_name_by_id("TaxRate - {}".format(line["SalesItemLineDetail"]["TaxCodeRef"]["value"])),
-						"uom": "Unit",
-						"description": "Shipping",
-						"income_account": self.default_shipping_account,
-						"qty": 1,
-						"price_list_rate": line["Amount"],
-						"cost_center": self.default_cost_center,
-						"warehouse": self.default_warehouse,
-						"item_tax_rate": json.dumps(self._get_item_taxes(tax_code))
-					})
+					items.append(
+						{
+							"item_name": "Shipping",
+							"conversion_factor": 1,
+							"expense_account": self._get_account_name_by_id(
+								"TaxRate - {}".format(line["SalesItemLineDetail"]["TaxCodeRef"]["value"])
+							),
+							"uom": "Unit",
+							"description": "Shipping",
+							"income_account": self.default_shipping_account,
+							"qty": 1,
+							"price_list_rate": line["Amount"],
+							"cost_center": self.default_cost_center,
+							"warehouse": self.default_warehouse,
+							"item_tax_rate": json.dumps(self._get_item_taxes(tax_code)),
+						}
+					)
 				if is_return:
 					items[-1]["qty"] *= -1
 			elif line["DetailType"] == "DescriptionOnly":
-				items[-1].update({
-					"margin_type": "Percentage",
-					"margin_rate_or_amount": int(line["Description"].split("%")[0]),
-				})
+				items[-1].update(
+					{
+						"margin_type": "Percentage",
+						"margin_rate_or_amount": int(line["Description"].split("%")[0]),
+					}
+				)
 		return items
 
-
 	def _get_item_taxes(self, tax_code):
 		tax_rates = self.tax_rates
 		item_taxes = {}
@@ -723,30 +768,31 @@
 				if rate_list_type in tax_code:
 					for tax_rate_detail in tax_code[rate_list_type]["TaxRateDetail"]:
 						if tax_rate_detail["TaxTypeApplicable"] == "TaxOnAmount":
-							tax_head = self._get_account_name_by_id("TaxRate - {}".format(tax_rate_detail["TaxRateRef"]["value"]))
+							tax_head = self._get_account_name_by_id(
+								"TaxRate - {}".format(tax_rate_detail["TaxRateRef"]["value"])
+							)
 							tax_rate = tax_rates[tax_rate_detail["TaxRateRef"]["value"]]
 							item_taxes[tax_head] = tax_rate["RateValue"]
 		return item_taxes
 
-
 	def _get_invoice_payments(self, invoice, is_return=False, is_pos=False):
 		if is_pos:
 			amount = invoice["TotalAmt"]
 			if is_return:
 				amount = -amount
-			return [{
-				"mode_of_payment": "Cash",
-				"account": self._get_account_name_by_id(invoice["DepositToAccountRef"]["value"]),
-				"amount": amount,
-			}]
-
+			return [
+				{
+					"mode_of_payment": "Cash",
+					"account": self._get_account_name_by_id(invoice["DepositToAccountRef"]["value"]),
+					"amount": amount,
+				}
+			]
 
 	def _get_discount(self, lines):
 		for line in lines:
 			if line["DetailType"] == "DiscountLineDetail" and "Amount" in line["DiscountLineDetail"]:
 				return line
 
-
 	def _save_invoice_as_journal_entry(self, invoice, quickbooks_id):
 		try:
 			accounts = []
@@ -758,8 +804,9 @@
 					account_line["credit_in_account_currency"] = line["credit"]
 				if frappe.db.get_value("Account", line["account"], "account_type") == "Receivable":
 					account_line["party_type"] = "Customer"
-					account_line["party"] = frappe.get_all("Customer",
-						filters={"quickbooks_id": invoice["CustomerRef"]["value"], "company": self.company}
+					account_line["party"] = frappe.get_all(
+						"Customer",
+						filters={"quickbooks_id": invoice["CustomerRef"]["value"], "company": self.company},
 					)[0]["name"]
 
 				accounts.append(account_line)
@@ -769,7 +816,6 @@
 		except Exception as e:
 			self._log_error(e, [invoice, accounts])
 
-
 	def _save_journal_entry(self, journal_entry):
 		# JournalEntry is equivalent to a Journal Entry
 
@@ -782,13 +828,17 @@
 			accounts = []
 			for line in lines:
 				if line["DetailType"] == "JournalEntryLineDetail":
-					account_name = self._get_account_name_by_id(line["JournalEntryLineDetail"]["AccountRef"]["value"])
+					account_name = self._get_account_name_by_id(
+						line["JournalEntryLineDetail"]["AccountRef"]["value"]
+					)
 					posting_type = line["JournalEntryLineDetail"]["PostingType"]
-					accounts.append({
-						"account": account_name,
-						posting_type_field_mapping[posting_type]: line["Amount"],
-						"cost_center": self.default_cost_center,
-					})
+					accounts.append(
+						{
+							"account": account_name,
+							posting_type_field_mapping[posting_type]: line["Amount"],
+							"cost_center": self.default_cost_center,
+						}
+					)
 			return accounts
 
 		quickbooks_id = "Journal Entry - {}".format(journal_entry["Id"])
@@ -796,39 +846,41 @@
 		posting_date = journal_entry["TxnDate"]
 		self.__save_journal_entry(quickbooks_id, accounts, posting_date)
 
-
 	def __save_journal_entry(self, quickbooks_id, accounts, posting_date):
 		try:
-			if not frappe.db.exists({"doctype": "Journal Entry", "quickbooks_id": quickbooks_id, "company": self.company}):
-				je = frappe.get_doc({
-					"doctype": "Journal Entry",
-					"quickbooks_id": quickbooks_id,
-					"company": self.company,
-					"posting_date": posting_date,
-					"accounts": accounts,
-					"multi_currency": 1,
-				})
+			if not frappe.db.exists(
+				{"doctype": "Journal Entry", "quickbooks_id": quickbooks_id, "company": self.company}
+			):
+				je = frappe.get_doc(
+					{
+						"doctype": "Journal Entry",
+						"quickbooks_id": quickbooks_id,
+						"company": self.company,
+						"posting_date": posting_date,
+						"accounts": accounts,
+						"multi_currency": 1,
+					}
+				)
 				je.insert()
 				je.submit()
 		except Exception as e:
 			self._log_error(e, [accounts, json.loads(je.as_json())])
 
-
 	def _save_bill(self, bill):
 		# Bill is equivalent to a Purchase Invoice
 		quickbooks_id = "Bill - {}".format(bill["Id"])
 		self.__save_purchase_invoice(bill, quickbooks_id)
 
-
 	def _save_vendor_credit(self, vendor_credit):
 		# Vendor Credit is equivalent to a return Purchase Invoice
 		quickbooks_id = "Vendor Credit - {}".format(vendor_credit["Id"])
 		self.__save_purchase_invoice(vendor_credit, quickbooks_id, is_return=True)
 
-
 	def __save_purchase_invoice(self, invoice, quickbooks_id, is_return=False):
 		try:
-			if not frappe.db.exists({"doctype": "Purchase Invoice", "quickbooks_id": quickbooks_id, "company": self.company}):
+			if not frappe.db.exists(
+				{"doctype": "Purchase Invoice", "quickbooks_id": quickbooks_id, "company": self.company}
+			):
 				credit_to_account = self._get_account_name_by_id(invoice["APAccountRef"]["value"])
 				invoice_dict = {
 					"doctype": "Purchase Invoice",
@@ -838,11 +890,13 @@
 					"posting_date": invoice["TxnDate"],
 					"due_date": invoice.get("DueDate", invoice["TxnDate"]),
 					"credit_to": credit_to_account,
-					"supplier": frappe.get_all("Supplier",
+					"supplier": frappe.get_all(
+						"Supplier",
 						filters={
 							"quickbooks_id": invoice["VendorRef"]["value"],
 							"company": self.company,
-						})[0]["name"],
+						},
+					)[0]["name"],
 					"items": self._get_pi_items(invoice, is_return=is_return),
 					"taxes": self._get_taxes(invoice),
 					"set_posting_time": 1,
@@ -857,7 +911,6 @@
 		except Exception as e:
 			self._log_error(e, [invoice, invoice_dict, json.loads(invoice_doc.as_json())])
 
-
 	def _get_pi_items(self, purchase_invoice, is_return=False):
 		items = []
 		for line in purchase_invoice["Line"]:
@@ -869,24 +922,29 @@
 						tax_code = purchase_invoice["TxnTaxDetail"]["TxnTaxCodeRef"]["value"]
 					else:
 						tax_code = "NON"
-				item = frappe.db.get_all("Item",
+				item = frappe.db.get_all(
+					"Item",
 					filters={
 						"quickbooks_id": line["ItemBasedExpenseLineDetail"]["ItemRef"]["value"],
-						"company": self.company
+						"company": self.company,
 					},
-					fields=["name", "stock_uom"]
+					fields=["name", "stock_uom"],
 				)[0]
-				items.append({
-					"item_code": item["name"],
-					"conversion_factor": 1,
-					"uom": item["stock_uom"],
-					"description": line.get("Description", line["ItemBasedExpenseLineDetail"]["ItemRef"]["name"]),
-					"qty": line["ItemBasedExpenseLineDetail"]["Qty"],
-					"price_list_rate": line["ItemBasedExpenseLineDetail"]["UnitPrice"],
-					"warehouse": self.default_warehouse,
-					"cost_center": self.default_cost_center,
-					"item_tax_rate": json.dumps(self._get_item_taxes(tax_code)),
-				})
+				items.append(
+					{
+						"item_code": item["name"],
+						"conversion_factor": 1,
+						"uom": item["stock_uom"],
+						"description": line.get(
+							"Description", line["ItemBasedExpenseLineDetail"]["ItemRef"]["name"]
+						),
+						"qty": line["ItemBasedExpenseLineDetail"]["Qty"],
+						"price_list_rate": line["ItemBasedExpenseLineDetail"]["UnitPrice"],
+						"warehouse": self.default_warehouse,
+						"cost_center": self.default_cost_center,
+						"item_tax_rate": json.dumps(self._get_item_taxes(tax_code)),
+					}
+				)
 			elif line["DetailType"] == "AccountBasedExpenseLineDetail":
 				if line["AccountBasedExpenseLineDetail"]["TaxCodeRef"]["value"] != "TAX":
 					tax_code = line["AccountBasedExpenseLineDetail"]["TaxCodeRef"]["value"]
@@ -895,23 +953,30 @@
 						tax_code = purchase_invoice["TxnTaxDetail"]["TxnTaxCodeRef"]["value"]
 					else:
 						tax_code = "NON"
-				items.append({
-					"item_name": line.get("Description", line["AccountBasedExpenseLineDetail"]["AccountRef"]["name"]),
-					"conversion_factor": 1,
-					"expense_account": self._get_account_name_by_id(line["AccountBasedExpenseLineDetail"]["AccountRef"]["value"]),
-					"uom": "Unit",
-					"description": line.get("Description", line["AccountBasedExpenseLineDetail"]["AccountRef"]["name"]),
-					"qty": 1,
-					"price_list_rate": line["Amount"],
-					"warehouse": self.default_warehouse,
-					"cost_center": self.default_cost_center,
-					"item_tax_rate": json.dumps(self._get_item_taxes(tax_code)),
-				})
+				items.append(
+					{
+						"item_name": line.get(
+							"Description", line["AccountBasedExpenseLineDetail"]["AccountRef"]["name"]
+						),
+						"conversion_factor": 1,
+						"expense_account": self._get_account_name_by_id(
+							line["AccountBasedExpenseLineDetail"]["AccountRef"]["value"]
+						),
+						"uom": "Unit",
+						"description": line.get(
+							"Description", line["AccountBasedExpenseLineDetail"]["AccountRef"]["name"]
+						),
+						"qty": 1,
+						"price_list_rate": line["Amount"],
+						"warehouse": self.default_warehouse,
+						"cost_center": self.default_cost_center,
+						"item_tax_rate": json.dumps(self._get_item_taxes(tax_code)),
+					}
+				)
 			if is_return:
 				items[-1]["qty"] *= -1
 		return items
 
-
 	def _save_payment(self, payment):
 		try:
 			quickbooks_id = "Payment - {}".format(payment["Id"])
@@ -928,8 +993,11 @@
 				if linked_transaction["TxnType"] == "Invoice":
 					si_quickbooks_id = "Invoice - {}".format(linked_transaction["TxnId"])
 					# Invoice could have been saved as a Sales Invoice or a Journal Entry
-					if frappe.db.exists({"doctype": "Sales Invoice", "quickbooks_id": si_quickbooks_id, "company": self.company}):
-						sales_invoice = frappe.get_all("Sales Invoice",
+					if frappe.db.exists(
+						{"doctype": "Sales Invoice", "quickbooks_id": si_quickbooks_id, "company": self.company}
+					):
+						sales_invoice = frappe.get_all(
+							"Sales Invoice",
 							filters={
 								"quickbooks_id": si_quickbooks_id,
 								"company": self.company,
@@ -941,43 +1009,51 @@
 						party = sales_invoice["customer"]
 						party_account = sales_invoice["debit_to"]
 
-					if frappe.db.exists({"doctype": "Journal Entry", "quickbooks_id": si_quickbooks_id, "company": self.company}):
-						journal_entry = frappe.get_doc("Journal Entry",
+					if frappe.db.exists(
+						{"doctype": "Journal Entry", "quickbooks_id": si_quickbooks_id, "company": self.company}
+					):
+						journal_entry = frappe.get_doc(
+							"Journal Entry",
 							{
 								"quickbooks_id": si_quickbooks_id,
 								"company": self.company,
-							}
+							},
 						)
 						# Invoice saved as a Journal Entry must have party and party_type set on line containing Receivable Account
-						customer_account_line = list(filter(lambda acc: acc.party_type == "Customer", journal_entry.accounts))[0]
+						customer_account_line = list(
+							filter(lambda acc: acc.party_type == "Customer", journal_entry.accounts)
+						)[0]
 
 						reference_type = "Journal Entry"
 						reference_name = journal_entry.name
 						party = customer_account_line.party
 						party_account = customer_account_line.account
 
-					accounts.append({
-						"party_type": "Customer",
-						"party": party,
-						"reference_type": reference_type,
-						"reference_name": reference_name,
-						"account": party_account,
-						"credit_in_account_currency": line["Amount"],
-						"cost_center": self.default_cost_center,
-					})
+					accounts.append(
+						{
+							"party_type": "Customer",
+							"party": party,
+							"reference_type": reference_type,
+							"reference_name": reference_name,
+							"account": party_account,
+							"credit_in_account_currency": line["Amount"],
+							"cost_center": self.default_cost_center,
+						}
+					)
 
 			deposit_account = self._get_account_name_by_id(payment["DepositToAccountRef"]["value"])
-			accounts.append({
-				"account": deposit_account,
-				"debit_in_account_currency": payment["TotalAmt"],
-				"cost_center": self.default_cost_center,
-			})
+			accounts.append(
+				{
+					"account": deposit_account,
+					"debit_in_account_currency": payment["TotalAmt"],
+					"cost_center": self.default_cost_center,
+				}
+			)
 			posting_date = payment["TxnDate"]
 			self.__save_journal_entry(quickbooks_id, accounts, posting_date)
 		except Exception as e:
 			self._log_error(e, [payment, accounts])
 
-
 	def _save_bill_payment(self, bill_payment):
 		try:
 			quickbooks_id = "BillPayment - {}".format(bill_payment["Id"])
@@ -987,8 +1063,11 @@
 				linked_transaction = line["LinkedTxn"][0]
 				if linked_transaction["TxnType"] == "Bill":
 					pi_quickbooks_id = "Bill - {}".format(linked_transaction["TxnId"])
-					if frappe.db.exists({"doctype": "Purchase Invoice", "quickbooks_id": pi_quickbooks_id, "company": self.company}):
-						purchase_invoice = frappe.get_all("Purchase Invoice",
+					if frappe.db.exists(
+						{"doctype": "Purchase Invoice", "quickbooks_id": pi_quickbooks_id, "company": self.company}
+					):
+						purchase_invoice = frappe.get_all(
+							"Purchase Invoice",
 							filters={
 								"quickbooks_id": pi_quickbooks_id,
 								"company": self.company,
@@ -999,15 +1078,17 @@
 						reference_name = purchase_invoice["name"]
 						party = purchase_invoice["supplier"]
 						party_account = purchase_invoice["credit_to"]
-					accounts.append({
-						"party_type": "Supplier",
-						"party": party,
-						"reference_type": reference_type,
-						"reference_name": reference_name,
-						"account": party_account,
-						"debit_in_account_currency": line["Amount"],
-						"cost_center": self.default_cost_center,
-					})
+					accounts.append(
+						{
+							"party_type": "Supplier",
+							"party": party,
+							"reference_type": reference_type,
+							"reference_name": reference_name,
+							"account": party_account,
+							"debit_in_account_currency": line["Amount"],
+							"cost_center": self.default_cost_center,
+						}
+					)
 
 			if bill_payment["PayType"] == "Check":
 				bank_account_id = bill_payment["CheckPayment"]["BankAccountRef"]["value"]
@@ -1015,49 +1096,68 @@
 				bank_account_id = bill_payment["CreditCardPayment"]["CCAccountRef"]["value"]
 
 			bank_account = self._get_account_name_by_id(bank_account_id)
-			accounts.append({
-				"account": bank_account,
-				"credit_in_account_currency": bill_payment["TotalAmt"],
-				"cost_center": self.default_cost_center,
-			})
+			accounts.append(
+				{
+					"account": bank_account,
+					"credit_in_account_currency": bill_payment["TotalAmt"],
+					"cost_center": self.default_cost_center,
+				}
+			)
 			posting_date = bill_payment["TxnDate"]
 			self.__save_journal_entry(quickbooks_id, accounts, posting_date)
 		except Exception as e:
 			self._log_error(e, [bill_payment, accounts])
 
-
 	def _save_purchase(self, purchase):
 		try:
 			quickbooks_id = "Purchase - {}".format(purchase["Id"])
 			# Credit Bank Account
-			accounts = [{
-				"account": self._get_account_name_by_id(purchase["AccountRef"]["value"]),
-				"credit_in_account_currency": purchase["TotalAmt"],
-				"cost_center": self.default_cost_center,
-			}]
+			accounts = [
+				{
+					"account": self._get_account_name_by_id(purchase["AccountRef"]["value"]),
+					"credit_in_account_currency": purchase["TotalAmt"],
+					"cost_center": self.default_cost_center,
+				}
+			]
 
 			# Debit Mentioned Accounts
 			for line in purchase["Line"]:
 				if line["DetailType"] == "AccountBasedExpenseLineDetail":
-					account = self._get_account_name_by_id(line["AccountBasedExpenseLineDetail"]["AccountRef"]["value"])
+					account = self._get_account_name_by_id(
+						line["AccountBasedExpenseLineDetail"]["AccountRef"]["value"]
+					)
 				elif line["DetailType"] == "ItemBasedExpenseLineDetail":
-					account = frappe.get_doc("Item",
-						{"quickbooks_id": line["ItemBasedExpenseLineDetail"]["ItemRef"]["value"], "company": self.company}
-					).item_defaults[0].expense_account
-				accounts.append({
-					"account": account,
-					"debit_in_account_currency": line["Amount"],
-					"cost_center": self.default_cost_center,
-				})
+					account = (
+						frappe.get_doc(
+							"Item",
+							{
+								"quickbooks_id": line["ItemBasedExpenseLineDetail"]["ItemRef"]["value"],
+								"company": self.company,
+							},
+						)
+						.item_defaults[0]
+						.expense_account
+					)
+				accounts.append(
+					{
+						"account": account,
+						"debit_in_account_currency": line["Amount"],
+						"cost_center": self.default_cost_center,
+					}
+				)
 
 			# Debit Tax Accounts
 			if "TxnTaxDetail" in purchase:
 				for line in purchase["TxnTaxDetail"]["TaxLine"]:
-					accounts.append({
-						"account": self._get_account_name_by_id("TaxRate - {}".format(line["TaxLineDetail"]["TaxRateRef"]["value"])),
-						"debit_in_account_currency": line["Amount"],
-						"cost_center": self.default_cost_center,
-					})
+					accounts.append(
+						{
+							"account": self._get_account_name_by_id(
+								"TaxRate - {}".format(line["TaxLineDetail"]["TaxRateRef"]["value"])
+							),
+							"debit_in_account_currency": line["Amount"],
+							"cost_center": self.default_cost_center,
+						}
+					)
 
 			# If purchase["Credit"] is set to be True then it represents a refund
 			if purchase.get("Credit"):
@@ -1074,61 +1174,64 @@
 		except Exception as e:
 			self._log_error(e, [purchase, accounts])
 
-
 	def _save_deposit(self, deposit):
 		try:
 			quickbooks_id = "Deposit - {}".format(deposit["Id"])
 			# Debit Bank Account
-			accounts = [{
-				"account": self._get_account_name_by_id(deposit["DepositToAccountRef"]["value"]),
-				"debit_in_account_currency": deposit["TotalAmt"],
-				"cost_center": self.default_cost_center,
-			}]
+			accounts = [
+				{
+					"account": self._get_account_name_by_id(deposit["DepositToAccountRef"]["value"]),
+					"debit_in_account_currency": deposit["TotalAmt"],
+					"cost_center": self.default_cost_center,
+				}
+			]
 
 			# Credit Mentioned Accounts
 			for line in deposit["Line"]:
 				if "LinkedTxn" in line:
-					accounts.append({
-						"account": self.undeposited_funds_account,
-						"credit_in_account_currency": line["Amount"],
-						"cost_center": self.default_cost_center,
-					})
+					accounts.append(
+						{
+							"account": self.undeposited_funds_account,
+							"credit_in_account_currency": line["Amount"],
+							"cost_center": self.default_cost_center,
+						}
+					)
 				else:
-					accounts.append({
-						"account": self._get_account_name_by_id(line["DepositLineDetail"]["AccountRef"]["value"]),
-						"credit_in_account_currency": line["Amount"],
-						"cost_center": self.default_cost_center,
-					})
+					accounts.append(
+						{
+							"account": self._get_account_name_by_id(line["DepositLineDetail"]["AccountRef"]["value"]),
+							"credit_in_account_currency": line["Amount"],
+							"cost_center": self.default_cost_center,
+						}
+					)
 
 			# Debit Cashback if mentioned
 			if "CashBack" in deposit:
-				accounts.append({
-					"account": self._get_account_name_by_id(deposit["CashBack"]["AccountRef"]["value"]),
-					"debit_in_account_currency": deposit["CashBack"]["Amount"],
-					"cost_center": self.default_cost_center,
-				})
+				accounts.append(
+					{
+						"account": self._get_account_name_by_id(deposit["CashBack"]["AccountRef"]["value"]),
+						"debit_in_account_currency": deposit["CashBack"]["Amount"],
+						"cost_center": self.default_cost_center,
+					}
+				)
 
 			posting_date = deposit["TxnDate"]
 			self.__save_journal_entry(quickbooks_id, accounts, posting_date)
 		except Exception as e:
 			self._log_error(e, [deposit, accounts])
 
-
 	def _save_advance_payment(self, advance_payment):
 		quickbooks_id = "Advance Payment - {}".format(advance_payment["id"])
 		self.__save_ledger_entry_as_je(advance_payment, quickbooks_id)
 
-
 	def _save_tax_payment(self, tax_payment):
 		quickbooks_id = "Tax Payment - {}".format(tax_payment["id"])
 		self.__save_ledger_entry_as_je(tax_payment, quickbooks_id)
 
-
 	def _save_inventory_qty_adjust(self, inventory_qty_adjust):
 		quickbooks_id = "Inventory Qty Adjust - {}".format(inventory_qty_adjust["id"])
 		self.__save_ledger_entry_as_je(inventory_qty_adjust, quickbooks_id)
 
-
 	def __save_ledger_entry_as_je(self, ledger_entry, quickbooks_id):
 		try:
 			accounts = []
@@ -1145,7 +1248,6 @@
 		except Exception as e:
 			self._log_error(e, ledger_entry)
 
-
 	def _get_taxes(self, entry):
 		taxes = []
 		if "TxnTaxDetail" not in entry or "TaxLine" not in entry["TxnTaxDetail"]:
@@ -1155,27 +1257,30 @@
 			account_head = self._get_account_name_by_id("TaxRate - {}".format(tax_rate))
 			tax_type_applicable = self._get_tax_type(tax_rate)
 			if tax_type_applicable == "TaxOnAmount":
-				taxes.append({
-					"charge_type": "On Net Total",
-					"account_head": account_head,
-					"description": account_head,
-					"cost_center": self.default_cost_center,
-					"rate": 0,
-				})
+				taxes.append(
+					{
+						"charge_type": "On Net Total",
+						"account_head": account_head,
+						"description": account_head,
+						"cost_center": self.default_cost_center,
+						"rate": 0,
+					}
+				)
 			else:
 				parent_tax_rate = self._get_parent_tax_rate(tax_rate)
 				parent_row_id = self._get_parent_row_id(parent_tax_rate, taxes)
-				taxes.append({
-					"charge_type": "On Previous Row Amount",
-					"row_id": parent_row_id,
-					"account_head": account_head,
-					"description": account_head,
-					"cost_center": self.default_cost_center,
-					"rate": line["TaxLineDetail"]["TaxPercent"],
-				})
+				taxes.append(
+					{
+						"charge_type": "On Previous Row Amount",
+						"row_id": parent_row_id,
+						"account_head": account_head,
+						"description": account_head,
+						"cost_center": self.default_cost_center,
+						"rate": line["TaxLineDetail"]["TaxPercent"],
+					}
+				)
 		return taxes
 
-
 	def _get_tax_type(self, tax_rate):
 		for tax_code in self.tax_codes.values():
 			for rate_list_type in ("SalesTaxRateList", "PurchaseTaxRateList"):
@@ -1184,7 +1289,6 @@
 						if tax_rate_detail["TaxRateRef"]["value"] == tax_rate:
 							return tax_rate_detail["TaxTypeApplicable"]
 
-
 	def _get_parent_tax_rate(self, tax_rate):
 		parent = None
 		for tax_code in self.tax_codes.values():
@@ -1198,34 +1302,33 @@
 							if tax_rate_detail["TaxOrder"] == parent:
 								return tax_rate_detail["TaxRateRef"]["value"]
 
-
 	def _get_parent_row_id(self, tax_rate, taxes):
 		tax_account = self._get_account_name_by_id("TaxRate - {}".format(tax_rate))
 		for index, tax in enumerate(taxes):
 			if tax["account_head"] == tax_account:
 				return index + 1
 
-
 	def _create_address(self, entity, doctype, address, address_type):
 		try:
 			if not frappe.db.exists({"doctype": "Address", "quickbooks_id": address["Id"]}):
-				frappe.get_doc({
-					"doctype": "Address",
-					"quickbooks_address_id": address["Id"],
-					"address_title": entity.name,
-					"address_type": address_type,
-					"address_line1": address["Line1"],
-					"city": address["City"],
-					"links": [{"link_doctype": doctype, "link_name": entity.name}]
-				}).insert()
+				frappe.get_doc(
+					{
+						"doctype": "Address",
+						"quickbooks_address_id": address["Id"],
+						"address_title": entity.name,
+						"address_type": address_type,
+						"address_line1": address["Line1"],
+						"city": address["City"],
+						"links": [{"link_doctype": doctype, "link_name": entity.name}],
+					}
+				).insert()
 		except Exception as e:
 			self._log_error(e, address)
 
-
 	def _get(self, *args, **kwargs):
 		kwargs["headers"] = {
 			"Accept": "application/json",
-			"Authorization": "Bearer {}".format(self.access_token)
+			"Authorization": "Bearer {}".format(self.access_token),
 		}
 		response = requests.get(*args, **kwargs)
 		# HTTP Status code 401 here means that the access_token is expired
@@ -1236,43 +1339,41 @@
 			response = self._get(*args, **kwargs)
 		return response
 
-
 	def _get_account_name_by_id(self, quickbooks_id):
-		return frappe.get_all("Account", filters={"quickbooks_id": quickbooks_id, "company": self.company})[0]["name"]
-
+		return frappe.get_all(
+			"Account", filters={"quickbooks_id": quickbooks_id, "company": self.company}
+		)[0]["name"]
 
 	def _publish(self, *args, **kwargs):
 		frappe.publish_realtime("quickbooks_progress_update", *args, **kwargs)
 
-
 	def _get_unique_account_name(self, quickbooks_name, number=0):
 		if number:
 			quickbooks_account_name = "{} - {} - QB".format(quickbooks_name, number)
 		else:
 			quickbooks_account_name = "{} - QB".format(quickbooks_name)
 		company_encoded_account_name = encode_company_abbr(quickbooks_account_name, self.company)
-		if frappe.db.exists({"doctype": "Account", "name": company_encoded_account_name, "company": self.company}):
+		if frappe.db.exists(
+			{"doctype": "Account", "name": company_encoded_account_name, "company": self.company}
+		):
 			unique_account_name = self._get_unique_account_name(quickbooks_name, number + 1)
 		else:
 			unique_account_name = quickbooks_account_name
 		return unique_account_name
 
-
 	def _log_error(self, execption, data=""):
-		frappe.log_error(title="QuickBooks Migration Error",
-			message="\n".join([
-				"Data",
-				json.dumps(data,
-					sort_keys=True,
-					indent=4,
-					separators=(',', ': ')
-				),
-				"Exception",
-				traceback.format_exc()
-			])
+		frappe.log_error(
+			title="QuickBooks Migration Error",
+			message="\n".join(
+				[
+					"Data",
+					json.dumps(data, sort_keys=True, indent=4, separators=(",", ": ")),
+					"Exception",
+					traceback.format_exc(),
+				]
+			),
 		)
 
-
 	def set_indicator(self, status):
 		self.status = status
 		self.save()
diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
index 26bd19f..7d676e4 100644
--- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
+++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
@@ -36,6 +36,7 @@
 
 	return doc
 
+
 class TallyMigration(Document):
 	def validate(self):
 		failed_import_log = json.loads(self.failed_import_log)
@@ -73,14 +74,16 @@
 
 	def dump_processed_data(self, data):
 		for key, value in data.items():
-			f = frappe.get_doc({
-				"doctype": "File",
-				"file_name":  key + ".json",
-				"attached_to_doctype": self.doctype,
-				"attached_to_name": self.name,
-				"content": json.dumps(value),
-				"is_private": True
-			})
+			f = frappe.get_doc(
+				{
+					"doctype": "File",
+					"file_name": key + ".json",
+					"attached_to_doctype": self.doctype,
+					"attached_to_name": self.name,
+					"content": json.dumps(value),
+					"is_private": True,
+				}
+			)
 			try:
 				f.insert(ignore_if_duplicate=True)
 			except frappe.DuplicateEntryError:
@@ -88,8 +91,12 @@
 			setattr(self, key, f.file_url)
 
 	def set_account_defaults(self):
-		self.default_cost_center, self.default_round_off_account = frappe.db.get_value("Company", self.erpnext_company, ["cost_center", "round_off_account"])
-		self.default_warehouse = frappe.db.get_value("Stock Settings", "Stock Settings", "default_warehouse")
+		self.default_cost_center, self.default_round_off_account = frappe.db.get_value(
+			"Company", self.erpnext_company, ["cost_center", "round_off_account"]
+		)
+		self.default_warehouse = frappe.db.get_value(
+			"Stock Settings", "Stock Settings", "default_warehouse"
+		)
 
 	def _process_master_data(self):
 		def get_company_name(collection):
@@ -100,18 +107,24 @@
 				"Application of Funds (Assets)": "Asset",
 				"Expenses": "Expense",
 				"Income": "Income",
-				"Source of Funds (Liabilities)": "Liability"
+				"Source of Funds (Liabilities)": "Liability",
 			}
 			roots = set(root_type_map.keys())
-			accounts = list(get_groups(collection.find_all("GROUP"))) + list(get_ledgers(collection.find_all("LEDGER")))
+			accounts = list(get_groups(collection.find_all("GROUP"))) + list(
+				get_ledgers(collection.find_all("LEDGER"))
+			)
 			children, parents = get_children_and_parent_dict(accounts)
-			group_set =  [acc[1] for acc in accounts if acc[2]]
+			group_set = [acc[1] for acc in accounts if acc[2]]
 			children, customers, suppliers = remove_parties(parents, children, group_set)
 
 			try:
 				coa = traverse({}, children, roots, roots, group_set)
 			except RecursionError:
-				self.log(_("Error occured while parsing Chart of Accounts: Please make sure that no two accounts have the same name"))
+				self.log(
+					_(
+						"Error occured while parsing Chart of Accounts: Please make sure that no two accounts have the same name"
+					)
+				)
 
 			for account in coa:
 				coa[account]["root_type"] = root_type_map[account]
@@ -185,42 +198,48 @@
 				links = []
 				if account.NAME.string.strip() in customers:
 					party_type = "Customer"
-					parties.append({
-						"doctype": party_type,
-						"customer_name": account.NAME.string.strip(),
-						"tax_id": account.INCOMETAXNUMBER.string.strip() if account.INCOMETAXNUMBER else None,
-						"customer_group": "All Customer Groups",
-						"territory": "All Territories",
-						"customer_type": "Individual",
-					})
+					parties.append(
+						{
+							"doctype": party_type,
+							"customer_name": account.NAME.string.strip(),
+							"tax_id": account.INCOMETAXNUMBER.string.strip() if account.INCOMETAXNUMBER else None,
+							"customer_group": "All Customer Groups",
+							"territory": "All Territories",
+							"customer_type": "Individual",
+						}
+					)
 					links.append({"link_doctype": party_type, "link_name": account["NAME"]})
 
 				if account.NAME.string.strip() in suppliers:
 					party_type = "Supplier"
-					parties.append({
-						"doctype": party_type,
-						"supplier_name": account.NAME.string.strip(),
-						"pan": account.INCOMETAXNUMBER.string.strip() if account.INCOMETAXNUMBER else None,
-						"supplier_group": "All Supplier Groups",
-						"supplier_type": "Individual",
-					})
+					parties.append(
+						{
+							"doctype": party_type,
+							"supplier_name": account.NAME.string.strip(),
+							"pan": account.INCOMETAXNUMBER.string.strip() if account.INCOMETAXNUMBER else None,
+							"supplier_group": "All Supplier Groups",
+							"supplier_type": "Individual",
+						}
+					)
 					links.append({"link_doctype": party_type, "link_name": account["NAME"]})
 
 				if party_type:
 					address = "\n".join([a.string.strip() for a in account.find_all("ADDRESS")])
-					addresses.append({
-						"doctype": "Address",
-						"address_line1": address[:140].strip(),
-						"address_line2": address[140:].strip(),
-						"country": account.COUNTRYNAME.string.strip() if account.COUNTRYNAME else None,
-						"state": account.LEDSTATENAME.string.strip() if account.LEDSTATENAME else None,
-						"gst_state": account.LEDSTATENAME.string.strip() if account.LEDSTATENAME else None,
-						"pin_code": account.PINCODE.string.strip() if account.PINCODE else None,
-						"mobile": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None,
-						"phone": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None,
-						"gstin": account.PARTYGSTIN.string.strip() if account.PARTYGSTIN else None,
-						"links": links
-					})
+					addresses.append(
+						{
+							"doctype": "Address",
+							"address_line1": address[:140].strip(),
+							"address_line2": address[140:].strip(),
+							"country": account.COUNTRYNAME.string.strip() if account.COUNTRYNAME else None,
+							"state": account.LEDSTATENAME.string.strip() if account.LEDSTATENAME else None,
+							"gst_state": account.LEDSTATENAME.string.strip() if account.LEDSTATENAME else None,
+							"pin_code": account.PINCODE.string.strip() if account.PINCODE else None,
+							"mobile": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None,
+							"phone": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None,
+							"gstin": account.PARTYGSTIN.string.strip() if account.PARTYGSTIN else None,
+							"links": links,
+						}
+					)
 			return parties, addresses
 
 		def get_stock_items_uoms(collection):
@@ -231,14 +250,16 @@
 			items = []
 			for item in collection.find_all("STOCKITEM"):
 				stock_uom = item.BASEUNITS.string.strip() if item.BASEUNITS else self.default_uom
-				items.append({
-					"doctype": "Item",
-					"item_code" : item.NAME.string.strip(),
-					"stock_uom": stock_uom.strip(),
-					"is_stock_item": 0,
-					"item_group": "All Item Groups",
-					"item_defaults": [{"company": self.erpnext_company}]
-				})
+				items.append(
+					{
+						"doctype": "Item",
+						"item_code": item.NAME.string.strip(),
+						"stock_uom": stock_uom.strip(),
+						"is_stock_item": 0,
+						"item_group": "All Item Groups",
+						"item_defaults": [{"company": self.erpnext_company}],
+					}
+				)
 
 			return items, uoms
 
@@ -257,7 +278,13 @@
 
 			self.publish("Process Master Data", _("Processing Items and UOMs"), 4, 5)
 			items, uoms = get_stock_items_uoms(collection)
-			data = {"chart_of_accounts": chart_of_accounts, "parties": parties, "addresses": addresses, "items": items, "uoms": uoms}
+			data = {
+				"chart_of_accounts": chart_of_accounts,
+				"parties": parties,
+				"addresses": addresses,
+				"items": items,
+				"uoms": uoms,
+			}
 
 			self.publish("Process Master Data", _("Done"), 5, 5)
 			self.dump_processed_data(data)
@@ -272,7 +299,10 @@
 			self.set_status()
 
 	def publish(self, title, message, count, total):
-		frappe.publish_realtime("tally_migration_progress_update", {"title": title, "message": message, "count": count, "total": total})
+		frappe.publish_realtime(
+			"tally_migration_progress_update",
+			{"title": title, "message": message, "count": count, "total": total},
+		)
 
 	def _import_master_data(self):
 		def create_company_and_coa(coa_file_url):
@@ -280,12 +310,14 @@
 			frappe.local.flags.ignore_chart_of_accounts = True
 
 			try:
-				company = frappe.get_doc({
-					"doctype": "Company",
-					"company_name": self.erpnext_company,
-					"default_currency": "INR",
-					"enable_perpetual_inventory": 0,
-				}).insert()
+				company = frappe.get_doc(
+					{
+						"doctype": "Company",
+						"company_name": self.erpnext_company,
+						"default_currency": "INR",
+						"enable_perpetual_inventory": 0,
+					}
+				).insert()
 			except frappe.DuplicateEntryError:
 				company = frappe.get_doc("Company", self.erpnext_company)
 				unset_existing_data(self.erpnext_company)
@@ -358,8 +390,16 @@
 			for voucher in collection.find_all("VOUCHER"):
 				if voucher.ISCANCELLED.string.strip() == "Yes":
 					continue
-				inventory_entries = voucher.find_all("INVENTORYENTRIES.LIST") + voucher.find_all("ALLINVENTORYENTRIES.LIST") + voucher.find_all("INVENTORYENTRIESIN.LIST") + voucher.find_all("INVENTORYENTRIESOUT.LIST")
-				if voucher.VOUCHERTYPENAME.string.strip() not in ["Journal", "Receipt", "Payment", "Contra"] and inventory_entries:
+				inventory_entries = (
+					voucher.find_all("INVENTORYENTRIES.LIST")
+					+ voucher.find_all("ALLINVENTORYENTRIES.LIST")
+					+ voucher.find_all("INVENTORYENTRIESIN.LIST")
+					+ voucher.find_all("INVENTORYENTRIESOUT.LIST")
+				)
+				if (
+					voucher.VOUCHERTYPENAME.string.strip() not in ["Journal", "Receipt", "Payment", "Contra"]
+					and inventory_entries
+				):
 					function = voucher_to_invoice
 				else:
 					function = voucher_to_journal_entry
@@ -375,9 +415,14 @@
 
 		def voucher_to_journal_entry(voucher):
 			accounts = []
-			ledger_entries = voucher.find_all("ALLLEDGERENTRIES.LIST") + voucher.find_all("LEDGERENTRIES.LIST")
+			ledger_entries = voucher.find_all("ALLLEDGERENTRIES.LIST") + voucher.find_all(
+				"LEDGERENTRIES.LIST"
+			)
 			for entry in ledger_entries:
-				account = {"account": encode_company_abbr(entry.LEDGERNAME.string.strip(), self.erpnext_company), "cost_center": self.default_cost_center}
+				account = {
+					"account": encode_company_abbr(entry.LEDGERNAME.string.strip(), self.erpnext_company),
+					"cost_center": self.default_cost_center,
+				}
 				if entry.ISPARTYLEDGER.string.strip() == "Yes":
 					party_details = get_party(entry.LEDGERNAME.string.strip())
 					if party_details:
@@ -438,7 +483,12 @@
 			return invoice
 
 		def get_voucher_items(voucher, doctype):
-			inventory_entries = voucher.find_all("INVENTORYENTRIES.LIST") + voucher.find_all("ALLINVENTORYENTRIES.LIST") + voucher.find_all("INVENTORYENTRIESIN.LIST") + voucher.find_all("INVENTORYENTRIESOUT.LIST")
+			inventory_entries = (
+				voucher.find_all("INVENTORYENTRIES.LIST")
+				+ voucher.find_all("ALLINVENTORYENTRIES.LIST")
+				+ voucher.find_all("INVENTORYENTRIESIN.LIST")
+				+ voucher.find_all("INVENTORYENTRIESOUT.LIST")
+			)
 			if doctype == "Sales Invoice":
 				account_field = "income_account"
 			elif doctype == "Purchase Invoice":
@@ -446,32 +496,41 @@
 			items = []
 			for entry in inventory_entries:
 				qty, uom = entry.ACTUALQTY.string.strip().split()
-				items.append({
-					"item_code": entry.STOCKITEMNAME.string.strip(),
-					"description": entry.STOCKITEMNAME.string.strip(),
-					"qty": qty.strip(),
-					"uom": uom.strip(),
-					"conversion_factor": 1,
-					"price_list_rate": entry.RATE.string.strip().split("/")[0],
-					"cost_center": self.default_cost_center,
-					"warehouse": self.default_warehouse,
-					account_field: encode_company_abbr(entry.find_all("ACCOUNTINGALLOCATIONS.LIST")[0].LEDGERNAME.string.strip(), self.erpnext_company),
-				})
+				items.append(
+					{
+						"item_code": entry.STOCKITEMNAME.string.strip(),
+						"description": entry.STOCKITEMNAME.string.strip(),
+						"qty": qty.strip(),
+						"uom": uom.strip(),
+						"conversion_factor": 1,
+						"price_list_rate": entry.RATE.string.strip().split("/")[0],
+						"cost_center": self.default_cost_center,
+						"warehouse": self.default_warehouse,
+						account_field: encode_company_abbr(
+							entry.find_all("ACCOUNTINGALLOCATIONS.LIST")[0].LEDGERNAME.string.strip(),
+							self.erpnext_company,
+						),
+					}
+				)
 			return items
 
 		def get_voucher_taxes(voucher):
-			ledger_entries = voucher.find_all("ALLLEDGERENTRIES.LIST") + voucher.find_all("LEDGERENTRIES.LIST")
+			ledger_entries = voucher.find_all("ALLLEDGERENTRIES.LIST") + voucher.find_all(
+				"LEDGERENTRIES.LIST"
+			)
 			taxes = []
 			for entry in ledger_entries:
 				if entry.ISPARTYLEDGER.string.strip() == "No":
 					tax_account = encode_company_abbr(entry.LEDGERNAME.string.strip(), self.erpnext_company)
-					taxes.append({
-						"charge_type": "Actual",
-						"account_head": tax_account,
-						"description": tax_account,
-						"tax_amount": entry.AMOUNT.string.strip(),
-						"cost_center": self.default_cost_center,
-					})
+					taxes.append(
+						{
+							"charge_type": "Actual",
+							"account_head": tax_account,
+							"description": tax_account,
+							"tax_amount": entry.AMOUNT.string.strip(),
+							"cost_center": self.default_cost_center,
+						}
+					)
 			return taxes
 
 		def get_party(party):
@@ -502,8 +561,11 @@
 	def _import_day_book_data(self):
 		def create_fiscal_years(vouchers):
 			from frappe.utils.data import add_years, getdate
+
 			earliest_date = getdate(min(voucher["posting_date"] for voucher in vouchers))
-			oldest_year = frappe.get_all("Fiscal Year", fields=["year_start_date", "year_end_date"], order_by="year_start_date")[0]
+			oldest_year = frappe.get_all(
+				"Fiscal Year", fields=["year_start_date", "year_end_date"], order_by="year_start_date"
+			)[0]
 			while earliest_date < oldest_year.year_start_date:
 				new_year = frappe.get_doc({"doctype": "Fiscal Year"})
 				new_year.year_start_date = add_years(oldest_year.year_start_date, -1)
@@ -520,32 +582,46 @@
 				"fieldtype": "Data",
 				"fieldname": "tally_guid",
 				"read_only": 1,
-				"label": "Tally GUID"
+				"label": "Tally GUID",
 			}
 			tally_voucher_no_df = {
 				"fieldtype": "Data",
 				"fieldname": "tally_voucher_no",
 				"read_only": 1,
-				"label": "Tally Voucher Number"
+				"label": "Tally Voucher Number",
 			}
 			for df in [tally_guid_df, tally_voucher_no_df]:
 				for doctype in doctypes:
 					create_custom_field(doctype, df)
 
 		def create_price_list():
-			frappe.get_doc({
-				"doctype": "Price List",
-				"price_list_name": "Tally Price List",
-				"selling": 1,
-				"buying": 1,
-				"enabled": 1,
-				"currency": "INR"
-			}).insert()
+			frappe.get_doc(
+				{
+					"doctype": "Price List",
+					"price_list_name": "Tally Price List",
+					"selling": 1,
+					"buying": 1,
+					"enabled": 1,
+					"currency": "INR",
+				}
+			).insert()
 
 		try:
-			frappe.db.set_value("Account", encode_company_abbr(self.tally_creditors_account, self.erpnext_company), "account_type", "Payable")
-			frappe.db.set_value("Account", encode_company_abbr(self.tally_debtors_account, self.erpnext_company), "account_type", "Receivable")
-			frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.default_round_off_account)
+			frappe.db.set_value(
+				"Account",
+				encode_company_abbr(self.tally_creditors_account, self.erpnext_company),
+				"account_type",
+				"Payable",
+			)
+			frappe.db.set_value(
+				"Account",
+				encode_company_abbr(self.tally_debtors_account, self.erpnext_company),
+				"account_type",
+				"Receivable",
+			)
+			frappe.db.set_value(
+				"Company", self.erpnext_company, "round_off_account", self.default_round_off_account
+			)
 
 			vouchers_file = frappe.get_doc("File", {"file_url": self.vouchers})
 			vouchers = json.loads(vouchers_file.get_content())
@@ -560,7 +636,16 @@
 			for index in range(0, total, VOUCHER_CHUNK_SIZE):
 				if index + VOUCHER_CHUNK_SIZE >= total:
 					is_last = True
-				frappe.enqueue_doc(self.doctype, self.name, "_import_vouchers", queue="long", timeout=3600, start=index+1, total=total, is_last=is_last)
+				frappe.enqueue_doc(
+					self.doctype,
+					self.name,
+					"_import_vouchers",
+					queue="long",
+					timeout=3600,
+					start=index + 1,
+					total=total,
+					is_last=is_last,
+				)
 
 		except Exception:
 			self.log()
@@ -572,7 +657,7 @@
 		frappe.flags.in_migrate = True
 		vouchers_file = frappe.get_doc("File", {"file_url": self.vouchers})
 		vouchers = json.loads(vouchers_file.get_content())
-		chunk = vouchers[start: start + VOUCHER_CHUNK_SIZE]
+		chunk = vouchers[start : start + VOUCHER_CHUNK_SIZE]
 
 		for index, voucher in enumerate(chunk, start=start):
 			try:
@@ -617,17 +702,22 @@
 			if sys.exc_info()[1].__class__ != frappe.DuplicateEntryError:
 				failed_import_log = json.loads(self.failed_import_log)
 				doc = data.as_dict()
-				failed_import_log.append({
-					"doc": doc,
-					"exc": traceback.format_exc()
-				})
-				self.failed_import_log = json.dumps(failed_import_log, separators=(',', ':'))
+				failed_import_log.append({"doc": doc, "exc": traceback.format_exc()})
+				self.failed_import_log = json.dumps(failed_import_log, separators=(",", ":"))
 				self.save()
 				frappe.db.commit()
 
 		else:
 			data = data or self.status
-			message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()])
+			message = "\n".join(
+				[
+					"Data:",
+					json.dumps(data, default=str, indent=4),
+					"--" * 50,
+					"\nException:",
+					traceback.format_exc(),
+				]
+			)
 			return frappe.log_error(title="Tally Migration Error", message=message)
 
 	def set_status(self, status=""):
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py
index d4bbe88..2148863 100644
--- a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py
+++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py
@@ -14,27 +14,31 @@
 
 
 class TaxJarSettings(Document):
-
 	def on_update(self):
 		TAXJAR_CREATE_TRANSACTIONS = self.taxjar_create_transactions
 		TAXJAR_CALCULATE_TAX = self.taxjar_calculate_tax
 		TAXJAR_SANDBOX_MODE = self.is_sandbox
 
-		fields_already_exist = frappe.db.exists('Custom Field', {'dt': ('in', ['Item','Sales Invoice Item']), 'fieldname':'product_tax_category'})
-		fields_hidden = frappe.get_value('Custom Field', {'dt': ('in', ['Sales Invoice Item'])}, 'hidden')
+		fields_already_exist = frappe.db.exists(
+			"Custom Field",
+			{"dt": ("in", ["Item", "Sales Invoice Item"]), "fieldname": "product_tax_category"},
+		)
+		fields_hidden = frappe.get_value(
+			"Custom Field", {"dt": ("in", ["Sales Invoice Item"])}, "hidden"
+		)
 
-		if (TAXJAR_CREATE_TRANSACTIONS or TAXJAR_CALCULATE_TAX or TAXJAR_SANDBOX_MODE):
+		if TAXJAR_CREATE_TRANSACTIONS or TAXJAR_CALCULATE_TAX or TAXJAR_SANDBOX_MODE:
 			if not fields_already_exist:
 				add_product_tax_categories()
 				make_custom_fields()
 				add_permissions()
-				frappe.enqueue('erpnext.regional.united_states.setup.add_product_tax_categories', now=False)
+				frappe.enqueue("erpnext.regional.united_states.setup.add_product_tax_categories", now=False)
 
 			elif fields_already_exist and fields_hidden:
-				toggle_tax_category_fields(hidden='0')
+				toggle_tax_category_fields(hidden="0")
 
 		elif fields_already_exist:
-			toggle_tax_category_fields(hidden='1')
+			toggle_tax_category_fields(hidden="1")
 
 	def validate(self):
 		self.calculate_taxes_validation_for_create_transactions()
@@ -46,54 +50,97 @@
 
 		new_nexus_list = [frappe._dict(address) for address in nexus]
 
-		self.set('nexus', [])
-		self.set('nexus', new_nexus_list)
+		self.set("nexus", [])
+		self.set("nexus", new_nexus_list)
 		self.save()
 
 	def calculate_taxes_validation_for_create_transactions(self):
 		if not self.taxjar_calculate_tax and (self.taxjar_create_transactions or self.is_sandbox):
-			frappe.throw(frappe._('Before enabling <b>Create Transaction</b> or <b>Sandbox Mode</b>, you need to check the <b>Enable Tax Calculation</b> box'))
+			frappe.throw(
+				frappe._(
+					"Before enabling <b>Create Transaction</b> or <b>Sandbox Mode</b>, you need to check the <b>Enable Tax Calculation</b> box"
+				)
+			)
 
 
 def toggle_tax_category_fields(hidden):
-	frappe.set_value('Custom Field', {'dt':'Sales Invoice Item', 'fieldname':'product_tax_category'}, 'hidden', hidden)
-	frappe.set_value('Custom Field', {'dt':'Item', 'fieldname':'product_tax_category'}, 'hidden', hidden)
+	frappe.set_value(
+		"Custom Field",
+		{"dt": "Sales Invoice Item", "fieldname": "product_tax_category"},
+		"hidden",
+		hidden,
+	)
+	frappe.set_value(
+		"Custom Field", {"dt": "Item", "fieldname": "product_tax_category"}, "hidden", hidden
+	)
 
 
 def add_product_tax_categories():
-	with open(os.path.join(os.path.dirname(__file__), 'product_tax_category_data.json'), 'r') as f:
+	with open(os.path.join(os.path.dirname(__file__), "product_tax_category_data.json"), "r") as f:
 		tax_categories = json.loads(f.read())
-	create_tax_categories(tax_categories['categories'])
+	create_tax_categories(tax_categories["categories"])
+
 
 def create_tax_categories(data):
 	for d in data:
-		if not frappe.db.exists('Product Tax Category',{'product_tax_code':d.get('product_tax_code')}):
-			tax_category = frappe.new_doc('Product Tax Category')
+		if not frappe.db.exists("Product Tax Category", {"product_tax_code": d.get("product_tax_code")}):
+			tax_category = frappe.new_doc("Product Tax Category")
 			tax_category.description = d.get("description")
 			tax_category.product_tax_code = d.get("product_tax_code")
 			tax_category.category_name = d.get("name")
 			tax_category.db_insert()
 
+
 def make_custom_fields(update=True):
 	custom_fields = {
-		'Sales Invoice Item': [
-			dict(fieldname='product_tax_category', fieldtype='Link', insert_after='description', options='Product Tax Category',
-				label='Product Tax Category', fetch_from='item_code.product_tax_category'),
-			dict(fieldname='tax_collectable', fieldtype='Currency', insert_after='net_amount',
-				label='Tax Collectable', read_only=1, options='currency'),
-			dict(fieldname='taxable_amount', fieldtype='Currency', insert_after='tax_collectable',
-				label='Taxable Amount', read_only=1, options='currency')
+		"Sales Invoice Item": [
+			dict(
+				fieldname="product_tax_category",
+				fieldtype="Link",
+				insert_after="description",
+				options="Product Tax Category",
+				label="Product Tax Category",
+				fetch_from="item_code.product_tax_category",
+			),
+			dict(
+				fieldname="tax_collectable",
+				fieldtype="Currency",
+				insert_after="net_amount",
+				label="Tax Collectable",
+				read_only=1,
+				options="currency",
+			),
+			dict(
+				fieldname="taxable_amount",
+				fieldtype="Currency",
+				insert_after="tax_collectable",
+				label="Taxable Amount",
+				read_only=1,
+				options="currency",
+			),
 		],
-		'Item': [
-			dict(fieldname='product_tax_category', fieldtype='Link', insert_after='item_group', options='Product Tax Category',
-				label='Product Tax Category')
-		]
+		"Item": [
+			dict(
+				fieldname="product_tax_category",
+				fieldtype="Link",
+				insert_after="item_group",
+				options="Product Tax Category",
+				label="Product Tax Category",
+			)
+		],
 	}
 	create_custom_fields(custom_fields, update=update)
 
+
 def add_permissions():
 	doctype = "Product Tax Category"
-	for role in ('Accounts Manager', 'Accounts User', 'System Manager','Item Manager', 'Stock Manager'):
+	for role in (
+		"Accounts Manager",
+		"Accounts User",
+		"System Manager",
+		"Item Manager",
+		"Stock Manager",
+	):
 		add_permission(doctype, role, 0)
-		update_permission_property(doctype, role, 0, 'write', 1)
-		update_permission_property(doctype, role, 0, 'create', 1)
+		update_permission_property(doctype, role, 0, "write", 1)
+		update_permission_property(doctype, role, 0, "create", 1)
diff --git a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
index 309d2cb..2e18776 100644
--- a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
+++ b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
@@ -22,11 +22,23 @@
 			custom_fields = {}
 			# create
 			for doctype in ["Customer", "Sales Order", "Item", "Address"]:
-				df = dict(fieldname='woocommerce_id', label='Woocommerce ID', fieldtype='Data', read_only=1, print_hide=1)
+				df = dict(
+					fieldname="woocommerce_id",
+					label="Woocommerce ID",
+					fieldtype="Data",
+					read_only=1,
+					print_hide=1,
+				)
 				create_custom_field(doctype, df)
 
 			for doctype in ["Customer", "Address"]:
-				df = dict(fieldname='woocommerce_email', label='Woocommerce Email', fieldtype='Data', read_only=1, print_hide=1)
+				df = dict(
+					fieldname="woocommerce_email",
+					label="Woocommerce Email",
+					fieldtype="Data",
+					read_only=1,
+					print_hide=1,
+				)
 				create_custom_field(doctype, df)
 
 			if not frappe.get_value("Item Group", {"name": _("WooCommerce Products")}):
@@ -58,21 +70,21 @@
 			# for CI Test to work
 			url = "http://localhost:8000"
 
-		server_url = '{uri.scheme}://{uri.netloc}'.format(
-			uri=urlparse(url)
-		)
+		server_url = "{uri.scheme}://{uri.netloc}".format(uri=urlparse(url))
 
 		delivery_url = server_url + endpoint
 		self.endpoint = delivery_url
 
+
 @frappe.whitelist()
 def generate_secret():
 	woocommerce_settings = frappe.get_doc("Woocommerce Settings")
 	woocommerce_settings.secret = frappe.generate_hash()
 	woocommerce_settings.save()
 
+
 @frappe.whitelist()
 def get_series():
 	return {
-		"sales_order_series" : frappe.get_meta("Sales Order").get_options("naming_series") or "SO-WOO-",
+		"sales_order_series": frappe.get_meta("Sales Order").get_options("naming_series") or "SO-WOO-",
 	}
diff --git a/erpnext/erpnext_integrations/exotel_integration.py b/erpnext/erpnext_integrations/exotel_integration.py
index 167fcb7..fd9f74e 100644
--- a/erpnext/erpnext_integrations/exotel_integration.py
+++ b/erpnext/erpnext_integrations/exotel_integration.py
@@ -1,20 +1,21 @@
 import frappe
 import requests
-from frappe import _
 
 # api/method/erpnext.erpnext_integrations.exotel_integration.handle_incoming_call
 # api/method/erpnext.erpnext_integrations.exotel_integration.handle_end_call
 # api/method/erpnext.erpnext_integrations.exotel_integration.handle_missed_call
 
+
 @frappe.whitelist(allow_guest=True)
 def handle_incoming_call(**kwargs):
 	try:
 		exotel_settings = get_exotel_settings()
-		if not exotel_settings.enabled: return
+		if not exotel_settings.enabled:
+			return
 
 		call_payload = kwargs
-		status = call_payload.get('Status')
-		if status == 'free':
+		status = call_payload.get("Status")
+		if status == "free":
 			return
 
 		call_log = get_call_log(call_payload)
@@ -24,87 +25,107 @@
 			update_call_log(call_payload, call_log=call_log)
 	except Exception as e:
 		frappe.db.rollback()
-		frappe.log_error(title=_('Error in Exotel incoming call'))
-		frappe.db.commit()
+		exotel_settings.log_error("Error in Exotel incoming call")
+
 
 @frappe.whitelist(allow_guest=True)
 def handle_end_call(**kwargs):
-	update_call_log(kwargs, 'Completed')
+	update_call_log(kwargs, "Completed")
+
 
 @frappe.whitelist(allow_guest=True)
 def handle_missed_call(**kwargs):
-	update_call_log(kwargs, 'Missed')
+	status = ""
+	call_type = kwargs.get("CallType")
+	dial_call_status = kwargs.get("DialCallStatus")
 
-def update_call_log(call_payload, status='Ringing', call_log=None):
+	if call_type == "incomplete" and dial_call_status == "no-answer":
+		status = "No Answer"
+	elif call_type == "client-hangup" and dial_call_status == "canceled":
+		status = "Canceled"
+	elif call_type == "incomplete" and dial_call_status == "failed":
+		status = "Failed"
+
+	update_call_log(kwargs, status)
+
+
+def update_call_log(call_payload, status="Ringing", call_log=None):
 	call_log = call_log or get_call_log(call_payload)
+
+	# for a new sid, call_log and get_call_log will be empty so create a new log
+	if not call_log:
+		call_log = create_call_log(call_payload)
 	if call_log:
 		call_log.status = status
-		call_log.to = call_payload.get('DialWhomNumber')
-		call_log.duration = call_payload.get('DialCallDuration') or 0
-		call_log.recording_url = call_payload.get('RecordingUrl')
+		call_log.to = call_payload.get("DialWhomNumber")
+		call_log.duration = call_payload.get("DialCallDuration") or 0
+		call_log.recording_url = call_payload.get("RecordingUrl")
 		call_log.save(ignore_permissions=True)
 		frappe.db.commit()
 		return call_log
 
-def get_call_log(call_payload):
-	call_log = frappe.get_all('Call Log', {
-		'id': call_payload.get('CallSid'),
-	}, limit=1)
 
-	if call_log:
-		return frappe.get_doc('Call Log', call_log[0].name)
+def get_call_log(call_payload):
+	call_log_id = call_payload.get("CallSid")
+	if frappe.db.exists("Call Log", call_log_id):
+		return frappe.get_doc("Call Log", call_log_id)
+
 
 def create_call_log(call_payload):
-	call_log = frappe.new_doc('Call Log')
-	call_log.id = call_payload.get('CallSid')
-	call_log.to = call_payload.get('DialWhomNumber')
-	call_log.medium = call_payload.get('To')
-	call_log.status = 'Ringing'
-	setattr(call_log, 'from', call_payload.get('CallFrom'))
+	call_log = frappe.new_doc("Call Log")
+	call_log.id = call_payload.get("CallSid")
+	call_log.to = call_payload.get("DialWhomNumber")
+	call_log.medium = call_payload.get("To")
+	call_log.status = "Ringing"
+	setattr(call_log, "from", call_payload.get("CallFrom"))
 	call_log.save(ignore_permissions=True)
 	frappe.db.commit()
 	return call_log
 
+
 @frappe.whitelist()
 def get_call_status(call_id):
-	endpoint = get_exotel_endpoint('Calls/{call_id}.json'.format(call_id=call_id))
+	endpoint = get_exotel_endpoint("Calls/{call_id}.json".format(call_id=call_id))
 	response = requests.get(endpoint)
-	status = response.json().get('Call', {}).get('Status')
+	status = response.json().get("Call", {}).get("Status")
 	return status
 
+
 @frappe.whitelist()
 def make_a_call(from_number, to_number, caller_id):
-	endpoint = get_exotel_endpoint('Calls/connect.json?details=true')
-	response = requests.post(endpoint, data={
-		'From': from_number,
-		'To': to_number,
-		'CallerId': caller_id
-	})
+	endpoint = get_exotel_endpoint("Calls/connect.json?details=true")
+	response = requests.post(
+		endpoint, data={"From": from_number, "To": to_number, "CallerId": caller_id}
+	)
 
 	return response.json()
 
+
 def get_exotel_settings():
-	return frappe.get_single('Exotel Settings')
+	return frappe.get_single("Exotel Settings")
+
 
 def whitelist_numbers(numbers, caller_id):
-	endpoint = get_exotel_endpoint('CustomerWhitelist')
-	response = requests.post(endpoint, data={
-		'VirtualNumber': caller_id,
-		'Number': numbers,
-	})
+	endpoint = get_exotel_endpoint("CustomerWhitelist")
+	response = requests.post(
+		endpoint,
+		data={
+			"VirtualNumber": caller_id,
+			"Number": numbers,
+		},
+	)
 
 	return response
 
+
 def get_all_exophones():
-	endpoint = get_exotel_endpoint('IncomingPhoneNumbers')
+	endpoint = get_exotel_endpoint("IncomingPhoneNumbers")
 	response = requests.post(endpoint)
 	return response
 
+
 def get_exotel_endpoint(action):
 	settings = get_exotel_settings()
-	return 'https://{api_key}:{api_token}@api.exotel.com/v1/Accounts/{sid}/{action}'.format(
-		api_key=settings.api_key,
-		api_token=settings.api_token,
-		sid=settings.account_sid,
-		action=action
+	return "https://{api_key}:{api_token}@api.exotel.com/v1/Accounts/{sid}/{action}".format(
+		api_key=settings.api_key, api_token=settings.api_token, sid=settings.account_sid, action=action
 	)
diff --git a/erpnext/erpnext_integrations/stripe_integration.py b/erpnext/erpnext_integrations/stripe_integration.py
index 502cb5f..2d7e8a5 100644
--- a/erpnext/erpnext_integrations/stripe_integration.py
+++ b/erpnext/erpnext_integrations/stripe_integration.py
@@ -16,17 +16,21 @@
 
 	try:
 		stripe_settings.integration_request = create_request_log(stripe_settings.data, "Host", "Stripe")
-		stripe_settings.payment_plans = frappe.get_doc("Payment Request", stripe_settings.data.reference_docname).subscription_plans
+		stripe_settings.payment_plans = frappe.get_doc(
+			"Payment Request", stripe_settings.data.reference_docname
+		).subscription_plans
 		return create_subscription_on_stripe(stripe_settings)
 
 	except Exception:
-		frappe.log_error(frappe.get_traceback())
-		return{
+		stripe_settings.log_error("Unable to create Stripe subscription")
+		return {
 			"redirect_to": frappe.redirect_to_message(
-				_('Server Error'),
-				_("It seems that there is an issue with the server's stripe configuration. In case of failure, the amount will get refunded to your account.")
+				_("Server Error"),
+				_(
+					"It seems that there is an issue with the server's stripe configuration. In case of failure, the amount will get refunded to your account."
+				),
 			),
-			"status": 401
+			"status": 401,
 		}
 
 
@@ -40,20 +44,20 @@
 		customer = stripe.Customer.create(
 			source=stripe_settings.data.stripe_token_id,
 			description=stripe_settings.data.payer_name,
-			email=stripe_settings.data.payer_email
+			email=stripe_settings.data.payer_email,
 		)
 
 		subscription = stripe.Subscription.create(customer=customer, items=items)
 
 		if subscription.status == "active":
-			stripe_settings.integration_request.db_set('status', 'Completed', update_modified=False)
+			stripe_settings.integration_request.db_set("status", "Completed", update_modified=False)
 			stripe_settings.flags.status_changed_to = "Completed"
 
 		else:
-			stripe_settings.integration_request.db_set('status', 'Failed', update_modified=False)
-			frappe.log_error('Subscription N°: ' + subscription.id, 'Stripe Payment not completed')
+			stripe_settings.integration_request.db_set("status", "Failed", update_modified=False)
+			frappe.log_error(f"Stripe Subscription ID {subscription.id}: Payment failed")
 	except Exception:
-		stripe_settings.integration_request.db_set('status', 'Failed', update_modified=False)
-		frappe.log_error(frappe.get_traceback())
+		stripe_settings.integration_request.db_set("status", "Failed", update_modified=False)
+		stripe_settings.log_error("Unable to create Stripe subscription")
 
 	return stripe_settings.finalize_request()
diff --git a/erpnext/erpnext_integrations/taxjar_integration.py b/erpnext/erpnext_integrations/taxjar_integration.py
index 14c86d5..b8893aa 100644
--- a/erpnext/erpnext_integrations/taxjar_integration.py
+++ b/erpnext/erpnext_integrations/taxjar_integration.py
@@ -8,14 +8,92 @@
 
 from erpnext import get_default_company, get_region
 
-SUPPORTED_COUNTRY_CODES = ["AT", "AU", "BE", "BG", "CA", "CY", "CZ", "DE", "DK", "EE", "ES", "FI",
-	"FR", "GB", "GR", "HR", "HU", "IE", "IT", "LT", "LU", "LV", "MT", "NL", "PL", "PT", "RO",
-	"SE", "SI", "SK", "US"]
-SUPPORTED_STATE_CODES = ['AL', 'AK', 'AZ', 'AR', 'CA', 'CO', 'CT', 'DE', 'DC', 'FL', 'GA', 'HI', 'ID', 'IL',
-	'IN', 'IA', 'KS', 'KY', 'LA', 'ME', 'MD', 'MA', 'MI', 'MN', 'MS', 'MO', 'MT', 'NE',
-	'NV', 'NH', 'NJ', 'NM', 'NY', 'NC', 'ND', 'OH', 'OK', 'OR', 'PA', 'RI', 'SC', 'SD',
-	'TN', 'TX', 'UT', 'VT', 'VA', 'WA', 'WV', 'WI', 'WY']
-
+SUPPORTED_COUNTRY_CODES = [
+	"AT",
+	"AU",
+	"BE",
+	"BG",
+	"CA",
+	"CY",
+	"CZ",
+	"DE",
+	"DK",
+	"EE",
+	"ES",
+	"FI",
+	"FR",
+	"GB",
+	"GR",
+	"HR",
+	"HU",
+	"IE",
+	"IT",
+	"LT",
+	"LU",
+	"LV",
+	"MT",
+	"NL",
+	"PL",
+	"PT",
+	"RO",
+	"SE",
+	"SI",
+	"SK",
+	"US",
+]
+SUPPORTED_STATE_CODES = [
+	"AL",
+	"AK",
+	"AZ",
+	"AR",
+	"CA",
+	"CO",
+	"CT",
+	"DE",
+	"DC",
+	"FL",
+	"GA",
+	"HI",
+	"ID",
+	"IL",
+	"IN",
+	"IA",
+	"KS",
+	"KY",
+	"LA",
+	"ME",
+	"MD",
+	"MA",
+	"MI",
+	"MN",
+	"MS",
+	"MO",
+	"MT",
+	"NE",
+	"NV",
+	"NH",
+	"NJ",
+	"NM",
+	"NY",
+	"NC",
+	"ND",
+	"OH",
+	"OK",
+	"OR",
+	"PA",
+	"RI",
+	"SC",
+	"SD",
+	"TN",
+	"TX",
+	"UT",
+	"VT",
+	"VA",
+	"WA",
+	"WV",
+	"WI",
+	"WY",
+]
 
 
 def get_client():
@@ -30,14 +108,14 @@
 
 	if api_key and api_url:
 		client = taxjar.Client(api_key=api_key, api_url=api_url)
-		client.set_api_config('headers', {
-				'x-api-version': '2022-01-24'
-			})
+		client.set_api_config("headers", {"x-api-version": "2022-01-24"})
 		return client
 
 
 def create_transaction(doc, method):
-	TAXJAR_CREATE_TRANSACTIONS = frappe.db.get_single_value("TaxJar Settings", "taxjar_create_transactions")
+	TAXJAR_CREATE_TRANSACTIONS = frappe.db.get_single_value(
+		"TaxJar Settings", "taxjar_create_transactions"
+	)
 
 	"""Create an order transaction in TaxJar"""
 
@@ -60,10 +138,10 @@
 	if not tax_dict:
 		return
 
-	tax_dict['transaction_id'] = doc.name
-	tax_dict['transaction_date'] = frappe.utils.today()
-	tax_dict['sales_tax'] = sales_tax
-	tax_dict['amount'] = doc.total + tax_dict['shipping']
+	tax_dict["transaction_id"] = doc.name
+	tax_dict["transaction_date"] = frappe.utils.today()
+	tax_dict["sales_tax"] = sales_tax
+	tax_dict["amount"] = doc.total + tax_dict["shipping"]
 
 	try:
 		if doc.is_return:
@@ -78,7 +156,9 @@
 
 def delete_transaction(doc, method):
 	"""Delete an existing TaxJar order transaction"""
-	TAXJAR_CREATE_TRANSACTIONS = frappe.db.get_single_value("TaxJar Settings", "taxjar_create_transactions")
+	TAXJAR_CREATE_TRANSACTIONS = frappe.db.get_single_value(
+		"TaxJar Settings", "taxjar_create_transactions"
+	)
 
 	if not TAXJAR_CREATE_TRANSACTIONS:
 		return
@@ -109,10 +189,10 @@
 	line_items = [get_line_item_dict(item, doc.docstatus) for item in doc.items]
 
 	if from_shipping_state not in SUPPORTED_STATE_CODES:
-		from_shipping_state = get_state_code(from_address, 'Company')
+		from_shipping_state = get_state_code(from_address, "Company")
 
 	if to_shipping_state not in SUPPORTED_STATE_CODES:
-		to_shipping_state = get_state_code(to_address, 'Shipping')
+		to_shipping_state = get_state_code(to_address, "Shipping")
 
 	tax_dict = {
 		"from_country": from_country_code,
@@ -128,10 +208,11 @@
 		"shipping": shipping,
 		"amount": doc.net_total,
 		"plugin": "erpnext",
-		"line_items": line_items
+		"line_items": line_items,
 	}
 	return tax_dict
 
+
 def get_state_code(address, location):
 	if address is not None:
 		state_code = get_iso_3166_2_state_code(address)
@@ -142,21 +223,21 @@
 
 	return state_code
 
+
 def get_line_item_dict(item, docstatus):
 	tax_dict = dict(
-		id = item.get('idx'),
-		quantity = item.get('qty'),
-		unit_price = item.get('rate'),
-		product_tax_code = item.get('product_tax_category')
+		id=item.get("idx"),
+		quantity=item.get("qty"),
+		unit_price=item.get("rate"),
+		product_tax_code=item.get("product_tax_category"),
 	)
 
 	if docstatus == 1:
-		tax_dict.update({
-			'sales_tax':item.get('tax_collectable')
-		})
+		tax_dict.update({"sales_tax": item.get("tax_collectable")})
 
 	return tax_dict
 
+
 def set_sales_tax(doc, method):
 	TAX_ACCOUNT_HEAD = frappe.db.get_single_value("TaxJar Settings", "tax_account_head")
 	TAXJAR_CALCULATE_TAX = frappe.db.get_single_value("TaxJar Settings", "taxjar_calculate_tax")
@@ -164,7 +245,7 @@
 	if not TAXJAR_CALCULATE_TAX:
 		return
 
-	if get_region(doc.company) != 'United States':
+	if get_region(doc.company) != "United States":
 		return
 
 	if not doc.items:
@@ -197,22 +278,26 @@
 					doc.run_method("calculate_taxes_and_totals")
 					break
 			else:
-				doc.append("taxes", {
-					"charge_type": "Actual",
-					"description": "Sales Tax",
-					"account_head": TAX_ACCOUNT_HEAD,
-					"tax_amount": tax_data.amount_to_collect
-				})
+				doc.append(
+					"taxes",
+					{
+						"charge_type": "Actual",
+						"description": "Sales Tax",
+						"account_head": TAX_ACCOUNT_HEAD,
+						"tax_amount": tax_data.amount_to_collect,
+					},
+				)
 			# Assigning values to tax_collectable and taxable_amount fields in sales item table
 			for item in tax_data.breakdown.line_items:
-				doc.get('items')[cint(item.id)-1].tax_collectable = item.tax_collectable
-				doc.get('items')[cint(item.id)-1].taxable_amount = item.taxable_amount
+				doc.get("items")[cint(item.id) - 1].tax_collectable = item.tax_collectable
+				doc.get("items")[cint(item.id) - 1].taxable_amount = item.taxable_amount
 
 			doc.run_method("calculate_taxes_and_totals")
 
+
 def check_for_nexus(doc, tax_dict):
 	TAX_ACCOUNT_HEAD = frappe.db.get_single_value("TaxJar Settings", "tax_account_head")
-	if not frappe.db.get_value('TaxJar Nexus', {'region_code': tax_dict["to_state"]}):
+	if not frappe.db.get_value("TaxJar Nexus", {"region_code": tax_dict["to_state"]}):
 		for item in doc.get("items"):
 			item.tax_collectable = flt(0)
 			item.taxable_amount = flt(0)
@@ -222,13 +307,17 @@
 				doc.taxes.remove(tax)
 		return
 
+
 def check_sales_tax_exemption(doc):
 	# if the party is exempt from sales tax, then set all tax account heads to zero
 	TAX_ACCOUNT_HEAD = frappe.db.get_single_value("TaxJar Settings", "tax_account_head")
 
-	sales_tax_exempted = hasattr(doc, "exempt_from_sales_tax") and doc.exempt_from_sales_tax \
-		or frappe.db.has_column("Customer", "exempt_from_sales_tax") \
+	sales_tax_exempted = (
+		hasattr(doc, "exempt_from_sales_tax")
+		and doc.exempt_from_sales_tax
+		or frappe.db.has_column("Customer", "exempt_from_sales_tax")
 		and frappe.db.get_value("Customer", doc.customer, "exempt_from_sales_tax")
+	)
 
 	if sales_tax_exempted:
 		for tax in doc.taxes:
@@ -240,6 +329,7 @@
 	else:
 		return False
 
+
 def validate_tax_request(tax_dict):
 	"""Return the sales tax that should be collected for a given order."""
 
@@ -283,9 +373,12 @@
 
 def get_iso_3166_2_state_code(address):
 	import pycountry
+
 	country_code = frappe.db.get_value("Country", address.get("country"), "code")
 
-	error_message = _("""{0} is not a valid state! Check for typos or enter the ISO code for your state.""").format(address.get("state"))
+	error_message = _(
+		"""{0} is not a valid state! Check for typos or enter the ISO code for your state."""
+	).format(address.get("state"))
 	state = address.get("state").upper().strip()
 
 	# The max length for ISO state codes is 3, excluding the country code
@@ -306,7 +399,7 @@
 		except LookupError:
 			frappe.throw(_(error_message))
 		else:
-			return lookup_state.code.split('-')[1]
+			return lookup_state.code.split("-")[1]
 
 
 def sanitize_error_response(response):
@@ -317,7 +410,7 @@
 		"to zip": "Zipcode",
 		"to city": "City",
 		"to state": "State",
-		"to country": "Country"
+		"to country": "Country",
 	}
 
 	for k, v in sanitized_responses.items():
diff --git a/erpnext/erpnext_integrations/utils.py b/erpnext/erpnext_integrations/utils.py
index 30d3948..981486e 100644
--- a/erpnext/erpnext_integrations/utils.py
+++ b/erpnext/erpnext_integrations/utils.py
@@ -9,28 +9,24 @@
 from erpnext import get_default_company
 
 
-def validate_webhooks_request(doctype,  hmac_key, secret_key='secret'):
+def validate_webhooks_request(doctype, hmac_key, secret_key="secret"):
 	def innerfn(fn):
 		settings = frappe.get_doc(doctype)
 
 		if frappe.request and settings and settings.get(secret_key) and not frappe.flags.in_test:
 			sig = base64.b64encode(
-				hmac.new(
-					settings.get(secret_key).encode('utf8'),
-					frappe.request.data,
-					hashlib.sha256
-				).digest()
+				hmac.new(settings.get(secret_key).encode("utf8"), frappe.request.data, hashlib.sha256).digest()
 			)
 
-			if frappe.request.data and \
-				not sig == bytes(frappe.get_request_header(hmac_key).encode()):
-					frappe.throw(_("Unverified Webhook Data"))
+			if frappe.request.data and not sig == bytes(frappe.get_request_header(hmac_key).encode()):
+				frappe.throw(_("Unverified Webhook Data"))
 			frappe.set_user(settings.modified_by)
 
 		return fn
 
 	return innerfn
 
+
 def get_webhook_address(connector_name, method, exclude_uri=False, force_https=False):
 	endpoint = "erpnext.erpnext_integrations.connectors.{0}.{1}".format(connector_name, method)
 
@@ -50,34 +46,40 @@
 
 	return server_url
 
+
 def create_mode_of_payment(gateway, payment_type="General"):
-	payment_gateway_account = frappe.db.get_value("Payment Gateway Account", {
-			"payment_gateway": gateway
-		}, ['payment_account'])
+	payment_gateway_account = frappe.db.get_value(
+		"Payment Gateway Account", {"payment_gateway": gateway}, ["payment_account"]
+	)
 
 	mode_of_payment = frappe.db.exists("Mode of Payment", gateway)
 	if not mode_of_payment and payment_gateway_account:
-		mode_of_payment = frappe.get_doc({
-			"doctype": "Mode of Payment",
-			"mode_of_payment": gateway,
-			"enabled": 1,
-			"type": payment_type,
-			"accounts": [{
-				"doctype": "Mode of Payment Account",
-				"company": get_default_company(),
-				"default_account": payment_gateway_account
-			}]
-		})
+		mode_of_payment = frappe.get_doc(
+			{
+				"doctype": "Mode of Payment",
+				"mode_of_payment": gateway,
+				"enabled": 1,
+				"type": payment_type,
+				"accounts": [
+					{
+						"doctype": "Mode of Payment Account",
+						"company": get_default_company(),
+						"default_account": payment_gateway_account,
+					}
+				],
+			}
+		)
 		mode_of_payment.insert(ignore_permissions=True)
 
 		return mode_of_payment
 	elif mode_of_payment:
 		return frappe.get_doc("Mode of Payment", mode_of_payment)
 
+
 def get_tracking_url(carrier, tracking_number):
 	# Return the formatted Tracking URL.
-	tracking_url = ''
-	url_reference = frappe.get_value('Parcel Service', carrier, 'url_reference')
+	tracking_url = ""
+	url_reference = frappe.get_value("Parcel Service", carrier, "url_reference")
 	if url_reference:
-		tracking_url = frappe.render_template(url_reference, {'tracking_number': tracking_number})
+		tracking_url = frappe.render_template(url_reference, {"tracking_number": tracking_number})
 	return tracking_url
diff --git a/erpnext/exceptions.py b/erpnext/exceptions.py
index 8d6f13a..86c29d4 100644
--- a/erpnext/exceptions.py
+++ b/erpnext/exceptions.py
@@ -2,9 +2,25 @@
 
 
 # accounts
-class PartyFrozen(frappe.ValidationError): pass
-class InvalidAccountCurrency(frappe.ValidationError): pass
-class InvalidCurrency(frappe.ValidationError): pass
-class PartyDisabled(frappe.ValidationError):pass
-class InvalidAccountDimensionError(frappe.ValidationError): pass
-class MandatoryAccountDimensionError(frappe.ValidationError): pass
+class PartyFrozen(frappe.ValidationError):
+	pass
+
+
+class InvalidAccountCurrency(frappe.ValidationError):
+	pass
+
+
+class InvalidCurrency(frappe.ValidationError):
+	pass
+
+
+class PartyDisabled(frappe.ValidationError):
+	pass
+
+
+class InvalidAccountDimensionError(frappe.ValidationError):
+	pass
+
+
+class MandatoryAccountDimensionError(frappe.ValidationError):
+	pass
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 7c6941e..0b7dd33 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -12,7 +12,7 @@
 app_logo_url = "/assets/erpnext/images/erpnext-logo.svg"
 
 
-develop_version = '13.x.x-develop'
+develop_version = "14.x.x-develop"
 
 app_include_js = "erpnext.bundle.js"
 app_include_css = "erpnext.bundle.css"
@@ -25,12 +25,10 @@
 	"Communication": "public/js/communication.js",
 	"Event": "public/js/event.js",
 	"Newsletter": "public/js/newsletter.js",
-	"Contact": "public/js/contact.js"
+	"Contact": "public/js/contact.js",
 }
 
-override_doctype_class = {
-	'Address': 'erpnext.accounts.custom.address.ERPNextAddress'
-}
+override_doctype_class = {"Address": "erpnext.accounts.custom.address.ERPNextAddress"}
 
 welcome_email = "erpnext.setup.utils.welcome_email"
 
@@ -51,137 +49,206 @@
 
 on_session_creation = [
 	"erpnext.portal.utils.create_customer_or_supplier",
-	"erpnext.e_commerce.shopping_cart.utils.set_cart_count"
+	"erpnext.e_commerce.shopping_cart.utils.set_cart_count",
 ]
 on_logout = "erpnext.e_commerce.shopping_cart.utils.clear_cart_count"
 
-treeviews = ['Account', 'Cost Center', 'Warehouse', 'Item Group', 'Customer Group', 'Sales Person', 'Territory', 'Assessment Group', 'Department']
+treeviews = [
+	"Account",
+	"Cost Center",
+	"Warehouse",
+	"Item Group",
+	"Customer Group",
+	"Supplier Group",
+	"Sales Person",
+	"Territory",
+	"Department",
+]
 
 # website
-update_website_context = ["erpnext.e_commerce.shopping_cart.utils.update_website_context", "erpnext.education.doctype.education_settings.education_settings.update_website_context"]
+update_website_context = [
+	"erpnext.e_commerce.shopping_cart.utils.update_website_context",
+]
 my_account_context = "erpnext.e_commerce.shopping_cart.utils.update_my_account_context"
 webform_list_context = "erpnext.controllers.website_list_for_contact.get_webform_list_context"
 
-calendars = ["Task", "Work Order", "Leave Application", "Sales Order", "Holiday List", "Course Schedule"]
+calendars = [
+	"Task",
+	"Work Order",
+	"Leave Application",
+	"Sales Order",
+	"Holiday List",
+]
 
-domains = {
-	'Distribution': 'erpnext.domains.distribution',
-	'Education': 'erpnext.domains.education',
-	'Manufacturing': 'erpnext.domains.manufacturing',
-	'Retail': 'erpnext.domains.retail',
-	'Services': 'erpnext.domains.services',
-}
-
-website_generators = ["Item Group", "Website Item", "BOM", "Sales Partner",
-	"Job Opening", "Student Admission"]
+website_generators = ["Item Group", "Website Item", "BOM", "Sales Partner", "Job Opening"]
 
 website_context = {
 	"favicon": "/assets/erpnext/images/erpnext-favicon.svg",
-	"splash_image": "/assets/erpnext/images/erpnext-logo.svg"
+	"splash_image": "/assets/erpnext/images/erpnext-logo.svg",
 }
 
 website_route_rules = [
 	{"from_route": "/orders", "to_route": "Sales Order"},
-	{"from_route": "/orders/<path:name>", "to_route": "order",
-		"defaults": {
-			"doctype": "Sales Order",
-			"parents": [{"label": _("Orders"), "route": "orders"}]
-		}
+	{
+		"from_route": "/orders/<path:name>",
+		"to_route": "order",
+		"defaults": {"doctype": "Sales Order", "parents": [{"label": _("Orders"), "route": "orders"}]},
 	},
 	{"from_route": "/invoices", "to_route": "Sales Invoice"},
-	{"from_route": "/invoices/<path:name>", "to_route": "order",
+	{
+		"from_route": "/invoices/<path:name>",
+		"to_route": "order",
 		"defaults": {
 			"doctype": "Sales Invoice",
-			"parents": [{"label": _("Invoices"), "route": "invoices"}]
-		}
+			"parents": [{"label": _("Invoices"), "route": "invoices"}],
+		},
 	},
 	{"from_route": "/supplier-quotations", "to_route": "Supplier Quotation"},
-	{"from_route": "/supplier-quotations/<path:name>", "to_route": "order",
+	{
+		"from_route": "/supplier-quotations/<path:name>",
+		"to_route": "order",
 		"defaults": {
 			"doctype": "Supplier Quotation",
-			"parents": [{"label": _("Supplier Quotation"), "route": "supplier-quotations"}]
-		}
+			"parents": [{"label": _("Supplier Quotation"), "route": "supplier-quotations"}],
+		},
 	},
 	{"from_route": "/purchase-orders", "to_route": "Purchase Order"},
-	{"from_route": "/purchase-orders/<path:name>", "to_route": "order",
+	{
+		"from_route": "/purchase-orders/<path:name>",
+		"to_route": "order",
 		"defaults": {
 			"doctype": "Purchase Order",
-			"parents": [{"label": _("Purchase Order"), "route": "purchase-orders"}]
-		}
+			"parents": [{"label": _("Purchase Order"), "route": "purchase-orders"}],
+		},
 	},
 	{"from_route": "/purchase-invoices", "to_route": "Purchase Invoice"},
-	{"from_route": "/purchase-invoices/<path:name>", "to_route": "order",
+	{
+		"from_route": "/purchase-invoices/<path:name>",
+		"to_route": "order",
 		"defaults": {
 			"doctype": "Purchase Invoice",
-			"parents": [{"label": _("Purchase Invoice"), "route": "purchase-invoices"}]
-		}
+			"parents": [{"label": _("Purchase Invoice"), "route": "purchase-invoices"}],
+		},
 	},
 	{"from_route": "/quotations", "to_route": "Quotation"},
-	{"from_route": "/quotations/<path:name>", "to_route": "order",
+	{
+		"from_route": "/quotations/<path:name>",
+		"to_route": "order",
 		"defaults": {
 			"doctype": "Quotation",
-			"parents": [{"label": _("Quotations"), "route": "quotations"}]
-		}
+			"parents": [{"label": _("Quotations"), "route": "quotations"}],
+		},
 	},
 	{"from_route": "/shipments", "to_route": "Delivery Note"},
-	{"from_route": "/shipments/<path:name>", "to_route": "order",
+	{
+		"from_route": "/shipments/<path:name>",
+		"to_route": "order",
 		"defaults": {
 			"doctype": "Delivery Note",
-			"parents": [{"label": _("Shipments"), "route": "shipments"}]
-		}
+			"parents": [{"label": _("Shipments"), "route": "shipments"}],
+		},
 	},
 	{"from_route": "/rfq", "to_route": "Request for Quotation"},
-	{"from_route": "/rfq/<path:name>", "to_route": "rfq",
+	{
+		"from_route": "/rfq/<path:name>",
+		"to_route": "rfq",
 		"defaults": {
 			"doctype": "Request for Quotation",
-			"parents": [{"label": _("Request for Quotation"), "route": "rfq"}]
-		}
+			"parents": [{"label": _("Request for Quotation"), "route": "rfq"}],
+		},
 	},
 	{"from_route": "/addresses", "to_route": "Address"},
-	{"from_route": "/addresses/<path:name>", "to_route": "addresses",
-		"defaults": {
-			"doctype": "Address",
-			"parents": [{"label": _("Addresses"), "route": "addresses"}]
-		}
+	{
+		"from_route": "/addresses/<path:name>",
+		"to_route": "addresses",
+		"defaults": {"doctype": "Address", "parents": [{"label": _("Addresses"), "route": "addresses"}]},
 	},
 	{"from_route": "/jobs", "to_route": "Job Opening"},
-	{"from_route": "/admissions", "to_route": "Student Admission"},
 	{"from_route": "/boms", "to_route": "BOM"},
 	{"from_route": "/timesheets", "to_route": "Timesheet"},
 	{"from_route": "/material-requests", "to_route": "Material Request"},
-	{"from_route": "/material-requests/<path:name>", "to_route": "material_request_info",
+	{
+		"from_route": "/material-requests/<path:name>",
+		"to_route": "material_request_info",
 		"defaults": {
 			"doctype": "Material Request",
-			"parents": [{"label": _("Material Request"), "route": "material-requests"}]
-		}
+			"parents": [{"label": _("Material Request"), "route": "material-requests"}],
+		},
 	},
-	{"from_route": "/project", "to_route": "Project"}
+	{"from_route": "/project", "to_route": "Project"},
 ]
 
 standard_portal_menu_items = [
 	{"title": _("Projects"), "route": "/project", "reference_doctype": "Project"},
-	{"title": _("Request for Quotations"), "route": "/rfq", "reference_doctype": "Request for Quotation", "role": "Supplier"},
-	{"title": _("Supplier Quotation"), "route": "/supplier-quotations", "reference_doctype": "Supplier Quotation", "role": "Supplier"},
-	{"title": _("Purchase Orders"), "route": "/purchase-orders", "reference_doctype": "Purchase Order", "role": "Supplier"},
-	{"title": _("Purchase Invoices"), "route": "/purchase-invoices", "reference_doctype": "Purchase Invoice", "role": "Supplier"},
-	{"title": _("Quotations"), "route": "/quotations", "reference_doctype": "Quotation", "role":"Customer"},
-	{"title": _("Orders"), "route": "/orders", "reference_doctype": "Sales Order", "role":"Customer"},
-	{"title": _("Invoices"), "route": "/invoices", "reference_doctype": "Sales Invoice", "role":"Customer"},
-	{"title": _("Shipments"), "route": "/shipments", "reference_doctype": "Delivery Note", "role":"Customer"},
-	{"title": _("Issues"), "route": "/issues", "reference_doctype": "Issue", "role":"Customer"},
+	{
+		"title": _("Request for Quotations"),
+		"route": "/rfq",
+		"reference_doctype": "Request for Quotation",
+		"role": "Supplier",
+	},
+	{
+		"title": _("Supplier Quotation"),
+		"route": "/supplier-quotations",
+		"reference_doctype": "Supplier Quotation",
+		"role": "Supplier",
+	},
+	{
+		"title": _("Purchase Orders"),
+		"route": "/purchase-orders",
+		"reference_doctype": "Purchase Order",
+		"role": "Supplier",
+	},
+	{
+		"title": _("Purchase Invoices"),
+		"route": "/purchase-invoices",
+		"reference_doctype": "Purchase Invoice",
+		"role": "Supplier",
+	},
+	{
+		"title": _("Quotations"),
+		"route": "/quotations",
+		"reference_doctype": "Quotation",
+		"role": "Customer",
+	},
+	{
+		"title": _("Orders"),
+		"route": "/orders",
+		"reference_doctype": "Sales Order",
+		"role": "Customer",
+	},
+	{
+		"title": _("Invoices"),
+		"route": "/invoices",
+		"reference_doctype": "Sales Invoice",
+		"role": "Customer",
+	},
+	{
+		"title": _("Shipments"),
+		"route": "/shipments",
+		"reference_doctype": "Delivery Note",
+		"role": "Customer",
+	},
+	{"title": _("Issues"), "route": "/issues", "reference_doctype": "Issue", "role": "Customer"},
 	{"title": _("Addresses"), "route": "/addresses", "reference_doctype": "Address"},
-	{"title": _("Timesheets"), "route": "/timesheets", "reference_doctype": "Timesheet", "role":"Customer"},
-	{"title": _("Fees"), "route": "/fees", "reference_doctype": "Fees", "role":"Student"},
+	{
+		"title": _("Timesheets"),
+		"route": "/timesheets",
+		"reference_doctype": "Timesheet",
+		"role": "Customer",
+	},
 	{"title": _("Newsletter"), "route": "/newsletters", "reference_doctype": "Newsletter"},
-	{"title": _("Admission"), "route": "/admissions", "reference_doctype": "Student Admission", "role": "Student"},
-	{"title": _("Material Request"), "route": "/material-requests", "reference_doctype": "Material Request", "role": "Customer"},
+	{
+		"title": _("Material Request"),
+		"route": "/material-requests",
+		"reference_doctype": "Material Request",
+		"role": "Customer",
+	},
 	{"title": _("Appointment Booking"), "route": "/book_appointment"},
 ]
 
 default_roles = [
-	{'role': 'Customer', 'doctype':'Contact', 'email_field': 'email_id'},
-	{'role': 'Supplier', 'doctype':'Contact', 'email_field': 'email_id'},
-	{'role': 'Student', 'doctype':'Student', 'email_field': 'student_email_id'},
+	{"role": "Customer", "doctype": "Contact", "email_field": "email_id"},
+	{"role": "Supplier", "doctype": "Contact", "email_field": "email_id"},
 ]
 
 sounds = [
@@ -189,9 +256,7 @@
 	{"name": "call-disconnect", "src": "/assets/erpnext/sounds/call-disconnect.mp3", "volume": 0.2},
 ]
 
-has_upload_permission = {
-	"Employee": "erpnext.hr.doctype.employee.employee.has_upload_permission"
-}
+has_upload_permission = {"Employee": "erpnext.hr.doctype.employee.employee.has_upload_permission"}
 
 has_website_permission = {
 	"Sales Order": "erpnext.controllers.website_list_for_contact.has_website_permission",
@@ -220,18 +285,20 @@
 	},
 	"Stock Entry": {
 		"on_submit": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty",
-		"on_cancel": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty"
+		"on_cancel": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty",
 	},
 	"User": {
 		"after_insert": "frappe.contacts.doctype.contact.contact.update_contact",
 		"validate": "erpnext.hr.doctype.employee.employee.validate_employee_role",
-		"on_update": ["erpnext.hr.doctype.employee.employee.update_user_permissions",
-			"erpnext.portal.utils.set_default_role"]
+		"on_update": [
+			"erpnext.hr.doctype.employee.employee.update_user_permissions",
+			"erpnext.portal.utils.set_default_role",
+		],
 	},
 	"Communication": {
 		"on_update": [
 			"erpnext.support.doctype.service_level_agreement.service_level_agreement.on_communication_update",
-			"erpnext.support.doctype.issue.issue.set_first_response_time"
+			"erpnext.support.doctype.issue.issue.set_first_response_time",
 		]
 	},
 	"Sales Taxes and Charges Template": {
@@ -242,56 +309,57 @@
 			"erpnext.regional.create_transaction_log",
 			"erpnext.regional.italy.utils.sales_invoice_on_submit",
 			"erpnext.regional.saudi_arabia.utils.create_qr_code",
-			"erpnext.erpnext_integrations.taxjar_integration.create_transaction"
+			"erpnext.erpnext_integrations.taxjar_integration.create_transaction",
 		],
 		"on_cancel": [
 			"erpnext.regional.italy.utils.sales_invoice_on_cancel",
 			"erpnext.erpnext_integrations.taxjar_integration.delete_transaction",
-			"erpnext.regional.saudi_arabia.utils.delete_qr_code_file"
+			"erpnext.regional.saudi_arabia.utils.delete_qr_code_file",
 		],
 		"on_trash": "erpnext.regional.check_deletion_permission",
 	},
-	"POS Invoice": {
-		"on_submit": ["erpnext.regional.saudi_arabia.utils.create_qr_code"]
-	},
+	"POS Invoice": {"on_submit": ["erpnext.regional.saudi_arabia.utils.create_qr_code"]},
 	"Purchase Invoice": {
 		"validate": [
 			"erpnext.regional.united_arab_emirates.utils.update_grand_total_for_rcm",
 			"erpnext.regional.united_arab_emirates.utils.validate_returns",
+			"erpnext.regional.india.utils.update_taxable_values",
 		]
 	},
 	"Payment Entry": {
-		"on_submit": ["erpnext.regional.create_transaction_log", "erpnext.accounts.doctype.payment_request.payment_request.update_payment_req_status", "erpnext.accounts.doctype.dunning.dunning.resolve_dunning"],
-		"on_trash": "erpnext.regional.check_deletion_permission"
+		"on_submit": [
+			"erpnext.regional.create_transaction_log",
+			"erpnext.accounts.doctype.payment_request.payment_request.update_payment_req_status",
+			"erpnext.accounts.doctype.dunning.dunning.resolve_dunning",
+		],
+		"on_trash": "erpnext.regional.check_deletion_permission",
 	},
-	'Address': {
-		'validate': [
-			'erpnext.regional.italy.utils.set_state_code',
+	"Address": {
+		"validate": [
+			"erpnext.regional.italy.utils.set_state_code",
 		],
 	},
 	"Contact": {
 		"on_trash": "erpnext.support.doctype.issue.issue.update_issue",
 		"after_insert": "erpnext.telephony.doctype.call_log.call_log.link_existing_conversations",
-		"validate": ["erpnext.crm.utils.update_lead_phone_numbers"]
+		"validate": ["erpnext.crm.utils.update_lead_phone_numbers"],
 	},
 	"Email Unsubscribe": {
 		"after_insert": "erpnext.crm.doctype.email_campaign.email_campaign.unsubscribe_recipient"
 	},
-	('Quotation', 'Sales Order', 'Sales Invoice'): {
-		'validate': ["erpnext.erpnext_integrations.taxjar_integration.set_sales_tax"]
+	("Quotation", "Sales Order", "Sales Invoice"): {
+		"validate": ["erpnext.erpnext_integrations.taxjar_integration.set_sales_tax"]
 	},
-	"Company": {
-		"on_trash": ["erpnext.regional.saudi_arabia.utils.delete_vat_settings_for_company"]
-	},
+	"Company": {"on_trash": ["erpnext.regional.saudi_arabia.utils.delete_vat_settings_for_company"]},
 	"Integration Request": {
 		"validate": "erpnext.accounts.doctype.payment_request.payment_request.validate_payment"
-	}
+	},
 }
 
 # On cancel event Payment Entry will be exempted and all linked submittable doctype will get cancelled.
 # to maintain data integrity we exempted payment entry. it will un-link when sales invoice get cancelled.
 # if payment entry not in auto cancel exempted doctypes it will cancel payment entry.
-auto_cancel_exempted_doctypes= [
+auto_cancel_exempted_doctypes = [
 	"Payment Entry",
 ]
 
@@ -299,27 +367,30 @@
 
 scheduler_events = {
 	"cron": {
+		"0/5 * * * *": [
+			"erpnext.manufacturing.doctype.bom_update_log.bom_update_log.resume_bom_cost_update_jobs",
+		],
 		"0/30 * * * *": [
 			"erpnext.utilities.doctype.video.video.update_youtube_data",
-		]
+		],
 	},
 	"all": [
 		"erpnext.projects.doctype.project.project.project_status_update_reminder",
 		"erpnext.hr.doctype.interview.interview.send_interview_reminder",
-		"erpnext.crm.doctype.social_media_post.social_media_post.process_scheduled_social_media_posts"
+		"erpnext.crm.doctype.social_media_post.social_media_post.process_scheduled_social_media_posts",
 	],
 	"hourly": [
-		'erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.trigger_emails',
+		"erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.trigger_emails",
 		"erpnext.accounts.doctype.subscription.subscription.process_all",
 		"erpnext.accounts.doctype.gl_entry.gl_entry.rename_gle_sle_docs",
 		"erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.automatic_synchronization",
 		"erpnext.projects.doctype.project.project.hourly_reminder",
 		"erpnext.projects.doctype.project.project.collect_project_status",
-		"erpnext.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts"
+		"erpnext.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts",
 	],
 	"hourly_long": [
 		"erpnext.stock.doctype.repost_item_valuation.repost_item_valuation.repost_entries",
-		"erpnext.bulk_transaction.doctype.bulk_transaction_log.bulk_transaction_log.retry_failing_transaction"
+		"erpnext.bulk_transaction.doctype.bulk_transaction_log.bulk_transaction_log.retry_failing_transaction",
 	],
 	"daily": [
 		"erpnext.stock.reorder_item.reorder_item",
@@ -347,28 +418,24 @@
 		"erpnext.selling.doctype.quotation.quotation.set_expired_status",
 		"erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status",
 		"erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_auto_email",
-		"erpnext.hr.doctype.interview.interview.send_daily_feedback_reminder"
+		"erpnext.hr.doctype.interview.interview.send_daily_feedback_reminder",
 	],
 	"daily_long": [
 		"erpnext.setup.doctype.email_digest.email_digest.send",
-		"erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms",
+		"erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.auto_update_latest_price_in_all_boms",
 		"erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry.process_expired_allocation",
 		"erpnext.hr.utils.generate_leave_encashment",
 		"erpnext.hr.utils.allocate_earned_leaves",
 		"erpnext.loan_management.doctype.process_loan_security_shortfall.process_loan_security_shortfall.create_process_loan_security_shortfall",
 		"erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual.process_loan_interest_accrual_for_term_loans",
-		"erpnext.crm.doctype.lead.lead.daily_open_lead"
+		"erpnext.crm.doctype.lead.lead.daily_open_lead",
 	],
-	"weekly": [
-		"erpnext.hr.doctype.employee.employee_reminders.send_reminders_in_advance_weekly"
-	],
-	"monthly": [
-		"erpnext.hr.doctype.employee.employee_reminders.send_reminders_in_advance_monthly"
-	],
+	"weekly": ["erpnext.hr.doctype.employee.employee_reminders.send_reminders_in_advance_weekly"],
+	"monthly": ["erpnext.hr.doctype.employee.employee_reminders.send_reminders_in_advance_monthly"],
 	"monthly_long": [
 		"erpnext.accounts.deferred_revenue.process_deferred_accounting",
-		"erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual.process_loan_interest_accrual_for_demand_loans"
-	]
+		"erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual.process_loan_interest_accrual_for_demand_loans",
+	],
 }
 
 email_brand_image = "assets/erpnext/images/erpnext-logo.jpg"
@@ -387,51 +454,85 @@
 }
 
 bot_parsers = [
-	'erpnext.utilities.bot.FindItemBot',
+	"erpnext.utilities.bot.FindItemBot",
 ]
 
-get_site_info = 'erpnext.utilities.get_site_info'
+get_site_info = "erpnext.utilities.get_site_info"
 
 payment_gateway_enabled = "erpnext.accounts.utils.create_payment_gateway_account"
 
 communication_doctypes = ["Customer", "Supplier"]
 
-accounting_dimension_doctypes = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset",
-	"Expense Claim", "Expense Claim Detail", "Expense Taxes and Charges", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note",
-	"Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item",
-	"Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
-	"Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
-	"Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription",
-	"Subscription Plan", "POS Invoice", "POS Invoice Item"
+accounting_dimension_doctypes = [
+	"GL Entry",
+	"Payment Ledger Entry",
+	"Sales Invoice",
+	"Purchase Invoice",
+	"Payment Entry",
+	"Asset",
+	"Expense Claim",
+	"Expense Claim Detail",
+	"Expense Taxes and Charges",
+	"Stock Entry",
+	"Budget",
+	"Payroll Entry",
+	"Delivery Note",
+	"Sales Invoice Item",
+	"Purchase Invoice Item",
+	"Purchase Order Item",
+	"Journal Entry Account",
+	"Material Request Item",
+	"Delivery Note Item",
+	"Purchase Receipt Item",
+	"Stock Entry Detail",
+	"Payment Entry Deduction",
+	"Sales Taxes and Charges",
+	"Purchase Taxes and Charges",
+	"Shipping Rule",
+	"Landed Cost Item",
+	"Asset Value Adjustment",
+	"Loyalty Program",
+	"Stock Reconciliation",
+	"Travel Request",
+	"POS Profile",
+	"Opening Invoice Creation Tool",
+	"Opening Invoice Creation Tool Item",
+	"Subscription",
+	"Subscription Plan",
+	"POS Invoice",
+	"POS Invoice Item",
+	"Purchase Order",
+	"Purchase Receipt",
+	"Sales Order",
 ]
 
 regional_overrides = {
-	'France': {
-		'erpnext.tests.test_regional.test_method': 'erpnext.regional.france.utils.test_method'
+	"France": {
+		"erpnext.tests.test_regional.test_method": "erpnext.regional.france.utils.test_method"
 	},
-	'United Arab Emirates': {
-		'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data',
-		'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.united_arab_emirates.utils.make_regional_gl_entries',
+	"United Arab Emirates": {
+		"erpnext.controllers.taxes_and_totals.update_itemised_tax_data": "erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data",
+		"erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries": "erpnext.regional.united_arab_emirates.utils.make_regional_gl_entries",
 	},
-	'Saudi Arabia': {
-		'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data'
+	"Saudi Arabia": {
+		"erpnext.controllers.taxes_and_totals.update_itemised_tax_data": "erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data"
 	},
-	'Italy': {
-		'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.italy.utils.update_itemised_tax_data',
-		'erpnext.controllers.accounts_controller.validate_regional': 'erpnext.regional.italy.utils.sales_invoice_validate',
-	}
+	"Italy": {
+		"erpnext.controllers.taxes_and_totals.update_itemised_tax_data": "erpnext.regional.italy.utils.update_itemised_tax_data",
+		"erpnext.controllers.accounts_controller.validate_regional": "erpnext.regional.italy.utils.sales_invoice_validate",
+	},
 }
 user_privacy_documents = [
 	{
-		'doctype': 'Lead',
-		'match_field': 'email_id',
-		'personal_fields': ['phone', 'mobile_no', 'fax', 'website', 'lead_name'],
+		"doctype": "Lead",
+		"match_field": "email_id",
+		"personal_fields": ["phone", "mobile_no", "fax", "website", "lead_name"],
 	},
 	{
-		'doctype': 'Opportunity',
-		'match_field': 'contact_email',
-		'personal_fields': ['contact_mobile', 'contact_display', 'customer_name'],
-	}
+		"doctype": "Opportunity",
+		"match_field": "contact_email",
+		"personal_fields": ["contact_mobile", "contact_display", "customer_name"],
+	},
 ]
 
 # ERPNext doctypes for Global Search
@@ -486,50 +587,8 @@
 		{"doctype": "Maintenance Visit", "index": 46},
 		{"doctype": "Warranty Claim", "index": 47},
 	],
-	"Education": [
-		{'doctype': 'Article', 'index': 1},
-		{'doctype': 'Video', 'index': 2},
-		{'doctype': 'Topic', 'index': 3},
-		{'doctype': 'Course', 'index': 4},
-		{'doctype': 'Program', 'index': 5},
-		{'doctype': 'Quiz', 'index': 6},
-		{'doctype': 'Question', 'index': 7},
-		{'doctype': 'Fee Schedule', 'index': 8},
-		{'doctype': 'Fee Structure', 'index': 9},
-		{'doctype': 'Fees', 'index': 10},
-		{'doctype': 'Student Group', 'index': 11},
-		{'doctype': 'Student', 'index': 12},
-		{'doctype': 'Instructor', 'index': 13},
-		{'doctype': 'Course Activity', 'index': 14},
-		{'doctype': 'Quiz Activity', 'index': 15},
-		{'doctype': 'Course Enrollment', 'index': 16},
-		{'doctype': 'Program Enrollment', 'index': 17},
-		{'doctype': 'Student Language', 'index': 18},
-		{'doctype': 'Student Applicant', 'index': 19},
-		{'doctype': 'Assessment Result', 'index': 20},
-		{'doctype': 'Assessment Plan', 'index': 21},
-		{'doctype': 'Grading Scale', 'index': 22},
-		{'doctype': 'Guardian', 'index': 23},
-		{'doctype': 'Student Leave Application', 'index': 24},
-		{'doctype': 'Student Log', 'index': 25},
-		{'doctype': 'Room', 'index': 26},
-		{'doctype': 'Course Schedule', 'index': 27},
-		{'doctype': 'Student Attendance', 'index': 28},
-		{'doctype': 'Announcement', 'index': 29},
-		{'doctype': 'Student Category', 'index': 30},
-		{'doctype': 'Assessment Group', 'index': 31},
-		{'doctype': 'Student Batch Name', 'index': 32},
-		{'doctype': 'Assessment Criteria', 'index': 33},
-		{'doctype': 'Academic Year', 'index': 34},
-		{'doctype': 'Academic Term', 'index': 35},
-		{'doctype': 'School House', 'index': 36},
-		{'doctype': 'Student Admission', 'index': 37},
-		{'doctype': 'Fee Category', 'index': 38},
-		{'doctype': 'Assessment Code', 'index': 39},
-		{'doctype': 'Discussion', 'index': 40},
-	],
 }
 
 additional_timeline_content = {
-	'*': ['erpnext.telephony.doctype.call_log.call_log.get_linked_call_logs']
+	"*": ["erpnext.telephony.doctype.call_log.call_log.get_linked_call_logs"]
 }
diff --git a/erpnext/hr/doctype/appointment_letter/appointment_letter.py b/erpnext/hr/doctype/appointment_letter/appointment_letter.py
index 71327bf..a58589a 100644
--- a/erpnext/hr/doctype/appointment_letter/appointment_letter.py
+++ b/erpnext/hr/doctype/appointment_letter/appointment_letter.py
@@ -9,18 +9,21 @@
 class AppointmentLetter(Document):
 	pass
 
+
 @frappe.whitelist()
 def get_appointment_letter_details(template):
 	body = []
-	intro = frappe.get_list('Appointment Letter Template',
-		fields=['introduction', 'closing_notes'],
-		filters={'name': template}
+	intro = frappe.get_list(
+		"Appointment Letter Template",
+		fields=["introduction", "closing_notes"],
+		filters={"name": template},
 	)[0]
-	content = frappe.get_all('Appointment Letter content',
-		fields=['title', 'description'],
-		filters={'parent': template},
-		order_by='idx'
+	content = frappe.get_all(
+		"Appointment Letter content",
+		fields=["title", "description"],
+		filters={"parent": template},
+		order_by="idx",
 	)
 	body.append(intro)
-	body.append({'description': content})
+	body.append({"description": content})
 	return body
diff --git a/erpnext/hr/doctype/appraisal/appraisal.py b/erpnext/hr/doctype/appraisal/appraisal.py
index 83273f8..382c643 100644
--- a/erpnext/hr/doctype/appraisal/appraisal.py
+++ b/erpnext/hr/doctype/appraisal/appraisal.py
@@ -34,46 +34,61 @@
 			frappe.throw(_("End Date can not be less than Start Date"))
 
 	def validate_existing_appraisal(self):
-		chk = frappe.db.sql("""select name from `tabAppraisal` where employee=%s
+		chk = frappe.db.sql(
+			"""select name from `tabAppraisal` where employee=%s
 			and (status='Submitted' or status='Completed')
 			and ((start_date>=%s and start_date<=%s)
 			or (end_date>=%s and end_date<=%s))""",
-			(self.employee,self.start_date,self.end_date,self.start_date,self.end_date))
+			(self.employee, self.start_date, self.end_date, self.start_date, self.end_date),
+		)
 		if chk:
-			frappe.throw(_("Appraisal {0} created for Employee {1} in the given date range").format(chk[0][0], self.employee_name))
+			frappe.throw(
+				_("Appraisal {0} created for Employee {1} in the given date range").format(
+					chk[0][0], self.employee_name
+				)
+			)
 
 	def calculate_total(self):
-		total, total_w  = 0, 0
-		for d in self.get('goals'):
+		total, total_w = 0, 0
+		for d in self.get("goals"):
 			if d.score:
 				d.score_earned = flt(d.score) * flt(d.per_weightage) / 100
 				total = total + d.score_earned
 			total_w += flt(d.per_weightage)
 
 		if int(total_w) != 100:
-			frappe.throw(_("Total weightage assigned should be 100%.<br>It is {0}").format(str(total_w) + "%"))
+			frappe.throw(
+				_("Total weightage assigned should be 100%.<br>It is {0}").format(str(total_w) + "%")
+			)
 
-		if frappe.db.get_value("Employee", self.employee, "user_id") != \
-				frappe.session.user and total == 0:
+		if (
+			frappe.db.get_value("Employee", self.employee, "user_id") != frappe.session.user and total == 0
+		):
 			frappe.throw(_("Total cannot be zero"))
 
 		self.total_score = total
 
 	def on_submit(self):
-		frappe.db.set(self, 'status', 'Submitted')
+		frappe.db.set(self, "status", "Submitted")
 
 	def on_cancel(self):
-		frappe.db.set(self, 'status', 'Cancelled')
+		frappe.db.set(self, "status", "Cancelled")
+
 
 @frappe.whitelist()
 def fetch_appraisal_template(source_name, target_doc=None):
-	target_doc = get_mapped_doc("Appraisal Template", source_name, {
-		"Appraisal Template": {
-			"doctype": "Appraisal",
+	target_doc = get_mapped_doc(
+		"Appraisal Template",
+		source_name,
+		{
+			"Appraisal Template": {
+				"doctype": "Appraisal",
+			},
+			"Appraisal Template Goal": {
+				"doctype": "Appraisal Goal",
+			},
 		},
-		"Appraisal Template Goal": {
-			"doctype": "Appraisal Goal",
-		}
-	}, target_doc)
+		target_doc,
+	)
 
 	return target_doc
diff --git a/erpnext/hr/doctype/appraisal/test_appraisal.py b/erpnext/hr/doctype/appraisal/test_appraisal.py
index 90c30ef..13a39f3 100644
--- a/erpnext/hr/doctype/appraisal/test_appraisal.py
+++ b/erpnext/hr/doctype/appraisal/test_appraisal.py
@@ -5,5 +5,6 @@
 
 # test_records = frappe.get_test_records('Appraisal')
 
+
 class TestAppraisal(unittest.TestCase):
 	pass
diff --git a/erpnext/hr/doctype/appraisal_template/appraisal_template_dashboard.py b/erpnext/hr/doctype/appraisal_template/appraisal_template_dashboard.py
index 116a3f9..476de4f 100644
--- a/erpnext/hr/doctype/appraisal_template/appraisal_template_dashboard.py
+++ b/erpnext/hr/doctype/appraisal_template/appraisal_template_dashboard.py
@@ -1,9 +1,7 @@
 def get_data():
-     return {
-        'fieldname': 'kra_template',
-        'transactions': [
-            {
-                'items': ['Appraisal']
-            },
-        ],
-    }
+	return {
+		"fieldname": "kra_template",
+		"transactions": [
+			{"items": ["Appraisal"]},
+		],
+	}
diff --git a/erpnext/hr/doctype/appraisal_template/test_appraisal_template.py b/erpnext/hr/doctype/appraisal_template/test_appraisal_template.py
index d0e81a7..560e992 100644
--- a/erpnext/hr/doctype/appraisal_template/test_appraisal_template.py
+++ b/erpnext/hr/doctype/appraisal_template/test_appraisal_template.py
@@ -5,5 +5,6 @@
 
 # test_records = frappe.get_test_records('Appraisal Template')
 
+
 class TestAppraisalTemplate(unittest.TestCase):
 	pass
diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py
index b1eaaf8..f3cae80 100644
--- a/erpnext/hr/doctype/attendance/attendance.py
+++ b/erpnext/hr/doctype/attendance/attendance.py
@@ -5,81 +5,222 @@
 import frappe
 from frappe import _
 from frappe.model.document import Document
-from frappe.utils import cint, cstr, formatdate, get_datetime, getdate, nowdate
+from frappe.query_builder import Criterion
+from frappe.utils import cint, cstr, formatdate, get_datetime, get_link_to_form, getdate, nowdate
 
+from erpnext.hr.doctype.shift_assignment.shift_assignment import has_overlapping_timings
 from erpnext.hr.utils import get_holiday_dates_for_employee, validate_active_employee
 
 
+class DuplicateAttendanceError(frappe.ValidationError):
+	pass
+
+
+class OverlappingShiftAttendanceError(frappe.ValidationError):
+	pass
+
+
 class Attendance(Document):
 	def validate(self):
 		from erpnext.controllers.status_updater import validate_status
+
 		validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day", "Work From Home"])
 		validate_active_employee(self.employee)
 		self.validate_attendance_date()
 		self.validate_duplicate_record()
+		self.validate_overlapping_shift_attendance()
 		self.validate_employee_status()
 		self.check_leave_record()
 
+	def on_cancel(self):
+		self.unlink_attendance_from_checkins()
+
 	def validate_attendance_date(self):
 		date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining")
 
 		# leaves can be marked for future dates
-		if self.status != 'On Leave' and not self.leave_application and getdate(self.attendance_date) > getdate(nowdate()):
+		if (
+			self.status != "On Leave"
+			and not self.leave_application
+			and getdate(self.attendance_date) > getdate(nowdate())
+		):
 			frappe.throw(_("Attendance can not be marked for future dates"))
 		elif date_of_joining and getdate(self.attendance_date) < getdate(date_of_joining):
 			frappe.throw(_("Attendance date can not be less than employee's joining date"))
 
 	def validate_duplicate_record(self):
-		res = frappe.db.sql("""
-			select name from `tabAttendance`
-			where employee = %s
-				and attendance_date = %s
-				and name != %s
-				and docstatus != 2
-		""", (self.employee, getdate(self.attendance_date), self.name))
-		if res:
-			frappe.throw(_("Attendance for employee {0} is already marked for the date {1}").format(
-				frappe.bold(self.employee), frappe.bold(self.attendance_date)))
+		duplicate = get_duplicate_attendance_record(
+			self.employee, self.attendance_date, self.shift, self.name
+		)
+
+		if duplicate:
+			frappe.throw(
+				_("Attendance for employee {0} is already marked for the date {1}: {2}").format(
+					frappe.bold(self.employee),
+					frappe.bold(self.attendance_date),
+					get_link_to_form("Attendance", duplicate[0].name),
+				),
+				title=_("Duplicate Attendance"),
+				exc=DuplicateAttendanceError,
+			)
+
+	def validate_overlapping_shift_attendance(self):
+		attendance = get_overlapping_shift_attendance(
+			self.employee, self.attendance_date, self.shift, self.name
+		)
+
+		if attendance:
+			frappe.throw(
+				_("Attendance for employee {0} is already marked for an overlapping shift {1}: {2}").format(
+					frappe.bold(self.employee),
+					frappe.bold(attendance.shift),
+					get_link_to_form("Attendance", attendance.name),
+				),
+				title=_("Overlapping Shift Attendance"),
+				exc=OverlappingShiftAttendanceError,
+			)
 
 	def validate_employee_status(self):
 		if frappe.db.get_value("Employee", self.employee, "status") == "Inactive":
 			frappe.throw(_("Cannot mark attendance for an Inactive employee {0}").format(self.employee))
 
 	def check_leave_record(self):
-		leave_record = frappe.db.sql("""
+		leave_record = frappe.db.sql(
+			"""
 			select leave_type, half_day, half_day_date
 			from `tabLeave Application`
 			where employee = %s
 				and %s between from_date and to_date
 				and status = 'Approved'
 				and docstatus = 1
-		""", (self.employee, self.attendance_date), as_dict=True)
+		""",
+			(self.employee, self.attendance_date),
+			as_dict=True,
+		)
 		if leave_record:
 			for d in leave_record:
 				self.leave_type = d.leave_type
 				if d.half_day_date == getdate(self.attendance_date):
-					self.status = 'Half Day'
-					frappe.msgprint(_("Employee {0} on Half day on {1}")
-						.format(self.employee, formatdate(self.attendance_date)))
+					self.status = "Half Day"
+					frappe.msgprint(
+						_("Employee {0} on Half day on {1}").format(self.employee, formatdate(self.attendance_date))
+					)
 				else:
-					self.status = 'On Leave'
-					frappe.msgprint(_("Employee {0} is on Leave on {1}")
-						.format(self.employee, formatdate(self.attendance_date)))
+					self.status = "On Leave"
+					frappe.msgprint(
+						_("Employee {0} is on Leave on {1}").format(self.employee, formatdate(self.attendance_date))
+					)
 
 		if self.status in ("On Leave", "Half Day"):
 			if not leave_record:
-				frappe.msgprint(_("No leave record found for employee {0} on {1}")
-					.format(self.employee, formatdate(self.attendance_date)), alert=1)
+				frappe.msgprint(
+					_("No leave record found for employee {0} on {1}").format(
+						self.employee, formatdate(self.attendance_date)
+					),
+					alert=1,
+				)
 		elif self.leave_type:
 			self.leave_type = None
 			self.leave_application = None
 
 	def validate_employee(self):
-		emp = frappe.db.sql("select name from `tabEmployee` where name = %s and status = 'Active'",
-		 	self.employee)
+		emp = frappe.db.sql(
+			"select name from `tabEmployee` where name = %s and status = 'Active'", self.employee
+		)
 		if not emp:
 			frappe.throw(_("Employee {0} is not active or does not exist").format(self.employee))
 
+	def unlink_attendance_from_checkins(self):
+		EmployeeCheckin = frappe.qb.DocType("Employee Checkin")
+		linked_logs = (
+			frappe.qb.from_(EmployeeCheckin)
+			.select(EmployeeCheckin.name)
+			.where(EmployeeCheckin.attendance == self.name)
+			.for_update()
+			.run(as_dict=True)
+		)
+
+		if linked_logs:
+			(
+				frappe.qb.update(EmployeeCheckin)
+				.set("attendance", "")
+				.where(EmployeeCheckin.attendance == self.name)
+			).run()
+
+			frappe.msgprint(
+				msg=_("Unlinked Attendance record from Employee Checkins: {}").format(
+					", ".join(get_link_to_form("Employee Checkin", log.name) for log in linked_logs)
+				),
+				title=_("Unlinked logs"),
+				indicator="blue",
+				is_minimizable=True,
+				wide=True,
+			)
+
+
+def get_duplicate_attendance_record(employee, attendance_date, shift, name=None):
+	attendance = frappe.qb.DocType("Attendance")
+	query = (
+		frappe.qb.from_(attendance)
+		.select(attendance.name)
+		.where((attendance.employee == employee) & (attendance.docstatus < 2))
+	)
+
+	if shift:
+		query = query.where(
+			Criterion.any(
+				[
+					Criterion.all(
+						[
+							((attendance.shift.isnull()) | (attendance.shift == "")),
+							(attendance.attendance_date == attendance_date),
+						]
+					),
+					Criterion.all(
+						[
+							((attendance.shift.isnotnull()) | (attendance.shift != "")),
+							(attendance.attendance_date == attendance_date),
+							(attendance.shift == shift),
+						]
+					),
+				]
+			)
+		)
+	else:
+		query = query.where((attendance.attendance_date == attendance_date))
+
+	if name:
+		query = query.where(attendance.name != name)
+
+	return query.run(as_dict=True)
+
+
+def get_overlapping_shift_attendance(employee, attendance_date, shift, name=None):
+	if not shift:
+		return {}
+
+	attendance = frappe.qb.DocType("Attendance")
+	query = (
+		frappe.qb.from_(attendance)
+		.select(attendance.name, attendance.shift)
+		.where(
+			(attendance.employee == employee)
+			& (attendance.docstatus < 2)
+			& (attendance.attendance_date == attendance_date)
+			& (attendance.shift != shift)
+		)
+	)
+
+	if name:
+		query = query.where(attendance.name != name)
+
+	overlapping_attendance = query.run(as_dict=True)
+
+	if overlapping_attendance and has_overlapping_timings(shift, overlapping_attendance[0].shift):
+		return overlapping_attendance[0]
+	return {}
+
+
 @frappe.whitelist()
 def get_events(start, end, filters=None):
 	events = []
@@ -90,10 +231,12 @@
 		return events
 
 	from frappe.desk.reportview import get_filters_cond
+
 	conditions = get_filters_cond("Attendance", filters, [])
 	add_attendance(events, start, end, conditions=conditions)
 	return events
 
+
 def add_attendance(events, start, end, conditions=None):
 	query = """select name, attendance_date, status
 		from `tabAttendance` where
@@ -102,93 +245,132 @@
 	if conditions:
 		query += conditions
 
-	for d in frappe.db.sql(query, {"from_date":start, "to_date":end}, as_dict=True):
+	for d in frappe.db.sql(query, {"from_date": start, "to_date": end}, as_dict=True):
 		e = {
 			"name": d.name,
 			"doctype": "Attendance",
 			"start": d.attendance_date,
 			"end": d.attendance_date,
 			"title": cstr(d.status),
-			"docstatus": d.docstatus
+			"docstatus": d.docstatus,
 		}
 		if e not in events:
 			events.append(e)
 
-def mark_attendance(employee, attendance_date, status, shift=None, leave_type=None, ignore_validate=False):
-	if not frappe.db.exists('Attendance', {'employee':employee, 'attendance_date':attendance_date, 'docstatus':('!=', '2')}):
-		company = frappe.db.get_value('Employee', employee, 'company')
-		attendance = frappe.get_doc({
-			'doctype': 'Attendance',
-			'employee': employee,
-			'attendance_date': attendance_date,
-			'status': status,
-			'company': company,
-			'shift': shift,
-			'leave_type': leave_type
-		})
-		attendance.flags.ignore_validate = ignore_validate
-		attendance.insert()
-		attendance.submit()
-		return attendance.name
+
+def mark_attendance(
+	employee,
+	attendance_date,
+	status,
+	shift=None,
+	leave_type=None,
+	ignore_validate=False,
+	late_entry=False,
+	early_exit=False,
+):
+	if get_duplicate_attendance_record(employee, attendance_date, shift):
+		return
+
+	if get_overlapping_shift_attendance(employee, attendance_date, shift):
+		return
+
+	company = frappe.db.get_value("Employee", employee, "company")
+	attendance = frappe.get_doc(
+		{
+			"doctype": "Attendance",
+			"employee": employee,
+			"attendance_date": attendance_date,
+			"status": status,
+			"company": company,
+			"shift": shift,
+			"leave_type": leave_type,
+			"late_entry": late_entry,
+			"early_exit": early_exit,
+		}
+	)
+	attendance.flags.ignore_validate = ignore_validate
+	attendance.insert()
+	attendance.submit()
+	return attendance.name
+
 
 @frappe.whitelist()
 def mark_bulk_attendance(data):
 	import json
+
 	if isinstance(data, str):
 		data = json.loads(data)
 	data = frappe._dict(data)
-	company = frappe.get_value('Employee', data.employee, 'company')
+	company = frappe.get_value("Employee", data.employee, "company")
 	if not data.unmarked_days:
 		frappe.throw(_("Please select a date."))
 		return
 
 	for date in data.unmarked_days:
 		doc_dict = {
-			'doctype': 'Attendance',
-			'employee': data.employee,
-			'attendance_date': get_datetime(date),
-			'status': data.status,
-			'company': company,
+			"doctype": "Attendance",
+			"employee": data.employee,
+			"attendance_date": get_datetime(date),
+			"status": data.status,
+			"company": company,
 		}
 		attendance = frappe.get_doc(doc_dict).insert()
 		attendance.submit()
 
 
 def get_month_map():
-	return frappe._dict({
-		"January": 1,
-		"February": 2,
-		"March": 3,
-		"April": 4,
-		"May": 5,
-		"June": 6,
-		"July": 7,
-		"August": 8,
-		"September": 9,
-		"October": 10,
-		"November": 11,
-		"December": 12
-		})
+	return frappe._dict(
+		{
+			"January": 1,
+			"February": 2,
+			"March": 3,
+			"April": 4,
+			"May": 5,
+			"June": 6,
+			"July": 7,
+			"August": 8,
+			"September": 9,
+			"October": 10,
+			"November": 11,
+			"December": 12,
+		}
+	)
+
 
 @frappe.whitelist()
 def get_unmarked_days(employee, month, exclude_holidays=0):
 	import calendar
-	month_map = get_month_map()
 
+	month_map = get_month_map()
 	today = get_datetime()
 
-	dates_of_month = ['{}-{}-{}'.format(today.year, month_map[month], r) for r in range(1, calendar.monthrange(today.year, month_map[month])[1] + 1)]
+	joining_date, relieving_date = frappe.get_cached_value(
+		"Employee", employee, ["date_of_joining", "relieving_date"]
+	)
+	start_day = 1
+	end_day = calendar.monthrange(today.year, month_map[month])[1] + 1
 
-	length = len(dates_of_month)
-	month_start, month_end = dates_of_month[0], dates_of_month[length-1]
+	if joining_date and joining_date.month == month_map[month]:
+		start_day = joining_date.day
 
+	if relieving_date and relieving_date.month == month_map[month]:
+		end_day = relieving_date.day + 1
 
-	records = frappe.get_all("Attendance", fields = ['attendance_date', 'employee'] , filters = [
-		["attendance_date", ">=", month_start],
-		["attendance_date", "<=", month_end],
-		["employee", "=", employee],
-		["docstatus", "!=", 2]
-	])
+	dates_of_month = [
+		"{}-{}-{}".format(today.year, month_map[month], r) for r in range(start_day, end_day)
+	]
+	month_start, month_end = dates_of_month[0], dates_of_month[-1]
+
+	records = frappe.get_all(
+		"Attendance",
+		fields=["attendance_date", "employee"],
+		filters=[
+			["attendance_date", ">=", month_start],
+			["attendance_date", "<=", month_end],
+			["employee", "=", employee],
+			["docstatus", "!=", 2],
+		],
+	)
 
 	marked_days = [get_datetime(record.attendance_date) for record in records]
 	if cint(exclude_holidays):
@@ -200,7 +382,7 @@
 
 	for date in dates_of_month:
 		date_time = get_datetime(date)
-		if today.day == date_time.day and today.month == date_time.month:
+		if today.day <= date_time.day and today.month <= date_time.month:
 			break
 		if date_time not in marked_days:
 			unmarked_days.append(date)
diff --git a/erpnext/hr/doctype/attendance/attendance_dashboard.py b/erpnext/hr/doctype/attendance/attendance_dashboard.py
index 4bb36a0..abed78f 100644
--- a/erpnext/hr/doctype/attendance/attendance_dashboard.py
+++ b/erpnext/hr/doctype/attendance/attendance_dashboard.py
@@ -1,10 +1,2 @@
 def get_data():
-	return {
-		'fieldname': 'attendance',
-		'transactions': [
-			{
-				'label': '',
-				'items': ['Employee Checkin']
-			}
-		]
-	}
+	return {"fieldname": "attendance", "transactions": [{"label": "", "items": ["Employee Checkin"]}]}
diff --git a/erpnext/hr/doctype/attendance/test_attendance.py b/erpnext/hr/doctype/attendance/test_attendance.py
index a770d70..c85ec65 100644
--- a/erpnext/hr/doctype/attendance/test_attendance.py
+++ b/erpnext/hr/doctype/attendance/test_attendance.py
@@ -1,20 +1,232 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
 # See license.txt
 
-import unittest
-
 import frappe
-from frappe.utils import nowdate
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import (
+	add_days,
+	add_months,
+	get_last_day,
+	get_year_ending,
+	get_year_start,
+	getdate,
+	nowdate,
+)
 
-test_records = frappe.get_test_records('Attendance')
+from erpnext.hr.doctype.attendance.attendance import (
+	DuplicateAttendanceError,
+	OverlappingShiftAttendanceError,
+	get_month_map,
+	get_unmarked_days,
+	mark_attendance,
+)
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.leave_application.test_leave_application import get_first_sunday
 
-class TestAttendance(unittest.TestCase):
+test_records = frappe.get_test_records("Attendance")
+
+
+class TestAttendance(FrappeTestCase):
+	def setUp(self):
+		from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list
+
+		from_date = get_year_start(getdate())
+		to_date = get_year_ending(getdate())
+		self.holiday_list = make_holiday_list(from_date=from_date, to_date=to_date)
+		frappe.db.delete("Attendance")
+
+	def test_duplicate_attendance(self):
+		employee = make_employee("test_duplicate_attendance@example.com", company="_Test Company")
+		date = nowdate()
+
+		mark_attendance(employee, date, "Present")
+		attendance = frappe.get_doc(
+			{
+				"doctype": "Attendance",
+				"employee": employee,
+				"attendance_date": date,
+				"status": "Absent",
+				"company": "_Test Company",
+			}
+		)
+
+		self.assertRaises(DuplicateAttendanceError, attendance.insert)
+
+	def test_duplicate_attendance_with_shift(self):
+		from erpnext.hr.doctype.shift_type.test_shift_type import setup_shift_type
+
+		employee = make_employee("test_duplicate_attendance@example.com", company="_Test Company")
+		date = nowdate()
+
+		shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00")
+		mark_attendance(employee, date, "Present", shift=shift_1.name)
+
+		# attendance record with shift
+		attendance = frappe.get_doc(
+			{
+				"doctype": "Attendance",
+				"employee": employee,
+				"attendance_date": date,
+				"status": "Absent",
+				"company": "_Test Company",
+				"shift": shift_1.name,
+			}
+		)
+
+		self.assertRaises(DuplicateAttendanceError, attendance.insert)
+
+		# attendance record without any shift
+		attendance = frappe.get_doc(
+			{
+				"doctype": "Attendance",
+				"employee": employee,
+				"attendance_date": date,
+				"status": "Absent",
+				"company": "_Test Company",
+			}
+		)
+
+		self.assertRaises(DuplicateAttendanceError, attendance.insert)
+
+	def test_overlapping_shift_attendance_validation(self):
+		from erpnext.hr.doctype.shift_type.test_shift_type import setup_shift_type
+
+		employee = make_employee("test_overlap_attendance@example.com", company="_Test Company")
+		date = nowdate()
+
+		shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00")
+		shift_2 = setup_shift_type(shift_type="Shift 2", start_time="09:30:00", end_time="11:00:00")
+
+		mark_attendance(employee, date, "Present", shift=shift_1.name)
+
+		# attendance record with overlapping shift
+		attendance = frappe.get_doc(
+			{
+				"doctype": "Attendance",
+				"employee": employee,
+				"attendance_date": date,
+				"status": "Absent",
+				"company": "_Test Company",
+				"shift": shift_2.name,
+			}
+		)
+
+		self.assertRaises(OverlappingShiftAttendanceError, attendance.insert)
+
+	def test_allow_attendance_with_different_shifts(self):
+		# allows attendance with 2 different non-overlapping shifts
+		from erpnext.hr.doctype.shift_type.test_shift_type import setup_shift_type
+
+		employee = make_employee("test_duplicate_attendance@example.com", company="_Test Company")
+		date = nowdate()
+
+		shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00")
+		shift_2 = setup_shift_type(shift_type="Shift 2", start_time="11:00:00", end_time="12:00:00")
+
+		mark_attendance(employee, date, "Present", shift_1.name)
+		frappe.get_doc(
+			{
+				"doctype": "Attendance",
+				"employee": employee,
+				"attendance_date": date,
+				"status": "Absent",
+				"company": "_Test Company",
+				"shift": shift_2.name,
+			}
+		).insert()
+
 	def test_mark_absent(self):
-		from erpnext.hr.doctype.employee.test_employee import make_employee
 		employee = make_employee("test_mark_absent@example.com")
 		date = nowdate()
-		frappe.db.delete('Attendance', {'employee':employee, 'attendance_date':date})
-		from erpnext.hr.doctype.attendance.attendance import mark_attendance
-		attendance = mark_attendance(employee, date, 'Absent')
-		fetch_attendance = frappe.get_value('Attendance', {'employee':employee, 'attendance_date':date, 'status':'Absent'})
+
+		attendance = mark_attendance(employee, date, "Absent")
+		fetch_attendance = frappe.get_value(
+			"Attendance", {"employee": employee, "attendance_date": date, "status": "Absent"}
+		)
 		self.assertEqual(attendance, fetch_attendance)
+
+	def test_unmarked_days(self):
+		first_sunday = get_first_sunday(
+			self.holiday_list, for_date=get_last_day(add_months(getdate(), -1))
+		)
+		attendance_date = add_days(first_sunday, 1)
+
+		employee = make_employee(
+			"test_unmarked_days@example.com", date_of_joining=add_days(attendance_date, -1)
+		)
+		frappe.db.set_value("Employee", employee, "holiday_list", self.holiday_list)
+
+		mark_attendance(employee, attendance_date, "Present")
+		month_name = get_month_name(attendance_date)
+
+		unmarked_days = get_unmarked_days(employee, month_name)
+		unmarked_days = [getdate(date) for date in unmarked_days]
+
+		# attendance already marked for the day
+		self.assertNotIn(attendance_date, unmarked_days)
+		# attendance unmarked
+		self.assertIn(getdate(add_days(attendance_date, 1)), unmarked_days)
+		# holiday considered in unmarked days
+		self.assertIn(first_sunday, unmarked_days)
+
+	def test_unmarked_days_excluding_holidays(self):
+		first_sunday = get_first_sunday(
+			self.holiday_list, for_date=get_last_day(add_months(getdate(), -1))
+		)
+		attendance_date = add_days(first_sunday, 1)
+
+		employee = make_employee(
+			"test_unmarked_days@example.com", date_of_joining=add_days(attendance_date, -1)
+		)
+		frappe.db.set_value("Employee", employee, "holiday_list", self.holiday_list)
+
+		mark_attendance(employee, attendance_date, "Present")
+		month_name = get_month_name(attendance_date)
+
+		unmarked_days = get_unmarked_days(employee, month_name, exclude_holidays=True)
+		unmarked_days = [getdate(date) for date in unmarked_days]
+
+		# attendance already marked for the day
+		self.assertNotIn(attendance_date, unmarked_days)
+		# attendance unmarked
+		self.assertIn(getdate(add_days(attendance_date, 1)), unmarked_days)
+		# holidays not considered in unmarked days
+		self.assertNotIn(first_sunday, unmarked_days)
+
+	def test_unmarked_days_as_per_joining_and_relieving_dates(self):
+		first_sunday = get_first_sunday(
+			self.holiday_list, for_date=get_last_day(add_months(getdate(), -1))
+		)
+		date = add_days(first_sunday, 1)
+
+		doj = add_days(date, 1)
+		relieving_date = add_days(date, 5)
+		employee = make_employee(
+			"test_unmarked_days_as_per_doj@example.com", date_of_joining=doj, relieving_date=relieving_date
+		)
+
+		frappe.db.set_value("Employee", employee, "holiday_list", self.holiday_list)
+
+		attendance_date = add_days(date, 2)
+		mark_attendance(employee, attendance_date, "Present")
+		month_name = get_month_name(attendance_date)
+
+		unmarked_days = get_unmarked_days(employee, month_name)
+		unmarked_days = [getdate(date) for date in unmarked_days]
+
+		# attendance already marked for the day
+		self.assertNotIn(attendance_date, unmarked_days)
+		# date before doj not in unmarked days
+		self.assertNotIn(add_days(doj, -1), unmarked_days)
+		# date after relieving not in unmarked days
+		self.assertNotIn(add_days(relieving_date, 1), unmarked_days)
+
+	def tearDown(self):
+		frappe.db.rollback()
+
+
+def get_month_name(date):
+	month_number = date.month
+	for month, number in get_month_map().items():
+		if number == month_number:
+			return month
diff --git a/erpnext/hr/doctype/attendance_request/attendance_request.py b/erpnext/hr/doctype/attendance_request/attendance_request.py
index 8fbe7c7..78652f6 100644
--- a/erpnext/hr/doctype/attendance_request/attendance_request.py
+++ b/erpnext/hr/doctype/attendance_request/attendance_request.py
@@ -16,17 +16,19 @@
 		validate_active_employee(self.employee)
 		validate_dates(self, self.from_date, self.to_date)
 		if self.half_day:
-			if not getdate(self.from_date)<=getdate(self.half_day_date)<=getdate(self.to_date):
+			if not getdate(self.from_date) <= getdate(self.half_day_date) <= getdate(self.to_date):
 				frappe.throw(_("Half day date should be in between from date and to date"))
 
 	def on_submit(self):
 		self.create_attendance()
 
 	def on_cancel(self):
-		attendance_list = frappe.get_list("Attendance", {'employee': self.employee, 'attendance_request': self.name})
+		attendance_list = frappe.get_list(
+			"Attendance", {"employee": self.employee, "attendance_request": self.name}
+		)
 		if attendance_list:
 			for attendance in attendance_list:
-				attendance_obj = frappe.get_doc("Attendance", attendance['name'])
+				attendance_obj = frappe.get_doc("Attendance", attendance["name"])
 				attendance_obj.cancel()
 
 	def create_attendance(self):
@@ -53,15 +55,24 @@
 	def validate_if_attendance_not_applicable(self, attendance_date):
 		# Check if attendance_date is a Holiday
 		if is_holiday(self.employee, attendance_date):
-			frappe.msgprint(_("Attendance not submitted for {0} as it is a Holiday.").format(attendance_date), alert=1)
+			frappe.msgprint(
+				_("Attendance not submitted for {0} as it is a Holiday.").format(attendance_date), alert=1
+			)
 			return True
 
 		# Check if employee on Leave
-		leave_record = frappe.db.sql("""select half_day from `tabLeave Application`
+		leave_record = frappe.db.sql(
+			"""select half_day from `tabLeave Application`
 			where employee = %s and %s between from_date and to_date
-			and docstatus = 1""", (self.employee, attendance_date), as_dict=True)
+			and docstatus = 1""",
+			(self.employee, attendance_date),
+			as_dict=True,
+		)
 		if leave_record:
-			frappe.msgprint(_("Attendance not submitted for {0} as {1} on leave.").format(attendance_date, self.employee), alert=1)
+			frappe.msgprint(
+				_("Attendance not submitted for {0} as {1} on leave.").format(attendance_date, self.employee),
+				alert=1,
+			)
 			return True
 
 		return False
diff --git a/erpnext/hr/doctype/attendance_request/attendance_request_dashboard.py b/erpnext/hr/doctype/attendance_request/attendance_request_dashboard.py
index 9197057..059725c 100644
--- a/erpnext/hr/doctype/attendance_request/attendance_request_dashboard.py
+++ b/erpnext/hr/doctype/attendance_request/attendance_request_dashboard.py
@@ -1,9 +1,2 @@
 def get_data():
-	return {
-		'fieldname':  'attendance_request',
-		'transactions': [
-			{
-				'items': ['Attendance']
-			}
-		]
-	}
+	return {"fieldname": "attendance_request", "transactions": [{"items": ["Attendance"]}]}
diff --git a/erpnext/hr/doctype/attendance_request/test_attendance_request.py b/erpnext/hr/doctype/attendance_request/test_attendance_request.py
index 3f0442c..ee436f5 100644
--- a/erpnext/hr/doctype/attendance_request/test_attendance_request.py
+++ b/erpnext/hr/doctype/attendance_request/test_attendance_request.py
@@ -9,6 +9,7 @@
 
 test_dependencies = ["Employee"]
 
+
 class TestAttendanceRequest(unittest.TestCase):
 	def setUp(self):
 		for doctype in ["Attendance Request", "Attendance"]:
@@ -34,10 +35,10 @@
 			"Attendance",
 			filters={
 				"attendance_request": attendance_request.name,
-				"attendance_date": date(date.today().year, 1, 1)
+				"attendance_date": date(date.today().year, 1, 1),
 			},
 			fieldname=["status", "docstatus"],
-			as_dict=True
+			as_dict=True,
 		)
 		self.assertEqual(attendance.status, "Present")
 		self.assertEqual(attendance.docstatus, 1)
@@ -51,9 +52,9 @@
 			"Attendance",
 			filters={
 				"attendance_request": attendance_request.name,
-				"attendance_date": date(date.today().year, 1, 1)
+				"attendance_date": date(date.today().year, 1, 1),
 			},
-			fieldname="docstatus"
+			fieldname="docstatus",
 		)
 		self.assertEqual(attendance_docstatus, 2)
 
@@ -74,11 +75,11 @@
 			"Attendance",
 			filters={
 				"attendance_request": attendance_request.name,
-				"attendance_date": date(date.today().year, 1, 1)
+				"attendance_date": date(date.today().year, 1, 1),
 			},
-			fieldname="status"
+			fieldname="status",
 		)
-		self.assertEqual(attendance_status, 'Work From Home')
+		self.assertEqual(attendance_status, "Work From Home")
 
 		attendance_request.cancel()
 
@@ -88,11 +89,12 @@
 			"Attendance",
 			filters={
 				"attendance_request": attendance_request.name,
-				"attendance_date": date(date.today().year, 1, 1)
+				"attendance_date": date(date.today().year, 1, 1),
 			},
-			fieldname="docstatus"
+			fieldname="docstatus",
 		)
 		self.assertEqual(attendance_docstatus, 2)
 
+
 def get_employee():
 	return frappe.get_doc("Employee", "_T-Employee-00001")
diff --git a/erpnext/hr/doctype/branch/test_branch.py b/erpnext/hr/doctype/branch/test_branch.py
index e84c6e4..c14d4aa 100644
--- a/erpnext/hr/doctype/branch/test_branch.py
+++ b/erpnext/hr/doctype/branch/test_branch.py
@@ -3,4 +3,4 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Branch')
+test_records = frappe.get_test_records("Branch")
diff --git a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py
index 7d60515..d233226 100644
--- a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py
+++ b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py
@@ -18,14 +18,15 @@
 
 
 class CompensatoryLeaveRequest(Document):
-
 	def validate(self):
 		validate_active_employee(self.employee)
 		validate_dates(self, self.work_from_date, self.work_end_date)
 		if self.half_day:
 			if not self.half_day_date:
 				frappe.throw(_("Half Day Date is mandatory"))
-			if not getdate(self.work_from_date)<=getdate(self.half_day_date)<=getdate(self.work_end_date):
+			if (
+				not getdate(self.work_from_date) <= getdate(self.half_day_date) <= getdate(self.work_end_date)
+			):
 				frappe.throw(_("Half Day Date should be in between Work From Date and Work End Date"))
 		validate_overlap(self, self.work_from_date, self.work_end_date)
 		self.validate_holidays()
@@ -34,13 +35,16 @@
 			frappe.throw(_("Leave Type is madatory"))
 
 	def validate_attendance(self):
-		attendance = frappe.get_all('Attendance',
+		attendance = frappe.get_all(
+			"Attendance",
 			filters={
-				'attendance_date': ['between', (self.work_from_date, self.work_end_date)],
-				'status': 'Present',
-				'docstatus': 1,
-				'employee': self.employee
-			}, fields=['attendance_date', 'status'])
+				"attendance_date": ["between", (self.work_from_date, self.work_end_date)],
+				"status": "Present",
+				"docstatus": 1,
+				"employee": self.employee,
+			},
+			fields=["attendance_date", "status"],
+		)
 
 		if len(attendance) < date_diff(self.work_end_date, self.work_from_date) + 1:
 			frappe.throw(_("You are not present all day(s) between compensatory leave request days"))
@@ -49,7 +53,9 @@
 		holidays = get_holiday_dates_for_employee(self.employee, self.work_from_date, self.work_end_date)
 		if len(holidays) < date_diff(self.work_end_date, self.work_from_date) + 1:
 			if date_diff(self.work_end_date, self.work_from_date):
-				msg = _("The days between {0} to {1} are not valid holidays.").format(frappe.bold(format_date(self.work_from_date)), frappe.bold(format_date(self.work_end_date)))
+				msg = _("The days between {0} to {1} are not valid holidays.").format(
+					frappe.bold(format_date(self.work_from_date)), frappe.bold(format_date(self.work_end_date))
+				)
 			else:
 				msg = _("{0} is not a holiday.").format(frappe.bold(format_date(self.work_from_date)))
 
@@ -70,13 +76,19 @@
 				leave_allocation.db_set("total_leaves_allocated", leave_allocation.total_leaves_allocated)
 
 				# generate additional ledger entry for the new compensatory leaves off
-				create_additional_leave_ledger_entry(leave_allocation, date_difference, add_days(self.work_end_date, 1))
+				create_additional_leave_ledger_entry(
+					leave_allocation, date_difference, add_days(self.work_end_date, 1)
+				)
 
 			else:
 				leave_allocation = self.create_leave_allocation(leave_period, date_difference)
 			self.db_set("leave_allocation", leave_allocation.name)
 		else:
-			frappe.throw(_("There is no leave period in between {0} and {1}").format(format_date(self.work_from_date), format_date(self.work_end_date)))
+			frappe.throw(
+				_("There is no leave period in between {0} and {1}").format(
+					format_date(self.work_from_date), format_date(self.work_end_date)
+				)
+			)
 
 	def on_cancel(self):
 		if self.leave_allocation:
@@ -93,10 +105,13 @@
 				leave_allocation.db_set("total_leaves_allocated", leave_allocation.total_leaves_allocated)
 
 				# create reverse entry on cancelation
-				create_additional_leave_ledger_entry(leave_allocation, date_difference * -1, add_days(self.work_end_date, 1))
+				create_additional_leave_ledger_entry(
+					leave_allocation, date_difference * -1, add_days(self.work_end_date, 1)
+				)
 
 	def get_existing_allocation_for_period(self, leave_period):
-		leave_allocation = frappe.db.sql("""
+		leave_allocation = frappe.db.sql(
+			"""
 			select name
 			from `tabLeave Allocation`
 			where employee=%(employee)s and leave_type=%(leave_type)s
@@ -104,12 +119,15 @@
 				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))
-		""", {
-			"from_date": leave_period[0].from_date,
-			"to_date": leave_period[0].to_date,
-			"employee": self.employee,
-			"leave_type": self.leave_type
-		}, as_dict=1)
+		""",
+			{
+				"from_date": leave_period[0].from_date,
+				"to_date": leave_period[0].to_date,
+				"employee": self.employee,
+				"leave_type": self.leave_type,
+			},
+			as_dict=1,
+		)
 
 		if leave_allocation:
 			return frappe.get_doc("Leave Allocation", leave_allocation[0].name)
@@ -118,18 +136,20 @@
 
 	def create_leave_allocation(self, leave_period, date_difference):
 		is_carry_forward = frappe.db.get_value("Leave Type", self.leave_type, "is_carry_forward")
-		allocation = frappe.get_doc(dict(
-			doctype="Leave Allocation",
-			employee=self.employee,
-			employee_name=self.employee_name,
-			leave_type=self.leave_type,
-			from_date=add_days(self.work_end_date, 1),
-			to_date=leave_period[0].to_date,
-			carry_forward=cint(is_carry_forward),
-			new_leaves_allocated=date_difference,
-			total_leaves_allocated=date_difference,
-			description=self.reason
-		))
+		allocation = frappe.get_doc(
+			dict(
+				doctype="Leave Allocation",
+				employee=self.employee,
+				employee_name=self.employee_name,
+				leave_type=self.leave_type,
+				from_date=add_days(self.work_end_date, 1),
+				to_date=leave_period[0].to_date,
+				carry_forward=cint(is_carry_forward),
+				new_leaves_allocated=date_difference,
+				total_leaves_allocated=date_difference,
+				description=self.reason,
+			)
+		)
 		allocation.insert(ignore_permissions=True)
 		allocation.submit()
 		return allocation
diff --git a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py b/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py
index 5e51879..7bbec29 100644
--- a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py
+++ b/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py
@@ -12,12 +12,17 @@
 
 test_dependencies = ["Employee"]
 
+
 class TestCompensatoryLeaveRequest(unittest.TestCase):
 	def setUp(self):
-		frappe.db.sql(''' delete from `tabCompensatory Leave Request`''')
-		frappe.db.sql(''' delete from `tabLeave Ledger Entry`''')
-		frappe.db.sql(''' delete from `tabLeave Allocation`''')
-		frappe.db.sql(''' delete from `tabAttendance` where attendance_date in {0} '''.format((today(), add_days(today(), -1)))) #nosec
+		frappe.db.sql(""" delete from `tabCompensatory Leave Request`""")
+		frappe.db.sql(""" delete from `tabLeave Ledger Entry`""")
+		frappe.db.sql(""" delete from `tabLeave Allocation`""")
+		frappe.db.sql(
+			""" delete from `tabAttendance` where attendance_date in {0} """.format(
+				(today(), add_days(today(), -1))
+			)
+		)  # nosec
 		create_leave_period(add_months(today(), -3), add_months(today(), 3), "_Test Company")
 		create_holiday_list()
 
@@ -26,7 +31,7 @@
 		employee.save()
 
 	def test_leave_balance_on_submit(self):
-		''' check creation of leave allocation on submission of compensatory leave request '''
+		"""check creation of leave allocation on submission of compensatory leave request"""
 		employee = get_employee()
 		mark_attendance(employee)
 		compensatory_leave_request = get_compensatory_leave_request(employee.name)
@@ -34,18 +39,27 @@
 		before = get_leave_balance_on(employee.name, compensatory_leave_request.leave_type, today())
 		compensatory_leave_request.submit()
 
-		self.assertEqual(get_leave_balance_on(employee.name, compensatory_leave_request.leave_type, add_days(today(), 1)), before + 1)
+		self.assertEqual(
+			get_leave_balance_on(
+				employee.name, compensatory_leave_request.leave_type, add_days(today(), 1)
+			),
+			before + 1,
+		)
 
 	def test_leave_allocation_update_on_submit(self):
 		employee = get_employee()
 		mark_attendance(employee, date=add_days(today(), -1))
-		compensatory_leave_request = get_compensatory_leave_request(employee.name, leave_date=add_days(today(), -1))
+		compensatory_leave_request = get_compensatory_leave_request(
+			employee.name, leave_date=add_days(today(), -1)
+		)
 		compensatory_leave_request.submit()
 
 		# leave allocation creation on submit
-		leaves_allocated = frappe.db.get_value('Leave Allocation', {
-				'name': compensatory_leave_request.leave_allocation
-			}, ['total_leaves_allocated'])
+		leaves_allocated = frappe.db.get_value(
+			"Leave Allocation",
+			{"name": compensatory_leave_request.leave_allocation},
+			["total_leaves_allocated"],
+		)
 		self.assertEqual(leaves_allocated, 1)
 
 		mark_attendance(employee)
@@ -53,20 +67,22 @@
 		compensatory_leave_request.submit()
 
 		# leave allocation updates on submission of second compensatory leave request
-		leaves_allocated = frappe.db.get_value('Leave Allocation', {
-				'name': compensatory_leave_request.leave_allocation
-			}, ['total_leaves_allocated'])
+		leaves_allocated = frappe.db.get_value(
+			"Leave Allocation",
+			{"name": compensatory_leave_request.leave_allocation},
+			["total_leaves_allocated"],
+		)
 		self.assertEqual(leaves_allocated, 2)
 
 	def test_creation_of_leave_ledger_entry_on_submit(self):
-		''' check creation of leave ledger entry on submission of leave request '''
+		"""check creation of leave ledger entry on submission of leave request"""
 		employee = get_employee()
 		mark_attendance(employee)
 		compensatory_leave_request = get_compensatory_leave_request(employee.name)
 		compensatory_leave_request.submit()
 
 		filters = dict(transaction_name=compensatory_leave_request.leave_allocation)
-		leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=filters)
+		leave_ledger_entry = frappe.get_all("Leave Ledger Entry", fields="*", filters=filters)
 
 		self.assertEqual(len(leave_ledger_entry), 1)
 		self.assertEqual(leave_ledger_entry[0].employee, compensatory_leave_request.employee)
@@ -75,60 +91,67 @@
 
 		# check reverse leave ledger entry on cancellation
 		compensatory_leave_request.cancel()
-		leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=filters, order_by = 'creation desc')
+		leave_ledger_entry = frappe.get_all(
+			"Leave Ledger Entry", fields="*", filters=filters, order_by="creation desc"
+		)
 
 		self.assertEqual(len(leave_ledger_entry), 2)
 		self.assertEqual(leave_ledger_entry[0].employee, compensatory_leave_request.employee)
 		self.assertEqual(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type)
 		self.assertEqual(leave_ledger_entry[0].leaves, -1)
 
+
 def get_compensatory_leave_request(employee, leave_date=today()):
-	prev_comp_leave_req = frappe.db.get_value('Compensatory Leave Request',
-		dict(leave_type='Compensatory Off',
+	prev_comp_leave_req = frappe.db.get_value(
+		"Compensatory Leave Request",
+		dict(
+			leave_type="Compensatory Off",
 			work_from_date=leave_date,
 			work_end_date=leave_date,
-			employee=employee), 'name')
-	if prev_comp_leave_req:
-		return frappe.get_doc('Compensatory Leave Request', prev_comp_leave_req)
-
-	return frappe.get_doc(dict(
-			doctype='Compensatory Leave Request',
 			employee=employee,
-			leave_type='Compensatory Off',
+		),
+		"name",
+	)
+	if prev_comp_leave_req:
+		return frappe.get_doc("Compensatory Leave Request", prev_comp_leave_req)
+
+	return frappe.get_doc(
+		dict(
+			doctype="Compensatory Leave Request",
+			employee=employee,
+			leave_type="Compensatory Off",
 			work_from_date=leave_date,
 			work_end_date=leave_date,
-			reason='test'
-		)).insert()
+			reason="test",
+		)
+	).insert()
 
-def mark_attendance(employee, date=today(), status='Present'):
-	if not frappe.db.exists(dict(doctype='Attendance', employee=employee.name, attendance_date=date, status='Present')):
-		attendance = frappe.get_doc({
-				"doctype": "Attendance",
-				"employee": employee.name,
-				"attendance_date": date,
-				"status": status
-		})
+
+def mark_attendance(employee, date=today(), status="Present"):
+	if not frappe.db.exists(
+		dict(doctype="Attendance", employee=employee.name, attendance_date=date, status="Present")
+	):
+		attendance = frappe.get_doc(
+			{"doctype": "Attendance", "employee": employee.name, "attendance_date": date, "status": status}
+		)
 		attendance.save()
 		attendance.submit()
 
+
 def create_holiday_list():
 	if frappe.db.exists("Holiday List", "_Test Compensatory Leave"):
 		return
 
-	holiday_list = frappe.get_doc({
-		"doctype": "Holiday List",
-		"from_date": add_months(today(), -3),
-		"to_date": add_months(today(), 3),
-		"holidays": [
-			{
-				"description": "Test Holiday",
-				"holiday_date": today()
-			},
-			{
-				"description": "Test Holiday 1",
-				"holiday_date": add_days(today(), -1)
-			}
-		],
-		"holiday_list_name": "_Test Compensatory Leave"
-	})
+	holiday_list = frappe.get_doc(
+		{
+			"doctype": "Holiday List",
+			"from_date": add_months(today(), -3),
+			"to_date": add_months(today(), 3),
+			"holidays": [
+				{"description": "Test Holiday", "holiday_date": today()},
+				{"description": "Test Holiday 1", "holiday_date": add_days(today(), -1)},
+			],
+			"holiday_list_name": "_Test Compensatory Leave",
+		}
+	)
 	holiday_list.save()
diff --git a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py
index fe11c47..bcb0161 100644
--- a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py
+++ b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py
@@ -11,53 +11,59 @@
 
 class DailyWorkSummary(Document):
 	def send_mails(self, dws_group, emails):
-		'''Send emails to get daily work summary to all users \
-			in selected daily work summary group'''
-		incoming_email_account = frappe.db.get_value('Email Account',
-			dict(enable_incoming=1, default_incoming=1),
-				'email_id')
+		"""Send emails to get daily work summary to all users \
+			in selected daily work summary group"""
+		incoming_email_account = frappe.db.get_value(
+			"Email Account", dict(enable_incoming=1, default_incoming=1), "email_id"
+		)
 
-		self.db_set('email_sent_to', '\n'.join(emails))
-		frappe.sendmail(recipients=emails,
+		self.db_set("email_sent_to", "\n".join(emails))
+		frappe.sendmail(
+			recipients=emails,
 			message=dws_group.message,
 			subject=dws_group.subject,
 			reference_doctype=self.doctype,
 			reference_name=self.name,
-			reply_to=incoming_email_account)
+			reply_to=incoming_email_account,
+		)
 
 	def send_summary(self):
-		'''Send summary of all replies. Called at midnight'''
+		"""Send summary of all replies. Called at midnight"""
 		args = self.get_message_details()
 		emails = get_user_emails_from_group(self.daily_work_summary_group)
-		frappe.sendmail(recipients=emails,
-			template='daily_work_summary',
+		frappe.sendmail(
+			recipients=emails,
+			template="daily_work_summary",
 			args=args,
 			subject=_(self.daily_work_summary_group),
 			reference_doctype=self.doctype,
-			reference_name=self.name)
+			reference_name=self.name,
+		)
 
-		self.db_set('status', 'Sent')
+		self.db_set("status", "Sent")
 
 	def get_message_details(self):
-		'''Return args for template'''
-		dws_group = frappe.get_doc('Daily Work Summary Group',
-			self.daily_work_summary_group)
+		"""Return args for template"""
+		dws_group = frappe.get_doc("Daily Work Summary Group", self.daily_work_summary_group)
 
-		replies = frappe.get_all('Communication',
-			fields=['content', 'text_content', 'sender'],
-			filters=dict(reference_doctype=self.doctype,
+		replies = frappe.get_all(
+			"Communication",
+			fields=["content", "text_content", "sender"],
+			filters=dict(
+				reference_doctype=self.doctype,
 				reference_name=self.name,
-				communication_type='Communication',
-				sent_or_received='Received'),
-			order_by='creation asc')
+				communication_type="Communication",
+				sent_or_received="Received",
+			),
+			order_by="creation asc",
+		)
 
 		did_not_reply = self.email_sent_to.split()
 
 		for d in replies:
-			user = frappe.db.get_values("User",
-				{"email": d.sender},
-				["full_name", "user_image"],
-				as_dict=True)
+			user = frappe.db.get_values(
+				"User", {"email": d.sender}, ["full_name", "user_image"], as_dict=True
+			)
 
 			d.sender_name = user[0].full_name if user else d.sender
 			d.image = user[0].image if user and user[0].image else None
@@ -66,17 +72,13 @@
 			# make thumbnail image
 			try:
 				if original_image:
-					file_name = frappe.get_list('File',
-						{'file_url': original_image})
+					file_name = frappe.get_list("File", {"file_url": original_image})
 
 					if file_name:
 						file_name = file_name[0].name
-						file_doc = frappe.get_doc('File', file_name)
+						file_doc = frappe.get_doc("File", file_name)
 						thumbnail_image = file_doc.make_thumbnail(
-							set_as_thumbnail=False,
-							width=100,
-							height=100,
-							crop=True
+							set_as_thumbnail=False, width=100, height=100, crop=True
 						)
 						d.image = thumbnail_image
 			except Exception:
@@ -85,34 +87,33 @@
 			if d.sender in did_not_reply:
 				did_not_reply.remove(d.sender)
 			if d.text_content:
-				d.content = frappe.utils.md_to_html(
-					EmailReplyParser.parse_reply(d.text_content)
-				)
+				d.content = frappe.utils.md_to_html(EmailReplyParser.parse_reply(d.text_content))
 
-		did_not_reply = [(frappe.db.get_value("User", {"email": email}, "full_name") or email)
-			for email in did_not_reply]
+		did_not_reply = [
+			(frappe.db.get_value("User", {"email": email}, "full_name") or email) for email in did_not_reply
+		]
 
-		return dict(replies=replies,
+		return dict(
+			replies=replies,
 			original_message=dws_group.message,
-			title=_('Work Summary for {0}').format(
-				global_date_format(self.creation)
-			),
-			did_not_reply=', '.join(did_not_reply) or '',
-			did_not_reply_title=_('No replies from'))
+			title=_("Work Summary for {0}").format(global_date_format(self.creation)),
+			did_not_reply=", ".join(did_not_reply) or "",
+			did_not_reply_title=_("No replies from"),
+		)
 
 
 def get_user_emails_from_group(group):
-	'''Returns list of email of enabled users from the given group
+	"""Returns list of email of enabled users from the given group
 
-	:param group: Daily Work Summary Group `name`'''
+	:param group: Daily Work Summary Group `name`"""
 	group_doc = group
 	if isinstance(group_doc, str):
-		group_doc = frappe.get_doc('Daily Work Summary Group', group)
+		group_doc = frappe.get_doc("Daily Work Summary Group", group)
 
 	emails = get_users_email(group_doc)
 
 	return emails
 
+
 def get_users_email(doc):
-	return [d.email for d in doc.users
-		if frappe.db.get_value("User", d.user, "enabled")]
+	return [d.email for d in doc.users if frappe.db.get_value("User", d.user, "enabled")]
diff --git a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py
index 5edfb31..7034365 100644
--- a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py
+++ b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py
@@ -9,82 +9,96 @@
 
 # test_records = frappe.get_test_records('Daily Work Summary')
 
+
 class TestDailyWorkSummary(unittest.TestCase):
 	def test_email_trigger(self):
 		self.setup_and_prepare_test()
 		for d in self.users:
 			# check that email is sent to users
 			if d.message:
-				self.assertTrue(d.email in [d.recipient for d in self.emails
-					if self.groups.subject in d.message])
+				self.assertTrue(
+					d.email in [d.recipient for d in self.emails if self.groups.subject in d.message]
+				)
 
 	def test_email_trigger_failed(self):
-		hour = '00:00'
-		if frappe.utils.nowtime().split(':')[0] == '00':
-			hour = '01:00'
+		hour = "00:00"
+		if frappe.utils.nowtime().split(":")[0] == "00":
+			hour = "01:00"
 
 		self.setup_and_prepare_test(hour)
 
 		for d in self.users:
 			# check that email is not sent to users
-			self.assertFalse(d.email in [d.recipient for d in self.emails
-				if self.groups.subject in d.message])
+			self.assertFalse(
+				d.email in [d.recipient for d in self.emails if self.groups.subject in d.message]
+			)
 
 	def test_incoming(self):
 		# get test mail with message-id as in-reply-to
 		self.setup_and_prepare_test()
 		with open(os.path.join(os.path.dirname(__file__), "test_data", "test-reply.raw"), "r") as f:
-			if not self.emails: return
-			test_mails = [f.read().replace('{{ sender }}',
-			self.users[-1].email).replace('{{ message_id }}',
-			self.emails[-1].message_id)]
+			if not self.emails:
+				return
+			test_mails = [
+				f.read()
+				.replace("{{ sender }}", self.users[-1].email)
+				.replace("{{ message_id }}", self.emails[-1].message_id)
+			]
 
 		# pull the mail
 		email_account = frappe.get_doc("Email Account", "_Test Email Account 1")
-		email_account.db_set('enable_incoming', 1)
+		email_account.db_set("enable_incoming", 1)
 		email_account.receive(test_mails=test_mails)
 
-		daily_work_summary = frappe.get_doc('Daily Work Summary',
-			frappe.get_all('Daily Work Summary')[0].name)
+		daily_work_summary = frappe.get_doc(
+			"Daily Work Summary", frappe.get_all("Daily Work Summary")[0].name
+		)
 
 		args = daily_work_summary.get_message_details()
 
-		self.assertTrue('I built Daily Work Summary!' in args.get('replies')[0].content)
+		self.assertTrue("I built Daily Work Summary!" in args.get("replies")[0].content)
 
 	def setup_and_prepare_test(self, hour=None):
-		frappe.db.sql('delete from `tabDaily Work Summary`')
-		frappe.db.sql('delete from `tabEmail Queue`')
-		frappe.db.sql('delete from `tabEmail Queue Recipient`')
-		frappe.db.sql('delete from `tabCommunication`')
-		frappe.db.sql('delete from `tabDaily Work Summary Group`')
+		frappe.db.sql("delete from `tabDaily Work Summary`")
+		frappe.db.sql("delete from `tabEmail Queue`")
+		frappe.db.sql("delete from `tabEmail Queue Recipient`")
+		frappe.db.sql("delete from `tabCommunication`")
+		frappe.db.sql("delete from `tabDaily Work Summary Group`")
 
-		self.users = frappe.get_all('User',
-			fields=['email'],
-			filters=dict(email=('!=', 'test@example.com')))
+		self.users = frappe.get_all(
+			"User", fields=["email"], filters=dict(email=("!=", "test@example.com"))
+		)
 		self.setup_groups(hour)
 
 		from erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group import trigger_emails
+
 		trigger_emails()
 
 		# check if emails are created
 
-		self.emails = frappe.db.sql("""select r.recipient, q.message, q.message_id \
+		self.emails = frappe.db.sql(
+			"""select r.recipient, q.message, q.message_id \
 			from `tabEmail Queue` as q, `tabEmail Queue Recipient` as r \
-			where q.name = r.parent""", as_dict=1)
-
+			where q.name = r.parent""",
+			as_dict=1,
+		)
 
 	def setup_groups(self, hour=None):
 		# setup email to trigger at this hour
 		if not hour:
-			hour = frappe.utils.nowtime().split(':')[0]
-			hour = hour+':00'
+			hour = frappe.utils.nowtime().split(":")[0]
+			hour = hour + ":00"
 
-		groups = frappe.get_doc(dict(doctype="Daily Work Summary Group",
-			name="Daily Work Summary",
-			users=self.users,
-			send_emails_at=hour,
-			subject="this is a subject for testing summary emails",
-			message='this is a message for testing summary emails'))
+		groups = frappe.get_doc(
+			dict(
+				doctype="Daily Work Summary Group",
+				name="Daily Work Summary",
+				users=self.users,
+				send_emails_at=hour,
+				subject="this is a subject for testing summary emails",
+				message="this is a message for testing summary emails",
+			)
+		)
 		groups.insert()
 
 		self.groups = groups
diff --git a/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.py b/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.py
index ed98168..4342f1c 100644
--- a/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.py
+++ b/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.py
@@ -15,37 +15,41 @@
 	def validate(self):
 		if self.users:
 			if not frappe.flags.in_test and not is_incoming_account_enabled():
-				frappe.throw(_('Please enable default incoming account before creating Daily Work Summary Group'))
+				frappe.throw(
+					_("Please enable default incoming account before creating Daily Work Summary Group")
+				)
 
 
 def trigger_emails():
-	'''Send emails to Employees at the given hour asking
-			them what did they work on today'''
+	"""Send emails to Employees at the given hour asking
+	them what did they work on today"""
 	groups = frappe.get_all("Daily Work Summary Group")
 	for d in groups:
 		group_doc = frappe.get_doc("Daily Work Summary Group", d)
-		if (is_current_hour(group_doc.send_emails_at)
+		if (
+			is_current_hour(group_doc.send_emails_at)
 			and not is_holiday(group_doc.holiday_list)
-			and group_doc.enabled):
+			and group_doc.enabled
+		):
 			emails = get_user_emails_from_group(group_doc)
 			# find emails relating to a company
 			if emails:
 				daily_work_summary = frappe.get_doc(
-					dict(doctype='Daily Work Summary', daily_work_summary_group=group_doc.name)
+					dict(doctype="Daily Work Summary", daily_work_summary_group=group_doc.name)
 				).insert()
 				daily_work_summary.send_mails(group_doc, emails)
 
 
 def is_current_hour(hour):
-	return frappe.utils.nowtime().split(':')[0] == hour.split(':')[0]
+	return frappe.utils.nowtime().split(":")[0] == hour.split(":")[0]
 
 
 def send_summary():
-	'''Send summary to everyone'''
-	for d in frappe.get_all('Daily Work Summary', dict(status='Open')):
-		daily_work_summary = frappe.get_doc('Daily Work Summary', d.name)
+	"""Send summary to everyone"""
+	for d in frappe.get_all("Daily Work Summary", dict(status="Open")):
+		daily_work_summary = frappe.get_doc("Daily Work Summary", d.name)
 		daily_work_summary.send_summary()
 
 
 def is_incoming_account_enabled():
-	return frappe.db.get_value('Email Account', dict(enable_incoming=1, default_incoming=1))
+	return frappe.db.get_value("Email Account", dict(enable_incoming=1, default_incoming=1))
diff --git a/erpnext/hr/doctype/department/department.py b/erpnext/hr/doctype/department/department.py
index 71300c4..159fa02 100644
--- a/erpnext/hr/doctype/department/department.py
+++ b/erpnext/hr/doctype/department/department.py
@@ -9,7 +9,7 @@
 
 
 class Department(NestedSet):
-	nsm_parent_field = 'parent_department'
+	nsm_parent_field = "parent_department"
 
 	def autoname(self):
 		root = get_root_of("Department")
@@ -26,7 +26,7 @@
 
 	def before_rename(self, old, new, merge=False):
 		# renaming consistency with abbreviation
-		if not frappe.get_cached_value('Company',  self.company,  'abbr') in new:
+		if not frappe.get_cached_value("Company", self.company, "abbr") in new:
 			new = get_abbreviated_name(new, self.company)
 
 		return new
@@ -39,17 +39,20 @@
 		super(Department, self).on_trash()
 		delete_events(self.doctype, self.name)
 
+
 def on_doctype_update():
 	frappe.db.add_index("Department", ["lft", "rgt"])
 
+
 def get_abbreviated_name(name, company):
-	abbr = frappe.get_cached_value('Company',  company,  'abbr')
-	new_name = '{0} - {1}'.format(name, abbr)
+	abbr = frappe.get_cached_value("Company", company, "abbr")
+	new_name = "{0} - {1}".format(name, abbr)
 	return new_name
 
+
 @frappe.whitelist()
 def get_children(doctype, parent=None, company=None, is_root=False):
-	condition = ''
+	condition = ""
 	var_dict = {
 		"name": get_root_of("Department"),
 		"parent": parent,
@@ -62,18 +65,26 @@
 	else:
 		condition = "parent_department = %(parent)s"
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select
 			name as value,
 			is_group as expandable
 		from `tab{doctype}`
 		where
 			{condition}
-		order by name""".format(doctype=doctype, condition=condition), var_dict, as_dict=1)
+		order by name""".format(
+			doctype=doctype, condition=condition
+		),
+		var_dict,
+		as_dict=1,
+	)
+
 
 @frappe.whitelist()
 def add_node():
 	from frappe.desk.treeview import make_tree_args
+
 	args = frappe.form_dict
 	args = make_tree_args(**args)
 
diff --git a/erpnext/hr/doctype/department/test_department.py b/erpnext/hr/doctype/department/test_department.py
index 95bf663..b8c043f 100644
--- a/erpnext/hr/doctype/department/test_department.py
+++ b/erpnext/hr/doctype/department/test_department.py
@@ -6,20 +6,26 @@
 import frappe
 
 test_ignore = ["Leave Block List"]
+
+
 class TestDepartment(unittest.TestCase):
-    def test_remove_department_data(self):
-        doc = create_department("Test Department")
-        frappe.delete_doc('Department', doc.name)
+	def test_remove_department_data(self):
+		doc = create_department("Test Department")
+		frappe.delete_doc("Department", doc.name)
+
 
 def create_department(department_name, parent_department=None):
-    doc = frappe.get_doc({
-        'doctype': 'Department',
-        'is_group': 0,
-        'parent_department': parent_department,
-        'department_name': department_name,
-        'company': frappe.defaults.get_defaults().company
-    }).insert()
+	doc = frappe.get_doc(
+		{
+			"doctype": "Department",
+			"is_group": 0,
+			"parent_department": parent_department,
+			"department_name": department_name,
+			"company": frappe.defaults.get_defaults().company,
+		}
+	).insert()
 
-    return doc
+	return doc
 
-test_records = frappe.get_test_records('Department')
+
+test_records = frappe.get_test_records("Department")
diff --git a/erpnext/hr/doctype/department_approver/department_approver.py b/erpnext/hr/doctype/department_approver/department_approver.py
index 375ae7c..87bdddd 100644
--- a/erpnext/hr/doctype/department_approver/department_approver.py
+++ b/erpnext/hr/doctype/department_approver/department_approver.py
@@ -10,6 +10,7 @@
 class DepartmentApprover(Document):
 	pass
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_approvers(doctype, txt, searchfield, start, page_len, filters):
@@ -20,25 +21,44 @@
 	approvers = []
 	department_details = {}
 	department_list = []
-	employee = frappe.get_value("Employee", filters.get("employee"), ["employee_name","department", "leave_approver", "expense_approver", "shift_request_approver"], as_dict=True)
+	employee = frappe.get_value(
+		"Employee",
+		filters.get("employee"),
+		["employee_name", "department", "leave_approver", "expense_approver", "shift_request_approver"],
+		as_dict=True,
+	)
 
 	employee_department = filters.get("department") or employee.department
 	if employee_department:
-		department_details = frappe.db.get_value("Department", {"name": employee_department}, ["lft", "rgt"], as_dict=True)
+		department_details = frappe.db.get_value(
+			"Department", {"name": employee_department}, ["lft", "rgt"], as_dict=True
+		)
 	if department_details:
-		department_list = frappe.db.sql("""select name from `tabDepartment` where lft <= %s
+		department_list = frappe.db.sql(
+			"""select name from `tabDepartment` where lft <= %s
 			and rgt >= %s
 			and disabled=0
-			order by lft desc""", (department_details.lft, department_details.rgt), as_list=True)
+			order by lft desc""",
+			(department_details.lft, department_details.rgt),
+			as_list=True,
+		)
 
 	if filters.get("doctype") == "Leave Application" and employee.leave_approver:
-		approvers.append(frappe.db.get_value("User", employee.leave_approver, ['name', 'first_name', 'last_name']))
+		approvers.append(
+			frappe.db.get_value("User", employee.leave_approver, ["name", "first_name", "last_name"])
+		)
 
 	if filters.get("doctype") == "Expense Claim" and employee.expense_approver:
-		approvers.append(frappe.db.get_value("User", employee.expense_approver, ['name', 'first_name', 'last_name']))
+		approvers.append(
+			frappe.db.get_value("User", employee.expense_approver, ["name", "first_name", "last_name"])
+		)
 
 	if filters.get("doctype") == "Shift Request" and employee.shift_request_approver:
-		approvers.append(frappe.db.get_value("User", employee.shift_request_approver, ['name', 'first_name', 'last_name']))
+		approvers.append(
+			frappe.db.get_value(
+				"User", employee.shift_request_approver, ["name", "first_name", "last_name"]
+			)
+		)
 
 	if filters.get("doctype") == "Leave Application":
 		parentfield = "leave_approvers"
@@ -51,17 +71,23 @@
 		field_name = "Shift Request Approver"
 	if department_list:
 		for d in department_list:
-			approvers += frappe.db.sql("""select user.name, user.first_name, user.last_name from
+			approvers += frappe.db.sql(
+				"""select user.name, user.first_name, user.last_name from
 				tabUser user, `tabDepartment Approver` approver where
 				approver.parent = %s
 				and user.name like %s
 				and approver.parentfield = %s
-				and approver.approver=user.name""",(d, "%" + txt + "%", parentfield), as_list=True)
+				and approver.approver=user.name""",
+				(d, "%" + txt + "%", parentfield),
+				as_list=True,
+			)
 
 	if len(approvers) == 0:
-		error_msg = _("Please set {0} for the Employee: {1}").format(field_name, frappe.bold(employee.employee_name))
+		error_msg = _("Please set {0} for the Employee: {1}").format(
+			field_name, frappe.bold(employee.employee_name)
+		)
 		if department_list:
-			error_msg += _(" or for Department: {0}").format(frappe.bold(employee_department))
+			error_msg += " " + _("or for Department: {0}").format(frappe.bold(employee_department))
 		frappe.throw(error_msg, title=_(field_name + " Missing"))
 
 	return set(tuple(approver) for approver in approvers)
diff --git a/erpnext/hr/doctype/designation/test_designation.py b/erpnext/hr/doctype/designation/test_designation.py
index f2d6d36..0840d13 100644
--- a/erpnext/hr/doctype/designation/test_designation.py
+++ b/erpnext/hr/doctype/designation/test_designation.py
@@ -5,15 +5,18 @@
 
 # test_records = frappe.get_test_records('Designation')
 
-def create_designation(**args):
-    args = frappe._dict(args)
-    if frappe.db.exists("Designation", args.designation_name or "_Test designation"):
-        return frappe.get_doc("Designation", args.designation_name or "_Test designation")
 
-    designation = frappe.get_doc({
-        "doctype": "Designation",
-        "designation_name": args.designation_name or "_Test designation",
-        "description": args.description or "_Test description"
-    })
-    designation.save()
-    return designation
+def create_designation(**args):
+	args = frappe._dict(args)
+	if frappe.db.exists("Designation", args.designation_name or "_Test designation"):
+		return frappe.get_doc("Designation", args.designation_name or "_Test designation")
+
+	designation = frappe.get_doc(
+		{
+			"doctype": "Designation",
+			"designation_name": args.designation_name or "_Test designation",
+			"description": args.description or "_Test description",
+		}
+	)
+	designation.save()
+	return designation
diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json
index d592a9c..4247914 100644
--- a/erpnext/hr/doctype/employee/employee.json
+++ b/erpnext/hr/doctype/employee/employee.json
@@ -4,7 +4,7 @@
  "allow_import": 1,
  "allow_rename": 1,
  "autoname": "naming_series:",
- "creation": "2013-03-07 09:04:18",
+ "creation": "2022-02-21 11:54:09.632218",
  "doctype": "DocType",
  "document_type": "Setup",
  "editable_grid": 1,
@@ -62,6 +62,8 @@
   "holiday_list",
   "default_shift",
   "salary_information",
+  "salary_currency",
+  "ctc",
   "salary_mode",
   "payroll_cost_center",
   "column_break_52",
@@ -807,17 +809,30 @@
    "fieldtype": "Link",
    "label": "Shift Request Approver",
    "options": "User"
+  },
+  {
+   "fieldname": "salary_currency",
+   "fieldtype": "Link",
+   "label": "Salary Currency",
+   "options": "Currency"
+  },
+  {
+   "fieldname": "ctc",
+   "fieldtype": "Currency",
+   "label": "Cost to Company (CTC)",
+   "options": "salary_currency"
   }
  ],
  "icon": "fa fa-user",
  "idx": 24,
  "image_field": "image",
  "links": [],
- "modified": "2021-06-17 11:31:37.730760",
+ "modified": "2022-06-10 01:29:32.952091",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Employee",
  "name_case": "Title Case",
+ "naming_rule": "By \"Naming Series\" field",
  "owner": "Administrator",
  "permissions": [
   {
@@ -859,5 +874,6 @@
  "show_name_in_global_search": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "title_field": "employee_name"
 }
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py
index 6e52eb9..d6a911d 100755
--- a/erpnext/hr/doctype/employee/employee.py
+++ b/erpnext/hr/doctype/employee/employee.py
@@ -18,22 +18,25 @@
 
 class EmployeeUserDisabledError(frappe.ValidationError):
 	pass
+
+
 class InactiveEmployeeStatusError(frappe.ValidationError):
 	pass
 
+
 class Employee(NestedSet):
-	nsm_parent_field = 'reports_to'
+	nsm_parent_field = "reports_to"
 
 	def autoname(self):
 		naming_method = frappe.db.get_value("HR Settings", None, "emp_created_by")
 		if not naming_method:
 			throw(_("Please setup Employee Naming System in Human Resource > HR Settings"))
 		else:
-			if naming_method == 'Naming Series':
+			if naming_method == "Naming Series":
 				set_name_by_naming_series(self)
-			elif naming_method == 'Employee Number':
+			elif naming_method == "Employee Number":
 				self.name = self.employee_number
-			elif naming_method == 'Full Name':
+			elif naming_method == "Full Name":
 				self.set_employee_name()
 				self.name = self.employee_name
 
@@ -41,6 +44,7 @@
 
 	def validate(self):
 		from erpnext.controllers.status_updater import validate_status
+
 		validate_status(self.status, ["Active", "Inactive", "Suspended", "Left"])
 
 		self.employee = self.name
@@ -58,25 +62,25 @@
 		else:
 			existing_user_id = frappe.db.get_value("Employee", self.name, "user_id")
 			if existing_user_id:
-				remove_user_permission(
-					"Employee", self.name, existing_user_id)
+				remove_user_permission("Employee", self.name, existing_user_id)
 
 	def after_rename(self, old, new, merge):
 		self.db_set("employee", new)
 
 	def set_employee_name(self):
-		self.employee_name = ' '.join(filter(lambda x: x, [self.first_name, self.middle_name, self.last_name]))
+		self.employee_name = " ".join(
+			filter(lambda x: x, [self.first_name, self.middle_name, self.last_name])
+		)
 
 	def validate_user_details(self):
 		if self.user_id:
-			data = frappe.db.get_value('User',
-				self.user_id, ['enabled', 'user_image'], as_dict=1)
+			data = frappe.db.get_value("User", self.user_id, ["enabled", "user_image"], as_dict=1)
 
 			if not data:
 				self.user_id = None
 				return
 
-			if data.get("user_image") and self.image == '':
+			if data.get("user_image") and self.image == "":
 				self.image = data.get("user_image")
 			self.validate_for_enabled_user_id(data.get("enabled", 0))
 			self.validate_duplicate_user_id()
@@ -93,14 +97,14 @@
 		self.update_approver_role()
 
 	def update_user_permissions(self):
-		if not self.create_user_permission: return
-		if not has_permission('User Permission', ptype='write', raise_exception=False): return
+		if not self.create_user_permission:
+			return
+		if not has_permission("User Permission", ptype="write", raise_exception=False):
+			return
 
-		employee_user_permission_exists = frappe.db.exists('User Permission', {
-			'allow': 'Employee',
-			'for_value': self.name,
-			'user': self.user_id
-		})
+		employee_user_permission_exists = frappe.db.exists(
+			"User Permission", {"allow": "Employee", "for_value": self.name, "user": self.user_id}
+		)
 
 		if employee_user_permission_exists:
 			return
@@ -137,12 +141,14 @@
 			if not user.user_image:
 				user.user_image = self.image
 				try:
-					frappe.get_doc({
-						"doctype": "File",
-						"file_url": self.image,
-						"attached_to_doctype": "User",
-						"attached_to_name": self.user_id
-					}).insert(ignore_if_duplicate=True)
+					frappe.get_doc(
+						{
+							"doctype": "File",
+							"file_url": self.image,
+							"attached_to_doctype": "User",
+							"attached_to_name": self.user_id,
+						}
+					).insert(ignore_if_duplicate=True)
 				except frappe.DuplicateEntryError:
 					# already exists
 					pass
@@ -164,16 +170,32 @@
 		if self.date_of_birth and getdate(self.date_of_birth) > getdate(today()):
 			throw(_("Date of Birth cannot be greater than today."))
 
-		if self.date_of_birth and self.date_of_joining and getdate(self.date_of_birth) >= getdate(self.date_of_joining):
+		if (
+			self.date_of_birth
+			and self.date_of_joining
+			and getdate(self.date_of_birth) >= getdate(self.date_of_joining)
+		):
 			throw(_("Date of Joining must be greater than Date of Birth"))
 
-		elif self.date_of_retirement and self.date_of_joining and (getdate(self.date_of_retirement) <= getdate(self.date_of_joining)):
+		elif (
+			self.date_of_retirement
+			and self.date_of_joining
+			and (getdate(self.date_of_retirement) <= getdate(self.date_of_joining))
+		):
 			throw(_("Date Of Retirement must be greater than Date of Joining"))
 
-		elif self.relieving_date and self.date_of_joining and (getdate(self.relieving_date) < getdate(self.date_of_joining)):
+		elif (
+			self.relieving_date
+			and self.date_of_joining
+			and (getdate(self.relieving_date) < getdate(self.date_of_joining))
+		):
 			throw(_("Relieving Date must be greater than or equal to Date of Joining"))
 
-		elif self.contract_end_date and self.date_of_joining and (getdate(self.contract_end_date) <= getdate(self.date_of_joining)):
+		elif (
+			self.contract_end_date
+			and self.date_of_joining
+			and (getdate(self.contract_end_date) <= getdate(self.date_of_joining))
+		):
 			throw(_("Contract End Date must be greater than Date of Joining"))
 
 	def validate_email(self):
@@ -189,14 +211,20 @@
 			self.prefered_email = preferred_email
 
 	def validate_status(self):
-		if self.status == 'Left':
-			reports_to = frappe.db.get_all('Employee',
-				filters={'reports_to': self.name, 'status': "Active"},
-				fields=['name','employee_name']
+		if self.status == "Left":
+			reports_to = frappe.db.get_all(
+				"Employee",
+				filters={"reports_to": self.name, "status": "Active"},
+				fields=["name", "employee_name"],
 			)
 			if reports_to:
-				link_to_employees = [frappe.utils.get_link_to_form('Employee', employee.name, label=employee.employee_name) for employee in reports_to]
-				message = _("The following employees are currently still reporting to {0}:").format(frappe.bold(self.employee_name))
+				link_to_employees = [
+					frappe.utils.get_link_to_form("Employee", employee.name, label=employee.employee_name)
+					for employee in reports_to
+				]
+				message = _("The following employees are currently still reporting to {0}:").format(
+					frappe.bold(self.employee_name)
+				)
 				message += "<br><br><ul><li>" + "</li><li>".join(link_to_employees)
 				message += "</li></ul><br>"
 				message += _("Please make sure the employees above report to another Active employee.")
@@ -205,7 +233,7 @@
 				throw(_("Please enter relieving date."))
 
 	def validate_for_enabled_user_id(self, enabled):
-		if not self.status == 'Active':
+		if not self.status == "Active":
 			return
 
 		if enabled is None:
@@ -214,11 +242,16 @@
 			frappe.throw(_("User {0} is disabled").format(self.user_id), EmployeeUserDisabledError)
 
 	def validate_duplicate_user_id(self):
-		employee = frappe.db.sql_list("""select name from `tabEmployee` where
-			user_id=%s and status='Active' and name!=%s""", (self.user_id, self.name))
+		employee = frappe.db.sql_list(
+			"""select name from `tabEmployee` where
+			user_id=%s and status='Active' and name!=%s""",
+			(self.user_id, self.name),
+		)
 		if employee:
-			throw(_("User {0} is already assigned to Employee {1}").format(
-				self.user_id, employee[0]), frappe.DuplicateEntryError)
+			throw(
+				_("User {0} is already assigned to Employee {1}").format(self.user_id, employee[0]),
+				frappe.DuplicateEntryError,
+			)
 
 	def validate_reports_to(self):
 		if self.reports_to == self.name:
@@ -227,17 +260,25 @@
 	def on_trash(self):
 		self.update_nsm_model()
 		delete_events(self.doctype, self.name)
-		if frappe.db.exists("Employee Transfer", {'new_employee_id': self.name, 'docstatus': 1}):
-			emp_transfer = frappe.get_doc("Employee Transfer", {'new_employee_id': self.name, 'docstatus': 1})
-			emp_transfer.db_set("new_employee_id", '')
+		if frappe.db.exists("Employee Transfer", {"new_employee_id": self.name, "docstatus": 1}):
+			emp_transfer = frappe.get_doc(
+				"Employee Transfer", {"new_employee_id": self.name, "docstatus": 1}
+			)
+			emp_transfer.db_set("new_employee_id", "")
 
 	def validate_preferred_email(self):
 		if self.prefered_contact_email and not self.get(scrub(self.prefered_contact_email)):
 			frappe.msgprint(_("Please enter {0}").format(self.prefered_contact_email))
 
 	def validate_onboarding_process(self):
-		employee_onboarding = frappe.get_all("Employee Onboarding",
-			filters={"job_applicant": self.job_applicant, "docstatus": 1, "boarding_status": ("!=", "Completed")})
+		employee_onboarding = frappe.get_all(
+			"Employee Onboarding",
+			filters={
+				"job_applicant": self.job_applicant,
+				"docstatus": 1,
+				"boarding_status": ("!=", "Completed"),
+			},
+		)
 		if employee_onboarding:
 			doc = frappe.get_doc("Employee Onboarding", employee_onboarding[0].name)
 			doc.validate_employee_creation()
@@ -245,20 +286,26 @@
 
 	def reset_employee_emails_cache(self):
 		prev_doc = self.get_doc_before_save() or {}
-		cell_number = cstr(self.get('cell_number'))
-		prev_number = cstr(prev_doc.get('cell_number'))
-		if (cell_number != prev_number or
-			self.get('user_id') != prev_doc.get('user_id')):
-			frappe.cache().hdel('employees_with_number', cell_number)
-			frappe.cache().hdel('employees_with_number', prev_number)
+		cell_number = cstr(self.get("cell_number"))
+		prev_number = cstr(prev_doc.get("cell_number"))
+		if cell_number != prev_number or self.get("user_id") != prev_doc.get("user_id"):
+			frappe.cache().hdel("employees_with_number", cell_number)
+			frappe.cache().hdel("employees_with_number", prev_number)
+
 
 def get_timeline_data(doctype, name):
-	'''Return timeline for attendance'''
-	return dict(frappe.db.sql('''select unix_timestamp(attendance_date), count(*)
+	"""Return timeline for attendance"""
+	return dict(
+		frappe.db.sql(
+			"""select unix_timestamp(attendance_date), count(*)
 		from `tabAttendance` where employee=%s
 			and attendance_date > date_sub(curdate(), interval 1 year)
 			and status in ('Present', 'Half Day')
-			group by attendance_date''', name))
+			group by attendance_date""",
+			name,
+		)
+	)
+
 
 @frappe.whitelist()
 def get_retirement_date(date_of_birth=None):
@@ -266,14 +313,15 @@
 	if date_of_birth:
 		try:
 			retirement_age = int(frappe.db.get_single_value("HR Settings", "retirement_age") or 60)
-			dt = add_years(getdate(date_of_birth),retirement_age)
-			ret = {'date_of_retirement': dt.strftime('%Y-%m-%d')}
+			dt = add_years(getdate(date_of_birth), retirement_age)
+			ret = {"date_of_retirement": dt.strftime("%Y-%m-%d")}
 		except ValueError:
 			# invalid date
 			ret = {}
 
 	return ret
 
+
 def validate_employee_role(doc, method):
 	# called via User hook
 	if "Employee" in [d.role for d in doc.get("roles")]:
@@ -281,39 +329,52 @@
 			frappe.msgprint(_("Please set User ID field in an Employee record to set Employee Role"))
 			doc.get("roles").remove(doc.get("roles", {"role": "Employee"})[0])
 
+
 def update_user_permissions(doc, method):
 	# called via User hook
 	if "Employee" in [d.role for d in doc.get("roles")]:
-		if not has_permission('User Permission', ptype='write', raise_exception=False): return
+		if not has_permission("User Permission", ptype="write", raise_exception=False):
+			return
 		employee = frappe.get_doc("Employee", {"user_id": doc.name})
 		employee.update_user_permissions()
 
+
 def get_employee_email(employee_doc):
-	return employee_doc.get("user_id") or employee_doc.get("personal_email") or employee_doc.get("company_email")
+	return (
+		employee_doc.get("user_id")
+		or employee_doc.get("personal_email")
+		or employee_doc.get("company_email")
+	)
+
 
 def get_holiday_list_for_employee(employee, raise_exception=True):
 	if employee:
 		holiday_list, company = frappe.db.get_value("Employee", employee, ["holiday_list", "company"])
 	else:
-		holiday_list=''
-		company=frappe.db.get_value("Global Defaults", None, "default_company")
+		holiday_list = ""
+		company = frappe.db.get_value("Global Defaults", None, "default_company")
 
 	if not holiday_list:
-		holiday_list = frappe.get_cached_value('Company',  company,  "default_holiday_list")
+		holiday_list = frappe.get_cached_value("Company", company, "default_holiday_list")
 
 	if not holiday_list and raise_exception:
-		frappe.throw(_('Please set a default Holiday List for Employee {0} or Company {1}').format(employee, company))
+		frappe.throw(
+			_("Please set a default Holiday List for Employee {0} or Company {1}").format(employee, company)
+		)
 
 	return holiday_list
 
-def is_holiday(employee, date=None, raise_exception=True, only_non_weekly=False, with_description=False):
-	'''
+
+def is_holiday(
+	employee, date=None, raise_exception=True, only_non_weekly=False, with_description=False
+):
+	"""
 	Returns True if given Employee has an holiday on the given date
-		:param employee: Employee `name`
-		:param date: Date to check. Will check for today if None
-		:param raise_exception: Raise an exception if no holiday list found, default is True
-		:param only_non_weekly: Check only non-weekly holidays, default is False
-	'''
+	        :param employee: Employee `name`
+	        :param date: Date to check. Will check for today if None
+	        :param raise_exception: Raise an exception if no holiday list found, default is True
+	        :param only_non_weekly: Check only non-weekly holidays, default is False
+	"""
 
 	holiday_list = get_holiday_list_for_employee(employee, raise_exception)
 	if not date:
@@ -322,34 +383,28 @@
 	if not holiday_list:
 		return False
 
-	filters = {
-		'parent': holiday_list,
-		'holiday_date': date
-	}
+	filters = {"parent": holiday_list, "holiday_date": date}
 	if only_non_weekly:
-		filters['weekly_off'] = False
+		filters["weekly_off"] = False
 
-	holidays = frappe.get_all(
-		'Holiday',
-		fields=['description'],
-		filters=filters,
-		pluck='description'
-	)
+	holidays = frappe.get_all("Holiday", fields=["description"], filters=filters, pluck="description")
 
 	if with_description:
 		return len(holidays) > 0, holidays
 
 	return len(holidays) > 0
 
+
 @frappe.whitelist()
-def deactivate_sales_person(status = None, employee = None):
+def deactivate_sales_person(status=None, employee=None):
 	if status == "Left":
 		sales_person = frappe.db.get_value("Sales Person", {"Employee": employee})
 		if sales_person:
 			frappe.db.set_value("Sales Person", sales_person, "enabled", 0)
 
+
 @frappe.whitelist()
-def create_user(employee, user = None, email=None):
+def create_user(employee, user=None, email=None):
 	emp = frappe.get_doc("Employee", employee)
 
 	employee_name = emp.employee_name.split(" ")
@@ -367,93 +422,98 @@
 		emp.prefered_email = email
 
 	user = frappe.new_doc("User")
-	user.update({
-		"name": emp.employee_name,
-		"email": emp.prefered_email,
-		"enabled": 1,
-		"first_name": first_name,
-		"middle_name": middle_name,
-		"last_name": last_name,
-		"gender": emp.gender,
-		"birth_date": emp.date_of_birth,
-		"phone": emp.cell_number,
-		"bio": emp.bio
-	})
+	user.update(
+		{
+			"name": emp.employee_name,
+			"email": emp.prefered_email,
+			"enabled": 1,
+			"first_name": first_name,
+			"middle_name": middle_name,
+			"last_name": last_name,
+			"gender": emp.gender,
+			"birth_date": emp.date_of_birth,
+			"phone": emp.cell_number,
+			"bio": emp.bio,
+		}
+	)
 	user.insert()
 	return user.name
 
+
 def get_all_employee_emails(company):
-	'''Returns list of employee emails either based on user_id or company_email'''
-	employee_list = frappe.get_all('Employee',
-		fields=['name','employee_name'],
-		filters={
-			'status': 'Active',
-			'company': company
-		}
+	"""Returns list of employee emails either based on user_id or company_email"""
+	employee_list = frappe.get_all(
+		"Employee", fields=["name", "employee_name"], filters={"status": "Active", "company": company}
 	)
 	employee_emails = []
 	for employee in employee_list:
 		if not employee:
 			continue
-		user, company_email, personal_email = frappe.db.get_value('Employee',
-			employee, ['user_id', 'company_email', 'personal_email'])
+		user, company_email, personal_email = frappe.db.get_value(
+			"Employee", employee, ["user_id", "company_email", "personal_email"]
+		)
 		email = user or company_email or personal_email
 		if email:
 			employee_emails.append(email)
 	return employee_emails
 
+
 def get_employee_emails(employee_list):
-	'''Returns list of employee emails either based on user_id or company_email'''
+	"""Returns list of employee emails either based on user_id or company_email"""
 	employee_emails = []
 	for employee in employee_list:
 		if not employee:
 			continue
-		user, company_email, personal_email = frappe.db.get_value('Employee', employee,
-											['user_id', 'company_email', 'personal_email'])
+		user, company_email, personal_email = frappe.db.get_value(
+			"Employee", employee, ["user_id", "company_email", "personal_email"]
+		)
 		email = user or company_email or personal_email
 		if email:
 			employee_emails.append(email)
 	return employee_emails
 
+
 @frappe.whitelist()
 def get_children(doctype, parent=None, company=None, is_root=False, is_tree=False):
 
-	filters = [['status', '=', 'Active']]
-	if company and company != 'All Companies':
-		filters.append(['company', '=', company])
+	filters = [["status", "=", "Active"]]
+	if company and company != "All Companies":
+		filters.append(["company", "=", company])
 
-	fields = ['name as value', 'employee_name as title']
+	fields = ["name as value", "employee_name as title"]
 
 	if is_root:
-		parent = ''
-	if parent and company and parent!=company:
-		filters.append(['reports_to', '=', parent])
+		parent = ""
+	if parent and company and parent != company:
+		filters.append(["reports_to", "=", parent])
 	else:
-		filters.append(['reports_to', '=', ''])
+		filters.append(["reports_to", "=", ""])
 
-	employees = frappe.get_list(doctype, fields=fields,
-		filters=filters, order_by='name')
+	employees = frappe.get_list(doctype, fields=fields, filters=filters, order_by="name")
 
 	for employee in employees:
-		is_expandable = frappe.get_all(doctype, filters=[
-			['reports_to', '=', employee.get('value')]
-		])
+		is_expandable = frappe.get_all(doctype, filters=[["reports_to", "=", employee.get("value")]])
 		employee.expandable = 1 if is_expandable else 0
 
 	return employees
 
+
 def on_doctype_update():
 	frappe.db.add_index("Employee", ["lft", "rgt"])
 
-def has_user_permission_for_employee(user_name, employee_name):
-	return frappe.db.exists({
-		'doctype': 'User Permission',
-		'user': user_name,
-		'allow': 'Employee',
-		'for_value': employee_name
-	})
 
-def has_upload_permission(doc, ptype='read', user=None):
+def has_user_permission_for_employee(user_name, employee_name):
+	return frappe.db.exists(
+		{
+			"doctype": "User Permission",
+			"user": user_name,
+			"allow": "Employee",
+			"for_value": employee_name,
+		}
+	)
+
+
+def has_upload_permission(doc, ptype="read", user=None):
 	if not user:
 		user = frappe.session.user
 	if get_doc_permissions(doc, user=user, ptype=ptype).get(ptype):
diff --git a/erpnext/hr/doctype/employee/employee_dashboard.py b/erpnext/hr/doctype/employee/employee_dashboard.py
index fb62b04..da89a1d 100644
--- a/erpnext/hr/doctype/employee/employee_dashboard.py
+++ b/erpnext/hr/doctype/employee/employee_dashboard.py
@@ -3,53 +3,48 @@
 
 def get_data():
 	return {
-		'heatmap': True,
-		'heatmap_message': _('This is based on the attendance of this Employee'),
-		'fieldname': 'employee',
-		'non_standard_fieldnames': {
-			'Bank Account': 'party',
-			'Employee Grievance': 'raised_by'
-		},
-		'transactions': [
+		"heatmap": True,
+		"heatmap_message": _("This is based on the attendance of this Employee"),
+		"fieldname": "employee",
+		"non_standard_fieldnames": {"Bank Account": "party", "Employee Grievance": "raised_by"},
+		"transactions": [
+			{"label": _("Attendance"), "items": ["Attendance", "Attendance Request", "Employee Checkin"]},
 			{
-				'label': _('Attendance'),
-				'items': ['Attendance', 'Attendance Request', 'Employee Checkin']
+				"label": _("Leave"),
+				"items": ["Leave Application", "Leave Allocation", "Leave Policy Assignment"],
 			},
 			{
-				'label': _('Leave'),
-				'items': ['Leave Application', 'Leave Allocation', 'Leave Policy Assignment']
+				"label": _("Lifecycle"),
+				"items": [
+					"Employee Onboarding",
+					"Employee Transfer",
+					"Employee Promotion",
+					"Employee Grievance",
+				],
 			},
 			{
-				'label': _('Lifecycle'),
-				'items': ['Employee Onboarding', 'Employee Transfer', 'Employee Promotion', 'Employee Grievance']
+				"label": _("Exit"),
+				"items": ["Employee Separation", "Exit Interview", "Full and Final Statement"],
+			},
+			{"label": _("Shift"), "items": ["Shift Request", "Shift Assignment"]},
+			{"label": _("Expense"), "items": ["Expense Claim", "Travel Request", "Employee Advance"]},
+			{"label": _("Benefit"), "items": ["Employee Benefit Application", "Employee Benefit Claim"]},
+			{
+				"label": _("Payroll"),
+				"items": [
+					"Salary Structure Assignment",
+					"Salary Slip",
+					"Additional Salary",
+					"Timesheet",
+					"Employee Incentive",
+					"Retention Bonus",
+					"Bank Account",
+				],
 			},
 			{
-				'label': _('Exit'),
-				'items': ['Employee Separation', 'Exit Interview', 'Full and Final Statement']
+				"label": _("Training"),
+				"items": ["Training Event", "Training Result", "Training Feedback", "Employee Skill Map"],
 			},
-			{
-				'label': _('Shift'),
-				'items': ['Shift Request', 'Shift Assignment']
-			},
-			{
-				'label': _('Expense'),
-				'items': ['Expense Claim', 'Travel Request', 'Employee Advance']
-			},
-			{
-				'label': _('Benefit'),
-				'items': ['Employee Benefit Application', 'Employee Benefit Claim']
-			},
-			{
-				'label': _('Payroll'),
-				'items': ['Salary Structure Assignment', 'Salary Slip', 'Additional Salary', 'Timesheet','Employee Incentive', 'Retention Bonus', 'Bank Account']
-			},
-			{
-				'label': _('Training'),
-				'items': ['Training Event', 'Training Result', 'Training Feedback', 'Employee Skill Map']
-			},
-			{
-				'label': _('Evaluation'),
-				'items': ['Appraisal']
-			},
-		]
+			{"label": _("Evaluation"), "items": ["Appraisal"]},
+		],
 	}
diff --git a/erpnext/hr/doctype/employee/employee_reminders.py b/erpnext/hr/doctype/employee/employee_reminders.py
index 0bb6637..f09d7ff 100644
--- a/erpnext/hr/doctype/employee/employee_reminders.py
+++ b/erpnext/hr/doctype/employee/employee_reminders.py
@@ -44,13 +44,10 @@
 	else:
 		return
 
-	employees = frappe.db.get_all('Employee', filters={'status': 'Active'}, pluck='name')
+	employees = frappe.db.get_all("Employee", filters={"status": "Active"}, pluck="name")
 	for employee in employees:
 		holidays = get_holidays_for_employee(
-			employee,
-			start_date, end_date,
-			only_non_weekly=True,
-			raise_exception=False
+			employee, start_date, end_date, only_non_weekly=True, raise_exception=False
 		)
 
 		send_holidays_reminder_in_advance(employee, holidays)
@@ -60,7 +57,7 @@
 	if not holidays:
 		return
 
-	employee_doc = frappe.get_doc('Employee', employee)
+	employee_doc = frappe.get_doc("Employee", employee)
 	employee_email = get_employee_email(employee_doc)
 	frequency = frappe.db.get_single_value("HR Settings", "frequency")
 
@@ -70,15 +67,18 @@
 		subject=_("Upcoming Holidays Reminder"),
 		template="holiday_reminder",
 		args=dict(
-			reminder_text=_("Hey {}! This email is to remind you about the upcoming holidays.").format(employee_doc.get('first_name')),
+			reminder_text=_("Hey {}! This email is to remind you about the upcoming holidays.").format(
+				employee_doc.get("first_name")
+			),
 			message=_("Below is the list of upcoming holidays for you:"),
 			advance_holiday_reminder=True,
 			holidays=holidays,
-			frequency=frequency[:-2]
+			frequency=frequency[:-2],
 		),
-		header=email_header
+		header=email_header,
 	)
 
+
 # ------------------
 # BIRTHDAY REMINDERS
 # ------------------
@@ -109,10 +109,10 @@
 
 def get_birthday_reminder_text_and_message(birthday_persons):
 	if len(birthday_persons) == 1:
-		birthday_person_text = birthday_persons[0]['name']
+		birthday_person_text = birthday_persons[0]["name"]
 	else:
 		# converts ["Jim", "Rim", "Dim"] to Jim, Rim & Dim
-		person_names = [d['name'] for d in birthday_persons]
+		person_names = [d["name"] for d in birthday_persons]
 		birthday_person_text = comma_sep(person_names, frappe._("{0} & {1}"), False)
 
 	reminder_text = _("Today is {0}'s birthday 🎉").format(birthday_person_text)
@@ -133,7 +133,7 @@
 			birthday_persons=birthday_persons,
 			message=message,
 		),
-		header=_("Birthday Reminder 🎂")
+		header=_("Birthday Reminder 🎂"),
 	)
 
 
@@ -150,15 +150,16 @@
 	from collections import defaultdict
 
 	# Set column based on event type
-	if event_type == 'birthday':
-		condition_column = 'date_of_birth'
-	elif event_type == 'work_anniversary':
-		condition_column = 'date_of_joining'
+	if event_type == "birthday":
+		condition_column = "date_of_birth"
+	elif event_type == "work_anniversary":
+		condition_column = "date_of_joining"
 	else:
 		return
 
-	employees_born_today = frappe.db.multisql({
-		"mariadb": f"""
+	employees_born_today = frappe.db.multisql(
+		{
+			"mariadb": f"""
 			SELECT `personal_email`, `company`, `company_email`, `user_id`, `employee_name` AS 'name', `image`, `date_of_joining`
 			FROM `tabEmployee`
 			WHERE
@@ -170,7 +171,7 @@
 			AND
 				`status` = 'Active'
 		""",
-		"postgres": f"""
+			"postgres": f"""
 			SELECT "personal_email", "company", "company_email", "user_id", "employee_name" AS 'name', "image"
 			FROM "tabEmployee"
 			WHERE
@@ -182,12 +183,15 @@
 			AND
 				"status" = 'Active'
 		""",
-	}, dict(today=today(), condition_column=condition_column), as_dict=1)
+		},
+		dict(today=today(), condition_column=condition_column),
+		as_dict=1,
+	)
 
 	grouped_employees = defaultdict(lambda: [])
 
 	for employee_doc in employees_born_today:
-		grouped_employees[employee_doc.get('company')].append(employee_doc)
+		grouped_employees[employee_doc.get("company")].append(employee_doc)
 
 	return grouped_employees
 
@@ -222,20 +226,20 @@
 
 def get_work_anniversary_reminder_text_and_message(anniversary_persons):
 	if len(anniversary_persons) == 1:
-		anniversary_person = anniversary_persons[0]['name']
+		anniversary_person = anniversary_persons[0]["name"]
 		persons_name = anniversary_person
 		# Number of years completed at the company
-		completed_years = getdate().year - anniversary_persons[0]['date_of_joining'].year
-		anniversary_person += f" completed {completed_years} year(s)"
+		completed_years = getdate().year - anniversary_persons[0]["date_of_joining"].year
+		anniversary_person += f" completed {get_pluralized_years(completed_years)}"
 	else:
 		person_names_with_years = []
 		names = []
 		for person in anniversary_persons:
-			person_text = person['name']
+			person_text = person["name"]
 			names.append(person_text)
 			# Number of years completed at the company
-			completed_years = getdate().year - person['date_of_joining'].year
-			person_text += f" completed {completed_years} year(s)"
+			completed_years = getdate().year - person["date_of_joining"].year
+			person_text += f" completed {get_pluralized_years(completed_years)}"
 			person_names_with_years.append(person_text)
 
 		# converts ["Jim", "Rim", "Dim"] to Jim, Rim & Dim
@@ -250,6 +254,12 @@
 	return reminder_text, message
 
 
+def get_pluralized_years(years):
+	if years == 1:
+		return "1 year"
+	return f"{years} years"
+
+
 def send_work_anniversary_reminder(recipients, reminder_text, anniversary_persons, message):
 	frappe.sendmail(
 		recipients=recipients,
@@ -260,5 +270,5 @@
 			anniversary_persons=anniversary_persons,
 			message=message,
 		),
-		header=_("Work Anniversary Reminder")
+		header=_("Work Anniversary Reminder"),
 	)
diff --git a/erpnext/hr/doctype/employee/test_employee.py b/erpnext/hr/doctype/employee/test_employee.py
index 67cbea6..50894b6 100644
--- a/erpnext/hr/doctype/employee/test_employee.py
+++ b/erpnext/hr/doctype/employee/test_employee.py
@@ -9,7 +9,8 @@
 import erpnext
 from erpnext.hr.doctype.employee.employee import InactiveEmployeeStatusError
 
-test_records = frappe.get_test_records('Employee')
+test_records = frappe.get_test_records("Employee")
+
 
 class TestEmployee(unittest.TestCase):
 	def test_employee_status_left(self):
@@ -21,7 +22,7 @@
 		employee2_doc.reports_to = employee1_doc.name
 		employee2_doc.save()
 		employee1_doc.reload()
-		employee1_doc.status = 'Left'
+		employee1_doc.status = "Left"
 		self.assertRaises(InactiveEmployeeStatusError, employee1_doc.save)
 
 	def test_employee_status_inactive(self):
@@ -36,11 +37,19 @@
 		employee_doc.reload()
 
 		make_holiday_list()
-		frappe.db.set_value("Company", employee_doc.company, "default_holiday_list", "Salary Slip Test Holiday List")
+		frappe.db.set_value(
+			"Company", employee_doc.company, "default_holiday_list", "Salary Slip Test Holiday List"
+		)
 
-		frappe.db.sql("""delete from `tabSalary Structure` where name='Test Inactive Employee Salary Slip'""")
-		salary_structure = make_salary_structure("Test Inactive Employee Salary Slip", "Monthly",
-			employee=employee_doc.name, company=employee_doc.company)
+		frappe.db.sql(
+			"""delete from `tabSalary Structure` where name='Test Inactive Employee Salary Slip'"""
+		)
+		salary_structure = make_salary_structure(
+			"Test Inactive Employee Salary Slip",
+			"Monthly",
+			employee=employee_doc.name,
+			company=employee_doc.company,
+		)
 		salary_slip = make_salary_slip(salary_structure.name, employee=employee_doc.name)
 
 		self.assertRaises(InactiveEmployeeStatusError, salary_slip.save)
@@ -48,38 +57,43 @@
 	def tearDown(self):
 		frappe.db.rollback()
 
+
 def make_employee(user, company=None, **kwargs):
 	if not frappe.db.get_value("User", user):
-		frappe.get_doc({
-			"doctype": "User",
-			"email": user,
-			"first_name": user,
-			"new_password": "password",
-			"send_welcome_email": 0,
-			"roles": [{"doctype": "Has Role", "role": "Employee"}]
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "User",
+				"email": user,
+				"first_name": user,
+				"new_password": "password",
+				"send_welcome_email": 0,
+				"roles": [{"doctype": "Has Role", "role": "Employee"}],
+			}
+		).insert()
 
 	if not frappe.db.get_value("Employee", {"user_id": user}):
-		employee = frappe.get_doc({
-			"doctype": "Employee",
-			"naming_series": "EMP-",
-			"first_name": user,
-			"company": company or erpnext.get_default_company(),
-			"user_id": user,
-			"date_of_birth": "1990-05-08",
-			"date_of_joining": "2013-01-01",
-			"department": frappe.get_all("Department", fields="name")[0].name,
-			"gender": "Female",
-			"company_email": user,
-			"prefered_contact_email": "Company Email",
-			"prefered_email": user,
-			"status": "Active",
-			"employment_type": "Intern"
-		})
+		employee = frappe.get_doc(
+			{
+				"doctype": "Employee",
+				"naming_series": "EMP-",
+				"first_name": user,
+				"company": company or erpnext.get_default_company(),
+				"user_id": user,
+				"date_of_birth": "1990-05-08",
+				"date_of_joining": "2013-01-01",
+				"department": frappe.get_all("Department", fields="name")[0].name,
+				"gender": "Female",
+				"company_email": user,
+				"prefered_contact_email": "Company Email",
+				"prefered_email": user,
+				"status": "Active",
+				"employment_type": "Intern",
+			}
+		)
 		if kwargs:
 			employee.update(kwargs)
 		employee.insert()
 		return employee.name
 	else:
-		frappe.db.set_value("Employee", {"employee_name":user}, "status", "Active")
-		return frappe.get_value("Employee", {"employee_name":user}, "name")
+		frappe.db.set_value("Employee", {"employee_name": user}, "status", "Active")
+		return frappe.get_value("Employee", {"employee_name": user}, "name")
diff --git a/erpnext/hr/doctype/employee/test_employee_reminders.py b/erpnext/hr/doctype/employee/test_employee_reminders.py
index a4097ab..9bde77c 100644
--- a/erpnext/hr/doctype/employee/test_employee_reminders.py
+++ b/erpnext/hr/doctype/employee/test_employee_reminders.py
@@ -21,23 +21,22 @@
 		# Create a test holiday list
 		test_holiday_dates = cls.get_test_holiday_dates()
 		test_holiday_list = make_holiday_list(
-			'TestHolidayRemindersList',
+			"TestHolidayRemindersList",
 			holiday_dates=[
-				{'holiday_date': test_holiday_dates[0], 'description': 'test holiday1'},
-				{'holiday_date': test_holiday_dates[1], 'description': 'test holiday2'},
-				{'holiday_date': test_holiday_dates[2], 'description': 'test holiday3', 'weekly_off': 1},
-				{'holiday_date': test_holiday_dates[3], 'description': 'test holiday4'},
-				{'holiday_date': test_holiday_dates[4], 'description': 'test holiday5'},
-				{'holiday_date': test_holiday_dates[5], 'description': 'test holiday6'},
+				{"holiday_date": test_holiday_dates[0], "description": "test holiday1"},
+				{"holiday_date": test_holiday_dates[1], "description": "test holiday2"},
+				{"holiday_date": test_holiday_dates[2], "description": "test holiday3", "weekly_off": 1},
+				{"holiday_date": test_holiday_dates[3], "description": "test holiday4"},
+				{"holiday_date": test_holiday_dates[4], "description": "test holiday5"},
+				{"holiday_date": test_holiday_dates[5], "description": "test holiday6"},
 			],
-			from_date=getdate()-timedelta(days=10),
-			to_date=getdate()+timedelta(weeks=5)
+			from_date=getdate() - timedelta(days=10),
+			to_date=getdate() + timedelta(weeks=5),
 		)
 
 		# Create a test employee
 		test_employee = frappe.get_doc(
-			'Employee',
-			make_employee('test@gopher.io', company="_Test Company")
+			"Employee", make_employee("test@gopher.io", company="_Test Company")
 		)
 
 		# Attach the holiday list to employee
@@ -49,16 +48,16 @@
 		cls.test_holiday_dates = test_holiday_dates
 
 		# Employee without holidays in this month/week
-		test_employee_2 = make_employee('test@empwithoutholiday.io', company="_Test Company")
-		test_employee_2 = frappe.get_doc('Employee', test_employee_2)
+		test_employee_2 = make_employee("test@empwithoutholiday.io", company="_Test Company")
+		test_employee_2 = frappe.get_doc("Employee", test_employee_2)
 
 		test_holiday_list = make_holiday_list(
-			'TestHolidayRemindersList2',
+			"TestHolidayRemindersList2",
 			holiday_dates=[
-				{'holiday_date': add_months(getdate(), 1), 'description': 'test holiday1'},
+				{"holiday_date": add_months(getdate(), 1), "description": "test holiday1"},
 			],
 			from_date=add_months(getdate(), -2),
-			to_date=add_months(getdate(), 2)
+			to_date=add_months(getdate(), 2),
 		)
 		test_employee_2.holiday_list = test_holiday_list.name
 		test_employee_2.save()
@@ -71,11 +70,11 @@
 		today_date = getdate()
 		return [
 			today_date,
-			today_date-timedelta(days=4),
-			today_date-timedelta(days=3),
-			today_date+timedelta(days=1),
-			today_date+timedelta(days=3),
-			today_date+timedelta(weeks=3)
+			today_date - timedelta(days=4),
+			today_date - timedelta(days=3),
+			today_date + timedelta(days=1),
+			today_date + timedelta(days=3),
+			today_date + timedelta(weeks=3),
 		]
 
 	def setUp(self):
@@ -88,19 +87,23 @@
 
 		self.assertTrue(is_holiday(self.test_employee.name))
 		self.assertTrue(is_holiday(self.test_employee.name, date=self.test_holiday_dates[1]))
-		self.assertFalse(is_holiday(self.test_employee.name, date=getdate()-timedelta(days=1)))
+		self.assertFalse(is_holiday(self.test_employee.name, date=getdate() - timedelta(days=1)))
 
 		# Test weekly_off holidays
 		self.assertTrue(is_holiday(self.test_employee.name, date=self.test_holiday_dates[2]))
-		self.assertFalse(is_holiday(self.test_employee.name, date=self.test_holiday_dates[2], only_non_weekly=True))
+		self.assertFalse(
+			is_holiday(self.test_employee.name, date=self.test_holiday_dates[2], only_non_weekly=True)
+		)
 
 		# Test with descriptions
 		has_holiday, descriptions = is_holiday(self.test_employee.name, with_description=True)
 		self.assertTrue(has_holiday)
-		self.assertTrue('test holiday1' in descriptions)
+		self.assertTrue("test holiday1" in descriptions)
 
 	def test_birthday_reminders(self):
-		employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0])
+		employee = frappe.get_doc(
+			"Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0]
+		)
 		employee.date_of_birth = "1992" + frappe.utils.nowdate()[4:]
 		employee.company_email = "test@example.com"
 		employee.company = "_Test Company"
@@ -124,7 +127,8 @@
 		self.assertTrue("Subject: Birthday Reminder" in email_queue[0].message)
 
 	def test_work_anniversary_reminders(self):
-		make_employee("test_work_anniversary@gmail.com",
+		make_employee(
+			"test_work_anniversary@gmail.com",
 			date_of_joining="1998" + frappe.utils.nowdate()[4:],
 			company="_Test Company",
 		)
@@ -134,7 +138,7 @@
 			send_work_anniversary_reminders,
 		)
 
-		employees_having_work_anniversary = get_employees_having_an_event_today('work_anniversary')
+		employees_having_work_anniversary = get_employees_having_an_event_today("work_anniversary")
 		employees = employees_having_work_anniversary.get("_Test Company") or []
 		user_ids = []
 		for entry in employees:
@@ -152,14 +156,15 @@
 		self.assertTrue("Subject: Work Anniversary Reminder" in email_queue[0].message)
 
 	def test_work_anniversary_reminder_not_sent_for_0_years(self):
-		make_employee("test_work_anniversary_2@gmail.com",
+		make_employee(
+			"test_work_anniversary_2@gmail.com",
 			date_of_joining=getdate(),
 			company="_Test Company",
 		)
 
 		from erpnext.hr.doctype.employee.employee_reminders import get_employees_having_an_event_today
 
-		employees_having_work_anniversary = get_employees_having_an_event_today('work_anniversary')
+		employees_having_work_anniversary = get_employees_having_an_event_today("work_anniversary")
 		employees = employees_having_work_anniversary.get("_Test Company") or []
 		user_ids = []
 		for entry in employees:
@@ -168,20 +173,18 @@
 		self.assertTrue("test_work_anniversary_2@gmail.com" not in user_ids)
 
 	def test_send_holidays_reminder_in_advance(self):
-		setup_hr_settings('Weekly')
+		setup_hr_settings("Weekly")
 
 		holidays = get_holidays_for_employee(
-					self.test_employee.get('name'),
-					getdate(), getdate() + timedelta(days=3),
-					only_non_weekly=True,
-					raise_exception=False
-				)
-
-		send_holidays_reminder_in_advance(
-			self.test_employee.get('name'),
-			holidays
+			self.test_employee.get("name"),
+			getdate(),
+			getdate() + timedelta(days=3),
+			only_non_weekly=True,
+			raise_exception=False,
 		)
 
+		send_holidays_reminder_in_advance(self.test_employee.get("name"), holidays)
+
 		email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
 		self.assertEqual(len(email_queue), 1)
 		self.assertTrue("Holidays this Week." in email_queue[0].message)
@@ -189,67 +192,69 @@
 	def test_advance_holiday_reminders_monthly(self):
 		from erpnext.hr.doctype.employee.employee_reminders import send_reminders_in_advance_monthly
 
-		setup_hr_settings('Monthly')
+		setup_hr_settings("Monthly")
 
 		# disable emp 2, set same holiday list
-		frappe.db.set_value('Employee', self.test_employee_2.name, {
-			'status': 'Left',
-			'holiday_list': self.test_employee.holiday_list
-		})
+		frappe.db.set_value(
+			"Employee",
+			self.test_employee_2.name,
+			{"status": "Left", "holiday_list": self.test_employee.holiday_list},
+		)
 
 		send_reminders_in_advance_monthly()
 		email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
 		self.assertTrue(len(email_queue) > 0)
 
 		# even though emp 2 has holiday, non-active employees should not be recipients
-		recipients = frappe.db.get_all('Email Queue Recipient', pluck='recipient')
+		recipients = frappe.db.get_all("Email Queue Recipient", pluck="recipient")
 		self.assertTrue(self.test_employee_2.user_id not in recipients)
 
 		# teardown: enable emp 2
-		frappe.db.set_value('Employee', self.test_employee_2.name, {
-			'status': 'Active',
-			'holiday_list': self.holiday_list_2.name
-		})
+		frappe.db.set_value(
+			"Employee",
+			self.test_employee_2.name,
+			{"status": "Active", "holiday_list": self.holiday_list_2.name},
+		)
 
 	def test_advance_holiday_reminders_weekly(self):
 		from erpnext.hr.doctype.employee.employee_reminders import send_reminders_in_advance_weekly
 
-		setup_hr_settings('Weekly')
+		setup_hr_settings("Weekly")
 
 		# disable emp 2, set same holiday list
-		frappe.db.set_value('Employee', self.test_employee_2.name, {
-			'status': 'Left',
-			'holiday_list': self.test_employee.holiday_list
-		})
+		frappe.db.set_value(
+			"Employee",
+			self.test_employee_2.name,
+			{"status": "Left", "holiday_list": self.test_employee.holiday_list},
+		)
 
 		send_reminders_in_advance_weekly()
 		email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
 		self.assertTrue(len(email_queue) > 0)
 
 		# even though emp 2 has holiday, non-active employees should not be recipients
-		recipients = frappe.db.get_all('Email Queue Recipient', pluck='recipient')
+		recipients = frappe.db.get_all("Email Queue Recipient", pluck="recipient")
 		self.assertTrue(self.test_employee_2.user_id not in recipients)
 
 		# teardown: enable emp 2
-		frappe.db.set_value('Employee', self.test_employee_2.name, {
-			'status': 'Active',
-			'holiday_list': self.holiday_list_2.name
-		})
+		frappe.db.set_value(
+			"Employee",
+			self.test_employee_2.name,
+			{"status": "Active", "holiday_list": self.holiday_list_2.name},
+		)
 
 	def test_reminder_not_sent_if_no_holdays(self):
-		setup_hr_settings('Monthly')
+		setup_hr_settings("Monthly")
 
 		# reminder not sent if there are no holidays
 		holidays = get_holidays_for_employee(
-			self.test_employee_2.get('name'),
-			getdate(), getdate() + timedelta(days=3),
+			self.test_employee_2.get("name"),
+			getdate(),
+			getdate() + timedelta(days=3),
 			only_non_weekly=True,
-			raise_exception=False
+			raise_exception=False,
 		)
-		send_holidays_reminder_in_advance(
-			self.test_employee_2.get('name'),
-			holidays
-		)
+		send_holidays_reminder_in_advance(self.test_employee_2.get("name"), holidays)
 		email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
 		self.assertEqual(len(email_queue), 0)
 
@@ -259,5 +264,5 @@
 	hr_settings = frappe.get_doc("HR Settings", "HR Settings")
 	hr_settings.send_holiday_reminders = 1
 	set_proceed_with_frequency_change()
-	hr_settings.frequency = frequency or 'Weekly'
-	hr_settings.save()
\ No newline at end of file
+	hr_settings.frequency = frequency or "Weekly"
+	hr_settings.save()
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js
index 7d1c7cb..37ae75a 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.js
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.js
@@ -65,9 +65,10 @@
 			);
 		}
 
-		if (frm.doc.docstatus === 1 &&
-			(flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) && flt(frm.doc.paid_amount) != flt(frm.doc.return_amount))) {
-
+		if (
+			frm.doc.docstatus === 1
+			&& (flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) - flt(frm.doc.return_amount))
+		) {
 			if (frm.doc.repay_unclaimed_amount_from_salary == 0 && frappe.model.can_create("Journal Entry")) {
 				frm.add_custom_button(__("Return"), function() {
 					frm.trigger('make_return_entry');
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py
index 79d389d..c1876b1 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.py
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.py
@@ -16,17 +16,19 @@
 class EmployeeAdvanceOverPayment(frappe.ValidationError):
 	pass
 
+
 class EmployeeAdvance(Document):
 	def onload(self):
-		self.get("__onload").make_payment_via_journal_entry = frappe.db.get_single_value('Accounts Settings',
-			'make_payment_via_journal_entry')
+		self.get("__onload").make_payment_via_journal_entry = frappe.db.get_single_value(
+			"Accounts Settings", "make_payment_via_journal_entry"
+		)
 
 	def validate(self):
 		validate_active_employee(self.employee)
 		self.set_status()
 
 	def on_cancel(self):
-		self.ignore_linked_doctypes = ('GL Entry')
+		self.ignore_linked_doctypes = "GL Entry"
 		self.set_status(update=True)
 
 	def set_status(self, update=False):
@@ -37,13 +39,23 @@
 		if self.docstatus == 0:
 			status = "Draft"
 		elif self.docstatus == 1:
-			if flt(self.claimed_amount) > 0 and flt(self.claimed_amount, precision) == flt(self.paid_amount, precision):
+			if flt(self.claimed_amount) > 0 and flt(self.claimed_amount, precision) == flt(
+				self.paid_amount, precision
+			):
 				status = "Claimed"
-			elif flt(self.return_amount) > 0 and flt(self.return_amount, precision) == flt(self.paid_amount, precision):
+			elif flt(self.return_amount) > 0 and flt(self.return_amount, precision) == flt(
+				self.paid_amount, precision
+			):
 				status = "Returned"
-			elif flt(self.claimed_amount) > 0 and (flt(self.return_amount) > 0) and total_amount == flt(self.paid_amount, precision):
+			elif (
+				flt(self.claimed_amount) > 0
+				and (flt(self.return_amount) > 0)
+				and total_amount == flt(self.paid_amount, precision)
+			):
 				status = "Partly Claimed and Returned"
-			elif flt(self.paid_amount) > 0 and flt(self.advance_amount, precision) == flt(self.paid_amount, precision):
+			elif flt(self.paid_amount) > 0 and flt(self.advance_amount, precision) == flt(
+				self.paid_amount, precision
+			):
 				status = "Paid"
 			else:
 				status = "Unpaid"
@@ -60,30 +72,30 @@
 
 		paid_amount = (
 			frappe.qb.from_(gle)
-				.select(Sum(gle.debit).as_("paid_amount"))
-				.where(
-					(gle.against_voucher_type == 'Employee Advance')
-					& (gle.against_voucher == self.name)
-					& (gle.party_type == 'Employee')
-					& (gle.party == self.employee)
-					& (gle.docstatus == 1)
-					& (gle.is_cancelled == 0)
-				)
-			).run(as_dict=True)[0].paid_amount or 0
+			.select(Sum(gle.debit).as_("paid_amount"))
+			.where(
+				(gle.against_voucher_type == "Employee Advance")
+				& (gle.against_voucher == self.name)
+				& (gle.party_type == "Employee")
+				& (gle.party == self.employee)
+				& (gle.docstatus == 1)
+				& (gle.is_cancelled == 0)
+			)
+		).run(as_dict=True)[0].paid_amount or 0
 
 		return_amount = (
 			frappe.qb.from_(gle)
-				.select(Sum(gle.credit).as_("return_amount"))
-				.where(
-					(gle.against_voucher_type == 'Employee Advance')
-					& (gle.voucher_type != 'Expense Claim')
-					& (gle.against_voucher == self.name)
-					& (gle.party_type == 'Employee')
-					& (gle.party == self.employee)
-					& (gle.docstatus == 1)
-					& (gle.is_cancelled == 0)
-				)
-			).run(as_dict=True)[0].return_amount or 0
+			.select(Sum(gle.credit).as_("return_amount"))
+			.where(
+				(gle.against_voucher_type == "Employee Advance")
+				& (gle.voucher_type != "Expense Claim")
+				& (gle.against_voucher == self.name)
+				& (gle.party_type == "Employee")
+				& (gle.party == self.employee)
+				& (gle.docstatus == 1)
+				& (gle.is_cancelled == 0)
+			)
+		).run(as_dict=True)[0].return_amount or 0
 
 		if paid_amount != 0:
 			paid_amount = flt(paid_amount) / flt(self.exchange_rate)
@@ -91,8 +103,10 @@
 			return_amount = flt(return_amount) / flt(self.exchange_rate)
 
 		if flt(paid_amount) > self.advance_amount:
-			frappe.throw(_("Row {0}# Paid Amount cannot be greater than requested advance amount"),
-				EmployeeAdvanceOverPayment)
+			frappe.throw(
+				_("Row {0}# Paid Amount cannot be greater than requested advance amount"),
+				EmployeeAdvanceOverPayment,
+			)
 
 		if flt(return_amount) > self.paid_amount - self.claimed_amount:
 			frappe.throw(_("Return amount cannot be greater unclaimed amount"))
@@ -102,7 +116,9 @@
 		self.set_status(update=True)
 
 	def update_claimed_amount(self):
-		claimed_amount = frappe.db.sql("""
+		claimed_amount = (
+			frappe.db.sql(
+				"""
 			SELECT sum(ifnull(allocated_amount, 0))
 			FROM `tabExpense Claim Advance` eca, `tabExpense Claim` ec
 			WHERE
@@ -111,7 +127,11 @@
 				AND ec.name = eca.parent
 				AND ec.docstatus=1
 				AND eca.allocated_amount > 0
-		""", self.name)[0][0] or 0
+		""",
+				self.name,
+			)[0][0]
+			or 0
+		)
 
 		frappe.db.set_value("Employee Advance", self.name, "claimed_amount", flt(claimed_amount))
 		self.reload()
@@ -120,56 +140,69 @@
 
 @frappe.whitelist()
 def get_pending_amount(employee, posting_date):
-	employee_due_amount = frappe.get_all("Employee Advance", \
-		filters = {"employee":employee, "docstatus":1, "posting_date":("<=", posting_date)}, \
-		fields = ["advance_amount", "paid_amount"])
+	employee_due_amount = frappe.get_all(
+		"Employee Advance",
+		filters={"employee": employee, "docstatus": 1, "posting_date": ("<=", posting_date)},
+		fields=["advance_amount", "paid_amount"],
+	)
 	return sum([(emp.advance_amount - emp.paid_amount) for emp in employee_due_amount])
 
+
 @frappe.whitelist()
 def make_bank_entry(dt, dn):
 	doc = frappe.get_doc(dt, dn)
-	payment_account = get_default_bank_cash_account(doc.company, account_type="Cash",
-		mode_of_payment=doc.mode_of_payment)
+	payment_account = get_default_bank_cash_account(
+		doc.company, account_type="Cash", mode_of_payment=doc.mode_of_payment
+	)
 	if not payment_account:
 		frappe.throw(_("Please set a Default Cash Account in Company defaults"))
 
-	advance_account_currency = frappe.db.get_value('Account', doc.advance_account, 'account_currency')
+	advance_account_currency = frappe.db.get_value("Account", doc.advance_account, "account_currency")
 
-	advance_amount, advance_exchange_rate = get_advance_amount_advance_exchange_rate(advance_account_currency,doc )
+	advance_amount, advance_exchange_rate = get_advance_amount_advance_exchange_rate(
+		advance_account_currency, doc
+	)
 
 	paying_amount, paying_exchange_rate = get_paying_amount_paying_exchange_rate(payment_account, doc)
 
 	je = frappe.new_doc("Journal Entry")
 	je.posting_date = nowdate()
-	je.voucher_type = 'Bank Entry'
+	je.voucher_type = "Bank Entry"
 	je.company = doc.company
-	je.remark = 'Payment against Employee Advance: ' + dn + '\n' + doc.purpose
+	je.remark = "Payment against Employee Advance: " + dn + "\n" + doc.purpose
 	je.multi_currency = 1 if advance_account_currency != payment_account.account_currency else 0
 
-	je.append("accounts", {
-		"account": doc.advance_account,
-		"account_currency": advance_account_currency,
-		"exchange_rate": flt(advance_exchange_rate),
-		"debit_in_account_currency": flt(advance_amount),
-		"reference_type": "Employee Advance",
-		"reference_name": doc.name,
-		"party_type": "Employee",
-		"cost_center": erpnext.get_default_cost_center(doc.company),
-		"party": doc.employee,
-		"is_advance": "Yes"
-	})
+	je.append(
+		"accounts",
+		{
+			"account": doc.advance_account,
+			"account_currency": advance_account_currency,
+			"exchange_rate": flt(advance_exchange_rate),
+			"debit_in_account_currency": flt(advance_amount),
+			"reference_type": "Employee Advance",
+			"reference_name": doc.name,
+			"party_type": "Employee",
+			"cost_center": erpnext.get_default_cost_center(doc.company),
+			"party": doc.employee,
+			"is_advance": "Yes",
+		},
+	)
 
-	je.append("accounts", {
-		"account": payment_account.account,
-		"cost_center": erpnext.get_default_cost_center(doc.company),
-		"credit_in_account_currency": flt(paying_amount),
-		"account_currency": payment_account.account_currency,
-		"account_type": payment_account.account_type,
-		"exchange_rate": flt(paying_exchange_rate)
-	})
+	je.append(
+		"accounts",
+		{
+			"account": payment_account.account,
+			"cost_center": erpnext.get_default_cost_center(doc.company),
+			"credit_in_account_currency": flt(paying_amount),
+			"account_currency": payment_account.account_currency,
+			"account_type": payment_account.account_type,
+			"exchange_rate": flt(paying_exchange_rate),
+		},
+	)
 
 	return je.as_dict()
 
+
 def get_advance_amount_advance_exchange_rate(advance_account_currency, doc):
 	if advance_account_currency != doc.currency:
 		advance_amount = flt(doc.advance_amount) * flt(doc.exchange_rate)
@@ -180,6 +213,7 @@
 
 	return advance_amount, advance_exchange_rate
 
+
 def get_paying_amount_paying_exchange_rate(payment_account, doc):
 	if payment_account.account_currency != doc.currency:
 		paying_amount = flt(doc.advance_amount) * flt(doc.exchange_rate)
@@ -190,6 +224,7 @@
 
 	return paying_amount, paying_exchange_rate
 
+
 @frappe.whitelist()
 def create_return_through_additional_salary(doc):
 	import json
@@ -197,7 +232,7 @@
 	if isinstance(doc, str):
 		doc = frappe._dict(json.loads(doc))
 
-	additional_salary = frappe.new_doc('Additional Salary')
+	additional_salary = frappe.new_doc("Additional Salary")
 	additional_salary.employee = doc.employee
 	additional_salary.currency = doc.currency
 	additional_salary.amount = doc.paid_amount - doc.claimed_amount
@@ -207,56 +242,81 @@
 
 	return additional_salary
 
+
 @frappe.whitelist()
-def make_return_entry(employee, company, employee_advance_name, return_amount,  advance_account, currency, exchange_rate, mode_of_payment=None):
-	bank_cash_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment)
+def make_return_entry(
+	employee,
+	company,
+	employee_advance_name,
+	return_amount,
+	advance_account,
+	currency,
+	exchange_rate,
+	mode_of_payment=None,
+):
+	bank_cash_account = get_default_bank_cash_account(
+		company, account_type="Cash", mode_of_payment=mode_of_payment
+	)
 	if not bank_cash_account:
 		frappe.throw(_("Please set a Default Cash Account in Company defaults"))
 
-	advance_account_currency = frappe.db.get_value('Account', advance_account, 'account_currency')
+	advance_account_currency = frappe.db.get_value("Account", advance_account, "account_currency")
 
-	je = frappe.new_doc('Journal Entry')
+	je = frappe.new_doc("Journal Entry")
 	je.posting_date = nowdate()
 	je.voucher_type = get_voucher_type(mode_of_payment)
 	je.company = company
-	je.remark = 'Return against Employee Advance: ' + employee_advance_name
+	je.remark = "Return against Employee Advance: " + employee_advance_name
 	je.multi_currency = 1 if advance_account_currency != bank_cash_account.account_currency else 0
 
-	advance_account_amount = flt(return_amount) if advance_account_currency==currency \
+	advance_account_amount = (
+		flt(return_amount)
+		if advance_account_currency == currency
 		else flt(return_amount) * flt(exchange_rate)
+	)
 
-	je.append('accounts', {
-		'account': advance_account,
-		'credit_in_account_currency': advance_account_amount,
-		'account_currency': advance_account_currency,
-		'exchange_rate': flt(exchange_rate) if advance_account_currency == currency else 1,
-		'reference_type': 'Employee Advance',
-		'reference_name': employee_advance_name,
-		'party_type': 'Employee',
-		'party': employee,
-		'is_advance': 'Yes',
-		'cost_center': erpnext.get_default_cost_center(company)
-	})
+	je.append(
+		"accounts",
+		{
+			"account": advance_account,
+			"credit_in_account_currency": advance_account_amount,
+			"account_currency": advance_account_currency,
+			"exchange_rate": flt(exchange_rate) if advance_account_currency == currency else 1,
+			"reference_type": "Employee Advance",
+			"reference_name": employee_advance_name,
+			"party_type": "Employee",
+			"party": employee,
+			"is_advance": "Yes",
+			"cost_center": erpnext.get_default_cost_center(company),
+		},
+	)
 
-	bank_amount = flt(return_amount) if bank_cash_account.account_currency==currency \
+	bank_amount = (
+		flt(return_amount)
+		if bank_cash_account.account_currency == currency
 		else flt(return_amount) * flt(exchange_rate)
+	)
 
-	je.append("accounts", {
-		"account": bank_cash_account.account,
-		"debit_in_account_currency": bank_amount,
-		"account_currency": bank_cash_account.account_currency,
-		"account_type": bank_cash_account.account_type,
-		"exchange_rate": flt(exchange_rate) if bank_cash_account.account_currency == currency else 1,
-		"cost_center": erpnext.get_default_cost_center(company)
-	})
+	je.append(
+		"accounts",
+		{
+			"account": bank_cash_account.account,
+			"debit_in_account_currency": bank_amount,
+			"account_currency": bank_cash_account.account_currency,
+			"account_type": bank_cash_account.account_type,
+			"exchange_rate": flt(exchange_rate) if bank_cash_account.account_currency == currency else 1,
+			"cost_center": erpnext.get_default_cost_center(company),
+		},
+	)
 
 	return je.as_dict()
 
+
 def get_voucher_type(mode_of_payment=None):
 	voucher_type = "Cash Entry"
 
 	if mode_of_payment:
-		mode_of_payment_type = frappe.get_cached_value('Mode of Payment', mode_of_payment, 'type')
+		mode_of_payment_type = frappe.get_cached_value("Mode of Payment", mode_of_payment, "type")
 		if mode_of_payment_type == "Bank":
 			voucher_type = "Bank Entry"
 
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py b/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py
index 9450258..73fac51 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py
+++ b/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py
@@ -1,16 +1,9 @@
 def get_data():
 	return {
-		'fieldname': 'employee_advance',
-		'non_standard_fieldnames': {
-			'Payment Entry': 'reference_name',
-			'Journal Entry': 'reference_name'
+		"fieldname": "employee_advance",
+		"non_standard_fieldnames": {
+			"Payment Entry": "reference_name",
+			"Journal Entry": "reference_name",
 		},
-		'transactions': [
-			{
-				'items': ['Expense Claim']
-			},
-			{
-				'items': ['Payment Entry', 'Journal Entry']
-			}
-		]
+		"transactions": [{"items": ["Expense Claim"]}, {"items": ["Payment Entry", "Journal Entry"]}],
 	}
diff --git a/erpnext/hr/doctype/employee_advance/test_employee_advance.py b/erpnext/hr/doctype/employee_advance/test_employee_advance.py
index e3c1487..81a0876 100644
--- a/erpnext/hr/doctype/employee_advance/test_employee_advance.py
+++ b/erpnext/hr/doctype/employee_advance/test_employee_advance.py
@@ -68,7 +68,9 @@
 	def test_claimed_status(self):
 		# CLAIMED Status check, full amount claimed
 		payable_account = get_payable_account("_Test Company")
-		claim = make_expense_claim(payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True)
+		claim = make_expense_claim(
+			payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True
+		)
 
 		advance = make_employee_advance(claim.employee)
 		pe = make_payment_entry(advance)
@@ -95,7 +97,9 @@
 
 	def test_partly_claimed_and_returned_status(self):
 		payable_account = get_payable_account("_Test Company")
-		claim = make_expense_claim(payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True)
+		claim = make_expense_claim(
+			payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True
+		)
 
 		advance = make_employee_advance(claim.employee)
 		pe = make_payment_entry(advance)
@@ -103,7 +107,9 @@
 
 		# PARTLY CLAIMED AND RETURNED status check
 		# 500 Claimed, 500 Returned
-		claim = make_expense_claim(payable_account, 500, 500, "_Test Company", "Travel Expenses - _TC", do_not_submit=True)
+		claim = make_expense_claim(
+			payable_account, 500, 500, "_Test Company", "Travel Expenses - _TC", do_not_submit=True
+		)
 
 		advance = make_employee_advance(claim.employee)
 		pe = make_payment_entry(advance)
@@ -125,7 +131,7 @@
 			advance_account=advance.advance_account,
 			mode_of_payment=advance.mode_of_payment,
 			currency=advance.currency,
-			exchange_rate=advance.exchange_rate
+			exchange_rate=advance.exchange_rate,
 		)
 
 		entry = frappe.get_doc(entry)
@@ -160,7 +166,9 @@
 
 		args = {"type": "Deduction"}
 		create_salary_component("Advance Salary - Deduction", **args)
-		make_salary_structure("Test Additional Salary for Advance Return", "Monthly", employee=employee_name)
+		make_salary_structure(
+			"Test Additional Salary for Advance Return", "Monthly", employee=employee_name
+		)
 
 		# additional salary for 700 first
 		advance.reload()
@@ -204,10 +212,11 @@
 
 	return journal_entry
 
+
 def make_employee_advance(employee_name, args=None):
 	doc = frappe.new_doc("Employee Advance")
 	doc.employee = employee_name
-	doc.company  = "_Test company"
+	doc.company = "_Test Company"
 	doc.purpose = "For site visit"
 	doc.currency = erpnext.get_company_currency("_Test company")
 	doc.exchange_rate = 1
@@ -233,13 +242,16 @@
 		else:
 			allocated_amount = flt(entry.paid_amount) - flt(entry.claimed_amount)
 
-		claim.append("advances", {
-			"employee_advance": entry.name,
-			"posting_date": entry.posting_date,
-			"advance_account": entry.advance_account,
-			"advance_paid": entry.paid_amount,
-			"unclaimed_amount": allocated_amount,
-			"allocated_amount": allocated_amount
-		})
+		claim.append(
+			"advances",
+			{
+				"employee_advance": entry.name,
+				"posting_date": entry.posting_date,
+				"advance_account": entry.advance_account,
+				"advance_paid": entry.paid_amount,
+				"unclaimed_amount": allocated_amount,
+				"allocated_amount": allocated_amount,
+			},
+		)
 
-	return claim
\ No newline at end of file
+	return claim
diff --git a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py
index af2ca50..43665cc 100644
--- a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py
+++ b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py
@@ -14,32 +14,31 @@
 
 
 @frappe.whitelist()
-def get_employees(date, department = None, branch = None, company = None):
+def get_employees(date, department=None, branch=None, company=None):
 	attendance_not_marked = []
 	attendance_marked = []
 	filters = {"status": "Active", "date_of_joining": ["<=", date]}
 
-	for field, value in {'department': department,
-		'branch': branch, 'company': company}.items():
+	for field, value in {"department": department, "branch": branch, "company": company}.items():
 		if value:
 			filters[field] = value
 
-	employee_list = frappe.get_list("Employee", fields=["employee", "employee_name"], filters=filters, order_by="employee_name")
+	employee_list = frappe.get_list(
+		"Employee", fields=["employee", "employee_name"], filters=filters, order_by="employee_name"
+	)
 	marked_employee = {}
-	for emp in frappe.get_list("Attendance", fields=["employee", "status"],
-							   filters={"attendance_date": date}):
-		marked_employee[emp['employee']] = emp['status']
+	for emp in frappe.get_list(
+		"Attendance", fields=["employee", "status"], filters={"attendance_date": date}
+	):
+		marked_employee[emp["employee"]] = emp["status"]
 
 	for employee in employee_list:
-		employee['status'] = marked_employee.get(employee['employee'])
-		if employee['employee'] not in marked_employee:
+		employee["status"] = marked_employee.get(employee["employee"])
+		if employee["employee"] not in marked_employee:
 			attendance_not_marked.append(employee)
 		else:
 			attendance_marked.append(employee)
-	return {
-		"marked": attendance_marked,
-		"unmarked": attendance_not_marked
-	}
+	return {"marked": attendance_marked, "unmarked": attendance_not_marked}
 
 
 @frappe.whitelist()
@@ -53,16 +52,18 @@
 		else:
 			leave_type = None
 
-		company = frappe.db.get_value("Employee", employee['employee'], "Company", cache=True)
+		company = frappe.db.get_value("Employee", employee["employee"], "Company", cache=True)
 
-		attendance=frappe.get_doc(dict(
-			doctype='Attendance',
-			employee=employee.get('employee'),
-			employee_name=employee.get('employee_name'),
-			attendance_date=getdate(date),
-			status=status,
-			leave_type=leave_type,
-			company=company
-		))
+		attendance = frappe.get_doc(
+			dict(
+				doctype="Attendance",
+				employee=employee.get("employee"),
+				employee_name=employee.get("employee_name"),
+				attendance_date=getdate(date),
+				status=status,
+				leave_type=leave_type,
+				company=company,
+			)
+		)
 		attendance.insert()
-		attendance.submit()
\ No newline at end of file
+		attendance.submit()
diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.py b/erpnext/hr/doctype/employee_checkin/employee_checkin.py
index c1d4ac7..ecee541 100644
--- a/erpnext/hr/doctype/employee_checkin/employee_checkin.py
+++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.py
@@ -5,8 +5,12 @@
 import frappe
 from frappe import _
 from frappe.model.document import Document
-from frappe.utils import cint, get_datetime
+from frappe.utils import cint, get_datetime, get_link_to_form
 
+from erpnext.hr.doctype.attendance.attendance import (
+	get_duplicate_attendance_record,
+	get_overlapping_shift_attendance,
+)
 from erpnext.hr.doctype.shift_assignment.shift_assignment import (
 	get_actual_start_end_datetime_of_shift,
 )
@@ -20,31 +24,50 @@
 		self.fetch_shift()
 
 	def validate_duplicate_log(self):
-		doc = frappe.db.exists('Employee Checkin', {
-			'employee': self.employee,
-			'time': self.time,
-			'name': ['!=', self.name]})
+		doc = frappe.db.exists(
+			"Employee Checkin", {"employee": self.employee, "time": self.time, "name": ["!=", self.name]}
+		)
 		if doc:
-			doc_link = frappe.get_desk_link('Employee Checkin', doc)
-			frappe.throw(_('This employee already has a log with the same timestamp.{0}')
-				.format("<Br>" + doc_link))
+			doc_link = frappe.get_desk_link("Employee Checkin", doc)
+			frappe.throw(
+				_("This employee already has a log with the same timestamp.{0}").format("<Br>" + doc_link)
+			)
 
 	def fetch_shift(self):
-		shift_actual_timings = get_actual_start_end_datetime_of_shift(self.employee, get_datetime(self.time), True)
-		if shift_actual_timings[0] and shift_actual_timings[1]:
-			if shift_actual_timings[2].shift_type.determine_check_in_and_check_out == 'Strictly based on Log Type in Employee Checkin' and not self.log_type and not self.skip_auto_attendance:
-				frappe.throw(_('Log Type is required for check-ins falling in the shift: {0}.').format(shift_actual_timings[2].shift_type.name))
+		shift_actual_timings = get_actual_start_end_datetime_of_shift(
+			self.employee, get_datetime(self.time), True
+		)
+		if shift_actual_timings:
+			if (
+				shift_actual_timings.shift_type.determine_check_in_and_check_out
+				== "Strictly based on Log Type in Employee Checkin"
+				and not self.log_type
+				and not self.skip_auto_attendance
+			):
+				frappe.throw(
+					_("Log Type is required for check-ins falling in the shift: {0}.").format(
+						shift_actual_timings.shift_type.name
+					)
+				)
 			if not self.attendance:
-				self.shift = shift_actual_timings[2].shift_type.name
-				self.shift_actual_start = shift_actual_timings[0]
-				self.shift_actual_end = shift_actual_timings[1]
-				self.shift_start = shift_actual_timings[2].start_datetime
-				self.shift_end = shift_actual_timings[2].end_datetime
+				self.shift = shift_actual_timings.shift_type.name
+				self.shift_actual_start = shift_actual_timings.actual_start
+				self.shift_actual_end = shift_actual_timings.actual_end
+				self.shift_start = shift_actual_timings.start_datetime
+				self.shift_end = shift_actual_timings.end_datetime
 		else:
 			self.shift = None
 
+
 @frappe.whitelist()
-def add_log_based_on_employee_field(employee_field_value, timestamp, device_id=None, log_type=None, skip_auto_attendance=0, employee_fieldname='attendance_device_id'):
+def add_log_based_on_employee_field(
+	employee_field_value,
+	timestamp,
+	device_id=None,
+	log_type=None,
+	skip_auto_attendance=0,
+	employee_fieldname="attendance_device_id",
+):
 	"""Finds the relevant Employee using the employee field value and creates a Employee Checkin.
 
 	:param employee_field_value: The value to look for in employee field.
@@ -58,11 +81,20 @@
 	if not employee_field_value or not timestamp:
 		frappe.throw(_("'employee_field_value' and 'timestamp' are required."))
 
-	employee = frappe.db.get_values("Employee", {employee_fieldname: employee_field_value}, ["name", "employee_name", employee_fieldname], as_dict=True)
+	employee = frappe.db.get_values(
+		"Employee",
+		{employee_fieldname: employee_field_value},
+		["name", "employee_name", employee_fieldname],
+		as_dict=True,
+	)
 	if employee:
 		employee = employee[0]
 	else:
-		frappe.throw(_("No Employee found for the given employee field value. '{}': {}").format(employee_fieldname,employee_field_value))
+		frappe.throw(
+			_("No Employee found for the given employee field value. '{}': {}").format(
+				employee_fieldname, employee_field_value
+			)
+		)
 
 	doc = frappe.new_doc("Employee Checkin")
 	doc.employee = employee.name
@@ -70,13 +102,24 @@
 	doc.time = timestamp
 	doc.device_id = device_id
 	doc.log_type = log_type
-	if cint(skip_auto_attendance) == 1: doc.skip_auto_attendance = '1'
+	if cint(skip_auto_attendance) == 1:
+		doc.skip_auto_attendance = "1"
 	doc.insert()
 
 	return doc
 
 
-def mark_attendance_and_link_log(logs, attendance_status, attendance_date, working_hours=None, late_entry=False, early_exit=False, in_time=None, out_time=None, shift=None):
+def mark_attendance_and_link_log(
+	logs,
+	attendance_status,
+	attendance_date,
+	working_hours=None,
+	late_entry=False,
+	early_exit=False,
+	in_time=None,
+	out_time=None,
+	shift=None,
+):
 	"""Creates an attendance and links the attendance to the Employee Checkin.
 	Note: If attendance is already present for the given date, the logs are marked as skipped and no exception is thrown.
 
@@ -87,40 +130,52 @@
 	"""
 	log_names = [x.name for x in logs]
 	employee = logs[0].employee
-	if attendance_status == 'Skip':
-		frappe.db.sql("""update `tabEmployee Checkin`
-			set skip_auto_attendance = %s
-			where name in %s""", ('1', log_names))
+
+	if attendance_status == "Skip":
+		skip_attendance_in_checkins(log_names)
 		return None
-	elif attendance_status in ('Present', 'Absent', 'Half Day'):
-		employee_doc = frappe.get_doc('Employee', employee)
-		if not frappe.db.exists('Attendance', {'employee':employee, 'attendance_date':attendance_date, 'docstatus':('!=', '2')}):
+
+	elif attendance_status in ("Present", "Absent", "Half Day"):
+		employee_doc = frappe.get_doc("Employee", employee)
+		duplicate = get_duplicate_attendance_record(employee, attendance_date, shift)
+		overlapping = get_overlapping_shift_attendance(employee, attendance_date, shift)
+
+		if not duplicate and not overlapping:
 			doc_dict = {
-				'doctype': 'Attendance',
-				'employee': employee,
-				'attendance_date': attendance_date,
-				'status': attendance_status,
-				'working_hours': working_hours,
-				'company': employee_doc.company,
-				'shift': shift,
-				'late_entry': late_entry,
-				'early_exit': early_exit,
-				'in_time': in_time,
-				'out_time': out_time
+				"doctype": "Attendance",
+				"employee": employee,
+				"attendance_date": attendance_date,
+				"status": attendance_status,
+				"working_hours": working_hours,
+				"company": employee_doc.company,
+				"shift": shift,
+				"late_entry": late_entry,
+				"early_exit": early_exit,
+				"in_time": in_time,
+				"out_time": out_time,
 			}
 			attendance = frappe.get_doc(doc_dict).insert()
 			attendance.submit()
-			frappe.db.sql("""update `tabEmployee Checkin`
+
+			if attendance_status == "Absent":
+				attendance.add_comment(
+					text=_("Employee was marked Absent for not meeting the working hours threshold.")
+				)
+
+			frappe.db.sql(
+				"""update `tabEmployee Checkin`
 				set attendance = %s
-				where name in %s""", (attendance.name, log_names))
+				where name in %s""",
+				(attendance.name, log_names),
+			)
 			return attendance
 		else:
-			frappe.db.sql("""update `tabEmployee Checkin`
-				set skip_auto_attendance = %s
-				where name in %s""", ('1', log_names))
+			skip_attendance_in_checkins(log_names)
+			add_comment_in_checkins(log_names, duplicate, overlapping)
 			return None
+
 	else:
-		frappe.throw(_('{} is an invalid Attendance Status.').format(attendance_status))
+		frappe.throw(_("{} is an invalid Attendance Status.").format(attendance_status))
 
 
 def calculate_working_hours(logs, check_in_out_type, working_hours_calc_type):
@@ -133,29 +188,35 @@
 	"""
 	total_hours = 0
 	in_time = out_time = None
-	if check_in_out_type == 'Alternating entries as IN and OUT during the same shift':
+	if check_in_out_type == "Alternating entries as IN and OUT during the same shift":
 		in_time = logs[0].time
 		if len(logs) >= 2:
 			out_time = logs[-1].time
-		if working_hours_calc_type == 'First Check-in and Last Check-out':
+		if working_hours_calc_type == "First Check-in and Last Check-out":
 			# assumption in this case: First log always taken as IN, Last log always taken as OUT
 			total_hours = time_diff_in_hours(in_time, logs[-1].time)
-		elif working_hours_calc_type == 'Every Valid Check-in and Check-out':
+		elif working_hours_calc_type == "Every Valid Check-in and Check-out":
 			logs = logs[:]
 			while len(logs) >= 2:
 				total_hours += time_diff_in_hours(logs[0].time, logs[1].time)
 				del logs[:2]
 
-	elif check_in_out_type == 'Strictly based on Log Type in Employee Checkin':
-		if working_hours_calc_type == 'First Check-in and Last Check-out':
-			first_in_log_index = find_index_in_dict(logs, 'log_type', 'IN')
-			first_in_log = logs[first_in_log_index] if first_in_log_index or first_in_log_index == 0 else None
-			last_out_log_index = find_index_in_dict(reversed(logs), 'log_type', 'OUT')
-			last_out_log = logs[len(logs)-1-last_out_log_index] if last_out_log_index or last_out_log_index == 0 else None
+	elif check_in_out_type == "Strictly based on Log Type in Employee Checkin":
+		if working_hours_calc_type == "First Check-in and Last Check-out":
+			first_in_log_index = find_index_in_dict(logs, "log_type", "IN")
+			first_in_log = (
+				logs[first_in_log_index] if first_in_log_index or first_in_log_index == 0 else None
+			)
+			last_out_log_index = find_index_in_dict(reversed(logs), "log_type", "OUT")
+			last_out_log = (
+				logs[len(logs) - 1 - last_out_log_index]
+				if last_out_log_index or last_out_log_index == 0
+				else None
+			)
 			if first_in_log and last_out_log:
 				in_time, out_time = first_in_log.time, last_out_log.time
 				total_hours = time_diff_in_hours(in_time, out_time)
-		elif working_hours_calc_type == 'Every Valid Check-in and Check-out':
+		elif working_hours_calc_type == "Every Valid Check-in and Check-out":
 			in_log = out_log = None
 			for log in logs:
 				if in_log and out_log:
@@ -165,16 +226,53 @@
 					total_hours += time_diff_in_hours(in_log.time, out_log.time)
 					in_log = out_log = None
 				if not in_log:
-					in_log = log if log.log_type == 'IN'  else None
+					in_log = log if log.log_type == "IN" else None
+					if in_log and not in_time:
+						in_time = in_log.time
 				elif not out_log:
-					out_log = log if log.log_type == 'OUT'  else None
+					out_log = log if log.log_type == "OUT" else None
+
 			if in_log and out_log:
 				out_time = out_log.time
 				total_hours += time_diff_in_hours(in_log.time, out_log.time)
+
 	return total_hours, in_time, out_time
 
+
 def time_diff_in_hours(start, end):
-	return round((end-start).total_seconds() / 3600, 1)
+	return round(float((end - start).total_seconds()) / 3600, 2)
+
 
 def find_index_in_dict(dict_list, key, value):
 	return next((index for (index, d) in enumerate(dict_list) if d[key] == value), None)
+
+
+def add_comment_in_checkins(log_names, duplicate, overlapping):
+	if duplicate:
+		text = _("Auto Attendance skipped due to duplicate attendance record: {}").format(
+			get_link_to_form("Attendance", duplicate[0].name)
+		)
+	else:
+		text = _("Auto Attendance skipped due to overlapping attendance record: {}").format(
+			get_link_to_form("Attendance", overlapping.name)
+		)
+
+	for name in log_names:
+		frappe.get_doc(
+			{
+				"doctype": "Comment",
+				"comment_type": "Comment",
+				"reference_doctype": "Employee Checkin",
+				"reference_name": name,
+				"content": text,
+			}
+		).insert(ignore_permissions=True)
+
+
+def skip_attendance_in_checkins(log_names):
+	EmployeeCheckin = frappe.qb.DocType("Employee Checkin")
+	(
+		frappe.qb.update(EmployeeCheckin)
+		.set("skip_auto_attendance", 1)
+		.where(EmployeeCheckin.name.isin(log_names))
+	).run()
diff --git a/erpnext/hr/doctype/employee_checkin/test_employee_checkin.py b/erpnext/hr/doctype/employee_checkin/test_employee_checkin.py
index 254bf9e..eb81f7d 100644
--- a/erpnext/hr/doctype/employee_checkin/test_employee_checkin.py
+++ b/erpnext/hr/doctype/employee_checkin/test_employee_checkin.py
@@ -2,10 +2,19 @@
 # See license.txt
 
 import unittest
-from datetime import timedelta
+from datetime import datetime, timedelta
 
 import frappe
-from frappe.utils import now_datetime, nowdate
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import (
+	add_days,
+	get_time,
+	get_year_ending,
+	get_year_start,
+	getdate,
+	now_datetime,
+	nowdate,
+)
 
 from erpnext.hr.doctype.employee.test_employee import make_employee
 from erpnext.hr.doctype.employee_checkin.employee_checkin import (
@@ -13,91 +22,325 @@
 	calculate_working_hours,
 	mark_attendance_and_link_log,
 )
+from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list
+from erpnext.hr.doctype.leave_application.test_leave_application import get_first_sunday
+from erpnext.hr.doctype.shift_type.test_shift_type import make_shift_assignment, setup_shift_type
+from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list
 
 
-class TestEmployeeCheckin(unittest.TestCase):
+class TestEmployeeCheckin(FrappeTestCase):
+	def setUp(self):
+		frappe.db.delete("Shift Type")
+		frappe.db.delete("Shift Assignment")
+		frappe.db.delete("Employee Checkin")
+
+		from_date = get_year_start(getdate())
+		to_date = get_year_ending(getdate())
+		self.holiday_list = make_holiday_list(from_date=from_date, to_date=to_date)
+
 	def test_add_log_based_on_employee_field(self):
 		employee = make_employee("test_add_log_based_on_employee_field@example.com")
 		employee = frappe.get_doc("Employee", employee)
-		employee.attendance_device_id = '3344'
+		employee.attendance_device_id = "3344"
 		employee.save()
 
 		time_now = now_datetime().__str__()[:-7]
-		employee_checkin = add_log_based_on_employee_field('3344', time_now, 'mumbai_first_floor', 'IN')
+		employee_checkin = add_log_based_on_employee_field("3344", time_now, "mumbai_first_floor", "IN")
 		self.assertEqual(employee_checkin.employee, employee.name)
 		self.assertEqual(employee_checkin.time, time_now)
-		self.assertEqual(employee_checkin.device_id, 'mumbai_first_floor')
-		self.assertEqual(employee_checkin.log_type, 'IN')
+		self.assertEqual(employee_checkin.device_id, "mumbai_first_floor")
+		self.assertEqual(employee_checkin.log_type, "IN")
 
 	def test_mark_attendance_and_link_log(self):
 		employee = make_employee("test_mark_attendance_and_link_log@example.com")
 		logs = make_n_checkins(employee, 3)
-		mark_attendance_and_link_log(logs, 'Skip', nowdate())
+		mark_attendance_and_link_log(logs, "Skip", nowdate())
 		log_names = [log.name for log in logs]
-		logs_count = frappe.db.count('Employee Checkin', {'name':['in', log_names], 'skip_auto_attendance':1})
+		logs_count = frappe.db.count(
+			"Employee Checkin", {"name": ["in", log_names], "skip_auto_attendance": 1}
+		)
 		self.assertEqual(logs_count, 3)
 
 		logs = make_n_checkins(employee, 4, 2)
 		now_date = nowdate()
-		frappe.db.delete('Attendance', {'employee':employee})
-		attendance = mark_attendance_and_link_log(logs, 'Present', now_date, 8.2)
+		frappe.db.delete("Attendance", {"employee": employee})
+		attendance = mark_attendance_and_link_log(logs, "Present", now_date, 8.2)
 		log_names = [log.name for log in logs]
-		logs_count = frappe.db.count('Employee Checkin', {'name':['in', log_names], 'attendance':attendance.name})
+		logs_count = frappe.db.count(
+			"Employee Checkin", {"name": ["in", log_names], "attendance": attendance.name}
+		)
 		self.assertEqual(logs_count, 4)
-		attendance_count = frappe.db.count('Attendance', {'status':'Present', 'working_hours':8.2,
-			'employee':employee, 'attendance_date':now_date})
+		attendance_count = frappe.db.count(
+			"Attendance",
+			{"status": "Present", "working_hours": 8.2, "employee": employee, "attendance_date": now_date},
+		)
 		self.assertEqual(attendance_count, 1)
 
+	def test_unlink_attendance_on_cancellation(self):
+		employee = make_employee("test_mark_attendance_and_link_log@example.com")
+		logs = make_n_checkins(employee, 3)
+
+		frappe.db.delete("Attendance", {"employee": employee})
+		attendance = mark_attendance_and_link_log(logs, "Present", nowdate(), 8.2)
+		attendance.cancel()
+
+		linked_logs = frappe.db.get_all("Employee Checkin", {"attendance": attendance.name})
+		self.assertEquals(len(linked_logs), 0)
+
 	def test_calculate_working_hours(self):
-		check_in_out_type = ['Alternating entries as IN and OUT during the same shift',
-			'Strictly based on Log Type in Employee Checkin']
-		working_hours_calc_type = ['First Check-in and Last Check-out',
-			'Every Valid Check-in and Check-out']
+		check_in_out_type = [
+			"Alternating entries as IN and OUT during the same shift",
+			"Strictly based on Log Type in Employee Checkin",
+		]
+		working_hours_calc_type = [
+			"First Check-in and Last Check-out",
+			"Every Valid Check-in and Check-out",
+		]
 		logs_type_1 = [
-			{'time':now_datetime()-timedelta(minutes=390)},
-			{'time':now_datetime()-timedelta(minutes=300)},
-			{'time':now_datetime()-timedelta(minutes=270)},
-			{'time':now_datetime()-timedelta(minutes=90)},
-			{'time':now_datetime()-timedelta(minutes=0)}
-			]
+			{"time": now_datetime() - timedelta(minutes=390)},
+			{"time": now_datetime() - timedelta(minutes=300)},
+			{"time": now_datetime() - timedelta(minutes=270)},
+			{"time": now_datetime() - timedelta(minutes=90)},
+			{"time": now_datetime() - timedelta(minutes=0)},
+		]
 		logs_type_2 = [
-			{'time':now_datetime()-timedelta(minutes=390),'log_type':'OUT'},
-			{'time':now_datetime()-timedelta(minutes=360),'log_type':'IN'},
-			{'time':now_datetime()-timedelta(minutes=300),'log_type':'OUT'},
-			{'time':now_datetime()-timedelta(minutes=290),'log_type':'IN'},
-			{'time':now_datetime()-timedelta(minutes=260),'log_type':'OUT'},
-			{'time':now_datetime()-timedelta(minutes=240),'log_type':'IN'},
-			{'time':now_datetime()-timedelta(minutes=150),'log_type':'IN'},
-			{'time':now_datetime()-timedelta(minutes=60),'log_type':'OUT'}
-			]
+			{"time": now_datetime() - timedelta(minutes=390), "log_type": "OUT"},
+			{"time": now_datetime() - timedelta(minutes=360), "log_type": "IN"},
+			{"time": now_datetime() - timedelta(minutes=300), "log_type": "OUT"},
+			{"time": now_datetime() - timedelta(minutes=290), "log_type": "IN"},
+			{"time": now_datetime() - timedelta(minutes=260), "log_type": "OUT"},
+			{"time": now_datetime() - timedelta(minutes=240), "log_type": "IN"},
+			{"time": now_datetime() - timedelta(minutes=150), "log_type": "IN"},
+			{"time": now_datetime() - timedelta(minutes=60), "log_type": "OUT"},
+		]
 		logs_type_1 = [frappe._dict(x) for x in logs_type_1]
 		logs_type_2 = [frappe._dict(x) for x in logs_type_2]
 
-		working_hours = calculate_working_hours(logs_type_1,check_in_out_type[0],working_hours_calc_type[0])
+		working_hours = calculate_working_hours(
+			logs_type_1, check_in_out_type[0], working_hours_calc_type[0]
+		)
 		self.assertEqual(working_hours, (6.5, logs_type_1[0].time, logs_type_1[-1].time))
 
-		working_hours = calculate_working_hours(logs_type_1,check_in_out_type[0],working_hours_calc_type[1])
+		working_hours = calculate_working_hours(
+			logs_type_1, check_in_out_type[0], working_hours_calc_type[1]
+		)
 		self.assertEqual(working_hours, (4.5, logs_type_1[0].time, logs_type_1[-1].time))
 
-		working_hours = calculate_working_hours(logs_type_2,check_in_out_type[1],working_hours_calc_type[0])
+		working_hours = calculate_working_hours(
+			logs_type_2, check_in_out_type[1], working_hours_calc_type[0]
+		)
 		self.assertEqual(working_hours, (5, logs_type_2[1].time, logs_type_2[-1].time))
 
-		working_hours = calculate_working_hours(logs_type_2,check_in_out_type[1],working_hours_calc_type[1])
+		working_hours = calculate_working_hours(
+			logs_type_2, check_in_out_type[1], working_hours_calc_type[1]
+		)
 		self.assertEqual(working_hours, (4.5, logs_type_2[1].time, logs_type_2[-1].time))
 
+		working_hours = calculate_working_hours(
+			[logs_type_2[1], logs_type_2[-1]], check_in_out_type[1], working_hours_calc_type[1]
+		)
+		self.assertEqual(working_hours, (5.0, logs_type_2[1].time, logs_type_2[-1].time))
+
+	def test_fetch_shift(self):
+		employee = make_employee("test_employee_checkin@example.com", company="_Test Company")
+
+		# shift setup for 8-12
+		shift_type = setup_shift_type()
+		date = getdate()
+		make_shift_assignment(shift_type.name, employee, date)
+
+		# within shift time
+		timestamp = datetime.combine(date, get_time("08:45:00"))
+		log = make_checkin(employee, timestamp)
+		self.assertEqual(log.shift, shift_type.name)
+
+		# "begin checkin before shift time" = 60 mins, so should work for 7:00:00
+		timestamp = datetime.combine(date, get_time("07:00:00"))
+		log = make_checkin(employee, timestamp)
+		self.assertEqual(log.shift, shift_type.name)
+
+		# "allow checkout after shift end time" = 60 mins, so should work for 13:00:00
+		timestamp = datetime.combine(date, get_time("13:00:00"))
+		log = make_checkin(employee, timestamp)
+		self.assertEqual(log.shift, shift_type.name)
+
+		# should not fetch this shift beyond allowed time
+		timestamp = datetime.combine(date, get_time("13:01:00"))
+		log = make_checkin(employee, timestamp)
+		self.assertIsNone(log.shift)
+
+	def test_fetch_shift_for_assignment_with_end_date(self):
+		employee = make_employee("test_employee_checkin@example.com", company="_Test Company")
+
+		# shift setup for 8-12
+		shift1 = setup_shift_type()
+		# 12:30 - 16:30
+		shift2 = setup_shift_type(shift_type="Shift 2", start_time="12:30:00", end_time="16:30:00")
+
+		date = getdate()
+		make_shift_assignment(shift1.name, employee, date, add_days(date, 15))
+		make_shift_assignment(shift2.name, employee, date, add_days(date, 15))
+
+		timestamp = datetime.combine(date, get_time("08:45:00"))
+		log = make_checkin(employee, timestamp)
+		self.assertEqual(log.shift, shift1.name)
+
+		timestamp = datetime.combine(date, get_time("12:45:00"))
+		log = make_checkin(employee, timestamp)
+		self.assertEqual(log.shift, shift2.name)
+
+		# log after end date
+		timestamp = datetime.combine(add_days(date, 16), get_time("12:45:00"))
+		log = make_checkin(employee, timestamp)
+		self.assertIsNone(log.shift)
+
+	def test_shift_start_and_end_timings(self):
+		employee = make_employee("test_employee_checkin@example.com", company="_Test Company")
+
+		# shift setup for 8-12
+		shift_type = setup_shift_type()
+		date = getdate()
+		make_shift_assignment(shift_type.name, employee, date)
+
+		timestamp = datetime.combine(date, get_time("08:45:00"))
+		log = make_checkin(employee, timestamp)
+
+		self.assertEqual(log.shift, shift_type.name)
+		self.assertEqual(log.shift_start, datetime.combine(date, get_time("08:00:00")))
+		self.assertEqual(log.shift_end, datetime.combine(date, get_time("12:00:00")))
+		self.assertEqual(log.shift_actual_start, datetime.combine(date, get_time("07:00:00")))
+		self.assertEqual(log.shift_actual_end, datetime.combine(date, get_time("13:00:00")))
+
+	def test_fetch_shift_based_on_default_shift(self):
+		employee = make_employee("test_default_shift@example.com", company="_Test Company")
+		default_shift = setup_shift_type(
+			shift_type="Default Shift", start_time="14:00:00", end_time="16:00:00"
+		)
+
+		date = getdate()
+		frappe.db.set_value("Employee", employee, "default_shift", default_shift.name)
+
+		timestamp = datetime.combine(date, get_time("14:45:00"))
+		log = make_checkin(employee, timestamp)
+
+		# should consider default shift
+		self.assertEqual(log.shift, default_shift.name)
+
+	def test_fetch_shift_spanning_over_two_days(self):
+		employee = make_employee("test_employee_checkin@example.com", company="_Test Company")
+		shift_type = setup_shift_type(
+			shift_type="Midnight Shift", start_time="23:00:00", end_time="01:00:00"
+		)
+		date = getdate()
+		next_day = add_days(date, 1)
+		make_shift_assignment(shift_type.name, employee, date)
+
+		# log falls in the first day
+		timestamp = datetime.combine(date, get_time("23:00:00"))
+		log = make_checkin(employee, timestamp)
+
+		self.assertEqual(log.shift, shift_type.name)
+		self.assertEqual(log.shift_start, datetime.combine(date, get_time("23:00:00")))
+		self.assertEqual(log.shift_end, datetime.combine(next_day, get_time("01:00:00")))
+		self.assertEqual(log.shift_actual_start, datetime.combine(date, get_time("22:00:00")))
+		self.assertEqual(log.shift_actual_end, datetime.combine(next_day, get_time("02:00:00")))
+
+		log.delete()
+
+		# log falls in the second day
+		prev_day = add_days(date, -1)
+		timestamp = datetime.combine(date, get_time("01:30:00"))
+		log = make_checkin(employee, timestamp)
+		self.assertEqual(log.shift, shift_type.name)
+		self.assertEqual(log.shift_start, datetime.combine(prev_day, get_time("23:00:00")))
+		self.assertEqual(log.shift_end, datetime.combine(date, get_time("01:00:00")))
+		self.assertEqual(log.shift_actual_start, datetime.combine(prev_day, get_time("22:00:00")))
+		self.assertEqual(log.shift_actual_end, datetime.combine(date, get_time("02:00:00")))
+
+	def test_no_shift_fetched_on_holiday_as_per_shift_holiday_list(self):
+		date = getdate()
+		from_date = get_year_start(date)
+		to_date = get_year_ending(date)
+		holiday_list = make_holiday_list(from_date=from_date, to_date=to_date)
+
+		employee = make_employee("test_shift_with_holiday@example.com", company="_Test Company")
+		setup_shift_type(shift_type="Test Holiday Shift", holiday_list=holiday_list)
+
+		first_sunday = get_first_sunday(holiday_list, for_date=date)
+		timestamp = datetime.combine(first_sunday, get_time("08:00:00"))
+		log = make_checkin(employee, timestamp)
+
+		self.assertIsNone(log.shift)
+
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
+	def test_no_shift_fetched_on_holiday_as_per_employee_holiday_list(self):
+		employee = make_employee("test_shift_with_holiday@example.com", company="_Test Company")
+		shift_type = setup_shift_type(shift_type="Test Holiday Shift")
+		shift_type.holiday_list = None
+		shift_type.save()
+
+		date = getdate()
+
+		first_sunday = get_first_sunday(self.holiday_list, for_date=date)
+		timestamp = datetime.combine(first_sunday, get_time("08:00:00"))
+		log = make_checkin(employee, timestamp)
+
+		self.assertIsNone(log.shift)
+
+	def test_consecutive_shift_assignments_overlapping_within_grace_period(self):
+		# test adjustment for start and end times if they are overlapping
+		# within "begin_check_in_before_shift_start_time" and "allow_check_out_after_shift_end_time" periods
+		employee = make_employee("test_shift@example.com", company="_Test Company")
+
+		# 8 - 12
+		shift1 = setup_shift_type()
+		# 12:30 - 16:30
+		shift2 = setup_shift_type(
+			shift_type="Consecutive Shift", start_time="12:30:00", end_time="16:30:00"
+		)
+
+		# the actual start and end times (with grace) for these shifts are 7 - 13 and 11:30 - 17:30
+		date = getdate()
+		make_shift_assignment(shift1.name, employee, date)
+		make_shift_assignment(shift2.name, employee, date)
+
+		# log at 12:30 should set shift2 and actual start as 12 and not 11:30
+		timestamp = datetime.combine(date, get_time("12:30:00"))
+		log = make_checkin(employee, timestamp)
+		self.assertEqual(log.shift, shift2.name)
+		self.assertEqual(log.shift_start, datetime.combine(date, get_time("12:30:00")))
+		self.assertEqual(log.shift_actual_start, datetime.combine(date, get_time("12:00:00")))
+
+		# log at 12:00 should set shift1 and actual end as 12 and not 1 since the next shift's grace starts
+		timestamp = datetime.combine(date, get_time("12:00:00"))
+		log = make_checkin(employee, timestamp)
+		self.assertEqual(log.shift, shift1.name)
+		self.assertEqual(log.shift_end, datetime.combine(date, get_time("12:00:00")))
+		self.assertEqual(log.shift_actual_end, datetime.combine(date, get_time("12:00:00")))
+
+		# log at 12:01 should set shift2
+		timestamp = datetime.combine(date, get_time("12:01:00"))
+		log = make_checkin(employee, timestamp)
+		self.assertEqual(log.shift, shift2.name)
+
+
 def make_n_checkins(employee, n, hours_to_reverse=1):
-	logs = [make_checkin(employee, now_datetime() - timedelta(hours=hours_to_reverse, minutes=n+1))]
-	for i in range(n-1):
-		logs.append(make_checkin(employee, now_datetime() - timedelta(hours=hours_to_reverse, minutes=n-i)))
+	logs = [make_checkin(employee, now_datetime() - timedelta(hours=hours_to_reverse, minutes=n + 1))]
+	for i in range(n - 1):
+		logs.append(
+			make_checkin(employee, now_datetime() - timedelta(hours=hours_to_reverse, minutes=n - i))
+		)
 	return logs
 
 
 def make_checkin(employee, time=now_datetime()):
-	log = frappe.get_doc({
-		"doctype": "Employee Checkin",
-		"employee" : employee,
-		"time" : time,
-		"device_id" : "device1",
-		"log_type" : "IN"
-		}).insert()
+	log = frappe.get_doc(
+		{
+			"doctype": "Employee Checkin",
+			"employee": employee,
+			"time": time,
+			"device_id": "device1",
+			"log_type": "IN",
+		}
+	).insert()
 	return log
diff --git a/erpnext/hr/doctype/employee_grade/employee_grade.json b/erpnext/hr/doctype/employee_grade/employee_grade.json
index 88b061a..4967137 100644
--- a/erpnext/hr/doctype/employee_grade/employee_grade.json
+++ b/erpnext/hr/doctype/employee_grade/employee_grade.json
@@ -8,7 +8,9 @@
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
-  "default_salary_structure"
+  "default_salary_structure",
+  "currency",
+  "default_base_pay"
  ],
  "fields": [
   {
@@ -16,14 +18,31 @@
    "fieldtype": "Link",
    "label": "Default Salary Structure",
    "options": "Salary Structure"
+  },
+  {
+   "depends_on": "default_salary_structure",
+   "fieldname": "default_base_pay",
+   "fieldtype": "Currency",
+   "label": "Default Base Pay",
+   "options": "currency"
+  },
+  {
+   "fetch_from": "default_salary_structure.currency",
+   "fieldname": "currency",
+   "fieldtype": "Link",
+   "hidden": 1,
+   "label": "Currency",
+   "options": "Currency",
+   "read_only": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-08-26 13:12:07.815330",
+ "modified": "2022-05-06 15:42:10.395508",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Employee Grade",
+ "naming_rule": "Set by user",
  "owner": "Administrator",
  "permissions": [
   {
@@ -65,5 +84,6 @@
  ],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_grade/employee_grade_dashboard.py b/erpnext/hr/doctype/employee_grade/employee_grade_dashboard.py
index 6825dae..efc68ce 100644
--- a/erpnext/hr/doctype/employee_grade/employee_grade_dashboard.py
+++ b/erpnext/hr/doctype/employee_grade/employee_grade_dashboard.py
@@ -1,11 +1,9 @@
 def get_data():
 	return {
-		'transactions': [
+		"transactions": [
 			{
-				'items': ['Employee', 'Leave Period'],
+				"items": ["Employee", "Leave Period"],
 			},
-			{
-				'items': ['Employee Onboarding Template', 'Employee Separation Template']
-			}
+			{"items": ["Employee Onboarding Template", "Employee Separation Template"]},
 		]
 	}
diff --git a/erpnext/hr/doctype/employee_grievance/employee_grievance.py b/erpnext/hr/doctype/employee_grievance/employee_grievance.py
index fd9a33b..45de79f 100644
--- a/erpnext/hr/doctype/employee_grievance/employee_grievance.py
+++ b/erpnext/hr/doctype/employee_grievance/employee_grievance.py
@@ -9,7 +9,8 @@
 class EmployeeGrievance(Document):
 	def on_submit(self):
 		if self.status not in ["Invalid", "Resolved"]:
-			frappe.throw(_("Only Employee Grievance with status {0} or {1} can be submitted").format(
-				bold("Invalid"),
-				bold("Resolved"))
+			frappe.throw(
+				_("Only Employee Grievance with status {0} or {1} can be submitted").format(
+					bold("Invalid"), bold("Resolved")
+				)
 			)
diff --git a/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py b/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py
index e2d0002..910d882 100644
--- a/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py
+++ b/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py
@@ -13,6 +13,7 @@
 	def test_create_employee_grievance(self):
 		create_employee_grievance()
 
+
 def create_employee_grievance():
 	grievance_type = create_grievance_type()
 	emp_1 = make_employee("test_emp_grievance_@example.com", company="_Test Company")
@@ -27,10 +28,10 @@
 	grievance.grievance_against = emp_2
 	grievance.description = "test descrip"
 
-	#set cause
+	# set cause
 	grievance.cause_of_grievance = "test cause"
 
-	#resolution details
+	# resolution details
 	grievance.resolution_date = today()
 	grievance.resolution_detail = "test resolution detail"
 	grievance.resolved_by = "test_emp_grievance_@example.com"
diff --git a/erpnext/hr/doctype/employee_group/test_employee_group.py b/erpnext/hr/doctype/employee_group/test_employee_group.py
index a87f400..3922f54 100644
--- a/erpnext/hr/doctype/employee_group/test_employee_group.py
+++ b/erpnext/hr/doctype/employee_group/test_employee_group.py
@@ -11,17 +11,16 @@
 class TestEmployeeGroup(unittest.TestCase):
 	pass
 
+
 def make_employee_group():
 	employee = make_employee("testemployee@example.com")
-	employee_group = frappe.get_doc({
-		"doctype": "Employee Group",
-		"employee_group_name": "_Test Employee Group",
-		"employee_list": [
-			{
-				"employee": employee
-			}
-		]
-	})
+	employee_group = frappe.get_doc(
+		{
+			"doctype": "Employee Group",
+			"employee_group_name": "_Test Employee Group",
+			"employee_list": [{"employee": employee}],
+		}
+	)
 	employee_group_exist = frappe.db.exists("Employee Group", "_Test Employee Group")
 	if not employee_group_exist:
 		employee_group.insert()
@@ -29,6 +28,7 @@
 	else:
 		return employee_group_exist
 
+
 def get_employee_group():
 	employee_group = frappe.db.exists("Employee Group", "_Test Employee Group")
 	return employee_group
diff --git a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py
index a0939a8..059f83a 100644
--- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py
+++ b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py
@@ -9,7 +9,9 @@
 from erpnext.controllers.employee_boarding_controller import EmployeeBoardingController
 
 
-class IncompleteTaskError(frappe.ValidationError): pass
+class IncompleteTaskError(frappe.ValidationError):
+	pass
+
 
 class EmployeeOnboarding(EmployeeBoardingController):
 	def validate(self):
@@ -19,12 +21,18 @@
 
 	def set_employee(self):
 		if not self.employee:
-			self.employee = frappe.db.get_value('Employee', {'job_applicant': self.job_applicant}, 'name')
+			self.employee = frappe.db.get_value("Employee", {"job_applicant": self.job_applicant}, "name")
 
 	def validate_duplicate_employee_onboarding(self):
-		emp_onboarding = frappe.db.exists("Employee Onboarding", {"job_applicant": self.job_applicant, "docstatus": ("!=", 2)})
+		emp_onboarding = frappe.db.exists(
+			"Employee Onboarding", {"job_applicant": self.job_applicant, "docstatus": ("!=", 2)}
+		)
 		if emp_onboarding and emp_onboarding != self.name:
-			frappe.throw(_("Employee Onboarding: {0} already exists for Job Applicant: {1}").format(frappe.bold(emp_onboarding), frappe.bold(self.job_applicant)))
+			frappe.throw(
+				_("Employee Onboarding: {0} already exists for Job Applicant: {1}").format(
+					frappe.bold(emp_onboarding), frappe.bold(self.job_applicant)
+				)
+			)
 
 	def validate_employee_creation(self):
 		if self.docstatus != 1:
@@ -36,7 +44,10 @@
 				else:
 					task_status = frappe.db.get_value("Task", activity.task, "status")
 					if task_status not in ["Completed", "Cancelled"]:
-						frappe.throw(_("All the mandatory tasks for employee creation are not completed yet."), IncompleteTaskError)
+						frappe.throw(
+							_("All the mandatory tasks for employee creation are not completed yet."),
+							IncompleteTaskError,
+						)
 
 	def on_submit(self):
 		super(EmployeeOnboarding, self).on_submit()
@@ -47,19 +58,29 @@
 	def on_cancel(self):
 		super(EmployeeOnboarding, self).on_cancel()
 
+
 @frappe.whitelist()
 def make_employee(source_name, target_doc=None):
 	doc = frappe.get_doc("Employee Onboarding", source_name)
 	doc.validate_employee_creation()
+
 	def set_missing_values(source, target):
 		target.personal_email = frappe.db.get_value("Job Applicant", source.job_applicant, "email_id")
 		target.status = "Active"
-	doc = get_mapped_doc("Employee Onboarding", source_name, {
+
+	doc = get_mapped_doc(
+		"Employee Onboarding",
+		source_name,
+		{
 			"Employee Onboarding": {
 				"doctype": "Employee",
 				"field_map": {
 					"first_name": "employee_name",
 					"employee_grade": "grade",
-				}}
-		}, target_doc, set_missing_values)
+				},
+			}
+		},
+		target_doc,
+		set_missing_values,
+	)
 	return doc
diff --git a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
index 0fb821d..9d91e4b 100644
--- a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
+++ b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
@@ -16,8 +16,8 @@
 
 class TestEmployeeOnboarding(unittest.TestCase):
 	def setUp(self):
-		if frappe.db.exists('Employee Onboarding', {'employee_name': 'Test Researcher'}):
-			frappe.delete_doc('Employee Onboarding', {'employee_name': 'Test Researcher'})
+		if frappe.db.exists("Employee Onboarding", {"employee_name": "Test Researcher"}):
+			frappe.delete_doc("Employee Onboarding", {"employee_name": "Test Researcher"})
 
 		project = "Employee Onboarding : test@researcher.com"
 		frappe.db.sql("delete from tabProject where name=%s", project)
@@ -26,100 +26,109 @@
 	def test_employee_onboarding_incomplete_task(self):
 		onboarding = create_employee_onboarding()
 
-		project_name = frappe.db.get_value('Project', onboarding.project, 'project_name')
-		self.assertEqual(project_name, 'Employee Onboarding : test@researcher.com')
+		project_name = frappe.db.get_value("Project", onboarding.project, "project_name")
+		self.assertEqual(project_name, "Employee Onboarding : test@researcher.com")
 
 		# don't allow making employee if onboarding is not complete
 		self.assertRaises(IncompleteTaskError, make_employee, onboarding.name)
 
 		# boarding status
-		self.assertEqual(onboarding.boarding_status, 'Pending')
+		self.assertEqual(onboarding.boarding_status, "Pending")
 
 		# start and end dates
-		start_date, end_date = frappe.db.get_value('Task', onboarding.activities[0].task, ['exp_start_date', 'exp_end_date'])
+		start_date, end_date = frappe.db.get_value(
+			"Task", onboarding.activities[0].task, ["exp_start_date", "exp_end_date"]
+		)
 		self.assertEqual(getdate(start_date), getdate(onboarding.boarding_begins_on))
 		self.assertEqual(getdate(end_date), add_days(start_date, onboarding.activities[0].duration))
 
-		start_date, end_date = frappe.db.get_value('Task', onboarding.activities[1].task, ['exp_start_date', 'exp_end_date'])
-		self.assertEqual(getdate(start_date), add_days(onboarding.boarding_begins_on, onboarding.activities[0].duration))
+		start_date, end_date = frappe.db.get_value(
+			"Task", onboarding.activities[1].task, ["exp_start_date", "exp_end_date"]
+		)
+		self.assertEqual(
+			getdate(start_date), add_days(onboarding.boarding_begins_on, onboarding.activities[0].duration)
+		)
 		self.assertEqual(getdate(end_date), add_days(start_date, onboarding.activities[1].duration))
 
 		# complete the task
-		project = frappe.get_doc('Project', onboarding.project)
-		for task in frappe.get_all('Task', dict(project=project.name)):
-			task = frappe.get_doc('Task', task.name)
-			task.status = 'Completed'
+		project = frappe.get_doc("Project", onboarding.project)
+		for task in frappe.get_all("Task", dict(project=project.name)):
+			task = frappe.get_doc("Task", task.name)
+			task.status = "Completed"
 			task.save()
 
 		# boarding status
 		onboarding.reload()
-		self.assertEqual(onboarding.boarding_status, 'Completed')
+		self.assertEqual(onboarding.boarding_status, "Completed")
 
 		# make employee
 		onboarding.reload()
 		employee = make_employee(onboarding.name)
 		employee.first_name = employee.employee_name
 		employee.date_of_joining = getdate()
-		employee.date_of_birth = '1990-05-08'
-		employee.gender = 'Female'
+		employee.date_of_birth = "1990-05-08"
+		employee.gender = "Female"
 		employee.insert()
-		self.assertEqual(employee.employee_name, 'Test Researcher')
+		self.assertEqual(employee.employee_name, "Test Researcher")
 
 	def tearDown(self):
 		frappe.db.rollback()
 
 
 def get_job_applicant():
-	if frappe.db.exists('Job Applicant', 'test@researcher.com'):
-		return frappe.get_doc('Job Applicant', 'test@researcher.com')
-	applicant = frappe.new_doc('Job Applicant')
-	applicant.applicant_name = 'Test Researcher'
-	applicant.email_id = 'test@researcher.com'
-	applicant.designation = 'Researcher'
-	applicant.status = 'Open'
-	applicant.cover_letter = 'I am a great Researcher.'
+	if frappe.db.exists("Job Applicant", "test@researcher.com"):
+		return frappe.get_doc("Job Applicant", "test@researcher.com")
+	applicant = frappe.new_doc("Job Applicant")
+	applicant.applicant_name = "Test Researcher"
+	applicant.email_id = "test@researcher.com"
+	applicant.designation = "Researcher"
+	applicant.status = "Open"
+	applicant.cover_letter = "I am a great Researcher."
 	applicant.insert()
 	return applicant
 
+
 def get_job_offer(applicant_name):
-	job_offer = frappe.db.exists('Job Offer', {'job_applicant': applicant_name})
+	job_offer = frappe.db.exists("Job Offer", {"job_applicant": applicant_name})
 	if job_offer:
-		return frappe.get_doc('Job Offer', job_offer)
+		return frappe.get_doc("Job Offer", job_offer)
 
 	job_offer = create_job_offer(job_applicant=applicant_name)
 	job_offer.submit()
 	return job_offer
 
+
 def create_employee_onboarding():
 	applicant = get_job_applicant()
 	job_offer = get_job_offer(applicant.name)
 
-	holiday_list = make_holiday_list('_Test Employee Boarding')
-	holiday_list = frappe.get_doc('Holiday List', holiday_list)
+	holiday_list = make_holiday_list("_Test Employee Boarding")
+	holiday_list = frappe.get_doc("Holiday List", holiday_list)
 	holiday_list.holidays = []
 	holiday_list.save()
 
-	onboarding = frappe.new_doc('Employee Onboarding')
+	onboarding = frappe.new_doc("Employee Onboarding")
 	onboarding.job_applicant = applicant.name
 	onboarding.job_offer = job_offer.name
 	onboarding.date_of_joining = onboarding.boarding_begins_on = getdate()
-	onboarding.company = '_Test Company'
+	onboarding.company = "_Test Company"
 	onboarding.holiday_list = holiday_list.name
-	onboarding.designation = 'Researcher'
-	onboarding.append('activities', {
-		'activity_name': 'Assign ID Card',
-		'role': 'HR User',
-		'required_for_employee_creation': 1,
-		'begin_on': 0,
-		'duration': 1
-	})
-	onboarding.append('activities', {
-		'activity_name': 'Assign a laptop',
-		'role': 'HR User',
-		'begin_on': 1,
-		'duration': 1
-	})
-	onboarding.status = 'Pending'
+	onboarding.designation = "Researcher"
+	onboarding.append(
+		"activities",
+		{
+			"activity_name": "Assign ID Card",
+			"role": "HR User",
+			"required_for_employee_creation": 1,
+			"begin_on": 0,
+			"duration": 1,
+		},
+	)
+	onboarding.append(
+		"activities",
+		{"activity_name": "Assign a laptop", "role": "HR User", "begin_on": 1, "duration": 1},
+	)
+	onboarding.status = "Pending"
 	onboarding.insert()
 	onboarding.submit()
 
diff --git a/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template_dashboard.py b/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template_dashboard.py
index 3b846a0..93237ee 100644
--- a/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template_dashboard.py
+++ b/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template_dashboard.py
@@ -1,9 +1,7 @@
 def get_data():
-     return {
-        'fieldname': 'employee_onboarding_template',
-        'transactions': [
-            {
-                'items': ['Employee Onboarding']
-            },
-        ],
-    }
+	return {
+		"fieldname": "employee_onboarding_template",
+		"transactions": [
+			{"items": ["Employee Onboarding"]},
+		],
+	}
diff --git a/erpnext/hr/doctype/employee_promotion/employee_promotion.json b/erpnext/hr/doctype/employee_promotion/employee_promotion.json
index e0fbd23..173573e 100644
--- a/erpnext/hr/doctype/employee_promotion/employee_promotion.json
+++ b/erpnext/hr/doctype/employee_promotion/employee_promotion.json
@@ -1,397 +1,172 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "autoname": "HR-EMP-PRO-.YYYY.-.#####", 
- "beta": 0, 
- "creation": "2018-04-13 18:33:59.476562", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "actions": [],
+ "autoname": "HR-EMP-PRO-.YYYY.-.#####",
+ "creation": "2018-04-13 18:33:59.476562",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "employee",
+  "employee_name",
+  "department",
+  "salary_currency",
+  "column_break_3",
+  "promotion_date",
+  "company",
+  "details_section",
+  "promotion_details",
+  "salary_details_section",
+  "current_ctc",
+  "column_break_12",
+  "revised_ctc",
+  "amended_from"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "employee", 
-   "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": "Employee", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Employee", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "employee",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Employee",
+   "options": "Employee",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_from": "employee.employee_name", 
-   "fieldname": "employee_name", 
-   "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": "Employee Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "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
-  }, 
+   "fetch_from": "employee.employee_name",
+   "fieldname": "employee_name",
+   "fieldtype": "Data",
+   "label": "Employee Name",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_from": "employee.department", 
-   "fieldname": "department", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Department", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Department", 
-   "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
-  }, 
+   "fetch_from": "employee.department",
+   "fieldname": "department",
+   "fieldtype": "Link",
+   "label": "Department",
+   "options": "Department",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_3", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "promotion_date", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Promotion Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "promotion_date",
+   "fieldtype": "Date",
+   "label": "Promotion Date",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_from": "employee.company", 
-   "fieldname": "company", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 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
-  }, 
+   "fetch_from": "employee.company",
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "details_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": "Employee Promotion Details", 
-   "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
-  }, 
+   "description": "Set the properties that should be updated in the Employee master on promotion submission",
+   "fieldname": "details_section",
+   "fieldtype": "Section Break",
+   "label": "Employee Promotion Details"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "promotion_details", 
-   "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": "Employee Promotion Detail", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Employee Property History", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "promotion_details",
+   "fieldtype": "Table",
+   "options": "Employee Property History",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "amended_from", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Amended From", 
-   "length": 0, 
-   "no_copy": 1, 
-   "options": "Employee Promotion", 
-   "permlevel": 0, 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Employee Promotion",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "salary_details_section",
+   "fieldtype": "Section Break",
+   "label": "Salary Details"
+  },
+  {
+   "fieldname": "column_break_12",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fetch_from": "employee.salary_currency",
+   "fieldname": "salary_currency",
+   "fieldtype": "Link",
+   "label": "Salary Currency",
+   "options": "Currency",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "employee.ctc",
+   "fetch_if_empty": 1,
+   "fieldname": "current_ctc",
+   "fieldtype": "Currency",
+   "label": "Current CTC",
+   "mandatory_depends_on": "revised_ctc",
+   "options": "salary_currency"
+  },
+  {
+   "depends_on": "current_ctc",
+   "fieldname": "revised_ctc",
+   "fieldtype": "Currency",
+   "label": "Revised CTC",
+   "options": "salary_currency"
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 1, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2018-08-21 16:15:40.284987", 
- "modified_by": "Administrator", 
- "module": "HR", 
- "name": "Employee Promotion", 
- "name_case": "", 
- "owner": "Administrator", 
+ ],
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2022-04-22 18:47:10.168744",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Employee Promotion",
+ "naming_rule": "Expression (old style)",
+ "owner": "Administrator",
  "permissions": [
   {
-   "amend": 0, 
-   "cancel": 0, 
-   "create": 0, 
-   "delete": 0, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Employee", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 0
-  }, 
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Employee",
+   "share": 1
+  },
   {
-   "amend": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 0, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "HR User", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 1, 
+   "create": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "submit": 1,
    "write": 1
-  }, 
+  },
   {
-   "amend": 1, 
-   "cancel": 1, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "HR Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 1, 
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "submit": 1,
    "write": 1
   }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "title_field": "employee_name", 
- "track_changes": 1, 
- "track_seen": 0, 
- "track_views": 0
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": [],
+ "title_field": "employee_name",
+ "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_promotion/employee_promotion.py b/erpnext/hr/doctype/employee_promotion/employee_promotion.py
index cf6156e..8c802e9 100644
--- a/erpnext/hr/doctype/employee_promotion/employee_promotion.py
+++ b/erpnext/hr/doctype/employee_promotion/employee_promotion.py
@@ -16,15 +16,27 @@
 
 	def before_submit(self):
 		if getdate(self.promotion_date) > getdate():
-			frappe.throw(_("Employee Promotion cannot be submitted before Promotion Date"),
-				frappe.DocstatusTransitionError)
+			frappe.throw(
+				_("Employee Promotion cannot be submitted before Promotion Date"),
+				frappe.DocstatusTransitionError,
+			)
 
 	def on_submit(self):
 		employee = frappe.get_doc("Employee", self.employee)
-		employee = update_employee_work_history(employee, self.promotion_details, date=self.promotion_date)
+		employee = update_employee_work_history(
+			employee, self.promotion_details, date=self.promotion_date
+		)
+
+		if self.revised_ctc:
+			employee.ctc = self.revised_ctc
+
 		employee.save()
 
 	def on_cancel(self):
 		employee = frappe.get_doc("Employee", self.employee)
 		employee = update_employee_work_history(employee, self.promotion_details, cancel=True)
+
+		if self.revised_ctc:
+			employee.ctc = self.current_ctc
+
 		employee.save()
diff --git a/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py b/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py
index fc9d195..71bb1a6 100644
--- a/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py
+++ b/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py
@@ -4,33 +4,94 @@
 import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import add_days, getdate
 
 from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee
 
 
-class TestEmployeePromotion(unittest.TestCase):
+class TestEmployeePromotion(FrappeTestCase):
 	def setUp(self):
-		self.employee = make_employee("employee@promotions.com")
-		frappe.db.sql("""delete from `tabEmployee Promotion`""")
+		frappe.db.delete("Employee Promotion")
 
 	def test_submit_before_promotion_date(self):
-		promotion_obj = frappe.get_doc({
-			"doctype": "Employee Promotion",
-			"employee": self.employee,
-			"promotion_details" :[
-				{
-				"property": "Designation",
-				"current": "Software Developer",
-				"new": "Project Manager",
-				"fieldname": "designation"
-				}
-			]
-		})
-		promotion_obj.promotion_date = add_days(getdate(), 1)
-		promotion_obj.save()
-		self.assertRaises(frappe.DocstatusTransitionError, promotion_obj.submit)
-		promotion = frappe.get_doc("Employee Promotion", promotion_obj.name)
+		employee = make_employee("employee@promotions.com")
+		promotion = frappe.get_doc(
+			{
+				"doctype": "Employee Promotion",
+				"employee": employee,
+				"promotion_details": [
+					{
+						"property": "Designation",
+						"current": "Software Developer",
+						"new": "Project Manager",
+						"fieldname": "designation",
+					}
+				],
+			}
+		)
+		promotion.promotion_date = add_days(getdate(), 1)
+		self.assertRaises(frappe.DocstatusTransitionError, promotion.submit)
+
 		promotion.promotion_date = getdate()
 		promotion.submit()
 		self.assertEqual(promotion.docstatus, 1)
+
+	def test_employee_history(self):
+		for grade in ["L1", "L2"]:
+			frappe.get_doc({"doctype": "Employee Grade", "__newname": grade}).insert()
+
+		employee = make_employee(
+			"test_employee_promotion@example.com",
+			company="_Test Company",
+			date_of_birth=getdate("30-09-1980"),
+			date_of_joining=getdate("01-10-2021"),
+			designation="Software Developer",
+			grade="L1",
+			salary_currency="INR",
+			ctc="500000",
+		)
+
+		promotion = frappe.get_doc(
+			{
+				"doctype": "Employee Promotion",
+				"employee": employee,
+				"promotion_date": getdate(),
+				"revised_ctc": "1000000",
+				"promotion_details": [
+					{
+						"property": "Designation",
+						"current": "Software Developer",
+						"new": "Project Manager",
+						"fieldname": "designation",
+					},
+					{"property": "Grade", "current": "L1", "new": "L2", "fieldname": "grade"},
+				],
+			}
+		).submit()
+
+		# employee fields updated
+		employee = frappe.get_doc("Employee", employee)
+		self.assertEqual(employee.grade, "L2")
+		self.assertEqual(employee.designation, "Project Manager")
+		self.assertEqual(employee.ctc, 1000000)
+
+		# internal work history updated
+		self.assertEqual(employee.internal_work_history[0].designation, "Software Developer")
+		self.assertEqual(employee.internal_work_history[0].from_date, getdate("01-10-2021"))
+
+		self.assertEqual(employee.internal_work_history[1].designation, "Project Manager")
+		self.assertEqual(employee.internal_work_history[1].from_date, getdate())
+
+		promotion.cancel()
+		employee.reload()
+
+		# fields restored
+		self.assertEqual(employee.grade, "L1")
+		self.assertEqual(employee.designation, "Software Developer")
+		self.assertEqual(employee.ctc, 500000)
+
+		# internal work history updated on cancellation
+		self.assertEqual(len(employee.internal_work_history), 1)
+		self.assertEqual(employee.internal_work_history[0].designation, "Software Developer")
+		self.assertEqual(employee.internal_work_history[0].from_date, getdate("01-10-2021"))
diff --git a/erpnext/hr/doctype/employee_referral/employee_referral.py b/erpnext/hr/doctype/employee_referral/employee_referral.py
index eaa42c7..47cbfbc 100644
--- a/erpnext/hr/doctype/employee_referral/employee_referral.py
+++ b/erpnext/hr/doctype/employee_referral/employee_referral.py
@@ -30,7 +30,7 @@
 @frappe.whitelist()
 def create_job_applicant(source_name, target_doc=None):
 	emp_ref = frappe.get_doc("Employee Referral", source_name)
-	#just for Api call if some set status apart from default Status
+	# just for Api call if some set status apart from default Status
 	status = emp_ref.status
 	if emp_ref.status in ["Pending", "In process"]:
 		status = "Open"
@@ -47,9 +47,13 @@
 	job_applicant.resume_link = emp_ref.resume_link
 	job_applicant.save()
 
-	frappe.msgprint(_("Job Applicant {0} created successfully.").format(
-		get_link_to_form("Job Applicant", job_applicant.name)),
-		title=_("Success"), indicator="green")
+	frappe.msgprint(
+		_("Job Applicant {0} created successfully.").format(
+			get_link_to_form("Job Applicant", job_applicant.name)
+		),
+		title=_("Success"),
+		indicator="green",
+	)
 
 	emp_ref.db_set("status", "In Process")
 
@@ -60,7 +64,6 @@
 def create_additional_salary(doc):
 	import json
 
-
 	if isinstance(doc, str):
 		doc = frappe._dict(json.loads(doc))
 
diff --git a/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py b/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py
index 07c2402..4d683fb 100644
--- a/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py
+++ b/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py
@@ -1,13 +1,8 @@
 def get_data():
 	return {
-		'fieldname': 'employee_referral',
-		'non_standard_fieldnames': {
-			'Additional Salary': 'ref_docname'
-		},
-		'transactions': [
-			{
-				'items': ['Job Applicant', 'Additional Salary']
-			},
-
-		]
+		"fieldname": "employee_referral",
+		"non_standard_fieldnames": {"Additional Salary": "ref_docname"},
+		"transactions": [
+			{"items": ["Job Applicant", "Additional Salary"]},
+		],
 	}
diff --git a/erpnext/hr/doctype/employee_referral/test_employee_referral.py b/erpnext/hr/doctype/employee_referral/test_employee_referral.py
index 529e355..475a935 100644
--- a/erpnext/hr/doctype/employee_referral/test_employee_referral.py
+++ b/erpnext/hr/doctype/employee_referral/test_employee_referral.py
@@ -15,7 +15,6 @@
 
 
 class TestEmployeeReferral(unittest.TestCase):
-
 	def setUp(self):
 		frappe.db.sql("DELETE FROM `tabJob Applicant`")
 		frappe.db.sql("DELETE FROM `tabEmployee Referral`")
@@ -23,13 +22,12 @@
 	def test_workflow_and_status_sync(self):
 		emp_ref = create_employee_referral()
 
-		#Check Initial status
+		# Check Initial status
 		self.assertTrue(emp_ref.status, "Pending")
 
 		job_applicant = create_job_applicant(emp_ref.name)
 
-
-		#Check status sync
+		# Check status sync
 		emp_ref.reload()
 		self.assertTrue(emp_ref.status, "In Process")
 
@@ -47,7 +45,6 @@
 		emp_ref.reload()
 		self.assertTrue(emp_ref.status, "Accepted")
 
-
 		# Check for Referral reference in additional salary
 
 		add_sal = create_additional_salary(emp_ref)
diff --git a/erpnext/hr/doctype/employee_separation/test_employee_separation.py b/erpnext/hr/doctype/employee_separation/test_employee_separation.py
index f83c1e5..df31d09 100644
--- a/erpnext/hr/doctype/employee_separation/test_employee_separation.py
+++ b/erpnext/hr/doctype/employee_separation/test_employee_separation.py
@@ -6,44 +6,43 @@
 import frappe
 from frappe.utils import getdate
 
-test_dependencies = ['Employee Onboarding']
+test_dependencies = ["Employee Onboarding"]
+
 
 class TestEmployeeSeparation(unittest.TestCase):
 	def test_employee_separation(self):
 		separation = create_employee_separation()
 
 		self.assertEqual(separation.docstatus, 1)
-		self.assertEqual(separation.boarding_status, 'Pending')
+		self.assertEqual(separation.boarding_status, "Pending")
 
-		project = frappe.get_doc('Project', separation.project)
-		project.percent_complete_method = 'Manual'
-		project.status = 'Completed'
+		project = frappe.get_doc("Project", separation.project)
+		project.percent_complete_method = "Manual"
+		project.status = "Completed"
 		project.save()
 
 		separation.reload()
-		self.assertEqual(separation.boarding_status, 'Completed')
+		self.assertEqual(separation.boarding_status, "Completed")
 
 		separation.cancel()
-		self.assertEqual(separation.project, '')
+		self.assertEqual(separation.project, "")
 
 	def tearDown(self):
-		for entry in frappe.get_all('Employee Separation'):
-			doc = frappe.get_doc('Employee Separation', entry.name)
+		for entry in frappe.get_all("Employee Separation"):
+			doc = frappe.get_doc("Employee Separation", entry.name)
 			if doc.docstatus == 1:
 				doc.cancel()
 			doc.delete()
 
+
 def create_employee_separation():
-	employee = frappe.db.get_value('Employee', {'status': 'Active', 'company': '_Test Company'})
-	separation = frappe.new_doc('Employee Separation')
+	employee = frappe.db.get_value("Employee", {"status": "Active", "company": "_Test Company"})
+	separation = frappe.new_doc("Employee Separation")
 	separation.employee = employee
 	separation.boarding_begins_on = getdate()
-	separation.company = '_Test Company'
-	separation.append('activities', {
-		'activity_name': 'Deactivate Employee',
-		'role': 'HR User'
-	})
-	separation.boarding_status = 'Pending'
+	separation.company = "_Test Company"
+	separation.append("activities", {"activity_name": "Deactivate Employee", "role": "HR User"})
+	separation.boarding_status = "Pending"
 	separation.insert()
 	separation.submit()
 	return separation
diff --git a/erpnext/hr/doctype/employee_separation_template/employee_separation_template_dashboard.py b/erpnext/hr/doctype/employee_separation_template/employee_separation_template_dashboard.py
index 6e2a83e..3ffd8dd 100644
--- a/erpnext/hr/doctype/employee_separation_template/employee_separation_template_dashboard.py
+++ b/erpnext/hr/doctype/employee_separation_template/employee_separation_template_dashboard.py
@@ -1,9 +1,7 @@
 def get_data():
-     return {
-        'fieldname': 'employee_separation_template',
-        'transactions': [
-            {
-                'items': ['Employee Separation']
-            },
-        ],
-    }
+	return {
+		"fieldname": "employee_separation_template",
+		"transactions": [
+			{"items": ["Employee Separation"]},
+		],
+	}
diff --git a/erpnext/hr/doctype/employee_transfer/employee_transfer.py b/erpnext/hr/doctype/employee_transfer/employee_transfer.py
index f927d41..6dbefe5 100644
--- a/erpnext/hr/doctype/employee_transfer/employee_transfer.py
+++ b/erpnext/hr/doctype/employee_transfer/employee_transfer.py
@@ -13,8 +13,10 @@
 class EmployeeTransfer(Document):
 	def before_submit(self):
 		if getdate(self.transfer_date) > getdate():
-			frappe.throw(_("Employee Transfer cannot be submitted before Transfer Date"),
-				frappe.DocstatusTransitionError)
+			frappe.throw(
+				_("Employee Transfer cannot be submitted before Transfer Date"),
+				frappe.DocstatusTransitionError,
+			)
 
 	def on_submit(self):
 		employee = frappe.get_doc("Employee", self.employee)
@@ -22,22 +24,26 @@
 			new_employee = frappe.copy_doc(employee)
 			new_employee.name = None
 			new_employee.employee_number = None
-			new_employee = update_employee_work_history(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
 				new_employee.company = self.new_company
-			#move user_id to new employee before insert
+			# move user_id to new employee before insert
 			if employee.user_id and not self.validate_user_in_details():
 				new_employee.user_id = employee.user_id
 				employee.db_set("user_id", "")
 			new_employee.insert()
 			self.db_set("new_employee_id", new_employee.name)
-			#relieve the old employee
+			# relieve the old employee
 			employee.db_set("relieving_date", self.transfer_date)
 			employee.db_set("status", "Left")
 		else:
-			employee = update_employee_work_history(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
@@ -47,14 +53,18 @@
 		employee = frappe.get_doc("Employee", self.employee)
 		if self.create_new_employee_id:
 			if self.new_employee_id:
-				frappe.throw(_("Please delete the Employee {0} to cancel this document").format(
-					"<a href='/app/Form/Employee/{0}'>{0}</a>".format(self.new_employee_id)
-				))
-			#mark the employee as active
+				frappe.throw(
+					_("Please delete the Employee {0} to cancel this document").format(
+						"<a href='/app/Form/Employee/{0}'>{0}</a>".format(self.new_employee_id)
+					)
+				)
+			# mark the employee as active
 			employee.status = "Active"
-			employee.relieving_date = ''
+			employee.relieving_date = ""
 		else:
-			employee = update_employee_work_history(employee, self.transfer_details, date=self.transfer_date, 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 64eee40..37a190a 100644
--- a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py
+++ b/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py
@@ -19,18 +19,20 @@
 	def test_submit_before_transfer_date(self):
 		make_employee("employee2@transfers.com")
 
-		transfer_obj = frappe.get_doc({
-			"doctype": "Employee Transfer",
-			"employee": frappe.get_value("Employee", {"user_id":"employee2@transfers.com"}, "name"),
-			"transfer_details" :[
-				{
-				"property": "Designation",
-				"current": "Software Developer",
-				"new": "Project Manager",
-				"fieldname": "designation"
-				}
-			]
-		})
+		transfer_obj = frappe.get_doc(
+			{
+				"doctype": "Employee Transfer",
+				"employee": frappe.get_value("Employee", {"user_id": "employee2@transfers.com"}, "name"),
+				"transfer_details": [
+					{
+						"property": "Designation",
+						"current": "Software Developer",
+						"new": "Project Manager",
+						"fieldname": "designation",
+					}
+				],
+			}
+		)
 		transfer_obj.transfer_date = add_days(getdate(), 1)
 		transfer_obj.save()
 		self.assertRaises(frappe.DocstatusTransitionError, transfer_obj.submit)
@@ -42,32 +44,35 @@
 	def test_new_employee_creation(self):
 		make_employee("employee3@transfers.com")
 
-		transfer = frappe.get_doc({
-			"doctype": "Employee Transfer",
-			"employee": frappe.get_value("Employee", {"user_id":"employee3@transfers.com"}, "name"),
-			"create_new_employee_id": 1,
-			"transfer_date": getdate(),
-			"transfer_details" :[
-				{
-				"property": "Designation",
-				"current": "Software Developer",
-				"new": "Project Manager",
-				"fieldname": "designation"
-				}
-			]
-		}).insert()
+		transfer = frappe.get_doc(
+			{
+				"doctype": "Employee Transfer",
+				"employee": frappe.get_value("Employee", {"user_id": "employee3@transfers.com"}, "name"),
+				"create_new_employee_id": 1,
+				"transfer_date": getdate(),
+				"transfer_details": [
+					{
+						"property": "Designation",
+						"current": "Software Developer",
+						"new": "Project Manager",
+						"fieldname": "designation",
+					}
+				],
+			}
+		).insert()
 		transfer.submit()
 		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):
-		employee = make_employee("employee4@transfers.com",
+		employee = make_employee(
+			"employee4@transfers.com",
 			company="Test Company",
 			date_of_birth=getdate("30-09-1980"),
 			date_of_joining=getdate("01-10-2021"),
 			department="Accounts - TC",
-			designation="Accountant"
+			designation="Accountant",
 		)
 		transfer = create_employee_transfer(employee)
 
@@ -94,36 +99,40 @@
 
 def create_company():
 	if not frappe.db.exists("Company", "Test Company"):
-		frappe.get_doc({
-			"doctype": "Company",
-			"company_name": "Test Company",
-			"default_currency": "INR",
-			"country": "India"
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Company",
+				"company_name": "Test Company",
+				"default_currency": "INR",
+				"country": "India",
+			}
+		).insert()
 
 
 def create_employee_transfer(employee):
-	doc = frappe.get_doc({
-		"doctype": "Employee Transfer",
-		"employee": employee,
-		"transfer_date": getdate(),
-		"transfer_details": [
-			{
-				"property": "Designation",
-				"current": "Accountant",
-				"new": "Manager",
-				"fieldname": "designation"
-			},
-			{
-				"property": "Department",
-				"current": "Accounts - TC",
-				"new": "Management - TC",
-				"fieldname": "department"
-			}
-		]
-	})
+	doc = frappe.get_doc(
+		{
+			"doctype": "Employee Transfer",
+			"employee": employee,
+			"transfer_date": getdate(),
+			"transfer_details": [
+				{
+					"property": "Designation",
+					"current": "Accountant",
+					"new": "Manager",
+					"fieldname": "designation",
+				},
+				{
+					"property": "Department",
+					"current": "Accounts - TC",
+					"new": "Management - TC",
+					"fieldname": "department",
+				},
+			],
+		}
+	)
 
 	doc.save()
 	doc.submit()
 
-	return doc
\ No newline at end of file
+	return doc
diff --git a/erpnext/hr/doctype/employee_transfer_property/__init__.py b/erpnext/hr/doctype/employee_transfer_property/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/hr/doctype/employee_transfer_property/__init__.py
+++ /dev/null
diff --git a/erpnext/hr/doctype/employee_transfer_property/employee_transfer_property.js b/erpnext/hr/doctype/employee_transfer_property/employee_transfer_property.js
deleted file mode 100644
index 9987c82..0000000
--- a/erpnext/hr/doctype/employee_transfer_property/employee_transfer_property.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('Employee Transfer Property', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/hr/doctype/employee_transfer_property/employee_transfer_property.json b/erpnext/hr/doctype/employee_transfer_property/employee_transfer_property.json
deleted file mode 100644
index 829169d..0000000
--- a/erpnext/hr/doctype/employee_transfer_property/employee_transfer_property.json
+++ /dev/null
@@ -1,154 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2018-04-13 18:24:30.579965", 
- "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": "property", 
-   "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": "Property", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "current", 
-   "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": "Current", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "new", 
-   "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": "New", 
-   "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": "2018-04-13 18:25:54.889579", 
- "modified_by": "Administrator", 
- "module": "HR", 
- "name": "Employee Transfer Property", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [
-  {
-   "amend": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "System Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 1, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_transfer_property/employee_transfer_property.py b/erpnext/hr/doctype/employee_transfer_property/employee_transfer_property.py
deleted file mode 100644
index 76e2006..0000000
--- a/erpnext/hr/doctype/employee_transfer_property/employee_transfer_property.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class EmployeeTransferProperty(Document):
-	pass
diff --git a/erpnext/hr/doctype/employee_transfer_property/test_employee_transfer_property.py b/erpnext/hr/doctype/employee_transfer_property/test_employee_transfer_property.py
deleted file mode 100644
index 981d46f..0000000
--- a/erpnext/hr/doctype/employee_transfer_property/test_employee_transfer_property.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestEmployeeTransferProperty(unittest.TestCase):
-	pass
diff --git a/erpnext/hr/doctype/employment_type/test_employment_type.py b/erpnext/hr/doctype/employment_type/test_employment_type.py
index c43f963..fdf6965 100644
--- a/erpnext/hr/doctype/employment_type/test_employment_type.py
+++ b/erpnext/hr/doctype/employment_type/test_employment_type.py
@@ -3,4 +3,4 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Employment Type')
+test_records = frappe.get_test_records("Employment Type")
diff --git a/erpnext/hr/doctype/exit_interview/exit_interview.py b/erpnext/hr/doctype/exit_interview/exit_interview.py
index 59fb2fd..ce4355b 100644
--- a/erpnext/hr/doctype/exit_interview/exit_interview.py
+++ b/erpnext/hr/doctype/exit_interview/exit_interview.py
@@ -16,41 +16,45 @@
 		self.set_employee_email()
 
 	def validate_relieving_date(self):
-		if not frappe.db.get_value('Employee', self.employee, 'relieving_date'):
-			frappe.throw(_('Please set the relieving date for employee {0}').format(
-				get_link_to_form('Employee', self.employee)),
-				title=_('Relieving Date Missing'))
+		if not frappe.db.get_value("Employee", self.employee, "relieving_date"):
+			frappe.throw(
+				_("Please set the relieving date for employee {0}").format(
+					get_link_to_form("Employee", self.employee)
+				),
+				title=_("Relieving Date Missing"),
+			)
 
 	def validate_duplicate_interview(self):
-		doc = frappe.db.exists('Exit Interview', {
-			'employee': self.employee,
-			'name': ('!=', self.name),
-			'docstatus': ('!=', 2)
-		})
+		doc = frappe.db.exists(
+			"Exit Interview", {"employee": self.employee, "name": ("!=", self.name), "docstatus": ("!=", 2)}
+		)
 		if doc:
-			frappe.throw(_('Exit Interview {0} already exists for Employee: {1}').format(
-				get_link_to_form('Exit Interview', doc), frappe.bold(self.employee)),
-				frappe.DuplicateEntryError)
+			frappe.throw(
+				_("Exit Interview {0} already exists for Employee: {1}").format(
+					get_link_to_form("Exit Interview", doc), frappe.bold(self.employee)
+				),
+				frappe.DuplicateEntryError,
+			)
 
 	def set_employee_email(self):
-		employee = frappe.get_doc('Employee', self.employee)
+		employee = frappe.get_doc("Employee", self.employee)
 		self.email = get_employee_email(employee)
 
 	def on_submit(self):
-		if self.status != 'Completed':
-			frappe.throw(_('Only Completed documents can be submitted'))
+		if self.status != "Completed":
+			frappe.throw(_("Only Completed documents can be submitted"))
 
 		self.update_interview_date_in_employee()
 
 	def on_cancel(self):
 		self.update_interview_date_in_employee()
-		self.db_set('status', 'Cancelled')
+		self.db_set("status", "Cancelled")
 
 	def update_interview_date_in_employee(self):
 		if self.docstatus == 1:
-			frappe.db.set_value('Employee', self.employee, 'held_on', self.date)
+			frappe.db.set_value("Employee", self.employee, "held_on", self.date)
 		elif self.docstatus == 2:
-			frappe.db.set_value('Employee', self.employee, 'held_on', None)
+			frappe.db.set_value("Employee", self.employee, "held_on", None)
 
 
 @frappe.whitelist()
@@ -62,17 +66,19 @@
 	email_failure = []
 
 	for exit_interview in interviews:
-		interview = frappe.get_doc('Exit Interview', exit_interview.get('name'))
-		if interview.get('questionnaire_email_sent'):
+		interview = frappe.get_doc("Exit Interview", exit_interview.get("name"))
+		if interview.get("questionnaire_email_sent"):
 			continue
 
-		employee = frappe.get_doc('Employee', interview.employee)
+		employee = frappe.get_doc("Employee", interview.employee)
 		email = get_employee_email(employee)
 
 		context = interview.as_dict()
 		context.update(employee.as_dict())
-		template_name = frappe.db.get_single_value('HR Settings', 'exit_questionnaire_notification_template')
-		template = frappe.get_doc('Email Template', template_name)
+		template_name = frappe.db.get_single_value(
+			"HR Settings", "exit_questionnaire_notification_template"
+		)
+		template = frappe.get_doc("Email Template", template_name)
 
 		if email:
 			frappe.sendmail(
@@ -80,13 +86,13 @@
 				subject=template.subject,
 				message=frappe.render_template(template.response, context),
 				reference_doctype=interview.doctype,
-				reference_name=interview.name
+				reference_name=interview.name,
 			)
-			interview.db_set('questionnaire_email_sent', True)
+			interview.db_set("questionnaire_email_sent", 1)
 			interview.notify_update()
 			email_success.append(email)
 		else:
-			email_failure.append(get_link_to_form('Employee', employee.name))
+			email_failure.append(get_link_to_form("Employee", employee.name))
 
 	show_email_summary(email_success, email_failure)
 
@@ -98,34 +104,43 @@
 		interviews = json.loads(interviews)
 
 	if not len(interviews):
-		frappe.throw(_('Atleast one interview has to be selected.'))
+		frappe.throw(_("Atleast one interview has to be selected."))
 
 	return interviews
 
 
 def validate_questionnaire_settings():
-	settings = frappe.db.get_value('HR Settings', 'HR Settings',
-		['exit_questionnaire_web_form', 'exit_questionnaire_notification_template'], as_dict=True)
+	settings = frappe.db.get_value(
+		"HR Settings",
+		"HR Settings",
+		["exit_questionnaire_web_form", "exit_questionnaire_notification_template"],
+		as_dict=True,
+	)
 
-	if not settings.exit_questionnaire_web_form or not settings.exit_questionnaire_notification_template:
+	if (
+		not settings.exit_questionnaire_web_form or not settings.exit_questionnaire_notification_template
+	):
 		frappe.throw(
-			_('Please set {0} and {1} in {2}.').format(
-				frappe.bold('Exit Questionnaire Web Form'),
-				frappe.bold('Notification Template'),
-				get_link_to_form('HR Settings', 'HR Settings')),
-			title=_('Settings Missing')
+			_("Please set {0} and {1} in {2}.").format(
+				frappe.bold("Exit Questionnaire Web Form"),
+				frappe.bold("Notification Template"),
+				get_link_to_form("HR Settings", "HR Settings"),
+			),
+			title=_("Settings Missing"),
 		)
 
 
 def show_email_summary(email_success, email_failure):
-	message = ''
+	message = ""
 	if email_success:
-		message += _('{0}: {1}').format(
-			frappe.bold('Sent Successfully'), ', '.join(email_success))
+		message += _("{0}: {1}").format(frappe.bold("Sent Successfully"), ", ".join(email_success))
 	if message and email_failure:
-		message += '<br><br>'
+		message += "<br><br>"
 	if email_failure:
-		message += _('{0} due to missing email information for employee(s): {1}').format(
-			frappe.bold('Sending Failed'), ', '.join(email_failure))
+		message += _("{0} due to missing email information for employee(s): {1}").format(
+			frappe.bold("Sending Failed"), ", ".join(email_failure)
+		)
 
-	frappe.msgprint(message, title=_('Exit Questionnaire'), indicator='blue', is_minimizable=True, wide=True)
+	frappe.msgprint(
+		message, title=_("Exit Questionnaire"), indicator="blue", is_minimizable=True, wide=True
+	)
diff --git a/erpnext/hr/doctype/exit_interview/test_exit_interview.py b/erpnext/hr/doctype/exit_interview/test_exit_interview.py
index 8e076ed..9c2c644 100644
--- a/erpnext/hr/doctype/exit_interview/test_exit_interview.py
+++ b/erpnext/hr/doctype/exit_interview/test_exit_interview.py
@@ -16,84 +16,88 @@
 
 class TestExitInterview(unittest.TestCase):
 	def setUp(self):
-		frappe.db.sql('delete from `tabExit Interview`')
+		frappe.db.sql("delete from `tabExit Interview`")
 
 	def test_duplicate_interview(self):
-		employee = make_employee('employeeexitint1@example.com')
-		frappe.db.set_value('Employee', employee, 'relieving_date', getdate())
+		employee = make_employee("employeeexitint1@example.com")
+		frappe.db.set_value("Employee", employee, "relieving_date", getdate())
 		interview = create_exit_interview(employee)
 
 		doc = frappe.copy_doc(interview)
 		self.assertRaises(frappe.DuplicateEntryError, doc.save)
 
 	def test_relieving_date_validation(self):
-		employee = make_employee('employeeexitint2@example.com')
+		employee = make_employee("employeeexitint2@example.com")
 		# unset relieving date
-		frappe.db.set_value('Employee', employee, 'relieving_date', None)
+		frappe.db.set_value("Employee", employee, "relieving_date", None)
 
 		interview = create_exit_interview(employee, save=False)
 		self.assertRaises(frappe.ValidationError, interview.save)
 
 		# set relieving date
-		frappe.db.set_value('Employee', employee, 'relieving_date', getdate())
+		frappe.db.set_value("Employee", employee, "relieving_date", getdate())
 		interview = create_exit_interview(employee)
 		self.assertTrue(interview.name)
 
 	def test_interview_date_updated_in_employee_master(self):
-		employee = make_employee('employeeexit3@example.com')
-		frappe.db.set_value('Employee', employee, 'relieving_date', getdate())
+		employee = make_employee("employeeexit3@example.com")
+		frappe.db.set_value("Employee", employee, "relieving_date", getdate())
 
 		interview = create_exit_interview(employee)
-		interview.status = 'Completed'
-		interview.employee_status = 'Exit Confirmed'
+		interview.status = "Completed"
+		interview.employee_status = "Exit Confirmed"
 
 		# exit interview date updated on submit
 		interview.submit()
-		self.assertEqual(frappe.db.get_value('Employee', employee, 'held_on'), interview.date)
+		self.assertEqual(frappe.db.get_value("Employee", employee, "held_on"), interview.date)
 
 		# exit interview reset on cancel
 		interview.reload()
 		interview.cancel()
-		self.assertEqual(frappe.db.get_value('Employee', employee, 'held_on'), None)
+		self.assertEqual(frappe.db.get_value("Employee", employee, "held_on"), None)
 
 	def test_send_exit_questionnaire(self):
 		create_custom_doctype()
 		create_webform()
 		template = create_notification_template()
 
-		webform = frappe.db.get_all('Web Form', limit=1)
-		frappe.db.set_value('HR Settings', 'HR Settings', {
-			'exit_questionnaire_web_form': webform[0].name,
-			'exit_questionnaire_notification_template': template
-		})
+		webform = frappe.db.get_all("Web Form", limit=1)
+		frappe.db.set_value(
+			"HR Settings",
+			"HR Settings",
+			{
+				"exit_questionnaire_web_form": webform[0].name,
+				"exit_questionnaire_notification_template": template,
+			},
+		)
 
-		employee = make_employee('employeeexit3@example.com')
-		frappe.db.set_value('Employee', employee, 'relieving_date', getdate())
+		employee = make_employee("employeeexit3@example.com")
+		frappe.db.set_value("Employee", employee, "relieving_date", getdate())
 
 		interview = create_exit_interview(employee)
 		send_exit_questionnaire([interview])
 
-		email_queue = frappe.db.get_all('Email Queue', ['name', 'message'], limit=1)
-		self.assertTrue('Subject: Exit Questionnaire Notification' in email_queue[0].message)
+		email_queue = frappe.db.get_all("Email Queue", ["name", "message"], limit=1)
+		self.assertTrue("Subject: Exit Questionnaire Notification" in email_queue[0].message)
 
 	def tearDown(self):
 		frappe.db.rollback()
 
 
 def create_exit_interview(employee, save=True):
-	interviewer = create_user('test_exit_interviewer@example.com')
+	interviewer = create_user("test_exit_interviewer@example.com")
 
-	doc = frappe.get_doc({
-		'doctype': 'Exit Interview',
-		'employee': employee,
-		'company': '_Test Company',
-		'status': 'Pending',
-		'date': getdate(),
-		'interviewers': [{
-			'interviewer': interviewer.name
-		}],
-		'interview_summary': 'Test'
-	})
+	doc = frappe.get_doc(
+		{
+			"doctype": "Exit Interview",
+			"employee": employee,
+			"company": "_Test Company",
+			"status": "Pending",
+			"date": getdate(),
+			"interviewers": [{"interviewer": interviewer.name}],
+			"interview_summary": "Test",
+		}
+	)
 
 	if save:
 		return doc.insert()
@@ -101,18 +105,22 @@
 
 
 def create_notification_template():
-	template = frappe.db.exists('Email Template', _('Exit Questionnaire Notification'))
+	template = frappe.db.exists("Email Template", _("Exit Questionnaire Notification"))
 	if not template:
-		base_path = frappe.get_app_path('erpnext', 'hr', 'doctype')
-		response = frappe.read_file(os.path.join(base_path, 'exit_interview/exit_questionnaire_notification_template.html'))
+		base_path = frappe.get_app_path("erpnext", "hr", "doctype")
+		response = frappe.read_file(
+			os.path.join(base_path, "exit_interview/exit_questionnaire_notification_template.html")
+		)
 
-		template = frappe.get_doc({
-			'doctype': 'Email Template',
-			'name': _('Exit Questionnaire Notification'),
-			'response': response,
-			'subject': _('Exit Questionnaire Notification'),
-			'owner': frappe.session.user,
-		}).insert(ignore_permissions=True)
+		template = frappe.get_doc(
+			{
+				"doctype": "Email Template",
+				"name": _("Exit Questionnaire Notification"),
+				"response": response,
+				"subject": _("Exit Questionnaire Notification"),
+				"owner": frappe.session.user,
+			}
+		).insert(ignore_permissions=True)
 		template = template.name
 
-	return template
\ No newline at end of file
+	return template
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py
index fe04efb..589763c 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.py
@@ -13,13 +13,19 @@
 from erpnext.hr.utils import set_employee_name, share_doc_with_approver, validate_active_employee
 
 
-class InvalidExpenseApproverError(frappe.ValidationError): pass
-class ExpenseApproverIdentityError(frappe.ValidationError): pass
+class InvalidExpenseApproverError(frappe.ValidationError):
+	pass
+
+
+class ExpenseApproverIdentityError(frappe.ValidationError):
+	pass
+
 
 class ExpenseClaim(AccountsController):
 	def onload(self):
-		self.get("__onload").make_payment_via_journal_entry = frappe.db.get_single_value('Accounts Settings',
-			'make_payment_via_journal_entry')
+		self.get("__onload").make_payment_via_journal_entry = frappe.db.get_single_value(
+			"Accounts Settings", "make_payment_via_journal_entry"
+		)
 
 	def validate(self):
 		validate_active_employee(self.employee)
@@ -36,21 +42,35 @@
 			self.project = frappe.db.get_value("Task", self.task, "project")
 
 	def set_status(self, update=False):
-		status = {
-			"0": "Draft",
-			"1": "Submitted",
-			"2": "Cancelled"
-		}[cstr(self.docstatus or 0)]
+		status = {"0": "Draft", "1": "Submitted", "2": "Cancelled"}[cstr(self.docstatus or 0)]
 
-		paid_amount = flt(self.total_amount_reimbursed) + flt(self.total_advance_amount)
 		precision = self.precision("grand_total")
-		if (self.is_paid or (flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1
-			and flt(self.grand_total, precision) == flt(paid_amount, precision))) and self.approval_status == 'Approved':
+
+		if (
+			# set as paid
+			self.is_paid
+			or (
+				flt(self.total_sanctioned_amount > 0)
+				and (
+					# grand total is reimbursed
+					(
+						self.docstatus == 1
+						and flt(self.grand_total, precision) == flt(self.total_amount_reimbursed, precision)
+					)
+					# grand total (to be paid) is 0 since linked advances already cover the claimed amount
+					or (flt(self.grand_total, precision) == 0)
+				)
+			)
+		) and self.approval_status == "Approved":
 			status = "Paid"
-		elif flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1 and self.approval_status == 'Approved':
+		elif (
+			flt(self.total_sanctioned_amount) > 0
+			and self.docstatus == 1
+			and self.approval_status == "Approved"
+		):
 			status = "Unpaid"
-		elif self.docstatus == 1 and self.approval_status == 'Rejected':
-			status = 'Rejected'
+		elif self.docstatus == 1 and self.approval_status == "Rejected":
+			status = "Rejected"
 
 		if update:
 			self.db_set("status", status)
@@ -62,14 +82,16 @@
 
 	def set_payable_account(self):
 		if not self.payable_account and not self.is_paid:
-			self.payable_account = frappe.get_cached_value('Company', self.company, 'default_expense_claim_payable_account')
+			self.payable_account = frappe.get_cached_value(
+				"Company", self.company, "default_expense_claim_payable_account"
+			)
 
 	def set_cost_center(self):
 		if not self.cost_center:
-			self.cost_center = frappe.get_cached_value('Company', self.company, 'cost_center')
+			self.cost_center = frappe.get_cached_value("Company", self.company, "cost_center")
 
 	def on_submit(self):
-		if self.approval_status=="Draft":
+		if self.approval_status == "Draft":
 			frappe.throw(_("""Approval Status must be 'Approved' or 'Rejected'"""))
 
 		self.update_task_and_project()
@@ -83,7 +105,7 @@
 
 	def on_cancel(self):
 		self.update_task_and_project()
-		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+		self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
 		if self.payable_account:
 			self.make_gl_entries(cancel=True)
 
@@ -114,43 +136,51 @@
 		# payable entry
 		if self.grand_total:
 			gl_entry.append(
-				self.get_gl_dict({
-					"account": self.payable_account,
-					"credit": self.grand_total,
-					"credit_in_account_currency": self.grand_total,
-					"against": ",".join([d.default_account for d in self.expenses]),
-					"party_type": "Employee",
-					"party": self.employee,
-					"against_voucher_type": self.doctype,
-					"against_voucher": self.name,
-					"cost_center": self.cost_center
-				}, item=self)
+				self.get_gl_dict(
+					{
+						"account": self.payable_account,
+						"credit": self.grand_total,
+						"credit_in_account_currency": self.grand_total,
+						"against": ",".join([d.default_account for d in self.expenses]),
+						"party_type": "Employee",
+						"party": self.employee,
+						"against_voucher_type": self.doctype,
+						"against_voucher": self.name,
+						"cost_center": self.cost_center,
+					},
+					item=self,
+				)
 			)
 
 		# expense entries
 		for data in self.expenses:
 			gl_entry.append(
-				self.get_gl_dict({
-					"account": data.default_account,
-					"debit": data.sanctioned_amount,
-					"debit_in_account_currency": data.sanctioned_amount,
-					"against": self.employee,
-					"cost_center": data.cost_center or self.cost_center
-				}, item=data)
+				self.get_gl_dict(
+					{
+						"account": data.default_account,
+						"debit": data.sanctioned_amount,
+						"debit_in_account_currency": data.sanctioned_amount,
+						"against": self.employee,
+						"cost_center": data.cost_center or self.cost_center,
+					},
+					item=data,
+				)
 			)
 
 		for data in self.advances:
 			gl_entry.append(
-				self.get_gl_dict({
-					"account": data.advance_account,
-					"credit": data.allocated_amount,
-					"credit_in_account_currency": data.allocated_amount,
-					"against": ",".join([d.default_account for d in self.expenses]),
-					"party_type": "Employee",
-					"party": self.employee,
-					"against_voucher_type": "Employee Advance",
-					"against_voucher": data.employee_advance
-				})
+				self.get_gl_dict(
+					{
+						"account": data.advance_account,
+						"credit": data.allocated_amount,
+						"credit_in_account_currency": data.allocated_amount,
+						"against": ",".join([d.default_account for d in self.expenses]),
+						"party_type": "Employee",
+						"party": self.employee,
+						"against_voucher_type": "Employee Advance",
+						"against_voucher": data.employee_advance,
+					}
+				)
 			)
 
 		self.add_tax_gl_entries(gl_entry)
@@ -159,25 +189,31 @@
 			# payment entry
 			payment_account = get_bank_cash_account(self.mode_of_payment, self.company).get("account")
 			gl_entry.append(
-				self.get_gl_dict({
-					"account": payment_account,
-					"credit": self.grand_total,
-					"credit_in_account_currency": self.grand_total,
-					"against": self.employee
-				}, item=self)
+				self.get_gl_dict(
+					{
+						"account": payment_account,
+						"credit": self.grand_total,
+						"credit_in_account_currency": self.grand_total,
+						"against": self.employee,
+					},
+					item=self,
+				)
 			)
 
 			gl_entry.append(
-				self.get_gl_dict({
-					"account": self.payable_account,
-					"party_type": "Employee",
-					"party": self.employee,
-					"against": payment_account,
-					"debit": self.grand_total,
-					"debit_in_account_currency": self.grand_total,
-					"against_voucher": self.name,
-					"against_voucher_type": self.doctype,
-				}, item=self)
+				self.get_gl_dict(
+					{
+						"account": self.payable_account,
+						"party_type": "Employee",
+						"party": self.employee,
+						"against": payment_account,
+						"debit": self.grand_total,
+						"debit_in_account_currency": self.grand_total,
+						"against_voucher": self.name,
+						"against_voucher_type": self.doctype,
+					},
+					item=self,
+				)
 			)
 
 		return gl_entry
@@ -186,22 +222,28 @@
 		# tax table gl entries
 		for tax in self.get("taxes"):
 			gl_entries.append(
-				self.get_gl_dict({
-					"account": tax.account_head,
-					"debit": tax.tax_amount,
-					"debit_in_account_currency": tax.tax_amount,
-					"against": self.employee,
-					"cost_center": self.cost_center,
-					"against_voucher_type": self.doctype,
-					"against_voucher": self.name
-				}, item=tax)
+				self.get_gl_dict(
+					{
+						"account": tax.account_head,
+						"debit": tax.tax_amount,
+						"debit_in_account_currency": tax.tax_amount,
+						"against": self.employee,
+						"cost_center": self.cost_center,
+						"against_voucher_type": self.doctype,
+						"against_voucher": self.name,
+					},
+					item=tax,
+				)
 			)
 
 	def validate_account_details(self):
 		for data in self.expenses:
 			if not data.cost_center:
-				frappe.throw(_("Row {0}: {1} is required in the expenses table to book an expense claim.")
-					.format(data.idx, frappe.bold("Cost Center")))
+				frappe.throw(
+					_("Row {0}: {1} is required in the expenses table to book an expense claim.").format(
+						data.idx, frappe.bold("Cost Center")
+					)
+				)
 
 		if self.is_paid:
 			if not self.mode_of_payment:
@@ -210,8 +252,8 @@
 	def calculate_total_amount(self):
 		self.total_claimed_amount = 0
 		self.total_sanctioned_amount = 0
-		for d in self.get('expenses'):
-			if self.approval_status == 'Rejected':
+		for d in self.get("expenses"):
+			if self.approval_status == "Rejected":
 				d.sanctioned_amount = 0.0
 
 			self.total_claimed_amount += flt(d.amount)
@@ -222,12 +264,16 @@
 		self.total_taxes_and_charges = 0
 		for tax in self.taxes:
 			if tax.rate:
-				tax.tax_amount = flt(self.total_sanctioned_amount) * flt(tax.rate/100)
+				tax.tax_amount = flt(self.total_sanctioned_amount) * flt(tax.rate / 100)
 
 			tax.total = flt(tax.tax_amount) + flt(self.total_sanctioned_amount)
 			self.total_taxes_and_charges += flt(tax.tax_amount)
 
-		self.grand_total = flt(self.total_sanctioned_amount) + flt(self.total_taxes_and_charges) - flt(self.total_advance_amount)
+		self.grand_total = (
+			flt(self.total_sanctioned_amount)
+			+ flt(self.total_taxes_and_charges)
+			- flt(self.total_advance_amount)
+		)
 
 	def update_task(self):
 		task = frappe.get_doc("Task", self.task)
@@ -237,16 +283,23 @@
 	def validate_advances(self):
 		self.total_advance_amount = 0
 		for d in self.get("advances"):
-			ref_doc = frappe.db.get_value("Employee Advance", d.employee_advance,
-				["posting_date", "paid_amount", "claimed_amount", "advance_account"], as_dict=1)
+			ref_doc = frappe.db.get_value(
+				"Employee Advance",
+				d.employee_advance,
+				["posting_date", "paid_amount", "claimed_amount", "advance_account"],
+				as_dict=1,
+			)
 			d.posting_date = ref_doc.posting_date
 			d.advance_account = ref_doc.advance_account
 			d.advance_paid = ref_doc.paid_amount
 			d.unclaimed_amount = flt(ref_doc.paid_amount) - flt(ref_doc.claimed_amount)
 
 			if d.allocated_amount and flt(d.allocated_amount) > flt(d.unclaimed_amount):
-				frappe.throw(_("Row {0}# Allocated amount {1} cannot be greater than unclaimed amount {2}")
-					.format(d.idx, d.allocated_amount, d.unclaimed_amount))
+				frappe.throw(
+					_("Row {0}# Allocated amount {1} cannot be greater than unclaimed amount {2}").format(
+						d.idx, d.allocated_amount, d.unclaimed_amount
+					)
+				)
 
 			self.total_advance_amount += flt(d.allocated_amount)
 
@@ -255,27 +308,36 @@
 			if flt(self.total_advance_amount, precision) > flt(self.total_claimed_amount, precision):
 				frappe.throw(_("Total advance amount cannot be greater than total claimed amount"))
 
-			if self.total_sanctioned_amount \
-					and flt(self.total_advance_amount, precision) > flt(self.total_sanctioned_amount, precision):
+			if self.total_sanctioned_amount and flt(self.total_advance_amount, precision) > flt(
+				self.total_sanctioned_amount, precision
+			):
 				frappe.throw(_("Total advance amount cannot be greater than total sanctioned amount"))
 
 	def validate_sanctioned_amount(self):
-		for d in self.get('expenses'):
+		for d in self.get("expenses"):
 			if flt(d.sanctioned_amount) > flt(d.amount):
-				frappe.throw(_("Sanctioned Amount cannot be greater than Claim Amount in Row {0}.").format(d.idx))
+				frappe.throw(
+					_("Sanctioned Amount cannot be greater than Claim Amount in Row {0}.").format(d.idx)
+				)
 
 	def set_expense_account(self, validate=False):
 		for expense in self.expenses:
 			if not expense.default_account or not validate:
-				expense.default_account = get_expense_claim_account(expense.expense_type, self.company)["account"]
+				expense.default_account = get_expense_claim_account(expense.expense_type, self.company)[
+					"account"
+				]
+
 
 def update_reimbursed_amount(doc, amount):
 
 	doc.total_amount_reimbursed += amount
-	frappe.db.set_value("Expense Claim", doc.name , "total_amount_reimbursed", doc.total_amount_reimbursed)
+	frappe.db.set_value(
+		"Expense Claim", doc.name, "total_amount_reimbursed", doc.total_amount_reimbursed
+	)
 
 	doc.set_status()
-	frappe.db.set_value("Expense Claim", doc.name , "status", doc.status)
+	frappe.db.set_value("Expense Claim", doc.name, "status", doc.status)
+
 
 @frappe.whitelist()
 def make_bank_entry(dt, dn):
@@ -286,69 +348,80 @@
 	if not default_bank_cash_account:
 		default_bank_cash_account = get_default_bank_cash_account(expense_claim.company, "Cash")
 
-	payable_amount = flt(expense_claim.total_sanctioned_amount) \
-		- flt(expense_claim.total_amount_reimbursed) - flt(expense_claim.total_advance_amount)
+	payable_amount = (
+		flt(expense_claim.total_sanctioned_amount)
+		- flt(expense_claim.total_amount_reimbursed)
+		- flt(expense_claim.total_advance_amount)
+	)
 
 	je = frappe.new_doc("Journal Entry")
-	je.voucher_type = 'Bank Entry'
+	je.voucher_type = "Bank Entry"
 	je.company = expense_claim.company
-	je.remark = 'Payment against Expense Claim: ' + dn
+	je.remark = "Payment against Expense Claim: " + dn
 
-	je.append("accounts", {
-		"account": expense_claim.payable_account,
-		"debit_in_account_currency": payable_amount,
-		"reference_type": "Expense Claim",
-		"party_type": "Employee",
-		"party": expense_claim.employee,
-		"cost_center": erpnext.get_default_cost_center(expense_claim.company),
-		"reference_name": expense_claim.name
-	})
+	je.append(
+		"accounts",
+		{
+			"account": expense_claim.payable_account,
+			"debit_in_account_currency": payable_amount,
+			"reference_type": "Expense Claim",
+			"party_type": "Employee",
+			"party": expense_claim.employee,
+			"cost_center": erpnext.get_default_cost_center(expense_claim.company),
+			"reference_name": expense_claim.name,
+		},
+	)
 
-	je.append("accounts", {
-		"account": default_bank_cash_account.account,
-		"credit_in_account_currency": payable_amount,
-		"reference_type": "Expense Claim",
-		"reference_name": expense_claim.name,
-		"balance": default_bank_cash_account.balance,
-		"account_currency": default_bank_cash_account.account_currency,
-		"cost_center": erpnext.get_default_cost_center(expense_claim.company),
-		"account_type": default_bank_cash_account.account_type
-	})
+	je.append(
+		"accounts",
+		{
+			"account": default_bank_cash_account.account,
+			"credit_in_account_currency": payable_amount,
+			"reference_type": "Expense Claim",
+			"reference_name": expense_claim.name,
+			"balance": default_bank_cash_account.balance,
+			"account_currency": default_bank_cash_account.account_currency,
+			"cost_center": erpnext.get_default_cost_center(expense_claim.company),
+			"account_type": default_bank_cash_account.account_type,
+		},
+	)
 
 	return je.as_dict()
 
+
 @frappe.whitelist()
 def get_expense_claim_account_and_cost_center(expense_claim_type, company):
 	data = get_expense_claim_account(expense_claim_type, company)
 	cost_center = erpnext.get_default_cost_center(company)
 
-	return {
-		"account": data.get("account"),
-		"cost_center": cost_center
-	}
+	return {"account": data.get("account"), "cost_center": cost_center}
+
 
 @frappe.whitelist()
 def get_expense_claim_account(expense_claim_type, company):
-	account = frappe.db.get_value("Expense Claim Account",
-		{"parent": expense_claim_type, "company": company}, "default_account")
+	account = frappe.db.get_value(
+		"Expense Claim Account", {"parent": expense_claim_type, "company": company}, "default_account"
+	)
 	if not account:
-		frappe.throw(_("Set the default account for the {0} {1}")
-			.format(frappe.bold("Expense Claim Type"), get_link_to_form("Expense Claim Type", expense_claim_type)))
+		frappe.throw(
+			_("Set the default account for the {0} {1}").format(
+				frappe.bold("Expense Claim Type"), get_link_to_form("Expense Claim Type", expense_claim_type)
+			)
+		)
 
-	return {
-		"account": account
-	}
+	return {"account": account}
+
 
 @frappe.whitelist()
 def get_advances(employee, advance_id=None):
 	advance = frappe.qb.DocType("Employee Advance")
 
-	query = (
-		frappe.qb.from_(advance)
-		.select(
-			advance.name, advance.posting_date, advance.paid_amount,
-			advance.claimed_amount, advance.advance_account
-		)
+	query = frappe.qb.from_(advance).select(
+		advance.name,
+		advance.posting_date,
+		advance.paid_amount,
+		advance.claimed_amount,
+		advance.advance_account,
 	)
 
 	if not advance_id:
@@ -366,25 +439,26 @@
 
 @frappe.whitelist()
 def get_expense_claim(
-	employee_name, company, employee_advance_name, posting_date, paid_amount, claimed_amount):
-	default_payable_account = frappe.get_cached_value('Company',  company,  "default_payable_account")
-	default_cost_center = frappe.get_cached_value('Company',  company,  'cost_center')
+	employee_name, company, employee_advance_name, posting_date, paid_amount, claimed_amount
+):
+	default_payable_account = frappe.get_cached_value("Company", company, "default_payable_account")
+	default_cost_center = frappe.get_cached_value("Company", company, "cost_center")
 
-	expense_claim = frappe.new_doc('Expense Claim')
+	expense_claim = frappe.new_doc("Expense Claim")
 	expense_claim.company = company
 	expense_claim.employee = employee_name
 	expense_claim.payable_account = default_payable_account
 	expense_claim.cost_center = default_cost_center
 	expense_claim.is_paid = 1 if flt(paid_amount) else 0
 	expense_claim.append(
-		'advances',
+		"advances",
 		{
-			'employee_advance': employee_advance_name,
-			'posting_date': posting_date,
-			'advance_paid': flt(paid_amount),
-			'unclaimed_amount': flt(paid_amount) - flt(claimed_amount),
-			'allocated_amount': flt(paid_amount) - flt(claimed_amount)
-		}
+			"employee_advance": employee_advance_name,
+			"posting_date": posting_date,
+			"advance_paid": flt(paid_amount),
+			"unclaimed_amount": flt(paid_amount) - flt(claimed_amount),
+			"allocated_amount": flt(paid_amount) - flt(claimed_amount),
+		},
 	)
 
 	return expense_claim
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py b/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py
index 7539c71..8b1acc6 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py
+++ b/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py
@@ -3,18 +3,10 @@
 
 def get_data():
 	return {
-        'fieldname': 'reference_name',
-		'internal_links': {
-			'Employee Advance': ['advances', 'employee_advance']
-		},
-		'transactions': [
-			{
-				'label': _('Payment'),
-				'items': ['Payment Entry', 'Journal Entry']
-			},
-			{
-				'label': _('Reference'),
-				'items': ['Employee Advance']
-			},
-		]
+		"fieldname": "reference_name",
+		"internal_links": {"Employee Advance": ["advances", "employee_advance"]},
+		"transactions": [
+			{"label": _("Payment"), "items": ["Payment Entry", "Journal Entry"]},
+			{"label": _("Reference"), "items": ["Employee Advance"]},
+		],
 	}
diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
index 2a07920..9b3d53a 100644
--- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
@@ -10,8 +10,8 @@
 from erpnext.hr.doctype.employee.test_employee import make_employee
 from erpnext.hr.doctype.expense_claim.expense_claim import make_bank_entry
 
-test_dependencies = ['Employee']
-company_name = '_Test Company 3'
+test_dependencies = ["Employee"]
+company_name = "_Test Company 3"
 
 
 class TestExpenseClaim(unittest.TestCase):
@@ -23,28 +23,26 @@
 		frappe.db.sql("""delete from `tabProject`""")
 		frappe.db.sql("update `tabExpense Claim` set project = '', task = ''")
 
-		project = frappe.get_doc({
-			"project_name": "_Test Project 1",
-			"doctype": "Project"
-		})
+		project = frappe.get_doc({"project_name": "_Test Project 1", "doctype": "Project"})
 		project.save()
 
-		task = frappe.get_doc(dict(
-			doctype = 'Task',
-			subject = '_Test Project Task 1',
-			status = 'Open',
-			project = project.name
-		)).insert()
+		task = frappe.get_doc(
+			dict(doctype="Task", subject="_Test Project Task 1", status="Open", project=project.name)
+		).insert()
 
 		task_name = task.name
 		payable_account = get_payable_account(company_name)
 
-		make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC3", project.name, task_name)
+		make_expense_claim(
+			payable_account, 300, 200, company_name, "Travel Expenses - _TC3", project.name, task_name
+		)
 
 		self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200)
 		self.assertEqual(frappe.db.get_value("Project", project.name, "total_expense_claim"), 200)
 
-		expense_claim2 = make_expense_claim(payable_account, 600, 500, company_name, "Travel Expenses - _TC3", project.name, task_name)
+		expense_claim2 = make_expense_claim(
+			payable_account, 600, 500, company_name, "Travel Expenses - _TC3", project.name, task_name
+		)
 
 		self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700)
 		self.assertEqual(frappe.db.get_value("Project", project.name, "total_expense_claim"), 700)
@@ -56,7 +54,9 @@
 
 	def test_expense_claim_status(self):
 		payable_account = get_payable_account(company_name)
-		expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC3")
+		expense_claim = make_expense_claim(
+			payable_account, 300, 200, company_name, "Travel Expenses - _TC3"
+		)
 
 		je_dict = make_bank_entry("Expense Claim", expense_claim.name)
 		je = frappe.get_doc(je_dict)
@@ -72,24 +72,110 @@
 		expense_claim = frappe.get_doc("Expense Claim", expense_claim.name)
 		self.assertEqual(expense_claim.status, "Unpaid")
 
+		# expense claim without any sanctioned amount should not have status as Paid
+		claim = make_expense_claim(payable_account, 1000, 0, "_Test Company", "Travel Expenses - _TC")
+		self.assertEqual(claim.total_sanctioned_amount, 0)
+		self.assertEqual(claim.status, "Submitted")
+
+		# no gl entries created
+		gl_entry = frappe.get_all(
+			"GL Entry", {"voucher_type": "Expense Claim", "voucher_no": claim.name}
+		)
+		self.assertEqual(len(gl_entry), 0)
+
+	def test_expense_claim_against_fully_paid_advances(self):
+		from erpnext.hr.doctype.employee_advance.test_employee_advance import (
+			get_advances_for_claim,
+			make_employee_advance,
+			make_payment_entry,
+		)
+
+		frappe.db.delete("Employee Advance")
+
+		payable_account = get_payable_account("_Test Company")
+		claim = make_expense_claim(
+			payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True
+		)
+
+		advance = make_employee_advance(claim.employee)
+		pe = make_payment_entry(advance)
+		pe.submit()
+
+		# claim for already paid out advances
+		claim = get_advances_for_claim(claim, advance.name)
+		claim.save()
+		claim.submit()
+
+		self.assertEqual(claim.grand_total, 0)
+		self.assertEqual(claim.status, "Paid")
+
+	def test_expense_claim_partially_paid_via_advance(self):
+		from erpnext.hr.doctype.employee_advance.test_employee_advance import (
+			get_advances_for_claim,
+			make_employee_advance,
+		)
+		from erpnext.hr.doctype.employee_advance.test_employee_advance import (
+			make_payment_entry as make_advance_payment,
+		)
+
+		frappe.db.delete("Employee Advance")
+
+		payable_account = get_payable_account("_Test Company")
+		claim = make_expense_claim(
+			payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True
+		)
+
+		# link advance for partial amount
+		advance = make_employee_advance(claim.employee, {"advance_amount": 500})
+		pe = make_advance_payment(advance)
+		pe.submit()
+
+		claim = get_advances_for_claim(claim, advance.name)
+		claim.save()
+		claim.submit()
+
+		self.assertEqual(claim.grand_total, 500)
+		self.assertEqual(claim.status, "Unpaid")
+
+		# reimburse remaning amount
+		make_payment_entry(claim, payable_account, 500)
+		claim.reload()
+
+		self.assertEqual(claim.total_amount_reimbursed, 500)
+		self.assertEqual(claim.status, "Paid")
+
 	def test_expense_claim_gl_entry(self):
 		payable_account = get_payable_account(company_name)
 		taxes = generate_taxes()
-		expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC3",
-			do_not_submit=True, taxes=taxes)
+		expense_claim = make_expense_claim(
+			payable_account,
+			300,
+			200,
+			company_name,
+			"Travel Expenses - _TC3",
+			do_not_submit=True,
+			taxes=taxes,
+		)
 		expense_claim.submit()
 
-		gl_entries = frappe.db.sql("""select account, debit, credit
+		gl_entries = frappe.db.sql(
+			"""select account, debit, credit
 			from `tabGL Entry` where voucher_type='Expense Claim' and voucher_no=%s
-			order by account asc""", expense_claim.name, as_dict=1)
+			order by account asc""",
+			expense_claim.name,
+			as_dict=1,
+		)
 
 		self.assertTrue(gl_entries)
 
-		expected_values = dict((d[0], d) for d in [
-			['Output Tax CGST - _TC3',18.0, 0.0],
-			[payable_account, 0.0, 218.0],
-			["Travel Expenses - _TC3", 200.0, 0.0]
-		])
+		expected_values = dict(
+			(d[0], d)
+			for d in [
+				["Output Tax CGST - _TC3", 18.0, 0.0],
+				[payable_account, 0.0, 218.0],
+				["Travel Expenses - _TC3", 200.0, 0.0],
+			]
+		)
 
 		for gle in gl_entries:
 			self.assertEqual(expected_values[gle.account][0], gle.account)
@@ -98,20 +184,30 @@
 
 	def test_rejected_expense_claim(self):
 		payable_account = get_payable_account(company_name)
-		expense_claim = frappe.get_doc({
-			"doctype": "Expense Claim",
-			"employee": "_T-Employee-00001",
-			"payable_account": payable_account,
-			"approval_status": "Rejected",
-			"expenses":
-				[{"expense_type": "Travel", "default_account": "Travel Expenses - _TC3", "amount": 300, "sanctioned_amount": 200}]
-		})
+		expense_claim = frappe.get_doc(
+			{
+				"doctype": "Expense Claim",
+				"employee": "_T-Employee-00001",
+				"payable_account": payable_account,
+				"approval_status": "Rejected",
+				"expenses": [
+					{
+						"expense_type": "Travel",
+						"default_account": "Travel Expenses - _TC3",
+						"amount": 300,
+						"sanctioned_amount": 200,
+					}
+				],
+			}
+		)
 		expense_claim.submit()
 
-		self.assertEqual(expense_claim.status, 'Rejected')
+		self.assertEqual(expense_claim.status, "Rejected")
 		self.assertEqual(expense_claim.total_sanctioned_amount, 0.0)
 
-		gl_entry = frappe.get_all('GL Entry', {'voucher_type': 'Expense Claim', 'voucher_no': expense_claim.name})
+		gl_entry = frappe.get_all(
+			"GL Entry", {"voucher_type": "Expense Claim", "voucher_no": expense_claim.name}
+		)
 		self.assertEqual(len(gl_entry), 0)
 
 	def test_expense_approver_perms(self):
@@ -120,7 +216,9 @@
 
 		# check doc shared
 		payable_account = get_payable_account("_Test Company")
-		expense_claim = make_expense_claim(payable_account, 300, 200, "_Test Company", "Travel Expenses - _TC", do_not_submit=True)
+		expense_claim = make_expense_claim(
+			payable_account, 300, 200, "_Test Company", "Travel Expenses - _TC", do_not_submit=True
+		)
 		expense_claim.expense_approver = user
 		expense_claim.save()
 		self.assertTrue(expense_claim.name in frappe.share.get_shared("Expense Claim", user))
@@ -144,51 +242,76 @@
 	def test_multiple_payment_entries_against_expense(self):
 		# Creating expense claim
 		payable_account = get_payable_account("_Test Company")
-		expense_claim = make_expense_claim(payable_account, 5500, 5500, "_Test Company", "Travel Expenses - _TC")
+		expense_claim = make_expense_claim(
+			payable_account, 5500, 5500, "_Test Company", "Travel Expenses - _TC"
+		)
 		expense_claim.save()
 		expense_claim.submit()
 
 		# Payment entry 1: paying 500
-		make_payment_entry(expense_claim, payable_account,500)
-		outstanding_amount, total_amount_reimbursed = get_outstanding_and_total_reimbursed_amounts(expense_claim)
+		make_payment_entry(expense_claim, payable_account, 500)
+		outstanding_amount, total_amount_reimbursed = get_outstanding_and_total_reimbursed_amounts(
+			expense_claim
+		)
 		self.assertEqual(outstanding_amount, 5000)
 		self.assertEqual(total_amount_reimbursed, 500)
 
 		# Payment entry 1: paying 2000
-		make_payment_entry(expense_claim, payable_account,2000)
-		outstanding_amount, total_amount_reimbursed = get_outstanding_and_total_reimbursed_amounts(expense_claim)
+		make_payment_entry(expense_claim, payable_account, 2000)
+		outstanding_amount, total_amount_reimbursed = get_outstanding_and_total_reimbursed_amounts(
+			expense_claim
+		)
 		self.assertEqual(outstanding_amount, 3000)
 		self.assertEqual(total_amount_reimbursed, 2500)
 
 		# Payment entry 1: paying 3000
-		make_payment_entry(expense_claim, payable_account,3000)
-		outstanding_amount, total_amount_reimbursed = get_outstanding_and_total_reimbursed_amounts(expense_claim)
+		make_payment_entry(expense_claim, payable_account, 3000)
+		outstanding_amount, total_amount_reimbursed = get_outstanding_and_total_reimbursed_amounts(
+			expense_claim
+		)
 		self.assertEqual(outstanding_amount, 0)
 		self.assertEqual(total_amount_reimbursed, 5500)
 
 
 def get_payable_account(company):
-	return frappe.get_cached_value('Company', company, 'default_payable_account')
+	return frappe.get_cached_value("Company", company, "default_payable_account")
+
 
 def generate_taxes():
-	parent_account = frappe.db.get_value('Account',
-		{'company': company_name, 'is_group':1, 'account_type': 'Tax'},
-		'name')
-	account = create_account(company=company_name, account_name="Output Tax CGST", account_type="Tax", parent_account=parent_account)
-	return {'taxes':[{
-		"account_head": account,
-		"rate": 9,
-		"description": "CGST",
-		"tax_amount": 10,
-		"total": 210
-	}]}
+	parent_account = frappe.db.get_value(
+		"Account", {"company": company_name, "is_group": 1, "account_type": "Tax"}, "name"
+	)
+	account = create_account(
+		company=company_name,
+		account_name="Output Tax CGST",
+		account_type="Tax",
+		parent_account=parent_account,
+	)
+	return {
+		"taxes": [
+			{"account_head": account, "rate": 9, "description": "CGST", "tax_amount": 10, "total": 210}
+		]
+	}
 
-def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, project=None, task_name=None, do_not_submit=False, taxes=None):
+
+def make_expense_claim(
+	payable_account,
+	amount,
+	sanctioned_amount,
+	company,
+	account,
+	project=None,
+	task_name=None,
+	do_not_submit=False,
+	taxes=None,
+):
 	employee = frappe.db.get_value("Employee", {"status": "Active"})
 	if not employee:
 		employee = make_employee("test_employee@expense_claim.com", company=company)
 
-	currency, cost_center = frappe.db.get_value('Company', company, ['default_currency', 'cost_center'])
+	currency, cost_center = frappe.db.get_value(
+		"Company", company, ["default_currency", "cost_center"]
+	)
 	expense_claim = {
 		"doctype": "Expense Claim",
 		"employee": employee,
@@ -196,14 +319,16 @@
 		"approval_status": "Approved",
 		"company": company,
 		"currency": currency,
-		"expenses": [{
-			"expense_type": "Travel",
-			"default_account": account,
-			"currency": currency,
-			"amount": amount,
-			"sanctioned_amount": sanctioned_amount,
-			"cost_center": cost_center
-		}]
+		"expenses": [
+			{
+				"expense_type": "Travel",
+				"default_account": account,
+				"currency": currency,
+				"amount": amount,
+				"sanctioned_amount": sanctioned_amount,
+				"cost_center": cost_center,
+			}
+		],
 	}
 	if taxes:
 		expense_claim.update(taxes)
@@ -220,17 +345,24 @@
 	expense_claim.submit()
 	return expense_claim
 
-def get_outstanding_and_total_reimbursed_amounts(expense_claim):
-	outstanding_amount = flt(frappe.db.get_value("Expense Claim", expense_claim.name, "total_sanctioned_amount")) - \
-			flt(frappe.db.get_value("Expense Claim", expense_claim.name, "total_amount_reimbursed"))
-	total_amount_reimbursed = flt(frappe.db.get_value("Expense Claim", expense_claim.name, "total_amount_reimbursed"))
 
-	return outstanding_amount,total_amount_reimbursed
+def get_outstanding_and_total_reimbursed_amounts(expense_claim):
+	outstanding_amount = flt(
+		frappe.db.get_value("Expense Claim", expense_claim.name, "total_sanctioned_amount")
+	) - flt(frappe.db.get_value("Expense Claim", expense_claim.name, "total_amount_reimbursed"))
+	total_amount_reimbursed = flt(
+		frappe.db.get_value("Expense Claim", expense_claim.name, "total_amount_reimbursed")
+	)
+
+	return outstanding_amount, total_amount_reimbursed
+
 
 def make_payment_entry(expense_claim, payable_account, amt):
 	from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
 
-	pe = get_payment_entry("Expense Claim", expense_claim.name, bank_account="_Test Bank USD - _TC", bank_amount=amt)
+	pe = get_payment_entry(
+		"Expense Claim", expense_claim.name, bank_account="_Test Bank USD - _TC", bank_amount=amt
+	)
 	pe.reference_no = "1"
 	pe.reference_date = nowdate()
 	pe.source_exchange_rate = 1
diff --git a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.py b/erpnext/hr/doctype/expense_claim_type/expense_claim_type.py
index 570b2c1..6d29f7d 100644
--- a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.py
+++ b/erpnext/hr/doctype/expense_claim_type/expense_claim_type.py
@@ -18,12 +18,13 @@
 		for entry in self.accounts:
 			accounts_list.append(entry.company)
 
-		if len(accounts_list)!= len(set(accounts_list)):
+		if len(accounts_list) != len(set(accounts_list)):
 			frappe.throw(_("Same Company is entered more than once"))
 
 	def validate_accounts(self):
 		for entry in self.accounts:
 			"""Error when Company of Ledger account doesn't match with Company Selected"""
 			if frappe.db.get_value("Account", entry.default_account, "company") != entry.company:
-				frappe.throw(_("Account {0} does not match with Company {1}"
-					).format(entry.default_account, entry.company))
+				frappe.throw(
+					_("Account {0} does not match with Company {1}").format(entry.default_account, entry.company)
+				)
diff --git a/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.py b/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.py
index a2403b6..62348e2 100644
--- a/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.py
+++ b/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.py
@@ -5,5 +5,6 @@
 
 # test_records = frappe.get_test_records('Expense Claim Type')
 
+
 class TestExpenseClaimType(unittest.TestCase):
 	pass
diff --git a/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.py b/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.py
index f539537..8137a0d 100644
--- a/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.py
+++ b/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.py
@@ -39,16 +39,20 @@
 				for data in self.get_assets_movement():
 					self.append("assets_allocated", data)
 		else:
-			frappe.throw(_("Set Relieving Date for Employee: {0}").format(get_link_to_form("Employee", self.employee)))
+			frappe.throw(
+				_("Set Relieving Date for Employee: {0}").format(get_link_to_form("Employee", self.employee))
+			)
 
 	def create_component_row(self, components, component_type):
 		for component in components:
-			self.append(component_type, {
-				"status": "Unsettled",
-				"reference_document_type": component if component != "Bonus" else "Additional Salary",
-				"component": component
-			})
-
+			self.append(
+				component_type,
+				{
+					"status": "Unsettled",
+					"reference_document_type": component if component != "Bonus" else "Additional Salary",
+					"component": component,
+				},
+			)
 
 	def get_payable_component(self):
 		return [
@@ -66,13 +70,11 @@
 		]
 
 	def get_assets_movement(self):
-		asset_movements = frappe.get_all("Asset Movement Item",
-			filters = {"docstatus": 1},
-			fields = ["asset", "from_employee", "to_employee", "parent", "asset_name"],
-			or_filters = {
-				"from_employee": self.employee,
-				"to_employee": self.employee
-			}
+		asset_movements = frappe.get_all(
+			"Asset Movement Item",
+			filters={"docstatus": 1},
+			fields=["asset", "from_employee", "to_employee", "parent", "asset_name"],
+			or_filters={"from_employee": self.employee, "to_employee": self.employee},
 		)
 
 		data = []
@@ -90,12 +92,14 @@
 			inwards_counts = [movement.asset for movement in inward_movements].count(movement.asset)
 
 			if inwards_counts > outwards_count:
-				data.append({
-					"reference": movement.parent,
-					"asset_name": movement.asset_name,
-					"date": frappe.db.get_value("Asset Movement", movement.parent, "transaction_date"),
-					"status": "Owned"
-				})
+				data.append(
+					{
+						"reference": movement.parent,
+						"asset_name": movement.asset_name,
+						"date": frappe.db.get_value("Asset Movement", movement.parent, "transaction_date"),
+						"status": "Owned",
+					}
+				)
 		return data
 
 	@frappe.whitelist()
@@ -112,7 +116,7 @@
 			if data.amount > 0 and not data.paid_via_salary_slip:
 				account_dict = {
 					"account": data.account,
-					"debit_in_account_currency": flt(data.amount, precision)
+					"debit_in_account_currency": flt(data.amount, precision),
 				}
 				if data.reference_document_type == "Expense Claim":
 					account_dict["party_type"] = "Employee"
@@ -124,7 +128,7 @@
 			if data.amount > 0:
 				account_dict = {
 					"account": data.account,
-					"credit_in_account_currency": flt(data.amount, precision)
+					"credit_in_account_currency": flt(data.amount, precision),
 				}
 				if data.reference_document_type == "Employee Advance":
 					account_dict["party_type"] = "Employee"
@@ -132,46 +136,67 @@
 
 				jv.append("accounts", account_dict)
 
-		jv.append("accounts", {
-			"credit_in_account_currency": difference if difference > 0 else 0,
-			"debit_in_account_currency": -(difference) if difference < 0 else 0,
-			"reference_type": self.doctype,
-			"reference_name": self.name
-		})
+		jv.append(
+			"accounts",
+			{
+				"credit_in_account_currency": difference if difference > 0 else 0,
+				"debit_in_account_currency": -(difference) if difference < 0 else 0,
+				"reference_type": self.doctype,
+				"reference_name": self.name,
+			},
+		)
 		return jv
 
+
 @frappe.whitelist()
 def get_account_and_amount(ref_doctype, ref_document):
 	if not ref_doctype or not ref_document:
 		return None
 
 	if ref_doctype == "Salary Slip":
-		salary_details = frappe.db.get_value("Salary Slip", ref_document, ["payroll_entry", "net_pay"], as_dict=1)
+		salary_details = frappe.db.get_value(
+			"Salary Slip", ref_document, ["payroll_entry", "net_pay"], as_dict=1
+		)
 		amount = salary_details.net_pay
-		payable_account = frappe.db.get_value("Payroll Entry", salary_details.payroll_entry, "payroll_payable_account") if salary_details.payroll_entry else None
+		payable_account = (
+			frappe.db.get_value("Payroll Entry", salary_details.payroll_entry, "payroll_payable_account")
+			if salary_details.payroll_entry
+			else None
+		)
 		return [payable_account, amount]
 
 	if ref_doctype == "Gratuity":
-		payable_account, amount = frappe.db.get_value("Gratuity", ref_document, ["payable_account", "amount"])
+		payable_account, amount = frappe.db.get_value(
+			"Gratuity", ref_document, ["payable_account", "amount"]
+		)
 		return [payable_account, amount]
 
 	if ref_doctype == "Expense Claim":
-		details = frappe.db.get_value("Expense Claim", ref_document,
-			["payable_account", "grand_total", "total_amount_reimbursed", "total_advance_amount"], as_dict=True)
+		details = frappe.db.get_value(
+			"Expense Claim",
+			ref_document,
+			["payable_account", "grand_total", "total_amount_reimbursed", "total_advance_amount"],
+			as_dict=True,
+		)
 		payable_account = details.payable_account
 		amount = details.grand_total - (details.total_amount_reimbursed + details.total_advance_amount)
 		return [payable_account, amount]
 
 	if ref_doctype == "Loan":
-		details = frappe.db.get_value("Loan", ref_document,
-			["payment_account", "total_payment", "total_amount_paid"], as_dict=1)
+		details = frappe.db.get_value(
+			"Loan", ref_document, ["payment_account", "total_payment", "total_amount_paid"], as_dict=1
+		)
 		payment_account = details.payment_account
 		amount = details.total_payment - details.total_amount_paid
 		return [payment_account, amount]
 
 	if ref_doctype == "Employee Advance":
-		details = frappe.db.get_value("Employee Advance", ref_document,
-			["advance_account","paid_amount", "claimed_amount", "return_amount"], as_dict = 1)
+		details = frappe.db.get_value(
+			"Employee Advance",
+			ref_document,
+			["advance_account", "paid_amount", "claimed_amount", "return_amount"],
+			as_dict=1,
+		)
 		payment_account = details.advance_account
 		amount = details.paid_amount - (details.claimed_amount + details.return_amount)
 		return [payment_account, amount]
diff --git a/erpnext/hr/doctype/full_and_final_statement/test_full_and_final_statement.py b/erpnext/hr/doctype/full_and_final_statement/test_full_and_final_statement.py
index f6c1d15..8c6723f 100644
--- a/erpnext/hr/doctype/full_and_final_statement/test_full_and_final_statement.py
+++ b/erpnext/hr/doctype/full_and_final_statement/test_full_and_final_statement.py
@@ -12,7 +12,6 @@
 
 
 class TestFullandFinalStatement(unittest.TestCase):
-
 	def setUp(self):
 		create_asset_data()
 
@@ -27,18 +26,26 @@
 		frappe.db.set_value("Employee", employee, "relieving_date", add_days(today(), 30))
 		fnf = create_full_and_final_statement(employee)
 
-		payables_bootstraped_component = ["Salary Slip", "Gratuity",
-			"Expense Claim", "Bonus", "Leave Encashment"]
+		payables_bootstraped_component = [
+			"Salary Slip",
+			"Gratuity",
+			"Expense Claim",
+			"Bonus",
+			"Leave Encashment",
+		]
 
 		receivable_bootstraped_component = ["Loan", "Employee Advance"]
 
-		#checking payable s and receivables bootstraped value
+		# checking payable s and receivables bootstraped value
 		self.assertEqual([payable.component for payable in fnf.payables], payables_bootstraped_component)
-		self.assertEqual([receivable.component for receivable in fnf.receivables], receivable_bootstraped_component)
+		self.assertEqual(
+			[receivable.component for receivable in fnf.receivables], receivable_bootstraped_component
+		)
 
-		#checking allocated asset
+		# checking allocated asset
 		self.assertIn(movement, [asset.reference for asset in fnf.assets_allocated])
 
+
 def create_full_and_final_statement(employee):
 	fnf = frappe.new_doc("Full and Final Statement")
 	fnf.employee = employee
@@ -46,6 +53,7 @@
 	fnf.save()
 	return fnf
 
+
 def create_asset_movement(employee):
 	asset_name = create_asset()
 	movement = frappe.new_doc("Asset Movement")
@@ -53,18 +61,17 @@
 	movement.purpose = "Issue"
 	movement.transaction_date = today()
 
-	movement.append("assets", {
-		"asset": asset_name,
-		"to_employee": employee
-	})
+	movement.append("assets", {"asset": asset_name, "to_employee": employee})
 
 	movement.save()
 	movement.submit()
 	return movement.name
 
+
 def create_asset():
-	pr = make_purchase_receipt(item_code="Macbook Pro",
-			qty=1, rate=100000.0, location="Test Location")
+	pr = make_purchase_receipt(
+		item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location"
+	)
 
 	asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name")
 	asset = frappe.get_doc("Asset", asset_name)
diff --git a/erpnext/hr/doctype/holiday_list/holiday_list.py b/erpnext/hr/doctype/holiday_list/holiday_list.py
index a8c8c16..fad827a 100644
--- a/erpnext/hr/doctype/holiday_list/holiday_list.py
+++ b/erpnext/hr/doctype/holiday_list/holiday_list.py
@@ -10,7 +10,9 @@
 from frappe.utils import cint, formatdate, getdate, today
 
 
-class OverlapError(frappe.ValidationError): pass
+class OverlapError(frappe.ValidationError):
+	pass
+
 
 class HolidayList(Document):
 	def validate(self):
@@ -21,9 +23,14 @@
 	def get_weekly_off_dates(self):
 		self.validate_values()
 		date_list = self.get_weekly_off_date_list(self.from_date, self.to_date)
-		last_idx = max([cint(d.idx) for d in self.get("holidays")] or [0,])
+		last_idx = max(
+			[cint(d.idx) for d in self.get("holidays")]
+			or [
+				0,
+			]
+		)
 		for i, d in enumerate(date_list):
-			ch = self.append('holidays', {})
+			ch = self.append("holidays", {})
 			ch.description = _(self.weekly_off)
 			ch.holiday_date = d
 			ch.weekly_off = 1
@@ -33,14 +40,17 @@
 		if not self.weekly_off:
 			throw(_("Please select weekly off day"))
 
-
 	def validate_days(self):
 		if getdate(self.from_date) > getdate(self.to_date):
 			throw(_("To Date cannot be before From Date"))
 
 		for day in self.get("holidays"):
 			if not (getdate(self.from_date) <= getdate(day.holiday_date) <= getdate(self.to_date)):
-				frappe.throw(_("The holiday on {0} is not between From Date and To Date").format(formatdate(day.holiday_date)))
+				frappe.throw(
+					_("The holiday on {0} is not between From Date and To Date").format(
+						formatdate(day.holiday_date)
+					)
+				)
 
 	def get_weekly_off_date_list(self, start_date, end_date):
 		start_date, end_date = getdate(start_date), getdate(end_date)
@@ -66,7 +76,8 @@
 
 	@frappe.whitelist()
 	def clear_table(self):
-		self.set('holidays', [])
+		self.set("holidays", [])
+
 
 @frappe.whitelist()
 def get_events(start, end, filters=None):
@@ -82,23 +93,28 @@
 		filters = []
 
 	if start:
-		filters.append(['Holiday', 'holiday_date', '>', getdate(start)])
+		filters.append(["Holiday", "holiday_date", ">", getdate(start)])
 	if end:
-		filters.append(['Holiday', 'holiday_date', '<', getdate(end)])
+		filters.append(["Holiday", "holiday_date", "<", getdate(end)])
 
-	return frappe.get_list('Holiday List',
-		fields=['name', '`tabHoliday`.holiday_date', '`tabHoliday`.description', '`tabHoliday List`.color'],
-		filters = filters,
-		update={"allDay": 1})
+	return frappe.get_list(
+		"Holiday List",
+		fields=[
+			"name",
+			"`tabHoliday`.holiday_date",
+			"`tabHoliday`.description",
+			"`tabHoliday List`.color",
+		],
+		filters=filters,
+		update={"allDay": 1},
+	)
 
 
 def is_holiday(holiday_list, date=None):
-	"""Returns true if the given date is a holiday in the given holiday list
-	"""
+	"""Returns true if the given date is a holiday in the given holiday list"""
 	if date is None:
 		date = today()
 	if holiday_list:
-		return bool(frappe.get_all('Holiday List',
-			dict(name=holiday_list, holiday_date=date)))
+		return bool(frappe.get_all("Holiday List", dict(name=holiday_list, holiday_date=date)))
 	else:
 		return False
diff --git a/erpnext/hr/doctype/holiday_list/holiday_list_dashboard.py b/erpnext/hr/doctype/holiday_list/holiday_list_dashboard.py
index 4a540ce..0cbf094 100644
--- a/erpnext/hr/doctype/holiday_list/holiday_list_dashboard.py
+++ b/erpnext/hr/doctype/holiday_list/holiday_list_dashboard.py
@@ -1,19 +1,15 @@
 def get_data():
 	return {
-		'fieldname': 'holiday_list',
-		'non_standard_fieldnames': {
-			'Company': 'default_holiday_list',
-			'Leave Period': 'optional_holiday_list'
+		"fieldname": "holiday_list",
+		"non_standard_fieldnames": {
+			"Company": "default_holiday_list",
+			"Leave Period": "optional_holiday_list",
 		},
-		'transactions': [
+		"transactions": [
 			{
-				'items': ['Company', 'Employee', 'Workstation'],
+				"items": ["Company", "Employee", "Workstation"],
 			},
-			{
-				'items': ['Leave Period', 'Shift Type']
-			},
-			{
-				'items': ['Service Level', 'Service Level Agreement']
-			}
-		]
+			{"items": ["Leave Period", "Shift Type"]},
+			{"items": ["Service Level", "Service Level Agreement"]},
+		],
 	}
diff --git a/erpnext/hr/doctype/holiday_list/test_holiday_list.py b/erpnext/hr/doctype/holiday_list/test_holiday_list.py
index c9239ed..d32cfe8 100644
--- a/erpnext/hr/doctype/holiday_list/test_holiday_list.py
+++ b/erpnext/hr/doctype/holiday_list/test_holiday_list.py
@@ -2,6 +2,7 @@
 # License: GNU General Public License v3. See license.txt
 
 import unittest
+from contextlib import contextmanager
 from datetime import timedelta
 
 import frappe
@@ -11,22 +12,50 @@
 class TestHolidayList(unittest.TestCase):
 	def test_holiday_list(self):
 		today_date = getdate()
-		test_holiday_dates = [today_date-timedelta(days=5), today_date-timedelta(days=4)]
-		holiday_list = make_holiday_list("test_holiday_list",
+		test_holiday_dates = [today_date - timedelta(days=5), today_date - timedelta(days=4)]
+		holiday_list = make_holiday_list(
+			"test_holiday_list",
 			holiday_dates=[
-				{'holiday_date': test_holiday_dates[0], 'description': 'test holiday'},
-				{'holiday_date': test_holiday_dates[1], 'description': 'test holiday2'}
-			])
-		fetched_holiday_list = frappe.get_value('Holiday List', holiday_list.name)
+				{"holiday_date": test_holiday_dates[0], "description": "test holiday"},
+				{"holiday_date": test_holiday_dates[1], "description": "test holiday2"},
+			],
+		)
+		fetched_holiday_list = frappe.get_value("Holiday List", holiday_list.name)
 		self.assertEqual(holiday_list.name, fetched_holiday_list)
 
-def make_holiday_list(name, from_date=getdate()-timedelta(days=10), to_date=getdate(), holiday_dates=None):
+
+def make_holiday_list(
+	name, from_date=getdate() - timedelta(days=10), to_date=getdate(), holiday_dates=None
+):
 	frappe.delete_doc_if_exists("Holiday List", name, force=1)
-	doc = frappe.get_doc({
-		"doctype": "Holiday List",
-		"holiday_list_name": name,
-		"from_date" : from_date,
-		"to_date" : to_date,
-		"holidays" : holiday_dates
-		}).insert()
+	doc = frappe.get_doc(
+		{
+			"doctype": "Holiday List",
+			"holiday_list_name": name,
+			"from_date": from_date,
+			"to_date": to_date,
+			"holidays": holiday_dates,
+		}
+	).insert()
 	return doc
+
+
+@contextmanager
+def set_holiday_list(holiday_list, company_name):
+	"""
+	Context manager for setting holiday list in tests
+	"""
+	try:
+		company = frappe.get_doc("Company", company_name)
+		previous_holiday_list = company.default_holiday_list
+
+		company.default_holiday_list = holiday_list
+		company.save()
+
+		yield
+
+	finally:
+		# restore holiday list setup
+		company = frappe.get_doc("Company", company_name)
+		company.default_holiday_list = previous_holiday_list
+		company.save()
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.py b/erpnext/hr/doctype/hr_settings/hr_settings.py
index c295bcb..b56f3db 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.py
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.py
@@ -10,6 +10,7 @@
 # Wether to proceed with frequency change
 PROCEED_WITH_FREQUENCY_CHANGE = False
 
+
 class HRSettings(Document):
 	def validate(self):
 		self.set_naming_series()
@@ -21,22 +22,25 @@
 		PROCEED_WITH_FREQUENCY_CHANGE = False
 
 	def set_naming_series(self):
-		from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
-		set_by_naming_series("Employee", "employee_number",
-			self.get("emp_created_by")=="Naming Series", hide_name_field=True)
+		from erpnext.utilities.naming import set_by_naming_series
+
+		set_by_naming_series(
+			"Employee",
+			"employee_number",
+			self.get("emp_created_by") == "Naming Series",
+			hide_name_field=True,
+		)
 
 	def validate_frequency_change(self):
 		weekly_job, monthly_job = None, None
 
 		try:
 			weekly_job = frappe.get_doc(
-				'Scheduled Job Type',
-				'employee_reminders.send_reminders_in_advance_weekly'
+				"Scheduled Job Type", "employee_reminders.send_reminders_in_advance_weekly"
 			)
 
 			monthly_job = frappe.get_doc(
-				'Scheduled Job Type',
-				'employee_reminders.send_reminders_in_advance_monthly'
+				"Scheduled Job Type", "employee_reminders.send_reminders_in_advance_monthly"
 			)
 		except frappe.DoesNotExistError:
 			return
@@ -62,17 +66,20 @@
 		from_date = frappe.bold(format_date(from_date))
 		to_date = frappe.bold(format_date(to_date))
 		frappe.msgprint(
-			msg=frappe._('Employees will miss holiday reminders from {} until {}. <br> Do you want to proceed with this change?').format(from_date, to_date),
-			title='Confirm change in Frequency',
+			msg=frappe._(
+				"Employees will miss holiday reminders from {} until {}. <br> Do you want to proceed with this change?"
+			).format(from_date, to_date),
+			title="Confirm change in Frequency",
 			primary_action={
-				'label': frappe._('Yes, Proceed'),
-				'client_action': 'erpnext.proceed_save_with_reminders_frequency_change'
+				"label": frappe._("Yes, Proceed"),
+				"client_action": "erpnext.proceed_save_with_reminders_frequency_change",
 			},
-			raise_exception=frappe.ValidationError
+			raise_exception=frappe.ValidationError,
 		)
 
+
 @frappe.whitelist()
 def set_proceed_with_frequency_change():
-	'''Enables proceed with frequency change'''
+	"""Enables proceed with frequency change"""
 	global PROCEED_WITH_FREQUENCY_CHANGE
 	PROCEED_WITH_FREQUENCY_CHANGE = True
diff --git a/erpnext/hr/doctype/interest/test_interest.py b/erpnext/hr/doctype/interest/test_interest.py
index d4ecd9b..eacb57f 100644
--- a/erpnext/hr/doctype/interest/test_interest.py
+++ b/erpnext/hr/doctype/interest/test_interest.py
@@ -5,5 +5,6 @@
 
 # test_records = frappe.get_test_records('Interest')
 
+
 class TestInterest(unittest.TestCase):
 	pass
diff --git a/erpnext/hr/doctype/interview/interview.py b/erpnext/hr/doctype/interview/interview.py
index a3b111c..a6e9af2 100644
--- a/erpnext/hr/doctype/interview/interview.py
+++ b/erpnext/hr/doctype/interview/interview.py
@@ -13,6 +13,7 @@
 class DuplicateInterviewRoundError(frappe.ValidationError):
 	pass
 
+
 class Interview(Document):
 	def validate(self):
 		self.validate_duplicate_interview()
@@ -21,37 +22,47 @@
 		self.set_average_rating()
 
 	def on_submit(self):
-		if self.status not in ['Cleared', 'Rejected']:
-			frappe.throw(_('Only Interviews with Cleared or Rejected status can be submitted.'), title=_('Not Allowed'))
+		if self.status not in ["Cleared", "Rejected"]:
+			frappe.throw(
+				_("Only Interviews with Cleared or Rejected status can be submitted."), title=_("Not Allowed")
+			)
 
 	def validate_duplicate_interview(self):
-		duplicate_interview = frappe.db.exists('Interview', {
-				'job_applicant': self.job_applicant,
-				'interview_round': self.interview_round,
-				'docstatus': 1
-			}
+		duplicate_interview = frappe.db.exists(
+			"Interview",
+			{"job_applicant": self.job_applicant, "interview_round": self.interview_round, "docstatus": 1},
 		)
 
 		if duplicate_interview:
-			frappe.throw(_('Job Applicants are not allowed to appear twice for the same Interview round. Interview {0} already scheduled for Job Applicant {1}').format(
-				frappe.bold(get_link_to_form('Interview', duplicate_interview)),
-				frappe.bold(self.job_applicant)
-			))
+			frappe.throw(
+				_(
+					"Job Applicants are not allowed to appear twice for the same Interview round. Interview {0} already scheduled for Job Applicant {1}"
+				).format(
+					frappe.bold(get_link_to_form("Interview", duplicate_interview)),
+					frappe.bold(self.job_applicant),
+				)
+			)
 
 	def validate_designation(self):
-		applicant_designation = frappe.db.get_value('Job Applicant', self.job_applicant, 'designation')
-		if self.designation :
+		applicant_designation = frappe.db.get_value("Job Applicant", self.job_applicant, "designation")
+		if self.designation:
 			if self.designation != applicant_designation:
-				frappe.throw(_('Interview Round {0} is only for Designation {1}. Job Applicant has applied for the role {2}').format(
-					self.interview_round, frappe.bold(self.designation), applicant_designation),
-					exc=DuplicateInterviewRoundError)
+				frappe.throw(
+					_(
+						"Interview Round {0} is only for Designation {1}. Job Applicant has applied for the role {2}"
+					).format(
+						self.interview_round, frappe.bold(self.designation), applicant_designation
+					),
+					exc=DuplicateInterviewRoundError,
+				)
 		else:
 			self.designation = applicant_designation
 
 	def validate_overlap(self):
-		interviewers = [entry.interviewer for entry in self.interview_details] or ['']
+		interviewers = [entry.interviewer for entry in self.interview_details] or [""]
 
-		overlaps = frappe.db.sql("""
+		overlaps = frappe.db.sql(
+			"""
 			SELECT interview.name
 			FROM `tabInterview` as interview
 			INNER JOIN `tabInterview Detail` as detail
@@ -61,12 +72,25 @@
 				((from_time < %s and to_time > %s) or
 				(from_time > %s and to_time < %s) or
 				(from_time = %s))
-			""", (self.scheduled_on, self.name, self.job_applicant, interviewers,
-			self.from_time, self.to_time, self.from_time, self.to_time, self.from_time))
+			""",
+			(
+				self.scheduled_on,
+				self.name,
+				self.job_applicant,
+				interviewers,
+				self.from_time,
+				self.to_time,
+				self.from_time,
+				self.to_time,
+				self.from_time,
+			),
+		)
 
 		if overlaps:
-			overlapping_details = _('Interview overlaps with {0}').format(get_link_to_form('Interview', overlaps[0][0]))
-			frappe.throw(overlapping_details, title=_('Overlap'))
+			overlapping_details = _("Interview overlaps with {0}").format(
+				get_link_to_form("Interview", overlaps[0][0])
+			)
+			frappe.throw(overlapping_details, title=_("Overlap"))
 
 	def set_average_rating(self):
 		total_rating = 0
@@ -74,7 +98,9 @@
 			if entry.average_rating:
 				total_rating += entry.average_rating
 
-		self.average_rating = flt(total_rating / len(self.interview_details) if len(self.interview_details) else 0)
+		self.average_rating = flt(
+			total_rating / len(self.interview_details) if len(self.interview_details) else 0
+		)
 
 	@frappe.whitelist()
 	def reschedule_interview(self, scheduled_on, from_time, to_time):
@@ -82,137 +108,155 @@
 		from_time = self.from_time
 		to_time = self.to_time
 
-		self.db_set({
-			'scheduled_on': scheduled_on,
-			'from_time': from_time,
-			'to_time': to_time
-		})
+		self.db_set({"scheduled_on": scheduled_on, "from_time": from_time, "to_time": to_time})
 		self.notify_update()
 
 		recipients = get_recipients(self.name)
 
 		try:
 			frappe.sendmail(
-				recipients= recipients,
-				subject=_('Interview: {0} Rescheduled').format(self.name),
-				message=_('Your Interview session is rescheduled from {0} {1} - {2} to {3} {4} - {5}').format(
-					original_date, from_time, to_time, self.scheduled_on, self.from_time, self.to_time),
+				recipients=recipients,
+				subject=_("Interview: {0} Rescheduled").format(self.name),
+				message=_("Your Interview session is rescheduled from {0} {1} - {2} to {3} {4} - {5}").format(
+					original_date, from_time, to_time, self.scheduled_on, self.from_time, self.to_time
+				),
 				reference_doctype=self.doctype,
-				reference_name=self.name
+				reference_name=self.name,
 			)
 		except Exception:
-			frappe.msgprint(_('Failed to send the Interview Reschedule notification. Please configure your email account.'))
+			frappe.msgprint(
+				_("Failed to send the Interview Reschedule notification. Please configure your email account.")
+			)
 
-		frappe.msgprint(_('Interview Rescheduled successfully'), indicator='green')
+		frappe.msgprint(_("Interview Rescheduled successfully"), indicator="green")
 
 
 def get_recipients(name, for_feedback=0):
-	interview = frappe.get_doc('Interview', name)
+	interview = frappe.get_doc("Interview", name)
 
 	if for_feedback:
 		recipients = [d.interviewer for d in interview.interview_details if not d.interview_feedback]
 	else:
 		recipients = [d.interviewer for d in interview.interview_details]
-		recipients.append(frappe.db.get_value('Job Applicant', interview.job_applicant, 'email_id'))
+		recipients.append(frappe.db.get_value("Job Applicant", interview.job_applicant, "email_id"))
 
 	return recipients
 
 
 @frappe.whitelist()
 def get_interviewers(interview_round):
-	return frappe.get_all('Interviewer', filters={'parent': interview_round}, fields=['user as interviewer'])
+	return frappe.get_all(
+		"Interviewer", filters={"parent": interview_round}, fields=["user as interviewer"]
+	)
 
 
 def send_interview_reminder():
-	reminder_settings = frappe.db.get_value('HR Settings', 'HR Settings',
-		['send_interview_reminder', 'interview_reminder_template'], as_dict=True)
+	reminder_settings = frappe.db.get_value(
+		"HR Settings",
+		"HR Settings",
+		["send_interview_reminder", "interview_reminder_template"],
+		as_dict=True,
+	)
 
 	if not reminder_settings.send_interview_reminder:
 		return
 
-	remind_before = cstr(frappe.db.get_single_value('HR Settings', 'remind_before')) or '01:00:00'
-	remind_before = datetime.datetime.strptime(remind_before, '%H:%M:%S')
+	remind_before = cstr(frappe.db.get_single_value("HR Settings", "remind_before")) or "01:00:00"
+	remind_before = datetime.datetime.strptime(remind_before, "%H:%M:%S")
 	reminder_date_time = datetime.datetime.now() + datetime.timedelta(
-		hours=remind_before.hour, minutes=remind_before.minute, seconds=remind_before.second)
+		hours=remind_before.hour, minutes=remind_before.minute, seconds=remind_before.second
+	)
 
-	interviews = frappe.get_all('Interview', filters={
-		'scheduled_on': ['between', (datetime.datetime.now(), reminder_date_time)],
-		'status': 'Pending',
-		'reminded': 0,
-		'docstatus': ['!=', 2]
-	})
+	interviews = frappe.get_all(
+		"Interview",
+		filters={
+			"scheduled_on": ["between", (datetime.datetime.now(), reminder_date_time)],
+			"status": "Pending",
+			"reminded": 0,
+			"docstatus": ["!=", 2],
+		},
+	)
 
-	interview_template = frappe.get_doc('Email Template', reminder_settings.interview_reminder_template)
+	interview_template = frappe.get_doc(
+		"Email Template", reminder_settings.interview_reminder_template
+	)
 
 	for d in interviews:
-		doc = frappe.get_doc('Interview', d.name)
+		doc = frappe.get_doc("Interview", d.name)
 		context = doc.as_dict()
 		message = frappe.render_template(interview_template.response, context)
 		recipients = get_recipients(doc.name)
 
 		frappe.sendmail(
-			recipients= recipients,
+			recipients=recipients,
 			subject=interview_template.subject,
 			message=message,
 			reference_doctype=doc.doctype,
-			reference_name=doc.name
+			reference_name=doc.name,
 		)
 
-		doc.db_set('reminded', 1)
+		doc.db_set("reminded", 1)
 
 
 def send_daily_feedback_reminder():
-	reminder_settings = frappe.db.get_value('HR Settings', 'HR Settings',
-		['send_interview_feedback_reminder', 'feedback_reminder_notification_template'], as_dict=True)
+	reminder_settings = frappe.db.get_value(
+		"HR Settings",
+		"HR Settings",
+		["send_interview_feedback_reminder", "feedback_reminder_notification_template"],
+		as_dict=True,
+	)
 
 	if not reminder_settings.send_interview_feedback_reminder:
 		return
 
-	interview_feedback_template = frappe.get_doc('Email Template', reminder_settings.feedback_reminder_notification_template)
-	interviews = frappe.get_all('Interview', filters={'status': ['in', ['Under Review', 'Pending']], 'docstatus': ['!=', 2]})
+	interview_feedback_template = frappe.get_doc(
+		"Email Template", reminder_settings.feedback_reminder_notification_template
+	)
+	interviews = frappe.get_all(
+		"Interview", filters={"status": ["in", ["Under Review", "Pending"]], "docstatus": ["!=", 2]}
+	)
 
 	for entry in interviews:
 		recipients = get_recipients(entry.name, for_feedback=1)
 
-		doc = frappe.get_doc('Interview', entry.name)
+		doc = frappe.get_doc("Interview", entry.name)
 		context = doc.as_dict()
 
 		message = frappe.render_template(interview_feedback_template.response, context)
 
 		if len(recipients):
 			frappe.sendmail(
-				recipients= recipients,
+				recipients=recipients,
 				subject=interview_feedback_template.subject,
 				message=message,
-				reference_doctype='Interview',
-				reference_name=entry.name
+				reference_doctype="Interview",
+				reference_name=entry.name,
 			)
 
 
 @frappe.whitelist()
 def get_expected_skill_set(interview_round):
-	return frappe.get_all('Expected Skill Set', filters ={'parent': interview_round}, fields=['skill'])
+	return frappe.get_all("Expected Skill Set", filters={"parent": interview_round}, fields=["skill"])
 
 
 @frappe.whitelist()
 def create_interview_feedback(data, interview_name, interviewer, job_applicant):
 	import json
 
-
 	if isinstance(data, str):
 		data = frappe._dict(json.loads(data))
 
 	if frappe.session.user != interviewer:
-		frappe.throw(_('Only Interviewer Are allowed to submit Interview Feedback'))
+		frappe.throw(_("Only Interviewer Are allowed to submit Interview Feedback"))
 
-	interview_feedback = frappe.new_doc('Interview Feedback')
+	interview_feedback = frappe.new_doc("Interview Feedback")
 	interview_feedback.interview = interview_name
 	interview_feedback.interviewer = interviewer
 	interview_feedback.job_applicant = job_applicant
 
 	for d in data.skill_set:
 		d = frappe._dict(d)
-		interview_feedback.append('skill_assessment', {'skill': d.skill, 'rating': d.rating})
+		interview_feedback.append("skill_assessment", {"skill": d.skill, "rating": d.rating})
 
 	interview_feedback.feedback = data.feedback
 	interview_feedback.result = data.result
@@ -220,24 +264,33 @@
 	interview_feedback.save()
 	interview_feedback.submit()
 
-	frappe.msgprint(_('Interview Feedback {0} submitted successfully').format(
-		get_link_to_form('Interview Feedback', interview_feedback.name)))
+	frappe.msgprint(
+		_("Interview Feedback {0} submitted successfully").format(
+			get_link_to_form("Interview Feedback", interview_feedback.name)
+		)
+	)
 
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_interviewer_list(doctype, txt, searchfield, start, page_len, filters):
 	filters = [
-		['Has Role', 'parent', 'like', '%{}%'.format(txt)],
-		['Has Role', 'role', '=', 'interviewer'],
-		['Has Role', 'parenttype', '=', 'User']
+		["Has Role", "parent", "like", "%{}%".format(txt)],
+		["Has Role", "role", "=", "interviewer"],
+		["Has Role", "parenttype", "=", "User"],
 	]
 
 	if filters and isinstance(filters, list):
 		filters.extend(filters)
 
-	return frappe.get_all('Has Role', limit_start=start, limit_page_length=page_len,
-		filters=filters, fields = ['parent'], as_list=1)
+	return frappe.get_all(
+		"Has Role",
+		limit_start=start,
+		limit_page_length=page_len,
+		filters=filters,
+		fields=["parent"],
+		as_list=1,
+	)
 
 
 @frappe.whitelist()
@@ -256,12 +309,13 @@
 		"Pending": "#fff4f0",
 		"Under Review": "#d3e8fc",
 		"Cleared": "#eaf5ed",
-		"Rejected": "#fce7e7"
+		"Rejected": "#fce7e7",
 	}
 
-	conditions = get_event_conditions('Interview', filters)
+	conditions = get_event_conditions("Interview", filters)
 
-	interviews = frappe.db.sql("""
+	interviews = frappe.db.sql(
+		"""
 			SELECT DISTINCT
 				`tabInterview`.name, `tabInterview`.job_applicant, `tabInterview`.interview_round,
 				`tabInterview`.scheduled_on, `tabInterview`.status, `tabInterview`.from_time as from_time,
@@ -272,10 +326,13 @@
 				(`tabInterview`.scheduled_on between %(start)s and %(end)s)
 				and docstatus != 2
 				{conditions}
-			""".format(conditions=conditions), {
-				"start": start,
-				"end": end
-			}, as_dict=True, update={"allDay": 0})
+			""".format(
+			conditions=conditions
+		),
+		{"start": start, "end": end},
+		as_dict=True,
+		update={"allDay": 0},
+	)
 
 	for d in interviews:
 		subject_data = []
@@ -286,11 +343,11 @@
 
 		color = event_color.get(d.status)
 		interview_data = {
-			'from': get_datetime('%s %s' % (d.scheduled_on, d.from_time or '00:00:00')),
-			'to': get_datetime('%s %s' % (d.scheduled_on, d.to_time or '00:00:00')),
-			'name': d.name,
-			'subject': '\n'.join(subject_data),
-			'color': color if color else "#89bcde"
+			"from": get_datetime("%s %s" % (d.scheduled_on, d.from_time or "00:00:00")),
+			"to": get_datetime("%s %s" % (d.scheduled_on, d.to_time or "00:00:00")),
+			"name": d.name,
+			"subject": "\n".join(subject_data),
+			"color": color if color else "#89bcde",
 		}
 
 		events.append(interview_data)
diff --git a/erpnext/hr/doctype/interview/test_interview.py b/erpnext/hr/doctype/interview/test_interview.py
index fdb11af..ae8493a 100644
--- a/erpnext/hr/doctype/interview/test_interview.py
+++ b/erpnext/hr/doctype/interview/test_interview.py
@@ -19,23 +19,30 @@
 class TestInterview(unittest.TestCase):
 	def test_validations_for_designation(self):
 		job_applicant = create_job_applicant()
-		interview = create_interview_and_dependencies(job_applicant.name, designation='_Test_Sales_manager', save=0)
+		interview = create_interview_and_dependencies(
+			job_applicant.name, designation="_Test_Sales_manager", save=0
+		)
 		self.assertRaises(DuplicateInterviewRoundError, interview.save)
 
 	def test_notification_on_rescheduling(self):
 		job_applicant = create_job_applicant()
-		interview = create_interview_and_dependencies(job_applicant.name, scheduled_on=add_days(getdate(), -4))
+		interview = create_interview_and_dependencies(
+			job_applicant.name, scheduled_on=add_days(getdate(), -4)
+		)
 
 		previous_scheduled_date = interview.scheduled_on
 		frappe.db.sql("DELETE FROM `tabEmail Queue`")
 
-		interview.reschedule_interview(add_days(getdate(previous_scheduled_date), 2),
-			from_time=nowtime(), to_time=nowtime())
+		interview.reschedule_interview(
+			add_days(getdate(previous_scheduled_date), 2), from_time=nowtime(), to_time=nowtime()
+		)
 		interview.reload()
 
 		self.assertEqual(interview.scheduled_on, add_days(getdate(previous_scheduled_date), 2))
 
-		notification = frappe.get_all("Email Queue", filters={"message": ("like", "%Your Interview session is rescheduled from%")})
+		notification = frappe.get_all(
+			"Email Queue", filters={"message": ("like", "%Your Interview session is rescheduled from%")}
+		)
 		self.assertIsNotNone(notification)
 
 	def test_notification_for_scheduling(self):
@@ -76,29 +83,33 @@
 		interview = create_interview_and_dependencies(job_applicant.name)
 
 		details = get_interview_details(job_applicant.name)
-		self.assertEqual(details.get('stars'), 5)
-		self.assertEqual(details.get('interviews').get(interview.name), {
-			'name': interview.name,
-			'interview_round': interview.interview_round,
-			'expected_average_rating': interview.expected_average_rating * 5,
-			'average_rating': interview.average_rating * 5,
-			'status': 'Pending'
-		})
+		self.assertEqual(details.get("stars"), 5)
+		self.assertEqual(
+			details.get("interviews").get(interview.name),
+			{
+				"name": interview.name,
+				"interview_round": interview.interview_round,
+				"expected_average_rating": interview.expected_average_rating * 5,
+				"average_rating": interview.average_rating * 5,
+				"status": "Pending",
+			},
+		)
 
 	def tearDown(self):
 		frappe.db.rollback()
 
 
-def create_interview_and_dependencies(job_applicant, scheduled_on=None, from_time=None, to_time=None, designation=None, save=1):
+def create_interview_and_dependencies(
+	job_applicant, scheduled_on=None, from_time=None, to_time=None, designation=None, save=1
+):
 	if designation:
-		designation=create_designation(designation_name = "_Test_Sales_manager").name
+		designation = create_designation(designation_name="_Test_Sales_manager").name
 
 	interviewer_1 = create_user("test_interviewer1@example.com", "Interviewer")
 	interviewer_2 = create_user("test_interviewer2@example.com", "Interviewer")
 
 	interview_round = create_interview_round(
-		"Technical Round", ["Python", "JS"],
-		designation=designation, save=True
+		"Technical Round", ["Python", "JS"], designation=designation, save=True
 	)
 
 	interview = frappe.new_doc("Interview")
@@ -116,6 +127,7 @@
 
 	return interview
 
+
 def create_interview_round(name, skill_set, interviewers=[], designation=None, save=True):
 	create_skill_set(skill_set)
 	interview_round = frappe.new_doc("Interview Round")
@@ -130,15 +142,14 @@
 		interview_round.append("expected_skill_set", {"skill": skill})
 
 	for interviewer in interviewers:
-		interview_round.append("interviewer", {
-			"user": interviewer
-		})
+		interview_round.append("interviewer", {"user": interviewer})
 
 	if save:
 		interview_round.save()
 
 	return interview_round
 
+
 def create_skill_set(skill_set):
 	for skill in skill_set:
 		if not frappe.db.exists("Skill", skill):
@@ -146,6 +157,7 @@
 			doc.skill_name = skill
 			doc.save()
 
+
 def create_interview_type(name="test_interview_type"):
 	if frappe.db.exists("Interview Type", name):
 		return frappe.get_doc("Interview Type", name).name
@@ -157,32 +169,41 @@
 
 		return doc.name
 
+
 def setup_reminder_settings():
-	if not frappe.db.exists('Email Template', _('Interview Reminder')):
-		base_path = frappe.get_app_path('erpnext', 'hr', 'doctype')
-		response = frappe.read_file(os.path.join(base_path, 'interview/interview_reminder_notification_template.html'))
+	if not frappe.db.exists("Email Template", _("Interview Reminder")):
+		base_path = frappe.get_app_path("erpnext", "hr", "doctype")
+		response = frappe.read_file(
+			os.path.join(base_path, "interview/interview_reminder_notification_template.html")
+		)
 
-		frappe.get_doc({
-			'doctype': 'Email Template',
-			'name': _('Interview Reminder'),
-			'response': response,
-			'subject': _('Interview Reminder'),
-			'owner': frappe.session.user,
-		}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "Email Template",
+				"name": _("Interview Reminder"),
+				"response": response,
+				"subject": _("Interview Reminder"),
+				"owner": frappe.session.user,
+			}
+		).insert(ignore_permissions=True)
 
-	if not frappe.db.exists('Email Template', _('Interview Feedback Reminder')):
-		base_path = frappe.get_app_path('erpnext', 'hr', 'doctype')
-		response = frappe.read_file(os.path.join(base_path, 'interview/interview_feedback_reminder_template.html'))
+	if not frappe.db.exists("Email Template", _("Interview Feedback Reminder")):
+		base_path = frappe.get_app_path("erpnext", "hr", "doctype")
+		response = frappe.read_file(
+			os.path.join(base_path, "interview/interview_feedback_reminder_template.html")
+		)
 
-		frappe.get_doc({
-			'doctype': 'Email Template',
-			'name': _('Interview Feedback Reminder'),
-			'response': response,
-			'subject': _('Interview Feedback Reminder'),
-			'owner': frappe.session.user,
-		}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "Email Template",
+				"name": _("Interview Feedback Reminder"),
+				"response": response,
+				"subject": _("Interview Feedback Reminder"),
+				"owner": frappe.session.user,
+			}
+		).insert(ignore_permissions=True)
 
-	hr_settings = frappe.get_doc('HR Settings')
-	hr_settings.interview_reminder_template = _('Interview Reminder')
-	hr_settings.feedback_reminder_notification_template = _('Interview Feedback Reminder')
+	hr_settings = frappe.get_doc("HR Settings")
+	hr_settings.interview_reminder_template = _("Interview Reminder")
+	hr_settings.feedback_reminder_notification_template = _("Interview Feedback Reminder")
 	hr_settings.save()
diff --git a/erpnext/hr/doctype/interview_feedback/interview_feedback.py b/erpnext/hr/doctype/interview_feedback/interview_feedback.py
index 2ff00c1..5bb498f 100644
--- a/erpnext/hr/doctype/interview_feedback/interview_feedback.py
+++ b/erpnext/hr/doctype/interview_feedback/interview_feedback.py
@@ -24,28 +24,36 @@
 	def validate_interviewer(self):
 		applicable_interviewers = get_applicable_interviewers(self.interview)
 		if self.interviewer not in applicable_interviewers:
-			frappe.throw(_('{0} is not allowed to submit Interview Feedback for the Interview: {1}').format(
-				frappe.bold(self.interviewer), frappe.bold(self.interview)))
+			frappe.throw(
+				_("{0} is not allowed to submit Interview Feedback for the Interview: {1}").format(
+					frappe.bold(self.interviewer), frappe.bold(self.interview)
+				)
+			)
 
 	def validate_interview_date(self):
-		scheduled_date = frappe.db.get_value('Interview', self.interview, 'scheduled_on')
+		scheduled_date = frappe.db.get_value("Interview", self.interview, "scheduled_on")
 
 		if getdate() < getdate(scheduled_date) and self.docstatus == 1:
-			frappe.throw(_('{0} submission before {1} is not allowed').format(
-				frappe.bold('Interview Feedback'),
-				frappe.bold('Interview Scheduled Date')
-			))
+			frappe.throw(
+				_("{0} submission before {1} is not allowed").format(
+					frappe.bold("Interview Feedback"), frappe.bold("Interview Scheduled Date")
+				)
+			)
 
 	def validate_duplicate(self):
-		duplicate_feedback = frappe.db.exists('Interview Feedback', {
-			'interviewer': self.interviewer,
-			'interview': self.interview,
-			'docstatus': 1
-		})
+		duplicate_feedback = frappe.db.exists(
+			"Interview Feedback",
+			{"interviewer": self.interviewer, "interview": self.interview, "docstatus": 1},
+		)
 
 		if duplicate_feedback:
-			frappe.throw(_('Feedback already submitted for the Interview {0}. Please cancel the previous Interview Feedback {1} to continue.').format(
-				self.interview, get_link_to_form('Interview Feedback', duplicate_feedback)))
+			frappe.throw(
+				_(
+					"Feedback already submitted for the Interview {0}. Please cancel the previous Interview Feedback {1} to continue."
+				).format(
+					self.interview, get_link_to_form("Interview Feedback", duplicate_feedback)
+				)
+			)
 
 	def calculate_average_rating(self):
 		total_rating = 0
@@ -53,10 +61,12 @@
 			if d.rating:
 				total_rating += d.rating
 
-		self.average_rating = flt(total_rating / len(self.skill_assessment) if len(self.skill_assessment) else 0)
+		self.average_rating = flt(
+			total_rating / len(self.skill_assessment) if len(self.skill_assessment) else 0
+		)
 
 	def update_interview_details(self):
-		doc = frappe.get_doc('Interview', self.interview)
+		doc = frappe.get_doc("Interview", self.interview)
 
 		if self.docstatus == 2:
 			for entry in doc.interview_details:
@@ -77,5 +87,5 @@
 
 @frappe.whitelist()
 def get_applicable_interviewers(interview):
-	data = frappe.get_all('Interview Detail', filters={'parent': interview}, fields=['interviewer'])
+	data = frappe.get_all("Interview Detail", filters={"parent": interview}, fields=["interviewer"])
 	return [d.interviewer for d in data]
diff --git a/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py b/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py
index 19c4642..63d4775 100644
--- a/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py
+++ b/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py
@@ -17,14 +17,16 @@
 	def test_validation_for_skill_set(self):
 		frappe.set_user("Administrator")
 		job_applicant = create_job_applicant()
-		interview = create_interview_and_dependencies(job_applicant.name, scheduled_on=add_days(getdate(), -1))
+		interview = create_interview_and_dependencies(
+			job_applicant.name, scheduled_on=add_days(getdate(), -1)
+		)
 		skill_ratings = get_skills_rating(interview.interview_round)
 
 		interviewer = interview.interview_details[0].interviewer
-		create_skill_set(['Leadership'])
+		create_skill_set(["Leadership"])
 
 		interview_feedback = create_interview_feedback(interview.name, interviewer, skill_ratings)
-		interview_feedback.append("skill_assessment", {"skill": 'Leadership', 'rating': 0.8})
+		interview_feedback.append("skill_assessment", {"skill": "Leadership", "rating": 0.8})
 		frappe.set_user(interviewer)
 
 		self.assertRaises(frappe.ValidationError, interview_feedback.save)
@@ -33,7 +35,9 @@
 
 	def test_average_ratings_on_feedback_submission_and_cancellation(self):
 		job_applicant = create_job_applicant()
-		interview = create_interview_and_dependencies(job_applicant.name, scheduled_on=add_days(getdate(), -1))
+		interview = create_interview_and_dependencies(
+			job_applicant.name, scheduled_on=add_days(getdate(), -1)
+		)
 		skill_ratings = get_skills_rating(interview.interview_round)
 
 		# For First Interviewer Feedback
@@ -48,20 +52,26 @@
 			if d.rating:
 				total_rating += d.rating
 
-		avg_rating = flt(total_rating / len(feedback_1.skill_assessment) if len(feedback_1.skill_assessment) else 0)
+		avg_rating = flt(
+			total_rating / len(feedback_1.skill_assessment) if len(feedback_1.skill_assessment) else 0
+		)
 
 		self.assertEqual(flt(avg_rating, 2), flt(feedback_1.average_rating, 2))
 
-		avg_on_interview_detail = frappe.db.get_value('Interview Detail', {
-			'parent': feedback_1.interview,
-			'interviewer': feedback_1.interviewer,
-			'interview_feedback': feedback_1.name
-		}, 'average_rating')
+		avg_on_interview_detail = frappe.db.get_value(
+			"Interview Detail",
+			{
+				"parent": feedback_1.interview,
+				"interviewer": feedback_1.interviewer,
+				"interview_feedback": feedback_1.name,
+			},
+			"average_rating",
+		)
 
 		# 1. average should be reflected in Interview Detail.
 		self.assertEqual(flt(avg_on_interview_detail, 2), flt(feedback_1.average_rating, 2))
 
-		'''For Second Interviewer Feedback'''
+		"""For Second Interviewer Feedback"""
 		interviewer = interview.interview_details[1].interviewer
 		frappe.set_user(interviewer)
 
@@ -95,7 +105,9 @@
 def get_skills_rating(interview_round):
 	import random
 
-	skills = frappe.get_all("Expected Skill Set", filters={"parent": interview_round}, fields = ["skill"])
+	skills = frappe.get_all(
+		"Expected Skill Set", filters={"parent": interview_round}, fields=["skill"]
+	)
 	for d in skills:
 		d["rating"] = random.random()
 	return skills
diff --git a/erpnext/hr/doctype/interview_round/interview_round.py b/erpnext/hr/doctype/interview_round/interview_round.py
index 0f442c3..83dbf0e 100644
--- a/erpnext/hr/doctype/interview_round/interview_round.py
+++ b/erpnext/hr/doctype/interview_round/interview_round.py
@@ -11,6 +11,7 @@
 class InterviewRound(Document):
 	pass
 
+
 @frappe.whitelist()
 def create_interview(doc):
 	if isinstance(doc, str):
@@ -24,10 +25,5 @@
 	if doc.interviewers:
 		interview.interview_details = []
 		for data in doc.interviewers:
-			interview.append("interview_details", {
-				"interviewer": data.user
-			})
+			interview.append("interview_details", {"interviewer": data.user})
 	return interview
-
-
-
diff --git a/erpnext/hr/doctype/interview_round/test_interview_round.py b/erpnext/hr/doctype/interview_round/test_interview_round.py
index dcec941..9568165 100644
--- a/erpnext/hr/doctype/interview_round/test_interview_round.py
+++ b/erpnext/hr/doctype/interview_round/test_interview_round.py
@@ -8,4 +8,3 @@
 
 class TestInterviewRound(unittest.TestCase):
 	pass
-
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.py b/erpnext/hr/doctype/job_applicant/job_applicant.py
index ccc21ce..5b0a4ca 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant.py
+++ b/erpnext/hr/doctype/job_applicant/job_applicant.py
@@ -13,7 +13,9 @@
 from erpnext.hr.doctype.interview.interview import get_interviewers
 
 
-class DuplicationError(frappe.ValidationError): pass
+class DuplicationError(frappe.ValidationError):
+	pass
+
 
 class JobApplicant(Document):
 	def onload(self):
@@ -36,8 +38,8 @@
 			self.set_status_for_employee_referral()
 
 		if not self.applicant_name and self.email_id:
-			guess = self.email_id.split('@')[0]
-			self.applicant_name = ' '.join([p.capitalize() for p in guess.split('.')])
+			guess = self.email_id.split("@")[0]
+			self.applicant_name = " ".join([p.capitalize() for p in guess.split(".")])
 
 	def set_status_for_employee_referral(self):
 		emp_ref = frappe.get_doc("Employee Referral", self.employee_referral)
@@ -46,11 +48,11 @@
 		elif self.status in ["Accepted", "Rejected"]:
 			emp_ref.db_set("status", self.status)
 
+
 @frappe.whitelist()
 def create_interview(doc, interview_round):
 	import json
 
-
 	if isinstance(doc, str):
 		doc = json.loads(doc)
 		doc = frappe.get_doc(doc)
@@ -58,7 +60,11 @@
 	round_designation = frappe.db.get_value("Interview Round", interview_round, "designation")
 
 	if round_designation and doc.designation and round_designation != doc.designation:
-		frappe.throw(_("Interview Round {0} is only applicable for the Designation {1}").format(interview_round, round_designation))
+		frappe.throw(
+			_("Interview Round {0} is only applicable for the Designation {1}").format(
+				interview_round, round_designation
+			)
+		)
 
 	interview = frappe.new_doc("Interview")
 	interview.interview_round = interview_round
@@ -69,23 +75,25 @@
 	interviewer_detail = get_interviewers(interview_round)
 
 	for d in interviewer_detail:
-		interview.append("interview_details", {
-			"interviewer": d.interviewer
-		})
+		interview.append("interview_details", {"interviewer": d.interviewer})
 	return interview
 
+
 @frappe.whitelist()
 def get_interview_details(job_applicant):
-	interview_details = frappe.db.get_all("Interview",
-		filters={"job_applicant":job_applicant, "docstatus": ["!=", 2]},
-		fields=["name", "interview_round", "expected_average_rating", "average_rating", "status"]
+	interview_details = frappe.db.get_all(
+		"Interview",
+		filters={"job_applicant": job_applicant, "docstatus": ["!=", 2]},
+		fields=["name", "interview_round", "expected_average_rating", "average_rating", "status"],
 	)
 	interview_detail_map = {}
 	meta = frappe.get_meta("Interview")
 	number_of_stars = meta.get_options("expected_average_rating") or 5
 
 	for detail in interview_details:
-		detail.expected_average_rating = detail.expected_average_rating * number_of_stars if detail.expected_average_rating else 0
+		detail.expected_average_rating = (
+			detail.expected_average_rating * number_of_stars if detail.expected_average_rating else 0
+		)
 		detail.average_rating = detail.average_rating * number_of_stars if detail.average_rating else 0
 
 		interview_detail_map[detail.name] = detail
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py b/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py
index 56331ac..14b944a 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py
+++ b/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py
@@ -1,15 +1,9 @@
 def get_data():
 	return {
-		'fieldname': 'job_applicant',
-		'transactions': [
-			{
-				'items': ['Employee', 'Employee Onboarding']
-			},
-			{
-				'items': ['Job Offer', 'Appointment Letter']
-			},
-			{
-				'items': ['Interview']
-			}
+		"fieldname": "job_applicant",
+		"transactions": [
+			{"items": ["Employee", "Employee Onboarding"]},
+			{"items": ["Job Offer", "Appointment Letter"]},
+			{"items": ["Interview"]},
 		],
 	}
diff --git a/erpnext/hr/doctype/job_applicant/test_job_applicant.py b/erpnext/hr/doctype/job_applicant/test_job_applicant.py
index bf16220..99d1161 100644
--- a/erpnext/hr/doctype/job_applicant/test_job_applicant.py
+++ b/erpnext/hr/doctype/job_applicant/test_job_applicant.py
@@ -10,21 +10,25 @@
 
 class TestJobApplicant(unittest.TestCase):
 	def test_job_applicant_naming(self):
-		applicant = frappe.get_doc({
-			"doctype": "Job Applicant",
-			"status": "Open",
-			"applicant_name": "_Test Applicant",
-			"email_id": "job_applicant_naming@example.com"
-		}).insert()
-		self.assertEqual(applicant.name, 'job_applicant_naming@example.com')
+		applicant = frappe.get_doc(
+			{
+				"doctype": "Job Applicant",
+				"status": "Open",
+				"applicant_name": "_Test Applicant",
+				"email_id": "job_applicant_naming@example.com",
+			}
+		).insert()
+		self.assertEqual(applicant.name, "job_applicant_naming@example.com")
 
-		applicant = frappe.get_doc({
-			"doctype": "Job Applicant",
-			"status": "Open",
-			"applicant_name": "_Test Applicant",
-			"email_id": "job_applicant_naming@example.com"
-		}).insert()
-		self.assertEqual(applicant.name, 'job_applicant_naming@example.com-1')
+		applicant = frappe.get_doc(
+			{
+				"doctype": "Job Applicant",
+				"status": "Open",
+				"applicant_name": "_Test Applicant",
+				"email_id": "job_applicant_naming@example.com",
+			}
+		).insert()
+		self.assertEqual(applicant.name, "job_applicant_naming@example.com-1")
 
 	def tearDown(self):
 		frappe.db.rollback()
@@ -41,11 +45,13 @@
 	if frappe.db.exists("Job Applicant", filters):
 		return frappe.get_doc("Job Applicant", filters)
 
-	job_applicant = frappe.get_doc({
-		"doctype": "Job Applicant",
-		"status": args.status or "Open",
-		"designation":  create_designation().name
-	})
+	job_applicant = frappe.get_doc(
+		{
+			"doctype": "Job Applicant",
+			"status": args.status or "Open",
+			"designation": create_designation().name,
+		}
+	)
 
 	job_applicant.update(filters)
 	job_applicant.save()
diff --git a/erpnext/hr/doctype/job_offer/job_offer.py b/erpnext/hr/doctype/job_offer/job_offer.py
index 072fc73..b46930a 100644
--- a/erpnext/hr/doctype/job_offer/job_offer.py
+++ b/erpnext/hr/doctype/job_offer/job_offer.py
@@ -17,9 +17,15 @@
 
 	def validate(self):
 		self.validate_vacancies()
-		job_offer = frappe.db.exists("Job Offer",{"job_applicant": self.job_applicant, "docstatus": ["!=", 2]})
+		job_offer = frappe.db.exists(
+			"Job Offer", {"job_applicant": self.job_applicant, "docstatus": ["!=", 2]}
+		)
 		if job_offer and job_offer != self.name:
-			frappe.throw(_("Job Offer: {0} is already for Job Applicant: {1}").format(frappe.bold(job_offer), frappe.bold(self.job_applicant)))
+			frappe.throw(
+				_("Job Offer: {0} is already for Job Applicant: {1}").format(
+					frappe.bold(job_offer), frappe.bold(self.job_applicant)
+				)
+			)
 
 	def validate_vacancies(self):
 		staffing_plan = get_staffing_plan_detail(self.designation, self.company, self.offer_date)
@@ -27,7 +33,7 @@
 		if staffing_plan and check_vacancies:
 			job_offers = self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date)
 			if not staffing_plan.get("vacancies") or cint(staffing_plan.vacancies) - len(job_offers) <= 0:
-				error_variable = 'for ' + frappe.bold(self.designation)
+				error_variable = "for " + frappe.bold(self.designation)
 				if staffing_plan.get("parent"):
 					error_variable = frappe.bold(get_link_to_form("Staffing Plan", staffing_plan.parent))
 
@@ -37,20 +43,27 @@
 		update_job_applicant(self.status, self.job_applicant)
 
 	def get_job_offer(self, from_date, to_date):
-		''' Returns job offer created during a time period '''
-		return frappe.get_all("Job Offer", filters={
-				"offer_date": ['between', (from_date, to_date)],
+		"""Returns job offer created during a time period"""
+		return frappe.get_all(
+			"Job Offer",
+			filters={
+				"offer_date": ["between", (from_date, to_date)],
 				"designation": self.designation,
 				"company": self.company,
-				"docstatus": 1
-			}, fields=['name'])
+				"docstatus": 1,
+			},
+			fields=["name"],
+		)
+
 
 def update_job_applicant(status, job_applicant):
 	if status in ("Accepted", "Rejected"):
 		frappe.set_value("Job Applicant", job_applicant, "status", status)
 
+
 def get_staffing_plan_detail(designation, company, offer_date):
-	detail = frappe.db.sql("""
+	detail = frappe.db.sql(
+		"""
 		SELECT DISTINCT spd.parent,
 			sp.from_date as from_date,
 			sp.to_date as to_date,
@@ -64,21 +77,31 @@
 			AND sp.company=%s
 			AND spd.parent = sp.name
 			AND %s between sp.from_date and sp.to_date
-	""", (designation, company, offer_date), as_dict=1)
+	""",
+		(designation, company, offer_date),
+		as_dict=1,
+	)
 
 	return frappe._dict(detail[0]) if (detail and detail[0].parent) else None
 
+
 @frappe.whitelist()
 def make_employee(source_name, target_doc=None):
 	def set_missing_values(source, target):
-		target.personal_email, target.first_name = frappe.db.get_value("Job Applicant", \
-			source.job_applicant, ["email_id", "applicant_name"])
-	doc = get_mapped_doc("Job Offer", source_name, {
+		target.personal_email, target.first_name = frappe.db.get_value(
+			"Job Applicant", source.job_applicant, ["email_id", "applicant_name"]
+		)
+
+	doc = get_mapped_doc(
+		"Job Offer",
+		source_name,
+		{
 			"Job Offer": {
 				"doctype": "Employee",
-				"field_map": {
-					"applicant_name": "employee_name",
-					"offer_date": "scheduled_confirmation_date"
-				}}
-		}, target_doc, set_missing_values)
+				"field_map": {"applicant_name": "employee_name", "offer_date": "scheduled_confirmation_date"},
+			}
+		},
+		target_doc,
+		set_missing_values,
+	)
 	return doc
diff --git a/erpnext/hr/doctype/job_offer/test_job_offer.py b/erpnext/hr/doctype/job_offer/test_job_offer.py
index d94e03c..9c4cb36 100644
--- a/erpnext/hr/doctype/job_offer/test_job_offer.py
+++ b/erpnext/hr/doctype/job_offer/test_job_offer.py
@@ -12,17 +12,19 @@
 
 # test_records = frappe.get_test_records('Job Offer')
 
+
 class TestJobOffer(unittest.TestCase):
 	def test_job_offer_creation_against_vacancies(self):
 		frappe.db.set_value("HR Settings", None, "check_vacancies", 1)
 		job_applicant = create_job_applicant(email_id="test_job_offer@example.com")
 		job_offer = create_job_offer(job_applicant=job_applicant.name, designation="UX Designer")
 
-		create_staffing_plan(name='Test No Vacancies', staffing_details=[{
-			"designation": "UX Designer",
-			"vacancies": 0,
-			"estimated_cost_per_position": 5000
-		}])
+		create_staffing_plan(
+			name="Test No Vacancies",
+			staffing_details=[
+				{"designation": "UX Designer", "vacancies": 0, "estimated_cost_per_position": 5000}
+			],
+		)
 		self.assertRaises(frappe.ValidationError, job_offer.submit)
 
 		# test creation of job offer when vacancies are not present
@@ -47,7 +49,8 @@
 		frappe.db.set_value("HR Settings", None, "check_vacancies", 1)
 
 	def tearDown(self):
-		frappe.db.sql("DELETE FROM `tabJob Offer` WHERE 1")
+		frappe.db.sql("DELETE FROM `tabJob Offer`")
+
 
 def create_job_offer(**args):
 	args = frappe._dict(args)
@@ -57,32 +60,34 @@
 	if not frappe.db.exists("Designation", args.designation):
 		designation = create_designation(designation_name=args.designation)
 
-	job_offer = frappe.get_doc({
-		"doctype": "Job Offer",
-		"job_applicant": args.job_applicant or job_applicant.name,
-		"offer_date": args.offer_date or nowdate(),
-		"designation": args.designation or "Researcher",
-		"status": args.status or "Accepted"
-	})
+	job_offer = frappe.get_doc(
+		{
+			"doctype": "Job Offer",
+			"job_applicant": args.job_applicant or job_applicant.name,
+			"offer_date": args.offer_date or nowdate(),
+			"designation": args.designation or "Researcher",
+			"status": args.status or "Accepted",
+		}
+	)
 	return job_offer
 
+
 def create_staffing_plan(**args):
 	args = frappe._dict(args)
 	make_company()
 	frappe.db.set_value("Company", "_Test Company", "is_group", 1)
 	if frappe.db.exists("Staffing Plan", args.name or "Test"):
 		return
-	staffing_plan = frappe.get_doc({
-		"doctype": "Staffing Plan",
-		"name": args.name or "Test",
-		"from_date": args.from_date or nowdate(),
-		"to_date": args.to_date or add_days(nowdate(), 10),
-		"staffing_details": args.staffing_details or [{
-			"designation": "Researcher",
-			"vacancies": 1,
-			"estimated_cost_per_position": 50000
-		}]
-	})
+	staffing_plan = frappe.get_doc(
+		{
+			"doctype": "Staffing Plan",
+			"name": args.name or "Test",
+			"from_date": args.from_date or nowdate(),
+			"to_date": args.to_date or add_days(nowdate(), 10),
+			"staffing_details": args.staffing_details
+			or [{"designation": "Researcher", "vacancies": 1, "estimated_cost_per_position": 50000}],
+		}
+	)
 	staffing_plan.insert()
 	staffing_plan.submit()
 	return staffing_plan
diff --git a/erpnext/hr/doctype/job_opening/job_opening.py b/erpnext/hr/doctype/job_opening/job_opening.py
index d53daf1..ce7caa3 100644
--- a/erpnext/hr/doctype/job_opening/job_opening.py
+++ b/erpnext/hr/doctype/job_opening/job_opening.py
@@ -6,6 +6,7 @@
 
 import frappe
 from frappe import _
+from frappe.utils import get_link_to_form
 from frappe.website.website_generator import WebsiteGenerator
 
 from erpnext.hr.doctype.staffing_plan.staffing_plan import (
@@ -16,67 +17,84 @@
 
 class JobOpening(WebsiteGenerator):
 	website = frappe._dict(
-		template = "templates/generators/job_opening.html",
-		condition_field = "publish",
-		page_title_field = "job_title",
+		template="templates/generators/job_opening.html",
+		condition_field="publish",
+		page_title_field="job_title",
 	)
 
 	def validate(self):
 		if not self.route:
-			self.route = frappe.scrub(self.job_title).replace('_', '-')
+			self.route = frappe.scrub(self.job_title).replace("_", "-")
 		self.validate_current_vacancies()
 
 	def validate_current_vacancies(self):
 		if not self.staffing_plan:
-			staffing_plan = get_active_staffing_plan_details(self.company,
-				self.designation)
+			staffing_plan = get_active_staffing_plan_details(self.company, self.designation)
 			if staffing_plan:
 				self.staffing_plan = staffing_plan[0].name
 				self.planned_vacancies = staffing_plan[0].vacancies
 		elif not self.planned_vacancies:
-			planned_vacancies = frappe.db.sql("""
-				select vacancies from `tabStaffing Plan Detail`
-				where parent=%s and designation=%s""", (self.staffing_plan, self.designation))
-			self.planned_vacancies = planned_vacancies[0][0] if planned_vacancies else None
+			self.planned_vacancies = frappe.db.get_value(
+				"Staffing Plan Detail",
+				{"parent": self.staffing_plan, "designation": self.designation},
+				"vacancies",
+			)
 
 		if self.staffing_plan and self.planned_vacancies:
 			staffing_plan_company = frappe.db.get_value("Staffing Plan", self.staffing_plan, "company")
-			lft, rgt = frappe.get_cached_value('Company',  staffing_plan_company,  ["lft", "rgt"])
 
-			designation_counts = get_designation_counts(self.designation, self.company)
-			current_count = designation_counts['employee_count'] + designation_counts['job_openings']
+			designation_counts = get_designation_counts(self.designation, self.company, self.name)
+			current_count = designation_counts["employee_count"] + designation_counts["job_openings"]
 
-			if self.planned_vacancies <= current_count:
-				frappe.throw(_("Job Openings for designation {0} already open or hiring completed as per Staffing Plan {1}").format(
-					self.designation, self.staffing_plan))
+			number_of_positions = frappe.db.get_value(
+				"Staffing Plan Detail",
+				{"parent": self.staffing_plan, "designation": self.designation},
+				"number_of_positions",
+			)
+
+			if number_of_positions <= current_count:
+				frappe.throw(
+					_(
+						"Job Openings for the designation {0} are already open or the hiring is complete as per the Staffing Plan {1}"
+					).format(
+						frappe.bold(self.designation), get_link_to_form("Staffing Plan", self.staffing_plan)
+					),
+					title=_("Vacancies fulfilled"),
+				)
 
 	def get_context(self, context):
-		context.parents = [{'route': 'jobs', 'title': _('All Jobs') }]
+		context.parents = [{"route": "jobs", "title": _("All Jobs")}]
+
 
 def get_list_context(context):
 	context.title = _("Jobs")
-	context.introduction = _('Current Job Openings')
+	context.introduction = _("Current Job Openings")
 	context.get_list = get_job_openings
 
-def get_job_openings(doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by=None):
-	fields = ['name', 'status', 'job_title', 'description', 'publish_salary_range',
-				'lower_range', 'upper_range', 'currency', 'job_application_route']
+
+def get_job_openings(
+	doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by=None
+):
+	fields = [
+		"name",
+		"status",
+		"job_title",
+		"description",
+		"publish_salary_range",
+		"lower_range",
+		"upper_range",
+		"currency",
+		"job_application_route",
+	]
 
 	filters = filters or {}
-	filters.update({
-		'status': 'Open'
-	})
+	filters.update({"status": "Open"})
 
 	if txt:
-		filters.update({
-			'job_title': ['like', '%{0}%'.format(txt)],
-			'description': ['like', '%{0}%'.format(txt)]
-		})
+		filters.update(
+			{"job_title": ["like", "%{0}%".format(txt)], "description": ["like", "%{0}%".format(txt)]}
+		)
 
-	return frappe.get_all(doctype,
-		filters,
-		fields,
-		start=limit_start,
-		page_length=limit_page_length,
-		order_by=order_by
+	return frappe.get_all(
+		doctype, filters, fields, start=limit_start, page_length=limit_page_length, order_by=order_by
 	)
diff --git a/erpnext/hr/doctype/job_opening/job_opening_dashboard.py b/erpnext/hr/doctype/job_opening/job_opening_dashboard.py
index 67600dc..a309328 100644
--- a/erpnext/hr/doctype/job_opening/job_opening_dashboard.py
+++ b/erpnext/hr/doctype/job_opening/job_opening_dashboard.py
@@ -1,9 +1,5 @@
 def get_data():
-     return {
-        'fieldname': 'job_title',
-        'transactions': [
-            {
-                'items': ['Job Applicant']
-            }
-        ],
-    }
+	return {
+		"fieldname": "job_title",
+		"transactions": [{"items": ["Job Applicant"]}],
+	}
diff --git a/erpnext/hr/doctype/job_opening/test_job_opening.py b/erpnext/hr/doctype/job_opening/test_job_opening.py
index a1c3a1d..e991054 100644
--- a/erpnext/hr/doctype/job_opening/test_job_opening.py
+++ b/erpnext/hr/doctype/job_opening/test_job_opening.py
@@ -3,7 +3,77 @@
 
 import unittest
 
-# test_records = frappe.get_test_records('Job Opening')
+import frappe
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import add_days, getdate
 
-class TestJobOpening(unittest.TestCase):
-	pass
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.staffing_plan.test_staffing_plan import make_company
+
+
+class TestJobOpening(FrappeTestCase):
+	def setUp(self):
+		frappe.db.delete("Staffing Plan")
+		frappe.db.delete("Staffing Plan Detail")
+		frappe.db.delete("Job Opening")
+
+		make_company("_Test Opening Company", "_TOC")
+		frappe.db.delete("Employee", {"company": "_Test Opening Company"})
+
+	def test_vacancies_fulfilled(self):
+		make_employee(
+			"test_job_opening@example.com", company="_Test Opening Company", designation="Designer"
+		)
+
+		staffing_plan = frappe.get_doc(
+			{
+				"doctype": "Staffing Plan",
+				"company": "_Test Opening Company",
+				"name": "Test",
+				"from_date": getdate(),
+				"to_date": add_days(getdate(), 10),
+			}
+		)
+
+		staffing_plan.append(
+			"staffing_details",
+			{"designation": "Designer", "vacancies": 1, "estimated_cost_per_position": 50000},
+		)
+		staffing_plan.insert()
+		staffing_plan.submit()
+
+		self.assertEqual(staffing_plan.staffing_details[0].number_of_positions, 2)
+
+		# allows creating 1 job opening as per vacancy
+		opening_1 = get_job_opening()
+		opening_1.insert()
+
+		# vacancies as per staffing plan already fulfilled via job opening and existing employee count
+		opening_2 = get_job_opening(job_title="Designer New")
+		self.assertRaises(frappe.ValidationError, opening_2.insert)
+
+		# allows updating existing job opening
+		opening_1.status = "Closed"
+		opening_1.save()
+
+
+def get_job_opening(**args):
+	args = frappe._dict(args)
+
+	opening = frappe.db.exists("Job Opening", {"job_title": args.job_title or "Designer"})
+	if opening:
+		return frappe.get_doc("Job Opening", opening)
+
+	opening = frappe.get_doc(
+		{
+			"doctype": "Job Opening",
+			"job_title": "Designer",
+			"designation": "Designer",
+			"company": "_Test Opening Company",
+			"status": "Open",
+		}
+	)
+
+	opening.update(args)
+
+	return opening
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json
index 9ecbe01..9d1db9b 100644
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.json
@@ -237,7 +237,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-01-18 19:15:53.262536",
+ "modified": "2022-04-07 09:50:33.145825",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Leave Allocation",
@@ -281,5 +281,6 @@
  "sort_order": "DESC",
  "states": [],
  "timeline_field": "employee",
- "title_field": "employee_name"
+ "title_field": "employee_name",
+ "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py
index 232118f..8fae2a9 100755
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py
@@ -15,36 +15,65 @@
 from erpnext.hr.utils import get_leave_period, set_employee_name
 
 
-class OverlapError(frappe.ValidationError): pass
-class BackDatedAllocationError(frappe.ValidationError): pass
-class OverAllocationError(frappe.ValidationError): pass
-class LessAllocationError(frappe.ValidationError): pass
-class ValueMultiplierError(frappe.ValidationError): pass
+class OverlapError(frappe.ValidationError):
+	pass
+
+
+class BackDatedAllocationError(frappe.ValidationError):
+	pass
+
+
+class OverAllocationError(frappe.ValidationError):
+	pass
+
+
+class LessAllocationError(frappe.ValidationError):
+	pass
+
+
+class ValueMultiplierError(frappe.ValidationError):
+	pass
+
 
 class LeaveAllocation(Document):
 	def validate(self):
 		self.validate_period()
 		self.validate_allocation_overlap()
-		self.validate_back_dated_allocation()
-		self.set_total_leaves_allocated()
-		self.validate_total_leaves_allocated()
 		self.validate_lwp()
 		set_employee_name(self)
+		self.set_total_leaves_allocated()
+		self.validate_leave_days_and_dates()
+
+	def validate_leave_days_and_dates(self):
+		# all validations that should run on save as well as on update after submit
+		self.validate_back_dated_allocation()
+		self.validate_total_leaves_allocated()
 		self.validate_leave_allocation_days()
 
 	def validate_leave_allocation_days(self):
 		company = frappe.db.get_value("Employee", self.employee, "company")
 		leave_period = get_leave_period(self.from_date, self.to_date, company)
-		max_leaves_allowed = flt(frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed"))
+		max_leaves_allowed = flt(
+			frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed")
+		)
 		if max_leaves_allowed > 0:
 			leave_allocated = 0
 			if leave_period:
-				leave_allocated = get_leave_allocation_for_period(self.employee, self.leave_type,
-					leave_period[0].from_date, leave_period[0].to_date)
+				leave_allocated = get_leave_allocation_for_period(
+					self.employee,
+					self.leave_type,
+					leave_period[0].from_date,
+					leave_period[0].to_date,
+					exclude_allocation=self.name,
+				)
 			leave_allocated += flt(self.new_leaves_allocated)
 			if leave_allocated > max_leaves_allowed:
-				frappe.throw(_("Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period")
-					.format(self.leave_type, self.employee))
+				frappe.throw(
+					_(
+						"Total allocated leaves are more than maximum allocation allowed for {0} leave type for employee {1} in the period"
+					).format(self.leave_type, self.employee),
+					OverAllocationError,
+				)
 
 	def on_submit(self):
 		self.create_leave_ledger_entry()
@@ -64,25 +93,34 @@
 	def on_update_after_submit(self):
 		if self.has_value_changed("new_leaves_allocated"):
 			self.validate_against_leave_applications()
+
+			# recalculate total leaves allocated
+			self.total_leaves_allocated = flt(self.unused_leaves) + flt(self.new_leaves_allocated)
+			# run required validations again since total leaves are being updated
+			self.validate_leave_days_and_dates()
+
 			leaves_to_be_added = self.new_leaves_allocated - self.get_existing_leave_count()
 			args = {
 				"leaves": leaves_to_be_added,
 				"from_date": self.from_date,
 				"to_date": self.to_date,
-				"is_carry_forward": 0
+				"is_carry_forward": 0,
 			}
 			create_leave_ledger_entry(self, args, True)
+			self.db_update()
 
 	def get_existing_leave_count(self):
-		ledger_entries = frappe.get_all("Leave Ledger Entry",
-								filters={
-									"transaction_type": "Leave Allocation",
-									"transaction_name": self.name,
-									"employee": self.employee,
-									"company": self.company,
-									"leave_type": self.leave_type
-								},
-								pluck="leaves")
+		ledger_entries = frappe.get_all(
+			"Leave Ledger Entry",
+			filters={
+				"transaction_type": "Leave Allocation",
+				"transaction_name": self.name,
+				"employee": self.employee,
+				"company": self.company,
+				"leave_type": self.leave_type,
+			},
+			pluck="leaves",
+		)
 		total_existing_leaves = 0
 		for entry in ledger_entries:
 			total_existing_leaves += entry
@@ -90,21 +128,33 @@
 		return total_existing_leaves
 
 	def validate_against_leave_applications(self):
-		leaves_taken = get_approved_leaves_for_period(self.employee, self.leave_type,
-			self.from_date, self.to_date)
+		leaves_taken = get_approved_leaves_for_period(
+			self.employee, self.leave_type, self.from_date, self.to_date
+		)
 		if flt(leaves_taken) > flt(self.total_leaves_allocated):
 			if frappe.db.get_value("Leave Type", self.leave_type, "allow_negative"):
-				frappe.msgprint(_("Note: Total allocated leaves {0} shouldn't be less than already approved leaves {1} for the period").format(self.total_leaves_allocated, leaves_taken))
+				frappe.msgprint(
+					_(
+						"Note: Total allocated leaves {0} shouldn't be less than already approved leaves {1} for the period"
+					).format(self.total_leaves_allocated, leaves_taken)
+				)
 			else:
-				frappe.throw(_("Total allocated leaves {0} cannot be less than already approved leaves {1} for the period").format(self.total_leaves_allocated, leaves_taken), LessAllocationError)
+				frappe.throw(
+					_(
+						"Total allocated leaves {0} cannot be less than already approved leaves {1} for the period"
+					).format(self.total_leaves_allocated, leaves_taken),
+					LessAllocationError,
+				)
 
 	def update_leave_policy_assignments_when_no_allocations_left(self):
-		allocations = frappe.db.get_list("Leave Allocation", filters = {
-			"docstatus": 1,
-			"leave_policy_assignment": self.leave_policy_assignment
-		})
+		allocations = frappe.db.get_list(
+			"Leave Allocation",
+			filters={"docstatus": 1, "leave_policy_assignment": self.leave_policy_assignment},
+		)
 		if len(allocations) == 0:
-			frappe.db.set_value("Leave Policy Assignment", self.leave_policy_assignment ,"leaves_allocated", 0)
+			frappe.db.set_value(
+				"Leave Policy Assignment", self.leave_policy_assignment, "leaves_allocated", 0
+			)
 
 	def validate_period(self):
 		if date_diff(self.to_date, self.from_date) <= 0:
@@ -112,10 +162,13 @@
 
 	def validate_lwp(self):
 		if frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"):
-			frappe.throw(_("Leave Type {0} cannot be allocated since it is leave without pay").format(self.leave_type))
+			frappe.throw(
+				_("Leave Type {0} cannot be allocated since it is leave without pay").format(self.leave_type)
+			)
 
 	def validate_allocation_overlap(self):
-		leave_allocation = frappe.db.sql("""
+		leave_allocation = frappe.db.sql(
+			"""
 			SELECT
 				name
 			FROM `tabLeave Allocation`
@@ -123,29 +176,44 @@
 				employee=%s AND leave_type=%s
 				AND name <> %s AND docstatus=1
 				AND to_date >= %s AND from_date <= %s""",
-			(self.employee, self.leave_type, self.name, self.from_date, self.to_date))
+			(self.employee, self.leave_type, self.name, self.from_date, self.to_date),
+		)
 
 		if leave_allocation:
-			frappe.msgprint(_("{0} already allocated for Employee {1} for period {2} to {3}")
-				.format(self.leave_type, self.employee, formatdate(self.from_date), formatdate(self.to_date)))
+			frappe.msgprint(
+				_("{0} already allocated for Employee {1} for period {2} to {3}").format(
+					self.leave_type, self.employee, formatdate(self.from_date), formatdate(self.to_date)
+				)
+			)
 
-			frappe.throw(_('Reference') + ': <a href="/app/Form/Leave Allocation/{0}">{0}</a>'
-				.format(leave_allocation[0][0]), OverlapError)
+			frappe.throw(
+				_("Reference")
+				+ ': <a href="/app/Form/Leave Allocation/{0}">{0}</a>'.format(leave_allocation[0][0]),
+				OverlapError,
+			)
 
 	def validate_back_dated_allocation(self):
-		future_allocation = frappe.db.sql("""select name, from_date from `tabLeave Allocation`
+		future_allocation = frappe.db.sql(
+			"""select name, from_date from `tabLeave Allocation`
 			where employee=%s and leave_type=%s and docstatus=1 and from_date > %s
-			and carry_forward=1""", (self.employee, self.leave_type, self.to_date), as_dict=1)
+			and carry_forward=1""",
+			(self.employee, self.leave_type, self.to_date),
+			as_dict=1,
+		)
 
 		if future_allocation:
-			frappe.throw(_("Leave cannot be allocated before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}")
-				.format(formatdate(future_allocation[0].from_date), future_allocation[0].name),
-					BackDatedAllocationError)
+			frappe.throw(
+				_(
+					"Leave cannot be allocated before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}"
+				).format(formatdate(future_allocation[0].from_date), future_allocation[0].name),
+				BackDatedAllocationError,
+			)
 
 	@frappe.whitelist()
 	def set_total_leaves_allocated(self):
-		self.unused_leaves = get_carry_forwarded_leaves(self.employee,
-			self.leave_type, self.from_date, self.carry_forward)
+		self.unused_leaves = get_carry_forwarded_leaves(
+			self.employee, self.leave_type, self.from_date, self.carry_forward
+		)
 
 		self.total_leaves_allocated = flt(self.unused_leaves) + flt(self.new_leaves_allocated)
 
@@ -154,11 +222,14 @@
 		if self.carry_forward:
 			self.set_carry_forwarded_leaves_in_previous_allocation()
 
-		if not self.total_leaves_allocated \
-			and not frappe.db.get_value("Leave Type", self.leave_type, "is_earned_leave") \
-			and not frappe.db.get_value("Leave Type", self.leave_type, "is_compensatory"):
-				frappe.throw(_("Total leaves allocated is mandatory for Leave Type {0}")
-					.format(self.leave_type))
+		if (
+			not self.total_leaves_allocated
+			and not frappe.db.get_value("Leave Type", self.leave_type, "is_earned_leave")
+			and not frappe.db.get_value("Leave Type", self.leave_type, "is_compensatory")
+		):
+			frappe.throw(
+				_("Total leaves allocated is mandatory for Leave Type {0}").format(self.leave_type)
+			)
 
 	def limit_carry_forward_based_on_max_allowed_leaves(self):
 		max_leaves_allowed = frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed")
@@ -167,29 +238,46 @@
 			self.unused_leaves = max_leaves_allowed - flt(self.new_leaves_allocated)
 
 	def set_carry_forwarded_leaves_in_previous_allocation(self, on_cancel=False):
-		''' Set carry forwarded leaves in previous allocation '''
+		"""Set carry forwarded leaves in previous allocation"""
 		previous_allocation = get_previous_allocation(self.from_date, self.leave_type, self.employee)
 		if on_cancel:
 			self.unused_leaves = 0.0
 		if previous_allocation:
-			frappe.db.set_value("Leave Allocation", previous_allocation.name,
-				'carry_forwarded_leaves_count', self.unused_leaves)
+			frappe.db.set_value(
+				"Leave Allocation",
+				previous_allocation.name,
+				"carry_forwarded_leaves_count",
+				self.unused_leaves,
+			)
 
 	def validate_total_leaves_allocated(self):
 		# Adding a day to include To Date in the difference
 		date_difference = date_diff(self.to_date, self.from_date) + 1
 		if date_difference < self.total_leaves_allocated:
-			frappe.throw(_("Total allocated leaves are more than days in the period"), OverAllocationError)
+			if frappe.db.get_value("Leave Type", self.leave_type, "allow_over_allocation"):
+				frappe.msgprint(
+					_("<b>Total Leaves Allocated</b> are more than the number of days in the allocation period"),
+					indicator="orange",
+					alert=True,
+				)
+			else:
+				frappe.throw(
+					_("<b>Total Leaves Allocated</b> are more than the number of days in the allocation period"),
+					exc=OverAllocationError,
+					title=_("Over Allocation"),
+				)
 
 	def create_leave_ledger_entry(self, submit=True):
 		if self.unused_leaves:
-			expiry_days = frappe.db.get_value("Leave Type", self.leave_type, "expire_carry_forwarded_leaves_after_days")
+			expiry_days = frappe.db.get_value(
+				"Leave Type", self.leave_type, "expire_carry_forwarded_leaves_after_days"
+			)
 			end_date = add_days(self.from_date, expiry_days - 1) if expiry_days else self.to_date
 			args = dict(
 				leaves=self.unused_leaves,
 				from_date=self.from_date,
-				to_date= min(getdate(end_date), getdate(self.to_date)),
-				is_carry_forward=1
+				to_date=min(getdate(end_date), getdate(self.to_date)),
+				is_carry_forward=1,
 			)
 			create_leave_ledger_entry(self, args, submit)
 
@@ -197,74 +285,85 @@
 			leaves=self.new_leaves_allocated,
 			from_date=self.from_date,
 			to_date=self.to_date,
-			is_carry_forward=0
+			is_carry_forward=0,
 		)
 		create_leave_ledger_entry(self, args, submit)
 
+
 def get_previous_allocation(from_date, leave_type, employee):
-	''' Returns document properties of previous allocation '''
-	return frappe.db.get_value("Leave Allocation",
+	"""Returns document properties of previous allocation"""
+	return frappe.db.get_value(
+		"Leave Allocation",
 		filters={
-			'to_date': ("<", from_date),
-			'leave_type': leave_type,
-			'employee': employee,
-			'docstatus': 1
+			"to_date": ("<", from_date),
+			"leave_type": leave_type,
+			"employee": employee,
+			"docstatus": 1,
 		},
-		order_by='to_date DESC',
-		fieldname=['name', 'from_date', 'to_date', 'employee', 'leave_type'], as_dict=1)
+		order_by="to_date DESC",
+		fieldname=["name", "from_date", "to_date", "employee", "leave_type"],
+		as_dict=1,
+	)
 
-def get_leave_allocation_for_period(employee, leave_type, from_date, to_date):
-	leave_allocated = 0
-	leave_allocations = frappe.db.sql("""
-		select employee, leave_type, from_date, to_date, total_leaves_allocated
-		from `tabLeave Allocation`
-		where employee=%(employee)s and leave_type=%(leave_type)s
-			and docstatus=1
-			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))
-	""", {
-		"from_date": from_date,
-		"to_date": to_date,
-		"employee": employee,
-		"leave_type": leave_type
-	}, as_dict=1)
 
-	if leave_allocations:
-		for leave_alloc in leave_allocations:
-			leave_allocated += leave_alloc.total_leaves_allocated
+def get_leave_allocation_for_period(
+	employee, leave_type, from_date, to_date, exclude_allocation=None
+):
+	from frappe.query_builder.functions import Sum
 
-	return leave_allocated
+	Allocation = frappe.qb.DocType("Leave Allocation")
+	return (
+		frappe.qb.from_(Allocation)
+		.select(Sum(Allocation.total_leaves_allocated).as_("total_allocated_leaves"))
+		.where(
+			(Allocation.employee == employee)
+			& (Allocation.leave_type == leave_type)
+			& (Allocation.docstatus == 1)
+			& (Allocation.name != exclude_allocation)
+			& (
+				(Allocation.from_date.between(from_date, to_date))
+				| (Allocation.to_date.between(from_date, to_date))
+				| ((Allocation.from_date < from_date) & (Allocation.to_date > to_date))
+			)
+		)
+	).run()[0][0] or 0.0
+
 
 @frappe.whitelist()
 def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None):
-	''' Returns carry forwarded leaves for the given employee '''
+	"""Returns carry forwarded leaves for the given employee"""
 	unused_leaves = 0.0
 	previous_allocation = get_previous_allocation(date, leave_type, employee)
 	if carry_forward and previous_allocation:
 		validate_carry_forward(leave_type)
-		unused_leaves = get_unused_leaves(employee, leave_type,
-			previous_allocation.from_date, previous_allocation.to_date)
+		unused_leaves = get_unused_leaves(
+			employee, leave_type, previous_allocation.from_date, previous_allocation.to_date
+		)
 		if unused_leaves:
-			max_carry_forwarded_leaves = frappe.db.get_value("Leave Type",
-				leave_type, "maximum_carry_forwarded_leaves")
+			max_carry_forwarded_leaves = frappe.db.get_value(
+				"Leave Type", leave_type, "maximum_carry_forwarded_leaves"
+			)
 			if max_carry_forwarded_leaves and unused_leaves > flt(max_carry_forwarded_leaves):
 				unused_leaves = flt(max_carry_forwarded_leaves)
 
 	return unused_leaves
 
+
 def get_unused_leaves(employee, leave_type, from_date, to_date):
-	''' Returns unused leaves between the given period while skipping leave allocation expiry '''
-	leaves = frappe.get_all("Leave Ledger Entry", filters={
-		'employee': employee,
-		'leave_type': leave_type,
-		'from_date': ('>=', from_date),
-		'to_date': ('<=', to_date)
-		}, or_filters={
-			'is_expired': 0,
-			'is_carry_forward': 1
-		}, fields=['sum(leaves) as leaves'])
-	return flt(leaves[0]['leaves'])
+	"""Returns unused leaves between the given period while skipping leave allocation expiry"""
+	leaves = frappe.get_all(
+		"Leave Ledger Entry",
+		filters={
+			"employee": employee,
+			"leave_type": leave_type,
+			"from_date": (">=", from_date),
+			"to_date": ("<=", to_date),
+		},
+		or_filters={"is_expired": 0, "is_carry_forward": 1},
+		fields=["sum(leaves) as leaves"],
+	)
+	return flt(leaves[0]["leaves"])
+
 
 def validate_carry_forward(leave_type):
 	if not frappe.db.get_value("Leave Type", leave_type, "is_carry_forward"):
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py b/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py
index 631beef..96e81db 100644
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py
@@ -1,17 +1,6 @@
 def get_data():
-     return {
-        'fieldname': 'leave_allocation',
-        'transactions': [
-            {
-                'items': ['Compensatory Leave Request']
-            },
-            {
-                'items': ['Leave Encashment']
-            }
-        ],
-        'reports': [
-			{
-				'items': ['Employee Leave Balance']
-			}
-		]
-    }
+	return {
+		"fieldname": "leave_allocation",
+		"transactions": [{"items": ["Compensatory Leave Request"]}, {"items": ["Leave Encashment"]}],
+		"reports": [{"items": ["Employee Leave Balance"]}],
+	}
diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
index 1fe9139..b4a42d3 100644
--- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
+++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
@@ -1,24 +1,27 @@
 import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import add_days, add_months, getdate, nowdate
 
 import erpnext
 from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.leave_allocation.leave_allocation import (
+	BackDatedAllocationError,
+	OverAllocationError,
+)
 from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation
 from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
 
 
-class TestLeaveAllocation(unittest.TestCase):
-	@classmethod
-	def setUpClass(cls):
-		frappe.db.sql("delete from `tabLeave Period`")
+class TestLeaveAllocation(FrappeTestCase):
+	def setUp(self):
+		frappe.db.delete("Leave Period")
+		frappe.db.delete("Leave Allocation")
+		frappe.db.delete("Leave Ledger Entry")
 
-		emp_id = make_employee("test_emp_leave_allocation@salary.com")
-		cls.employee = frappe.get_doc("Employee", emp_id)
-
-	def tearDown(self):
-		frappe.db.rollback()
+		emp_id = make_employee("test_emp_leave_allocation@salary.com", company="_Test Company")
+		self.employee = frappe.get_doc("Employee", emp_id)
 
 	def test_overlapping_allocation(self):
 		leaves = [
@@ -31,7 +34,7 @@
 				"from_date": getdate("2015-10-01"),
 				"to_date": getdate("2015-10-31"),
 				"new_leaves_allocated": 5,
-				"docstatus": 1
+				"docstatus": 1,
 			},
 			{
 				"doctype": "Leave Allocation",
@@ -41,42 +44,196 @@
 				"leave_type": "_Test Leave Type",
 				"from_date": getdate("2015-09-01"),
 				"to_date": getdate("2015-11-30"),
-				"new_leaves_allocated": 5
-			}
+				"new_leaves_allocated": 5,
+			},
 		]
 
 		frappe.get_doc(leaves[0]).save()
 		self.assertRaises(frappe.ValidationError, frappe.get_doc(leaves[1]).save)
 
 	def test_invalid_period(self):
-		doc = frappe.get_doc({
-			"doctype": "Leave Allocation",
-			"__islocal": 1,
-			"employee": self.employee.name,
-			"employee_name": self.employee.employee_name,
-			"leave_type": "_Test Leave Type",
-			"from_date": getdate("2015-09-30"),
-			"to_date": getdate("2015-09-1"),
-			"new_leaves_allocated": 5
-		})
+		doc = frappe.get_doc(
+			{
+				"doctype": "Leave Allocation",
+				"__islocal": 1,
+				"employee": self.employee.name,
+				"employee_name": self.employee.employee_name,
+				"leave_type": "_Test Leave Type",
+				"from_date": getdate("2015-09-30"),
+				"to_date": getdate("2015-09-1"),
+				"new_leaves_allocated": 5,
+			}
+		)
 
 		# invalid period
 		self.assertRaises(frappe.ValidationError, doc.save)
 
-	def test_allocated_leave_days_over_period(self):
-		doc = frappe.get_doc({
-			"doctype": "Leave Allocation",
-			"__islocal": 1,
-			"employee": self.employee.name,
-			"employee_name": self.employee.employee_name,
-			"leave_type": "_Test Leave Type",
-			"from_date": getdate("2015-09-1"),
-			"to_date": getdate("2015-09-30"),
-			"new_leaves_allocated": 35
-		})
+	def test_validation_for_over_allocation(self):
+		leave_type = create_leave_type(leave_type_name="Test Over Allocation", is_carry_forward=1)
+		leave_type.save()
+
+		doc = frappe.get_doc(
+			{
+				"doctype": "Leave Allocation",
+				"__islocal": 1,
+				"employee": self.employee.name,
+				"employee_name": self.employee.employee_name,
+				"leave_type": leave_type.name,
+				"from_date": getdate("2015-09-1"),
+				"to_date": getdate("2015-09-30"),
+				"new_leaves_allocated": 35,
+				"carry_forward": 1,
+			}
+		)
 
 		# allocated leave more than period
-		self.assertRaises(frappe.ValidationError, doc.save)
+		self.assertRaises(OverAllocationError, doc.save)
+
+		leave_type.allow_over_allocation = 1
+		leave_type.save()
+
+		# allows creating a leave allocation with more leave days than period days
+		doc = frappe.get_doc(
+			{
+				"doctype": "Leave Allocation",
+				"__islocal": 1,
+				"employee": self.employee.name,
+				"employee_name": self.employee.employee_name,
+				"leave_type": leave_type.name,
+				"from_date": getdate("2015-09-1"),
+				"to_date": getdate("2015-09-30"),
+				"new_leaves_allocated": 35,
+				"carry_forward": 1,
+			}
+		).insert()
+
+	def test_validation_for_over_allocation_post_submission(self):
+		allocation = frappe.get_doc(
+			{
+				"doctype": "Leave Allocation",
+				"__islocal": 1,
+				"employee": self.employee.name,
+				"employee_name": self.employee.employee_name,
+				"leave_type": "_Test Leave Type",
+				"from_date": getdate("2015-09-1"),
+				"to_date": getdate("2015-09-30"),
+				"new_leaves_allocated": 15,
+			}
+		).submit()
+		allocation.reload()
+		# allocated leaves more than period after submission
+		allocation.new_leaves_allocated = 35
+		self.assertRaises(OverAllocationError, allocation.save)
+
+	def test_validation_for_over_allocation_based_on_leave_setup(self):
+		frappe.delete_doc_if_exists("Leave Period", "Test Allocation Period")
+		leave_period = frappe.get_doc(
+			dict(
+				name="Test Allocation Period",
+				doctype="Leave Period",
+				from_date=add_months(nowdate(), -6),
+				to_date=add_months(nowdate(), 6),
+				company="_Test Company",
+				is_active=1,
+			)
+		).insert()
+
+		leave_type = create_leave_type(leave_type_name="_Test Allocation Validation", is_carry_forward=1)
+		leave_type.max_leaves_allowed = 25
+		leave_type.save()
+
+		# 15 leaves allocated in this period
+		allocation = create_leave_allocation(
+			leave_type=leave_type.name,
+			employee=self.employee.name,
+			employee_name=self.employee.employee_name,
+			from_date=leave_period.from_date,
+			to_date=nowdate(),
+		)
+		allocation.submit()
+
+		# trying to allocate additional 15 leaves
+		allocation = create_leave_allocation(
+			leave_type=leave_type.name,
+			employee=self.employee.name,
+			employee_name=self.employee.employee_name,
+			from_date=add_days(nowdate(), 1),
+			to_date=leave_period.to_date,
+		)
+		self.assertRaises(OverAllocationError, allocation.save)
+
+	def test_validation_for_over_allocation_based_on_leave_setup_post_submission(self):
+		frappe.delete_doc_if_exists("Leave Period", "Test Allocation Period")
+		leave_period = frappe.get_doc(
+			dict(
+				name="Test Allocation Period",
+				doctype="Leave Period",
+				from_date=add_months(nowdate(), -6),
+				to_date=add_months(nowdate(), 6),
+				company="_Test Company",
+				is_active=1,
+			)
+		).insert()
+
+		leave_type = create_leave_type(leave_type_name="_Test Allocation Validation", is_carry_forward=1)
+		leave_type.max_leaves_allowed = 30
+		leave_type.save()
+
+		# 15 leaves allocated
+		allocation = create_leave_allocation(
+			leave_type=leave_type.name,
+			employee=self.employee.name,
+			employee_name=self.employee.employee_name,
+			from_date=leave_period.from_date,
+			to_date=nowdate(),
+		)
+		allocation.submit()
+		allocation.reload()
+
+		# allocate additional 15 leaves
+		allocation = create_leave_allocation(
+			leave_type=leave_type.name,
+			employee=self.employee.name,
+			employee_name=self.employee.employee_name,
+			from_date=add_days(nowdate(), 1),
+			to_date=leave_period.to_date,
+		)
+		allocation.submit()
+		allocation.reload()
+
+		# trying to allocate 25 leaves in 2nd alloc within leave period
+		# total leaves = 40 which is more than `max_leaves_allowed` setting i.e. 30
+		allocation.new_leaves_allocated = 25
+		self.assertRaises(OverAllocationError, allocation.save)
+
+	def test_validate_back_dated_allocation_update(self):
+		leave_type = create_leave_type(leave_type_name="_Test_CF_leave", is_carry_forward=1)
+		leave_type.save()
+
+		# initial leave allocation = 15
+		leave_allocation = create_leave_allocation(
+			employee=self.employee.name,
+			employee_name=self.employee.employee_name,
+			leave_type="_Test_CF_leave",
+			from_date=add_months(nowdate(), -12),
+			to_date=add_months(nowdate(), -1),
+			carry_forward=0,
+		)
+		leave_allocation.submit()
+
+		# new_leaves = 15, carry_forwarded = 10
+		leave_allocation_1 = create_leave_allocation(
+			employee=self.employee.name,
+			employee_name=self.employee.employee_name,
+			leave_type="_Test_CF_leave",
+			carry_forward=1,
+		)
+		leave_allocation_1.submit()
+
+		# try updating initial leave allocation
+		leave_allocation.reload()
+		leave_allocation.new_leaves_allocated = 20
+		self.assertRaises(BackDatedAllocationError, leave_allocation.save)
 
 	def test_carry_forward_calculation(self):
 		leave_type = create_leave_type(leave_type_name="_Test_CF_leave", is_carry_forward=1)
@@ -91,7 +248,8 @@
 			leave_type="_Test_CF_leave",
 			from_date=add_months(nowdate(), -12),
 			to_date=add_months(nowdate(), -1),
-			carry_forward=0)
+			carry_forward=0,
+		)
 		leave_allocation.submit()
 
 		# carry forwarded leaves considering maximum_carry_forwarded_leaves
@@ -100,10 +258,13 @@
 			employee=self.employee.name,
 			employee_name=self.employee.employee_name,
 			leave_type="_Test_CF_leave",
-			carry_forward=1)
+			carry_forward=1,
+		)
 		leave_allocation_1.submit()
+		leave_allocation_1.reload()
 
 		self.assertEqual(leave_allocation_1.unused_leaves, 10)
+		self.assertEqual(leave_allocation_1.total_leaves_allocated, 25)
 
 		leave_allocation_1.cancel()
 
@@ -114,7 +275,8 @@
 			employee_name=self.employee.employee_name,
 			leave_type="_Test_CF_leave",
 			carry_forward=1,
-			new_leaves_allocated=25)
+			new_leaves_allocated=25,
+		)
 		leave_allocation_2.submit()
 
 		self.assertEqual(leave_allocation_2.unused_leaves, 5)
@@ -123,7 +285,8 @@
 		leave_type = create_leave_type(
 			leave_type_name="_Test_CF_leave_expiry",
 			is_carry_forward=1,
-			expire_carry_forwarded_leaves_after_days=90)
+			expire_carry_forwarded_leaves_after_days=90,
+		)
 		leave_type.save()
 
 		# initial leave allocation
@@ -133,7 +296,8 @@
 			leave_type="_Test_CF_leave_expiry",
 			from_date=add_months(nowdate(), -24),
 			to_date=add_months(nowdate(), -12),
-			carry_forward=0)
+			carry_forward=0,
+		)
 		leave_allocation.submit()
 
 		leave_allocation = create_leave_allocation(
@@ -142,7 +306,8 @@
 			leave_type="_Test_CF_leave_expiry",
 			from_date=add_days(nowdate(), -90),
 			to_date=add_days(nowdate(), 100),
-			carry_forward=1)
+			carry_forward=1,
+		)
 		leave_allocation.submit()
 
 		# expires all the carry forwarded leaves after 90 days
@@ -155,19 +320,21 @@
 			leave_type="_Test_CF_leave_expiry",
 			carry_forward=1,
 			from_date=add_months(nowdate(), 6),
-			to_date=add_months(nowdate(), 12))
+			to_date=add_months(nowdate(), 12),
+		)
 		leave_allocation_1.submit()
 
 		self.assertEqual(leave_allocation_1.unused_leaves, leave_allocation.new_leaves_allocated)
 
 	def test_creation_of_leave_ledger_entry_on_submit(self):
 		leave_allocation = create_leave_allocation(
-			employee=self.employee.name,
-			employee_name=self.employee.employee_name
+			employee=self.employee.name, employee_name=self.employee.employee_name
 		)
 		leave_allocation.submit()
 
-		leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=dict(transaction_name=leave_allocation.name))
+		leave_ledger_entry = frappe.get_all(
+			"Leave Ledger Entry", fields="*", filters=dict(transaction_name=leave_allocation.name)
+		)
 
 		self.assertEqual(len(leave_ledger_entry), 1)
 		self.assertEqual(leave_ledger_entry[0].employee, leave_allocation.employee)
@@ -176,54 +343,63 @@
 
 		# check if leave ledger entry is deleted on cancellation
 		leave_allocation.cancel()
-		self.assertFalse(frappe.db.exists("Leave Ledger Entry", {'transaction_name':leave_allocation.name}))
+		self.assertFalse(
+			frappe.db.exists("Leave Ledger Entry", {"transaction_name": leave_allocation.name})
+		)
 
 	def test_leave_addition_after_submit(self):
 		leave_allocation = create_leave_allocation(
-			employee=self.employee.name,
-			employee_name=self.employee.employee_name
+			employee=self.employee.name, employee_name=self.employee.employee_name
 		)
 		leave_allocation.submit()
+		leave_allocation.reload()
 		self.assertTrue(leave_allocation.total_leaves_allocated, 15)
+
 		leave_allocation.new_leaves_allocated = 40
 		leave_allocation.submit()
+		leave_allocation.reload()
 		self.assertTrue(leave_allocation.total_leaves_allocated, 40)
 
 	def test_leave_subtraction_after_submit(self):
 		leave_allocation = create_leave_allocation(
-			employee=self.employee.name,
-			employee_name=self.employee.employee_name
+			employee=self.employee.name, employee_name=self.employee.employee_name
 		)
 		leave_allocation.submit()
+		leave_allocation.reload()
 		self.assertTrue(leave_allocation.total_leaves_allocated, 15)
+
 		leave_allocation.new_leaves_allocated = 10
 		leave_allocation.submit()
+		leave_allocation.reload()
 		self.assertTrue(leave_allocation.total_leaves_allocated, 10)
 
 	def test_validation_against_leave_application_after_submit(self):
 		from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list
 
 		make_holiday_list()
-		frappe.db.set_value("Company", self.employee.company, "default_holiday_list", "Salary Slip Test Holiday List")
+		frappe.db.set_value(
+			"Company", self.employee.company, "default_holiday_list", "Salary Slip Test Holiday List"
+		)
 
 		leave_allocation = create_leave_allocation(
-			employee=self.employee.name,
-			employee_name=self.employee.employee_name
+			employee=self.employee.name, employee_name=self.employee.employee_name
 		)
 		leave_allocation.submit()
 		self.assertTrue(leave_allocation.total_leaves_allocated, 15)
 
-		leave_application = frappe.get_doc({
-			"doctype": 'Leave Application',
-			"employee": self.employee.name,
-			"leave_type": "_Test Leave Type",
-			"from_date": add_months(nowdate(), 2),
-			"to_date": add_months(add_days(nowdate(), 10), 2),
-			"company": self.employee.company,
-			"docstatus": 1,
-			"status": "Approved",
-			"leave_approver": 'test@example.com'
-		})
+		leave_application = frappe.get_doc(
+			{
+				"doctype": "Leave Application",
+				"employee": self.employee.name,
+				"leave_type": "_Test Leave Type",
+				"from_date": add_months(nowdate(), 2),
+				"to_date": add_months(add_days(nowdate(), 10), 2),
+				"company": self.employee.company,
+				"docstatus": 1,
+				"status": "Approved",
+				"leave_approver": "test@example.com",
+			}
+		)
 		leave_application.submit()
 		leave_application.reload()
 
@@ -232,22 +408,26 @@
 		leave_allocation.total_leaves_allocated = leave_application.total_leave_days - 1
 		self.assertRaises(frappe.ValidationError, leave_allocation.submit)
 
+
 def create_leave_allocation(**args):
 	args = frappe._dict(args)
 
 	emp_id = make_employee("test_emp_leave_allocation@salary.com")
 	employee = frappe.get_doc("Employee", emp_id)
 
-	return frappe.get_doc({
-		"doctype": "Leave Allocation",
-		"__islocal": 1,
-		"employee": args.employee or employee.name,
-		"employee_name": args.employee_name or employee.employee_name,
-		"leave_type": args.leave_type or "_Test Leave Type",
-		"from_date": args.from_date or nowdate(),
-		"new_leaves_allocated": args.new_leaves_allocated or 15,
-		"carry_forward": args.carry_forward or 0,
-		"to_date": args.to_date or add_months(nowdate(), 12)
-	})
+	return frappe.get_doc(
+		{
+			"doctype": "Leave Allocation",
+			"__islocal": 1,
+			"employee": args.employee or employee.name,
+			"employee_name": args.employee_name or employee.employee_name,
+			"leave_type": args.leave_type or "_Test Leave Type",
+			"from_date": args.from_date or nowdate(),
+			"new_leaves_allocated": args.new_leaves_allocated or 15,
+			"carry_forward": args.carry_forward or 0,
+			"to_date": args.to_date or add_months(nowdate(), 12),
+		}
+	)
+
 
 test_dependencies = ["Employee", "Leave Type"]
diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js
index 9e8cb55..ee00e67 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.js
+++ b/erpnext/hr/doctype/leave_application/leave_application.js
@@ -52,7 +52,7 @@
 	make_dashboard: function(frm) {
 		var leave_details;
 		let lwps;
-		if (frm.doc.employee) {
+		if (frm.doc.employee && frm.doc.from_date) {
 			frappe.call({
 				method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_details",
 				async: false,
@@ -146,6 +146,7 @@
 	},
 
 	to_date: function(frm) {
+		frm.trigger("make_dashboard");
 		frm.trigger("half_day_datepicker");
 		frm.trigger("calculate_total_days");
 	},
@@ -172,7 +173,7 @@
 					date: frm.doc.from_date,
 					to_date: frm.doc.to_date,
 					leave_type: frm.doc.leave_type,
-					consider_all_leaves_in_the_allocation_period: true
+					consider_all_leaves_in_the_allocation_period: 1
 				},
 				callback: function (r) {
 					if (!r.exc && r.message) {
diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py
index 70250f5..d49d1bd 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.py
+++ b/erpnext/hr/doctype/leave_application/leave_application.py
@@ -1,9 +1,11 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 
+from typing import Dict, Optional, Tuple
 
 import frappe
 from frappe import _
+from frappe.query_builder.functions import Max, Min, Sum
 from frappe.utils import (
 	add_days,
 	cint,
@@ -30,10 +32,29 @@
 )
 
 
-class LeaveDayBlockedError(frappe.ValidationError): pass
-class OverlapError(frappe.ValidationError): pass
-class AttendanceAlreadyMarkedError(frappe.ValidationError): pass
-class NotAnOptionalHoliday(frappe.ValidationError): pass
+class LeaveDayBlockedError(frappe.ValidationError):
+	pass
+
+
+class OverlapError(frappe.ValidationError):
+	pass
+
+
+class AttendanceAlreadyMarkedError(frappe.ValidationError):
+	pass
+
+
+class NotAnOptionalHoliday(frappe.ValidationError):
+	pass
+
+
+class InsufficientLeaveBalanceError(frappe.ValidationError):
+	pass
+
+
+class LeaveAcrossAllocationsError(frappe.ValidationError):
+	pass
+
 
 from frappe.model.document import Document
 
@@ -54,7 +75,7 @@
 		self.validate_salary_processed_days()
 		self.validate_attendance()
 		self.set_half_day_date()
-		if frappe.db.get_value("Leave Type", self.leave_type, 'is_optional_leave'):
+		if frappe.db.get_value("Leave Type", self.leave_type, "is_optional_leave"):
 			self.validate_optional_leave()
 		self.validate_applicable_after()
 
@@ -67,8 +88,10 @@
 		share_doc_with_approver(self, self.leave_approver)
 
 	def on_submit(self):
-		if self.status == "Open":
-			frappe.throw(_("Only Leave Applications with status 'Approved' and 'Rejected' can be submitted"))
+		if self.status in ["Open", "Cancelled"]:
+			frappe.throw(
+				_("Only Leave Applications with status 'Approved' and 'Rejected' can be submitted")
+			)
 
 		self.validate_back_dated_application()
 		self.update_attendance()
@@ -95,7 +118,9 @@
 			leave_type = frappe.get_doc("Leave Type", self.leave_type)
 			if leave_type.applicable_after > 0:
 				date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining")
-				leave_days = get_approved_leaves_for_period(self.employee, False, date_of_joining, self.from_date)
+				leave_days = get_approved_leaves_for_period(
+					self.employee, False, date_of_joining, self.from_date
+				)
 				number_of_days = date_diff(getdate(self.from_date), date_of_joining)
 				if number_of_days >= 0:
 					holidays = 0
@@ -103,29 +128,48 @@
 						holidays = get_holidays(self.employee, date_of_joining, self.from_date)
 					number_of_days = number_of_days - leave_days - holidays
 					if number_of_days < leave_type.applicable_after:
-						frappe.throw(_("{0} applicable after {1} working days").format(self.leave_type, leave_type.applicable_after))
+						frappe.throw(
+							_("{0} applicable after {1} working days").format(
+								self.leave_type, leave_type.applicable_after
+							)
+						)
 
 	def validate_dates(self):
 		if frappe.db.get_single_value("HR Settings", "restrict_backdated_leave_application"):
 			if self.from_date and getdate(self.from_date) < getdate():
-				allowed_role = frappe.db.get_single_value("HR Settings", "role_allowed_to_create_backdated_leave_application")
+				allowed_role = frappe.db.get_single_value(
+					"HR Settings", "role_allowed_to_create_backdated_leave_application"
+				)
 				user = frappe.get_doc("User", frappe.session.user)
 				user_roles = [d.role for d in user.roles]
 				if not allowed_role:
-					frappe.throw(_("Backdated Leave Application is restricted. Please set the {} in {}").format(
-						frappe.bold("Role Allowed to Create Backdated Leave Application"), get_link_to_form("HR Settings", "HR Settings")))
+					frappe.throw(
+						_("Backdated Leave Application is restricted. Please set the {} in {}").format(
+							frappe.bold("Role Allowed to Create Backdated Leave Application"),
+							get_link_to_form("HR Settings", "HR Settings"),
+						)
+					)
 
-				if (allowed_role and allowed_role not in user_roles):
-					frappe.throw(_("Only users with the {0} role can create backdated leave applications").format(allowed_role))
+				if allowed_role and allowed_role not in user_roles:
+					frappe.throw(
+						_("Only users with the {0} role can create backdated leave applications").format(
+							allowed_role
+						)
+					)
 
 		if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)):
 			frappe.throw(_("To date cannot be before from date"))
 
-		if self.half_day and self.half_day_date \
-			and (getdate(self.half_day_date) < getdate(self.from_date)
-			or getdate(self.half_day_date) > getdate(self.to_date)):
+		if (
+			self.half_day
+			and self.half_day_date
+			and (
+				getdate(self.half_day_date) < getdate(self.from_date)
+				or getdate(self.half_day_date) > getdate(self.to_date)
+			)
+		):
 
-				frappe.throw(_("Half Day Date should be between From Date and To Date"))
+			frappe.throw(_("Half Day Date should be between From Date and To Date"))
 
 		if not is_lwp(self.leave_type):
 			self.validate_dates_across_allocation()
@@ -134,30 +178,55 @@
 	def validate_dates_across_allocation(self):
 		if frappe.db.get_value("Leave Type", self.leave_type, "allow_negative"):
 			return
-		def _get_leave_allocation_record(date):
-			allocation = frappe.db.sql("""select name from `tabLeave Allocation`
-				where employee=%s and leave_type=%s and docstatus=1
-				and %s between from_date and to_date""", (self.employee, self.leave_type, date))
 
-			return allocation and allocation[0][0]
+		alloc_on_from_date, alloc_on_to_date = self.get_allocation_based_on_application_dates()
+
+		if not (alloc_on_from_date or alloc_on_to_date):
+			frappe.throw(_("Application period cannot be outside leave allocation period"))
+		elif self.is_separate_ledger_entry_required(alloc_on_from_date, alloc_on_to_date):
+			frappe.throw(
+				_("Application period cannot be across two allocation records"),
+				exc=LeaveAcrossAllocationsError,
+			)
+
+	def get_allocation_based_on_application_dates(self) -> Tuple[Dict, Dict]:
+		"""Returns allocation name, from and to dates for application dates"""
+
+		def _get_leave_allocation_record(date):
+			LeaveAllocation = frappe.qb.DocType("Leave Allocation")
+			allocation = (
+				frappe.qb.from_(LeaveAllocation)
+				.select(LeaveAllocation.name, LeaveAllocation.from_date, LeaveAllocation.to_date)
+				.where(
+					(LeaveAllocation.employee == self.employee)
+					& (LeaveAllocation.leave_type == self.leave_type)
+					& (LeaveAllocation.docstatus == 1)
+					& ((date >= LeaveAllocation.from_date) & (date <= LeaveAllocation.to_date))
+				)
+			).run(as_dict=True)
+
+			return allocation and allocation[0]
 
 		allocation_based_on_from_date = _get_leave_allocation_record(self.from_date)
 		allocation_based_on_to_date = _get_leave_allocation_record(self.to_date)
 
-		if not (allocation_based_on_from_date or allocation_based_on_to_date):
-			frappe.throw(_("Application period cannot be outside leave allocation period"))
-
-		elif allocation_based_on_from_date != allocation_based_on_to_date:
-			frappe.throw(_("Application period cannot be across two allocation records"))
+		return allocation_based_on_from_date, allocation_based_on_to_date
 
 	def validate_back_dated_application(self):
-		future_allocation = frappe.db.sql("""select name, from_date from `tabLeave Allocation`
+		future_allocation = frappe.db.sql(
+			"""select name, from_date from `tabLeave Allocation`
 			where employee=%s and leave_type=%s and docstatus=1 and from_date > %s
-			and carry_forward=1""", (self.employee, self.leave_type, self.to_date), as_dict=1)
+			and carry_forward=1""",
+			(self.employee, self.leave_type, self.to_date),
+			as_dict=1,
+		)
 
 		if future_allocation:
-			frappe.throw(_("Leave cannot be applied/cancelled before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}")
-				.format(formatdate(future_allocation[0].from_date), future_allocation[0].name))
+			frappe.throw(
+				_(
+					"Leave cannot be applied/cancelled before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}"
+				).format(formatdate(future_allocation[0].from_date), future_allocation[0].name)
+			)
 
 	def update_attendance(self):
 		if self.status != "Approved":
@@ -169,8 +238,9 @@
 
 		for dt in daterange(getdate(self.from_date), getdate(self.to_date)):
 			date = dt.strftime("%Y-%m-%d")
-			attendance_name = frappe.db.exists("Attendance", dict(employee = self.employee,
-				attendance_date = date, docstatus = ('!=', 2)))
+			attendance_name = frappe.db.exists(
+				"Attendance", dict(employee=self.employee, attendance_date=date, docstatus=("!=", 2))
+			)
 
 			# don't mark attendance for holidays
 			# if leave type does not include holidays within leaves as leaves
@@ -187,17 +257,17 @@
 			self.create_or_update_attendance(attendance_name, date)
 
 	def create_or_update_attendance(self, attendance_name, date):
-		status = "Half Day" if self.half_day_date and getdate(date) == getdate(self.half_day_date) else "On Leave"
+		status = (
+			"Half Day"
+			if self.half_day_date and getdate(date) == getdate(self.half_day_date)
+			else "On Leave"
+		)
 
 		if attendance_name:
 			# update existing attendance, change absent to on leave
-			doc = frappe.get_doc('Attendance', attendance_name)
+			doc = frappe.get_doc("Attendance", attendance_name)
 			if doc.status != status:
-				doc.db_set({
-					'status': status,
-					'leave_type': self.leave_type,
-					'leave_application': self.name
-				})
+				doc.db_set({"status": status, "leave_type": self.leave_type, "leave_application": self.name})
 		else:
 			# make new attendance and submit it
 			doc = frappe.new_doc("Attendance")
@@ -214,8 +284,12 @@
 
 	def cancel_attendance(self):
 		if self.docstatus == 2:
-			attendance = frappe.db.sql("""select name from `tabAttendance` where employee = %s\
-				and (attendance_date between %s and %s) and docstatus < 2 and status in ('On Leave', 'Half Day')""",(self.employee, self.from_date, self.to_date), as_dict=1)
+			attendance = frappe.db.sql(
+				"""select name from `tabAttendance` where employee = %s\
+				and (attendance_date between %s and %s) and docstatus < 2 and status in ('On Leave', 'Half Day')""",
+				(self.employee, self.from_date, self.to_date),
+				as_dict=1,
+			)
 			for name in attendance:
 				frappe.db.set_value("Attendance", name, "docstatus", 2)
 
@@ -223,21 +297,29 @@
 		if not frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"):
 			return
 
-		last_processed_pay_slip = frappe.db.sql("""
+		last_processed_pay_slip = frappe.db.sql(
+			"""
 			select start_date, end_date from `tabSalary Slip`
 			where docstatus = 1 and employee = %s
 			and ((%s between start_date and end_date) or (%s between start_date and end_date))
 			order by modified desc limit 1
-		""",(self.employee, self.to_date, self.from_date))
+		""",
+			(self.employee, self.to_date, self.from_date),
+		)
 
 		if last_processed_pay_slip:
-			frappe.throw(_("Salary already processed for period between {0} and {1}, Leave application period cannot be between this date range.").format(formatdate(last_processed_pay_slip[0][0]),
-				formatdate(last_processed_pay_slip[0][1])))
-
+			frappe.throw(
+				_(
+					"Salary already processed for period between {0} and {1}, Leave application period cannot be between this date range."
+				).format(
+					formatdate(last_processed_pay_slip[0][0]), formatdate(last_processed_pay_slip[0][1])
+				)
+			)
 
 	def show_block_day_warning(self):
-		block_dates = get_applicable_block_dates(self.from_date, self.to_date,
-			self.employee, self.company, all_lists=True)
+		block_dates = get_applicable_block_dates(
+			self.from_date, self.to_date, self.employee, self.company, all_lists=True
+		)
 
 		if block_dates:
 			frappe.msgprint(_("Warning: Leave application contains following block dates") + ":")
@@ -245,53 +327,99 @@
 				frappe.msgprint(formatdate(d.block_date) + ": " + d.reason)
 
 	def validate_block_days(self):
-		block_dates = get_applicable_block_dates(self.from_date, self.to_date,
-			self.employee, self.company)
+		block_dates = get_applicable_block_dates(
+			self.from_date, self.to_date, self.employee, self.company
+		)
 
 		if block_dates and self.status == "Approved":
 			frappe.throw(_("You are not authorized to approve leaves on Block Dates"), LeaveDayBlockedError)
 
 	def validate_balance_leaves(self):
 		if self.from_date and self.to_date:
-			self.total_leave_days = get_number_of_leave_days(self.employee, self.leave_type,
-				self.from_date, self.to_date, self.half_day, self.half_day_date)
+			self.total_leave_days = get_number_of_leave_days(
+				self.employee, self.leave_type, self.from_date, self.to_date, self.half_day, self.half_day_date
+			)
 
 			if self.total_leave_days <= 0:
-				frappe.throw(_("The day(s) on which you are applying for leave are holidays. You need not apply for leave."))
+				frappe.throw(
+					_(
+						"The day(s) on which you are applying for leave are holidays. You need not apply for leave."
+					)
+				)
 
 			if not is_lwp(self.leave_type):
-				self.leave_balance = get_leave_balance_on(self.employee, self.leave_type, self.from_date, self.to_date,
-					consider_all_leaves_in_the_allocation_period=True)
-				if self.status != "Rejected" and (self.leave_balance < self.total_leave_days or not self.leave_balance):
-					if frappe.db.get_value("Leave Type", self.leave_type, "allow_negative"):
-						frappe.msgprint(_("Note: There is not enough leave balance for Leave Type {0}")
-							.format(self.leave_type))
-					else:
-						frappe.throw(_("There is not enough leave balance for Leave Type {0}")
-							.format(self.leave_type))
+				leave_balance = get_leave_balance_on(
+					self.employee,
+					self.leave_type,
+					self.from_date,
+					self.to_date,
+					consider_all_leaves_in_the_allocation_period=True,
+					for_consumption=True,
+				)
+				self.leave_balance = leave_balance.get("leave_balance")
+				leave_balance_for_consumption = leave_balance.get("leave_balance_for_consumption")
+
+				if self.status != "Rejected" and (
+					leave_balance_for_consumption < self.total_leave_days or not leave_balance_for_consumption
+				):
+					self.show_insufficient_balance_message(leave_balance_for_consumption)
+
+	def show_insufficient_balance_message(self, leave_balance_for_consumption: float) -> None:
+		alloc_on_from_date, alloc_on_to_date = self.get_allocation_based_on_application_dates()
+
+		if frappe.db.get_value("Leave Type", self.leave_type, "allow_negative"):
+			if leave_balance_for_consumption != self.leave_balance:
+				msg = _("Warning: Insufficient leave balance for Leave Type {0} in this allocation.").format(
+					frappe.bold(self.leave_type)
+				)
+				msg += "<br><br>"
+				msg += _(
+					"Actual balances aren't available because the leave application spans over different leave allocations. You can still apply for leaves which would be compensated during the next allocation."
+				)
+			else:
+				msg = _("Warning: Insufficient leave balance for Leave Type {0}.").format(
+					frappe.bold(self.leave_type)
+				)
+
+			frappe.msgprint(msg, title=_("Warning"), indicator="orange")
+		else:
+			frappe.throw(
+				_("Insufficient leave balance for Leave Type {0}").format(frappe.bold(self.leave_type)),
+				exc=InsufficientLeaveBalanceError,
+				title=_("Insufficient Balance"),
+			)
 
 	def validate_leave_overlap(self):
 		if not self.name:
 			# hack! if name is null, it could cause problems with !=
 			self.name = "New Leave Application"
 
-		for d in frappe.db.sql("""
+		for d in frappe.db.sql(
+			"""
 			select
 				name, leave_type, posting_date, from_date, to_date, total_leave_days, half_day_date
 			from `tabLeave Application`
-			where employee = %(employee)s and docstatus < 2 and status in ("Open", "Approved")
+			where employee = %(employee)s and docstatus < 2 and status in ('Open', 'Approved')
 			and to_date >= %(from_date)s and from_date <= %(to_date)s
-			and name != %(name)s""", {
+			and name != %(name)s""",
+			{
 				"employee": self.employee,
 				"from_date": self.from_date,
 				"to_date": self.to_date,
-				"name": self.name
-			}, as_dict = 1):
+				"name": self.name,
+			},
+			as_dict=1,
+		):
 
-			if cint(self.half_day)==1 and getdate(self.half_day_date) == getdate(d.half_day_date) and (
-				flt(self.total_leave_days)==0.5
-				or getdate(self.from_date) == getdate(d.to_date)
-				or getdate(self.to_date) == getdate(d.from_date)):
+			if (
+				cint(self.half_day) == 1
+				and getdate(self.half_day_date) == getdate(d.half_day_date)
+				and (
+					flt(self.total_leave_days) == 0.5
+					or getdate(self.from_date) == getdate(d.to_date)
+					or getdate(self.to_date) == getdate(d.from_date)
+				)
+			):
 
 				total_leaves_on_half_day = self.get_total_leaves_on_half_day()
 				if total_leaves_on_half_day >= 1:
@@ -301,22 +429,22 @@
 
 	def throw_overlap_error(self, d):
 		form_link = get_link_to_form("Leave Application", d.name)
-		msg = _("Employee {0} has already applied for {1} between {2} and {3} : {4}").format(self.employee,
-			d['leave_type'], formatdate(d['from_date']), formatdate(d['to_date']), form_link)
+		msg = _("Employee {0} has already applied for {1} between {2} and {3} : {4}").format(
+			self.employee, d["leave_type"], formatdate(d["from_date"]), formatdate(d["to_date"]), form_link
+		)
 		frappe.throw(msg, OverlapError)
 
 	def get_total_leaves_on_half_day(self):
-		leave_count_on_half_day_date = frappe.db.sql("""select count(name) from `tabLeave Application`
+		leave_count_on_half_day_date = frappe.db.sql(
+			"""select count(name) from `tabLeave Application`
 			where employee = %(employee)s
 			and docstatus < 2
-			and status in ("Open", "Approved")
+			and status in ('Open', 'Approved')
 			and half_day = 1
 			and half_day_date = %(half_day_date)s
-			and name != %(name)s""", {
-				"employee": self.employee,
-				"half_day_date": self.half_day_date,
-				"name": self.name
-			})[0][0]
+			and name != %(name)s""",
+			{"employee": self.employee, "half_day_date": self.half_day_date, "name": self.name},
+		)[0][0]
 
 		return leave_count_on_half_day_date * 0.5
 
@@ -326,24 +454,36 @@
 			frappe.throw(_("Leave of type {0} cannot be longer than {1}").format(self.leave_type, max_days))
 
 	def validate_attendance(self):
-		attendance = frappe.db.sql("""select name from `tabAttendance` where employee = %s and (attendance_date between %s and %s)
-					and status = "Present" and docstatus = 1""",
-			(self.employee, self.from_date, self.to_date))
+		attendance = frappe.db.sql(
+			"""select name from `tabAttendance` where employee = %s and (attendance_date between %s and %s)
+					and status = 'Present' and docstatus = 1""",
+			(self.employee, self.from_date, self.to_date),
+		)
 		if attendance:
-			frappe.throw(_("Attendance for employee {0} is already marked for this day").format(self.employee),
-				AttendanceAlreadyMarkedError)
+			frappe.throw(
+				_("Attendance for employee {0} is already marked for this day").format(self.employee),
+				AttendanceAlreadyMarkedError,
+			)
 
 	def validate_optional_leave(self):
 		leave_period = get_leave_period(self.from_date, self.to_date, self.company)
 		if not leave_period:
 			frappe.throw(_("Cannot find active Leave Period"))
-		optional_holiday_list = frappe.db.get_value("Leave Period", leave_period[0]["name"], "optional_holiday_list")
+		optional_holiday_list = frappe.db.get_value(
+			"Leave Period", leave_period[0]["name"], "optional_holiday_list"
+		)
 		if not optional_holiday_list:
-			frappe.throw(_("Optional Holiday List not set for leave period {0}").format(leave_period[0]["name"]))
+			frappe.throw(
+				_("Optional Holiday List not set for leave period {0}").format(leave_period[0]["name"])
+			)
 		day = getdate(self.from_date)
 		while day <= getdate(self.to_date):
-			if not frappe.db.exists({"doctype": "Holiday", "parent": optional_holiday_list, "holiday_date": day}):
-				frappe.throw(_("{0} is not in Optional Holiday List").format(formatdate(day)), NotAnOptionalHoliday)
+			if not frappe.db.exists(
+				{"doctype": "Holiday", "parent": optional_holiday_list, "holiday_date": day}
+			):
+				frappe.throw(
+					_("{0} is not in Optional Holiday List").format(formatdate(day)), NotAnOptionalHoliday
+				)
 			day = add_days(day, 1)
 
 	def set_half_day_date(self):
@@ -358,44 +498,50 @@
 		if not employee.user_id:
 			return
 
-		parent_doc = frappe.get_doc('Leave Application', self.name)
+		parent_doc = frappe.get_doc("Leave Application", self.name)
 		args = parent_doc.as_dict()
 
-		template = frappe.db.get_single_value('HR Settings', 'leave_status_notification_template')
+		template = frappe.db.get_single_value("HR Settings", "leave_status_notification_template")
 		if not template:
 			frappe.msgprint(_("Please set default template for Leave Status Notification in HR Settings."))
 			return
 		email_template = frappe.get_doc("Email Template", template)
 		message = frappe.render_template(email_template.response, args)
 
-		self.notify({
-			# for post in messages
-			"message": message,
-			"message_to": employee.user_id,
-			# for email
-			"subject": email_template.subject,
-			"notify": "employee"
-		})
+		self.notify(
+			{
+				# for post in messages
+				"message": message,
+				"message_to": employee.user_id,
+				# for email
+				"subject": email_template.subject,
+				"notify": "employee",
+			}
+		)
 
 	def notify_leave_approver(self):
 		if self.leave_approver:
-			parent_doc = frappe.get_doc('Leave Application', self.name)
+			parent_doc = frappe.get_doc("Leave Application", self.name)
 			args = parent_doc.as_dict()
 
-			template = frappe.db.get_single_value('HR Settings', 'leave_approval_notification_template')
+			template = frappe.db.get_single_value("HR Settings", "leave_approval_notification_template")
 			if not template:
-				frappe.msgprint(_("Please set default template for Leave Approval Notification in HR Settings."))
+				frappe.msgprint(
+					_("Please set default template for Leave Approval Notification in HR Settings.")
+				)
 				return
 			email_template = frappe.get_doc("Email Template", template)
 			message = frappe.render_template(email_template.response, args)
 
-			self.notify({
-				# for post in messages
-				"message": message,
-				"message_to": self.leave_approver,
-				# for email
-				"subject": email_template.subject
-			})
+			self.notify(
+				{
+					# for post in messages
+					"message": message,
+					"message_to": self.leave_approver,
+					# for email
+					"subject": email_template.subject,
+				}
+			)
 
 	def notify(self, args):
 		args = frappe._dict(args)
@@ -404,154 +550,264 @@
 			contact = args.message_to
 			if not isinstance(contact, list):
 				if not args.notify == "employee":
-					contact = frappe.get_doc('User', contact).email or contact
+					contact = frappe.get_doc("User", contact).email or contact
 
-			sender      	    = dict()
-			sender['email']     = frappe.get_doc('User', frappe.session.user).email
-			sender['full_name'] = get_fullname(sender['email'])
+			sender = dict()
+			sender["email"] = frappe.get_doc("User", frappe.session.user).email
+			sender["full_name"] = get_fullname(sender["email"])
 
 			try:
 				frappe.sendmail(
-					recipients = contact,
-					sender = sender['email'],
-					subject = args.subject,
-					message = args.message,
+					recipients=contact,
+					sender=sender["email"],
+					subject=args.subject,
+					message=args.message,
 				)
 				frappe.msgprint(_("Email sent to {0}").format(contact))
 			except frappe.OutgoingEmailError:
 				pass
 
 	def create_leave_ledger_entry(self, submit=True):
-		if self.status != 'Approved' and submit:
+		if self.status != "Approved" and submit:
 			return
 
-		expiry_date = get_allocation_expiry(self.employee, self.leave_type,
-			self.to_date, self.from_date)
-
+		expiry_date = get_allocation_expiry_for_cf_leaves(
+			self.employee, self.leave_type, self.to_date, self.from_date
+		)
 		lwp = frappe.db.get_value("Leave Type", self.leave_type, "is_lwp")
 
 		if expiry_date:
 			self.create_ledger_entry_for_intermediate_allocation_expiry(expiry_date, submit, lwp)
 		else:
-			raise_exception = True
-			if frappe.flags.in_patch:
-				raise_exception=False
+			alloc_on_from_date, alloc_on_to_date = self.get_allocation_based_on_application_dates()
+			if self.is_separate_ledger_entry_required(alloc_on_from_date, alloc_on_to_date):
+				# required only if negative balance is allowed for leave type
+				# else will be stopped in validation itself
+				self.create_separate_ledger_entries(alloc_on_from_date, alloc_on_to_date, submit, lwp)
+			else:
+				raise_exception = False if frappe.flags.in_patch else True
+				args = dict(
+					leaves=self.total_leave_days * -1,
+					from_date=self.from_date,
+					to_date=self.to_date,
+					is_lwp=lwp,
+					holiday_list=get_holiday_list_for_employee(self.employee, raise_exception=raise_exception)
+					or "",
+				)
+				create_leave_ledger_entry(self, args, submit)
 
-			args = dict(
-				leaves=self.total_leave_days * -1,
-				from_date=self.from_date,
-				to_date=self.to_date,
-				is_lwp=lwp,
-				holiday_list=get_holiday_list_for_employee(self.employee, raise_exception=raise_exception) or ''
+	def is_separate_ledger_entry_required(
+		self, alloc_on_from_date: Optional[Dict] = None, alloc_on_to_date: Optional[Dict] = None
+	) -> bool:
+		"""Checks if application dates fall in separate allocations"""
+		if (
+			(alloc_on_from_date and not alloc_on_to_date)
+			or (not alloc_on_from_date and alloc_on_to_date)
+			or (
+				alloc_on_from_date and alloc_on_to_date and alloc_on_from_date.name != alloc_on_to_date.name
+			)
+		):
+			return True
+		return False
+
+	def create_separate_ledger_entries(self, alloc_on_from_date, alloc_on_to_date, submit, lwp):
+		"""Creates separate ledger entries for application period falling into separate allocations"""
+		# for creating separate ledger entries existing allocation periods should be consecutive
+		if (
+			submit
+			and alloc_on_from_date
+			and alloc_on_to_date
+			and add_days(alloc_on_from_date.to_date, 1) != alloc_on_to_date.from_date
+		):
+			frappe.throw(
+				_(
+					"Leave Application period cannot be across two non-consecutive leave allocations {0} and {1}."
+				).format(
+					get_link_to_form("Leave Allocation", alloc_on_from_date.name),
+					get_link_to_form("Leave Allocation", alloc_on_to_date),
+				)
+			)
+
+		raise_exception = False if frappe.flags.in_patch else True
+
+		if alloc_on_from_date:
+			first_alloc_end = alloc_on_from_date.to_date
+			second_alloc_start = add_days(alloc_on_from_date.to_date, 1)
+		else:
+			first_alloc_end = add_days(alloc_on_to_date.from_date, -1)
+			second_alloc_start = alloc_on_to_date.from_date
+
+		leaves_in_first_alloc = get_number_of_leave_days(
+			self.employee,
+			self.leave_type,
+			self.from_date,
+			first_alloc_end,
+			self.half_day,
+			self.half_day_date,
+		)
+		leaves_in_second_alloc = get_number_of_leave_days(
+			self.employee,
+			self.leave_type,
+			second_alloc_start,
+			self.to_date,
+			self.half_day,
+			self.half_day_date,
+		)
+
+		args = dict(
+			is_lwp=lwp,
+			holiday_list=get_holiday_list_for_employee(self.employee, raise_exception=raise_exception)
+			or "",
+		)
+
+		if leaves_in_first_alloc:
+			args.update(
+				dict(from_date=self.from_date, to_date=first_alloc_end, leaves=leaves_in_first_alloc * -1)
+			)
+			create_leave_ledger_entry(self, args, submit)
+
+		if leaves_in_second_alloc:
+			args.update(
+				dict(from_date=second_alloc_start, to_date=self.to_date, leaves=leaves_in_second_alloc * -1)
 			)
 			create_leave_ledger_entry(self, args, submit)
 
 	def create_ledger_entry_for_intermediate_allocation_expiry(self, expiry_date, submit, lwp):
-		''' splits leave application into two ledger entries to consider expiry of allocation '''
+		"""Splits leave application into two ledger entries to consider expiry of allocation"""
+		raise_exception = False if frappe.flags.in_patch else True
 
-		raise_exception = True
-		if frappe.flags.in_patch:
-			raise_exception=False
-
-		args = dict(
-			from_date=self.from_date,
-			to_date=expiry_date,
-			leaves=(date_diff(expiry_date, self.from_date) + 1) * -1,
-			is_lwp=lwp,
-			holiday_list=get_holiday_list_for_employee(self.employee, raise_exception=raise_exception) or ''
+		leaves = get_number_of_leave_days(
+			self.employee, self.leave_type, self.from_date, expiry_date, self.half_day, self.half_day_date
 		)
-		create_leave_ledger_entry(self, args, submit)
+
+		if leaves:
+			args = dict(
+				from_date=self.from_date,
+				to_date=expiry_date,
+				leaves=leaves * -1,
+				is_lwp=lwp,
+				holiday_list=get_holiday_list_for_employee(self.employee, raise_exception=raise_exception)
+				or "",
+			)
+			create_leave_ledger_entry(self, args, submit)
 
 		if getdate(expiry_date) != getdate(self.to_date):
 			start_date = add_days(expiry_date, 1)
-			args.update(dict(
-				from_date=start_date,
-				to_date=self.to_date,
-				leaves=date_diff(self.to_date, expiry_date) * -1
-			))
-			create_leave_ledger_entry(self, args, submit)
+			leaves = get_number_of_leave_days(
+				self.employee, self.leave_type, start_date, self.to_date, self.half_day, self.half_day_date
+			)
+
+			if leaves:
+				args.update(dict(from_date=start_date, to_date=self.to_date, leaves=leaves * -1))
+				create_leave_ledger_entry(self, args, submit)
 
 
-def get_allocation_expiry(employee, leave_type, to_date, from_date):
-	''' Returns expiry of carry forward allocation in leave ledger entry '''
-	expiry =  frappe.get_all("Leave Ledger Entry",
+def get_allocation_expiry_for_cf_leaves(
+	employee: str, leave_type: str, to_date: str, from_date: str
+) -> str:
+	"""Returns expiry of carry forward allocation in leave ledger entry"""
+	expiry = frappe.get_all(
+		"Leave Ledger Entry",
 		filters={
-			'employee': employee,
-			'leave_type': leave_type,
-			'is_carry_forward': 1,
-			'transaction_type': 'Leave Allocation',
-			'to_date': ['between', (from_date, to_date)]
-		},fields=['to_date'])
-	return expiry[0]['to_date'] if expiry else None
+			"employee": employee,
+			"leave_type": leave_type,
+			"is_carry_forward": 1,
+			"transaction_type": "Leave Allocation",
+			"to_date": ["between", (from_date, to_date)],
+			"docstatus": 1,
+		},
+		fields=["to_date"],
+	)
+	return expiry[0]["to_date"] if expiry else ""
+
 
 @frappe.whitelist()
-def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day = None, half_day_date = None, holiday_list = None):
+def get_number_of_leave_days(
+	employee: str,
+	leave_type: str,
+	from_date: str,
+	to_date: str,
+	half_day: Optional[int] = None,
+	half_day_date: Optional[str] = None,
+	holiday_list: Optional[str] = None,
+) -> float:
+	"""Returns number of leave days between 2 dates after considering half day and holidays
+	(Based on the include_holiday setting in Leave Type)"""
 	number_of_days = 0
 	if cint(half_day) == 1:
-		if from_date == to_date:
+		if getdate(from_date) == getdate(to_date):
 			number_of_days = 0.5
-		elif half_day_date and half_day_date <= to_date:
-			number_of_days = date_diff(to_date, from_date) + .5
+		elif half_day_date and getdate(from_date) <= getdate(half_day_date) <= getdate(to_date):
+			number_of_days = date_diff(to_date, from_date) + 0.5
 		else:
 			number_of_days = date_diff(to_date, from_date) + 1
-
 	else:
 		number_of_days = date_diff(to_date, from_date) + 1
 
 	if not frappe.db.get_value("Leave Type", leave_type, "include_holiday"):
-		number_of_days = flt(number_of_days) - flt(get_holidays(employee, from_date, to_date, holiday_list=holiday_list))
+		number_of_days = flt(number_of_days) - flt(
+			get_holidays(employee, from_date, to_date, holiday_list=holiday_list)
+		)
 	return number_of_days
 
+
 @frappe.whitelist()
 def get_leave_details(employee, date):
 	allocation_records = get_leave_allocation_records(employee, date)
 	leave_allocation = {}
 	for d in allocation_records:
 		allocation = allocation_records.get(d, frappe._dict())
-
-		total_allocated_leaves = frappe.db.get_value('Leave Allocation', {
-			'from_date': ('<=', date),
-			'to_date': ('>=', date),
-			'employee': employee,
-			'leave_type': allocation.leave_type,
-		}, 'SUM(total_leaves_allocated)') or 0
-
-		remaining_leaves = get_leave_balance_on(employee, d, date, to_date = allocation.to_date,
-			consider_all_leaves_in_the_allocation_period=True)
+		remaining_leaves = get_leave_balance_on(
+			employee, d, date, to_date=allocation.to_date, consider_all_leaves_in_the_allocation_period=True
+		)
 
 		end_date = allocation.to_date
 		leaves_taken = get_leaves_for_period(employee, d, allocation.from_date, end_date) * -1
-		leaves_pending = get_pending_leaves_for_period(employee, d, allocation.from_date, end_date)
+		leaves_pending = get_leaves_pending_approval_for_period(
+			employee, d, allocation.from_date, end_date
+		)
+		expired_leaves = allocation.total_leaves_allocated - (remaining_leaves + leaves_taken)
 
 		leave_allocation[d] = {
-			"total_leaves": total_allocated_leaves,
-			"expired_leaves": total_allocated_leaves - (remaining_leaves + leaves_taken),
+			"total_leaves": allocation.total_leaves_allocated,
+			"expired_leaves": expired_leaves if expired_leaves > 0 else 0,
 			"leaves_taken": leaves_taken,
-			"pending_leaves": leaves_pending,
-			"remaining_leaves": remaining_leaves}
+			"leaves_pending_approval": leaves_pending,
+			"remaining_leaves": remaining_leaves,
+		}
 
-	#is used in set query
-	lwps = frappe.get_list("Leave Type", filters = {"is_lwp": 1})
-	lwps = [lwp.name for lwp in lwps]
+	# is used in set query
+	lwp = frappe.get_list("Leave Type", filters={"is_lwp": 1}, pluck="name")
 
-	ret = {
-		'leave_allocation': leave_allocation,
-		'leave_approver': get_leave_approver(employee),
-		'lwps': lwps
+	return {
+		"leave_allocation": leave_allocation,
+		"leave_approver": get_leave_approver(employee),
+		"lwps": lwp,
 	}
 
-	return ret
 
 @frappe.whitelist()
-def get_leave_balance_on(employee, leave_type, date, to_date=None, consider_all_leaves_in_the_allocation_period=False):
-	'''
-		Returns leave balance till date
-		:param employee: employee name
-		:param leave_type: leave type
-		:param date: date to check balance on
-		:param to_date: future date to check for allocation expiry
-		:param consider_all_leaves_in_the_allocation_period: consider all leaves taken till the allocation end date
-	'''
+def get_leave_balance_on(
+	employee: str,
+	leave_type: str,
+	date: str,
+	to_date: str = None,
+	consider_all_leaves_in_the_allocation_period: bool = False,
+	for_consumption: bool = False,
+):
+	"""
+	Returns leave balance till date
+	:param employee: employee name
+	:param leave_type: leave type
+	:param date: date to check balance on
+	:param to_date: future date to check for allocation expiry
+	:param consider_all_leaves_in_the_allocation_period: consider all leaves taken till the allocation end date
+	:param for_consumption: flag to check if leave balance is required for consumption or display
+	        eg: employee has leave balance = 10 but allocation is expiring in 1 day so employee can only consume 1 leave
+	        in this case leave_balance = 10 but leave_balance_for_consumption = 1
+	        if True, returns a dict eg: {'leave_balance': 10, 'leave_balance_for_consumption': 1}
+	        else, returns leave_balance (in this case 10)
+	"""
 
 	if not to_date:
 		to_date = nowdate()
@@ -559,98 +815,148 @@
 	allocation_records = get_leave_allocation_records(employee, date, leave_type)
 	allocation = allocation_records.get(leave_type, frappe._dict())
 
-	end_date = allocation.to_date if consider_all_leaves_in_the_allocation_period else date
-	expiry = get_allocation_expiry(employee, leave_type, to_date, date)
+	end_date = allocation.to_date if cint(consider_all_leaves_in_the_allocation_period) else date
+	cf_expiry = get_allocation_expiry_for_cf_leaves(employee, leave_type, to_date, date)
 
 	leaves_taken = get_leaves_for_period(employee, leave_type, allocation.from_date, end_date)
 
-	return get_remaining_leaves(allocation, leaves_taken, date, expiry)
+	remaining_leaves = get_remaining_leaves(allocation, leaves_taken, date, cf_expiry)
+
+	if for_consumption:
+		return remaining_leaves
+	else:
+		return remaining_leaves.get("leave_balance")
+
 
 def get_leave_allocation_records(employee, date, leave_type=None):
-	''' returns the total allocated leaves and carry forwarded leaves based on ledger entries '''
+	"""Returns the total allocated leaves and carry forwarded leaves based on ledger entries"""
+	Ledger = frappe.qb.DocType("Leave Ledger Entry")
 
-	conditions = ("and leave_type='%s'" % leave_type) if leave_type else ""
-	allocation_details = frappe.db.sql("""
-		SELECT
-			SUM(CASE WHEN is_carry_forward = 1 THEN leaves ELSE 0 END) as cf_leaves,
-			SUM(CASE WHEN is_carry_forward = 0 THEN leaves ELSE 0 END) as new_leaves,
-			MIN(from_date) as from_date,
-			MAX(to_date) as to_date,
-			leave_type
-		FROM `tabLeave Ledger Entry`
-		WHERE
-			from_date <= %(date)s
-			AND to_date >= %(date)s
-			AND docstatus=1
-			AND transaction_type="Leave Allocation"
-			AND employee=%(employee)s
-			AND is_expired=0
-			AND is_lwp=0
-			{0}
-		GROUP BY employee, leave_type
-	""".format(conditions), dict(date=date, employee=employee), as_dict=1) #nosec
+	cf_leave_case = (
+		frappe.qb.terms.Case().when(Ledger.is_carry_forward == "1", Ledger.leaves).else_(0)
+	)
+	sum_cf_leaves = Sum(cf_leave_case).as_("cf_leaves")
+
+	new_leaves_case = (
+		frappe.qb.terms.Case().when(Ledger.is_carry_forward == "0", Ledger.leaves).else_(0)
+	)
+	sum_new_leaves = Sum(new_leaves_case).as_("new_leaves")
+
+	query = (
+		frappe.qb.from_(Ledger)
+		.select(
+			sum_cf_leaves,
+			sum_new_leaves,
+			Min(Ledger.from_date).as_("from_date"),
+			Max(Ledger.to_date).as_("to_date"),
+			Ledger.leave_type,
+		)
+		.where(
+			(Ledger.from_date <= date)
+			& (Ledger.to_date >= date)
+			& (Ledger.docstatus == 1)
+			& (Ledger.transaction_type == "Leave Allocation")
+			& (Ledger.employee == employee)
+			& (Ledger.is_expired == 0)
+			& (Ledger.is_lwp == 0)
+		)
+	)
+
+	if leave_type:
+		query = query.where((Ledger.leave_type == leave_type))
+	query = query.groupby(Ledger.employee, Ledger.leave_type)
+
+	allocation_details = query.run(as_dict=True)
 
 	allocated_leaves = frappe._dict()
 	for d in allocation_details:
-		allocated_leaves.setdefault(d.leave_type, frappe._dict({
-			"from_date": d.from_date,
-			"to_date": d.to_date,
-			"total_leaves_allocated": flt(d.cf_leaves) + flt(d.new_leaves),
-			"unused_leaves": d.cf_leaves,
-			"new_leaves_allocated": d.new_leaves,
-			"leave_type": d.leave_type
-		}))
+		allocated_leaves.setdefault(
+			d.leave_type,
+			frappe._dict(
+				{
+					"from_date": d.from_date,
+					"to_date": d.to_date,
+					"total_leaves_allocated": flt(d.cf_leaves) + flt(d.new_leaves),
+					"unused_leaves": d.cf_leaves,
+					"new_leaves_allocated": d.new_leaves,
+					"leave_type": d.leave_type,
+				}
+			),
+		)
 	return allocated_leaves
 
-def get_pending_leaves_for_period(employee, leave_type, from_date, to_date):
-	''' Returns leaves that are pending approval '''
-	leaves = frappe.get_all("Leave Application",
-		filters={
-			"employee": employee,
-			"leave_type": leave_type,
-			"status": "Open"
-		},
+
+def get_leaves_pending_approval_for_period(
+	employee: str, leave_type: str, from_date: str, to_date: str
+) -> float:
+	"""Returns leaves that are pending for approval"""
+	leaves = frappe.get_all(
+		"Leave Application",
+		filters={"employee": employee, "leave_type": leave_type, "status": "Open"},
 		or_filters={
 			"from_date": ["between", (from_date, to_date)],
-			"to_date": ["between", (from_date, to_date)]
-		}, fields=['SUM(total_leave_days) as leaves'])[0]
-	return leaves['leaves'] if leaves['leaves'] else 0.0
+			"to_date": ["between", (from_date, to_date)],
+		},
+		fields=["SUM(total_leave_days) as leaves"],
+	)[0]
+	return leaves["leaves"] if leaves["leaves"] else 0.0
 
-def get_remaining_leaves(allocation, leaves_taken, date, expiry):
-	''' Returns minimum leaves remaining after comparing with remaining days for allocation expiry '''
+
+def get_remaining_leaves(
+	allocation: Dict, leaves_taken: float, date: str, cf_expiry: str
+) -> Dict[str, float]:
+	"""Returns a dict of leave_balance and leave_balance_for_consumption
+	leave_balance returns the available leave balance
+	leave_balance_for_consumption returns the minimum leaves remaining after comparing with remaining days for allocation expiry
+	"""
+
 	def _get_remaining_leaves(remaining_leaves, end_date):
-
+		"""Returns minimum leaves remaining after comparing with remaining days for allocation expiry"""
 		if remaining_leaves > 0:
 			remaining_days = date_diff(end_date, date) + 1
 			remaining_leaves = min(remaining_days, remaining_leaves)
 
 		return remaining_leaves
 
-	total_leaves = flt(allocation.total_leaves_allocated) + flt(leaves_taken)
+	leave_balance = leave_balance_for_consumption = flt(allocation.total_leaves_allocated) + flt(
+		leaves_taken
+	)
 
-	if expiry and allocation.unused_leaves:
-		remaining_leaves = flt(allocation.unused_leaves) + flt(leaves_taken)
-		remaining_leaves = _get_remaining_leaves(remaining_leaves, expiry)
+	# balance for carry forwarded leaves
+	if cf_expiry and allocation.unused_leaves:
+		cf_leaves = flt(allocation.unused_leaves) + flt(leaves_taken)
+		remaining_cf_leaves = _get_remaining_leaves(cf_leaves, cf_expiry)
 
-		total_leaves = flt(allocation.new_leaves_allocated) + flt(remaining_leaves)
+		leave_balance = flt(allocation.new_leaves_allocated) + flt(cf_leaves)
+		leave_balance_for_consumption = flt(allocation.new_leaves_allocated) + flt(remaining_cf_leaves)
 
-	return _get_remaining_leaves(total_leaves, allocation.to_date)
+	remaining_leaves = _get_remaining_leaves(leave_balance_for_consumption, allocation.to_date)
+	return frappe._dict(leave_balance=leave_balance, leave_balance_for_consumption=remaining_leaves)
 
-def get_leaves_for_period(employee, leave_type, from_date, to_date, do_not_skip_expired_leaves=False):
+
+def get_leaves_for_period(
+	employee: str, leave_type: str, from_date: str, to_date: str, skip_expired_leaves: bool = True
+) -> float:
 	leave_entries = get_leave_entries(employee, leave_type, from_date, to_date)
 	leave_days = 0
 
 	for leave_entry in leave_entries:
-		inclusive_period = leave_entry.from_date >= getdate(from_date) and leave_entry.to_date <= getdate(to_date)
+		inclusive_period = leave_entry.from_date >= getdate(
+			from_date
+		) and leave_entry.to_date <= getdate(to_date)
 
-		if  inclusive_period and leave_entry.transaction_type == 'Leave Encashment':
+		if inclusive_period and leave_entry.transaction_type == "Leave Encashment":
 			leave_days += leave_entry.leaves
 
-		elif inclusive_period and leave_entry.transaction_type == 'Leave Allocation' and leave_entry.is_expired \
-			and (do_not_skip_expired_leaves or not skip_expiry_leaves(leave_entry, to_date)):
+		elif (
+			inclusive_period
+			and leave_entry.transaction_type == "Leave Allocation"
+			and leave_entry.is_expired
+			and not skip_expired_leaves
+		):
 			leave_days += leave_entry.leaves
 
-		elif leave_entry.transaction_type == 'Leave Application':
+		elif leave_entry.transaction_type == "Leave Application":
 			if leave_entry.from_date < getdate(from_date):
 				leave_entry.from_date = from_date
 			if leave_entry.to_date > getdate(to_date):
@@ -661,23 +967,30 @@
 			# fetch half day date for leaves with half days
 			if leave_entry.leaves % 1:
 				half_day = 1
-				half_day_date = frappe.db.get_value('Leave Application',
-					{'name': leave_entry.transaction_name}, ['half_day_date'])
+				half_day_date = frappe.db.get_value(
+					"Leave Application", {"name": leave_entry.transaction_name}, ["half_day_date"]
+				)
 
-			leave_days += get_number_of_leave_days(employee, leave_type,
-				leave_entry.from_date, leave_entry.to_date, half_day, half_day_date, holiday_list=leave_entry.holiday_list) * -1
+			leave_days += (
+				get_number_of_leave_days(
+					employee,
+					leave_type,
+					leave_entry.from_date,
+					leave_entry.to_date,
+					half_day,
+					half_day_date,
+					holiday_list=leave_entry.holiday_list,
+				)
+				* -1
+			)
 
 	return leave_days
 
-def skip_expiry_leaves(leave_entry, date):
-	''' Checks whether the expired leaves coincide with the to_date of leave balance check.
-		This allows backdated leave entry creation for non carry forwarded allocation '''
-	end_date = frappe.db.get_value("Leave Allocation", {'name': leave_entry.transaction_name}, ['to_date'])
-	return True if end_date == date and not leave_entry.is_carry_forward else False
 
 def get_leave_entries(employee, leave_type, from_date, to_date):
-	''' Returns leave entries between from_date and to_date. '''
-	return frappe.db.sql("""
+	"""Returns leave entries between from_date and to_date."""
+	return frappe.db.sql(
+		"""
 		SELECT
 			employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type, holiday_list,
 			is_carry_forward, is_expired
@@ -689,44 +1002,47 @@
 			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))
-	""", {
-		"from_date": from_date,
-		"to_date": to_date,
-		"employee": employee,
-		"leave_type": leave_type
-	}, as_dict=1)
+	""",
+		{"from_date": from_date, "to_date": to_date, "employee": employee, "leave_type": leave_type},
+		as_dict=1,
+	)
+
 
 @frappe.whitelist()
-def get_holidays(employee, from_date, to_date, holiday_list = None):
-	'''get holidays between two dates for the given employee'''
+def get_holidays(employee, from_date, to_date, holiday_list=None):
+	"""get holidays between two dates for the given employee"""
 	if not holiday_list:
 		holiday_list = get_holiday_list_for_employee(employee)
 
-	holidays = frappe.db.sql("""select count(distinct holiday_date) from `tabHoliday` h1, `tabHoliday List` h2
+	holidays = frappe.db.sql(
+		"""select count(distinct holiday_date) from `tabHoliday` h1, `tabHoliday List` h2
 		where h1.parent = h2.name and h1.holiday_date between %s and %s
-		and h2.name = %s""", (from_date, to_date, holiday_list))[0][0]
+		and h2.name = %s""",
+		(from_date, to_date, holiday_list),
+	)[0][0]
 
 	return holidays
 
+
 def is_lwp(leave_type):
 	lwp = frappe.db.sql("select is_lwp from `tabLeave Type` where name = %s", leave_type)
 	return lwp and cint(lwp[0][0]) or 0
 
+
 @frappe.whitelist()
 def get_events(start, end, filters=None):
 	from frappe.desk.reportview import get_filters_cond
+
 	events = []
 
-	employee = frappe.db.get_value("Employee",
-		filters={"user_id": frappe.session.user},
-		fieldname=["name", "company"],
-		as_dict=True
+	employee = frappe.db.get_value(
+		"Employee", filters={"user_id": frappe.session.user}, fieldname=["name", "company"], as_dict=True
 	)
 
 	if employee:
 		employee, company = employee.name, employee.company
 	else:
-		employee = ''
+		employee = ""
 		company = frappe.db.get_value("Global Defaults", None, "default_company")
 
 	conditions = get_filters_cond("Leave Application", filters, [])
@@ -740,6 +1056,7 @@
 
 	return events
 
+
 def add_department_leaves(events, start, end, employee, company):
 	department = frappe.db.get_value("Employee", employee, "department")
 
@@ -747,18 +1064,24 @@
 		return
 
 	# department leaves
-	department_employees = frappe.db.sql_list("""select name from tabEmployee where department=%s
-		and company=%s""", (department, company))
+	department_employees = frappe.db.sql_list(
+		"""select name from tabEmployee where department=%s
+		and company=%s""",
+		(department, company),
+	)
 
-	filter_conditions = " and employee in (\"%s\")" % '", "'.join(department_employees)
+	filter_conditions = ' and employee in ("%s")' % '", "'.join(department_employees)
 	add_leaves(events, start, end, filter_conditions=filter_conditions)
 
 
 def add_leaves(events, start, end, filter_conditions=None):
 	from frappe.desk.reportview import build_match_conditions
+
 	conditions = []
 
-	if not cint(frappe.db.get_value("HR Settings", None, "show_leaves_of_all_department_members_in_calendar")):
+	if not cint(
+		frappe.db.get_value("HR Settings", None, "show_leaves_of_all_department_members_in_calendar")
+	):
 		match_conditions = build_match_conditions("Leave Application")
 
 		if match_conditions:
@@ -779,16 +1102,16 @@
 	WHERE
 		from_date <= %(end)s AND to_date >= %(start)s <= to_date
 		AND docstatus < 2
-		AND status != 'Rejected'
+		AND status in ('Approved', 'Open')
 	"""
 
 	if conditions:
-		query += ' AND ' + ' AND '.join(conditions)
+		query += " AND " + " AND ".join(conditions)
 
 	if filter_conditions:
 		query += filter_conditions
 
-	for d in frappe.db.sql(query, {"start":start, "end": end}, as_dict=True):
+	for d in frappe.db.sql(query, {"start": start, "end": end}, as_dict=True):
 		e = {
 			"name": d.name,
 			"doctype": "Leave Application",
@@ -797,7 +1120,9 @@
 			"docstatus": d.docstatus,
 			"color": d.color,
 			"all_day": int(not d.half_day),
-			"title": cstr(d.employee_name) + f' ({cstr(d.leave_type)})' + (' ' + _('(Half Day)') if d.half_day else ''),
+			"title": cstr(d.employee_name)
+			+ f" ({cstr(d.leave_type)})"
+			+ (" " + _("(Half Day)") if d.half_day else ""),
 		}
 		if e not in events:
 			events.append(e)
@@ -811,62 +1136,82 @@
 	block_dates = get_applicable_block_dates(start, end, employee, company, all_lists=True)
 
 	for block_date in block_dates:
-		events.append({
-			"doctype": "Leave Block List Date",
-			"from_date": block_date.block_date,
-			"to_date": block_date.block_date,
-			"title": _("Leave Blocked") + ": " + block_date.reason,
-			"name": "_" + str(cnt),
-		})
-		cnt+=1
+		events.append(
+			{
+				"doctype": "Leave Block List Date",
+				"from_date": block_date.block_date,
+				"to_date": block_date.block_date,
+				"title": _("Leave Blocked") + ": " + block_date.reason,
+				"name": "_" + str(cnt),
+			}
+		)
+		cnt += 1
+
 
 def add_holidays(events, start, end, employee, company):
 	applicable_holiday_list = get_holiday_list_for_employee(employee, company)
 	if not applicable_holiday_list:
 		return
 
-	for holiday in frappe.db.sql("""select name, holiday_date, description
+	for holiday in frappe.db.sql(
+		"""select name, holiday_date, description
 		from `tabHoliday` where parent=%s and holiday_date between %s and %s""",
-		(applicable_holiday_list, start, end), as_dict=True):
-			events.append({
+		(applicable_holiday_list, start, end),
+		as_dict=True,
+	):
+		events.append(
+			{
 				"doctype": "Holiday",
 				"from_date": holiday.holiday_date,
-				"to_date":  holiday.holiday_date,
+				"to_date": holiday.holiday_date,
 				"title": _("Holiday") + ": " + cstr(holiday.description),
-				"name": holiday.name
-			})
+				"name": holiday.name,
+			}
+		)
+
 
 @frappe.whitelist()
 def get_mandatory_approval(doctype):
 	mandatory = ""
 	if doctype == "Leave Application":
-		mandatory = frappe.db.get_single_value('HR Settings',
-				'leave_approver_mandatory_in_leave_application')
+		mandatory = frappe.db.get_single_value(
+			"HR Settings", "leave_approver_mandatory_in_leave_application"
+		)
 	else:
-		mandatory = frappe.db.get_single_value('HR Settings',
-				'expense_approver_mandatory_in_expense_claim')
+		mandatory = frappe.db.get_single_value(
+			"HR Settings", "expense_approver_mandatory_in_expense_claim"
+		)
 
 	return mandatory
 
-def get_approved_leaves_for_period(employee, leave_type, from_date, to_date):
-	query = """
-		select employee, leave_type, from_date, to_date, total_leave_days
-		from `tabLeave Application`
-		where employee=%(employee)s
-			and docstatus=1
-			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))
-	"""
-	if leave_type:
-		query += "and leave_type=%(leave_type)s"
 
-	leave_applications = frappe.db.sql(query,{
-		"from_date": from_date,
-		"to_date": to_date,
-		"employee": employee,
-		"leave_type": leave_type
-	}, as_dict=1)
+def get_approved_leaves_for_period(employee, leave_type, from_date, to_date):
+	LeaveApplication = frappe.qb.DocType("Leave Application")
+	query = (
+		frappe.qb.from_(LeaveApplication)
+		.select(
+			LeaveApplication.employee,
+			LeaveApplication.leave_type,
+			LeaveApplication.from_date,
+			LeaveApplication.to_date,
+			LeaveApplication.total_leave_days,
+		)
+		.where(
+			(LeaveApplication.employee == employee)
+			& (LeaveApplication.docstatus == 1)
+			& (LeaveApplication.status == "Approved")
+			& (
+				(LeaveApplication.from_date.between(from_date, to_date))
+				| (LeaveApplication.to_date.between(from_date, to_date))
+				| ((LeaveApplication.from_date < from_date) & (LeaveApplication.to_date > to_date))
+			)
+		)
+	)
+
+	if leave_type:
+		query = query.where(LeaveApplication.leave_type == leave_type)
+
+	leave_applications = query.run(as_dict=True)
 
 	leave_days = 0
 	for leave_app in leave_applications:
@@ -878,18 +1223,24 @@
 			if leave_app.to_date > getdate(to_date):
 				leave_app.to_date = to_date
 
-			leave_days += get_number_of_leave_days(employee, leave_type,
-				leave_app.from_date, leave_app.to_date)
+			leave_days += get_number_of_leave_days(
+				employee, leave_type, leave_app.from_date, leave_app.to_date
+			)
 
 	return leave_days
 
+
 @frappe.whitelist()
 def get_leave_approver(employee):
-	leave_approver, department = frappe.db.get_value("Employee",
-		employee, ["leave_approver", "department"])
+	leave_approver, department = frappe.db.get_value(
+		"Employee", employee, ["leave_approver", "department"]
+	)
 
 	if not leave_approver and department:
-		leave_approver = frappe.db.get_value('Department Approver', {'parent': department,
-			'parentfield': 'leave_approvers', 'idx': 1}, 'approver')
+		leave_approver = frappe.db.get_value(
+			"Department Approver",
+			{"parent": department, "parentfield": "leave_approvers", "idx": 1},
+			"approver",
+		)
 
 	return leave_approver
diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html
index 9f667a6..e755322 100644
--- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html
+++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html
@@ -4,11 +4,11 @@
 	<thead>
 		<tr>
 			<th style="width: 16%">{{ __("Leave Type") }}</th>
-			<th style="width: 16%" class="text-right">{{ __("Total Allocated Leave") }}</th>
-			<th style="width: 16%" class="text-right">{{ __("Expired Leave") }}</th>
-			<th style="width: 16%" class="text-right">{{ __("Used Leave") }}</th>
-			<th style="width: 16%" class="text-right">{{ __("Pending Leave") }}</th>
-			<th style="width: 16%" class="text-right">{{ __("Available Leave") }}</th>
+			<th style="width: 16%" class="text-right">{{ __("Total Allocated Leave(s)") }}</th>
+			<th style="width: 16%" class="text-right">{{ __("Expired Leave(s)") }}</th>
+			<th style="width: 16%" class="text-right">{{ __("Used Leave(s)") }}</th>
+			<th style="width: 16%" class="text-right">{{ __("Leave(s) Pending Approval") }}</th>
+			<th style="width: 16%" class="text-right">{{ __("Available Leave(s)") }}</th>
 		</tr>
 	</thead>
 	<tbody>
@@ -18,7 +18,7 @@
 				<td class="text-right"> {%= value["total_leaves"] %} </td>
 				<td class="text-right"> {%= value["expired_leaves"] %} </td>
 				<td class="text-right"> {%= value["leaves_taken"] %} </td>
-				<td class="text-right"> {%= value["pending_leaves"] %} </td>
+				<td class="text-right"> {%= value["leaves_pending_approval"] %} </td>
 				<td class="text-right"> {%= value["remaining_leaves"] %} </td>
 			</tr>
 		{% } %}
diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.py b/erpnext/hr/doctype/leave_application/leave_application_dashboard.py
index 8b0b98d..ee5cbe9 100644
--- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.py
+++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.py
@@ -3,16 +3,7 @@
 
 def get_data():
 	return {
-		'fieldname': 'leave_application',
-		'transactions': [
-			{
-				'items': ['Attendance']
-			}
-		],
-        'reports': [
-			{
-                'label': _('Reports'),
-				'items': ['Employee Leave Balance']
-			}
-		]
-    }
+		"fieldname": "leave_application",
+		"transactions": [{"items": ["Attendance"]}],
+		"reports": [{"label": _("Reports"), "items": ["Employee Leave Balance"]}],
+	}
diff --git a/erpnext/hr/doctype/leave_application/leave_application_list.js b/erpnext/hr/doctype/leave_application/leave_application_list.js
index a3c03b1..157271a 100644
--- a/erpnext/hr/doctype/leave_application/leave_application_list.js
+++ b/erpnext/hr/doctype/leave_application/leave_application_list.js
@@ -1,13 +1,14 @@
-frappe.listview_settings['Leave Application'] = {
+frappe.listview_settings["Leave Application"] = {
 	add_fields: ["leave_type", "employee", "employee_name", "total_leave_days", "from_date", "to_date"],
 	has_indicator_for_draft: 1,
 	get_indicator: function (doc) {
-		if (doc.status === "Approved") {
-			return [__("Approved"), "green", "status,=,Approved"];
-		} else if (doc.status === "Rejected") {
-			return [__("Rejected"), "red", "status,=,Rejected"];
-		} else {
-			return [__("Open"), "red", "status,=,Open"];
-		}
+		let status_color = {
+			"Approved": "green",
+			"Rejected": "red",
+			"Open": "orange",
+			"Cancelled": "red",
+			"Submitted": "blue"
+		};
+		return [__(doc.status), status_color[doc.status], "status,=," + doc.status];
 	}
 };
diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py
index 6d27f4a..1b9505e 100644
--- a/erpnext/hr/doctype/leave_application/test_leave_application.py
+++ b/erpnext/hr/doctype/leave_application/test_leave_application.py
@@ -17,12 +17,17 @@
 )
 
 from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list
 from erpnext.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation
 from erpnext.hr.doctype.leave_application.leave_application import (
+	InsufficientLeaveBalanceError,
+	LeaveAcrossAllocationsError,
 	LeaveDayBlockedError,
 	NotAnOptionalHoliday,
 	OverlapError,
+	get_leave_allocation_records,
 	get_leave_balance_on,
+	get_leave_details,
 )
 from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import (
 	create_assignment_for_multiple_employees,
@@ -33,7 +38,7 @@
 	make_leave_application,
 )
 
-test_dependencies = ["Leave Allocation", "Leave Block List", "Employee"]
+test_dependencies = ["Leave Type", "Leave Allocation", "Leave Block List", "Employee"]
 
 _test_records = [
 	{
@@ -44,7 +49,7 @@
 		"description": "_Test Reason",
 		"leave_type": "_Test Leave Type",
 		"posting_date": "2013-01-02",
-		"to_date": "2013-05-05"
+		"to_date": "2013-05-05",
 	},
 	{
 		"company": "_Test Company",
@@ -54,7 +59,7 @@
 		"description": "_Test Reason",
 		"leave_type": "_Test Leave Type",
 		"posting_date": "2013-01-02",
-		"to_date": "2013-05-05"
+		"to_date": "2013-05-05",
 	},
 	{
 		"company": "_Test Company",
@@ -64,27 +69,47 @@
 		"description": "_Test Reason",
 		"leave_type": "_Test Leave Type LWP",
 		"posting_date": "2013-01-02",
-		"to_date": "2013-01-15"
-	}
+		"to_date": "2013-01-15",
+	},
 ]
 
 
 class TestLeaveApplication(unittest.TestCase):
 	def setUp(self):
-		for dt in ["Leave Application", "Leave Allocation", "Salary Slip", "Leave Ledger Entry"]:
-			frappe.db.sql("DELETE FROM `tab%s`" % dt) #nosec
+		for dt in [
+			"Leave Application",
+			"Leave Allocation",
+			"Salary Slip",
+			"Leave Ledger Entry",
+			"Leave Period",
+			"Leave Policy Assignment",
+		]:
+			frappe.db.delete(dt)
 
 		frappe.set_user("Administrator")
 		set_leave_approver()
 
-		frappe.db.sql("delete from tabAttendance where employee='_T-Employee-00001'")
+		frappe.db.delete("Attendance", {"employee": "_T-Employee-00001"})
+		frappe.db.set_value("Employee", "_T-Employee-00001", "holiday_list", "")
+
+		from_date = get_year_start(getdate())
+		to_date = get_year_ending(getdate())
+		self.holiday_list = make_holiday_list(from_date=from_date, to_date=to_date)
+
+		if not frappe.db.exists("Leave Type", "_Test Leave Type"):
+			frappe.get_doc(
+				dict(leave_type_name="_Test Leave Type", doctype="Leave Type", include_holiday=True)
+			).insert()
 
 	def tearDown(self):
 		frappe.db.rollback()
+		frappe.set_user("Administrator")
 
 	def _clear_roles(self):
-		frappe.db.sql("""delete from `tabHas Role` where parent in
-			("test@example.com", "test1@example.com", "test2@example.com")""")
+		frappe.db.sql(
+			"""delete from `tabHas Role` where parent in
+			('test@example.com', 'test1@example.com', 'test2@example.com')"""
+		)
 
 	def _clear_applications(self):
 		frappe.db.sql("""delete from `tabLeave Application`""")
@@ -95,77 +120,232 @@
 		application.to_date = "2013-01-05"
 		return application
 
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
+	def test_validate_application_across_allocations(self):
+		# Test validation for application dates when negative balance is disabled
+		frappe.delete_doc_if_exists("Leave Type", "Test Leave Validation", force=1)
+		leave_type = frappe.get_doc(
+			dict(leave_type_name="Test Leave Validation", doctype="Leave Type", allow_negative=False)
+		).insert()
+
+		employee = get_employee()
+		date = getdate()
+		first_sunday = get_first_sunday(self.holiday_list, for_date=get_year_start(date))
+
+		leave_application = frappe.get_doc(
+			dict(
+				doctype="Leave Application",
+				employee=employee.name,
+				leave_type=leave_type.name,
+				from_date=add_days(first_sunday, 1),
+				to_date=add_days(first_sunday, 4),
+				company="_Test Company",
+				status="Approved",
+				leave_approver="test@example.com",
+			)
+		)
+		# Application period cannot be outside leave allocation period
+		self.assertRaises(frappe.ValidationError, leave_application.insert)
+
+		make_allocation_record(
+			leave_type=leave_type.name, from_date=get_year_start(date), to_date=get_year_ending(date)
+		)
+
+		leave_application = frappe.get_doc(
+			dict(
+				doctype="Leave Application",
+				employee=employee.name,
+				leave_type=leave_type.name,
+				from_date=add_days(first_sunday, -10),
+				to_date=add_days(first_sunday, 1),
+				company="_Test Company",
+				status="Approved",
+				leave_approver="test@example.com",
+			)
+		)
+
+		# Application period cannot be across two allocation records
+		self.assertRaises(LeaveAcrossAllocationsError, leave_application.insert)
+
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
+	def test_insufficient_leave_balance_validation(self):
+		# CASE 1: Validation when allow negative is disabled
+		frappe.delete_doc_if_exists("Leave Type", "Test Leave Validation", force=1)
+		leave_type = frappe.get_doc(
+			dict(leave_type_name="Test Leave Validation", doctype="Leave Type", allow_negative=False)
+		).insert()
+
+		employee = get_employee()
+		date = getdate()
+		first_sunday = get_first_sunday(self.holiday_list, for_date=get_year_start(date))
+
+		# allocate 2 leaves, apply for more
+		make_allocation_record(
+			leave_type=leave_type.name,
+			from_date=get_year_start(date),
+			to_date=get_year_ending(date),
+			leaves=2,
+		)
+		leave_application = frappe.get_doc(
+			dict(
+				doctype="Leave Application",
+				employee=employee.name,
+				leave_type=leave_type.name,
+				from_date=add_days(first_sunday, 1),
+				to_date=add_days(first_sunday, 3),
+				company="_Test Company",
+				status="Approved",
+				leave_approver="test@example.com",
+			)
+		)
+		self.assertRaises(InsufficientLeaveBalanceError, leave_application.insert)
+
+		# CASE 2: Allows creating application with a warning message when allow negative is enabled
+		frappe.db.set_value("Leave Type", "Test Leave Validation", "allow_negative", True)
+		make_leave_application(
+			employee.name, add_days(first_sunday, 1), add_days(first_sunday, 3), leave_type.name
+		)
+
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
+	def test_separate_leave_ledger_entry_for_boundary_applications(self):
+		# When application falls in 2 different allocations and Allow Negative is enabled
+		# creates separate leave ledger entries
+		frappe.delete_doc_if_exists("Leave Type", "Test Leave Validation", force=1)
+		leave_type = frappe.get_doc(
+			dict(
+				leave_type_name="Test Leave Validation",
+				doctype="Leave Type",
+				allow_negative=True,
+				include_holiday=True,
+			)
+		).insert()
+
+		employee = get_employee()
+		date = getdate()
+		year_start = getdate(get_year_start(date))
+		year_end = getdate(get_year_ending(date))
+
+		make_allocation_record(leave_type=leave_type.name, from_date=year_start, to_date=year_end)
+		# application across allocations
+
+		# CASE 1: from date has no allocation, to date has an allocation / both dates have allocation
+		start_date = add_days(year_start, -10)
+		application = make_leave_application(
+			employee.name,
+			start_date,
+			add_days(year_start, 3),
+			leave_type.name,
+			half_day=1,
+			half_day_date=start_date,
+		)
+
+		# 2 separate leave ledger entries
+		ledgers = frappe.db.get_all(
+			"Leave Ledger Entry",
+			{"transaction_type": "Leave Application", "transaction_name": application.name},
+			["leaves", "from_date", "to_date"],
+			order_by="from_date",
+		)
+		self.assertEqual(len(ledgers), 2)
+
+		self.assertEqual(ledgers[0].from_date, application.from_date)
+		self.assertEqual(ledgers[0].to_date, add_days(year_start, -1))
+
+		self.assertEqual(ledgers[1].from_date, year_start)
+		self.assertEqual(ledgers[1].to_date, application.to_date)
+
+		# CASE 2: from date has an allocation, to date has no allocation
+		application = make_leave_application(
+			employee.name, add_days(year_end, -3), add_days(year_end, 5), leave_type.name
+		)
+
+		# 2 separate leave ledger entries
+		ledgers = frappe.db.get_all(
+			"Leave Ledger Entry",
+			{"transaction_type": "Leave Application", "transaction_name": application.name},
+			["leaves", "from_date", "to_date"],
+			order_by="from_date",
+		)
+		self.assertEqual(len(ledgers), 2)
+
+		self.assertEqual(ledgers[0].from_date, application.from_date)
+		self.assertEqual(ledgers[0].to_date, year_end)
+
+		self.assertEqual(ledgers[1].from_date, add_days(year_end, 1))
+		self.assertEqual(ledgers[1].to_date, application.to_date)
+
 	def test_overwrite_attendance(self):
-		'''check attendance is automatically created on leave approval'''
+		"""check attendance is automatically created on leave approval"""
 		make_allocation_record()
 		application = self.get_application(_test_records[0])
-		application.status = 'Approved'
-		application.from_date = '2018-01-01'
-		application.to_date = '2018-01-03'
+		application.status = "Approved"
+		application.from_date = "2018-01-01"
+		application.to_date = "2018-01-03"
 		application.insert()
 		application.submit()
 
-		attendance = frappe.get_all('Attendance', ['name', 'status', 'attendance_date'],
-			dict(attendance_date=('between', ['2018-01-01', '2018-01-03']), docstatus=("!=", 2)))
+		attendance = frappe.get_all(
+			"Attendance",
+			["name", "status", "attendance_date"],
+			dict(attendance_date=("between", ["2018-01-01", "2018-01-03"]), docstatus=("!=", 2)),
+		)
 
 		# attendance created for all 3 days
 		self.assertEqual(len(attendance), 3)
 
 		# all on leave
-		self.assertTrue(all([d.status == 'On Leave' for d in attendance]))
+		self.assertTrue(all([d.status == "On Leave" for d in attendance]))
 
 		# dates
 		dates = [d.attendance_date for d in attendance]
-		for d in ('2018-01-01', '2018-01-02', '2018-01-03'):
+		for d in ("2018-01-01", "2018-01-02", "2018-01-03"):
 			self.assertTrue(getdate(d) in dates)
 
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
 	def test_attendance_for_include_holidays(self):
 		# Case 1: leave type with 'Include holidays within leaves as leaves' enabled
 		frappe.delete_doc_if_exists("Leave Type", "Test Include Holidays", force=1)
-		leave_type = frappe.get_doc(dict(
-			leave_type_name="Test Include Holidays",
-			doctype="Leave Type",
-			include_holiday=True
-		)).insert()
+		leave_type = frappe.get_doc(
+			dict(leave_type_name="Test Include Holidays", doctype="Leave Type", include_holiday=True)
+		).insert()
 
 		date = getdate()
-		make_allocation_record(leave_type=leave_type.name, from_date=get_year_start(date), to_date=get_year_ending(date))
+		make_allocation_record(
+			leave_type=leave_type.name, from_date=get_year_start(date), to_date=get_year_ending(date)
+		)
 
-		holiday_list = make_holiday_list()
 		employee = get_employee()
-		frappe.db.set_value("Company", employee.company, "default_holiday_list", holiday_list)
-		first_sunday = get_first_sunday(holiday_list)
+		first_sunday = get_first_sunday(self.holiday_list)
 
-		leave_application = make_leave_application(employee.name, first_sunday, add_days(first_sunday, 3), leave_type.name)
+		leave_application = make_leave_application(
+			employee.name, first_sunday, add_days(first_sunday, 3), leave_type.name
+		)
 		leave_application.reload()
 		self.assertEqual(leave_application.total_leave_days, 4)
-		self.assertEqual(frappe.db.count('Attendance', {'leave_application': leave_application.name}), 4)
+		self.assertEqual(frappe.db.count("Attendance", {"leave_application": leave_application.name}), 4)
 
 		leave_application.cancel()
 
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
 	def test_attendance_update_for_exclude_holidays(self):
 		# Case 2: leave type with 'Include holidays within leaves as leaves' disabled
 		frappe.delete_doc_if_exists("Leave Type", "Test Do Not Include Holidays", force=1)
-		leave_type = frappe.get_doc(dict(
-			leave_type_name="Test Do Not Include Holidays",
-			doctype="Leave Type",
-			include_holiday=False
-		)).insert()
+		leave_type = frappe.get_doc(
+			dict(
+				leave_type_name="Test Do Not Include Holidays", doctype="Leave Type", include_holiday=False
+			)
+		).insert()
 
 		date = getdate()
-		make_allocation_record(leave_type=leave_type.name, from_date=get_year_start(date), to_date=get_year_ending(date))
+		make_allocation_record(
+			leave_type=leave_type.name, from_date=get_year_start(date), to_date=get_year_ending(date)
+		)
 
-		holiday_list = make_holiday_list()
 		employee = get_employee()
-		frappe.db.set_value("Company", employee.company, "default_holiday_list", holiday_list)
-		first_sunday = get_first_sunday(holiday_list)
+		first_sunday = get_first_sunday(self.holiday_list)
 
 		# already marked attendance on a holiday should be deleted in this case
-		config = {
-			"doctype": "Attendance",
-			"employee": employee.name,
-			"status": "Present"
-		}
+		config = {"doctype": "Attendance", "employee": employee.name, "status": "Present"}
 		attendance_on_holiday = frappe.get_doc(config)
 		attendance_on_holiday.attendance_date = first_sunday
 		attendance_on_holiday.flags.ignore_validate = True
@@ -177,8 +357,11 @@
 		attendance.flags.ignore_validate = True
 		attendance.save()
 
-		leave_application = make_leave_application(employee.name, first_sunday, add_days(first_sunday, 3), leave_type.name)
+		leave_application = make_leave_application(
+			employee.name, first_sunday, add_days(first_sunday, 3), leave_type.name, employee.company
+		)
 		leave_application.reload()
+
 		# holiday should be excluded while marking attendance
 		self.assertEqual(leave_application.total_leave_days, 3)
 		self.assertEqual(frappe.db.count("Attendance", {"leave_application": leave_application.name}), 3)
@@ -193,11 +376,13 @@
 		self._clear_roles()
 
 		from frappe.utils.user import add_role
+
 		add_role("test@example.com", "HR User")
 		clear_user_permissions_for_doctype("Employee")
 
-		frappe.db.set_value("Department", "_Test Department - _TC",
-			"leave_block_list", "_Test Leave Block List")
+		frappe.db.set_value(
+			"Department", "_Test Department - _TC", "leave_block_list", "_Test Leave Block List"
+		)
 
 		make_allocation_record()
 
@@ -220,6 +405,7 @@
 		self._clear_applications()
 
 		from frappe.utils.user import add_role
+
 		add_role("test@example.com", "Employee")
 		frappe.set_user("test@example.com")
 
@@ -236,6 +422,7 @@
 		self._clear_applications()
 
 		from frappe.utils.user import add_role
+
 		add_role("test@example.com", "Employee")
 		frappe.set_user("test@example.com")
 
@@ -269,6 +456,7 @@
 		self._clear_applications()
 
 		from frappe.utils.user import add_role
+
 		add_role("test@example.com", "Employee")
 
 		frappe.set_user("test@example.com")
@@ -291,6 +479,7 @@
 		self._clear_applications()
 
 		from frappe.utils.user import add_role
+
 		add_role("test@example.com", "Employee")
 
 		frappe.set_user("test@example.com")
@@ -320,51 +509,49 @@
 		application.half_day_date = "2013-01-05"
 		application.insert()
 
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
 	def test_optional_leave(self):
 		leave_period = get_leave_period()
 		today = nowdate()
-		holiday_list = 'Test Holiday List for Optional Holiday'
+		holiday_list = "Test Holiday List for Optional Holiday"
 		employee = get_employee()
 
-		default_holiday_list = make_holiday_list()
-		frappe.db.set_value("Company", employee.company, "default_holiday_list", default_holiday_list)
-		first_sunday = get_first_sunday(default_holiday_list)
-
+		first_sunday = get_first_sunday(self.holiday_list)
 		optional_leave_date = add_days(first_sunday, 1)
 
-		if not frappe.db.exists('Holiday List', holiday_list):
-			frappe.get_doc(dict(
-				doctype = 'Holiday List',
-				holiday_list_name = holiday_list,
-				from_date = add_months(today, -6),
-				to_date = add_months(today, 6),
-				holidays = [
-					dict(holiday_date = optional_leave_date, description = 'Test')
-				]
-			)).insert()
+		if not frappe.db.exists("Holiday List", holiday_list):
+			frappe.get_doc(
+				dict(
+					doctype="Holiday List",
+					holiday_list_name=holiday_list,
+					from_date=add_months(today, -6),
+					to_date=add_months(today, 6),
+					holidays=[dict(holiday_date=optional_leave_date, description="Test")],
+				)
+			).insert()
 
-		frappe.db.set_value('Leave Period', leave_period.name, 'optional_holiday_list', holiday_list)
-		leave_type = 'Test Optional Type'
-		if not frappe.db.exists('Leave Type', leave_type):
-			frappe.get_doc(dict(
-				leave_type_name = leave_type,
-				doctype = 'Leave Type',
-				is_optional_leave = 1
-			)).insert()
+		frappe.db.set_value("Leave Period", leave_period.name, "optional_holiday_list", holiday_list)
+		leave_type = "Test Optional Type"
+		if not frappe.db.exists("Leave Type", leave_type):
+			frappe.get_doc(
+				dict(leave_type_name=leave_type, doctype="Leave Type", is_optional_leave=1)
+			).insert()
 
 		allocate_leaves(employee, leave_period, leave_type, 10)
 
 		date = add_days(first_sunday, 2)
 
-		leave_application = frappe.get_doc(dict(
-			doctype = 'Leave Application',
-			employee = employee.name,
-			company = '_Test Company',
-			description = "_Test Reason",
-			leave_type = leave_type,
-			from_date = date,
-			to_date = date,
-		))
+		leave_application = frappe.get_doc(
+			dict(
+				doctype="Leave Application",
+				employee=employee.name,
+				company="_Test Company",
+				description="_Test Reason",
+				leave_type=leave_type,
+				from_date=date,
+				to_date=date,
+			)
+		)
 
 		# can only apply on optional holidays
 		self.assertRaises(NotAnOptionalHoliday, leave_application.insert)
@@ -382,118 +569,125 @@
 		employee = get_employee()
 		leave_period = get_leave_period()
 		frappe.delete_doc_if_exists("Leave Type", "Test Leave Type", force=1)
-		leave_type = frappe.get_doc(dict(
-			leave_type_name = 'Test Leave Type',
-			doctype = 'Leave Type',
-			max_leaves_allowed = 5
-		)).insert()
+		leave_type = frappe.get_doc(
+			dict(leave_type_name="Test Leave Type", doctype="Leave Type", max_leaves_allowed=5)
+		).insert()
 
 		date = add_days(nowdate(), -7)
 
 		allocate_leaves(employee, leave_period, leave_type.name, 5)
 
-		leave_application = frappe.get_doc(dict(
-			doctype = 'Leave Application',
-			employee = employee.name,
-			leave_type = leave_type.name,
-			description = "_Test Reason",
-			from_date = date,
-			to_date = add_days(date, 2),
-			company = "_Test Company",
-			docstatus = 1,
-			status = "Approved"
-		))
+		leave_application = frappe.get_doc(
+			dict(
+				doctype="Leave Application",
+				employee=employee.name,
+				leave_type=leave_type.name,
+				description="_Test Reason",
+				from_date=date,
+				to_date=add_days(date, 2),
+				company="_Test Company",
+				docstatus=1,
+				status="Approved",
+			)
+		)
 		leave_application.submit()
 
-		leave_application = frappe.get_doc(dict(
-			doctype = 'Leave Application',
-			employee = employee.name,
-			leave_type = leave_type.name,
-			description = "_Test Reason",
-			from_date = add_days(date, 4),
-			to_date = add_days(date, 8),
-			company = "_Test Company",
-			docstatus = 1,
-			status = "Approved"
-		))
+		leave_application = frappe.get_doc(
+			dict(
+				doctype="Leave Application",
+				employee=employee.name,
+				leave_type=leave_type.name,
+				description="_Test Reason",
+				from_date=add_days(date, 4),
+				to_date=add_days(date, 8),
+				company="_Test Company",
+				docstatus=1,
+				status="Approved",
+			)
+		)
 		self.assertRaises(frappe.ValidationError, leave_application.insert)
 
 	def test_applicable_after(self):
 		employee = get_employee()
 		leave_period = get_leave_period()
 		frappe.delete_doc_if_exists("Leave Type", "Test Leave Type", force=1)
-		leave_type = frappe.get_doc(dict(
-			leave_type_name = 'Test Leave Type',
-			doctype = 'Leave Type',
-			applicable_after = 15
-		)).insert()
+		leave_type = frappe.get_doc(
+			dict(leave_type_name="Test Leave Type", doctype="Leave Type", applicable_after=15)
+		).insert()
 		date = add_days(nowdate(), -7)
-		frappe.db.set_value('Employee', employee.name, "date_of_joining", date)
+		frappe.db.set_value("Employee", employee.name, "date_of_joining", date)
 		allocate_leaves(employee, leave_period, leave_type.name, 10)
 
-		leave_application = frappe.get_doc(dict(
-			doctype = 'Leave Application',
-			employee = employee.name,
-			leave_type = leave_type.name,
-			description = "_Test Reason",
-			from_date = date,
-			to_date = add_days(date, 4),
-			company = "_Test Company",
-			docstatus = 1,
-			status = "Approved"
-		))
+		leave_application = frappe.get_doc(
+			dict(
+				doctype="Leave Application",
+				employee=employee.name,
+				leave_type=leave_type.name,
+				description="_Test Reason",
+				from_date=date,
+				to_date=add_days(date, 4),
+				company="_Test Company",
+				docstatus=1,
+				status="Approved",
+			)
+		)
 
 		self.assertRaises(frappe.ValidationError, leave_application.insert)
 
 		frappe.delete_doc_if_exists("Leave Type", "Test Leave Type 1", force=1)
-		leave_type_1 = frappe.get_doc(dict(
-			leave_type_name = 'Test Leave Type 1',
-			doctype = 'Leave Type'
-		)).insert()
+		leave_type_1 = frappe.get_doc(
+			dict(leave_type_name="Test Leave Type 1", doctype="Leave Type")
+		).insert()
 
 		allocate_leaves(employee, leave_period, leave_type_1.name, 10)
 
-		leave_application = frappe.get_doc(dict(
-		doctype = 'Leave Application',
-			employee = employee.name,
-			leave_type = leave_type_1.name,
-			description = "_Test Reason",
-			from_date = date,
-			to_date = add_days(date, 4),
-			company = "_Test Company",
-			docstatus = 1,
-			status = "Approved"
-		))
+		leave_application = frappe.get_doc(
+			dict(
+				doctype="Leave Application",
+				employee=employee.name,
+				leave_type=leave_type_1.name,
+				description="_Test Reason",
+				from_date=date,
+				to_date=add_days(date, 4),
+				company="_Test Company",
+				docstatus=1,
+				status="Approved",
+			)
+		)
 
 		self.assertTrue(leave_application.insert())
-		frappe.db.set_value('Employee', employee.name, "date_of_joining", "2010-01-01")
+		frappe.db.set_value("Employee", employee.name, "date_of_joining", "2010-01-01")
 
 	def test_max_continuous_leaves(self):
 		employee = get_employee()
 		leave_period = get_leave_period()
 		frappe.delete_doc_if_exists("Leave Type", "Test Leave Type", force=1)
-		leave_type = frappe.get_doc(dict(
-			leave_type_name = 'Test Leave Type',
-			doctype = 'Leave Type',
-			max_leaves_allowed = 15,
-			max_continuous_days_allowed = 3
-		)).insert()
+		leave_type = frappe.get_doc(
+			dict(
+				leave_type_name="Test Leave Type",
+				doctype="Leave Type",
+				max_leaves_allowed=15,
+				max_continuous_days_allowed=3,
+			)
+		).insert()
 
 		date = add_days(nowdate(), -7)
 
 		allocate_leaves(employee, leave_period, leave_type.name, 10)
 
-		leave_application = frappe.get_doc(dict(
-			doctype = 'Leave Application',
-			employee = employee.name,
-			leave_type = leave_type.name,
-			description = "_Test Reason",
-			from_date = date,
-			to_date = add_days(date, 4),
-			company = "_Test Company",
-			docstatus = 1,
-			status = "Approved"
-		))
+		leave_application = frappe.get_doc(
+			dict(
+				doctype="Leave Application",
+				employee=employee.name,
+				leave_type=leave_type.name,
+				description="_Test Reason",
+				from_date=date,
+				to_date=add_days(date, 4),
+				company="_Test Company",
+				docstatus=1,
+				status="Approved",
+			)
+		)
 
 		self.assertRaises(frappe.ValidationError, leave_application.insert)
 
@@ -502,87 +696,72 @@
 		leave_type = create_leave_type(
 			leave_type_name="_Test_CF_leave_expiry",
 			is_carry_forward=1,
-			expire_carry_forwarded_leaves_after_days=90)
-		leave_type.submit()
+			expire_carry_forwarded_leaves_after_days=90,
+		)
+		leave_type.insert()
 
 		create_carry_forwarded_allocation(employee, leave_type)
+		details = get_leave_balance_on(
+			employee.name, leave_type.name, nowdate(), add_days(nowdate(), 8), for_consumption=True
+		)
 
-		self.assertEqual(get_leave_balance_on(employee.name, leave_type.name, nowdate(), add_days(nowdate(), 8)), 21)
+		self.assertEqual(details.leave_balance_for_consumption, 21)
+		self.assertEqual(details.leave_balance, 30)
 
 	def test_earned_leaves_creation(self):
-
-		frappe.db.sql('''delete from `tabLeave Period`''')
-		frappe.db.sql('''delete from `tabLeave Policy Assignment`''')
-		frappe.db.sql('''delete from `tabLeave Allocation`''')
-		frappe.db.sql('''delete from `tabLeave Ledger Entry`''')
+		from erpnext.hr.utils import allocate_earned_leaves
 
 		leave_period = get_leave_period()
 		employee = get_employee()
-		leave_type = 'Test Earned Leave Type'
-		frappe.delete_doc_if_exists("Leave Type", 'Test Earned Leave Type', force=1)
-		frappe.get_doc(dict(
-			leave_type_name = leave_type,
-			doctype = 'Leave Type',
-			is_earned_leave = 1,
-			earned_leave_frequency = 'Monthly',
-			rounding = 0.5,
-			max_leaves_allowed = 6
-		)).insert()
+		leave_type = "Test Earned Leave Type"
+		make_policy_assignment(employee, leave_type, leave_period)
 
-		leave_policy = frappe.get_doc({
-			"doctype": "Leave Policy",
-			"title": "Test Leave Policy",
-			"leave_policy_details": [{"leave_type": leave_type, "annual_allocation": 6}]
-		}).insert()
+		for i in range(0, 14):
+			allocate_earned_leaves()
 
-		data = {
-			"assignment_based_on": "Leave Period",
-			"leave_policy": leave_policy.name,
-			"leave_period": leave_period.name
-		}
-
-		leave_policy_assignments = create_assignment_for_multiple_employees([employee.name], frappe._dict(data))
-
-		from erpnext.hr.utils import allocate_earned_leaves
-		i = 0
-		while(i<14):
-			allocate_earned_leaves(ignore_duplicates=True)
-			i += 1
 		self.assertEqual(get_leave_balance_on(employee.name, leave_type, nowdate()), 6)
 
 		# validate earned leaves creation without maximum leaves
-		frappe.db.set_value('Leave Type', leave_type, 'max_leaves_allowed', 0)
-		i = 0
-		while(i<6):
-			allocate_earned_leaves(ignore_duplicates=True)
-			i += 1
+		frappe.db.set_value("Leave Type", leave_type, "max_leaves_allowed", 0)
+
+		for i in range(0, 6):
+			allocate_earned_leaves()
+
 		self.assertEqual(get_leave_balance_on(employee.name, leave_type, nowdate()), 9)
 
 	# test to not consider current leave in leave balance while submitting
 	def test_current_leave_on_submit(self):
 		employee = get_employee()
-		leave_type = 'Sick leave'
-		allocation = frappe.get_doc(dict(
-			doctype = 'Leave Allocation',
-			employee = employee.name,
-			leave_type = leave_type,
-			from_date = '2018-10-01',
-			to_date = '2018-10-10',
-			new_leaves_allocated = 1
-		))
+
+		leave_type = "Sick Leave"
+		if not frappe.db.exists("Leave Type", leave_type):
+			frappe.get_doc(dict(leave_type_name=leave_type, doctype="Leave Type")).insert()
+
+		allocation = frappe.get_doc(
+			dict(
+				doctype="Leave Allocation",
+				employee=employee.name,
+				leave_type=leave_type,
+				from_date="2018-10-01",
+				to_date="2018-10-10",
+				new_leaves_allocated=1,
+			)
+		)
 		allocation.insert(ignore_permissions=True)
 		allocation.submit()
-		leave_application = frappe.get_doc(dict(
-			doctype = 'Leave Application',
-			employee = employee.name,
-			leave_type = leave_type,
-			description = "_Test Reason",
-			from_date = '2018-10-02',
-			to_date = '2018-10-02',
-			company = '_Test Company',
-			status = 'Approved',
-			leave_approver = 'test@example.com'
-		))
+		leave_application = frappe.get_doc(
+			dict(
+				doctype="Leave Application",
+				employee=employee.name,
+				leave_type=leave_type,
+				description="_Test Reason",
+				from_date="2018-10-02",
+				to_date="2018-10-02",
+				company="_Test Company",
+				status="Approved",
+				leave_approver="test@example.com",
+			)
+		)
 		self.assertTrue(leave_application.insert())
 		leave_application.submit()
 		self.assertEqual(leave_application.docstatus, 1)
@@ -590,26 +769,31 @@
 	def test_creation_of_leave_ledger_entry_on_submit(self):
 		employee = get_employee()
 
-		leave_type = create_leave_type(leave_type_name = 'Test Leave Type 1')
+		leave_type = create_leave_type(leave_type_name="Test Leave Type 1")
 		leave_type.save()
 
-		leave_allocation = create_leave_allocation(employee=employee.name, employee_name=employee.employee_name,
-			leave_type=leave_type.name)
+		leave_allocation = create_leave_allocation(
+			employee=employee.name, employee_name=employee.employee_name, leave_type=leave_type.name
+		)
 		leave_allocation.submit()
 
-		leave_application = frappe.get_doc(dict(
-			doctype = 'Leave Application',
-			employee = employee.name,
-			leave_type = leave_type.name,
-			from_date = add_days(nowdate(), 1),
-			to_date = add_days(nowdate(), 4),
-			description = "_Test Reason",
-			company = "_Test Company",
-			docstatus = 1,
-			status = "Approved"
-		))
+		leave_application = frappe.get_doc(
+			dict(
+				doctype="Leave Application",
+				employee=employee.name,
+				leave_type=leave_type.name,
+				from_date=add_days(nowdate(), 1),
+				to_date=add_days(nowdate(), 4),
+				description="_Test Reason",
+				company="_Test Company",
+				docstatus=1,
+				status="Approved",
+			)
+		)
 		leave_application.submit()
-		leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=dict(transaction_name=leave_application.name))
+		leave_ledger_entry = frappe.get_all(
+			"Leave Ledger Entry", fields="*", filters=dict(transaction_name=leave_application.name)
+		)
 
 		self.assertEqual(leave_ledger_entry[0].employee, leave_application.employee)
 		self.assertEqual(leave_ledger_entry[0].leave_type, leave_application.leave_type)
@@ -617,37 +801,47 @@
 
 		# check if leave ledger entry is deleted on cancellation
 		leave_application.cancel()
-		self.assertFalse(frappe.db.exists("Leave Ledger Entry", {'transaction_name':leave_application.name}))
+		self.assertFalse(
+			frappe.db.exists("Leave Ledger Entry", {"transaction_name": leave_application.name})
+		)
 
 	def test_ledger_entry_creation_on_intermediate_allocation_expiry(self):
 		employee = get_employee()
 		leave_type = create_leave_type(
 			leave_type_name="_Test_CF_leave_expiry",
 			is_carry_forward=1,
-			expire_carry_forwarded_leaves_after_days=90)
+			expire_carry_forwarded_leaves_after_days=90,
+			include_holiday=True,
+		)
 		leave_type.submit()
 
 		create_carry_forwarded_allocation(employee, leave_type)
 
-		leave_application = frappe.get_doc(dict(
-			doctype = 'Leave Application',
-			employee = employee.name,
-			leave_type = leave_type.name,
-			from_date = add_days(nowdate(), -3),
-			to_date = add_days(nowdate(), 7),
-			description = "_Test Reason",
-			company = "_Test Company",
-			docstatus = 1,
-			status = "Approved"
-		))
+		leave_application = frappe.get_doc(
+			dict(
+				doctype="Leave Application",
+				employee=employee.name,
+				leave_type=leave_type.name,
+				from_date=add_days(nowdate(), -3),
+				to_date=add_days(nowdate(), 7),
+				half_day=1,
+				half_day_date=add_days(nowdate(), -3),
+				description="_Test Reason",
+				company="_Test Company",
+				docstatus=1,
+				status="Approved",
+			)
+		)
 		leave_application.submit()
 
-		leave_ledger_entry = frappe.get_all('Leave Ledger Entry', '*', filters=dict(transaction_name=leave_application.name))
+		leave_ledger_entry = frappe.get_all(
+			"Leave Ledger Entry", "*", filters=dict(transaction_name=leave_application.name)
+		)
 
 		self.assertEqual(len(leave_ledger_entry), 2)
 		self.assertEqual(leave_ledger_entry[0].employee, leave_application.employee)
 		self.assertEqual(leave_ledger_entry[0].leave_type, leave_application.leave_type)
-		self.assertEqual(leave_ledger_entry[0].leaves, -9)
+		self.assertEqual(leave_ledger_entry[0].leaves, -8.5)
 		self.assertEqual(leave_ledger_entry[1].leaves, -2)
 
 	def test_leave_application_creation_after_expiry(self):
@@ -656,12 +850,18 @@
 		leave_type = create_leave_type(
 			leave_type_name="_Test_CF_leave_expiry",
 			is_carry_forward=1,
-			expire_carry_forwarded_leaves_after_days=90)
+			expire_carry_forwarded_leaves_after_days=90,
+		)
 		leave_type.submit()
 
 		create_carry_forwarded_allocation(employee, leave_type)
 
-		self.assertEqual(get_leave_balance_on(employee.name, leave_type.name, add_days(nowdate(), -85), add_days(nowdate(), -84)), 0)
+		self.assertEqual(
+			get_leave_balance_on(
+				employee.name, leave_type.name, add_days(nowdate(), -85), add_days(nowdate(), -84)
+			),
+			0,
+		)
 
 	def test_leave_approver_perms(self):
 		employee = get_employee()
@@ -677,8 +877,8 @@
 		make_allocation_record(employee.name)
 
 		application = self.get_application(_test_records[0])
-		application.from_date = '2018-01-01'
-		application.to_date = '2018-01-03'
+		application.from_date = "2018-01-01"
+		application.to_date = "2018-01-03"
 		application.leave_approver = user
 		application.insert()
 		self.assertTrue(application.name in frappe.share.get_shared("Leave Application", user))
@@ -704,92 +904,252 @@
 		employee.leave_approver = ""
 		employee.save()
 
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
+	def test_get_leave_details_for_dashboard(self):
+		employee = get_employee()
+		date = getdate()
+		year_start = getdate(get_year_start(date))
+		year_end = getdate(get_year_ending(date))
+
+		# ALLOCATION = 30
+		allocation = make_allocation_record(
+			employee=employee.name, from_date=year_start, to_date=year_end
+		)
+
+		# USED LEAVES = 4
+		first_sunday = get_first_sunday(self.holiday_list)
+		leave_application = make_leave_application(
+			employee.name, add_days(first_sunday, 1), add_days(first_sunday, 4), "_Test Leave Type"
+		)
+		leave_application.reload()
+
+		# LEAVES PENDING APPROVAL = 1
+		leave_application = make_leave_application(
+			employee.name,
+			add_days(first_sunday, 5),
+			add_days(first_sunday, 5),
+			"_Test Leave Type",
+			submit=False,
+		)
+		leave_application.status = "Open"
+		leave_application.save()
+
+		details = get_leave_details(employee.name, allocation.from_date)
+		leave_allocation = details["leave_allocation"]["_Test Leave Type"]
+		self.assertEqual(leave_allocation["total_leaves"], 30)
+		self.assertEqual(leave_allocation["leaves_taken"], 4)
+		self.assertEqual(leave_allocation["expired_leaves"], 0)
+		self.assertEqual(leave_allocation["leaves_pending_approval"], 1)
+		self.assertEqual(leave_allocation["remaining_leaves"], 26)
+
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
+	def test_get_earned_leave_details_for_dashboard(self):
+		from erpnext.hr.utils import allocate_earned_leaves
+
+		leave_period = get_leave_period()
+		employee = get_employee()
+		leave_type = "Test Earned Leave Type"
+		leave_policy_assignments = make_policy_assignment(employee, leave_type, leave_period)
+		allocation = frappe.db.get_value(
+			"Leave Allocation",
+			{"leave_policy_assignment": leave_policy_assignments[0]},
+			"name",
+		)
+		allocation = frappe.get_doc("Leave Allocation", allocation)
+		allocation.new_leaves_allocated = 2
+		allocation.save()
+
+		for i in range(0, 6):
+			allocate_earned_leaves()
+
+		first_sunday = get_first_sunday(self.holiday_list)
+		make_leave_application(
+			employee.name, add_days(first_sunday, 1), add_days(first_sunday, 1), leave_type
+		)
+
+		details = get_leave_details(employee.name, allocation.from_date)
+		leave_allocation = details["leave_allocation"][leave_type]
+		expected = {
+			"total_leaves": 2.0,
+			"expired_leaves": 0.0,
+			"leaves_taken": 1.0,
+			"leaves_pending_approval": 0.0,
+			"remaining_leaves": 1.0,
+		}
+		self.assertEqual(leave_allocation, expected)
+
+		details = get_leave_details(employee.name, getdate())
+		leave_allocation = details["leave_allocation"][leave_type]
+
+		expected = {
+			"total_leaves": 5.0,
+			"expired_leaves": 0.0,
+			"leaves_taken": 1.0,
+			"leaves_pending_approval": 0.0,
+			"remaining_leaves": 4.0,
+		}
+		self.assertEqual(leave_allocation, expected)
+
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
+	def test_get_leave_allocation_records(self):
+		employee = get_employee()
+		leave_type = create_leave_type(
+			leave_type_name="_Test_CF_leave_expiry",
+			is_carry_forward=1,
+			expire_carry_forwarded_leaves_after_days=90,
+		)
+		leave_type.insert()
+
+		leave_alloc = create_carry_forwarded_allocation(employee, leave_type)
+		details = get_leave_allocation_records(employee.name, getdate(), leave_type.name)
+		expected_data = {
+			"from_date": getdate(leave_alloc.from_date),
+			"to_date": getdate(leave_alloc.to_date),
+			"total_leaves_allocated": 30.0,
+			"unused_leaves": 15.0,
+			"new_leaves_allocated": 15.0,
+			"leave_type": leave_type.name,
+		}
+		self.assertEqual(details.get(leave_type.name), expected_data)
+
 
 def create_carry_forwarded_allocation(employee, leave_type):
-		# initial leave allocation
-		leave_allocation = create_leave_allocation(
-			leave_type="_Test_CF_leave_expiry",
-			employee=employee.name,
-			employee_name=employee.employee_name,
-			from_date=add_months(nowdate(), -24),
-			to_date=add_months(nowdate(), -12),
-			carry_forward=0)
-		leave_allocation.submit()
+	# initial leave allocation
+	leave_allocation = create_leave_allocation(
+		leave_type="_Test_CF_leave_expiry",
+		employee=employee.name,
+		employee_name=employee.employee_name,
+		from_date=add_months(nowdate(), -24),
+		to_date=add_months(nowdate(), -12),
+		carry_forward=0,
+	)
+	leave_allocation.submit()
 
-		leave_allocation = create_leave_allocation(
-			leave_type="_Test_CF_leave_expiry",
-			employee=employee.name,
-			employee_name=employee.employee_name,
-			from_date=add_days(nowdate(), -84),
-			to_date=add_days(nowdate(), 100),
-			carry_forward=1)
-		leave_allocation.submit()
+	leave_allocation = create_leave_allocation(
+		leave_type="_Test_CF_leave_expiry",
+		employee=employee.name,
+		employee_name=employee.employee_name,
+		from_date=add_days(nowdate(), -84),
+		to_date=add_days(nowdate(), 100),
+		carry_forward=1,
+	)
+	leave_allocation.submit()
 
-def make_allocation_record(employee=None, leave_type=None, from_date=None, to_date=None):
-	allocation = frappe.get_doc({
-		"doctype": "Leave Allocation",
-		"employee": employee or "_T-Employee-00001",
-		"leave_type": leave_type or "_Test Leave Type",
-		"from_date": from_date or "2013-01-01",
-		"to_date": to_date or "2019-12-31",
-		"new_leaves_allocated": 30
-	})
+	return leave_allocation
+
+
+def make_allocation_record(
+	employee=None, leave_type=None, from_date=None, to_date=None, carry_forward=False, leaves=None
+):
+	allocation = frappe.get_doc(
+		{
+			"doctype": "Leave Allocation",
+			"employee": employee or "_T-Employee-00001",
+			"leave_type": leave_type or "_Test Leave Type",
+			"from_date": from_date or "2013-01-01",
+			"to_date": to_date or "2019-12-31",
+			"new_leaves_allocated": leaves or 30,
+			"carry_forward": carry_forward,
+		}
+	)
 
 	allocation.insert(ignore_permissions=True)
 	allocation.submit()
 
+	return allocation
+
+
 def get_employee():
 	return frappe.get_doc("Employee", "_T-Employee-00001")
 
+
 def set_leave_approver():
 	employee = get_employee()
 	dept_doc = frappe.get_doc("Department", employee.department)
-	dept_doc.append('leave_approvers', {
-		'approver': 'test@example.com'
-	})
+	dept_doc.append("leave_approvers", {"approver": "test@example.com"})
 	dept_doc.save(ignore_permissions=True)
 
+
 def get_leave_period():
-	leave_period_name = frappe.db.exists({
-		"doctype": "Leave Period",
-		"company": "_Test Company"
-	})
+	leave_period_name = frappe.db.get_value("Leave Period", {"company": "_Test Company"})
 	if leave_period_name:
-		return frappe.get_doc("Leave Period", leave_period_name[0][0])
+		return frappe.get_doc("Leave Period", leave_period_name)
 	else:
-		return frappe.get_doc(dict(
-				name = 'Test Leave Period',
-				doctype = 'Leave Period',
-				from_date = add_months(nowdate(), -6),
-				to_date = add_months(nowdate(), 6),
-				company = "_Test Company",
-				is_active = 1
-			)).insert()
+		return frappe.get_doc(
+			dict(
+				name="Test Leave Period",
+				doctype="Leave Period",
+				from_date=add_months(nowdate(), -6),
+				to_date=add_months(nowdate(), 6),
+				company="_Test Company",
+				is_active=1,
+			)
+		).insert()
+
 
 def allocate_leaves(employee, leave_period, leave_type, new_leaves_allocated, eligible_leaves=0):
-	allocate_leave = frappe.get_doc({
-		"doctype": "Leave Allocation",
-		"__islocal": 1,
-		"employee": employee.name,
-		"employee_name": employee.employee_name,
-		"leave_type": leave_type,
-		"from_date": leave_period.from_date,
-		"to_date": leave_period.to_date,
-		"new_leaves_allocated": new_leaves_allocated,
-		"docstatus": 1
-	}).insert()
+	allocate_leave = frappe.get_doc(
+		{
+			"doctype": "Leave Allocation",
+			"__islocal": 1,
+			"employee": employee.name,
+			"employee_name": employee.employee_name,
+			"leave_type": leave_type,
+			"from_date": leave_period.from_date,
+			"to_date": leave_period.to_date,
+			"new_leaves_allocated": new_leaves_allocated,
+			"docstatus": 1,
+		}
+	).insert()
 
 	allocate_leave.submit()
 
 
-def get_first_sunday(holiday_list):
-	month_start_date = get_first_day(nowdate())
-	month_end_date = get_last_day(nowdate())
-	first_sunday = frappe.db.sql("""
+def get_first_sunday(holiday_list, for_date=None):
+	date = for_date or getdate()
+	month_start_date = get_first_day(date)
+	month_end_date = get_last_day(date)
+	first_sunday = frappe.db.sql(
+		"""
 		select holiday_date from `tabHoliday`
 		where parent = %s
 			and holiday_date between %s and %s
 		order by holiday_date
-	""", (holiday_list, month_start_date, month_end_date))[0][0]
+	""",
+		(holiday_list, month_start_date, month_end_date),
+	)[0][0]
 
-	return first_sunday
\ No newline at end of file
+	return first_sunday
+
+
+def make_policy_assignment(employee, leave_type, leave_period):
+	frappe.delete_doc_if_exists("Leave Type", leave_type, force=1)
+	frappe.get_doc(
+		dict(
+			leave_type_name=leave_type,
+			doctype="Leave Type",
+			is_earned_leave=1,
+			earned_leave_frequency="Monthly",
+			rounding=0.5,
+			max_leaves_allowed=6,
+		)
+	).insert()
+
+	leave_policy = frappe.get_doc(
+		{
+			"doctype": "Leave Policy",
+			"title": "Test Leave Policy",
+			"leave_policy_details": [{"leave_type": leave_type, "annual_allocation": 6}],
+		}
+	).insert()
+
+	data = {
+		"assignment_based_on": "Leave Period",
+		"leave_policy": leave_policy.name,
+		"leave_period": leave_period.name,
+	}
+
+	leave_policy_assignments = create_assignment_for_multiple_employees(
+		[employee.name], frappe._dict(data)
+	)
+	return leave_policy_assignments
diff --git a/erpnext/hr/doctype/leave_block_list/leave_block_list.py b/erpnext/hr/doctype/leave_block_list/leave_block_list.py
index d6b77f9..a57ba84 100644
--- a/erpnext/hr/doctype/leave_block_list/leave_block_list.py
+++ b/erpnext/hr/doctype/leave_block_list/leave_block_list.py
@@ -10,7 +10,6 @@
 
 
 class LeaveBlockList(Document):
-
 	def validate(self):
 		dates = []
 		for d in self.get("leave_block_list_dates"):
@@ -20,23 +19,29 @@
 				frappe.msgprint(_("Date is repeated") + ":" + d.block_date, raise_exception=1)
 			dates.append(d.block_date)
 
+
 @frappe.whitelist()
-def get_applicable_block_dates(from_date, to_date, employee=None,
-	company=None, all_lists=False):
+def get_applicable_block_dates(from_date, to_date, employee=None, company=None, all_lists=False):
 	block_dates = []
 	for block_list in get_applicable_block_lists(employee, company, all_lists):
-		block_dates.extend(frappe.db.sql("""select block_date, reason
+		block_dates.extend(
+			frappe.db.sql(
+				"""select block_date, reason
 			from `tabLeave Block List Date` where parent=%s
-			and block_date between %s and %s""", (block_list, from_date, to_date),
-			as_dict=1))
+			and block_date between %s and %s""",
+				(block_list, from_date, to_date),
+				as_dict=1,
+			)
+		)
 
 	return block_dates
 
+
 def get_applicable_block_lists(employee=None, company=None, all_lists=False):
 	block_lists = []
 
 	if not employee:
-		employee = frappe.db.get_value("Employee", {"user_id":frappe.session.user})
+		employee = frappe.db.get_value("Employee", {"user_id": frappe.session.user})
 		if not employee:
 			return []
 
@@ -49,18 +54,25 @@
 				block_lists.append(block_list)
 
 	# per department
-	department = frappe.db.get_value("Employee",employee, "department")
+	department = frappe.db.get_value("Employee", employee, "department")
 	if department:
 		block_list = frappe.db.get_value("Department", department, "leave_block_list")
 		add_block_list(block_list)
 
 	# global
-	for block_list in frappe.db.sql_list("""select name from `tabLeave Block List`
-		where applies_to_all_departments=1 and company=%s""", company):
+	for block_list in frappe.db.sql_list(
+		"""select name from `tabLeave Block List`
+		where applies_to_all_departments=1 and company=%s""",
+		company,
+	):
 		add_block_list(block_list)
 
 	return list(set(block_lists))
 
+
 def is_user_in_allow_list(block_list):
-	return frappe.session.user in frappe.db.sql_list("""select allow_user
-		from `tabLeave Block List Allow` where parent=%s""", block_list)
+	return frappe.session.user in frappe.db.sql_list(
+		"""select allow_user
+		from `tabLeave Block List Allow` where parent=%s""",
+		block_list,
+	)
diff --git a/erpnext/hr/doctype/leave_block_list/leave_block_list_dashboard.py b/erpnext/hr/doctype/leave_block_list/leave_block_list_dashboard.py
index 7cca62e..afeb5de 100644
--- a/erpnext/hr/doctype/leave_block_list/leave_block_list_dashboard.py
+++ b/erpnext/hr/doctype/leave_block_list/leave_block_list_dashboard.py
@@ -1,9 +1,2 @@
 def get_data():
-	return {
-		'fieldname':  'leave_block_list',
-		'transactions': [
-			{
-				'items': ['Department']
-			}
-		]
-	}
+	return {"fieldname": "leave_block_list", "transactions": [{"items": ["Department"]}]}
diff --git a/erpnext/hr/doctype/leave_block_list/test_leave_block_list.py b/erpnext/hr/doctype/leave_block_list/test_leave_block_list.py
index afbabb6..be85a35 100644
--- a/erpnext/hr/doctype/leave_block_list/test_leave_block_list.py
+++ b/erpnext/hr/doctype/leave_block_list/test_leave_block_list.py
@@ -15,24 +15,36 @@
 
 	def test_get_applicable_block_dates(self):
 		frappe.set_user("test@example.com")
-		frappe.db.set_value("Department", "_Test Department - _TC", "leave_block_list",
-			"_Test Leave Block List")
-		self.assertTrue(getdate("2013-01-02") in
-			[d.block_date for d in get_applicable_block_dates("2013-01-01", "2013-01-03")])
+		frappe.db.set_value(
+			"Department", "_Test Department - _TC", "leave_block_list", "_Test Leave Block List"
+		)
+		self.assertTrue(
+			getdate("2013-01-02")
+			in [d.block_date for d in get_applicable_block_dates("2013-01-01", "2013-01-03")]
+		)
 
 	def test_get_applicable_block_dates_for_allowed_user(self):
 		frappe.set_user("test1@example.com")
-		frappe.db.set_value("Department", "_Test Department 1 - _TC", "leave_block_list",
-			"_Test Leave Block List")
-		self.assertEqual([], [d.block_date for d in get_applicable_block_dates("2013-01-01", "2013-01-03")])
+		frappe.db.set_value(
+			"Department", "_Test Department 1 - _TC", "leave_block_list", "_Test Leave Block List"
+		)
+		self.assertEqual(
+			[], [d.block_date for d in get_applicable_block_dates("2013-01-01", "2013-01-03")]
+		)
 
 	def test_get_applicable_block_dates_all_lists(self):
 		frappe.set_user("test1@example.com")
-		frappe.db.set_value("Department", "_Test Department 1 - _TC", "leave_block_list",
-			"_Test Leave Block List")
-		self.assertTrue(getdate("2013-01-02") in
-			[d.block_date for d in get_applicable_block_dates("2013-01-01", "2013-01-03", all_lists=True)])
+		frappe.db.set_value(
+			"Department", "_Test Department 1 - _TC", "leave_block_list", "_Test Leave Block List"
+		)
+		self.assertTrue(
+			getdate("2013-01-02")
+			in [
+				d.block_date for d in get_applicable_block_dates("2013-01-01", "2013-01-03", all_lists=True)
+			]
+		)
+
 
 test_dependencies = ["Employee"]
 
-test_records = frappe.get_test_records('Leave Block List')
+test_records = frappe.get_test_records("Leave Block List")
diff --git a/erpnext/hr/doctype/leave_control_panel/leave_control_panel.py b/erpnext/hr/doctype/leave_control_panel/leave_control_panel.py
index 19f97b8..c57f8ae 100644
--- a/erpnext/hr/doctype/leave_control_panel/leave_control_panel.py
+++ b/erpnext/hr/doctype/leave_control_panel/leave_control_panel.py
@@ -18,8 +18,12 @@
 
 		condition_str = " and " + " and ".join(conditions) if len(conditions) else ""
 
-		e = frappe.db.sql("select name from tabEmployee where status='Active' {condition}"
-			.format(condition=condition_str), tuple(values))
+		e = frappe.db.sql(
+			"select name from tabEmployee where status='Active' {condition}".format(
+				condition=condition_str
+			),
+			tuple(values),
+		)
 
 		return e
 
@@ -27,7 +31,7 @@
 		for f in ["from_date", "to_date", "leave_type", "no_of_days"]:
 			if not self.get(f):
 				frappe.throw(_("{0} is required").format(self.meta.get_label(f)))
-		self.validate_from_to_dates('from_date', 'to_date')
+		self.validate_from_to_dates("from_date", "to_date")
 
 	@frappe.whitelist()
 	def allocate_leave(self):
@@ -39,10 +43,10 @@
 
 		for d in self.get_employees():
 			try:
-				la = frappe.new_doc('Leave Allocation')
+				la = frappe.new_doc("Leave Allocation")
 				la.set("__islocal", 1)
 				la.employee = cstr(d[0])
-				la.employee_name = frappe.db.get_value('Employee',cstr(d[0]),'employee_name')
+				la.employee_name = frappe.db.get_value("Employee", cstr(d[0]), "employee_name")
 				la.leave_type = self.leave_type
 				la.from_date = self.from_date
 				la.to_date = self.to_date
diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py
index 8ef0e36..7c0f0db 100644
--- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py
+++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py
@@ -7,7 +7,7 @@
 from frappe.model.document import Document
 from frappe.utils import getdate, nowdate
 
-from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leaves
+from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period
 from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry
 from erpnext.hr.utils import set_employee_name, validate_active_employee
 from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import (
@@ -26,9 +26,12 @@
 			self.encashment_date = getdate(nowdate())
 
 	def validate_salary_structure(self):
-		if not frappe.db.exists('Salary Structure Assignment', {'employee': self.employee}):
-			frappe.throw(_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(self.employee))
-
+		if not frappe.db.exists("Salary Structure Assignment", {"employee": self.employee}):
+			frappe.throw(
+				_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(
+					self.employee
+				)
+			)
 
 	def before_submit(self):
 		if self.encashment_amount <= 0:
@@ -36,7 +39,7 @@
 
 	def on_submit(self):
 		if not self.leave_allocation:
-			self.leave_allocation = self.get_leave_allocation().get('name')
+			self.leave_allocation = self.get_leave_allocation().get("name")
 		additional_salary = frappe.new_doc("Additional Salary")
 		additional_salary.company = frappe.get_value("Employee", self.employee, "company")
 		additional_salary.employee = self.employee
@@ -52,8 +55,13 @@
 		additional_salary.submit()
 
 		# Set encashed leaves in Allocation
-		frappe.db.set_value("Leave Allocation", self.leave_allocation, "total_leaves_encashed",
-				frappe.db.get_value('Leave Allocation', self.leave_allocation, 'total_leaves_encashed') + self.encashable_days)
+		frappe.db.set_value(
+			"Leave Allocation",
+			self.leave_allocation,
+			"total_leaves_encashed",
+			frappe.db.get_value("Leave Allocation", self.leave_allocation, "total_leaves_encashed")
+			+ self.encashable_days,
+		)
 
 		self.create_leave_ledger_entry()
 
@@ -63,40 +71,83 @@
 			self.db_set("additional_salary", "")
 
 		if self.leave_allocation:
-			frappe.db.set_value("Leave Allocation", self.leave_allocation, "total_leaves_encashed",
-				frappe.db.get_value('Leave Allocation', self.leave_allocation, 'total_leaves_encashed') - self.encashable_days)
+			frappe.db.set_value(
+				"Leave Allocation",
+				self.leave_allocation,
+				"total_leaves_encashed",
+				frappe.db.get_value("Leave Allocation", self.leave_allocation, "total_leaves_encashed")
+				- self.encashable_days,
+			)
 		self.create_leave_ledger_entry(submit=False)
 
 	@frappe.whitelist()
 	def get_leave_details_for_encashment(self):
-		salary_structure = get_assigned_salary_structure(self.employee, self.encashment_date or getdate(nowdate()))
+		salary_structure = get_assigned_salary_structure(
+			self.employee, self.encashment_date or getdate(nowdate())
+		)
 		if not salary_structure:
-			frappe.throw(_("No Salary Structure assigned for Employee {0} on given date {1}").format(self.employee, self.encashment_date))
+			frappe.throw(
+				_("No Salary Structure assigned for Employee {0} on given date {1}").format(
+					self.employee, self.encashment_date
+				)
+			)
 
-		if not frappe.db.get_value("Leave Type", self.leave_type, 'allow_encashment'):
+		if not frappe.db.get_value("Leave Type", self.leave_type, "allow_encashment"):
 			frappe.throw(_("Leave Type {0} is not encashable").format(self.leave_type))
 
 		allocation = self.get_leave_allocation()
 
 		if not allocation:
-			frappe.throw(_("No Leaves Allocated to Employee: {0} for Leave Type: {1}").format(self.employee, self.leave_type))
+			frappe.throw(
+				_("No Leaves Allocated to Employee: {0} for Leave Type: {1}").format(
+					self.employee, self.leave_type
+				)
+			)
 
-		self.leave_balance = allocation.total_leaves_allocated - allocation.carry_forwarded_leaves_count\
-			- get_unused_leaves(self.employee, self.leave_type, allocation.from_date, self.encashment_date)
+		self.leave_balance = (
+			allocation.total_leaves_allocated
+			- allocation.carry_forwarded_leaves_count
+			# adding this because the function returns a -ve number
+			+ get_leaves_for_period(
+				self.employee, self.leave_type, allocation.from_date, self.encashment_date
+			)
+		)
 
-		encashable_days = self.leave_balance - frappe.db.get_value('Leave Type', self.leave_type, 'encashment_threshold_days')
+		encashable_days = self.leave_balance - frappe.db.get_value(
+			"Leave Type", self.leave_type, "encashment_threshold_days"
+		)
 		self.encashable_days = encashable_days if encashable_days > 0 else 0
 
-		per_day_encashment = frappe.db.get_value('Salary Structure', salary_structure , 'leave_encashment_amount_per_day')
-		self.encashment_amount = self.encashable_days * per_day_encashment if per_day_encashment > 0 else 0
+		per_day_encashment = frappe.db.get_value(
+			"Salary Structure", salary_structure, "leave_encashment_amount_per_day"
+		)
+		self.encashment_amount = (
+			self.encashable_days * per_day_encashment if per_day_encashment > 0 else 0
+		)
 
 		self.leave_allocation = allocation.name
 		return True
 
 	def get_leave_allocation(self):
-		leave_allocation = frappe.db.sql("""select name, to_date, total_leaves_allocated, carry_forwarded_leaves_count from `tabLeave Allocation` where '{0}'
-		between from_date and to_date and docstatus=1 and leave_type='{1}'
-		and employee= '{2}'""".format(self.encashment_date or getdate(nowdate()), self.leave_type, self.employee), as_dict=1) #nosec
+		date = self.encashment_date or getdate()
+
+		LeaveAllocation = frappe.qb.DocType("Leave Allocation")
+		leave_allocation = (
+			frappe.qb.from_(LeaveAllocation)
+			.select(
+				LeaveAllocation.name,
+				LeaveAllocation.from_date,
+				LeaveAllocation.to_date,
+				LeaveAllocation.total_leaves_allocated,
+				LeaveAllocation.carry_forwarded_leaves_count,
+			)
+			.where(
+				((LeaveAllocation.from_date <= date) & (date <= LeaveAllocation.to_date))
+				& (LeaveAllocation.docstatus == 1)
+				& (LeaveAllocation.leave_type == self.leave_type)
+				& (LeaveAllocation.employee == self.employee)
+			)
+		).run(as_dict=True)
 
 		return leave_allocation[0] if leave_allocation else None
 
@@ -105,7 +156,7 @@
 			leaves=self.encashable_days * -1,
 			from_date=self.encashment_date,
 			to_date=self.encashment_date,
-			is_carry_forward=0
+			is_carry_forward=0,
 		)
 		create_leave_ledger_entry(self, args, submit)
 
@@ -114,27 +165,26 @@
 		if not leave_allocation:
 			return
 
-		to_date = leave_allocation.get('to_date')
+		to_date = leave_allocation.get("to_date")
 		if to_date < getdate(nowdate()):
 			args = frappe._dict(
-				leaves=self.encashable_days,
-				from_date=to_date,
-				to_date=to_date,
-				is_carry_forward=0
+				leaves=self.encashable_days, from_date=to_date, to_date=to_date, is_carry_forward=0
 			)
 			create_leave_ledger_entry(self, args, submit)
 
 
 def create_leave_encashment(leave_allocation):
-	''' Creates leave encashment for the given allocations '''
+	"""Creates leave encashment for the given allocations"""
 	for allocation in leave_allocation:
 		if not get_assigned_salary_structure(allocation.employee, allocation.to_date):
 			continue
-		leave_encashment = frappe.get_doc(dict(
-			doctype="Leave Encashment",
-			leave_period=allocation.leave_period,
-			employee=allocation.employee,
-			leave_type=allocation.leave_type,
-			encashment_date=allocation.to_date
-		))
+		leave_encashment = frappe.get_doc(
+			dict(
+				doctype="Leave Encashment",
+				leave_period=allocation.leave_period,
+				employee=allocation.employee,
+				leave_type=allocation.leave_type,
+				encashment_date=allocation.to_date,
+			)
+		)
 		leave_encashment.insert(ignore_permissions=True)
diff --git a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
index 99a479d..d06b6a3 100644
--- a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
+++ b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
@@ -4,62 +4,83 @@
 import unittest
 
 import frappe
-from frappe.utils import add_months, today
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import add_days, get_year_ending, get_year_start, getdate
 
 from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list
 from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period
 from erpnext.hr.doctype.leave_policy.test_leave_policy import create_leave_policy
 from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import (
 	create_assignment_for_multiple_employees,
 )
+from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
+	make_holiday_list,
+	make_leave_application,
+)
 from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
 
-test_dependencies = ["Leave Type"]
+test_records = frappe.get_test_records("Leave Type")
 
-class TestLeaveEncashment(unittest.TestCase):
+
+class TestLeaveEncashment(FrappeTestCase):
 	def setUp(self):
-		frappe.db.sql('''delete from `tabLeave Period`''')
-		frappe.db.sql('''delete from `tabLeave Policy Assignment`''')
-		frappe.db.sql('''delete from `tabLeave Allocation`''')
-		frappe.db.sql('''delete from `tabLeave Ledger Entry`''')
-		frappe.db.sql('''delete from `tabAdditional Salary`''')
+		frappe.db.delete("Leave Period")
+		frappe.db.delete("Leave Policy Assignment")
+		frappe.db.delete("Leave Allocation")
+		frappe.db.delete("Leave Ledger Entry")
+		frappe.db.delete("Additional Salary")
+		frappe.db.delete("Leave Encashment")
+
+		if not frappe.db.exists("Leave Type", "_Test Leave Type Encashment"):
+			frappe.get_doc(test_records[2]).insert()
+
+		date = getdate()
+		year_start = getdate(get_year_start(date))
+		year_end = getdate(get_year_ending(date))
+
+		make_holiday_list("_Test Leave Encashment", year_start, year_end)
 
 		# create the leave policy
 		leave_policy = create_leave_policy(
-			leave_type="_Test Leave Type Encashment",
-			annual_allocation=10)
+			leave_type="_Test Leave Type Encashment", annual_allocation=10
+		)
 		leave_policy.submit()
 
 		# create employee, salary structure and assignment
-		self.employee = make_employee("test_employee_encashment@example.com")
+		self.employee = make_employee("test_employee_encashment@example.com", company="_Test Company")
 
-		self.leave_period = create_leave_period(add_months(today(), -3), add_months(today(), 3))
+		self.leave_period = create_leave_period(year_start, year_end, "_Test Company")
 
 		data = {
 			"assignment_based_on": "Leave Period",
 			"leave_policy": leave_policy.name,
-			"leave_period": self.leave_period.name
+			"leave_period": self.leave_period.name,
 		}
 
-		leave_policy_assignments = create_assignment_for_multiple_employees([self.employee], frappe._dict(data))
+		leave_policy_assignments = create_assignment_for_multiple_employees(
+			[self.employee], frappe._dict(data)
+		)
 
-		salary_structure = make_salary_structure("Salary Structure for Encashment", "Monthly", self.employee,
-			other_details={"leave_encashment_amount_per_day": 50})
+		salary_structure = make_salary_structure(
+			"Salary Structure for Encashment",
+			"Monthly",
+			self.employee,
+			other_details={"leave_encashment_amount_per_day": 50},
+		)
 
-	def tearDown(self):
-		for dt in ["Leave Period", "Leave Allocation", "Leave Ledger Entry", "Additional Salary", "Leave Encashment", "Salary Structure", "Leave Policy"]:
-			frappe.db.sql("delete from `tab%s`" % dt)
-
+	@set_holiday_list("_Test Leave Encashment", "_Test Company")
 	def test_leave_balance_value_and_amount(self):
-		frappe.db.sql('''delete from `tabLeave Encashment`''')
-		leave_encashment = frappe.get_doc(dict(
-			doctype='Leave Encashment',
-			employee=self.employee,
-			leave_type="_Test Leave Type Encashment",
-			leave_period=self.leave_period.name,
-			payroll_date=today(),
-			currency="INR"
-		)).insert()
+		leave_encashment = frappe.get_doc(
+			dict(
+				doctype="Leave Encashment",
+				employee=self.employee,
+				leave_type="_Test Leave Type Encashment",
+				leave_period=self.leave_period.name,
+				encashment_date=self.leave_period.to_date,
+				currency="INR",
+			)
+		).insert()
 
 		self.assertEqual(leave_encashment.leave_balance, 10)
 		self.assertEqual(leave_encashment.encashable_days, 5)
@@ -68,23 +89,58 @@
 		leave_encashment.submit()
 
 		# assert links
-		add_sal = frappe.get_all("Additional Salary", filters = {"ref_docname": leave_encashment.name})[0]
+		add_sal = frappe.get_all("Additional Salary", filters={"ref_docname": leave_encashment.name})[0]
 		self.assertTrue(add_sal)
 
-	def test_creation_of_leave_ledger_entry_on_submit(self):
-		frappe.db.sql('''delete from `tabLeave Encashment`''')
-		leave_encashment = frappe.get_doc(dict(
-			doctype='Leave Encashment',
-			employee=self.employee,
-			leave_type="_Test Leave Type Encashment",
-			leave_period=self.leave_period.name,
-			payroll_date=today(),
-			currency="INR"
-		)).insert()
+	@set_holiday_list("_Test Leave Encashment", "_Test Company")
+	def test_leave_balance_value_with_leaves_and_amount(self):
+		date = self.leave_period.from_date
+		leave_application = make_leave_application(
+			self.employee, date, add_days(date, 3), "_Test Leave Type Encashment"
+		)
+		leave_application.reload()
+
+		leave_encashment = frappe.get_doc(
+			dict(
+				doctype="Leave Encashment",
+				employee=self.employee,
+				leave_type="_Test Leave Type Encashment",
+				leave_period=self.leave_period.name,
+				encashment_date=self.leave_period.to_date,
+				currency="INR",
+			)
+		).insert()
+
+		self.assertEqual(leave_encashment.leave_balance, 10 - leave_application.total_leave_days)
+		# encashable days threshold is 5, total leaves are 6, so encashable days = 6-5 = 1
+		# with charge of 50 per day
+		self.assertEqual(leave_encashment.encashable_days, leave_encashment.leave_balance - 5)
+		self.assertEqual(leave_encashment.encashment_amount, 50)
 
 		leave_encashment.submit()
 
-		leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=dict(transaction_name=leave_encashment.name))
+		# assert links
+		add_sal = frappe.get_all("Additional Salary", filters={"ref_docname": leave_encashment.name})[0]
+		self.assertTrue(add_sal)
+
+	@set_holiday_list("_Test Leave Encashment", "_Test Company")
+	def test_creation_of_leave_ledger_entry_on_submit(self):
+		leave_encashment = frappe.get_doc(
+			dict(
+				doctype="Leave Encashment",
+				employee=self.employee,
+				leave_type="_Test Leave Type Encashment",
+				leave_period=self.leave_period.name,
+				encashment_date=self.leave_period.to_date,
+				currency="INR",
+			)
+		).insert()
+
+		leave_encashment.submit()
+
+		leave_ledger_entry = frappe.get_all(
+			"Leave Ledger Entry", fields="*", filters=dict(transaction_name=leave_encashment.name)
+		)
 
 		self.assertEqual(len(leave_ledger_entry), 1)
 		self.assertEqual(leave_ledger_entry[0].employee, leave_encashment.employee)
@@ -93,7 +149,11 @@
 
 		# check if leave ledger entry is deleted on cancellation
 
-		frappe.db.sql("Delete from `tabAdditional Salary` WHERE ref_docname = %s", (leave_encashment.name) )
+		frappe.db.sql(
+			"Delete from `tabAdditional Salary` WHERE ref_docname = %s", (leave_encashment.name)
+		)
 
 		leave_encashment.cancel()
-		self.assertFalse(frappe.db.exists("Leave Ledger Entry", {'transaction_name':leave_encashment.name}))
+		self.assertFalse(
+			frappe.db.exists("Leave Ledger Entry", {"transaction_name": leave_encashment.name})
+		)
diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py
index 5c5299e..fed9f77 100644
--- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py
+++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py
@@ -20,9 +20,11 @@
 		else:
 			frappe.throw(_("Only expired allocation can be cancelled"))
 
+
 def validate_leave_allocation_against_leave_application(ledger):
-	''' Checks that leave allocation has no leave application against it '''
-	leave_application_records = frappe.db.sql_list("""
+	"""Checks that leave allocation has no leave application against it"""
+	leave_application_records = frappe.db.sql_list(
+		"""
 		SELECT transaction_name
 		FROM `tabLeave Ledger Entry`
 		WHERE
@@ -31,15 +33,21 @@
 			AND transaction_type='Leave Application'
 			AND from_date>=%s
 			AND to_date<=%s
-	""", (ledger.employee, ledger.leave_type, ledger.from_date, ledger.to_date))
+	""",
+		(ledger.employee, ledger.leave_type, ledger.from_date, ledger.to_date),
+	)
 
 	if leave_application_records:
-		frappe.throw(_("Leave allocation {0} is linked with the Leave Application {1}").format(
-			ledger.transaction_name, ', '.join(leave_application_records)))
+		frappe.throw(
+			_("Leave allocation {0} is linked with the Leave Application {1}").format(
+				ledger.transaction_name, ", ".join(leave_application_records)
+			)
+		)
+
 
 def create_leave_ledger_entry(ref_doc, args, submit=True):
 	ledger = frappe._dict(
-		doctype='Leave Ledger Entry',
+		doctype="Leave Ledger Entry",
 		employee=ref_doc.employee,
 		employee_name=ref_doc.employee_name,
 		leave_type=ref_doc.leave_type,
@@ -47,7 +55,7 @@
 		transaction_name=ref_doc.name,
 		is_carry_forward=0,
 		is_expired=0,
-		is_lwp=0
+		is_lwp=0,
 	)
 	ledger.update(args)
 
@@ -58,54 +66,69 @@
 	else:
 		delete_ledger_entry(ledger)
 
+
 def delete_ledger_entry(ledger):
-	''' Delete ledger entry on cancel of leave application/allocation/encashment '''
+	"""Delete ledger entry on cancel of leave application/allocation/encashment"""
 	if ledger.transaction_type == "Leave Allocation":
 		validate_leave_allocation_against_leave_application(ledger)
 
 	expired_entry = get_previous_expiry_ledger_entry(ledger)
-	frappe.db.sql("""DELETE
+	frappe.db.sql(
+		"""DELETE
 		FROM `tabLeave Ledger Entry`
 		WHERE
 			`transaction_name`=%s
-			OR `name`=%s""", (ledger.transaction_name, expired_entry))
+			OR `name`=%s""",
+		(ledger.transaction_name, expired_entry),
+	)
+
 
 def get_previous_expiry_ledger_entry(ledger):
-	''' Returns the expiry ledger entry having same creation date as the ledger entry to be cancelled '''
-	creation_date = frappe.db.get_value("Leave Ledger Entry", filters={
-			'transaction_name': ledger.transaction_name,
-			'is_expired': 0,
-			'transaction_type': 'Leave Allocation'
-		}, fieldname=['creation'])
+	"""Returns the expiry ledger entry having same creation date as the ledger entry to be cancelled"""
+	creation_date = frappe.db.get_value(
+		"Leave Ledger Entry",
+		filters={
+			"transaction_name": ledger.transaction_name,
+			"is_expired": 0,
+			"transaction_type": "Leave Allocation",
+		},
+		fieldname=["creation"],
+	)
 
-	creation_date = creation_date.strftime(DATE_FORMAT) if creation_date else ''
+	creation_date = creation_date.strftime(DATE_FORMAT) if creation_date else ""
 
-	return frappe.db.get_value("Leave Ledger Entry", filters={
-		'creation': ('like', creation_date+"%"),
-		'employee': ledger.employee,
-		'leave_type': ledger.leave_type,
-		'is_expired': 1,
-		'docstatus': 1,
-		'is_carry_forward': 0
-	}, fieldname=['name'])
+	return frappe.db.get_value(
+		"Leave Ledger Entry",
+		filters={
+			"creation": ("like", creation_date + "%"),
+			"employee": ledger.employee,
+			"leave_type": ledger.leave_type,
+			"is_expired": 1,
+			"docstatus": 1,
+			"is_carry_forward": 0,
+		},
+		fieldname=["name"],
+	)
+
 
 def process_expired_allocation():
-	''' Check if a carry forwarded allocation has expired and create a expiry ledger entry
-		Case 1: carry forwarded expiry period is set for the leave type,
-			create a separate leave expiry entry against each entry of carry forwarded and non carry forwarded leaves
-		Case 2: leave type has no specific expiry period for carry forwarded leaves
-			and there is no carry forwarded leave allocation, create a single expiry against the remaining leaves.
-	'''
+	"""Check if a carry forwarded allocation has expired and create a expiry ledger entry
+	Case 1: carry forwarded expiry period is set for the leave type,
+	        create a separate leave expiry entry against each entry of carry forwarded and non carry forwarded leaves
+	Case 2: leave type has no specific expiry period for carry forwarded leaves
+	        and there is no carry forwarded leave allocation, create a single expiry against the remaining leaves.
+	"""
 
 	# fetch leave type records that has carry forwarded leaves expiry
-	leave_type_records = frappe.db.get_values("Leave Type", filters={
-			'expire_carry_forwarded_leaves_after_days': (">", 0)
-		}, fieldname=['name'])
+	leave_type_records = frappe.db.get_values(
+		"Leave Type", filters={"expire_carry_forwarded_leaves_after_days": (">", 0)}, fieldname=["name"]
+	)
 
-	leave_type = [record[0] for record in leave_type_records] or ['']
+	leave_type = [record[0] for record in leave_type_records] or [""]
 
 	# fetch non expired leave ledger entry of transaction_type allocation
-	expire_allocation = frappe.db.sql("""
+	expire_allocation = frappe.db.sql(
+		"""
 		SELECT
 			leaves, to_date, employee, leave_type,
 			is_carry_forward, transaction_name as name, transaction_type
@@ -123,32 +146,41 @@
 						OR (is_carry_forward = 0 AND leave_type not in %s)
 			)))
 			AND transaction_type = 'Leave Allocation'
-			AND to_date < %s""", (leave_type, today()), as_dict=1)
+			AND to_date < %s""",
+		(leave_type, today()),
+		as_dict=1,
+	)
 
 	if expire_allocation:
 		create_expiry_ledger_entry(expire_allocation)
 
+
 def create_expiry_ledger_entry(allocations):
-	''' Create ledger entry for expired allocation '''
+	"""Create ledger entry for expired allocation"""
 	for allocation in allocations:
 		if allocation.is_carry_forward:
 			expire_carried_forward_allocation(allocation)
 		else:
 			expire_allocation(allocation)
 
+
 def get_remaining_leaves(allocation):
-	''' Returns remaining leaves from the given allocation '''
-	return frappe.db.get_value("Leave Ledger Entry",
+	"""Returns remaining leaves from the given allocation"""
+	return frappe.db.get_value(
+		"Leave Ledger Entry",
 		filters={
-			'employee': allocation.employee,
-			'leave_type': allocation.leave_type,
-			'to_date': ('<=', allocation.to_date),
-			'docstatus': 1
-		}, fieldname=['SUM(leaves)'])
+			"employee": allocation.employee,
+			"leave_type": allocation.leave_type,
+			"to_date": ("<=", allocation.to_date),
+			"docstatus": 1,
+		},
+		fieldname=["SUM(leaves)"],
+	)
+
 
 @frappe.whitelist()
 def expire_allocation(allocation, expiry_date=None):
-	''' expires non-carry forwarded allocation '''
+	"""expires non-carry forwarded allocation"""
 	leaves = get_remaining_leaves(allocation)
 	expiry_date = expiry_date if expiry_date else allocation.to_date
 
@@ -157,21 +189,28 @@
 		args = dict(
 			leaves=flt(leaves) * -1,
 			transaction_name=allocation.name,
-			transaction_type='Leave Allocation',
+			transaction_type="Leave Allocation",
 			from_date=expiry_date,
 			to_date=expiry_date,
 			is_carry_forward=0,
-			is_expired=1
+			is_expired=1,
 		)
 		create_leave_ledger_entry(allocation, args)
 
 	frappe.db.set_value("Leave Allocation", allocation.name, "expired", 1)
 
+
 def expire_carried_forward_allocation(allocation):
-	''' Expires remaining leaves in the on carried forward allocation '''
+	"""Expires remaining leaves in the on carried forward allocation"""
 	from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period
-	leaves_taken = get_leaves_for_period(allocation.employee, allocation.leave_type,
-		allocation.from_date, allocation.to_date, do_not_skip_expired_leaves=True)
+
+	leaves_taken = get_leaves_for_period(
+		allocation.employee,
+		allocation.leave_type,
+		allocation.from_date,
+		allocation.to_date,
+		skip_expired_leaves=False,
+	)
 	leaves = flt(allocation.leaves) + flt(leaves_taken)
 
 	# allow expired leaves entry to be created
@@ -183,6 +222,6 @@
 			is_carry_forward=allocation.is_carry_forward,
 			is_expired=1,
 			from_date=allocation.to_date,
-			to_date=allocation.to_date
+			to_date=allocation.to_date,
 		)
 		create_leave_ledger_entry(allocation, args)
diff --git a/erpnext/hr/doctype/leave_period/leave_period.py b/erpnext/hr/doctype/leave_period/leave_period.py
index b1cb688..6e62bb5 100644
--- a/erpnext/hr/doctype/leave_period/leave_period.py
+++ b/erpnext/hr/doctype/leave_period/leave_period.py
@@ -11,7 +11,6 @@
 
 
 class LeavePeriod(Document):
-
 	def validate(self):
 		self.validate_dates()
 		validate_overlap(self, self.from_date, self.to_date, self.company)
diff --git a/erpnext/hr/doctype/leave_period/leave_period_dashboard.py b/erpnext/hr/doctype/leave_period/leave_period_dashboard.py
index 1adae0f..854f988 100644
--- a/erpnext/hr/doctype/leave_period/leave_period_dashboard.py
+++ b/erpnext/hr/doctype/leave_period/leave_period_dashboard.py
@@ -3,11 +3,6 @@
 
 def get_data():
 	return {
-		'fieldname': 'leave_period',
-		'transactions': [
-			{
-				'label': _('Transactions'),
-				'items': ['Leave Allocation']
-			}
-		]
+		"fieldname": "leave_period",
+		"transactions": [{"label": _("Transactions"), "items": ["Leave Allocation"]}],
 	}
diff --git a/erpnext/hr/doctype/leave_period/test_leave_period.py b/erpnext/hr/doctype/leave_period/test_leave_period.py
index 10936dd..0923574 100644
--- a/erpnext/hr/doctype/leave_period/test_leave_period.py
+++ b/erpnext/hr/doctype/leave_period/test_leave_period.py
@@ -9,23 +9,32 @@
 
 test_dependencies = ["Employee", "Leave Type", "Leave Policy"]
 
+
 class TestLeavePeriod(unittest.TestCase):
 	pass
 
+
 def create_leave_period(from_date, to_date, company=None):
-	leave_period = frappe.db.get_value('Leave Period',
-		dict(company=company or erpnext.get_default_company(),
+	leave_period = frappe.db.get_value(
+		"Leave Period",
+		dict(
+			company=company or erpnext.get_default_company(),
 			from_date=from_date,
 			to_date=to_date,
-			is_active=1), 'name')
+			is_active=1,
+		),
+		"name",
+	)
 	if leave_period:
 		return frappe.get_doc("Leave Period", leave_period)
 
-	leave_period = frappe.get_doc({
-		"doctype": "Leave Period",
-		"company": company or erpnext.get_default_company(),
-		"from_date": from_date,
-		"to_date": to_date,
-		"is_active": 1
-	}).insert()
+	leave_period = frappe.get_doc(
+		{
+			"doctype": "Leave Period",
+			"company": company or erpnext.get_default_company(),
+			"from_date": from_date,
+			"to_date": to_date,
+			"is_active": 1,
+		}
+	).insert()
 	return leave_period
diff --git a/erpnext/hr/doctype/leave_policy/leave_policy.py b/erpnext/hr/doctype/leave_policy/leave_policy.py
index 80450d5..33c9493 100644
--- a/erpnext/hr/doctype/leave_policy/leave_policy.py
+++ b/erpnext/hr/doctype/leave_policy/leave_policy.py
@@ -11,6 +11,12 @@
 	def validate(self):
 		if self.leave_policy_details:
 			for lp_detail in self.leave_policy_details:
-				max_leaves_allowed = frappe.db.get_value("Leave Type", lp_detail.leave_type, "max_leaves_allowed")
+				max_leaves_allowed = frappe.db.get_value(
+					"Leave Type", lp_detail.leave_type, "max_leaves_allowed"
+				)
 				if max_leaves_allowed > 0 and lp_detail.annual_allocation > max_leaves_allowed:
-					frappe.throw(_("Maximum leave allowed in the leave type {0} is {1}").format(lp_detail.leave_type, max_leaves_allowed))
+					frappe.throw(
+						_("Maximum leave allowed in the leave type {0} is {1}").format(
+							lp_detail.leave_type, max_leaves_allowed
+						)
+					)
diff --git a/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py b/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py
index 73782d6..57ea93e 100644
--- a/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py
+++ b/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py
@@ -3,11 +3,8 @@
 
 def get_data():
 	return {
-		'fieldname':  'leave_policy',
-		'transactions': [
-			{
-				'label': _('Leaves'),
-				'items': ['Leave Policy Assignment', 'Leave Allocation']
-			},
-		]
+		"fieldname": "leave_policy",
+		"transactions": [
+			{"label": _("Leaves"), "items": ["Leave Policy Assignment", "Leave Allocation"]},
+		],
 	}
diff --git a/erpnext/hr/doctype/leave_policy/test_leave_policy.py b/erpnext/hr/doctype/leave_policy/test_leave_policy.py
index a4b8af7..33d5508 100644
--- a/erpnext/hr/doctype/leave_policy/test_leave_policy.py
+++ b/erpnext/hr/doctype/leave_policy/test_leave_policy.py
@@ -15,18 +15,25 @@
 			leave_type.max_leaves_allowed = 2
 			leave_type.save()
 
-		leave_policy = create_leave_policy(leave_type=leave_type.name, annual_allocation=leave_type.max_leaves_allowed + 1)
+		leave_policy = create_leave_policy(
+			leave_type=leave_type.name, annual_allocation=leave_type.max_leaves_allowed + 1
+		)
 
 		self.assertRaises(frappe.ValidationError, leave_policy.insert)
 
+
 def create_leave_policy(**args):
-	''' Returns an object of leave policy '''
+	"""Returns an object of leave policy"""
 	args = frappe._dict(args)
-	return frappe.get_doc({
-		"doctype": "Leave Policy",
-		"title": "Test Leave Policy",
-		"leave_policy_details": [{
-			"leave_type": args.leave_type or "_Test Leave Type",
-			"annual_allocation": args.annual_allocation or 10
-		}]
-	})
+	return frappe.get_doc(
+		{
+			"doctype": "Leave Policy",
+			"title": "Test Leave Policy",
+			"leave_policy_details": [
+				{
+					"leave_type": args.leave_type or "_Test Leave Type",
+					"annual_allocation": args.annual_allocation or 10,
+				}
+			],
+		}
+	)
diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py
index c11a821..2ed86f3 100644
--- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py
+++ b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py
@@ -8,35 +8,62 @@
 import frappe
 from frappe import _, bold
 from frappe.model.document import Document
-from frappe.utils import date_diff, flt, formatdate, get_last_day, getdate
+from frappe.utils import date_diff, flt, formatdate, get_last_day, get_link_to_form, getdate
 
 
 class LeavePolicyAssignment(Document):
 	def validate(self):
-		self.validate_policy_assignment_overlap()
 		self.set_dates()
+		self.validate_policy_assignment_overlap()
+		self.warn_about_carry_forwarding()
 
 	def on_submit(self):
 		self.grant_leave_alloc_for_employee()
 
 	def set_dates(self):
 		if self.assignment_based_on == "Leave Period":
-			self.effective_from, self.effective_to = frappe.db.get_value("Leave Period", self.leave_period, ["from_date", "to_date"])
+			self.effective_from, self.effective_to = frappe.db.get_value(
+				"Leave Period", self.leave_period, ["from_date", "to_date"]
+			)
 		elif self.assignment_based_on == "Joining Date":
 			self.effective_from = frappe.db.get_value("Employee", self.employee, "date_of_joining")
 
 	def validate_policy_assignment_overlap(self):
-		leave_policy_assignments = frappe.get_all("Leave Policy Assignment", filters = {
-			"employee": self.employee,
-			"name": ("!=", self.name),
-			"docstatus": 1,
-			"effective_to": (">=", self.effective_from),
-			"effective_from": ("<=", self.effective_to)
-		})
+		leave_policy_assignments = frappe.get_all(
+			"Leave Policy Assignment",
+			filters={
+				"employee": self.employee,
+				"name": ("!=", self.name),
+				"docstatus": 1,
+				"effective_to": (">=", self.effective_from),
+				"effective_from": ("<=", self.effective_to),
+			},
+		)
 
 		if len(leave_policy_assignments):
-			frappe.throw(_("Leave Policy: {0} already assigned for Employee {1} for period {2} to {3}")
-				.format(bold(self.leave_policy), bold(self.employee), bold(formatdate(self.effective_from)), bold(formatdate(self.effective_to))))
+			frappe.throw(
+				_("Leave Policy: {0} already assigned for Employee {1} for period {2} to {3}").format(
+					bold(self.leave_policy),
+					bold(self.employee),
+					bold(formatdate(self.effective_from)),
+					bold(formatdate(self.effective_to)),
+				)
+			)
+
+	def warn_about_carry_forwarding(self):
+		if not self.carry_forward:
+			return
+
+		leave_types = get_leave_type_details()
+		leave_policy = frappe.get_doc("Leave Policy", self.leave_policy)
+
+		for policy in leave_policy.leave_policy_details:
+			leave_type = leave_types.get(policy.leave_type)
+			if not leave_type.is_carry_forward:
+				msg = _(
+					"Leaves for the Leave Type {0} won't be carry-forwarded since carry-forwarding is disabled."
+				).format(frappe.bold(get_link_to_form("Leave Type", leave_type.name)))
+				frappe.msgprint(msg, indicator="orange", alert=True)
 
 	@frappe.whitelist()
 	def grant_leave_alloc_for_employee(self):
@@ -52,41 +79,54 @@
 			for leave_policy_detail in leave_policy.leave_policy_details:
 				if not leave_type_details.get(leave_policy_detail.leave_type).is_lwp:
 					leave_allocation, new_leaves_allocated = self.create_leave_allocation(
-						leave_policy_detail.leave_type, leave_policy_detail.annual_allocation,
-						leave_type_details, date_of_joining
+						leave_policy_detail.leave_type,
+						leave_policy_detail.annual_allocation,
+						leave_type_details,
+						date_of_joining,
 					)
-					leave_allocations[leave_policy_detail.leave_type] = {"name": leave_allocation, "leaves": new_leaves_allocated}
+					leave_allocations[leave_policy_detail.leave_type] = {
+						"name": leave_allocation,
+						"leaves": new_leaves_allocated,
+					}
 			self.db_set("leaves_allocated", 1)
 			return leave_allocations
 
-	def create_leave_allocation(self, leave_type, new_leaves_allocated, leave_type_details, date_of_joining):
+	def create_leave_allocation(
+		self, leave_type, new_leaves_allocated, leave_type_details, date_of_joining
+	):
 		# Creates leave allocation for the given employee in the provided leave period
 		carry_forward = self.carry_forward
 		if self.carry_forward and not leave_type_details.get(leave_type).is_carry_forward:
 			carry_forward = 0
 
-		new_leaves_allocated = self.get_new_leaves(leave_type, new_leaves_allocated,
-			leave_type_details, date_of_joining)
+		new_leaves_allocated = self.get_new_leaves(
+			leave_type, new_leaves_allocated, leave_type_details, date_of_joining
+		)
 
-		allocation = frappe.get_doc(dict(
-			doctype="Leave Allocation",
-			employee=self.employee,
-			leave_type=leave_type,
-			from_date=self.effective_from,
-			to_date=self.effective_to,
-			new_leaves_allocated=new_leaves_allocated,
-			leave_period=self.leave_period if self.assignment_based_on == "Leave Policy" else '',
-			leave_policy_assignment = self.name,
-			leave_policy = self.leave_policy,
-			carry_forward=carry_forward
-			))
-		allocation.save(ignore_permissions = True)
+		allocation = frappe.get_doc(
+			dict(
+				doctype="Leave Allocation",
+				employee=self.employee,
+				leave_type=leave_type,
+				from_date=self.effective_from,
+				to_date=self.effective_to,
+				new_leaves_allocated=new_leaves_allocated,
+				leave_period=self.leave_period if self.assignment_based_on == "Leave Policy" else "",
+				leave_policy_assignment=self.name,
+				leave_policy=self.leave_policy,
+				carry_forward=carry_forward,
+			)
+		)
+		allocation.save(ignore_permissions=True)
 		allocation.submit()
 		return allocation.name, new_leaves_allocated
 
 	def get_new_leaves(self, leave_type, new_leaves_allocated, leave_type_details, date_of_joining):
 		from frappe.model.meta import get_field_precision
-		precision = get_field_precision(frappe.get_meta("Leave Allocation").get_field("new_leaves_allocated"))
+
+		precision = get_field_precision(
+			frappe.get_meta("Leave Allocation").get_field("new_leaves_allocated")
+		)
 
 		# Earned Leaves and Compensatory Leaves are allocated by scheduler, initially allocate 0
 		if leave_type_details.get(leave_type).is_compensatory == 1:
@@ -97,16 +137,22 @@
 				new_leaves_allocated = 0
 			else:
 				# get leaves for past months if assignment is based on Leave Period / Joining Date
-				new_leaves_allocated = self.get_leaves_for_passed_months(leave_type, new_leaves_allocated, leave_type_details, date_of_joining)
+				new_leaves_allocated = self.get_leaves_for_passed_months(
+					leave_type, new_leaves_allocated, leave_type_details, date_of_joining
+				)
 
 		# Calculate leaves at pro-rata basis for employees joining after the beginning of the given leave period
 		elif getdate(date_of_joining) > getdate(self.effective_from):
-			remaining_period = ((date_diff(self.effective_to, date_of_joining) + 1) / (date_diff(self.effective_to, self.effective_from) + 1))
+			remaining_period = (date_diff(self.effective_to, date_of_joining) + 1) / (
+				date_diff(self.effective_to, self.effective_from) + 1
+			)
 			new_leaves_allocated = ceil(new_leaves_allocated * remaining_period)
 
 		return flt(new_leaves_allocated, precision)
 
-	def get_leaves_for_passed_months(self, leave_type, new_leaves_allocated, leave_type_details, date_of_joining):
+	def get_leaves_for_passed_months(
+		self, leave_type, new_leaves_allocated, leave_type_details, date_of_joining
+	):
 		from erpnext.hr.utils import get_monthly_earned_leave
 
 		current_date = frappe.flags.current_date or getdate()
@@ -129,8 +175,11 @@
 			months_passed = add_current_month_if_applicable(months_passed, date_of_joining, based_on_doj)
 
 		if months_passed > 0:
-			monthly_earned_leave = get_monthly_earned_leave(new_leaves_allocated,
-				leave_type_details.get(leave_type).earned_leave_frequency, leave_type_details.get(leave_type).rounding)
+			monthly_earned_leave = get_monthly_earned_leave(
+				new_leaves_allocated,
+				leave_type_details.get(leave_type).earned_leave_frequency,
+				leave_type_details.get(leave_type).rounding,
+			)
 			new_leaves_allocated = monthly_earned_leave * months_passed
 		else:
 			new_leaves_allocated = 0
@@ -159,7 +208,7 @@
 def create_assignment_for_multiple_employees(employees, data):
 
 	if isinstance(employees, str):
-		employees= json.loads(employees)
+		employees = json.loads(employees)
 
 	if isinstance(data, str):
 		data = frappe._dict(json.loads(data))
@@ -186,11 +235,23 @@
 
 	return docs_name
 
+
 def get_leave_type_details():
 	leave_type_details = frappe._dict()
-	leave_types = frappe.get_all("Leave Type",
-		fields=["name", "is_lwp", "is_earned_leave", "is_compensatory", "based_on_date_of_joining",
-			"is_carry_forward", "expire_carry_forwarded_leaves_after_days", "earned_leave_frequency", "rounding"])
+	leave_types = frappe.get_all(
+		"Leave Type",
+		fields=[
+			"name",
+			"is_lwp",
+			"is_earned_leave",
+			"is_compensatory",
+			"based_on_date_of_joining",
+			"is_carry_forward",
+			"expire_carry_forwarded_leaves_after_days",
+			"earned_leave_frequency",
+			"rounding",
+		],
+	)
 	for d in leave_types:
 		leave_type_details.setdefault(d.name, d)
 	return leave_type_details
diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_dashboard.py b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_dashboard.py
index 4363439..13b39c7 100644
--- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_dashboard.py
+++ b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_dashboard.py
@@ -3,11 +3,8 @@
 
 def get_data():
 	return {
-		'fieldname':  'leave_policy_assignment',
-		'transactions': [
-			{
-				'label': _('Leaves'),
-				'items': ['Leave Allocation']
-			},
-		]
+		"fieldname": "leave_policy_assignment",
+		"transactions": [
+			{"label": _("Leaves"), "items": ["Leave Allocation"]},
+		],
 	}
diff --git a/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py b/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py
index a19ddce..031ed0e 100644
--- a/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py
+++ b/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py
@@ -4,7 +4,8 @@
 import unittest
 
 import frappe
-from frappe.utils import add_months, get_first_day, get_last_day, getdate
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import add_days, add_months, get_first_day, get_last_day, getdate
 
 from erpnext.hr.doctype.leave_application.test_leave_application import (
 	get_employee,
@@ -17,9 +18,16 @@
 
 test_dependencies = ["Employee"]
 
-class TestLeavePolicyAssignment(unittest.TestCase):
+
+class TestLeavePolicyAssignment(FrappeTestCase):
 	def setUp(self):
-		for doctype in ["Leave Period", "Leave Application", "Leave Allocation", "Leave Policy Assignment", "Leave Ledger Entry"]:
+		for doctype in [
+			"Leave Period",
+			"Leave Application",
+			"Leave Allocation",
+			"Leave Policy Assignment",
+			"Leave Ledger Entry",
+		]:
 			frappe.db.delete(doctype)
 
 		employee = get_employee()
@@ -32,19 +40,31 @@
 		leave_policy = create_leave_policy()
 		leave_policy.submit()
 
+		self.employee.date_of_joining = get_first_day(leave_period.from_date)
+		self.employee.save()
+
 		data = {
 			"assignment_based_on": "Leave Period",
 			"leave_policy": leave_policy.name,
-			"leave_period": leave_period.name
+			"leave_period": leave_period.name,
 		}
-		leave_policy_assignments = create_assignment_for_multiple_employees([self.employee.name], frappe._dict(data))
-		self.assertEqual(frappe.db.get_value("Leave Policy Assignment", leave_policy_assignments[0], "leaves_allocated"), 1)
+		leave_policy_assignments = create_assignment_for_multiple_employees(
+			[self.employee.name], frappe._dict(data)
+		)
+		self.assertEqual(
+			frappe.db.get_value("Leave Policy Assignment", leave_policy_assignments[0], "leaves_allocated"),
+			1,
+		)
 
-		leave_allocation = frappe.get_list("Leave Allocation", filters={
-			"employee": self.employee.name,
-			"leave_policy":leave_policy.name,
-			"leave_policy_assignment": leave_policy_assignments[0],
-			"docstatus": 1})[0]
+		leave_allocation = frappe.get_list(
+			"Leave Allocation",
+			filters={
+				"employee": self.employee.name,
+				"leave_policy": leave_policy.name,
+				"leave_policy_assignment": leave_policy_assignments[0],
+				"docstatus": 1,
+			},
+		)[0]
 		leave_alloc_doc = frappe.get_doc("Leave Allocation", leave_allocation)
 
 		self.assertEqual(leave_alloc_doc.new_leaves_allocated, 10)
@@ -63,65 +83,94 @@
 		data = {
 			"assignment_based_on": "Leave Period",
 			"leave_policy": leave_policy.name,
-			"leave_period": leave_period.name
+			"leave_period": leave_period.name,
 		}
-		leave_policy_assignments = create_assignment_for_multiple_employees([self.employee.name], frappe._dict(data))
+		leave_policy_assignments = create_assignment_for_multiple_employees(
+			[self.employee.name], frappe._dict(data)
+		)
 
 		# every leave is allocated no more leave can be granted now
-		self.assertEqual(frappe.db.get_value("Leave Policy Assignment", leave_policy_assignments[0], "leaves_allocated"), 1)
-		leave_allocation = frappe.get_list("Leave Allocation", filters={
-			"employee": self.employee.name,
-			"leave_policy":leave_policy.name,
-			"leave_policy_assignment": leave_policy_assignments[0],
-			"docstatus": 1})[0]
+		self.assertEqual(
+			frappe.db.get_value("Leave Policy Assignment", leave_policy_assignments[0], "leaves_allocated"),
+			1,
+		)
+		leave_allocation = frappe.get_list(
+			"Leave Allocation",
+			filters={
+				"employee": self.employee.name,
+				"leave_policy": leave_policy.name,
+				"leave_policy_assignment": leave_policy_assignments[0],
+				"docstatus": 1,
+			},
+		)[0]
 
 		leave_alloc_doc = frappe.get_doc("Leave Allocation", leave_allocation)
 		leave_alloc_doc.cancel()
 		leave_alloc_doc.delete()
-		self.assertEqual(frappe.db.get_value("Leave Policy Assignment", leave_policy_assignments[0], "leaves_allocated"), 0)
+		self.assertEqual(
+			frappe.db.get_value("Leave Policy Assignment", leave_policy_assignments[0], "leaves_allocated"),
+			0,
+		)
 
 	def test_earned_leave_allocation(self):
 		leave_period = create_leave_period("Test Earned Leave Period")
 		leave_type = create_earned_leave_type("Test Earned Leave")
 
-		leave_policy = frappe.get_doc({
-			"doctype": "Leave Policy",
-			"title": "Test Leave Policy",
-			"leave_policy_details": [{"leave_type": leave_type.name, "annual_allocation": 6}]
-		}).submit()
+		leave_policy = frappe.get_doc(
+			{
+				"doctype": "Leave Policy",
+				"title": "Test Leave Policy",
+				"leave_policy_details": [{"leave_type": leave_type.name, "annual_allocation": 6}],
+			}
+		).submit()
 
 		data = {
 			"assignment_based_on": "Leave Period",
 			"leave_policy": leave_policy.name,
-			"leave_period": leave_period.name
+			"leave_period": leave_period.name,
 		}
-		leave_policy_assignments = create_assignment_for_multiple_employees([self.employee.name], frappe._dict(data))
 
+		# second last day of the month
 		# leaves allocated should be 0 since it is an earned leave and allocation happens via scheduler based on set frequency
-		leaves_allocated = frappe.db.get_value("Leave Allocation", {
-			"leave_policy_assignment": leave_policy_assignments[0]
-		}, "total_leaves_allocated")
+		frappe.flags.current_date = add_days(get_last_day(getdate()), -1)
+		leave_policy_assignments = create_assignment_for_multiple_employees(
+			[self.employee.name], frappe._dict(data)
+		)
+
+		leaves_allocated = frappe.db.get_value(
+			"Leave Allocation",
+			{"leave_policy_assignment": leave_policy_assignments[0]},
+			"total_leaves_allocated",
+		)
 		self.assertEqual(leaves_allocated, 0)
 
 	def test_earned_leave_alloc_for_passed_months_based_on_leave_period(self):
-		leave_period, leave_policy = setup_leave_period_and_policy(get_first_day(add_months(getdate(), -1)))
+		leave_period, leave_policy = setup_leave_period_and_policy(
+			get_first_day(add_months(getdate(), -1))
+		)
 
 		# Case 1: assignment created one month after the leave period, should allocate 1 leave
 		frappe.flags.current_date = get_first_day(getdate())
 		data = {
 			"assignment_based_on": "Leave Period",
 			"leave_policy": leave_policy.name,
-			"leave_period": leave_period.name
+			"leave_period": leave_period.name,
 		}
-		leave_policy_assignments = create_assignment_for_multiple_employees([self.employee.name], frappe._dict(data))
+		leave_policy_assignments = create_assignment_for_multiple_employees(
+			[self.employee.name], frappe._dict(data)
+		)
 
-		leaves_allocated = frappe.db.get_value("Leave Allocation", {
-			"leave_policy_assignment": leave_policy_assignments[0]
-		}, "total_leaves_allocated")
+		leaves_allocated = frappe.db.get_value(
+			"Leave Allocation",
+			{"leave_policy_assignment": leave_policy_assignments[0]},
+			"total_leaves_allocated",
+		)
 		self.assertEqual(leaves_allocated, 1)
 
 	def test_earned_leave_alloc_for_passed_months_on_month_end_based_on_leave_period(self):
-		leave_period, leave_policy = setup_leave_period_and_policy(get_first_day(add_months(getdate(), -2)))
+		leave_period, leave_policy = setup_leave_period_and_policy(
+			get_first_day(add_months(getdate(), -2))
+		)
 		# Case 2: assignment created on the last day of the leave period's latter month
 		# should allocate 1 leave for current month even though the month has not ended
 		# since the daily job might have already executed
@@ -130,32 +179,35 @@
 		data = {
 			"assignment_based_on": "Leave Period",
 			"leave_policy": leave_policy.name,
-			"leave_period": leave_period.name
+			"leave_period": leave_period.name,
 		}
-		leave_policy_assignments = create_assignment_for_multiple_employees([self.employee.name], frappe._dict(data))
+		leave_policy_assignments = create_assignment_for_multiple_employees(
+			[self.employee.name], frappe._dict(data)
+		)
 
-		leaves_allocated = frappe.db.get_value("Leave Allocation", {
-			"leave_policy_assignment": leave_policy_assignments[0]
-		}, "total_leaves_allocated")
-		self.assertEqual(leaves_allocated, 3)
-
-		# if the daily job is not completed yet, there is another check present
-		# to ensure leave is not already allocated to avoid duplication
-		from erpnext.hr.utils import allocate_earned_leaves
-		allocate_earned_leaves()
-
-		leaves_allocated = frappe.db.get_value("Leave Allocation", {
-			"leave_policy_assignment": leave_policy_assignments[0]
-		}, "total_leaves_allocated")
+		leaves_allocated = frappe.db.get_value(
+			"Leave Allocation",
+			{"leave_policy_assignment": leave_policy_assignments[0]},
+			"total_leaves_allocated",
+		)
 		self.assertEqual(leaves_allocated, 3)
 
 	def test_earned_leave_alloc_for_passed_months_with_cf_leaves_based_on_leave_period(self):
 		from erpnext.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation
 
-		leave_period, leave_policy = setup_leave_period_and_policy(get_first_day(add_months(getdate(), -2)))
+		leave_period, leave_policy = setup_leave_period_and_policy(
+			get_first_day(add_months(getdate(), -2))
+		)
 		# initial leave allocation = 5
-		leave_allocation = create_leave_allocation(employee=self.employee.name, employee_name=self.employee.employee_name, leave_type="Test Earned Leave",
-			from_date=add_months(getdate(), -12), to_date=add_months(getdate(), -3), new_leaves_allocated=5, carry_forward=0)
+		leave_allocation = create_leave_allocation(
+			employee=self.employee.name,
+			employee_name=self.employee.employee_name,
+			leave_type="Test Earned Leave",
+			from_date=add_months(getdate(), -12),
+			to_date=add_months(getdate(), -3),
+			new_leaves_allocated=5,
+			carry_forward=0,
+		)
 		leave_allocation.submit()
 
 		# Case 3: assignment created on the last day of the leave period's latter month with carry forwarding
@@ -164,35 +216,33 @@
 			"assignment_based_on": "Leave Period",
 			"leave_policy": leave_policy.name,
 			"leave_period": leave_period.name,
-			"carry_forward": 1
+			"carry_forward": 1,
 		}
 		# carry forwarded leaves = 5, 3 leaves allocated for passed months
-		leave_policy_assignments = create_assignment_for_multiple_employees([self.employee.name], frappe._dict(data))
+		leave_policy_assignments = create_assignment_for_multiple_employees(
+			[self.employee.name], frappe._dict(data)
+		)
 
-		details = frappe.db.get_value("Leave Allocation", {
-			"leave_policy_assignment": leave_policy_assignments[0]
-		}, ["total_leaves_allocated", "new_leaves_allocated", "unused_leaves", "name"], as_dict=True)
+		details = frappe.db.get_value(
+			"Leave Allocation",
+			{"leave_policy_assignment": leave_policy_assignments[0]},
+			["total_leaves_allocated", "new_leaves_allocated", "unused_leaves", "name"],
+			as_dict=True,
+		)
 		self.assertEqual(details.new_leaves_allocated, 2)
 		self.assertEqual(details.unused_leaves, 5)
 		self.assertEqual(details.total_leaves_allocated, 7)
 
-		# if the daily job is not completed yet, there is another check present
-		# to ensure leave is not already allocated to avoid duplication
-		from erpnext.hr.utils import is_earned_leave_already_allocated
-		frappe.flags.current_date = get_last_day(getdate())
-
-		allocation = frappe.get_doc("Leave Allocation", details.name)
-		# 1 leave is still pending to be allocated, irrespective of carry forwarded leaves
-		self.assertFalse(is_earned_leave_already_allocated(allocation, leave_policy.leave_policy_details[0].annual_allocation))
-
 	def test_earned_leave_alloc_for_passed_months_based_on_joining_date(self):
 		# tests leave alloc for earned leaves for assignment based on joining date in policy assignment
 		leave_type = create_earned_leave_type("Test Earned Leave")
-		leave_policy = frappe.get_doc({
-			"doctype": "Leave Policy",
-			"title": "Test Leave Policy",
-			"leave_policy_details": [{"leave_type": leave_type.name, "annual_allocation": 12}]
-		}).submit()
+		leave_policy = frappe.get_doc(
+			{
+				"doctype": "Leave Policy",
+				"title": "Test Leave Policy",
+				"leave_policy_details": [{"leave_type": leave_type.name, "annual_allocation": 12}],
+			}
+		).submit()
 
 		# joining date set to 2 months back
 		self.employee.date_of_joining = get_first_day(add_months(getdate(), -2))
@@ -200,29 +250,26 @@
 
 		# assignment created on the last day of the current month
 		frappe.flags.current_date = get_last_day(getdate())
-		data = {
-			"assignment_based_on": "Joining Date",
-			"leave_policy": leave_policy.name
-		}
-		leave_policy_assignments = create_assignment_for_multiple_employees([self.employee.name], frappe._dict(data))
-		leaves_allocated = frappe.db.get_value("Leave Allocation", {"leave_policy_assignment": leave_policy_assignments[0]},
-			"total_leaves_allocated")
-		effective_from = frappe.db.get_value("Leave Policy Assignment", leave_policy_assignments[0], "effective_from")
+		data = {"assignment_based_on": "Joining Date", "leave_policy": leave_policy.name}
+		leave_policy_assignments = create_assignment_for_multiple_employees(
+			[self.employee.name], frappe._dict(data)
+		)
+		leaves_allocated = frappe.db.get_value(
+			"Leave Allocation",
+			{"leave_policy_assignment": leave_policy_assignments[0]},
+			"total_leaves_allocated",
+		)
+		effective_from = frappe.db.get_value(
+			"Leave Policy Assignment", leave_policy_assignments[0], "effective_from"
+		)
 		self.assertEqual(effective_from, self.employee.date_of_joining)
 		self.assertEqual(leaves_allocated, 3)
 
-		# to ensure leave is not already allocated to avoid duplication
-		from erpnext.hr.utils import allocate_earned_leaves
-		frappe.flags.current_date = get_last_day(getdate())
-		allocate_earned_leaves()
-
-		leaves_allocated = frappe.db.get_value("Leave Allocation", {"leave_policy_assignment": leave_policy_assignments[0]},
-			"total_leaves_allocated")
-		self.assertEqual(leaves_allocated, 3)
-
 	def test_grant_leaves_on_doj_for_earned_leaves_based_on_leave_period(self):
 		# tests leave alloc based on leave period for earned leaves with "based on doj" configuration in leave type
-		leave_period, leave_policy = setup_leave_period_and_policy(get_first_day(add_months(getdate(), -2)), based_on_doj=True)
+		leave_period, leave_policy = setup_leave_period_and_policy(
+			get_first_day(add_months(getdate(), -2)), based_on_doj=True
+		)
 
 		# joining date set to 2 months back
 		self.employee.date_of_joining = get_first_day(add_months(getdate(), -2))
@@ -234,34 +281,29 @@
 		data = {
 			"assignment_based_on": "Leave Period",
 			"leave_policy": leave_policy.name,
-			"leave_period": leave_period.name
+			"leave_period": leave_period.name,
 		}
-		leave_policy_assignments = create_assignment_for_multiple_employees([self.employee.name], frappe._dict(data))
+		leave_policy_assignments = create_assignment_for_multiple_employees(
+			[self.employee.name], frappe._dict(data)
+		)
 
-		leaves_allocated = frappe.db.get_value("Leave Allocation", {
-			"leave_policy_assignment": leave_policy_assignments[0]
-		}, "total_leaves_allocated")
-		self.assertEqual(leaves_allocated, 3)
-
-		# if the daily job is not completed yet, there is another check present
-		# to ensure leave is not already allocated to avoid duplication
-		from erpnext.hr.utils import allocate_earned_leaves
-		frappe.flags.current_date = get_first_day(getdate())
-		allocate_earned_leaves()
-
-		leaves_allocated = frappe.db.get_value("Leave Allocation", {
-			"leave_policy_assignment": leave_policy_assignments[0]
-		}, "total_leaves_allocated")
+		leaves_allocated = frappe.db.get_value(
+			"Leave Allocation",
+			{"leave_policy_assignment": leave_policy_assignments[0]},
+			"total_leaves_allocated",
+		)
 		self.assertEqual(leaves_allocated, 3)
 
 	def test_grant_leaves_on_doj_for_earned_leaves_based_on_joining_date(self):
 		# tests leave alloc based on joining date for earned leaves with "based on doj" configuration in leave type
 		leave_type = create_earned_leave_type("Test Earned Leave", based_on_doj=True)
-		leave_policy = frappe.get_doc({
-			"doctype": "Leave Policy",
-			"title": "Test Leave Policy",
-			"leave_policy_details": [{"leave_type": leave_type.name, "annual_allocation": 12}]
-		}).submit()
+		leave_policy = frappe.get_doc(
+			{
+				"doctype": "Leave Policy",
+				"title": "Test Leave Policy",
+				"leave_policy_details": [{"leave_type": leave_type.name, "annual_allocation": 12}],
+			}
+		).submit()
 
 		# joining date set to 2 months back
 		# leave should be allocated for current month too since this day is same as the joining day
@@ -270,28 +312,22 @@
 
 		# assignment created on the first day of the current month
 		frappe.flags.current_date = get_first_day(getdate())
-		data = {
-			"assignment_based_on": "Joining Date",
-			"leave_policy": leave_policy.name
-		}
-		leave_policy_assignments = create_assignment_for_multiple_employees([self.employee.name], frappe._dict(data))
-		leaves_allocated = frappe.db.get_value("Leave Allocation", {"leave_policy_assignment": leave_policy_assignments[0]},
-			"total_leaves_allocated")
-		effective_from = frappe.db.get_value("Leave Policy Assignment", leave_policy_assignments[0], "effective_from")
+		data = {"assignment_based_on": "Joining Date", "leave_policy": leave_policy.name}
+		leave_policy_assignments = create_assignment_for_multiple_employees(
+			[self.employee.name], frappe._dict(data)
+		)
+		leaves_allocated = frappe.db.get_value(
+			"Leave Allocation",
+			{"leave_policy_assignment": leave_policy_assignments[0]},
+			"total_leaves_allocated",
+		)
+		effective_from = frappe.db.get_value(
+			"Leave Policy Assignment", leave_policy_assignments[0], "effective_from"
+		)
 		self.assertEqual(effective_from, self.employee.date_of_joining)
 		self.assertEqual(leaves_allocated, 3)
 
-		# to ensure leave is not already allocated to avoid duplication
-		from erpnext.hr.utils import allocate_earned_leaves
-		frappe.flags.current_date = get_first_day(getdate())
-		allocate_earned_leaves()
-
-		leaves_allocated = frappe.db.get_value("Leave Allocation", {"leave_policy_assignment": leave_policy_assignments[0]},
-			"total_leaves_allocated")
-		self.assertEqual(leaves_allocated, 3)
-
 	def tearDown(self):
-		frappe.db.rollback()
 		frappe.db.set_value("Employee", self.employee.name, "date_of_joining", self.original_doj)
 		frappe.flags.current_date = None
 
@@ -299,15 +335,17 @@
 def create_earned_leave_type(leave_type, based_on_doj=False):
 	frappe.delete_doc_if_exists("Leave Type", leave_type, force=1)
 
-	return frappe.get_doc(dict(
-		leave_type_name=leave_type,
-		doctype="Leave Type",
-		is_earned_leave=1,
-		earned_leave_frequency="Monthly",
-		rounding=0.5,
-		is_carry_forward=1,
-		based_on_date_of_joining=based_on_doj
-	)).insert()
+	return frappe.get_doc(
+		dict(
+			leave_type_name=leave_type,
+			doctype="Leave Type",
+			is_earned_leave=1,
+			earned_leave_frequency="Monthly",
+			rounding=0.5,
+			is_carry_forward=1,
+			based_on_date_of_joining=based_on_doj,
+		)
+	).insert()
 
 
 def create_leave_period(name, start_date=None):
@@ -315,24 +353,27 @@
 	if not start_date:
 		start_date = get_first_day(getdate())
 
-	return frappe.get_doc(dict(
-		name=name,
-		doctype="Leave Period",
-		from_date=start_date,
-		to_date=add_months(start_date, 12),
-		company="_Test Company",
-		is_active=1
-	)).insert()
+	return frappe.get_doc(
+		dict(
+			name=name,
+			doctype="Leave Period",
+			from_date=start_date,
+			to_date=add_months(start_date, 12),
+			company="_Test Company",
+			is_active=1,
+		)
+	).insert()
 
 
 def setup_leave_period_and_policy(start_date, based_on_doj=False):
 	leave_type = create_earned_leave_type("Test Earned Leave", based_on_doj)
-	leave_period = create_leave_period("Test Earned Leave Period",
-		start_date=start_date)
-	leave_policy = frappe.get_doc({
-		"doctype": "Leave Policy",
-		"title": "Test Leave Policy",
-		"leave_policy_details": [{"leave_type": leave_type.name, "annual_allocation": 12}]
-	}).insert()
+	leave_period = create_leave_period("Test Earned Leave Period", start_date=start_date)
+	leave_policy = frappe.get_doc(
+		{
+			"doctype": "Leave Policy",
+			"title": "Test Leave Policy",
+			"leave_policy_details": [{"leave_type": leave_type.name, "annual_allocation": 12}],
+		}
+	).insert()
 
-	return leave_period, leave_policy
\ No newline at end of file
+	return leave_period, leave_policy
diff --git a/erpnext/hr/doctype/leave_type/leave_type.json b/erpnext/hr/doctype/leave_type/leave_type.json
index 06ca4cd..d40ff09 100644
--- a/erpnext/hr/doctype/leave_type/leave_type.json
+++ b/erpnext/hr/doctype/leave_type/leave_type.json
@@ -19,6 +19,7 @@
   "fraction_of_daily_salary_per_leave",
   "is_optional_leave",
   "allow_negative",
+  "allow_over_allocation",
   "include_holiday",
   "is_compensatory",
   "carry_forward_section",
@@ -211,15 +212,23 @@
    "fieldtype": "Float",
    "label": "Fraction of Daily Salary per Leave",
    "mandatory_depends_on": "eval:doc.is_ppl == 1"
+  },
+  {
+   "default": "0",
+   "description": "Allows allocating more leaves than the number of days in the allocation period.",
+   "fieldname": "allow_over_allocation",
+   "fieldtype": "Check",
+   "label": "Allow Over Allocation"
   }
  ],
  "icon": "fa fa-flag",
  "idx": 1,
  "links": [],
- "modified": "2021-10-02 11:59:40.503359",
+ "modified": "2022-05-09 05:01:38.957545",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Leave Type",
+ "naming_rule": "By fieldname",
  "owner": "Administrator",
  "permissions": [
   {
@@ -251,5 +260,6 @@
  ],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/hr/doctype/leave_type/leave_type.py b/erpnext/hr/doctype/leave_type/leave_type.py
index 4b59c2c..82b9bd6 100644
--- a/erpnext/hr/doctype/leave_type/leave_type.py
+++ b/erpnext/hr/doctype/leave_type/leave_type.py
@@ -11,17 +11,23 @@
 class LeaveType(Document):
 	def validate(self):
 		if self.is_lwp:
-			leave_allocation = frappe.get_all("Leave Allocation", filters={
-				'leave_type': self.name,
-				'from_date': ("<=", today()),
-				'to_date': (">=", today())
-			}, fields=['name'])
-			leave_allocation = [l['name'] for l in leave_allocation]
+			leave_allocation = frappe.get_all(
+				"Leave Allocation",
+				filters={"leave_type": self.name, "from_date": ("<=", today()), "to_date": (">=", today())},
+				fields=["name"],
+			)
+			leave_allocation = [l["name"] for l in leave_allocation]
 			if leave_allocation:
-				frappe.throw(_('Leave application is linked with leave allocations {0}. Leave application cannot be set as leave without pay').format(", ".join(leave_allocation))) #nosec
+				frappe.throw(
+					_(
+						"Leave application is linked with leave allocations {0}. Leave application cannot be set as leave without pay"
+					).format(", ".join(leave_allocation))
+				)  # nosec
 
 		if self.is_lwp and self.is_ppl:
 			frappe.throw(_("Leave Type can be either without pay or partial pay"))
 
-		if self.is_ppl and (self.fraction_of_daily_salary_per_leave < 0 or  self.fraction_of_daily_salary_per_leave > 1):
+		if self.is_ppl and (
+			self.fraction_of_daily_salary_per_leave < 0 or self.fraction_of_daily_salary_per_leave > 1
+		):
 			frappe.throw(_("The fraction of Daily Salary per Leave should be between 0 and 1"))
diff --git a/erpnext/hr/doctype/leave_type/leave_type_dashboard.py b/erpnext/hr/doctype/leave_type/leave_type_dashboard.py
index 074d3e4..269a1ec 100644
--- a/erpnext/hr/doctype/leave_type/leave_type_dashboard.py
+++ b/erpnext/hr/doctype/leave_type/leave_type_dashboard.py
@@ -1,12 +1,10 @@
 def get_data():
 	return {
-		'fieldname': 'leave_type',
-		'transactions': [
+		"fieldname": "leave_type",
+		"transactions": [
 			{
-				'items': ['Leave Allocation', 'Leave Application'],
+				"items": ["Leave Allocation", "Leave Application"],
 			},
-			{
-				'items': ['Attendance', 'Leave Encashment']
-			}
-		]
+			{"items": ["Attendance", "Leave Encashment"]},
+		],
 	}
diff --git a/erpnext/hr/doctype/leave_type/test_leave_type.py b/erpnext/hr/doctype/leave_type/test_leave_type.py
index c1b64e9..69f9e12 100644
--- a/erpnext/hr/doctype/leave_type/test_leave_type.py
+++ b/erpnext/hr/doctype/leave_type/test_leave_type.py
@@ -3,27 +3,30 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Leave Type')
+test_records = frappe.get_test_records("Leave Type")
+
 
 def create_leave_type(**args):
-    args = frappe._dict(args)
-    if frappe.db.exists("Leave Type", args.leave_type_name):
-        return frappe.get_doc("Leave Type", args.leave_type_name)
-    leave_type = frappe.get_doc({
-        "doctype": "Leave Type",
-        "leave_type_name": args.leave_type_name or "_Test Leave Type",
-        "include_holiday": args.include_holidays or 1,
-        "allow_encashment": args.allow_encashment or 0,
-        "is_earned_leave": args.is_earned_leave or 0,
-        "is_lwp": args.is_lwp or 0,
-        "is_ppl":args.is_ppl or 0,
-        "is_carry_forward": args.is_carry_forward or 0,
-        "expire_carry_forwarded_leaves_after_days": args.expire_carry_forwarded_leaves_after_days or 0,
-        "encashment_threshold_days": args.encashment_threshold_days or 5,
-        "earning_component": "Leave Encashment"
-    })
+	args = frappe._dict(args)
+	if frappe.db.exists("Leave Type", args.leave_type_name):
+		return frappe.get_doc("Leave Type", args.leave_type_name)
+	leave_type = frappe.get_doc(
+		{
+			"doctype": "Leave Type",
+			"leave_type_name": args.leave_type_name or "_Test Leave Type",
+			"include_holiday": args.include_holidays or 1,
+			"allow_encashment": args.allow_encashment or 0,
+			"is_earned_leave": args.is_earned_leave or 0,
+			"is_lwp": args.is_lwp or 0,
+			"is_ppl": args.is_ppl or 0,
+			"is_carry_forward": args.is_carry_forward or 0,
+			"expire_carry_forwarded_leaves_after_days": args.expire_carry_forwarded_leaves_after_days or 0,
+			"encashment_threshold_days": args.encashment_threshold_days or 5,
+			"earning_component": "Leave Encashment",
+		}
+	)
 
-    if leave_type.is_ppl:
-        leave_type.fraction_of_daily_salary_per_leave = args.fraction_of_daily_salary_per_leave or 0.5
+	if leave_type.is_ppl:
+		leave_type.fraction_of_daily_salary_per_leave = args.fraction_of_daily_salary_per_leave or 0.5
 
-    return leave_type
+	return leave_type
diff --git a/erpnext/hr/doctype/offer_term/test_offer_term.py b/erpnext/hr/doctype/offer_term/test_offer_term.py
index 2e5ed75..2bea7b2 100644
--- a/erpnext/hr/doctype/offer_term/test_offer_term.py
+++ b/erpnext/hr/doctype/offer_term/test_offer_term.py
@@ -5,5 +5,6 @@
 
 # test_records = frappe.get_test_records('Offer Term')
 
+
 class TestOfferTerm(unittest.TestCase):
 	pass
diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.py b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
index 5177302..51298de 100644
--- a/erpnext/hr/doctype/shift_assignment/shift_assignment.py
+++ b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
@@ -3,105 +3,156 @@
 
 
 from datetime import datetime, timedelta
+from typing import Dict, List
 
 import frappe
 from frappe import _
 from frappe.model.document import Document
-from frappe.utils import cstr, getdate, now_datetime, nowdate
+from frappe.query_builder import Criterion
+from frappe.utils import cstr, get_datetime, get_link_to_form, get_time, getdate, now_datetime
 
 from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
 from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
 from erpnext.hr.utils import validate_active_employee
 
 
+class OverlappingShiftError(frappe.ValidationError):
+	pass
+
+
 class ShiftAssignment(Document):
 	def validate(self):
 		validate_active_employee(self.employee)
-		self.validate_overlapping_dates()
+		self.validate_overlapping_shifts()
 
 		if self.end_date:
-			self.validate_from_to_dates('start_date', 'end_date')
+			self.validate_from_to_dates("start_date", "end_date")
 
-	def validate_overlapping_dates(self):
+	def validate_overlapping_shifts(self):
+		overlapping_dates = self.get_overlapping_dates()
+		if len(overlapping_dates):
+			# if dates are overlapping, check if timings are overlapping, else allow
+			overlapping_timings = has_overlapping_timings(self.shift_type, overlapping_dates[0].shift_type)
+			if overlapping_timings:
+				self.throw_overlap_error(overlapping_dates[0])
+
+	def get_overlapping_dates(self):
 		if not self.name:
 			self.name = "New Shift Assignment"
 
-		condition = """and (
-				end_date is null
-				or
-					%(start_date)s between start_date and end_date
-		"""
+		shift = frappe.qb.DocType("Shift Assignment")
+		query = (
+			frappe.qb.from_(shift)
+			.select(shift.name, shift.shift_type, shift.docstatus, shift.status)
+			.where(
+				(shift.employee == self.employee)
+				& (shift.docstatus == 1)
+				& (shift.name != self.name)
+				& (shift.status == "Active")
+			)
+		)
 
 		if self.end_date:
-			condition  += """ or
-					%(end_date)s between start_date and end_date
-					or
-					start_date between %(start_date)s and %(end_date)s
-				) """
+			query = query.where(
+				Criterion.any(
+					[
+						Criterion.any(
+							[
+								shift.end_date.isnull(),
+								((self.start_date >= shift.start_date) & (self.start_date <= shift.end_date)),
+							]
+						),
+						Criterion.any(
+							[
+								((self.end_date >= shift.start_date) & (self.end_date <= shift.end_date)),
+								shift.start_date.between(self.start_date, self.end_date),
+							]
+						),
+					]
+				)
+			)
 		else:
-			condition += """ ) """
+			query = query.where(
+				shift.end_date.isnull()
+				| ((self.start_date >= shift.start_date) & (self.start_date <= shift.end_date))
+			)
 
-		assigned_shifts = frappe.db.sql("""
-			select name, shift_type, start_date ,end_date, docstatus, status
-			from `tabShift Assignment`
-			where
-				employee=%(employee)s and docstatus = 1
-				and name != %(name)s
-				and status = "Active"
-				{0}
-		""".format(condition), {
-			"employee": self.employee,
-			"shift_type": self.shift_type,
-			"start_date": self.start_date,
-			"end_date": self.end_date,
-			"name": self.name
-		}, as_dict = 1)
-
-		if len(assigned_shifts):
-			self.throw_overlap_error(assigned_shifts[0])
+		return query.run(as_dict=True)
 
 	def throw_overlap_error(self, shift_details):
 		shift_details = frappe._dict(shift_details)
 		if shift_details.docstatus == 1 and shift_details.status == "Active":
-			msg = _("Employee {0} already has Active Shift {1}: {2}").format(frappe.bold(self.employee), frappe.bold(self.shift_type), frappe.bold(shift_details.name))
-		if shift_details.start_date:
-			msg += _(" from {0}").format(getdate(self.start_date).strftime("%d-%m-%Y"))
-			title = "Ongoing Shift"
-			if shift_details.end_date:
-				msg += _(" to {0}").format(getdate(self.end_date).strftime("%d-%m-%Y"))
-				title = "Active Shift"
-		if msg:
-			frappe.throw(msg, title=title)
+			msg = _(
+				"Employee {0} already has an active Shift {1}: {2} that overlaps within this period."
+			).format(
+				frappe.bold(self.employee),
+				frappe.bold(shift_details.shift_type),
+				get_link_to_form("Shift Assignment", shift_details.name),
+			)
+			frappe.throw(msg, title=_("Overlapping Shifts"), exc=OverlappingShiftError)
+
+
+def has_overlapping_timings(shift_1: str, shift_2: str) -> bool:
+	"""
+	Accepts two shift types and checks whether their timings are overlapping
+	"""
+	curr_shift = frappe.db.get_value("Shift Type", shift_1, ["start_time", "end_time"], as_dict=True)
+	overlapping_shift = frappe.db.get_value(
+		"Shift Type", shift_2, ["start_time", "end_time"], as_dict=True
+	)
+
+	if (
+		(
+			curr_shift.start_time > overlapping_shift.start_time
+			and curr_shift.start_time < overlapping_shift.end_time
+		)
+		or (
+			curr_shift.end_time > overlapping_shift.start_time
+			and curr_shift.end_time < overlapping_shift.end_time
+		)
+		or (
+			curr_shift.start_time <= overlapping_shift.start_time
+			and curr_shift.end_time >= overlapping_shift.end_time
+		)
+	):
+		return True
+	return False
+
 
 @frappe.whitelist()
 def get_events(start, end, filters=None):
-	events = []
+	from frappe.desk.calendar import get_event_conditions
 
-	employee = frappe.db.get_value("Employee", {"user_id": frappe.session.user}, ["name", "company"],
-		as_dict=True)
+	employee = frappe.db.get_value(
+		"Employee", {"user_id": frappe.session.user}, ["name", "company"], as_dict=True
+	)
 	if employee:
 		employee, company = employee.name, employee.company
 	else:
-		employee=''
-		company=frappe.db.get_value("Global Defaults", None, "default_company")
+		employee = ""
+		company = frappe.db.get_value("Global Defaults", None, "default_company")
 
-	from frappe.desk.reportview import get_filters_cond
-	conditions = get_filters_cond("Shift Assignment", filters, [])
-	add_assignments(events, start, end, conditions=conditions)
+	conditions = get_event_conditions("Shift Assignment", filters)
+	events = add_assignments(start, end, conditions=conditions)
 	return events
 
-def add_assignments(events, start, end, conditions=None):
+
+def add_assignments(start, end, conditions=None):
+	events = []
+
 	query = """select name, start_date, end_date, employee_name,
 		employee, docstatus, shift_type
 		from `tabShift Assignment` where
-		start_date >= %(start_date)s
-		or end_date <=  %(end_date)s
-		or (%(start_date)s between start_date and end_date and %(end_date)s between start_date and end_date)
+		(
+			start_date >= %(start_date)s
+			or end_date <=  %(end_date)s
+			or (%(start_date)s between start_date and end_date and %(end_date)s between start_date and end_date)
+		)
 		and docstatus = 1"""
 	if conditions:
 		query += conditions
 
-	records = frappe.db.sql(query, {"start_date":start, "end_date":end}, as_dict=True)
+	records = frappe.db.sql(query, {"start_date": start, "end_date": end}, as_dict=True)
 	shift_timing_map = get_shift_type_timing([d.shift_type for d in records])
 
 	for d in records:
@@ -109,27 +160,33 @@
 		daily_event_end = d.end_date if d.end_date else getdate()
 		delta = timedelta(days=1)
 		while daily_event_start <= daily_event_end:
-			start_timing = frappe.utils.get_datetime(daily_event_start)+ shift_timing_map[d.shift_type]['start_time']
-			end_timing = frappe.utils.get_datetime(daily_event_start)+ shift_timing_map[d.shift_type]['end_time']
+			start_timing = (
+				frappe.utils.get_datetime(daily_event_start) + shift_timing_map[d.shift_type]["start_time"]
+			)
+			end_timing = (
+				frappe.utils.get_datetime(daily_event_start) + shift_timing_map[d.shift_type]["end_time"]
+			)
 			daily_event_start += delta
 			e = {
 				"name": d.name,
 				"doctype": "Shift Assignment",
 				"start_date": start_timing,
 				"end_date": end_timing,
-				"title": cstr(d.employee_name) + ": "+ \
-					cstr(d.shift_type),
+				"title": cstr(d.employee_name) + ": " + cstr(d.shift_type),
 				"docstatus": d.docstatus,
-				"allDay": 0
+				"allDay": 0,
 			}
 			if e not in events:
 				events.append(e)
 
 	return events
 
+
 def get_shift_type_timing(shift_types):
 	shift_timing_map = {}
-	data = frappe.get_all("Shift Type", filters = {"name": ("IN", shift_types)}, fields = ['name', 'start_time', 'end_time'])
+	data = frappe.get_all(
+		"Shift Type", filters={"name": ("IN", shift_types)}, fields=["name", "start_time", "end_time"]
+	)
 
 	for d in data:
 		shift_timing_map[d.name] = d
@@ -137,147 +194,324 @@
 	return shift_timing_map
 
 
-def get_employee_shift(employee, for_date=None, consider_default_shift=False, next_shift_direction=None):
+def get_shift_for_time(shifts: List[Dict], for_timestamp: datetime) -> Dict:
+	"""Returns shift with details for given timestamp"""
+	valid_shifts = []
+
+	for entry in shifts:
+		shift_details = get_shift_details(entry.shift_type, for_timestamp=for_timestamp)
+
+		if (
+			get_datetime(shift_details.actual_start)
+			<= get_datetime(for_timestamp)
+			<= get_datetime(shift_details.actual_end)
+		):
+			valid_shifts.append(shift_details)
+
+	valid_shifts.sort(key=lambda x: x["actual_start"])
+
+	if len(valid_shifts) > 1:
+		for i in range(len(valid_shifts) - 1):
+			# comparing 2 consecutive shifts and adjusting start and end times
+			# if they are overlapping within grace period
+			curr_shift = valid_shifts[i]
+			next_shift = valid_shifts[i + 1]
+
+			if curr_shift and next_shift:
+				next_shift.actual_start = (
+					curr_shift.end_datetime
+					if next_shift.actual_start < curr_shift.end_datetime
+					else next_shift.actual_start
+				)
+				curr_shift.actual_end = (
+					next_shift.actual_start
+					if curr_shift.actual_end > next_shift.actual_start
+					else curr_shift.actual_end
+				)
+
+			valid_shifts[i] = curr_shift
+			valid_shifts[i + 1] = next_shift
+
+		return get_exact_shift(valid_shifts, for_timestamp) or {}
+
+	return (valid_shifts and valid_shifts[0]) or {}
+
+
+def get_shifts_for_date(employee: str, for_timestamp: datetime) -> List[Dict[str, str]]:
+	"""Returns list of shifts with details for given date"""
+	assignment = frappe.qb.DocType("Shift Assignment")
+
+	return (
+		frappe.qb.from_(assignment)
+		.select(assignment.name, assignment.shift_type)
+		.where(
+			(assignment.employee == employee)
+			& (assignment.docstatus == 1)
+			& (assignment.status == "Active")
+			& (assignment.start_date <= getdate(for_timestamp.date()))
+			& (
+				Criterion.any(
+					[
+						assignment.end_date.isnull(),
+						(assignment.end_date.isnotnull() & (getdate(for_timestamp.date()) <= assignment.end_date)),
+					]
+				)
+			)
+		)
+	).run(as_dict=True)
+
+
+def get_shift_for_timestamp(employee: str, for_timestamp: datetime) -> Dict:
+	shifts = get_shifts_for_date(employee, for_timestamp)
+	if shifts:
+		return get_shift_for_time(shifts, for_timestamp)
+	return {}
+
+
+def get_employee_shift(
+	employee: str,
+	for_timestamp: datetime = None,
+	consider_default_shift: bool = False,
+	next_shift_direction: str = None,
+) -> Dict:
 	"""Returns a Shift Type for the given employee on the given date. (excluding the holidays)
 
 	:param employee: Employee for which shift is required.
-	:param for_date: Date on which shift are required
+	:param for_timestamp: DateTime on which shift is required
 	:param consider_default_shift: If set to true, default shift is taken when no shift assignment is found.
 	:param next_shift_direction: One of: None, 'forward', 'reverse'. Direction to look for next shift if shift not found on given date.
 	"""
-	if for_date is None:
-		for_date = nowdate()
-	default_shift = frappe.db.get_value('Employee', employee, 'default_shift')
-	shift_type_name = None
-	shift_assignment_details = frappe.db.get_value('Shift Assignment', {'employee':employee, 'start_date':('<=', for_date), 'docstatus': '1', 'status': "Active"}, ['shift_type', 'end_date'])
-
-	if shift_assignment_details:
-		shift_type_name = shift_assignment_details[0]
-
-		# if end_date present means that shift is over after end_date else it is a ongoing shift.
-		if shift_assignment_details[1] and for_date >= shift_assignment_details[1] :
-			shift_type_name = None
-
-	if not shift_type_name and consider_default_shift:
-		shift_type_name = default_shift
-	if shift_type_name:
-		holiday_list_name = frappe.db.get_value('Shift Type', shift_type_name, 'holiday_list')
-		if not holiday_list_name:
-			holiday_list_name = get_holiday_list_for_employee(employee, False)
-		if holiday_list_name and is_holiday(holiday_list_name, for_date):
-			shift_type_name = None
-
-	if not shift_type_name and next_shift_direction:
-		MAX_DAYS = 366
-		if consider_default_shift and default_shift:
-			direction = -1 if next_shift_direction == 'reverse' else +1
-			for i in range(MAX_DAYS):
-				date = for_date+timedelta(days=direction*(i+1))
-				shift_details = get_employee_shift(employee, date, consider_default_shift, None)
-				if shift_details:
-					shift_type_name = shift_details.shift_type.name
-					for_date = date
-					break
-		else:
-			direction = '<' if next_shift_direction == 'reverse' else '>'
-			sort_order = 'desc' if next_shift_direction == 'reverse' else 'asc'
-			dates = frappe.db.get_all('Shift Assignment',
-				['start_date', 'end_date'],
-				{'employee':employee, 'start_date':(direction, for_date), 'docstatus': '1', "status": "Active"},
-				as_list=True,
-				limit=MAX_DAYS, order_by="start_date "+sort_order)
-
-			if dates:
-				for date in dates:
-					if date[1] and date[1] < for_date:
-						continue
-					shift_details = get_employee_shift(employee, date[0], consider_default_shift, None)
-					if shift_details:
-						shift_type_name = shift_details.shift_type.name
-						for_date = date[0]
-						break
-
-	return get_shift_details(shift_type_name, for_date)
-
-
-def get_employee_shift_timings(employee, for_timestamp=None, consider_default_shift=False):
-	"""Returns previous shift, current/upcoming shift, next_shift for the given timestamp and employee
-	"""
 	if for_timestamp is None:
 		for_timestamp = now_datetime()
+
+	shift_details = get_shift_for_timestamp(employee, for_timestamp)
+
+	# if shift assignment is not found, consider default shift
+	default_shift = frappe.db.get_value("Employee", employee, "default_shift")
+	if not shift_details and consider_default_shift:
+		shift_details = get_shift_details(default_shift, for_timestamp)
+
+	# if its a holiday, reset
+	if shift_details and is_holiday_date(employee, shift_details):
+		shift_details = None
+
+	# if no shift is found, find next or prev shift assignment based on direction
+	if not shift_details and next_shift_direction:
+		shift_details = get_prev_or_next_shift(
+			employee, for_timestamp, consider_default_shift, default_shift, next_shift_direction
+		)
+
+	return shift_details or {}
+
+
+def get_prev_or_next_shift(
+	employee: str,
+	for_timestamp: datetime,
+	consider_default_shift: bool,
+	default_shift: str,
+	next_shift_direction: str,
+) -> Dict:
+	"""Returns a dict of shift details for the next or prev shift based on the next_shift_direction"""
+	MAX_DAYS = 366
+	shift_details = {}
+
+	if consider_default_shift and default_shift:
+		direction = -1 if next_shift_direction == "reverse" else 1
+		for i in range(MAX_DAYS):
+			date = for_timestamp + timedelta(days=direction * (i + 1))
+			shift_details = get_employee_shift(employee, date, consider_default_shift, None)
+			if shift_details:
+				break
+	else:
+		direction = "<" if next_shift_direction == "reverse" else ">"
+		sort_order = "desc" if next_shift_direction == "reverse" else "asc"
+		dates = frappe.db.get_all(
+			"Shift Assignment",
+			["start_date", "end_date"],
+			{
+				"employee": employee,
+				"start_date": (direction, for_timestamp.date()),
+				"docstatus": 1,
+				"status": "Active",
+			},
+			as_list=True,
+			limit=MAX_DAYS,
+			order_by="start_date " + sort_order,
+		)
+
+		if dates:
+			for date in dates:
+				if date[1] and date[1] < for_timestamp.date():
+					continue
+				shift_details = get_employee_shift(
+					employee, datetime.combine(date[0], for_timestamp.time()), consider_default_shift, None
+				)
+				if shift_details:
+					break
+
+	return shift_details or {}
+
+
+def is_holiday_date(employee: str, shift_details: Dict) -> bool:
+	holiday_list_name = frappe.db.get_value(
+		"Shift Type", shift_details.shift_type.name, "holiday_list"
+	)
+
+	if not holiday_list_name:
+		holiday_list_name = get_holiday_list_for_employee(employee, False)
+
+	return holiday_list_name and is_holiday(holiday_list_name, shift_details.start_datetime.date())
+
+
+def get_employee_shift_timings(
+	employee: str, for_timestamp: datetime = None, consider_default_shift: bool = False
+) -> List[Dict]:
+	"""Returns previous shift, current/upcoming shift, next_shift for the given timestamp and employee"""
+	if for_timestamp is None:
+		for_timestamp = now_datetime()
+
 	# write and verify a test case for midnight shift.
 	prev_shift = curr_shift = next_shift = None
-	curr_shift = get_employee_shift(employee, for_timestamp.date(), consider_default_shift, 'forward')
+	curr_shift = get_employee_shift(employee, for_timestamp, consider_default_shift, "forward")
 	if curr_shift:
-		next_shift = get_employee_shift(employee, curr_shift.start_datetime.date()+timedelta(days=1), consider_default_shift, 'forward')
-	prev_shift = get_employee_shift(employee, for_timestamp.date()+timedelta(days=-1), consider_default_shift, 'reverse')
+		next_shift = get_employee_shift(
+			employee, curr_shift.start_datetime + timedelta(days=1), consider_default_shift, "forward"
+		)
+	prev_shift = get_employee_shift(
+		employee, for_timestamp + timedelta(days=-1), consider_default_shift, "reverse"
+	)
 
 	if curr_shift:
+		# adjust actual start and end times if they are overlapping with grace period (before start and after end)
 		if prev_shift:
-			curr_shift.actual_start = prev_shift.end_datetime if curr_shift.actual_start < prev_shift.end_datetime else curr_shift.actual_start
-			prev_shift.actual_end = curr_shift.actual_start if prev_shift.actual_end > curr_shift.actual_start else prev_shift.actual_end
+			curr_shift.actual_start = (
+				prev_shift.end_datetime
+				if curr_shift.actual_start < prev_shift.end_datetime
+				else curr_shift.actual_start
+			)
+			prev_shift.actual_end = (
+				curr_shift.actual_start
+				if prev_shift.actual_end > curr_shift.actual_start
+				else prev_shift.actual_end
+			)
 		if next_shift:
-			next_shift.actual_start = curr_shift.end_datetime if next_shift.actual_start < curr_shift.end_datetime else next_shift.actual_start
-			curr_shift.actual_end = next_shift.actual_start if curr_shift.actual_end > next_shift.actual_start else curr_shift.actual_end
+			next_shift.actual_start = (
+				curr_shift.end_datetime
+				if next_shift.actual_start < curr_shift.end_datetime
+				else next_shift.actual_start
+			)
+			curr_shift.actual_end = (
+				next_shift.actual_start
+				if curr_shift.actual_end > next_shift.actual_start
+				else curr_shift.actual_end
+			)
+
 	return prev_shift, curr_shift, next_shift
 
 
-def get_shift_details(shift_type_name, for_date=None):
-	"""Returns Shift Details which contain some additional information as described below.
-	'shift_details' contains the following keys:
-		'shift_type' - Object of DocType Shift Type,
-		'start_datetime' - Date and Time of shift start on given date,
-		'end_datetime' - Date and Time of shift end on given date,
-		'actual_start' - datetime of shift start after adding 'begin_check_in_before_shift_start_time',
-		'actual_end' - datetime of shift end after adding 'allow_check_out_after_shift_end_time'(None is returned if this is zero)
+def get_actual_start_end_datetime_of_shift(
+	employee: str, for_timestamp: datetime, consider_default_shift: bool = False
+) -> Dict:
+	"""Returns a Dict containing shift details with actual_start and actual_end datetime values
+	Here 'actual' means taking into account the "begin_check_in_before_shift_start_time" and "allow_check_out_after_shift_end_time".
+	Empty Dict is returned if the timestamp is outside any actual shift timings.
 
-	:param shift_type_name: shift type name for which shift_details is required.
-	:param for_date: Date on which shift_details are required
+	:param employee (str): Employee name
+	:param for_timestamp (datetime, optional): Datetime value of checkin, if not provided considers current datetime
+	:param consider_default_shift (bool, optional): Flag (defaults to False) to specify whether to consider
+	default shift in employee master if no shift assignment is found
 	"""
-	if not shift_type_name:
-		return None
-	if not for_date:
-		for_date = nowdate()
-	shift_type = frappe.get_doc('Shift Type', shift_type_name)
-	start_datetime = datetime.combine(for_date, datetime.min.time()) + shift_type.start_time
-	for_date = for_date + timedelta(days=1) if shift_type.start_time > shift_type.end_time else for_date
-	end_datetime = datetime.combine(for_date, datetime.min.time()) + shift_type.end_time
-	actual_start = start_datetime - timedelta(minutes=shift_type.begin_check_in_before_shift_start_time)
-	actual_end = end_datetime + timedelta(minutes=shift_type.allow_check_out_after_shift_end_time)
-
-	return frappe._dict({
-		'shift_type': shift_type,
-		'start_datetime': start_datetime,
-		'end_datetime': end_datetime,
-		'actual_start': actual_start,
-		'actual_end': actual_end
-	})
+	shift_timings_as_per_timestamp = get_employee_shift_timings(
+		employee, for_timestamp, consider_default_shift
+	)
+	return get_exact_shift(shift_timings_as_per_timestamp, for_timestamp)
 
 
-def get_actual_start_end_datetime_of_shift(employee, for_datetime, consider_default_shift=False):
-	"""Takes a datetime and returns the 'actual' start datetime and end datetime of the shift in which the timestamp belongs.
-		Here 'actual' means - taking in to account the "begin_check_in_before_shift_start_time" and "allow_check_out_after_shift_end_time".
-		None is returned if the timestamp is outside any actual shift timings.
-		Shift Details is also returned(current/upcoming i.e. if timestamp not in any actual shift then details of next shift returned)
-	"""
-	actual_shift_start = actual_shift_end = shift_details = None
-	shift_timings_as_per_timestamp = get_employee_shift_timings(employee, for_datetime, consider_default_shift)
+def get_exact_shift(shifts: List, for_timestamp: datetime) -> Dict:
+	"""Returns the shift details (dict) for the exact shift in which the 'for_timestamp' value falls among multiple shifts"""
+	shift_details = dict()
 	timestamp_list = []
-	for shift in shift_timings_as_per_timestamp:
+
+	for shift in shifts:
 		if shift:
 			timestamp_list.extend([shift.actual_start, shift.actual_end])
 		else:
 			timestamp_list.extend([None, None])
+
 	timestamp_index = None
 	for index, timestamp in enumerate(timestamp_list):
-		if timestamp and for_datetime <= timestamp:
-			timestamp_index = index
-			break
-	if timestamp_index and timestamp_index%2 == 1:
-		shift_details = shift_timings_as_per_timestamp[int((timestamp_index-1)/2)]
-		actual_shift_start = shift_details.actual_start
-		actual_shift_end = shift_details.actual_end
-	elif timestamp_index:
-		shift_details = shift_timings_as_per_timestamp[int(timestamp_index/2)]
+		if not timestamp:
+			continue
 
-	return actual_shift_start, actual_shift_end, shift_details
+		if for_timestamp < timestamp:
+			timestamp_index = index
+		elif for_timestamp == timestamp:
+			# on timestamp boundary
+			if index % 2 == 1:
+				timestamp_index = index
+			else:
+				timestamp_index = index + 1
+
+		if timestamp_index:
+			break
+
+	if timestamp_index and timestamp_index % 2 == 1:
+		shift_details = shifts[int((timestamp_index - 1) / 2)]
+
+	return shift_details
+
+
+def get_shift_details(shift_type_name: str, for_timestamp: datetime = None) -> Dict:
+	"""Returns a Dict containing shift details with the following data:
+	'shift_type' - Object of DocType Shift Type,
+	'start_datetime' - datetime of shift start on given timestamp,
+	'end_datetime' - datetime of shift end on given timestamp,
+	'actual_start' - datetime of shift start after adding 'begin_check_in_before_shift_start_time',
+	'actual_end' - datetime of shift end after adding 'allow_check_out_after_shift_end_time' (None is returned if this is zero)
+
+	:param shift_type_name (str): shift type name for which shift_details are required.
+	:param for_timestamp (datetime, optional): Datetime value of checkin, if not provided considers current datetime
+	"""
+	if not shift_type_name:
+		return {}
+
+	if for_timestamp is None:
+		for_timestamp = now_datetime()
+
+	shift_type = frappe.get_doc("Shift Type", shift_type_name)
+	shift_actual_start = shift_type.start_time - timedelta(
+		minutes=shift_type.begin_check_in_before_shift_start_time
+	)
+
+	if shift_type.start_time > shift_type.end_time:
+		# shift spans accross 2 different days
+		if get_time(for_timestamp.time()) >= get_time(shift_actual_start):
+			# if for_timestamp is greater than start time, it's within the first day
+			start_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.start_time
+			for_timestamp = for_timestamp + timedelta(days=1)
+			end_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.end_time
+
+		elif get_time(for_timestamp.time()) < get_time(shift_actual_start):
+			# if for_timestamp is less than start time, it's within the second day
+			end_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.end_time
+			for_timestamp = for_timestamp + timedelta(days=-1)
+			start_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.start_time
+	else:
+		# start and end timings fall on the same day
+		start_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.start_time
+		end_datetime = datetime.combine(for_timestamp, datetime.min.time()) + shift_type.end_time
+
+	actual_start = start_datetime - timedelta(
+		minutes=shift_type.begin_check_in_before_shift_start_time
+	)
+	actual_end = end_datetime + timedelta(minutes=shift_type.allow_check_out_after_shift_end_time)
+
+	return frappe._dict(
+		{
+			"shift_type": shift_type,
+			"start_datetime": start_datetime,
+			"end_datetime": end_datetime,
+			"actual_start": actual_start,
+			"actual_end": actual_end,
+		}
+	)
diff --git a/erpnext/hr/doctype/shift_assignment/test_shift_assignment.py b/erpnext/hr/doctype/shift_assignment/test_shift_assignment.py
index d490081..de82a24 100644
--- a/erpnext/hr/doctype/shift_assignment/test_shift_assignment.py
+++ b/erpnext/hr/doctype/shift_assignment/test_shift_assignment.py
@@ -4,76 +4,168 @@
 import unittest
 
 import frappe
-from frappe.utils import add_days, nowdate
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import add_days, getdate, nowdate
+
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.shift_assignment.shift_assignment import OverlappingShiftError, get_events
+from erpnext.hr.doctype.shift_type.test_shift_type import make_shift_assignment, setup_shift_type
 
 test_dependencies = ["Shift Type"]
 
-class TestShiftAssignment(unittest.TestCase):
 
+class TestShiftAssignment(FrappeTestCase):
 	def setUp(self):
-		frappe.db.sql("delete from `tabShift Assignment`")
+		frappe.db.delete("Shift Assignment")
+		frappe.db.delete("Shift Type")
 
 	def test_make_shift_assignment(self):
-		shift_assignment = frappe.get_doc({
-			"doctype": "Shift Assignment",
-			"shift_type": "Day Shift",
-			"company": "_Test Company",
-			"employee": "_T-Employee-00001",
-			"start_date": nowdate()
-		}).insert()
+		setup_shift_type(shift_type="Day Shift")
+		shift_assignment = frappe.get_doc(
+			{
+				"doctype": "Shift Assignment",
+				"shift_type": "Day Shift",
+				"company": "_Test Company",
+				"employee": "_T-Employee-00001",
+				"start_date": nowdate(),
+			}
+		).insert()
 		shift_assignment.submit()
 
 		self.assertEqual(shift_assignment.docstatus, 1)
 
 	def test_overlapping_for_ongoing_shift(self):
 		# shift should be Ongoing if Only start_date is present and status = Active
-
-		shift_assignment_1 = frappe.get_doc({
-			"doctype": "Shift Assignment",
-			"shift_type": "Day Shift",
-			"company": "_Test Company",
-			"employee": "_T-Employee-00001",
-			"start_date": nowdate(),
-			"status": 'Active'
-		}).insert()
+		setup_shift_type(shift_type="Day Shift")
+		shift_assignment_1 = frappe.get_doc(
+			{
+				"doctype": "Shift Assignment",
+				"shift_type": "Day Shift",
+				"company": "_Test Company",
+				"employee": "_T-Employee-00001",
+				"start_date": nowdate(),
+				"status": "Active",
+			}
+		).insert()
 		shift_assignment_1.submit()
 
 		self.assertEqual(shift_assignment_1.docstatus, 1)
 
-		shift_assignment = frappe.get_doc({
-			"doctype": "Shift Assignment",
-			"shift_type": "Day Shift",
-			"company": "_Test Company",
-			"employee": "_T-Employee-00001",
-			"start_date": add_days(nowdate(), 2)
-		})
+		shift_assignment = frappe.get_doc(
+			{
+				"doctype": "Shift Assignment",
+				"shift_type": "Day Shift",
+				"company": "_Test Company",
+				"employee": "_T-Employee-00001",
+				"start_date": add_days(nowdate(), 2),
+			}
+		)
 
-		self.assertRaises(frappe.ValidationError, shift_assignment.save)
+		self.assertRaises(OverlappingShiftError, shift_assignment.save)
 
 	def test_overlapping_for_fixed_period_shift(self):
 		# shift should is for Fixed period if Only start_date and end_date both are present and status = Active
-
-			shift_assignment_1 = frappe.get_doc({
+		setup_shift_type(shift_type="Day Shift")
+		shift_assignment_1 = frappe.get_doc(
+			{
 				"doctype": "Shift Assignment",
 				"shift_type": "Day Shift",
 				"company": "_Test Company",
 				"employee": "_T-Employee-00001",
 				"start_date": nowdate(),
 				"end_date": add_days(nowdate(), 30),
-				"status": 'Active'
-			}).insert()
-			shift_assignment_1.submit()
+				"status": "Active",
+			}
+		).insert()
+		shift_assignment_1.submit()
 
-
-			# it should not allowed within period of any shift.
-			shift_assignment_3 = frappe.get_doc({
+		# it should not allowed within period of any shift.
+		shift_assignment_3 = frappe.get_doc(
+			{
 				"doctype": "Shift Assignment",
 				"shift_type": "Day Shift",
 				"company": "_Test Company",
 				"employee": "_T-Employee-00001",
-				"start_date":add_days(nowdate(), 10),
+				"start_date": add_days(nowdate(), 10),
 				"end_date": add_days(nowdate(), 35),
-				"status": 'Active'
-			})
+				"status": "Active",
+			}
+		)
 
-			self.assertRaises(frappe.ValidationError, shift_assignment_3.save)
+		self.assertRaises(OverlappingShiftError, shift_assignment_3.save)
+
+	def test_overlapping_for_a_fixed_period_shift_and_ongoing_shift(self):
+		employee = make_employee("test_shift_assignment@example.com", company="_Test Company")
+
+		# shift setup for 8-12
+		shift_type = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00")
+		date = getdate()
+		# shift with end date
+		make_shift_assignment(shift_type.name, employee, date, add_days(date, 30))
+
+		# shift setup for 11-15
+		shift_type = setup_shift_type(shift_type="Shift 2", start_time="11:00:00", end_time="15:00:00")
+		date = getdate()
+
+		# shift assignment without end date
+		shift2 = frappe.get_doc(
+			{
+				"doctype": "Shift Assignment",
+				"shift_type": shift_type.name,
+				"company": "_Test Company",
+				"employee": employee,
+				"start_date": date,
+			}
+		)
+		self.assertRaises(OverlappingShiftError, shift2.insert)
+
+	def test_overlap_validation_for_shifts_on_same_day_with_overlapping_timeslots(self):
+		employee = make_employee("test_shift_assignment@example.com", company="_Test Company")
+
+		# shift setup for 8-12
+		shift_type = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00")
+		date = getdate()
+		make_shift_assignment(shift_type.name, employee, date)
+
+		# shift setup for 11-15
+		shift_type = setup_shift_type(shift_type="Shift 2", start_time="11:00:00", end_time="15:00:00")
+		date = getdate()
+
+		shift2 = frappe.get_doc(
+			{
+				"doctype": "Shift Assignment",
+				"shift_type": shift_type.name,
+				"company": "_Test Company",
+				"employee": employee,
+				"start_date": date,
+			}
+		)
+		self.assertRaises(OverlappingShiftError, shift2.insert)
+
+	def test_multiple_shift_assignments_for_same_day(self):
+		employee = make_employee("test_shift_assignment@example.com", company="_Test Company")
+
+		# shift setup for 8-12
+		shift_type = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00")
+		date = getdate()
+		make_shift_assignment(shift_type.name, employee, date)
+
+		# shift setup for 13-15
+		shift_type = setup_shift_type(shift_type="Shift 2", start_time="13:00:00", end_time="15:00:00")
+		date = getdate()
+		make_shift_assignment(shift_type.name, employee, date)
+
+	def test_shift_assignment_calendar(self):
+		employee1 = make_employee("test_shift_assignment1@example.com", company="_Test Company")
+		employee2 = make_employee("test_shift_assignment2@example.com", company="_Test Company")
+
+		shift_type = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00")
+		date = getdate()
+		shift1 = make_shift_assignment(shift_type.name, employee1, date)
+		make_shift_assignment(shift_type.name, employee2, date)
+
+		events = get_events(
+			start=date, end=date, filters=[["Shift Assignment", "employee", "=", employee1, False]]
+		)
+		self.assertEqual(len(events), 1)
+		self.assertEqual(events[0]["name"], shift1.name)
diff --git a/erpnext/hr/doctype/shift_request/shift_request.py b/erpnext/hr/doctype/shift_request/shift_request.py
index d4fcf99..2bee240 100644
--- a/erpnext/hr/doctype/shift_request/shift_request.py
+++ b/erpnext/hr/doctype/shift_request/shift_request.py
@@ -5,18 +5,22 @@
 import frappe
 from frappe import _
 from frappe.model.document import Document
-from frappe.utils import formatdate, getdate
+from frappe.query_builder import Criterion
+from frappe.utils import get_link_to_form, getdate
 
+from erpnext.hr.doctype.shift_assignment.shift_assignment import has_overlapping_timings
 from erpnext.hr.utils import share_doc_with_approver, validate_active_employee
 
 
-class OverlapError(frappe.ValidationError): pass
+class OverlappingShiftRequestError(frappe.ValidationError):
+	pass
+
 
 class ShiftRequest(Document):
 	def validate(self):
 		validate_active_employee(self.employee)
 		self.validate_dates()
-		self.validate_shift_request_overlap_dates()
+		self.validate_overlapping_shift_requests()
 		self.validate_approver()
 		self.validate_default_shift()
 
@@ -39,24 +43,35 @@
 			assignment_doc.insert()
 			assignment_doc.submit()
 
-			frappe.msgprint(_("Shift Assignment: {0} created for Employee: {1}").format(frappe.bold(assignment_doc.name), frappe.bold(self.employee)))
+			frappe.msgprint(
+				_("Shift Assignment: {0} created for Employee: {1}").format(
+					frappe.bold(assignment_doc.name), frappe.bold(self.employee)
+				)
+			)
 
 	def on_cancel(self):
-		shift_assignment_list = frappe.get_list("Shift Assignment", {'employee': self.employee, 'shift_request': self.name})
+		shift_assignment_list = frappe.get_list(
+			"Shift Assignment", {"employee": self.employee, "shift_request": self.name}
+		)
 		if shift_assignment_list:
 			for shift in shift_assignment_list:
-				shift_assignment_doc = frappe.get_doc("Shift Assignment", shift['name'])
+				shift_assignment_doc = frappe.get_doc("Shift Assignment", shift["name"])
 				shift_assignment_doc.cancel()
 
 	def validate_default_shift(self):
 		default_shift = frappe.get_value("Employee", self.employee, "default_shift")
 		if self.shift_type == default_shift:
-			frappe.throw(_("You can not request for your Default Shift: {0}").format(frappe.bold(self.shift_type)))
+			frappe.throw(
+				_("You can not request for your Default Shift: {0}").format(frappe.bold(self.shift_type))
+			)
 
 	def validate_approver(self):
 		department = frappe.get_value("Employee", self.employee, "department")
 		shift_approver = frappe.get_value("Employee", self.employee, "shift_request_approver")
-		approvers = frappe.db.sql("""select approver from `tabDepartment Approver` where parent= %s and parentfield = 'shift_request_approver'""", (department))
+		approvers = frappe.db.sql(
+			"""select approver from `tabDepartment Approver` where parent= %s and parentfield = 'shift_request_approver'""",
+			(department),
+		)
 		approvers = [approver[0] for approver in approvers]
 		approvers.append(shift_approver)
 		if self.approver not in approvers:
@@ -66,33 +81,60 @@
 		if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)):
 			frappe.throw(_("To date cannot be before from date"))
 
-	def validate_shift_request_overlap_dates(self):
-			if not self.name:
-				self.name = "New Shift Request"
+	def validate_overlapping_shift_requests(self):
+		overlapping_dates = self.get_overlapping_dates()
+		if len(overlapping_dates):
+			# if dates are overlapping, check if timings are overlapping, else allow
+			overlapping_timings = has_overlapping_timings(self.shift_type, overlapping_dates[0].shift_type)
+			if overlapping_timings:
+				self.throw_overlap_error(overlapping_dates[0])
 
-			d = frappe.db.sql("""
-				select
-					name, shift_type, from_date, to_date
-				from `tabShift Request`
-				where employee = %(employee)s and docstatus < 2
-				and ((%(from_date)s >= from_date
-					and %(from_date)s <= to_date) or
-					( %(to_date)s >= from_date
-					and %(to_date)s <= to_date ))
-				and name != %(name)s""", {
-					"employee": self.employee,
-					"shift_type": self.shift_type,
-					"from_date": self.from_date,
-					"to_date": self.to_date,
-					"name": self.name
-				}, as_dict=1)
+	def get_overlapping_dates(self):
+		if not self.name:
+			self.name = "New Shift Request"
 
-			for date_overlap in d:
-				if date_overlap ['name']:
-					self.throw_overlap_error(date_overlap)
+		shift = frappe.qb.DocType("Shift Request")
+		query = (
+			frappe.qb.from_(shift)
+			.select(shift.name, shift.shift_type)
+			.where((shift.employee == self.employee) & (shift.docstatus < 2) & (shift.name != self.name))
+		)
 
-	def throw_overlap_error(self, d):
-		msg = _("Employee {0} has already applied for {1} between {2} and {3} : ").format(self.employee,
-			d['shift_type'], formatdate(d['from_date']), formatdate(d['to_date'])) \
-			+ """ <b><a href="/app/Form/Shift Request/{0}">{0}</a></b>""".format(d["name"])
-		frappe.throw(msg, OverlapError)
+		if self.to_date:
+			query = query.where(
+				Criterion.any(
+					[
+						Criterion.any(
+							[
+								shift.to_date.isnull(),
+								((self.from_date >= shift.from_date) & (self.from_date <= shift.to_date)),
+							]
+						),
+						Criterion.any(
+							[
+								((self.to_date >= shift.from_date) & (self.to_date <= shift.to_date)),
+								shift.from_date.between(self.from_date, self.to_date),
+							]
+						),
+					]
+				)
+			)
+		else:
+			query = query.where(
+				shift.to_date.isnull()
+				| ((self.from_date >= shift.from_date) & (self.from_date <= shift.to_date))
+			)
+
+		return query.run(as_dict=True)
+
+	def throw_overlap_error(self, shift_details):
+		shift_details = frappe._dict(shift_details)
+		msg = _(
+			"Employee {0} has already applied for Shift {1}: {2} that overlaps within this period"
+		).format(
+			frappe.bold(self.employee),
+			frappe.bold(shift_details.shift_type),
+			get_link_to_form("Shift Request", shift_details.name),
+		)
+
+		frappe.throw(msg, title=_("Overlapping Shift Requests"), exc=OverlappingShiftRequestError)
diff --git a/erpnext/hr/doctype/shift_request/shift_request_dashboard.py b/erpnext/hr/doctype/shift_request/shift_request_dashboard.py
index 531c98d..2859b8f 100644
--- a/erpnext/hr/doctype/shift_request/shift_request_dashboard.py
+++ b/erpnext/hr/doctype/shift_request/shift_request_dashboard.py
@@ -1,9 +1,7 @@
 def get_data():
-     return {
-        'fieldname': 'shift_request',
-        'transactions': [
-            {
-                'items': ['Shift Assignment']
-            },
-        ],
-    }
+	return {
+		"fieldname": "shift_request",
+		"transactions": [
+			{"items": ["Shift Assignment"]},
+		],
+	}
diff --git a/erpnext/hr/doctype/shift_request/test_shift_request.py b/erpnext/hr/doctype/shift_request/test_shift_request.py
index 3633c9b..c47418c 100644
--- a/erpnext/hr/doctype/shift_request/test_shift_request.py
+++ b/erpnext/hr/doctype/shift_request/test_shift_request.py
@@ -4,25 +4,30 @@
 import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import add_days, nowdate
 
 from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.shift_request.shift_request import OverlappingShiftRequestError
+from erpnext.hr.doctype.shift_type.test_shift_type import setup_shift_type
 
 test_dependencies = ["Shift Type"]
 
-class TestShiftRequest(unittest.TestCase):
-	def setUp(self):
-		for doctype in ["Shift Request", "Shift Assignment"]:
-			frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype))
 
-	def tearDown(self):
-		frappe.db.rollback()
+class TestShiftRequest(FrappeTestCase):
+	def setUp(self):
+		for doctype in ["Shift Request", "Shift Assignment", "Shift Type"]:
+			frappe.db.delete(doctype)
 
 	def test_make_shift_request(self):
 		"Test creation/updation of Shift Assignment from Shift Request."
-		department = frappe.get_value("Employee", "_T-Employee-00001", 'department')
+		setup_shift_type(shift_type="Day Shift")
+		department = frappe.get_value("Employee", "_T-Employee-00001", "department")
 		set_shift_approver(department)
-		approver = frappe.db.sql("""select approver from `tabDepartment Approver` where parent= %s and parentfield = 'shift_request_approver'""", (department))[0][0]
+		approver = frappe.db.sql(
+			"""select approver from `tabDepartment Approver` where parent= %s and parentfield = 'shift_request_approver'""",
+			(department),
+		)[0][0]
 
 		shift_request = make_shift_request(approver)
 
@@ -31,7 +36,7 @@
 			"Shift Assignment",
 			filters={"shift_request": shift_request.name},
 			fieldname=["employee", "docstatus"],
-			as_dict=True
+			as_dict=True,
 		)
 		self.assertEqual(shift_request.employee, shift_assignment.employee)
 		self.assertEqual(shift_assignment.docstatus, 1)
@@ -39,13 +44,12 @@
 		shift_request.cancel()
 
 		shift_assignment_docstatus = frappe.db.get_value(
-			"Shift Assignment",
-			filters={"shift_request": shift_request.name},
-			fieldname="docstatus"
+			"Shift Assignment", filters={"shift_request": shift_request.name}, fieldname="docstatus"
 		)
 		self.assertEqual(shift_assignment_docstatus, 2)
 
 	def test_shift_request_approver_perms(self):
+		setup_shift_type(shift_type="Day Shift")
 		employee = frappe.get_doc("Employee", "_T-Employee-00001")
 		user = "test_approver_perm_emp@example.com"
 		make_employee(user, "_Test Company")
@@ -62,7 +66,10 @@
 		shift_request.reload()
 		department = frappe.get_value("Employee", "_T-Employee-00001", "department")
 		set_shift_approver(department)
-		department_approver = frappe.db.sql("""select approver from `tabDepartment Approver` where parent= %s and parentfield = 'shift_request_approver'""", (department))[0][0]
+		department_approver = frappe.db.sql(
+			"""select approver from `tabDepartment Approver` where parent= %s and parentfield = 'shift_request_approver'""",
+			(department),
+		)[0][0]
 		shift_request.approver = department_approver
 		shift_request.save()
 		self.assertTrue(shift_request.name not in frappe.share.get_shared("Shift Request", user))
@@ -82,25 +89,167 @@
 		employee.shift_request_approver = ""
 		employee.save()
 
+	def test_overlap_for_request_without_to_date(self):
+		# shift should be Ongoing if Only from_date is present
+		user = "test_shift_request@example.com"
+		employee = make_employee(user, company="_Test Company", shift_request_approver=user)
+		setup_shift_type(shift_type="Day Shift")
+
+		shift_request = frappe.get_doc(
+			{
+				"doctype": "Shift Request",
+				"shift_type": "Day Shift",
+				"company": "_Test Company",
+				"employee": employee,
+				"from_date": nowdate(),
+				"approver": user,
+				"status": "Approved",
+			}
+		).submit()
+
+		shift_request = frappe.get_doc(
+			{
+				"doctype": "Shift Request",
+				"shift_type": "Day Shift",
+				"company": "_Test Company",
+				"employee": employee,
+				"from_date": add_days(nowdate(), 2),
+				"approver": user,
+				"status": "Approved",
+			}
+		)
+
+		self.assertRaises(OverlappingShiftRequestError, shift_request.save)
+
+	def test_overlap_for_request_with_from_and_to_dates(self):
+		user = "test_shift_request@example.com"
+		employee = make_employee(user, company="_Test Company", shift_request_approver=user)
+		setup_shift_type(shift_type="Day Shift")
+
+		shift_request = frappe.get_doc(
+			{
+				"doctype": "Shift Request",
+				"shift_type": "Day Shift",
+				"company": "_Test Company",
+				"employee": employee,
+				"from_date": nowdate(),
+				"to_date": add_days(nowdate(), 30),
+				"approver": user,
+				"status": "Approved",
+			}
+		).submit()
+
+		shift_request = frappe.get_doc(
+			{
+				"doctype": "Shift Request",
+				"shift_type": "Day Shift",
+				"company": "_Test Company",
+				"employee": employee,
+				"from_date": add_days(nowdate(), 10),
+				"to_date": add_days(nowdate(), 35),
+				"approver": user,
+				"status": "Approved",
+			}
+		)
+
+		self.assertRaises(OverlappingShiftRequestError, shift_request.save)
+
+	def test_overlapping_for_a_fixed_period_shift_and_ongoing_shift(self):
+		user = "test_shift_request@example.com"
+		employee = make_employee(user, company="_Test Company", shift_request_approver=user)
+
+		# shift setup for 8-12
+		shift_type = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00")
+		date = nowdate()
+
+		# shift with end date
+		frappe.get_doc(
+			{
+				"doctype": "Shift Request",
+				"shift_type": shift_type.name,
+				"company": "_Test Company",
+				"employee": employee,
+				"from_date": date,
+				"to_date": add_days(date, 30),
+				"approver": user,
+				"status": "Approved",
+			}
+		).submit()
+
+		# shift setup for 11-15
+		shift_type = setup_shift_type(shift_type="Shift 2", start_time="11:00:00", end_time="15:00:00")
+		shift2 = frappe.get_doc(
+			{
+				"doctype": "Shift Request",
+				"shift_type": shift_type.name,
+				"company": "_Test Company",
+				"employee": employee,
+				"from_date": date,
+				"approver": user,
+				"status": "Approved",
+			}
+		)
+
+		self.assertRaises(OverlappingShiftRequestError, shift2.insert)
+
+	def test_allow_non_overlapping_shift_requests_for_same_day(self):
+		user = "test_shift_request@example.com"
+		employee = make_employee(user, company="_Test Company", shift_request_approver=user)
+
+		# shift setup for 8-12
+		shift_type = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00")
+		date = nowdate()
+
+		# shift with end date
+		frappe.get_doc(
+			{
+				"doctype": "Shift Request",
+				"shift_type": shift_type.name,
+				"company": "_Test Company",
+				"employee": employee,
+				"from_date": date,
+				"to_date": add_days(date, 30),
+				"approver": user,
+				"status": "Approved",
+			}
+		).submit()
+
+		# shift setup for 13-15
+		shift_type = setup_shift_type(shift_type="Shift 2", start_time="13:00:00", end_time="15:00:00")
+		frappe.get_doc(
+			{
+				"doctype": "Shift Request",
+				"shift_type": shift_type.name,
+				"company": "_Test Company",
+				"employee": employee,
+				"from_date": date,
+				"approver": user,
+				"status": "Approved",
+			}
+		).submit()
+
 
 def set_shift_approver(department):
 	department_doc = frappe.get_doc("Department", department)
-	department_doc.append('shift_request_approver',{'approver': "test1@example.com"})
+	department_doc.append("shift_request_approver", {"approver": "test1@example.com"})
 	department_doc.save()
 	department_doc.reload()
 
+
 def make_shift_request(approver, do_not_submit=0):
-	shift_request = frappe.get_doc({
-		"doctype": "Shift Request",
-		"shift_type": "Day Shift",
-		"company": "_Test Company",
-		"employee": "_T-Employee-00001",
-		"employee_name": "_Test Employee",
-		"from_date": nowdate(),
-		"to_date": add_days(nowdate(), 10),
-		"approver": approver,
-		"status": "Approved"
-	}).insert()
+	shift_request = frappe.get_doc(
+		{
+			"doctype": "Shift Request",
+			"shift_type": "Day Shift",
+			"company": "_Test Company",
+			"employee": "_T-Employee-00001",
+			"employee_name": "_Test Employee",
+			"from_date": nowdate(),
+			"to_date": add_days(nowdate(), 10),
+			"approver": approver,
+			"status": "Approved",
+		}
+	).insert()
 
 	if do_not_submit:
 		return shift_request
diff --git a/erpnext/hr/doctype/shift_type/shift_type.py b/erpnext/hr/doctype/shift_type/shift_type.py
index 562a573..a61bb9e 100644
--- a/erpnext/hr/doctype/shift_type/shift_type.py
+++ b/erpnext/hr/doctype/shift_type/shift_type.py
@@ -3,133 +3,200 @@
 
 
 import itertools
-from datetime import timedelta
+from datetime import datetime, timedelta
 
 import frappe
 from frappe.model.document import Document
-from frappe.utils import cint, get_datetime, getdate
+from frappe.utils import cint, get_datetime, get_time, getdate
 
+from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import daterange
 from erpnext.hr.doctype.attendance.attendance import mark_attendance
 from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
 from erpnext.hr.doctype.employee_checkin.employee_checkin import (
 	calculate_working_hours,
 	mark_attendance_and_link_log,
 )
+from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
 from erpnext.hr.doctype.shift_assignment.shift_assignment import (
-	get_actual_start_end_datetime_of_shift,
 	get_employee_shift,
+	get_shift_details,
 )
 
 
 class ShiftType(Document):
 	@frappe.whitelist()
 	def process_auto_attendance(self):
-		if not cint(self.enable_auto_attendance) or not self.process_attendance_after or not self.last_sync_of_checkin:
+		if (
+			not cint(self.enable_auto_attendance)
+			or not self.process_attendance_after
+			or not self.last_sync_of_checkin
+		):
 			return
+
 		filters = {
-			'skip_auto_attendance':'0',
-			'attendance':('is', 'not set'),
-			'time':('>=', self.process_attendance_after),
-			'shift_actual_end': ('<', self.last_sync_of_checkin),
-			'shift': self.name
+			"skip_auto_attendance": 0,
+			"attendance": ("is", "not set"),
+			"time": (">=", self.process_attendance_after),
+			"shift_actual_end": ("<", self.last_sync_of_checkin),
+			"shift": self.name,
 		}
-		logs = frappe.db.get_list('Employee Checkin', fields="*", filters=filters, order_by="employee,time")
-		for key, group in itertools.groupby(logs, key=lambda x: (x['employee'], x['shift_actual_start'])):
+		logs = frappe.db.get_list(
+			"Employee Checkin", fields="*", filters=filters, order_by="employee,time"
+		)
+
+		for key, group in itertools.groupby(
+			logs, key=lambda x: (x["employee"], x["shift_actual_start"])
+		):
 			single_shift_logs = list(group)
-			attendance_status, working_hours, late_entry, early_exit, in_time, out_time = self.get_attendance(single_shift_logs)
-			mark_attendance_and_link_log(single_shift_logs, attendance_status, key[1].date(), working_hours, late_entry, early_exit, in_time, out_time, self.name)
+			(
+				attendance_status,
+				working_hours,
+				late_entry,
+				early_exit,
+				in_time,
+				out_time,
+			) = self.get_attendance(single_shift_logs)
+
+			mark_attendance_and_link_log(
+				single_shift_logs,
+				attendance_status,
+				key[1].date(),
+				working_hours,
+				late_entry,
+				early_exit,
+				in_time,
+				out_time,
+				self.name,
+			)
+
 		for employee in self.get_assigned_employee(self.process_attendance_after, True):
 			self.mark_absent_for_dates_with_no_attendance(employee)
 
 	def get_attendance(self, logs):
 		"""Return attendance_status, working_hours, late_entry, early_exit, in_time, out_time
 		for a set of logs belonging to a single shift.
-		Assumtion:
-			1. These logs belongs to an single shift, single employee and is not in a holiday date.
-			2. Logs are in chronological order
+		Assumptions:
+		1. These logs belongs to a single shift, single employee and it's not in a holiday date.
+		2. Logs are in chronological order
 		"""
 		late_entry = early_exit = False
-		total_working_hours, in_time, out_time = calculate_working_hours(logs, self.determine_check_in_and_check_out, self.working_hours_calculation_based_on)
-		if cint(self.enable_entry_grace_period) and in_time and in_time > logs[0].shift_start + timedelta(minutes=cint(self.late_entry_grace_period)):
+		total_working_hours, in_time, out_time = calculate_working_hours(
+			logs, self.determine_check_in_and_check_out, self.working_hours_calculation_based_on
+		)
+		if (
+			cint(self.enable_entry_grace_period)
+			and in_time
+			and in_time > logs[0].shift_start + timedelta(minutes=cint(self.late_entry_grace_period))
+		):
 			late_entry = True
 
-		if cint(self.enable_exit_grace_period) and out_time and out_time < logs[0].shift_end - timedelta(minutes=cint(self.early_exit_grace_period)):
+		if (
+			cint(self.enable_exit_grace_period)
+			and out_time
+			and out_time < logs[0].shift_end - timedelta(minutes=cint(self.early_exit_grace_period))
+		):
 			early_exit = True
 
-		if self.working_hours_threshold_for_absent and total_working_hours < self.working_hours_threshold_for_absent:
-			return 'Absent', total_working_hours, late_entry, early_exit, in_time, out_time
-		if self.working_hours_threshold_for_half_day and total_working_hours < self.working_hours_threshold_for_half_day:
-			return 'Half Day', total_working_hours, late_entry, early_exit, in_time, out_time
-		return 'Present', total_working_hours, late_entry, early_exit, in_time, out_time
+		if (
+			self.working_hours_threshold_for_half_day
+			and total_working_hours < self.working_hours_threshold_for_half_day
+		):
+			return "Half Day", total_working_hours, late_entry, early_exit, in_time, out_time
+		if (
+			self.working_hours_threshold_for_absent
+			and total_working_hours < self.working_hours_threshold_for_absent
+		):
+			return "Absent", total_working_hours, late_entry, early_exit, in_time, out_time
+		return "Present", total_working_hours, late_entry, early_exit, in_time, out_time
 
 	def mark_absent_for_dates_with_no_attendance(self, employee):
 		"""Marks Absents for the given employee on working days in this shift which have no attendance marked.
 		The Absent is marked starting from 'process_attendance_after' or employee creation date.
 		"""
-		date_of_joining, relieving_date, employee_creation = frappe.db.get_value("Employee", employee, ["date_of_joining", "relieving_date", "creation"])
-		if not date_of_joining:
-			date_of_joining = employee_creation.date()
-		start_date = max(getdate(self.process_attendance_after), date_of_joining)
-		actual_shift_datetime = get_actual_start_end_datetime_of_shift(employee, get_datetime(self.last_sync_of_checkin), True)
-		last_shift_time = actual_shift_datetime[0] if actual_shift_datetime[0] else get_datetime(self.last_sync_of_checkin)
-		prev_shift = get_employee_shift(employee, last_shift_time.date()-timedelta(days=1), True, 'reverse')
-		if prev_shift:
-			end_date = min(prev_shift.start_datetime.date(), relieving_date) if relieving_date else prev_shift.start_datetime.date()
-		else:
+		start_date, end_date = self.get_start_and_end_dates(employee)
+
+		# no shift assignment found, no need to process absent attendance records
+		if start_date is None:
 			return
+
 		holiday_list_name = self.holiday_list
 		if not holiday_list_name:
 			holiday_list_name = get_holiday_list_for_employee(employee, False)
-		dates = get_filtered_date_list(employee, start_date, end_date, holiday_list=holiday_list_name)
-		for date in dates:
-			shift_details = get_employee_shift(employee, date, True)
+
+		start_time = get_time(self.start_time)
+
+		for date in daterange(getdate(start_date), getdate(end_date)):
+			if is_holiday(holiday_list_name, date):
+				# skip marking absent on a holiday
+				continue
+
+			timestamp = datetime.combine(date, start_time)
+			shift_details = get_employee_shift(employee, timestamp, True)
+
 			if shift_details and shift_details.shift_type.name == self.name:
-				mark_attendance(employee, date, 'Absent', self.name)
+				attendance = mark_attendance(employee, date, "Absent", self.name)
+				if attendance:
+					frappe.get_doc(
+						{
+							"doctype": "Comment",
+							"comment_type": "Comment",
+							"reference_doctype": "Attendance",
+							"reference_name": attendance,
+							"content": frappe._("Employee was marked Absent due to missing Employee Checkins."),
+						}
+					).insert(ignore_permissions=True)
+
+	def get_start_and_end_dates(self, employee):
+		"""Returns start and end dates for checking attendance and marking absent
+		return: start date = max of `process_attendance_after` and DOJ
+		return: end date = min of shift before `last_sync_of_checkin` and Relieving Date
+		"""
+		date_of_joining, relieving_date, employee_creation = frappe.db.get_value(
+			"Employee", employee, ["date_of_joining", "relieving_date", "creation"]
+		)
+
+		if not date_of_joining:
+			date_of_joining = employee_creation.date()
+
+		start_date = max(getdate(self.process_attendance_after), date_of_joining)
+		end_date = None
+
+		shift_details = get_shift_details(self.name, get_datetime(self.last_sync_of_checkin))
+		last_shift_time = (
+			shift_details.actual_start if shift_details else get_datetime(self.last_sync_of_checkin)
+		)
+
+		# check if shift is found for 1 day before the last sync of checkin
+		# absentees are auto-marked 1 day after the shift to wait for any manual attendance records
+		prev_shift = get_employee_shift(employee, last_shift_time - timedelta(days=1), True, "reverse")
+		if prev_shift:
+			end_date = (
+				min(prev_shift.start_datetime.date(), relieving_date)
+				if relieving_date
+				else prev_shift.start_datetime.date()
+			)
+		else:
+			# no shift found
+			return None, None
+		return start_date, end_date
 
 	def get_assigned_employee(self, from_date=None, consider_default_shift=False):
-		filters = {'start_date':('>', from_date), 'shift_type': self.name, 'docstatus': '1'}
-		if not from_date:
-			del filters["start_date"]
+		filters = {"shift_type": self.name, "docstatus": "1"}
+		if from_date:
+			filters["start_date"] = (">", from_date)
 
-		assigned_employees = frappe.get_all('Shift Assignment', 'employee', filters, as_list=True)
-		assigned_employees = [x[0] for x in assigned_employees]
+		assigned_employees = frappe.get_all("Shift Assignment", filters=filters, pluck="employee")
 
 		if consider_default_shift:
-			filters = {'default_shift': self.name, 'status': ['!=', 'Inactive']}
-			default_shift_employees = frappe.get_all('Employee', 'name', filters, as_list=True)
-			default_shift_employees = [x[0] for x in default_shift_employees]
-			return list(set(assigned_employees+default_shift_employees))
+			filters = {"default_shift": self.name, "status": ["!=", "Inactive"]}
+			default_shift_employees = frappe.get_all("Employee", filters=filters, pluck="name")
+
+			return list(set(assigned_employees + default_shift_employees))
 		return assigned_employees
 
+
 def process_auto_attendance_for_all_shifts():
-	shift_list = frappe.get_all('Shift Type', 'name', {'enable_auto_attendance':'1'}, as_list=True)
+	shift_list = frappe.get_all("Shift Type", "name", {"enable_auto_attendance": "1"}, as_list=True)
 	for shift in shift_list:
-		doc = frappe.get_doc('Shift Type', shift[0])
+		doc = frappe.get_doc("Shift Type", shift[0])
 		doc.process_auto_attendance()
-
-def get_filtered_date_list(employee, start_date, end_date, filter_attendance=True, holiday_list=None):
-	"""Returns a list of dates after removing the dates with attendance and holidays
-	"""
-	base_dates_query = """select adddate(%(start_date)s, t2.i*100 + t1.i*10 + t0.i) selected_date from
-		(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
-		(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
-		(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2"""
-	condition_query = ''
-	if filter_attendance:
-		condition_query += """ and a.selected_date not in (
-			select attendance_date from `tabAttendance`
-			where docstatus = 1 and employee = %(employee)s
-			and attendance_date between %(start_date)s and %(end_date)s)"""
-	if holiday_list:
-		condition_query += """ and a.selected_date not in (
-			select holiday_date from `tabHoliday` where parenttype = 'Holiday List' and
-    		parentfield = 'holidays' and parent = %(holiday_list)s
-    		and holiday_date between %(start_date)s and %(end_date)s)"""
-
-	dates = frappe.db.sql("""select * from
-		({base_dates_query}) as a
-		where a.selected_date <= %(end_date)s {condition_query}
-		""".format(base_dates_query=base_dates_query, condition_query=condition_query),
-		{"employee":employee, "start_date":start_date, "end_date":end_date, "holiday_list":holiday_list}, as_list=True)
-
-	return [getdate(date[0]) for date in dates]
diff --git a/erpnext/hr/doctype/shift_type/shift_type_dashboard.py b/erpnext/hr/doctype/shift_type/shift_type_dashboard.py
index 919da2d..920d8fd 100644
--- a/erpnext/hr/doctype/shift_type/shift_type_dashboard.py
+++ b/erpnext/hr/doctype/shift_type/shift_type_dashboard.py
@@ -1,13 +1,8 @@
 def get_data():
 	return {
-		'fieldname': 'shift',
-		'non_standard_fieldnames': {
-			'Shift Request': 'shift_type',
-			'Shift Assignment': 'shift_type'
-		},
-		'transactions': [
-			{
-				'items': ['Attendance', 'Employee Checkin', 'Shift Request', 'Shift Assignment']
-			}
-		]
+		"fieldname": "shift",
+		"non_standard_fieldnames": {"Shift Request": "shift_type", "Shift Assignment": "shift_type"},
+		"transactions": [
+			{"items": ["Attendance", "Employee Checkin", "Shift Request", "Shift Assignment"]}
+		],
 	}
diff --git a/erpnext/hr/doctype/shift_type/test_shift_type.py b/erpnext/hr/doctype/shift_type/test_shift_type.py
index 7d2f29c..0d75292 100644
--- a/erpnext/hr/doctype/shift_type/test_shift_type.py
+++ b/erpnext/hr/doctype/shift_type/test_shift_type.py
@@ -2,7 +2,381 @@
 # See license.txt
 
 import unittest
+from datetime import datetime, timedelta
+
+import frappe
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import add_days, get_time, get_year_ending, get_year_start, getdate, now_datetime
+
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list
+from erpnext.hr.doctype.leave_application.test_leave_application import get_first_sunday
+from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list
 
 
-class TestShiftType(unittest.TestCase):
-	pass
+class TestShiftType(FrappeTestCase):
+	def setUp(self):
+		frappe.db.delete("Shift Type")
+		frappe.db.delete("Shift Assignment")
+		frappe.db.delete("Employee Checkin")
+		frappe.db.delete("Attendance")
+
+		from_date = get_year_start(getdate())
+		to_date = get_year_ending(getdate())
+		self.holiday_list = make_holiday_list(from_date=from_date, to_date=to_date)
+
+	def test_mark_attendance(self):
+		from erpnext.hr.doctype.employee_checkin.test_employee_checkin import make_checkin
+
+		employee = make_employee("test_employee_checkin@example.com", company="_Test Company")
+
+		shift_type = setup_shift_type()
+		date = getdate()
+		make_shift_assignment(shift_type.name, employee, date)
+
+		timestamp = datetime.combine(date, get_time("08:00:00"))
+		log_in = make_checkin(employee, timestamp)
+		self.assertEqual(log_in.shift, shift_type.name)
+
+		timestamp = datetime.combine(date, get_time("12:00:00"))
+		log_out = make_checkin(employee, timestamp)
+		self.assertEqual(log_out.shift, shift_type.name)
+
+		shift_type.process_auto_attendance()
+
+		attendance = frappe.db.get_value(
+			"Attendance", {"shift": shift_type.name}, ["status", "name"], as_dict=True
+		)
+		self.assertEqual(attendance.status, "Present")
+
+	def test_entry_and_exit_grace(self):
+		from erpnext.hr.doctype.employee_checkin.test_employee_checkin import make_checkin
+
+		employee = make_employee("test_employee_checkin@example.com", company="_Test Company")
+
+		# doesn't mark late entry until 60 mins after shift start i.e. till 9
+		# doesn't mark late entry until 60 mins before shift end i.e. 11
+		shift_type = setup_shift_type(
+			enable_entry_grace_period=1,
+			enable_exit_grace_period=1,
+			late_entry_grace_period=60,
+			early_exit_grace_period=60,
+		)
+		date = getdate()
+		make_shift_assignment(shift_type.name, employee, date)
+
+		timestamp = datetime.combine(date, get_time("09:30:00"))
+		log_in = make_checkin(employee, timestamp)
+		self.assertEqual(log_in.shift, shift_type.name)
+
+		timestamp = datetime.combine(date, get_time("10:30:00"))
+		log_out = make_checkin(employee, timestamp)
+		self.assertEqual(log_out.shift, shift_type.name)
+
+		shift_type.process_auto_attendance()
+
+		attendance = frappe.db.get_value(
+			"Attendance",
+			{"shift": shift_type.name},
+			["status", "name", "late_entry", "early_exit"],
+			as_dict=True,
+		)
+		self.assertEqual(attendance.status, "Present")
+		self.assertEqual(attendance.late_entry, 1)
+		self.assertEqual(attendance.early_exit, 1)
+
+	def test_working_hours_threshold_for_half_day(self):
+		from erpnext.hr.doctype.employee_checkin.test_employee_checkin import make_checkin
+
+		employee = make_employee("test_employee_checkin@example.com", company="_Test Company")
+		shift_type = setup_shift_type(shift_type="Half Day Test", working_hours_threshold_for_half_day=2)
+		date = getdate()
+		make_shift_assignment(shift_type.name, employee, date)
+
+		timestamp = datetime.combine(date, get_time("08:00:00"))
+		log_in = make_checkin(employee, timestamp)
+		self.assertEqual(log_in.shift, shift_type.name)
+
+		timestamp = datetime.combine(date, get_time("09:30:00"))
+		log_out = make_checkin(employee, timestamp)
+		self.assertEqual(log_out.shift, shift_type.name)
+
+		shift_type.process_auto_attendance()
+
+		attendance = frappe.db.get_value(
+			"Attendance", {"shift": shift_type.name}, ["status", "working_hours"], as_dict=True
+		)
+		self.assertEqual(attendance.status, "Half Day")
+		self.assertEqual(attendance.working_hours, 1.5)
+
+	def test_working_hours_threshold_for_absent(self):
+		from erpnext.hr.doctype.employee_checkin.test_employee_checkin import make_checkin
+
+		employee = make_employee("test_employee_checkin@example.com", company="_Test Company")
+		shift_type = setup_shift_type(shift_type="Absent Test", working_hours_threshold_for_absent=2)
+		date = getdate()
+		make_shift_assignment(shift_type.name, employee, date)
+
+		timestamp = datetime.combine(date, get_time("08:00:00"))
+		log_in = make_checkin(employee, timestamp)
+		self.assertEqual(log_in.shift, shift_type.name)
+
+		timestamp = datetime.combine(date, get_time("09:30:00"))
+		log_out = make_checkin(employee, timestamp)
+		self.assertEqual(log_out.shift, shift_type.name)
+
+		shift_type.process_auto_attendance()
+
+		attendance = frappe.db.get_value(
+			"Attendance", {"shift": shift_type.name}, ["status", "working_hours"], as_dict=True
+		)
+		self.assertEqual(attendance.status, "Absent")
+		self.assertEqual(attendance.working_hours, 1.5)
+
+	def test_working_hours_threshold_for_absent_and_half_day_1(self):
+		# considers half day over absent
+		from erpnext.hr.doctype.employee_checkin.test_employee_checkin import make_checkin
+
+		employee = make_employee("test_employee_checkin@example.com", company="_Test Company")
+		shift_type = setup_shift_type(
+			shift_type="Half Day + Absent Test",
+			working_hours_threshold_for_half_day=1,
+			working_hours_threshold_for_absent=2,
+		)
+		date = getdate()
+		make_shift_assignment(shift_type.name, employee, date)
+
+		timestamp = datetime.combine(date, get_time("08:00:00"))
+		log_in = make_checkin(employee, timestamp)
+		self.assertEqual(log_in.shift, shift_type.name)
+
+		timestamp = datetime.combine(date, get_time("08:45:00"))
+		log_out = make_checkin(employee, timestamp)
+		self.assertEqual(log_out.shift, shift_type.name)
+
+		shift_type.process_auto_attendance()
+
+		attendance = frappe.db.get_value(
+			"Attendance", {"shift": shift_type.name}, ["status", "working_hours"], as_dict=True
+		)
+		self.assertEqual(attendance.status, "Half Day")
+		self.assertEqual(attendance.working_hours, 0.75)
+
+	def test_working_hours_threshold_for_absent_and_half_day_2(self):
+		# considers absent over half day
+		from erpnext.hr.doctype.employee_checkin.test_employee_checkin import make_checkin
+
+		employee = make_employee("test_employee_checkin@example.com", company="_Test Company")
+		shift_type = setup_shift_type(
+			shift_type="Half Day + Absent Test",
+			working_hours_threshold_for_half_day=1,
+			working_hours_threshold_for_absent=2,
+		)
+		date = getdate()
+		make_shift_assignment(shift_type.name, employee, date)
+
+		timestamp = datetime.combine(date, get_time("08:00:00"))
+		log_in = make_checkin(employee, timestamp)
+		self.assertEqual(log_in.shift, shift_type.name)
+
+		timestamp = datetime.combine(date, get_time("09:30:00"))
+		log_out = make_checkin(employee, timestamp)
+		self.assertEqual(log_out.shift, shift_type.name)
+
+		shift_type.process_auto_attendance()
+
+		attendance = frappe.db.get_value("Attendance", {"shift": shift_type.name}, "status")
+		self.assertEqual(attendance, "Absent")
+
+	def test_mark_absent_for_dates_with_no_attendance(self):
+		employee = make_employee("test_employee_checkin@example.com", company="_Test Company")
+		shift_type = setup_shift_type(shift_type="Test Absent with no Attendance")
+
+		# absentees are auto-marked one day after to wait for any manual attendance records
+		date = add_days(getdate(), -1)
+		make_shift_assignment(shift_type.name, employee, date)
+
+		shift_type.process_auto_attendance()
+
+		attendance = frappe.db.get_value(
+			"Attendance", {"attendance_date": date, "employee": employee}, "status"
+		)
+		self.assertEqual(attendance, "Absent")
+
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
+	def test_skip_marking_absent_on_a_holiday(self):
+		employee = make_employee("test_employee_checkin@example.com", company="_Test Company")
+		shift_type = setup_shift_type(shift_type="Test Absent with no Attendance")
+		shift_type.holiday_list = None
+		shift_type.save()
+
+		# should not mark any attendance if no shift assignment is created
+		shift_type.process_auto_attendance()
+		attendance = frappe.db.get_value("Attendance", {"employee": employee}, "status")
+		self.assertIsNone(attendance)
+
+		first_sunday = get_first_sunday(self.holiday_list, for_date=getdate())
+		make_shift_assignment(shift_type.name, employee, first_sunday)
+
+		shift_type.process_auto_attendance()
+
+		attendance = frappe.db.get_value(
+			"Attendance", {"attendance_date": first_sunday, "employee": employee}, "status"
+		)
+		self.assertIsNone(attendance)
+
+	def test_get_start_and_end_dates(self):
+		date = getdate()
+
+		doj = add_days(date, -30)
+		relieving_date = add_days(date, -5)
+		employee = make_employee(
+			"test_employee_dates@example.com",
+			company="_Test Company",
+			date_of_joining=doj,
+			relieving_date=relieving_date,
+		)
+		shift_type = setup_shift_type(
+			shift_type="Test Absent with no Attendance", process_attendance_after=add_days(doj, 2)
+		)
+
+		make_shift_assignment(shift_type.name, employee, add_days(date, -25))
+
+		shift_type.process_auto_attendance()
+
+		# should not mark absent before shift assignment/process attendance after date
+		attendance = frappe.db.get_value(
+			"Attendance", {"attendance_date": doj, "employee": employee}, "name"
+		)
+		self.assertIsNone(attendance)
+
+		# mark absent on Relieving Date
+		attendance = frappe.db.get_value(
+			"Attendance", {"attendance_date": relieving_date, "employee": employee}, "status"
+		)
+		self.assertEquals(attendance, "Absent")
+
+		# should not mark absent after Relieving Date
+		attendance = frappe.db.get_value(
+			"Attendance", {"attendance_date": add_days(relieving_date, 1), "employee": employee}, "name"
+		)
+		self.assertIsNone(attendance)
+
+	def test_skip_auto_attendance_for_duplicate_record(self):
+		# Skip auto attendance in case of duplicate attendance record
+		from erpnext.hr.doctype.attendance.attendance import mark_attendance
+		from erpnext.hr.doctype.employee_checkin.test_employee_checkin import make_checkin
+
+		employee = make_employee("test_employee_checkin@example.com", company="_Test Company")
+
+		shift_type = setup_shift_type()
+		date = getdate()
+
+		# mark attendance
+		mark_attendance(employee, date, "Present")
+		make_shift_assignment(shift_type.name, employee, date)
+
+		timestamp = datetime.combine(date, get_time("08:00:00"))
+		log_in = make_checkin(employee, timestamp)
+		self.assertEqual(log_in.shift, shift_type.name)
+
+		timestamp = datetime.combine(date, get_time("12:00:00"))
+		log_out = make_checkin(employee, timestamp)
+		self.assertEqual(log_out.shift, shift_type.name)
+
+		# auto attendance should skip marking
+		shift_type.process_auto_attendance()
+
+		log_in.reload()
+		log_out.reload()
+		self.assertEqual(log_in.skip_auto_attendance, 1)
+		self.assertEqual(log_out.skip_auto_attendance, 1)
+
+	def test_skip_auto_attendance_for_overlapping_shift(self):
+		# Skip auto attendance in case of overlapping shift attendance record
+		# this case won't occur in case of shift assignment, since it will not allow overlapping shifts to be assigned
+		# can happen if manual attendance records are created
+		from erpnext.hr.doctype.attendance.attendance import mark_attendance
+		from erpnext.hr.doctype.employee_checkin.test_employee_checkin import make_checkin
+
+		employee = make_employee("test_employee_checkin@example.com", company="_Test Company")
+		shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00")
+		shift_2 = setup_shift_type(shift_type="Shift 2", start_time="09:30:00", end_time="11:00:00")
+
+		date = getdate()
+
+		# mark attendance
+		mark_attendance(employee, date, "Present", shift=shift_1.name)
+		make_shift_assignment(shift_2.name, employee, date)
+
+		timestamp = datetime.combine(date, get_time("09:30:00"))
+		log_in = make_checkin(employee, timestamp)
+		self.assertEqual(log_in.shift, shift_2.name)
+
+		timestamp = datetime.combine(date, get_time("11:00:00"))
+		log_out = make_checkin(employee, timestamp)
+		self.assertEqual(log_out.shift, shift_2.name)
+
+		# auto attendance should be skipped for shift 2
+		# since it is already marked for overlapping shift 1
+		shift_2.process_auto_attendance()
+
+		log_in.reload()
+		log_out.reload()
+		self.assertEqual(log_in.skip_auto_attendance, 1)
+		self.assertEqual(log_out.skip_auto_attendance, 1)
+
+
+def setup_shift_type(**args):
+	args = frappe._dict(args)
+	date = getdate()
+
+	shift_type = frappe.get_doc(
+		{
+			"doctype": "Shift Type",
+			"__newname": args.shift_type or "_Test Shift",
+			"start_time": "08:00:00",
+			"end_time": "12:00:00",
+			"enable_auto_attendance": 1,
+			"determine_check_in_and_check_out": "Alternating entries as IN and OUT during the same shift",
+			"working_hours_calculation_based_on": "First Check-in and Last Check-out",
+			"begin_check_in_before_shift_start_time": 60,
+			"allow_check_out_after_shift_end_time": 60,
+			"process_attendance_after": add_days(date, -2),
+			"last_sync_of_checkin": now_datetime() + timedelta(days=1),
+		}
+	)
+
+	holiday_list = "Employee Checkin Test Holiday List"
+	if not frappe.db.exists("Holiday List", "Employee Checkin Test Holiday List"):
+		holiday_list = frappe.get_doc(
+			{
+				"doctype": "Holiday List",
+				"holiday_list_name": "Employee Checkin Test Holiday List",
+				"from_date": get_year_start(date),
+				"to_date": get_year_ending(date),
+			}
+		).insert()
+		holiday_list = holiday_list.name
+
+	shift_type.holiday_list = holiday_list
+	shift_type.update(args)
+	shift_type.save()
+
+	return shift_type
+
+
+def make_shift_assignment(shift_type, employee, start_date, end_date=None):
+	shift_assignment = frappe.get_doc(
+		{
+			"doctype": "Shift Assignment",
+			"shift_type": shift_type,
+			"company": "_Test Company",
+			"employee": employee,
+			"start_date": start_date,
+			"end_date": end_date,
+		}
+	).insert()
+	shift_assignment.submit()
+
+	return shift_assignment
diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.py b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
index 7b2ea21..82472de 100644
--- a/erpnext/hr/doctype/staffing_plan/staffing_plan.py
+++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
@@ -9,8 +9,13 @@
 from frappe.utils.nestedset import get_descendants_of
 
 
-class SubsidiaryCompanyError(frappe.ValidationError): pass
-class ParentCompanyError(frappe.ValidationError): pass
+class SubsidiaryCompanyError(frappe.ValidationError):
+	pass
+
+
+class ParentCompanyError(frappe.ValidationError):
+	pass
+
 
 class StaffingPlan(Document):
 	def validate(self):
@@ -33,11 +38,11 @@
 		self.total_estimated_budget = 0
 
 		for detail in self.get("staffing_details"):
-			#Set readonly fields
+			# Set readonly fields
 			self.set_number_of_positions(detail)
 			designation_counts = get_designation_counts(detail.designation, self.company)
-			detail.current_count = designation_counts['employee_count']
-			detail.current_openings = designation_counts['job_openings']
+			detail.current_count = designation_counts["employee_count"]
+			detail.current_openings = designation_counts["job_openings"]
 
 			detail.total_estimated_cost = 0
 			if detail.number_of_positions > 0:
@@ -52,105 +57,140 @@
 	def validate_overlap(self, staffing_plan_detail):
 		# Validate if any submitted Staffing Plan exist for any Designations in this plan
 		# and spd.vacancies>0 ?
-		overlap = frappe.db.sql("""select spd.parent
+		overlap = frappe.db.sql(
+			"""select spd.parent
 			from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name
 			where spd.designation=%s and sp.docstatus=1
 			and sp.to_date >= %s and sp.from_date <= %s and sp.company = %s
-		""", (staffing_plan_detail.designation, self.from_date, self.to_date, self.company))
-		if overlap and overlap [0][0]:
-			frappe.throw(_("Staffing Plan {0} already exist for designation {1}")
-				.format(overlap[0][0], staffing_plan_detail.designation))
+		""",
+			(staffing_plan_detail.designation, self.from_date, self.to_date, self.company),
+		)
+		if overlap and overlap[0][0]:
+			frappe.throw(
+				_("Staffing Plan {0} already exist for designation {1}").format(
+					overlap[0][0], staffing_plan_detail.designation
+				)
+			)
 
 	def validate_with_parent_plan(self, staffing_plan_detail):
-		if not frappe.get_cached_value('Company',  self.company,  "parent_company"):
-			return # No parent, nothing to validate
+		if not frappe.get_cached_value("Company", self.company, "parent_company"):
+			return  # No parent, nothing to validate
 
 		# Get staffing plan applicable for the company (Parent Company)
-		parent_plan_details = get_active_staffing_plan_details(self.company, staffing_plan_detail.designation, self.from_date, self.to_date)
+		parent_plan_details = get_active_staffing_plan_details(
+			self.company, staffing_plan_detail.designation, self.from_date, self.to_date
+		)
 		if not parent_plan_details:
-			return #no staffing plan for any parent Company in hierarchy
+			return  # no staffing plan for any parent Company in hierarchy
 
 		# Fetch parent company which owns the staffing plan. NOTE: Parent could be higher up in the hierarchy
 		parent_company = frappe.db.get_value("Staffing Plan", parent_plan_details[0].name, "company")
 		# Parent plan available, validate with parent, siblings as well as children of staffing plan Company
-		if cint(staffing_plan_detail.vacancies) > cint(parent_plan_details[0].vacancies) or \
-			flt(staffing_plan_detail.total_estimated_cost) > flt(parent_plan_details[0].total_estimated_cost):
-			frappe.throw(_("You can only plan for upto {0} vacancies and budget {1} \
-				for {2} as per staffing plan {3} for parent company {4}.").format(
+		if cint(staffing_plan_detail.vacancies) > cint(parent_plan_details[0].vacancies) or flt(
+			staffing_plan_detail.total_estimated_cost
+		) > flt(parent_plan_details[0].total_estimated_cost):
+			frappe.throw(
+				_(
+					"You can only plan for upto {0} vacancies and budget {1} for {2} as per staffing plan {3} for parent company {4}."
+				).format(
 					cint(parent_plan_details[0].vacancies),
 					parent_plan_details[0].total_estimated_cost,
 					frappe.bold(staffing_plan_detail.designation),
 					parent_plan_details[0].name,
-					parent_company), ParentCompanyError)
+					parent_company,
+				),
+				ParentCompanyError,
+			)
 
-		#Get vacanices already planned for all companies down the hierarchy of Parent Company
-		lft, rgt = frappe.get_cached_value('Company',  parent_company,  ["lft", "rgt"])
-		all_sibling_details = frappe.db.sql("""select sum(spd.vacancies) as vacancies,
+		# Get vacanices already planned for all companies down the hierarchy of Parent Company
+		lft, rgt = frappe.get_cached_value("Company", parent_company, ["lft", "rgt"])
+		all_sibling_details = frappe.db.sql(
+			"""select sum(spd.vacancies) as vacancies,
 			sum(spd.total_estimated_cost) as total_estimated_cost
 			from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name
 			where spd.designation=%s and sp.docstatus=1
 			and sp.to_date >= %s and sp.from_date <=%s
 			and sp.company in (select name from tabCompany where lft > %s and rgt < %s)
-		""", (staffing_plan_detail.designation, self.from_date, self.to_date, lft, rgt), as_dict = 1)[0]
+		""",
+			(staffing_plan_detail.designation, self.from_date, self.to_date, lft, rgt),
+			as_dict=1,
+		)[0]
 
-		if (cint(parent_plan_details[0].vacancies) < \
-			(cint(staffing_plan_detail.vacancies) + cint(all_sibling_details.vacancies))) or \
-			(flt(parent_plan_details[0].total_estimated_cost) < \
-			(flt(staffing_plan_detail.total_estimated_cost) + flt(all_sibling_details.total_estimated_cost))):
-			frappe.throw(_("{0} vacancies and {1} budget for {2} already planned for subsidiary companies of {3}. \
-				You can only plan for upto {4} vacancies and and budget {5} as per staffing plan {6} for parent company {3}.").format(
+		if (
+			cint(parent_plan_details[0].vacancies)
+			< (cint(staffing_plan_detail.vacancies) + cint(all_sibling_details.vacancies))
+		) or (
+			flt(parent_plan_details[0].total_estimated_cost)
+			< (
+				flt(staffing_plan_detail.total_estimated_cost) + flt(all_sibling_details.total_estimated_cost)
+			)
+		):
+			frappe.throw(
+				_(
+					"{0} vacancies and {1} budget for {2} already planned for subsidiary companies of {3}. You can only plan for upto {4} vacancies and and budget {5} as per staffing plan {6} for parent company {3}."
+				).format(
 					cint(all_sibling_details.vacancies),
 					all_sibling_details.total_estimated_cost,
 					frappe.bold(staffing_plan_detail.designation),
 					parent_company,
 					cint(parent_plan_details[0].vacancies),
 					parent_plan_details[0].total_estimated_cost,
-					parent_plan_details[0].name))
+					parent_plan_details[0].name,
+				)
+			)
 
 	def validate_with_subsidiary_plans(self, staffing_plan_detail):
-		#Valdate this plan with all child company plan
-		children_details = frappe.db.sql("""select sum(spd.vacancies) as vacancies,
+		# Valdate this plan with all child company plan
+		children_details = frappe.db.sql(
+			"""select sum(spd.vacancies) as vacancies,
 			sum(spd.total_estimated_cost) as total_estimated_cost
 			from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name
 			where spd.designation=%s and sp.docstatus=1
 			and sp.to_date >= %s and sp.from_date <=%s
 			and sp.company in (select name from tabCompany where parent_company = %s)
-		""", (staffing_plan_detail.designation, self.from_date, self.to_date, self.company), as_dict = 1)[0]
+		""",
+			(staffing_plan_detail.designation, self.from_date, self.to_date, self.company),
+			as_dict=1,
+		)[0]
 
-		if children_details and \
-			cint(staffing_plan_detail.vacancies) < cint(children_details.vacancies) or \
-			flt(staffing_plan_detail.total_estimated_cost) < flt(children_details.total_estimated_cost):
-			frappe.throw(_("Subsidiary companies have already planned for {1} vacancies at a budget of {2}. \
-				Staffing Plan for {0} should allocate more vacancies and budget for {3} than planned for its subsidiary companies").format(
+		if (
+			children_details
+			and cint(staffing_plan_detail.vacancies) < cint(children_details.vacancies)
+			or flt(staffing_plan_detail.total_estimated_cost) < flt(children_details.total_estimated_cost)
+		):
+			frappe.throw(
+				_(
+					"Subsidiary companies have already planned for {1} vacancies at a budget of {2}. Staffing Plan for {0} should allocate more vacancies and budget for {3} than planned for its subsidiary companies"
+				).format(
 					self.company,
 					cint(children_details.vacancies),
 					children_details.total_estimated_cost,
-					frappe.bold(staffing_plan_detail.designation)), SubsidiaryCompanyError)
+					frappe.bold(staffing_plan_detail.designation),
+				),
+				SubsidiaryCompanyError,
+			)
+
 
 @frappe.whitelist()
-def get_designation_counts(designation, company):
+def get_designation_counts(designation, company, job_opening=None):
 	if not designation:
 		return False
 
-	employee_counts = {}
-	company_set = get_descendants_of('Company', company)
+	company_set = get_descendants_of("Company", company)
 	company_set.append(company)
 
-	employee_counts["employee_count"] = frappe.db.get_value("Employee",
-		filters={
-			'designation': designation,
-			'status': 'Active',
-			'company': ('in', company_set)
-		}, fieldname=['count(name)'])
+	employee_count = frappe.db.count(
+		"Employee", {"designation": designation, "status": "Active", "company": ("in", company_set)}
+	)
 
-	employee_counts['job_openings'] = frappe.db.get_value("Job Opening",
-		filters={
-			'designation': designation,
-			'status': 'Open',
-			'company': ('in', company_set)
-		}, fieldname=['count(name)'])
+	filters = {"designation": designation, "status": "Open", "company": ("in", company_set)}
+	if job_opening:
+		filters["name"] = ("!=", job_opening)
 
-	return employee_counts
+	job_openings = frappe.db.count("Job Opening", filters)
+
+	return {"employee_count": employee_count, "job_openings": job_openings}
+
 
 @frappe.whitelist()
 def get_active_staffing_plan_details(company, designation, from_date=None, to_date=None):
@@ -161,17 +201,22 @@
 	if not company or not designation:
 		frappe.throw(_("Please select Company and Designation"))
 
-	staffing_plan = frappe.db.sql("""
+	staffing_plan = frappe.db.sql(
+		"""
 		select sp.name, spd.vacancies, spd.total_estimated_cost
 		from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name
 		where company=%s and spd.designation=%s and sp.docstatus=1
-		and to_date >= %s and from_date <= %s """, (company, designation, from_date, to_date), as_dict = 1)
+		and to_date >= %s and from_date <= %s """,
+		(company, designation, from_date, to_date),
+		as_dict=1,
+	)
 
 	if not staffing_plan:
-		parent_company = frappe.get_cached_value('Company',  company,  "parent_company")
+		parent_company = frappe.get_cached_value("Company", company, "parent_company")
 		if parent_company:
-			staffing_plan = get_active_staffing_plan_details(parent_company,
-				designation, from_date, to_date)
+			staffing_plan = get_active_staffing_plan_details(
+				parent_company, designation, from_date, to_date
+			)
 
 	# Only a single staffing plan can be active for a designation on given date
 	return staffing_plan if staffing_plan else None
diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan_dashboard.py b/erpnext/hr/doctype/staffing_plan/staffing_plan_dashboard.py
index abde0d5..0f555d9 100644
--- a/erpnext/hr/doctype/staffing_plan/staffing_plan_dashboard.py
+++ b/erpnext/hr/doctype/staffing_plan/staffing_plan_dashboard.py
@@ -1,9 +1,5 @@
 def get_data():
-     return {
-        'fieldname': 'staffing_plan',
-        'transactions': [
-            {
-                'items': ['Job Opening']
-            }
-        ],
-    }
+	return {
+		"fieldname": "staffing_plan",
+		"transactions": [{"items": ["Job Opening"]}],
+	}
diff --git a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py
index 8ff0dbb..ac69c21 100644
--- a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py
+++ b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py
@@ -13,6 +13,7 @@
 
 test_dependencies = ["Designation"]
 
+
 class TestStaffingPlan(unittest.TestCase):
 	def test_staffing_plan(self):
 		_set_up()
@@ -24,11 +25,10 @@
 		staffing_plan.name = "Test"
 		staffing_plan.from_date = nowdate()
 		staffing_plan.to_date = add_days(nowdate(), 10)
-		staffing_plan.append("staffing_details", {
-			"designation": "Designer",
-			"vacancies": 6,
-			"estimated_cost_per_position": 50000
-		})
+		staffing_plan.append(
+			"staffing_details",
+			{"designation": "Designer", "vacancies": 6, "estimated_cost_per_position": 50000},
+		)
 		staffing_plan.insert()
 		staffing_plan.submit()
 		self.assertEqual(staffing_plan.total_estimated_budget, 300000.00)
@@ -42,11 +42,10 @@
 		staffing_plan.name = "Test 1"
 		staffing_plan.from_date = nowdate()
 		staffing_plan.to_date = add_days(nowdate(), 10)
-		staffing_plan.append("staffing_details", {
-			"designation": "Designer",
-			"vacancies": 3,
-			"estimated_cost_per_position": 45000
-		})
+		staffing_plan.append(
+			"staffing_details",
+			{"designation": "Designer", "vacancies": 3, "estimated_cost_per_position": 45000},
+		)
 		self.assertRaises(SubsidiaryCompanyError, staffing_plan.insert)
 
 	def test_staffing_plan_parent_company(self):
@@ -58,11 +57,10 @@
 		staffing_plan.name = "Test"
 		staffing_plan.from_date = nowdate()
 		staffing_plan.to_date = add_days(nowdate(), 10)
-		staffing_plan.append("staffing_details", {
-			"designation": "Designer",
-			"vacancies": 7,
-			"estimated_cost_per_position": 50000
-		})
+		staffing_plan.append(
+			"staffing_details",
+			{"designation": "Designer", "vacancies": 7, "estimated_cost_per_position": 50000},
+		)
 		staffing_plan.insert()
 		staffing_plan.submit()
 		self.assertEqual(staffing_plan.total_estimated_budget, 350000.00)
@@ -73,26 +71,30 @@
 		staffing_plan.name = "Test 1"
 		staffing_plan.from_date = nowdate()
 		staffing_plan.to_date = add_days(nowdate(), 10)
-		staffing_plan.append("staffing_details", {
-			"designation": "Designer",
-			"vacancies": 7,
-			"estimated_cost_per_position": 60000
-		})
+		staffing_plan.append(
+			"staffing_details",
+			{"designation": "Designer", "vacancies": 7, "estimated_cost_per_position": 60000},
+		)
 		staffing_plan.insert()
 		self.assertRaises(ParentCompanyError, staffing_plan.submit)
 
+
 def _set_up():
 	for doctype in ["Staffing Plan", "Staffing Plan Detail"]:
 		frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype))
 	make_company()
 
-def make_company():
-	if frappe.db.exists("Company", "_Test Company 10"):
+
+def make_company(name=None, abbr=None):
+	if not name:
+		name = "_Test Company 10"
+
+	if frappe.db.exists("Company", name):
 		return
 
 	company = frappe.new_doc("Company")
-	company.company_name = "_Test Company 10"
-	company.abbr = "_TC10"
+	company.company_name = name
+	company.abbr = abbr or "_TC10"
 	company.parent_company = "_Test Company 3"
 	company.default_currency = "INR"
 	company.country = "Pakistan"
diff --git a/erpnext/hr/doctype/training_event/test_training_event.py b/erpnext/hr/doctype/training_event/test_training_event.py
index f4329c9..ec7eb74 100644
--- a/erpnext/hr/doctype/training_event/test_training_event.py
+++ b/erpnext/hr/doctype/training_event/test_training_event.py
@@ -14,10 +14,7 @@
 		create_training_program("Basic Training")
 		employee = make_employee("robert_loan@trainig.com")
 		employee2 = make_employee("suzie.tan@trainig.com")
-		self.attendees = [
-			{"employee": employee},
-			{"employee": employee2}
-		]
+		self.attendees = [{"employee": employee}, {"employee": employee2}]
 
 	def test_training_event_status_update(self):
 		training_event = create_training_event(self.attendees)
@@ -43,20 +40,25 @@
 
 def create_training_program(training_program):
 	if not frappe.db.get_value("Training Program", training_program):
-		frappe.get_doc({
-			"doctype": "Training Program",
-			"training_program": training_program,
-			"description": training_program
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Training Program",
+				"training_program": training_program,
+				"description": training_program,
+			}
+		).insert()
+
 
 def create_training_event(attendees):
-	return frappe.get_doc({
-		"doctype": "Training Event",
-		"event_name": "Basic Training Event",
-		"training_program": "Basic Training",
-		"location": "Union Square",
-		"start_time": add_days(today(), 5),
-		"end_time": add_days(today(), 6),
-		"introduction": "Welcome to the Basic Training Event",
-		"employees": attendees
-	}).insert()
+	return frappe.get_doc(
+		{
+			"doctype": "Training Event",
+			"event_name": "Basic Training Event",
+			"training_program": "Basic Training",
+			"location": "Union Square",
+			"start_time": add_days(today(), 5),
+			"end_time": add_days(today(), 6),
+			"introduction": "Welcome to the Basic Training Event",
+			"employees": attendees,
+		}
+	).insert()
diff --git a/erpnext/hr/doctype/training_event/training_event.json b/erpnext/hr/doctype/training_event/training_event.json
index fcf845a..42e02ca 100644
--- a/erpnext/hr/doctype/training_event/training_event.json
+++ b/erpnext/hr/doctype/training_event/training_event.json
@@ -1,850 +1,231 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 1, 
- "allow_rename": 1, 
- "autoname": "field:event_name", 
- "beta": 0, 
- "creation": "2016-08-08 04:53:58.355206", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
+ "actions": [],
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "field:event_name",
+ "creation": "2016-08-08 04:53:58.355206",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "event_name",
+  "training_program",
+  "event_status",
+  "has_certificate",
+  "column_break_2",
+  "type",
+  "level",
+  "company",
+  "section_break_4",
+  "trainer_name",
+  "trainer_email",
+  "column_break_7",
+  "supplier",
+  "contact_number",
+  "section_break_9",
+  "course",
+  "location",
+  "column_break_12",
+  "start_time",
+  "end_time",
+  "section_break_15",
+  "introduction",
+  "section_break_18",
+  "employees",
+  "amended_from",
+  "employee_emails"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "event_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": "Event Name", 
-   "length": 0, 
-   "no_copy": 1, 
-   "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
-  }, 
+   "fieldname": "event_name",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Event Name",
+   "no_copy": 1,
+   "reqd": 1,
+   "unique": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "training_program", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Training Program", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Training Program", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "training_program",
+   "fieldtype": "Link",
+   "label": "Training Program",
+   "options": "Training Program"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 1, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "event_status", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 1, 
-   "label": "Event Status", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Scheduled\nCompleted\nCancelled", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "allow_on_submit": 1,
+   "fieldname": "event_status",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Event Status",
+   "options": "Scheduled\nCompleted\nCancelled",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:doc.type == 'Seminar' || doc.type == 'Workshop' || doc.type == 'Conference' || doc.type == 'Exam'", 
-   "fieldname": "has_certificate", 
-   "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": "Has Certificate", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "default": "0",
+   "depends_on": "eval:doc.type == 'Seminar' || doc.type == 'Workshop' || doc.type == 'Conference' || doc.type == 'Exam'",
+   "fieldname": "has_certificate",
+   "fieldtype": "Check",
+   "label": "Has Certificate"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_2", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "type", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 1, 
-   "label": "Type", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Seminar\nTheory\nWorkshop\nConference\nExam\nInternet\nSelf-Study", 
-   "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
-  }, 
+   "fieldname": "type",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Type",
+   "options": "Seminar\nTheory\nWorkshop\nConference\nExam\nInternet\nSelf-Study",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:doc.type == 'Seminar' || doc.type == 'Workshop' || doc.type == 'Exam'", 
-   "fieldname": "level", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Level", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "\nBeginner\nIntermediate\nAdvance", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "eval:doc.type == 'Seminar' || doc.type == 'Workshop' || doc.type == 'Exam'",
+   "fieldname": "level",
+   "fieldtype": "Select",
+   "label": "Level",
+   "options": "\nBeginner\nIntermediate\nAdvance"
+  },
   {
-   "allow_bulk_edit": 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": 0, 
-   "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, 
-   "unique": 0
-  }, 
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_4", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_4",
+   "fieldtype": "Section Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "trainer_name", 
-   "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": "Trainer 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, 
-   "unique": 0
-  }, 
+   "fieldname": "trainer_name",
+   "fieldtype": "Data",
+   "label": "Trainer Name"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "trainer_email", 
-   "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": "Trainer Email", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "trainer_email",
+   "fieldtype": "Data",
+   "label": "Trainer Email"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_7", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_7",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "supplier", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Supplier", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Supplier", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "supplier",
+   "fieldtype": "Link",
+   "label": "Supplier",
+   "options": "Supplier"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "contact_number", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Contact Number", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "contact_number",
+   "fieldtype": "Data",
+   "label": "Contact Number"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_9", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_9",
+   "fieldtype": "Section Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "course", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 1, 
-   "label": "Course", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Course", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "course",
+   "fieldtype": "Data",
+   "in_standard_filter": 1,
+   "label": "Course"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "location", 
-   "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": 1, 
-   "label": "Location", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "location",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Location",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_12", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_12",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "start_time", 
-   "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": "Start Time", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "start_time",
+   "fieldtype": "Datetime",
+   "label": "Start Time",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "end_time", 
-   "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": "End Time", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "end_time",
+   "fieldtype": "Datetime",
+   "label": "End Time",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_15", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_15",
+   "fieldtype": "Section Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "introduction", 
-   "fieldtype": "Text Editor", 
-   "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": "Introduction", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "introduction",
+   "fieldtype": "Text Editor",
+   "label": "Introduction",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_18", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Attendees", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_18",
+   "fieldtype": "Section Break",
+   "label": "Attendees"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 1, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "employees", 
-   "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": "Employees", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Training Event Employee", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "allow_on_submit": 1,
+   "fieldname": "employees",
+   "fieldtype": "Table",
+   "label": "Employees",
+   "options": "Training Event Employee"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "amended_from", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Amended From", 
-   "length": 0, 
-   "no_copy": 1, 
-   "options": "Training Event", 
-   "permlevel": 0, 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Training Event",
+   "print_hide": 1,
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "employee_emails", 
-   "fieldtype": "Small Text", 
-   "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": "Employee Emails", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Email", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
+   "fieldname": "employee_emails",
+   "fieldtype": "Small Text",
+   "hidden": 1,
+   "label": "Employee Emails",
+   "options": "Email"
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 1, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2019-03-12 10:56:29.065781", 
- "modified_by": "Administrator", 
- "module": "HR", 
- "name": "Training Event", 
- "name_case": "", 
- "owner": "Administrator", 
+ ],
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2022-04-28 13:29:35.139497",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Training Event",
+ "naming_rule": "By fieldname",
+ "owner": "Administrator",
  "permissions": [
   {
-   "amend": 1, 
-   "apply_user_permissions": 0, 
-   "cancel": 1, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 1, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "HR Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 1, 
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "import": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "submit": 1,
    "write": 1
   }
- ], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "search_fields": "event_name", 
- "show_name_in_global_search": 1, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "title_field": "event_name", 
- "track_changes": 0, 
- "track_seen": 0
-}
+ ],
+ "search_fields": "event_name",
+ "show_name_in_global_search": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": [],
+ "title_field": "event_name"
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/training_event/training_event.py b/erpnext/hr/doctype/training_event/training_event.py
index c8c8bbe..59972bb 100644
--- a/erpnext/hr/doctype/training_event/training_event.py
+++ b/erpnext/hr/doctype/training_event/training_event.py
@@ -19,21 +19,20 @@
 		self.set_status_for_attendees()
 
 	def set_employee_emails(self):
-		self.employee_emails = ', '.join(get_employee_emails([d.employee
-			for d in self.employees]))
+		self.employee_emails = ", ".join(get_employee_emails([d.employee for d in self.employees]))
 
 	def validate_period(self):
 		if time_diff_in_seconds(self.end_time, self.start_time) <= 0:
-			frappe.throw(_('End time cannot be before start time'))
+			frappe.throw(_("End time cannot be before start time"))
 
 	def set_status_for_attendees(self):
-		if self.event_status == 'Completed':
+		if self.event_status == "Completed":
 			for employee in self.employees:
-				if employee.attendance == 'Present' and employee.status != 'Feedback Submitted':
-					employee.status = 'Completed'
+				if employee.attendance == "Present" and employee.status != "Feedback Submitted":
+					employee.status = "Completed"
 
-		elif self.event_status == 'Scheduled':
+		elif self.event_status == "Scheduled":
 			for employee in self.employees:
-				employee.status = 'Open'
+				employee.status = "Open"
 
 		self.db_update_all()
diff --git a/erpnext/hr/doctype/training_event/training_event_dashboard.py b/erpnext/hr/doctype/training_event/training_event_dashboard.py
index 141fffc..ca13938 100644
--- a/erpnext/hr/doctype/training_event/training_event_dashboard.py
+++ b/erpnext/hr/doctype/training_event/training_event_dashboard.py
@@ -1,9 +1,7 @@
 def get_data():
-     return {
-        'fieldname': 'training_event',
-        'transactions': [
-            {
-                'items': ['Training Result', 'Training Feedback']
-            },
-        ],
-    }
+	return {
+		"fieldname": "training_event",
+		"transactions": [
+			{"items": ["Training Result", "Training Feedback"]},
+		],
+	}
diff --git a/erpnext/hr/doctype/training_feedback/test_training_feedback.py b/erpnext/hr/doctype/training_feedback/test_training_feedback.py
index 58ed623..c787b70 100644
--- a/erpnext/hr/doctype/training_feedback/test_training_feedback.py
+++ b/erpnext/hr/doctype/training_feedback/test_training_feedback.py
@@ -32,10 +32,9 @@
 		self.assertRaises(frappe.ValidationError, feedback.save)
 
 		# cannot record feedback for absent employee
-		employee = frappe.db.get_value("Training Event Employee", {
-			"parent": training_event.name,
-			"employee": self.employee
-		}, "name")
+		employee = frappe.db.get_value(
+			"Training Event Employee", {"parent": training_event.name, "employee": self.employee}, "name"
+		)
 
 		frappe.db.set_value("Training Event Employee", employee, "attendance", "Absent")
 		feedback = create_training_feedback(training_event.name, self.employee)
@@ -52,10 +51,9 @@
 		feedback = create_training_feedback(training_event.name, self.employee)
 		feedback.submit()
 
-		status = frappe.db.get_value("Training Event Employee", {
-			"parent": training_event.name,
-			"employee": self.employee
-		}, "status")
+		status = frappe.db.get_value(
+			"Training Event Employee", {"parent": training_event.name, "employee": self.employee}, "status"
+		)
 
 		self.assertEqual(status, "Feedback Submitted")
 
@@ -64,9 +62,11 @@
 
 
 def create_training_feedback(event, employee):
-	return frappe.get_doc({
-		"doctype": "Training Feedback",
-		"training_event": event,
-		"employee": employee,
-		"feedback": "Test"
-	})
+	return frappe.get_doc(
+		{
+			"doctype": "Training Feedback",
+			"training_event": event,
+			"employee": employee,
+			"feedback": "Test",
+		}
+	)
diff --git a/erpnext/hr/doctype/training_feedback/training_feedback.json b/erpnext/hr/doctype/training_feedback/training_feedback.json
index ebf5a50..e968911 100644
--- a/erpnext/hr/doctype/training_feedback/training_feedback.json
+++ b/erpnext/hr/doctype/training_feedback/training_feedback.json
@@ -1,7 +1,7 @@
 {
  "actions": [],
  "autoname": "HR-TRF-.YYYY.-.#####",
- "creation": "2016-08-08 06:35:34.158568",
+ "creation": "2022-01-27 13:14:35.935580",
  "doctype": "DocType",
  "editable_grid": 1,
  "engine": "InnoDB",
@@ -46,9 +46,8 @@
   {
    "fetch_from": "training_event.course",
    "fieldname": "course",
-   "fieldtype": "Link",
+   "fieldtype": "Data",
    "label": "Course",
-   "options": "Course",
    "read_only": 1
   },
   {
@@ -101,7 +100,7 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2022-01-18 19:32:20.805277",
+ "modified": "2022-04-28 13:32:29.261421",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Training Feedback",
diff --git a/erpnext/hr/doctype/training_feedback/training_feedback.py b/erpnext/hr/doctype/training_feedback/training_feedback.py
index 1f9ec3b..d5de28e 100644
--- a/erpnext/hr/doctype/training_feedback/training_feedback.py
+++ b/erpnext/hr/doctype/training_feedback/training_feedback.py
@@ -13,32 +13,35 @@
 		if training_event.docstatus != 1:
 			frappe.throw(_("{0} must be submitted").format(_("Training Event")))
 
-		emp_event_details = frappe.db.get_value("Training Event Employee", {
-			"parent": self.training_event,
-			"employee": self.employee
-		}, ["name", "attendance"], as_dict=True)
+		emp_event_details = frappe.db.get_value(
+			"Training Event Employee",
+			{"parent": self.training_event, "employee": self.employee},
+			["name", "attendance"],
+			as_dict=True,
+		)
 
 		if not emp_event_details:
-			frappe.throw(_("Employee {0} not found in Training Event Participants.").format(
-				frappe.bold(self.employee_name)))
+			frappe.throw(
+				_("Employee {0} not found in Training Event Participants.").format(
+					frappe.bold(self.employee_name)
+				)
+			)
 
 		if emp_event_details.attendance == "Absent":
 			frappe.throw(_("Feedback cannot be recorded for an absent Employee."))
 
 	def on_submit(self):
-		employee = frappe.db.get_value("Training Event Employee", {
-			"parent": self.training_event,
-			"employee": self.employee
-		})
+		employee = frappe.db.get_value(
+			"Training Event Employee", {"parent": self.training_event, "employee": self.employee}
+		)
 
 		if employee:
 			frappe.db.set_value("Training Event Employee", employee, "status", "Feedback Submitted")
 
 	def on_cancel(self):
-		employee = frappe.db.get_value("Training Event Employee", {
-			"parent": self.training_event,
-			"employee": self.employee
-		})
+		employee = frappe.db.get_value(
+			"Training Event Employee", {"parent": self.training_event, "employee": self.employee}
+		)
 
 		if employee:
 			frappe.db.set_value("Training Event Employee", employee, "status", "Completed")
diff --git a/erpnext/hr/doctype/training_program/training_program_dashboard.py b/erpnext/hr/doctype/training_program/training_program_dashboard.py
index 374c1e8..1735db1 100644
--- a/erpnext/hr/doctype/training_program/training_program_dashboard.py
+++ b/erpnext/hr/doctype/training_program/training_program_dashboard.py
@@ -3,11 +3,8 @@
 
 def get_data():
 	return {
-		'fieldname': 'training_program',
-		'transactions': [
-			{
-				'label': _('Training Events'),
-				'items': ['Training Event']
-			},
-		]
+		"fieldname": "training_program",
+		"transactions": [
+			{"label": _("Training Events"), "items": ["Training Event"]},
+		],
 	}
diff --git a/erpnext/hr/doctype/training_result/test_training_result.py b/erpnext/hr/doctype/training_result/test_training_result.py
index 1735ff4..136543c 100644
--- a/erpnext/hr/doctype/training_result/test_training_result.py
+++ b/erpnext/hr/doctype/training_result/test_training_result.py
@@ -5,5 +5,6 @@
 
 # test_records = frappe.get_test_records('Training Result')
 
+
 class TestTrainingResult(unittest.TestCase):
 	pass
diff --git a/erpnext/hr/doctype/training_result/training_result.py b/erpnext/hr/doctype/training_result/training_result.py
index bb5c71e..48a5b2c 100644
--- a/erpnext/hr/doctype/training_result/training_result.py
+++ b/erpnext/hr/doctype/training_result/training_result.py
@@ -13,22 +13,22 @@
 	def validate(self):
 		training_event = frappe.get_doc("Training Event", self.training_event)
 		if training_event.docstatus != 1:
-			frappe.throw(_('{0} must be submitted').format(_('Training Event')))
+			frappe.throw(_("{0} must be submitted").format(_("Training Event")))
 
-		self.employee_emails = ', '.join(get_employee_emails([d.employee
-			for d in self.employees]))
+		self.employee_emails = ", ".join(get_employee_emails([d.employee for d in self.employees]))
 
 	def on_submit(self):
 		training_event = frappe.get_doc("Training Event", self.training_event)
-		training_event.status = 'Completed'
+		training_event.status = "Completed"
 		for e in self.employees:
 			for e1 in training_event.employees:
 				if e1.employee == e.employee:
-					e1.status = 'Completed'
+					e1.status = "Completed"
 					break
 
 		training_event.save()
 
+
 @frappe.whitelist()
 def get_employees(training_event):
 	return frappe.get_doc("Training Event", training_event).employees
diff --git a/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py b/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py
index 4c7bd80..537c206 100644
--- a/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py
+++ b/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py
@@ -10,12 +10,15 @@
 from erpnext.hr.doctype.employee.test_employee import make_employee
 from erpnext.hr.doctype.upload_attendance.upload_attendance import get_data
 
-test_dependencies = ['Holiday List']
+test_dependencies = ["Holiday List"]
+
 
 class TestUploadAttendance(unittest.TestCase):
 	@classmethod
 	def setUpClass(cls):
-		frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", '_Test Holiday List')
+		frappe.db.set_value(
+			"Company", erpnext.get_default_company(), "default_holiday_list", "_Test Holiday List"
+		)
 
 	def test_date_range(self):
 		employee = make_employee("test_employee@company.com")
@@ -27,14 +30,13 @@
 		employee_doc.date_of_joining = date_of_joining
 		employee_doc.relieving_date = relieving_date
 		employee_doc.save()
-		args = {
-			"from_date": from_date,
-			"to_date": to_date
-		}
+		args = {"from_date": from_date, "to_date": to_date}
 		data = get_data(args)
 		filtered_data = []
 		for row in data:
 			if row[1] == employee:
 				filtered_data.append(row)
 		for row in filtered_data:
-			self.assertTrue(getdate(row[3]) >= getdate(date_of_joining) and getdate(row[3]) <= getdate(relieving_date))
+			self.assertTrue(
+				getdate(row[3]) >= getdate(date_of_joining) and getdate(row[3]) <= getdate(relieving_date)
+			)
diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.py b/erpnext/hr/doctype/upload_attendance/upload_attendance.py
index 94eb300..a66a481 100644
--- a/erpnext/hr/doctype/upload_attendance/upload_attendance.py
+++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.py
@@ -17,6 +17,7 @@
 class UploadAttendance(Document):
 	pass
 
+
 @frappe.whitelist()
 def get_template():
 	if not frappe.has_permission("Attendance", "create"):
@@ -38,29 +39,37 @@
 		return
 
 	# write out response as a type csv
-	frappe.response['result'] = cstr(w.getvalue())
-	frappe.response['type'] = 'csv'
-	frappe.response['doctype'] = "Attendance"
+	frappe.response["result"] = cstr(w.getvalue())
+	frappe.response["type"] = "csv"
+	frappe.response["doctype"] = "Attendance"
+
 
 def add_header(w):
-	status = ", ".join((frappe.get_meta("Attendance").get_field("status").options or "").strip().split("\n"))
+	status = ", ".join(
+		(frappe.get_meta("Attendance").get_field("status").options or "").strip().split("\n")
+	)
 	w.writerow(["Notes:"])
 	w.writerow(["Please do not change the template headings"])
 	w.writerow(["Status should be one of these values: " + status])
 	w.writerow(["If you are overwriting existing attendance records, 'ID' column mandatory"])
-	w.writerow(["ID", "Employee", "Employee Name", "Date", "Status", "Leave Type",
-		 "Company", "Naming Series"])
+	w.writerow(
+		["ID", "Employee", "Employee Name", "Date", "Status", "Leave Type", "Company", "Naming Series"]
+	)
 	return w
 
+
 def add_data(w, args):
 	data = get_data(args)
 	writedata(w, data)
 	return w
 
+
 def get_data(args):
 	dates = get_dates(args)
 	employees = get_active_employees()
-	holidays = get_holidays_for_employees([employee.name for employee in employees], args["from_date"], args["to_date"])
+	holidays = get_holidays_for_employees(
+		[employee.name for employee in employees], args["from_date"], args["to_date"]
+	)
 	existing_attendance_records = get_existing_attendance_records(args)
 	data = []
 	for date in dates:
@@ -71,27 +80,33 @@
 				if getdate(date) > getdate(employee.relieving_date):
 					continue
 			existing_attendance = {}
-			if existing_attendance_records \
-				and tuple([getdate(date), employee.name]) in existing_attendance_records \
-				and getdate(employee.date_of_joining) <= getdate(date) \
-				and getdate(employee.relieving_date) >= getdate(date):
-					existing_attendance = existing_attendance_records[tuple([getdate(date), employee.name])]
+			if (
+				existing_attendance_records
+				and tuple([getdate(date), employee.name]) in existing_attendance_records
+				and getdate(employee.date_of_joining) <= getdate(date)
+				and getdate(employee.relieving_date) >= getdate(date)
+			):
+				existing_attendance = existing_attendance_records[tuple([getdate(date), employee.name])]
 
 			employee_holiday_list = get_holiday_list_for_employee(employee.name)
 
 			row = [
 				existing_attendance and existing_attendance.name or "",
-				employee.name, employee.employee_name, date,
+				employee.name,
+				employee.employee_name,
+				date,
 				existing_attendance and existing_attendance.status or "",
-				existing_attendance and existing_attendance.leave_type or "", employee.company,
+				existing_attendance and existing_attendance.leave_type or "",
+				employee.company,
 				existing_attendance and existing_attendance.naming_series or get_naming_series(),
 			]
 			if date in holidays[employee_holiday_list]:
-				row[4] =  "Holiday"
+				row[4] = "Holiday"
 			data.append(row)
 
 	return data
 
+
 def get_holidays_for_employees(employees, from_date, to_date):
 	holidays = {}
 	for employee in employees:
@@ -102,30 +117,35 @@
 
 	return holidays
 
+
 def writedata(w, data):
 	for row in data:
 		w.writerow(row)
 
+
 def get_dates(args):
 	"""get list of dates in between from date and to date"""
 	no_of_days = date_diff(add_days(args["to_date"], 1), args["from_date"])
 	dates = [add_days(args["from_date"], i) for i in range(0, no_of_days)]
 	return dates
 
+
 def get_active_employees():
-	employees = frappe.db.get_all('Employee',
-		fields=['name', 'employee_name', 'date_of_joining', 'company', 'relieving_date'],
-		filters={
-			'docstatus': ['<', 2],
-			'status': 'Active'
-		}
+	employees = frappe.db.get_all(
+		"Employee",
+		fields=["name", "employee_name", "date_of_joining", "company", "relieving_date"],
+		filters={"docstatus": ["<", 2], "status": "Active"},
 	)
 	return employees
 
+
 def get_existing_attendance_records(args):
-	attendance = frappe.db.sql("""select name, attendance_date, employee, status, leave_type, naming_series
+	attendance = frappe.db.sql(
+		"""select name, attendance_date, employee, status, leave_type, naming_series
 		from `tabAttendance` where attendance_date between %s and %s and docstatus < 2""",
-		(args["from_date"], args["to_date"]), as_dict=1)
+		(args["from_date"], args["to_date"]),
+		as_dict=1,
+	)
 
 	existing_attendance = {}
 	for att in attendance:
@@ -133,6 +153,7 @@
 
 	return existing_attendance
 
+
 def get_naming_series():
 	series = frappe.get_meta("Attendance").get_field("naming_series").options.strip().split("\n")
 	if not series:
@@ -146,15 +167,16 @@
 		raise frappe.PermissionError
 
 	from frappe.utils.csvutils import read_csv_content
+
 	rows = read_csv_content(frappe.local.uploaded_file)
 	if not rows:
 		frappe.throw(_("Please select a csv file"))
 	frappe.enqueue(import_attendances, rows=rows, now=True if len(rows) < 200 else False)
 
-def import_attendances(rows):
 
+def import_attendances(rows):
 	def remove_holidays(rows):
-		rows = [ row for row in rows if row[4] != "Holiday"]
+		rows = [row for row in rows if row[4] != "Holiday"]
 		return rows
 
 	from frappe.modules import scrub
@@ -172,7 +194,8 @@
 	from frappe.utils.csvutils import check_record, import_doc
 
 	for i, row in enumerate(rows):
-		if not row: continue
+		if not row:
+			continue
 		row_idx = i + 5
 		d = frappe._dict(zip(columns, row))
 
@@ -183,16 +206,12 @@
 		try:
 			check_record(d)
 			ret.append(import_doc(d, "Attendance", 1, row_idx, submit=True))
-			frappe.publish_realtime('import_attendance', dict(
-				progress=i,
-				total=len(rows)
-			))
+			frappe.publish_realtime("import_attendance", dict(progress=i, total=len(rows)))
 		except AttributeError:
 			pass
 		except Exception as e:
 			error = True
-			ret.append('Error for row (#%d) %s : %s' % (row_idx,
-				len(row)>1 and row[1] or "", cstr(e)))
+			ret.append("Error for row (#%d) %s : %s" % (row_idx, len(row) > 1 and row[1] or "", cstr(e)))
 			frappe.errprint(frappe.get_traceback())
 
 	if error:
@@ -200,7 +219,4 @@
 	else:
 		frappe.db.commit()
 
-	frappe.publish_realtime('import_attendance', dict(
-		messages=ret,
-		error=error
-	))
+	frappe.publish_realtime("import_attendance", dict(messages=ret, error=error))
diff --git a/erpnext/hr/doctype/vehicle/test_vehicle.py b/erpnext/hr/doctype/vehicle/test_vehicle.py
index c5ea5a3..97fe651 100644
--- a/erpnext/hr/doctype/vehicle/test_vehicle.py
+++ b/erpnext/hr/doctype/vehicle/test_vehicle.py
@@ -8,18 +8,21 @@
 
 # test_records = frappe.get_test_records('Vehicle')
 
+
 class TestVehicle(unittest.TestCase):
 	def test_make_vehicle(self):
-		vehicle = frappe.get_doc({
-			"doctype": "Vehicle",
-			"license_plate": random_string(10).upper(),
-			"make": "Maruti",
-			"model": "PCM",
-			"last_odometer":5000,
-			"acquisition_date":frappe.utils.nowdate(),
-			"location": "Mumbai",
-			"chassis_no": "1234ABCD",
-			"uom": "Litre",
-			"vehicle_value":frappe.utils.flt(500000)
-		})
+		vehicle = frappe.get_doc(
+			{
+				"doctype": "Vehicle",
+				"license_plate": random_string(10).upper(),
+				"make": "Maruti",
+				"model": "PCM",
+				"last_odometer": 5000,
+				"acquisition_date": frappe.utils.nowdate(),
+				"location": "Mumbai",
+				"chassis_no": "1234ABCD",
+				"uom": "Litre",
+				"vehicle_value": frappe.utils.flt(500000),
+			}
+		)
 		vehicle.insert()
diff --git a/erpnext/hr/doctype/vehicle/vehicle.py b/erpnext/hr/doctype/vehicle/vehicle.py
index 946233b..22c14c3 100644
--- a/erpnext/hr/doctype/vehicle/vehicle.py
+++ b/erpnext/hr/doctype/vehicle/vehicle.py
@@ -15,9 +15,15 @@
 		if getdate(self.carbon_check_date) > getdate():
 			frappe.throw(_("Last carbon check date cannot be a future date"))
 
+
 def get_timeline_data(doctype, name):
-	'''Return timeline for vehicle log'''
-	return dict(frappe.db.sql('''select unix_timestamp(date), count(*)
+	"""Return timeline for vehicle log"""
+	return dict(
+		frappe.db.sql(
+			"""select unix_timestamp(date), count(*)
 	from `tabVehicle Log` where license_plate=%s
 	and date > date_sub(curdate(), interval 1 year)
-	group by date''', name))
+	group by date""",
+			name,
+		)
+	)
diff --git a/erpnext/hr/doctype/vehicle/vehicle_dashboard.py b/erpnext/hr/doctype/vehicle/vehicle_dashboard.py
index f6e5f06..758dfbd 100644
--- a/erpnext/hr/doctype/vehicle/vehicle_dashboard.py
+++ b/erpnext/hr/doctype/vehicle/vehicle_dashboard.py
@@ -3,18 +3,11 @@
 
 def get_data():
 	return {
-		'heatmap': True,
-		'heatmap_message': _('This is based on logs against this Vehicle. See timeline below for details'),
-		'fieldname': 'license_plate',
-		'non_standard_fieldnames':{
-			'Delivery Trip': 'vehicle'
-		},
-		'transactions': [
-			{
-				'items': ['Vehicle Log']
-			},
-			{
-				'items': ['Delivery Trip']
-			}
-		]
+		"heatmap": True,
+		"heatmap_message": _(
+			"This is based on logs against this Vehicle. See timeline below for details"
+		),
+		"fieldname": "license_plate",
+		"non_standard_fieldnames": {"Delivery Trip": "vehicle"},
+		"transactions": [{"items": ["Vehicle Log"]}, {"items": ["Delivery Trip"]}],
 	}
diff --git a/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py b/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py
index abb2887..bb29670 100644
--- a/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py
+++ b/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py
@@ -12,7 +12,9 @@
 
 class TestVehicleLog(unittest.TestCase):
 	def setUp(self):
-		employee_id = frappe.db.sql("""select name from `tabEmployee` where name='testdriver@example.com'""")
+		employee_id = frappe.db.sql(
+			"""select name from `tabEmployee` where name='testdriver@example.com'"""
+		)
 		self.employee_id = employee_id[0][0] if employee_id else None
 
 		if not self.employee_id:
@@ -27,11 +29,11 @@
 	def test_make_vehicle_log_and_syncing_of_odometer_value(self):
 		vehicle_log = make_vehicle_log(self.license_plate, self.employee_id)
 
-		#checking value of vehicle odometer value on submit.
+		# checking value of vehicle odometer value on submit.
 		vehicle = frappe.get_doc("Vehicle", self.license_plate)
 		self.assertEqual(vehicle.last_odometer, vehicle_log.odometer)
 
-		#checking value vehicle odometer on vehicle log cancellation.
+		# checking value vehicle odometer on vehicle log cancellation.
 		last_odometer = vehicle_log.last_odometer
 		current_odometer = vehicle_log.odometer
 		distance_travelled = current_odometer - last_odometer
@@ -48,7 +50,7 @@
 
 		expense_claim = make_expense_claim(vehicle_log.name)
 		fuel_expense = expense_claim.expenses[0].amount
-		self.assertEqual(fuel_expense, 50*500)
+		self.assertEqual(fuel_expense, 50 * 500)
 
 		vehicle_log.cancel()
 		frappe.delete_doc("Expense Claim", expense_claim.name)
@@ -67,8 +69,9 @@
 
 
 def get_vehicle(employee_id):
-	license_plate=random_string(10).upper()
-	vehicle = frappe.get_doc({
+	license_plate = random_string(10).upper()
+	vehicle = frappe.get_doc(
+		{
 			"doctype": "Vehicle",
 			"license_plate": cstr(license_plate),
 			"make": "Maruti",
@@ -79,8 +82,9 @@
 			"location": "Mumbai",
 			"chassis_no": "1234ABCD",
 			"uom": "Litre",
-			"vehicle_value": flt(500000)
-		})
+			"vehicle_value": flt(500000),
+		}
+	)
 	try:
 		vehicle.insert(ignore_if_duplicate=True)
 	except frappe.DuplicateEntryError:
@@ -89,29 +93,37 @@
 
 
 def make_vehicle_log(license_plate, employee_id, with_services=False):
-	vehicle_log = frappe.get_doc({
-		"doctype": "Vehicle Log",
-		"license_plate": cstr(license_plate),
-		"employee": employee_id,
-		"date": nowdate(),
-		"odometer": 5010,
-		"fuel_qty": flt(50),
-		"price": flt(500)
-	})
+	vehicle_log = frappe.get_doc(
+		{
+			"doctype": "Vehicle Log",
+			"license_plate": cstr(license_plate),
+			"employee": employee_id,
+			"date": nowdate(),
+			"odometer": 5010,
+			"fuel_qty": flt(50),
+			"price": flt(500),
+		}
+	)
 
 	if with_services:
-		vehicle_log.append("service_detail", {
-			"service_item": "Oil Change",
-			"type": "Inspection",
-			"frequency": "Mileage",
-			"expense_amount": flt(500)
-		})
-		vehicle_log.append("service_detail", {
-			"service_item": "Wheels",
-			"type": "Change",
-			"frequency": "Half Yearly",
-			"expense_amount": flt(1500)
-		})
+		vehicle_log.append(
+			"service_detail",
+			{
+				"service_item": "Oil Change",
+				"type": "Inspection",
+				"frequency": "Mileage",
+				"expense_amount": flt(500),
+			},
+		)
+		vehicle_log.append(
+			"service_detail",
+			{
+				"service_item": "Wheels",
+				"type": "Change",
+				"frequency": "Half Yearly",
+				"expense_amount": flt(1500),
+			},
+		)
 
 	vehicle_log.save()
 	vehicle_log.submit()
diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.py b/erpnext/hr/doctype/vehicle_log/vehicle_log.py
index e414141..2c1d9a4 100644
--- a/erpnext/hr/doctype/vehicle_log/vehicle_log.py
+++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.py
@@ -11,17 +11,24 @@
 class VehicleLog(Document):
 	def validate(self):
 		if flt(self.odometer) < flt(self.last_odometer):
-			frappe.throw(_("Current Odometer Value should be greater than Last Odometer Value {0}").format(self.last_odometer))
+			frappe.throw(
+				_("Current Odometer Value should be greater than Last Odometer Value {0}").format(
+					self.last_odometer
+				)
+			)
 
 	def on_submit(self):
 		frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", self.odometer)
 
 	def on_cancel(self):
 		distance_travelled = self.odometer - self.last_odometer
-		if(distance_travelled > 0):
-			updated_odometer_value = int(frappe.db.get_value("Vehicle", self.license_plate, "last_odometer")) - distance_travelled
+		if distance_travelled > 0:
+			updated_odometer_value = (
+				int(frappe.db.get_value("Vehicle", self.license_plate, "last_odometer")) - distance_travelled
+			)
 			frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", updated_odometer_value)
 
+
 @frappe.whitelist()
 def make_expense_claim(docname):
 	expense_claim = frappe.db.exists("Expense Claim", {"vehicle_log": docname})
@@ -39,9 +46,8 @@
 	exp_claim.employee = vehicle_log.employee
 	exp_claim.vehicle_log = vehicle_log.name
 	exp_claim.remark = _("Expense Claim for Vehicle Log {0}").format(vehicle_log.name)
-	exp_claim.append("expenses", {
-		"expense_date": vehicle_log.date,
-		"description": _("Vehicle Expenses"),
-		"amount": claim_amount
-	})
+	exp_claim.append(
+		"expenses",
+		{"expense_date": vehicle_log.date, "description": _("Vehicle Expenses"), "amount": claim_amount},
+	)
 	return exp_claim.as_dict()
diff --git a/erpnext/hr/employee_property_update.js b/erpnext/hr/employee_property_update.js
index 60d06b4..86130bf 100644
--- a/erpnext/hr/employee_property_update.js
+++ b/erpnext/hr/employee_property_update.js
@@ -8,39 +8,65 @@
 			};
 		});
 	},
-	onload: function(frm){
-		if(frm.doc.__islocal){
-			if(frm.doctype == "Employee Promotion"){
-				frm.doc.promotion_details = [];
-			}else if (frm.doctype == "Employee Transfer") {
-				frm.doc.transfer_details = [];
-			}
-		}
+
+	onload: function(frm) {
+		if (frm.doc.__islocal)
+			frm.trigger("clear_property_table");
 	},
+
 	employee: function(frm) {
-		frm.add_fetch("employee", "company", "company");
+		frm.trigger("clear_property_table");
 	},
+
+	clear_property_table: function(frm) {
+		let table = (frm.doctype == "Employee Promotion") ? "promotion_details" : "transfer_details";
+		frm.clear_table(table);
+		frm.refresh_field(table);
+
+		frm.fields_dict[table].grid.wrapper.find(".grid-add-row").hide();
+	},
+
 	refresh: function(frm) {
-		var table;
-		if(frm.doctype == "Employee Promotion"){
+		let table;
+		if (frm.doctype == "Employee Promotion") {
 			table = "promotion_details";
-		}else if (frm.doctype == "Employee Transfer") {
+		} else if (frm.doctype == "Employee Transfer") {
 			table = "transfer_details";
 		}
-		if(!table){return;}
-		cur_frm.fields_dict[table].grid.wrapper.find('.grid-add-row').hide();
-		cur_frm.fields_dict[table].grid.add_custom_button(__('Add Row'), () => {
-			if(!frm.doc.employee){
-				frappe.msgprint(__("Please select Employee"));
+
+		if (!table)
+			return;
+
+		frm.fields_dict[table].grid.wrapper.find(".grid-add-row").hide();
+		frm.events.setup_employee_property_button(frm, table);
+	},
+
+	setup_employee_property_button: function(frm, table) {
+		frm.fields_dict[table].grid.add_custom_button(__("Add Employee Property"), () => {
+			if (!frm.doc.employee) {
+				frappe.msgprint(__("Please select Employee first."));
 				return;
 			}
-			frappe.call({
-				method: 'erpnext.hr.utils.get_employee_fields_label',
-				callback: function(r) {
-					if(r.message){
-						show_dialog(frm, table, r.message);
+
+			const allowed_fields = [];
+			const exclude_fields = ["naming_series", "employee", "first_name", "middle_name", "last_name", "marital_status", "ctc",
+				"employee_name", "status", "image", "gender", "date_of_birth", "date_of_joining", "lft", "rgt", "old_parent"];
+
+			const exclude_field_types = ["HTML", "Section Break", "Column Break", "Button", "Read Only", "Tab Break", "Table"];
+
+			frappe.model.with_doctype("Employee", () => {
+				const field_label_map = {};
+				frappe.get_meta("Employee").fields.forEach(d => {
+					field_label_map[d.fieldname] = __(d.label) + ` (${d.fieldname})`;
+					if (!in_list(exclude_field_types, d.fieldtype) && !in_list(exclude_fields, d.fieldname)) {
+						allowed_fields.push({
+							label: field_label_map[d.fieldname],
+							value: d.fieldname,
+						});
 					}
-				}
+				});
+
+				show_dialog(frm, table, allowed_fields);
 			});
 		});
 	}
@@ -50,21 +76,20 @@
 	var d = new frappe.ui.Dialog({
 		title: "Update Property",
 		fields: [
-			{fieldname: "property", label: __('Select Property'), fieldtype:"Select", options: field_labels},
-			{fieldname: "current", fieldtype: "Data", label:__('Current'), read_only: true},
-			{fieldname: "field_html", fieldtype: "HTML"}
+			{fieldname: "property", label: __("Select Property"), fieldtype: "Autocomplete", options: field_labels},
+			{fieldname: "current", fieldtype: "Data", label: __("Current"), read_only: true},
+			{fieldname: "new_value", fieldtype: "Data", label: __("New")}
 		],
-		primary_action_label: __('Add to Details'),
+		primary_action_label: __("Add to Details"),
 		primary_action: () => {
-			d.get_primary_btn().attr('disabled', true);
-			if(d.data) {
-				var input = $('[data-fieldname="field_html"] input');
-				d.data.new = input.val();
-				$(input).remove();
+			d.get_primary_btn().attr("disabled", true);
+			if (d.data) {
+				d.data.new = d.get_values().new_value;
 				add_to_details(frm, d, table);
 			}
 		}
 	});
+
 	d.fields_dict["property"].df.onchange = () => {
 		let property = d.get_values().property;
 		d.data.fieldname = property;
@@ -73,10 +98,10 @@
 			method: 'erpnext.hr.utils.get_employee_field_property',
 			args: {employee: frm.doc.employee, fieldname: property},
 			callback: function(r) {
-				if(r.message){
+				if (r.message) {
 					d.data.current = r.message.value;
 					d.data.property = r.message.label;
-					d.fields_dict.field_html.$wrapper.html("");
+
 					d.set_value('current', r.message.value);
 					render_dynamic_field(d, r.message.datatype, r.message.options, property);
 					d.get_primary_btn().attr('disabled', false);
@@ -95,25 +120,26 @@
 		df: {
 			"fieldtype": fieldtype,
 			"fieldname": fieldname,
-			"options": options || ''
+			"options": options || '',
+			"label": __("New")
 		},
-		parent: d.fields_dict.field_html.wrapper,
+		parent: d.fields_dict.new_value.wrapper,
 		only_input: false
 	});
 	dynamic_field.make_input();
-	$(dynamic_field.label_area).text(__("New"));
+	d.replace_field("new_value", dynamic_field.df);
 };
 
 var add_to_details = function(frm, d, table) {
 	let data = d.data;
-	if(data.fieldname){
-		if(validate_duplicate(frm, table, data.fieldname)){
-			frappe.show_alert({message:__("Property already added"), indicator:'orange'});
+	if (data.fieldname) {
+		if (validate_duplicate(frm, table, data.fieldname)) {
+			frappe.show_alert({message: __("Property already added"), indicator: "orange"});
 			return false;
 		}
-		if(data.current == data.new){
-			frappe.show_alert({message:__("Nothing to change"), indicator:'orange'});
-			d.get_primary_btn().attr('disabled', false);
+		if (data.current == data.new) {
+			frappe.show_alert({message: __("Nothing to change"), indicator: "orange"});
+			d.get_primary_btn().attr("disabled", false);
 			return false;
 		}
 		frm.add_child(table, {
@@ -123,13 +149,16 @@
 			new: data.new
 		});
 		frm.refresh_field(table);
-		d.fields_dict.field_html.$wrapper.html("");
+
+		frm.fields_dict[table].grid.wrapper.find(".grid-add-row").hide();
+
+		d.fields_dict.new_value.$wrapper.html("");
 		d.set_value("property", "");
-		d.set_value('current', "");
-		frappe.show_alert({message:__("Added to details"),indicator:'green'});
+		d.set_value("current", "");
+		frappe.show_alert({message: __("Added to details"), indicator: "green"});
 		d.data = {};
-	}else {
-		frappe.show_alert({message:__("Value missing"),indicator:'red'});
+	} else {
+		frappe.show_alert({message: __("Value missing"), indicator: "red"});
 	}
 };
 
diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py
index 1e2d758..3674912 100644
--- a/erpnext/hr/page/organizational_chart/organizational_chart.py
+++ b/erpnext/hr/page/organizational_chart/organizational_chart.py
@@ -3,26 +3,27 @@
 
 @frappe.whitelist()
 def get_children(parent=None, company=None, exclude_node=None):
-	filters = [['status', '!=', 'Left']]
-	if company and company != 'All Companies':
-		filters.append(['company', '=', company])
+	filters = [["status", "!=", "Left"]]
+	if company and company != "All Companies":
+		filters.append(["company", "=", company])
 
 	if parent and company and parent != company:
-		filters.append(['reports_to', '=', parent])
+		filters.append(["reports_to", "=", parent])
 	else:
-		filters.append(['reports_to', '=', ''])
+		filters.append(["reports_to", "=", ""])
 
 	if exclude_node:
-		filters.append(['name', '!=', exclude_node])
+		filters.append(["name", "!=", exclude_node])
 
-	employees = frappe.get_list('Employee',
-		fields=['employee_name as name', 'name as id', 'reports_to', 'image', 'designation as title'],
+	employees = frappe.get_list(
+		"Employee",
+		fields=["employee_name as name", "name as id", "reports_to", "image", "designation as title"],
 		filters=filters,
-		order_by='name'
+		order_by="name",
 	)
 
 	for employee in employees:
-		is_expandable = frappe.db.count('Employee', filters={'reports_to': employee.get('id')})
+		is_expandable = frappe.db.count("Employee", filters={"reports_to": employee.get("id")})
 		employee.connections = get_connections(employee.id)
 		employee.expandable = 1 if is_expandable else 0
 
@@ -32,16 +33,12 @@
 def get_connections(employee):
 	num_connections = 0
 
-	nodes_to_expand = frappe.get_list('Employee', filters=[
-			['reports_to', '=', employee]
-		])
+	nodes_to_expand = frappe.get_list("Employee", filters=[["reports_to", "=", employee]])
 	num_connections += len(nodes_to_expand)
 
 	while nodes_to_expand:
 		parent = nodes_to_expand.pop(0)
-		descendants = frappe.get_list('Employee', filters=[
-			['reports_to', '=', parent.name]
-		])
+		descendants = frappe.get_list("Employee", filters=[["reports_to", "=", parent.name]])
 		num_connections += len(descendants)
 		nodes_to_expand.extend(descendants)
 
diff --git a/erpnext/hr/page/team_updates/team_updates.py b/erpnext/hr/page/team_updates/team_updates.py
index 0a4624c..c1fcb73 100644
--- a/erpnext/hr/page/team_updates/team_updates.py
+++ b/erpnext/hr/page/team_updates/team_updates.py
@@ -4,15 +4,20 @@
 
 @frappe.whitelist()
 def get_data(start=0):
-	#frappe.only_for('Employee', 'System Manager')
-	data = frappe.get_all('Communication',
-		fields=('content', 'text_content', 'sender', 'creation'),
-		filters=dict(reference_doctype='Daily Work Summary'),
-		order_by='creation desc', limit=40, start=start)
+	# frappe.only_for('Employee', 'System Manager')
+	data = frappe.get_all(
+		"Communication",
+		fields=("content", "text_content", "sender", "creation"),
+		filters=dict(reference_doctype="Daily Work Summary"),
+		order_by="creation desc",
+		limit=40,
+		start=start,
+	)
 
 	for d in data:
-		d.sender_name = frappe.db.get_value("Employee", {"user_id": d.sender},
-			"employee_name") or d.sender
+		d.sender_name = (
+			frappe.db.get_value("Employee", {"user_id": d.sender}, "employee_name") or d.sender
+		)
 		if d.text_content:
 			d.content = frappe.utils.md_to_html(EmailReplyParser.parse_reply(d.text_content))
 
diff --git a/erpnext/hr/report/daily_work_summary_replies/daily_work_summary_replies.py b/erpnext/hr/report/daily_work_summary_replies/daily_work_summary_replies.py
index 63764bb..d93688a 100644
--- a/erpnext/hr/report/daily_work_summary_replies/daily_work_summary_replies.py
+++ b/erpnext/hr/report/daily_work_summary_replies/daily_work_summary_replies.py
@@ -9,51 +9,53 @@
 
 
 def execute(filters=None):
-	if not filters.group: return [], []
+	if not filters.group:
+		return [], []
 	columns, data = get_columns(), get_data(filters)
 	return columns, data
 
+
 def get_columns(filters=None):
 	columns = [
-		{
-			"label": _("User"),
-			"fieldname": "user",
-			"fieldtype": "Data",
-			"width": 300
-		},
+		{"label": _("User"), "fieldname": "user", "fieldtype": "Data", "width": 300},
 		{
 			"label": _("Replies"),
 			"fieldname": "count",
 			"fieldtype": "data",
 			"width": 100,
-			"align": 'right',
+			"align": "right",
 		},
 		{
 			"label": _("Total"),
 			"fieldname": "total",
 			"fieldtype": "data",
 			"width": 100,
-			"align": 'right',
-		}
+			"align": "right",
+		},
 	]
 	return columns
 
+
 def get_data(filters):
-	daily_summary_emails = frappe.get_all('Daily Work Summary',
-		fields=["name"],
-		filters=[["creation","Between", filters.range]])
-	daily_summary_emails = [d.get('name') for d in daily_summary_emails]
-	replies = frappe.get_all('Communication',
-			fields=['content', 'text_content', 'sender'],
-			filters=[['reference_doctype','=', 'Daily Work Summary'],
-				['reference_name', 'in', daily_summary_emails],
-				['communication_type', '=', 'Communication'],
-				['sent_or_received', '=', 'Received']],
-			order_by='creation asc')
+	daily_summary_emails = frappe.get_all(
+		"Daily Work Summary", fields=["name"], filters=[["creation", "Between", filters.range]]
+	)
+	daily_summary_emails = [d.get("name") for d in daily_summary_emails]
+	replies = frappe.get_all(
+		"Communication",
+		fields=["content", "text_content", "sender"],
+		filters=[
+			["reference_doctype", "=", "Daily Work Summary"],
+			["reference_name", "in", daily_summary_emails],
+			["communication_type", "=", "Communication"],
+			["sent_or_received", "=", "Received"],
+		],
+		order_by="creation asc",
+	)
 	data = []
 	total = len(daily_summary_emails)
 	for user in get_user_emails_from_group(filters.group):
-		user_name = frappe.get_value('User', user, 'full_name')
+		user_name = frappe.get_value("User", user, "full_name")
 		count = len([d for d in replies if d.sender == user])
 		data.append([user_name, count, total])
 	return data
diff --git a/erpnext/hr/report/employee_advance_summary/employee_advance_summary.py b/erpnext/hr/report/employee_advance_summary/employee_advance_summary.py
index 62b83f2..29532f7 100644
--- a/erpnext/hr/report/employee_advance_summary/employee_advance_summary.py
+++ b/erpnext/hr/report/employee_advance_summary/employee_advance_summary.py
@@ -7,7 +7,8 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	advances_list = get_advances(filters)
 	columns = get_columns()
@@ -18,8 +19,16 @@
 
 	data = []
 	for advance in advances_list:
-		row = [advance.name, advance.employee, advance.company, advance.posting_date,
-		advance.advance_amount, advance.paid_amount,  advance.claimed_amount, advance.status]
+		row = [
+			advance.name,
+			advance.employee,
+			advance.company,
+			advance.posting_date,
+			advance.advance_amount,
+			advance.paid_amount,
+			advance.claimed_amount,
+			advance.status,
+		]
 		data.append(row)
 
 	return columns, data
@@ -32,54 +41,40 @@
 			"fieldname": "title",
 			"fieldtype": "Link",
 			"options": "Employee Advance",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("Employee"),
 			"fieldname": "employee",
 			"fieldtype": "Link",
 			"options": "Employee",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("Company"),
 			"fieldname": "company",
 			"fieldtype": "Link",
 			"options": "Company",
-			"width": 120
+			"width": 120,
 		},
-		{
-			"label": _("Posting Date"),
-			"fieldname": "posting_date",
-			"fieldtype": "Date",
-			"width": 120
-		},
+		{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 120},
 		{
 			"label": _("Advance Amount"),
 			"fieldname": "advance_amount",
 			"fieldtype": "Currency",
-			"width": 120
+			"width": 120,
 		},
-		{
-			"label": _("Paid Amount"),
-			"fieldname": "paid_amount",
-			"fieldtype": "Currency",
-			"width": 120
-		},
+		{"label": _("Paid Amount"), "fieldname": "paid_amount", "fieldtype": "Currency", "width": 120},
 		{
 			"label": _("Claimed Amount"),
 			"fieldname": "claimed_amount",
 			"fieldtype": "Currency",
-			"width": 120
+			"width": 120,
 		},
-		{
-			"label": _("Status"),
-			"fieldname": "status",
-			"fieldtype": "Data",
-			"width": 120
-		}
+		{"label": _("Status"), "fieldname": "status", "fieldtype": "Data", "width": 120},
 	]
 
+
 def get_conditions(filters):
 	conditions = ""
 
@@ -96,10 +91,15 @@
 
 	return conditions
 
+
 def get_advances(filters):
 	conditions = get_conditions(filters)
-	return frappe.db.sql("""select name, employee, paid_amount, status, advance_amount, claimed_amount, company,
+	return frappe.db.sql(
+		"""select name, employee, paid_amount, status, advance_amount, claimed_amount, company,
 		posting_date, purpose
 		from `tabEmployee Advance`
-		where docstatus<2 %s order by posting_date, name desc""" %
-		conditions, filters, as_dict=1)
+		where docstatus<2 %s order by posting_date, name desc"""
+		% conditions,
+		filters,
+		as_dict=1,
+	)
diff --git a/erpnext/hr/report/employee_analytics/employee_analytics.py b/erpnext/hr/report/employee_analytics/employee_analytics.py
index 3a75276..12be156 100644
--- a/erpnext/hr/report/employee_analytics/employee_analytics.py
+++ b/erpnext/hr/report/employee_analytics/employee_analytics.py
@@ -7,10 +7,11 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	if not filters["company"]:
-		frappe.throw(_('{0} is mandatory').format(_('Company')))
+		frappe.throw(_("{0} is mandatory").format(_("Company")))
 
 	columns = get_columns()
 	employees = get_employees(filters)
@@ -20,28 +21,41 @@
 		for department in parameters_result:
 			parameters.append(department)
 
-	chart = get_chart_data(parameters,employees, filters)
+	chart = get_chart_data(parameters, employees, filters)
 	return columns, employees, None, chart
 
+
 def get_columns():
 	return [
-		_("Employee") + ":Link/Employee:120", _("Name") + ":Data:200", _("Date of Birth")+ ":Date:100",
-		_("Branch") + ":Link/Branch:120", _("Department") + ":Link/Department:120",
-		_("Designation") + ":Link/Designation:120", _("Gender") + "::100", _("Company") + ":Link/Company:120"
+		_("Employee") + ":Link/Employee:120",
+		_("Name") + ":Data:200",
+		_("Date of Birth") + ":Date:100",
+		_("Branch") + ":Link/Branch:120",
+		_("Department") + ":Link/Department:120",
+		_("Designation") + ":Link/Designation:120",
+		_("Gender") + "::100",
+		_("Company") + ":Link/Company:120",
 	]
 
-def get_conditions(filters):
-	conditions = " and "+filters.get("parameter").lower().replace(" ","_")+" IS NOT NULL "
 
-	if filters.get("company"): conditions += " and company = '%s'" % \
-		filters["company"].replace("'", "\\'")
+def get_conditions(filters):
+	conditions = " and " + filters.get("parameter").lower().replace(" ", "_") + " IS NOT NULL "
+
+	if filters.get("company"):
+		conditions += " and company = '%s'" % filters["company"].replace("'", "\\'")
 	return conditions
 
+
 def get_employees(filters):
 	conditions = get_conditions(filters)
-	return frappe.db.sql("""select name, employee_name, date_of_birth,
+	return frappe.db.sql(
+		"""select name, employee_name, date_of_birth,
 	branch, department, designation,
-	gender, company from `tabEmployee` where status = 'Active' %s""" % conditions, as_list=1)
+	gender, company from `tabEmployee` where status = 'Active' %s"""
+		% conditions,
+		as_list=1,
+	)
+
 
 def get_parameters(filters):
 	if filters.get("parameter") == "Grade":
@@ -49,36 +63,37 @@
 	else:
 		parameter = filters.get("parameter")
 
-	return frappe.db.sql("""select name from `tab"""+ parameter +"""` """, as_list=1)
+	return frappe.db.sql("""select name from `tab""" + parameter + """` """, as_list=1)
 
-def get_chart_data(parameters,employees, filters):
+
+def get_chart_data(parameters, employees, filters):
 	if not parameters:
 		parameters = []
 	datasets = []
-	parameter_field_name = filters.get("parameter").lower().replace(" ","_")
+	parameter_field_name = filters.get("parameter").lower().replace(" ", "_")
 	label = []
 	for parameter in parameters:
 		if parameter:
-			total_employee = frappe.db.sql("""select count(*) from
-				`tabEmployee` where """+
-				parameter_field_name + """ = %s and  company = %s""" ,( parameter[0], filters.get("company")), as_list=1)
+			total_employee = frappe.db.sql(
+				"""select count(*) from
+				`tabEmployee` where """
+				+ parameter_field_name
+				+ """ = %s and  company = %s""",
+				(parameter[0], filters.get("company")),
+				as_list=1,
+			)
 			if total_employee[0][0]:
 				label.append(parameter)
 			datasets.append(total_employee[0][0])
 
-	values = [ value for value in datasets if value !=0]
+	values = [value for value in datasets if value != 0]
 
-	total_employee = frappe.db.count('Employee', {'status':'Active'})
+	total_employee = frappe.db.count("Employee", {"status": "Active"})
 	others = total_employee - sum(values)
 
 	label.append(["Not Set"])
 	values.append(others)
 
-	chart = {
-		"data": {
-			'labels': label,
-			'datasets': [{'name': 'Employees','values': values}]
-		}
-	}
+	chart = {"data": {"labels": label, "datasets": [{"name": "Employees", "values": values}]}}
 	chart["type"] = "donut"
 	return chart
diff --git a/erpnext/hr/report/employee_birthday/employee_birthday.py b/erpnext/hr/report/employee_birthday/employee_birthday.py
index cec5a48..a6a13d8 100644
--- a/erpnext/hr/report/employee_birthday/employee_birthday.py
+++ b/erpnext/hr/report/employee_birthday/employee_birthday.py
@@ -7,34 +7,59 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	columns = get_columns()
 	data = get_employees(filters)
 
 	return columns, data
 
+
 def get_columns():
 	return [
-		_("Employee") + ":Link/Employee:120", _("Name") + ":Data:200", _("Date of Birth")+ ":Date:100",
-		_("Branch") + ":Link/Branch:120", _("Department") + ":Link/Department:120",
-		_("Designation") + ":Link/Designation:120", _("Gender") + "::60", _("Company") + ":Link/Company:120"
+		_("Employee") + ":Link/Employee:120",
+		_("Name") + ":Data:200",
+		_("Date of Birth") + ":Date:100",
+		_("Branch") + ":Link/Branch:120",
+		_("Department") + ":Link/Department:120",
+		_("Designation") + ":Link/Designation:120",
+		_("Gender") + "::60",
+		_("Company") + ":Link/Company:120",
 	]
 
+
 def get_employees(filters):
 	conditions = get_conditions(filters)
-	return frappe.db.sql("""select name, employee_name, date_of_birth,
+	return frappe.db.sql(
+		"""select name, employee_name, date_of_birth,
 	branch, department, designation,
-	gender, company from tabEmployee where status = 'Active' %s""" % conditions, as_list=1)
+	gender, company from tabEmployee where status = 'Active' %s"""
+		% conditions,
+		as_list=1,
+	)
+
 
 def get_conditions(filters):
 	conditions = ""
 	if filters.get("month"):
-		month = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov",
-			"Dec"].index(filters["month"]) + 1
+		month = [
+			"Jan",
+			"Feb",
+			"Mar",
+			"Apr",
+			"May",
+			"Jun",
+			"Jul",
+			"Aug",
+			"Sep",
+			"Oct",
+			"Nov",
+			"Dec",
+		].index(filters["month"]) + 1
 		conditions += " and month(date_of_birth) = '%s'" % month
 
-	if filters.get("company"): conditions += " and company = '%s'" % \
-		filters["company"].replace("'", "\\'")
+	if filters.get("company"):
+		conditions += " and company = '%s'" % filters["company"].replace("'", "\\'")
 
 	return conditions
diff --git a/erpnext/hr/report/employee_exits/employee_exits.py b/erpnext/hr/report/employee_exits/employee_exits.py
index bde5a89..80b9ec1 100644
--- a/erpnext/hr/report/employee_exits/employee_exits.py
+++ b/erpnext/hr/report/employee_exits/employee_exits.py
@@ -5,6 +5,7 @@
 from frappe import _
 from frappe.query_builder import Order
 from frappe.utils import getdate
+from pypika import functions as fn
 
 
 def execute(filters=None):
@@ -15,104 +16,106 @@
 
 	return columns, data, None, chart, report_summary
 
+
 def get_columns():
 	return [
 		{
-			'label': _('Employee'),
-			'fieldname': 'employee',
-			'fieldtype': 'Link',
-			'options': 'Employee',
-			'width': 150
+			"label": _("Employee"),
+			"fieldname": "employee",
+			"fieldtype": "Link",
+			"options": "Employee",
+			"width": 150,
+		},
+		{"label": _("Employee Name"), "fieldname": "employee_name", "fieldtype": "Data", "width": 150},
+		{
+			"label": _("Date of Joining"),
+			"fieldname": "date_of_joining",
+			"fieldtype": "Date",
+			"width": 120,
+		},
+		{"label": _("Relieving Date"), "fieldname": "relieving_date", "fieldtype": "Date", "width": 120},
+		{
+			"label": _("Exit Interview"),
+			"fieldname": "exit_interview",
+			"fieldtype": "Link",
+			"options": "Exit Interview",
+			"width": 150,
 		},
 		{
-			'label': _('Employee Name'),
-			'fieldname': 'employee_name',
-			'fieldtype': 'Data',
-			'width': 150
+			"label": _("Interview Status"),
+			"fieldname": "interview_status",
+			"fieldtype": "Data",
+			"width": 130,
 		},
 		{
-			'label': _('Date of Joining'),
-			'fieldname': 'date_of_joining',
-			'fieldtype': 'Date',
-			'width': 120
+			"label": _("Final Decision"),
+			"fieldname": "employee_status",
+			"fieldtype": "Data",
+			"width": 150,
 		},
 		{
-			'label': _('Relieving Date'),
-			'fieldname': 'relieving_date',
-			'fieldtype': 'Date',
-			'width': 120
+			"label": _("Full and Final Statement"),
+			"fieldname": "full_and_final_statement",
+			"fieldtype": "Link",
+			"options": "Full and Final Statement",
+			"width": 180,
 		},
 		{
-			'label': _('Exit Interview'),
-			'fieldname': 'exit_interview',
-			'fieldtype': 'Link',
-			'options': 'Exit Interview',
-			'width': 150
+			"label": _("Department"),
+			"fieldname": "department",
+			"fieldtype": "Link",
+			"options": "Department",
+			"width": 120,
 		},
 		{
-			'label': _('Interview Status'),
-			'fieldname': 'interview_status',
-			'fieldtype': 'Data',
-			'width': 130
+			"label": _("Designation"),
+			"fieldname": "designation",
+			"fieldtype": "Link",
+			"options": "Designation",
+			"width": 120,
 		},
 		{
-			'label': _('Final Decision'),
-			'fieldname': 'employee_status',
-			'fieldtype': 'Data',
-			'width': 150
+			"label": _("Reports To"),
+			"fieldname": "reports_to",
+			"fieldtype": "Link",
+			"options": "Employee",
+			"width": 120,
 		},
-		{
-			'label': _('Full and Final Statement'),
-			'fieldname': 'full_and_final_statement',
-			'fieldtype': 'Link',
-			'options': 'Full and Final Statement',
-			'width': 180
-		},
-		{
-			'label': _('Department'),
-			'fieldname': 'department',
-			'fieldtype': 'Link',
-			'options': 'Department',
-			'width': 120
-		},
-		{
-			'label': _('Designation'),
-			'fieldname': 'designation',
-			'fieldtype': 'Link',
-			'options': 'Designation',
-			'width': 120
-		},
-		{
-			'label': _('Reports To'),
-			'fieldname': 'reports_to',
-			'fieldtype': 'Link',
-			'options': 'Employee',
-			'width': 120
-		}
 	]
 
+
 def get_data(filters):
-	employee = frappe.qb.DocType('Employee')
-	interview = frappe.qb.DocType('Exit Interview')
-	fnf = frappe.qb.DocType('Full and Final Statement')
+	employee = frappe.qb.DocType("Employee")
+	interview = frappe.qb.DocType("Exit Interview")
+	fnf = frappe.qb.DocType("Full and Final Statement")
 
 	query = (
 		frappe.qb.from_(employee)
-			.left_join(interview).on(interview.employee == employee.name)
-			.left_join(fnf).on(fnf.employee == employee.name)
-			.select(
-				employee.name.as_('employee'), employee.employee_name.as_('employee_name'),
-				employee.date_of_joining.as_('date_of_joining'), employee.relieving_date.as_('relieving_date'),
-				employee.department.as_('department'), employee.designation.as_('designation'),
-				employee.reports_to.as_('reports_to'), interview.name.as_('exit_interview'),
-				interview.status.as_('interview_status'), interview.employee_status.as_('employee_status'),
-				interview.reference_document_name.as_('questionnaire'), fnf.name.as_('full_and_final_statement'))
-			.distinct()
-			.where(
-				((employee.relieving_date.isnotnull()) | (employee.relieving_date != ''))
-				& ((interview.name.isnull()) | ((interview.name.isnotnull()) & (interview.docstatus != 2)))
-				& ((fnf.name.isnull()) | ((fnf.name.isnotnull()) & (fnf.docstatus != 2)))
-			).orderby(employee.relieving_date, order=Order.asc)
+		.left_join(interview)
+		.on(interview.employee == employee.name)
+		.left_join(fnf)
+		.on(fnf.employee == employee.name)
+		.select(
+			employee.name.as_("employee"),
+			employee.employee_name.as_("employee_name"),
+			employee.date_of_joining.as_("date_of_joining"),
+			employee.relieving_date.as_("relieving_date"),
+			employee.department.as_("department"),
+			employee.designation.as_("designation"),
+			employee.reports_to.as_("reports_to"),
+			interview.name.as_("exit_interview"),
+			interview.status.as_("interview_status"),
+			interview.employee_status.as_("employee_status"),
+			interview.reference_document_name.as_("questionnaire"),
+			fnf.name.as_("full_and_final_statement"),
+		)
+		.distinct()
+		.where(
+			(fn.Coalesce(fn.Cast(employee.relieving_date, "char"), "") != "")
+			& ((interview.name.isnull()) | ((interview.name.isnotnull()) & (interview.docstatus != 2)))
+			& ((fnf.name.isnull()) | ((fnf.name.isnotnull()) & (fnf.docstatus != 2)))
+		)
+		.orderby(employee.relieving_date, order=Order.asc)
 	)
 
 	query = get_conditions(filters, query, employee, interview, fnf)
@@ -122,44 +125,48 @@
 
 
 def get_conditions(filters, query, employee, interview, fnf):
-	if filters.get('from_date') and filters.get('to_date'):
-		query = query.where(employee.relieving_date[getdate(filters.get('from_date')):getdate(filters.get('to_date'))])
+	if filters.get("from_date") and filters.get("to_date"):
+		query = query.where(
+			employee.relieving_date[getdate(filters.get("from_date")) : getdate(filters.get("to_date"))]
+		)
 
-	elif filters.get('from_date'):
-		query = query.where(employee.relieving_date >= filters.get('from_date'))
+	elif filters.get("from_date"):
+		query = query.where(employee.relieving_date >= filters.get("from_date"))
 
-	elif filters.get('to_date'):
-		query = query.where(employee.relieving_date <= filters.get('to_date'))
+	elif filters.get("to_date"):
+		query = query.where(employee.relieving_date <= filters.get("to_date"))
 
-	if filters.get('company'):
-		query = query.where(employee.company == filters.get('company'))
+	if filters.get("company"):
+		query = query.where(employee.company == filters.get("company"))
 
-	if filters.get('department'):
-		query = query.where(employee.department == filters.get('department'))
+	if filters.get("department"):
+		query = query.where(employee.department == filters.get("department"))
 
-	if filters.get('designation'):
-		query = query.where(employee.designation == filters.get('designation'))
+	if filters.get("designation"):
+		query = query.where(employee.designation == filters.get("designation"))
 
-	if filters.get('employee'):
-		query = query.where(employee.name == filters.get('employee'))
+	if filters.get("employee"):
+		query = query.where(employee.name == filters.get("employee"))
 
-	if filters.get('reports_to'):
-		query = query.where(employee.reports_to == filters.get('reports_to'))
+	if filters.get("reports_to"):
+		query = query.where(employee.reports_to == filters.get("reports_to"))
 
-	if filters.get('interview_status'):
-		query = query.where(interview.status == filters.get('interview_status'))
+	if filters.get("interview_status"):
+		query = query.where(interview.status == filters.get("interview_status"))
 
-	if filters.get('final_decision'):
-		query = query.where(interview.employee_status == filters.get('final_decision'))
+	if filters.get("final_decision"):
+		query = query.where(interview.employee_status == filters.get("final_decision"))
 
-	if filters.get('exit_interview_pending'):
-		query = query.where((interview.name == '') | (interview.name.isnull()))
+	if filters.get("exit_interview_pending"):
+		query = query.where((interview.name == "") | (interview.name.isnull()))
 
-	if filters.get('questionnaire_pending'):
-		query = query.where((interview.reference_document_name == '') | (interview.reference_document_name.isnull()))
+	if filters.get("questionnaire_pending"):
+		query = query.where(
+			(interview.reference_document_name == "") | (interview.reference_document_name.isnull())
+		)
 
-	if filters.get('fnf_pending'):
-		query = query.where((fnf.name == '') | (fnf.name.isnull()))
+	if filters.get("fnf_pending"):
+		query = query.where((fnf.name == "") | (fnf.name.isnull()))
 
 	return query
 
@@ -173,20 +180,20 @@
 	pending = 0
 
 	for entry in data:
-		if entry.employee_status == 'Employee Retained':
+		if entry.employee_status == "Employee Retained":
 			retained += 1
-		elif entry.employee_status == 'Exit Confirmed':
+		elif entry.employee_status == "Exit Confirmed":
 			exit_confirmed += 1
 		else:
 			pending += 1
 
 	chart = {
-		'data': {
-			'labels': [_('Retained'), _('Exit Confirmed'), _('Decision Pending')],
-			'datasets': [{'name': _('Employee Status'), 'values': [retained, exit_confirmed, pending]}]
+		"data": {
+			"labels": [_("Retained"), _("Exit Confirmed"), _("Decision Pending")],
+			"datasets": [{"name": _("Employee Status"), "values": [retained, exit_confirmed, pending]}],
 		},
-		'type': 'donut',
-		'colors': ['green', 'red', 'blue'],
+		"type": "donut",
+		"colors": ["green", "red", "blue"],
 	}
 
 	return chart
@@ -203,28 +210,27 @@
 
 	return [
 		{
-			'value': total_resignations,
-			'label': _('Total Resignations'),
-			'indicator': 'Red' if total_resignations > 0 else 'Green',
-			'datatype': 'Int',
+			"value": total_resignations,
+			"label": _("Total Resignations"),
+			"indicator": "Red" if total_resignations > 0 else "Green",
+			"datatype": "Int",
 		},
 		{
-			'value': interviews_pending,
-			'label': _('Pending Interviews'),
-			'indicator': 'Blue' if interviews_pending > 0 else 'Green',
-			'datatype': 'Int',
+			"value": interviews_pending,
+			"label": _("Pending Interviews"),
+			"indicator": "Blue" if interviews_pending > 0 else "Green",
+			"datatype": "Int",
 		},
 		{
-			'value': fnf_pending,
-			'label': _('Pending FnF'),
-			'indicator': 'Blue' if fnf_pending > 0 else 'Green',
-			'datatype': 'Int',
+			"value": fnf_pending,
+			"label": _("Pending FnF"),
+			"indicator": "Blue" if fnf_pending > 0 else "Green",
+			"datatype": "Int",
 		},
 		{
-			'value': questionnaires_pending,
-			'label': _('Pending Questionnaires'),
-			'indicator': 'Blue' if questionnaires_pending > 0 else 'Green',
-			'datatype': 'Int'
+			"value": questionnaires_pending,
+			"label": _("Pending Questionnaires"),
+			"indicator": "Blue" if questionnaires_pending > 0 else "Green",
+			"datatype": "Int",
 		},
 	]
-
diff --git a/erpnext/hr/report/employee_exits/test_employee_exits.py b/erpnext/hr/report/employee_exits/test_employee_exits.py
index d7e95a6..a9a30de 100644
--- a/erpnext/hr/report/employee_exits/test_employee_exits.py
+++ b/erpnext/hr/report/employee_exits/test_employee_exits.py
@@ -28,33 +28,33 @@
 	@classmethod
 	def create_records(cls):
 		cls.emp1 = make_employee(
-			'employeeexit1@example.com',
-			company='Test Company',
-			date_of_joining=getdate('01-10-2021'),
+			"employeeexit1@example.com",
+			company="Test Company",
+			date_of_joining=getdate("01-10-2021"),
 			relieving_date=add_days(getdate(), 14),
-			designation='Accountant'
+			designation="Accountant",
 		)
 		cls.emp2 = make_employee(
-			'employeeexit2@example.com',
-			company='Test Company',
-			date_of_joining=getdate('01-12-2021'),
+			"employeeexit2@example.com",
+			company="Test Company",
+			date_of_joining=getdate("01-12-2021"),
 			relieving_date=add_days(getdate(), 15),
-			designation='Accountant'
+			designation="Accountant",
 		)
 
 		cls.emp3 = make_employee(
-			'employeeexit3@example.com',
-			company='Test Company',
-			date_of_joining=getdate('02-12-2021'),
+			"employeeexit3@example.com",
+			company="Test Company",
+			date_of_joining=getdate("02-12-2021"),
 			relieving_date=add_days(getdate(), 29),
-			designation='Engineer'
+			designation="Engineer",
 		)
 		cls.emp4 = make_employee(
-			'employeeexit4@example.com',
-			company='Test Company',
-			date_of_joining=getdate('01-12-2021'),
+			"employeeexit4@example.com",
+			company="Test Company",
+			date_of_joining=getdate("01-12-2021"),
 			relieving_date=add_days(getdate(), 30),
-			designation='Engineer'
+			designation="Engineer",
 		)
 
 		# exit interview for 3 employees only
@@ -69,174 +69,177 @@
 		# link questionnaire for a few records
 		# setting employee doctype as reference instead of creating a questionnaire
 		# since this is just for a test
-		frappe.db.set_value('Exit Interview', cls.interview1.name, {
-			'ref_doctype': 'Employee',
-			'reference_document_name': cls.emp1
-		})
+		frappe.db.set_value(
+			"Exit Interview",
+			cls.interview1.name,
+			{"ref_doctype": "Employee", "reference_document_name": cls.emp1},
+		)
 
-		frappe.db.set_value('Exit Interview', cls.interview2.name, {
-			'ref_doctype': 'Employee',
-			'reference_document_name': cls.emp2
-		})
+		frappe.db.set_value(
+			"Exit Interview",
+			cls.interview2.name,
+			{"ref_doctype": "Employee", "reference_document_name": cls.emp2},
+		)
 
-		frappe.db.set_value('Exit Interview', cls.interview3.name, {
-			'ref_doctype': 'Employee',
-			'reference_document_name': cls.emp3
-		})
-
+		frappe.db.set_value(
+			"Exit Interview",
+			cls.interview3.name,
+			{"ref_doctype": "Employee", "reference_document_name": cls.emp3},
+		)
 
 	def test_employee_exits_summary(self):
 		filters = {
-			'company': 'Test Company',
-			'from_date': getdate(),
-			'to_date': add_days(getdate(), 15),
-			'designation': 'Accountant'
+			"company": "Test Company",
+			"from_date": getdate(),
+			"to_date": add_days(getdate(), 15),
+			"designation": "Accountant",
 		}
 
 		report = execute(filters)
 
-		employee1 = frappe.get_doc('Employee', self.emp1)
-		employee2 = frappe.get_doc('Employee', self.emp2)
+		employee1 = frappe.get_doc("Employee", self.emp1)
+		employee2 = frappe.get_doc("Employee", self.emp2)
 		expected_data = [
 			{
-				'employee': employee1.name,
-				'employee_name': employee1.employee_name,
-				'date_of_joining': employee1.date_of_joining,
-				'relieving_date': employee1.relieving_date,
-				'department': employee1.department,
-				'designation': employee1.designation,
-				'reports_to': None,
-				'exit_interview': self.interview1.name,
-				'interview_status': self.interview1.status,
-				'employee_status': '',
-				'questionnaire': employee1.name,
-				'full_and_final_statement': self.fnf1.name
+				"employee": employee1.name,
+				"employee_name": employee1.employee_name,
+				"date_of_joining": employee1.date_of_joining,
+				"relieving_date": employee1.relieving_date,
+				"department": employee1.department,
+				"designation": employee1.designation,
+				"reports_to": None,
+				"exit_interview": self.interview1.name,
+				"interview_status": self.interview1.status,
+				"employee_status": "",
+				"questionnaire": employee1.name,
+				"full_and_final_statement": self.fnf1.name,
 			},
 			{
-				'employee': employee2.name,
-				'employee_name': employee2.employee_name,
-				'date_of_joining': employee2.date_of_joining,
-				'relieving_date': employee2.relieving_date,
-				'department': employee2.department,
-				'designation': employee2.designation,
-				'reports_to': None,
-				'exit_interview': self.interview2.name,
-				'interview_status': self.interview2.status,
-				'employee_status': '',
-				'questionnaire': employee2.name,
-				'full_and_final_statement': self.fnf2.name
-			}
+				"employee": employee2.name,
+				"employee_name": employee2.employee_name,
+				"date_of_joining": employee2.date_of_joining,
+				"relieving_date": employee2.relieving_date,
+				"department": employee2.department,
+				"designation": employee2.designation,
+				"reports_to": None,
+				"exit_interview": self.interview2.name,
+				"interview_status": self.interview2.status,
+				"employee_status": "",
+				"questionnaire": employee2.name,
+				"full_and_final_statement": self.fnf2.name,
+			},
 		]
 
-		self.assertEqual(expected_data, report[1]) # rows
-
+		self.assertEqual(expected_data, report[1])  # rows
 
 	def test_pending_exit_interviews_summary(self):
 		filters = {
-			'company': 'Test Company',
-			'from_date': getdate(),
-			'to_date': add_days(getdate(), 30),
-			'exit_interview_pending': 1
+			"company": "Test Company",
+			"from_date": getdate(),
+			"to_date": add_days(getdate(), 30),
+			"exit_interview_pending": 1,
 		}
 
 		report = execute(filters)
 
-		employee4 = frappe.get_doc('Employee', self.emp4)
-		expected_data = [{
-			'employee': employee4.name,
-			'employee_name': employee4.employee_name,
-			'date_of_joining': employee4.date_of_joining,
-			'relieving_date': employee4.relieving_date,
-			'department': employee4.department,
-			'designation': employee4.designation,
-			'reports_to': None,
-			'exit_interview': None,
-			'interview_status': None,
-			'employee_status': None,
-			'questionnaire': None,
-			'full_and_final_statement': None
-		}]
-
-		self.assertEqual(expected_data, report[1]) # rows
-
-	def test_pending_exit_questionnaire_summary(self):
-		filters = {
-			'company': 'Test Company',
-			'from_date': getdate(),
-			'to_date': add_days(getdate(), 30),
-			'questionnaire_pending': 1
-		}
-
-		report = execute(filters)
-
-		employee4 = frappe.get_doc('Employee', self.emp4)
-		expected_data = [{
-			'employee': employee4.name,
-			'employee_name': employee4.employee_name,
-			'date_of_joining': employee4.date_of_joining,
-			'relieving_date': employee4.relieving_date,
-			'department': employee4.department,
-			'designation': employee4.designation,
-			'reports_to': None,
-			'exit_interview': None,
-			'interview_status': None,
-			'employee_status': None,
-			'questionnaire': None,
-			'full_and_final_statement': None
-		}]
-
-		self.assertEqual(expected_data, report[1]) # rows
-
-
-	def test_pending_fnf_summary(self):
-		filters = {
-			'company': 'Test Company',
-			'fnf_pending': 1
-		}
-
-		report = execute(filters)
-
-		employee3 = frappe.get_doc('Employee', self.emp3)
-		employee4 = frappe.get_doc('Employee', self.emp4)
+		employee4 = frappe.get_doc("Employee", self.emp4)
 		expected_data = [
 			{
-				'employee': employee3.name,
-				'employee_name': employee3.employee_name,
-				'date_of_joining': employee3.date_of_joining,
-				'relieving_date': employee3.relieving_date,
-				'department': employee3.department,
-				'designation': employee3.designation,
-				'reports_to': None,
-				'exit_interview': self.interview3.name,
-				'interview_status': self.interview3.status,
-				'employee_status': '',
-				'questionnaire': employee3.name,
-				'full_and_final_statement': None
-			},
-			{
-				'employee': employee4.name,
-				'employee_name': employee4.employee_name,
-				'date_of_joining': employee4.date_of_joining,
-				'relieving_date': employee4.relieving_date,
-				'department': employee4.department,
-				'designation': employee4.designation,
-				'reports_to': None,
-				'exit_interview': None,
-				'interview_status': None,
-				'employee_status': None,
-				'questionnaire': None,
-				'full_and_final_statement': None
+				"employee": employee4.name,
+				"employee_name": employee4.employee_name,
+				"date_of_joining": employee4.date_of_joining,
+				"relieving_date": employee4.relieving_date,
+				"department": employee4.department,
+				"designation": employee4.designation,
+				"reports_to": None,
+				"exit_interview": None,
+				"interview_status": None,
+				"employee_status": None,
+				"questionnaire": None,
+				"full_and_final_statement": None,
 			}
 		]
 
-		self.assertEqual(expected_data, report[1]) # rows
+		self.assertEqual(expected_data, report[1])  # rows
+
+	def test_pending_exit_questionnaire_summary(self):
+		filters = {
+			"company": "Test Company",
+			"from_date": getdate(),
+			"to_date": add_days(getdate(), 30),
+			"questionnaire_pending": 1,
+		}
+
+		report = execute(filters)
+
+		employee4 = frappe.get_doc("Employee", self.emp4)
+		expected_data = [
+			{
+				"employee": employee4.name,
+				"employee_name": employee4.employee_name,
+				"date_of_joining": employee4.date_of_joining,
+				"relieving_date": employee4.relieving_date,
+				"department": employee4.department,
+				"designation": employee4.designation,
+				"reports_to": None,
+				"exit_interview": None,
+				"interview_status": None,
+				"employee_status": None,
+				"questionnaire": None,
+				"full_and_final_statement": None,
+			}
+		]
+
+		self.assertEqual(expected_data, report[1])  # rows
+
+	def test_pending_fnf_summary(self):
+		filters = {"company": "Test Company", "fnf_pending": 1}
+
+		report = execute(filters)
+
+		employee3 = frappe.get_doc("Employee", self.emp3)
+		employee4 = frappe.get_doc("Employee", self.emp4)
+		expected_data = [
+			{
+				"employee": employee3.name,
+				"employee_name": employee3.employee_name,
+				"date_of_joining": employee3.date_of_joining,
+				"relieving_date": employee3.relieving_date,
+				"department": employee3.department,
+				"designation": employee3.designation,
+				"reports_to": None,
+				"exit_interview": self.interview3.name,
+				"interview_status": self.interview3.status,
+				"employee_status": "",
+				"questionnaire": employee3.name,
+				"full_and_final_statement": None,
+			},
+			{
+				"employee": employee4.name,
+				"employee_name": employee4.employee_name,
+				"date_of_joining": employee4.date_of_joining,
+				"relieving_date": employee4.relieving_date,
+				"department": employee4.department,
+				"designation": employee4.designation,
+				"reports_to": None,
+				"exit_interview": None,
+				"interview_status": None,
+				"employee_status": None,
+				"questionnaire": None,
+				"full_and_final_statement": None,
+			},
+		]
+
+		self.assertEqual(expected_data, report[1])  # rows
 
 
 def create_company():
-	if not frappe.db.exists('Company', 'Test Company'):
-		frappe.get_doc({
-			'doctype': 'Company',
-			'company_name': 'Test Company',
-			'default_currency': 'INR',
-			'country': 'India'
-		}).insert()
\ No newline at end of file
+	if not frappe.db.exists("Company", "Test Company"):
+		frappe.get_doc(
+			{
+				"doctype": "Company",
+				"company_name": "Test Company",
+				"default_currency": "INR",
+				"country": "India",
+			}
+		).insert()
diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js
index 8bb3457..2339350 100644
--- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js
+++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js
@@ -4,21 +4,21 @@
 frappe.query_reports["Employee Leave Balance"] = {
 	"filters": [
 		{
-			"fieldname":"from_date",
+			"fieldname": "from_date",
 			"label": __("From Date"),
 			"fieldtype": "Date",
 			"reqd": 1,
 			"default": frappe.defaults.get_default("year_start_date")
 		},
 		{
-			"fieldname":"to_date",
+			"fieldname": "to_date",
 			"label": __("To Date"),
 			"fieldtype": "Date",
 			"reqd": 1,
 			"default": frappe.defaults.get_default("year_end_date")
 		},
 		{
-			"fieldname":"company",
+			"fieldname": "company",
 			"label": __("Company"),
 			"fieldtype": "Link",
 			"options": "Company",
@@ -26,16 +26,29 @@
 			"default": frappe.defaults.get_user_default("Company")
 		},
 		{
-			"fieldname":"department",
+			"fieldname": "department",
 			"label": __("Department"),
 			"fieldtype": "Link",
 			"options": "Department",
 		},
 		{
-			"fieldname":"employee",
+			"fieldname": "employee",
 			"label": __("Employee"),
 			"fieldtype": "Link",
 			"options": "Employee",
+		},
+		{
+			"fieldname": "employee_status",
+			"label": __("Employee Status"),
+			"fieldtype": "Select",
+			"options": [
+				"",
+				{ "value": "Active", "label": __("Active") },
+				{ "value": "Inactive", "label": __("Inactive") },
+				{ "value": "Suspended", "label": __("Suspended") },
+				{ "value": "Left", "label": __("Left") },
+			],
+			"default": "Active",
 		}
 	],
 
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 b375b18..1f7ade2 100644
--- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
+++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
@@ -3,18 +3,22 @@
 
 
 from itertools import groupby
+from typing import Dict, List, Optional, Tuple
 
 import frappe
 from frappe import _
-from frappe.utils import add_days
+from frappe.utils import add_days, getdate
 
+from erpnext.hr.doctype.leave_allocation.leave_allocation import get_previous_allocation
 from erpnext.hr.doctype.leave_application.leave_application import (
 	get_leave_balance_on,
 	get_leaves_for_period,
 )
 
+Filters = frappe._dict
 
-def execute(filters=None):
+
+def execute(filters: Optional[Filters] = None) -> Tuple:
 	if filters.to_date <= filters.from_date:
 		frappe.throw(_('"From Date" can not be greater than or equal to "To Date"'))
 
@@ -23,146 +27,179 @@
 	charts = get_chart_data(data)
 	return columns, data, None, charts
 
-def get_columns():
-	columns = [{
-		'label': _('Leave Type'),
-		'fieldtype': 'Link',
-		'fieldname': 'leave_type',
-		'width': 200,
-		'options': 'Leave Type'
-	}, {
-		'label': _('Employee'),
-		'fieldtype': 'Link',
-		'fieldname': 'employee',
-		'width': 100,
-		'options': 'Employee'
-	}, {
-		'label': _('Employee Name'),
-		'fieldtype': 'Dynamic Link',
-		'fieldname': 'employee_name',
-		'width': 100,
-		'options': 'employee'
-	}, {
-		'label': _('Opening Balance'),
-		'fieldtype': 'float',
-		'fieldname': 'opening_balance',
-		'width': 130,
-	}, {
-		'label': _('Leave Allocated'),
-		'fieldtype': 'float',
-		'fieldname': 'leaves_allocated',
-		'width': 130,
-	}, {
-		'label': _('Leave Taken'),
-		'fieldtype': 'float',
-		'fieldname': 'leaves_taken',
-		'width': 130,
-	}, {
-		'label': _('Leave Expired'),
-		'fieldtype': 'float',
-		'fieldname': 'leaves_expired',
-		'width': 130,
-	}, {
-		'label': _('Closing Balance'),
-		'fieldtype': 'float',
-		'fieldname': 'closing_balance',
-		'width': 130,
-	}]
 
-	return columns
+def get_columns() -> List[Dict]:
+	return [
+		{
+			"label": _("Leave Type"),
+			"fieldtype": "Link",
+			"fieldname": "leave_type",
+			"width": 200,
+			"options": "Leave Type",
+		},
+		{
+			"label": _("Employee"),
+			"fieldtype": "Link",
+			"fieldname": "employee",
+			"width": 100,
+			"options": "Employee",
+		},
+		{
+			"label": _("Employee Name"),
+			"fieldtype": "Dynamic Link",
+			"fieldname": "employee_name",
+			"width": 100,
+			"options": "employee",
+		},
+		{
+			"label": _("Opening Balance"),
+			"fieldtype": "float",
+			"fieldname": "opening_balance",
+			"width": 150,
+		},
+		{
+			"label": _("New Leave(s) Allocated"),
+			"fieldtype": "float",
+			"fieldname": "leaves_allocated",
+			"width": 200,
+		},
+		{
+			"label": _("Leave(s) Taken"),
+			"fieldtype": "float",
+			"fieldname": "leaves_taken",
+			"width": 150,
+		},
+		{
+			"label": _("Leave(s) Expired"),
+			"fieldtype": "float",
+			"fieldname": "leaves_expired",
+			"width": 150,
+		},
+		{
+			"label": _("Closing Balance"),
+			"fieldtype": "float",
+			"fieldname": "closing_balance",
+			"width": 150,
+		},
+	]
 
-def get_data(filters):
-	leave_types = frappe.db.get_list('Leave Type', pluck='name', order_by='name')
+
+def get_data(filters: Filters) -> List:
+	leave_types = frappe.db.get_list("Leave Type", pluck="name", order_by="name")
 	conditions = get_conditions(filters)
 
 	user = frappe.session.user
-	department_approver_map = get_department_leave_approver_map(filters.get('department'))
+	department_approver_map = get_department_leave_approver_map(filters.get("department"))
 
-	active_employees = frappe.get_list('Employee',
+	active_employees = frappe.get_list(
+		"Employee",
 		filters=conditions,
-		fields=['name', 'employee_name', 'department', 'user_id', 'leave_approver'])
+		fields=["name", "employee_name", "department", "user_id", "leave_approver"],
+	)
 
 	data = []
 
 	for leave_type in leave_types:
 		if len(active_employees) > 1:
-			data.append({
-				'leave_type': leave_type
-			})
+			data.append({"leave_type": leave_type})
 		else:
-			row = frappe._dict({
-				'leave_type': leave_type
-			})
+			row = frappe._dict({"leave_type": leave_type})
 
 		for employee in active_employees:
 
-			leave_approvers = department_approver_map.get(employee.department_name, []).append(employee.leave_approver)
+			leave_approvers = department_approver_map.get(employee.department_name, []).append(
+				employee.leave_approver
+			)
 
-			if (leave_approvers and len(leave_approvers) and user in leave_approvers) or (user in ["Administrator", employee.user_id]) \
-				or ("HR Manager" in frappe.get_roles(user)):
+			if (
+				(leave_approvers and len(leave_approvers) and user in leave_approvers)
+				or (user in ["Administrator", employee.user_id])
+				or ("HR Manager" in frappe.get_roles(user))
+			):
 				if len(active_employees) > 1:
 					row = frappe._dict()
-				row.employee = employee.name,
+				row.employee = employee.name
 				row.employee_name = employee.employee_name
 
-				leaves_taken = get_leaves_for_period(employee.name, leave_type,
-					filters.from_date, filters.to_date) * -1
+				leaves_taken = (
+					get_leaves_for_period(employee.name, leave_type, filters.from_date, filters.to_date) * -1
+				)
 
-				new_allocation, expired_leaves = get_allocated_and_expired_leaves(filters.from_date, filters.to_date, employee.name, leave_type)
-
-
-				opening = get_leave_balance_on(employee.name, leave_type, add_days(filters.from_date, -1)) #allocation boundary condition
+				new_allocation, expired_leaves, carry_forwarded_leaves = get_allocated_and_expired_leaves(
+					filters.from_date, filters.to_date, employee.name, leave_type
+				)
+				opening = get_opening_balance(employee.name, leave_type, filters, carry_forwarded_leaves)
 
 				row.leaves_allocated = new_allocation
-				row.leaves_expired = expired_leaves - leaves_taken if expired_leaves - leaves_taken > 0 else 0
+				row.leaves_expired = expired_leaves
 				row.opening_balance = opening
 				row.leaves_taken = leaves_taken
 
 				# not be shown on the basis of days left it create in user mind for carry_forward leave
-				row.closing_balance = (new_allocation + opening - (row.leaves_expired + leaves_taken))
+				row.closing_balance = new_allocation + opening - (row.leaves_expired + leaves_taken)
 				row.indent = 1
 				data.append(row)
 
 	return data
 
-def get_conditions(filters):
-	conditions={
-		'status': 'Active',
-	}
-	if filters.get('employee'):
-		conditions['name'] = filters.get('employee')
 
-	if filters.get('company'):
-		conditions['company'] = filters.get('company')
+def get_opening_balance(
+	employee: str, leave_type: str, filters: Filters, carry_forwarded_leaves: float
+) -> float:
+	# allocation boundary condition
+	# opening balance is the closing leave balance 1 day before the filter start date
+	opening_balance_date = add_days(filters.from_date, -1)
+	allocation = get_previous_allocation(filters.from_date, leave_type, employee)
 
-	if filters.get('department'):
-		conditions['department'] = filters.get('department')
+	if (
+		allocation
+		and allocation.get("to_date")
+		and opening_balance_date
+		and getdate(allocation.get("to_date")) == getdate(opening_balance_date)
+	):
+		# if opening balance date is same as the previous allocation's expiry
+		# then opening balance should only consider carry forwarded leaves
+		opening_balance = carry_forwarded_leaves
+	else:
+		# else directly get leave balance on the previous day
+		opening_balance = get_leave_balance_on(employee, leave_type, opening_balance_date)
+
+	return opening_balance
+
+
+def get_conditions(filters: Filters) -> Dict:
+	conditions = {}
+
+	if filters.get("employee"):
+		conditions["name"] = filters.get("employee")
+
+	if filters.get("company"):
+		conditions["company"] = filters.get("company")
+
+	if filters.get("department"):
+		conditions["department"] = filters.get("department")
+
+	if filters.get("employee_status"):
+		conditions["status"] = filters.get("employee_status")
 
 	return conditions
 
-def get_department_leave_approver_map(department=None):
 
+def get_department_leave_approver_map(department: Optional[str] = None):
 	# get current department and all its child
-	department_list = frappe.get_list('Department',
-						filters={
-							'disabled': 0
-						},
-						or_filters={
-							'name': department,
-							'parent_department': department
-						},
-						fields=['name'],
-						pluck='name'
-					)
+	department_list = frappe.get_list(
+		"Department",
+		filters={"disabled": 0},
+		or_filters={"name": department, "parent_department": department},
+		pluck="name",
+	)
 	# retrieve approvers list from current department and from its subsequent child departments
-	approver_list = frappe.get_all('Department Approver',
-						filters={
-							'parentfield': 'leave_approvers',
-							'parent': ('in', department_list)
-						},
-						fields=['parent', 'approver'],
-						as_list=1
-					)
+	approver_list = frappe.get_all(
+		"Department Approver",
+		filters={"parentfield": "leave_approvers", "parent": ("in", department_list)},
+		fields=["parent", "approver"],
+		as_list=True,
+	)
 
 	approvers = {}
 
@@ -171,73 +208,100 @@
 
 	return approvers
 
-def get_allocated_and_expired_leaves(from_date, to_date, employee, leave_type):
 
-	from frappe.utils import getdate
-
+def get_allocated_and_expired_leaves(
+	from_date: str, to_date: str, employee: str, leave_type: str
+) -> Tuple[float, float, float]:
 	new_allocation = 0
 	expired_leaves = 0
+	carry_forwarded_leaves = 0
 
-	records= frappe.db.sql("""
-		SELECT
-			employee, leave_type, from_date, to_date, leaves, transaction_name,
-			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))
-	""", {
-		"from_date": from_date,
-		"to_date": to_date,
-		"employee": employee,
-		"leave_type": leave_type
-	}, as_dict=1)
+	records = get_leave_ledger_entries(from_date, to_date, employee, leave_type)
 
 	for record in records:
+		# new allocation records with `is_expired=1` are created when leave expires
+		# these new records should not be considered, else it leads to negative leave balance
+		if record.is_expired:
+			continue
+
 		if record.to_date < getdate(to_date):
+			# leave allocations ending before to_date, reduce leaves taken within that period
+			# since they are already used, they won't expire
 			expired_leaves += record.leaves
+			expired_leaves += get_leaves_for_period(employee, leave_type, record.from_date, record.to_date)
 
 		if record.from_date >= getdate(from_date):
-			new_allocation += record.leaves
+			if record.is_carry_forward:
+				carry_forwarded_leaves += record.leaves
+			else:
+				new_allocation += record.leaves
 
-	return new_allocation, expired_leaves
+	return new_allocation, expired_leaves, carry_forwarded_leaves
 
-def get_chart_data(data):
+
+def get_leave_ledger_entries(
+	from_date: str, to_date: str, employee: str, leave_type: str
+) -> List[Dict]:
+	ledger = frappe.qb.DocType("Leave Ledger Entry")
+	records = (
+		frappe.qb.from_(ledger)
+		.select(
+			ledger.employee,
+			ledger.leave_type,
+			ledger.from_date,
+			ledger.to_date,
+			ledger.leaves,
+			ledger.transaction_name,
+			ledger.transaction_type,
+			ledger.is_carry_forward,
+			ledger.is_expired,
+		)
+		.where(
+			(ledger.docstatus == 1)
+			& (ledger.transaction_type == "Leave Allocation")
+			& (ledger.employee == employee)
+			& (ledger.leave_type == leave_type)
+			& (
+				(ledger.from_date[from_date:to_date])
+				| (ledger.to_date[from_date:to_date])
+				| ((ledger.from_date < from_date) & (ledger.to_date > to_date))
+			)
+		)
+	).run(as_dict=True)
+
+	return records
+
+
+def get_chart_data(data: List) -> Dict:
 	labels = []
 	datasets = []
 	employee_data = data
 
-	if data and data[0].get('employee_name'):
+	if data and data[0].get("employee_name"):
 		get_dataset_for_chart(employee_data, datasets, labels)
 
 	chart = {
-		'data': {
-			'labels': labels,
-			'datasets': datasets
-		},
-		'type': 'bar',
-		'colors': ['#456789', '#EE8888', '#7E77BF']
+		"data": {"labels": labels, "datasets": datasets},
+		"type": "bar",
+		"colors": ["#456789", "#EE8888", "#7E77BF"],
 	}
 
 	return chart
 
-def get_dataset_for_chart(employee_data, datasets, labels):
-	leaves = []
-	employee_data = sorted(employee_data, key=lambda k: k['employee_name'])
 
-	for key, group in groupby(employee_data, lambda x: x['employee_name']):
+def get_dataset_for_chart(employee_data: List, datasets: List, labels: List) -> List:
+	leaves = []
+	employee_data = sorted(employee_data, key=lambda k: k["employee_name"])
+
+	for key, group in groupby(employee_data, lambda x: x["employee_name"]):
 		for grp in group:
 			if grp.closing_balance:
-				leaves.append(frappe._dict({
-					'leave_type': grp.leave_type,
-					'closing_balance': grp.closing_balance
-				}))
+				leaves.append(
+					frappe._dict({"leave_type": grp.leave_type, "closing_balance": grp.closing_balance})
+				)
 
 		if leaves:
 			labels.append(key)
 
 	for leave in leaves:
-		datasets.append({'name': leave.leave_type, 'values': [leave.closing_balance]})
+		datasets.append({"name": leave.leave_type, "values": [leave.closing_balance]})
diff --git a/erpnext/hr/report/employee_leave_balance/test_employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/test_employee_leave_balance.py
new file mode 100644
index 0000000..5354abf
--- /dev/null
+++ b/erpnext/hr/report/employee_leave_balance/test_employee_leave_balance.py
@@ -0,0 +1,246 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+
+import unittest
+
+import frappe
+from frappe.utils import add_days, add_months, flt, get_year_ending, get_year_start, getdate
+
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list
+from erpnext.hr.doctype.leave_application.test_leave_application import (
+	get_first_sunday,
+	make_allocation_record,
+)
+from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation
+from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
+from erpnext.hr.report.employee_leave_balance.employee_leave_balance import execute
+from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
+	make_holiday_list,
+	make_leave_application,
+)
+
+test_records = frappe.get_test_records("Leave Type")
+
+
+class TestEmployeeLeaveBalance(unittest.TestCase):
+	def setUp(self):
+		for dt in [
+			"Leave Application",
+			"Leave Allocation",
+			"Salary Slip",
+			"Leave Ledger Entry",
+			"Leave Type",
+		]:
+			frappe.db.delete(dt)
+
+		frappe.set_user("Administrator")
+
+		self.employee_id = make_employee("test_emp_leave_balance@example.com", company="_Test Company")
+
+		self.date = getdate()
+		self.year_start = getdate(get_year_start(self.date))
+		self.mid_year = add_months(self.year_start, 6)
+		self.year_end = getdate(get_year_ending(self.date))
+
+		self.holiday_list = make_holiday_list(
+			"_Test Emp Balance Holiday List", self.year_start, self.year_end
+		)
+
+	def tearDown(self):
+		frappe.db.rollback()
+
+	@set_holiday_list("_Test Emp Balance Holiday List", "_Test Company")
+	def test_employee_leave_balance(self):
+		frappe.get_doc(test_records[0]).insert()
+
+		# 5 leaves
+		allocation1 = make_allocation_record(
+			employee=self.employee_id,
+			from_date=add_days(self.year_start, -11),
+			to_date=add_days(self.year_start, -1),
+			leaves=5,
+		)
+		# 30 leaves
+		allocation2 = make_allocation_record(
+			employee=self.employee_id, from_date=self.year_start, to_date=self.year_end
+		)
+		# expires 5 leaves
+		process_expired_allocation()
+
+		# 4 days leave
+		first_sunday = get_first_sunday(self.holiday_list, for_date=self.year_start)
+		leave_application = make_leave_application(
+			self.employee_id, add_days(first_sunday, 1), add_days(first_sunday, 4), "_Test Leave Type"
+		)
+		leave_application.reload()
+
+		filters = frappe._dict(
+			{
+				"from_date": allocation1.from_date,
+				"to_date": allocation2.to_date,
+				"employee": self.employee_id,
+			}
+		)
+
+		report = execute(filters)
+
+		expected_data = [
+			{
+				"leave_type": "_Test Leave Type",
+				"employee": self.employee_id,
+				"employee_name": "test_emp_leave_balance@example.com",
+				"leaves_allocated": flt(allocation1.new_leaves_allocated + allocation2.new_leaves_allocated),
+				"leaves_expired": flt(allocation1.new_leaves_allocated),
+				"opening_balance": flt(0),
+				"leaves_taken": flt(leave_application.total_leave_days),
+				"closing_balance": flt(allocation2.new_leaves_allocated - leave_application.total_leave_days),
+				"indent": 1,
+			}
+		]
+
+		self.assertEqual(report[1], expected_data)
+
+	@set_holiday_list("_Test Emp Balance Holiday List", "_Test Company")
+	def test_opening_balance_on_alloc_boundary_dates(self):
+		frappe.get_doc(test_records[0]).insert()
+
+		# 30 leaves allocated
+		allocation1 = make_allocation_record(
+			employee=self.employee_id, from_date=self.year_start, to_date=self.year_end
+		)
+		# 4 days leave application in the first allocation
+		first_sunday = get_first_sunday(self.holiday_list, for_date=self.year_start)
+		leave_application = make_leave_application(
+			self.employee_id, add_days(first_sunday, 1), add_days(first_sunday, 4), "_Test Leave Type"
+		)
+		leave_application.reload()
+
+		# Case 1: opening balance for first alloc boundary
+		filters = frappe._dict(
+			{"from_date": self.year_start, "to_date": self.year_end, "employee": self.employee_id}
+		)
+		report = execute(filters)
+		self.assertEqual(report[1][0].opening_balance, 0)
+
+		# Case 2: opening balance after leave application date
+		filters = frappe._dict(
+			{
+				"from_date": add_days(leave_application.to_date, 1),
+				"to_date": self.year_end,
+				"employee": self.employee_id,
+			}
+		)
+		report = execute(filters)
+		self.assertEqual(
+			report[1][0].opening_balance,
+			(allocation1.new_leaves_allocated - leave_application.total_leave_days),
+		)
+
+		# Case 3: leave balance shows actual balance and not consumption balance as per remaining days near alloc end date
+		# eg: 3 days left for alloc to end, leave balance should still be 26 and not 3
+		filters = frappe._dict(
+			{
+				"from_date": add_days(self.year_end, -3),
+				"to_date": self.year_end,
+				"employee": self.employee_id,
+			}
+		)
+		report = execute(filters)
+		self.assertEqual(
+			report[1][0].opening_balance,
+			(allocation1.new_leaves_allocated - leave_application.total_leave_days),
+		)
+
+	@set_holiday_list("_Test Emp Balance Holiday List", "_Test Company")
+	def test_opening_balance_considers_carry_forwarded_leaves(self):
+		leave_type = create_leave_type(leave_type_name="_Test_CF_leave_expiry", is_carry_forward=1)
+		leave_type.insert()
+
+		# 30 leaves allocated for first half of the year
+		allocation1 = make_allocation_record(
+			employee=self.employee_id,
+			from_date=self.year_start,
+			to_date=self.mid_year,
+			leave_type=leave_type.name,
+		)
+		# 4 days leave application in the first allocation
+		first_sunday = get_first_sunday(self.holiday_list, for_date=self.year_start)
+		leave_application = make_leave_application(
+			self.employee_id, first_sunday, add_days(first_sunday, 3), leave_type.name
+		)
+		leave_application.reload()
+		# 30 leaves allocated for second half of the year + carry forward leaves (26) from the previous allocation
+		allocation2 = make_allocation_record(
+			employee=self.employee_id,
+			from_date=add_days(self.mid_year, 1),
+			to_date=self.year_end,
+			carry_forward=True,
+			leave_type=leave_type.name,
+		)
+
+		# Case 1: carry forwarded leaves considered in opening balance for second alloc
+		filters = frappe._dict(
+			{
+				"from_date": add_days(self.mid_year, 1),
+				"to_date": self.year_end,
+				"employee": self.employee_id,
+			}
+		)
+		report = execute(filters)
+		# available leaves from old alloc
+		opening_balance = allocation1.new_leaves_allocated - leave_application.total_leave_days
+		self.assertEqual(report[1][0].opening_balance, opening_balance)
+
+		# Case 2: opening balance one day after alloc boundary = carry forwarded leaves + new leaves alloc
+		filters = frappe._dict(
+			{
+				"from_date": add_days(self.mid_year, 2),
+				"to_date": self.year_end,
+				"employee": self.employee_id,
+			}
+		)
+		report = execute(filters)
+		# available leaves from old alloc
+		opening_balance = allocation2.new_leaves_allocated + (
+			allocation1.new_leaves_allocated - leave_application.total_leave_days
+		)
+		self.assertEqual(report[1][0].opening_balance, opening_balance)
+
+	@set_holiday_list("_Test Emp Balance Holiday List", "_Test Company")
+	def test_employee_status_filter(self):
+		frappe.get_doc(test_records[0]).insert()
+		inactive_emp = make_employee("test_emp_status@example.com", company="_Test Company")
+
+		allocation = make_allocation_record(
+			employee=inactive_emp,
+			from_date=self.year_start,
+			to_date=self.year_end,
+			leaves=5,
+		)
+
+		# set employee as inactive
+		frappe.db.set_value("Employee", inactive_emp, "status", "Inactive")
+
+		filters = frappe._dict(
+			{
+				"from_date": allocation.from_date,
+				"to_date": allocation.to_date,
+				"employee": inactive_emp,
+				"employee_status": "Active",
+			}
+		)
+		report = execute(filters)
+		self.assertEqual(len(report[1]), 0)
+
+		filters = frappe._dict(
+			{
+				"from_date": allocation.from_date,
+				"to_date": allocation.to_date,
+				"employee": inactive_emp,
+				"employee_status": "Inactive",
+			}
+		)
+		report = execute(filters)
+		self.assertEqual(len(report[1]), 1)
diff --git a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.js b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.js
index cb05d11..26dd782 100644
--- a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.js
+++ b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.js
@@ -30,6 +30,19 @@
 			label: __('Department'),
 			fieldtype: 'Link',
 			options: 'Department',
+		},
+		{
+			fieldname: "employee_status",
+			label: __("Employee Status"),
+			fieldtype: "Select",
+			options: [
+				"",
+				{ "value": "Active", "label": __("Active") },
+				{ "value": "Inactive", "label": __("Inactive") },
+				{ "value": "Suspended", "label": __("Suspended") },
+				{ "value": "Left", "label": __("Left") },
+			],
+			default: "Active",
 		}
 	]
 };
diff --git a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py
index 71c18bb..82878db 100644
--- a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py
+++ b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py
@@ -19,11 +19,12 @@
 
 	return columns, data
 
+
 def get_columns(leave_types):
 	columns = [
 		_("Employee") + ":Link.Employee:150",
 		_("Employee Name") + "::200",
-		_("Department") +"::150"
+		_("Department") + "::150",
 	]
 
 	for leave_type in leave_types:
@@ -31,11 +32,13 @@
 
 	return columns
 
+
 def get_conditions(filters):
 	conditions = {
-		"status": "Active",
 		"company": filters.company,
 	}
+	if filters.get("employee_status"):
+		conditions.update({"status": filters.get("employee_status")})
 	if filters.get("department"):
 		conditions.update({"department": filters.get("department")})
 	if filters.get("employee"):
@@ -43,15 +46,18 @@
 
 	return conditions
 
+
 def get_data(filters, leave_types):
 	user = frappe.session.user
 	conditions = get_conditions(filters)
 
-	active_employees = frappe.get_list("Employee",
+	active_employees = frappe.get_list(
+		"Employee",
 		filters=conditions,
-		fields=["name", "employee_name", "department", "user_id", "leave_approver"])
+		fields=["name", "employee_name", "department", "user_id", "leave_approver"],
+	)
 
-	department_approver_map = get_department_leave_approver_map(filters.get('department'))
+	department_approver_map = get_department_leave_approver_map(filters.get("department"))
 
 	data = []
 	for employee in active_employees:
@@ -59,14 +65,18 @@
 		if employee.leave_approver:
 			leave_approvers.append(employee.leave_approver)
 
-		if (len(leave_approvers) and user in leave_approvers) or (user in ["Administrator", employee.user_id]) or ("HR Manager" in frappe.get_roles(user)):
+		if (
+			(len(leave_approvers) and user in leave_approvers)
+			or (user in ["Administrator", employee.user_id])
+			or ("HR Manager" in frappe.get_roles(user))
+		):
 			row = [employee.name, employee.employee_name, employee.department]
 			available_leave = get_leave_details(employee.name, filters.date)
 			for leave_type in leave_types:
 				remaining = 0
 				if leave_type in available_leave["leave_allocation"]:
 					# opening balance
-					remaining = available_leave["leave_allocation"][leave_type]['remaining_leaves']
+					remaining = available_leave["leave_allocation"][leave_type]["remaining_leaves"]
 
 				row += [remaining]
 
diff --git a/erpnext/hr/report/employee_leave_balance_summary/test_employee_leave_balance_summary.py b/erpnext/hr/report/employee_leave_balance_summary/test_employee_leave_balance_summary.py
new file mode 100644
index 0000000..2fd74b7
--- /dev/null
+++ b/erpnext/hr/report/employee_leave_balance_summary/test_employee_leave_balance_summary.py
@@ -0,0 +1,181 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+
+import unittest
+
+import frappe
+from frappe.utils import add_days, flt, get_year_ending, get_year_start, getdate
+
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list
+from erpnext.hr.doctype.leave_application.test_leave_application import (
+	get_first_sunday,
+	make_allocation_record,
+)
+from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation
+from erpnext.hr.report.employee_leave_balance_summary.employee_leave_balance_summary import execute
+from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
+	make_holiday_list,
+	make_leave_application,
+)
+
+test_records = frappe.get_test_records("Leave Type")
+
+
+class TestEmployeeLeaveBalance(unittest.TestCase):
+	def setUp(self):
+		for dt in [
+			"Leave Application",
+			"Leave Allocation",
+			"Salary Slip",
+			"Leave Ledger Entry",
+			"Leave Type",
+		]:
+			frappe.db.delete(dt)
+
+		frappe.set_user("Administrator")
+
+		self.employee_id = make_employee("test_emp_leave_balance@example.com", company="_Test Company")
+
+		self.date = getdate()
+		self.year_start = getdate(get_year_start(self.date))
+		self.year_end = getdate(get_year_ending(self.date))
+
+		self.holiday_list = make_holiday_list(
+			"_Test Emp Balance Holiday List", self.year_start, self.year_end
+		)
+
+	def tearDown(self):
+		frappe.db.rollback()
+
+	@set_holiday_list("_Test Emp Balance Holiday List", "_Test Company")
+	def test_employee_leave_balance_summary(self):
+		frappe.get_doc(test_records[0]).insert()
+
+		# 5 leaves
+		allocation1 = make_allocation_record(
+			employee=self.employee_id,
+			from_date=add_days(self.year_start, -11),
+			to_date=add_days(self.year_start, -1),
+			leaves=5,
+		)
+		# 30 leaves
+		allocation2 = make_allocation_record(
+			employee=self.employee_id, from_date=self.year_start, to_date=self.year_end
+		)
+
+		# 2 days leave within the first allocation
+		leave_application1 = make_leave_application(
+			self.employee_id,
+			add_days(self.year_start, -11),
+			add_days(self.year_start, -10),
+			"_Test Leave Type",
+		)
+		leave_application1.reload()
+
+		# expires 3 leaves
+		process_expired_allocation()
+
+		# 4 days leave within the second allocation
+		first_sunday = get_first_sunday(self.holiday_list, for_date=self.year_start)
+		leave_application2 = make_leave_application(
+			self.employee_id, add_days(first_sunday, 1), add_days(first_sunday, 4), "_Test Leave Type"
+		)
+		leave_application2.reload()
+
+		filters = frappe._dict(
+			{
+				"date": add_days(leave_application2.to_date, 1),
+				"company": "_Test Company",
+				"employee": self.employee_id,
+			}
+		)
+
+		report = execute(filters)
+
+		expected_data = [
+			[
+				self.employee_id,
+				"test_emp_leave_balance@example.com",
+				frappe.db.get_value("Employee", self.employee_id, "department"),
+				flt(
+					allocation1.new_leaves_allocated  # allocated = 5
+					+ allocation2.new_leaves_allocated  # allocated = 30
+					- leave_application1.total_leave_days  # leaves taken in the 1st alloc = 2
+					- (
+						allocation1.new_leaves_allocated - leave_application1.total_leave_days
+					)  # leaves expired from 1st alloc = 3
+					- leave_application2.total_leave_days  # leaves taken in the 2nd alloc = 4
+				),
+			]
+		]
+
+		self.assertEqual(report[1], expected_data)
+
+	@set_holiday_list("_Test Emp Balance Holiday List", "_Test Company")
+	def test_get_leave_balance_near_alloc_expiry(self):
+		frappe.get_doc(test_records[0]).insert()
+
+		# 30 leaves allocated
+		allocation = make_allocation_record(
+			employee=self.employee_id, from_date=self.year_start, to_date=self.year_end
+		)
+		# 4 days leave application in the first allocation
+		first_sunday = get_first_sunday(self.holiday_list, for_date=self.year_start)
+		leave_application = make_leave_application(
+			self.employee_id, add_days(first_sunday, 1), add_days(first_sunday, 4), "_Test Leave Type"
+		)
+		leave_application.reload()
+
+		# Leave balance should show actual balance, and not "consumption balance as per remaining days", near alloc end date
+		# eg: 3 days left for alloc to end, leave balance should still be 26 and not 3
+		filters = frappe._dict(
+			{"date": add_days(self.year_end, -3), "company": "_Test Company", "employee": self.employee_id}
+		)
+		report = execute(filters)
+
+		expected_data = [
+			[
+				self.employee_id,
+				"test_emp_leave_balance@example.com",
+				frappe.db.get_value("Employee", self.employee_id, "department"),
+				flt(allocation.new_leaves_allocated - leave_application.total_leave_days),
+			]
+		]
+
+		self.assertEqual(report[1], expected_data)
+
+	@set_holiday_list("_Test Emp Balance Holiday List", "_Test Company")
+	def test_employee_status_filter(self):
+		frappe.get_doc(test_records[0]).insert()
+
+		inactive_emp = make_employee("test_emp_status@example.com", company="_Test Company")
+		allocation = make_allocation_record(
+			employee=inactive_emp, from_date=self.year_start, to_date=self.year_end
+		)
+
+		# set employee as inactive
+		frappe.db.set_value("Employee", inactive_emp, "status", "Inactive")
+
+		filters = frappe._dict(
+			{
+				"date": allocation.from_date,
+				"company": "_Test Company",
+				"employee": inactive_emp,
+				"employee_status": "Active",
+			}
+		)
+		report = execute(filters)
+		self.assertEqual(len(report[1]), 0)
+
+		filters = frappe._dict(
+			{
+				"date": allocation.from_date,
+				"company": "_Test Company",
+				"employee": inactive_emp,
+				"employee_status": "Inactive",
+			}
+		)
+		report = execute(filters)
+		self.assertEqual(len(report[1]), 1)
diff --git a/erpnext/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.py b/erpnext/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.py
index 00a4a7c..f13fabf 100644
--- a/erpnext/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.py
+++ b/erpnext/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.py
@@ -21,16 +21,21 @@
 		_("Name") + ":Data:200",
 		_("Date") + ":Date:100",
 		_("Status") + ":Data:70",
-		_("Holiday") + ":Data:200"
+		_("Holiday") + ":Data:200",
 	]
 
+
 def get_employees(filters):
-	holiday_filter = [["holiday_date", ">=", filters.from_date], ["holiday_date", "<=", filters.to_date]]
+	holiday_filter = [
+		["holiday_date", ">=", filters.from_date],
+		["holiday_date", "<=", filters.to_date],
+	]
 	if filters.holiday_list:
 		holiday_filter.append(["parent", "=", filters.holiday_list])
 
-	holidays = frappe.get_all("Holiday", fields=["holiday_date", "description"],
-				filters=holiday_filter)
+	holidays = frappe.get_all(
+		"Holiday", fields=["holiday_date", "description"], filters=holiday_filter
+	)
 
 	holiday_names = {}
 	holidays_list = []
@@ -39,18 +44,23 @@
 		holidays_list.append(holiday.holiday_date)
 		holiday_names[holiday.holiday_date] = holiday.description
 
-	if(holidays_list):
+	if holidays_list:
 		cond = " attendance_date in %(holidays_list)s"
 
 		if filters.holiday_list:
-			cond += """ and (employee in (select employee from tabEmployee where holiday_list = %(holidays)s))"""
+			cond += (
+				""" and (employee in (select employee from tabEmployee where holiday_list = %(holidays)s))"""
+			)
 
-		employee_list = frappe.db.sql("""select
+		employee_list = frappe.db.sql(
+			"""select
 				employee, employee_name, attendance_date, status
 			from tabAttendance
-			where %s"""% cond.format(', '.join(["%s"] * len(holidays_list))),
-				{'holidays_list':holidays_list,
-				 'holidays':filters.holiday_list}, as_list=True)
+			where %s"""
+			% cond.format(", ".join(["%s"] * len(holidays_list))),
+			{"holidays_list": holidays_list, "holidays": filters.holiday_list},
+			as_list=True,
+		)
 
 		for employee_data in employee_list:
 			employee_data.append(holiday_names[employee_data[2]])
diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js
index 42f7cdb..6f4bbd5 100644
--- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js
+++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.js
@@ -66,8 +66,7 @@
 			"Default": 0,
 		}
 	],
-
-	"onload": function() {
+	onload: function() {
 		return  frappe.call({
 			method: "erpnext.hr.report.monthly_attendance_sheet.monthly_attendance_sheet.get_attendance_years",
 			callback: function(r) {
@@ -78,5 +77,25 @@
 				year_filter.set_input(year_filter.df.default);
 			}
 		});
+	},
+	formatter: function(value, row, column, data, default_formatter) {
+		value = default_formatter(value, row, column, data);
+		const summarized_view = frappe.query_report.get_filter_value('summarized_view');
+		const group_by = frappe.query_report.get_filter_value('group_by');
+
+		if (!summarized_view) {
+			if ((group_by && column.colIndex > 3) || (!group_by && column.colIndex > 2)) {
+				if (value == 'P' || value == 'WFH')
+					value = "<span style='color:green'>" + value + "</span>";
+				else if (value == 'A')
+					value = "<span style='color:red'>" + value + "</span>";
+				else if (value == 'HD')
+					value = "<span style='color:orange'>" + value + "</span>";
+				else if (value == 'L')
+					value = "<span style='color:#318AD8'>" + value + "</span>";
+			}
+		}
+
+		return value;
 	}
 }
diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py
index 9a993e5..efd2d38 100644
--- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py
+++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py
@@ -3,315 +3,618 @@
 
 
 from calendar import monthrange
+from itertools import groupby
+from typing import Dict, List, Optional, Tuple
 
 import frappe
-from frappe import _, msgprint
+from frappe import _
+from frappe.query_builder.functions import Count, Extract, Sum
 from frappe.utils import cint, cstr, getdate
 
+Filters = frappe._dict
+
 status_map = {
+	"Present": "P",
 	"Absent": "A",
 	"Half Day": "HD",
-	"Holiday": "<b>H</b>",
-	"Weekly Off": "<b>WO</b>",
+	"Work From Home": "WFH",
 	"On Leave": "L",
-	"Present": "P",
-	"Work From Home": "WFH"
-	}
+	"Holiday": "H",
+	"Weekly Off": "WO",
+}
 
-day_abbr = [
-	"Mon",
-	"Tue",
-	"Wed",
-	"Thu",
-	"Fri",
-	"Sat",
-	"Sun"
-]
+day_abbr = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
 
-def execute(filters=None):
-	if not filters: filters = {}
 
-	if filters.hide_year_field == 1:
-		filters.year = 2020
+def execute(filters: Optional[Filters] = None) -> Tuple:
+	filters = frappe._dict(filters or {})
 
-	conditions, filters = get_conditions(filters)
-	columns, days = get_columns(filters)
-	att_map = get_attendance_list(conditions, filters)
-	if not att_map:
+	if not (filters.month and filters.year):
+		frappe.throw(_("Please select month and year."))
+
+	attendance_map = get_attendance_map(filters)
+	if not attendance_map:
+		frappe.msgprint(_("No attendance records found."), alert=True, indicator="orange")
+		return [], [], None, None
+
+	columns = get_columns(filters)
+	data = get_data(filters, attendance_map)
+
+	if not data:
+		frappe.msgprint(
+			_("No attendance records found for this criteria."), alert=True, indicator="orange"
+		)
 		return columns, [], None, None
 
-	if filters.group_by:
-		emp_map, group_by_parameters = get_employee_details(filters.group_by, filters.company)
-		holiday_list = []
-		for parameter in group_by_parameters:
-			h_list = [emp_map[parameter][d]["holiday_list"] for d in emp_map[parameter] if emp_map[parameter][d]["holiday_list"]]
-			holiday_list += h_list
-	else:
-		emp_map = get_employee_details(filters.group_by, filters.company)
-		holiday_list = [emp_map[d]["holiday_list"] for d in emp_map if emp_map[d]["holiday_list"]]
+	message = get_message() if not filters.summarized_view else ""
+	chart = get_chart_data(attendance_map, filters)
+
+	return columns, data, message, chart
 
 
-	default_holiday_list = frappe.get_cached_value('Company',  filters.get("company"),  "default_holiday_list")
-	holiday_list.append(default_holiday_list)
-	holiday_list = list(set(holiday_list))
-	holiday_map = get_holiday(holiday_list, filters["month"])
+def get_message() -> str:
+	message = ""
+	colors = ["green", "red", "orange", "green", "#318AD8", "", ""]
 
-	data = []
+	count = 0
+	for status, abbr in status_map.items():
+		message += f"""
+			<span style='border-left: 2px solid {colors[count]}; padding-right: 12px; padding-left: 5px; margin-right: 3px;'>
+				{status} - {abbr}
+			</span>
+		"""
+		count += 1
 
-	leave_types = frappe.db.get_list("Leave Type")
-	leave_list = None
-	if filters.summarized_view:
-		leave_list = [d.name + ":Float:120" for d in leave_types]
-		columns.extend(leave_list)
-		columns.extend([_("Total Late Entries") + ":Float:120", _("Total Early Exits") + ":Float:120"])
-
-	if filters.group_by:
-		emp_att_map = {}
-		for parameter in group_by_parameters:
-			emp_map_set = set([key for key in emp_map[parameter].keys()])
-			att_map_set = set([key for key in att_map.keys()])
-			if (att_map_set & emp_map_set):
-				parameter_row = ["<b>"+ parameter + "</b>"] + ['' for day in range(filters["total_days_in_month"] + 2)]
-				data.append(parameter_row)
-				record, emp_att_data = add_data(emp_map[parameter], att_map, filters, holiday_map, conditions, default_holiday_list, leave_types=leave_types)
-				emp_att_map.update(emp_att_data)
-				data += record
-	else:
-		record, emp_att_map = add_data(emp_map, att_map, filters, holiday_map, conditions, default_holiday_list, leave_types=leave_types)
-		data += record
-
-	chart_data = get_chart_data(emp_att_map, days)
-
-	return columns, data, None, chart_data
-
-def get_chart_data(emp_att_map, days):
-	labels = []
-	datasets = [
-		{"name": "Absent", "values": []},
-		{"name": "Present", "values": []},
-		{"name": "Leave", "values": []},
-	]
-	for idx, day in enumerate(days, start=0):
-		p = day.replace("::65", "")
-		labels.append(day.replace("::65", ""))
-		total_absent_on_day = 0
-		total_leave_on_day = 0
-		total_present_on_day = 0
-		total_holiday = 0
-		for emp in emp_att_map.keys():
-			if emp_att_map[emp][idx]:
-				if emp_att_map[emp][idx] == "A":
-					total_absent_on_day += 1
-				if emp_att_map[emp][idx] in ["P", "WFH"]:
-					total_present_on_day += 1
-				if emp_att_map[emp][idx] == "HD":
-					total_present_on_day += 0.5
-					total_leave_on_day += 0.5
-				if emp_att_map[emp][idx] == "L":
-					total_leave_on_day += 1
+	return message
 
 
-		datasets[0]["values"].append(total_absent_on_day)
-		datasets[1]["values"].append(total_present_on_day)
-		datasets[2]["values"].append(total_leave_on_day)
-
-
-	chart = {
-		"data": {
-			'labels': labels,
-			'datasets': datasets
-		}
-	}
-
-	chart["type"] = "line"
-
-	return chart
-
-def add_data(employee_map, att_map, filters, holiday_map, conditions, default_holiday_list, leave_types=None):
-
-	record = []
-	emp_att_map = {}
-	for emp in employee_map:
-		emp_det = employee_map.get(emp)
-		if not emp_det or emp not in att_map:
-			continue
-
-		row = []
-		if filters.group_by:
-			row += [" "]
-		row += [emp, emp_det.employee_name]
-
-		total_p = total_a = total_l = total_h = total_um= 0.0
-		emp_status_map = []
-		for day in range(filters["total_days_in_month"]):
-			status = None
-			status = att_map.get(emp).get(day + 1)
-
-			if status is None and holiday_map:
-				emp_holiday_list = emp_det.holiday_list if emp_det.holiday_list else default_holiday_list
-
-				if emp_holiday_list in holiday_map:
-					for idx, ele in enumerate(holiday_map[emp_holiday_list]):
-						if day+1 == holiday_map[emp_holiday_list][idx][0]:
-							if holiday_map[emp_holiday_list][idx][1]:
-								status = "Weekly Off"
-							else:
-								status = "Holiday"
-							total_h += 1
-
-			abbr = status_map.get(status, "")
-			emp_status_map.append(abbr)
-
-			if  filters.summarized_view:
-				if status == "Present" or status == "Work From Home":
-					total_p += 1
-				elif status == "Absent":
-					total_a += 1
-				elif status == "On Leave":
-					total_l += 1
-				elif status == "Half Day":
-					total_p += 0.5
-					total_a += 0.5
-					total_l += 0.5
-				elif not status:
-					total_um += 1
-
-		if not filters.summarized_view:
-			row += emp_status_map
-
-		if filters.summarized_view:
-			row += [total_p, total_l, total_a, total_h, total_um]
-
-		if not filters.get("employee"):
-			filters.update({"employee": emp})
-			conditions += " and employee = %(employee)s"
-		elif not filters.get("employee") == emp:
-			filters.update({"employee": emp})
-
-		if filters.summarized_view:
-			leave_details = frappe.db.sql("""select leave_type, status, count(*) as count from `tabAttendance`\
-				where leave_type is not NULL %s group by leave_type, status""" % conditions, filters, as_dict=1)
-
-			time_default_counts = frappe.db.sql("""select (select count(*) from `tabAttendance` where \
-				late_entry = 1 %s) as late_entry_count, (select count(*) from tabAttendance where \
-				early_exit = 1 %s) as early_exit_count""" % (conditions, conditions), filters)
-
-			leaves = {}
-			for d in leave_details:
-				if d.status == "Half Day":
-					d.count = d.count * 0.5
-				if d.leave_type in leaves:
-					leaves[d.leave_type] += d.count
-				else:
-					leaves[d.leave_type] = d.count
-
-			for d in leave_types:
-				if d.name in leaves:
-					row.append(leaves[d.name])
-				else:
-					row.append("0.0")
-
-			row.extend([time_default_counts[0][0],time_default_counts[0][1]])
-		emp_att_map[emp] = emp_status_map
-		record.append(row)
-
-	return record, emp_att_map
-
-def get_columns(filters):
-
+def get_columns(filters: Filters) -> List[Dict]:
 	columns = []
 
 	if filters.group_by:
-		columns = [_(filters.group_by)+ ":Link/Branch:120"]
+		columns.append(
+			{
+				"label": _(filters.group_by),
+				"fieldname": frappe.scrub(filters.group_by),
+				"fieldtype": "Link",
+				"options": "Branch",
+				"width": 120,
+			}
+		)
 
-	columns += [
-		_("Employee") + ":Link/Employee:120", _("Employee Name") + ":Data/:120"
-	]
-	days = []
-	for day in range(filters["total_days_in_month"]):
-		date = str(filters.year) + "-" + str(filters.month)+ "-" + str(day+1)
-		day_name = day_abbr[getdate(date).weekday()]
-		days.append(cstr(day+1)+ " " +day_name +"::65")
-	if not filters.summarized_view:
-		columns += days
+	columns.extend(
+		[
+			{
+				"label": _("Employee"),
+				"fieldname": "employee",
+				"fieldtype": "Link",
+				"options": "Employee",
+				"width": 135,
+			},
+			{"label": _("Employee Name"), "fieldname": "employee_name", "fieldtype": "Data", "width": 120},
+		]
+	)
 
 	if filters.summarized_view:
-		columns += [_("Total Present") + ":Float:120", _("Total Leaves") + ":Float:120",  _("Total Absent") + ":Float:120", _("Total Holidays") + ":Float:120", _("Unmarked Days")+ ":Float:120"]
-	return columns, days
+		columns.extend(
+			[
+				{
+					"label": _("Total Present"),
+					"fieldname": "total_present",
+					"fieldtype": "Float",
+					"width": 110,
+				},
+				{"label": _("Total Leaves"), "fieldname": "total_leaves", "fieldtype": "Float", "width": 110},
+				{"label": _("Total Absent"), "fieldname": "total_absent", "fieldtype": "Float", "width": 110},
+				{
+					"label": _("Total Holidays"),
+					"fieldname": "total_holidays",
+					"fieldtype": "Float",
+					"width": 120,
+				},
+				{
+					"label": _("Unmarked Days"),
+					"fieldname": "unmarked_days",
+					"fieldtype": "Float",
+					"width": 130,
+				},
+			]
+		)
+		columns.extend(get_columns_for_leave_types())
+		columns.extend(
+			[
+				{
+					"label": _("Total Late Entries"),
+					"fieldname": "total_late_entries",
+					"fieldtype": "Float",
+					"width": 140,
+				},
+				{
+					"label": _("Total Early Exits"),
+					"fieldname": "total_early_exits",
+					"fieldtype": "Float",
+					"width": 140,
+				},
+			]
+		)
+	else:
+		columns.append({"label": _("Shift"), "fieldname": "shift", "fieldtype": "Data", "width": 120})
+		columns.extend(get_columns_for_days(filters))
 
-def get_attendance_list(conditions, filters):
-	attendance_list = frappe.db.sql("""select employee, day(attendance_date) as day_of_month,
-		status from tabAttendance where docstatus = 1 %s order by employee, attendance_date""" %
-		conditions, filters, as_dict=1)
+	return columns
 
-	if not attendance_list:
-		msgprint(_("No attendance record found"), alert=True, indicator="orange")
 
-	att_map = {}
+def get_columns_for_leave_types() -> List[Dict]:
+	leave_types = frappe.db.get_all("Leave Type", pluck="name")
+	types = []
+	for entry in leave_types:
+		types.append(
+			{"label": entry, "fieldname": frappe.scrub(entry), "fieldtype": "Float", "width": 120}
+		)
+
+	return types
+
+
+def get_columns_for_days(filters: Filters) -> List[Dict]:
+	total_days = get_total_days_in_month(filters)
+	days = []
+
+	for day in range(1, total_days + 1):
+		# forms the dates from selected year and month from filters
+		date = "{}-{}-{}".format(cstr(filters.year), cstr(filters.month), cstr(day))
+		# gets abbr from weekday number
+		weekday = day_abbr[getdate(date).weekday()]
+		# sets days as 1 Mon, 2 Tue, 3 Wed
+		label = "{} {}".format(cstr(day), weekday)
+		days.append({"label": label, "fieldtype": "Data", "fieldname": day, "width": 65})
+
+	return days
+
+
+def get_total_days_in_month(filters: Filters) -> int:
+	return monthrange(cint(filters.year), cint(filters.month))[1]
+
+
+def get_data(filters: Filters, attendance_map: Dict) -> List[Dict]:
+	employee_details, group_by_param_values = get_employee_related_details(
+		filters.group_by, filters.company
+	)
+	holiday_map = get_holiday_map(filters)
+	data = []
+
+	if filters.group_by:
+		group_by_column = frappe.scrub(filters.group_by)
+
+		for value in group_by_param_values:
+			if not value:
+				continue
+
+			records = get_rows(employee_details[value], filters, holiday_map, attendance_map)
+
+			if records:
+				data.append({group_by_column: frappe.bold(value)})
+				data.extend(records)
+	else:
+		data = get_rows(employee_details, filters, holiday_map, attendance_map)
+
+	return data
+
+
+def get_attendance_map(filters: Filters) -> Dict:
+	"""Returns a dictionary of employee wise attendance map as per shifts for all the days of the month like
+	{
+	        'employee1': {
+	                'Morning Shift': {1: 'Present', 2: 'Absent', ...}
+	                'Evening Shift': {1: 'Absent', 2: 'Present', ...}
+	        },
+	        'employee2': {
+	                'Afternoon Shift': {1: 'Present', 2: 'Absent', ...}
+	                'Night Shift': {1: 'Absent', 2: 'Absent', ...}
+	        }
+	}
+	"""
+	Attendance = frappe.qb.DocType("Attendance")
+	query = (
+		frappe.qb.from_(Attendance)
+		.select(
+			Attendance.employee,
+			Extract("day", Attendance.attendance_date).as_("day_of_month"),
+			Attendance.status,
+			Attendance.shift,
+		)
+		.where(
+			(Attendance.docstatus == 1)
+			& (Attendance.company == filters.company)
+			& (Extract("month", Attendance.attendance_date) == filters.month)
+			& (Extract("year", Attendance.attendance_date) == filters.year)
+		)
+	)
+	if filters.employee:
+		query = query.where(Attendance.employee == filters.employee)
+	query = query.orderby(Attendance.employee, Attendance.attendance_date)
+
+	attendance_list = query.run(as_dict=1)
+	attendance_map = {}
+
 	for d in attendance_list:
-		att_map.setdefault(d.employee, frappe._dict()).setdefault(d.day_of_month, "")
-		att_map[d.employee][d.day_of_month] = d.status
+		attendance_map.setdefault(d.employee, frappe._dict()).setdefault(d.shift, frappe._dict())
+		attendance_map[d.employee][d.shift][d.day_of_month] = d.status
 
-	return att_map
+	return attendance_map
 
-def get_conditions(filters):
-	if not (filters.get("month") and filters.get("year")):
-		msgprint(_("Please select month and year"), raise_exception=1)
 
-	filters["total_days_in_month"] = monthrange(cint(filters.year), cint(filters.month))[1]
-
-	conditions = " and month(attendance_date) = %(month)s and year(attendance_date) = %(year)s"
-
-	if filters.get("company"): conditions += " and company = %(company)s"
-	if filters.get("employee"): conditions += " and employee = %(employee)s"
-
-	return conditions, filters
-
-def get_employee_details(group_by, company):
-	emp_map = {}
-	query = """select name, employee_name, designation, department, branch, company,
-		holiday_list from `tabEmployee` where company = %s """ % frappe.db.escape(company)
+def get_employee_related_details(group_by: str, company: str) -> Tuple[Dict, List]:
+	"""Returns
+	1. nested dict for employee details
+	2. list of values for the group by filter
+	"""
+	Employee = frappe.qb.DocType("Employee")
+	query = (
+		frappe.qb.from_(Employee)
+		.select(
+			Employee.name,
+			Employee.employee_name,
+			Employee.designation,
+			Employee.grade,
+			Employee.department,
+			Employee.branch,
+			Employee.company,
+			Employee.holiday_list,
+		)
+		.where(Employee.company == company)
+	)
 
 	if group_by:
 		group_by = group_by.lower()
-		query += " order by " + group_by + " ASC"
+		query = query.orderby(group_by)
 
-	employee_details = frappe.db.sql(query , as_dict=1)
+	employee_details = query.run(as_dict=True)
 
-	group_by_parameters = []
+	group_by_param_values = []
+	emp_map = {}
+
 	if group_by:
+		for parameter, employees in groupby(employee_details, key=lambda d: d[group_by]):
+			group_by_param_values.append(parameter)
+			emp_map.setdefault(parameter, frappe._dict())
 
-		group_by_parameters = list(set(detail.get(group_by, "") for detail in employee_details if detail.get(group_by, "")))
-		for parameter in group_by_parameters:
-				emp_map[parameter] = {}
-
-
-	for d in employee_details:
-		if group_by and len(group_by_parameters):
-			if d.get(group_by, None):
-
-				emp_map[d.get(group_by)][d.name] = d
-		else:
-			emp_map[d.name] = d
-
-	if not group_by:
-		return emp_map
+			for emp in employees:
+				emp_map[parameter][emp.name] = emp
 	else:
-		return emp_map, group_by_parameters
+		for emp in employee_details:
+			emp_map[emp.name] = emp
 
-def get_holiday(holiday_list, month):
+	return emp_map, group_by_param_values
+
+
+def get_holiday_map(filters: Filters) -> Dict[str, List[Dict]]:
+	"""
+	Returns a dict of holidays falling in the filter month and year
+	with list name as key and list of holidays as values like
+	{
+	        'Holiday List 1': [
+	                {'day_of_month': '0' , 'weekly_off': 1},
+	                {'day_of_month': '1', 'weekly_off': 0}
+	        ],
+	        'Holiday List 2': [
+	                {'day_of_month': '0' , 'weekly_off': 1},
+	                {'day_of_month': '1', 'weekly_off': 0}
+	        ]
+	}
+	"""
+	# add default holiday list too
+	holiday_lists = frappe.db.get_all("Holiday List", pluck="name")
+	default_holiday_list = frappe.get_cached_value("Company", filters.company, "default_holiday_list")
+	holiday_lists.append(default_holiday_list)
+
 	holiday_map = frappe._dict()
-	for d in holiday_list:
-		if d:
-			holiday_map.setdefault(d, frappe.db.sql('''select day(holiday_date), weekly_off from `tabHoliday`
-				where parent=%s and month(holiday_date)=%s''', (d, month)))
+	Holiday = frappe.qb.DocType("Holiday")
+
+	for d in holiday_lists:
+		if not d:
+			continue
+
+		holidays = (
+			frappe.qb.from_(Holiday)
+			.select(Extract("day", Holiday.holiday_date).as_("day_of_month"), Holiday.weekly_off)
+			.where(
+				(Holiday.parent == d)
+				& (Extract("month", Holiday.holiday_date) == filters.month)
+				& (Extract("year", Holiday.holiday_date) == filters.year)
+			)
+		).run(as_dict=True)
+
+		holiday_map.setdefault(d, holidays)
 
 	return holiday_map
 
+
+def get_rows(
+	employee_details: Dict, filters: Filters, holiday_map: Dict, attendance_map: Dict
+) -> List[Dict]:
+	records = []
+	default_holiday_list = frappe.get_cached_value("Company", filters.company, "default_holiday_list")
+
+	for employee, details in employee_details.items():
+		emp_holiday_list = details.holiday_list or default_holiday_list
+		holidays = holiday_map.get(emp_holiday_list)
+
+		if filters.summarized_view:
+			attendance = get_attendance_status_for_summarized_view(employee, filters, holidays)
+			if not attendance:
+				continue
+
+			leave_summary = get_leave_summary(employee, filters)
+			entry_exits_summary = get_entry_exits_summary(employee, filters)
+
+			row = {"employee": employee, "employee_name": details.employee_name}
+			set_defaults_for_summarized_view(filters, row)
+			row.update(attendance)
+			row.update(leave_summary)
+			row.update(entry_exits_summary)
+
+			records.append(row)
+		else:
+			employee_attendance = attendance_map.get(employee)
+			if not employee_attendance:
+				continue
+
+			attendance_for_employee = get_attendance_status_for_detailed_view(
+				employee, filters, employee_attendance, holidays
+			)
+			# set employee details in the first row
+			attendance_for_employee[0].update(
+				{"employee": employee, "employee_name": details.employee_name}
+			)
+
+			records.extend(attendance_for_employee)
+
+	return records
+
+
+def set_defaults_for_summarized_view(filters, row):
+	for entry in get_columns(filters):
+		if entry.get("fieldtype") == "Float":
+			row[entry.get("fieldname")] = 0.0
+
+
+def get_attendance_status_for_summarized_view(
+	employee: str, filters: Filters, holidays: List
+) -> Dict:
+	"""Returns dict of attendance status for employee like
+	{'total_present': 1.5, 'total_leaves': 0.5, 'total_absent': 13.5, 'total_holidays': 8, 'unmarked_days': 5}
+	"""
+	summary, attendance_days = get_attendance_summary_and_days(employee, filters)
+	if not any(summary.values()):
+		return {}
+
+	total_days = get_total_days_in_month(filters)
+	total_holidays = total_unmarked_days = 0
+
+	for day in range(1, total_days + 1):
+		if day in attendance_days:
+			continue
+
+		status = get_holiday_status(day, holidays)
+		if status in ["Weekly Off", "Holiday"]:
+			total_holidays += 1
+		elif not status:
+			total_unmarked_days += 1
+
+	return {
+		"total_present": summary.total_present + summary.total_half_days,
+		"total_leaves": summary.total_leaves + summary.total_half_days,
+		"total_absent": summary.total_absent + summary.total_half_days,
+		"total_holidays": total_holidays,
+		"unmarked_days": total_unmarked_days,
+	}
+
+
+def get_attendance_summary_and_days(employee: str, filters: Filters) -> Tuple[Dict, List]:
+	Attendance = frappe.qb.DocType("Attendance")
+
+	present_case = (
+		frappe.qb.terms.Case()
+		.when(((Attendance.status == "Present") | (Attendance.status == "Work From Home")), 1)
+		.else_(0)
+	)
+	sum_present = Sum(present_case).as_("total_present")
+
+	absent_case = frappe.qb.terms.Case().when(Attendance.status == "Absent", 1).else_(0)
+	sum_absent = Sum(absent_case).as_("total_absent")
+
+	leave_case = frappe.qb.terms.Case().when(Attendance.status == "On Leave", 1).else_(0)
+	sum_leave = Sum(leave_case).as_("total_leaves")
+
+	half_day_case = frappe.qb.terms.Case().when(Attendance.status == "Half Day", 0.5).else_(0)
+	sum_half_day = Sum(half_day_case).as_("total_half_days")
+
+	summary = (
+		frappe.qb.from_(Attendance)
+		.select(
+			sum_present,
+			sum_absent,
+			sum_leave,
+			sum_half_day,
+		)
+		.where(
+			(Attendance.docstatus == 1)
+			& (Attendance.employee == employee)
+			& (Attendance.company == filters.company)
+			& (Extract("month", Attendance.attendance_date) == filters.month)
+			& (Extract("year", Attendance.attendance_date) == filters.year)
+		)
+	).run(as_dict=True)
+
+	days = (
+		frappe.qb.from_(Attendance)
+		.select(Extract("day", Attendance.attendance_date).as_("day_of_month"))
+		.distinct()
+		.where(
+			(Attendance.docstatus == 1)
+			& (Attendance.employee == employee)
+			& (Attendance.company == filters.company)
+			& (Extract("month", Attendance.attendance_date) == filters.month)
+			& (Extract("year", Attendance.attendance_date) == filters.year)
+		)
+	).run(pluck=True)
+
+	return summary[0], days
+
+
+def get_attendance_status_for_detailed_view(
+	employee: str, filters: Filters, employee_attendance: Dict, holidays: List
+) -> List[Dict]:
+	"""Returns list of shift-wise attendance status for employee
+	[
+	        {'shift': 'Morning Shift', 1: 'A', 2: 'P', 3: 'A'....},
+	        {'shift': 'Evening Shift', 1: 'P', 2: 'A', 3: 'P'....}
+	]
+	"""
+	total_days = get_total_days_in_month(filters)
+	attendance_values = []
+
+	for shift, status_dict in employee_attendance.items():
+		row = {"shift": shift}
+
+		for day in range(1, total_days + 1):
+			status = status_dict.get(day)
+			if status is None and holidays:
+				status = get_holiday_status(day, holidays)
+
+			abbr = status_map.get(status, "")
+			row[day] = abbr
+
+		attendance_values.append(row)
+
+	return attendance_values
+
+
+def get_holiday_status(day: int, holidays: List) -> str:
+	status = None
+	for holiday in holidays:
+		if day == holiday.get("day_of_month"):
+			if holiday.get("weekly_off"):
+				status = "Weekly Off"
+			else:
+				status = "Holiday"
+			break
+	return status
+
+
+def get_leave_summary(employee: str, filters: Filters) -> Dict[str, float]:
+	"""Returns a dict of leave type and corresponding leaves taken by employee like:
+	{'leave_without_pay': 1.0, 'sick_leave': 2.0}
+	"""
+	Attendance = frappe.qb.DocType("Attendance")
+	day_case = frappe.qb.terms.Case().when(Attendance.status == "Half Day", 0.5).else_(1)
+	sum_leave_days = Sum(day_case).as_("leave_days")
+
+	leave_details = (
+		frappe.qb.from_(Attendance)
+		.select(Attendance.leave_type, sum_leave_days)
+		.where(
+			(Attendance.employee == employee)
+			& (Attendance.docstatus == 1)
+			& (Attendance.company == filters.company)
+			& ((Attendance.leave_type.isnotnull()) | (Attendance.leave_type != ""))
+			& (Extract("month", Attendance.attendance_date) == filters.month)
+			& (Extract("year", Attendance.attendance_date) == filters.year)
+		)
+		.groupby(Attendance.leave_type)
+	).run(as_dict=True)
+
+	leaves = {}
+	for d in leave_details:
+		leave_type = frappe.scrub(d.leave_type)
+		leaves[leave_type] = d.leave_days
+
+	return leaves
+
+
+def get_entry_exits_summary(employee: str, filters: Filters) -> Dict[str, float]:
+	"""Returns total late entries and total early exits for employee like:
+	{'total_late_entries': 5, 'total_early_exits': 2}
+	"""
+	Attendance = frappe.qb.DocType("Attendance")
+
+	late_entry_case = frappe.qb.terms.Case().when(Attendance.late_entry == "1", "1")
+	count_late_entries = Count(late_entry_case).as_("total_late_entries")
+
+	early_exit_case = frappe.qb.terms.Case().when(Attendance.early_exit == "1", "1")
+	count_early_exits = Count(early_exit_case).as_("total_early_exits")
+
+	entry_exits = (
+		frappe.qb.from_(Attendance)
+		.select(count_late_entries, count_early_exits)
+		.where(
+			(Attendance.docstatus == 1)
+			& (Attendance.employee == employee)
+			& (Attendance.company == filters.company)
+			& (Extract("month", Attendance.attendance_date) == filters.month)
+			& (Extract("year", Attendance.attendance_date) == filters.year)
+		)
+	).run(as_dict=True)
+
+	return entry_exits[0]
+
+
 @frappe.whitelist()
-def get_attendance_years():
-	year_list = frappe.db.sql_list("""select distinct YEAR(attendance_date) from tabAttendance ORDER BY YEAR(attendance_date) DESC""")
-	if not year_list:
+def get_attendance_years() -> str:
+	"""Returns all the years for which attendance records exist"""
+	Attendance = frappe.qb.DocType("Attendance")
+	year_list = (
+		frappe.qb.from_(Attendance)
+		.select(Extract("year", Attendance.attendance_date).as_("year"))
+		.distinct()
+	).run(as_dict=True)
+
+	if year_list:
+		year_list.sort(key=lambda d: d.year, reverse=True)
+	else:
 		year_list = [getdate().year]
 
-	return "\n".join(str(year) for year in year_list)
+	return "\n".join(cstr(entry.year) for entry in year_list)
+
+
+def get_chart_data(attendance_map: Dict, filters: Filters) -> Dict:
+	days = get_columns_for_days(filters)
+	labels = []
+	absent = []
+	present = []
+	leave = []
+
+	for day in days:
+		labels.append(day["label"])
+		total_absent_on_day = total_leaves_on_day = total_present_on_day = 0
+
+		for employee, attendance_dict in attendance_map.items():
+			for shift, attendance in attendance_dict.items():
+				attendance_on_day = attendance.get(day["fieldname"])
+
+				if attendance_on_day == "Absent":
+					total_absent_on_day += 1
+				elif attendance_on_day in ["Present", "Work From Home"]:
+					total_present_on_day += 1
+				elif attendance_on_day == "Half Day":
+					total_present_on_day += 0.5
+					total_leaves_on_day += 0.5
+				elif attendance_on_day == "On Leave":
+					total_leaves_on_day += 1
+
+		absent.append(total_absent_on_day)
+		present.append(total_present_on_day)
+		leave.append(total_leaves_on_day)
+
+	return {
+		"data": {
+			"labels": labels,
+			"datasets": [
+				{"name": "Absent", "values": absent},
+				{"name": "Present", "values": present},
+				{"name": "Leave", "values": leave},
+			],
+		},
+		"type": "line",
+		"colors": ["red", "green", "blue"],
+	}
diff --git a/erpnext/hr/report/monthly_attendance_sheet/test_monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/test_monthly_attendance_sheet.py
new file mode 100644
index 0000000..cde7dd3
--- /dev/null
+++ b/erpnext/hr/report/monthly_attendance_sheet/test_monthly_attendance_sheet.py
@@ -0,0 +1,249 @@
+import frappe
+from dateutil.relativedelta import relativedelta
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import get_year_ending, get_year_start, getdate, now_datetime
+
+from erpnext.hr.doctype.attendance.attendance import mark_attendance
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list
+from erpnext.hr.doctype.leave_application.test_leave_application import make_allocation_record
+from erpnext.hr.report.monthly_attendance_sheet.monthly_attendance_sheet import execute
+from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
+	make_holiday_list,
+	make_leave_application,
+)
+
+test_dependencies = ["Shift Type"]
+
+
+class TestMonthlyAttendanceSheet(FrappeTestCase):
+	def setUp(self):
+		self.employee = make_employee("test_employee@example.com", company="_Test Company")
+		frappe.db.delete("Attendance")
+
+		date = getdate()
+		from_date = get_year_start(date)
+		to_date = get_year_ending(date)
+		make_holiday_list(from_date=from_date, to_date=to_date)
+
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
+	def test_monthly_attendance_sheet_report(self):
+		now = now_datetime()
+		previous_month = now.month - 1
+		previous_month_first = now.replace(day=1).replace(month=previous_month).date()
+
+		company = frappe.db.get_value("Employee", self.employee, "company")
+
+		# mark different attendance status on first 3 days of previous month
+		mark_attendance(self.employee, previous_month_first, "Absent")
+		mark_attendance(self.employee, previous_month_first + relativedelta(days=1), "Present")
+		mark_attendance(self.employee, previous_month_first + relativedelta(days=2), "On Leave")
+
+		filters = frappe._dict(
+			{
+				"month": previous_month,
+				"year": now.year,
+				"company": company,
+			}
+		)
+		report = execute(filters=filters)
+
+		record = report[1][0]
+		datasets = report[3]["data"]["datasets"]
+		absent = datasets[0]["values"]
+		present = datasets[1]["values"]
+		leaves = datasets[2]["values"]
+
+		# ensure correct attendance is reflected on the report
+		self.assertEqual(self.employee, record.get("employee"))
+		self.assertEqual(absent[0], 1)
+		self.assertEqual(present[1], 1)
+		self.assertEqual(leaves[2], 1)
+
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
+	def test_monthly_attendance_sheet_with_detailed_view(self):
+		now = now_datetime()
+		previous_month = now.month - 1
+		previous_month_first = now.replace(day=1).replace(month=previous_month).date()
+
+		company = frappe.db.get_value("Employee", self.employee, "company")
+
+		# attendance with shift
+		mark_attendance(self.employee, previous_month_first, "Absent", "Day Shift")
+		mark_attendance(
+			self.employee, previous_month_first + relativedelta(days=1), "Present", "Day Shift"
+		)
+
+		# attendance without shift
+		mark_attendance(self.employee, previous_month_first + relativedelta(days=2), "On Leave")
+		mark_attendance(self.employee, previous_month_first + relativedelta(days=3), "Present")
+
+		filters = frappe._dict(
+			{
+				"month": previous_month,
+				"year": now.year,
+				"company": company,
+			}
+		)
+		report = execute(filters=filters)
+
+		day_shift_row = report[1][0]
+		row_without_shift = report[1][1]
+
+		self.assertEqual(day_shift_row["shift"], "Day Shift")
+		self.assertEqual(day_shift_row[1], "A")  # absent on the 1st day of the month
+		self.assertEqual(day_shift_row[2], "P")  # present on the 2nd day
+
+		self.assertEqual(row_without_shift["shift"], None)
+		self.assertEqual(row_without_shift[3], "L")  # on leave on the 3rd day
+		self.assertEqual(row_without_shift[4], "P")  # present on the 4th day
+
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
+	def test_monthly_attendance_sheet_with_summarized_view(self):
+		now = now_datetime()
+		previous_month = now.month - 1
+		previous_month_first = now.replace(day=1).replace(month=previous_month).date()
+
+		company = frappe.db.get_value("Employee", self.employee, "company")
+
+		# attendance with shift
+		mark_attendance(self.employee, previous_month_first, "Absent", "Day Shift")
+		mark_attendance(
+			self.employee, previous_month_first + relativedelta(days=1), "Present", "Day Shift"
+		)
+		mark_attendance(
+			self.employee, previous_month_first + relativedelta(days=2), "Half Day"
+		)  # half day
+
+		mark_attendance(
+			self.employee, previous_month_first + relativedelta(days=3), "Present"
+		)  # attendance without shift
+		mark_attendance(
+			self.employee, previous_month_first + relativedelta(days=4), "Present", late_entry=1
+		)  # late entry
+		mark_attendance(
+			self.employee, previous_month_first + relativedelta(days=5), "Present", early_exit=1
+		)  # early exit
+
+		leave_application = get_leave_application(self.employee)
+
+		filters = frappe._dict(
+			{"month": previous_month, "year": now.year, "company": company, "summarized_view": 1}
+		)
+		report = execute(filters=filters)
+
+		row = report[1][0]
+		self.assertEqual(row["employee"], self.employee)
+
+		# 4 present + half day absent 0.5
+		self.assertEqual(row["total_present"], 4.5)
+		# 1 present + half day absent 0.5
+		self.assertEqual(row["total_absent"], 1.5)
+		# leave days + half day leave 0.5
+		self.assertEqual(row["total_leaves"], leave_application.total_leave_days + 0.5)
+
+		self.assertEqual(row["_test_leave_type"], leave_application.total_leave_days)
+		self.assertEqual(row["total_late_entries"], 1)
+		self.assertEqual(row["total_early_exits"], 1)
+
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
+	def test_attendance_with_group_by_filter(self):
+		now = now_datetime()
+		previous_month = now.month - 1
+		previous_month_first = now.replace(day=1).replace(month=previous_month).date()
+
+		company = frappe.db.get_value("Employee", self.employee, "company")
+
+		# attendance with shift
+		mark_attendance(self.employee, previous_month_first, "Absent", "Day Shift")
+		mark_attendance(
+			self.employee, previous_month_first + relativedelta(days=1), "Present", "Day Shift"
+		)
+
+		# attendance without shift
+		mark_attendance(self.employee, previous_month_first + relativedelta(days=2), "On Leave")
+		mark_attendance(self.employee, previous_month_first + relativedelta(days=3), "Present")
+
+		filters = frappe._dict(
+			{"month": previous_month, "year": now.year, "company": company, "group_by": "Department"}
+		)
+		report = execute(filters=filters)
+
+		department = frappe.db.get_value("Employee", self.employee, "department")
+		department_row = report[1][0]
+		self.assertIn(department, department_row["department"])
+
+		day_shift_row = report[1][1]
+		row_without_shift = report[1][2]
+
+		self.assertEqual(day_shift_row["shift"], "Day Shift")
+		self.assertEqual(day_shift_row[1], "A")  # absent on the 1st day of the month
+		self.assertEqual(day_shift_row[2], "P")  # present on the 2nd day
+
+		self.assertEqual(row_without_shift["shift"], None)
+		self.assertEqual(row_without_shift[3], "L")  # on leave on the 3rd day
+		self.assertEqual(row_without_shift[4], "P")  # present on the 4th day
+
+	def test_attendance_with_employee_filter(self):
+		now = now_datetime()
+		previous_month = now.month - 1
+		previous_month_first = now.replace(day=1).replace(month=previous_month).date()
+
+		company = frappe.db.get_value("Employee", self.employee, "company")
+
+		# mark different attendance status on first 3 days of previous month
+		mark_attendance(self.employee, previous_month_first, "Absent")
+		mark_attendance(self.employee, previous_month_first + relativedelta(days=1), "Present")
+		mark_attendance(self.employee, previous_month_first + relativedelta(days=2), "On Leave")
+
+		filters = frappe._dict(
+			{"month": previous_month, "year": now.year, "company": company, "employee": self.employee}
+		)
+		report = execute(filters=filters)
+
+		record = report[1][0]
+		datasets = report[3]["data"]["datasets"]
+		absent = datasets[0]["values"]
+		present = datasets[1]["values"]
+		leaves = datasets[2]["values"]
+
+		# ensure correct attendance is reflected on the report
+		self.assertEqual(self.employee, record.get("employee"))
+		self.assertEqual(absent[0], 1)
+		self.assertEqual(present[1], 1)
+		self.assertEqual(leaves[2], 1)
+
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
+	def test_validations(self):
+		# validation error for filters without month and year
+		self.assertRaises(frappe.ValidationError, execute_report_with_invalid_filters)
+
+		# execute report without attendance record
+		now = now_datetime()
+		previous_month = now.month - 1
+
+		company = frappe.db.get_value("Employee", self.employee, "company")
+		filters = frappe._dict(
+			{"month": previous_month, "year": now.year, "company": company, "group_by": "Department"}
+		)
+		report = execute(filters=filters)
+		self.assertEqual(report, ([], [], None, None))
+
+
+def get_leave_application(employee):
+	now = now_datetime()
+	previous_month = now.month - 1
+
+	date = getdate()
+	year_start = getdate(get_year_start(date))
+	year_end = getdate(get_year_ending(date))
+	make_allocation_record(employee=employee, from_date=year_start, to_date=year_end)
+
+	from_date = now.replace(day=7).replace(month=previous_month).date()
+	to_date = now.replace(day=8).replace(month=previous_month).date()
+	return make_leave_application(employee, from_date, to_date, "_Test Leave Type")
+
+
+def execute_report_with_invalid_filters():
+	filters = frappe._dict({"company": "_Test Company", "group_by": "Department"})
+	execute(filters=filters)
diff --git a/erpnext/hr/report/recruitment_analytics/recruitment_analytics.py b/erpnext/hr/report/recruitment_analytics/recruitment_analytics.py
index 6383a9b..b6caf40 100644
--- a/erpnext/hr/report/recruitment_analytics/recruitment_analytics.py
+++ b/erpnext/hr/report/recruitment_analytics/recruitment_analytics.py
@@ -8,7 +8,8 @@
 
 def execute(filters=None):
 
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 	filters = frappe._dict(filters)
 
 	columns = get_columns()
@@ -25,67 +26,53 @@
 			"fieldtype": "Link",
 			"fieldname": "staffing_plan",
 			"options": "Staffing Plan",
-			"width": 150
+			"width": 150,
 		},
 		{
 			"label": _("Job Opening"),
 			"fieldtype": "Link",
 			"fieldname": "job_opening",
 			"options": "Job Opening",
-			"width": 105
+			"width": 105,
 		},
 		{
 			"label": _("Job Applicant"),
 			"fieldtype": "Link",
 			"fieldname": "job_applicant",
 			"options": "Job Applicant",
-			"width": 150
+			"width": 150,
 		},
-		{
-			"label": _("Applicant name"),
-			"fieldtype": "data",
-			"fieldname": "applicant_name",
-			"width": 130
-		},
+		{"label": _("Applicant name"), "fieldtype": "data", "fieldname": "applicant_name", "width": 130},
 		{
 			"label": _("Application Status"),
 			"fieldtype": "Data",
 			"fieldname": "application_status",
-			"width": 150
+			"width": 150,
 		},
 		{
 			"label": _("Job Offer"),
 			"fieldtype": "Link",
 			"fieldname": "job_offer",
 			"options": "job Offer",
-			"width": 150
+			"width": 150,
 		},
-		{
-			"label": _("Designation"),
-			"fieldtype": "Data",
-			"fieldname": "designation",
-			"width": 100
-		},
-		{
-			"label": _("Offer Date"),
-			"fieldtype": "date",
-			"fieldname": "offer_date",
-			"width": 100
-		},
+		{"label": _("Designation"), "fieldtype": "Data", "fieldname": "designation", "width": 100},
+		{"label": _("Offer Date"), "fieldtype": "date", "fieldname": "offer_date", "width": 100},
 		{
 			"label": _("Job Offer status"),
 			"fieldtype": "Data",
 			"fieldname": "job_offer_status",
-			"width": 150
-		}
+			"width": 150,
+		},
 	]
 
+
 def get_data(filters):
 	data = []
 	staffing_plan_details = get_staffing_plan(filters)
-	staffing_plan_list  = list(set([details["name"] for details in staffing_plan_details]))
-	sp_jo_map , jo_list = get_job_opening(staffing_plan_list)
-	jo_ja_map , ja_list = get_job_applicant(jo_list)
+	staffing_plan_list = list(set([details["name"] for details in staffing_plan_details]))
+	sp_jo_map, jo_list = get_job_opening(staffing_plan_list)
+	jo_ja_map, ja_list = get_job_applicant(jo_list)
 	ja_joff_map = get_job_offer(ja_list)
 
 	for sp in sp_jo_map.keys():
@@ -100,37 +87,40 @@
 	if sp in sp_jo_map.keys():
 		for jo in sp_jo_map[sp]:
 			row = {
-				"staffing_plan" : sp,
-				"job_opening" : jo["name"],
+				"staffing_plan": sp,
+				"job_opening": jo["name"],
 			}
 			data.append(row)
-			child_row = get_child_row( jo["name"], jo_ja_map, ja_joff_map)
+			child_row = get_child_row(jo["name"], jo_ja_map, ja_joff_map)
 			data += child_row
 	return data
 
+
 def get_child_row(jo, jo_ja_map, ja_joff_map):
 	data = []
 	if jo in jo_ja_map.keys():
 		for ja in jo_ja_map[jo]:
 			row = {
-				"indent":1,
+				"indent": 1,
 				"job_applicant": ja.name,
 				"applicant_name": ja.applicant_name,
 				"application_status": ja.status,
 			}
 			if ja.name in ja_joff_map.keys():
-				jo_detail =ja_joff_map[ja.name][0]
+				jo_detail = ja_joff_map[ja.name][0]
 				row["job_offer"] = jo_detail.name
 				row["job_offer_status"] = jo_detail.status
-				row["offer_date"]= jo_detail.offer_date.strftime("%d-%m-%Y")
+				row["offer_date"] = jo_detail.offer_date.strftime("%d-%m-%Y")
 				row["designation"] = jo_detail.designation
 
 			data.append(row)
 	return data
 
+
 def get_staffing_plan(filters):
 
-	staffing_plan = frappe.db.sql("""
+	staffing_plan = frappe.db.sql(
+		"""
 	select
 		sp.name, sp.department, spd.designation, spd.vacancies, spd.current_count, spd.parent, sp.to_date
 	from
@@ -139,13 +129,20 @@
 			spd.parent = sp.name
 		And
 			sp.to_date > '{0}'
-		""".format(filters.on_date), as_dict = 1)
+		""".format(
+			filters.on_date
+		),
+		as_dict=1,
+	)
 
 	return staffing_plan
 
+
 def get_job_opening(sp_list):
 
-	job_openings = frappe.get_all("Job Opening", filters = [["staffing_plan", "IN", sp_list]], fields =["name", "staffing_plan"])
+	job_openings = frappe.get_all(
+		"Job Opening", filters=[["staffing_plan", "IN", sp_list]], fields=["name", "staffing_plan"]
+	)
 
 	sp_jo_map = {}
 	jo_list = []
@@ -160,12 +157,17 @@
 
 	return sp_jo_map, jo_list
 
+
 def get_job_applicant(jo_list):
 
 	jo_ja_map = {}
-	ja_list =[]
+	ja_list = []
 
-	applicants = frappe.get_all("Job Applicant", filters = [["job_title", "IN", jo_list]], fields =["name", "job_title","applicant_name", 'status'])
+	applicants = frappe.get_all(
+		"Job Applicant",
+		filters=[["job_title", "IN", jo_list]],
+		fields=["name", "job_title", "applicant_name", "status"],
+	)
 
 	for applicant in applicants:
 		if applicant.job_title not in jo_ja_map.keys():
@@ -175,12 +177,17 @@
 
 		ja_list.append(applicant.name)
 
-	return jo_ja_map , ja_list
+	return jo_ja_map, ja_list
+
 
 def get_job_offer(ja_list):
 	ja_joff_map = {}
 
-	offers = frappe.get_all("Job Offer", filters = [["job_applicant", "IN", ja_list]], fields =["name", "job_applicant", "status", 'offer_date', 'designation'])
+	offers = frappe.get_all(
+		"Job Offer",
+		filters=[["job_applicant", "IN", ja_list]],
+		fields=["name", "job_applicant", "status", "offer_date", "designation"],
+	)
 
 	for offer in offers:
 		if offer.job_applicant not in ja_joff_map.keys():
diff --git a/erpnext/hr/report/vehicle_expenses/test_vehicle_expenses.py b/erpnext/hr/report/vehicle_expenses/test_vehicle_expenses.py
index 8672e68..e546810 100644
--- a/erpnext/hr/report/vehicle_expenses/test_vehicle_expenses.py
+++ b/erpnext/hr/report/vehicle_expenses/test_vehicle_expenses.py
@@ -17,12 +17,14 @@
 class TestVehicleExpenses(unittest.TestCase):
 	@classmethod
 	def setUpClass(self):
-		frappe.db.sql('delete from `tabVehicle Log`')
+		frappe.db.sql("delete from `tabVehicle Log`")
 
-		employee_id = frappe.db.sql('''select name from `tabEmployee` where name="testdriver@example.com"''')
+		employee_id = frappe.db.sql(
+			"""select name from `tabEmployee` where name='testdriver@example.com' """
+		)
 		self.employee_id = employee_id[0][0] if employee_id else None
 		if not self.employee_id:
-			self.employee_id = make_employee('testdriver@example.com', company='_Test Company')
+			self.employee_id = make_employee("testdriver@example.com", company="_Test Company")
 
 		self.license_plate = get_vehicle(self.employee_id)
 
@@ -31,36 +33,35 @@
 		expense_claim = make_expense_claim(vehicle_log.name)
 
 		# Based on Fiscal Year
-		filters = {
-			'filter_based_on': 'Fiscal Year',
-			'fiscal_year': get_fiscal_year(getdate())[0]
-		}
+		filters = {"filter_based_on": "Fiscal Year", "fiscal_year": get_fiscal_year(getdate())[0]}
 
 		report = execute(filters)
 
-		expected_data = [{
-			'vehicle': self.license_plate,
-			'make': 'Maruti',
-			'model': 'PCM',
-			'location': 'Mumbai',
-			'log_name': vehicle_log.name,
-			'odometer': 5010,
-			'date': getdate(),
-			'fuel_qty': 50.0,
-			'fuel_price': 500.0,
-			'fuel_expense': 25000.0,
-			'service_expense': 2000.0,
-			'employee': self.employee_id
-		}]
+		expected_data = [
+			{
+				"vehicle": self.license_plate,
+				"make": "Maruti",
+				"model": "PCM",
+				"location": "Mumbai",
+				"log_name": vehicle_log.name,
+				"odometer": 5010,
+				"date": getdate(),
+				"fuel_qty": 50.0,
+				"fuel_price": 500.0,
+				"fuel_expense": 25000.0,
+				"service_expense": 2000.0,
+				"employee": self.employee_id,
+			}
+		]
 
 		self.assertEqual(report[1], expected_data)
 
 		# Based on Date Range
 		fiscal_year = get_fiscal_year(getdate(), as_dict=True)
 		filters = {
-			'filter_based_on': 'Date Range',
-			'from_date': fiscal_year.year_start_date,
-			'to_date': fiscal_year.year_end_date
+			"filter_based_on": "Date Range",
+			"from_date": fiscal_year.year_start_date,
+			"to_date": fiscal_year.year_end_date,
 		}
 
 		report = execute(filters)
@@ -68,9 +69,9 @@
 
 		# clean up
 		vehicle_log.cancel()
-		frappe.delete_doc('Expense Claim', expense_claim.name)
-		frappe.delete_doc('Vehicle Log', vehicle_log.name)
+		frappe.delete_doc("Expense Claim", expense_claim.name)
+		frappe.delete_doc("Vehicle Log", vehicle_log.name)
 
 	def tearDown(self):
-		frappe.delete_doc('Vehicle', self.license_plate, force=1)
-		frappe.delete_doc('Employee', self.employee_id, force=1)
+		frappe.delete_doc("Vehicle", self.license_plate, force=1)
+		frappe.delete_doc("Employee", self.employee_id, force=1)
diff --git a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py
index 17d1e9d..fc5510d 100644
--- a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py
+++ b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py
@@ -18,83 +18,44 @@
 
 	return columns, data, None, chart
 
+
 def get_columns():
 	return [
 		{
-			'fieldname': 'vehicle',
-			'fieldtype': 'Link',
-			'label': _('Vehicle'),
-			'options': 'Vehicle',
-			'width': 150
+			"fieldname": "vehicle",
+			"fieldtype": "Link",
+			"label": _("Vehicle"),
+			"options": "Vehicle",
+			"width": 150,
+		},
+		{"fieldname": "make", "fieldtype": "Data", "label": _("Make"), "width": 100},
+		{"fieldname": "model", "fieldtype": "Data", "label": _("Model"), "width": 80},
+		{"fieldname": "location", "fieldtype": "Data", "label": _("Location"), "width": 100},
+		{
+			"fieldname": "log_name",
+			"fieldtype": "Link",
+			"label": _("Vehicle Log"),
+			"options": "Vehicle Log",
+			"width": 100,
+		},
+		{"fieldname": "odometer", "fieldtype": "Int", "label": _("Odometer Value"), "width": 120},
+		{"fieldname": "date", "fieldtype": "Date", "label": _("Date"), "width": 100},
+		{"fieldname": "fuel_qty", "fieldtype": "Float", "label": _("Fuel Qty"), "width": 80},
+		{"fieldname": "fuel_price", "fieldtype": "Float", "label": _("Fuel Price"), "width": 100},
+		{"fieldname": "fuel_expense", "fieldtype": "Currency", "label": _("Fuel Expense"), "width": 150},
+		{
+			"fieldname": "service_expense",
+			"fieldtype": "Currency",
+			"label": _("Service Expense"),
+			"width": 150,
 		},
 		{
-			'fieldname': 'make',
-			'fieldtype': 'Data',
-			'label': _('Make'),
-			'width': 100
+			"fieldname": "employee",
+			"fieldtype": "Link",
+			"label": _("Employee"),
+			"options": "Employee",
+			"width": 150,
 		},
-		{
-			'fieldname': 'model',
-			'fieldtype': 'Data',
-			'label': _('Model'),
-			'width': 80
-		},
-		{
-			'fieldname': 'location',
-			'fieldtype': 'Data',
-			'label': _('Location'),
-			'width': 100
-		},
-		{
-			'fieldname': 'log_name',
-			'fieldtype': 'Link',
-			'label': _('Vehicle Log'),
-			'options': 'Vehicle Log',
-			'width': 100
-		},
-		{
-			'fieldname': 'odometer',
-			'fieldtype': 'Int',
-			'label': _('Odometer Value'),
-			'width': 120
-		},
-		{
-			'fieldname': 'date',
-			'fieldtype': 'Date',
-			'label': _('Date'),
-			'width': 100
-		},
-		{
-			'fieldname': 'fuel_qty',
-			'fieldtype': 'Float',
-			'label': _('Fuel Qty'),
-			'width': 80
-		},
-		{
-			'fieldname': 'fuel_price',
-			'fieldtype': 'Float',
-			'label': _('Fuel Price'),
-			'width': 100
-		},
-		{
-			'fieldname': 'fuel_expense',
-			'fieldtype': 'Currency',
-			'label': _('Fuel Expense'),
-			'width': 150
-		},
-		{
-			'fieldname': 'service_expense',
-			'fieldtype': 'Currency',
-			'label': _('Service Expense'),
-			'width': 150
-		},
-		{
-			'fieldname': 'employee',
-			'fieldtype': 'Link',
-			'label': _('Employee'),
-			'options': 'Employee',
-			'width': 150
-		}
 	]
 
 
@@ -102,7 +63,8 @@
 	start_date, end_date = get_period_dates(filters)
 	conditions, values = get_conditions(filters)
 
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 		SELECT
 			vhcl.license_plate as vehicle, vhcl.make, vhcl.model,
 			vhcl.location, log.name as log_name, log.odometer,
@@ -116,58 +78,70 @@
 			and log.docstatus = 1
 			and date between %(start_date)s and %(end_date)s
 			{0}
-		ORDER BY date""".format(conditions), values, as_dict=1)
+		ORDER BY date""".format(
+			conditions
+		),
+		values,
+		as_dict=1,
+	)
 
 	for row in data:
-		row['service_expense'] = get_service_expense(row.log_name)
+		row["service_expense"] = get_service_expense(row.log_name)
 
 	return data
 
 
 def get_conditions(filters):
-	conditions = ''
+	conditions = ""
 
 	start_date, end_date = get_period_dates(filters)
-	values = {
-		'start_date': start_date,
-		'end_date': end_date
-	}
+	values = {"start_date": start_date, "end_date": end_date}
 
 	if filters.employee:
-		conditions += ' and log.employee = %(employee)s'
-		values['employee'] = filters.employee
+		conditions += " and log.employee = %(employee)s"
+		values["employee"] = filters.employee
 
 	if filters.vehicle:
-		conditions += ' and vhcl.license_plate = %(vehicle)s'
-		values['vehicle'] = filters.vehicle
+		conditions += " and vhcl.license_plate = %(vehicle)s"
+		values["vehicle"] = filters.vehicle
 
 	return conditions, values
 
 
 def get_period_dates(filters):
-	if filters.filter_based_on == 'Fiscal Year' and filters.fiscal_year:
-		fy = frappe.db.get_value('Fiscal Year', filters.fiscal_year,
-			['year_start_date', 'year_end_date'], as_dict=True)
+	if filters.filter_based_on == "Fiscal Year" and filters.fiscal_year:
+		fy = frappe.db.get_value(
+			"Fiscal Year", filters.fiscal_year, ["year_start_date", "year_end_date"], as_dict=True
+		)
 		return fy.year_start_date, fy.year_end_date
 	else:
 		return filters.from_date, filters.to_date
 
 
 def get_service_expense(logname):
-	expense_amount = frappe.db.sql("""
+	expense_amount = frappe.db.sql(
+		"""
 		SELECT sum(expense_amount)
 		FROM
 			`tabVehicle Log` log, `tabVehicle Service` service
 		WHERE
 			service.parent=log.name and log.name=%s
-	""", logname)
+	""",
+		logname,
+	)
 
 	return flt(expense_amount[0][0]) if expense_amount else 0.0
 
 
 def get_chart_data(data, filters):
-	period_list = get_period_list(filters.fiscal_year, filters.fiscal_year,
-		filters.from_date, filters.to_date, filters.filter_based_on, 'Monthly')
+	period_list = get_period_list(
+		filters.fiscal_year,
+		filters.fiscal_year,
+		filters.from_date,
+		filters.to_date,
+		filters.filter_based_on,
+		"Monthly",
+	)
 
 	fuel_data, service_data = [], []
 
@@ -184,29 +158,20 @@
 		service_data.append([period.key, total_service_exp])
 
 	labels = [period.label for period in period_list]
-	fuel_exp_data= [row[1] for row in fuel_data]
-	service_exp_data= [row[1] for row in service_data]
+	fuel_exp_data = [row[1] for row in fuel_data]
+	service_exp_data = [row[1] for row in service_data]
 
 	datasets = []
 	if fuel_exp_data:
-		datasets.append({
-			'name': _('Fuel Expenses'),
-			'values': fuel_exp_data
-		})
+		datasets.append({"name": _("Fuel Expenses"), "values": fuel_exp_data})
 
 	if service_exp_data:
-		datasets.append({
-			'name': _('Service Expenses'),
-			'values': service_exp_data
-		})
+		datasets.append({"name": _("Service Expenses"), "values": service_exp_data})
 
 	chart = {
-		'data': {
-			'labels': labels,
-			'datasets': datasets
-		},
-		'type': 'line',
-		'fieldtype': 'Currency'
+		"data": {"labels": labels, "datasets": datasets},
+		"type": "line",
+		"fieldtype": "Currency",
 	}
 
 	return chart
diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py
index c174047..db69147 100644
--- a/erpnext/hr/utils.py
+++ b/erpnext/hr/utils.py
@@ -23,20 +23,26 @@
 )
 
 
-class DuplicateDeclarationError(frappe.ValidationError): pass
+class DuplicateDeclarationError(frappe.ValidationError):
+	pass
+
 
 def set_employee_name(doc):
 	if doc.employee and not doc.employee_name:
 		doc.employee_name = frappe.db.get_value("Employee", doc.employee, "employee_name")
 
+
 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
-		})
+		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:
@@ -47,8 +53,10 @@
 		new_data = item.new if not cancel else item.current
 		if fieldtype == "Date" and new_data:
 			new_data = getdate(new_data)
-		elif fieldtype =="Datetime" and new_data:
+		elif fieldtype == "Datetime" and new_data:
 			new_data = get_datetime(new_data)
+		elif fieldtype in ["Currency", "Float"] and new_data:
+			new_data = flt(new_data)
 		setattr(employee, item.fieldname, new_data)
 		if item.fieldname in ["department", "designation", "branch"]:
 			internal_work_history[item.fieldname] = item.new
@@ -62,6 +70,7 @@
 
 	return employee
 
+
 def delete_employee_work_history(details, employee, date):
 	filters = {}
 	for d in details:
@@ -82,16 +91,6 @@
 
 
 @frappe.whitelist()
-def get_employee_fields_label():
-	fields = []
-	for df in frappe.get_meta("Employee").get("fields"):
-		if df.fieldname in ["salutation", "user_id", "employee_number", "employment_type",
-			"holiday_list", "branch", "department", "designation", "grade",
-			"notice_number_of_days", "reports_to", "leave_policy", "company_email"]:
-				fields.append({"value": df.fieldname, "label": df.label})
-	return fields
-
-@frappe.whitelist()
 def get_employee_field_property(employee, fieldname):
 	if employee and fieldname:
 		field = frappe.get_meta("Employee").get_field(fieldname)
@@ -101,17 +100,15 @@
 			value = formatdate(value)
 		elif field.fieldtype == "Datetime":
 			value = format_datetime(value)
-		return {
-			"value" : value,
-			"datatype" : field.fieldtype,
-			"label" : field.label,
-			"options" : options
-		}
+		return {"value": value, "datatype": field.fieldtype, "label": field.label, "options": options}
 	else:
 		return False
 
+
 def validate_dates(doc, from_date, to_date):
-	date_of_joining, relieving_date = frappe.db.get_value("Employee", doc.employee, ["date_of_joining", "relieving_date"])
+	date_of_joining, relieving_date = frappe.db.get_value(
+		"Employee", doc.employee, ["date_of_joining", "relieving_date"]
+	)
 	if getdate(from_date) > getdate(to_date):
 		frappe.throw(_("To date can not be less than from date"))
 	elif getdate(from_date) > getdate(nowdate()):
@@ -121,7 +118,8 @@
 	elif relieving_date and getdate(to_date) > getdate(relieving_date):
 		frappe.throw(_("To date can not greater than employee's relieving date"))
 
-def validate_overlap(doc, from_date, to_date, company = None):
+
+def validate_overlap(doc, from_date, to_date, company=None):
 	query = """
 		select name
 		from `tab{0}`
@@ -131,15 +129,19 @@
 
 	if not doc.name:
 		# hack! if name is null, it could cause problems with !=
-		doc.name = "New "+doc.doctype
+		doc.name = "New " + doc.doctype
 
-	overlap_doc = frappe.db.sql(query.format(doc.doctype),{
+	overlap_doc = frappe.db.sql(
+		query.format(doc.doctype),
+		{
 			"employee": doc.get("employee"),
 			"from_date": from_date,
 			"to_date": to_date,
 			"name": doc.name,
-			"company": company
-		}, as_dict = 1)
+			"company": company,
+		},
+		as_dict=1,
+	)
 
 	if overlap_doc:
 		if doc.get("employee"):
@@ -148,6 +150,7 @@
 			exists_for = company
 		throw_overlap_error(doc, exists_for, overlap_doc[0].name, from_date, to_date)
 
+
 def get_doc_condition(doctype):
 	if doctype == "Compensatory Leave Request":
 		return "and employee = %(employee)s and docstatus < 2 \
@@ -159,23 +162,36 @@
 			or to_date between %(from_date)s and %(to_date)s \
 			or (from_date < %(from_date)s and to_date > %(to_date)s))"
 
+
 def throw_overlap_error(doc, exists_for, overlap_doc, from_date, to_date):
-	msg = _("A {0} exists between {1} and {2} (").format(doc.doctype,
-		formatdate(from_date), formatdate(to_date)) \
-		+ """ <b><a href="/app/Form/{0}/{1}">{1}</a></b>""".format(doc.doctype, overlap_doc) \
+	msg = (
+		_("A {0} exists between {1} and {2} (").format(
+			doc.doctype, formatdate(from_date), formatdate(to_date)
+		)
+		+ """ <b><a href="/app/Form/{0}/{1}">{1}</a></b>""".format(doc.doctype, overlap_doc)
 		+ _(") for {0}").format(exists_for)
+	)
 	frappe.throw(msg)
 
+
 def validate_duplicate_exemption_for_payroll_period(doctype, docname, payroll_period, employee):
-	existing_record = frappe.db.exists(doctype, {
-		"payroll_period": payroll_period,
-		"employee": employee,
-		'docstatus': ['<', 2],
-		'name': ['!=', docname]
-	})
+	existing_record = frappe.db.exists(
+		doctype,
+		{
+			"payroll_period": payroll_period,
+			"employee": employee,
+			"docstatus": ["<", 2],
+			"name": ["!=", docname],
+		},
+	)
 	if existing_record:
-		frappe.throw(_("{0} already exists for employee {1} and period {2}")
-			.format(doctype, employee, payroll_period), DuplicateDeclarationError)
+		frappe.throw(
+			_("{0} already exists for employee {1} and period {2}").format(
+				doctype, employee, payroll_period
+			),
+			DuplicateDeclarationError,
+		)
+
 
 def validate_tax_declaration(declarations):
 	subcategories = []
@@ -184,61 +200,79 @@
 			frappe.throw(_("More than one selection for {0} not allowed").format(d.exemption_sub_category))
 		subcategories.append(d.exemption_sub_category)
 
+
 def get_total_exemption_amount(declarations):
 	exemptions = frappe._dict()
 	for d in declarations:
 		exemptions.setdefault(d.exemption_category, frappe._dict())
 		category_max_amount = exemptions.get(d.exemption_category).max_amount
 		if not category_max_amount:
-			category_max_amount = frappe.db.get_value("Employee Tax Exemption Category", d.exemption_category, "max_amount")
+			category_max_amount = frappe.db.get_value(
+				"Employee Tax Exemption Category", d.exemption_category, "max_amount"
+			)
 			exemptions.get(d.exemption_category).max_amount = category_max_amount
-		sub_category_exemption_amount = d.max_amount \
-			if (d.max_amount and flt(d.amount) > flt(d.max_amount)) else d.amount
+		sub_category_exemption_amount = (
+			d.max_amount if (d.max_amount and flt(d.amount) > flt(d.max_amount)) else d.amount
+		)
 
 		exemptions.get(d.exemption_category).setdefault("total_exemption_amount", 0.0)
 		exemptions.get(d.exemption_category).total_exemption_amount += flt(sub_category_exemption_amount)
 
-		if category_max_amount and exemptions.get(d.exemption_category).total_exemption_amount > category_max_amount:
+		if (
+			category_max_amount
+			and exemptions.get(d.exemption_category).total_exemption_amount > category_max_amount
+		):
 			exemptions.get(d.exemption_category).total_exemption_amount = category_max_amount
 
 	total_exemption_amount = sum([flt(d.total_exemption_amount) for d in exemptions.values()])
 	return total_exemption_amount
 
+
 @frappe.whitelist()
 def get_leave_period(from_date, to_date, company):
-	leave_period = frappe.db.sql("""
+	leave_period = frappe.db.sql(
+		"""
 		select name, from_date, to_date
 		from `tabLeave Period`
 		where company=%(company)s and is_active=1
 			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))
-	""", {
-		"from_date": from_date,
-		"to_date": to_date,
-		"company": company
-	}, as_dict=1)
+	""",
+		{"from_date": from_date, "to_date": to_date, "company": company},
+		as_dict=1,
+	)
 
 	if leave_period:
 		return leave_period
 
+
 def generate_leave_encashment():
-	''' Generates a draft leave encashment on allocation expiry '''
+	"""Generates a draft leave encashment on allocation expiry"""
 	from erpnext.hr.doctype.leave_encashment.leave_encashment import create_leave_encashment
 
-	if frappe.db.get_single_value('HR Settings', 'auto_leave_encashment'):
-		leave_type = frappe.get_all('Leave Type', filters={'allow_encashment': 1}, fields=['name'])
-		leave_type=[l['name'] for l in leave_type]
+	if frappe.db.get_single_value("HR Settings", "auto_leave_encashment"):
+		leave_type = frappe.get_all("Leave Type", filters={"allow_encashment": 1}, fields=["name"])
+		leave_type = [l["name"] for l in leave_type]
 
-		leave_allocation = frappe.get_all("Leave Allocation", filters={
-			'to_date': add_days(today(), -1),
-			'leave_type': ('in', leave_type)
-		}, fields=['employee', 'leave_period', 'leave_type', 'to_date', 'total_leaves_allocated', 'new_leaves_allocated'])
+		leave_allocation = frappe.get_all(
+			"Leave Allocation",
+			filters={"to_date": add_days(today(), -1), "leave_type": ("in", leave_type)},
+			fields=[
+				"employee",
+				"leave_period",
+				"leave_type",
+				"to_date",
+				"total_leaves_allocated",
+				"new_leaves_allocated",
+			],
+		)
 
 		create_leave_encashment(leave_allocation=leave_allocation)
 
-def allocate_earned_leaves(ignore_duplicates=False):
-	'''Allocate earned leaves to Employees'''
+
+def allocate_earned_leaves():
+	"""Allocate earned leaves to Employees"""
 	e_leave_types = get_earned_leaves()
 	today = getdate()
 
@@ -251,37 +285,58 @@
 			if not allocation.leave_policy_assignment and not allocation.leave_policy:
 				continue
 
-			leave_policy = allocation.leave_policy if allocation.leave_policy else frappe.db.get_value(
-					"Leave Policy Assignment", allocation.leave_policy_assignment, ["leave_policy"])
+			leave_policy = (
+				allocation.leave_policy
+				if allocation.leave_policy
+				else frappe.db.get_value(
+					"Leave Policy Assignment", allocation.leave_policy_assignment, ["leave_policy"]
+				)
+			)
 
-			annual_allocation = frappe.db.get_value("Leave Policy Detail", filters={
-				'parent': leave_policy,
-				'leave_type': e_leave_type.name
-			}, fieldname=['annual_allocation'])
+			annual_allocation = frappe.db.get_value(
+				"Leave Policy Detail",
+				filters={"parent": leave_policy, "leave_type": e_leave_type.name},
+				fieldname=["annual_allocation"],
+			)
 
-			from_date=allocation.from_date
+			from_date = allocation.from_date
 
 			if e_leave_type.based_on_date_of_joining:
-				from_date  = frappe.db.get_value("Employee", allocation.employee, "date_of_joining")
+				from_date = frappe.db.get_value("Employee", allocation.employee, "date_of_joining")
 
-			if check_effective_date(from_date, today, e_leave_type.earned_leave_frequency, e_leave_type.based_on_date_of_joining):
-				update_previous_leave_allocation(allocation, annual_allocation, e_leave_type, ignore_duplicates)
+			if check_effective_date(
+				from_date, today, e_leave_type.earned_leave_frequency, e_leave_type.based_on_date_of_joining
+			):
+				update_previous_leave_allocation(allocation, annual_allocation, e_leave_type)
 
-def update_previous_leave_allocation(allocation, annual_allocation, e_leave_type, ignore_duplicates=False):
-		earned_leaves = get_monthly_earned_leave(annual_allocation, e_leave_type.earned_leave_frequency, e_leave_type.rounding)
 
-		allocation = frappe.get_doc('Leave Allocation', allocation.name)
-		new_allocation = flt(allocation.total_leaves_allocated) + flt(earned_leaves)
+def update_previous_leave_allocation(allocation, annual_allocation, e_leave_type):
+	earned_leaves = get_monthly_earned_leave(
+		annual_allocation, e_leave_type.earned_leave_frequency, e_leave_type.rounding
+	)
 
-		if new_allocation > e_leave_type.max_leaves_allowed and e_leave_type.max_leaves_allowed > 0:
-			new_allocation = e_leave_type.max_leaves_allowed
+	allocation = frappe.get_doc("Leave Allocation", allocation.name)
+	new_allocation = flt(allocation.total_leaves_allocated) + flt(earned_leaves)
 
-		if new_allocation != allocation.total_leaves_allocated:
-			today_date = today()
+	if new_allocation > e_leave_type.max_leaves_allowed and e_leave_type.max_leaves_allowed > 0:
+		new_allocation = e_leave_type.max_leaves_allowed
 
-			if ignore_duplicates or not is_earned_leave_already_allocated(allocation, annual_allocation):
-				allocation.db_set("total_leaves_allocated", new_allocation, update_modified=False)
-				create_additional_leave_ledger_entry(allocation, earned_leaves, today_date)
+	if new_allocation != allocation.total_leaves_allocated:
+		today_date = today()
+
+		allocation.db_set("total_leaves_allocated", new_allocation, update_modified=False)
+		create_additional_leave_ledger_entry(allocation, earned_leaves, today_date)
+
+		if e_leave_type.based_on_date_of_joining:
+			text = _("allocated {0} leave(s) via scheduler on {1} based on the date of joining").format(
+				frappe.bold(earned_leaves), frappe.bold(formatdate(today_date))
+			)
+		else:
+			text = _("allocated {0} leave(s) via scheduler on {1}").format(
+				frappe.bold(earned_leaves), frappe.bold(formatdate(today_date))
+			)
+
+		allocation.add_comment(comment_type="Info", text=text)
 
 
 def get_monthly_earned_leave(annual_leaves, frequency, rounding):
@@ -309,8 +364,9 @@
 	date_of_joining = frappe.db.get_value("Employee", allocation.employee, "date_of_joining")
 
 	assignment = frappe.get_doc("Leave Policy Assignment", allocation.leave_policy_assignment)
-	leaves_for_passed_months = assignment.get_leaves_for_passed_months(allocation.leave_type,
-		annual_allocation, leave_type_details, date_of_joining)
+	leaves_for_passed_months = assignment.get_leaves_for_passed_months(
+		allocation.leave_type, annual_allocation, leave_type_details, date_of_joining
+	)
 
 	# exclude carry-forwarded leaves while checking for leave allocation for passed months
 	num_allocations = allocation.total_leaves_allocated
@@ -323,26 +379,39 @@
 
 
 def get_leave_allocations(date, leave_type):
-	return frappe.db.sql("""select name, employee, from_date, to_date, leave_policy_assignment, leave_policy
+	return frappe.db.sql(
+		"""select name, employee, from_date, to_date, leave_policy_assignment, leave_policy
 		from `tabLeave Allocation`
 		where
 			%s between from_date and to_date and docstatus=1
 			and leave_type=%s""",
-	(date, leave_type), as_dict=1)
+		(date, leave_type),
+		as_dict=1,
+	)
 
 
 def get_earned_leaves():
-	return frappe.get_all("Leave Type",
-		fields=["name", "max_leaves_allowed", "earned_leave_frequency", "rounding", "based_on_date_of_joining"],
-		filters={'is_earned_leave' : 1})
+	return frappe.get_all(
+		"Leave Type",
+		fields=[
+			"name",
+			"max_leaves_allowed",
+			"earned_leave_frequency",
+			"rounding",
+			"based_on_date_of_joining",
+		],
+		filters={"is_earned_leave": 1},
+	)
+
 
 def create_additional_leave_ledger_entry(allocation, leaves, date):
-	''' Create leave ledger entry for leave types '''
+	"""Create leave ledger entry for leave types"""
 	allocation.new_leaves_allocated = leaves
 	allocation.from_date = date
 	allocation.unused_leaves = 0
 	allocation.create_leave_ledger_entry()
 
+
 def check_effective_date(from_date, to_date, frequency, based_on_date_of_joining):
 	import calendar
 
@@ -351,10 +420,12 @@
 	from_date = get_datetime(from_date)
 	to_date = get_datetime(to_date)
 	rd = relativedelta.relativedelta(to_date, from_date)
-	#last day of month
-	last_day =  calendar.monthrange(to_date.year, to_date.month)[1]
+	# last day of month
+	last_day = calendar.monthrange(to_date.year, to_date.month)[1]
 
-	if (from_date.day == to_date.day and based_on_date_of_joining) or (not based_on_date_of_joining and to_date.day == last_day):
+	if (from_date.day == to_date.day and based_on_date_of_joining) or (
+		not based_on_date_of_joining and to_date.day == last_day
+	):
 		if frequency == "Monthly":
 			return True
 		elif frequency == "Quarterly" and rd.months % 3:
@@ -370,21 +441,24 @@
 	return False
 
 
-def get_salary_assignment(employee, date):
-	assignment = frappe.db.sql("""
-		select * from `tabSalary Structure Assignment`
-		where employee=%(employee)s
-		and docstatus = 1
-		and %(on_date)s >= from_date order by from_date desc limit 1""", {
-			'employee': employee,
-			'on_date': date,
-		}, as_dict=1)
-	return assignment[0] if assignment else None
+def get_salary_assignments(employee, payroll_period):
+	start_date, end_date = frappe.db.get_value(
+		"Payroll Period", payroll_period, ["start_date", "end_date"]
+	)
+	assignments = frappe.db.get_all(
+		"Salary Structure Assignment",
+		filters={"employee": employee, "docstatus": 1, "from_date": ["between", (start_date, end_date)]},
+		fields=["*"],
+		order_by="from_date",
+	)
+
+	return assignments
+
 
 def get_sal_slip_total_benefit_given(employee, payroll_period, component=False):
 	total_given_benefit_amount = 0
 	query = """
-	select sum(sd.amount) as 'total_amount'
+	select sum(sd.amount) as total_amount
 	from `tabSalary Slip` ss, `tabSalary Detail` sd
 	where ss.employee=%(employee)s
 	and ss.docstatus = 1 and ss.name = sd.parent
@@ -398,17 +472,22 @@
 	if component:
 		query += "and sd.salary_component = %(component)s"
 
-	sum_of_given_benefit = frappe.db.sql(query, {
-		'employee': employee,
-		'start_date': payroll_period.start_date,
-		'end_date': payroll_period.end_date,
-		'component': component
-	}, as_dict=True)
+	sum_of_given_benefit = frappe.db.sql(
+		query,
+		{
+			"employee": employee,
+			"start_date": payroll_period.start_date,
+			"end_date": payroll_period.end_date,
+			"component": component,
+		},
+		as_dict=True,
+	)
 
 	if sum_of_given_benefit and flt(sum_of_given_benefit[0].total_amount) > 0:
 		total_given_benefit_amount = sum_of_given_benefit[0].total_amount
 	return total_given_benefit_amount
 
+
 def get_holiday_dates_for_employee(employee, start_date, end_date):
 	"""return a list of holiday dates for the given employee between start_date and end_date"""
 	# return only date
@@ -417,50 +496,48 @@
 	return [cstr(h.holiday_date) for h in holidays]
 
 
-def get_holidays_for_employee(employee, start_date, end_date, raise_exception=True, only_non_weekly=False):
+def get_holidays_for_employee(
+	employee, start_date, end_date, raise_exception=True, only_non_weekly=False
+):
 	"""Get Holidays for a given employee
 
-		`employee` (str)
-		`start_date` (str or datetime)
-		`end_date` (str or datetime)
-		`raise_exception` (bool)
-		`only_non_weekly` (bool)
+	`employee` (str)
+	`start_date` (str or datetime)
+	`end_date` (str or datetime)
+	`raise_exception` (bool)
+	`only_non_weekly` (bool)
 
-		return: list of dicts with `holiday_date` and `description`
+	return: list of dicts with `holiday_date` and `description`
 	"""
 	holiday_list = get_holiday_list_for_employee(employee, raise_exception=raise_exception)
 
 	if not holiday_list:
 		return []
 
-	filters = {
-		'parent': holiday_list,
-		'holiday_date': ('between', [start_date, end_date])
-	}
+	filters = {"parent": holiday_list, "holiday_date": ("between", [start_date, end_date])}
 
 	if only_non_weekly:
-		filters['weekly_off'] = False
+		filters["weekly_off"] = False
 
-	holidays = frappe.get_all(
-		'Holiday',
-		fields=['description', 'holiday_date'],
-		filters=filters
-	)
+	holidays = frappe.get_all("Holiday", fields=["description", "holiday_date"], filters=filters)
 
 	return holidays
 
+
 @erpnext.allow_regional
 def calculate_annual_eligible_hra_exemption(doc):
 	# Don't delete this method, used for localization
 	# Indian HRA Exemption Calculation
 	return {}
 
+
 @erpnext.allow_regional
 def calculate_hra_exemption_for_period(doc):
 	# Don't delete this method, used for localization
 	# Indian HRA Exemption Calculation
 	return {}
 
+
 def get_previous_claimed_amount(employee, payroll_period, non_pro_rata=False, component=False):
 	total_claimed_amount = 0
 	query = """
@@ -475,24 +552,29 @@
 	if component:
 		query += "and earning_component = %(component)s"
 
-	sum_of_claimed_amount = frappe.db.sql(query, {
-		'employee': employee,
-		'start_date': payroll_period.start_date,
-		'end_date': payroll_period.end_date,
-		'component': component
-	}, as_dict=True)
+	sum_of_claimed_amount = frappe.db.sql(
+		query,
+		{
+			"employee": employee,
+			"start_date": payroll_period.start_date,
+			"end_date": payroll_period.end_date,
+			"component": component,
+		},
+		as_dict=True,
+	)
 	if sum_of_claimed_amount and flt(sum_of_claimed_amount[0].total_amount) > 0:
 		total_claimed_amount = sum_of_claimed_amount[0].total_amount
 	return total_claimed_amount
 
+
 def share_doc_with_approver(doc, user):
 	# if approver does not have permissions, share
 	if not frappe.has_permission(doc=doc, ptype="submit", user=user):
-		frappe.share.add(doc.doctype, doc.name, user, submit=1,
-			flags={"ignore_share_permission": True})
+		frappe.share.add(doc.doctype, doc.name, user, submit=1, flags={"ignore_share_permission": True})
 
-		frappe.msgprint(_("Shared with the user {0} with {1} access").format(
-			user, frappe.bold("submit"), alert=True))
+		frappe.msgprint(
+			_("Shared with the user {0} with {1} access").format(user, frappe.bold("submit"), alert=True)
+		)
 
 	# remove shared doc if approver changes
 	doc_before_save = doc.get_doc_before_save()
@@ -500,14 +582,19 @@
 		approvers = {
 			"Leave Application": "leave_approver",
 			"Expense Claim": "expense_approver",
-			"Shift Request": "approver"
+			"Shift Request": "approver",
 		}
 
 		approver = approvers.get(doc.doctype)
 		if doc_before_save.get(approver) != doc.get(approver):
 			frappe.share.remove(doc.doctype, doc.name, doc_before_save.get(approver))
 
+
 def validate_active_employee(employee):
 	if frappe.db.get_value("Employee", employee, "status") == "Inactive":
-		frappe.throw(_("Transactions cannot be created for an Inactive Employee {0}.").format(
-			get_link_to_form("Employee", employee)), InactiveEmployeeStatusError)
+		frappe.throw(
+			_("Transactions cannot be created for an Inactive Employee {0}.").format(
+				get_link_to_form("Employee", employee)
+			),
+			InactiveEmployeeStatusError,
+		)
diff --git a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py
index 6144d9d..aab3d8c 100644
--- a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py
+++ b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py
@@ -12,10 +12,19 @@
 
 @frappe.whitelist()
 @cache_source
-def get_data(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None,
-	to_date = None, timespan = None, time_interval = None, heatmap_year = None):
+def get_data(
+	chart_name=None,
+	chart=None,
+	no_cache=None,
+	filters=None,
+	from_date=None,
+	to_date=None,
+	timespan=None,
+	time_interval=None,
+	heatmap_year=None,
+):
 	if chart_name:
-		chart = frappe.get_doc('Dashboard Chart', chart_name)
+		chart = frappe.get_doc("Dashboard Chart", chart_name)
 	else:
 		chart = frappe._dict(frappe.parse_json(chart))
 
@@ -29,28 +38,44 @@
 	labels = []
 	values = []
 
-	if filters.get('company'):
+	if filters.get("company"):
 		conditions = "AND company = %(company)s"
 
 	loan_security_details = get_loan_security_details()
 
-	unpledges = frappe._dict(frappe.db.sql("""
+	unpledges = frappe._dict(
+		frappe.db.sql(
+			"""
 		SELECT u.loan_security, sum(u.qty) as qty
 		FROM `tabLoan Security Unpledge` up, `tabUnpledge` u
 		WHERE u.parent = up.name
 		AND up.status = 'Approved'
 		{conditions}
 		GROUP BY u.loan_security
-	""".format(conditions=conditions), filters, as_list=1))
+	""".format(
+				conditions=conditions
+			),
+			filters,
+			as_list=1,
+		)
+	)
 
-	pledges = frappe._dict(frappe.db.sql("""
+	pledges = frappe._dict(
+		frappe.db.sql(
+			"""
 		SELECT p.loan_security, sum(p.qty) as qty
 		FROM `tabLoan Security Pledge` lp, `tabPledge`p
 		WHERE p.parent = lp.name
 		AND lp.status = 'Pledged'
 		{conditions}
 		GROUP BY p.loan_security
-	""".format(conditions=conditions), filters, as_list=1))
+	""".format(
+				conditions=conditions
+			),
+			filters,
+			as_list=1,
+		)
+	)
 
 	for security, qty in pledges.items():
 		current_pledges.setdefault(security, qty)
@@ -60,19 +85,15 @@
 
 	count = 0
 	for security, qty in sorted_pledges.items():
-		values.append(qty * loan_security_details.get(security, {}).get('latest_price', 0))
+		values.append(qty * loan_security_details.get(security, {}).get("latest_price", 0))
 		labels.append(security)
-		count +=1
+		count += 1
 
 		## Just need top 10 securities
 		if count == 10:
 			break
 
 	return {
-		'labels': labels,
-		'datasets': [{
-			'name': 'Top 10 Securities',
-			'chartType': 'bar',
-			'values': values
-		}]
+		"labels": labels,
+		"datasets": [{"name": "Top 10 Securities", "chartType": "bar", "values": values}],
 	}
diff --git a/erpnext/loan_management/doctype/loan/loan.js b/erpnext/loan_management/doctype/loan/loan.js
index 940a1bb..38328e6 100644
--- a/erpnext/loan_management/doctype/loan/loan.js
+++ b/erpnext/loan_management/doctype/loan/loan.js
@@ -93,6 +93,12 @@
 					frm.trigger("make_loan_refund");
 				},__('Create'));
 			}
+
+			if (frm.doc.status == "Loan Closure Requested" && frm.doc.is_term_loan && !frm.doc.is_secured_loan) {
+				frm.add_custom_button(__('Close Loan'), function() {
+					frm.trigger("close_unsecured_term_loan");
+				},__('Status'));
+			}
 		}
 		frm.trigger("toggle_fields");
 	},
@@ -174,6 +180,18 @@
 		})
 	},
 
+	close_unsecured_term_loan: function(frm) {
+		frappe.call({
+			args: {
+				"loan": frm.doc.name
+			},
+			method: "erpnext.loan_management.doctype.loan.loan.close_unsecured_term_loan",
+			callback: function () {
+				frm.refresh();
+			}
+		})
+	},
+
 	request_loan_closure: function(frm) {
 		frappe.confirm(__("Do you really want to close this loan"),
 			function() {
diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json
index 196f36f..ef78a64 100644
--- a/erpnext/loan_management/doctype/loan/loan.json
+++ b/erpnext/loan_management/doctype/loan/loan.json
@@ -32,6 +32,8 @@
   "monthly_repayment_amount",
   "repayment_start_date",
   "is_term_loan",
+  "accounting_dimensions_section",
+  "cost_center",
   "account_info",
   "mode_of_payment",
   "disbursement_account",
@@ -366,12 +368,23 @@
    "options": "Account",
    "read_only": 1,
    "reqd": 1
+  },
+  {
+   "fieldname": "accounting_dimensions_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Dimensions"
+  },
+  {
+   "fieldname": "cost_center",
+   "fieldtype": "Link",
+   "label": "Cost Center",
+   "options": "Cost Center"
   }
  ],
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-01-25 16:29:16.325501",
+ "modified": "2022-03-10 11:50:31.957360",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan",
diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py
index b798e08..90ce004 100644
--- a/erpnext/loan_management/doctype/loan/loan.py
+++ b/erpnext/loan_management/doctype/loan/loan.py
@@ -20,37 +20,60 @@
 
 class Loan(AccountsController):
 	def validate(self):
-		if self.applicant_type == 'Employee' and self.repay_from_salary:
+		if self.applicant_type == "Employee" and self.repay_from_salary:
 			validate_employee_currency_with_company_currency(self.applicant, self.company)
 		self.set_loan_amount()
 		self.validate_loan_amount()
 		self.set_missing_fields()
+		self.validate_cost_center()
 		self.validate_accounts()
 		self.check_sanctioned_amount_limit()
 		self.validate_repay_from_salary()
 
 		if self.is_term_loan:
-			validate_repayment_method(self.repayment_method, self.loan_amount, self.monthly_repayment_amount,
-				self.repayment_periods, self.is_term_loan)
+			validate_repayment_method(
+				self.repayment_method,
+				self.loan_amount,
+				self.monthly_repayment_amount,
+				self.repayment_periods,
+				self.is_term_loan,
+			)
 			self.make_repayment_schedule()
 			self.set_repayment_period()
 
 		self.calculate_totals()
 
 	def validate_accounts(self):
-		for fieldname in ['payment_account', 'loan_account', 'interest_income_account', 'penalty_income_account']:
-			company = frappe.get_value("Account", self.get(fieldname), 'company')
+		for fieldname in [
+			"payment_account",
+			"loan_account",
+			"interest_income_account",
+			"penalty_income_account",
+		]:
+			company = frappe.get_value("Account", self.get(fieldname), "company")
 
 			if company != self.company:
-				frappe.throw(_("Account {0} does not belongs to company {1}").format(frappe.bold(self.get(fieldname)),
-					frappe.bold(self.company)))
+				frappe.throw(
+					_("Account {0} does not belongs to company {1}").format(
+						frappe.bold(self.get(fieldname)), frappe.bold(self.company)
+					)
+				)
+
+	def validate_cost_center(self):
+		if not self.cost_center and self.rate_of_interest != 0.0:
+			self.cost_center = frappe.db.get_value("Company", self.company, "cost_center")
+
+			if not self.cost_center:
+				frappe.throw(_("Cost center is mandatory for loans having rate of interest greater than 0"))
 
 	def on_submit(self):
 		self.link_loan_security_pledge()
+		# Interest accrual for backdated term loans
+		self.accrue_loan_interest()
 
 	def on_cancel(self):
 		self.unlink_loan_security_pledge()
-		self.ignore_linked_doctypes = ['GL Entry']
+		self.ignore_linked_doctypes = ["GL Entry", "Payment Ledger Entry"]
 
 	def set_missing_fields(self):
 		if not self.company:
@@ -63,15 +86,25 @@
 			self.rate_of_interest = frappe.db.get_value("Loan Type", self.loan_type, "rate_of_interest")
 
 		if self.repayment_method == "Repay Over Number of Periods":
-			self.monthly_repayment_amount = get_monthly_repayment_amount(self.loan_amount, self.rate_of_interest, self.repayment_periods)
+			self.monthly_repayment_amount = get_monthly_repayment_amount(
+				self.loan_amount, self.rate_of_interest, self.repayment_periods
+			)
 
 	def check_sanctioned_amount_limit(self):
-		sanctioned_amount_limit = get_sanctioned_amount_limit(self.applicant_type, self.applicant, self.company)
+		sanctioned_amount_limit = get_sanctioned_amount_limit(
+			self.applicant_type, self.applicant, self.company
+		)
 		if sanctioned_amount_limit:
 			total_loan_amount = get_total_loan_amount(self.applicant_type, self.applicant, self.company)
 
-		if sanctioned_amount_limit and flt(self.loan_amount) + flt(total_loan_amount) > flt(sanctioned_amount_limit):
-			frappe.throw(_("Sanctioned Amount limit crossed for {0} {1}").format(self.applicant_type, frappe.bold(self.applicant)))
+		if sanctioned_amount_limit and flt(self.loan_amount) + flt(total_loan_amount) > flt(
+			sanctioned_amount_limit
+		):
+			frappe.throw(
+				_("Sanctioned Amount limit crossed for {0} {1}").format(
+					self.applicant_type, frappe.bold(self.applicant)
+				)
+			)
 
 	def validate_repay_from_salary(self):
 		if not self.is_term_loan and self.repay_from_salary:
@@ -84,8 +117,8 @@
 		self.repayment_schedule = []
 		payment_date = self.repayment_start_date
 		balance_amount = self.loan_amount
-		while(balance_amount > 0):
-			interest_amount = flt(balance_amount * flt(self.rate_of_interest) / (12*100))
+		while balance_amount > 0:
+			interest_amount = flt(balance_amount * flt(self.rate_of_interest) / (12 * 100))
 			principal_amount = self.monthly_repayment_amount - interest_amount
 			balance_amount = flt(balance_amount + interest_amount - self.monthly_repayment_amount)
 			if balance_amount < 0:
@@ -93,13 +126,16 @@
 				balance_amount = 0.0
 
 			total_payment = principal_amount + interest_amount
-			self.append("repayment_schedule", {
-				"payment_date": payment_date,
-				"principal_amount": principal_amount,
-				"interest_amount": interest_amount,
-				"total_payment": total_payment,
-				"balance_loan_amount": balance_amount
-			})
+			self.append(
+				"repayment_schedule",
+				{
+					"payment_date": payment_date,
+					"principal_amount": principal_amount,
+					"interest_amount": interest_amount,
+					"total_payment": total_payment,
+					"balance_loan_amount": balance_amount,
+				},
+			)
 			next_payment_date = add_single_month(payment_date)
 			payment_date = next_payment_date
 
@@ -117,14 +153,13 @@
 		if self.is_term_loan:
 			for data in self.repayment_schedule:
 				self.total_payment += data.total_payment
-				self.total_interest_payable +=data.interest_amount
+				self.total_interest_payable += data.interest_amount
 		else:
 			self.total_payment = self.loan_amount
 
 	def set_loan_amount(self):
 		if self.loan_application and not self.loan_amount:
-			self.loan_amount = frappe.db.get_value('Loan Application', self.loan_application, 'loan_amount')
-
+			self.loan_amount = frappe.db.get_value("Loan Application", self.loan_application, "loan_amount")
 
 	def validate_loan_amount(self):
 		if self.maximum_loan_amount and self.loan_amount > self.maximum_loan_amount:
@@ -136,30 +171,46 @@
 
 	def link_loan_security_pledge(self):
 		if self.is_secured_loan and self.loan_application:
-			maximum_loan_value = frappe.db.get_value('Loan Security Pledge',
-				{
-					'loan_application': self.loan_application,
-					'status': 'Requested'
-				},
-				'sum(maximum_loan_value)'
+			maximum_loan_value = frappe.db.get_value(
+				"Loan Security Pledge",
+				{"loan_application": self.loan_application, "status": "Requested"},
+				"sum(maximum_loan_value)",
 			)
 
 			if maximum_loan_value:
-				frappe.db.sql("""
+				frappe.db.sql(
+					"""
 					UPDATE `tabLoan Security Pledge`
 					SET loan = %s, pledge_time = %s, status = 'Pledged'
 					WHERE status = 'Requested' and loan_application = %s
-				""", (self.name, now_datetime(), self.loan_application))
+				""",
+					(self.name, now_datetime(), self.loan_application),
+				)
 
-				self.db_set('maximum_loan_amount', maximum_loan_value)
+				self.db_set("maximum_loan_amount", maximum_loan_value)
+
+	def accrue_loan_interest(self):
+		from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import (
+			process_loan_interest_accrual_for_term_loans,
+		)
+
+		if getdate(self.repayment_start_date) < getdate() and self.is_term_loan:
+			process_loan_interest_accrual_for_term_loans(
+				posting_date=getdate(), loan_type=self.loan_type, loan=self.name
+			)
 
 	def unlink_loan_security_pledge(self):
-		pledges = frappe.get_all('Loan Security Pledge', fields=['name'], filters={'loan': self.name})
+		pledges = frappe.get_all("Loan Security Pledge", fields=["name"], filters={"loan": self.name})
 		pledge_list = [d.name for d in pledges]
 		if pledge_list:
-			frappe.db.sql("""UPDATE `tabLoan Security Pledge` SET
+			frappe.db.sql(
+				"""UPDATE `tabLoan Security Pledge` SET
 				loan = '', status = 'Unpledged'
-				where name in (%s) """ % (', '.join(['%s']*len(pledge_list))), tuple(pledge_list)) #nosec
+				where name in (%s) """
+				% (", ".join(["%s"] * len(pledge_list))),
+				tuple(pledge_list),
+			)  # nosec
+
 
 def update_total_amount_paid(doc):
 	total_amount_paid = 0
@@ -168,24 +219,51 @@
 			total_amount_paid += data.total_payment
 	frappe.db.set_value("Loan", doc.name, "total_amount_paid", total_amount_paid)
 
+
 def get_total_loan_amount(applicant_type, applicant, company):
 	pending_amount = 0
-	loan_details = frappe.db.get_all("Loan",
-		filters={"applicant_type": applicant_type, "company": company, "applicant": applicant, "docstatus": 1,
-			"status": ("!=", "Closed")},
-		fields=["status", "total_payment", "disbursed_amount", "total_interest_payable", "total_principal_paid",
-			"written_off_amount"])
+	loan_details = frappe.db.get_all(
+		"Loan",
+		filters={
+			"applicant_type": applicant_type,
+			"company": company,
+			"applicant": applicant,
+			"docstatus": 1,
+			"status": ("!=", "Closed"),
+		},
+		fields=[
+			"status",
+			"total_payment",
+			"disbursed_amount",
+			"total_interest_payable",
+			"total_principal_paid",
+			"written_off_amount",
+		],
+	)
 
-	interest_amount = flt(frappe.db.get_value("Loan Interest Accrual", {"applicant_type": applicant_type,
-		"company": company, "applicant": applicant, "docstatus": 1}, "sum(interest_amount - paid_interest_amount)"))
+	interest_amount = flt(
+		frappe.db.get_value(
+			"Loan Interest Accrual",
+			{"applicant_type": applicant_type, "company": company, "applicant": applicant, "docstatus": 1},
+			"sum(interest_amount - paid_interest_amount)",
+		)
+	)
 
 	for loan in loan_details:
 		if loan.status in ("Disbursed", "Loan Closure Requested"):
-			pending_amount += flt(loan.total_payment) - flt(loan.total_interest_payable) \
-				- flt(loan.total_principal_paid) - flt(loan.written_off_amount)
+			pending_amount += (
+				flt(loan.total_payment)
+				- flt(loan.total_interest_payable)
+				- flt(loan.total_principal_paid)
+				- flt(loan.written_off_amount)
+			)
 		elif loan.status == "Partially Disbursed":
-			pending_amount += flt(loan.disbursed_amount) - flt(loan.total_interest_payable) \
-				- flt(loan.total_principal_paid) - flt(loan.written_off_amount)
+			pending_amount += (
+				flt(loan.disbursed_amount)
+				- flt(loan.total_interest_payable)
+				- flt(loan.total_principal_paid)
+				- flt(loan.written_off_amount)
+			)
 		elif loan.status == "Sanctioned":
 			pending_amount += flt(loan.total_payment)
 
@@ -193,12 +271,18 @@
 
 	return pending_amount
 
-def get_sanctioned_amount_limit(applicant_type, applicant, company):
-	return frappe.db.get_value('Sanctioned Loan Amount',
-		{'applicant_type': applicant_type, 'company': company, 'applicant': applicant},
-		'sanctioned_amount_limit')
 
-def validate_repayment_method(repayment_method, loan_amount, monthly_repayment_amount, repayment_periods, is_term_loan):
+def get_sanctioned_amount_limit(applicant_type, applicant, company):
+	return frappe.db.get_value(
+		"Sanctioned Loan Amount",
+		{"applicant_type": applicant_type, "company": company, "applicant": applicant},
+		"sanctioned_amount_limit",
+	)
+
+
+def validate_repayment_method(
+	repayment_method, loan_amount, monthly_repayment_amount, repayment_periods, is_term_loan
+):
 
 	if is_term_loan and not repayment_method:
 		frappe.throw(_("Repayment Method is mandatory for term loans"))
@@ -212,27 +296,34 @@
 		if monthly_repayment_amount > loan_amount:
 			frappe.throw(_("Monthly Repayment Amount cannot be greater than Loan Amount"))
 
+
 def get_monthly_repayment_amount(loan_amount, rate_of_interest, repayment_periods):
 	if rate_of_interest:
-		monthly_interest_rate = flt(rate_of_interest) / (12 *100)
-		monthly_repayment_amount = math.ceil((loan_amount * monthly_interest_rate *
-			(1 + monthly_interest_rate)**repayment_periods) \
-			/ ((1 + monthly_interest_rate)**repayment_periods - 1))
+		monthly_interest_rate = flt(rate_of_interest) / (12 * 100)
+		monthly_repayment_amount = math.ceil(
+			(loan_amount * monthly_interest_rate * (1 + monthly_interest_rate) ** repayment_periods)
+			/ ((1 + monthly_interest_rate) ** repayment_periods - 1)
+		)
 	else:
 		monthly_repayment_amount = math.ceil(flt(loan_amount) / repayment_periods)
 	return monthly_repayment_amount
 
+
 @frappe.whitelist()
 def request_loan_closure(loan, posting_date=None):
 	if not posting_date:
 		posting_date = getdate()
 
 	amounts = calculate_amounts(loan, posting_date)
-	pending_amount = amounts['pending_principal_amount'] + amounts['unaccrued_interest'] + \
-		amounts['interest_amount'] + amounts['penalty_amount']
+	pending_amount = (
+		amounts["pending_principal_amount"]
+		+ amounts["unaccrued_interest"]
+		+ amounts["interest_amount"]
+		+ amounts["penalty_amount"]
+	)
 
-	loan_type = frappe.get_value('Loan', loan, 'loan_type')
-	write_off_limit = frappe.get_value('Loan Type', loan_type, 'write_off_amount')
+	loan_type = frappe.get_value("Loan", loan, "loan_type")
+	write_off_limit = frappe.get_value("Loan Type", loan_type, "write_off_amount")
 
 	if pending_amount and abs(pending_amount) < write_off_limit:
 		# Auto create loan write off and update status as loan closure requested
@@ -241,7 +332,8 @@
 	elif pending_amount > 0:
 		frappe.throw(_("Cannot close loan as there is an outstanding of {0}").format(pending_amount))
 
-	frappe.db.set_value('Loan', loan, 'status', 'Loan Closure Requested')
+	frappe.db.set_value("Loan", loan, "status", "Loan Closure Requested")
+
 
 @frappe.whitelist()
 def get_loan_application(loan_application):
@@ -249,10 +341,28 @@
 	if loan:
 		return loan.as_dict()
 
+
+@frappe.whitelist()
+def close_unsecured_term_loan(loan):
+	loan_details = frappe.db.get_value(
+		"Loan", {"name": loan}, ["status", "is_term_loan", "is_secured_loan"], as_dict=1
+	)
+
+	if (
+		loan_details.status == "Loan Closure Requested"
+		and loan_details.is_term_loan
+		and not loan_details.is_secured_loan
+	):
+		frappe.db.set_value("Loan", loan, "status", "Closed")
+	else:
+		frappe.throw(_("Cannot close this loan until full repayment"))
+
+
 def close_loan(loan, total_amount_paid):
 	frappe.db.set_value("Loan", loan, "total_amount_paid", total_amount_paid)
 	frappe.db.set_value("Loan", loan, "status", "Closed")
 
+
 @frappe.whitelist()
 def make_loan_disbursement(loan, company, applicant_type, applicant, pending_amount=0, as_dict=0):
 	disbursement_entry = frappe.new_doc("Loan Disbursement")
@@ -269,6 +379,7 @@
 	else:
 		return disbursement_entry
 
+
 @frappe.whitelist()
 def make_repayment_entry(loan, applicant_type, applicant, loan_type, company, as_dict=0):
 	repayment_entry = frappe.new_doc("Loan Repayment")
@@ -284,27 +395,28 @@
 	else:
 		return repayment_entry
 
+
 @frappe.whitelist()
 def make_loan_write_off(loan, company=None, posting_date=None, amount=0, as_dict=0):
 	if not company:
-		company = frappe.get_value('Loan', loan, 'company')
+		company = frappe.get_value("Loan", loan, "company")
 
 	if not posting_date:
 		posting_date = getdate()
 
 	amounts = calculate_amounts(loan, posting_date)
-	pending_amount = amounts['pending_principal_amount']
+	pending_amount = amounts["pending_principal_amount"]
 
 	if amount and (amount > pending_amount):
-		frappe.throw(_('Write Off amount cannot be greater than pending loan amount'))
+		frappe.throw(_("Write Off amount cannot be greater than pending loan amount"))
 
 	if not amount:
 		amount = pending_amount
 
 	# get default write off account from company master
-	write_off_account = frappe.get_value('Company', company, 'write_off_account')
+	write_off_account = frappe.get_value("Company", company, "write_off_account")
 
-	write_off = frappe.new_doc('Loan Write Off')
+	write_off = frappe.new_doc("Loan Write Off")
 	write_off.loan = loan
 	write_off.posting_date = posting_date
 	write_off.write_off_account = write_off_account
@@ -316,26 +428,35 @@
 	else:
 		return write_off
 
+
 @frappe.whitelist()
-def unpledge_security(loan=None, loan_security_pledge=None, security_map=None, as_dict=0, save=0, submit=0, approve=0):
+def unpledge_security(
+	loan=None, loan_security_pledge=None, security_map=None, as_dict=0, save=0, submit=0, approve=0
+):
 	# if no security_map is passed it will be considered as full unpledge
 	if security_map and isinstance(security_map, str):
 		security_map = json.loads(security_map)
 
 	if loan:
 		pledge_qty_map = security_map or get_pledged_security_qty(loan)
-		loan_doc = frappe.get_doc('Loan', loan)
-		unpledge_request = create_loan_security_unpledge(pledge_qty_map, loan_doc.name, loan_doc.company,
-			loan_doc.applicant_type, loan_doc.applicant)
+		loan_doc = frappe.get_doc("Loan", loan)
+		unpledge_request = create_loan_security_unpledge(
+			pledge_qty_map, loan_doc.name, loan_doc.company, loan_doc.applicant_type, loan_doc.applicant
+		)
 	# will unpledge qty based on loan security pledge
 	elif loan_security_pledge:
 		security_map = {}
-		pledge_doc = frappe.get_doc('Loan Security Pledge', loan_security_pledge)
+		pledge_doc = frappe.get_doc("Loan Security Pledge", loan_security_pledge)
 		for security in pledge_doc.securities:
 			security_map.setdefault(security.loan_security, security.qty)
 
-		unpledge_request = create_loan_security_unpledge(security_map, pledge_doc.loan,
-			pledge_doc.company, pledge_doc.applicant_type, pledge_doc.applicant)
+		unpledge_request = create_loan_security_unpledge(
+			security_map,
+			pledge_doc.loan,
+			pledge_doc.company,
+			pledge_doc.applicant_type,
+			pledge_doc.applicant,
+		)
 
 	if save:
 		unpledge_request.save()
@@ -345,16 +466,17 @@
 
 	if approve:
 		if unpledge_request.docstatus == 1:
-			unpledge_request.status = 'Approved'
+			unpledge_request.status = "Approved"
 			unpledge_request.save()
 		else:
-			frappe.throw(_('Only submittted unpledge requests can be approved'))
+			frappe.throw(_("Only submittted unpledge requests can be approved"))
 
 	if as_dict:
 		return unpledge_request
 	else:
 		return unpledge_request
 
+
 def create_loan_security_unpledge(unpledge_map, loan, company, applicant_type, applicant):
 	unpledge_request = frappe.new_doc("Loan Security Unpledge")
 	unpledge_request.applicant_type = applicant_type
@@ -364,17 +486,16 @@
 
 	for security, qty in unpledge_map.items():
 		if qty:
-			unpledge_request.append('securities', {
-				"loan_security": security,
-				"qty": qty
-			})
+			unpledge_request.append("securities", {"loan_security": security, "qty": qty})
 
 	return unpledge_request
 
+
 def validate_employee_currency_with_company_currency(applicant, company):
 	from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import (
 		get_employee_currency,
 	)
+
 	if not applicant:
 		frappe.throw(_("Please select Applicant"))
 	if not company:
@@ -382,18 +503,20 @@
 	employee_currency = get_employee_currency(applicant)
 	company_currency = erpnext.get_company_currency(company)
 	if employee_currency != company_currency:
-		frappe.throw(_("Loan cannot be repayed from salary for Employee {0} because salary is processed in currency {1}")
-			.format(applicant, employee_currency))
+		frappe.throw(
+			_(
+				"Loan cannot be repayed from salary for Employee {0} because salary is processed in currency {1}"
+			).format(applicant, employee_currency)
+		)
+
 
 @frappe.whitelist()
 def get_shortfall_applicants():
-	loans = frappe.get_all('Loan Security Shortfall', {'status': 'Pending'}, pluck='loan')
-	applicants = set(frappe.get_all('Loan', {'name': ('in', loans)}, pluck='name'))
+	loans = frappe.get_all("Loan Security Shortfall", {"status": "Pending"}, pluck="loan")
+	applicants = set(frappe.get_all("Loan", {"name": ("in", loans)}, pluck="name"))
 
-	return {
-		"value": len(applicants),
-		"fieldtype": "Int"
-	}
+	return {"value": len(applicants), "fieldtype": "Int"}
+
 
 def add_single_month(date):
 	if getdate(date) == get_last_day(date):
@@ -401,29 +524,46 @@
 	else:
 		return add_months(date, 1)
 
+
 @frappe.whitelist()
 def make_refund_jv(loan, amount=0, reference_number=None, reference_date=None, submit=0):
-	loan_details = frappe.db.get_value('Loan', loan, ['applicant_type', 'applicant',
-		'loan_account', 'payment_account', 'posting_date', 'company', 'name',
-		'total_payment', 'total_principal_paid'], as_dict=1)
+	loan_details = frappe.db.get_value(
+		"Loan",
+		loan,
+		[
+			"applicant_type",
+			"applicant",
+			"loan_account",
+			"payment_account",
+			"posting_date",
+			"company",
+			"name",
+			"total_payment",
+			"total_principal_paid",
+		],
+		as_dict=1,
+	)
 
-	loan_details.doctype = 'Loan'
+	loan_details.doctype = "Loan"
 	loan_details[loan_details.applicant_type.lower()] = loan_details.applicant
 
 	if not amount:
 		amount = flt(loan_details.total_principal_paid - loan_details.total_payment)
 
 		if amount < 0:
-			frappe.throw(_('No excess amount pending for refund'))
+			frappe.throw(_("No excess amount pending for refund"))
 
-	refund_jv = get_payment_entry(loan_details, {
-		"party_type": loan_details.applicant_type,
-		"party_account": loan_details.loan_account,
-		"amount_field_party": 'debit_in_account_currency',
-		"amount_field_bank": 'credit_in_account_currency',
-		"amount": amount,
-		"bank_account": loan_details.payment_account
-	})
+	refund_jv = get_payment_entry(
+		loan_details,
+		{
+			"party_type": loan_details.applicant_type,
+			"party_account": loan_details.loan_account,
+			"amount_field_party": "debit_in_account_currency",
+			"amount_field_bank": "credit_in_account_currency",
+			"amount": amount,
+			"bank_account": loan_details.payment_account,
+		},
+	)
 
 	if reference_number:
 		refund_jv.cheque_no = reference_number
@@ -434,4 +574,4 @@
 	if submit:
 		refund_jv.submit()
 
-	return refund_jv
\ No newline at end of file
+	return refund_jv
diff --git a/erpnext/loan_management/doctype/loan/loan_dashboard.py b/erpnext/loan_management/doctype/loan/loan_dashboard.py
index c8a9e64..971d545 100644
--- a/erpnext/loan_management/doctype/loan/loan_dashboard.py
+++ b/erpnext/loan_management/doctype/loan/loan_dashboard.py
@@ -1,16 +1,19 @@
 def get_data():
 	return {
-		'fieldname': 'loan',
-		'non_standard_fieldnames': {
-			'Loan Disbursement': 'against_loan',
-			'Loan Repayment': 'against_loan',
+		"fieldname": "loan",
+		"non_standard_fieldnames": {
+			"Loan Disbursement": "against_loan",
+			"Loan Repayment": "against_loan",
 		},
-		'transactions': [
+		"transactions": [
+			{"items": ["Loan Security Pledge", "Loan Security Shortfall", "Loan Disbursement"]},
 			{
-				'items': ['Loan Security Pledge', 'Loan Security Shortfall', 'Loan Disbursement']
+				"items": [
+					"Loan Repayment",
+					"Loan Interest Accrual",
+					"Loan Write Off",
+					"Loan Security Unpledge",
+				]
 			},
-			{
-				'items': ['Loan Repayment', 'Loan Interest Accrual', 'Loan Write Off', 'Loan Security Unpledge']
-			}
-		]
+		],
 	}
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index 5ebb2e1..e2b0870 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -39,31 +39,69 @@
 class TestLoan(unittest.TestCase):
 	def setUp(self):
 		create_loan_accounts()
-		create_loan_type("Personal Loan", 500000, 8.4,
+		create_loan_type(
+			"Personal Loan",
+			500000,
+			8.4,
 			is_term_loan=1,
-			mode_of_payment='Cash',
-			disbursement_account='Disbursement Account - _TC',
-			payment_account='Payment Account - _TC',
-			loan_account='Loan Account - _TC',
-			interest_income_account='Interest Income Account - _TC',
-			penalty_income_account='Penalty Income Account - _TC')
+			mode_of_payment="Cash",
+			disbursement_account="Disbursement Account - _TC",
+			payment_account="Payment Account - _TC",
+			loan_account="Loan Account - _TC",
+			interest_income_account="Interest Income Account - _TC",
+			penalty_income_account="Penalty Income Account - _TC",
+		)
 
-		create_loan_type("Stock Loan", 2000000, 13.5, 25, 1, 5, 'Cash', 'Disbursement Account - _TC',
-			'Payment Account - _TC', 'Loan Account - _TC', 'Interest Income Account - _TC', 'Penalty Income Account - _TC')
+		create_loan_type(
+			"Stock Loan",
+			2000000,
+			13.5,
+			25,
+			1,
+			5,
+			"Cash",
+			"Disbursement Account - _TC",
+			"Payment Account - _TC",
+			"Loan Account - _TC",
+			"Interest Income Account - _TC",
+			"Penalty Income Account - _TC",
+		)
 
-		create_loan_type("Demand Loan", 2000000, 13.5, 25, 0, 5, 'Cash', 'Disbursement Account - _TC',
-			'Payment Account - _TC', 'Loan Account - _TC', 'Interest Income Account - _TC', 'Penalty Income Account - _TC')
+		create_loan_type(
+			"Demand Loan",
+			2000000,
+			13.5,
+			25,
+			0,
+			5,
+			"Cash",
+			"Disbursement Account - _TC",
+			"Payment Account - _TC",
+			"Loan Account - _TC",
+			"Interest Income Account - _TC",
+			"Penalty Income Account - _TC",
+		)
 
 		create_loan_security_type()
 		create_loan_security()
 
-		create_loan_security_price("Test Security 1", 500, "Nos", get_datetime() , get_datetime(add_to_date(nowdate(), hours=24)))
-		create_loan_security_price("Test Security 2", 250, "Nos", get_datetime() , get_datetime(add_to_date(nowdate(), hours=24)))
+		create_loan_security_price(
+			"Test Security 1", 500, "Nos", get_datetime(), get_datetime(add_to_date(nowdate(), hours=24))
+		)
+		create_loan_security_price(
+			"Test Security 2", 250, "Nos", get_datetime(), get_datetime(add_to_date(nowdate(), hours=24))
+		)
 
 		self.applicant1 = make_employee("robert_loan@loan.com")
-		make_salary_structure("Test Salary Structure Loan", "Monthly", employee=self.applicant1, currency='INR', company="_Test Company")
+		make_salary_structure(
+			"Test Salary Structure Loan",
+			"Monthly",
+			employee=self.applicant1,
+			currency="INR",
+			company="_Test Company",
+		)
 		if not frappe.db.exists("Customer", "_Test Loan Customer"):
-			frappe.get_doc(get_customer_dict('_Test Loan Customer')).insert(ignore_permissions=True)
+			frappe.get_doc(get_customer_dict("_Test Loan Customer")).insert(ignore_permissions=True)
 
 		if not frappe.db.exists("Customer", "_Test Loan Customer 1"):
 			frappe.get_doc(get_customer_dict("_Test Loan Customer 1")).insert(ignore_permissions=True)
@@ -74,7 +112,7 @@
 		create_loan(self.applicant1, "Personal Loan", 280000, "Repay Over Number of Periods", 20)
 
 	def test_loan(self):
-		loan = frappe.get_doc("Loan", {"applicant":self.applicant1})
+		loan = frappe.get_doc("Loan", {"applicant": self.applicant1})
 		self.assertEqual(loan.monthly_repayment_amount, 15052)
 		self.assertEqual(flt(loan.total_interest_payable, 0), 21034)
 		self.assertEqual(flt(loan.total_payment, 0), 301034)
@@ -83,7 +121,11 @@
 
 		self.assertEqual(len(schedule), 20)
 
-		for idx, principal_amount, interest_amount, balance_loan_amount in [[3, 13369, 1683, 227080], [19, 14941, 105, 0], [17, 14740, 312, 29785]]:
+		for idx, principal_amount, interest_amount, balance_loan_amount in [
+			[3, 13369, 1683, 227080],
+			[19, 14941, 105, 0],
+			[17, 14740, 312, 29785],
+		]:
 			self.assertEqual(flt(schedule[idx].principal_amount, 0), principal_amount)
 			self.assertEqual(flt(schedule[idx].interest_amount, 0), interest_amount)
 			self.assertEqual(flt(schedule[idx].balance_loan_amount, 0), balance_loan_amount)
@@ -98,30 +140,35 @@
 
 	def test_loan_with_security(self):
 
-		pledge = [{
-			"loan_security": "Test Security 1",
-			"qty": 4000.00,
-		}]
+		pledge = [
+			{
+				"loan_security": "Test Security 1",
+				"qty": 4000.00,
+			}
+		]
 
-		loan_application = create_loan_application('_Test Company', self.applicant2,
-			'Stock Loan', pledge, "Repay Over Number of Periods", 12)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant2, "Stock Loan", pledge, "Repay Over Number of Periods", 12
+		)
 		create_pledge(loan_application)
 
-		loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods",
-			12, loan_application)
+		loan = create_loan_with_security(
+			self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application
+		)
 		self.assertEqual(loan.loan_amount, 1000000)
 
 	def test_loan_disbursement(self):
-		pledge = [{
-			"loan_security": "Test Security 1",
-			"qty": 4000.00
-		}]
+		pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}]
 
-		loan_application = create_loan_application('_Test Company', self.applicant2, 'Stock Loan', pledge, "Repay Over Number of Periods", 12)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant2, "Stock Loan", pledge, "Repay Over Number of Periods", 12
+		)
 
 		create_pledge(loan_application)
 
-		loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application)
+		loan = create_loan_with_security(
+			self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application
+		)
 		self.assertEqual(loan.loan_amount, 1000000)
 
 		loan.submit()
@@ -130,14 +177,16 @@
 		loan_disbursement_entry2 = make_loan_disbursement_entry(loan.name, 500000)
 
 		loan = frappe.get_doc("Loan", loan.name)
-		gl_entries1 = frappe.db.get_all("GL Entry",
+		gl_entries1 = frappe.db.get_all(
+			"GL Entry",
 			fields=["name"],
-			filters = {'voucher_type': 'Loan Disbursement', 'voucher_no': loan_disbursement_entry1.name}
+			filters={"voucher_type": "Loan Disbursement", "voucher_no": loan_disbursement_entry1.name},
 		)
 
-		gl_entries2 = frappe.db.get_all("GL Entry",
+		gl_entries2 = frappe.db.get_all(
+			"GL Entry",
 			fields=["name"],
-			filters = {'voucher_type': 'Loan Disbursement', 'voucher_no': loan_disbursement_entry2.name}
+			filters={"voucher_type": "Loan Disbursement", "voucher_no": loan_disbursement_entry2.name},
 		)
 
 		self.assertEqual(loan.status, "Disbursed")
@@ -151,73 +200,93 @@
 		frappe.db.sql("DELETE FROM `tabLoan Application` where applicant = '_Test Loan Customer 1'")
 		frappe.db.sql("DELETE FROM `tabLoan Security Pledge` where applicant = '_Test Loan Customer 1'")
 
-		if not frappe.db.get_value("Sanctioned Loan Amount", filters={"applicant_type": "Customer",
-			"applicant": "_Test Loan Customer 1", "company": "_Test Company"}):
-			frappe.get_doc({
-				"doctype": "Sanctioned Loan Amount",
+		if not frappe.db.get_value(
+			"Sanctioned Loan Amount",
+			filters={
 				"applicant_type": "Customer",
 				"applicant": "_Test Loan Customer 1",
-				"sanctioned_amount_limit": 1500000,
-				"company": "_Test Company"
-			}).insert(ignore_permissions=True)
+				"company": "_Test Company",
+			},
+		):
+			frappe.get_doc(
+				{
+					"doctype": "Sanctioned Loan Amount",
+					"applicant_type": "Customer",
+					"applicant": "_Test Loan Customer 1",
+					"sanctioned_amount_limit": 1500000,
+					"company": "_Test Company",
+				}
+			).insert(ignore_permissions=True)
 
 		# Make First Loan
-		pledge = [{
-			"loan_security": "Test Security 1",
-			"qty": 4000.00
-		}]
+		pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}]
 
-		loan_application = create_loan_application('_Test Company', self.applicant3, 'Demand Loan', pledge)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant3, "Demand Loan", pledge
+		)
 		create_pledge(loan_application)
-		loan = create_demand_loan(self.applicant3, "Demand Loan", loan_application, posting_date='2019-10-01')
+		loan = create_demand_loan(
+			self.applicant3, "Demand Loan", loan_application, posting_date="2019-10-01"
+		)
 		loan.submit()
 
 		# Make second loan greater than the sanctioned amount
-		loan_application = create_loan_application('_Test Company', self.applicant3, 'Demand Loan', pledge,
-			do_not_save=True)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant3, "Demand Loan", pledge, do_not_save=True
+		)
 		self.assertRaises(frappe.ValidationError, loan_application.save)
 
 	def test_regular_loan_repayment(self):
-		pledge = [{
-			"loan_security": "Test Security 1",
-			"qty": 4000.00
-		}]
+		pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}]
 
-		loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant2, "Demand Loan", pledge
+		)
 		create_pledge(loan_application)
 
-		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+		loan = create_demand_loan(
+			self.applicant2, "Demand Loan", loan_application, posting_date="2019-10-01"
+		)
 		loan.submit()
 
 		self.assertEqual(loan.loan_amount, 1000000)
 
-		first_date = '2019-10-01'
-		last_date = '2019-10-30'
+		first_date = "2019-10-01"
+		last_date = "2019-10-30"
 
 		no_of_days = date_diff(last_date, first_date) + 1
 
-		accrued_interest_amount = flt((loan.loan_amount * loan.rate_of_interest * no_of_days)
-			/ (days_in_year(get_datetime(first_date).year) * 100), 2)
+		accrued_interest_amount = flt(
+			(loan.loan_amount * loan.rate_of_interest * no_of_days)
+			/ (days_in_year(get_datetime(first_date).year) * 100),
+			2,
+		)
 
 		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
 
-		process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+		process_loan_interest_accrual_for_demand_loans(posting_date=last_date)
 
-		repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 10), 111119)
+		repayment_entry = create_repayment_entry(
+			loan.name, self.applicant2, add_days(last_date, 10), 111119
+		)
 		repayment_entry.save()
 		repayment_entry.submit()
 
 		penalty_amount = (accrued_interest_amount * 5 * 25) / 100
-		self.assertEqual(flt(repayment_entry.penalty_amount,0), flt(penalty_amount, 0))
+		self.assertEqual(flt(repayment_entry.penalty_amount, 0), flt(penalty_amount, 0))
 
-		amounts = frappe.db.get_all('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount'])
+		amounts = frappe.db.get_all(
+			"Loan Interest Accrual", {"loan": loan.name}, ["paid_interest_amount"]
+		)
 
 		loan.load_from_db()
 
-		total_interest_paid = amounts[0]['paid_interest_amount'] + amounts[1]['paid_interest_amount']
-		self.assertEqual(amounts[1]['paid_interest_amount'], repayment_entry.interest_payable)
-		self.assertEqual(flt(loan.total_principal_paid, 0), flt(repayment_entry.amount_paid -
-			 penalty_amount - total_interest_paid, 0))
+		total_interest_paid = amounts[0]["paid_interest_amount"] + amounts[1]["paid_interest_amount"]
+		self.assertEqual(amounts[1]["paid_interest_amount"], repayment_entry.interest_payable)
+		self.assertEqual(
+			flt(loan.total_principal_paid, 0),
+			flt(repayment_entry.amount_paid - penalty_amount - total_interest_paid, 0),
+		)
 
 		# Check Repayment Entry cancel
 		repayment_entry.load_from_db()
@@ -228,21 +297,22 @@
 		self.assertEqual(loan.total_principal_paid, 0)
 
 	def test_loan_closure(self):
-		pledge = [{
-			"loan_security": "Test Security 1",
-			"qty": 4000.00
-		}]
+		pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}]
 
-		loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant2, "Demand Loan", pledge
+		)
 		create_pledge(loan_application)
 
-		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+		loan = create_demand_loan(
+			self.applicant2, "Demand Loan", loan_application, posting_date="2019-10-01"
+		)
 		loan.submit()
 
 		self.assertEqual(loan.loan_amount, 1000000)
 
-		first_date = '2019-10-01'
-		last_date = '2019-10-30'
+		first_date = "2019-10-01"
+		last_date = "2019-10-30"
 
 		no_of_days = date_diff(last_date, first_date) + 1
 
@@ -251,20 +321,27 @@
 		# 5 days as well though in grace period
 		no_of_days += 5
 
-		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
-			/ (days_in_year(get_datetime(first_date).year) * 100)
+		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) / (
+			days_in_year(get_datetime(first_date).year) * 100
+		)
 
 		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
-		process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+		process_loan_interest_accrual_for_demand_loans(posting_date=last_date)
 
-		repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5),
-			flt(loan.loan_amount + accrued_interest_amount))
+		repayment_entry = create_repayment_entry(
+			loan.name,
+			self.applicant2,
+			add_days(last_date, 5),
+			flt(loan.loan_amount + accrued_interest_amount),
+		)
 
 		repayment_entry.submit()
 
-		amount = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['sum(paid_interest_amount)'])
+		amount = frappe.db.get_value(
+			"Loan Interest Accrual", {"loan": loan.name}, ["sum(paid_interest_amount)"]
+		)
 
-		self.assertEqual(flt(amount, 0),flt(accrued_interest_amount, 0))
+		self.assertEqual(flt(amount, 0), flt(accrued_interest_amount, 0))
 		self.assertEqual(flt(repayment_entry.penalty_amount, 5), 0)
 
 		request_loan_closure(loan.name)
@@ -272,78 +349,101 @@
 		self.assertEqual(loan.status, "Loan Closure Requested")
 
 	def test_loan_repayment_for_term_loan(self):
-		pledges = [{
-			"loan_security": "Test Security 2",
-			"qty": 4000.00
-		},
-		{
-			"loan_security": "Test Security 1",
-			"qty": 2000.00
-		}]
+		pledges = [
+			{"loan_security": "Test Security 2", "qty": 4000.00},
+			{"loan_security": "Test Security 1", "qty": 2000.00},
+		]
 
-		loan_application = create_loan_application('_Test Company', self.applicant2, 'Stock Loan', pledges,
-			"Repay Over Number of Periods", 12)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant2, "Stock Loan", pledges, "Repay Over Number of Periods", 12
+		)
 		create_pledge(loan_application)
 
-		loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application,
-			posting_date=add_months(nowdate(), -1))
+		loan = create_loan_with_security(
+			self.applicant2,
+			"Stock Loan",
+			"Repay Over Number of Periods",
+			12,
+			loan_application,
+			posting_date=add_months(nowdate(), -1),
+		)
 
 		loan.submit()
 
-		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1))
+		make_loan_disbursement_entry(
+			loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1)
+		)
 
 		process_loan_interest_accrual_for_term_loans(posting_date=nowdate())
 
-		repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(nowdate(), 5), 89768.75)
+		repayment_entry = create_repayment_entry(
+			loan.name, self.applicant2, add_days(nowdate(), 5), 89768.75
+		)
 
 		repayment_entry.submit()
 
-		amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount',
-			'paid_principal_amount'])
+		amounts = frappe.db.get_value(
+			"Loan Interest Accrual", {"loan": loan.name}, ["paid_interest_amount", "paid_principal_amount"]
+		)
 
 		self.assertEqual(amounts[0], 11250.00)
 		self.assertEqual(amounts[1], 78303.00)
 
 	def test_repayment_schedule_update(self):
-		loan = create_loan(self.applicant2, "Personal Loan", 200000, "Repay Over Number of Periods", 4,
-			applicant_type='Customer', repayment_start_date='2021-04-30', posting_date='2021-04-01')
+		loan = create_loan(
+			self.applicant2,
+			"Personal Loan",
+			200000,
+			"Repay Over Number of Periods",
+			4,
+			applicant_type="Customer",
+			repayment_start_date="2021-04-30",
+			posting_date="2021-04-01",
+		)
 
 		loan.submit()
 
-		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date='2021-04-01')
+		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date="2021-04-01")
 
-		process_loan_interest_accrual_for_term_loans(posting_date='2021-05-01')
-		process_loan_interest_accrual_for_term_loans(posting_date='2021-06-01')
+		process_loan_interest_accrual_for_term_loans(posting_date="2021-05-01")
+		process_loan_interest_accrual_for_term_loans(posting_date="2021-06-01")
 
-		repayment_entry = create_repayment_entry(loan.name, self.applicant2, '2021-06-05', 120000)
+		repayment_entry = create_repayment_entry(loan.name, self.applicant2, "2021-06-05", 120000)
 		repayment_entry.submit()
 
 		loan.load_from_db()
 
-		self.assertEqual(flt(loan.get('repayment_schedule')[3].principal_amount, 2), 41369.83)
-		self.assertEqual(flt(loan.get('repayment_schedule')[3].interest_amount, 2), 289.59)
-		self.assertEqual(flt(loan.get('repayment_schedule')[3].total_payment, 2), 41659.41)
-		self.assertEqual(flt(loan.get('repayment_schedule')[3].balance_loan_amount, 2), 0)
+		self.assertEqual(flt(loan.get("repayment_schedule")[3].principal_amount, 2), 41369.83)
+		self.assertEqual(flt(loan.get("repayment_schedule")[3].interest_amount, 2), 289.59)
+		self.assertEqual(flt(loan.get("repayment_schedule")[3].total_payment, 2), 41659.41)
+		self.assertEqual(flt(loan.get("repayment_schedule")[3].balance_loan_amount, 2), 0)
 
 	def test_security_shortfall(self):
-		pledges = [{
-			"loan_security": "Test Security 2",
-			"qty": 8000.00,
-			"haircut": 50,
-		}]
+		pledges = [
+			{
+				"loan_security": "Test Security 2",
+				"qty": 8000.00,
+				"haircut": 50,
+			}
+		]
 
-		loan_application = create_loan_application('_Test Company', self.applicant2,
-			'Stock Loan', pledges, "Repay Over Number of Periods", 12)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant2, "Stock Loan", pledges, "Repay Over Number of Periods", 12
+		)
 
 		create_pledge(loan_application)
 
-		loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application)
+		loan = create_loan_with_security(
+			self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application
+		)
 		loan.submit()
 
 		make_loan_disbursement_entry(loan.name, loan.loan_amount)
 
-		frappe.db.sql("""UPDATE `tabLoan Security Price` SET loan_security_price = 100
-			where loan_security='Test Security 2'""")
+		frappe.db.sql(
+			"""UPDATE `tabLoan Security Price` SET loan_security_price = 100
+			where loan_security='Test Security 2'"""
+		)
 
 		create_process_loan_security_shortfall()
 		loan_security_shortfall = frappe.get_doc("Loan Security Shortfall", {"loan": loan.name})
@@ -353,8 +453,10 @@
 		self.assertEqual(loan_security_shortfall.security_value, 800000.00)
 		self.assertEqual(loan_security_shortfall.shortfall_amount, 600000.00)
 
-		frappe.db.sql(""" UPDATE `tabLoan Security Price` SET loan_security_price = 250
-			where loan_security='Test Security 2'""")
+		frappe.db.sql(
+			""" UPDATE `tabLoan Security Price` SET loan_security_price = 250
+			where loan_security='Test Security 2'"""
+		)
 
 		create_process_loan_security_shortfall()
 		loan_security_shortfall = frappe.get_doc("Loan Security Shortfall", {"loan": loan.name})
@@ -362,33 +464,40 @@
 		self.assertEqual(loan_security_shortfall.shortfall_amount, 0)
 
 	def test_loan_security_unpledge(self):
-		pledge = [{
-			"loan_security": "Test Security 1",
-			"qty": 4000.00
-		}]
+		pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}]
 
-		loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant2, "Demand Loan", pledge
+		)
 		create_pledge(loan_application)
 
-		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+		loan = create_demand_loan(
+			self.applicant2, "Demand Loan", loan_application, posting_date="2019-10-01"
+		)
 		loan.submit()
 
 		self.assertEqual(loan.loan_amount, 1000000)
 
-		first_date = '2019-10-01'
-		last_date = '2019-10-30'
+		first_date = "2019-10-01"
+		last_date = "2019-10-30"
 
 		no_of_days = date_diff(last_date, first_date) + 1
 
 		no_of_days += 5
 
-		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
-			/ (days_in_year(get_datetime(first_date).year) * 100)
+		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) / (
+			days_in_year(get_datetime(first_date).year) * 100
+		)
 
 		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
-		process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+		process_loan_interest_accrual_for_demand_loans(posting_date=last_date)
 
-		repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5), flt(loan.loan_amount + accrued_interest_amount))
+		repayment_entry = create_repayment_entry(
+			loan.name,
+			self.applicant2,
+			add_days(last_date, 5),
+			flt(loan.loan_amount + accrued_interest_amount),
+		)
 		repayment_entry.submit()
 
 		request_loan_closure(loan.name)
@@ -397,98 +506,108 @@
 
 		unpledge_request = unpledge_security(loan=loan.name, save=1)
 		unpledge_request.submit()
-		unpledge_request.status = 'Approved'
+		unpledge_request.status = "Approved"
 		unpledge_request.save()
 		loan.load_from_db()
 
 		pledged_qty = get_pledged_security_qty(loan.name)
 
-		self.assertEqual(loan.status, 'Closed')
+		self.assertEqual(loan.status, "Closed")
 		self.assertEqual(sum(pledged_qty.values()), 0)
 
 		amounts = amounts = calculate_amounts(loan.name, add_days(last_date, 5))
-		self.assertEqual(amounts['pending_principal_amount'], 0)
-		self.assertEqual(amounts['payable_principal_amount'], 0.0)
-		self.assertEqual(amounts['interest_amount'], 0)
+		self.assertEqual(amounts["pending_principal_amount"], 0)
+		self.assertEqual(amounts["payable_principal_amount"], 0.0)
+		self.assertEqual(amounts["interest_amount"], 0)
 
 	def test_partial_loan_security_unpledge(self):
-		pledge = [{
-			"loan_security": "Test Security 1",
-			"qty": 2000.00
-		},
-		{
-			"loan_security": "Test Security 2",
-			"qty": 4000.00
-		}]
+		pledge = [
+			{"loan_security": "Test Security 1", "qty": 2000.00},
+			{"loan_security": "Test Security 2", "qty": 4000.00},
+		]
 
-		loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant2, "Demand Loan", pledge
+		)
 		create_pledge(loan_application)
 
-		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+		loan = create_demand_loan(
+			self.applicant2, "Demand Loan", loan_application, posting_date="2019-10-01"
+		)
 		loan.submit()
 
 		self.assertEqual(loan.loan_amount, 1000000)
 
-		first_date = '2019-10-01'
-		last_date = '2019-10-30'
+		first_date = "2019-10-01"
+		last_date = "2019-10-30"
 
 		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
-		process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+		process_loan_interest_accrual_for_demand_loans(posting_date=last_date)
 
-		repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5), 600000)
+		repayment_entry = create_repayment_entry(
+			loan.name, self.applicant2, add_days(last_date, 5), 600000
+		)
 		repayment_entry.submit()
 
-		unpledge_map = {'Test Security 2': 2000}
+		unpledge_map = {"Test Security 2": 2000}
 
-		unpledge_request = unpledge_security(loan=loan.name, security_map = unpledge_map, save=1)
+		unpledge_request = unpledge_security(loan=loan.name, security_map=unpledge_map, save=1)
 		unpledge_request.submit()
-		unpledge_request.status = 'Approved'
+		unpledge_request.status = "Approved"
 		unpledge_request.save()
 		unpledge_request.submit()
 		unpledge_request.load_from_db()
 		self.assertEqual(unpledge_request.docstatus, 1)
 
 	def test_sanctioned_loan_security_unpledge(self):
-		pledge = [{
-			"loan_security": "Test Security 1",
-			"qty": 4000.00
-		}]
+		pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}]
 
-		loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant2, "Demand Loan", pledge
+		)
 		create_pledge(loan_application)
 
-		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+		loan = create_demand_loan(
+			self.applicant2, "Demand Loan", loan_application, posting_date="2019-10-01"
+		)
 		loan.submit()
 
 		self.assertEqual(loan.loan_amount, 1000000)
 
-		unpledge_map = {'Test Security 1': 4000}
-		unpledge_request = unpledge_security(loan=loan.name, security_map = unpledge_map, save=1)
+		unpledge_map = {"Test Security 1": 4000}
+		unpledge_request = unpledge_security(loan=loan.name, security_map=unpledge_map, save=1)
 		unpledge_request.submit()
-		unpledge_request.status = 'Approved'
+		unpledge_request.status = "Approved"
 		unpledge_request.save()
 		unpledge_request.submit()
 
 	def test_disbursal_check_with_shortfall(self):
-		pledges = [{
-			"loan_security": "Test Security 2",
-			"qty": 8000.00,
-			"haircut": 50,
-		}]
+		pledges = [
+			{
+				"loan_security": "Test Security 2",
+				"qty": 8000.00,
+				"haircut": 50,
+			}
+		]
 
-		loan_application = create_loan_application('_Test Company', self.applicant2,
-			'Stock Loan', pledges, "Repay Over Number of Periods", 12)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant2, "Stock Loan", pledges, "Repay Over Number of Periods", 12
+		)
 
 		create_pledge(loan_application)
 
-		loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application)
+		loan = create_loan_with_security(
+			self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application
+		)
 		loan.submit()
 
-		#Disbursing 7,00,000 from the allowed 10,00,000 according to security pledge
+		# Disbursing 7,00,000 from the allowed 10,00,000 according to security pledge
 		make_loan_disbursement_entry(loan.name, 700000)
 
-		frappe.db.sql("""UPDATE `tabLoan Security Price` SET loan_security_price = 100
-			where loan_security='Test Security 2'""")
+		frappe.db.sql(
+			"""UPDATE `tabLoan Security Price` SET loan_security_price = 100
+			where loan_security='Test Security 2'"""
+		)
 
 		create_process_loan_security_shortfall()
 		loan_security_shortfall = frappe.get_doc("Loan Security Shortfall", {"loan": loan.name})
@@ -496,422 +615,505 @@
 
 		self.assertEqual(get_disbursal_amount(loan.name), 0)
 
-		frappe.db.sql(""" UPDATE `tabLoan Security Price` SET loan_security_price = 250
-			where loan_security='Test Security 2'""")
+		frappe.db.sql(
+			""" UPDATE `tabLoan Security Price` SET loan_security_price = 250
+			where loan_security='Test Security 2'"""
+		)
 
 	def test_disbursal_check_without_shortfall(self):
-		pledges = [{
-			"loan_security": "Test Security 2",
-			"qty": 8000.00,
-			"haircut": 50,
-		}]
+		pledges = [
+			{
+				"loan_security": "Test Security 2",
+				"qty": 8000.00,
+				"haircut": 50,
+			}
+		]
 
-		loan_application = create_loan_application('_Test Company', self.applicant2,
-			'Stock Loan', pledges, "Repay Over Number of Periods", 12)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant2, "Stock Loan", pledges, "Repay Over Number of Periods", 12
+		)
 
 		create_pledge(loan_application)
 
-		loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application)
+		loan = create_loan_with_security(
+			self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application
+		)
 		loan.submit()
 
-		#Disbursing 7,00,000 from the allowed 10,00,000 according to security pledge
+		# Disbursing 7,00,000 from the allowed 10,00,000 according to security pledge
 		make_loan_disbursement_entry(loan.name, 700000)
 
 		self.assertEqual(get_disbursal_amount(loan.name), 300000)
 
 	def test_pending_loan_amount_after_closure_request(self):
-		pledge = [{
-			"loan_security": "Test Security 1",
-			"qty": 4000.00
-		}]
+		pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}]
 
-		loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant2, "Demand Loan", pledge
+		)
 		create_pledge(loan_application)
 
-		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+		loan = create_demand_loan(
+			self.applicant2, "Demand Loan", loan_application, posting_date="2019-10-01"
+		)
 		loan.submit()
 
 		self.assertEqual(loan.loan_amount, 1000000)
 
-		first_date = '2019-10-01'
-		last_date = '2019-10-30'
+		first_date = "2019-10-01"
+		last_date = "2019-10-30"
 
 		no_of_days = date_diff(last_date, first_date) + 1
 
 		no_of_days += 5
 
-		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
-			/ (days_in_year(get_datetime(first_date).year) * 100)
+		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) / (
+			days_in_year(get_datetime(first_date).year) * 100
+		)
 
 		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
-		process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+		process_loan_interest_accrual_for_demand_loans(posting_date=last_date)
 
 		amounts = calculate_amounts(loan.name, add_days(last_date, 5))
 
-		repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5), flt(loan.loan_amount + accrued_interest_amount))
+		repayment_entry = create_repayment_entry(
+			loan.name,
+			self.applicant2,
+			add_days(last_date, 5),
+			flt(loan.loan_amount + accrued_interest_amount),
+		)
 		repayment_entry.submit()
 
-		amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount',
-			'paid_principal_amount'])
+		amounts = frappe.db.get_value(
+			"Loan Interest Accrual", {"loan": loan.name}, ["paid_interest_amount", "paid_principal_amount"]
+		)
 
 		request_loan_closure(loan.name)
 		loan.load_from_db()
 		self.assertEqual(loan.status, "Loan Closure Requested")
 
 		amounts = calculate_amounts(loan.name, add_days(last_date, 5))
-		self.assertEqual(amounts['pending_principal_amount'], 0.0)
+		self.assertEqual(amounts["pending_principal_amount"], 0.0)
 
 	def test_partial_unaccrued_interest_payment(self):
-		pledge = [{
-			"loan_security": "Test Security 1",
-			"qty": 4000.00
-		}]
+		pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}]
 
-		loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant2, "Demand Loan", pledge
+		)
 		create_pledge(loan_application)
 
-		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+		loan = create_demand_loan(
+			self.applicant2, "Demand Loan", loan_application, posting_date="2019-10-01"
+		)
 		loan.submit()
 
 		self.assertEqual(loan.loan_amount, 1000000)
 
-		first_date = '2019-10-01'
-		last_date = '2019-10-30'
+		first_date = "2019-10-01"
+		last_date = "2019-10-30"
 
 		no_of_days = date_diff(last_date, first_date) + 1
 
 		no_of_days += 5.5
 
 		# get partial unaccrued interest amount
-		paid_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
-			/ (days_in_year(get_datetime(first_date).year) * 100)
+		paid_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) / (
+			days_in_year(get_datetime(first_date).year) * 100
+		)
 
 		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
-		process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+		process_loan_interest_accrual_for_demand_loans(posting_date=last_date)
 
 		amounts = calculate_amounts(loan.name, add_days(last_date, 5))
 
-		repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5),
-			paid_amount)
+		repayment_entry = create_repayment_entry(
+			loan.name, self.applicant2, add_days(last_date, 5), paid_amount
+		)
 
 		repayment_entry.submit()
 		repayment_entry.load_from_db()
 
-		partial_accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * 5) \
-			/ (days_in_year(get_datetime(first_date).year) * 100)
+		partial_accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * 5) / (
+			days_in_year(get_datetime(first_date).year) * 100
+		)
 
-		interest_amount = flt(amounts['interest_amount'] + partial_accrued_interest_amount, 2)
+		interest_amount = flt(amounts["interest_amount"] + partial_accrued_interest_amount, 2)
 		self.assertEqual(flt(repayment_entry.total_interest_paid, 0), flt(interest_amount, 0))
 
 	def test_penalty(self):
 		loan, amounts = create_loan_scenario_for_penalty(self)
 		# 30 days - grace period
 		penalty_days = 30 - 4
-		penalty_applicable_amount = flt(amounts['interest_amount']/2)
+		penalty_applicable_amount = flt(amounts["interest_amount"] / 2)
 		penalty_amount = flt((((penalty_applicable_amount * 25) / 100) * penalty_days), 2)
-		process = process_loan_interest_accrual_for_demand_loans(posting_date = '2019-11-30')
+		process = process_loan_interest_accrual_for_demand_loans(posting_date="2019-11-30")
 
-		calculated_penalty_amount = frappe.db.get_value('Loan Interest Accrual',
-			{'process_loan_interest_accrual': process, 'loan': loan.name}, 'penalty_amount')
+		calculated_penalty_amount = frappe.db.get_value(
+			"Loan Interest Accrual",
+			{"process_loan_interest_accrual": process, "loan": loan.name},
+			"penalty_amount",
+		)
 
 		self.assertEqual(loan.loan_amount, 1000000)
 		self.assertEqual(calculated_penalty_amount, penalty_amount)
 
 	def test_penalty_repayment(self):
 		loan, dummy = create_loan_scenario_for_penalty(self)
-		amounts = calculate_amounts(loan.name, '2019-11-30 00:00:00')
+		amounts = calculate_amounts(loan.name, "2019-11-30 00:00:00")
 
 		first_penalty = 10000
-		second_penalty = amounts['penalty_amount'] - 10000
+		second_penalty = amounts["penalty_amount"] - 10000
 
-		repayment_entry = create_repayment_entry(loan.name, self.applicant2, '2019-11-30 00:00:00', 10000)
+		repayment_entry = create_repayment_entry(
+			loan.name, self.applicant2, "2019-11-30 00:00:00", 10000
+		)
 		repayment_entry.submit()
 
-		amounts = calculate_amounts(loan.name, '2019-11-30 00:00:01')
-		self.assertEqual(amounts['penalty_amount'], second_penalty)
+		amounts = calculate_amounts(loan.name, "2019-11-30 00:00:01")
+		self.assertEqual(amounts["penalty_amount"], second_penalty)
 
-		repayment_entry = create_repayment_entry(loan.name, self.applicant2, '2019-11-30 00:00:01', second_penalty)
+		repayment_entry = create_repayment_entry(
+			loan.name, self.applicant2, "2019-11-30 00:00:01", second_penalty
+		)
 		repayment_entry.submit()
 
-		amounts = calculate_amounts(loan.name, '2019-11-30 00:00:02')
-		self.assertEqual(amounts['penalty_amount'], 0)
+		amounts = calculate_amounts(loan.name, "2019-11-30 00:00:02")
+		self.assertEqual(amounts["penalty_amount"], 0)
 
 	def test_loan_write_off_limit(self):
-		pledge = [{
-			"loan_security": "Test Security 1",
-			"qty": 4000.00
-		}]
+		pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}]
 
-		loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant2, "Demand Loan", pledge
+		)
 		create_pledge(loan_application)
 
-		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+		loan = create_demand_loan(
+			self.applicant2, "Demand Loan", loan_application, posting_date="2019-10-01"
+		)
 		loan.submit()
 
 		self.assertEqual(loan.loan_amount, 1000000)
 
-		first_date = '2019-10-01'
-		last_date = '2019-10-30'
+		first_date = "2019-10-01"
+		last_date = "2019-10-30"
 
 		no_of_days = date_diff(last_date, first_date) + 1
 		no_of_days += 5
 
-		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
-			/ (days_in_year(get_datetime(first_date).year) * 100)
+		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) / (
+			days_in_year(get_datetime(first_date).year) * 100
+		)
 
 		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
-		process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+		process_loan_interest_accrual_for_demand_loans(posting_date=last_date)
 
 		# repay 50 less so that it can be automatically written off
-		repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5),
-			flt(loan.loan_amount + accrued_interest_amount - 50))
+		repayment_entry = create_repayment_entry(
+			loan.name,
+			self.applicant2,
+			add_days(last_date, 5),
+			flt(loan.loan_amount + accrued_interest_amount - 50),
+		)
 
 		repayment_entry.submit()
 
-		amount = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['sum(paid_interest_amount)'])
+		amount = frappe.db.get_value(
+			"Loan Interest Accrual", {"loan": loan.name}, ["sum(paid_interest_amount)"]
+		)
 
-		self.assertEqual(flt(amount, 0),flt(accrued_interest_amount, 0))
+		self.assertEqual(flt(amount, 0), flt(accrued_interest_amount, 0))
 		self.assertEqual(flt(repayment_entry.penalty_amount, 5), 0)
 
 		amounts = calculate_amounts(loan.name, add_days(last_date, 5))
-		self.assertEqual(flt(amounts['pending_principal_amount'], 0), 50)
+		self.assertEqual(flt(amounts["pending_principal_amount"], 0), 50)
 
 		request_loan_closure(loan.name)
 		loan.load_from_db()
 		self.assertEqual(loan.status, "Loan Closure Requested")
 
 	def test_loan_repayment_against_partially_disbursed_loan(self):
-		pledge = [{
-			"loan_security": "Test Security 1",
-			"qty": 4000.00
-		}]
+		pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}]
 
-		loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant2, "Demand Loan", pledge
+		)
 		create_pledge(loan_application)
 
-		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+		loan = create_demand_loan(
+			self.applicant2, "Demand Loan", loan_application, posting_date="2019-10-01"
+		)
 		loan.submit()
 
-		first_date = '2019-10-01'
-		last_date = '2019-10-30'
+		first_date = "2019-10-01"
+		last_date = "2019-10-30"
 
-		make_loan_disbursement_entry(loan.name, loan.loan_amount/2, disbursement_date=first_date)
+		make_loan_disbursement_entry(loan.name, loan.loan_amount / 2, disbursement_date=first_date)
 
 		loan.load_from_db()
 
 		self.assertEqual(loan.status, "Partially Disbursed")
-		create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5),
-			flt(loan.loan_amount/3))
+		create_repayment_entry(
+			loan.name, self.applicant2, add_days(last_date, 5), flt(loan.loan_amount / 3)
+		)
 
 	def test_loan_amount_write_off(self):
-		pledge = [{
-			"loan_security": "Test Security 1",
-			"qty": 4000.00
-		}]
+		pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}]
 
-		loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant2, "Demand Loan", pledge
+		)
 		create_pledge(loan_application)
 
-		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+		loan = create_demand_loan(
+			self.applicant2, "Demand Loan", loan_application, posting_date="2019-10-01"
+		)
 		loan.submit()
 
 		self.assertEqual(loan.loan_amount, 1000000)
 
-		first_date = '2019-10-01'
-		last_date = '2019-10-30'
+		first_date = "2019-10-01"
+		last_date = "2019-10-30"
 
 		no_of_days = date_diff(last_date, first_date) + 1
 		no_of_days += 5
 
-		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
-			/ (days_in_year(get_datetime(first_date).year) * 100)
+		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) / (
+			days_in_year(get_datetime(first_date).year) * 100
+		)
 
 		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
-		process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+		process_loan_interest_accrual_for_demand_loans(posting_date=last_date)
 
 		# repay 100 less so that it can be automatically written off
-		repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5),
-			flt(loan.loan_amount + accrued_interest_amount - 100))
+		repayment_entry = create_repayment_entry(
+			loan.name,
+			self.applicant2,
+			add_days(last_date, 5),
+			flt(loan.loan_amount + accrued_interest_amount - 100),
+		)
 
 		repayment_entry.submit()
 
-		amount = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['sum(paid_interest_amount)'])
+		amount = frappe.db.get_value(
+			"Loan Interest Accrual", {"loan": loan.name}, ["sum(paid_interest_amount)"]
+		)
 
-		self.assertEqual(flt(amount, 0),flt(accrued_interest_amount, 0))
+		self.assertEqual(flt(amount, 0), flt(accrued_interest_amount, 0))
 		self.assertEqual(flt(repayment_entry.penalty_amount, 5), 0)
 
 		amounts = calculate_amounts(loan.name, add_days(last_date, 5))
-		self.assertEqual(flt(amounts['pending_principal_amount'], 0), 100)
+		self.assertEqual(flt(amounts["pending_principal_amount"], 0), 100)
 
-		we = make_loan_write_off(loan.name, amount=amounts['pending_principal_amount'])
+		we = make_loan_write_off(loan.name, amount=amounts["pending_principal_amount"])
 		we.submit()
 
 		amounts = calculate_amounts(loan.name, add_days(last_date, 5))
-		self.assertEqual(flt(amounts['pending_principal_amount'], 0), 0)
+		self.assertEqual(flt(amounts["pending_principal_amount"], 0), 0)
+
 
 def create_loan_scenario_for_penalty(doc):
-	pledge = [{
-		"loan_security": "Test Security 1",
-		"qty": 4000.00
-	}]
+	pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}]
 
-	loan_application = create_loan_application('_Test Company', doc.applicant2, 'Demand Loan', pledge)
+	loan_application = create_loan_application("_Test Company", doc.applicant2, "Demand Loan", pledge)
 	create_pledge(loan_application)
-	loan = create_demand_loan(doc.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+	loan = create_demand_loan(
+		doc.applicant2, "Demand Loan", loan_application, posting_date="2019-10-01"
+	)
 	loan.submit()
 
-	first_date = '2019-10-01'
-	last_date = '2019-10-30'
+	first_date = "2019-10-01"
+	last_date = "2019-10-30"
 
 	make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
-	process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+	process_loan_interest_accrual_for_demand_loans(posting_date=last_date)
 
 	amounts = calculate_amounts(loan.name, add_days(last_date, 1))
-	paid_amount = amounts['interest_amount']/2
+	paid_amount = amounts["interest_amount"] / 2
 
-	repayment_entry = create_repayment_entry(loan.name, doc.applicant2, add_days(last_date, 5),
-		paid_amount)
+	repayment_entry = create_repayment_entry(
+		loan.name, doc.applicant2, add_days(last_date, 5), paid_amount
+	)
 
 	repayment_entry.submit()
 
 	return loan, amounts
 
+
 def create_loan_accounts():
 	if not frappe.db.exists("Account", "Loans and Advances (Assets) - _TC"):
-		frappe.get_doc({
-			"doctype": "Account",
-			"account_name": "Loans and Advances (Assets)",
-			"company": "_Test Company",
-			"root_type": "Asset",
-			"report_type": "Balance Sheet",
-			"currency": "INR",
-			"parent_account": "Current Assets - _TC",
-			"account_type": "Bank",
-			"is_group": 1
-		}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "Account",
+				"account_name": "Loans and Advances (Assets)",
+				"company": "_Test Company",
+				"root_type": "Asset",
+				"report_type": "Balance Sheet",
+				"currency": "INR",
+				"parent_account": "Current Assets - _TC",
+				"account_type": "Bank",
+				"is_group": 1,
+			}
+		).insert(ignore_permissions=True)
 
 	if not frappe.db.exists("Account", "Loan Account - _TC"):
-		frappe.get_doc({
-			"doctype": "Account",
-			"company": "_Test Company",
-			"account_name": "Loan Account",
-			"root_type": "Asset",
-			"report_type": "Balance Sheet",
-			"currency": "INR",
-			"parent_account": "Loans and Advances (Assets) - _TC",
-			"account_type": "Bank",
-		}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "Account",
+				"company": "_Test Company",
+				"account_name": "Loan Account",
+				"root_type": "Asset",
+				"report_type": "Balance Sheet",
+				"currency": "INR",
+				"parent_account": "Loans and Advances (Assets) - _TC",
+				"account_type": "Bank",
+			}
+		).insert(ignore_permissions=True)
 
 	if not frappe.db.exists("Account", "Payment Account - _TC"):
-		frappe.get_doc({
-			"doctype": "Account",
-			"company": "_Test Company",
-			"account_name": "Payment Account",
-			"root_type": "Asset",
-			"report_type": "Balance Sheet",
-			"currency": "INR",
-			"parent_account": "Bank Accounts - _TC",
-			"account_type": "Bank",
-		}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "Account",
+				"company": "_Test Company",
+				"account_name": "Payment Account",
+				"root_type": "Asset",
+				"report_type": "Balance Sheet",
+				"currency": "INR",
+				"parent_account": "Bank Accounts - _TC",
+				"account_type": "Bank",
+			}
+		).insert(ignore_permissions=True)
 
 	if not frappe.db.exists("Account", "Disbursement Account - _TC"):
-		frappe.get_doc({
-			"doctype": "Account",
-			"company": "_Test Company",
-			"account_name": "Disbursement Account",
-			"root_type": "Asset",
-			"report_type": "Balance Sheet",
-			"currency": "INR",
-			"parent_account": "Bank Accounts - _TC",
-			"account_type": "Bank",
-		}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "Account",
+				"company": "_Test Company",
+				"account_name": "Disbursement Account",
+				"root_type": "Asset",
+				"report_type": "Balance Sheet",
+				"currency": "INR",
+				"parent_account": "Bank Accounts - _TC",
+				"account_type": "Bank",
+			}
+		).insert(ignore_permissions=True)
 
 	if not frappe.db.exists("Account", "Interest Income Account - _TC"):
-		frappe.get_doc({
-			"doctype": "Account",
-			"company": "_Test Company",
-			"root_type": "Income",
-			"account_name": "Interest Income Account",
-			"report_type": "Profit and Loss",
-			"currency": "INR",
-			"parent_account": "Direct Income - _TC",
-			"account_type": "Income Account",
-		}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "Account",
+				"company": "_Test Company",
+				"root_type": "Income",
+				"account_name": "Interest Income Account",
+				"report_type": "Profit and Loss",
+				"currency": "INR",
+				"parent_account": "Direct Income - _TC",
+				"account_type": "Income Account",
+			}
+		).insert(ignore_permissions=True)
 
 	if not frappe.db.exists("Account", "Penalty Income Account - _TC"):
-		frappe.get_doc({
-			"doctype": "Account",
-			"company": "_Test Company",
-			"account_name": "Penalty Income Account",
-			"root_type": "Income",
-			"report_type": "Profit and Loss",
-			"currency": "INR",
-			"parent_account": "Direct Income - _TC",
-			"account_type": "Income Account",
-		}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "Account",
+				"company": "_Test Company",
+				"account_name": "Penalty Income Account",
+				"root_type": "Income",
+				"report_type": "Profit and Loss",
+				"currency": "INR",
+				"parent_account": "Direct Income - _TC",
+				"account_type": "Income Account",
+			}
+		).insert(ignore_permissions=True)
 
-def create_loan_type(loan_name, maximum_loan_amount, rate_of_interest, penalty_interest_rate=None, is_term_loan=None, grace_period_in_days=None,
-	mode_of_payment=None, disbursement_account=None, payment_account=None, loan_account=None, interest_income_account=None, penalty_income_account=None,
-	repayment_method=None, repayment_periods=None):
+
+def create_loan_type(
+	loan_name,
+	maximum_loan_amount,
+	rate_of_interest,
+	penalty_interest_rate=None,
+	is_term_loan=None,
+	grace_period_in_days=None,
+	mode_of_payment=None,
+	disbursement_account=None,
+	payment_account=None,
+	loan_account=None,
+	interest_income_account=None,
+	penalty_income_account=None,
+	repayment_method=None,
+	repayment_periods=None,
+):
 
 	if not frappe.db.exists("Loan Type", loan_name):
-		loan_type = frappe.get_doc({
-			"doctype": "Loan Type",
-			"company": "_Test Company",
-			"loan_name": loan_name,
-			"is_term_loan": is_term_loan,
-			"maximum_loan_amount": maximum_loan_amount,
-			"rate_of_interest": rate_of_interest,
-			"penalty_interest_rate": penalty_interest_rate,
-			"grace_period_in_days": grace_period_in_days,
-			"mode_of_payment": mode_of_payment,
-			"disbursement_account": disbursement_account,
-			"payment_account": payment_account,
-			"loan_account": loan_account,
-			"interest_income_account": interest_income_account,
-			"penalty_income_account": penalty_income_account,
-			"repayment_method": repayment_method,
-			"repayment_periods": repayment_periods,
-			"write_off_amount": 100
-		}).insert()
+		loan_type = frappe.get_doc(
+			{
+				"doctype": "Loan Type",
+				"company": "_Test Company",
+				"loan_name": loan_name,
+				"is_term_loan": is_term_loan,
+				"maximum_loan_amount": maximum_loan_amount,
+				"rate_of_interest": rate_of_interest,
+				"penalty_interest_rate": penalty_interest_rate,
+				"grace_period_in_days": grace_period_in_days,
+				"mode_of_payment": mode_of_payment,
+				"disbursement_account": disbursement_account,
+				"payment_account": payment_account,
+				"loan_account": loan_account,
+				"interest_income_account": interest_income_account,
+				"penalty_income_account": penalty_income_account,
+				"repayment_method": repayment_method,
+				"repayment_periods": repayment_periods,
+				"write_off_amount": 100,
+			}
+		).insert()
 
 		loan_type.submit()
 
+
 def create_loan_security_type():
 	if not frappe.db.exists("Loan Security Type", "Stock"):
-		frappe.get_doc({
-			"doctype": "Loan Security Type",
-			"loan_security_type": "Stock",
-			"unit_of_measure": "Nos",
-			"haircut": 50.00,
-			"loan_to_value_ratio": 50
-		}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "Loan Security Type",
+				"loan_security_type": "Stock",
+				"unit_of_measure": "Nos",
+				"haircut": 50.00,
+				"loan_to_value_ratio": 50,
+			}
+		).insert(ignore_permissions=True)
+
 
 def create_loan_security():
 	if not frappe.db.exists("Loan Security", "Test Security 1"):
-		frappe.get_doc({
-			"doctype": "Loan Security",
-			"loan_security_type": "Stock",
-			"loan_security_code": "532779",
-			"loan_security_name": "Test Security 1",
-			"unit_of_measure": "Nos",
-			"haircut": 50.00,
-		}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "Loan Security",
+				"loan_security_type": "Stock",
+				"loan_security_code": "532779",
+				"loan_security_name": "Test Security 1",
+				"unit_of_measure": "Nos",
+				"haircut": 50.00,
+			}
+		).insert(ignore_permissions=True)
 
 	if not frappe.db.exists("Loan Security", "Test Security 2"):
-		frappe.get_doc({
-			"doctype": "Loan Security",
-			"loan_security_type": "Stock",
-			"loan_security_code": "531335",
-			"loan_security_name": "Test Security 2",
-			"unit_of_measure": "Nos",
-			"haircut": 50.00,
-		}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "Loan Security",
+				"loan_security_type": "Stock",
+				"loan_security_code": "531335",
+				"loan_security_name": "Test Security 2",
+				"unit_of_measure": "Nos",
+				"haircut": 50.00,
+			}
+		).insert(ignore_permissions=True)
+
 
 def create_loan_security_pledge(applicant, pledges, loan_application=None, loan=None):
 
 	lsp = frappe.new_doc("Loan Security Pledge")
-	lsp.applicant_type = 'Customer'
+	lsp.applicant_type = "Customer"
 	lsp.applicant = applicant
 	lsp.company = "_Test Company"
 	lsp.loan_application = loan_application
@@ -920,64 +1122,82 @@
 		lsp.loan = loan
 
 	for pledge in pledges:
-		lsp.append('securities', {
-			"loan_security": pledge['loan_security'],
-			"qty": pledge['qty']
-		})
+		lsp.append("securities", {"loan_security": pledge["loan_security"], "qty": pledge["qty"]})
 
 	lsp.save()
 	lsp.submit()
 
 	return lsp
 
+
 def make_loan_disbursement_entry(loan, amount, disbursement_date=None):
 
-	loan_disbursement_entry = frappe.get_doc({
-		"doctype": "Loan Disbursement",
-		"against_loan": loan,
-		"disbursement_date": disbursement_date,
-		"company": "_Test Company",
-		"disbursed_amount": amount,
-		"cost_center": 'Main - _TC'
-	}).insert(ignore_permissions=True)
+	loan_disbursement_entry = frappe.get_doc(
+		{
+			"doctype": "Loan Disbursement",
+			"against_loan": loan,
+			"disbursement_date": disbursement_date,
+			"company": "_Test Company",
+			"disbursed_amount": amount,
+			"cost_center": "Main - _TC",
+		}
+	).insert(ignore_permissions=True)
 
 	loan_disbursement_entry.save()
 	loan_disbursement_entry.submit()
 
 	return loan_disbursement_entry
 
+
 def create_loan_security_price(loan_security, loan_security_price, uom, from_date, to_date):
 
-	if not frappe.db.get_value("Loan Security Price",{"loan_security": loan_security,
-		"valid_from": ("<=", from_date), "valid_upto": (">=", to_date)}, 'name'):
+	if not frappe.db.get_value(
+		"Loan Security Price",
+		{"loan_security": loan_security, "valid_from": ("<=", from_date), "valid_upto": (">=", to_date)},
+		"name",
+	):
 
-		lsp = frappe.get_doc({
-			"doctype": "Loan Security Price",
-			"loan_security": loan_security,
-			"loan_security_price": loan_security_price,
-			"uom": uom,
-			"valid_from":from_date,
-			"valid_upto": to_date
-		}).insert(ignore_permissions=True)
+		lsp = frappe.get_doc(
+			{
+				"doctype": "Loan Security Price",
+				"loan_security": loan_security,
+				"loan_security_price": loan_security_price,
+				"uom": uom,
+				"valid_from": from_date,
+				"valid_upto": to_date,
+			}
+		).insert(ignore_permissions=True)
+
 
 def create_repayment_entry(loan, applicant, posting_date, paid_amount):
 
-	lr = frappe.get_doc({
-		"doctype": "Loan Repayment",
-		"against_loan": loan,
-		"company": "_Test Company",
-		"posting_date": posting_date or nowdate(),
-		"applicant": applicant,
-		"amount_paid": paid_amount,
-		"loan_type": "Stock Loan"
-	}).insert(ignore_permissions=True)
+	lr = frappe.get_doc(
+		{
+			"doctype": "Loan Repayment",
+			"against_loan": loan,
+			"company": "_Test Company",
+			"posting_date": posting_date or nowdate(),
+			"applicant": applicant,
+			"amount_paid": paid_amount,
+			"loan_type": "Stock Loan",
+		}
+	).insert(ignore_permissions=True)
 
 	return lr
 
-def create_loan_application(company, applicant, loan_type, proposed_pledges, repayment_method=None,
-	repayment_periods=None, posting_date=None, do_not_save=False):
-	loan_application = frappe.new_doc('Loan Application')
-	loan_application.applicant_type = 'Customer'
+
+def create_loan_application(
+	company,
+	applicant,
+	loan_type,
+	proposed_pledges,
+	repayment_method=None,
+	repayment_periods=None,
+	posting_date=None,
+	do_not_save=False,
+):
+	loan_application = frappe.new_doc("Loan Application")
+	loan_application.applicant_type = "Customer"
 	loan_application.company = company
 	loan_application.applicant = applicant
 	loan_application.loan_type = loan_type
@@ -989,7 +1209,7 @@
 		loan_application.repayment_periods = repayment_periods
 
 	for pledge in proposed_pledges:
-		loan_application.append('proposed_pledges', pledge)
+		loan_application.append("proposed_pledges", pledge)
 
 	if do_not_save:
 		return loan_application
@@ -997,75 +1217,99 @@
 	loan_application.save()
 	loan_application.submit()
 
-	loan_application.status = 'Approved'
+	loan_application.status = "Approved"
 	loan_application.save()
 
 	return loan_application.name
 
 
-def create_loan(applicant, loan_type, loan_amount, repayment_method, repayment_periods,
-	applicant_type=None, repayment_start_date=None, posting_date=None):
+def create_loan(
+	applicant,
+	loan_type,
+	loan_amount,
+	repayment_method,
+	repayment_periods,
+	applicant_type=None,
+	repayment_start_date=None,
+	posting_date=None,
+):
 
-	loan = frappe.get_doc({
-		"doctype": "Loan",
-		"applicant_type": applicant_type or "Employee",
-		"company": "_Test Company",
-		"applicant": applicant,
-		"loan_type": loan_type,
-		"loan_amount": loan_amount,
-		"repayment_method": repayment_method,
-		"repayment_periods": repayment_periods,
-		"repayment_start_date": repayment_start_date or nowdate(),
-		"is_term_loan": 1,
-		"posting_date": posting_date or nowdate()
-	})
+	loan = frappe.get_doc(
+		{
+			"doctype": "Loan",
+			"applicant_type": applicant_type or "Employee",
+			"company": "_Test Company",
+			"applicant": applicant,
+			"loan_type": loan_type,
+			"loan_amount": loan_amount,
+			"repayment_method": repayment_method,
+			"repayment_periods": repayment_periods,
+			"repayment_start_date": repayment_start_date or nowdate(),
+			"is_term_loan": 1,
+			"posting_date": posting_date or nowdate(),
+		}
+	)
 
 	loan.save()
 	return loan
 
-def create_loan_with_security(applicant, loan_type, repayment_method, repayment_periods, loan_application, posting_date=None, repayment_start_date=None):
-	loan = frappe.get_doc({
-		"doctype": "Loan",
-		"company": "_Test Company",
-		"applicant_type": "Customer",
-		"posting_date": posting_date or nowdate(),
-		"loan_application": loan_application,
-		"applicant": applicant,
-		"loan_type": loan_type,
-		"is_term_loan": 1,
-		"is_secured_loan": 1,
-		"repayment_method": repayment_method,
-		"repayment_periods": repayment_periods,
-		"repayment_start_date": repayment_start_date or nowdate(),
-		"mode_of_payment": frappe.db.get_value('Mode of Payment', {'type': 'Cash'}, 'name'),
-		"payment_account": 'Payment Account - _TC',
-		"loan_account": 'Loan Account - _TC',
-		"interest_income_account": 'Interest Income Account - _TC',
-		"penalty_income_account": 'Penalty Income Account - _TC',
-	})
+
+def create_loan_with_security(
+	applicant,
+	loan_type,
+	repayment_method,
+	repayment_periods,
+	loan_application,
+	posting_date=None,
+	repayment_start_date=None,
+):
+	loan = frappe.get_doc(
+		{
+			"doctype": "Loan",
+			"company": "_Test Company",
+			"applicant_type": "Customer",
+			"posting_date": posting_date or nowdate(),
+			"loan_application": loan_application,
+			"applicant": applicant,
+			"loan_type": loan_type,
+			"is_term_loan": 1,
+			"is_secured_loan": 1,
+			"repayment_method": repayment_method,
+			"repayment_periods": repayment_periods,
+			"repayment_start_date": repayment_start_date or nowdate(),
+			"mode_of_payment": frappe.db.get_value("Mode of Payment", {"type": "Cash"}, "name"),
+			"payment_account": "Payment Account - _TC",
+			"loan_account": "Loan Account - _TC",
+			"interest_income_account": "Interest Income Account - _TC",
+			"penalty_income_account": "Penalty Income Account - _TC",
+		}
+	)
 
 	loan.save()
 
 	return loan
 
+
 def create_demand_loan(applicant, loan_type, loan_application, posting_date=None):
 
-	loan = frappe.get_doc({
-		"doctype": "Loan",
-		"company": "_Test Company",
-		"applicant_type": "Customer",
-		"posting_date": posting_date or nowdate(),
-		'loan_application': loan_application,
-		"applicant": applicant,
-		"loan_type": loan_type,
-		"is_term_loan": 0,
-		"is_secured_loan": 1,
-		"mode_of_payment": frappe.db.get_value('Mode of Payment', {'type': 'Cash'}, 'name'),
-		"payment_account": 'Payment Account - _TC',
-		"loan_account": 'Loan Account - _TC',
-		"interest_income_account": 'Interest Income Account - _TC',
-		"penalty_income_account": 'Penalty Income Account - _TC',
-	})
+	loan = frappe.get_doc(
+		{
+			"doctype": "Loan",
+			"company": "_Test Company",
+			"applicant_type": "Customer",
+			"posting_date": posting_date or nowdate(),
+			"loan_application": loan_application,
+			"applicant": applicant,
+			"loan_type": loan_type,
+			"is_term_loan": 0,
+			"is_secured_loan": 1,
+			"mode_of_payment": frappe.db.get_value("Mode of Payment", {"type": "Cash"}, "name"),
+			"payment_account": "Payment Account - _TC",
+			"loan_account": "Loan Account - _TC",
+			"interest_income_account": "Interest Income Account - _TC",
+			"penalty_income_account": "Penalty Income Account - _TC",
+		}
+	)
 
 	loan.save()
 
diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.py b/erpnext/loan_management/doctype/loan_application/loan_application.py
index a8ffcb9..5f040e2 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application.py
+++ b/erpnext/loan_management/doctype/loan_application/loan_application.py
@@ -29,8 +29,13 @@
 		self.validate_loan_amount()
 
 		if self.is_term_loan:
-			validate_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount,
-				self.repayment_periods, self.is_term_loan)
+			validate_repayment_method(
+				self.repayment_method,
+				self.loan_amount,
+				self.repayment_amount,
+				self.repayment_periods,
+				self.is_term_loan,
+			)
 
 		self.validate_loan_type()
 
@@ -46,21 +51,35 @@
 		if not self.loan_amount:
 			frappe.throw(_("Loan Amount is mandatory"))
 
-		maximum_loan_limit = frappe.db.get_value('Loan Type', self.loan_type, 'maximum_loan_amount')
+		maximum_loan_limit = frappe.db.get_value("Loan Type", self.loan_type, "maximum_loan_amount")
 		if maximum_loan_limit and self.loan_amount > maximum_loan_limit:
-			frappe.throw(_("Loan Amount cannot exceed Maximum Loan Amount of {0}").format(maximum_loan_limit))
+			frappe.throw(
+				_("Loan Amount cannot exceed Maximum Loan Amount of {0}").format(maximum_loan_limit)
+			)
 
 		if self.maximum_loan_amount and self.loan_amount > self.maximum_loan_amount:
-			frappe.throw(_("Loan Amount exceeds maximum loan amount of {0} as per proposed securities").format(self.maximum_loan_amount))
+			frappe.throw(
+				_("Loan Amount exceeds maximum loan amount of {0} as per proposed securities").format(
+					self.maximum_loan_amount
+				)
+			)
 
 	def check_sanctioned_amount_limit(self):
-		sanctioned_amount_limit = get_sanctioned_amount_limit(self.applicant_type, self.applicant, self.company)
+		sanctioned_amount_limit = get_sanctioned_amount_limit(
+			self.applicant_type, self.applicant, self.company
+		)
 
 		if sanctioned_amount_limit:
 			total_loan_amount = get_total_loan_amount(self.applicant_type, self.applicant, self.company)
 
-		if sanctioned_amount_limit and flt(self.loan_amount) + flt(total_loan_amount) > flt(sanctioned_amount_limit):
-			frappe.throw(_("Sanctioned Amount limit crossed for {0} {1}").format(self.applicant_type, frappe.bold(self.applicant)))
+		if sanctioned_amount_limit and flt(self.loan_amount) + flt(total_loan_amount) > flt(
+			sanctioned_amount_limit
+		):
+			frappe.throw(
+				_("Sanctioned Amount limit crossed for {0} {1}").format(
+					self.applicant_type, frappe.bold(self.applicant)
+				)
+			)
 
 	def set_pledge_amount(self):
 		for proposed_pledge in self.proposed_pledges:
@@ -71,26 +90,31 @@
 			proposed_pledge.loan_security_price = get_loan_security_price(proposed_pledge.loan_security)
 
 			if not proposed_pledge.qty:
-				proposed_pledge.qty = cint(proposed_pledge.amount/proposed_pledge.loan_security_price)
+				proposed_pledge.qty = cint(proposed_pledge.amount / proposed_pledge.loan_security_price)
 
 			proposed_pledge.amount = proposed_pledge.qty * proposed_pledge.loan_security_price
-			proposed_pledge.post_haircut_amount = cint(proposed_pledge.amount - (proposed_pledge.amount * proposed_pledge.haircut/100))
+			proposed_pledge.post_haircut_amount = cint(
+				proposed_pledge.amount - (proposed_pledge.amount * proposed_pledge.haircut / 100)
+			)
 
 	def get_repayment_details(self):
 
 		if self.is_term_loan:
 			if self.repayment_method == "Repay Over Number of Periods":
-				self.repayment_amount = get_monthly_repayment_amount(self.loan_amount, self.rate_of_interest, self.repayment_periods)
+				self.repayment_amount = get_monthly_repayment_amount(
+					self.loan_amount, self.rate_of_interest, self.repayment_periods
+				)
 
 			if self.repayment_method == "Repay Fixed Amount per Period":
-				monthly_interest_rate = flt(self.rate_of_interest) / (12 *100)
+				monthly_interest_rate = flt(self.rate_of_interest) / (12 * 100)
 				if monthly_interest_rate:
-					min_repayment_amount = self.loan_amount*monthly_interest_rate
+					min_repayment_amount = self.loan_amount * monthly_interest_rate
 					if self.repayment_amount - min_repayment_amount <= 0:
-						frappe.throw(_("Repayment Amount must be greater than " \
-							+ str(flt(min_repayment_amount, 2))))
-					self.repayment_periods = math.ceil((math.log(self.repayment_amount) -
-						math.log(self.repayment_amount - min_repayment_amount)) /(math.log(1 + monthly_interest_rate)))
+						frappe.throw(_("Repayment Amount must be greater than " + str(flt(min_repayment_amount, 2))))
+					self.repayment_periods = math.ceil(
+						(math.log(self.repayment_amount) - math.log(self.repayment_amount - min_repayment_amount))
+						/ (math.log(1 + monthly_interest_rate))
+					)
 				else:
 					self.repayment_periods = self.loan_amount / self.repayment_amount
 
@@ -103,8 +127,8 @@
 		self.total_payable_amount = 0
 		self.total_payable_interest = 0
 
-		while(balance_amount > 0):
-			interest_amount = rounded(balance_amount * flt(self.rate_of_interest) / (12*100))
+		while balance_amount > 0:
+			interest_amount = rounded(balance_amount * flt(self.rate_of_interest) / (12 * 100))
 			balance_amount = rounded(balance_amount + interest_amount - self.repayment_amount)
 
 			self.total_payable_interest += interest_amount
@@ -123,12 +147,21 @@
 		if not self.loan_amount and self.is_secured_loan and self.proposed_pledges:
 			self.loan_amount = self.maximum_loan_amount
 
+
 @frappe.whitelist()
 def create_loan(source_name, target_doc=None, submit=0):
 	def update_accounts(source_doc, target_doc, source_parent):
-		account_details = frappe.get_all("Loan Type",
-			fields=["mode_of_payment", "payment_account","loan_account", "interest_income_account", "penalty_income_account"],
-			filters = {'name': source_doc.loan_type})[0]
+		account_details = frappe.get_all(
+			"Loan Type",
+			fields=[
+				"mode_of_payment",
+				"payment_account",
+				"loan_account",
+				"interest_income_account",
+				"penalty_income_account",
+			],
+			filters={"name": source_doc.loan_type},
+		)[0]
 
 		if source_doc.is_secured_loan:
 			target_doc.maximum_loan_amount = 0
@@ -140,22 +173,25 @@
 		target_doc.penalty_income_account = account_details.penalty_income_account
 		target_doc.loan_application = source_name
 
-
-	doclist = get_mapped_doc("Loan Application", source_name, {
-		"Loan Application": {
-			"doctype": "Loan",
-			"validation": {
-				"docstatus": ["=", 1]
-			},
-			"postprocess": update_accounts
-		}
-	}, target_doc)
+	doclist = get_mapped_doc(
+		"Loan Application",
+		source_name,
+		{
+			"Loan Application": {
+				"doctype": "Loan",
+				"validation": {"docstatus": ["=", 1]},
+				"postprocess": update_accounts,
+			}
+		},
+		target_doc,
+	)
 
 	if submit:
 		doclist.submit()
 
 	return doclist
 
+
 @frappe.whitelist()
 def create_pledge(loan_application, loan=None):
 	loan_application_doc = frappe.get_doc("Loan Application", loan_application)
@@ -171,12 +207,15 @@
 
 	for pledge in loan_application_doc.proposed_pledges:
 
-		lsp.append('securities', {
-			"loan_security": pledge.loan_security,
-			"qty": pledge.qty,
-			"loan_security_price": pledge.loan_security_price,
-			"haircut": pledge.haircut
-		})
+		lsp.append(
+			"securities",
+			{
+				"loan_security": pledge.loan_security,
+				"qty": pledge.qty,
+				"loan_security_price": pledge.loan_security_price,
+				"haircut": pledge.haircut,
+			},
+		)
 
 	lsp.save()
 	lsp.submit()
@@ -186,15 +225,14 @@
 
 	return lsp.name
 
-#This is a sandbox method to get the proposed pledges
+
+# This is a sandbox method to get the proposed pledges
 @frappe.whitelist()
 def get_proposed_pledge(securities):
 	if isinstance(securities, str):
 		securities = json.loads(securities)
 
-	proposed_pledges = {
-		'securities': []
-	}
+	proposed_pledges = {"securities": []}
 	maximum_loan_amount = 0
 
 	for security in securities:
@@ -205,15 +243,15 @@
 		security.loan_security_price = get_loan_security_price(security.loan_security)
 
 		if not security.qty:
-			security.qty = cint(security.amount/security.loan_security_price)
+			security.qty = cint(security.amount / security.loan_security_price)
 
 		security.amount = security.qty * security.loan_security_price
-		security.post_haircut_amount = cint(security.amount - (security.amount * security.haircut/100))
+		security.post_haircut_amount = cint(security.amount - (security.amount * security.haircut / 100))
 
 		maximum_loan_amount += security.post_haircut_amount
 
-		proposed_pledges['securities'].append(security)
+		proposed_pledges["securities"].append(security)
 
-	proposed_pledges['maximum_loan_amount'] = maximum_loan_amount
+	proposed_pledges["maximum_loan_amount"] = maximum_loan_amount
 
 	return proposed_pledges
diff --git a/erpnext/loan_management/doctype/loan_application/loan_application_dashboard.py b/erpnext/loan_management/doctype/loan_application/loan_application_dashboard.py
index e8e2a2a..1d90e9b 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application_dashboard.py
+++ b/erpnext/loan_management/doctype/loan_application/loan_application_dashboard.py
@@ -1,9 +1,7 @@
 def get_data():
-     return {
-        'fieldname': 'loan_application',
-        'transactions': [
-            {
-                'items': ['Loan', 'Loan Security Pledge']
-            },
-        ],
-    }
+	return {
+		"fieldname": "loan_application",
+		"transactions": [
+			{"items": ["Loan", "Loan Security Pledge"]},
+		],
+	}
diff --git a/erpnext/loan_management/doctype/loan_application/test_loan_application.py b/erpnext/loan_management/doctype/loan_application/test_loan_application.py
index 640709c..2a4bb88 100644
--- a/erpnext/loan_management/doctype/loan_application/test_loan_application.py
+++ b/erpnext/loan_management/doctype/loan_application/test_loan_application.py
@@ -15,27 +15,45 @@
 class TestLoanApplication(unittest.TestCase):
 	def setUp(self):
 		create_loan_accounts()
-		create_loan_type("Home Loan", 500000, 9.2, 0, 1, 0, 'Cash', 'Disbursement Account - _TC', 'Payment Account - _TC', 'Loan Account - _TC',
-			'Interest Income Account - _TC', 'Penalty Income Account - _TC', 'Repay Over Number of Periods', 18)
+		create_loan_type(
+			"Home Loan",
+			500000,
+			9.2,
+			0,
+			1,
+			0,
+			"Cash",
+			"Disbursement Account - _TC",
+			"Payment Account - _TC",
+			"Loan Account - _TC",
+			"Interest Income Account - _TC",
+			"Penalty Income Account - _TC",
+			"Repay Over Number of Periods",
+			18,
+		)
 		self.applicant = make_employee("kate_loan@loan.com", "_Test Company")
-		make_salary_structure("Test Salary Structure Loan", "Monthly", employee=self.applicant, currency='INR')
+		make_salary_structure(
+			"Test Salary Structure Loan", "Monthly", employee=self.applicant, currency="INR"
+		)
 		self.create_loan_application()
 
 	def create_loan_application(self):
 		loan_application = frappe.new_doc("Loan Application")
-		loan_application.update({
-			"applicant": self.applicant,
-			"loan_type": "Home Loan",
-			"rate_of_interest": 9.2,
-			"loan_amount": 250000,
-			"repayment_method": "Repay Over Number of Periods",
-			"repayment_periods": 18,
-			"company": "_Test Company"
-		})
+		loan_application.update(
+			{
+				"applicant": self.applicant,
+				"loan_type": "Home Loan",
+				"rate_of_interest": 9.2,
+				"loan_amount": 250000,
+				"repayment_method": "Repay Over Number of Periods",
+				"repayment_periods": 18,
+				"company": "_Test Company",
+			}
+		)
 		loan_application.insert()
 
 	def test_loan_totals(self):
-		loan_application = frappe.get_doc("Loan Application", {"applicant":self.applicant})
+		loan_application = frappe.get_doc("Loan Application", {"applicant": self.applicant})
 
 		self.assertEqual(loan_application.total_payable_interest, 18599)
 		self.assertEqual(loan_application.total_payable_amount, 268599)
diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
index 54a03b9..0c2042b 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
+++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
@@ -18,7 +18,6 @@
 
 
 class LoanDisbursement(AccountsController):
-
 	def validate(self):
 		self.set_missing_values()
 		self.validate_disbursal_amount()
@@ -30,7 +29,7 @@
 	def on_cancel(self):
 		self.set_status_and_amounts(cancel=1)
 		self.make_gl_entries(cancel=1)
-		self.ignore_linked_doctypes = ['GL Entry']
+		self.ignore_linked_doctypes = ["GL Entry", "Payment Ledger Entry"]
 
 	def set_missing_values(self):
 		if not self.disbursement_date:
@@ -49,21 +48,36 @@
 			frappe.throw(_("Disbursed Amount cannot be greater than {0}").format(possible_disbursal_amount))
 
 	def set_status_and_amounts(self, cancel=0):
-		loan_details = frappe.get_all("Loan",
-			fields = ["loan_amount", "disbursed_amount", "total_payment", "total_principal_paid", "total_interest_payable",
-				"status", "is_term_loan", "is_secured_loan"], filters= { "name": self.against_loan })[0]
+		loan_details = frappe.get_all(
+			"Loan",
+			fields=[
+				"loan_amount",
+				"disbursed_amount",
+				"total_payment",
+				"total_principal_paid",
+				"total_interest_payable",
+				"status",
+				"is_term_loan",
+				"is_secured_loan",
+			],
+			filters={"name": self.against_loan},
+		)[0]
 
 		if cancel:
 			disbursed_amount, status, total_payment = self.get_values_on_cancel(loan_details)
 		else:
 			disbursed_amount, status, total_payment = self.get_values_on_submit(loan_details)
 
-		frappe.db.set_value("Loan", self.against_loan, {
-			"disbursement_date": self.disbursement_date,
-			"disbursed_amount": disbursed_amount,
-			"status": status,
-			"total_payment": total_payment
-		})
+		frappe.db.set_value(
+			"Loan",
+			self.against_loan,
+			{
+				"disbursement_date": self.disbursement_date,
+				"disbursed_amount": disbursed_amount,
+				"status": status,
+				"total_payment": total_payment,
+			},
+		)
 
 	def get_values_on_cancel(self, loan_details):
 		disbursed_amount = loan_details.disbursed_amount - self.disbursed_amount
@@ -91,8 +105,11 @@
 		total_payment = loan_details.total_payment
 
 		if loan_details.status in ("Disbursed", "Partially Disbursed") and not loan_details.is_term_loan:
-			process_loan_interest_accrual_for_demand_loans(posting_date=add_days(self.disbursement_date, -1),
-				loan=self.against_loan, accrual_type="Disbursement")
+			process_loan_interest_accrual_for_demand_loans(
+				posting_date=add_days(self.disbursement_date, -1),
+				loan=self.against_loan,
+				accrual_type="Disbursement",
+			)
 
 		if disbursed_amount > loan_details.loan_amount:
 			topup_amount = disbursed_amount - loan_details.loan_amount
@@ -116,72 +133,96 @@
 		gle_map = []
 
 		gle_map.append(
-			self.get_gl_dict({
-				"account": self.loan_account,
-				"against": self.disbursement_account,
-				"debit": self.disbursed_amount,
-				"debit_in_account_currency": self.disbursed_amount,
-				"against_voucher_type": "Loan",
-				"against_voucher": self.against_loan,
-				"remarks": _("Disbursement against loan:") + self.against_loan,
-				"cost_center": self.cost_center,
-				"party_type": self.applicant_type,
-				"party": self.applicant,
-				"posting_date": self.disbursement_date
-			})
+			self.get_gl_dict(
+				{
+					"account": self.loan_account,
+					"against": self.disbursement_account,
+					"debit": self.disbursed_amount,
+					"debit_in_account_currency": self.disbursed_amount,
+					"against_voucher_type": "Loan",
+					"against_voucher": self.against_loan,
+					"remarks": _("Disbursement against loan:") + self.against_loan,
+					"cost_center": self.cost_center,
+					"party_type": self.applicant_type,
+					"party": self.applicant,
+					"posting_date": self.disbursement_date,
+				}
+			)
 		)
 
 		gle_map.append(
-			self.get_gl_dict({
-				"account": self.disbursement_account,
-				"against": self.loan_account,
-				"credit": self.disbursed_amount,
-				"credit_in_account_currency": self.disbursed_amount,
-				"against_voucher_type": "Loan",
-				"against_voucher": self.against_loan,
-				"remarks": _("Disbursement against loan:") + self.against_loan,
-				"cost_center": self.cost_center,
-				"posting_date": self.disbursement_date
-			})
+			self.get_gl_dict(
+				{
+					"account": self.disbursement_account,
+					"against": self.loan_account,
+					"credit": self.disbursed_amount,
+					"credit_in_account_currency": self.disbursed_amount,
+					"against_voucher_type": "Loan",
+					"against_voucher": self.against_loan,
+					"remarks": _("Disbursement against loan:") + self.against_loan,
+					"cost_center": self.cost_center,
+					"posting_date": self.disbursement_date,
+				}
+			)
 		)
 
 		if gle_map:
 			make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj)
 
+
 def get_total_pledged_security_value(loan):
 	update_time = get_datetime()
 
-	loan_security_price_map = frappe._dict(frappe.get_all("Loan Security Price",
-		fields=["loan_security", "loan_security_price"],
-		filters = {
-			"valid_from": ("<=", update_time),
-			"valid_upto": (">=", update_time)
-		}, as_list=1))
+	loan_security_price_map = frappe._dict(
+		frappe.get_all(
+			"Loan Security Price",
+			fields=["loan_security", "loan_security_price"],
+			filters={"valid_from": ("<=", update_time), "valid_upto": (">=", update_time)},
+			as_list=1,
+		)
+	)
 
-	hair_cut_map = frappe._dict(frappe.get_all('Loan Security',
-		fields=["name", "haircut"], as_list=1))
+	hair_cut_map = frappe._dict(
+		frappe.get_all("Loan Security", fields=["name", "haircut"], as_list=1)
+	)
 
 	security_value = 0.0
 	pledged_securities = get_pledged_security_qty(loan)
 
 	for security, qty in pledged_securities.items():
 		after_haircut_percentage = 100 - hair_cut_map.get(security)
-		security_value += (loan_security_price_map.get(security) * qty * after_haircut_percentage)/100
+		security_value += (loan_security_price_map.get(security) * qty * after_haircut_percentage) / 100
 
 	return security_value
 
+
 @frappe.whitelist()
 def get_disbursal_amount(loan, on_current_security_price=0):
 	from erpnext.loan_management.doctype.loan_repayment.loan_repayment import (
 		get_pending_principal_amount,
 	)
 
-	loan_details = frappe.get_value("Loan", loan, ["loan_amount", "disbursed_amount", "total_payment",
-		"total_principal_paid", "total_interest_payable", "status", "is_term_loan", "is_secured_loan",
-		"maximum_loan_amount", "written_off_amount"], as_dict=1)
+	loan_details = frappe.get_value(
+		"Loan",
+		loan,
+		[
+			"loan_amount",
+			"disbursed_amount",
+			"total_payment",
+			"total_principal_paid",
+			"total_interest_payable",
+			"status",
+			"is_term_loan",
+			"is_secured_loan",
+			"maximum_loan_amount",
+			"written_off_amount",
+		],
+		as_dict=1,
+	)
 
-	if loan_details.is_secured_loan and frappe.get_all('Loan Security Shortfall', filters={'loan': loan,
-		'status': 'Pending'}):
+	if loan_details.is_secured_loan and frappe.get_all(
+		"Loan Security Shortfall", filters={"loan": loan, "status": "Pending"}
+	):
 		return 0
 
 	pending_principal_amount = get_pending_principal_amount(loan_details)
@@ -198,10 +239,14 @@
 
 	disbursal_amount = flt(security_value) - flt(pending_principal_amount)
 
-	if loan_details.is_term_loan and (disbursal_amount + loan_details.loan_amount) > loan_details.loan_amount:
+	if (
+		loan_details.is_term_loan
+		and (disbursal_amount + loan_details.loan_amount) > loan_details.loan_amount
+	):
 		disbursal_amount = loan_details.loan_amount - loan_details.disbursed_amount
 
 	return disbursal_amount
 
+
 def get_maximum_amount_as_per_pledged_security(loan):
-	return flt(frappe.db.get_value('Loan Security Pledge', {'loan': loan}, 'sum(maximum_loan_value)'))
+	return flt(frappe.db.get_value("Loan Security Pledge", {"loan": loan}, "sum(maximum_loan_value)"))
diff --git a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
index 10be750..4daa2ed 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
+++ b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
@@ -40,34 +40,50 @@
 
 
 class TestLoanDisbursement(unittest.TestCase):
-
 	def setUp(self):
 		create_loan_accounts()
 
-		create_loan_type("Demand Loan", 2000000, 13.5, 25, 0, 5, 'Cash', 'Disbursement Account - _TC',
-			'Payment Account - _TC', 'Loan Account - _TC', 'Interest Income Account - _TC', 'Penalty Income Account - _TC')
+		create_loan_type(
+			"Demand Loan",
+			2000000,
+			13.5,
+			25,
+			0,
+			5,
+			"Cash",
+			"Disbursement Account - _TC",
+			"Payment Account - _TC",
+			"Loan Account - _TC",
+			"Interest Income Account - _TC",
+			"Penalty Income Account - _TC",
+		)
 
 		create_loan_security_type()
 		create_loan_security()
 
-		create_loan_security_price("Test Security 1", 500, "Nos", get_datetime() , get_datetime(add_to_date(nowdate(), hours=24)))
-		create_loan_security_price("Test Security 2", 250, "Nos", get_datetime() , get_datetime(add_to_date(nowdate(), hours=24)))
+		create_loan_security_price(
+			"Test Security 1", 500, "Nos", get_datetime(), get_datetime(add_to_date(nowdate(), hours=24))
+		)
+		create_loan_security_price(
+			"Test Security 2", 250, "Nos", get_datetime(), get_datetime(add_to_date(nowdate(), hours=24))
+		)
 
 		if not frappe.db.exists("Customer", "_Test Loan Customer"):
-			frappe.get_doc(get_customer_dict('_Test Loan Customer')).insert(ignore_permissions=True)
+			frappe.get_doc(get_customer_dict("_Test Loan Customer")).insert(ignore_permissions=True)
 
-		self.applicant = frappe.db.get_value("Customer", {'name': '_Test Loan Customer'}, 'name')
+		self.applicant = frappe.db.get_value("Customer", {"name": "_Test Loan Customer"}, "name")
 
 	def test_loan_topup(self):
-		pledge = [{
-			"loan_security": "Test Security 1",
-			"qty": 4000.00
-		}]
+		pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}]
 
-		loan_application = create_loan_application('_Test Company', self.applicant, 'Demand Loan', pledge)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant, "Demand Loan", pledge
+		)
 		create_pledge(loan_application)
 
-		loan = create_demand_loan(self.applicant, "Demand Loan", loan_application, posting_date=get_first_day(nowdate()))
+		loan = create_demand_loan(
+			self.applicant, "Demand Loan", loan_application, posting_date=get_first_day(nowdate())
+		)
 
 		loan.submit()
 
@@ -76,18 +92,22 @@
 
 		no_of_days = date_diff(last_date, first_date) + 1
 
-		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
-			/ (days_in_year(get_datetime().year) * 100)
+		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) / (
+			days_in_year(get_datetime().year) * 100
+		)
 
 		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
 
 		process_loan_interest_accrual_for_demand_loans(posting_date=add_days(last_date, 1))
 
 		# Should not be able to create loan disbursement entry before repayment
-		self.assertRaises(frappe.ValidationError, make_loan_disbursement_entry, loan.name,
-			500000, first_date)
+		self.assertRaises(
+			frappe.ValidationError, make_loan_disbursement_entry, loan.name, 500000, first_date
+		)
 
-		repayment_entry = create_repayment_entry(loan.name, self.applicant, add_days(get_last_day(nowdate()), 5), 611095.89)
+		repayment_entry = create_repayment_entry(
+			loan.name, self.applicant, add_days(get_last_day(nowdate()), 5), 611095.89
+		)
 
 		repayment_entry.submit()
 		loan.reload()
@@ -96,49 +116,48 @@
 		make_loan_disbursement_entry(loan.name, 500000, disbursement_date=add_days(last_date, 16))
 
 		# check for disbursement accrual
-		loan_interest_accrual = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name,
-			'accrual_type': 'Disbursement'})
+		loan_interest_accrual = frappe.db.get_value(
+			"Loan Interest Accrual", {"loan": loan.name, "accrual_type": "Disbursement"}
+		)
 
 		self.assertTrue(loan_interest_accrual)
 
 	def test_loan_topup_with_additional_pledge(self):
-		pledge = [{
-			"loan_security": "Test Security 1",
-			"qty": 4000.00
-		}]
+		pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}]
 
-		loan_application = create_loan_application('_Test Company', self.applicant, 'Demand Loan', pledge)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant, "Demand Loan", pledge
+		)
 		create_pledge(loan_application)
 
-		loan = create_demand_loan(self.applicant, "Demand Loan", loan_application, posting_date='2019-10-01')
+		loan = create_demand_loan(
+			self.applicant, "Demand Loan", loan_application, posting_date="2019-10-01"
+		)
 		loan.submit()
 
 		self.assertEqual(loan.loan_amount, 1000000)
 
-		first_date = '2019-10-01'
-		last_date = '2019-10-30'
+		first_date = "2019-10-01"
+		last_date = "2019-10-30"
 
 		# Disbursed 10,00,000 amount
 		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
-		process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+		process_loan_interest_accrual_for_demand_loans(posting_date=last_date)
 		amounts = calculate_amounts(loan.name, add_days(last_date, 1))
 
-		previous_interest = amounts['interest_amount']
+		previous_interest = amounts["interest_amount"]
 
-		pledge1 = [{
-			"loan_security": "Test Security 1",
-			"qty": 2000.00
-		}]
+		pledge1 = [{"loan_security": "Test Security 1", "qty": 2000.00}]
 
 		create_loan_security_pledge(self.applicant, pledge1, loan=loan.name)
 
 		# Topup 500000
 		make_loan_disbursement_entry(loan.name, 500000, disbursement_date=add_days(last_date, 1))
-		process_loan_interest_accrual_for_demand_loans(posting_date = add_days(last_date, 15))
+		process_loan_interest_accrual_for_demand_loans(posting_date=add_days(last_date, 15))
 		amounts = calculate_amounts(loan.name, add_days(last_date, 15))
 
-		per_day_interest = get_per_day_interest(1500000, 13.5, '2019-10-30')
+		per_day_interest = get_per_day_interest(1500000, 13.5, "2019-10-30")
 		interest = per_day_interest * 15
 
-		self.assertEqual(amounts['pending_principal_amount'], 1500000)
-		self.assertEqual(amounts['interest_amount'], flt(interest + previous_interest, 2))
+		self.assertEqual(amounts["pending_principal_amount"], 1500000)
+		self.assertEqual(amounts["interest_amount"], flt(interest + previous_interest, 2))
diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
index 1c800a0..0aeb448 100644
--- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
+++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
@@ -6,7 +6,6 @@
 from frappe import _
 from frappe.utils import add_days, cint, date_diff, flt, get_datetime, getdate, nowdate
 
-import erpnext
 from erpnext.accounts.general_ledger import make_gl_entries
 from erpnext.controllers.accounts_controller import AccountsController
 
@@ -33,45 +32,53 @@
 			self.update_is_accrued()
 
 		self.make_gl_entries(cancel=1)
-		self.ignore_linked_doctypes = ['GL Entry']
+		self.ignore_linked_doctypes = ["GL Entry", "Payment Ledger Entry"]
 
 	def update_is_accrued(self):
-		frappe.db.set_value('Repayment Schedule', self.repayment_schedule_name, 'is_accrued', 0)
+		frappe.db.set_value("Repayment Schedule", self.repayment_schedule_name, "is_accrued", 0)
 
 	def make_gl_entries(self, cancel=0, adv_adj=0):
 		gle_map = []
 
+		cost_center = frappe.db.get_value("Loan", self.loan, "cost_center")
+
 		if self.interest_amount:
 			gle_map.append(
-				self.get_gl_dict({
-					"account": self.loan_account,
-					"party_type": self.applicant_type,
-					"party": self.applicant,
-					"against": self.interest_income_account,
-					"debit": self.interest_amount,
-					"debit_in_account_currency": self.interest_amount,
-					"against_voucher_type": "Loan",
-					"against_voucher": self.loan,
-					"remarks": _("Interest accrued from {0} to {1} against loan: {2}").format(
-						self.last_accrual_date, self.posting_date, self.loan),
-					"cost_center": erpnext.get_default_cost_center(self.company),
-					"posting_date": self.posting_date
-				})
+				self.get_gl_dict(
+					{
+						"account": self.loan_account,
+						"party_type": self.applicant_type,
+						"party": self.applicant,
+						"against": self.interest_income_account,
+						"debit": self.interest_amount,
+						"debit_in_account_currency": self.interest_amount,
+						"against_voucher_type": "Loan",
+						"against_voucher": self.loan,
+						"remarks": _("Interest accrued from {0} to {1} against loan: {2}").format(
+							self.last_accrual_date, self.posting_date, self.loan
+						),
+						"cost_center": cost_center,
+						"posting_date": self.posting_date,
+					}
+				)
 			)
 
 			gle_map.append(
-				self.get_gl_dict({
-					"account": self.interest_income_account,
-					"against": self.loan_account,
-					"credit": self.interest_amount,
-					"credit_in_account_currency":  self.interest_amount,
-					"against_voucher_type": "Loan",
-					"against_voucher": self.loan,
-					"remarks": ("Interest accrued from {0} to {1} against loan: {2}").format(
-						self.last_accrual_date, self.posting_date, self.loan),
-					"cost_center": erpnext.get_default_cost_center(self.company),
-					"posting_date": self.posting_date
-				})
+				self.get_gl_dict(
+					{
+						"account": self.interest_income_account,
+						"against": self.loan_account,
+						"credit": self.interest_amount,
+						"credit_in_account_currency": self.interest_amount,
+						"against_voucher_type": "Loan",
+						"against_voucher": self.loan,
+						"remarks": ("Interest accrued from {0} to {1} against loan: {2}").format(
+							self.last_accrual_date, self.posting_date, self.loan
+						),
+						"cost_center": cost_center,
+						"posting_date": self.posting_date,
+					}
+				)
 			)
 
 		if gle_map:
@@ -81,7 +88,9 @@
 # For Eg: If Loan disbursement date is '01-09-2019' and disbursed amount is 1000000 and
 # rate of interest is 13.5 then first loan interest accural will be on '01-10-2019'
 # which means interest will be accrued for 30 days which should be equal to 11095.89
-def calculate_accrual_amount_for_demand_loans(loan, posting_date, process_loan_interest, accrual_type):
+def calculate_accrual_amount_for_demand_loans(
+	loan, posting_date, process_loan_interest, accrual_type
+):
 	from erpnext.loan_management.doctype.loan_repayment.loan_repayment import (
 		calculate_amounts,
 		get_pending_principal_amount,
@@ -95,51 +104,76 @@
 
 	pending_principal_amount = get_pending_principal_amount(loan)
 
-	interest_per_day = get_per_day_interest(pending_principal_amount, loan.rate_of_interest, posting_date)
+	interest_per_day = get_per_day_interest(
+		pending_principal_amount, loan.rate_of_interest, posting_date
+	)
 	payable_interest = interest_per_day * no_of_days
 
-	pending_amounts = calculate_amounts(loan.name, posting_date, payment_type='Loan Closure')
+	pending_amounts = calculate_amounts(loan.name, posting_date, payment_type="Loan Closure")
 
-	args = frappe._dict({
-		'loan': loan.name,
-		'applicant_type': loan.applicant_type,
-		'applicant': loan.applicant,
-		'interest_income_account': loan.interest_income_account,
-		'loan_account': loan.loan_account,
-		'pending_principal_amount': pending_principal_amount,
-		'interest_amount': payable_interest,
-		'total_pending_interest_amount': pending_amounts['interest_amount'],
-		'penalty_amount': pending_amounts['penalty_amount'],
-		'process_loan_interest': process_loan_interest,
-		'posting_date': posting_date,
-		'accrual_type': accrual_type
-	})
+	args = frappe._dict(
+		{
+			"loan": loan.name,
+			"applicant_type": loan.applicant_type,
+			"applicant": loan.applicant,
+			"interest_income_account": loan.interest_income_account,
+			"loan_account": loan.loan_account,
+			"pending_principal_amount": pending_principal_amount,
+			"interest_amount": payable_interest,
+			"total_pending_interest_amount": pending_amounts["interest_amount"],
+			"penalty_amount": pending_amounts["penalty_amount"],
+			"process_loan_interest": process_loan_interest,
+			"posting_date": posting_date,
+			"accrual_type": accrual_type,
+		}
+	)
 
 	if flt(payable_interest, precision) > 0.0:
 		make_loan_interest_accrual_entry(args)
 
-def make_accrual_interest_entry_for_demand_loans(posting_date, process_loan_interest, open_loans=None, loan_type=None, accrual_type="Regular"):
-	query_filters = {
-		"status": ('in', ['Disbursed', 'Partially Disbursed']),
-		"docstatus": 1
-	}
+
+def make_accrual_interest_entry_for_demand_loans(
+	posting_date, process_loan_interest, open_loans=None, loan_type=None, accrual_type="Regular"
+):
+	query_filters = {"status": ("in", ["Disbursed", "Partially Disbursed"]), "docstatus": 1}
 
 	if loan_type:
-		query_filters.update({
-			"loan_type": loan_type
-		})
+		query_filters.update({"loan_type": loan_type})
 
 	if not open_loans:
-		open_loans = frappe.get_all("Loan",
-			fields=["name", "total_payment", "total_amount_paid", "loan_account", "interest_income_account", "loan_amount",
-				"is_term_loan", "status", "disbursement_date", "disbursed_amount", "applicant_type", "applicant",
-				"rate_of_interest", "total_interest_payable", "written_off_amount", "total_principal_paid", "repayment_start_date"],
-			filters=query_filters)
+		open_loans = frappe.get_all(
+			"Loan",
+			fields=[
+				"name",
+				"total_payment",
+				"total_amount_paid",
+				"loan_account",
+				"interest_income_account",
+				"loan_amount",
+				"is_term_loan",
+				"status",
+				"disbursement_date",
+				"disbursed_amount",
+				"applicant_type",
+				"applicant",
+				"rate_of_interest",
+				"total_interest_payable",
+				"written_off_amount",
+				"total_principal_paid",
+				"repayment_start_date",
+			],
+			filters=query_filters,
+		)
 
 	for loan in open_loans:
-		calculate_accrual_amount_for_demand_loans(loan, posting_date, process_loan_interest, accrual_type)
+		calculate_accrual_amount_for_demand_loans(
+			loan, posting_date, process_loan_interest, accrual_type
+		)
 
-def make_accrual_interest_entry_for_term_loans(posting_date, process_loan_interest, term_loan=None, loan_type=None, accrual_type="Regular"):
+
+def make_accrual_interest_entry_for_term_loans(
+	posting_date, process_loan_interest, term_loan=None, loan_type=None, accrual_type="Regular"
+):
 	curr_date = posting_date or add_days(nowdate(), 1)
 
 	term_loans = get_term_loans(curr_date, term_loan, loan_type)
@@ -148,37 +182,44 @@
 
 	for loan in term_loans:
 		accrued_entries.append(loan.payment_entry)
-		args = frappe._dict({
-			'loan': loan.name,
-			'applicant_type': loan.applicant_type,
-			'applicant': loan.applicant,
-			'interest_income_account': loan.interest_income_account,
-			'loan_account': loan.loan_account,
-			'interest_amount': loan.interest_amount,
-			'payable_principal': loan.principal_amount,
-			'process_loan_interest': process_loan_interest,
-			'repayment_schedule_name': loan.payment_entry,
-			'posting_date': posting_date,
-			'accrual_type': accrual_type
-		})
+		args = frappe._dict(
+			{
+				"loan": loan.name,
+				"applicant_type": loan.applicant_type,
+				"applicant": loan.applicant,
+				"interest_income_account": loan.interest_income_account,
+				"loan_account": loan.loan_account,
+				"interest_amount": loan.interest_amount,
+				"payable_principal": loan.principal_amount,
+				"process_loan_interest": process_loan_interest,
+				"repayment_schedule_name": loan.payment_entry,
+				"posting_date": posting_date,
+				"accrual_type": accrual_type,
+			}
+		)
 
 		make_loan_interest_accrual_entry(args)
 
 	if accrued_entries:
-		frappe.db.sql("""UPDATE `tabRepayment Schedule`
-			SET is_accrued = 1 where name in (%s)""" #nosec
-			% ", ".join(['%s']*len(accrued_entries)), tuple(accrued_entries))
+		frappe.db.sql(
+			"""UPDATE `tabRepayment Schedule`
+			SET is_accrued = 1 where name in (%s)"""  # nosec
+			% ", ".join(["%s"] * len(accrued_entries)),
+			tuple(accrued_entries),
+		)
+
 
 def get_term_loans(date, term_loan=None, loan_type=None):
-	condition = ''
+	condition = ""
 
 	if term_loan:
-		condition +=' AND l.name = %s' % frappe.db.escape(term_loan)
+		condition += " AND l.name = %s" % frappe.db.escape(term_loan)
 
 	if loan_type:
-		condition += ' AND l.loan_type = %s' % frappe.db.escape(loan_type)
+		condition += " AND l.loan_type = %s" % frappe.db.escape(loan_type)
 
-	term_loans = frappe.db.sql("""SELECT l.name, l.total_payment, l.total_amount_paid, l.loan_account,
+	term_loans = frappe.db.sql(
+		"""SELECT l.name, l.total_payment, l.total_amount_paid, l.loan_account,
 			l.interest_income_account, l.is_term_loan, l.disbursement_date, l.applicant_type, l.applicant,
 			l.rate_of_interest, l.total_interest_payable, l.repayment_start_date, rs.name as payment_entry,
 			rs.payment_date, rs.principal_amount, rs.interest_amount, rs.is_accrued , rs.balance_loan_amount
@@ -189,10 +230,16 @@
 			AND rs.payment_date <= %s
 			AND rs.is_accrued=0 {0}
 			AND l.status = 'Disbursed'
-			ORDER BY rs.payment_date""".format(condition), (getdate(date)), as_dict=1)
+			ORDER BY rs.payment_date""".format(
+			condition
+		),
+		(getdate(date)),
+		as_dict=1,
+	)
 
 	return term_loans
 
+
 def make_loan_interest_accrual_entry(args):
 	precision = cint(frappe.db.get_default("currency_precision")) or 2
 
@@ -204,7 +251,9 @@
 	loan_interest_accrual.loan_account = args.loan_account
 	loan_interest_accrual.pending_principal_amount = flt(args.pending_principal_amount, precision)
 	loan_interest_accrual.interest_amount = flt(args.interest_amount, precision)
-	loan_interest_accrual.total_pending_interest_amount = flt(args.total_pending_interest_amount, precision)
+	loan_interest_accrual.total_pending_interest_amount = flt(
+		args.total_pending_interest_amount, precision
+	)
 	loan_interest_accrual.penalty_amount = flt(args.penalty_amount, precision)
 	loan_interest_accrual.posting_date = args.posting_date or nowdate()
 	loan_interest_accrual.process_loan_interest_accrual = args.process_loan_interest
@@ -223,15 +272,20 @@
 
 	return no_of_days
 
+
 def get_last_accrual_date(loan):
-	last_posting_date = frappe.db.sql(""" SELECT MAX(posting_date) from `tabLoan Interest Accrual`
-		WHERE loan = %s and docstatus = 1""", (loan))
+	last_posting_date = frappe.db.sql(
+		""" SELECT MAX(posting_date) from `tabLoan Interest Accrual`
+		WHERE loan = %s and docstatus = 1""",
+		(loan),
+	)
 
 	if last_posting_date[0][0]:
 		# interest for last interest accrual date is already booked, so add 1 day
 		return add_days(last_posting_date[0][0], 1)
 	else:
-		return frappe.db.get_value('Loan', loan, 'disbursement_date')
+		return frappe.db.get_value("Loan", loan, "disbursement_date")
+
 
 def days_in_year(year):
 	days = 365
@@ -241,8 +295,11 @@
 
 	return days
 
+
 def get_per_day_interest(principal_amount, rate_of_interest, posting_date=None):
 	if not posting_date:
 		posting_date = getdate()
 
-	return flt((principal_amount * rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100))
+	return flt(
+		(principal_amount * rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100)
+	)
diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py
index e8c7750..fd59393 100644
--- a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py
+++ b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py
@@ -30,78 +30,98 @@
 	def setUp(self):
 		create_loan_accounts()
 
-		create_loan_type("Demand Loan", 2000000, 13.5, 25, 0, 5, 'Cash', 'Disbursement Account - _TC',
-			'Payment Account - _TC', 'Loan Account - _TC', 'Interest Income Account - _TC', 'Penalty Income Account - _TC')
+		create_loan_type(
+			"Demand Loan",
+			2000000,
+			13.5,
+			25,
+			0,
+			5,
+			"Cash",
+			"Disbursement Account - _TC",
+			"Payment Account - _TC",
+			"Loan Account - _TC",
+			"Interest Income Account - _TC",
+			"Penalty Income Account - _TC",
+		)
 
 		create_loan_security_type()
 		create_loan_security()
 
-		create_loan_security_price("Test Security 1", 500, "Nos", get_datetime() , get_datetime(add_to_date(nowdate(), hours=24)))
+		create_loan_security_price(
+			"Test Security 1", 500, "Nos", get_datetime(), get_datetime(add_to_date(nowdate(), hours=24))
+		)
 
 		if not frappe.db.exists("Customer", "_Test Loan Customer"):
-			frappe.get_doc(get_customer_dict('_Test Loan Customer')).insert(ignore_permissions=True)
+			frappe.get_doc(get_customer_dict("_Test Loan Customer")).insert(ignore_permissions=True)
 
-		self.applicant = frappe.db.get_value("Customer", {'name': '_Test Loan Customer'}, 'name')
+		self.applicant = frappe.db.get_value("Customer", {"name": "_Test Loan Customer"}, "name")
 
 	def test_loan_interest_accural(self):
-		pledge = [{
-			"loan_security": "Test Security 1",
-			"qty": 4000.00
-		}]
+		pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}]
 
-		loan_application = create_loan_application('_Test Company', self.applicant, 'Demand Loan', pledge)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant, "Demand Loan", pledge
+		)
 		create_pledge(loan_application)
-		loan = create_demand_loan(self.applicant, "Demand Loan", loan_application,
-			posting_date=get_first_day(nowdate()))
+		loan = create_demand_loan(
+			self.applicant, "Demand Loan", loan_application, posting_date=get_first_day(nowdate())
+		)
 		loan.submit()
 
-		first_date = '2019-10-01'
-		last_date = '2019-10-30'
+		first_date = "2019-10-01"
+		last_date = "2019-10-30"
 
 		no_of_days = date_diff(last_date, first_date) + 1
 
-		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
-			/ (days_in_year(get_datetime(first_date).year) * 100)
+		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) / (
+			days_in_year(get_datetime(first_date).year) * 100
+		)
 		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
 		process_loan_interest_accrual_for_demand_loans(posting_date=last_date)
-		loan_interest_accural = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name})
+		loan_interest_accural = frappe.get_doc("Loan Interest Accrual", {"loan": loan.name})
 
 		self.assertEqual(flt(loan_interest_accural.interest_amount, 0), flt(accrued_interest_amount, 0))
 
 	def test_accumulated_amounts(self):
-		pledge = [{
-			"loan_security": "Test Security 1",
-			"qty": 4000.00
-		}]
+		pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}]
 
-		loan_application = create_loan_application('_Test Company', self.applicant, 'Demand Loan', pledge)
+		loan_application = create_loan_application(
+			"_Test Company", self.applicant, "Demand Loan", pledge
+		)
 		create_pledge(loan_application)
-		loan = create_demand_loan(self.applicant, "Demand Loan", loan_application,
-			posting_date=get_first_day(nowdate()))
+		loan = create_demand_loan(
+			self.applicant, "Demand Loan", loan_application, posting_date=get_first_day(nowdate())
+		)
 		loan.submit()
 
-		first_date = '2019-10-01'
-		last_date = '2019-10-30'
+		first_date = "2019-10-01"
+		last_date = "2019-10-30"
 
 		no_of_days = date_diff(last_date, first_date) + 1
-		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
-			/ (days_in_year(get_datetime(first_date).year) * 100)
+		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) / (
+			days_in_year(get_datetime(first_date).year) * 100
+		)
 		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
 		process_loan_interest_accrual_for_demand_loans(posting_date=last_date)
-		loan_interest_accrual = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name})
+		loan_interest_accrual = frappe.get_doc("Loan Interest Accrual", {"loan": loan.name})
 
 		self.assertEqual(flt(loan_interest_accrual.interest_amount, 0), flt(accrued_interest_amount, 0))
 
-		next_start_date = '2019-10-31'
-		next_end_date = '2019-11-29'
+		next_start_date = "2019-10-31"
+		next_end_date = "2019-11-29"
 
 		no_of_days = date_diff(next_end_date, next_start_date) + 1
 		process = process_loan_interest_accrual_for_demand_loans(posting_date=next_end_date)
-		new_accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
-			/ (days_in_year(get_datetime(first_date).year) * 100)
+		new_accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) / (
+			days_in_year(get_datetime(first_date).year) * 100
+		)
 
 		total_pending_interest_amount = flt(accrued_interest_amount + new_accrued_interest_amount, 0)
 
-		loan_interest_accrual = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name,
-			'process_loan_interest_accrual': process})
-		self.assertEqual(flt(loan_interest_accrual.total_pending_interest_amount, 0), total_pending_interest_amount)
+		loan_interest_accrual = frappe.get_doc(
+			"Loan Interest Accrual", {"loan": loan.name, "process_loan_interest_accrual": process}
+		)
+		self.assertEqual(
+			flt(loan_interest_accrual.total_pending_interest_amount, 0), total_pending_interest_amount
+		)
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index 67c2b1e..51f40d9 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -22,7 +22,6 @@
 
 
 class LoanRepayment(AccountsController):
-
 	def validate(self):
 		amounts = calculate_amounts(self.against_loan, self.posting_date)
 		self.set_missing_values(amounts)
@@ -42,7 +41,7 @@
 		self.check_future_accruals()
 		self.update_repayment_schedule(cancel=1)
 		self.mark_as_unpaid()
-		self.ignore_linked_doctypes = ['GL Entry']
+		self.ignore_linked_doctypes = ["GL Entry", "Payment Ledger Entry"]
 		self.make_gl_entries(cancel=1)
 
 	def set_missing_values(self, amounts):
@@ -55,32 +54,38 @@
 			self.cost_center = erpnext.get_default_cost_center(self.company)
 
 		if not self.interest_payable:
-			self.interest_payable = flt(amounts['interest_amount'], precision)
+			self.interest_payable = flt(amounts["interest_amount"], precision)
 
 		if not self.penalty_amount:
-			self.penalty_amount = flt(amounts['penalty_amount'], precision)
+			self.penalty_amount = flt(amounts["penalty_amount"], precision)
 
 		if not self.pending_principal_amount:
-			self.pending_principal_amount = flt(amounts['pending_principal_amount'], precision)
+			self.pending_principal_amount = flt(amounts["pending_principal_amount"], precision)
 
 		if not self.payable_principal_amount and self.is_term_loan:
-			self.payable_principal_amount = flt(amounts['payable_principal_amount'], precision)
+			self.payable_principal_amount = flt(amounts["payable_principal_amount"], precision)
 
 		if not self.payable_amount:
-			self.payable_amount = flt(amounts['payable_amount'], precision)
+			self.payable_amount = flt(amounts["payable_amount"], precision)
 
-		shortfall_amount = flt(frappe.db.get_value('Loan Security Shortfall', {'loan': self.against_loan, 'status': 'Pending'},
-			'shortfall_amount'))
+		shortfall_amount = flt(
+			frappe.db.get_value(
+				"Loan Security Shortfall", {"loan": self.against_loan, "status": "Pending"}, "shortfall_amount"
+			)
+		)
 
 		if shortfall_amount:
 			self.shortfall_amount = shortfall_amount
 
-		if amounts.get('due_date'):
-			self.due_date = amounts.get('due_date')
+		if amounts.get("due_date"):
+			self.due_date = amounts.get("due_date")
 
 	def check_future_entries(self):
-		future_repayment_date = frappe.db.get_value("Loan Repayment", {"posting_date": (">", self.posting_date),
-			"docstatus": 1, "against_loan": self.against_loan}, 'posting_date')
+		future_repayment_date = frappe.db.get_value(
+			"Loan Repayment",
+			{"posting_date": (">", self.posting_date), "docstatus": 1, "against_loan": self.against_loan},
+			"posting_date",
+		)
 
 		if future_repayment_date:
 			frappe.throw("Repayment already made till date {0}".format(get_datetime(future_repayment_date)))
@@ -99,106 +104,168 @@
 				last_accrual_date = get_last_accrual_date(self.against_loan)
 
 				# get posting date upto which interest has to be accrued
-				per_day_interest = get_per_day_interest(self.pending_principal_amount,
-					self.rate_of_interest, self.posting_date)
+				per_day_interest = get_per_day_interest(
+					self.pending_principal_amount, self.rate_of_interest, self.posting_date
+				)
 
-				no_of_days = flt(flt(self.total_interest_paid - self.interest_payable,
-					precision)/per_day_interest, 0) - 1
+				no_of_days = (
+					flt(flt(self.total_interest_paid - self.interest_payable, precision) / per_day_interest, 0)
+					- 1
+				)
 
 				posting_date = add_days(last_accrual_date, no_of_days)
 
 				# book excess interest paid
-				process = process_loan_interest_accrual_for_demand_loans(posting_date=posting_date,
-					loan=self.against_loan, accrual_type="Repayment")
+				process = process_loan_interest_accrual_for_demand_loans(
+					posting_date=posting_date, loan=self.against_loan, accrual_type="Repayment"
+				)
 
 				# get loan interest accrual to update paid amount
-				lia = frappe.db.get_value('Loan Interest Accrual', {'process_loan_interest_accrual':
-					process}, ['name', 'interest_amount', 'payable_principal_amount'], as_dict=1)
+				lia = frappe.db.get_value(
+					"Loan Interest Accrual",
+					{"process_loan_interest_accrual": process},
+					["name", "interest_amount", "payable_principal_amount"],
+					as_dict=1,
+				)
 
 				if lia:
-					self.append('repayment_details', {
-						'loan_interest_accrual': lia.name,
-						'paid_interest_amount': flt(self.total_interest_paid - self.interest_payable, precision),
-						'paid_principal_amount': 0.0,
-						'accrual_type': 'Repayment'
-					})
+					self.append(
+						"repayment_details",
+						{
+							"loan_interest_accrual": lia.name,
+							"paid_interest_amount": flt(self.total_interest_paid - self.interest_payable, precision),
+							"paid_principal_amount": 0.0,
+							"accrual_type": "Repayment",
+						},
+					)
 
 	def update_paid_amount(self):
-		loan = frappe.get_value("Loan", self.against_loan, ['total_amount_paid', 'total_principal_paid',
-			'status', 'is_secured_loan', 'total_payment', 'loan_amount', 'disbursed_amount', 'total_interest_payable',
-			'written_off_amount'], as_dict=1)
+		loan = frappe.get_value(
+			"Loan",
+			self.against_loan,
+			[
+				"total_amount_paid",
+				"total_principal_paid",
+				"status",
+				"is_secured_loan",
+				"total_payment",
+				"loan_amount",
+				"disbursed_amount",
+				"total_interest_payable",
+				"written_off_amount",
+			],
+			as_dict=1,
+		)
 
-		loan.update({
-			'total_amount_paid': loan.total_amount_paid + self.amount_paid,
-			'total_principal_paid': loan.total_principal_paid + self.principal_amount_paid
-		})
+		loan.update(
+			{
+				"total_amount_paid": loan.total_amount_paid + self.amount_paid,
+				"total_principal_paid": loan.total_principal_paid + self.principal_amount_paid,
+			}
+		)
 
 		pending_principal_amount = get_pending_principal_amount(loan)
 		if not loan.is_secured_loan and pending_principal_amount <= 0:
-			loan.update({'status': 'Loan Closure Requested'})
+			loan.update({"status": "Loan Closure Requested"})
 
 		for payment in self.repayment_details:
-			frappe.db.sql(""" UPDATE `tabLoan Interest Accrual`
+			frappe.db.sql(
+				""" UPDATE `tabLoan Interest Accrual`
 				SET paid_principal_amount = `paid_principal_amount` + %s,
 					paid_interest_amount = `paid_interest_amount` + %s
 				WHERE name = %s""",
-				(flt(payment.paid_principal_amount), flt(payment.paid_interest_amount), payment.loan_interest_accrual))
+				(
+					flt(payment.paid_principal_amount),
+					flt(payment.paid_interest_amount),
+					payment.loan_interest_accrual,
+				),
+			)
 
-		frappe.db.sql(""" UPDATE `tabLoan`
+		frappe.db.sql(
+			""" UPDATE `tabLoan`
 			SET total_amount_paid = %s, total_principal_paid = %s, status = %s
-			WHERE name = %s """, (loan.total_amount_paid, loan.total_principal_paid, loan.status,
-			self.against_loan))
+			WHERE name = %s """,
+			(loan.total_amount_paid, loan.total_principal_paid, loan.status, self.against_loan),
+		)
 
 		update_shortfall_status(self.against_loan, self.principal_amount_paid)
 
 	def mark_as_unpaid(self):
-		loan = frappe.get_value("Loan", self.against_loan, ['total_amount_paid', 'total_principal_paid',
-			'status', 'is_secured_loan', 'total_payment', 'loan_amount', 'disbursed_amount', 'total_interest_payable',
-			'written_off_amount'], as_dict=1)
+		loan = frappe.get_value(
+			"Loan",
+			self.against_loan,
+			[
+				"total_amount_paid",
+				"total_principal_paid",
+				"status",
+				"is_secured_loan",
+				"total_payment",
+				"loan_amount",
+				"disbursed_amount",
+				"total_interest_payable",
+				"written_off_amount",
+			],
+			as_dict=1,
+		)
 
 		no_of_repayments = len(self.repayment_details)
 
-		loan.update({
-			'total_amount_paid': loan.total_amount_paid - self.amount_paid,
-			'total_principal_paid': loan.total_principal_paid - self.principal_amount_paid
-		})
+		loan.update(
+			{
+				"total_amount_paid": loan.total_amount_paid - self.amount_paid,
+				"total_principal_paid": loan.total_principal_paid - self.principal_amount_paid,
+			}
+		)
 
-		if loan.status == 'Loan Closure Requested':
+		if loan.status == "Loan Closure Requested":
 			if loan.disbursed_amount >= loan.loan_amount:
-				loan['status'] = 'Disbursed'
+				loan["status"] = "Disbursed"
 			else:
-				loan['status'] = 'Partially Disbursed'
+				loan["status"] = "Partially Disbursed"
 
 		for payment in self.repayment_details:
-			frappe.db.sql(""" UPDATE `tabLoan Interest Accrual`
+			frappe.db.sql(
+				""" UPDATE `tabLoan Interest Accrual`
 				SET paid_principal_amount = `paid_principal_amount` - %s,
 					paid_interest_amount = `paid_interest_amount` - %s
 				WHERE name = %s""",
-				(payment.paid_principal_amount, payment.paid_interest_amount, payment.loan_interest_accrual))
+				(payment.paid_principal_amount, payment.paid_interest_amount, payment.loan_interest_accrual),
+			)
 
 			# Cancel repayment interest accrual
 			# checking idx as a preventive measure, repayment accrual will always be the last entry
-			if payment.accrual_type == 'Repayment' and payment.idx == no_of_repayments:
-				lia_doc = frappe.get_doc('Loan Interest Accrual', payment.loan_interest_accrual)
+			if payment.accrual_type == "Repayment" and payment.idx == no_of_repayments:
+				lia_doc = frappe.get_doc("Loan Interest Accrual", payment.loan_interest_accrual)
 				lia_doc.cancel()
 
-		frappe.db.sql(""" UPDATE `tabLoan`
+		frappe.db.sql(
+			""" UPDATE `tabLoan`
 			SET total_amount_paid = %s, total_principal_paid = %s, status = %s
-			WHERE name = %s """, (loan.total_amount_paid, loan.total_principal_paid, loan.status, self.against_loan))
+			WHERE name = %s """,
+			(loan.total_amount_paid, loan.total_principal_paid, loan.status, self.against_loan),
+		)
 
 	def check_future_accruals(self):
-		future_accrual_date = frappe.db.get_value("Loan Interest Accrual", {"posting_date": (">", self.posting_date),
-			"docstatus": 1, "loan": self.against_loan}, 'posting_date')
+		future_accrual_date = frappe.db.get_value(
+			"Loan Interest Accrual",
+			{"posting_date": (">", self.posting_date), "docstatus": 1, "loan": self.against_loan},
+			"posting_date",
+		)
 
 		if future_accrual_date:
-			frappe.throw("Cannot cancel. Interest accruals already processed till {0}".format(get_datetime(future_accrual_date)))
+			frappe.throw(
+				"Cannot cancel. Interest accruals already processed till {0}".format(
+					get_datetime(future_accrual_date)
+				)
+			)
 
 	def update_repayment_schedule(self, cancel=0):
 		if self.is_term_loan and self.principal_amount_paid > self.payable_principal_amount:
 			regenerate_repayment_schedule(self.against_loan, cancel)
 
 	def allocate_amounts(self, repayment_details):
-		self.set('repayment_details', [])
+		precision = cint(frappe.db.get_default("currency_precision")) or 2
+		self.set("repayment_details", [])
 		self.principal_amount_paid = 0
 		self.total_penalty_paid = 0
 		interest_paid = self.amount_paid
@@ -212,9 +279,9 @@
 
 		if interest_paid > 0:
 			if self.penalty_amount and interest_paid > self.penalty_amount:
-				self.total_penalty_paid = self.penalty_amount
+				self.total_penalty_paid = flt(self.penalty_amount, precision)
 			elif self.penalty_amount:
-				self.total_penalty_paid = interest_paid
+				self.total_penalty_paid = flt(interest_paid, precision)
 
 			interest_paid -= self.total_penalty_paid
 
@@ -231,15 +298,15 @@
 		idx = 1
 
 		if interest_paid > 0:
-			for lia, amounts in repayment_details.get('pending_accrual_entries', []).items():
+			for lia, amounts in repayment_details.get("pending_accrual_entries", []).items():
 				interest_amount = 0
-				if amounts['interest_amount'] <= interest_paid:
-					interest_amount = amounts['interest_amount']
+				if amounts["interest_amount"] <= interest_paid:
+					interest_amount = amounts["interest_amount"]
 					self.total_interest_paid += interest_amount
 					interest_paid -= interest_amount
 				elif interest_paid:
-					if interest_paid >= amounts['interest_amount']:
-						interest_amount = amounts['interest_amount']
+					if interest_paid >= amounts["interest_amount"]:
+						interest_amount = amounts["interest_amount"]
 						self.total_interest_paid += interest_amount
 						interest_paid = 0
 					else:
@@ -248,27 +315,32 @@
 						interest_paid = 0
 
 				if interest_amount:
-					self.append('repayment_details', {
-						'loan_interest_accrual': lia,
-						'paid_interest_amount': interest_amount,
-						'paid_principal_amount': 0
-					})
+					self.append(
+						"repayment_details",
+						{
+							"loan_interest_accrual": lia,
+							"paid_interest_amount": interest_amount,
+							"paid_principal_amount": 0,
+						},
+					)
 					updated_entries[lia] = idx
 					idx += 1
 
 		return interest_paid, updated_entries
 
-	def allocate_principal_amount_for_term_loans(self, interest_paid, repayment_details, updated_entries):
+	def allocate_principal_amount_for_term_loans(
+		self, interest_paid, repayment_details, updated_entries
+	):
 		if interest_paid > 0:
-			for lia, amounts in repayment_details.get('pending_accrual_entries', []).items():
+			for lia, amounts in repayment_details.get("pending_accrual_entries", []).items():
 				paid_principal = 0
-				if amounts['payable_principal_amount'] <= interest_paid:
-					paid_principal = amounts['payable_principal_amount']
+				if amounts["payable_principal_amount"] <= interest_paid:
+					paid_principal = amounts["payable_principal_amount"]
 					self.principal_amount_paid += paid_principal
 					interest_paid -= paid_principal
 				elif interest_paid:
-					if interest_paid >= amounts['payable_principal_amount']:
-						paid_principal = amounts['payable_principal_amount']
+					if interest_paid >= amounts["payable_principal_amount"]:
+						paid_principal = amounts["payable_principal_amount"]
 						self.principal_amount_paid += paid_principal
 						interest_paid = 0
 					else:
@@ -278,30 +350,34 @@
 
 				if updated_entries.get(lia):
 					idx = updated_entries.get(lia)
-					self.get('repayment_details')[idx-1].paid_principal_amount += paid_principal
+					self.get("repayment_details")[idx - 1].paid_principal_amount += paid_principal
 				else:
-					self.append('repayment_details', {
-						'loan_interest_accrual': lia,
-						'paid_interest_amount': 0,
-						'paid_principal_amount': paid_principal
-					})
+					self.append(
+						"repayment_details",
+						{
+							"loan_interest_accrual": lia,
+							"paid_interest_amount": 0,
+							"paid_principal_amount": paid_principal,
+						},
+					)
 
 		if interest_paid > 0:
 			self.principal_amount_paid += interest_paid
 
 	def allocate_excess_payment_for_demand_loans(self, interest_paid, repayment_details):
-		if repayment_details['unaccrued_interest'] and interest_paid > 0:
+		if repayment_details["unaccrued_interest"] and interest_paid > 0:
 			# no of days for which to accrue interest
 			# Interest can only be accrued for an entire day and not partial
-			if interest_paid > repayment_details['unaccrued_interest']:
-				interest_paid -= repayment_details['unaccrued_interest']
-				self.total_interest_paid += repayment_details['unaccrued_interest']
+			if interest_paid > repayment_details["unaccrued_interest"]:
+				interest_paid -= repayment_details["unaccrued_interest"]
+				self.total_interest_paid += repayment_details["unaccrued_interest"]
 			else:
 				# get no of days for which interest can be paid
-				per_day_interest = get_per_day_interest(self.pending_principal_amount,
-					self.rate_of_interest, self.posting_date)
+				per_day_interest = get_per_day_interest(
+					self.pending_principal_amount, self.rate_of_interest, self.posting_date
+				)
 
-				no_of_days = cint(interest_paid/per_day_interest)
+				no_of_days = cint(interest_paid / per_day_interest)
 				self.total_interest_paid += no_of_days * per_day_interest
 				interest_paid -= no_of_days * per_day_interest
 
@@ -312,12 +388,13 @@
 		gle_map = []
 
 		if self.shortfall_amount and self.amount_paid > self.shortfall_amount:
-			remarks = _("Shortfall Repayment of {0}.\nRepayment against Loan: {1}").format(self.shortfall_amount,
-				self.against_loan)
+			remarks = _("Shortfall Repayment of {0}.<br>Repayment against Loan: {1}").format(
+				self.shortfall_amount, self.against_loan
+			)
 		elif self.shortfall_amount:
 			remarks = _("Shortfall Repayment of {0}").format(self.shortfall_amount)
 		else:
-			remarks = _("Repayment against Loan: ") + self.against_loan
+			remarks = _("Repayment against Loan:") + " " + self.against_loan
 
 		if self.repay_from_salary:
 			payment_account = self.payroll_payable_account
@@ -326,91 +403,111 @@
 
 		if self.total_penalty_paid:
 			gle_map.append(
-				self.get_gl_dict({
-					"account": self.loan_account,
-					"against": payment_account,
-					"debit": self.total_penalty_paid,
-					"debit_in_account_currency": self.total_penalty_paid,
-					"against_voucher_type": "Loan",
-					"against_voucher": self.against_loan,
-					"remarks": _("Penalty against loan:") + self.against_loan,
-					"cost_center": self.cost_center,
-					"party_type": self.applicant_type,
-					"party": self.applicant,
-					"posting_date": getdate(self.posting_date)
-				})
+				self.get_gl_dict(
+					{
+						"account": self.loan_account,
+						"against": payment_account,
+						"debit": self.total_penalty_paid,
+						"debit_in_account_currency": self.total_penalty_paid,
+						"against_voucher_type": "Loan",
+						"against_voucher": self.against_loan,
+						"remarks": _("Penalty against loan:") + self.against_loan,
+						"cost_center": self.cost_center,
+						"party_type": self.applicant_type,
+						"party": self.applicant,
+						"posting_date": getdate(self.posting_date),
+					}
+				)
 			)
 
 			gle_map.append(
-				self.get_gl_dict({
-					"account": self.penalty_income_account,
-					"against": self.loan_account,
-					"credit": self.total_penalty_paid,
-					"credit_in_account_currency": self.total_penalty_paid,
-					"against_voucher_type": "Loan",
-					"against_voucher": self.against_loan,
-					"remarks": _("Penalty against loan:") + self.against_loan,
-					"cost_center": self.cost_center,
-					"posting_date": getdate(self.posting_date)
-				})
+				self.get_gl_dict(
+					{
+						"account": self.penalty_income_account,
+						"against": self.loan_account,
+						"credit": self.total_penalty_paid,
+						"credit_in_account_currency": self.total_penalty_paid,
+						"against_voucher_type": "Loan",
+						"against_voucher": self.against_loan,
+						"remarks": _("Penalty against loan:") + self.against_loan,
+						"cost_center": self.cost_center,
+						"posting_date": getdate(self.posting_date),
+					}
+				)
 			)
 
 		gle_map.append(
-			self.get_gl_dict({
-				"account": payment_account,
-				"against": self.loan_account + ", " + self.penalty_income_account,
-				"debit": self.amount_paid,
-				"debit_in_account_currency": self.amount_paid,
-				"against_voucher_type": "Loan",
-				"against_voucher": self.against_loan,
-				"remarks": remarks,
-				"cost_center": self.cost_center,
-				"posting_date": getdate(self.posting_date),
-				"party_type": self.applicant_type if self.repay_from_salary else '',
-				"party": self.applicant if self.repay_from_salary else ''
-			})
+			self.get_gl_dict(
+				{
+					"account": payment_account,
+					"against": self.loan_account + ", " + self.penalty_income_account,
+					"debit": self.amount_paid,
+					"debit_in_account_currency": self.amount_paid,
+					"against_voucher_type": "Loan",
+					"against_voucher": self.against_loan,
+					"remarks": remarks,
+					"cost_center": self.cost_center,
+					"posting_date": getdate(self.posting_date),
+				}
+			)
 		)
 
 		gle_map.append(
-			self.get_gl_dict({
-				"account": self.loan_account,
-				"party_type": self.applicant_type,
-				"party": self.applicant,
-				"against": payment_account,
-				"credit": self.amount_paid,
-				"credit_in_account_currency": self.amount_paid,
-				"against_voucher_type": "Loan",
-				"against_voucher": self.against_loan,
-				"remarks": remarks,
-				"cost_center": self.cost_center,
-				"posting_date": getdate(self.posting_date)
-			})
+			self.get_gl_dict(
+				{
+					"account": self.loan_account,
+					"party_type": self.applicant_type,
+					"party": self.applicant,
+					"against": payment_account,
+					"credit": self.amount_paid,
+					"credit_in_account_currency": self.amount_paid,
+					"against_voucher_type": "Loan",
+					"against_voucher": self.against_loan,
+					"remarks": remarks,
+					"cost_center": self.cost_center,
+					"posting_date": getdate(self.posting_date),
+				}
+			)
 		)
 
 		if gle_map:
 			make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj, merge_entries=False)
 
-def create_repayment_entry(loan, applicant, company, posting_date, loan_type,
-	payment_type, interest_payable, payable_principal_amount, amount_paid, penalty_amount=None,
-	payroll_payable_account=None):
 
-	lr = frappe.get_doc({
-		"doctype": "Loan Repayment",
-		"against_loan": loan,
-		"payment_type": payment_type,
-		"company": company,
-		"posting_date": posting_date,
-		"applicant": applicant,
-		"penalty_amount": penalty_amount,
-		"interest_payable": interest_payable,
-		"payable_principal_amount": payable_principal_amount,
-		"amount_paid": amount_paid,
-		"loan_type": loan_type,
-		"payroll_payable_account": payroll_payable_account
-	}).insert()
+def create_repayment_entry(
+	loan,
+	applicant,
+	company,
+	posting_date,
+	loan_type,
+	payment_type,
+	interest_payable,
+	payable_principal_amount,
+	amount_paid,
+	penalty_amount=None,
+	payroll_payable_account=None,
+):
+
+	lr = frappe.get_doc(
+		{
+			"doctype": "Loan Repayment",
+			"against_loan": loan,
+			"payment_type": payment_type,
+			"company": company,
+			"posting_date": posting_date,
+			"applicant": applicant,
+			"penalty_amount": penalty_amount,
+			"interest_payable": interest_payable,
+			"payable_principal_amount": payable_principal_amount,
+			"amount_paid": amount_paid,
+			"loan_type": loan_type,
+			"payroll_payable_account": payroll_payable_account,
+		}
+	).insert()
 
 	return lr
 
+
 def get_accrued_interest_entries(against_loan, posting_date=None):
 	if not posting_date:
 		posting_date = getdate()
@@ -430,35 +527,43 @@
 			AND
 				docstatus = 1
 			ORDER BY posting_date
-		""", (against_loan, posting_date), as_dict=1)
+		""",
+		(against_loan, posting_date),
+		as_dict=1,
+	)
 
 	return unpaid_accrued_entries
 
+
 def get_penalty_details(against_loan):
-	penalty_details = frappe.db.sql("""
+	penalty_details = frappe.db.sql(
+		"""
 		SELECT posting_date, (penalty_amount - total_penalty_paid) as pending_penalty_amount
 		FROM `tabLoan Repayment` where posting_date >= (SELECT MAX(posting_date) from `tabLoan Repayment`
 		where against_loan = %s) and docstatus = 1 and against_loan = %s
-	""", (against_loan, against_loan))
+	""",
+		(against_loan, against_loan),
+	)
 
 	if penalty_details:
 		return penalty_details[0][0], flt(penalty_details[0][1])
 	else:
 		return None, 0
 
+
 def regenerate_repayment_schedule(loan, cancel=0):
 	from erpnext.loan_management.doctype.loan.loan import (
 		add_single_month,
 		get_monthly_repayment_amount,
 	)
 
-	loan_doc = frappe.get_doc('Loan', loan)
+	loan_doc = frappe.get_doc("Loan", loan)
 	next_accrual_date = None
 	accrued_entries = 0
 	last_repayment_amount = 0
 	last_balance_amount = 0
 
-	for term in reversed(loan_doc.get('repayment_schedule')):
+	for term in reversed(loan_doc.get("repayment_schedule")):
 		if not term.is_accrued:
 			next_accrual_date = term.payment_date
 			loan_doc.remove(term)
@@ -473,20 +578,24 @@
 
 	balance_amount = get_pending_principal_amount(loan_doc)
 
-	if loan_doc.repayment_method == 'Repay Fixed Amount per Period':
-		monthly_repayment_amount = flt(balance_amount/len(loan_doc.get('repayment_schedule')) - accrued_entries)
+	if loan_doc.repayment_method == "Repay Fixed Amount per Period":
+		monthly_repayment_amount = flt(
+			balance_amount / len(loan_doc.get("repayment_schedule")) - accrued_entries
+		)
 	else:
-		if not cancel:
-			monthly_repayment_amount = get_monthly_repayment_amount(balance_amount,
-				loan_doc.rate_of_interest, loan_doc.repayment_periods - accrued_entries)
+		repayment_period = loan_doc.repayment_periods - accrued_entries
+		if not cancel and repayment_period > 0:
+			monthly_repayment_amount = get_monthly_repayment_amount(
+				balance_amount, loan_doc.rate_of_interest, repayment_period
+			)
 		else:
 			monthly_repayment_amount = last_repayment_amount
 			balance_amount = last_balance_amount
 
 	payment_date = next_accrual_date
 
-	while(balance_amount > 0):
-		interest_amount = flt(balance_amount * flt(loan_doc.rate_of_interest) / (12*100))
+	while balance_amount > 0:
+		interest_amount = flt(balance_amount * flt(loan_doc.rate_of_interest) / (12 * 100))
 		principal_amount = monthly_repayment_amount - interest_amount
 		balance_amount = flt(balance_amount + interest_amount - monthly_repayment_amount)
 		if balance_amount < 0:
@@ -494,31 +603,45 @@
 			balance_amount = 0.0
 
 		total_payment = principal_amount + interest_amount
-		loan_doc.append("repayment_schedule", {
-			"payment_date": payment_date,
-			"principal_amount": principal_amount,
-			"interest_amount": interest_amount,
-			"total_payment": total_payment,
-			"balance_loan_amount": balance_amount
-		})
+		loan_doc.append(
+			"repayment_schedule",
+			{
+				"payment_date": payment_date,
+				"principal_amount": principal_amount,
+				"interest_amount": interest_amount,
+				"total_payment": total_payment,
+				"balance_loan_amount": balance_amount,
+			},
+		)
 		next_payment_date = add_single_month(payment_date)
 		payment_date = next_payment_date
 
 	loan_doc.save()
 
+
 def get_pending_principal_amount(loan):
-	if loan.status in ('Disbursed', 'Closed') or loan.disbursed_amount >= loan.loan_amount:
-		pending_principal_amount = flt(loan.total_payment) - flt(loan.total_principal_paid) \
-			- flt(loan.total_interest_payable) - flt(loan.written_off_amount)
+	if loan.status in ("Disbursed", "Closed") or loan.disbursed_amount >= loan.loan_amount:
+		pending_principal_amount = (
+			flt(loan.total_payment)
+			- flt(loan.total_principal_paid)
+			- flt(loan.total_interest_payable)
+			- flt(loan.written_off_amount)
+		)
 	else:
-		pending_principal_amount = flt(loan.disbursed_amount) - flt(loan.total_principal_paid) \
-			- flt(loan.total_interest_payable) - flt(loan.written_off_amount)
+		pending_principal_amount = (
+			flt(loan.disbursed_amount)
+			- flt(loan.total_principal_paid)
+			- flt(loan.total_interest_payable)
+			- flt(loan.written_off_amount)
+		)
 
 	return pending_principal_amount
 
+
 # This function returns the amounts that are payable at the time of loan repayment based on posting date
 # So it pulls all the unpaid Loan Interest Accrual Entries and calculates the penalty if applicable
 
+
 def get_amounts(amounts, against_loan, posting_date):
 	precision = cint(frappe.db.get_default("currency_precision")) or 2
 
@@ -532,8 +655,8 @@
 	total_pending_interest = 0
 	penalty_amount = 0
 	payable_principal_amount = 0
-	final_due_date = ''
-	due_date = ''
+	final_due_date = ""
+	due_date = ""
 
 	for entry in accrued_interest_entries:
 		# Loan repayment due date is one day after the loan interest is accrued
@@ -549,16 +672,25 @@
 
 		no_of_late_days = date_diff(posting_date, due_date_after_grace_period) + 1
 
-		if no_of_late_days > 0 and (not against_loan_doc.repay_from_salary) and entry.accrual_type == 'Regular':
-			penalty_amount += (entry.interest_amount * (loan_type_details.penalty_interest_rate / 100) * no_of_late_days)
+		if (
+			no_of_late_days > 0
+			and (not against_loan_doc.repay_from_salary)
+			and entry.accrual_type == "Regular"
+		):
+			penalty_amount += (
+				entry.interest_amount * (loan_type_details.penalty_interest_rate / 100) * no_of_late_days
+			)
 
 		total_pending_interest += entry.interest_amount
 		payable_principal_amount += entry.payable_principal_amount
 
-		pending_accrual_entries.setdefault(entry.name, {
-			'interest_amount': flt(entry.interest_amount, precision),
-			'payable_principal_amount': flt(entry.payable_principal_amount, precision)
-		})
+		pending_accrual_entries.setdefault(
+			entry.name,
+			{
+				"interest_amount": flt(entry.interest_amount, precision),
+				"payable_principal_amount": flt(entry.payable_principal_amount, precision),
+			},
+		)
 
 		if due_date and not final_due_date:
 			final_due_date = add_days(due_date, loan_type_details.grace_period_in_days)
@@ -574,14 +706,18 @@
 
 	if pending_days > 0:
 		principal_amount = flt(pending_principal_amount, precision)
-		per_day_interest = get_per_day_interest(principal_amount, loan_type_details.rate_of_interest, posting_date)
-		unaccrued_interest += (pending_days * per_day_interest)
+		per_day_interest = get_per_day_interest(
+			principal_amount, loan_type_details.rate_of_interest, posting_date
+		)
+		unaccrued_interest += pending_days * per_day_interest
 
 	amounts["pending_principal_amount"] = flt(pending_principal_amount, precision)
 	amounts["payable_principal_amount"] = flt(payable_principal_amount, precision)
 	amounts["interest_amount"] = flt(total_pending_interest, precision)
 	amounts["penalty_amount"] = flt(penalty_amount + pending_penalty_amount, precision)
-	amounts["payable_amount"] = flt(payable_principal_amount + total_pending_interest + penalty_amount, precision)
+	amounts["payable_amount"] = flt(
+		payable_principal_amount + total_pending_interest + penalty_amount, precision
+	)
 	amounts["pending_accrual_entries"] = pending_accrual_entries
 	amounts["unaccrued_interest"] = flt(unaccrued_interest, precision)
 
@@ -590,24 +726,27 @@
 
 	return amounts
 
+
 @frappe.whitelist()
-def calculate_amounts(against_loan, posting_date, payment_type=''):
+def calculate_amounts(against_loan, posting_date, payment_type=""):
 	amounts = {
-		'penalty_amount': 0.0,
-		'interest_amount': 0.0,
-		'pending_principal_amount': 0.0,
-		'payable_principal_amount': 0.0,
-		'payable_amount': 0.0,
-		'unaccrued_interest': 0.0,
-		'due_date': ''
+		"penalty_amount": 0.0,
+		"interest_amount": 0.0,
+		"pending_principal_amount": 0.0,
+		"payable_principal_amount": 0.0,
+		"payable_amount": 0.0,
+		"unaccrued_interest": 0.0,
+		"due_date": "",
 	}
 
 	amounts = get_amounts(amounts, against_loan, posting_date)
 
 	# update values for closure
-	if payment_type == 'Loan Closure':
-		amounts['payable_principal_amount'] = amounts['pending_principal_amount']
-		amounts['interest_amount'] += amounts['unaccrued_interest']
-		amounts['payable_amount'] = amounts['payable_principal_amount'] + amounts['interest_amount']
+	if payment_type == "Loan Closure":
+		amounts["payable_principal_amount"] = amounts["pending_principal_amount"]
+		amounts["interest_amount"] += amounts["unaccrued_interest"]
+		amounts["payable_amount"] = (
+			amounts["payable_principal_amount"] + amounts["interest_amount"] + amounts["penalty_amount"]
+		)
 
 	return amounts
diff --git a/erpnext/loan_management/doctype/loan_security/loan_security_dashboard.py b/erpnext/loan_management/doctype/loan_security/loan_security_dashboard.py
index 964a1ae..1d96885 100644
--- a/erpnext/loan_management/doctype/loan_security/loan_security_dashboard.py
+++ b/erpnext/loan_management/doctype/loan_security/loan_security_dashboard.py
@@ -1,12 +1,8 @@
 def get_data():
 	return {
-		'fieldname': 'loan_security',
-		'transactions': [
-			{
-				'items': ['Loan Application', 'Loan Security Price']
-			},
-			{
-				'items': ['Loan Security Pledge', 'Loan Security Unpledge']
-			}
-		]
+		"fieldname": "loan_security",
+		"transactions": [
+			{"items": ["Loan Application", "Loan Security Price"]},
+			{"items": ["Loan Security Pledge", "Loan Security Unpledge"]},
+		],
 	}
diff --git a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py
index 7d02645..f0d5954 100644
--- a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py
+++ b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py
@@ -40,22 +40,28 @@
 			if security.loan_security not in security_list:
 				security_list.append(security.loan_security)
 			else:
-				frappe.throw(_('Loan Security {0} added multiple times').format(frappe.bold(
-					security.loan_security)))
+				frappe.throw(
+					_("Loan Security {0} added multiple times").format(frappe.bold(security.loan_security))
+				)
 
 	def validate_loan_security_type(self):
-		existing_pledge = ''
+		existing_pledge = ""
 
 		if self.loan:
-			existing_pledge = frappe.db.get_value('Loan Security Pledge', {'loan': self.loan, 'docstatus': 1}, ['name'])
+			existing_pledge = frappe.db.get_value(
+				"Loan Security Pledge", {"loan": self.loan, "docstatus": 1}, ["name"]
+			)
 
 		if existing_pledge:
-			loan_security_type = frappe.db.get_value('Pledge', {'parent': existing_pledge}, ['loan_security_type'])
+			loan_security_type = frappe.db.get_value(
+				"Pledge", {"parent": existing_pledge}, ["loan_security_type"]
+			)
 		else:
 			loan_security_type = self.securities[0].loan_security_type
 
-		ltv_ratio_map = frappe._dict(frappe.get_all("Loan Security Type",
-			fields=["name", "loan_to_value_ratio"], as_list=1))
+		ltv_ratio_map = frappe._dict(
+			frappe.get_all("Loan Security Type", fields=["name", "loan_to_value_ratio"], as_list=1)
+		)
 
 		ltv_ratio = ltv_ratio_map.get(loan_security_type)
 
@@ -63,7 +69,6 @@
 			if ltv_ratio_map.get(security.loan_security_type) != ltv_ratio:
 				frappe.throw(_("Loan Securities with different LTV ratio cannot be pledged against one loan"))
 
-
 	def set_pledge_amount(self):
 		total_security_value = 0
 		maximum_loan_value = 0
@@ -77,10 +82,10 @@
 				pledge.loan_security_price = get_loan_security_price(pledge.loan_security)
 
 			if not pledge.qty:
-				pledge.qty = cint(pledge.amount/pledge.loan_security_price)
+				pledge.qty = cint(pledge.amount / pledge.loan_security_price)
 
 			pledge.amount = pledge.qty * pledge.loan_security_price
-			pledge.post_haircut_amount = cint(pledge.amount - (pledge.amount * pledge.haircut/100))
+			pledge.post_haircut_amount = cint(pledge.amount - (pledge.amount * pledge.haircut / 100))
 
 			total_security_value += pledge.amount
 			maximum_loan_value += pledge.post_haircut_amount
@@ -88,12 +93,19 @@
 		self.total_security_value = total_security_value
 		self.maximum_loan_value = maximum_loan_value
 
+
 def update_loan(loan, maximum_value_against_pledge, cancel=0):
-	maximum_loan_value = frappe.db.get_value('Loan', {'name': loan}, ['maximum_loan_amount'])
+	maximum_loan_value = frappe.db.get_value("Loan", {"name": loan}, ["maximum_loan_amount"])
 
 	if cancel:
-		frappe.db.sql(""" UPDATE `tabLoan` SET maximum_loan_amount=%s
-			WHERE name=%s""", (maximum_loan_value - maximum_value_against_pledge, loan))
+		frappe.db.sql(
+			""" UPDATE `tabLoan` SET maximum_loan_amount=%s
+			WHERE name=%s""",
+			(maximum_loan_value - maximum_value_against_pledge, loan),
+		)
 	else:
-		frappe.db.sql(""" UPDATE `tabLoan` SET maximum_loan_amount=%s, is_secured_loan=1
-			WHERE name=%s""", (maximum_loan_value + maximum_value_against_pledge, loan))
+		frappe.db.sql(
+			""" UPDATE `tabLoan` SET maximum_loan_amount=%s, is_secured_loan=1
+			WHERE name=%s""",
+			(maximum_loan_value + maximum_value_against_pledge, loan),
+		)
diff --git a/erpnext/loan_management/doctype/loan_security_price/loan_security_price.py b/erpnext/loan_management/doctype/loan_security_price/loan_security_price.py
index fca9dd6..45c4459 100644
--- a/erpnext/loan_management/doctype/loan_security_price/loan_security_price.py
+++ b/erpnext/loan_management/doctype/loan_security_price/loan_security_price.py
@@ -17,23 +17,37 @@
 		if self.valid_from > self.valid_upto:
 			frappe.throw(_("Valid From Time must be lesser than Valid Upto Time."))
 
-		existing_loan_security = frappe.db.sql(""" SELECT name from `tabLoan Security Price`
+		existing_loan_security = frappe.db.sql(
+			""" SELECT name from `tabLoan Security Price`
 			WHERE loan_security = %s AND name != %s AND (valid_from BETWEEN %s and %s OR valid_upto BETWEEN %s and %s) """,
-			(self.loan_security, self.name, self.valid_from, self.valid_upto, self.valid_from, self.valid_upto))
+			(
+				self.loan_security,
+				self.name,
+				self.valid_from,
+				self.valid_upto,
+				self.valid_from,
+				self.valid_upto,
+			),
+		)
 
 		if existing_loan_security:
 			frappe.throw(_("Loan Security Price overlapping with {0}").format(existing_loan_security[0][0]))
 
+
 @frappe.whitelist()
 def get_loan_security_price(loan_security, valid_time=None):
 	if not valid_time:
 		valid_time = get_datetime()
 
-	loan_security_price = frappe.db.get_value("Loan Security Price", {
-		'loan_security': loan_security,
-		'valid_from': ("<=",valid_time),
-		'valid_upto': (">=", valid_time)
-	}, 'loan_security_price')
+	loan_security_price = frappe.db.get_value(
+		"Loan Security Price",
+		{
+			"loan_security": loan_security,
+			"valid_from": ("<=", valid_time),
+			"valid_upto": (">=", valid_time),
+		},
+		"loan_security_price",
+	)
 
 	if not loan_security_price:
 		frappe.throw(_("No valid Loan Security Price found for {0}").format(frappe.bold(loan_security)))
diff --git a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
index 20e451b..b901e62 100644
--- a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
+++ b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
@@ -14,27 +14,42 @@
 class LoanSecurityShortfall(Document):
 	pass
 
+
 def update_shortfall_status(loan, security_value, on_cancel=0):
-	loan_security_shortfall = frappe.db.get_value("Loan Security Shortfall",
-		{"loan": loan, "status": "Pending"}, ['name', 'shortfall_amount'], as_dict=1)
+	loan_security_shortfall = frappe.db.get_value(
+		"Loan Security Shortfall",
+		{"loan": loan, "status": "Pending"},
+		["name", "shortfall_amount"],
+		as_dict=1,
+	)
 
 	if not loan_security_shortfall:
 		return
 
 	if security_value >= loan_security_shortfall.shortfall_amount:
-		frappe.db.set_value("Loan Security Shortfall", loan_security_shortfall.name, {
-			"status": "Completed",
-			"shortfall_amount": loan_security_shortfall.shortfall_amount,
-			"shortfall_percentage": 0
-		})
+		frappe.db.set_value(
+			"Loan Security Shortfall",
+			loan_security_shortfall.name,
+			{
+				"status": "Completed",
+				"shortfall_amount": loan_security_shortfall.shortfall_amount,
+				"shortfall_percentage": 0,
+			},
+		)
 	else:
-		frappe.db.set_value("Loan Security Shortfall", loan_security_shortfall.name,
-			"shortfall_amount", loan_security_shortfall.shortfall_amount - security_value)
+		frappe.db.set_value(
+			"Loan Security Shortfall",
+			loan_security_shortfall.name,
+			"shortfall_amount",
+			loan_security_shortfall.shortfall_amount - security_value,
+		)
 
 
 @frappe.whitelist()
 def add_security(loan):
-	loan_details = frappe.db.get_value("Loan", loan, ['applicant', 'company', 'applicant_type'], as_dict=1)
+	loan_details = frappe.db.get_value(
+		"Loan", loan, ["applicant", "company", "applicant_type"], as_dict=1
+	)
 
 	loan_security_pledge = frappe.new_doc("Loan Security Pledge")
 	loan_security_pledge.loan = loan
@@ -44,33 +59,51 @@
 
 	return loan_security_pledge.as_dict()
 
+
 def check_for_ltv_shortfall(process_loan_security_shortfall):
 
 	update_time = get_datetime()
 
-	loan_security_price_map = frappe._dict(frappe.get_all("Loan Security Price",
-		fields=["loan_security", "loan_security_price"],
-		filters = {
-			"valid_from": ("<=", update_time),
-			"valid_upto": (">=", update_time)
-		}, as_list=1))
+	loan_security_price_map = frappe._dict(
+		frappe.get_all(
+			"Loan Security Price",
+			fields=["loan_security", "loan_security_price"],
+			filters={"valid_from": ("<=", update_time), "valid_upto": (">=", update_time)},
+			as_list=1,
+		)
+	)
 
-	loans = frappe.get_all('Loan', fields=['name', 'loan_amount', 'total_principal_paid', 'total_payment',
-		'total_interest_payable', 'disbursed_amount', 'status'],
-		filters={'status': ('in',['Disbursed','Partially Disbursed']), 'is_secured_loan': 1})
+	loans = frappe.get_all(
+		"Loan",
+		fields=[
+			"name",
+			"loan_amount",
+			"total_principal_paid",
+			"total_payment",
+			"total_interest_payable",
+			"disbursed_amount",
+			"status",
+		],
+		filters={"status": ("in", ["Disbursed", "Partially Disbursed"]), "is_secured_loan": 1},
+	)
 
-	loan_shortfall_map = frappe._dict(frappe.get_all("Loan Security Shortfall",
-		fields=["loan", "name"], filters={"status": "Pending"}, as_list=1))
+	loan_shortfall_map = frappe._dict(
+		frappe.get_all(
+			"Loan Security Shortfall", fields=["loan", "name"], filters={"status": "Pending"}, as_list=1
+		)
+	)
 
 	loan_security_map = {}
 
 	for loan in loans:
-		if loan.status == 'Disbursed':
-			outstanding_amount = flt(loan.total_payment) - flt(loan.total_interest_payable) \
-				- flt(loan.total_principal_paid)
+		if loan.status == "Disbursed":
+			outstanding_amount = (
+				flt(loan.total_payment) - flt(loan.total_interest_payable) - flt(loan.total_principal_paid)
+			)
 		else:
-			outstanding_amount = flt(loan.disbursed_amount) - flt(loan.total_interest_payable) \
-				- flt(loan.total_principal_paid)
+			outstanding_amount = (
+				flt(loan.disbursed_amount) - flt(loan.total_interest_payable) - flt(loan.total_principal_paid)
+			)
 
 		pledged_securities = get_pledged_security_qty(loan.name)
 		ltv_ratio = 0.0
@@ -81,21 +114,36 @@
 				ltv_ratio = get_ltv_ratio(security)
 			security_value += flt(loan_security_price_map.get(security)) * flt(qty)
 
-		current_ratio = (outstanding_amount/security_value) * 100 if security_value else 0
+		current_ratio = (outstanding_amount / security_value) * 100 if security_value else 0
 
 		if current_ratio > ltv_ratio:
 			shortfall_amount = outstanding_amount - ((security_value * ltv_ratio) / 100)
-			create_loan_security_shortfall(loan.name, outstanding_amount, security_value, shortfall_amount,
-				current_ratio, process_loan_security_shortfall)
+			create_loan_security_shortfall(
+				loan.name,
+				outstanding_amount,
+				security_value,
+				shortfall_amount,
+				current_ratio,
+				process_loan_security_shortfall,
+			)
 		elif loan_shortfall_map.get(loan.name):
 			shortfall_amount = outstanding_amount - ((security_value * ltv_ratio) / 100)
 			if shortfall_amount <= 0:
 				shortfall = loan_shortfall_map.get(loan.name)
 				update_pending_shortfall(shortfall)
 
-def create_loan_security_shortfall(loan, loan_amount, security_value, shortfall_amount, shortfall_ratio,
-	process_loan_security_shortfall):
-	existing_shortfall = frappe.db.get_value("Loan Security Shortfall", {"loan": loan, "status": "Pending"}, "name")
+
+def create_loan_security_shortfall(
+	loan,
+	loan_amount,
+	security_value,
+	shortfall_amount,
+	shortfall_ratio,
+	process_loan_security_shortfall,
+):
+	existing_shortfall = frappe.db.get_value(
+		"Loan Security Shortfall", {"loan": loan, "status": "Pending"}, "name"
+	)
 
 	if existing_shortfall:
 		ltv_shortfall = frappe.get_doc("Loan Security Shortfall", existing_shortfall)
@@ -111,16 +159,17 @@
 	ltv_shortfall.process_loan_security_shortfall = process_loan_security_shortfall
 	ltv_shortfall.save()
 
+
 def get_ltv_ratio(loan_security):
-	loan_security_type = frappe.db.get_value('Loan Security', loan_security, 'loan_security_type')
-	ltv_ratio = frappe.db.get_value('Loan Security Type', loan_security_type, 'loan_to_value_ratio')
+	loan_security_type = frappe.db.get_value("Loan Security", loan_security, "loan_security_type")
+	ltv_ratio = frappe.db.get_value("Loan Security Type", loan_security_type, "loan_to_value_ratio")
 	return ltv_ratio
 
+
 def update_pending_shortfall(shortfall):
 	# Get all pending loan security shortfall
-	frappe.db.set_value("Loan Security Shortfall", shortfall,
-		{
-			"status": "Completed",
-			"shortfall_amount": 0,
-			"shortfall_percentage": 0
-		})
+	frappe.db.set_value(
+		"Loan Security Shortfall",
+		shortfall,
+		{"status": "Completed", "shortfall_amount": 0, "shortfall_percentage": 0},
+	)
diff --git a/erpnext/loan_management/doctype/loan_security_type/loan_security_type_dashboard.py b/erpnext/loan_management/doctype/loan_security_type/loan_security_type_dashboard.py
index 2a9ceed..8fc4520 100644
--- a/erpnext/loan_management/doctype/loan_security_type/loan_security_type_dashboard.py
+++ b/erpnext/loan_management/doctype/loan_security_type/loan_security_type_dashboard.py
@@ -1,12 +1,8 @@
 def get_data():
 	return {
-		'fieldname': 'loan_security_type',
-		'transactions': [
-			{
-				'items': ['Loan Security', 'Loan Security Price']
-			},
-			{
-				'items': ['Loan Security Pledge', 'Loan Security Unpledge']
-			}
-		]
+		"fieldname": "loan_security_type",
+		"transactions": [
+			{"items": ["Loan Security", "Loan Security Price"]},
+			{"items": ["Loan Security Pledge", "Loan Security Unpledge"]},
+		],
 	}
diff --git a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
index 4567374..0ab7beb 100644
--- a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
+++ b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
@@ -15,7 +15,7 @@
 
 	def on_cancel(self):
 		self.update_loan_status(cancel=1)
-		self.db_set('status', 'Requested')
+		self.db_set("status", "Requested")
 
 	def validate_duplicate_securities(self):
 		security_list = []
@@ -23,8 +23,11 @@
 			if d.loan_security not in security_list:
 				security_list.append(d.loan_security)
 			else:
-				frappe.throw(_("Row {0}: Loan Security {1} added multiple times").format(
-					d.idx, frappe.bold(d.loan_security)))
+				frappe.throw(
+					_("Row {0}: Loan Security {1} added multiple times").format(
+						d.idx, frappe.bold(d.loan_security)
+					)
+				)
 
 	def validate_unpledge_qty(self):
 		from erpnext.loan_management.doctype.loan_repayment.loan_repayment import (
@@ -36,18 +39,33 @@
 
 		pledge_qty_map = get_pledged_security_qty(self.loan)
 
-		ltv_ratio_map = frappe._dict(frappe.get_all("Loan Security Type",
-			fields=["name", "loan_to_value_ratio"], as_list=1))
+		ltv_ratio_map = frappe._dict(
+			frappe.get_all("Loan Security Type", fields=["name", "loan_to_value_ratio"], as_list=1)
+		)
 
-		loan_security_price_map = frappe._dict(frappe.get_all("Loan Security Price",
-			fields=["loan_security", "loan_security_price"],
-			filters = {
-				"valid_from": ("<=", get_datetime()),
-				"valid_upto": (">=", get_datetime())
-			}, as_list=1))
+		loan_security_price_map = frappe._dict(
+			frappe.get_all(
+				"Loan Security Price",
+				fields=["loan_security", "loan_security_price"],
+				filters={"valid_from": ("<=", get_datetime()), "valid_upto": (">=", get_datetime())},
+				as_list=1,
+			)
+		)
 
-		loan_details = frappe.get_value("Loan", self.loan, ['total_payment', 'total_principal_paid', 'loan_amount',
-			'total_interest_payable', 'written_off_amount', 'disbursed_amount', 'status'], as_dict=1)
+		loan_details = frappe.get_value(
+			"Loan",
+			self.loan,
+			[
+				"total_payment",
+				"total_principal_paid",
+				"loan_amount",
+				"total_interest_payable",
+				"written_off_amount",
+				"disbursed_amount",
+				"status",
+			],
+			as_dict=1,
+		)
 
 		pending_principal_amount = get_pending_principal_amount(loan_details)
 
@@ -58,8 +76,13 @@
 		for security in self.securities:
 			pledged_qty = pledge_qty_map.get(security.loan_security, 0)
 			if security.qty > pledged_qty:
-				msg = _("Row {0}: {1} {2} of {3} is pledged against Loan {4}.").format(security.idx, pledged_qty, security.uom,
-					frappe.bold(security.loan_security), frappe.bold(self.loan))
+				msg = _("Row {0}: {1} {2} of {3} is pledged against Loan {4}.").format(
+					security.idx,
+					pledged_qty,
+					security.uom,
+					frappe.bold(security.loan_security),
+					frappe.bold(self.loan),
+				)
 				msg += "<br>"
 				msg += _("You are trying to unpledge more.")
 				frappe.throw(msg, title=_("Loan Security Unpledge Error"))
@@ -78,14 +101,14 @@
 		if not security_value and flt(pending_principal_amount, 2) > 0:
 			self._throw(security_value, pending_principal_amount, ltv_ratio)
 
-		if security_value and flt(pending_principal_amount/security_value) * 100 > ltv_ratio:
+		if security_value and flt(pending_principal_amount / security_value) * 100 > ltv_ratio:
 			self._throw(security_value, pending_principal_amount, ltv_ratio)
 
 	def _throw(self, security_value, pending_principal_amount, ltv_ratio):
 		msg = _("Loan Security Value after unpledge is {0}").format(frappe.bold(security_value))
-		msg += '<br>'
+		msg += "<br>"
 		msg += _("Pending principal amount is {0}").format(frappe.bold(flt(pending_principal_amount, 2)))
-		msg += '<br>'
+		msg += "<br>"
 		msg += _("Loan To Security Value ratio must always be {0}").format(frappe.bold(ltv_ratio))
 		frappe.throw(msg, title=_("Loan To Value ratio breach"))
 
@@ -95,13 +118,13 @@
 	def approve(self):
 		if self.status == "Approved" and not self.unpledge_time:
 			self.update_loan_status()
-			self.db_set('unpledge_time', get_datetime())
+			self.db_set("unpledge_time", get_datetime())
 
 	def update_loan_status(self, cancel=0):
 		if cancel:
-			loan_status = frappe.get_value('Loan', self.loan, 'status')
-			if loan_status == 'Closed':
-				frappe.db.set_value('Loan', self.loan, 'status', 'Loan Closure Requested')
+			loan_status = frappe.get_value("Loan", self.loan, "status")
+			if loan_status == "Closed":
+				frappe.db.set_value("Loan", self.loan, "status", "Loan Closure Requested")
 		else:
 			pledged_qty = 0
 			current_pledges = get_pledged_security_qty(self.loan)
@@ -110,34 +133,41 @@
 				pledged_qty += qty
 
 			if not pledged_qty:
-				frappe.db.set_value('Loan', self.loan,
-					{
-						'status': 'Closed',
-						'closure_date': getdate()
-					})
+				frappe.db.set_value("Loan", self.loan, {"status": "Closed", "closure_date": getdate()})
+
 
 @frappe.whitelist()
 def get_pledged_security_qty(loan):
 
 	current_pledges = {}
 
-	unpledges = frappe._dict(frappe.db.sql("""
+	unpledges = frappe._dict(
+		frappe.db.sql(
+			"""
 		SELECT u.loan_security, sum(u.qty) as qty
 		FROM `tabLoan Security Unpledge` up, `tabUnpledge` u
 		WHERE up.loan = %s
 		AND u.parent = up.name
 		AND up.status = 'Approved'
 		GROUP BY u.loan_security
-	""", (loan)))
+	""",
+			(loan),
+		)
+	)
 
-	pledges = frappe._dict(frappe.db.sql("""
+	pledges = frappe._dict(
+		frappe.db.sql(
+			"""
 		SELECT p.loan_security, sum(p.qty) as qty
 		FROM `tabLoan Security Pledge` lp, `tabPledge`p
 		WHERE lp.loan = %s
 		AND p.parent = lp.name
 		AND lp.status = 'Pledged'
 		GROUP BY p.loan_security
-	""", (loan)))
+	""",
+			(loan),
+		)
+	)
 
 	for security, qty in pledges.items():
 		current_pledges.setdefault(security, qty)
diff --git a/erpnext/loan_management/doctype/loan_type/loan_type.py b/erpnext/loan_management/doctype/loan_type/loan_type.py
index 592229c..51ee05b 100644
--- a/erpnext/loan_management/doctype/loan_type/loan_type.py
+++ b/erpnext/loan_management/doctype/loan_type/loan_type.py
@@ -12,12 +12,20 @@
 		self.validate_accounts()
 
 	def validate_accounts(self):
-		for fieldname in ['payment_account', 'loan_account', 'interest_income_account', 'penalty_income_account']:
-			company = frappe.get_value("Account", self.get(fieldname), 'company')
+		for fieldname in [
+			"payment_account",
+			"loan_account",
+			"interest_income_account",
+			"penalty_income_account",
+		]:
+			company = frappe.get_value("Account", self.get(fieldname), "company")
 
 			if company and company != self.company:
-				frappe.throw(_("Account {0} does not belong to company {1}").format(frappe.bold(self.get(fieldname)),
-					frappe.bold(self.company)))
+				frappe.throw(
+					_("Account {0} does not belong to company {1}").format(
+						frappe.bold(self.get(fieldname)), frappe.bold(self.company)
+					)
+				)
 
-		if self.get('loan_account') == self.get('payment_account'):
-			frappe.throw(_('Loan Account and Payment Account cannot be same'))
+		if self.get("loan_account") == self.get("payment_account"):
+			frappe.throw(_("Loan Account and Payment Account cannot be same"))
diff --git a/erpnext/loan_management/doctype/loan_type/loan_type_dashboard.py b/erpnext/loan_management/doctype/loan_type/loan_type_dashboard.py
index 245e102..e2467c6 100644
--- a/erpnext/loan_management/doctype/loan_type/loan_type_dashboard.py
+++ b/erpnext/loan_management/doctype/loan_type/loan_type_dashboard.py
@@ -1,12 +1,5 @@
 def get_data():
 	return {
-		'fieldname': 'loan_type',
-		'transactions': [
-			{
-				'items': ['Loan Repayment', 'Loan']
-			},
-			{
-				'items': ['Loan Application']
-			}
-		]
+		"fieldname": "loan_type",
+		"transactions": [{"items": ["Loan Repayment", "Loan"]}, {"items": ["Loan Application"]}],
 	}
diff --git a/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py b/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py
index 35be587..25aecf6 100644
--- a/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py
+++ b/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py
@@ -22,11 +22,16 @@
 
 	def validate_write_off_amount(self):
 		precision = cint(frappe.db.get_default("currency_precision")) or 2
-		total_payment, principal_paid, interest_payable, written_off_amount = frappe.get_value("Loan", self.loan,
-			['total_payment', 'total_principal_paid','total_interest_payable', 'written_off_amount'])
+		total_payment, principal_paid, interest_payable, written_off_amount = frappe.get_value(
+			"Loan",
+			self.loan,
+			["total_payment", "total_principal_paid", "total_interest_payable", "written_off_amount"],
+		)
 
-		pending_principal_amount = flt(flt(total_payment) - flt(interest_payable) - flt(principal_paid) - flt(written_off_amount),
-			precision)
+		pending_principal_amount = flt(
+			flt(total_payment) - flt(interest_payable) - flt(principal_paid) - flt(written_off_amount),
+			precision,
+		)
 
 		if self.write_off_amount > pending_principal_amount:
 			frappe.throw(_("Write off amount cannot be greater than pending principal amount"))
@@ -37,52 +42,55 @@
 
 	def on_cancel(self):
 		self.update_outstanding_amount(cancel=1)
-		self.ignore_linked_doctypes = ['GL Entry']
+		self.ignore_linked_doctypes = ["GL Entry", "Payment Ledger Entry"]
 		self.make_gl_entries(cancel=1)
 
 	def update_outstanding_amount(self, cancel=0):
-		written_off_amount = frappe.db.get_value('Loan', self.loan, 'written_off_amount')
+		written_off_amount = frappe.db.get_value("Loan", self.loan, "written_off_amount")
 
 		if cancel:
 			written_off_amount -= self.write_off_amount
 		else:
 			written_off_amount += self.write_off_amount
 
-		frappe.db.set_value('Loan', self.loan, 'written_off_amount', written_off_amount)
-
+		frappe.db.set_value("Loan", self.loan, "written_off_amount", written_off_amount)
 
 	def make_gl_entries(self, cancel=0):
 		gl_entries = []
 		loan_details = frappe.get_doc("Loan", self.loan)
 
 		gl_entries.append(
-			self.get_gl_dict({
-				"account": self.write_off_account,
-				"against": loan_details.loan_account,
-				"debit": self.write_off_amount,
-				"debit_in_account_currency": self.write_off_amount,
-				"against_voucher_type": "Loan",
-				"against_voucher": self.loan,
-				"remarks": _("Against Loan:") + self.loan,
-				"cost_center": self.cost_center,
-				"posting_date": getdate(self.posting_date)
-			})
+			self.get_gl_dict(
+				{
+					"account": self.write_off_account,
+					"against": loan_details.loan_account,
+					"debit": self.write_off_amount,
+					"debit_in_account_currency": self.write_off_amount,
+					"against_voucher_type": "Loan",
+					"against_voucher": self.loan,
+					"remarks": _("Against Loan:") + self.loan,
+					"cost_center": self.cost_center,
+					"posting_date": getdate(self.posting_date),
+				}
+			)
 		)
 
 		gl_entries.append(
-			self.get_gl_dict({
-				"account": loan_details.loan_account,
-				"party_type": loan_details.applicant_type,
-				"party": loan_details.applicant,
-				"against": self.write_off_account,
-				"credit": self.write_off_amount,
-				"credit_in_account_currency": self.write_off_amount,
-				"against_voucher_type": "Loan",
-				"against_voucher": self.loan,
-				"remarks": _("Against Loan:") + self.loan,
-				"cost_center": self.cost_center,
-				"posting_date": getdate(self.posting_date)
-			})
+			self.get_gl_dict(
+				{
+					"account": loan_details.loan_account,
+					"party_type": loan_details.applicant_type,
+					"party": loan_details.applicant,
+					"against": self.write_off_account,
+					"credit": self.write_off_amount,
+					"credit_in_account_currency": self.write_off_amount,
+					"against_voucher_type": "Loan",
+					"against_voucher": self.loan,
+					"remarks": _("Against Loan:") + self.loan,
+					"cost_center": self.cost_center,
+					"posting_date": getdate(self.posting_date),
+				}
+			)
 		)
 
 		make_gl_entries(gl_entries, cancel=cancel, merge_entries=False)
diff --git a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py
index 4c34ccd..81464a3 100644
--- a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py
+++ b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py
@@ -17,24 +17,36 @@
 		open_loans = []
 
 		if self.loan:
-			loan_doc = frappe.get_doc('Loan', self.loan)
+			loan_doc = frappe.get_doc("Loan", self.loan)
 			if loan_doc:
 				open_loans.append(loan_doc)
 
-		if (not self.loan or not loan_doc.is_term_loan) and self.process_type != 'Term Loans':
-			make_accrual_interest_entry_for_demand_loans(self.posting_date, self.name,
-				open_loans = open_loans, loan_type = self.loan_type, accrual_type=self.accrual_type)
+		if (not self.loan or not loan_doc.is_term_loan) and self.process_type != "Term Loans":
+			make_accrual_interest_entry_for_demand_loans(
+				self.posting_date,
+				self.name,
+				open_loans=open_loans,
+				loan_type=self.loan_type,
+				accrual_type=self.accrual_type,
+			)
 
-		if (not self.loan or loan_doc.is_term_loan) and self.process_type != 'Demand Loans':
-			make_accrual_interest_entry_for_term_loans(self.posting_date, self.name, term_loan=self.loan,
-				loan_type=self.loan_type, accrual_type=self.accrual_type)
+		if (not self.loan or loan_doc.is_term_loan) and self.process_type != "Demand Loans":
+			make_accrual_interest_entry_for_term_loans(
+				self.posting_date,
+				self.name,
+				term_loan=self.loan,
+				loan_type=self.loan_type,
+				accrual_type=self.accrual_type,
+			)
 
 
-def process_loan_interest_accrual_for_demand_loans(posting_date=None, loan_type=None, loan=None, accrual_type="Regular"):
-	loan_process = frappe.new_doc('Process Loan Interest Accrual')
+def process_loan_interest_accrual_for_demand_loans(
+	posting_date=None, loan_type=None, loan=None, accrual_type="Regular"
+):
+	loan_process = frappe.new_doc("Process Loan Interest Accrual")
 	loan_process.posting_date = posting_date or nowdate()
 	loan_process.loan_type = loan_type
-	loan_process.process_type = 'Demand Loans'
+	loan_process.process_type = "Demand Loans"
 	loan_process.loan = loan
 	loan_process.accrual_type = accrual_type
 
@@ -42,25 +54,26 @@
 
 	return loan_process.name
 
+
 def process_loan_interest_accrual_for_term_loans(posting_date=None, loan_type=None, loan=None):
 
 	if not term_loan_accrual_pending(posting_date or nowdate()):
 		return
 
-	loan_process = frappe.new_doc('Process Loan Interest Accrual')
+	loan_process = frappe.new_doc("Process Loan Interest Accrual")
 	loan_process.posting_date = posting_date or nowdate()
 	loan_process.loan_type = loan_type
-	loan_process.process_type = 'Term Loans'
+	loan_process.process_type = "Term Loans"
 	loan_process.loan = loan
 
 	loan_process.submit()
 
 	return loan_process.name
 
+
 def term_loan_accrual_pending(date):
-	pending_accrual = frappe.db.get_value('Repayment Schedule', {
-		'payment_date': ('<=', date),
-		'is_accrued': 0
-	})
+	pending_accrual = frappe.db.get_value(
+		"Repayment Schedule", {"payment_date": ("<=", date), "is_accrued": 0}
+	)
 
 	return pending_accrual
diff --git a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual_dashboard.py b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual_dashboard.py
index 932087c..ac85df7 100644
--- a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual_dashboard.py
+++ b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual_dashboard.py
@@ -1,9 +1,5 @@
 def get_data():
 	return {
-		'fieldname': 'process_loan_interest_accrual',
-		'transactions': [
-			{
-				'items': ['Loan Interest Accrual']
-			}
-		]
+		"fieldname": "process_loan_interest_accrual",
+		"transactions": [{"items": ["Loan Interest Accrual"]}],
 	}
diff --git a/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.py b/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.py
index ba9fb0c..fffc5d4 100644
--- a/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.py
+++ b/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.py
@@ -13,16 +13,18 @@
 
 class ProcessLoanSecurityShortfall(Document):
 	def onload(self):
-		self.set_onload('update_time', get_datetime())
+		self.set_onload("update_time", get_datetime())
 
 	def on_submit(self):
 		check_for_ltv_shortfall(self.name)
 
+
 def create_process_loan_security_shortfall():
 	if check_for_secured_loans():
 		process = frappe.new_doc("Process Loan Security Shortfall")
 		process.update_time = get_datetime()
 		process.submit()
 
+
 def check_for_secured_loans():
-	return frappe.db.count('Loan', {'docstatus': 1, 'is_secured_loan': 1})
+	return frappe.db.count("Loan", {"docstatus": 1, "is_secured_loan": 1})
diff --git a/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall_dashboard.py b/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall_dashboard.py
index 1f5d843..4d7b163 100644
--- a/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall_dashboard.py
+++ b/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall_dashboard.py
@@ -1,9 +1,5 @@
 def get_data():
 	return {
-		'fieldname': 'process_loan_security_shortfall',
-		'transactions': [
-			{
-				'items': ['Loan Security Shortfall']
-			}
-		]
+		"fieldname": "process_loan_security_shortfall",
+		"transactions": [{"items": ["Loan Security Shortfall"]}],
 	}
diff --git a/erpnext/loan_management/doctype/sanctioned_loan_amount/sanctioned_loan_amount.py b/erpnext/loan_management/doctype/sanctioned_loan_amount/sanctioned_loan_amount.py
index 6063b7b..e7487cb 100644
--- a/erpnext/loan_management/doctype/sanctioned_loan_amount/sanctioned_loan_amount.py
+++ b/erpnext/loan_management/doctype/sanctioned_loan_amount/sanctioned_loan_amount.py
@@ -9,9 +9,13 @@
 
 class SanctionedLoanAmount(Document):
 	def validate(self):
-		sanctioned_doc = frappe.db.exists('Sanctioned Loan Amount', {'applicant': self.applicant, 'company': self.company})
+		sanctioned_doc = frappe.db.exists(
+			"Sanctioned Loan Amount", {"applicant": self.applicant, "company": self.company}
+		)
 
 		if sanctioned_doc and sanctioned_doc != self.name:
-			frappe.throw(_("Sanctioned Loan Amount already exists for {0} against company {1}").format(
-				frappe.bold(self.applicant), frappe.bold(self.company)
-			))
+			frappe.throw(
+				_("Sanctioned Loan Amount already exists for {0} against company {1}").format(
+					frappe.bold(self.applicant), frappe.bold(self.company)
+				)
+			)
diff --git a/erpnext/loan_management/loan_common.js b/erpnext/loan_management/loan_common.js
index 43980ff..247e30b 100644
--- a/erpnext/loan_management/loan_common.js
+++ b/erpnext/loan_management/loan_common.js
@@ -3,11 +3,6 @@
 
 frappe.ui.form.on(cur_frm.doctype, {
 	refresh: function(frm) {
-		if (!frappe.boot.active_domains.includes("Non Profit")) {
-			frm.set_df_property('applicant_type', 'options', ['Employee', 'Customer']);
-			frm.refresh_field('applicant_type');
-		}
-
 		if (['Loan Disbursement', 'Loan Repayment', 'Loan Interest Accrual', 'Loan Write Off'].includes(frm.doc.doctype)
 			&& frm.doc.docstatus > 0) {
 
diff --git a/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py
index 512b47f..02da810 100644
--- a/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py
+++ b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py
@@ -17,84 +17,166 @@
 
 def get_columns(filters):
 	columns = [
-		{"label": _("Applicant Type"), "fieldname": "applicant_type", "options": "DocType", "width": 100},
-		{"label": _("Applicant Name"), "fieldname": "applicant_name", "fieldtype": "Dynamic Link", "options": "applicant_type", "width": 150},
-		{"label": _("Loan Security"), "fieldname": "loan_security", "fieldtype": "Link", "options": "Loan Security", "width": 160},
-		{"label": _("Loan Security Code"), "fieldname": "loan_security_code", "fieldtype": "Data", "width": 100},
-		{"label": _("Loan Security Name"), "fieldname": "loan_security_name", "fieldtype": "Data", "width": 150},
+		{
+			"label": _("Applicant Type"),
+			"fieldname": "applicant_type",
+			"options": "DocType",
+			"width": 100,
+		},
+		{
+			"label": _("Applicant Name"),
+			"fieldname": "applicant_name",
+			"fieldtype": "Dynamic Link",
+			"options": "applicant_type",
+			"width": 150,
+		},
+		{
+			"label": _("Loan Security"),
+			"fieldname": "loan_security",
+			"fieldtype": "Link",
+			"options": "Loan Security",
+			"width": 160,
+		},
+		{
+			"label": _("Loan Security Code"),
+			"fieldname": "loan_security_code",
+			"fieldtype": "Data",
+			"width": 100,
+		},
+		{
+			"label": _("Loan Security Name"),
+			"fieldname": "loan_security_name",
+			"fieldtype": "Data",
+			"width": 150,
+		},
 		{"label": _("Haircut"), "fieldname": "haircut", "fieldtype": "Percent", "width": 100},
-		{"label": _("Loan Security Type"), "fieldname": "loan_security_type", "fieldtype": "Link", "options": "Loan Security Type", "width": 120},
+		{
+			"label": _("Loan Security Type"),
+			"fieldname": "loan_security_type",
+			"fieldtype": "Link",
+			"options": "Loan Security Type",
+			"width": 120,
+		},
 		{"label": _("Disabled"), "fieldname": "disabled", "fieldtype": "Check", "width": 80},
 		{"label": _("Total Qty"), "fieldname": "total_qty", "fieldtype": "Float", "width": 100},
-		{"label": _("Latest Price"), "fieldname": "latest_price", "fieldtype": "Currency", "options": "currency", "width": 100},
-		{"label": _("Price Valid Upto"), "fieldname": "price_valid_upto", "fieldtype": "Datetime", "width": 100},
-		{"label": _("Current Value"), "fieldname": "current_value", "fieldtype": "Currency", "options": "currency", "width": 100},
-		{"label": _("% Of Applicant Portfolio"), "fieldname": "portfolio_percent", "fieldtype": "Percentage", "width": 100},
-		{"label": _("Currency"), "fieldname": "currency", "fieldtype": "Currency", "options": "Currency", "hidden": 1, "width": 100},
+		{
+			"label": _("Latest Price"),
+			"fieldname": "latest_price",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 100,
+		},
+		{
+			"label": _("Price Valid Upto"),
+			"fieldname": "price_valid_upto",
+			"fieldtype": "Datetime",
+			"width": 100,
+		},
+		{
+			"label": _("Current Value"),
+			"fieldname": "current_value",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 100,
+		},
+		{
+			"label": _("% Of Applicant Portfolio"),
+			"fieldname": "portfolio_percent",
+			"fieldtype": "Percentage",
+			"width": 100,
+		},
+		{
+			"label": _("Currency"),
+			"fieldname": "currency",
+			"fieldtype": "Currency",
+			"options": "Currency",
+			"hidden": 1,
+			"width": 100,
+		},
 	]
 
 	return columns
 
+
 def get_data(filters):
 	data = []
 	loan_security_details = get_loan_security_details()
-	pledge_values, total_value_map, applicant_type_map = get_applicant_wise_total_loan_security_qty(filters,
-		loan_security_details)
+	pledge_values, total_value_map, applicant_type_map = get_applicant_wise_total_loan_security_qty(
+		filters, loan_security_details
+	)
 
-	currency = erpnext.get_company_currency(filters.get('company'))
+	currency = erpnext.get_company_currency(filters.get("company"))
 
 	for key, qty in pledge_values.items():
 		if qty:
 			row = {}
-			current_value = flt(qty * loan_security_details.get(key[1], {}).get('latest_price', 0))
-			valid_upto = loan_security_details.get(key[1], {}).get('valid_upto')
+			current_value = flt(qty * loan_security_details.get(key[1], {}).get("latest_price", 0))
+			valid_upto = loan_security_details.get(key[1], {}).get("valid_upto")
 
 			row.update(loan_security_details.get(key[1]))
-			row.update({
-				'applicant_type': applicant_type_map.get(key[0]),
-				'applicant_name': key[0],
-				'total_qty': qty,
-				'current_value': current_value,
-				'price_valid_upto': valid_upto,
-				'portfolio_percent': flt(current_value * 100 / total_value_map.get(key[0]), 2) if total_value_map.get(key[0]) \
+			row.update(
+				{
+					"applicant_type": applicant_type_map.get(key[0]),
+					"applicant_name": key[0],
+					"total_qty": qty,
+					"current_value": current_value,
+					"price_valid_upto": valid_upto,
+					"portfolio_percent": flt(current_value * 100 / total_value_map.get(key[0]), 2)
+					if total_value_map.get(key[0])
 					else 0.0,
-				'currency': currency
-			})
+					"currency": currency,
+				}
+			)
 
 			data.append(row)
 
 	return data
 
+
 def get_loan_security_details():
 	security_detail_map = {}
 	loan_security_price_map = {}
 	lsp_validity_map = {}
 
-	loan_security_prices = frappe.db.sql("""
+	loan_security_prices = frappe.db.sql(
+		"""
 		SELECT loan_security, loan_security_price, valid_upto
 		FROM `tabLoan Security Price` t1
 		WHERE valid_from >= (SELECT MAX(valid_from) FROM `tabLoan Security Price` t2
 		WHERE t1.loan_security = t2.loan_security)
-	""", as_dict=1)
+	""",
+		as_dict=1,
+	)
 
 	for security in loan_security_prices:
 		loan_security_price_map.setdefault(security.loan_security, security.loan_security_price)
 		lsp_validity_map.setdefault(security.loan_security, security.valid_upto)
 
-	loan_security_details = frappe.get_all('Loan Security', fields=['name as loan_security',
-		'loan_security_code', 'loan_security_name', 'haircut', 'loan_security_type',
-		'disabled'])
+	loan_security_details = frappe.get_all(
+		"Loan Security",
+		fields=[
+			"name as loan_security",
+			"loan_security_code",
+			"loan_security_name",
+			"haircut",
+			"loan_security_type",
+			"disabled",
+		],
+	)
 
 	for security in loan_security_details:
-		security.update({
-			'latest_price': flt(loan_security_price_map.get(security.loan_security)),
-			'valid_upto': lsp_validity_map.get(security.loan_security)
-		})
+		security.update(
+			{
+				"latest_price": flt(loan_security_price_map.get(security.loan_security)),
+				"valid_upto": lsp_validity_map.get(security.loan_security),
+			}
+		)
 
 		security_detail_map.setdefault(security.loan_security, security)
 
 	return security_detail_map
 
+
 def get_applicant_wise_total_loan_security_qty(filters, loan_security_details):
 	current_pledges = {}
 	total_value_map = {}
@@ -102,39 +184,53 @@
 	applicant_wise_unpledges = {}
 	conditions = ""
 
-	if filters.get('company'):
+	if filters.get("company"):
 		conditions = "AND company = %(company)s"
 
-	unpledges = frappe.db.sql("""
+	unpledges = frappe.db.sql(
+		"""
 		SELECT up.applicant, u.loan_security, sum(u.qty) as qty
 		FROM `tabLoan Security Unpledge` up, `tabUnpledge` u
 		WHERE u.parent = up.name
 		AND up.status = 'Approved'
 		{conditions}
 		GROUP BY up.applicant, u.loan_security
-	""".format(conditions=conditions), filters, as_dict=1)
+	""".format(
+			conditions=conditions
+		),
+		filters,
+		as_dict=1,
+	)
 
 	for unpledge in unpledges:
 		applicant_wise_unpledges.setdefault((unpledge.applicant, unpledge.loan_security), unpledge.qty)
 
-	pledges = frappe.db.sql("""
+	pledges = frappe.db.sql(
+		"""
 		SELECT lp.applicant_type, lp.applicant, p.loan_security, sum(p.qty) as qty
 		FROM `tabLoan Security Pledge` lp, `tabPledge`p
 		WHERE p.parent = lp.name
 		AND lp.status = 'Pledged'
 		{conditions}
 		GROUP BY lp.applicant, p.loan_security
-	""".format(conditions=conditions), filters, as_dict=1)
+	""".format(
+			conditions=conditions
+		),
+		filters,
+		as_dict=1,
+	)
 
 	for security in pledges:
 		current_pledges.setdefault((security.applicant, security.loan_security), security.qty)
 		total_value_map.setdefault(security.applicant, 0.0)
 		applicant_type_map.setdefault(security.applicant, security.applicant_type)
 
-		current_pledges[(security.applicant, security.loan_security)] -= \
-			applicant_wise_unpledges.get((security.applicant, security.loan_security), 0.0)
+		current_pledges[(security.applicant, security.loan_security)] -= applicant_wise_unpledges.get(
+			(security.applicant, security.loan_security), 0.0
+		)
 
-		total_value_map[security.applicant] += current_pledges.get((security.applicant, security.loan_security)) \
-			* loan_security_details.get(security.loan_security, {}).get('latest_price', 0)
+		total_value_map[security.applicant] += current_pledges.get(
+			(security.applicant, security.loan_security)
+		) * loan_security_details.get(security.loan_security, {}).get("latest_price", 0)
 
 	return current_pledges, total_value_map, applicant_type_map
diff --git a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py
index 7c51267..9186ce6 100644
--- a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py
+++ b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py
@@ -17,41 +17,148 @@
 	data = get_active_loan_details(filters)
 	return columns, data
 
+
 def get_columns(filters):
 	columns = [
 		{"label": _("Loan"), "fieldname": "loan", "fieldtype": "Link", "options": "Loan", "width": 160},
 		{"label": _("Status"), "fieldname": "status", "fieldtype": "Data", "width": 160},
-		{"label": _("Applicant Type"), "fieldname": "applicant_type", "options": "DocType", "width": 100},
-		{"label": _("Applicant Name"), "fieldname": "applicant_name", "fieldtype": "Dynamic Link", "options": "applicant_type", "width": 150},
-		{"label": _("Loan Type"), "fieldname": "loan_type", "fieldtype": "Link", "options": "Loan Type", "width": 100},
-		{"label": _("Sanctioned Amount"), "fieldname": "sanctioned_amount", "fieldtype": "Currency", "options": "currency", "width": 120},
-		{"label": _("Disbursed Amount"), "fieldname": "disbursed_amount", "fieldtype": "Currency", "options": "currency", "width": 120},
-		{"label": _("Penalty Amount"), "fieldname": "penalty", "fieldtype": "Currency", "options": "currency", "width": 120},
-		{"label": _("Accrued Interest"), "fieldname": "accrued_interest", "fieldtype": "Currency", "options": "currency", "width": 120},
-		{"label": _("Total Repayment"), "fieldname": "total_repayment", "fieldtype": "Currency", "options": "currency", "width": 120},
-		{"label": _("Principal Outstanding"), "fieldname": "principal_outstanding", "fieldtype": "Currency", "options": "currency", "width": 120},
-		{"label": _("Interest Outstanding"), "fieldname": "interest_outstanding", "fieldtype": "Currency", "options": "currency", "width": 120},
-		{"label": _("Total Outstanding"), "fieldname": "total_outstanding", "fieldtype": "Currency", "options": "currency", "width": 120},
-		{"label": _("Undue Booked Interest"), "fieldname": "undue_interest", "fieldtype": "Currency", "options": "currency", "width": 120},
-		{"label": _("Interest %"), "fieldname": "rate_of_interest", "fieldtype": "Percent", "width": 100},
-		{"label": _("Penalty Interest %"), "fieldname": "penalty_interest", "fieldtype": "Percent", "width": 100},
-		{"label": _("Loan To Value Ratio"), "fieldname": "loan_to_value", "fieldtype": "Percent", "width": 100},
-		{"label": _("Currency"), "fieldname": "currency", "fieldtype": "Currency", "options": "Currency", "hidden": 1, "width": 100},
+		{
+			"label": _("Applicant Type"),
+			"fieldname": "applicant_type",
+			"options": "DocType",
+			"width": 100,
+		},
+		{
+			"label": _("Applicant Name"),
+			"fieldname": "applicant_name",
+			"fieldtype": "Dynamic Link",
+			"options": "applicant_type",
+			"width": 150,
+		},
+		{
+			"label": _("Loan Type"),
+			"fieldname": "loan_type",
+			"fieldtype": "Link",
+			"options": "Loan Type",
+			"width": 100,
+		},
+		{
+			"label": _("Sanctioned Amount"),
+			"fieldname": "sanctioned_amount",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 120,
+		},
+		{
+			"label": _("Disbursed Amount"),
+			"fieldname": "disbursed_amount",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 120,
+		},
+		{
+			"label": _("Penalty Amount"),
+			"fieldname": "penalty",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 120,
+		},
+		{
+			"label": _("Accrued Interest"),
+			"fieldname": "accrued_interest",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 120,
+		},
+		{
+			"label": _("Total Repayment"),
+			"fieldname": "total_repayment",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 120,
+		},
+		{
+			"label": _("Principal Outstanding"),
+			"fieldname": "principal_outstanding",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 120,
+		},
+		{
+			"label": _("Interest Outstanding"),
+			"fieldname": "interest_outstanding",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 120,
+		},
+		{
+			"label": _("Total Outstanding"),
+			"fieldname": "total_outstanding",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 120,
+		},
+		{
+			"label": _("Undue Booked Interest"),
+			"fieldname": "undue_interest",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 120,
+		},
+		{
+			"label": _("Interest %"),
+			"fieldname": "rate_of_interest",
+			"fieldtype": "Percent",
+			"width": 100,
+		},
+		{
+			"label": _("Penalty Interest %"),
+			"fieldname": "penalty_interest",
+			"fieldtype": "Percent",
+			"width": 100,
+		},
+		{
+			"label": _("Loan To Value Ratio"),
+			"fieldname": "loan_to_value",
+			"fieldtype": "Percent",
+			"width": 100,
+		},
+		{
+			"label": _("Currency"),
+			"fieldname": "currency",
+			"fieldtype": "Currency",
+			"options": "Currency",
+			"hidden": 1,
+			"width": 100,
+		},
 	]
 
 	return columns
 
+
 def get_active_loan_details(filters):
 
 	filter_obj = {"status": ("!=", "Closed")}
-	if filters.get('company'):
-		filter_obj.update({'company': filters.get('company')})
+	if filters.get("company"):
+		filter_obj.update({"company": filters.get("company")})
 
-	loan_details = frappe.get_all("Loan",
-		fields=["name as loan", "applicant_type", "applicant as applicant_name", "loan_type",
-		"disbursed_amount", "rate_of_interest", "total_payment", "total_principal_paid",
-		"total_interest_payable", "written_off_amount", "status"],
-		filters=filter_obj)
+	loan_details = frappe.get_all(
+		"Loan",
+		fields=[
+			"name as loan",
+			"applicant_type",
+			"applicant as applicant_name",
+			"loan_type",
+			"disbursed_amount",
+			"rate_of_interest",
+			"total_payment",
+			"total_principal_paid",
+			"total_interest_payable",
+			"written_off_amount",
+			"status",
+		],
+		filters=filter_obj,
+	)
 
 	loan_list = [d.loan for d in loan_details]
 
@@ -62,70 +169,105 @@
 	penal_interest_rate_map = get_penal_interest_rate_map()
 	payments = get_payments(loan_list)
 	accrual_map = get_interest_accruals(loan_list)
-	currency = erpnext.get_company_currency(filters.get('company'))
+	currency = erpnext.get_company_currency(filters.get("company"))
 
 	for loan in loan_details:
-		total_payment = loan.total_payment if loan.status == 'Disbursed' else loan.disbursed_amount
+		total_payment = loan.total_payment if loan.status == "Disbursed" else loan.disbursed_amount
 
-		loan.update({
-			"sanctioned_amount": flt(sanctioned_amount_map.get(loan.applicant_name)),
-			"principal_outstanding": flt(total_payment) - flt(loan.total_principal_paid) \
-				- flt(loan.total_interest_payable) - flt(loan.written_off_amount),
-			"total_repayment": flt(payments.get(loan.loan)),
-			"accrued_interest": flt(accrual_map.get(loan.loan, {}).get("accrued_interest")),
-			"interest_outstanding": flt(accrual_map.get(loan.loan, {}).get("interest_outstanding")),
-			"penalty": flt(accrual_map.get(loan.loan, {}).get("penalty")),
-			"penalty_interest": penal_interest_rate_map.get(loan.loan_type),
-			"undue_interest": flt(accrual_map.get(loan.loan, {}).get("undue_interest")),
-			"loan_to_value": 0.0,
-			"currency": currency
-		})
+		loan.update(
+			{
+				"sanctioned_amount": flt(sanctioned_amount_map.get(loan.applicant_name)),
+				"principal_outstanding": flt(total_payment)
+				- flt(loan.total_principal_paid)
+				- flt(loan.total_interest_payable)
+				- flt(loan.written_off_amount),
+				"total_repayment": flt(payments.get(loan.loan)),
+				"accrued_interest": flt(accrual_map.get(loan.loan, {}).get("accrued_interest")),
+				"interest_outstanding": flt(accrual_map.get(loan.loan, {}).get("interest_outstanding")),
+				"penalty": flt(accrual_map.get(loan.loan, {}).get("penalty")),
+				"penalty_interest": penal_interest_rate_map.get(loan.loan_type),
+				"undue_interest": flt(accrual_map.get(loan.loan, {}).get("undue_interest")),
+				"loan_to_value": 0.0,
+				"currency": currency,
+			}
+		)
 
-		loan['total_outstanding'] = loan['principal_outstanding'] + loan['interest_outstanding'] \
-			+ loan['penalty']
+		loan["total_outstanding"] = (
+			loan["principal_outstanding"] + loan["interest_outstanding"] + loan["penalty"]
+		)
 
 		if loan_wise_security_value.get(loan.loan):
-			loan['loan_to_value'] = flt((loan['principal_outstanding'] * 100) / loan_wise_security_value.get(loan.loan))
+			loan["loan_to_value"] = flt(
+				(loan["principal_outstanding"] * 100) / loan_wise_security_value.get(loan.loan)
+			)
 
 	return loan_details
 
+
 def get_sanctioned_amount_map():
-	return frappe._dict(frappe.get_all("Sanctioned Loan Amount", fields=["applicant", "sanctioned_amount_limit"],
-		as_list=1))
+	return frappe._dict(
+		frappe.get_all(
+			"Sanctioned Loan Amount", fields=["applicant", "sanctioned_amount_limit"], as_list=1
+		)
+	)
+
 
 def get_payments(loans):
-	return frappe._dict(frappe.get_all("Loan Repayment", fields=["against_loan", "sum(amount_paid)"],
-		filters={"against_loan": ("in", loans)}, group_by="against_loan", as_list=1))
+	return frappe._dict(
+		frappe.get_all(
+			"Loan Repayment",
+			fields=["against_loan", "sum(amount_paid)"],
+			filters={"against_loan": ("in", loans)},
+			group_by="against_loan",
+			as_list=1,
+		)
+	)
+
 
 def get_interest_accruals(loans):
 	accrual_map = {}
 
-	interest_accruals = frappe.get_all("Loan Interest Accrual",
-		fields=["loan", "interest_amount", "posting_date", "penalty_amount",
-		"paid_interest_amount", "accrual_type"], filters={"loan": ("in", loans)}, order_by="posting_date desc")
+	interest_accruals = frappe.get_all(
+		"Loan Interest Accrual",
+		fields=[
+			"loan",
+			"interest_amount",
+			"posting_date",
+			"penalty_amount",
+			"paid_interest_amount",
+			"accrual_type",
+		],
+		filters={"loan": ("in", loans)},
+		order_by="posting_date desc",
+	)
 
 	for entry in interest_accruals:
-		accrual_map.setdefault(entry.loan, {
-			"accrued_interest": 0.0,
-			"undue_interest": 0.0,
-			"interest_outstanding": 0.0,
-			"last_accrual_date": '',
-			"due_date": ''
-		})
+		accrual_map.setdefault(
+			entry.loan,
+			{
+				"accrued_interest": 0.0,
+				"undue_interest": 0.0,
+				"interest_outstanding": 0.0,
+				"last_accrual_date": "",
+				"due_date": "",
+			},
+		)
 
-		if entry.accrual_type == 'Regular':
-			if not accrual_map[entry.loan]['due_date']:
-				accrual_map[entry.loan]['due_date'] = add_days(entry.posting_date, 1)
-			if not accrual_map[entry.loan]['last_accrual_date']:
-				accrual_map[entry.loan]['last_accrual_date'] = entry.posting_date
+		if entry.accrual_type == "Regular":
+			if not accrual_map[entry.loan]["due_date"]:
+				accrual_map[entry.loan]["due_date"] = add_days(entry.posting_date, 1)
+			if not accrual_map[entry.loan]["last_accrual_date"]:
+				accrual_map[entry.loan]["last_accrual_date"] = entry.posting_date
 
-		due_date = accrual_map[entry.loan]['due_date']
-		last_accrual_date = accrual_map[entry.loan]['last_accrual_date']
+		due_date = accrual_map[entry.loan]["due_date"]
+		last_accrual_date = accrual_map[entry.loan]["last_accrual_date"]
 
 		if due_date and getdate(entry.posting_date) < getdate(due_date):
-			accrual_map[entry.loan]["interest_outstanding"] += entry.interest_amount - entry.paid_interest_amount
+			accrual_map[entry.loan]["interest_outstanding"] += (
+				entry.interest_amount - entry.paid_interest_amount
+			)
 		else:
-			accrual_map[entry.loan]['undue_interest'] += entry.interest_amount - entry.paid_interest_amount
+			accrual_map[entry.loan]["undue_interest"] += entry.interest_amount - entry.paid_interest_amount
 
 		accrual_map[entry.loan]["accrued_interest"] += entry.interest_amount
 
@@ -134,8 +276,12 @@
 
 	return accrual_map
 
+
 def get_penal_interest_rate_map():
-	return frappe._dict(frappe.get_all("Loan Type", fields=["name", "penalty_interest_rate"], as_list=1))
+	return frappe._dict(
+		frappe.get_all("Loan Type", fields=["name", "penalty_interest_rate"], as_list=1)
+	)
+
 
 def get_loan_wise_pledges(filters):
 	loan_wise_unpledges = {}
@@ -143,37 +289,51 @@
 
 	conditions = ""
 
-	if filters.get('company'):
+	if filters.get("company"):
 		conditions = "AND company = %(company)s"
 
-	unpledges = frappe.db.sql("""
+	unpledges = frappe.db.sql(
+		"""
 		SELECT up.loan, u.loan_security, sum(u.qty) as qty
 		FROM `tabLoan Security Unpledge` up, `tabUnpledge` u
 		WHERE u.parent = up.name
 		AND up.status = 'Approved'
 		{conditions}
 		GROUP BY up.loan, u.loan_security
-	""".format(conditions=conditions), filters, as_dict=1)
+	""".format(
+			conditions=conditions
+		),
+		filters,
+		as_dict=1,
+	)
 
 	for unpledge in unpledges:
 		loan_wise_unpledges.setdefault((unpledge.loan, unpledge.loan_security), unpledge.qty)
 
-	pledges = frappe.db.sql("""
+	pledges = frappe.db.sql(
+		"""
 		SELECT lp.loan, p.loan_security, sum(p.qty) as qty
 		FROM `tabLoan Security Pledge` lp, `tabPledge`p
 		WHERE p.parent = lp.name
 		AND lp.status = 'Pledged'
 		{conditions}
 		GROUP BY lp.loan, p.loan_security
-	""".format(conditions=conditions), filters, as_dict=1)
+	""".format(
+			conditions=conditions
+		),
+		filters,
+		as_dict=1,
+	)
 
 	for security in pledges:
 		current_pledges.setdefault((security.loan, security.loan_security), security.qty)
-		current_pledges[(security.loan, security.loan_security)] -= \
-			loan_wise_unpledges.get((security.loan, security.loan_security), 0.0)
+		current_pledges[(security.loan, security.loan_security)] -= loan_wise_unpledges.get(
+			(security.loan, security.loan_security), 0.0
+		)
 
 	return current_pledges
 
+
 def get_loan_wise_security_value(filters, current_pledges):
 	loan_security_details = get_loan_security_details()
 	loan_wise_security_value = {}
@@ -181,7 +341,8 @@
 	for key in current_pledges:
 		qty = current_pledges.get(key)
 		loan_wise_security_value.setdefault(key[0], 0.0)
-		loan_wise_security_value[key[0]] += \
-			flt(qty * loan_security_details.get(key[1], {}).get('latest_price', 0))
+		loan_wise_security_value[key[0]] += flt(
+			qty * loan_security_details.get(key[1], {}).get("latest_price", 0)
+		)
 
 	return loan_wise_security_value
diff --git a/erpnext/loan_management/report/loan_repayment_and_closure/loan_repayment_and_closure.py b/erpnext/loan_management/report/loan_repayment_and_closure/loan_repayment_and_closure.py
index 68fd3d8..253b994 100644
--- a/erpnext/loan_management/report/loan_repayment_and_closure/loan_repayment_and_closure.py
+++ b/erpnext/loan_management/report/loan_repayment_and_closure/loan_repayment_and_closure.py
@@ -11,101 +11,96 @@
 	data = get_data(filters)
 	return columns, data
 
+
 def get_columns():
 	return [
-			{
-				"label": _("Posting Date"),
-				"fieldtype": "Date",
-				"fieldname": "posting_date",
-				"width": 100
-			},
-			{
-				"label": _("Loan Repayment"),
-				"fieldtype": "Link",
-				"fieldname": "loan_repayment",
-				"options": "Loan Repayment",
-				"width": 100
-			},
-			{
-				"label": _("Against Loan"),
-				"fieldtype": "Link",
-				"fieldname": "against_loan",
-				"options": "Loan",
-				"width": 200
-			},
-			{
-				"label": _("Applicant"),
-				"fieldtype": "Data",
-				"fieldname": "applicant",
-				"width": 150
-			},
-			{
-				"label": _("Payment Type"),
-				"fieldtype": "Data",
-				"fieldname": "payment_type",
-				"width": 150
-			},
-			{
-				"label": _("Principal Amount"),
-				"fieldtype": "Currency",
-				"fieldname": "principal_amount",
-				"options": "currency",
-				"width": 100
-			},
-			{
-				"label": _("Interest Amount"),
-				"fieldtype": "Currency",
-				"fieldname": "interest",
-				"options": "currency",
-				"width": 100
-			},
-			{
-				"label": _("Penalty Amount"),
-				"fieldtype": "Currency",
-				"fieldname": "penalty",
-				"options": "currency",
-				"width": 100
-			},
-			{
-				"label": _("Payable Amount"),
-				"fieldtype": "Currency",
-				"fieldname": "payable_amount",
-				"options": "currency",
-				"width": 100
-			},
-			{
-				"label": _("Paid Amount"),
-				"fieldtype": "Currency",
-				"fieldname": "paid_amount",
-				"options": "currency",
-				"width": 100
-			},
-			{
-				"label": _("Currency"),
-				"fieldtype": "Link",
-				"fieldname": "currency",
-				"options": "Currency",
-				"width": 100
-			}
-		]
+		{"label": _("Posting Date"), "fieldtype": "Date", "fieldname": "posting_date", "width": 100},
+		{
+			"label": _("Loan Repayment"),
+			"fieldtype": "Link",
+			"fieldname": "loan_repayment",
+			"options": "Loan Repayment",
+			"width": 100,
+		},
+		{
+			"label": _("Against Loan"),
+			"fieldtype": "Link",
+			"fieldname": "against_loan",
+			"options": "Loan",
+			"width": 200,
+		},
+		{"label": _("Applicant"), "fieldtype": "Data", "fieldname": "applicant", "width": 150},
+		{"label": _("Payment Type"), "fieldtype": "Data", "fieldname": "payment_type", "width": 150},
+		{
+			"label": _("Principal Amount"),
+			"fieldtype": "Currency",
+			"fieldname": "principal_amount",
+			"options": "currency",
+			"width": 100,
+		},
+		{
+			"label": _("Interest Amount"),
+			"fieldtype": "Currency",
+			"fieldname": "interest",
+			"options": "currency",
+			"width": 100,
+		},
+		{
+			"label": _("Penalty Amount"),
+			"fieldtype": "Currency",
+			"fieldname": "penalty",
+			"options": "currency",
+			"width": 100,
+		},
+		{
+			"label": _("Payable Amount"),
+			"fieldtype": "Currency",
+			"fieldname": "payable_amount",
+			"options": "currency",
+			"width": 100,
+		},
+		{
+			"label": _("Paid Amount"),
+			"fieldtype": "Currency",
+			"fieldname": "paid_amount",
+			"options": "currency",
+			"width": 100,
+		},
+		{
+			"label": _("Currency"),
+			"fieldtype": "Link",
+			"fieldname": "currency",
+			"options": "Currency",
+			"width": 100,
+		},
+	]
+
 
 def get_data(filters):
 	data = []
 
 	query_filters = {
 		"docstatus": 1,
-		"company": filters.get('company'),
+		"company": filters.get("company"),
 	}
 
-	if filters.get('applicant'):
-		query_filters.update({
-			"applicant": filters.get('applicant')
-		})
+	if filters.get("applicant"):
+		query_filters.update({"applicant": filters.get("applicant")})
 
-	loan_repayments = frappe.get_all("Loan Repayment",
-		filters = query_filters,
-		fields=["posting_date", "applicant", "name", "against_loan", "payable_amount",
-			"pending_principal_amount", "interest_payable", "penalty_amount", "amount_paid"]
+	loan_repayments = frappe.get_all(
+		"Loan Repayment",
+		filters=query_filters,
+		fields=[
+			"posting_date",
+			"applicant",
+			"name",
+			"against_loan",
+			"payable_amount",
+			"pending_principal_amount",
+			"interest_payable",
+			"penalty_amount",
+			"amount_paid",
+		],
 	)
 
 	default_currency = frappe.get_cached_value("Company", filters.get("company"), "default_currency")
@@ -122,7 +117,7 @@
 			"penalty": repayment.penalty_amount,
 			"payable_amount": repayment.payable_amount,
 			"paid_amount": repayment.amount_paid,
-			"currency": default_currency
+			"currency": default_currency,
 		}
 
 		data.append(row)
diff --git a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py
index 7dbb966..a92f960 100644
--- a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py
+++ b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py
@@ -17,46 +17,110 @@
 	data = get_data(filters)
 	return columns, data
 
+
 def get_columns(filters):
 	columns = [
-		{"label": _("Loan Security"), "fieldname": "loan_security", "fieldtype": "Link", "options": "Loan Security", "width": 160},
-		{"label": _("Loan Security Code"), "fieldname": "loan_security_code", "fieldtype": "Data", "width": 100},
-		{"label": _("Loan Security Name"), "fieldname": "loan_security_name", "fieldtype": "Data", "width": 150},
+		{
+			"label": _("Loan Security"),
+			"fieldname": "loan_security",
+			"fieldtype": "Link",
+			"options": "Loan Security",
+			"width": 160,
+		},
+		{
+			"label": _("Loan Security Code"),
+			"fieldname": "loan_security_code",
+			"fieldtype": "Data",
+			"width": 100,
+		},
+		{
+			"label": _("Loan Security Name"),
+			"fieldname": "loan_security_name",
+			"fieldtype": "Data",
+			"width": 150,
+		},
 		{"label": _("Haircut"), "fieldname": "haircut", "fieldtype": "Percent", "width": 100},
-		{"label": _("Loan Security Type"), "fieldname": "loan_security_type", "fieldtype": "Link", "options": "Loan Security Type", "width": 120},
+		{
+			"label": _("Loan Security Type"),
+			"fieldname": "loan_security_type",
+			"fieldtype": "Link",
+			"options": "Loan Security Type",
+			"width": 120,
+		},
 		{"label": _("Disabled"), "fieldname": "disabled", "fieldtype": "Check", "width": 80},
 		{"label": _("Total Qty"), "fieldname": "total_qty", "fieldtype": "Float", "width": 100},
-		{"label": _("Latest Price"), "fieldname": "latest_price", "fieldtype": "Currency", "options": "currency", "width": 100},
-		{"label": _("Price Valid Upto"), "fieldname": "price_valid_upto", "fieldtype": "Datetime", "width": 100},
-		{"label": _("Current Value"), "fieldname": "current_value", "fieldtype": "Currency", "options": "currency", "width": 100},
-		{"label": _("% Of Total Portfolio"), "fieldname": "portfolio_percent", "fieldtype": "Percentage", "width": 100},
-		{"label": _("Pledged Applicant Count"), "fieldname": "pledged_applicant_count", "fieldtype": "Percentage", "width": 100},
-		{"label": _("Currency"), "fieldname": "currency", "fieldtype": "Currency", "options": "Currency", "hidden": 1, "width": 100},
+		{
+			"label": _("Latest Price"),
+			"fieldname": "latest_price",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 100,
+		},
+		{
+			"label": _("Price Valid Upto"),
+			"fieldname": "price_valid_upto",
+			"fieldtype": "Datetime",
+			"width": 100,
+		},
+		{
+			"label": _("Current Value"),
+			"fieldname": "current_value",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 100,
+		},
+		{
+			"label": _("% Of Total Portfolio"),
+			"fieldname": "portfolio_percent",
+			"fieldtype": "Percentage",
+			"width": 100,
+		},
+		{
+			"label": _("Pledged Applicant Count"),
+			"fieldname": "pledged_applicant_count",
+			"fieldtype": "Percentage",
+			"width": 100,
+		},
+		{
+			"label": _("Currency"),
+			"fieldname": "currency",
+			"fieldtype": "Currency",
+			"options": "Currency",
+			"hidden": 1,
+			"width": 100,
+		},
 	]
 
 	return columns
 
+
 def get_data(filters):
 	data = []
 	loan_security_details = get_loan_security_details()
-	current_pledges, total_portfolio_value = get_company_wise_loan_security_details(filters, loan_security_details)
-	currency = erpnext.get_company_currency(filters.get('company'))
+	current_pledges, total_portfolio_value = get_company_wise_loan_security_details(
+		filters, loan_security_details
+	)
+	currency = erpnext.get_company_currency(filters.get("company"))
 
 	for security, value in current_pledges.items():
-		if value.get('qty'):
+		if value.get("qty"):
 			row = {}
-			current_value = flt(value.get('qty', 0) * loan_security_details.get(security, {}).get('latest_price', 0))
-			valid_upto = loan_security_details.get(security, {}).get('valid_upto')
+			current_value = flt(
+				value.get("qty", 0) * loan_security_details.get(security, {}).get("latest_price", 0)
+			)
+			valid_upto = loan_security_details.get(security, {}).get("valid_upto")
 
 			row.update(loan_security_details.get(security))
-			row.update({
-				'total_qty': value.get('qty'),
-				'current_value': current_value,
-				'price_valid_upto': valid_upto,
-				'portfolio_percent': flt(current_value * 100 / total_portfolio_value, 2),
-				'pledged_applicant_count': value.get('applicant_count'),
-				'currency': currency
-			})
+			row.update(
+				{
+					"total_qty": value.get("qty"),
+					"current_value": current_value,
+					"price_valid_upto": valid_upto,
+					"portfolio_percent": flt(current_value * 100 / total_portfolio_value, 2),
+					"pledged_applicant_count": value.get("applicant_count"),
+					"currency": currency,
+				}
+			)
 
 			data.append(row)
 
@@ -64,21 +128,19 @@
 
 
 def get_company_wise_loan_security_details(filters, loan_security_details):
-	pledge_values, total_value_map, applicant_type_map = get_applicant_wise_total_loan_security_qty(filters,
-		loan_security_details)
+	pledge_values, total_value_map, applicant_type_map = get_applicant_wise_total_loan_security_qty(
+		filters, loan_security_details
+	)
 
 	total_portfolio_value = 0
 	security_wise_map = {}
 	for key, qty in pledge_values.items():
-		security_wise_map.setdefault(key[1], {
-			'qty': 0.0,
-			'applicant_count': 0.0
-		})
+		security_wise_map.setdefault(key[1], {"qty": 0.0, "applicant_count": 0.0})
 
-		security_wise_map[key[1]]['qty'] += qty
+		security_wise_map[key[1]]["qty"] += qty
 		if qty:
-			security_wise_map[key[1]]['applicant_count'] += 1
+			security_wise_map[key[1]]["applicant_count"] += 1
 
-		total_portfolio_value += flt(qty * loan_security_details.get(key[1], {}).get('latest_price', 0))
+		total_portfolio_value += flt(qty * loan_security_details.get(key[1], {}).get("latest_price", 0))
 
 	return security_wise_map, total_portfolio_value
diff --git a/erpnext/loan_management/report/loan_security_status/loan_security_status.py b/erpnext/loan_management/report/loan_security_status/loan_security_status.py
index b7e7168..9a5a180 100644
--- a/erpnext/loan_management/report/loan_security_status/loan_security_status.py
+++ b/erpnext/loan_management/report/loan_security_status/loan_security_status.py
@@ -11,66 +11,41 @@
 	data = get_data(filters)
 	return columns, data
 
+
 def get_columns(filters):
-	columns= [
+	columns = [
 		{
 			"label": _("Loan Security Pledge"),
 			"fieldtype": "Link",
 			"fieldname": "loan_security_pledge",
 			"options": "Loan Security Pledge",
-			"width": 200
+			"width": 200,
 		},
-		{
-			"label": _("Loan"),
-			"fieldtype": "Link",
-			"fieldname": "loan",
-			"options": "Loan",
-			"width": 200
-		},
-		{
-			"label": _("Applicant"),
-			"fieldtype": "Data",
-			"fieldname": "applicant",
-			"width": 200
-		},
-		{
-			"label": _("Status"),
-			"fieldtype": "Data",
-			"fieldname": "status",
-			"width": 100
-		},
-		{
-			"label": _("Pledge Time"),
-			"fieldtype": "Data",
-			"fieldname": "pledge_time",
-			"width": 150
-		},
+		{"label": _("Loan"), "fieldtype": "Link", "fieldname": "loan", "options": "Loan", "width": 200},
+		{"label": _("Applicant"), "fieldtype": "Data", "fieldname": "applicant", "width": 200},
+		{"label": _("Status"), "fieldtype": "Data", "fieldname": "status", "width": 100},
+		{"label": _("Pledge Time"), "fieldtype": "Data", "fieldname": "pledge_time", "width": 150},
 		{
 			"label": _("Loan Security"),
 			"fieldtype": "Link",
 			"fieldname": "loan_security",
 			"options": "Loan Security",
-			"width": 150
+			"width": 150,
 		},
-		{
-			"label": _("Quantity"),
-			"fieldtype": "Float",
-			"fieldname": "qty",
-			"width": 100
-		},
+		{"label": _("Quantity"), "fieldtype": "Float", "fieldname": "qty", "width": 100},
 		{
 			"label": _("Loan Security Price"),
 			"fieldtype": "Currency",
 			"fieldname": "loan_security_price",
 			"options": "currency",
-			"width": 200
+			"width": 200,
 		},
 		{
 			"label": _("Loan Security Value"),
 			"fieldtype": "Currency",
 			"fieldname": "loan_security_value",
 			"options": "currency",
-			"width": 200
+			"width": 200,
 		},
 		{
 			"label": _("Currency"),
@@ -78,18 +53,20 @@
 			"fieldname": "currency",
 			"options": "Currency",
 			"width": 50,
-			"hidden": 1
-		}
+			"hidden": 1,
+		},
 	]
 
 	return columns
 
+
 def get_data(filters):
 
 	data = []
 	conditions = get_conditions(filters)
 
-	loan_security_pledges = frappe.db.sql("""
+	loan_security_pledges = frappe.db.sql(
+		"""
 		SELECT
 			p.name, p.applicant, p.loan, p.status, p.pledge_time,
 			c.loan_security, c.qty, c.loan_security_price, c.amount
@@ -100,7 +77,12 @@
 			AND c.parent = p.name
 			AND p.company = %(company)s
 			{conditions}
-	""".format(conditions = conditions), (filters), as_dict=1) #nosec
+	""".format(
+			conditions=conditions
+		),
+		(filters),
+		as_dict=1,
+	)  # nosec
 
 	default_currency = frappe.get_cached_value("Company", filters.get("company"), "default_currency")
 
@@ -121,6 +103,7 @@
 
 	return data
 
+
 def get_conditions(filters):
 	conditions = []
 
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js
index 035290d..5252798 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js
+++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js
@@ -140,26 +140,6 @@
 		}
 	}
 
-	start_date(doc, cdt, cdn) {
-		this.set_no_of_visits(doc, cdt, cdn);
-	}
-
-	end_date(doc, cdt, cdn) {
-		this.set_no_of_visits(doc, cdt, cdn);
-	}
-
-	periodicity(doc, cdt, cdn) {
-		this.set_no_of_visits(doc, cdt, cdn);
-	}
-
-	set_no_of_visits(doc, cdt, cdn) {
-		var item = frappe.get_doc(cdt, cdn);
-		let me = this;
-		if (item.start_date && item.periodicity) {
-			me.frm.call('validate_end_date_visits');
-
-		}
-	}
 };
 
 extend_cscript(cur_frm.cscript, new erpnext.maintenance.MaintenanceSchedule({frm: cur_frm}));
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
index 07d928c..04c080c 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
@@ -16,17 +16,17 @@
 	def generate_schedule(self):
 		if self.docstatus != 0:
 			return
-		self.set('schedules', [])
+		self.set("schedules", [])
 		count = 1
-		for d in self.get('items'):
+		for d in self.get("items"):
 			self.validate_maintenance_detail()
 			s_list = []
 			s_list = self.create_schedule_list(d.start_date, d.end_date, d.no_of_visits, d.sales_person)
 			for i in range(d.no_of_visits):
-				child = self.append('schedules')
+				child = self.append("schedules")
 				child.item_code = d.item_code
 				child.item_name = d.item_name
-				child.scheduled_date = s_list[i].strftime('%Y-%m-%d')
+				child.scheduled_date = s_list[i].strftime("%Y-%m-%d")
 				if d.serial_no:
 					child.serial_no = d.serial_no
 				child.idx = count
@@ -37,18 +37,14 @@
 
 	@frappe.whitelist()
 	def validate_end_date_visits(self):
-		days_in_period = {
-			"Weekly": 7,
-			"Monthly": 30,
-			"Quarterly": 91,
-			"Half Yearly": 182,
-			"Yearly": 365
-		}
+		days_in_period = {"Weekly": 7, "Monthly": 30, "Quarterly": 91, "Half Yearly": 182, "Yearly": 365}
 		for item in self.items:
 			if item.periodicity and item.periodicity != "Random" and item.start_date:
 				if not item.end_date:
 					if item.no_of_visits:
-						item.end_date = add_days(item.start_date, item.no_of_visits * days_in_period[item.periodicity])
+						item.end_date = add_days(
+							item.start_date, item.no_of_visits * days_in_period[item.periodicity]
+						)
 					else:
 						item.end_date = add_days(item.start_date, days_in_period[item.periodicity])
 
@@ -61,20 +57,23 @@
 					item.no_of_visits = cint(diff / days_in_period[item.periodicity])
 
 				elif item.no_of_visits > no_of_visits:
-					item.end_date = add_days(item.start_date, item.no_of_visits * days_in_period[item.periodicity])
+					item.end_date = add_days(
+						item.start_date, item.no_of_visits * days_in_period[item.periodicity]
+					)
 
 				elif item.no_of_visits < no_of_visits:
-					item.end_date = add_days(item.start_date, item.no_of_visits * days_in_period[item.periodicity])
-
+					item.end_date = add_days(
+						item.start_date, item.no_of_visits * days_in_period[item.periodicity]
+					)
 
 	def on_submit(self):
-		if not self.get('schedules'):
+		if not self.get("schedules"):
 			throw(_("Please click on 'Generate Schedule' to get schedule"))
 		self.check_serial_no_added()
 		self.validate_schedule()
 
 		email_map = {}
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if d.serial_no:
 				serial_nos = get_valid_serial_nos(d.serial_no)
 				self.validate_serial_no(d.item_code, serial_nos, d.start_date)
@@ -90,29 +89,37 @@
 
 			if no_email_sp:
 				frappe.msgprint(
-					_("Setting Events to {0}, since the Employee attached to the below Sales Persons does not have a User ID{1}").format(
-						self.owner, "<br>" + "<br>".join(no_email_sp)
-					)
+					_(
+						"Setting Events to {0}, since the Employee attached to the below Sales Persons does not have a User ID{1}"
+					).format(self.owner, "<br>" + "<br>".join(no_email_sp))
 				)
 
-			scheduled_date = frappe.db.sql("""select scheduled_date from
+			scheduled_date = frappe.db.sql(
+				"""select scheduled_date from
 				`tabMaintenance Schedule Detail` where sales_person=%s and item_code=%s and
-				parent=%s""", (d.sales_person, d.item_code, self.name), as_dict=1)
+				parent=%s""",
+				(d.sales_person, d.item_code, self.name),
+				as_dict=1,
+			)
 
 			for key in scheduled_date:
-				description =frappe._("Reference: {0}, Item Code: {1} and Customer: {2}").format(self.name, d.item_code, self.customer)
-				event = frappe.get_doc({
-					"doctype": "Event",
-					"owner": email_map.get(d.sales_person, self.owner),
-					"subject": description,
-					"description": description,
-					"starts_on": cstr(key["scheduled_date"]) + " 10:00:00",
-					"event_type": "Private",
-				})
+				description = frappe._("Reference: {0}, Item Code: {1} and Customer: {2}").format(
+					self.name, d.item_code, self.customer
+				)
+				event = frappe.get_doc(
+					{
+						"doctype": "Event",
+						"owner": email_map.get(d.sales_person, self.owner),
+						"subject": description,
+						"description": description,
+						"starts_on": cstr(key["scheduled_date"]) + " 10:00:00",
+						"event_type": "Private",
+					}
+				)
 				event.add_participant(self.doctype, self.name)
 				event.insert(ignore_permissions=1)
 
-		frappe.db.set(self, 'status', 'Submitted')
+		frappe.db.set(self, "status", "Submitted")
 
 	def create_schedule_list(self, start_date, end_date, no_of_visit, sales_person):
 		schedule_list = []
@@ -121,11 +128,12 @@
 		add_by = date_diff / no_of_visit
 
 		for visit in range(cint(no_of_visit)):
-			if (getdate(start_date_copy) < getdate(end_date)):
+			if getdate(start_date_copy) < getdate(end_date):
 				start_date_copy = add_days(start_date_copy, add_by)
 				if len(schedule_list) < no_of_visit:
-					schedule_date = self.validate_schedule_date_for_holiday_list(getdate(start_date_copy),
-						sales_person)
+					schedule_date = self.validate_schedule_date_for_holiday_list(
+						getdate(start_date_copy), sales_person
+					)
 					if schedule_date > getdate(end_date):
 						schedule_date = getdate(end_date)
 					schedule_list.append(schedule_date)
@@ -139,9 +147,11 @@
 		if employee:
 			holiday_list = get_holiday_list_for_employee(employee)
 		else:
-			holiday_list = frappe.get_cached_value('Company', self.company, "default_holiday_list")
+			holiday_list = frappe.get_cached_value("Company", self.company, "default_holiday_list")
 
-		holidays = frappe.db.sql_list('''select holiday_date from `tabHoliday` where parent=%s''', holiday_list)
+		holidays = frappe.db.sql_list(
+			"""select holiday_date from `tabHoliday` where parent=%s""", holiday_list
+		)
 
 		if not validated and holidays:
 
@@ -157,25 +167,28 @@
 
 	def validate_dates_with_periodicity(self):
 		for d in self.get("items"):
-			if d.start_date and d.end_date and d.periodicity and d.periodicity!="Random":
+			if d.start_date and d.end_date and d.periodicity and d.periodicity != "Random":
 				date_diff = (getdate(d.end_date) - getdate(d.start_date)).days + 1
 				days_in_period = {
 					"Weekly": 7,
 					"Monthly": 30,
 					"Quarterly": 90,
 					"Half Yearly": 180,
-					"Yearly": 365
+					"Yearly": 365,
 				}
 
 				if date_diff < days_in_period[d.periodicity]:
-					throw(_("Row {0}: To set {1} periodicity, difference between from and to date must be greater than or equal to {2}")
-						.format(d.idx, d.periodicity, days_in_period[d.periodicity]))
+					throw(
+						_(
+							"Row {0}: To set {1} periodicity, difference between from and to date must be greater than or equal to {2}"
+						).format(d.idx, d.periodicity, days_in_period[d.periodicity])
+					)
 
 	def validate_maintenance_detail(self):
-		if not self.get('items'):
+		if not self.get("items"):
 			throw(_("Please enter Maintaince Details first"))
 
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if not d.item_code:
 				throw(_("Please select item code"))
 			elif not d.start_date or not d.end_date:
@@ -189,14 +202,37 @@
 				throw(_("Start date should be less than end date for Item {0}").format(d.item_code))
 
 	def validate_sales_order(self):
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if d.sales_order:
-				chk = frappe.db.sql("""select ms.name from `tabMaintenance Schedule` ms,
+				chk = frappe.db.sql(
+					"""select ms.name from `tabMaintenance Schedule` ms,
 					`tabMaintenance Schedule Item` msi where msi.parent=ms.name and
-					msi.sales_order=%s and ms.docstatus=1""", d.sales_order)
+					msi.sales_order=%s and ms.docstatus=1""",
+					d.sales_order,
+				)
 				if chk:
 					throw(_("Maintenance Schedule {0} exists against {1}").format(chk[0][0], d.sales_order))
 
+	def validate_items_table_change(self):
+		doc_before_save = self.get_doc_before_save()
+		if not doc_before_save:
+			return
+		for prev_item, item in zip(doc_before_save.items, self.items):
+			fields = [
+				"item_code",
+				"start_date",
+				"end_date",
+				"periodicity",
+				"sales_person",
+				"no_of_visits",
+				"serial_no",
+			]
+			for field in fields:
+				b_doc = prev_item.as_dict()
+				doc = item.as_dict()
+				if cstr(b_doc[field]) != cstr(doc[field]):
+					return True
+
 	def validate_no_of_visits(self):
 		return len(self.schedules) != sum(d.no_of_visits for d in self.items)
 
@@ -205,11 +241,11 @@
 		self.validate_maintenance_detail()
 		self.validate_dates_with_periodicity()
 		self.validate_sales_order()
-		if not self.schedules or self.validate_no_of_visits():
+		if not self.schedules or self.validate_items_table_change() or self.validate_no_of_visits():
 			self.generate_schedule()
 
 	def on_update(self):
-		frappe.db.set(self, 'status', 'Draft')
+		frappe.db.set(self, "status", "Draft")
 
 	def update_amc_date(self, serial_nos, amc_expiry_date=None):
 		for serial_no in serial_nos:
@@ -219,65 +255,96 @@
 
 	def validate_serial_no(self, item_code, serial_nos, amc_start_date):
 		for serial_no in serial_nos:
-			sr_details = frappe.db.get_value("Serial No", serial_no,
-				["warranty_expiry_date", "amc_expiry_date", "warehouse", "delivery_date", "item_code"], as_dict=1)
+			sr_details = frappe.db.get_value(
+				"Serial No",
+				serial_no,
+				["warranty_expiry_date", "amc_expiry_date", "warehouse", "delivery_date", "item_code"],
+				as_dict=1,
+			)
 
 			if not sr_details:
 				frappe.throw(_("Serial No {0} not found").format(serial_no))
 
 			if sr_details.get("item_code") != item_code:
-				frappe.throw(_("Serial No {0} does not belong to Item {1}")
-					.format(frappe.bold(serial_no), frappe.bold(item_code)), title="Invalid")
+				frappe.throw(
+					_("Serial No {0} does not belong to Item {1}").format(
+						frappe.bold(serial_no), frappe.bold(item_code)
+					),
+					title=_("Invalid"),
+				)
 
-			if sr_details.warranty_expiry_date \
-				and getdate(sr_details.warranty_expiry_date) >= getdate(amc_start_date):
-				throw(_("Serial No {0} is under warranty upto {1}")
-					.format(serial_no, sr_details.warranty_expiry_date))
+			if sr_details.warranty_expiry_date and getdate(sr_details.warranty_expiry_date) >= getdate(
+				amc_start_date
+			):
+				throw(
+					_("Serial No {0} is under warranty upto {1}").format(
+						serial_no, sr_details.warranty_expiry_date
+					)
+				)
 
-			if sr_details.amc_expiry_date and getdate(sr_details.amc_expiry_date) >= getdate(amc_start_date):
-				throw(_("Serial No {0} is under maintenance contract upto {1}")
-					.format(serial_no, sr_details.amc_expiry_date))
+			if sr_details.amc_expiry_date and getdate(sr_details.amc_expiry_date) >= getdate(
+				amc_start_date
+			):
+				throw(
+					_("Serial No {0} is under maintenance contract upto {1}").format(
+						serial_no, sr_details.amc_expiry_date
+					)
+				)
 
-			if not sr_details.warehouse and sr_details.delivery_date and \
-				getdate(sr_details.delivery_date) >= getdate(amc_start_date):
-				throw(_("Maintenance start date can not be before delivery date for Serial No {0}")
-					.format(serial_no))
+			if (
+				not sr_details.warehouse
+				and sr_details.delivery_date
+				and getdate(sr_details.delivery_date) >= getdate(amc_start_date)
+			):
+				throw(
+					_("Maintenance start date can not be before delivery date for Serial No {0}").format(
+						serial_no
+					)
+				)
 
 	def validate_schedule(self):
-		item_lst1 =[]
-		item_lst2 =[]
-		for d in self.get('items'):
+		item_lst1 = []
+		item_lst2 = []
+		for d in self.get("items"):
 			if d.item_code not in item_lst1:
 				item_lst1.append(d.item_code)
 
-		for m in self.get('schedules'):
+		for m in self.get("schedules"):
 			if m.item_code not in item_lst2:
 				item_lst2.append(m.item_code)
 
 		if len(item_lst1) != len(item_lst2):
-			throw(_("Maintenance Schedule is not generated for all the items. Please click on 'Generate Schedule'"))
+			throw(
+				_(
+					"Maintenance Schedule is not generated for all the items. Please click on 'Generate Schedule'"
+				)
+			)
 		else:
 			for x in item_lst1:
 				if x not in item_lst2:
 					throw(_("Please click on 'Generate Schedule'"))
 
 	def check_serial_no_added(self):
-		serial_present =[]
-		for d in self.get('items'):
+		serial_present = []
+		for d in self.get("items"):
 			if d.serial_no:
 				serial_present.append(d.item_code)
 
-		for m in self.get('schedules'):
+		for m in self.get("schedules"):
 			if serial_present:
 				if m.item_code in serial_present and not m.serial_no:
-					throw(_("Please click on 'Generate Schedule' to fetch Serial No added for Item {0}").format(m.item_code))
+					throw(
+						_("Please click on 'Generate Schedule' to fetch Serial No added for Item {0}").format(
+							m.item_code
+						)
+					)
 
 	def on_cancel(self):
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if d.serial_no:
 				serial_nos = get_valid_serial_nos(d.serial_no)
 				self.update_amc_date(serial_nos)
-		frappe.db.set(self, 'status', 'Cancelled')
+		frappe.db.set(self, "status", "Cancelled")
 		delete_events(self.doctype, self.name)
 
 	def on_trash(self):
@@ -301,23 +368,26 @@
 			return items
 		elif data_type == "id":
 			for schedule in self.schedules:
-				if schedule.item_name == item_name and s_date == formatdate(schedule.scheduled_date, "dd-mm-yyyy"):
+				if schedule.item_name == item_name and s_date == formatdate(
+					schedule.scheduled_date, "dd-mm-yyyy"
+				):
 					return schedule.name
 
+
 @frappe.whitelist()
 def get_serial_nos_from_schedule(item_code, schedule=None):
 	serial_nos = []
 	if schedule:
-		serial_nos = frappe.db.get_value('Maintenance Schedule Item', {
-			'parent': schedule,
-			'item_code': item_code
-		}, 'serial_no')
+		serial_nos = frappe.db.get_value(
+			"Maintenance Schedule Item", {"parent": schedule, "item_code": item_code}, "serial_no"
+		)
 
 	if serial_nos:
 		serial_nos = get_serial_nos(serial_nos)
 
 	return serial_nos
 
+
 @frappe.whitelist()
 def make_maintenance_visit(source_name, target_doc=None, item_name=None, s_id=None):
 	from frappe.model.mapper import get_mapped_doc
@@ -331,27 +401,26 @@
 		if len(serial_nos) == 1:
 			target.serial_no = serial_nos[0]
 		else:
-			target.serial_no = ''
+			target.serial_no = ""
 
-	doclist = get_mapped_doc("Maintenance Schedule", source_name, {
-		"Maintenance Schedule": {
-			"doctype": "Maintenance Visit",
-			"field_map": {
-				"name": "maintenance_schedule"
+	doclist = get_mapped_doc(
+		"Maintenance Schedule",
+		source_name,
+		{
+			"Maintenance Schedule": {
+				"doctype": "Maintenance Visit",
+				"field_map": {"name": "maintenance_schedule"},
+				"validation": {"docstatus": ["=", 1]},
+				"postprocess": update_status_and_detail,
 			},
-			"validation": {
-				"docstatus": ["=", 1]
+			"Maintenance Schedule Item": {
+				"doctype": "Maintenance Visit Purpose",
+				"condition": lambda doc: doc.item_name == item_name,
+				"field_map": {"sales_person": "service_person"},
+				"postprocess": update_serial,
 			},
-			"postprocess": update_status_and_detail
 		},
-		"Maintenance Schedule Item": {
-			"doctype": "Maintenance Visit Purpose",
-			"condition": lambda doc: doc.item_name == item_name,
-			"field_map": {
-				"sales_person": "service_person"
-			},
-			"postprocess": update_serial
-		}
-	}, target_doc)
+		target_doc,
+	)
 
 	return doclist
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
index 6e727e5..2268e0f 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
@@ -16,6 +16,7 @@
 
 # test_records = frappe.get_test_records('Maintenance Schedule')
 
+
 class TestMaintenanceSchedule(unittest.TestCase):
 	def test_events_should_be_created_and_deleted(self):
 		ms = make_maintenance_schedule()
@@ -42,15 +43,15 @@
 		expected_end_date = add_days(i.start_date, i.no_of_visits * 7)
 		self.assertEqual(i.end_date, expected_end_date)
 
-		items = ms.get_pending_data(data_type = "items")
-		items = items.split('\n')
+		items = ms.get_pending_data(data_type="items")
+		items = items.split("\n")
 		items.pop(0)
-		expected_items = ['_Test Item']
+		expected_items = ["_Test Item"]
 		self.assertTrue(items, expected_items)
 
 		# "dates" contains all generated schedule dates
-		dates = ms.get_pending_data(data_type = "date", item_name = i.item_name)
-		dates = dates.split('\n')
+		dates = ms.get_pending_data(data_type="date", item_name=i.item_name)
+		dates = dates.split("\n")
 		dates.pop(0)
 		expected_dates.append(formatdate(add_days(i.start_date, 7), "dd-MM-yyyy"))
 		expected_dates.append(formatdate(add_days(i.start_date, 14), "dd-MM-yyyy"))
@@ -59,33 +60,38 @@
 		self.assertEqual(dates, expected_dates)
 
 		ms.submit()
-		s_id = ms.get_pending_data(data_type = "id", item_name = i.item_name, s_date = expected_dates[1])
+		s_id = ms.get_pending_data(data_type="id", item_name=i.item_name, s_date=expected_dates[1])
 
 		# Check if item is mapped in visit.
-		test_map_visit = make_maintenance_visit(source_name = ms.name, item_name = "_Test Item", s_id = s_id)
+		test_map_visit = make_maintenance_visit(source_name=ms.name, item_name="_Test Item", s_id=s_id)
 		self.assertEqual(len(test_map_visit.purposes), 1)
 		self.assertEqual(test_map_visit.purposes[0].item_name, "_Test Item")
 
-		visit = frappe.new_doc('Maintenance Visit')
+		visit = frappe.new_doc("Maintenance Visit")
 		visit = test_map_visit
 		visit.maintenance_schedule = ms.name
 		visit.maintenance_schedule_detail = s_id
 		visit.completion_status = "Partially Completed"
-		visit.set('purposes', [{
-			'item_code': i.item_code,
-			'description': "test",
-			'work_done': "test",
-			'service_person': "Sales Team",
-		}])
+		visit.set(
+			"purposes",
+			[
+				{
+					"item_code": i.item_code,
+					"description": "test",
+					"work_done": "test",
+					"service_person": "Sales Team",
+				}
+			],
+		)
 		visit.save()
 		visit.submit()
-		ms = frappe.get_doc('Maintenance Schedule', ms.name)
+		ms = frappe.get_doc("Maintenance Schedule", ms.name)
 
-		#checks if visit status is back updated in schedule
+		# checks if visit status is back updated in schedule
 		self.assertTrue(ms.schedules[1].completion_status, "Partially Completed")
 		self.assertEqual(format_date(visit.mntc_date), format_date(ms.schedules[1].actual_date))
 
-		#checks if visit status is updated on cancel
+		# checks if visit status is updated on cancel
 		visit.cancel()
 		ms.reload()
 		self.assertTrue(ms.schedules[1].completion_status, "Pending")
@@ -117,22 +123,54 @@
 
 		frappe.db.rollback()
 
+	def test_schedule_with_serials(self):
+		# Checks whether serials are automatically updated when changing in items table.
+		# Also checks if other fields trigger generate schdeule if changed in items table.
+		item_code = "_Test Serial Item"
+		make_serial_item_with_serial(item_code)
+		ms = make_maintenance_schedule(item_code=item_code, serial_no="TEST001, TEST002")
+		ms.save()
+
+		# Before Save
+		self.assertEqual(ms.schedules[0].serial_no, "TEST001, TEST002")
+		self.assertEqual(ms.schedules[0].sales_person, "Sales Team")
+		self.assertEqual(len(ms.schedules), 4)
+		self.assertFalse(ms.validate_items_table_change())
+		# After Save
+		ms.items[0].serial_no = "TEST001"
+		ms.items[0].sales_person = "_Test Sales Person"
+		ms.items[0].no_of_visits = 2
+		self.assertTrue(ms.validate_items_table_change())
+		ms.save()
+		self.assertEqual(ms.schedules[0].serial_no, "TEST001")
+		self.assertEqual(ms.schedules[0].sales_person, "_Test Sales Person")
+		self.assertEqual(len(ms.schedules), 2)
+		# When user manually deleted a row from schedules table.
+		ms.schedules.pop()
+		self.assertEqual(len(ms.schedules), 1)
+		ms.save()
+		self.assertEqual(len(ms.schedules), 2)
+
+		frappe.db.rollback()
+
+
 def make_serial_item_with_serial(item_code):
 	serial_item_doc = create_item(item_code, is_stock_item=1)
 	if not serial_item_doc.has_serial_no or not serial_item_doc.serial_no_series:
 		serial_item_doc.has_serial_no = 1
 		serial_item_doc.serial_no_series = "TEST.###"
 		serial_item_doc.save(ignore_permissions=True)
-	active_serials = frappe.db.get_all('Serial No', {"status": "Active", "item_code": item_code})
+	active_serials = frappe.db.get_all("Serial No", {"status": "Active", "item_code": item_code})
 	if len(active_serials) < 2:
 		make_serialized_item(item_code=item_code)
 
+
 def get_events(ms):
-	return frappe.get_all("Event Participants", filters={
-			"reference_doctype": ms.doctype,
-			"reference_docname": ms.name,
-			"parenttype": "Event"
-		})
+	return frappe.get_all(
+		"Event Participants",
+		filters={"reference_doctype": ms.doctype, "reference_docname": ms.name, "parenttype": "Event"},
+	)
+
 
 def make_maintenance_schedule(**args):
 	ms = frappe.new_doc("Maintenance Schedule")
@@ -140,14 +178,17 @@
 	ms.customer = "_Test Customer"
 	ms.transaction_date = today()
 
-	ms.append("items", {
-		"item_code": args.get("item_code") or "_Test Item",
-		"start_date": today(),
-		"periodicity": "Weekly",
-		"no_of_visits": 4,
-		"serial_no": args.get("serial_no"),
-		"sales_person": "Sales Team",
-	})
+	ms.append(
+		"items",
+		{
+			"item_code": args.get("item_code") or "_Test Item",
+			"start_date": today(),
+			"periodicity": "Weekly",
+			"no_of_visits": 4,
+			"serial_no": args.get("serial_no"),
+			"sales_person": "Sales Team",
+		},
+	)
 	ms.insert(ignore_permissions=True)
 
 	return ms
diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
index 72686e7..e2f6cb3 100644
--- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
+++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
@@ -12,6 +12,9 @@
 		// filters for serial no based on item code
 		if (frm.doc.maintenance_type === "Scheduled") {
 			let item_code = frm.doc.purposes[0].item_code;
+			if (!item_code) {
+				return;
+			}
 			frappe.call({
 				method: "erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule.get_serial_nos_from_schedule",
 				args: {
diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py
index 6fe2466..66f4426 100644
--- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py
+++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py
@@ -14,23 +14,29 @@
 		return _("To {0}").format(self.customer_name)
 
 	def validate_serial_no(self):
-		for d in self.get('purposes'):
+		for d in self.get("purposes"):
 			if d.serial_no and not frappe.db.exists("Serial No", d.serial_no):
 				frappe.throw(_("Serial No {0} does not exist").format(d.serial_no))
 
 	def validate_purpose_table(self):
 		if not self.purposes:
-			frappe.throw(_("Add Items in the Purpose Table"), title="Purposes Required")
+			frappe.throw(_("Add Items in the Purpose Table"), title=_("Purposes Required"))
 
 	def validate_maintenance_date(self):
 		if self.maintenance_type == "Scheduled" and self.maintenance_schedule_detail:
-			item_ref = frappe.db.get_value('Maintenance Schedule Detail', self.maintenance_schedule_detail, 'item_reference')
+			item_ref = frappe.db.get_value(
+				"Maintenance Schedule Detail", self.maintenance_schedule_detail, "item_reference"
+			)
 			if item_ref:
-				start_date, end_date = frappe.db.get_value('Maintenance Schedule Item', item_ref, ['start_date', 'end_date'])
-				if get_datetime(self.mntc_date) < get_datetime(start_date) or get_datetime(self.mntc_date) > get_datetime(end_date):
-					frappe.throw(_("Date must be between {0} and {1}")
-						.format(format_date(start_date), format_date(end_date)))
-
+				start_date, end_date = frappe.db.get_value(
+					"Maintenance Schedule Item", item_ref, ["start_date", "end_date"]
+				)
+				if get_datetime(self.mntc_date) < get_datetime(start_date) or get_datetime(
+					self.mntc_date
+				) > get_datetime(end_date):
+					frappe.throw(
+						_("Date must be between {0} and {1}").format(format_date(start_date), format_date(end_date))
+					)
 
 	def validate(self):
 		self.validate_serial_no()
@@ -44,73 +50,87 @@
 			status = self.completion_status
 			actual_date = self.mntc_date
 		if self.maintenance_schedule_detail:
-			frappe.db.set_value('Maintenance Schedule Detail', self.maintenance_schedule_detail, 'completion_status', status)
-			frappe.db.set_value('Maintenance Schedule Detail', self.maintenance_schedule_detail, 'actual_date', actual_date)
+			frappe.db.set_value(
+				"Maintenance Schedule Detail", self.maintenance_schedule_detail, "completion_status", status
+			)
+			frappe.db.set_value(
+				"Maintenance Schedule Detail", self.maintenance_schedule_detail, "actual_date", actual_date
+			)
 
 	def update_customer_issue(self, flag):
 		if not self.maintenance_schedule:
-			for d in self.get('purposes'):
-				if d.prevdoc_docname and d.prevdoc_doctype == 'Warranty Claim' :
-					if flag==1:
+			for d in self.get("purposes"):
+				if d.prevdoc_docname and d.prevdoc_doctype == "Warranty Claim":
+					if flag == 1:
 						mntc_date = self.mntc_date
 						service_person = d.service_person
 						work_done = d.work_done
 						status = "Open"
-						if self.completion_status == 'Fully Completed':
-							status = 'Closed'
-						elif self.completion_status == 'Partially Completed':
-							status = 'Work In Progress'
+						if self.completion_status == "Fully Completed":
+							status = "Closed"
+						elif self.completion_status == "Partially Completed":
+							status = "Work In Progress"
 					else:
-						nm = frappe.db.sql("select t1.name, t1.mntc_date, t2.service_person, t2.work_done from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 where t2.parent = t1.name and t1.completion_status = 'Partially Completed' and t2.prevdoc_docname = %s and t1.name!=%s and t1.docstatus = 1 order by t1.name desc limit 1", (d.prevdoc_docname, self.name))
+						nm = frappe.db.sql(
+							"select t1.name, t1.mntc_date, t2.service_person, t2.work_done from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 where t2.parent = t1.name and t1.completion_status = 'Partially Completed' and t2.prevdoc_docname = %s and t1.name!=%s and t1.docstatus = 1 order by t1.name desc limit 1",
+							(d.prevdoc_docname, self.name),
+						)
 
 						if nm:
-							status = 'Work In Progress'
-							mntc_date = nm and nm[0][1] or ''
-							service_person = nm and nm[0][2] or ''
-							work_done = nm and nm[0][3] or ''
+							status = "Work In Progress"
+							mntc_date = nm and nm[0][1] or ""
+							service_person = nm and nm[0][2] or ""
+							work_done = nm and nm[0][3] or ""
 						else:
-							status = 'Open'
+							status = "Open"
 							mntc_date = None
 							service_person = None
 							work_done = None
 
-					wc_doc = frappe.get_doc('Warranty Claim', d.prevdoc_docname)
-					wc_doc.update({
-						'resolution_date': mntc_date,
-						'resolved_by': service_person,
-						'resolution_details': work_done,
-						'status': status
-					})
+					wc_doc = frappe.get_doc("Warranty Claim", d.prevdoc_docname)
+					wc_doc.update(
+						{
+							"resolution_date": mntc_date,
+							"resolved_by": service_person,
+							"resolution_details": work_done,
+							"status": status,
+						}
+					)
 
 					wc_doc.db_update()
 
 	def check_if_last_visit(self):
 		"""check if last maintenance visit against same sales order/ Warranty Claim"""
 		check_for_docname = None
-		for d in self.get('purposes'):
+		for d in self.get("purposes"):
 			if d.prevdoc_docname:
 				check_for_docname = d.prevdoc_docname
-				#check_for_doctype = d.prevdoc_doctype
+				# check_for_doctype = d.prevdoc_doctype
 
 		if check_for_docname:
-			check = frappe.db.sql("select t1.name from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 where t2.parent = t1.name and t1.name!=%s and t2.prevdoc_docname=%s and t1.docstatus = 1 and (t1.mntc_date > %s or (t1.mntc_date = %s and t1.mntc_time > %s))", (self.name, check_for_docname, self.mntc_date, self.mntc_date, self.mntc_time))
+			check = frappe.db.sql(
+				"select t1.name from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 where t2.parent = t1.name and t1.name!=%s and t2.prevdoc_docname=%s and t1.docstatus = 1 and (t1.mntc_date > %s or (t1.mntc_date = %s and t1.mntc_time > %s))",
+				(self.name, check_for_docname, self.mntc_date, self.mntc_date, self.mntc_time),
+			)
 
 			if check:
 				check_lst = [x[0] for x in check]
-				check_lst =','.join(check_lst)
-				frappe.throw(_("Cancel Material Visits {0} before cancelling this Maintenance Visit").format(check_lst))
+				check_lst = ",".join(check_lst)
+				frappe.throw(
+					_("Cancel Material Visits {0} before cancelling this Maintenance Visit").format(check_lst)
+				)
 				raise Exception
 			else:
 				self.update_customer_issue(0)
 
 	def on_submit(self):
 		self.update_customer_issue(1)
-		frappe.db.set(self, 'status', 'Submitted')
+		frappe.db.set(self, "status", "Submitted")
 		self.update_status_and_actual_date()
 
 	def on_cancel(self):
 		self.check_if_last_visit()
-		frappe.db.set(self, 'status', 'Cancelled')
+		frappe.db.set(self, "status", "Cancelled")
 		self.update_status_and_actual_date(cancel=True)
 
 	def on_update(self):
diff --git a/erpnext/maintenance/doctype/maintenance_visit/test_maintenance_visit.py b/erpnext/maintenance/doctype/maintenance_visit/test_maintenance_visit.py
index 6a9e70a..9c868c4 100644
--- a/erpnext/maintenance/doctype/maintenance_visit/test_maintenance_visit.py
+++ b/erpnext/maintenance/doctype/maintenance_visit/test_maintenance_visit.py
@@ -8,9 +8,11 @@
 
 # test_records = frappe.get_test_records('Maintenance Visit')
 
+
 class TestMaintenanceVisit(unittest.TestCase):
 	pass
 
+
 def make_maintenance_visit():
 	mv = frappe.new_doc("Maintenance Visit")
 	mv.company = "_Test Company"
@@ -20,22 +22,23 @@
 
 	sales_person = make_sales_person("Dwight Schrute")
 
-	mv.append("purposes", {
-		"item_code": "_Test Item",
-		"sales_person": "Sales Team",
-		"description": "Test Item",
-		"work_done": "Test Work Done",
-		"service_person": sales_person.name
-	})
+	mv.append(
+		"purposes",
+		{
+			"item_code": "_Test Item",
+			"sales_person": "Sales Team",
+			"description": "Test Item",
+			"work_done": "Test Work Done",
+			"service_person": sales_person.name,
+		},
+	)
 	mv.insert(ignore_permissions=True)
 
 	return mv
 
+
 def make_sales_person(name):
-	sales_person = frappe.get_doc({
-		'doctype': "Sales Person",
-		'sales_person_name': name
-	})
-	sales_person.insert(ignore_if_duplicate = True)
+	sales_person = frappe.get_doc({"doctype": "Sales Person", "sales_person_name": name})
+	sales_person.insert(ignore_if_duplicate=True)
 
 	return sales_person
diff --git a/erpnext/manufacturing/dashboard_fixtures.py b/erpnext/manufacturing/dashboard_fixtures.py
index 1bc12ff..9e64f4d 100644
--- a/erpnext/manufacturing/dashboard_fixtures.py
+++ b/erpnext/manufacturing/dashboard_fixtures.py
@@ -11,33 +11,39 @@
 
 
 def get_data():
-	return frappe._dict({
-		"dashboards": get_dashboards(),
-		"charts": get_charts(),
-		"number_cards": get_number_cards(),
-	})
+	return frappe._dict(
+		{
+			"dashboards": get_dashboards(),
+			"charts": get_charts(),
+			"number_cards": get_number_cards(),
+		}
+	)
+
 
 def get_dashboards():
-	return [{
-		"name": "Manufacturing",
-		"dashboard_name": "Manufacturing",
-		"charts": [
-			{ "chart": "Produced Quantity", "width": "Half" },
-			{ "chart": "Completed Operation", "width": "Half" },
-			{ "chart": "Work Order Analysis", "width": "Half" },
-			{ "chart": "Quality Inspection Analysis", "width": "Half" },
-			{ "chart": "Pending Work Order", "width": "Half" },
-			{ "chart": "Last Month Downtime Analysis", "width": "Half" },
-			{ "chart": "Work Order Qty Analysis", "width": "Full" },
-			{ "chart": "Job Card Analysis", "width": "Full" }
-		],
-		"cards": [
-			{ "card": "Monthly Total Work Order" },
-			{ "card": "Monthly Completed Work Order" },
-			{ "card": "Ongoing Job Card" },
-			{ "card": "Monthly Quality Inspection"}
-		]
-	}]
+	return [
+		{
+			"name": "Manufacturing",
+			"dashboard_name": "Manufacturing",
+			"charts": [
+				{"chart": "Produced Quantity", "width": "Half"},
+				{"chart": "Completed Operation", "width": "Half"},
+				{"chart": "Work Order Analysis", "width": "Half"},
+				{"chart": "Quality Inspection Analysis", "width": "Half"},
+				{"chart": "Pending Work Order", "width": "Half"},
+				{"chart": "Last Month Downtime Analysis", "width": "Half"},
+				{"chart": "Work Order Qty Analysis", "width": "Full"},
+				{"chart": "Job Card Analysis", "width": "Full"},
+			],
+			"cards": [
+				{"card": "Monthly Total Work Order"},
+				{"card": "Monthly Completed Work Order"},
+				{"card": "Ongoing Job Card"},
+				{"card": "Monthly Quality Inspection"},
+			],
+		}
+	]
+
 
 def get_charts():
 	company = erpnext.get_default_company()
@@ -45,200 +51,198 @@
 	if not company:
 		company = frappe.db.get_value("Company", {"is_group": 0}, "name")
 
-	return [{
-		"doctype": "Dashboard Chart",
-		"based_on": "modified",
-		"chart_type": "Sum",
-		"chart_name": _("Produced Quantity"),
-		"name": "Produced Quantity",
-		"document_type": "Work Order",
-		"filters_json": json.dumps([['Work Order', 'docstatus', '=', 1, False]]),
-		"group_by_type": "Count",
-		"time_interval": "Monthly",
-		"timespan": "Last Year",
-		"owner": "Administrator",
-		"type": "Line",
-		"value_based_on": "produced_qty",
-		"is_public": 1,
-		"timeseries": 1
-	}, {
-		"doctype": "Dashboard Chart",
-		"based_on": "creation",
-		"chart_type": "Sum",
-		"chart_name": _("Completed Operation"),
-		"name": "Completed Operation",
-		"document_type": "Work Order Operation",
-		"filters_json": json.dumps([['Work Order Operation', 'docstatus', '=', 1, False]]),
-		"group_by_type": "Count",
-		"time_interval": "Quarterly",
-		"timespan": "Last Year",
-		"owner": "Administrator",
-		"type": "Line",
-		"value_based_on": "completed_qty",
-		"is_public": 1,
-		"timeseries": 1
-	}, {
-		"doctype": "Dashboard Chart",
-		"time_interval": "Yearly",
-		"chart_type": "Report",
-		"chart_name": _("Work Order Analysis"),
-		"name": "Work Order Analysis",
-		"timespan": "Last Year",
-		"report_name": "Work Order Summary",
-		"owner": "Administrator",
-		"filters_json": json.dumps({"company": company, "charts_based_on": "Status"}),
-		"type": "Donut",
-		"is_public": 1,
-		"is_custom": 1,
-		"custom_options": json.dumps({
-			"axisOptions": {
-				"shortenYAxisNumbers": 1
-			},
-			"height": 300
-		}),
-	}, {
-		"doctype": "Dashboard Chart",
-		"time_interval": "Yearly",
-		"chart_type": "Report",
-		"chart_name": _("Quality Inspection Analysis"),
-		"name": "Quality Inspection Analysis",
-		"timespan": "Last Year",
-		"report_name": "Quality Inspection Summary",
-		"owner": "Administrator",
-		"filters_json": json.dumps({}),
-		"type": "Donut",
-		"is_public": 1,
-		"is_custom": 1,
-		"custom_options": json.dumps({
-			"axisOptions": {
-				"shortenYAxisNumbers": 1
-			},
-			"height": 300
-		}),
-	}, {
-		"doctype": "Dashboard Chart",
-		"time_interval": "Yearly",
-		"chart_type": "Report",
-		"chart_name": _("Pending Work Order"),
-		"name": "Pending Work Order",
-		"timespan": "Last Year",
-		"report_name": "Work Order Summary",
-		"filters_json": json.dumps({"company": company, "charts_based_on": "Age"}),
-		"owner": "Administrator",
-		"type": "Donut",
-		"is_public": 1,
-		"is_custom": 1,
-		"custom_options": json.dumps({
-			"axisOptions": {
-				"shortenYAxisNumbers": 1
-			},
-			"height": 300
-		}),
-	}, {
-		"doctype": "Dashboard Chart",
-		"time_interval": "Yearly",
-		"chart_type": "Report",
-		"chart_name": _("Last Month Downtime Analysis"),
-		"name": "Last Month Downtime Analysis",
-		"timespan": "Last Year",
-		"filters_json": json.dumps({}),
-		"report_name": "Downtime Analysis",
-		"owner": "Administrator",
-		"is_public": 1,
-		"is_custom": 1,
-		"type": "Bar"
-	}, {
-		"doctype": "Dashboard Chart",
-		"time_interval": "Yearly",
-		"chart_type": "Report",
-		"chart_name": _("Work Order Qty Analysis"),
-		"name": "Work Order Qty Analysis",
-		"timespan": "Last Year",
-		"report_name": "Work Order Summary",
-		"filters_json": json.dumps({"company": company, "charts_based_on": "Quantity"}),
-		"owner": "Administrator",
-		"type": "Bar",
-		"is_public": 1,
-		"is_custom": 1,
-		"custom_options": json.dumps({
-			"barOptions": { "stacked": 1 }
-		}),
-	}, {
-		"doctype": "Dashboard Chart",
-		"time_interval": "Yearly",
-		"chart_type": "Report",
-		"chart_name": _("Job Card Analysis"),
-		"name": "Job Card Analysis",
-		"timespan": "Last Year",
-		"report_name": "Job Card Summary",
-		"owner": "Administrator",
-		"is_public": 1,
-		"is_custom": 1,
-		"filters_json": json.dumps({"company": company, "docstatus": 1, "range":"Monthly"}),
-		"custom_options": json.dumps({
-			"barOptions": { "stacked": 1 }
-		}),
-		"type": "Bar"
-	}]
+	return [
+		{
+			"doctype": "Dashboard Chart",
+			"based_on": "modified",
+			"chart_type": "Sum",
+			"chart_name": _("Produced Quantity"),
+			"name": "Produced Quantity",
+			"document_type": "Work Order",
+			"filters_json": json.dumps([["Work Order", "docstatus", "=", 1, False]]),
+			"group_by_type": "Count",
+			"time_interval": "Monthly",
+			"timespan": "Last Year",
+			"owner": "Administrator",
+			"type": "Line",
+			"value_based_on": "produced_qty",
+			"is_public": 1,
+			"timeseries": 1,
+		},
+		{
+			"doctype": "Dashboard Chart",
+			"based_on": "creation",
+			"chart_type": "Sum",
+			"chart_name": _("Completed Operation"),
+			"name": "Completed Operation",
+			"document_type": "Work Order Operation",
+			"filters_json": json.dumps([["Work Order Operation", "docstatus", "=", 1, False]]),
+			"group_by_type": "Count",
+			"time_interval": "Quarterly",
+			"timespan": "Last Year",
+			"owner": "Administrator",
+			"type": "Line",
+			"value_based_on": "completed_qty",
+			"is_public": 1,
+			"timeseries": 1,
+		},
+		{
+			"doctype": "Dashboard Chart",
+			"time_interval": "Yearly",
+			"chart_type": "Report",
+			"chart_name": _("Work Order Analysis"),
+			"name": "Work Order Analysis",
+			"timespan": "Last Year",
+			"report_name": "Work Order Summary",
+			"owner": "Administrator",
+			"filters_json": json.dumps({"company": company, "charts_based_on": "Status"}),
+			"type": "Donut",
+			"is_public": 1,
+			"is_custom": 1,
+			"custom_options": json.dumps({"axisOptions": {"shortenYAxisNumbers": 1}, "height": 300}),
+		},
+		{
+			"doctype": "Dashboard Chart",
+			"time_interval": "Yearly",
+			"chart_type": "Report",
+			"chart_name": _("Quality Inspection Analysis"),
+			"name": "Quality Inspection Analysis",
+			"timespan": "Last Year",
+			"report_name": "Quality Inspection Summary",
+			"owner": "Administrator",
+			"filters_json": json.dumps({}),
+			"type": "Donut",
+			"is_public": 1,
+			"is_custom": 1,
+			"custom_options": json.dumps({"axisOptions": {"shortenYAxisNumbers": 1}, "height": 300}),
+		},
+		{
+			"doctype": "Dashboard Chart",
+			"time_interval": "Yearly",
+			"chart_type": "Report",
+			"chart_name": _("Pending Work Order"),
+			"name": "Pending Work Order",
+			"timespan": "Last Year",
+			"report_name": "Work Order Summary",
+			"filters_json": json.dumps({"company": company, "charts_based_on": "Age"}),
+			"owner": "Administrator",
+			"type": "Donut",
+			"is_public": 1,
+			"is_custom": 1,
+			"custom_options": json.dumps({"axisOptions": {"shortenYAxisNumbers": 1}, "height": 300}),
+		},
+		{
+			"doctype": "Dashboard Chart",
+			"time_interval": "Yearly",
+			"chart_type": "Report",
+			"chart_name": _("Last Month Downtime Analysis"),
+			"name": "Last Month Downtime Analysis",
+			"timespan": "Last Year",
+			"filters_json": json.dumps({}),
+			"report_name": "Downtime Analysis",
+			"owner": "Administrator",
+			"is_public": 1,
+			"is_custom": 1,
+			"type": "Bar",
+		},
+		{
+			"doctype": "Dashboard Chart",
+			"time_interval": "Yearly",
+			"chart_type": "Report",
+			"chart_name": _("Work Order Qty Analysis"),
+			"name": "Work Order Qty Analysis",
+			"timespan": "Last Year",
+			"report_name": "Work Order Summary",
+			"filters_json": json.dumps({"company": company, "charts_based_on": "Quantity"}),
+			"owner": "Administrator",
+			"type": "Bar",
+			"is_public": 1,
+			"is_custom": 1,
+			"custom_options": json.dumps({"barOptions": {"stacked": 1}}),
+		},
+		{
+			"doctype": "Dashboard Chart",
+			"time_interval": "Yearly",
+			"chart_type": "Report",
+			"chart_name": _("Job Card Analysis"),
+			"name": "Job Card Analysis",
+			"timespan": "Last Year",
+			"report_name": "Job Card Summary",
+			"owner": "Administrator",
+			"is_public": 1,
+			"is_custom": 1,
+			"filters_json": json.dumps({"company": company, "docstatus": 1, "range": "Monthly"}),
+			"custom_options": json.dumps({"barOptions": {"stacked": 1}}),
+			"type": "Bar",
+		},
+	]
+
 
 def get_number_cards():
 	start_date = add_months(nowdate(), -1)
 	end_date = nowdate()
 
-	return [{
-		"doctype": "Number Card",
-		"document_type": "Work Order",
-		"name": "Monthly Total Work Order",
-		"filters_json": json.dumps([
-			['Work Order', 'docstatus', '=', 1],
-			['Work Order', 'creation', 'between', [start_date, end_date]]
-		]),
-		"function": "Count",
-		"is_public": 1,
-		"label": _("Monthly Total Work Orders"),
-		"show_percentage_stats": 1,
-		"stats_time_interval": "Weekly"
-	},
-	{
-		"doctype": "Number Card",
-		"document_type": "Work Order",
-		"name": "Monthly Completed Work Order",
-		"filters_json": json.dumps([
-			['Work Order', 'status', '=', 'Completed'],
-			['Work Order', 'docstatus', '=', 1],
-			['Work Order', 'creation', 'between', [start_date, end_date]]
-		]),
-		"function": "Count",
-		"is_public": 1,
-		"label": _("Monthly Completed Work Orders"),
-		"show_percentage_stats": 1,
-		"stats_time_interval": "Weekly"
-	},
-	{
-		"doctype": "Number Card",
-		"document_type": "Job Card",
-		"name": "Ongoing Job Card",
-		"filters_json": json.dumps([
-			['Job Card', 'status','!=','Completed'],
-			['Job Card', 'docstatus', '=', 1]
-		]),
-		"function": "Count",
-		"is_public": 1,
-		"label": _("Ongoing Job Cards"),
-		"show_percentage_stats": 1,
-		"stats_time_interval": "Weekly"
-	},
-	{
-		"doctype": "Number Card",
-		"document_type": "Quality Inspection",
-		"name": "Monthly Quality Inspection",
-		"filters_json": json.dumps([
-			['Quality Inspection', 'docstatus', '=', 1],
-			['Quality Inspection', 'creation', 'between', [start_date, end_date]]
-		]),
-		"function": "Count",
-		"is_public": 1,
-		"label": _("Monthly Quality Inspections"),
-		"show_percentage_stats": 1,
-		"stats_time_interval": "Weekly"
-	}]
+	return [
+		{
+			"doctype": "Number Card",
+			"document_type": "Work Order",
+			"name": "Monthly Total Work Order",
+			"filters_json": json.dumps(
+				[
+					["Work Order", "docstatus", "=", 1],
+					["Work Order", "creation", "between", [start_date, end_date]],
+				]
+			),
+			"function": "Count",
+			"is_public": 1,
+			"label": _("Monthly Total Work Orders"),
+			"show_percentage_stats": 1,
+			"stats_time_interval": "Weekly",
+		},
+		{
+			"doctype": "Number Card",
+			"document_type": "Work Order",
+			"name": "Monthly Completed Work Order",
+			"filters_json": json.dumps(
+				[
+					["Work Order", "status", "=", "Completed"],
+					["Work Order", "docstatus", "=", 1],
+					["Work Order", "creation", "between", [start_date, end_date]],
+				]
+			),
+			"function": "Count",
+			"is_public": 1,
+			"label": _("Monthly Completed Work Orders"),
+			"show_percentage_stats": 1,
+			"stats_time_interval": "Weekly",
+		},
+		{
+			"doctype": "Number Card",
+			"document_type": "Job Card",
+			"name": "Ongoing Job Card",
+			"filters_json": json.dumps(
+				[["Job Card", "status", "!=", "Completed"], ["Job Card", "docstatus", "=", 1]]
+			),
+			"function": "Count",
+			"is_public": 1,
+			"label": _("Ongoing Job Cards"),
+			"show_percentage_stats": 1,
+			"stats_time_interval": "Weekly",
+		},
+		{
+			"doctype": "Number Card",
+			"document_type": "Quality Inspection",
+			"name": "Monthly Quality Inspection",
+			"filters_json": json.dumps(
+				[
+					["Quality Inspection", "docstatus", "=", 1],
+					["Quality Inspection", "creation", "between", [start_date, end_date]],
+				]
+			),
+			"function": "Count",
+			"is_public": 1,
+			"label": _("Monthly Quality Inspections"),
+			"show_percentage_stats": 1,
+			"stats_time_interval": "Weekly",
+		},
+	]
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
index 5340c51..ff21401 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
@@ -29,7 +29,9 @@
 
 	def update_ordered_qty(self):
 		ref_doctype = "Sales Order" if self.blanket_order_type == "Selling" else "Purchase Order"
-		item_ordered_qty = frappe._dict(frappe.db.sql("""
+		item_ordered_qty = frappe._dict(
+			frappe.db.sql(
+				"""
 			select trans_item.item_code, sum(trans_item.stock_qty) as qty
 			from `tab{0} Item` trans_item, `tab{0}` trans
 			where trans.name = trans_item.parent
@@ -37,18 +39,24 @@
 				and trans.docstatus=1
 				and trans.status not in ('Closed', 'Stopped')
 			group by trans_item.item_code
-		""".format(ref_doctype), self.name))
+		""".format(
+					ref_doctype
+				),
+				self.name,
+			)
+		)
 
 		for d in self.items:
 			d.db_set("ordered_qty", item_ordered_qty.get(d.item_code, 0))
 
+
 @frappe.whitelist()
 def make_order(source_name):
 	doctype = frappe.flags.args.doctype
 
 	def update_doc(source_doc, target_doc, source_parent):
-		if doctype == 'Quotation':
-			target_doc.quotation_to = 'Customer'
+		if doctype == "Quotation":
+			target_doc.quotation_to = "Customer"
 			target_doc.party_name = source_doc.customer
 
 	def update_item(source, target, source_parent):
@@ -62,18 +70,16 @@
 			target.against_blanket_order = 1
 			target.blanket_order = source_name
 
-	target_doc = get_mapped_doc("Blanket Order", source_name, {
-		"Blanket Order": {
-			"doctype": doctype,
-			"postprocess": update_doc
-		},
-		"Blanket Order Item": {
-			"doctype": doctype + " Item",
-			"field_map": {
-				"rate": "blanket_order_rate",
-				"parent": "blanket_order"
+	target_doc = get_mapped_doc(
+		"Blanket Order",
+		source_name,
+		{
+			"Blanket Order": {"doctype": doctype, "postprocess": update_doc},
+			"Blanket Order Item": {
+				"doctype": doctype + " Item",
+				"field_map": {"rate": "blanket_order_rate", "parent": "blanket_order"},
+				"postprocess": update_item,
 			},
-			"postprocess": update_item
-		}
-	})
+		},
+	)
 	return target_doc
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py
index c6745c8..3106234 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py
@@ -1,9 +1,5 @@
 def get_data():
 	return {
-		'fieldname': 'blanket_order',
-		'transactions': [
-			{
-				'items': ['Purchase Order', 'Sales Order', 'Quotation']
-			}
-		]
+		"fieldname": "blanket_order",
+		"transactions": [{"items": ["Purchase Order", "Sales Order", "Quotation"]}],
 	}
diff --git a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
index d4d337d..2f1f3ae 100644
--- a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
+++ b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
@@ -16,7 +16,7 @@
 	def test_sales_order_creation(self):
 		bo = make_blanket_order(blanket_order_type="Selling")
 
-		frappe.flags.args.doctype = 'Sales Order'
+		frappe.flags.args.doctype = "Sales Order"
 		so = make_order(bo.name)
 		so.currency = get_company_currency(so.company)
 		so.delivery_date = today()
@@ -33,16 +33,15 @@
 		self.assertEqual(so.items[0].qty, bo.items[0].ordered_qty)
 
 		# test the quantity
-		frappe.flags.args.doctype = 'Sales Order'
+		frappe.flags.args.doctype = "Sales Order"
 		so1 = make_order(bo.name)
 		so1.currency = get_company_currency(so1.company)
-		self.assertEqual(so1.items[0].qty, (bo.items[0].qty-bo.items[0].ordered_qty))
-
+		self.assertEqual(so1.items[0].qty, (bo.items[0].qty - bo.items[0].ordered_qty))
 
 	def test_purchase_order_creation(self):
 		bo = make_blanket_order(blanket_order_type="Purchasing")
 
-		frappe.flags.args.doctype = 'Purchase Order'
+		frappe.flags.args.doctype = "Purchase Order"
 		po = make_order(bo.name)
 		po.currency = get_company_currency(po.company)
 		po.schedule_date = today()
@@ -59,11 +58,10 @@
 		self.assertEqual(po.items[0].qty, bo.items[0].ordered_qty)
 
 		# test the quantity
-		frappe.flags.args.doctype = 'Purchase Order'
+		frappe.flags.args.doctype = "Purchase Order"
 		po1 = make_order(bo.name)
 		po1.currency = get_company_currency(po1.company)
-		self.assertEqual(po1.items[0].qty, (bo.items[0].qty-bo.items[0].ordered_qty))
-
+		self.assertEqual(po1.items[0].qty, (bo.items[0].qty - bo.items[0].ordered_qty))
 
 
 def make_blanket_order(**args):
@@ -80,11 +78,14 @@
 	bo.from_date = today()
 	bo.to_date = add_months(bo.from_date, months=12)
 
-	bo.append("items", {
-		"item_code": args.item_code or "_Test Item",
-		"qty": args.quantity or 1000,
-		"rate": args.rate or 100
-	})
+	bo.append(
+		"items",
+		{
+			"item_code": args.item_code or "_Test Item",
+			"qty": args.quantity or 1000,
+			"rate": args.rate or 100,
+		},
+	)
 
 	bo.insert()
 	bo.submit()
diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js
index 8a7634e..ecad41f 100644
--- a/erpnext/manufacturing/doctype/bom/bom.js
+++ b/erpnext/manufacturing/doctype/bom/bom.js
@@ -81,7 +81,7 @@
 			}
 		)
 
-		if (!frm.doc.__islocal && frm.doc.docstatus<2) {
+		if (!frm.is_new() && frm.doc.docstatus<2) {
 			frm.add_custom_button(__("Update Cost"), function() {
 				frm.events.update_cost(frm, true);
 			});
@@ -93,6 +93,13 @@
 			});
 		}
 
+		if (!frm.is_new() && !frm.doc.docstatus == 0) {
+			frm.add_custom_button(__("New Version"), function() {
+				let new_bom = frappe.model.copy_doc(frm.doc);
+				frappe.set_route("Form", "BOM", new_bom.name);
+			});
+		}
+
 		if(frm.doc.docstatus==1) {
 			frm.add_custom_button(__("Work Order"), function() {
 				frm.trigger("make_work_order");
@@ -499,15 +506,11 @@
 
 cur_frm.cscript.rate = function(doc, cdt, cdn) {
 	var d = locals[cdt][cdn];
-	var scrap_items = false;
-
-	if(cdt == 'BOM Scrap Item') {
-		scrap_items = true;
-	}
+	const is_scrap_item = cdt == "BOM Scrap Item";
 
 	if (d.bom_no) {
 		frappe.msgprint(__("You cannot change the rate if BOM is mentioned against any Item."));
-		get_bom_material_detail(doc, cdt, cdn, scrap_items);
+		get_bom_material_detail(doc, cdt, cdn, is_scrap_item);
 	} else {
 		erpnext.bom.calculate_rm_cost(doc);
 		erpnext.bom.calculate_scrap_materials_cost(doc);
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 37d2b9f..4c88eca 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -1,10 +1,11 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 
 import functools
+import re
 from collections import deque
 from operator import itemgetter
-from typing import List
+from typing import Dict, List
 
 import frappe
 from frappe import _
@@ -18,9 +19,11 @@
 from erpnext.stock.doctype.item.item import get_item_details
 from erpnext.stock.get_item_details import get_conversion_factor, get_price_list_rate
 
-form_grid_templates = {
-	"items": "templates/form_grid/item_grid.html"
-}
+form_grid_templates = {"items": "templates/form_grid/item_grid.html"}
+
+
+class BOMRecursionError(frappe.ValidationError):
+	pass
 
 
 class BOMTree:
@@ -30,10 +33,12 @@
 	# ref: https://docs.python.org/3/reference/datamodel.html#slots
 	__slots__ = ["name", "child_items", "is_bom", "item_code", "exploded_qty", "qty"]
 
-	def __init__(self, name: str, is_bom: bool = True, exploded_qty: float = 1.0, qty: float = 1) -> None:
+	def __init__(
+		self, name: str, is_bom: bool = True, exploded_qty: float = 1.0, qty: float = 1
+	) -> None:
 		self.name = name  # name of node, BOM number if is_bom else item_code
 		self.child_items: List["BOMTree"] = []  # list of child items
-		self.is_bom = is_bom   # true if the node is a BOM and not a leaf item
+		self.is_bom = is_bom  # true if the node is a BOM and not a leaf item
 		self.item_code: str = None  # item_code associated with node
 		self.qty = qty  # required unit quantity to make one unit of parent item.
 		self.exploded_qty = exploded_qty  # total exploded qty required for making root of tree.
@@ -61,12 +66,12 @@
 		"""Get level order traversal of tree.
 		E.g. for following tree the traversal will return list of nodes in order from top to bottom.
 		BOM:
-			- SubAssy1
-				- item1
-				- item2
-			- SubAssy2
-				- item3
-			- item4
+		        - SubAssy1
+		                - item1
+		                - item2
+		        - SubAssy2
+		                - item3
+		        - item4
 
 		returns = [SubAssy1, item1, item2, SubAssy2, item3, item4]
 		"""
@@ -95,52 +100,84 @@
 			rep += child.__repr__(level=level + 1)
 		return rep
 
+
 class BOM(WebsiteGenerator):
 	website = frappe._dict(
 		# page_title_field = "item_name",
-		condition_field = "show_in_website",
-		template = "templates/generators/bom.html"
+		condition_field="show_in_website",
+		template="templates/generators/bom.html",
 	)
 
 	def autoname(self):
-		names = frappe.db.sql_list("""select name from `tabBOM` where item=%s""", self.item)
+		# ignore amended documents while calculating current index
+		existing_boms = frappe.get_all(
+			"BOM", filters={"item": self.item, "amended_from": ["is", "not set"]}, pluck="name"
+		)
 
-		if names:
-			# name can be BOM/ITEM/001, BOM/ITEM/001-1, BOM-ITEM-001, BOM-ITEM-001-1
-
-			# split by item
-			names = [name.split(self.item, 1) for name in names]
-			names = [d[-1][1:] for d in filter(lambda x: len(x) > 1 and x[-1], names)]
-
-			# split by (-) if cancelled
-			if names:
-				names = [cint(name.split('-')[-1]) for name in names]
-				idx = max(names) + 1
-			else:
-				idx = 1
+		if existing_boms:
+			index = self.get_next_version_index(existing_boms)
 		else:
-			idx = 1
+			index = 1
 
-		name = 'BOM-' + self.item + ('-%.3i' % idx)
+		prefix = self.doctype
+		suffix = "%.3i" % index  # convert index to string (1 -> "001")
+		bom_name = f"{prefix}-{self.item}-{suffix}"
+
+		if len(bom_name) <= 140:
+			name = bom_name
+		else:
+			# since max characters for name is 140, remove enough characters from the
+			# item name to fit the prefix, suffix and the separators
+			truncated_length = 140 - (len(prefix) + len(suffix) + 2)
+			truncated_item_name = self.item[:truncated_length]
+			# if a partial word is found after truncate, remove the extra characters
+			truncated_item_name = truncated_item_name.rsplit(" ", 1)[0]
+			name = f"{prefix}-{truncated_item_name}-{suffix}"
+
 		if frappe.db.exists("BOM", name):
 			conflicting_bom = frappe.get_doc("BOM", name)
 
 			if conflicting_bom.item != self.item:
-				msg = (_("A BOM with name {0} already exists for item {1}.")
-					.format(frappe.bold(name), frappe.bold(conflicting_bom.item)))
+				msg = _("A BOM with name {0} already exists for item {1}.").format(
+					frappe.bold(name), frappe.bold(conflicting_bom.item)
+				)
 
-				frappe.throw(_("{0}{1} Did you rename the item? Please contact Administrator / Tech support")
-					.format(msg, "<br>"))
+				frappe.throw(
+					_("{0}{1} Did you rename the item? Please contact Administrator / Tech support").format(
+						msg, "<br>"
+					)
+				)
 
 		self.name = name
 
+	@staticmethod
+	def get_next_version_index(existing_boms: List[str]) -> int:
+		# split by "/" and "-"
+		delimiters = ["/", "-"]
+		pattern = "|".join(map(re.escape, delimiters))
+		bom_parts = [re.split(pattern, bom_name) for bom_name in existing_boms]
+
+		# filter out BOMs that do not follow the following formats: BOM/ITEM/001, BOM-ITEM-001
+		valid_bom_parts = list(filter(lambda x: len(x) > 1 and x[-1], bom_parts))
+
+		# extract the current index from the BOM parts
+		if valid_bom_parts:
+			# handle cancelled and submitted documents
+			indexes = [cint(part[-1]) for part in valid_bom_parts]
+			index = max(indexes) + 1
+		else:
+			index = 1
+
+		return index
+
 	def validate(self):
-		self.route = frappe.scrub(self.name).replace('_', '-')
+		self.route = frappe.scrub(self.name).replace("_", "-")
 
 		if not self.company:
 			frappe.throw(_("Please select a Company first."), title=_("Mandatory"))
 
 		self.clear_operations()
+		self.clear_inspection()
 		self.validate_main_item()
 		self.validate_currency()
 		self.set_conversion_rate()
@@ -152,16 +189,17 @@
 		self.validate_transfer_against()
 		self.set_routing_operations()
 		self.validate_operations()
+		self.update_exploded_items(save=False)
 		self.calculate_cost()
 		self.update_stock_qty()
-		self.update_cost(update_parent=False, from_child_bom=True, update_hour_rate = False, save=False)
+		self.update_cost(update_parent=False, from_child_bom=True, update_hour_rate=False, save=False)
 		self.validate_scrap_items()
 
 	def get_context(self, context):
-		context.parents = [{'name': 'boms', 'title': _('All BOMs') }]
+		context.parents = [{"name": "boms", "title": _("All BOMs")}]
 
 	def on_update(self):
-		frappe.cache().hdel('bom_children', self.name)
+		frappe.cache().hdel("bom_children", self.name)
 		self.check_recursion()
 
 	def on_submit(self):
@@ -191,36 +229,52 @@
 	def get_routing(self):
 		if self.routing:
 			self.set("operations", [])
-			fields = ["sequence_id", "operation", "workstation", "description",
-				"time_in_mins", "batch_size", "operating_cost", "idx", "hour_rate"]
+			fields = [
+				"sequence_id",
+				"operation",
+				"workstation",
+				"description",
+				"time_in_mins",
+				"batch_size",
+				"operating_cost",
+				"idx",
+				"hour_rate",
+				"set_cost_based_on_bom_qty",
+				"fixed_time",
+			]
 
-			for row in frappe.get_all("BOM Operation", fields = fields,
-				filters = {'parenttype': 'Routing', 'parent': self.routing}, order_by="sequence_id, idx"):
-				child = self.append('operations', row)
-				child.hour_rate = flt(row.hour_rate / self.conversion_rate, 2)
+			for row in frappe.get_all(
+				"BOM Operation",
+				fields=fields,
+				filters={"parenttype": "Routing", "parent": self.routing},
+				order_by="sequence_id, idx",
+			):
+				child = self.append("operations", row)
+				child.hour_rate = flt(row.hour_rate / self.conversion_rate, child.precision("hour_rate"))
 
 	def set_bom_material_details(self):
 		for item in self.get("items"):
 			self.validate_bom_currency(item)
 
-			item.bom_no = ''
-			if not item.do_not_explode:
-				item.bom_no = item.bom_no
+			if item.do_not_explode:
+				item.bom_no = ""
 
-			ret = self.get_bom_material_detail({
-				"company": self.company,
-				"item_code": item.item_code,
-				"item_name": item.item_name,
-				"bom_no": item.bom_no,
-				"stock_qty": item.stock_qty,
-				"include_item_in_manufacturing": item.include_item_in_manufacturing,
-				"qty": item.qty,
-				"uom": item.uom,
-				"stock_uom": item.stock_uom,
-				"conversion_factor": item.conversion_factor,
-				"sourced_by_supplier": item.sourced_by_supplier,
-				"do_not_explode": item.do_not_explode
-			})
+			ret = self.get_bom_material_detail(
+				{
+					"company": self.company,
+					"item_code": item.item_code,
+					"item_name": item.item_name,
+					"bom_no": item.bom_no,
+					"stock_qty": item.stock_qty,
+					"include_item_in_manufacturing": item.include_item_in_manufacturing,
+					"qty": item.qty,
+					"uom": item.uom,
+					"stock_uom": item.stock_uom,
+					"conversion_factor": item.conversion_factor,
+					"sourced_by_supplier": item.sourced_by_supplier,
+					"do_not_explode": item.do_not_explode,
+				}
+			)
 
 			for r in ret:
 				if not item.get(r):
@@ -232,7 +286,7 @@
 				"item_code": item.item_code,
 				"company": self.company,
 				"scrap_items": True,
-				"bom_no": '',
+				"bom_no": "",
 			}
 			ret = self.get_bom_material_detail(args)
 			for key, value in ret.items():
@@ -241,116 +295,113 @@
 
 	@frappe.whitelist()
 	def get_bom_material_detail(self, args=None):
-		""" Get raw material details like uom, desc and rate"""
+		"""Get raw material details like uom, desc and rate"""
 		if not args:
-			args = frappe.form_dict.get('args')
+			args = frappe.form_dict.get("args")
 
 		if isinstance(args, str):
 			import json
+
 			args = json.loads(args)
 
-		item = self.get_item_det(args['item_code'])
+		item = self.get_item_det(args["item_code"])
 
-		args['bom_no'] = args['bom_no'] or item and cstr(item['default_bom']) or ''
-		args['transfer_for_manufacture'] = (cstr(args.get('include_item_in_manufacturing', '')) or
-			item and item.include_item_in_manufacturing or 0)
+		args["bom_no"] = args["bom_no"] or item and cstr(item["default_bom"]) or ""
+		args["transfer_for_manufacture"] = (
+			cstr(args.get("include_item_in_manufacturing", ""))
+			or item
+			and item.include_item_in_manufacturing
+			or 0
+		)
 		args.update(item)
 
 		rate = self.get_rm_rate(args)
 		ret_item = {
-			 'item_name'	: item and args['item_name'] or '',
-			 'description'  : item and args['description'] or '',
-			 'image'		: item and args['image'] or '',
-			 'stock_uom'	: item and args['stock_uom'] or '',
-			 'uom'			: item and args['stock_uom'] or '',
- 			 'conversion_factor': 1,
-			 'bom_no'		: args['bom_no'],
-			 'rate'			: rate,
-			 'qty'			: args.get("qty") or args.get("stock_qty") or 1,
-			 'stock_qty'	: args.get("qty") or args.get("stock_qty") or 1,
-			 'base_rate'	: flt(rate) * (flt(self.conversion_rate) or 1),
-			 'include_item_in_manufacturing': cint(args.get('transfer_for_manufacture')),
-			 'sourced_by_supplier'		: args.get('sourced_by_supplier', 0)
+			"item_name": item and args["item_name"] or "",
+			"description": item and args["description"] or "",
+			"image": item and args["image"] or "",
+			"stock_uom": item and args["stock_uom"] or "",
+			"uom": item and args["stock_uom"] or "",
+			"conversion_factor": 1,
+			"bom_no": args["bom_no"],
+			"rate": rate,
+			"qty": args.get("qty") or args.get("stock_qty") or 1,
+			"stock_qty": args.get("qty") or args.get("stock_qty") or 1,
+			"base_rate": flt(rate) * (flt(self.conversion_rate) or 1),
+			"include_item_in_manufacturing": cint(args.get("transfer_for_manufacture")),
+			"sourced_by_supplier": args.get("sourced_by_supplier", 0),
 		}
 
-		if args.get('do_not_explode'):
-			ret_item['bom_no'] = ''
+		if args.get("do_not_explode"):
+			ret_item["bom_no"] = ""
 
 		return ret_item
 
 	def validate_bom_currency(self, item):
-		if item.get('bom_no') and frappe.db.get_value('BOM', item.get('bom_no'), 'currency') != self.currency:
-			frappe.throw(_("Row {0}: Currency of the BOM #{1} should be equal to the selected currency {2}")
-				.format(item.idx, item.bom_no, self.currency))
+		if (
+			item.get("bom_no")
+			and frappe.db.get_value("BOM", item.get("bom_no"), "currency") != self.currency
+		):
+			frappe.throw(
+				_("Row {0}: Currency of the BOM #{1} should be equal to the selected currency {2}").format(
+					item.idx, item.bom_no, self.currency
+				)
+			)
 
 	def get_rm_rate(self, arg):
-		"""	Get raw material rate as per selected method, if bom exists takes bom cost """
+		"""Get raw material rate as per selected method, if bom exists takes bom cost"""
 		rate = 0
 		if not self.rm_cost_as_per:
 			self.rm_cost_as_per = "Valuation Rate"
 
-		if arg.get('scrap_items'):
+		if arg.get("scrap_items"):
 			rate = get_valuation_rate(arg)
 		elif arg:
-			#Customer Provided parts and Supplier sourced parts will have zero rate
-			if not frappe.db.get_value('Item', arg["item_code"], 'is_customer_provided_item') and not arg.get('sourced_by_supplier'):
-				if arg.get('bom_no') and self.set_rate_of_sub_assembly_item_based_on_bom:
-					rate = flt(self.get_bom_unitcost(arg['bom_no'])) * (arg.get("conversion_factor") or 1)
+			# Customer Provided parts and Supplier sourced parts will have zero rate
+			if not frappe.db.get_value(
+				"Item", arg["item_code"], "is_customer_provided_item"
+			) and not arg.get("sourced_by_supplier"):
+				if arg.get("bom_no") and self.set_rate_of_sub_assembly_item_based_on_bom:
+					rate = flt(self.get_bom_unitcost(arg["bom_no"])) * (arg.get("conversion_factor") or 1)
 				else:
 					rate = get_bom_item_rate(arg, self)
 
 					if not rate:
 						if self.rm_cost_as_per == "Price List":
-							frappe.msgprint(_("Price not found for item {0} in price list {1}")
-								.format(arg["item_code"], self.buying_price_list), alert=True)
+							frappe.msgprint(
+								_("Price not found for item {0} in price list {1}").format(
+									arg["item_code"], self.buying_price_list
+								),
+								alert=True,
+							)
 						else:
-							frappe.msgprint(_("{0} not found for item {1}")
-								.format(self.rm_cost_as_per, arg["item_code"]), alert=True)
+							frappe.msgprint(
+								_("{0} not found for item {1}").format(self.rm_cost_as_per, arg["item_code"]), alert=True
+							)
 		return flt(rate) * flt(self.plc_conversion_rate or 1) / (self.conversion_rate or 1)
 
 	@frappe.whitelist()
-	def update_cost(self, update_parent=True, from_child_bom=False, update_hour_rate = True, save=True):
+	def update_cost(self, update_parent=True, from_child_bom=False, update_hour_rate=True, save=True):
 		if self.docstatus == 2:
 			return
 
 		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,
-				"bom_no": d.bom_no,
-				"qty": d.qty,
-				"uom": d.uom,
-				"stock_uom": d.stock_uom,
-				"conversion_factor": d.conversion_factor,
-				"sourced_by_supplier": d.sourced_by_supplier
-			})
-
-			if rate:
-				d.rate = rate
-			d.amount = flt(d.rate) * flt(d.qty)
-			d.base_rate = flt(d.rate) * flt(self.conversion_rate)
-			d.base_amount = flt(d.amount) * flt(self.conversion_rate)
-
-			if save:
-				d.db_update()
-
 		if self.docstatus == 1:
 			self.flags.ignore_validate_update_after_submit = True
-			self.calculate_cost(update_hour_rate)
+
+		self.calculate_cost(save_updates=save, update_hour_rate=update_hour_rate)
+
 		if save:
 			self.db_update()
 
-		self.update_exploded_items(save=save)
-
 		# update parent BOMs
 		if self.total_cost != existing_bom_cost and update_parent:
-			parent_boms = frappe.db.sql_list("""select distinct parent from `tabBOM Item`
-				where bom_no = %s and docstatus=1 and parenttype='BOM'""", self.name)
+			parent_boms = frappe.db.sql_list(
+				"""select distinct parent from `tabBOM Item`
+				where bom_no = %s and docstatus=1 and parenttype='BOM'""",
+				self.name,
+			)
 
 			for bom in parent_boms:
 				frappe.get_doc("BOM", bom).update_cost(from_child_bom=True)
@@ -362,41 +413,54 @@
 		if self.total_cost:
 			cost = self.total_cost / self.quantity
 
-			frappe.db.sql("""update `tabBOM Item` set rate=%s, amount=stock_qty*%s
+			frappe.db.sql(
+				"""update `tabBOM Item` set rate=%s, amount=stock_qty*%s
 				where bom_no = %s and docstatus < 2 and parenttype='BOM'""",
-				(cost, cost, self.name))
+				(cost, cost, self.name),
+			)
 
 	def get_bom_unitcost(self, bom_no):
-		bom = frappe.db.sql("""select name, base_total_cost/quantity as unit_cost from `tabBOM`
-			where is_active = 1 and name = %s""", bom_no, as_dict=1)
-		return bom and bom[0]['unit_cost'] or 0
+		bom = frappe.db.sql(
+			"""select name, base_total_cost/quantity as unit_cost from `tabBOM`
+			where is_active = 1 and name = %s""",
+			bom_no,
+			as_dict=1,
+		)
+		return bom and bom[0]["unit_cost"] or 0
 
 	def manage_default_bom(self):
-		""" Uncheck others if current one is selected as default or
-			check the current one as default if it the only bom for the selected item,
-			update default bom in item master
+		"""Uncheck others if current one is selected as default or
+		check the current one as default if it the only bom for the selected item,
+		update default bom in item master
 		"""
 		if self.is_default and self.is_active:
 			from frappe.model.utils import set_default
+
 			set_default(self, "item")
 			item = frappe.get_doc("Item", self.item)
 			if item.default_bom != self.name:
-				frappe.db.set_value('Item', self.item, 'default_bom', self.name)
-		elif not frappe.db.exists(dict(doctype='BOM', docstatus=1, item=self.item, is_default=1)) \
-			and self.is_active:
+				frappe.db.set_value("Item", self.item, "default_bom", self.name)
+		elif (
+			not frappe.db.exists(dict(doctype="BOM", docstatus=1, item=self.item, is_default=1))
+			and self.is_active
+		):
 			frappe.db.set(self, "is_default", 1)
 		else:
 			frappe.db.set(self, "is_default", 0)
 			item = frappe.get_doc("Item", self.item)
 			if item.default_bom == self.name:
-				frappe.db.set_value('Item', self.item, 'default_bom', None)
+				frappe.db.set_value("Item", self.item, "default_bom", None)
 
 	def clear_operations(self):
 		if not self.with_operations:
-			self.set('operations', [])
+			self.set("operations", [])
+
+	def clear_inspection(self):
+		if not self.inspection_required:
+			self.quality_inspection_template = None
 
 	def validate_main_item(self):
-		""" Validate main FG item"""
+		"""Validate main FG item"""
 		item = self.get_item_det(self.item)
 		if not item:
 			frappe.throw(_("Item {0} does not exist in the system or has expired").format(self.item))
@@ -404,30 +468,34 @@
 			ret = frappe.db.get_value("Item", self.item, ["description", "stock_uom", "item_name"])
 			self.description = ret[0]
 			self.uom = ret[1]
-			self.item_name= ret[2]
+			self.item_name = ret[2]
 
 		if not self.quantity:
 			frappe.throw(_("Quantity should be greater than 0"))
 
 	def validate_currency(self):
-		if self.rm_cost_as_per == 'Price List':
-			price_list_currency = frappe.db.get_value('Price List', self.buying_price_list, 'currency')
+		if self.rm_cost_as_per == "Price List":
+			price_list_currency = frappe.db.get_value("Price List", self.buying_price_list, "currency")
 			if price_list_currency not in (self.currency, self.company_currency()):
-				frappe.throw(_("Currency of the price list {0} must be {1} or {2}")
-					.format(self.buying_price_list, self.currency, self.company_currency()))
+				frappe.throw(
+					_("Currency of the price list {0} must be {1} or {2}").format(
+						self.buying_price_list, self.currency, self.company_currency()
+					)
+				)
 
 	def update_stock_qty(self):
-		for m in self.get('items'):
+		for m in self.get("items"):
 			if not m.conversion_factor:
-				m.conversion_factor = flt(get_conversion_factor(m.item_code, m.uom)['conversion_factor'])
+				m.conversion_factor = flt(get_conversion_factor(m.item_code, m.uom)["conversion_factor"])
 			if m.uom and m.qty:
-				m.stock_qty = flt(m.conversion_factor)*flt(m.qty)
+				m.stock_qty = flt(m.conversion_factor) * flt(m.qty)
 			if not m.uom and m.stock_uom:
 				m.uom = m.stock_uom
 				m.qty = m.stock_qty
 
 	def validate_uom_is_interger(self):
 		from erpnext.utilities.transaction_base import validate_uom_is_integer
+
 		validate_uom_is_integer(self, "uom", "qty", "BOM Item")
 		validate_uom_is_integer(self, "stock_uom", "stock_qty", "BOM Item")
 
@@ -435,23 +503,26 @@
 		if self.currency == self.company_currency():
 			self.conversion_rate = 1
 		elif self.conversion_rate == 1 or flt(self.conversion_rate) <= 0:
-			self.conversion_rate = get_exchange_rate(self.currency, self.company_currency(), args="for_buying")
+			self.conversion_rate = get_exchange_rate(
+				self.currency, self.company_currency(), args="for_buying"
+			)
 
 	def set_plc_conversion_rate(self):
 		if self.rm_cost_as_per in ["Valuation Rate", "Last Purchase Rate"]:
 			self.plc_conversion_rate = 1
 		elif not self.plc_conversion_rate and self.price_list_currency:
-			self.plc_conversion_rate = get_exchange_rate(self.price_list_currency,
-				self.company_currency(), args="for_buying")
+			self.plc_conversion_rate = get_exchange_rate(
+				self.price_list_currency, self.company_currency(), args="for_buying"
+			)
 
 	def validate_materials(self):
-		""" Validate raw material entries """
+		"""Validate raw material entries"""
 
-		if not self.get('items'):
+		if not self.get("items"):
 			frappe.throw(_("Raw Materials cannot be blank."))
 
 		check_list = []
-		for m in self.get('items'):
+		for m in self.get("items"):
 			if m.bom_no:
 				validate_bom_no(m.item_code, m.bom_no)
 			if flt(m.qty) <= 0:
@@ -459,36 +530,42 @@
 			check_list.append(m)
 
 	def check_recursion(self, bom_list=None):
-		""" Check whether recursion occurs in any bom"""
+		"""Check whether recursion occurs in any bom"""
+
 		def _throw_error(bom_name):
-			frappe.throw(_("BOM recursion: {0} cannot be parent or child of {0}").format(bom_name))
+			frappe.throw(
+				_("BOM recursion: {1} cannot be parent or child of {0}").format(self.name, bom_name),
+				exc=BOMRecursionError,
+			)
 
 		bom_list = self.traverse_tree()
-		child_items = frappe.get_all('BOM Item', fields=["bom_no", "item_code"],
-			filters={'parent': ('in', bom_list), 'parenttype': 'BOM'}) or []
+		child_items = frappe.get_all(
+			"BOM Item",
+			fields=["bom_no", "item_code"],
+			filters={"parent": ("in", bom_list), "parenttype": "BOM"},
+		)
 
-		child_bom = {d.bom_no for d in child_items}
-		child_items_codes = {d.item_code for d in child_items}
+		for item in child_items:
+			if self.name == item.bom_no:
+				_throw_error(self.name)
+			if self.item == item.item_code and item.bom_no:
+				# Same item but with different BOM should not be allowed.
+				# Same item can appear recursively once as long as it doesn't have BOM.
+				_throw_error(item.bom_no)
 
-		if self.name in child_bom:
-			_throw_error(self.name)
-
-		if self.item in child_items_codes:
-			_throw_error(self.item)
-
-		bom_nos = frappe.get_all('BOM Item', fields=["parent"],
-			filters={'bom_no': self.name, 'parenttype': 'BOM'}) or []
-
-		if self.name in {d.parent for d in bom_nos}:
+		if self.name in {d.bom_no for d in self.items}:
 			_throw_error(self.name)
 
 	def traverse_tree(self, bom_list=None):
 		def _get_children(bom_no):
-			children = frappe.cache().hget('bom_children', bom_no)
+			children = frappe.cache().hget("bom_children", bom_no)
 			if children is None:
-				children = frappe.db.sql_list("""SELECT `bom_no` FROM `tabBOM Item`
-					WHERE `parent`=%s AND `bom_no`!='' AND `parenttype`='BOM'""", bom_no)
-				frappe.cache().hset('bom_children', bom_no, children)
+				children = frappe.db.sql_list(
+					"""SELECT `bom_no` FROM `tabBOM Item`
+					WHERE `parent`=%s AND `bom_no`!='' AND `parenttype`='BOM'""",
+					bom_no,
+				)
+				frappe.cache().hset("bom_children", bom_no, children)
 			return children
 
 		count = 0
@@ -498,7 +575,7 @@
 		if self.name not in bom_list:
 			bom_list.append(self.name)
 
-		while(count < len(bom_list)):
+		while count < len(bom_list):
 			for child_bom in _get_children(bom_list[count]):
 				if child_bom not in bom_list:
 					bom_list.append(child_bom)
@@ -506,19 +583,25 @@
 		bom_list.reverse()
 		return bom_list
 
-	def calculate_cost(self, update_hour_rate = False):
+	def calculate_cost(self, save_updates=False, update_hour_rate=False):
 		"""Calculate bom totals"""
 		self.calculate_op_cost(update_hour_rate)
-		self.calculate_rm_cost()
-		self.calculate_sm_cost()
-		self.total_cost = self.operating_cost + self.raw_material_cost - self.scrap_material_cost
-		self.base_total_cost = self.base_operating_cost + self.base_raw_material_cost - self.base_scrap_material_cost
+		self.calculate_rm_cost(save=save_updates)
+		self.calculate_sm_cost(save=save_updates)
+		if save_updates:
+			# not via doc event, table is not regenerated and needs updation
+			self.calculate_exploded_cost()
 
-	def calculate_op_cost(self, update_hour_rate = False):
+		self.total_cost = self.operating_cost + self.raw_material_cost - self.scrap_material_cost
+		self.base_total_cost = (
+			self.base_operating_cost + self.base_raw_material_cost - self.base_scrap_material_cost
+		)
+
+	def calculate_op_cost(self, update_hour_rate=False):
 		"""Update workstation rate and calculates totals"""
 		self.operating_cost = 0
 		self.base_operating_cost = 0
-		for d in self.get('operations'):
+		for d in self.get("operations"):
 			if d.workstation:
 				self.update_rate_and_time(d, update_hour_rate)
 
@@ -531,13 +614,14 @@
 			self.operating_cost += flt(operating_cost)
 			self.base_operating_cost += flt(base_operating_cost)
 
-	def update_rate_and_time(self, row, update_hour_rate = False):
+	def update_rate_and_time(self, row, update_hour_rate=False):
 		if not row.hour_rate or update_hour_rate:
 			hour_rate = flt(frappe.get_cached_value("Workstation", row.workstation, "hour_rate"))
 
 			if hour_rate:
-				row.hour_rate = (hour_rate / flt(self.conversion_rate)
-					if self.conversion_rate and hour_rate else hour_rate)
+				row.hour_rate = (
+					hour_rate / flt(self.conversion_rate) if self.conversion_rate and hour_rate else hour_rate
+				)
 
 		if row.hour_rate and row.time_in_mins:
 			row.base_hour_rate = flt(row.hour_rate) * flt(self.conversion_rate)
@@ -549,72 +633,124 @@
 		if update_hour_rate:
 			row.db_update()
 
-	def calculate_rm_cost(self):
+	def calculate_rm_cost(self, save=False):
 		"""Fetch RM rate as per today's valuation rate and calculate totals"""
 		total_rm_cost = 0
 		base_total_rm_cost = 0
 
-		for d in self.get('items'):
+		for d in self.get("items"):
+			old_rate = d.rate
+			d.rate = self.get_rm_rate(
+				{
+					"company": self.company,
+					"item_code": d.item_code,
+					"bom_no": d.bom_no,
+					"qty": d.qty,
+					"uom": d.uom,
+					"stock_uom": d.stock_uom,
+					"conversion_factor": d.conversion_factor,
+					"sourced_by_supplier": d.sourced_by_supplier,
+				}
+			)
+
 			d.base_rate = flt(d.rate) * flt(self.conversion_rate)
 			d.amount = flt(d.rate, d.precision("rate")) * flt(d.qty, d.precision("qty"))
 			d.base_amount = d.amount * flt(self.conversion_rate)
-			d.qty_consumed_per_unit = flt(d.stock_qty, d.precision("stock_qty")) \
-				/ flt(self.quantity, self.precision("quantity"))
+			d.qty_consumed_per_unit = flt(d.stock_qty, d.precision("stock_qty")) / flt(
+				self.quantity, self.precision("quantity")
+			)
 
 			total_rm_cost += d.amount
 			base_total_rm_cost += d.base_amount
+			if save and (old_rate != d.rate):
+				d.db_update()
 
 		self.raw_material_cost = total_rm_cost
 		self.base_raw_material_cost = base_total_rm_cost
 
-	def calculate_sm_cost(self):
+	def calculate_sm_cost(self, save=False):
 		"""Fetch RM rate as per today's valuation rate and calculate totals"""
 		total_sm_cost = 0
 		base_total_sm_cost = 0
 
-		for d in self.get('scrap_items'):
-			d.base_rate = flt(d.rate, d.precision("rate")) * flt(self.conversion_rate, self.precision("conversion_rate"))
+		for d in self.get("scrap_items"):
+			d.base_rate = flt(d.rate, d.precision("rate")) * flt(
+				self.conversion_rate, self.precision("conversion_rate")
+			)
 			d.amount = flt(d.rate, d.precision("rate")) * flt(d.stock_qty, d.precision("stock_qty"))
-			d.base_amount = flt(d.amount, d.precision("amount")) * flt(self.conversion_rate, self.precision("conversion_rate"))
+			d.base_amount = flt(d.amount, d.precision("amount")) * flt(
+				self.conversion_rate, self.precision("conversion_rate")
+			)
 			total_sm_cost += d.amount
 			base_total_sm_cost += d.base_amount
+			if save:
+				d.db_update()
 
 		self.scrap_material_cost = total_sm_cost
 		self.base_scrap_material_cost = base_total_sm_cost
 
-	def update_new_bom(self, old_bom, new_bom, rate):
-		for d in self.get("items"):
-			if d.bom_no != old_bom: continue
+	def calculate_exploded_cost(self):
+		"Set exploded row cost from it's parent BOM."
+		rm_rate_map = self.get_rm_rate_map()
 
-			d.bom_no = new_bom
-			d.rate = rate
-			d.amount = (d.stock_qty or d.qty) * rate
+		for row in self.get("exploded_items"):
+			old_rate = flt(row.rate)
+			row.rate = rm_rate_map.get(row.item_code)
+			row.amount = flt(row.stock_qty) * flt(row.rate)
+
+			if old_rate != row.rate:
+				# Only db_update if changed
+				row.db_update()
+
+	def get_rm_rate_map(self) -> Dict[str, float]:
+		"Create Raw Material-Rate map for Exploded Items. Fetch rate from Items table or Subassembly BOM."
+		rm_rate_map = {}
+
+		for item in self.get("items"):
+			if item.bom_no:
+				# Get Item-Rate from Subassembly BOM
+				explosion_items = frappe.get_all(
+					"BOM Explosion Item",
+					filters={"parent": item.bom_no},
+					fields=["item_code", "rate"],
+					order_by=None,  # to avoid sort index creation at db level (granular change)
+				)
+				explosion_item_rate = {item.item_code: flt(item.rate) for item in explosion_items}
+				rm_rate_map.update(explosion_item_rate)
+			else:
+				rm_rate_map[item.item_code] = flt(item.base_rate) / flt(item.conversion_factor or 1.0)
+
+		return rm_rate_map
 
 	def update_exploded_items(self, save=True):
-		""" Update Flat BOM, following will be correct data"""
+		"""Update Flat BOM, following will be correct data"""
 		self.get_exploded_items()
 		self.add_exploded_items(save=save)
 
 	def get_exploded_items(self):
-		""" Get all raw materials including items from child bom"""
+		"""Get all raw materials including items from child bom"""
 		self.cur_exploded_items = {}
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if d.bom_no:
 				self.get_child_exploded_items(d.bom_no, d.stock_qty)
 			elif d.item_code:
-				self.add_to_cur_exploded_items(frappe._dict({
-					'item_code'		: d.item_code,
-					'item_name'		: d.item_name,
-					'operation'		: d.operation,
-					'source_warehouse': d.source_warehouse,
-					'description'	: d.description,
-					'image'			: d.image,
-					'stock_uom'		: d.stock_uom,
-					'stock_qty'		: flt(d.stock_qty),
-					'rate'			: flt(d.base_rate) / (flt(d.conversion_factor) or 1.0),
-					'include_item_in_manufacturing': d.include_item_in_manufacturing,
-					'sourced_by_supplier': d.sourced_by_supplier
-				}))
+				self.add_to_cur_exploded_items(
+					frappe._dict(
+						{
+							"item_code": d.item_code,
+							"item_name": d.item_name,
+							"operation": d.operation,
+							"source_warehouse": d.source_warehouse,
+							"description": d.description,
+							"image": d.image,
+							"stock_uom": d.stock_uom,
+							"stock_qty": flt(d.stock_qty),
+							"rate": flt(d.base_rate) / (flt(d.conversion_factor) or 1.0),
+							"include_item_in_manufacturing": d.include_item_in_manufacturing,
+							"sourced_by_supplier": d.sourced_by_supplier,
+						}
+					)
+				)
 
 	def company_currency(self):
 		return erpnext.get_company_currency(self.company)
@@ -626,9 +762,10 @@
 			self.cur_exploded_items[args.item_code] = args
 
 	def get_child_exploded_items(self, bom_no, stock_qty):
-		""" Add all items from Flat BOM of child BOM"""
+		"""Add all items from Flat BOM of child BOM"""
 		# Did not use qty_consumed_per_unit in the query, as it leads to rounding loss
-		child_fb_items = frappe.db.sql("""
+		child_fb_items = frappe.db.sql(
+			"""
 			SELECT
 				bom_item.item_code,
 				bom_item.item_name,
@@ -641,36 +778,43 @@
 				bom_item.include_item_in_manufacturing,
 				bom_item.sourced_by_supplier,
 				bom_item.stock_qty / ifnull(bom.quantity, 1) AS qty_consumed_per_unit
-			FROM `tabBOM Explosion Item` bom_item, tabBOM bom
+			FROM `tabBOM Explosion Item` bom_item, `tabBOM` bom
 			WHERE
 				bom_item.parent = bom.name
 				AND bom.name = %s
 				AND bom.docstatus = 1
-		""", bom_no, as_dict = 1)
+		""",
+			bom_no,
+			as_dict=1,
+		)
 
 		for d in child_fb_items:
-			self.add_to_cur_exploded_items(frappe._dict({
-				'item_code'				: d['item_code'],
-				'item_name'				: d['item_name'],
-				'source_warehouse'		: d['source_warehouse'],
-				'operation'				: d['operation'],
-				'description'			: d['description'],
-				'stock_uom'				: d['stock_uom'],
-				'stock_qty'				: d['qty_consumed_per_unit'] * stock_qty,
-				'rate'					: flt(d['rate']),
-				'include_item_in_manufacturing': d.get('include_item_in_manufacturing', 0),
-				'sourced_by_supplier': d.get('sourced_by_supplier', 0)
-			}))
+			self.add_to_cur_exploded_items(
+				frappe._dict(
+					{
+						"item_code": d["item_code"],
+						"item_name": d["item_name"],
+						"source_warehouse": d["source_warehouse"],
+						"operation": d["operation"],
+						"description": d["description"],
+						"stock_uom": d["stock_uom"],
+						"stock_qty": d["qty_consumed_per_unit"] * stock_qty,
+						"rate": flt(d["rate"]),
+						"include_item_in_manufacturing": d.get("include_item_in_manufacturing", 0),
+						"sourced_by_supplier": d.get("sourced_by_supplier", 0),
+					}
+				)
+			)
 
 	def add_exploded_items(self, save=True):
 		"Add items to Flat BOM table"
-		self.set('exploded_items', [])
+		self.set("exploded_items", [])
 
 		if save:
 			frappe.db.sql("""delete from `tabBOM Explosion Item` where parent=%s""", self.name)
 
 		for d in sorted(self.cur_exploded_items, key=itemgetter(0)):
-			ch = self.append('exploded_items', {})
+			ch = self.append("exploded_items", {})
 			for i in self.cur_exploded_items[d].keys():
 				ch.set(i, self.cur_exploded_items[d][i])
 			ch.amount = flt(ch.stock_qty) * flt(ch.rate)
@@ -682,10 +826,13 @@
 
 	def validate_bom_links(self):
 		if not self.is_active:
-			act_pbom = frappe.db.sql("""select distinct bom_item.parent from `tabBOM Item` bom_item
+			act_pbom = frappe.db.sql(
+				"""select distinct bom_item.parent from `tabBOM Item` bom_item
 				where bom_item.bom_no = %s and bom_item.docstatus = 1 and bom_item.parenttype='BOM'
 				and exists (select * from `tabBOM` where name = bom_item.parent
-					and docstatus = 1 and is_active = 1)""", self.name)
+					and docstatus = 1 and is_active = 1)""",
+				self.name,
+			)
 
 			if act_pbom and act_pbom[0][0]:
 				frappe.throw(_("Cannot deactivate or cancel BOM as it is linked with other BOMs"))
@@ -694,20 +841,23 @@
 		if not self.with_operations:
 			self.transfer_material_against = "Work Order"
 		if not self.transfer_material_against and not self.is_new():
-			frappe.throw(_("Setting {} is required").format(self.meta.get_label("transfer_material_against")), title=_("Missing value"))
+			frappe.throw(
+				_("Setting {} is required").format(self.meta.get_label("transfer_material_against")),
+				title=_("Missing value"),
+			)
 
 	def set_routing_operations(self):
 		if self.routing and self.with_operations and not self.operations:
 			self.get_routing()
 
 	def validate_operations(self):
-		if self.with_operations and not self.get('operations') and self.docstatus == 1:
+		if self.with_operations and not self.get("operations") and self.docstatus == 1:
 			frappe.throw(_("Operations cannot be left blank"))
 
 		if self.with_operations:
 			for d in self.operations:
 				if not d.description:
-					d.description = frappe.db.get_value('Operation', d.operation, 'description')
+					d.description = frappe.db.get_value("Operation", d.operation, "description")
 				if not d.batch_size or d.batch_size <= 0:
 					d.batch_size = 1
 
@@ -719,99 +869,127 @@
 		for item in self.scrap_items:
 			msg = ""
 			if item.item_code == self.item and not item.is_process_loss:
-				msg = _('Scrap/Loss Item: {0} should have Is Process Loss checked as it is the same as the item to be manufactured or repacked.') \
-					.format(frappe.bold(item.item_code))
+				msg = _(
+					"Scrap/Loss Item: {0} should have Is Process Loss checked as it is the same as the item to be manufactured or repacked."
+				).format(frappe.bold(item.item_code))
 			elif item.item_code != self.item and item.is_process_loss:
-				msg = _('Scrap/Loss Item: {0} should not have Is Process Loss checked as it is different from  the item to be manufactured or repacked') \
-					.format(frappe.bold(item.item_code))
+				msg = _(
+					"Scrap/Loss Item: {0} should not have Is Process Loss checked as it is different from  the item to be manufactured or repacked"
+				).format(frappe.bold(item.item_code))
 
 			must_be_whole_number = frappe.get_value("UOM", item.stock_uom, "must_be_whole_number")
 			if item.is_process_loss and must_be_whole_number:
-				msg = _("Item: {0} with Stock UOM: {1} cannot be a Scrap/Loss Item as {1} is a whole UOM.") \
-					.format(frappe.bold(item.item_code), frappe.bold(item.stock_uom))
+				msg = _(
+					"Item: {0} with Stock UOM: {1} cannot be a Scrap/Loss Item as {1} is a whole UOM."
+				).format(frappe.bold(item.item_code), frappe.bold(item.stock_uom))
 
 			if item.is_process_loss and (item.stock_qty >= self.quantity):
-				msg = _("Scrap/Loss Item: {0} should have Qty less than finished goods Quantity.") \
-					.format(frappe.bold(item.item_code))
+				msg = _("Scrap/Loss Item: {0} should have Qty less than finished goods Quantity.").format(
+					frappe.bold(item.item_code)
+				)
 
 			if item.is_process_loss and (item.rate > 0):
-				msg = _("Scrap/Loss Item: {0} should have Rate set to 0 because Is Process Loss is checked.") \
-					.format(frappe.bold(item.item_code))
+				msg = _(
+					"Scrap/Loss Item: {0} should have Rate set to 0 because Is Process Loss is checked."
+				).format(frappe.bold(item.item_code))
 
 			if msg:
 				frappe.throw(msg, title=_("Note"))
 
+
 def get_bom_item_rate(args, bom_doc):
-	if bom_doc.rm_cost_as_per == 'Valuation Rate':
+	if bom_doc.rm_cost_as_per == "Valuation Rate":
 		rate = get_valuation_rate(args) * (args.get("conversion_factor") or 1)
-	elif bom_doc.rm_cost_as_per == 'Last Purchase Rate':
-		rate = ( flt(args.get('last_purchase_rate')) \
-			or frappe.db.get_value("Item", args['item_code'], "last_purchase_rate")) \
-				* (args.get("conversion_factor") or 1)
+	elif bom_doc.rm_cost_as_per == "Last Purchase Rate":
+		rate = (
+			flt(args.get("last_purchase_rate"))
+			or flt(frappe.db.get_value("Item", args["item_code"], "last_purchase_rate"))
+		) * (args.get("conversion_factor") or 1)
 	elif bom_doc.rm_cost_as_per == "Price List":
 		if not bom_doc.buying_price_list:
 			frappe.throw(_("Please select Price List"))
-		bom_args = frappe._dict({
-			"doctype": "BOM",
-			"price_list": bom_doc.buying_price_list,
-			"qty": args.get("qty") or 1,
-			"uom": args.get("uom") or args.get("stock_uom"),
-			"stock_uom": args.get("stock_uom"),
-			"transaction_type": "buying",
-			"company": bom_doc.company,
-			"currency": bom_doc.currency,
-			"conversion_rate": 1, # Passed conversion rate as 1 purposefully, as conversion rate is applied at the end of the function
-			"conversion_factor": args.get("conversion_factor") or 1,
-			"plc_conversion_rate": 1,
-			"ignore_party": True,
-			"ignore_conversion_rate": True
-		})
+		bom_args = frappe._dict(
+			{
+				"doctype": "BOM",
+				"price_list": bom_doc.buying_price_list,
+				"qty": args.get("qty") or 1,
+				"uom": args.get("uom") or args.get("stock_uom"),
+				"stock_uom": args.get("stock_uom"),
+				"transaction_type": "buying",
+				"company": bom_doc.company,
+				"currency": bom_doc.currency,
+				"conversion_rate": 1,  # Passed conversion rate as 1 purposefully, as conversion rate is applied at the end of the function
+				"conversion_factor": args.get("conversion_factor") or 1,
+				"plc_conversion_rate": 1,
+				"ignore_party": True,
+				"ignore_conversion_rate": True,
+			}
+		)
 		item_doc = frappe.get_cached_doc("Item", args.get("item_code"))
 		price_list_data = get_price_list_rate(bom_args, item_doc)
 		rate = price_list_data.price_list_rate
 
-	return rate
+	return flt(rate)
 
-def get_valuation_rate(args):
-	""" Get weighted average of valuation rate from all warehouses """
 
-	total_qty, total_value, valuation_rate = 0.0, 0.0, 0.0
-	item_bins = frappe.db.sql("""
-		select
-			bin.actual_qty, bin.stock_value
-		from
-			`tabBin` bin, `tabWarehouse` warehouse
-		where
-			bin.item_code=%(item)s
-			and bin.warehouse = warehouse.name
-			and warehouse.company=%(company)s""",
-		{"item": args['item_code'], "company": args['company']}, as_dict=1)
+def get_valuation_rate(data):
+	"""
+	1) Get average valuation rate from all warehouses
+	2) If no value, get last valuation rate from SLE
+	3) If no value, get valuation rate from Item
+	"""
+	from frappe.query_builder.functions import Sum
 
-	for d in item_bins:
-		total_qty += flt(d.actual_qty)
-		total_value += flt(d.stock_value)
+	item_code, company = data.get("item_code"), data.get("company")
+	valuation_rate = 0.0
 
-	if total_qty:
-		valuation_rate =  total_value / total_qty
+	bin_table = frappe.qb.DocType("Bin")
+	wh_table = frappe.qb.DocType("Warehouse")
+	item_valuation = (
+		frappe.qb.from_(bin_table)
+		.join(wh_table)
+		.on(bin_table.warehouse == wh_table.name)
+		.select((Sum(bin_table.stock_value) / Sum(bin_table.actual_qty)).as_("valuation_rate"))
+		.where((bin_table.item_code == item_code) & (wh_table.company == company))
+	).run(as_dict=True)[0]
 
-	if valuation_rate <= 0:
-		last_valuation_rate = frappe.db.sql("""select valuation_rate
-			from `tabStock Ledger Entry`
-			where item_code = %s and valuation_rate > 0 and is_cancelled = 0
-			order by posting_date desc, posting_time desc, creation desc limit 1""", args['item_code'])
+	valuation_rate = item_valuation.get("valuation_rate")
 
-		valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0
+	if (valuation_rate is not None) and valuation_rate <= 0:
+		# Explicit null value check. If None, Bins don't exist, neither does SLE
+		sle = frappe.qb.DocType("Stock Ledger Entry")
+		last_val_rate = (
+			frappe.qb.from_(sle)
+			.select(sle.valuation_rate)
+			.where((sle.item_code == item_code) & (sle.valuation_rate > 0) & (sle.is_cancelled == 0))
+			.orderby(sle.posting_date, order=frappe.qb.desc)
+			.orderby(sle.posting_time, order=frappe.qb.desc)
+			.orderby(sle.creation, order=frappe.qb.desc)
+			.limit(1)
+		).run(as_dict=True)
+
+		valuation_rate = flt(last_val_rate[0].get("valuation_rate")) if last_val_rate else 0
 
 	if not valuation_rate:
-		valuation_rate = frappe.db.get_value("Item", args['item_code'], "valuation_rate")
+		valuation_rate = frappe.db.get_value("Item", item_code, "valuation_rate")
 
 	return flt(valuation_rate)
 
+
 def get_list_context(context):
 	context.title = _("Bill of Materials")
 	# context.introduction = _('Boms')
 
-def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_items=0, include_non_stock_items=False, fetch_qty_in_stock_uom=True):
+
+def get_bom_items_as_dict(
+	bom,
+	company,
+	qty=1,
+	fetch_exploded=1,
+	fetch_scrap_items=0,
+	include_non_stock_items=False,
+	fetch_qty_in_stock_uom=True,
+):
 	item_dict = {}
 
 	# Did not use qty_consumed_per_unit in the query, as it leads to rounding loss
@@ -848,30 +1026,40 @@
 
 	is_stock_item = 0 if include_non_stock_items else 1
 	if cint(fetch_exploded):
-		query = query.format(table="BOM Explosion Item",
+		query = query.format(
+			table="BOM Explosion Item",
 			where_conditions="",
 			is_stock_item=is_stock_item,
 			qty_field="stock_qty",
-			select_columns = """, bom_item.source_warehouse, bom_item.operation,
+			select_columns=""", bom_item.source_warehouse, bom_item.operation,
 				bom_item.include_item_in_manufacturing, bom_item.description, bom_item.rate, bom_item.sourced_by_supplier,
-				(Select idx from `tabBOM Item` where item_code = bom_item.item_code and parent = %(parent)s limit 1) as idx""")
-
-		items = frappe.db.sql(query, { "parent": bom, "qty": qty, "bom": bom, "company": company }, as_dict=True)
-	elif fetch_scrap_items:
-		query = query.format(
-			table="BOM Scrap Item", where_conditions="",
-			select_columns=", bom_item.idx, item.description, is_process_loss",
-			is_stock_item=is_stock_item, qty_field="stock_qty"
+				(Select idx from `tabBOM Item` where item_code = bom_item.item_code and parent = %(parent)s limit 1) as idx""",
 		)
 
-		items = frappe.db.sql(query, { "qty": qty, "bom": bom, "company": company }, as_dict=True)
+		items = frappe.db.sql(
+			query, {"parent": bom, "qty": qty, "bom": bom, "company": company}, as_dict=True
+		)
+	elif fetch_scrap_items:
+		query = query.format(
+			table="BOM Scrap Item",
+			where_conditions="",
+			select_columns=", item.description, is_process_loss",
+			is_stock_item=is_stock_item,
+			qty_field="stock_qty",
+		)
+
+		items = frappe.db.sql(query, {"qty": qty, "bom": bom, "company": company}, as_dict=True)
 	else:
-		query = query.format(table="BOM Item", where_conditions="", is_stock_item=is_stock_item,
+		query = query.format(
+			table="BOM Item",
+			where_conditions="",
+			is_stock_item=is_stock_item,
 			qty_field="stock_qty" if fetch_qty_in_stock_uom else "qty",
-			select_columns = """, bom_item.uom, bom_item.conversion_factor, bom_item.source_warehouse,
-				bom_item.idx, bom_item.operation, bom_item.include_item_in_manufacturing, bom_item.sourced_by_supplier,
-				bom_item.description, bom_item.base_rate as rate """)
-		items = frappe.db.sql(query, { "qty": qty, "bom": bom, "company": company }, as_dict=True)
+			select_columns=""", bom_item.uom, bom_item.conversion_factor, bom_item.source_warehouse,
+				bom_item.operation, bom_item.include_item_in_manufacturing, bom_item.sourced_by_supplier,
+				bom_item.description, bom_item.base_rate as rate """,
+		)
+		items = frappe.db.sql(query, {"qty": qty, "bom": bom, "company": company}, as_dict=True)
 
 	for item in items:
 		if item.item_code in item_dict:
@@ -880,21 +1068,28 @@
 			item_dict[item.item_code] = item
 
 	for item, item_details in item_dict.items():
-		for d in [["Account", "expense_account", "stock_adjustment_account"],
-			["Cost Center", "cost_center", "cost_center"], ["Warehouse", "default_warehouse", ""]]:
-				company_in_record = frappe.db.get_value(d[0], item_details.get(d[1]), "company")
-				if not item_details.get(d[1]) or (company_in_record and company != company_in_record):
-					item_dict[item][d[1]] = frappe.get_cached_value('Company',  company,  d[2]) if d[2] else None
+		for d in [
+			["Account", "expense_account", "stock_adjustment_account"],
+			["Cost Center", "cost_center", "cost_center"],
+			["Warehouse", "default_warehouse", ""],
+		]:
+			company_in_record = frappe.db.get_value(d[0], item_details.get(d[1]), "company")
+			if not item_details.get(d[1]) or (company_in_record and company != company_in_record):
+				item_dict[item][d[1]] = frappe.get_cached_value("Company", company, d[2]) if d[2] else None
 
 	return item_dict
 
+
 @frappe.whitelist()
 def get_bom_items(bom, company, qty=1, fetch_exploded=1):
-	items = get_bom_items_as_dict(bom, company, qty, fetch_exploded, include_non_stock_items=True).values()
+	items = get_bom_items_as_dict(
+		bom, company, qty, fetch_exploded, include_non_stock_items=True
+	).values()
 	items = list(items)
-	items.sort(key = functools.cmp_to_key(lambda a, b: a.item_code > b.item_code and 1 or -1))
+	items.sort(key=functools.cmp_to_key(lambda a, b: a.item_code > b.item_code and 1 or -1))
 	return items
 
+
 def validate_bom_no(item, bom_no):
 	"""Validate BOM No of sub-contracted items"""
 	bom = frappe.get_doc("BOM", bom_no)
@@ -906,21 +1101,24 @@
 	if item:
 		rm_item_exists = False
 		for d in bom.items:
-			if (d.item_code.lower() == item.lower()):
+			if d.item_code.lower() == item.lower():
 				rm_item_exists = True
 		for d in bom.scrap_items:
-			if (d.item_code.lower() == item.lower()):
+			if d.item_code.lower() == item.lower():
 				rm_item_exists = True
-		if bom.item.lower() == item.lower() or \
-			bom.item.lower() == cstr(frappe.db.get_value("Item", item, "variant_of")).lower():
-				rm_item_exists = True
+		if (
+			bom.item.lower() == item.lower()
+			or bom.item.lower() == cstr(frappe.db.get_value("Item", item, "variant_of")).lower()
+		):
+			rm_item_exists = True
 		if not rm_item_exists:
 			frappe.throw(_("BOM {0} does not belong to Item {1}").format(bom_no, item))
 
+
 @frappe.whitelist()
 def get_children(parent=None, is_root=False, **filters):
-	if not parent or parent=="BOM":
-		frappe.msgprint(_('Please select a BOM'))
+	if not parent or parent == "BOM":
+		frappe.msgprint(_("Please select a BOM"))
 		return
 
 	if parent:
@@ -930,121 +1128,120 @@
 		bom_doc = frappe.get_cached_doc("BOM", frappe.form_dict.parent)
 		frappe.has_permission("BOM", doc=bom_doc, throw=True)
 
-		bom_items = frappe.get_all('BOM Item',
-			fields=['item_code', 'bom_no as value', 'stock_qty'],
-			filters=[['parent', '=', frappe.form_dict.parent]],
-			order_by='idx')
+		bom_items = frappe.get_all(
+			"BOM Item",
+			fields=["item_code", "bom_no as value", "stock_qty"],
+			filters=[["parent", "=", frappe.form_dict.parent]],
+			order_by="idx",
+		)
 
-		item_names = tuple(d.get('item_code') for d in bom_items)
+		item_names = tuple(d.get("item_code") for d in bom_items)
 
-		items = frappe.get_list('Item',
-			fields=['image', 'description', 'name', 'stock_uom', 'item_name', 'is_sub_contracted_item'],
-			filters=[['name', 'in', item_names]]) # to get only required item dicts
+		items = frappe.get_list(
+			"Item",
+			fields=["image", "description", "name", "stock_uom", "item_name", "is_sub_contracted_item"],
+			filters=[["name", "in", item_names]],
+		)  # to get only required item dicts
 
 		for bom_item in bom_items:
 			# extend bom_item dict with respective item dict
 			bom_item.update(
 				# returns an item dict from items list which matches with item_code
-				next(item for item in items if item.get('name')
-					== bom_item.get('item_code'))
+				next(item for item in items if item.get("name") == bom_item.get("item_code"))
 			)
 
 			bom_item.parent_bom_qty = bom_doc.quantity
-			bom_item.expandable = 0 if bom_item.value in ('', None)  else 1
+			bom_item.expandable = 0 if bom_item.value in ("", None) else 1
 			bom_item.image = frappe.db.escape(bom_item.image)
 
 		return bom_items
 
-def get_boms_in_bottom_up_order(bom_no=None):
-	def _get_parent(bom_no):
-		return frappe.db.sql_list("""
-			select distinct bom_item.parent from `tabBOM Item` bom_item
-			where bom_item.bom_no = %s and bom_item.docstatus=1 and bom_item.parenttype='BOM'
-				and exists(select bom.name from `tabBOM` bom where bom.name=bom_item.parent and bom.is_active=1)
-		""", bom_no)
-
-	count = 0
-	bom_list = []
-	if bom_no:
-		bom_list.append(bom_no)
-	else:
-		# get all leaf BOMs
-		bom_list = frappe.db.sql_list("""select name from `tabBOM` bom
-			where docstatus=1 and is_active=1
-				and not exists(select bom_no from `tabBOM Item`
-					where parent=bom.name and ifnull(bom_no, '')!='')""")
-
-	while(count < len(bom_list)):
-		for child_bom in _get_parent(bom_list[count]):
-			if child_bom not in bom_list:
-				bom_list.append(child_bom)
-		count += 1
-
-	return bom_list
 
 def add_additional_cost(stock_entry, work_order):
 	# Add non stock items cost in the additional cost
 	stock_entry.additional_costs = []
-	expenses_included_in_valuation = frappe.get_cached_value("Company", work_order.company,
-		"expenses_included_in_valuation")
+	expenses_included_in_valuation = frappe.get_cached_value(
+		"Company", work_order.company, "expenses_included_in_valuation"
+	)
 
 	add_non_stock_items_cost(stock_entry, work_order, expenses_included_in_valuation)
 	add_operations_cost(stock_entry, work_order, expenses_included_in_valuation)
 
+
 def add_non_stock_items_cost(stock_entry, work_order, expense_account):
-	bom = frappe.get_doc('BOM', work_order.bom_no)
-	table = 'exploded_items' if work_order.get('use_multi_level_bom') else 'items'
+	bom = frappe.get_doc("BOM", work_order.bom_no)
+	table = "exploded_items" if work_order.get("use_multi_level_bom") else "items"
 
 	items = {}
 	for d in bom.get(table):
 		items.setdefault(d.item_code, d.amount)
 
-	non_stock_items = frappe.get_all('Item',
-		fields="name", filters={'name': ('in', list(items.keys())), 'ifnull(is_stock_item, 0)': 0}, as_list=1)
+	non_stock_items = frappe.get_all(
+		"Item",
+		fields="name",
+		filters={"name": ("in", list(items.keys())), "ifnull(is_stock_item, 0)": 0},
+		as_list=1,
+	)
 
 	non_stock_items_cost = 0.0
 	for name in non_stock_items:
-		non_stock_items_cost += flt(items.get(name[0])) * flt(stock_entry.fg_completed_qty) / flt(bom.quantity)
+		non_stock_items_cost += (
+			flt(items.get(name[0])) * flt(stock_entry.fg_completed_qty) / flt(bom.quantity)
+		)
 
 	if non_stock_items_cost:
-		stock_entry.append('additional_costs', {
-			'expense_account': expense_account,
-			'description': _("Non stock items"),
-			'amount': non_stock_items_cost
-		})
+		stock_entry.append(
+			"additional_costs",
+			{
+				"expense_account": expense_account,
+				"description": _("Non stock items"),
+				"amount": non_stock_items_cost,
+			},
+		)
+
 
 def add_operations_cost(stock_entry, work_order=None, expense_account=None):
 	from erpnext.stock.doctype.stock_entry.stock_entry import get_operating_cost_per_unit
+
 	operating_cost_per_unit = get_operating_cost_per_unit(work_order, stock_entry.bom_no)
 
 	if operating_cost_per_unit:
-		stock_entry.append('additional_costs', {
-			"expense_account": expense_account,
-			"description": _("Operating Cost as per Work Order / BOM"),
-			"amount": operating_cost_per_unit * flt(stock_entry.fg_completed_qty)
-		})
+		stock_entry.append(
+			"additional_costs",
+			{
+				"expense_account": expense_account,
+				"description": _("Operating Cost as per Work Order / BOM"),
+				"amount": operating_cost_per_unit * flt(stock_entry.fg_completed_qty),
+			},
+		)
 
 	if work_order and work_order.additional_operating_cost and work_order.qty:
-		additional_operating_cost_per_unit = \
-			flt(work_order.additional_operating_cost) / flt(work_order.qty)
+		additional_operating_cost_per_unit = flt(work_order.additional_operating_cost) / flt(
+			work_order.qty
+		)
 
 		if additional_operating_cost_per_unit:
-			stock_entry.append('additional_costs', {
-				"expense_account": expense_account,
-				"description": "Additional Operating Cost",
-				"amount": additional_operating_cost_per_unit * flt(stock_entry.fg_completed_qty)
-			})
+			stock_entry.append(
+				"additional_costs",
+				{
+					"expense_account": expense_account,
+					"description": "Additional Operating Cost",
+					"amount": additional_operating_cost_per_unit * flt(stock_entry.fg_completed_qty),
+				},
+			)
+
 
 @frappe.whitelist()
 def get_bom_diff(bom1, bom2):
 	from frappe.model import table_fields
 
 	if bom1 == bom2:
-		frappe.throw(_("BOM 1 {0} and BOM 2 {1} should not be same")
-			.format(frappe.bold(bom1), frappe.bold(bom2)))
+		frappe.throw(
+			_("BOM 1 {0} and BOM 2 {1} should not be same").format(frappe.bold(bom1), frappe.bold(bom2))
+		)
 
-	doc1 = frappe.get_doc('BOM', bom1)
-	doc2 = frappe.get_doc('BOM', bom2)
+	doc1 = frappe.get_doc("BOM", bom1)
+	doc2 = frappe.get_doc("BOM", bom2)
 
 	out = get_diff(doc1, doc2)
 	out.row_changed = []
@@ -1054,10 +1251,10 @@
 	meta = doc1.meta
 
 	identifiers = {
-		'operations': 'operation',
-		'items': 'item_code',
-		'scrap_items': 'item_code',
-		'exploded_items': 'item_code'
+		"operations": "operation",
+		"items": "item_code",
+		"scrap_items": "item_code",
+		"exploded_items": "item_code",
 	}
 
 	for df in meta.fields:
@@ -1088,6 +1285,7 @@
 
 	return out
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def item_query(doctype, txt, searchfield, start, page_len, filters):
@@ -1097,25 +1295,28 @@
 	order_by = "idx desc, name, item_name"
 
 	fields = ["name", "item_group", "item_name", "description"]
-	fields.extend([field for field in searchfields
-		if not field in ["name", "item_group", "description"]])
+	fields.extend(
+		[field for field in searchfields if not field in ["name", "item_group", "description"]]
+	)
 
-	searchfields = searchfields + [field for field in [searchfield or "name", "item_code", "item_group", "item_name"]
-		if not field in searchfields]
+	searchfields = searchfields + [
+		field
+		for field in [searchfield or "name", "item_code", "item_group", "item_name"]
+		if not field in searchfields
+	]
 
-	query_filters = {
-		"disabled": 0,
-		"ifnull(end_of_life, '5050-50-50')": (">", today())
-	}
+	query_filters = {"disabled": 0, "end_of_life": (">", today())}
 
 	or_cond_filters = {}
 	if txt:
 		for s_field in searchfields:
 			or_cond_filters[s_field] = ("like", "%{0}%".format(txt))
 
-		barcodes = frappe.get_all("Item Barcode",
+		barcodes = frappe.get_all(
+			"Item Barcode",
 			fields=["distinct parent as item_code"],
-			filters = {"barcode": ("like", "%{0}%".format(txt))})
+			filters={"barcode": ("like", "%{0}%".format(txt))},
+		)
 
 		barcodes = [d.item_code for d in barcodes]
 		if barcodes:
@@ -1129,10 +1330,17 @@
 	if filters and filters.get("is_stock_item"):
 		query_filters["is_stock_item"] = 1
 
-	return frappe.get_list("Item",
-		fields = fields, filters=query_filters,
-		or_filters = or_cond_filters, order_by=order_by,
-		limit_start=start, limit_page_length=page_len, as_list=1)
+	return frappe.get_list(
+		"Item",
+		fields=fields,
+		filters=query_filters,
+		or_filters=or_cond_filters,
+		order_by=order_by,
+		limit_start=start,
+		limit_page_length=page_len,
+		as_list=1,
+	)
+
 
 @frappe.whitelist()
 def make_variant_bom(source_name, bom_no, item, variant_items, target_doc=None):
@@ -1143,28 +1351,31 @@
 		doc.quantity = 1
 
 		item_data = get_item_details(item)
-		doc.update({
-			"item_name": item_data.item_name,
-			"description": item_data.description,
-			"uom": item_data.stock_uom,
-			"allow_alternative_item": item_data.allow_alternative_item
-		})
+		doc.update(
+			{
+				"item_name": item_data.item_name,
+				"description": item_data.description,
+				"uom": item_data.stock_uom,
+				"allow_alternative_item": item_data.allow_alternative_item,
+			}
+		)
 
 		add_variant_item(variant_items, doc, source_name)
 
-	doc = get_mapped_doc('BOM', source_name, {
-		'BOM': {
-			'doctype': 'BOM',
-			'validation': {
-				'docstatus': ['=', 1]
-			}
+	doc = get_mapped_doc(
+		"BOM",
+		source_name,
+		{
+			"BOM": {"doctype": "BOM", "validation": {"docstatus": ["=", 1]}},
+			"BOM Item": {
+				"doctype": "BOM Item",
+				# stop get_mapped_doc copying parent bom_no to children
+				"field_no_map": ["bom_no"],
+				"condition": lambda doc: doc.has_variants == 0,
+			},
 		},
-		'BOM Item': {
-			'doctype': 'BOM Item',
-			# stop get_mapped_doc copying parent bom_no to children
-			'field_no_map': ['bom_no'],
-			'condition': lambda doc: doc.has_variants == 0
-		},
-	}, target_doc, postprocess)
+		target_doc,
+		postprocess,
+	)
 
 	return doc
diff --git a/erpnext/manufacturing/doctype/bom/bom_dashboard.py b/erpnext/manufacturing/doctype/bom/bom_dashboard.py
index 0699f74..d8a810b 100644
--- a/erpnext/manufacturing/doctype/bom/bom_dashboard.py
+++ b/erpnext/manufacturing/doctype/bom/bom_dashboard.py
@@ -3,27 +3,28 @@
 
 def get_data():
 	return {
-		'fieldname': 'bom_no',
-		'non_standard_fieldnames': {
-			'Item': 'default_bom',
-			'Purchase Order': 'bom',
-			'Purchase Receipt': 'bom',
-			'Purchase Invoice': 'bom'
+		"fieldname": "bom_no",
+		"non_standard_fieldnames": {
+			"Item": "default_bom",
+			"Purchase Order": "bom",
+			"Purchase Receipt": "bom",
+			"Purchase Invoice": "bom",
 		},
-		'transactions': [
+		"transactions": [
+			{"label": _("Stock"), "items": ["Item", "Stock Entry", "Quality Inspection"]},
+			{"label": _("Manufacture"), "items": ["BOM", "Work Order", "Job Card"]},
 			{
-				'label': _('Stock'),
-				'items': ['Item', 'Stock Entry', 'Quality Inspection']
+				"label": _("Subcontract"),
+				"items": ["Purchase Order", "Purchase Receipt", "Purchase Invoice"],
 			},
-			{
-				'label': _('Manufacture'),
-				'items': ['BOM', 'Work Order', 'Job Card']
-			},
-			{
-				'label': _('Subcontract'),
-				'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
-			}
 		],
-		'disable_create_buttons': ["Item", "Purchase Order", "Purchase Receipt",
-			"Purchase Invoice", "Job Card", "Stock Entry", "BOM"]
+		"disable_create_buttons": [
+			"Item",
+			"Purchase Order",
+			"Purchase Receipt",
+			"Purchase Invoice",
+			"Job Card",
+			"Stock Entry",
+			"BOM",
+		],
 	}
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index 3cc91b3..182a20c 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -6,38 +6,41 @@
 from functools import partial
 
 import frappe
-from frappe.test_runner import make_test_records
 from frappe.tests.utils import FrappeTestCase
 from frappe.utils import cstr, flt
 
 from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
-from erpnext.manufacturing.doctype.bom.bom import item_query, make_variant_bom
-from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
+from erpnext.manufacturing.doctype.bom.bom import BOMRecursionError, item_query, make_variant_bom
+from erpnext.manufacturing.doctype.bom_update_log.test_bom_update_log import (
+	update_cost_in_all_boms_in_test,
+)
 from erpnext.stock.doctype.item.test_item import make_item
 from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
 	create_stock_reconciliation,
 )
 from erpnext.tests.test_subcontracting import set_backflush_based_on
 
-test_records = frappe.get_test_records('BOM')
+test_records = frappe.get_test_records("BOM")
+test_dependencies = ["Item", "Quality Inspection Template"]
+
 
 class TestBOM(FrappeTestCase):
-	def setUp(self):
-		if not frappe.get_value('Item', '_Test Item'):
-			make_test_records('Item')
-
 	def test_get_items(self):
 		from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
-		items_dict = get_bom_items_as_dict(bom=get_default_bom(),
-			company="_Test Company", qty=1, fetch_exploded=0)
+
+		items_dict = get_bom_items_as_dict(
+			bom=get_default_bom(), company="_Test Company", qty=1, fetch_exploded=0
+		)
 		self.assertTrue(test_records[2]["items"][0]["item_code"] in items_dict)
 		self.assertTrue(test_records[2]["items"][1]["item_code"] in items_dict)
 		self.assertEqual(len(items_dict.values()), 2)
 
 	def test_get_items_exploded(self):
 		from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
-		items_dict = get_bom_items_as_dict(bom=get_default_bom(),
-			company="_Test Company", qty=1, fetch_exploded=1)
+
+		items_dict = get_bom_items_as_dict(
+			bom=get_default_bom(), company="_Test Company", qty=1, fetch_exploded=1
+		)
 		self.assertTrue(test_records[2]["items"][0]["item_code"] in items_dict)
 		self.assertFalse(test_records[2]["items"][1]["item_code"] in items_dict)
 		self.assertTrue(test_records[0]["items"][0]["item_code"] in items_dict)
@@ -46,13 +49,14 @@
 
 	def test_get_items_list(self):
 		from erpnext.manufacturing.doctype.bom.bom import get_bom_items
+
 		self.assertEqual(len(get_bom_items(bom=get_default_bom(), company="_Test Company")), 3)
 
 	def test_default_bom(self):
 		def _get_default_bom_in_item():
 			return cstr(frappe.db.get_value("Item", "_Test FG Item 2", "default_bom"))
 
-		bom = frappe.get_doc("BOM", {"item":"_Test FG Item 2", "is_default": 1})
+		bom = frappe.get_doc("BOM", {"item": "_Test FG Item 2", "is_default": 1})
 		self.assertEqual(_get_default_bom_in_item(), bom.name)
 
 		bom.is_active = 0
@@ -60,28 +64,38 @@
 		self.assertEqual(_get_default_bom_in_item(), "")
 
 		bom.is_active = 1
-		bom.is_default=1
+		bom.is_default = 1
 		bom.save()
 
 		self.assertTrue(_get_default_bom_in_item(), bom.name)
 
 	def test_update_bom_cost_in_all_boms(self):
 		# get current rate for '_Test Item 2'
-		rm_rate = frappe.db.sql("""select rate from `tabBOM Item`
-			where parent='BOM-_Test Item Home Desktop Manufactured-001'
-			and item_code='_Test Item 2' and docstatus=1 and parenttype='BOM'""")
-		rm_rate = rm_rate[0][0] if rm_rate else 0
+		bom_rates = frappe.db.get_values(
+			"BOM Item",
+			{
+				"parent": "BOM-_Test Item Home Desktop Manufactured-001",
+				"item_code": "_Test Item 2",
+				"docstatus": 1,
+			},
+			fieldname=["rate", "base_rate"],
+			as_dict=True,
+		)
+		rm_base_rate = bom_rates[0].get("base_rate") if bom_rates else 0
 
 		# Reset item valuation rate
-		reset_item_valuation_rate(item_code='_Test Item 2', qty=200, rate=rm_rate + 10)
+		reset_item_valuation_rate(item_code="_Test Item 2", qty=200, rate=rm_base_rate + 10)
 
 		# update cost of all BOMs based on latest valuation rate
-		update_cost()
+		update_cost_in_all_boms_in_test()
 
 		# check if new valuation rate updated in all BOMs
-		for d in frappe.db.sql("""select rate from `tabBOM Item`
-			where item_code='_Test Item 2' and docstatus=1 and parenttype='BOM'""", as_dict=1):
-				self.assertEqual(d.rate, rm_rate + 10)
+		for d in frappe.db.sql(
+			"""select base_rate from `tabBOM Item`
+			where item_code='_Test Item 2' and docstatus=1 and parenttype='BOM'""",
+			as_dict=1,
+		):
+			self.assertEqual(d.base_rate, rm_base_rate + 10)
 
 	def test_bom_cost(self):
 		bom = frappe.copy_doc(test_records[2])
@@ -96,7 +110,9 @@
 		for row in bom.items:
 			raw_material_cost += row.amount
 
-		base_raw_material_cost = raw_material_cost * flt(bom.conversion_rate, bom.precision("conversion_rate"))
+		base_raw_material_cost = raw_material_cost * flt(
+			bom.conversion_rate, bom.precision("conversion_rate")
+		)
 		base_op_cost = op_cost * flt(bom.conversion_rate, bom.precision("conversion_rate"))
 
 		# test amounts in selected currency, almostEqual checks for 7 digits by default
@@ -124,14 +140,15 @@
 		for op_row in bom.operations:
 			self.assertAlmostEqual(op_row.cost_per_unit, op_row.operating_cost / 2)
 
-		self.assertAlmostEqual(bom.operating_cost, op_cost/2)
+		self.assertAlmostEqual(bom.operating_cost, op_cost / 2)
 		bom.delete()
 
 	def test_bom_cost_multi_uom_multi_currency_based_on_price_list(self):
 		frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependent", 1)
 		for item_code, rate in (("_Test Item", 3600), ("_Test Item Home Desktop Manufactured", 3000)):
-			frappe.db.sql("delete from `tabItem Price` where price_list='_Test Price List' and item_code=%s",
-				item_code)
+			frappe.db.sql(
+				"delete from `tabItem Price` where price_list='_Test Price List' and item_code=%s", item_code
+			)
 			item_price = frappe.new_doc("Item Price")
 			item_price.price_list = "_Test Price List"
 			item_price.item_code = item_code
@@ -146,7 +163,7 @@
 		bom.items[0].conversion_factor = 5
 		bom.insert()
 
-		bom.update_cost(update_hour_rate = False)
+		bom.update_cost(update_hour_rate=False)
 
 		# test amounts in selected currency
 		self.assertEqual(bom.items[0].rate, 300)
@@ -171,11 +188,12 @@
 		bom.insert()
 
 		reset_item_valuation_rate(
-			item_code='_Test Item',
-			warehouse_list=frappe.get_all("Warehouse",
-				{"is_group":0, "company": bom.company}, pluck="name"),
+			item_code="_Test Item",
+			warehouse_list=frappe.get_all(
+				"Warehouse", {"is_group": 0, "company": bom.company}, pluck="name"
+			),
 			qty=200,
-			rate=200
+			rate=200,
 		)
 
 		bom.update_cost()
@@ -184,68 +202,64 @@
 
 	def test_subcontractor_sourced_item(self):
 		item_code = "_Test Subcontracted FG Item 1"
-		set_backflush_based_on('Material Transferred for Subcontract')
+		set_backflush_based_on("Material Transferred for Subcontract")
 
-		if not frappe.db.exists('Item', item_code):
-			make_item(item_code, {
-				'is_stock_item': 1,
-				'is_sub_contracted_item': 1,
-				'stock_uom': 'Nos'
-			})
+		if not frappe.db.exists("Item", item_code):
+			make_item(item_code, {"is_stock_item": 1, "is_sub_contracted_item": 1, "stock_uom": "Nos"})
 
-		if not frappe.db.exists('Item', "Test Extra Item 1"):
-			make_item("Test Extra Item 1", {
-				'is_stock_item': 1,
-				'stock_uom': 'Nos'
-			})
+		if not frappe.db.exists("Item", "Test Extra Item 1"):
+			make_item("Test Extra Item 1", {"is_stock_item": 1, "stock_uom": "Nos"})
 
-		if not frappe.db.exists('Item', "Test Extra Item 2"):
-			make_item("Test Extra Item 2", {
-				'is_stock_item': 1,
-				'stock_uom': 'Nos'
-			})
+		if not frappe.db.exists("Item", "Test Extra Item 2"):
+			make_item("Test Extra Item 2", {"is_stock_item": 1, "stock_uom": "Nos"})
 
-		if not frappe.db.exists('Item', "Test Extra Item 3"):
-			make_item("Test Extra Item 3", {
-				'is_stock_item': 1,
-				'stock_uom': 'Nos'
-			})
-		bom = frappe.get_doc({
-			'doctype': 'BOM',
-			'is_default': 1,
-			'item': item_code,
-			'currency': 'USD',
-			'quantity': 1,
-			'company': '_Test Company'
-		})
+		if not frappe.db.exists("Item", "Test Extra Item 3"):
+			make_item("Test Extra Item 3", {"is_stock_item": 1, "stock_uom": "Nos"})
+		bom = frappe.get_doc(
+			{
+				"doctype": "BOM",
+				"is_default": 1,
+				"item": item_code,
+				"currency": "USD",
+				"quantity": 1,
+				"company": "_Test Company",
+			}
+		)
 
 		for item in ["Test Extra Item 1", "Test Extra Item 2"]:
-			item_doc = frappe.get_doc('Item', item)
+			item_doc = frappe.get_doc("Item", item)
 
-			bom.append('items', {
-				'item_code': item,
-				'qty': 1,
-				'uom': item_doc.stock_uom,
-				'stock_uom': item_doc.stock_uom,
-				'rate': item_doc.valuation_rate
-			})
+			bom.append(
+				"items",
+				{
+					"item_code": item,
+					"qty": 1,
+					"uom": item_doc.stock_uom,
+					"stock_uom": item_doc.stock_uom,
+					"rate": item_doc.valuation_rate,
+				},
+			)
 
-		bom.append('items', {
-			'item_code': "Test Extra Item 3",
-			'qty': 1,
-			'uom': item_doc.stock_uom,
-			'stock_uom': item_doc.stock_uom,
-			'rate': 0,
-			'sourced_by_supplier': 1
-		})
+		bom.append(
+			"items",
+			{
+				"item_code": "Test Extra Item 3",
+				"qty": 1,
+				"uom": item_doc.stock_uom,
+				"stock_uom": item_doc.stock_uom,
+				"rate": 0,
+				"sourced_by_supplier": 1,
+			},
+		)
 		bom.insert(ignore_permissions=True)
 		bom.update_cost()
 		bom.submit()
 		# test that sourced_by_supplier rate is zero even after updating cost
 		self.assertEqual(bom.items[2].rate, 0)
 		# test in Purchase Order sourced_by_supplier is not added to Supplied Item
-		po = create_purchase_order(item_code=item_code, qty=1,
-			is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC")
+		po = create_purchase_order(
+			item_code=item_code, qty=1, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
+		)
 		bom_items = sorted([d.item_code for d in bom.items if d.sourced_by_supplier != 1])
 		supplied_items = sorted([d.rm_item_code for d in po.supplied_items])
 		self.assertEqual(bom_items, supplied_items)
@@ -253,7 +267,10 @@
 	def test_bom_tree_representation(self):
 		bom_tree = {
 			"Assembly": {
-				"SubAssembly1": {"ChildPart1": {}, "ChildPart2": {},},
+				"SubAssembly1": {
+					"ChildPart1": {},
+					"ChildPart2": {},
+				},
 				"SubAssembly2": {"ChildPart3": {}},
 				"SubAssembly3": {"SubSubAssy1": {"ChildPart4": {}}},
 				"ChildPart5": {},
@@ -264,7 +281,7 @@
 		parent_bom = create_nested_bom(bom_tree, prefix="")
 		created_tree = parent_bom.get_tree_representation()
 
-		reqd_order = level_order_traversal(bom_tree)[1:] # skip first item
+		reqd_order = level_order_traversal(bom_tree)[1:]  # skip first item
 		created_order = created_tree.level_order_traversal()
 
 		self.assertEqual(len(reqd_order), len(created_order))
@@ -276,14 +293,23 @@
 		from erpnext.controllers.item_variant import create_variant
 
 		template_item = make_item(
-			"_TestTemplateItem", {"has_variants": 1, "attributes": [{"attribute": "Test Size"},]}
+			"_TestTemplateItem",
+			{
+				"has_variants": 1,
+				"attributes": [
+					{"attribute": "Test Size"},
+				],
+			},
 		)
 		variant = create_variant(template_item.item_code, {"Test Size": "Large"})
 		variant.insert(ignore_if_duplicate=True)
 
 		bom_tree = {
 			template_item.item_code: {
-				"SubAssembly1": {"ChildPart1": {}, "ChildPart2": {},},
+				"SubAssembly1": {
+					"ChildPart1": {},
+					"ChildPart2": {},
+				},
 				"ChildPart5": {},
 			}
 		}
@@ -305,43 +331,36 @@
 
 	def test_bom_recursion_1st_level(self):
 		"""BOM should not allow BOM item again in child"""
-		item_code = "_Test BOM Recursion"
-		make_item(item_code, {'is_stock_item': 1})
+		item_code = make_item(properties={"is_stock_item": 1}).name
 
 		bom = frappe.new_doc("BOM")
 		bom.item = item_code
 		bom.append("items", frappe._dict(item_code=item_code))
-		with self.assertRaises(frappe.ValidationError) as err:
+		bom.save()
+		with self.assertRaises(BOMRecursionError):
+			bom.items[0].bom_no = bom.name
 			bom.save()
 
-		self.assertTrue("recursion" in str(err.exception).lower())
-		frappe.delete_doc("BOM", bom.name, ignore_missing=True)
-
 	def test_bom_recursion_transitive(self):
-		item1 = "_Test BOM Recursion"
-		item2 = "_Test BOM Recursion 2"
-		make_item(item1, {'is_stock_item': 1})
-		make_item(item2, {'is_stock_item': 1})
+		item1 = make_item(properties={"is_stock_item": 1}).name
+		item2 = make_item(properties={"is_stock_item": 1}).name
 
 		bom1 = frappe.new_doc("BOM")
 		bom1.item = item1
 		bom1.append("items", frappe._dict(item_code=item2))
 		bom1.save()
-		bom1.submit()
 
 		bom2 = frappe.new_doc("BOM")
 		bom2.item = item2
 		bom2.append("items", frappe._dict(item_code=item1))
+		bom2.save()
 
-		with self.assertRaises(frappe.ValidationError) as err:
+		bom2.items[0].bom_no = bom1.name
+		bom1.items[0].bom_no = bom2.name
+
+		with self.assertRaises(BOMRecursionError):
+			bom1.save()
 			bom2.save()
-			bom2.submit()
-
-		self.assertTrue("recursion" in str(err.exception).lower())
-
-		bom1.cancel()
-		frappe.delete_doc("BOM", bom1.name, ignore_missing=True, force=True)
-		frappe.delete_doc("BOM", bom2.name, ignore_missing=True, force=True)
 
 	def test_bom_with_process_loss_item(self):
 		fg_item_non_whole, fg_item_whole, bom_item = create_process_loss_bom_items()
@@ -377,19 +396,29 @@
 		self.assertRaises(frappe.ValidationError, bom_doc.submit)
 
 	def test_bom_item_query(self):
-		query = partial(item_query, doctype="Item", txt="", searchfield="name", start=0, page_len=20, filters={"is_stock_item": 1})
+		query = partial(
+			item_query,
+			doctype="Item",
+			txt="",
+			searchfield="name",
+			start=0,
+			page_len=20,
+			filters={"is_stock_item": 1},
+		)
 
 		test_items = query(txt="_Test")
 		filtered = query(txt="_Test Item 2")
 
-		self.assertNotEqual(len(test_items), len(filtered), msg="Item filtering showing excessive results")
+		self.assertNotEqual(
+			len(test_items), len(filtered), msg="Item filtering showing excessive results"
+		)
 		self.assertTrue(0 < len(filtered) <= 3, msg="Item filtering showing excessive results")
 
 	def test_exclude_exploded_items_from_bom(self):
 		bom_no = get_default_bom()
-		new_bom = frappe.copy_doc(frappe.get_doc('BOM', bom_no))
+		new_bom = frappe.copy_doc(frappe.get_doc("BOM", bom_no))
 		for row in new_bom.items:
-			if row.item_code == '_Test Item Home Desktop Manufactured':
+			if row.item_code == "_Test Item Home Desktop Manufactured":
 				self.assertTrue(row.bom_no)
 				row.do_not_explode = True
 
@@ -398,13 +427,15 @@
 		new_bom.load_from_db()
 
 		for row in new_bom.items:
-			if row.item_code == '_Test Item Home Desktop Manufactured' and row.do_not_explode:
+			if row.item_code == "_Test Item Home Desktop Manufactured" and row.do_not_explode:
 				self.assertFalse(row.bom_no)
 
 		new_bom.delete()
 
 	def test_valid_transfer_defaults(self):
-		bom_with_op = frappe.db.get_value("BOM", {"item": "_Test FG Item 2", "with_operations": 1, "is_active": 1})
+		bom_with_op = frappe.db.get_value(
+			"BOM", {"item": "_Test FG Item 2", "with_operations": 1, "is_active": 1}
+		)
 		bom = frappe.copy_doc(frappe.get_doc("BOM", bom_with_op), ignore_no_copy=False)
 
 		# test defaults
@@ -432,10 +463,107 @@
 		self.assertEqual(bom.transfer_material_against, "Work Order")
 		bom.delete()
 
+	def test_bom_name_length(self):
+		"""test >140 char names"""
+		bom_tree = {"x" * 140: {" ".join(["abc"] * 35): {}}}
+		create_nested_bom(bom_tree, prefix="")
+
+	def test_version_index(self):
+
+		bom = frappe.new_doc("BOM")
+
+		version_index_test_cases = [
+			(1, []),
+			(1, ["BOM#XYZ"]),
+			(2, ["BOM/ITEM/001"]),
+			(2, ["BOM-ITEM-001"]),
+			(3, ["BOM-ITEM-001", "BOM-ITEM-002"]),
+			(4, ["BOM-ITEM-001", "BOM-ITEM-002", "BOM-ITEM-003"]),
+		]
+
+		for expected_index, existing_boms in version_index_test_cases:
+			with self.subTest():
+				self.assertEqual(
+					expected_index,
+					bom.get_next_version_index(existing_boms),
+					msg=f"Incorrect index for {existing_boms}",
+				)
+
+	def test_bom_versioning(self):
+		bom_tree = {frappe.generate_hash(length=10): {frappe.generate_hash(length=10): {}}}
+		bom = create_nested_bom(bom_tree, prefix="")
+		self.assertEqual(int(bom.name.split("-")[-1]), 1)
+		original_bom_name = bom.name
+
+		bom.cancel()
+		bom.reload()
+		self.assertEqual(bom.name, original_bom_name)
+
+		# create a new amendment
+		amendment = frappe.copy_doc(bom)
+		amendment.docstatus = 0
+		amendment.amended_from = bom.name
+
+		amendment.save()
+		amendment.submit()
+		amendment.reload()
+
+		self.assertNotEqual(amendment.name, bom.name)
+		# `origname-001-1` version
+		self.assertEqual(int(amendment.name.split("-")[-1]), 1)
+		self.assertEqual(int(amendment.name.split("-")[-2]), 1)
+
+		# create a new version
+		version = frappe.copy_doc(amendment)
+		version.docstatus = 0
+		version.amended_from = None
+		version.save()
+		self.assertNotEqual(amendment.name, version.name)
+		self.assertEqual(int(version.name.split("-")[-1]), 2)
+
+	def test_clear_inpection_quality(self):
+
+		bom = frappe.copy_doc(test_records[2], ignore_no_copy=True)
+		bom.docstatus = 0
+		bom.is_default = 0
+		bom.quality_inspection_template = "_Test Quality Inspection Template"
+		bom.inspection_required = 1
+		bom.save()
+		bom.reload()
+
+		self.assertEqual(bom.quality_inspection_template, "_Test Quality Inspection Template")
+
+		bom.inspection_required = 0
+		bom.save()
+		bom.reload()
+
+		self.assertEqual(bom.quality_inspection_template, None)
+
+	def test_bom_pricing_based_on_lpp(self):
+		from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+
+		parent = frappe.generate_hash(length=10)
+		child = frappe.generate_hash(length=10)
+		bom_tree = {parent: {child: {}}}
+		bom = create_nested_bom(bom_tree, prefix="")
+
+		# add last purchase price
+		make_purchase_receipt(item_code=child, rate=42)
+
+		bom = frappe.copy_doc(bom)
+		bom.docstatus = 0
+		bom.amended_from = None
+		bom.rm_cost_as_per = "Last Purchase Rate"
+		bom.conversion_rate = 1
+		bom.save()
+		bom.submit()
+		self.assertEqual(bom.items[0].rate, 42)
+
 
 def get_default_bom(item_code="_Test FG Item 2"):
 	return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
 
+
 def level_order_traversal(node):
 	traversal = []
 	q = deque()
@@ -450,9 +578,9 @@
 
 	return traversal
 
+
 def create_nested_bom(tree, prefix="_Test bom "):
-	""" Helper function to create a simple nested bom from tree describing item names. (along with required items)
-	"""
+	"""Helper function to create a simple nested bom from tree describing item names. (along with required items)"""
 
 	def create_items(bom_tree):
 		for item_code, subtree in bom_tree.items():
@@ -460,6 +588,7 @@
 			if not frappe.db.exists("Item", bom_item_code):
 				frappe.get_doc(doctype="Item", item_code=bom_item_code, item_group="_Test Item Group").insert()
 			create_items(subtree)
+
 	create_items(tree)
 
 	def dfs(tree, node):
@@ -481,6 +610,7 @@
 			bom = frappe.get_doc(doctype="BOM", item=bom_item_code)
 			for child_item in child_items.keys():
 				bom.append("items", {"item_code": prefix + child_item})
+			bom.company = "_Test Company"
 			bom.currency = "INR"
 			bom.insert()
 			bom.submit()
@@ -493,10 +623,13 @@
 		warehouse_list = [warehouse_list]
 
 	if not warehouse_list:
-		warehouse_list = frappe.db.sql_list("""
+		warehouse_list = frappe.db.sql_list(
+			"""
 			select warehouse from `tabBin`
 			where item_code=%s and actual_qty > 0
-		""", item_code)
+		""",
+			item_code,
+		)
 
 		if not warehouse_list:
 			warehouse_list.append("_Test Warehouse - _TC")
@@ -504,44 +637,51 @@
 	for warehouse in warehouse_list:
 		create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=qty, rate=rate)
 
+
 def create_bom_with_process_loss_item(
-		fg_item, bom_item, scrap_qty, scrap_rate, fg_qty=2, is_process_loss=1):
+	fg_item, bom_item, scrap_qty, scrap_rate, fg_qty=2, is_process_loss=1
+):
 	bom_doc = frappe.new_doc("BOM")
 	bom_doc.item = fg_item.item_code
 	bom_doc.quantity = fg_qty
-	bom_doc.append("items", {
-		"item_code": bom_item.item_code,
-		"qty": 1,
-		"uom": bom_item.stock_uom,
-		"stock_uom": bom_item.stock_uom,
-		"rate": 100.0
-	})
-	bom_doc.append("scrap_items", {
-		"item_code": fg_item.item_code,
-		"qty": scrap_qty,
-		"stock_qty": scrap_qty,
-		"uom": fg_item.stock_uom,
-		"stock_uom": fg_item.stock_uom,
-		"rate": scrap_rate,
-		"is_process_loss": is_process_loss
-	})
+	bom_doc.append(
+		"items",
+		{
+			"item_code": bom_item.item_code,
+			"qty": 1,
+			"uom": bom_item.stock_uom,
+			"stock_uom": bom_item.stock_uom,
+			"rate": 100.0,
+		},
+	)
+	bom_doc.append(
+		"scrap_items",
+		{
+			"item_code": fg_item.item_code,
+			"qty": scrap_qty,
+			"stock_qty": scrap_qty,
+			"uom": fg_item.stock_uom,
+			"stock_uom": fg_item.stock_uom,
+			"rate": scrap_rate,
+			"is_process_loss": is_process_loss,
+		},
+	)
 	bom_doc.currency = "INR"
 	return bom_doc
 
+
 def create_process_loss_bom_items():
 	item_list = [
 		("_Test Item - Non Whole UOM", "Kg"),
 		("_Test Item - Whole UOM", "Unit"),
-		("_Test PL BOM Item", "Unit")
+		("_Test PL BOM Item", "Unit"),
 	]
 	return [create_process_loss_bom_item(it) for it in item_list]
 
+
 def create_process_loss_bom_item(item_tuple):
 	item_code, stock_uom = item_tuple
 	if frappe.db.exists("Item", item_code) is None:
-		return make_item(
-			item_code,
-			{'stock_uom':stock_uom, 'valuation_rate':100}
-		)
+		return make_item(item_code, {"stock_uom": stock_uom, "valuation_rate": 100})
 	else:
 		return frappe.get_doc("Item", item_code)
diff --git a/erpnext/manufacturing/doctype/bom/test_records.json b/erpnext/manufacturing/doctype/bom/test_records.json
index 25730f9..507d319 100644
--- a/erpnext/manufacturing/doctype/bom/test_records.json
+++ b/erpnext/manufacturing/doctype/bom/test_records.json
@@ -32,6 +32,7 @@
   "is_active": 1,
   "is_default": 1,
   "item": "_Test Item Home Desktop Manufactured",
+  "company": "_Test Company",
   "quantity": 1.0
  },
  {
diff --git a/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json b/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json
index f01d856..9b1db63 100644
--- a/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json
+++ b/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json
@@ -169,13 +169,15 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-10-08 16:21:29.386212",
+ "modified": "2022-05-27 13:42:23.305455",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "BOM Explosion Item",
+ "naming_rule": "Random",
  "owner": "Administrator",
  "permissions": [],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/bom_item/bom_item.json b/erpnext/manufacturing/doctype/bom_item/bom_item.json
index 3406215..0a8ae7b 100644
--- a/erpnext/manufacturing/doctype/bom_item/bom_item.json
+++ b/erpnext/manufacturing/doctype/bom_item/bom_item.json
@@ -33,7 +33,6 @@
   "amount",
   "base_amount",
   "section_break_18",
-  "scrap",
   "qty_consumed_per_unit",
   "section_break_27",
   "has_variants",
@@ -224,15 +223,6 @@
    "fieldtype": "Section Break"
   },
   {
-   "columns": 1,
-   "fieldname": "scrap",
-   "fieldtype": "Float",
-   "label": "Scrap %",
-   "oldfieldname": "scrap",
-   "oldfieldtype": "Currency",
-   "print_hide": 1
-  },
-  {
    "fieldname": "qty_consumed_per_unit",
    "fieldtype": "Float",
    "hidden": 1,
@@ -298,7 +288,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2022-01-24 16:57:57.020232",
+ "modified": "2022-05-19 02:32:43.785470",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "BOM Item",
diff --git a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
index c7be7ef..b965a43 100644
--- a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
+++ b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
@@ -66,7 +66,8 @@
    "label": "Hour Rate",
    "oldfieldname": "hour_rate",
    "oldfieldtype": "Currency",
-   "options": "currency"
+   "options": "currency",
+   "precision": "2"
   },
   {
    "description": "In minutes",
@@ -108,7 +109,6 @@
    "read_only": 1
   },
   {
-   "default": "5",
    "depends_on": "eval:parent.doctype == 'BOM'",
    "fieldname": "base_operating_cost",
    "fieldtype": "Currency",
@@ -186,7 +186,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-12-15 03:00:00.473173",
+ "modified": "2022-04-08 01:18:33.547481",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "BOM Operation",
diff --git a/erpnext/education/doctype/__init__.py b/erpnext/manufacturing/doctype/bom_update_batch/__init__.py
similarity index 100%
copy from erpnext/education/doctype/__init__.py
copy to erpnext/manufacturing/doctype/bom_update_batch/__init__.py
diff --git a/erpnext/manufacturing/doctype/bom_update_batch/bom_update_batch.json b/erpnext/manufacturing/doctype/bom_update_batch/bom_update_batch.json
new file mode 100644
index 0000000..b867d2a
--- /dev/null
+++ b/erpnext/manufacturing/doctype/bom_update_batch/bom_update_batch.json
@@ -0,0 +1,54 @@
+{
+ "actions": [],
+ "autoname": "hash",
+ "creation": "2022-05-31 17:34:39.825537",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+  "level",
+  "batch_no",
+  "boms_updated",
+  "status"
+ ],
+ "fields": [
+  {
+   "fieldname": "level",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Level"
+  },
+  {
+   "fieldname": "batch_no",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Batch No."
+  },
+  {
+   "fieldname": "boms_updated",
+   "fieldtype": "Long Text",
+   "hidden": 1,
+   "in_list_view": 1,
+   "label": "BOMs Updated"
+  },
+  {
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Status",
+   "options": "Pending\nCompleted",
+   "read_only": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2022-06-06 14:50:35.161062",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "BOM Update Batch",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
diff --git a/erpnext/manufacturing/doctype/bom_update_batch/bom_update_batch.py b/erpnext/manufacturing/doctype/bom_update_batch/bom_update_batch.py
new file mode 100644
index 0000000..f952e43
--- /dev/null
+++ b/erpnext/manufacturing/doctype/bom_update_batch/bom_update_batch.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class BOMUpdateBatch(Document):
+	pass
diff --git a/erpnext/education/doctype/instructor_log/__init__.py b/erpnext/manufacturing/doctype/bom_update_log/__init__.py
similarity index 100%
rename from erpnext/education/doctype/instructor_log/__init__.py
rename to erpnext/manufacturing/doctype/bom_update_log/__init__.py
diff --git a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.js b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.js
new file mode 100644
index 0000000..6da808e
--- /dev/null
+++ b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('BOM Update Log', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.json b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.json
new file mode 100644
index 0000000..c32e383
--- /dev/null
+++ b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.json
@@ -0,0 +1,136 @@
+{
+ "actions": [],
+ "autoname": "BOM-UPDT-LOG-.#####",
+ "creation": "2022-03-16 14:23:35.210155",
+ "description": "BOM Update Tool Log with job status maintained",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "current_bom",
+  "new_bom",
+  "column_break_3",
+  "update_type",
+  "status",
+  "error_log",
+  "progress_section",
+  "current_level",
+  "processed_boms",
+  "bom_batches",
+  "amended_from"
+ ],
+ "fields": [
+  {
+   "fieldname": "current_bom",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Current BOM",
+   "options": "BOM"
+  },
+  {
+   "fieldname": "new_bom",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "New BOM",
+   "options": "BOM"
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "update_type",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Update Type",
+   "options": "Replace BOM\nUpdate Cost"
+  },
+  {
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "label": "Status",
+   "options": "Queued\nIn Progress\nCompleted\nFailed"
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "BOM Update Log",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "error_log",
+   "fieldtype": "Link",
+   "label": "Error Log",
+   "options": "Error Log"
+  },
+  {
+   "collapsible": 1,
+   "depends_on": "eval: doc.update_type == \"Update Cost\"",
+   "fieldname": "progress_section",
+   "fieldtype": "Section Break",
+   "label": "Progress"
+  },
+  {
+   "fieldname": "processed_boms",
+   "fieldtype": "Long Text",
+   "hidden": 1,
+   "label": "Processed BOMs"
+  },
+  {
+   "fieldname": "bom_batches",
+   "fieldtype": "Table",
+   "options": "BOM Update Batch"
+  },
+  {
+   "fieldname": "current_level",
+   "fieldtype": "Int",
+   "label": "Current Level"
+  }
+ ],
+ "in_create": 1,
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2022-06-06 15:15:23.883251",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "BOM Update Log",
+ "naming_rule": "Expression (old style)",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Manufacturing Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": [],
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py
new file mode 100644
index 0000000..9c9c240
--- /dev/null
+++ b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py
@@ -0,0 +1,234 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+import json
+from typing import Any, Dict, List, Optional, Tuple, Union
+
+import frappe
+from frappe import _
+from frappe.model.document import Document
+from frappe.utils import cint, cstr
+
+from erpnext.manufacturing.doctype.bom_update_log.bom_updation_utils import (
+	get_leaf_boms,
+	get_next_higher_level_boms,
+	handle_exception,
+	replace_bom,
+	set_values_in_log,
+)
+
+
+class BOMMissingError(frappe.ValidationError):
+	pass
+
+
+class BOMUpdateLog(Document):
+	def validate(self):
+		if self.update_type == "Replace BOM":
+			self.validate_boms_are_specified()
+			self.validate_same_bom()
+			self.validate_bom_items()
+		else:
+			self.validate_bom_cost_update_in_progress()
+
+		self.status = "Queued"
+
+	def validate_boms_are_specified(self):
+		if self.update_type == "Replace BOM" and not (self.current_bom and self.new_bom):
+			frappe.throw(
+				msg=_("Please mention the Current and New BOM for replacement."),
+				title=_("Mandatory"),
+				exc=BOMMissingError,
+			)
+
+	def validate_same_bom(self):
+		if cstr(self.current_bom) == cstr(self.new_bom):
+			frappe.throw(_("Current BOM and New BOM can not be same"))
+
+	def validate_bom_items(self):
+		current_bom_item = frappe.db.get_value("BOM", self.current_bom, "item")
+		new_bom_item = frappe.db.get_value("BOM", self.new_bom, "item")
+
+		if current_bom_item != new_bom_item:
+			frappe.throw(_("The selected BOMs are not for the same item"))
+
+	def validate_bom_cost_update_in_progress(self):
+		"If another Cost Updation Log is still in progress, dont make new ones."
+
+		wip_log = frappe.get_all(
+			"BOM Update Log",
+			{"update_type": "Update Cost", "status": ["in", ["Queued", "In Progress"]]},
+			limit_page_length=1,
+		)
+		if wip_log:
+			log_link = frappe.utils.get_link_to_form("BOM Update Log", wip_log[0].name)
+			frappe.throw(
+				_("BOM Updation already in progress. Please wait until {0} is complete.").format(log_link),
+				title=_("Note"),
+			)
+
+	def on_submit(self):
+		if self.update_type == "Replace BOM":
+			boms = {"current_bom": self.current_bom, "new_bom": self.new_bom}
+			frappe.enqueue(
+				method="erpnext.manufacturing.doctype.bom_update_log.bom_update_log.run_replace_bom_job",
+				doc=self,
+				boms=boms,
+				timeout=40000,
+				now=frappe.flags.in_test,
+			)
+		else:
+			process_boms_cost_level_wise(self)
+
+
+def run_replace_bom_job(
+	doc: "BOMUpdateLog",
+	boms: Optional[Dict[str, str]] = None,
+) -> None:
+	try:
+		doc.db_set("status", "In Progress")
+
+		if not frappe.flags.in_test:
+			frappe.db.commit()
+
+		frappe.db.auto_commit_on_many_writes = 1
+		boms = frappe._dict(boms or {})
+		replace_bom(boms, doc.name)
+
+		doc.db_set("status", "Completed")
+	except Exception:
+		handle_exception(doc)
+	finally:
+		frappe.db.auto_commit_on_many_writes = 0
+
+		if not frappe.flags.in_test:
+			frappe.db.commit()  # nosemgrep
+
+
+def process_boms_cost_level_wise(
+	update_doc: "BOMUpdateLog", parent_boms: List[str] = None
+) -> Union[None, Tuple]:
+	"Queue jobs at the start of new BOM Level in 'Update Cost' Jobs."
+
+	current_boms = {}
+	values = {}
+
+	if update_doc.status == "Queued":
+		# First level yet to process. On Submit.
+		current_level = 0
+		current_boms = get_leaf_boms()
+		values = {
+			"processed_boms": json.dumps({}),
+			"status": "In Progress",
+			"current_level": current_level,
+		}
+	else:
+		# Resume next level. via Cron Job.
+		if not parent_boms:
+			return
+
+		current_level = cint(update_doc.current_level) + 1
+
+		# Process the next level BOMs. Stage parents as current BOMs.
+		current_boms = parent_boms.copy()
+		values = {"current_level": current_level}
+
+	set_values_in_log(update_doc.name, values, commit=True)
+	queue_bom_cost_jobs(current_boms, update_doc, current_level)
+
+
+def queue_bom_cost_jobs(
+	current_boms_list: List[str], update_doc: "BOMUpdateLog", current_level: int
+) -> None:
+	"Queue batches of 20k BOMs of the same level to process parallelly"
+	batch_no = 0
+
+	while current_boms_list:
+		batch_no += 1
+		batch_size = 20_000
+		boms_to_process = current_boms_list[:batch_size]  # slice out batch of 20k BOMs
+
+		# update list to exclude 20K (queued) BOMs
+		current_boms_list = current_boms_list[batch_size:] if len(current_boms_list) > batch_size else []
+
+		batch_row = update_doc.append(
+			"bom_batches", {"level": current_level, "batch_no": batch_no, "status": "Pending"}
+		)
+		batch_row.db_insert()
+
+		frappe.enqueue(
+			method="erpnext.manufacturing.doctype.bom_update_log.bom_updation_utils.update_cost_in_level",
+			doc=update_doc,
+			bom_list=boms_to_process,
+			batch_name=batch_row.name,
+			queue="long",
+			now=frappe.flags.in_test,
+		)
+
+
+def resume_bom_cost_update_jobs():
+	"""
+	1. Checks for In Progress BOM Update Log.
+	2. Checks if this job has completed the _current level_.
+	3. If current level is complete, get parent BOMs and start next level.
+	4. If no parents, mark as Complete.
+	5. If current level is WIP, skip the Log.
+
+	Called every 5 minutes via Cron job.
+	"""
+
+	in_progress_logs = frappe.db.get_all(
+		"BOM Update Log",
+		{"update_type": "Update Cost", "status": "In Progress"},
+		["name", "processed_boms", "current_level"],
+	)
+	if not in_progress_logs:
+		return
+
+	for log in in_progress_logs:
+		# check if all log batches of current level are processed
+		bom_batches = frappe.db.get_all(
+			"BOM Update Batch",
+			{"parent": log.name, "level": log.current_level},
+			["name", "boms_updated", "status"],
+		)
+		incomplete_level = any(row.get("status") == "Pending" for row in bom_batches)
+		if not bom_batches or incomplete_level:
+			continue
+
+		# Prep parent BOMs & updated processed BOMs for next level
+		current_boms, processed_boms = get_processed_current_boms(log, bom_batches)
+		parent_boms = get_next_higher_level_boms(child_boms=current_boms, processed_boms=processed_boms)
+
+		# Unset processed BOMs if log is complete, it is used for next level BOMs
+		set_values_in_log(
+			log.name,
+			values={
+				"processed_boms": json.dumps([] if not parent_boms else processed_boms),
+				"status": "Completed" if not parent_boms else "In Progress",
+			},
+			commit=True,
+		)
+
+		if parent_boms:  # there is a next level to process
+			process_boms_cost_level_wise(
+				update_doc=frappe.get_doc("BOM Update Log", log.name), parent_boms=parent_boms
+			)
+
+
+def get_processed_current_boms(
+	log: Dict[str, Any], bom_batches: Dict[str, Any]
+) -> Tuple[List[str], Dict[str, Any]]:
+	"""
+	Aggregate all BOMs from BOM Update Batch rows into 'processed_boms' field
+	and into current boms list.
+	"""
+	processed_boms = json.loads(log.processed_boms) if log.processed_boms else {}
+	current_boms = []
+
+	for row in bom_batches:
+		boms_updated = json.loads(row.boms_updated)
+		current_boms.extend(boms_updated)
+		boms_updated_dict = {bom: True for bom in boms_updated}
+		processed_boms.update(boms_updated_dict)
+
+	return current_boms, processed_boms
diff --git a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log_list.js b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log_list.js
new file mode 100644
index 0000000..e39b563
--- /dev/null
+++ b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log_list.js
@@ -0,0 +1,13 @@
+frappe.listview_settings['BOM Update Log'] = {
+	add_fields: ["status"],
+	get_indicator: function(doc) {
+		let status_map = {
+			"Queued": "orange",
+			"In Progress": "blue",
+			"Completed": "green",
+			"Failed": "red"
+		};
+
+		return [__(doc.status), status_map[doc.status], "status,=," + doc.status];
+	}
+};
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/bom_update_log/bom_updation_utils.py b/erpnext/manufacturing/doctype/bom_update_log/bom_updation_utils.py
new file mode 100644
index 0000000..af115e3
--- /dev/null
+++ b/erpnext/manufacturing/doctype/bom_update_log/bom_updation_utils.py
@@ -0,0 +1,225 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import copy
+import json
+from collections import defaultdict
+from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
+
+if TYPE_CHECKING:
+	from erpnext.manufacturing.doctype.bom_update_log.bom_update_log import BOMUpdateLog
+
+import frappe
+from frappe import _
+
+
+def replace_bom(boms: Dict, log_name: str) -> None:
+	"Replace current BOM with new BOM in parent BOMs."
+
+	current_bom = boms.get("current_bom")
+	new_bom = boms.get("new_bom")
+
+	unit_cost = get_bom_unit_cost(new_bom)
+	update_new_bom_in_bom_items(unit_cost, current_bom, new_bom)
+
+	frappe.cache().delete_key("bom_children")
+	parent_boms = get_ancestor_boms(new_bom)
+
+	for bom in parent_boms:
+		bom_obj = frappe.get_doc("BOM", bom)
+		# this is only used for versioning and we do not want
+		# to make separate db calls by using load_doc_before_save
+		# which proves to be expensive while doing bulk replace
+		bom_obj._doc_before_save = copy.deepcopy(bom_obj)
+		bom_obj.update_exploded_items()
+		bom_obj.calculate_cost()
+		bom_obj.update_parent_cost()
+		bom_obj.db_update()
+		bom_obj.flags.updater_reference = {
+			"doctype": "BOM Update Log",
+			"docname": log_name,
+			"label": _("via BOM Update Tool"),
+		}
+		bom_obj.save_version()
+
+
+def update_cost_in_level(
+	doc: "BOMUpdateLog", bom_list: List[str], batch_name: Union[int, str]
+) -> None:
+	"Updates Cost for BOMs within a given level. Runs via background jobs."
+
+	try:
+		status = frappe.db.get_value("BOM Update Log", doc.name, "status")
+		if status == "Failed":
+			return
+
+		update_cost_in_boms(bom_list=bom_list)  # main updation logic
+
+		bom_batch = frappe.qb.DocType("BOM Update Batch")
+		(
+			frappe.qb.update(bom_batch)
+			.set(bom_batch.boms_updated, json.dumps(bom_list))
+			.set(bom_batch.status, "Completed")
+			.where(bom_batch.name == batch_name)
+		).run()
+	except Exception:
+		handle_exception(doc)
+	finally:
+		if not frappe.flags.in_test:
+			frappe.db.commit()  # nosemgrep
+
+
+def get_ancestor_boms(new_bom: str, bom_list: Optional[List] = None) -> List:
+	"Recursively get all ancestors of BOM."
+
+	bom_list = bom_list or []
+	bom_item = frappe.qb.DocType("BOM Item")
+
+	parents = (
+		frappe.qb.from_(bom_item)
+		.select(bom_item.parent)
+		.where((bom_item.bom_no == new_bom) & (bom_item.docstatus < 2) & (bom_item.parenttype == "BOM"))
+		.run(as_dict=True)
+	)
+
+	for d in parents:
+		if new_bom == d.parent:
+			frappe.throw(_("BOM recursion: {0} cannot be child of {1}").format(new_bom, d.parent))
+
+		bom_list.append(d.parent)
+		get_ancestor_boms(d.parent, bom_list)
+
+	return list(set(bom_list))
+
+
+def update_new_bom_in_bom_items(unit_cost: float, current_bom: str, new_bom: str) -> None:
+	bom_item = frappe.qb.DocType("BOM Item")
+	(
+		frappe.qb.update(bom_item)
+		.set(bom_item.bom_no, new_bom)
+		.set(bom_item.rate, unit_cost)
+		.set(bom_item.amount, (bom_item.stock_qty * unit_cost))
+		.where(
+			(bom_item.bom_no == current_bom) & (bom_item.docstatus < 2) & (bom_item.parenttype == "BOM")
+		)
+	).run()
+
+
+def get_bom_unit_cost(bom_name: str) -> float:
+	bom = frappe.qb.DocType("BOM")
+	new_bom_unitcost = (
+		frappe.qb.from_(bom).select(bom.total_cost / bom.quantity).where(bom.name == bom_name).run()
+	)
+
+	return frappe.utils.flt(new_bom_unitcost[0][0])
+
+
+def update_cost_in_boms(bom_list: List[str]) -> None:
+	"Updates cost in given BOMs. Returns current and total updated BOMs."
+
+	for index, bom in enumerate(bom_list):
+		bom_doc = frappe.get_doc("BOM", bom, for_update=True)
+		bom_doc.calculate_cost(save_updates=True, update_hour_rate=True)
+		bom_doc.db_update()
+
+		if (index % 50 == 0) and not frappe.flags.in_test:
+			frappe.db.commit()  # nosemgrep
+
+
+def get_next_higher_level_boms(
+	child_boms: List[str], processed_boms: Dict[str, bool]
+) -> List[str]:
+	"Generate immediate higher level dependants with no unresolved dependencies (children)."
+
+	def _all_children_are_processed(parent_bom):
+		child_boms = dependency_map.get(parent_bom)
+		return all(processed_boms.get(bom) for bom in child_boms)
+
+	dependants_map, dependency_map = _generate_dependence_map()
+
+	dependants = []
+	for bom in child_boms:
+		# generate list of immediate dependants
+		parents = dependants_map.get(bom) or []
+		dependants.extend(parents)
+
+	dependants = set(dependants)  # remove duplicates
+	resolved_dependants = set()
+
+	# consider only if children are all resolved
+	for parent_bom in dependants:
+		if _all_children_are_processed(parent_bom):
+			resolved_dependants.add(parent_bom)
+
+	return list(resolved_dependants)
+
+
+def get_leaf_boms() -> List[str]:
+	"Get BOMs that have no dependencies."
+
+	return frappe.db.sql_list(
+		"""select name from `tabBOM` bom
+		where docstatus=1 and is_active=1
+			and not exists(select bom_no from `tabBOM Item`
+				where parent=bom.name and ifnull(bom_no, '')!='')"""
+	)
+
+
+def _generate_dependence_map() -> defaultdict:
+	"""
+	Generate maps such as: { BOM-1: [Dependant-BOM-1, Dependant-BOM-2, ..] }.
+	Here BOM-1 is the leaf/lower level node/dependency.
+	The list contains one level higher nodes/dependants that depend on BOM-1.
+
+	Generate and return the reverse as well.
+	"""
+
+	bom = frappe.qb.DocType("BOM")
+	bom_item = frappe.qb.DocType("BOM Item")
+
+	bom_items = (
+		frappe.qb.from_(bom_item)
+		.join(bom)
+		.on(bom_item.parent == bom.name)
+		.select(bom_item.bom_no, bom_item.parent)
+		.where(
+			(bom_item.bom_no.isnotnull())
+			& (bom_item.bom_no != "")
+			& (bom.docstatus == 1)
+			& (bom.is_active == 1)
+			& (bom_item.parenttype == "BOM")
+		)
+	).run(as_dict=True)
+
+	child_parent_map = defaultdict(list)
+	parent_child_map = defaultdict(list)
+	for row in bom_items:
+		child_parent_map[row.bom_no].append(row.parent)
+		parent_child_map[row.parent].append(row.bom_no)
+
+	return child_parent_map, parent_child_map
+
+
+def set_values_in_log(log_name: str, values: Dict[str, Any], commit: bool = False) -> None:
+	"Update BOM Update Log record."
+
+	if not values:
+		return
+
+	bom_update_log = frappe.qb.DocType("BOM Update Log")
+	query = frappe.qb.update(bom_update_log).where(bom_update_log.name == log_name)
+
+	for key, value in values.items():
+		query = query.set(key, value)
+	query.run()
+
+	if commit and not frappe.flags.in_test:
+		frappe.db.commit()  # nosemgrep
+
+
+def handle_exception(doc: "BOMUpdateLog") -> None:
+	"Rolls back and fails BOM Update Log."
+
+	frappe.db.rollback()
+	error_log = doc.log_error("BOM Update Tool Error")
+	set_values_in_log(doc.name, {"status": "Failed", "error_log": error_log.name})
diff --git a/erpnext/manufacturing/doctype/bom_update_log/test_bom_update_log.py b/erpnext/manufacturing/doctype/bom_update_log/test_bom_update_log.py
new file mode 100644
index 0000000..b38fc89
--- /dev/null
+++ b/erpnext/manufacturing/doctype/bom_update_log/test_bom_update_log.py
@@ -0,0 +1,71 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+import frappe
+from frappe.tests.utils import FrappeTestCase
+
+from erpnext.manufacturing.doctype.bom_update_log.bom_update_log import (
+	BOMMissingError,
+	resume_bom_cost_update_jobs,
+)
+from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import (
+	enqueue_replace_bom,
+	enqueue_update_cost,
+)
+
+test_records = frappe.get_test_records("BOM")
+
+
+class TestBOMUpdateLog(FrappeTestCase):
+	"Test BOM Update Tool Operations via BOM Update Log."
+
+	def setUp(self):
+		bom_doc = frappe.copy_doc(test_records[0])
+		bom_doc.items[1].item_code = "_Test Item"
+		bom_doc.insert()
+
+		self.boms = frappe._dict(
+			current_bom="BOM-_Test Item Home Desktop Manufactured-001",
+			new_bom=bom_doc.name,
+		)
+
+		self.new_bom_doc = bom_doc
+
+	def tearDown(self):
+		frappe.db.rollback()
+
+	def test_bom_update_log_validate(self):
+		"""
+		1) Test if BOM presence is validated.
+		2) Test if same BOMs are validated.
+		3) Test of non-existent BOM is validated.
+		"""
+
+		with self.assertRaises(BOMMissingError):
+			enqueue_replace_bom(boms={})
+
+		with self.assertRaises(frappe.ValidationError):
+			enqueue_replace_bom(boms=frappe._dict(current_bom=self.boms.new_bom, new_bom=self.boms.new_bom))
+
+		with self.assertRaises(frappe.ValidationError):
+			enqueue_replace_bom(boms=frappe._dict(current_bom=self.boms.new_bom, new_bom="Dummy BOM"))
+
+	def test_bom_update_log_completion(self):
+		"Test if BOM Update Log handles job completion correctly."
+
+		log = enqueue_replace_bom(boms=self.boms)
+		log.reload()
+		self.assertEqual(log.status, "Completed")
+
+
+def update_cost_in_all_boms_in_test():
+	"""
+	Utility to run 'Update Cost' job in tests without Cron job until fully complete.
+	"""
+	log = enqueue_update_cost()  # create BOM Update Log
+
+	while log.status != "Completed":
+		resume_bom_cost_update_jobs()  # run cron job until complete
+		log.reload()
+
+	return log
diff --git a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.js b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.js
index bf5fe2e..7ba6517 100644
--- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.js
+++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.js
@@ -20,30 +20,67 @@
 
 	refresh: function(frm) {
 		frm.disable_save();
+		frm.events.disable_button(frm, "replace");
+
+		frm.add_custom_button(__("View BOM Update Log"), () => {
+			frappe.set_route("List", "BOM Update Log");
+		});
 	},
 
-	replace: function(frm) {
+	disable_button: (frm, field, disable=true) => {
+		frm.get_field(field).input.disabled = disable;
+	},
+
+	current_bom: (frm) => {
+		if (frm.doc.current_bom && frm.doc.new_bom) {
+			frm.events.disable_button(frm, "replace", false);
+		}
+	},
+
+	new_bom: (frm) => {
+		if (frm.doc.current_bom && frm.doc.new_bom) {
+			frm.events.disable_button(frm, "replace", false);
+		}
+	},
+
+	replace: (frm) => {
 		if (frm.doc.current_bom && frm.doc.new_bom) {
 			frappe.call({
 				method: "erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.enqueue_replace_bom",
 				freeze: true,
 				args: {
-					args: {
+					boms: {
 						"current_bom": frm.doc.current_bom,
 						"new_bom": frm.doc.new_bom
 					}
+				},
+				callback: result => {
+					if (result && result.message && !result.exc) {
+						frm.events.confirm_job_start(frm, result.message);
+					}
 				}
 			});
 		}
 	},
 
-	update_latest_price_in_all_boms: function() {
+	update_latest_price_in_all_boms: (frm) => {
 		frappe.call({
 			method: "erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.enqueue_update_cost",
 			freeze: true,
-			callback: function() {
-				frappe.msgprint(__("Latest price updated in all BOMs"));
+			callback: result => {
+				if (result && result.message && !result.exc) {
+					frm.events.confirm_job_start(frm, result.message);
+				}
 			}
 		});
+	},
+
+	confirm_job_start: (frm, log_data) => {
+		let log_link = frappe.utils.get_form_link("BOM Update Log", log_data.name, true);
+		frappe.msgprint({
+			"message": __("BOM Updation is queued and may take a few minutes. Check {0} for progress.", [log_link]),
+			"title": __("BOM Update Initiated"),
+			"indicator": "blue"
+		});
 	}
 });
diff --git a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
index 0e3955f..d16fcd0 100644
--- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
+++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
@@ -1,113 +1,66 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
 
-
 import json
+from typing import TYPE_CHECKING, Dict, Literal, Optional, Union
 
-import click
+if TYPE_CHECKING:
+	from erpnext.manufacturing.doctype.bom_update_log.bom_update_log import BOMUpdateLog
+
 import frappe
-from frappe import _
 from frappe.model.document import Document
-from frappe.utils import cstr, flt
-
-from erpnext.manufacturing.doctype.bom.bom import get_boms_in_bottom_up_order
 
 
 class BOMUpdateTool(Document):
-	def replace_bom(self):
-		self.validate_bom()
+	pass
 
-		unit_cost = get_new_bom_unit_cost(self.new_bom)
-		self.update_new_bom(unit_cost)
-
-		frappe.cache().delete_key('bom_children')
-		bom_list = self.get_parent_boms(self.new_bom)
-
-		with click.progressbar(bom_list) as bom_list:
-			pass
-		for bom in bom_list:
-			try:
-				bom_obj = frappe.get_cached_doc('BOM', bom)
-				# this is only used for versioning and we do not want
-				# to make separate db calls by using load_doc_before_save
-				# which proves to be expensive while doing bulk replace
-				bom_obj._doc_before_save = bom_obj
-				bom_obj.update_new_bom(self.current_bom, self.new_bom, unit_cost)
-				bom_obj.update_exploded_items()
-				bom_obj.calculate_cost()
-				bom_obj.update_parent_cost()
-				bom_obj.db_update()
-				if bom_obj.meta.get('track_changes') and not bom_obj.flags.ignore_version:
-					bom_obj.save_version()
-			except Exception:
-				frappe.log_error(frappe.get_traceback())
-
-	def validate_bom(self):
-		if cstr(self.current_bom) == cstr(self.new_bom):
-			frappe.throw(_("Current BOM and New BOM can not be same"))
-
-		if frappe.db.get_value("BOM", self.current_bom, "item") \
-			!= frappe.db.get_value("BOM", self.new_bom, "item"):
-				frappe.throw(_("The selected BOMs are not for the same item"))
-
-	def update_new_bom(self, unit_cost):
-		frappe.db.sql("""update `tabBOM Item` set bom_no=%s,
-			rate=%s, amount=stock_qty*%s where bom_no = %s and docstatus < 2 and parenttype='BOM'""",
-			(self.new_bom, unit_cost, unit_cost, self.current_bom))
-
-	def get_parent_boms(self, bom, bom_list=None):
-		if bom_list is None:
-			bom_list = []
-		data = frappe.db.sql("""SELECT DISTINCT parent FROM `tabBOM Item`
-			WHERE bom_no = %s AND docstatus < 2 AND parenttype='BOM'""", bom)
-
-		for d in data:
-			if self.new_bom == d[0]:
-				frappe.throw(_("BOM recursion: {0} cannot be child of {1}").format(bom, self.new_bom))
-
-			bom_list.append(d[0])
-			self.get_parent_boms(d[0], bom_list)
-
-		return list(set(bom_list))
-
-def get_new_bom_unit_cost(bom):
-	new_bom_unitcost = frappe.db.sql("""SELECT `total_cost`/`quantity`
-		FROM `tabBOM` WHERE name = %s""", bom)
-
-	return flt(new_bom_unitcost[0][0]) if new_bom_unitcost else 0
 
 @frappe.whitelist()
-def enqueue_replace_bom(args):
-	if isinstance(args, str):
-		args = json.loads(args)
+def enqueue_replace_bom(
+	boms: Optional[Union[Dict, str]] = None, args: Optional[Union[Dict, str]] = None
+) -> "BOMUpdateLog":
+	"""Returns a BOM Update Log (that queues a job) for BOM Replacement."""
+	boms = boms or args
+	if isinstance(boms, str):
+		boms = json.loads(boms)
 
-	frappe.enqueue("erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.replace_bom", args=args, timeout=40000)
-	frappe.msgprint(_("Queued for replacing the BOM. It may take a few minutes."))
+	update_log = create_bom_update_log(boms=boms)
+	return update_log
+
 
 @frappe.whitelist()
-def enqueue_update_cost():
-	frappe.enqueue("erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_cost", timeout=40000)
-	frappe.msgprint(_("Queued for updating latest price in all Bill of Materials. It may take a few minutes."))
+def enqueue_update_cost() -> "BOMUpdateLog":
+	"""Returns a BOM Update Log (that queues a job) for BOM Cost Updation."""
+	update_log = create_bom_update_log(update_type="Update Cost")
+	return update_log
 
-def update_latest_price_in_all_boms():
+
+def auto_update_latest_price_in_all_boms() -> None:
+	"""Called via hooks.py."""
 	if frappe.db.get_single_value("Manufacturing Settings", "update_bom_costs_automatically"):
-		update_cost()
+		wip_log = frappe.get_all(
+			"BOM Update Log",
+			{"update_type": "Update Cost", "status": ["in", ["Queued", "In Progress"]]},
+			limit_page_length=1,
+		)
+		if not wip_log:
+			create_bom_update_log(update_type="Update Cost")
 
-def replace_bom(args):
-	frappe.db.auto_commit_on_many_writes = 1
-	args = frappe._dict(args)
 
-	doc = frappe.get_doc("BOM Update Tool")
-	doc.current_bom = args.current_bom
-	doc.new_bom = args.new_bom
-	doc.replace_bom()
+def create_bom_update_log(
+	boms: Optional[Dict[str, str]] = None,
+	update_type: Literal["Replace BOM", "Update Cost"] = "Replace BOM",
+) -> "BOMUpdateLog":
+	"""Creates a BOM Update Log that handles the background job."""
 
-	frappe.db.auto_commit_on_many_writes = 0
-
-def update_cost():
-	frappe.db.auto_commit_on_many_writes = 1
-	bom_list = get_boms_in_bottom_up_order()
-	for bom in bom_list:
-		frappe.get_doc("BOM", bom).update_cost(update_parent=False, from_child_bom=True)
-
-	frappe.db.auto_commit_on_many_writes = 0
+	boms = boms or {}
+	current_bom = boms.get("current_bom")
+	new_bom = boms.get("new_bom")
+	return frappe.get_doc(
+		{
+			"doctype": "BOM Update Log",
+			"current_bom": current_bom,
+			"new_bom": new_bom,
+			"update_type": update_type,
+		}
+	).submit()
diff --git a/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py b/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py
index b4c625d..5dd557f 100644
--- a/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py
+++ b/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py
@@ -1,16 +1,25 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 
 import frappe
 from frappe.tests.utils import FrappeTestCase
 
-from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
+from erpnext.manufacturing.doctype.bom_update_log.test_bom_update_log import (
+	update_cost_in_all_boms_in_test,
+)
+from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import enqueue_replace_bom
 from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
 from erpnext.stock.doctype.item.test_item import create_item
 
-test_records = frappe.get_test_records('BOM')
+test_records = frappe.get_test_records("BOM")
+
 
 class TestBOMUpdateTool(FrappeTestCase):
+	"Test major functions run via BOM Update Tool."
+
+	def tearDown(self):
+		frappe.db.rollback()
+
 	def test_replace_bom(self):
 		current_bom = "BOM-_Test Item Home Desktop Manufactured-001"
 
@@ -18,18 +27,11 @@
 		bom_doc.items[1].item_code = "_Test Item"
 		bom_doc.insert()
 
-		update_tool = frappe.get_doc("BOM Update Tool")
-		update_tool.current_bom = current_bom
-		update_tool.new_bom = bom_doc.name
-		update_tool.replace_bom()
+		boms = frappe._dict(current_bom=current_bom, new_bom=bom_doc.name)
+		enqueue_replace_bom(boms=boms)
 
-		self.assertFalse(frappe.db.sql("select name from `tabBOM Item` where bom_no=%s", current_bom))
-		self.assertTrue(frappe.db.sql("select name from `tabBOM Item` where bom_no=%s", bom_doc.name))
-
-		# reverse, as it affects other testcases
-		update_tool.current_bom = bom_doc.name
-		update_tool.new_bom = current_bom
-		update_tool.replace_bom()
+		self.assertFalse(frappe.db.exists("BOM Item", {"bom_no": current_bom, "docstatus": 1}))
+		self.assertTrue(frappe.db.exists("BOM Item", {"bom_no": bom_doc.name, "docstatus": 1}))
 
 	def test_bom_cost(self):
 		for item in ["BOM Cost Test Item 1", "BOM Cost Test Item 2", "BOM Cost Test Item 3"]:
@@ -37,23 +39,26 @@
 			if item_doc.valuation_rate != 100.00:
 				frappe.db.set_value("Item", item_doc.name, "valuation_rate", 100)
 
-		bom_no = frappe.db.get_value('BOM', {'item': 'BOM Cost Test Item 1'}, "name")
+		bom_no = frappe.db.get_value("BOM", {"item": "BOM Cost Test Item 1"}, "name")
 		if not bom_no:
-			doc = make_bom(item = 'BOM Cost Test Item 1',
-				raw_materials =['BOM Cost Test Item 2', 'BOM Cost Test Item 3'], currency="INR")
+			doc = make_bom(
+				item="BOM Cost Test Item 1",
+				raw_materials=["BOM Cost Test Item 2", "BOM Cost Test Item 3"],
+				currency="INR",
+			)
 		else:
 			doc = frappe.get_doc("BOM", bom_no)
 
 		self.assertEqual(doc.total_cost, 200)
 
 		frappe.db.set_value("Item", "BOM Cost Test Item 2", "valuation_rate", 200)
-		update_cost()
+		update_cost_in_all_boms_in_test()
 
 		doc.load_from_db()
 		self.assertEqual(doc.total_cost, 300)
 
 		frappe.db.set_value("Item", "BOM Cost Test Item 2", "valuation_rate", 100)
-		update_cost()
+		update_cost_in_all_boms_in_test()
 
 		doc.load_from_db()
 		self.assertEqual(doc.total_cost, 200)
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js
index d85b8a6..b6646b1 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.js
+++ b/erpnext/manufacturing/doctype/job_card/job_card.js
@@ -28,12 +28,12 @@
 		frappe.flags.resume_job = 0;
 		let has_items = frm.doc.items && frm.doc.items.length;
 
-		if (frm.doc.__onload.work_order_closed) {
+		if (!frm.is_new() && frm.doc.__onload.work_order_closed) {
 			frm.disable_save();
 			return;
 		}
 
-		if (!frm.doc.__islocal && has_items && frm.doc.docstatus < 2) {
+		if (!frm.is_new() && has_items && frm.doc.docstatus < 2) {
 			let to_request = frm.doc.for_quantity > frm.doc.transferred_qty;
 			let excess_transfer_allowed = frm.doc.__onload.job_card_excess_transfer;
 
@@ -73,10 +73,22 @@
 		if (frm.doc.docstatus == 0 && !frm.is_new() &&
 			(frm.doc.for_quantity > frm.doc.total_completed_qty || !frm.doc.for_quantity)
 			&& (frm.doc.items || !frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) {
-			frm.trigger("prepare_timer_buttons");
+
+			// if Job Card is link to Work Order, the job card must not be able to start if Work Order not "Started"
+			// and if stock mvt for WIP is required
+			if (frm.doc.work_order) {
+				frappe.db.get_value('Work Order', frm.doc.work_order, ['skip_transfer', 'status'], (result) => {
+					if (result.skip_transfer === 1 || result.status == 'In Process') {
+						frm.trigger("prepare_timer_buttons");
+					}
+				});
+			} else {
+				frm.trigger("prepare_timer_buttons");
+			}
 		}
 
 		frm.trigger("setup_quality_inspection");
+
 		if (frm.doc.work_order) {
 			frappe.db.get_value('Work Order', frm.doc.work_order,
 				'transfer_material_against').then((r) => {
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 9f4ace2..ed45106 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -26,15 +26,31 @@
 )
 
 
-class OverlapError(frappe.ValidationError): pass
+class OverlapError(frappe.ValidationError):
+	pass
 
-class OperationMismatchError(frappe.ValidationError): pass
-class OperationSequenceError(frappe.ValidationError): pass
-class JobCardCancelError(frappe.ValidationError): pass
+
+class OperationMismatchError(frappe.ValidationError):
+	pass
+
+
+class OperationSequenceError(frappe.ValidationError):
+	pass
+
+
+class JobCardCancelError(frappe.ValidationError):
+	pass
+
+
+class JobCardOverTransferError(frappe.ValidationError):
+	pass
+
 
 class JobCard(Document):
 	def onload(self):
-		excess_transfer = frappe.db.get_single_value("Manufacturing Settings", "job_card_excess_transfer")
+		excess_transfer = frappe.db.get_single_value(
+			"Manufacturing Settings", "job_card_excess_transfer"
+		)
 		self.set_onload("job_card_excess_transfer", excess_transfer)
 		self.set_onload("work_order_closed", self.is_work_order_closed())
 
@@ -48,27 +64,35 @@
 		self.validate_work_order()
 
 	def set_sub_operations(self):
-		if self.operation:
+		if not self.sub_operations and self.operation:
 			self.sub_operations = []
-			for row in frappe.get_all('Sub Operation',
-				filters = {'parent': self.operation}, fields=['operation', 'idx'], order_by='idx'):
-				row.status = 'Pending'
+			for row in frappe.get_all(
+				"Sub Operation",
+				filters={"parent": self.operation},
+				fields=["operation", "idx"],
+				order_by="idx",
+			):
+				row.status = "Pending"
 				row.sub_operation = row.operation
-				self.append('sub_operations', row)
+				self.append("sub_operations", row)
 
 	def validate_time_logs(self):
 		self.total_time_in_mins = 0.0
 		self.total_completed_qty = 0.0
 
-		if self.get('time_logs'):
-			for d in self.get('time_logs'):
+		if self.get("time_logs"):
+			for d in self.get("time_logs"):
 				if d.to_time and get_datetime(d.from_time) > get_datetime(d.to_time):
 					frappe.throw(_("Row {0}: From time must be less than to time").format(d.idx))
 
 				data = self.get_overlap_for(d)
 				if data:
-					frappe.throw(_("Row {0}: From Time and To Time of {1} is overlapping with {2}")
-						.format(d.idx, self.name, data.name), OverlapError)
+					frappe.throw(
+						_("Row {0}: From Time and To Time of {1} is overlapping with {2}").format(
+							d.idx, self.name, data.name
+						),
+						OverlapError,
+					)
 
 				if d.from_time and d.to_time:
 					d.time_in_mins = time_diff_in_hours(d.to_time, d.from_time) * 60
@@ -86,8 +110,9 @@
 		production_capacity = 1
 
 		if self.workstation:
-			production_capacity = frappe.get_cached_value("Workstation",
-				self.workstation, 'production_capacity') or 1
+			production_capacity = (
+				frappe.get_cached_value("Workstation", self.workstation, "production_capacity") or 1
+			)
 			validate_overlap_for = " and jc.workstation = %(workstation)s "
 
 		if args.get("employee"):
@@ -95,11 +120,12 @@
 			production_capacity = 1
 			validate_overlap_for = " and jctl.employee = %(employee)s "
 
-		extra_cond = ''
+		extra_cond = ""
 		if check_next_available_slot:
 			extra_cond = " or (%(from_time)s <= jctl.from_time and %(to_time)s <= jctl.to_time)"
 
-		existing = frappe.db.sql("""select jc.name as name, jctl.to_time from
+		existing = frappe.db.sql(
+			"""select jc.name as name, jctl.to_time from
 			`tabJob Card Time Log` jctl, `tabJob Card` jc where jctl.parent = jc.name and
 			(
 				(%(from_time)s > jctl.from_time and %(from_time)s < jctl.to_time) or
@@ -107,15 +133,19 @@
 				(%(from_time)s <= jctl.from_time and %(to_time)s >= jctl.to_time) {0}
 			)
 			and jctl.name != %(name)s and jc.name != %(parent)s and jc.docstatus < 2 {1}
-			order by jctl.to_time desc limit 1""".format(extra_cond, validate_overlap_for),
+			order by jctl.to_time desc limit 1""".format(
+				extra_cond, validate_overlap_for
+			),
 			{
 				"from_time": args.from_time,
 				"to_time": args.to_time,
 				"name": args.name or "No Name",
 				"parent": args.parent or "No Name",
 				"employee": args.get("employee"),
-				"workstation": self.workstation
-			}, as_dict=True)
+				"workstation": self.workstation,
+			},
+			as_dict=True,
+		)
 
 		if existing and production_capacity > len(existing):
 			return
@@ -125,10 +155,7 @@
 	def schedule_time_logs(self, row):
 		row.remaining_time_in_mins = row.time_in_mins
 		while row.remaining_time_in_mins > 0:
-			args = frappe._dict({
-				"from_time": row.planned_start_time,
-				"to_time": row.planned_end_time
-			})
+			args = frappe._dict({"from_time": row.planned_start_time, "to_time": row.planned_end_time})
 
 			self.validate_overlap_for_workstation(args, row)
 			self.check_workstation_time(row)
@@ -141,13 +168,16 @@
 
 	def check_workstation_time(self, row):
 		workstation_doc = frappe.get_cached_doc("Workstation", self.workstation)
-		if (not workstation_doc.working_hours or
-			cint(frappe.db.get_single_value("Manufacturing Settings", "allow_overtime"))):
+		if not workstation_doc.working_hours or cint(
+			frappe.db.get_single_value("Manufacturing Settings", "allow_overtime")
+		):
 			if get_datetime(row.planned_end_time) < get_datetime(row.planned_start_time):
 				row.planned_end_time = add_to_date(row.planned_start_time, minutes=row.time_in_mins)
 				row.remaining_time_in_mins = 0.0
 			else:
-				row.remaining_time_in_mins -= time_diff_in_minutes(row.planned_end_time, row.planned_start_time)
+				row.remaining_time_in_mins -= time_diff_in_minutes(
+					row.planned_end_time, row.planned_start_time
+				)
 
 			self.update_time_logs(row)
 			return
@@ -167,14 +197,15 @@
 			workstation_start_time = datetime.datetime.combine(start_date, get_time(time_slot.start_time))
 			workstation_end_time = datetime.datetime.combine(start_date, get_time(time_slot.end_time))
 
-			if (get_datetime(row.planned_start_time) >= workstation_start_time and
-				get_datetime(row.planned_start_time) <= workstation_end_time):
+			if (
+				get_datetime(row.planned_start_time) >= workstation_start_time
+				and get_datetime(row.planned_start_time) <= workstation_end_time
+			):
 				time_in_mins = time_diff_in_minutes(workstation_end_time, row.planned_start_time)
 
 				# If remaining time fit in workstation time logs else split hours as per workstation time
 				if time_in_mins > row.remaining_time_in_mins:
-					row.planned_end_time = add_to_date(row.planned_start_time,
-						minutes=row.remaining_time_in_mins)
+					row.planned_end_time = add_to_date(row.planned_start_time, minutes=row.remaining_time_in_mins)
 					row.remaining_time_in_mins = 0
 				else:
 					row.planned_end_time = add_to_date(row.planned_start_time, minutes=time_in_mins)
@@ -182,14 +213,16 @@
 
 				self.update_time_logs(row)
 
-				if total_idx != (i+1) and row.remaining_time_in_mins > 0:
-					row.planned_start_time = datetime.datetime.combine(start_date,
-						get_time(workstation_doc.working_hours[i+1].start_time))
+				if total_idx != (i + 1) and row.remaining_time_in_mins > 0:
+					row.planned_start_time = datetime.datetime.combine(
+						start_date, get_time(workstation_doc.working_hours[i + 1].start_time)
+					)
 
 		if row.remaining_time_in_mins > 0:
 			start_date = add_days(start_date, 1)
-			row.planned_start_time = datetime.datetime.combine(start_date,
-				get_time(workstation_doc.working_hours[0].start_time))
+			row.planned_start_time = datetime.datetime.combine(
+				start_date, get_time(workstation_doc.working_hours[0].start_time)
+			)
 
 	def add_time_log(self, args):
 		last_row = []
@@ -204,21 +237,25 @@
 		if last_row and args.get("complete_time"):
 			for row in self.time_logs:
 				if not row.to_time:
-					row.update({
-						"to_time": get_datetime(args.get("complete_time")),
-						"operation": args.get("sub_operation"),
-						"completed_qty": args.get("completed_qty") or 0.0
-					})
+					row.update(
+						{
+							"to_time": get_datetime(args.get("complete_time")),
+							"operation": args.get("sub_operation"),
+							"completed_qty": args.get("completed_qty") or 0.0,
+						}
+					)
 		elif args.get("start_time"):
-			new_args = frappe._dict({
-				"from_time": get_datetime(args.get("start_time")),
-				"operation": args.get("sub_operation"),
-				"completed_qty": 0.0
-			})
+			new_args = frappe._dict(
+				{
+					"from_time": get_datetime(args.get("start_time")),
+					"operation": args.get("sub_operation"),
+					"completed_qty": 0.0,
+				}
+			)
 
 			if employees:
 				for name in employees:
-					new_args.employee = name.get('employee')
+					new_args.employee = name.get("employee")
 					self.add_start_time_log(new_args)
 			else:
 				self.add_start_time_log(new_args)
@@ -236,10 +273,7 @@
 
 	def set_employees(self, employees):
 		for name in employees:
-			self.append('employee', {
-				'employee': name.get('employee'),
-				'completed_qty': 0.0
-			})
+			self.append("employee", {"employee": name.get("employee"), "completed_qty": 0.0})
 
 	def reset_timer_value(self, args):
 		self.started_time = None
@@ -263,13 +297,17 @@
 		operation_wise_completed_time = {}
 		for time_log in self.time_logs:
 			if time_log.operation not in operation_wise_completed_time:
-				operation_wise_completed_time.setdefault(time_log.operation,
-					frappe._dict({"status": "Pending", "completed_qty":0.0, "completed_time": 0.0, "employee": []}))
+				operation_wise_completed_time.setdefault(
+					time_log.operation,
+					frappe._dict(
+						{"status": "Pending", "completed_qty": 0.0, "completed_time": 0.0, "employee": []}
+					),
+				)
 
 			op_row = operation_wise_completed_time[time_log.operation]
 			op_row.status = "Work In Progress" if not time_log.time_in_mins else "Complete"
-			if self.status == 'On Hold':
-				op_row.status = 'Pause'
+			if self.status == "On Hold":
+				op_row.status = "Pause"
 
 			op_row.employee.append(time_log.employee)
 			if time_log.time_in_mins:
@@ -279,7 +317,7 @@
 		for row in self.sub_operations:
 			operation_deatils = operation_wise_completed_time.get(row.sub_operation)
 			if operation_deatils:
-				if row.status != 'Complete':
+				if row.status != "Complete":
 					row.status = operation_deatils.status
 
 				row.completed_time = operation_deatils.completed_time
@@ -289,43 +327,52 @@
 					if operation_deatils.completed_qty:
 						row.completed_qty = operation_deatils.completed_qty / len(set(operation_deatils.employee))
 			else:
-				row.status = 'Pending'
+				row.status = "Pending"
 				row.completed_time = 0.0
 				row.completed_qty = 0.0
 
 	def update_time_logs(self, row):
-		self.append("time_logs", {
-			"from_time": row.planned_start_time,
-			"to_time": row.planned_end_time,
-			"completed_qty": 0,
-			"time_in_mins": time_diff_in_minutes(row.planned_end_time, row.planned_start_time),
-		})
+		self.append(
+			"time_logs",
+			{
+				"from_time": row.planned_start_time,
+				"to_time": row.planned_end_time,
+				"completed_qty": 0,
+				"time_in_mins": time_diff_in_minutes(row.planned_end_time, row.planned_start_time),
+			},
+		)
 
 	@frappe.whitelist()
 	def get_required_items(self):
-		if not self.get('work_order'):
+		if not self.get("work_order"):
 			return
 
-		doc = frappe.get_doc('Work Order', self.get('work_order'))
-		if doc.transfer_material_against == 'Work Order' or doc.skip_transfer:
+		doc = frappe.get_doc("Work Order", self.get("work_order"))
+		if doc.transfer_material_against == "Work Order" or doc.skip_transfer:
 			return
 
 		for d in doc.required_items:
 			if not d.operation:
-				frappe.throw(_("Row {0} : Operation is required against the raw material item {1}")
-					.format(d.idx, d.item_code))
+				frappe.throw(
+					_("Row {0} : Operation is required against the raw material item {1}").format(
+						d.idx, d.item_code
+					)
+				)
 
-			if self.get('operation') == d.operation:
-				self.append('items', {
-					"item_code": d.item_code,
-					"source_warehouse": d.source_warehouse,
-					"uom": frappe.db.get_value("Item", d.item_code, 'stock_uom'),
-					"item_name": d.item_name,
-					"description": d.description,
-					"required_qty": (d.required_qty * flt(self.for_quantity)) / doc.qty,
-					"rate": d.rate,
-					"amount": d.amount
-				})
+			if self.get("operation") == d.operation:
+				self.append(
+					"items",
+					{
+						"item_code": d.item_code,
+						"source_warehouse": d.source_warehouse,
+						"uom": frappe.db.get_value("Item", d.item_code, "stock_uom"),
+						"item_name": d.item_name,
+						"description": d.description,
+						"required_qty": (d.required_qty * flt(self.for_quantity)) / doc.qty,
+						"rate": d.rate,
+						"amount": d.amount,
+					},
+				)
 
 	def on_submit(self):
 		self.validate_transfer_qty()
@@ -339,31 +386,52 @@
 
 	def validate_transfer_qty(self):
 		if self.items and self.transferred_qty < self.for_quantity:
-			frappe.throw(_('Materials needs to be transferred to the work in progress warehouse for the job card {0}')
-				.format(self.name))
+			frappe.throw(
+				_(
+					"Materials needs to be transferred to the work in progress warehouse for the job card {0}"
+				).format(self.name)
+			)
 
 	def validate_job_card(self):
-		if self.work_order and frappe.get_cached_value('Work Order', self.work_order, 'status') == 'Stopped':
-			frappe.throw(_("Transaction not allowed against stopped Work Order {0}")
-				.format(get_link_to_form('Work Order', self.work_order)))
+		if (
+			self.work_order
+			and frappe.get_cached_value("Work Order", self.work_order, "status") == "Stopped"
+		):
+			frappe.throw(
+				_("Transaction not allowed against stopped Work Order {0}").format(
+					get_link_to_form("Work Order", self.work_order)
+				)
+			)
 
 		if not self.time_logs:
-			frappe.throw(_("Time logs are required for {0} {1}")
-				.format(bold("Job Card"), get_link_to_form("Job Card", self.name)))
+			frappe.throw(
+				_("Time logs are required for {0} {1}").format(
+					bold("Job Card"), get_link_to_form("Job Card", self.name)
+				)
+			)
 
 		if self.for_quantity and self.total_completed_qty != self.for_quantity:
 			total_completed_qty = bold(_("Total Completed Qty"))
 			qty_to_manufacture = bold(_("Qty to Manufacture"))
 
-			frappe.throw(_("The {0} ({1}) must be equal to {2} ({3})")
-				.format(total_completed_qty, bold(self.total_completed_qty), qty_to_manufacture,bold(self.for_quantity)))
+			frappe.throw(
+				_("The {0} ({1}) must be equal to {2} ({3})").format(
+					total_completed_qty,
+					bold(self.total_completed_qty),
+					qty_to_manufacture,
+					bold(self.for_quantity),
+				)
+			)
 
 	def update_work_order(self):
 		if not self.work_order:
 			return
 
-		if self.is_corrective_job_card and not cint(frappe.db.get_single_value('Manufacturing Settings',
-			'add_corrective_operation_cost_in_finished_good_valuation')):
+		if self.is_corrective_job_card and not cint(
+			frappe.db.get_single_value(
+				"Manufacturing Settings", "add_corrective_operation_cost_in_finished_good_valuation"
+			)
+		):
 			return
 
 		for_quantity, time_in_mins = 0, 0
@@ -375,7 +443,7 @@
 			for_quantity = flt(data[0].completed_qty)
 			time_in_mins = flt(data[0].time_in_mins)
 
-		wo = frappe.get_doc('Work Order', self.work_order)
+		wo = frappe.get_doc("Work Order", self.work_order)
 
 		if self.is_corrective_job_card:
 			self.update_corrective_in_work_order(wo)
@@ -386,8 +454,11 @@
 
 	def update_corrective_in_work_order(self, wo):
 		wo.corrective_operation_cost = 0.0
-		for row in frappe.get_all('Job Card', fields = ['total_time_in_mins', 'hour_rate'],
-			filters = {'is_corrective_job_card': 1, 'docstatus': 1, 'work_order': self.work_order}):
+		for row in frappe.get_all(
+			"Job Card",
+			fields=["total_time_in_mins", "hour_rate"],
+			filters={"is_corrective_job_card": 1, "docstatus": 1, "work_order": self.work_order},
+		):
 			wo.corrective_operation_cost += flt(row.total_time_in_mins) * flt(row.hour_rate)
 
 		wo.calculate_operating_cost()
@@ -395,27 +466,37 @@
 		wo.save()
 
 	def validate_produced_quantity(self, for_quantity, wo):
-		if self.docstatus < 2: return
+		if self.docstatus < 2:
+			return
 
 		if wo.produced_qty > for_quantity:
-			first_part_msg = (_("The {0} {1} is used to calculate the valuation cost for the finished good {2}.")
-				.format(frappe.bold(_("Job Card")), frappe.bold(self.name), frappe.bold(self.production_item)))
+			first_part_msg = _(
+				"The {0} {1} is used to calculate the valuation cost for the finished good {2}."
+			).format(
+				frappe.bold(_("Job Card")), frappe.bold(self.name), frappe.bold(self.production_item)
+			)
 
-			second_part_msg = (_("Kindly cancel the Manufacturing Entries first against the work order {0}.")
-				.format(frappe.bold(get_link_to_form("Work Order", self.work_order))))
+			second_part_msg = _(
+				"Kindly cancel the Manufacturing Entries first against the work order {0}."
+			).format(frappe.bold(get_link_to_form("Work Order", self.work_order)))
 
-			frappe.throw(_("{0} {1}").format(first_part_msg, second_part_msg),
-				JobCardCancelError, title = _("Error"))
+			frappe.throw(
+				_("{0} {1}").format(first_part_msg, second_part_msg), JobCardCancelError, title=_("Error")
+			)
 
 	def update_work_order_data(self, for_quantity, time_in_mins, wo):
-		time_data = frappe.db.sql("""
+		time_data = frappe.db.sql(
+			"""
 				SELECT
 					min(from_time) as start_time, max(to_time) as end_time
 				FROM `tabJob Card` jc, `tabJob Card Time Log` jctl
 				WHERE
 					jctl.parent = jc.name and jc.work_order = %s and jc.operation_id = %s
 					and jc.docstatus = 1 and IFNULL(jc.is_corrective_job_card, 0) = 0
-			""", (self.work_order, self.operation_id), as_dict=1)
+			""",
+			(self.work_order, self.operation_id),
+			as_dict=1,
+		)
 
 		for data in wo.operations:
 			if data.get("name") == self.operation_id:
@@ -434,92 +515,146 @@
 		wo.save()
 
 	def get_current_operation_data(self):
-		return frappe.get_all('Job Card',
-			fields = ["sum(total_time_in_mins) as time_in_mins", "sum(total_completed_qty) as completed_qty"],
-			filters = {"docstatus": 1, "work_order": self.work_order, "operation_id": self.operation_id,
-				"is_corrective_job_card": 0})
+		return frappe.get_all(
+			"Job Card",
+			fields=["sum(total_time_in_mins) as time_in_mins", "sum(total_completed_qty) as completed_qty"],
+			filters={
+				"docstatus": 1,
+				"work_order": self.work_order,
+				"operation_id": self.operation_id,
+				"is_corrective_job_card": 0,
+			},
+		)
 
-	def set_transferred_qty_in_job_card(self, ste_doc):
+	def set_transferred_qty_in_job_card_item(self, ste_doc):
+		from frappe.query_builder.functions import Sum
+
+		def _validate_over_transfer(row, transferred_qty):
+			"Block over transfer of items if not allowed in settings."
+			required_qty = frappe.db.get_value("Job Card Item", row.job_card_item, "required_qty")
+			is_excess = flt(transferred_qty) > flt(required_qty)
+			if is_excess:
+				frappe.throw(
+					_(
+						"Row #{0}: Cannot transfer more than Required Qty {1} for Item {2} against Job Card {3}"
+					).format(
+						row.idx, frappe.bold(required_qty), frappe.bold(row.item_code), ste_doc.job_card
+					),
+					title=_("Excess Transfer"),
+					exc=JobCardOverTransferError,
+				)
+
 		for row in ste_doc.items:
-			if not row.job_card_item: continue
+			if not row.job_card_item:
+				continue
 
-			qty = frappe.db.sql(""" SELECT SUM(qty) from `tabStock Entry Detail` sed, `tabStock Entry` se
-				WHERE  sed.job_card_item = %s and se.docstatus = 1 and sed.parent = se.name and
-				se.purpose = 'Material Transfer for Manufacture'
-			""", (row.job_card_item))[0][0]
+			sed = frappe.qb.DocType("Stock Entry Detail")
+			se = frappe.qb.DocType("Stock Entry")
+			transferred_qty = (
+				frappe.qb.from_(sed)
+				.join(se)
+				.on(sed.parent == se.name)
+				.select(Sum(sed.qty))
+				.where(
+					(sed.job_card_item == row.job_card_item)
+					& (se.docstatus == 1)
+					& (se.purpose == "Material Transfer for Manufacture")
+				)
+			).run()[0][0]
 
-			frappe.db.set_value('Job Card Item', row.job_card_item, 'transferred_qty', flt(qty))
+			allow_excess = frappe.db.get_single_value("Manufacturing Settings", "job_card_excess_transfer")
+			if not allow_excess:
+				_validate_over_transfer(row, transferred_qty)
+
+			frappe.db.set_value("Job Card Item", row.job_card_item, "transferred_qty", flt(transferred_qty))
 
 	def set_transferred_qty(self, update_status=False):
-		"Set total FG Qty for which RM was transferred."
+		"Set total FG Qty in Job Card for which RM was transferred."
 		if not self.items:
 			self.transferred_qty = self.for_quantity if self.docstatus == 1 else 0
 
-		doc = frappe.get_doc('Work Order', self.get('work_order'))
-		if doc.transfer_material_against == 'Work Order' or doc.skip_transfer:
+		doc = frappe.get_doc("Work Order", self.get("work_order"))
+		if doc.transfer_material_against == "Work Order" or doc.skip_transfer:
 			return
 
 		if self.items:
 			# sum of 'For Quantity' of Stock Entries against JC
-			self.transferred_qty = frappe.db.get_value('Stock Entry', {
-				'job_card': self.name,
-				'work_order': self.work_order,
-				'docstatus': 1,
-				'purpose': 'Material Transfer for Manufacture'
-			}, 'sum(fg_completed_qty)') or 0
+			self.transferred_qty = (
+				frappe.db.get_value(
+					"Stock Entry",
+					{
+						"job_card": self.name,
+						"work_order": self.work_order,
+						"docstatus": 1,
+						"purpose": "Material Transfer for Manufacture",
+					},
+					"sum(fg_completed_qty)",
+				)
+				or 0
+			)
 
 		self.db_set("transferred_qty", self.transferred_qty)
 
 		qty = 0
 		if self.work_order:
-			doc = frappe.get_doc('Work Order', self.work_order)
-			if doc.transfer_material_against == 'Job Card' and not doc.skip_transfer:
+			doc = frappe.get_doc("Work Order", self.work_order)
+			if doc.transfer_material_against == "Job Card" and not doc.skip_transfer:
 				completed = True
 				for d in doc.operations:
-					if d.status != 'Completed':
+					if d.status != "Completed":
 						completed = False
 						break
 
 				if completed:
-					job_cards = frappe.get_all('Job Card', filters = {'work_order': self.work_order,
-						'docstatus': ('!=', 2)}, fields = 'sum(transferred_qty) as qty', group_by='operation_id')
+					job_cards = frappe.get_all(
+						"Job Card",
+						filters={"work_order": self.work_order, "docstatus": ("!=", 2)},
+						fields="sum(transferred_qty) as qty",
+						group_by="operation_id",
+					)
 
 					if job_cards:
 						qty = min(d.qty for d in job_cards)
 
-			doc.db_set('material_transferred_for_manufacturing', qty)
+			doc.db_set("material_transferred_for_manufacturing", qty)
 
 		self.set_status(update_status)
 
 	def set_status(self, update_status=False):
-		if self.status == "On Hold": return
+		if self.status == "On Hold" and self.docstatus == 0:
+			return
 
-		self.status = {
-			0: "Open",
-			1: "Submitted",
-			2: "Cancelled"
-		}[self.docstatus or 0]
+		self.status = {0: "Open", 1: "Submitted", 2: "Cancelled"}[self.docstatus or 0]
 
-		if self.time_logs:
-			self.status = 'Work In Progress'
-
-		if (self.docstatus == 1 and
-			(self.for_quantity <= self.total_completed_qty or not self.items)):
-			self.status = 'Completed'
-
-		if self.status != 'Completed':
+		if self.docstatus < 2:
 			if self.for_quantity <= self.transferred_qty:
-				self.status = 'Material Transferred'
+				self.status = "Material Transferred"
+
+			if self.time_logs:
+				self.status = "Work In Progress"
+
+			if self.docstatus == 1 and (self.for_quantity <= self.total_completed_qty or not self.items):
+				self.status = "Completed"
 
 		if update_status:
-			self.db_set('status', self.status)
+			self.db_set("status", self.status)
 
 	def validate_operation_id(self):
-		if (self.get("operation_id") and self.get("operation_row_number") and self.operation and self.work_order and
-			frappe.get_cached_value("Work Order Operation", self.operation_row_number, "name") != self.operation_id):
+		if (
+			self.get("operation_id")
+			and self.get("operation_row_number")
+			and self.operation
+			and self.work_order
+			and frappe.get_cached_value("Work Order Operation", self.operation_row_number, "name")
+			!= self.operation_id
+		):
 			work_order = bold(get_link_to_form("Work Order", self.work_order))
-			frappe.throw(_("Operation {0} does not belong to the work order {1}")
-				.format(bold(self.operation), work_order), OperationMismatchError)
+			frappe.throw(
+				_("Operation {0} does not belong to the work order {1}").format(
+					bold(self.operation), work_order
+				),
+				OperationMismatchError,
+			)
 
 	def validate_sequence_id(self):
 		if self.is_corrective_job_card:
@@ -535,18 +670,25 @@
 
 		current_operation_qty += flt(self.total_completed_qty)
 
-		data = frappe.get_all("Work Order Operation",
-			fields = ["operation", "status", "completed_qty"],
-			filters={"docstatus": 1, "parent": self.work_order, "sequence_id": ('<', self.sequence_id)},
-			order_by = "sequence_id, idx")
+		data = frappe.get_all(
+			"Work Order Operation",
+			fields=["operation", "status", "completed_qty"],
+			filters={"docstatus": 1, "parent": self.work_order, "sequence_id": ("<", self.sequence_id)},
+			order_by="sequence_id, idx",
+		)
 
-		message = "Job Card {0}: As per the sequence of the operations in the work order {1}".format(bold(self.name),
-			bold(get_link_to_form("Work Order", self.work_order)))
+		message = "Job Card {0}: As per the sequence of the operations in the work order {1}".format(
+			bold(self.name), bold(get_link_to_form("Work Order", self.work_order))
+		)
 
 		for row in data:
 			if row.status != "Completed" and row.completed_qty < current_operation_qty:
-				frappe.throw(_("{0}, complete the operation {1} before the operation {2}.")
-					.format(message, bold(row.operation), bold(self.operation)), OperationSequenceError)
+				frappe.throw(
+					_("{0}, complete the operation {1} before the operation {2}.").format(
+						message, bold(row.operation), bold(self.operation)
+					),
+					OperationSequenceError,
+				)
 
 	def validate_work_order(self):
 		if self.is_work_order_closed():
@@ -554,13 +696,14 @@
 
 	def is_work_order_closed(self):
 		if self.work_order:
-			status = frappe.get_value('Work Order', self.work_order)
+			status = frappe.get_value("Work Order", self.work_order)
 
 			if status == "Closed":
 				return True
 
 		return False
 
+
 @frappe.whitelist()
 def make_time_log(args):
 	if isinstance(args, str):
@@ -571,16 +714,17 @@
 	doc.validate_sequence_id()
 	doc.add_time_log(args)
 
+
 @frappe.whitelist()
 def get_operation_details(work_order, operation):
 	if work_order and operation:
-		return frappe.get_all("Work Order Operation", fields = ["name", "idx"],
-			filters = {
-				"parent": work_order,
-				"operation": operation
-			}
+		return frappe.get_all(
+			"Work Order Operation",
+			fields=["name", "idx"],
+			filters={"parent": work_order, "operation": operation},
 		)
 
+
 @frappe.whitelist()
 def get_operations(doctype, txt, searchfield, start, page_len, filters):
 	if not filters.get("work_order"):
@@ -590,12 +734,16 @@
 	if txt:
 		args["operation"] = ("like", "%{0}%".format(txt))
 
-	return frappe.get_all("Work Order Operation",
-		filters = args,
-		fields = ["distinct operation as operation"],
-		limit_start = start,
-		limit_page_length = page_len,
-		order_by="idx asc", as_list=1)
+	return frappe.get_all(
+		"Work Order Operation",
+		filters=args,
+		fields=["distinct operation as operation"],
+		limit_start=start,
+		limit_page_length=page_len,
+		order_by="idx asc",
+		as_list=1,
+	)
+
 
 @frappe.whitelist()
 def make_material_request(source_name, target_doc=None):
@@ -605,26 +753,29 @@
 	def set_missing_values(source, target):
 		target.material_request_type = "Material Transfer"
 
-	doclist = get_mapped_doc("Job Card", source_name, {
-		"Job Card": {
-			"doctype": "Material Request",
-			"field_map": {
-				"name": "job_card",
+	doclist = get_mapped_doc(
+		"Job Card",
+		source_name,
+		{
+			"Job Card": {
+				"doctype": "Material Request",
+				"field_map": {
+					"name": "job_card",
+				},
+			},
+			"Job Card Item": {
+				"doctype": "Material Request Item",
+				"field_map": {"required_qty": "qty", "uom": "stock_uom", "name": "job_card_item"},
+				"postprocess": update_item,
 			},
 		},
-		"Job Card Item": {
-			"doctype": "Material Request Item",
-			"field_map": {
-				"required_qty": "qty",
-				"uom": "stock_uom",
-				"name": "job_card_item"
-			},
-			"postprocess": update_item,
-		}
-	}, target_doc, set_missing_values)
+		target_doc,
+		set_missing_values,
+	)
 
 	return doclist
 
+
 @frappe.whitelist()
 def make_stock_entry(source_name, target_doc=None):
 	def update_item(source, target, source_parent):
@@ -642,44 +793,51 @@
 		target.from_bom = 1
 
 		# avoid negative 'For Quantity'
-		pending_fg_qty = flt(source.get('for_quantity', 0)) - flt(source.get('transferred_qty', 0))
+		pending_fg_qty = flt(source.get("for_quantity", 0)) - flt(source.get("transferred_qty", 0))
 		target.fg_completed_qty = pending_fg_qty if pending_fg_qty > 0 else 0
 
-		target.set_transfer_qty()
-		target.calculate_rate_and_amount()
 		target.set_missing_values()
 		target.set_stock_entry_type()
 
-		wo_allows_alternate_item = frappe.db.get_value("Work Order", target.work_order, "allow_alternative_item")
+		wo_allows_alternate_item = frappe.db.get_value(
+			"Work Order", target.work_order, "allow_alternative_item"
+		)
 		for item in target.items:
-			item.allow_alternative_item = int(wo_allows_alternate_item and
-					frappe.get_cached_value("Item", item.item_code, "allow_alternative_item"))
+			item.allow_alternative_item = int(
+				wo_allows_alternate_item
+				and frappe.get_cached_value("Item", item.item_code, "allow_alternative_item")
+			)
 
-	doclist = get_mapped_doc("Job Card", source_name, {
-		"Job Card": {
-			"doctype": "Stock Entry",
-			"field_map": {
-				"name": "job_card",
-				"for_quantity": "fg_completed_qty"
+	doclist = get_mapped_doc(
+		"Job Card",
+		source_name,
+		{
+			"Job Card": {
+				"doctype": "Stock Entry",
+				"field_map": {"name": "job_card", "for_quantity": "fg_completed_qty"},
+			},
+			"Job Card Item": {
+				"doctype": "Stock Entry Detail",
+				"field_map": {
+					"source_warehouse": "s_warehouse",
+					"required_qty": "qty",
+					"name": "job_card_item",
+				},
+				"postprocess": update_item,
+				"condition": lambda doc: doc.required_qty > 0,
 			},
 		},
-		"Job Card Item": {
-			"doctype": "Stock Entry Detail",
-			"field_map": {
-				"source_warehouse": "s_warehouse",
-				"required_qty": "qty",
-				"name": "job_card_item"
-			},
-			"postprocess": update_item,
-			"condition": lambda doc: doc.required_qty > 0
-		}
-	}, target_doc, set_missing_values)
+		target_doc,
+		set_missing_values,
+	)
 
 	return doclist
 
+
 def time_diff_in_minutes(string_ed_date, string_st_date):
 	return time_diff(string_ed_date, string_st_date).total_seconds() / 60
 
+
 @frappe.whitelist()
 def get_job_details(start, end, filters=None):
 	events = []
@@ -687,41 +845,49 @@
 	event_color = {
 		"Completed": "#cdf5a6",
 		"Material Transferred": "#ffdd9e",
-		"Work In Progress": "#D3D3D3"
+		"Work In Progress": "#D3D3D3",
 	}
 
 	from frappe.desk.reportview import get_filters_cond
+
 	conditions = get_filters_cond("Job Card", filters, [])
 
-	job_cards = frappe.db.sql(""" SELECT `tabJob Card`.name, `tabJob Card`.work_order,
+	job_cards = frappe.db.sql(
+		""" SELECT `tabJob Card`.name, `tabJob Card`.work_order,
 			`tabJob Card`.status, ifnull(`tabJob Card`.remarks, ''),
 			min(`tabJob Card Time Log`.from_time) as from_time,
 			max(`tabJob Card Time Log`.to_time) as to_time
 		FROM `tabJob Card` , `tabJob Card Time Log`
 		WHERE
 			`tabJob Card`.name = `tabJob Card Time Log`.parent {0}
-			group by `tabJob Card`.name""".format(conditions), as_dict=1)
+			group by `tabJob Card`.name""".format(
+			conditions
+		),
+		as_dict=1,
+	)
 
 	for d in job_cards:
-			subject_data = []
-			for field in ["name", "work_order", "remarks"]:
-				if not d.get(field): continue
+		subject_data = []
+		for field in ["name", "work_order", "remarks"]:
+			if not d.get(field):
+				continue
 
-				subject_data.append(d.get(field))
+			subject_data.append(d.get(field))
 
-			color = event_color.get(d.status)
-			job_card_data = {
-				'from_time': d.from_time,
-				'to_time': d.to_time,
-				'name': d.name,
-				'subject': '\n'.join(subject_data),
-				'color': color if color else "#89bcde"
-			}
+		color = event_color.get(d.status)
+		job_card_data = {
+			"from_time": d.from_time,
+			"to_time": d.to_time,
+			"name": d.name,
+			"subject": "\n".join(subject_data),
+			"color": color if color else "#89bcde",
+		}
 
-			events.append(job_card_data)
+		events.append(job_card_data)
 
 	return events
 
+
 @frappe.whitelist()
 def make_corrective_job_card(source_name, operation=None, for_operation=None, target_doc=None):
 	def set_missing_values(source, target):
@@ -729,20 +895,27 @@
 		target.operation = operation
 		target.for_operation = for_operation
 
-		target.set('time_logs', [])
-		target.set('employee', [])
-		target.set('items', [])
+		target.set("time_logs", [])
+		target.set("employee", [])
+		target.set("items", [])
+		target.set("sub_operations", [])
 		target.set_sub_operations()
 		target.get_required_items()
 		target.validate_time_logs()
 
-	doclist = get_mapped_doc("Job Card", source_name, {
-		"Job Card": {
-			"doctype": "Job Card",
-			"field_map": {
-				"name": "for_job_card",
-			},
-		}
-	}, target_doc, set_missing_values)
+	doclist = get_mapped_doc(
+		"Job Card",
+		source_name,
+		{
+			"Job Card": {
+				"doctype": "Job Card",
+				"field_map": {
+					"name": "for_job_card",
+				},
+			}
+		},
+		target_doc,
+		set_missing_values,
+	)
 
 	return doclist
diff --git a/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py b/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py
index 2c48872..14c1f36 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py
@@ -3,18 +3,10 @@
 
 def get_data():
 	return {
-		'fieldname': 'job_card',
-		'non_standard_fieldnames': {
-			'Quality Inspection': 'reference_name'
-		},
-		'transactions': [
-			{
-				'label': _('Transactions'),
-				'items': ['Material Request', 'Stock Entry']
-			},
-			{
-				'label': _('Reference'),
-				'items': ['Quality Inspection']
-			}
-		]
+		"fieldname": "job_card",
+		"non_standard_fieldnames": {"Quality Inspection": "reference_name"},
+		"transactions": [
+			{"label": _("Transactions"), "items": ["Material Request", "Stock Entry"]},
+			{"label": _("Reference"), "items": ["Quality Inspection"]},
+		],
 	}
diff --git a/erpnext/manufacturing/doctype/job_card/job_card_list.js b/erpnext/manufacturing/doctype/job_card/job_card_list.js
index 8017209..5d883bf 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card_list.js
+++ b/erpnext/manufacturing/doctype/job_card/job_card_list.js
@@ -1,15 +1,17 @@
 frappe.listview_settings['Job Card'] = {
+	has_indicator_for_draft: true,
+
 	get_indicator: function(doc) {
-		if (doc.status === "Work In Progress") {
-			return [__("Work In Progress"), "orange", "status,=,Work In Progress"];
-		} else if (doc.status === "Completed") {
-			return [__("Completed"), "green", "status,=,Completed"];
-		} else if (doc.docstatus == 2) {
-			return [__("Cancelled"), "red", "status,=,Cancelled"];
-		} else if (doc.status === "Material Transferred") {
-			return [__('Material Transferred'), "blue", "status,=,Material Transferred"];
-		} else {
-			return [__("Open"), "red", "status,=,Open"];
-		}
+		const status_colors = {
+			"Work In Progress": "orange",
+			"Completed": "green",
+			"Cancelled": "red",
+			"Material Transferred": "blue",
+			"Open": "red",
+		};
+		const status = doc.status || "Open";
+		const color = status_colors[status] || "blue";
+
+		return [__(status), color, `status,=,${status}`];
 	}
 };
diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.py b/erpnext/manufacturing/doctype/job_card/test_job_card.py
index 33425d2..ac71141 100644
--- a/erpnext/manufacturing/doctype/job_card/test_job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/test_job_card.py
@@ -1,15 +1,25 @@
 # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 
-import frappe
-from frappe.tests.utils import FrappeTestCase
-from frappe.utils import random_string
 
-from erpnext.manufacturing.doctype.job_card.job_card import OperationMismatchError, OverlapError
+from typing import Literal
+
+import frappe
+from frappe.tests.utils import FrappeTestCase, change_settings
+from frappe.utils import random_string
+from frappe.utils.data import add_to_date, now
+
+from erpnext.manufacturing.doctype.job_card.job_card import (
+	JobCardOverTransferError,
+	OperationMismatchError,
+	OverlapError,
+	make_corrective_job_card,
+)
 from erpnext.manufacturing.doctype.job_card.job_card import (
 	make_stock_entry as make_stock_entry_from_jc,
 )
 from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
+from erpnext.manufacturing.doctype.work_order.work_order import WorkOrder
 from erpnext.manufacturing.doctype.workstation.test_workstation import make_workstation
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 
@@ -17,39 +27,40 @@
 class TestJobCard(FrappeTestCase):
 	def setUp(self):
 		make_bom_for_jc_tests()
+		self.transfer_material_against: Literal["Work Order", "Job Card"] = "Work Order"
+		self.source_warehouse = None
+		self._work_order = None
 
-		transfer_material_against, source_warehouse = None, None
+	@property
+	def work_order(self) -> WorkOrder:
+		"""Work Order lazily created for tests."""
+		if not self._work_order:
+			self._work_order = make_wo_order_test_record(
+				item="_Test FG Item 2",
+				qty=2,
+				transfer_material_against=self.transfer_material_against,
+				source_warehouse=self.source_warehouse,
+			)
+		return self._work_order
 
-		tests_that_skip_setup = (
-			"test_job_card_material_transfer_correctness",
-		)
-		tests_that_transfer_against_jc = (
-			"test_job_card_multiple_materials_transfer",
-			"test_job_card_excess_material_transfer",
-			"test_job_card_partial_material_transfer"
-		)
-
-		if self._testMethodName in tests_that_skip_setup:
-			return
-
-		if self._testMethodName in tests_that_transfer_against_jc:
-			transfer_material_against = "Job Card"
-			source_warehouse = "Stores - _TC"
-
-		self.work_order = make_wo_order_test_record(
-			item="_Test FG Item 2",
-			qty=2,
-			transfer_material_against=transfer_material_against,
-			source_warehouse=source_warehouse
-		)
+	def generate_required_stock(self, work_order: WorkOrder) -> None:
+		"""Create twice the stock for all required items in work order."""
+		for item in work_order.required_items:
+			make_stock_entry(
+				item_code=item.item_code,
+				target=item.source_warehouse or self.source_warehouse,
+				qty=item.required_qty * 2,
+				basic_rate=100,
+			)
 
 	def tearDown(self):
 		frappe.db.rollback()
 
-	def test_job_card(self):
+	def test_job_card_operations(self):
 
-		job_cards = frappe.get_all('Job Card',
-			filters = {'work_order': self.work_order.name}, fields = ["operation_id", "name"])
+		job_cards = frappe.get_all(
+			"Job Card", filters={"work_order": self.work_order.name}, fields=["operation_id", "name"]
+		)
 
 		if job_cards:
 			job_card = job_cards[0]
@@ -59,89 +70,84 @@
 			doc.operation_id = "Test Data"
 			self.assertRaises(OperationMismatchError, doc.save)
 
-		for d in job_cards:
-			frappe.delete_doc("Job Card", d.name)
-
 	def test_job_card_with_different_work_station(self):
-		job_cards = frappe.get_all('Job Card',
-			filters = {'work_order': self.work_order.name},
-			fields = ["operation_id", "workstation", "name", "for_quantity"])
+		job_cards = frappe.get_all(
+			"Job Card",
+			filters={"work_order": self.work_order.name},
+			fields=["operation_id", "workstation", "name", "for_quantity"],
+		)
 
 		job_card = job_cards[0]
 
 		if job_card:
-			workstation = frappe.db.get_value("Workstation",
-				{"name": ("not in", [job_card.workstation])}, "name")
+			workstation = frappe.db.get_value(
+				"Workstation", {"name": ("not in", [job_card.workstation])}, "name"
+			)
 
 			if not workstation or job_card.workstation == workstation:
 				workstation = make_workstation(workstation_name=random_string(5)).name
 
 			doc = frappe.get_doc("Job Card", job_card.name)
 			doc.workstation = workstation
-			doc.append("time_logs", {
-				"from_time": "2009-01-01 12:06:25",
-				"to_time": "2009-01-01 12:37:25",
-				"time_in_mins": "31.00002",
-				"completed_qty": job_card.for_quantity
-			})
+			doc.append(
+				"time_logs",
+				{
+					"from_time": "2009-01-01 12:06:25",
+					"to_time": "2009-01-01 12:37:25",
+					"time_in_mins": "31.00002",
+					"completed_qty": job_card.for_quantity,
+				},
+			)
 			doc.submit()
 
-			completed_qty = frappe.db.get_value("Work Order Operation", job_card.operation_id, "completed_qty")
+			completed_qty = frappe.db.get_value(
+				"Work Order Operation", job_card.operation_id, "completed_qty"
+			)
 			self.assertEqual(completed_qty, job_card.for_quantity)
 
-			doc.cancel()
-
-			for d in job_cards:
-				frappe.delete_doc("Job Card", d.name)
-
 	def test_job_card_overlap(self):
 		wo2 = make_wo_order_test_record(item="_Test FG Item 2", qty=2)
 
-		jc1_name = frappe.db.get_value("Job Card", {'work_order': self.work_order.name})
-		jc2_name = frappe.db.get_value("Job Card", {'work_order': wo2.name})
+		jc1 = frappe.get_last_doc("Job Card", {"work_order": self.work_order.name})
+		jc2 = frappe.get_last_doc("Job Card", {"work_order": wo2.name})
 
-		jc1 = frappe.get_doc("Job Card", jc1_name)
-		jc2 = frappe.get_doc("Job Card", jc2_name)
+		employee = "_T-Employee-00001"  # from test records
 
-		employee = "_T-Employee-00001" # from test records
-
-		jc1.append("time_logs", {
-			"from_time": "2021-01-01 00:00:00",
-			"to_time": "2021-01-01 08:00:00",
-			"completed_qty": 1,
-			"employee": employee,
-		})
+		jc1.append(
+			"time_logs",
+			{
+				"from_time": "2021-01-01 00:00:00",
+				"to_time": "2021-01-01 08:00:00",
+				"completed_qty": 1,
+				"employee": employee,
+			},
+		)
 		jc1.save()
 
 		# add a new entry in same time slice
-		jc2.append("time_logs", {
-			"from_time": "2021-01-01 00:01:00",
-			"to_time": "2021-01-01 06:00:00",
-			"completed_qty": 1,
-			"employee": employee,
-		})
+		jc2.append(
+			"time_logs",
+			{
+				"from_time": "2021-01-01 00:01:00",
+				"to_time": "2021-01-01 06:00:00",
+				"completed_qty": 1,
+				"employee": employee,
+			},
+		)
 		self.assertRaises(OverlapError, jc2.save)
 
 	def test_job_card_multiple_materials_transfer(self):
 		"Test transferring RMs separately against Job Card with multiple RMs."
-		make_stock_entry(
-			item_code="_Test Item",
-			target="Stores - _TC",
-			qty=10,
-			basic_rate=100
-		)
-		make_stock_entry(
-			item_code="_Test Item Home Desktop Manufactured",
-			target="Stores - _TC",
-			qty=6,
-			basic_rate=100
-		)
+		self.transfer_material_against = "Job Card"
+		self.source_warehouse = "Stores - _TC"
 
-		job_card_name = frappe.db.get_value("Job Card", {'work_order': self.work_order.name})
+		self.generate_required_stock(self.work_order)
+
+		job_card_name = frappe.db.get_value("Job Card", {"work_order": self.work_order.name})
 		job_card = frappe.get_doc("Job Card", job_card_name)
 
 		transfer_entry_1 = make_stock_entry_from_jc(job_card_name)
-		del transfer_entry_1.items[1] # transfer only 1 of 2 RMs
+		del transfer_entry_1.items[1]  # transfer only 1 of 2 RMs
 		transfer_entry_1.insert()
 		transfer_entry_1.submit()
 
@@ -160,15 +166,58 @@
 		# transfer was made for 2 fg qty in first transfer Stock Entry
 		self.assertEqual(transfer_entry_2.fg_completed_qty, 0)
 
+	@change_settings("Manufacturing Settings", {"job_card_excess_transfer": 1})
 	def test_job_card_excess_material_transfer(self):
 		"Test transferring more than required RM against Job Card."
-		make_stock_entry(item_code="_Test Item", target="Stores - _TC",
-			qty=25, basic_rate=100)
-		make_stock_entry(item_code="_Test Item Home Desktop Manufactured",
-			target="Stores - _TC", qty=15, basic_rate=100)
+		self.transfer_material_against = "Job Card"
+		self.source_warehouse = "Stores - _TC"
 
-		job_card_name = frappe.db.get_value("Job Card", {'work_order': self.work_order.name})
-		job_card = frappe.get_doc("Job Card", job_card_name)
+		self.generate_required_stock(self.work_order)
+
+		job_card = frappe.get_last_doc("Job Card", {"work_order": self.work_order.name})
+		self.assertEqual(job_card.status, "Open")
+
+		# fully transfer both RMs
+		transfer_entry_1 = make_stock_entry_from_jc(job_card.name)
+		transfer_entry_1.insert()
+		transfer_entry_1.submit()
+
+		# transfer extra qty of both RM due to previously damaged RM
+		transfer_entry_2 = make_stock_entry_from_jc(job_card.name)
+		# deliberately change 'For Quantity'
+		transfer_entry_2.fg_completed_qty = 1
+		transfer_entry_2.items[0].qty = 5
+		transfer_entry_2.items[1].qty = 3
+		transfer_entry_2.insert()
+		transfer_entry_2.submit()
+
+		job_card.reload()
+		self.assertGreater(job_card.transferred_qty, job_card.for_quantity)
+
+		# Check if 'For Quantity' is negative
+		# as 'transferred_qty' > Qty to Manufacture
+		transfer_entry_3 = make_stock_entry_from_jc(job_card.name)
+		self.assertEqual(transfer_entry_3.fg_completed_qty, 0)
+
+		job_card.append(
+			"time_logs",
+			{"from_time": "2021-01-01 00:01:00", "to_time": "2021-01-01 06:00:00", "completed_qty": 2},
+		)
+		job_card.save()
+		job_card.submit()
+
+		# JC is Completed with excess transfer
+		self.assertEqual(job_card.status, "Completed")
+
+	@change_settings("Manufacturing Settings", {"job_card_excess_transfer": 0})
+	def test_job_card_excess_material_transfer_block(self):
+
+		self.transfer_material_against = "Job Card"
+		self.source_warehouse = "Stores - _TC"
+
+		self.generate_required_stock(self.work_order)
+
+		job_card_name = frappe.db.get_value("Job Card", {"work_order": self.work_order.name})
 
 		# fully transfer both RMs
 		transfer_entry_1 = make_stock_entry_from_jc(job_card_name)
@@ -182,40 +231,19 @@
 		transfer_entry_2.items[0].qty = 5
 		transfer_entry_2.items[1].qty = 3
 		transfer_entry_2.insert()
-		transfer_entry_2.submit()
-
-		job_card.reload()
-		self.assertGreater(job_card.transferred_qty, job_card.for_quantity)
-
-		# Check if 'For Quantity' is negative
-		# as 'transferred_qty' > Qty to Manufacture
-		transfer_entry_3 = make_stock_entry_from_jc(job_card_name)
-		self.assertEqual(transfer_entry_3.fg_completed_qty, 0)
-
-		job_card.append("time_logs", {
-			"from_time": "2021-01-01 00:01:00",
-			"to_time": "2021-01-01 06:00:00",
-			"completed_qty": 2
-		})
-		job_card.save()
-		job_card.submit()
-
-		# JC is Completed with excess transfer
-		self.assertEqual(job_card.status, "Completed")
+		self.assertRaises(JobCardOverTransferError, transfer_entry_2.submit)
 
 	def test_job_card_partial_material_transfer(self):
 		"Test partial material transfer against Job Card"
+		self.transfer_material_against = "Job Card"
+		self.source_warehouse = "Stores - _TC"
 
-		make_stock_entry(item_code="_Test Item", target="Stores - _TC",
-			qty=25, basic_rate=100)
-		make_stock_entry(item_code="_Test Item Home Desktop Manufactured",
-			target="Stores - _TC", qty=15, basic_rate=100)
+		self.generate_required_stock(self.work_order)
 
-		job_card_name = frappe.db.get_value("Job Card", {'work_order': self.work_order.name})
-		job_card = frappe.get_doc("Job Card", job_card_name)
+		job_card = frappe.get_last_doc("Job Card", {"work_order": self.work_order.name})
 
 		# partially transfer
-		transfer_entry = make_stock_entry_from_jc(job_card_name)
+		transfer_entry = make_stock_entry_from_jc(job_card.name)
 		transfer_entry.fg_completed_qty = 1
 		transfer_entry.get_items()
 		transfer_entry.insert()
@@ -227,7 +255,7 @@
 		self.assertEqual(transfer_entry.items[1].qty, 3)
 
 		# transfer remaining
-		transfer_entry_2 = make_stock_entry_from_jc(job_card_name)
+		transfer_entry_2 = make_stock_entry_from_jc(job_card.name)
 
 		self.assertEqual(transfer_entry_2.fg_completed_qty, 1)
 		self.assertEqual(transfer_entry_2.items[0].qty, 5)
@@ -241,15 +269,14 @@
 
 	def test_job_card_material_transfer_correctness(self):
 		"""
-			1. Test if only current Job Card Items are pulled in a Stock Entry against a Job Card
-			2. Test impact of changing 'For Qty' in such a Stock Entry
+		1. Test if only current Job Card Items are pulled in a Stock Entry against a Job Card
+		2. Test impact of changing 'For Qty' in such a Stock Entry
 		"""
 		create_bom_with_multiple_operations()
 		work_order = make_wo_with_transfer_against_jc()
 
 		job_card_name = frappe.db.get_value(
-			"Job Card",
-			{"work_order": work_order.name,"operation": "Test Operation A"}
+			"Job Card", {"work_order": work_order.name, "operation": "Test Operation A"}
 		)
 		job_card = frappe.get_doc("Job Card", job_card_name)
 
@@ -273,7 +300,74 @@
 		self.assertEqual(transfer_entry.items[0].item_code, "_Test Item")
 		self.assertEqual(transfer_entry.items[0].qty, 2)
 
-		# rollback via tearDown method
+	@change_settings(
+		"Manufacturing Settings", {"add_corrective_operation_cost_in_finished_good_valuation": 1}
+	)
+	def test_corrective_costing(self):
+		job_card = frappe.get_last_doc("Job Card", {"work_order": self.work_order.name})
+
+		job_card.append(
+			"time_logs",
+			{"from_time": now(), "to_time": add_to_date(now(), hours=1), "completed_qty": 2},
+		)
+		job_card.submit()
+
+		self.work_order.reload()
+		original_cost = self.work_order.total_operating_cost
+
+		# Create a corrective operation against it
+		corrective_action = frappe.get_doc(
+			doctype="Operation", is_corrective_operation=1, name=frappe.generate_hash()
+		).insert()
+
+		corrective_job_card = make_corrective_job_card(
+			job_card.name, operation=corrective_action.name, for_operation=job_card.operation
+		)
+		corrective_job_card.hour_rate = 100
+		corrective_job_card.insert()
+		corrective_job_card.append(
+			"time_logs",
+			{
+				"from_time": add_to_date(now(), hours=2),
+				"to_time": add_to_date(now(), hours=2, minutes=30),
+				"completed_qty": 2,
+			},
+		)
+		corrective_job_card.submit()
+
+		self.work_order.reload()
+		cost_after_correction = self.work_order.total_operating_cost
+		self.assertGreater(cost_after_correction, original_cost)
+
+		corrective_job_card.cancel()
+		self.work_order.reload()
+		cost_after_cancel = self.work_order.total_operating_cost
+		self.assertEqual(cost_after_cancel, original_cost)
+
+	def test_job_card_statuses(self):
+		def assertStatus(status):
+			jc.set_status()
+			self.assertEqual(jc.status, status)
+
+		jc = frappe.new_doc("Job Card")
+		jc.for_quantity = 2
+		jc.transferred_qty = 1
+		jc.total_completed_qty = 0
+		assertStatus("Open")
+
+		jc.transferred_qty = jc.for_quantity
+		assertStatus("Material Transferred")
+
+		jc.append("time_logs", {})
+		assertStatus("Work In Progress")
+
+		jc.docstatus = 1
+		jc.total_completed_qty = jc.for_quantity
+		assertStatus("Completed")
+
+		jc.docstatus = 2
+		assertStatus("Cancelled")
+
 
 def create_bom_with_multiple_operations():
 	"Create a BOM with multiple operations and Material Transfer against Job Card"
@@ -286,19 +380,22 @@
 		"operation": "Test Operation A",
 		"workstation": "_Test Workstation A",
 		"hour_rate_rent": 300,
-		"time_in_mins": 60
+		"time_in_mins": 60,
 	}
 	make_workstation(row)
 	make_operation(row)
 
-	bom_doc.append("operations", {
-		"operation": "Test Operation A",
-		"description": "Test Operation A",
-		"workstation": "_Test Workstation A",
-		"hour_rate": 300,
-		"time_in_mins": 60,
-		"operating_cost": 100
-	})
+	bom_doc.append(
+		"operations",
+		{
+			"operation": "Test Operation A",
+			"description": "Test Operation A",
+			"workstation": "_Test Workstation A",
+			"hour_rate": 300,
+			"time_in_mins": 60,
+			"operating_cost": 100,
+		},
+	)
 
 	bom_doc.transfer_material_against = "Job Card"
 	bom_doc.save()
@@ -306,6 +403,7 @@
 
 	return bom_doc
 
+
 def make_wo_with_transfer_against_jc():
 	"Create a WO with multiple operations and Material Transfer against Job Card"
 
@@ -314,7 +412,7 @@
 		qty=4,
 		transfer_material_against="Job Card",
 		source_warehouse="Stores - _TC",
-		do_not_submit=True
+		do_not_submit=True,
 	)
 	work_order.required_items[0].operation = "Test Operation A"
 	work_order.required_items[1].operation = "_Test Operation 1"
@@ -322,8 +420,9 @@
 
 	return work_order
 
+
 def make_bom_for_jc_tests():
-	test_records = frappe.get_test_records('BOM')
+	test_records = frappe.get_test_records("BOM")
 	bom = frappe.copy_doc(test_records[2])
 	bom.set_rate_of_sub_assembly_item_based_on_bom = 0
 	bom.rm_cost_as_per = "Valuation Rate"
diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py
index c919e8b..730a857 100644
--- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py
+++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py
@@ -11,14 +11,19 @@
 class ManufacturingSettings(Document):
 	pass
 
+
 def get_mins_between_operations():
-	return relativedelta(minutes=cint(frappe.db.get_single_value("Manufacturing Settings",
-		"mins_between_operations")) or 10)
+	return relativedelta(
+		minutes=cint(frappe.db.get_single_value("Manufacturing Settings", "mins_between_operations"))
+		or 10
+	)
+
 
 @frappe.whitelist()
 def is_material_consumption_enabled():
-	if not hasattr(frappe.local, 'material_consumption'):
-		frappe.local.material_consumption = cint(frappe.db.get_single_value('Manufacturing Settings',
-			'material_consumption'))
+	if not hasattr(frappe.local, "material_consumption"):
+		frappe.local.material_consumption = cint(
+			frappe.db.get_single_value("Manufacturing Settings", "material_consumption")
+		)
 
 	return frappe.local.material_consumption
diff --git a/erpnext/manufacturing/doctype/operation/operation.py b/erpnext/manufacturing/doctype/operation/operation.py
index 41726f3..9c8f9ac 100644
--- a/erpnext/manufacturing/doctype/operation/operation.py
+++ b/erpnext/manufacturing/doctype/operation/operation.py
@@ -19,12 +19,14 @@
 		operation_list = []
 		for row in self.sub_operations:
 			if row.operation in operation_list:
-				frappe.throw(_("The operation {0} can not add multiple times")
-					.format(frappe.bold(row.operation)))
+				frappe.throw(
+					_("The operation {0} can not add multiple times").format(frappe.bold(row.operation))
+				)
 
 			if self.name == row.operation:
-				frappe.throw(_("The operation {0} can not be the sub operation")
-					.format(frappe.bold(row.operation)))
+				frappe.throw(
+					_("The operation {0} can not be the sub operation").format(frappe.bold(row.operation))
+				)
 
 			operation_list.append(row.operation)
 
diff --git a/erpnext/manufacturing/doctype/operation/operation_dashboard.py b/erpnext/manufacturing/doctype/operation/operation_dashboard.py
index 9f7efa2..8dc901a 100644
--- a/erpnext/manufacturing/doctype/operation/operation_dashboard.py
+++ b/erpnext/manufacturing/doctype/operation/operation_dashboard.py
@@ -3,11 +3,6 @@
 
 def get_data():
 	return {
-		'fieldname': 'operation',
-		'transactions': [
-			{
-				'label': _('Manufacture'),
-				'items': ['BOM', 'Work Order', 'Job Card']
-			}
-		]
+		"fieldname": "operation",
+		"transactions": [{"label": _("Manufacture"), "items": ["BOM", "Work Order", "Job Card"]}],
 	}
diff --git a/erpnext/manufacturing/doctype/operation/test_operation.py b/erpnext/manufacturing/doctype/operation/test_operation.py
index e511084..ce9f8e0 100644
--- a/erpnext/manufacturing/doctype/operation/test_operation.py
+++ b/erpnext/manufacturing/doctype/operation/test_operation.py
@@ -5,11 +5,13 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Operation')
+test_records = frappe.get_test_records("Operation")
+
 
 class TestOperation(unittest.TestCase):
 	pass
 
+
 def make_operation(*args, **kwargs):
 	args = args if args else kwargs
 	if isinstance(args, tuple):
@@ -18,11 +20,9 @@
 	args = frappe._dict(args)
 
 	if not frappe.db.exists("Operation", args.operation):
-		doc = frappe.get_doc({
-			"doctype": "Operation",
-			"name": args.operation,
-			"workstation": args.workstation
-		})
+		doc = frappe.get_doc(
+			{"doctype": "Operation", "name": args.operation, "workstation": args.workstation}
+		)
 		doc.insert()
 		return doc
 
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index e8759f5..59ddf1f 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -2,6 +2,13 @@
 // For license information, please see license.txt
 
 frappe.ui.form.on('Production Plan', {
+
+	before_save: function(frm) {
+		// preserve temporary names on production plan item to re-link sub-assembly items
+		frm.doc.po_items.forEach(item => {
+			item.temporary_name = item.name;
+		});
+	},
 	setup: function(frm) {
 		frm.custom_make_buttons = {
 			'Work Order': 'Work Order / Subcontract PO',
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index 3bfb764..85f9843 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -190,7 +190,7 @@
    "label": "Select Items to Manufacture"
   },
   {
-   "depends_on": "get_items_from",
+   "depends_on": "eval:doc.get_items_from && doc.docstatus == 0",
    "fieldname": "get_items",
    "fieldtype": "Button",
    "label": "Get Finished Goods for Manufacture"
@@ -198,6 +198,7 @@
   {
    "fieldname": "po_items",
    "fieldtype": "Table",
+   "label": "Assembly Items",
    "no_copy": 1,
    "options": "Production Plan Item",
    "reqd": 1
@@ -357,6 +358,7 @@
    "options": "Production Plan Sub Assembly Item"
   },
   {
+   "depends_on": "eval:doc.po_items && doc.po_items.length && doc.docstatus == 0",
    "fieldname": "get_sub_assembly_items",
    "fieldtype": "Button",
    "label": "Get Sub Assembly Items"
@@ -382,7 +384,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-02-23 17:16:10.629378",
+ "modified": "2022-03-25 09:15:25.017664",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Production Plan",
@@ -404,5 +406,6 @@
   }
  ],
  "sort_field": "modified",
- "sort_order": "ASC"
+ "sort_order": "ASC",
+ "states": []
 }
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 48cd753..8a28454 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -32,10 +32,11 @@
 		self.set_pending_qty_in_row_without_reference()
 		self.calculate_total_planned_qty()
 		self.set_status()
+		self._rename_temporary_references()
 
 	def set_pending_qty_in_row_without_reference(self):
 		"Set Pending Qty in independent rows (not from SO or MR)."
-		if self.docstatus > 0: # set only to initialise value before submit
+		if self.docstatus > 0:  # set only to initialise value before submit
 			return
 
 		for item in self.po_items:
@@ -48,7 +49,7 @@
 			self.total_planned_qty += flt(d.planned_qty)
 
 	def validate_data(self):
-		for d in self.get('po_items'):
+		for d in self.get("po_items"):
 			if not d.bom_no:
 				frappe.throw(_("Please select BOM for Item in Row {0}").format(d.idx))
 			else:
@@ -57,9 +58,21 @@
 			if not flt(d.planned_qty):
 				frappe.throw(_("Please enter Planned Qty for Item {0} at row {1}").format(d.item_code, d.idx))
 
+	def _rename_temporary_references(self):
+		"""po_items and sub_assembly_items items are both constructed client side without saving.
+
+		Attempt to fix linkages by using temporary names to map final row names.
+		"""
+		new_name_map = {d.temporary_name: d.name for d in self.po_items if d.temporary_name}
+		actual_names = {d.name for d in self.po_items}
+
+		for sub_assy in self.sub_assembly_items:
+			if sub_assy.production_plan_item not in actual_names:
+				sub_assy.production_plan_item = new_name_map.get(sub_assy.production_plan_item)
+
 	@frappe.whitelist()
 	def get_open_sales_orders(self):
-		""" Pull sales orders  which are pending to deliver based on criteria selected"""
+		"""Pull sales orders  which are pending to deliver based on criteria selected"""
 		open_so = get_sales_orders(self)
 
 		if open_so:
@@ -68,20 +81,23 @@
 			frappe.msgprint(_("Sales orders are not available for production"))
 
 	def add_so_in_table(self, open_so):
-		""" Add sales orders in the table"""
-		self.set('sales_orders', [])
+		"""Add sales orders in the table"""
+		self.set("sales_orders", [])
 
 		for data in open_so:
-			self.append('sales_orders', {
-				'sales_order': data.name,
-				'sales_order_date': data.transaction_date,
-				'customer': data.customer,
-				'grand_total': data.base_grand_total
-			})
+			self.append(
+				"sales_orders",
+				{
+					"sales_order": data.name,
+					"sales_order_date": data.transaction_date,
+					"customer": data.customer,
+					"grand_total": data.base_grand_total,
+				},
+			)
 
 	@frappe.whitelist()
 	def get_pending_material_requests(self):
-		""" Pull Material Requests that are pending based on criteria selected"""
+		"""Pull Material Requests that are pending based on criteria selected"""
 		mr_filter = item_filter = ""
 		if self.from_date:
 			mr_filter += " and mr.transaction_date >= %(from_date)s"
@@ -93,7 +109,8 @@
 		if self.item_code:
 			item_filter += " and mr_item.item_code = %(item)s"
 
-		pending_mr = frappe.db.sql("""
+		pending_mr = frappe.db.sql(
+			"""
 			select distinct mr.name, mr.transaction_date
 			from `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
 			where mr_item.parent = mr.name
@@ -102,29 +119,34 @@
 				and mr_item.qty > ifnull(mr_item.ordered_qty,0) {0} {1}
 				and (exists (select name from `tabBOM` bom where bom.item=mr_item.item_code
 					and bom.is_active = 1))
-			""".format(mr_filter, item_filter), {
+			""".format(
+				mr_filter, item_filter
+			),
+			{
 				"from_date": self.from_date,
 				"to_date": self.to_date,
 				"warehouse": self.warehouse,
 				"item": self.item_code,
-				"company": self.company
-			}, as_dict=1)
+				"company": self.company,
+			},
+			as_dict=1,
+		)
 
 		self.add_mr_in_table(pending_mr)
 
 	def add_mr_in_table(self, pending_mr):
-		""" Add Material Requests in the table"""
-		self.set('material_requests', [])
+		"""Add Material Requests in the table"""
+		self.set("material_requests", [])
 
 		for data in pending_mr:
-			self.append('material_requests', {
-				'material_request': data.name,
-				'material_request_date': data.transaction_date
-			})
+			self.append(
+				"material_requests",
+				{"material_request": data.name, "material_request_date": data.transaction_date},
+			)
 
 	@frappe.whitelist()
 	def get_items(self):
-		self.set('po_items', [])
+		self.set("po_items", [])
 		if self.get_items_from == "Sales Order":
 			self.get_so_items()
 
@@ -139,10 +161,12 @@
 	def get_bom_item(self):
 		"""Check if Item or if its Template has a BOM."""
 		bom_item = None
-		has_bom = frappe.db.exists({'doctype': 'BOM', 'item': self.item_code, 'docstatus': 1})
+		has_bom = frappe.db.exists({"doctype": "BOM", "item": self.item_code, "docstatus": 1})
 		if not has_bom:
-			template_item = frappe.db.get_value('Item', self.item_code, ['variant_of'])
-			bom_item = "bom.item = {0}".format(frappe.db.escape(template_item)) if template_item else bom_item
+			template_item = frappe.db.get_value("Item", self.item_code, ["variant_of"])
+			bom_item = (
+				"bom.item = {0}".format(frappe.db.escape(template_item)) if template_item else bom_item
+			)
 		return bom_item
 
 	def get_so_items(self):
@@ -154,11 +178,12 @@
 
 		item_condition = ""
 		bom_item = "bom.item = so_item.item_code"
-		if self.item_code and frappe.db.exists('Item', self.item_code):
+		if self.item_code and frappe.db.exists("Item", self.item_code):
 			bom_item = self.get_bom_item() or bom_item
-			item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code))
+			item_condition = " and so_item.item_code = {0}".format(frappe.db.escape(self.item_code))
 
-		items = frappe.db.sql("""
+		items = frappe.db.sql(
+			"""
 			select
 				distinct parent, item_code, warehouse,
 				(qty - work_order_qty) * conversion_factor as pending_qty,
@@ -168,16 +193,17 @@
 			where
 				parent in (%s) and docstatus = 1 and qty > work_order_qty
 				and exists (select name from `tabBOM` bom where %s
-				and bom.is_active = 1) %s""" %
-			(", ".join(["%s"] * len(so_list)),
-			bom_item,
-			item_condition),
-			tuple(so_list), as_dict=1)
+				and bom.is_active = 1) %s"""
+			% (", ".join(["%s"] * len(so_list)), bom_item, item_condition),
+			tuple(so_list),
+			as_dict=1,
+		)
 
 		if self.item_code:
-			item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code))
+			item_condition = " and so_item.item_code = {0}".format(frappe.db.escape(self.item_code))
 
-		packed_items = frappe.db.sql("""select distinct pi.parent, pi.item_code, pi.warehouse as warehouse,
+		packed_items = frappe.db.sql(
+			"""select distinct pi.parent, pi.item_code, pi.warehouse as warehouse,
 			(((so_item.qty - so_item.work_order_qty) * pi.qty) / so_item.qty)
 				as pending_qty, pi.parent_item, pi.description, so_item.name
 			from `tabSales Order Item` so_item, `tabPacked Item` pi
@@ -185,16 +211,23 @@
 			and pi.parent_item = so_item.item_code
 			and so_item.parent in (%s) and so_item.qty > so_item.work_order_qty
 			and exists (select name from `tabBOM` bom where bom.item=pi.item_code
-					and bom.is_active = 1) %s""" % \
-			(", ".join(["%s"] * len(so_list)), item_condition), tuple(so_list), as_dict=1)
+					and bom.is_active = 1) %s"""
+			% (", ".join(["%s"] * len(so_list)), item_condition),
+			tuple(so_list),
+			as_dict=1,
+		)
 
 		self.add_items(items + packed_items)
 		self.calculate_total_planned_qty()
 
 	def get_mr_items(self):
 		# Check for empty table or empty rows
-		if not self.get("material_requests") or not self.get_so_mr_list("material_request", "material_requests"):
-			frappe.throw(_("Please fill the Material Requests table"), title=_("Material Requests Required"))
+		if not self.get("material_requests") or not self.get_so_mr_list(
+			"material_request", "material_requests"
+		):
+			frappe.throw(
+				_("Please fill the Material Requests table"), title=_("Material Requests Required")
+			)
 
 		mr_list = self.get_so_mr_list("material_request", "material_requests")
 
@@ -202,13 +235,17 @@
 		if self.item_code:
 			item_condition = " and mr_item.item_code ={0}".format(frappe.db.escape(self.item_code))
 
-		items = frappe.db.sql("""select distinct parent, name, item_code, warehouse, description,
+		items = frappe.db.sql(
+			"""select distinct parent, name, item_code, warehouse, description,
 			(qty - ordered_qty) * conversion_factor as pending_qty
 			from `tabMaterial Request Item` mr_item
 			where parent in (%s) and docstatus = 1 and qty > ordered_qty
 			and exists (select name from `tabBOM` bom where bom.item=mr_item.item_code
-				and bom.is_active = 1) %s""" % \
-			(", ".join(["%s"] * len(mr_list)), item_condition), tuple(mr_list), as_dict=1)
+				and bom.is_active = 1) %s"""
+			% (", ".join(["%s"] * len(mr_list)), item_condition),
+			tuple(mr_list),
+			as_dict=1,
+		)
 
 		self.add_items(items)
 		self.calculate_total_planned_qty()
@@ -219,37 +256,36 @@
 			item_details = get_item_details(data.item_code)
 			if self.combine_items:
 				if item_details.bom_no in refs:
-					refs[item_details.bom_no]['so_details'].append({
-						'sales_order': data.parent,
-						'sales_order_item': data.name,
-						'qty': data.pending_qty
-					})
-					refs[item_details.bom_no]['qty'] += data.pending_qty
+					refs[item_details.bom_no]["so_details"].append(
+						{"sales_order": data.parent, "sales_order_item": data.name, "qty": data.pending_qty}
+					)
+					refs[item_details.bom_no]["qty"] += data.pending_qty
 					continue
 
 				else:
 					refs[item_details.bom_no] = {
-						'qty': data.pending_qty,
-						'po_item_ref': data.name,
-						'so_details': []
+						"qty": data.pending_qty,
+						"po_item_ref": data.name,
+						"so_details": [],
 					}
-					refs[item_details.bom_no]['so_details'].append({
-						'sales_order': data.parent,
-						'sales_order_item': data.name,
-						'qty': data.pending_qty
-					})
+					refs[item_details.bom_no]["so_details"].append(
+						{"sales_order": data.parent, "sales_order_item": data.name, "qty": data.pending_qty}
+					)
 
-			pi = self.append('po_items', {
-				'warehouse': data.warehouse,
-				'item_code': data.item_code,
-				'description': data.description or item_details.description,
-				'stock_uom': item_details and item_details.stock_uom or '',
-				'bom_no': item_details and item_details.bom_no or '',
-				'planned_qty': data.pending_qty,
-				'pending_qty': data.pending_qty,
-				'planned_start_date': now_datetime(),
-				'product_bundle_item': data.parent_item
-			})
+			pi = self.append(
+				"po_items",
+				{
+					"warehouse": data.warehouse,
+					"item_code": data.item_code,
+					"description": data.description or item_details.description,
+					"stock_uom": item_details and item_details.stock_uom or "",
+					"bom_no": item_details and item_details.bom_no or "",
+					"planned_qty": data.pending_qty,
+					"pending_qty": data.pending_qty,
+					"planned_start_date": now_datetime(),
+					"product_bundle_item": data.parent_item,
+				},
+			)
 			pi._set_defaults()
 
 			if self.get_items_from == "Sales Order":
@@ -264,20 +300,23 @@
 
 		if refs:
 			for po_item in self.po_items:
-				po_item.planned_qty = refs[po_item.bom_no]['qty']
-				po_item.pending_qty = refs[po_item.bom_no]['qty']
-				po_item.sales_order = ''
+				po_item.planned_qty = refs[po_item.bom_no]["qty"]
+				po_item.pending_qty = refs[po_item.bom_no]["qty"]
+				po_item.sales_order = ""
 			self.add_pp_ref(refs)
 
 	def add_pp_ref(self, refs):
 		for bom_no in refs:
-			for so_detail in refs[bom_no]['so_details']:
-				self.append('prod_plan_references', {
-						'item_reference': refs[bom_no]['po_item_ref'],
-						'sales_order': so_detail['sales_order'],
-						'sales_order_item': so_detail['sales_order_item'],
-						'qty': so_detail['qty']
-				})
+			for so_detail in refs[bom_no]["so_details"]:
+				self.append(
+					"prod_plan_references",
+					{
+						"item_reference": refs[bom_no]["po_item_ref"],
+						"sales_order": so_detail["sales_order"],
+						"sales_order_item": so_detail["sales_order_item"],
+						"qty": so_detail["qty"],
+					},
+				)
 
 	def calculate_total_produced_qty(self):
 		self.total_produced_qty = 0
@@ -295,27 +334,24 @@
 
 		self.calculate_total_produced_qty()
 		self.set_status()
-		self.db_set('status', self.status)
+		self.db_set("status", self.status)
 
 	def on_cancel(self):
-		self.db_set('status', 'Cancelled')
+		self.db_set("status", "Cancelled")
 		self.delete_draft_work_order()
 
 	def delete_draft_work_order(self):
-		for d in frappe.get_all('Work Order', fields = ["name"],
-			filters = {'docstatus': 0, 'production_plan': ("=", self.name)}):
-			frappe.delete_doc('Work Order', d.name)
+		for d in frappe.get_all(
+			"Work Order", fields=["name"], filters={"docstatus": 0, "production_plan": ("=", self.name)}
+		):
+			frappe.delete_doc("Work Order", d.name)
 
 	@frappe.whitelist()
 	def set_status(self, close=None):
-		self.status = {
-			0: 'Draft',
-			1: 'Submitted',
-			2: 'Cancelled'
-		}.get(self.docstatus)
+		self.status = {0: "Draft", 1: "Submitted", 2: "Cancelled"}.get(self.docstatus)
 
 		if close:
-			self.db_set('status', 'Closed')
+			self.db_set("status", "Closed")
 			return
 
 		if self.total_produced_qty > 0:
@@ -323,12 +359,12 @@
 			if self.all_items_completed():
 				self.status = "Completed"
 
-		if self.status != 'Completed':
+		if self.status != "Completed":
 			self.update_ordered_status()
 			self.update_requested_status()
 
 		if close is not None:
-			self.db_set('status', self.status)
+			self.db_set("status", self.status)
 
 	def update_ordered_status(self):
 		update_status = False
@@ -336,8 +372,8 @@
 			if d.planned_qty == d.ordered_qty:
 				update_status = True
 
-		if update_status and self.status != 'Completed':
-			self.status = 'In Process'
+		if update_status and self.status != "Completed":
+			self.status = "In Process"
 
 	def update_requested_status(self):
 		if not self.mr_items:
@@ -349,44 +385,44 @@
 				update_status = False
 
 		if update_status:
-			self.status = 'Material Requested'
+			self.status = "Material Requested"
 
 	def get_production_items(self):
 		item_dict = {}
 
 		for d in self.po_items:
 			item_details = {
-				"production_item"		: d.item_code,
-				"use_multi_level_bom"   : d.include_exploded_items,
-				"sales_order"			: d.sales_order,
-				"sales_order_item"		: d.sales_order_item,
-				"material_request"		: d.material_request,
-				"material_request_item"	: d.material_request_item,
-				"bom_no"				: d.bom_no,
-				"description"			: d.description,
-				"stock_uom"				: d.stock_uom,
-				"company"				: self.company,
-				"fg_warehouse"			: d.warehouse,
-				"production_plan"       : self.name,
-				"production_plan_item"  : d.name,
-				"product_bundle_item"	: d.product_bundle_item,
-				"planned_start_date"    : d.planned_start_date,
-				"project"               : self.project
+				"production_item": d.item_code,
+				"use_multi_level_bom": d.include_exploded_items,
+				"sales_order": d.sales_order,
+				"sales_order_item": d.sales_order_item,
+				"material_request": d.material_request,
+				"material_request_item": d.material_request_item,
+				"bom_no": d.bom_no,
+				"description": d.description,
+				"stock_uom": d.stock_uom,
+				"company": self.company,
+				"fg_warehouse": d.warehouse,
+				"production_plan": self.name,
+				"production_plan_item": d.name,
+				"product_bundle_item": d.product_bundle_item,
+				"planned_start_date": d.planned_start_date,
+				"project": self.project,
 			}
 
-			if not item_details['project'] and d.sales_order:
-				item_details['project'] = frappe.get_cached_value("Sales Order", d.sales_order, "project")
+			if not item_details["project"] and d.sales_order:
+				item_details["project"] = frappe.get_cached_value("Sales Order", d.sales_order, "project")
 
 			if self.get_items_from == "Material Request":
-				item_details.update({
-					"qty": d.planned_qty
-				})
+				item_details.update({"qty": d.planned_qty})
 				item_dict[(d.item_code, d.material_request_item, d.warehouse)] = item_details
 			else:
-				item_details.update({
-					"qty": flt(item_dict.get((d.item_code, d.sales_order, d.warehouse),{})
-						.get("qty")) + (flt(d.planned_qty) - flt(d.ordered_qty))
-				})
+				item_details.update(
+					{
+						"qty": flt(item_dict.get((d.item_code, d.sales_order, d.warehouse), {}).get("qty"))
+						+ (flt(d.planned_qty) - flt(d.ordered_qty))
+					}
+				)
 				item_dict[(d.item_code, d.sales_order, d.warehouse)] = item_details
 
 		return item_dict
@@ -402,15 +438,15 @@
 		self.make_work_order_for_finished_goods(wo_list, default_warehouses)
 		self.make_work_order_for_subassembly_items(wo_list, subcontracted_po, default_warehouses)
 		self.make_subcontracted_purchase_order(subcontracted_po, po_list)
-		self.show_list_created_message('Work Order', wo_list)
-		self.show_list_created_message('Purchase Order', po_list)
+		self.show_list_created_message("Work Order", wo_list)
+		self.show_list_created_message("Purchase Order", po_list)
 
 	def make_work_order_for_finished_goods(self, wo_list, default_warehouses):
 		items_data = self.get_production_items()
 
 		for key, item in items_data.items():
 			if self.sub_assembly_items:
-				item['use_multi_level_bom'] = 0
+				item["use_multi_level_bom"] = 0
 
 			set_default_warehouses(item, default_warehouses)
 			work_order = self.create_work_order(item)
@@ -419,13 +455,14 @@
 
 	def make_work_order_for_subassembly_items(self, wo_list, subcontracted_po, default_warehouses):
 		for row in self.sub_assembly_items:
-			if row.type_of_manufacturing == 'Subcontract':
+			if row.type_of_manufacturing == "Subcontract":
 				subcontracted_po.setdefault(row.supplier, []).append(row)
 				continue
 
 			work_order_data = {
-				'wip_warehouse': default_warehouses.get('wip_warehouse'),
-				'fg_warehouse': default_warehouses.get('fg_warehouse')
+				"wip_warehouse": default_warehouses.get("wip_warehouse"),
+				"fg_warehouse": default_warehouses.get("fg_warehouse"),
+				"company": self.get("company"),
 			}
 
 			self.prepare_data_for_sub_assembly_items(row, work_order_data)
@@ -434,41 +471,60 @@
 				wo_list.append(work_order)
 
 	def prepare_data_for_sub_assembly_items(self, row, wo_data):
-		for field in ["production_item", "item_name", "qty", "fg_warehouse",
-			"description", "bom_no", "stock_uom", "bom_level",
-			"production_plan_item", "schedule_date"]:
+		for field in [
+			"production_item",
+			"item_name",
+			"qty",
+			"fg_warehouse",
+			"description",
+			"bom_no",
+			"stock_uom",
+			"bom_level",
+			"production_plan_item",
+			"schedule_date",
+		]:
 			if row.get(field):
 				wo_data[field] = row.get(field)
 
-		wo_data.update({
-			"use_multi_level_bom": 0,
-			"production_plan": self.name,
-			"production_plan_sub_assembly_item": row.name
-		})
+		wo_data.update(
+			{
+				"use_multi_level_bom": 0,
+				"production_plan": self.name,
+				"production_plan_sub_assembly_item": row.name,
+			}
+		)
 
 	def make_subcontracted_purchase_order(self, subcontracted_po, purchase_orders):
 		if not subcontracted_po:
 			return
 
 		for supplier, po_list in subcontracted_po.items():
-			po = frappe.new_doc('Purchase Order')
+			po = frappe.new_doc("Purchase Order")
+			po.company = self.company
 			po.supplier = supplier
 			po.schedule_date = getdate(po_list[0].schedule_date) if po_list[0].schedule_date else nowdate()
-			po.is_subcontracted = 'Yes'
+			po.is_subcontracted = 1
 			for row in po_list:
 				po_data = {
-					'item_code': row.production_item,
-					'warehouse': row.fg_warehouse,
-					'production_plan_sub_assembly_item': row.name,
-					'bom': row.bom_no,
-					'production_plan': self.name
+					"item_code": row.production_item,
+					"warehouse": row.fg_warehouse,
+					"production_plan_sub_assembly_item": row.name,
+					"bom": row.bom_no,
+					"production_plan": self.name,
 				}
 
-				for field in ['schedule_date', 'qty', 'uom', 'stock_uom', 'item_name',
-					'description', 'production_plan_item']:
+				for field in [
+					"schedule_date",
+					"qty",
+					"uom",
+					"stock_uom",
+					"item_name",
+					"description",
+					"production_plan_item",
+				]:
 					po_data[field] = row.get(field)
 
-				po.append('items', po_data)
+				po.append("items", po_data)
 
 			po.set_missing_values()
 			po.flags.ignore_mandatory = True
@@ -490,7 +546,7 @@
 
 		wo = frappe.new_doc("Work Order")
 		wo.update(item)
-		wo.planned_start_date = item.get('planned_start_date') or item.get('schedule_date')
+		wo.planned_start_date = item.get("planned_start_date") or item.get("schedule_date")
 
 		if item.get("warehouse"):
 			wo.fg_warehouse = item.get("warehouse")
@@ -508,54 +564,60 @@
 
 	@frappe.whitelist()
 	def make_material_request(self):
-		'''Create Material Requests grouped by Sales Order and Material Request Type'''
+		"""Create Material Requests grouped by Sales Order and Material Request Type"""
 		material_request_list = []
 		material_request_map = {}
 
 		for item in self.mr_items:
-			item_doc = frappe.get_cached_doc('Item', item.item_code)
+			item_doc = frappe.get_cached_doc("Item", item.item_code)
 
 			material_request_type = item.material_request_type or item_doc.default_material_request_type
 
 			# key for Sales Order:Material Request Type:Customer
-			key = '{}:{}:{}'.format(item.sales_order, material_request_type, item_doc.customer or '')
+			key = "{}:{}:{}".format(item.sales_order, material_request_type, item_doc.customer or "")
 			schedule_date = add_days(nowdate(), cint(item_doc.lead_time_days))
 
 			if not key in material_request_map:
 				# make a new MR for the combination
 				material_request_map[key] = frappe.new_doc("Material Request")
 				material_request = material_request_map[key]
-				material_request.update({
-					"transaction_date": nowdate(),
-					"status": "Draft",
-					"company": self.company,
-					'material_request_type': material_request_type,
-					'customer': item_doc.customer or ''
-				})
+				material_request.update(
+					{
+						"transaction_date": nowdate(),
+						"status": "Draft",
+						"company": self.company,
+						"material_request_type": material_request_type,
+						"customer": item_doc.customer or "",
+					}
+				)
 				material_request_list.append(material_request)
 			else:
 				material_request = material_request_map[key]
 
 			# add item
-			material_request.append("items", {
-				"item_code": item.item_code,
-				"from_warehouse": item.from_warehouse,
-				"qty": item.quantity,
-				"schedule_date": schedule_date,
-				"warehouse": item.warehouse,
-				"sales_order": item.sales_order,
-				'production_plan': self.name,
-				'material_request_plan_item': item.name,
-				"project": frappe.db.get_value("Sales Order", item.sales_order, "project") \
-					if item.sales_order else None
-			})
+			material_request.append(
+				"items",
+				{
+					"item_code": item.item_code,
+					"from_warehouse": item.from_warehouse,
+					"qty": item.quantity,
+					"schedule_date": schedule_date,
+					"warehouse": item.warehouse,
+					"sales_order": item.sales_order,
+					"production_plan": self.name,
+					"material_request_plan_item": item.name,
+					"project": frappe.db.get_value("Sales Order", item.sales_order, "project")
+					if item.sales_order
+					else None,
+				},
+			)
 
 		for material_request in material_request_list:
 			# submit
 			material_request.flags.ignore_permissions = 1
 			material_request.run_method("set_missing_values")
 
-			if self.get('submit_material_request'):
+			if self.get("submit_material_request"):
 				material_request.submit()
 			else:
 				material_request.save()
@@ -563,17 +625,19 @@
 		frappe.flags.mute_messages = False
 
 		if material_request_list:
-			material_request_list = ["""<a href="/app/Form/Material Request/{0}">{1}</a>""".format(m.name, m.name) \
-				for m in material_request_list]
+			material_request_list = [
+				"""<a href="/app/Form/Material Request/{0}">{1}</a>""".format(m.name, m.name)
+				for m in material_request_list
+			]
 			msgprint(_("{0} created").format(comma_and(material_request_list)))
-		else :
+		else:
 			msgprint(_("No material request created"))
 
 	@frappe.whitelist()
 	def get_sub_assembly_items(self, manufacturing_type=None):
 		"Fetch sub assembly items and optionally combine them."
 		self.sub_assembly_items = []
-		sub_assembly_items_store = [] # temporary store to process all subassembly items
+		sub_assembly_items_store = []  # temporary store to process all subassembly items
 
 		for row in self.po_items:
 			bom_data = []
@@ -585,7 +649,7 @@
 			# Combine subassembly items
 			sub_assembly_items_store = self.combine_subassembly_items(sub_assembly_items_store)
 
-		sub_assembly_items_store.sort(key= lambda d: d.bom_level, reverse=True) # sort by bom level
+		sub_assembly_items_store.sort(key=lambda d: d.bom_level, reverse=True)  # sort by bom level
 
 		for idx, row in enumerate(sub_assembly_items_store):
 			row.idx = idx + 1
@@ -598,16 +662,19 @@
 			data.production_plan_item = row.name
 			data.fg_warehouse = row.warehouse
 			data.schedule_date = row.planned_start_date
-			data.type_of_manufacturing = manufacturing_type or ("Subcontract" if data.is_sub_contracted_item
-				else "In House")
+			data.type_of_manufacturing = manufacturing_type or (
+				"Subcontract" if data.is_sub_contracted_item else "In House"
+			)
 
 	def combine_subassembly_items(self, sub_assembly_items_store):
 		"Aggregate if same: Item, Warehouse, Inhouse/Outhouse Manu.g, BOM No."
 		key_wise_data = {}
 		for row in sub_assembly_items_store:
 			key = (
-				row.get("production_item"), row.get("fg_warehouse"),
-				row.get("bom_no"), row.get("type_of_manufacturing")
+				row.get("production_item"),
+				row.get("fg_warehouse"),
+				row.get("bom_no"),
+				row.get("type_of_manufacturing"),
 			)
 			if key not in key_wise_data:
 				# intialise (item, wh, bom no, man.g type) wise dict
@@ -625,12 +692,15 @@
 				# add row with key
 				key_wise_data[key] = row
 
-		sub_assembly_items_store = [key_wise_data[key] for key in key_wise_data] # unpack into single level list
+		sub_assembly_items_store = [
+			key_wise_data[key] for key in key_wise_data
+		]  # unpack into single level list
 		return sub_assembly_items_store
 
 	def all_items_completed(self):
-		all_items_produced = all(flt(d.planned_qty) - flt(d.produced_qty) < 0.000001
-									for d in self.po_items)
+		all_items_produced = all(
+			flt(d.planned_qty) - flt(d.produced_qty) < 0.000001 for d in self.po_items
+		)
 		if not all_items_produced:
 			return False
 
@@ -647,40 +717,81 @@
 		all_work_orders_completed = all(s == "Completed" for s in wo_status)
 		return all_work_orders_completed
 
+
 @frappe.whitelist()
 def download_raw_materials(doc, warehouses=None):
 	if isinstance(doc, str):
 		doc = frappe._dict(json.loads(doc))
 
-	item_list = [['Item Code', 'Item Name', 'Description',
-		'Stock UOM', 'Warehouse', 'Required Qty as per BOM',
-		'Projected Qty', 'Available Qty In Hand', 'Ordered Qty', 'Planned Qty',
-		'Reserved Qty for Production', 'Safety Stock', 'Required Qty']]
+	item_list = [
+		[
+			"Item Code",
+			"Item Name",
+			"Description",
+			"Stock UOM",
+			"Warehouse",
+			"Required Qty as per BOM",
+			"Projected Qty",
+			"Available Qty In Hand",
+			"Ordered Qty",
+			"Planned Qty",
+			"Reserved Qty for Production",
+			"Safety Stock",
+			"Required Qty",
+		]
+	]
 
 	doc.warehouse = None
 	frappe.flags.show_qty_in_stock_uom = 1
-	items = get_items_for_material_requests(doc, warehouses=warehouses, get_parent_warehouse_data=True)
+	items = get_items_for_material_requests(
+		doc, warehouses=warehouses, get_parent_warehouse_data=True
+	)
 
 	for d in items:
-		item_list.append([d.get('item_code'), d.get('item_name'),
-			d.get('description'), d.get('stock_uom'), d.get('warehouse'),
-			d.get('required_bom_qty'), d.get('projected_qty'), d.get('actual_qty'), d.get('ordered_qty'),
-			d.get('planned_qty'), d.get('reserved_qty_for_production'), d.get('safety_stock'), d.get('quantity')])
+		item_list.append(
+			[
+				d.get("item_code"),
+				d.get("item_name"),
+				d.get("description"),
+				d.get("stock_uom"),
+				d.get("warehouse"),
+				d.get("required_bom_qty"),
+				d.get("projected_qty"),
+				d.get("actual_qty"),
+				d.get("ordered_qty"),
+				d.get("planned_qty"),
+				d.get("reserved_qty_for_production"),
+				d.get("safety_stock"),
+				d.get("quantity"),
+			]
+		)
 
-		if not doc.get('for_warehouse'):
-			row = {'item_code': d.get('item_code')}
+		if not doc.get("for_warehouse"):
+			row = {"item_code": d.get("item_code")}
 			for bin_dict in get_bin_details(row, doc.company, all_warehouse=True):
-				if d.get("warehouse") == bin_dict.get('warehouse'):
+				if d.get("warehouse") == bin_dict.get("warehouse"):
 					continue
 
-				item_list.append(['', '', '', bin_dict.get('warehouse'), '',
-					bin_dict.get('projected_qty', 0), bin_dict.get('actual_qty', 0),
-					bin_dict.get('ordered_qty', 0), bin_dict.get('reserved_qty_for_production', 0)])
+				item_list.append(
+					[
+						"",
+						"",
+						"",
+						bin_dict.get("warehouse"),
+						"",
+						bin_dict.get("projected_qty", 0),
+						bin_dict.get("actual_qty", 0),
+						bin_dict.get("ordered_qty", 0),
+						bin_dict.get("reserved_qty_for_production", 0),
+					]
+				)
 
 	build_csv_response(item_list, doc.name)
 
+
 def get_exploded_items(item_details, company, bom_no, include_non_stock_items, planned_qty=1):
-	for d in frappe.db.sql("""select bei.item_code, item.default_bom as bom,
+	for d in frappe.db.sql(
+		"""select bei.item_code, item.default_bom as bom,
 			ifnull(sum(bei.stock_qty/ifnull(bom.quantity, 1)), 0)*%s as qty, item.item_name,
 			bei.description, bei.stock_uom, item.min_order_qty, bei.source_warehouse,
 			item.default_material_request_type, item.min_order_qty, item_default.default_warehouse,
@@ -696,21 +807,38 @@
 		where
 			bei.docstatus < 2
 			and bom.name=%s and item.is_stock_item in (1, {0})
-		group by bei.item_code, bei.stock_uom""".format(0 if include_non_stock_items else 1),
-		(planned_qty, company, bom_no), as_dict=1):
+		group by bei.item_code, bei.stock_uom""".format(
+			0 if include_non_stock_items else 1
+		),
+		(planned_qty, company, bom_no),
+		as_dict=1,
+	):
 		if not d.conversion_factor and d.purchase_uom:
 			d.conversion_factor = get_uom_conversion_factor(d.item_code, d.purchase_uom)
-		item_details.setdefault(d.get('item_code'), d)
+		item_details.setdefault(d.get("item_code"), d)
 
 	return item_details
 
-def get_uom_conversion_factor(item_code, uom):
-	return frappe.db.get_value('UOM Conversion Detail',
-		{'parent': item_code, 'uom': uom}, 'conversion_factor')
 
-def get_subitems(doc, data, item_details, bom_no, company, include_non_stock_items,
-	include_subcontracted_items, parent_qty, planned_qty=1):
-	items = frappe.db.sql("""
+def get_uom_conversion_factor(item_code, uom):
+	return frappe.db.get_value(
+		"UOM Conversion Detail", {"parent": item_code, "uom": uom}, "conversion_factor"
+	)
+
+
+def get_subitems(
+	doc,
+	data,
+	item_details,
+	bom_no,
+	company,
+	include_non_stock_items,
+	include_subcontracted_items,
+	parent_qty,
+	planned_qty=1,
+):
+	items = frappe.db.sql(
+		"""
 		SELECT
 			bom_item.item_code, default_material_request_type, item.item_name,
 			ifnull(%(parent_qty)s * sum(bom_item.stock_qty/ifnull(bom.quantity, 1)) * %(planned_qty)s, 0) as qty,
@@ -721,7 +849,7 @@
 		FROM
 			`tabBOM Item` bom_item
 			JOIN `tabBOM` bom ON bom.name = bom_item.parent
-			JOIN tabItem item ON bom_item.item_code = item.name
+			JOIN `tabItem` item ON bom_item.item_code = item.name
 			LEFT JOIN `tabItem Default` item_default
 				ON item.name = item_default.parent and item_default.company = %(company)s
 			LEFT JOIN `tabUOM Conversion Detail` item_uom
@@ -730,15 +858,15 @@
 			bom.name = %(bom)s
 			and bom_item.docstatus < 2
 			and item.is_stock_item in (1, {0})
-		group by bom_item.item_code""".format(0 if include_non_stock_items else 1),{
-			'bom': bom_no,
-			'parent_qty': parent_qty,
-			'planned_qty': planned_qty,
-			'company': company
-		}, as_dict=1)
+		group by bom_item.item_code""".format(
+			0 if include_non_stock_items else 1
+		),
+		{"bom": bom_no, "parent_qty": parent_qty, "planned_qty": planned_qty, "company": company},
+		as_dict=1,
+	)
 
 	for d in items:
-		if not data.get('include_exploded_items') or not d.default_bom:
+		if not data.get("include_exploded_items") or not d.default_bom:
 			if d.item_code in item_details:
 				item_details[d.item_code].qty = item_details[d.item_code].qty + d.qty
 			else:
@@ -747,93 +875,111 @@
 
 				item_details[d.item_code] = d
 
-		if data.get('include_exploded_items') and d.default_bom:
-			if ((d.default_material_request_type in ["Manufacture", "Purchase"] and
-				not d.is_sub_contracted) or (d.is_sub_contracted and include_subcontracted_items)):
+		if data.get("include_exploded_items") and d.default_bom:
+			if (
+				d.default_material_request_type in ["Manufacture", "Purchase"] and not d.is_sub_contracted
+			) or (d.is_sub_contracted and include_subcontracted_items):
 				if d.qty > 0:
-					get_subitems(doc, data, item_details, d.default_bom, company,
-						include_non_stock_items, include_subcontracted_items, d.qty)
+					get_subitems(
+						doc,
+						data,
+						item_details,
+						d.default_bom,
+						company,
+						include_non_stock_items,
+						include_subcontracted_items,
+						d.qty,
+					)
 	return item_details
 
-def get_material_request_items(row, sales_order, company,
-	ignore_existing_ordered_qty, include_safety_stock, warehouse, bin_dict):
-	total_qty = row['qty']
+
+def get_material_request_items(
+	row, sales_order, company, ignore_existing_ordered_qty, include_safety_stock, warehouse, bin_dict
+):
+	total_qty = row["qty"]
 
 	required_qty = 0
 	if ignore_existing_ordered_qty or bin_dict.get("projected_qty", 0) < 0:
 		required_qty = total_qty
 	elif total_qty > bin_dict.get("projected_qty", 0):
 		required_qty = total_qty - bin_dict.get("projected_qty", 0)
-	if required_qty > 0 and required_qty < row['min_order_qty']:
-		required_qty = row['min_order_qty']
+	if required_qty > 0 and required_qty < row["min_order_qty"]:
+		required_qty = row["min_order_qty"]
 	item_group_defaults = get_item_group_defaults(row.item_code, company)
 
-	if not row['purchase_uom']:
-		row['purchase_uom'] = row['stock_uom']
+	if not row["purchase_uom"]:
+		row["purchase_uom"] = row["stock_uom"]
 
-	if row['purchase_uom'] != row['stock_uom']:
-		if not (row['conversion_factor'] or frappe.flags.show_qty_in_stock_uom):
-			frappe.throw(_("UOM Conversion factor ({0} -> {1}) not found for item: {2}")
-				.format(row['purchase_uom'], row['stock_uom'], row.item_code))
+	if row["purchase_uom"] != row["stock_uom"]:
+		if not (row["conversion_factor"] or frappe.flags.show_qty_in_stock_uom):
+			frappe.throw(
+				_("UOM Conversion factor ({0} -> {1}) not found for item: {2}").format(
+					row["purchase_uom"], row["stock_uom"], row.item_code
+				)
+			)
 
-			required_qty = required_qty / row['conversion_factor']
+			required_qty = required_qty / row["conversion_factor"]
 
-	if frappe.db.get_value("UOM", row['purchase_uom'], "must_be_whole_number"):
+	if frappe.db.get_value("UOM", row["purchase_uom"], "must_be_whole_number"):
 		required_qty = ceil(required_qty)
 
 	if include_safety_stock:
-		required_qty += flt(row['safety_stock'])
+		required_qty += flt(row["safety_stock"])
 
 	if required_qty > 0:
 		return {
-			'item_code': row.item_code,
-			'item_name': row.item_name,
-			'quantity': required_qty,
-			'required_bom_qty': total_qty,
-			'stock_uom': row.get("stock_uom"),
-			'warehouse': warehouse or row.get('source_warehouse') \
-				or row.get('default_warehouse') or item_group_defaults.get("default_warehouse"),
-			'safety_stock': row.safety_stock,
-			'actual_qty': bin_dict.get("actual_qty", 0),
-			'projected_qty': bin_dict.get("projected_qty", 0),
-			'ordered_qty': bin_dict.get("ordered_qty", 0),
-			'reserved_qty_for_production': bin_dict.get("reserved_qty_for_production", 0),
-			'min_order_qty': row['min_order_qty'],
-			'material_request_type': row.get("default_material_request_type"),
-			'sales_order': sales_order,
-			'description': row.get("description"),
-			'uom': row.get("purchase_uom") or row.get("stock_uom")
+			"item_code": row.item_code,
+			"item_name": row.item_name,
+			"quantity": required_qty,
+			"required_bom_qty": total_qty,
+			"stock_uom": row.get("stock_uom"),
+			"warehouse": warehouse
+			or row.get("source_warehouse")
+			or row.get("default_warehouse")
+			or item_group_defaults.get("default_warehouse"),
+			"safety_stock": row.safety_stock,
+			"actual_qty": bin_dict.get("actual_qty", 0),
+			"projected_qty": bin_dict.get("projected_qty", 0),
+			"ordered_qty": bin_dict.get("ordered_qty", 0),
+			"reserved_qty_for_production": bin_dict.get("reserved_qty_for_production", 0),
+			"min_order_qty": row["min_order_qty"],
+			"material_request_type": row.get("default_material_request_type"),
+			"sales_order": sales_order,
+			"description": row.get("description"),
+			"uom": row.get("purchase_uom") or row.get("stock_uom"),
 		}
 
+
 def get_sales_orders(self):
 	so_filter = item_filter = ""
 	bom_item = "bom.item = so_item.item_code"
 
 	date_field_mapper = {
-		'from_date': ('>=', 'so.transaction_date'),
-		'to_date': ('<=', 'so.transaction_date'),
-		'from_delivery_date': ('>=', 'so_item.delivery_date'),
-		'to_delivery_date': ('<=', 'so_item.delivery_date')
+		"from_date": (">=", "so.transaction_date"),
+		"to_date": ("<=", "so.transaction_date"),
+		"from_delivery_date": (">=", "so_item.delivery_date"),
+		"to_delivery_date": ("<=", "so_item.delivery_date"),
 	}
 
 	for field, value in date_field_mapper.items():
 		if self.get(field):
 			so_filter += f" and {value[1]} {value[0]} %({field})s"
 
-	for field in ['customer', 'project', 'sales_order_status']:
+	for field in ["customer", "project", "sales_order_status"]:
 		if self.get(field):
-			so_field = 'status' if field == 'sales_order_status' else field
+			so_field = "status" if field == "sales_order_status" else field
 			so_filter += f" and so.{so_field} = %({field})s"
 
-	if self.item_code and frappe.db.exists('Item', self.item_code):
+	if self.item_code and frappe.db.exists("Item", self.item_code):
 		bom_item = self.get_bom_item() or bom_item
 		item_filter += " and so_item.item_code = %(item_code)s"
 
-	open_so = frappe.db.sql(f"""
+	open_so = frappe.db.sql(
+		f"""
 		select distinct so.name, so.transaction_date, so.customer, so.base_grand_total
 		from `tabSales Order` so, `tabSales Order Item` so_item
 		where so_item.parent = so.name
-			and so.docstatus = 1 and so.status not in ("Stopped", "Closed")
+			and so.docstatus = 1 and so.status not in ('Stopped', 'Closed')
 			and so.company = %(company)s
 			and so_item.qty > so_item.work_order_qty {so_filter} {item_filter}
 			and (exists (select name from `tabBOM` bom where {bom_item}
@@ -842,10 +988,14 @@
 					where pi.parent = so.name and pi.parent_item = so_item.item_code
 						and exists (select name from `tabBOM` bom where bom.item=pi.item_code
 							and bom.is_active = 1)))
-		""", self.as_dict(), as_dict=1)
+		""",
+		self.as_dict(),
+		as_dict=1,
+	)
 
 	return open_so
 
+
 @frappe.whitelist()
 def get_bin_details(row, company, for_warehouse=None, all_warehouse=False):
 	if isinstance(row, str):
@@ -854,30 +1004,42 @@
 	company = frappe.db.escape(company)
 	conditions, warehouse = "", ""
 
-	conditions = " and warehouse in (select name from `tabWarehouse` where company = {0})".format(company)
+	conditions = " and warehouse in (select name from `tabWarehouse` where company = {0})".format(
+		company
+	)
 	if not all_warehouse:
-		warehouse = for_warehouse or row.get('source_warehouse') or row.get('default_warehouse')
+		warehouse = for_warehouse or row.get("source_warehouse") or row.get("default_warehouse")
 
 	if warehouse:
 		lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"])
 		conditions = """ and warehouse in (select name from `tabWarehouse`
 			where lft >= {0} and rgt <= {1} and name=`tabBin`.warehouse and company = {2})
-		""".format(lft, rgt, company)
+		""".format(
+			lft, rgt, company
+		)
 
-	return frappe.db.sql(""" select ifnull(sum(projected_qty),0) as projected_qty,
+	return frappe.db.sql(
+		""" select ifnull(sum(projected_qty),0) as projected_qty,
 		ifnull(sum(actual_qty),0) as actual_qty, ifnull(sum(ordered_qty),0) as ordered_qty,
 		ifnull(sum(reserved_qty_for_production),0) as reserved_qty_for_production, warehouse,
 		ifnull(sum(planned_qty),0) as planned_qty
 		from `tabBin` where item_code = %(item_code)s {conditions}
 		group by item_code, warehouse
-	""".format(conditions=conditions), { "item_code": row['item_code'] }, as_dict=1)
+	""".format(
+			conditions=conditions
+		),
+		{"item_code": row["item_code"]},
+		as_dict=1,
+	)
+
 
 @frappe.whitelist()
 def get_so_details(sales_order):
-	return frappe.db.get_value("Sales Order", sales_order,
-		['transaction_date', 'customer', 'grand_total'], as_dict=1
+	return frappe.db.get_value(
+		"Sales Order", sales_order, ["transaction_date", "customer", "grand_total"], as_dict=1
 	)
 
+
 def get_warehouse_list(warehouses):
 	warehouse_list = []
 
@@ -885,7 +1047,7 @@
 		warehouses = json.loads(warehouses)
 
 	for row in warehouses:
-		child_warehouses = frappe.db.get_descendants('Warehouse', row.get("warehouse"))
+		child_warehouses = frappe.db.get_descendants("Warehouse", row.get("warehouse"))
 		if child_warehouses:
 			warehouse_list.extend(child_warehouses)
 		else:
@@ -893,6 +1055,7 @@
 
 	return warehouse_list
 
+
 @frappe.whitelist()
 def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_data=None):
 	if isinstance(doc, str):
@@ -901,73 +1064,92 @@
 	if warehouses:
 		warehouses = list(set(get_warehouse_list(warehouses)))
 
-		if doc.get("for_warehouse") and not get_parent_warehouse_data and doc.get("for_warehouse") in warehouses:
+		if (
+			doc.get("for_warehouse")
+			and not get_parent_warehouse_data
+			and doc.get("for_warehouse") in warehouses
+		):
 			warehouses.remove(doc.get("for_warehouse"))
 
-	doc['mr_items'] = []
+	doc["mr_items"] = []
 
-	po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items')
+	po_items = doc.get("po_items") if doc.get("po_items") else doc.get("items")
 	# Check for empty table or empty rows
-	if not po_items or not [row.get('item_code') for row in po_items if row.get('item_code')]:
-		frappe.throw(_("Items to Manufacture are required to pull the Raw Materials associated with it."),
-			title=_("Items Required"))
+	if not po_items or not [row.get("item_code") for row in po_items if row.get("item_code")]:
+		frappe.throw(
+			_("Items to Manufacture are required to pull the Raw Materials associated with it."),
+			title=_("Items Required"),
+		)
 
-	company = doc.get('company')
-	ignore_existing_ordered_qty = doc.get('ignore_existing_ordered_qty')
-	include_safety_stock = doc.get('include_safety_stock')
+	company = doc.get("company")
+	ignore_existing_ordered_qty = doc.get("ignore_existing_ordered_qty")
+	include_safety_stock = doc.get("include_safety_stock")
 
 	so_item_details = frappe._dict()
 	for data in po_items:
 		if not data.get("include_exploded_items") and doc.get("sub_assembly_items"):
 			data["include_exploded_items"] = 1
 
-		planned_qty = data.get('required_qty') or data.get('planned_qty')
-		ignore_existing_ordered_qty = data.get('ignore_existing_ordered_qty') or ignore_existing_ordered_qty
-		warehouse = doc.get('for_warehouse')
+		planned_qty = data.get("required_qty") or data.get("planned_qty")
+		ignore_existing_ordered_qty = (
+			data.get("ignore_existing_ordered_qty") or ignore_existing_ordered_qty
+		)
+		warehouse = doc.get("for_warehouse")
 
 		item_details = {}
 		if data.get("bom") or data.get("bom_no"):
-			if data.get('required_qty'):
-				bom_no = data.get('bom')
+			if data.get("required_qty"):
+				bom_no = data.get("bom")
 				include_non_stock_items = 1
-				include_subcontracted_items = 1 if data.get('include_exploded_items') else 0
+				include_subcontracted_items = 1 if data.get("include_exploded_items") else 0
 			else:
-				bom_no = data.get('bom_no')
-				include_subcontracted_items = doc.get('include_subcontracted_items')
-				include_non_stock_items = doc.get('include_non_stock_items')
+				bom_no = data.get("bom_no")
+				include_subcontracted_items = doc.get("include_subcontracted_items")
+				include_non_stock_items = doc.get("include_non_stock_items")
 
 			if not planned_qty:
-				frappe.throw(_("For row {0}: Enter Planned Qty").format(data.get('idx')))
+				frappe.throw(_("For row {0}: Enter Planned Qty").format(data.get("idx")))
 
 			if bom_no:
-				if data.get('include_exploded_items') and include_subcontracted_items:
+				if data.get("include_exploded_items") and include_subcontracted_items:
 					# fetch exploded items from BOM
-					item_details = get_exploded_items(item_details,
-						company, bom_no, include_non_stock_items, planned_qty=planned_qty)
+					item_details = get_exploded_items(
+						item_details, company, bom_no, include_non_stock_items, planned_qty=planned_qty
+					)
 				else:
-					item_details = get_subitems(doc, data, item_details, bom_no, company,
-						include_non_stock_items, include_subcontracted_items, 1, planned_qty=planned_qty)
-		elif data.get('item_code'):
-			item_master = frappe.get_doc('Item', data['item_code']).as_dict()
+					item_details = get_subitems(
+						doc,
+						data,
+						item_details,
+						bom_no,
+						company,
+						include_non_stock_items,
+						include_subcontracted_items,
+						1,
+						planned_qty=planned_qty,
+					)
+		elif data.get("item_code"):
+			item_master = frappe.get_doc("Item", data["item_code"]).as_dict()
 			purchase_uom = item_master.purchase_uom or item_master.stock_uom
-			conversion_factor = (get_uom_conversion_factor(item_master.name, purchase_uom)
-				if item_master.purchase_uom else 1.0)
+			conversion_factor = (
+				get_uom_conversion_factor(item_master.name, purchase_uom) if item_master.purchase_uom else 1.0
+			)
 
 			item_details[item_master.name] = frappe._dict(
 				{
-					'item_name' : item_master.item_name,
-					'default_bom' : doc.bom,
-					'purchase_uom' : purchase_uom,
-					'default_warehouse': item_master.default_warehouse,
-					'min_order_qty' : item_master.min_order_qty,
-					'default_material_request_type' : item_master.default_material_request_type,
-					'qty': planned_qty or 1,
-					'is_sub_contracted' : item_master.is_subcontracted_item,
-					'item_code' : item_master.name,
-					'description' : item_master.description,
-					'stock_uom' : item_master.stock_uom,
-					'conversion_factor' : conversion_factor,
-					'safety_stock': item_master.safety_stock
+					"item_name": item_master.item_name,
+					"default_bom": doc.bom,
+					"purchase_uom": purchase_uom,
+					"default_warehouse": item_master.default_warehouse,
+					"min_order_qty": item_master.min_order_qty,
+					"default_material_request_type": item_master.default_material_request_type,
+					"qty": planned_qty or 1,
+					"is_sub_contracted": item_master.is_subcontracted_item,
+					"item_code": item_master.name,
+					"description": item_master.description,
+					"stock_uom": item_master.stock_uom,
+					"conversion_factor": conversion_factor,
+					"safety_stock": item_master.safety_stock,
 				}
 			)
 
@@ -976,7 +1158,9 @@
 		for item_code, details in item_details.items():
 			so_item_details.setdefault(sales_order, frappe._dict())
 			if item_code in so_item_details.get(sales_order, {}):
-				so_item_details[sales_order][item_code]['qty'] = so_item_details[sales_order][item_code].get("qty", 0) + flt(details.qty)
+				so_item_details[sales_order][item_code]["qty"] = so_item_details[sales_order][item_code].get(
+					"qty", 0
+				) + flt(details.qty)
 			else:
 				so_item_details[sales_order][item_code] = details
 
@@ -988,8 +1172,15 @@
 			bin_dict = bin_dict[0] if bin_dict else {}
 
 			if details.qty > 0:
-				items = get_material_request_items(details, sales_order, company,
-					ignore_existing_ordered_qty, include_safety_stock, warehouse, bin_dict)
+				items = get_material_request_items(
+					details,
+					sales_order,
+					company,
+					ignore_existing_ordered_qty,
+					include_safety_stock,
+					warehouse,
+					bin_dict,
+				)
 				if items:
 					mr_items.append(items)
 
@@ -1002,51 +1193,62 @@
 
 	if not mr_items:
 		to_enable = frappe.bold(_("Ignore Existing Projected Quantity"))
-		warehouse = frappe.bold(doc.get('for_warehouse'))
-		message = _("As there are sufficient raw materials, Material Request is not required for Warehouse {0}.").format(warehouse) + "<br><br>"
+		warehouse = frappe.bold(doc.get("for_warehouse"))
+		message = (
+			_(
+				"As there are sufficient raw materials, Material Request is not required for Warehouse {0}."
+			).format(warehouse)
+			+ "<br><br>"
+		)
 		message += _("If you still want to proceed, please enable {0}.").format(to_enable)
 
 		frappe.msgprint(message, title=_("Note"))
 
 	return mr_items
 
+
 def get_materials_from_other_locations(item, warehouses, new_mr_items, company):
 	from erpnext.stock.doctype.pick_list.pick_list import get_available_item_locations
-	locations = get_available_item_locations(item.get("item_code"),
-		warehouses, item.get("quantity"), company, ignore_validation=True)
+
+	locations = get_available_item_locations(
+		item.get("item_code"), warehouses, item.get("quantity"), company, ignore_validation=True
+	)
 
 	required_qty = item.get("quantity")
 	# get available material by transferring to production warehouse
 	for d in locations:
-		if required_qty <=0: return
+		if required_qty <= 0:
+			return
 
 		new_dict = copy.deepcopy(item)
 		quantity = required_qty if d.get("qty") > required_qty else d.get("qty")
 
-		if required_qty > 0:
-			new_dict.update({
+		new_dict.update(
+			{
 				"quantity": quantity,
 				"material_request_type": "Material Transfer",
 				"uom": new_dict.get("stock_uom"),  # internal transfer should be in stock UOM
-				"from_warehouse": d.get("warehouse")
-			})
+				"from_warehouse": d.get("warehouse"),
+			}
+		)
 
-			required_qty -= quantity
-			new_mr_items.append(new_dict)
+		required_qty -= quantity
+		new_mr_items.append(new_dict)
 
 	# raise purchase request for remaining qty
 	if required_qty:
 		stock_uom, purchase_uom = frappe.db.get_value(
-			'Item',
-			item['item_code'],
-			['stock_uom', 'purchase_uom']
+			"Item", item["item_code"], ["stock_uom", "purchase_uom"]
 		)
 
-		if purchase_uom != stock_uom and purchase_uom == item['uom']:
-			conversion_factor = get_uom_conversion_factor(item['item_code'], item['uom'])
+		if purchase_uom != stock_uom and purchase_uom == item["uom"]:
+			conversion_factor = get_uom_conversion_factor(item["item_code"], item["uom"])
 			if not (conversion_factor or frappe.flags.show_qty_in_stock_uom):
-				frappe.throw(_("UOM Conversion factor ({0} -> {1}) not found for item: {2}")
-					.format(purchase_uom, stock_uom, item['item_code']))
+				frappe.throw(
+					_("UOM Conversion factor ({0} -> {1}) not found for item: {2}").format(
+						purchase_uom, stock_uom, item["item_code"]
+					)
+				)
 
 			required_qty = required_qty / conversion_factor
 
@@ -1057,6 +1259,7 @@
 
 		new_mr_items.append(item)
 
+
 @frappe.whitelist()
 def get_item_data(item_code):
 	item_details = get_item_details(item_code)
@@ -1064,33 +1267,39 @@
 	return {
 		"bom_no": item_details.get("bom_no"),
 		"stock_uom": item_details.get("stock_uom")
-#		"description": item_details.get("description")
+		# 		"description": item_details.get("description")
 	}
 
+
 def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, indent=0):
 	data = get_bom_children(parent=bom_no)
 	for d in data:
 		if d.expandable:
 			parent_item_code = frappe.get_cached_value("BOM", bom_no, "item")
 			stock_qty = (d.stock_qty / d.parent_bom_qty) * flt(to_produce_qty)
-			bom_data.append(frappe._dict({
-				'parent_item_code': parent_item_code,
-				'description': d.description,
-				'production_item': d.item_code,
-				'item_name': d.item_name,
-				'stock_uom': d.stock_uom,
-				'uom': d.stock_uom,
-				'bom_no': d.value,
-				'is_sub_contracted_item': d.is_sub_contracted_item,
-				'bom_level': indent,
-				'indent': indent,
-				'stock_qty': stock_qty
-			}))
+			bom_data.append(
+				frappe._dict(
+					{
+						"parent_item_code": parent_item_code,
+						"description": d.description,
+						"production_item": d.item_code,
+						"item_name": d.item_name,
+						"stock_uom": d.stock_uom,
+						"uom": d.stock_uom,
+						"bom_no": d.value,
+						"is_sub_contracted_item": d.is_sub_contracted_item,
+						"bom_level": indent,
+						"indent": indent,
+						"stock_qty": stock_qty,
+					}
+				)
+			)
 
 			if d.value:
-				get_sub_assembly_items(d.value, bom_data, stock_qty, indent=indent+1)
+				get_sub_assembly_items(d.value, bom_data, stock_qty, indent=indent + 1)
+
 
 def set_default_warehouses(row, default_warehouses):
-	for field in ['wip_warehouse', 'fg_warehouse']:
+	for field in ["wip_warehouse", "fg_warehouse"]:
 		if not row.get(field):
 			row[field] = default_warehouses.get(field)
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py b/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py
index e13f042..6fc28a3 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py
@@ -3,15 +3,9 @@
 
 def get_data():
 	return {
-		'fieldname': 'production_plan',
-		'transactions': [
-			{
-				'label': _('Transactions'),
-				'items': ['Work Order', 'Material Request']
-			},
-			{
-				'label': _('Subcontract'),
-				'items': ['Purchase Order']
-			},
-		]
+		"fieldname": "production_plan",
+		"transactions": [
+			{"label": _("Transactions"), "items": ["Work Order", "Material Request"]},
+			{"label": _("Subcontract"), "items": ["Purchase Order"]},
+		],
 	}
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index eeab788..e88049d 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -21,80 +21,88 @@
 
 class TestProductionPlan(FrappeTestCase):
 	def setUp(self):
-		for item in ['Test Production Item 1', 'Subassembly Item 1',
-			'Raw Material Item 1', 'Raw Material Item 2']:
+		for item in [
+			"Test Production Item 1",
+			"Subassembly Item 1",
+			"Raw Material Item 1",
+			"Raw Material Item 2",
+		]:
 			create_item(item, valuation_rate=100)
 
-			sr = frappe.db.get_value('Stock Reconciliation Item',
-				{'item_code': item, 'docstatus': 1}, 'parent')
+			sr = frappe.db.get_value(
+				"Stock Reconciliation Item", {"item_code": item, "docstatus": 1}, "parent"
+			)
 			if sr:
-				sr_doc = frappe.get_doc('Stock Reconciliation', sr)
+				sr_doc = frappe.get_doc("Stock Reconciliation", sr)
 				sr_doc.cancel()
 
-		create_item('Test Non Stock Raw Material', is_stock_item=0)
-		for item, raw_materials in {'Subassembly Item 1': ['Raw Material Item 1', 'Raw Material Item 2'],
-			'Test Production Item 1': ['Raw Material Item 1', 'Subassembly Item 1',
-			'Test Non Stock Raw Material']}.items():
-			if not frappe.db.get_value('BOM', {'item': item}):
-				make_bom(item = item, raw_materials = raw_materials)
+		create_item("Test Non Stock Raw Material", is_stock_item=0)
+		for item, raw_materials in {
+			"Subassembly Item 1": ["Raw Material Item 1", "Raw Material Item 2"],
+			"Test Production Item 1": [
+				"Raw Material Item 1",
+				"Subassembly Item 1",
+				"Test Non Stock Raw Material",
+			],
+		}.items():
+			if not frappe.db.get_value("BOM", {"item": item}):
+				make_bom(item=item, raw_materials=raw_materials)
 
 	def tearDown(self) -> None:
 		frappe.db.rollback()
 
 	def test_production_plan_mr_creation(self):
 		"Test if MRs are created for unavailable raw materials."
-		pln = create_production_plan(item_code='Test Production Item 1')
+		pln = create_production_plan(item_code="Test Production Item 1")
 		self.assertTrue(len(pln.mr_items), 2)
 
 		pln.make_material_request()
 		pln.reload()
-		self.assertTrue(pln.status, 'Material Requested')
+		self.assertTrue(pln.status, "Material Requested")
 
 		material_requests = frappe.get_all(
-			'Material Request Item',
-			fields = ['distinct parent'],
-			filters = {'production_plan': pln.name},
-			as_list=1
+			"Material Request Item",
+			fields=["distinct parent"],
+			filters={"production_plan": pln.name},
+			as_list=1,
 		)
 
 		self.assertTrue(len(material_requests), 2)
 
 		pln.make_work_order()
-		work_orders = frappe.get_all('Work Order', fields = ['name'],
-			filters = {'production_plan': pln.name}, as_list=1)
+		work_orders = frappe.get_all(
+			"Work Order", fields=["name"], filters={"production_plan": pln.name}, as_list=1
+		)
 
 		self.assertTrue(len(work_orders), len(pln.po_items))
 
 		for name in material_requests:
-			mr = frappe.get_doc('Material Request', name[0])
+			mr = frappe.get_doc("Material Request", name[0])
 			if mr.docstatus != 0:
 				mr.cancel()
 
 		for name in work_orders:
-			mr = frappe.delete_doc('Work Order', name[0])
+			mr = frappe.delete_doc("Work Order", name[0])
 
-		pln = frappe.get_doc('Production Plan', pln.name)
+		pln = frappe.get_doc("Production Plan", pln.name)
 		pln.cancel()
 
 	def test_production_plan_start_date(self):
 		"Test if Work Order has same Planned Start Date as Prod Plan."
 		planned_date = add_to_date(date=None, days=3)
 		plan = create_production_plan(
-			item_code='Test Production Item 1',
-			planned_start_date=planned_date
+			item_code="Test Production Item 1", planned_start_date=planned_date
 		)
 		plan.make_work_order()
 
 		work_orders = frappe.get_all(
-			'Work Order',
-			fields = ['name', 'planned_start_date'],
-			filters = {'production_plan': plan.name}
+			"Work Order", fields=["name", "planned_start_date"], filters={"production_plan": plan.name}
 		)
 
 		self.assertEqual(work_orders[0].planned_start_date, planned_date)
 
 		for wo in work_orders:
-			frappe.delete_doc('Work Order', wo.name)
+			frappe.delete_doc("Work Order", wo.name)
 
 		plan.reload()
 		plan.cancel()
@@ -104,15 +112,14 @@
 		- Enable 'ignore_existing_ordered_qty'.
 		- Test if MR Planning table pulls Raw Material Qty even if it is in stock.
 		"""
-		sr1 = create_stock_reconciliation(item_code="Raw Material Item 1",
-			target="_Test Warehouse - _TC", qty=1, rate=110)
-		sr2 = create_stock_reconciliation(item_code="Raw Material Item 2",
-			target="_Test Warehouse - _TC", qty=1, rate=120)
-
-		pln = create_production_plan(
-			item_code='Test Production Item 1',
-			ignore_existing_ordered_qty=1
+		sr1 = create_stock_reconciliation(
+			item_code="Raw Material Item 1", target="_Test Warehouse - _TC", qty=1, rate=110
 		)
+		sr2 = create_stock_reconciliation(
+			item_code="Raw Material Item 2", target="_Test Warehouse - _TC", qty=1, rate=120
+		)
+
+		pln = create_production_plan(item_code="Test Production Item 1", ignore_existing_ordered_qty=1)
 		self.assertTrue(len(pln.mr_items))
 		self.assertTrue(flt(pln.mr_items[0].quantity), 1.0)
 
@@ -122,19 +129,13 @@
 
 	def test_production_plan_with_non_stock_item(self):
 		"Test if MR Planning table includes Non Stock RM."
-		pln = create_production_plan(
-			item_code='Test Production Item 1',
-			include_non_stock_items=1
-		)
+		pln = create_production_plan(item_code="Test Production Item 1", include_non_stock_items=1)
 		self.assertTrue(len(pln.mr_items), 3)
 		pln.cancel()
 
 	def test_production_plan_without_multi_level(self):
 		"Test MR Planning table for non exploded BOM."
-		pln = create_production_plan(
-			item_code='Test Production Item 1',
-			use_multi_level_bom=0
-		)
+		pln = create_production_plan(item_code="Test Production Item 1", use_multi_level_bom=0)
 		self.assertTrue(len(pln.mr_items), 2)
 		pln.cancel()
 
@@ -144,15 +145,15 @@
 		- Test if MR Planning table avoids pulling Raw Material Qty as it is in stock for
 		non exploded BOM.
 		"""
-		sr1 = create_stock_reconciliation(item_code="Raw Material Item 1",
-			target="_Test Warehouse - _TC", qty=1, rate=130)
-		sr2 = create_stock_reconciliation(item_code="Subassembly Item 1",
-			target="_Test Warehouse - _TC", qty=1, rate=140)
+		sr1 = create_stock_reconciliation(
+			item_code="Raw Material Item 1", target="_Test Warehouse - _TC", qty=1, rate=130
+		)
+		sr2 = create_stock_reconciliation(
+			item_code="Subassembly Item 1", target="_Test Warehouse - _TC", qty=1, rate=140
+		)
 
 		pln = create_production_plan(
-			item_code='Test Production Item 1',
-			use_multi_level_bom=0,
-			ignore_existing_ordered_qty=0
+			item_code="Test Production Item 1", use_multi_level_bom=0, ignore_existing_ordered_qty=0
 		)
 		self.assertFalse(len(pln.mr_items))
 
@@ -162,73 +163,86 @@
 
 	def test_production_plan_sales_orders(self):
 		"Test if previously fulfilled SO (with WO) is pulled into Prod Plan."
-		item = 'Test Production Item 1'
+		item = "Test Production Item 1"
 		so = make_sales_order(item_code=item, qty=1)
 		sales_order = so.name
 		sales_order_item = so.items[0].name
 
-		pln = frappe.new_doc('Production Plan')
+		pln = frappe.new_doc("Production Plan")
 		pln.company = so.company
-		pln.get_items_from = 'Sales Order'
+		pln.get_items_from = "Sales Order"
 
-		pln.append('sales_orders', {
-			'sales_order': so.name,
-			'sales_order_date': so.transaction_date,
-			'customer': so.customer,
-			'grand_total': so.grand_total
-		})
+		pln.append(
+			"sales_orders",
+			{
+				"sales_order": so.name,
+				"sales_order_date": so.transaction_date,
+				"customer": so.customer,
+				"grand_total": so.grand_total,
+			},
+		)
 
 		pln.get_so_items()
 		pln.submit()
 		pln.make_work_order()
 
-		work_order = frappe.db.get_value('Work Order', {'sales_order': sales_order,
-			'production_plan': pln.name, 'sales_order_item': sales_order_item}, 'name')
+		work_order = frappe.db.get_value(
+			"Work Order",
+			{"sales_order": sales_order, "production_plan": pln.name, "sales_order_item": sales_order_item},
+			"name",
+		)
 
-		wo_doc = frappe.get_doc('Work Order', work_order)
-		wo_doc.update({
-			'wip_warehouse': 'Work In Progress - _TC',
-			'fg_warehouse': 'Finished Goods - _TC'
-		})
+		wo_doc = frappe.get_doc("Work Order", work_order)
+		wo_doc.update(
+			{"wip_warehouse": "Work In Progress - _TC", "fg_warehouse": "Finished Goods - _TC"}
+		)
 		wo_doc.submit()
 
-		so_wo_qty = frappe.db.get_value('Sales Order Item', sales_order_item, 'work_order_qty')
+		so_wo_qty = frappe.db.get_value("Sales Order Item", sales_order_item, "work_order_qty")
 		self.assertTrue(so_wo_qty, 5)
 
-		pln = frappe.new_doc('Production Plan')
-		pln.update({
-			'from_date': so.transaction_date,
-			'to_date': so.transaction_date,
-			'customer': so.customer,
-			'item_code': item,
-			'sales_order_status': so.status
-		})
+		pln = frappe.new_doc("Production Plan")
+		pln.update(
+			{
+				"from_date": so.transaction_date,
+				"to_date": so.transaction_date,
+				"customer": so.customer,
+				"item_code": item,
+				"sales_order_status": so.status,
+			}
+		)
 		sales_orders = get_sales_orders(pln) or {}
-		sales_orders = [d.get('name') for d in sales_orders if d.get('name') == sales_order]
+		sales_orders = [d.get("name") for d in sales_orders if d.get("name") == sales_order]
 
 		self.assertEqual(sales_orders, [])
 
 	def test_production_plan_combine_items(self):
 		"Test combining FG items in Production Plan."
-		item = 'Test Production Item 1'
+		item = "Test Production Item 1"
 		so1 = make_sales_order(item_code=item, qty=1)
 
-		pln = frappe.new_doc('Production Plan')
+		pln = frappe.new_doc("Production Plan")
 		pln.company = so1.company
-		pln.get_items_from = 'Sales Order'
-		pln.append('sales_orders', {
-			'sales_order': so1.name,
-			'sales_order_date': so1.transaction_date,
-			'customer': so1.customer,
-			'grand_total': so1.grand_total
-		})
+		pln.get_items_from = "Sales Order"
+		pln.append(
+			"sales_orders",
+			{
+				"sales_order": so1.name,
+				"sales_order_date": so1.transaction_date,
+				"customer": so1.customer,
+				"grand_total": so1.grand_total,
+			},
+		)
 		so2 = make_sales_order(item_code=item, qty=2)
-		pln.append('sales_orders', {
-			'sales_order': so2.name,
-			'sales_order_date': so2.transaction_date,
-			'customer': so2.customer,
-			'grand_total': so2.grand_total
-		})
+		pln.append(
+			"sales_orders",
+			{
+				"sales_order": so2.name,
+				"sales_order_date": so2.transaction_date,
+				"customer": so2.customer,
+				"grand_total": so2.grand_total,
+			},
+		)
 		pln.combine_items = 1
 		pln.get_items()
 		pln.submit()
@@ -236,26 +250,31 @@
 		self.assertTrue(pln.po_items[0].planned_qty, 3)
 
 		pln.make_work_order()
-		work_order = frappe.db.get_value('Work Order', {
-			'production_plan_item': pln.po_items[0].name,
-			'production_plan': pln.name
-		}, 'name')
+		work_order = frappe.db.get_value(
+			"Work Order",
+			{"production_plan_item": pln.po_items[0].name, "production_plan": pln.name},
+			"name",
+		)
 
-		wo_doc = frappe.get_doc('Work Order', work_order)
-		wo_doc.update({
-			'wip_warehouse': 'Work In Progress - _TC',
-		})
+		wo_doc = frappe.get_doc("Work Order", work_order)
+		wo_doc.update(
+			{
+				"wip_warehouse": "Work In Progress - _TC",
+			}
+		)
 
 		wo_doc.submit()
 		so_items = []
 		for plan_reference in pln.prod_plan_references:
 			so_items.append(plan_reference.sales_order_item)
-			so_wo_qty = frappe.db.get_value('Sales Order Item', plan_reference.sales_order_item, 'work_order_qty')
+			so_wo_qty = frappe.db.get_value(
+				"Sales Order Item", plan_reference.sales_order_item, "work_order_qty"
+			)
 			self.assertEqual(so_wo_qty, plan_reference.qty)
 
 		wo_doc.cancel()
 		for so_item in so_items:
-			so_wo_qty = frappe.db.get_value('Sales Order Item', so_item, 'work_order_qty')
+			so_wo_qty = frappe.db.get_value("Sales Order Item", so_item, "work_order_qty")
 			self.assertEqual(so_wo_qty, 0.0)
 
 		pln.reload()
@@ -269,12 +288,8 @@
 		"""
 		from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
 
-		bom_tree_1 = {
-			"Red-Car": {"Wheel": {"Rubber": {}}}
-		}
-		bom_tree_2 = {
-			"Green-Car": {"Wheel": {"Rubber": {}}}
-		}
+		bom_tree_1 = {"Red-Car": {"Wheel": {"Rubber": {}}}}
+		bom_tree_2 = {"Green-Car": {"Wheel": {"Rubber": {}}}}
 
 		parent_bom_1 = create_nested_bom(bom_tree_1, prefix="")
 		parent_bom_2 = create_nested_bom(bom_tree_2, prefix="")
@@ -284,20 +299,23 @@
 		frappe.db.set_value("BOM Item", parent_bom_2.items[0].name, "bom_no", subassembly_bom)
 
 		plan = create_production_plan(item_code="Red-Car", use_multi_level_bom=1, do_not_save=True)
-		plan.append("po_items", { # Add Green-Car to Prod Plan
-			'use_multi_level_bom': 1,
-			'item_code': "Green-Car",
-			'bom_no': frappe.db.get_value('Item', "Green-Car", 'default_bom'),
-			'planned_qty': 1,
-			'planned_start_date': now_datetime()
-		})
+		plan.append(
+			"po_items",
+			{  # Add Green-Car to Prod Plan
+				"use_multi_level_bom": 1,
+				"item_code": "Green-Car",
+				"bom_no": frappe.db.get_value("Item", "Green-Car", "default_bom"),
+				"planned_qty": 1,
+				"planned_start_date": now_datetime(),
+			},
+		)
 		plan.get_sub_assembly_items()
 		self.assertTrue(len(plan.sub_assembly_items), 2)
 
 		plan.combine_sub_items = 1
 		plan.get_sub_assembly_items()
 
-		self.assertTrue(len(plan.sub_assembly_items), 1) # check if sub-assembly items merged
+		self.assertTrue(len(plan.sub_assembly_items), 1)  # check if sub-assembly items merged
 		self.assertEqual(plan.sub_assembly_items[0].qty, 2.0)
 		self.assertEqual(plan.sub_assembly_items[0].stock_qty, 2.0)
 
@@ -307,25 +325,29 @@
 		self.assertTrue(len(plan.sub_assembly_items), 2)
 
 	def test_pp_to_mr_customer_provided(self):
-		" Test Material Request from Production Plan for Customer Provided Item."
-		create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
-		create_item('Production Item CUST')
+		"Test Material Request from Production Plan for Customer Provided Item."
+		create_item(
+			"CUST-0987", is_customer_provided_item=1, customer="_Test Customer", is_purchase_item=0
+		)
+		create_item("Production Item CUST")
 
-		for item, raw_materials in {'Production Item CUST': ['Raw Material Item 1', 'CUST-0987']}.items():
-			if not frappe.db.get_value('BOM', {'item': item}):
-				make_bom(item = item, raw_materials = raw_materials)
-		production_plan = create_production_plan(item_code = 'Production Item CUST')
+		for item, raw_materials in {
+			"Production Item CUST": ["Raw Material Item 1", "CUST-0987"]
+		}.items():
+			if not frappe.db.get_value("BOM", {"item": item}):
+				make_bom(item=item, raw_materials=raw_materials)
+		production_plan = create_production_plan(item_code="Production Item CUST")
 		production_plan.make_material_request()
 
 		material_request = frappe.db.get_value(
-			'Material Request Item',
-			{'production_plan': production_plan.name, 'item_code': 'CUST-0987'},
-			'parent'
+			"Material Request Item",
+			{"production_plan": production_plan.name, "item_code": "CUST-0987"},
+			"parent",
 		)
-		mr = frappe.get_doc('Material Request', material_request)
+		mr = frappe.get_doc("Material Request", material_request)
 
-		self.assertTrue(mr.material_request_type, 'Customer Provided')
-		self.assertTrue(mr.customer, '_Test Customer')
+		self.assertTrue(mr.material_request_type, "Customer Provided")
+		self.assertTrue(mr.customer, "_Test Customer")
 
 	def test_production_plan_with_multi_level_bom(self):
 		"""
@@ -339,33 +361,34 @@
 			create_item(item_code, is_stock_item=1)
 
 		# created bom upto 3 level
-		if not frappe.db.get_value('BOM', {'item': "Test BOM 3"}):
-			make_bom(item = "Test BOM 3", raw_materials = ["Test RM BOM 1"], rm_qty=3)
+		if not frappe.db.get_value("BOM", {"item": "Test BOM 3"}):
+			make_bom(item="Test BOM 3", raw_materials=["Test RM BOM 1"], rm_qty=3)
 
-		if not frappe.db.get_value('BOM', {'item': "Test BOM 2"}):
-			make_bom(item = "Test BOM 2", raw_materials = ["Test BOM 3"], rm_qty=3)
+		if not frappe.db.get_value("BOM", {"item": "Test BOM 2"}):
+			make_bom(item="Test BOM 2", raw_materials=["Test BOM 3"], rm_qty=3)
 
-		if not frappe.db.get_value('BOM', {'item': "Test BOM 1"}):
-			make_bom(item = "Test BOM 1", raw_materials = ["Test BOM 2"], rm_qty=2)
+		if not frappe.db.get_value("BOM", {"item": "Test BOM 1"}):
+			make_bom(item="Test BOM 1", raw_materials=["Test BOM 2"], rm_qty=2)
 
 		item_code = "Test BOM 1"
-		pln = frappe.new_doc('Production Plan')
+		pln = frappe.new_doc("Production Plan")
 		pln.company = "_Test Company"
-		pln.append("po_items", {
-			"item_code": item_code,
-			"bom_no": frappe.db.get_value('BOM', {'item': "Test BOM 1"}),
-			"planned_qty": 3
-		})
+		pln.append(
+			"po_items",
+			{
+				"item_code": item_code,
+				"bom_no": frappe.db.get_value("BOM", {"item": "Test BOM 1"}),
+				"planned_qty": 3,
+			},
+		)
 
-		pln.get_sub_assembly_items('In House')
+		pln.get_sub_assembly_items("In House")
 		pln.submit()
 		pln.make_work_order()
 
-		#last level sub-assembly work order produce qty
+		# last level sub-assembly work order produce qty
 		to_produce_qty = frappe.db.get_value(
-			"Work Order",
-			{"production_plan": pln.name, "production_item": "Test BOM 3"},
-			"qty"
+			"Work Order", {"production_plan": pln.name, "production_item": "Test BOM 3"}, "qty"
 		)
 
 		self.assertEqual(to_produce_qty, 18.0)
@@ -374,70 +397,72 @@
 
 	def test_get_warehouse_list_group(self):
 		"Check if required child warehouses are returned."
-		warehouse_json = '[{\"warehouse\":\"_Test Warehouse Group - _TC\"}]'
+		warehouse_json = '[{"warehouse":"_Test Warehouse Group - _TC"}]'
 
 		warehouses = set(get_warehouse_list(warehouse_json))
 		expected_warehouses = {"_Test Warehouse Group-C1 - _TC", "_Test Warehouse Group-C2 - _TC"}
 
 		missing_warehouse = expected_warehouses - warehouses
 
-		self.assertTrue(len(missing_warehouse) == 0,
-				msg=f"Following warehouses were expected {', '.join(missing_warehouse)}")
+		self.assertTrue(
+			len(missing_warehouse) == 0,
+			msg=f"Following warehouses were expected {', '.join(missing_warehouse)}",
+		)
 
 	def test_get_warehouse_list_single(self):
 		"Check if same warehouse is returned in absence of child warehouses."
-		warehouse_json = '[{\"warehouse\":\"_Test Scrap Warehouse - _TC\"}]'
+		warehouse_json = '[{"warehouse":"_Test Scrap Warehouse - _TC"}]'
 
 		warehouses = set(get_warehouse_list(warehouse_json))
-		expected_warehouses = {"_Test Scrap Warehouse - _TC", }
+		expected_warehouses = {
+			"_Test Scrap Warehouse - _TC",
+		}
 
 		self.assertEqual(warehouses, expected_warehouses)
 
 	def test_get_sales_order_with_variant(self):
 		"Check if Template BOM is fetched in absence of Variant BOM."
-		rm_item = create_item('PIV_RM', valuation_rate = 100)
-		if not frappe.db.exists('Item', {"item_code": 'PIV'}):
-			item = create_item('PIV', valuation_rate = 100)
+		rm_item = create_item("PIV_RM", valuation_rate=100)
+		if not frappe.db.exists("Item", {"item_code": "PIV"}):
+			item = create_item("PIV", valuation_rate=100)
 			variant_settings = {
 				"attributes": [
-					{
-						"attribute": "Colour"
-					},
+					{"attribute": "Colour"},
 				],
-				"has_variants": 1
+				"has_variants": 1,
 			}
 			item.update(variant_settings)
 			item.save()
-			parent_bom = make_bom(item = 'PIV', raw_materials = [rm_item.item_code])
-		if not frappe.db.exists('BOM', {"item": 'PIV'}):
-			parent_bom = make_bom(item = 'PIV', raw_materials = [rm_item.item_code])
+			parent_bom = make_bom(item="PIV", raw_materials=[rm_item.item_code])
+		if not frappe.db.exists("BOM", {"item": "PIV"}):
+			parent_bom = make_bom(item="PIV", raw_materials=[rm_item.item_code])
 		else:
-			parent_bom = frappe.get_doc('BOM', {"item": 'PIV'})
+			parent_bom = frappe.get_doc("BOM", {"item": "PIV"})
 
-		if not frappe.db.exists('Item', {"item_code": 'PIV-RED'}):
+		if not frappe.db.exists("Item", {"item_code": "PIV-RED"}):
 			variant = create_variant("PIV", {"Colour": "Red"})
 			variant.save()
-			variant_bom = make_bom(item = variant.item_code, raw_materials = [rm_item.item_code])
+			variant_bom = make_bom(item=variant.item_code, raw_materials=[rm_item.item_code])
 		else:
-			variant = frappe.get_doc('Item', 'PIV-RED')
-		if not frappe.db.exists('BOM', {"item": 'PIV-RED'}):
-			variant_bom = make_bom(item = variant.item_code, raw_materials = [rm_item.item_code])
+			variant = frappe.get_doc("Item", "PIV-RED")
+		if not frappe.db.exists("BOM", {"item": "PIV-RED"}):
+			variant_bom = make_bom(item=variant.item_code, raw_materials=[rm_item.item_code])
 
 		"""Testing when item variant has a BOM"""
 		so = make_sales_order(item_code="PIV-RED", qty=5)
-		pln = frappe.new_doc('Production Plan')
+		pln = frappe.new_doc("Production Plan")
 		pln.company = so.company
-		pln.get_items_from = 'Sales Order'
-		pln.item_code = 'PIV-RED'
+		pln.get_items_from = "Sales Order"
+		pln.item_code = "PIV-RED"
 		pln.get_open_sales_orders()
 		self.assertEqual(pln.sales_orders[0].sales_order, so.name)
 		pln.get_so_items()
-		self.assertEqual(pln.po_items[0].item_code, 'PIV-RED')
+		self.assertEqual(pln.po_items[0].item_code, "PIV-RED")
 		self.assertEqual(pln.po_items[0].bom_no, variant_bom.name)
 		so.cancel()
-		frappe.delete_doc('Sales Order', so.name)
+		frappe.delete_doc("Sales Order", so.name)
 		variant_bom.cancel()
-		frappe.delete_doc('BOM', variant_bom.name)
+		frappe.delete_doc("BOM", variant_bom.name)
 
 		"""Testing when item variant doesn't have a BOM"""
 		so = make_sales_order(item_code="PIV-RED", qty=5)
@@ -445,7 +470,7 @@
 		self.assertEqual(pln.sales_orders[0].sales_order, so.name)
 		pln.po_items = []
 		pln.get_so_items()
-		self.assertEqual(pln.po_items[0].item_code, 'PIV-RED')
+		self.assertEqual(pln.po_items[0].item_code, "PIV-RED")
 		self.assertEqual(pln.po_items[0].bom_no, parent_bom.name)
 
 		frappe.db.rollback()
@@ -457,27 +482,35 @@
 		prefix = "_TestLevel_"
 		boms = {
 			"Assembly": {
-				"SubAssembly1": {"ChildPart1": {}, "ChildPart2": {},},
+				"SubAssembly1": {
+					"ChildPart1": {},
+					"ChildPart2": {},
+				},
 				"ChildPart6": {},
 				"SubAssembly4": {"SubSubAssy2": {"ChildPart7": {}}},
 			},
 			"MegaDeepAssy": {
-				"SecretSubassy": {"SecretPart": {"VerySecret" : { "SuperSecret": {"Classified": {}}}},},
-																# ^ assert that this is
-																# first item in subassy table
-			}
+				"SecretSubassy": {
+					"SecretPart": {"VerySecret": {"SuperSecret": {"Classified": {}}}},
+				},
+				# ^ assert that this is
+				# first item in subassy table
+			},
 		}
 		create_nested_bom(boms, prefix=prefix)
 
 		items = [prefix + item_code for item_code in boms.keys()]
 		plan = create_production_plan(item_code=items[0], do_not_save=True)
-		plan.append("po_items", {
-			'use_multi_level_bom': 1,
-			'item_code': items[1],
-			'bom_no': frappe.db.get_value('Item', items[1], 'default_bom'),
-			'planned_qty': 1,
-			'planned_start_date': now_datetime()
-		})
+		plan.append(
+			"po_items",
+			{
+				"use_multi_level_bom": 1,
+				"item_code": items[1],
+				"bom_no": frappe.db.get_value("Item", items[1], "default_bom"),
+				"planned_qty": 1,
+				"planned_start_date": now_datetime(),
+			},
+		)
 		plan.get_sub_assembly_items()
 
 		bom_level_order = [d.bom_level for d in plan.sub_assembly_items]
@@ -487,6 +520,7 @@
 
 	def test_multiple_work_order_for_production_plan_item(self):
 		"Test producing Prod Plan (making WO) in parts."
+
 		def create_work_order(item, pln, qty):
 			# Get Production Items
 			items_data = pln.get_production_items()
@@ -497,14 +531,13 @@
 			# Create and Submit Work Order for each item in items_data
 			for key, item in items_data.items():
 				if pln.sub_assembly_items:
-					item['use_multi_level_bom'] = 0
+					item["use_multi_level_bom"] = 0
 
 				wo_name = pln.create_work_order(item)
 				wo_doc = frappe.get_doc("Work Order", wo_name)
-				wo_doc.update({
-					'wip_warehouse': 'Work In Progress - _TC',
-					'fg_warehouse': 'Finished Goods - _TC'
-				})
+				wo_doc.update(
+					{"wip_warehouse": "Work In Progress - _TC", "fg_warehouse": "Finished Goods - _TC"}
+				)
 				wo_doc.submit()
 				wo_list.append(wo_name)
 
@@ -554,34 +587,30 @@
 			make_stock_entry as make_se_from_wo,
 		)
 
-		make_stock_entry(item_code="Raw Material Item 1",
-			target="Work In Progress - _TC",
-			qty=2, basic_rate=100
+		make_stock_entry(
+			item_code="Raw Material Item 1", target="Work In Progress - _TC", qty=2, basic_rate=100
 		)
-		make_stock_entry(item_code="Raw Material Item 2",
-			target="Work In Progress - _TC",
-			qty=2, basic_rate=100
+		make_stock_entry(
+			item_code="Raw Material Item 2", target="Work In Progress - _TC", qty=2, basic_rate=100
 		)
 
-		item = 'Test Production Item 1'
+		item = "Test Production Item 1"
 		so = make_sales_order(item_code=item, qty=1)
 
 		pln = create_production_plan(
-			company=so.company,
-			get_items_from="Sales Order",
-			sales_order=so,
-			skip_getting_mr_items=True
+			company=so.company, get_items_from="Sales Order", sales_order=so, skip_getting_mr_items=True
 		)
 		self.assertEqual(pln.po_items[0].pending_qty, 1)
 
 		wo = make_wo_order_test_record(
-			item_code=item, qty=1,
+			item_code=item,
+			qty=1,
 			company=so.company,
-			wip_warehouse='Work In Progress - _TC',
-			fg_warehouse='Finished Goods - _TC',
+			wip_warehouse="Work In Progress - _TC",
+			fg_warehouse="Finished Goods - _TC",
 			skip_transfer=1,
 			use_multi_level_bom=1,
-			do_not_submit=True
+			do_not_submit=True,
 		)
 		wo.production_plan = pln.name
 		wo.production_plan_item = pln.po_items[0].name
@@ -604,29 +633,25 @@
 			make_stock_entry as make_se_from_wo,
 		)
 
-		make_stock_entry(item_code="Raw Material Item 1",
-			target="Work In Progress - _TC",
-			qty=2, basic_rate=100
+		make_stock_entry(
+			item_code="Raw Material Item 1", target="Work In Progress - _TC", qty=2, basic_rate=100
 		)
-		make_stock_entry(item_code="Raw Material Item 2",
-			target="Work In Progress - _TC",
-			qty=2, basic_rate=100
+		make_stock_entry(
+			item_code="Raw Material Item 2", target="Work In Progress - _TC", qty=2, basic_rate=100
 		)
 
-		pln = create_production_plan(
-			item_code='Test Production Item 1',
-			skip_getting_mr_items=True
-		)
+		pln = create_production_plan(item_code="Test Production Item 1", skip_getting_mr_items=True)
 		self.assertEqual(pln.po_items[0].pending_qty, 1)
 
 		wo = make_wo_order_test_record(
-			item_code='Test Production Item 1', qty=1,
+			item_code="Test Production Item 1",
+			qty=1,
 			company=pln.company,
-			wip_warehouse='Work In Progress - _TC',
-			fg_warehouse='Finished Goods - _TC',
+			wip_warehouse="Work In Progress - _TC",
+			fg_warehouse="Finished Goods - _TC",
 			skip_transfer=1,
 			use_multi_level_bom=1,
-			do_not_submit=True
+			do_not_submit=True,
 		)
 		wo.production_plan = pln.name
 		wo.production_plan_item = pln.po_items[0].name
@@ -644,17 +669,57 @@
 
 	def test_qty_based_status(self):
 		pp = frappe.new_doc("Production Plan")
-		pp.po_items = [
-			frappe._dict(planned_qty=5, produce_qty=4)
-		]
+		pp.po_items = [frappe._dict(planned_qty=5, produce_qty=4)]
 		self.assertFalse(pp.all_items_completed())
 
 		pp.po_items = [
 			frappe._dict(planned_qty=5, produce_qty=10),
-			frappe._dict(planned_qty=5, produce_qty=4)
+			frappe._dict(planned_qty=5, produce_qty=4),
 		]
 		self.assertFalse(pp.all_items_completed())
 
+	def test_production_plan_planned_qty(self):
+		pln = create_production_plan(item_code="_Test FG Item", planned_qty=0.55)
+		pln.make_work_order()
+		work_order = frappe.db.get_value("Work Order", {"production_plan": pln.name}, "name")
+		wo_doc = frappe.get_doc("Work Order", work_order)
+		wo_doc.update(
+			{"wip_warehouse": "Work In Progress - _TC", "fg_warehouse": "Finished Goods - _TC"}
+		)
+		wo_doc.submit()
+		self.assertEqual(wo_doc.qty, 0.55)
+
+	def test_temporary_name_relinking(self):
+
+		pp = frappe.new_doc("Production Plan")
+
+		# this can not be unittested so mocking data that would be expected
+		# from client side.
+		for _ in range(10):
+			po_item = pp.append(
+				"po_items",
+				{
+					"name": frappe.generate_hash(length=10),
+					"temporary_name": frappe.generate_hash(length=10),
+				},
+			)
+			pp.append("sub_assembly_items", {"production_plan_item": po_item.temporary_name})
+		pp._rename_temporary_references()
+
+		for po_item, subassy_item in zip(pp.po_items, pp.sub_assembly_items):
+			self.assertEqual(po_item.name, subassy_item.production_plan_item)
+
+		# bad links should be erased
+		pp.append("sub_assembly_items", {"production_plan_item": frappe.generate_hash(length=16)})
+		pp._rename_temporary_references()
+		self.assertIsNone(pp.sub_assembly_items[-1].production_plan_item)
+		pp.sub_assembly_items.pop()
+
+		# reattempting on same doc shouldn't change anything
+		pp._rename_temporary_references()
+		for po_item, subassy_item in zip(pp.po_items, pp.sub_assembly_items):
+			self.assertEqual(po_item.name, subassy_item.production_plan_item)
+
 
 def create_production_plan(**args):
 	"""
@@ -664,40 +729,48 @@
 	"""
 	args = frappe._dict(args)
 
-	pln = frappe.get_doc({
-		'doctype': 'Production Plan',
-		'company': args.company or '_Test Company',
-		'customer': args.customer or '_Test Customer',
-		'posting_date': nowdate(),
-		'include_non_stock_items': args.include_non_stock_items or 0,
-		'include_subcontracted_items': args.include_subcontracted_items or 0,
-		'ignore_existing_ordered_qty': args.ignore_existing_ordered_qty or 0,
-		'get_items_from': 'Sales Order'
-	})
+	pln = frappe.get_doc(
+		{
+			"doctype": "Production Plan",
+			"company": args.company or "_Test Company",
+			"customer": args.customer or "_Test Customer",
+			"posting_date": nowdate(),
+			"include_non_stock_items": args.include_non_stock_items or 0,
+			"include_subcontracted_items": args.include_subcontracted_items or 0,
+			"ignore_existing_ordered_qty": args.ignore_existing_ordered_qty or 0,
+			"get_items_from": "Sales Order",
+		}
+	)
 
 	if not args.get("sales_order"):
-		pln.append('po_items', {
-			'use_multi_level_bom': args.use_multi_level_bom or 1,
-			'item_code': args.item_code,
-			'bom_no': frappe.db.get_value('Item', args.item_code, 'default_bom'),
-			'planned_qty': args.planned_qty or 1,
-			'planned_start_date': args.planned_start_date or now_datetime()
-		})
+		pln.append(
+			"po_items",
+			{
+				"use_multi_level_bom": args.use_multi_level_bom or 1,
+				"item_code": args.item_code,
+				"bom_no": frappe.db.get_value("Item", args.item_code, "default_bom"),
+				"planned_qty": args.planned_qty or 1,
+				"planned_start_date": args.planned_start_date or now_datetime(),
+			},
+		)
 
 	if args.get("get_items_from") == "Sales Order" and args.get("sales_order"):
 		so = args.get("sales_order")
-		pln.append('sales_orders', {
-			'sales_order': so.name,
-			'sales_order_date': so.transaction_date,
-			'customer': so.customer,
-			'grand_total': so.grand_total
-		})
+		pln.append(
+			"sales_orders",
+			{
+				"sales_order": so.name,
+				"sales_order_date": so.transaction_date,
+				"customer": so.customer,
+				"grand_total": so.grand_total,
+			},
+		)
 		pln.get_items()
 
 	if not args.get("skip_getting_mr_items"):
 		mr_items = get_items_for_material_requests(pln.as_dict())
 		for d in mr_items:
-			pln.append('mr_items', d)
+			pln.append("mr_items", d)
 
 	if not args.do_not_save:
 		pln.insert()
@@ -706,31 +779,36 @@
 
 	return pln
 
+
 def make_bom(**args):
 	args = frappe._dict(args)
 
-	bom = frappe.get_doc({
-		'doctype': 'BOM',
-		'is_default': 1,
-		'item': args.item,
-		'currency': args.currency or 'USD',
-		'quantity': args.quantity or 1,
-		'company': args.company or '_Test Company',
-		'routing': args.routing,
-		'with_operations': args.with_operations or 0
-	})
+	bom = frappe.get_doc(
+		{
+			"doctype": "BOM",
+			"is_default": 1,
+			"item": args.item,
+			"currency": args.currency or "USD",
+			"quantity": args.quantity or 1,
+			"company": args.company or "_Test Company",
+			"routing": args.routing,
+			"with_operations": args.with_operations or 0,
+		}
+	)
 
 	for item in args.raw_materials:
-		item_doc = frappe.get_doc('Item', item)
-
-		bom.append('items', {
-			'item_code': item,
-			'qty': args.rm_qty or 1.0,
-			'uom': item_doc.stock_uom,
-			'stock_uom': item_doc.stock_uom,
-			'rate': item_doc.valuation_rate or args.rate,
-			'source_warehouse': args.source_warehouse
-		})
+		item_doc = frappe.get_doc("Item", item)
+		bom.append(
+			"items",
+			{
+				"item_code": item,
+				"qty": args.rm_qty or 1.0,
+				"uom": item_doc.stock_uom,
+				"stock_uom": item_doc.stock_uom,
+				"rate": item_doc.valuation_rate or args.rate,
+				"source_warehouse": args.source_warehouse,
+			},
+		)
 
 	if not args.do_not_save:
 		bom.insert(ignore_permissions=True)
diff --git a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json
index f829d57..df5862f 100644
--- a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json
+++ b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json
@@ -27,7 +27,8 @@
   "material_request",
   "material_request_item",
   "product_bundle_item",
-  "item_reference"
+  "item_reference",
+  "temporary_name"
  ],
  "fields": [
   {
@@ -204,17 +205,25 @@
    "fieldtype": "Data",
    "hidden": 1,
    "label": "Item Reference"
+  },
+  {
+   "fieldname": "temporary_name",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "temporary name"
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-06-28 18:31:06.822168",
+ "modified": "2022-03-24 04:54:09.940224",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Production Plan Item",
+ "naming_rule": "Random",
  "owner": "Administrator",
  "permissions": [],
  "sort_field": "modified",
- "sort_order": "ASC"
+ "sort_order": "ASC",
+ "states": []
 }
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/routing/routing.py b/erpnext/manufacturing/doctype/routing/routing.py
index b207906..d4c37cf 100644
--- a/erpnext/manufacturing/doctype/routing/routing.py
+++ b/erpnext/manufacturing/doctype/routing/routing.py
@@ -19,9 +19,11 @@
 	def calculate_operating_cost(self):
 		for operation in self.operations:
 			if not operation.hour_rate:
-				operation.hour_rate = frappe.db.get_value("Workstation", operation.workstation, 'hour_rate')
-			operation.operating_cost = flt(flt(operation.hour_rate) * flt(operation.time_in_mins) / 60,
-					operation.precision("operating_cost"))
+				operation.hour_rate = frappe.db.get_value("Workstation", operation.workstation, "hour_rate")
+			operation.operating_cost = flt(
+				flt(operation.hour_rate) * flt(operation.time_in_mins) / 60,
+				operation.precision("operating_cost"),
+			)
 
 	def set_routing_id(self):
 		sequence_id = 0
@@ -29,7 +31,10 @@
 			if not row.sequence_id:
 				row.sequence_id = sequence_id + 1
 			elif sequence_id and row.sequence_id and cint(sequence_id) > cint(row.sequence_id):
-				frappe.throw(_("At row #{0}: the sequence id {1} cannot be less than previous row sequence id {2}")
-					.format(row.idx, row.sequence_id, sequence_id))
+				frappe.throw(
+					_("At row #{0}: the sequence id {1} cannot be less than previous row sequence id {2}").format(
+						row.idx, row.sequence_id, sequence_id
+					)
+				)
 
 			sequence_id = row.sequence_id
diff --git a/erpnext/manufacturing/doctype/routing/routing_dashboard.py b/erpnext/manufacturing/doctype/routing/routing_dashboard.py
index d051e38..65d7a45 100644
--- a/erpnext/manufacturing/doctype/routing/routing_dashboard.py
+++ b/erpnext/manufacturing/doctype/routing/routing_dashboard.py
@@ -1,9 +1,2 @@
 def get_data():
-	return {
-		'fieldname': 'routing',
-		'transactions': [
-			{
-				'items': ['BOM']
-			}
-		]
-	}
+	return {"fieldname": "routing", "transactions": [{"items": ["BOM"]}]}
diff --git a/erpnext/manufacturing/doctype/routing/test_routing.py b/erpnext/manufacturing/doctype/routing/test_routing.py
index 696d9bc..48f1851 100644
--- a/erpnext/manufacturing/doctype/routing/test_routing.py
+++ b/erpnext/manufacturing/doctype/routing/test_routing.py
@@ -16,24 +16,27 @@
 
 	@classmethod
 	def tearDownClass(cls):
-		frappe.db.sql('delete from tabBOM where item=%s', cls.item_code)
+		frappe.db.sql("delete from tabBOM where item=%s", cls.item_code)
 
 	def test_sequence_id(self):
-		operations = [{"operation": "Test Operation A", "workstation": "Test Workstation A", "time_in_mins": 30},
-			{"operation": "Test Operation B", "workstation": "Test Workstation A", "time_in_mins": 20}]
+		operations = [
+			{"operation": "Test Operation A", "workstation": "Test Workstation A", "time_in_mins": 30},
+			{"operation": "Test Operation B", "workstation": "Test Workstation A", "time_in_mins": 20},
+		]
 
 		make_test_records("UOM")
 
 		setup_operations(operations)
 		routing_doc = create_routing(routing_name="Testing Route", operations=operations)
 		bom_doc = setup_bom(item_code=self.item_code, routing=routing_doc.name)
-		wo_doc = make_wo_order_test_record(production_item = self.item_code, bom_no=bom_doc.name)
+		wo_doc = make_wo_order_test_record(production_item=self.item_code, bom_no=bom_doc.name)
 
 		for row in routing_doc.operations:
 			self.assertEqual(row.sequence_id, row.idx)
 
-		for data in frappe.get_all("Job Card",
-			filters={"work_order": wo_doc.name}, order_by="sequence_id desc"):
+		for data in frappe.get_all(
+			"Job Card", filters={"work_order": wo_doc.name}, order_by="sequence_id desc"
+		):
 			job_card_doc = frappe.get_doc("Job Card", data.name)
 			job_card_doc.time_logs[0].completed_qty = 10
 			if job_card_doc.sequence_id != 1:
@@ -52,33 +55,25 @@
 				"operation": "Test Operation A",
 				"workstation": "_Test Workstation A",
 				"hour_rate_rent": 300,
-				"hour_rate_labour": 750 ,
-				"time_in_mins": 30
+				"hour_rate_labour": 750,
+				"time_in_mins": 30,
 			},
 			{
 				"operation": "Test Operation B",
 				"workstation": "_Test Workstation B",
 				"hour_rate_labour": 200,
 				"hour_rate_rent": 1000,
-				"time_in_mins": 20
-			}
+				"time_in_mins": 20,
+			},
 		]
 
 		test_routing_operations = [
-			{
-				"operation": "Test Operation A",
-				"workstation": "_Test Workstation A",
-				"time_in_mins": 30
-			},
-			{
-				"operation": "Test Operation B",
-				"workstation": "_Test Workstation A",
-				"time_in_mins": 20
-			}
+			{"operation": "Test Operation A", "workstation": "_Test Workstation A", "time_in_mins": 30},
+			{"operation": "Test Operation B", "workstation": "_Test Workstation A", "time_in_mins": 20},
 		]
 		setup_operations(operations)
 		routing_doc = create_routing(routing_name="Routing Test", operations=test_routing_operations)
-		bom_doc = setup_bom(item_code="_Testing Item", routing=routing_doc.name, currency = 'INR')
+		bom_doc = setup_bom(item_code="_Testing Item", routing=routing_doc.name, currency="INR")
 		self.assertEqual(routing_doc.operations[0].time_in_mins, 30)
 		self.assertEqual(routing_doc.operations[1].time_in_mins, 20)
 		routing_doc.operations[0].time_in_mins = 90
@@ -93,10 +88,12 @@
 def setup_operations(rows):
 	from erpnext.manufacturing.doctype.operation.test_operation import make_operation
 	from erpnext.manufacturing.doctype.workstation.test_workstation import make_workstation
+
 	for row in rows:
 		make_workstation(row)
 		make_operation(row)
 
+
 def create_routing(**args):
 	args = frappe._dict(args)
 
@@ -108,7 +105,7 @@
 			doc.insert()
 		except frappe.DuplicateEntryError:
 			doc = frappe.get_doc("Routing", args.routing_name)
-			doc.delete_key('operations')
+			doc.delete_key("operations")
 			for operation in args.operations:
 				doc.append("operations", operation)
 
@@ -116,28 +113,35 @@
 
 	return doc
 
+
 def setup_bom(**args):
 	from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
 
 	args = frappe._dict(args)
 
-	if not frappe.db.exists('Item', args.item_code):
-		make_item(args.item_code, {
-			'is_stock_item': 1
-		})
+	if not frappe.db.exists("Item", args.item_code):
+		make_item(args.item_code, {"is_stock_item": 1})
 
 	if not args.raw_materials:
-		if not frappe.db.exists('Item', "Test Extra Item N-1"):
-			make_item("Test Extra Item N-1", {
-				'is_stock_item': 1,
-			})
+		if not frappe.db.exists("Item", "Test Extra Item N-1"):
+			make_item(
+				"Test Extra Item N-1",
+				{
+					"is_stock_item": 1,
+				},
+			)
 
-		args.raw_materials = ['Test Extra Item N-1']
+		args.raw_materials = ["Test Extra Item N-1"]
 
-	name = frappe.db.get_value('BOM', {'item': args.item_code}, 'name')
+	name = frappe.db.get_value("BOM", {"item": args.item_code}, "name")
 	if not name:
-		bom_doc = make_bom(item = args.item_code, raw_materials = args.get("raw_materials"),
-			routing = args.routing, with_operations=1, currency = args.currency)
+		bom_doc = make_bom(
+			item=args.item_code,
+			raw_materials=args.get("raw_materials"),
+			routing=args.routing,
+			with_operations=1,
+			currency=args.currency,
+		)
 	else:
 		bom_doc = frappe.get_doc("BOM", name)
 
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index bc07d22..27e7e24 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -2,7 +2,7 @@
 # License: GNU General Public License v3. See license.txt
 
 import frappe
-from frappe.tests.utils import FrappeTestCase, timeout
+from frappe.tests.utils import FrappeTestCase, change_settings, timeout
 from frappe.utils import add_days, add_months, cint, flt, now, today
 
 from erpnext.manufacturing.doctype.job_card.job_card import JobCardCancelError
@@ -26,29 +26,36 @@
 
 class TestWorkOrder(FrappeTestCase):
 	def setUp(self):
-		self.warehouse = '_Test Warehouse 2 - _TC'
-		self.item = '_Test Item'
+		self.warehouse = "_Test Warehouse 2 - _TC"
+		self.item = "_Test Item"
 
 	def tearDown(self):
 		frappe.db.rollback()
 
 	def check_planned_qty(self):
 
-		planned0 = frappe.db.get_value("Bin", {"item_code": "_Test FG Item",
-			"warehouse": "_Test Warehouse 1 - _TC"}, "planned_qty") or 0
+		planned0 = (
+			frappe.db.get_value(
+				"Bin", {"item_code": "_Test FG Item", "warehouse": "_Test Warehouse 1 - _TC"}, "planned_qty"
+			)
+			or 0
+		)
 
 		wo_order = make_wo_order_test_record()
 
-		planned1 = frappe.db.get_value("Bin", {"item_code": "_Test FG Item",
-			"warehouse": "_Test Warehouse 1 - _TC"}, "planned_qty")
+		planned1 = frappe.db.get_value(
+			"Bin", {"item_code": "_Test FG Item", "warehouse": "_Test Warehouse 1 - _TC"}, "planned_qty"
+		)
 
 		self.assertEqual(planned1, planned0 + 10)
 
 		# add raw materials to stores
-		test_stock_entry.make_stock_entry(item_code="_Test Item",
-			target="Stores - _TC", qty=100, basic_rate=100)
-		test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
-			target="Stores - _TC", qty=100, basic_rate=100)
+		test_stock_entry.make_stock_entry(
+			item_code="_Test Item", target="Stores - _TC", qty=100, basic_rate=100
+		)
+		test_stock_entry.make_stock_entry(
+			item_code="_Test Item Home Desktop 100", target="Stores - _TC", qty=100, basic_rate=100
+		)
 
 		# from stores to wip
 		s = frappe.get_doc(make_stock_entry(wo_order.name, "Material Transfer for Manufacture", 4))
@@ -64,8 +71,9 @@
 
 		self.assertEqual(frappe.db.get_value("Work Order", wo_order.name, "produced_qty"), 4)
 
-		planned2 = frappe.db.get_value("Bin", {"item_code": "_Test FG Item",
-			"warehouse": "_Test Warehouse 1 - _TC"}, "planned_qty")
+		planned2 = frappe.db.get_value(
+			"Bin", {"item_code": "_Test FG Item", "warehouse": "_Test Warehouse 1 - _TC"}, "planned_qty"
+		)
 
 		self.assertEqual(planned2, planned0 + 6)
 
@@ -74,10 +82,12 @@
 	def test_over_production(self):
 		wo_doc = self.check_planned_qty()
 
-		test_stock_entry.make_stock_entry(item_code="_Test Item",
-			target="_Test Warehouse - _TC", qty=100, basic_rate=100)
-		test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
-			target="_Test Warehouse - _TC", qty=100, basic_rate=100)
+		test_stock_entry.make_stock_entry(
+			item_code="_Test Item", target="_Test Warehouse - _TC", qty=100, basic_rate=100
+		)
+		test_stock_entry.make_stock_entry(
+			item_code="_Test Item Home Desktop 100", target="_Test Warehouse - _TC", qty=100, basic_rate=100
+		)
 
 		s = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 7))
 		s.insert()
@@ -85,13 +95,14 @@
 		self.assertRaises(StockOverProductionError, s.submit)
 
 	def test_planned_operating_cost(self):
-		wo_order = make_wo_order_test_record(item="_Test FG Item 2",
-			planned_start_date=now(), qty=1, do_not_save=True)
+		wo_order = make_wo_order_test_record(
+			item="_Test FG Item 2", planned_start_date=now(), qty=1, do_not_save=True
+		)
 		wo_order.set_work_order_operations()
 		cost = wo_order.planned_operating_cost
 		wo_order.qty = 2
 		wo_order.set_work_order_operations()
-		self.assertEqual(wo_order.planned_operating_cost, cost*2)
+		self.assertEqual(wo_order.planned_operating_cost, cost * 2)
 
 	def test_reserved_qty_for_partial_completion(self):
 		item = "_Test Item"
@@ -102,27 +113,30 @@
 		# reset to correct value
 		bin1_at_start.update_reserved_qty_for_production()
 
-		wo_order = make_wo_order_test_record(item="_Test FG Item", qty=2,
-			source_warehouse=warehouse, skip_transfer=1)
+		wo_order = make_wo_order_test_record(
+			item="_Test FG Item", qty=2, source_warehouse=warehouse, skip_transfer=1
+		)
 
 		reserved_qty_on_submission = cint(get_bin(item, warehouse).reserved_qty_for_production)
 
 		# reserved qty for production is updated
 		self.assertEqual(cint(bin1_at_start.reserved_qty_for_production) + 2, reserved_qty_on_submission)
 
-
-		test_stock_entry.make_stock_entry(item_code="_Test Item",
-			target=warehouse, qty=100, basic_rate=100)
-		test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
-			target=warehouse, qty=100, basic_rate=100)
+		test_stock_entry.make_stock_entry(
+			item_code="_Test Item", target=warehouse, qty=100, basic_rate=100
+		)
+		test_stock_entry.make_stock_entry(
+			item_code="_Test Item Home Desktop 100", target=warehouse, qty=100, basic_rate=100
+		)
 
 		s = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 1))
 		s.submit()
 
 		bin1_at_completion = get_bin(item, warehouse)
 
-		self.assertEqual(cint(bin1_at_completion.reserved_qty_for_production),
-			reserved_qty_on_submission - 1)
+		self.assertEqual(
+			cint(bin1_at_completion.reserved_qty_for_production), reserved_qty_on_submission - 1
+		)
 
 	def test_production_item(self):
 		wo_order = make_wo_order_test_record(item="_Test FG Item", qty=1, do_not_save=True)
@@ -146,16 +160,20 @@
 		# reset to correct value
 		self.bin1_at_start.update_reserved_qty_for_production()
 
-		self.wo_order = make_wo_order_test_record(item="_Test FG Item", qty=2,
-			source_warehouse=self.warehouse)
+		self.wo_order = make_wo_order_test_record(
+			item="_Test FG Item", qty=2, source_warehouse=self.warehouse
+		)
 
 		self.bin1_on_submit = get_bin(self.item, self.warehouse)
 
 		# reserved qty for production is updated
-		self.assertEqual(cint(self.bin1_at_start.reserved_qty_for_production) + 2,
-			cint(self.bin1_on_submit.reserved_qty_for_production))
-		self.assertEqual(cint(self.bin1_at_start.projected_qty),
-			cint(self.bin1_on_submit.projected_qty) + 2)
+		self.assertEqual(
+			cint(self.bin1_at_start.reserved_qty_for_production) + 2,
+			cint(self.bin1_on_submit.reserved_qty_for_production),
+		)
+		self.assertEqual(
+			cint(self.bin1_at_start.projected_qty), cint(self.bin1_on_submit.projected_qty) + 2
+		)
 
 	def test_reserved_qty_for_production_cancel(self):
 		self.test_reserved_qty_for_production_submit()
@@ -165,52 +183,57 @@
 		bin1_on_cancel = get_bin(self.item, self.warehouse)
 
 		# reserved_qty_for_producion updated
-		self.assertEqual(cint(self.bin1_at_start.reserved_qty_for_production),
-			cint(bin1_on_cancel.reserved_qty_for_production))
-		self.assertEqual(self.bin1_at_start.projected_qty,
-			cint(bin1_on_cancel.projected_qty))
+		self.assertEqual(
+			cint(self.bin1_at_start.reserved_qty_for_production),
+			cint(bin1_on_cancel.reserved_qty_for_production),
+		)
+		self.assertEqual(self.bin1_at_start.projected_qty, cint(bin1_on_cancel.projected_qty))
 
 	def test_reserved_qty_for_production_on_stock_entry(self):
-		test_stock_entry.make_stock_entry(item_code="_Test Item",
-			target= self.warehouse, qty=100, basic_rate=100)
-		test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
-			target= self.warehouse, qty=100, basic_rate=100)
+		test_stock_entry.make_stock_entry(
+			item_code="_Test Item", target=self.warehouse, qty=100, basic_rate=100
+		)
+		test_stock_entry.make_stock_entry(
+			item_code="_Test Item Home Desktop 100", target=self.warehouse, qty=100, basic_rate=100
+		)
 
 		self.test_reserved_qty_for_production_submit()
 
-		s = frappe.get_doc(make_stock_entry(self.wo_order.name,
-			"Material Transfer for Manufacture", 2))
+		s = frappe.get_doc(make_stock_entry(self.wo_order.name, "Material Transfer for Manufacture", 2))
 
 		s.submit()
 
 		bin1_on_start_production = get_bin(self.item, self.warehouse)
 
 		# reserved_qty_for_producion updated
-		self.assertEqual(cint(self.bin1_at_start.reserved_qty_for_production),
-			cint(bin1_on_start_production.reserved_qty_for_production))
+		self.assertEqual(
+			cint(self.bin1_at_start.reserved_qty_for_production),
+			cint(bin1_on_start_production.reserved_qty_for_production),
+		)
 
 		# projected qty will now be 2 less (becuase of item movement)
-		self.assertEqual(cint(self.bin1_at_start.projected_qty),
-			cint(bin1_on_start_production.projected_qty) + 2)
+		self.assertEqual(
+			cint(self.bin1_at_start.projected_qty), cint(bin1_on_start_production.projected_qty) + 2
+		)
 
 		s = frappe.get_doc(make_stock_entry(self.wo_order.name, "Manufacture", 2))
 
 		bin1_on_end_production = get_bin(self.item, self.warehouse)
 
 		# no change in reserved / projected
-		self.assertEqual(cint(bin1_on_end_production.reserved_qty_for_production),
-			cint(bin1_on_start_production.reserved_qty_for_production))
+		self.assertEqual(
+			cint(bin1_on_end_production.reserved_qty_for_production),
+			cint(bin1_on_start_production.reserved_qty_for_production),
+		)
 
 	def test_reserved_qty_for_production_closed(self):
 
-		wo1 = make_wo_order_test_record(item="_Test FG Item", qty=2,
-			source_warehouse=self.warehouse)
+		wo1 = make_wo_order_test_record(item="_Test FG Item", qty=2, source_warehouse=self.warehouse)
 		item = wo1.required_items[0].item_code
 		bin_before = get_bin(item, self.warehouse)
 		bin_before.update_reserved_qty_for_production()
 
-		make_wo_order_test_record(item="_Test FG Item", qty=2,
-			source_warehouse=self.warehouse)
+		make_wo_order_test_record(item="_Test FG Item", qty=2, source_warehouse=self.warehouse)
 		close_work_order(wo1.name, "Closed")
 
 		bin_after = get_bin(item, self.warehouse)
@@ -220,10 +243,15 @@
 		cancel_stock_entry = []
 		allow_overproduction("overproduction_percentage_for_work_order", 30)
 		wo_order = make_wo_order_test_record(planned_start_date=now(), qty=100)
-		ste1 = test_stock_entry.make_stock_entry(item_code="_Test Item",
-			target="_Test Warehouse - _TC", qty=120, basic_rate=5000.0)
-		ste2 = test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
-			target="_Test Warehouse - _TC", qty=240, basic_rate=1000.0)
+		ste1 = test_stock_entry.make_stock_entry(
+			item_code="_Test Item", target="_Test Warehouse - _TC", qty=120, basic_rate=5000.0
+		)
+		ste2 = test_stock_entry.make_stock_entry(
+			item_code="_Test Item Home Desktop 100",
+			target="_Test Warehouse - _TC",
+			qty=240,
+			basic_rate=1000.0,
+		)
 
 		cancel_stock_entry.extend([ste1.name, ste2.name])
 
@@ -253,33 +281,37 @@
 		allow_overproduction("overproduction_percentage_for_work_order", 0)
 
 	def test_reserved_qty_for_stopped_production(self):
-		test_stock_entry.make_stock_entry(item_code="_Test Item",
-			target= self.warehouse, qty=100, basic_rate=100)
-		test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
-			target= self.warehouse, qty=100, basic_rate=100)
+		test_stock_entry.make_stock_entry(
+			item_code="_Test Item", target=self.warehouse, qty=100, basic_rate=100
+		)
+		test_stock_entry.make_stock_entry(
+			item_code="_Test Item Home Desktop 100", target=self.warehouse, qty=100, basic_rate=100
+		)
 
 		# 	0 0 0
 
 		self.test_reserved_qty_for_production_submit()
 
-		#2 0 -2
+		# 2 0 -2
 
-		s = frappe.get_doc(make_stock_entry(self.wo_order.name,
-			"Material Transfer for Manufacture", 1))
+		s = frappe.get_doc(make_stock_entry(self.wo_order.name, "Material Transfer for Manufacture", 1))
 
 		s.submit()
 
-		#1 -1 0
+		# 1 -1 0
 
 		bin1_on_start_production = get_bin(self.item, self.warehouse)
 
 		# reserved_qty_for_producion updated
-		self.assertEqual(cint(self.bin1_at_start.reserved_qty_for_production) + 1,
-			cint(bin1_on_start_production.reserved_qty_for_production))
+		self.assertEqual(
+			cint(self.bin1_at_start.reserved_qty_for_production) + 1,
+			cint(bin1_on_start_production.reserved_qty_for_production),
+		)
 
 		# projected qty will now be 2 less (becuase of item movement)
-		self.assertEqual(cint(self.bin1_at_start.projected_qty),
-			cint(bin1_on_start_production.projected_qty) + 2)
+		self.assertEqual(
+			cint(self.bin1_at_start.projected_qty), cint(bin1_on_start_production.projected_qty) + 2
+		)
 
 		# STOP
 		stop_unstop(self.wo_order.name, "Stopped")
@@ -287,19 +319,24 @@
 		bin1_on_stop_production = get_bin(self.item, self.warehouse)
 
 		# no change in reserved / projected
-		self.assertEqual(cint(bin1_on_stop_production.reserved_qty_for_production),
-			cint(self.bin1_at_start.reserved_qty_for_production))
-		self.assertEqual(cint(bin1_on_stop_production.projected_qty) + 1,
-			cint(self.bin1_at_start.projected_qty))
+		self.assertEqual(
+			cint(bin1_on_stop_production.reserved_qty_for_production),
+			cint(self.bin1_at_start.reserved_qty_for_production),
+		)
+		self.assertEqual(
+			cint(bin1_on_stop_production.projected_qty) + 1, cint(self.bin1_at_start.projected_qty)
+		)
 
 	def test_scrap_material_qty(self):
 		wo_order = make_wo_order_test_record(planned_start_date=now(), qty=2)
 
 		# add raw materials to stores
-		test_stock_entry.make_stock_entry(item_code="_Test Item",
-			target="Stores - _TC", qty=10, basic_rate=5000.0)
-		test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
-			target="Stores - _TC", qty=10, basic_rate=1000.0)
+		test_stock_entry.make_stock_entry(
+			item_code="_Test Item", target="Stores - _TC", qty=10, basic_rate=5000.0
+		)
+		test_stock_entry.make_stock_entry(
+			item_code="_Test Item Home Desktop 100", target="Stores - _TC", qty=10, basic_rate=1000.0
+		)
 
 		s = frappe.get_doc(make_stock_entry(wo_order.name, "Material Transfer for Manufacture", 2))
 		for d in s.get("items"):
@@ -311,8 +348,9 @@
 		s.insert()
 		s.submit()
 
-		wo_order_details = frappe.db.get_value("Work Order", wo_order.name,
-			["scrap_warehouse", "qty", "produced_qty", "bom_no"], as_dict=1)
+		wo_order_details = frappe.db.get_value(
+			"Work Order", wo_order.name, ["scrap_warehouse", "qty", "produced_qty", "bom_no"], as_dict=1
+		)
 
 		scrap_item_details = get_scrap_item_details(wo_order_details.bom_no)
 
@@ -321,15 +359,20 @@
 		for item in s.items:
 			if item.bom_no and item.item_code in scrap_item_details:
 				self.assertEqual(wo_order_details.scrap_warehouse, item.t_warehouse)
-				self.assertEqual(flt(wo_order_details.qty)*flt(scrap_item_details[item.item_code]), item.qty)
+				self.assertEqual(flt(wo_order_details.qty) * flt(scrap_item_details[item.item_code]), item.qty)
 
 	def test_allow_overproduction(self):
 		allow_overproduction("overproduction_percentage_for_work_order", 0)
 		wo_order = make_wo_order_test_record(planned_start_date=now(), qty=2)
-		test_stock_entry.make_stock_entry(item_code="_Test Item",
-			target="_Test Warehouse - _TC", qty=10, basic_rate=5000.0)
-		test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
-			target="_Test Warehouse - _TC", qty=10, basic_rate=1000.0)
+		test_stock_entry.make_stock_entry(
+			item_code="_Test Item", target="_Test Warehouse - _TC", qty=10, basic_rate=5000.0
+		)
+		test_stock_entry.make_stock_entry(
+			item_code="_Test Item Home Desktop 100",
+			target="_Test Warehouse - _TC",
+			qty=10,
+			basic_rate=1000.0,
+		)
 
 		s = frappe.get_doc(make_stock_entry(wo_order.name, "Material Transfer for Manufacture", 3))
 		s.insert()
@@ -346,43 +389,56 @@
 		so = make_sales_order(item_code="_Test FG Item", qty=2)
 
 		allow_overproduction("overproduction_percentage_for_sales_order", 0)
-		wo_order = make_wo_order_test_record(planned_start_date=now(),
-			sales_order=so.name, qty=3, do_not_save=True)
+		wo_order = make_wo_order_test_record(
+			planned_start_date=now(), sales_order=so.name, qty=3, do_not_save=True
+		)
 
 		self.assertRaises(OverProductionError, wo_order.save)
 
 		allow_overproduction("overproduction_percentage_for_sales_order", 50)
-		wo_order = make_wo_order_test_record(planned_start_date=now(),
-			sales_order=so.name, qty=3)
+		wo_order = make_wo_order_test_record(planned_start_date=now(), sales_order=so.name, qty=3)
 
-		wo_order.submit()
 		self.assertEqual(wo_order.docstatus, 1)
 
 		allow_overproduction("overproduction_percentage_for_sales_order", 0)
 
 	def test_work_order_with_non_stock_item(self):
-		items = {'Finished Good Test Item For non stock': 1, '_Test FG Item': 1, '_Test FG Non Stock Item': 0}
+		items = {
+			"Finished Good Test Item For non stock": 1,
+			"_Test FG Item": 1,
+			"_Test FG Non Stock Item": 0,
+		}
 		for item, is_stock_item in items.items():
-			make_item(item, {
-				'is_stock_item': is_stock_item
-			})
+			make_item(item, {"is_stock_item": is_stock_item})
 
-		if not frappe.db.get_value('Item Price', {'item_code': '_Test FG Non Stock Item'}):
-			frappe.get_doc({
-				'doctype': 'Item Price',
-				'item_code': '_Test FG Non Stock Item',
-				'price_list_rate': 1000,
-				'price_list': 'Standard Buying'
-			}).insert(ignore_permissions=True)
+		if not frappe.db.get_value("Item Price", {"item_code": "_Test FG Non Stock Item"}):
+			frappe.get_doc(
+				{
+					"doctype": "Item Price",
+					"item_code": "_Test FG Non Stock Item",
+					"price_list_rate": 1000,
+					"price_list": "_Test Price List India",
+				}
+			).insert(ignore_permissions=True)
 
-		fg_item = 'Finished Good Test Item For non stock'
-		test_stock_entry.make_stock_entry(item_code="_Test FG Item",
-			target="_Test Warehouse - _TC", qty=1, basic_rate=100)
+		fg_item = "Finished Good Test Item For non stock"
+		test_stock_entry.make_stock_entry(
+			item_code="_Test FG Item", target="_Test Warehouse - _TC", qty=1, basic_rate=100
+		)
 
-		if not frappe.db.get_value('BOM', {'item': fg_item}):
-			make_bom(item=fg_item, rate=1000, raw_materials = ['_Test FG Item', '_Test FG Non Stock Item'])
+		if not frappe.db.get_value("BOM", {"item": fg_item, "docstatus": 1}):
+			bom = make_bom(
+				item=fg_item,
+				rate=1000,
+				raw_materials=["_Test FG Item", "_Test FG Non Stock Item"],
+				do_not_save=True,
+			)
+			bom.rm_cost_as_per = "Price List"  # non stock item won't have valuation rate
+			bom.buying_price_list = "_Test Price List India"
+			bom.currency = "INR"
+			bom.save()
 
-		wo = make_wo_order_test_record(production_item = fg_item)
+		wo = make_wo_order_test_record(production_item=fg_item)
 
 		se = frappe.get_doc(make_stock_entry(wo.name, "Material Transfer for Manufacture", 1))
 		se.insert()
@@ -396,25 +452,25 @@
 	@timeout(seconds=60)
 	def test_job_card(self):
 		stock_entries = []
-		bom = frappe.get_doc('BOM', {
-			'docstatus': 1,
-			'with_operations': 1,
-			'company': '_Test Company'
-		})
+		bom = frappe.get_doc("BOM", {"docstatus": 1, "with_operations": 1, "company": "_Test Company"})
 
-		work_order = make_wo_order_test_record(item=bom.item, qty=1,
-			bom_no=bom.name, source_warehouse="_Test Warehouse - _TC")
+		work_order = make_wo_order_test_record(
+			item=bom.item, qty=1, bom_no=bom.name, source_warehouse="_Test Warehouse - _TC"
+		)
 
 		for row in work_order.required_items:
-			stock_entry_doc = test_stock_entry.make_stock_entry(item_code=row.item_code,
-				target="_Test Warehouse - _TC", qty=row.required_qty, basic_rate=100)
+			stock_entry_doc = test_stock_entry.make_stock_entry(
+				item_code=row.item_code, target="_Test Warehouse - _TC", qty=row.required_qty, basic_rate=100
+			)
 			stock_entries.append(stock_entry_doc)
 
 		ste = frappe.get_doc(make_stock_entry(work_order.name, "Material Transfer for Manufacture", 1))
 		ste.submit()
 		stock_entries.append(ste)
 
-		job_cards = frappe.get_all('Job Card', filters = {'work_order': work_order.name}, order_by='creation asc')
+		job_cards = frappe.get_all(
+			"Job Card", filters={"work_order": work_order.name}, order_by="creation asc"
+		)
 		self.assertEqual(len(job_cards), len(bom.operations))
 
 		for i, job_card in enumerate(job_cards):
@@ -435,29 +491,33 @@
 			stock_entry.cancel()
 
 	def test_capcity_planning(self):
-		frappe.db.set_value("Manufacturing Settings", None, {
-			"disable_capacity_planning": 0,
-			"capacity_planning_for_days": 1
-		})
+		frappe.db.set_value(
+			"Manufacturing Settings",
+			None,
+			{"disable_capacity_planning": 0, "capacity_planning_for_days": 1},
+		)
 
-		data = frappe.get_cached_value('BOM', {'docstatus': 1, 'item': '_Test FG Item 2',
-			'with_operations': 1, 'company': '_Test Company'}, ['name', 'item'])
+		data = frappe.get_cached_value(
+			"BOM",
+			{"docstatus": 1, "item": "_Test FG Item 2", "with_operations": 1, "company": "_Test Company"},
+			["name", "item"],
+		)
 
 		if data:
 			bom, bom_item = data
 
 			planned_start_date = add_months(today(), months=-1)
-			work_order = make_wo_order_test_record(item=bom_item,
-				qty=10, bom_no=bom, planned_start_date=planned_start_date)
+			work_order = make_wo_order_test_record(
+				item=bom_item, qty=10, bom_no=bom, planned_start_date=planned_start_date
+			)
 
-			work_order1 = make_wo_order_test_record(item=bom_item,
-				qty=30, bom_no=bom, planned_start_date=planned_start_date, do_not_submit=1)
+			work_order1 = make_wo_order_test_record(
+				item=bom_item, qty=30, bom_no=bom, planned_start_date=planned_start_date, do_not_submit=1
+			)
 
 			self.assertRaises(CapacityError, work_order1.submit)
 
-			frappe.db.set_value("Manufacturing Settings", None, {
-				"capacity_planning_for_days": 30
-			})
+			frappe.db.set_value("Manufacturing Settings", None, {"capacity_planning_for_days": 30})
 
 			work_order1.reload()
 			work_order1.submit()
@@ -467,22 +527,22 @@
 			work_order.cancel()
 
 	def test_work_order_with_non_transfer_item(self):
-		items = {'Finished Good Transfer Item': 1, '_Test FG Item': 1, '_Test FG Item 1': 0}
+		items = {"Finished Good Transfer Item": 1, "_Test FG Item": 1, "_Test FG Item 1": 0}
 		for item, allow_transfer in items.items():
-			make_item(item, {
-				'include_item_in_manufacturing': allow_transfer
-			})
+			make_item(item, {"include_item_in_manufacturing": allow_transfer})
 
-		fg_item = 'Finished Good Transfer Item'
-		test_stock_entry.make_stock_entry(item_code="_Test FG Item",
-			target="_Test Warehouse - _TC", qty=1, basic_rate=100)
-		test_stock_entry.make_stock_entry(item_code="_Test FG Item 1",
-			target="_Test Warehouse - _TC", qty=1, basic_rate=100)
+		fg_item = "Finished Good Transfer Item"
+		test_stock_entry.make_stock_entry(
+			item_code="_Test FG Item", target="_Test Warehouse - _TC", qty=1, basic_rate=100
+		)
+		test_stock_entry.make_stock_entry(
+			item_code="_Test FG Item 1", target="_Test Warehouse - _TC", qty=1, basic_rate=100
+		)
 
-		if not frappe.db.get_value('BOM', {'item': fg_item}):
-			make_bom(item=fg_item, raw_materials = ['_Test FG Item', '_Test FG Item 1'])
+		if not frappe.db.get_value("BOM", {"item": fg_item}):
+			make_bom(item=fg_item, raw_materials=["_Test FG Item", "_Test FG Item 1"])
 
-		wo = make_wo_order_test_record(production_item = fg_item)
+		wo = make_wo_order_test_record(production_item=fg_item)
 		ste = frappe.get_doc(make_stock_entry(wo.name, "Material Transfer for Manufacture", 1))
 		ste.insert()
 		ste.submit()
@@ -500,39 +560,42 @@
 		rm1 = "Test Batch Size Item RM 1 For BOM"
 
 		for item in ["Test Batch Size Item For BOM", "Test Batch Size Item RM 1 For BOM"]:
-			make_item(item, {
-				"include_item_in_manufacturing": 1,
-				"is_stock_item": 1
-			})
+			make_item(item, {"include_item_in_manufacturing": 1, "is_stock_item": 1})
 
-		bom_name = frappe.db.get_value("BOM",
-			{"item": fg_item, "is_active": 1, "with_operations": 1}, "name")
+		bom_name = frappe.db.get_value(
+			"BOM", {"item": fg_item, "is_active": 1, "with_operations": 1}, "name"
+		)
 
 		if not bom_name:
-			bom = make_bom(item=fg_item, rate=1000, raw_materials = [rm1], do_not_save=True)
+			bom = make_bom(item=fg_item, rate=1000, raw_materials=[rm1], do_not_save=True)
 			bom.with_operations = 1
-			bom.append("operations", {
-				"operation": "_Test Operation 1",
-				"workstation": "_Test Workstation 1",
-				"description": "Test Data",
-				"operating_cost": 100,
-				"time_in_mins": 40,
-				"batch_size": 5
-			})
+			bom.append(
+				"operations",
+				{
+					"operation": "_Test Operation 1",
+					"workstation": "_Test Workstation 1",
+					"description": "Test Data",
+					"operating_cost": 100,
+					"time_in_mins": 40,
+					"batch_size": 5,
+				},
+			)
 
 			bom.save()
 			bom.submit()
 			bom_name = bom.name
 
-		work_order = make_wo_order_test_record(item=fg_item,
-			planned_start_date=now(), qty=1, do_not_save=True)
+		work_order = make_wo_order_test_record(
+			item=fg_item, planned_start_date=now(), qty=1, do_not_save=True
+		)
 
 		work_order.set_work_order_operations()
 		work_order.save()
 		self.assertEqual(work_order.operations[0].time_in_mins, 8.0)
 
-		work_order1 = make_wo_order_test_record(item=fg_item,
-			planned_start_date=now(), qty=5, do_not_save=True)
+		work_order1 = make_wo_order_test_record(
+			item=fg_item, planned_start_date=now(), qty=5, do_not_save=True
+		)
 
 		work_order1.set_work_order_operations()
 		work_order1.save()
@@ -542,65 +605,73 @@
 		fg_item = "Test Batch Size Item For BOM 3"
 		rm1 = "Test Batch Size Item RM 1 For BOM 3"
 
-		frappe.db.set_value('Manufacturing Settings', None, 'make_serial_no_batch_from_work_order', 0)
+		frappe.db.set_value("Manufacturing Settings", None, "make_serial_no_batch_from_work_order", 0)
 		for item in ["Test Batch Size Item For BOM 3", "Test Batch Size Item RM 1 For BOM 3"]:
-			item_args = {
-				"include_item_in_manufacturing": 1,
-				"is_stock_item": 1
-			}
+			item_args = {"include_item_in_manufacturing": 1, "is_stock_item": 1}
 
 			if item == fg_item:
-				item_args['has_batch_no'] = 1
-				item_args['create_new_batch'] = 1
-				item_args['batch_number_series'] = 'TBSI3.#####'
+				item_args["has_batch_no"] = 1
+				item_args["create_new_batch"] = 1
+				item_args["batch_number_series"] = "TBSI3.#####"
 
 			make_item(item, item_args)
 
-		bom_name = frappe.db.get_value("BOM",
-			{"item": fg_item, "is_active": 1, "with_operations": 1}, "name")
+		bom_name = frappe.db.get_value(
+			"BOM", {"item": fg_item, "is_active": 1, "with_operations": 1}, "name"
+		)
 
 		if not bom_name:
-			bom = make_bom(item=fg_item, rate=1000, raw_materials = [rm1], do_not_save=True)
+			bom = make_bom(item=fg_item, rate=1000, raw_materials=[rm1], do_not_save=True)
 			bom.save()
 			bom.submit()
 			bom_name = bom.name
 
-		work_order = make_wo_order_test_record(item=fg_item, skip_transfer=True, planned_start_date=now(), qty=1)
+		work_order = make_wo_order_test_record(
+			item=fg_item, skip_transfer=True, planned_start_date=now(), qty=1
+		)
 		ste1 = frappe.get_doc(make_stock_entry(work_order.name, "Manufacture", 1))
-		for row in ste1.get('items'):
+		for row in ste1.get("items"):
 			if row.is_finished_item:
 				self.assertEqual(row.item_code, fg_item)
 
-		work_order = make_wo_order_test_record(item=fg_item, skip_transfer=True, planned_start_date=now(), qty=1)
-		frappe.db.set_value('Manufacturing Settings', None, 'make_serial_no_batch_from_work_order', 1)
+		work_order = make_wo_order_test_record(
+			item=fg_item, skip_transfer=True, planned_start_date=now(), qty=1
+		)
+		frappe.db.set_value("Manufacturing Settings", None, "make_serial_no_batch_from_work_order", 1)
 		ste1 = frappe.get_doc(make_stock_entry(work_order.name, "Manufacture", 1))
-		for row in ste1.get('items'):
+		for row in ste1.get("items"):
 			if row.is_finished_item:
 				self.assertEqual(row.item_code, fg_item)
 
-		work_order = make_wo_order_test_record(item=fg_item, skip_transfer=True, planned_start_date=now(),
-			qty=30, do_not_save = True)
+		work_order = make_wo_order_test_record(
+			item=fg_item, skip_transfer=True, planned_start_date=now(), qty=30, do_not_save=True
+		)
 		work_order.batch_size = 10
 		work_order.insert()
 		work_order.submit()
 		self.assertEqual(work_order.has_batch_no, 1)
 		ste1 = frappe.get_doc(make_stock_entry(work_order.name, "Manufacture", 30))
-		for row in ste1.get('items'):
+		for row in ste1.get("items"):
 			if row.is_finished_item:
 				self.assertEqual(row.item_code, fg_item)
 				self.assertEqual(row.qty, 10)
 
-		frappe.db.set_value('Manufacturing Settings', None, 'make_serial_no_batch_from_work_order', 0)
+		frappe.db.set_value("Manufacturing Settings", None, "make_serial_no_batch_from_work_order", 0)
 
 	def test_partial_material_consumption(self):
 		frappe.db.set_value("Manufacturing Settings", None, "material_consumption", 1)
 		wo_order = make_wo_order_test_record(planned_start_date=now(), qty=4)
 
 		ste_cancel_list = []
-		ste1 = test_stock_entry.make_stock_entry(item_code="_Test Item",
-			target="_Test Warehouse - _TC", qty=20, basic_rate=5000.0)
-		ste2 = test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
-			target="_Test Warehouse - _TC", qty=20, basic_rate=1000.0)
+		ste1 = test_stock_entry.make_stock_entry(
+			item_code="_Test Item", target="_Test Warehouse - _TC", qty=20, basic_rate=5000.0
+		)
+		ste2 = test_stock_entry.make_stock_entry(
+			item_code="_Test Item Home Desktop 100",
+			target="_Test Warehouse - _TC",
+			qty=20,
+			basic_rate=1000.0,
+		)
 
 		ste_cancel_list.extend([ste1, ste2])
 
@@ -626,16 +697,25 @@
 
 	def test_extra_material_transfer(self):
 		frappe.db.set_value("Manufacturing Settings", None, "material_consumption", 0)
-		frappe.db.set_value("Manufacturing Settings", None, "backflush_raw_materials_based_on",
-			"Material Transferred for Manufacture")
+		frappe.db.set_value(
+			"Manufacturing Settings",
+			None,
+			"backflush_raw_materials_based_on",
+			"Material Transferred for Manufacture",
+		)
 
 		wo_order = make_wo_order_test_record(planned_start_date=now(), qty=4)
 
 		ste_cancel_list = []
-		ste1 = test_stock_entry.make_stock_entry(item_code="_Test Item",
-			target="_Test Warehouse - _TC", qty=20, basic_rate=5000.0)
-		ste2 = test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
-			target="_Test Warehouse - _TC", qty=20, basic_rate=1000.0)
+		ste1 = test_stock_entry.make_stock_entry(
+			item_code="_Test Item", target="_Test Warehouse - _TC", qty=20, basic_rate=5000.0
+		)
+		ste2 = test_stock_entry.make_stock_entry(
+			item_code="_Test Item Home Desktop 100",
+			target="_Test Warehouse - _TC",
+			qty=20,
+			basic_rate=1000.0,
+		)
 
 		ste_cancel_list.extend([ste1, ste2])
 
@@ -667,30 +747,31 @@
 		frappe.db.set_value("Manufacturing Settings", None, "backflush_raw_materials_based_on", "BOM")
 
 	def test_make_stock_entry_for_customer_provided_item(self):
-		finished_item = 'Test Item for Make Stock Entry 1'
-		make_item(finished_item, {
+		finished_item = "Test Item for Make Stock Entry 1"
+		make_item(finished_item, {"include_item_in_manufacturing": 1, "is_stock_item": 1})
+
+		customer_provided_item = "CUST-0987"
+		make_item(
+			customer_provided_item,
+			{
+				"is_purchase_item": 0,
+				"is_customer_provided_item": 1,
+				"is_stock_item": 1,
 				"include_item_in_manufacturing": 1,
-				"is_stock_item": 1
-			})
+				"customer": "_Test Customer",
+			},
+		)
 
-		customer_provided_item = 'CUST-0987'
-		make_item(customer_provided_item, {
-			'is_purchase_item': 0,
-			'is_customer_provided_item': 1,
-			"is_stock_item": 1,
-			"include_item_in_manufacturing": 1,
-			'customer': '_Test Customer'
-		})
-
-		if not frappe.db.exists('BOM', {'item': finished_item}):
+		if not frappe.db.exists("BOM", {"item": finished_item}):
 			make_bom(item=finished_item, raw_materials=[customer_provided_item], rm_qty=1)
 
 		company = "_Test Company with perpetual inventory"
 		customer_warehouse = create_warehouse("Test Customer Provided Warehouse", company=company)
-		wo = make_wo_order_test_record(item=finished_item, qty=1, source_warehouse=customer_warehouse,
-			company=company)
+		wo = make_wo_order_test_record(
+			item=finished_item, qty=1, source_warehouse=customer_warehouse, company=company
+		)
 
-		ste = frappe.get_doc(make_stock_entry(wo.name, purpose='Material Transfer for Manufacture'))
+		ste = frappe.get_doc(make_stock_entry(wo.name, purpose="Material Transfer for Manufacture"))
 		ste.insert()
 
 		self.assertEqual(len(ste.items), 1)
@@ -699,26 +780,33 @@
 			self.assertEqual(item.valuation_rate, 0)
 
 	def test_valuation_rate_missing_on_make_stock_entry(self):
-		item_name = 'Test Valuation Rate Missing'
-		rm_item = '_Test raw material item'
-		make_item(item_name, {
-			"is_stock_item": 1,
-			"include_item_in_manufacturing": 1,
-		})
-		make_item('_Test raw material item', {
-			"is_stock_item": 1,
-			"include_item_in_manufacturing": 1,
-		})
+		item_name = "Test Valuation Rate Missing"
+		rm_item = "_Test raw material item"
+		make_item(
+			item_name,
+			{
+				"is_stock_item": 1,
+				"include_item_in_manufacturing": 1,
+			},
+		)
+		make_item(
+			"_Test raw material item",
+			{
+				"is_stock_item": 1,
+				"include_item_in_manufacturing": 1,
+			},
+		)
 
-		if not frappe.db.get_value('BOM', {'item': item_name}):
+		if not frappe.db.get_value("BOM", {"item": item_name}):
 			make_bom(item=item_name, raw_materials=[rm_item], rm_qty=1)
 
 		company = "_Test Company with perpetual inventory"
 		source_warehouse = create_warehouse("Test Valuation Rate Missing Warehouse", company=company)
-		wo = make_wo_order_test_record(item=item_name, qty=1, source_warehouse=source_warehouse,
-			company=company)
+		wo = make_wo_order_test_record(
+			item=item_name, qty=1, source_warehouse=source_warehouse, company=company
+		)
 
-		stock_entry = frappe.get_doc(make_stock_entry(wo.name, 'Material Transfer for Manufacture'))
+		stock_entry = frappe.get_doc(make_stock_entry(wo.name, "Material Transfer for Manufacture"))
 		self.assertRaises(frappe.ValidationError, stock_entry.save)
 
 	def test_wo_completion_with_pl_bom(self):
@@ -728,19 +816,19 @@
 		)
 
 		qty = 4
-		scrap_qty = 0.25 # bom item qty = 1, consider as 25% of FG
+		scrap_qty = 0.25  # bom item qty = 1, consider as 25% of FG
 		source_warehouse = "Stores - _TC"
 		wip_warehouse = "_Test Warehouse - _TC"
 		fg_item_non_whole, _, bom_item = create_process_loss_bom_items()
 
-		test_stock_entry.make_stock_entry(item_code=bom_item.item_code,
-			target=source_warehouse, qty=4, basic_rate=100)
+		test_stock_entry.make_stock_entry(
+			item_code=bom_item.item_code, target=source_warehouse, qty=4, basic_rate=100
+		)
 
 		bom_no = f"BOM-{fg_item_non_whole.item_code}-001"
 		if not frappe.db.exists("BOM", bom_no):
 			bom_doc = create_bom_with_process_loss_item(
-				fg_item_non_whole, bom_item, scrap_qty=scrap_qty,
-				scrap_rate=0, fg_qty=1, is_process_loss=1
+				fg_item_non_whole, bom_item, scrap_qty=scrap_qty, scrap_rate=0, fg_qty=1, is_process_loss=1
 			)
 			bom_doc.submit()
 
@@ -753,16 +841,12 @@
 			stock_uom=fg_item_non_whole.stock_uom,
 		)
 
-		se = frappe.get_doc(
-			make_stock_entry(wo.name, "Material Transfer for Manufacture", qty)
-		)
+		se = frappe.get_doc(make_stock_entry(wo.name, "Material Transfer for Manufacture", qty))
 		se.get("items")[0].s_warehouse = "Stores - _TC"
 		se.insert()
 		se.submit()
 
-		se = frappe.get_doc(
-			make_stock_entry(wo.name, "Manufacture", qty)
-		)
+		se = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", qty))
 		se.insert()
 		se.submit()
 
@@ -779,41 +863,52 @@
 		self.assertEqual(fg_item.qty, actual_fg_qty)
 
 		# Testing Work Order values
-		self.assertEqual(
-			frappe.db.get_value("Work Order", wo.name, "produced_qty"),
-			qty
-		)
-		self.assertEqual(
-			frappe.db.get_value("Work Order", wo.name, "process_loss_qty"),
-			total_pl_qty
-		)
+		self.assertEqual(frappe.db.get_value("Work Order", wo.name, "produced_qty"), qty)
+		self.assertEqual(frappe.db.get_value("Work Order", wo.name, "process_loss_qty"), total_pl_qty)
 
 	@timeout(seconds=60)
 	def test_job_card_scrap_item(self):
-		items = ['Test FG Item for Scrap Item Test', 'Test RM Item 1 for Scrap Item Test',
-			'Test RM Item 2 for Scrap Item Test']
+		items = [
+			"Test FG Item for Scrap Item Test",
+			"Test RM Item 1 for Scrap Item Test",
+			"Test RM Item 2 for Scrap Item Test",
+		]
 
-		company = '_Test Company with perpetual inventory'
+		company = "_Test Company with perpetual inventory"
 		for item_code in items:
-			create_item(item_code = item_code, is_stock_item = 1,
-				is_purchase_item=1, opening_stock=100, valuation_rate=10, company=company, warehouse='Stores - TCP1')
+			create_item(
+				item_code=item_code,
+				is_stock_item=1,
+				is_purchase_item=1,
+				opening_stock=100,
+				valuation_rate=10,
+				company=company,
+				warehouse="Stores - TCP1",
+			)
 
-		item = 'Test FG Item for Scrap Item Test'
-		raw_materials = ['Test RM Item 1 for Scrap Item Test', 'Test RM Item 2 for Scrap Item Test']
-		if not frappe.db.get_value('BOM', {'item': item}):
-			bom = make_bom(item=item, source_warehouse='Stores - TCP1', raw_materials=raw_materials, do_not_save=True)
+		item = "Test FG Item for Scrap Item Test"
+		raw_materials = ["Test RM Item 1 for Scrap Item Test", "Test RM Item 2 for Scrap Item Test"]
+		if not frappe.db.get_value("BOM", {"item": item}):
+			bom = make_bom(
+				item=item, source_warehouse="Stores - TCP1", raw_materials=raw_materials, do_not_save=True
+			)
 			bom.with_operations = 1
-			bom.append('operations', {
-				'operation': '_Test Operation 1',
-				'workstation': '_Test Workstation 1',
-				'hour_rate': 20,
-				'time_in_mins': 60
-			})
+			bom.append(
+				"operations",
+				{
+					"operation": "_Test Operation 1",
+					"workstation": "_Test Workstation 1",
+					"hour_rate": 20,
+					"time_in_mins": 60,
+				},
+			)
 
 			bom.submit()
 
-		wo_order = make_wo_order_test_record(item=item, company=company, planned_start_date=now(), qty=20, skip_transfer=1)
-		job_card = frappe.db.get_value('Job Card', {'work_order': wo_order.name}, 'name')
+		wo_order = make_wo_order_test_record(
+			item=item, company=company, planned_start_date=now(), qty=20, skip_transfer=1
+		)
+		job_card = frappe.db.get_value("Job Card", {"work_order": wo_order.name}, "name")
 		update_job_card(job_card)
 
 		stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 10))
@@ -822,8 +917,10 @@
 				self.assertEqual(row.qty, 1)
 
 		# Partial Job Card 1 with qty 10
-		wo_order = make_wo_order_test_record(item=item, company=company, planned_start_date=add_days(now(), 60), qty=20, skip_transfer=1)
-		job_card = frappe.db.get_value('Job Card', {'work_order': wo_order.name}, 'name')
+		wo_order = make_wo_order_test_record(
+			item=item, company=company, planned_start_date=add_days(now(), 60), qty=20, skip_transfer=1
+		)
+		job_card = frappe.db.get_value("Job Card", {"work_order": wo_order.name}, "name")
 		update_job_card(job_card, 10)
 
 		stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 10))
@@ -836,12 +933,12 @@
 		wo_order.load_from_db()
 		for row in wo_order.operations:
 			n_dict = row.as_dict()
-			n_dict['qty'] = 10
-			n_dict['pending_qty'] = 10
+			n_dict["qty"] = 10
+			n_dict["pending_qty"] = 10
 			operations.append(n_dict)
 
 		make_job_card(wo_order.name, operations)
-		job_card = frappe.db.get_value('Job Card', {'work_order': wo_order.name, 'docstatus': 0}, 'name')
+		job_card = frappe.db.get_value("Job Card", {"work_order": wo_order.name, "docstatus": 0}, "name")
 		update_job_card(job_card, 10)
 
 		stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 10))
@@ -850,181 +947,313 @@
 				self.assertEqual(row.qty, 2)
 
 	def test_close_work_order(self):
-		items = ['Test FG Item for Closed WO', 'Test RM Item 1 for Closed WO',
-			'Test RM Item 2 for Closed WO']
+		items = [
+			"Test FG Item for Closed WO",
+			"Test RM Item 1 for Closed WO",
+			"Test RM Item 2 for Closed WO",
+		]
 
-		company = '_Test Company with perpetual inventory'
+		company = "_Test Company with perpetual inventory"
 		for item_code in items:
-			create_item(item_code = item_code, is_stock_item = 1,
-				is_purchase_item=1, opening_stock=100, valuation_rate=10, company=company, warehouse='Stores - TCP1')
+			create_item(
+				item_code=item_code,
+				is_stock_item=1,
+				is_purchase_item=1,
+				opening_stock=100,
+				valuation_rate=10,
+				company=company,
+				warehouse="Stores - TCP1",
+			)
 
-		item = 'Test FG Item for Closed WO'
-		raw_materials = ['Test RM Item 1 for Closed WO', 'Test RM Item 2 for Closed WO']
-		if not frappe.db.get_value('BOM', {'item': item}):
-			bom = make_bom(item=item, source_warehouse='Stores - TCP1', raw_materials=raw_materials, do_not_save=True)
+		item = "Test FG Item for Closed WO"
+		raw_materials = ["Test RM Item 1 for Closed WO", "Test RM Item 2 for Closed WO"]
+		if not frappe.db.get_value("BOM", {"item": item}):
+			bom = make_bom(
+				item=item, source_warehouse="Stores - TCP1", raw_materials=raw_materials, do_not_save=True
+			)
 			bom.with_operations = 1
-			bom.append('operations', {
-				'operation': '_Test Operation 1',
-				'workstation': '_Test Workstation 1',
-				'hour_rate': 20,
-				'time_in_mins': 60
-			})
+			bom.append(
+				"operations",
+				{
+					"operation": "_Test Operation 1",
+					"workstation": "_Test Workstation 1",
+					"hour_rate": 20,
+					"time_in_mins": 60,
+				},
+			)
 
 			bom.submit()
 
-		wo_order = make_wo_order_test_record(item=item, company=company, planned_start_date=now(), qty=20, skip_transfer=1)
-		job_cards = frappe.db.get_value('Job Card', {'work_order': wo_order.name}, 'name')
+		wo_order = make_wo_order_test_record(
+			item=item, company=company, planned_start_date=now(), qty=20, skip_transfer=1
+		)
+		job_cards = frappe.db.get_value("Job Card", {"work_order": wo_order.name}, "name")
 
 		if len(job_cards) == len(bom.operations):
 			for jc in job_cards:
-				job_card_doc = frappe.get_doc('Job Card', jc)
-				job_card_doc.append('time_logs', {
-					'from_time': now(),
-					'time_in_mins': 60,
-					'completed_qty': job_card_doc.for_quantity
-				})
+				job_card_doc = frappe.get_doc("Job Card", jc)
+				job_card_doc.append(
+					"time_logs",
+					{"from_time": now(), "time_in_mins": 60, "completed_qty": job_card_doc.for_quantity},
+				)
 
 				job_card_doc.submit()
 
 			close_work_order(wo_order, "Closed")
-			self.assertEqual(wo_order.get('status'), "Closed")
+			self.assertEqual(wo_order.get("status"), "Closed")
 
 	def test_fix_time_operations(self):
-		bom = frappe.get_doc({
-			"doctype": "BOM",
-			"item": "_Test FG Item 2",
-			"is_active": 1,
-			"is_default": 1,
-			"quantity": 1.0,
-			"with_operations": 1,
-			"operations": [
-				{
-					"operation": "_Test Operation 1",
-					"description": "_Test",
-					"workstation": "_Test Workstation 1",
-					"time_in_mins": 60,
-					"operating_cost": 140,
-					"fixed_time": 1
-				}
-			],
-			"items": [
-				{
-					"amount": 5000.0,
-					"doctype": "BOM Item",
-					"item_code": "_Test Item",
-					"parentfield": "items",
-					"qty": 1.0,
-					"rate": 5000.0,
-				},
-			],
-		})
+		bom = frappe.get_doc(
+			{
+				"doctype": "BOM",
+				"item": "_Test FG Item 2",
+				"is_active": 1,
+				"is_default": 1,
+				"quantity": 1.0,
+				"with_operations": 1,
+				"operations": [
+					{
+						"operation": "_Test Operation 1",
+						"description": "_Test",
+						"workstation": "_Test Workstation 1",
+						"time_in_mins": 60,
+						"operating_cost": 140,
+						"fixed_time": 1,
+					}
+				],
+				"items": [
+					{
+						"amount": 5000.0,
+						"doctype": "BOM Item",
+						"item_code": "_Test Item",
+						"parentfield": "items",
+						"qty": 1.0,
+						"rate": 5000.0,
+					},
+				],
+			}
+		)
 		bom.save()
 		bom.submit()
 
-
-		wo1 = make_wo_order_test_record(item=bom.item, bom_no=bom.name, qty=1, skip_transfer=1, do_not_submit=1)
-		wo2 = make_wo_order_test_record(item=bom.item, bom_no=bom.name, qty=2, skip_transfer=1, do_not_submit=1)
+		wo1 = make_wo_order_test_record(
+			item=bom.item, bom_no=bom.name, qty=1, skip_transfer=1, do_not_submit=1
+		)
+		wo2 = make_wo_order_test_record(
+			item=bom.item, bom_no=bom.name, qty=2, skip_transfer=1, do_not_submit=1
+		)
 
 		self.assertEqual(wo1.operations[0].time_in_mins, wo2.operations[0].time_in_mins)
 
 	def test_partial_manufacture_entries(self):
 		cancel_stock_entry = []
 
-		frappe.db.set_value("Manufacturing Settings", None,
-			"backflush_raw_materials_based_on", "Material Transferred for Manufacture")
+		frappe.db.set_value(
+			"Manufacturing Settings",
+			None,
+			"backflush_raw_materials_based_on",
+			"Material Transferred for Manufacture",
+		)
 
 		wo_order = make_wo_order_test_record(planned_start_date=now(), qty=100)
-		ste1 = test_stock_entry.make_stock_entry(item_code="_Test Item",
-			target="_Test Warehouse - _TC", qty=120, basic_rate=5000.0)
-		ste2 = test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
-			target="_Test Warehouse - _TC", qty=240, basic_rate=1000.0)
+		ste1 = test_stock_entry.make_stock_entry(
+			item_code="_Test Item", target="_Test Warehouse - _TC", qty=120, basic_rate=5000.0
+		)
+		ste2 = test_stock_entry.make_stock_entry(
+			item_code="_Test Item Home Desktop 100",
+			target="_Test Warehouse - _TC",
+			qty=240,
+			basic_rate=1000.0,
+		)
 
 		cancel_stock_entry.extend([ste1.name, ste2.name])
 
 		sm = frappe.get_doc(make_stock_entry(wo_order.name, "Material Transfer for Manufacture", 100))
-		for row in sm.get('items'):
-			if row.get('item_code') == '_Test Item':
+		for row in sm.get("items"):
+			if row.get("item_code") == "_Test Item":
 				row.qty = 110
 
 		sm.submit()
 		cancel_stock_entry.append(sm.name)
 
 		s = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 90))
-		for row in s.get('items'):
-			if row.get('item_code') == '_Test Item':
-				self.assertEqual(row.get('qty'), 100)
+		for row in s.get("items"):
+			if row.get("item_code") == "_Test Item":
+				self.assertEqual(row.get("qty"), 100)
 		s.submit()
 		cancel_stock_entry.append(s.name)
 
 		s1 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 5))
-		for row in s1.get('items'):
-			if row.get('item_code') == '_Test Item':
-				self.assertEqual(row.get('qty'), 5)
+		for row in s1.get("items"):
+			if row.get("item_code") == "_Test Item":
+				self.assertEqual(row.get("qty"), 5)
 		s1.submit()
 		cancel_stock_entry.append(s1.name)
 
 		s2 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 5))
-		for row in s2.get('items'):
-			if row.get('item_code') == '_Test Item':
-				self.assertEqual(row.get('qty'), 5)
+		for row in s2.get("items"):
+			if row.get("item_code") == "_Test Item":
+				self.assertEqual(row.get("qty"), 5)
 
 		cancel_stock_entry.reverse()
 		for ste in cancel_stock_entry:
 			doc = frappe.get_doc("Stock Entry", ste)
 			doc.cancel()
 
-		frappe.db.set_value("Manufacturing Settings", None,
-			"backflush_raw_materials_based_on", "BOM")
+		frappe.db.set_value("Manufacturing Settings", None, "backflush_raw_materials_based_on", "BOM")
+
+	@change_settings("Manufacturing Settings", {"make_serial_no_batch_from_work_order": 1})
+	def test_auto_batch_creation(self):
+		from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
+
+		fg_item = frappe.generate_hash(length=20)
+		child_item = frappe.generate_hash(length=20)
+
+		bom_tree = {fg_item: {child_item: {}}}
+
+		create_nested_bom(bom_tree, prefix="")
+
+		item = frappe.get_doc("Item", fg_item)
+		item.has_batch_no = 1
+		item.create_new_batch = 0
+		item.save()
+
+		try:
+			make_wo_order_test_record(item=fg_item)
+		except frappe.MandatoryError:
+			self.fail("Batch generation causing failing in Work Order")
+
+	@change_settings(
+		"Manufacturing Settings",
+		{"backflush_raw_materials_based_on": "Material Transferred for Manufacture"},
+	)
+	def test_manufacture_entry_mapped_idx_with_exploded_bom(self):
+		"""Test if WO containing BOM with partial exploded items and scrap items, maps idx correctly."""
+		test_stock_entry.make_stock_entry(
+			item_code="_Test Item",
+			target="_Test Warehouse - _TC",
+			basic_rate=5000.0,
+			qty=2,
+		)
+		test_stock_entry.make_stock_entry(
+			item_code="_Test Item Home Desktop 100",
+			target="_Test Warehouse - _TC",
+			basic_rate=1000.0,
+			qty=2,
+		)
+
+		wo_order = make_wo_order_test_record(
+			qty=1,
+			use_multi_level_bom=1,
+			skip_transfer=1,
+		)
+
+		ste_manu = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 1))
+
+		for index, row in enumerate(ste_manu.get("items"), start=1):
+			self.assertEqual(index, row.idx)
+
+	@change_settings(
+		"Manufacturing Settings",
+		{"backflush_raw_materials_based_on": "Material Transferred for Manufacture"},
+	)
+	def test_work_order_multiple_material_transfer(self):
+		"""
+		Test transferring multiple RMs in separate Stock Entries.
+		"""
+		work_order = make_wo_order_test_record(planned_start_date=now(), qty=1)
+		test_stock_entry.make_stock_entry(  # stock up RM
+			item_code="_Test Item",
+			target="_Test Warehouse - _TC",
+			qty=1,
+			basic_rate=5000.0,
+		)
+		test_stock_entry.make_stock_entry(  # stock up RM
+			item_code="_Test Item Home Desktop 100",
+			target="_Test Warehouse - _TC",
+			qty=2,
+			basic_rate=1000.0,
+		)
+
+		transfer_entry = frappe.get_doc(
+			make_stock_entry(work_order.name, "Material Transfer for Manufacture", 1)
+		)
+		del transfer_entry.get("items")[0]  # transfer only one RM
+		transfer_entry.submit()
+
+		# WO's "Material Transferred for Mfg" shows all is transferred, one RM is pending
+		work_order.reload()
+		self.assertEqual(work_order.material_transferred_for_manufacturing, 1)
+		self.assertEqual(work_order.required_items[0].transferred_qty, 0)
+		self.assertEqual(work_order.required_items[1].transferred_qty, 2)
+
+		final_transfer_entry = frappe.get_doc(  # transfer last RM with For Quantity = 0
+			make_stock_entry(work_order.name, "Material Transfer for Manufacture", 0)
+		)
+		final_transfer_entry.save()
+
+		self.assertEqual(final_transfer_entry.fg_completed_qty, 0.0)
+		self.assertEqual(final_transfer_entry.items[0].qty, 1)
+
+		final_transfer_entry.submit()
+		work_order.reload()
+
+		# WO's "Material Transferred for Mfg" shows all is transferred, no RM is pending
+		self.assertEqual(work_order.material_transferred_for_manufacturing, 1)
+		self.assertEqual(work_order.required_items[0].transferred_qty, 1)
+		self.assertEqual(work_order.required_items[1].transferred_qty, 2)
+
 
 def update_job_card(job_card, jc_qty=None):
-	employee = frappe.db.get_value('Employee', {'status': 'Active'}, 'name')
-	job_card_doc = frappe.get_doc('Job Card', job_card)
-	job_card_doc.set('scrap_items', [
-		{
-			'item_code': 'Test RM Item 1 for Scrap Item Test',
-			'stock_qty': 2
-		},
-		{
-			'item_code': 'Test RM Item 2 for Scrap Item Test',
-			'stock_qty': 2
-		},
-	])
+	employee = frappe.db.get_value("Employee", {"status": "Active"}, "name")
+	job_card_doc = frappe.get_doc("Job Card", job_card)
+	job_card_doc.set(
+		"scrap_items",
+		[
+			{"item_code": "Test RM Item 1 for Scrap Item Test", "stock_qty": 2},
+			{"item_code": "Test RM Item 2 for Scrap Item Test", "stock_qty": 2},
+		],
+	)
 
 	if jc_qty:
 		job_card_doc.for_quantity = jc_qty
 
-	job_card_doc.append('time_logs', {
-		'from_time': now(),
-		'employee': employee,
-		'time_in_mins': 60,
-		'completed_qty': job_card_doc.for_quantity
-	})
+	job_card_doc.append(
+		"time_logs",
+		{
+			"from_time": now(),
+			"employee": employee,
+			"time_in_mins": 60,
+			"completed_qty": job_card_doc.for_quantity,
+		},
+	)
 
 	job_card_doc.submit()
 
 
 def get_scrap_item_details(bom_no):
 	scrap_items = {}
-	for item in frappe.db.sql("""select item_code, stock_qty from `tabBOM Scrap Item`
-		where parent = %s""", bom_no, as_dict=1):
+	for item in frappe.db.sql(
+		"""select item_code, stock_qty from `tabBOM Scrap Item`
+		where parent = %s""",
+		bom_no,
+		as_dict=1,
+	):
 		scrap_items[item.item_code] = item.stock_qty
 
 	return scrap_items
 
+
 def allow_overproduction(fieldname, percentage):
 	doc = frappe.get_doc("Manufacturing Settings")
-	doc.update({
-		fieldname: percentage
-	})
+	doc.update({fieldname: percentage})
 	doc.save()
 
+
 def make_wo_order_test_record(**args):
 	args = frappe._dict(args)
 	if args.company and args.company != "_Test Company":
-		warehouse_map = {
-			"fg_warehouse": "_Test FG Warehouse",
-			"wip_warehouse": "_Test WIP Warehouse"
-		}
+		warehouse_map = {"fg_warehouse": "_Test FG Warehouse", "wip_warehouse": "_Test WIP Warehouse"}
 
 		for attr, wh_name in warehouse_map.items():
 			if not args.get(attr):
@@ -1032,16 +1261,17 @@
 
 	wo_order = frappe.new_doc("Work Order")
 	wo_order.production_item = args.production_item or args.item or args.item_code or "_Test FG Item"
-	wo_order.bom_no = args.bom_no or frappe.db.get_value("BOM", {"item": wo_order.production_item,
-		"is_active": 1, "is_default": 1})
+	wo_order.bom_no = args.bom_no or frappe.db.get_value(
+		"BOM", {"item": wo_order.production_item, "is_active": 1, "is_default": 1}
+	)
 	wo_order.qty = args.qty or 10
 	wo_order.wip_warehouse = args.wip_warehouse or "_Test Warehouse - _TC"
 	wo_order.fg_warehouse = args.fg_warehouse or "_Test Warehouse 1 - _TC"
 	wo_order.scrap_warehouse = args.fg_warehouse or "_Test Scrap Warehouse - _TC"
 	wo_order.company = args.company or "_Test Company"
 	wo_order.stock_uom = args.stock_uom or "_Test UOM"
-	wo_order.use_multi_level_bom= args.use_multi_level_bom or 0
-	wo_order.skip_transfer=args.skip_transfer or 0
+	wo_order.use_multi_level_bom = args.use_multi_level_bom or 0
+	wo_order.skip_transfer = args.skip_transfer or 0
 	wo_order.get_items_and_operations_from_bom()
 	wo_order.sales_order = args.sales_order or None
 	wo_order.planned_start_date = args.planned_start_date or now()
@@ -1058,4 +1288,5 @@
 			wo_order.submit()
 	return wo_order
 
-test_records = frappe.get_test_records('Work Order')
+
+test_records = frappe.get_test_records("Work Order")
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index 6433a99..20f1503 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -540,8 +540,10 @@
 				|| frm.doc.transfer_material_against == 'Job Card') ? 0 : 1;
 
 			if (show_start_btn) {
-				if ((flt(doc.material_transferred_for_manufacturing) < flt(doc.qty))
-					&& frm.doc.status != 'Stopped') {
+				let pending_to_transfer = frm.doc.required_items.some(
+					item => flt(item.transferred_qty) < flt(item.required_qty)
+				);
+				if (pending_to_transfer && frm.doc.status != 'Stopped') {
 					frm.has_start_btn = true;
 					frm.add_custom_button(__('Create Pick List'), function() {
 						erpnext.work_order.create_pick_list(frm);
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 374ab86..7b86253 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -42,11 +42,26 @@
 from erpnext.utilities.transaction_base import validate_uom_is_integer
 
 
-class OverProductionError(frappe.ValidationError): pass
-class CapacityError(frappe.ValidationError): pass
-class StockOverProductionError(frappe.ValidationError): pass
-class OperationTooLongError(frappe.ValidationError): pass
-class ItemHasVariantError(frappe.ValidationError): pass
+class OverProductionError(frappe.ValidationError):
+	pass
+
+
+class CapacityError(frappe.ValidationError):
+	pass
+
+
+class StockOverProductionError(frappe.ValidationError):
+	pass
+
+
+class OperationTooLongError(frappe.ValidationError):
+	pass
+
+
+class ItemHasVariantError(frappe.ValidationError):
+	pass
+
+
 class SerialNoQtyError(frappe.ValidationError):
 	pass
 
@@ -74,12 +89,13 @@
 
 		validate_uom_is_integer(self, "stock_uom", ["qty", "produced_qty"])
 
-		self.set_required_items(reset_only_qty = len(self.get("required_items")))
+		self.set_required_items(reset_only_qty=len(self.get("required_items")))
 
 	def validate_sales_order(self):
 		if self.sales_order:
 			self.check_sales_order_on_hold_or_close()
-			so = frappe.db.sql("""
+			so = frappe.db.sql(
+				"""
 				select so.name, so_item.delivery_date, so.project
 				from `tabSales Order` so
 				inner join `tabSales Order Item` so_item on so_item.parent = so.name
@@ -88,10 +104,14 @@
 					and so.skip_delivery_note  = 0 and (
 					so_item.item_code=%s or
 					pk_item.item_code=%s )
-			""", (self.sales_order, self.production_item, self.production_item), as_dict=1)
+			""",
+				(self.sales_order, self.production_item, self.production_item),
+				as_dict=1,
+			)
 
 			if not so:
-				so = frappe.db.sql("""
+				so = frappe.db.sql(
+					"""
 					select
 						so.name, so_item.delivery_date, so.project
 					from
@@ -102,7 +122,10 @@
 						and so.skip_delivery_note = 0
 						and so_item.item_code = packed_item.parent_item
 						and so.docstatus = 1 and packed_item.item_code=%s
-				""", (self.sales_order, self.production_item), as_dict=1)
+				""",
+					(self.sales_order, self.production_item),
+					as_dict=1,
+				)
 
 			if len(so):
 				if not self.expected_delivery_date:
@@ -123,7 +146,9 @@
 
 	def set_default_warehouse(self):
 		if not self.wip_warehouse:
-			self.wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_wip_warehouse")
+			self.wip_warehouse = frappe.db.get_single_value(
+				"Manufacturing Settings", "default_wip_warehouse"
+			)
 		if not self.fg_warehouse:
 			self.fg_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_fg_warehouse")
 
@@ -145,40 +170,55 @@
 			self.planned_operating_cost += flt(d.planned_operating_cost)
 			self.actual_operating_cost += flt(d.actual_operating_cost)
 
-		variable_cost = self.actual_operating_cost if self.actual_operating_cost \
-			else self.planned_operating_cost
+		variable_cost = (
+			self.actual_operating_cost if self.actual_operating_cost else self.planned_operating_cost
+		)
 
-		self.total_operating_cost = (flt(self.additional_operating_cost)
-			+ flt(variable_cost) + flt(self.corrective_operation_cost))
+		self.total_operating_cost = (
+			flt(self.additional_operating_cost) + flt(variable_cost) + flt(self.corrective_operation_cost)
+		)
 
 	def validate_work_order_against_so(self):
 		# already ordered qty
-		ordered_qty_against_so = frappe.db.sql("""select sum(qty) from `tabWork Order`
+		ordered_qty_against_so = frappe.db.sql(
+			"""select sum(qty) from `tabWork Order`
 			where production_item = %s and sales_order = %s and docstatus < 2 and name != %s""",
-			(self.production_item, self.sales_order, self.name))[0][0]
+			(self.production_item, self.sales_order, self.name),
+		)[0][0]
 
 		total_qty = flt(ordered_qty_against_so) + flt(self.qty)
 
 		# get qty from Sales Order Item table
-		so_item_qty = frappe.db.sql("""select sum(stock_qty) from `tabSales Order Item`
+		so_item_qty = frappe.db.sql(
+			"""select sum(stock_qty) from `tabSales Order Item`
 			where parent = %s and item_code = %s""",
-			(self.sales_order, self.production_item))[0][0]
+			(self.sales_order, self.production_item),
+		)[0][0]
 		# get qty from Packing Item table
-		dnpi_qty = frappe.db.sql("""select sum(qty) from `tabPacked Item`
+		dnpi_qty = frappe.db.sql(
+			"""select sum(qty) from `tabPacked Item`
 			where parent = %s and parenttype = 'Sales Order' and item_code = %s""",
-			(self.sales_order, self.production_item))[0][0]
+			(self.sales_order, self.production_item),
+		)[0][0]
 		# total qty in SO
 		so_qty = flt(so_item_qty) + flt(dnpi_qty)
 
-		allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings",
-			"overproduction_percentage_for_sales_order"))
+		allowance_percentage = flt(
+			frappe.db.get_single_value(
+				"Manufacturing Settings", "overproduction_percentage_for_sales_order"
+			)
+		)
 
-		if total_qty > so_qty + (allowance_percentage/100 * so_qty):
-			frappe.throw(_("Cannot produce more Item {0} than Sales Order quantity {1}")
-				.format(self.production_item, so_qty), OverProductionError)
+		if total_qty > so_qty + (allowance_percentage / 100 * so_qty):
+			frappe.throw(
+				_("Cannot produce more Item {0} than Sales Order quantity {1}").format(
+					self.production_item, so_qty
+				),
+				OverProductionError,
+			)
 
 	def update_status(self, status=None):
-		'''Update status of work order if unknown'''
+		"""Update status of work order if unknown"""
 		if status != "Stopped" and status != "Closed":
 			status = self.get_status(status)
 
@@ -190,17 +230,22 @@
 		return status
 
 	def get_status(self, status=None):
-		'''Return the status based on stock entries against this work order'''
+		"""Return the status based on stock entries against this work order"""
 		if not status:
 			status = self.status
 
-		if self.docstatus==0:
-			status = 'Draft'
-		elif self.docstatus==1:
-			if status != 'Stopped':
-				stock_entries = frappe._dict(frappe.db.sql("""select purpose, sum(fg_completed_qty)
+		if self.docstatus == 0:
+			status = "Draft"
+		elif self.docstatus == 1:
+			if status != "Stopped":
+				stock_entries = frappe._dict(
+					frappe.db.sql(
+						"""select purpose, sum(fg_completed_qty)
 					from `tabStock Entry` where work_order=%s and docstatus=1
-					group by purpose""", self.name))
+					group by purpose""",
+						self.name,
+					)
+				)
 
 				status = "Not Started"
 				if stock_entries:
@@ -209,31 +254,46 @@
 					if flt(produced_qty) >= flt(self.qty):
 						status = "Completed"
 		else:
-			status = 'Cancelled'
+			status = "Cancelled"
 
 		return status
 
 	def update_work_order_qty(self):
 		"""Update **Manufactured Qty** and **Material Transferred for Qty** in Work Order
-			based on Stock Entry"""
+		based on Stock Entry"""
 
-		allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings",
-			"overproduction_percentage_for_work_order"))
+		allowance_percentage = flt(
+			frappe.db.get_single_value("Manufacturing Settings", "overproduction_percentage_for_work_order")
+		)
 
-		for purpose, fieldname in (("Manufacture", "produced_qty"),
-			("Material Transfer for Manufacture", "material_transferred_for_manufacturing")):
-			if (purpose == 'Material Transfer for Manufacture' and
-				self.operations and self.transfer_material_against == 'Job Card'):
+		for purpose, fieldname in (
+			("Manufacture", "produced_qty"),
+			("Material Transfer for Manufacture", "material_transferred_for_manufacturing"),
+		):
+			if (
+				purpose == "Material Transfer for Manufacture"
+				and self.operations
+				and self.transfer_material_against == "Job Card"
+			):
 				continue
 
-			qty = flt(frappe.db.sql("""select sum(fg_completed_qty)
+			qty = flt(
+				frappe.db.sql(
+					"""select sum(fg_completed_qty)
 				from `tabStock Entry` where work_order=%s and docstatus=1
-				and purpose=%s""", (self.name, purpose))[0][0])
+				and purpose=%s""",
+					(self.name, purpose),
+				)[0][0]
+			)
 
-			completed_qty = self.qty + (allowance_percentage/100 * self.qty)
+			completed_qty = self.qty + (allowance_percentage / 100 * self.qty)
 			if qty > completed_qty:
-				frappe.throw(_("{0} ({1}) cannot be greater than planned quantity ({2}) in Work Order {3}").format(\
-					self.meta.get_label(fieldname), qty, completed_qty, self.name), StockOverProductionError)
+				frappe.throw(
+					_("{0} ({1}) cannot be greater than planned quantity ({2}) in Work Order {3}").format(
+						self.meta.get_label(fieldname), qty, completed_qty, self.name
+					),
+					StockOverProductionError,
+				)
 
 			self.db_set(fieldname, qty)
 			self.set_process_loss_qty()
@@ -247,7 +307,9 @@
 			self.update_production_plan_status()
 
 	def set_process_loss_qty(self):
-		process_loss_qty = flt(frappe.db.sql("""
+		process_loss_qty = flt(
+			frappe.db.sql(
+				"""
 				SELECT sum(qty) FROM `tabStock Entry Detail`
 				WHERE
 					is_process_loss=1
@@ -258,21 +320,33 @@
 							AND purpose='Manufacture'
 							AND docstatus=1
 					)
-			""", (self.name, ))[0][0])
+			""",
+				(self.name,),
+			)[0][0]
+		)
 		if process_loss_qty is not None:
-			self.db_set('process_loss_qty', process_loss_qty)
+			self.db_set("process_loss_qty", process_loss_qty)
 
 	def update_production_plan_status(self):
-		production_plan = frappe.get_doc('Production Plan', self.production_plan)
+		production_plan = frappe.get_doc("Production Plan", self.production_plan)
 		produced_qty = 0
 		if self.production_plan_item:
-			total_qty = frappe.get_all("Work Order", fields = "sum(produced_qty) as produced_qty",
-				filters = {'docstatus': 1, 'production_plan': self.production_plan,
-					'production_plan_item': self.production_plan_item}, as_list=1)
+			total_qty = frappe.get_all(
+				"Work Order",
+				fields="sum(produced_qty) as produced_qty",
+				filters={
+					"docstatus": 1,
+					"production_plan": self.production_plan,
+					"production_plan_item": self.production_plan_item,
+				},
+				as_list=1,
+			)
 
 			produced_qty = total_qty[0][0] if total_qty else 0
 
-		production_plan.run_method("update_produced_pending_qty", produced_qty, self.production_plan_item)
+		production_plan.run_method(
+			"update_produced_pending_qty", produced_qty, self.production_plan_item
+		)
 
 	def before_submit(self):
 		self.create_serial_no_batch_no()
@@ -283,7 +357,9 @@
 		if not self.fg_warehouse:
 			frappe.throw(_("For Warehouse is required before Submit"))
 
-		if self.production_plan and frappe.db.exists('Production Plan Item Reference',{'parent':self.production_plan}):
+		if self.production_plan and frappe.db.exists(
+			"Production Plan Item Reference", {"parent": self.production_plan}
+		):
 			self.update_work_order_qty_in_combined_so()
 		else:
 			self.update_work_order_qty_in_so()
@@ -296,9 +372,11 @@
 
 	def on_cancel(self):
 		self.validate_cancel()
-		frappe.db.set(self,'status', 'Cancelled')
+		frappe.db.set(self, "status", "Cancelled")
 
-		if self.production_plan and frappe.db.exists('Production Plan Item Reference',{'parent':self.production_plan}):
+		if self.production_plan and frappe.db.exists(
+			"Production Plan Item Reference", {"parent": self.production_plan}
+		):
 			self.update_work_order_qty_in_combined_so()
 		else:
 			self.update_work_order_qty_in_so()
@@ -314,16 +392,15 @@
 		if not (self.has_serial_no or self.has_batch_no):
 			return
 
-		if not cint(frappe.db.get_single_value("Manufacturing Settings", "make_serial_no_batch_from_work_order")):
+		if not cint(
+			frappe.db.get_single_value("Manufacturing Settings", "make_serial_no_batch_from_work_order")
+		):
 			return
 
 		if self.has_batch_no:
 			self.create_batch_for_finished_good()
 
-		args = {
-			"item_code": self.production_item,
-			"work_order": self.name
-		}
+		args = {"item_code": self.production_item, "work_order": self.name}
 
 		if self.has_serial_no:
 			self.make_serial_nos(args)
@@ -333,6 +410,17 @@
 		if not self.batch_size:
 			self.batch_size = total_qty
 
+		batch_auto_creation = frappe.get_cached_value("Item", self.production_item, "create_new_batch")
+		if not batch_auto_creation:
+			frappe.msgprint(
+				_("Batch not created for item {} since it does not have a batch series.").format(
+					frappe.bold(self.production_item)
+				),
+				alert=True,
+				indicator="orange",
+			)
+			return
+
 		while total_qty > 0:
 			qty = self.batch_size
 			if self.batch_size >= total_qty:
@@ -344,19 +432,23 @@
 				qty = total_qty
 				total_qty = 0
 
-			make_batch(frappe._dict({
-				"item": self.production_item,
-				"qty_to_produce": qty,
-				"reference_doctype": self.doctype,
-				"reference_name": self.name
-			}))
+			make_batch(
+				frappe._dict(
+					{
+						"item": self.production_item,
+						"qty_to_produce": qty,
+						"reference_doctype": self.doctype,
+						"reference_name": self.name,
+					}
+				)
+			)
 
 	def delete_auto_created_batch_and_serial_no(self):
-		for row in frappe.get_all("Serial No", filters = {"work_order": self.name}):
+		for row in frappe.get_all("Serial No", filters={"work_order": self.name}):
 			frappe.delete_doc("Serial No", row.name)
 			self.db_set("serial_no", "")
 
-		for row in frappe.get_all("Batch", filters = {"reference_name": self.name}):
+		for row in frappe.get_all("Batch", filters={"reference_name": self.name}):
 			frappe.delete_doc("Batch", row.name)
 
 	def make_serial_nos(self, args):
@@ -371,8 +463,12 @@
 
 		serial_nos_length = len(get_serial_nos(self.serial_no))
 		if serial_nos_length != self.qty:
-			frappe.throw(_("{0} Serial Numbers required for Item {1}. You have provided {2}.")
-				.format(self.qty, self.production_item, serial_nos_length), SerialNoQtyError)
+			frappe.throw(
+				_("{0} Serial Numbers required for Item {1}. You have provided {2}.").format(
+					self.qty, self.production_item, serial_nos_length
+				),
+				SerialNoQtyError,
+			)
 
 	def create_job_card(self):
 		manufacturing_settings_doc = frappe.get_doc("Manufacturing Settings")
@@ -385,8 +481,7 @@
 			while qty > 0:
 				qty = split_qty_based_on_batch_size(self, row, qty)
 				if row.job_card_qty > 0:
-					self.prepare_data_for_job_card(row, index,
-						plan_days, enable_capacity_planning)
+					self.prepare_data_for_job_card(row, index, plan_days, enable_capacity_planning)
 
 		planned_end_date = self.operations and self.operations[-1].planned_end_time
 		if planned_end_date:
@@ -396,12 +491,14 @@
 		self.set_operation_start_end_time(index, row)
 
 		if not row.workstation:
-			frappe.throw(_("Row {0}: select the workstation against the operation {1}")
-				.format(row.idx, row.operation))
+			frappe.throw(
+				_("Row {0}: select the workstation against the operation {1}").format(row.idx, row.operation)
+			)
 
 		original_start_time = row.planned_start_time
-		job_card_doc = create_job_card(self, row, auto_create=True,
-			enable_capacity_planning=enable_capacity_planning)
+		job_card_doc = create_job_card(
+			self, row, auto_create=True, enable_capacity_planning=enable_capacity_planning
+		)
 
 		if enable_capacity_planning and job_card_doc:
 			row.planned_start_time = job_card_doc.time_logs[-1].from_time
@@ -409,22 +506,29 @@
 
 			if date_diff(row.planned_start_time, original_start_time) > plan_days:
 				frappe.message_log.pop()
-				frappe.throw(_("Unable to find the time slot in the next {0} days for the operation {1}.")
-					.format(plan_days, row.operation), CapacityError)
+				frappe.throw(
+					_("Unable to find the time slot in the next {0} days for the operation {1}.").format(
+						plan_days, row.operation
+					),
+					CapacityError,
+				)
 
 			row.db_update()
 
 	def set_operation_start_end_time(self, idx, row):
 		"""Set start and end time for given operation. If first operation, set start as
 		`planned_start_date`, else add time diff to end time of earlier operation."""
-		if idx==0:
+		if idx == 0:
 			# first operation at planned_start date
 			row.planned_start_time = self.planned_start_date
 		else:
-			row.planned_start_time = get_datetime(self.operations[idx-1].planned_end_time)\
-				+ get_mins_between_operations()
+			row.planned_start_time = (
+				get_datetime(self.operations[idx - 1].planned_end_time) + get_mins_between_operations()
+			)
 
-		row.planned_end_time = get_datetime(row.planned_start_time) + relativedelta(minutes = row.time_in_mins)
+		row.planned_end_time = get_datetime(row.planned_start_time) + relativedelta(
+			minutes=row.time_in_mins
+		)
 
 		if row.planned_start_time == row.planned_end_time:
 			frappe.throw(_("Capacity Planning Error, planned start time can not be same as end time"))
@@ -434,22 +538,35 @@
 			frappe.throw(_("Stopped Work Order cannot be cancelled, Unstop it first to cancel"))
 
 		# Check whether any stock entry exists against this Work Order
-		stock_entry = frappe.db.sql("""select name from `tabStock Entry`
-			where work_order = %s and docstatus = 1""", self.name)
+		stock_entry = frappe.db.sql(
+			"""select name from `tabStock Entry`
+			where work_order = %s and docstatus = 1""",
+			self.name,
+		)
 		if stock_entry:
-			frappe.throw(_("Cannot cancel because submitted Stock Entry {0} exists").format(frappe.utils.get_link_to_form('Stock Entry', stock_entry[0][0])))
+			frappe.throw(
+				_("Cannot cancel because submitted Stock Entry {0} exists").format(
+					frappe.utils.get_link_to_form("Stock Entry", stock_entry[0][0])
+				)
+			)
 
 	def update_planned_qty(self):
-		update_bin_qty(self.production_item, self.fg_warehouse, {
-			"planned_qty": get_planned_qty(self.production_item, self.fg_warehouse)
-		})
+		update_bin_qty(
+			self.production_item,
+			self.fg_warehouse,
+			{"planned_qty": get_planned_qty(self.production_item, self.fg_warehouse)},
+		)
 
 		if self.material_request:
 			mr_obj = frappe.get_doc("Material Request", self.material_request)
 			mr_obj.update_requested_qty([self.material_request_item])
 
 	def update_ordered_qty(self):
-		if self.production_plan and self.production_plan_item:
+		if (
+			self.production_plan
+			and self.production_plan_item
+			and not self.production_plan_sub_assembly_item
+		):
 			qty = frappe.get_value("Production Plan Item", self.production_plan_item, "ordered_qty") or 0.0
 
 			if self.docstatus == 1:
@@ -457,12 +574,11 @@
 			elif self.docstatus == 2:
 				qty -= self.qty
 
-			frappe.db.set_value('Production Plan Item',
-				self.production_plan_item, 'ordered_qty', qty)
+			frappe.db.set_value("Production Plan Item", self.production_plan_item, "ordered_qty", qty)
 
-			doc = frappe.get_doc('Production Plan', self.production_plan)
+			doc = frappe.get_doc("Production Plan", self.production_plan)
 			doc.set_status()
-			doc.db_set('status', doc.status)
+			doc.db_set("status", doc.status)
 
 	def update_work_order_qty_in_so(self):
 		if not self.sales_order and not self.sales_order_item:
@@ -470,8 +586,11 @@
 
 		total_bundle_qty = 1
 		if self.product_bundle_item:
-			total_bundle_qty = frappe.db.sql(""" select sum(qty) from
-				`tabProduct Bundle Item` where parent = %s""", (frappe.db.escape(self.product_bundle_item)))[0][0]
+			total_bundle_qty = frappe.db.sql(
+				""" select sum(qty) from
+				`tabProduct Bundle Item` where parent = %s""",
+				(frappe.db.escape(self.product_bundle_item)),
+			)[0][0]
 
 			if not total_bundle_qty:
 				# product bundle is 0 (product bundle allows 0 qty for items)
@@ -479,49 +598,78 @@
 
 		cond = "product_bundle_item = %s" if self.product_bundle_item else "production_item = %s"
 
-		qty = frappe.db.sql(""" select sum(qty) from
+		qty = frappe.db.sql(
+			""" select sum(qty) from
 			`tabWork Order` where sales_order = %s and docstatus = 1 and {0}
-			""".format(cond), (self.sales_order, (self.product_bundle_item or self.production_item)), as_list=1)
+			""".format(
+				cond
+			),
+			(self.sales_order, (self.product_bundle_item or self.production_item)),
+			as_list=1,
+		)
 
 		work_order_qty = qty[0][0] if qty and qty[0][0] else 0
-		frappe.db.set_value('Sales Order Item',
-			self.sales_order_item, 'work_order_qty', flt(work_order_qty/total_bundle_qty, 2))
+		frappe.db.set_value(
+			"Sales Order Item",
+			self.sales_order_item,
+			"work_order_qty",
+			flt(work_order_qty / total_bundle_qty, 2),
+		)
 
 	def update_work_order_qty_in_combined_so(self):
 		total_bundle_qty = 1
 		if self.product_bundle_item:
-			total_bundle_qty = frappe.db.sql(""" select sum(qty) from
-				`tabProduct Bundle Item` where parent = %s""", (frappe.db.escape(self.product_bundle_item)))[0][0]
+			total_bundle_qty = frappe.db.sql(
+				""" select sum(qty) from
+				`tabProduct Bundle Item` where parent = %s""",
+				(frappe.db.escape(self.product_bundle_item)),
+			)[0][0]
 
 			if not total_bundle_qty:
 				# product bundle is 0 (product bundle allows 0 qty for items)
 				total_bundle_qty = 1
 
-		prod_plan = frappe.get_doc('Production Plan', self.production_plan)
-		item_reference = frappe.get_value('Production Plan Item', self.production_plan_item, 'sales_order_item')
+		prod_plan = frappe.get_doc("Production Plan", self.production_plan)
+		item_reference = frappe.get_value(
+			"Production Plan Item", self.production_plan_item, "sales_order_item"
+		)
 
 		for plan_reference in prod_plan.prod_plan_references:
 			work_order_qty = 0.0
 			if plan_reference.item_reference == item_reference:
 				if self.docstatus == 1:
 					work_order_qty = flt(plan_reference.qty) / total_bundle_qty
-				frappe.db.set_value('Sales Order Item',
-					plan_reference.sales_order_item, 'work_order_qty', work_order_qty)
+				frappe.db.set_value(
+					"Sales Order Item", plan_reference.sales_order_item, "work_order_qty", work_order_qty
+				)
 
 	def update_completed_qty_in_material_request(self):
 		if self.material_request:
-			frappe.get_doc("Material Request", self.material_request).update_completed_qty([self.material_request_item])
+			frappe.get_doc("Material Request", self.material_request).update_completed_qty(
+				[self.material_request_item]
+			)
 
 	def set_work_order_operations(self):
 		"""Fetch operations from BOM and set in 'Work Order'"""
 
 		def _get_operations(bom_no, qty=1):
-			data = frappe.get_all("BOM Operation",
-					filters={"parent": bom_no},
-					fields=["operation", "description", "workstation", "idx",
-						"base_hour_rate as hour_rate", "time_in_mins", "parent as bom",
-						"batch_size", "sequence_id", "fixed_time"],
-					order_by="idx")
+			data = frappe.get_all(
+				"BOM Operation",
+				filters={"parent": bom_no},
+				fields=[
+					"operation",
+					"description",
+					"workstation",
+					"idx",
+					"base_hour_rate as hour_rate",
+					"time_in_mins",
+					"parent as bom",
+					"batch_size",
+					"sequence_id",
+					"fixed_time",
+				],
+				order_by="idx",
+			)
 
 			for d in data:
 				if not d.fixed_time:
@@ -530,9 +678,8 @@
 
 			return data
 
-
-		self.set('operations', [])
-		if not self.bom_no or not frappe.get_cached_value('BOM', self.bom_no, 'with_operations'):
+		self.set("operations", [])
+		if not self.bom_no or not frappe.get_cached_value("BOM", self.bom_no, "with_operations"):
 			return
 
 		operations = []
@@ -546,12 +693,12 @@
 					operations.extend(_get_operations(node.name, qty=node.exploded_qty))
 
 		bom_qty = frappe.get_cached_value("BOM", self.bom_no, "quantity")
-		operations.extend(_get_operations(self.bom_no, qty=1.0/bom_qty))
+		operations.extend(_get_operations(self.bom_no, qty=1.0 / bom_qty))
 
 		for correct_index, operation in enumerate(operations, start=1):
 			operation.idx = correct_index
 
-		self.set('operations', operations)
+		self.set("operations", operations)
 		self.calculate_time()
 
 	def calculate_time(self):
@@ -567,16 +714,27 @@
 		holidays = {}
 
 		if holiday_list not in holidays:
-			holiday_list_days = [getdate(d[0]) for d in frappe.get_all("Holiday", fields=["holiday_date"],
-				filters={"parent": holiday_list}, order_by="holiday_date", limit_page_length=0, as_list=1)]
+			holiday_list_days = [
+				getdate(d[0])
+				for d in frappe.get_all(
+					"Holiday",
+					fields=["holiday_date"],
+					filters={"parent": holiday_list},
+					order_by="holiday_date",
+					limit_page_length=0,
+					as_list=1,
+				)
+			]
 
 			holidays[holiday_list] = holiday_list_days
 
 		return holidays[holiday_list]
 
 	def update_operation_status(self):
-		allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings", "overproduction_percentage_for_work_order"))
-		max_allowed_qty_for_wo = flt(self.qty) + (allowance_percentage/100 * flt(self.qty))
+		allowance_percentage = flt(
+			frappe.db.get_single_value("Manufacturing Settings", "overproduction_percentage_for_work_order")
+		)
+		max_allowed_qty_for_wo = flt(self.qty) + (allowance_percentage / 100 * flt(self.qty))
 
 		for d in self.get("operations"):
 			if not d.completed_qty:
@@ -592,7 +750,9 @@
 
 	def set_actual_dates(self):
 		if self.get("operations"):
-			actual_start_dates = [d.actual_start_time for d in self.get("operations") if d.actual_start_time]
+			actual_start_dates = [
+				d.actual_start_time for d in self.get("operations") if d.actual_start_time
+			]
 			if actual_start_dates:
 				self.actual_start_date = min(actual_start_dates)
 
@@ -600,20 +760,21 @@
 			if actual_end_dates:
 				self.actual_end_date = max(actual_end_dates)
 		else:
-			data = frappe.get_all("Stock Entry",
-				fields = ["timestamp(posting_date, posting_time) as posting_datetime"],
-				filters = {
+			data = frappe.get_all(
+				"Stock Entry",
+				fields=["timestamp(posting_date, posting_time) as posting_datetime"],
+				filters={
 					"work_order": self.name,
-					"purpose": ("in", ["Material Transfer for Manufacture", "Manufacture"])
-				}
+					"purpose": ("in", ["Material Transfer for Manufacture", "Manufacture"]),
+				},
 			)
 
 			if data and len(data):
 				dates = [d.posting_datetime for d in data]
-				self.db_set('actual_start_date', min(dates))
+				self.db_set("actual_start_date", min(dates))
 
 				if self.status == "Completed":
-					self.db_set('actual_end_date', max(dates))
+					self.db_set("actual_end_date", max(dates))
 
 		self.set_lead_time()
 
@@ -636,20 +797,39 @@
 		if not self.qty > 0:
 			frappe.throw(_("Quantity to Manufacture must be greater than 0."))
 
-		if self.production_plan and self.production_plan_item:
-			qty_dict = frappe.db.get_value("Production Plan Item", self.production_plan_item, ["planned_qty", "ordered_qty"], as_dict=1)
+		if (
+			self.production_plan
+			and self.production_plan_item
+			and not self.production_plan_sub_assembly_item
+		):
+			qty_dict = frappe.db.get_value(
+				"Production Plan Item", self.production_plan_item, ["planned_qty", "ordered_qty"], as_dict=1
+			)
 
-			allowance_qty =flt(frappe.db.get_single_value("Manufacturing Settings",
-			"overproduction_percentage_for_work_order"))/100 * qty_dict.get("planned_qty", 0)
+			if not qty_dict:
+				return
+
+			allowance_qty = (
+				flt(
+					frappe.db.get_single_value(
+						"Manufacturing Settings", "overproduction_percentage_for_work_order"
+					)
+				)
+				/ 100
+				* qty_dict.get("planned_qty", 0)
+			)
 
 			max_qty = qty_dict.get("planned_qty", 0) + allowance_qty - qty_dict.get("ordered_qty", 0)
 
-			if max_qty < 1:
-				frappe.throw(_("Cannot produce more item for {0}")
-				.format(self.production_item), OverProductionError)
+			if not max_qty > 0:
+				frappe.throw(
+					_("Cannot produce more item for {0}").format(self.production_item), OverProductionError
+				)
 			elif self.qty > max_qty:
-				frappe.throw(_("Cannot produce more than {0} items for {1}")
-				.format(max_qty, self.production_item), OverProductionError)
+				frappe.throw(
+					_("Cannot produce more than {0} items for {1}").format(max_qty, self.production_item),
+					OverProductionError,
+				)
 
 	def validate_transfer_against(self):
 		if not self.docstatus == 1:
@@ -658,8 +838,10 @@
 		if not self.operations:
 			self.transfer_material_against = "Work Order"
 		if not self.transfer_material_against:
-			frappe.throw(_("Setting {} is required").format(self.meta.get_label("transfer_material_against")), title=_("Missing value"))
-
+			frappe.throw(
+				_("Setting {} is required").format(self.meta.get_label("transfer_material_against")),
+				title=_("Missing value"),
+			)
 
 	def validate_operation_time(self):
 		for d in self.operations:
@@ -667,14 +849,14 @@
 				frappe.throw(_("Operation Time must be greater than 0 for Operation {0}").format(d.operation))
 
 	def update_required_items(self):
-		'''
+		"""
 		update bin reserved_qty_for_production
 		called from Stock Entry for production, after submit, cancel
-		'''
+		"""
 		# calculate consumed qty based on submitted stock entries
 		self.update_consumed_qty_for_required_items()
 
-		if self.docstatus==1:
+		if self.docstatus == 1:
 			# calculate transferred qty based on submitted stock entries
 			self.update_transferred_qty_for_required_items()
 
@@ -682,7 +864,7 @@
 			self.update_reserved_qty_for_production()
 
 	def update_reserved_qty_for_production(self, items=None):
-		'''update reserved_qty_for_production in bins'''
+		"""update reserved_qty_for_production in bins"""
 		for d in self.required_items:
 			if d.source_warehouse:
 				stock_bin = get_bin(d.item_code, d.source_warehouse)
@@ -704,17 +886,18 @@
 				d.available_qty_at_wip_warehouse = get_latest_stock_qty(d.item_code, self.wip_warehouse)
 
 	def set_required_items(self, reset_only_qty=False):
-		'''set required_items for production to keep track of reserved qty'''
+		"""set required_items for production to keep track of reserved qty"""
 		if not reset_only_qty:
 			self.required_items = []
 
 		operation = None
-		if self.get('operations') and len(self.operations) == 1:
+		if self.get("operations") and len(self.operations) == 1:
 			operation = self.operations[0].operation
 
 		if self.bom_no and self.qty:
-			item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=self.qty,
-				fetch_exploded = self.use_multi_level_bom)
+			item_dict = get_bom_items_as_dict(
+				self.bom_no, self.company, qty=self.qty, fetch_exploded=self.use_multi_level_bom
+			)
 
 			if reset_only_qty:
 				for d in self.get("required_items"):
@@ -724,19 +907,22 @@
 					if not d.operation:
 						d.operation = operation
 			else:
-				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,
-						'operation': item.operation or operation,
-						'item_code': item.item_code,
-						'item_name': item.item_name,
-						'description': item.description,
-						'allow_alternative_item': item.allow_alternative_item,
-						'required_qty': item.qty,
-						'source_warehouse': item.source_warehouse or item.default_warehouse,
-						'include_item_in_manufacturing': item.include_item_in_manufacturing
-					})
+				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,
+							"operation": item.operation or operation,
+							"item_code": item.item_code,
+							"item_name": item.item_name,
+							"description": item.description,
+							"allow_alternative_item": item.allow_alternative_item,
+							"required_qty": item.qty,
+							"source_warehouse": item.source_warehouse or item.default_warehouse,
+							"include_item_in_manufacturing": item.include_item_in_manufacturing,
+						},
+					)
 
 					if not self.project:
 						self.project = item.get("project")
@@ -744,32 +930,33 @@
 			self.set_available_qty()
 
 	def update_transferred_qty_for_required_items(self):
-		'''update transferred qty from submitted stock entries for that item against
-			the work order'''
+		"""update transferred qty from submitted stock entries for that item against
+		the work order"""
 
 		for d in self.required_items:
-			transferred_qty = frappe.db.sql('''select sum(qty)
+			transferred_qty = frappe.db.sql(
+				"""select sum(qty)
 				from `tabStock Entry` entry, `tabStock Entry Detail` detail
 				where
 					entry.work_order = %(name)s
-					and entry.purpose = "Material Transfer for Manufacture"
+					and entry.purpose = 'Material Transfer for Manufacture'
 					and entry.docstatus = 1
 					and detail.parent = entry.name
-					and (detail.item_code = %(item)s or detail.original_item = %(item)s)''', {
-						'name': self.name,
-						'item': d.item_code
-					})[0][0]
+					and (detail.item_code = %(item)s or detail.original_item = %(item)s)""",
+				{"name": self.name, "item": d.item_code},
+			)[0][0]
 
-			d.db_set('transferred_qty', flt(transferred_qty), update_modified = False)
+			d.db_set("transferred_qty", flt(transferred_qty), update_modified=False)
 
 	def update_consumed_qty_for_required_items(self):
-		'''
-			Update consumed qty from submitted stock entries
-			against a work order for each stock item
-		'''
+		"""
+		Update consumed qty from submitted stock entries
+		against a work order for each stock item
+		"""
 
 		for item in self.required_items:
-			consumed_qty = frappe.db.sql('''
+			consumed_qty = frappe.db.sql(
+				"""
 				SELECT
 					SUM(qty)
 				FROM
@@ -784,85 +971,97 @@
 						AND detail.s_warehouse IS NOT null
 						AND (detail.item_code = %(item)s
 							OR detail.original_item = %(item)s)
-				''', {
-					'name': self.name,
-					'item': item.item_code
-				})[0][0]
+				""",
+				{"name": self.name, "item": item.item_code},
+			)[0][0]
 
-			item.db_set('consumed_qty', flt(consumed_qty), update_modified=False)
+			item.db_set("consumed_qty", flt(consumed_qty), update_modified=False)
 
 	@frappe.whitelist()
 	def make_bom(self):
-		data = frappe.db.sql(""" select sed.item_code, sed.qty, sed.s_warehouse
+		data = frappe.db.sql(
+			""" select sed.item_code, sed.qty, sed.s_warehouse
 			from `tabStock Entry Detail` sed, `tabStock Entry` se
 			where se.name = sed.parent and se.purpose = 'Manufacture'
 			and (sed.t_warehouse is null or sed.t_warehouse = '') and se.docstatus = 1
-			and se.work_order = %s""", (self.name), as_dict=1)
+			and se.work_order = %s""",
+			(self.name),
+			as_dict=1,
+		)
 
 		bom = frappe.new_doc("BOM")
 		bom.item = self.production_item
 		bom.conversion_rate = 1
 
 		for d in data:
-			bom.append('items', {
-				'item_code': d.item_code,
-				'qty': d.qty,
-				'source_warehouse': d.s_warehouse
-			})
+			bom.append("items", {"item_code": d.item_code, "qty": d.qty, "source_warehouse": d.s_warehouse})
 
 		if self.operations:
-			bom.set('operations', self.operations)
+			bom.set("operations", self.operations)
 			bom.with_operations = 1
 
 		bom.set_bom_material_details()
 		return bom
 
 	def update_batch_produced_qty(self, stock_entry_doc):
-		if not cint(frappe.db.get_single_value("Manufacturing Settings", "make_serial_no_batch_from_work_order")):
+		if not cint(
+			frappe.db.get_single_value("Manufacturing Settings", "make_serial_no_batch_from_work_order")
+		):
 			return
 
 		for row in stock_entry_doc.items:
 			if row.batch_no and (row.is_finished_item or row.is_scrap_item):
-				qty = frappe.get_all("Stock Entry Detail", filters = {"batch_no": row.batch_no, "docstatus": 1},
-					or_filters= {"is_finished_item": 1, "is_scrap_item": 1}, fields = ["sum(qty)"], as_list=1)[0][0]
+				qty = frappe.get_all(
+					"Stock Entry Detail",
+					filters={"batch_no": row.batch_no, "docstatus": 1},
+					or_filters={"is_finished_item": 1, "is_scrap_item": 1},
+					fields=["sum(qty)"],
+					as_list=1,
+				)[0][0]
 
 				frappe.db.set_value("Batch", row.batch_no, "produced_qty", flt(qty))
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_bom_operations(doctype, txt, searchfield, start, page_len, filters):
 	if txt:
-		filters['operation'] = ('like', '%%%s%%' % txt)
+		filters["operation"] = ("like", "%%%s%%" % txt)
 
-	return frappe.get_all('BOM Operation',
-		filters = filters, fields = ['operation'], as_list=1)
+	return frappe.get_all("BOM Operation", filters=filters, fields=["operation"], as_list=1)
+
 
 @frappe.whitelist()
-def get_item_details(item, project = None, skip_bom_info=False):
-	res = frappe.db.sql("""
+def get_item_details(item, project=None, skip_bom_info=False):
+	res = frappe.db.sql(
+		"""
 		select stock_uom, description, item_name, allow_alternative_item,
 			include_item_in_manufacturing
 		from `tabItem`
 		where disabled=0
 			and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s)
 			and name=%s
-	""", (nowdate(), item), as_dict=1)
+	""",
+		(nowdate(), item),
+		as_dict=1,
+	)
 
 	if not res:
 		return {}
 
 	res = res[0]
-	if skip_bom_info: return res
+	if skip_bom_info:
+		return res
 
 	filters = {"item": item, "is_default": 1, "docstatus": 1}
 
 	if project:
 		filters = {"item": item, "project": project}
 
-	res["bom_no"] = frappe.db.get_value("BOM", filters = filters)
+	res["bom_no"] = frappe.db.get_value("BOM", filters=filters)
 
 	if not res["bom_no"]:
-		variant_of= frappe.db.get_value("Item", item, "variant_of")
+		variant_of = frappe.db.get_value("Item", item, "variant_of")
 
 		if variant_of:
 			res["bom_no"] = frappe.db.get_value("BOM", filters={"item": variant_of, "is_default": 1})
@@ -870,19 +1069,26 @@
 	if not res["bom_no"]:
 		if project:
 			res = get_item_details(item)
-			frappe.msgprint(_("Default BOM not found for Item {0} and Project {1}").format(item, project), alert=1)
+			frappe.msgprint(
+				_("Default BOM not found for Item {0} and Project {1}").format(item, project), alert=1
+			)
 		else:
 			frappe.throw(_("Default BOM for {0} not found").format(item))
 
-	bom_data = frappe.db.get_value('BOM', res['bom_no'],
-		['project', 'allow_alternative_item', 'transfer_material_against', 'item_name'], as_dict=1)
+	bom_data = frappe.db.get_value(
+		"BOM",
+		res["bom_no"],
+		["project", "allow_alternative_item", "transfer_material_against", "item_name"],
+		as_dict=1,
+	)
 
-	res['project'] = project or bom_data.pop("project")
+	res["project"] = project or bom_data.pop("project")
 	res.update(bom_data)
 	res.update(check_if_scrap_warehouse_mandatory(res["bom_no"]))
 
 	return res
 
+
 @frappe.whitelist()
 def make_work_order(bom_no, item, qty=0, project=None, variant_items=None):
 	if not frappe.has_permission("Work Order", "write"):
@@ -904,43 +1110,51 @@
 
 	return wo_doc
 
+
 def add_variant_item(variant_items, wo_doc, bom_no, table_name="items"):
 	if isinstance(variant_items, str):
 		variant_items = json.loads(variant_items)
 
 	for item in variant_items:
-		args = frappe._dict({
-			"item_code": item.get("variant_item_code"),
-			"required_qty": item.get("qty"),
-			"qty": item.get("qty"), # for bom
-			"source_warehouse": item.get("source_warehouse"),
-			"operation": item.get("operation")
-		})
+		args = frappe._dict(
+			{
+				"item_code": item.get("variant_item_code"),
+				"required_qty": item.get("qty"),
+				"qty": item.get("qty"),  # for bom
+				"source_warehouse": item.get("source_warehouse"),
+				"operation": item.get("operation"),
+			}
+		)
 
 		bom_doc = frappe.get_cached_doc("BOM", bom_no)
 		item_data = get_item_details(args.item_code, skip_bom_info=True)
 		args.update(item_data)
 
-		args["rate"] = get_bom_item_rate({
-			"company": wo_doc.company,
-			"item_code": args.get("item_code"),
-			"qty": args.get("required_qty"),
-			"uom": args.get("stock_uom"),
-			"stock_uom": args.get("stock_uom"),
-			"conversion_factor": 1
-		}, bom_doc)
+		args["rate"] = get_bom_item_rate(
+			{
+				"company": wo_doc.company,
+				"item_code": args.get("item_code"),
+				"qty": args.get("required_qty"),
+				"uom": args.get("stock_uom"),
+				"stock_uom": args.get("stock_uom"),
+				"conversion_factor": 1,
+			},
+			bom_doc,
+		)
 
 		if not args.source_warehouse:
-			args["source_warehouse"] = get_item_defaults(item.get("variant_item_code"),
-				wo_doc.company).default_warehouse
+			args["source_warehouse"] = get_item_defaults(
+				item.get("variant_item_code"), wo_doc.company
+			).default_warehouse
 
 		args["amount"] = flt(args.get("required_qty")) * flt(args.get("rate"))
 		args["uom"] = item_data.stock_uom
 		wo_doc.append(table_name, args)
 
+
 @frappe.whitelist()
 def check_if_scrap_warehouse_mandatory(bom_no):
-	res = {"set_scrap_wh_mandatory": False }
+	res = {"set_scrap_wh_mandatory": False}
 	if bom_no:
 		bom = frappe.get_doc("BOM", bom_no)
 
@@ -949,12 +1163,14 @@
 
 	return res
 
+
 @frappe.whitelist()
 def set_work_order_ops(name):
-	po = frappe.get_doc('Work Order', name)
+	po = frappe.get_doc("Work Order", name)
 	po.set_work_order_operations()
 	po.save()
 
+
 @frappe.whitelist()
 def make_stock_entry(work_order_id, purpose, qty=None):
 	work_order = frappe.get_doc("Work Order", work_order_id)
@@ -970,12 +1186,17 @@
 	stock_entry.from_bom = 1
 	stock_entry.bom_no = work_order.bom_no
 	stock_entry.use_multi_level_bom = work_order.use_multi_level_bom
-	stock_entry.fg_completed_qty = qty or (flt(work_order.qty) - flt(work_order.produced_qty))
-	if work_order.bom_no:
-		stock_entry.inspection_required = frappe.db.get_value('BOM',
-			work_order.bom_no, 'inspection_required')
+	# accept 0 qty as well
+	stock_entry.fg_completed_qty = (
+		qty if qty is not None else (flt(work_order.qty) - flt(work_order.produced_qty))
+	)
 
-	if purpose=="Material Transfer for Manufacture":
+	if work_order.bom_no:
+		stock_entry.inspection_required = frappe.db.get_value(
+			"BOM", work_order.bom_no, "inspection_required"
+		)
+
+	if purpose == "Material Transfer for Manufacture":
 		stock_entry.to_warehouse = wip_warehouse
 		stock_entry.project = work_order.project
 	else:
@@ -988,6 +1209,7 @@
 	stock_entry.set_serial_no_batch_for_finished_good()
 	return stock_entry.as_dict()
 
+
 @frappe.whitelist()
 def get_default_warehouse():
 	doc = frappe.get_cached_doc("Manufacturing Settings")
@@ -995,12 +1217,13 @@
 	return {
 		"wip_warehouse": doc.default_wip_warehouse,
 		"fg_warehouse": doc.default_fg_warehouse,
-		"scrap_warehouse": doc.default_scrap_warehouse
+		"scrap_warehouse": doc.default_scrap_warehouse,
 	}
 
+
 @frappe.whitelist()
 def stop_unstop(work_order, status):
-	""" Called from client side on Stop/Unstop event"""
+	"""Called from client side on Stop/Unstop event"""
 
 	if not frappe.has_permission("Work Order", "write"):
 		frappe.throw(_("Not permitted"), frappe.PermissionError)
@@ -1017,24 +1240,29 @@
 
 	return pro_order.status
 
+
 @frappe.whitelist()
 def query_sales_order(production_item):
-	out = frappe.db.sql_list("""
+	out = frappe.db.sql_list(
+		"""
 		select distinct so.name from `tabSales Order` so, `tabSales Order Item` so_item
 		where so_item.parent=so.name and so_item.item_code=%s and so.docstatus=1
 	union
 		select distinct so.name from `tabSales Order` so, `tabPacked Item` pi_item
 		where pi_item.parent=so.name and pi_item.item_code=%s and so.docstatus=1
-	""", (production_item, production_item))
+	""",
+		(production_item, production_item),
+	)
 
 	return out
 
+
 @frappe.whitelist()
 def make_job_card(work_order, operations):
 	if isinstance(operations, str):
 		operations = json.loads(operations)
 
-	work_order = frappe.get_doc('Work Order', work_order)
+	work_order = frappe.get_doc("Work Order", work_order)
 	for row in operations:
 		row = frappe._dict(row)
 		validate_operation_data(row)
@@ -1044,6 +1272,7 @@
 			if row.job_card_qty > 0:
 				create_job_card(work_order, row, auto_create=True)
 
+
 @frappe.whitelist()
 def close_work_order(work_order, status):
 	if not frappe.has_permission("Work Order", "write"):
@@ -1051,15 +1280,17 @@
 
 	work_order = frappe.get_doc("Work Order", work_order)
 	if work_order.get("operations"):
-		job_cards = frappe.get_list("Job Card",
-			filters={
-				"work_order": work_order.name,
-				"status": "Work In Progress"
-			}, pluck='name')
+		job_cards = frappe.get_list(
+			"Job Card", filters={"work_order": work_order.name, "status": "Work In Progress"}, pluck="name"
+		)
 
 		if job_cards:
 			job_cards = ", ".join(job_cards)
-			frappe.throw(_("Can not close Work Order. Since {0} Job Cards are in Work In Progress state.").format(job_cards))
+			frappe.throw(
+				_("Can not close Work Order. Since {0} Job Cards are in Work In Progress state.").format(
+					job_cards
+				)
+			)
 
 	work_order.update_status(status)
 	work_order.update_planned_qty()
@@ -1067,9 +1298,11 @@
 	work_order.notify_update()
 	return work_order.status
 
+
 def split_qty_based_on_batch_size(wo_doc, row, qty):
-	if not cint(frappe.db.get_value("Operation",
-		row.operation, "create_job_card_based_on_batch_size")):
+	if not cint(
+		frappe.db.get_value("Operation", row.operation, "create_job_card_based_on_batch_size")
+	):
 		row.batch_size = row.get("qty") or wo_doc.qty
 
 	row.job_card_qty = row.batch_size
@@ -1083,55 +1316,63 @@
 
 	return qty
 
+
 def get_serial_nos_for_job_card(row, wo_doc):
 	if not wo_doc.serial_no:
 		return
 
 	serial_nos = get_serial_nos(wo_doc.serial_no)
 	used_serial_nos = []
-	for d in frappe.get_all('Job Card', fields=['serial_no'],
-		filters={'docstatus': ('<', 2), 'work_order': wo_doc.name, 'operation_id': row.name}):
+	for d in frappe.get_all(
+		"Job Card",
+		fields=["serial_no"],
+		filters={"docstatus": ("<", 2), "work_order": wo_doc.name, "operation_id": row.name},
+	):
 		used_serial_nos.extend(get_serial_nos(d.serial_no))
 
 	serial_nos = sorted(list(set(serial_nos) - set(used_serial_nos)))
-	row.serial_no = '\n'.join(serial_nos[0:row.job_card_qty])
+	row.serial_no = "\n".join(serial_nos[0 : cint(row.job_card_qty)])
+
 
 def validate_operation_data(row):
 	if row.get("qty") <= 0:
-		frappe.throw(_("Quantity to Manufacture can not be zero for the operation {0}")
-			.format(
+		frappe.throw(
+			_("Quantity to Manufacture can not be zero for the operation {0}").format(
 				frappe.bold(row.get("operation"))
 			)
 		)
 
 	if row.get("qty") > row.get("pending_qty"):
-		frappe.throw(_("For operation {0}: Quantity ({1}) can not be greter than pending quantity({2})")
-			.format(
+		frappe.throw(
+			_("For operation {0}: Quantity ({1}) can not be greter than pending quantity({2})").format(
 				frappe.bold(row.get("operation")),
 				frappe.bold(row.get("qty")),
-				frappe.bold(row.get("pending_qty"))
+				frappe.bold(row.get("pending_qty")),
 			)
 		)
 
+
 def create_job_card(work_order, row, enable_capacity_planning=False, auto_create=False):
 	doc = frappe.new_doc("Job Card")
-	doc.update({
-		'work_order': work_order.name,
-		'operation': row.get("operation"),
-		'workstation': row.get("workstation"),
-		'posting_date': nowdate(),
-		'for_quantity': row.job_card_qty or work_order.get('qty', 0),
-		'operation_id': row.get("name"),
-		'bom_no': work_order.bom_no,
-		'project': work_order.project,
-		'company': work_order.company,
-		'sequence_id': row.get("sequence_id"),
-		'wip_warehouse': work_order.wip_warehouse,
-		'hour_rate': row.get("hour_rate"),
-		'serial_no': row.get("serial_no")
-	})
+	doc.update(
+		{
+			"work_order": work_order.name,
+			"operation": row.get("operation"),
+			"workstation": row.get("workstation"),
+			"posting_date": nowdate(),
+			"for_quantity": row.job_card_qty or work_order.get("qty", 0),
+			"operation_id": row.get("name"),
+			"bom_no": work_order.bom_no,
+			"project": work_order.project,
+			"company": work_order.company,
+			"sequence_id": row.get("sequence_id"),
+			"wip_warehouse": work_order.wip_warehouse,
+			"hour_rate": row.get("hour_rate"),
+			"serial_no": row.get("serial_no"),
+		}
+	)
 
-	if work_order.transfer_material_against == 'Job Card' and not work_order.skip_transfer:
+	if work_order.transfer_material_against == "Job Card" and not work_order.skip_transfer:
 		doc.get_required_items()
 
 	if auto_create:
@@ -1140,19 +1381,28 @@
 			doc.schedule_time_logs(row)
 
 		doc.insert()
-		frappe.msgprint(_("Job card {0} created").format(get_link_to_form("Job Card", doc.name)), alert=True)
+		frappe.msgprint(
+			_("Job card {0} created").format(get_link_to_form("Job Card", doc.name)), alert=True
+		)
+
+	if enable_capacity_planning:
+		# automatically added scheduling rows shouldn't change status to WIP
+		doc.db_set("status", "Open")
 
 	return doc
 
+
 def get_work_order_operation_data(work_order, operation, workstation):
 	for d in work_order.operations:
 		if d.operation == operation and d.workstation == workstation:
 			return d
 
+
 @frappe.whitelist()
 def create_pick_list(source_name, target_doc=None, for_qty=None):
-	for_qty = for_qty or json.loads(target_doc).get('for_qty')
-	max_finished_goods_qty = frappe.db.get_value('Work Order', source_name, 'qty')
+	for_qty = for_qty or json.loads(target_doc).get("for_qty")
+	max_finished_goods_qty = frappe.db.get_value("Work Order", source_name, "qty")
+
 	def update_item_quantity(source, target, source_parent):
 		pending_to_issue = flt(source.required_qty) - flt(source.transferred_qty)
 		desire_to_transfer = flt(source.required_qty) / max_finished_goods_qty * flt(for_qty)
@@ -1166,25 +1416,25 @@
 		if qty:
 			target.qty = qty
 			target.stock_qty = qty
-			target.uom = frappe.get_value('Item', source.item_code, 'stock_uom')
+			target.uom = frappe.get_value("Item", source.item_code, "stock_uom")
 			target.stock_uom = target.uom
 			target.conversion_factor = 1
 		else:
 			target.delete()
 
-	doc = get_mapped_doc('Work Order', source_name, {
-		'Work Order': {
-			'doctype': 'Pick List',
-			'validation': {
-				'docstatus': ['=', 1]
-			}
+	doc = get_mapped_doc(
+		"Work Order",
+		source_name,
+		{
+			"Work Order": {"doctype": "Pick List", "validation": {"docstatus": ["=", 1]}},
+			"Work Order Item": {
+				"doctype": "Pick List Item",
+				"postprocess": update_item_quantity,
+				"condition": lambda doc: abs(doc.transferred_qty) < abs(doc.required_qty),
+			},
 		},
-		'Work Order Item': {
-			'doctype': 'Pick List Item',
-			'postprocess': update_item_quantity,
-			'condition': lambda doc: abs(doc.transferred_qty) < abs(doc.required_qty)
-		},
-	}, target_doc)
+		target_doc,
+	)
 
 	doc.for_qty = for_qty
 
@@ -1192,26 +1442,31 @@
 
 	return doc
 
+
 def get_reserved_qty_for_production(item_code: str, warehouse: str) -> float:
 	"""Get total reserved quantity for any item in specified warehouse"""
 	wo = frappe.qb.DocType("Work Order")
 	wo_item = frappe.qb.DocType("Work Order Item")
 
 	return (
-	frappe.qb
-		.from_(wo)
+		frappe.qb.from_(wo)
 		.from_(wo_item)
-		.select(Sum(Case()
+		.select(
+			Sum(
+				Case()
 				.when(wo.skip_transfer == 0, wo_item.required_qty - wo_item.transferred_qty)
-				.else_(wo_item.required_qty - wo_item.consumed_qty))
+				.else_(wo_item.required_qty - wo_item.consumed_qty)
 			)
+		)
 		.where(
 			(wo_item.item_code == item_code)
 			& (wo_item.parent == wo.name)
 			& (wo.docstatus == 1)
 			& (wo_item.source_warehouse == warehouse)
 			& (wo.status.notin(["Stopped", "Completed", "Closed"]))
-			& ((wo_item.required_qty > wo_item.transferred_qty)
-				| (wo_item.required_qty > wo_item.consumed_qty))
+			& (
+				(wo_item.required_qty > wo_item.transferred_qty)
+				| (wo_item.required_qty > wo_item.consumed_qty)
+			)
 		)
 	).run()[0][0] or 0.0
diff --git a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py
index 37dd11a..465460f 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py
@@ -3,18 +3,10 @@
 
 def get_data():
 	return {
-		'fieldname': 'work_order',
-		'non_standard_fieldnames': {
-			'Batch': 'reference_name'
-		},
-		'transactions': [
-			{
-				'label': _('Transactions'),
-				'items': ['Stock Entry', 'Job Card', 'Pick List']
-			},
-			{
-				'label': _('Reference'),
-				'items': ['Serial No', 'Batch']
-			}
-		]
+		"fieldname": "work_order",
+		"non_standard_fieldnames": {"Batch": "reference_name"},
+		"transactions": [
+			{"label": _("Transactions"), "items": ["Stock Entry", "Job Card", "Pick List"]},
+			{"label": _("Reference"), "items": ["Serial No", "Batch"]},
+		],
 	}
diff --git a/erpnext/manufacturing/doctype/work_order_item/work_order_item.py b/erpnext/manufacturing/doctype/work_order_item/work_order_item.py
index 4311d3b..1792747 100644
--- a/erpnext/manufacturing/doctype/work_order_item/work_order_item.py
+++ b/erpnext/manufacturing/doctype/work_order_item/work_order_item.py
@@ -9,5 +9,6 @@
 class WorkOrderItem(Document):
 	pass
 
+
 def on_doctype_update():
 	frappe.db.add_index("Work Order Item", ["item_code", "source_warehouse"])
diff --git a/erpnext/manufacturing/doctype/workstation/test_workstation.py b/erpnext/manufacturing/doctype/workstation/test_workstation.py
index dd51017..6db985c 100644
--- a/erpnext/manufacturing/doctype/workstation/test_workstation.py
+++ b/erpnext/manufacturing/doctype/workstation/test_workstation.py
@@ -13,19 +13,42 @@
 )
 
 test_dependencies = ["Warehouse"]
-test_records = frappe.get_test_records('Workstation')
-make_test_records('Workstation')
+test_records = frappe.get_test_records("Workstation")
+make_test_records("Workstation")
+
 
 class TestWorkstation(FrappeTestCase):
 	def test_validate_timings(self):
-		check_if_within_operating_hours("_Test Workstation 1", "Operation 1", "2013-02-02 11:00:00", "2013-02-02 19:00:00")
-		check_if_within_operating_hours("_Test Workstation 1", "Operation 1", "2013-02-02 10:00:00", "2013-02-02 20:00:00")
-		self.assertRaises(NotInWorkingHoursError, check_if_within_operating_hours,
-			"_Test Workstation 1", "Operation 1", "2013-02-02 05:00:00", "2013-02-02 20:00:00")
-		self.assertRaises(NotInWorkingHoursError, check_if_within_operating_hours,
-			"_Test Workstation 1", "Operation 1", "2013-02-02 05:00:00", "2013-02-02 20:00:00")
-		self.assertRaises(WorkstationHolidayError, check_if_within_operating_hours,
-			"_Test Workstation 1", "Operation 1", "2013-02-01 10:00:00", "2013-02-02 20:00:00")
+		check_if_within_operating_hours(
+			"_Test Workstation 1", "Operation 1", "2013-02-02 11:00:00", "2013-02-02 19:00:00"
+		)
+		check_if_within_operating_hours(
+			"_Test Workstation 1", "Operation 1", "2013-02-02 10:00:00", "2013-02-02 20:00:00"
+		)
+		self.assertRaises(
+			NotInWorkingHoursError,
+			check_if_within_operating_hours,
+			"_Test Workstation 1",
+			"Operation 1",
+			"2013-02-02 05:00:00",
+			"2013-02-02 20:00:00",
+		)
+		self.assertRaises(
+			NotInWorkingHoursError,
+			check_if_within_operating_hours,
+			"_Test Workstation 1",
+			"Operation 1",
+			"2013-02-02 05:00:00",
+			"2013-02-02 20:00:00",
+		)
+		self.assertRaises(
+			WorkstationHolidayError,
+			check_if_within_operating_hours,
+			"_Test Workstation 1",
+			"Operation 1",
+			"2013-02-01 10:00:00",
+			"2013-02-02 20:00:00",
+		)
 
 	def test_update_bom_operation_rate(self):
 		operations = [
@@ -33,14 +56,14 @@
 				"operation": "Test Operation A",
 				"workstation": "_Test Workstation A",
 				"hour_rate_rent": 300,
-				"time_in_mins": 60
+				"time_in_mins": 60,
 			},
 			{
 				"operation": "Test Operation B",
 				"workstation": "_Test Workstation B",
 				"hour_rate_rent": 1000,
-				"time_in_mins": 60
-			}
+				"time_in_mins": 60,
+			},
 		]
 
 		for row in operations:
@@ -48,21 +71,13 @@
 			make_operation(row)
 
 		test_routing_operations = [
-			{
-				"operation": "Test Operation A",
-				"workstation": "_Test Workstation A",
-				"time_in_mins": 60
-			},
-			{
-				"operation": "Test Operation B",
-				"workstation": "_Test Workstation A",
-				"time_in_mins": 60
-			}
+			{"operation": "Test Operation A", "workstation": "_Test Workstation A", "time_in_mins": 60},
+			{"operation": "Test Operation B", "workstation": "_Test Workstation A", "time_in_mins": 60},
 		]
-		routing_doc = create_routing(routing_name = "Routing Test", operations=test_routing_operations)
+		routing_doc = create_routing(routing_name="Routing Test", operations=test_routing_operations)
 		bom_doc = setup_bom(item_code="_Testing Item", routing=routing_doc.name, currency="INR")
 		w1 = frappe.get_doc("Workstation", "_Test Workstation A")
-		#resets values
+		# resets values
 		w1.hour_rate_rent = 300
 		w1.hour_rate_labour = 0
 		w1.save()
@@ -72,13 +87,14 @@
 		self.assertEqual(bom_doc.operations[0].hour_rate, 300)
 		w1.hour_rate_rent = 250
 		w1.save()
-		#updating after setting new rates in workstations
+		# updating after setting new rates in workstations
 		bom_doc.update_cost()
 		bom_doc.reload()
 		self.assertEqual(w1.hour_rate, 250)
 		self.assertEqual(bom_doc.operations[0].hour_rate, 250)
 		self.assertEqual(bom_doc.operations[1].hour_rate, 250)
 
+
 def make_workstation(*args, **kwargs):
 	args = args if args else kwargs
 	if isinstance(args, tuple):
@@ -88,10 +104,7 @@
 
 	workstation_name = args.workstation_name or args.workstation
 	if not frappe.db.exists("Workstation", workstation_name):
-		doc = frappe.get_doc({
-			"doctype": "Workstation",
-			"workstation_name": workstation_name
-		})
+		doc = frappe.get_doc({"doctype": "Workstation", "workstation_name": workstation_name})
 		doc.hour_rate_rent = args.get("hour_rate_rent")
 		doc.hour_rate_labour = args.get("hour_rate_labour")
 		doc.insert()
diff --git a/erpnext/manufacturing/doctype/workstation/workstation.py b/erpnext/manufacturing/doctype/workstation/workstation.py
index 4cfd410..59e5318 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation.py
+++ b/erpnext/manufacturing/doctype/workstation/workstation.py
@@ -19,14 +19,26 @@
 from erpnext.support.doctype.issue.issue import get_holidays
 
 
-class WorkstationHolidayError(frappe.ValidationError): pass
-class NotInWorkingHoursError(frappe.ValidationError): pass
-class OverlapError(frappe.ValidationError): pass
+class WorkstationHolidayError(frappe.ValidationError):
+	pass
+
+
+class NotInWorkingHoursError(frappe.ValidationError):
+	pass
+
+
+class OverlapError(frappe.ValidationError):
+	pass
+
 
 class Workstation(Document):
 	def validate(self):
-		self.hour_rate = (flt(self.hour_rate_labour) + flt(self.hour_rate_electricity) +
-			flt(self.hour_rate_consumable) + flt(self.hour_rate_rent))
+		self.hour_rate = (
+			flt(self.hour_rate_labour)
+			+ flt(self.hour_rate_electricity)
+			+ flt(self.hour_rate_consumable)
+			+ flt(self.hour_rate_rent)
+		)
 
 	def on_update(self):
 		self.validate_overlap_for_operation_timings()
@@ -35,29 +47,41 @@
 	def validate_overlap_for_operation_timings(self):
 		"""Check if there is no overlap in setting Workstation Operating Hours"""
 		for d in self.get("working_hours"):
-			existing = frappe.db.sql_list("""select idx from `tabWorkstation Working Hour`
+			existing = frappe.db.sql_list(
+				"""select idx from `tabWorkstation Working Hour`
 				where parent = %s and name != %s
 					and (
 						(start_time between %s and %s) or
 						(end_time between %s and %s) or
 						(%s between start_time and end_time))
-				""", (self.name, d.name, d.start_time, d.end_time, d.start_time, d.end_time, d.start_time))
+				""",
+				(self.name, d.name, d.start_time, d.end_time, d.start_time, d.end_time, d.start_time),
+			)
 
 			if existing:
-				frappe.throw(_("Row #{0}: Timings conflicts with row {1}").format(d.idx, comma_and(existing)), OverlapError)
+				frappe.throw(
+					_("Row #{0}: Timings conflicts with row {1}").format(d.idx, comma_and(existing)), OverlapError
+				)
 
 	def update_bom_operation(self):
-		bom_list = frappe.db.sql("""select DISTINCT parent from `tabBOM Operation`
-			where workstation = %s and parenttype = 'routing' """, self.name)
+		bom_list = frappe.db.sql(
+			"""select DISTINCT parent from `tabBOM Operation`
+			where workstation = %s and parenttype = 'routing' """,
+			self.name,
+		)
 
 		for bom_no in bom_list:
-			frappe.db.sql("""update `tabBOM Operation` set hour_rate = %s
+			frappe.db.sql(
+				"""update `tabBOM Operation` set hour_rate = %s
 				where parent = %s and workstation = %s""",
-				(self.hour_rate, bom_no[0], self.name))
+				(self.hour_rate, bom_no[0], self.name),
+			)
 
 	def validate_workstation_holiday(self, schedule_date, skip_holiday_list_check=False):
-		if not skip_holiday_list_check and (not self.holiday_list or
-			cint(frappe.db.get_single_value("Manufacturing Settings", "allow_production_on_holidays"))):
+		if not skip_holiday_list_check and (
+			not self.holiday_list
+			or cint(frappe.db.get_single_value("Manufacturing Settings", "allow_production_on_holidays"))
+		):
 			return schedule_date
 
 		if schedule_date in tuple(get_holidays(self.holiday_list)):
@@ -66,18 +90,25 @@
 
 		return schedule_date
 
+
 @frappe.whitelist()
 def get_default_holiday_list():
-	return frappe.get_cached_value('Company',  frappe.defaults.get_user_default("Company"),  "default_holiday_list")
+	return frappe.get_cached_value(
+		"Company", frappe.defaults.get_user_default("Company"), "default_holiday_list"
+	)
+
 
 def check_if_within_operating_hours(workstation, operation, from_datetime, to_datetime):
 	if from_datetime and to_datetime:
-		if not cint(frappe.db.get_value("Manufacturing Settings", "None", "allow_production_on_holidays")):
+		if not cint(
+			frappe.db.get_value("Manufacturing Settings", "None", "allow_production_on_holidays")
+		):
 			check_workstation_for_holiday(workstation, from_datetime, to_datetime)
 
 		if not cint(frappe.db.get_value("Manufacturing Settings", None, "allow_overtime")):
 			is_within_operating_hours(workstation, operation, from_datetime, to_datetime)
 
+
 def is_within_operating_hours(workstation, operation, from_datetime, to_datetime):
 	operation_length = time_diff_in_seconds(to_datetime, from_datetime)
 	workstation = frappe.get_doc("Workstation", workstation)
@@ -87,21 +118,35 @@
 
 	for working_hour in workstation.working_hours:
 		if working_hour.start_time and working_hour.end_time:
-			slot_length = (to_timedelta(working_hour.end_time or "") - to_timedelta(working_hour.start_time or "")).total_seconds()
+			slot_length = (
+				to_timedelta(working_hour.end_time or "") - to_timedelta(working_hour.start_time or "")
+			).total_seconds()
 			if slot_length >= operation_length:
 				return
 
-	frappe.throw(_("Operation {0} longer than any available working hours in workstation {1}, break down the operation into multiple operations").format(operation, workstation.name), NotInWorkingHoursError)
+	frappe.throw(
+		_(
+			"Operation {0} longer than any available working hours in workstation {1}, break down the operation into multiple operations"
+		).format(operation, workstation.name),
+		NotInWorkingHoursError,
+	)
+
 
 def check_workstation_for_holiday(workstation, from_datetime, to_datetime):
 	holiday_list = frappe.db.get_value("Workstation", workstation, "holiday_list")
 	if holiday_list and from_datetime and to_datetime:
 		applicable_holidays = []
-		for d in frappe.db.sql("""select holiday_date from `tabHoliday` where parent = %s
+		for d in frappe.db.sql(
+			"""select holiday_date from `tabHoliday` where parent = %s
 			and holiday_date between %s and %s """,
-			(holiday_list, getdate(from_datetime), getdate(to_datetime))):
-				applicable_holidays.append(formatdate(d[0]))
+			(holiday_list, getdate(from_datetime), getdate(to_datetime)),
+		):
+			applicable_holidays.append(formatdate(d[0]))
 
 		if applicable_holidays:
-			frappe.throw(_("Workstation is closed on the following dates as per Holiday List: {0}")
-				.format(holiday_list) + "\n" + "\n".join(applicable_holidays), WorkstationHolidayError)
+			frappe.throw(
+				_("Workstation is closed on the following dates as per Holiday List: {0}").format(holiday_list)
+				+ "\n"
+				+ "\n".join(applicable_holidays),
+				WorkstationHolidayError,
+			)
diff --git a/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py b/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py
index 1fa1494..6d02221 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py
+++ b/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py
@@ -3,17 +3,22 @@
 
 def get_data():
 	return {
-		'fieldname': 'workstation',
-		'transactions': [
+		"fieldname": "workstation",
+		"transactions": [
+			{"label": _("Master"), "items": ["BOM", "Routing", "Operation"]},
 			{
-				'label': _('Master'),
-				'items': ['BOM', 'Routing', 'Operation']
+				"label": _("Transaction"),
+				"items": [
+					"Work Order",
+					"Job Card",
+				],
 			},
-			{
-				'label': _('Transaction'),
-				'items': ['Work Order', 'Job Card',]
-			}
 		],
-		'disable_create_buttons': ['BOM', 'Routing', 'Operation',
-			'Work Order', 'Job Card',]
+		"disable_create_buttons": [
+			"BOM",
+			"Routing",
+			"Operation",
+			"Work Order",
+			"Job Card",
+		],
 	}
diff --git a/erpnext/manufacturing/report/bom_explorer/bom_explorer.py b/erpnext/manufacturing/report/bom_explorer/bom_explorer.py
index 19a80ab..2aa31be 100644
--- a/erpnext/manufacturing/report/bom_explorer/bom_explorer.py
+++ b/erpnext/manufacturing/report/bom_explorer/bom_explorer.py
@@ -3,6 +3,7 @@
 
 
 import frappe
+from frappe import _
 
 
 def execute(filters=None):
@@ -11,81 +12,55 @@
 	get_data(filters, data)
 	return columns, data
 
+
 def get_data(filters, data):
 	get_exploded_items(filters.bom, data)
 
+
 def get_exploded_items(bom, data, indent=0, qty=1):
-	exploded_items = frappe.get_all("BOM Item",
+	exploded_items = frappe.get_all(
+		"BOM Item",
 		filters={"parent": bom},
-		fields= ['qty','bom_no','qty','scrap','item_code','item_name','description','uom'])
+		fields=["qty", "bom_no", "qty", "item_code", "item_name", "description", "uom"],
+	)
 
 	for item in exploded_items:
 		print(item.bom_no, indent)
 		item["indent"] = indent
-		data.append({
-			'item_code': item.item_code,
-			'item_name': item.item_name,
-			'indent': indent,
-			'bom_level': indent,
-			'bom': item.bom_no,
-			'qty': item.qty * qty,
-			'uom': item.uom,
-			'description': item.description,
-			'scrap': item.scrap
-		})
+		data.append(
+			{
+				"item_code": item.item_code,
+				"item_name": item.item_name,
+				"indent": indent,
+				"bom_level": indent,
+				"bom": item.bom_no,
+				"qty": item.qty * qty,
+				"uom": item.uom,
+				"description": item.description,
+			}
+		)
 		if item.bom_no:
-			get_exploded_items(item.bom_no, data, indent=indent+1, qty=item.qty)
+			get_exploded_items(item.bom_no, data, indent=indent + 1, qty=item.qty)
+
 
 def get_columns():
 	return [
 		{
-			"label": "Item Code",
+			"label": _("Item Code"),
 			"fieldtype": "Link",
 			"fieldname": "item_code",
 			"width": 300,
-			"options": "Item"
+			"options": "Item",
 		},
+		{"label": _("Item Name"), "fieldtype": "data", "fieldname": "item_name", "width": 100},
+		{"label": _("BOM"), "fieldtype": "Link", "fieldname": "bom", "width": 150, "options": "BOM"},
+		{"label": _("Qty"), "fieldtype": "data", "fieldname": "qty", "width": 100},
+		{"label": _("UOM"), "fieldtype": "data", "fieldname": "uom", "width": 100},
+		{"label": _("BOM Level"), "fieldtype": "Int", "fieldname": "bom_level", "width": 100},
 		{
-			"label": "Item Name",
-			"fieldtype": "data",
-			"fieldname": "item_name",
-			"width": 100
-		},
-		{
-			"label": "BOM",
-			"fieldtype": "Link",
-			"fieldname": "bom",
-			"width": 150,
-			"options": "BOM"
-		},
-		{
-			"label": "Qty",
-			"fieldtype": "data",
-			"fieldname": "qty",
-			"width": 100
-		},
-		{
-			"label": "UOM",
-			"fieldtype": "data",
-			"fieldname": "uom",
-			"width": 100
-		},
-		{
-			"label": "BOM Level",
-			"fieldtype": "Int",
-			"fieldname": "bom_level",
-			"width": 100
-		},
-		{
-			"label": "Standard Description",
+			"label": _("Standard Description"),
 			"fieldtype": "data",
 			"fieldname": "description",
-			"width": 150
-		},
-		{
-			"label": "Scrap",
-			"fieldtype": "data",
-			"fieldname": "scrap",
-			"width": 100
+			"width": 150,
 		},
 	]
diff --git a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py
index eda9eb9..92c69cf 100644
--- a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py
+++ b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py
@@ -11,6 +11,7 @@
 	columns = get_columns(filters)
 	return columns, data
 
+
 def get_data(filters):
 	bom_wise_data = {}
 	bom_data, report_data = [], []
@@ -24,11 +25,9 @@
 			bom_data.append(d.name)
 			row.update(d)
 		else:
-			row.update({
-				"operation": d.operation,
-				"workstation": d.workstation,
-				"time_in_mins": d.time_in_mins
-			})
+			row.update(
+				{"operation": d.operation, "workstation": d.workstation, "time_in_mins": d.time_in_mins}
+			)
 
 		# maintain BOM wise data for grouping such as:
 		# {"BOM A": [{Row1}, {Row2}], "BOM B": ...}
@@ -43,20 +42,25 @@
 
 	return report_data
 
+
 def get_filtered_data(filters):
 	bom = frappe.qb.DocType("BOM")
 	bom_ops = frappe.qb.DocType("BOM Operation")
 
 	bom_ops_query = (
 		frappe.qb.from_(bom)
-		.join(bom_ops).on(bom.name == bom_ops.parent)
+		.join(bom_ops)
+		.on(bom.name == bom_ops.parent)
 		.select(
-			bom.name, bom.item, bom.item_name, bom.uom,
-			bom_ops.operation, bom_ops.workstation, bom_ops.time_in_mins
-		).where(
-			(bom.docstatus == 1)
-			& (bom.is_active == 1)
+			bom.name,
+			bom.item,
+			bom.item_name,
+			bom.uom,
+			bom_ops.operation,
+			bom_ops.workstation,
+			bom_ops.time_in_mins,
 		)
+		.where((bom.docstatus == 1) & (bom.is_active == 1))
 	)
 
 	if filters.get("item_code"):
@@ -66,18 +70,20 @@
 		bom_ops_query = bom_ops_query.where(bom.name.isin(filters.get("bom_id")))
 
 	if filters.get("workstation"):
-		bom_ops_query = bom_ops_query.where(
-			bom_ops.workstation == filters.get("workstation")
-		)
+		bom_ops_query = bom_ops_query.where(bom_ops.workstation == filters.get("workstation"))
 
 	bom_operation_data = bom_ops_query.run(as_dict=True)
 
 	return bom_operation_data
 
+
 def get_bom_count(bom_data):
-	data = frappe.get_all("BOM Item",
+	data = frappe.get_all(
+		"BOM Item",
 		fields=["count(name) as count", "bom_no"],
-		filters= {"bom_no": ("in", bom_data)}, group_by = "bom_no")
+		filters={"bom_no": ("in", bom_data)},
+		group_by="bom_no",
+	)
 
 	bom_count = {}
 	for d in data:
@@ -85,58 +91,42 @@
 
 	return bom_count
 
+
 def get_args():
-	return frappe._dict({
-		"name": "",
-		"item": "",
-		"item_name": "",
-		"uom": ""
-	})
+	return frappe._dict({"name": "", "item": "", "item_name": "", "uom": ""})
+
 
 def get_columns(filters):
-	return [{
-		"label": _("BOM ID"),
-		"options": "BOM",
-		"fieldname": "name",
-		"fieldtype": "Link",
-		"width": 220
-	}, {
-		"label": _("Item Code"),
-		"options": "Item",
-		"fieldname": "item",
-		"fieldtype": "Link",
-		"width": 150
-	}, {
-		"label": _("Item Name"),
-		"fieldname": "item_name",
-		"fieldtype": "Data",
-		"width": 110
-	}, {
-		"label": _("UOM"),
-		"options": "UOM",
-		"fieldname": "uom",
-		"fieldtype": "Link",
-		"width": 100
-	}, {
-		"label": _("Operation"),
-		"options": "Operation",
-		"fieldname": "operation",
-		"fieldtype": "Link",
-		"width": 140
-	}, {
-		"label": _("Workstation"),
-		"options": "Workstation",
-		"fieldname": "workstation",
-		"fieldtype": "Link",
-		"width": 110
-	}, {
-		"label": _("Time (In Mins)"),
-		"fieldname": "time_in_mins",
-		"fieldtype": "Float",
-		"width": 120
-	}, {
-		"label": _("Sub-assembly BOM Count"),
-		"fieldname": "used_as_subassembly_items",
-		"fieldtype": "Int",
-		"width": 200
-	}]
+	return [
+		{"label": _("BOM ID"), "options": "BOM", "fieldname": "name", "fieldtype": "Link", "width": 220},
+		{
+			"label": _("Item Code"),
+			"options": "Item",
+			"fieldname": "item",
+			"fieldtype": "Link",
+			"width": 150,
+		},
+		{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 110},
+		{"label": _("UOM"), "options": "UOM", "fieldname": "uom", "fieldtype": "Link", "width": 100},
+		{
+			"label": _("Operation"),
+			"options": "Operation",
+			"fieldname": "operation",
+			"fieldtype": "Link",
+			"width": 140,
+		},
+		{
+			"label": _("Workstation"),
+			"options": "Workstation",
+			"fieldname": "workstation",
+			"fieldtype": "Link",
+			"width": 110,
+		},
+		{"label": _("Time (In Mins)"), "fieldname": "time_in_mins", "fieldtype": "Float", "width": 120},
+		{
+			"label": _("Sub-assembly BOM Count"),
+			"fieldname": "used_as_subassembly_items",
+			"fieldtype": "Int",
+			"width": 200,
+		},
+	]
diff --git a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
index 2693352..933be3e 100644
--- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
+++ b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
@@ -23,14 +23,24 @@
 		summ_data.append(get_report_data(last_pur_price, reqd_qty, row, manufacture_details))
 	return columns, summ_data
 
+
 def get_report_data(last_pur_price, reqd_qty, row, manufacture_details):
 	to_build = row.to_build if row.to_build > 0 else 0
 	diff_qty = to_build - reqd_qty
-	return [row.item_code, row.description,
-		comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer', []), add_quotes=False),
-		comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer_part', []), add_quotes=False),
-		row.actual_qty, str(to_build),
-		reqd_qty, diff_qty, last_pur_price]
+	return [
+		row.item_code,
+		row.description,
+		comma_and(manufacture_details.get(row.item_code, {}).get("manufacturer", []), add_quotes=False),
+		comma_and(
+			manufacture_details.get(row.item_code, {}).get("manufacturer_part", []), add_quotes=False
+		),
+		row.actual_qty,
+		str(to_build),
+		reqd_qty,
+		diff_qty,
+		last_pur_price,
+	]
+
 
 def get_columns():
 	"""return columns"""
@@ -41,12 +51,13 @@
 		_("Manufacturer Part Number") + "::250",
 		_("Qty") + ":Float:50",
 		_("Stock Qty") + ":Float:100",
-		_("Reqd Qty")+ ":Float:100",
-		_("Diff Qty")+ ":Float:100",
-		_("Last Purchase Price")+ ":Float:100",
+		_("Reqd Qty") + ":Float:100",
+		_("Diff Qty") + ":Float:100",
+		_("Last Purchase Price") + ":Float:100",
 	]
 	return columns
 
+
 def get_bom_stock(filters):
 	conditions = ""
 	bom = filters.get("bom")
@@ -59,18 +70,23 @@
 		qty_field = "stock_qty"
 
 	if filters.get("warehouse"):
-		warehouse_details = frappe.db.get_value("Warehouse", filters.get("warehouse"), ["lft", "rgt"], as_dict=1)
+		warehouse_details = frappe.db.get_value(
+			"Warehouse", filters.get("warehouse"), ["lft", "rgt"], as_dict=1
+		)
 		if warehouse_details:
-			conditions += " and exists (select name from `tabWarehouse` wh \
-				where wh.lft >= %s and wh.rgt <= %s and ledger.warehouse = wh.name)" % (warehouse_details.lft,
-				warehouse_details.rgt)
+			conditions += (
+				" and exists (select name from `tabWarehouse` wh \
+				where wh.lft >= %s and wh.rgt <= %s and ledger.warehouse = wh.name)"
+				% (warehouse_details.lft, warehouse_details.rgt)
+			)
 		else:
 			conditions += " and ledger.warehouse = %s" % frappe.db.escape(filters.get("warehouse"))
 
 	else:
 		conditions += ""
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 			SELECT
 				bom_item.item_code,
 				bom_item.description,
@@ -86,14 +102,21 @@
 			WHERE
 				bom_item.parent = '{bom}' and bom_item.parenttype='BOM'
 
-			GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1)
+			GROUP BY bom_item.item_code""".format(
+			qty_field=qty_field, table=table, conditions=conditions, bom=bom
+		),
+		as_dict=1,
+	)
+
 
 def get_manufacturer_records():
-	details = frappe.get_all('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no", "item_code"])
+	details = frappe.get_all(
+		"Item Manufacturer", fields=["manufacturer", "manufacturer_part_no", "item_code"]
+	)
 	manufacture_details = frappe._dict()
 	for detail in details:
-		dic = manufacture_details.setdefault(detail.get('item_code'), {})
-		dic.setdefault('manufacturer', []).append(detail.get('manufacturer'))
-		dic.setdefault('manufacturer_part', []).append(detail.get('manufacturer_part_no'))
+		dic = manufacture_details.setdefault(detail.get("item_code"), {})
+		dic.setdefault("manufacturer", []).append(detail.get("manufacturer"))
+		dic.setdefault("manufacturer_part", []).append(detail.get("manufacturer_part_no"))
 
 	return manufacture_details
diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
index fa94391..34e9826 100644
--- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
+++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
@@ -7,7 +7,8 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	columns = get_columns()
 
@@ -15,6 +16,7 @@
 
 	return columns, data
 
+
 def get_columns():
 	"""return columns"""
 	columns = [
@@ -29,6 +31,7 @@
 
 	return columns
 
+
 def get_bom_stock(filters):
 	conditions = ""
 	bom = filters.get("bom")
@@ -37,25 +40,30 @@
 	qty_field = "stock_qty"
 
 	qty_to_produce = filters.get("qty_to_produce", 1)
-	if  int(qty_to_produce) <= 0:
+	if int(qty_to_produce) <= 0:
 		frappe.throw(_("Quantity to Produce can not be less than Zero"))
 
 	if filters.get("show_exploded_view"):
 		table = "`tabBOM Explosion Item`"
 
 	if filters.get("warehouse"):
-		warehouse_details = frappe.db.get_value("Warehouse", filters.get("warehouse"), ["lft", "rgt"], as_dict=1)
+		warehouse_details = frappe.db.get_value(
+			"Warehouse", filters.get("warehouse"), ["lft", "rgt"], as_dict=1
+		)
 		if warehouse_details:
-			conditions += " and exists (select name from `tabWarehouse` wh \
-				where wh.lft >= %s and wh.rgt <= %s and ledger.warehouse = wh.name)" % (warehouse_details.lft,
-				warehouse_details.rgt)
+			conditions += (
+				" and exists (select name from `tabWarehouse` wh \
+				where wh.lft >= %s and wh.rgt <= %s and ledger.warehouse = wh.name)"
+				% (warehouse_details.lft, warehouse_details.rgt)
+			)
 		else:
 			conditions += " and ledger.warehouse = %s" % frappe.db.escape(filters.get("warehouse"))
 
 	else:
 		conditions += ""
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 			SELECT
 				bom_item.item_code,
 				bom_item.description ,
@@ -74,9 +82,10 @@
 				bom_item.parent = {bom} and bom_item.parenttype='BOM'
 
 			GROUP BY bom_item.item_code""".format(
-				qty_field=qty_field,
-				table=table,
-				conditions=conditions,
-				bom=frappe.db.escape(bom),
-				qty_to_produce=qty_to_produce or 1)
-			)
+			qty_field=qty_field,
+			table=table,
+			conditions=conditions,
+			bom=frappe.db.escape(bom),
+			qty_to_produce=qty_to_produce or 1,
+		)
+	)
diff --git a/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.py b/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.py
index a5ae43e..da28343 100644
--- a/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.py
+++ b/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.py
@@ -12,98 +12,99 @@
 	data = get_data(filters)
 	return columns, data
 
+
 def get_columns(filters):
-	columns = [{
+	columns = [
+		{
 			"label": _("Work Order"),
 			"fieldname": "work_order",
 			"fieldtype": "Link",
 			"options": "Work Order",
-			"width": 120
-		}]
-
-	if not filters.get('bom_no'):
-		columns.extend([
-			{
-				"label": _("BOM No"),
-				"fieldname": "bom_no",
-				"fieldtype": "Link",
-				"options": "BOM",
-				"width": 180
-			}
-		])
-
-	columns.extend([
-		{
-			"label": _("Finished Good"),
-			"fieldname": "production_item",
-			"fieldtype": "Link",
-			"options": "Item",
-			"width": 120
-		},
-		{
-			"label": _("Ordered Qty"),
-			"fieldname": "qty",
-			"fieldtype": "Float",
-			"width": 120
-		},
-		{
-			"label": _("Produced Qty"),
-			"fieldname": "produced_qty",
-			"fieldtype": "Float",
-			"width": 120
-		},
-		{
-			"label": _("Raw Material"),
-			"fieldname": "raw_material_code",
-			"fieldtype": "Link",
-			"options": "Item",
-			"width": 120
-		},
-		{
-			"label": _("Required Qty"),
-			"fieldname": "required_qty",
-			"fieldtype": "Float",
-			"width": 120
-		},
-		{
-			"label": _("Consumed Qty"),
-			"fieldname": "consumed_qty",
-			"fieldtype": "Float",
-			"width": 120
+			"width": 120,
 		}
-	])
+	]
+
+	if not filters.get("bom_no"):
+		columns.extend(
+			[
+				{
+					"label": _("BOM No"),
+					"fieldname": "bom_no",
+					"fieldtype": "Link",
+					"options": "BOM",
+					"width": 180,
+				}
+			]
+		)
+
+	columns.extend(
+		[
+			{
+				"label": _("Finished Good"),
+				"fieldname": "production_item",
+				"fieldtype": "Link",
+				"options": "Item",
+				"width": 120,
+			},
+			{"label": _("Ordered Qty"), "fieldname": "qty", "fieldtype": "Float", "width": 120},
+			{"label": _("Produced Qty"), "fieldname": "produced_qty", "fieldtype": "Float", "width": 120},
+			{
+				"label": _("Raw Material"),
+				"fieldname": "raw_material_code",
+				"fieldtype": "Link",
+				"options": "Item",
+				"width": 120,
+			},
+			{"label": _("Required Qty"), "fieldname": "required_qty", "fieldtype": "Float", "width": 120},
+			{"label": _("Consumed Qty"), "fieldname": "consumed_qty", "fieldtype": "Float", "width": 120},
+		]
+	)
 
 	return columns
 
+
 def get_data(filters):
 	cond = "1=1"
 
-	if filters.get('bom_no') and not filters.get('work_order'):
-		cond += " and bom_no = '%s'" % filters.get('bom_no')
+	if filters.get("bom_no") and not filters.get("work_order"):
+		cond += " and bom_no = '%s'" % filters.get("bom_no")
 
-	if filters.get('work_order'):
-		cond += " and name = '%s'" % filters.get('work_order')
+	if filters.get("work_order"):
+		cond += " and name = '%s'" % filters.get("work_order")
 
 	results = []
-	for d in frappe.db.sql(""" select name as work_order, qty, produced_qty, production_item, bom_no
-		from `tabWork Order` where produced_qty > qty and docstatus = 1 and {0}""".format(cond), as_dict=1):
+	for d in frappe.db.sql(
+		""" select name as work_order, qty, produced_qty, production_item, bom_no
+		from `tabWork Order` where produced_qty > qty and docstatus = 1 and {0}""".format(
+			cond
+		),
+		as_dict=1,
+	):
 		results.append(d)
 
-		for data in frappe.get_all('Work Order Item', fields=["item_code as raw_material_code",
-			"required_qty", "consumed_qty"], filters={'parent': d.work_order, 'parenttype': 'Work Order'}):
+		for data in frappe.get_all(
+			"Work Order Item",
+			fields=["item_code as raw_material_code", "required_qty", "consumed_qty"],
+			filters={"parent": d.work_order, "parenttype": "Work Order"},
+		):
 			results.append(data)
 
 	return results
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_work_orders(doctype, txt, searchfield, start, page_len, filters):
 	cond = "1=1"
-	if filters.get('bom_no'):
-		cond += " and bom_no = '%s'" % filters.get('bom_no')
+	if filters.get("bom_no"):
+		cond += " and bom_no = '%s'" % filters.get("bom_no")
 
-	return frappe.db.sql("""select name from `tabWork Order`
+	return frappe.db.sql(
+		"""select name from `tabWork Order`
 		where name like %(name)s and {0} and produced_qty > qty and docstatus = 1
-		order by name limit {1}, {2}""".format(cond, start, page_len),{
-			'name': "%%%s%%" % txt
-		}, as_list=1)
+		order by name limit {2} offset {1}""".format(
+			cond, start, page_len
+		),
+		{"name": "%%%s%%" % txt},
+		as_list=1,
+	)
diff --git a/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py b/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py
index 88b2117..481fe51 100644
--- a/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py
+++ b/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py
@@ -11,58 +11,77 @@
 
 def get_data(report_filters):
 	data = []
-	operations = frappe.get_all("Operation", filters = {"is_corrective_operation": 1})
+	operations = frappe.get_all("Operation", filters={"is_corrective_operation": 1})
 	if operations:
-		if report_filters.get('operation'):
-			operations = [report_filters.get('operation')]
+		if report_filters.get("operation"):
+			operations = [report_filters.get("operation")]
 		else:
 			operations = [d.name for d in operations]
 
 		job_card = frappe.qb.DocType("Job Card")
 
-		operating_cost = ((job_card.hour_rate) * (job_card.total_time_in_mins) / 60.0).as_('operating_cost')
-		item_code = (job_card.production_item).as_('item_code')
+		operating_cost = ((job_card.hour_rate) * (job_card.total_time_in_mins) / 60.0).as_(
+			"operating_cost"
+		)
+		item_code = (job_card.production_item).as_("item_code")
 
-		query = (frappe.qb
-					.from_(job_card)
-					.select(job_card.name, job_card.work_order, item_code, job_card.item_name,
-						job_card.operation, job_card.serial_no, job_card.batch_no,
-						job_card.workstation, job_card.total_time_in_mins, job_card.hour_rate,
-						operating_cost)
-					.where(
-						(job_card.docstatus == 1)
-						& (job_card.is_corrective_job_card == 1))
-					.groupby(job_card.name)
-				)
+		query = (
+			frappe.qb.from_(job_card)
+			.select(
+				job_card.name,
+				job_card.work_order,
+				item_code,
+				job_card.item_name,
+				job_card.operation,
+				job_card.serial_no,
+				job_card.batch_no,
+				job_card.workstation,
+				job_card.total_time_in_mins,
+				job_card.hour_rate,
+				operating_cost,
+			)
+			.where((job_card.docstatus == 1) & (job_card.is_corrective_job_card == 1))
+			.groupby(job_card.name)
+		)
 
 		query = append_filters(query, report_filters, operations, job_card)
 		data = query.run(as_dict=True)
 	return data
 
-def append_filters(query, report_filters, operations, job_card):
-	"""Append optional filters to query builder. """
 
-	for field in ("name", "work_order", "operation", "workstation",
-			"company", "serial_no", "batch_no", "production_item"):
+def append_filters(query, report_filters, operations, job_card):
+	"""Append optional filters to query builder."""
+
+	for field in (
+		"name",
+		"work_order",
+		"operation",
+		"workstation",
+		"company",
+		"serial_no",
+		"batch_no",
+		"production_item",
+	):
 		if report_filters.get(field):
-			if field == 'serial_no':
-				query = query.where(job_card[field].like('%{}%'.format(report_filters.get(field))))
-			elif field == 'operation':
+			if field == "serial_no":
+				query = query.where(job_card[field].like("%{}%".format(report_filters.get(field))))
+			elif field == "operation":
 				query = query.where(job_card[field].isin(operations))
 			else:
 				query = query.where(job_card[field] == report_filters.get(field))
 
-	if report_filters.get('from_date') or report_filters.get('to_date'):
+	if report_filters.get("from_date") or report_filters.get("to_date"):
 		job_card_time_log = frappe.qb.DocType("Job Card Time Log")
 
 		query = query.join(job_card_time_log).on(job_card.name == job_card_time_log.parent)
-		if report_filters.get('from_date'):
-			query = query.where(job_card_time_log.from_time >= report_filters.get('from_date'))
-		if report_filters.get('to_date'):
-			query = query.where(job_card_time_log.to_time <= report_filters.get('to_date'))
+		if report_filters.get("from_date"):
+			query = query.where(job_card_time_log.from_time >= report_filters.get("from_date"))
+		if report_filters.get("to_date"):
+			query = query.where(job_card_time_log.to_time <= report_filters.get("to_date"))
 
 	return query
 
+
 def get_columns(filters):
 	return [
 		{
@@ -70,64 +89,49 @@
 			"fieldtype": "Link",
 			"fieldname": "name",
 			"options": "Job Card",
-			"width": "120"
+			"width": "120",
 		},
 		{
 			"label": _("Work Order"),
 			"fieldtype": "Link",
 			"fieldname": "work_order",
 			"options": "Work Order",
-			"width": "100"
+			"width": "100",
 		},
 		{
 			"label": _("Item Code"),
 			"fieldtype": "Link",
 			"fieldname": "item_code",
 			"options": "Item",
-			"width": "100"
+			"width": "100",
 		},
-		{
-			"label": _("Item Name"),
-			"fieldtype": "Data",
-			"fieldname": "item_name",
-			"width": "100"
-		},
+		{"label": _("Item Name"), "fieldtype": "Data", "fieldname": "item_name", "width": "100"},
 		{
 			"label": _("Operation"),
 			"fieldtype": "Link",
 			"fieldname": "operation",
 			"options": "Operation",
-			"width": "100"
+			"width": "100",
 		},
-		{
-			"label": _("Serial No"),
-			"fieldtype": "Data",
-			"fieldname": "serial_no",
-			"width": "100"
-		},
-		{
-			"label": _("Batch No"),
-			"fieldtype": "Data",
-			"fieldname": "batch_no",
-			"width": "100"
-		},
+		{"label": _("Serial No"), "fieldtype": "Data", "fieldname": "serial_no", "width": "100"},
+		{"label": _("Batch No"), "fieldtype": "Data", "fieldname": "batch_no", "width": "100"},
 		{
 			"label": _("Workstation"),
 			"fieldtype": "Link",
 			"fieldname": "workstation",
 			"options": "Workstation",
-			"width": "100"
+			"width": "100",
 		},
 		{
 			"label": _("Operating Cost"),
 			"fieldtype": "Currency",
 			"fieldname": "operating_cost",
-			"width": "150"
+			"width": "150",
 		},
 		{
 			"label": _("Total Time (in Mins)"),
 			"fieldtype": "Float",
 			"fieldname": "total_time_in_mins",
-			"width": "150"
-		}
+			"width": "150",
+		},
 	]
diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py
index 2c515d1..80a1564 100644
--- a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py
+++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py
@@ -14,10 +14,20 @@
 	chart_data = get_chart_data(data, filters)
 	return columns, data, None, chart_data
 
+
 def get_data(filters):
 	query_filters = {}
 
-	fields = ["name", "workstation", "operator", "from_time", "to_time", "downtime", "stop_reason", "remarks"]
+	fields = [
+		"name",
+		"workstation",
+		"operator",
+		"from_time",
+		"to_time",
+		"downtime",
+		"stop_reason",
+		"remarks",
+	]
 
 	query_filters["from_time"] = (">=", filters.get("from_date"))
 	query_filters["to_time"] = ("<=", filters.get("to_date"))
@@ -25,13 +35,14 @@
 	if filters.get("workstation"):
 		query_filters["workstation"] = filters.get("workstation")
 
-	data = frappe.get_all("Downtime Entry", fields= fields, filters=query_filters) or []
+	data = frappe.get_all("Downtime Entry", fields=fields, filters=query_filters) or []
 	for d in data:
 		if d.downtime:
 			d.downtime = d.downtime / 60
 
 	return data
 
+
 def get_chart_data(data, columns):
 	labels = sorted(list(set([d.workstation for d in data])))
 
@@ -47,17 +58,13 @@
 		datasets.append(workstation_wise_data.get(label, 0))
 
 	chart = {
-		"data": {
-			"labels": labels,
-			"datasets": [
-				{"name": "Machine Downtime", "values": datasets}
-			]
-		},
-		"type": "bar"
+		"data": {"labels": labels, "datasets": [{"name": "Machine Downtime", "values": datasets}]},
+		"type": "bar",
 	}
 
 	return chart
 
+
 def get_columns(filters):
 	return [
 		{
@@ -65,50 +72,25 @@
 			"fieldname": "name",
 			"fieldtype": "Link",
 			"options": "Downtime Entry",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Machine"),
 			"fieldname": "workstation",
 			"fieldtype": "Link",
 			"options": "Workstation",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Operator"),
 			"fieldname": "operator",
 			"fieldtype": "Link",
 			"options": "Employee",
-			"width": 130
+			"width": 130,
 		},
-		{
-			"label": _("From Time"),
-			"fieldname": "from_time",
-			"fieldtype": "Datetime",
-			"width": 160
-		},
-		{
-			"label": _("To Time"),
-			"fieldname": "to_time",
-			"fieldtype": "Datetime",
-			"width": 160
-		},
-		{
-			"label": _("Downtime (In Hours)"),
-			"fieldname": "downtime",
-			"fieldtype": "Float",
-			"width": 150
-		},
-		{
-			"label": _("Stop Reason"),
-			"fieldname": "stop_reason",
-			"fieldtype": "Data",
-			"width": 220
-		},
-		{
-			"label": _("Remarks"),
-			"fieldname": "remarks",
-			"fieldtype": "Text",
-			"width": 100
-		}
+		{"label": _("From Time"), "fieldname": "from_time", "fieldtype": "Datetime", "width": 160},
+		{"label": _("To Time"), "fieldname": "to_time", "fieldtype": "Datetime", "width": 160},
+		{"label": _("Downtime (In Hours)"), "fieldname": "downtime", "fieldtype": "Float", "width": 150},
+		{"label": _("Stop Reason"), "fieldname": "stop_reason", "fieldtype": "Data", "width": 220},
+		{"label": _("Remarks"), "fieldname": "remarks", "fieldtype": "Text", "width": 100},
 	]
diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py
index 26b3359..7500744 100644
--- a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py
+++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py
@@ -14,6 +14,7 @@
 def execute(filters=None):
 	return ForecastingReport(filters).execute_report()
 
+
 class ExponentialSmoothingForecast(object):
 	def forecast_future_data(self):
 		for key, value in self.period_wise_data.items():
@@ -26,24 +27,22 @@
 
 				elif forecast_data:
 					previous_period_data = forecast_data[-1]
-					value[forecast_key] = (previous_period_data[1] +
-						flt(self.filters.smoothing_constant) * (
-							flt(previous_period_data[0]) - flt(previous_period_data[1])
-						)
+					value[forecast_key] = previous_period_data[1] + flt(self.filters.smoothing_constant) * (
+						flt(previous_period_data[0]) - flt(previous_period_data[1])
 					)
 
 				if value.get(forecast_key):
 					# will be use to forecaset next period
 					forecast_data.append([value.get(period.key), value.get(forecast_key)])
 
+
 class ForecastingReport(ExponentialSmoothingForecast):
 	def __init__(self, filters=None):
 		self.filters = frappe._dict(filters or {})
 		self.data = []
 		self.doctype = self.filters.based_on_document
 		self.child_doctype = self.doctype + " Item"
-		self.based_on_field = ("qty"
-			if self.filters.based_on_field == "Qty" else "amount")
+		self.based_on_field = "qty" if self.filters.based_on_field == "Qty" else "amount"
 		self.fieldtype = "Float" if self.based_on_field == "qty" else "Currency"
 		self.company_currency = erpnext.get_company_currency(self.filters.company)
 
@@ -63,8 +62,15 @@
 		self.period_wise_data = {}
 
 		from_date = add_years(self.filters.from_date, cint(self.filters.no_of_years) * -1)
-		self.period_list = get_period_list(from_date, self.filters.to_date,
-			from_date, self.filters.to_date, "Date Range", self.filters.periodicity, ignore_fiscal_year=True)
+		self.period_list = get_period_list(
+			from_date,
+			self.filters.to_date,
+			from_date,
+			self.filters.to_date,
+			"Date Range",
+			self.filters.periodicity,
+			ignore_fiscal_year=True,
+		)
 
 		order_data = self.get_data_for_forecast() or []
 
@@ -76,8 +82,10 @@
 			period_data = self.period_wise_data[key]
 			for period in self.period_list:
 				# check if posting date is within the period
-				if (entry.posting_date >= period.from_date and entry.posting_date <= period.to_date):
-					period_data[period.key] = period_data.get(period.key, 0.0) + flt(entry.get(self.based_on_field))
+				if entry.posting_date >= period.from_date and entry.posting_date <= period.to_date:
+					period_data[period.key] = period_data.get(period.key, 0.0) + flt(
+						entry.get(self.based_on_field)
+					)
 
 		for key, value in self.period_wise_data.items():
 			list_of_period_value = [value.get(p.key, 0) for p in self.period_list]
@@ -90,12 +98,12 @@
 	def get_data_for_forecast(self):
 		cond = ""
 		if self.filters.item_code:
-			cond = " AND soi.item_code = %s" %(frappe.db.escape(self.filters.item_code))
+			cond = " AND soi.item_code = %s" % (frappe.db.escape(self.filters.item_code))
 
 		warehouses = []
 		if self.filters.warehouse:
 			warehouses = get_child_warehouses(self.filters.warehouse)
-			cond += " AND soi.warehouse in ({})".format(','.join(['%s'] * len(warehouses)))
+			cond += " AND soi.warehouse in ({})".format(",".join(["%s"] * len(warehouses)))
 
 		input_data = [self.filters.from_date, self.filters.company]
 		if warehouses:
@@ -103,7 +111,8 @@
 
 		date_field = "posting_date" if self.doctype == "Delivery Note" else "transaction_date"
 
-		return frappe.db.sql("""
+		return frappe.db.sql(
+			"""
 			SELECT
 				so.{date_field} as posting_date, soi.item_code, soi.warehouse,
 				soi.item_name, soi.stock_qty as qty, soi.base_amount as amount
@@ -112,23 +121,27 @@
 			WHERE
 				so.docstatus = 1 AND so.name = soi.parent AND
 				so.{date_field} < %s AND so.company = %s {cond}
-		""".format(doc=self.doctype, child_doc=self.child_doctype, date_field=date_field, cond=cond),
-			tuple(input_data), as_dict=1)
+		""".format(
+				doc=self.doctype, child_doc=self.child_doctype, date_field=date_field, cond=cond
+			),
+			tuple(input_data),
+			as_dict=1,
+		)
 
 	def prepare_final_data(self):
 		self.data = []
 
-		if not self.period_wise_data: return
+		if not self.period_wise_data:
+			return
 
 		for key in self.period_wise_data:
 			self.data.append(self.period_wise_data.get(key))
 
 	def add_total(self):
-		if not self.data: return
+		if not self.data:
+			return
 
-		total_row = {
-			"item_code": _(frappe.bold("Total Quantity"))
-		}
+		total_row = {"item_code": _(frappe.bold("Total Quantity"))}
 
 		for value in self.data:
 			for period in self.period_list:
@@ -145,43 +158,52 @@
 		self.data.append(total_row)
 
 	def get_columns(self):
-		columns = [{
-			"label": _("Item Code"),
-			"options": "Item",
-			"fieldname": "item_code",
-			"fieldtype": "Link",
-			"width": 130
-		}, {
-			"label": _("Warehouse"),
-			"options": "Warehouse",
-			"fieldname": "warehouse",
-			"fieldtype": "Link",
-			"width": 130
-		}]
+		columns = [
+			{
+				"label": _("Item Code"),
+				"options": "Item",
+				"fieldname": "item_code",
+				"fieldtype": "Link",
+				"width": 130,
+			},
+			{
+				"label": _("Warehouse"),
+				"options": "Warehouse",
+				"fieldname": "warehouse",
+				"fieldtype": "Link",
+				"width": 130,
+			},
+		]
 
-		width = 180 if self.filters.periodicity in ['Yearly', "Half-Yearly", "Quarterly"] else 100
+		width = 180 if self.filters.periodicity in ["Yearly", "Half-Yearly", "Quarterly"] else 100
 		for period in self.period_list:
-			if (self.filters.periodicity in ['Yearly', "Half-Yearly", "Quarterly"]
-				or period.from_date >= getdate(self.filters.from_date)):
+			if self.filters.periodicity in [
+				"Yearly",
+				"Half-Yearly",
+				"Quarterly",
+			] or period.from_date >= getdate(self.filters.from_date):
 
 				forecast_key = period.key
 				label = _(period.label)
 				if period.from_date >= getdate(self.filters.from_date):
-					forecast_key = 'forecast_' + period.key
+					forecast_key = "forecast_" + period.key
 					label = _(period.label) + " " + _("(Forecast)")
 
-				columns.append({
-					"label": label,
-					"fieldname": forecast_key,
-					"fieldtype": self.fieldtype,
-					"width": width,
-					"default": 0.0
-				})
+				columns.append(
+					{
+						"label": label,
+						"fieldname": forecast_key,
+						"fieldtype": self.fieldtype,
+						"width": width,
+						"default": 0.0,
+					}
+				)
 
 		return columns
 
 	def get_chart_data(self):
-		if not self.data: return
+		if not self.data:
+			return
 
 		labels = []
 		self.total_demand = []
@@ -206,40 +228,35 @@
 			"data": {
 				"labels": labels,
 				"datasets": [
-					{
-						"name": "Demand",
-						"values": self.total_demand
-					},
-					{
-						"name": "Forecast",
-						"values": self.total_forecast
-					}
-				]
+					{"name": "Demand", "values": self.total_demand},
+					{"name": "Forecast", "values": self.total_forecast},
+				],
 			},
-			"type": "line"
+			"type": "line",
 		}
 
 	def get_summary_data(self):
-		if not self.data: return
+		if not self.data:
+			return
 
 		return [
 			{
 				"value": sum(self.total_demand),
 				"label": _("Total Demand (Past Data)"),
 				"currency": self.company_currency,
-				"datatype": self.fieldtype
+				"datatype": self.fieldtype,
 			},
 			{
 				"value": sum(self.total_history_forecast),
 				"label": _("Total Forecast (Past Data)"),
 				"currency": self.company_currency,
-				"datatype": self.fieldtype
+				"datatype": self.fieldtype,
 			},
 			{
 				"value": sum(self.total_future_forecast),
 				"indicator": "Green",
 				"label": _("Total Forecast (Future Data)"),
 				"currency": self.company_currency,
-				"datatype": self.fieldtype
-			}
+				"datatype": self.fieldtype,
+			},
 		]
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 4046bb1..a86c7a4 100644
--- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py
+++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py
@@ -16,23 +16,34 @@
 	chart_data = get_chart_data(data, filters)
 	return columns, data, None, chart_data
 
+
 def get_data(filters):
 	query_filters = {
 		"docstatus": ("<", 2),
-		"posting_date": ("between", [filters.from_date, filters.to_date])
+		"posting_date": ("between", [filters.from_date, filters.to_date]),
 	}
 
-	fields = ["name", "status", "work_order", "production_item", "item_name", "posting_date",
-		"total_completed_qty", "workstation", "operation", "total_time_in_mins"]
+	fields = [
+		"name",
+		"status",
+		"work_order",
+		"production_item",
+		"item_name",
+		"posting_date",
+		"total_completed_qty",
+		"workstation",
+		"operation",
+		"total_time_in_mins",
+	]
 
 	for field in ["work_order", "workstation", "operation", "company"]:
 		if filters.get(field):
 			query_filters[field] = ("in", filters.get(field))
 
-	data = frappe.get_all("Job Card",
-		fields= fields, filters=query_filters)
+	data = frappe.get_all("Job Card", fields=fields, filters=query_filters)
 
-	if not data: return []
+	if not data:
+		return []
 
 	job_cards = [d.name for d in data]
 
@@ -42,9 +53,12 @@
 	}
 
 	job_card_time_details = {}
-	for job_card_data in frappe.get_all("Job Card Time Log",
+	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"):
+		filters=job_card_time_filter,
+		group_by="parent",
+	):
 		job_card_time_details[job_card_data.parent] = job_card_data
 
 	res = []
@@ -60,6 +74,7 @@
 
 	return res
 
+
 def get_chart_data(job_card_details, filters):
 	labels, periodic_data = prepare_chart_data(job_card_details, filters)
 
@@ -73,23 +88,15 @@
 	datasets.append({"name": "Open", "values": open_job_cards})
 	datasets.append({"name": "Completed", "values": completed})
 
-	chart = {
-		"data": {
-			'labels': labels,
-			'datasets': datasets
-		},
-		"type": "bar"
-	}
+	chart = {"data": {"labels": labels, "datasets": datasets}, "type": "bar"}
 
 	return chart
 
+
 def prepare_chart_data(job_card_details, filters):
 	labels = []
 
-	periodic_data = {
-		"Open": {},
-		"Completed": {}
-	}
+	periodic_data = {"Open": {}, "Completed": {}}
 
 	filters.range = "Monthly"
 
@@ -110,6 +117,7 @@
 
 	return labels, periodic_data
 
+
 def get_columns(filters):
 	columns = [
 		{
@@ -117,84 +125,62 @@
 			"fieldname": "name",
 			"fieldtype": "Link",
 			"options": "Job Card",
-			"width": 100
+			"width": 100,
 		},
-		{
-			"label": _("Posting Date"),
-			"fieldname": "posting_date",
-			"fieldtype": "Date",
-			"width": 100
-		},
+		{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 100},
 	]
 
 	if not filters.get("status"):
 		columns.append(
-			{
-				"label": _("Status"),
-				"fieldname": "status",
-				"width": 100
-			},
+			{"label": _("Status"), "fieldname": "status", "width": 100},
 		)
 
-	columns.extend([
-		{
-			"label": _("Work Order"),
-			"fieldname": "work_order",
-			"fieldtype": "Link",
-			"options": "Work Order",
-			"width": 100
-		},
-		{
-			"label": _("Production Item"),
-			"fieldname": "production_item",
-			"fieldtype": "Link",
-			"options": "Item",
-			"width": 110
-		},
-		{
-			"label": _("Item Name"),
-			"fieldname": "item_name",
-			"fieldtype": "Data",
-			"width": 100
-		},
-		{
-			"label": _("Workstation"),
-			"fieldname": "workstation",
-			"fieldtype": "Link",
-			"options": "Workstation",
-			"width": 110
-		},
-		{
-			"label": _("Operation"),
-			"fieldname": "operation",
-			"fieldtype": "Link",
-			"options": "Operation",
-			"width": 110
-		},
-		{
-			"label": _("Total Completed Qty"),
-			"fieldname": "total_completed_qty",
-			"fieldtype": "Float",
-			"width": 120
-		},
-		{
-			"label": _("From Time"),
-			"fieldname": "from_time",
-			"fieldtype": "Datetime",
-			"width": 120
-		},
-		{
-			"label": _("To Time"),
-			"fieldname": "to_time",
-			"fieldtype": "Datetime",
-			"width": 120
-		},
-		{
-			"label": _("Time Required (In Mins)"),
-			"fieldname": "total_time_in_mins",
-			"fieldtype": "Float",
-			"width": 100
-		}
-	])
+	columns.extend(
+		[
+			{
+				"label": _("Work Order"),
+				"fieldname": "work_order",
+				"fieldtype": "Link",
+				"options": "Work Order",
+				"width": 100,
+			},
+			{
+				"label": _("Production Item"),
+				"fieldname": "production_item",
+				"fieldtype": "Link",
+				"options": "Item",
+				"width": 110,
+			},
+			{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 100},
+			{
+				"label": _("Workstation"),
+				"fieldname": "workstation",
+				"fieldtype": "Link",
+				"options": "Workstation",
+				"width": 110,
+			},
+			{
+				"label": _("Operation"),
+				"fieldname": "operation",
+				"fieldtype": "Link",
+				"options": "Operation",
+				"width": 110,
+			},
+			{
+				"label": _("Total Completed Qty"),
+				"fieldname": "total_completed_qty",
+				"fieldtype": "Float",
+				"width": 120,
+			},
+			{"label": _("From Time"), "fieldname": "from_time", "fieldtype": "Datetime", "width": 120},
+			{"label": _("To Time"), "fieldname": "to_time", "fieldtype": "Datetime", "width": 120},
+			{
+				"label": _("Time Required (In Mins)"),
+				"fieldname": "total_time_in_mins",
+				"fieldtype": "Float",
+				"width": 100,
+			},
+		]
+	)
 
 	return columns
diff --git a/erpnext/manufacturing/report/process_loss_report/process_loss_report.py b/erpnext/manufacturing/report/process_loss_report/process_loss_report.py
index 9b544da..b10e643 100644
--- a/erpnext/manufacturing/report/process_loss_report/process_loss_report.py
+++ b/erpnext/manufacturing/report/process_loss_report/process_loss_report.py
@@ -12,87 +12,71 @@
 Columns = List[Dict[str, str]]
 QueryArgs = Dict[str, str]
 
+
 def execute(filters: Filters) -> Tuple[Columns, Data]:
 	columns = get_columns()
 	data = get_data(filters)
 	return columns, data
 
+
 def get_data(filters: Filters) -> Data:
 	query_args = get_query_args(filters)
 	data = run_query(query_args)
 	update_data_with_total_pl_value(data)
 	return data
 
+
 def get_columns() -> Columns:
 	return [
 		{
-			'label': _('Work Order'),
-			'fieldname': 'name',
-			'fieldtype': 'Link',
-			'options': 'Work Order',
-			'width': '200'
+			"label": _("Work Order"),
+			"fieldname": "name",
+			"fieldtype": "Link",
+			"options": "Work Order",
+			"width": "200",
 		},
 		{
-			'label': _('Item'),
-			'fieldname': 'production_item',
-			'fieldtype': 'Link',
-			'options': 'Item',
-			'width': '100'
+			"label": _("Item"),
+			"fieldname": "production_item",
+			"fieldtype": "Link",
+			"options": "Item",
+			"width": "100",
 		},
+		{"label": _("Status"), "fieldname": "status", "fieldtype": "Data", "width": "100"},
 		{
-			'label': _('Status'),
-			'fieldname': 'status',
-			'fieldtype': 'Data',
-			'width': '100'
+			"label": _("Manufactured Qty"),
+			"fieldname": "produced_qty",
+			"fieldtype": "Float",
+			"width": "150",
 		},
+		{"label": _("Loss Qty"), "fieldname": "process_loss_qty", "fieldtype": "Float", "width": "150"},
 		{
-			'label': _('Manufactured Qty'),
-			'fieldname': 'produced_qty',
-			'fieldtype': 'Float',
-			'width': '150'
+			"label": _("Actual Manufactured Qty"),
+			"fieldname": "actual_produced_qty",
+			"fieldtype": "Float",
+			"width": "150",
 		},
+		{"label": _("Loss Value"), "fieldname": "total_pl_value", "fieldtype": "Float", "width": "150"},
+		{"label": _("FG Value"), "fieldname": "total_fg_value", "fieldtype": "Float", "width": "150"},
 		{
-			'label': _('Loss Qty'),
-			'fieldname': 'process_loss_qty',
-			'fieldtype': 'Float',
-			'width': '150'
+			"label": _("Raw Material Value"),
+			"fieldname": "total_rm_value",
+			"fieldtype": "Float",
+			"width": "150",
 		},
-		{
-			'label': _('Actual Manufactured Qty'),
-			'fieldname': 'actual_produced_qty',
-			'fieldtype': 'Float',
-			'width': '150'
-		},
-		{
-			'label': _('Loss Value'),
-			'fieldname': 'total_pl_value',
-			'fieldtype': 'Float',
-			'width': '150'
-		},
-		{
-			'label': _('FG Value'),
-			'fieldname': 'total_fg_value',
-			'fieldtype': 'Float',
-			'width': '150'
-		},
-		{
-			'label': _('Raw Material Value'),
-			'fieldname': 'total_rm_value',
-			'fieldtype': 'Float',
-			'width': '150'
-		}
 	]
 
+
 def get_query_args(filters: Filters) -> QueryArgs:
 	query_args = {}
 	query_args.update(filters)
-	query_args.update(
-		get_filter_conditions(filters)
-	)
+	query_args.update(get_filter_conditions(filters))
 	return query_args
 
+
 def run_query(query_args: QueryArgs) -> Data:
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		SELECT
 			wo.name, wo.status, wo.production_item, wo.qty,
 			wo.produced_qty, wo.process_loss_qty,
@@ -111,23 +95,26 @@
 			{work_order_filter}
 		GROUP BY
 			se.work_order
-	""".format(**query_args), query_args, as_dict=1)
+	""".format(
+			**query_args
+		),
+		query_args,
+		as_dict=1,
+	)
+
 
 def update_data_with_total_pl_value(data: Data) -> None:
 	for row in data:
-		value_per_unit_fg = row['total_fg_value'] / row['actual_produced_qty']
-		row['total_pl_value'] = row['process_loss_qty'] * value_per_unit_fg
+		value_per_unit_fg = row["total_fg_value"] / row["actual_produced_qty"]
+		row["total_pl_value"] = row["process_loss_qty"] * value_per_unit_fg
+
 
 def get_filter_conditions(filters: Filters) -> QueryArgs:
 	filter_conditions = dict(item_filter="", work_order_filter="")
 	if "item" in filters:
 		production_item = filters.get("item")
-		filter_conditions.update(
-			{"item_filter": f"AND wo.production_item='{production_item}'"}
-		)
+		filter_conditions.update({"item_filter": f"AND wo.production_item='{production_item}'"})
 	if "work_order" in filters:
 		work_order_name = filters.get("work_order")
-		filter_conditions.update(
-			{"work_order_filter": f"AND wo.name='{work_order_name}'"}
-		)
+		filter_conditions.update({"work_order_filter": f"AND wo.name='{work_order_name}'"})
 	return filter_conditions
diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.py b/erpnext/manufacturing/report/production_analytics/production_analytics.py
index d4743d3..12b5d19 100644
--- a/erpnext/manufacturing/report/production_analytics/production_analytics.py
+++ b/erpnext/manufacturing/report/production_analytics/production_analytics.py
@@ -12,16 +12,11 @@
 def execute(filters=None):
 	columns = get_columns(filters)
 	data, chart = get_data(filters, columns)
-	return columns, data, None , chart
+	return columns, data, None, chart
+
 
 def get_columns(filters):
-	columns =[
-		{
-			"label": _("Status"),
-			"fieldname": "Status",
-			"fieldtype": "Data",
-			"width": 140
-		}]
+	columns = [{"label": _("Status"), "fieldname": "Status", "fieldtype": "Data", "width": 140}]
 
 	ranges = get_period_date_ranges(filters)
 
@@ -29,22 +24,20 @@
 
 		period = get_period(end_date, filters)
 
-		columns.append({
-			"label": _(period),
-			"fieldname": scrub(period),
-			"fieldtype": "Float",
-			"width": 120
-		})
+		columns.append(
+			{"label": _(period), "fieldname": scrub(period), "fieldtype": "Float", "width": 120}
+		)
 
 	return columns
 
+
 def get_periodic_data(filters, entry):
 	periodic_data = {
 		"All Work Orders": {},
 		"Not Started": {},
 		"Overdue": {},
 		"Pending": {},
-		"Completed": {}
+		"Completed": {},
 	}
 
 	ranges = get_period_date_ranges(filters)
@@ -52,34 +45,37 @@
 	for from_date, end_date in ranges:
 		period = get_period(end_date, filters)
 		for d in entry:
-			if getdate(d.creation) <= getdate(from_date) or getdate(d.creation) <= getdate(end_date) :
+			if getdate(d.creation) <= getdate(from_date) or getdate(d.creation) <= getdate(end_date):
 				periodic_data = update_periodic_data(periodic_data, "All Work Orders", period)
-				if d.status == 'Completed':
-					if getdate(d.actual_end_date) < getdate(from_date) or getdate(d.modified) < getdate(from_date):
+				if d.status == "Completed":
+					if getdate(d.actual_end_date) < getdate(from_date) or getdate(d.modified) < getdate(
+						from_date
+					):
 						periodic_data = update_periodic_data(periodic_data, "Completed", period)
-					elif getdate(d.actual_start_date) < getdate(from_date) :
+					elif getdate(d.actual_start_date) < getdate(from_date):
 						periodic_data = update_periodic_data(periodic_data, "Pending", period)
-					elif getdate(d.planned_start_date) < getdate(from_date) :
+					elif getdate(d.planned_start_date) < getdate(from_date):
 						periodic_data = update_periodic_data(periodic_data, "Overdue", period)
 					else:
 						periodic_data = update_periodic_data(periodic_data, "Not Started", period)
 
-				elif d.status == 'In Process':
-					if getdate(d.actual_start_date) < getdate(from_date) :
+				elif d.status == "In Process":
+					if getdate(d.actual_start_date) < getdate(from_date):
 						periodic_data = update_periodic_data(periodic_data, "Pending", period)
-					elif getdate(d.planned_start_date) < getdate(from_date) :
+					elif getdate(d.planned_start_date) < getdate(from_date):
 						periodic_data = update_periodic_data(periodic_data, "Overdue", period)
 					else:
 						periodic_data = update_periodic_data(periodic_data, "Not Started", period)
 
-				elif d.status == 'Not Started':
-					if getdate(d.planned_start_date) < getdate(from_date) :
+				elif d.status == "Not Started":
+					if getdate(d.planned_start_date) < getdate(from_date):
 						periodic_data = update_periodic_data(periodic_data, "Overdue", period)
 					else:
 						periodic_data = update_periodic_data(periodic_data, "Not Started", period)
 
 	return periodic_data
 
+
 def update_periodic_data(periodic_data, status, period):
 	if periodic_data.get(status).get(period):
 		periodic_data[status][period] += 1
@@ -88,22 +84,33 @@
 
 	return periodic_data
 
+
 def get_data(filters, columns):
 	data = []
-	entry = frappe.get_all("Work Order",
-		fields=["creation", "modified", "actual_start_date", "actual_end_date", "planned_start_date", "planned_end_date", "status"],
-		filters={"docstatus": 1, "company": filters["company"] })
+	entry = frappe.get_all(
+		"Work Order",
+		fields=[
+			"creation",
+			"modified",
+			"actual_start_date",
+			"actual_end_date",
+			"planned_start_date",
+			"planned_end_date",
+			"status",
+		],
+		filters={"docstatus": 1, "company": filters["company"]},
+	)
 
-	periodic_data = get_periodic_data(filters,entry)
+	periodic_data = get_periodic_data(filters, entry)
 
 	labels = ["All Work Orders", "Not Started", "Overdue", "Pending", "Completed"]
-	chart_data = get_chart_data(periodic_data,columns)
+	chart_data = get_chart_data(periodic_data, columns)
 	ranges = get_period_date_ranges(filters)
 
 	for label in labels:
 		work = {}
 		work["Status"] = label
-		for dummy,end_date in ranges:
+		for dummy, end_date in ranges:
 			period = get_period(end_date, filters)
 			if periodic_data.get(label).get(period):
 				work[scrub(period)] = periodic_data.get(label).get(period)
@@ -113,10 +120,11 @@
 
 	return data, chart_data
 
+
 def get_chart_data(periodic_data, columns):
 	labels = [d.get("label") for d in columns[1:]]
 
-	all_data, not_start, overdue, pending, completed = [], [], [] , [], []
+	all_data, not_start, overdue, pending, completed = [], [], [], [], []
 	datasets = []
 
 	for d in labels:
@@ -126,18 +134,13 @@
 		pending.append(periodic_data.get("Pending").get(d))
 		completed.append(periodic_data.get("Completed").get(d))
 
-	datasets.append({'name':'All Work Orders', 'values': all_data})
-	datasets.append({'name':'Not Started', 'values': not_start})
-	datasets.append({'name':'Overdue', 'values': overdue})
-	datasets.append({'name':'Pending', 'values': pending})
-	datasets.append({'name':'Completed', 'values': completed})
+	datasets.append({"name": "All Work Orders", "values": all_data})
+	datasets.append({"name": "Not Started", "values": not_start})
+	datasets.append({"name": "Overdue", "values": overdue})
+	datasets.append({"name": "Pending", "values": pending})
+	datasets.append({"name": "Completed", "values": completed})
 
-	chart = {
-		"data": {
-			'labels': labels,
-			'datasets': datasets
-		}
-	}
+	chart = {"data": {"labels": labels, "datasets": datasets}}
 	chart["type"] = "line"
 
 	return chart
diff --git a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py
index aaa2314..2c8f82f 100644
--- a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py
+++ b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py
@@ -3,6 +3,7 @@
 
 
 import frappe
+from frappe import _
 from frappe.utils import flt
 
 
@@ -13,6 +14,7 @@
 
 	return columns, data
 
+
 def get_data(filters):
 	data = []
 
@@ -23,6 +25,7 @@
 
 	return data
 
+
 def get_production_plan_item_details(filters, data, order_details):
 	itemwise_indent = {}
 
@@ -30,127 +33,110 @@
 	for row in production_plan_doc.po_items:
 		work_order = frappe.get_value(
 			"Work Order",
-			{
-				"production_plan_item": row.name,
-				"bom_no": row.bom_no,
-				"production_item": row.item_code
-			},
-			"name"
+			{"production_plan_item": row.name, "bom_no": row.bom_no, "production_item": row.item_code},
+			"name",
 		)
 
 		if row.item_code not in itemwise_indent:
 			itemwise_indent.setdefault(row.item_code, {})
 
-		data.append({
-			"indent": 0,
-			"item_code": row.item_code,
-			"item_name": frappe.get_cached_value("Item", row.item_code, "item_name"),
-			"qty": row.planned_qty,
-			"document_type": "Work Order",
-			"document_name": work_order or "",
-			"bom_level": 0,
-			"produced_qty": order_details.get((work_order, row.item_code), {}).get("produced_qty", 0),
-			"pending_qty": flt(row.planned_qty) - flt(order_details.get((work_order, row.item_code), {}).get("produced_qty", 0))
-		})
+		data.append(
+			{
+				"indent": 0,
+				"item_code": row.item_code,
+				"item_name": frappe.get_cached_value("Item", row.item_code, "item_name"),
+				"qty": row.planned_qty,
+				"document_type": "Work Order",
+				"document_name": work_order or "",
+				"bom_level": 0,
+				"produced_qty": order_details.get((work_order, row.item_code), {}).get("produced_qty", 0),
+				"pending_qty": flt(row.planned_qty)
+				- flt(order_details.get((work_order, row.item_code), {}).get("produced_qty", 0)),
+			}
+		)
 
-		get_production_plan_sub_assembly_item_details(filters, row, production_plan_doc, data, order_details)
+		get_production_plan_sub_assembly_item_details(
+			filters, row, production_plan_doc, data, order_details
+		)
 
-def get_production_plan_sub_assembly_item_details(filters, row, production_plan_doc, data, order_details):
+
+def get_production_plan_sub_assembly_item_details(
+	filters, row, production_plan_doc, data, order_details
+):
 	for item in production_plan_doc.sub_assembly_items:
 		if row.name == item.production_plan_item:
-			subcontracted_item = (item.type_of_manufacturing == 'Subcontract')
+			subcontracted_item = item.type_of_manufacturing == "Subcontract"
 
 			if subcontracted_item:
 				docname = frappe.get_value(
 					"Purchase Order Item",
-					{
-						"production_plan_sub_assembly_item": item.name,
-						"docstatus": ("<", 2)
-					},
-					"parent"
+					{"production_plan_sub_assembly_item": item.name, "docstatus": ("<", 2)},
+					"parent",
 				)
 			else:
 				docname = frappe.get_value(
-					"Work Order",
-					{
-						"production_plan_sub_assembly_item": item.name,
-						"docstatus": ("<", 2)
-					},
-					"name"
+					"Work Order", {"production_plan_sub_assembly_item": item.name, "docstatus": ("<", 2)}, "name"
 				)
 
-			data.append({
-				"indent": 1,
-				"item_code": item.production_item,
-				"item_name": item.item_name,
-				"qty": item.qty,
-				"document_type": "Work Order" if not subcontracted_item else "Purchase Order",
-				"document_name": docname or "",
-				"bom_level": item.bom_level,
-				"produced_qty": order_details.get((docname, item.production_item), {}).get("produced_qty", 0),
-				"pending_qty": flt(item.qty) - flt(order_details.get((docname, item.production_item), {}).get("produced_qty", 0))
-			})
+			data.append(
+				{
+					"indent": 1,
+					"item_code": item.production_item,
+					"item_name": item.item_name,
+					"qty": item.qty,
+					"document_type": "Work Order" if not subcontracted_item else "Purchase Order",
+					"document_name": docname or "",
+					"bom_level": item.bom_level,
+					"produced_qty": order_details.get((docname, item.production_item), {}).get("produced_qty", 0),
+					"pending_qty": flt(item.qty)
+					- flt(order_details.get((docname, item.production_item), {}).get("produced_qty", 0)),
+				}
+			)
+
 
 def get_work_order_details(filters, order_details):
-	for row in frappe.get_all("Work Order", filters = {"production_plan": filters.get("production_plan")},
-		fields=["name", "produced_qty", "production_plan", "production_item"]):
+	for row in frappe.get_all(
+		"Work Order",
+		filters={"production_plan": filters.get("production_plan")},
+		fields=["name", "produced_qty", "production_plan", "production_item"],
+	):
 		order_details.setdefault((row.name, row.production_item), row)
 
+
 def get_purchase_order_details(filters, order_details):
-	for row in frappe.get_all("Purchase Order Item", filters = {"production_plan": filters.get("production_plan")},
-		fields=["parent", "received_qty as produced_qty", "item_code"]):
+	for row in frappe.get_all(
+		"Purchase Order Item",
+		filters={"production_plan": filters.get("production_plan")},
+		fields=["parent", "received_qty as produced_qty", "item_code"],
+	):
 		order_details.setdefault((row.parent, row.item_code), row)
 
+
 def get_column(filters):
 	return [
 		{
-			"label": "Finished Good",
+			"label": _("Finished Good"),
 			"fieldtype": "Link",
 			"fieldname": "item_code",
 			"width": 300,
-			"options": "Item"
+			"options": "Item",
 		},
+		{"label": _("Item Name"), "fieldtype": "data", "fieldname": "item_name", "width": 100},
 		{
-			"label": "Item Name",
-			"fieldtype": "data",
-			"fieldname": "item_name",
-			"width": 100
-		},
-		{
-			"label": "Document Type",
+			"label": _("Document Type"),
 			"fieldtype": "Link",
 			"fieldname": "document_type",
 			"width": 150,
-			"options": "DocType"
+			"options": "DocType",
 		},
 		{
-			"label": "Document Name",
+			"label": _("Document Name"),
 			"fieldtype": "Dynamic Link",
 			"fieldname": "document_name",
-			"width": 150
+			"width": 150,
 		},
-		{
-			"label": "BOM Level",
-			"fieldtype": "Int",
-			"fieldname": "bom_level",
-			"width": 100
-		},
-		{
-			"label": "Order Qty",
-			"fieldtype": "Float",
-			"fieldname": "qty",
-			"width": 120
-		},
-		{
-			"label": "Received Qty",
-			"fieldtype": "Float",
-			"fieldname": "produced_qty",
-			"width": 160
-		},
-		{
-			"label": "Pending Qty",
-			"fieldtype": "Float",
-			"fieldname": "pending_qty",
-			"width": 110
-		}
+		{"label": _("BOM Level"), "fieldtype": "Int", "fieldname": "bom_level", "width": 100},
+		{"label": _("Order Qty"), "fieldtype": "Float", "fieldname": "qty", "width": 120},
+		{"label": _("Received Qty"), "fieldtype": "Float", "fieldname": "produced_qty", "width": 160},
+		{"label": _("Pending Qty"), "fieldtype": "Float", "fieldname": "pending_qty", "width": 110},
 	]
diff --git a/erpnext/manufacturing/report/production_planning_report/production_planning_report.py b/erpnext/manufacturing/report/production_planning_report/production_planning_report.py
index e1e7225..1404888 100644
--- a/erpnext/manufacturing/report/production_planning_report/production_planning_report.py
+++ b/erpnext/manufacturing/report/production_planning_report/production_planning_report.py
@@ -15,38 +15,36 @@
 			stock_qty as qty_to_manufacture, `tabSales Order Item`.parent as name, bom_no, warehouse,
 			`tabSales Order Item`.delivery_date, `tabSales Order`.base_grand_total """,
 		"filters": """`tabSales Order Item`.docstatus = 1 and stock_qty > produced_qty
-			and `tabSales Order`.per_delivered < 100.0"""
+			and `tabSales Order`.per_delivered < 100.0""",
 	},
 	"Material Request": {
 		"fields": """ item_code as production_item, item_name as production_item_name, stock_uom,
 			stock_qty as qty_to_manufacture, `tabMaterial Request Item`.parent as name, bom_no, warehouse,
 			`tabMaterial Request Item`.schedule_date """,
 		"filters": """`tabMaterial Request`.docstatus = 1 and `tabMaterial Request`.per_ordered < 100
-			and `tabMaterial Request`.material_request_type = 'Manufacture' """
+			and `tabMaterial Request`.material_request_type = 'Manufacture' """,
 	},
 	"Work Order": {
 		"fields": """ production_item, item_name as production_item_name, planned_start_date,
 			stock_uom, qty as qty_to_manufacture, name, bom_no, fg_warehouse as warehouse """,
-		"filters": "docstatus = 1 and status not in ('Completed', 'Stopped')"
+		"filters": "docstatus = 1 and status not in ('Completed', 'Stopped')",
 	},
 }
 
 order_mapper = {
 	"Sales Order": {
 		"Delivery Date": "`tabSales Order Item`.delivery_date asc",
-		"Total Amount": "`tabSales Order`.base_grand_total desc"
+		"Total Amount": "`tabSales Order`.base_grand_total desc",
 	},
-	"Material Request": {
-		"Required Date": "`tabMaterial Request Item`.schedule_date asc"
-	},
-	"Work Order": {
-		"Planned Start Date": "planned_start_date asc"
-	}
+	"Material Request": {"Required Date": "`tabMaterial Request Item`.schedule_date asc"},
+	"Work Order": {"Planned Start Date": "planned_start_date asc"},
 }
 
+
 def execute(filters=None):
 	return ProductionPlanReport(filters).execute_report()
 
+
 class ProductionPlanReport(object):
 	def __init__(self, filters=None):
 		self.filters = frappe._dict(filters or {})
@@ -65,46 +63,64 @@
 		return self.columns, self.data
 
 	def get_open_orders(self):
-		doctype = ("`tabWork Order`" if self.filters.based_on == "Work Order"
-			else "`tab{doc}`, `tab{doc} Item`".format(doc=self.filters.based_on))
+		doctype = (
+			"`tabWork Order`"
+			if self.filters.based_on == "Work Order"
+			else "`tab{doc}`, `tab{doc} Item`".format(doc=self.filters.based_on)
+		)
 
 		filters = mapper.get(self.filters.based_on)["filters"]
 		filters = self.prepare_other_conditions(filters, self.filters.based_on)
 		order_by = " ORDER BY %s" % (order_mapper[self.filters.based_on][self.filters.order_by])
 
-		self.orders = frappe.db.sql(""" SELECT {fields} from {doctype}
+		self.orders = frappe.db.sql(
+			""" SELECT {fields} from {doctype}
 			WHERE {filters} {order_by}""".format(
-				doctype = doctype,
-				filters = filters,
-				order_by = order_by,
-				fields = mapper.get(self.filters.based_on)["fields"]
-			), tuple(self.filters.docnames), as_dict=1)
+				doctype=doctype,
+				filters=filters,
+				order_by=order_by,
+				fields=mapper.get(self.filters.based_on)["fields"],
+			),
+			tuple(self.filters.docnames),
+			as_dict=1,
+		)
 
 	def prepare_other_conditions(self, filters, doctype):
 		if self.filters.docnames:
 			field = "name" if doctype == "Work Order" else "`tab{} Item`.parent".format(doctype)
-			filters += " and %s in (%s)" % (field, ','.join(['%s'] * len(self.filters.docnames)))
+			filters += " and %s in (%s)" % (field, ",".join(["%s"] * len(self.filters.docnames)))
 
 		if doctype != "Work Order":
 			filters += " and `tab{doc}`.name = `tab{doc} Item`.parent".format(doc=doctype)
 
 		if self.filters.company:
-			filters += " and `tab%s`.company = %s" %(doctype, frappe.db.escape(self.filters.company))
+			filters += " and `tab%s`.company = %s" % (doctype, frappe.db.escape(self.filters.company))
 
 		return filters
 
 	def get_raw_materials(self):
-		if not self.orders: return
+		if not self.orders:
+			return
 		self.warehouses = [d.warehouse for d in self.orders]
 		self.item_codes = [d.production_item for d in self.orders]
 
 		if self.filters.based_on == "Work Order":
 			work_orders = [d.name for d in self.orders]
 
-			raw_materials = frappe.get_all("Work Order Item",
-				fields=["parent", "item_code", "item_name as raw_material_name",
-					"source_warehouse as warehouse", "required_qty"],
-				filters = {"docstatus": 1, "parent": ("in", work_orders), "source_warehouse": ("!=", "")}) or []
+			raw_materials = (
+				frappe.get_all(
+					"Work Order Item",
+					fields=[
+						"parent",
+						"item_code",
+						"item_name as raw_material_name",
+						"source_warehouse as warehouse",
+						"required_qty",
+					],
+					filters={"docstatus": 1, "parent": ("in", work_orders), "source_warehouse": ("!=", "")},
+				)
+				or []
+			)
 			self.warehouses.extend([d.source_warehouse for d in raw_materials])
 
 		else:
@@ -118,21 +134,32 @@
 
 				bom_nos.append(bom_no)
 
-			bom_doctype = ("BOM Explosion Item"
-				if self.filters.include_subassembly_raw_materials else "BOM Item")
+			bom_doctype = (
+				"BOM Explosion Item" if self.filters.include_subassembly_raw_materials else "BOM Item"
+			)
 
-			qty_field = ("qty_consumed_per_unit"
-				if self.filters.include_subassembly_raw_materials else "(bom_item.qty / bom.quantity)")
+			qty_field = (
+				"qty_consumed_per_unit"
+				if self.filters.include_subassembly_raw_materials
+				else "(bom_item.qty / bom.quantity)"
+			)
 
-			raw_materials = frappe.db.sql(""" SELECT bom_item.parent, bom_item.item_code,
+			raw_materials = frappe.db.sql(
+				""" SELECT bom_item.parent, bom_item.item_code,
 					bom_item.item_name as raw_material_name, {0} as required_qty_per_unit
 				FROM
 					`tabBOM` as bom, `tab{1}` as bom_item
 				WHERE
 					bom_item.parent in ({2}) and bom_item.parent = bom.name and bom.docstatus = 1
-			""".format(qty_field, bom_doctype, ','.join(["%s"] * len(bom_nos))), tuple(bom_nos), as_dict=1)
+			""".format(
+					qty_field, bom_doctype, ",".join(["%s"] * len(bom_nos))
+				),
+				tuple(bom_nos),
+				as_dict=1,
+			)
 
-		if not raw_materials: return
+		if not raw_materials:
+			return
 
 		self.item_codes.extend([d.item_code for d in raw_materials])
 
@@ -144,15 +171,20 @@
 			rows.append(d)
 
 	def get_item_details(self):
-		if not (self.orders and self.item_codes): return
+		if not (self.orders and self.item_codes):
+			return
 
 		self.item_details = {}
-		for d in frappe.get_all("Item Default", fields = ["parent", "default_warehouse"],
-			filters = {"company": self.filters.company, "parent": ("in", self.item_codes)}):
+		for d in frappe.get_all(
+			"Item Default",
+			fields=["parent", "default_warehouse"],
+			filters={"company": self.filters.company, "parent": ("in", self.item_codes)},
+		):
 			self.item_details[d.parent] = d
 
 	def get_bin_details(self):
-		if not (self.orders and self.raw_materials_dict): return
+		if not (self.orders and self.raw_materials_dict):
+			return
 
 		self.bin_details = {}
 		self.mrp_warehouses = []
@@ -160,48 +192,55 @@
 			self.mrp_warehouses.extend(get_child_warehouses(self.filters.raw_material_warehouse))
 			self.warehouses.extend(self.mrp_warehouses)
 
-		for d in frappe.get_all("Bin",
+		for d in frappe.get_all(
+			"Bin",
 			fields=["warehouse", "item_code", "actual_qty", "ordered_qty", "projected_qty"],
-			filters = {"item_code": ("in", self.item_codes), "warehouse": ("in", self.warehouses)}):
+			filters={"item_code": ("in", self.item_codes), "warehouse": ("in", self.warehouses)},
+		):
 			key = (d.item_code, d.warehouse)
 			if key not in self.bin_details:
 				self.bin_details.setdefault(key, d)
 
 	def get_purchase_details(self):
-		if not (self.orders and self.raw_materials_dict): return
+		if not (self.orders and self.raw_materials_dict):
+			return
 
 		self.purchase_details = {}
 
-		purchased_items = frappe.get_all("Purchase Order Item",
+		purchased_items = frappe.get_all(
+			"Purchase Order Item",
 			fields=["item_code", "min(schedule_date) as arrival_date", "qty as arrival_qty", "warehouse"],
 			filters={
 				"item_code": ("in", self.item_codes),
 				"warehouse": ("in", self.warehouses),
 				"docstatus": 1,
 			},
-			group_by = "item_code, warehouse")
+			group_by="item_code, warehouse",
+		)
 		for d in purchased_items:
 			key = (d.item_code, d.warehouse)
 			if key not in self.purchase_details:
 				self.purchase_details.setdefault(key, d)
 
 	def prepare_data(self):
-		if not self.orders: return
+		if not self.orders:
+			return
 
 		for d in self.orders:
 			key = d.name if self.filters.based_on == "Work Order" else d.bom_no
 
-			if not self.raw_materials_dict.get(key): continue
+			if not self.raw_materials_dict.get(key):
+				continue
 
 			bin_data = self.bin_details.get((d.production_item, d.warehouse)) or {}
-			d.update({
-				"for_warehouse": d.warehouse,
-				"available_qty": 0
-			})
+			d.update({"for_warehouse": d.warehouse, "available_qty": 0})
 
 			if bin_data and bin_data.get("actual_qty") > 0 and d.qty_to_manufacture:
-				d.available_qty = (bin_data.get("actual_qty")
-					if (d.qty_to_manufacture > bin_data.get("actual_qty")) else d.qty_to_manufacture)
+				d.available_qty = (
+					bin_data.get("actual_qty")
+					if (d.qty_to_manufacture > bin_data.get("actual_qty"))
+					else d.qty_to_manufacture
+				)
 
 				bin_data["actual_qty"] -= d.available_qty
 
@@ -232,8 +271,9 @@
 			d.remaining_qty = d.required_qty
 			self.pick_materials_from_warehouses(d, data, warehouses)
 
-			if (d.remaining_qty and self.filters.raw_material_warehouse
-				and d.remaining_qty != d.required_qty):
+			if (
+				d.remaining_qty and self.filters.raw_material_warehouse and d.remaining_qty != d.required_qty
+			):
 				row = self.get_args()
 				d.warehouse = self.filters.raw_material_warehouse
 				d.required_qty = d.remaining_qty
@@ -243,7 +283,8 @@
 
 	def pick_materials_from_warehouses(self, args, order_data, warehouses):
 		for index, warehouse in enumerate(warehouses):
-			if not args.remaining_qty: return
+			if not args.remaining_qty:
+				return
 
 			row = self.get_args()
 
@@ -255,14 +296,18 @@
 
 			args.allotted_qty = 0
 			if bin_data and bin_data.get("actual_qty") > 0:
-				args.allotted_qty = (bin_data.get("actual_qty")
-					if (args.required_qty > bin_data.get("actual_qty")) else args.required_qty)
+				args.allotted_qty = (
+					bin_data.get("actual_qty")
+					if (args.required_qty > bin_data.get("actual_qty"))
+					else args.required_qty
+				)
 
 				args.remaining_qty -= args.allotted_qty
 				bin_data["actual_qty"] -= args.allotted_qty
 
-			if ((self.mrp_warehouses and (args.allotted_qty or index == len(warehouses) - 1))
-				or not self.mrp_warehouses):
+			if (
+				self.mrp_warehouses and (args.allotted_qty or index == len(warehouses) - 1)
+			) or not self.mrp_warehouses:
 				if not self.index:
 					row.update(order_data)
 					self.index += 1
@@ -275,52 +320,45 @@
 				self.data.append(row)
 
 	def get_args(self):
-		return frappe._dict({
-			"work_order": "",
-			"sales_order": "",
-			"production_item": "",
-			"production_item_name": "",
-			"qty_to_manufacture": "",
-			"produced_qty": ""
-		})
+		return frappe._dict(
+			{
+				"work_order": "",
+				"sales_order": "",
+				"production_item": "",
+				"production_item_name": "",
+				"qty_to_manufacture": "",
+				"produced_qty": "",
+			}
+		)
 
 	def get_columns(self):
 		based_on = self.filters.based_on
 
-		self.columns = [{
-			"label": _("ID"),
-			"options": based_on,
-			"fieldname": "name",
-			"fieldtype": "Link",
-			"width": 100
-		}, {
-			"label": _("Item Code"),
-			"fieldname": "production_item",
-			"fieldtype": "Link",
-			"options": "Item",
-			"width": 120
-		}, {
-			"label": _("Item Name"),
-			"fieldname": "production_item_name",
-			"fieldtype": "Data",
-			"width": 130
-		}, {
-			"label": _("Warehouse"),
-			"options": "Warehouse",
-			"fieldname": "for_warehouse",
-			"fieldtype": "Link",
-			"width": 100
-		}, {
-			"label": _("Order Qty"),
-			"fieldname": "qty_to_manufacture",
-			"fieldtype": "Float",
-			"width": 80
-		}, {
-			"label": _("Available"),
-			"fieldname": "available_qty",
-			"fieldtype": "Float",
-			"width": 80
-		}]
+		self.columns = [
+			{"label": _("ID"), "options": based_on, "fieldname": "name", "fieldtype": "Link", "width": 100},
+			{
+				"label": _("Item Code"),
+				"fieldname": "production_item",
+				"fieldtype": "Link",
+				"options": "Item",
+				"width": 120,
+			},
+			{
+				"label": _("Item Name"),
+				"fieldname": "production_item_name",
+				"fieldtype": "Data",
+				"width": 130,
+			},
+			{
+				"label": _("Warehouse"),
+				"options": "Warehouse",
+				"fieldname": "for_warehouse",
+				"fieldtype": "Link",
+				"width": 100,
+			},
+			{"label": _("Order Qty"), "fieldname": "qty_to_manufacture", "fieldtype": "Float", "width": 80},
+			{"label": _("Available"), "fieldname": "available_qty", "fieldtype": "Float", "width": 80},
+		]
 
 		fieldname, fieldtype = "delivery_date", "Date"
 		if self.filters.based_on == "Sales Order" and self.filters.order_by == "Total Amount":
@@ -330,48 +368,50 @@
 		elif self.filters.based_on == "Work Order":
 			fieldname = "planned_start_date"
 
-		self.columns.append({
-			"label": _(self.filters.order_by),
-			"fieldname": fieldname,
-			"fieldtype": fieldtype,
-			"width": 100
-		})
+		self.columns.append(
+			{
+				"label": _(self.filters.order_by),
+				"fieldname": fieldname,
+				"fieldtype": fieldtype,
+				"width": 100,
+			}
+		)
 
-		self.columns.extend([{
-			"label": _("Raw Material Code"),
-			"fieldname": "item_code",
-			"fieldtype": "Link",
-			"options": "Item",
-			"width": 120
-		}, {
-			"label": _("Raw Material Name"),
-			"fieldname": "raw_material_name",
-			"fieldtype": "Data",
-			"width": 130
-		}, {
-			"label": _("Warehouse"),
-			"options": "Warehouse",
-			"fieldname": "warehouse",
-			"fieldtype": "Link",
-			"width": 110
-		}, {
-			"label": _("Required Qty"),
-			"fieldname": "required_qty",
-			"fieldtype": "Float",
-			"width": 100
-		}, {
-			"label": _("Allotted Qty"),
-			"fieldname": "allotted_qty",
-			"fieldtype": "Float",
-			"width": 100
-		}, {
-			"label": _("Expected Arrival Date"),
-			"fieldname": "arrival_date",
-			"fieldtype": "Date",
-			"width": 160
-		}, {
-			"label": _("Arrival Quantity"),
-			"fieldname": "arrival_qty",
-			"fieldtype": "Float",
-			"width": 140
-		}])
+		self.columns.extend(
+			[
+				{
+					"label": _("Raw Material Code"),
+					"fieldname": "item_code",
+					"fieldtype": "Link",
+					"options": "Item",
+					"width": 120,
+				},
+				{
+					"label": _("Raw Material Name"),
+					"fieldname": "raw_material_name",
+					"fieldtype": "Data",
+					"width": 130,
+				},
+				{
+					"label": _("Warehouse"),
+					"options": "Warehouse",
+					"fieldname": "warehouse",
+					"fieldtype": "Link",
+					"width": 110,
+				},
+				{"label": _("Required Qty"), "fieldname": "required_qty", "fieldtype": "Float", "width": 100},
+				{"label": _("Allotted Qty"), "fieldname": "allotted_qty", "fieldtype": "Float", "width": 100},
+				{
+					"label": _("Expected Arrival Date"),
+					"fieldname": "arrival_date",
+					"fieldtype": "Date",
+					"width": 160,
+				},
+				{
+					"label": _("Arrival Quantity"),
+					"fieldname": "arrival_qty",
+					"fieldtype": "Float",
+					"width": 140,
+				},
+			]
+		)
diff --git a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py
index a0c4a43..de96a6c 100644
--- a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py
+++ b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py
@@ -11,51 +11,58 @@
 	data = get_data(filters)
 	columns = get_columns(filters)
 	chart_data = get_chart_data(data, filters)
-	return columns, data , None, chart_data
+	return columns, data, None, chart_data
+
 
 def get_data(filters):
 	query_filters = {"docstatus": ("<", 2)}
 
-	fields = ["name", "status", "report_date", "item_code", "item_name", "sample_size",
-		"inspection_type", "reference_type", "reference_name", "inspected_by"]
+	fields = [
+		"name",
+		"status",
+		"report_date",
+		"item_code",
+		"item_name",
+		"sample_size",
+		"inspection_type",
+		"reference_type",
+		"reference_name",
+		"inspected_by",
+	]
 
 	for field in ["status", "item_code", "status", "inspected_by"]:
 		if filters.get(field):
 			query_filters[field] = ("in", filters.get(field))
 
-	query_filters["report_date"] = (">=", filters.get("from_date"))
-	query_filters["report_date"] = ("<=", filters.get("to_date"))
+	query_filters["report_date"] = ["between", [filters.get("from_date"), filters.get("to_date")]]
 
-	return frappe.get_all("Quality Inspection",
-		fields= fields, filters=query_filters, order_by="report_date asc")
+	return frappe.get_all(
+		"Quality Inspection", fields=fields, filters=query_filters, order_by="report_date asc"
+	)
+
 
 def get_chart_data(periodic_data, columns):
 	labels = ["Rejected", "Accepted"]
 
-	status_wise_data = {
-		"Accepted": 0,
-		"Rejected": 0
-	}
+	status_wise_data = {"Accepted": 0, "Rejected": 0}
 
 	datasets = []
 
 	for d in periodic_data:
 		status_wise_data[d.status] += 1
 
-	datasets.append({'name':'Qty Wise Chart',
-		'values': [status_wise_data.get("Rejected"), status_wise_data.get("Accepted")]})
+	datasets.append(
+		{
+			"name": "Qty Wise Chart",
+			"values": [status_wise_data.get("Rejected"), status_wise_data.get("Accepted")],
+		}
+	)
 
-	chart = {
-		"data": {
-			'labels': labels,
-			'datasets': datasets
-		},
-		"type": "donut",
-		"height": 300
-	}
+	chart = {"data": {"labels": labels, "datasets": datasets}, "type": "donut", "height": 300}
 
 	return chart
 
+
 def get_columns(filters):
 	columns = [
 		{
@@ -63,71 +70,49 @@
 			"fieldname": "name",
 			"fieldtype": "Link",
 			"options": "Work Order",
-			"width": 100
+			"width": 100,
 		},
-		{
-			"label": _("Report Date"),
-			"fieldname": "report_date",
-			"fieldtype": "Date",
-			"width": 150
-		}
+		{"label": _("Report Date"), "fieldname": "report_date", "fieldtype": "Date", "width": 150},
 	]
 
 	if not filters.get("status"):
 		columns.append(
-			{
-				"label": _("Status"),
-				"fieldname": "status",
-				"width": 100
-			},
+			{"label": _("Status"), "fieldname": "status", "width": 100},
 		)
 
-	columns.extend([
-		{
-			"label": _("Item Code"),
-			"fieldname": "item_code",
-			"fieldtype": "Link",
-			"options": "Item",
-			"width": 130
-		},
-		{
-			"label": _("Item Name"),
-			"fieldname": "item_name",
-			"fieldtype": "Data",
-			"width": 130
-		},
-		{
-			"label": _("Sample Size"),
-			"fieldname": "sample_size",
-			"fieldtype": "Float",
-			"width": 110
-		},
-		{
-			"label": _("Inspection Type"),
-			"fieldname": "inspection_type",
-			"fieldtype": "Data",
-			"width": 110
-		},
-		{
-			"label": _("Document Type"),
-			"fieldname": "reference_type",
-			"fieldtype": "Data",
-			"width": 90
-		},
-		{
-			"label": _("Document Name"),
-			"fieldname": "reference_name",
-			"fieldtype": "Dynamic Link",
-			"options": "reference_type",
-			"width": 150
-		},
-		{
-			"label": _("Inspected By"),
-			"fieldname": "inspected_by",
-			"fieldtype": "Link",
-			"options": "User",
-			"width": 150
-		}
-	])
+	columns.extend(
+		[
+			{
+				"label": _("Item Code"),
+				"fieldname": "item_code",
+				"fieldtype": "Link",
+				"options": "Item",
+				"width": 130,
+			},
+			{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 130},
+			{"label": _("Sample Size"), "fieldname": "sample_size", "fieldtype": "Float", "width": 110},
+			{
+				"label": _("Inspection Type"),
+				"fieldname": "inspection_type",
+				"fieldtype": "Data",
+				"width": 110,
+			},
+			{"label": _("Document Type"), "fieldname": "reference_type", "fieldtype": "Data", "width": 90},
+			{
+				"label": _("Document Name"),
+				"fieldname": "reference_name",
+				"fieldtype": "Dynamic Link",
+				"options": "reference_type",
+				"width": 150,
+			},
+			{
+				"label": _("Inspected By"),
+				"fieldname": "inspected_by",
+				"fieldtype": "Link",
+				"options": "User",
+				"width": 150,
+			},
+		]
+	)
 
 	return columns
diff --git a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py
index 0528348..8158bc9 100644
--- a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py
+++ b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py
@@ -12,12 +12,13 @@
 
 	return columns, data
 
+
 def get_data(report_filters):
 	fields = get_fields()
 	filters = get_filter_condition(report_filters)
 
 	wo_items = {}
-	for d in frappe.get_all("Work Order", filters = filters, fields=fields):
+	for d in frappe.get_all("Work Order", filters=filters, fields=fields):
 		d.extra_consumed_qty = 0.0
 		if d.consumed_qty and d.consumed_qty > d.required_qty:
 			d.extra_consumed_qty = d.consumed_qty - d.required_qty
@@ -29,7 +30,7 @@
 	for key, wo_data in wo_items.items():
 		for index, row in enumerate(wo_data):
 			if index != 0:
-				#If one work order has multiple raw materials then show parent data in the first row only
+				# If one work order has multiple raw materials then show parent data in the first row only
 				for field in ["name", "status", "production_item", "qty", "produced_qty"]:
 					row[field] = ""
 
@@ -37,17 +38,28 @@
 
 	return data
 
+
 def get_fields():
-	return ["`tabWork Order Item`.`parent`", "`tabWork Order Item`.`item_code` as raw_material_item_code",
-		"`tabWork Order Item`.`item_name` as raw_material_name", "`tabWork Order Item`.`required_qty`",
-		"`tabWork Order Item`.`transferred_qty`", "`tabWork Order Item`.`consumed_qty`", "`tabWork Order`.`status`",
-		"`tabWork Order`.`name`", "`tabWork Order`.`production_item`", "`tabWork Order`.`qty`",
-		"`tabWork Order`.`produced_qty`"]
+	return [
+		"`tabWork Order Item`.`parent`",
+		"`tabWork Order Item`.`item_code` as raw_material_item_code",
+		"`tabWork Order Item`.`item_name` as raw_material_name",
+		"`tabWork Order Item`.`required_qty`",
+		"`tabWork Order Item`.`transferred_qty`",
+		"`tabWork Order Item`.`consumed_qty`",
+		"`tabWork Order`.`status`",
+		"`tabWork Order`.`name`",
+		"`tabWork Order`.`production_item`",
+		"`tabWork Order`.`qty`",
+		"`tabWork Order`.`produced_qty`",
+	]
+
 
 def get_filter_condition(report_filters):
 	filters = {
-		"docstatus": 1, "status": ("in", ["In Process", "Completed", "Stopped"]),
-		"creation": ("between", [report_filters.from_date, report_filters.to_date])
+		"docstatus": 1,
+		"status": ("in", ["In Process", "Completed", "Stopped"]),
+		"creation": ("between", [report_filters.from_date, report_filters.to_date]),
 	}
 
 	for field in ["name", "production_item", "company", "status"]:
@@ -58,6 +70,7 @@
 
 	return filters
 
+
 def get_columns():
 	return [
 		{
@@ -65,67 +78,38 @@
 			"fieldname": "name",
 			"fieldtype": "Link",
 			"options": "Work Order",
-			"width": 80
+			"width": 80,
 		},
-		{
-			"label": _("Status"),
-			"fieldname": "status",
-			"fieldtype": "Data",
-			"width": 80
-		},
+		{"label": _("Status"), "fieldname": "status", "fieldtype": "Data", "width": 80},
 		{
 			"label": _("Production Item"),
 			"fieldname": "production_item",
 			"fieldtype": "Link",
 			"options": "Item",
-			"width": 130
+			"width": 130,
 		},
-		{
-			"label": _("Qty to Produce"),
-			"fieldname": "qty",
-			"fieldtype": "Float",
-			"width": 120
-		},
-		{
-			"label": _("Produced Qty"),
-			"fieldname": "produced_qty",
-			"fieldtype": "Float",
-			"width": 110
-		},
+		{"label": _("Qty to Produce"), "fieldname": "qty", "fieldtype": "Float", "width": 120},
+		{"label": _("Produced Qty"), "fieldname": "produced_qty", "fieldtype": "Float", "width": 110},
 		{
 			"label": _("Raw Material Item"),
 			"fieldname": "raw_material_item_code",
 			"fieldtype": "Link",
 			"options": "Item",
-			"width": 150
+			"width": 150,
 		},
-		{
-			"label": _("Item Name"),
-			"fieldname": "raw_material_name",
-			"width": 130
-		},
-		{
-			"label": _("Required Qty"),
-			"fieldname": "required_qty",
-			"fieldtype": "Float",
-			"width": 100
-		},
+		{"label": _("Item Name"), "fieldname": "raw_material_name", "width": 130},
+		{"label": _("Required Qty"), "fieldname": "required_qty", "fieldtype": "Float", "width": 100},
 		{
 			"label": _("Transferred Qty"),
 			"fieldname": "transferred_qty",
 			"fieldtype": "Float",
-			"width": 100
+			"width": 100,
 		},
-		{
-			"label": _("Consumed Qty"),
-			"fieldname": "consumed_qty",
-			"fieldtype": "Float",
-			"width": 100
-		},
+		{"label": _("Consumed Qty"), "fieldname": "consumed_qty", "fieldtype": "Float", "width": 100},
 		{
 			"label": _("Extra Consumed Qty"),
 			"fieldname": "extra_consumed_qty",
 			"fieldtype": "Float",
-			"width": 100
-		}
+			"width": 100,
+		},
 	]
diff --git a/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py b/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py
index db0b239..063ebba 100644
--- a/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py
+++ b/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py
@@ -3,6 +3,7 @@
 
 
 import frappe
+from frappe import _
 from frappe.utils import cint
 
 
@@ -12,17 +13,20 @@
 	columns = get_columns()
 	return columns, data
 
+
 def get_item_list(wo_list, filters):
 	out = []
 
-	#Add a row for each item/qty
+	# Add a row for each item/qty
 	for wo_details in wo_list:
 		desc = frappe.db.get_value("BOM", wo_details.bom_no, "description")
 
-		for wo_item_details in frappe.db.get_values("Work Order Item",
-			{"parent": wo_details.name}, ["item_code", "source_warehouse"], as_dict=1):
+		for wo_item_details in frappe.db.get_values(
+			"Work Order Item", {"parent": wo_details.name}, ["item_code", "source_warehouse"], as_dict=1
+		):
 
-			item_list = frappe.db.sql("""SELECT
+			item_list = frappe.db.sql(
+				"""SELECT
 					bom_item.item_code as item_code,
 					ifnull(ledger.actual_qty*bom.quantity/bom_item.stock_qty,0) as build_qty
 				FROM
@@ -36,8 +40,14 @@
 					and bom.name = %(bom)s
 				GROUP BY
 					bom_item.item_code""",
-			{"bom": wo_details.bom_no, "warehouse": wo_item_details.source_warehouse,
-				"filterhouse": filters.warehouse, "item_code": wo_item_details.item_code}, as_dict=1)
+				{
+					"bom": wo_details.bom_no,
+					"warehouse": wo_item_details.source_warehouse,
+					"filterhouse": filters.warehouse,
+					"item_code": wo_item_details.item_code,
+				},
+				as_dict=1,
+			)
 
 			stock_qty = 0
 			count = 0
@@ -54,97 +64,105 @@
 			else:
 				build = "N"
 
-			row = frappe._dict({
-				"work_order": wo_details.name,
-				"status": wo_details.status,
-				"req_items": cint(count),
-				"instock": stock_qty,
-				"description": desc,
-				"source_warehouse": wo_item_details.source_warehouse,
-				"item_code": wo_item_details.item_code,
-				"bom_no": wo_details.bom_no,
-				"qty": wo_details.qty,
-				"buildable_qty": buildable_qty,
-				"ready_to_build": build
-			})
+			row = frappe._dict(
+				{
+					"work_order": wo_details.name,
+					"status": wo_details.status,
+					"req_items": cint(count),
+					"instock": stock_qty,
+					"description": desc,
+					"source_warehouse": wo_item_details.source_warehouse,
+					"item_code": wo_item_details.item_code,
+					"bom_no": wo_details.bom_no,
+					"qty": wo_details.qty,
+					"buildable_qty": buildable_qty,
+					"ready_to_build": build,
+				}
+			)
 
 			out.append(row)
 
 	return out
 
+
 def get_work_orders():
-	out =  frappe.get_all("Work Order", filters={"docstatus": 1, "status": ( "!=","Completed")},
-		fields=["name","status", "bom_no", "qty", "produced_qty"], order_by='name')
+	out = frappe.get_all(
+		"Work Order",
+		filters={"docstatus": 1, "status": ("!=", "Completed")},
+		fields=["name", "status", "bom_no", "qty", "produced_qty"],
+		order_by="name",
+	)
 
 	return out
 
+
 def get_columns():
-	columns = [{
-		"fieldname": "work_order",
-		"label": "Work Order",
-		"fieldtype": "Link",
-		"options": "Work Order",
-		"width": 110
-	}, {
-		"fieldname": "bom_no",
-		"label": "BOM",
-		"fieldtype": "Link",
-		"options": "BOM",
-		"width": 120
-	}, {
-		"fieldname": "description",
-		"label": "Description",
-		"fieldtype": "Data",
-		"options": "",
-		"width": 230
-	}, {
-		"fieldname": "item_code",
-		"label": "Item Code",
-		"fieldtype": "Link",
-		"options": "Item",
-		"width": 110
-	},{
-		"fieldname": "source_warehouse",
-		"label": "Source Warehouse",
-		"fieldtype": "Link",
-		"options": "Warehouse",
-		"width": 110
-	},{
-		"fieldname": "qty",
-		"label": "Qty to Build",
-		"fieldtype": "Data",
-		"options": "",
-		"width": 110
-	}, {
-		"fieldname": "status",
-		"label": "Status",
-		"fieldtype": "Data",
-		"options": "",
-		"width": 100
-	}, {
-		"fieldname": "req_items",
-		"label": "# Req'd Items",
-		"fieldtype": "Data",
-		"options": "",
-		"width": 105
-	}, {
-		"fieldname": "instock",
-		"label": "# In Stock",
-		"fieldtype": "Data",
-		"options": "",
-		"width": 105
-	}, {
-		"fieldname": "buildable_qty",
-		"label": "Buildable Qty",
-		"fieldtype": "Data",
-		"options": "",
-		"width": 100
-	}, {
-		"fieldname": "ready_to_build",
-		"label": "Build All?",
-		"fieldtype": "Data",
-		"options": "",
-		"width": 90
-	}]
+	columns = [
+		{
+			"fieldname": "work_order",
+			"label": _("Work Order"),
+			"fieldtype": "Link",
+			"options": "Work Order",
+			"width": 110,
+		},
+		{"fieldname": "bom_no", "label": _("BOM"), "fieldtype": "Link", "options": "BOM", "width": 120},
+		{
+			"fieldname": "description",
+			"label": _("Description"),
+			"fieldtype": "Data",
+			"options": "",
+			"width": 230,
+		},
+		{
+			"fieldname": "item_code",
+			"label": _("Item Code"),
+			"fieldtype": "Link",
+			"options": "Item",
+			"width": 110,
+		},
+		{
+			"fieldname": "source_warehouse",
+			"label": _("Source Warehouse"),
+			"fieldtype": "Link",
+			"options": "Warehouse",
+			"width": 110,
+		},
+		{
+			"fieldname": "qty",
+			"label": _("Qty to Build"),
+			"fieldtype": "Data",
+			"options": "",
+			"width": 110,
+		},
+		{"fieldname": "status", "label": _("Status"), "fieldtype": "Data", "options": "", "width": 100},
+		{
+			"fieldname": "req_items",
+			"label": _("# Req'd Items"),
+			"fieldtype": "Data",
+			"options": "",
+			"width": 105,
+		},
+		{
+			"fieldname": "instock",
+			"label": _("# In Stock"),
+			"fieldtype": "Data",
+			"options": "",
+			"width": 105,
+		},
+		{
+			"fieldname": "buildable_qty",
+			"label": _("Buildable Qty"),
+			"fieldtype": "Data",
+			"options": "",
+			"width": 100,
+		},
+		{
+			"fieldname": "ready_to_build",
+			"label": _("Build All?"),
+			"fieldtype": "Data",
+			"options": "",
+			"width": 90,
+		},
+	]
 
 	return columns
diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py
index d7469dd..2368bfd 100644
--- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py
+++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py
@@ -21,11 +21,23 @@
 	chart_data = get_chart_data(data, filters)
 	return columns, data, None, chart_data
 
+
 def get_data(filters):
 	query_filters = {"docstatus": ("<", 2)}
 
-	fields = ["name", "status", "sales_order", "production_item", "qty", "produced_qty",
-		"planned_start_date", "planned_end_date", "actual_start_date", "actual_end_date", "lead_time"]
+	fields = [
+		"name",
+		"status",
+		"sales_order",
+		"production_item",
+		"qty",
+		"produced_qty",
+		"planned_start_date",
+		"planned_end_date",
+		"actual_start_date",
+		"actual_end_date",
+		"lead_time",
+	]
 
 	for field in ["sales_order", "production_item", "status", "company"]:
 		if filters.get(field):
@@ -34,15 +46,16 @@
 	query_filters["planned_start_date"] = (">=", filters.get("from_date"))
 	query_filters["planned_end_date"] = ("<=", filters.get("to_date"))
 
-	data = frappe.get_all("Work Order",
-		fields= fields, filters=query_filters, order_by="planned_start_date asc")
+	data = frappe.get_all(
+		"Work Order", fields=fields, filters=query_filters, order_by="planned_start_date asc"
+	)
 
 	res = []
 	for d in data:
 		start_date = d.actual_start_date or d.planned_start_date
 		d.age = 0
 
-		if d.status != 'Completed':
+		if d.status != "Completed":
 			d.age = date_diff(today(), start_date)
 
 		if filters.get("age") <= d.age:
@@ -50,6 +63,7 @@
 
 	return res
 
+
 def get_chart_data(data, filters):
 	if filters.get("charts_based_on") == "Status":
 		return get_chart_based_on_status(data)
@@ -58,6 +72,7 @@
 	else:
 		return get_chart_based_on_qty(data, filters)
 
+
 def get_chart_based_on_status(data):
 	labels = frappe.get_meta("Work Order").get_options("status").split("\n")
 	if "" in labels:
@@ -71,25 +86,18 @@
 	values = [status_wise_data[label] for label in labels]
 
 	chart = {
-		"data": {
-			'labels': labels,
-			'datasets': [{'name':'Qty Wise Chart', 'values': values}]
-		},
+		"data": {"labels": labels, "datasets": [{"name": "Qty Wise Chart", "values": values}]},
 		"type": "donut",
-		"height": 300
+		"height": 300,
 	}
 
 	return chart
 
+
 def get_chart_based_on_age(data):
 	labels = ["0-30 Days", "30-60 Days", "60-90 Days", "90 Above"]
 
-	age_wise_data = {
-		"0-30 Days": 0,
-		"30-60 Days": 0,
-		"60-90 Days": 0,
-		"90 Above": 0
-	}
+	age_wise_data = {"0-30 Days": 0, "30-60 Days": 0, "60-90 Days": 0, "90 Above": 0}
 
 	for d in data:
 		if d.age > 0 and d.age <= 30:
@@ -101,20 +109,22 @@
 		else:
 			age_wise_data["90 Above"] += 1
 
-	values = [age_wise_data["0-30 Days"], age_wise_data["30-60 Days"],
-		age_wise_data["60-90 Days"], age_wise_data["90 Above"]]
+	values = [
+		age_wise_data["0-30 Days"],
+		age_wise_data["30-60 Days"],
+		age_wise_data["60-90 Days"],
+		age_wise_data["90 Above"],
+	]
 
 	chart = {
-		"data": {
-			'labels': labels,
-			'datasets': [{'name':'Qty Wise Chart', 'values': values}]
-		},
+		"data": {"labels": labels, "datasets": [{"name": "Qty Wise Chart", "values": values}]},
 		"type": "donut",
-		"height": 300
+		"height": 300,
 	}
 
 	return chart
 
+
 def get_chart_based_on_qty(data, filters):
 	labels, periodic_data = prepare_chart_data(data, filters)
 
@@ -129,25 +139,18 @@
 	datasets.append({"name": "Completed", "values": completed})
 
 	chart = {
-		"data": {
-			'labels': labels,
-			'datasets': datasets
-		},
+		"data": {"labels": labels, "datasets": datasets},
 		"type": "bar",
-		"barOptions": {
-			"stacked": 1
-		}
+		"barOptions": {"stacked": 1},
 	}
 
 	return chart
 
+
 def prepare_chart_data(data, filters):
 	labels = []
 
-	periodic_data = {
-		"Pending": {},
-		"Completed": {}
-	}
+	periodic_data = {"Pending": {}, "Completed": {}}
 
 	filters.range = "Monthly"
 
@@ -165,11 +168,12 @@
 
 		for d in data:
 			if getdate(d.planned_start_date) >= from_date and getdate(d.planned_start_date) <= end_date:
-				periodic_data["Pending"][period] += (flt(d.qty) - flt(d.produced_qty))
+				periodic_data["Pending"][period] += flt(d.qty) - flt(d.produced_qty)
 				periodic_data["Completed"][period] += flt(d.produced_qty)
 
 	return labels, periodic_data
 
+
 def get_columns(filters):
 	columns = [
 		{
@@ -177,90 +181,77 @@
 			"fieldname": "name",
 			"fieldtype": "Link",
 			"options": "Work Order",
-			"width": 100
+			"width": 100,
 		},
 	]
 
 	if not filters.get("status"):
 		columns.append(
-			{
-				"label": _("Status"),
-				"fieldname": "status",
-				"width": 100
-			},
+			{"label": _("Status"), "fieldname": "status", "width": 100},
 		)
 
-	columns.extend([
-		{
-			"label": _("Production Item"),
-			"fieldname": "production_item",
-			"fieldtype": "Link",
-			"options": "Item",
-			"width": 130
-		},
-		{
-			"label": _("Produce Qty"),
-			"fieldname": "qty",
-			"fieldtype": "Float",
-			"width": 110
-		},
-		{
-			"label": _("Produced Qty"),
-			"fieldname": "produced_qty",
-			"fieldtype": "Float",
-			"width": 110
-		},
-		{
-			"label": _("Sales Order"),
-			"fieldname": "sales_order",
-			"fieldtype": "Link",
-			"options": "Sales Order",
-			"width": 90
-		},
-		{
-			"label": _("Planned Start Date"),
-			"fieldname": "planned_start_date",
-			"fieldtype": "Date",
-			"width": 150
-		},
-		{
-			"label": _("Planned End Date"),
-			"fieldname": "planned_end_date",
-			"fieldtype": "Date",
-			"width": 150
-		}
-	])
-
-	if filters.get("status") != 'Not Started':
-		columns.extend([
+	columns.extend(
+		[
 			{
-				"label": _("Actual Start Date"),
-				"fieldname": "actual_start_date",
+				"label": _("Production Item"),
+				"fieldname": "production_item",
+				"fieldtype": "Link",
+				"options": "Item",
+				"width": 130,
+			},
+			{"label": _("Produce Qty"), "fieldname": "qty", "fieldtype": "Float", "width": 110},
+			{"label": _("Produced Qty"), "fieldname": "produced_qty", "fieldtype": "Float", "width": 110},
+			{
+				"label": _("Sales Order"),
+				"fieldname": "sales_order",
+				"fieldtype": "Link",
+				"options": "Sales Order",
+				"width": 90,
+			},
+			{
+				"label": _("Planned Start Date"),
+				"fieldname": "planned_start_date",
 				"fieldtype": "Date",
-				"width": 100
+				"width": 150,
 			},
 			{
-				"label": _("Actual End Date"),
-				"fieldname": "actual_end_date",
+				"label": _("Planned End Date"),
+				"fieldname": "planned_end_date",
 				"fieldtype": "Date",
-				"width": 100
+				"width": 150,
 			},
-			{
-				"label": _("Age"),
-				"fieldname": "age",
-				"fieldtype": "Float",
-				"width": 110
-			},
-		])
+		]
+	)
 
-	if filters.get("status") == 'Completed':
-		columns.extend([
-			{
-				"label": _("Lead Time (in mins)"),
-				"fieldname": "lead_time",
-				"fieldtype": "Float",
-				"width": 110
-			},
-		])
+	if filters.get("status") != "Not Started":
+		columns.extend(
+			[
+				{
+					"label": _("Actual Start Date"),
+					"fieldname": "actual_start_date",
+					"fieldtype": "Date",
+					"width": 100,
+				},
+				{
+					"label": _("Actual End Date"),
+					"fieldname": "actual_end_date",
+					"fieldtype": "Date",
+					"width": 100,
+				},
+				{"label": _("Age"), "fieldname": "age", "fieldtype": "Float", "width": 110},
+			]
+		)
+
+	if filters.get("status") == "Completed":
+		columns.extend(
+			[
+				{
+					"label": _("Lead Time (in mins)"),
+					"fieldname": "lead_time",
+					"fieldtype": "Float",
+					"width": 110,
+				},
+			]
+		)
 
 	return columns
diff --git a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json
index 05ca2a8..549f5af 100644
--- a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json
+++ b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json
@@ -1,6 +1,6 @@
 {
  "charts": [],
- "content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Plan\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Forecasting\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order Summary\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Stock Report\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Planning Report\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Production\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Bill of Materials\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]",
+ "content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Plan\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Job Card\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Forecasting\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Stock Report\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Planning Report\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Production\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Bill of Materials\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]",
  "creation": "2020-03-02 17:11:37.032604",
  "docstatus": 0,
  "doctype": "Workspace",
@@ -402,51 +402,48 @@
    "type": "Link"
   }
  ],
- "modified": "2022-01-13 17:40:09.474747",
+ "modified": "2022-06-15 15:18:57.062935",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Manufacturing",
  "owner": "Administrator",
  "parent_page": "",
  "public": 1,
- "restrict_to_domain": "Manufacturing",
+ "quick_lists": [],
+ "restrict_to_domain": "",
  "roles": [],
  "sequence_id": 17.0,
  "shortcuts": [
   {
-   "color": "Green",
-   "format": "{} Active",
-   "label": "Item",
-   "link_to": "Item",
-   "restrict_to_domain": "Manufacturing",
-   "stats_filter": "{\n    \"disabled\": 0\n}",
-   "type": "DocType"
-  },
-  {
-   "color": "Green",
-   "format": "{} Active",
+   "color": "Grey",
+   "doc_view": "List",
    "label": "BOM",
    "link_to": "BOM",
-   "restrict_to_domain": "Manufacturing",
-   "stats_filter": "{\n    \"is_active\": 1\n}",
+   "stats_filter": "{\"is_active\":[\"=\",1]}",
    "type": "DocType"
   },
   {
-   "color": "Yellow",
-   "format": "{} Open",
-   "label": "Work Order",
-   "link_to": "Work Order",
-   "restrict_to_domain": "Manufacturing",
-   "stats_filter": "{ \n    \"status\": [\"in\", \n        [\"Draft\", \"Not Started\", \"In Process\"]\n    ]\n}",
-   "type": "DocType"
-  },
-  {
-   "color": "Yellow",
-   "format": "{} Open",
+   "color": "Grey",
+   "doc_view": "List",
    "label": "Production Plan",
    "link_to": "Production Plan",
-   "restrict_to_domain": "Manufacturing",
-   "stats_filter": "{ \n    \"status\": [\"not in\", [\"Completed\"]]\n}",
+   "stats_filter": "{\"status\":[\"not in\",[\"Closed\",\"Cancelled\",\"Completed\"]]}",
+   "type": "DocType"
+  },
+  {
+   "color": "Grey",
+   "doc_view": "List",
+   "label": "Work Order",
+   "link_to": "Work Order",
+   "stats_filter": "{\"status\":[\"not in\",[\"Closed\",\"Cancelled\",\"Completed\"]]}",
+   "type": "DocType"
+  },
+  {
+   "color": "Grey",
+   "doc_view": "List",
+   "label": "Job Card",
+   "link_to": "Job Card",
+   "stats_filter": "{\"status\":[\"not in\",[\"Cancelled\",\"Completed\",null]]}",
    "type": "DocType"
   },
   {
@@ -455,12 +452,6 @@
    "type": "Report"
   },
   {
-   "label": "Work Order Summary",
-   "link_to": "Work Order Summary",
-   "restrict_to_domain": "Manufacturing",
-   "type": "Report"
-  },
-  {
    "label": "BOM Stock Report",
    "link_to": "BOM Stock Report",
    "type": "Report"
@@ -469,12 +460,6 @@
    "label": "Production Planning Report",
    "link_to": "Production Planning Report",
    "type": "Report"
-  },
-  {
-   "label": "Dashboard",
-   "link_to": "Manufacturing",
-   "restrict_to_domain": "Manufacturing",
-   "type": "Dashboard"
   }
  ],
  "title": "Manufacturing"
diff --git a/erpnext/modules.txt b/erpnext/modules.txt
index c6b3159..869166b 100644
--- a/erpnext/modules.txt
+++ b/erpnext/modules.txt
@@ -12,7 +12,6 @@
 Assets
 Portal
 Maintenance
-Education
 Regional
 ERPNext Integrations
 Quality Management
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index e657e3b..aab33aa 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -1,9 +1,10 @@
 [pre_model_sync]
 erpnext.patches.v12_0.update_is_cancelled_field
-erpnext.patches.v13_0.add_bin_unique_constraint
 erpnext.patches.v11_0.rename_production_order_to_work_order
+erpnext.patches.v13_0.add_bin_unique_constraint
 erpnext.patches.v11_0.refactor_naming_series
 erpnext.patches.v11_0.refactor_autoname_naming
+erpnext.patches.v14_0.change_is_subcontracted_fieldtype
 execute:frappe.reload_doc("accounts", "doctype", "POS Payment Method") #2020-05-28
 execute:frappe.reload_doc("HR", "doctype", "HR Settings") #2020-01-16 #2020-07-24
 erpnext.patches.v4_2.update_requested_and_ordered_qty #2021-03-31
@@ -217,7 +218,6 @@
 erpnext.patches.v13_0.update_reason_for_resignation_in_employee
 execute:frappe.delete_doc("Report", "Quoted Item Comparison")
 erpnext.patches.v13_0.update_member_email_address
-erpnext.patches.v13_0.create_leave_policy_assignment_based_on_employee_current_leave_policy
 erpnext.patches.v13_0.update_pos_closing_entry_in_merge_log
 erpnext.patches.v13_0.add_po_to_global_search
 erpnext.patches.v13_0.update_returned_qty_in_pr_dn
@@ -244,8 +244,6 @@
 erpnext.patches.v13_0.make_non_standard_user_type #13-04-2021 #17-01-2022
 erpnext.patches.v13_0.update_shipment_status
 erpnext.patches.v13_0.remove_attribute_field_from_item_variant_setting
-erpnext.patches.v13_0.germany_make_custom_fields
-erpnext.patches.v13_0.germany_fill_debtor_creditor_number
 erpnext.patches.v13_0.set_pos_closing_as_failed
 erpnext.patches.v13_0.rename_stop_to_send_birthday_reminders
 execute:frappe.rename_doc("Workspace", "Loan Management", "Loans", force=True)
@@ -294,7 +292,6 @@
 erpnext.patches.v12_0.update_production_plan_status
 erpnext.patches.v13_0.healthcare_deprecation_warning
 erpnext.patches.v13_0.item_naming_series_not_mandatory
-erpnext.patches.v14_0.delete_healthcare_doctypes
 erpnext.patches.v13_0.update_category_in_ltds_certificate
 erpnext.patches.v13_0.fetch_thumbnail_in_website_items
 erpnext.patches.v13_0.update_maintenance_schedule_field_in_visit
@@ -303,7 +300,6 @@
 erpnext.patches.v13_0.rename_ksa_qr_field
 erpnext.patches.v13_0.wipe_serial_no_field_for_0_qty
 erpnext.patches.v13_0.disable_ksa_print_format_for_others # 16-12-2021
-execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings')
 erpnext.patches.v14_0.set_payroll_cost_centers
 erpnext.patches.v13_0.agriculture_deprecation_warning
 erpnext.patches.v13_0.hospitality_deprecation_warning
@@ -311,14 +307,19 @@
 erpnext.patches.v13_0.delete_bank_reconciliation_detail
 erpnext.patches.v13_0.enable_provisional_accounting
 erpnext.patches.v13_0.non_profit_deprecation_warning
+erpnext.patches.v13_0.enable_ksa_vat_docs #1
 
 [post_model_sync]
+execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings')
 erpnext.patches.v14_0.rename_ongoing_status_in_sla_documents
 erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template
 erpnext.patches.v14_0.delete_shopify_doctypes
+erpnext.patches.v14_0.delete_healthcare_doctypes
 erpnext.patches.v14_0.delete_hub_doctypes
 erpnext.patches.v14_0.delete_hospitality_doctypes # 20-01-2022
-erpnext.patches.v14_0.delete_agriculture_doctypes
+erpnext.patches.v14_0.delete_agriculture_doctypes # 15-06-2022
+erpnext.patches.v14_0.delete_education_doctypes
+erpnext.patches.v14_0.delete_datev_doctypes
 erpnext.patches.v14_0.rearrange_company_fields
 erpnext.patches.v14_0.update_leave_notification_template
 erpnext.patches.v13_0.update_sane_transfer_against
@@ -333,4 +334,19 @@
 erpnext.patches.v13_0.update_accounts_in_loan_docs
 erpnext.patches.v14_0.update_batch_valuation_flag
 erpnext.patches.v14_0.delete_non_profit_doctypes
-erpnext.patches.v14_0.update_employee_advance_status
+erpnext.patches.v13_0.update_employee_advance_status
+erpnext.patches.v13_0.add_cost_center_in_loans
+erpnext.patches.v13_0.set_return_against_in_pos_invoice_references
+erpnext.patches.v13_0.remove_unknown_links_to_prod_plan_items # 24-03-2022
+erpnext.patches.v13_0.update_expense_claim_status_for_paid_advances
+erpnext.patches.v13_0.copy_custom_field_filters_to_website_item
+erpnext.patches.v13_0.change_default_item_manufacturer_fieldtype
+erpnext.patches.v13_0.requeue_recoverable_reposts
+erpnext.patches.v14_0.discount_accounting_separation
+erpnext.patches.v14_0.delete_employee_transfer_property_doctype
+erpnext.patches.v13_0.create_accounting_dimensions_in_orders
+erpnext.patches.v13_0.set_per_billed_in_return_delivery_note
+execute:frappe.delete_doc("DocType", "Naming Series")
+erpnext.patches.v13_0.set_payroll_entry_status
+erpnext.patches.v13_0.job_card_status_on_hold
+erpnext.patches.v14_0.migrate_gl_to_payment_ledger
diff --git a/erpnext/patches/v10_0/add_default_cash_flow_mappers.py b/erpnext/patches/v10_0/add_default_cash_flow_mappers.py
index 165ca02..5493258 100644
--- a/erpnext/patches/v10_0/add_default_cash_flow_mappers.py
+++ b/erpnext/patches/v10_0/add_default_cash_flow_mappers.py
@@ -8,8 +8,8 @@
 
 
 def execute():
-    frappe.reload_doc('accounts', 'doctype', frappe.scrub('Cash Flow Mapping'))
-    frappe.reload_doc('accounts', 'doctype', frappe.scrub('Cash Flow Mapper'))
-    frappe.reload_doc('accounts', 'doctype', frappe.scrub('Cash Flow Mapping Template Details'))
+	frappe.reload_doc("accounts", "doctype", frappe.scrub("Cash Flow Mapping"))
+	frappe.reload_doc("accounts", "doctype", frappe.scrub("Cash Flow Mapper"))
+	frappe.reload_doc("accounts", "doctype", frappe.scrub("Cash Flow Mapping Template Details"))
 
-    create_default_cash_flow_mapper_templates()
+	create_default_cash_flow_mapper_templates()
diff --git a/erpnext/patches/v10_0/fichier_des_ecritures_comptables_for_france.py b/erpnext/patches/v10_0/fichier_des_ecritures_comptables_for_france.py
index cdf5ba2..4449729 100644
--- a/erpnext/patches/v10_0/fichier_des_ecritures_comptables_for_france.py
+++ b/erpnext/patches/v10_0/fichier_des_ecritures_comptables_for_france.py
@@ -8,6 +8,6 @@
 
 
 def execute():
-	frappe.reload_doc('regional', 'report', 'fichier_des_ecritures_comptables_[fec]')
-	for d in frappe.get_all('Company', filters = {'country': 'France'}):
+	frappe.reload_doc("regional", "report", "fichier_des_ecritures_comptables_[fec]")
+	for d in frappe.get_all("Company", filters={"country": "France"}):
 		install_country_fixtures(d.name)
diff --git a/erpnext/patches/v10_0/item_barcode_childtable_migrate.py b/erpnext/patches/v10_0/item_barcode_childtable_migrate.py
index ffff95d..e2d0943 100644
--- a/erpnext/patches/v10_0/item_barcode_childtable_migrate.py
+++ b/erpnext/patches/v10_0/item_barcode_childtable_migrate.py
@@ -7,26 +7,30 @@
 
 def execute():
 	frappe.reload_doc("stock", "doctype", "item_barcode")
-	if frappe.get_all("Item Barcode", limit=1): return
-	if "barcode" not in frappe.db.get_table_columns("Item"): return
+	if frappe.get_all("Item Barcode", limit=1):
+		return
+	if "barcode" not in frappe.db.get_table_columns("Item"):
+		return
 
-	items_barcode = frappe.db.sql("select name, barcode from tabItem where barcode is not null", as_dict=True)
+	items_barcode = frappe.db.sql(
+		"select name, barcode from tabItem where barcode is not null", as_dict=True
+	)
 	frappe.reload_doc("stock", "doctype", "item")
 
-
-
 	for item in items_barcode:
 		barcode = item.barcode.strip()
 
-		if barcode and '<' not in barcode:
+		if barcode and "<" not in barcode:
 			try:
-				frappe.get_doc({
-					'idx': 0,
-					'doctype': 'Item Barcode',
-					'barcode': barcode,
-					'parenttype': 'Item',
-					'parent': item.name,
-					'parentfield': 'barcodes'
-				}).insert()
+				frappe.get_doc(
+					{
+						"idx": 0,
+						"doctype": "Item Barcode",
+						"barcode": barcode,
+						"parenttype": "Item",
+						"parent": item.name,
+						"parentfield": "barcodes",
+					}
+				).insert()
 			except (frappe.DuplicateEntryError, frappe.UniqueValidationError):
 				continue
diff --git a/erpnext/patches/v10_0/migrate_daily_work_summary_settings_to_daily_work_summary_group.py b/erpnext/patches/v10_0/migrate_daily_work_summary_settings_to_daily_work_summary_group.py
index fd51184..2cbbe05 100644
--- a/erpnext/patches/v10_0/migrate_daily_work_summary_settings_to_daily_work_summary_group.py
+++ b/erpnext/patches/v10_0/migrate_daily_work_summary_settings_to_daily_work_summary_group.py
@@ -6,13 +6,13 @@
 
 
 def execute():
-	if not frappe.db.table_exists('Daily Work Summary Group'):
+	if not frappe.db.table_exists("Daily Work Summary Group"):
 		frappe.reload_doc("hr", "doctype", "daily_work_summary_group")
 		frappe.reload_doc("hr", "doctype", "daily_work_summary_group_user")
 
 		# check if Daily Work Summary Settings Company table exists
 		try:
-			frappe.db.sql('DESC `tabDaily Work Summary Settings Company`')
+			frappe.db.sql("DESC `tabDaily Work Summary Settings Company`")
 		except Exception:
 			return
 
@@ -20,19 +20,24 @@
 		previous_setting = get_previous_setting()
 		if previous_setting["companies"]:
 			for d in previous_setting["companies"]:
-				users = frappe.get_list("Employee", dict(
-					company=d.company, user_id=("!=", " ")), "user_id as user")
-				if(len(users)):
+				users = frappe.get_list(
+					"Employee", dict(company=d.company, user_id=("!=", " ")), "user_id as user"
+				)
+				if len(users):
 					# create new group entry for each company entry
-					new_group = frappe.get_doc(dict(doctype="Daily Work Summary Group",
-						name="Daily Work Summary for " + d.company,
-						users=users,
-						send_emails_at=d.send_emails_at,
-						subject=previous_setting["subject"],
-						message=previous_setting["message"]))
+					new_group = frappe.get_doc(
+						dict(
+							doctype="Daily Work Summary Group",
+							name="Daily Work Summary for " + d.company,
+							users=users,
+							send_emails_at=d.send_emails_at,
+							subject=previous_setting["subject"],
+							message=previous_setting["message"],
+						)
+					)
 					new_group.flags.ignore_permissions = True
 					new_group.flags.ignore_validate = True
-					new_group.insert(ignore_if_duplicate = True)
+					new_group.insert(ignore_if_duplicate=True)
 
 	frappe.delete_doc("DocType", "Daily Work Summary Settings")
 	frappe.delete_doc("DocType", "Daily Work Summary Settings Company")
@@ -41,11 +46,13 @@
 def get_previous_setting():
 	obj = {}
 	setting_data = frappe.db.sql(
-		"select field, value from tabSingles where doctype='Daily Work Summary Settings'")
+		"select field, value from tabSingles where doctype='Daily Work Summary Settings'"
+	)
 	for field, value in setting_data:
 		obj[field] = value
 	obj["companies"] = get_setting_companies()
 	return obj
 
+
 def get_setting_companies():
 	return frappe.db.sql("select * from `tabDaily Work Summary Settings Company`", as_dict=True)
diff --git a/erpnext/patches/v10_0/rename_price_to_rate_in_pricing_rule.py b/erpnext/patches/v10_0/rename_price_to_rate_in_pricing_rule.py
index 525d1ff..1d5518f 100644
--- a/erpnext/patches/v10_0/rename_price_to_rate_in_pricing_rule.py
+++ b/erpnext/patches/v10_0/rename_price_to_rate_in_pricing_rule.py
@@ -10,5 +10,5 @@
 		rename_field("Pricing Rule", "price", "rate")
 
 	except Exception as e:
-		if e.args[0]!=1054:
+		if e.args[0] != 1054:
 			raise
diff --git a/erpnext/patches/v10_0/set_currency_in_pricing_rule.py b/erpnext/patches/v10_0/set_currency_in_pricing_rule.py
index 3f3d424..d68148e 100644
--- a/erpnext/patches/v10_0/set_currency_in_pricing_rule.py
+++ b/erpnext/patches/v10_0/set_currency_in_pricing_rule.py
@@ -5,8 +5,10 @@
 	frappe.reload_doctype("Pricing Rule")
 
 	currency = frappe.db.get_default("currency")
-	for doc in frappe.get_all('Pricing Rule', fields = ["company", "name"]):
+	for doc in frappe.get_all("Pricing Rule", fields=["company", "name"]):
 		if doc.company:
-			currency = frappe.get_cached_value('Company',  doc.company,  "default_currency")
+			currency = frappe.get_cached_value("Company", doc.company, "default_currency")
 
-		frappe.db.sql("""update `tabPricing Rule` set currency = %s where name = %s""",(currency, doc.name))
+		frappe.db.sql(
+			"""update `tabPricing Rule` set currency = %s where name = %s""", (currency, doc.name)
+		)
diff --git a/erpnext/patches/v10_0/update_translatable_fields.py b/erpnext/patches/v10_0/update_translatable_fields.py
index 471f537..61b4ba5 100644
--- a/erpnext/patches/v10_0/update_translatable_fields.py
+++ b/erpnext/patches/v10_0/update_translatable_fields.py
@@ -2,37 +2,38 @@
 
 
 def execute():
-	'''
+	"""
 	Enable translatable in these fields
 	- Customer Name
 	- Supplier Name
 	- Contact Name
 	- Item Name/ Description
 	- Address
-	'''
+	"""
 
-	frappe.reload_doc('core', 'doctype', 'docfield')
-	frappe.reload_doc('custom', 'doctype', 'custom_field')
+	frappe.reload_doc("core", "doctype", "docfield")
+	frappe.reload_doc("custom", "doctype", "custom_field")
 
 	enable_for_fields = [
-		['Customer', 'customer_name'],
-		['Supplier', 'supplier_name'],
-		['Contact', 'first_name'],
-		['Contact', 'last_name'],
-		['Item', 'item_name'],
-		['Item', 'description'],
-		['Address', 'address_line1'],
-		['Address', 'address_line2'],
+		["Customer", "customer_name"],
+		["Supplier", "supplier_name"],
+		["Contact", "first_name"],
+		["Contact", "last_name"],
+		["Item", "item_name"],
+		["Item", "description"],
+		["Address", "address_line1"],
+		["Address", "address_line2"],
 	]
 
-
 	for f in enable_for_fields:
-		frappe.get_doc({
-			'doctype': 'Property Setter',
-			'doc_type': f[0],
-			'doctype_or_field': 'DocField',
-			'field_name': f[1],
-			'property': 'translatable',
-			'propery_type': 'Check',
-			'value': 1
-		}).db_insert()
+		frappe.get_doc(
+			{
+				"doctype": "Property Setter",
+				"doc_type": f[0],
+				"doctype_or_field": "DocField",
+				"field_name": f[1],
+				"property": "translatable",
+				"propery_type": "Check",
+				"value": 1,
+			}
+		).db_insert()
diff --git a/erpnext/patches/v10_1/transfer_subscription_to_auto_repeat.py b/erpnext/patches/v10_1/transfer_subscription_to_auto_repeat.py
index 6530b81..87151c1 100644
--- a/erpnext/patches/v10_1/transfer_subscription_to_auto_repeat.py
+++ b/erpnext/patches/v10_1/transfer_subscription_to_auto_repeat.py
@@ -3,41 +3,57 @@
 
 
 def execute():
-	frappe.reload_doc('automation', 'doctype', 'auto_repeat')
+	frappe.reload_doc("automation", "doctype", "auto_repeat")
 
 	doctypes_to_rename = {
-		'accounts': ['Journal Entry', 'Payment Entry', 'Purchase Invoice', 'Sales Invoice'],
-		'buying': ['Purchase Order', 'Supplier Quotation'],
-		'selling': ['Quotation', 'Sales Order'],
-		'stock': ['Delivery Note', 'Purchase Receipt']
+		"accounts": ["Journal Entry", "Payment Entry", "Purchase Invoice", "Sales Invoice"],
+		"buying": ["Purchase Order", "Supplier Quotation"],
+		"selling": ["Quotation", "Sales Order"],
+		"stock": ["Delivery Note", "Purchase Receipt"],
 	}
 
 	for module, doctypes in doctypes_to_rename.items():
 		for doctype in doctypes:
-			frappe.reload_doc(module, 'doctype', frappe.scrub(doctype))
+			frappe.reload_doc(module, "doctype", frappe.scrub(doctype))
 
-			if frappe.db.has_column(doctype, 'subscription'):
-				rename_field(doctype, 'subscription', 'auto_repeat')
+			if frappe.db.has_column(doctype, "subscription"):
+				rename_field(doctype, "subscription", "auto_repeat")
 
-	subscriptions = frappe.db.sql('select * from `tabSubscription`', as_dict=1)
+	subscriptions = frappe.db.sql("select * from `tabSubscription`", as_dict=1)
 
 	for doc in subscriptions:
-		doc['doctype'] = 'Auto Repeat'
+		doc["doctype"] = "Auto Repeat"
 		auto_repeat = frappe.get_doc(doc)
 		auto_repeat.db_insert()
 
-	frappe.db.sql('delete from `tabSubscription`')
+	frappe.db.sql("delete from `tabSubscription`")
 	frappe.db.commit()
 	drop_columns_from_subscription()
 
+
 def drop_columns_from_subscription():
-	fields_to_drop = {'Subscription': []}
-	for field in ['naming_series', 'reference_doctype', 'reference_document', 'start_date',
-		'end_date', 'submit_on_creation', 'disabled', 'frequency', 'repeat_on_day',
-		'next_schedule_date', 'notify_by_email', 'subject', 'recipients', 'print_format',
-		'message', 'status', 'amended_from']:
+	fields_to_drop = {"Subscription": []}
+	for field in [
+		"naming_series",
+		"reference_doctype",
+		"reference_document",
+		"start_date",
+		"end_date",
+		"submit_on_creation",
+		"disabled",
+		"frequency",
+		"repeat_on_day",
+		"next_schedule_date",
+		"notify_by_email",
+		"subject",
+		"recipients",
+		"print_format",
+		"message",
+		"status",
+		"amended_from",
+	]:
 
 		if field in frappe.db.get_table_columns("Subscription"):
-			fields_to_drop['Subscription'].append(field)
+			fields_to_drop["Subscription"].append(field)
 
 	frappe.model.delete_fields(fields_to_drop, delete=1)
diff --git a/erpnext/patches/v11_0/add_default_dispatch_notification_template.py b/erpnext/patches/v11_0/add_default_dispatch_notification_template.py
index c7771a5..48ca9b9 100644
--- a/erpnext/patches/v11_0/add_default_dispatch_notification_template.py
+++ b/erpnext/patches/v11_0/add_default_dispatch_notification_template.py
@@ -10,15 +10,19 @@
 
 	if not frappe.db.exists("Email Template", _("Dispatch Notification")):
 		base_path = frappe.get_app_path("erpnext", "stock", "doctype")
-		response = frappe.read_file(os.path.join(base_path, "delivery_trip/dispatch_notification_template.html"))
+		response = frappe.read_file(
+			os.path.join(base_path, "delivery_trip/dispatch_notification_template.html")
+		)
 
-		frappe.get_doc({
-			"doctype": "Email Template",
-			"name": _("Dispatch Notification"),
-			"response": response,
-			"subject": _("Your order is out for delivery!"),
-			"owner": frappe.session.user,
-		}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "Email Template",
+				"name": _("Dispatch Notification"),
+				"response": response,
+				"subject": _("Your order is out for delivery!"),
+				"owner": frappe.session.user,
+			}
+		).insert(ignore_permissions=True)
 
 	delivery_settings = frappe.get_doc("Delivery Settings")
 	delivery_settings.dispatch_template = _("Dispatch Notification")
diff --git a/erpnext/patches/v11_0/add_default_email_template_for_leave.py b/erpnext/patches/v11_0/add_default_email_template_for_leave.py
index fdf3046..1fddc7f 100644
--- a/erpnext/patches/v11_0/add_default_email_template_for_leave.py
+++ b/erpnext/patches/v11_0/add_default_email_template_for_leave.py
@@ -7,25 +7,32 @@
 def execute():
 	frappe.reload_doc("email", "doctype", "email_template")
 
-	if not frappe.db.exists("Email Template", _('Leave Approval Notification')):
+	if not frappe.db.exists("Email Template", _("Leave Approval Notification")):
 		base_path = frappe.get_app_path("erpnext", "hr", "doctype")
-		response = frappe.read_file(os.path.join(base_path, "leave_application/leave_application_email_template.html"))
-		frappe.get_doc({
-			'doctype': 'Email Template',
-			'name': _("Leave Approval Notification"),
-			'response': response,
-			'subject': _("Leave Approval Notification"),
-			'owner': frappe.session.user,
-		}).insert(ignore_permissions=True)
+		response = frappe.read_file(
+			os.path.join(base_path, "leave_application/leave_application_email_template.html")
+		)
+		frappe.get_doc(
+			{
+				"doctype": "Email Template",
+				"name": _("Leave Approval Notification"),
+				"response": response,
+				"subject": _("Leave Approval Notification"),
+				"owner": frappe.session.user,
+			}
+		).insert(ignore_permissions=True)
 
-
-	if not frappe.db.exists("Email Template", _('Leave Status Notification')):
+	if not frappe.db.exists("Email Template", _("Leave Status Notification")):
 		base_path = frappe.get_app_path("erpnext", "hr", "doctype")
-		response = frappe.read_file(os.path.join(base_path, "leave_application/leave_application_email_template.html"))
-		frappe.get_doc({
-			'doctype': 'Email Template',
-			'name': _("Leave Status Notification"),
-			'response': response,
-			'subject': _("Leave Status Notification"),
-			'owner': frappe.session.user,
-		}).insert(ignore_permissions=True)
+		response = frappe.read_file(
+			os.path.join(base_path, "leave_application/leave_application_email_template.html")
+		)
+		frappe.get_doc(
+			{
+				"doctype": "Email Template",
+				"name": _("Leave Status Notification"),
+				"response": response,
+				"subject": _("Leave Status Notification"),
+				"owner": frappe.session.user,
+			}
+		).insert(ignore_permissions=True)
diff --git a/erpnext/patches/v11_0/add_expense_claim_default_account.py b/erpnext/patches/v11_0/add_expense_claim_default_account.py
index f5658c5..ff39350 100644
--- a/erpnext/patches/v11_0/add_expense_claim_default_account.py
+++ b/erpnext/patches/v11_0/add_expense_claim_default_account.py
@@ -8,4 +8,9 @@
 
 	for company in companies:
 		if company.default_payable_account is not None:
-			frappe.db.set_value("Company", company.name, "default_expense_claim_payable_account", company.default_payable_account)
+			frappe.db.set_value(
+				"Company",
+				company.name,
+				"default_expense_claim_payable_account",
+				company.default_payable_account,
+			)
diff --git a/erpnext/patches/v11_0/add_index_on_nestedset_doctypes.py b/erpnext/patches/v11_0/add_index_on_nestedset_doctypes.py
index 7c99f58..f354616 100644
--- a/erpnext/patches/v11_0/add_index_on_nestedset_doctypes.py
+++ b/erpnext/patches/v11_0/add_index_on_nestedset_doctypes.py
@@ -7,6 +7,16 @@
 
 def execute():
 	frappe.reload_doc("assets", "doctype", "Location")
-	for dt in ("Account", "Cost Center", "File", "Employee", "Location", "Task", "Customer Group", "Sales Person", "Territory"):
+	for dt in (
+		"Account",
+		"Cost Center",
+		"File",
+		"Employee",
+		"Location",
+		"Task",
+		"Customer Group",
+		"Sales Person",
+		"Territory",
+	):
 		frappe.reload_doctype(dt)
 		frappe.get_doc("DocType", dt).run_module_method("on_doctype_update")
diff --git a/erpnext/patches/v11_0/add_item_group_defaults.py b/erpnext/patches/v11_0/add_item_group_defaults.py
index 026047a..4e6505a 100644
--- a/erpnext/patches/v11_0/add_item_group_defaults.py
+++ b/erpnext/patches/v11_0/add_item_group_defaults.py
@@ -6,31 +6,36 @@
 
 
 def execute():
-	'''
+	"""
 
 	Fields to move from item group to item defaults child table
 	[ default_cost_center, default_expense_account, default_income_account ]
 
-	'''
+	"""
 
-	frappe.reload_doc('stock', 'doctype', 'item_default')
-	frappe.reload_doc('setup', 'doctype', 'item_group')
+	frappe.reload_doc("stock", "doctype", "item_default")
+	frappe.reload_doc("setup", "doctype", "item_group")
 
 	companies = frappe.get_all("Company")
-	item_groups = frappe.db.sql("""select name, default_income_account, default_expense_account,\
-		default_cost_center from `tabItem Group`""", as_dict=True)
+	item_groups = frappe.db.sql(
+		"""select name, default_income_account, default_expense_account,\
+		default_cost_center from `tabItem Group`""",
+		as_dict=True,
+	)
 
 	if len(companies) == 1:
 		for item_group in item_groups:
 			doc = frappe.get_doc("Item Group", item_group.get("name"))
 			item_group_defaults = []
-			item_group_defaults.append({
-				"company": companies[0].name,
-				"income_account": item_group.get("default_income_account"),
-				"expense_account": item_group.get("default_expense_account"),
-				"buying_cost_center": item_group.get("default_cost_center"),
-				"selling_cost_center": item_group.get("default_cost_center")
-			})
+			item_group_defaults.append(
+				{
+					"company": companies[0].name,
+					"income_account": item_group.get("default_income_account"),
+					"expense_account": item_group.get("default_expense_account"),
+					"buying_cost_center": item_group.get("default_cost_center"),
+					"selling_cost_center": item_group.get("default_cost_center"),
+				}
+			)
 			doc.extend("item_group_defaults", item_group_defaults)
 			for child_doc in doc.item_group_defaults:
 				child_doc.db_insert()
@@ -38,10 +43,11 @@
 		item_group_dict = {
 			"default_expense_account": ["expense_account"],
 			"default_income_account": ["income_account"],
-			"default_cost_center": ["buying_cost_center", "selling_cost_center"]
+			"default_cost_center": ["buying_cost_center", "selling_cost_center"],
 		}
 		for item_group in item_groups:
 			item_group_defaults = []
+
 			def insert_into_item_defaults(doc_field_name, doc_field_value, company):
 				for d in item_group_defaults:
 					if d.get("company") == company:
@@ -50,18 +56,16 @@
 							d[doc_field_name[1]] = doc_field_value
 						return
 
-				item_group_defaults.append({
-					"company": company,
-					doc_field_name[0]: doc_field_value
-				})
+				item_group_defaults.append({"company": company, doc_field_name[0]: doc_field_value})
 
-				if(len(doc_field_name) > 1):
-					item_group_defaults[len(item_group_defaults)-1][doc_field_name[1]] = doc_field_value
+				if len(doc_field_name) > 1:
+					item_group_defaults[len(item_group_defaults) - 1][doc_field_name[1]] = doc_field_value
 
 			for d in [
-					["default_expense_account", "Account"], ["default_income_account", "Account"],
-					["default_cost_center", "Cost Center"]
-				]:
+				["default_expense_account", "Account"],
+				["default_income_account", "Account"],
+				["default_cost_center", "Cost Center"],
+			]:
 				if item_group.get(d[0]):
 					company = frappe.get_value(d[1], item_group.get(d[0]), "company", cache=True)
 					doc_field_name = item_group_dict.get(d[0])
diff --git a/erpnext/patches/v11_0/add_market_segments.py b/erpnext/patches/v11_0/add_market_segments.py
index 6dcbf99..d1111c2 100644
--- a/erpnext/patches/v11_0/add_market_segments.py
+++ b/erpnext/patches/v11_0/add_market_segments.py
@@ -4,8 +4,8 @@
 
 
 def execute():
-	frappe.reload_doc('crm', 'doctype', 'market_segment')
+	frappe.reload_doc("crm", "doctype", "market_segment")
 
-	frappe.local.lang = frappe.db.get_default("lang") or 'en'
+	frappe.local.lang = frappe.db.get_default("lang") or "en"
 
 	add_market_segments()
diff --git a/erpnext/patches/v11_0/add_sales_stages.py b/erpnext/patches/v11_0/add_sales_stages.py
index 064b721..0dac1e1 100644
--- a/erpnext/patches/v11_0/add_sales_stages.py
+++ b/erpnext/patches/v11_0/add_sales_stages.py
@@ -4,8 +4,8 @@
 
 
 def execute():
-	frappe.reload_doc('crm', 'doctype', 'sales_stage')
+	frappe.reload_doc("crm", "doctype", "sales_stage")
 
-	frappe.local.lang = frappe.db.get_default("lang") or 'en'
+	frappe.local.lang = frappe.db.get_default("lang") or "en"
 
 	add_sale_stages()
diff --git a/erpnext/patches/v11_0/check_buying_selling_in_currency_exchange.py b/erpnext/patches/v11_0/check_buying_selling_in_currency_exchange.py
index 5730212..d9d7981 100644
--- a/erpnext/patches/v11_0/check_buying_selling_in_currency_exchange.py
+++ b/erpnext/patches/v11_0/check_buying_selling_in_currency_exchange.py
@@ -2,5 +2,5 @@
 
 
 def execute():
-	frappe.reload_doc('setup', 'doctype', 'currency_exchange')
+	frappe.reload_doc("setup", "doctype", "currency_exchange")
 	frappe.db.sql("""update `tabCurrency Exchange` set for_buying = 1, for_selling = 1""")
diff --git a/erpnext/patches/v11_0/create_department_records_for_each_company.py b/erpnext/patches/v11_0/create_department_records_for_each_company.py
index 034418c..84be2be 100644
--- a/erpnext/patches/v11_0/create_department_records_for_each_company.py
+++ b/erpnext/patches/v11_0/create_department_records_for_each_company.py
@@ -4,11 +4,11 @@
 
 
 def execute():
-	frappe.local.lang = frappe.db.get_default("lang") or 'en'
+	frappe.local.lang = frappe.db.get_default("lang") or "en"
 
-	for doctype in ['department', 'leave_period', 'staffing_plan', 'job_opening']:
+	for doctype in ["department", "leave_period", "staffing_plan", "job_opening"]:
 		frappe.reload_doc("hr", "doctype", doctype)
-	frappe.reload_doc("Payroll", "doctype", 'payroll_entry')
+	frappe.reload_doc("Payroll", "doctype", "payroll_entry")
 
 	companies = frappe.db.get_all("Company", fields=["name", "abbr"])
 	departments = frappe.db.get_all("Department")
@@ -35,7 +35,7 @@
 			# append list of new department for each company
 			comp_dict[company.name][department.name] = copy_doc.name
 
-	rebuild_tree('Department', 'parent_department')
+	rebuild_tree("Department", "parent_department")
 	doctypes = ["Asset", "Employee", "Payroll Entry", "Staffing Plan", "Job Opening"]
 
 	for d in doctypes:
@@ -43,7 +43,8 @@
 
 	update_instructors(comp_dict)
 
-	frappe.local.lang = 'en'
+	frappe.local.lang = "en"
+
 
 def update_records(doctype, comp_dict):
 	when_then = []
@@ -51,20 +52,27 @@
 		records = comp_dict[company]
 
 		for department in records:
-			when_then.append('''
+			when_then.append(
+				"""
 				WHEN company = "%s" and department = "%s"
 				THEN "%s"
-			'''%(company, department, records[department]))
+			"""
+				% (company, department, records[department])
+			)
 
 	if not when_then:
 		return
 
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		update
 			`tab%s`
 		set
 			department = CASE %s END
-	"""%(doctype, " ".join(when_then)))
+	"""
+		% (doctype, " ".join(when_then))
+	)
+
 
 def update_instructors(comp_dict):
 	when_then = []
@@ -74,17 +82,23 @@
 		records = comp_dict[employee.company] if employee.company else []
 
 		for department in records:
-			when_then.append('''
+			when_then.append(
+				"""
 				WHEN employee = "%s" and department = "%s"
 				THEN "%s"
-			'''%(employee.name, department, records[department]))
+			"""
+				% (employee.name, department, records[department])
+			)
 
 	if not when_then:
 		return
 
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		update
 			`tabInstructor`
 		set
 			department = CASE %s END
-	"""%(" ".join(when_then)))
+	"""
+		% (" ".join(when_then))
+	)
diff --git a/erpnext/patches/v11_0/create_salary_structure_assignments.py b/erpnext/patches/v11_0/create_salary_structure_assignments.py
index 823eca1..b81e867 100644
--- a/erpnext/patches/v11_0/create_salary_structure_assignments.py
+++ b/erpnext/patches/v11_0/create_salary_structure_assignments.py
@@ -13,48 +13,62 @@
 
 
 def execute():
-	frappe.reload_doc('Payroll', 'doctype', 'Salary Structure')
+	frappe.reload_doc("Payroll", "doctype", "Salary Structure")
 	frappe.reload_doc("Payroll", "doctype", "Salary Structure Assignment")
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		delete from `tabSalary Structure Assignment`
 		where salary_structure in (select name from `tabSalary Structure` where is_active='No' or docstatus!=1)
-	""")
-	if frappe.db.table_exists('Salary Structure Employee'):
-		ss_details = frappe.db.sql("""
+	"""
+	)
+	if frappe.db.table_exists("Salary Structure Employee"):
+		ss_details = frappe.db.sql(
+			"""
 			select sse.employee, sse.employee_name, sse.from_date, sse.to_date,
 				sse.base, sse.variable, sse.parent as salary_structure, ss.company
 			from `tabSalary Structure Employee` sse, `tabSalary Structure` ss
 			where ss.name = sse.parent AND ss.is_active='Yes'
-			AND sse.employee in (select name from `tabEmployee` where ifNull(status, '') != 'Left')""", as_dict=1)
+			AND sse.employee in (select name from `tabEmployee` where ifNull(status, '') != 'Left')""",
+			as_dict=1,
+		)
 	else:
 		cols = ""
 		if "base" in frappe.db.get_table_columns("Salary Structure"):
 			cols = ", base, variable"
 
-		ss_details = frappe.db.sql("""
+		ss_details = frappe.db.sql(
+			"""
 			select name as salary_structure, employee, employee_name, from_date, to_date, company {0}
 			from `tabSalary Structure`
 			where is_active='Yes'
 			AND employee in (select name from `tabEmployee` where ifNull(status, '') != 'Left')
-		""".format(cols), as_dict=1)
+		""".format(
+				cols
+			),
+			as_dict=1,
+		)
 
 	all_companies = frappe.db.get_all("Company", fields=["name", "default_currency"])
 	for d in all_companies:
 		company = d.name
 		company_currency = d.default_currency
 
-		frappe.db.sql("""update `tabSalary Structure` set currency = %s where company=%s""", (company_currency, company))
+		frappe.db.sql(
+			"""update `tabSalary Structure` set currency = %s where company=%s""",
+			(company_currency, company),
+		)
 
 	for d in ss_details:
 		try:
-			joining_date, relieving_date = frappe.db.get_value("Employee", d.employee,
-				["date_of_joining", "relieving_date"])
+			joining_date, relieving_date = frappe.db.get_value(
+				"Employee", d.employee, ["date_of_joining", "relieving_date"]
+			)
 			from_date = d.from_date
 			if joining_date and getdate(from_date) < joining_date:
 				from_date = joining_date
 			elif relieving_date and getdate(from_date) > relieving_date:
 				continue
-			company_currency = frappe.db.get_value('Company', d.company, 'default_currency')
+			company_currency = frappe.db.get_value("Company", d.company, "default_currency")
 
 			s = frappe.new_doc("Salary Structure Assignment")
 			s.employee = d.employee
diff --git a/erpnext/patches/v11_0/drop_column_max_days_allowed.py b/erpnext/patches/v11_0/drop_column_max_days_allowed.py
index f0803cb..4b4770d 100644
--- a/erpnext/patches/v11_0/drop_column_max_days_allowed.py
+++ b/erpnext/patches/v11_0/drop_column_max_days_allowed.py
@@ -3,5 +3,5 @@
 
 def execute():
 	if frappe.db.exists("DocType", "Leave Type"):
-		if 'max_days_allowed' in frappe.db.get_table_columns("Leave Type"):
+		if "max_days_allowed" in frappe.db.get_table_columns("Leave Type"):
 			frappe.db.sql("alter table `tabLeave Type` drop column max_days_allowed")
diff --git a/erpnext/patches/v11_0/hr_ux_cleanups.py b/erpnext/patches/v11_0/hr_ux_cleanups.py
index 43c8504..0749bfc 100644
--- a/erpnext/patches/v11_0/hr_ux_cleanups.py
+++ b/erpnext/patches/v11_0/hr_ux_cleanups.py
@@ -2,11 +2,11 @@
 
 
 def execute():
-	frappe.reload_doctype('Employee')
-	frappe.db.sql('update tabEmployee set first_name = employee_name')
+	frappe.reload_doctype("Employee")
+	frappe.db.sql("update tabEmployee set first_name = employee_name")
 
 	# update holiday list
-	frappe.reload_doctype('Holiday List')
-	for holiday_list in frappe.get_all('Holiday List'):
-		holiday_list = frappe.get_doc('Holiday List', holiday_list.name)
-		holiday_list.db_set('total_holidays', len(holiday_list.holidays), update_modified = False)
+	frappe.reload_doctype("Holiday List")
+	for holiday_list in frappe.get_all("Holiday List"):
+		holiday_list = frappe.get_doc("Holiday List", holiday_list.name)
+		holiday_list.db_set("total_holidays", len(holiday_list.holidays), update_modified=False)
diff --git a/erpnext/patches/v11_0/make_asset_finance_book_against_old_entries.py b/erpnext/patches/v11_0/make_asset_finance_book_against_old_entries.py
index cd3869b..2131456 100644
--- a/erpnext/patches/v11_0/make_asset_finance_book_against_old_entries.py
+++ b/erpnext/patches/v11_0/make_asset_finance_book_against_old_entries.py
@@ -6,40 +6,50 @@
 
 
 def execute():
-	frappe.reload_doc('assets', 'doctype', 'asset_finance_book')
-	frappe.reload_doc('assets', 'doctype', 'depreciation_schedule')
-	frappe.reload_doc('assets', 'doctype', 'asset_category')
-	frappe.reload_doc('assets', 'doctype', 'asset')
-	frappe.reload_doc('assets', 'doctype', 'asset_movement')
-	frappe.reload_doc('assets', 'doctype', 'asset_category_account')
+	frappe.reload_doc("assets", "doctype", "asset_finance_book")
+	frappe.reload_doc("assets", "doctype", "depreciation_schedule")
+	frappe.reload_doc("assets", "doctype", "asset_category")
+	frappe.reload_doc("assets", "doctype", "asset")
+	frappe.reload_doc("assets", "doctype", "asset_movement")
+	frappe.reload_doc("assets", "doctype", "asset_category_account")
 
 	if frappe.db.has_column("Asset", "warehouse"):
-		frappe.db.sql(""" update `tabAsset` ast, `tabWarehouse` wh
-			set ast.location = wh.warehouse_name where ast.warehouse = wh.name""")
+		frappe.db.sql(
+			""" update `tabAsset` ast, `tabWarehouse` wh
+			set ast.location = wh.warehouse_name where ast.warehouse = wh.name"""
+		)
 
-		for d in frappe.get_all('Asset'):
-			doc = frappe.get_doc('Asset', d.name)
+		for d in frappe.get_all("Asset"):
+			doc = frappe.get_doc("Asset", d.name)
 			if doc.calculate_depreciation:
-				fb = doc.append('finance_books', {
-					'depreciation_method': doc.depreciation_method,
-					'total_number_of_depreciations': doc.total_number_of_depreciations,
-					'frequency_of_depreciation': doc.frequency_of_depreciation,
-					'depreciation_start_date': doc.next_depreciation_date,
-					'expected_value_after_useful_life': doc.expected_value_after_useful_life,
-					'value_after_depreciation': doc.value_after_depreciation
-				})
+				fb = doc.append(
+					"finance_books",
+					{
+						"depreciation_method": doc.depreciation_method,
+						"total_number_of_depreciations": doc.total_number_of_depreciations,
+						"frequency_of_depreciation": doc.frequency_of_depreciation,
+						"depreciation_start_date": doc.next_depreciation_date,
+						"expected_value_after_useful_life": doc.expected_value_after_useful_life,
+						"value_after_depreciation": doc.value_after_depreciation,
+					},
+				)
 
 				fb.db_update()
 
-		frappe.db.sql(""" update `tabDepreciation Schedule` ds, `tabAsset` ast
-			set ds.depreciation_method = ast.depreciation_method, ds.finance_book_id = 1 where ds.parent = ast.name """)
+		frappe.db.sql(
+			""" update `tabDepreciation Schedule` ds, `tabAsset` ast
+			set ds.depreciation_method = ast.depreciation_method, ds.finance_book_id = 1 where ds.parent = ast.name """
+		)
 
-		for category in frappe.get_all('Asset Category'):
+		for category in frappe.get_all("Asset Category"):
 			asset_category_doc = frappe.get_doc("Asset Category", category)
-			row = asset_category_doc.append('finance_books', {
-				'depreciation_method': asset_category_doc.depreciation_method,
-				'total_number_of_depreciations': asset_category_doc.total_number_of_depreciations,
-				'frequency_of_depreciation': asset_category_doc.frequency_of_depreciation
-			})
+			row = asset_category_doc.append(
+				"finance_books",
+				{
+					"depreciation_method": asset_category_doc.depreciation_method,
+					"total_number_of_depreciations": asset_category_doc.total_number_of_depreciations,
+					"frequency_of_depreciation": asset_category_doc.frequency_of_depreciation,
+				},
+			)
 
 			row.db_update()
diff --git a/erpnext/patches/v11_0/make_italian_localization_fields.py b/erpnext/patches/v11_0/make_italian_localization_fields.py
index 8ff23a5..1b9793d 100644
--- a/erpnext/patches/v11_0/make_italian_localization_fields.py
+++ b/erpnext/patches/v11_0/make_italian_localization_fields.py
@@ -9,11 +9,11 @@
 
 
 def execute():
-	company = frappe.get_all('Company', filters = {'country': 'Italy'})
+	company = frappe.get_all("Company", filters={"country": "Italy"})
 	if not company:
 		return
 
-	frappe.reload_doc('regional', 'report', 'electronic_invoice_register')
+	frappe.reload_doc("regional", "report", "electronic_invoice_register")
 	make_custom_fields()
 	setup_report()
 
@@ -25,15 +25,21 @@
 	if condition:
 		condition = "state_code = (case state {0} end),".format(condition)
 
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		UPDATE tabAddress set {condition} country_code = UPPER(ifnull((select code
 			from `tabCountry` where name = `tabAddress`.country), ''))
 			where country_code is null and state_code is null
-	""".format(condition=condition))
+	""".format(
+			condition=condition
+		)
+	)
 
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		UPDATE `tabSales Invoice Item` si, `tabSales Order` so
 			set si.customer_po_no = so.po_no, si.customer_po_date = so.po_date
 		WHERE
 			si.sales_order = so.name and so.po_no is not null
-	""")
+	"""
+	)
diff --git a/erpnext/patches/v11_0/make_job_card.py b/erpnext/patches/v11_0/make_job_card.py
index 120e018..d4b2089 100644
--- a/erpnext/patches/v11_0/make_job_card.py
+++ b/erpnext/patches/v11_0/make_job_card.py
@@ -8,21 +8,26 @@
 
 
 def execute():
-	frappe.reload_doc('manufacturing', 'doctype', 'work_order')
-	frappe.reload_doc('manufacturing', 'doctype', 'work_order_item')
-	frappe.reload_doc('manufacturing', 'doctype', 'job_card')
-	frappe.reload_doc('manufacturing', 'doctype', 'job_card_item')
+	frappe.reload_doc("manufacturing", "doctype", "work_order")
+	frappe.reload_doc("manufacturing", "doctype", "work_order_item")
+	frappe.reload_doc("manufacturing", "doctype", "job_card")
+	frappe.reload_doc("manufacturing", "doctype", "job_card_item")
 
-	fieldname = frappe.db.get_value('DocField', {'fieldname': 'work_order', 'parent': 'Timesheet'}, 'fieldname')
+	fieldname = frappe.db.get_value(
+		"DocField", {"fieldname": "work_order", "parent": "Timesheet"}, "fieldname"
+	)
 	if not fieldname:
-		fieldname = frappe.db.get_value('DocField', {'fieldname': 'production_order', 'parent': 'Timesheet'}, 'fieldname')
-		if not fieldname: return
+		fieldname = frappe.db.get_value(
+			"DocField", {"fieldname": "production_order", "parent": "Timesheet"}, "fieldname"
+		)
+		if not fieldname:
+			return
 
-	for d in frappe.get_all('Timesheet',
-		filters={fieldname: ['!=', ""], 'docstatus': 0},
-		fields=[fieldname, 'name']):
+	for d in frappe.get_all(
+		"Timesheet", filters={fieldname: ["!=", ""], "docstatus": 0}, fields=[fieldname, "name"]
+	):
 		if d[fieldname]:
-			doc = frappe.get_doc('Work Order', d[fieldname])
+			doc = frappe.get_doc("Work Order", d[fieldname])
 			for row in doc.operations:
 				create_job_card(doc, row, auto_create=True)
-			frappe.delete_doc('Timesheet', d.name)
+			frappe.delete_doc("Timesheet", d.name)
diff --git a/erpnext/patches/v11_0/make_location_from_warehouse.py b/erpnext/patches/v11_0/make_location_from_warehouse.py
index ef6262b..c863bb7 100644
--- a/erpnext/patches/v11_0/make_location_from_warehouse.py
+++ b/erpnext/patches/v11_0/make_location_from_warehouse.py
@@ -7,14 +7,16 @@
 
 
 def execute():
-	if not frappe.db.get_value('Asset', {'docstatus': ('<', 2) }, 'name'): return
-	frappe.reload_doc('assets', 'doctype', 'location')
-	frappe.reload_doc('stock', 'doctype', 'warehouse')
+	if not frappe.db.get_value("Asset", {"docstatus": ("<", 2)}, "name"):
+		return
+	frappe.reload_doc("assets", "doctype", "location")
+	frappe.reload_doc("stock", "doctype", "warehouse")
 
-	for d in frappe.get_all('Warehouse',
-		fields = ['warehouse_name', 'is_group', 'parent_warehouse'], order_by="lft asc"):
+	for d in frappe.get_all(
+		"Warehouse", fields=["warehouse_name", "is_group", "parent_warehouse"], order_by="lft asc"
+	):
 		try:
-			loc = frappe.new_doc('Location')
+			loc = frappe.new_doc("Location")
 			loc.location_name = d.warehouse_name
 			loc.is_group = d.is_group
 			loc.flags.ignore_mandatory = True
@@ -27,5 +29,6 @@
 
 	rebuild_tree("Location", "parent_location")
 
+
 def get_parent_warehouse_name(warehouse):
-	return frappe.db.get_value('Warehouse', warehouse, 'warehouse_name')
+	return frappe.db.get_value("Warehouse", warehouse, "warehouse_name")
diff --git a/erpnext/patches/v11_0/make_quality_inspection_template.py b/erpnext/patches/v11_0/make_quality_inspection_template.py
index 58c9fb9..deebfa8 100644
--- a/erpnext/patches/v11_0/make_quality_inspection_template.py
+++ b/erpnext/patches/v11_0/make_quality_inspection_template.py
@@ -6,21 +6,29 @@
 
 
 def execute():
-	frappe.reload_doc('stock', 'doctype', 'quality_inspection_template')
-	frappe.reload_doc('stock', 'doctype', 'item')
+	frappe.reload_doc("stock", "doctype", "quality_inspection_template")
+	frappe.reload_doc("stock", "doctype", "item")
 
-	for data in frappe.get_all('Item Quality Inspection Parameter',
-		fields = ["distinct parent"], filters = {'parenttype': 'Item'}):
+	for data in frappe.get_all(
+		"Item Quality Inspection Parameter", fields=["distinct parent"], filters={"parenttype": "Item"}
+	):
 		qc_doc = frappe.new_doc("Quality Inspection Template")
-		qc_doc.quality_inspection_template_name = 'QIT/%s' % data.parent
+		qc_doc.quality_inspection_template_name = "QIT/%s" % data.parent
 		qc_doc.flags.ignore_mandatory = True
 		qc_doc.save(ignore_permissions=True)
 
-		frappe.db.set_value('Item', data.parent, "quality_inspection_template", qc_doc.name, update_modified=False)
-		frappe.db.sql(""" update `tabItem Quality Inspection Parameter`
+		frappe.db.set_value(
+			"Item", data.parent, "quality_inspection_template", qc_doc.name, update_modified=False
+		)
+		frappe.db.sql(
+			""" update `tabItem Quality Inspection Parameter`
 			set parentfield = 'item_quality_inspection_parameter', parenttype = 'Quality Inspection Template',
-			parent = %s where parenttype = 'Item' and parent = %s""", (qc_doc.name, data.parent))
+			parent = %s where parenttype = 'Item' and parent = %s""",
+			(qc_doc.name, data.parent),
+		)
 
 	# update field in item variant settings
-	frappe.db.sql(""" update `tabVariant Field` set field_name = 'quality_inspection_template'
-		where field_name = 'quality_parameters'""")
+	frappe.db.sql(
+		""" update `tabVariant Field` set field_name = 'quality_inspection_template'
+		where field_name = 'quality_parameters'"""
+	)
diff --git a/erpnext/patches/v11_0/merge_land_unit_with_location.py b/erpnext/patches/v11_0/merge_land_unit_with_location.py
index e1d0b12..c1afef6 100644
--- a/erpnext/patches/v11_0/merge_land_unit_with_location.py
+++ b/erpnext/patches/v11_0/merge_land_unit_with_location.py
@@ -8,51 +8,55 @@
 
 def execute():
 	# Rename and reload the Land Unit and Linked Land Unit doctypes
-	if frappe.db.table_exists('Land Unit') and not frappe.db.table_exists('Location'):
-		frappe.rename_doc('DocType', 'Land Unit', 'Location', force=True)
+	if frappe.db.table_exists("Land Unit") and not frappe.db.table_exists("Location"):
+		frappe.rename_doc("DocType", "Land Unit", "Location", force=True)
 
-	frappe.reload_doc('assets', 'doctype', 'location')
+	frappe.reload_doc("assets", "doctype", "location")
 
-	if frappe.db.table_exists('Linked Land Unit') and not frappe.db.table_exists('Linked Location'):
-		frappe.rename_doc('DocType', 'Linked Land Unit', 'Linked Location', force=True)
+	if frappe.db.table_exists("Linked Land Unit") and not frappe.db.table_exists("Linked Location"):
+		frappe.rename_doc("DocType", "Linked Land Unit", "Linked Location", force=True)
 
-	frappe.reload_doc('assets', 'doctype', 'linked_location')
+	frappe.reload_doc("assets", "doctype", "linked_location")
 
-	if not frappe.db.table_exists('Crop Cycle'):
-		frappe.reload_doc('agriculture', 'doctype', 'crop_cycle')
+	if not frappe.db.table_exists("Crop Cycle"):
+		frappe.reload_doc("agriculture", "doctype", "crop_cycle")
 
 	# Rename the fields in related doctypes
-	if 'linked_land_unit' in frappe.db.get_table_columns('Crop Cycle'):
-		rename_field('Crop Cycle', 'linked_land_unit', 'linked_location')
+	if "linked_land_unit" in frappe.db.get_table_columns("Crop Cycle"):
+		rename_field("Crop Cycle", "linked_land_unit", "linked_location")
 
-	if 'land_unit' in frappe.db.get_table_columns('Linked Location'):
-		rename_field('Linked Location', 'land_unit', 'location')
+	if "land_unit" in frappe.db.get_table_columns("Linked Location"):
+		rename_field("Linked Location", "land_unit", "location")
 
 	if not frappe.db.exists("Location", "All Land Units"):
-		frappe.get_doc({"doctype": "Location", "is_group": True, "location_name": "All Land Units"}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{"doctype": "Location", "is_group": True, "location_name": "All Land Units"}
+		).insert(ignore_permissions=True)
 
-	if frappe.db.table_exists('Land Unit'):
-		land_units = frappe.get_all('Land Unit', fields=['*'], order_by='lft')
+	if frappe.db.table_exists("Land Unit"):
+		land_units = frappe.get_all("Land Unit", fields=["*"], order_by="lft")
 
 		for land_unit in land_units:
-			if not frappe.db.exists('Location', land_unit.get('land_unit_name')):
-				frappe.get_doc({
-					'doctype': 'Location',
-					'location_name': land_unit.get('land_unit_name'),
-					'parent_location': land_unit.get('parent_land_unit') or "All Land Units",
-					'is_container': land_unit.get('is_container'),
-					'is_group': land_unit.get('is_group'),
-					'latitude': land_unit.get('latitude'),
-					'longitude': land_unit.get('longitude'),
-					'area': land_unit.get('area'),
-					'location': land_unit.get('location'),
-					'lft': land_unit.get('lft'),
-					'rgt': land_unit.get('rgt')
-				}).insert(ignore_permissions=True)
+			if not frappe.db.exists("Location", land_unit.get("land_unit_name")):
+				frappe.get_doc(
+					{
+						"doctype": "Location",
+						"location_name": land_unit.get("land_unit_name"),
+						"parent_location": land_unit.get("parent_land_unit") or "All Land Units",
+						"is_container": land_unit.get("is_container"),
+						"is_group": land_unit.get("is_group"),
+						"latitude": land_unit.get("latitude"),
+						"longitude": land_unit.get("longitude"),
+						"area": land_unit.get("area"),
+						"location": land_unit.get("location"),
+						"lft": land_unit.get("lft"),
+						"rgt": land_unit.get("rgt"),
+					}
+				).insert(ignore_permissions=True)
 
 	# Delete the Land Unit and Linked Land Unit doctypes
-	if frappe.db.table_exists('Land Unit'):
-		frappe.delete_doc('DocType', 'Land Unit', force=1)
+	if frappe.db.table_exists("Land Unit"):
+		frappe.delete_doc("DocType", "Land Unit", force=1)
 
-	if frappe.db.table_exists('Linked Land Unit'):
-		frappe.delete_doc('DocType', 'Linked Land Unit', force=1)
+	if frappe.db.table_exists("Linked Land Unit"):
+		frappe.delete_doc("DocType", "Linked Land Unit", force=1)
diff --git a/erpnext/patches/v11_0/move_item_defaults_to_child_table_for_multicompany.py b/erpnext/patches/v11_0/move_item_defaults_to_child_table_for_multicompany.py
index bfc3fbc..37c0779 100644
--- a/erpnext/patches/v11_0/move_item_defaults_to_child_table_for_multicompany.py
+++ b/erpnext/patches/v11_0/move_item_defaults_to_child_table_for_multicompany.py
@@ -6,22 +6,23 @@
 
 
 def execute():
-	'''
+	"""
 
 	Fields to move from the item to item defaults child table
 	[ default_warehouse, buying_cost_center, expense_account, selling_cost_center, income_account ]
 
-	'''
-	if not frappe.db.has_column('Item', 'default_warehouse'):
+	"""
+	if not frappe.db.has_column("Item", "default_warehouse"):
 		return
 
-	frappe.reload_doc('stock', 'doctype', 'item_default')
-	frappe.reload_doc('stock', 'doctype', 'item')
+	frappe.reload_doc("stock", "doctype", "item_default")
+	frappe.reload_doc("stock", "doctype", "item")
 
 	companies = frappe.get_all("Company")
 	if len(companies) == 1 and not frappe.get_all("Item Default", limit=1):
 		try:
-			frappe.db.sql('''
+			frappe.db.sql(
+				"""
 					INSERT INTO `tabItem Default`
 						(name, parent, parenttype, parentfield, idx, company, default_warehouse,
 						buying_cost_center, selling_cost_center, expense_account, income_account, default_supplier)
@@ -30,22 +31,30 @@
 						'item_defaults' as parentfield, 1 as idx, %s as company, default_warehouse,
 						buying_cost_center, selling_cost_center, expense_account, income_account, default_supplier
 					FROM `tabItem`;
-			''', companies[0].name)
+			""",
+				companies[0].name,
+			)
 		except Exception:
 			pass
 	else:
-		item_details = frappe.db.sql(""" SELECT name, default_warehouse,
+		item_details = frappe.db.sql(
+			""" SELECT name, default_warehouse,
 				buying_cost_center, expense_account, selling_cost_center, income_account
 			FROM tabItem
 			WHERE
-				name not in (select distinct parent from `tabItem Default`) and ifnull(disabled, 0) = 0"""
-		, as_dict=1)
+				name not in (select distinct parent from `tabItem Default`) and ifnull(disabled, 0) = 0""",
+			as_dict=1,
+		)
 
 		items_default_data = {}
 		for item_data in item_details:
-			for d in [["default_warehouse", "Warehouse"], ["expense_account", "Account"],
-				["income_account", "Account"], ["buying_cost_center", "Cost Center"],
-				["selling_cost_center", "Cost Center"]]:
+			for d in [
+				["default_warehouse", "Warehouse"],
+				["expense_account", "Account"],
+				["income_account", "Account"],
+				["buying_cost_center", "Cost Center"],
+				["selling_cost_center", "Cost Center"],
+			]:
 				if item_data.get(d[0]):
 					company = frappe.get_value(d[1], item_data.get(d[0]), "company", cache=True)
 
@@ -73,25 +82,32 @@
 
 		for item_code, companywise_item_data in items_default_data.items():
 			for company, item_default_data in companywise_item_data.items():
-				to_insert_data.append((
-					frappe.generate_hash("", 10),
-					item_code,
-					'Item',
-					'item_defaults',
-					company,
-					item_default_data.get('default_warehouse'),
-					item_default_data.get('expense_account'),
-					item_default_data.get('income_account'),
-					item_default_data.get('buying_cost_center'),
-					item_default_data.get('selling_cost_center'),
-				))
+				to_insert_data.append(
+					(
+						frappe.generate_hash("", 10),
+						item_code,
+						"Item",
+						"item_defaults",
+						company,
+						item_default_data.get("default_warehouse"),
+						item_default_data.get("expense_account"),
+						item_default_data.get("income_account"),
+						item_default_data.get("buying_cost_center"),
+						item_default_data.get("selling_cost_center"),
+					)
+				)
 
 		if to_insert_data:
-			frappe.db.sql('''
+			frappe.db.sql(
+				"""
 				INSERT INTO `tabItem Default`
 				(
 					`name`, `parent`, `parenttype`, `parentfield`, `company`, `default_warehouse`,
 					`expense_account`, `income_account`, `buying_cost_center`, `selling_cost_center`
 				)
 				VALUES {}
-			'''.format(', '.join(['%s'] * len(to_insert_data))), tuple(to_insert_data))
+			""".format(
+					", ".join(["%s"] * len(to_insert_data))
+				),
+				tuple(to_insert_data),
+			)
diff --git a/erpnext/patches/v11_0/move_leave_approvers_from_employee.py b/erpnext/patches/v11_0/move_leave_approvers_from_employee.py
index 80e5ef7..f91a7db 100644
--- a/erpnext/patches/v11_0/move_leave_approvers_from_employee.py
+++ b/erpnext/patches/v11_0/move_leave_approvers_from_employee.py
@@ -7,20 +7,23 @@
 	frappe.reload_doc("hr", "doctype", "employee")
 	frappe.reload_doc("hr", "doctype", "department")
 
-	if frappe.db.has_column('Department', 'leave_approver'):
-		rename_field('Department', "leave_approver", "leave_approvers")
+	if frappe.db.has_column("Department", "leave_approver"):
+		rename_field("Department", "leave_approver", "leave_approvers")
 
-	if frappe.db.has_column('Department', 'expense_approver'):
-		rename_field('Department', "expense_approver", "expense_approvers")
+	if frappe.db.has_column("Department", "expense_approver"):
+		rename_field("Department", "expense_approver", "expense_approvers")
 
 	if not frappe.db.table_exists("Employee Leave Approver"):
 		return
 
-	approvers = frappe.db.sql("""select distinct app.leave_approver, emp.department from
+	approvers = frappe.db.sql(
+		"""select distinct app.leave_approver, emp.department from
 	`tabEmployee Leave Approver` app, `tabEmployee` emp
 		where app.parenttype = 'Employee'
 		and emp.name = app.parent
-		""", as_dict=True)
+		""",
+		as_dict=True,
+	)
 
 	for record in approvers:
 		if record.department:
@@ -28,6 +31,4 @@
 			if not department:
 				return
 			if not len(department.leave_approvers):
-				department.append("leave_approvers",{
-					"approver": record.leave_approver
-				}).db_insert()
+				department.append("leave_approvers", {"approver": record.leave_approver}).db_insert()
diff --git a/erpnext/patches/v11_0/rebuild_tree_for_company.py b/erpnext/patches/v11_0/rebuild_tree_for_company.py
index cad9c6c..fc06c5d 100644
--- a/erpnext/patches/v11_0/rebuild_tree_for_company.py
+++ b/erpnext/patches/v11_0/rebuild_tree_for_company.py
@@ -4,4 +4,4 @@
 
 def execute():
 	frappe.reload_doc("setup", "doctype", "company")
-	rebuild_tree('Company', 'parent_company')
+	rebuild_tree("Company", "parent_company")
diff --git a/erpnext/patches/v11_0/refactor_autoname_naming.py b/erpnext/patches/v11_0/refactor_autoname_naming.py
index 1c4d8f1..fa8f4eb 100644
--- a/erpnext/patches/v11_0/refactor_autoname_naming.py
+++ b/erpnext/patches/v11_0/refactor_autoname_naming.py
@@ -6,99 +6,100 @@
 from frappe.custom.doctype.property_setter.property_setter import make_property_setter
 
 doctype_series_map = {
-	'Activity Cost': 'PROJ-ACC-.#####',
-	'Agriculture Task': 'AG-TASK-.#####',
-	'Assessment Plan': 'EDU-ASP-.YYYY.-.#####',
-	'Assessment Result': 'EDU-RES-.YYYY.-.#####',
-	'Asset Movement': 'ACC-ASM-.YYYY.-.#####',
-	'Attendance Request': 'HR-ARQ-.YY.-.MM.-.#####',
-	'Authorization Rule': 'HR-ARU-.#####',
-	'Bank Guarantee': 'ACC-BG-.YYYY.-.#####',
-	'Bin': 'MAT-BIN-.YYYY.-.#####',
-	'Certification Application': 'NPO-CAPP-.YYYY.-.#####',
-	'Certified Consultant': 'NPO-CONS-.YYYY.-.#####',
-	'Chat Room': 'CHAT-ROOM-.#####',
-	'Compensatory Leave Request': 'HR-CMP-.YY.-.MM.-.#####',
-	'Client Script': 'SYS-SCR-.#####',
-	'Employee Benefit Application': 'HR-BEN-APP-.YY.-.MM.-.#####',
-	'Employee Benefit Application Detail': '',
-	'Employee Benefit Claim': 'HR-BEN-CLM-.YY.-.MM.-.#####',
-	'Employee Incentive': 'HR-EINV-.YY.-.MM.-.#####',
-	'Employee Onboarding': 'HR-EMP-ONB-.YYYY.-.#####',
-	'Employee Onboarding Template': 'HR-EMP-ONT-.#####',
-	'Employee Promotion': 'HR-EMP-PRO-.YYYY.-.#####',
-	'Employee Separation': 'HR-EMP-SEP-.YYYY.-.#####',
-	'Employee Separation Template': 'HR-EMP-STP-.#####',
-	'Employee Tax Exemption Declaration': 'HR-TAX-DEC-.YYYY.-.#####',
-	'Employee Tax Exemption Proof Submission': 'HR-TAX-PRF-.YYYY.-.#####',
-	'Employee Transfer': 'HR-EMP-TRN-.YYYY.-.#####',
-	'Event': 'EVENT-.YYYY.-.#####',
-	'Exchange Rate Revaluation': 'ACC-ERR-.YYYY.-.#####',
-	'GL Entry': 'ACC-GLE-.YYYY.-.#####',
-	'Guardian': 'EDU-GRD-.YYYY.-.#####',
-	'Hotel Room Reservation': 'HTL-RES-.YYYY.-.#####',
-	'Item Price': '',
-	'Job Applicant': 'HR-APP-.YYYY.-.#####',
-	'Job Offer': 'HR-OFF-.YYYY.-.#####',
-	'Leave Encashment': 'HR-ENC-.YYYY.-.#####',
-	'Leave Period': 'HR-LPR-.YYYY.-.#####',
-	'Leave Policy': 'HR-LPOL-.YYYY.-.#####',
-	'Loan': 'ACC-LOAN-.YYYY.-.#####',
-	'Loan Application': 'ACC-LOAP-.YYYY.-.#####',
-	'Loyalty Point Entry': '',
-	'Membership': 'NPO-MSH-.YYYY.-.#####',
-	'Packing Slip': 'MAT-PAC-.YYYY.-.#####',
-	'Patient Appointment': 'HLC-APP-.YYYY.-.#####',
-	'Payment Terms Template Detail': '',
-	'Payroll Entry': 'HR-PRUN-.YYYY.-.#####',
-	'Period Closing Voucher': 'ACC-PCV-.YYYY.-.#####',
-	'Plant Analysis': 'AG-PLA-.YYYY.-.#####',
-	'POS Closing Entry': 'POS-CLO-.YYYY.-.#####',
-	'Prepared Report': 'SYS-PREP-.YYYY.-.#####',
-	'Program Enrollment': 'EDU-ENR-.YYYY.-.#####',
-	'Quotation Item': '',
-	'Restaurant Reservation': 'RES-RES-.YYYY.-.#####',
-	'Retention Bonus': 'HR-RTB-.YYYY.-.#####',
-	'Room': 'HTL-ROOM-.YYYY.-.#####',
-	'Salary Structure Assignment': 'HR-SSA-.YY.-.MM.-.#####',
-	'Sales Taxes and Charges': '',
-	'Share Transfer': 'ACC-SHT-.YYYY.-.#####',
-	'Shift Assignment': 'HR-SHA-.YY.-.MM.-.#####',
-	'Shift Request': 'HR-SHR-.YY.-.MM.-.#####',
-	'SMS Log': 'SYS-SMS-.#####',
-	'Soil Analysis': 'AG-ANA-.YY.-.MM.-.#####',
-	'Soil Texture': 'AG-TEX-.YYYY.-.#####',
-	'Stock Ledger Entry': 'MAT-SLE-.YYYY.-.#####',
-	'Student Leave Application': 'EDU-SLA-.YYYY.-.#####',
-	'Student Log': 'EDU-SLOG-.YYYY.-.#####',
-	'Subscription': 'ACC-SUB-.YYYY.-.#####',
-	'Task': 'TASK-.YYYY.-.#####',
-	'Tax Rule': 'ACC-TAX-RULE-.YYYY.-.#####',
-	'Training Feedback': 'HR-TRF-.YYYY.-.#####',
-	'Training Result': 'HR-TRR-.YYYY.-.#####',
-	'Travel Request': 'HR-TRQ-.YYYY.-.#####',
-	'UOM Conversion Factor': 'MAT-UOM-CNV-.#####',
-	'Water Analysis': 'HR-WAT-.YYYY.-.#####',
-	'Workflow Action': 'SYS-WACT-.#####',
+	"Activity Cost": "PROJ-ACC-.#####",
+	"Agriculture Task": "AG-TASK-.#####",
+	"Assessment Plan": "EDU-ASP-.YYYY.-.#####",
+	"Assessment Result": "EDU-RES-.YYYY.-.#####",
+	"Asset Movement": "ACC-ASM-.YYYY.-.#####",
+	"Attendance Request": "HR-ARQ-.YY.-.MM.-.#####",
+	"Authorization Rule": "HR-ARU-.#####",
+	"Bank Guarantee": "ACC-BG-.YYYY.-.#####",
+	"Bin": "MAT-BIN-.YYYY.-.#####",
+	"Certification Application": "NPO-CAPP-.YYYY.-.#####",
+	"Certified Consultant": "NPO-CONS-.YYYY.-.#####",
+	"Chat Room": "CHAT-ROOM-.#####",
+	"Compensatory Leave Request": "HR-CMP-.YY.-.MM.-.#####",
+	"Client Script": "SYS-SCR-.#####",
+	"Employee Benefit Application": "HR-BEN-APP-.YY.-.MM.-.#####",
+	"Employee Benefit Application Detail": "",
+	"Employee Benefit Claim": "HR-BEN-CLM-.YY.-.MM.-.#####",
+	"Employee Incentive": "HR-EINV-.YY.-.MM.-.#####",
+	"Employee Onboarding": "HR-EMP-ONB-.YYYY.-.#####",
+	"Employee Onboarding Template": "HR-EMP-ONT-.#####",
+	"Employee Promotion": "HR-EMP-PRO-.YYYY.-.#####",
+	"Employee Separation": "HR-EMP-SEP-.YYYY.-.#####",
+	"Employee Separation Template": "HR-EMP-STP-.#####",
+	"Employee Tax Exemption Declaration": "HR-TAX-DEC-.YYYY.-.#####",
+	"Employee Tax Exemption Proof Submission": "HR-TAX-PRF-.YYYY.-.#####",
+	"Employee Transfer": "HR-EMP-TRN-.YYYY.-.#####",
+	"Event": "EVENT-.YYYY.-.#####",
+	"Exchange Rate Revaluation": "ACC-ERR-.YYYY.-.#####",
+	"GL Entry": "ACC-GLE-.YYYY.-.#####",
+	"Guardian": "EDU-GRD-.YYYY.-.#####",
+	"Hotel Room Reservation": "HTL-RES-.YYYY.-.#####",
+	"Item Price": "",
+	"Job Applicant": "HR-APP-.YYYY.-.#####",
+	"Job Offer": "HR-OFF-.YYYY.-.#####",
+	"Leave Encashment": "HR-ENC-.YYYY.-.#####",
+	"Leave Period": "HR-LPR-.YYYY.-.#####",
+	"Leave Policy": "HR-LPOL-.YYYY.-.#####",
+	"Loan": "ACC-LOAN-.YYYY.-.#####",
+	"Loan Application": "ACC-LOAP-.YYYY.-.#####",
+	"Loyalty Point Entry": "",
+	"Membership": "NPO-MSH-.YYYY.-.#####",
+	"Packing Slip": "MAT-PAC-.YYYY.-.#####",
+	"Patient Appointment": "HLC-APP-.YYYY.-.#####",
+	"Payment Terms Template Detail": "",
+	"Payroll Entry": "HR-PRUN-.YYYY.-.#####",
+	"Period Closing Voucher": "ACC-PCV-.YYYY.-.#####",
+	"Plant Analysis": "AG-PLA-.YYYY.-.#####",
+	"POS Closing Entry": "POS-CLO-.YYYY.-.#####",
+	"Prepared Report": "SYS-PREP-.YYYY.-.#####",
+	"Program Enrollment": "EDU-ENR-.YYYY.-.#####",
+	"Quotation Item": "",
+	"Restaurant Reservation": "RES-RES-.YYYY.-.#####",
+	"Retention Bonus": "HR-RTB-.YYYY.-.#####",
+	"Room": "HTL-ROOM-.YYYY.-.#####",
+	"Salary Structure Assignment": "HR-SSA-.YY.-.MM.-.#####",
+	"Sales Taxes and Charges": "",
+	"Share Transfer": "ACC-SHT-.YYYY.-.#####",
+	"Shift Assignment": "HR-SHA-.YY.-.MM.-.#####",
+	"Shift Request": "HR-SHR-.YY.-.MM.-.#####",
+	"SMS Log": "SYS-SMS-.#####",
+	"Soil Analysis": "AG-ANA-.YY.-.MM.-.#####",
+	"Soil Texture": "AG-TEX-.YYYY.-.#####",
+	"Stock Ledger Entry": "MAT-SLE-.YYYY.-.#####",
+	"Subscription": "ACC-SUB-.YYYY.-.#####",
+	"Task": "TASK-.YYYY.-.#####",
+	"Tax Rule": "ACC-TAX-RULE-.YYYY.-.#####",
+	"Training Feedback": "HR-TRF-.YYYY.-.#####",
+	"Training Result": "HR-TRR-.YYYY.-.#####",
+	"Travel Request": "HR-TRQ-.YYYY.-.#####",
+	"UOM Conversion Factor": "MAT-UOM-CNV-.#####",
+	"Water Analysis": "HR-WAT-.YYYY.-.#####",
+	"Workflow Action": "SYS-WACT-.#####",
 }
 
+
 def execute():
 	series_to_set = get_series()
 	for doctype, opts in series_to_set.items():
-		set_series(doctype, opts['value'])
+		set_series(doctype, opts["value"])
+
 
 def set_series(doctype, value):
-	doc = frappe.db.exists('Property Setter', {'doc_type': doctype, 'property': 'autoname'})
+	doc = frappe.db.exists("Property Setter", {"doc_type": doctype, "property": "autoname"})
 	if doc:
-		frappe.db.set_value('Property Setter', doc, 'value', value)
+		frappe.db.set_value("Property Setter", doc, "value", value)
 	else:
-		make_property_setter(doctype, '', 'autoname', value, '', for_doctype = True)
+		make_property_setter(doctype, "", "autoname", value, "", for_doctype=True)
+
 
 def get_series():
 	series_to_set = {}
 
 	for doctype in doctype_series_map:
-		if not frappe.db.exists('DocType', doctype):
+		if not frappe.db.exists("DocType", doctype):
 			continue
 
 		if not frappe.db.a_row_exists(doctype):
@@ -110,10 +111,11 @@
 
 		# set autoname property setter
 		if series_to_preserve:
-			series_to_set[doctype] = {'value': series_to_preserve}
+			series_to_set[doctype] = {"value": series_to_preserve}
 
 	return series_to_set
 
+
 def get_series_to_preserve(doctype):
-	series_to_preserve = frappe.db.get_value('DocType', doctype, 'autoname')
+	series_to_preserve = frappe.db.get_value("DocType", doctype, "autoname")
 	return series_to_preserve
diff --git a/erpnext/patches/v11_0/refactor_naming_series.py b/erpnext/patches/v11_0/refactor_naming_series.py
index 820d13e..12c768f 100644
--- a/erpnext/patches/v11_0/refactor_naming_series.py
+++ b/erpnext/patches/v11_0/refactor_naming_series.py
@@ -6,81 +6,87 @@
 from frappe.custom.doctype.property_setter.property_setter import make_property_setter
 
 doctype_series_map = {
-	'Additional Salary': 'HR-ADS-.YY.-.MM.-',
-	'Appraisal': 'HR-APR-.YY.-.MM.',
-	'Asset': 'ACC-ASS-.YYYY.-',
-	'Attendance': 'HR-ATT-.YYYY.-',
-	'Auto Repeat': 'SYS-ARP-.YYYY.-',
-	'Blanket Order': 'MFG-BLR-.YYYY.-',
-	'Campaign': 'SAL-CAM-.YYYY.-',
-	'Course Schedule': 'EDU-CSH-.YYYY.-',
-	'Customer': 'CUST-.YYYY.-',
-	'Delivery Note': 'MAT-DN-.YYYY.-',
-	'Delivery Trip': 'MAT-DT-.YYYY.-',
-	'Driver': 'HR-DRI-.YYYY.-',
-	'Employee': 'HR-EMP-',
-	'Employee Advance': 'HR-EAD-.YYYY.-',
-	'Expense Claim': 'HR-EXP-.YYYY.-',
-	'Fee Schedule': 'EDU-FSH-.YYYY.-',
-	'Fee Structure': 'EDU-FST-.YYYY.-',
-	'Fees': 'EDU-FEE-.YYYY.-',
-	'Installation Note': 'MAT-INS-.YYYY.-',
-	'Instructor': 'EDU-INS-.YYYY.-',
-	'Issue': 'ISS-.YYYY.-',
-	'Journal Entry': 'ACC-JV-.YYYY.-',
-	'Landed Cost Voucher': 'MAT-LCV-.YYYY.-',
-	'Lead': 'CRM-LEAD-.YYYY.-',
-	'Leave Allocation': 'HR-LAL-.YYYY.-',
-	'Leave Application': 'HR-LAP-.YYYY.-',
-	'Maintenance Schedule': 'MAT-MSH-.YYYY.-',
-	'Maintenance Visit': 'MAT-MVS-.YYYY.-',
-	'Material Request': 'MAT-MR-.YYYY.-',
-	'Member': 'NPO-MEM-.YYYY.-',
-	'Opportunity': 'CRM-OPP-.YYYY.-',
-	'Packing Slip': 'MAT-PAC-.YYYY.-',
-	'Payment Entry': 'ACC-PAY-.YYYY.-',
-	'Payment Request': 'ACC-PRQ-.YYYY.-',
-	'Production Plan': 'MFG-PP-.YYYY.-',
-	'Project Update': 'PROJ-UPD-.YYYY.-',
-	'Purchase Invoice': 'ACC-PINV-.YYYY.-',
-	'Purchase Order': 'PUR-ORD-.YYYY.-',
-	'Purchase Receipt': 'MAT-PRE-.YYYY.-',
-	'Quality Inspection': 'MAT-QA-.YYYY.-',
-	'Quotation': 'SAL-QTN-.YYYY.-',
-	'Request for Quotation': 'PUR-RFQ-.YYYY.-',
-	'Sales Invoice': 'ACC-SINV-.YYYY.-',
-	'Sales Order': 'SAL-ORD-.YYYY.-',
-	'Sample Collection': 'HLC-SC-.YYYY.-',
-	'Shareholder': 'ACC-SH-.YYYY.-',
-	'Stock Entry': 'MAT-STE-.YYYY.-',
-	'Stock Reconciliation': 'MAT-RECO-.YYYY.-',
-	'Student': 'EDU-STU-.YYYY.-',
-	'Student Applicant': 'EDU-APP-.YYYY.-',
-	'Supplier': 'SUP-.YYYY.-',
-	'Supplier Quotation': 'PUR-SQTN-.YYYY.-',
-	'Supplier Scorecard Period': 'PU-SSP-.YYYY.-',
-	'Timesheet': 'TS-.YYYY.-',
-	'Vehicle Log': 'HR-VLOG-.YYYY.-',
-	'Warranty Claim': 'SER-WRN-.YYYY.-',
-	'Work Order': 'MFG-WO-.YYYY.-'
+	"Additional Salary": "HR-ADS-.YY.-.MM.-",
+	"Appraisal": "HR-APR-.YY.-.MM.",
+	"Asset": "ACC-ASS-.YYYY.-",
+	"Attendance": "HR-ATT-.YYYY.-",
+	"Auto Repeat": "SYS-ARP-.YYYY.-",
+	"Blanket Order": "MFG-BLR-.YYYY.-",
+	"Campaign": "SAL-CAM-.YYYY.-",
+	"Course Schedule": "EDU-CSH-.YYYY.-",
+	"Customer": "CUST-.YYYY.-",
+	"Delivery Note": "MAT-DN-.YYYY.-",
+	"Delivery Trip": "MAT-DT-.YYYY.-",
+	"Driver": "HR-DRI-.YYYY.-",
+	"Employee": "HR-EMP-",
+	"Employee Advance": "HR-EAD-.YYYY.-",
+	"Expense Claim": "HR-EXP-.YYYY.-",
+	"Fee Schedule": "EDU-FSH-.YYYY.-",
+	"Fee Structure": "EDU-FST-.YYYY.-",
+	"Fees": "EDU-FEE-.YYYY.-",
+	"Installation Note": "MAT-INS-.YYYY.-",
+	"Instructor": "EDU-INS-.YYYY.-",
+	"Issue": "ISS-.YYYY.-",
+	"Journal Entry": "ACC-JV-.YYYY.-",
+	"Landed Cost Voucher": "MAT-LCV-.YYYY.-",
+	"Lead": "CRM-LEAD-.YYYY.-",
+	"Leave Allocation": "HR-LAL-.YYYY.-",
+	"Leave Application": "HR-LAP-.YYYY.-",
+	"Maintenance Schedule": "MAT-MSH-.YYYY.-",
+	"Maintenance Visit": "MAT-MVS-.YYYY.-",
+	"Material Request": "MAT-MR-.YYYY.-",
+	"Member": "NPO-MEM-.YYYY.-",
+	"Opportunity": "CRM-OPP-.YYYY.-",
+	"Packing Slip": "MAT-PAC-.YYYY.-",
+	"Payment Entry": "ACC-PAY-.YYYY.-",
+	"Payment Request": "ACC-PRQ-.YYYY.-",
+	"Production Plan": "MFG-PP-.YYYY.-",
+	"Project Update": "PROJ-UPD-.YYYY.-",
+	"Purchase Invoice": "ACC-PINV-.YYYY.-",
+	"Purchase Order": "PUR-ORD-.YYYY.-",
+	"Purchase Receipt": "MAT-PRE-.YYYY.-",
+	"Quality Inspection": "MAT-QA-.YYYY.-",
+	"Quotation": "SAL-QTN-.YYYY.-",
+	"Request for Quotation": "PUR-RFQ-.YYYY.-",
+	"Sales Invoice": "ACC-SINV-.YYYY.-",
+	"Sales Order": "SAL-ORD-.YYYY.-",
+	"Sample Collection": "HLC-SC-.YYYY.-",
+	"Shareholder": "ACC-SH-.YYYY.-",
+	"Stock Entry": "MAT-STE-.YYYY.-",
+	"Stock Reconciliation": "MAT-RECO-.YYYY.-",
+	"Student": "EDU-STU-.YYYY.-",
+	"Student Applicant": "EDU-APP-.YYYY.-",
+	"Supplier": "SUP-.YYYY.-",
+	"Supplier Quotation": "PUR-SQTN-.YYYY.-",
+	"Supplier Scorecard Period": "PU-SSP-.YYYY.-",
+	"Timesheet": "TS-.YYYY.-",
+	"Vehicle Log": "HR-VLOG-.YYYY.-",
+	"Warranty Claim": "SER-WRN-.YYYY.-",
+	"Work Order": "MFG-WO-.YYYY.-",
 }
 
+
 def execute():
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		update `tabProperty Setter`
 		set name=concat(doc_type, '-', field_name, '-', property)
 		where property='fetch_from'
-	""")
+	"""
+	)
 	series_to_set = get_series()
 	for doctype, opts in series_to_set.items():
 		set_series(doctype, opts["options"], opts["default"])
 
+
 def set_series(doctype, options, default):
 	def _make_property_setter(property_name, value):
-		property_setter = frappe.db.exists('Property Setter',
-			{'doc_type': doctype, 'field_name': 'naming_series', 'property': property_name})
+		property_setter = frappe.db.exists(
+			"Property Setter",
+			{"doc_type": doctype, "field_name": "naming_series", "property": property_name},
+		)
 		if property_setter:
-			frappe.db.set_value('Property Setter', property_setter, 'value', value)
+			frappe.db.set_value("Property Setter", property_setter, "value", value)
 		else:
 			make_property_setter(doctype, "naming_series", "options", value, "Text")
 
@@ -88,17 +94,18 @@
 	if default:
 		_make_property_setter("default", default)
 
+
 def get_series():
 	series_to_set = {}
 
 	for doctype in doctype_series_map:
-		if not frappe.db.exists('DocType', doctype):
+		if not frappe.db.exists("DocType", doctype):
 			continue
 		if not frappe.db.a_row_exists(doctype):
 			continue
-		if not frappe.db.has_column(doctype, 'naming_series'):
+		if not frappe.db.has_column(doctype, "naming_series"):
 			continue
-		if not frappe.get_meta(doctype).has_field('naming_series'):
+		if not frappe.get_meta(doctype).has_field("naming_series"):
 			continue
 		series_to_preserve = list(filter(None, get_series_to_preserve(doctype)))
 		default_series = get_default_series(doctype)
@@ -116,12 +123,18 @@
 
 	return series_to_set
 
+
 def get_series_to_preserve(doctype):
-	series_to_preserve = frappe.db.sql_list("""select distinct naming_series from `tab{doctype}` where ifnull(naming_series, '') != ''""".format(doctype=doctype))
+	series_to_preserve = frappe.db.sql_list(
+		"""select distinct naming_series from `tab{doctype}` where ifnull(naming_series, '') != ''""".format(
+			doctype=doctype
+		)
+	)
 	series_to_preserve.sort()
 	return series_to_preserve
 
+
 def get_default_series(doctype):
 	field = frappe.get_meta(doctype).get_field("naming_series")
-	default_series = field.get('default', '') if field else ''
+	default_series = field.get("default", "") if field else ""
 	return default_series
diff --git a/erpnext/patches/v11_0/remove_barcodes_field_from_copy_fields_to_variants.py b/erpnext/patches/v11_0/remove_barcodes_field_from_copy_fields_to_variants.py
index caf74f5..2e0204c 100644
--- a/erpnext/patches/v11_0/remove_barcodes_field_from_copy_fields_to_variants.py
+++ b/erpnext/patches/v11_0/remove_barcodes_field_from_copy_fields_to_variants.py
@@ -2,7 +2,7 @@
 
 
 def execute():
-	'''Remove barcodes field from "Copy Fields to Variants" table because barcodes must be unique'''
+	"""Remove barcodes field from "Copy Fields to Variants" table because barcodes must be unique"""
 
-	settings = frappe.get_doc('Item Variant Settings')
+	settings = frappe.get_doc("Item Variant Settings")
 	settings.remove_invalid_fields_for_copy_fields_in_variants()
diff --git a/erpnext/patches/v11_0/rename_additional_salary_component_additional_salary.py b/erpnext/patches/v11_0/rename_additional_salary_component_additional_salary.py
index 8fa876d..036ae8e 100644
--- a/erpnext/patches/v11_0/rename_additional_salary_component_additional_salary.py
+++ b/erpnext/patches/v11_0/rename_additional_salary_component_additional_salary.py
@@ -2,9 +2,10 @@
 
 # this patch should have been included with this PR https://github.com/frappe/erpnext/pull/14302
 
+
 def execute():
 	if frappe.db.table_exists("Additional Salary Component"):
 		if not frappe.db.table_exists("Additional Salary"):
 			frappe.rename_doc("DocType", "Additional Salary Component", "Additional Salary")
 
-		frappe.delete_doc('DocType', "Additional Salary Component")
+		frappe.delete_doc("DocType", "Additional Salary Component")
diff --git a/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py b/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py
index c7a3aa2..c444c16 100644
--- a/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py
+++ b/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py
@@ -6,6 +6,8 @@
 
 
 def execute():
-	if frappe.db.table_exists("Asset Adjustment") and not frappe.db.table_exists("Asset Value Adjustment"):
-		frappe.rename_doc('DocType', 'Asset Adjustment', 'Asset Value Adjustment', force=True)
-		frappe.reload_doc('assets', 'doctype', 'asset_value_adjustment')
+	if frappe.db.table_exists("Asset Adjustment") and not frappe.db.table_exists(
+		"Asset Value Adjustment"
+	):
+		frappe.rename_doc("DocType", "Asset Adjustment", "Asset Value Adjustment", force=True)
+		frappe.reload_doc("assets", "doctype", "asset_value_adjustment")
diff --git a/erpnext/patches/v11_0/rename_bom_wo_fields.py b/erpnext/patches/v11_0/rename_bom_wo_fields.py
index cab7d0a..fb25eeb 100644
--- a/erpnext/patches/v11_0/rename_bom_wo_fields.py
+++ b/erpnext/patches/v11_0/rename_bom_wo_fields.py
@@ -7,28 +7,36 @@
 
 
 def execute():
-    # updating column value to handle field change from Data to Currency
-    changed_field = "base_scrap_material_cost"
-    frappe.db.sql(f"update `tabBOM` set {changed_field} = '0' where trim(coalesce({changed_field}, ''))= ''")
+	# updating column value to handle field change from Data to Currency
+	changed_field = "base_scrap_material_cost"
+	frappe.db.sql(
+		f"update `tabBOM` set {changed_field} = '0' where trim(coalesce({changed_field}, ''))= ''"
+	)
 
-    for doctype in ['BOM Explosion Item', 'BOM Item', 'Work Order Item', 'Item']:
-        if frappe.db.has_column(doctype, 'allow_transfer_for_manufacture'):
-            if doctype != 'Item':
-                frappe.reload_doc('manufacturing', 'doctype', frappe.scrub(doctype))
-            else:
-                frappe.reload_doc('stock', 'doctype', frappe.scrub(doctype))
+	for doctype in ["BOM Explosion Item", "BOM Item", "Work Order Item", "Item"]:
+		if frappe.db.has_column(doctype, "allow_transfer_for_manufacture"):
+			if doctype != "Item":
+				frappe.reload_doc("manufacturing", "doctype", frappe.scrub(doctype))
+			else:
+				frappe.reload_doc("stock", "doctype", frappe.scrub(doctype))
 
-            rename_field(doctype, "allow_transfer_for_manufacture", "include_item_in_manufacturing")
+			rename_field(doctype, "allow_transfer_for_manufacture", "include_item_in_manufacturing")
 
-    for doctype in ['BOM', 'Work Order']:
-        frappe.reload_doc('manufacturing', 'doctype', frappe.scrub(doctype))
+	for doctype in ["BOM", "Work Order"]:
+		frappe.reload_doc("manufacturing", "doctype", frappe.scrub(doctype))
 
-        if frappe.db.has_column(doctype, 'transfer_material_against_job_card'):
-            frappe.db.sql(""" UPDATE `tab%s`
+		if frappe.db.has_column(doctype, "transfer_material_against_job_card"):
+			frappe.db.sql(
+				""" UPDATE `tab%s`
                 SET transfer_material_against = CASE WHEN
                     transfer_material_against_job_card = 1 then 'Job Card' Else 'Work Order' END
-                WHERE docstatus < 2""" % (doctype))
-        else:
-            frappe.db.sql(""" UPDATE `tab%s`
+                WHERE docstatus < 2"""
+				% (doctype)
+			)
+		else:
+			frappe.db.sql(
+				""" UPDATE `tab%s`
                 SET transfer_material_against = 'Work Order'
-                WHERE docstatus < 2""" % (doctype))
+                WHERE docstatus < 2"""
+				% (doctype)
+			)
diff --git a/erpnext/patches/v11_0/rename_duplicate_item_code_values.py b/erpnext/patches/v11_0/rename_duplicate_item_code_values.py
index 61f3856..1f65e14 100644
--- a/erpnext/patches/v11_0/rename_duplicate_item_code_values.py
+++ b/erpnext/patches/v11_0/rename_duplicate_item_code_values.py
@@ -3,7 +3,9 @@
 
 def execute():
 	items = []
-	items = frappe.db.sql("""select item_code from `tabItem` group by item_code having count(*) > 1""", as_dict=True)
+	items = frappe.db.sql(
+		"""select item_code from `tabItem` group by item_code having count(*) > 1""", as_dict=True
+	)
 	if items:
 		for item in items:
 			frappe.db.sql("""update `tabItem` set item_code=name where item_code = %s""", (item.item_code))
diff --git a/erpnext/patches/v11_0/rename_field_max_days_allowed.py b/erpnext/patches/v11_0/rename_field_max_days_allowed.py
index 4b55aa0..0813770 100644
--- a/erpnext/patches/v11_0/rename_field_max_days_allowed.py
+++ b/erpnext/patches/v11_0/rename_field_max_days_allowed.py
@@ -3,11 +3,13 @@
 
 
 def execute():
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		UPDATE `tabLeave Type`
 		SET max_days_allowed = '0'
 		WHERE trim(coalesce(max_days_allowed, '')) = ''
-	""")
+	"""
+	)
 	frappe.db.sql_ddl("""ALTER table `tabLeave Type` modify max_days_allowed int(8) NOT NULL""")
 	frappe.reload_doc("hr", "doctype", "leave_type")
 	rename_field("Leave Type", "max_days_allowed", "max_continuous_days_allowed")
diff --git a/erpnext/patches/v11_0/rename_members_with_naming_series.py b/erpnext/patches/v11_0/rename_members_with_naming_series.py
index 95fb55d..4dffbc8 100644
--- a/erpnext/patches/v11_0/rename_members_with_naming_series.py
+++ b/erpnext/patches/v11_0/rename_members_with_naming_series.py
@@ -3,8 +3,8 @@
 
 def execute():
 	frappe.reload_doc("non_profit", "doctype", "member")
-	old_named_members = frappe.get_all("Member", filters = {"name": ("not like", "MEM-%")})
-	correctly_named_members = frappe.get_all("Member", filters = {"name": ("like", "MEM-%")})
+	old_named_members = frappe.get_all("Member", filters={"name": ("not like", "MEM-%")})
+	correctly_named_members = frappe.get_all("Member", filters={"name": ("like", "MEM-%")})
 	current_index = len(correctly_named_members)
 
 	for member in old_named_members:
diff --git a/erpnext/patches/v11_0/rename_overproduction_percent_field.py b/erpnext/patches/v11_0/rename_overproduction_percent_field.py
index c78ec5d..74699db 100644
--- a/erpnext/patches/v11_0/rename_overproduction_percent_field.py
+++ b/erpnext/patches/v11_0/rename_overproduction_percent_field.py
@@ -7,5 +7,9 @@
 
 
 def execute():
-	frappe.reload_doc('manufacturing', 'doctype', 'manufacturing_settings')
-	rename_field('Manufacturing Settings', 'over_production_allowance_percentage', 'overproduction_percentage_for_sales_order')
+	frappe.reload_doc("manufacturing", "doctype", "manufacturing_settings")
+	rename_field(
+		"Manufacturing Settings",
+		"over_production_allowance_percentage",
+		"overproduction_percentage_for_sales_order",
+	)
diff --git a/erpnext/patches/v11_0/rename_production_order_to_work_order.py b/erpnext/patches/v11_0/rename_production_order_to_work_order.py
index 453a571..b58ac4e 100644
--- a/erpnext/patches/v11_0/rename_production_order_to_work_order.py
+++ b/erpnext/patches/v11_0/rename_production_order_to_work_order.py
@@ -7,22 +7,28 @@
 
 
 def execute():
-	frappe.rename_doc('DocType', 'Production Order', 'Work Order', force=True)
-	frappe.reload_doc('manufacturing', 'doctype', 'work_order')
+	frappe.rename_doc("DocType", "Production Order", "Work Order", force=True)
+	frappe.reload_doc("manufacturing", "doctype", "work_order")
 
-	frappe.rename_doc('DocType', 'Production Order Item', 'Work Order Item', force=True)
-	frappe.reload_doc('manufacturing', 'doctype', 'work_order_item')
+	frappe.rename_doc("DocType", "Production Order Item", "Work Order Item", force=True)
+	frappe.reload_doc("manufacturing", "doctype", "work_order_item")
 
-	frappe.rename_doc('DocType', 'Production Order Operation', 'Work Order Operation', force=True)
-	frappe.reload_doc('manufacturing', 'doctype', 'work_order_operation')
+	frappe.rename_doc("DocType", "Production Order Operation", "Work Order Operation", force=True)
+	frappe.reload_doc("manufacturing", "doctype", "work_order_operation")
 
-	frappe.reload_doc('projects', 'doctype', 'timesheet')
-	frappe.reload_doc('stock', 'doctype', 'stock_entry')
+	frappe.reload_doc("projects", "doctype", "timesheet")
+	frappe.reload_doc("stock", "doctype", "stock_entry")
 	rename_field("Timesheet", "production_order", "work_order")
 	rename_field("Stock Entry", "production_order", "work_order")
 
-	frappe.rename_doc("Report", "Production Orders in Progress", "Work Orders in Progress", force=True)
+	frappe.rename_doc(
+		"Report", "Production Orders in Progress", "Work Orders in Progress", force=True
+	)
 	frappe.rename_doc("Report", "Completed Production Orders", "Completed Work Orders", force=True)
 	frappe.rename_doc("Report", "Open Production Orders", "Open Work Orders", force=True)
-	frappe.rename_doc("Report", "Issued Items Against Production Order", "Issued Items Against Work Order", force=True)
-	frappe.rename_doc("Report", "Production Order Stock Report", "Work Order Stock Report", force=True)
+	frappe.rename_doc(
+		"Report", "Issued Items Against Production Order", "Issued Items Against Work Order", force=True
+	)
+	frappe.rename_doc(
+		"Report", "Production Order Stock Report", "Work Order Stock Report", force=True
+	)
diff --git a/erpnext/patches/v11_0/rename_supplier_type_to_supplier_group.py b/erpnext/patches/v11_0/rename_supplier_type_to_supplier_group.py
index 3f87550..96daba7 100644
--- a/erpnext/patches/v11_0/rename_supplier_type_to_supplier_group.py
+++ b/erpnext/patches/v11_0/rename_supplier_type_to_supplier_group.py
@@ -6,10 +6,10 @@
 
 def execute():
 	if frappe.db.table_exists("Supplier Group"):
-		frappe.reload_doc('setup', 'doctype', 'supplier_group')
+		frappe.reload_doc("setup", "doctype", "supplier_group")
 	elif frappe.db.table_exists("Supplier Type"):
 		frappe.rename_doc("DocType", "Supplier Type", "Supplier Group", force=True)
-		frappe.reload_doc('setup', 'doctype', 'supplier_group')
+		frappe.reload_doc("setup", "doctype", "supplier_group")
 		frappe.reload_doc("accounts", "doctype", "pricing_rule")
 		frappe.reload_doc("accounts", "doctype", "tax_rule")
 		frappe.reload_doc("buying", "doctype", "buying_settings")
@@ -22,16 +22,23 @@
 
 	build_tree()
 
-def build_tree():
-	frappe.db.sql("""update `tabSupplier Group` set parent_supplier_group = '{0}'
-		where is_group = 0""".format(_('All Supplier Groups')))
 
-	if not frappe.db.exists("Supplier Group", _('All Supplier Groups')):
-		frappe.get_doc({
-			'doctype': 'Supplier Group',
-			'supplier_group_name': _('All Supplier Groups'),
-			'is_group': 1,
-			'parent_supplier_group': ''
-		}).insert(ignore_permissions=True)
+def build_tree():
+	frappe.db.sql(
+		"""update `tabSupplier Group` set parent_supplier_group = '{0}'
+		where is_group = 0""".format(
+			_("All Supplier Groups")
+		)
+	)
+
+	if not frappe.db.exists("Supplier Group", _("All Supplier Groups")):
+		frappe.get_doc(
+			{
+				"doctype": "Supplier Group",
+				"supplier_group_name": _("All Supplier Groups"),
+				"is_group": 1,
+				"parent_supplier_group": "",
+			}
+		).insert(ignore_permissions=True)
 
 	rebuild_tree("Supplier Group", "parent_supplier_group")
diff --git a/erpnext/patches/v11_0/renamed_from_to_fields_in_project.py b/erpnext/patches/v11_0/renamed_from_to_fields_in_project.py
index f23a814..4dc2521 100644
--- a/erpnext/patches/v11_0/renamed_from_to_fields_in_project.py
+++ b/erpnext/patches/v11_0/renamed_from_to_fields_in_project.py
@@ -7,8 +7,8 @@
 
 
 def execute():
-    frappe.reload_doc('projects', 'doctype', 'project')
+	frappe.reload_doc("projects", "doctype", "project")
 
-    if frappe.db.has_column('Project', 'from'):
-        rename_field('Project', 'from', 'from_time')
-        rename_field('Project', 'to', 'to_time')
+	if frappe.db.has_column("Project", "from"):
+		rename_field("Project", "from", "from_time")
+		rename_field("Project", "to", "to_time")
diff --git a/erpnext/patches/v11_0/set_department_for_doctypes.py b/erpnext/patches/v11_0/set_department_for_doctypes.py
index b1098ab..4900458 100644
--- a/erpnext/patches/v11_0/set_department_for_doctypes.py
+++ b/erpnext/patches/v11_0/set_department_for_doctypes.py
@@ -2,22 +2,35 @@
 
 # Set department value based on employee value
 
+
 def execute():
 
 	doctypes_to_update = {
-		'hr': ['Appraisal', 'Leave Allocation', 'Expense Claim', 'Salary Slip',
-			'Attendance', 'Training Feedback', 'Training Result Employee','Leave Application',
-			'Employee Advance', 'Training Event Employee', 'Payroll Employee Detail'],
-		'education': ['Instructor'],
-		'projects': ['Activity Cost', 'Timesheet'],
-		'setup': ['Sales Person']
+		"hr": [
+			"Appraisal",
+			"Leave Allocation",
+			"Expense Claim",
+			"Salary Slip",
+			"Attendance",
+			"Training Feedback",
+			"Training Result Employee",
+			"Leave Application",
+			"Employee Advance",
+			"Training Event Employee",
+			"Payroll Employee Detail",
+		],
+		"projects": ["Activity Cost", "Timesheet"],
+		"setup": ["Sales Person"],
 	}
 
 	for module, doctypes in doctypes_to_update.items():
 		for doctype in doctypes:
 			if frappe.db.table_exists(doctype):
-				frappe.reload_doc(module, 'doctype', frappe.scrub(doctype))
-				frappe.db.sql("""
+				frappe.reload_doc(module, "doctype", frappe.scrub(doctype))
+				frappe.db.sql(
+					"""
 					update `tab%s` dt
 					set department=(select department from `tabEmployee` where name=dt.employee)
-				""" % doctype)
+				"""
+					% doctype
+				)
diff --git a/erpnext/patches/v11_0/set_salary_component_properties.py b/erpnext/patches/v11_0/set_salary_component_properties.py
index 99c3b0b..3ec9f8a 100644
--- a/erpnext/patches/v11_0/set_salary_component_properties.py
+++ b/erpnext/patches/v11_0/set_salary_component_properties.py
@@ -2,15 +2,21 @@
 
 
 def execute():
-	frappe.reload_doc('Payroll', 'doctype', 'salary_detail')
-	frappe.reload_doc('Payroll', 'doctype', 'salary_component')
+	frappe.reload_doc("Payroll", "doctype", "salary_detail")
+	frappe.reload_doc("Payroll", "doctype", "salary_component")
 
 	frappe.db.sql("update `tabSalary Component` set is_tax_applicable=1 where type='Earning'")
 
-	frappe.db.sql("""update `tabSalary Component` set variable_based_on_taxable_salary=1
-	    where type='Deduction' and name in ('TDS', 'Tax Deducted at Source')""")
+	frappe.db.sql(
+		"""update `tabSalary Component` set variable_based_on_taxable_salary=1
+	    where type='Deduction' and name in ('TDS', 'Tax Deducted at Source')"""
+	)
 
-	frappe.db.sql("""update `tabSalary Detail` set is_tax_applicable=1
-	    where parentfield='earnings' and statistical_component=0""")
-	frappe.db.sql("""update `tabSalary Detail` set variable_based_on_taxable_salary=1
-	    where parentfield='deductions' and salary_component in ('TDS', 'Tax Deducted at Source')""")
+	frappe.db.sql(
+		"""update `tabSalary Detail` set is_tax_applicable=1
+	    where parentfield='earnings' and statistical_component=0"""
+	)
+	frappe.db.sql(
+		"""update `tabSalary Detail` set variable_based_on_taxable_salary=1
+	    where parentfield='deductions' and salary_component in ('TDS', 'Tax Deducted at Source')"""
+	)
diff --git a/erpnext/patches/v11_0/set_update_field_and_value_in_workflow_state.py b/erpnext/patches/v11_0/set_update_field_and_value_in_workflow_state.py
index a44daaa..548a7cb 100644
--- a/erpnext/patches/v11_0/set_update_field_and_value_in_workflow_state.py
+++ b/erpnext/patches/v11_0/set_update_field_and_value_in_workflow_state.py
@@ -3,17 +3,19 @@
 
 
 def execute():
-	for doctype in ['Expense Claim', 'Leave Application']:
+	for doctype in ["Expense Claim", "Leave Application"]:
 
 		active_workflow = get_workflow_name(doctype)
-		if not active_workflow: continue
+		if not active_workflow:
+			continue
 
-		workflow_states = frappe.get_all('Workflow Document State',
-			filters=[['parent', '=', active_workflow]],
-			fields=['*'])
+		workflow_states = frappe.get_all(
+			"Workflow Document State", filters=[["parent", "=", active_workflow]], fields=["*"]
+		)
 
 		for state in workflow_states:
-			if state.update_field: continue
-			status_field = 'approval_status' if doctype=="Expense Claim" else 'status'
-			frappe.set_value('Workflow Document State', state.name, 'update_field', status_field)
-			frappe.set_value('Workflow Document State', state.name, 'update_value', state.state)
+			if state.update_field:
+				continue
+			status_field = "approval_status" if doctype == "Expense Claim" else "status"
+			frappe.set_value("Workflow Document State", state.name, "update_field", status_field)
+			frappe.set_value("Workflow Document State", state.name, "update_value", state.state)
diff --git a/erpnext/patches/v11_0/set_user_permissions_for_department.py b/erpnext/patches/v11_0/set_user_permissions_for_department.py
index bb7ef87..9b5cb24 100644
--- a/erpnext/patches/v11_0/set_user_permissions_for_department.py
+++ b/erpnext/patches/v11_0/set_user_permissions_for_department.py
@@ -2,18 +2,24 @@
 
 
 def execute():
-    user_permissions = frappe.db.sql("""select name, for_value from `tabUser Permission`
-        where allow='Department'""", as_dict=1)
-    for d in user_permissions:
-        user_permission = frappe.get_doc("User Permission", d.name)
-        for new_dept in frappe.db.sql("""select name from tabDepartment
-            where ifnull(company, '') != '' and department_name=%s""", d.for_value):
-            try:
-                new_user_permission = frappe.copy_doc(user_permission)
-                new_user_permission.for_value = new_dept[0]
-                new_user_permission.save()
-            except frappe.DuplicateEntryError:
-                pass
+	user_permissions = frappe.db.sql(
+		"""select name, for_value from `tabUser Permission`
+        where allow='Department'""",
+		as_dict=1,
+	)
+	for d in user_permissions:
+		user_permission = frappe.get_doc("User Permission", d.name)
+		for new_dept in frappe.db.sql(
+			"""select name from tabDepartment
+            where ifnull(company, '') != '' and department_name=%s""",
+			d.for_value,
+		):
+			try:
+				new_user_permission = frappe.copy_doc(user_permission)
+				new_user_permission.for_value = new_dept[0]
+				new_user_permission.save()
+			except frappe.DuplicateEntryError:
+				pass
 
-    frappe.reload_doc("hr", "doctype", "department")
-    frappe.db.sql("update tabDepartment set disabled=1 where ifnull(company, '') = ''")
+	frappe.reload_doc("hr", "doctype", "department")
+	frappe.db.sql("update tabDepartment set disabled=1 where ifnull(company, '') = ''")
diff --git a/erpnext/patches/v11_0/skip_user_permission_check_for_department.py b/erpnext/patches/v11_0/skip_user_permission_check_for_department.py
index d387577..1327da9 100644
--- a/erpnext/patches/v11_0/skip_user_permission_check_for_department.py
+++ b/erpnext/patches/v11_0/skip_user_permission_check_for_department.py
@@ -4,20 +4,37 @@
 # Skips user permission check for doctypes where department link field was recently added
 # https://github.com/frappe/erpnext/pull/14121
 
+
 def execute():
 	doctypes_to_skip = []
-	for doctype in ['Appraisal', 'Leave Allocation', 'Expense Claim', 'Instructor', 'Salary Slip',
-					'Attendance', 'Training Feedback', 'Training Result Employee',
-					'Leave Application', 'Employee Advance', 'Activity Cost', 'Training Event Employee',
-					'Timesheet', 'Sales Person', 'Payroll Employee Detail']:
-		if frappe.db.exists('Custom Field', { 'dt': doctype, 'fieldname': 'department'}): continue
+	for doctype in [
+		"Appraisal",
+		"Leave Allocation",
+		"Expense Claim",
+		"Instructor",
+		"Salary Slip",
+		"Attendance",
+		"Training Feedback",
+		"Training Result Employee",
+		"Leave Application",
+		"Employee Advance",
+		"Activity Cost",
+		"Training Event Employee",
+		"Timesheet",
+		"Sales Person",
+		"Payroll Employee Detail",
+	]:
+		if frappe.db.exists("Custom Field", {"dt": doctype, "fieldname": "department"}):
+			continue
 		doctypes_to_skip.append(doctype)
 
-	frappe.reload_doctype('User Permission')
+	frappe.reload_doctype("User Permission")
 
-	user_permissions = frappe.get_all("User Permission",
-		filters=[['allow', '=', 'Department'], ['applicable_for', 'in', [None] + doctypes_to_skip]],
-		fields=['name', 'applicable_for'])
+	user_permissions = frappe.get_all(
+		"User Permission",
+		filters=[["allow", "=", "Department"], ["applicable_for", "in", [None] + doctypes_to_skip]],
+		fields=["name", "applicable_for"],
+	)
 
 	user_permissions_to_delete = []
 	new_user_permissions_list = []
@@ -37,24 +54,32 @@
 			for doctype in applicable_for_doctypes:
 				if doctype:
 					# Maintain sequence (name, user, allow, for_value, applicable_for, apply_to_all_doctypes)
-					new_user_permissions_list.append((
-						frappe.generate_hash("", 10),
-						user_permission.user,
-						user_permission.allow,
-						user_permission.for_value,
-						doctype,
-						0
-					))
+					new_user_permissions_list.append(
+						(
+							frappe.generate_hash("", 10),
+							user_permission.user,
+							user_permission.allow,
+							user_permission.for_value,
+							doctype,
+							0,
+						)
+					)
 
 	if new_user_permissions_list:
-		frappe.db.sql('''
+		frappe.db.sql(
+			"""
 			INSERT INTO `tabUser Permission`
 			(`name`, `user`, `allow`, `for_value`, `applicable_for`, `apply_to_all_doctypes`)
-			VALUES {}'''.format(', '.join(['%s'] * len(new_user_permissions_list))), # nosec
-			tuple(new_user_permissions_list)
+			VALUES {}""".format(
+				", ".join(["%s"] * len(new_user_permissions_list))
+			),  # nosec
+			tuple(new_user_permissions_list),
 		)
 
 	if user_permissions_to_delete:
-		frappe.db.sql('DELETE FROM `tabUser Permission` WHERE `name` IN ({})'.format( # nosec
-			','.join(['%s'] * len(user_permissions_to_delete))
-		), tuple(user_permissions_to_delete))
+		frappe.db.sql(
+			"DELETE FROM `tabUser Permission` WHERE `name` IN ({})".format(  # nosec
+				",".join(["%s"] * len(user_permissions_to_delete))
+			),
+			tuple(user_permissions_to_delete),
+		)
diff --git a/erpnext/patches/v11_0/uom_conversion_data.py b/erpnext/patches/v11_0/uom_conversion_data.py
index 81e547b..5dee084 100644
--- a/erpnext/patches/v11_0/uom_conversion_data.py
+++ b/erpnext/patches/v11_0/uom_conversion_data.py
@@ -14,8 +14,8 @@
 		# delete conversion data and insert again
 		frappe.db.sql("delete from `tabUOM Conversion Factor`")
 		try:
-			frappe.delete_doc('UOM', 'Hundredweight')
-			frappe.delete_doc('UOM', 'Pound Cubic Yard')
+			frappe.delete_doc("UOM", "Hundredweight")
+			frappe.delete_doc("UOM", "Pound Cubic Yard")
 		except frappe.LinkExistsError:
 			pass
 
diff --git a/erpnext/patches/v11_0/update_account_type_in_party_type.py b/erpnext/patches/v11_0/update_account_type_in_party_type.py
index c66cef0..47252eb 100644
--- a/erpnext/patches/v11_0/update_account_type_in_party_type.py
+++ b/erpnext/patches/v11_0/update_account_type_in_party_type.py
@@ -6,9 +6,14 @@
 
 
 def execute():
-	frappe.reload_doc('setup', 'doctype', 'party_type')
-	party_types = {'Customer': 'Receivable', 'Supplier': 'Payable',
-		'Employee': 'Payable', 'Member': 'Receivable', 'Shareholder': 'Payable', 'Student': 'Receivable'}
+	frappe.reload_doc("setup", "doctype", "party_type")
+	party_types = {
+		"Customer": "Receivable",
+		"Supplier": "Payable",
+		"Employee": "Payable",
+		"Member": "Receivable",
+		"Shareholder": "Payable",
+	}
 
 	for party_type, account_type in party_types.items():
-		frappe.db.set_value('Party Type', party_type, 'account_type', account_type)
+		frappe.db.set_value("Party Type", party_type, "account_type", account_type)
diff --git a/erpnext/patches/v11_0/update_allow_transfer_for_manufacture.py b/erpnext/patches/v11_0/update_allow_transfer_for_manufacture.py
index 3e36a4b..a7351d2 100644
--- a/erpnext/patches/v11_0/update_allow_transfer_for_manufacture.py
+++ b/erpnext/patches/v11_0/update_allow_transfer_for_manufacture.py
@@ -6,16 +6,22 @@
 
 
 def execute():
-	frappe.reload_doc('stock', 'doctype', 'item')
-	frappe.db.sql(""" update `tabItem` set include_item_in_manufacturing = 1
-		where ifnull(is_stock_item, 0) = 1""")
+	frappe.reload_doc("stock", "doctype", "item")
+	frappe.db.sql(
+		""" update `tabItem` set include_item_in_manufacturing = 1
+		where ifnull(is_stock_item, 0) = 1"""
+	)
 
-	for doctype in ['BOM Item', 'Work Order Item', 'BOM Explosion Item']:
-		frappe.reload_doc('manufacturing', 'doctype', frappe.scrub(doctype))
+	for doctype in ["BOM Item", "Work Order Item", "BOM Explosion Item"]:
+		frappe.reload_doc("manufacturing", "doctype", frappe.scrub(doctype))
 
-		frappe.db.sql(""" update `tab{0}` child, tabItem item
+		frappe.db.sql(
+			""" update `tab{0}` child, tabItem item
 			set
 				child.include_item_in_manufacturing = 1
 			where
 				child.item_code = item.name and ifnull(item.is_stock_item, 0) = 1
-		""".format(doctype))
+		""".format(
+				doctype
+			)
+		)
diff --git a/erpnext/patches/v11_0/update_backflush_subcontract_rm_based_on_bom.py b/erpnext/patches/v11_0/update_backflush_subcontract_rm_based_on_bom.py
index f3a2ac6..51ba706 100644
--- a/erpnext/patches/v11_0/update_backflush_subcontract_rm_based_on_bom.py
+++ b/erpnext/patches/v11_0/update_backflush_subcontract_rm_based_on_bom.py
@@ -6,15 +6,19 @@
 
 
 def execute():
-	frappe.reload_doc('buying', 'doctype', 'buying_settings')
-	frappe.db.set_value('Buying Settings', None, 'backflush_raw_materials_of_subcontract_based_on', 'BOM')
+	frappe.reload_doc("buying", "doctype", "buying_settings")
+	frappe.db.set_value(
+		"Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", "BOM"
+	)
 
-	frappe.reload_doc('stock', 'doctype', 'stock_entry_detail')
-	frappe.db.sql(""" update `tabStock Entry Detail` as sed,
+	frappe.reload_doc("stock", "doctype", "stock_entry_detail")
+	frappe.db.sql(
+		""" update `tabStock Entry Detail` as sed,
 		`tabStock Entry` as se, `tabPurchase Order Item Supplied` as pois
 		set
 			sed.subcontracted_item = pois.main_item_code
 		where
 			se.purpose = 'Send to Subcontractor' and sed.parent = se.name
 			and pois.rm_item_code = sed.item_code and se.docstatus = 1
-			and pois.parenttype = 'Purchase Order'""")
+			and pois.parenttype = 'Purchase Order'"""
+	)
diff --git a/erpnext/patches/v11_0/update_brand_in_item_price.py b/erpnext/patches/v11_0/update_brand_in_item_price.py
index ce1df78..f4859ae 100644
--- a/erpnext/patches/v11_0/update_brand_in_item_price.py
+++ b/erpnext/patches/v11_0/update_brand_in_item_price.py
@@ -6,11 +6,13 @@
 
 
 def execute():
-	frappe.reload_doc('stock', 'doctype', 'item_price')
+	frappe.reload_doc("stock", "doctype", "item_price")
 
-	frappe.db.sql(""" update `tabItem Price`, `tabItem`
+	frappe.db.sql(
+		""" update `tabItem Price`, `tabItem`
 		set
 			`tabItem Price`.brand = `tabItem`.brand
 		where
 			`tabItem Price`.item_code = `tabItem`.name
-			and `tabItem`.brand is not null and `tabItem`.brand != ''""")
+			and `tabItem`.brand is not null and `tabItem`.brand != ''"""
+	)
diff --git a/erpnext/patches/v11_0/update_delivery_trip_status.py b/erpnext/patches/v11_0/update_delivery_trip_status.py
index 35b9535..1badfab 100755
--- a/erpnext/patches/v11_0/update_delivery_trip_status.py
+++ b/erpnext/patches/v11_0/update_delivery_trip_status.py
@@ -6,18 +6,14 @@
 
 
 def execute():
-	frappe.reload_doc('setup', 'doctype', 'global_defaults', force=True)
-	frappe.reload_doc('stock', 'doctype', 'delivery_trip')
-	frappe.reload_doc('stock', 'doctype', 'delivery_stop', force=True)
+	frappe.reload_doc("setup", "doctype", "global_defaults", force=True)
+	frappe.reload_doc("stock", "doctype", "delivery_trip")
+	frappe.reload_doc("stock", "doctype", "delivery_stop", force=True)
 
 	for trip in frappe.get_all("Delivery Trip"):
 		trip_doc = frappe.get_doc("Delivery Trip", trip.name)
 
-		status = {
-			0: "Draft",
-			1: "Scheduled",
-			2: "Cancelled"
-		}[trip_doc.docstatus]
+		status = {0: "Draft", 1: "Scheduled", 2: "Cancelled"}[trip_doc.docstatus]
 
 		if trip_doc.docstatus == 1:
 			visited_stops = [stop.visited for stop in trip_doc.delivery_stops]
diff --git a/erpnext/patches/v11_0/update_department_lft_rgt.py b/erpnext/patches/v11_0/update_department_lft_rgt.py
index aff8e15..bca5e9e 100644
--- a/erpnext/patches/v11_0/update_department_lft_rgt.py
+++ b/erpnext/patches/v11_0/update_department_lft_rgt.py
@@ -4,16 +4,18 @@
 
 
 def execute():
-	""" assign lft and rgt appropriately """
+	"""assign lft and rgt appropriately"""
 	frappe.reload_doc("hr", "doctype", "department")
-	if not frappe.db.exists("Department", _('All Departments')):
-		frappe.get_doc({
-			'doctype': 'Department',
-			'department_name': _('All Departments'),
-			'is_group': 1
-		}).insert(ignore_permissions=True, ignore_mandatory=True)
+	if not frappe.db.exists("Department", _("All Departments")):
+		frappe.get_doc(
+			{"doctype": "Department", "department_name": _("All Departments"), "is_group": 1}
+		).insert(ignore_permissions=True, ignore_mandatory=True)
 
-	frappe.db.sql("""update `tabDepartment` set parent_department = '{0}'
-		where is_group = 0""".format(_('All Departments')))
+	frappe.db.sql(
+		"""update `tabDepartment` set parent_department = '{0}'
+		where is_group = 0""".format(
+			_("All Departments")
+		)
+	)
 
 	rebuild_tree("Department", "parent_department")
diff --git a/erpnext/patches/v11_0/update_sales_partner_type.py b/erpnext/patches/v11_0/update_sales_partner_type.py
index ef58499f..2d37fd6 100644
--- a/erpnext/patches/v11_0/update_sales_partner_type.py
+++ b/erpnext/patches/v11_0/update_sales_partner_type.py
@@ -5,25 +5,28 @@
 def execute():
 	from erpnext.setup.setup_wizard.operations.install_fixtures import default_sales_partner_type
 
-	frappe.reload_doc('selling', 'doctype', 'sales_partner_type')
+	frappe.reload_doc("selling", "doctype", "sales_partner_type")
 
-	frappe.local.lang = frappe.db.get_default("lang") or 'en'
+	frappe.local.lang = frappe.db.get_default("lang") or "en"
 
 	for s in default_sales_partner_type:
 		insert_sales_partner_type(_(s))
 
 	# get partner type in existing forms (customized)
 	# and create a document if not created
-	for d in ['Sales Partner']:
-		partner_type = frappe.db.sql_list('select distinct partner_type from `tab{0}`'.format(d))
+	for d in ["Sales Partner"]:
+		partner_type = frappe.db.sql_list("select distinct partner_type from `tab{0}`".format(d))
 		for s in partner_type:
 			if s and s not in default_sales_partner_type:
 				insert_sales_partner_type(s)
 
 		# remove customization for partner type
-		for p in frappe.get_all('Property Setter', {'doc_type':d, 'field_name':'partner_type', 'property':'options'}):
-			frappe.delete_doc('Property Setter', p.name)
+		for p in frappe.get_all(
+			"Property Setter", {"doc_type": d, "field_name": "partner_type", "property": "options"}
+		):
+			frappe.delete_doc("Property Setter", p.name)
+
 
 def insert_sales_partner_type(s):
-	if not frappe.db.exists('Sales Partner Type', s):
-		frappe.get_doc(dict(doctype='Sales Partner Type', sales_partner_type=s)).insert()
+	if not frappe.db.exists("Sales Partner Type", s):
+		frappe.get_doc(dict(doctype="Sales Partner Type", sales_partner_type=s)).insert()
diff --git a/erpnext/patches/v11_0/update_total_qty_field.py b/erpnext/patches/v11_0/update_total_qty_field.py
index 4e807b4..09fcdb8 100644
--- a/erpnext/patches/v11_0/update_total_qty_field.py
+++ b/erpnext/patches/v11_0/update_total_qty_field.py
@@ -2,33 +2,46 @@
 
 
 def execute():
-	frappe.reload_doc('buying', 'doctype', 'purchase_order')
-	frappe.reload_doc('buying', 'doctype', 'supplier_quotation')
-	frappe.reload_doc('selling', 'doctype', 'sales_order')
-	frappe.reload_doc('selling', 'doctype', 'quotation')
-	frappe.reload_doc('stock', 'doctype', 'delivery_note')
-	frappe.reload_doc('stock', 'doctype', 'purchase_receipt')
-	frappe.reload_doc('accounts', 'doctype', 'sales_invoice')
-	frappe.reload_doc('accounts', 'doctype', 'purchase_invoice')
+	frappe.reload_doc("buying", "doctype", "purchase_order")
+	frappe.reload_doc("buying", "doctype", "supplier_quotation")
+	frappe.reload_doc("selling", "doctype", "sales_order")
+	frappe.reload_doc("selling", "doctype", "quotation")
+	frappe.reload_doc("stock", "doctype", "delivery_note")
+	frappe.reload_doc("stock", "doctype", "purchase_receipt")
+	frappe.reload_doc("accounts", "doctype", "sales_invoice")
+	frappe.reload_doc("accounts", "doctype", "purchase_invoice")
 
-	doctypes = ["Sales Order", "Sales Invoice", "Delivery Note",\
-		"Purchase Order", "Purchase Invoice", "Purchase Receipt", "Quotation", "Supplier Quotation"]
+	doctypes = [
+		"Sales Order",
+		"Sales Invoice",
+		"Delivery Note",
+		"Purchase Order",
+		"Purchase Invoice",
+		"Purchase Receipt",
+		"Quotation",
+		"Supplier Quotation",
+	]
 
 	for doctype in doctypes:
-		total_qty = frappe.db.sql('''
+		total_qty = frappe.db.sql(
+			"""
 			SELECT
 				parent, SUM(qty) as qty
 			FROM
 				`tab{0} Item`
 			where parenttype = '{0}'
 			GROUP BY parent
-		'''.format(doctype), as_dict = True)
+		""".format(
+				doctype
+			),
+			as_dict=True,
+		)
 
 		# Query to update total_qty might become too big, Update in batches
 		# batch_size is chosen arbitrarily, Don't try too hard to reason about it
 		batch_size = 100000
 		for i in range(0, len(total_qty), batch_size):
-			batch_transactions = total_qty[i:i + batch_size]
+			batch_transactions = total_qty[i : i + batch_size]
 
 			# UPDATE with CASE for some reason cannot use PRIMARY INDEX,
 			# causing all rows to be examined, leading to a very slow update
@@ -42,7 +55,11 @@
 			for d in batch_transactions:
 				values.append("({0}, {1})".format(frappe.db.escape(d.parent), d.qty))
 			conditions = ",".join(values)
-			frappe.db.sql("""
+			frappe.db.sql(
+				"""
 				INSERT INTO `tab{}` (name, total_qty) VALUES {}
 				ON DUPLICATE KEY UPDATE name = VALUES(name), total_qty = VALUES(total_qty)
-			""".format(doctype, conditions))
+			""".format(
+					doctype, conditions
+				)
+			)
diff --git a/erpnext/patches/v11_1/delete_bom_browser.py b/erpnext/patches/v11_1/delete_bom_browser.py
index 9b5c169..09ee169 100644
--- a/erpnext/patches/v11_1/delete_bom_browser.py
+++ b/erpnext/patches/v11_1/delete_bom_browser.py
@@ -6,4 +6,4 @@
 
 
 def execute():
-    frappe.delete_doc_if_exists('Page', 'bom-browser')
+	frappe.delete_doc_if_exists("Page", "bom-browser")
diff --git a/erpnext/patches/v11_1/make_job_card_time_logs.py b/erpnext/patches/v11_1/make_job_card_time_logs.py
index 100cd64..beb2c4e 100644
--- a/erpnext/patches/v11_1/make_job_card_time_logs.py
+++ b/erpnext/patches/v11_1/make_job_card_time_logs.py
@@ -6,25 +6,45 @@
 
 
 def execute():
-    frappe.reload_doc('manufacturing', 'doctype', 'job_card_time_log')
+	frappe.reload_doc("manufacturing", "doctype", "job_card_time_log")
 
-    if (frappe.db.table_exists("Job Card")
-        and frappe.get_meta("Job Card").has_field("actual_start_date")):
-        time_logs = []
-        for d in frappe.get_all('Job Card',
-            fields = ["actual_start_date", "actual_end_date", "time_in_mins", "name", "for_quantity"],
-            filters = {'docstatus': ("<", 2)}):
-            if d.actual_start_date:
-                time_logs.append([d.actual_start_date, d.actual_end_date, d.time_in_mins,
-                    d.for_quantity, d.name, 'Job Card', 'time_logs', frappe.generate_hash("", 10)])
+	if frappe.db.table_exists("Job Card") and frappe.get_meta("Job Card").has_field(
+		"actual_start_date"
+	):
+		time_logs = []
+		for d in frappe.get_all(
+			"Job Card",
+			fields=["actual_start_date", "actual_end_date", "time_in_mins", "name", "for_quantity"],
+			filters={"docstatus": ("<", 2)},
+		):
+			if d.actual_start_date:
+				time_logs.append(
+					[
+						d.actual_start_date,
+						d.actual_end_date,
+						d.time_in_mins,
+						d.for_quantity,
+						d.name,
+						"Job Card",
+						"time_logs",
+						frappe.generate_hash("", 10),
+					]
+				)
 
-        if time_logs:
-            frappe.db.sql(""" INSERT INTO
+		if time_logs:
+			frappe.db.sql(
+				""" INSERT INTO
                 `tabJob Card Time Log`
                     (from_time, to_time, time_in_mins, completed_qty, parent, parenttype, parentfield, name)
                 values {values}
-            """.format(values = ','.join(['%s'] * len(time_logs))), tuple(time_logs))
+            """.format(
+					values=",".join(["%s"] * len(time_logs))
+				),
+				tuple(time_logs),
+			)
 
-        frappe.reload_doc('manufacturing', 'doctype', 'job_card')
-        frappe.db.sql(""" update `tabJob Card` set total_completed_qty = for_quantity,
-            total_time_in_mins = time_in_mins where docstatus < 2 """)
+		frappe.reload_doc("manufacturing", "doctype", "job_card")
+		frappe.db.sql(
+			""" update `tabJob Card` set total_completed_qty = for_quantity,
+            total_time_in_mins = time_in_mins where docstatus < 2 """
+		)
diff --git a/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py b/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py
index d292d7a..b681f25 100644
--- a/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py
+++ b/erpnext/patches/v11_1/move_customer_lead_to_dynamic_column.py
@@ -8,8 +8,14 @@
 def execute():
 	frappe.reload_doctype("Quotation")
 	frappe.db.sql(""" UPDATE `tabQuotation` set party_name = lead WHERE quotation_to = 'Lead' """)
-	frappe.db.sql(""" UPDATE `tabQuotation` set party_name = customer WHERE quotation_to = 'Customer' """)
+	frappe.db.sql(
+		""" UPDATE `tabQuotation` set party_name = customer WHERE quotation_to = 'Customer' """
+	)
 
 	frappe.reload_doctype("Opportunity")
-	frappe.db.sql(""" UPDATE `tabOpportunity` set party_name = lead WHERE opportunity_from = 'Lead' """)
-	frappe.db.sql(""" UPDATE `tabOpportunity` set party_name = customer WHERE opportunity_from = 'Customer' """)
+	frappe.db.sql(
+		""" UPDATE `tabOpportunity` set party_name = lead WHERE opportunity_from = 'Lead' """
+	)
+	frappe.db.sql(
+		""" UPDATE `tabOpportunity` set party_name = customer WHERE opportunity_from = 'Customer' """
+	)
diff --git a/erpnext/patches/v11_1/renamed_delayed_item_report.py b/erpnext/patches/v11_1/renamed_delayed_item_report.py
index c160b79..8624781 100644
--- a/erpnext/patches/v11_1/renamed_delayed_item_report.py
+++ b/erpnext/patches/v11_1/renamed_delayed_item_report.py
@@ -6,6 +6,6 @@
 
 
 def execute():
-    for report in ["Delayed Order Item Summary", "Delayed Order Summary"]:
-        if frappe.db.exists("Report", report):
-            frappe.delete_doc("Report", report)
+	for report in ["Delayed Order Item Summary", "Delayed Order Summary"]:
+		if frappe.db.exists("Report", report):
+			frappe.delete_doc("Report", report)
diff --git a/erpnext/patches/v11_1/set_default_action_for_quality_inspection.py b/erpnext/patches/v11_1/set_default_action_for_quality_inspection.py
index 672b762..39aa6dd 100644
--- a/erpnext/patches/v11_1/set_default_action_for_quality_inspection.py
+++ b/erpnext/patches/v11_1/set_default_action_for_quality_inspection.py
@@ -6,11 +6,13 @@
 
 
 def execute():
-    stock_settings = frappe.get_doc('Stock Settings')
-    if stock_settings.default_warehouse and not frappe.db.exists("Warehouse", stock_settings.default_warehouse):
-        stock_settings.default_warehouse = None
-    if stock_settings.stock_uom and not frappe.db.exists("UOM", stock_settings.stock_uom):
-        stock_settings.stock_uom = None
-    stock_settings.flags.ignore_mandatory = True
-    stock_settings.action_if_quality_inspection_is_not_submitted = "Stop"
-    stock_settings.save()
+	stock_settings = frappe.get_doc("Stock Settings")
+	if stock_settings.default_warehouse and not frappe.db.exists(
+		"Warehouse", stock_settings.default_warehouse
+	):
+		stock_settings.default_warehouse = None
+	if stock_settings.stock_uom and not frappe.db.exists("UOM", stock_settings.stock_uom):
+		stock_settings.stock_uom = None
+	stock_settings.flags.ignore_mandatory = True
+	stock_settings.action_if_quality_inspection_is_not_submitted = "Stop"
+	stock_settings.save()
diff --git a/erpnext/patches/v11_1/set_missing_opportunity_from.py b/erpnext/patches/v11_1/set_missing_opportunity_from.py
index beec63a..ae5f620 100644
--- a/erpnext/patches/v11_1/set_missing_opportunity_from.py
+++ b/erpnext/patches/v11_1/set_missing_opportunity_from.py
@@ -5,13 +5,23 @@
 
 	frappe.reload_doctype("Opportunity")
 	if frappe.db.has_column("Opportunity", "enquiry_from"):
-		frappe.db.sql(""" UPDATE `tabOpportunity` set opportunity_from = enquiry_from
-			where ifnull(opportunity_from, '') = '' and ifnull(enquiry_from, '') != ''""")
+		frappe.db.sql(
+			""" UPDATE `tabOpportunity` set opportunity_from = enquiry_from
+			where ifnull(opportunity_from, '') = '' and ifnull(enquiry_from, '') != ''"""
+		)
 
-	if frappe.db.has_column("Opportunity", "lead") and frappe.db.has_column("Opportunity", "enquiry_from"):
-		frappe.db.sql(""" UPDATE `tabOpportunity` set party_name = lead
-			where enquiry_from = 'Lead' and ifnull(party_name, '') = '' and ifnull(lead, '') != ''""")
+	if frappe.db.has_column("Opportunity", "lead") and frappe.db.has_column(
+		"Opportunity", "enquiry_from"
+	):
+		frappe.db.sql(
+			""" UPDATE `tabOpportunity` set party_name = lead
+			where enquiry_from = 'Lead' and ifnull(party_name, '') = '' and ifnull(lead, '') != ''"""
+		)
 
-	if frappe.db.has_column("Opportunity", "customer") and frappe.db.has_column("Opportunity", "enquiry_from"):
-		frappe.db.sql(""" UPDATE `tabOpportunity` set party_name = customer
-			 where enquiry_from = 'Customer' and ifnull(party_name, '') = '' and ifnull(customer, '') != ''""")
+	if frappe.db.has_column("Opportunity", "customer") and frappe.db.has_column(
+		"Opportunity", "enquiry_from"
+	):
+		frappe.db.sql(
+			""" UPDATE `tabOpportunity` set party_name = customer
+			 where enquiry_from = 'Customer' and ifnull(party_name, '') = '' and ifnull(customer, '') != ''"""
+		)
diff --git a/erpnext/patches/v11_1/set_missing_title_for_quotation.py b/erpnext/patches/v11_1/set_missing_title_for_quotation.py
index 93d9f0e..6e7e2c9 100644
--- a/erpnext/patches/v11_1/set_missing_title_for_quotation.py
+++ b/erpnext/patches/v11_1/set_missing_title_for_quotation.py
@@ -4,7 +4,8 @@
 def execute():
 	frappe.reload_doctype("Quotation")
 	# update customer_name from Customer document if quotation_to is set to Customer
-	frappe.db.sql('''
+	frappe.db.sql(
+		"""
 		update tabQuotation, tabCustomer
 		set
 			tabQuotation.customer_name = tabCustomer.customer_name,
@@ -13,11 +14,13 @@
 			tabQuotation.customer_name is null
 			and tabQuotation.party_name = tabCustomer.name
 			and tabQuotation.quotation_to = 'Customer'
-	''')
+	"""
+	)
 
 	# update customer_name from Lead document if quotation_to is set to Lead
 
-	frappe.db.sql('''
+	frappe.db.sql(
+		"""
 		update tabQuotation, tabLead
 		set
 			tabQuotation.customer_name =  case when ifnull(tabLead.company_name, '') != '' then tabLead.company_name else tabLead.lead_name end,
@@ -26,4 +29,5 @@
 			tabQuotation.customer_name is null
 			and tabQuotation.party_name = tabLead.name
 			and tabQuotation.quotation_to = 'Lead'
-	''')
+	"""
+	)
diff --git a/erpnext/patches/v11_1/set_salary_details_submittable.py b/erpnext/patches/v11_1/set_salary_details_submittable.py
index ac082b1..e5ecce6 100644
--- a/erpnext/patches/v11_1/set_salary_details_submittable.py
+++ b/erpnext/patches/v11_1/set_salary_details_submittable.py
@@ -2,8 +2,10 @@
 
 
 def execute():
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		update `tabSalary Structure` ss, `tabSalary Detail` sd
 		set sd.docstatus=1
 		where ss.name=sd.parent and ss.docstatus=1 and sd.parenttype='Salary Structure'
-	""")
+	"""
+	)
diff --git a/erpnext/patches/v11_1/set_status_for_material_request_type_manufacture.py b/erpnext/patches/v11_1/set_status_for_material_request_type_manufacture.py
index 0d4f3d2..6e2edfe 100644
--- a/erpnext/patches/v11_1/set_status_for_material_request_type_manufacture.py
+++ b/erpnext/patches/v11_1/set_status_for_material_request_type_manufacture.py
@@ -2,8 +2,10 @@
 
 
 def execute():
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		update `tabMaterial Request`
 		set status='Manufactured'
 		where docstatus=1 and material_request_type='Manufacture' and per_ordered=100 and status != 'Stopped'
-	""")
+	"""
+	)
diff --git a/erpnext/patches/v11_1/set_variant_based_on.py b/erpnext/patches/v11_1/set_variant_based_on.py
index 2e06e63..1d57527 100644
--- a/erpnext/patches/v11_1/set_variant_based_on.py
+++ b/erpnext/patches/v11_1/set_variant_based_on.py
@@ -6,7 +6,9 @@
 
 
 def execute():
-	frappe.db.sql("""update tabItem set variant_based_on = 'Item Attribute'
+	frappe.db.sql(
+		"""update tabItem set variant_based_on = 'Item Attribute'
 		where ifnull(variant_based_on, '') = ''
 		and (has_variants=1 or ifnull(variant_of, '') != '')
-	""")
+	"""
+	)
diff --git a/erpnext/patches/v11_1/setup_guardian_role.py b/erpnext/patches/v11_1/setup_guardian_role.py
index dd9c1d2..2b25e13 100644
--- a/erpnext/patches/v11_1/setup_guardian_role.py
+++ b/erpnext/patches/v11_1/setup_guardian_role.py
@@ -2,11 +2,8 @@
 
 
 def execute():
-	if 'Education' in frappe.get_active_domains() and not frappe.db.exists("Role", "Guardian"):
+	if "Education" in frappe.get_active_domains() and not frappe.db.exists("Role", "Guardian"):
 		doc = frappe.new_doc("Role")
-		doc.update({
-			"role_name": "Guardian",
-			"desk_access": 0
-		})
+		doc.update({"role_name": "Guardian", "desk_access": 0})
 
 		doc.insert(ignore_permissions=True)
diff --git a/erpnext/patches/v11_1/update_bank_transaction_status.py b/erpnext/patches/v11_1/update_bank_transaction_status.py
index 9b8be3d..0615b04 100644
--- a/erpnext/patches/v11_1/update_bank_transaction_status.py
+++ b/erpnext/patches/v11_1/update_bank_transaction_status.py
@@ -6,22 +6,26 @@
 
 
 def execute():
-    frappe.reload_doc("accounts", "doctype", "bank_transaction")
+	frappe.reload_doc("accounts", "doctype", "bank_transaction")
 
-    bank_transaction_fields = frappe.get_meta("Bank Transaction").get_valid_columns()
+	bank_transaction_fields = frappe.get_meta("Bank Transaction").get_valid_columns()
 
-    if 'debit' in bank_transaction_fields:
-        frappe.db.sql(""" UPDATE `tabBank Transaction`
+	if "debit" in bank_transaction_fields:
+		frappe.db.sql(
+			""" UPDATE `tabBank Transaction`
             SET status = 'Reconciled'
             WHERE
                 status = 'Settled' and (debit = allocated_amount or credit = allocated_amount)
                 and ifnull(allocated_amount, 0) > 0
-        """)
+        """
+		)
 
-    elif 'deposit' in bank_transaction_fields:
-        frappe.db.sql(""" UPDATE `tabBank Transaction`
+	elif "deposit" in bank_transaction_fields:
+		frappe.db.sql(
+			""" UPDATE `tabBank Transaction`
             SET status = 'Reconciled'
             WHERE
                 status = 'Settled' and (deposit = allocated_amount or withdrawal = allocated_amount)
                 and ifnull(allocated_amount, 0) > 0
-        """)
+        """
+		)
diff --git a/erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py b/erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py
index 902df20..16e11ed 100644
--- a/erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py
+++ b/erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py
@@ -6,21 +6,23 @@
 
 
 def execute():
-	'''
-		default supplier was not set in the item defaults for multi company instance,
-			this patch will set the default supplier
+	"""
+	default supplier was not set in the item defaults for multi company instance,
+	        this patch will set the default supplier
 
-	'''
-	if not frappe.db.has_column('Item', 'default_supplier'):
+	"""
+	if not frappe.db.has_column("Item", "default_supplier"):
 		return
 
-	frappe.reload_doc('stock', 'doctype', 'item_default')
-	frappe.reload_doc('stock', 'doctype', 'item')
+	frappe.reload_doc("stock", "doctype", "item_default")
+	frappe.reload_doc("stock", "doctype", "item")
 
 	companies = frappe.get_all("Company")
 	if len(companies) > 1:
-		frappe.db.sql(""" UPDATE `tabItem Default`, `tabItem`
+		frappe.db.sql(
+			""" UPDATE `tabItem Default`, `tabItem`
 			SET `tabItem Default`.default_supplier = `tabItem`.default_supplier
 			WHERE
 				`tabItem Default`.parent = `tabItem`.name and `tabItem Default`.default_supplier is null
-				and `tabItem`.default_supplier is not null and `tabItem`.default_supplier != '' """)
+				and `tabItem`.default_supplier is not null and `tabItem`.default_supplier != '' """
+		)
diff --git a/erpnext/patches/v11_1/woocommerce_set_creation_user.py b/erpnext/patches/v11_1/woocommerce_set_creation_user.py
index 19958ee..e2d9e3e 100644
--- a/erpnext/patches/v11_1/woocommerce_set_creation_user.py
+++ b/erpnext/patches/v11_1/woocommerce_set_creation_user.py
@@ -3,7 +3,7 @@
 
 
 def execute():
-	frappe.reload_doc("erpnext_integrations", "doctype","woocommerce_settings")
+	frappe.reload_doc("erpnext_integrations", "doctype", "woocommerce_settings")
 	doc = frappe.get_doc("Woocommerce Settings")
 
 	if cint(doc.enable_sync):
diff --git a/erpnext/patches/v12_0/add_default_buying_selling_terms_in_company.py b/erpnext/patches/v12_0/add_default_buying_selling_terms_in_company.py
index 80187d8..284b616 100644
--- a/erpnext/patches/v12_0/add_default_buying_selling_terms_in_company.py
+++ b/erpnext/patches/v12_0/add_default_buying_selling_terms_in_company.py
@@ -8,12 +8,16 @@
 
 def execute():
 	frappe.reload_doc("setup", "doctype", "company")
-	if frappe.db.has_column('Company', 'default_terms'):
-		rename_field('Company', "default_terms", "default_selling_terms")
+	if frappe.db.has_column("Company", "default_terms"):
+		rename_field("Company", "default_terms", "default_selling_terms")
 
-		for company in frappe.get_all("Company", ["name", "default_selling_terms", "default_buying_terms"]):
+		for company in frappe.get_all(
+			"Company", ["name", "default_selling_terms", "default_buying_terms"]
+		):
 			if company.default_selling_terms and not company.default_buying_terms:
-				frappe.db.set_value("Company", company.name, "default_buying_terms", company.default_selling_terms)
+				frappe.db.set_value(
+					"Company", company.name, "default_buying_terms", company.default_selling_terms
+				)
 
 	frappe.reload_doc("setup", "doctype", "terms_and_conditions")
 	frappe.db.sql("update `tabTerms and Conditions` set selling=1, buying=1, hr=1")
diff --git a/erpnext/patches/v12_0/add_document_type_field_for_italy_einvoicing.py b/erpnext/patches/v12_0/add_document_type_field_for_italy_einvoicing.py
index f860cb4..a98976a 100644
--- a/erpnext/patches/v12_0/add_document_type_field_for_italy_einvoicing.py
+++ b/erpnext/patches/v12_0/add_document_type_field_for_italy_einvoicing.py
@@ -3,15 +3,19 @@
 
 
 def execute():
-	company = frappe.get_all('Company', filters = {'country': 'Italy'})
+	company = frappe.get_all("Company", filters={"country": "Italy"})
 	if not company:
 		return
 
 	custom_fields = {
-		'Sales Invoice': [
-			dict(fieldname='type_of_document', label='Type of Document',
-				fieldtype='Select', insert_after='customer_fiscal_code',
-				options='\nTD01\nTD02\nTD03\nTD04\nTD05\nTD06\nTD16\nTD17\nTD18\nTD19\nTD20\nTD21\nTD22\nTD23\nTD24\nTD25\nTD26\nTD27'),
+		"Sales Invoice": [
+			dict(
+				fieldname="type_of_document",
+				label="Type of Document",
+				fieldtype="Select",
+				insert_after="customer_fiscal_code",
+				options="\nTD01\nTD02\nTD03\nTD04\nTD05\nTD06\nTD16\nTD17\nTD18\nTD19\nTD20\nTD21\nTD22\nTD23\nTD24\nTD25\nTD26\nTD27",
+			),
 		]
 	}
 
diff --git a/erpnext/patches/v12_0/add_item_name_in_work_orders.py b/erpnext/patches/v12_0/add_item_name_in_work_orders.py
index d765b93..0e5cd4e 100644
--- a/erpnext/patches/v12_0/add_item_name_in_work_orders.py
+++ b/erpnext/patches/v12_0/add_item_name_in_work_orders.py
@@ -4,11 +4,13 @@
 def execute():
 	frappe.reload_doc("manufacturing", "doctype", "work_order")
 
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		UPDATE
 			`tabWork Order` wo
 				JOIN `tabItem` item ON wo.production_item = item.item_code
 		SET
 			wo.item_name = item.item_name
-	""")
+	"""
+	)
 	frappe.db.commit()
diff --git a/erpnext/patches/v12_0/add_permission_in_lower_deduction.py b/erpnext/patches/v12_0/add_permission_in_lower_deduction.py
index 1d77e5a..24748b2 100644
--- a/erpnext/patches/v12_0/add_permission_in_lower_deduction.py
+++ b/erpnext/patches/v12_0/add_permission_in_lower_deduction.py
@@ -3,12 +3,12 @@
 
 
 def execute():
-	company = frappe.get_all('Company', filters = {'country': 'India'})
+	company = frappe.get_all("Company", filters={"country": "India"})
 	if not company:
 		return
 
-	frappe.reload_doc('regional', 'doctype', 'Lower Deduction Certificate')
+	frappe.reload_doc("regional", "doctype", "Lower Deduction Certificate")
 
-	add_permission('Lower Deduction Certificate', 'Accounts Manager', 0)
-	update_permission_property('Lower Deduction Certificate', 'Accounts Manager', 0, 'write', 1)
-	update_permission_property('Lower Deduction Certificate', 'Accounts Manager', 0, 'create', 1)
+	add_permission("Lower Deduction Certificate", "Accounts Manager", 0)
+	update_permission_property("Lower Deduction Certificate", "Accounts Manager", 0, "write", 1)
+	update_permission_property("Lower Deduction Certificate", "Accounts Manager", 0, "create", 1)
diff --git a/erpnext/patches/v12_0/add_state_code_for_ladakh.py b/erpnext/patches/v12_0/add_state_code_for_ladakh.py
index 6722b7b..4b06eb1 100644
--- a/erpnext/patches/v12_0/add_state_code_for_ladakh.py
+++ b/erpnext/patches/v12_0/add_state_code_for_ladakh.py
@@ -5,15 +5,15 @@
 
 def execute():
 
-	company = frappe.get_all('Company', filters = {'country': 'India'})
+	company = frappe.get_all("Company", filters={"country": "India"})
 	if not company:
 		return
 
-	custom_fields = ['Address-gst_state', 'Tax Category-gst_state']
+	custom_fields = ["Address-gst_state", "Tax Category-gst_state"]
 
 	# Update options in gst_state custom fields
 	for field in custom_fields:
-		if frappe.db.exists('Custom Field', field):
-			gst_state_field = frappe.get_doc('Custom Field', field)
-			gst_state_field.options = '\n'.join(states)
+		if frappe.db.exists("Custom Field", field):
+			gst_state_field = frappe.get_doc("Custom Field", field)
+			gst_state_field.options = "\n".join(states)
 			gst_state_field.save()
diff --git a/erpnext/patches/v12_0/add_taxjar_integration_field.py b/erpnext/patches/v12_0/add_taxjar_integration_field.py
index b0ddf00..9217384 100644
--- a/erpnext/patches/v12_0/add_taxjar_integration_field.py
+++ b/erpnext/patches/v12_0/add_taxjar_integration_field.py
@@ -4,7 +4,7 @@
 
 
 def execute():
-	company = frappe.get_all('Company', filters={'country': 'United States'})
+	company = frappe.get_all("Company", filters={"country": "United States"})
 	if not company:
 		return
 
diff --git a/erpnext/patches/v12_0/add_variant_of_in_item_attribute_table.py b/erpnext/patches/v12_0/add_variant_of_in_item_attribute_table.py
index c3a422c..7f044c8 100644
--- a/erpnext/patches/v12_0/add_variant_of_in_item_attribute_table.py
+++ b/erpnext/patches/v12_0/add_variant_of_in_item_attribute_table.py
@@ -2,9 +2,11 @@
 
 
 def execute():
-	frappe.reload_doc('stock', 'doctype', 'item_variant_attribute')
-	frappe.db.sql('''
+	frappe.reload_doc("stock", "doctype", "item_variant_attribute")
+	frappe.db.sql(
+		"""
 		UPDATE `tabItem Variant Attribute` t1
 		INNER JOIN `tabItem` t2 ON t2.name = t1.parent
 		SET t1.variant_of = t2.variant_of
-	''')
+	"""
+	)
diff --git a/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py b/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py
index aec9cb8..744ea1c 100644
--- a/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py
+++ b/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py
@@ -4,10 +4,13 @@
 
 def execute():
 
-	frappe.reload_doc('accounts', 'doctype', 'accounting_dimension')
+	frappe.reload_doc("accounts", "doctype", "accounting_dimension")
 
-	accounting_dimensions = frappe.db.sql("""select fieldname, label, document_type, disabled from
-		`tabAccounting Dimension`""", as_dict=1)
+	accounting_dimensions = frappe.db.sql(
+		"""select fieldname, label, document_type, disabled from
+		`tabAccounting Dimension`""",
+		as_dict=1,
+	)
 
 	if not accounting_dimensions:
 		return
@@ -15,13 +18,19 @@
 	count = 1
 	for d in accounting_dimensions:
 
-		if count%2 == 0:
-			insert_after_field = 'dimension_col_break'
+		if count % 2 == 0:
+			insert_after_field = "dimension_col_break"
 		else:
-			insert_after_field = 'accounting_dimensions_section'
+			insert_after_field = "accounting_dimensions_section"
 
-		for doctype in ["Subscription Plan", "Subscription", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item",
-			"Expense Claim Detail", "Expense Taxes and Charges"]:
+		for doctype in [
+			"Subscription Plan",
+			"Subscription",
+			"Opening Invoice Creation Tool",
+			"Opening Invoice Creation Tool Item",
+			"Expense Claim Detail",
+			"Expense Taxes and Charges",
+		]:
 
 			field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname})
 
@@ -33,7 +42,7 @@
 				"label": d.label,
 				"fieldtype": "Link",
 				"options": d.document_type,
-				"insert_after": insert_after_field
+				"insert_after": insert_after_field,
 			}
 
 			create_custom_field(doctype, df)
diff --git a/erpnext/patches/v12_0/create_default_energy_point_rules.py b/erpnext/patches/v12_0/create_default_energy_point_rules.py
index 35eaca7..da200de 100644
--- a/erpnext/patches/v12_0/create_default_energy_point_rules.py
+++ b/erpnext/patches/v12_0/create_default_energy_point_rules.py
@@ -4,5 +4,5 @@
 
 
 def execute():
-	frappe.reload_doc('social', 'doctype', 'energy_point_rule')
+	frappe.reload_doc("social", "doctype", "energy_point_rule")
 	create_default_energy_point_rules()
diff --git a/erpnext/patches/v12_0/create_irs_1099_field_united_states.py b/erpnext/patches/v12_0/create_irs_1099_field_united_states.py
index efcc7cf..80e9047 100644
--- a/erpnext/patches/v12_0/create_irs_1099_field_united_states.py
+++ b/erpnext/patches/v12_0/create_irs_1099_field_united_states.py
@@ -5,12 +5,12 @@
 
 def execute():
 
-	frappe.reload_doc('accounts', 'doctype', 'allowed_to_transact_with', force=True)
-	frappe.reload_doc('accounts', 'doctype', 'pricing_rule_detail', force=True)
-	frappe.reload_doc('crm', 'doctype', 'lost_reason_detail', force=True)
-	frappe.reload_doc('setup', 'doctype', 'quotation_lost_reason_detail', force=True)
+	frappe.reload_doc("accounts", "doctype", "allowed_to_transact_with", force=True)
+	frappe.reload_doc("accounts", "doctype", "pricing_rule_detail", force=True)
+	frappe.reload_doc("crm", "doctype", "lost_reason_detail", force=True)
+	frappe.reload_doc("setup", "doctype", "quotation_lost_reason_detail", force=True)
 
-	company = frappe.get_all('Company', filters = {'country': 'United States'})
+	company = frappe.get_all("Company", filters={"country": "United States"})
 	if not company:
 		return
 
diff --git a/erpnext/patches/v12_0/create_itc_reversal_custom_fields.py b/erpnext/patches/v12_0/create_itc_reversal_custom_fields.py
index d4fbded..906baf2 100644
--- a/erpnext/patches/v12_0/create_itc_reversal_custom_fields.py
+++ b/erpnext/patches/v12_0/create_itc_reversal_custom_fields.py
@@ -6,44 +6,76 @@
 
 
 def execute():
-	company = frappe.get_all('Company', filters = {'country': 'India'}, fields=['name'])
+	company = frappe.get_all("Company", filters={"country": "India"}, fields=["name"])
 	if not company:
 		return
 
 	frappe.reload_doc("regional", "doctype", "gst_settings")
 	frappe.reload_doc("accounts", "doctype", "gst_account")
 
-	journal_entry_types = frappe.get_meta("Journal Entry").get_options("voucher_type").split("\n") + ['Reversal Of ITC']
-	make_property_setter('Journal Entry', 'voucher_type', 'options', '\n'.join(journal_entry_types), '')
+	journal_entry_types = frappe.get_meta("Journal Entry").get_options("voucher_type").split("\n") + [
+		"Reversal Of ITC"
+	]
+	make_property_setter(
+		"Journal Entry", "voucher_type", "options", "\n".join(journal_entry_types), ""
+	)
 
 	custom_fields = {
-		'Journal Entry': [
-			dict(fieldname='reversal_type', label='Reversal Type',
-				fieldtype='Select', insert_after='voucher_type', print_hide=1,
+		"Journal Entry": [
+			dict(
+				fieldname="reversal_type",
+				label="Reversal Type",
+				fieldtype="Select",
+				insert_after="voucher_type",
+				print_hide=1,
 				options="As per rules 42 & 43 of CGST Rules\nOthers",
 				depends_on="eval:doc.voucher_type=='Reversal Of ITC'",
-				mandatory_depends_on="eval:doc.voucher_type=='Reversal Of ITC'"),
-			dict(fieldname='company_address', label='Company Address',
-				fieldtype='Link', options='Address', insert_after='reversal_type',
-				print_hide=1, depends_on="eval:doc.voucher_type=='Reversal Of ITC'",
-				mandatory_depends_on="eval:doc.voucher_type=='Reversal Of ITC'"),
-			dict(fieldname='company_gstin', label='Company GSTIN',
-				fieldtype='Data', read_only=1, insert_after='company_address', print_hide=1,
-				fetch_from='company_address.gstin',
+				mandatory_depends_on="eval:doc.voucher_type=='Reversal Of ITC'",
+			),
+			dict(
+				fieldname="company_address",
+				label="Company Address",
+				fieldtype="Link",
+				options="Address",
+				insert_after="reversal_type",
+				print_hide=1,
 				depends_on="eval:doc.voucher_type=='Reversal Of ITC'",
-				mandatory_depends_on="eval:doc.voucher_type=='Reversal Of ITC'")
+				mandatory_depends_on="eval:doc.voucher_type=='Reversal Of ITC'",
+			),
+			dict(
+				fieldname="company_gstin",
+				label="Company GSTIN",
+				fieldtype="Data",
+				read_only=1,
+				insert_after="company_address",
+				print_hide=1,
+				fetch_from="company_address.gstin",
+				depends_on="eval:doc.voucher_type=='Reversal Of ITC'",
+				mandatory_depends_on="eval:doc.voucher_type=='Reversal Of ITC'",
+			),
 		],
-		'Purchase Invoice': [
-			dict(fieldname='eligibility_for_itc', label='Eligibility For ITC',
-				fieldtype='Select', insert_after='reason_for_issuing_document', print_hide=1,
-				options='Input Service Distributor\nImport Of Service\nImport Of Capital Goods\nITC on Reverse Charge\nIneligible As Per Section 17(5)\nIneligible Others\nAll Other ITC',
-				default="All Other ITC")
+		"Purchase Invoice": [
+			dict(
+				fieldname="eligibility_for_itc",
+				label="Eligibility For ITC",
+				fieldtype="Select",
+				insert_after="reason_for_issuing_document",
+				print_hide=1,
+				options="Input Service Distributor\nImport Of Service\nImport Of Capital Goods\nITC on Reverse Charge\nIneligible As Per Section 17(5)\nIneligible Others\nAll Other ITC",
+				default="All Other ITC",
+			)
 		],
-		'Purchase Invoice Item': [
-			dict(fieldname='taxable_value', label='Taxable Value',
-				fieldtype='Currency', insert_after='base_net_amount', hidden=1, options="Company:company:default_currency",
-				print_hide=1)
-		]
+		"Purchase Invoice Item": [
+			dict(
+				fieldname="taxable_value",
+				label="Taxable Value",
+				fieldtype="Currency",
+				insert_after="base_net_amount",
+				hidden=1,
+				options="Company:company:default_currency",
+				print_hide=1,
+			)
+		],
 	}
 
 	create_custom_fields(custom_fields, update=True)
@@ -53,28 +85,40 @@
 
 	gst_accounts = get_gst_accounts(only_non_reverse_charge=1)
 
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		UPDATE `tabCustom Field` SET fieldtype='Currency', options='Company:company:default_currency'
 		WHERE dt = 'Purchase Invoice' and fieldname in ('itc_integrated_tax', 'itc_state_tax', 'itc_central_tax',
 			'itc_cess_amount')
-	""")
+	"""
+	)
 
-	frappe.db.sql("""UPDATE `tabPurchase Invoice` set itc_integrated_tax = '0'
-		WHERE trim(coalesce(itc_integrated_tax, '')) = '' """)
+	frappe.db.sql(
+		"""UPDATE `tabPurchase Invoice` set itc_integrated_tax = '0'
+		WHERE trim(coalesce(itc_integrated_tax, '')) = '' """
+	)
 
-	frappe.db.sql("""UPDATE `tabPurchase Invoice` set itc_state_tax = '0'
-		WHERE trim(coalesce(itc_state_tax, '')) = '' """)
+	frappe.db.sql(
+		"""UPDATE `tabPurchase Invoice` set itc_state_tax = '0'
+		WHERE trim(coalesce(itc_state_tax, '')) = '' """
+	)
 
-	frappe.db.sql("""UPDATE `tabPurchase Invoice` set itc_central_tax = '0'
-		WHERE trim(coalesce(itc_central_tax, '')) = '' """)
+	frappe.db.sql(
+		"""UPDATE `tabPurchase Invoice` set itc_central_tax = '0'
+		WHERE trim(coalesce(itc_central_tax, '')) = '' """
+	)
 
-	frappe.db.sql("""UPDATE `tabPurchase Invoice` set itc_cess_amount = '0'
-		WHERE trim(coalesce(itc_cess_amount, '')) = '' """)
+	frappe.db.sql(
+		"""UPDATE `tabPurchase Invoice` set itc_cess_amount = '0'
+		WHERE trim(coalesce(itc_cess_amount, '')) = '' """
+	)
 
 	# Get purchase invoices
-	invoices = frappe.get_all('Purchase Invoice',
-		{'posting_date': ('>=', '2021-04-01'), 'eligibility_for_itc': ('!=', 'Ineligible')},
-		['name'])
+	invoices = frappe.get_all(
+		"Purchase Invoice",
+		{"posting_date": (">=", "2021-04-01"), "eligibility_for_itc": ("!=", "Ineligible")},
+		["name"],
+	)
 
 	amount_map = {}
 
@@ -82,37 +126,42 @@
 		invoice_list = set([d.name for d in invoices])
 
 		# Get GST applied
-		amounts = frappe.db.sql("""
+		amounts = frappe.db.sql(
+			"""
 			SELECT parent, account_head, sum(base_tax_amount_after_discount_amount) as amount
 			FROM `tabPurchase Taxes and Charges`
 			where parent in %s
 			GROUP BY parent, account_head
-		""", (invoice_list), as_dict=1)
+		""",
+			(invoice_list),
+			as_dict=1,
+		)
 
 		for d in amounts:
-			amount_map.setdefault(d.parent,
-			{
-				'itc_integrated_tax': 0,
-				'itc_state_tax': 0,
-				'itc_central_tax': 0,
-				'itc_cess_amount': 0
-			})
+			amount_map.setdefault(
+				d.parent,
+				{"itc_integrated_tax": 0, "itc_state_tax": 0, "itc_central_tax": 0, "itc_cess_amount": 0},
+			)
 			if not gst_accounts:
 				continue
 
-			if d.account_head in gst_accounts.get('igst_account'):
-				amount_map[d.parent]['itc_integrated_tax'] += d.amount
-			if d.account_head in gst_accounts.get('cgst_account'):
-				amount_map[d.parent]['itc_central_tax'] += d.amount
-			if d.account_head in gst_accounts.get('sgst_account'):
-				amount_map[d.parent]['itc_state_tax'] += d.amount
-			if d.account_head in gst_accounts.get('cess_account'):
-				amount_map[d.parent]['itc_cess_amount'] += d.amount
+			if d.account_head in gst_accounts.get("igst_account"):
+				amount_map[d.parent]["itc_integrated_tax"] += d.amount
+			if d.account_head in gst_accounts.get("cgst_account"):
+				amount_map[d.parent]["itc_central_tax"] += d.amount
+			if d.account_head in gst_accounts.get("sgst_account"):
+				amount_map[d.parent]["itc_state_tax"] += d.amount
+			if d.account_head in gst_accounts.get("cess_account"):
+				amount_map[d.parent]["itc_cess_amount"] += d.amount
 
 		for invoice, values in amount_map.items():
-			frappe.db.set_value('Purchase Invoice', invoice, {
-				'itc_integrated_tax': values.get('itc_integrated_tax'),
-				'itc_central_tax': values.get('itc_central_tax'),
-				'itc_state_tax': values['itc_state_tax'],
-				'itc_cess_amount': values['itc_cess_amount'],
-			})
+			frappe.db.set_value(
+				"Purchase Invoice",
+				invoice,
+				{
+					"itc_integrated_tax": values.get("itc_integrated_tax"),
+					"itc_central_tax": values.get("itc_central_tax"),
+					"itc_state_tax": values["itc_state_tax"],
+					"itc_cess_amount": values["itc_cess_amount"],
+				},
+			)
diff --git a/erpnext/patches/v12_0/create_taxable_value_field.py b/erpnext/patches/v12_0/create_taxable_value_field.py
index 55717cc..ad95303 100644
--- a/erpnext/patches/v12_0/create_taxable_value_field.py
+++ b/erpnext/patches/v12_0/create_taxable_value_field.py
@@ -3,15 +3,21 @@
 
 
 def execute():
-	company = frappe.get_all('Company', filters = {'country': 'India'})
+	company = frappe.get_all("Company", filters={"country": "India"})
 	if not company:
 		return
 
 	custom_fields = {
-		'Sales Invoice Item': [
-			dict(fieldname='taxable_value', label='Taxable Value',
-				fieldtype='Currency', insert_after='base_net_amount', hidden=1, options="Company:company:default_currency",
-				print_hide=1)
+		"Sales Invoice Item": [
+			dict(
+				fieldname="taxable_value",
+				label="Taxable Value",
+				fieldtype="Currency",
+				insert_after="base_net_amount",
+				hidden=1,
+				options="Company:company:default_currency",
+				print_hide=1,
+			)
 		]
 	}
 
diff --git a/erpnext/patches/v12_0/delete_priority_property_setter.py b/erpnext/patches/v12_0/delete_priority_property_setter.py
index cacc463..fbb0243 100644
--- a/erpnext/patches/v12_0/delete_priority_property_setter.py
+++ b/erpnext/patches/v12_0/delete_priority_property_setter.py
@@ -2,9 +2,11 @@
 
 
 def execute():
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		DELETE FROM `tabProperty Setter`
 		WHERE `tabProperty Setter`.doc_type='Issue'
 			AND `tabProperty Setter`.field_name='priority'
 			AND `tabProperty Setter`.property='options'
-	""")
+	"""
+	)
diff --git a/erpnext/patches/v12_0/fix_percent_complete_for_projects.py b/erpnext/patches/v12_0/fix_percent_complete_for_projects.py
index 36f51bc..1fbcfa4 100644
--- a/erpnext/patches/v12_0/fix_percent_complete_for_projects.py
+++ b/erpnext/patches/v12_0/fix_percent_complete_for_projects.py
@@ -4,10 +4,13 @@
 
 def execute():
 	for project in frappe.get_all("Project", fields=["name", "percent_complete_method"]):
-		total = frappe.db.count('Task', dict(project=project.name))
+		total = frappe.db.count("Task", dict(project=project.name))
 		if project.percent_complete_method == "Task Completion" and total > 0:
-			completed = frappe.db.sql("""select count(name) from tabTask where
-					project=%s and status in ('Cancelled', 'Completed')""", project.name)[0][0]
+			completed = frappe.db.sql(
+				"""select count(name) from tabTask where
+					project=%s and status in ('Cancelled', 'Completed')""",
+				project.name,
+			)[0][0]
 			percent_complete = flt(flt(completed) / total * 100, 2)
 			if project.percent_complete != percent_complete:
 				frappe.db.set_value("Project", project.name, "percent_complete", percent_complete)
diff --git a/erpnext/patches/v12_0/fix_quotation_expired_status.py b/erpnext/patches/v12_0/fix_quotation_expired_status.py
index e5c4b8c..285183b 100644
--- a/erpnext/patches/v12_0/fix_quotation_expired_status.py
+++ b/erpnext/patches/v12_0/fix_quotation_expired_status.py
@@ -13,12 +13,13 @@
 			so_item.docstatus = 1 and so.docstatus = 1
 			and so_item.parent = so.name
 			and so_item.prevdoc_docname = qo.name
-			and qo.valid_till < so.transaction_date""" # check if SO was created after quotation expired
+			and qo.valid_till < so.transaction_date"""  # check if SO was created after quotation expired
 
 	frappe.db.sql(
-		"""UPDATE `tabQuotation` qo SET qo.status = 'Expired' WHERE {cond} and exists({invalid_so_against_quo})"""
-			.format(cond=cond, invalid_so_against_quo=invalid_so_against_quo)
+		"""UPDATE `tabQuotation` qo SET qo.status = 'Expired' WHERE {cond} and exists({invalid_so_against_quo})""".format(
+			cond=cond, invalid_so_against_quo=invalid_so_against_quo
 		)
+	)
 
 	valid_so_against_quo = """
 		SELECT
@@ -27,9 +28,10 @@
 			so_item.docstatus = 1 and so.docstatus = 1
 			and so_item.parent = so.name
 			and so_item.prevdoc_docname = qo.name
-			and qo.valid_till >= so.transaction_date""" # check if SO was created before quotation expired
+			and qo.valid_till >= so.transaction_date"""  # check if SO was created before quotation expired
 
 	frappe.db.sql(
-		"""UPDATE `tabQuotation` qo SET qo.status = 'Closed' WHERE {cond} and exists({valid_so_against_quo})"""
-			.format(cond=cond, valid_so_against_quo=valid_so_against_quo)
+		"""UPDATE `tabQuotation` qo SET qo.status = 'Closed' WHERE {cond} and exists({valid_so_against_quo})""".format(
+			cond=cond, valid_so_against_quo=valid_so_against_quo
 		)
+	)
diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py
index 90e46d0..354c509 100644
--- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py
+++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py
@@ -7,8 +7,8 @@
 
 
 def execute():
-	""" Generates leave ledger entries for leave allocation/application/encashment
-		for last allocation """
+	"""Generates leave ledger entries for leave allocation/application/encashment
+	for last allocation"""
 	frappe.reload_doc("HR", "doctype", "Leave Ledger Entry")
 	frappe.reload_doc("HR", "doctype", "Leave Encashment")
 	frappe.reload_doc("HR", "doctype", "Leave Type")
@@ -22,55 +22,79 @@
 	generate_encashment_leave_ledger_entries()
 	generate_expiry_allocation_ledger_entries()
 
+
 def update_leave_allocation_fieldname():
-	''' maps data from old field to the new field '''
-	frappe.db.sql("""
+	"""maps data from old field to the new field"""
+	frappe.db.sql(
+		"""
 		UPDATE `tabLeave Allocation`
 		SET `unused_leaves` = `carry_forwarded_leaves`
-	""")
+	"""
+	)
+
 
 def generate_allocation_ledger_entries():
-	''' fix ledger entries for missing leave allocation transaction '''
+	"""fix ledger entries for missing leave allocation transaction"""
 	allocation_list = get_allocation_records()
 
 	for allocation in allocation_list:
-		if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Allocation', 'transaction_name': allocation.name}):
+		if not frappe.db.exists(
+			"Leave Ledger Entry",
+			{"transaction_type": "Leave Allocation", "transaction_name": allocation.name},
+		):
 			allocation_obj = frappe.get_doc("Leave Allocation", allocation)
 			allocation_obj.create_leave_ledger_entry()
 
+
 def generate_application_leave_ledger_entries():
-	''' fix ledger entries for missing leave application transaction '''
+	"""fix ledger entries for missing leave application transaction"""
 	leave_applications = get_leaves_application_records()
 
 	for application in leave_applications:
-		if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Application', 'transaction_name': application.name}):
+		if not frappe.db.exists(
+			"Leave Ledger Entry",
+			{"transaction_type": "Leave Application", "transaction_name": application.name},
+		):
 			frappe.get_doc("Leave Application", application.name).create_leave_ledger_entry()
 
+
 def generate_encashment_leave_ledger_entries():
-	''' fix ledger entries for missing leave encashment transaction '''
+	"""fix ledger entries for missing leave encashment transaction"""
 	leave_encashments = get_leave_encashment_records()
 
 	for encashment in leave_encashments:
-		if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Encashment', 'transaction_name': encashment.name}):
+		if not frappe.db.exists(
+			"Leave Ledger Entry",
+			{"transaction_type": "Leave Encashment", "transaction_name": encashment.name},
+		):
 			frappe.get_doc("Leave Encashment", encashment).create_leave_ledger_entry()
 
+
 def generate_expiry_allocation_ledger_entries():
-	''' fix ledger entries for missing leave allocation transaction '''
+	"""fix ledger entries for missing leave allocation transaction"""
 	from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import expire_allocation
+
 	allocation_list = get_allocation_records()
 
 	for allocation in allocation_list:
-		if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Allocation', 'transaction_name': allocation.name, 'is_expired': 1}):
+		if not frappe.db.exists(
+			"Leave Ledger Entry",
+			{"transaction_type": "Leave Allocation", "transaction_name": allocation.name, "is_expired": 1},
+		):
 			allocation_obj = frappe.get_doc("Leave Allocation", allocation)
 			if allocation_obj.to_date <= getdate(today()):
 				expire_allocation(allocation_obj)
 
+
 def get_allocation_records():
-	return frappe.get_all("Leave Allocation", filters={"docstatus": 1},
-		fields=['name'], order_by='to_date ASC')
+	return frappe.get_all(
+		"Leave Allocation", filters={"docstatus": 1}, fields=["name"], order_by="to_date ASC"
+	)
+
 
 def get_leaves_application_records():
-	return frappe.get_all("Leave Application", filters={"docstatus": 1}, fields=['name'])
+	return frappe.get_all("Leave Application", filters={"docstatus": 1}, fields=["name"])
+
 
 def get_leave_encashment_records():
-	return frappe.get_all("Leave Encashment", filters={"docstatus": 1}, fields=['name'])
+	return frappe.get_all("Leave Encashment", filters={"docstatus": 1}, fields=["name"])
diff --git a/erpnext/patches/v12_0/make_item_manufacturer.py b/erpnext/patches/v12_0/make_item_manufacturer.py
index d66f429..3f23365 100644
--- a/erpnext/patches/v12_0/make_item_manufacturer.py
+++ b/erpnext/patches/v12_0/make_item_manufacturer.py
@@ -9,20 +9,29 @@
 	frappe.reload_doc("stock", "doctype", "item_manufacturer")
 
 	item_manufacturer = []
-	for d in frappe.db.sql(""" SELECT name, manufacturer, manufacturer_part_no, creation, owner
-		FROM `tabItem` WHERE manufacturer is not null and manufacturer != ''""", as_dict=1):
-		item_manufacturer.append((
-			frappe.generate_hash("", 10),
-			d.name,
-			d.manufacturer,
-			d.manufacturer_part_no,
-			d.creation,
-			d.owner
-		))
+	for d in frappe.db.sql(
+		""" SELECT name, manufacturer, manufacturer_part_no, creation, owner
+		FROM `tabItem` WHERE manufacturer is not null and manufacturer != ''""",
+		as_dict=1,
+	):
+		item_manufacturer.append(
+			(
+				frappe.generate_hash("", 10),
+				d.name,
+				d.manufacturer,
+				d.manufacturer_part_no,
+				d.creation,
+				d.owner,
+			)
+		)
 
 	if item_manufacturer:
-		frappe.db.sql('''
+		frappe.db.sql(
+			"""
 			INSERT INTO `tabItem Manufacturer`
 			(`name`, `item_code`, `manufacturer`, `manufacturer_part_no`, `creation`, `owner`)
-			VALUES {}'''.format(', '.join(['%s'] * len(item_manufacturer))), tuple(item_manufacturer)
+			VALUES {}""".format(
+				", ".join(["%s"] * len(item_manufacturer))
+			),
+			tuple(item_manufacturer),
 		)
diff --git a/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py b/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py
index b3ee340..671dfd1 100644
--- a/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py
+++ b/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py
@@ -2,13 +2,22 @@
 
 
 def execute():
-	frappe.reload_doc('accounts', 'doctype', 'bank', force=1)
+	frappe.reload_doc("accounts", "doctype", "bank", force=1)
 
-	if frappe.db.table_exists('Bank') and frappe.db.table_exists('Bank Account') and frappe.db.has_column('Bank Account', 'swift_number'):
-		frappe.db.sql("""
-			UPDATE `tabBank` b, `tabBank Account` ba
-			SET b.swift_number = ba.swift_number WHERE b.name = ba.bank
-		""")
+	if (
+		frappe.db.table_exists("Bank")
+		and frappe.db.table_exists("Bank Account")
+		and frappe.db.has_column("Bank Account", "swift_number")
+	):
+		try:
+			frappe.db.sql(
+				"""
+				UPDATE `tabBank` b, `tabBank Account` ba
+				SET b.swift_number = ba.swift_number WHERE b.name = ba.bank
+			"""
+			)
+		except Exception as e:
+			frappe.log_error("Bank to Bank Account patch migration failed")
 
-	frappe.reload_doc('accounts', 'doctype', 'bank_account')
-	frappe.reload_doc('accounts', 'doctype', 'payment_request')
+	frappe.reload_doc("accounts", "doctype", "bank_account")
+	frappe.reload_doc("accounts", "doctype", "payment_request")
diff --git a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py
index 82dfba5..17c1966 100644
--- a/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py
+++ b/erpnext/patches/v12_0/move_credit_limit_to_customer_credit_limit.py
@@ -6,7 +6,7 @@
 
 
 def execute():
-	''' Move credit limit and bypass credit limit to the child table of customer credit limit '''
+	"""Move credit limit and bypass credit limit to the child table of customer credit limit"""
 	frappe.reload_doc("Selling", "doctype", "Customer Credit Limit")
 	frappe.reload_doc("Selling", "doctype", "Customer")
 	frappe.reload_doc("Setup", "doctype", "Customer Group")
@@ -16,28 +16,32 @@
 
 	move_credit_limit_to_child_table()
 
-def move_credit_limit_to_child_table():
-	''' maps data from old field to the new field in the child table '''
 
-	companies = frappe.get_all("Company", 'name')
+def move_credit_limit_to_child_table():
+	"""maps data from old field to the new field in the child table"""
+
+	companies = frappe.get_all("Company", "name")
 	for doctype in ("Customer", "Customer Group"):
 		fields = ""
-		if doctype == "Customer" \
-				and frappe.db.has_column('Customer', 'bypass_credit_limit_check_at_sales_order'):
+		if doctype == "Customer" and frappe.db.has_column(
+			"Customer", "bypass_credit_limit_check_at_sales_order"
+		):
 			fields = ", bypass_credit_limit_check_at_sales_order"
 
-		credit_limit_records = frappe.db.sql('''
+		credit_limit_records = frappe.db.sql(
+			"""
 			SELECT name, credit_limit {0}
 			FROM `tab{1}` where credit_limit > 0
-		'''.format(fields, doctype), as_dict=1) #nosec
+		""".format(
+				fields, doctype
+			),
+			as_dict=1,
+		)  # nosec
 
 		for record in credit_limit_records:
 			doc = frappe.get_doc(doctype, record.name)
 			for company in companies:
-				row = frappe._dict({
-					'credit_limit': record.credit_limit,
-					'company': company.name
-				})
+				row = frappe._dict({"credit_limit": record.credit_limit, "company": company.name})
 				if doctype == "Customer":
 					row.bypass_credit_limit_check = record.bypass_credit_limit_check_at_sales_order
 
diff --git a/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py
index 5de7e69..8b8d963 100644
--- a/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py
+++ b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py
@@ -6,7 +6,7 @@
 
 
 def execute():
-    ''' Move from due_advance_amount to pending_amount '''
+	"""Move from due_advance_amount to pending_amount"""
 
-    if frappe.db.has_column("Employee Advance", "due_advance_amount"):
-        frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''')
+	if frappe.db.has_column("Employee Advance", "due_advance_amount"):
+		frappe.db.sql(""" UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount """)
diff --git a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
index 905aebe..c4c3b69 100644
--- a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
+++ b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
@@ -12,17 +12,22 @@
 
 	frappe.reload_doc("accounts", "doctype", "item_tax_template_detail", force=1)
 	frappe.reload_doc("accounts", "doctype", "item_tax_template", force=1)
-	existing_templates = frappe.db.sql("""select template.name, details.tax_type, details.tax_rate
+	existing_templates = frappe.db.sql(
+		"""select template.name, details.tax_type, details.tax_rate
 		from `tabItem Tax Template` template, `tabItem Tax Template Detail` details
 		where details.parent=template.name
-		""", as_dict=1)
+		""",
+		as_dict=1,
+	)
 
 	if len(existing_templates):
 		for d in existing_templates:
 			item_tax_templates.setdefault(d.name, {})
 			item_tax_templates[d.name][d.tax_type] = d.tax_rate
 
-	for d in frappe.db.sql("""select parent as item_code, tax_type, tax_rate from `tabItem Tax`""", as_dict=1):
+	for d in frappe.db.sql(
+		"""select parent as item_code, tax_type, tax_rate from `tabItem Tax`""", as_dict=1
+	):
 		old_item_taxes.setdefault(d.item_code, [])
 		old_item_taxes[d.item_code].append(d)
 
@@ -49,7 +54,9 @@
 				item_tax_map[d.tax_type] = d.tax_rate
 
 		tax_types = []
-		item_tax_template_name = get_item_tax_template(item_tax_templates, item_tax_map, item_code, tax_types=tax_types)
+		item_tax_template_name = get_item_tax_template(
+			item_tax_templates, item_tax_map, item_code, tax_types=tax_types
+		)
 
 		# update the item tax table
 		frappe.db.sql("delete from `tabItem Tax` where parent=%s and parenttype='Item'", item_code)
@@ -61,17 +68,29 @@
 				d.db_insert()
 
 	doctypes = [
-		'Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice',
-		'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice'
+		"Quotation",
+		"Sales Order",
+		"Delivery Note",
+		"Sales Invoice",
+		"Supplier Quotation",
+		"Purchase Order",
+		"Purchase Receipt",
+		"Purchase Invoice",
 	]
 
 	for dt in doctypes:
-		for d in frappe.db.sql("""select name, parenttype, parent, item_code, item_tax_rate from `tab{0} Item`
+		for d in frappe.db.sql(
+			"""select name, parenttype, parent, item_code, item_tax_rate from `tab{0} Item`
 								where ifnull(item_tax_rate, '') not in ('', '{{}}')
-								and item_tax_template is NULL""".format(dt), as_dict=1):
+								and item_tax_template is NULL""".format(
+				dt
+			),
+			as_dict=1,
+		):
 			item_tax_map = json.loads(d.item_tax_rate)
-			item_tax_template_name = get_item_tax_template(item_tax_templates,
-				item_tax_map, d.item_code, d.parenttype, d.parent, tax_types=tax_types)
+			item_tax_template_name = get_item_tax_template(
+				item_tax_templates, item_tax_map, d.item_code, d.parenttype, d.parent, tax_types=tax_types
+			)
 			frappe.db.set_value(dt + " Item", d.name, "item_tax_template", item_tax_template_name)
 
 	frappe.db.auto_commit_on_many_writes = False
@@ -81,7 +100,10 @@
 	settings.determine_address_tax_category_from = "Billing Address"
 	settings.save()
 
-def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parenttype=None, parent=None, tax_types=None):
+
+def get_item_tax_template(
+	item_tax_templates, item_tax_map, item_code, parenttype=None, parent=None, tax_types=None
+):
 	# search for previously created item tax template by comparing tax maps
 	for template, item_tax_template_map in item_tax_templates.items():
 		if item_tax_map == item_tax_template_map:
@@ -90,13 +112,26 @@
 	# if no item tax template found, create one
 	item_tax_template = frappe.new_doc("Item Tax Template")
 	item_tax_template.title = make_autoname("Item Tax Template-.####")
+	item_tax_template_name = item_tax_template.title
 
 	for tax_type, tax_rate in item_tax_map.items():
-		account_details = frappe.db.get_value("Account", tax_type, ['name', 'account_type', 'company'], as_dict=1)
+		account_details = frappe.db.get_value(
+			"Account", tax_type, ["name", "account_type", "company"], as_dict=1
+		)
 		if account_details:
 			item_tax_template.company = account_details.company
-			if account_details.account_type not in ('Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation'):
-				frappe.db.set_value('Account', account_details.name, 'account_type', 'Chargeable')
+			if not item_tax_template_name:
+				# set name once company is set as name is generated from company & title
+				# setting name is required to update `item_tax_templates` dict
+				item_tax_template_name = item_tax_template.set_new_name()
+			if account_details.account_type not in (
+				"Tax",
+				"Chargeable",
+				"Income Account",
+				"Expense Account",
+				"Expenses Included In Valuation",
+			):
+				frappe.db.set_value("Account", account_details.name, "account_type", "Chargeable")
 		else:
 			parts = tax_type.strip().split(" - ")
 			account_name = " - ".join(parts[:-1])
@@ -104,18 +139,25 @@
 				tax_type = None
 			else:
 				company = get_company(parts[-1], parenttype, parent)
-				parent_account = frappe.get_value("Account", {"account_name": account_name, "company": company}, "parent_account")
+				parent_account = frappe.get_value(
+					"Account", {"account_name": account_name, "company": company}, "parent_account"
+				)
 				if not parent_account:
-					parent_account = frappe.db.get_value("Account",
-						filters={"account_type": "Tax", "root_type": "Liability", "is_group": 0, "company": company}, fieldname="parent_account")
+					parent_account = frappe.db.get_value(
+						"Account",
+						filters={"account_type": "Tax", "root_type": "Liability", "is_group": 0, "company": company},
+						fieldname="parent_account",
+					)
 				if not parent_account:
-					parent_account = frappe.db.get_value("Account",
-						filters={"account_type": "Tax", "root_type": "Liability", "is_group": 1, "company": company})
+					parent_account = frappe.db.get_value(
+						"Account",
+						filters={"account_type": "Tax", "root_type": "Liability", "is_group": 1, "company": company},
+					)
 				filters = {
 					"account_name": account_name,
 					"company": company,
 					"account_type": "Tax",
-					"parent_account": parent_account
+					"parent_account": parent_account,
 				}
 				tax_type = frappe.db.get_value("Account", filters)
 				if not tax_type:
@@ -125,28 +167,38 @@
 						account.insert()
 						tax_type = account.name
 					except frappe.DuplicateEntryError:
-						tax_type = frappe.db.get_value("Account", {"account_name": account_name, "company": company}, "name")
+						tax_type = frappe.db.get_value(
+							"Account", {"account_name": account_name, "company": company}, "name"
+						)
 
 		account_type = frappe.get_cached_value("Account", tax_type, "account_type")
 
-		if tax_type and account_type in ('Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation'):
+		if tax_type and account_type in (
+			"Tax",
+			"Chargeable",
+			"Income Account",
+			"Expense Account",
+			"Expenses Included In Valuation",
+		):
 			if tax_type not in tax_types:
 				item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate})
 				tax_types.append(tax_type)
-			item_tax_templates.setdefault(item_tax_template.title, {})
-			item_tax_templates[item_tax_template.title][tax_type] = tax_rate
+			item_tax_templates.setdefault(item_tax_template_name, {})
+			item_tax_templates[item_tax_template_name][tax_type] = tax_rate
+
 	if item_tax_template.get("taxes"):
 		item_tax_template.save()
 		return item_tax_template.name
 
+
 def get_company(company_abbr, parenttype=None, parent=None):
 	if parenttype and parent:
-		company = frappe.get_cached_value(parenttype, parent, 'company')
+		company = frappe.get_cached_value(parenttype, parent, "company")
 	else:
 		company = frappe.db.get_value("Company", filters={"abbr": company_abbr})
 
 	if not company:
-		companies = frappe.get_all('Company')
+		companies = frappe.get_all("Company")
 		if len(companies) == 1:
 			company = companies[0].name
 
diff --git a/erpnext/patches/v12_0/move_plaid_settings_to_doctype.py b/erpnext/patches/v12_0/move_plaid_settings_to_doctype.py
index c396891..6788cb2 100644
--- a/erpnext/patches/v12_0/move_plaid_settings_to_doctype.py
+++ b/erpnext/patches/v12_0/move_plaid_settings_to_doctype.py
@@ -12,10 +12,12 @@
 		if not (frappe.conf.plaid_client_id and frappe.conf.plaid_env and frappe.conf.plaid_secret):
 			plaid_settings.enabled = 0
 		else:
-			plaid_settings.update({
-				"plaid_client_id": frappe.conf.plaid_client_id,
-				"plaid_env": frappe.conf.plaid_env,
-				"plaid_secret": frappe.conf.plaid_secret
-			})
+			plaid_settings.update(
+				{
+					"plaid_client_id": frappe.conf.plaid_client_id,
+					"plaid_env": frappe.conf.plaid_env,
+					"plaid_secret": frappe.conf.plaid_secret,
+				}
+			)
 		plaid_settings.flags.ignore_mandatory = True
 		plaid_settings.save()
diff --git a/erpnext/patches/v12_0/move_target_distribution_from_parent_to_child.py b/erpnext/patches/v12_0/move_target_distribution_from_parent_to_child.py
index 36fe18d..7192610 100644
--- a/erpnext/patches/v12_0/move_target_distribution_from_parent_to_child.py
+++ b/erpnext/patches/v12_0/move_target_distribution_from_parent_to_child.py
@@ -6,19 +6,23 @@
 
 
 def execute():
-    frappe.reload_doc("setup", "doctype", "target_detail")
-    frappe.reload_doc("core", "doctype", "prepared_report")
+	frappe.reload_doc("setup", "doctype", "target_detail")
+	frappe.reload_doc("core", "doctype", "prepared_report")
 
-    for d in ['Sales Person', 'Sales Partner', 'Territory']:
-        frappe.db.sql("""
+	for d in ["Sales Person", "Sales Partner", "Territory"]:
+		frappe.db.sql(
+			"""
             UPDATE `tab{child_doc}`, `tab{parent_doc}`
             SET
                 `tab{child_doc}`.distribution_id = `tab{parent_doc}`.distribution_id
             WHERE
                 `tab{child_doc}`.parent = `tab{parent_doc}`.name
                 and `tab{parent_doc}`.distribution_id is not null and `tab{parent_doc}`.distribution_id != ''
-        """.format(parent_doc = d, child_doc = "Target Detail"))
+        """.format(
+				parent_doc=d, child_doc="Target Detail"
+			)
+		)
 
-    frappe.delete_doc("Report", "Sales Partner-wise Transaction Summary")
-    frappe.delete_doc("Report", "Sales Person Target Variance Item Group-Wise")
-    frappe.delete_doc("Report", "Territory Target Variance Item Group-Wise")
+	frappe.delete_doc("Report", "Sales Partner-wise Transaction Summary")
+	frappe.delete_doc("Report", "Sales Person Target Variance Item Group-Wise")
+	frappe.delete_doc("Report", "Territory Target Variance Item Group-Wise")
diff --git a/erpnext/patches/v12_0/purchase_receipt_status.py b/erpnext/patches/v12_0/purchase_receipt_status.py
index 459221e..3b828d6 100644
--- a/erpnext/patches/v12_0/purchase_receipt_status.py
+++ b/erpnext/patches/v12_0/purchase_receipt_status.py
@@ -7,6 +7,7 @@
 
 logger = frappe.logger("patch", allow_site=True, file_count=50)
 
+
 def execute():
 	affected_purchase_receipts = frappe.db.sql(
 		"""select name from `tabPurchase Receipt`
@@ -16,13 +17,13 @@
 	if not affected_purchase_receipts:
 		return
 
-	logger.info("purchase_receipt_status: begin patch, PR count: {}"
-				.format(len(affected_purchase_receipts)))
+	logger.info(
+		"purchase_receipt_status: begin patch, PR count: {}".format(len(affected_purchase_receipts))
+	)
 
 	frappe.reload_doc("stock", "doctype", "Purchase Receipt")
 	frappe.reload_doc("stock", "doctype", "Purchase Receipt Item")
 
-
 	for pr in affected_purchase_receipts:
 		pr_name = pr[0]
 		logger.info("purchase_receipt_status: patching PR - {}".format(pr_name))
diff --git a/erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py b/erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py
index 8dec9ff..661152b 100644
--- a/erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py
+++ b/erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py
@@ -4,13 +4,18 @@
 
 
 def execute():
-	bin_details = frappe.db.sql("""
+	bin_details = frappe.db.sql(
+		"""
 		SELECT item_code, warehouse
-		FROM `tabBin`""",as_dict=1)
+		FROM `tabBin`""",
+		as_dict=1,
+	)
 
 	for entry in bin_details:
 		if not (entry.item_code and entry.warehouse):
 			continue
-		update_bin_qty(entry.get("item_code"), entry.get("warehouse"), {
-			"indented_qty": get_indented_qty(entry.get("item_code"), entry.get("warehouse"))
-		})
+		update_bin_qty(
+			entry.get("item_code"),
+			entry.get("warehouse"),
+			{"indented_qty": get_indented_qty(entry.get("item_code"), entry.get("warehouse"))},
+		)
diff --git a/erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py b/erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py
index 12768a6..b18f4eb 100644
--- a/erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py
+++ b/erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py
@@ -4,10 +4,15 @@
 def execute():
 	frappe.reload_doc("accounts", "doctype", "tax_category")
 	frappe.reload_doc("stock", "doctype", "item_manufacturer")
-	company = frappe.get_all('Company', filters = {'country': 'India'})
+	company = frappe.get_all("Company", filters={"country": "India"})
 	if not company:
 		return
 	if frappe.db.exists("Custom Field", "Company-bank_remittance_section"):
-		deprecated_fields = ['bank_remittance_section', 'client_code', 'remittance_column_break', 'product_code']
+		deprecated_fields = [
+			"bank_remittance_section",
+			"client_code",
+			"remittance_column_break",
+			"product_code",
+		]
 		for i in range(len(deprecated_fields)):
-			frappe.delete_doc("Custom Field", 'Company-'+deprecated_fields[i])
+			frappe.delete_doc("Custom Field", "Company-" + deprecated_fields[i])
diff --git a/erpnext/patches/v12_0/remove_denied_leaves_from_leave_ledger.py b/erpnext/patches/v12_0/remove_denied_leaves_from_leave_ledger.py
index d1d4bcc..4029a3f 100644
--- a/erpnext/patches/v12_0/remove_denied_leaves_from_leave_ledger.py
+++ b/erpnext/patches/v12_0/remove_denied_leaves_from_leave_ledger.py
@@ -6,8 +6,8 @@
 
 
 def execute():
-	''' Delete leave ledger entry created
-		via leave applications with status != Approved '''
+	"""Delete leave ledger entry created
+	via leave applications with status != Approved"""
 	if not frappe.db.a_row_exists("Leave Ledger Entry"):
 		return
 
@@ -15,14 +15,21 @@
 	if leave_application_list:
 		delete_denied_leaves_from_leave_ledger_entry(leave_application_list)
 
+
 def get_denied_leave_application_list():
-	return frappe.db.sql_list(''' Select name from `tabLeave Application` where status <> 'Approved' ''')
+	return frappe.db.sql_list(
+		""" Select name from `tabLeave Application` where status <> 'Approved' """
+	)
+
 
 def delete_denied_leaves_from_leave_ledger_entry(leave_application_list):
 	if leave_application_list:
-		frappe.db.sql(''' Delete
+		frappe.db.sql(
+			""" Delete
 			FROM `tabLeave Ledger Entry`
 			WHERE
 				transaction_type = 'Leave Application'
-				AND transaction_name in (%s) ''' % (', '.join(['%s'] * len(leave_application_list))), #nosec
-				tuple(leave_application_list))
+				AND transaction_name in (%s) """
+			% (", ".join(["%s"] * len(leave_application_list))),  # nosec
+			tuple(leave_application_list),
+		)
diff --git a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py
index 6ad68cc..8247734 100644
--- a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py
+++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py
@@ -7,16 +7,18 @@
 
 def execute():
 	"""Delete duplicate leave ledger entries of type allocation created."""
-	frappe.reload_doc('hr', 'doctype', 'leave_ledger_entry')
+	frappe.reload_doc("hr", "doctype", "leave_ledger_entry")
 	if not frappe.db.a_row_exists("Leave Ledger Entry"):
 		return
 
 	duplicate_records_list = get_duplicate_records()
 	delete_duplicate_ledger_entries(duplicate_records_list)
 
+
 def get_duplicate_records():
 	"""Fetch all but one duplicate records from the list of expired leave allocation."""
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		SELECT name, employee, transaction_name, leave_type, is_carry_forward, from_date, to_date
 		FROM `tabLeave Ledger Entry`
 		WHERE
@@ -29,13 +31,17 @@
 			count(name) > 1
 		ORDER BY
 			creation
-	""")
+	"""
+	)
+
 
 def delete_duplicate_ledger_entries(duplicate_records_list):
 	"""Delete duplicate leave ledger entries."""
-	if not duplicate_records_list: return
+	if not duplicate_records_list:
+		return
 	for d in duplicate_records_list:
-		frappe.db.sql('''
+		frappe.db.sql(
+			"""
 			DELETE FROM `tabLeave Ledger Entry`
 			WHERE name != %s
 				AND employee = %s
@@ -44,4 +50,6 @@
 				AND is_carry_forward = %s
 				AND from_date = %s
 				AND to_date = %s
-		''', tuple(d))
+		""",
+			tuple(d),
+		)
diff --git a/erpnext/patches/v12_0/rename_account_type_doctype.py b/erpnext/patches/v12_0/rename_account_type_doctype.py
index e33a1d0..ab19554 100644
--- a/erpnext/patches/v12_0/rename_account_type_doctype.py
+++ b/erpnext/patches/v12_0/rename_account_type_doctype.py
@@ -2,6 +2,6 @@
 
 
 def execute():
-	frappe.rename_doc('DocType', 'Account Type', 'Bank Account Type', force=True)
-	frappe.rename_doc('DocType', 'Account Subtype', 'Bank Account Subtype', force=True)
-	frappe.reload_doc('accounts', 'doctype', 'bank_account')
+	frappe.rename_doc("DocType", "Account Type", "Bank Account Type", force=True)
+	frappe.rename_doc("DocType", "Account Subtype", "Bank Account Subtype", force=True)
+	frappe.reload_doc("accounts", "doctype", "bank_account")
diff --git a/erpnext/patches/v12_0/rename_bank_account_field_in_journal_entry_account.py b/erpnext/patches/v12_0/rename_bank_account_field_in_journal_entry_account.py
index a5d986a..9268753 100644
--- a/erpnext/patches/v12_0/rename_bank_account_field_in_journal_entry_account.py
+++ b/erpnext/patches/v12_0/rename_bank_account_field_in_journal_entry_account.py
@@ -7,12 +7,13 @@
 
 
 def execute():
-	''' Change the fieldname from bank_account_no to bank_account '''
+	"""Change the fieldname from bank_account_no to bank_account"""
 	if not frappe.get_meta("Journal Entry Account").has_field("bank_account"):
 		frappe.reload_doc("Accounts", "doctype", "Journal Entry Account")
 		update_journal_entry_account_fieldname()
 
+
 def update_journal_entry_account_fieldname():
-	''' maps data from old field to the new field '''
-	if frappe.db.has_column('Journal Entry Account', 'bank_account_no'):
+	"""maps data from old field to the new field"""
+	if frappe.db.has_column("Journal Entry Account", "bank_account_no"):
 		rename_field("Journal Entry Account", "bank_account_no", "bank_account")
diff --git a/erpnext/patches/v12_0/rename_bank_reconciliation.py b/erpnext/patches/v12_0/rename_bank_reconciliation.py
index 51ff0c8..aacd6e8 100644
--- a/erpnext/patches/v12_0/rename_bank_reconciliation.py
+++ b/erpnext/patches/v12_0/rename_bank_reconciliation.py
@@ -7,8 +7,8 @@
 
 def execute():
 	if frappe.db.table_exists("Bank Reconciliation"):
-		frappe.rename_doc('DocType', 'Bank Reconciliation', 'Bank Clearance', force=True)
-		frappe.reload_doc('Accounts', 'doctype', 'Bank Clearance')
+		frappe.rename_doc("DocType", "Bank Reconciliation", "Bank Clearance", force=True)
+		frappe.reload_doc("Accounts", "doctype", "Bank Clearance")
 
-		frappe.rename_doc('DocType', 'Bank Reconciliation Detail', 'Bank Clearance Detail', force=True)
-		frappe.reload_doc('Accounts', 'doctype', 'Bank Clearance Detail')
+		frappe.rename_doc("DocType", "Bank Reconciliation Detail", "Bank Clearance Detail", force=True)
+		frappe.reload_doc("Accounts", "doctype", "Bank Clearance Detail")
diff --git a/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py b/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py
index 629cd5b..e2a3887 100644
--- a/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py
+++ b/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py
@@ -5,11 +5,24 @@
 
 
 def _rename_single_field(**kwargs):
-	count = frappe.db.sql("SELECT COUNT(*) FROM tabSingles WHERE doctype='{doctype}' AND field='{new_name}';".format(**kwargs))[0][0] #nosec
+	count = frappe.db.sql(
+		"SELECT COUNT(*) FROM tabSingles WHERE doctype='{doctype}' AND field='{new_name}';".format(
+			**kwargs
+		)
+	)[0][
+		0
+	]  # nosec
 	if count == 0:
-		frappe.db.sql("UPDATE tabSingles SET field='{new_name}' WHERE doctype='{doctype}' AND field='{old_name}';".format(**kwargs)) #nosec
+		frappe.db.sql(
+			"UPDATE tabSingles SET field='{new_name}' WHERE doctype='{doctype}' AND field='{old_name}';".format(
+				**kwargs
+			)
+		)  # nosec
+
 
 def execute():
-	_rename_single_field(doctype = "Bank Clearance", old_name = "bank_account" , new_name = "account")
-	_rename_single_field(doctype = "Bank Clearance", old_name = "bank_account_no", new_name = "bank_account")
+	_rename_single_field(doctype="Bank Clearance", old_name="bank_account", new_name="account")
+	_rename_single_field(
+		doctype="Bank Clearance", old_name="bank_account_no", new_name="bank_account"
+	)
 	frappe.reload_doc("Accounts", "doctype", "Bank Clearance")
diff --git a/erpnext/patches/v12_0/rename_lost_reason_detail.py b/erpnext/patches/v12_0/rename_lost_reason_detail.py
index 96ae979..2f7f842 100644
--- a/erpnext/patches/v12_0/rename_lost_reason_detail.py
+++ b/erpnext/patches/v12_0/rename_lost_reason_detail.py
@@ -2,17 +2,23 @@
 
 
 def execute():
-    if frappe.db.exists("DocType", "Lost Reason Detail"):
-        frappe.reload_doc("crm", "doctype", "opportunity_lost_reason")
-        frappe.reload_doc("crm", "doctype", "opportunity_lost_reason_detail")
-        frappe.reload_doc("setup", "doctype", "quotation_lost_reason_detail")
+	if frappe.db.exists("DocType", "Lost Reason Detail"):
+		frappe.reload_doc("crm", "doctype", "opportunity_lost_reason")
+		frappe.reload_doc("crm", "doctype", "opportunity_lost_reason_detail")
+		frappe.reload_doc("setup", "doctype", "quotation_lost_reason_detail")
 
-        frappe.db.sql("""INSERT INTO `tabOpportunity Lost Reason Detail` SELECT * FROM `tabLost Reason Detail` WHERE `parenttype` = 'Opportunity'""")
+		frappe.db.sql(
+			"""INSERT INTO `tabOpportunity Lost Reason Detail` SELECT * FROM `tabLost Reason Detail` WHERE `parenttype` = 'Opportunity'"""
+		)
 
-        frappe.db.sql("""INSERT INTO `tabQuotation Lost Reason Detail` SELECT * FROM `tabLost Reason Detail` WHERE `parenttype` = 'Quotation'""")
+		frappe.db.sql(
+			"""INSERT INTO `tabQuotation Lost Reason Detail` SELECT * FROM `tabLost Reason Detail` WHERE `parenttype` = 'Quotation'"""
+		)
 
-        frappe.db.sql("""INSERT INTO `tabQuotation Lost Reason` (`name`, `creation`, `modified`, `modified_by`, `owner`, `docstatus`, `parent`, `parentfield`, `parenttype`, `idx`, `_comments`, `_assign`, `_user_tags`, `_liked_by`, `order_lost_reason`)
+		frappe.db.sql(
+			"""INSERT INTO `tabQuotation Lost Reason` (`name`, `creation`, `modified`, `modified_by`, `owner`, `docstatus`, `parent`, `parentfield`, `parenttype`, `idx`, `_comments`, `_assign`, `_user_tags`, `_liked_by`, `order_lost_reason`)
             SELECT o.`name`, o.`creation`, o.`modified`, o.`modified_by`, o.`owner`, o.`docstatus`, o.`parent`, o.`parentfield`, o.`parenttype`, o.`idx`, o.`_comments`, o.`_assign`, o.`_user_tags`, o.`_liked_by`, o.`lost_reason`
-            FROM `tabOpportunity Lost Reason` o LEFT JOIN `tabQuotation Lost Reason` q ON q.name = o.name WHERE q.name IS NULL""")
+            FROM `tabOpportunity Lost Reason` o LEFT JOIN `tabQuotation Lost Reason` q ON q.name = o.name WHERE q.name IS NULL"""
+		)
 
-        frappe.delete_doc("DocType", "Lost Reason Detail")
+		frappe.delete_doc("DocType", "Lost Reason Detail")
diff --git a/erpnext/patches/v12_0/rename_pos_closing_doctype.py b/erpnext/patches/v12_0/rename_pos_closing_doctype.py
index f5f0112..fb80f8d 100644
--- a/erpnext/patches/v12_0/rename_pos_closing_doctype.py
+++ b/erpnext/patches/v12_0/rename_pos_closing_doctype.py
@@ -7,17 +7,19 @@
 def execute():
 	if frappe.db.table_exists("POS Closing Voucher"):
 		if not frappe.db.exists("DocType", "POS Closing Entry"):
-			frappe.rename_doc('DocType', 'POS Closing Voucher', 'POS Closing Entry', force=True)
+			frappe.rename_doc("DocType", "POS Closing Voucher", "POS Closing Entry", force=True)
 
-		if not frappe.db.exists('DocType', 'POS Closing Entry Taxes'):
-			frappe.rename_doc('DocType', 'POS Closing Voucher Taxes', 'POS Closing Entry Taxes', force=True)
+		if not frappe.db.exists("DocType", "POS Closing Entry Taxes"):
+			frappe.rename_doc("DocType", "POS Closing Voucher Taxes", "POS Closing Entry Taxes", force=True)
 
-		if not frappe.db.exists('DocType', 'POS Closing Voucher Details'):
-			frappe.rename_doc('DocType', 'POS Closing Voucher Details', 'POS Closing Entry Detail', force=True)
+		if not frappe.db.exists("DocType", "POS Closing Voucher Details"):
+			frappe.rename_doc(
+				"DocType", "POS Closing Voucher Details", "POS Closing Entry Detail", force=True
+			)
 
-		frappe.reload_doc('Accounts', 'doctype', 'POS Closing Entry')
-		frappe.reload_doc('Accounts', 'doctype', 'POS Closing Entry Taxes')
-		frappe.reload_doc('Accounts', 'doctype', 'POS Closing Entry Detail')
+		frappe.reload_doc("Accounts", "doctype", "POS Closing Entry")
+		frappe.reload_doc("Accounts", "doctype", "POS Closing Entry Taxes")
+		frappe.reload_doc("Accounts", "doctype", "POS Closing Entry Detail")
 
 	if frappe.db.exists("DocType", "POS Closing Voucher"):
 		frappe.delete_doc("DocType", "POS Closing Voucher")
diff --git a/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py b/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py
index 87630fb..8d4c013 100644
--- a/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py
+++ b/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py
@@ -5,16 +5,17 @@
 import frappe
 
 doctypes = {
-    'Price Discount Slab': 'Promotional Scheme Price Discount',
-    'Product Discount Slab': 'Promotional Scheme Product Discount',
-    'Apply Rule On Item Code': 'Pricing Rule Item Code',
-    'Apply Rule On Item Group': 'Pricing Rule Item Group',
-    'Apply Rule On Brand': 'Pricing Rule Brand'
+	"Price Discount Slab": "Promotional Scheme Price Discount",
+	"Product Discount Slab": "Promotional Scheme Product Discount",
+	"Apply Rule On Item Code": "Pricing Rule Item Code",
+	"Apply Rule On Item Group": "Pricing Rule Item Group",
+	"Apply Rule On Brand": "Pricing Rule Brand",
 }
 
+
 def execute():
-    for old_doc, new_doc in doctypes.items():
-        if not frappe.db.table_exists(new_doc) and frappe.db.table_exists(old_doc):
-            frappe.rename_doc('DocType', old_doc, new_doc)
-            frappe.reload_doc("accounts", "doctype", frappe.scrub(new_doc))
-            frappe.delete_doc("DocType", old_doc)
+	for old_doc, new_doc in doctypes.items():
+		if not frappe.db.table_exists(new_doc) and frappe.db.table_exists(old_doc):
+			frappe.rename_doc("DocType", old_doc, new_doc)
+			frappe.reload_doc("accounts", "doctype", frappe.scrub(new_doc))
+			frappe.delete_doc("DocType", old_doc)
diff --git a/erpnext/patches/v12_0/rename_tolerance_fields.py b/erpnext/patches/v12_0/rename_tolerance_fields.py
index ca2427b..ef1ba65 100644
--- a/erpnext/patches/v12_0/rename_tolerance_fields.py
+++ b/erpnext/patches/v12_0/rename_tolerance_fields.py
@@ -7,8 +7,8 @@
 	frappe.reload_doc("stock", "doctype", "stock_settings")
 	frappe.reload_doc("accounts", "doctype", "accounts_settings")
 
-	rename_field('Stock Settings', "tolerance", "over_delivery_receipt_allowance")
-	rename_field('Item', "tolerance", "over_delivery_receipt_allowance")
+	rename_field("Stock Settings", "tolerance", "over_delivery_receipt_allowance")
+	rename_field("Item", "tolerance", "over_delivery_receipt_allowance")
 
 	qty_allowance = frappe.db.get_single_value("Stock Settings", "over_delivery_receipt_allowance")
 	frappe.db.set_value("Accounts Settings", None, "over_delivery_receipt_allowance", qty_allowance)
diff --git a/erpnext/patches/v12_0/replace_accounting_with_accounts_in_home_settings.py b/erpnext/patches/v12_0/replace_accounting_with_accounts_in_home_settings.py
index ff332f7..21dd258 100644
--- a/erpnext/patches/v12_0/replace_accounting_with_accounts_in_home_settings.py
+++ b/erpnext/patches/v12_0/replace_accounting_with_accounts_in_home_settings.py
@@ -2,5 +2,7 @@
 
 
 def execute():
-	frappe.db.sql("""UPDATE `tabUser` SET `home_settings` = REPLACE(`home_settings`, 'Accounting', 'Accounts')""")
-	frappe.cache().delete_key('home_settings')
+	frappe.db.sql(
+		"""UPDATE `tabUser` SET `home_settings` = REPLACE(`home_settings`, 'Accounting', 'Accounts')"""
+	)
+	frappe.cache().delete_key("home_settings")
diff --git a/erpnext/patches/v12_0/repost_stock_ledger_entries_for_target_warehouse.py b/erpnext/patches/v12_0/repost_stock_ledger_entries_for_target_warehouse.py
index 198963d..a4a8587 100644
--- a/erpnext/patches/v12_0/repost_stock_ledger_entries_for_target_warehouse.py
+++ b/erpnext/patches/v12_0/repost_stock_ledger_entries_for_target_warehouse.py
@@ -6,51 +6,79 @@
 
 
 def execute():
-	warehouse_perm = frappe.get_all("User Permission",
-		fields=["count(*) as p_count", "is_default", "user"], filters={"allow": "Warehouse"}, group_by="user")
+	warehouse_perm = frappe.get_all(
+		"User Permission",
+		fields=["count(*) as p_count", "is_default", "user"],
+		filters={"allow": "Warehouse"},
+		group_by="user",
+	)
 
 	if not warehouse_perm:
 		return
 
 	execute_patch = False
 	for perm_data in warehouse_perm:
-		if perm_data.p_count == 1 or (perm_data.p_count > 1 and frappe.get_all("User Permission",
-			filters = {"user": perm_data.user, "allow": "warehouse", "is_default": 1}, limit=1)):
+		if perm_data.p_count == 1 or (
+			perm_data.p_count > 1
+			and frappe.get_all(
+				"User Permission",
+				filters={"user": perm_data.user, "allow": "warehouse", "is_default": 1},
+				limit=1,
+			)
+		):
 			execute_patch = True
 			break
 
-	if not execute_patch: return
+	if not execute_patch:
+		return
 
 	for doctype in ["Sales Invoice", "Delivery Note"]:
-		if not frappe.get_meta(doctype + ' Item').get_field("target_warehouse").hidden: continue
+		if not frappe.get_meta(doctype + " Item").get_field("target_warehouse").hidden:
+			continue
 
 		cond = ""
 		if doctype == "Sales Invoice":
 			cond = " AND parent_doc.update_stock = 1"
 
-		data = frappe.db.sql(""" SELECT parent_doc.name as name, child_doc.name as child_name
+		data = frappe.db.sql(
+			""" SELECT parent_doc.name as name, child_doc.name as child_name
 			FROM
 				`tab{doctype}` parent_doc, `tab{doctype} Item` child_doc
 			WHERE
 				parent_doc.name = child_doc.parent AND parent_doc.docstatus < 2
 				AND child_doc.target_warehouse is not null AND child_doc.target_warehouse != ''
 				AND child_doc.creation > '2020-04-16' {cond}
-		""".format(doctype=doctype, cond=cond), as_dict=1)
+		""".format(
+				doctype=doctype, cond=cond
+			),
+			as_dict=1,
+		)
 
 		if data:
 			names = [d.child_name for d in data]
-			frappe.db.sql(""" UPDATE `tab{0} Item` set target_warehouse = null
-				WHERE name in ({1}) """.format(doctype, ','.join(["%s"] * len(names) )), tuple(names))
+			frappe.db.sql(
+				""" UPDATE `tab{0} Item` set target_warehouse = null
+				WHERE name in ({1}) """.format(
+					doctype, ",".join(["%s"] * len(names))
+				),
+				tuple(names),
+			)
 
-			frappe.db.sql(""" UPDATE `tabPacked Item` set target_warehouse = null
+			frappe.db.sql(
+				""" UPDATE `tabPacked Item` set target_warehouse = null
 				WHERE parenttype = '{0}' and parent_detail_docname in ({1})
-			""".format(doctype, ','.join(["%s"] * len(names) )), tuple(names))
+			""".format(
+					doctype, ",".join(["%s"] * len(names))
+				),
+				tuple(names),
+			)
 
 			parent_names = list(set([d.name for d in data]))
 
 			for d in parent_names:
 				doc = frappe.get_doc(doctype, d)
-				if doc.docstatus != 1: continue
+				if doc.docstatus != 1:
+					continue
 
 				doc.docstatus = 2
 				doc.update_stock_ledger()
@@ -61,9 +89,13 @@
 				doc.update_stock_ledger()
 				doc.make_gl_entries()
 
-	if frappe.get_meta('Sales Order Item').get_field("target_warehouse").hidden:
-		frappe.db.sql(""" UPDATE `tabSales Order Item` set target_warehouse = null
-			WHERE creation > '2020-04-16' and docstatus < 2 """)
+	if frappe.get_meta("Sales Order Item").get_field("target_warehouse").hidden:
+		frappe.db.sql(
+			""" UPDATE `tabSales Order Item` set target_warehouse = null
+			WHERE creation > '2020-04-16' and docstatus < 2 """
+		)
 
-		frappe.db.sql(""" UPDATE `tabPacked Item` set target_warehouse = null
-			WHERE creation > '2020-04-16' and docstatus < 2 and parenttype = 'Sales Order' """)
+		frappe.db.sql(
+			""" UPDATE `tabPacked Item` set target_warehouse = null
+			WHERE creation > '2020-04-16' and docstatus < 2 and parenttype = 'Sales Order' """
+		)
diff --git a/erpnext/patches/v12_0/set_against_blanket_order_in_sales_and_purchase_order.py b/erpnext/patches/v12_0/set_against_blanket_order_in_sales_and_purchase_order.py
index b76e34a..d88593b 100644
--- a/erpnext/patches/v12_0/set_against_blanket_order_in_sales_and_purchase_order.py
+++ b/erpnext/patches/v12_0/set_against_blanket_order_in_sales_and_purchase_order.py
@@ -3,12 +3,16 @@
 
 def execute():
 
-	frappe.reload_doc('selling', 'doctype', 'sales_order_item', force=True)
-	frappe.reload_doc('buying', 'doctype', 'purchase_order_item', force=True)
+	frappe.reload_doc("selling", "doctype", "sales_order_item", force=True)
+	frappe.reload_doc("buying", "doctype", "purchase_order_item", force=True)
 
-	for doctype in ('Sales Order Item', 'Purchase Order Item'):
-		frappe.db.sql("""
+	for doctype in ("Sales Order Item", "Purchase Order Item"):
+		frappe.db.sql(
+			"""
 			UPDATE `tab{0}`
 			SET against_blanket_order = 1
 			WHERE ifnull(blanket_order, '') != ''
-		""".format(doctype))
+		""".format(
+				doctype
+			)
+		)
diff --git a/erpnext/patches/v12_0/set_automatically_process_deferred_accounting_in_accounts_settings.py b/erpnext/patches/v12_0/set_automatically_process_deferred_accounting_in_accounts_settings.py
index 8f29fc8..37af989 100644
--- a/erpnext/patches/v12_0/set_automatically_process_deferred_accounting_in_accounts_settings.py
+++ b/erpnext/patches/v12_0/set_automatically_process_deferred_accounting_in_accounts_settings.py
@@ -4,4 +4,6 @@
 def execute():
 	frappe.reload_doc("accounts", "doctype", "accounts_settings")
 
-	frappe.db.set_value("Accounts Settings", None, "automatically_process_deferred_accounting_entry", 1)
+	frappe.db.set_value(
+		"Accounts Settings", None, "automatically_process_deferred_accounting_entry", 1
+	)
diff --git a/erpnext/patches/v12_0/set_cost_center_in_child_table_of_expense_claim.py b/erpnext/patches/v12_0/set_cost_center_in_child_table_of_expense_claim.py
index d3045a1..a5b4c66 100644
--- a/erpnext/patches/v12_0/set_cost_center_in_child_table_of_expense_claim.py
+++ b/erpnext/patches/v12_0/set_cost_center_in_child_table_of_expense_claim.py
@@ -2,9 +2,11 @@
 
 
 def execute():
-	frappe.reload_doc('hr', 'doctype', 'expense_claim_detail')
-	frappe.db.sql("""
+	frappe.reload_doc("hr", "doctype", "expense_claim_detail")
+	frappe.db.sql(
+		"""
 		UPDATE `tabExpense Claim Detail` child, `tabExpense Claim` par
 		SET child.cost_center = par.cost_center
 		WHERE child.parent = par.name
-	""")
+	"""
+	)
diff --git a/erpnext/patches/v12_0/set_cwip_and_delete_asset_settings.py b/erpnext/patches/v12_0/set_cwip_and_delete_asset_settings.py
index d1e0e45..952f64b 100644
--- a/erpnext/patches/v12_0/set_cwip_and_delete_asset_settings.py
+++ b/erpnext/patches/v12_0/set_cwip_and_delete_asset_settings.py
@@ -3,8 +3,8 @@
 
 
 def execute():
-	'''Get 'Disable CWIP Accounting value' from Asset Settings, set it in 'Enable Capital Work in Progress Accounting' field
-	in Company, delete Asset Settings '''
+	"""Get 'Disable CWIP Accounting value' from Asset Settings, set it in 'Enable Capital Work in Progress Accounting' field
+	in Company, delete Asset Settings"""
 
 	if frappe.db.exists("DocType", "Asset Settings"):
 		frappe.reload_doctype("Asset Category")
diff --git a/erpnext/patches/v12_0/set_default_batch_size.py b/erpnext/patches/v12_0/set_default_batch_size.py
index 6fb6945..ac3e2f4 100644
--- a/erpnext/patches/v12_0/set_default_batch_size.py
+++ b/erpnext/patches/v12_0/set_default_batch_size.py
@@ -2,18 +2,22 @@
 
 
 def execute():
-    frappe.reload_doc("manufacturing", "doctype", "bom_operation")
-    frappe.reload_doc("manufacturing", "doctype", "work_order_operation")
+	frappe.reload_doc("manufacturing", "doctype", "bom_operation")
+	frappe.reload_doc("manufacturing", "doctype", "work_order_operation")
 
-    frappe.db.sql("""
+	frappe.db.sql(
+		"""
         UPDATE
             `tabBOM Operation` bo
         SET
             bo.batch_size = 1
-    """)
-    frappe.db.sql("""
+    """
+	)
+	frappe.db.sql(
+		"""
         UPDATE
             `tabWork Order Operation` wop
         SET
             wop.batch_size = 1
-    """)
+    """
+	)
diff --git a/erpnext/patches/v12_0/set_default_homepage_type.py b/erpnext/patches/v12_0/set_default_homepage_type.py
index 1e4333a..d70b28e 100644
--- a/erpnext/patches/v12_0/set_default_homepage_type.py
+++ b/erpnext/patches/v12_0/set_default_homepage_type.py
@@ -2,4 +2,4 @@
 
 
 def execute():
-	frappe.db.set_value('Homepage', 'Homepage', 'hero_section_based_on', 'Default')
+	frappe.db.set_value("Homepage", "Homepage", "hero_section_based_on", "Default")
diff --git a/erpnext/patches/v12_0/set_employee_preferred_emails.py b/erpnext/patches/v12_0/set_employee_preferred_emails.py
index f6eb12e..a6159c6 100644
--- a/erpnext/patches/v12_0/set_employee_preferred_emails.py
+++ b/erpnext/patches/v12_0/set_employee_preferred_emails.py
@@ -2,9 +2,11 @@
 

 

 def execute():

-	employees = frappe.get_all("Employee",

+	employees = frappe.get_all(

+		"Employee",

 		filters={"prefered_email": ""},

-		fields=["name", "prefered_contact_email", "company_email", "personal_email", "user_id"])

+		fields=["name", "prefered_contact_email", "company_email", "personal_email", "user_id"],

+	)

 

 	for employee in employees:

 		if not employee.prefered_contact_email:

@@ -13,4 +15,6 @@
 		preferred_email_field = frappe.scrub(employee.prefered_contact_email)

 

 		preferred_email = employee.get(preferred_email_field)

-		frappe.db.set_value("Employee", employee.name, "prefered_email", preferred_email, update_modified=False)

+		frappe.db.set_value(

+			"Employee", employee.name, "prefered_email", preferred_email, update_modified=False

+		)

diff --git a/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py b/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py
index 50d9fee..9588e02 100644
--- a/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py
+++ b/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py
@@ -2,14 +2,19 @@
 
 
 def execute():
-	frappe.reload_doctype('Landed Cost Taxes and Charges')
+	frappe.reload_doctype("Landed Cost Taxes and Charges")
 
-	company_account_map = frappe._dict(frappe.db.sql("""
+	company_account_map = frappe._dict(
+		frappe.db.sql(
+			"""
 		SELECT name, expenses_included_in_valuation from `tabCompany`
-	"""))
+	"""
+		)
+	)
 
 	for company, account in company_account_map.items():
-		frappe.db.sql("""
+		frappe.db.sql(
+			"""
 			UPDATE
 				`tabLanded Cost Taxes and Charges` t, `tabLanded Cost Voucher` l
 			SET
@@ -18,9 +23,12 @@
 				l.docstatus = 1
 				AND l.company = %s
 				AND t.parent = l.name
-		""", (account, company))
+		""",
+			(account, company),
+		)
 
-		frappe.db.sql("""
+		frappe.db.sql(
+			"""
 			UPDATE
 				`tabLanded Cost Taxes and Charges` t, `tabStock Entry` s
 			SET
@@ -29,4 +37,6 @@
 				s.docstatus = 1
 				AND s.company = %s
 				AND t.parent = s.name
-		""", (account, company))
+		""",
+			(account, company),
+		)
diff --git a/erpnext/patches/v12_0/set_job_offer_applicant_email.py b/erpnext/patches/v12_0/set_job_offer_applicant_email.py
index 7dd8492..0e3b5c4 100644
--- a/erpnext/patches/v12_0/set_job_offer_applicant_email.py
+++ b/erpnext/patches/v12_0/set_job_offer_applicant_email.py
@@ -4,9 +4,11 @@
 def execute():
 	frappe.reload_doc("hr", "doctype", "job_offer")
 
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		UPDATE
 			`tabJob Offer` AS offer
 		SET
 			applicant_email = (SELECT email_id FROM `tabJob Applicant` WHERE name = offer.job_applicant)
-	""")
+	"""
+	)
diff --git a/erpnext/patches/v12_0/set_lead_title_field.py b/erpnext/patches/v12_0/set_lead_title_field.py
index 86e0003..eda3007 100644
--- a/erpnext/patches/v12_0/set_lead_title_field.py
+++ b/erpnext/patches/v12_0/set_lead_title_field.py
@@ -3,9 +3,11 @@
 
 def execute():
 	frappe.reload_doc("crm", "doctype", "lead")
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		UPDATE
 			`tabLead`
 		SET
 			title = IF(organization_lead = 1, company_name, lead_name)
-	""")
+	"""
+	)
diff --git a/erpnext/patches/v12_0/set_multi_uom_in_rfq.py b/erpnext/patches/v12_0/set_multi_uom_in_rfq.py
index a8e0ec1..4d19007 100644
--- a/erpnext/patches/v12_0/set_multi_uom_in_rfq.py
+++ b/erpnext/patches/v12_0/set_multi_uom_in_rfq.py
@@ -6,10 +6,12 @@
 
 
 def execute():
-	frappe.reload_doc('buying', 'doctype', 'request_for_quotation_item')
+	frappe.reload_doc("buying", "doctype", "request_for_quotation_item")
 
-	frappe.db.sql("""UPDATE `tabRequest for Quotation Item`
+	frappe.db.sql(
+		"""UPDATE `tabRequest for Quotation Item`
 			SET
 				stock_uom = uom,
 				conversion_factor = 1,
-				stock_qty = qty""")
+				stock_qty = qty"""
+	)
diff --git a/erpnext/patches/v12_0/set_payment_entry_status.py b/erpnext/patches/v12_0/set_payment_entry_status.py
index f879295..2a3a3ad 100644
--- a/erpnext/patches/v12_0/set_payment_entry_status.py
+++ b/erpnext/patches/v12_0/set_payment_entry_status.py
@@ -3,8 +3,10 @@
 
 def execute():
 	frappe.reload_doctype("Payment Entry")
-	frappe.db.sql("""update `tabPayment Entry` set status = CASE
+	frappe.db.sql(
+		"""update `tabPayment Entry` set status = CASE
 		WHEN docstatus = 1 THEN 'Submitted'
 		WHEN docstatus = 2 THEN 'Cancelled'
 		ELSE 'Draft'
-		END;""")
+		END;"""
+	)
diff --git a/erpnext/patches/v12_0/set_permission_einvoicing.py b/erpnext/patches/v12_0/set_permission_einvoicing.py
index 01cab14..65d7097 100644
--- a/erpnext/patches/v12_0/set_permission_einvoicing.py
+++ b/erpnext/patches/v12_0/set_permission_einvoicing.py
@@ -5,7 +5,7 @@
 
 
 def execute():
-	company = frappe.get_all('Company', filters = {'country': 'Italy'})
+	company = frappe.get_all("Company", filters={"country": "Italy"})
 
 	if not company:
 		return
@@ -14,6 +14,6 @@
 
 	frappe.reload_doc("regional", "doctype", "import_supplier_invoice")
 
-	add_permission('Import Supplier Invoice', 'Accounts Manager', 0)
-	update_permission_property('Import Supplier Invoice', 'Accounts Manager', 0, 'write', 1)
-	update_permission_property('Import Supplier Invoice', 'Accounts Manager', 0, 'create', 1)
+	add_permission("Import Supplier Invoice", "Accounts Manager", 0)
+	update_permission_property("Import Supplier Invoice", "Accounts Manager", 0, "write", 1)
+	update_permission_property("Import Supplier Invoice", "Accounts Manager", 0, "create", 1)
diff --git a/erpnext/patches/v12_0/set_priority_for_support.py b/erpnext/patches/v12_0/set_priority_for_support.py
index 6d7d099..a8a07e7 100644
--- a/erpnext/patches/v12_0/set_priority_for_support.py
+++ b/erpnext/patches/v12_0/set_priority_for_support.py
@@ -4,21 +4,20 @@
 def execute():
 	frappe.reload_doc("support", "doctype", "issue_priority")
 	frappe.reload_doc("support", "doctype", "service_level_priority")
-	frappe.reload_doc('support', 'doctype', 'issue')
+	frappe.reload_doc("support", "doctype", "issue")
 
 	set_issue_priority()
 	set_priority_for_issue()
 	set_priorities_service_level()
 	set_priorities_service_level_agreement()
 
+
 def set_issue_priority():
 	# Adds priority from issue to Issue Priority DocType as Priority is a new DocType.
 	for priority in frappe.get_meta("Issue").get_field("priority").options.split("\n"):
 		if priority and not frappe.db.exists("Issue Priority", priority):
-			frappe.get_doc({
-				"doctype": "Issue Priority",
-				"name": priority
-			}).insert(ignore_permissions=True)
+			frappe.get_doc({"doctype": "Issue Priority", "name": priority}).insert(ignore_permissions=True)
+
 
 def set_priority_for_issue():
 	# Sets priority for Issues as Select field is changed to Link field.
@@ -28,38 +27,63 @@
 	for issue in issue_priority:
 		frappe.db.set_value("Issue", issue.name, "priority", issue.priority)
 
+
 def set_priorities_service_level():
 	# Migrates "priority", "response_time", "response_time_period", "resolution_time", "resolution_time_period" to Child Table
 	# as a Service Level can have multiple priorities
 	try:
-		service_level_priorities = frappe.get_list("Service Level", fields=["name", "priority", "response_time", "response_time_period", "resolution_time", "resolution_time_period"])
+		service_level_priorities = frappe.get_list(
+			"Service Level",
+			fields=[
+				"name",
+				"priority",
+				"response_time",
+				"response_time_period",
+				"resolution_time",
+				"resolution_time_period",
+			],
+		)
 
 		frappe.reload_doc("support", "doctype", "service_level")
 		frappe.reload_doc("support", "doctype", "support_settings")
-		frappe.db.set_value('Support Settings', None, 'track_service_level_agreement', 1)
+		frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1)
 
 		for service_level in service_level_priorities:
 			if service_level:
 				doc = frappe.get_doc("Service Level", service_level.name)
 				if not doc.priorities:
-					doc.append("priorities", {
-						"priority": service_level.priority,
-						"default_priority": 1,
-						"response_time": service_level.response_time,
-						"response_time_period": service_level.response_time_period,
-						"resolution_time": service_level.resolution_time,
-						"resolution_time_period": service_level.resolution_time_period
-					})
+					doc.append(
+						"priorities",
+						{
+							"priority": service_level.priority,
+							"default_priority": 1,
+							"response_time": service_level.response_time,
+							"response_time_period": service_level.response_time_period,
+							"resolution_time": service_level.resolution_time,
+							"resolution_time_period": service_level.resolution_time_period,
+						},
+					)
 					doc.flags.ignore_validate = True
 					doc.save(ignore_permissions=True)
 	except frappe.db.TableMissingError:
 		frappe.reload_doc("support", "doctype", "service_level")
 
+
 def set_priorities_service_level_agreement():
 	# Migrates "priority", "response_time", "response_time_period", "resolution_time", "resolution_time_period" to Child Table
 	# as a Service Level Agreement can have multiple priorities
 	try:
-		service_level_agreement_priorities = frappe.get_list("Service Level Agreement", fields=["name", "priority", "response_time", "response_time_period", "resolution_time", "resolution_time_period"])
+		service_level_agreement_priorities = frappe.get_list(
+			"Service Level Agreement",
+			fields=[
+				"name",
+				"priority",
+				"response_time",
+				"response_time_period",
+				"resolution_time",
+				"resolution_time_period",
+			],
+		)
 
 		frappe.reload_doc("support", "doctype", "service_level_agreement")
 
@@ -71,14 +95,17 @@
 					doc.entity_type = "Customer"
 					doc.entity = doc.customer
 
-				doc.append("priorities", {
-					"priority": service_level_agreement.priority,
-					"default_priority": 1,
-					"response_time": service_level_agreement.response_time,
-					"response_time_period": service_level_agreement.response_time_period,
-					"resolution_time": service_level_agreement.resolution_time,
-					"resolution_time_period": service_level_agreement.resolution_time_period
-				})
+				doc.append(
+					"priorities",
+					{
+						"priority": service_level_agreement.priority,
+						"default_priority": 1,
+						"response_time": service_level_agreement.response_time,
+						"response_time_period": service_level_agreement.response_time_period,
+						"resolution_time": service_level_agreement.resolution_time,
+						"resolution_time_period": service_level_agreement.resolution_time_period,
+					},
+				)
 				doc.flags.ignore_validate = True
 				doc.save(ignore_permissions=True)
 	except frappe.db.TableMissingError:
diff --git a/erpnext/patches/v12_0/set_produced_qty_field_in_sales_order_for_work_order.py b/erpnext/patches/v12_0/set_produced_qty_field_in_sales_order_for_work_order.py
index 9c851dd..562ebed 100644
--- a/erpnext/patches/v12_0/set_produced_qty_field_in_sales_order_for_work_order.py
+++ b/erpnext/patches/v12_0/set_produced_qty_field_in_sales_order_for_work_order.py
@@ -4,12 +4,14 @@
 
 
 def execute():
-	frappe.reload_doctype('Sales Order Item')
-	frappe.reload_doctype('Sales Order')
+	frappe.reload_doctype("Sales Order Item")
+	frappe.reload_doctype("Sales Order")
 
-	for d in frappe.get_all('Work Order',
-		fields = ['sales_order', 'sales_order_item'],
-		filters={'sales_order': ('!=', ''), 'sales_order_item': ('!=', '')}):
+	for d in frappe.get_all(
+		"Work Order",
+		fields=["sales_order", "sales_order_item"],
+		filters={"sales_order": ("!=", ""), "sales_order_item": ("!=", "")},
+	):
 
 		# update produced qty in sales order
 		update_produced_qty_in_so_item(d.sales_order, d.sales_order_item)
diff --git a/erpnext/patches/v12_0/set_production_capacity_in_workstation.py b/erpnext/patches/v12_0/set_production_capacity_in_workstation.py
index bd2f7e2..0246c35 100644
--- a/erpnext/patches/v12_0/set_production_capacity_in_workstation.py
+++ b/erpnext/patches/v12_0/set_production_capacity_in_workstation.py
@@ -2,7 +2,9 @@
 
 
 def execute():
-    frappe.reload_doc("manufacturing", "doctype", "workstation")
+	frappe.reload_doc("manufacturing", "doctype", "workstation")
 
-    frappe.db.sql(""" UPDATE `tabWorkstation`
-        SET production_capacity = 1 """)
+	frappe.db.sql(
+		""" UPDATE `tabWorkstation`
+        SET production_capacity = 1 """
+	)
diff --git a/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py b/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py
index a15166e..2edf0f5 100644
--- a/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py
+++ b/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py
@@ -5,29 +5,36 @@
 
 def execute():
 
-	frappe.reload_doc('stock', 'doctype', 'delivery_note_item', force=True)
-	frappe.reload_doc('stock', 'doctype', 'purchase_receipt_item', force=True)
+	frappe.reload_doc("stock", "doctype", "delivery_note_item", force=True)
+	frappe.reload_doc("stock", "doctype", "purchase_receipt_item", force=True)
 
 	def map_rows(doc_row, return_doc_row, detail_field, doctype):
 		"""Map rows after identifying similar ones."""
 
-		frappe.db.sql(""" UPDATE `tab{doctype} Item` set {detail_field} = '{doc_row_name}'
-				where name = '{return_doc_row_name}'""" \
-			.format(doctype=doctype,
-					detail_field=detail_field,
-					doc_row_name=doc_row.get('name'),
-					return_doc_row_name=return_doc_row.get('name'))) #nosec
+		frappe.db.sql(
+			""" UPDATE `tab{doctype} Item` set {detail_field} = '{doc_row_name}'
+				where name = '{return_doc_row_name}'""".format(
+				doctype=doctype,
+				detail_field=detail_field,
+				doc_row_name=doc_row.get("name"),
+				return_doc_row_name=return_doc_row.get("name"),
+			)
+		)  # nosec
 
 	def row_is_mappable(doc_row, return_doc_row, detail_field):
 		"""Checks if two rows are similar enough to be mapped."""
 
 		if doc_row.item_code == return_doc_row.item_code and not return_doc_row.get(detail_field):
-			if doc_row.get('batch_no') and return_doc_row.get('batch_no') and doc_row.batch_no == return_doc_row.batch_no:
+			if (
+				doc_row.get("batch_no")
+				and return_doc_row.get("batch_no")
+				and doc_row.batch_no == return_doc_row.batch_no
+			):
 				return True
 
-			elif doc_row.get('serial_no') and return_doc_row.get('serial_no'):
-				doc_sn = doc_row.serial_no.split('\n')
-				return_doc_sn = return_doc_row.serial_no.split('\n')
+			elif doc_row.get("serial_no") and return_doc_row.get("serial_no"):
+				doc_sn = doc_row.serial_no.split("\n")
+				return_doc_sn = return_doc_row.serial_no.split("\n")
 
 				if set(doc_sn) & set(return_doc_sn):
 					# if two rows have serial nos in common, map them
@@ -42,12 +49,17 @@
 		"""Returns a map of documents and it's return documents.
 		Format => { 'document' : ['return_document_1','return_document_2'] }"""
 
-		return_against_documents = frappe.db.sql("""
+		return_against_documents = frappe.db.sql(
+			"""
 			SELECT
 				return_against as document, name as return_document
 			FROM `tab{doctype}`
 			WHERE
-				is_return = 1 and docstatus = 1""".format(doctype=doctype),as_dict=1) #nosec
+				is_return = 1 and docstatus = 1""".format(
+				doctype=doctype
+			),
+			as_dict=1,
+		)  # nosec
 
 		for entry in return_against_documents:
 			return_document_map[entry.document].append(entry.return_document)
@@ -58,7 +70,7 @@
 		"""Map each row of the original document in the return document."""
 		mapped = []
 		return_document_map = defaultdict(list)
-		detail_field = "purchase_receipt_item" if doctype=="Purchase Receipt" else "dn_detail"
+		detail_field = "purchase_receipt_item" if doctype == "Purchase Receipt" else "dn_detail"
 
 		child_doc = frappe.scrub("{0} Item".format(doctype))
 		frappe.reload_doc("stock", "doctype", child_doc)
@@ -67,25 +79,27 @@
 
 		count = 0
 
-		#iterate through original documents and its return documents
+		# iterate through original documents and its return documents
 		for docname in return_document_map:
 			doc_items = frappe.get_cached_doc(doctype, docname).get("items")
 			for return_doc in return_document_map[docname]:
 				return_doc_items = frappe.get_cached_doc(doctype, return_doc).get("items")
 
-				#iterate through return document items and original document items for mapping
+				# iterate through return document items and original document items for mapping
 				for return_item in return_doc_items:
 					for doc_item in doc_items:
-						if row_is_mappable(doc_item, return_item, detail_field) and doc_item.get('name') not in mapped:
+						if (
+							row_is_mappable(doc_item, return_item, detail_field) and doc_item.get("name") not in mapped
+						):
 							map_rows(doc_item, return_item, detail_field, doctype)
-							mapped.append(doc_item.get('name'))
+							mapped.append(doc_item.get("name"))
 							break
 						else:
 							continue
 
 			# commit after every 100 sql updates
 			count += 1
-			if count%100 == 0:
+			if count % 100 == 0:
 				frappe.db.commit()
 
 	set_document_detail_in_return_document("Purchase Receipt")
diff --git a/erpnext/patches/v12_0/set_quotation_status.py b/erpnext/patches/v12_0/set_quotation_status.py
index 91e77e4..bebedd3 100644
--- a/erpnext/patches/v12_0/set_quotation_status.py
+++ b/erpnext/patches/v12_0/set_quotation_status.py
@@ -3,5 +3,7 @@
 
 def execute():
 
-	frappe.db.sql(""" UPDATE `tabQuotation` set status = 'Open'
-		where docstatus = 1 and status = 'Submitted' """)
+	frappe.db.sql(
+		""" UPDATE `tabQuotation` set status = 'Open'
+		where docstatus = 1 and status = 'Submitted' """
+	)
diff --git a/erpnext/patches/v12_0/set_received_qty_in_material_request_as_per_stock_uom.py b/erpnext/patches/v12_0/set_received_qty_in_material_request_as_per_stock_uom.py
index d41134d..dcdd19f 100644
--- a/erpnext/patches/v12_0/set_received_qty_in_material_request_as_per_stock_uom.py
+++ b/erpnext/patches/v12_0/set_received_qty_in_material_request_as_per_stock_uom.py
@@ -2,13 +2,16 @@
 
 
 def execute():
-	purchase_receipts = frappe.db.sql("""
+	purchase_receipts = frappe.db.sql(
+		"""
 		SELECT
 			 parent from `tabPurchase Receipt Item`
 		WHERE
 			material_request is not null
 			AND docstatus=1
-		""",as_dict=1)
+		""",
+		as_dict=1,
+	)
 
 	purchase_receipts = set([d.parent for d in purchase_receipts])
 
@@ -16,15 +19,15 @@
 		doc = frappe.get_doc("Purchase Receipt", pr)
 		doc.status_updater = [
 			{
-			'source_dt': 'Purchase Receipt Item',
-			'target_dt': 'Material Request Item',
-			'join_field': 'material_request_item',
-			'target_field': 'received_qty',
-			'target_parent_dt': 'Material Request',
-			'target_parent_field': 'per_received',
-			'target_ref_field': 'stock_qty',
-			'source_field': 'stock_qty',
-			'percent_join_field': 'material_request'
+				"source_dt": "Purchase Receipt Item",
+				"target_dt": "Material Request Item",
+				"join_field": "material_request_item",
+				"target_field": "received_qty",
+				"target_parent_dt": "Material Request",
+				"target_parent_field": "per_received",
+				"target_ref_field": "stock_qty",
+				"source_field": "stock_qty",
+				"percent_join_field": "material_request",
 			}
 		]
 		doc.update_qty()
diff --git a/erpnext/patches/v12_0/set_serial_no_status.py b/erpnext/patches/v12_0/set_serial_no_status.py
index 8c136e6..8ab342e 100644
--- a/erpnext/patches/v12_0/set_serial_no_status.py
+++ b/erpnext/patches/v12_0/set_serial_no_status.py
@@ -3,17 +3,22 @@
 
 
 def execute():
-	frappe.reload_doc('stock', 'doctype', 'serial_no')
+	frappe.reload_doc("stock", "doctype", "serial_no")
 
-	serial_no_list = frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date, warehouse from `tabSerial No`
-		where (status is NULL OR status='')""", as_dict = 1)
+	serial_no_list = frappe.db.sql(
+		"""select name, delivery_document_type, warranty_expiry_date, warehouse from `tabSerial No`
+		where (status is NULL OR status='')""",
+		as_dict=1,
+	)
 	if len(serial_no_list) > 20000:
 		frappe.db.auto_commit_on_many_writes = True
 
 	for serial_no in serial_no_list:
 		if serial_no.get("delivery_document_type"):
 			status = "Delivered"
-		elif serial_no.get("warranty_expiry_date") and getdate(serial_no.get("warranty_expiry_date")) <= getdate(nowdate()):
+		elif serial_no.get("warranty_expiry_date") and getdate(
+			serial_no.get("warranty_expiry_date")
+		) <= getdate(nowdate()):
 			status = "Expired"
 		elif not serial_no.get("warehouse"):
 			status = "Inactive"
diff --git a/erpnext/patches/v12_0/set_task_status.py b/erpnext/patches/v12_0/set_task_status.py
index 1b4955a..1c6654e 100644
--- a/erpnext/patches/v12_0/set_task_status.py
+++ b/erpnext/patches/v12_0/set_task_status.py
@@ -2,14 +2,16 @@
 
 
 def execute():
-	frappe.reload_doctype('Task')
+	frappe.reload_doctype("Task")
 
 	# add "Completed" if customized
-	property_setter_name = frappe.db.exists('Property Setter', dict(doc_type='Task', field_name = 'status', property = 'options'))
+	property_setter_name = frappe.db.exists(
+		"Property Setter", dict(doc_type="Task", field_name="status", property="options")
+	)
 	if property_setter_name:
-		property_setter = frappe.get_doc('Property Setter', property_setter_name)
+		property_setter = frappe.get_doc("Property Setter", property_setter_name)
 		if not "Completed" in property_setter.value:
-			property_setter.value = property_setter.value + '\nCompleted'
+			property_setter.value = property_setter.value + "\nCompleted"
 			property_setter.save()
 
 	# renamed default status to Completed as status "Closed" is ambiguous
diff --git a/erpnext/patches/v12_0/set_total_batch_quantity.py b/erpnext/patches/v12_0/set_total_batch_quantity.py
index 7296eaa..068e0a6 100644
--- a/erpnext/patches/v12_0/set_total_batch_quantity.py
+++ b/erpnext/patches/v12_0/set_total_batch_quantity.py
@@ -5,7 +5,12 @@
 	frappe.reload_doc("stock", "doctype", "batch")
 
 	for batch in frappe.get_all("Batch", fields=["name", "batch_id"]):
-		batch_qty = frappe.db.get_value("Stock Ledger Entry",
-			{"docstatus": 1, "batch_no": batch.batch_id, "is_cancelled": 0},
-			"sum(actual_qty)") or 0.0
+		batch_qty = (
+			frappe.db.get_value(
+				"Stock Ledger Entry",
+				{"docstatus": 1, "batch_no": batch.batch_id, "is_cancelled": 0},
+				"sum(actual_qty)",
+			)
+			or 0.0
+		)
 		frappe.db.set_value("Batch", batch.name, "batch_qty", batch_qty, update_modified=False)
diff --git a/erpnext/patches/v12_0/set_updated_purpose_in_pick_list.py b/erpnext/patches/v12_0/set_updated_purpose_in_pick_list.py
index 300d0f2..1e39081 100644
--- a/erpnext/patches/v12_0/set_updated_purpose_in_pick_list.py
+++ b/erpnext/patches/v12_0/set_updated_purpose_in_pick_list.py
@@ -6,6 +6,8 @@
 
 
 def execute():
-    frappe.reload_doc("stock", "doctype", "pick_list")
-    frappe.db.sql("""UPDATE `tabPick List` set purpose = 'Delivery'
-        WHERE docstatus = 1  and purpose = 'Delivery against Sales Order' """)
+	frappe.reload_doc("stock", "doctype", "pick_list")
+	frappe.db.sql(
+		"""UPDATE `tabPick List` set purpose = 'Delivery'
+        WHERE docstatus = 1  and purpose = 'Delivery against Sales Order' """
+	)
diff --git a/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py b/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py
index 154d7ba..94322cd 100644
--- a/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py
+++ b/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py
@@ -3,6 +3,8 @@
 
 def execute():
 	frappe.reload_doc("buying", "doctype", "supplier_quotation")
-	frappe.db.sql("""UPDATE `tabSupplier Quotation`
+	frappe.db.sql(
+		"""UPDATE `tabSupplier Quotation`
 		SET valid_till = DATE_ADD(transaction_date , INTERVAL 1 MONTH)
-		WHERE docstatus < 2""")
+		WHERE docstatus < 2"""
+	)
diff --git a/erpnext/patches/v12_0/stock_entry_enhancements.py b/erpnext/patches/v12_0/stock_entry_enhancements.py
index 94d8ff9..db099a3 100644
--- a/erpnext/patches/v12_0/stock_entry_enhancements.py
+++ b/erpnext/patches/v12_0/stock_entry_enhancements.py
@@ -2,7 +2,6 @@
 # License: GNU General Public License v3. See license.txt
 
 
-
 import frappe
 from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 
@@ -10,44 +9,61 @@
 def execute():
 	create_stock_entry_types()
 
-	company = frappe.db.get_value("Company", {'country': 'India'}, 'name')
+	company = frappe.db.get_value("Company", {"country": "India"}, "name")
 	if company:
 		add_gst_hsn_code_field()
 
+
 def create_stock_entry_types():
-	frappe.reload_doc('stock', 'doctype', 'stock_entry_type')
-	frappe.reload_doc('stock', 'doctype', 'stock_entry')
+	frappe.reload_doc("stock", "doctype", "stock_entry_type")
+	frappe.reload_doc("stock", "doctype", "stock_entry")
 
-	for purpose in ["Material Issue", "Material Receipt", "Material Transfer",
-		"Material Transfer for Manufacture", "Material Consumption for Manufacture", "Manufacture",
-		"Repack", "Send to Subcontractor"]:
+	for purpose in [
+		"Material Issue",
+		"Material Receipt",
+		"Material Transfer",
+		"Material Transfer for Manufacture",
+		"Material Consumption for Manufacture",
+		"Manufacture",
+		"Repack",
+		"Send to Subcontractor",
+	]:
 
-		ste_type = frappe.get_doc({
-			'doctype': 'Stock Entry Type',
-			'name': purpose,
-			'purpose': purpose
-		})
+		ste_type = frappe.get_doc({"doctype": "Stock Entry Type", "name": purpose, "purpose": purpose})
 
 		try:
 			ste_type.insert()
 		except frappe.DuplicateEntryError:
 			pass
 
-	frappe.db.sql(" UPDATE `tabStock Entry` set purpose = 'Send to Subcontractor' where purpose = 'Subcontract'")
+	frappe.db.sql(
+		" UPDATE `tabStock Entry` set purpose = 'Send to Subcontractor' where purpose = 'Subcontract'"
+	)
 	frappe.db.sql(" UPDATE `tabStock Entry` set stock_entry_type = purpose ")
 
+
 def add_gst_hsn_code_field():
 	custom_fields = {
-		'Stock Entry Detail': [dict(fieldname='gst_hsn_code', label='HSN/SAC',
-			fieldtype='Data', fetch_from='item_code.gst_hsn_code',
-			insert_after='description', allow_on_submit=1, print_hide=0)]
+		"Stock Entry Detail": [
+			dict(
+				fieldname="gst_hsn_code",
+				label="HSN/SAC",
+				fieldtype="Data",
+				fetch_from="item_code.gst_hsn_code",
+				insert_after="description",
+				allow_on_submit=1,
+				print_hide=0,
+			)
+		]
 	}
 
-	create_custom_fields(custom_fields, ignore_validate = frappe.flags.in_patch, update=True)
+	create_custom_fields(custom_fields, ignore_validate=frappe.flags.in_patch, update=True)
 
-	frappe.db.sql(""" update `tabStock Entry Detail`, `tabItem`
+	frappe.db.sql(
+		""" update `tabStock Entry Detail`, `tabItem`
 		SET
 			`tabStock Entry Detail`.gst_hsn_code = `tabItem`.gst_hsn_code
 		Where
 			`tabItem`.name = `tabStock Entry Detail`.item_code and `tabItem`.gst_hsn_code is not null
-	""")
+	"""
+	)
diff --git a/erpnext/patches/v12_0/unhide_cost_center_field.py b/erpnext/patches/v12_0/unhide_cost_center_field.py
index 7245021..5f91ef6 100644
--- a/erpnext/patches/v12_0/unhide_cost_center_field.py
+++ b/erpnext/patches/v12_0/unhide_cost_center_field.py
@@ -6,9 +6,11 @@
 
 
 def execute():
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		DELETE FROM `tabProperty Setter`
 		WHERE doc_type in ('Sales Invoice', 'Purchase Invoice', 'Payment Entry')
 		AND field_name = 'cost_center'
 		AND property = 'hidden'
-	""")
+	"""
+	)
diff --git a/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py
index 5b5f623..332609b 100644
--- a/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py
+++ b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py
@@ -2,27 +2,29 @@
 
 
 def execute():
-    """
-    set proper customer and supplier details for item price
-    based on selling and buying values
-    """
+	"""
+	set proper customer and supplier details for item price
+	based on selling and buying values
+	"""
 
-    # update for selling
-    frappe.db.sql(
-        """UPDATE `tabItem Price` ip, `tabPrice List` pl
+	# update for selling
+	frappe.db.sql(
+		"""UPDATE `tabItem Price` ip, `tabPrice List` pl
         SET ip.`reference` = ip.`customer`, ip.`supplier` = NULL
         WHERE ip.`selling` = 1
         AND ip.`buying` = 0
         AND (ip.`supplier` IS NOT NULL OR ip.`supplier` = '')
         AND ip.`price_list` = pl.`name`
-        AND pl.`enabled` = 1""")
+        AND pl.`enabled` = 1"""
+	)
 
-    # update for buying
-    frappe.db.sql(
-        """UPDATE `tabItem Price` ip, `tabPrice List` pl
+	# update for buying
+	frappe.db.sql(
+		"""UPDATE `tabItem Price` ip, `tabPrice List` pl
         SET ip.`reference` = ip.`supplier`, ip.`customer` = NULL
         WHERE ip.`selling` = 0
         AND ip.`buying` = 1
         AND (ip.`customer` IS NOT NULL OR ip.`customer` = '')
         AND ip.`price_list` = pl.`name`
-        AND pl.`enabled` = 1""")
+        AND pl.`enabled` = 1"""
+	)
diff --git a/erpnext/patches/v12_0/update_bom_in_so_mr.py b/erpnext/patches/v12_0/update_bom_in_so_mr.py
index 132f3bd..114f65d 100644
--- a/erpnext/patches/v12_0/update_bom_in_so_mr.py
+++ b/erpnext/patches/v12_0/update_bom_in_so_mr.py
@@ -10,11 +10,15 @@
 		if doctype == "Material Request":
 			condition = " and doc.per_ordered < 100 and doc.material_request_type = 'Manufacture'"
 
-		frappe.db.sql(""" UPDATE `tab{doc}` as doc, `tab{doc} Item` as child_doc, tabItem as item
+		frappe.db.sql(
+			""" UPDATE `tab{doc}` as doc, `tab{doc} Item` as child_doc, tabItem as item
 			SET
 				child_doc.bom_no = item.default_bom
 			WHERE
 				child_doc.item_code = item.name and child_doc.docstatus < 2
 				and child_doc.parent = doc.name
 				and item.default_bom is not null and item.default_bom != '' {cond}
-		""".format(doc = doctype, cond = condition))
+		""".format(
+				doc=doctype, cond=condition
+			)
+		)
diff --git a/erpnext/patches/v12_0/update_due_date_in_gle.py b/erpnext/patches/v12_0/update_due_date_in_gle.py
index e4418b0..a1c4f51 100644
--- a/erpnext/patches/v12_0/update_due_date_in_gle.py
+++ b/erpnext/patches/v12_0/update_due_date_in_gle.py
@@ -2,16 +2,19 @@
 
 
 def execute():
-    frappe.reload_doc("accounts", "doctype", "gl_entry")
+	frappe.reload_doc("accounts", "doctype", "gl_entry")
 
-    for doctype in ["Sales Invoice", "Purchase Invoice", "Journal Entry"]:
-        frappe.reload_doc("accounts", "doctype", frappe.scrub(doctype))
+	for doctype in ["Sales Invoice", "Purchase Invoice", "Journal Entry"]:
+		frappe.reload_doc("accounts", "doctype", frappe.scrub(doctype))
 
-        frappe.db.sql(""" UPDATE `tabGL Entry`, `tab{doctype}`
+		frappe.db.sql(
+			""" UPDATE `tabGL Entry`, `tab{doctype}`
             SET
                 `tabGL Entry`.due_date = `tab{doctype}`.due_date
             WHERE
                 `tabGL Entry`.voucher_no = `tab{doctype}`.name and `tabGL Entry`.party is not null
                 and `tabGL Entry`.voucher_type in ('Sales Invoice', 'Purchase Invoice', 'Journal Entry')
-                and `tabGL Entry`.account in (select name from `tabAccount` where account_type in ('Receivable', 'Payable'))""" #nosec
-            .format(doctype=doctype))
+                and `tabGL Entry`.account in (select name from `tabAccount` where account_type in ('Receivable', 'Payable'))""".format(  # nosec
+				doctype=doctype
+			)
+		)
diff --git a/erpnext/patches/v12_0/update_end_date_and_status_in_email_campaign.py b/erpnext/patches/v12_0/update_end_date_and_status_in_email_campaign.py
index ef4204f..570b77b 100644
--- a/erpnext/patches/v12_0/update_end_date_and_status_in_email_campaign.py
+++ b/erpnext/patches/v12_0/update_end_date_and_status_in_email_campaign.py
@@ -3,22 +3,22 @@
 
 
 def execute():
-    if frappe.db.exists('DocType', 'Email Campaign'):
-        email_campaign = frappe.get_all('Email Campaign')
-        for campaign in email_campaign:
-            doc = frappe.get_doc("Email Campaign",campaign["name"])
-            send_after_days = []
+	if frappe.db.exists("DocType", "Email Campaign"):
+		email_campaign = frappe.get_all("Email Campaign")
+		for campaign in email_campaign:
+			doc = frappe.get_doc("Email Campaign", campaign["name"])
+			send_after_days = []
 
-            camp = frappe.get_doc("Campaign", doc.campaign_name)
-            for entry in camp.get("campaign_schedules"):
-                send_after_days.append(entry.send_after_days)
-            if send_after_days:
-                end_date = add_days(getdate(doc.start_date), max(send_after_days))
-                doc.db_set("end_date", end_date)
-            today_date = getdate(today())
-            if doc.start_date > today_date:
-                doc.db_set("status", "Scheduled")
-            elif end_date >= today_date:
-                doc.db_set("status", "In Progress")
-            elif end_date < today_date:
-                doc.db_set("status", "Completed")
+			camp = frappe.get_doc("Campaign", doc.campaign_name)
+			for entry in camp.get("campaign_schedules"):
+				send_after_days.append(entry.send_after_days)
+			if send_after_days:
+				end_date = add_days(getdate(doc.start_date), max(send_after_days))
+				doc.db_set("end_date", end_date)
+			today_date = getdate(today())
+			if doc.start_date > today_date:
+				doc.db_set("status", "Scheduled")
+			elif end_date >= today_date:
+				doc.db_set("status", "In Progress")
+			elif end_date < today_date:
+				doc.db_set("status", "Completed")
diff --git a/erpnext/patches/v12_0/update_healthcare_refactored_changes.py b/erpnext/patches/v12_0/update_healthcare_refactored_changes.py
deleted file mode 100644
index f553ee9..0000000
--- a/erpnext/patches/v12_0/update_healthcare_refactored_changes.py
+++ /dev/null
@@ -1,136 +0,0 @@
-import frappe
-from frappe.model.utils.rename_field import rename_field
-from frappe.modules import get_doctype_module, scrub
-
-field_rename_map = {
-	'Healthcare Settings': [
-		['patient_master_name', 'patient_name_by'],
-		['max_visit', 'max_visits'],
-		['reg_sms', 'send_registration_msg'],
-		['reg_msg', 'registration_msg'],
-		['app_con', 'send_appointment_confirmation'],
-		['app_con_msg', 'appointment_confirmation_msg'],
-		['no_con', 'avoid_confirmation'],
-		['app_rem', 'send_appointment_reminder'],
-		['app_rem_msg', 'appointment_reminder_msg'],
-		['rem_before', 'remind_before'],
-		['manage_customer', 'link_customer_to_patient'],
-		['create_test_on_si_submit', 'create_lab_test_on_si_submit'],
-		['require_sample_collection', 'create_sample_collection_for_lab_test'],
-		['require_test_result_approval', 'lab_test_approval_required'],
-		['manage_appointment_invoice_automatically', 'automate_appointment_invoicing']
-	],
-	'Drug Prescription':[
-		['use_interval', 'usage_interval'],
-		['in_every', 'interval_uom']
-	],
-	'Lab Test Template':[
-		['sample_quantity', 'sample_qty'],
-		['sample_collection_details', 'sample_details']
-	],
-	'Sample Collection':[
-		['sample_quantity', 'sample_qty'],
-		['sample_collection_details', 'sample_details']
-	],
-	'Fee Validity': [
-		['max_visit', 'max_visits']
-	]
-}
-
-def execute():
-	for dn in field_rename_map:
-		if frappe.db.exists('DocType', dn):
-			if dn == 'Healthcare Settings':
-				frappe.reload_doctype('Healthcare Settings')
-			else:
-				frappe.reload_doc(get_doctype_module(dn), "doctype", scrub(dn))
-
-	for dt, field_list in field_rename_map.items():
-		if frappe.db.exists('DocType', dt):
-			for field in field_list:
-				if dt == 'Healthcare Settings':
-					rename_field(dt, field[0], field[1])
-				elif frappe.db.has_column(dt, field[0]):
-					rename_field(dt, field[0], field[1])
-
-	# first name mandatory in Patient
-	if frappe.db.exists('DocType', 'Patient'):
-		patients = frappe.db.sql("select name, patient_name from `tabPatient`", as_dict=1)
-		frappe.reload_doc('healthcare', 'doctype', 'patient')
-		for entry in patients:
-			name = entry.patient_name.split(' ')
-			frappe.db.set_value('Patient', entry.name, 'first_name', name[0])
-
-	# mark Healthcare Practitioner status as Disabled
-	if frappe.db.exists('DocType', 'Healthcare Practitioner'):
-		practitioners = frappe.db.sql("select name from `tabHealthcare Practitioner` where 'active'= 0", as_dict=1)
-		practitioners_lst = [p.name for p in practitioners]
-		frappe.reload_doc('healthcare', 'doctype', 'healthcare_practitioner')
-		if practitioners_lst:
-			frappe.db.sql("update `tabHealthcare Practitioner` set status = 'Disabled' where name IN %(practitioners)s""", {"practitioners": practitioners_lst})
-
-	# set Clinical Procedure status
-	if frappe.db.exists('DocType', 'Clinical Procedure'):
-		frappe.reload_doc('healthcare', 'doctype', 'clinical_procedure')
-		frappe.db.sql("""
-			UPDATE
-				`tabClinical Procedure`
-			SET
-				docstatus = (CASE WHEN status = 'Cancelled' THEN 2
-								WHEN status = 'Draft' THEN 0
-								ELSE 1
-							END)
-		""")
-
-	# set complaints and diagnosis in table multiselect in Patient Encounter
-	if frappe.db.exists('DocType', 'Patient Encounter'):
-		field_list = [
-			['visit_department', 'medical_department'],
-			['type', 'appointment_type']
-		]
-		encounter_details = frappe.db.sql("""select symptoms, diagnosis, name from `tabPatient Encounter`""", as_dict=True)
-		frappe.reload_doc('healthcare', 'doctype', 'patient_encounter')
-		frappe.reload_doc('healthcare', 'doctype', 'patient_encounter_symptom')
-		frappe.reload_doc('healthcare', 'doctype', 'patient_encounter_diagnosis')
-
-		for field in field_list:
-			if frappe.db.has_column(dt, field[0]):
-				rename_field(dt, field[0], field[1])
-
-		for entry in encounter_details:
-			doc = frappe.get_doc('Patient Encounter', entry.name)
-			symptoms = entry.symptoms.split('\n') if entry.symptoms else []
-			for symptom in symptoms:
-				if not frappe.db.exists('Complaint', symptom):
-					frappe.get_doc({
-						'doctype': 'Complaint',
-						'complaints': symptom
-					}).insert()
-				row = doc.append('symptoms', {
-					'complaint': symptom
-				})
-				row.db_update()
-
-			diagnosis = entry.diagnosis.split('\n') if entry.diagnosis else []
-			for d in diagnosis:
-				if not frappe.db.exists('Diagnosis', d):
-					frappe.get_doc({
-						'doctype': 'Diagnosis',
-						'diagnosis': d
-					}).insert()
-				row = doc.append('diagnosis', {
-					'diagnosis': d
-				})
-				row.db_update()
-			doc.db_update()
-
-	if frappe.db.exists('DocType', 'Fee Validity'):
-		# update fee validity status
-		frappe.db.sql("""
-			UPDATE
-				`tabFee Validity`
-			SET
-				status = (CASE WHEN visited >= max_visits THEN 'Completed'
-								ELSE 'Pending'
-							END)
-		""")
diff --git a/erpnext/patches/v12_0/update_is_cancelled_field.py b/erpnext/patches/v12_0/update_is_cancelled_field.py
index 0401034..398dd70 100644
--- a/erpnext/patches/v12_0/update_is_cancelled_field.py
+++ b/erpnext/patches/v12_0/update_is_cancelled_field.py
@@ -2,28 +2,35 @@
 
 
 def execute():
-	#handle type casting for is_cancelled field
+	# handle type casting for is_cancelled field
 	module_doctypes = (
-		('stock', 'Stock Ledger Entry'),
-		('stock', 'Serial No'),
-		('accounts', 'GL Entry')
+		("stock", "Stock Ledger Entry"),
+		("stock", "Serial No"),
+		("accounts", "GL Entry"),
 	)
 
 	for module, doctype in module_doctypes:
-		if (not frappe.db.has_column(doctype, "is_cancelled")
+		if (
+			not frappe.db.has_column(doctype, "is_cancelled")
 			or frappe.db.get_column_type(doctype, "is_cancelled").lower() == "int(1)"
 		):
 			continue
 
-		frappe.db.sql("""
+		frappe.db.sql(
+			"""
 				UPDATE `tab{doctype}`
 				SET is_cancelled = 0
-				where is_cancelled in ('', NULL, 'No')"""
-				.format(doctype=doctype))
-		frappe.db.sql("""
+				where is_cancelled in ('', 'No') or is_cancelled is NULL""".format(
+				doctype=doctype
+			)
+		)
+		frappe.db.sql(
+			"""
 				UPDATE `tab{doctype}`
 				SET is_cancelled = 1
-				where is_cancelled = 'Yes'"""
-				.format(doctype=doctype))
+				where is_cancelled = 'Yes'""".format(
+				doctype=doctype
+			)
+		)
 
 		frappe.reload_doc(module, "doctype", frappe.scrub(doctype))
diff --git a/erpnext/patches/v12_0/update_item_tax_template_company.py b/erpnext/patches/v12_0/update_item_tax_template_company.py
index abd4f6f..489f70d 100644
--- a/erpnext/patches/v12_0/update_item_tax_template_company.py
+++ b/erpnext/patches/v12_0/update_item_tax_template_company.py
@@ -2,12 +2,12 @@
 
 
 def execute():
-    frappe.reload_doc('accounts', 'doctype', 'item_tax_template')
+	frappe.reload_doc("accounts", "doctype", "item_tax_template")
 
-    item_tax_template_list = frappe.get_list('Item Tax Template')
-    for template in item_tax_template_list:
-        doc = frappe.get_doc('Item Tax Template', template.name)
-        for tax in doc.taxes:
-            doc.company = frappe.get_value('Account', tax.tax_type, 'company')
-            break
-        doc.save()
+	item_tax_template_list = frappe.get_list("Item Tax Template")
+	for template in item_tax_template_list:
+		doc = frappe.get_doc("Item Tax Template", template.name)
+		for tax in doc.taxes:
+			doc.company = frappe.get_value("Account", tax.tax_type, "company")
+			break
+		doc.save()
diff --git a/erpnext/patches/v12_0/update_owner_fields_in_acc_dimension_custom_fields.py b/erpnext/patches/v12_0/update_owner_fields_in_acc_dimension_custom_fields.py
index e5f24d4..7dc0af9 100644
--- a/erpnext/patches/v12_0/update_owner_fields_in_acc_dimension_custom_fields.py
+++ b/erpnext/patches/v12_0/update_owner_fields_in_acc_dimension_custom_fields.py
@@ -6,15 +6,21 @@
 
 
 def execute():
-	accounting_dimensions = frappe.db.sql("""select fieldname from
-		`tabAccounting Dimension`""", as_dict=1)
+	accounting_dimensions = frappe.db.sql(
+		"""select fieldname from
+		`tabAccounting Dimension`""",
+		as_dict=1,
+	)
 
 	doclist = get_doctypes_with_dimensions()
 
 	for dimension in accounting_dimensions:
-		frappe.db.sql("""
+		frappe.db.sql(
+			"""
 			UPDATE `tabCustom Field`
 			SET owner = 'Administrator'
 			WHERE fieldname = %s
-			AND dt IN (%s)""" %			#nosec
-			('%s', ', '.join(['%s']* len(doclist))), tuple([dimension.fieldname] + doclist))
+			AND dt IN (%s)"""
+			% ("%s", ", ".join(["%s"] * len(doclist))),  # nosec
+			tuple([dimension.fieldname] + doclist),
+		)
diff --git a/erpnext/patches/v12_0/update_price_list_currency_in_bom.py b/erpnext/patches/v12_0/update_price_list_currency_in_bom.py
index ea3e002..5710320 100644
--- a/erpnext/patches/v12_0/update_price_list_currency_in_bom.py
+++ b/erpnext/patches/v12_0/update_price_list_currency_in_bom.py
@@ -8,16 +8,19 @@
 	frappe.reload_doc("manufacturing", "doctype", "bom")
 	frappe.reload_doc("manufacturing", "doctype", "bom_item")
 
-	frappe.db.sql(""" UPDATE `tabBOM`, `tabPrice List`
+	frappe.db.sql(
+		""" UPDATE `tabBOM`, `tabPrice List`
 		SET
 			`tabBOM`.price_list_currency = `tabPrice List`.currency,
 			`tabBOM`.plc_conversion_rate = 1.0
 		WHERE
 			`tabBOM`.buying_price_list = `tabPrice List`.name AND `tabBOM`.docstatus < 2
 			AND `tabBOM`.rm_cost_as_per = 'Price List'
-	""")
+	"""
+	)
 
-	for d in frappe.db.sql("""
+	for d in frappe.db.sql(
+		"""
 		SELECT
 			bom.creation, bom.name, bom.price_list_currency as currency,
 			company.default_currency as company_currency
@@ -25,8 +28,11 @@
 			`tabBOM` as bom, `tabCompany` as company
 		WHERE
 			bom.company = company.name AND bom.rm_cost_as_per = 'Price List' AND
-			bom.price_list_currency != company.default_currency AND bom.docstatus < 2""", as_dict=1):
-			plc_conversion_rate = get_exchange_rate(d.currency,
-				d.company_currency, getdate(d.creation), "for_buying")
+			bom.price_list_currency != company.default_currency AND bom.docstatus < 2""",
+		as_dict=1,
+	):
+		plc_conversion_rate = get_exchange_rate(
+			d.currency, d.company_currency, getdate(d.creation), "for_buying"
+		)
 
-			frappe.db.set_value("BOM", d.name, "plc_conversion_rate", plc_conversion_rate)
+		frappe.db.set_value("BOM", d.name, "plc_conversion_rate", plc_conversion_rate)
diff --git a/erpnext/patches/v12_0/update_price_or_product_discount.py b/erpnext/patches/v12_0/update_price_or_product_discount.py
index 53c0ba5..64344c8 100644
--- a/erpnext/patches/v12_0/update_price_or_product_discount.py
+++ b/erpnext/patches/v12_0/update_price_or_product_discount.py
@@ -4,5 +4,7 @@
 def execute():
 	frappe.reload_doc("accounts", "doctype", "pricing_rule")
 
-	frappe.db.sql(""" UPDATE `tabPricing Rule` SET price_or_product_discount = 'Price'
-		WHERE ifnull(price_or_product_discount,'') = '' """)
+	frappe.db.sql(
+		""" UPDATE `tabPricing Rule` SET price_or_product_discount = 'Price'
+		WHERE ifnull(price_or_product_discount,'') = '' """
+	)
diff --git a/erpnext/patches/v12_0/update_pricing_rule_fields.py b/erpnext/patches/v12_0/update_pricing_rule_fields.py
index b7c36ae..8da06b0 100644
--- a/erpnext/patches/v12_0/update_pricing_rule_fields.py
+++ b/erpnext/patches/v12_0/update_pricing_rule_fields.py
@@ -4,68 +4,120 @@
 
 import frappe
 
-parentfield = {
-	'item_code': 'items',
-	'item_group': 'item_groups',
-	'brand': 'brands'
-}
+parentfield = {"item_code": "items", "item_group": "item_groups", "brand": "brands"}
+
 
 def execute():
 
-	if not frappe.get_all('Pricing Rule', limit=1):
+	if not frappe.get_all("Pricing Rule", limit=1):
 		return
 
-	frappe.reload_doc('accounts', 'doctype', 'pricing_rule_detail')
-	doctypes = {'Supplier Quotation': 'buying', 'Purchase Order': 'buying', 'Purchase Invoice': 'accounts',
-		'Purchase Receipt': 'stock', 'Quotation': 'selling', 'Sales Order': 'selling',
-		'Sales Invoice': 'accounts', 'Delivery Note': 'stock'}
+	frappe.reload_doc("accounts", "doctype", "pricing_rule_detail")
+	doctypes = {
+		"Supplier Quotation": "buying",
+		"Purchase Order": "buying",
+		"Purchase Invoice": "accounts",
+		"Purchase Receipt": "stock",
+		"Quotation": "selling",
+		"Sales Order": "selling",
+		"Sales Invoice": "accounts",
+		"Delivery Note": "stock",
+	}
 
 	for doctype, module in doctypes.items():
-		frappe.reload_doc(module, 'doctype', frappe.scrub(doctype))
+		frappe.reload_doc(module, "doctype", frappe.scrub(doctype))
 
-		child_doc = frappe.scrub(doctype) + '_item'
-		frappe.reload_doc(module, 'doctype', child_doc, force=True)
+		child_doc = frappe.scrub(doctype) + "_item"
+		frappe.reload_doc(module, "doctype", child_doc, force=True)
 
-		child_doctype = doctype + ' Item'
+		child_doctype = doctype + " Item"
 
-		frappe.db.sql(""" UPDATE `tab{child_doctype}` SET pricing_rules = pricing_rule
+		frappe.db.sql(
+			""" UPDATE `tab{child_doctype}` SET pricing_rules = pricing_rule
 			WHERE docstatus < 2 and pricing_rule is not null and pricing_rule != ''
-		""".format(child_doctype= child_doctype))
+		""".format(
+				child_doctype=child_doctype
+			)
+		)
 
-		data = frappe.db.sql(""" SELECT pricing_rule, name, parent,
+		data = frappe.db.sql(
+			""" SELECT pricing_rule, name, parent,
 				parenttype, creation, modified, docstatus, modified_by, owner, name
 			FROM `tab{child_doc}` where docstatus < 2 and pricing_rule is not null
-			and pricing_rule != ''""".format(child_doc=child_doctype), as_dict=1)
+			and pricing_rule != ''""".format(
+				child_doc=child_doctype
+			),
+			as_dict=1,
+		)
 
 		values = []
 		for d in data:
-			values.append((d.pricing_rule, d.name, d.parent, 'pricing_rules', d.parenttype,
-				d.creation, d.modified, d.docstatus, d.modified_by, d.owner, frappe.generate_hash("", 10)))
+			values.append(
+				(
+					d.pricing_rule,
+					d.name,
+					d.parent,
+					"pricing_rules",
+					d.parenttype,
+					d.creation,
+					d.modified,
+					d.docstatus,
+					d.modified_by,
+					d.owner,
+					frappe.generate_hash("", 10),
+				)
+			)
 
 		if values:
-			frappe.db.sql(""" INSERT INTO
+			frappe.db.sql(
+				""" INSERT INTO
 				`tabPricing Rule Detail` (`pricing_rule`, `child_docname`, `parent`, `parentfield`, `parenttype`,
 				`creation`, `modified`, `docstatus`, `modified_by`, `owner`, `name`)
-			VALUES {values} """.format(values=', '.join(['%s'] * len(values))), tuple(values))
+			VALUES {values} """.format(
+					values=", ".join(["%s"] * len(values))
+				),
+				tuple(values),
+			)
 
-	frappe.reload_doc('accounts', 'doctype', 'pricing_rule')
+	frappe.reload_doc("accounts", "doctype", "pricing_rule")
 
-	for doctype, apply_on in {'Pricing Rule Item Code': 'Item Code',
-		'Pricing Rule Item Group': 'Item Group', 'Pricing Rule Brand': 'Brand'}.items():
-		frappe.reload_doc('accounts', 'doctype', frappe.scrub(doctype))
+	for doctype, apply_on in {
+		"Pricing Rule Item Code": "Item Code",
+		"Pricing Rule Item Group": "Item Group",
+		"Pricing Rule Brand": "Brand",
+	}.items():
+		frappe.reload_doc("accounts", "doctype", frappe.scrub(doctype))
 
 		field = frappe.scrub(apply_on)
-		data = frappe.get_all('Pricing Rule', fields=[field, "name", "creation", "modified",
-			"owner", "modified_by"], filters= {'apply_on': apply_on})
+		data = frappe.get_all(
+			"Pricing Rule",
+			fields=[field, "name", "creation", "modified", "owner", "modified_by"],
+			filters={"apply_on": apply_on},
+		)
 
 		values = []
 		for d in data:
-			values.append((d.get(field), d.name, parentfield.get(field), 'Pricing Rule',
-				d.creation, d.modified, d.owner, d.modified_by, frappe.generate_hash("", 10)))
+			values.append(
+				(
+					d.get(field),
+					d.name,
+					parentfield.get(field),
+					"Pricing Rule",
+					d.creation,
+					d.modified,
+					d.owner,
+					d.modified_by,
+					frappe.generate_hash("", 10),
+				)
+			)
 
 		if values:
-			frappe.db.sql(""" INSERT INTO
+			frappe.db.sql(
+				""" INSERT INTO
 				`tab{doctype}` ({field}, parent, parentfield, parenttype, creation, modified,
 					owner, modified_by, name)
-			VALUES {values} """.format(doctype=doctype,
-				field=field, values=', '.join(['%s'] * len(values))), tuple(values))
+			VALUES {values} """.format(
+					doctype=doctype, field=field, values=", ".join(["%s"] * len(values))
+				),
+				tuple(values),
+			)
diff --git a/erpnext/patches/v12_0/update_production_plan_status.py b/erpnext/patches/v12_0/update_production_plan_status.py
index 06fc503..dc65ec2 100644
--- a/erpnext/patches/v12_0/update_production_plan_status.py
+++ b/erpnext/patches/v12_0/update_production_plan_status.py
@@ -6,7 +6,8 @@
 
 def execute():
 	frappe.reload_doc("manufacturing", "doctype", "production_plan")
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		UPDATE `tabProduction Plan` ppl
 		SET status = "Completed"
 		WHERE ppl.name IN (
@@ -28,4 +29,5 @@
 				HAVING should_set = 1
 			) ss
 		)
-	""")
+	"""
+	)
diff --git a/erpnext/patches/v12_0/update_state_code_for_daman_and_diu.py b/erpnext/patches/v12_0/update_state_code_for_daman_and_diu.py
index 25cf6b9..d7e96fa 100644
--- a/erpnext/patches/v12_0/update_state_code_for_daman_and_diu.py
+++ b/erpnext/patches/v12_0/update_state_code_for_daman_and_diu.py
@@ -5,20 +5,22 @@
 
 def execute():
 
-	company = frappe.get_all('Company', filters = {'country': 'India'})
+	company = frappe.get_all("Company", filters={"country": "India"})
 	if not company:
 		return
 
 	# Update options in gst_state custom field
-	gst_state = frappe.get_doc('Custom Field', 'Address-gst_state')
-	gst_state.options = '\n'.join(states)
+	gst_state = frappe.get_doc("Custom Field", "Address-gst_state")
+	gst_state.options = "\n".join(states)
 	gst_state.save()
 
 	# Update gst_state and state code in existing address
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		UPDATE `tabAddress`
 		SET
 			gst_state = 'Dadra and Nagar Haveli and Daman and Diu',
 			gst_state_number = 26
 		WHERE gst_state = 'Daman and Diu'
-	""")
+	"""
+	)
diff --git a/erpnext/patches/v13_0/add_bin_unique_constraint.py b/erpnext/patches/v13_0/add_bin_unique_constraint.py
index 57fbaae..38a8500 100644
--- a/erpnext/patches/v13_0/add_bin_unique_constraint.py
+++ b/erpnext/patches/v13_0/add_bin_unique_constraint.py
@@ -14,13 +14,16 @@
 	delete_broken_bins()
 	delete_and_patch_duplicate_bins()
 
+
 def delete_broken_bins():
 	# delete useless bins
 	frappe.db.sql("delete from `tabBin` where item_code is null or warehouse is null")
 
+
 def delete_and_patch_duplicate_bins():
 
-	duplicate_bins = frappe.db.sql("""
+	duplicate_bins = frappe.db.sql(
+		"""
 		SELECT
 			item_code, warehouse, count(*) as bin_count
 		FROM
@@ -29,18 +32,19 @@
 			item_code, warehouse
 		HAVING
 			bin_count > 1
-	""", as_dict=1)
+	""",
+		as_dict=1,
+	)
 
 	for duplicate_bin in duplicate_bins:
 		item_code = duplicate_bin.item_code
 		warehouse = duplicate_bin.warehouse
-		existing_bins = frappe.get_list("Bin",
-				filters={
-					"item_code": item_code,
-					"warehouse": warehouse
-					},
-				fields=["name"],
-				order_by="creation",)
+		existing_bins = frappe.get_list(
+			"Bin",
+			filters={"item_code": item_code, "warehouse": warehouse},
+			fields=["name"],
+			order_by="creation",
+		)
 
 		# keep last one
 		existing_bins.pop()
@@ -53,7 +57,7 @@
 			"indented_qty": get_indented_qty(item_code, warehouse),
 			"ordered_qty": get_ordered_qty(item_code, warehouse),
 			"planned_qty": get_planned_qty(item_code, warehouse),
-			"actual_qty": get_balance_qty_from_sle(item_code, warehouse)
+			"actual_qty": get_balance_qty_from_sle(item_code, warehouse),
 		}
 
 		bin = get_bin(item_code, warehouse)
diff --git a/erpnext/patches/v13_0/add_cost_center_in_loans.py b/erpnext/patches/v13_0/add_cost_center_in_loans.py
new file mode 100644
index 0000000..e293cf2
--- /dev/null
+++ b/erpnext/patches/v13_0/add_cost_center_in_loans.py
@@ -0,0 +1,12 @@
+import frappe
+
+
+def execute():
+	frappe.reload_doc("loan_management", "doctype", "loan")
+	loan = frappe.qb.DocType("Loan")
+
+	for company in frappe.get_all("Company", pluck="name"):
+		default_cost_center = frappe.db.get_value("Company", company, "cost_center")
+		frappe.qb.update(loan).set(loan.cost_center, default_cost_center).where(
+			loan.company == company
+		).run()
diff --git a/erpnext/patches/v13_0/add_custom_field_for_south_africa.py b/erpnext/patches/v13_0/add_custom_field_for_south_africa.py
index b34b5c1..353376b 100644
--- a/erpnext/patches/v13_0/add_custom_field_for_south_africa.py
+++ b/erpnext/patches/v13_0/add_custom_field_for_south_africa.py
@@ -7,13 +7,13 @@
 
 
 def execute():
-	company = frappe.get_all('Company', filters = {'country': 'South Africa'})
+	company = frappe.get_all("Company", filters={"country": "South Africa"})
 	if not company:
 		return
 
-	frappe.reload_doc('regional', 'doctype', 'south_africa_vat_settings')
-	frappe.reload_doc('regional', 'report', 'vat_audit_report')
-	frappe.reload_doc('accounts', 'doctype', 'south_africa_vat_account')
+	frappe.reload_doc("regional", "doctype", "south_africa_vat_settings")
+	frappe.reload_doc("regional", "report", "vat_audit_report")
+	frappe.reload_doc("accounts", "doctype", "south_africa_vat_account")
 
 	make_custom_fields()
 	add_permissions()
diff --git a/erpnext/patches/v13_0/add_default_interview_notification_templates.py b/erpnext/patches/v13_0/add_default_interview_notification_templates.py
index 6b5de52..9a47efe 100644
--- a/erpnext/patches/v13_0/add_default_interview_notification_templates.py
+++ b/erpnext/patches/v13_0/add_default_interview_notification_templates.py
@@ -5,32 +5,40 @@
 
 
 def execute():
-	if not frappe.db.exists('Email Template', _('Interview Reminder')):
-		base_path = frappe.get_app_path('erpnext', 'hr', 'doctype')
-		response = frappe.read_file(os.path.join(base_path, 'interview/interview_reminder_notification_template.html'))
+	if not frappe.db.exists("Email Template", _("Interview Reminder")):
+		base_path = frappe.get_app_path("erpnext", "hr", "doctype")
+		response = frappe.read_file(
+			os.path.join(base_path, "interview/interview_reminder_notification_template.html")
+		)
 
-		frappe.get_doc({
-			'doctype': 'Email Template',
-			'name': _('Interview Reminder'),
-			'response': response,
-			'subject': _('Interview Reminder'),
-			'owner': frappe.session.user,
-		}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "Email Template",
+				"name": _("Interview Reminder"),
+				"response": response,
+				"subject": _("Interview Reminder"),
+				"owner": frappe.session.user,
+			}
+		).insert(ignore_permissions=True)
 
-	if not frappe.db.exists('Email Template', _('Interview Feedback Reminder')):
-		base_path = frappe.get_app_path('erpnext', 'hr', 'doctype')
-		response = frappe.read_file(os.path.join(base_path, 'interview/interview_feedback_reminder_template.html'))
+	if not frappe.db.exists("Email Template", _("Interview Feedback Reminder")):
+		base_path = frappe.get_app_path("erpnext", "hr", "doctype")
+		response = frappe.read_file(
+			os.path.join(base_path, "interview/interview_feedback_reminder_template.html")
+		)
 
-		frappe.get_doc({
-			'doctype': 'Email Template',
-			'name': _('Interview Feedback Reminder'),
-			'response': response,
-			'subject': _('Interview Feedback Reminder'),
-			'owner': frappe.session.user,
-		}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "Email Template",
+				"name": _("Interview Feedback Reminder"),
+				"response": response,
+				"subject": _("Interview Feedback Reminder"),
+				"owner": frappe.session.user,
+			}
+		).insert(ignore_permissions=True)
 
-	hr_settings = frappe.get_doc('HR Settings')
-	hr_settings.interview_reminder_template = _('Interview Reminder')
-	hr_settings.feedback_reminder_notification_template = _('Interview Feedback Reminder')
+	hr_settings = frappe.get_doc("HR Settings")
+	hr_settings.interview_reminder_template = _("Interview Reminder")
+	hr_settings.feedback_reminder_notification_template = _("Interview Feedback Reminder")
 	hr_settings.flags.ignore_links = True
 	hr_settings.save()
diff --git a/erpnext/patches/v13_0/add_doctype_to_sla.py b/erpnext/patches/v13_0/add_doctype_to_sla.py
index 8cee378..5f5974f 100644
--- a/erpnext/patches/v13_0/add_doctype_to_sla.py
+++ b/erpnext/patches/v13_0/add_doctype_to_sla.py
@@ -7,15 +7,15 @@
 
 
 def execute():
-	frappe.reload_doc('support', 'doctype', 'sla_fulfilled_on_status')
-	frappe.reload_doc('support', 'doctype', 'service_level_agreement')
-	if frappe.db.has_column('Service Level Agreement', 'enable'):
-		rename_field('Service Level Agreement', 'enable', 'enabled')
+	frappe.reload_doc("support", "doctype", "sla_fulfilled_on_status")
+	frappe.reload_doc("support", "doctype", "service_level_agreement")
+	if frappe.db.has_column("Service Level Agreement", "enable"):
+		rename_field("Service Level Agreement", "enable", "enabled")
 
-	for sla in frappe.get_all('Service Level Agreement'):
-		agreement = frappe.get_doc('Service Level Agreement', sla.name)
-		agreement.document_type = 'Issue'
+	for sla in frappe.get_all("Service Level Agreement"):
+		agreement = frappe.get_doc("Service Level Agreement", sla.name)
+		agreement.document_type = "Issue"
 		agreement.apply_sla_for_resolution = 1
-		agreement.append('sla_fulfilled_on', {'status': 'Resolved'})
-		agreement.append('sla_fulfilled_on', {'status': 'Closed'})
+		agreement.append("sla_fulfilled_on", {"status": "Resolved"})
+		agreement.append("sla_fulfilled_on", {"status": "Closed"})
 		agreement.save()
diff --git a/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py b/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py
index bd18b9b..ddbb7fd 100644
--- a/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py
+++ b/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py
@@ -9,31 +9,34 @@
 
 
 def execute():
-	if not frappe.db.has_column('Work Order', 'has_batch_no'):
+	if not frappe.db.has_column("Work Order", "has_batch_no"):
 		return
 
-	frappe.reload_doc('manufacturing', 'doctype', 'manufacturing_settings')
-	if cint(frappe.db.get_single_value('Manufacturing Settings', 'make_serial_no_batch_from_work_order')):
+	frappe.reload_doc("manufacturing", "doctype", "manufacturing_settings")
+	if cint(
+		frappe.db.get_single_value("Manufacturing Settings", "make_serial_no_batch_from_work_order")
+	):
 		return
 
-	frappe.reload_doc('manufacturing', 'doctype', 'work_order')
+	frappe.reload_doc("manufacturing", "doctype", "work_order")
 	filters = {
-		'docstatus': 1,
-		'produced_qty': ('>', 0),
-		'creation': ('>=', '2021-06-29 00:00:00'),
-		'has_batch_no': 1
+		"docstatus": 1,
+		"produced_qty": (">", 0),
+		"creation": (">=", "2021-06-29 00:00:00"),
+		"has_batch_no": 1,
 	}
 
-	fields = ['name', 'production_item']
+	fields = ["name", "production_item"]
 
-	work_orders = [d.name for d in frappe.get_all('Work Order', filters = filters, fields=fields)]
+	work_orders = [d.name for d in frappe.get_all("Work Order", filters=filters, fields=fields)]
 
 	if not work_orders:
 		return
 
 	repost_stock_entries = []
 
-	stock_entries = frappe.db.sql_list('''
+	stock_entries = frappe.db.sql_list(
+		"""
 		SELECT
 			se.name
 		FROM
@@ -45,18 +48,20 @@
 			)
 		ORDER BY
 			se.posting_date, se.posting_time
-	''',  (work_orders,))
+	""",
+		(work_orders,),
+	)
 
 	if stock_entries:
-		print('Length of stock entries', len(stock_entries))
+		print("Length of stock entries", len(stock_entries))
 
 	for stock_entry in stock_entries:
-		doc = frappe.get_doc('Stock Entry', stock_entry)
+		doc = frappe.get_doc("Stock Entry", stock_entry)
 		doc.set_work_order_details()
 		doc.load_items_from_bom()
 		doc.calculate_rate_and_amount()
 		set_expense_account(doc)
-		doc.make_batches('t_warehouse')
+		doc.make_batches("t_warehouse")
 
 		if doc.docstatus == 0:
 			doc.save()
@@ -67,10 +72,14 @@
 	for repost_doc in repost_stock_entries:
 		repost_future_sle_and_gle(repost_doc)
 
+
 def set_expense_account(doc):
 	for row in doc.items:
 		if row.is_finished_item and not row.expense_account:
-			row.expense_account = frappe.get_cached_value('Company', doc.company, 'stock_adjustment_account')
+			row.expense_account = frappe.get_cached_value(
+				"Company", doc.company, "stock_adjustment_account"
+			)
+
 
 def repost_stock_entry(doc):
 	doc.db_update()
@@ -86,29 +95,35 @@
 		try:
 			make_sl_entries(sl_entries, True)
 		except Exception:
-			print(f'SLE entries not posted for the stock entry {doc.name}')
-			traceback = frappe.get_traceback()
-			frappe.log_error(traceback)
+			print(f"SLE entries not posted for the stock entry {doc.name}")
+			doc.log_error("Stock respost failed")
+
 
 def get_sle_for_target_warehouse(doc, sl_entries, finished_item_row):
-	for d in doc.get('items'):
+	for d in doc.get("items"):
 		if cstr(d.t_warehouse) and finished_item_row and d.name == finished_item_row.name:
-			sle = doc.get_sl_entries(d, {
-				"warehouse": cstr(d.t_warehouse),
-				"actual_qty": flt(d.transfer_qty),
-				"incoming_rate": flt(d.valuation_rate)
-			})
+			sle = doc.get_sl_entries(
+				d,
+				{
+					"warehouse": cstr(d.t_warehouse),
+					"actual_qty": flt(d.transfer_qty),
+					"incoming_rate": flt(d.valuation_rate),
+				},
+			)
 
 			sle.recalculate_rate = 1
 			sl_entries.append(sle)
 
+
 def repost_future_sle_and_gle(doc):
-	args = frappe._dict({
-		"posting_date": doc.posting_date,
-		"posting_time": doc.posting_time,
-		"voucher_type": doc.doctype,
-		"voucher_no": doc.name,
-		"company": doc.company
-	})
+	args = frappe._dict(
+		{
+			"posting_date": doc.posting_date,
+			"posting_time": doc.posting_time,
+			"voucher_type": doc.doctype,
+			"voucher_no": doc.name,
+			"company": doc.company,
+		}
+	)
 
 	create_repost_item_valuation_entry(args)
diff --git a/erpnext/patches/v13_0/add_naming_series_to_old_projects.py b/erpnext/patches/v13_0/add_naming_series_to_old_projects.py
index 71abe2e..7dce95c 100644
--- a/erpnext/patches/v13_0/add_naming_series_to_old_projects.py
+++ b/erpnext/patches/v13_0/add_naming_series_to_old_projects.py
@@ -4,8 +4,10 @@
 def execute():
 	frappe.reload_doc("projects", "doctype", "project")
 
-	frappe.db.sql("""UPDATE `tabProject`
+	frappe.db.sql(
+		"""UPDATE `tabProject`
 		SET
 			naming_series = 'PROJ-.####'
 		WHERE
-			naming_series is NULL""")
+			naming_series is NULL"""
+	)
diff --git a/erpnext/patches/v13_0/add_po_to_global_search.py b/erpnext/patches/v13_0/add_po_to_global_search.py
index 7fbaffb..514cd34 100644
--- a/erpnext/patches/v13_0/add_po_to_global_search.py
+++ b/erpnext/patches/v13_0/add_po_to_global_search.py
@@ -2,15 +2,13 @@
 
 
 def execute():
-    global_search_settings = frappe.get_single("Global Search Settings")
+	global_search_settings = frappe.get_single("Global Search Settings")
 
-    if "Purchase Order" in (
-        dt.document_type for dt in global_search_settings.allowed_in_global_search
-    ):
-        return
+	if "Purchase Order" in (
+		dt.document_type for dt in global_search_settings.allowed_in_global_search
+	):
+		return
 
-    global_search_settings.append(
-        "allowed_in_global_search", {"document_type": "Purchase Order"}
-    )
+	global_search_settings.append("allowed_in_global_search", {"document_type": "Purchase Order"})
 
-    global_search_settings.save(ignore_permissions=True)
+	global_search_settings.save(ignore_permissions=True)
diff --git a/erpnext/patches/v13_0/change_default_item_manufacturer_fieldtype.py b/erpnext/patches/v13_0/change_default_item_manufacturer_fieldtype.py
new file mode 100644
index 0000000..0b00188
--- /dev/null
+++ b/erpnext/patches/v13_0/change_default_item_manufacturer_fieldtype.py
@@ -0,0 +1,16 @@
+import frappe
+
+
+def execute():
+
+	# Erase all default item manufacturers that dont exist.
+	item = frappe.qb.DocType("Item")
+	manufacturer = frappe.qb.DocType("Manufacturer")
+
+	(
+		frappe.qb.update(item)
+		.set(item.default_item_manufacturer, None)
+		.left_join(manufacturer)
+		.on(item.default_item_manufacturer == manufacturer.name)
+		.where(manufacturer.name.isnull() & item.default_item_manufacturer.isnotnull())
+	).run()
diff --git a/erpnext/patches/v13_0/change_default_pos_print_format.py b/erpnext/patches/v13_0/change_default_pos_print_format.py
index 23cad31..be478a2 100644
--- a/erpnext/patches/v13_0/change_default_pos_print_format.py
+++ b/erpnext/patches/v13_0/change_default_pos_print_format.py
@@ -5,4 +5,5 @@
 	frappe.db.sql(
 		"""UPDATE `tabPOS Profile` profile
 		SET profile.`print_format` = 'POS Invoice'
-		WHERE profile.`print_format` = 'Point of Sale'""")
+		WHERE profile.`print_format` = 'Point of Sale'"""
+	)
diff --git a/erpnext/patches/v13_0/check_is_income_tax_component.py b/erpnext/patches/v13_0/check_is_income_tax_component.py
index 5e1df14..0ae3a3e 100644
--- a/erpnext/patches/v13_0/check_is_income_tax_component.py
+++ b/erpnext/patches/v13_0/check_is_income_tax_component.py
@@ -10,33 +10,36 @@
 
 def execute():
 
-	doctypes = ['salary_component',
-		'Employee Tax Exemption Declaration',
-		'Employee Tax Exemption Proof Submission',
-		'Employee Tax Exemption Declaration Category',
-		'Employee Tax Exemption Proof Submission Detail',
-		'gratuity_rule',
-		'gratuity_rule_slab',
-		'gratuity_applicable_component'
+	doctypes = [
+		"salary_component",
+		"Employee Tax Exemption Declaration",
+		"Employee Tax Exemption Proof Submission",
+		"Employee Tax Exemption Declaration Category",
+		"Employee Tax Exemption Proof Submission Detail",
+		"gratuity_rule",
+		"gratuity_rule_slab",
+		"gratuity_applicable_component",
 	]
 
 	for doctype in doctypes:
-		frappe.reload_doc('Payroll', 'doctype', doctype, force=True)
+		frappe.reload_doc("Payroll", "doctype", doctype, force=True)
 
-
-	reports = ['Professional Tax Deductions', 'Provident Fund Deductions', 'E-Invoice Summary']
+	reports = ["Professional Tax Deductions", "Provident Fund Deductions", "E-Invoice Summary"]
 	for report in reports:
-		frappe.reload_doc('Regional', 'Report', report)
-		frappe.reload_doc('Regional', 'Report', report)
+		frappe.reload_doc("Regional", "Report", report)
+		frappe.reload_doc("Regional", "Report", report)
 
 	if erpnext.get_region() == "India":
-		create_custom_field('Salary Component',
-			dict(fieldname='component_type',
-			label='Component Type',
-			fieldtype='Select',
-			insert_after='description',
-			options='\nProvident Fund\nAdditional Provident Fund\nProvident Fund Loan\nProfessional Tax',
-			depends_on='eval:doc.type == "Deduction"')
+		create_custom_field(
+			"Salary Component",
+			dict(
+				fieldname="component_type",
+				label="Component Type",
+				fieldtype="Select",
+				insert_after="description",
+				options="\nProvident Fund\nAdditional Provident Fund\nProvident Fund Loan\nProfessional Tax",
+				depends_on='eval:doc.type == "Deduction"',
+			),
 		)
 
 	if frappe.db.exists("Salary Component", "Income Tax"):
@@ -44,7 +47,9 @@
 	if frappe.db.exists("Salary Component", "TDS"):
 		frappe.db.set_value("Salary Component", "TDS", "is_income_tax_component", 1)
 
-	components = frappe.db.sql("select name from `tabSalary Component` where variable_based_on_taxable_salary = 1", as_dict=1)
+	components = frappe.db.sql(
+		"select name from `tabSalary Component` where variable_based_on_taxable_salary = 1", as_dict=1
+	)
 	for component in components:
 		frappe.db.set_value("Salary Component", component.name, "is_income_tax_component", 1)
 
@@ -52,4 +57,6 @@
 		if frappe.db.exists("Salary Component", "Provident Fund"):
 			frappe.db.set_value("Salary Component", "Provident Fund", "component_type", "Provident Fund")
 		if frappe.db.exists("Salary Component", "Professional Tax"):
-			frappe.db.set_value("Salary Component", "Professional Tax", "component_type", "Professional Tax")
+			frappe.db.set_value(
+				"Salary Component", "Professional Tax", "component_type", "Professional Tax"
+			)
diff --git a/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py b/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py
index bc64c63..efbb96c 100644
--- a/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py
+++ b/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py
@@ -2,22 +2,24 @@
 
 
 def execute():
-	frappe.reload_doc('stock', 'doctype', 'quality_inspection_parameter')
+	frappe.reload_doc("stock", "doctype", "quality_inspection_parameter")
 
 	# get all distinct parameters from QI readigs table
-	reading_params = frappe.db.get_all("Quality Inspection Reading", fields=["distinct specification"])
+	reading_params = frappe.db.get_all(
+		"Quality Inspection Reading", fields=["distinct specification"]
+	)
 	reading_params = [d.specification for d in reading_params]
 
 	# get all distinct parameters from QI Template as some may be unused in QI
-	template_params = frappe.db.get_all("Item Quality Inspection Parameter", fields=["distinct specification"])
+	template_params = frappe.db.get_all(
+		"Item Quality Inspection Parameter", fields=["distinct specification"]
+	)
 	template_params = [d.specification for d in template_params]
 
 	params = list(set(reading_params + template_params))
 
 	for parameter in params:
 		if not frappe.db.exists("Quality Inspection Parameter", parameter):
-			frappe.get_doc({
-				"doctype": "Quality Inspection Parameter",
-				"parameter": parameter,
-				"description": parameter
-			}).insert(ignore_permissions=True)
+			frappe.get_doc(
+				{"doctype": "Quality Inspection Parameter", "parameter": parameter, "description": parameter}
+			).insert(ignore_permissions=True)
diff --git a/erpnext/patches/v13_0/convert_to_website_item_in_item_card_group_template.py b/erpnext/patches/v13_0/convert_to_website_item_in_item_card_group_template.py
index d3ee3f8..1bac0fd 100644
--- a/erpnext/patches/v13_0/convert_to_website_item_in_item_card_group_template.py
+++ b/erpnext/patches/v13_0/convert_to_website_item_in_item_card_group_template.py
@@ -7,51 +7,54 @@
 
 
 def execute():
-    """
-        Convert all Item links to Website Item link values in
-        exisitng 'Item Card Group' Web Page Block data.
-    """
-    frappe.reload_doc("e_commerce", "web_template", "item_card_group")
+	"""
+	Convert all Item links to Website Item link values in
+	exisitng 'Item Card Group' Web Page Block data.
+	"""
+	frappe.reload_doc("e_commerce", "web_template", "item_card_group")
 
-    blocks = frappe.db.get_all(
-        "Web Page Block",
-        filters={"web_template": "Item Card Group"},
-        fields=["parent", "web_template_values", "name"]
-    )
+	blocks = frappe.db.get_all(
+		"Web Page Block",
+		filters={"web_template": "Item Card Group"},
+		fields=["parent", "web_template_values", "name"],
+	)
 
-    fields = generate_fields_to_edit()
+	fields = generate_fields_to_edit()
 
-    for block in blocks:
-        web_template_value = json.loads(block.get('web_template_values'))
+	for block in blocks:
+		web_template_value = json.loads(block.get("web_template_values"))
 
-        for field in fields:
-            item = web_template_value.get(field)
-            if not item:
-                continue
+		for field in fields:
+			item = web_template_value.get(field)
+			if not item:
+				continue
 
-            if frappe.db.exists("Website Item", {"item_code": item}):
-                website_item = frappe.db.get_value("Website Item", {"item_code": item})
-            else:
-                website_item = make_new_website_item(item)
+			if frappe.db.exists("Website Item", {"item_code": item}):
+				website_item = frappe.db.get_value("Website Item", {"item_code": item})
+			else:
+				website_item = make_new_website_item(item)
 
-            if website_item:
-                web_template_value[field] = website_item
+			if website_item:
+				web_template_value[field] = website_item
 
-        frappe.db.set_value("Web Page Block", block.name, "web_template_values", json.dumps(web_template_value))
+		frappe.db.set_value(
+			"Web Page Block", block.name, "web_template_values", json.dumps(web_template_value)
+		)
+
 
 def generate_fields_to_edit() -> List:
-    fields = []
-    for i in range(1, 13):
-        fields.append(f"card_{i}_item") # fields like 'card_1_item', etc.
+	fields = []
+	for i in range(1, 13):
+		fields.append(f"card_{i}_item")  # fields like 'card_1_item', etc.
 
-    return fields
+	return fields
+
 
 def make_new_website_item(item: str) -> Union[str, None]:
-    try:
-        doc = frappe.get_doc("Item", item)
-        web_item = make_website_item(doc) # returns [website_item.name, item_name]
-        return web_item[0]
-    except Exception:
-        title = f"{item}: Error while converting to Website Item "
-        frappe.log_error(title + "for Item Card Group Template" + "\n\n" + frappe.get_traceback(), title=title)
-        return None
+	try:
+		doc = frappe.get_doc("Item", item)
+		web_item = make_website_item(doc)  # returns [website_item.name, item_name]
+		return web_item[0]
+	except Exception:
+		doc.log_error("Website Item creation failed")
+		return None
diff --git a/erpnext/patches/v13_0/copy_custom_field_filters_to_website_item.py b/erpnext/patches/v13_0/copy_custom_field_filters_to_website_item.py
new file mode 100644
index 0000000..e8d0b59
--- /dev/null
+++ b/erpnext/patches/v13_0/copy_custom_field_filters_to_website_item.py
@@ -0,0 +1,94 @@
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+
+
+def execute():
+	"Add Field Filters, that are not standard fields in Website Item, as Custom Fields."
+
+	def move_table_multiselect_data(docfield):
+		"Copy child table data (Table Multiselect) from Item to Website Item for a docfield."
+		table_multiselect_data = get_table_multiselect_data(docfield)
+		field = docfield.fieldname
+
+		for row in table_multiselect_data:
+			# add copied multiselect data rows in Website Item
+			web_item = frappe.db.get_value("Website Item", {"item_code": row.parent})
+			web_item_doc = frappe.get_doc("Website Item", web_item)
+
+			child_doc = frappe.new_doc(docfield.options, web_item_doc, field)
+
+			for field in ["name", "creation", "modified", "idx"]:
+				row[field] = None
+
+			child_doc.update(row)
+
+			child_doc.parenttype = "Website Item"
+			child_doc.parent = web_item
+
+			child_doc.insert()
+
+	def get_table_multiselect_data(docfield):
+		child_table = frappe.qb.DocType(docfield.options)
+		item = frappe.qb.DocType("Item")
+
+		table_multiselect_data = (  # query table data for field
+			frappe.qb.from_(child_table)
+			.join(item)
+			.on(item.item_code == child_table.parent)
+			.select(child_table.star)
+			.where((child_table.parentfield == docfield.fieldname) & (item.published_in_website == 1))
+		).run(as_dict=True)
+
+		return table_multiselect_data
+
+	settings = frappe.get_doc("E Commerce Settings")
+
+	if not (settings.enable_field_filters or settings.filter_fields):
+		return
+
+	item_meta = frappe.get_meta("Item")
+	valid_item_fields = [
+		df.fieldname for df in item_meta.fields if df.fieldtype in ["Link", "Table MultiSelect"]
+	]
+
+	web_item_meta = frappe.get_meta("Website Item")
+	valid_web_item_fields = [
+		df.fieldname for df in web_item_meta.fields if df.fieldtype in ["Link", "Table MultiSelect"]
+	]
+
+	for row in settings.filter_fields:
+		# skip if illegal field
+		if row.fieldname not in valid_item_fields:
+			continue
+
+		# if Item field is not in Website Item, add it as a custom field
+		if row.fieldname not in valid_web_item_fields:
+			df = item_meta.get_field(row.fieldname)
+			create_custom_field(
+				"Website Item",
+				dict(
+					owner="Administrator",
+					fieldname=df.fieldname,
+					label=df.label,
+					fieldtype=df.fieldtype,
+					options=df.options,
+					description=df.description,
+					read_only=df.read_only,
+					no_copy=df.no_copy,
+					insert_after="on_backorder",
+				),
+			)
+
+			# map field values
+			if df.fieldtype == "Table MultiSelect":
+				move_table_multiselect_data(df)
+			else:
+				frappe.db.sql(  # nosemgrep
+					"""
+						UPDATE `tabWebsite Item` wi, `tabItem` i
+						SET wi.{0} = i.{0}
+						WHERE wi.item_code = i.item_code
+					""".format(
+						row.fieldname
+					)
+				)
diff --git a/erpnext/patches/v13_0/create_accounting_dimensions_in_orders.py b/erpnext/patches/v13_0/create_accounting_dimensions_in_orders.py
new file mode 100644
index 0000000..7e6e820
--- /dev/null
+++ b/erpnext/patches/v13_0/create_accounting_dimensions_in_orders.py
@@ -0,0 +1,39 @@
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+
+
+def execute():
+	accounting_dimensions = frappe.db.get_all(
+		"Accounting Dimension", fields=["fieldname", "label", "document_type", "disabled"]
+	)
+
+	if not accounting_dimensions:
+		return
+
+	count = 1
+	for d in accounting_dimensions:
+
+		if count % 2 == 0:
+			insert_after_field = "dimension_col_break"
+		else:
+			insert_after_field = "accounting_dimensions_section"
+
+		for doctype in ["Purchase Order", "Purchase Receipt", "Sales Order"]:
+
+			field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname})
+
+			if field:
+				continue
+
+			df = {
+				"fieldname": d.fieldname,
+				"label": d.label,
+				"fieldtype": "Link",
+				"options": d.document_type,
+				"insert_after": insert_after_field,
+			}
+
+			create_custom_field(doctype, df, ignore_validate=True)
+			frappe.clear_cache(doctype=doctype)
+
+		count += 1
diff --git a/erpnext/patches/v13_0/create_accounting_dimensions_in_pos_doctypes.py b/erpnext/patches/v13_0/create_accounting_dimensions_in_pos_doctypes.py
index 4450108..51ab0e8 100644
--- a/erpnext/patches/v13_0/create_accounting_dimensions_in_pos_doctypes.py
+++ b/erpnext/patches/v13_0/create_accounting_dimensions_in_pos_doctypes.py
@@ -3,9 +3,12 @@
 
 
 def execute():
-	frappe.reload_doc('accounts', 'doctype', 'accounting_dimension')
-	accounting_dimensions = frappe.db.sql("""select fieldname, label, document_type, disabled from
-		`tabAccounting Dimension`""", as_dict=1)
+	frappe.reload_doc("accounts", "doctype", "accounting_dimension")
+	accounting_dimensions = frappe.db.sql(
+		"""select fieldname, label, document_type, disabled from
+		`tabAccounting Dimension`""",
+		as_dict=1,
+	)
 
 	if not accounting_dimensions:
 		return
@@ -14,9 +17,9 @@
 	for d in accounting_dimensions:
 
 		if count % 2 == 0:
-			insert_after_field = 'dimension_col_break'
+			insert_after_field = "dimension_col_break"
 		else:
-			insert_after_field = 'accounting_dimensions_section'
+			insert_after_field = "accounting_dimensions_section"
 
 		for doctype in ["POS Invoice", "POS Invoice Item"]:
 
@@ -32,10 +35,10 @@
 				"label": d.label,
 				"fieldtype": "Link",
 				"options": d.document_type,
-				"insert_after": insert_after_field
+				"insert_after": insert_after_field,
 			}
 
-			if df['fieldname'] not in fieldnames:
+			if df["fieldname"] not in fieldnames:
 				create_custom_field(doctype, df)
 				frappe.clear_cache(doctype=doctype)
 
diff --git a/erpnext/patches/v13_0/create_custom_field_for_finance_book.py b/erpnext/patches/v13_0/create_custom_field_for_finance_book.py
index 313b0e9..2b8666d 100644
--- a/erpnext/patches/v13_0/create_custom_field_for_finance_book.py
+++ b/erpnext/patches/v13_0/create_custom_field_for_finance_book.py
@@ -3,18 +3,18 @@
 
 
 def execute():
-	company = frappe.get_all('Company', filters = {'country': 'India'})
+	company = frappe.get_all("Company", filters={"country": "India"})
 	if not company:
 		return
 
 	custom_field = {
-		'Finance Book': [
+		"Finance Book": [
 			{
-				'fieldname': 'for_income_tax',
-				'label': 'For Income Tax',
-				'fieldtype': 'Check',
-				'insert_after': 'finance_book_name',
-				'description': 'If the asset is put to use for less than 180 days, the first Depreciation Rate will be reduced by 50%.'
+				"fieldname": "for_income_tax",
+				"label": "For Income Tax",
+				"fieldtype": "Check",
+				"insert_after": "finance_book_name",
+				"description": "If the asset is put to use for less than 180 days, the first Depreciation Rate will be reduced by 50%.",
 			}
 		]
 	}
diff --git a/erpnext/patches/v13_0/create_ksa_vat_custom_fields.py b/erpnext/patches/v13_0/create_ksa_vat_custom_fields.py
index f33b4b3..093463a 100644
--- a/erpnext/patches/v13_0/create_ksa_vat_custom_fields.py
+++ b/erpnext/patches/v13_0/create_ksa_vat_custom_fields.py
@@ -4,9 +4,8 @@
 
 
 def execute():
-	company = frappe.get_all('Company', filters = {'country': 'Saudi Arabia'})
+	company = frappe.get_all("Company", filters={"country": "Saudi Arabia"})
 	if not company:
 		return
 
 	make_custom_fields()
-
diff --git a/erpnext/patches/v13_0/create_leave_policy_assignment_based_on_employee_current_leave_policy.py b/erpnext/patches/v13_0/create_leave_policy_assignment_based_on_employee_current_leave_policy.py
deleted file mode 100644
index 5512543..0000000
--- a/erpnext/patches/v13_0/create_leave_policy_assignment_based_on_employee_current_leave_policy.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# Copyright (c) 2019, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-
-import frappe
-
-
-def execute():
-    if "leave_policy" in frappe.db.get_table_columns("Employee"):
-        employees_with_leave_policy = frappe.db.sql("SELECT name, leave_policy FROM `tabEmployee` WHERE leave_policy IS NOT NULL and leave_policy != ''", as_dict = 1)
-
-        employee_with_assignment = []
-        leave_policy =[]
-
-        #for employee
-
-        for employee in employees_with_leave_policy:
-            alloc = frappe.db.exists("Leave Allocation", {"employee":employee.name, "leave_policy": employee.leave_policy, "docstatus": 1})
-            if not alloc:
-                create_assignment(employee.name, employee.leave_policy)
-
-            employee_with_assignment.append(employee.name)
-            leave_policy.append(employee.leave_policy)
-
-
-    if "default_leave_policy" in frappe.db.get_table_columns("Employee"):
-        employee_grade_with_leave_policy = frappe.db.sql("SELECT name, default_leave_policy FROM `tabEmployee Grade` WHERE default_leave_policy IS NOT NULL and default_leave_policy!=''", as_dict = 1)
-
-        #for whole employee Grade
-
-        for grade in employee_grade_with_leave_policy:
-            employees = get_employee_with_grade(grade.name)
-            for employee in employees:
-
-                if employee not in employee_with_assignment: #Will ensure no duplicate
-                    alloc = frappe.db.exists("Leave Allocation", {"employee":employee.name, "leave_policy": grade.default_leave_policy, "docstatus": 1})
-                    if not alloc:
-                        create_assignment(employee.name, grade.default_leave_policy)
-                    leave_policy.append(grade.default_leave_policy)
-
-    #for old Leave allocation and leave policy from allocation, which may got updated in employee grade.
-    leave_allocations = frappe.db.sql("SELECT leave_policy, leave_period, employee FROM `tabLeave Allocation` WHERE leave_policy IS NOT NULL and leave_policy != '' and docstatus = 1 ", as_dict = 1)
-
-    for allocation in leave_allocations:
-        if allocation.leave_policy not in leave_policy:
-            create_assignment(allocation.employee, allocation.leave_policy, leave_period=allocation.leave_period,
-                allocation_exists=True)
-
-def create_assignment(employee, leave_policy, leave_period=None, allocation_exists = False):
-
-    filters = {"employee":employee, "leave_policy": leave_policy}
-    if leave_period:
-        filters["leave_period"] = leave_period
-
-    frappe.reload_doc('hr', 'doctype', 'leave_policy_assignment')
-
-    if not frappe.db.exists("Leave Policy Assignment" , filters):
-        lpa = frappe.new_doc("Leave Policy Assignment")
-        lpa.employee = employee
-        lpa.leave_policy = leave_policy
-
-        lpa.flags.ignore_mandatory = True
-        if allocation_exists:
-            lpa.assignment_based_on = 'Leave Period'
-            lpa.leave_period = leave_period
-            lpa.leaves_allocated = 1
-
-        lpa.save()
-        if allocation_exists:
-            lpa.submit()
-            #Updating old Leave Allocation
-            frappe.db.sql("Update `tabLeave Allocation` set leave_policy_assignment = %s", lpa.name)
-
-
-def get_employee_with_grade(grade):
-    return frappe.get_list("Employee", filters = {"grade": grade})
diff --git a/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py b/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py
index 87c9cf1..66aae9a 100644
--- a/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py
+++ b/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py
@@ -8,12 +8,13 @@
 
 
 def execute():
-	company = frappe.get_all('Company', filters = {'country': ['in', ['Saudi Arabia', 'United Arab Emirates']]})
+	company = frappe.get_all(
+		"Company", filters={"country": ["in", ["Saudi Arabia", "United Arab Emirates"]]}
+	)
 	if not company:
 		return
 
-
-	frappe.reload_doc('accounts', 'doctype', 'pos_invoice')
-	frappe.reload_doc('accounts', 'doctype', 'pos_invoice_item')
+	frappe.reload_doc("accounts", "doctype", "pos_invoice")
+	frappe.reload_doc("accounts", "doctype", "pos_invoice_item")
 
 	make_custom_fields()
diff --git a/erpnext/patches/v13_0/create_website_items.py b/erpnext/patches/v13_0/create_website_items.py
index da162a3..cb7bfc3 100644
--- a/erpnext/patches/v13_0/create_website_items.py
+++ b/erpnext/patches/v13_0/create_website_items.py
@@ -11,14 +11,31 @@
 	frappe.reload_doc("e_commerce", "doctype", "e_commerce_settings")
 	frappe.reload_doc("stock", "doctype", "item")
 
-	item_fields = ["item_code", "item_name", "item_group", "stock_uom", "brand", "image",
-		"has_variants", "variant_of", "description", "weightage"]
-	web_fields_to_map = ["route", "slideshow", "website_image_alt",
-		"website_warehouse", "web_long_description", "website_content", "thumbnail"]
+	item_fields = [
+		"item_code",
+		"item_name",
+		"item_group",
+		"stock_uom",
+		"brand",
+		"image",
+		"has_variants",
+		"variant_of",
+		"description",
+		"weightage",
+	]
+	web_fields_to_map = [
+		"route",
+		"slideshow",
+		"website_image_alt",
+		"website_warehouse",
+		"web_long_description",
+		"website_content",
+		"thumbnail",
+	]
 
 	# get all valid columns (fields) from Item master DB schema
-	item_table_fields = frappe.db.sql("desc `tabItem`", as_dict=1) # nosemgrep
-	item_table_fields = [d.get('Field') for d in item_table_fields]
+	item_table_fields = frappe.db.sql("desc `tabItem`", as_dict=1)  # nosemgrep
+	item_table_fields = [d.get("Field") for d in item_table_fields]
 
 	# prepare fields to query from Item, check if the web field exists in Item master
 	web_query_fields = []
@@ -38,11 +55,7 @@
 		# most likely a fresh installation that doesnt need this patch
 		return
 
-	items = frappe.db.get_all(
-		"Item",
-		fields=item_fields,
-		or_filters=or_filters
-	)
+	items = frappe.db.get_all("Item", fields=item_fields, or_filters=or_filters)
 	total_count = len(items)
 
 	for count, item in enumerate(items, start=1):
@@ -62,11 +75,11 @@
 		for doctype in ("Website Item Group", "Item Website Specification"):
 			frappe.db.set_value(
 				doctype,
-				{"parenttype": "Item", "parent": item.item_code}, # filters
-				{"parenttype": "Website Item", "parent": website_item.name} # value dict
+				{"parenttype": "Item", "parent": item.item_code},  # filters
+				{"parenttype": "Website Item", "parent": website_item.name},  # value dict
 			)
 
-		if count % 20 == 0: # commit after every 20 items
+		if count % 20 == 0:  # commit after every 20 items
 			frappe.db.commit()
 
-		frappe.utils.update_progress_bar('Creating Website Items', count, total_count)
+		frappe.utils.update_progress_bar("Creating Website Items", count, total_count)
diff --git a/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py b/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py
index 078c558..5cbd0b5 100644
--- a/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py
+++ b/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py
@@ -5,35 +5,68 @@
 
 
 def execute():
-	company = frappe.get_all('Company', filters = {'country': 'United States'}, fields=['name'])
+	company = frappe.get_all("Company", filters={"country": "United States"}, fields=["name"])
 	if not company:
 		return
 
-	TAXJAR_CREATE_TRANSACTIONS = frappe.db.get_single_value("TaxJar Settings", "taxjar_create_transactions")
+	TAXJAR_CREATE_TRANSACTIONS = frappe.db.get_single_value(
+		"TaxJar Settings", "taxjar_create_transactions"
+	)
 	TAXJAR_CALCULATE_TAX = frappe.db.get_single_value("TaxJar Settings", "taxjar_calculate_tax")
 	TAXJAR_SANDBOX_MODE = frappe.db.get_single_value("TaxJar Settings", "is_sandbox")
 
-	if (not TAXJAR_CREATE_TRANSACTIONS and not TAXJAR_CALCULATE_TAX and not TAXJAR_SANDBOX_MODE):
+	if not TAXJAR_CREATE_TRANSACTIONS and not TAXJAR_CALCULATE_TAX and not TAXJAR_SANDBOX_MODE:
 		return
 
 	custom_fields = {
-		'Sales Invoice Item': [
-			dict(fieldname='product_tax_category', fieldtype='Link', insert_after='description', options='Product Tax Category',
-				label='Product Tax Category', fetch_from='item_code.product_tax_category'),
-			dict(fieldname='tax_collectable', fieldtype='Currency', insert_after='net_amount',
-				label='Tax Collectable', read_only=1, options='currency'),
-			dict(fieldname='taxable_amount', fieldtype='Currency', insert_after='tax_collectable',
-				label='Taxable Amount', read_only=1, options='currency')
+		"Sales Invoice Item": [
+			dict(
+				fieldname="product_tax_category",
+				fieldtype="Link",
+				insert_after="description",
+				options="Product Tax Category",
+				label="Product Tax Category",
+				fetch_from="item_code.product_tax_category",
+			),
+			dict(
+				fieldname="tax_collectable",
+				fieldtype="Currency",
+				insert_after="net_amount",
+				label="Tax Collectable",
+				read_only=1,
+				options="currency",
+			),
+			dict(
+				fieldname="taxable_amount",
+				fieldtype="Currency",
+				insert_after="tax_collectable",
+				label="Taxable Amount",
+				read_only=1,
+				options="currency",
+			),
 		],
-		'Item': [
-			dict(fieldname='product_tax_category', fieldtype='Link', insert_after='item_group', options='Product Tax Category',
-				label='Product Tax Category')
+		"Item": [
+			dict(
+				fieldname="product_tax_category",
+				fieldtype="Link",
+				insert_after="item_group",
+				options="Product Tax Category",
+				label="Product Tax Category",
+			)
 		],
-		'TaxJar Settings': [
-			dict(fieldname='company', fieldtype='Link', insert_after='configuration', options='Company',
-				label='Company')
-		]
+		"TaxJar Settings": [
+			dict(
+				fieldname="company",
+				fieldtype="Link",
+				insert_after="configuration",
+				options="Company",
+				label="Company",
+			)
+		],
 	}
 	create_custom_fields(custom_fields, update=True)
 	add_permissions()
-	frappe.enqueue('erpnext.erpnext_integrations.doctype.taxjar_settings.taxjar_settings.add_product_tax_categories', now=True)
+	frappe.enqueue(
+		"erpnext.erpnext_integrations.doctype.taxjar_settings.taxjar_settings.add_product_tax_categories",
+		now=True,
+	)
diff --git a/erpnext/patches/v13_0/delete_bank_reconciliation_detail.py b/erpnext/patches/v13_0/delete_bank_reconciliation_detail.py
index 75953b0..c53eb79 100644
--- a/erpnext/patches/v13_0/delete_bank_reconciliation_detail.py
+++ b/erpnext/patches/v13_0/delete_bank_reconciliation_detail.py
@@ -7,7 +7,8 @@
 
 def execute():
 
-	if frappe.db.exists('DocType', 'Bank Reconciliation Detail') and \
-		frappe.db.exists('DocType', 'Bank Clearance Detail'):
+	if frappe.db.exists("DocType", "Bank Reconciliation Detail") and frappe.db.exists(
+		"DocType", "Bank Clearance Detail"
+	):
 
-		frappe.delete_doc("DocType", 'Bank Reconciliation Detail', force=1)
+		frappe.delete_doc("DocType", "Bank Reconciliation Detail", force=1)
diff --git a/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py b/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py
index 2c5c577..3755315 100644
--- a/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py
+++ b/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py
@@ -22,7 +22,7 @@
 
 	frappe.delete_doc("Page", "bank-reconciliation", force=1)
 
-	frappe.reload_doc('accounts', 'doctype', 'bank_transaction')
+	frappe.reload_doc("accounts", "doctype", "bank_transaction")
 
 	rename_field("Bank Transaction", "debit", "deposit")
 	rename_field("Bank Transaction", "credit", "withdrawal")
diff --git a/erpnext/patches/v13_0/delete_old_purchase_reports.py b/erpnext/patches/v13_0/delete_old_purchase_reports.py
index e57d6d0..987f53f 100644
--- a/erpnext/patches/v13_0/delete_old_purchase_reports.py
+++ b/erpnext/patches/v13_0/delete_old_purchase_reports.py
@@ -8,9 +8,12 @@
 
 
 def execute():
-	reports_to_delete = ["Requested Items To Be Ordered",
-		"Purchase Order Items To Be Received or Billed","Purchase Order Items To Be Received",
-		"Purchase Order Items To Be Billed"]
+	reports_to_delete = [
+		"Requested Items To Be Ordered",
+		"Purchase Order Items To Be Received or Billed",
+		"Purchase Order Items To Be Received",
+		"Purchase Order Items To Be Billed",
+	]
 
 	for report in reports_to_delete:
 		if frappe.db.exists("Report", report):
@@ -19,8 +22,9 @@
 
 			frappe.delete_doc("Report", report)
 
+
 def delete_auto_email_reports(report):
-	""" Check for one or multiple Auto Email Reports and delete """
+	"""Check for one or multiple Auto Email Reports and delete"""
 	auto_email_reports = frappe.db.get_values("Auto Email Report", {"report": report}, ["name"])
 	for auto_email_report in auto_email_reports:
 		frappe.delete_doc("Auto Email Report", auto_email_report[0])
diff --git a/erpnext/patches/v13_0/delete_old_sales_reports.py b/erpnext/patches/v13_0/delete_old_sales_reports.py
index e6eba0a..b31c9d1 100644
--- a/erpnext/patches/v13_0/delete_old_sales_reports.py
+++ b/erpnext/patches/v13_0/delete_old_sales_reports.py
@@ -18,14 +18,16 @@
 
 			frappe.delete_doc("Report", report)
 
+
 def delete_auto_email_reports(report):
-	""" Check for one or multiple Auto Email Reports and delete """
+	"""Check for one or multiple Auto Email Reports and delete"""
 	auto_email_reports = frappe.db.get_values("Auto Email Report", {"report": report}, ["name"])
 	for auto_email_report in auto_email_reports:
 		frappe.delete_doc("Auto Email Report", auto_email_report[0])
 
+
 def delete_links_from_desktop_icons(report):
-	""" Check for one or multiple Desktop Icons and delete """
+	"""Check for one or multiple Desktop Icons and delete"""
 	desktop_icons = frappe.db.get_values("Desktop Icon", {"_report": report}, ["name"])
 	for desktop_icon in desktop_icons:
-		frappe.delete_doc("Desktop Icon", desktop_icon[0])
\ No newline at end of file
+		frappe.delete_doc("Desktop Icon", desktop_icon[0])
diff --git a/erpnext/patches/v13_0/delete_orphaned_tables.py b/erpnext/patches/v13_0/delete_orphaned_tables.py
index c32f830..794be09 100644
--- a/erpnext/patches/v13_0/delete_orphaned_tables.py
+++ b/erpnext/patches/v13_0/delete_orphaned_tables.py
@@ -7,63 +7,66 @@
 
 
 def execute():
-    frappe.reload_doc('setup', 'doctype', 'transaction_deletion_record')
+	frappe.reload_doc("setup", "doctype", "transaction_deletion_record")
 
-    if has_deleted_company_transactions():
-        child_doctypes = get_child_doctypes_whose_parent_doctypes_were_affected()
+	if has_deleted_company_transactions():
+		child_doctypes = get_child_doctypes_whose_parent_doctypes_were_affected()
 
-        for doctype in child_doctypes:
-            docs = frappe.get_all(doctype, fields=['name', 'parent', 'parenttype', 'creation'])
+		for doctype in child_doctypes:
+			docs = frappe.get_all(doctype, fields=["name", "parent", "parenttype", "creation"])
 
-            for doc in docs:
-                if not frappe.db.exists(doc['parenttype'], doc['parent']):
-                    frappe.db.delete(doctype, {'name': doc['name']})
+			for doc in docs:
+				if not frappe.db.exists(doc["parenttype"], doc["parent"]):
+					frappe.db.delete(doctype, {"name": doc["name"]})
 
-                elif check_for_new_doc_with_same_name_as_deleted_parent(doc):
-                    frappe.db.delete(doctype, {'name': doc['name']})
+				elif check_for_new_doc_with_same_name_as_deleted_parent(doc):
+					frappe.db.delete(doctype, {"name": doc["name"]})
+
 
 def has_deleted_company_transactions():
-    return frappe.get_all('Transaction Deletion Record')
+	return frappe.get_all("Transaction Deletion Record")
+
 
 def get_child_doctypes_whose_parent_doctypes_were_affected():
-    parent_doctypes = get_affected_doctypes()
-    child_doctypes = frappe.get_all(
-        'DocField',
-        filters={
-            'fieldtype': 'Table',
-            'parent':['in', parent_doctypes]
-        }, pluck='options')
+	parent_doctypes = get_affected_doctypes()
+	child_doctypes = frappe.get_all(
+		"DocField", filters={"fieldtype": "Table", "parent": ["in", parent_doctypes]}, pluck="options"
+	)
 
-    return child_doctypes
+	return child_doctypes
+
 
 def get_affected_doctypes():
-    affected_doctypes = []
-    tdr_docs = frappe.get_all('Transaction Deletion Record', pluck="name")
+	affected_doctypes = []
+	tdr_docs = frappe.get_all("Transaction Deletion Record", pluck="name")
 
-    for tdr in tdr_docs:
-        tdr_doc = frappe.get_doc("Transaction Deletion Record", tdr)
+	for tdr in tdr_docs:
+		tdr_doc = frappe.get_doc("Transaction Deletion Record", tdr)
 
-        for doctype in tdr_doc.doctypes:
-            if is_not_child_table(doctype.doctype_name):
-                affected_doctypes.append(doctype.doctype_name)
+		for doctype in tdr_doc.doctypes:
+			if is_not_child_table(doctype.doctype_name):
+				affected_doctypes.append(doctype.doctype_name)
 
-    affected_doctypes = remove_duplicate_items(affected_doctypes)
-    return affected_doctypes
+	affected_doctypes = remove_duplicate_items(affected_doctypes)
+	return affected_doctypes
+
 
 def is_not_child_table(doctype):
-    return not bool(frappe.get_value('DocType', doctype, 'istable'))
+	return not bool(frappe.get_value("DocType", doctype, "istable"))
+
 
 def remove_duplicate_items(affected_doctypes):
-    return list(set(affected_doctypes))
+	return list(set(affected_doctypes))
+
 
 def check_for_new_doc_with_same_name_as_deleted_parent(doc):
-    """
-        Compares creation times of parent and child docs.
-        Since Transaction Deletion Record resets the naming series after deletion,
-        it allows the creation of new docs with the same names as the deleted ones.
-    """
+	"""
+	Compares creation times of parent and child docs.
+	Since Transaction Deletion Record resets the naming series after deletion,
+	it allows the creation of new docs with the same names as the deleted ones.
+	"""
 
-    parent_creation_time = frappe.db.get_value(doc['parenttype'], doc['parent'], 'creation')
-    child_creation_time = doc['creation']
+	parent_creation_time = frappe.db.get_value(doc["parenttype"], doc["parent"], "creation")
+	child_creation_time = doc["creation"]
 
-    return getdate(parent_creation_time) > getdate(child_creation_time)
+	return getdate(parent_creation_time) > getdate(child_creation_time)
diff --git a/erpnext/patches/v13_0/delete_report_requested_items_to_order.py b/erpnext/patches/v13_0/delete_report_requested_items_to_order.py
index 87565f0..430a305 100644
--- a/erpnext/patches/v13_0/delete_report_requested_items_to_order.py
+++ b/erpnext/patches/v13_0/delete_report_requested_items_to_order.py
@@ -2,12 +2,16 @@
 
 
 def execute():
-	""" Check for one or multiple Auto Email Reports and delete """
-	auto_email_reports = frappe.db.get_values("Auto Email Report", {"report": "Requested Items to Order"}, ["name"])
+	"""Check for one or multiple Auto Email Reports and delete"""
+	auto_email_reports = frappe.db.get_values(
+		"Auto Email Report", {"report": "Requested Items to Order"}, ["name"]
+	)
 	for auto_email_report in auto_email_reports:
 		frappe.delete_doc("Auto Email Report", auto_email_report[0])
 
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		DELETE FROM `tabReport`
 		WHERE name = 'Requested Items to Order'
-	""")
+	"""
+	)
diff --git a/erpnext/patches/v13_0/disable_ksa_print_format_for_others.py b/erpnext/patches/v13_0/disable_ksa_print_format_for_others.py
index aa2a2d3..84b6c37 100644
--- a/erpnext/patches/v13_0/disable_ksa_print_format_for_others.py
+++ b/erpnext/patches/v13_0/disable_ksa_print_format_for_others.py
@@ -7,13 +7,13 @@
 
 
 def execute():
-	company = frappe.get_all('Company', filters = {'country': 'Saudi Arabia'})
+	company = frappe.get_all("Company", filters={"country": "Saudi Arabia"})
 	if company:
 		add_print_formats()
 		return
 
-	if frappe.db.exists('DocType', 'Print Format'):
+	if frappe.db.exists("DocType", "Print Format"):
 		frappe.reload_doc("regional", "print_format", "ksa_vat_invoice", force=True)
 		frappe.reload_doc("regional", "print_format", "ksa_pos_invoice", force=True)
-		for d in ('KSA VAT Invoice', 'KSA POS Invoice'):
+		for d in ("KSA VAT Invoice", "KSA POS Invoice"):
 			frappe.db.set_value("Print Format", d, "disabled", 1)
diff --git a/erpnext/patches/v13_0/drop_razorpay_payload_column.py b/erpnext/patches/v13_0/drop_razorpay_payload_column.py
index 611ba7e..ca166ce 100644
--- a/erpnext/patches/v13_0/drop_razorpay_payload_column.py
+++ b/erpnext/patches/v13_0/drop_razorpay_payload_column.py
@@ -3,5 +3,5 @@
 
 def execute():
 	if frappe.db.exists("DocType", "Membership"):
-		if 'webhook_payload' in frappe.db.get_table_columns("Membership"):
+		if "webhook_payload" in frappe.db.get_table_columns("Membership"):
 			frappe.db.sql("alter table `tabMembership` drop column webhook_payload")
diff --git a/erpnext/patches/v13_0/enable_ksa_vat_docs.py b/erpnext/patches/v13_0/enable_ksa_vat_docs.py
new file mode 100644
index 0000000..4adf4d7
--- /dev/null
+++ b/erpnext/patches/v13_0/enable_ksa_vat_docs.py
@@ -0,0 +1,12 @@
+import frappe
+
+from erpnext.regional.saudi_arabia.setup import add_permissions, add_print_formats
+
+
+def execute():
+	company = frappe.get_all("Company", filters={"country": "Saudi Arabia"})
+	if not company:
+		return
+
+	add_print_formats()
+	add_permissions()
diff --git a/erpnext/patches/v13_0/enable_provisional_accounting.py b/erpnext/patches/v13_0/enable_provisional_accounting.py
index 8e22270..dd18167 100644
--- a/erpnext/patches/v13_0/enable_provisional_accounting.py
+++ b/erpnext/patches/v13_0/enable_provisional_accounting.py
@@ -6,14 +6,11 @@
 
 	company = frappe.qb.DocType("Company")
 
-	frappe.qb.update(
-		company
-	).set(
-		company.enable_provisional_accounting_for_non_stock_items, company.enable_perpetual_inventory_for_non_stock_items
-	).set(
-		company.default_provisional_account, company.service_received_but_not_billed
-	).where(
+	frappe.qb.update(company).set(
+		company.enable_provisional_accounting_for_non_stock_items,
+		company.enable_perpetual_inventory_for_non_stock_items,
+	).set(company.default_provisional_account, company.service_received_but_not_billed).where(
 		company.enable_perpetual_inventory_for_non_stock_items == 1
 	).where(
 		company.service_received_but_not_billed.isnotnull()
-	).run()
\ No newline at end of file
+	).run()
diff --git a/erpnext/patches/v13_0/enable_scheduler_job_for_item_reposting.py b/erpnext/patches/v13_0/enable_scheduler_job_for_item_reposting.py
index 7a51b43..68b5cde 100644
--- a/erpnext/patches/v13_0/enable_scheduler_job_for_item_reposting.py
+++ b/erpnext/patches/v13_0/enable_scheduler_job_for_item_reposting.py
@@ -2,7 +2,6 @@
 
 
 def execute():
-	frappe.reload_doc('core', 'doctype', 'scheduled_job_type')
-	if frappe.db.exists('Scheduled Job Type', 'repost_item_valuation.repost_entries'):
-		frappe.db.set_value('Scheduled Job Type',
-			'repost_item_valuation.repost_entries', 'stopped', 0)
+	frappe.reload_doc("core", "doctype", "scheduled_job_type")
+	if frappe.db.exists("Scheduled Job Type", "repost_item_valuation.repost_entries"):
+		frappe.db.set_value("Scheduled Job Type", "repost_item_valuation.repost_entries", "stopped", 0)
diff --git a/erpnext/patches/v13_0/enable_uoms.py b/erpnext/patches/v13_0/enable_uoms.py
index 4d3f637..8efd67e 100644
--- a/erpnext/patches/v13_0/enable_uoms.py
+++ b/erpnext/patches/v13_0/enable_uoms.py
@@ -2,12 +2,12 @@
 
 
 def execute():
-	frappe.reload_doc('setup', 'doctype', 'uom')
+	frappe.reload_doc("setup", "doctype", "uom")
 
 	uom = frappe.qb.DocType("UOM")
 
-	(frappe.qb
-		.update(uom)
+	(
+		frappe.qb.update(uom)
 		.set(uom.enabled, 1)
 		.where(uom.creation >= "2021-10-18")  # date when this field was released
 	).run()
diff --git a/erpnext/patches/v13_0/fetch_thumbnail_in_website_items.py b/erpnext/patches/v13_0/fetch_thumbnail_in_website_items.py
index 32ad542..9197d86 100644
--- a/erpnext/patches/v13_0/fetch_thumbnail_in_website_items.py
+++ b/erpnext/patches/v13_0/fetch_thumbnail_in_website_items.py
@@ -2,15 +2,10 @@
 
 
 def execute():
-    if frappe.db.has_column("Item", "thumbnail"):
-        website_item = frappe.qb.DocType("Website Item").as_("wi")
-        item = frappe.qb.DocType("Item")
+	if frappe.db.has_column("Item", "thumbnail"):
+		website_item = frappe.qb.DocType("Website Item").as_("wi")
+		item = frappe.qb.DocType("Item")
 
-        frappe.qb.update(website_item).inner_join(item).on(
-            website_item.item_code == item.item_code
-        ).set(
-            website_item.thumbnail, item.thumbnail
-        ).where(
-            website_item.website_image.notnull()
-            & website_item.thumbnail.isnull()
-        ).run()
+		frappe.qb.update(website_item).inner_join(item).on(website_item.item_code == item.item_code).set(
+			website_item.thumbnail, item.thumbnail
+		).where(website_item.website_image.notnull() & website_item.thumbnail.isnull()).run()
diff --git a/erpnext/patches/v13_0/fix_invoice_statuses.py b/erpnext/patches/v13_0/fix_invoice_statuses.py
index 4395757..253b425 100644
--- a/erpnext/patches/v13_0/fix_invoice_statuses.py
+++ b/erpnext/patches/v13_0/fix_invoice_statuses.py
@@ -8,6 +8,7 @@
 
 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
@@ -35,39 +36,28 @@
 			fields=fields,
 			filters={
 				"docstatus": 1,
-				"status": ("in", (
-					"Overdue",
-					"Overdue and Discounted",
-					"Partly Paid",
-					"Partly Paid and Discounted"
-				)),
+				"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
-		}
+		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)}
+			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)
+			invoices_to_update[item.parent].setdefault("payment_schedule", []).append(item)
 
 		status_map = {}
 
@@ -81,19 +71,11 @@
 			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
-			)
-
+			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")
-	)
+	outstanding_amount = flt(doc.outstanding_amount, doc.precision("outstanding_amount"))
 	total = get_total_in_party_account_currency(doc)
 
 	status = ""
diff --git a/erpnext/patches/v13_0/fix_non_unique_represents_company.py b/erpnext/patches/v13_0/fix_non_unique_represents_company.py
index e91c1db..c604f9c 100644
--- a/erpnext/patches/v13_0/fix_non_unique_represents_company.py
+++ b/erpnext/patches/v13_0/fix_non_unique_represents_company.py
@@ -2,8 +2,10 @@
 
 
 def execute():
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		update tabCustomer
 		set represents_company = NULL
 		where represents_company = ''
-	""")
+	"""
+	)
diff --git a/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py b/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py
deleted file mode 100644
index 72cda75..0000000
--- a/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyright (c) 2019, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-
-import frappe
-
-
-def execute():
-	"""Move account number into the new custom field debtor_creditor_number.
-
-	German companies used to use a dedicated payable/receivable account for
-	every party to mimick party accounts in the external accounting software
-	"DATEV". This is no longer necessary. The reference ID for DATEV will be
-	stored in a new custom field "debtor_creditor_number".
-	"""
-	company_list = frappe.get_all('Company', filters={'country': 'Germany'})
-
-	for company in company_list:
-		party_account_list = frappe.get_all('Party Account', filters={'company': company.name}, fields=['name', 'account', 'debtor_creditor_number'])
-		for party_account in party_account_list:
-			if (not party_account.account) or party_account.debtor_creditor_number:
-				# account empty or debtor_creditor_number already filled
-				continue
-
-			account_number = frappe.db.get_value('Account', party_account.account, 'account_number')
-			if not account_number:
-				continue
-
-			frappe.db.set_value('Party Account', party_account.name, 'debtor_creditor_number', account_number)
-			frappe.db.set_value('Party Account', party_account.name, 'account', '')
diff --git a/erpnext/patches/v13_0/germany_make_custom_fields.py b/erpnext/patches/v13_0/germany_make_custom_fields.py
deleted file mode 100644
index 80b6a39..0000000
--- a/erpnext/patches/v13_0/germany_make_custom_fields.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (c) 2019, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-
-import frappe
-
-from erpnext.regional.germany.setup import make_custom_fields
-
-
-def execute():
-	"""Execute the make_custom_fields method for german companies.
-
-	It is usually run once at setup of a new company. Since it's new, run it
-	once for existing companies as well.
-	"""
-	company_list = frappe.get_all('Company', filters = {'country': 'Germany'})
-	if not company_list:
-		return
-
-	make_custom_fields()
diff --git a/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py b/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py
deleted file mode 100644
index 98ce12b..0000000
--- a/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py
+++ /dev/null
@@ -1,90 +0,0 @@
-import frappe
-from frappe.model.utils.rename_field import rename_field
-
-
-def execute():
-	if frappe.db.exists('DocType', 'Lab Test') and frappe.db.exists('DocType', 'Lab Test Template'):
-		# rename child doctypes
-		doctypes = {
-			'Lab Test Groups': 'Lab Test Group Template',
-			'Normal Test Items': 'Normal Test Result',
-			'Sensitivity Test Items': 'Sensitivity Test Result',
-			'Special Test Items': 'Descriptive Test Result',
-			'Special Test Template': 'Descriptive Test Template'
-		}
-
-		frappe.reload_doc('healthcare', 'doctype', 'lab_test')
-		frappe.reload_doc('healthcare', 'doctype', 'lab_test_template')
-
-		for old_dt, new_dt in doctypes.items():
-			frappe.flags.link_fields = {}
-			should_rename = (
-				frappe.db.table_exists(old_dt)
-				and not frappe.db.table_exists(new_dt)
-			)
-			if should_rename:
-				frappe.reload_doc('healthcare', 'doctype', frappe.scrub(old_dt))
-				frappe.rename_doc('DocType', old_dt, new_dt, force=True)
-				frappe.reload_doc('healthcare', 'doctype', frappe.scrub(new_dt))
-				frappe.delete_doc_if_exists('DocType', old_dt)
-
-		parent_fields = {
-			'Lab Test Group Template': 'lab_test_groups',
-			'Descriptive Test Template': 'descriptive_test_templates',
-			'Normal Test Result': 'normal_test_items',
-			'Sensitivity Test Result': 'sensitivity_test_items',
-			'Descriptive Test Result': 'descriptive_test_items'
-		}
-
-		for doctype, parentfield in parent_fields.items():
-			frappe.db.sql("""
-				UPDATE `tab{0}`
-				SET parentfield = %(parentfield)s
-			""".format(doctype), {'parentfield': parentfield})
-
-		# copy renamed child table fields (fields were already renamed in old doctype json, hence sql)
-		rename_fields = {
-			'lab_test_name': 'test_name',
-			'lab_test_event': 'test_event',
-			'lab_test_uom': 'test_uom',
-			'lab_test_comment': 'test_comment'
-		}
-
-		for new, old in rename_fields.items():
-			if frappe.db.has_column('Normal Test Result', old):
-				frappe.db.sql("""UPDATE `tabNormal Test Result` SET {} = {}"""
-					.format(new, old))
-
-		if frappe.db.has_column('Normal Test Template', 'test_event'):
-			frappe.db.sql("""UPDATE `tabNormal Test Template` SET lab_test_event = test_event""")
-
-		if frappe.db.has_column('Normal Test Template', 'test_uom'):
-			frappe.db.sql("""UPDATE `tabNormal Test Template` SET lab_test_uom = test_uom""")
-
-		if frappe.db.has_column('Descriptive Test Result', 'test_particulars'):
-			frappe.db.sql("""UPDATE `tabDescriptive Test Result` SET lab_test_particulars = test_particulars""")
-
-		rename_fields = {
-			'lab_test_template': 'test_template',
-			'lab_test_description': 'test_description',
-			'lab_test_rate': 'test_rate'
-		}
-
-		for new, old in rename_fields.items():
-			if frappe.db.has_column('Lab Test Group Template', old):
-				frappe.db.sql("""UPDATE `tabLab Test Group Template` SET {} = {}"""
-					.format(new, old))
-
-		# rename field
-		frappe.reload_doc('healthcare', 'doctype', 'lab_test')
-		if frappe.db.has_column('Lab Test', 'special_toggle'):
-			rename_field('Lab Test', 'special_toggle', 'descriptive_toggle')
-
-	if frappe.db.exists('DocType', 'Lab Test Group Template'):
-		# fix select field option
-		frappe.reload_doc('healthcare', 'doctype', 'lab_test_group_template')
-		frappe.db.sql("""
-			UPDATE `tabLab Test Group Template`
-			SET template_or_new_line = 'Add New Line'
-			WHERE template_or_new_line = 'Add new line'
-		""")
diff --git a/erpnext/patches/v13_0/item_naming_series_not_mandatory.py b/erpnext/patches/v13_0/item_naming_series_not_mandatory.py
index 5fe85a4..0235a62 100644
--- a/erpnext/patches/v13_0/item_naming_series_not_mandatory.py
+++ b/erpnext/patches/v13_0/item_naming_series_not_mandatory.py
@@ -1,11 +1,16 @@
 import frappe
 
-from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
+from erpnext.utilities.naming import set_by_naming_series
 
 
 def execute():
 
 	stock_settings = frappe.get_doc("Stock Settings")
 
-	set_by_naming_series("Item", "item_code",
-		stock_settings.get("item_naming_by")=="Naming Series", hide_name_field=True, make_mandatory=0)
+	set_by_naming_series(
+		"Item",
+		"item_code",
+		stock_settings.get("item_naming_by") == "Naming Series",
+		hide_name_field=True,
+		make_mandatory=0,
+	)
diff --git a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
index 0f2ac4b..f6427ca 100644
--- a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
+++ b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
@@ -7,18 +7,18 @@
 
 def execute():
 	doctypes_to_reload = [
-			("stock", "repost_item_valuation"),
-			("stock", "stock_entry_detail"),
-			("stock", "purchase_receipt_item"),
-			("stock", "delivery_note_item"),
-			("stock", "packed_item"),
-			("accounts", "sales_invoice_item"),
-			("accounts", "purchase_invoice_item"),
-			("buying", "purchase_receipt_item_supplied")
-		]
+		("stock", "repost_item_valuation"),
+		("stock", "stock_entry_detail"),
+		("stock", "purchase_receipt_item"),
+		("stock", "delivery_note_item"),
+		("stock", "packed_item"),
+		("accounts", "sales_invoice_item"),
+		("accounts", "purchase_invoice_item"),
+		("buying", "purchase_receipt_item_supplied"),
+	]
 
 	for module, doctype in doctypes_to_reload:
-		frappe.reload_doc(module, 'doctype', doctype)
+		frappe.reload_doc(module, "doctype", doctype)
 
 	reposting_project_deployed_on = get_creation_time()
 	posting_date = getdate(reposting_project_deployed_on)
@@ -32,7 +32,8 @@
 
 	company_list = []
 
-	data = frappe.db.sql('''
+	data = frappe.db.sql(
+		"""
 		SELECT
 			name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time, company
 		FROM
@@ -41,7 +42,10 @@
 			creation > %s
 			and is_cancelled = 0
 		ORDER BY timestamp(posting_date, posting_time) asc, creation asc
-	''', reposting_project_deployed_on, as_dict=1)
+	""",
+		reposting_project_deployed_on,
+		as_dict=1,
+	)
 
 	frappe.db.auto_commit_on_many_writes = 1
 	print("Reposting Stock Ledger Entries...")
@@ -51,30 +55,36 @@
 		if d.company not in company_list:
 			company_list.append(d.company)
 
-		update_entries_after({
-			"item_code": d.item_code,
-			"warehouse": d.warehouse,
-			"posting_date": d.posting_date,
-			"posting_time": d.posting_time,
-			"voucher_type": d.voucher_type,
-			"voucher_no": d.voucher_no,
-			"sle_id": d.name
-		}, allow_negative_stock=True)
+		update_entries_after(
+			{
+				"item_code": d.item_code,
+				"warehouse": d.warehouse,
+				"posting_date": d.posting_date,
+				"posting_time": d.posting_time,
+				"voucher_type": d.voucher_type,
+				"voucher_no": d.voucher_no,
+				"sle_id": d.name,
+			},
+			allow_negative_stock=True,
+		)
 
 		i += 1
-		if i%100 == 0:
+		if i % 100 == 0:
 			print(i, "/", total_sle)
 
-
 	print("Reposting General Ledger Entries...")
 
 	if data:
-		for row in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
+		for row in frappe.get_all("Company", filters={"enable_perpetual_inventory": 1}):
 			if row.name in company_list:
 				update_gl_entries_after(posting_date, posting_time, company=row.name)
 
 	frappe.db.auto_commit_on_many_writes = 0
 
+
 def get_creation_time():
-	return frappe.db.sql(''' SELECT create_time FROM
-		INFORMATION_SCHEMA.TABLES where TABLE_NAME = "tabRepost Item Valuation" ''', as_list=1)[0][0]
+	return frappe.db.sql(
+		""" SELECT create_time FROM
+		INFORMATION_SCHEMA.TABLES where TABLE_NAME = "tabRepost Item Valuation" """,
+		as_list=1,
+	)[0][0]
diff --git a/erpnext/patches/v13_0/job_card_status_on_hold.py b/erpnext/patches/v13_0/job_card_status_on_hold.py
new file mode 100644
index 0000000..8c67c3c
--- /dev/null
+++ b/erpnext/patches/v13_0/job_card_status_on_hold.py
@@ -0,0 +1,19 @@
+import frappe
+
+
+def execute():
+	job_cards = frappe.get_all(
+		"Job Card",
+		{"status": "On Hold", "docstatus": ("!=", 0)},
+		pluck="name",
+	)
+
+	for idx, job_card in enumerate(job_cards):
+		try:
+			doc = frappe.get_doc("Job Card", job_card)
+			doc.set_status()
+			doc.db_set("status", doc.status, update_modified=False)
+			if idx % 100 == 0:
+				frappe.db.commit()
+		except Exception:
+			continue
diff --git a/erpnext/patches/v13_0/loyalty_points_entry_for_pos_invoice.py b/erpnext/patches/v13_0/loyalty_points_entry_for_pos_invoice.py
index 68bcd8a..69a695e 100644
--- a/erpnext/patches/v13_0/loyalty_points_entry_for_pos_invoice.py
+++ b/erpnext/patches/v13_0/loyalty_points_entry_for_pos_invoice.py
@@ -6,15 +6,16 @@
 
 
 def execute():
-	'''`sales_invoice` field from loyalty point entry is splitted into `invoice_type` & `invoice` fields'''
+	"""`sales_invoice` field from loyalty point entry is splitted into `invoice_type` & `invoice` fields"""
 
 	frappe.reload_doc("Accounts", "doctype", "loyalty_point_entry")
 
-	if not frappe.db.has_column('Loyalty Point Entry', 'sales_invoice'):
+	if not frappe.db.has_column("Loyalty Point Entry", "sales_invoice"):
 		return
 
 	frappe.db.sql(
 		"""UPDATE `tabLoyalty Point Entry` lpe
 		SET lpe.`invoice_type` = 'Sales Invoice', lpe.`invoice` = lpe.`sales_invoice`
 		WHERE lpe.`sales_invoice` IS NOT NULL
-		AND (lpe.`invoice` IS NULL OR lpe.`invoice` = '')""")
+		AND (lpe.`invoice` IS NULL OR lpe.`invoice` = '')"""
+	)
diff --git a/erpnext/patches/v13_0/make_homepage_products_website_items.py b/erpnext/patches/v13_0/make_homepage_products_website_items.py
index 7a7ddba..50bfd35 100644
--- a/erpnext/patches/v13_0/make_homepage_products_website_items.py
+++ b/erpnext/patches/v13_0/make_homepage_products_website_items.py
@@ -12,4 +12,4 @@
 		row.item_code = web_item
 
 	homepage.flags.ignore_mandatory = True
-	homepage.save()
\ No newline at end of file
+	homepage.save()
diff --git a/erpnext/patches/v13_0/make_non_standard_user_type.py b/erpnext/patches/v13_0/make_non_standard_user_type.py
index ff241a3..e2c0685 100644
--- a/erpnext/patches/v13_0/make_non_standard_user_type.py
+++ b/erpnext/patches/v13_0/make_non_standard_user_type.py
@@ -9,22 +9,34 @@
 
 def execute():
 	doctype_dict = {
-		'projects': ['Timesheet'],
-		'payroll': [
-			'Salary Slip', 'Employee Tax Exemption Declaration', 'Employee Tax Exemption Proof Submission',
-			'Employee Benefit Application', 'Employee Benefit Claim'
+		"projects": ["Timesheet"],
+		"payroll": [
+			"Salary Slip",
+			"Employee Tax Exemption Declaration",
+			"Employee Tax Exemption Proof Submission",
+			"Employee Benefit Application",
+			"Employee Benefit Claim",
 		],
-		'hr': [
-			'Employee', 'Expense Claim', 'Leave Application', 'Attendance Request', 'Compensatory Leave Request',
-			'Holiday List', 'Employee Advance', 'Training Program', 'Training Feedback',
-			'Shift Request', 'Employee Grievance', 'Employee Referral', 'Travel Request'
-		]
+		"hr": [
+			"Employee",
+			"Expense Claim",
+			"Leave Application",
+			"Attendance Request",
+			"Compensatory Leave Request",
+			"Holiday List",
+			"Employee Advance",
+			"Training Program",
+			"Training Feedback",
+			"Shift Request",
+			"Employee Grievance",
+			"Employee Referral",
+			"Travel Request",
+		],
 	}
 
 	for module, doctypes in doctype_dict.items():
 		for doctype in doctypes:
-			frappe.reload_doc(module, 'doctype', doctype)
-
+			frappe.reload_doc(module, "doctype", doctype)
 
 	frappe.flags.ignore_select_perm = True
 	frappe.flags.update_select_perm_after_migrate = True
diff --git a/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py b/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py
index 1c65998..6c64ef6 100644
--- a/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py
+++ b/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py
@@ -4,10 +4,11 @@
 
 
 def execute():
-	frappe.reload_doc('accounts', 'doctype', 'purchase_invoice_advance')
-	frappe.reload_doc('accounts', 'doctype', 'sales_invoice_advance')
+	frappe.reload_doc("accounts", "doctype", "purchase_invoice_advance")
+	frappe.reload_doc("accounts", "doctype", "sales_invoice_advance")
 
-	purchase_invoices = frappe.db.sql("""
+	purchase_invoices = frappe.db.sql(
+		"""
 		select
 			parenttype as type, parent as name
 		from
@@ -18,9 +19,12 @@
 			and ifnull(exchange_gain_loss, 0) != 0
 		group by
 			parent
-	""", as_dict=1)
+	""",
+		as_dict=1,
+	)
 
-	sales_invoices = frappe.db.sql("""
+	sales_invoices = frappe.db.sql(
+		"""
 		select
 			parenttype as type, parent as name
 		from
@@ -31,14 +35,19 @@
 			and ifnull(exchange_gain_loss, 0) != 0
 		group by
 			parent
-	""", as_dict=1)
+	""",
+		as_dict=1,
+	)
 
 	if purchase_invoices + sales_invoices:
-		frappe.log_error(json.dumps(purchase_invoices + sales_invoices, indent=2), title="Patch Log")
+		frappe.log_error(
+			"Fix invalid gain / loss patch log",
+			message=json.dumps(purchase_invoices + sales_invoices, indent=2),
+		)
 
-	acc_frozen_upto = frappe.db.get_value('Accounts Settings', None, 'acc_frozen_upto')
+	acc_frozen_upto = frappe.db.get_value("Accounts Settings", None, "acc_frozen_upto")
 	if acc_frozen_upto:
-		frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
+		frappe.db.set_value("Accounts Settings", None, "acc_frozen_upto", None)
 
 	for invoice in purchase_invoices + sales_invoices:
 		try:
@@ -47,13 +56,13 @@
 			doc.make_gl_entries()
 			for advance in doc.advances:
 				if advance.ref_exchange_rate == 1:
-					advance.db_set('exchange_gain_loss', 0, False)
+					advance.db_set("exchange_gain_loss", 0, False)
 			doc.docstatus = 1
 			doc.make_gl_entries()
 			frappe.db.commit()
 		except Exception:
 			frappe.db.rollback()
-			print(f'Failed to correct gl entries of {invoice.name}')
+			print(f"Failed to correct gl entries of {invoice.name}")
 
 	if acc_frozen_upto:
-		frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', acc_frozen_upto)
\ No newline at end of file
+		frappe.db.set_value("Accounts Settings", None, "acc_frozen_upto", acc_frozen_upto)
diff --git a/erpnext/patches/v13_0/move_branch_code_to_bank_account.py b/erpnext/patches/v13_0/move_branch_code_to_bank_account.py
index 350744f..2406127 100644
--- a/erpnext/patches/v13_0/move_branch_code_to_bank_account.py
+++ b/erpnext/patches/v13_0/move_branch_code_to_bank_account.py
@@ -7,11 +7,15 @@
 
 def execute():
 
-	frappe.reload_doc('accounts', 'doctype', 'bank_account')
-	frappe.reload_doc('accounts', 'doctype', 'bank')
+	frappe.reload_doc("accounts", "doctype", "bank_account")
+	frappe.reload_doc("accounts", "doctype", "bank")
 
-	if frappe.db.has_column('Bank', 'branch_code') and frappe.db.has_column('Bank Account', 'branch_code'):
-		frappe.db.sql("""UPDATE `tabBank` b, `tabBank Account` ba
+	if frappe.db.has_column("Bank", "branch_code") and frappe.db.has_column(
+		"Bank Account", "branch_code"
+	):
+		frappe.db.sql(
+			"""UPDATE `tabBank` b, `tabBank Account` ba
 			SET ba.branch_code = b.branch_code
 			WHERE ba.bank = b.name AND
-			ifnull(b.branch_code, '') != '' AND ifnull(ba.branch_code, '') = ''""")
+			ifnull(b.branch_code, '') != '' AND ifnull(ba.branch_code, '') = ''"""
+		)
diff --git a/erpnext/patches/v13_0/move_doctype_reports_and_notification_from_hr_to_payroll.py b/erpnext/patches/v13_0/move_doctype_reports_and_notification_from_hr_to_payroll.py
index c07caae..0290af0 100644
--- a/erpnext/patches/v13_0/move_doctype_reports_and_notification_from_hr_to_payroll.py
+++ b/erpnext/patches/v13_0/move_doctype_reports_and_notification_from_hr_to_payroll.py
@@ -6,47 +6,47 @@
 
 
 def execute():
-    frappe.db.sql("""UPDATE `tabPrint Format`
+	frappe.db.sql(
+		"""UPDATE `tabPrint Format`
         SET module = 'Payroll'
         WHERE name IN ('Salary Slip Based On Timesheet', 'Salary Slip Standard')"""
-    )
+	)
 
-    frappe.db.sql("""UPDATE `tabNotification` SET module='Payroll' WHERE name='Retention Bonus';"""
-    )
+	frappe.db.sql("""UPDATE `tabNotification` SET module='Payroll' WHERE name='Retention Bonus';""")
 
-    doctypes_moved = [
-        'Employee Benefit Application Detail',
-        'Employee Tax Exemption Declaration Category',
-        'Salary Component',
-        'Employee Tax Exemption Proof Submission Detail',
-        'Income Tax Slab Other Charges',
-        'Taxable Salary Slab',
-        'Payroll Period Date',
-        'Salary Slip Timesheet',
-        'Payroll Employee Detail',
-        'Salary Detail',
-        'Employee Tax Exemption Sub Category',
-        'Employee Tax Exemption Category',
-        'Employee Benefit Claim',
-        'Employee Benefit Application',
-        'Employee Other Income',
-        'Employee Tax Exemption Proof Submission',
-        'Employee Tax Exemption Declaration',
-        'Employee Incentive',
-        'Retention Bonus',
-        'Additional Salary',
-        'Income Tax Slab',
-        'Payroll Period',
-        'Salary Slip',
-        'Payroll Entry',
-        'Salary Structure Assignment',
-        'Salary Structure'
-    ]
+	doctypes_moved = [
+		"Employee Benefit Application Detail",
+		"Employee Tax Exemption Declaration Category",
+		"Salary Component",
+		"Employee Tax Exemption Proof Submission Detail",
+		"Income Tax Slab Other Charges",
+		"Taxable Salary Slab",
+		"Payroll Period Date",
+		"Salary Slip Timesheet",
+		"Payroll Employee Detail",
+		"Salary Detail",
+		"Employee Tax Exemption Sub Category",
+		"Employee Tax Exemption Category",
+		"Employee Benefit Claim",
+		"Employee Benefit Application",
+		"Employee Other Income",
+		"Employee Tax Exemption Proof Submission",
+		"Employee Tax Exemption Declaration",
+		"Employee Incentive",
+		"Retention Bonus",
+		"Additional Salary",
+		"Income Tax Slab",
+		"Payroll Period",
+		"Salary Slip",
+		"Payroll Entry",
+		"Salary Structure Assignment",
+		"Salary Structure",
+	]
 
-    for doctype in doctypes_moved:
-        frappe.delete_doc_if_exists("DocType", doctype)
+	for doctype in doctypes_moved:
+		frappe.delete_doc_if_exists("DocType", doctype)
 
-    reports = ["Salary Register", "Bank Remittance"]
+	reports = ["Salary Register", "Bank Remittance"]
 
-    for report in reports:
-        frappe.delete_doc_if_exists("Report", report)
+	for report in reports:
+		frappe.delete_doc_if_exists("Report", report)
diff --git a/erpnext/patches/v13_0/move_payroll_setting_separately_from_hr_settings.py b/erpnext/patches/v13_0/move_payroll_setting_separately_from_hr_settings.py
index fca7c09..37a3c35 100644
--- a/erpnext/patches/v13_0/move_payroll_setting_separately_from_hr_settings.py
+++ b/erpnext/patches/v13_0/move_payroll_setting_separately_from_hr_settings.py
@@ -6,7 +6,8 @@
 
 
 def execute():
-    data = frappe.db.sql('''SELECT *
+	data = frappe.db.sql(
+		"""SELECT *
         FROM `tabSingles`
         WHERE
             doctype = "HR Settings"
@@ -21,7 +22,9 @@
                 "payroll_based_on",
                 "password_policy"
             )
-            ''', as_dict=1)
+            """,
+		as_dict=1,
+	)
 
-    for d in data:
-        frappe.db.set_value("Payroll Settings", None, d.field, d.value)
+	for d in data:
+		frappe.db.set_value("Payroll Settings", None, d.field, d.value)
diff --git a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py
index d1ea22f..f84a739 100644
--- a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py
+++ b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py
@@ -6,28 +6,42 @@
 
 
 def execute():
-	if not (frappe.db.table_exists("Payroll Period") and frappe.db.table_exists("Taxable Salary Slab")):
+	if not (
+		frappe.db.table_exists("Payroll Period") and frappe.db.table_exists("Taxable Salary Slab")
+	):
 		return
 
-	for doctype in ("income_tax_slab", "salary_structure_assignment", "employee_other_income", "income_tax_slab_other_charges"):
+	for doctype in (
+		"income_tax_slab",
+		"salary_structure_assignment",
+		"employee_other_income",
+		"income_tax_slab_other_charges",
+	):
 		frappe.reload_doc("Payroll", "doctype", doctype)
 
-
-	standard_tax_exemption_amount_exists = frappe.db.has_column("Payroll Period", "standard_tax_exemption_amount")
+	standard_tax_exemption_amount_exists = frappe.db.has_column(
+		"Payroll Period", "standard_tax_exemption_amount"
+	)
 
 	select_fields = "name, start_date, end_date"
 	if standard_tax_exemption_amount_exists:
 		select_fields = "name, start_date, end_date, standard_tax_exemption_amount"
 
 	for company in frappe.get_all("Company"):
-		payroll_periods =  frappe.db.sql("""
+		payroll_periods = frappe.db.sql(
+			"""
 			SELECT
 				{0}
 			FROM
 				`tabPayroll Period`
 			WHERE company=%s
 			ORDER BY start_date DESC
-		""".format(select_fields), company.name, as_dict = 1)
+		""".format(
+				select_fields
+			),
+			company.name,
+			as_dict=1,
+		)
 
 		for i, period in enumerate(payroll_periods):
 			income_tax_slab = frappe.new_doc("Income Tax Slab")
@@ -48,13 +62,17 @@
 			income_tax_slab.submit()
 
 			frappe.db.sql(
-			""" UPDATE `tabTaxable Salary Slab`
+				""" UPDATE `tabTaxable Salary Slab`
 				SET parent = %s , parentfield = 'slabs' , parenttype = "Income Tax Slab"
 				WHERE parent = %s
-			""", (income_tax_slab.name, period.name), as_dict = 1)
+			""",
+				(income_tax_slab.name, period.name),
+				as_dict=1,
+			)
 
 			if i == 0:
-				frappe.db.sql("""
+				frappe.db.sql(
+					"""
 					UPDATE
 						`tabSalary Structure Assignment`
 					set
@@ -63,16 +81,19 @@
 						company = %s
 						and from_date >= %s
 						and docstatus < 2
-				""", (income_tax_slab.name, company.name, period.start_date))
+				""",
+					(income_tax_slab.name, company.name, period.start_date),
+				)
 
 	# move other incomes to separate document
 	if not frappe.db.table_exists("Employee Tax Exemption Proof Submission"):
 		return
 
 	migrated = []
-	proofs = frappe.get_all("Employee Tax Exemption Proof Submission",
-		filters = {'docstatus': 1},
-		fields =['payroll_period', 'employee', 'company', 'income_from_other_sources']
+	proofs = frappe.get_all(
+		"Employee Tax Exemption Proof Submission",
+		filters={"docstatus": 1},
+		fields=["payroll_period", "employee", "company", "income_from_other_sources"],
 	)
 	for proof in proofs:
 		if proof.income_from_other_sources:
@@ -91,14 +112,17 @@
 	if not frappe.db.table_exists("Employee Tax Exemption Declaration"):
 		return
 
-	declerations = frappe.get_all("Employee Tax Exemption Declaration",
-		filters = {'docstatus': 1},
-		fields =['payroll_period', 'employee', 'company', 'income_from_other_sources']
+	declerations = frappe.get_all(
+		"Employee Tax Exemption Declaration",
+		filters={"docstatus": 1},
+		fields=["payroll_period", "employee", "company", "income_from_other_sources"],
 	)
 
 	for declaration in declerations:
-		if declaration.income_from_other_sources \
-				and [declaration.employee, declaration.payroll_period] not in migrated:
+		if (
+			declaration.income_from_other_sources
+			and [declaration.employee, declaration.payroll_period] not in migrated
+		):
 			employee_other_income = frappe.new_doc("Employee Other Income")
 			employee_other_income.employee = declaration.employee
 			employee_other_income.payroll_period = declaration.payroll_period
diff --git a/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py b/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py
index 7c10a31..45acf49 100644
--- a/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py
+++ b/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py
@@ -10,43 +10,58 @@
 
 	frappe.reload_doc("hr", "doctype", "Leave Encashment")
 
-	additional_salaries = frappe.get_all("Additional Salary",
-		fields = ['name', "salary_slip", "type", "salary_component"],
-		filters = {'salary_slip': ['!=', '']},
-		group_by = 'salary_slip'
-	)
-	leave_encashments = frappe.get_all("Leave Encashment",
-		fields = ["name","additional_salary"],
-		filters = {'additional_salary': ['!=', '']}
-	)
-	employee_incentives = frappe.get_all("Employee Incentive",
-		fields= ["name", "additional_salary"],
-		filters = {'additional_salary': ['!=', '']}
-	)
+	if frappe.db.has_column("Leave Encashment", "additional_salary"):
+		leave_encashments = frappe.get_all(
+			"Leave Encashment",
+			fields=["name", "additional_salary"],
+			filters={"additional_salary": ["!=", ""]},
+		)
+		for leave_encashment in leave_encashments:
+			frappe.db.sql(
+				""" UPDATE `tabAdditional Salary`
+				SET ref_doctype = 'Leave Encashment', ref_docname = %s
+				WHERE name = %s
+			""",
+				(leave_encashment["name"], leave_encashment["additional_salary"]),
+			)
 
-	for incentive in employee_incentives:
-		frappe.db.sql(""" UPDATE `tabAdditional Salary`
-			SET ref_doctype = 'Employee Incentive', ref_docname = %s
-			WHERE name = %s
-		""", (incentive['name'], incentive['additional_salary']))
+	if frappe.db.has_column("Employee Incentive", "additional_salary"):
+		employee_incentives = frappe.get_all(
+			"Employee Incentive",
+			fields=["name", "additional_salary"],
+			filters={"additional_salary": ["!=", ""]},
+		)
 
+		for incentive in employee_incentives:
+			frappe.db.sql(
+				""" UPDATE `tabAdditional Salary`
+				SET ref_doctype = 'Employee Incentive', ref_docname = %s
+				WHERE name = %s
+			""",
+				(incentive["name"], incentive["additional_salary"]),
+			)
 
-	for leave_encashment in leave_encashments:
-		frappe.db.sql(""" UPDATE `tabAdditional Salary`
-			SET ref_doctype = 'Leave Encashment', ref_docname = %s
-			WHERE name = %s
-		""", (leave_encashment['name'], leave_encashment['additional_salary']))
+	if frappe.db.has_column("Additional Salary", "salary_slip"):
+		additional_salaries = frappe.get_all(
+			"Additional Salary",
+			fields=["name", "salary_slip", "type", "salary_component"],
+			filters={"salary_slip": ["!=", ""]},
+			group_by="salary_slip",
+		)
 
-	salary_slips = [sal["salary_slip"] for sal in additional_salaries]
+		salary_slips = [sal["salary_slip"] for sal in additional_salaries]
 
-	for salary in additional_salaries:
-		comp_type = "earnings" if salary['type'] == 'Earning' else 'deductions'
-		if salary["salary_slip"] and salary_slips.count(salary["salary_slip"]) == 1:
-			frappe.db.sql("""
-				UPDATE `tabSalary Detail`
-				SET additional_salary = %s
-				WHERE parenttype = 'Salary Slip'
-					and parentfield = %s
-					and parent = %s
-					and salary_component = %s
-			""", (salary["name"], comp_type, salary["salary_slip"], salary["salary_component"]))
+		for salary in additional_salaries:
+			comp_type = "earnings" if salary["type"] == "Earning" else "deductions"
+			if salary["salary_slip"] and salary_slips.count(salary["salary_slip"]) == 1:
+				frappe.db.sql(
+					"""
+					UPDATE `tabSalary Detail`
+					SET additional_salary = %s
+					WHERE parenttype = 'Salary Slip'
+						and parentfield = %s
+						and parent = %s
+						and salary_component = %s
+				""",
+					(salary["name"], comp_type, salary["salary_slip"], salary["salary_component"]),
+				)
diff --git a/erpnext/patches/v13_0/populate_e_commerce_settings.py b/erpnext/patches/v13_0/populate_e_commerce_settings.py
index 8f9ee51..ecf512b 100644
--- a/erpnext/patches/v13_0/populate_e_commerce_settings.py
+++ b/erpnext/patches/v13_0/populate_e_commerce_settings.py
@@ -8,17 +8,30 @@
 	frappe.reload_doc("portal", "doctype", "website_attribute")
 
 	products_settings_fields = [
-		"hide_variants", "products_per_page",
-		"enable_attribute_filters", "enable_field_filters"
+		"hide_variants",
+		"products_per_page",
+		"enable_attribute_filters",
+		"enable_field_filters",
 	]
 
 	shopping_cart_settings_fields = [
-		"enabled", "show_attachments", "show_price",
-		"show_stock_availability", "enable_variants", "show_contact_us_button",
-		"show_quantity_in_website", "show_apply_coupon_code_in_website",
-		"allow_items_not_in_stock", "company", "price_list", "default_customer_group",
-		"quotation_series", "enable_checkout", "payment_success_url",
-		"payment_gateway_account", "save_quotations_as_draft"
+		"enabled",
+		"show_attachments",
+		"show_price",
+		"show_stock_availability",
+		"enable_variants",
+		"show_contact_us_button",
+		"show_quantity_in_website",
+		"show_apply_coupon_code_in_website",
+		"allow_items_not_in_stock",
+		"company",
+		"price_list",
+		"default_customer_group",
+		"quotation_series",
+		"enable_checkout",
+		"payment_success_url",
+		"payment_gateway_account",
+		"save_quotations_as_draft",
 	]
 
 	settings = frappe.get_doc("E Commerce Settings")
@@ -27,12 +40,8 @@
 		singles = frappe.qb.DocType("Singles")
 		query = (
 			frappe.qb.from_(singles)
-			.select(
-				singles["field"], singles.value
-			).where(
-				(singles.doctype == doctype)
-				& (singles["field"].isin(fields))
-			)
+			.select(singles["field"], singles.value)
+			.where((singles.doctype == doctype) & (singles["field"].isin(fields)))
 		)
 		data = query.run(as_dict=True)
 
@@ -54,9 +63,6 @@
 		frappe.db.set_value(
 			doctype,
 			{"parent": "Products Settings"},
-			{
-				"parenttype": "E Commerce Settings",
-				"parent": "E Commerce Settings"
-			},
-			update_modified=False
+			{"parenttype": "E Commerce Settings", "parent": "E Commerce Settings"},
+			update_modified=False,
 		)
diff --git a/erpnext/patches/v13_0/print_uom_after_quantity_patch.py b/erpnext/patches/v13_0/print_uom_after_quantity_patch.py
deleted file mode 100644
index 3da6f74..0000000
--- a/erpnext/patches/v13_0/print_uom_after_quantity_patch.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2019, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-
-from erpnext.setup.install import create_print_uom_after_qty_custom_field
-
-
-def execute():
-    create_print_uom_after_qty_custom_field()
diff --git a/erpnext/patches/v13_0/remove_attribute_field_from_item_variant_setting.py b/erpnext/patches/v13_0/remove_attribute_field_from_item_variant_setting.py
index bbe3eb5..4efbe4d 100644
--- a/erpnext/patches/v13_0/remove_attribute_field_from_item_variant_setting.py
+++ b/erpnext/patches/v13_0/remove_attribute_field_from_item_variant_setting.py
@@ -5,5 +5,7 @@
 	"""Remove has_variants and attribute fields from item variant settings."""
 	frappe.reload_doc("stock", "doctype", "Item Variant Settings")
 
-	frappe.db.sql("""delete from `tabVariant Field`
-			where field_name in ('attributes', 'has_variants')""")
+	frappe.db.sql(
+		"""delete from `tabVariant Field`
+			where field_name in ('attributes', 'has_variants')"""
+	)
diff --git a/erpnext/patches/v13_0/remove_bad_selling_defaults.py b/erpnext/patches/v13_0/remove_bad_selling_defaults.py
index 0262539..efd2098 100644
--- a/erpnext/patches/v13_0/remove_bad_selling_defaults.py
+++ b/erpnext/patches/v13_0/remove_bad_selling_defaults.py
@@ -3,7 +3,7 @@
 
 
 def execute():
-	frappe.reload_doctype('Selling Settings')
+	frappe.reload_doctype("Selling Settings")
 	selling_settings = frappe.get_single("Selling Settings")
 
 	if selling_settings.customer_group in (_("All Customer Groups"), "All Customer Groups"):
@@ -12,5 +12,5 @@
 	if selling_settings.territory in (_("All Territories"), "All Territories"):
 		selling_settings.territory = None
 
-	selling_settings.flags.ignore_mandatory=True
+	selling_settings.flags.ignore_mandatory = True
 	selling_settings.save(ignore_permissions=True)
diff --git a/erpnext/patches/v13_0/remove_unknown_links_to_prod_plan_items.py b/erpnext/patches/v13_0/remove_unknown_links_to_prod_plan_items.py
new file mode 100644
index 0000000..3f4a51b
--- /dev/null
+++ b/erpnext/patches/v13_0/remove_unknown_links_to_prod_plan_items.py
@@ -0,0 +1,32 @@
+import frappe
+
+
+def execute():
+	"""
+	Remove "production_plan_item" field where linked field doesn't exist in tha table.
+	"""
+
+	work_order = frappe.qb.DocType("Work Order")
+	pp_item = frappe.qb.DocType("Production Plan Item")
+
+	broken_work_orders = (
+		frappe.qb.from_(work_order)
+		.left_join(pp_item)
+		.on(work_order.production_plan_item == pp_item.name)
+		.select(work_order.name)
+		.where(
+			(work_order.docstatus == 0)
+			& (work_order.production_plan_item.notnull())
+			& (work_order.production_plan_item.like("new-production-plan%"))
+			& (pp_item.name.isnull())
+		)
+	).run(pluck=True)
+
+	if not broken_work_orders:
+		return
+
+	(
+		frappe.qb.update(work_order)
+		.set(work_order.production_plan_item, None)
+		.where(work_order.name.isin(broken_work_orders))
+	).run()
diff --git a/erpnext/patches/v13_0/rename_discharge_date_in_ip_record.py b/erpnext/patches/v13_0/rename_discharge_date_in_ip_record.py
deleted file mode 100644
index 3bd717d..0000000
--- a/erpnext/patches/v13_0/rename_discharge_date_in_ip_record.py
+++ /dev/null
@@ -1,8 +0,0 @@
-import frappe
-from frappe.model.utils.rename_field import rename_field
-
-
-def execute():
-	frappe.reload_doc("Healthcare", "doctype", "Inpatient Record")
-	if frappe.db.has_column("Inpatient Record", "discharge_date"):
-		rename_field("Inpatient Record", "discharge_date", "discharge_datetime")
diff --git a/erpnext/patches/v13_0/rename_issue_doctype_fields.py b/erpnext/patches/v13_0/rename_issue_doctype_fields.py
index bf5438c..a9b6df7 100644
--- a/erpnext/patches/v13_0/rename_issue_doctype_fields.py
+++ b/erpnext/patches/v13_0/rename_issue_doctype_fields.py
@@ -7,63 +7,78 @@
 
 
 def execute():
-	if frappe.db.exists('DocType', 'Issue'):
-		issues = frappe.db.get_all('Issue', fields=['name', 'response_by_variance', 'resolution_by_variance', 'mins_to_first_response'],
-			 order_by='creation desc')
-		frappe.reload_doc('support', 'doctype', 'issue')
+	if frappe.db.exists("DocType", "Issue"):
+		issues = frappe.db.get_all(
+			"Issue",
+			fields=["name", "response_by_variance", "resolution_by_variance", "mins_to_first_response"],
+			order_by="creation desc",
+		)
+		frappe.reload_doc("support", "doctype", "issue")
 
 		# rename fields
 		rename_map = {
-			'agreement_fulfilled': 'agreement_status',
-			'mins_to_first_response': 'first_response_time'
+			"agreement_fulfilled": "agreement_status",
+			"mins_to_first_response": "first_response_time",
 		}
 		for old, new in rename_map.items():
-			rename_field('Issue', old, new)
+			rename_field("Issue", old, new)
 
 		# change fieldtype to duration
 		count = 0
 		for entry in issues:
-			response_by_variance = convert_to_seconds(entry.response_by_variance, 'Hours')
-			resolution_by_variance = convert_to_seconds(entry.resolution_by_variance, 'Hours')
-			mins_to_first_response = convert_to_seconds(entry.mins_to_first_response, 'Minutes')
-			frappe.db.set_value('Issue', entry.name, {
-				'response_by_variance': response_by_variance,
-				'resolution_by_variance': resolution_by_variance,
-				'first_response_time': mins_to_first_response
-			}, update_modified=False)
+			response_by_variance = convert_to_seconds(entry.response_by_variance, "Hours")
+			resolution_by_variance = convert_to_seconds(entry.resolution_by_variance, "Hours")
+			mins_to_first_response = convert_to_seconds(entry.mins_to_first_response, "Minutes")
+			frappe.db.set_value(
+				"Issue",
+				entry.name,
+				{
+					"response_by_variance": response_by_variance,
+					"resolution_by_variance": resolution_by_variance,
+					"first_response_time": mins_to_first_response,
+				},
+				update_modified=False,
+			)
 			# commit after every 100 updates
 			count += 1
-			if count%100 == 0:
+			if count % 100 == 0:
 				frappe.db.commit()
 
-	if frappe.db.exists('DocType', 'Opportunity'):
-		opportunities = frappe.db.get_all('Opportunity', fields=['name', 'mins_to_first_response'], order_by='creation desc')
-		frappe.reload_doctype('Opportunity', force=True)
-		rename_field('Opportunity', 'mins_to_first_response', 'first_response_time')
+	if frappe.db.exists("DocType", "Opportunity"):
+		opportunities = frappe.db.get_all(
+			"Opportunity", fields=["name", "mins_to_first_response"], order_by="creation desc"
+		)
+		frappe.reload_doctype("Opportunity", force=True)
+		rename_field("Opportunity", "mins_to_first_response", "first_response_time")
 
 		# change fieldtype to duration
-		frappe.reload_doc('crm', 'doctype', 'opportunity', force=True)
+		frappe.reload_doc("crm", "doctype", "opportunity", force=True)
 		count = 0
 		for entry in opportunities:
-			mins_to_first_response = convert_to_seconds(entry.mins_to_first_response, 'Minutes')
-			frappe.db.set_value('Opportunity', entry.name, 'first_response_time', mins_to_first_response, update_modified=False)
+			mins_to_first_response = convert_to_seconds(entry.mins_to_first_response, "Minutes")
+			frappe.db.set_value(
+				"Opportunity", entry.name, "first_response_time", mins_to_first_response, update_modified=False
+			)
 			# commit after every 100 updates
 			count += 1
-			if count%100 == 0:
+			if count % 100 == 0:
 				frappe.db.commit()
 
 	# renamed reports from "Minutes to First Response for Issues" to "First Response Time for Issues". Same for Opportunity
-	for report in ['Minutes to First Response for Issues', 'Minutes to First Response for Opportunity']:
-		if frappe.db.exists('Report', report):
-			frappe.delete_doc('Report', report, ignore_permissions=True)
+	for report in [
+		"Minutes to First Response for Issues",
+		"Minutes to First Response for Opportunity",
+	]:
+		if frappe.db.exists("Report", report):
+			frappe.delete_doc("Report", report, ignore_permissions=True)
 
 
 def convert_to_seconds(value, unit):
 	seconds = 0
-	if value == 0:
+	if not value:
 		return seconds
-	if unit == 'Hours':
+	if unit == "Hours":
 		seconds = value * 3600
-	if unit == 'Minutes':
+	if unit == "Minutes":
 		seconds = value * 60
 	return seconds
diff --git a/erpnext/patches/v13_0/rename_issue_status_hold_to_on_hold.py b/erpnext/patches/v13_0/rename_issue_status_hold_to_on_hold.py
index b129cbe..d3896dd 100644
--- a/erpnext/patches/v13_0/rename_issue_status_hold_to_on_hold.py
+++ b/erpnext/patches/v13_0/rename_issue_status_hold_to_on_hold.py
@@ -6,16 +6,19 @@
 
 
 def execute():
-	if frappe.db.exists('DocType', 'Issue'):
+	if frappe.db.exists("DocType", "Issue"):
 		frappe.reload_doc("support", "doctype", "issue")
 		rename_status()
 
+
 def rename_status():
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		UPDATE
 			`tabIssue`
 		SET
 			status = 'On Hold'
 		WHERE
 			status = 'Hold'
-	""")
+	"""
+	)
diff --git a/erpnext/patches/v13_0/rename_ksa_qr_field.py b/erpnext/patches/v13_0/rename_ksa_qr_field.py
index f4f9b17..e4b9141 100644
--- a/erpnext/patches/v13_0/rename_ksa_qr_field.py
+++ b/erpnext/patches/v13_0/rename_ksa_qr_field.py
@@ -7,26 +7,30 @@
 
 
 def execute():
-	company = frappe.get_all('Company', filters = {'country': 'Saudi Arabia'})
+	company = frappe.get_all("Company", filters={"country": "Saudi Arabia"})
 	if not company:
 		return
 
-	if frappe.db.exists('DocType', 'Sales Invoice'):
-		frappe.reload_doc('accounts', 'doctype', 'sales_invoice', force=True)
+	if frappe.db.exists("DocType", "Sales Invoice"):
+		frappe.reload_doc("accounts", "doctype", "sales_invoice", force=True)
 
 		# rename_field method assumes that the field already exists or the doc is synced
-		if not frappe.db.has_column('Sales Invoice', 'ksa_einv_qr'):
-			create_custom_fields({
-				'Sales Invoice': [
-					dict(
-						fieldname='ksa_einv_qr',
-						label='KSA E-Invoicing QR',
-						fieldtype='Attach Image',
-						read_only=1, no_copy=1, hidden=1
-					)
-				]
-			})
+		if not frappe.db.has_column("Sales Invoice", "ksa_einv_qr"):
+			create_custom_fields(
+				{
+					"Sales Invoice": [
+						dict(
+							fieldname="ksa_einv_qr",
+							label="KSA E-Invoicing QR",
+							fieldtype="Attach Image",
+							read_only=1,
+							no_copy=1,
+							hidden=1,
+						)
+					]
+				}
+			)
 
-		if frappe.db.has_column('Sales Invoice', 'qr_code'):
-			rename_field('Sales Invoice', 'qr_code', 'ksa_einv_qr')
+		if frappe.db.has_column("Sales Invoice", "qr_code"):
+			rename_field("Sales Invoice", "qr_code", "ksa_einv_qr")
 			frappe.delete_doc_if_exists("Custom Field", "Sales Invoice-qr_code")
diff --git a/erpnext/patches/v13_0/rename_membership_settings_to_non_profit_settings.py b/erpnext/patches/v13_0/rename_membership_settings_to_non_profit_settings.py
index 265e2a9..0e54234 100644
--- a/erpnext/patches/v13_0/rename_membership_settings_to_non_profit_settings.py
+++ b/erpnext/patches/v13_0/rename_membership_settings_to_non_profit_settings.py
@@ -15,7 +15,7 @@
 			"enable_razorpay": "enable_razorpay_for_memberships",
 			"debit_account": "membership_debit_account",
 			"payment_account": "membership_payment_account",
-			"webhook_secret": "membership_webhook_secret"
+			"webhook_secret": "membership_webhook_secret",
 		}
 
 		for old_name, new_name in rename_fields_map.items():
diff --git a/erpnext/patches/v13_0/rename_stop_to_send_birthday_reminders.py b/erpnext/patches/v13_0/rename_stop_to_send_birthday_reminders.py
index 813fbd2..434dbb4 100644
--- a/erpnext/patches/v13_0/rename_stop_to_send_birthday_reminders.py
+++ b/erpnext/patches/v13_0/rename_stop_to_send_birthday_reminders.py
@@ -3,20 +3,17 @@
 
 
 def execute():
-	frappe.reload_doc('hr', 'doctype', 'hr_settings')
+	frappe.reload_doc("hr", "doctype", "hr_settings")
 
 	try:
 		# Rename the field
-		rename_field('HR Settings', 'stop_birthday_reminders', 'send_birthday_reminders')
+		rename_field("HR Settings", "stop_birthday_reminders", "send_birthday_reminders")
 
 		# Reverse the value
-		old_value = frappe.db.get_single_value('HR Settings', 'send_birthday_reminders')
+		old_value = frappe.db.get_single_value("HR Settings", "send_birthday_reminders")
 
 		frappe.db.set_value(
-			'HR Settings',
-			'HR Settings',
-			'send_birthday_reminders',
-			1 if old_value == 0 else 0
+			"HR Settings", "HR Settings", "send_birthday_reminders", 1 if old_value == 0 else 0
 		)
 
 	except Exception as e:
diff --git a/erpnext/patches/v13_0/replace_pos_payment_mode_table.py b/erpnext/patches/v13_0/replace_pos_payment_mode_table.py
index a2c960c..ba2feb3 100644
--- a/erpnext/patches/v13_0/replace_pos_payment_mode_table.py
+++ b/erpnext/patches/v13_0/replace_pos_payment_mode_table.py
@@ -10,9 +10,13 @@
 	pos_profiles = frappe.get_all("POS Profile")
 
 	for pos_profile in pos_profiles:
-		payments = frappe.db.sql("""
+		payments = frappe.db.sql(
+			"""
 			select idx, parentfield, parenttype, parent, mode_of_payment, `default` from `tabSales Invoice Payment` where parent=%s
-		""", pos_profile.name, as_dict=1)
+		""",
+			pos_profile.name,
+			as_dict=1,
+		)
 		if payments:
 			for payment_mode in payments:
 				pos_payment_method = frappe.new_doc("POS Payment Method")
diff --git a/erpnext/patches/v13_0/replace_supplier_item_group_with_party_specific_item.py b/erpnext/patches/v13_0/replace_supplier_item_group_with_party_specific_item.py
index ba96fdd..bf82f44 100644
--- a/erpnext/patches/v13_0/replace_supplier_item_group_with_party_specific_item.py
+++ b/erpnext/patches/v13_0/replace_supplier_item_group_with_party_specific_item.py
@@ -5,7 +5,7 @@
 
 
 def execute():
-	if frappe.db.table_exists('Supplier Item Group'):
+	if frappe.db.table_exists("Supplier Item Group"):
 		frappe.reload_doc("selling", "doctype", "party_specific_item")
 		sig = frappe.db.get_all("Supplier Item Group", fields=["name", "supplier", "item_group"])
 		for item in sig:
diff --git a/erpnext/patches/v13_0/requeue_failed_reposts.py b/erpnext/patches/v13_0/requeue_failed_reposts.py
index 213cb9e..752490d 100644
--- a/erpnext/patches/v13_0/requeue_failed_reposts.py
+++ b/erpnext/patches/v13_0/requeue_failed_reposts.py
@@ -4,9 +4,11 @@
 
 def execute():
 
-	reposts = frappe.get_all("Repost Item Valuation",
-		{"status": "Failed", "modified": [">", "2021-10-05"] },
-		["name", "modified", "error_log"])
+	reposts = frappe.get_all(
+		"Repost Item Valuation",
+		{"status": "Failed", "modified": [">", "2021-10-05"]},
+		["name", "modified", "error_log"],
+	)
 
 	for repost in reposts:
 		if "check_freezing_date" in cstr(repost.error_log):
diff --git a/erpnext/patches/v13_0/requeue_recoverable_reposts.py b/erpnext/patches/v13_0/requeue_recoverable_reposts.py
new file mode 100644
index 0000000..f37c21c
--- /dev/null
+++ b/erpnext/patches/v13_0/requeue_recoverable_reposts.py
@@ -0,0 +1,21 @@
+import frappe
+
+
+def execute():
+	recoverable = ("QueryDeadlockError", "QueryTimeoutError", "JobTimeoutException")
+
+	failed_reposts = frappe.get_all(
+		"Repost Item Valuation",
+		fields=["name", "error_log"],
+		filters={
+			"status": "Failed",
+			"docstatus": 1,
+			"modified": (">", "2022-04-20"),
+			"error_log": ("is", "set"),
+		},
+	)
+	for riv in failed_reposts:
+		for exc in recoverable:
+			if exc in riv.error_log:
+				frappe.db.set_value("Repost Item Valuation", riv.name, "status", "Queued")
+				break
diff --git a/erpnext/patches/v13_0/reset_clearance_date_for_intracompany_payment_entries.py b/erpnext/patches/v13_0/reset_clearance_date_for_intracompany_payment_entries.py
index 69fc6a2..5dfea5e 100644
--- a/erpnext/patches/v13_0/reset_clearance_date_for_intracompany_payment_entries.py
+++ b/erpnext/patches/v13_0/reset_clearance_date_for_intracompany_payment_entries.py
@@ -6,40 +6,35 @@
 
 
 def execute():
-    """
-        Reset Clearance Date for Payment Entries of type Internal Transfer that have only been reconciled with one Bank Transaction.
-        This will allow the Payment Entries to be reconciled with the second Bank Transaction using the Bank Reconciliation Tool.
-    """
+	"""
+	Reset Clearance Date for Payment Entries of type Internal Transfer that have only been reconciled with one Bank Transaction.
+	This will allow the Payment Entries to be reconciled with the second Bank Transaction using the Bank Reconciliation Tool.
+	"""
 
-    intra_company_pe = get_intra_company_payment_entries_with_clearance_dates()
-    reconciled_bank_transactions = get_reconciled_bank_transactions(intra_company_pe)
+	intra_company_pe = get_intra_company_payment_entries_with_clearance_dates()
+	reconciled_bank_transactions = get_reconciled_bank_transactions(intra_company_pe)
 
-    for payment_entry in reconciled_bank_transactions:
-        if len(reconciled_bank_transactions[payment_entry]) == 1:
-            frappe.db.set_value('Payment Entry', payment_entry, 'clearance_date', None)
+	for payment_entry in reconciled_bank_transactions:
+		if len(reconciled_bank_transactions[payment_entry]) == 1:
+			frappe.db.set_value("Payment Entry", payment_entry, "clearance_date", None)
+
 
 def get_intra_company_payment_entries_with_clearance_dates():
-    return frappe.get_all(
-        'Payment Entry',
-        filters = {
-            'payment_type': 'Internal Transfer',
-            'clearance_date': ["not in", None]
-        },
-        pluck = 'name'
-    )
+	return frappe.get_all(
+		"Payment Entry",
+		filters={"payment_type": "Internal Transfer", "clearance_date": ["not in", None]},
+		pluck="name",
+	)
+
 
 def get_reconciled_bank_transactions(intra_company_pe):
-    """Returns dictionary where each key:value pair is Payment Entry : List of Bank Transactions reconciled with Payment Entry"""
+	"""Returns dictionary where each key:value pair is Payment Entry : List of Bank Transactions reconciled with Payment Entry"""
 
-    reconciled_bank_transactions = {}
+	reconciled_bank_transactions = {}
 
-    for payment_entry in intra_company_pe:
-        reconciled_bank_transactions[payment_entry] = frappe.get_all(
-            'Bank Transaction Payments',
-            filters = {
-                'payment_entry': payment_entry
-            },
-            pluck='parent'
-        )
+	for payment_entry in intra_company_pe:
+		reconciled_bank_transactions[payment_entry] = frappe.get_all(
+			"Bank Transaction Payments", filters={"payment_entry": payment_entry}, pluck="parent"
+		)
 
-    return reconciled_bank_transactions
+	return reconciled_bank_transactions
diff --git a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py
deleted file mode 100644
index f82a0d5..0000000
--- a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import frappe
-
-
-def execute():
-	company = frappe.db.get_single_value('Global Defaults', 'default_company')
-	doctypes = ['Clinical Procedure', 'Inpatient Record', 'Lab Test', 'Sample Collection', 'Patient Appointment', 'Patient Encounter', 'Vital Signs', 'Therapy Session', 'Therapy Plan', 'Patient Assessment']
-	for entry in doctypes:
-		if frappe.db.exists('DocType', entry):
-			frappe.reload_doc('Healthcare', 'doctype', entry)
-			frappe.db.sql("update `tab{dt}` set company = {company} where ifnull(company, '') = ''".format(dt=entry, company=frappe.db.escape(company)))
diff --git a/erpnext/patches/v13_0/set_company_in_leave_ledger_entry.py b/erpnext/patches/v13_0/set_company_in_leave_ledger_entry.py
index c744f35..adc8784 100644
--- a/erpnext/patches/v13_0/set_company_in_leave_ledger_entry.py
+++ b/erpnext/patches/v13_0/set_company_in_leave_ledger_entry.py
@@ -2,7 +2,11 @@
 
 
 def execute():
-	frappe.reload_doc('HR', 'doctype', 'Leave Allocation')
-	frappe.reload_doc('HR', 'doctype', 'Leave Ledger Entry')
-	frappe.db.sql("""update `tabLeave Ledger Entry` as lle set company = (select company from `tabEmployee` where employee = lle.employee)""")
-	frappe.db.sql("""update `tabLeave Allocation` as la set company = (select company from `tabEmployee` where employee = la.employee)""")
+	frappe.reload_doc("HR", "doctype", "Leave Allocation")
+	frappe.reload_doc("HR", "doctype", "Leave Ledger Entry")
+	frappe.db.sql(
+		"""update `tabLeave Ledger Entry` as lle set company = (select company from `tabEmployee` where employee = lle.employee)"""
+	)
+	frappe.db.sql(
+		"""update `tabLeave Allocation` as la set company = (select company from `tabEmployee` where employee = la.employee)"""
+	)
diff --git a/erpnext/patches/v13_0/set_operation_time_based_on_operating_cost.py b/erpnext/patches/v13_0/set_operation_time_based_on_operating_cost.py
index e26285e..ef02e25 100644
--- a/erpnext/patches/v13_0/set_operation_time_based_on_operating_cost.py
+++ b/erpnext/patches/v13_0/set_operation_time_based_on_operating_cost.py
@@ -2,10 +2,11 @@
 
 
 def execute():
-	frappe.reload_doc('manufacturing', 'doctype', 'bom')
-	frappe.reload_doc('manufacturing', 'doctype', 'bom_operation')
+	frappe.reload_doc("manufacturing", "doctype", "bom")
+	frappe.reload_doc("manufacturing", "doctype", "bom_operation")
 
-	frappe.db.sql('''
+	frappe.db.sql(
+		"""
 		UPDATE
 			`tabBOM Operation`
 		SET
@@ -13,4 +14,5 @@
 		WHERE
 			time_in_mins = 0 AND operating_cost > 0
 			AND hour_rate > 0 AND docstatus = 1 AND parenttype = "BOM"
-	''')
+	"""
+	)
diff --git a/erpnext/patches/v13_0/set_payment_channel_in_payment_gateway_account.py b/erpnext/patches/v13_0/set_payment_channel_in_payment_gateway_account.py
index 87b3389..0cefa02 100644
--- a/erpnext/patches/v13_0/set_payment_channel_in_payment_gateway_account.py
+++ b/erpnext/patches/v13_0/set_payment_channel_in_payment_gateway_account.py
@@ -10,8 +10,11 @@
 	frappe.reload_doc("Accounts", "doctype", "Payment Gateway Account")
 	set_payment_channel_as_email()
 
+
 def set_payment_channel_as_email():
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		UPDATE `tabPayment Gateway Account`
 		SET `payment_channel` = "Email"
-	""")
+	"""
+	)
diff --git a/erpnext/patches/v13_0/set_payroll_entry_status.py b/erpnext/patches/v13_0/set_payroll_entry_status.py
new file mode 100644
index 0000000..97adff9
--- /dev/null
+++ b/erpnext/patches/v13_0/set_payroll_entry_status.py
@@ -0,0 +1,16 @@
+import frappe
+from frappe.query_builder import Case
+
+
+def execute():
+	PayrollEntry = frappe.qb.DocType("Payroll Entry")
+
+	(
+		frappe.qb.update(PayrollEntry).set(
+			"status",
+			Case()
+			.when(PayrollEntry.docstatus == 0, "Draft")
+			.when(PayrollEntry.docstatus == 1, "Submitted")
+			.else_("Cancelled"),
+		)
+	).run()
diff --git a/erpnext/patches/v13_0/set_per_billed_in_return_delivery_note.py b/erpnext/patches/v13_0/set_per_billed_in_return_delivery_note.py
new file mode 100644
index 0000000..a4d7012
--- /dev/null
+++ b/erpnext/patches/v13_0/set_per_billed_in_return_delivery_note.py
@@ -0,0 +1,29 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+
+
+def execute():
+	dn = frappe.qb.DocType("Delivery Note")
+	dn_item = frappe.qb.DocType("Delivery Note Item")
+
+	dn_list = (
+		frappe.qb.from_(dn)
+		.inner_join(dn_item)
+		.on(dn.name == dn_item.parent)
+		.select(dn.name)
+		.where(dn.docstatus == 1)
+		.where(dn.is_return == 1)
+		.where(dn.per_billed < 100)
+		.where(dn_item.returned_qty > 0)
+		.run(as_dict=True)
+	)
+
+	frappe.qb.update(dn_item).inner_join(dn).on(dn.name == dn_item.parent).set(
+		dn_item.returned_qty, 0
+	).where(dn.is_return == 1).where(dn_item.returned_qty > 0).run()
+
+	for d in dn_list:
+		dn_doc = frappe.get_doc("Delivery Note", d.get("name"))
+		dn_doc.run_method("update_billing_status")
diff --git a/erpnext/patches/v13_0/set_pos_closing_as_failed.py b/erpnext/patches/v13_0/set_pos_closing_as_failed.py
index 6a3785d..e2226c1 100644
--- a/erpnext/patches/v13_0/set_pos_closing_as_failed.py
+++ b/erpnext/patches/v13_0/set_pos_closing_as_failed.py
@@ -2,6 +2,6 @@
 
 
 def execute():
-    frappe.reload_doc('accounts', 'doctype', 'pos_closing_entry')
+	frappe.reload_doc("accounts", "doctype", "pos_closing_entry")
 
-    frappe.db.sql("update `tabPOS Closing Entry` set `status` = 'Failed' where `status` = 'Queued'")
+	frappe.db.sql("update `tabPOS Closing Entry` set `status` = 'Failed' where `status` = 'Queued'")
diff --git a/erpnext/patches/v13_0/set_return_against_in_pos_invoice_references.py b/erpnext/patches/v13_0/set_return_against_in_pos_invoice_references.py
new file mode 100644
index 0000000..d29b739
--- /dev/null
+++ b/erpnext/patches/v13_0/set_return_against_in_pos_invoice_references.py
@@ -0,0 +1,36 @@
+import frappe
+
+
+def execute():
+	"""
+	Fetch and Set is_return & return_against from POS Invoice in POS Invoice References table.
+	"""
+
+	POSClosingEntry = frappe.qb.DocType("POS Closing Entry")
+	open_pos_closing_entries = (
+		frappe.qb.from_(POSClosingEntry)
+		.select(POSClosingEntry.name)
+		.where(POSClosingEntry.docstatus == 0)
+		.run(pluck=True)
+	)
+
+	if not open_pos_closing_entries:
+		return
+
+	POSInvoiceReference = frappe.qb.DocType("POS Invoice Reference")
+	POSInvoice = frappe.qb.DocType("POS Invoice")
+	pos_invoice_references = (
+		frappe.qb.from_(POSInvoiceReference)
+		.join(POSInvoice)
+		.on(POSInvoiceReference.pos_invoice == POSInvoice.name)
+		.select(POSInvoiceReference.name, POSInvoice.is_return, POSInvoice.return_against)
+		.where(POSInvoiceReference.parent.isin(open_pos_closing_entries))
+		.run(as_dict=True)
+	)
+
+	for row in pos_invoice_references:
+		frappe.db.set_value("POS Invoice Reference", row.name, "is_return", row.is_return)
+		if row.is_return:
+			frappe.db.set_value("POS Invoice Reference", row.name, "return_against", row.return_against)
+		else:
+			frappe.db.set_value("POS Invoice Reference", row.name, "return_against", None)
diff --git a/erpnext/patches/v13_0/set_status_in_maintenance_schedule_table.py b/erpnext/patches/v13_0/set_status_in_maintenance_schedule_table.py
index 9887ad9..e1c8526 100644
--- a/erpnext/patches/v13_0/set_status_in_maintenance_schedule_table.py
+++ b/erpnext/patches/v13_0/set_status_in_maintenance_schedule_table.py
@@ -3,8 +3,10 @@
 
 def execute():
 	frappe.reload_doc("maintenance", "doctype", "Maintenance Schedule Detail")
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		UPDATE `tabMaintenance Schedule Detail`
 		SET completion_status = 'Pending'
 		WHERE docstatus < 2
-	""")
+	"""
+	)
diff --git a/erpnext/patches/v13_0/set_training_event_attendance.py b/erpnext/patches/v13_0/set_training_event_attendance.py
index e44f321..7b55758 100644
--- a/erpnext/patches/v13_0/set_training_event_attendance.py
+++ b/erpnext/patches/v13_0/set_training_event_attendance.py
@@ -2,8 +2,10 @@
 
 
 def execute():
-    frappe.reload_doc('hr', 'doctype', 'training_event')
-    frappe.reload_doc('hr', 'doctype', 'training_event_employee')
+	frappe.reload_doc("hr", "doctype", "training_event")
+	frappe.reload_doc("hr", "doctype", "training_event_employee")
 
-    frappe.db.sql("update `tabTraining Event Employee` set `attendance` = 'Present'")
-    frappe.db.sql("update `tabTraining Event Employee` set `is_mandatory` = 1 where `attendance` = 'Mandatory'")
+	frappe.db.sql("update `tabTraining Event Employee` set `attendance` = 'Present'")
+	frappe.db.sql(
+		"update `tabTraining Event Employee` set `is_mandatory` = 1 where `attendance` = 'Mandatory'"
+	)
diff --git a/erpnext/patches/v13_0/set_work_order_qty_in_so_from_mr.py b/erpnext/patches/v13_0/set_work_order_qty_in_so_from_mr.py
index f097ab9..1adf0d8 100644
--- a/erpnext/patches/v13_0/set_work_order_qty_in_so_from_mr.py
+++ b/erpnext/patches/v13_0/set_work_order_qty_in_so_from_mr.py
@@ -2,35 +2,37 @@
 
 
 def execute():
-    """
-    1. Get submitted Work Orders with MR, MR Item and SO set
-    2. Get SO Item detail from MR Item detail in WO, and set in WO
-    3. Update work_order_qty in SO
-    """
-    work_order = frappe.qb.DocType("Work Order")
-    query = (
-        frappe.qb.from_(work_order)
-        .select(
-            work_order.name, work_order.produced_qty,
-            work_order.material_request,
-            work_order.material_request_item,
-            work_order.sales_order
-        ).where(
-            (work_order.material_request.isnotnull())
-            & (work_order.material_request_item.isnotnull())
-            & (work_order.sales_order.isnotnull())
-            & (work_order.docstatus == 1)
-            & (work_order.produced_qty > 0)
-        )
-    )
-    results = query.run(as_dict=True)
+	"""
+	1. Get submitted Work Orders with MR, MR Item and SO set
+	2. Get SO Item detail from MR Item detail in WO, and set in WO
+	3. Update work_order_qty in SO
+	"""
+	work_order = frappe.qb.DocType("Work Order")
+	query = (
+		frappe.qb.from_(work_order)
+		.select(
+			work_order.name,
+			work_order.produced_qty,
+			work_order.material_request,
+			work_order.material_request_item,
+			work_order.sales_order,
+		)
+		.where(
+			(work_order.material_request.isnotnull())
+			& (work_order.material_request_item.isnotnull())
+			& (work_order.sales_order.isnotnull())
+			& (work_order.docstatus == 1)
+			& (work_order.produced_qty > 0)
+		)
+	)
+	results = query.run(as_dict=True)
 
-    for row in results:
-        so_item = frappe.get_value(
-            "Material Request Item", row.material_request_item, "sales_order_item"
-        )
-        frappe.db.set_value("Work Order", row.name, "sales_order_item", so_item)
+	for row in results:
+		so_item = frappe.get_value(
+			"Material Request Item", row.material_request_item, "sales_order_item"
+		)
+		frappe.db.set_value("Work Order", row.name, "sales_order_item", so_item)
 
-        if so_item:
-            wo = frappe.get_doc("Work Order", row.name)
-            wo.update_work_order_qty_in_so()
+		if so_item:
+			wo = frappe.get_doc("Work Order", row.name)
+			wo.update_work_order_qty_in_so()
diff --git a/erpnext/patches/v13_0/set_youtube_video_id.py b/erpnext/patches/v13_0/set_youtube_video_id.py
index e1eb1b9..9766bb8 100644
--- a/erpnext/patches/v13_0/set_youtube_video_id.py
+++ b/erpnext/patches/v13_0/set_youtube_video_id.py
@@ -4,7 +4,7 @@
 
 
 def execute():
-	frappe.reload_doc("utilities", "doctype","video")
+	frappe.reload_doc("utilities", "doctype", "video")
 
 	for video in frappe.get_all("Video", fields=["name", "url", "youtube_video_id"]):
 		if video.url and not video.youtube_video_id:
diff --git a/erpnext/patches/v13_0/setting_custom_roles_for_some_regional_reports.py b/erpnext/patches/v13_0/setting_custom_roles_for_some_regional_reports.py
index dc3f8aa..40c10f3 100644
--- a/erpnext/patches/v13_0/setting_custom_roles_for_some_regional_reports.py
+++ b/erpnext/patches/v13_0/setting_custom_roles_for_some_regional_reports.py
@@ -4,8 +4,8 @@
 
 
 def execute():
-    company = frappe.get_all('Company', filters = {'country': 'India'})
-    if not company:
-        return
+	company = frappe.get_all("Company", filters={"country": "India"})
+	if not company:
+		return
 
-    add_custom_roles_for_reports()
+	add_custom_roles_for_reports()
diff --git a/erpnext/patches/v13_0/setup_fields_for_80g_certificate_and_donation.py b/erpnext/patches/v13_0/setup_fields_for_80g_certificate_and_donation.py
index 2d35ea3..1c36b53 100644
--- a/erpnext/patches/v13_0/setup_fields_for_80g_certificate_and_donation.py
+++ b/erpnext/patches/v13_0/setup_fields_for_80g_certificate_and_donation.py
@@ -4,15 +4,13 @@
 
 
 def execute():
-	if frappe.get_all('Company', filters = {'country': 'India'}):
-		frappe.reload_doc('accounts', 'doctype', 'POS Invoice')
-		frappe.reload_doc('accounts', 'doctype', 'POS Invoice Item')
+	if frappe.get_all("Company", filters={"country": "India"}):
+		frappe.reload_doc("accounts", "doctype", "POS Invoice")
+		frappe.reload_doc("accounts", "doctype", "POS Invoice Item")
 
 		make_custom_fields()
 
-		if not frappe.db.exists('Party Type', 'Donor'):
-			frappe.get_doc({
-				'doctype': 'Party Type',
-				'party_type': 'Donor',
-				'account_type': 'Receivable'
-			}).insert(ignore_permissions=True)
+		if not frappe.db.exists("Party Type", "Donor"):
+			frappe.get_doc(
+				{"doctype": "Party Type", "party_type": "Donor", "account_type": "Receivable"}
+			).insert(ignore_permissions=True)
diff --git a/erpnext/patches/v13_0/setup_gratuity_rule_for_india_and_uae.py b/erpnext/patches/v13_0/setup_gratuity_rule_for_india_and_uae.py
index 82cc1ff..093e8a7 100644
--- a/erpnext/patches/v13_0/setup_gratuity_rule_for_india_and_uae.py
+++ b/erpnext/patches/v13_0/setup_gratuity_rule_for_india_and_uae.py
@@ -6,12 +6,14 @@
 
 
 def execute():
-    frappe.reload_doc('payroll', 'doctype', 'gratuity_rule')
-    frappe.reload_doc('payroll', 'doctype', 'gratuity_rule_slab')
-    frappe.reload_doc('payroll', 'doctype', 'gratuity_applicable_component')
-    if frappe.db.exists("Company", {"country": "India"}):
-        from erpnext.regional.india.setup import create_gratuity_rule
-        create_gratuity_rule()
-    if frappe.db.exists("Company", {"country": "United Arab Emirates"}):
-        from erpnext.regional.united_arab_emirates.setup import create_gratuity_rule
-        create_gratuity_rule()
+	frappe.reload_doc("payroll", "doctype", "gratuity_rule")
+	frappe.reload_doc("payroll", "doctype", "gratuity_rule_slab")
+	frappe.reload_doc("payroll", "doctype", "gratuity_applicable_component")
+	if frappe.db.exists("Company", {"country": "India"}):
+		from erpnext.regional.india.setup import create_gratuity_rule
+
+		create_gratuity_rule()
+	if frappe.db.exists("Company", {"country": "United Arab Emirates"}):
+		from erpnext.regional.united_arab_emirates.setup import create_gratuity_rule
+
+		create_gratuity_rule()
diff --git a/erpnext/patches/v13_0/setup_uae_vat_fields.py b/erpnext/patches/v13_0/setup_uae_vat_fields.py
index d89e052..7046646 100644
--- a/erpnext/patches/v13_0/setup_uae_vat_fields.py
+++ b/erpnext/patches/v13_0/setup_uae_vat_fields.py
@@ -7,12 +7,12 @@
 
 
 def execute():
-	company = frappe.get_all('Company', filters = {'country': 'United Arab Emirates'})
+	company = frappe.get_all("Company", filters={"country": "United Arab Emirates"})
 	if not company:
 		return
 
-	frappe.reload_doc('regional', 'report', 'uae_vat_201')
-	frappe.reload_doc('regional', 'doctype', 'uae_vat_settings')
-	frappe.reload_doc('regional', 'doctype', 'uae_vat_account')
+	frappe.reload_doc("regional", "report", "uae_vat_201")
+	frappe.reload_doc("regional", "doctype", "uae_vat_settings")
+	frappe.reload_doc("regional", "doctype", "uae_vat_account")
 
 	setup()
diff --git a/erpnext/patches/v13_0/stock_entry_enhancements.py b/erpnext/patches/v13_0/stock_entry_enhancements.py
index 968a83a..005980e 100644
--- a/erpnext/patches/v13_0/stock_entry_enhancements.py
+++ b/erpnext/patches/v13_0/stock_entry_enhancements.py
@@ -6,27 +6,31 @@
 
 
 def execute():
-    frappe.reload_doc("stock", "doctype", "stock_entry")
-    if frappe.db.has_column("Stock Entry", "add_to_transit"):
-        frappe.db.sql("""
+	frappe.reload_doc("stock", "doctype", "stock_entry")
+	if frappe.db.has_column("Stock Entry", "add_to_transit"):
+		frappe.db.sql(
+			"""
             UPDATE `tabStock Entry` SET
             stock_entry_type = 'Material Transfer',
             purpose = 'Material Transfer',
             add_to_transit = 1 WHERE stock_entry_type = 'Send to Warehouse'
-            """)
+            """
+		)
 
-        frappe.db.sql("""UPDATE `tabStock Entry` SET
+		frappe.db.sql(
+			"""UPDATE `tabStock Entry` SET
             stock_entry_type = 'Material Transfer',
             purpose = 'Material Transfer'
             WHERE stock_entry_type = 'Receive at Warehouse'
-            """)
+            """
+		)
 
-        frappe.reload_doc("stock", "doctype", "warehouse_type")
-        if not frappe.db.exists('Warehouse Type', 'Transit'):
-            doc = frappe.new_doc('Warehouse Type')
-            doc.name = 'Transit'
-            doc.insert()
+		frappe.reload_doc("stock", "doctype", "warehouse_type")
+		if not frappe.db.exists("Warehouse Type", "Transit"):
+			doc = frappe.new_doc("Warehouse Type")
+			doc.name = "Transit"
+			doc.insert()
 
-        frappe.reload_doc("stock", "doctype", "stock_entry_type")
-        frappe.delete_doc_if_exists("Stock Entry Type", "Send to Warehouse")
-        frappe.delete_doc_if_exists("Stock Entry Type", "Receive at Warehouse")
+		frappe.reload_doc("stock", "doctype", "stock_entry_type")
+		frappe.delete_doc_if_exists("Stock Entry Type", "Send to Warehouse")
+		frappe.delete_doc_if_exists("Stock Entry Type", "Receive at Warehouse")
diff --git a/erpnext/patches/v13_0/trim_sales_invoice_custom_field_length.py b/erpnext/patches/v13_0/trim_sales_invoice_custom_field_length.py
index fd48c0d..5f3fc57 100644
--- a/erpnext/patches/v13_0/trim_sales_invoice_custom_field_length.py
+++ b/erpnext/patches/v13_0/trim_sales_invoice_custom_field_length.py
@@ -7,12 +7,10 @@
 
 
 def execute():
-	company = frappe.get_all('Company', filters = {'country': 'India'})
+	company = frappe.get_all("Company", filters={"country": "India"})
 	if not company:
 		return
 
-	custom_fields = {
-		'Sales Invoice': get_custom_fields().get('Sales Invoice')
-	}
+	custom_fields = {"Sales Invoice": get_custom_fields().get("Sales Invoice")}
 
 	create_custom_fields(custom_fields, update=True)
diff --git a/erpnext/patches/v13_0/trim_whitespace_from_serial_nos.py b/erpnext/patches/v13_0/trim_whitespace_from_serial_nos.py
index 4ec22e9..b69a408 100644
--- a/erpnext/patches/v13_0/trim_whitespace_from_serial_nos.py
+++ b/erpnext/patches/v13_0/trim_whitespace_from_serial_nos.py
@@ -4,7 +4,8 @@
 
 
 def execute():
-	broken_sles = frappe.db.sql("""
+	broken_sles = frappe.db.sql(
+		"""
 			select name, serial_no
 			from `tabStock Ledger Entry`
 			where
@@ -12,15 +13,15 @@
 				and ( serial_no like %s or serial_no like %s or serial_no like %s or serial_no like %s
 					or serial_no = %s )
 			""",
-			(
-				" %",    # leading whitespace
-				"% ",    # trailing whitespace
-				"%\n %", # leading whitespace on newline
-				"% \n%", # trailing whitespace on newline
-				"\n",    # just new line
-			),
-			as_dict=True,
-		)
+		(
+			" %",  # leading whitespace
+			"% ",  # trailing whitespace
+			"%\n %",  # leading whitespace on newline
+			"% \n%",  # trailing whitespace on newline
+			"\n",  # just new line
+		),
+		as_dict=True,
+	)
 
 	frappe.db.MAX_WRITES_PER_TRANSACTION += len(broken_sles)
 
@@ -37,7 +38,9 @@
 		if correct_sr_no == sle.serial_no:
 			continue
 
-		frappe.db.set_value("Stock Ledger Entry", sle.name, "serial_no", correct_sr_no, update_modified=False)
+		frappe.db.set_value(
+			"Stock Ledger Entry", sle.name, "serial_no", correct_sr_no, update_modified=False
+		)
 		broken_serial_nos.update(serial_no_list)
 
 	if not broken_serial_nos:
@@ -45,14 +48,15 @@
 
 	# Patch serial No documents if they don't have purchase info
 	# Purchase info is used for fetching incoming rate
-	broken_sr_no_records = frappe.get_list("Serial No",
-			filters={
-				"status":"Active",
-				"name": ("in", broken_serial_nos),
-				"purchase_document_type": ("is", "not set")
-			},
-			pluck="name",
-		)
+	broken_sr_no_records = frappe.get_list(
+		"Serial No",
+		filters={
+			"status": "Active",
+			"name": ("in", broken_serial_nos),
+			"purchase_document_type": ("is", "not set"),
+		},
+		pluck="name",
+	)
 
 	frappe.db.MAX_WRITES_PER_TRANSACTION += len(broken_sr_no_records)
 
diff --git a/erpnext/patches/v13_0/update_accounts_in_loan_docs.py b/erpnext/patches/v13_0/update_accounts_in_loan_docs.py
index 440f912..bf98e9e 100644
--- a/erpnext/patches/v13_0/update_accounts_in_loan_docs.py
+++ b/erpnext/patches/v13_0/update_accounts_in_loan_docs.py
@@ -6,31 +6,13 @@
 	lr = frappe.qb.DocType("Loan Repayment").as_("lr")
 	loan = frappe.qb.DocType("Loan")
 
-	frappe.qb.update(
-		ld
-	).inner_join(
-		loan
-	).on(
-		loan.name == ld.against_loan
-	).set(
+	frappe.qb.update(ld).inner_join(loan).on(loan.name == ld.against_loan).set(
 		ld.disbursement_account, loan.disbursement_account
-	).set(
-		ld.loan_account, loan.loan_account
-	).where(
-		ld.docstatus < 2
-	).run()
+	).set(ld.loan_account, loan.loan_account).where(ld.docstatus < 2).run()
 
-	frappe.qb.update(
-		lr
-	).inner_join(
-		loan
-	).on(
-		loan.name == lr.against_loan
-	).set(
+	frappe.qb.update(lr).inner_join(loan).on(loan.name == lr.against_loan).set(
 		lr.payment_account, loan.payment_account
-	).set(
-		lr.loan_account, loan.loan_account
-	).set(
+	).set(lr.loan_account, loan.loan_account).set(
 		lr.penalty_income_account, loan.penalty_income_account
 	).where(
 		lr.docstatus < 2
diff --git a/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py b/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py
index 60466eb..3c6c5b5 100644
--- a/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py
+++ b/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py
@@ -11,11 +11,9 @@
 	frappe.reload_doc("manufacturing", "doctype", "work_order_item")
 	frappe.reload_doc("manufacturing", "doctype", "job_card")
 
-	data = frappe.get_all("Work Order",
-		filters = {
-			"docstatus": 1,
-			"status": ("in", ["In Process", "Completed"])
-		})
+	data = frappe.get_all(
+		"Work Order", filters={"docstatus": 1, "status": ("in", ["In Process", "Completed"])}
+	)
 
 	for d in data:
 		doc = frappe.get_doc("Work Order", d.name)
@@ -23,18 +21,22 @@
 		doc.db_set("actual_start_date", doc.actual_start_date, update_modified=False)
 
 		if doc.status == "Completed":
-			frappe.db.set_value("Work Order", d.name, {
-				"actual_end_date": doc.actual_end_date,
-				"lead_time": doc.lead_time
-			}, update_modified=False)
+			frappe.db.set_value(
+				"Work Order",
+				d.name,
+				{"actual_end_date": doc.actual_end_date, "lead_time": doc.lead_time},
+				update_modified=False,
+			)
 
 			if not doc.planned_end_date:
 				planned_end_date = add_to_date(doc.planned_start_date, minutes=doc.lead_time)
 				doc.db_set("planned_end_date", doc.actual_start_date, update_modified=False)
 
-	frappe.db.sql(""" UPDATE `tabJob Card` as jc, `tabWork Order` as wo
+	frappe.db.sql(
+		""" UPDATE `tabJob Card` as jc, `tabWork Order` as wo
 		SET
 			jc.production_item = wo.production_item, jc.item_name = wo.item_name
 		WHERE
 			jc.work_order = wo.name and IFNULL(jc.production_item, "") = ""
-	""")
\ No newline at end of file
+	"""
+	)
diff --git a/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py b/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py
index dc973a9..e37f291 100644
--- a/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py
+++ b/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py
@@ -2,7 +2,7 @@
 
 
 def execute():
-	""" Correct amount in child table of required items table."""
+	"""Correct amount in child table of required items table."""
 
 	frappe.reload_doc("manufacturing", "doctype", "work_order")
 	frappe.reload_doc("manufacturing", "doctype", "work_order_item")
diff --git a/erpnext/patches/v13_0/update_asset_quantity_field.py b/erpnext/patches/v13_0/update_asset_quantity_field.py
index 47884d1..265a3bb 100644
--- a/erpnext/patches/v13_0/update_asset_quantity_field.py
+++ b/erpnext/patches/v13_0/update_asset_quantity_field.py
@@ -2,7 +2,7 @@
 
 
 def execute():
-	if frappe.db.count('Asset'):
+	if frappe.db.count("Asset"):
 		frappe.reload_doc("assets", "doctype", "Asset")
-		asset = frappe.qb.DocType('Asset')
-		frappe.qb.update(asset).set(asset.asset_quantity, 1).run()
\ No newline at end of file
+		asset = frappe.qb.DocType("Asset")
+		frappe.qb.update(asset).set(asset.asset_quantity, 1).run()
diff --git a/erpnext/patches/v13_0/update_category_in_ltds_certificate.py b/erpnext/patches/v13_0/update_category_in_ltds_certificate.py
index a5f5a23..5a0873e 100644
--- a/erpnext/patches/v13_0/update_category_in_ltds_certificate.py
+++ b/erpnext/patches/v13_0/update_category_in_ltds_certificate.py
@@ -2,19 +2,15 @@
 
 
 def execute():
-	company = frappe.get_all('Company', filters = {'country': 'India'})
+	company = frappe.get_all("Company", filters={"country": "India"})
 	if not company:
 		return
 
-	frappe.reload_doc('regional', 'doctype', 'lower_deduction_certificate')
+	frappe.reload_doc("regional", "doctype", "lower_deduction_certificate")
 
 	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(
+	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
+	).where(ldc.tax_withholding_category.isnull()).run()
diff --git a/erpnext/patches/v13_0/update_dates_in_tax_withholding_category.py b/erpnext/patches/v13_0/update_dates_in_tax_withholding_category.py
index 90fb50f..c538476 100644
--- a/erpnext/patches/v13_0/update_dates_in_tax_withholding_category.py
+++ b/erpnext/patches/v13_0/update_dates_in_tax_withholding_category.py
@@ -5,22 +5,23 @@
 
 
 def execute():
-	frappe.reload_doc('accounts', 'doctype', 'Tax Withholding Rate')
+	frappe.reload_doc("accounts", "doctype", "Tax Withholding Rate")
 
-	if frappe.db.has_column('Tax Withholding Rate', 'fiscal_year'):
-		tds_category_rates = frappe.get_all('Tax Withholding Rate', fields=['name', 'fiscal_year'])
+	if frappe.db.has_column("Tax Withholding Rate", "fiscal_year"):
+		tds_category_rates = frappe.get_all("Tax Withholding Rate", fields=["name", "fiscal_year"])
 
 		fiscal_year_map = {}
-		fiscal_year_details = frappe.get_all('Fiscal Year', fields=['name', 'year_start_date', 'year_end_date'])
+		fiscal_year_details = frappe.get_all(
+			"Fiscal Year", fields=["name", "year_start_date", "year_end_date"]
+		)
 
 		for d in fiscal_year_details:
 			fiscal_year_map.setdefault(d.name, d)
 
 		for rate in tds_category_rates:
-			from_date = fiscal_year_map.get(rate.fiscal_year).get('year_start_date')
-			to_date = fiscal_year_map.get(rate.fiscal_year).get('year_end_date')
+			from_date = fiscal_year_map.get(rate.fiscal_year).get("year_start_date")
+			to_date = fiscal_year_map.get(rate.fiscal_year).get("year_end_date")
 
-			frappe.db.set_value('Tax Withholding Rate', rate.name, {
-				'from_date': from_date,
-				'to_date': to_date
-			})
\ No newline at end of file
+			frappe.db.set_value(
+				"Tax Withholding Rate", rate.name, {"from_date": from_date, "to_date": to_date}
+			)
diff --git a/erpnext/patches/v13_0/update_deferred_settings.py b/erpnext/patches/v13_0/update_deferred_settings.py
index 1b63635..03fe66f 100644
--- a/erpnext/patches/v13_0/update_deferred_settings.py
+++ b/erpnext/patches/v13_0/update_deferred_settings.py
@@ -5,8 +5,8 @@
 
 
 def execute():
-	accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
-	accounts_settings.book_deferred_entries_based_on = 'Days'
+	accounts_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
+	accounts_settings.book_deferred_entries_based_on = "Days"
 	accounts_settings.book_deferred_entries_via_journal_entry = 0
 	accounts_settings.submit_journal_entries = 0
 	accounts_settings.save()
diff --git a/erpnext/patches/v13_0/update_disbursement_account.py b/erpnext/patches/v13_0/update_disbursement_account.py
index c56fa8f..d6aba47 100644
--- a/erpnext/patches/v13_0/update_disbursement_account.py
+++ b/erpnext/patches/v13_0/update_disbursement_account.py
@@ -9,14 +9,6 @@
 	loan_type = frappe.qb.DocType("Loan Type")
 	loan = frappe.qb.DocType("Loan")
 
-	frappe.qb.update(
-		loan_type
-	).set(
-		loan_type.disbursement_account, loan_type.payment_account
-	).run()
+	frappe.qb.update(loan_type).set(loan_type.disbursement_account, loan_type.payment_account).run()
 
-	frappe.qb.update(
-		loan
-	).set(
-		loan.disbursement_account, loan.payment_account
-	).run()
\ No newline at end of file
+	frappe.qb.update(loan).set(loan.disbursement_account, loan.payment_account).run()
diff --git a/erpnext/patches/v13_0/update_employee_advance_status.py b/erpnext/patches/v13_0/update_employee_advance_status.py
new file mode 100644
index 0000000..fc9e05e
--- /dev/null
+++ b/erpnext/patches/v13_0/update_employee_advance_status.py
@@ -0,0 +1,29 @@
+import frappe
+
+
+def execute():
+	frappe.reload_doc("hr", "doctype", "employee_advance")
+
+	advance = frappe.qb.DocType("Employee Advance")
+	(
+		frappe.qb.update(advance)
+		.set(advance.status, "Returned")
+		.where(
+			(advance.docstatus == 1)
+			& ((advance.return_amount) & (advance.paid_amount == advance.return_amount))
+			& (advance.status == "Paid")
+		)
+	).run()
+
+	(
+		frappe.qb.update(advance)
+		.set(advance.status, "Partly Claimed and Returned")
+		.where(
+			(advance.docstatus == 1)
+			& (
+				(advance.claimed_amount & advance.return_amount)
+				& (advance.paid_amount == (advance.return_amount + advance.claimed_amount))
+			)
+			& (advance.status == "Paid")
+		)
+	).run()
diff --git a/erpnext/patches/v13_0/update_exchange_rate_settings.py b/erpnext/patches/v13_0/update_exchange_rate_settings.py
index b7ec232..ed11c62 100644
--- a/erpnext/patches/v13_0/update_exchange_rate_settings.py
+++ b/erpnext/patches/v13_0/update_exchange_rate_settings.py
@@ -4,7 +4,7 @@
 
 
 def execute():
-	frappe.reload_doc("accounts", "doctype", "currency_exchange_settings")
 	frappe.reload_doc("accounts", "doctype", "currency_exchange_settings_result")
 	frappe.reload_doc("accounts", "doctype", "currency_exchange_settings_details")
-	setup_currency_exchange()
\ No newline at end of file
+	frappe.reload_doc("accounts", "doctype", "currency_exchange_settings")
+	setup_currency_exchange()
diff --git a/erpnext/patches/v13_0/update_expense_claim_status_for_paid_advances.py b/erpnext/patches/v13_0/update_expense_claim_status_for_paid_advances.py
new file mode 100644
index 0000000..2bc17ae
--- /dev/null
+++ b/erpnext/patches/v13_0/update_expense_claim_status_for_paid_advances.py
@@ -0,0 +1,25 @@
+import frappe
+
+
+def execute():
+	"""
+	Update Expense Claim status to Paid if:
+	        - the entire required amount is already covered via linked advances
+	        - the claim is partially paid via advances and the rest is reimbursed
+	"""
+
+	ExpenseClaim = frappe.qb.DocType("Expense Claim")
+
+	(
+		frappe.qb.update(ExpenseClaim)
+		.set(ExpenseClaim.status, "Paid")
+		.where(
+			(
+				(ExpenseClaim.grand_total == 0)
+				| (ExpenseClaim.grand_total == ExpenseClaim.total_amount_reimbursed)
+			)
+			& (ExpenseClaim.approval_status == "Approved")
+			& (ExpenseClaim.docstatus == 1)
+			& (ExpenseClaim.total_sanctioned_amount > 0)
+		)
+	).run()
diff --git a/erpnext/patches/v13_0/update_job_card_details.py b/erpnext/patches/v13_0/update_job_card_details.py
index 12f9006..73baecf 100644
--- a/erpnext/patches/v13_0/update_job_card_details.py
+++ b/erpnext/patches/v13_0/update_job_card_details.py
@@ -10,8 +10,10 @@
 	frappe.reload_doc("manufacturing", "doctype", "job_card_item")
 	frappe.reload_doc("manufacturing", "doctype", "work_order_operation")
 
-	frappe.db.sql(""" update `tabJob Card` jc, `tabWork Order Operation` wo
+	frappe.db.sql(
+		""" update `tabJob Card` jc, `tabWork Order Operation` wo
 		SET	jc.hour_rate =  wo.hour_rate
 		WHERE
 			jc.operation_id = wo.name and jc.docstatus < 2 and wo.hour_rate > 0
-	""")
+	"""
+	)
diff --git a/erpnext/patches/v13_0/update_job_card_status.py b/erpnext/patches/v13_0/update_job_card_status.py
index 797a3e2..f2d12da 100644
--- a/erpnext/patches/v13_0/update_job_card_status.py
+++ b/erpnext/patches/v13_0/update_job_card_status.py
@@ -7,8 +7,8 @@
 def execute():
 
 	job_card = frappe.qb.DocType("Job Card")
-	(frappe.qb
-		.update(job_card)
+	(
+		frappe.qb.update(job_card)
 		.set(job_card.status, "Completed")
 		.where(
 			(job_card.docstatus == 1)
diff --git a/erpnext/patches/v13_0/update_maintenance_schedule_field_in_visit.py b/erpnext/patches/v13_0/update_maintenance_schedule_field_in_visit.py
index 7a8c1c6..136d34e 100644
--- a/erpnext/patches/v13_0/update_maintenance_schedule_field_in_visit.py
+++ b/erpnext/patches/v13_0/update_maintenance_schedule_field_in_visit.py
@@ -1,25 +1,20 @@
-
 import frappe
 
 
 def execute():
-	frappe.reload_doctype('Maintenance Visit')
-	frappe.reload_doctype('Maintenance Visit Purpose')
+	frappe.reload_doctype("Maintenance Visit")
+	frappe.reload_doctype("Maintenance Visit Purpose")
 
 	# Updates the Maintenance Schedule link to fetch serial nos
 	from frappe.query_builder.functions import Coalesce
-	mvp = frappe.qb.DocType('Maintenance Visit Purpose')
-	mv = frappe.qb.DocType('Maintenance Visit')
 
-	frappe.qb.update(
-		mv
-	).join(
-		mvp
-	).on(mvp.parent == mv.name).set(
-		mv.maintenance_schedule,
-		Coalesce(mvp.prevdoc_docname, '')
+	mvp = frappe.qb.DocType("Maintenance Visit Purpose")
+	mv = frappe.qb.DocType("Maintenance Visit")
+
+	frappe.qb.update(mv).join(mvp).on(mvp.parent == mv.name).set(
+		mv.maintenance_schedule, Coalesce(mvp.prevdoc_docname, "")
 	).where(
-		(mv.maintenance_type == "Scheduled")
-		& (mvp.prevdoc_docname.notnull())
-		& (mv.docstatus < 2)
-	).run(as_dict=1)
+		(mv.maintenance_type == "Scheduled") & (mvp.prevdoc_docname.notnull()) & (mv.docstatus < 2)
+	).run(
+		as_dict=1
+	)
diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py
index e226f1d..a1d40b7 100644
--- a/erpnext/patches/v13_0/update_old_loans.py
+++ b/erpnext/patches/v13_0/update_old_loans.py
@@ -16,69 +16,110 @@
 
 	# Create a penalty account for loan types
 
-	frappe.reload_doc('loan_management', 'doctype', 'loan_type')
-	frappe.reload_doc('loan_management', 'doctype', 'loan')
-	frappe.reload_doc('loan_management', 'doctype', 'repayment_schedule')
-	frappe.reload_doc('loan_management', 'doctype', 'process_loan_interest_accrual')
-	frappe.reload_doc('loan_management', 'doctype', 'loan_repayment')
-	frappe.reload_doc('loan_management', 'doctype', 'loan_repayment_detail')
-	frappe.reload_doc('loan_management', 'doctype', 'loan_interest_accrual')
-	frappe.reload_doc('accounts', 'doctype', 'gl_entry')
-	frappe.reload_doc('accounts', 'doctype', 'journal_entry_account')
+	frappe.reload_doc("loan_management", "doctype", "loan_type")
+	frappe.reload_doc("loan_management", "doctype", "loan")
+	frappe.reload_doc("loan_management", "doctype", "repayment_schedule")
+	frappe.reload_doc("loan_management", "doctype", "process_loan_interest_accrual")
+	frappe.reload_doc("loan_management", "doctype", "loan_repayment")
+	frappe.reload_doc("loan_management", "doctype", "loan_repayment_detail")
+	frappe.reload_doc("loan_management", "doctype", "loan_interest_accrual")
+	frappe.reload_doc("accounts", "doctype", "gl_entry")
+	frappe.reload_doc("accounts", "doctype", "journal_entry_account")
 
 	updated_loan_types = []
 	loans_to_close = []
 
 	# Update old loan status as closed
-	if frappe.db.has_column('Repayment Schedule', 'paid'):
-		loans_list = frappe.db.sql("""SELECT distinct parent from `tabRepayment Schedule`
-			where paid = 0 and docstatus = 1""", as_dict=1)
+	if frappe.db.has_column("Repayment Schedule", "paid"):
+		loans_list = frappe.db.sql(
+			"""SELECT distinct parent from `tabRepayment Schedule`
+			where paid = 0 and docstatus = 1""",
+			as_dict=1,
+		)
 
 		loans_to_close = [d.parent for d in loans_list]
 
 	if loans_to_close:
-		frappe.db.sql("UPDATE `tabLoan` set status = 'Closed' where name not in (%s)" % (', '.join(['%s'] * len(loans_to_close))), tuple(loans_to_close))
+		frappe.db.sql(
+			"UPDATE `tabLoan` set status = 'Closed' where name not in (%s)"
+			% (", ".join(["%s"] * len(loans_to_close))),
+			tuple(loans_to_close),
+		)
 
-	loans = frappe.get_all('Loan', fields=['name', 'loan_type', 'company', 'status', 'mode_of_payment',
-		'applicant_type', 'applicant', 'loan_account', 'payment_account', 'interest_income_account'],
-		filters={'docstatus': 1, 'status': ('!=', 'Closed')})
+	loans = frappe.get_all(
+		"Loan",
+		fields=[
+			"name",
+			"loan_type",
+			"company",
+			"status",
+			"mode_of_payment",
+			"applicant_type",
+			"applicant",
+			"loan_account",
+			"payment_account",
+			"interest_income_account",
+		],
+		filters={"docstatus": 1, "status": ("!=", "Closed")},
+	)
 
 	for loan in loans:
 		# Update details in Loan Types and Loan
-		loan_type_company = frappe.db.get_value('Loan Type', loan.loan_type, 'company')
+		loan_type_company = frappe.db.get_value("Loan Type", loan.loan_type, "company")
 		loan_type = loan.loan_type
 
-		group_income_account = frappe.get_value('Account', {'company': loan.company,
-			'is_group': 1, 'root_type': 'Income', 'account_name': _('Indirect Income')})
+		group_income_account = frappe.get_value(
+			"Account",
+			{
+				"company": loan.company,
+				"is_group": 1,
+				"root_type": "Income",
+				"account_name": _("Indirect Income"),
+			},
+		)
 
 		if not group_income_account:
-			group_income_account = frappe.get_value('Account', {'company': loan.company,
-				'is_group': 1, 'root_type': 'Income'})
+			group_income_account = frappe.get_value(
+				"Account", {"company": loan.company, "is_group": 1, "root_type": "Income"}
+			)
 
-		penalty_account = create_account(company=loan.company, account_type='Income Account',
-			account_name='Penalty Account', parent_account=group_income_account)
+		penalty_account = create_account(
+			company=loan.company,
+			account_type="Income Account",
+			account_name="Penalty Account",
+			parent_account=group_income_account,
+		)
 
 		# Same loan type used for multiple companies
 		if loan_type_company and loan_type_company != loan.company:
 			# get loan type for appropriate company
-			loan_type_name = frappe.get_value('Loan Type', {'company': loan.company,
-				'mode_of_payment': loan.mode_of_payment, 'loan_account': loan.loan_account,
-				'payment_account': loan.payment_account, 'interest_income_account': loan.interest_income_account,
-				'penalty_income_account': loan.penalty_income_account}, 'name')
+			loan_type_name = frappe.get_value(
+				"Loan Type",
+				{
+					"company": loan.company,
+					"mode_of_payment": loan.mode_of_payment,
+					"loan_account": loan.loan_account,
+					"payment_account": loan.payment_account,
+					"interest_income_account": loan.interest_income_account,
+					"penalty_income_account": loan.penalty_income_account,
+				},
+				"name",
+			)
 
 			if not loan_type_name:
 				loan_type_name = create_loan_type(loan, loan_type_name, penalty_account)
 
 			# update loan type in loan
-			frappe.db.sql("UPDATE `tabLoan` set loan_type = %s where name = %s", (loan_type_name,
-				loan.name))
+			frappe.db.sql(
+				"UPDATE `tabLoan` set loan_type = %s where name = %s", (loan_type_name, loan.name)
+			)
 
 			loan_type = loan_type_name
 			if loan_type_name not in updated_loan_types:
 				updated_loan_types.append(loan_type_name)
 
 		elif not loan_type_company:
-			loan_type_doc = frappe.get_doc('Loan Type', loan.loan_type)
+			loan_type_doc = frappe.get_doc("Loan Type", loan.loan_type)
 			loan_type_doc.is_term_loan = 1
 			loan_type_doc.company = loan.company
 			loan_type_doc.mode_of_payment = loan.mode_of_payment
@@ -91,26 +132,29 @@
 			loan_type = loan.loan_type
 
 		if loan_type in updated_loan_types:
-			if loan.status == 'Fully Disbursed':
-				status = 'Disbursed'
-			elif loan.status == 'Repaid/Closed':
-				status = 'Closed'
+			if loan.status == "Fully Disbursed":
+				status = "Disbursed"
+			elif loan.status == "Repaid/Closed":
+				status = "Closed"
 			else:
 				status = loan.status
 
-			frappe.db.set_value('Loan', loan.name, {
-				'is_term_loan': 1,
-				'penalty_income_account': penalty_account,
-				'status': status
-			})
+			frappe.db.set_value(
+				"Loan",
+				loan.name,
+				{"is_term_loan": 1, "penalty_income_account": penalty_account, "status": status},
+			)
 
-			process_loan_interest_accrual_for_term_loans(posting_date=nowdate(), loan_type=loan_type,
-				loan=loan.name)
+			process_loan_interest_accrual_for_term_loans(
+				posting_date=nowdate(), loan_type=loan_type, loan=loan.name
+			)
 
-
-			if frappe.db.has_column('Repayment Schedule', 'paid'):
-				total_principal, total_interest = frappe.db.get_value('Repayment Schedule', {'paid': 1, 'parent': loan.name},
-					['sum(principal_amount) as total_principal', 'sum(interest_amount) as total_interest'])
+			if frappe.db.has_column("Repayment Schedule", "paid"):
+				total_principal, total_interest = frappe.db.get_value(
+					"Repayment Schedule",
+					{"paid": 1, "parent": loan.name},
+					["sum(principal_amount) as total_principal", "sum(interest_amount) as total_interest"],
+				)
 
 				accrued_entries = get_accrued_interest_entries(loan.name)
 				for entry in accrued_entries:
@@ -127,17 +171,20 @@
 					else:
 						principal_paid = flt(total_principal)
 
-					frappe.db.sql(""" UPDATE `tabLoan Interest Accrual`
+					frappe.db.sql(
+						""" UPDATE `tabLoan Interest Accrual`
 						SET paid_principal_amount = `paid_principal_amount` + %s,
 							paid_interest_amount = `paid_interest_amount` + %s
 						WHERE name = %s""",
-						(principal_paid, interest_paid, entry.name))
+						(principal_paid, interest_paid, entry.name),
+					)
 
 					total_principal = flt(total_principal) - principal_paid
 					total_interest = flt(total_interest) - interest_paid
 
+
 def create_loan_type(loan, loan_type_name, penalty_account):
-	loan_type_doc = frappe.new_doc('Loan Type')
+	loan_type_doc = frappe.new_doc("Loan Type")
 	loan_type_doc.loan_name = make_autoname("Loan Type-.####")
 	loan_type_doc.is_term_loan = 1
 	loan_type_doc.company = loan.company
diff --git a/erpnext/patches/v13_0/update_payment_terms_outstanding.py b/erpnext/patches/v13_0/update_payment_terms_outstanding.py
index aea09ad..d0c25f3 100644
--- a/erpnext/patches/v13_0/update_payment_terms_outstanding.py
+++ b/erpnext/patches/v13_0/update_payment_terms_outstanding.py
@@ -7,10 +7,12 @@
 
 def execute():
 	frappe.reload_doc("accounts", "doctype", "Payment Schedule")
-	if frappe.db.count('Payment Schedule'):
-		frappe.db.sql('''
+	if frappe.db.count("Payment Schedule"):
+		frappe.db.sql(
+			"""
 			UPDATE
 				`tabPayment Schedule` ps
 			SET
 				ps.outstanding = (ps.payment_amount - ps.paid_amount)
-		''')
+		"""
+		)
diff --git a/erpnext/patches/v13_0/update_pos_closing_entry_in_merge_log.py b/erpnext/patches/v13_0/update_pos_closing_entry_in_merge_log.py
index b2e3559..49826df 100644
--- a/erpnext/patches/v13_0/update_pos_closing_entry_in_merge_log.py
+++ b/erpnext/patches/v13_0/update_pos_closing_entry_in_merge_log.py
@@ -8,8 +8,9 @@
 def execute():
 	frappe.reload_doc("accounts", "doctype", "POS Invoice Merge Log")
 	frappe.reload_doc("accounts", "doctype", "POS Closing Entry")
-	if frappe.db.count('POS Invoice Merge Log'):
-		frappe.db.sql('''
+	if frappe.db.count("POS Invoice Merge Log"):
+		frappe.db.sql(
+			"""
 			UPDATE
 				`tabPOS Invoice Merge Log` log, `tabPOS Invoice Reference` log_ref
 			SET
@@ -20,7 +21,8 @@
 				)
 			WHERE
 				log_ref.parent = log.name
-		''')
+		"""
+		)
 
-		frappe.db.sql('''UPDATE `tabPOS Closing Entry` SET status = 'Submitted' where docstatus = 1''')
-		frappe.db.sql('''UPDATE `tabPOS Closing Entry` SET status = 'Cancelled' where docstatus = 2''')
+		frappe.db.sql("""UPDATE `tabPOS Closing Entry` SET status = 'Submitted' where docstatus = 1""")
+		frappe.db.sql("""UPDATE `tabPOS Closing Entry` SET status = 'Cancelled' where docstatus = 2""")
diff --git a/erpnext/patches/v13_0/update_project_template_tasks.py b/erpnext/patches/v13_0/update_project_template_tasks.py
index 29debc6..c9a2322 100644
--- a/erpnext/patches/v13_0/update_project_template_tasks.py
+++ b/erpnext/patches/v13_0/update_project_template_tasks.py
@@ -11,38 +11,39 @@
 	frappe.reload_doc("projects", "doctype", "task")
 
 	# Update property setter status if any
-	property_setter = frappe.db.get_value('Property Setter', {'doc_type': 'Task',
-		'field_name': 'status', 'property': 'options'})
+	property_setter = frappe.db.get_value(
+		"Property Setter", {"doc_type": "Task", "field_name": "status", "property": "options"}
+	)
 
 	if property_setter:
-		property_setter_doc = frappe.get_doc('Property Setter', {'doc_type': 'Task',
-			'field_name': 'status', 'property': 'options'})
+		property_setter_doc = frappe.get_doc(
+			"Property Setter", {"doc_type": "Task", "field_name": "status", "property": "options"}
+		)
 		property_setter_doc.value += "\nTemplate"
 		property_setter_doc.save()
 
-	for template_name in frappe.get_all('Project Template'):
+	for template_name in frappe.get_all("Project Template"):
 		template = frappe.get_doc("Project Template", template_name.name)
 		replace_tasks = False
 		new_tasks = []
 		for task in template.tasks:
 			if task.subject:
 				replace_tasks = True
-				new_task = frappe.get_doc(dict(
-					doctype = "Task",
-					subject = task.subject,
-					start = task.start,
-					duration = task.duration,
-					task_weight = task.task_weight,
-					description = task.description,
-					is_template = 1
-				)).insert()
+				new_task = frappe.get_doc(
+					dict(
+						doctype="Task",
+						subject=task.subject,
+						start=task.start,
+						duration=task.duration,
+						task_weight=task.task_weight,
+						description=task.description,
+						is_template=1,
+					)
+				).insert()
 				new_tasks.append(new_task)
 
 		if replace_tasks:
 			template.tasks = []
 			for tsk in new_tasks:
-				template.append("tasks", {
-					"task": tsk.name,
-					"subject": tsk.subject
-				})
+				template.append("tasks", {"task": tsk.name, "subject": tsk.subject})
 			template.save()
diff --git a/erpnext/patches/v13_0/update_reason_for_resignation_in_employee.py b/erpnext/patches/v13_0/update_reason_for_resignation_in_employee.py
index f9bfc54..31aa292 100644
--- a/erpnext/patches/v13_0/update_reason_for_resignation_in_employee.py
+++ b/erpnext/patches/v13_0/update_reason_for_resignation_in_employee.py
@@ -6,10 +6,12 @@
 
 
 def execute():
-    frappe.reload_doc("hr", "doctype", "employee")
+	frappe.reload_doc("hr", "doctype", "employee")
 
-    if frappe.db.has_column("Employee", "reason_for_resignation"):
-        frappe.db.sql(""" UPDATE `tabEmployee`
+	if frappe.db.has_column("Employee", "reason_for_resignation"):
+		frappe.db.sql(
+			""" UPDATE `tabEmployee`
             SET reason_for_leaving = reason_for_resignation
             WHERE status = 'Left' and reason_for_leaving is null and reason_for_resignation is not null
-        """)
+        """
+		)
diff --git a/erpnext/patches/v13_0/update_recipient_email_digest.py b/erpnext/patches/v13_0/update_recipient_email_digest.py
index d4d45af..af7f3ff 100644
--- a/erpnext/patches/v13_0/update_recipient_email_digest.py
+++ b/erpnext/patches/v13_0/update_recipient_email_digest.py
@@ -6,17 +6,19 @@
 
 
 def execute():
-    frappe.reload_doc("setup", "doctype", "Email Digest")
-    frappe.reload_doc("setup", "doctype", "Email Digest Recipient")
-    email_digests = frappe.db.get_list('Email Digest', fields=['name', 'recipient_list'])
-    for email_digest in email_digests:
-        if email_digest.recipient_list:
-            for recipient in email_digest.recipient_list.split("\n"):
-                doc = frappe.get_doc({
-                    'doctype': 'Email Digest Recipient',
-                    'parenttype': 'Email Digest',
-                    'parentfield': 'recipients',
-                    'parent': email_digest.name,
-                    'recipient': recipient
-                })
-                doc.insert()
+	frappe.reload_doc("setup", "doctype", "Email Digest")
+	frappe.reload_doc("setup", "doctype", "Email Digest Recipient")
+	email_digests = frappe.db.get_list("Email Digest", fields=["name", "recipient_list"])
+	for email_digest in email_digests:
+		if email_digest.recipient_list:
+			for recipient in email_digest.recipient_list.split("\n"):
+				doc = frappe.get_doc(
+					{
+						"doctype": "Email Digest Recipient",
+						"parenttype": "Email Digest",
+						"parentfield": "recipients",
+						"parent": email_digest.name,
+						"recipient": recipient,
+					}
+				)
+				doc.insert()
diff --git a/erpnext/patches/v13_0/update_reserved_qty_closed_wo.py b/erpnext/patches/v13_0/update_reserved_qty_closed_wo.py
index 00926b0..72e77fe 100644
--- a/erpnext/patches/v13_0/update_reserved_qty_closed_wo.py
+++ b/erpnext/patches/v13_0/update_reserved_qty_closed_wo.py
@@ -9,15 +9,12 @@
 	wo_item = frappe.qb.DocType("Work Order Item")
 
 	incorrect_item_wh = (
-		frappe.qb
-			.from_(wo)
-			.join(wo_item).on(wo.name == wo_item.parent)
-			.select(wo_item.item_code, wo.source_warehouse).distinct()
-			.where(
-				(wo.status == "Closed")
-				& (wo.docstatus == 1)
-				& (wo.source_warehouse.notnull())
-			)
+		frappe.qb.from_(wo)
+		.join(wo_item)
+		.on(wo.name == wo_item.parent)
+		.select(wo_item.item_code, wo.source_warehouse)
+		.distinct()
+		.where((wo.status == "Closed") & (wo.docstatus == 1) & (wo.source_warehouse.notnull()))
 	).run()
 
 	for item_code, warehouse in incorrect_item_wh:
diff --git a/erpnext/patches/v13_0/update_response_by_variance.py b/erpnext/patches/v13_0/update_response_by_variance.py
index d65e903..7304455 100644
--- a/erpnext/patches/v13_0/update_response_by_variance.py
+++ b/erpnext/patches/v13_0/update_response_by_variance.py
@@ -6,27 +6,49 @@
 
 
 def execute():
-	if frappe.db.exists('DocType', 'Issue') and frappe.db.count('Issue'):
-		invalid_issues = frappe.get_all('Issue', {
-			'first_responded_on': ['is', 'set'],
-			'response_by_variance': ['<', 0]
-		}, ["name", "response_by_variance", "timestampdiff(Second, `first_responded_on`, `response_by`) as variance"])
+	if frappe.db.exists("DocType", "Issue") and frappe.db.count("Issue"):
+		invalid_issues = frappe.get_all(
+			"Issue",
+			{"first_responded_on": ["is", "set"], "response_by_variance": ["<", 0]},
+			[
+				"name",
+				"response_by_variance",
+				"timestampdiff(Second, `first_responded_on`, `response_by`) as variance",
+			],
+		)
 
 		# issues which has response_by_variance set as -ve
 		# but diff between first_responded_on & response_by is +ve i.e SLA isn't failed
-		invalid_issues = [d for d in invalid_issues if d.get('variance') > 0]
+		invalid_issues = [d for d in invalid_issues if d.get("variance") > 0]
 
 		for issue in invalid_issues:
-			frappe.db.set_value('Issue', issue.get('name'), 'response_by_variance', issue.get('variance'), update_modified=False)
+			frappe.db.set_value(
+				"Issue",
+				issue.get("name"),
+				"response_by_variance",
+				issue.get("variance"),
+				update_modified=False,
+			)
 
-		invalid_issues = frappe.get_all('Issue', {
-			'resolution_date': ['is', 'set'],
-			'resolution_by_variance': ['<', 0]
-		}, ["name", "resolution_by_variance", "timestampdiff(Second, `resolution_date`, `resolution_by`) as variance"])
+		invalid_issues = frappe.get_all(
+			"Issue",
+			{"resolution_date": ["is", "set"], "resolution_by_variance": ["<", 0]},
+			[
+				"name",
+				"resolution_by_variance",
+				"timestampdiff(Second, `resolution_date`, `resolution_by`) as variance",
+			],
+		)
 
 		# issues which has resolution_by_variance set as -ve
 		# but diff between resolution_date & resolution_by is +ve i.e SLA isn't failed
-		invalid_issues = [d for d in invalid_issues if d.get('variance') > 0]
+		invalid_issues = [d for d in invalid_issues if d.get("variance") > 0]
 
 		for issue in invalid_issues:
-			frappe.db.set_value('Issue', issue.get('name'), 'resolution_by_variance', issue.get('variance'), update_modified=False)
+			frappe.db.set_value(
+				"Issue",
+				issue.get("name"),
+				"resolution_by_variance",
+				issue.get("variance"),
+				update_modified=False,
+			)
diff --git a/erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py b/erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py
index dd64e05..9b5845f 100644
--- a/erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py
+++ b/erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py
@@ -6,14 +6,16 @@
 
 
 def execute():
-	frappe.reload_doc('stock', 'doctype', 'purchase_receipt')
-	frappe.reload_doc('stock', 'doctype', 'purchase_receipt_item')
-	frappe.reload_doc('stock', 'doctype', 'delivery_note')
-	frappe.reload_doc('stock', 'doctype', 'delivery_note_item')
-	frappe.reload_doc('stock', 'doctype', 'stock_settings')
+	frappe.reload_doc("stock", "doctype", "purchase_receipt")
+	frappe.reload_doc("stock", "doctype", "purchase_receipt_item")
+	frappe.reload_doc("stock", "doctype", "delivery_note")
+	frappe.reload_doc("stock", "doctype", "delivery_note_item")
+	frappe.reload_doc("stock", "doctype", "stock_settings")
 
 	def update_from_return_docs(doctype):
-		for return_doc in frappe.get_all(doctype, filters={'is_return' : 1, 'docstatus' : 1, 'return_against': ('!=', '')}):
+		for return_doc in frappe.get_all(
+			doctype, filters={"is_return": 1, "docstatus": 1, "return_against": ("!=", "")}
+		):
 			# Update original receipt/delivery document from return
 			return_doc = frappe.get_cached_doc(doctype, return_doc.name)
 			try:
@@ -27,9 +29,11 @@
 			frappe.db.commit()
 
 	# Set received qty in stock uom in PR, as returned qty is checked against it
-	frappe.db.sql(""" update `tabPurchase Receipt Item`
+	frappe.db.sql(
+		""" update `tabPurchase Receipt Item`
 		set received_stock_qty = received_qty * conversion_factor
-		where docstatus = 1 """)
+		where docstatus = 1 """
+	)
 
-	for doctype in ('Purchase Receipt', 'Delivery Note'):
+	for doctype in ("Purchase Receipt", "Delivery Note"):
 		update_from_return_docs(doctype)
diff --git a/erpnext/patches/v13_0/update_sane_transfer_against.py b/erpnext/patches/v13_0/update_sane_transfer_against.py
index a163d38..45691e2 100644
--- a/erpnext/patches/v13_0/update_sane_transfer_against.py
+++ b/erpnext/patches/v13_0/update_sane_transfer_against.py
@@ -4,8 +4,8 @@
 def execute():
 	bom = frappe.qb.DocType("BOM")
 
-	(frappe.qb
-		.update(bom)
+	(
+		frappe.qb.update(bom)
 		.set(bom.transfer_material_against, "Work Order")
 		.where(bom.with_operations == 0)
 	).run()
diff --git a/erpnext/patches/v13_0/update_shipment_status.py b/erpnext/patches/v13_0/update_shipment_status.py
index f2d7d1d..d21caf7 100644
--- a/erpnext/patches/v13_0/update_shipment_status.py
+++ b/erpnext/patches/v13_0/update_shipment_status.py
@@ -5,11 +5,15 @@
 	frappe.reload_doc("stock", "doctype", "shipment")
 
 	# update submitted status
-	frappe.db.sql("""UPDATE `tabShipment`
+	frappe.db.sql(
+		"""UPDATE `tabShipment`
 					SET status = "Submitted"
-					WHERE status = "Draft" AND docstatus = 1""")
+					WHERE status = "Draft" AND docstatus = 1"""
+	)
 
 	# update cancelled status
-	frappe.db.sql("""UPDATE `tabShipment`
+	frappe.db.sql(
+		"""UPDATE `tabShipment`
 					SET status = "Cancelled"
-					WHERE status = "Draft" AND docstatus = 2""")
+					WHERE status = "Draft" AND docstatus = 2"""
+	)
diff --git a/erpnext/patches/v13_0/update_sla_enhancements.py b/erpnext/patches/v13_0/update_sla_enhancements.py
index 7f61020..84c683a 100644
--- a/erpnext/patches/v13_0/update_sla_enhancements.py
+++ b/erpnext/patches/v13_0/update_sla_enhancements.py
@@ -8,78 +8,94 @@
 def execute():
 	# add holiday list and employee group fields in SLA
 	# change response and resolution time in priorities child table
-	if frappe.db.exists('DocType', 'Service Level Agreement'):
-		sla_details = frappe.db.get_all('Service Level Agreement', fields=['name', 'service_level'])
-		priorities = frappe.db.get_all('Service Level Priority', fields=['*'], filters={
-			'parenttype': ('in', ['Service Level Agreement', 'Service Level'])
-		})
+	if frappe.db.exists("DocType", "Service Level Agreement"):
+		sla_details = frappe.db.get_all("Service Level Agreement", fields=["name", "service_level"])
+		priorities = frappe.db.get_all(
+			"Service Level Priority",
+			fields=["*"],
+			filters={"parenttype": ("in", ["Service Level Agreement", "Service Level"])},
+		)
 
-		frappe.reload_doc('support', 'doctype', 'service_level_agreement')
-		frappe.reload_doc('support', 'doctype', 'pause_sla_on_status')
-		frappe.reload_doc('support', 'doctype', 'service_level_priority')
-		frappe.reload_doc('support', 'doctype', 'service_day')
+		frappe.reload_doc("support", "doctype", "service_level_agreement")
+		frappe.reload_doc("support", "doctype", "pause_sla_on_status")
+		frappe.reload_doc("support", "doctype", "service_level_priority")
+		frappe.reload_doc("support", "doctype", "service_day")
 
 		for entry in sla_details:
-			values = frappe.db.get_value('Service Level', entry.service_level, ['holiday_list', 'employee_group'])
+			values = frappe.db.get_value(
+				"Service Level", entry.service_level, ["holiday_list", "employee_group"]
+			)
 			if values:
 				holiday_list = values[0]
 				employee_group = values[1]
-				frappe.db.set_value('Service Level Agreement', entry.name, {
-					'holiday_list': holiday_list,
-					'employee_group': employee_group
-				})
+				frappe.db.set_value(
+					"Service Level Agreement",
+					entry.name,
+					{"holiday_list": holiday_list, "employee_group": employee_group},
+				)
 
 		priority_dict = {}
 
 		for priority in priorities:
-			if priority.parenttype == 'Service Level Agreement':
+			if priority.parenttype == "Service Level Agreement":
 				response_time = convert_to_seconds(priority.response_time, priority.response_time_period)
 				resolution_time = convert_to_seconds(priority.resolution_time, priority.resolution_time_period)
-				frappe.db.set_value('Service Level Priority', priority.name, {
-					'response_time': response_time,
-					'resolution_time': resolution_time
-				})
-			if priority.parenttype == 'Service Level':
+				frappe.db.set_value(
+					"Service Level Priority",
+					priority.name,
+					{"response_time": response_time, "resolution_time": resolution_time},
+				)
+			if priority.parenttype == "Service Level":
 				if not priority.parent in priority_dict:
 					priority_dict[priority.parent] = []
 				priority_dict[priority.parent].append(priority)
 
-
 		# copy Service Levels to Service Level Agreements
 		sl = [entry.service_level for entry in sla_details]
-		if frappe.db.exists('DocType', 'Service Level'):
-			service_levels = frappe.db.get_all('Service Level', filters={'service_level': ('not in', sl)}, fields=['*'])
+		if frappe.db.exists("DocType", "Service Level"):
+			service_levels = frappe.db.get_all(
+				"Service Level", filters={"service_level": ("not in", sl)}, fields=["*"]
+			)
 			for entry in service_levels:
-				sla = frappe.new_doc('Service Level Agreement')
+				sla = frappe.new_doc("Service Level Agreement")
 				sla.service_level = entry.service_level
 				sla.holiday_list = entry.holiday_list
 				sla.employee_group = entry.employee_group
 				sla.flags.ignore_validate = True
 				sla = sla.insert(ignore_mandatory=True)
 
-				frappe.db.sql("""
+				frappe.db.sql(
+					"""
 					UPDATE
 						`tabService Day`
 					SET
 						parent = %(new_parent)s , parentfield = 'support_and_resolution', parenttype = 'Service Level Agreement'
 					WHERE
 						parent = %(old_parent)s
-				""", {'new_parent': sla.name, 'old_parent': entry.name}, as_dict = 1)
+				""",
+					{"new_parent": sla.name, "old_parent": entry.name},
+					as_dict=1,
+				)
 
 				priority_list = priority_dict.get(entry.name)
 				if priority_list:
-					sla = frappe.get_doc('Service Level Agreement', sla.name)
+					sla = frappe.get_doc("Service Level Agreement", sla.name)
 					for priority in priority_list:
-						row = sla.append('priorities', {
-							'priority': priority.priority,
-							'default_priority': priority.default_priority,
-							'response_time': convert_to_seconds(priority.response_time, priority.response_time_period),
-							'resolution_time': convert_to_seconds(priority.resolution_time, priority.resolution_time_period)
-						})
+						row = sla.append(
+							"priorities",
+							{
+								"priority": priority.priority,
+								"default_priority": priority.default_priority,
+								"response_time": convert_to_seconds(priority.response_time, priority.response_time_period),
+								"resolution_time": convert_to_seconds(
+									priority.resolution_time, priority.resolution_time_period
+								),
+							},
+						)
 						row.db_update()
 					sla.db_update()
 
-	frappe.delete_doc_if_exists('DocType', 'Service Level')
+	frappe.delete_doc_if_exists("DocType", "Service Level")
 
 
 def convert_to_seconds(value, unit):
diff --git a/erpnext/patches/v13_0/update_start_end_date_for_old_shift_assignment.py b/erpnext/patches/v13_0/update_start_end_date_for_old_shift_assignment.py
index 665cc39..6d26ac5 100644
--- a/erpnext/patches/v13_0/update_start_end_date_for_old_shift_assignment.py
+++ b/erpnext/patches/v13_0/update_start_end_date_for_old_shift_assignment.py
@@ -6,8 +6,10 @@
 
 
 def execute():
-    frappe.reload_doc('hr', 'doctype', 'shift_assignment')
-    if frappe.db.has_column('Shift Assignment', 'date'):
-        frappe.db.sql("""update `tabShift Assignment`
+	frappe.reload_doc("hr", "doctype", "shift_assignment")
+	if frappe.db.has_column("Shift Assignment", "date"):
+		frappe.db.sql(
+			"""update `tabShift Assignment`
             set end_date=date, start_date=date
-            where date IS NOT NULL and start_date IS NULL and end_date IS NULL;""")
+            where date IS NOT NULL and start_date IS NULL and end_date IS NULL;"""
+		)
diff --git a/erpnext/patches/v13_0/update_subscription.py b/erpnext/patches/v13_0/update_subscription.py
index b67c74d..66b3def 100644
--- a/erpnext/patches/v13_0/update_subscription.py
+++ b/erpnext/patches/v13_0/update_subscription.py
@@ -7,12 +7,13 @@
 
 def execute():
 
-	frappe.reload_doc('accounts', 'doctype', 'subscription')
-	frappe.reload_doc('accounts', 'doctype', 'subscription_invoice')
-	frappe.reload_doc('accounts', 'doctype', 'subscription_plan')
+	frappe.reload_doc("accounts", "doctype", "subscription")
+	frappe.reload_doc("accounts", "doctype", "subscription_invoice")
+	frappe.reload_doc("accounts", "doctype", "subscription_plan")
 
-	if frappe.db.has_column('Subscription', 'customer'):
-		frappe.db.sql("""
+	if frappe.db.has_column("Subscription", "customer"):
+		frappe.db.sql(
+			"""
 			UPDATE `tabSubscription`
 			SET
 				start_date = start,
@@ -20,22 +21,28 @@
 				party = customer,
 				sales_tax_template = tax_template
 			WHERE IFNULL(party,'') = ''
-		""")
+		"""
+		)
 
-	frappe.db.sql("""
+	frappe.db.sql(
+		"""
 		UPDATE `tabSubscription Invoice`
 		SET document_type = 'Sales Invoice'
 		WHERE IFNULL(document_type, '') = ''
-	""")
+	"""
+	)
 
 	price_determination_map = {
-		'Fixed rate': 'Fixed Rate',
-		'Based on price list': 'Based On Price List'
+		"Fixed rate": "Fixed Rate",
+		"Based on price list": "Based On Price List",
 	}
 
 	for key, value in price_determination_map.items():
-		frappe.db.sql("""
+		frappe.db.sql(
+			"""
 			UPDATE `tabSubscription Plan`
 			SET price_determination = %s
 			WHERE price_determination = %s
-		""", (value, key))
+		""",
+			(value, key),
+		)
diff --git a/erpnext/patches/v13_0/update_subscription_status_in_memberships.py b/erpnext/patches/v13_0/update_subscription_status_in_memberships.py
index e21fe57..d7c8499 100644
--- a/erpnext/patches/v13_0/update_subscription_status_in_memberships.py
+++ b/erpnext/patches/v13_0/update_subscription_status_in_memberships.py
@@ -2,9 +2,11 @@
 
 
 def execute():
-	if frappe.db.exists('DocType', 'Member'):
-		frappe.reload_doc('Non Profit', 'doctype', 'Member')
+	if frappe.db.exists("DocType", "Member"):
+		frappe.reload_doc("Non Profit", "doctype", "Member")
 
-		if frappe.db.has_column('Member', 'subscription_activated'):
-			frappe.db.sql('UPDATE `tabMember` SET subscription_status = "Active" WHERE subscription_activated = 1')
-			frappe.db.sql_ddl('ALTER table `tabMember` DROP COLUMN subscription_activated')
+		if frappe.db.has_column("Member", "subscription_activated"):
+			frappe.db.sql(
+				'UPDATE `tabMember` SET subscription_status = "Active" WHERE subscription_activated = 1'
+			)
+			frappe.db.sql_ddl("ALTER table `tabMember` DROP COLUMN subscription_activated")
diff --git a/erpnext/patches/v13_0/update_tds_check_field.py b/erpnext/patches/v13_0/update_tds_check_field.py
index 436d2e6..0505266 100644
--- a/erpnext/patches/v13_0/update_tds_check_field.py
+++ b/erpnext/patches/v13_0/update_tds_check_field.py
@@ -2,9 +2,12 @@
 
 
 def execute():
-	if frappe.db.has_table("Tax Withholding Category") \
-		and frappe.db.has_column("Tax Withholding Category", "round_off_tax_amount"):
-		frappe.db.sql("""
+	if frappe.db.has_table("Tax Withholding Category") and frappe.db.has_column(
+		"Tax Withholding Category", "round_off_tax_amount"
+	):
+		frappe.db.sql(
+			"""
 			UPDATE `tabTax Withholding Category` set round_off_tax_amount = 0
 			WHERE round_off_tax_amount IS NULL
-		""")
+		"""
+		)
diff --git a/erpnext/patches/v13_0/update_timesheet_changes.py b/erpnext/patches/v13_0/update_timesheet_changes.py
index a5e3391..02654c1 100644
--- a/erpnext/patches/v13_0/update_timesheet_changes.py
+++ b/erpnext/patches/v13_0/update_timesheet_changes.py
@@ -9,17 +9,23 @@
 	if frappe.db.has_column("Timesheet Detail", "billable"):
 		rename_field("Timesheet Detail", "billable", "is_billable")
 
-	base_currency = frappe.defaults.get_global_default('currency')
+	base_currency = frappe.defaults.get_global_default("currency")
 
-	frappe.db.sql("""UPDATE `tabTimesheet Detail`
+	frappe.db.sql(
+		"""UPDATE `tabTimesheet Detail`
 			SET base_billing_rate = billing_rate,
 			base_billing_amount = billing_amount,
 			base_costing_rate = costing_rate,
-			base_costing_amount = costing_amount""")
+			base_costing_amount = costing_amount"""
+	)
 
-	frappe.db.sql("""UPDATE `tabTimesheet`
+	frappe.db.sql(
+		"""UPDATE `tabTimesheet`
 			SET currency = '{0}',
 			exchange_rate = 1.0,
 			base_total_billable_amount = total_billable_amount,
 			base_total_billed_amount = total_billed_amount,
-			base_total_costing_amount = total_costing_amount""".format(base_currency))
+			base_total_costing_amount = total_costing_amount""".format(
+			base_currency
+		)
+	)
diff --git a/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py b/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py
index 902707b..326fc57 100644
--- a/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py
+++ b/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py
@@ -2,10 +2,10 @@
 
 
 def execute():
-	frappe.reload_doc('custom', 'doctype', 'custom_field', force=True)
-	company = frappe.get_all('Company', filters = {'country': 'India'})
+	frappe.reload_doc("custom", "doctype", "custom_field", force=True)
+	company = frappe.get_all("Company", filters={"country": "India"})
 	if not company:
 		return
 
-	if frappe.db.exists('Custom Field', { 'fieldname': 'vehicle_no' }):
-		frappe.db.set_value('Custom Field', { 'fieldname': 'vehicle_no' }, 'mandatory_depends_on', '')
+	if frappe.db.exists("Custom Field", {"fieldname": "vehicle_no"}):
+		frappe.db.set_value("Custom Field", {"fieldname": "vehicle_no"}, "mandatory_depends_on", "")
diff --git a/erpnext/patches/v13_0/updates_for_multi_currency_payroll.py b/erpnext/patches/v13_0/updates_for_multi_currency_payroll.py
index c760a6a..b395c01c 100644
--- a/erpnext/patches/v13_0/updates_for_multi_currency_payroll.py
+++ b/erpnext/patches/v13_0/updates_for_multi_currency_payroll.py
@@ -8,122 +8,116 @@
 
 def execute():
 
-	frappe.reload_doc('Accounts', 'doctype', 'Salary Component Account')
-	if frappe.db.has_column('Salary Component Account', 'default_account'):
+	frappe.reload_doc("Accounts", "doctype", "Salary Component Account")
+	if frappe.db.has_column("Salary Component Account", "default_account"):
 		rename_field("Salary Component Account", "default_account", "account")
 
 	doctype_list = [
-		{
-		'module':'HR',
-		'doctype':'Employee Advance'
-		},
-		{
-		'module':'HR',
-		'doctype':'Leave Encashment'
-		},
-		{
-		'module':'Payroll',
-		'doctype':'Additional Salary'
-		},
-		{
-		'module':'Payroll',
-		'doctype':'Employee Benefit Application'
-		},
-		{
-		'module':'Payroll',
-		'doctype':'Employee Benefit Claim'
-		},
-		{
-		'module':'Payroll',
-		'doctype':'Employee Incentive'
-		},
-		{
-		'module':'Payroll',
-		'doctype':'Employee Tax Exemption Declaration'
-		},
-		{
-		'module':'Payroll',
-		'doctype':'Employee Tax Exemption Proof Submission'
-		},
-		{
-		'module':'Payroll',
-		'doctype':'Income Tax Slab'
-		},
-		{
-		'module':'Payroll',
-		'doctype':'Payroll Entry'
-		},
-		{
-		'module':'Payroll',
-		'doctype':'Retention Bonus'
-		},
-		{
-		'module':'Payroll',
-		'doctype':'Salary Structure'
-		},
-		{
-		'module':'Payroll',
-		'doctype':'Salary Structure Assignment'
-		},
-		{
-		'module':'Payroll',
-		'doctype':'Salary Slip'
-		},
+		{"module": "HR", "doctype": "Employee Advance"},
+		{"module": "HR", "doctype": "Leave Encashment"},
+		{"module": "Payroll", "doctype": "Additional Salary"},
+		{"module": "Payroll", "doctype": "Employee Benefit Application"},
+		{"module": "Payroll", "doctype": "Employee Benefit Claim"},
+		{"module": "Payroll", "doctype": "Employee Incentive"},
+		{"module": "Payroll", "doctype": "Employee Tax Exemption Declaration"},
+		{"module": "Payroll", "doctype": "Employee Tax Exemption Proof Submission"},
+		{"module": "Payroll", "doctype": "Income Tax Slab"},
+		{"module": "Payroll", "doctype": "Payroll Entry"},
+		{"module": "Payroll", "doctype": "Retention Bonus"},
+		{"module": "Payroll", "doctype": "Salary Structure"},
+		{"module": "Payroll", "doctype": "Salary Structure Assignment"},
+		{"module": "Payroll", "doctype": "Salary Slip"},
 	]
 
 	for item in doctype_list:
-		frappe.reload_doc(item['module'], 'doctype', item['doctype'])
+		frappe.reload_doc(item["module"], "doctype", item["doctype"])
 
 	# update company in employee advance based on employee company
-	for dt in ['Employee Incentive', 'Leave Encashment', 'Employee Benefit Application', 'Employee Benefit Claim']:
-		frappe.db.sql("""
+	for dt in [
+		"Employee Incentive",
+		"Leave Encashment",
+		"Employee Benefit Application",
+		"Employee Benefit Claim",
+	]:
+		frappe.db.sql(
+			"""
 			update `tab{doctype}`
 			set company = (select company from tabEmployee where name=`tab{doctype}`.employee)
-		""".format(doctype=dt))
+		""".format(
+				doctype=dt
+			)
+		)
 
 	# update exchange rate for employee advance
 	frappe.db.sql("update `tabEmployee Advance` set exchange_rate=1")
 
 	# get all companies and it's currency
-	all_companies = frappe.db.get_all("Company", fields=["name", "default_currency", "default_payroll_payable_account"])
+	all_companies = frappe.db.get_all(
+		"Company", fields=["name", "default_currency", "default_payroll_payable_account"]
+	)
 	for d in all_companies:
 		company = d.name
 		company_currency = d.default_currency
 		default_payroll_payable_account = d.default_payroll_payable_account
 
 		if not default_payroll_payable_account:
-			default_payroll_payable_account = frappe.db.get_value("Account",
-				{"account_name": _("Payroll Payable"), "company": company, "account_currency": company_currency, "is_group": 0})
+			default_payroll_payable_account = frappe.db.get_value(
+				"Account",
+				{
+					"account_name": _("Payroll Payable"),
+					"company": company,
+					"account_currency": company_currency,
+					"is_group": 0,
+				},
+			)
 
 		# update currency in following doctypes based on company currency
-		doctypes_for_currency = ['Employee Advance', 'Leave Encashment', 'Employee Benefit Application',
-			'Employee Benefit Claim', 'Employee Incentive', 'Additional Salary',
-			'Employee Tax Exemption Declaration', 'Employee Tax Exemption Proof Submission',
-			'Income Tax Slab', 'Retention Bonus', 'Salary Structure']
+		doctypes_for_currency = [
+			"Employee Advance",
+			"Leave Encashment",
+			"Employee Benefit Application",
+			"Employee Benefit Claim",
+			"Employee Incentive",
+			"Additional Salary",
+			"Employee Tax Exemption Declaration",
+			"Employee Tax Exemption Proof Submission",
+			"Income Tax Slab",
+			"Retention Bonus",
+			"Salary Structure",
+		]
 
 		for dt in doctypes_for_currency:
-			frappe.db.sql("""update `tab{doctype}` set currency = %s where company=%s"""
-				.format(doctype=dt), (company_currency, company))
+			frappe.db.sql(
+				"""update `tab{doctype}` set currency = %s where company=%s""".format(doctype=dt),
+				(company_currency, company),
+			)
 
 		# update fields in payroll entry
-		frappe.db.sql("""
+		frappe.db.sql(
+			"""
 			update `tabPayroll Entry`
 			set currency = %s,
 				exchange_rate = 1,
 				payroll_payable_account=%s
 			where company=%s
-		""", (company_currency, default_payroll_payable_account, company))
+		""",
+			(company_currency, default_payroll_payable_account, company),
+		)
 
 		# update fields in Salary Structure Assignment
-		frappe.db.sql("""
+		frappe.db.sql(
+			"""
 			update `tabSalary Structure Assignment`
 			set currency = %s,
 				payroll_payable_account=%s
 			where company=%s
-		""", (company_currency, default_payroll_payable_account, company))
+		""",
+			(company_currency, default_payroll_payable_account, company),
+		)
 
 		# update fields in Salary Slip
-		frappe.db.sql("""
+		frappe.db.sql(
+			"""
 			update `tabSalary Slip`
 			set currency = %s,
 				exchange_rate = 1,
@@ -134,4 +128,6 @@
 				base_rounded_total = rounded_total,
 				base_total_in_words = total_in_words
 			where company=%s
-		""", (company_currency, company))
+		""",
+			(company_currency, company),
+		)
diff --git a/erpnext/patches/v13_0/wipe_serial_no_field_for_0_qty.py b/erpnext/patches/v13_0/wipe_serial_no_field_for_0_qty.py
index e43a8ba..693d06d 100644
--- a/erpnext/patches/v13_0/wipe_serial_no_field_for_0_qty.py
+++ b/erpnext/patches/v13_0/wipe_serial_no_field_for_0_qty.py
@@ -11,8 +11,6 @@
 
 	sr_item = frappe.qb.DocType(doctype)
 
-	(frappe.qb
-		.update(sr_item)
-		.set(sr_item.current_serial_no, None)
-		.where(sr_item.current_qty == 0)
+	(
+		frappe.qb.update(sr_item).set(sr_item.current_serial_no, None).where(sr_item.current_qty == 0)
 	).run()
diff --git a/erpnext/patches/v14_0/add_default_exit_questionnaire_notification_template.py b/erpnext/patches/v14_0/add_default_exit_questionnaire_notification_template.py
index 2a8b6ef..001c8ef 100644
--- a/erpnext/patches/v14_0/add_default_exit_questionnaire_notification_template.py
+++ b/erpnext/patches/v14_0/add_default_exit_questionnaire_notification_template.py
@@ -8,15 +8,19 @@
 	template = frappe.db.exists("Email Template", _("Exit Questionnaire Notification"))
 	if not template:
 		base_path = frappe.get_app_path("erpnext", "hr", "doctype")
-		response = frappe.read_file(os.path.join(base_path, "exit_interview/exit_questionnaire_notification_template.html"))
+		response = frappe.read_file(
+			os.path.join(base_path, "exit_interview/exit_questionnaire_notification_template.html")
+		)
 
-		template = frappe.get_doc({
-			"doctype": "Email Template",
-			"name": _("Exit Questionnaire Notification"),
-			"response": response,
-			"subject": _("Exit Questionnaire Notification"),
-			"owner": frappe.session.user,
-		}).insert(ignore_permissions=True)
+		template = frappe.get_doc(
+			{
+				"doctype": "Email Template",
+				"name": _("Exit Questionnaire Notification"),
+				"response": response,
+				"subject": _("Exit Questionnaire Notification"),
+				"owner": frappe.session.user,
+			}
+		).insert(ignore_permissions=True)
 		template = template.name
 
 	hr_settings = frappe.get_doc("HR Settings")
diff --git a/erpnext/patches/v14_0/change_is_subcontracted_fieldtype.py b/erpnext/patches/v14_0/change_is_subcontracted_fieldtype.py
new file mode 100644
index 0000000..9b07ba8
--- /dev/null
+++ b/erpnext/patches/v14_0/change_is_subcontracted_fieldtype.py
@@ -0,0 +1,26 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+
+
+def execute():
+	for doctype in ["Purchase Order", "Purchase Receipt", "Purchase Invoice", "Supplier Quotation"]:
+		frappe.db.sql(
+			"""
+				UPDATE `tab{doctype}`
+				SET is_subcontracted = 0
+				where is_subcontracted in ('', 'No') or is_subcontracted is null""".format(
+				doctype=doctype
+			)
+		)
+		frappe.db.sql(
+			"""
+				UPDATE `tab{doctype}`
+				SET is_subcontracted = 1
+				where is_subcontracted = 'Yes'""".format(
+				doctype=doctype
+			)
+		)
+
+		frappe.reload_doc(frappe.get_meta(doctype).module, "doctype", frappe.scrub(doctype))
diff --git a/erpnext/patches/v14_0/delete_agriculture_doctypes.py b/erpnext/patches/v14_0/delete_agriculture_doctypes.py
index d7fe832..8ec0c33 100644
--- a/erpnext/patches/v14_0/delete_agriculture_doctypes.py
+++ b/erpnext/patches/v14_0/delete_agriculture_doctypes.py
@@ -2,18 +2,25 @@
 
 
 def execute():
+	if "agriculture" in frappe.get_installed_apps():
+		return
+
 	frappe.delete_doc("Module Def", "Agriculture", ignore_missing=True, force=True)
 
 	frappe.delete_doc("Workspace", "Agriculture", ignore_missing=True, force=True)
 
-	reports = frappe.get_all("Report", {"module": "agriculture", "is_standard": "Yes"}, pluck='name')
+	reports = frappe.get_all("Report", {"module": "agriculture", "is_standard": "Yes"}, pluck="name")
 	for report in reports:
 		frappe.delete_doc("Report", report, ignore_missing=True, force=True)
 
-	dashboards = frappe.get_all("Dashboard", {"module": "agriculture", "is_standard": 1}, pluck='name')
+	dashboards = frappe.get_all(
+		"Dashboard", {"module": "agriculture", "is_standard": 1}, pluck="name"
+	)
 	for dashboard in dashboards:
 		frappe.delete_doc("Dashboard", dashboard, ignore_missing=True, force=True)
 
-	doctypes = frappe.get_all("DocType", {"module": "agriculture", "custom": 0}, pluck='name')
+	doctypes = frappe.get_all("DocType", {"module": "agriculture", "custom": 0}, pluck="name")
 	for doctype in doctypes:
 		frappe.delete_doc("DocType", doctype, ignore_missing=True)
+
+	frappe.delete_doc("Module Def", "Agriculture", ignore_missing=True, force=True)
diff --git a/erpnext/patches/v14_0/delete_amazon_mws_doctype.py b/erpnext/patches/v14_0/delete_amazon_mws_doctype.py
index 525da6c..636e743 100644
--- a/erpnext/patches/v14_0/delete_amazon_mws_doctype.py
+++ b/erpnext/patches/v14_0/delete_amazon_mws_doctype.py
@@ -2,4 +2,4 @@
 
 
 def execute():
-	frappe.delete_doc("DocType", "Amazon MWS Settings", ignore_missing=True)
\ No newline at end of file
+	frappe.delete_doc("DocType", "Amazon MWS Settings", ignore_missing=True)
diff --git a/erpnext/patches/v14_0/delete_datev_doctypes.py b/erpnext/patches/v14_0/delete_datev_doctypes.py
new file mode 100644
index 0000000..a5de91f
--- /dev/null
+++ b/erpnext/patches/v14_0/delete_datev_doctypes.py
@@ -0,0 +1,13 @@
+import frappe
+
+
+def execute():
+	install_apps = frappe.get_installed_apps()
+	if "erpnext_datev_uo" in install_apps or "erpnext_datev" in install_apps:
+		return
+
+	# doctypes
+	frappe.delete_doc("DocType", "DATEV Settings", ignore_missing=True, force=True)
+
+	# reports
+	frappe.delete_doc("Report", "DATEV", ignore_missing=True, force=True)
diff --git a/erpnext/patches/v14_0/delete_education_doctypes.py b/erpnext/patches/v14_0/delete_education_doctypes.py
new file mode 100644
index 0000000..76b2300
--- /dev/null
+++ b/erpnext/patches/v14_0/delete_education_doctypes.py
@@ -0,0 +1,55 @@
+import click
+import frappe
+
+
+def execute():
+	if "education" in frappe.get_installed_apps():
+		return
+
+	frappe.delete_doc("Workspace", "Education", ignore_missing=True, force=True)
+
+	pages = frappe.get_all("Page", {"module": "education"}, pluck="name")
+	for page in pages:
+		frappe.delete_doc("Page", page, ignore_missing=True, force=True)
+
+	reports = frappe.get_all("Report", {"module": "education", "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": "education", "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": "education", "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": "education", "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": "education", "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": "education", "is_standard": 1}, pluck="name")
+	for card in cards:
+		frappe.delete_doc("Number Card", card, ignore_missing=True, force=True)
+
+	doctypes = frappe.get_all("DocType", {"module": "education", "custom": 0}, pluck="name")
+	for doctype in doctypes:
+		frappe.delete_doc("DocType", doctype, ignore_missing=True)
+
+	frappe.delete_doc("Module Def", "Education", ignore_missing=True, force=True)
+
+	click.secho(
+		"Education Module is moved to a separate app"
+		"Please install the app to continue using the module: https://github.com/frappe/education",
+		fg="yellow",
+	)
diff --git a/erpnext/patches/v14_0/delete_employee_transfer_property_doctype.py b/erpnext/patches/v14_0/delete_employee_transfer_property_doctype.py
new file mode 100644
index 0000000..b50e010
--- /dev/null
+++ b/erpnext/patches/v14_0/delete_employee_transfer_property_doctype.py
@@ -0,0 +1,5 @@
+import frappe
+
+
+def execute():
+	frappe.delete_doc("DocType", "Employee Transfer Property", ignore_missing=True)
diff --git a/erpnext/patches/v14_0/delete_healthcare_doctypes.py b/erpnext/patches/v14_0/delete_healthcare_doctypes.py
index 3a4f8f5..2c699e4 100644
--- a/erpnext/patches/v14_0/delete_healthcare_doctypes.py
+++ b/erpnext/patches/v14_0/delete_healthcare_doctypes.py
@@ -7,58 +7,59 @@
 
 	frappe.delete_doc("Workspace", "Healthcare", ignore_missing=True, force=True)
 
-	pages = frappe.get_all("Page", {"module": "healthcare"}, pluck='name')
+	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')
+	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')
+	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')
+	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')
+	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')
+	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')
+	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')
+	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')
+	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)
 
 	custom_fields = {
-		'Sales Invoice': ['patient', 'patient_name', 'ref_practitioner'],
-		'Sales Invoice Item': ['reference_dt', 'reference_dn'],
-		'Stock Entry': ['inpatient_medication_entry'],
-		'Stock Entry Detail': ['patient', 'inpatient_medication_entry_child'],
+		"Sales Invoice": ["patient", "patient_name", "ref_practitioner"],
+		"Sales Invoice Item": ["reference_dt", "reference_dn"],
+		"Stock Entry": ["inpatient_medication_entry"],
+		"Stock Entry Detail": ["patient", "inpatient_medication_entry_child"],
 	}
 	for doc, fields in custom_fields.items():
-		filters = {
-			'dt': doc,
-			'fieldname': ['in', fields]
-		}
-		records = frappe.get_all('Custom Field', filters=filters, pluck='name')
+		filters = {"dt": doc, "fieldname": ["in", fields]}
+		records = frappe.get_all("Custom Field", filters=filters, pluck="name")
 		for record in records:
-			frappe.delete_doc('Custom Field', record, ignore_missing=True, force=True)
+			frappe.delete_doc("Custom Field", record, ignore_missing=True, force=True)
diff --git a/erpnext/patches/v14_0/delete_hospitality_doctypes.py b/erpnext/patches/v14_0/delete_hospitality_doctypes.py
index d0216f8..8fa3fe0 100644
--- a/erpnext/patches/v14_0/delete_hospitality_doctypes.py
+++ b/erpnext/patches/v14_0/delete_hospitality_doctypes.py
@@ -2,22 +2,22 @@
 
 
 def execute():
-	modules = ['Hotels', 'Restaurant']
+	modules = ["Hotels", "Restaurant"]
 
 	for module in modules:
 		frappe.delete_doc("Module Def", module, ignore_missing=True, force=True)
 
 		frappe.delete_doc("Workspace", module, ignore_missing=True, force=True)
 
-		reports = frappe.get_all("Report", {"module": module, "is_standard": "Yes"}, pluck='name')
+		reports = frappe.get_all("Report", {"module": module, "is_standard": "Yes"}, pluck="name")
 		for report in reports:
 			frappe.delete_doc("Report", report, ignore_missing=True, force=True)
 
-		dashboards = frappe.get_all("Dashboard", {"module": module, "is_standard": 1}, pluck='name')
+		dashboards = frappe.get_all("Dashboard", {"module": module, "is_standard": 1}, pluck="name")
 		for dashboard in dashboards:
 			frappe.delete_doc("Dashboard", dashboard, ignore_missing=True, force=True)
 
-		doctypes = frappe.get_all("DocType", {"module": module, "custom": 0}, pluck='name')
+		doctypes = frappe.get_all("DocType", {"module": module, "custom": 0}, pluck="name")
 		for doctype in doctypes:
 			frappe.delete_doc("DocType", doctype, ignore_missing=True)
 
diff --git a/erpnext/patches/v14_0/delete_hub_doctypes.py b/erpnext/patches/v14_0/delete_hub_doctypes.py
index d1e9e31..08f4445 100644
--- a/erpnext/patches/v14_0/delete_hub_doctypes.py
+++ b/erpnext/patches/v14_0/delete_hub_doctypes.py
@@ -3,7 +3,7 @@
 
 def execute():
 
-	doctypes = frappe.get_all("DocType", {"module": "Hub Node", "custom": 0}, pluck='name')
+	doctypes = frappe.get_all("DocType", {"module": "Hub Node", "custom": 0}, pluck="name")
 	for doctype in doctypes:
 		frappe.delete_doc("DocType", doctype, ignore_missing=True)
 
diff --git a/erpnext/patches/v14_0/delete_non_profit_doctypes.py b/erpnext/patches/v14_0/delete_non_profit_doctypes.py
index 565b10c..e9ea8b1 100644
--- a/erpnext/patches/v14_0/delete_non_profit_doctypes.py
+++ b/erpnext/patches/v14_0/delete_non_profit_doctypes.py
@@ -6,31 +6,33 @@
 
 	frappe.delete_doc("Workspace", "Non Profit", ignore_missing=True, force=True)
 
-	print_formats = frappe.get_all("Print Format", {"module": "Non Profit", "standard": "Yes"}, pluck='name')
+	print_formats = frappe.get_all(
+		"Print Format", {"module": "Non Profit", "standard": "Yes"}, pluck="name"
+	)
 	for print_format in print_formats:
 		frappe.delete_doc("Print Format", print_format, ignore_missing=True, force=True)
 
-	print_formats = ['80G Certificate for Membership', '80G Certificate for Donation']
+	print_formats = ["80G Certificate for Membership", "80G Certificate for Donation"]
 	for print_format in print_formats:
 		frappe.delete_doc("Print Format", print_format, ignore_missing=True, force=True)
 
-	reports = frappe.get_all("Report", {"module": "Non Profit", "is_standard": "Yes"}, pluck='name')
+	reports = frappe.get_all("Report", {"module": "Non Profit", "is_standard": "Yes"}, pluck="name")
 	for report in reports:
 		frappe.delete_doc("Report", report, ignore_missing=True, force=True)
 
-	dashboards = frappe.get_all("Dashboard", {"module": "Non Profit", "is_standard": 1}, pluck='name')
+	dashboards = frappe.get_all("Dashboard", {"module": "Non Profit", "is_standard": 1}, pluck="name")
 	for dashboard in dashboards:
 		frappe.delete_doc("Dashboard", dashboard, ignore_missing=True, force=True)
 
-	doctypes = frappe.get_all("DocType", {"module": "Non Profit", "custom": 0}, pluck='name')
+	doctypes = frappe.get_all("DocType", {"module": "Non Profit", "custom": 0}, pluck="name")
 	for doctype in doctypes:
 		frappe.delete_doc("DocType", doctype, ignore_missing=True)
 
-	doctypes = ['Tax Exemption 80G Certificate', 'Tax Exemption 80G Certificate Detail']
+	doctypes = ["Tax Exemption 80G Certificate", "Tax Exemption 80G Certificate Detail"]
 	for doctype in doctypes:
 		frappe.delete_doc("DocType", doctype, ignore_missing=True)
 
-	forms = ['grant-application', 'certification-application', 'certification-application-usd']
+	forms = ["grant-application", "certification-application", "certification-application-usd"]
 	for form in forms:
 		frappe.delete_doc("Web Form", form, ignore_missing=True, force=True)
 
@@ -40,24 +42,24 @@
 	]
 	for record in custom_records:
 		try:
-			frappe.delete_doc(record['doctype'], record['name'], ignore_missing=True)
+			frappe.delete_doc(record["doctype"], record["name"], ignore_missing=True)
 		except frappe.LinkExistsError:
 			pass
 
 	custom_fields = {
-		'Member': ['pan_number'],
-		'Donor': ['pan_number'],
-		'Company': [
-			'non_profit_section', 'company_80g_number', 'with_effect_from',
-			'non_profit_column_break', 'pan_details'
+		"Member": ["pan_number"],
+		"Donor": ["pan_number"],
+		"Company": [
+			"non_profit_section",
+			"company_80g_number",
+			"with_effect_from",
+			"non_profit_column_break",
+			"pan_details",
 		],
 	}
 
 	for doc, fields in custom_fields.items():
-		filters = {
-			'dt': doc,
-			'fieldname': ['in', fields]
-		}
-		records = frappe.get_all('Custom Field', filters=filters, pluck='name')
+		filters = {"dt": doc, "fieldname": ["in", fields]}
+		records = frappe.get_all("Custom Field", filters=filters, pluck="name")
 		for record in records:
-			frappe.delete_doc('Custom Field', record, ignore_missing=True, force=True)
+			frappe.delete_doc("Custom Field", record, ignore_missing=True, force=True)
diff --git a/erpnext/patches/v14_0/discount_accounting_separation.py b/erpnext/patches/v14_0/discount_accounting_separation.py
new file mode 100644
index 0000000..0d1349a
--- /dev/null
+++ b/erpnext/patches/v14_0/discount_accounting_separation.py
@@ -0,0 +1,11 @@
+import frappe
+
+
+def execute():
+	data = frappe.db.sql(
+		'select value from tabSingles where doctype="Accounts Settings" and field="enable_discount_accounting"'
+	)
+	discount_account = data and int(data[0][0]) or 0
+	if discount_account:
+		for doctype in ["Buying Settings", "Selling Settings"]:
+			frappe.db.set_value(doctype, doctype, "enable_discount_accounting", 1, update_modified=False)
diff --git a/erpnext/patches/v14_0/migrate_cost_center_allocations.py b/erpnext/patches/v14_0/migrate_cost_center_allocations.py
index c4f097f..3bd2693 100644
--- a/erpnext/patches/v14_0/migrate_cost_center_allocations.py
+++ b/erpnext/patches/v14_0/migrate_cost_center_allocations.py
@@ -4,13 +4,14 @@
 
 def execute():
 	for dt in ("cost_center_allocation", "cost_center_allocation_percentage"):
-		frappe.reload_doc('accounts', 'doctype', dt)
+		frappe.reload_doc("accounts", "doctype", dt)
 
 	cc_allocations = get_existing_cost_center_allocations()
 	if cc_allocations:
 		create_new_cost_center_allocation_records(cc_allocations)
 
-	frappe.delete_doc('DocType', 'Distributed Cost Center', ignore_missing=True)
+	frappe.delete_doc("DocType", "Distributed Cost Center", ignore_missing=True)
+
 
 def create_new_cost_center_allocation_records(cc_allocations):
 	for main_cc, allocations in cc_allocations.items():
@@ -19,13 +20,11 @@
 		cca.valid_from = today()
 
 		for child_cc, percentage in allocations.items():
-			cca.append("allocation_percentages", ({
-				"cost_center": child_cc,
-				"percentage": percentage
-			}))
+			cca.append("allocation_percentages", ({"cost_center": child_cc, "percentage": percentage}))
 		cca.save()
 		cca.submit()
 
+
 def get_existing_cost_center_allocations():
 	if not frappe.db.exists("DocType", "Distributed Cost Center"):
 		return
@@ -35,14 +34,16 @@
 
 	records = (
 		frappe.qb.from_(par)
-		.inner_join(child).on(par.name == child.parent)
+		.inner_join(child)
+		.on(par.name == child.parent)
 		.select(par.name, child.cost_center, child.percentage_allocation)
 		.where(par.enable_distributed_cost_center == 1)
 	).run(as_dict=True)
 
 	cc_allocations = frappe._dict()
 	for d in records:
-		cc_allocations.setdefault(d.name, frappe._dict())\
-			.setdefault(d.cost_center, d.percentage_allocation)
+		cc_allocations.setdefault(d.name, frappe._dict()).setdefault(
+			d.cost_center, d.percentage_allocation
+		)
 
-	return cc_allocations
\ No newline at end of file
+	return cc_allocations
diff --git a/erpnext/patches/v14_0/migrate_crm_settings.py b/erpnext/patches/v14_0/migrate_crm_settings.py
index 0c77853..696a100 100644
--- a/erpnext/patches/v14_0/migrate_crm_settings.py
+++ b/erpnext/patches/v14_0/migrate_crm_settings.py
@@ -2,16 +2,21 @@
 
 
 def execute():
-	settings = frappe.db.get_value('Selling Settings', 'Selling Settings', [
-		'campaign_naming_by',
-		'close_opportunity_after_days',
-		'default_valid_till'
-	], as_dict=True)
+	settings = frappe.db.get_value(
+		"Selling Settings",
+		"Selling Settings",
+		["campaign_naming_by", "close_opportunity_after_days", "default_valid_till"],
+		as_dict=True,
+	)
 
-	frappe.reload_doc('crm', 'doctype', 'crm_settings')
+	frappe.reload_doc("crm", "doctype", "crm_settings")
 	if settings:
-		frappe.db.set_value('CRM Settings', 'CRM Settings', {
-			'campaign_naming_by': settings.campaign_naming_by,
-			'close_opportunity_after_days': settings.close_opportunity_after_days,
-			'default_valid_till': settings.default_valid_till
-		})
+		frappe.db.set_value(
+			"CRM Settings",
+			"CRM Settings",
+			{
+				"campaign_naming_by": settings.campaign_naming_by,
+				"close_opportunity_after_days": settings.close_opportunity_after_days,
+				"default_valid_till": settings.default_valid_till,
+			},
+		)
diff --git a/erpnext/patches/v14_0/migrate_gl_to_payment_ledger.py b/erpnext/patches/v14_0/migrate_gl_to_payment_ledger.py
new file mode 100644
index 0000000..1e0d20d
--- /dev/null
+++ b/erpnext/patches/v14_0/migrate_gl_to_payment_ledger.py
@@ -0,0 +1,135 @@
+import frappe
+from frappe import qb
+from frappe.query_builder import Case
+from frappe.query_builder.custom import ConstantColumn
+from frappe.query_builder.functions import IfNull
+
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+	get_dimensions,
+	make_dimension_in_accounting_doctypes,
+)
+
+
+def create_accounting_dimension_fields():
+	dimensions_and_defaults = get_dimensions()
+	if dimensions_and_defaults:
+		for dimension in dimensions_and_defaults[0]:
+			make_dimension_in_accounting_doctypes(dimension, ["Payment Ledger Entry"])
+
+
+def generate_name_for_payment_ledger_entries(gl_entries):
+	for index, entry in enumerate(gl_entries, 1):
+		entry.name = index
+
+
+def get_columns():
+	columns = [
+		"name",
+		"creation",
+		"modified",
+		"modified_by",
+		"owner",
+		"docstatus",
+		"posting_date",
+		"account_type",
+		"account",
+		"party_type",
+		"party",
+		"voucher_type",
+		"voucher_no",
+		"against_voucher_type",
+		"against_voucher_no",
+		"amount",
+		"amount_in_account_currency",
+		"account_currency",
+		"company",
+		"cost_center",
+		"due_date",
+		"finance_book",
+	]
+
+	dimensions_and_defaults = get_dimensions()
+	if dimensions_and_defaults:
+		for dimension in dimensions_and_defaults[0]:
+			columns.append(dimension.fieldname)
+
+	return columns
+
+
+def build_insert_query():
+	ple = qb.DocType("Payment Ledger Entry")
+	columns = get_columns()
+	insert_query = qb.into(ple)
+
+	# build 'insert' columns in query
+	insert_query = insert_query.columns(tuple(columns))
+
+	return insert_query
+
+
+def insert_chunk_into_payment_ledger(insert_query, gl_entries):
+	if gl_entries:
+		columns = get_columns()
+
+		# build tuple of data with same column order
+		for entry in gl_entries:
+			data = ()
+			for column in columns:
+				data += (entry[column],)
+			insert_query = insert_query.insert(data)
+		insert_query.run()
+
+
+def execute():
+	if frappe.reload_doc("accounts", "doctype", "payment_ledger_entry"):
+		# create accounting dimension fields in Payment Ledger
+		create_accounting_dimension_fields()
+
+		gl = qb.DocType("GL Entry")
+		account = qb.DocType("Account")
+
+		gl_entries = (
+			qb.from_(gl)
+			.inner_join(account)
+			.on((gl.account == account.name) & (account.account_type.isin(["Receivable", "Payable"])))
+			.select(
+				gl.star,
+				ConstantColumn(1).as_("docstatus"),
+				account.account_type.as_("account_type"),
+				IfNull(gl.against_voucher_type, gl.voucher_type).as_("against_voucher_type"),
+				IfNull(gl.against_voucher, gl.voucher_no).as_("against_voucher_no"),
+				# convert debit/credit to amount
+				Case()
+				.when(account.account_type == "Receivable", gl.debit - gl.credit)
+				.else_(gl.credit - gl.debit)
+				.as_("amount"),
+				# convert debit/credit in account currency to amount in account currency
+				Case()
+				.when(
+					account.account_type == "Receivable",
+					gl.debit_in_account_currency - gl.credit_in_account_currency,
+				)
+				.else_(gl.credit_in_account_currency - gl.debit_in_account_currency)
+				.as_("amount_in_account_currency"),
+			)
+			.where(gl.is_cancelled == 0)
+			.orderby(gl.creation)
+			.run(as_dict=True)
+		)
+
+		# primary key(name) for payment ledger records
+		generate_name_for_payment_ledger_entries(gl_entries)
+
+		# split data into chunks
+		chunk_size = 1000
+		try:
+			for i in range(0, len(gl_entries), chunk_size):
+				insert_query = build_insert_query()
+				insert_chunk_into_payment_ledger(insert_query, gl_entries[i : i + chunk_size])
+				frappe.db.commit()
+		except Exception as err:
+			frappe.db.rollback()
+			ple = qb.DocType("Payment Ledger Entry")
+			qb.from_(ple).delete().where(ple.docstatus >= 0).run()
+			frappe.db.commit()
+			raise err
diff --git a/erpnext/patches/v14_0/rearrange_company_fields.py b/erpnext/patches/v14_0/rearrange_company_fields.py
index fd7eb7f..d188a0c 100644
--- a/erpnext/patches/v14_0/rearrange_company_fields.py
+++ b/erpnext/patches/v14_0/rearrange_company_fields.py
@@ -3,25 +3,64 @@
 
 def execute():
 	custom_fields = {
-		'Company': [
-			dict(fieldname='hra_section', label='HRA Settings',
-				fieldtype='Section Break', insert_after='asset_received_but_not_billed', collapsible=1),
-			dict(fieldname='basic_component', label='Basic Component',
-				fieldtype='Link', options='Salary Component', insert_after='hra_section'),
-			dict(fieldname='hra_component', label='HRA Component',
-				fieldtype='Link', options='Salary Component', insert_after='basic_component'),
-			dict(fieldname='hra_column_break', fieldtype='Column Break', insert_after='hra_component'),
-			dict(fieldname='arrear_component', label='Arrear Component',
-				fieldtype='Link', options='Salary Component', insert_after='hra_column_break'),
-			dict(fieldname='non_profit_section', label='Non Profit Settings',
-				fieldtype='Section Break', insert_after='arrear_component', collapsible=1),
-			dict(fieldname='company_80g_number', label='80G Number',
-				fieldtype='Data', insert_after='non_profit_section'),
-			dict(fieldname='with_effect_from', label='80G With Effect From',
-				fieldtype='Date', insert_after='company_80g_number'),
-			dict(fieldname='non_profit_column_break', fieldtype='Column Break', insert_after='with_effect_from'),
-			dict(fieldname='pan_details', label='PAN Number',
-				fieldtype='Data', insert_after='non_profit_column_break')
+		"Company": [
+			dict(
+				fieldname="hra_section",
+				label="HRA Settings",
+				fieldtype="Section Break",
+				insert_after="asset_received_but_not_billed",
+				collapsible=1,
+			),
+			dict(
+				fieldname="basic_component",
+				label="Basic Component",
+				fieldtype="Link",
+				options="Salary Component",
+				insert_after="hra_section",
+			),
+			dict(
+				fieldname="hra_component",
+				label="HRA Component",
+				fieldtype="Link",
+				options="Salary Component",
+				insert_after="basic_component",
+			),
+			dict(fieldname="hra_column_break", fieldtype="Column Break", insert_after="hra_component"),
+			dict(
+				fieldname="arrear_component",
+				label="Arrear Component",
+				fieldtype="Link",
+				options="Salary Component",
+				insert_after="hra_column_break",
+			),
+			dict(
+				fieldname="non_profit_section",
+				label="Non Profit Settings",
+				fieldtype="Section Break",
+				insert_after="arrear_component",
+				collapsible=1,
+			),
+			dict(
+				fieldname="company_80g_number",
+				label="80G Number",
+				fieldtype="Data",
+				insert_after="non_profit_section",
+			),
+			dict(
+				fieldname="with_effect_from",
+				label="80G With Effect From",
+				fieldtype="Date",
+				insert_after="company_80g_number",
+			),
+			dict(
+				fieldname="non_profit_column_break", fieldtype="Column Break", insert_after="with_effect_from"
+			),
+			dict(
+				fieldname="pan_details",
+				label="PAN Number",
+				fieldtype="Data",
+				insert_after="non_profit_column_break",
+			),
 		]
 	}
 
diff --git a/erpnext/patches/v14_0/rename_ongoing_status_in_sla_documents.py b/erpnext/patches/v14_0/rename_ongoing_status_in_sla_documents.py
index 1cc5f38..8ea96ab 100644
--- a/erpnext/patches/v14_0/rename_ongoing_status_in_sla_documents.py
+++ b/erpnext/patches/v14_0/rename_ongoing_status_in_sla_documents.py
@@ -2,26 +2,20 @@
 
 
 def execute():
-	active_sla_documents = [sla.document_type for sla in frappe.get_all("Service Level Agreement", fields=["document_type"])]
+	active_sla_documents = [
+		sla.document_type for sla in frappe.get_all("Service Level Agreement", fields=["document_type"])
+	]
 
 	for doctype in active_sla_documents:
 		doctype = frappe.qb.DocType(doctype)
 		try:
-			frappe.qb.update(
-				doctype
-			).set(
-				doctype.agreement_status, 'First Response Due'
-			).where(
+			frappe.qb.update(doctype).set(doctype.agreement_status, "First Response Due").where(
 				doctype.first_responded_on.isnull()
 			).run()
 
-			frappe.qb.update(
-				doctype
-			).set(
-				doctype.agreement_status, 'Resolution Due'
-			).where(
-				doctype.agreement_status == 'Ongoing'
+			frappe.qb.update(doctype).set(doctype.agreement_status, "Resolution Due").where(
+				doctype.agreement_status == "Ongoing"
 			).run()
 
 		except Exception:
-			frappe.log_error(title='Failed to Patch SLA Status')
\ No newline at end of file
+			frappe.log_error("Failed to Patch SLA Status")
diff --git a/erpnext/patches/v14_0/set_payroll_cost_centers.py b/erpnext/patches/v14_0/set_payroll_cost_centers.py
index 89b305b..0951e39 100644
--- a/erpnext/patches/v14_0/set_payroll_cost_centers.py
+++ b/erpnext/patches/v14_0/set_payroll_cost_centers.py
@@ -2,8 +2,8 @@
 
 
 def execute():
-	frappe.reload_doc('payroll', 'doctype', 'employee_cost_center')
-	frappe.reload_doc('payroll', 'doctype', 'salary_structure_assignment')
+	frappe.reload_doc("payroll", "doctype", "employee_cost_center")
+	frappe.reload_doc("payroll", "doctype", "salary_structure_assignment")
 
 	employees = frappe.get_all("Employee", fields=["department", "payroll_cost_center", "name"])
 
@@ -16,17 +16,14 @@
 		if cost_center:
 			employee_cost_center.setdefault(d.name, cost_center)
 
-	salary_structure_assignments = frappe.get_all("Salary Structure Assignment",
-		filters = {"docstatus": ["!=", 2]},
-		fields=["name", "employee"])
+	salary_structure_assignments = frappe.get_all(
+		"Salary Structure Assignment", filters={"docstatus": ["!=", 2]}, fields=["name", "employee"]
+	)
 
 	for d in salary_structure_assignments:
 		cost_center = employee_cost_center.get(d.employee)
 		if cost_center:
 			assignment = frappe.get_doc("Salary Structure Assignment", d.name)
 			if not assignment.get("payroll_cost_centers"):
-				assignment.append("payroll_cost_centers", {
-					"cost_center": cost_center,
-					"percentage": 100
-				})
-				assignment.save()
\ No newline at end of file
+				assignment.append("payroll_cost_centers", {"cost_center": cost_center, "percentage": 100})
+				assignment.save()
diff --git a/erpnext/patches/v14_0/update_employee_advance_status.py b/erpnext/patches/v14_0/update_employee_advance_status.py
deleted file mode 100644
index a20e35a..0000000
--- a/erpnext/patches/v14_0/update_employee_advance_status.py
+++ /dev/null
@@ -1,26 +0,0 @@
-import frappe
-
-
-def execute():
-	frappe.reload_doc('hr', 'doctype', 'employee_advance')
-
-	advance = frappe.qb.DocType('Employee Advance')
-	(frappe.qb
-		.update(advance)
-		.set(advance.status, 'Returned')
-		.where(
-			(advance.docstatus == 1)
-			& ((advance.return_amount) & (advance.paid_amount == advance.return_amount))
-			& (advance.status == 'Paid')
-		)
-	).run()
-
-	(frappe.qb
-		.update(advance)
-		.set(advance.status, 'Partly Claimed and Returned')
-		.where(
-			(advance.docstatus == 1)
-			& ((advance.claimed_amount & advance.return_amount) & (advance.paid_amount == (advance.return_amount + advance.claimed_amount)))
-			& (advance.status == 'Paid')
-		)
-	).run()
\ No newline at end of file
diff --git a/erpnext/patches/v14_0/update_leave_notification_template.py b/erpnext/patches/v14_0/update_leave_notification_template.py
index e744054..aec5f8b 100644
--- a/erpnext/patches/v14_0/update_leave_notification_template.py
+++ b/erpnext/patches/v14_0/update_leave_notification_template.py
@@ -6,7 +6,9 @@
 
 def execute():
 	base_path = frappe.get_app_path("erpnext", "hr", "doctype")
-	response = frappe.read_file(os.path.join(base_path, "leave_application/leave_application_email_template.html"))
+	response = frappe.read_file(
+		os.path.join(base_path, "leave_application/leave_application_email_template.html")
+	)
 
 	template = frappe.db.exists("Email Template", _("Leave Approval Notification"))
 	if template:
diff --git a/erpnext/patches/v14_0/update_opportunity_currency_fields.py b/erpnext/patches/v14_0/update_opportunity_currency_fields.py
index 75049a6..076de52 100644
--- a/erpnext/patches/v14_0/update_opportunity_currency_fields.py
+++ b/erpnext/patches/v14_0/update_opportunity_currency_fields.py
@@ -6,9 +6,12 @@
 
 
 def execute():
-	opportunities = frappe.db.get_list('Opportunity', filters={
-		'opportunity_amount': ['>', 0]
-	}, fields=['name', 'company', 'currency', 'opportunity_amount'])
+	frappe.reload_doctype("Opportunity")
+	opportunities = frappe.db.get_list(
+		"Opportunity",
+		filters={"opportunity_amount": [">", 0]},
+		fields=["name", "company", "currency", "opportunity_amount"],
+	)
 
 	for opportunity in opportunities:
 		company_currency = erpnext.get_company_currency(opportunity.company)
@@ -21,7 +24,9 @@
 			conversion_rate = 1
 			base_opportunity_amount = flt(opportunity.opportunity_amount)
 
-		frappe.db.set_value('Opportunity', opportunity.name, {
-			'conversion_rate': conversion_rate,
-			'base_opportunity_amount': base_opportunity_amount
-		}, update_modified=False)
+		frappe.db.set_value(
+			"Opportunity",
+			opportunity.name,
+			{"conversion_rate": conversion_rate, "base_opportunity_amount": base_opportunity_amount},
+			update_modified=False,
+		)
diff --git a/erpnext/patches/v4_2/repost_reserved_qty.py b/erpnext/patches/v4_2/repost_reserved_qty.py
index ed4b19d..72ac524 100644
--- a/erpnext/patches/v4_2/repost_reserved_qty.py
+++ b/erpnext/patches/v4_2/repost_reserved_qty.py
@@ -11,7 +11,8 @@
 	for doctype in ("Sales Order Item", "Bin"):
 		frappe.reload_doctype(doctype)
 
-	repost_for = frappe.db.sql("""
+	repost_for = frappe.db.sql(
+		"""
 		select
 			distinct item_code, warehouse
 		from
@@ -26,17 +27,18 @@
 			) so_item
 		where
 			exists(select name from tabItem where name=so_item.item_code and ifnull(is_stock_item, 0)=1)
-	""")
+	"""
+	)
 
 	for item_code, warehouse in repost_for:
 		if not (item_code and warehouse):
 			continue
-		update_bin_qty(item_code, warehouse, {
-			"reserved_qty": get_reserved_qty(item_code, warehouse)
-		})
+		update_bin_qty(item_code, warehouse, {"reserved_qty": get_reserved_qty(item_code, warehouse)})
 
-	frappe.db.sql("""delete from tabBin
+	frappe.db.sql(
+		"""delete from tabBin
 		where exists(
 			select name from tabItem where name=tabBin.item_code and ifnull(is_stock_item, 0) = 0
 		)
-	""")
+	"""
+	)
diff --git a/erpnext/patches/v4_2/update_requested_and_ordered_qty.py b/erpnext/patches/v4_2/update_requested_and_ordered_qty.py
index dd79410..8ebc649 100644
--- a/erpnext/patches/v4_2/update_requested_and_ordered_qty.py
+++ b/erpnext/patches/v4_2/update_requested_and_ordered_qty.py
@@ -8,20 +8,26 @@
 def execute():
 	from erpnext.stock.stock_balance import get_indented_qty, get_ordered_qty, update_bin_qty
 
-	count=0
-	for item_code, warehouse in frappe.db.sql("""select distinct item_code, warehouse from
+	count = 0
+	for item_code, warehouse in frappe.db.sql(
+		"""select distinct item_code, warehouse from
 		(select item_code, warehouse from tabBin
 		union
-		select item_code, warehouse from `tabStock Ledger Entry`) a"""):
-			try:
-				if not (item_code and warehouse):
-					continue
-				count += 1
-				update_bin_qty(item_code, warehouse, {
+		select item_code, warehouse from `tabStock Ledger Entry`) a"""
+	):
+		try:
+			if not (item_code and warehouse):
+				continue
+			count += 1
+			update_bin_qty(
+				item_code,
+				warehouse,
+				{
 					"indented_qty": get_indented_qty(item_code, warehouse),
-					"ordered_qty": get_ordered_qty(item_code, warehouse)
-				})
-				if count % 200 == 0:
-					frappe.db.commit()
-			except Exception:
-				frappe.db.rollback()
+					"ordered_qty": get_ordered_qty(item_code, warehouse),
+				},
+			)
+			if count % 200 == 0:
+				frappe.db.commit()
+		except Exception:
+			frappe.db.rollback()
diff --git a/erpnext/patches/v5_7/update_item_description_based_on_item_master.py b/erpnext/patches/v5_7/update_item_description_based_on_item_master.py
index c46187c..edb0eaa 100644
--- a/erpnext/patches/v5_7/update_item_description_based_on_item_master.py
+++ b/erpnext/patches/v5_7/update_item_description_based_on_item_master.py
@@ -2,12 +2,16 @@
 
 
 def execute():
-	name = frappe.db.sql(""" select name from `tabPatch Log` \
+	name = frappe.db.sql(
+		""" select name from `tabPatch Log` \
 		where \
-			patch like 'execute:frappe.db.sql("update `tabProduction Order` pro set description%' """)
+			patch like 'execute:frappe.db.sql("update `tabProduction Order` pro set description%' """
+	)
 	if not name:
-		frappe.db.sql("update `tabProduction Order` pro \
+		frappe.db.sql(
+			"update `tabProduction Order` pro \
 			set \
 				description = (select description from tabItem where name=pro.production_item) \
 			where \
-				ifnull(description, '') = ''")
+				ifnull(description, '') = ''"
+		)
diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py
index d618568..18bd3b7 100644
--- a/erpnext/payroll/doctype/additional_salary/additional_salary.py
+++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py
@@ -30,12 +30,17 @@
 			frappe.throw(_("Amount should not be less than zero"))
 
 	def validate_salary_structure(self):
-		if not frappe.db.exists('Salary Structure Assignment', {'employee': self.employee}):
-			frappe.throw(_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(self.employee))
+		if not frappe.db.exists("Salary Structure Assignment", {"employee": self.employee}):
+			frappe.throw(
+				_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(
+					self.employee
+				)
+			)
 
 	def validate_recurring_additional_salary_overlap(self):
 		if self.is_recurring:
-			additional_salaries = frappe.db.sql("""
+			additional_salaries = frappe.db.sql(
+				"""
 				SELECT
 					name
 				FROM `tabAdditional Salary`
@@ -47,22 +52,28 @@
 					AND salary_component = %s
 					AND to_date >= %s
 					AND from_date <= %s""",
-				(self.employee, self.name, self.salary_component, self.from_date, self.to_date), as_dict = 1)
+				(self.employee, self.name, self.salary_component, self.from_date, self.to_date),
+				as_dict=1,
+			)
 
 			additional_salaries = [salary.name for salary in additional_salaries]
 
 			if additional_salaries and len(additional_salaries):
-				frappe.throw(_("Additional Salary: {0} already exist for Salary Component: {1} for period {2} and {3}").format(
-					bold(comma_and(additional_salaries)),
-					bold(self.salary_component),
-					bold(formatdate(self.from_date)),
-					bold(formatdate(self.to_date)
-				)))
-
+				frappe.throw(
+					_(
+						"Additional Salary: {0} already exist for Salary Component: {1} for period {2} and {3}"
+					).format(
+						bold(comma_and(additional_salaries)),
+						bold(self.salary_component),
+						bold(formatdate(self.from_date)),
+						bold(formatdate(self.to_date)),
+					)
+				)
 
 	def validate_dates(self):
-		date_of_joining, relieving_date = frappe.db.get_value("Employee", self.employee,
-			["date_of_joining", "relieving_date"])
+		date_of_joining, relieving_date = frappe.db.get_value(
+			"Employee", self.employee, ["date_of_joining", "relieving_date"]
+		)
 
 		if getdate(self.from_date) > getdate(self.to_date):
 			frappe.throw(_("From Date can not be greater than To Date."))
@@ -81,19 +92,27 @@
 
 	def validate_employee_referral(self):
 		if self.ref_doctype == "Employee Referral":
-			referral_details = frappe.db.get_value("Employee Referral", self.ref_docname,
-				["is_applicable_for_referral_bonus", "status"], as_dict=1)
+			referral_details = frappe.db.get_value(
+				"Employee Referral",
+				self.ref_docname,
+				["is_applicable_for_referral_bonus", "status"],
+				as_dict=1,
+			)
 
 			if not referral_details.is_applicable_for_referral_bonus:
-				frappe.throw(_("Employee Referral {0} is not applicable for referral bonus.").format(
-					self.ref_docname))
+				frappe.throw(
+					_("Employee Referral {0} is not applicable for referral bonus.").format(self.ref_docname)
+				)
 
 			if self.type == "Deduction":
 				frappe.throw(_("Earning Salary Component is required for Employee Referral Bonus."))
 
 			if referral_details.status != "Accepted":
-				frappe.throw(_("Additional Salary for referral bonus can only be created against Employee Referral with status {0}").format(
-					frappe.bold("Accepted")))
+				frappe.throw(
+					_(
+						"Additional Salary for referral bonus can only be created against Employee Referral with status {0}"
+					).format(frappe.bold("Accepted"))
+				)
 
 	def update_return_amount_in_employee_advance(self):
 		if self.ref_doctype == "Employee Advance" and self.ref_docname:
@@ -125,28 +144,54 @@
 		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):
-	comp_type = 'Earning' if component_type == 'earnings' else 'Deduction'
+	from frappe.query_builder import Criterion
 
-	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')
+	comp_type = "Earning" if component_type == "earnings" else "Deduction"
 
-	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_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(
+			Criterion.any(
+				[
+					Criterion.all(
+						[  # is recurring and additional salary dates fall within the payroll period
+							additional_sal.is_recurring == 1,
+							additional_sal.from_date <= end_date,
+							additional_sal.to_date >= end_date,
+						]
+					),
+					Criterion.all(
+						[  # is not recurring and additional salary's payroll date falls within the payroll period
+							additional_sal.is_recurring == 0,
+							additional_sal.payroll_date[start_date:end_date],
+						]
+					),
+				]
+			)
+		)
+		.run(as_dict=True)
+	)
 
 	additional_salaries = []
 	components_to_overwrite = []
@@ -154,8 +199,12 @@
 	for d in additional_salary_list:
 		if d.overwrite:
 			if d.component in components_to_overwrite:
-				frappe.throw(_("Multiple Additional Salaries with overwrite property exist for Salary Component {0} between {1} and {2}.").format(
-					frappe.bold(d.component), start_date, end_date), title=_("Error"))
+				frappe.throw(
+					_(
+						"Multiple Additional Salaries with overwrite property exist for Salary Component {0} between {1} and {2}."
+					).format(frappe.bold(d.component), start_date, end_date),
+					title=_("Error"),
+				)
 
 			components_to_overwrite.append(d.component)
 
diff --git a/erpnext/payroll/doctype/additional_salary/test_additional_salary.py b/erpnext/payroll/doctype/additional_salary/test_additional_salary.py
index 84de912..bd73936 100644
--- a/erpnext/payroll/doctype/additional_salary/test_additional_salary.py
+++ b/erpnext/payroll/doctype/additional_salary/test_additional_salary.py
@@ -4,7 +4,8 @@
 import unittest
 
 import frappe
-from frappe.utils import add_days, nowdate
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import add_days, add_months, nowdate
 
 import erpnext
 from erpnext.hr.doctype.employee.test_employee import make_employee
@@ -16,40 +17,87 @@
 from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
 
 
-class TestAdditionalSalary(unittest.TestCase):
-
+class TestAdditionalSalary(FrappeTestCase):
 	def setUp(self):
 		setup_test()
 
-	def tearDown(self):
-		for dt in ["Salary Slip", "Additional Salary", "Salary Structure Assignment", "Salary Structure"]:
-			frappe.db.sql("delete from `tab%s`" % dt)
-
 	def test_recurring_additional_salary(self):
 		amount = 0
 		salary_component = None
 		emp_id = make_employee("test_additional@salary.com")
 		frappe.db.set_value("Employee", emp_id, "relieving_date", add_days(nowdate(), 1800))
-		salary_structure = make_salary_structure("Test Salary Structure Additional Salary", "Monthly", employee=emp_id)
+		salary_structure = make_salary_structure(
+			"Test Salary Structure Additional Salary", "Monthly", employee=emp_id
+		)
 		add_sal = get_additional_salary(emp_id)
 
-		ss = make_employee_salary_slip("test_additional@salary.com", "Monthly", salary_structure=salary_structure.name)
+		ss = make_employee_salary_slip(
+			"test_additional@salary.com", "Monthly", salary_structure=salary_structure.name
+		)
 		for earning in ss.earnings:
 			if earning.salary_component == "Recurring Salary Component":
 				amount = earning.amount
 				salary_component = earning.salary_component
+				break
 
 		self.assertEqual(amount, add_sal.amount)
 		self.assertEqual(salary_component, add_sal.salary_component)
 
-def get_additional_salary(emp_id):
+	def test_non_recurring_additional_salary(self):
+		amount = 0
+		salary_component = None
+		date = nowdate()
+
+		emp_id = make_employee("test_additional@salary.com")
+		frappe.db.set_value("Employee", emp_id, "relieving_date", add_days(date, 1800))
+		salary_structure = make_salary_structure(
+			"Test Salary Structure Additional Salary", "Monthly", employee=emp_id
+		)
+		add_sal = get_additional_salary(emp_id, recurring=False, payroll_date=date)
+
+		ss = make_employee_salary_slip(
+			"test_additional@salary.com", "Monthly", salary_structure=salary_structure.name
+		)
+
+		amount, salary_component = None, None
+		for earning in ss.earnings:
+			if earning.salary_component == "Recurring Salary Component":
+				amount = earning.amount
+				salary_component = earning.salary_component
+				break
+
+		self.assertEqual(amount, add_sal.amount)
+		self.assertEqual(salary_component, add_sal.salary_component)
+
+		# should not show up in next months
+		ss.posting_date = add_months(date, 1)
+		ss.start_date = ss.end_date = None
+		ss.earnings = []
+		ss.deductions = []
+		ss.save()
+
+		amount, salary_component = None, None
+		for earning in ss.earnings:
+			if earning.salary_component == "Recurring Salary Component":
+				amount = earning.amount
+				salary_component = earning.salary_component
+				break
+
+		self.assertIsNone(amount)
+		self.assertIsNone(salary_component)
+
+
+def get_additional_salary(emp_id, recurring=True, payroll_date=None):
 	create_salary_component("Recurring Salary Component")
 	add_sal = frappe.new_doc("Additional Salary")
 	add_sal.employee = emp_id
 	add_sal.salary_component = "Recurring Salary Component"
-	add_sal.is_recurring = 1
+
+	add_sal.is_recurring = 1 if recurring else 0
 	add_sal.from_date = add_days(nowdate(), -50)
 	add_sal.to_date = add_days(nowdate(), 180)
+	add_sal.payroll_date = payroll_date
+
 	add_sal.amount = 5000
 	add_sal.currency = erpnext.get_default_currency()
 	add_sal.save()
diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py
index eda5015..8df1bb6 100644
--- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py
+++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py
@@ -5,7 +5,7 @@
 import frappe
 from frappe import _
 from frappe.model.document import Document
-from frappe.utils import add_days, cint, cstr, date_diff, getdate, rounded
+from frappe.utils import add_days, cstr, date_diff, flt, getdate, rounded
 
 from erpnext.hr.utils import (
 	get_holiday_dates_for_employee,
@@ -27,26 +27,40 @@
 		validate_active_employee(self.employee)
 		self.validate_duplicate_on_payroll_period()
 		if not self.max_benefits:
-			self.max_benefits = get_max_benefits_remaining(self.employee, self.date, self.payroll_period)
+			self.max_benefits = flt(
+				get_max_benefits_remaining(self.employee, self.date, self.payroll_period),
+				self.precision("max_benefits"),
+			)
 		if self.max_benefits and self.max_benefits > 0:
 			self.validate_max_benefit_for_component()
 			self.validate_prev_benefit_claim()
-			if self.remaining_benefit > 0:
+			if self.remaining_benefit and self.remaining_benefit > 0:
 				self.validate_remaining_benefit_amount()
 		else:
-			frappe.throw(_("As per your assigned Salary Structure you cannot apply for benefits").format(self.employee))
+			frappe.throw(
+				_("As per your assigned Salary Structure you cannot apply for benefits").format(self.employee)
+			)
 
 	def validate_prev_benefit_claim(self):
 		if self.employee_benefits:
 			for benefit in self.employee_benefits:
 				if benefit.pay_against_benefit_claim == 1:
 					payroll_period = frappe.get_doc("Payroll Period", self.payroll_period)
-					benefit_claimed = get_previous_claimed_amount(self.employee, payroll_period, component = benefit.earning_component)
-					benefit_given = get_sal_slip_total_benefit_given(self.employee, payroll_period, component = benefit.earning_component)
+					benefit_claimed = get_previous_claimed_amount(
+						self.employee, payroll_period, component=benefit.earning_component
+					)
+					benefit_given = get_sal_slip_total_benefit_given(
+						self.employee, payroll_period, component=benefit.earning_component
+					)
 					benefit_claim_remining = benefit_claimed - benefit_given
 					if benefit_claimed > 0 and benefit_claim_remining > benefit.amount:
-						frappe.throw(_("An amount of {0} already claimed for the component {1}, set the amount equal or greater than {2}").format(
-							benefit_claimed, benefit.earning_component, benefit_claim_remining))
+						frappe.throw(
+							_(
+								"An amount of {0} already claimed for the component {1}, set the amount equal or greater than {2}"
+							).format(
+								benefit_claimed, benefit.earning_component, benefit_claim_remining
+							)
+						)
 
 	def validate_remaining_benefit_amount(self):
 		# check salary structure earnings have flexi component (sum of max_benefit_amount)
@@ -65,52 +79,80 @@
 			if salary_structure.earnings:
 				for earnings in salary_structure.earnings:
 					if earnings.is_flexible_benefit == 1 and earnings.salary_component not in benefit_components:
-						pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", earnings.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"])
+						pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value(
+							"Salary Component",
+							earnings.salary_component,
+							["pay_against_benefit_claim", "max_benefit_amount"],
+						)
 						if pay_against_benefit_claim != 1:
 							pro_rata_amount += max_benefit_amount
 						else:
 							non_pro_rata_amount += max_benefit_amount
 
-			if pro_rata_amount == 0  and non_pro_rata_amount == 0:
-				frappe.throw(_("Please add the remaining benefits {0} to any of the existing component").format(self.remaining_benefit))
+			if pro_rata_amount == 0 and non_pro_rata_amount == 0:
+				frappe.throw(
+					_("Please add the remaining benefits {0} to any of the existing component").format(
+						self.remaining_benefit
+					)
+				)
 			elif non_pro_rata_amount > 0 and non_pro_rata_amount < rounded(self.remaining_benefit):
-				frappe.throw(_("You can claim only an amount of {0}, the rest amount {1} should be in the application as pro-rata component").format(
-					non_pro_rata_amount, self.remaining_benefit - non_pro_rata_amount))
+				frappe.throw(
+					_(
+						"You can claim only an amount of {0}, the rest amount {1} should be in the application as pro-rata component"
+					).format(non_pro_rata_amount, self.remaining_benefit - non_pro_rata_amount)
+				)
 			elif non_pro_rata_amount == 0:
-				frappe.throw(_("Please add the remaining benefits {0} to the application as pro-rata component").format(
-					self.remaining_benefit))
+				frappe.throw(
+					_("Please add the remaining benefits {0} to the application as pro-rata component").format(
+						self.remaining_benefit
+					)
+				)
 
 	def validate_max_benefit_for_component(self):
 		if self.employee_benefits:
 			max_benefit_amount = 0
 			for employee_benefit in self.employee_benefits:
 				self.validate_max_benefit(employee_benefit.earning_component)
-				max_benefit_amount += employee_benefit.amount
+				max_benefit_amount += flt(employee_benefit.amount)
 			if max_benefit_amount > self.max_benefits:
-				frappe.throw(_("Maximum benefit amount of employee {0} exceeds {1}").format(self.employee, self.max_benefits))
+				frappe.throw(
+					_("Maximum benefit amount of employee {0} exceeds {1}").format(
+						self.employee, self.max_benefits
+					)
+				)
 
 	def validate_max_benefit(self, earning_component_name):
-		max_benefit_amount = frappe.db.get_value("Salary Component", earning_component_name, "max_benefit_amount")
+		max_benefit_amount = frappe.db.get_value(
+			"Salary Component", earning_component_name, "max_benefit_amount"
+		)
 		benefit_amount = 0
 		for employee_benefit in self.employee_benefits:
 			if employee_benefit.earning_component == earning_component_name:
-				benefit_amount += employee_benefit.amount
-		prev_sal_slip_flexi_amount = get_sal_slip_total_benefit_given(self.employee, frappe.get_doc("Payroll Period", self.payroll_period), earning_component_name)
+				benefit_amount += flt(employee_benefit.amount)
+
+		prev_sal_slip_flexi_amount = get_sal_slip_total_benefit_given(
+			self.employee, frappe.get_doc("Payroll Period", self.payroll_period), earning_component_name
+		)
 		benefit_amount += prev_sal_slip_flexi_amount
 		if rounded(benefit_amount, 2) > max_benefit_amount:
-			frappe.throw(_("Maximum benefit amount of component {0} exceeds {1}").format(earning_component_name, max_benefit_amount))
+			frappe.throw(
+				_("Maximum benefit amount of component {0} exceeds {1}").format(
+					earning_component_name, max_benefit_amount
+				)
+			)
 
 	def validate_duplicate_on_payroll_period(self):
 		application = frappe.db.exists(
 			"Employee Benefit Application",
-			{
-				'employee': self.employee,
-				'payroll_period': self.payroll_period,
-				'docstatus': 1
-			}
+			{"employee": self.employee, "payroll_period": self.payroll_period, "docstatus": 1},
 		)
 		if application:
-			frappe.throw(_("Employee {0} already submited an apllication {1} for the payroll period {2}").format(self.employee, application, self.payroll_period))
+			frappe.throw(
+				_("Employee {0} already submited an apllication {1} for the payroll period {2}").format(
+					self.employee, application, self.payroll_period
+				)
+			)
+
 
 @frappe.whitelist()
 def get_max_benefits(employee, on_date):
@@ -121,6 +163,7 @@
 			return max_benefits
 	return False
 
+
 @frappe.whitelist()
 def get_max_benefits_remaining(employee, on_date, payroll_period):
 	max_benefits = get_max_benefits(employee, on_date)
@@ -141,9 +184,14 @@
 				sal_struct = frappe.get_doc("Salary Structure", sal_struct_name)
 				for sal_struct_row in sal_struct.get("earnings"):
 					salary_component = frappe.get_doc("Salary Component", sal_struct_row.salary_component)
-					if salary_component.depends_on_payment_days == 1 and salary_component.pay_against_benefit_claim != 1:
+					if (
+						salary_component.depends_on_payment_days == 1
+						and salary_component.pay_against_benefit_claim != 1
+					):
 						have_depends_on_payment_days = True
-						benefit_amount = get_benefit_amount_based_on_pro_rata(sal_struct, salary_component.max_benefit_amount)
+						benefit_amount = get_benefit_amount_based_on_pro_rata(
+							sal_struct, salary_component.max_benefit_amount
+						)
 						amount_per_day = benefit_amount / payroll_period_days
 						per_day_amount_total += amount_per_day
 
@@ -159,71 +207,115 @@
 			return max_benefits - prev_sal_slip_flexi_total
 	return max_benefits
 
+
 def calculate_lwp(employee, start_date, holidays, working_days):
 	lwp = 0
 	holidays = "','".join(holidays)
+
 	for d in range(working_days):
-		dt = add_days(cstr(getdate(start_date)), d)
-		leave = frappe.db.sql("""
-			select t1.name, t1.half_day
-			from `tabLeave Application` t1, `tabLeave Type` t2
-			where t2.name = t1.leave_type
-			and t2.is_lwp = 1
-			and t1.docstatus = 1
-			and t1.employee = %(employee)s
-			and CASE WHEN t2.include_holiday != 1 THEN %(dt)s not in ('{0}') and %(dt)s between from_date and to_date
-			WHEN t2.include_holiday THEN %(dt)s between from_date and to_date
-			END
-			""".format(holidays), {"employee": employee, "dt": dt})
-		if leave:
-			lwp = cint(leave[0][1]) and (lwp + 0.5) or (lwp + 1)
+		date = add_days(cstr(getdate(start_date)), d)
+
+		LeaveApplication = frappe.qb.DocType("Leave Application")
+		LeaveType = frappe.qb.DocType("Leave Type")
+
+		is_half_day = (
+			frappe.qb.terms.Case()
+			.when(
+				(
+					(LeaveApplication.half_day_date == date)
+					| (LeaveApplication.from_date == LeaveApplication.to_date)
+				),
+				LeaveApplication.half_day,
+			)
+			.else_(0)
+		).as_("is_half_day")
+
+		query = (
+			frappe.qb.from_(LeaveApplication)
+			.inner_join(LeaveType)
+			.on((LeaveType.name == LeaveApplication.leave_type))
+			.select(LeaveApplication.name, is_half_day)
+			.where(
+				(LeaveType.is_lwp == 1)
+				& (LeaveApplication.docstatus == 1)
+				& (LeaveApplication.status == "Approved")
+				& (LeaveApplication.employee == employee)
+				& ((LeaveApplication.from_date <= date) & (date <= LeaveApplication.to_date))
+			)
+		)
+
+		# if it's a holiday only include if leave type has "include holiday" enabled
+		if date in holidays:
+			query = query.where((LeaveType.include_holiday == "1"))
+		leaves = query.run(as_dict=True)
+
+		if leaves:
+			lwp += 0.5 if leaves[0].is_half_day else 1
+
 	return lwp
 
-def get_benefit_component_amount(employee, start_date, end_date, salary_component, sal_struct, payroll_frequency, payroll_period):
+
+def get_benefit_component_amount(
+	employee, start_date, end_date, salary_component, sal_struct, payroll_frequency, payroll_period
+):
 	if not payroll_period:
-		frappe.msgprint(_("Start and end dates not in a valid Payroll Period, cannot calculate {0}")
-			.format(salary_component))
+		frappe.msgprint(
+			_("Start and end dates not in a valid Payroll Period, cannot calculate {0}").format(
+				salary_component
+			)
+		)
 		return False
 
 	# Considering there is only one application for a year
-	benefit_application = frappe.db.sql("""
+	benefit_application = frappe.db.sql(
+		"""
 		select name
 		from `tabEmployee Benefit Application`
 		where
 			payroll_period=%(payroll_period)s
 			and employee=%(employee)s
 			and docstatus = 1
-	""", {
-		'employee': employee,
-		'payroll_period': payroll_period.name
-	})
+	""",
+		{"employee": employee, "payroll_period": payroll_period.name},
+	)
 
 	current_benefit_amount = 0.0
-	component_max_benefit, depends_on_payment_days = frappe.db.get_value("Salary Component",
-		salary_component, ["max_benefit_amount", "depends_on_payment_days"])
+	component_max_benefit, depends_on_payment_days = frappe.db.get_value(
+		"Salary Component", salary_component, ["max_benefit_amount", "depends_on_payment_days"]
+	)
 
 	benefit_amount = 0
 	if benefit_application:
-		benefit_amount = frappe.db.get_value("Employee Benefit Application Detail",
-			{"parent": benefit_application[0][0], "earning_component": salary_component}, "amount")
+		benefit_amount = frappe.db.get_value(
+			"Employee Benefit Application Detail",
+			{"parent": benefit_application[0][0], "earning_component": salary_component},
+			"amount",
+		)
 	elif component_max_benefit:
 		benefit_amount = get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit)
 
 	current_benefit_amount = 0
 	if benefit_amount:
-		total_sub_periods = get_period_factor(employee,
-			start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days)[0]
+		total_sub_periods = get_period_factor(
+			employee, start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days
+		)[0]
 
 		current_benefit_amount = benefit_amount / total_sub_periods
 
 	return current_benefit_amount
 
+
 def get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit):
 	max_benefits_total = 0
 	benefit_amount = 0
 	for d in sal_struct.get("earnings"):
 		if d.is_flexible_benefit == 1:
-			component = frappe.db.get_value("Salary Component", d.salary_component, ["max_benefit_amount", "pay_against_benefit_claim"], as_dict=1)
+			component = frappe.db.get_value(
+				"Salary Component",
+				d.salary_component,
+				["max_benefit_amount", "pay_against_benefit_claim"],
+				as_dict=1,
+			)
 			if not component.pay_against_benefit_claim:
 				max_benefits_total += component.max_benefit_amount
 
@@ -234,34 +326,46 @@
 
 	return benefit_amount
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_earning_components(doctype, txt, searchfield, start, page_len, filters):
 	if len(filters) < 2:
 		return {}
 
-	salary_structure = get_assigned_salary_structure(filters['employee'], filters['date'])
+	salary_structure = get_assigned_salary_structure(filters["employee"], filters["date"])
 
 	if salary_structure:
-		return frappe.db.sql("""
+		return frappe.db.sql(
+			"""
 			select salary_component
 			from `tabSalary Detail`
 			where parent = %s and is_flexible_benefit = 1
 			order by name
-		""", salary_structure)
+		""",
+			salary_structure,
+		)
 	else:
-		frappe.throw(_("Salary Structure not found for employee {0} and date {1}")
-			.format(filters['employee'], filters['date']))
+		frappe.throw(
+			_("Salary Structure not found for employee {0} and date {1}").format(
+				filters["employee"], filters["date"]
+			)
+		)
+
 
 @frappe.whitelist()
 def get_earning_components_max_benefits(employee, date, earning_component):
 	salary_structure = get_assigned_salary_structure(employee, date)
-	amount = frappe.db.sql("""
+	amount = frappe.db.sql(
+		"""
 			select amount
 			from `tabSalary Detail`
 			where parent = %s and is_flexible_benefit = 1
 			and salary_component = %s
 			order by name
-		""", salary_structure, earning_component)
+		""",
+		salary_structure,
+		earning_component,
+	)
 
 	return amount if amount else 0
diff --git a/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py b/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py
index 02149ad..de8f9b6 100644
--- a/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py
+++ b/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py
@@ -3,6 +3,82 @@
 
 import unittest
 
+import frappe
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import add_days, date_diff, get_year_ending, get_year_start, getdate
 
-class TestEmployeeBenefitApplication(unittest.TestCase):
-	pass
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list
+from erpnext.hr.doctype.leave_application.test_leave_application import get_first_sunday
+from erpnext.hr.utils import get_holiday_dates_for_employee
+from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import (
+	calculate_lwp,
+)
+from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import (
+	create_payroll_period,
+)
+from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
+	make_holiday_list,
+	make_leave_application,
+)
+from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip
+from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
+
+
+class TestEmployeeBenefitApplication(FrappeTestCase):
+	def setUp(self):
+		date = getdate()
+		make_holiday_list(from_date=get_year_start(date), to_date=get_year_ending(date))
+
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
+	def test_employee_benefit_application(self):
+		payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company")
+		employee = make_employee("test_employee_benefits@salary.com", company="_Test Company")
+		first_sunday = get_first_sunday("Salary Slip Test Holiday List")
+
+		leave_application = make_leave_application(
+			employee,
+			add_days(first_sunday, 1),
+			add_days(first_sunday, 3),
+			"Leave Without Pay",
+			half_day=1,
+			half_day_date=add_days(first_sunday, 1),
+			submit=True,
+		)
+
+		frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0)
+		salary_structure = make_salary_structure(
+			"Test Employee Benefits",
+			"Monthly",
+			other_details={"max_benefits": 100000},
+			include_flexi_benefits=True,
+			employee=employee,
+			payroll_period=payroll_period,
+		)
+		salary_slip = make_salary_slip(salary_structure.name, employee=employee, posting_date=getdate())
+		salary_slip.insert()
+		salary_slip.submit()
+
+		application = make_employee_benefit_application(
+			employee, payroll_period.name, date=leave_application.to_date
+		)
+		self.assertEqual(application.employee_benefits[0].max_benefit_amount, 15000)
+
+		holidays = get_holiday_dates_for_employee(employee, payroll_period.start_date, application.date)
+		working_days = date_diff(application.date, payroll_period.start_date) + 1
+		lwp = calculate_lwp(employee, payroll_period.start_date, holidays, working_days)
+		self.assertEqual(lwp, 2.5)
+
+
+def make_employee_benefit_application(employee, payroll_period, date):
+	frappe.db.delete("Employee Benefit Application")
+
+	return frappe.get_doc(
+		{
+			"doctype": "Employee Benefit Application",
+			"employee": employee,
+			"date": date,
+			"payroll_period": payroll_period,
+			"employee_benefits": [{"earning_component": "Medical Allowance", "amount": 1500}],
+		}
+	).insert()
diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py
index 801ce4b..6ec34b9 100644
--- a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py
+++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py
@@ -23,9 +23,15 @@
 		max_benefits = get_max_benefits(self.employee, self.claim_date)
 		if not max_benefits or max_benefits <= 0:
 			frappe.throw(_("Employee {0} has no maximum benefit amount").format(self.employee))
-		payroll_period = get_payroll_period(self.claim_date, self.claim_date, frappe.db.get_value("Employee", self.employee, "company"))
+		payroll_period = get_payroll_period(
+			self.claim_date, self.claim_date, frappe.db.get_value("Employee", self.employee, "company")
+		)
 		if not payroll_period:
-			frappe.throw(_("{0} is not in a valid Payroll Period").format(frappe.format(self.claim_date, dict(fieldtype='Date'))))
+			frappe.throw(
+				_("{0} is not in a valid Payroll Period").format(
+					frappe.format(self.claim_date, dict(fieldtype="Date"))
+				)
+			)
 		self.validate_max_benefit_for_component(payroll_period)
 		self.validate_max_benefit_for_sal_struct(max_benefits)
 		self.validate_benefit_claim_amount(max_benefits, payroll_period)
@@ -36,21 +42,30 @@
 		claimed_amount = self.claimed_amount
 		claimed_amount += get_previous_claimed_amount(self.employee, payroll_period)
 		if max_benefits < claimed_amount:
-			frappe.throw(_("Maximum benefit of employee {0} exceeds {1} by the sum {2} of previous claimed\
-			amount").format(self.employee, max_benefits, claimed_amount-max_benefits))
+			frappe.throw(
+				_(
+					"Maximum benefit of employee {0} exceeds {1} by the sum {2} of previous claimed amount"
+				).format(self.employee, max_benefits, claimed_amount - max_benefits)
+			)
 
 	def validate_max_benefit_for_sal_struct(self, max_benefits):
 		if self.claimed_amount > max_benefits:
-			frappe.throw(_("Maximum benefit amount of employee {0} exceeds {1}").format(self.employee, max_benefits))
+			frappe.throw(
+				_("Maximum benefit amount of employee {0} exceeds {1}").format(self.employee, max_benefits)
+			)
 
 	def validate_max_benefit_for_component(self, payroll_period):
 		if self.max_amount_eligible:
 			claimed_amount = self.claimed_amount
-			claimed_amount += get_previous_claimed_amount(self.employee,
-				payroll_period, component = self.earning_component)
+			claimed_amount += get_previous_claimed_amount(
+				self.employee, payroll_period, component=self.earning_component
+			)
 			if claimed_amount > self.max_amount_eligible:
-				frappe.throw(_("Maximum amount eligible for the component {0} exceeds {1}")
-					.format(self.earning_component, self.max_amount_eligible))
+				frappe.throw(
+					_("Maximum amount eligible for the component {0} exceeds {1}").format(
+						self.earning_component, self.max_amount_eligible
+					)
+				)
 
 	def validate_non_pro_rata_benefit_claim(self, max_benefits, payroll_period):
 		claimed_amount = self.claimed_amount
@@ -64,30 +79,38 @@
 				sal_struct = frappe.get_doc("Salary Structure", sal_struct_name)
 				pro_rata_amount = get_benefit_pro_rata_ratio_amount(self.employee, self.claim_date, sal_struct)
 
-		claimed_amount += get_previous_claimed_amount(self.employee, payroll_period, non_pro_rata = True)
+		claimed_amount += get_previous_claimed_amount(self.employee, payroll_period, non_pro_rata=True)
 		if max_benefits < pro_rata_amount + claimed_amount:
-			frappe.throw(_("Maximum benefit of employee {0} exceeds {1} by the sum {2} of benefit application pro-rata component\
-			amount and previous claimed amount").format(self.employee, max_benefits, pro_rata_amount+claimed_amount-max_benefits))
+			frappe.throw(
+				_(
+					"Maximum benefit of employee {0} exceeds {1} by the sum {2} of benefit application pro-rata component amount and previous claimed amount"
+				).format(
+					self.employee, max_benefits, pro_rata_amount + claimed_amount - max_benefits
+				)
+			)
 
 	def get_pro_rata_amount_in_application(self, payroll_period):
 		application = frappe.db.exists(
 			"Employee Benefit Application",
-			{
-				'employee': self.employee,
-				'payroll_period': payroll_period,
-				'docstatus': 1
-			}
+			{"employee": self.employee, "payroll_period": payroll_period, "docstatus": 1},
 		)
 		if application:
-			return frappe.db.get_value("Employee Benefit Application", application, "pro_rata_dispensed_amount")
+			return frappe.db.get_value(
+				"Employee Benefit Application", application, "pro_rata_dispensed_amount"
+			)
 		return False
 
+
 def get_benefit_pro_rata_ratio_amount(employee, on_date, sal_struct):
 	total_pro_rata_max = 0
 	benefit_amount_total = 0
 	for sal_struct_row in sal_struct.get("earnings"):
 		try:
-			pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"])
+			pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value(
+				"Salary Component",
+				sal_struct_row.salary_component,
+				["pay_against_benefit_claim", "max_benefit_amount"],
+			)
 		except TypeError:
 			# show the error in tests?
 			frappe.throw(_("Unable to find Salary Component {0}").format(sal_struct_row.salary_component))
@@ -95,7 +118,11 @@
 			total_pro_rata_max += max_benefit_amount
 	if total_pro_rata_max > 0:
 		for sal_struct_row in sal_struct.get("earnings"):
-			pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"])
+			pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value(
+				"Salary Component",
+				sal_struct_row.salary_component,
+				["pay_against_benefit_claim", "max_benefit_amount"],
+			)
 
 			if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1:
 				component_max = max_benefit_amount
@@ -105,6 +132,7 @@
 				benefit_amount_total += benefit_amount
 	return benefit_amount_total
 
+
 def get_benefit_claim_amount(employee, start_date, end_date, salary_component=None):
 	query = """
 		select sum(claimed_amount)
@@ -119,41 +147,54 @@
 	if salary_component:
 		query += " and earning_component = %(earning_component)s"
 
-	claimed_amount = flt(frappe.db.sql(query, {
-		'employee': employee,
-		'start_date': start_date,
-		'end_date': end_date,
-		'earning_component': salary_component
-	})[0][0])
+	claimed_amount = flt(
+		frappe.db.sql(
+			query,
+			{
+				"employee": employee,
+				"start_date": start_date,
+				"end_date": end_date,
+				"earning_component": salary_component,
+			},
+		)[0][0]
+	)
 
 	return claimed_amount
 
+
 def get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payroll_period):
 	pro_rata_amount = 0
 	claimed_amount = 0
 	application = frappe.db.exists(
 		"Employee Benefit Application",
-		{
-			'employee': employee,
-			'payroll_period': payroll_period.name,
-			'docstatus': 1
-		}
+		{"employee": employee, "payroll_period": payroll_period.name, "docstatus": 1},
 	)
 	if application:
 		application_obj = frappe.get_doc("Employee Benefit Application", application)
-		pro_rata_amount = application_obj.pro_rata_dispensed_amount + application_obj.max_benefits - application_obj.remaining_benefit
+		pro_rata_amount = (
+			application_obj.pro_rata_dispensed_amount
+			+ application_obj.max_benefits
+			- application_obj.remaining_benefit
+		)
 	else:
 		pro_rata_amount = get_benefit_pro_rata_ratio_amount(employee, sal_slip_start_date, sal_struct)
 
-	claimed_amount += get_benefit_claim_amount(employee, payroll_period.start_date, payroll_period.end_date)
+	claimed_amount += get_benefit_claim_amount(
+		employee, payroll_period.start_date, payroll_period.end_date
+	)
 
 	return claimed_amount + pro_rata_amount
 
-def get_last_payroll_period_benefits(employee, sal_slip_start_date, sal_slip_end_date, payroll_period,  sal_struct):
+
+def get_last_payroll_period_benefits(
+	employee, sal_slip_start_date, sal_slip_end_date, payroll_period, sal_struct
+):
 	max_benefits = get_max_benefits(employee, payroll_period.end_date)
 	if not max_benefits:
 		max_benefits = 0
-	remaining_benefit = max_benefits - get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payroll_period)
+	remaining_benefit = max_benefits - get_total_benefit_dispensed(
+		employee, sal_struct, sal_slip_start_date, payroll_period
+	)
 	if remaining_benefit > 0:
 		have_remaining = True
 		# Set the remaining benefits to flexi non pro-rata component in the salary structure
@@ -162,7 +203,9 @@
 			if d.is_flexible_benefit == 1:
 				salary_component = frappe.get_doc("Salary Component", d.salary_component)
 				if salary_component.pay_against_benefit_claim == 1:
-					claimed_amount = get_benefit_claim_amount(employee, payroll_period.start_date, sal_slip_end_date, d.salary_component)
+					claimed_amount = get_benefit_claim_amount(
+						employee, payroll_period.start_date, sal_slip_end_date, d.salary_component
+					)
 					amount_fit_to_component = salary_component.max_benefit_amount - claimed_amount
 					if amount_fit_to_component > 0:
 						if remaining_benefit > amount_fit_to_component:
@@ -171,19 +214,23 @@
 						else:
 							amount = remaining_benefit
 							have_remaining = False
-						current_claimed_amount = get_benefit_claim_amount(employee, sal_slip_start_date, sal_slip_end_date, d.salary_component)
+						current_claimed_amount = get_benefit_claim_amount(
+							employee, sal_slip_start_date, sal_slip_end_date, d.salary_component
+						)
 						amount += current_claimed_amount
 						struct_row = {}
 						salary_components_dict = {}
-						struct_row['depends_on_payment_days'] = salary_component.depends_on_payment_days
-						struct_row['salary_component'] = salary_component.name
-						struct_row['abbr'] = salary_component.salary_component_abbr
-						struct_row['do_not_include_in_total'] = salary_component.do_not_include_in_total
-						struct_row['is_tax_applicable'] = salary_component.is_tax_applicable,
-						struct_row['is_flexible_benefit'] = salary_component.is_flexible_benefit,
-						struct_row['variable_based_on_taxable_salary'] = salary_component.variable_based_on_taxable_salary
-						salary_components_dict['amount'] = amount
-						salary_components_dict['struct_row'] = struct_row
+						struct_row["depends_on_payment_days"] = salary_component.depends_on_payment_days
+						struct_row["salary_component"] = salary_component.name
+						struct_row["abbr"] = salary_component.salary_component_abbr
+						struct_row["do_not_include_in_total"] = salary_component.do_not_include_in_total
+						struct_row["is_tax_applicable"] = (salary_component.is_tax_applicable,)
+						struct_row["is_flexible_benefit"] = (salary_component.is_flexible_benefit,)
+						struct_row[
+							"variable_based_on_taxable_salary"
+						] = salary_component.variable_based_on_taxable_salary
+						salary_components_dict["amount"] = amount
+						salary_components_dict["struct_row"] = struct_row
 						salary_components_array.append(salary_components_dict)
 			if not have_remaining:
 				break
diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py
index a37e224..7686185 100644
--- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py
+++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py
@@ -15,13 +15,17 @@
 		self.validate_salary_structure()
 
 	def validate_salary_structure(self):
-		if not frappe.db.exists('Salary Structure Assignment', {'employee': self.employee}):
-			frappe.throw(_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(self.employee))
+		if not frappe.db.exists("Salary Structure Assignment", {"employee": self.employee}):
+			frappe.throw(
+				_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(
+					self.employee
+				)
+			)
 
 	def on_submit(self):
-		company = frappe.db.get_value('Employee', self.employee, 'company')
+		company = frappe.db.get_value("Employee", self.employee, "company")
 
-		additional_salary = frappe.new_doc('Additional Salary')
+		additional_salary = frappe.new_doc("Additional Salary")
 		additional_salary.employee = self.employee
 		additional_salary.currency = self.currency
 		additional_salary.salary_component = self.salary_component
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py
index 9b5eab6..3d1d965 100644
--- a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py
+++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py
@@ -20,7 +20,9 @@
 	def validate(self):
 		validate_active_employee(self.employee)
 		validate_tax_declaration(self.declarations)
-		validate_duplicate_exemption_for_payroll_period(self.doctype, self.name, self.payroll_period, self.employee)
+		validate_duplicate_exemption_for_payroll_period(
+			self.doctype, self.name, self.payroll_period, self.employee
+		)
 		self.set_total_declared_amount()
 		self.set_total_exemption_amount()
 		self.calculate_hra_exemption()
@@ -31,7 +33,9 @@
 			self.total_declared_amount += flt(d.amount)
 
 	def set_total_exemption_amount(self):
-		self.total_exemption_amount = get_total_exemption_amount(self.declarations)
+		self.total_exemption_amount = flt(
+			get_total_exemption_amount(self.declarations), self.precision("total_exemption_amount")
+		)
 
 	def calculate_hra_exemption(self):
 		self.salary_structure_hra, self.annual_hra_exemption, self.monthly_hra_exemption = 0, 0, 0
@@ -39,21 +43,36 @@
 			hra_exemption = calculate_annual_eligible_hra_exemption(self)
 			if hra_exemption:
 				self.total_exemption_amount += hra_exemption["annual_exemption"]
-				self.salary_structure_hra = hra_exemption["hra_amount"]
-				self.annual_hra_exemption = hra_exemption["annual_exemption"]
-				self.monthly_hra_exemption = hra_exemption["monthly_exemption"]
+				self.total_exemption_amount = flt(
+					self.total_exemption_amount, self.precision("total_exemption_amount")
+				)
+				self.salary_structure_hra = flt(
+					hra_exemption["hra_amount"], self.precision("salary_structure_hra")
+				)
+				self.annual_hra_exemption = flt(
+					hra_exemption["annual_exemption"], self.precision("annual_hra_exemption")
+				)
+				self.monthly_hra_exemption = flt(
+					hra_exemption["monthly_exemption"], self.precision("monthly_hra_exemption")
+				)
+
 
 @frappe.whitelist()
 def make_proof_submission(source_name, target_doc=None):
-	doclist = get_mapped_doc("Employee Tax Exemption Declaration", source_name, {
-		"Employee Tax Exemption Declaration": {
-			"doctype": "Employee Tax Exemption Proof Submission",
-			"field_no_map": ["monthly_house_rent", "monthly_hra_exemption"]
+	doclist = get_mapped_doc(
+		"Employee Tax Exemption Declaration",
+		source_name,
+		{
+			"Employee Tax Exemption Declaration": {
+				"doctype": "Employee Tax Exemption Proof Submission",
+				"field_no_map": ["monthly_house_rent", "monthly_hra_exemption"],
+			},
+			"Employee Tax Exemption Declaration Category": {
+				"doctype": "Employee Tax Exemption Proof Submission Detail",
+				"add_if_empty": True,
+			},
 		},
-		"Employee Tax Exemption Declaration Category": {
-			"doctype": "Employee Tax Exemption Proof Submission Detail",
-			"add_if_empty": True
-		}
-	}, target_doc)
+		target_doc,
+	)
 
 	return doclist
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py
index fc28afd..2d8df35 100644
--- a/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py
+++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py
@@ -4,127 +4,487 @@
 import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import add_months, getdate
 
 import erpnext
 from erpnext.hr.doctype.employee.test_employee import make_employee
 from erpnext.hr.utils import DuplicateDeclarationError
 
 
-class TestEmployeeTaxExemptionDeclaration(unittest.TestCase):
+class TestEmployeeTaxExemptionDeclaration(FrappeTestCase):
 	def setUp(self):
-		make_employee("employee@taxexepmtion.com")
-		make_employee("employee1@taxexepmtion.com")
-		create_payroll_period()
+		make_employee("employee@taxexemption.com", company="_Test Company")
+		make_employee("employee1@taxexemption.com", company="_Test Company")
+		create_payroll_period(company="_Test Company")
 		create_exemption_category()
-		frappe.db.sql("""delete from `tabEmployee Tax Exemption Declaration`""")
+		frappe.db.delete("Employee Tax Exemption Declaration")
+		frappe.db.delete("Salary Structure Assignment")
 
 	def test_duplicate_category_in_declaration(self):
-		declaration = frappe.get_doc({
-			"doctype": "Employee Tax Exemption Declaration",
-			"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
-			"company": erpnext.get_default_company(),
-			"payroll_period": "_Test Payroll Period",
-			"currency": erpnext.get_default_currency(),
-			"declarations": [
-				dict(exemption_sub_category = "_Test Sub Category",
-					exemption_category = "_Test Category",
-					amount = 100000),
-				dict(exemption_sub_category = "_Test Sub Category",
-					exemption_category = "_Test Category",
-					amount = 50000)
-			]
-		})
+		declaration = frappe.get_doc(
+			{
+				"doctype": "Employee Tax Exemption Declaration",
+				"employee": frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name"),
+				"company": erpnext.get_default_company(),
+				"payroll_period": "_Test Payroll Period",
+				"currency": erpnext.get_default_currency(),
+				"declarations": [
+					dict(
+						exemption_sub_category="_Test Sub Category",
+						exemption_category="_Test Category",
+						amount=100000,
+					),
+					dict(
+						exemption_sub_category="_Test Sub Category",
+						exemption_category="_Test Category",
+						amount=50000,
+					),
+				],
+			}
+		)
 		self.assertRaises(frappe.ValidationError, declaration.save)
 
 	def test_duplicate_entry_for_payroll_period(self):
-		declaration = frappe.get_doc({
-			"doctype": "Employee Tax Exemption Declaration",
-			"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
-			"company":  erpnext.get_default_company(),
-			"payroll_period": "_Test Payroll Period",
-			"currency": erpnext.get_default_currency(),
-			"declarations": [
-				dict(exemption_sub_category = "_Test Sub Category",
-					exemption_category = "_Test Category",
-					amount = 100000),
-				dict(exemption_sub_category = "_Test1 Sub Category",
-					exemption_category = "_Test Category",
-					amount = 50000),
-			]
-		}).insert()
+		declaration = frappe.get_doc(
+			{
+				"doctype": "Employee Tax Exemption Declaration",
+				"employee": frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name"),
+				"company": erpnext.get_default_company(),
+				"payroll_period": "_Test Payroll Period",
+				"currency": erpnext.get_default_currency(),
+				"declarations": [
+					dict(
+						exemption_sub_category="_Test Sub Category",
+						exemption_category="_Test Category",
+						amount=100000,
+					),
+					dict(
+						exemption_sub_category="_Test1 Sub Category",
+						exemption_category="_Test Category",
+						amount=50000,
+					),
+				],
+			}
+		).insert()
 
-		duplicate_declaration = frappe.get_doc({
-			"doctype": "Employee Tax Exemption Declaration",
-			"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
-			"company":  erpnext.get_default_company(),
-			"payroll_period": "_Test Payroll Period",
-			"currency": erpnext.get_default_currency(),
-			"declarations": [
-				dict(exemption_sub_category = "_Test Sub Category",
-					exemption_category = "_Test Category",
-					amount = 100000)
-			]
-		})
+		duplicate_declaration = frappe.get_doc(
+			{
+				"doctype": "Employee Tax Exemption Declaration",
+				"employee": frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name"),
+				"company": erpnext.get_default_company(),
+				"payroll_period": "_Test Payroll Period",
+				"currency": erpnext.get_default_currency(),
+				"declarations": [
+					dict(
+						exemption_sub_category="_Test Sub Category",
+						exemption_category="_Test Category",
+						amount=100000,
+					)
+				],
+			}
+		)
 		self.assertRaises(DuplicateDeclarationError, duplicate_declaration.insert)
-		duplicate_declaration.employee = frappe.get_value("Employee", {"user_id":"employee1@taxexepmtion.com"}, "name")
+		duplicate_declaration.employee = frappe.get_value(
+			"Employee", {"user_id": "employee1@taxexemption.com"}, "name"
+		)
 		self.assertTrue(duplicate_declaration.insert)
 
 	def test_exemption_amount(self):
-		declaration = frappe.get_doc({
-			"doctype": "Employee Tax Exemption Declaration",
-			"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
-			"company":  erpnext.get_default_company(),
-			"payroll_period": "_Test Payroll Period",
-			"currency": erpnext.get_default_currency(),
-			"declarations": [
-				dict(exemption_sub_category = "_Test Sub Category",
-					exemption_category = "_Test Category",
-					amount = 80000),
-				dict(exemption_sub_category = "_Test1 Sub Category",
-					exemption_category = "_Test Category",
-					amount = 60000),
-			]
-		}).insert()
+		declaration = frappe.get_doc(
+			{
+				"doctype": "Employee Tax Exemption Declaration",
+				"employee": frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name"),
+				"company": erpnext.get_default_company(),
+				"payroll_period": "_Test Payroll Period",
+				"currency": erpnext.get_default_currency(),
+				"declarations": [
+					dict(
+						exemption_sub_category="_Test Sub Category",
+						exemption_category="_Test Category",
+						amount=80000,
+					),
+					dict(
+						exemption_sub_category="_Test1 Sub Category",
+						exemption_category="_Test Category",
+						amount=60000,
+					),
+				],
+			}
+		).insert()
 
 		self.assertEqual(declaration.total_exemption_amount, 100000)
 
+	def test_india_hra_exemption(self):
+		# set country
+		current_country = frappe.flags.country
+		frappe.flags.country = "India"
+
+		setup_hra_exemption_prerequisites("Monthly")
+		employee = frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name")
+
+		declaration = frappe.get_doc(
+			{
+				"doctype": "Employee Tax Exemption Declaration",
+				"employee": employee,
+				"company": "_Test Company",
+				"payroll_period": "_Test Payroll Period",
+				"currency": "INR",
+				"monthly_house_rent": 50000,
+				"rented_in_metro_city": 1,
+				"declarations": [
+					dict(
+						exemption_sub_category="_Test Sub Category",
+						exemption_category="_Test Category",
+						amount=80000,
+					),
+					dict(
+						exemption_sub_category="_Test1 Sub Category",
+						exemption_category="_Test Category",
+						amount=60000,
+					),
+				],
+			}
+		).insert()
+
+		# Monthly HRA received = 3000
+		# should set HRA exemption as per actual annual HRA because that's the minimum
+		self.assertEqual(declaration.monthly_hra_exemption, 3000)
+		self.assertEqual(declaration.annual_hra_exemption, 36000)
+		# 100000 Standard Exemption + 36000 HRA exemption
+		self.assertEqual(declaration.total_exemption_amount, 136000)
+
+		# reset
+		frappe.flags.country = current_country
+
+	def test_india_hra_exemption_with_daily_payroll_frequency(self):
+		# set country
+		current_country = frappe.flags.country
+		frappe.flags.country = "India"
+
+		setup_hra_exemption_prerequisites("Daily")
+		employee = frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name")
+
+		declaration = frappe.get_doc(
+			{
+				"doctype": "Employee Tax Exemption Declaration",
+				"employee": employee,
+				"company": "_Test Company",
+				"payroll_period": "_Test Payroll Period",
+				"currency": "INR",
+				"monthly_house_rent": 170000,
+				"rented_in_metro_city": 1,
+				"declarations": [
+					dict(
+						exemption_sub_category="_Test1 Sub Category",
+						exemption_category="_Test Category",
+						amount=60000,
+					),
+				],
+			}
+		).insert()
+
+		# Daily HRA received = 3000
+		# should set HRA exemption as per (rent - 10% of Basic Salary), that's the minimum
+		self.assertEqual(declaration.monthly_hra_exemption, 17916.67)
+		self.assertEqual(declaration.annual_hra_exemption, 215000)
+		# 50000 Standard Exemption + 215000 HRA exemption
+		self.assertEqual(declaration.total_exemption_amount, 265000)
+
+		# reset
+		frappe.flags.country = current_country
+
+	def test_india_hra_exemption_with_weekly_payroll_frequency(self):
+		# set country
+		current_country = frappe.flags.country
+		frappe.flags.country = "India"
+
+		setup_hra_exemption_prerequisites("Weekly")
+		employee = frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name")
+
+		declaration = frappe.get_doc(
+			{
+				"doctype": "Employee Tax Exemption Declaration",
+				"employee": employee,
+				"company": "_Test Company",
+				"payroll_period": "_Test Payroll Period",
+				"currency": "INR",
+				"monthly_house_rent": 170000,
+				"rented_in_metro_city": 1,
+				"declarations": [
+					dict(
+						exemption_sub_category="_Test1 Sub Category",
+						exemption_category="_Test Category",
+						amount=60000,
+					),
+				],
+			}
+		).insert()
+
+		# Weekly HRA received = 3000
+		# should set HRA exemption as per actual annual HRA because that's the minimum
+		self.assertEqual(declaration.monthly_hra_exemption, 13000)
+		self.assertEqual(declaration.annual_hra_exemption, 156000)
+		# 50000 Standard Exemption + 156000 HRA exemption
+		self.assertEqual(declaration.total_exemption_amount, 206000)
+
+		# reset
+		frappe.flags.country = current_country
+
+	def test_india_hra_exemption_with_fortnightly_payroll_frequency(self):
+		# set country
+		current_country = frappe.flags.country
+		frappe.flags.country = "India"
+
+		setup_hra_exemption_prerequisites("Fortnightly")
+		employee = frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name")
+
+		declaration = frappe.get_doc(
+			{
+				"doctype": "Employee Tax Exemption Declaration",
+				"employee": employee,
+				"company": "_Test Company",
+				"payroll_period": "_Test Payroll Period",
+				"currency": "INR",
+				"monthly_house_rent": 170000,
+				"rented_in_metro_city": 1,
+				"declarations": [
+					dict(
+						exemption_sub_category="_Test1 Sub Category",
+						exemption_category="_Test Category",
+						amount=60000,
+					),
+				],
+			}
+		).insert()
+
+		# Fortnightly HRA received = 3000
+		# should set HRA exemption as per actual annual HRA because that's the minimum
+		self.assertEqual(declaration.monthly_hra_exemption, 6500)
+		self.assertEqual(declaration.annual_hra_exemption, 78000)
+		# 50000 Standard Exemption + 78000 HRA exemption
+		self.assertEqual(declaration.total_exemption_amount, 128000)
+
+		# reset
+		frappe.flags.country = current_country
+
+	def test_india_hra_exemption_with_bimonthly_payroll_frequency(self):
+		# set country
+		current_country = frappe.flags.country
+		frappe.flags.country = "India"
+
+		setup_hra_exemption_prerequisites("Bimonthly")
+		employee = frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name")
+
+		declaration = frappe.get_doc(
+			{
+				"doctype": "Employee Tax Exemption Declaration",
+				"employee": employee,
+				"company": "_Test Company",
+				"payroll_period": "_Test Payroll Period",
+				"currency": "INR",
+				"monthly_house_rent": 50000,
+				"rented_in_metro_city": 1,
+				"declarations": [
+					dict(
+						exemption_sub_category="_Test Sub Category",
+						exemption_category="_Test Category",
+						amount=80000,
+					),
+					dict(
+						exemption_sub_category="_Test1 Sub Category",
+						exemption_category="_Test Category",
+						amount=60000,
+					),
+				],
+			}
+		).insert()
+
+		# Bimonthly HRA received = 3000
+		# should set HRA exemption as per actual annual HRA because that's the minimum
+		self.assertEqual(declaration.monthly_hra_exemption, 1500)
+		self.assertEqual(declaration.annual_hra_exemption, 18000)
+		# 100000 Standard Exemption + 18000 HRA exemption
+		self.assertEqual(declaration.total_exemption_amount, 118000)
+
+		# reset
+		frappe.flags.country = current_country
+
+	def test_india_hra_exemption_with_multiple_salary_structure_assignments(self):
+		from erpnext.payroll.doctype.salary_slip.test_salary_slip import create_tax_slab
+		from erpnext.payroll.doctype.salary_structure.test_salary_structure import (
+			create_salary_structure_assignment,
+			make_salary_structure,
+		)
+
+		# set country
+		current_country = frappe.flags.country
+		frappe.flags.country = "India"
+
+		employee = make_employee("employee@taxexemption2.com", company="_Test Company")
+		payroll_period = create_payroll_period(name="_Test Payroll Period", company="_Test Company")
+
+		create_tax_slab(
+			payroll_period,
+			allow_tax_exemption=True,
+			currency="INR",
+			effective_date=getdate("2019-04-01"),
+			company="_Test Company",
+		)
+
+		frappe.db.set_value(
+			"Company", "_Test Company", {"basic_component": "Basic Salary", "hra_component": "HRA"}
+		)
+
+		# salary structure with base 50000, HRA 3000
+		make_salary_structure(
+			"Monthly Structure for HRA Exemption 1",
+			"Monthly",
+			employee=employee,
+			company="_Test Company",
+			currency="INR",
+			payroll_period=payroll_period.name,
+			from_date=payroll_period.start_date,
+		)
+
+		# salary structure with base 70000, HRA = base * 0.2 = 14000
+		salary_structure = make_salary_structure(
+			"Monthly Structure for HRA Exemption 2",
+			"Monthly",
+			employee=employee,
+			company="_Test Company",
+			currency="INR",
+			payroll_period=payroll_period.name,
+			from_date=payroll_period.start_date,
+			dont_submit=True,
+		)
+		for component_row in salary_structure.earnings:
+			if component_row.salary_component == "HRA":
+				component_row.amount = 0
+				component_row.amount_based_on_formula = 1
+				component_row.formula = "base * 0.2"
+				break
+
+		salary_structure.submit()
+
+		create_salary_structure_assignment(
+			employee,
+			salary_structure.name,
+			from_date=add_months(payroll_period.start_date, 6),
+			company="_Test Company",
+			currency="INR",
+			payroll_period=payroll_period.name,
+			base=70000,
+			allow_duplicate=True,
+		)
+
+		declaration = frappe.get_doc(
+			{
+				"doctype": "Employee Tax Exemption Declaration",
+				"employee": employee,
+				"company": "_Test Company",
+				"payroll_period": payroll_period.name,
+				"currency": "INR",
+				"monthly_house_rent": 50000,
+				"rented_in_metro_city": 1,
+				"declarations": [
+					dict(
+						exemption_sub_category="_Test1 Sub Category",
+						exemption_category="_Test Category",
+						amount=60000,
+					),
+				],
+			}
+		).insert()
+
+		# Monthly HRA received = 50000 * 6 months + 70000 * 6 months
+		# should set HRA exemption as per actual annual HRA because that's the minimum
+		self.assertEqual(declaration.monthly_hra_exemption, 8500)
+		self.assertEqual(declaration.annual_hra_exemption, 102000)
+		# 50000 Standard Exemption + 102000 HRA exemption
+		self.assertEqual(declaration.total_exemption_amount, 152000)
+
+		# reset
+		frappe.flags.country = current_country
+
+
 def create_payroll_period(**args):
 	args = frappe._dict(args)
 	name = args.name or "_Test Payroll Period"
 	if not frappe.db.exists("Payroll Period", name):
 		from datetime import date
-		payroll_period = frappe.get_doc(dict(
-			doctype = 'Payroll Period',
-			name = name,
-			company =  args.company or erpnext.get_default_company(),
-			start_date = args.start_date or date(date.today().year, 1, 1),
-			end_date = args.end_date or date(date.today().year, 12, 31)
-		)).insert()
+
+		payroll_period = frappe.get_doc(
+			dict(
+				doctype="Payroll Period",
+				name=name,
+				company=args.company or erpnext.get_default_company(),
+				start_date=args.start_date or date(date.today().year, 1, 1),
+				end_date=args.end_date or date(date.today().year, 12, 31),
+			)
+		).insert()
 		return payroll_period
 	else:
 		return frappe.get_doc("Payroll Period", name)
 
+
 def create_exemption_category():
 	if not frappe.db.exists("Employee Tax Exemption Category", "_Test Category"):
-		category = frappe.get_doc({
-			"doctype": "Employee Tax Exemption Category",
-			"name": "_Test Category",
-			"deduction_component": "Income Tax",
-			"max_amount": 100000
-		}).insert()
+		category = frappe.get_doc(
+			{
+				"doctype": "Employee Tax Exemption Category",
+				"name": "_Test Category",
+				"deduction_component": "Income Tax",
+				"max_amount": 100000,
+			}
+		).insert()
 	if not frappe.db.exists("Employee Tax Exemption Sub Category", "_Test Sub Category"):
-		frappe.get_doc({
-			"doctype": "Employee Tax Exemption Sub Category",
-			"name": "_Test Sub Category",
-			"exemption_category": "_Test Category",
-			"max_amount": 100000,
-			"is_active": 1
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Employee Tax Exemption Sub Category",
+				"name": "_Test Sub Category",
+				"exemption_category": "_Test Category",
+				"max_amount": 100000,
+				"is_active": 1,
+			}
+		).insert()
 	if not frappe.db.exists("Employee Tax Exemption Sub Category", "_Test1 Sub Category"):
-		frappe.get_doc({
-			"doctype": "Employee Tax Exemption Sub Category",
-			"name": "_Test1 Sub Category",
-			"exemption_category": "_Test Category",
-			"max_amount": 50000,
-			"is_active": 1
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Employee Tax Exemption Sub Category",
+				"name": "_Test1 Sub Category",
+				"exemption_category": "_Test Category",
+				"max_amount": 50000,
+				"is_active": 1,
+			}
+		).insert()
+
+
+def setup_hra_exemption_prerequisites(frequency, employee=None):
+	from erpnext.payroll.doctype.salary_slip.test_salary_slip import create_tax_slab
+	from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
+
+	payroll_period = create_payroll_period(name="_Test Payroll Period", company="_Test Company")
+	if not employee:
+		employee = frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name")
+
+	create_tax_slab(
+		payroll_period,
+		allow_tax_exemption=True,
+		currency="INR",
+		effective_date=getdate("2019-04-01"),
+		company="_Test Company",
+	)
+
+	make_salary_structure(
+		f"{frequency} Structure for HRA Exemption",
+		frequency,
+		employee=employee,
+		company="_Test Company",
+		currency="INR",
+		payroll_period=payroll_period,
+	)
+
+	frappe.db.set_value(
+		"Company", "_Test Company", {"basic_component": "Basic Salary", "hra_component": "HRA"}
+	)
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py
index 56e73b3..b3b66b9 100644
--- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py
+++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py
@@ -21,7 +21,9 @@
 		self.set_total_actual_amount()
 		self.set_total_exemption_amount()
 		self.calculate_hra_exemption()
-		validate_duplicate_exemption_for_payroll_period(self.doctype, self.name, self.payroll_period, self.employee)
+		validate_duplicate_exemption_for_payroll_period(
+			self.doctype, self.name, self.payroll_period, self.employee
+		)
 
 	def set_total_actual_amount(self):
 		self.total_actual_amount = flt(self.get("house_rent_payment_amount"))
@@ -29,7 +31,9 @@
 			self.total_actual_amount += flt(d.amount)
 
 	def set_total_exemption_amount(self):
-		self.exemption_amount = get_total_exemption_amount(self.tax_exemption_proofs)
+		self.exemption_amount = flt(
+			get_total_exemption_amount(self.tax_exemption_proofs), self.precision("exemption_amount")
+		)
 
 	def calculate_hra_exemption(self):
 		self.monthly_hra_exemption, self.monthly_house_rent, self.total_eligible_hra_exemption = 0, 0, 0
@@ -37,6 +41,13 @@
 			hra_exemption = calculate_hra_exemption_for_period(self)
 			if hra_exemption:
 				self.exemption_amount += hra_exemption["total_eligible_hra_exemption"]
-				self.monthly_hra_exemption = hra_exemption["monthly_exemption"]
-				self.monthly_house_rent = hra_exemption["monthly_house_rent"]
-				self.total_eligible_hra_exemption = hra_exemption["total_eligible_hra_exemption"]
+				self.exemption_amount = flt(self.exemption_amount, self.precision("exemption_amount"))
+				self.monthly_hra_exemption = flt(
+					hra_exemption["monthly_exemption"], self.precision("monthly_hra_exemption")
+				)
+				self.monthly_house_rent = flt(
+					hra_exemption["monthly_house_rent"], self.precision("monthly_house_rent")
+				)
+				self.total_eligible_hra_exemption = flt(
+					hra_exemption["total_eligible_hra_exemption"], self.precision("total_eligible_hra_exemption")
+				)
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py
index f2aa64c..416cf31 100644
--- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py
+++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py
@@ -4,55 +4,133 @@
 import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 
+from erpnext.hr.doctype.employee.test_employee import make_employee
 from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import (
 	create_exemption_category,
 	create_payroll_period,
+	setup_hra_exemption_prerequisites,
 )
 
 
-class TestEmployeeTaxExemptionProofSubmission(unittest.TestCase):
-	def setup(self):
-		make_employee("employee@proofsubmission.com")
-		create_payroll_period()
+class TestEmployeeTaxExemptionProofSubmission(FrappeTestCase):
+	def setUp(self):
+		make_employee("employee@proofsubmission.com", company="_Test Company")
+		create_payroll_period(company="_Test Company")
 		create_exemption_category()
-		frappe.db.sql("""delete from `tabEmployee Tax Exemption Proof Submission`""")
+		frappe.db.delete("Employee Tax Exemption Proof Submission")
+		frappe.db.delete("Salary Structure Assignment")
 
 	def test_exemption_amount_lesser_than_category_max(self):
-		declaration = frappe.get_doc({
-			"doctype": "Employee Tax Exemption Proof Submission",
-			"employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"),
-			"payroll_period": "Test Payroll Period",
-			"tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category",
-							type_of_proof = "Test Proof",
-							exemption_category = "_Test Category",
-							amount = 150000)]
-		})
-		self.assertRaises(frappe.ValidationError, declaration.save)
-		declaration = frappe.get_doc({
-			"doctype": "Employee Tax Exemption Proof Submission",
-			"payroll_period": "Test Payroll Period",
-			"employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"),
-			"tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category",
-							type_of_proof = "Test Proof",
-							exemption_category = "_Test Category",
-							amount = 100000)]
-		})
-		self.assertTrue(declaration.save)
-		self.assertTrue(declaration.submit)
+		proof = frappe.get_doc(
+			{
+				"doctype": "Employee Tax Exemption Proof Submission",
+				"employee": frappe.get_value("Employee", {"user_id": "employee@proofsubmission.com"}, "name"),
+				"payroll_period": "Test Payroll Period",
+				"tax_exemption_proofs": [
+					dict(
+						exemption_sub_category="_Test Sub Category",
+						type_of_proof="Test Proof",
+						exemption_category="_Test Category",
+						amount=150000,
+					)
+				],
+			}
+		)
+		self.assertRaises(frappe.ValidationError, proof.save)
+		proof = frappe.get_doc(
+			{
+				"doctype": "Employee Tax Exemption Proof Submission",
+				"payroll_period": "Test Payroll Period",
+				"employee": frappe.get_value("Employee", {"user_id": "employee@proofsubmission.com"}, "name"),
+				"tax_exemption_proofs": [
+					dict(
+						exemption_sub_category="_Test Sub Category",
+						type_of_proof="Test Proof",
+						exemption_category="_Test Category",
+						amount=100000,
+					)
+				],
+			}
+		)
+		self.assertTrue(proof.save)
+		self.assertTrue(proof.submit)
 
 	def test_duplicate_category_in_proof_submission(self):
-		declaration = frappe.get_doc({
-			"doctype": "Employee Tax Exemption Proof Submission",
-			"employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"),
-			"payroll_period": "Test Payroll Period",
-			"tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category",
-							exemption_category = "_Test Category",
-							type_of_proof = "Test Proof",
-							amount = 100000),
-							dict(exemption_sub_category = "_Test Sub Category",
-							exemption_category = "_Test Category",
-							amount = 50000),
-							]
-		})
-		self.assertRaises(frappe.ValidationError, declaration.save)
+		proof = frappe.get_doc(
+			{
+				"doctype": "Employee Tax Exemption Proof Submission",
+				"employee": frappe.get_value("Employee", {"user_id": "employee@proofsubmission.com"}, "name"),
+				"payroll_period": "Test Payroll Period",
+				"tax_exemption_proofs": [
+					dict(
+						exemption_sub_category="_Test Sub Category",
+						exemption_category="_Test Category",
+						type_of_proof="Test Proof",
+						amount=100000,
+					),
+					dict(
+						exemption_sub_category="_Test Sub Category",
+						exemption_category="_Test Category",
+						amount=50000,
+					),
+				],
+			}
+		)
+		self.assertRaises(frappe.ValidationError, proof.save)
+
+	def test_india_hra_exemption(self):
+		# set country
+		current_country = frappe.flags.country
+		frappe.flags.country = "India"
+
+		employee = frappe.get_value("Employee", {"user_id": "employee@proofsubmission.com"}, "name")
+		setup_hra_exemption_prerequisites("Monthly", employee)
+		payroll_period = frappe.db.get_value(
+			"Payroll Period", "_Test Payroll Period", ["start_date", "end_date"], as_dict=True
+		)
+
+		proof = frappe.get_doc(
+			{
+				"doctype": "Employee Tax Exemption Proof Submission",
+				"employee": employee,
+				"company": "_Test Company",
+				"payroll_period": "_Test Payroll Period",
+				"currency": "INR",
+				"house_rent_payment_amount": 600000,
+				"rented_in_metro_city": 1,
+				"rented_from_date": payroll_period.start_date,
+				"rented_to_date": payroll_period.end_date,
+				"tax_exemption_proofs": [
+					dict(
+						exemption_sub_category="_Test Sub Category",
+						exemption_category="_Test Category",
+						type_of_proof="Test Proof",
+						amount=100000,
+					),
+					dict(
+						exemption_sub_category="_Test1 Sub Category",
+						exemption_category="_Test Category",
+						type_of_proof="Test Proof",
+						amount=50000,
+					),
+				],
+			}
+		).insert()
+
+		self.assertEqual(proof.monthly_house_rent, 50000)
+
+		# Monthly HRA received = 3000
+		# should set HRA exemption as per actual annual HRA because that's the minimum
+		self.assertEqual(proof.monthly_hra_exemption, 3000)
+		self.assertEqual(proof.total_eligible_hra_exemption, 36000)
+
+		# total exemptions + house rent payment amount
+		self.assertEqual(proof.total_actual_amount, 750000)
+
+		# 100000 Standard Exemption + 36000 HRA exemption
+		self.assertEqual(proof.exemption_amount, 136000)
+
+		# reset
+		frappe.flags.country = current_country
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py
index 4ac11f7..fb75d67 100644
--- a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py
+++ b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py
@@ -10,7 +10,12 @@
 
 class EmployeeTaxExemptionSubCategory(Document):
 	def validate(self):
-		category_max_amount = frappe.db.get_value("Employee Tax Exemption Category", self.exemption_category, "max_amount")
+		category_max_amount = frappe.db.get_value(
+			"Employee Tax Exemption Category", self.exemption_category, "max_amount"
+		)
 		if flt(self.max_amount) > flt(category_max_amount):
-			frappe.throw(_("Max Exemption Amount cannot be greater than maximum exemption amount {0} of Tax Exemption Category {1}")
-				.format(category_max_amount, self.exemption_category))
+			frappe.throw(
+				_(
+					"Max Exemption Amount cannot be greater than maximum exemption amount {0} of Tax Exemption Category {1}"
+				).format(category_max_amount, self.exemption_category)
+			)
diff --git a/erpnext/payroll/doctype/gratuity/gratuity.json b/erpnext/payroll/doctype/gratuity/gratuity.json
index 1fd1cec..c540baf 100644
--- a/erpnext/payroll/doctype/gratuity/gratuity.json
+++ b/erpnext/payroll/doctype/gratuity/gratuity.json
@@ -76,9 +76,8 @@
    "fieldtype": "Select",
    "in_list_view": 1,
    "label": "Status",
-   "options": "Draft\nUnpaid\nPaid",
-   "read_only": 1,
-   "reqd": 1
+   "options": "Draft\nUnpaid\nPaid\nSubmitted\nCancelled",
+   "read_only": 1
   },
   {
    "depends_on": "eval: !doc.pay_via_salary_slip",
@@ -194,7 +193,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-02-02 14:00:45.536152",
+ "modified": "2022-05-27 13:56:14.349183",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Gratuity",
diff --git a/erpnext/payroll/doctype/gratuity/gratuity.py b/erpnext/payroll/doctype/gratuity/gratuity.py
index 939634a..91740ae 100644
--- a/erpnext/payroll/doctype/gratuity/gratuity.py
+++ b/erpnext/payroll/doctype/gratuity/gratuity.py
@@ -27,7 +27,7 @@
 			self.create_gl_entries()
 
 	def on_cancel(self):
-		self.ignore_linked_doctypes = ['GL Entry']
+		self.ignore_linked_doctypes = ["GL Entry"]
 		self.create_gl_entries(cancel=True)
 
 	def create_gl_entries(self, cancel=False):
@@ -39,28 +39,34 @@
 		# payable entry
 		if self.amount:
 			gl_entry.append(
-				self.get_gl_dict({
-					"account": self.payable_account,
-					"credit": self.amount,
-					"credit_in_account_currency": self.amount,
-					"against": self.expense_account,
-					"party_type": "Employee",
-					"party": self.employee,
-					"against_voucher_type": self.doctype,
-					"against_voucher": self.name,
-					"cost_center": self.cost_center
-				}, item=self)
+				self.get_gl_dict(
+					{
+						"account": self.payable_account,
+						"credit": self.amount,
+						"credit_in_account_currency": self.amount,
+						"against": self.expense_account,
+						"party_type": "Employee",
+						"party": self.employee,
+						"against_voucher_type": self.doctype,
+						"against_voucher": self.name,
+						"cost_center": self.cost_center,
+					},
+					item=self,
+				)
 			)
 
 			# expense entries
 			gl_entry.append(
-				self.get_gl_dict({
-					"account": self.expense_account,
-					"debit": self.amount,
-					"debit_in_account_currency": self.amount,
-					"against": self.payable_account,
-					"cost_center": self.cost_center
-				}, item=self)
+				self.get_gl_dict(
+					{
+						"account": self.expense_account,
+						"debit": self.amount,
+						"debit_in_account_currency": self.amount,
+						"against": self.payable_account,
+						"cost_center": self.cost_center,
+					},
+					item=self,
+				)
 			)
 		else:
 			frappe.throw(_("Total Amount can not be zero"))
@@ -69,7 +75,7 @@
 
 	def create_additional_salary(self):
 		if self.pay_via_salary_slip:
-			additional_salary = frappe.new_doc('Additional Salary')
+			additional_salary = frappe.new_doc("Additional Salary")
 			additional_salary.employee = self.employee
 			additional_salary.salary_component = self.salary_component
 			additional_salary.overwrite_salary_structure_amount = 0
@@ -81,19 +87,22 @@
 			additional_salary.submit()
 
 	def set_total_advance_paid(self):
-		paid_amount = frappe.db.sql("""
+		paid_amount = frappe.db.sql(
+			"""
 			select ifnull(sum(debit_in_account_currency), 0) as paid_amount
 			from `tabGL Entry`
 			where against_voucher_type = 'Gratuity'
 				and against_voucher = %s
 				and party_type = 'Employee'
 				and party = %s
-		""", (self.name, self.employee), as_dict=1)[0].paid_amount
+		""",
+			(self.name, self.employee),
+			as_dict=1,
+		)[0].paid_amount
 
 		if flt(paid_amount) > self.amount:
 			frappe.throw(_("Row {0}# Paid Amount cannot be greater than Total amount"))
 
-
 		self.db_set("paid_amount", paid_amount)
 		if self.amount == self.paid_amount:
 			self.db_set("status", "Paid")
@@ -104,69 +113,97 @@
 	current_work_experience = calculate_work_experience(employee, gratuity_rule) or 0
 	gratuity_amount = calculate_gratuity_amount(employee, gratuity_rule, current_work_experience) or 0
 
-	return {'current_work_experience': current_work_experience, "amount": gratuity_amount}
+	return {"current_work_experience": current_work_experience, "amount": gratuity_amount}
+
 
 def calculate_work_experience(employee, gratuity_rule):
 
-	total_working_days_per_year, minimum_year_for_gratuity = frappe.db.get_value("Gratuity Rule", gratuity_rule, ["total_working_days_per_year", "minimum_year_for_gratuity"])
+	total_working_days_per_year, minimum_year_for_gratuity = frappe.db.get_value(
+		"Gratuity Rule", gratuity_rule, ["total_working_days_per_year", "minimum_year_for_gratuity"]
+	)
 
-	date_of_joining, relieving_date = frappe.db.get_value('Employee', employee, ['date_of_joining', 'relieving_date'])
+	date_of_joining, relieving_date = frappe.db.get_value(
+		"Employee", employee, ["date_of_joining", "relieving_date"]
+	)
 	if not relieving_date:
-		frappe.throw(_("Please set Relieving Date for employee: {0}").format(bold(get_link_to_form("Employee", employee))))
+		frappe.throw(
+			_("Please set Relieving Date for employee: {0}").format(
+				bold(get_link_to_form("Employee", employee))
+			)
+		)
 
-	method = frappe.db.get_value("Gratuity Rule", gratuity_rule, "work_experience_calculation_function")
-	employee_total_workings_days = calculate_employee_total_workings_days(employee, date_of_joining, relieving_date)
+	method = frappe.db.get_value(
+		"Gratuity Rule", gratuity_rule, "work_experience_calculation_function"
+	)
+	employee_total_workings_days = calculate_employee_total_workings_days(
+		employee, date_of_joining, relieving_date
+	)
 
-	current_work_experience = employee_total_workings_days/total_working_days_per_year or 1
-	current_work_experience = get_work_experience_using_method(method, current_work_experience, minimum_year_for_gratuity, employee)
+	current_work_experience = employee_total_workings_days / total_working_days_per_year or 1
+	current_work_experience = get_work_experience_using_method(
+		method, current_work_experience, minimum_year_for_gratuity, employee
+	)
 	return current_work_experience
 
-def calculate_employee_total_workings_days(employee, date_of_joining, relieving_date ):
+
+def calculate_employee_total_workings_days(employee, date_of_joining, relieving_date):
 	employee_total_workings_days = (get_datetime(relieving_date) - get_datetime(date_of_joining)).days
 
 	payroll_based_on = frappe.db.get_value("Payroll Settings", None, "payroll_based_on") or "Leave"
 	if payroll_based_on == "Leave":
 		total_lwp = get_non_working_days(employee, relieving_date, "On Leave")
 		employee_total_workings_days -= total_lwp
-	elif  payroll_based_on == "Attendance":
+	elif payroll_based_on == "Attendance":
 		total_absents = get_non_working_days(employee, relieving_date, "Absent")
 		employee_total_workings_days -= total_absents
 
 	return employee_total_workings_days
 
-def get_work_experience_using_method(method, current_work_experience, minimum_year_for_gratuity, employee):
+
+def get_work_experience_using_method(
+	method, current_work_experience, minimum_year_for_gratuity, employee
+):
 	if method == "Round off Work Experience":
 		current_work_experience = round(current_work_experience)
 	else:
 		current_work_experience = floor(current_work_experience)
 
 	if current_work_experience < minimum_year_for_gratuity:
-		frappe.throw(_("Employee: {0} have to complete minimum {1} years for gratuity").format(bold(employee), minimum_year_for_gratuity))
+		frappe.throw(
+			_("Employee: {0} have to complete minimum {1} years for gratuity").format(
+				bold(employee), minimum_year_for_gratuity
+			)
+		)
 	return current_work_experience
 
+
 def get_non_working_days(employee, relieving_date, status):
 
-	filters={
-			"docstatus": 1,
-			"status": status,
-			"employee": employee,
-			"attendance_date": ("<=", get_datetime(relieving_date))
-		}
+	filters = {
+		"docstatus": 1,
+		"status": status,
+		"employee": employee,
+		"attendance_date": ("<=", get_datetime(relieving_date)),
+	}
 
 	if status == "On Leave":
-		lwp_leave_types =  frappe.get_list("Leave Type", filters = {"is_lwp":1})
+		lwp_leave_types = frappe.get_list("Leave Type", filters={"is_lwp": 1})
 		lwp_leave_types = [leave_type.name for leave_type in lwp_leave_types]
-		filters["leave_type"] =  ("IN", lwp_leave_types)
+		filters["leave_type"] = ("IN", lwp_leave_types)
 
-
-	record = frappe.get_all("Attendance", filters=filters, fields = ["COUNT(name) as total_lwp"])
+	record = frappe.get_all("Attendance", filters=filters, fields=["COUNT(name) as total_lwp"])
 	return record[0].total_lwp if len(record) else 0
 
+
 def calculate_gratuity_amount(employee, gratuity_rule, experience):
 	applicable_earnings_component = get_applicable_components(gratuity_rule)
-	total_applicable_components_amount = get_total_applicable_component_amount(employee, applicable_earnings_component, gratuity_rule)
+	total_applicable_components_amount = get_total_applicable_component_amount(
+		employee, applicable_earnings_component, gratuity_rule
+	)
 
-	calculate_gratuity_amount_based_on = frappe.db.get_value("Gratuity Rule", gratuity_rule, "calculate_gratuity_amount_based_on")
+	calculate_gratuity_amount_based_on = frappe.db.get_value(
+		"Gratuity Rule", gratuity_rule, "calculate_gratuity_amount_based_on"
+	)
 	gratuity_amount = 0
 	slabs = get_gratuity_rule_slabs(gratuity_rule)
 	slab_found = False
@@ -174,49 +211,78 @@
 
 	for slab in slabs:
 		if calculate_gratuity_amount_based_on == "Current Slab":
-			slab_found, gratuity_amount = calculate_amount_based_on_current_slab(slab.from_year, slab.to_year,
-				experience, total_applicable_components_amount, slab.fraction_of_applicable_earnings)
+			slab_found, gratuity_amount = calculate_amount_based_on_current_slab(
+				slab.from_year,
+				slab.to_year,
+				experience,
+				total_applicable_components_amount,
+				slab.fraction_of_applicable_earnings,
+			)
 			if slab_found:
-					break
+				break
 
 		elif calculate_gratuity_amount_based_on == "Sum of all previous slabs":
 			if slab.to_year == 0 and slab.from_year == 0:
-				gratuity_amount += year_left * total_applicable_components_amount * slab.fraction_of_applicable_earnings
+				gratuity_amount += (
+					year_left * total_applicable_components_amount * slab.fraction_of_applicable_earnings
+				)
 				slab_found = True
 				break
 
-			if experience > slab.to_year and experience > slab.from_year and slab.to_year !=0:
-				gratuity_amount += (slab.to_year - slab.from_year) * total_applicable_components_amount * slab.fraction_of_applicable_earnings
-				year_left -= (slab.to_year - slab.from_year)
+			if experience > slab.to_year and experience > slab.from_year and slab.to_year != 0:
+				gratuity_amount += (
+					(slab.to_year - slab.from_year)
+					* total_applicable_components_amount
+					* slab.fraction_of_applicable_earnings
+				)
+				year_left -= slab.to_year - slab.from_year
 				slab_found = True
 			elif slab.from_year <= experience and (experience < slab.to_year or slab.to_year == 0):
-				gratuity_amount += year_left * total_applicable_components_amount * slab.fraction_of_applicable_earnings
+				gratuity_amount += (
+					year_left * total_applicable_components_amount * slab.fraction_of_applicable_earnings
+				)
 				slab_found = True
 
 	if not slab_found:
-		frappe.throw(_("No Suitable Slab found for Calculation of gratuity amount in Gratuity Rule: {0}").format(bold(gratuity_rule)))
+		frappe.throw(
+			_("No Suitable Slab found for Calculation of gratuity amount in Gratuity Rule: {0}").format(
+				bold(gratuity_rule)
+			)
+		)
 	return gratuity_amount
 
+
 def get_applicable_components(gratuity_rule):
-	applicable_earnings_component = frappe.get_all("Gratuity Applicable Component", filters= {'parent': gratuity_rule}, fields=["salary_component"])
+	applicable_earnings_component = frappe.get_all(
+		"Gratuity Applicable Component", filters={"parent": gratuity_rule}, fields=["salary_component"]
+	)
 	if len(applicable_earnings_component) == 0:
-		frappe.throw(_("No Applicable Earnings Component found for Gratuity Rule: {0}").format(bold(get_link_to_form("Gratuity Rule",gratuity_rule))))
-	applicable_earnings_component = [component.salary_component for component in applicable_earnings_component]
+		frappe.throw(
+			_("No Applicable Earnings Component found for Gratuity Rule: {0}").format(
+				bold(get_link_to_form("Gratuity Rule", gratuity_rule))
+			)
+		)
+	applicable_earnings_component = [
+		component.salary_component for component in applicable_earnings_component
+	]
 
 	return applicable_earnings_component
 
+
 def get_total_applicable_component_amount(employee, applicable_earnings_component, gratuity_rule):
-	sal_slip  = get_last_salary_slip(employee)
+	sal_slip = get_last_salary_slip(employee)
 	if not sal_slip:
 		frappe.throw(_("No Salary Slip is found for Employee: {0}").format(bold(employee)))
-	component_and_amounts = frappe.get_all("Salary Detail",
+	component_and_amounts = frappe.get_all(
+		"Salary Detail",
 		filters={
 			"docstatus": 1,
-			'parent': sal_slip,
+			"parent": sal_slip,
 			"parentfield": "earnings",
-			'salary_component': ('in', applicable_earnings_component)
+			"salary_component": ("in", applicable_earnings_component),
 		},
-		fields=["amount"])
+		fields=["amount"],
+	)
 	total_applicable_components_amount = 0
 	if not len(component_and_amounts):
 		frappe.throw(_("No Applicable Component is present in last month salary slip"))
@@ -224,30 +290,44 @@
 		total_applicable_components_amount += data.amount
 	return total_applicable_components_amount
 
-def calculate_amount_based_on_current_slab(from_year, to_year, experience, total_applicable_components_amount, fraction_of_applicable_earnings):
-	slab_found = False; gratuity_amount = 0
+
+def calculate_amount_based_on_current_slab(
+	from_year,
+	to_year,
+	experience,
+	total_applicable_components_amount,
+	fraction_of_applicable_earnings,
+):
+	slab_found = False
+	gratuity_amount = 0
 	if experience >= from_year and (to_year == 0 or experience < to_year):
-		gratuity_amount = total_applicable_components_amount * experience * fraction_of_applicable_earnings
+		gratuity_amount = (
+			total_applicable_components_amount * experience * fraction_of_applicable_earnings
+		)
 		if fraction_of_applicable_earnings:
 			slab_found = True
 
 	return slab_found, gratuity_amount
 
+
 def get_gratuity_rule_slabs(gratuity_rule):
-	return frappe.get_all("Gratuity Rule Slab", filters= {'parent': gratuity_rule}, fields = ["*"], order_by="idx")
+	return frappe.get_all(
+		"Gratuity Rule Slab", filters={"parent": gratuity_rule}, fields=["*"], order_by="idx"
+	)
+
 
 def get_salary_structure(employee):
-	return frappe.get_list("Salary Structure Assignment", filters = {
-			"employee": employee, 'docstatus': 1
-		},
+	return frappe.get_list(
+		"Salary Structure Assignment",
+		filters={"employee": employee, "docstatus": 1},
 		fields=["from_date", "salary_structure"],
-		order_by = "from_date desc")[0].salary_structure
+		order_by="from_date desc",
+	)[0].salary_structure
+
 
 def get_last_salary_slip(employee):
-	salary_slips = frappe.get_list("Salary Slip", filters = {
-			"employee": employee, 'docstatus': 1
-		},
-		order_by = "start_date desc"
+	salary_slips = frappe.get_list(
+		"Salary Slip", filters={"employee": employee, "docstatus": 1}, order_by="start_date desc"
 	)
 	if not salary_slips:
 		return
diff --git a/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py b/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py
index 771a6fe..9396461 100644
--- a/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py
+++ b/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py
@@ -3,14 +3,9 @@
 
 def get_data():
 	return {
-		'fieldname': 'reference_name',
-		'non_standard_fieldnames': {
-			'Additional Salary': 'ref_docname',
+		"fieldname": "reference_name",
+		"non_standard_fieldnames": {
+			"Additional Salary": "ref_docname",
 		},
-		'transactions': [
-			{
-				'label': _('Payment'),
-				'items': ['Payment Entry', 'Additional Salary']
-			}
-		]
+		"transactions": [{"label": _("Payment"), "items": ["Payment Entry", "Additional Salary"]}],
 	}
diff --git a/erpnext/payroll/doctype/gratuity/gratuity_list.js b/erpnext/payroll/doctype/gratuity/gratuity_list.js
new file mode 100644
index 0000000..20e3d5b
--- /dev/null
+++ b/erpnext/payroll/doctype/gratuity/gratuity_list.js
@@ -0,0 +1,12 @@
+frappe.listview_settings["Gratuity"] = {
+	get_indicator: function(doc) {
+		let status_color = {
+			"Draft": "red",
+			"Submitted": "blue",
+			"Cancelled": "red",
+			"Paid": "green",
+			"Unpaid": "orange",
+		};
+		return [__(doc.status), status_color[doc.status], "status,=,"+doc.status];
+	}
+};
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/gratuity/test_gratuity.py b/erpnext/payroll/doctype/gratuity/test_gratuity.py
index 90e8061..1155a06 100644
--- a/erpnext/payroll/doctype/gratuity/test_gratuity.py
+++ b/erpnext/payroll/doctype/gratuity/test_gratuity.py
@@ -4,121 +4,153 @@
 import unittest
 
 import frappe
-from frappe.utils import add_days, flt, get_datetime, getdate
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import add_days, add_months, floor, flt, get_datetime, get_first_day, getdate
 
 from erpnext.hr.doctype.employee.test_employee import make_employee
 from erpnext.hr.doctype.expense_claim.test_expense_claim import get_payable_account
+from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list
 from erpnext.payroll.doctype.gratuity.gratuity import get_last_salary_slip
 from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
 	make_deduction_salary_component,
 	make_earning_salary_component,
 	make_employee_salary_slip,
+	make_holiday_list,
 )
+from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip
 from erpnext.regional.united_arab_emirates.setup import create_gratuity_rule
 
 test_dependencies = ["Salary Component", "Salary Slip", "Account"]
-class TestGratuity(unittest.TestCase):
+
+
+class TestGratuity(FrappeTestCase):
 	def setUp(self):
 		frappe.db.delete("Gratuity")
+		frappe.db.delete("Salary Slip")
 		frappe.db.delete("Additional Salary", {"ref_doctype": "Gratuity"})
 
-		make_earning_salary_component(setup=True, test_tax=True, company_list=['_Test Company'])
-		make_deduction_salary_component(setup=True, test_tax=True, company_list=['_Test Company'])
+		make_earning_salary_component(
+			setup=True, test_tax=True, company_list=["_Test Company"], include_flexi_benefits=True
+		)
+		make_deduction_salary_component(setup=True, test_tax=True, company_list=["_Test Company"])
+		make_holiday_list()
 
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
 	def test_get_last_salary_slip_should_return_none_for_new_employee(self):
-		new_employee = make_employee("new_employee@salary.com", company='_Test Company')
+		new_employee = make_employee("new_employee@salary.com", company="_Test Company")
 		salary_slip = get_last_salary_slip(new_employee)
-		assert salary_slip is None
+		self.assertIsNone(salary_slip)
 
-	def test_check_gratuity_amount_based_on_current_slab_and_additional_salary_creation(self):
-		employee, sal_slip = create_employee_and_get_last_salary_slip()
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
+	def test_gratuity_based_on_current_slab_via_additional_salary(self):
+		"""
+		Range	|	Fraction
+		5-0		|	1
+		"""
+		doj = add_days(getdate(), -(6 * 365))
+		relieving_date = getdate()
+
+		employee = make_employee(
+			"test_employee_gratuity@salary.com",
+			company="_Test Company",
+			date_of_joining=doj,
+			relieving_date=relieving_date,
+		)
+		sal_slip = create_salary_slip("test_employee_gratuity@salary.com")
 
 		rule = get_gratuity_rule("Rule Under Unlimited Contract on termination (UAE)")
 		gratuity = create_gratuity(pay_via_salary_slip=1, employee=employee, rule=rule.name)
 
 		# work experience calculation
-		date_of_joining, relieving_date = frappe.db.get_value('Employee', employee, ['date_of_joining', 'relieving_date'])
-		employee_total_workings_days = (get_datetime(relieving_date) - get_datetime(date_of_joining)).days
+		employee_total_workings_days = (get_datetime(relieving_date) - get_datetime(doj)).days
+		experience = floor(employee_total_workings_days / rule.total_working_days_per_year)
+		self.assertEqual(gratuity.current_work_experience, experience)
 
-		experience = employee_total_workings_days/rule.total_working_days_per_year
-		gratuity.reload()
-		from math import floor
-		self.assertEqual(floor(experience), gratuity.current_work_experience)
-
-		#amount Calculation
-		component_amount = frappe.get_all("Salary Detail",
-		filters={
-			"docstatus": 1,
-			'parent': sal_slip,
-			"parentfield": "earnings",
-			'salary_component': "Basic Salary"
-		},
-		fields=["amount"])
-
-		''' 5 - 0 fraction is 1 '''
-
+		# amount calculation
+		component_amount = frappe.get_all(
+			"Salary Detail",
+			filters={
+				"docstatus": 1,
+				"parent": sal_slip,
+				"parentfield": "earnings",
+				"salary_component": "Basic Salary",
+			},
+			fields=["amount"],
+			limit=1,
+		)
 		gratuity_amount = component_amount[0].amount * experience
-		gratuity.reload()
-
 		self.assertEqual(flt(gratuity_amount, 2), flt(gratuity.amount, 2))
 
 		# additional salary creation (Pay via salary slip)
 		self.assertTrue(frappe.db.exists("Additional Salary", {"ref_docname": gratuity.name}))
 
-	def test_check_gratuity_amount_based_on_all_previous_slabs(self):
-		employee, sal_slip = create_employee_and_get_last_salary_slip()
+		# gratuity should be marked "Paid" on the next salary slip submission
+		salary_slip = make_salary_slip("Test Gratuity", employee=employee)
+		salary_slip.posting_date = getdate()
+		salary_slip.insert()
+		salary_slip.submit()
+
+		gratuity.reload()
+		self.assertEqual(gratuity.status, "Paid")
+
+	@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
+	def test_gratuity_based_on_all_previous_slabs_via_payment_entry(self):
+		"""
+		Range	|	Fraction
+		0-1		|	0
+		1-5		|	0.7
+		5-0		|	1
+		"""
+		from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
+
+		doj = add_days(getdate(), -(6 * 365))
+		relieving_date = getdate()
+
+		employee = make_employee(
+			"test_employee_gratuity@salary.com",
+			company="_Test Company",
+			date_of_joining=doj,
+			relieving_date=relieving_date,
+		)
+
+		sal_slip = create_salary_slip("test_employee_gratuity@salary.com")
 		rule = get_gratuity_rule("Rule Under Limited Contract (UAE)")
 		set_mode_of_payment_account()
 
-		gratuity = create_gratuity(expense_account = 'Payment Account - _TC', mode_of_payment='Cash', employee=employee)
+		gratuity = create_gratuity(
+			expense_account="Payment Account - _TC", mode_of_payment="Cash", employee=employee
+		)
 
-		#work experience calculation
-		date_of_joining, relieving_date = frappe.db.get_value('Employee', employee, ['date_of_joining', 'relieving_date'])
-		employee_total_workings_days = (get_datetime(relieving_date) - get_datetime(date_of_joining)).days
+		# work experience calculation
+		employee_total_workings_days = (get_datetime(relieving_date) - get_datetime(doj)).days
+		experience = floor(employee_total_workings_days / rule.total_working_days_per_year)
+		self.assertEqual(gratuity.current_work_experience, experience)
 
-		experience = employee_total_workings_days/rule.total_working_days_per_year
+		# amount calculation
+		component_amount = frappe.get_all(
+			"Salary Detail",
+			filters={
+				"docstatus": 1,
+				"parent": sal_slip,
+				"parentfield": "earnings",
+				"salary_component": "Basic Salary",
+			},
+			fields=["amount"],
+			limit=1,
+		)
 
-		gratuity.reload()
-
-		from math import floor
-
-		self.assertEqual(floor(experience), gratuity.current_work_experience)
-
-		#amount Calculation
-		component_amount = frappe.get_all("Salary Detail",
-		filters={
-			"docstatus": 1,
-			'parent': sal_slip,
-			"parentfield": "earnings",
-			'salary_component': "Basic Salary"
-		},
-		fields=["amount"])
-
-		''' range  | Fraction
-			0-1    |    0
-			1-5    |   0.7
-			5-0    |    1
-		'''
-
-		gratuity_amount = ((0 * 1) + (4 * 0.7) + (1 * 1)) *  component_amount[0].amount
-		gratuity.reload()
-
+		gratuity_amount = ((0 * 1) + (4 * 0.7) + (1 * 1)) * component_amount[0].amount
 		self.assertEqual(flt(gratuity_amount, 2), flt(gratuity.amount, 2))
 		self.assertEqual(gratuity.status, "Unpaid")
 
-		from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
-		pay_entry = get_payment_entry("Gratuity", gratuity.name)
-		pay_entry.reference_no = "123467"
-		pay_entry.reference_date = getdate()
-		pay_entry.save()
-		pay_entry.submit()
+		pe = get_payment_entry("Gratuity", gratuity.name)
+		pe.reference_no = "123467"
+		pe.reference_date = getdate()
+		pe.submit()
+
 		gratuity.reload()
-
 		self.assertEqual(gratuity.status, "Paid")
-		self.assertEqual(flt(gratuity.paid_amount,2), flt(gratuity.amount, 2))
-
-	def tearDown(self):
-		frappe.db.rollback()
+		self.assertEqual(flt(gratuity.paid_amount, 2), flt(gratuity.amount, 2))
 
 
 def get_gratuity_rule(name):
@@ -127,14 +159,12 @@
 		create_gratuity_rule()
 	rule = frappe.get_doc("Gratuity Rule", name)
 	rule.applicable_earnings_component = []
-	rule.append("applicable_earnings_component", {
-		"salary_component": "Basic Salary"
-	})
+	rule.append("applicable_earnings_component", {"salary_component": "Basic Salary"})
 	rule.save()
-	rule.reload()
 
 	return rule
 
+
 def create_gratuity(**args):
 	if args:
 		args = frappe._dict(args)
@@ -147,15 +177,16 @@
 		gratuity.payroll_date = getdate()
 		gratuity.salary_component = "Performance Bonus"
 	else:
-		gratuity.expense_account = args.expense_account or 'Payment Account - _TC'
+		gratuity.expense_account = args.expense_account or "Payment Account - _TC"
 		gratuity.payable_account = args.payable_account or get_payable_account("_Test Company")
-		gratuity.mode_of_payment = args.mode_of_payment or 'Cash'
+		gratuity.mode_of_payment = args.mode_of_payment or "Cash"
 
 	gratuity.save()
 	gratuity.submit()
 
 	return gratuity
 
+
 def set_mode_of_payment_account():
 	if not frappe.db.exists("Account", "Payment Account - _TC"):
 		mode_of_payment = create_account()
@@ -163,14 +194,15 @@
 	mode_of_payment = frappe.get_doc("Mode of Payment", "Cash")
 
 	mode_of_payment.accounts = []
-	mode_of_payment.append("accounts", {
-		"company": "_Test Company",
-		"default_account": "_Test Bank - _TC"
-	})
+	mode_of_payment.append(
+		"accounts", {"company": "_Test Company", "default_account": "_Test Bank - _TC"}
+	)
 	mode_of_payment.save()
 
+
 def create_account():
-	return frappe.get_doc({
+	return frappe.get_doc(
+		{
 			"doctype": "Account",
 			"company": "_Test Company",
 			"account_name": "Payment Account",
@@ -179,22 +211,21 @@
 			"currency": "INR",
 			"parent_account": "Bank Accounts - _TC",
 			"account_type": "Bank",
-		}).insert(ignore_permissions=True)
+		}
+	).insert(ignore_permissions=True)
 
-def create_employee_and_get_last_salary_slip():
-	employee = make_employee("test_employee@salary.com", company='_Test Company')
-	frappe.db.set_value("Employee", employee, "relieving_date", getdate())
-	frappe.db.set_value("Employee", employee, "date_of_joining", add_days(getdate(), - (6*365)))
-	if not frappe.db.exists("Salary Slip", {"employee":employee}):
-		salary_slip = make_employee_salary_slip("test_employee@salary.com", "Monthly")
+
+def create_salary_slip(employee):
+	if not frappe.db.exists("Salary Slip", {"employee": employee}):
+		posting_date = get_first_day(add_months(getdate(), -1))
+		salary_slip = make_employee_salary_slip(
+			employee, "Monthly", "Test Gratuity", posting_date=posting_date
+		)
+		salary_slip.start_date = posting_date
+		salary_slip.end_date = None
 		salary_slip.submit()
 		salary_slip = salary_slip.name
 	else:
 		salary_slip = get_last_salary_slip(employee)
 
-	if not frappe.db.get_value("Employee", "test_employee@salary.com", "holiday_list"):
-		from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list
-		make_holiday_list()
-		frappe.db.set_value("Company", '_Test Company', "default_holiday_list", "Salary Slip Test Holiday List")
-
-	return employee, salary_slip
+	return salary_slip
diff --git a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js
index 014a121..7290a9e 100644
--- a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js
+++ b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js
@@ -34,7 +34,7 @@
 	to_year(frm, cdt, cdn) {
 		let row = locals[cdt][cdn];
 		if (row.to_year <= row.from_year && row.to_year === 0) {
-			frappe.throw(__("To(Year) year can not be less than From(year) "));
+			frappe.throw(__("To(Year) year can not be less than From(year)"));
 		}
 	}
 });
diff --git a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py
index d30cfc6..5cde79a 100644
--- a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py
+++ b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py
@@ -8,25 +8,34 @@
 
 
 class GratuityRule(Document):
-
 	def validate(self):
 		for current_slab in self.gratuity_rule_slabs:
 			if (current_slab.from_year > current_slab.to_year) and current_slab.to_year != 0:
-				frappe.throw(_("Row {0}: From (Year) can not be greater than To (Year)").format(current_slab.idx))
+				frappe.throw(
+					_("Row {0}: From (Year) can not be greater than To (Year)").format(current_slab.idx)
+				)
 
-			if current_slab.to_year == 0 and current_slab.from_year == 0 and len(self.gratuity_rule_slabs) > 1:
-				frappe.throw(_("You can not define multiple slabs if you have a slab with no lower and upper limits."))
+			if (
+				current_slab.to_year == 0 and current_slab.from_year == 0 and len(self.gratuity_rule_slabs) > 1
+			):
+				frappe.throw(
+					_("You can not define multiple slabs if you have a slab with no lower and upper limits.")
+				)
+
 
 def get_gratuity_rule(name, slabs, **args):
 	args = frappe._dict(args)
 
 	rule = frappe.new_doc("Gratuity Rule")
 	rule.name = name
-	rule.calculate_gratuity_amount_based_on = args.calculate_gratuity_amount_based_on or "Current Slab"
-	rule.work_experience_calculation_method = args.work_experience_calculation_method or "Take Exact Completed Years"
+	rule.calculate_gratuity_amount_based_on = (
+		args.calculate_gratuity_amount_based_on or "Current Slab"
+	)
+	rule.work_experience_calculation_method = (
+		args.work_experience_calculation_method or "Take Exact Completed Years"
+	)
 	rule.minimum_year_for_gratuity = 1
 
-
 	for slab in slabs:
 		slab = frappe._dict(slab)
 		rule.append("gratuity_rule_slabs", slab)
diff --git a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py
index e7c67fb..fa5a9de 100644
--- a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py
+++ b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py
@@ -3,11 +3,6 @@
 
 def get_data():
 	return {
-		'fieldname': 'gratuity_rule',
-		'transactions': [
-			{
-				'label': _('Gratuity'),
-				'items': ['Gratuity']
-			}
-		]
+		"fieldname": "gratuity_rule",
+		"transactions": [{"label": _("Gratuity"), "items": ["Gratuity"]}],
 	}
diff --git a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py
index 040b2c8..e62d61f 100644
--- a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py
+++ b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py
@@ -4,7 +4,7 @@
 
 from frappe.model.document import Document
 
-#import frappe
+# import frappe
 import erpnext
 
 
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js
index 496c37b..b06f350 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js
@@ -40,30 +40,69 @@
 	},
 
 	refresh: function (frm) {
-		if (frm.doc.docstatus == 0) {
-			if (!frm.is_new()) {
+		if (frm.doc.docstatus === 0 && !frm.is_new()) {
+			frm.page.clear_primary_action();
+			frm.add_custom_button(__("Get Employees"),
+				function () {
+					frm.events.get_employee_details(frm);
+				}
+			).toggleClass("btn-primary", !(frm.doc.employees || []).length);
+		}
+
+		if (
+			(frm.doc.employees || []).length
+			&& !frappe.model.has_workflow(frm.doctype)
+			&& !cint(frm.doc.salary_slips_created)
+			&& (frm.doc.docstatus != 2)
+		) {
+			if (frm.doc.docstatus == 0) {
 				frm.page.clear_primary_action();
-				frm.add_custom_button(__("Get Employees"),
-					function () {
-						frm.events.get_employee_details(frm);
-					}
-				).toggleClass('btn-primary', !(frm.doc.employees || []).length);
-			}
-			if ((frm.doc.employees || []).length && !frappe.model.has_workflow(frm.doctype)) {
-				frm.page.clear_primary_action();
-				frm.page.set_primary_action(__('Create Salary Slips'), () => {
-					frm.save('Submit').then(() => {
+				frm.page.set_primary_action(__("Create Salary Slips"), () => {
+					frm.save("Submit").then(() => {
 						frm.page.clear_primary_action();
 						frm.refresh();
 						frm.events.refresh(frm);
 					});
 				});
+			} else if (frm.doc.docstatus == 1 && frm.doc.status == "Failed") {
+				frm.add_custom_button(__("Create Salary Slip"), function () {
+					frm.call("create_salary_slips", {}, () => {
+						frm.reload_doc();
+					});
+				}).addClass("btn-primary");
 			}
 		}
-		if (frm.doc.docstatus == 1) {
+
+		if (frm.doc.docstatus == 1 && frm.doc.status == "Submitted") {
 			if (frm.custom_buttons) frm.clear_custom_buttons();
 			frm.events.add_context_buttons(frm);
 		}
+
+		if (frm.doc.status == "Failed" && frm.doc.error_message) {
+			const issue = `<a id="jump_to_error" style="text-decoration: underline;">issue</a>`;
+			let process = (cint(frm.doc.salary_slips_created)) ? "submission" : "creation";
+
+			frm.dashboard.set_headline(
+				__("Salary Slip {0} failed. You can resolve the {1} and retry {0}.", [process, issue])
+			);
+
+			$("#jump_to_error").on("click", (e) => {
+				e.preventDefault();
+				frappe.utils.scroll_to(
+					frm.get_field("error_message").$wrapper,
+					true,
+					30
+				);
+			});
+		}
+
+		frappe.realtime.on("completed_salary_slip_creation", function() {
+			frm.reload_doc();
+		});
+
+		frappe.realtime.on("completed_salary_slip_submission", function() {
+			frm.reload_doc();
+		});
 	},
 
 	get_employee_details: function (frm) {
@@ -88,7 +127,7 @@
 			doc: frm.doc,
 			method: "create_salary_slips",
 			callback: function () {
-				frm.refresh();
+				frm.reload_doc();
 				frm.toolbar.refresh();
 			}
 		});
@@ -97,7 +136,7 @@
 	add_context_buttons: function (frm) {
 		if (frm.doc.salary_slips_submitted || (frm.doc.__onload && frm.doc.__onload.submitted_ss)) {
 			frm.events.add_bank_entry_button(frm);
-		} else if (frm.doc.salary_slips_created) {
+		} else if (frm.doc.salary_slips_created && frm.doc.status != 'Queued') {
 			frm.add_custom_button(__("Submit Salary Slip"), function () {
 				submit_salary_slip(frm);
 			}).addClass("btn-primary");
@@ -112,7 +151,7 @@
 			},
 			callback: function (r) {
 				if (r.message && !r.message.submitted) {
-					frm.add_custom_button("Make Bank Entry", function () {
+					frm.add_custom_button(__("Make Bank Entry"), function () {
 						make_bank_entry(frm);
 					}).addClass("btn-primary");
 				}
@@ -331,6 +370,7 @@
 				method: 'submit_salary_slips',
 				args: {},
 				callback: function () {
+					frm.reload_doc();
 					frm.events.refresh(frm);
 				},
 				doc: frm.doc,
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.json b/erpnext/payroll/doctype/payroll_entry/payroll_entry.json
index 0444134..17882eb 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.json
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.json
@@ -8,11 +8,11 @@
  "engine": "InnoDB",
  "field_order": [
   "section_break0",
-  "column_break0",
   "posting_date",
   "payroll_frequency",
   "company",
   "column_break1",
+  "status",
   "currency",
   "exchange_rate",
   "payroll_payable_account",
@@ -41,11 +41,14 @@
   "cost_center",
   "account",
   "payment_account",
-  "amended_from",
   "column_break_33",
   "bank_account",
   "salary_slips_created",
-  "salary_slips_submitted"
+  "salary_slips_submitted",
+  "failure_details_section",
+  "error_message",
+  "section_break_41",
+  "amended_from"
  ],
  "fields": [
   {
@@ -54,11 +57,6 @@
    "label": "Select Employees"
   },
   {
-   "fieldname": "column_break0",
-   "fieldtype": "Column Break",
-   "width": "50%"
-  },
-  {
    "default": "Today",
    "fieldname": "posting_date",
    "fieldtype": "Date",
@@ -231,6 +229,7 @@
    "fieldtype": "Check",
    "hidden": 1,
    "label": "Salary Slips Created",
+   "no_copy": 1,
    "read_only": 1
   },
   {
@@ -239,6 +238,7 @@
    "fieldtype": "Check",
    "hidden": 1,
    "label": "Salary Slips Submitted",
+   "no_copy": 1,
    "read_only": 1
   },
   {
@@ -284,15 +284,44 @@
    "label": "Payroll Payable Account",
    "options": "Account",
    "reqd": 1
+  },
+  {
+   "collapsible": 1,
+   "collapsible_depends_on": "error_message",
+   "depends_on": "eval:doc.status=='Failed';",
+   "fieldname": "failure_details_section",
+   "fieldtype": "Section Break",
+   "label": "Failure Details"
+  },
+  {
+   "depends_on": "eval:doc.status=='Failed';",
+   "fieldname": "error_message",
+   "fieldtype": "Small Text",
+   "label": "Error Message",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "section_break_41",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "label": "Status",
+   "options": "Draft\nSubmitted\nCancelled\nQueued\nFailed",
+   "print_hide": 1,
+   "read_only": 1
   }
  ],
  "icon": "fa fa-cog",
  "is_submittable": 1,
  "links": [],
- "modified": "2020-12-17 15:13:17.766210",
+ "modified": "2022-03-16 12:45:21.662765",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Payroll Entry",
+ "naming_rule": "Expression (old style)",
  "owner": "Administrator",
  "permissions": [
   {
@@ -308,5 +337,6 @@
   }
  ],
  "sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "states": []
 }
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
index a634dfe..86a8c12 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
@@ -1,6 +1,7 @@
 # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
 
+import json
 
 import frappe
 from dateutil.relativedelta import relativedelta
@@ -16,6 +17,7 @@
 	comma_and,
 	date_diff,
 	flt,
+	get_link_to_form,
 	getdate,
 )
 
@@ -29,56 +31,96 @@
 
 class PayrollEntry(Document):
 	def onload(self):
-		if not self.docstatus==1 or self.salary_slips_submitted:
+		if not self.docstatus == 1 or self.salary_slips_submitted:
 			return
 
 		# check if salary slips were manually submitted
-		entries = frappe.db.count("Salary Slip", {'payroll_entry': self.name, 'docstatus': 1}, ['name'])
+		entries = frappe.db.count("Salary Slip", {"payroll_entry": self.name, "docstatus": 1}, ["name"])
 		if cint(entries) == len(self.employees):
 			self.set_onload("submitted_ss", True)
 
 	def validate(self):
 		self.number_of_employees = len(self.employees)
+		self.set_status()
 
 	def on_submit(self):
+		self.set_status(update=True, status="Submitted")
 		self.create_salary_slips()
 
 	def before_submit(self):
 		self.validate_employee_details()
+		self.validate_payroll_payable_account()
 		if self.validate_attendance:
 			if self.validate_employee_attendance():
 				frappe.throw(_("Cannot Submit, Employees left to mark attendance"))
 
+	def set_status(self, status=None, update=False):
+		if not status:
+			status = {0: "Draft", 1: "Submitted", 2: "Cancelled"}[self.docstatus or 0]
+
+		if update:
+			self.db_set("status", status)
+		else:
+			self.status = status
+
 	def validate_employee_details(self):
 		emp_with_sal_slip = []
 		for employee_details in self.employees:
-			if frappe.db.exists("Salary Slip", {"employee": employee_details.employee, "start_date": self.start_date, "end_date": self.end_date, "docstatus": 1}):
+			if frappe.db.exists(
+				"Salary Slip",
+				{
+					"employee": employee_details.employee,
+					"start_date": self.start_date,
+					"end_date": self.end_date,
+					"docstatus": 1,
+				},
+			):
 				emp_with_sal_slip.append(employee_details.employee)
 
 		if len(emp_with_sal_slip):
 			frappe.throw(_("Salary Slip already exists for {0}").format(comma_and(emp_with_sal_slip)))
 
+	def validate_payroll_payable_account(self):
+		if frappe.db.get_value("Account", self.payroll_payable_account, "account_type"):
+			frappe.throw(
+				_(
+					"Account type cannot be set for payroll payable account {0}, please remove and try again"
+				).format(frappe.bold(get_link_to_form("Account", self.payroll_payable_account)))
+			)
+
 	def on_cancel(self):
-		frappe.delete_doc("Salary Slip", frappe.db.sql_list("""select name from `tabSalary Slip`
-			where payroll_entry=%s """, (self.name)))
+		frappe.delete_doc(
+			"Salary Slip",
+			frappe.db.sql_list(
+				"""select name from `tabSalary Slip`
+			where payroll_entry=%s """,
+				(self.name),
+			),
+		)
 		self.db_set("salary_slips_created", 0)
 		self.db_set("salary_slips_submitted", 0)
+		self.set_status(update=True, status="Cancelled")
+		self.db_set("error_message", "")
 
 	def get_emp_list(self):
 		"""
-			Returns list of active employees based on selected criteria
-			and for which salary structure exists
+		Returns list of active employees based on selected criteria
+		and for which salary structure exists
 		"""
 		self.check_mandatory()
 		filters = self.make_filters()
 		cond = get_filter_condition(filters)
 		cond += get_joining_relieving_condition(self.start_date, self.end_date)
 
-		condition = ''
+		condition = ""
 		if self.payroll_frequency:
-			condition = """and payroll_frequency = '%(payroll_frequency)s'"""% {"payroll_frequency": self.payroll_frequency}
+			condition = """and payroll_frequency = '%(payroll_frequency)s'""" % {
+				"payroll_frequency": self.payroll_frequency
+			}
 
-		sal_struct = get_sal_struct(self.company, self.currency, self.salary_slip_based_on_timesheet, condition)
+		sal_struct = get_sal_struct(
+			self.company, self.currency, self.salary_slip_based_on_timesheet, condition
+		)
 		if sal_struct:
 			cond += "and t2.salary_structure IN %(sal_struct)s "
 			cond += "and t2.payroll_payable_account = %(payroll_payable_account)s "
@@ -89,20 +131,25 @@
 
 	def make_filters(self):
 		filters = frappe._dict()
-		filters['company'] = self.company
-		filters['branch'] = self.branch
-		filters['department'] = self.department
-		filters['designation'] = self.designation
+		filters["company"] = self.company
+		filters["branch"] = self.branch
+		filters["department"] = self.department
+		filters["designation"] = self.designation
 
 		return filters
 
 	@frappe.whitelist()
 	def fill_employee_details(self):
-		self.set('employees', [])
+		self.set("employees", [])
 		employees = self.get_emp_list()
 		if not employees:
-			error_msg = _("No employees found for the mentioned criteria:<br>Company: {0}<br> Currency: {1}<br>Payroll Payable Account: {2}").format(
-				frappe.bold(self.company), frappe.bold(self.currency), frappe.bold(self.payroll_payable_account))
+			error_msg = _(
+				"No employees found for the mentioned criteria:<br>Company: {0}<br> Currency: {1}<br>Payroll Payable Account: {2}"
+			).format(
+				frappe.bold(self.company),
+				frappe.bold(self.currency),
+				frappe.bold(self.payroll_payable_account),
+			)
 			if self.branch:
 				error_msg += "<br>" + _("Branch: {0}").format(frappe.bold(self.branch))
 			if self.department:
@@ -116,40 +163,54 @@
 			frappe.throw(error_msg, title=_("No employees found"))
 
 		for d in employees:
-			self.append('employees', d)
+			self.append("employees", d)
 
 		self.number_of_employees = len(self.employees)
 		if self.validate_attendance:
 			return self.validate_employee_attendance()
 
 	def check_mandatory(self):
-		for fieldname in ['company', 'start_date', 'end_date']:
+		for fieldname in ["company", "start_date", "end_date"]:
 			if not self.get(fieldname):
 				frappe.throw(_("Please set {0}").format(self.meta.get_label(fieldname)))
 
 	@frappe.whitelist()
 	def create_salary_slips(self):
 		"""
-			Creates salary slip for selected employees if already not created
+		Creates salary slip for selected employees if already not created
 		"""
-		self.check_permission('write')
+		self.check_permission("write")
 		employees = [emp.employee for emp in self.employees]
 		if employees:
-			args = frappe._dict({
-				"salary_slip_based_on_timesheet": self.salary_slip_based_on_timesheet,
-				"payroll_frequency": self.payroll_frequency,
-				"start_date": self.start_date,
-				"end_date": self.end_date,
-				"company": self.company,
-				"posting_date": self.posting_date,
-				"deduct_tax_for_unclaimed_employee_benefits": self.deduct_tax_for_unclaimed_employee_benefits,
-				"deduct_tax_for_unsubmitted_tax_exemption_proof": self.deduct_tax_for_unsubmitted_tax_exemption_proof,
-				"payroll_entry": self.name,
-				"exchange_rate": self.exchange_rate,
-				"currency": self.currency
-			})
-			if len(employees) > 30:
-				frappe.enqueue(create_salary_slips_for_employees, timeout=600, employees=employees, args=args)
+			args = frappe._dict(
+				{
+					"salary_slip_based_on_timesheet": self.salary_slip_based_on_timesheet,
+					"payroll_frequency": self.payroll_frequency,
+					"start_date": self.start_date,
+					"end_date": self.end_date,
+					"company": self.company,
+					"posting_date": self.posting_date,
+					"deduct_tax_for_unclaimed_employee_benefits": self.deduct_tax_for_unclaimed_employee_benefits,
+					"deduct_tax_for_unsubmitted_tax_exemption_proof": self.deduct_tax_for_unsubmitted_tax_exemption_proof,
+					"payroll_entry": self.name,
+					"exchange_rate": self.exchange_rate,
+					"currency": self.currency,
+				}
+			)
+			if len(employees) > 30 or frappe.flags.enqueue_payroll_entry:
+				self.db_set("status", "Queued")
+				frappe.enqueue(
+					create_salary_slips_for_employees,
+					timeout=600,
+					employees=employees,
+					args=args,
+					publish_progress=False,
+				)
+				frappe.msgprint(
+					_("Salary Slip creation is queued. It may take a few minutes"),
+					alert=True,
+					indicator="blue",
+				)
 			else:
 				create_salary_slips_for_employees(employees, args, publish_progress=False)
 				# since this method is called via frm.call this doc needs to be updated manually
@@ -157,33 +218,45 @@
 
 	def get_sal_slip_list(self, ss_status, as_dict=False):
 		"""
-			Returns list of salary slips based on selected criteria
+		Returns list of salary slips based on selected criteria
 		"""
 
 		ss = frappe.qb.DocType("Salary Slip")
 		ss_list = (
 			frappe.qb.from_(ss)
-				.select(ss.name, ss.salary_structure)
-				.where(
-					(ss.docstatus == ss_status)
-					& (ss.start_date >= self.start_date)
-					& (ss.end_date <= self.end_date)
-					& (ss.payroll_entry == self.name)
-					& ((ss.journal_entry.isnull()) | (ss.journal_entry == ""))
-					& (Coalesce(ss.salary_slip_based_on_timesheet, 0) == self.salary_slip_based_on_timesheet)
-				)
+			.select(ss.name, ss.salary_structure)
+			.where(
+				(ss.docstatus == ss_status)
+				& (ss.start_date >= self.start_date)
+				& (ss.end_date <= self.end_date)
+				& (ss.payroll_entry == self.name)
+				& ((ss.journal_entry.isnull()) | (ss.journal_entry == ""))
+				& (Coalesce(ss.salary_slip_based_on_timesheet, 0) == self.salary_slip_based_on_timesheet)
+			)
 		).run(as_dict=as_dict)
 
 		return ss_list
 
 	@frappe.whitelist()
 	def submit_salary_slips(self):
-		self.check_permission('write')
-		ss_list = self.get_sal_slip_list(ss_status=0)
-		if len(ss_list) > 30:
-			frappe.enqueue(submit_salary_slips_for_employees, timeout=600, payroll_entry=self, salary_slips=ss_list)
+		self.check_permission("write")
+		salary_slips = self.get_sal_slip_list(ss_status=0)
+		if len(salary_slips) > 30 or frappe.flags.enqueue_payroll_entry:
+			self.db_set("status", "Queued")
+			frappe.enqueue(
+				submit_salary_slips_for_employees,
+				timeout=600,
+				payroll_entry=self,
+				salary_slips=salary_slips,
+				publish_progress=False,
+			)
+			frappe.msgprint(
+				_("Salary Slip submission is queued. It may take a few minutes"),
+				alert=True,
+				indicator="blue",
+			)
 		else:
-			submit_salary_slips_for_employees(self, ss_list, publish_progress=False)
+			submit_salary_slips_for_employees(self, salary_slips, publish_progress=False)
 
 	def email_salary_slip(self, submitted_ss):
 		if frappe.db.get_single_value("Payroll Settings", "email_salary_slip_to_employee"):
@@ -191,82 +264,100 @@
 				ss.email_salary_slip()
 
 	def get_salary_component_account(self, salary_component):
-		account = frappe.db.get_value("Salary Component Account",
-			{"parent": salary_component, "company": self.company}, "account")
+		account = frappe.db.get_value(
+			"Salary Component Account", {"parent": salary_component, "company": self.company}, "account"
+		)
 
 		if not account:
-			frappe.throw(_("Please set account in Salary Component {0}")
-				.format(salary_component))
+			frappe.throw(
+				_("Please set account in Salary Component {0}").format(
+					get_link_to_form("Salary Component", salary_component)
+				)
+			)
 
 		return account
 
 	def get_salary_components(self, component_type):
-		salary_slips = self.get_sal_slip_list(ss_status = 1, as_dict = True)
+		salary_slips = self.get_sal_slip_list(ss_status=1, as_dict=True)
 
 		if salary_slips:
 			ss = frappe.qb.DocType("Salary Slip")
 			ssd = frappe.qb.DocType("Salary Detail")
 			salary_components = (
 				frappe.qb.from_(ss)
-					.join(ssd)
-					.on(ss.name == ssd.parent)
-					.select(ssd.salary_component, ssd.amount, ssd.parentfield, ss.salary_structure, ss.employee)
-					.where(
-						(ssd.parentfield == component_type)
-						& (ss.name.isin(tuple([d.name for d in salary_slips])))
-					)
+				.join(ssd)
+				.on(ss.name == ssd.parent)
+				.select(ssd.salary_component, ssd.amount, ssd.parentfield, ss.salary_structure, ss.employee)
+				.where(
+					(ssd.parentfield == component_type) & (ss.name.isin(tuple([d.name for d in salary_slips])))
+				)
 			).run(as_dict=True)
 
 			return salary_components
 
-	def get_salary_component_total(self, component_type = None):
+	def get_salary_component_total(self, component_type=None):
 		salary_components = self.get_salary_components(component_type)
 		if salary_components:
 			component_dict = {}
 			self.employee_cost_centers = {}
 			for item in salary_components:
-				employee_cost_centers = self.get_payroll_cost_centers_for_employee(item.employee, item.salary_structure)
+				employee_cost_centers = self.get_payroll_cost_centers_for_employee(
+					item.employee, item.salary_structure
+				)
 
 				add_component_to_accrual_jv_entry = True
 				if component_type == "earnings":
-					is_flexible_benefit, only_tax_impact = \
-						frappe.get_cached_value("Salary Component",item['salary_component'], ['is_flexible_benefit', 'only_tax_impact'])
-					if is_flexible_benefit == 1 and only_tax_impact ==1:
+					is_flexible_benefit, only_tax_impact = frappe.get_cached_value(
+						"Salary Component", item["salary_component"], ["is_flexible_benefit", "only_tax_impact"]
+					)
+					if is_flexible_benefit == 1 and only_tax_impact == 1:
 						add_component_to_accrual_jv_entry = False
 
 				if add_component_to_accrual_jv_entry:
 					for cost_center, percentage in employee_cost_centers.items():
 						amount_against_cost_center = flt(item.amount) * percentage / 100
-						component_dict[(item.salary_component, cost_center)] \
-							= component_dict.get((item.salary_component, cost_center), 0) + amount_against_cost_center
+						component_dict[(item.salary_component, cost_center)] = (
+							component_dict.get((item.salary_component, cost_center), 0) + amount_against_cost_center
+						)
 
-			account_details = self.get_account(component_dict = component_dict)
+			account_details = self.get_account(component_dict=component_dict)
 			return account_details
 
 	def get_payroll_cost_centers_for_employee(self, employee, salary_structure):
 		if not self.employee_cost_centers.get(employee):
-			ss_assignment_name = frappe.db.get_value("Salary Structure Assignment",
-				{"employee": employee, "salary_structure": salary_structure, "docstatus": 1}, 'name')
+			ss_assignment_name = frappe.db.get_value(
+				"Salary Structure Assignment",
+				{"employee": employee, "salary_structure": salary_structure, "docstatus": 1},
+				"name",
+			)
 
 			if ss_assignment_name:
-				cost_centers = dict(frappe.get_all("Employee Cost Center", {"parent": ss_assignment_name},
-					["cost_center", "percentage"], as_list=1))
+				cost_centers = dict(
+					frappe.get_all(
+						"Employee Cost Center",
+						{"parent": ss_assignment_name},
+						["cost_center", "percentage"],
+						as_list=1,
+					)
+				)
 				if not cost_centers:
-					default_cost_center, department = frappe.get_cached_value("Employee", employee, ["payroll_cost_center", "department"])
+					default_cost_center, department = frappe.get_cached_value(
+						"Employee", employee, ["payroll_cost_center", "department"]
+					)
 					if not default_cost_center and department:
-						default_cost_center = frappe.get_cached_value("Department", department, "payroll_cost_center")
+						default_cost_center = frappe.get_cached_value(
+							"Department", department, "payroll_cost_center"
+						)
 					if not default_cost_center:
 						default_cost_center = self.cost_center
 
-					cost_centers = {
-						default_cost_center: 100
-					}
+					cost_centers = {default_cost_center: 100}
 
 				self.employee_cost_centers.setdefault(employee, cost_centers)
 
 		return self.employee_cost_centers.get(employee, {})
 
-	def get_account(self, component_dict = None):
+	def get_account(self, component_dict=None):
 		account_dict = {}
 		for key, amount in component_dict.items():
 			account = self.get_salary_component_account(key[0])
@@ -275,8 +366,8 @@
 
 	def make_accrual_jv_entry(self):
 		self.check_permission("write")
-		earnings = self.get_salary_component_total(component_type = "earnings") or {}
-		deductions = self.get_salary_component_total(component_type = "deductions") or {}
+		earnings = self.get_salary_component_total(component_type="earnings") or {}
+		deductions = self.get_salary_component_total(component_type="deductions") or {}
 		payroll_payable_account = self.payroll_payable_account
 		jv_name = ""
 		precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency")
@@ -284,8 +375,9 @@
 		if earnings or deductions:
 			journal_entry = frappe.new_doc("Journal Entry")
 			journal_entry.voucher_type = "Journal Entry"
-			journal_entry.user_remark = _("Accrual Journal Entry for salaries from {0} to {1}")\
-				.format(self.start_date, self.end_date)
+			journal_entry.user_remark = _("Accrual Journal Entry for salaries from {0} to {1}").format(
+				self.start_date, self.end_date
+			)
 			journal_entry.company = self.company
 			journal_entry.posting_date = self.posting_date
 			accounting_dimensions = get_accounting_dimensions() or []
@@ -298,36 +390,57 @@
 
 			# Earnings
 			for acc_cc, amount in earnings.items():
-				exchange_rate, amt = self.get_amount_and_exchange_rate_for_journal_entry(acc_cc[0], amount, company_currency, currencies)
+				exchange_rate, amt = self.get_amount_and_exchange_rate_for_journal_entry(
+					acc_cc[0], amount, company_currency, currencies
+				)
 				payable_amount += flt(amount, precision)
-				accounts.append(self.update_accounting_dimensions({
-					"account": acc_cc[0],
-					"debit_in_account_currency": flt(amt, precision),
-					"exchange_rate": flt(exchange_rate),
-					"cost_center": acc_cc[1] or self.cost_center,
-					"project": self.project
-				}, accounting_dimensions))
+				accounts.append(
+					self.update_accounting_dimensions(
+						{
+							"account": acc_cc[0],
+							"debit_in_account_currency": flt(amt, precision),
+							"exchange_rate": flt(exchange_rate),
+							"cost_center": acc_cc[1] or self.cost_center,
+							"project": self.project,
+						},
+						accounting_dimensions,
+					)
+				)
 
 			# Deductions
 			for acc_cc, amount in deductions.items():
-				exchange_rate, amt = self.get_amount_and_exchange_rate_for_journal_entry(acc_cc[0], amount, company_currency, currencies)
+				exchange_rate, amt = self.get_amount_and_exchange_rate_for_journal_entry(
+					acc_cc[0], amount, company_currency, currencies
+				)
 				payable_amount -= flt(amount, precision)
-				accounts.append(self.update_accounting_dimensions({
-					"account": acc_cc[0],
-					"credit_in_account_currency": flt(amt, precision),
-					"exchange_rate": flt(exchange_rate),
-					"cost_center": acc_cc[1] or self.cost_center,
-					"project": self.project
-				}, accounting_dimensions))
+				accounts.append(
+					self.update_accounting_dimensions(
+						{
+							"account": acc_cc[0],
+							"credit_in_account_currency": flt(amt, precision),
+							"exchange_rate": flt(exchange_rate),
+							"cost_center": acc_cc[1] or self.cost_center,
+							"project": self.project,
+						},
+						accounting_dimensions,
+					)
+				)
 
 			# Payable amount
-			exchange_rate, payable_amt = self.get_amount_and_exchange_rate_for_journal_entry(payroll_payable_account, payable_amount, company_currency, currencies)
-			accounts.append(self.update_accounting_dimensions({
-				"account": payroll_payable_account,
-				"credit_in_account_currency": flt(payable_amt, precision),
-				"exchange_rate": flt(exchange_rate),
-				"cost_center": self.cost_center
-			}, accounting_dimensions))
+			exchange_rate, payable_amt = self.get_amount_and_exchange_rate_for_journal_entry(
+				payroll_payable_account, payable_amount, company_currency, currencies
+			)
+			accounts.append(
+				self.update_accounting_dimensions(
+					{
+						"account": payroll_payable_account,
+						"credit_in_account_currency": flt(payable_amt, precision),
+						"exchange_rate": flt(exchange_rate),
+						"cost_center": self.cost_center,
+					},
+					accounting_dimensions,
+				)
+			)
 
 			journal_entry.set("accounts", accounts)
 			if len(currencies) > 1:
@@ -339,7 +452,7 @@
 			try:
 				journal_entry.submit()
 				jv_name = journal_entry.name
-				self.update_salary_slip_status(jv_name = jv_name)
+				self.update_salary_slip_status(jv_name=jv_name)
 			except Exception as e:
 				if type(e) in (str, list, tuple):
 					frappe.msgprint(e)
@@ -353,10 +466,12 @@
 
 		return row
 
-	def get_amount_and_exchange_rate_for_journal_entry(self, account, amount, company_currency, currencies):
+	def get_amount_and_exchange_rate_for_journal_entry(
+		self, account, amount, company_currency, currencies
+	):
 		conversion_rate = 1
 		exchange_rate = self.exchange_rate
-		account_currency = frappe.db.get_value('Account', account, 'account_currency')
+		account_currency = frappe.db.get_value("Account", account, "account_currency")
 		if account_currency not in currencies:
 			currencies.append(account_currency)
 		if account_currency == company_currency:
@@ -367,26 +482,45 @@
 
 	@frappe.whitelist()
 	def make_payment_entry(self):
-		self.check_permission('write')
+		self.check_permission("write")
 
-		salary_slip_name_list = frappe.db.sql(""" select t1.name from `tabSalary Slip` t1
+		salary_slip_name_list = frappe.db.sql(
+			""" select t1.name from `tabSalary Slip` t1
 			where t1.docstatus = 1 and start_date >= %s and end_date <= %s and t1.payroll_entry = %s
-			""", (self.start_date, self.end_date, self.name), as_list = True)
+			""",
+			(self.start_date, self.end_date, self.name),
+			as_list=True,
+		)
 
 		if salary_slip_name_list and len(salary_slip_name_list) > 0:
 			salary_slip_total = 0
 			for salary_slip_name in salary_slip_name_list:
 				salary_slip = frappe.get_doc("Salary Slip", salary_slip_name[0])
 				for sal_detail in salary_slip.earnings:
-					is_flexible_benefit, only_tax_impact, creat_separate_je, statistical_component = frappe.db.get_value("Salary Component", sal_detail.salary_component,
-						['is_flexible_benefit', 'only_tax_impact', 'create_separate_payment_entry_against_benefit_claim', 'statistical_component'])
+					(
+						is_flexible_benefit,
+						only_tax_impact,
+						creat_separate_je,
+						statistical_component,
+					) = frappe.db.get_value(
+						"Salary Component",
+						sal_detail.salary_component,
+						[
+							"is_flexible_benefit",
+							"only_tax_impact",
+							"create_separate_payment_entry_against_benefit_claim",
+							"statistical_component",
+						],
+					)
 					if only_tax_impact != 1 and statistical_component != 1:
 						if is_flexible_benefit == 1 and creat_separate_je == 1:
 							self.create_journal_entry(sal_detail.amount, sal_detail.salary_component)
 						else:
 							salary_slip_total += sal_detail.amount
 				for sal_detail in salary_slip.deductions:
-					statistical_component = frappe.db.get_value("Salary Component", sal_detail.salary_component, 'statistical_component')
+					statistical_component = frappe.db.get_value(
+						"Salary Component", sal_detail.salary_component, "statistical_component"
+					)
 					if statistical_component != 1:
 						salary_slip_total -= sal_detail.amount
 			if salary_slip_total > 0:
@@ -402,91 +536,114 @@
 		company_currency = erpnext.get_company_currency(self.company)
 		accounting_dimensions = get_accounting_dimensions() or []
 
-		exchange_rate, amount = self.get_amount_and_exchange_rate_for_journal_entry(self.payment_account, je_payment_amount, company_currency, currencies)
-		accounts.append(self.update_accounting_dimensions({
-			"account": self.payment_account,
-			"bank_account": self.bank_account,
-			"credit_in_account_currency": flt(amount, precision),
-			"exchange_rate": flt(exchange_rate),
-		}, accounting_dimensions))
+		exchange_rate, amount = self.get_amount_and_exchange_rate_for_journal_entry(
+			self.payment_account, je_payment_amount, company_currency, currencies
+		)
+		accounts.append(
+			self.update_accounting_dimensions(
+				{
+					"account": self.payment_account,
+					"bank_account": self.bank_account,
+					"credit_in_account_currency": flt(amount, precision),
+					"exchange_rate": flt(exchange_rate),
+				},
+				accounting_dimensions,
+			)
+		)
 
-		exchange_rate, amount = self.get_amount_and_exchange_rate_for_journal_entry(payroll_payable_account, je_payment_amount, company_currency, currencies)
-		accounts.append(self.update_accounting_dimensions({
-			"account": payroll_payable_account,
-			"debit_in_account_currency": flt(amount, precision),
-			"exchange_rate": flt(exchange_rate),
-			"reference_type": self.doctype,
-			"reference_name": self.name
-		}, accounting_dimensions))
+		exchange_rate, amount = self.get_amount_and_exchange_rate_for_journal_entry(
+			payroll_payable_account, je_payment_amount, company_currency, currencies
+		)
+		accounts.append(
+			self.update_accounting_dimensions(
+				{
+					"account": payroll_payable_account,
+					"debit_in_account_currency": flt(amount, precision),
+					"exchange_rate": flt(exchange_rate),
+					"reference_type": self.doctype,
+					"reference_name": self.name,
+				},
+				accounting_dimensions,
+			)
+		)
 
 		if len(currencies) > 1:
-				multi_currency = 1
+			multi_currency = 1
 
-		journal_entry = frappe.new_doc('Journal Entry')
-		journal_entry.voucher_type = 'Bank Entry'
-		journal_entry.user_remark = _('Payment of {0} from {1} to {2}')\
-			.format(user_remark, self.start_date, self.end_date)
+		journal_entry = frappe.new_doc("Journal Entry")
+		journal_entry.voucher_type = "Bank Entry"
+		journal_entry.user_remark = _("Payment of {0} from {1} to {2}").format(
+			user_remark, self.start_date, self.end_date
+		)
 		journal_entry.company = self.company
 		journal_entry.posting_date = self.posting_date
 		journal_entry.multi_currency = multi_currency
 
 		journal_entry.set("accounts", accounts)
-		journal_entry.save(ignore_permissions = True)
+		journal_entry.save(ignore_permissions=True)
 
-	def update_salary_slip_status(self, jv_name = None):
+	def update_salary_slip_status(self, jv_name=None):
 		ss_list = self.get_sal_slip_list(ss_status=1)
 		for ss in ss_list:
-			ss_obj = frappe.get_doc("Salary Slip",ss[0])
+			ss_obj = frappe.get_doc("Salary Slip", ss[0])
 			frappe.db.set_value("Salary Slip", ss_obj.name, "journal_entry", jv_name)
 
 	def set_start_end_dates(self):
-		self.update(get_start_end_dates(self.payroll_frequency,
-			self.start_date or self.posting_date, self.company))
+		self.update(
+			get_start_end_dates(self.payroll_frequency, self.start_date or self.posting_date, self.company)
+		)
 
 	@frappe.whitelist()
 	def validate_employee_attendance(self):
 		employees_to_mark_attendance = []
 		days_in_payroll, days_holiday, days_attendance_marked = 0, 0, 0
 		for employee_detail in self.employees:
-			employee_joining_date = frappe.db.get_value("Employee", employee_detail.employee, 'date_of_joining')
+			employee_joining_date = frappe.db.get_value(
+				"Employee", employee_detail.employee, "date_of_joining"
+			)
 			start_date = self.start_date
 			if employee_joining_date > getdate(self.start_date):
 				start_date = employee_joining_date
 			days_holiday = self.get_count_holidays_of_employee(employee_detail.employee, start_date)
-			days_attendance_marked = self.get_count_employee_attendance(employee_detail.employee, start_date)
+			days_attendance_marked = self.get_count_employee_attendance(
+				employee_detail.employee, start_date
+			)
 			days_in_payroll = date_diff(self.end_date, start_date) + 1
 			if days_in_payroll > days_holiday + days_attendance_marked:
-				employees_to_mark_attendance.append({
-					"employee": employee_detail.employee,
-					"employee_name": employee_detail.employee_name
-				})
+				employees_to_mark_attendance.append(
+					{"employee": employee_detail.employee, "employee_name": employee_detail.employee_name}
+				)
 		return employees_to_mark_attendance
 
 	def get_count_holidays_of_employee(self, employee, start_date):
 		holiday_list = get_holiday_list_for_employee(employee)
 		holidays = 0
 		if holiday_list:
-			days = frappe.db.sql("""select count(*) from tabHoliday where
-				parent=%s and holiday_date between %s and %s""", (holiday_list,
-				start_date, self.end_date))
+			days = frappe.db.sql(
+				"""select count(*) from tabHoliday where
+				parent=%s and holiday_date between %s and %s""",
+				(holiday_list, start_date, self.end_date),
+			)
 			if days and days[0][0]:
 				holidays = days[0][0]
 		return holidays
 
 	def get_count_employee_attendance(self, employee, start_date):
 		marked_days = 0
-		attendances = frappe.get_all("Attendance",
-			fields = ["count(*)"],
-			filters = {
-				"employee": employee,
-				"attendance_date": ('between', [start_date, self.end_date])
-			}, as_list=1)
+		attendances = frappe.get_all(
+			"Attendance",
+			fields=["count(*)"],
+			filters={"employee": employee, "attendance_date": ("between", [start_date, self.end_date])},
+			as_list=1,
+		)
 		if attendances and attendances[0][0]:
 			marked_days = attendances[0][0]
 		return marked_days
 
+
 def get_sal_struct(company, currency, salary_slip_based_on_timesheet, condition):
-	return frappe.db.sql_list("""
+	return frappe.db.sql_list(
+		"""
 		select
 			name from `tabSalary Structure`
 		where
@@ -495,26 +652,40 @@
 			and company = %(company)s
 			and currency = %(currency)s and
 			ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s
-			{condition}""".format(condition=condition),
-		{"company": company, "currency": currency, "salary_slip_based_on_timesheet": salary_slip_based_on_timesheet})
+			{condition}""".format(
+			condition=condition
+		),
+		{
+			"company": company,
+			"currency": currency,
+			"salary_slip_based_on_timesheet": salary_slip_based_on_timesheet,
+		},
+	)
+
 
 def get_filter_condition(filters):
-	cond = ''
-	for f in ['company', 'branch', 'department', 'designation']:
+	cond = ""
+	for f in ["company", "branch", "department", "designation"]:
 		if filters.get(f):
 			cond += " and t1." + f + " = " + frappe.db.escape(filters.get(f))
 
 	return cond
 
+
 def get_joining_relieving_condition(start_date, end_date):
 	cond = """
-		and ifnull(t1.date_of_joining, '0000-00-00') <= '%(end_date)s'
+		and ifnull(t1.date_of_joining, '1900-01-01') <= '%(end_date)s'
 		and ifnull(t1.relieving_date, '2199-12-31') >= '%(start_date)s'
-	""" % {"start_date": start_date, "end_date": end_date}
+	""" % {
+		"start_date": start_date,
+		"end_date": end_date,
+	}
 	return cond
 
+
 def get_emp_list(sal_struct, cond, end_date, payroll_payable_account):
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 			select
 				distinct t1.name as employee, t1.employee_name, t1.department, t1.designation
 			from
@@ -524,19 +695,37 @@
 				and t2.docstatus = 1
 				and t1.status != 'Inactive'
 		%s order by t2.from_date desc
-		""" % cond, {"sal_struct": tuple(sal_struct), "from_date": end_date, "payroll_payable_account": payroll_payable_account}, as_dict=True)
+		"""
+		% cond,
+		{
+			"sal_struct": tuple(sal_struct),
+			"from_date": end_date,
+			"payroll_payable_account": payroll_payable_account,
+		},
+		as_dict=True,
+	)
+
 
 def remove_payrolled_employees(emp_list, start_date, end_date):
 	new_emp_list = []
 	for employee_details in emp_list:
-		if not frappe.db.exists("Salary Slip", {"employee": employee_details.employee, "start_date": start_date, "end_date": end_date, "docstatus": 1}):
+		if not frappe.db.exists(
+			"Salary Slip",
+			{
+				"employee": employee_details.employee,
+				"start_date": start_date,
+				"end_date": end_date,
+				"docstatus": 1,
+			},
+		):
 			new_emp_list.append(employee_details)
 
 	return new_emp_list
 
+
 @frappe.whitelist()
 def get_start_end_dates(payroll_frequency, start_date=None, company=None):
-	'''Returns dict of start and end dates for given payroll frequency based on start_date'''
+	"""Returns dict of start and end dates for given payroll frequency based on start_date"""
 
 	if payroll_frequency == "Monthly" or payroll_frequency == "Bimonthly" or payroll_frequency == "":
 		fiscal_year = get_fiscal_year(start_date, company=company)[0]
@@ -544,14 +733,14 @@
 		m = get_month_details(fiscal_year, month)
 		if payroll_frequency == "Bimonthly":
 			if getdate(start_date).day <= 15:
-				start_date = m['month_start_date']
-				end_date = m['month_mid_end_date']
+				start_date = m["month_start_date"]
+				end_date = m["month_mid_end_date"]
 			else:
-				start_date = m['month_mid_start_date']
-				end_date = m['month_end_date']
+				start_date = m["month_mid_start_date"]
+				end_date = m["month_end_date"]
 		else:
-			start_date = m['month_start_date']
-			end_date = m['month_end_date']
+			start_date = m["month_start_date"]
+			end_date = m["month_end_date"]
 
 	if payroll_frequency == "Weekly":
 		end_date = add_days(start_date, 6)
@@ -562,16 +751,15 @@
 	if payroll_frequency == "Daily":
 		end_date = start_date
 
-	return frappe._dict({
-		'start_date': start_date, 'end_date': end_date
-	})
+	return frappe._dict({"start_date": start_date, "end_date": end_date})
+
 
 def get_frequency_kwargs(frequency_name):
 	frequency_dict = {
-		'monthly': {'months': 1},
-		'fortnightly': {'days': 14},
-		'weekly': {'days': 7},
-		'daily': {'days': 1}
+		"monthly": {"months": 1},
+		"fortnightly": {"days": 14},
+		"weekly": {"days": 7},
+		"daily": {"days": 1},
 	}
 	return frequency_dict.get(frequency_name)
 
@@ -579,16 +767,18 @@
 @frappe.whitelist()
 def get_end_date(start_date, frequency):
 	start_date = getdate(start_date)
-	frequency = frequency.lower() if frequency else 'monthly'
-	kwargs = get_frequency_kwargs(frequency) if frequency != 'bimonthly' else get_frequency_kwargs('monthly')
+	frequency = frequency.lower() if frequency else "monthly"
+	kwargs = (
+		get_frequency_kwargs(frequency) if frequency != "bimonthly" else get_frequency_kwargs("monthly")
+	)
 
 	# weekly, fortnightly and daily intervals have fixed days so no problems
 	end_date = add_to_date(start_date, **kwargs) - relativedelta(days=1)
-	if frequency != 'bimonthly':
+	if frequency != "bimonthly":
 		return dict(end_date=end_date.strftime(DATE_FORMAT))
 
 	else:
-		return dict(end_date='')
+		return dict(end_date="")
 
 
 def get_month_details(year, month):
@@ -596,32 +786,36 @@
 	if ysd:
 		import calendar
 		import datetime
-		diff_mnt = cint(month)-cint(ysd.month)
-		if diff_mnt<0:
-			diff_mnt = 12-int(ysd.month)+cint(month)
-		msd = ysd + relativedelta(months=diff_mnt) # month start date
-		month_days = cint(calendar.monthrange(cint(msd.year) ,cint(month))[1]) # days in month
-		mid_start = datetime.date(msd.year, cint(month), 16) # month mid start date
-		mid_end = datetime.date(msd.year, cint(month), 15) # month mid end date
-		med = datetime.date(msd.year, cint(month), month_days) # month end date
-		return frappe._dict({
-			'year': msd.year,
-			'month_start_date': msd,
-			'month_end_date': med,
-			'month_mid_start_date': mid_start,
-			'month_mid_end_date': mid_end,
-			'month_days': month_days
-		})
+
+		diff_mnt = cint(month) - cint(ysd.month)
+		if diff_mnt < 0:
+			diff_mnt = 12 - int(ysd.month) + cint(month)
+		msd = ysd + relativedelta(months=diff_mnt)  # month start date
+		month_days = cint(calendar.monthrange(cint(msd.year), cint(month))[1])  # days in month
+		mid_start = datetime.date(msd.year, cint(month), 16)  # month mid start date
+		mid_end = datetime.date(msd.year, cint(month), 15)  # month mid end date
+		med = datetime.date(msd.year, cint(month), month_days)  # month end date
+		return frappe._dict(
+			{
+				"year": msd.year,
+				"month_start_date": msd,
+				"month_end_date": med,
+				"month_mid_start_date": mid_start,
+				"month_mid_end_date": mid_end,
+				"month_days": month_days,
+			}
+		)
 	else:
 		frappe.throw(_("Fiscal Year {0} not found").format(year))
 
+
 def get_payroll_entry_bank_entries(payroll_entry_name):
 	journal_entries = frappe.db.sql(
-		'select name from `tabJournal Entry Account` '
+		"select name from `tabJournal Entry Account` "
 		'where reference_type="Payroll Entry" '
-		'and reference_name=%s and docstatus=1',
+		"and reference_name=%s and docstatus=1",
 		payroll_entry_name,
-		as_dict=1
+		as_dict=1,
 	)
 
 	return journal_entries
@@ -631,103 +825,167 @@
 def payroll_entry_has_bank_entries(name):
 	response = {}
 	bank_entries = get_payroll_entry_bank_entries(name)
-	response['submitted'] = 1 if bank_entries else 0
+	response["submitted"] = 1 if bank_entries else 0
 
 	return response
 
+
+def log_payroll_failure(process, payroll_entry, error):
+	error_log = frappe.log_error(
+		title=_("Salary Slip {0} failed for Payroll Entry {1}").format(process, payroll_entry.name)
+	)
+	message_log = frappe.message_log.pop() if frappe.message_log else str(error)
+
+	try:
+		error_message = json.loads(message_log).get("message")
+	except Exception:
+		error_message = message_log
+
+	error_message += "\n" + _("Check Error Log {0} for more details.").format(
+		get_link_to_form("Error Log", error_log.name)
+	)
+
+	payroll_entry.db_set({"error_message": error_message, "status": "Failed"})
+
+
 def create_salary_slips_for_employees(employees, args, publish_progress=True):
-	salary_slips_exists_for = get_existing_salary_slips(employees, args)
-	count=0
-	salary_slips_not_created = []
-	for emp in employees:
-		if emp not in salary_slips_exists_for:
-			args.update({
-				"doctype": "Salary Slip",
-				"employee": emp
-			})
-			ss = frappe.get_doc(args)
-			ss.insert()
-			count+=1
-			if publish_progress:
-				frappe.publish_progress(count*100/len(set(employees) - set(salary_slips_exists_for)),
-					title = _("Creating Salary Slips..."))
+	try:
+		payroll_entry = frappe.get_doc("Payroll Entry", args.payroll_entry)
+		salary_slips_exist_for = get_existing_salary_slips(employees, args)
+		count = 0
 
-		else:
-			salary_slips_not_created.append(emp)
+		for emp in employees:
+			if emp not in salary_slips_exist_for:
+				args.update({"doctype": "Salary Slip", "employee": emp})
+				frappe.get_doc(args).insert()
 
-	payroll_entry = frappe.get_doc("Payroll Entry", args.payroll_entry)
-	payroll_entry.db_set("salary_slips_created", 1)
-	payroll_entry.notify_update()
+				count += 1
+				if publish_progress:
+					frappe.publish_progress(
+						count * 100 / len(set(employees) - set(salary_slips_exist_for)),
+						title=_("Creating Salary Slips..."),
+					)
 
-	if salary_slips_not_created:
-		frappe.msgprint(_("Salary Slips already exists for employees {}, and will not be processed by this payroll.")
-			.format(frappe.bold(", ".join([emp for emp in salary_slips_not_created]))) , title=_("Message"), indicator="orange")
+		payroll_entry.db_set({"status": "Submitted", "salary_slips_created": 1, "error_message": ""})
+
+		if salary_slips_exist_for:
+			frappe.msgprint(
+				_(
+					"Salary Slips already exist for employees {}, and will not be processed by this payroll."
+				).format(frappe.bold(", ".join(emp for emp in salary_slips_exist_for))),
+				title=_("Message"),
+				indicator="orange",
+			)
+
+	except Exception as e:
+		frappe.db.rollback()
+		log_payroll_failure("creation", payroll_entry, e)
+
+	finally:
+		frappe.db.commit()  # nosemgrep
+		frappe.publish_realtime("completed_salary_slip_creation")
+
+
+def show_payroll_submission_status(submitted, unsubmitted, payroll_entry):
+	if not submitted and not unsubmitted:
+		frappe.msgprint(
+			_(
+				"No salary slip found to submit for the above selected criteria OR salary slip already submitted"
+			)
+		)
+	elif submitted and not unsubmitted:
+		frappe.msgprint(
+			_("Salary Slips submitted for period from {0} to {1}").format(
+				payroll_entry.start_date, payroll_entry.end_date
+			)
+		)
+	elif unsubmitted:
+		frappe.msgprint(
+			_("Could not submit some Salary Slips: {}").format(
+				", ".join(get_link_to_form("Salary Slip", entry) for entry in unsubmitted)
+			)
+		)
+
 
 def get_existing_salary_slips(employees, args):
-	return frappe.db.sql_list("""
+	return frappe.db.sql_list(
+		"""
 		select distinct employee from `tabSalary Slip`
 		where docstatus!= 2 and company = %s and payroll_entry = %s
 			and start_date >= %s and end_date <= %s
 			and employee in (%s)
-	""" % ('%s', '%s', '%s', '%s', ', '.join(['%s']*len(employees))),
-		[args.company, args.payroll_entry, args.start_date, args.end_date] + employees)
+	"""
+		% ("%s", "%s", "%s", "%s", ", ".join(["%s"] * len(employees))),
+		[args.company, args.payroll_entry, args.start_date, args.end_date] + employees,
+	)
+
 
 def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progress=True):
-	submitted_ss = []
-	not_submitted_ss = []
-	frappe.flags.via_payroll_entry = True
+	try:
+		submitted = []
+		unsubmitted = []
+		frappe.flags.via_payroll_entry = True
+		count = 0
 
-	count = 0
-	for ss in salary_slips:
-		ss_obj = frappe.get_doc("Salary Slip",ss[0])
-		if ss_obj.net_pay<0:
-			not_submitted_ss.append(ss[0])
-		else:
-			try:
-				ss_obj.submit()
-				submitted_ss.append(ss_obj)
-			except frappe.ValidationError:
-				not_submitted_ss.append(ss[0])
+		for entry in salary_slips:
+			salary_slip = frappe.get_doc("Salary Slip", entry[0])
+			if salary_slip.net_pay < 0:
+				unsubmitted.append(entry[0])
+			else:
+				try:
+					salary_slip.submit()
+					submitted.append(salary_slip)
+				except frappe.ValidationError:
+					unsubmitted.append(entry[0])
 
-		count += 1
-		if publish_progress:
-			frappe.publish_progress(count*100/len(salary_slips), title = _("Submitting Salary Slips..."))
-	if submitted_ss:
-		payroll_entry.make_accrual_jv_entry()
-		frappe.msgprint(_("Salary Slip submitted for period from {0} to {1}")
-			.format(ss_obj.start_date, ss_obj.end_date))
+			count += 1
+			if publish_progress:
+				frappe.publish_progress(count * 100 / len(salary_slips), title=_("Submitting Salary Slips..."))
 
-		payroll_entry.email_salary_slip(submitted_ss)
+		if submitted:
+			payroll_entry.make_accrual_jv_entry()
+			payroll_entry.email_salary_slip(submitted)
+			payroll_entry.db_set({"salary_slips_submitted": 1, "status": "Submitted", "error_message": ""})
 
-		payroll_entry.db_set("salary_slips_submitted", 1)
-		payroll_entry.notify_update()
+		show_payroll_submission_status(submitted, unsubmitted, payroll_entry)
 
-	if not submitted_ss and not not_submitted_ss:
-		frappe.msgprint(_("No salary slip found to submit for the above selected criteria OR salary slip already submitted"))
+	except Exception as e:
+		frappe.db.rollback()
+		log_payroll_failure("submission", payroll_entry, e)
 
-	if not_submitted_ss:
-		frappe.msgprint(_("Could not submit some Salary Slips"))
+	finally:
+		frappe.db.commit()  # nosemgrep
+		frappe.publish_realtime("completed_salary_slip_submission")
+
+	frappe.flags.via_payroll_entry = False
+
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_payroll_entries_for_jv(doctype, txt, searchfield, start, page_len, filters):
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select name from `tabPayroll Entry`
 		where `{key}` LIKE %(txt)s
 		and name not in
 			(select reference_name from `tabJournal Entry Account`
 				where reference_type="Payroll Entry")
-		order by name limit %(start)s, %(page_len)s"""
-		.format(key=searchfield), {
-			'txt': "%%%s%%" % frappe.db.escape(txt),
-			'start': start, 'page_len': page_len
-		})
+		order by name limit %(page_len)s offset %(start)s""".format(
+			key=searchfield
+		),
+		{"txt": "%%%s%%" % txt, "start": start, "page_len": page_len},
+	)
+
 
 def get_employee_list(filters):
 	cond = get_filter_condition(filters)
 	cond += get_joining_relieving_condition(filters.start_date, filters.end_date)
-	condition = """and payroll_frequency = '%(payroll_frequency)s'"""% {"payroll_frequency": filters.payroll_frequency}
-	sal_struct = get_sal_struct(filters.company, filters.currency, filters.salary_slip_based_on_timesheet, condition)
+	condition = """and payroll_frequency = '%(payroll_frequency)s'""" % {
+		"payroll_frequency": filters.payroll_frequency
+	}
+	sal_struct = get_sal_struct(
+		filters.company, filters.currency, filters.salary_slip_based_on_timesheet, condition
+	)
 	if sal_struct:
 		cond += "and t2.salary_structure IN %(sal_struct)s "
 		cond += "and t2.payroll_payable_account = %(payroll_payable_account)s "
@@ -738,34 +996,38 @@
 
 	return []
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def employee_query(doctype, txt, searchfield, start, page_len, filters):
 	filters = frappe._dict(filters)
 	conditions = []
 	include_employees = []
-	emp_cond = ''
+	emp_cond = ""
 
 	if not filters.payroll_frequency:
-		frappe.throw(_('Select Payroll Frequency.'))
+		frappe.throw(_("Select Payroll Frequency."))
 
 	if filters.start_date and filters.end_date:
 		employee_list = get_employee_list(filters)
-		emp = filters.get('employees') or []
-		include_employees = [employee.employee for employee in employee_list if employee.employee not in emp]
-		filters.pop('start_date')
-		filters.pop('end_date')
-		filters.pop('salary_slip_based_on_timesheet')
-		filters.pop('payroll_frequency')
-		filters.pop('payroll_payable_account')
-		filters.pop('currency')
+		emp = filters.get("employees") or []
+		include_employees = [
+			employee.employee for employee in employee_list if employee.employee not in emp
+		]
+		filters.pop("start_date")
+		filters.pop("end_date")
+		filters.pop("salary_slip_based_on_timesheet")
+		filters.pop("payroll_frequency")
+		filters.pop("payroll_payable_account")
+		filters.pop("currency")
 		if filters.employees is not None:
-			filters.pop('employees')
+			filters.pop("employees")
 
 		if include_employees:
-			emp_cond += 'and employee in %(include_employees)s'
+			emp_cond += "and employee in %(include_employees)s"
 
-	return frappe.db.sql("""select name, employee_name from `tabEmployee`
+	return frappe.db.sql(
+		"""select name, employee_name from `tabEmployee`
 		where status = 'Active'
 			and docstatus < 2
 			and ({key} like %(txt)s
@@ -773,18 +1035,23 @@
 			{emp_cond}
 			{fcond} {mcond}
 		order by
-			if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
-			if(locate(%(_txt)s, employee_name), locate(%(_txt)s, employee_name), 99999),
+			(case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end),
+			(case when locate(%(_txt)s, employee_name) > 0 then locate(%(_txt)s, employee_name) else 99999 end),
 			idx desc,
 			name, employee_name
-		limit %(start)s, %(page_len)s""".format(**{
-			'key': searchfield,
-			'fcond': get_filters_cond(doctype, filters, conditions),
-			'mcond': get_match_cond(doctype),
-			'emp_cond': emp_cond
-		}), {
-			'txt': "%%%s%%" % txt,
-			'_txt': txt.replace("%", ""),
-			'start': start,
-			'page_len': page_len,
-			'include_employees': include_employees})
+		limit %(page_len)s offset %(start)s""".format(
+			**{
+				"key": searchfield,
+				"fcond": get_filters_cond(doctype, filters, conditions),
+				"mcond": get_match_cond(doctype),
+				"emp_cond": emp_cond,
+			}
+		),
+		{
+			"txt": "%%%s%%" % txt,
+			"_txt": txt.replace("%", ""),
+			"start": start,
+			"page_len": page_len,
+			"include_employees": include_employees,
+		},
+	)
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py
index a33b28d..eb93d68 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py
@@ -1,13 +1,9 @@
 def get_data():
 	return {
-		'fieldname': 'payroll_entry',
-		'non_standard_fieldnames': {
-			'Journal Entry': 'reference_name',
-			'Payment Entry': 'reference_name',
+		"fieldname": "payroll_entry",
+		"non_standard_fieldnames": {
+			"Journal Entry": "reference_name",
+			"Payment Entry": "reference_name",
 		},
-		'transactions': [
-			{
-				'items': ['Salary Slip', 'Journal Entry']
-			}
-		]
+		"transactions": [{"items": ["Salary Slip", "Journal Entry"]}],
 	}
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry_list.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry_list.js
new file mode 100644
index 0000000..56390b7
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry_list.js
@@ -0,0 +1,18 @@
+// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+// render
+frappe.listview_settings['Payroll Entry'] = {
+	has_indicator_for_draft: 1,
+	get_indicator: function(doc) {
+		var status_color = {
+			'Draft': 'red',
+			'Submitted': 'blue',
+			'Queued': 'orange',
+			'Failed': 'red',
+			'Cancelled': 'red'
+
+		};
+		return [__(doc.status), status_color[doc.status], 'status,=,'+doc.status];
+	}
+};
diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
index 3b7f4b2..0363a0c 100644
--- a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
@@ -5,6 +5,7 @@
 
 import frappe
 from dateutil.relativedelta import relativedelta
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import add_months
 
 import erpnext
@@ -22,233 +23,377 @@
 from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_end_date, get_start_end_dates
 from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
 	create_account,
-	get_salary_component_account,
 	make_deduction_salary_component,
 	make_earning_salary_component,
-	make_employee_salary_slip,
+	set_salary_component_account,
 )
 from erpnext.payroll.doctype.salary_structure.test_salary_structure import (
 	create_salary_structure_assignment,
 	make_salary_structure,
 )
 
-test_dependencies = ['Holiday List']
+test_dependencies = ["Holiday List"]
 
-class TestPayrollEntry(unittest.TestCase):
-	@classmethod
-	def setUpClass(cls):
-		frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", '_Test Holiday List')
 
+class TestPayrollEntry(FrappeTestCase):
 	def setUp(self):
-		for dt in ["Salary Slip", "Salary Component", "Salary Component Account",
-			"Payroll Entry", "Salary Structure", "Salary Structure Assignment", "Payroll Employee Detail", "Additional Salary"]:
-				frappe.db.sql("delete from `tab%s`" % dt)
+		for dt in [
+			"Salary Slip",
+			"Salary Component",
+			"Salary Component Account",
+			"Payroll Entry",
+			"Salary Structure",
+			"Salary Structure Assignment",
+			"Payroll Employee Detail",
+			"Additional Salary",
+			"Loan",
+		]:
+			frappe.db.delete(dt)
 
 		make_earning_salary_component(setup=True, company_list=["_Test Company"])
 		make_deduction_salary_component(setup=True, test_tax=False, company_list=["_Test Company"])
 
+		frappe.db.set_value("Company", "_Test Company", "default_holiday_list", "_Test Holiday List")
 		frappe.db.set_value("Payroll Settings", None, "email_salary_slip_to_employee", 0)
 
-	def test_payroll_entry(self): # pylint: disable=no-self-use
-		company = erpnext.get_default_company()
-		for data in frappe.get_all('Salary Component', fields = ["name"]):
-			if not frappe.db.get_value('Salary Component Account',
-				{'parent': data.name, 'company': company}, 'name'):
-				get_salary_component_account(data.name)
+		# set default payable account
+		default_account = frappe.db.get_value(
+			"Company", "_Test Company", "default_payroll_payable_account"
+		)
+		if not default_account or default_account != "_Test Payroll Payable - _TC":
+			create_account(
+				account_name="_Test Payroll Payable",
+				company="_Test Company",
+				parent_account="Current Liabilities - _TC",
+				account_type="Payable",
+			)
+			frappe.db.set_value(
+				"Company", "_Test Company", "default_payroll_payable_account", "_Test Payroll Payable - _TC"
+			)
 
-		employee = frappe.db.get_value("Employee", {'company': company})
-		company_doc = frappe.get_doc('Company', company)
-		make_salary_structure("_Test Salary Structure", "Monthly", employee, company=company, currency=company_doc.default_currency)
-		dates = get_start_end_dates('Monthly', nowdate())
-		if not frappe.db.get_value("Salary Slip", {"start_date": dates.start_date, "end_date": dates.end_date}):
-			make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date, payable_account=company_doc.default_payroll_payable_account,
-				currency=company_doc.default_currency)
+	def test_payroll_entry(self):
+		company = frappe.get_doc("Company", "_Test Company")
+		employee = frappe.db.get_value("Employee", {"company": "_Test Company"})
+		setup_salary_structure(employee, company)
 
-	def test_multi_currency_payroll_entry(self): # pylint: disable=no-self-use
-		company = erpnext.get_default_company()
-		employee = make_employee("test_muti_currency_employee@payroll.com", company=company)
-		for data in frappe.get_all('Salary Component', fields = ["name"]):
-			if not frappe.db.get_value('Salary Component Account',
-				{'parent': data.name, 'company': company}, 'name'):
-				get_salary_component_account(data.name)
+		dates = get_start_end_dates("Monthly", nowdate())
+		make_payroll_entry(
+			start_date=dates.start_date,
+			end_date=dates.end_date,
+			payable_account=company.default_payroll_payable_account,
+			currency=company.default_currency,
+			company=company.name,
+		)
 
-		company_doc = frappe.get_doc('Company', company)
-		salary_structure = make_salary_structure("_Test Multi Currency Salary Structure", "Monthly", company=company, currency='USD')
-		create_salary_structure_assignment(employee, salary_structure.name, company=company, currency='USD')
-		frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""",(frappe.db.get_value("Employee", {"user_id": "test_muti_currency_employee@payroll.com"})))
-		salary_slip = get_salary_slip("test_muti_currency_employee@payroll.com", "Monthly", "_Test Multi Currency Salary Structure")
-		dates = get_start_end_dates('Monthly', nowdate())
-		payroll_entry = make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date,
-			payable_account=company_doc.default_payroll_payable_account, currency='USD', exchange_rate=70)
+	def test_multi_currency_payroll_entry(self):
+		company = frappe.get_doc("Company", "_Test Company")
+		employee = make_employee(
+			"test_muti_currency_employee@payroll.com", company=company.name, department="Accounts - _TC"
+		)
+		salary_structure = "_Test Multi Currency Salary Structure"
+		setup_salary_structure(employee, company, "USD", salary_structure)
+
+		dates = get_start_end_dates("Monthly", nowdate())
+		payroll_entry = make_payroll_entry(
+			start_date=dates.start_date,
+			end_date=dates.end_date,
+			payable_account=company.default_payroll_payable_account,
+			currency="USD",
+			exchange_rate=70,
+			company=company.name,
+			cost_center="Main - _TC",
+		)
 		payroll_entry.make_payment_entry()
 
-		salary_slip.load_from_db()
+		salary_slip = frappe.db.get_value("Salary Slip", {"payroll_entry": payroll_entry.name}, "name")
+		salary_slip = frappe.get_doc("Salary Slip", salary_slip)
 
+		payroll_entry.reload()
 		payroll_je = salary_slip.journal_entry
 		if payroll_je:
-			payroll_je_doc = frappe.get_doc('Journal Entry', payroll_je)
-
+			payroll_je_doc = frappe.get_doc("Journal Entry", payroll_je)
 			self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_debit)
 			self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_credit)
 
-		payment_entry = frappe.db.sql('''
+		payment_entry = frappe.db.sql(
+			"""
 			Select ifnull(sum(je.total_debit),0) as total_debit, ifnull(sum(je.total_credit),0) as total_credit from `tabJournal Entry` je, `tabJournal Entry Account` jea
 			Where je.name = jea.parent
 			And jea.reference_name = %s
-			''', (payroll_entry.name), as_dict=1)
-
+			""",
+			(payroll_entry.name),
+			as_dict=1,
+		)
 		self.assertEqual(salary_slip.base_net_pay, payment_entry[0].total_debit)
 		self.assertEqual(salary_slip.base_net_pay, payment_entry[0].total_credit)
 
-	def test_payroll_entry_with_employee_cost_center(self): # pylint: disable=no-self-use
-		for data in frappe.get_all('Salary Component', fields = ["name"]):
-			if not frappe.db.get_value('Salary Component Account',
-				{'parent': data.name, 'company': "_Test Company"}, 'name'):
-				get_salary_component_account(data.name)
+	def test_payroll_entry_with_employee_cost_center(self):
+		if not frappe.db.exists("Department", "cc - _TC"):
+			frappe.get_doc(
+				{"doctype": "Department", "department_name": "cc", "company": "_Test Company"}
+			).insert()
 
-		if not frappe.db.exists('Department', "cc - _TC"):
-			frappe.get_doc({
-				'doctype': 'Department',
-				'department_name': "cc",
-				"company": "_Test Company"
-			}).insert()
+		employee1 = make_employee(
+			"test_employee1@example.com",
+			payroll_cost_center="_Test Cost Center - _TC",
+			department="cc - _TC",
+			company="_Test Company",
+		)
+		employee2 = make_employee(
+			"test_employee2@example.com", department="cc - _TC", company="_Test Company"
+		)
 
-		frappe.db.sql("""delete from `tabEmployee` where employee_name='test_employee1@example.com' """)
-		frappe.db.sql("""delete from `tabEmployee` where employee_name='test_employee2@example.com' """)
-		frappe.db.sql("""delete from `tabSalary Structure` where name='_Test Salary Structure 1' """)
-		frappe.db.sql("""delete from `tabSalary Structure` where name='_Test Salary Structure 2' """)
+		company = frappe.get_doc("Company", "_Test Company")
+		setup_salary_structure(employee1, company)
 
-		employee1 = make_employee("test_employee1@example.com", payroll_cost_center="_Test Cost Center - _TC",
-			department="cc - _TC", company="_Test Company")
-		employee2 = make_employee("test_employee2@example.com", department="cc - _TC", company="_Test Company")
-
-		if not frappe.db.exists("Account", "_Test Payroll Payable - _TC"):
-				create_account(account_name="_Test Payroll Payable",
-					company="_Test Company", parent_account="Current Liabilities - _TC", account_type="Payable")
-
-		if not frappe.db.get_value("Company", "_Test Company", "default_payroll_payable_account") or \
-			frappe.db.get_value("Company", "_Test Company", "default_payroll_payable_account") != "_Test Payroll Payable - _TC":
-				frappe.db.set_value("Company", "_Test Company", "default_payroll_payable_account",
-					"_Test Payroll Payable - _TC")
-		currency=frappe.db.get_value("Company", "_Test Company", "default_currency")
-
-		make_salary_structure("_Test Salary Structure 1", "Monthly", employee1, company="_Test Company", currency=currency, test_tax=False)
-		ss = make_salary_structure("_Test Salary Structure 2", "Monthly", employee2, company="_Test Company", currency=currency, test_tax=False)
+		ss = make_salary_structure(
+			"_Test Salary Structure 2",
+			"Monthly",
+			employee2,
+			company="_Test Company",
+			currency=company.default_currency,
+			test_tax=False,
+		)
 
 		# update cost centers in salary structure assignment for employee2
-		ssa = frappe.db.get_value("Salary Structure Assignment",
-			{"employee": employee2, "salary_structure": ss.name, "docstatus": 1}, 'name')
+		ssa = frappe.db.get_value(
+			"Salary Structure Assignment",
+			{"employee": employee2, "salary_structure": ss.name, "docstatus": 1},
+			"name",
+		)
 
 		ssa_doc = frappe.get_doc("Salary Structure Assignment", ssa)
 		ssa_doc.payroll_cost_centers = []
-		ssa_doc.append("payroll_cost_centers", {
-			"cost_center": "_Test Cost Center - _TC",
-			"percentage": 60
-		})
-		ssa_doc.append("payroll_cost_centers", {
-			"cost_center": "_Test Cost Center 2 - _TC",
-			"percentage": 40
-		})
-
+		ssa_doc.append(
+			"payroll_cost_centers", {"cost_center": "_Test Cost Center - _TC", "percentage": 60}
+		)
+		ssa_doc.append(
+			"payroll_cost_centers", {"cost_center": "_Test Cost Center 2 - _TC", "percentage": 40}
+		)
 		ssa_doc.save()
 
-		dates = get_start_end_dates('Monthly', nowdate())
-		if not frappe.db.get_value("Salary Slip", {"start_date": dates.start_date, "end_date": dates.end_date}):
-			pe = make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date, payable_account="_Test Payroll Payable - _TC",
-				currency=frappe.db.get_value("Company", "_Test Company", "default_currency"), department="cc - _TC", company="_Test Company", payment_account="Cash - _TC", cost_center="Main - _TC")
-			je = frappe.db.get_value("Salary Slip", {"payroll_entry": pe.name}, "journal_entry")
-			je_entries = frappe.db.sql("""
-				select account, cost_center, debit, credit
-				from `tabJournal Entry Account`
-				where parent=%s
-				order by account, cost_center
-			""", je)
-			expected_je = (
-				('_Test Payroll Payable - _TC', 'Main - _TC', 0.0, 155600.0),
-				('Salary - _TC', '_Test Cost Center - _TC', 124800.0, 0.0),
-				('Salary - _TC', '_Test Cost Center 2 - _TC', 31200.0, 0.0),
-				('Salary Deductions - _TC', '_Test Cost Center - _TC', 0.0, 320.0),
-				('Salary Deductions - _TC', '_Test Cost Center 2 - _TC', 0.0, 80.0)
-			)
+		dates = get_start_end_dates("Monthly", nowdate())
+		pe = make_payroll_entry(
+			start_date=dates.start_date,
+			end_date=dates.end_date,
+			payable_account="_Test Payroll Payable - _TC",
+			currency=frappe.db.get_value("Company", "_Test Company", "default_currency"),
+			department="cc - _TC",
+			company="_Test Company",
+			payment_account="Cash - _TC",
+			cost_center="Main - _TC",
+		)
+		je = frappe.db.get_value("Salary Slip", {"payroll_entry": pe.name}, "journal_entry")
+		je_entries = frappe.db.sql(
+			"""
+			select account, cost_center, debit, credit
+			from `tabJournal Entry Account`
+			where parent=%s
+			order by account, cost_center
+		""",
+			je,
+		)
+		expected_je = (
+			("_Test Payroll Payable - _TC", "Main - _TC", 0.0, 155600.0),
+			("Salary - _TC", "_Test Cost Center - _TC", 124800.0, 0.0),
+			("Salary - _TC", "_Test Cost Center 2 - _TC", 31200.0, 0.0),
+			("Salary Deductions - _TC", "_Test Cost Center - _TC", 0.0, 320.0),
+			("Salary Deductions - _TC", "_Test Cost Center 2 - _TC", 0.0, 80.0),
+		)
 
-			self.assertEqual(je_entries, expected_je)
+		self.assertEqual(je_entries, expected_je)
 
 	def test_get_end_date(self):
-		self.assertEqual(get_end_date('2017-01-01', 'monthly'), {'end_date': '2017-01-31'})
-		self.assertEqual(get_end_date('2017-02-01', 'monthly'), {'end_date': '2017-02-28'})
-		self.assertEqual(get_end_date('2017-02-01', 'fortnightly'), {'end_date': '2017-02-14'})
-		self.assertEqual(get_end_date('2017-02-01', 'bimonthly'), {'end_date': ''})
-		self.assertEqual(get_end_date('2017-01-01', 'bimonthly'), {'end_date': ''})
-		self.assertEqual(get_end_date('2020-02-15', 'bimonthly'), {'end_date': ''})
-		self.assertEqual(get_end_date('2017-02-15', 'monthly'), {'end_date': '2017-03-14'})
-		self.assertEqual(get_end_date('2017-02-15', 'daily'), {'end_date': '2017-02-15'})
+		self.assertEqual(get_end_date("2017-01-01", "monthly"), {"end_date": "2017-01-31"})
+		self.assertEqual(get_end_date("2017-02-01", "monthly"), {"end_date": "2017-02-28"})
+		self.assertEqual(get_end_date("2017-02-01", "fortnightly"), {"end_date": "2017-02-14"})
+		self.assertEqual(get_end_date("2017-02-01", "bimonthly"), {"end_date": ""})
+		self.assertEqual(get_end_date("2017-01-01", "bimonthly"), {"end_date": ""})
+		self.assertEqual(get_end_date("2020-02-15", "bimonthly"), {"end_date": ""})
+		self.assertEqual(get_end_date("2017-02-15", "monthly"), {"end_date": "2017-03-14"})
+		self.assertEqual(get_end_date("2017-02-15", "daily"), {"end_date": "2017-02-15"})
 
 	def test_loan(self):
-		branch = "Test Employee Branch"
-		applicant = make_employee("test_employee@loan.com", company="_Test Company")
 		company = "_Test Company"
+		branch = "Test Employee Branch"
+
+		if not frappe.db.exists("Branch", branch):
+			frappe.get_doc({"doctype": "Branch", "branch": branch}).insert()
 		holiday_list = make_holiday("test holiday for loan")
 
-		company_doc = frappe.get_doc('Company', company)
-		if not company_doc.default_payroll_payable_account:
-			company_doc.default_payroll_payable_account = frappe.db.get_value('Account',
-				{'company': company, 'root_type': 'Liability', 'account_type': ''}, 'name')
-			company_doc.save()
+		applicant = make_employee(
+			"test_employee@loan.com", company="_Test Company", branch=branch, holiday_list=holiday_list
+		)
+		company_doc = frappe.get_doc("Company", company)
 
-		if not frappe.db.exists('Branch', branch):
-			frappe.get_doc({
-				'doctype': 'Branch',
-				'branch': branch
-			}).insert()
-
-		employee_doc = frappe.get_doc('Employee', applicant)
-		employee_doc.branch = branch
-		employee_doc.holiday_list = holiday_list
-		employee_doc.save()
-
-		salary_structure = "Test Salary Structure for Loan"
-		make_salary_structure(salary_structure, "Monthly", employee=employee_doc.name, company="_Test Company", currency=company_doc.default_currency)
+		make_salary_structure(
+			"Test Salary Structure for Loan",
+			"Monthly",
+			employee=applicant,
+			company="_Test Company",
+			currency=company_doc.default_currency,
+		)
 
 		if not frappe.db.exists("Loan Type", "Car Loan"):
 			create_loan_accounts()
-			create_loan_type("Car Loan", 500000, 8.4,
+			create_loan_type(
+				"Car Loan",
+				500000,
+				8.4,
 				is_term_loan=1,
-				mode_of_payment='Cash',
-				disbursement_account='Disbursement Account - _TC',
-				payment_account='Payment Account - _TC',
-				loan_account='Loan Account - _TC',
-				interest_income_account='Interest Income Account - _TC',
-				penalty_income_account='Penalty Income Account - _TC')
+				mode_of_payment="Cash",
+				disbursement_account="Disbursement Account - _TC",
+				payment_account="Payment Account - _TC",
+				loan_account="Loan Account - _TC",
+				interest_income_account="Interest Income Account - _TC",
+				penalty_income_account="Penalty Income Account - _TC",
+			)
 
-		loan = create_loan(applicant, "Car Loan", 280000, "Repay Over Number of Periods", 20, posting_date=add_months(nowdate(), -1))
+		loan = create_loan(
+			applicant,
+			"Car Loan",
+			280000,
+			"Repay Over Number of Periods",
+			20,
+			posting_date=add_months(nowdate(), -1),
+		)
 		loan.repay_from_salary = 1
 		loan.submit()
 
-		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1))
+		make_loan_disbursement_entry(
+			loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1)
+		)
 		process_loan_interest_accrual_for_term_loans(posting_date=nowdate())
 
-		dates = get_start_end_dates('Monthly', nowdate())
-		make_payroll_entry(company="_Test Company", start_date=dates.start_date, payable_account=company_doc.default_payroll_payable_account,
-			currency=company_doc.default_currency, end_date=dates.end_date, branch=branch, cost_center="Main - _TC", payment_account="Cash - _TC")
+		dates = get_start_end_dates("Monthly", nowdate())
+		make_payroll_entry(
+			company="_Test Company",
+			start_date=dates.start_date,
+			payable_account=company_doc.default_payroll_payable_account,
+			currency=company_doc.default_currency,
+			end_date=dates.end_date,
+			branch=branch,
+			cost_center="Main - _TC",
+			payment_account="Cash - _TC",
+		)
 
-		name = frappe.db.get_value('Salary Slip',
-			{'posting_date': nowdate(), 'employee': applicant}, 'name')
+		name = frappe.db.get_value(
+			"Salary Slip", {"posting_date": nowdate(), "employee": applicant}, "name"
+		)
 
-		salary_slip = frappe.get_doc('Salary Slip', name)
+		salary_slip = frappe.get_doc("Salary Slip", name)
 		for row in salary_slip.loans:
 			if row.loan == loan.name:
-				interest_amount = (280000 * 8.4)/(12*100)
+				interest_amount = (280000 * 8.4) / (12 * 100)
 				principal_amount = loan.monthly_repayment_amount - interest_amount
 				self.assertEqual(row.interest_amount, interest_amount)
 				self.assertEqual(row.principal_amount, principal_amount)
-				self.assertEqual(row.total_payment,
-					interest_amount + principal_amount)
+				self.assertEqual(row.total_payment, interest_amount + principal_amount)
 
-		if salary_slip.docstatus == 0:
-			frappe.delete_doc('Salary Slip', name)
+	def test_salary_slip_operation_queueing(self):
+		company = "_Test Company"
+		company_doc = frappe.get_doc("Company", company)
+		employee = make_employee("test_employee@payroll.com", company=company)
+		setup_salary_structure(employee, company_doc)
+
+		# enqueue salary slip creation via payroll entry
+		# Payroll Entry status should change to Queued
+		dates = get_start_end_dates("Monthly", nowdate())
+		payroll_entry = get_payroll_entry(
+			start_date=dates.start_date,
+			end_date=dates.end_date,
+			payable_account=company_doc.default_payroll_payable_account,
+			currency=company_doc.default_currency,
+			company=company_doc.name,
+			cost_center="Main - _TC",
+		)
+		frappe.flags.enqueue_payroll_entry = True
+		payroll_entry.submit()
+		payroll_entry.reload()
+
+		self.assertEqual(payroll_entry.status, "Queued")
+		frappe.flags.enqueue_payroll_entry = False
+
+	def test_salary_slip_operation_failure(self):
+		company = "_Test Company"
+		company_doc = frappe.get_doc("Company", company)
+		employee = make_employee("test_employee@payroll.com", company=company)
+
+		salary_structure = make_salary_structure(
+			"_Test Salary Structure",
+			"Monthly",
+			employee,
+			company=company,
+			currency=company_doc.default_currency,
+		)
+
+		# reset account in component to test submission failure
+		component = frappe.get_doc("Salary Component", salary_structure.earnings[0].salary_component)
+		component.accounts = []
+		component.save()
+
+		# salary slip submission via payroll entry
+		# Payroll Entry status should change to Failed because of the missing account setup
+		dates = get_start_end_dates("Monthly", nowdate())
+		payroll_entry = get_payroll_entry(
+			start_date=dates.start_date,
+			end_date=dates.end_date,
+			payable_account=company_doc.default_payroll_payable_account,
+			currency=company_doc.default_currency,
+			company=company_doc.name,
+			cost_center="Main - _TC",
+		)
+
+		# set employee as Inactive to check creation failure
+		frappe.db.set_value("Employee", employee, "status", "Inactive")
+		payroll_entry.submit()
+		payroll_entry.reload()
+		self.assertEqual(payroll_entry.status, "Failed")
+		self.assertIsNotNone(payroll_entry.error_message)
+
+		frappe.db.set_value("Employee", employee, "status", "Active")
+		payroll_entry.submit()
+		payroll_entry.submit_salary_slips()
+
+		payroll_entry.reload()
+		self.assertEqual(payroll_entry.status, "Failed")
+		self.assertIsNotNone(payroll_entry.error_message)
+
+		# set accounts
+		for data in frappe.get_all("Salary Component", pluck="name"):
+			set_salary_component_account(data, company_list=[company])
+
+		# Payroll Entry successful, status should change to Submitted
+		payroll_entry.submit_salary_slips()
+		payroll_entry.reload()
+
+		self.assertEqual(payroll_entry.status, "Submitted")
+		self.assertEqual(payroll_entry.error_message, "")
+
+	def test_payroll_entry_status(self):
+		company = "_Test Company"
+		company_doc = frappe.get_doc("Company", company)
+		employee = make_employee("test_employee@payroll.com", company=company)
+
+		setup_salary_structure(employee, company_doc)
+
+		dates = get_start_end_dates("Monthly", nowdate())
+		payroll_entry = get_payroll_entry(
+			start_date=dates.start_date,
+			end_date=dates.end_date,
+			payable_account=company_doc.default_payroll_payable_account,
+			currency=company_doc.default_currency,
+			company=company_doc.name,
+			cost_center="Main - _TC",
+		)
+		payroll_entry.submit()
+		self.assertEqual(payroll_entry.status, "Submitted")
+
+		payroll_entry.cancel()
+		self.assertEqual(payroll_entry.status, "Cancelled")
 
 
-def make_payroll_entry(**args):
+def get_payroll_entry(**args):
 	args = frappe._dict(args)
 
 	payroll_entry = frappe.new_doc("Payroll Entry")
@@ -271,20 +416,34 @@
 		payroll_entry.payment_account = args.payment_account
 
 	payroll_entry.fill_employee_details()
-	payroll_entry.save()
-	payroll_entry.create_salary_slips()
+	payroll_entry.insert()
+
+	# Commit so that the first salary slip creation failure does not rollback the Payroll Entry insert.
+	frappe.db.commit()  # nosemgrep
+
+	return payroll_entry
+
+
+def make_payroll_entry(**args):
+	payroll_entry = get_payroll_entry(**args)
+	payroll_entry.submit()
 	payroll_entry.submit_salary_slips()
-	if payroll_entry.get_sal_slip_list(ss_status = 1):
+	if payroll_entry.get_sal_slip_list(ss_status=1):
 		payroll_entry.make_payment_entry()
 
 	return payroll_entry
 
+
 def get_payment_account():
-	return frappe.get_value('Account',
-		{'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name")
+	return frappe.get_value(
+		"Account",
+		{"account_type": "Cash", "company": erpnext.get_default_company(), "is_group": 0},
+		"name",
+	)
+
 
 def make_holiday(holiday_list_name):
-	if not frappe.db.exists('Holiday List', holiday_list_name):
+	if not frappe.db.exists("Holiday List", holiday_list_name):
 		current_fiscal_year = get_fiscal_year(nowdate(), as_dict=True)
 		dt = getdate(nowdate())
 
@@ -292,29 +451,34 @@
 		republic_day = dt + relativedelta(month=1, day=26, year=dt.year)
 		test_holiday = dt + relativedelta(month=2, day=2, year=dt.year)
 
-		frappe.get_doc({
-			'doctype': 'Holiday List',
-			'from_date': current_fiscal_year.year_start_date,
-			'to_date': current_fiscal_year.year_end_date,
-			'holiday_list_name': holiday_list_name,
-			'holidays': [{
-				'holiday_date': new_year,
-				'description': 'New Year'
-			}, {
-				'holiday_date': republic_day,
-				'description': 'Republic Day'
-			}, {
-				'holiday_date': test_holiday,
-				'description': 'Test Holiday'
-			}]
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Holiday List",
+				"from_date": current_fiscal_year.year_start_date,
+				"to_date": current_fiscal_year.year_end_date,
+				"holiday_list_name": holiday_list_name,
+				"holidays": [
+					{"holiday_date": new_year, "description": "New Year"},
+					{"holiday_date": republic_day, "description": "Republic Day"},
+					{"holiday_date": test_holiday, "description": "Test Holiday"},
+				],
+			}
+		).insert()
 
 	return holiday_list_name
 
-def get_salary_slip(user, period, salary_structure):
-	salary_slip = make_employee_salary_slip(user, period, salary_structure)
-	salary_slip.exchange_rate = 70
-	salary_slip.calculate_net_pay()
-	salary_slip.db_update()
 
-	return salary_slip
+def setup_salary_structure(employee, company_doc, currency=None, salary_structure=None):
+	for data in frappe.get_all("Salary Component", pluck="name"):
+		if not frappe.db.get_value(
+			"Salary Component Account", {"parent": data, "company": company_doc.name}, "name"
+		):
+			set_salary_component_account(data)
+
+	make_salary_structure(
+		salary_structure or "_Test Salary Structure",
+		"Monthly",
+		employee,
+		company=company_doc.name,
+		currency=(currency or company_doc.default_currency),
+	)
diff --git a/erpnext/payroll/doctype/payroll_period/payroll_period.py b/erpnext/payroll/doctype/payroll_period/payroll_period.py
index 659ec6d..e1f1cab 100644
--- a/erpnext/payroll/doctype/payroll_period/payroll_period.py
+++ b/erpnext/payroll/doctype/payroll_period/payroll_period.py
@@ -30,71 +30,92 @@
 			"""
 		if not self.name:
 			# hack! if name is null, it could cause problems with !=
-			self.name = "New "+self.doctype
+			self.name = "New " + self.doctype
 
-		overlap_doc = frappe.db.sql(query.format(self.doctype),{
+		overlap_doc = frappe.db.sql(
+			query.format(self.doctype),
+			{
 				"start_date": self.start_date,
 				"end_date": self.end_date,
 				"name": self.name,
-				"company": self.company
-			}, as_dict = 1)
+				"company": self.company,
+			},
+			as_dict=1,
+		)
 
 		if overlap_doc:
-			msg = _("A {0} exists between {1} and {2} (").format(self.doctype,
-				formatdate(self.start_date), formatdate(self.end_date)) \
-				+ """ <b><a href="/app/Form/{0}/{1}">{1}</a></b>""".format(self.doctype, overlap_doc[0].name) \
+			msg = (
+				_("A {0} exists between {1} and {2} (").format(
+					self.doctype, formatdate(self.start_date), formatdate(self.end_date)
+				)
+				+ """ <b><a href="/app/Form/{0}/{1}">{1}</a></b>""".format(self.doctype, overlap_doc[0].name)
 				+ _(") for {0}").format(self.company)
+			)
 			frappe.throw(msg)
 
+
 def get_payroll_period_days(start_date, end_date, employee, company=None):
 	if not company:
 		company = frappe.db.get_value("Employee", employee, "company")
-	payroll_period = frappe.db.sql("""
+	payroll_period = frappe.db.sql(
+		"""
 		select name, start_date, end_date
 		from `tabPayroll Period`
 		where
 			company=%(company)s
 			and %(start_date)s between start_date and end_date
 			and %(end_date)s between start_date and end_date
-	""", {
-		'company': company,
-		'start_date': start_date,
-		'end_date': end_date
-	})
+	""",
+		{"company": company, "start_date": start_date, "end_date": end_date},
+	)
 
 	if len(payroll_period) > 0:
 		actual_no_of_days = date_diff(getdate(payroll_period[0][2]), getdate(payroll_period[0][1])) + 1
 		working_days = actual_no_of_days
-		if not cint(frappe.db.get_value("Payroll Settings", None, "include_holidays_in_total_working_days")):
-			holidays = get_holiday_dates_for_employee(employee, getdate(payroll_period[0][1]), getdate(payroll_period[0][2]))
+		if not cint(
+			frappe.db.get_value("Payroll Settings", None, "include_holidays_in_total_working_days")
+		):
+			holidays = get_holiday_dates_for_employee(
+				employee, getdate(payroll_period[0][1]), getdate(payroll_period[0][2])
+			)
 			working_days -= len(holidays)
 		return payroll_period[0][0], working_days, actual_no_of_days
 	return False, False, False
 
+
 def get_payroll_period(from_date, to_date, company):
-	payroll_period = frappe.db.sql("""
+	payroll_period = frappe.db.sql(
+		"""
 		select name, start_date, end_date
 		from `tabPayroll Period`
 		where start_date<=%s and end_date>= %s and company=%s
-	""", (from_date, to_date, company), as_dict=1)
+	""",
+		(from_date, to_date, company),
+		as_dict=1,
+	)
 
 	return payroll_period[0] if payroll_period else None
 
-def get_period_factor(employee, start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days=0):
+
+def get_period_factor(
+	employee, start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days=0
+):
 	# TODO if both deduct checked update the factor to make tax consistent
 	period_start, period_end = payroll_period.start_date, payroll_period.end_date
-	joining_date, relieving_date = frappe.db.get_value("Employee", employee, ["date_of_joining", "relieving_date"])
+	joining_date, relieving_date = frappe.db.get_value(
+		"Employee", employee, ["date_of_joining", "relieving_date"]
+	)
 
 	if getdate(joining_date) > getdate(period_start):
 		period_start = joining_date
 	if relieving_date and getdate(relieving_date) < getdate(period_end):
 		period_end = relieving_date
 		if month_diff(period_end, start_date) > 1:
-			start_date = add_months(start_date, - (month_diff(period_end, start_date)+1))
+			start_date = add_months(start_date, -(month_diff(period_end, start_date) + 1))
 
 	total_sub_periods, remaining_sub_periods = 0.0, 0.0
 
-	if payroll_frequency ==  "Monthly" and not depends_on_payment_days:
+	if payroll_frequency == "Monthly" and not depends_on_payment_days:
 		total_sub_periods = month_diff(payroll_period.end_date, payroll_period.start_date)
 		remaining_sub_periods = month_diff(period_end, start_date)
 	else:
diff --git a/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py b/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py
index 8a3332f..96632c5 100644
--- a/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py
+++ b/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py
@@ -1,9 +1,7 @@
 def get_data():
-     return {
-        'fieldname': 'payroll_period',
-        'transactions': [
-            {
-                'items': ['Employee Tax Exemption Proof Submission', 'Employee Tax Exemption Declaration']
-            },
-        ],
-    }
+	return {
+		"fieldname": "payroll_period",
+		"transactions": [
+			{"items": ["Employee Tax Exemption Proof Submission", "Employee Tax Exemption Declaration"]},
+		],
+	}
diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.py b/erpnext/payroll/doctype/payroll_settings/payroll_settings.py
index 6fd3094..33614e9 100644
--- a/erpnext/payroll/doctype/payroll_settings/payroll_settings.py
+++ b/erpnext/payroll/doctype/payroll_settings/payroll_settings.py
@@ -21,12 +21,25 @@
 			if not self.password_policy:
 				frappe.throw(_("Password policy for Salary Slips is not set"))
 
-
 	def on_update(self):
 		self.toggle_rounded_total()
 		frappe.clear_cache()
 
 	def toggle_rounded_total(self):
 		self.disable_rounded_total = cint(self.disable_rounded_total)
-		make_property_setter("Salary Slip", "rounded_total", "hidden", self.disable_rounded_total, "Check", validate_fields_for_doctype=False)
-		make_property_setter("Salary Slip", "rounded_total", "print_hide", self.disable_rounded_total, "Check", validate_fields_for_doctype=False)
+		make_property_setter(
+			"Salary Slip",
+			"rounded_total",
+			"hidden",
+			self.disable_rounded_total,
+			"Check",
+			validate_fields_for_doctype=False,
+		)
+		make_property_setter(
+			"Salary Slip",
+			"rounded_total",
+			"print_hide",
+			self.disable_rounded_total,
+			"Check",
+			validate_fields_for_doctype=False,
+		)
diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py
index 10e8381..cdcd9a9 100644
--- a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py
+++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py
@@ -14,14 +14,14 @@
 	def validate(self):
 		validate_active_employee(self.employee)
 		if getdate(self.bonus_payment_date) < getdate():
-			frappe.throw(_('Bonus Payment Date cannot be a past date'))
+			frappe.throw(_("Bonus Payment Date cannot be a past date"))
 
 	def on_submit(self):
-		company = frappe.db.get_value('Employee', self.employee, 'company')
+		company = frappe.db.get_value("Employee", self.employee, "company")
 		additional_salary = self.get_additional_salary()
 
 		if not additional_salary:
-			additional_salary = frappe.new_doc('Additional Salary')
+			additional_salary = frappe.new_doc("Additional Salary")
 			additional_salary.employee = self.employee
 			additional_salary.salary_component = self.salary_component
 			additional_salary.amount = self.bonus_amount
@@ -34,29 +34,36 @@
 			# self.db_set('additional_salary', additional_salary.name)
 
 		else:
-			bonus_added = frappe.db.get_value('Additional Salary', additional_salary, 'amount') + self.bonus_amount
-			frappe.db.set_value('Additional Salary', additional_salary, 'amount', bonus_added)
-			self.db_set('additional_salary', additional_salary)
+			bonus_added = (
+				frappe.db.get_value("Additional Salary", additional_salary, "amount") + self.bonus_amount
+			)
+			frappe.db.set_value("Additional Salary", additional_salary, "amount", bonus_added)
+			self.db_set("additional_salary", additional_salary)
 
 	def on_cancel(self):
 
 		additional_salary = self.get_additional_salary()
 		if self.additional_salary:
-			bonus_removed = frappe.db.get_value('Additional Salary', self.additional_salary, 'amount') - self.bonus_amount
+			bonus_removed = (
+				frappe.db.get_value("Additional Salary", self.additional_salary, "amount") - self.bonus_amount
+			)
 			if bonus_removed == 0:
-				frappe.get_doc('Additional Salary', self.additional_salary).cancel()
+				frappe.get_doc("Additional Salary", self.additional_salary).cancel()
 			else:
-				frappe.db.set_value('Additional Salary', self.additional_salary, 'amount', bonus_removed)
+				frappe.db.set_value("Additional Salary", self.additional_salary, "amount", bonus_removed)
 
 			# self.db_set('additional_salary', '')
 
 	def get_additional_salary(self):
-		return frappe.db.exists('Additional Salary', {
-				'employee': self.employee,
-				'salary_component': self.salary_component,
-				'payroll_date': self.bonus_payment_date,
-				'company': self.company,
-				'docstatus': 1,
-				'ref_doctype': self.doctype,
-				'ref_docname': self.name
-			})
+		return frappe.db.exists(
+			"Additional Salary",
+			{
+				"employee": self.employee,
+				"salary_component": self.salary_component,
+				"payroll_date": self.bonus_payment_date,
+				"company": self.company,
+				"docstatus": 1,
+				"ref_doctype": self.doctype,
+				"ref_docname": self.name,
+			},
+		)
diff --git a/erpnext/payroll/doctype/salary_component/salary_component.py b/erpnext/payroll/doctype/salary_component/salary_component.py
index b8def58..409c4a1 100644
--- a/erpnext/payroll/doctype/salary_component/salary_component.py
+++ b/erpnext/payroll/doctype/salary_component/salary_component.py
@@ -12,9 +12,13 @@
 
 	def validate_abbr(self):
 		if not self.salary_component_abbr:
-			self.salary_component_abbr = ''.join([c[0] for c in
-				self.salary_component.split()]).upper()
+			self.salary_component_abbr = "".join([c[0] for c in self.salary_component.split()]).upper()
 
 		self.salary_component_abbr = self.salary_component_abbr.strip()
-		self.salary_component_abbr = append_number_if_name_exists('Salary Component', self.salary_component_abbr,
-			'salary_component_abbr', separator='_', filters={"name": ["!=", self.name]})
+		self.salary_component_abbr = append_number_if_name_exists(
+			"Salary Component",
+			self.salary_component_abbr,
+			"salary_component_abbr",
+			separator="_",
+			filters={"name": ["!=", self.name]},
+		)
diff --git a/erpnext/payroll/doctype/salary_component/test_salary_component.py b/erpnext/payroll/doctype/salary_component/test_salary_component.py
index 6e00971..cd729e8 100644
--- a/erpnext/payroll/doctype/salary_component/test_salary_component.py
+++ b/erpnext/payroll/doctype/salary_component/test_salary_component.py
@@ -7,15 +7,18 @@
 
 # test_records = frappe.get_test_records('Salary Component')
 
+
 class TestSalaryComponent(unittest.TestCase):
 	pass
 
 
 def create_salary_component(component_name, **args):
 	if not frappe.db.exists("Salary Component", component_name):
-			frappe.get_doc({
+		frappe.get_doc(
+			{
 				"doctype": "Salary Component",
 				"salary_component": component_name,
 				"type": args.get("type") or "Earning",
-				"is_tax_applicable": args.get("is_tax_applicable") or 1
-			}).insert()
+				"is_tax_applicable": args.get("is_tax_applicable") or 1,
+			}
+		).insert()
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index d2a3998..e1ccc11 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -29,6 +29,9 @@
 	calculate_amounts,
 	create_repayment_entry,
 )
+from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import (
+	process_loan_interest_accrual_for_term_loans,
+)
 from erpnext.payroll.doctype.additional_salary.additional_salary import get_additional_salaries
 from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import (
 	get_benefit_component_amount,
@@ -48,14 +51,14 @@
 class SalarySlip(TransactionBase):
 	def __init__(self, *args, **kwargs):
 		super(SalarySlip, self).__init__(*args, **kwargs)
-		self.series = 'Sal Slip/{0}/.#####'.format(self.employee)
+		self.series = "Sal Slip/{0}/.#####".format(self.employee)
 		self.whitelisted_globals = {
 			"int": int,
 			"float": float,
 			"long": int,
 			"round": round,
 			"date": datetime.date,
-			"getdate": getdate
+			"getdate": getdate,
 		}
 
 	def autoname(self):
@@ -73,7 +76,7 @@
 			# get details from salary structure
 			self.get_emp_and_working_day_details()
 		else:
-			self.get_working_days_details(lwp = self.leave_without_pay)
+			self.get_working_days_details(lwp=self.leave_without_pay)
 
 		self.calculate_net_pay()
 		self.compute_year_to_date()
@@ -82,10 +85,16 @@
 		self.add_leave_balances()
 
 		if frappe.db.get_single_value("Payroll Settings", "max_working_hours_against_timesheet"):
-			max_working_hours = frappe.db.get_single_value("Payroll Settings", "max_working_hours_against_timesheet")
+			max_working_hours = frappe.db.get_single_value(
+				"Payroll Settings", "max_working_hours_against_timesheet"
+			)
 			if self.salary_slip_based_on_timesheet and (self.total_working_hours > int(max_working_hours)):
-				frappe.msgprint(_("Total working hours should not be greater than max working hours {0}").
-								format(max_working_hours), alert=True)
+				frappe.msgprint(
+					_("Total working hours should not be greater than max working hours {0}").format(
+						max_working_hours
+					),
+					alert=True,
+				)
 
 	def set_net_total_in_words(self):
 		doc_currency = self.currency
@@ -102,24 +111,30 @@
 			self.set_status()
 			self.update_status(self.name)
 			self.make_loan_repayment_entry()
-			if (frappe.db.get_single_value("Payroll Settings", "email_salary_slip_to_employee")) and not frappe.flags.via_payroll_entry:
+			if (
+				frappe.db.get_single_value("Payroll Settings", "email_salary_slip_to_employee")
+			) and not frappe.flags.via_payroll_entry:
 				self.email_salary_slip()
 
 		self.update_payment_status_for_gratuity()
 
 	def update_payment_status_for_gratuity(self):
-		add_salary = frappe.db.get_all("Additional Salary",
-			filters = {
-				"payroll_date": ("BETWEEN", [self.start_date, self.end_date]),
+		additional_salary = frappe.db.get_all(
+			"Additional Salary",
+			filters={
+				"payroll_date": ("between", [self.start_date, self.end_date]),
 				"employee": self.employee,
 				"ref_doctype": "Gratuity",
 				"docstatus": 1,
-			}, fields = ["ref_docname", "name"], limit=1)
+			},
+			fields=["ref_docname", "name"],
+			limit=1,
+		)
 
-		if len(add_salary):
+		if additional_salary:
 			status = "Paid" if self.docstatus == 1 else "Unpaid"
-			if add_salary[0].name in [data.additional_salary for data in self.earnings]:
-				frappe.db.set_value("Gratuity", add_salary.ref_docname, "status", status)
+			if additional_salary[0].name in [entry.additional_salary for entry in self.earnings]:
+				frappe.db.set_value("Gratuity", additional_salary[0].ref_docname, "status", status)
 
 	def on_cancel(self):
 		self.set_status()
@@ -129,6 +144,7 @@
 
 	def on_trash(self):
 		from frappe.model.naming import revert_series_if_last
+
 		revert_series_if_last(self.series, self.name)
 
 	def get_status(self):
@@ -146,9 +162,7 @@
 
 		if not joining_date:
 			joining_date, relieving_date = frappe.get_cached_value(
-				"Employee",
-				self.employee,
-				("date_of_joining", "relieving_date")
+				"Employee", self.employee, ("date_of_joining", "relieving_date")
 			)
 
 		if date_diff(self.end_date, joining_date) < 0:
@@ -165,16 +179,26 @@
 			cond = ""
 			if self.payroll_entry:
 				cond += "and payroll_entry = '{0}'".format(self.payroll_entry)
-			ret_exist = frappe.db.sql("""select name from `tabSalary Slip`
+			ret_exist = frappe.db.sql(
+				"""select name from `tabSalary Slip`
 						where start_date = %s and end_date = %s and docstatus != 2
-						and employee = %s and name != %s {0}""".format(cond),
-						(self.start_date, self.end_date, self.employee, self.name))
+						and employee = %s and name != %s {0}""".format(
+					cond
+				),
+				(self.start_date, self.end_date, self.employee, self.name),
+			)
 			if ret_exist:
-				frappe.throw(_("Salary Slip of employee {0} already created for this period").format(self.employee))
+				frappe.throw(
+					_("Salary Slip of employee {0} already created for this period").format(self.employee)
+				)
 		else:
 			for data in self.timesheets:
-				if frappe.db.get_value('Timesheet', data.time_sheet, 'status') == 'Payrolled':
-					frappe.throw(_("Salary Slip of employee {0} already created for time sheet {1}").format(self.employee, data.time_sheet))
+				if frappe.db.get_value("Timesheet", data.time_sheet, "status") == "Payrolled":
+					frappe.throw(
+						_("Salary Slip of employee {0} already created for time sheet {1}").format(
+							self.employee, data.time_sheet
+						)
+					)
 
 	def get_date_details(self):
 		if not self.end_date:
@@ -184,7 +208,7 @@
 
 	@frappe.whitelist()
 	def get_emp_and_working_day_details(self):
-		'''First time, load all the components from salary structure'''
+		"""First time, load all the components from salary structure"""
 		if self.employee:
 			self.set("earnings", [])
 			self.set("deductions", [])
@@ -193,52 +217,65 @@
 				self.get_date_details()
 
 			joining_date, relieving_date = frappe.get_cached_value(
-				"Employee",
-				self.employee,
-				("date_of_joining", "relieving_date")
+				"Employee", self.employee, ("date_of_joining", "relieving_date")
 			)
 
 			self.validate_dates(joining_date, relieving_date)
 
-			#getin leave details
+			# getin leave details
 			self.get_working_days_details(joining_date, relieving_date)
 			struct = self.check_sal_struct(joining_date, relieving_date)
 
 			if struct:
-				self._salary_structure_doc = frappe.get_doc('Salary Structure', struct)
-				self.salary_slip_based_on_timesheet = self._salary_structure_doc.salary_slip_based_on_timesheet or 0
+				self._salary_structure_doc = frappe.get_doc("Salary Structure", struct)
+				self.salary_slip_based_on_timesheet = (
+					self._salary_structure_doc.salary_slip_based_on_timesheet or 0
+				)
 				self.set_time_sheet()
 				self.pull_sal_struct()
-				ps = frappe.db.get_value("Payroll Settings", None, ["payroll_based_on","consider_unmarked_attendance_as"], as_dict=1)
+				ps = frappe.db.get_value(
+					"Payroll Settings", None, ["payroll_based_on", "consider_unmarked_attendance_as"], as_dict=1
+				)
 				return [ps.payroll_based_on, ps.consider_unmarked_attendance_as]
 
 	def set_time_sheet(self):
 		if self.salary_slip_based_on_timesheet:
 			self.set("timesheets", [])
-			timesheets = frappe.db.sql(""" select * from `tabTimesheet` where employee = %(employee)s and start_date BETWEEN %(start_date)s AND %(end_date)s and (status = 'Submitted' or
-				status = 'Billed')""", {'employee': self.employee, 'start_date': self.start_date, 'end_date': self.end_date}, as_dict=1)
+			timesheets = frappe.db.sql(
+				""" select * from `tabTimesheet` where employee = %(employee)s and start_date BETWEEN %(start_date)s AND %(end_date)s and (status = 'Submitted' or
+				status = 'Billed')""",
+				{"employee": self.employee, "start_date": self.start_date, "end_date": self.end_date},
+				as_dict=1,
+			)
 
 			for data in timesheets:
-				self.append('timesheets', {
-					'time_sheet': data.name,
-					'working_hours': data.total_hours
-				})
+				self.append("timesheets", {"time_sheet": data.name, "working_hours": data.total_hours})
 
 	def check_sal_struct(self, joining_date, relieving_date):
 		cond = """and sa.employee=%(employee)s and (sa.from_date <= %(start_date)s or
 				sa.from_date <= %(end_date)s or sa.from_date <= %(joining_date)s)"""
 		if self.payroll_frequency:
-			cond += """and ss.payroll_frequency = '%(payroll_frequency)s'""" % {"payroll_frequency": self.payroll_frequency}
+			cond += """and ss.payroll_frequency = '%(payroll_frequency)s'""" % {
+				"payroll_frequency": self.payroll_frequency
+			}
 
-		st_name = frappe.db.sql("""
+		st_name = frappe.db.sql(
+			"""
 			select sa.salary_structure
 			from `tabSalary Structure Assignment` sa join `tabSalary Structure` ss
 			where sa.salary_structure=ss.name
 				and sa.docstatus = 1 and ss.docstatus = 1 and ss.is_active ='Yes' %s
 			order by sa.from_date desc
 			limit 1
-		""" %cond, {'employee': self.employee, 'start_date': self.start_date,
-			'end_date': self.end_date, 'joining_date': joining_date})
+		"""
+			% cond,
+			{
+				"employee": self.employee,
+				"start_date": self.start_date,
+				"end_date": self.end_date,
+				"joining_date": joining_date,
+			},
+		)
 
 		if st_name:
 			self.salary_structure = st_name[0][0]
@@ -246,8 +283,12 @@
 
 		else:
 			self.salary_structure = None
-			frappe.msgprint(_("No active or default Salary Structure found for employee {0} for the given dates")
-				.format(self.employee), title=_('Salary Structure Missing'))
+			frappe.msgprint(
+				_("No active or default Salary Structure found for employee {0} for the given dates").format(
+					self.employee
+				),
+				title=_("Salary Structure Missing"),
+			)
 
 	def pull_sal_struct(self):
 		from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip
@@ -259,13 +300,19 @@
 			self.total_working_hours = sum([d.working_hours or 0.0 for d in self.timesheets]) or 0.0
 			wages_amount = self.hour_rate * self.total_working_hours
 
-			self.add_earning_for_hourly_wages(self, self._salary_structure_doc.salary_component, wages_amount)
+			self.add_earning_for_hourly_wages(
+				self, self._salary_structure_doc.salary_component, wages_amount
+			)
 
 		make_salary_slip(self._salary_structure_doc.name, self)
 
-	def get_working_days_details(self, joining_date=None, relieving_date=None, lwp=None, for_preview=0):
+	def get_working_days_details(
+		self, joining_date=None, relieving_date=None, lwp=None, for_preview=0
+	):
 		payroll_based_on = frappe.db.get_value("Payroll Settings", None, "payroll_based_on")
-		include_holidays_in_total_working_days = frappe.db.get_single_value("Payroll Settings", "include_holidays_in_total_working_days")
+		include_holidays_in_total_working_days = frappe.db.get_single_value(
+			"Payroll Settings", "include_holidays_in_total_working_days"
+		)
 
 		working_days = date_diff(self.end_date, self.start_date) + 1
 		if for_preview:
@@ -292,14 +339,16 @@
 		if not lwp:
 			lwp = actual_lwp
 		elif lwp != actual_lwp:
-			frappe.msgprint(_("Leave Without Pay does not match with approved {} records")
-				.format(payroll_based_on))
+			frappe.msgprint(
+				_("Leave Without Pay does not match with approved {} records").format(payroll_based_on)
+			)
 
 		self.leave_without_pay = lwp
 		self.total_working_days = working_days
 
-		payment_days = self.get_payment_days(joining_date,
-			relieving_date, include_holidays_in_total_working_days)
+		payment_days = self.get_payment_days(
+			joining_date, relieving_date, include_holidays_in_total_working_days
+		)
 
 		if flt(payment_days) > flt(lwp):
 			self.payment_days = flt(payment_days) - flt(lwp)
@@ -307,33 +356,81 @@
 			if payroll_based_on == "Attendance":
 				self.payment_days -= flt(absent)
 
-			unmarked_days = self.get_unmarked_days()
-			consider_unmarked_attendance_as = frappe.db.get_value("Payroll Settings", None, "consider_unmarked_attendance_as") or "Present"
+			consider_unmarked_attendance_as = (
+				frappe.db.get_value("Payroll Settings", None, "consider_unmarked_attendance_as") or "Present"
+			)
 
-			if payroll_based_on == "Attendance" and consider_unmarked_attendance_as =="Absent":
-				self.absent_days += unmarked_days #will be treated as absent
+			if payroll_based_on == "Attendance" and consider_unmarked_attendance_as == "Absent":
+				unmarked_days = self.get_unmarked_days(include_holidays_in_total_working_days)
+				self.absent_days += unmarked_days  # will be treated as absent
 				self.payment_days -= unmarked_days
-				if include_holidays_in_total_working_days:
-					for holiday in holidays:
-						if not frappe.db.exists("Attendance", {"employee": self.employee, "attendance_date": holiday, "docstatus": 1 }):
-							self.payment_days += 1
 		else:
 			self.payment_days = 0
 
-	def get_unmarked_days(self):
-		marked_days = frappe.get_all("Attendance", filters = {
-					"attendance_date": ["between", [self.start_date, self.end_date]],
-					"employee": self.employee,
-					"docstatus": 1
-				}, fields = ["COUNT(*) as marked_days"])[0].marked_days
+	def get_unmarked_days(self, include_holidays_in_total_working_days):
+		unmarked_days = self.total_working_days
+		joining_date, relieving_date = frappe.get_cached_value(
+			"Employee", self.employee, ["date_of_joining", "relieving_date"]
+		)
+		start_date = self.start_date
+		end_date = self.end_date
 
-		return self.total_working_days - marked_days
+		if joining_date and (getdate(self.start_date) < joining_date <= getdate(self.end_date)):
+			start_date = joining_date
+			unmarked_days = self.get_unmarked_days_based_on_doj_or_relieving(
+				unmarked_days,
+				include_holidays_in_total_working_days,
+				self.start_date,
+				add_days(joining_date, -1),
+			)
 
+		if relieving_date and (getdate(self.start_date) <= relieving_date < getdate(self.end_date)):
+			end_date = relieving_date
+			unmarked_days = self.get_unmarked_days_based_on_doj_or_relieving(
+				unmarked_days,
+				include_holidays_in_total_working_days,
+				add_days(relieving_date, 1),
+				self.end_date,
+			)
+
+		# exclude days for which attendance has been marked
+		unmarked_days -= frappe.get_all(
+			"Attendance",
+			filters={
+				"attendance_date": ["between", [start_date, end_date]],
+				"employee": self.employee,
+				"docstatus": 1,
+			},
+			fields=["COUNT(*) as marked_days"],
+		)[0].marked_days
+
+		return unmarked_days
+
+	def get_unmarked_days_based_on_doj_or_relieving(
+		self, unmarked_days, include_holidays_in_total_working_days, start_date, end_date
+	):
+		"""
+		Exclude days before DOJ or after
+		Relieving Date from unmarked days
+		"""
+		from erpnext.hr.doctype.employee.employee import is_holiday
+
+		if include_holidays_in_total_working_days:
+			unmarked_days -= date_diff(end_date, start_date) + 1
+		else:
+			# exclude only if not holidays
+			for days in range(date_diff(end_date, start_date) + 1):
+				date = add_days(end_date, -days)
+				if not is_holiday(self.employee, date):
+					unmarked_days -= 1
+
+		return unmarked_days
 
 	def get_payment_days(self, joining_date, relieving_date, include_holidays_in_total_working_days):
 		if not joining_date:
-			joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
-				["date_of_joining", "relieving_date"])
+			joining_date, relieving_date = frappe.get_cached_value(
+				"Employee", self.employee, ["date_of_joining", "relieving_date"]
+			)
 
 		start_date = getdate(self.start_date)
 		if joining_date:
@@ -347,8 +444,7 @@
 			if getdate(self.start_date) <= relieving_date <= getdate(self.end_date):
 				end_date = relieving_date
 			elif relieving_date < getdate(self.start_date):
-				frappe.throw(_("Employee relieved on {0} must be set as 'Left'")
-					.format(relieving_date))
+				frappe.throw(_("Employee relieved on {0} must be set as 'Left'").format(relieving_date))
 
 		payment_days = date_diff(end_date, start_date) + 1
 
@@ -364,41 +460,26 @@
 	def calculate_lwp_or_ppl_based_on_leave_application(self, holidays, working_days):
 		lwp = 0
 		holidays = "','".join(holidays)
-		daily_wages_fraction_for_half_day = \
+		daily_wages_fraction_for_half_day = (
 			flt(frappe.db.get_value("Payroll Settings", None, "daily_wages_fraction_for_half_day")) or 0.5
+		)
 
 		for d in range(working_days):
-			dt = add_days(cstr(getdate(self.start_date)), d)
-			leave = frappe.db.sql("""
-				SELECT t1.name,
-					CASE WHEN (t1.half_day_date = %(dt)s or t1.to_date = t1.from_date)
-					THEN t1.half_day else 0 END,
-					t2.is_ppl,
-					t2.fraction_of_daily_salary_per_leave
-				FROM `tabLeave Application` t1, `tabLeave Type` t2
-				WHERE t2.name = t1.leave_type
-				AND (t2.is_lwp = 1 or t2.is_ppl = 1)
-				AND t1.docstatus = 1
-				AND t1.employee = %(employee)s
-				AND ifnull(t1.salary_slip, '') = ''
-				AND CASE
-					WHEN t2.include_holiday != 1
-						THEN %(dt)s not in ('{0}') and %(dt)s between from_date and to_date
-					WHEN t2.include_holiday
-						THEN %(dt)s between from_date and to_date
-					END
-				""".format(holidays), {"employee": self.employee, "dt": dt})
+			date = add_days(cstr(getdate(self.start_date)), d)
+			leave = get_lwp_or_ppl_for_date(date, self.employee, holidays)
 
 			if leave:
 				equivalent_lwp_count = 0
-				is_half_day_leave = cint(leave[0][1])
-				is_partially_paid_leave = cint(leave[0][2])
-				fraction_of_daily_salary_per_leave = flt(leave[0][3])
+				is_half_day_leave = cint(leave[0].is_half_day)
+				is_partially_paid_leave = cint(leave[0].is_ppl)
+				fraction_of_daily_salary_per_leave = flt(leave[0].fraction_of_daily_salary_per_leave)
 
-				equivalent_lwp_count =  (1 - daily_wages_fraction_for_half_day) if is_half_day_leave else 1
+				equivalent_lwp_count = (1 - daily_wages_fraction_for_half_day) if is_half_day_leave else 1
 
 				if is_partially_paid_leave:
-					equivalent_lwp_count *= fraction_of_daily_salary_per_leave if fraction_of_daily_salary_per_leave else 1
+					equivalent_lwp_count *= (
+						fraction_of_daily_salary_per_leave if fraction_of_daily_salary_per_leave else 1
+					)
 
 				lwp += equivalent_lwp_count
 
@@ -408,49 +489,69 @@
 		lwp = 0
 		absent = 0
 
-		daily_wages_fraction_for_half_day = \
+		daily_wages_fraction_for_half_day = (
 			flt(frappe.db.get_value("Payroll Settings", None, "daily_wages_fraction_for_half_day")) or 0.5
+		)
 
-		leave_types = frappe.get_all("Leave Type",
+		leave_types = frappe.get_all(
+			"Leave Type",
 			or_filters=[["is_ppl", "=", 1], ["is_lwp", "=", 1]],
-			fields =["name", "is_lwp", "is_ppl", "fraction_of_daily_salary_per_leave", "include_holiday"])
+			fields=["name", "is_lwp", "is_ppl", "fraction_of_daily_salary_per_leave", "include_holiday"],
+		)
 
 		leave_type_map = {}
 		for leave_type in leave_types:
 			leave_type_map[leave_type.name] = leave_type
 
-		attendances = frappe.db.sql('''
+		attendances = frappe.db.sql(
+			"""
 			SELECT attendance_date, status, leave_type
 			FROM `tabAttendance`
 			WHERE
-				status in ("Absent", "Half Day", "On leave")
+				status in ('Absent', 'Half Day', 'On leave')
 				AND employee = %s
 				AND docstatus = 1
 				AND attendance_date between %s and %s
-		''', values=(self.employee, self.start_date, self.end_date), as_dict=1)
+		""",
+			values=(self.employee, self.start_date, self.end_date),
+			as_dict=1,
+		)
 
 		for d in attendances:
-			if d.status in ('Half Day', 'On Leave') and d.leave_type and d.leave_type not in leave_type_map.keys():
+			if (
+				d.status in ("Half Day", "On Leave")
+				and d.leave_type
+				and d.leave_type not in leave_type_map.keys()
+			):
 				continue
 
 			if formatdate(d.attendance_date, "yyyy-mm-dd") in holidays:
-				if d.status == "Absent" or \
-					(d.leave_type and d.leave_type in leave_type_map.keys() and not leave_type_map[d.leave_type]['include_holiday']):
-						continue
+				if d.status == "Absent" or (
+					d.leave_type
+					and d.leave_type in leave_type_map.keys()
+					and not leave_type_map[d.leave_type]["include_holiday"]
+				):
+					continue
 
 			if d.leave_type:
-				fraction_of_daily_salary_per_leave = leave_type_map[d.leave_type]["fraction_of_daily_salary_per_leave"]
+				fraction_of_daily_salary_per_leave = leave_type_map[d.leave_type][
+					"fraction_of_daily_salary_per_leave"
+				]
 
 			if d.status == "Half Day":
-				equivalent_lwp =  (1 - daily_wages_fraction_for_half_day)
+				equivalent_lwp = 1 - daily_wages_fraction_for_half_day
 
 				if d.leave_type in leave_type_map.keys() and leave_type_map[d.leave_type]["is_ppl"]:
-					equivalent_lwp *= fraction_of_daily_salary_per_leave if fraction_of_daily_salary_per_leave else 1
+					equivalent_lwp *= (
+						fraction_of_daily_salary_per_leave if fraction_of_daily_salary_per_leave else 1
+					)
 				lwp += equivalent_lwp
 			elif d.status == "On Leave" and d.leave_type and d.leave_type in leave_type_map.keys():
 				equivalent_lwp = 1
 				if leave_type_map[d.leave_type]["is_ppl"]:
-					equivalent_lwp *= fraction_of_daily_salary_per_leave if fraction_of_daily_salary_per_leave else 1
+					equivalent_lwp *= (
+						fraction_of_daily_salary_per_leave if fraction_of_daily_salary_per_leave else 1
+					)
 				lwp += equivalent_lwp
 			elif d.status == "Absent":
 				absent += 1
@@ -470,15 +571,17 @@
 				"abbr": frappe.db.get_value("Salary Component", salary_component, "salary_component_abbr"),
 				"amount": self.hour_rate * self.total_working_hours,
 				"default_amount": 0.0,
-				"additional_amount": 0.0
+				"additional_amount": 0.0,
 			}
-			doc.append('earnings', wages_row)
+			doc.append("earnings", wages_row)
 
 	def calculate_net_pay(self):
 		if self.salary_structure:
 			self.calculate_component_amounts("earnings")
 		self.gross_pay = self.get_component_totals("earnings", depends_on_payment_days=1)
-		self.base_gross_pay = flt(flt(self.gross_pay) * flt(self.exchange_rate), self.precision('base_gross_pay'))
+		self.base_gross_pay = flt(
+			flt(self.gross_pay) * flt(self.exchange_rate), self.precision("base_gross_pay")
+		)
 
 		if self.salary_structure:
 			self.calculate_component_amounts("deductions")
@@ -489,18 +592,24 @@
 
 	def set_net_pay(self):
 		self.total_deduction = self.get_component_totals("deductions")
-		self.base_total_deduction = flt(flt(self.total_deduction) * flt(self.exchange_rate), self.precision('base_total_deduction'))
+		self.base_total_deduction = flt(
+			flt(self.total_deduction) * flt(self.exchange_rate), self.precision("base_total_deduction")
+		)
 		self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment))
 		self.rounded_total = rounded(self.net_pay)
-		self.base_net_pay = flt(flt(self.net_pay) * flt(self.exchange_rate), self.precision('base_net_pay'))
-		self.base_rounded_total = flt(rounded(self.base_net_pay), self.precision('base_net_pay'))
+		self.base_net_pay = flt(
+			flt(self.net_pay) * flt(self.exchange_rate), self.precision("base_net_pay")
+		)
+		self.base_rounded_total = flt(rounded(self.base_net_pay), self.precision("base_net_pay"))
 		if self.hour_rate:
-			self.base_hour_rate = flt(flt(self.hour_rate) * flt(self.exchange_rate), self.precision('base_hour_rate'))
+			self.base_hour_rate = flt(
+				flt(self.hour_rate) * flt(self.exchange_rate), self.precision("base_hour_rate")
+			)
 		self.set_net_total_in_words()
 
 	def calculate_component_amounts(self, component_type):
-		if not getattr(self, '_salary_structure_doc', None):
-			self._salary_structure_doc = frappe.get_doc('Salary Structure', self.salary_structure)
+		if not getattr(self, "_salary_structure_doc", None):
+			self._salary_structure_doc = frappe.get_doc("Salary Structure", self.salary_structure)
 
 		payroll_period = get_payroll_period(self.start_date, self.end_date, self.company)
 
@@ -519,15 +628,13 @@
 				self.update_component_row(struct_row, amount, component_type)
 
 	def get_data_for_eval(self):
-		'''Returns data for evaluating formula'''
+		"""Returns data for evaluating formula"""
 		data = frappe._dict()
 		employee = frappe.get_doc("Employee", self.employee).as_dict()
 
 		start_date = getdate(self.start_date)
 		date_to_validate = (
-			employee.date_of_joining
-			if employee.date_of_joining > start_date
-			else start_date
+			employee.date_of_joining if employee.date_of_joining > start_date else start_date
 		)
 
 		salary_structure_assignment = frappe.get_value(
@@ -545,8 +652,9 @@
 
 		if not salary_structure_assignment:
 			frappe.throw(
-				_("Please assign a Salary Structure for Employee {0} "
-				"applicable from or before {1} first").format(
+				_(
+					"Please assign a Salary Structure for Employee {0} " "applicable from or before {1} first"
+				).format(
 					frappe.bold(self.employee_name),
 					frappe.bold(formatdate(date_to_validate)),
 				)
@@ -561,7 +669,7 @@
 		for sc in salary_components:
 			data.setdefault(sc.salary_component_abbr, 0)
 
-		for key in ('earnings', 'deductions'):
+		for key in ("earnings", "deductions"):
 			for d in self.get(key):
 				data[d.abbr] = d.amount
 
@@ -584,8 +692,10 @@
 			return amount
 
 		except NameError as err:
-			frappe.throw(_("{0} <br> This error can be due to missing or deleted field.").format(err),
-				title=_("Name error"))
+			frappe.throw(
+				_("{0} <br> This error can be due to missing or deleted field.").format(err),
+				title=_("Name error"),
+			)
 		except SyntaxError as err:
 			frappe.throw(_("Syntax error in formula or condition: {0}").format(err))
 		except Exception as e:
@@ -595,13 +705,27 @@
 	def add_employee_benefits(self, payroll_period):
 		for struct_row in self._salary_structure_doc.get("earnings"):
 			if struct_row.is_flexible_benefit == 1:
-				if frappe.db.get_value("Salary Component", struct_row.salary_component, "pay_against_benefit_claim") != 1:
-					benefit_component_amount = get_benefit_component_amount(self.employee, self.start_date, self.end_date,
-						struct_row.salary_component, self._salary_structure_doc, self.payroll_frequency, payroll_period)
+				if (
+					frappe.db.get_value(
+						"Salary Component", struct_row.salary_component, "pay_against_benefit_claim"
+					)
+					!= 1
+				):
+					benefit_component_amount = get_benefit_component_amount(
+						self.employee,
+						self.start_date,
+						self.end_date,
+						struct_row.salary_component,
+						self._salary_structure_doc,
+						self.payroll_frequency,
+						payroll_period,
+					)
 					if benefit_component_amount:
 						self.update_component_row(struct_row, benefit_component_amount, "earnings")
 				else:
-					benefit_claim_amount = get_benefit_claim_amount(self.employee, self.start_date, self.end_date, struct_row.salary_component)
+					benefit_claim_amount = get_benefit_claim_amount(
+						self.employee, self.start_date, self.end_date, struct_row.salary_component
+					)
 					if benefit_claim_amount:
 						self.update_component_row(struct_row, benefit_claim_amount, "earnings")
 
@@ -609,9 +733,10 @@
 
 	def adjust_benefits_in_last_payroll_period(self, payroll_period):
 		if payroll_period:
-			if (getdate(payroll_period.end_date) <= getdate(self.end_date)):
-				last_benefits = get_last_payroll_period_benefits(self.employee, self.start_date, self.end_date,
-					payroll_period, self._salary_structure_doc)
+			if getdate(payroll_period.end_date) <= getdate(self.end_date):
+				last_benefits = get_last_payroll_period_benefits(
+					self.employee, self.start_date, self.end_date, payroll_period, self._salary_structure_doc
+				)
 				if last_benefits:
 					for last_benefit in last_benefits:
 						last_benefit = frappe._dict(last_benefit)
@@ -619,8 +744,9 @@
 						self.update_component_row(frappe._dict(last_benefit.struct_row), amount, "earnings")
 
 	def add_additional_salary_components(self, component_type):
-		additional_salaries = get_additional_salaries(self.employee,
-			self.start_date, self.end_date, component_type)
+		additional_salaries = get_additional_salaries(
+			self.employee, self.start_date, self.end_date, component_type
+		)
 
 		for additional_salary in additional_salaries:
 			self.update_component_row(
@@ -628,7 +754,7 @@
 				additional_salary.amount,
 				component_type,
 				additional_salary,
-				is_recurring = additional_salary.is_recurring
+				is_recurring=additional_salary.is_recurring,
 			)
 
 	def add_tax_components(self, payroll_period):
@@ -641,40 +767,43 @@
 				other_deduction_components.append(d.salary_component)
 
 		if not tax_components:
-			tax_components = [d.name for d in frappe.get_all("Salary Component", filters={"variable_based_on_taxable_salary": 1})
-				if d.name not in other_deduction_components]
+			tax_components = [
+				d.name
+				for d in frappe.get_all("Salary Component", filters={"variable_based_on_taxable_salary": 1})
+				if d.name not in other_deduction_components
+			]
 
 		for d in tax_components:
 			tax_amount = self.calculate_variable_based_on_taxable_salary(d, payroll_period)
 			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, is_recurring = 0):
+	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:
 				continue
 
-			if (
-				(
-					not d.additional_salary
-					and (not additional_salary or additional_salary.overwrite)
-				) or (
-					additional_salary
-					and additional_salary.name == d.additional_salary
-				)
+			if (not d.additional_salary and (not additional_salary or additional_salary.overwrite)) or (
+				additional_salary and additional_salary.name == d.additional_salary
 			):
 				component_row = d
 				break
 
 		if additional_salary and additional_salary.overwrite:
 			# Additional Salary with overwrite checked, remove default rows of same component
-			self.set(component_type, [
-				d for d in self.get(component_type)
-				if d.salary_component != component_data.salary_component
-				or (d.additional_salary and additional_salary.name != d.additional_salary)
-				or d == component_row
-			])
+			self.set(
+				component_type,
+				[
+					d
+					for d in self.get(component_type)
+					if d.salary_component != component_data.salary_component
+					or (d.additional_salary and additional_salary.name != d.additional_salary)
+					or d == component_row
+				],
+			)
 
 		if not component_row:
 			if not amount:
@@ -682,30 +811,38 @@
 
 			component_row = self.append(component_type)
 			for attr in (
-				'depends_on_payment_days', 'salary_component', 'abbr',
-				'do_not_include_in_total', 'is_tax_applicable',
-				'is_flexible_benefit', 'variable_based_on_taxable_salary',
-				'exempted_from_income_tax'
+				"depends_on_payment_days",
+				"salary_component",
+				"abbr",
+				"do_not_include_in_total",
+				"is_tax_applicable",
+				"is_flexible_benefit",
+				"variable_based_on_taxable_salary",
+				"exempted_from_income_tax",
 			):
 				component_row.set(attr, component_data.get(attr))
 
 		if additional_salary:
 			if additional_salary.overwrite:
-				component_row.additional_amount = flt(flt(amount) - flt(component_row.get("default_amount", 0)),
-					component_row.precision("additional_amount"))
+				component_row.additional_amount = flt(
+					flt(amount) - flt(component_row.get("default_amount", 0)),
+					component_row.precision("additional_amount"),
+				)
 			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 = \
+			component_row.deduct_full_tax_on_selected_payroll_date = (
 				additional_salary.deduct_full_tax_on_selected_payroll_date
+			)
 		else:
 			component_row.default_amount = amount
 			component_row.additional_amount = 0
-			component_row.deduct_full_tax_on_selected_payroll_date = \
+			component_row.deduct_full_tax_on_selected_payroll_date = (
 				component_data.deduct_full_tax_on_selected_payroll_date
+			)
 
 		component_row.amount = amount
 
@@ -713,7 +850,9 @@
 
 	def update_component_amount_based_on_payment_days(self, component_row):
 		joining_date, relieving_date = self.get_joining_and_relieving_dates()
-		component_row.amount = self.get_amount_based_on_payment_days(component_row, joining_date, relieving_date)[0]
+		component_row.amount = self.get_amount_based_on_payment_days(
+			component_row, joining_date, relieving_date
+		)[0]
 
 	def set_precision_for_component_amounts(self):
 		for component_type in ("earnings", "deductions"):
@@ -722,8 +861,11 @@
 
 	def calculate_variable_based_on_taxable_salary(self, tax_component, payroll_period):
 		if not payroll_period:
-			frappe.msgprint(_("Start and end dates not in a valid Payroll Period, cannot calculate {0}.")
-				.format(tax_component))
+			frappe.msgprint(
+				_("Start and end dates not in a valid Payroll Period, cannot calculate {0}.").format(
+					tax_component
+				)
+			)
 			return
 
 		# Deduct taxes forcefully for unsubmitted tax exemption proof and unclaimed benefits in the last period
@@ -738,23 +880,34 @@
 		tax_slab = self.get_income_tax_slabs(payroll_period)
 
 		# get remaining numbers of sub-period (period for which one salary is processed)
-		remaining_sub_periods = get_period_factor(self.employee,
-			self.start_date, self.end_date, self.payroll_frequency, payroll_period)[1]
+		remaining_sub_periods = get_period_factor(
+			self.employee, self.start_date, self.end_date, self.payroll_frequency, payroll_period
+		)[1]
 		# get taxable_earnings, paid_taxes for previous period
-		previous_taxable_earnings = self.get_taxable_earnings_for_prev_period(payroll_period.start_date,
-			self.start_date, tax_slab.allow_tax_exemption)
-		previous_total_paid_taxes = self.get_tax_paid_in_period(payroll_period.start_date, self.start_date, tax_component)
+		previous_taxable_earnings = self.get_taxable_earnings_for_prev_period(
+			payroll_period.start_date, self.start_date, tax_slab.allow_tax_exemption
+		)
+		previous_total_paid_taxes = self.get_tax_paid_in_period(
+			payroll_period.start_date, self.start_date, tax_component
+		)
 
 		# get taxable_earnings for current period (all days)
-		current_taxable_earnings = self.get_taxable_earnings(tax_slab.allow_tax_exemption, payroll_period=payroll_period)
-		future_structured_taxable_earnings = current_taxable_earnings.taxable_earnings * (math.ceil(remaining_sub_periods) - 1)
+		current_taxable_earnings = self.get_taxable_earnings(
+			tax_slab.allow_tax_exemption, payroll_period=payroll_period
+		)
+		future_structured_taxable_earnings = current_taxable_earnings.taxable_earnings * (
+			math.ceil(remaining_sub_periods) - 1
+		)
 
 		# get taxable_earnings, addition_earnings for current actual payment days
-		current_taxable_earnings_for_payment_days = self.get_taxable_earnings(tax_slab.allow_tax_exemption,
-			based_on_payment_days=1, payroll_period=payroll_period)
+		current_taxable_earnings_for_payment_days = self.get_taxable_earnings(
+			tax_slab.allow_tax_exemption, based_on_payment_days=1, payroll_period=payroll_period
+		)
 		current_structured_taxable_earnings = current_taxable_earnings_for_payment_days.taxable_earnings
 		current_additional_earnings = current_taxable_earnings_for_payment_days.additional_income
-		current_additional_earnings_with_full_tax = current_taxable_earnings_for_payment_days.additional_income_with_full_tax
+		current_additional_earnings_with_full_tax = (
+			current_taxable_earnings_for_payment_days.additional_income_with_full_tax
+		)
 
 		# Get taxable unclaimed benefits
 		unclaimed_taxable_benefits = 0
@@ -765,25 +918,43 @@
 		# Total exemption amount based on tax exemption declaration
 		total_exemption_amount = self.get_total_exemption_amount(payroll_period, tax_slab)
 
-		#Employee Other Incomes
+		# Employee Other Incomes
 		other_incomes = self.get_income_form_other_sources(payroll_period) or 0.0
 
 		# Total taxable earnings including additional and other incomes
-		total_taxable_earnings = previous_taxable_earnings + current_structured_taxable_earnings + future_structured_taxable_earnings \
-			+ current_additional_earnings + other_incomes + unclaimed_taxable_benefits - total_exemption_amount
+		total_taxable_earnings = (
+			previous_taxable_earnings
+			+ current_structured_taxable_earnings
+			+ future_structured_taxable_earnings
+			+ current_additional_earnings
+			+ other_incomes
+			+ unclaimed_taxable_benefits
+			- total_exemption_amount
+		)
 
 		# Total taxable earnings without additional earnings with full tax
-		total_taxable_earnings_without_full_tax_addl_components = total_taxable_earnings - current_additional_earnings_with_full_tax
+		total_taxable_earnings_without_full_tax_addl_components = (
+			total_taxable_earnings - current_additional_earnings_with_full_tax
+		)
 
 		# Structured tax amount
-		total_structured_tax_amount = self.calculate_tax_by_tax_slab(
-			total_taxable_earnings_without_full_tax_addl_components, tax_slab)
-		current_structured_tax_amount = (total_structured_tax_amount - previous_total_paid_taxes) / remaining_sub_periods
+		eval_locals = self.get_data_for_eval()
+		total_structured_tax_amount = calculate_tax_by_tax_slab(
+			total_taxable_earnings_without_full_tax_addl_components,
+			tax_slab,
+			self.whitelisted_globals,
+			eval_locals,
+		)
+		current_structured_tax_amount = (
+			total_structured_tax_amount - previous_total_paid_taxes
+		) / remaining_sub_periods
 
 		# Total taxable earnings with additional earnings with full tax
 		full_tax_on_additional_earnings = 0.0
 		if current_additional_earnings_with_full_tax:
-			total_tax_amount = self.calculate_tax_by_tax_slab(total_taxable_earnings, tax_slab)
+			total_tax_amount = calculate_tax_by_tax_slab(
+				total_taxable_earnings, tax_slab, self.whitelisted_globals, eval_locals
+			)
 			full_tax_on_additional_earnings = total_tax_amount - total_structured_tax_amount
 
 		current_tax_amount = current_structured_tax_amount + full_tax_on_additional_earnings
@@ -793,25 +964,33 @@
 		return current_tax_amount
 
 	def get_income_tax_slabs(self, payroll_period):
-		income_tax_slab, ss_assignment_name = frappe.db.get_value("Salary Structure Assignment",
-			{"employee": self.employee, "salary_structure": self.salary_structure, "docstatus": 1}, ["income_tax_slab", 'name'])
+		income_tax_slab, ss_assignment_name = frappe.db.get_value(
+			"Salary Structure Assignment",
+			{"employee": self.employee, "salary_structure": self.salary_structure, "docstatus": 1},
+			["income_tax_slab", "name"],
+		)
 
 		if not income_tax_slab:
-			frappe.throw(_("Income Tax Slab not set in Salary Structure Assignment: {0}").format(ss_assignment_name))
+			frappe.throw(
+				_("Income Tax Slab not set in Salary Structure Assignment: {0}").format(ss_assignment_name)
+			)
 
 		income_tax_slab_doc = frappe.get_doc("Income Tax Slab", income_tax_slab)
 		if income_tax_slab_doc.disabled:
 			frappe.throw(_("Income Tax Slab: {0} is disabled").format(income_tax_slab))
 
 		if getdate(income_tax_slab_doc.effective_from) > getdate(payroll_period.start_date):
-			frappe.throw(_("Income Tax Slab must be effective on or before Payroll Period Start Date: {0}")
-				.format(payroll_period.start_date))
+			frappe.throw(
+				_("Income Tax Slab must be effective on or before Payroll Period Start Date: {0}").format(
+					payroll_period.start_date
+				)
+			)
 
 		return income_tax_slab_doc
 
-
 	def get_taxable_earnings_for_prev_period(self, start_date, end_date, allow_tax_exemption=False):
-		taxable_earnings = frappe.db.sql("""
+		taxable_earnings = frappe.db.sql(
+			"""
 			select sum(sd.amount)
 			from
 				`tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name
@@ -823,16 +1002,15 @@
 				and ss.employee=%(employee)s
 				and ss.start_date between %(from_date)s and %(to_date)s
 				and ss.end_date between %(from_date)s and %(to_date)s
-			""", {
-				"employee": self.employee,
-				"from_date": start_date,
-				"to_date": end_date
-			})
+			""",
+			{"employee": self.employee, "from_date": start_date, "to_date": end_date},
+		)
 		taxable_earnings = flt(taxable_earnings[0][0]) if taxable_earnings else 0
 
 		exempted_amount = 0
 		if allow_tax_exemption:
-			exempted_amount = frappe.db.sql("""
+			exempted_amount = frappe.db.sql(
+				"""
 				select sum(sd.amount)
 				from
 					`tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name
@@ -844,18 +1022,18 @@
 					and ss.employee=%(employee)s
 					and ss.start_date between %(from_date)s and %(to_date)s
 					and ss.end_date between %(from_date)s and %(to_date)s
-				""", {
-					"employee": self.employee,
-					"from_date": start_date,
-					"to_date": end_date
-				})
+				""",
+				{"employee": self.employee, "from_date": start_date, "to_date": end_date},
+			)
 			exempted_amount = flt(exempted_amount[0][0]) if exempted_amount else 0
 
 		return taxable_earnings - exempted_amount
 
 	def get_tax_paid_in_period(self, start_date, end_date, tax_component):
 		# find total_tax_paid, tax paid for benefit, additional_salary
-		total_tax_paid = flt(frappe.db.sql("""
+		total_tax_paid = flt(
+			frappe.db.sql(
+				"""
 			select
 				sum(sd.amount)
 			from
@@ -868,16 +1046,21 @@
 				and ss.employee=%(employee)s
 				and ss.start_date between %(from_date)s and %(to_date)s
 				and ss.end_date between %(from_date)s and %(to_date)s
-		""", {
-			"salary_component": tax_component,
-			"employee": self.employee,
-			"from_date": start_date,
-			"to_date": end_date
-		})[0][0])
+		""",
+				{
+					"salary_component": tax_component,
+					"employee": self.employee,
+					"from_date": start_date,
+					"to_date": end_date,
+				},
+			)[0][0]
+		)
 
 		return total_tax_paid
 
-	def get_taxable_earnings(self, allow_tax_exemption=False, based_on_payment_days=0, payroll_period=None):
+	def get_taxable_earnings(
+		self, allow_tax_exemption=False, based_on_payment_days=0, payroll_period=None
+	):
 		joining_date, relieving_date = self.get_joining_and_relieving_dates()
 
 		taxable_earnings = 0
@@ -887,7 +1070,9 @@
 
 		for earning in self.earnings:
 			if based_on_payment_days:
-				amount, additional_amount = self.get_amount_based_on_payment_days(earning, joining_date, relieving_date)
+				amount, additional_amount = self.get_amount_based_on_payment_days(
+					earning, joining_date, relieving_date
+				)
 			else:
 				if earning.additional_amount:
 					amount, additional_amount = earning.amount, earning.additional_amount
@@ -898,13 +1083,14 @@
 				if earning.is_flexible_benefit:
 					flexi_benefits += amount
 				else:
-					taxable_earnings += (amount - additional_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, payroll_period) # Used earning.additional_amount to consider the amount for the full month
+						additional_income += self.get_future_recurring_additional_amount(
+							earning.additional_salary, earning.additional_amount, payroll_period
+						)  # 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
@@ -914,25 +1100,32 @@
 				if ded.exempted_from_income_tax:
 					amount, additional_amount = ded.amount, ded.additional_amount
 					if based_on_payment_days:
-						amount, additional_amount = self.get_amount_based_on_payment_days(ded, joining_date, relieving_date)
+						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, payroll_period) # Used ded.additional_amount to consider the amount for the full month
+						additional_income -= self.get_future_recurring_additional_amount(
+							ded.additional_salary, ded.additional_amount, payroll_period
+						)  # Used ded.additional_amount to consider the amount for the full month
 
-		return frappe._dict({
-			"taxable_earnings": taxable_earnings,
-			"additional_income": additional_income,
-			"additional_income_with_full_tax": additional_income_with_full_tax,
-			"flexi_benefits": flexi_benefits
-		})
+		return frappe._dict(
+			{
+				"taxable_earnings": taxable_earnings,
+				"additional_income": additional_income,
+				"additional_income_with_full_tax": additional_income_with_full_tax,
+				"flexi_benefits": flexi_benefits,
+			}
+		)
 
-	def get_future_recurring_additional_amount(self, additional_salary, monthly_additional_amount, payroll_period):
+	def get_future_recurring_additional_amount(
+		self, additional_salary, monthly_additional_amount, payroll_period
+	):
 		future_recurring_additional_amount = 0
-		to_date = frappe.db.get_value("Additional Salary", additional_salary, 'to_date')
+		to_date = frappe.db.get_value("Additional Salary", additional_salary, "to_date")
 
 		# future month count excluding current
 		from_date, to_date = getdate(self.start_date), getdate(to_date)
@@ -942,42 +1135,69 @@
 		if getdate(to_date) > getdate(payroll_period.end_date):
 			to_date = getdate(payroll_period.end_date)
 
-		future_recurring_period = ((to_date.year - from_date.year) * 12) + (to_date.month - from_date.month)
+		future_recurring_period = ((to_date.year - from_date.year) * 12) + (
+			to_date.month - from_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
+			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
-		timesheet_component = frappe.db.get_value("Salary Structure", self.salary_structure, "salary_component")
+		timesheet_component = frappe.db.get_value(
+			"Salary Structure", self.salary_structure, "salary_component"
+		)
 
-		if (self.salary_structure and
-			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 (row.salary_component != timesheet_component or
-				getdate(self.start_date) < joining_date or
-				(relieving_date and getdate(self.end_date) > relieving_date)
-			)):
-			additional_amount = flt((flt(row.additional_amount) * flt(self.payment_days)
-				/ cint(self.total_working_days)), row.precision("additional_amount"))
-			amount = flt((flt(row.default_amount) * flt(self.payment_days)
-				/ cint(self.total_working_days)), row.precision("amount")) + additional_amount
+		if (
+			self.salary_structure
+			and 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 (
+				row.salary_component != timesheet_component
+				or getdate(self.start_date) < joining_date
+				or (relieving_date and getdate(self.end_date) > relieving_date)
+			)
+		):
+			additional_amount = flt(
+				(flt(row.additional_amount) * flt(self.payment_days) / cint(self.total_working_days)),
+				row.precision("additional_amount"),
+			)
+			amount = (
+				flt(
+					(flt(row.default_amount) * flt(self.payment_days) / cint(self.total_working_days)),
+					row.precision("amount"),
+				)
+				+ additional_amount
+			)
 
-		elif not self.payment_days and row.salary_component != timesheet_component and cint(row.depends_on_payment_days):
+		elif (
+			not self.payment_days
+			and row.salary_component != timesheet_component
+			and cint(row.depends_on_payment_days)
+		):
 			amount, additional_amount = 0, 0
 		elif not row.amount:
 			amount = flt(row.default_amount) + flt(row.additional_amount)
 
 		# apply rounding
-		if frappe.get_cached_value("Salary Component", row.salary_component, "round_to_the_nearest_integer"):
-			amount, additional_amount = rounded(amount), rounded(additional_amount)
+		if frappe.get_cached_value(
+			"Salary Component", row.salary_component, "round_to_the_nearest_integer"
+		):
+			amount, additional_amount = rounded(amount or 0), rounded(additional_amount or 0)
 
 		return amount, additional_amount
 
 	def calculate_unclaimed_taxable_benefits(self, payroll_period):
 		# get total sum of benefits paid
-		total_benefits_paid = flt(frappe.db.sql("""
+		total_benefits_paid = flt(
+			frappe.db.sql(
+				"""
 			select sum(sd.amount)
 			from `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name
 			where
@@ -988,21 +1208,29 @@
 				and ss.employee=%(employee)s
 				and ss.start_date between %(start_date)s and %(end_date)s
 				and ss.end_date between %(start_date)s and %(end_date)s
-		""", {
-			"employee": self.employee,
-			"start_date": payroll_period.start_date,
-			"end_date": self.start_date
-		})[0][0])
+		""",
+				{
+					"employee": self.employee,
+					"start_date": payroll_period.start_date,
+					"end_date": self.start_date,
+				},
+			)[0][0]
+		)
 
 		# get total benefits claimed
-		total_benefits_claimed = flt(frappe.db.sql("""
+		total_benefits_claimed = flt(
+			frappe.db.sql(
+				"""
 			select sum(claimed_amount)
 			from `tabEmployee Benefit Claim`
 			where
 				docstatus=1
 				and employee=%s
 				and claim_date between %s and %s
-		""", (self.employee, payroll_period.start_date, self.end_date))[0][0])
+		""",
+				(self.employee, payroll_period.start_date, self.end_date),
+			)[0][0]
+		)
 
 		return total_benefits_paid - total_benefits_claimed
 
@@ -1010,15 +1238,19 @@
 		total_exemption_amount = 0
 		if tax_slab.allow_tax_exemption:
 			if self.deduct_tax_for_unsubmitted_tax_exemption_proof:
-				exemption_proof = frappe.db.get_value("Employee Tax Exemption Proof Submission",
+				exemption_proof = frappe.db.get_value(
+					"Employee Tax Exemption Proof Submission",
 					{"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1},
-					["exemption_amount"])
+					["exemption_amount"],
+				)
 				if exemption_proof:
 					total_exemption_amount = exemption_proof
 			else:
-				declaration = frappe.db.get_value("Employee Tax Exemption Declaration",
+				declaration = frappe.db.get_value(
+					"Employee Tax Exemption Declaration",
 					{"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1},
-					["total_exemption_amount"])
+					["total_exemption_amount"],
+				)
 				if declaration:
 					total_exemption_amount = declaration
 
@@ -1027,61 +1259,21 @@
 		return total_exemption_amount
 
 	def get_income_form_other_sources(self, payroll_period):
-		return frappe.get_all("Employee Other Income",
+		return frappe.get_all(
+			"Employee Other Income",
 			filters={
 				"employee": self.employee,
 				"payroll_period": payroll_period.name,
 				"company": self.company,
-				"docstatus": 1
+				"docstatus": 1,
 			},
-			fields="SUM(amount) as total_amount"
+			fields="SUM(amount) as total_amount",
 		)[0].total_amount
 
-	def calculate_tax_by_tax_slab(self, annual_taxable_earning, tax_slab):
-		data = self.get_data_for_eval()
-		data.update({"annual_taxable_earning": annual_taxable_earning})
-		tax_amount = 0
-		for slab in tax_slab.slabs:
-			cond = cstr(slab.condition).strip()
-			if cond and not self.eval_tax_slab_condition(cond, data):
-				continue
-			if not slab.to_amount and annual_taxable_earning >= slab.from_amount:
-				tax_amount += (annual_taxable_earning - slab.from_amount + 1) * slab.percent_deduction *.01
-				continue
-			if annual_taxable_earning >= slab.from_amount and annual_taxable_earning < slab.to_amount:
-				tax_amount += (annual_taxable_earning - slab.from_amount + 1) * slab.percent_deduction *.01
-			elif annual_taxable_earning >= slab.from_amount and annual_taxable_earning >= slab.to_amount:
-				tax_amount += (slab.to_amount - slab.from_amount + 1) * slab.percent_deduction * .01
-
-		# other taxes and charges on income tax
-		for d in tax_slab.other_taxes_and_charges:
-			if flt(d.min_taxable_income) and flt(d.min_taxable_income) > annual_taxable_earning:
-				continue
-
-			if flt(d.max_taxable_income) and flt(d.max_taxable_income) < annual_taxable_earning:
-				continue
-
-			tax_amount += tax_amount * flt(d.percent) / 100
-
-		return tax_amount
-
-	def eval_tax_slab_condition(self, condition, data):
-		try:
-			condition = condition.strip()
-			if condition:
-				return frappe.safe_eval(condition, self.whitelisted_globals, data)
-		except NameError as err:
-			frappe.throw(_("{0} <br> This error can be due to missing or deleted field.").format(err),
-				title=_("Name error"))
-		except SyntaxError as err:
-			frappe.throw(_("Syntax error in condition: {0}").format(err))
-		except Exception as e:
-			frappe.throw(_("Error in formula or condition: {0}").format(e))
-			raise
-
 	def get_component_totals(self, component_type, depends_on_payment_days=0):
-		joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
-			["date_of_joining", "relieving_date"])
+		joining_date, relieving_date = frappe.get_cached_value(
+			"Employee", self.employee, ["date_of_joining", "relieving_date"]
+		)
 
 		total = 0.0
 		for d in self.get(component_type):
@@ -1094,14 +1286,17 @@
 		return total
 
 	def get_joining_and_relieving_dates(self):
-		joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
-			["date_of_joining", "relieving_date"])
+		joining_date, relieving_date = frappe.get_cached_value(
+			"Employee", self.employee, ["date_of_joining", "relieving_date"]
+		)
 
 		if not relieving_date:
 			relieving_date = getdate(self.end_date)
 
 		if not joining_date:
-			frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name)))
+			frappe.throw(
+				_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name))
+			)
 
 		return joining_date, relieving_date
 
@@ -1110,28 +1305,38 @@
 		self.total_interest_amount = 0
 		self.total_principal_amount = 0
 
-		if not self.get('loans'):
+		if not self.get("loans"):
 			for loan in self.get_loan_details():
 
 				amounts = calculate_amounts(loan.name, self.posting_date, "Regular Payment")
 
-				if amounts['interest_amount'] or amounts['payable_principal_amount']:
-					self.append('loans', {
-						'loan': loan.name,
-						'total_payment': amounts['interest_amount'] + amounts['payable_principal_amount'],
-						'interest_amount': amounts['interest_amount'],
-						'principal_amount': amounts['payable_principal_amount'],
-						'loan_account': loan.loan_account,
-						'interest_income_account': loan.interest_income_account
-					})
+				if amounts["interest_amount"] or amounts["payable_principal_amount"]:
+					self.append(
+						"loans",
+						{
+							"loan": loan.name,
+							"total_payment": amounts["interest_amount"] + amounts["payable_principal_amount"],
+							"interest_amount": amounts["interest_amount"],
+							"principal_amount": amounts["payable_principal_amount"],
+							"loan_account": loan.loan_account,
+							"interest_income_account": loan.interest_income_account,
+						},
+					)
 
-		for payment in self.get('loans'):
+		for payment in self.get("loans"):
 			amounts = calculate_amounts(payment.loan, self.posting_date, "Regular Payment")
-			total_amount = amounts['interest_amount'] + amounts['payable_principal_amount']
+			total_amount = amounts["interest_amount"] + amounts["payable_principal_amount"]
 			if payment.total_payment > total_amount:
-				frappe.throw(_("""Row {0}: Paid amount {1} is greater than pending accrued amount {2} against loan {3}""")
-					.format(payment.idx, frappe.bold(payment.total_payment),
-						frappe.bold(total_amount), frappe.bold(payment.loan)))
+				frappe.throw(
+					_(
+						"""Row {0}: Paid amount {1} is greater than pending accrued amount {2} against loan {3}"""
+					).format(
+						payment.idx,
+						frappe.bold(payment.total_payment),
+						frappe.bold(total_amount),
+						frappe.bold(payment.loan),
+					)
+				)
 
 			self.total_interest_amount += payment.interest_amount
 			self.total_principal_amount += payment.principal_amount
@@ -1139,27 +1344,49 @@
 			self.total_loan_repayment += payment.total_payment
 
 	def get_loan_details(self):
-		return frappe.get_all("Loan",
-			fields=["name", "interest_income_account", "loan_account", "loan_type"],
-			filters = {
+		loan_details = frappe.get_all(
+			"Loan",
+			fields=["name", "interest_income_account", "loan_account", "loan_type", "is_term_loan"],
+			filters={
 				"applicant": self.employee,
 				"docstatus": 1,
 				"repay_from_salary": 1,
-				"company": self.company
-			})
+				"company": self.company,
+			},
+		)
+
+		if loan_details:
+			for loan in loan_details:
+				if loan.is_term_loan:
+					process_loan_interest_accrual_for_term_loans(
+						posting_date=self.posting_date, loan_type=loan.loan_type, loan=loan.name
+					)
+
+		return loan_details
 
 	def make_loan_repayment_entry(self):
 		payroll_payable_account = get_payroll_payable_account(self.company, self.payroll_entry)
 		for loan in self.loans:
 			if loan.total_payment:
-				repayment_entry = create_repayment_entry(loan.loan, self.employee,
-					self.company, self.posting_date, loan.loan_type, "Regular Payment", loan.interest_amount,
-					loan.principal_amount, loan.total_payment, payroll_payable_account=payroll_payable_account)
+				repayment_entry = create_repayment_entry(
+					loan.loan,
+					self.employee,
+					self.company,
+					self.posting_date,
+					loan.loan_type,
+					"Regular Payment",
+					loan.interest_amount,
+					loan.principal_amount,
+					loan.total_payment,
+					payroll_payable_account=payroll_payable_account,
+				)
 
 				repayment_entry.save()
 				repayment_entry.submit()
 
-				frappe.db.set_value("Salary Slip Loan", loan.name, "loan_repayment_entry", repayment_entry.name)
+				frappe.db.set_value(
+					"Salary Slip Loan", loan.name, "loan_repayment_entry", repayment_entry.name
+				)
 
 	def cancel_loan_repayment_entry(self):
 		for loan in self.loans:
@@ -1175,19 +1402,23 @@
 		if payroll_settings.encrypt_salary_slips_in_emails:
 			password = generate_password_for_pdf(payroll_settings.password_policy, self.employee)
 			message += """<br>Note: Your salary slip is password protected,
-				the password to unlock the PDF is of the format {0}. """.format(payroll_settings.password_policy)
+				the password to unlock the PDF is of the format {0}. """.format(
+				payroll_settings.password_policy
+			)
 
 		if receiver:
 			email_args = {
 				"recipients": [receiver],
 				"message": _(message),
-				"subject": 'Salary Slip - from {0} to {1}'.format(self.start_date, self.end_date),
-				"attachments": [frappe.attach_print(self.doctype, self.name, file_name=self.name, password=password)],
+				"subject": "Salary Slip - from {0} to {1}".format(self.start_date, self.end_date),
+				"attachments": [
+					frappe.attach_print(self.doctype, self.name, file_name=self.name, password=password)
+				],
 				"reference_doctype": self.doctype,
-				"reference_name": self.name
-				}
+				"reference_name": self.name,
+			}
 			if not frappe.flags.in_test:
-				enqueue(method=frappe.sendmail, queue='short', timeout=300, is_async=True, **email_args)
+				enqueue(method=frappe.sendmail, queue="short", timeout=300, is_async=True, **email_args)
 			else:
 				frappe.sendmail(**email_args)
 		else:
@@ -1196,21 +1427,20 @@
 	def update_status(self, salary_slip=None):
 		for data in self.timesheets:
 			if data.time_sheet:
-				timesheet = frappe.get_doc('Timesheet', data.time_sheet)
+				timesheet = frappe.get_doc("Timesheet", data.time_sheet)
 				timesheet.salary_slip = salary_slip
 				timesheet.flags.ignore_validate_update_after_submit = True
 				timesheet.set_status()
 				timesheet.save()
 
 	def set_status(self, status=None):
-		'''Get and update status'''
+		"""Get and update status"""
 		if not status:
 			status = self.get_status()
 		self.db_set("status", status)
 
-
 	def process_salary_structure(self, for_preview=0):
-		'''Calculate salary after salary structure details have been updated'''
+		"""Calculate salary after salary structure details have been updated"""
 		if not self.salary_slip_based_on_timesheet:
 			self.get_date_details()
 		self.pull_emp_details()
@@ -1218,7 +1448,9 @@
 		self.calculate_net_pay()
 
 	def pull_emp_details(self):
-		emp = frappe.db.get_value("Employee", self.employee, ["bank_name", "bank_ac_no", "salary_mode"], as_dict=1)
+		emp = frappe.db.get_value(
+			"Employee", self.employee, ["bank_name", "bank_ac_no", "salary_mode"], as_dict=1
+		)
 		if emp:
 			self.mode_of_payment = emp.salary_mode
 			self.bank_name = emp.bank_name
@@ -1248,12 +1480,12 @@
 	def set_base_totals(self):
 		self.base_gross_pay = flt(self.gross_pay) * flt(self.exchange_rate)
 		self.base_total_deduction = flt(self.total_deduction) * flt(self.exchange_rate)
-		self.rounded_total = rounded(self.net_pay)
+		self.rounded_total = rounded(self.net_pay or 0)
 		self.base_net_pay = flt(self.net_pay) * flt(self.exchange_rate)
-		self.base_rounded_total = rounded(self.base_net_pay)
+		self.base_rounded_total = rounded(self.base_net_pay or 0)
 		self.set_net_total_in_words()
 
-	#calculate total working hours, earnings based on hourly wages and totals
+	# calculate total working hours, earnings based on hourly wages and totals
 	def calculate_total_for_salary_slip_based_on_timesheet(self):
 		if self.timesheets:
 			self.total_working_hours = 0
@@ -1263,7 +1495,9 @@
 
 		wages_amount = self.total_working_hours * self.hour_rate
 		self.base_hour_rate = flt(self.hour_rate) * flt(self.exchange_rate)
-		salary_component = frappe.db.get_value('Salary Structure', {'name': self.salary_structure}, 'salary_component')
+		salary_component = frappe.db.get_value(
+			"Salary Structure", {"name": self.salary_structure}, "salary_component"
+		)
 		if self.earnings:
 			for i, earning in enumerate(self.earnings):
 				if earning.salary_component == salary_component:
@@ -1275,14 +1509,17 @@
 		year_to_date = 0
 		period_start_date, period_end_date = self.get_year_to_date_period()
 
-		salary_slip_sum = frappe.get_list('Salary Slip',
-			fields = ['sum(net_pay) as net_sum', 'sum(gross_pay) as gross_sum'],
-			filters = {'employee' : self.employee,
-				'start_date' : ['>=', period_start_date],
-				'end_date' : ['<', period_end_date],
-				'name': ['!=', self.name],
-				'docstatus': 1
-			})
+		salary_slip_sum = frappe.get_list(
+			"Salary Slip",
+			fields=["sum(net_pay) as net_sum", "sum(gross_pay) as gross_sum"],
+			filters={
+				"employee": self.employee,
+				"start_date": [">=", period_start_date],
+				"end_date": ["<", period_end_date],
+				"name": ["!=", self.name],
+				"docstatus": 1,
+			},
+		)
 
 		year_to_date = flt(salary_slip_sum[0].net_sum) if salary_slip_sum else 0.0
 		gross_year_to_date = flt(salary_slip_sum[0].gross_sum) if salary_slip_sum else 0.0
@@ -1295,14 +1532,17 @@
 	def compute_month_to_date(self):
 		month_to_date = 0
 		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' : self.employee,
-				'start_date' : ['>=', first_day_of_the_month],
-				'end_date' : ['<', self.start_date],
-				'name': ['!=', self.name],
-				'docstatus': 1
-			})
+		salary_slip_sum = frappe.get_list(
+			"Salary Slip",
+			fields=["sum(net_pay) as sum"],
+			filters={
+				"employee": self.employee,
+				"start_date": [">=", first_day_of_the_month],
+				"end_date": ["<", self.start_date],
+				"name": ["!=", self.name],
+				"docstatus": 1,
+			},
+		)
 
 		month_to_date = flt(salary_slip_sum[0].sum) if salary_slip_sum else 0.0
 
@@ -1312,10 +1552,11 @@
 	def compute_component_wise_year_to_date(self):
 		period_start_date, period_end_date = self.get_year_to_date_period()
 
-		for key in ('earnings', 'deductions'):
+		for key in ("earnings", "deductions"):
 			for component in self.get(key):
 				year_to_date = 0
-				component_sum = frappe.db.sql("""
+				component_sum = frappe.db.sql(
+					"""
 					SELECT sum(detail.amount) as sum
 					FROM `tabSalary Detail` as detail
 					INNER JOIN `tabSalary Slip` as salary_slip
@@ -1327,8 +1568,13 @@
 						AND salary_slip.end_date < %(period_end_date)s
 						AND salary_slip.name != %(docname)s
 						AND salary_slip.docstatus = 1""",
-						{'employee': self.employee, 'component': component.salary_component, 'period_start_date': period_start_date,
-							'period_end_date': period_end_date, 'docname': self.name}
+					{
+						"employee": self.employee,
+						"component": component.salary_component,
+						"period_start_date": period_start_date,
+						"period_end_date": period_end_date,
+						"docname": self.name,
+					},
 				)
 
 				year_to_date = flt(component_sum[0][0]) if component_sum else 0.0
@@ -1350,34 +1596,44 @@
 		return period_start_date, period_end_date
 
 	def add_leave_balances(self):
-		self.set('leave_details', [])
+		self.set("leave_details", [])
 
-		if frappe.db.get_single_value('Payroll Settings', 'show_leave_balances_in_salary_slip'):
+		if frappe.db.get_single_value("Payroll Settings", "show_leave_balances_in_salary_slip"):
 			from erpnext.hr.doctype.leave_application.leave_application import get_leave_details
+
 			leave_details = get_leave_details(self.employee, self.end_date)
 
-			for leave_type, leave_values in leave_details['leave_allocation'].items():
-				self.append('leave_details', {
-					'leave_type': leave_type,
-					'total_allocated_leaves': flt(leave_values.get('total_leaves')),
-					'expired_leaves': flt(leave_values.get('expired_leaves')),
-					'used_leaves': flt(leave_values.get('leaves_taken')),
-					'pending_leaves': flt(leave_values.get('pending_leaves')),
-					'available_leaves': flt(leave_values.get('remaining_leaves'))
-				})
+			for leave_type, leave_values in leave_details["leave_allocation"].items():
+				self.append(
+					"leave_details",
+					{
+						"leave_type": leave_type,
+						"total_allocated_leaves": flt(leave_values.get("total_leaves")),
+						"expired_leaves": flt(leave_values.get("expired_leaves")),
+						"used_leaves": flt(leave_values.get("leaves_taken")),
+						"pending_leaves": flt(leave_values.get("leaves_pending_approval")),
+						"available_leaves": flt(leave_values.get("remaining_leaves")),
+					},
+				)
+
 
 def unlink_ref_doc_from_salary_slip(ref_no):
-	linked_ss = frappe.db.sql_list("""select name from `tabSalary Slip`
-	where journal_entry=%s and docstatus < 2""", (ref_no))
+	linked_ss = frappe.db.sql_list(
+		"""select name from `tabSalary Slip`
+	where journal_entry=%s and docstatus < 2""",
+		(ref_no),
+	)
 	if linked_ss:
 		for ss in linked_ss:
 			ss_doc = frappe.get_doc("Salary Slip", ss)
 			frappe.db.set_value("Salary Slip", ss_doc.name, "journal_entry", "")
 
+
 def generate_password_for_pdf(policy_template, employee):
 	employee = frappe.get_doc("Employee", employee)
 	return policy_template.format(**employee.as_dict())
 
+
 def get_salary_component_data(component):
 	return frappe.get_value(
 		"Salary Component",
@@ -1394,10 +1650,115 @@
 		as_dict=1,
 	)
 
+
 def get_payroll_payable_account(company, payroll_entry):
 	if payroll_entry:
-		payroll_payable_account = frappe.db.get_value('Payroll Entry', payroll_entry, 'payroll_payable_account')
+		payroll_payable_account = frappe.db.get_value(
+			"Payroll Entry", payroll_entry, "payroll_payable_account"
+		)
 	else:
-		payroll_payable_account = frappe.db.get_value('Company', company, 'default_payroll_payable_account')
+		payroll_payable_account = frappe.db.get_value(
+			"Company", company, "default_payroll_payable_account"
+		)
 
-	return payroll_payable_account
\ No newline at end of file
+	return payroll_payable_account
+
+
+def calculate_tax_by_tax_slab(
+	annual_taxable_earning, tax_slab, eval_globals=None, eval_locals=None
+):
+	eval_locals.update({"annual_taxable_earning": annual_taxable_earning})
+	tax_amount = 0
+	for slab in tax_slab.slabs:
+		cond = cstr(slab.condition).strip()
+		if cond and not eval_tax_slab_condition(cond, eval_globals, eval_locals):
+			continue
+		if not slab.to_amount and annual_taxable_earning >= slab.from_amount:
+			tax_amount += (annual_taxable_earning - slab.from_amount + 1) * slab.percent_deduction * 0.01
+			continue
+		if annual_taxable_earning >= slab.from_amount and annual_taxable_earning < slab.to_amount:
+			tax_amount += (annual_taxable_earning - slab.from_amount + 1) * slab.percent_deduction * 0.01
+		elif annual_taxable_earning >= slab.from_amount and annual_taxable_earning >= slab.to_amount:
+			tax_amount += (slab.to_amount - slab.from_amount + 1) * slab.percent_deduction * 0.01
+
+	# other taxes and charges on income tax
+	for d in tax_slab.other_taxes_and_charges:
+		if flt(d.min_taxable_income) and flt(d.min_taxable_income) > annual_taxable_earning:
+			continue
+
+		if flt(d.max_taxable_income) and flt(d.max_taxable_income) < annual_taxable_earning:
+			continue
+
+		tax_amount += tax_amount * flt(d.percent) / 100
+
+	return tax_amount
+
+
+def eval_tax_slab_condition(condition, eval_globals=None, eval_locals=None):
+	if not eval_globals:
+		eval_globals = {
+			"int": int,
+			"float": float,
+			"long": int,
+			"round": round,
+			"date": datetime.date,
+			"getdate": getdate,
+		}
+
+	try:
+		condition = condition.strip()
+		if condition:
+			return frappe.safe_eval(condition, eval_globals, eval_locals)
+	except NameError as err:
+		frappe.throw(
+			_("{0} <br> This error can be due to missing or deleted field.").format(err),
+			title=_("Name error"),
+		)
+	except SyntaxError as err:
+		frappe.throw(_("Syntax error in condition: {0} in Income Tax Slab").format(err))
+	except Exception as e:
+		frappe.throw(_("Error in formula or condition: {0} in Income Tax Slab").format(e))
+		raise
+
+
+def get_lwp_or_ppl_for_date(date, employee, holidays):
+	LeaveApplication = frappe.qb.DocType("Leave Application")
+	LeaveType = frappe.qb.DocType("Leave Type")
+
+	is_half_day = (
+		frappe.qb.terms.Case()
+		.when(
+			(
+				(LeaveApplication.half_day_date == date)
+				| (LeaveApplication.from_date == LeaveApplication.to_date)
+			),
+			LeaveApplication.half_day,
+		)
+		.else_(0)
+	).as_("is_half_day")
+
+	query = (
+		frappe.qb.from_(LeaveApplication)
+		.inner_join(LeaveType)
+		.on((LeaveType.name == LeaveApplication.leave_type))
+		.select(
+			LeaveApplication.name,
+			LeaveType.is_ppl,
+			LeaveType.fraction_of_daily_salary_per_leave,
+			(is_half_day),
+		)
+		.where(
+			(((LeaveType.is_lwp == 1) | (LeaveType.is_ppl == 1)))
+			& (LeaveApplication.docstatus == 1)
+			& (LeaveApplication.status == "Approved")
+			& (LeaveApplication.employee == employee)
+			& ((LeaveApplication.salary_slip.isnull()) | (LeaveApplication.salary_slip == ""))
+			& ((LeaveApplication.from_date <= date) & (date <= LeaveApplication.to_date))
+		)
+	)
+
+	# if it's a holiday only include if leave type has "include holiday" enabled
+	if date in holidays:
+		query = query.where((LeaveType.include_holiday == "1"))
+
+	return query.run(as_dict=True)
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
index 6a5debf..a8b6bb5 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -7,10 +7,12 @@
 
 import frappe
 from frappe.model.document import Document
+from frappe.tests.utils import change_settings
 from frappe.utils import (
 	add_days,
 	add_months,
 	cstr,
+	date_diff,
 	flt,
 	get_first_day,
 	get_last_day,
@@ -21,6 +23,7 @@
 
 import erpnext
 from erpnext.accounts.utils import get_fiscal_year
+from erpnext.hr.doctype.attendance.attendance import mark_attendance
 from erpnext.hr.doctype.employee.test_employee import make_employee
 from erpnext.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation
 from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
@@ -35,18 +38,18 @@
 class TestSalarySlip(unittest.TestCase):
 	def setUp(self):
 		setup_test()
+		frappe.flags.pop("via_payroll_entry", None)
 
 	def tearDown(self):
+		frappe.db.rollback()
 		frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0)
 		frappe.set_user("Administrator")
 
+	@change_settings(
+		"Payroll Settings", {"payroll_based_on": "Attendance", "daily_wages_fraction_for_half_day": 0.75}
+	)
 	def test_payment_days_based_on_attendance(self):
-		from erpnext.hr.doctype.attendance.attendance import mark_attendance
-		no_of_days = self.get_no_of_days()
-
-		# Payroll based on attendance
-		frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Attendance")
-		frappe.db.set_value("Payroll Settings", None, "daily_wages_fraction_for_half_day", 0.75)
+		no_of_days = get_no_of_days()
 
 		emp_id = make_employee("test_payment_days_based_on_attendance@salary.com")
 		frappe.db.set_value("Employee", emp_id, {"relieving_date": None, "status": "Active"})
@@ -56,21 +59,50 @@
 		month_start_date = get_first_day(nowdate())
 		month_end_date = get_last_day(nowdate())
 
-		first_sunday = frappe.db.sql("""
+		first_sunday = frappe.db.sql(
+			"""
 			select holiday_date from `tabHoliday`
 			where parent = 'Salary Slip Test Holiday List'
 				and holiday_date between %s and %s
 			order by holiday_date
-		""", (month_start_date, month_end_date))[0][0]
+		""",
+			(month_start_date, month_end_date),
+		)[0][0]
 
-		mark_attendance(emp_id, first_sunday, 'Absent', ignore_validate=True) # invalid lwp
-		mark_attendance(emp_id, add_days(first_sunday, 1), 'Absent', ignore_validate=True) # counted as absent
-		mark_attendance(emp_id, add_days(first_sunday, 2), 'Half Day', leave_type='Leave Without Pay', ignore_validate=True) # valid 0.75 lwp
-		mark_attendance(emp_id, add_days(first_sunday, 3), 'On Leave', leave_type='Leave Without Pay', ignore_validate=True) # valid lwp
-		mark_attendance(emp_id, add_days(first_sunday, 4), 'On Leave', leave_type='Casual Leave', ignore_validate=True) # invalid lwp
-		mark_attendance(emp_id, add_days(first_sunday, 7), 'On Leave', leave_type='Leave Without Pay', ignore_validate=True) # invalid lwp
+		mark_attendance(emp_id, first_sunday, "Absent", ignore_validate=True)  # invalid lwp
+		mark_attendance(
+			emp_id, add_days(first_sunday, 1), "Absent", ignore_validate=True
+		)  # counted as absent
+		mark_attendance(
+			emp_id,
+			add_days(first_sunday, 2),
+			"Half Day",
+			leave_type="Leave Without Pay",
+			ignore_validate=True,
+		)  # valid 0.75 lwp
+		mark_attendance(
+			emp_id,
+			add_days(first_sunday, 3),
+			"On Leave",
+			leave_type="Leave Without Pay",
+			ignore_validate=True,
+		)  # valid lwp
+		mark_attendance(
+			emp_id, add_days(first_sunday, 4), "On Leave", leave_type="Casual Leave", ignore_validate=True
+		)  # invalid lwp
+		mark_attendance(
+			emp_id,
+			add_days(first_sunday, 7),
+			"On Leave",
+			leave_type="Leave Without Pay",
+			ignore_validate=True,
+		)  # invalid lwp
 
-		ss = make_employee_salary_slip("test_payment_days_based_on_attendance@salary.com", "Monthly", "Test Payment Based On Attendence")
+		ss = make_employee_salary_slip(
+			"test_payment_days_based_on_attendance@salary.com",
+			"Monthly",
+			"Test Payment Based On Attendence",
+		)
 
 		self.assertEqual(ss.leave_without_pay, 1.25)
 		self.assertEqual(ss.absent_days, 1)
@@ -80,18 +112,162 @@
 
 		self.assertEqual(ss.payment_days, days_in_month - no_of_holidays - 2.25)
 
-		#Gross pay calculation based on attendances
-		gross_pay = 78000 - ((78000 / (days_in_month - no_of_holidays)) * flt(ss.leave_without_pay + ss.absent_days))
+		# Gross pay calculation based on attendances
+		gross_pay = 78000 - (
+			(78000 / (days_in_month - no_of_holidays)) * flt(ss.leave_without_pay + ss.absent_days)
+		)
 
 		self.assertEqual(ss.gross_pay, gross_pay)
 
-		frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave")
+	@change_settings(
+		"Payroll Settings",
+		{
+			"payroll_based_on": "Attendance",
+			"consider_unmarked_attendance_as": "Absent",
+			"include_holidays_in_total_working_days": True,
+		},
+	)
+	def test_payment_days_for_mid_joinee_including_holidays(self):
+		no_of_days = get_no_of_days()
+		month_start_date, month_end_date = get_first_day(nowdate()), get_last_day(nowdate())
 
+		new_emp_id = make_employee("test_payment_days_based_on_joining_date@salary.com")
+		joining_date, relieving_date = add_days(month_start_date, 3), add_days(month_end_date, -5)
+
+		for days in range(date_diff(month_end_date, month_start_date) + 1):
+			date = add_days(month_start_date, days)
+			mark_attendance(new_emp_id, date, "Present", ignore_validate=True)
+
+		# Case 1: relieving in mid month
+		frappe.db.set_value(
+			"Employee",
+			new_emp_id,
+			{"date_of_joining": month_start_date, "relieving_date": relieving_date, "status": "Active"},
+		)
+
+		new_ss = make_employee_salary_slip(
+			"test_payment_days_based_on_joining_date@salary.com",
+			"Monthly",
+			"Test Payment Based On Attendence",
+		)
+		self.assertEqual(new_ss.payment_days, no_of_days[0] - 5)
+
+		# Case 2: joining in mid month
+		frappe.db.set_value(
+			"Employee",
+			new_emp_id,
+			{"date_of_joining": joining_date, "relieving_date": month_end_date, "status": "Active"},
+		)
+
+		frappe.delete_doc("Salary Slip", new_ss.name, force=True)
+		new_ss = make_employee_salary_slip(
+			"test_payment_days_based_on_joining_date@salary.com",
+			"Monthly",
+			"Test Payment Based On Attendence",
+		)
+		self.assertEqual(new_ss.payment_days, no_of_days[0] - 3)
+
+		# Case 3: joining and relieving in mid-month
+		frappe.db.set_value(
+			"Employee",
+			new_emp_id,
+			{"date_of_joining": joining_date, "relieving_date": relieving_date, "status": "Left"},
+		)
+
+		frappe.delete_doc("Salary Slip", new_ss.name, force=True)
+		new_ss = make_employee_salary_slip(
+			"test_payment_days_based_on_joining_date@salary.com",
+			"Monthly",
+			"Test Payment Based On Attendence",
+		)
+
+		self.assertEqual(new_ss.total_working_days, no_of_days[0])
+		self.assertEqual(new_ss.payment_days, no_of_days[0] - 8)
+
+	@change_settings(
+		"Payroll Settings",
+		{
+			"payroll_based_on": "Attendance",
+			"consider_unmarked_attendance_as": "Absent",
+			"include_holidays_in_total_working_days": True,
+		},
+	)
+	def test_payment_days_for_mid_joinee_including_holidays_and_unmarked_days(self):
+		# tests mid month joining and relieving along with unmarked days
+		from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
+
+		no_of_days = get_no_of_days()
+		month_start_date, month_end_date = get_first_day(nowdate()), get_last_day(nowdate())
+
+		new_emp_id = make_employee("test_payment_days_based_on_joining_date@salary.com")
+		joining_date, relieving_date = add_days(month_start_date, 3), add_days(month_end_date, -5)
+		holidays = 0
+
+		for days in range(date_diff(relieving_date, joining_date) + 1):
+			date = add_days(joining_date, days)
+			if not is_holiday("Salary Slip Test Holiday List", date):
+				mark_attendance(new_emp_id, date, "Present", ignore_validate=True)
+			else:
+				holidays += 1
+
+		frappe.db.set_value(
+			"Employee",
+			new_emp_id,
+			{"date_of_joining": joining_date, "relieving_date": relieving_date, "status": "Left"},
+		)
+
+		new_ss = make_employee_salary_slip(
+			"test_payment_days_based_on_joining_date@salary.com",
+			"Monthly",
+			"Test Payment Based On Attendence",
+		)
+
+		self.assertEqual(new_ss.total_working_days, no_of_days[0])
+		self.assertEqual(new_ss.payment_days, no_of_days[0] - holidays - 8)
+
+	@change_settings(
+		"Payroll Settings",
+		{
+			"payroll_based_on": "Attendance",
+			"consider_unmarked_attendance_as": "Absent",
+			"include_holidays_in_total_working_days": False,
+		},
+	)
+	def test_payment_days_for_mid_joinee_excluding_holidays(self):
+		from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
+
+		no_of_days = get_no_of_days()
+		month_start_date, month_end_date = get_first_day(nowdate()), get_last_day(nowdate())
+
+		new_emp_id = make_employee("test_payment_days_based_on_joining_date@salary.com")
+		joining_date, relieving_date = add_days(month_start_date, 3), add_days(month_end_date, -5)
+		frappe.db.set_value(
+			"Employee",
+			new_emp_id,
+			{"date_of_joining": joining_date, "relieving_date": relieving_date, "status": "Left"},
+		)
+
+		holidays = 0
+
+		for days in range(date_diff(relieving_date, joining_date) + 1):
+			date = add_days(joining_date, days)
+			if not is_holiday("Salary Slip Test Holiday List", date):
+				mark_attendance(new_emp_id, date, "Present", ignore_validate=True)
+			else:
+				holidays += 1
+
+		new_ss = make_employee_salary_slip(
+			"test_payment_days_based_on_joining_date@salary.com",
+			"Monthly",
+			"Test Payment Based On Attendence",
+		)
+
+		self.assertEqual(new_ss.total_working_days, no_of_days[0] - no_of_days[1])
+		self.assertEqual(new_ss.payment_days, no_of_days[0] - holidays - 8)
+
+	@change_settings("Payroll Settings", {"payroll_based_on": "Leave"})
 	def test_payment_days_based_on_leave_application(self):
-		no_of_days = self.get_no_of_days()
-
-		# Payroll based on attendance
-		frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave")
+		no_of_days = get_no_of_days()
 
 		emp_id = make_employee("test_payment_days_based_on_leave_application@salary.com")
 		frappe.db.set_value("Employee", emp_id, {"relieving_date": None, "status": "Active"})
@@ -101,30 +277,41 @@
 		month_start_date = get_first_day(nowdate())
 		month_end_date = get_last_day(nowdate())
 
-		first_sunday = frappe.db.sql("""
+		first_sunday = frappe.db.sql(
+			"""
 			select holiday_date from `tabHoliday`
 			where parent = 'Salary Slip Test Holiday List'
 				and holiday_date between %s and %s
 			order by holiday_date
-		""", (month_start_date, month_end_date))[0][0]
+		""",
+			(month_start_date, month_end_date),
+		)[0][0]
 
 		make_leave_application(emp_id, first_sunday, add_days(first_sunday, 3), "Leave Without Pay")
 
-		leave_type_ppl = create_leave_type(leave_type_name="Test Partially Paid Leave", is_ppl = 1)
+		leave_type_ppl = create_leave_type(leave_type_name="Test Partially Paid Leave", is_ppl=1)
 		leave_type_ppl.save()
 
 		alloc = create_leave_allocation(
-			employee = emp_id, from_date = add_days(first_sunday, 4),
-			to_date = add_days(first_sunday, 10), new_leaves_allocated = 3,
-			leave_type = "Test Partially Paid Leave")
+			employee=emp_id,
+			from_date=add_days(first_sunday, 4),
+			to_date=add_days(first_sunday, 10),
+			new_leaves_allocated=3,
+			leave_type="Test Partially Paid Leave",
+		)
 		alloc.save()
 		alloc.submit()
 
-		#two day leave ppl with fraction_of_daily_salary_per_leave = 0.5 equivalent to single day lwp
-		make_leave_application(emp_id, add_days(first_sunday, 4), add_days(first_sunday, 5), "Test Partially Paid Leave")
+		# two day leave ppl with fraction_of_daily_salary_per_leave = 0.5 equivalent to single day lwp
+		make_leave_application(
+			emp_id, add_days(first_sunday, 4), add_days(first_sunday, 5), "Test Partially Paid Leave"
+		)
 
-		ss = make_employee_salary_slip("test_payment_days_based_on_leave_application@salary.com", "Monthly", "Test Payment Based On Leave Application")
-
+		ss = make_employee_salary_slip(
+			"test_payment_days_based_on_leave_application@salary.com",
+			"Monthly",
+			"Test Payment Based On Leave Application",
+		)
 
 		self.assertEqual(ss.leave_without_pay, 4)
 
@@ -133,8 +320,7 @@
 
 		self.assertEqual(ss.payment_days, days_in_month - no_of_holidays - 4)
 
-		frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave")
-
+	@change_settings("Payroll Settings", {"payroll_based_on": "Attendance"})
 	def test_payment_days_in_salary_slip_based_on_timesheet(self):
 		from erpnext.hr.doctype.attendance.attendance import mark_attendance
 		from erpnext.projects.doctype.timesheet.test_timesheet import (
@@ -145,24 +331,30 @@
 			make_salary_slip as make_salary_slip_for_timesheet,
 		)
 
-		# Payroll based on attendance
-		frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Attendance")
-
-		emp = make_employee("test_employee_timesheet@salary.com", company="_Test Company", holiday_list="Salary Slip Test Holiday List")
+		emp = make_employee(
+			"test_employee_timesheet@salary.com",
+			company="_Test Company",
+			holiday_list="Salary Slip Test Holiday List",
+		)
 		frappe.db.set_value("Employee", emp, {"relieving_date": None, "status": "Active"})
 
 		# mark attendance
 		month_start_date = get_first_day(nowdate())
 		month_end_date = get_last_day(nowdate())
 
-		first_sunday = frappe.db.sql("""
+		first_sunday = frappe.db.sql(
+			"""
 			select holiday_date from `tabHoliday`
 			where parent = 'Salary Slip Test Holiday List'
 				and holiday_date between %s and %s
 			order by holiday_date
-		""", (month_start_date, month_end_date))[0][0]
+		""",
+			(month_start_date, month_end_date),
+		)[0][0]
 
-		mark_attendance(emp, add_days(first_sunday, 1), 'Absent', ignore_validate=True) # counted as absent
+		mark_attendance(
+			emp, add_days(first_sunday, 1), "Absent", ignore_validate=True
+		)  # counted as absent
 
 		# salary structure based on timesheet
 		make_salary_structure_for_timesheet(emp)
@@ -174,49 +366,57 @@
 		salary_slip.submit()
 		salary_slip.reload()
 
-		no_of_days = self.get_no_of_days()
+		no_of_days = get_no_of_days()
 		days_in_month = no_of_days[0]
 		no_of_holidays = no_of_days[1]
 
 		self.assertEqual(salary_slip.payment_days, days_in_month - no_of_holidays - 1)
 
 		# gross pay calculation based on attendance (payment days)
-		gross_pay = 78100 - ((78000 / (days_in_month - no_of_holidays)) * flt(salary_slip.leave_without_pay + salary_slip.absent_days))
+		gross_pay = 78100 - (
+			(78000 / (days_in_month - no_of_holidays))
+			* flt(salary_slip.leave_without_pay + salary_slip.absent_days)
+		)
 
 		self.assertEqual(salary_slip.gross_pay, flt(gross_pay, 2))
 
-		frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave")
-
+	@change_settings("Payroll Settings", {"payroll_based_on": "Attendance"})
 	def test_component_amount_dependent_on_another_payment_days_based_component(self):
 		from erpnext.hr.doctype.attendance.attendance import mark_attendance
 		from erpnext.payroll.doctype.salary_structure.test_salary_structure import (
 			create_salary_structure_assignment,
 		)
 
-		# Payroll based on attendance
-		frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Attendance")
-
 		salary_structure = make_salary_structure_for_payment_days_based_component_dependency()
 		employee = make_employee("test_payment_days_based_component@salary.com", company="_Test Company")
 
 		# base = 50000
-		create_salary_structure_assignment(employee, salary_structure.name, company="_Test Company", currency="INR")
+		create_salary_structure_assignment(
+			employee, salary_structure.name, company="_Test Company", currency="INR"
+		)
 
 		# mark employee absent for a day since this case works fine if payment days are equal to working days
 		month_start_date = get_first_day(nowdate())
 		month_end_date = get_last_day(nowdate())
 
-		first_sunday = frappe.db.sql("""
+		first_sunday = frappe.db.sql(
+			"""
 			select holiday_date from `tabHoliday`
 			where parent = 'Salary Slip Test Holiday List'
 				and holiday_date between %s and %s
 			order by holiday_date
-		""", (month_start_date, month_end_date))[0][0]
+		""",
+			(month_start_date, month_end_date),
+		)[0][0]
 
-		mark_attendance(employee, add_days(first_sunday, 1), 'Absent', ignore_validate=True) # counted as absent
+		mark_attendance(
+			employee, add_days(first_sunday, 1), "Absent", ignore_validate=True
+		)  # counted as absent
 
 		# make salary slip and assert payment days
-		ss = make_salary_slip_for_payment_days_dependency_test("test_payment_days_based_component@salary.com", salary_structure.name)
+		ss = make_salary_slip_for_payment_days_dependency_test(
+			"test_payment_days_based_component@salary.com", salary_structure.name
+		)
 		self.assertEqual(ss.absent_days, 1)
 
 		ss.reload()
@@ -238,17 +438,32 @@
 		expected_amount = flt((flt(ss.gross_pay) - payment_days_based_comp_amount) * 0.12, precision)
 
 		self.assertEqual(actual_amount, expected_amount)
-		frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave")
 
+	@change_settings("Payroll Settings", {"include_holidays_in_total_working_days": 1})
 	def test_salary_slip_with_holidays_included(self):
-		no_of_days = self.get_no_of_days()
-		frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 1)
+		no_of_days = get_no_of_days()
 		make_employee("test_salary_slip_with_holidays_included@salary.com")
-		frappe.db.set_value("Employee", frappe.get_value("Employee",
-			{"employee_name":"test_salary_slip_with_holidays_included@salary.com"}, "name"), "relieving_date", None)
-		frappe.db.set_value("Employee", frappe.get_value("Employee",
-			{"employee_name":"test_salary_slip_with_holidays_included@salary.com"}, "name"), "status", "Active")
-		ss = make_employee_salary_slip("test_salary_slip_with_holidays_included@salary.com", "Monthly", "Test Salary Slip With Holidays Included")
+		frappe.db.set_value(
+			"Employee",
+			frappe.get_value(
+				"Employee", {"employee_name": "test_salary_slip_with_holidays_included@salary.com"}, "name"
+			),
+			"relieving_date",
+			None,
+		)
+		frappe.db.set_value(
+			"Employee",
+			frappe.get_value(
+				"Employee", {"employee_name": "test_salary_slip_with_holidays_included@salary.com"}, "name"
+			),
+			"status",
+			"Active",
+		)
+		ss = make_employee_salary_slip(
+			"test_salary_slip_with_holidays_included@salary.com",
+			"Monthly",
+			"Test Salary Slip With Holidays Included",
+		)
 
 		self.assertEqual(ss.total_working_days, no_of_days[0])
 		self.assertEqual(ss.payment_days, no_of_days[0])
@@ -256,15 +471,31 @@
 		self.assertEqual(ss.earnings[1].amount, 3000)
 		self.assertEqual(ss.gross_pay, 78000)
 
+	@change_settings("Payroll Settings", {"include_holidays_in_total_working_days": 0})
 	def test_salary_slip_with_holidays_excluded(self):
-		no_of_days = self.get_no_of_days()
-		frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0)
+		no_of_days = get_no_of_days()
 		make_employee("test_salary_slip_with_holidays_excluded@salary.com")
-		frappe.db.set_value("Employee", frappe.get_value("Employee",
-			{"employee_name":"test_salary_slip_with_holidays_excluded@salary.com"}, "name"), "relieving_date", None)
-		frappe.db.set_value("Employee", frappe.get_value("Employee",
-			{"employee_name":"test_salary_slip_with_holidays_excluded@salary.com"}, "name"), "status", "Active")
-		ss = make_employee_salary_slip("test_salary_slip_with_holidays_excluded@salary.com", "Monthly",  "Test Salary Slip With Holidays Excluded")
+		frappe.db.set_value(
+			"Employee",
+			frappe.get_value(
+				"Employee", {"employee_name": "test_salary_slip_with_holidays_excluded@salary.com"}, "name"
+			),
+			"relieving_date",
+			None,
+		)
+		frappe.db.set_value(
+			"Employee",
+			frappe.get_value(
+				"Employee", {"employee_name": "test_salary_slip_with_holidays_excluded@salary.com"}, "name"
+			),
+			"status",
+			"Active",
+		)
+		ss = make_employee_salary_slip(
+			"test_salary_slip_with_holidays_excluded@salary.com",
+			"Monthly",
+			"Test Salary Slip With Holidays Excluded",
+		)
 
 		self.assertEqual(ss.total_working_days, no_of_days[0] - no_of_days[1])
 		self.assertEqual(ss.payment_days, no_of_days[0] - no_of_days[1])
@@ -273,35 +504,34 @@
 		self.assertEqual(ss.earnings[1].amount, 3000)
 		self.assertEqual(ss.gross_pay, 78000)
 
+	@change_settings("Payroll Settings", {"include_holidays_in_total_working_days": 1})
 	def test_payment_days(self):
 		from erpnext.payroll.doctype.salary_structure.test_salary_structure import (
 			create_salary_structure_assignment,
 		)
 
-		no_of_days = self.get_no_of_days()
-		# Holidays not included in working days
-		frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 1)
+		no_of_days = get_no_of_days()
 
 		# set joinng date in the same month
 		employee = make_employee("test_payment_days@salary.com")
 		if getdate(nowdate()).day >= 15:
-			relieving_date = getdate(add_days(nowdate(),-10))
-			date_of_joining = getdate(add_days(nowdate(),-10))
+			relieving_date = getdate(add_days(nowdate(), -10))
+			date_of_joining = getdate(add_days(nowdate(), -10))
 		elif getdate(nowdate()).day < 15 and getdate(nowdate()).day >= 5:
-			date_of_joining = getdate(add_days(nowdate(),-3))
-			relieving_date = getdate(add_days(nowdate(),-3))
+			date_of_joining = getdate(add_days(nowdate(), -3))
+			relieving_date = getdate(add_days(nowdate(), -3))
 		elif getdate(nowdate()).day < 5 and not getdate(nowdate()).day == 1:
-			date_of_joining = getdate(add_days(nowdate(),-1))
-			relieving_date = getdate(add_days(nowdate(),-1))
+			date_of_joining = getdate(add_days(nowdate(), -1))
+			relieving_date = getdate(add_days(nowdate(), -1))
 		elif getdate(nowdate()).day == 1:
 			date_of_joining = getdate(nowdate())
 			relieving_date = getdate(nowdate())
 
-		frappe.db.set_value("Employee", employee, {
-			"date_of_joining": date_of_joining,
-			"relieving_date": None,
-			"status": "Active"
-		})
+		frappe.db.set_value(
+			"Employee",
+			employee,
+			{"date_of_joining": date_of_joining, "relieving_date": None, "status": "Active"},
+		)
 
 		salary_structure = "Test Payment Days"
 		ss = make_employee_salary_slip("test_payment_days@salary.com", "Monthly", salary_structure)
@@ -310,11 +540,15 @@
 		self.assertEqual(ss.payment_days, (no_of_days[0] - getdate(date_of_joining).day + 1))
 
 		# set relieving date in the same month
-		frappe.db.set_value("Employee", employee, {
-			"date_of_joining": add_days(nowdate(),-60),
-			"relieving_date": relieving_date,
-			"status": "Left"
-		})
+		frappe.db.set_value(
+			"Employee",
+			employee,
+			{
+				"date_of_joining": add_days(nowdate(), -60),
+				"relieving_date": relieving_date,
+				"status": "Left",
+			},
+		)
 
 		if date_of_joining.day > 1:
 			self.assertRaises(frappe.ValidationError, ss.save)
@@ -326,30 +560,43 @@
 		self.assertEqual(ss.total_working_days, no_of_days[0])
 		self.assertEqual(ss.payment_days, getdate(relieving_date).day)
 
-		frappe.db.set_value("Employee", frappe.get_value("Employee",
-			{"employee_name":"test_payment_days@salary.com"}, "name"), "relieving_date", None)
-		frappe.db.set_value("Employee", frappe.get_value("Employee",
-			{"employee_name":"test_payment_days@salary.com"}, "name"), "status", "Active")
+		frappe.db.set_value(
+			"Employee",
+			frappe.get_value("Employee", {"employee_name": "test_payment_days@salary.com"}, "name"),
+			"relieving_date",
+			None,
+		)
+		frappe.db.set_value(
+			"Employee",
+			frappe.get_value("Employee", {"employee_name": "test_payment_days@salary.com"}, "name"),
+			"status",
+			"Active",
+		)
 
 	def test_employee_salary_slip_read_permission(self):
 		make_employee("test_employee_salary_slip_read_permission@salary.com")
 
-		salary_slip_test_employee = make_employee_salary_slip("test_employee_salary_slip_read_permission@salary.com", "Monthly", "Test Employee Salary Slip Read Permission")
+		salary_slip_test_employee = make_employee_salary_slip(
+			"test_employee_salary_slip_read_permission@salary.com",
+			"Monthly",
+			"Test Employee Salary Slip Read Permission",
+		)
 		frappe.set_user("test_employee_salary_slip_read_permission@salary.com")
 		self.assertTrue(salary_slip_test_employee.has_permission("read"))
 
+	@change_settings("Payroll Settings", {"email_salary_slip_to_employee": 1})
 	def test_email_salary_slip(self):
-		frappe.db.sql("delete from `tabEmail Queue`")
+		frappe.db.delete("Email Queue")
 
-		frappe.db.set_value("Payroll Settings", None, "email_salary_slip_to_employee", 1)
+		user_id = "test_email_salary_slip@salary.com"
 
-		make_employee("test_email_salary_slip@salary.com")
-		ss = make_employee_salary_slip("test_email_salary_slip@salary.com", "Monthly", "Test Salary Slip Email")
+		make_employee(user_id, company="_Test Company")
+		ss = make_employee_salary_slip(user_id, "Monthly", "Test Salary Slip Email")
 		ss.company = "_Test Company"
 		ss.save()
 		ss.submit()
 
-		email_queue = frappe.db.sql("""select name from `tabEmail Queue`""")
+		email_queue = frappe.db.a_row_exists("Email Queue")
 		self.assertTrue(email_queue)
 
 	def test_loan_repayment_salary_slip(self):
@@ -368,34 +615,58 @@
 
 		create_loan_accounts()
 
-		create_loan_type("Car Loan", 500000, 8.4,
+		create_loan_type(
+			"Car Loan",
+			500000,
+			8.4,
 			is_term_loan=1,
-			mode_of_payment='Cash',
-			disbursement_account='Disbursement Account - _TC',
-			payment_account='Payment Account - _TC',
-			loan_account='Loan Account - _TC',
-			interest_income_account='Interest Income Account - _TC',
-			penalty_income_account='Penalty Income Account - _TC')
+			mode_of_payment="Cash",
+			disbursement_account="Disbursement Account - _TC",
+			payment_account="Payment Account - _TC",
+			loan_account="Loan Account - _TC",
+			interest_income_account="Interest Income Account - _TC",
+			penalty_income_account="Penalty Income Account - _TC",
+		)
 
 		payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company")
 
-		make_salary_structure("Test Loan Repayment Salary Structure", "Monthly", employee=applicant, currency='INR',
-			payroll_period=payroll_period)
+		make_salary_structure(
+			"Test Loan Repayment Salary Structure",
+			"Monthly",
+			employee=applicant,
+			currency="INR",
+			payroll_period=payroll_period,
+		)
 
-		frappe.db.sql("delete from tabLoan where applicant = 'test_loan_repayment_salary_slip@salary.com'")
-		loan = create_loan(applicant, "Car Loan", 11000, "Repay Over Number of Periods", 20, posting_date=add_months(nowdate(), -1))
+		frappe.db.sql(
+			"delete from tabLoan where applicant = 'test_loan_repayment_salary_slip@salary.com'"
+		)
+		loan = create_loan(
+			applicant,
+			"Car Loan",
+			11000,
+			"Repay Over Number of Periods",
+			20,
+			posting_date=add_months(nowdate(), -1),
+		)
 		loan.repay_from_salary = 1
 		loan.submit()
 
-		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1))
+		make_loan_disbursement_entry(
+			loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1)
+		)
 
 		process_loan_interest_accrual_for_term_loans(posting_date=nowdate())
 
-		ss = make_employee_salary_slip("test_loan_repayment_salary_slip@salary.com", "Monthly", "Test Loan Repayment Salary Structure")
+		ss = make_employee_salary_slip(
+			"test_loan_repayment_salary_slip@salary.com", "Monthly", "Test Loan Repayment Salary Structure"
+		)
 		ss.submit()
 
 		self.assertEqual(ss.total_loan_repayment, 592)
-		self.assertEqual(ss.net_pay, (flt(ss.gross_pay) - (flt(ss.total_deduction) + flt(ss.total_loan_repayment))))
+		self.assertEqual(
+			ss.net_pay, (flt(ss.gross_pay) - (flt(ss.total_deduction) + flt(ss.total_loan_repayment)))
+		)
 
 	def test_payroll_frequency(self):
 		fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company())[0]
@@ -404,18 +675,22 @@
 
 		for payroll_frequency in ["Monthly", "Bimonthly", "Fortnightly", "Weekly", "Daily"]:
 			make_employee(payroll_frequency + "_test_employee@salary.com")
-			ss = make_employee_salary_slip(payroll_frequency + "_test_employee@salary.com", payroll_frequency, payroll_frequency + "_Test Payroll Frequency")
+			ss = make_employee_salary_slip(
+				payroll_frequency + "_test_employee@salary.com",
+				payroll_frequency,
+				payroll_frequency + "_Test Payroll Frequency",
+			)
 			if payroll_frequency == "Monthly":
-				self.assertEqual(ss.end_date, m['month_end_date'])
+				self.assertEqual(ss.end_date, m["month_end_date"])
 			elif payroll_frequency == "Bimonthly":
 				if getdate(ss.start_date).day <= 15:
-					self.assertEqual(ss.end_date, m['month_mid_end_date'])
+					self.assertEqual(ss.end_date, m["month_mid_end_date"])
 				else:
-					self.assertEqual(ss.end_date, m['month_end_date'])
+					self.assertEqual(ss.end_date, m["month_end_date"])
 			elif payroll_frequency == "Fortnightly":
-				self.assertEqual(ss.end_date, add_days(nowdate(),13))
+				self.assertEqual(ss.end_date, add_days(nowdate(), 13))
 			elif payroll_frequency == "Weekly":
-				self.assertEqual(ss.end_date, add_days(nowdate(),6))
+				self.assertEqual(ss.end_date, add_days(nowdate(), 6))
 			elif payroll_frequency == "Daily":
 				self.assertEqual(ss.end_date, nowdate())
 
@@ -423,14 +698,22 @@
 		from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
 
 		applicant = make_employee("test_multi_currency_salary_slip@salary.com", company="_Test Company")
-		frappe.db.sql("""delete from `tabSalary Structure` where name='Test Multi Currency Salary Slip'""")
-		salary_structure = make_salary_structure("Test Multi Currency Salary Slip", "Monthly", employee=applicant, company="_Test Company", currency='USD')
-		salary_slip = make_salary_slip(salary_structure.name, employee = applicant)
+		frappe.db.sql(
+			"""delete from `tabSalary Structure` where name='Test Multi Currency Salary Slip'"""
+		)
+		salary_structure = make_salary_structure(
+			"Test Multi Currency Salary Slip",
+			"Monthly",
+			employee=applicant,
+			company="_Test Company",
+			currency="USD",
+		)
+		salary_slip = make_salary_slip(salary_structure.name, employee=applicant)
 		salary_slip.exchange_rate = 70
 		salary_slip.calculate_net_pay()
 
 		self.assertEqual(salary_slip.gross_pay, 78000)
-		self.assertEqual(salary_slip.base_gross_pay, 78000*70)
+		self.assertEqual(salary_slip.base_gross_pay, 78000 * 70)
 
 	def test_year_to_date_computation(self):
 		from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
@@ -439,20 +722,36 @@
 
 		payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company")
 
-		create_tax_slab(payroll_period, allow_tax_exemption=True, currency="INR", effective_date=getdate("2019-04-01"),
-			company="_Test Company")
+		create_tax_slab(
+			payroll_period,
+			allow_tax_exemption=True,
+			currency="INR",
+			effective_date=getdate("2019-04-01"),
+			company="_Test Company",
+		)
 
-		salary_structure = make_salary_structure("Monthly Salary Structure Test for Salary Slip YTD",
-			"Monthly", employee=applicant, company="_Test Company", currency="INR", payroll_period=payroll_period)
+		salary_structure = make_salary_structure(
+			"Monthly Salary Structure Test for Salary Slip YTD",
+			"Monthly",
+			employee=applicant,
+			company="_Test Company",
+			currency="INR",
+			payroll_period=payroll_period,
+		)
 
 		# clear salary slip for this employee
 		frappe.db.sql("DELETE FROM `tabSalary Slip` where employee_name = 'test_ytd@salary.com'")
 
-		create_salary_slips_for_payroll_period(applicant, salary_structure.name,
-			payroll_period, deduct_random=False, num=6)
+		create_salary_slips_for_payroll_period(
+			applicant, salary_structure.name, payroll_period, deduct_random=False, num=6
+		)
 
-		salary_slips = frappe.get_all('Salary Slip', fields=['year_to_date', 'net_pay'], filters={'employee_name':
-			'test_ytd@salary.com'}, order_by = 'posting_date')
+		salary_slips = frappe.get_all(
+			"Salary Slip",
+			fields=["year_to_date", "net_pay"],
+			filters={"employee_name": "test_ytd@salary.com"},
+			order_by="posting_date",
+		)
 
 		year_to_date = 0
 		for slip in salary_slips:
@@ -467,20 +766,36 @@
 
 		payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company")
 
-		create_tax_slab(payroll_period, allow_tax_exemption=True, currency="INR", effective_date=getdate("2019-04-01"),
-			company="_Test Company")
+		create_tax_slab(
+			payroll_period,
+			allow_tax_exemption=True,
+			currency="INR",
+			effective_date=getdate("2019-04-01"),
+			company="_Test Company",
+		)
 
-		salary_structure = make_salary_structure("Monthly Salary Structure Test for Salary Slip YTD",
-			"Monthly", employee=applicant, company="_Test Company", currency="INR", payroll_period=payroll_period)
+		salary_structure = make_salary_structure(
+			"Monthly Salary Structure Test for Salary Slip YTD",
+			"Monthly",
+			employee=applicant,
+			company="_Test Company",
+			currency="INR",
+			payroll_period=payroll_period,
+		)
 
 		# clear salary slip for this employee
 		frappe.db.sql("DELETE FROM `tabSalary Slip` where employee_name = '%s'" % employee_name)
 
-		create_salary_slips_for_payroll_period(applicant, salary_structure.name,
-			payroll_period, deduct_random=False, num=3)
+		create_salary_slips_for_payroll_period(
+			applicant, salary_structure.name, payroll_period, deduct_random=False, num=3
+		)
 
-		salary_slips = frappe.get_all("Salary Slip", fields=["name"], filters={"employee_name":
-			employee_name}, order_by="posting_date")
+		salary_slips = frappe.get_all(
+			"Salary Slip",
+			fields=["name"],
+			filters={"employee_name": employee_name},
+			order_by="posting_date",
+		)
 
 		year_to_date = dict()
 		for slip in salary_slips:
@@ -511,21 +826,28 @@
 			"Employee Tax Exemption Declaration",
 			"Employee Tax Exemption Proof Submission",
 			"Employee Benefit Claim",
-			"Salary Structure Assignment"
+			"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)
+		salary_structure = make_salary_structure(
+			"Stucture to test tax",
+			"Monthly",
+			other_details={"max_benefits": 100000},
+			test_tax=True,
+			include_flexi_benefits=True,
+			employee=employee,
+			payroll_period=payroll_period,
+		)
 
 		# create salary slip for whole period deducting tax only on last period
 		# to find the total tax amount paid
-		create_salary_slips_for_payroll_period(employee, salary_structure.name,
-			payroll_period, deduct_random=False)
+		create_salary_slips_for_payroll_period(
+			employee, salary_structure.name, payroll_period, deduct_random=False
+		)
 		tax_paid = get_tax_paid_in_period(employee)
 
 		annual_tax = 113589.0
@@ -540,8 +862,9 @@
 		create_exemption_declaration(employee, payroll_period.name)
 
 		# create for payroll deducting in random months
-		data["deducted_dates"] = create_salary_slips_for_payroll_period(employee,
-			salary_structure.name, payroll_period)
+		data["deducted_dates"] = create_salary_slips_for_payroll_period(
+			employee, salary_structure.name, payroll_period
+		)
 		tax_paid = get_tax_paid_in_period(employee)
 
 		# No proof, benefit claim sumitted, total tax paid, should not change
@@ -556,12 +879,14 @@
 
 		# Submit benefit claim for total 50000
 		data["benefit-1"] = create_benefit_claim(employee, payroll_period, 15000, "Medical Allowance")
-		data["benefit-2"] = create_benefit_claim(employee, payroll_period, 35000, "Leave Travel Allowance")
-
+		data["benefit-2"] = create_benefit_claim(
+			employee, payroll_period, 35000, "Leave Travel Allowance"
+		)
 
 		frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee))
-		data["deducted_dates"] = create_salary_slips_for_payroll_period(employee,
-			salary_structure.name, payroll_period)
+		data["deducted_dates"] = create_salary_slips_for_payroll_period(
+			employee, salary_structure.name, payroll_period
+		)
 		tax_paid = get_tax_paid_in_period(employee)
 
 		# total taxable income 416000, 166000 @ 5% ie. 8300
@@ -574,8 +899,9 @@
 		# create additional salary of 150000
 		frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee))
 		data["additional-1"] = create_additional_salary(employee, payroll_period, 150000)
-		data["deducted_dates"] = create_salary_slips_for_payroll_period(employee,
-			salary_structure.name, payroll_period)
+		data["deducted_dates"] = create_salary_slips_for_payroll_period(
+			employee, salary_structure.name, payroll_period
+		)
 
 		# total taxable income 566000, 250000 @ 5%, 66000 @ 20%, 12500 + 13200
 		tax_paid = get_tax_paid_in_period(employee)
@@ -604,20 +930,26 @@
 			"Employee Tax Exemption Declaration",
 			"Employee Tax Exemption Proof Submission",
 			"Employee Benefit Claim",
-			"Salary Structure Assignment"
+			"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)
+		salary_structure = make_salary_structure(
+			"Stucture to test tax",
+			"Monthly",
+			other_details={"max_benefits": 100000},
+			test_tax=True,
+			include_flexi_benefits=True,
+			employee=employee,
+			payroll_period=payroll_period,
+		)
 
-
-		create_salary_slips_for_payroll_period(employee, salary_structure.name,
-			payroll_period, deduct_random=False, num=3)
+		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)
 
@@ -626,7 +958,7 @@
 
 		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)
@@ -634,8 +966,9 @@
 
 		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)
+		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)
 
@@ -651,44 +984,56 @@
 		activity_type.wage_rate = 25
 		activity_type.save()
 
-	def get_no_of_days(self):
-		no_of_days_in_month = calendar.monthrange(getdate(nowdate()).year,
-			getdate(nowdate()).month)
-		no_of_holidays_in_month = len([1 for i in calendar.monthcalendar(getdate(nowdate()).year,
-			getdate(nowdate()).month) if i[6] != 0])
 
-		return [no_of_days_in_month[1], no_of_holidays_in_month]
+def get_no_of_days():
+	no_of_days_in_month = calendar.monthrange(getdate(nowdate()).year, getdate(nowdate()).month)
+	no_of_holidays_in_month = len(
+		[
+			1
+			for i in calendar.monthcalendar(getdate(nowdate()).year, getdate(nowdate()).month)
+			if i[6] != 0
+		]
+	)
 
-def make_employee_salary_slip(user, payroll_frequency, salary_structure=None):
+	return [no_of_days_in_month[1], no_of_holidays_in_month]
+
+
+def make_employee_salary_slip(user, payroll_frequency, salary_structure=None, posting_date=None):
 	from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
 
 	if not salary_structure:
 		salary_structure = payroll_frequency + " Salary Structure Test for Salary Slip"
 
-	employee = frappe.db.get_value("Employee",
-					{
-						"user_id": user
-					},
-					["name", "company", "employee_name"],
-					as_dict=True)
+	employee = frappe.db.get_value(
+		"Employee", {"user_id": user}, ["name", "company", "employee_name"], as_dict=True
+	)
 
-	salary_structure_doc = make_salary_structure(salary_structure, payroll_frequency, employee=employee.name, company=employee.company)
-	salary_slip_name = frappe.db.get_value("Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": user})})
+	salary_structure_doc = make_salary_structure(
+		salary_structure,
+		payroll_frequency,
+		employee=employee.name,
+		company=employee.company,
+		from_date=posting_date,
+	)
+	salary_slip_name = frappe.db.get_value(
+		"Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": user})}
+	)
 
 	if not salary_slip_name:
-		salary_slip = make_salary_slip(salary_structure_doc.name, employee = employee.name)
+		salary_slip = make_salary_slip(salary_structure_doc.name, employee=employee.name)
 		salary_slip.employee_name = employee.employee_name
 		salary_slip.payroll_frequency = payroll_frequency
-		salary_slip.posting_date = nowdate()
+		salary_slip.posting_date = posting_date or nowdate()
 		salary_slip.insert()
 	else:
-		salary_slip = frappe.get_doc('Salary Slip', salary_slip_name)
+		salary_slip = frappe.get_doc("Salary Slip", salary_slip_name)
 
 	return salary_slip
 
+
 def make_salary_component(salary_components, test_tax, company_list=None):
 	for salary_component in salary_components:
-		if frappe.db.exists('Salary Component', salary_component["salary_component"]):
+		if frappe.db.exists("Salary Component", salary_component["salary_component"]):
 			continue
 
 		if test_tax:
@@ -706,9 +1051,10 @@
 		doc.update(salary_component)
 		doc.insert()
 
-		get_salary_component_account(doc, company_list)
+		set_salary_component_account(doc, company_list)
 
-def get_salary_component_account(sal_comp, company_list=None):
+
+def set_salary_component_account(sal_comp, company_list=None):
 	company = erpnext.get_default_company()
 
 	if company_list and company not in company_list:
@@ -719,7 +1065,7 @@
 
 	if not sal_comp.get("accounts"):
 		for d in company_list:
-			company_abbr = frappe.get_cached_value('Company', d, 'abbr')
+			company_abbr = frappe.get_cached_value("Company", d, "abbr")
 
 			if sal_comp.type == "Earning":
 				account_name = "Salary"
@@ -728,177 +1074,212 @@
 				account_name = "Salary Deductions"
 				parent_account = "Current Liabilities - " + company_abbr
 
-			sal_comp.append("accounts", {
-				"company": d,
-				"account": create_account(account_name, d, parent_account)
-			})
+			sal_comp.append(
+				"accounts", {"company": d, "account": create_account(account_name, d, parent_account)}
+			)
 			sal_comp.save()
 
+
 def create_account(account_name, company, parent_account, account_type=None):
-	company_abbr = frappe.get_cached_value('Company',  company,  'abbr')
+	company_abbr = frappe.get_cached_value("Company", company, "abbr")
 	account = frappe.db.get_value("Account", account_name + " - " + company_abbr)
 	if not account:
-		frappe.get_doc({
-			"doctype": "Account",
-			"account_name": account_name,
-			"parent_account": parent_account,
-			"company": company
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Account",
+				"account_name": account_name,
+				"parent_account": parent_account,
+				"company": company,
+			}
+		).insert()
 	return account
 
-def make_earning_salary_component(setup=False, test_tax=False, company_list=None):
+
+def make_earning_salary_component(
+	setup=False, test_tax=False, company_list=None, include_flexi_benefits=False
+):
 	data = [
 		{
-			"salary_component": 'Basic Salary',
-			"abbr":'BS',
-			"condition": 'base > 10000',
-			"formula": 'base',
+			"salary_component": "Basic Salary",
+			"abbr": "BS",
+			"condition": "base > 10000",
+			"formula": "base",
 			"type": "Earning",
-			"amount_based_on_formula": 1
+			"amount_based_on_formula": 1,
 		},
+		{"salary_component": "HRA", "abbr": "H", "amount": 3000, "type": "Earning"},
 		{
-			"salary_component": 'HRA',
-			"abbr":'H',
-			"amount": 3000,
-			"type": "Earning"
-		},
-		{
-			"salary_component": 'Special Allowance',
-			"abbr":'SA',
-			"condition": 'H < 10000',
-			"formula": 'BS*.5',
+			"salary_component": "Special Allowance",
+			"abbr": "SA",
+			"condition": "H < 10000",
+			"formula": "BS*.5",
 			"type": "Earning",
-			"amount_based_on_formula": 1
+			"amount_based_on_formula": 1,
 		},
-		{
-			"salary_component": "Leave Encashment",
-			"abbr": 'LE',
-			"type": "Earning"
-		}
+		{"salary_component": "Leave Encashment", "abbr": "LE", "type": "Earning"},
 	]
+	if include_flexi_benefits:
+		data.extend(
+			[
+				{
+					"salary_component": "Leave Travel Allowance",
+					"abbr": "B",
+					"is_flexible_benefit": 1,
+					"type": "Earning",
+					"pay_against_benefit_claim": 1,
+					"max_benefit_amount": 100000,
+					"depends_on_payment_days": 0,
+				},
+				{
+					"salary_component": "Medical Allowance",
+					"abbr": "B",
+					"is_flexible_benefit": 1,
+					"pay_against_benefit_claim": 0,
+					"type": "Earning",
+					"max_benefit_amount": 15000,
+					"depends_on_payment_days": 1,
+				},
+			]
+		)
 	if test_tax:
-		data.extend([
-			{
-				"salary_component": "Leave Travel Allowance",
-				"abbr": 'B',
-				"is_flexible_benefit": 1,
-				"type": "Earning",
-				"pay_against_benefit_claim": 1,
-				"max_benefit_amount": 100000,
-				"depends_on_payment_days": 0
-			},
-			{
-				"salary_component": "Medical Allowance",
-				"abbr": 'B',
-				"is_flexible_benefit": 1,
-				"pay_against_benefit_claim": 0,
-				"type": "Earning",
-				"max_benefit_amount": 15000
-			},
-			{
-				"salary_component": "Performance Bonus",
-				"abbr": 'B',
-				"type": "Earning"
-			}
-		])
+		data.extend(
+			[
+				{"salary_component": "Performance Bonus", "abbr": "B", "type": "Earning"},
+			]
+		)
+
 	if setup or test_tax:
 		make_salary_component(data, test_tax, company_list)
-	data.append({
-		"salary_component": 'Basic Salary',
-		"abbr":'BS',
-		"condition": 'base < 10000',
-		"formula": 'base*.2',
-		"type": "Earning",
-		"amount_based_on_formula": 1
-	})
+
+	data.append(
+		{
+			"salary_component": "Basic Salary",
+			"abbr": "BS",
+			"condition": "base < 10000",
+			"formula": "base*.2",
+			"type": "Earning",
+			"amount_based_on_formula": 1,
+		}
+	)
 	return data
 
+
 def make_deduction_salary_component(setup=False, test_tax=False, company_list=None):
-	data =  [
+	data = [
 		{
-			"salary_component": 'Professional Tax',
-			"abbr":'PT',
+			"salary_component": "Professional Tax",
+			"abbr": "PT",
 			"type": "Deduction",
 			"amount": 200,
-			"exempted_from_income_tax": 1
-
+			"exempted_from_income_tax": 1,
 		}
 	]
 	if not test_tax:
-		data.append({
-			"salary_component": 'TDS',
-			"abbr":'T',
-			"condition": 'employment_type=="Intern"',
-			"type": "Deduction",
-			"round_to_the_nearest_integer": 1
-		})
+		data.append(
+			{
+				"salary_component": "TDS",
+				"abbr": "T",
+				"condition": 'employment_type=="Intern"',
+				"type": "Deduction",
+				"round_to_the_nearest_integer": 1,
+			}
+		)
 	else:
-		data.append({
-			"salary_component": 'TDS',
-			"abbr":'T',
-			"type": "Deduction",
-			"depends_on_payment_days": 0,
-			"variable_based_on_taxable_salary": 1,
-			"round_to_the_nearest_integer": 1
-		})
+		data.append(
+			{
+				"salary_component": "TDS",
+				"abbr": "T",
+				"type": "Deduction",
+				"depends_on_payment_days": 0,
+				"variable_based_on_taxable_salary": 1,
+				"round_to_the_nearest_integer": 1,
+			}
+		)
 	if setup or test_tax:
 		make_salary_component(data, test_tax, company_list)
 
 	return data
 
+
 def get_tax_paid_in_period(employee):
-	tax_paid_amount = frappe.db.sql("""select sum(sd.amount) from `tabSalary Detail`
+	tax_paid_amount = frappe.db.sql(
+		"""select sum(sd.amount) from `tabSalary Detail`
 		sd join `tabSalary Slip` ss where ss.name=sd.parent and ss.employee=%s
-		and ss.docstatus=1 and sd.salary_component='TDS'""", (employee))
+		and ss.docstatus=1 and sd.salary_component='TDS'""",
+		(employee),
+	)
 	return tax_paid_amount[0][0]
 
+
 def create_exemption_declaration(employee, payroll_period):
 	create_exemption_category()
-	declaration = frappe.get_doc({
-		"doctype": "Employee Tax Exemption Declaration",
-		"employee": employee,
-		"payroll_period": payroll_period,
-		"company": erpnext.get_default_company(),
-		"currency": erpnext.get_default_currency()
-	})
-	declaration.append("declarations", {
-		"exemption_sub_category": "_Test Sub Category",
-		"exemption_category": "_Test Category",
-		"amount": 100000
-	})
+	declaration = frappe.get_doc(
+		{
+			"doctype": "Employee Tax Exemption Declaration",
+			"employee": employee,
+			"payroll_period": payroll_period,
+			"company": erpnext.get_default_company(),
+			"currency": erpnext.get_default_currency(),
+		}
+	)
+	declaration.append(
+		"declarations",
+		{
+			"exemption_sub_category": "_Test Sub Category",
+			"exemption_category": "_Test Category",
+			"amount": 100000,
+		},
+	)
 	declaration.submit()
 
+
 def create_proof_submission(employee, payroll_period, amount):
 	submission_date = add_months(payroll_period.start_date, random.randint(0, 11))
-	proof_submission = frappe.get_doc({
-		"doctype": "Employee Tax Exemption Proof Submission",
-		"employee": employee,
-		"payroll_period": payroll_period.name,
-		"submission_date": submission_date,
-		"currency": erpnext.get_default_currency()
-	})
-	proof_submission.append("tax_exemption_proofs", {
-		"exemption_sub_category": "_Test Sub Category",
-		"exemption_category": "_Test Category",
-		"type_of_proof": "Test", "amount": amount
-	})
+	proof_submission = frappe.get_doc(
+		{
+			"doctype": "Employee Tax Exemption Proof Submission",
+			"employee": employee,
+			"payroll_period": payroll_period.name,
+			"submission_date": submission_date,
+			"currency": erpnext.get_default_currency(),
+		}
+	)
+	proof_submission.append(
+		"tax_exemption_proofs",
+		{
+			"exemption_sub_category": "_Test Sub Category",
+			"exemption_category": "_Test Category",
+			"type_of_proof": "Test",
+			"amount": amount,
+		},
+	)
 	proof_submission.submit()
 	return submission_date
 
+
 def create_benefit_claim(employee, payroll_period, amount, component):
 	claim_date = add_months(payroll_period.start_date, random.randint(0, 11))
-	frappe.get_doc({
-		"doctype": "Employee Benefit Claim",
-		"employee": employee,
-		"claimed_amount": amount,
-		"claim_date": claim_date,
-		"earning_component": component,
-		"currency": erpnext.get_default_currency()
-	}).submit()
+	frappe.get_doc(
+		{
+			"doctype": "Employee Benefit Claim",
+			"employee": employee,
+			"claimed_amount": amount,
+			"claim_date": claim_date,
+			"earning_component": component,
+			"currency": erpnext.get_default_currency(),
+		}
+	).submit()
 	return claim_date
 
-def create_tax_slab(payroll_period, effective_date = None, allow_tax_exemption = False, dont_submit = False, currency=None,
-	company=None):
+
+def create_tax_slab(
+	payroll_period,
+	effective_date=None,
+	allow_tax_exemption=False,
+	dont_submit=False,
+	currency=None,
+	company=None,
+):
 	if not currency:
 		currency = erpnext.get_default_currency()
 
@@ -910,17 +1291,10 @@
 			"from_amount": 250000,
 			"to_amount": 500000,
 			"percent_deduction": 5,
-			"condition": "annual_taxable_earning > 500000"
+			"condition": "annual_taxable_earning > 500000",
 		},
-		{
-			"from_amount": 500001,
-			"to_amount": 1000000,
-			"percent_deduction": 20
-		},
-		{
-			"from_amount": 1000001,
-			"percent_deduction": 30
-		}
+		{"from_amount": 500001, "to_amount": 1000000, "percent_deduction": 20},
+		{"from_amount": 1000001, "percent_deduction": 30},
 	]
 
 	income_tax_slab_name = frappe.db.get_value("Income Tax Slab", {"currency": currency})
@@ -928,7 +1302,7 @@
 		income_tax_slab = frappe.new_doc("Income Tax Slab")
 		income_tax_slab.name = "Tax Slab: " + payroll_period.name + " " + cstr(currency)
 		income_tax_slab.effective_from = effective_date or add_days(payroll_period.start_date, -2)
-		income_tax_slab.company = company or ''
+		income_tax_slab.company = company or ""
 		income_tax_slab.currency = currency
 
 		if allow_tax_exemption:
@@ -938,10 +1312,7 @@
 		for item in slabs:
 			income_tax_slab.append("slabs", item)
 
-		income_tax_slab.append("other_taxes_and_charges", {
-			"description": "cess",
-			"percent": 4
-		})
+		income_tax_slab.append("other_taxes_and_charges", {"description": "cess", "percent": 4})
 
 		income_tax_slab.save()
 		if not dont_submit:
@@ -951,12 +1322,21 @@
 	else:
 		return income_tax_slab_name
 
-def create_salary_slips_for_payroll_period(employee, salary_structure, payroll_period, deduct_random=True, num=12):
+
+def create_salary_slips_for_payroll_period(
+	employee, salary_structure, payroll_period, deduct_random=True, num=12
+):
 	deducted_dates = []
 	i = 0
 	while i < num:
-		slip = frappe.get_doc({"doctype": "Salary Slip", "employee": employee,
-				"salary_structure": salary_structure, "frequency": "Monthly"})
+		slip = frappe.get_doc(
+			{
+				"doctype": "Salary Slip",
+				"employee": employee,
+				"salary_structure": salary_structure,
+				"frequency": "Monthly",
+			}
+		)
 		if i == 0:
 			posting_date = add_days(payroll_period.start_date, 25)
 		else:
@@ -975,67 +1355,100 @@
 		i += 1
 	return deducted_dates
 
+
 def create_additional_salary(employee, payroll_period, amount):
 	salary_date = add_months(payroll_period.start_date, random.randint(0, 11))
-	frappe.get_doc({
-		"doctype": "Additional Salary",
-		"employee": employee,
-		"company": erpnext.get_default_company(),
-		"salary_component": "Performance Bonus",
-		"payroll_date": salary_date,
-		"amount": amount,
-		"type": "Earning",
-		"currency": erpnext.get_default_currency()
-	}).submit()
+	frappe.get_doc(
+		{
+			"doctype": "Additional Salary",
+			"employee": employee,
+			"company": erpnext.get_default_company(),
+			"salary_component": "Performance Bonus",
+			"payroll_date": salary_date,
+			"amount": amount,
+			"type": "Earning",
+			"currency": erpnext.get_default_currency(),
+		}
+	).submit()
 	return salary_date
 
-def make_leave_application(employee, from_date, to_date, leave_type, company=None):
-	leave_application = frappe.get_doc(dict(
-		doctype = 'Leave Application',
-		employee = employee,
-		leave_type = leave_type,
-		from_date = from_date,
-		to_date = to_date,
-		company = company or erpnext.get_default_company() or "_Test Company",
-		docstatus = 1,
-		status = "Approved",
-		leave_approver = 'test@example.com'
-	))
-	leave_application.submit()
+
+def make_leave_application(
+	employee,
+	from_date,
+	to_date,
+	leave_type,
+	company=None,
+	half_day=False,
+	half_day_date=None,
+	submit=True,
+):
+	leave_application = frappe.get_doc(
+		dict(
+			doctype="Leave Application",
+			employee=employee,
+			leave_type=leave_type,
+			from_date=from_date,
+			to_date=to_date,
+			half_day=half_day,
+			half_day_date=half_day_date,
+			company=company or erpnext.get_default_company() or "_Test Company",
+			status="Approved",
+			leave_approver="test@example.com",
+		)
+	).insert()
+
+	if submit:
+		leave_application.submit()
 
 	return leave_application
 
+
 def setup_test():
 	make_earning_salary_component(setup=True, company_list=["_Test Company"])
 	make_deduction_salary_component(setup=True, company_list=["_Test Company"])
 
-	for dt in ["Leave Application", "Leave Allocation", "Salary Slip", "Attendance", "Additional Salary"]:
+	for dt in [
+		"Leave Application",
+		"Leave Allocation",
+		"Salary Slip",
+		"Attendance",
+		"Additional Salary",
+	]:
 		frappe.db.sql("delete from `tab%s`" % dt)
 
 	make_holiday_list()
 
-	frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List")
+	frappe.db.set_value(
+		"Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List"
+	)
 	frappe.db.set_value("Payroll Settings", None, "email_salary_slip_to_employee", 0)
-	frappe.db.set_value('HR Settings', None, 'leave_status_notification_template', None)
-	frappe.db.set_value('HR Settings', None, 'leave_approval_notification_template', None)
+	frappe.db.set_value("HR Settings", None, "leave_status_notification_template", None)
+	frappe.db.set_value("HR Settings", None, "leave_approval_notification_template", None)
 
-def make_holiday_list(holiday_list_name=None):
+
+def make_holiday_list(list_name=None, from_date=None, to_date=None):
 	fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company())
-	holiday_list = frappe.db.exists("Holiday List", holiday_list_name or "Salary Slip Test Holiday List")
-	if not holiday_list:
-		holiday_list = frappe.get_doc({
+	name = list_name or "Salary Slip Test Holiday List"
+
+	frappe.delete_doc_if_exists("Holiday List", name, force=True)
+
+	holiday_list = frappe.get_doc(
+		{
 			"doctype": "Holiday List",
-			"holiday_list_name": holiday_list_name or "Salary Slip Test Holiday List",
-			"from_date": fiscal_year[1],
-			"to_date": fiscal_year[2],
-			"weekly_off": "Sunday"
-		}).insert()
-		holiday_list.get_weekly_off_dates()
-		holiday_list.save()
-		holiday_list = holiday_list.name
+			"holiday_list_name": name,
+			"from_date": from_date or fiscal_year[1],
+			"to_date": to_date or fiscal_year[2],
+			"weekly_off": "Sunday",
+		}
+	).insert()
+	holiday_list.get_weekly_off_dates()
+	holiday_list.save()
+	holiday_list = holiday_list.name
 
 	return holiday_list
 
+
 def make_salary_structure_for_payment_days_based_component_dependency():
 	earnings = [
 		{
@@ -1043,7 +1456,7 @@
 			"abbr": "P_BS",
 			"type": "Earning",
 			"formula": "base",
-			"amount_based_on_formula": 1
+			"amount_based_on_formula": 1,
 		},
 		{
 			"salary_component": "HRA - Payment Days",
@@ -1051,8 +1464,8 @@
 			"type": "Earning",
 			"depends_on_payment_days": 1,
 			"amount_based_on_formula": 1,
-			"formula": "base * 0.20"
-		}
+			"formula": "base * 0.20",
+		},
 	]
 
 	make_salary_component(earnings, False, company_list=["_Test Company"])
@@ -1063,7 +1476,7 @@
 			"abbr": "P_PT",
 			"type": "Deduction",
 			"depends_on_payment_days": 1,
-			"amount": 200.00
+			"amount": 200.00,
 		},
 		{
 			"salary_component": "P - Employee Provident Fund",
@@ -1072,8 +1485,8 @@
 			"exempted_from_income_tax": 1,
 			"amount_based_on_formula": 1,
 			"depends_on_payment_days": 0,
-			"formula": "(gross_pay - P_HRA) * 0.12"
-		}
+			"formula": "(gross_pay - P_HRA) * 0.12",
+		},
 	]
 
 	make_salary_component(deductions, False, company_list=["_Test Company"])
@@ -1088,7 +1501,7 @@
 		"company": "_Test Company",
 		"payroll_frequency": "Monthly",
 		"payment_account": get_random("Account", filters={"account_currency": "INR"}),
-		"currency": "INR"
+		"currency": "INR",
 	}
 
 	salary_structure_doc = frappe.get_doc(details)
@@ -1104,12 +1517,10 @@
 
 	return salary_structure_doc
 
+
 def make_salary_slip_for_payment_days_dependency_test(employee, salary_structure):
 	employee = frappe.db.get_value(
-		"Employee",
-		{"user_id": employee},
-		["name", "company", "employee_name"],
-		as_dict=True
+		"Employee", {"user_id": employee}, ["name", "company", "employee_name"], as_dict=True
 	)
 
 	salary_slip_name = frappe.db.get_value("Salary Slip", {"employee": employee.name})
@@ -1125,16 +1536,21 @@
 
 	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()
+
+def create_recurring_additional_salary(
+	employee, salary_component, amount, from_date, to_date, company=None
+):
+	frappe.get_doc(
+		{
+			"doctype": "Additional Salary",
+			"employee": employee,
+			"company": company or erpnext.get_default_company(),
+			"salary_component": salary_component,
+			"is_recurring": 1,
+			"from_date": from_date,
+			"to_date": to_date,
+			"amount": amount,
+			"type": "Earning",
+			"currency": erpnext.get_default_currency(),
+		}
+	).submit()
diff --git a/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.json b/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.json
index 7ac453b..60ed453 100644
--- a/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.json
+++ b/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.json
@@ -26,7 +26,7 @@
    "fieldname": "total_allocated_leaves",
    "fieldtype": "Float",
    "in_list_view": 1,
-   "label": "Total Allocated Leave",
+   "label": "Total Allocated Leave(s)",
    "no_copy": 1,
    "read_only": 1
   },
@@ -34,7 +34,7 @@
    "fieldname": "expired_leaves",
    "fieldtype": "Float",
    "in_list_view": 1,
-   "label": "Expired Leave",
+   "label": "Expired Leave(s)",
    "no_copy": 1,
    "read_only": 1
   },
@@ -42,7 +42,7 @@
    "fieldname": "used_leaves",
    "fieldtype": "Float",
    "in_list_view": 1,
-   "label": "Used Leave",
+   "label": "Used Leave(s)",
    "no_copy": 1,
    "read_only": 1
   },
@@ -50,7 +50,7 @@
    "fieldname": "pending_leaves",
    "fieldtype": "Float",
    "in_list_view": 1,
-   "label": "Pending Leave",
+   "label": "Leave(s) Pending Approval",
    "no_copy": 1,
    "read_only": 1
   },
@@ -58,7 +58,7 @@
    "fieldname": "available_leaves",
    "fieldtype": "Float",
    "in_list_view": 1,
-   "label": "Available Leave",
+   "label": "Available Leave(s)",
    "no_copy": 1,
    "read_only": 1
   }
@@ -66,7 +66,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-02-19 10:47:48.546724",
+ "modified": "2022-02-28 14:01:32.327204",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Salary Slip Leave",
@@ -74,5 +74,6 @@
  "permissions": [],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.js b/erpnext/payroll/doctype/salary_structure/salary_structure.js
index d5c20dc..7853b48 100755
--- a/erpnext/payroll/doctype/salary_structure/salary_structure.js
+++ b/erpnext/payroll/doctype/salary_structure/salary_structure.js
@@ -164,6 +164,15 @@
 			primary_action_label: __('Assign')
 		});
 
+		d.fields_dict.grade.df.onchange = function() {
+			const grade = d.fields_dict.grade.value;
+			if (grade) {
+				frappe.db.get_value('Employee Grade', grade, 'default_base_pay')
+					.then(({ message }) => {
+						d.set_value('base', message.default_base_pay);
+					});
+			}
+		};
 
 		d.show();
 	},
diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.py b/erpnext/payroll/doctype/salary_structure/salary_structure.py
index 4cbf948..edf17db 100644
--- a/erpnext/payroll/doctype/salary_structure/salary_structure.py
+++ b/erpnext/payroll/doctype/salary_structure/salary_structure.py
@@ -20,12 +20,21 @@
 		self.validate_component_based_on_tax_slab()
 
 	def set_missing_values(self):
-		overwritten_fields = ["depends_on_payment_days", "variable_based_on_taxable_salary", "is_tax_applicable", "is_flexible_benefit"]
+		overwritten_fields = [
+			"depends_on_payment_days",
+			"variable_based_on_taxable_salary",
+			"is_tax_applicable",
+			"is_flexible_benefit",
+		]
 		overwritten_fields_if_missing = ["amount_based_on_formula", "formula", "amount"]
 		for table in ["earnings", "deductions"]:
 			for d in self.get(table):
-				component_default_value = frappe.db.get_value("Salary Component", cstr(d.salary_component),
-					overwritten_fields + overwritten_fields_if_missing, as_dict=1)
+				component_default_value = frappe.db.get_value(
+					"Salary Component",
+					cstr(d.salary_component),
+					overwritten_fields + overwritten_fields_if_missing,
+					as_dict=1,
+				)
 				if component_default_value:
 					for fieldname in overwritten_fields:
 						value = component_default_value.get(fieldname)
@@ -39,8 +48,11 @@
 	def validate_component_based_on_tax_slab(self):
 		for row in self.deductions:
 			if row.variable_based_on_taxable_salary and (row.amount or row.formula):
-				frappe.throw(_("Row #{0}: Cannot set amount or formula for Salary Component {1} with Variable Based On Taxable Salary")
-					.format(row.idx, row.salary_component))
+				frappe.throw(
+					_(
+						"Row #{0}: Cannot set amount or formula for Salary Component {1} with Variable Based On Taxable Salary"
+					).format(row.idx, row.salary_component)
+				)
 
 	def validate_amount(self):
 		if flt(self.net_pay) < 0 and self.salary_slip_based_on_timesheet:
@@ -63,16 +75,23 @@
 			for earning_component in self.earnings:
 				if earning_component.is_flexible_benefit == 1:
 					have_a_flexi = True
-					max_of_component = frappe.db.get_value("Salary Component", earning_component.salary_component, "max_benefit_amount")
+					max_of_component = frappe.db.get_value(
+						"Salary Component", earning_component.salary_component, "max_benefit_amount"
+					)
 					flexi_amount += max_of_component
 
 			if have_a_flexi and flt(self.max_benefits) == 0:
 				frappe.throw(_("Max benefits should be greater than zero to dispense benefits"))
 			if have_a_flexi and flexi_amount and flt(self.max_benefits) > flexi_amount:
-				frappe.throw(_("Total flexible benefit component amount {0} should not be less than max benefits {1}")
-					.format(flexi_amount, self.max_benefits))
+				frappe.throw(
+					_(
+						"Total flexible benefit component amount {0} should not be less than max benefits {1}"
+					).format(flexi_amount, self.max_benefits)
+				)
 		if not have_a_flexi and flt(self.max_benefits) > 0:
-			frappe.throw(_("Salary Structure should have flexible benefit component(s) to dispense benefit amount"))
+			frappe.throw(
+				_("Salary Structure should have flexible benefit component(s) to dispense benefit amount")
+			)
 
 	def get_employees(self, **kwargs):
 		conditions, values = [], []
@@ -83,58 +102,117 @@
 
 		condition_str = " and " + " and ".join(conditions) if conditions else ""
 
-		employees = frappe.db.sql_list("select name from tabEmployee where status='Active' {condition}"
-			.format(condition=condition_str), tuple(values))
+		employees = frappe.db.sql_list(
+			"select name from tabEmployee where status='Active' {condition}".format(
+				condition=condition_str
+			),
+			tuple(values),
+		)
 
 		return employees
 
 	@frappe.whitelist()
-	def assign_salary_structure(self, grade=None, department=None, designation=None, employee=None,
-			payroll_payable_account=None, from_date=None, base=None, variable=None, income_tax_slab=None):
-		employees = self.get_employees(company= self.company, grade= grade,department= department,designation= designation,name=employee)
+	def assign_salary_structure(
+		self,
+		grade=None,
+		department=None,
+		designation=None,
+		employee=None,
+		payroll_payable_account=None,
+		from_date=None,
+		base=None,
+		variable=None,
+		income_tax_slab=None,
+	):
+		employees = self.get_employees(
+			company=self.company, grade=grade, department=department, designation=designation, name=employee
+		)
 
 		if employees:
 			if len(employees) > 20:
-				frappe.enqueue(assign_salary_structure_for_employees, timeout=600,
-					employees=employees, salary_structure=self,
+				frappe.enqueue(
+					assign_salary_structure_for_employees,
+					timeout=600,
+					employees=employees,
+					salary_structure=self,
 					payroll_payable_account=payroll_payable_account,
-					from_date=from_date, base=base, variable=variable, income_tax_slab=income_tax_slab)
+					from_date=from_date,
+					base=base,
+					variable=variable,
+					income_tax_slab=income_tax_slab,
+				)
 			else:
-				assign_salary_structure_for_employees(employees, self,
+				assign_salary_structure_for_employees(
+					employees,
+					self,
 					payroll_payable_account=payroll_payable_account,
-					from_date=from_date, base=base, variable=variable, income_tax_slab=income_tax_slab)
+					from_date=from_date,
+					base=base,
+					variable=variable,
+					income_tax_slab=income_tax_slab,
+				)
 		else:
 			frappe.msgprint(_("No Employee Found"))
 
 
-
-def assign_salary_structure_for_employees(employees, salary_structure, payroll_payable_account=None, from_date=None, base=None, variable=None, income_tax_slab=None):
+def assign_salary_structure_for_employees(
+	employees,
+	salary_structure,
+	payroll_payable_account=None,
+	from_date=None,
+	base=None,
+	variable=None,
+	income_tax_slab=None,
+):
 	salary_structures_assignments = []
 	existing_assignments_for = get_existing_assignments(employees, salary_structure, from_date)
-	count=0
+	count = 0
 	for employee in employees:
 		if employee in existing_assignments_for:
 			continue
-		count +=1
+		count += 1
 
-		salary_structures_assignment = create_salary_structures_assignment(employee,
-			salary_structure, payroll_payable_account, from_date, base, variable, income_tax_slab)
+		salary_structures_assignment = create_salary_structures_assignment(
+			employee, salary_structure, payroll_payable_account, from_date, base, variable, income_tax_slab
+		)
 		salary_structures_assignments.append(salary_structures_assignment)
-		frappe.publish_progress(count*100/len(set(employees) - set(existing_assignments_for)), title = _("Assigning Structures..."))
+		frappe.publish_progress(
+			count * 100 / len(set(employees) - set(existing_assignments_for)),
+			title=_("Assigning Structures..."),
+		)
 
 	if salary_structures_assignments:
 		frappe.msgprint(_("Structures have been assigned successfully"))
 
 
-def create_salary_structures_assignment(employee, salary_structure, payroll_payable_account, from_date, base, variable, income_tax_slab=None):
+def create_salary_structures_assignment(
+	employee,
+	salary_structure,
+	payroll_payable_account,
+	from_date,
+	base,
+	variable,
+	income_tax_slab=None,
+):
 	if not payroll_payable_account:
-		payroll_payable_account = frappe.db.get_value('Company', salary_structure.company, 'default_payroll_payable_account')
+		payroll_payable_account = frappe.db.get_value(
+			"Company", salary_structure.company, "default_payroll_payable_account"
+		)
 		if not payroll_payable_account:
 			frappe.throw(_('Please set "Default Payroll Payable Account" in Company Defaults'))
-	payroll_payable_account_currency = frappe.db.get_value('Account',  payroll_payable_account, 'account_currency')
+	payroll_payable_account_currency = frappe.db.get_value(
+		"Account", payroll_payable_account, "account_currency"
+	)
 	company_curency = erpnext.get_company_currency(salary_structure.company)
-	if payroll_payable_account_currency != salary_structure.currency and payroll_payable_account_currency != company_curency:
-		frappe.throw(_("Invalid Payroll Payable Account. The account currency must be {0} or {1}").format(salary_structure.currency, company_curency))
+	if (
+		payroll_payable_account_currency != salary_structure.currency
+		and payroll_payable_account_currency != company_curency
+	):
+		frappe.throw(
+			_("Invalid Payroll Payable Account. The account currency must be {0} or {1}").format(
+				salary_structure.currency, company_curency
+			)
+		)
 
 	assignment = frappe.new_doc("Salary Structure Assignment")
 	assignment.employee = employee
@@ -146,61 +224,96 @@
 	assignment.base = base
 	assignment.variable = variable
 	assignment.income_tax_slab = income_tax_slab
-	assignment.save(ignore_permissions = True)
+	assignment.save(ignore_permissions=True)
 	assignment.submit()
 	return assignment.name
 
 
 def get_existing_assignments(employees, salary_structure, from_date):
-	salary_structures_assignments = frappe.db.sql_list("""
+	salary_structures_assignments = frappe.db.sql_list(
+		"""
 		select distinct employee from `tabSalary Structure Assignment`
 		where salary_structure=%s and employee in (%s)
 		and from_date=%s  and company= %s and docstatus=1
-	""" % ('%s', ', '.join(['%s']*len(employees)),'%s', '%s'), [salary_structure.name] + employees+[from_date]+[salary_structure.company])
+	"""
+		% ("%s", ", ".join(["%s"] * len(employees)), "%s", "%s"),
+		[salary_structure.name] + employees + [from_date] + [salary_structure.company],
+	)
 	if salary_structures_assignments:
-		frappe.msgprint(_("Skipping Salary Structure Assignment for the following employees, as Salary Structure Assignment records already exists against them. {0}")
-			.format("\n".join(salary_structures_assignments)))
+		frappe.msgprint(
+			_(
+				"Skipping Salary Structure Assignment for the following employees, as Salary Structure Assignment records already exists against them. {0}"
+			).format("\n".join(salary_structures_assignments))
+		)
 	return salary_structures_assignments
 
+
 @frappe.whitelist()
-def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None, for_preview=0, ignore_permissions=False):
+def make_salary_slip(
+	source_name,
+	target_doc=None,
+	employee=None,
+	posting_date=None,
+	as_print=False,
+	print_format=None,
+	for_preview=0,
+	ignore_permissions=False,
+):
 	def postprocess(source, target):
 		if employee:
-			employee_details = frappe.db.get_value("Employee", employee,
-				["employee_name", "branch", "designation", "department"], as_dict=1)
+			employee_details = frappe.db.get_value(
+				"Employee", employee, ["employee_name", "branch", "designation", "department"], as_dict=1
+			)
 			target.employee = employee
 			target.employee_name = employee_details.employee_name
 			target.branch = employee_details.branch
 			target.designation = employee_details.designation
 			target.department = employee_details.department
 
-		target.run_method('process_salary_structure', for_preview=for_preview)
+			if posting_date:
+				target.posting_date = posting_date
 
-	doc = get_mapped_doc("Salary Structure", source_name, {
-		"Salary Structure": {
-			"doctype": "Salary Slip",
-			"field_map": {
-				"total_earning": "gross_pay",
-				"name": "salary_structure",
-				"currency": "currency"
+		target.run_method("process_salary_structure", for_preview=for_preview)
+
+	doc = get_mapped_doc(
+		"Salary Structure",
+		source_name,
+		{
+			"Salary Structure": {
+				"doctype": "Salary Slip",
+				"field_map": {
+					"total_earning": "gross_pay",
+					"name": "salary_structure",
+					"currency": "currency",
+				},
 			}
-		}
-	}, target_doc, postprocess, ignore_child_tables=True, ignore_permissions=ignore_permissions)
+		},
+		target_doc,
+		postprocess,
+		ignore_child_tables=True,
+		ignore_permissions=ignore_permissions,
+	)
 
 	if cint(as_print):
-		doc.name = 'Preview for {0}'.format(employee)
-		return frappe.get_print(doc.doctype, doc.name, doc = doc, print_format = print_format)
+		doc.name = "Preview for {0}".format(employee)
+		return frappe.get_print(doc.doctype, doc.name, doc=doc, print_format=print_format)
 	else:
 		return doc
 
 
 @frappe.whitelist()
 def get_employees(salary_structure):
-	employees = frappe.get_list('Salary Structure Assignment',
-		filters={'salary_structure': salary_structure, 'docstatus': 1}, fields=['employee'])
+	employees = frappe.get_list(
+		"Salary Structure Assignment",
+		filters={"salary_structure": salary_structure, "docstatus": 1},
+		fields=["employee"],
+	)
 
 	if not employees:
-		frappe.throw(_("There's no Employee with Salary Structure: {0}. Assign {1} to an Employee to preview Salary Slip").format(
-			salary_structure, salary_structure))
+		frappe.throw(
+			_(
+				"There's no Employee with Salary Structure: {0}. Assign {1} to an Employee to preview Salary Slip"
+			).format(salary_structure, salary_structure)
+		)
 
 	return list(set([d.employee for d in employees]))
diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py b/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py
index 014d0ba..cf363b4 100644
--- a/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py
+++ b/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py
@@ -1,15 +1,9 @@
 def get_data():
 	return {
-		'fieldname': 'salary_structure',
-		'non_standard_fieldnames': {
-			'Employee Grade': 'default_salary_structure'
-		},
-		'transactions': [
-			{
-				'items': ['Salary Structure Assignment', 'Salary Slip']
-			},
-			{
-				'items': ['Employee Grade']
-			},
-		]
+		"fieldname": "salary_structure",
+		"non_standard_fieldnames": {"Employee Grade": "default_salary_structure"},
+		"transactions": [
+			{"items": ["Salary Structure Assignment", "Salary Slip"]},
+			{"items": ["Employee Grade"]},
+		],
 	}
diff --git a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
index e2d0d1c..8cc2ea3 100644
--- a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
+++ b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
@@ -4,6 +4,7 @@
 import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import add_years, date_diff, get_first_day, nowdate
 from frappe.utils.make_random import get_random
 
@@ -22,25 +23,33 @@
 
 test_dependencies = ["Fiscal Year"]
 
-class TestSalaryStructure(unittest.TestCase):
+
+class TestSalaryStructure(FrappeTestCase):
 	def setUp(self):
 		for dt in ["Salary Slip", "Salary Structure", "Salary Structure Assignment"]:
 			frappe.db.sql("delete from `tab%s`" % dt)
 
 		self.make_holiday_list()
-		frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Structure Test Holiday List")
+		frappe.db.set_value(
+			"Company",
+			erpnext.get_default_company(),
+			"default_holiday_list",
+			"Salary Structure Test Holiday List",
+		)
 		make_employee("test_employee@salary.com")
 		make_employee("test_employee_2@salary.com")
 
 	def make_holiday_list(self):
 		if not frappe.db.get_value("Holiday List", "Salary Structure Test Holiday List"):
-			holiday_list = frappe.get_doc({
-				"doctype": "Holiday List",
-				"holiday_list_name": "Salary Structure Test Holiday List",
-				"from_date": nowdate(),
-				"to_date": add_years(nowdate(), 1),
-				"weekly_off": "Sunday"
-			}).insert()
+			holiday_list = frappe.get_doc(
+				{
+					"doctype": "Holiday List",
+					"holiday_list_name": "Salary Structure Test Holiday List",
+					"from_date": nowdate(),
+					"to_date": add_years(nowdate(), 1),
+					"weekly_off": "Sunday",
+				}
+			).insert()
 			holiday_list.get_weekly_off_dates()
 			holiday_list.save()
 
@@ -48,31 +57,33 @@
 
 		emp = make_employee("test_employee_3@salary.com")
 
-		sal_struct = make_salary_structure("Salary Structure 2", "Monthly", dont_submit = True)
+		sal_struct = make_salary_structure("Salary Structure 2", "Monthly", dont_submit=True)
 
 		sal_struct.earnings = [sal_struct.earnings[0]]
 		sal_struct.earnings[0].amount_based_on_formula = 1
-		sal_struct.earnings[0].formula =  "base"
+		sal_struct.earnings[0].formula = "base"
 
 		sal_struct.deductions = [sal_struct.deductions[0]]
 
 		sal_struct.deductions[0].amount_based_on_formula = 1
 		sal_struct.deductions[0].condition = "gross_pay > 100"
-		sal_struct.deductions[0].formula =  "gross_pay * 0.2"
+		sal_struct.deductions[0].formula = "gross_pay * 0.2"
 
 		sal_struct.submit()
 
 		assignment = create_salary_structure_assignment(emp, "Salary Structure 2")
-		ss = make_salary_slip(sal_struct.name, employee = emp)
+		ss = make_salary_slip(sal_struct.name, employee=emp)
 
 		self.assertEqual(assignment.base * 0.2, ss.deductions[0].amount)
 
 	def test_amount_totals(self):
 		frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0)
-		sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee_2@salary.com"})
+		sal_slip = frappe.get_value("Salary Slip", {"employee_name": "test_employee_2@salary.com"})
 		if not sal_slip:
-			sal_slip = make_employee_salary_slip("test_employee_2@salary.com", "Monthly", "Salary Structure Sample")
-			self.assertEqual(sal_slip.get("salary_structure"), 'Salary Structure Sample')
+			sal_slip = make_employee_salary_slip(
+				"test_employee_2@salary.com", "Monthly", "Salary Structure Sample"
+			)
+			self.assertEqual(sal_slip.get("salary_structure"), "Salary Structure Sample")
 			self.assertEqual(sal_slip.get("earnings")[0].amount, 50000)
 			self.assertEqual(sal_slip.get("earnings")[1].amount, 3000)
 			self.assertEqual(sal_slip.get("earnings")[2].amount, 25000)
@@ -84,12 +95,12 @@
 		salary_structure = make_salary_structure("Salary Structure Sample", "Monthly", dont_submit=True)
 
 		for row in salary_structure.earnings:
-			row.formula = "\n%s\n\n"%row.formula
-			row.condition = "\n%s\n\n"%row.condition
+			row.formula = "\n%s\n\n" % row.formula
+			row.condition = "\n%s\n\n" % row.condition
 
 		for row in salary_structure.deductions:
-			row.formula = "\n%s\n\n"%row.formula
-			row.condition = "\n%s\n\n"%row.condition
+			row.formula = "\n%s\n\n" % row.formula
+			row.condition = "\n%s\n\n" % row.condition
 
 		salary_structure.save()
 
@@ -101,30 +112,63 @@
 
 	def test_salary_structures_assignment(self):
 		company_currency = erpnext.get_default_currency()
-		salary_structure = make_salary_structure("Salary Structure Sample", "Monthly", currency=company_currency)
+		salary_structure = make_salary_structure(
+			"Salary Structure Sample", "Monthly", currency=company_currency
+		)
 		employee = "test_assign_stucture@salary.com"
 		employee_doc_name = make_employee(employee)
 		# clear the already assigned stuctures
-		frappe.db.sql('''delete from `tabSalary Structure Assignment` where employee=%s and salary_structure=%s ''',
-					  ("test_assign_stucture@salary.com",salary_structure.name))
-		#test structure_assignment
-		salary_structure.assign_salary_structure(employee=employee_doc_name,from_date='2013-01-01',base=5000,variable=200)
-		salary_structure_assignment = frappe.get_doc("Salary Structure Assignment",{'employee':employee_doc_name, 'from_date':'2013-01-01'})
+		frappe.db.sql(
+			"""delete from `tabSalary Structure Assignment` where employee=%s and salary_structure=%s """,
+			("test_assign_stucture@salary.com", salary_structure.name),
+		)
+		# test structure_assignment
+		salary_structure.assign_salary_structure(
+			employee=employee_doc_name, from_date="2013-01-01", base=5000, variable=200
+		)
+		salary_structure_assignment = frappe.get_doc(
+			"Salary Structure Assignment", {"employee": employee_doc_name, "from_date": "2013-01-01"}
+		)
 		self.assertEqual(salary_structure_assignment.docstatus, 1)
 		self.assertEqual(salary_structure_assignment.base, 5000)
 		self.assertEqual(salary_structure_assignment.variable, 200)
 
+	def test_employee_grade_defaults(self):
+		salary_structure = make_salary_structure(
+			"Salary Structure - Lead", "Monthly", currency="INR", company="_Test Company"
+		)
+		create_employee_grade("Lead", salary_structure.name)
+		employee = make_employee("test_employee_grade@salary.com", company="_Test Company", grade="Lead")
+
+		# structure assignment should have the default salary structure and base pay
+		salary_structure.assign_salary_structure(employee=employee, from_date=nowdate())
+		structure, base = frappe.db.get_value(
+			"Salary Structure Assignment",
+			{"employee": employee, "salary_structure": salary_structure.name, "from_date": nowdate()},
+			["salary_structure", "base"],
+		)
+		self.assertEqual(structure, salary_structure.name)
+		self.assertEqual(base, 50000)
+
 	def test_multi_currency_salary_structure(self):
 		make_employee("test_muti_currency_employee@salary.com")
-		sal_struct = make_salary_structure("Salary Structure Multi Currency", "Monthly", currency='USD')
-		self.assertEqual(sal_struct.currency, 'USD')
+		sal_struct = make_salary_structure("Salary Structure Multi Currency", "Monthly", currency="USD")
+		self.assertEqual(sal_struct.currency, "USD")
 
-def make_salary_structure(salary_structure, payroll_frequency, employee=None,
-	from_date=None, dont_submit=False, other_details=None,test_tax=False,
-	company=None, currency=erpnext.get_default_currency(), payroll_period=None):
-	if test_tax:
-		frappe.db.sql("""delete from `tabSalary Structure` where name=%s""",(salary_structure))
 
+def make_salary_structure(
+	salary_structure,
+	payroll_frequency,
+	employee=None,
+	from_date=None,
+	dont_submit=False,
+	other_details=None,
+	test_tax=False,
+	company=None,
+	currency=erpnext.get_default_currency(),
+	payroll_period=None,
+	include_flexi_benefits=False,
+):
 	if frappe.db.exists("Salary Structure", salary_structure):
 		frappe.db.delete("Salary Structure", salary_structure)
 
@@ -132,11 +176,18 @@
 		"doctype": "Salary Structure",
 		"name": salary_structure,
 		"company": company or erpnext.get_default_company(),
-		"earnings": make_earning_salary_component(setup=True,  test_tax=test_tax, company_list=["_Test Company"]),
-		"deductions": make_deduction_salary_component(setup=True, test_tax=test_tax, company_list=["_Test Company"]),
+		"earnings": make_earning_salary_component(
+			setup=True,
+			test_tax=test_tax,
+			company_list=["_Test Company"],
+			include_flexi_benefits=include_flexi_benefits,
+		),
+		"deductions": make_deduction_salary_component(
+			setup=True, test_tax=test_tax, company_list=["_Test Company"]
+		),
 		"payroll_frequency": payroll_frequency,
-		"payment_account": get_random("Account", filters={'account_currency': currency}),
-		"currency": currency
+		"payment_account": get_random("Account", filters={"account_currency": currency}),
+		"currency": currency,
 	}
 	if other_details and isinstance(other_details, dict):
 		details.update(other_details)
@@ -145,31 +196,44 @@
 	if not dont_submit:
 		salary_structure_doc.submit()
 
-	filters = {'employee':employee, 'docstatus': 1}
+	filters = {"employee": employee, "docstatus": 1}
 	if not from_date and payroll_period:
 		from_date = payroll_period.start_date
 
 	if from_date:
-		filters['from_date'] = from_date
+		filters["from_date"] = from_date
 
-	if employee and not frappe.db.get_value("Salary Structure Assignment",
-		filters) and salary_structure_doc.docstatus==1:
+	if (
+		employee
+		and not frappe.db.get_value("Salary Structure Assignment", filters)
+		and salary_structure_doc.docstatus == 1
+	):
 		create_salary_structure_assignment(
 			employee,
 			salary_structure,
 			from_date=from_date,
 			company=company,
 			currency=currency,
-			payroll_period=payroll_period
+			payroll_period=payroll_period,
 		)
 
 	return salary_structure_doc
 
-def create_salary_structure_assignment(employee, salary_structure, from_date=None, company=None, currency=erpnext.get_default_currency(),
-	payroll_period=None):
 
-	if frappe.db.exists("Salary Structure Assignment", {"employee": employee}):
-		frappe.db.sql("""delete from `tabSalary Structure Assignment` where employee=%s""",(employee))
+def create_salary_structure_assignment(
+	employee,
+	salary_structure,
+	from_date=None,
+	company=None,
+	currency=erpnext.get_default_currency(),
+	payroll_period=None,
+	base=None,
+	allow_duplicate=False,
+):
+	if not allow_duplicate and frappe.db.exists(
+		"Salary Structure Assignment", {"employee": employee}
+	):
+		frappe.db.sql("""delete from `tabSalary Structure Assignment` where employee=%s""", (employee))
 
 	if not payroll_period:
 		payroll_period = create_payroll_period()
@@ -181,7 +245,7 @@
 
 	salary_structure_assignment = frappe.new_doc("Salary Structure Assignment")
 	salary_structure_assignment.employee = employee
-	salary_structure_assignment.base = 50000
+	salary_structure_assignment.base = base or 50000
 	salary_structure_assignment.variable = 5000
 
 	if not from_date:
@@ -200,7 +264,20 @@
 	salary_structure_assignment.submit()
 	return salary_structure_assignment
 
+
 def get_payable_account(company=None):
 	if not company:
 		company = erpnext.get_default_company()
 	return frappe.db.get_value("Company", company, "default_payroll_payable_account")
+
+
+def create_employee_grade(grade, default_structure=None):
+	if not frappe.db.exists("Employee Grade", grade):
+		frappe.get_doc(
+			{
+				"doctype": "Employee Grade",
+				"__newname": grade,
+				"default_salary_structure": default_structure,
+				"default_base_pay": 50000,
+			}
+		).insert()
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
index 613246e..91d49a0 100644
--- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
+++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
@@ -10,6 +10,7 @@
   "employee",
   "employee_name",
   "department",
+  "grade",
   "company",
   "payroll_payable_account",
   "column_break_6",
@@ -67,6 +68,8 @@
    "fieldtype": "Column Break"
   },
   {
+   "fetch_from": "grade.default_salary_structure",
+   "fetch_if_empty": 1,
    "fieldname": "salary_structure",
    "fieldtype": "Link",
    "in_list_view": 1,
@@ -96,6 +99,8 @@
    "label": "Base & Variable"
   },
   {
+   "fetch_from": "grade.default_base_pay",
+   "fetch_if_empty": 1,
    "fieldname": "base",
    "fieldtype": "Currency",
    "label": "Base",
@@ -158,11 +163,19 @@
    "fieldtype": "Table",
    "label": "Cost Centers",
    "options": "Employee Cost Center"
+  },
+  {
+   "fetch_from": "employee.grade",
+   "fieldname": "grade",
+   "fieldtype": "Link",
+   "label": "Grade",
+   "options": "Employee Grade",
+   "read_only": 1
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2022-01-19 12:43:54.439073",
+ "modified": "2022-05-06 12:18:36.972336",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Salary Structure Assignment",
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
index 8359478..28b5eb1 100644
--- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
+++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
@@ -8,7 +8,9 @@
 from frappe.utils import flt, getdate
 
 
-class DuplicateAssignment(frappe.ValidationError): pass
+class DuplicateAssignment(frappe.ValidationError):
+	pass
+
 
 class SalaryStructureAssignment(Document):
 	def validate(self):
@@ -21,38 +23,61 @@
 		self.validate_cost_center_distribution()
 
 	def validate_dates(self):
-		joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
-			["date_of_joining", "relieving_date"])
+		joining_date, relieving_date = frappe.db.get_value(
+			"Employee", self.employee, ["date_of_joining", "relieving_date"]
+		)
 
 		if self.from_date:
-			if frappe.db.exists("Salary Structure Assignment", {"employee": self.employee, "from_date": self.from_date, "docstatus": 1}):
+			if frappe.db.exists(
+				"Salary Structure Assignment",
+				{"employee": self.employee, "from_date": self.from_date, "docstatus": 1},
+			):
 				frappe.throw(_("Salary Structure Assignment for Employee already exists"), DuplicateAssignment)
 
 			if joining_date and getdate(self.from_date) < joining_date:
-				frappe.throw(_("From Date {0} cannot be before employee's joining Date {1}")
-					.format(self.from_date, joining_date))
+				frappe.throw(
+					_("From Date {0} cannot be before employee's joining Date {1}").format(
+						self.from_date, joining_date
+					)
+				)
 
 			# flag - old_employee is for migrating the old employees data via patch
 			if relieving_date and getdate(self.from_date) > relieving_date and not self.flags.old_employee:
-				frappe.throw(_("From Date {0} cannot be after employee's relieving Date {1}")
-					.format(self.from_date, relieving_date))
+				frappe.throw(
+					_("From Date {0} cannot be after employee's relieving Date {1}").format(
+						self.from_date, relieving_date
+					)
+				)
 
 	def validate_income_tax_slab(self):
 		if not self.income_tax_slab:
 			return
 
-		income_tax_slab_currency = frappe.db.get_value('Income Tax Slab', self.income_tax_slab, 'currency')
+		income_tax_slab_currency = frappe.db.get_value(
+			"Income Tax Slab", self.income_tax_slab, "currency"
+		)
 		if self.currency != income_tax_slab_currency:
-			frappe.throw(_("Currency of selected Income Tax Slab should be {0} instead of {1}").format(self.currency, income_tax_slab_currency))
+			frappe.throw(
+				_("Currency of selected Income Tax Slab should be {0} instead of {1}").format(
+					self.currency, income_tax_slab_currency
+				)
+			)
 
 	def set_payroll_payable_account(self):
 		if not self.payroll_payable_account:
-			payroll_payable_account = frappe.db.get_value('Company', self.company, 'default_payroll_payable_account')
+			payroll_payable_account = frappe.db.get_value(
+				"Company", self.company, "default_payroll_payable_account"
+			)
 			if not payroll_payable_account:
 				payroll_payable_account = frappe.db.get_value(
-					"Account", {
-						"account_name": _("Payroll Payable"), "company": self.company, "account_currency": frappe.db.get_value(
-							"Company", self.company, "default_currency"), "is_group": 0})
+					"Account",
+					{
+						"account_name": _("Payroll Payable"),
+						"company": self.company,
+						"account_currency": frappe.db.get_value("Company", self.company, "default_currency"),
+						"is_group": 0,
+					},
+				)
 			self.payroll_payable_account = payroll_payable_account
 
 	@frappe.whitelist()
@@ -60,10 +85,9 @@
 		self.payroll_cost_centers = []
 		default_payroll_cost_center = self.get_payroll_cost_center()
 		if default_payroll_cost_center:
-			self.append("payroll_cost_centers", {
-				"cost_center": default_payroll_cost_center,
-				"percentage": 100
-			})
+			self.append(
+				"payroll_cost_centers", {"cost_center": default_payroll_cost_center, "percentage": 100}
+			)
 
 	def get_payroll_cost_center(self):
 		payroll_cost_center = frappe.db.get_value("Employee", self.employee, "payroll_cost_center")
@@ -82,20 +106,29 @@
 def get_assigned_salary_structure(employee, on_date):
 	if not employee or not on_date:
 		return None
-	salary_structure = frappe.db.sql("""
+	salary_structure = frappe.db.sql(
+		"""
 		select salary_structure from `tabSalary Structure Assignment`
 		where employee=%(employee)s
 		and docstatus = 1
-		and %(on_date)s >= from_date order by from_date desc limit 1""", {
-			'employee': employee,
-			'on_date': on_date,
-		})
+		and %(on_date)s >= from_date order by from_date desc limit 1""",
+		{
+			"employee": employee,
+			"on_date": on_date,
+		},
+	)
 	return salary_structure[0][0] if salary_structure else None
 
 
 @frappe.whitelist()
 def get_employee_currency(employee):
-	employee_currency = frappe.db.get_value('Salary Structure Assignment', {'employee': employee}, 'currency')
+	employee_currency = frappe.db.get_value(
+		"Salary Structure Assignment", {"employee": employee}, "currency"
+	)
 	if not employee_currency:
-		frappe.throw(_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(employee))
+		frappe.throw(
+			_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(
+				employee
+			)
+		)
 	return employee_currency
diff --git a/erpnext/payroll/report/bank_remittance/bank_remittance.py b/erpnext/payroll/report/bank_remittance/bank_remittance.py
index 6c3bd37..9d8efff 100644
--- a/erpnext/payroll/report/bank_remittance/bank_remittance.py
+++ b/erpnext/payroll/report/bank_remittance/bank_remittance.py
@@ -13,63 +13,47 @@
 			"fieldtype": "Link",
 			"fieldname": "payroll_no",
 			"options": "Payroll Entry",
-			"width": 150
+			"width": 150,
 		},
 		{
 			"label": _("Debit A/C Number"),
 			"fieldtype": "Int",
 			"fieldname": "debit_account",
 			"hidden": 1,
-			"width": 200
+			"width": 200,
 		},
-		{
-			"label": _("Payment Date"),
-			"fieldtype": "Data",
-			"fieldname": "payment_date",
-			"width": 100
-		},
+		{"label": _("Payment Date"), "fieldtype": "Data", "fieldname": "payment_date", "width": 100},
 		{
 			"label": _("Employee Name"),
 			"fieldtype": "Link",
 			"fieldname": "employee_name",
 			"options": "Employee",
-			"width": 200
+			"width": 200,
 		},
-		{
-			"label": _("Bank Name"),
-			"fieldtype": "Data",
-			"fieldname": "bank_name",
-			"width": 50
-		},
+		{"label": _("Bank Name"), "fieldtype": "Data", "fieldname": "bank_name", "width": 50},
 		{
 			"label": _("Employee A/C Number"),
 			"fieldtype": "Int",
 			"fieldname": "employee_account_no",
-			"width": 50
-		}
+			"width": 50,
+		},
 	]
 
-	if frappe.db.has_column('Employee', 'ifsc_code'):
-		columns.append({
-			"label": _("IFSC Code"),
-			"fieldtype": "Data",
-			"fieldname": "bank_code",
-			"width": 100
-		})
+	if frappe.db.has_column("Employee", "ifsc_code"):
+		columns.append(
+			{"label": _("IFSC Code"), "fieldtype": "Data", "fieldname": "bank_code", "width": 100}
+		)
 
-	columns += [{
-		"label": _("Currency"),
-		"fieldtype": "Data",
-		"fieldname": "currency",
-		"width": 50
-	},
-	{
-		"label": _("Net Salary Amount"),
-		"fieldtype": "Currency",
-		"options": "currency",
-		"fieldname": "amount",
-		"width": 100
-	}]
+	columns += [
+		{"label": _("Currency"), "fieldtype": "Data", "fieldname": "currency", "width": 50},
+		{
+			"label": _("Net Salary Amount"),
+			"fieldtype": "Currency",
+			"options": "currency",
+			"fieldname": "amount",
+			"width": 100,
+		},
+	]
 
 	data = []
 
@@ -77,41 +61,48 @@
 	payroll_entries = get_payroll_entries(accounts, filters)
 	salary_slips = get_salary_slips(payroll_entries)
 
-	if frappe.db.has_column('Employee', 'ifsc_code'):
+	if frappe.db.has_column("Employee", "ifsc_code"):
 		get_emp_bank_ifsc_code(salary_slips)
 
 	for salary in salary_slips:
-		if salary.bank_name and salary.bank_account_no and salary.debit_acc_no and salary.status in ["Submitted", "Paid"]:
+		if (
+			salary.bank_name
+			and salary.bank_account_no
+			and salary.debit_acc_no
+			and salary.status in ["Submitted", "Paid"]
+		):
 			row = {
 				"payroll_no": salary.payroll_entry,
 				"debit_account": salary.debit_acc_no,
-				"payment_date": frappe.utils.formatdate(salary.modified.strftime('%Y-%m-%d')),
+				"payment_date": frappe.utils.formatdate(salary.modified.strftime("%Y-%m-%d")),
 				"bank_name": salary.bank_name,
 				"employee_account_no": salary.bank_account_no,
 				"bank_code": salary.ifsc_code,
-				"employee_name": salary.employee+": " + salary.employee_name,
-				"currency": frappe.get_cached_value('Company', filters.company, 'default_currency'),
+				"employee_name": salary.employee + ": " + salary.employee_name,
+				"currency": frappe.get_cached_value("Company", filters.company, "default_currency"),
 				"amount": salary.net_pay,
 			}
 			data.append(row)
 
 	return columns, data
 
+
 def get_bank_accounts():
 	accounts = [d.name for d in get_all("Account", filters={"account_type": "Bank"})]
 	return accounts
 
+
 def get_payroll_entries(accounts, filters):
 	payroll_filter = [
-		('payment_account', 'IN', accounts),
-		('number_of_employees', '>', 0),
-		('Company', '=', filters.company)
+		("payment_account", "IN", accounts),
+		("number_of_employees", ">", 0),
+		("Company", "=", filters.company),
 	]
 	if filters.to_date:
-		payroll_filter.append(('posting_date', '<', filters.to_date))
+		payroll_filter.append(("posting_date", "<", filters.to_date))
 
 	if filters.from_date:
-		payroll_filter.append(('posting_date', '>', filters.from_date))
+		payroll_filter.append(("posting_date", ">", filters.from_date))
 
 	entries = get_all("Payroll Entry", payroll_filter, ["name", "payment_account"])
 
@@ -119,10 +110,22 @@
 	entries = set_company_account(payment_accounts, entries)
 	return entries
 
+
 def get_salary_slips(payroll_entries):
-	payroll  = [d.name for d in payroll_entries]
-	salary_slips = get_all("Salary Slip", filters = [("payroll_entry", "IN", payroll)],
-		fields = ["modified", "net_pay", "bank_name", "bank_account_no", "payroll_entry", "employee", "employee_name", "status"]
+	payroll = [d.name for d in payroll_entries]
+	salary_slips = get_all(
+		"Salary Slip",
+		filters=[("payroll_entry", "IN", payroll)],
+		fields=[
+			"modified",
+			"net_pay",
+			"bank_name",
+			"bank_account_no",
+			"payroll_entry",
+			"employee",
+			"employee_name",
+			"status",
+		],
 	)
 
 	payroll_entry_map = {}
@@ -132,12 +135,13 @@
 	# appending company debit accounts
 	for slip in salary_slips:
 		if slip.payroll_entry:
-			slip["debit_acc_no"] = payroll_entry_map[slip.payroll_entry]['company_account']
+			slip["debit_acc_no"] = payroll_entry_map[slip.payroll_entry]["company_account"]
 		else:
 			slip["debit_acc_no"] = None
 
 	return salary_slips
 
+
 def get_emp_bank_ifsc_code(salary_slips):
 	emp_names = [d.employee for d in salary_slips]
 	ifsc_codes = get_all("Employee", [("name", "IN", emp_names)], ["ifsc_code", "name"])
@@ -147,20 +151,23 @@
 		ifsc_codes_map[code.name] = code
 
 	for slip in salary_slips:
-		slip["ifsc_code"] = ifsc_codes_map[code.name]['ifsc_code']
+		slip["ifsc_code"] = ifsc_codes_map[code.name]["ifsc_code"]
 
 	return salary_slips
 
+
 def set_company_account(payment_accounts, payroll_entries):
-	company_accounts = get_all("Bank Account", [("account", "in", payment_accounts)], ["account", "bank_account_no"])
+	company_accounts = get_all(
+		"Bank Account", [("account", "in", payment_accounts)], ["account", "bank_account_no"]
+	)
 	company_accounts_map = {}
 	for acc in company_accounts:
 		company_accounts_map[acc.account] = acc
 
 	for entry in payroll_entries:
-		company_account = ''
+		company_account = ""
 		if entry.payment_account in company_accounts_map:
-			company_account = company_accounts_map[entry.payment_account]['bank_account_no']
+			company_account = company_accounts_map[entry.payment_account]["bank_account_no"]
 		entry["company_account"] = company_account
 
 	return payroll_entries
diff --git a/erpnext/education/doctype/content_question/__init__.py b/erpnext/payroll/report/income_tax_computation/__init__.py
similarity index 100%
rename from erpnext/education/doctype/content_question/__init__.py
rename to erpnext/payroll/report/income_tax_computation/__init__.py
diff --git a/erpnext/payroll/report/income_tax_computation/income_tax_computation.js b/erpnext/payroll/report/income_tax_computation/income_tax_computation.js
new file mode 100644
index 0000000..26e463f
--- /dev/null
+++ b/erpnext/payroll/report/income_tax_computation/income_tax_computation.js
@@ -0,0 +1,47 @@
+// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Income Tax Computation"] = {
+	"filters": [
+		{
+			"fieldname":"company",
+			"label": __("Company"),
+			"fieldtype": "Link",
+			"options": "Company",
+			"default": frappe.defaults.get_user_default("Company"),
+			"width": "100px",
+			"reqd": 1
+		},
+		{
+			"fieldname":"payroll_period",
+			"label": __("Payroll Period"),
+			"fieldtype": "Link",
+			"options": "Payroll Period",
+			"width": "100px",
+			"reqd": 1
+		},
+		{
+			"fieldname":"employee",
+			"label": __("Employee"),
+			"fieldtype": "Link",
+			"options": "Employee",
+			"width": "100px"
+		},
+		{
+			"fieldname":"department",
+			"label": __("Department"),
+			"fieldtype": "Link",
+			"options": "Department",
+			"width": "100px",
+		},
+		{
+			"fieldname":"consider_tax_exemption_declaration",
+			"label": __("Consider Tax Exemption Declaration"),
+			"fieldtype": "Check",
+			"width": "180px"
+		}
+	]
+};
+
+
diff --git a/erpnext/payroll/report/income_tax_computation/income_tax_computation.json b/erpnext/payroll/report/income_tax_computation/income_tax_computation.json
new file mode 100644
index 0000000..7cb5b22
--- /dev/null
+++ b/erpnext/payroll/report/income_tax_computation/income_tax_computation.json
@@ -0,0 +1,36 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2022-02-17 17:19:30.921422",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "letter_head": "",
+ "modified": "2022-02-23 13:07:30.347861",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Income Tax Computation",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Salary Slip",
+ "report_name": "Income Tax Computation",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "Employee"
+  },
+  {
+   "role": "HR User"
+  },
+  {
+   "role": "HR Manager"
+  },
+  {
+   "role": "Employee Self Service"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/payroll/report/income_tax_computation/income_tax_computation.py b/erpnext/payroll/report/income_tax_computation/income_tax_computation.py
new file mode 100644
index 0000000..739ed8e
--- /dev/null
+++ b/erpnext/payroll/report/income_tax_computation/income_tax_computation.py
@@ -0,0 +1,513 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe import _, scrub
+from frappe.query_builder.functions import Sum
+from frappe.utils import add_days, flt, getdate, rounded
+
+from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_start_end_dates
+from erpnext.payroll.doctype.salary_slip.salary_slip import calculate_tax_by_tax_slab
+
+
+def execute(filters=None):
+	return IncomeTaxComputationReport(filters).run()
+
+
+class IncomeTaxComputationReport(object):
+	def __init__(self, filters=None):
+		self.filters = frappe._dict(filters or {})
+		self.columns = []
+		self.data = []
+		self.employees = frappe._dict()
+		self.payroll_period_start_date = None
+		self.payroll_period_end_date = None
+		if self.filters.payroll_period:
+			self.payroll_period_start_date, self.payroll_period_end_date = frappe.db.get_value(
+				"Payroll Period", self.filters.payroll_period, ["start_date", "end_date"]
+			)
+
+	def run(self):
+		self.get_fixed_columns()
+		self.get_data()
+		return self.columns, self.data
+
+	def get_data(self):
+		self.get_employee_details()
+		self.get_future_salary_slips()
+		self.get_ctc()
+		self.get_tax_exempted_earnings_and_deductions()
+		self.get_employee_tax_exemptions()
+		self.get_hra()
+		self.get_standard_tax_exemption()
+		self.get_total_taxable_amount()
+		self.get_applicable_tax()
+		self.get_total_deducted_tax()
+		self.get_payable_tax()
+
+		self.data = list(self.employees.values())
+
+	def get_employee_details(self):
+		filters, or_filters = self.get_employee_filters()
+		fields = [
+			"name as employee",
+			"employee_name",
+			"department",
+			"designation",
+			"date_of_joining",
+			"relieving_date",
+		]
+
+		employees = frappe.get_all("Employee", filters=filters, or_filters=or_filters, fields=fields)
+		ss_assignments = self.get_ss_assignments([d.employee for d in employees])
+
+		for d in employees:
+			if d.employee in list(ss_assignments.keys()):
+				d.update(ss_assignments[d.employee])
+				self.employees.setdefault(d.employee, d)
+
+		if not self.employees:
+			frappe.throw(_("No employees found with selected filters and active salary structure"))
+
+	def get_employee_filters(self):
+		filters = {"company": self.filters.company}
+		or_filters = {
+			"status": "Active",
+			"relieving_date": ["between", [self.payroll_period_start_date, self.payroll_period_end_date]],
+		}
+		if self.filters.employee:
+			filters = {"name": self.filters.employee}
+		elif self.filters.department:
+			filters.update({"department": self.filters.department})
+
+		return filters, or_filters
+
+	def get_ss_assignments(self, employees):
+		ss_assignments = frappe.get_all(
+			"Salary Structure Assignment",
+			filters={
+				"employee": ["in", employees],
+				"docstatus": 1,
+				"salary_structure": ["is", "set"],
+				"income_tax_slab": ["is", "set"],
+			},
+			fields=["employee", "income_tax_slab", "salary_structure"],
+			order_by="from_date desc",
+		)
+
+		employee_ss_assignments = frappe._dict()
+		for d in ss_assignments:
+			if d.employee not in list(employee_ss_assignments.keys()):
+				tax_slab = frappe.get_cached_value(
+					"Income Tax Slab", d.income_tax_slab, ["allow_tax_exemption", "disabled"], as_dict=1
+				)
+
+				if tax_slab and not tax_slab.disabled:
+					employee_ss_assignments.setdefault(
+						d.employee,
+						{
+							"salary_structure": d.salary_structure,
+							"income_tax_slab": d.income_tax_slab,
+							"allow_tax_exemption": tax_slab.allow_tax_exemption,
+						},
+					)
+		return employee_ss_assignments
+
+	def get_future_salary_slips(self):
+		self.future_salary_slips = frappe._dict()
+		for employee in list(self.employees.keys()):
+			last_ss = self.get_last_salary_slip(employee)
+			if last_ss and last_ss.end_date == self.payroll_period_end_date:
+				continue
+
+			relieving_date = self.employees[employee].get("relieving_date", "")
+			if last_ss:
+				ss_start_date = add_days(last_ss.end_date, 1)
+			else:
+				ss_start_date = self.payroll_period_start_date
+				last_ss = frappe._dict(
+					{
+						"payroll_frequency": "Monthly",
+						"salary_structure": self.employees[employee].get("salary_structure"),
+					}
+				)
+
+			while getdate(ss_start_date) < getdate(self.payroll_period_end_date) and (
+				not relieving_date or getdate(ss_start_date) < relieving_date
+			):
+				ss_end_date = get_start_end_dates(last_ss.payroll_frequency, ss_start_date).end_date
+
+				ss = frappe.new_doc("Salary Slip")
+				ss.employee = employee
+				ss.start_date = ss_start_date
+				ss.end_date = ss_end_date
+				ss.salary_structure = last_ss.salary_structure
+				ss.payroll_frequency = last_ss.payroll_frequency
+				ss.company = self.filters.company
+				try:
+					ss.process_salary_structure(for_preview=1)
+					self.future_salary_slips.setdefault(employee, []).append(ss.as_dict())
+				except Exception:
+					break
+
+				ss_start_date = add_days(ss_end_date, 1)
+
+	def get_last_salary_slip(self, employee):
+		last_salary_slip = frappe.db.get_value(
+			"Salary Slip",
+			{
+				"employee": employee,
+				"docstatus": 1,
+				"start_date": ["between", [self.payroll_period_start_date, self.payroll_period_end_date]],
+			},
+			["start_date", "end_date", "salary_structure", "payroll_frequency"],
+			order_by="start_date desc",
+			as_dict=1,
+		)
+
+		return last_salary_slip
+
+	def get_ctc(self):
+		# Get total earnings from existing salary slip
+		ss = frappe.qb.DocType("Salary Slip")
+		existing_ss = frappe._dict(
+			(
+				frappe.qb.from_(ss)
+				.select(ss.employee, Sum(ss.base_gross_pay).as_("amount"))
+				.where(ss.docstatus == 1)
+				.where(ss.employee.isin(list(self.employees.keys())))
+				.where(ss.start_date >= self.payroll_period_start_date)
+				.where(ss.end_date <= self.payroll_period_end_date)
+				.groupby(ss.employee)
+			).run()
+		)
+
+		for employee in list(self.employees.keys()):
+			future_ss_earnings = self.get_future_earnings(employee)
+			ctc = flt(existing_ss.get(employee)) + future_ss_earnings
+
+			self.employees[employee].setdefault("ctc", ctc)
+
+	def get_future_earnings(self, employee):
+		future_earnings = 0.0
+		for ss in self.future_salary_slips.get(employee, []):
+			future_earnings += flt(ss.base_gross_pay)
+
+		return future_earnings
+
+	def get_tax_exempted_earnings_and_deductions(self):
+		tax_exempted_components = self.get_tax_exempted_components()
+
+		# Get component totals from existing salary slips
+		ss = frappe.qb.DocType("Salary Slip")
+		ss_comps = frappe.qb.DocType("Salary Detail")
+
+		records = (
+			frappe.qb.from_(ss)
+			.inner_join(ss_comps)
+			.on(ss.name == ss_comps.parent)
+			.select(ss.name, ss.employee, ss_comps.salary_component, Sum(ss_comps.amount).as_("amount"))
+			.where(ss.docstatus == 1)
+			.where(ss.employee.isin(list(self.employees.keys())))
+			.where(ss_comps.salary_component.isin(tax_exempted_components))
+			.where(ss.start_date >= self.payroll_period_start_date)
+			.where(ss.end_date <= self.payroll_period_end_date)
+			.groupby(ss.employee, ss_comps.salary_component)
+		).run(as_dict=True)
+
+		existing_ss_exemptions = frappe._dict()
+		for d in records:
+			existing_ss_exemptions.setdefault(d.employee, {}).setdefault(
+				scrub(d.salary_component), d.amount
+			)
+
+		for employee in list(self.employees.keys()):
+			if not self.employees[employee]["allow_tax_exemption"]:
+				continue
+
+			exemptions = existing_ss_exemptions.get(employee, {})
+			self.add_exemptions_from_future_salary_slips(employee, exemptions)
+			self.employees[employee].update(exemptions)
+
+			total_exemptions = sum(list(exemptions.values()))
+			self.add_to_total_exemption(employee, total_exemptions)
+
+	def add_exemptions_from_future_salary_slips(self, employee, exemptions):
+		for ss in self.future_salary_slips.get(employee, []):
+			for e in ss.earnings:
+				if not e.is_tax_applicable:
+					exemptions.setdefault(scrub(e.salary_component), 0)
+					exemptions[scrub(e.salary_component)] += flt(e.amount)
+
+			for d in ss.deductions:
+				if d.exempted_from_income_tax:
+					exemptions.setdefault(scrub(d.salary_component), 0)
+					exemptions[scrub(d.salary_component)] += flt(d.amount)
+
+		return exemptions
+
+	def get_tax_exempted_components(self):
+		# nontaxable earning components
+		nontaxable_earning_components = [
+			d.name
+			for d in frappe.get_all(
+				"Salary Component", {"type": "Earning", "is_tax_applicable": 0, "disabled": 0}
+			)
+		]
+
+		# tax exempted deduction components
+		tax_exempted_deduction_components = [
+			d.name
+			for d in frappe.get_all(
+				"Salary Component", {"type": "Deduction", "exempted_from_income_tax": 1, "disabled": 0}
+			)
+		]
+
+		tax_exempted_components = nontaxable_earning_components + tax_exempted_deduction_components
+
+		# Add columns
+		for d in tax_exempted_components:
+			self.add_column(d)
+
+		return tax_exempted_components
+
+	def add_to_total_exemption(self, employee, amount):
+		self.employees[employee].setdefault("total_exemption", 0)
+		self.employees[employee]["total_exemption"] += amount
+
+	def get_employee_tax_exemptions(self):
+		# add columns
+		exemption_categories = frappe.get_all("Employee Tax Exemption Category", {"is_active": 1})
+		for d in exemption_categories:
+			self.add_column(d.name)
+
+		self.employees_with_proofs = []
+		self.get_tax_exemptions("Employee Tax Exemption Proof Submission")
+		if self.filters.consider_tax_exemption_declaration:
+			self.get_tax_exemptions("Employee Tax Exemption Declaration")
+
+	def get_tax_exemptions(self, source):
+		# Get category-wise exmeptions based on submitted proofs or declarations
+		if source == "Employee Tax Exemption Proof Submission":
+			child_doctype = "Employee Tax Exemption Proof Submission Detail"
+		else:
+			child_doctype = "Employee Tax Exemption Declaration Category"
+
+		max_exemptions = self.get_max_exemptions_based_on_category()
+
+		par = frappe.qb.DocType(source)
+		child = frappe.qb.DocType(child_doctype)
+
+		records = (
+			frappe.qb.from_(par)
+			.inner_join(child)
+			.on(par.name == child.parent)
+			.select(par.employee, child.exemption_category, Sum(child.amount).as_("amount"))
+			.where(par.docstatus == 1)
+			.where(par.employee.isin(list(self.employees.keys())))
+			.where(par.payroll_period == self.filters.payroll_period)
+			.groupby(par.employee, child.exemption_category)
+		).run(as_dict=True)
+
+		for d in records:
+			if not self.employees[d.employee]["allow_tax_exemption"]:
+				continue
+
+			if source == "Employee Tax Exemption Declaration" and d.employee in self.employees_with_proofs:
+				continue
+
+			amount = flt(d.amount)
+			max_eligible_amount = flt(max_exemptions.get(d.exemption_category))
+			if max_eligible_amount and amount > max_eligible_amount:
+				amount = max_eligible_amount
+
+			self.employees[d.employee].setdefault(scrub(d.exemption_category), amount)
+			self.add_to_total_exemption(d.employee, amount)
+
+			if (
+				source == "Employee Tax Exemption Proof Submission"
+				and d.employee not in self.employees_with_proofs
+			):
+				self.employees_with_proofs.append(d.employee)
+
+	def get_max_exemptions_based_on_category(self):
+		return dict(
+			frappe.get_all(
+				"Employee Tax Exemption Category",
+				filters={"is_active": 1},
+				fields=["name", "max_amount"],
+				as_list=1,
+			)
+		)
+
+	def get_hra(self):
+		if not frappe.get_meta("Employee Tax Exemption Declaration").has_field("monthly_house_rent"):
+			return
+
+		self.add_column("HRA")
+
+		self.employees_with_proofs = []
+		self.get_eligible_hra("Employee Tax Exemption Proof Submission")
+		if self.filters.consider_tax_exemption_declaration:
+			self.get_eligible_hra("Employee Tax Exemption Declaration")
+
+	def get_eligible_hra(self, source):
+		if source == "Employee Tax Exemption Proof Submission":
+			hra_amount_field = "total_eligible_hra_exemption"
+		else:
+			hra_amount_field = "annual_hra_exemption"
+
+		records = frappe.get_all(
+			source,
+			filters={
+				"docstatus": 1,
+				"employee": ["in", list(self.employees.keys())],
+				"payroll_period": self.filters.payroll_period,
+			},
+			fields=["employee", hra_amount_field],
+			as_list=1,
+		)
+
+		for d in records:
+			if not self.employees[d[0]]["allow_tax_exemption"]:
+				continue
+
+			if d[0] not in self.employees_with_proofs:
+				self.employees[d[0]].setdefault("hra", d[1])
+				self.add_to_total_exemption(d[0], d[1])
+				self.employees_with_proofs.append(d[0])
+
+	def get_standard_tax_exemption(self):
+		self.add_column("Standard Tax Exemption")
+
+		standard_exemptions_per_slab = dict(
+			frappe.get_all(
+				"Income Tax Slab",
+				filters={"company": self.filters.company, "docstatus": 1, "disabled": 0},
+				fields=["name", "standard_tax_exemption_amount"],
+				as_list=1,
+			)
+		)
+
+		for emp, emp_details in self.employees.items():
+			if not self.employees[emp]["allow_tax_exemption"]:
+				continue
+
+			income_tax_slab = emp_details.get("income_tax_slab")
+			standard_exemption = standard_exemptions_per_slab.get(income_tax_slab, 0)
+			emp_details["standard_tax_exemption"] = standard_exemption
+			self.add_to_total_exemption(emp, standard_exemption)
+
+		self.add_column("Total Exemption")
+
+	def get_total_taxable_amount(self):
+		self.add_column("Total Taxable Amount")
+		for emp, emp_details in self.employees.items():
+			emp_details["total_taxable_amount"] = flt(emp_details.get("ctc")) - flt(
+				emp_details.get("total_exemption")
+			)
+
+	def get_applicable_tax(self):
+		self.add_column("Applicable Tax")
+
+		is_tax_rounded = frappe.db.get_value(
+			"Salary Component",
+			{"variable_based_on_taxable_salary": 1, "disabled": 0},
+			"round_to_the_nearest_integer",
+		)
+
+		for emp, emp_details in self.employees.items():
+			tax_slab = emp_details.get("income_tax_slab")
+			if tax_slab:
+				tax_slab = frappe.get_cached_doc("Income Tax Slab", tax_slab)
+				employee_dict = frappe.get_doc("Employee", emp).as_dict()
+				tax_amount = calculate_tax_by_tax_slab(
+					emp_details["total_taxable_amount"], tax_slab, eval_globals=None, eval_locals=employee_dict
+				)
+			else:
+				tax_amount = 0.0
+
+			if is_tax_rounded:
+				tax_amount = rounded(tax_amount)
+			emp_details["applicable_tax"] = tax_amount
+
+	def get_total_deducted_tax(self):
+		self.add_column("Total Tax Deducted")
+
+		ss = frappe.qb.DocType("Salary Slip")
+		ss_ded = frappe.qb.DocType("Salary Detail")
+
+		records = (
+			frappe.qb.from_(ss)
+			.inner_join(ss_ded)
+			.on(ss.name == ss_ded.parent)
+			.select(ss.employee, Sum(ss_ded.amount).as_("amount"))
+			.where(ss.docstatus == 1)
+			.where(ss.employee.isin(list(self.employees.keys())))
+			.where(ss_ded.parentfield == "deductions")
+			.where(ss_ded.variable_based_on_taxable_salary == 1)
+			.where(ss.start_date >= self.payroll_period_start_date)
+			.where(ss.end_date <= self.payroll_period_end_date)
+			.groupby(ss.employee)
+		).run(as_dict=True)
+
+		for d in records:
+			self.employees[d.employee].setdefault("total_tax_deducted", d.amount)
+
+	def get_payable_tax(self):
+		self.add_column("Payable Tax")
+
+		for emp, emp_details in self.employees.items():
+			emp_details["payable_tax"] = flt(emp_details.get("applicable_tax")) - flt(
+				emp_details.get("total_tax_deducted")
+			)
+
+	def add_column(self, label, fieldname=None, fieldtype=None, options=None, width=None):
+		col = {
+			"label": _(label),
+			"fieldname": fieldname or scrub(label),
+			"fieldtype": fieldtype or "Currency",
+			"options": options,
+			"width": width or "140px",
+		}
+		self.columns.append(col)
+
+	def get_fixed_columns(self):
+		self.columns = [
+			{
+				"label": _("Employee"),
+				"fieldname": "employee",
+				"fieldtype": "Link",
+				"options": "Employee",
+				"width": "140px",
+			},
+			{
+				"label": _("Employee Name"),
+				"fieldname": "employee_name",
+				"fieldtype": "Data",
+				"width": "160px",
+			},
+			{
+				"label": _("Department"),
+				"fieldname": "department",
+				"fieldtype": "Link",
+				"options": "Department",
+				"width": "140px",
+			},
+			{
+				"label": _("Designation"),
+				"fieldname": "designation",
+				"fieldtype": "Link",
+				"options": "Designation",
+				"width": "140px",
+			},
+			{"label": _("Date of Joining"), "fieldname": "date_of_joining", "fieldtype": "Date"},
+			{
+				"label": _("Income Tax Slab"),
+				"fieldname": "income_tax_slab",
+				"fieldtype": "Link",
+				"options": "Income Tax Slab",
+				"width": "140px",
+			},
+			{"label": _("CTC"), "fieldname": "ctc", "fieldtype": "Currency", "width": "140px"},
+		]
diff --git a/erpnext/payroll/report/income_tax_computation/test_income_tax_computation.py b/erpnext/payroll/report/income_tax_computation/test_income_tax_computation.py
new file mode 100644
index 0000000..57ca317
--- /dev/null
+++ b/erpnext/payroll/report/income_tax_computation/test_income_tax_computation.py
@@ -0,0 +1,115 @@
+import unittest
+
+import frappe
+from frappe.utils import getdate
+
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import (
+	create_payroll_period,
+)
+from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
+	create_exemption_declaration,
+	create_salary_slips_for_payroll_period,
+	create_tax_slab,
+)
+from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
+from erpnext.payroll.report.income_tax_computation.income_tax_computation import execute
+
+
+class TestIncomeTaxComputation(unittest.TestCase):
+	def setUp(self):
+		self.cleanup_records()
+		self.create_records()
+
+	def tearDown(self):
+		frappe.db.rollback()
+
+	def cleanup_records(self):
+		frappe.db.sql("delete from `tabEmployee Tax Exemption Declaration`")
+		frappe.db.sql("delete from `tabPayroll Period`")
+		frappe.db.sql("delete from `tabIncome Tax Slab`")
+		frappe.db.sql("delete from `tabSalary Component`")
+		frappe.db.sql("delete from `tabEmployee Benefit Application`")
+		frappe.db.sql("delete from `tabEmployee Benefit Claim`")
+		frappe.db.sql("delete from `tabEmployee` where company='_Test Company'")
+		frappe.db.sql("delete from `tabSalary Slip`")
+
+	def create_records(self):
+		self.employee = make_employee(
+			"employee_tax_computation@example.com",
+			company="_Test Company",
+			date_of_joining=getdate("01-10-2021"),
+		)
+
+		self.payroll_period = create_payroll_period(
+			name="_Test Payroll Period 1", company="_Test Company"
+		)
+
+		self.income_tax_slab = create_tax_slab(
+			self.payroll_period,
+			allow_tax_exemption=True,
+			effective_date=getdate("2019-04-01"),
+			company="_Test Company",
+		)
+		salary_structure = make_salary_structure(
+			"Monthly Salary Structure Test Income Tax Computation",
+			"Monthly",
+			employee=self.employee,
+			company="_Test Company",
+			currency="INR",
+			payroll_period=self.payroll_period,
+			test_tax=True,
+		)
+
+		create_exemption_declaration(self.employee, self.payroll_period.name)
+
+		create_salary_slips_for_payroll_period(
+			self.employee, salary_structure.name, self.payroll_period, deduct_random=False, num=3
+		)
+
+	def test_report(self):
+		filters = frappe._dict(
+			{
+				"company": "_Test Company",
+				"payroll_period": self.payroll_period.name,
+				"employee": self.employee,
+			}
+		)
+
+		result = execute(filters)
+
+		expected_data = {
+			"employee": self.employee,
+			"employee_name": "employee_tax_computation@example.com",
+			"department": "All Departments",
+			"income_tax_slab": self.income_tax_slab,
+			"ctc": 936000.0,
+			"professional_tax": 2400.0,
+			"standard_tax_exemption": 50000,
+			"total_exemption": 52400.0,
+			"total_taxable_amount": 883600.0,
+			"applicable_tax": 92789.0,
+			"total_tax_deducted": 17997.0,
+			"payable_tax": 74792,
+		}
+
+		for key, val in expected_data.items():
+			self.assertEqual(result[1][0].get(key), val)
+
+		# Run report considering tax exemption declaration
+		filters.consider_tax_exemption_declaration = 1
+
+		result = execute(filters)
+
+		expected_data.update(
+			{
+				"_test_category": 100000.0,
+				"total_exemption": 152400.0,
+				"total_taxable_amount": 783600.0,
+				"applicable_tax": 71989.0,
+				"payable_tax": 53992.0,
+			}
+		)
+
+		for key, val in expected_data.items():
+			self.assertEqual(result[1][0].get(key), val)
diff --git a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py
index a2ec36d..c133ab4 100644
--- a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py
+++ b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py
@@ -12,64 +12,53 @@
 
 	return columns, data
 
+
 def get_columns(filters):
-	columns = [
+	return [
 		{
 			"label": _("Employee"),
 			"options": "Employee",
 			"fieldname": "employee",
 			"fieldtype": "Link",
-			"width": 200
+			"width": 200,
 		},
 		{
 			"label": _("Employee Name"),
 			"options": "Employee",
 			"fieldname": "employee_name",
 			"fieldtype": "Link",
-			"width": 160
-		}]
-
-	columns += [{
-			"label": _("Income Tax Component"),
-			"fieldname": "it_comp",
-			"fieldtype": "Data",
-			"width": 170
+			"width": 160,
 		},
+		{"label": _("Income Tax Component"), "fieldname": "it_comp", "fieldtype": "Data", "width": 170},
 		{
 			"label": _("Income Tax Amount"),
 			"fieldname": "it_amount",
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Gross Pay"),
 			"fieldname": "gross_pay",
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 140
+			"width": 140,
 		},
-		{
-			"label": _("Posting Date"),
-			"fieldname": "posting_date",
-			"fieldtype": "Date",
-			"width": 140
-		}
+		{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 140},
 	]
 
-	return columns
 
 def get_conditions(filters):
 	conditions = [""]
 
 	if filters.get("department"):
-		conditions.append("sal.department = '%s' " % (filters["department"]) )
+		conditions.append("sal.department = '%s' " % (filters["department"]))
 
 	if filters.get("branch"):
-		conditions.append("sal.branch = '%s' " % (filters["branch"]) )
+		conditions.append("sal.branch = '%s' " % (filters["branch"]))
 
 	if filters.get("company"):
-		conditions.append("sal.company = '%s' " % (filters["company"]) )
+		conditions.append("sal.company = '%s' " % (filters["company"]))
 
 	if filters.get("month"):
 		conditions.append("month(sal.start_date) = '%s' " % (filters["month"]))
@@ -84,8 +73,10 @@
 
 	data = []
 
-	component_types = frappe.db.sql(""" select name from `tabSalary Component`
-		where is_income_tax_component = 1 """)
+	component_types = frappe.db.sql(
+		""" select name from `tabSalary Component`
+		where is_income_tax_component = 1 """
+	)
 
 	component_types = [comp_type[0] for comp_type in component_types]
 
@@ -94,23 +85,30 @@
 
 	conditions = get_conditions(filters)
 
-	entry = frappe.db.sql(""" select sal.employee, sal.employee_name, sal.posting_date, ded.salary_component, ded.amount,sal.gross_pay
+	entry = frappe.db.sql(
+		""" select sal.employee, sal.employee_name, sal.posting_date, ded.salary_component, ded.amount,sal.gross_pay
 		from `tabSalary Slip` sal, `tabSalary Detail` ded
 		where sal.name = ded.parent
 		and ded.parentfield = 'deductions'
 		and ded.parenttype = 'Salary Slip'
 		and sal.docstatus = 1 %s
 		and ded.salary_component in (%s)
-	""" % (conditions , ", ".join(['%s']*len(component_types))), tuple(component_types), as_dict=1)
+	"""
+		% (conditions, ", ".join(["%s"] * len(component_types))),
+		tuple(component_types),
+		as_dict=1,
+	)
 
 	for d in entry:
-		data.append({
-			"employee": d.employee,
-			"employee_name": d.employee_name,
-			"it_comp": d.salary_component,
-			"posting_date": d.posting_date,
-			"it_amount": d.amount,
-			"gross_pay": d.gross_pay
-		})
+		data.append(
+			{
+				"employee": d.employee,
+				"employee_name": d.employee_name,
+				"it_comp": d.salary_component,
+				"posting_date": d.posting_date,
+				"it_amount": d.amount,
+				"gross_pay": d.gross_pay,
+			}
+		)
 
 	return data
diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py
index fa68575..4223f9d 100644
--- a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py
+++ b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py
@@ -19,42 +19,39 @@
 
 	columns = get_columns(filters, mode_of_payments)
 	data, total_rows, report_summary = get_data(filters, mode_of_payments)
-	chart =  get_chart(mode_of_payments, total_rows)
+	chart = get_chart(mode_of_payments, total_rows)
 
 	return columns, data, None, chart, report_summary
 
+
 def get_columns(filters, mode_of_payments):
-	columns = [{
-		"label": _("Branch"),
-		"options": "Branch",
-		"fieldname": "branch",
-		"fieldtype": "Link",
-		"width": 200
-	}]
+	columns = [
+		{
+			"label": _("Branch"),
+			"options": "Branch",
+			"fieldname": "branch",
+			"fieldtype": "Link",
+			"width": 200,
+		}
+	]
 
 	for mode in mode_of_payments:
-		columns.append({
-			"label": _(mode),
-			"fieldname": mode,
-			"fieldtype": "Currency",
-			"width": 160
-		})
+		columns.append({"label": _(mode), "fieldname": mode, "fieldtype": "Currency", "width": 160})
 
-	columns.append({
-		"label": _("Total"),
-		"fieldname": "total",
-		"fieldtype": "Currency",
-		"width": 140
-	})
+	columns.append({"label": _("Total"), "fieldname": "total", "fieldtype": "Currency", "width": 140})
 
 	return columns
 
+
 def get_payment_modes():
-	mode_of_payments = frappe.db.sql_list("""
+	mode_of_payments = frappe.db.sql_list(
+		"""
 		select distinct mode_of_payment from `tabSalary Slip` where docstatus = 1
-	""")
+	"""
+	)
 	return mode_of_payments
 
+
 def prepare_data(entry):
 	branch_wise_entries = {}
 	gross_pay = 0
@@ -68,36 +65,42 @@
 
 	return branch_wise_entries, gross_pay
 
+
 def get_data(filters, mode_of_payments):
 	data = []
 
 	conditions = get_conditions(filters)
 
-	entry = frappe.db.sql("""
+	entry = frappe.db.sql(
+		"""
 		select branch, mode_of_payment, sum(net_pay) as net_pay, sum(gross_pay) as gross_pay
 		from `tabSalary Slip` sal
 		where docstatus = 1 %s
 		group by branch, mode_of_payment
-		""" % (conditions), as_dict=1)
+		"""
+		% (conditions),
+		as_dict=1,
+	)
 
 	branch_wise_entries, gross_pay = prepare_data(entry)
 
-	branches = frappe.db.sql_list("""
+	branches = frappe.db.sql_list(
+		"""
 		select distinct branch from `tabSalary Slip` sal
 		where docstatus = 1 %s
-	""" % (conditions))
+	"""
+		% (conditions)
+	)
 
 	total_row = {"total": 0, "branch": "Total"}
 
 	for branch in branches:
 		total = 0
-		row = {
-			"branch": branch
-		}
+		row = {"branch": branch}
 		for mode in mode_of_payments:
 			if branch_wise_entries.get(branch).get(mode):
 				row[mode] = branch_wise_entries.get(branch).get(mode)
-				total +=  branch_wise_entries.get(branch).get(mode)
+				total += branch_wise_entries.get(branch).get(mode)
 
 		row["total"] = total
 		data.append(row)
@@ -110,24 +113,18 @@
 	if data:
 		data.append(total_row)
 		data.append({})
-		data.append({
-			"branch": "<b>Total Gross Pay</b>",
-			mode_of_payments[0]:gross_pay
-		})
-		data.append({
-			"branch": "<b>Total Deductions</b>",
-			mode_of_payments[0]:total_deductions
-		})
-		data.append({
-			"branch": "<b>Total Net Pay</b>",
-			mode_of_payments[0]:total_row.get("total")
-		})
+		data.append({"branch": "<b>Total Gross Pay</b>", mode_of_payments[0]: gross_pay})
+		data.append({"branch": "<b>Total Deductions</b>", mode_of_payments[0]: total_deductions})
+		data.append({"branch": "<b>Total Net Pay</b>", mode_of_payments[0]: total_row.get("total")})
 
 		currency = erpnext.get_company_currency(filters.company)
-		report_summary = get_report_summary(gross_pay, total_deductions, total_row.get("total"), currency)
+		report_summary = get_report_summary(
+			gross_pay, total_deductions, total_row.get("total"), currency
+		)
 
 	return data, total_row, report_summary
 
+
 def get_total_based_on_mode_of_payment(data, mode_of_payments):
 
 	total = 0
@@ -140,31 +137,33 @@
 	total_row["total"] = total
 	return total_row
 
+
 def get_report_summary(gross_pay, total_deductions, net_pay, currency):
 	return [
 		{
 			"value": gross_pay,
-			"label": "Total Gross Pay",
+			"label": _("Total Gross Pay"),
 			"indicator": "Green",
 			"datatype": "Currency",
-			"currency": currency
+			"currency": currency,
 		},
 		{
 			"value": total_deductions,
-			"label": "Total Deduction",
+			"label": _("Total Deduction"),
 			"datatype": "Currency",
 			"indicator": "Red",
-			"currency": currency
+			"currency": currency,
 		},
 		{
 			"value": net_pay,
-			"label": "Total Net Pay",
+			"label": _("Total Net Pay"),
 			"datatype": "Currency",
 			"indicator": "Blue",
-			"currency": currency
-		}
+			"currency": currency,
+		},
 	]
 
+
 def get_chart(mode_of_payments, data):
 	if data:
 		values = []
@@ -175,10 +174,7 @@
 			labels.append([mode])
 
 		chart = {
-			"data": {
-				"labels": labels,
-				"datasets": [{'name': 'Mode Of Payments', "values": values}]
-			}
+			"data": {"labels": labels, "datasets": [{"name": "Mode Of Payments", "values": values}]}
 		}
-		chart['type'] = "bar"
+		chart["type"] = "bar"
 		return chart
diff --git a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py
index 578c816..4f9370b 100644
--- a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py
+++ b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py
@@ -14,6 +14,7 @@
 
 	return columns, data
 
+
 def get_columns(filters):
 	columns = [
 		{
@@ -21,71 +22,52 @@
 			"options": "Branch",
 			"fieldname": "branch",
 			"fieldtype": "Link",
-			"width": 200
+			"width": 200,
 		},
 		{
 			"label": _("Employee Name"),
 			"options": "Employee",
 			"fieldname": "employee_name",
 			"fieldtype": "Link",
-			"width": 160
+			"width": 160,
 		},
 		{
 			"label": _("Employee"),
-			"options":"Employee",
+			"options": "Employee",
 			"fieldname": "employee",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Gross Pay"),
 			"fieldname": "gross_pay",
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 140
+			"width": 140,
 		},
-		{
-			"label": _("Bank"),
-			"fieldname": "bank",
-			"fieldtype": "Data",
-			"width": 140
-		},
-		{
-			"label": _("Account No"),
-			"fieldname": "account_no",
-			"fieldtype": "Data",
-			"width": 140
-		},
+		{"label": _("Bank"), "fieldname": "bank", "fieldtype": "Data", "width": 140},
+		{"label": _("Account No"), "fieldname": "account_no", "fieldtype": "Data", "width": 140},
 	]
 	if erpnext.get_region() == "India":
 		columns += [
-			{
-				"label": _("IFSC"),
-				"fieldname": "ifsc",
-				"fieldtype": "Data",
-				"width": 140
-			},
-			{
-				"label": _("MICR"),
-				"fieldname": "micr",
-				"fieldtype": "Data",
-				"width": 140
-			}
+			{"label": _("IFSC"), "fieldname": "ifsc", "fieldtype": "Data", "width": 140},
+			{"label": _("MICR"), "fieldname": "micr", "fieldtype": "Data", "width": 140},
 		]
 
 	return columns
 
+
 def get_conditions(filters):
 	conditions = [""]
 
 	if filters.get("department"):
-		conditions.append("department = '%s' " % (filters["department"]) )
+		conditions.append("department = '%s' " % (filters["department"]))
 
 	if filters.get("branch"):
-		conditions.append("branch = '%s' " % (filters["branch"]) )
+		conditions.append("branch = '%s' " % (filters["branch"]))
 
 	if filters.get("company"):
-		conditions.append("company = '%s' " % (filters["company"]) )
+		conditions.append("company = '%s' " % (filters["company"]))
 
 	if filters.get("month"):
 		conditions.append("month(start_date) = '%s' " % (filters["month"]))
@@ -95,6 +77,7 @@
 
 	return " and ".join(conditions)
 
+
 def get_data(filters):
 
 	data = []
@@ -103,36 +86,39 @@
 	if erpnext.get_region() == "India":
 		fields += ["ifsc_code", "micr_code"]
 
-
-	employee_details = frappe.get_list("Employee", fields = fields)
+	employee_details = frappe.get_list("Employee", fields=fields)
 	employee_data_dict = {}
 
 	for d in employee_details:
 		employee_data_dict.setdefault(
-			d.employee,{
-				"bank_ac_no" : d.bank_ac_no,
-				"ifsc_code" : d.ifsc_code or None,
-				"micr_code" : d.micr_code or  None,
-				"branch" : d.branch,
-				"salary_mode" : d.salary_mode,
-				"bank_name": d.bank_name
-			}
+			d.employee,
+			{
+				"bank_ac_no": d.bank_ac_no,
+				"ifsc_code": d.ifsc_code or None,
+				"micr_code": d.micr_code or None,
+				"branch": d.branch,
+				"salary_mode": d.salary_mode,
+				"bank_name": d.bank_name,
+			},
 		)
 
 	conditions = get_conditions(filters)
 
-	entry = frappe.db.sql(""" select employee, employee_name, gross_pay
+	entry = frappe.db.sql(
+		""" select employee, employee_name, gross_pay
 		from `tabSalary Slip`
 		where docstatus = 1 %s """
-		%(conditions), as_dict =1)
+		% (conditions),
+		as_dict=1,
+	)
 
 	for d in entry:
 
 		employee = {
-			"branch" : employee_data_dict.get(d.employee).get("branch"),
-			"employee_name" : d.employee_name,
-			"employee" : d.employee,
-			"gross_pay" : d.gross_pay,
+			"branch": employee_data_dict.get(d.employee).get("branch"),
+			"employee_name": d.employee_name,
+			"employee": d.employee,
+			"gross_pay": d.gross_pay,
 		}
 
 		if employee_data_dict.get(d.employee).get("salary_mode") == "Bank":
@@ -144,7 +130,9 @@
 		else:
 			employee["account_no"] = employee_data_dict.get(d.employee).get("salary_mode")
 
-		if filters.get("type") and employee_data_dict.get(d.employee).get("salary_mode") == filters.get("type"):
+		if filters.get("type") and employee_data_dict.get(d.employee).get("salary_mode") == filters.get(
+			"type"
+		):
 			data.append(employee)
 		elif not filters.get("type"):
 			data.append(employee)
diff --git a/erpnext/payroll/report/salary_register/salary_register.py b/erpnext/payroll/report/salary_register/salary_register.py
index 78deb22..0a62b43 100644
--- a/erpnext/payroll/report/salary_register/salary_register.py
+++ b/erpnext/payroll/report/salary_register/salary_register.py
@@ -10,29 +10,46 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 	currency = None
-	if filters.get('currency'):
-		currency = filters.get('currency')
+	if filters.get("currency"):
+		currency = filters.get("currency")
 	company_currency = erpnext.get_company_currency(filters.get("company"))
 	salary_slips = get_salary_slips(filters, company_currency)
-	if not salary_slips: return [], []
+	if not salary_slips:
+		return [], []
 
 	columns, earning_types, ded_types = get_columns(salary_slips)
 	ss_earning_map = get_ss_earning_map(salary_slips, currency, company_currency)
-	ss_ded_map = get_ss_ded_map(salary_slips,currency, company_currency)
+	ss_ded_map = get_ss_ded_map(salary_slips, currency, company_currency)
 	doj_map = get_employee_doj_map()
 
 	data = []
 	for ss in salary_slips:
-		row = [ss.name, ss.employee, ss.employee_name, doj_map.get(ss.employee), ss.branch, ss.department, ss.designation,
-			ss.company, ss.start_date, ss.end_date, ss.leave_without_pay, ss.payment_days]
+		row = [
+			ss.name,
+			ss.employee,
+			ss.employee_name,
+			doj_map.get(ss.employee),
+			ss.branch,
+			ss.department,
+			ss.designation,
+			ss.company,
+			ss.start_date,
+			ss.end_date,
+			ss.leave_without_pay,
+			ss.payment_days,
+		]
 
-		if ss.branch is not None: columns[3] = columns[3].replace('-1','120')
-		if ss.department is not None: columns[4] = columns[4].replace('-1','120')
-		if ss.designation is not None: columns[5] = columns[5].replace('-1','120')
-		if ss.leave_without_pay is not None: columns[9] = columns[9].replace('-1','130')
-
+		if ss.branch is not None:
+			columns[3] = columns[3].replace("-1", "120")
+		if ss.department is not None:
+			columns[4] = columns[4].replace("-1", "120")
+		if ss.designation is not None:
+			columns[5] = columns[5].replace("-1", "120")
+		if ss.leave_without_pay is not None:
+			columns[9] = columns[9].replace("-1", "130")
 
 		for e in earning_types:
 			row.append(ss_earning_map.get(ss.name, {}).get(e))
@@ -48,7 +65,10 @@
 		row.append(ss.total_loan_repayment)
 
 		if currency == company_currency:
-			row += [flt(ss.total_deduction) * flt(ss.exchange_rate), flt(ss.net_pay) * flt(ss.exchange_rate)]
+			row += [
+				flt(ss.total_deduction) * flt(ss.exchange_rate),
+				flt(ss.net_pay) * flt(ss.exchange_rate),
+			]
 		else:
 			row += [ss.total_deduction, ss.net_pay]
 		row.append(currency or company_currency)
@@ -56,53 +76,81 @@
 
 	return columns, data
 
+
 def get_columns(salary_slips):
 	"""
 	columns = [
+	        _("Salary Slip ID") + ":Link/Salary Slip:150",
+	        _("Employee") + ":Link/Employee:120",
+	        _("Employee Name") + "::140",
+	        _("Date of Joining") + "::80",
+	        _("Branch") + ":Link/Branch:120",
+	        _("Department") + ":Link/Department:120",
+	        _("Designation") + ":Link/Designation:120",
+	        _("Company") + ":Link/Company:120",
+	        _("Start Date") + "::80",
+	        _("End Date") + "::80",
+	        _("Leave Without Pay") + ":Float:130",
+	        _("Payment Days") + ":Float:120",
+	        _("Currency") + ":Link/Currency:80"
+	]
+	"""
+	columns = [
 		_("Salary Slip ID") + ":Link/Salary Slip:150",
 		_("Employee") + ":Link/Employee:120",
 		_("Employee Name") + "::140",
 		_("Date of Joining") + "::80",
-		_("Branch") + ":Link/Branch:120",
-		_("Department") + ":Link/Department:120",
+		_("Branch") + ":Link/Branch:-1",
+		_("Department") + ":Link/Department:-1",
 		_("Designation") + ":Link/Designation:120",
 		_("Company") + ":Link/Company:120",
 		_("Start Date") + "::80",
 		_("End Date") + "::80",
-		_("Leave Without Pay") + ":Float:130",
+		_("Leave Without Pay") + ":Float:50",
 		_("Payment Days") + ":Float:120",
-		_("Currency") + ":Link/Currency:80"
-	]
-	"""
-	columns = [
-		_("Salary Slip ID") + ":Link/Salary Slip:150",_("Employee") + ":Link/Employee:120", _("Employee Name") + "::140",
-		_("Date of Joining") + "::80", _("Branch") + ":Link/Branch:-1", _("Department") + ":Link/Department:-1",
-		_("Designation") + ":Link/Designation:120", _("Company") + ":Link/Company:120", _("Start Date") + "::80",
-		_("End Date") + "::80", _("Leave Without Pay") + ":Float:50", _("Payment Days") + ":Float:120"
 	]
 
 	salary_components = {_("Earning"): [], _("Deduction"): []}
 
-	for component in frappe.db.sql("""select distinct sd.salary_component, sc.type
+	for component in frappe.db.sql(
+		"""select distinct sd.salary_component, sc.type
 		from `tabSalary Detail` sd, `tabSalary Component` sc
-		where sc.name=sd.salary_component and sd.amount != 0 and sd.parent in (%s)""" %
-		(', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=1):
+		where sc.name=sd.salary_component and sd.amount != 0 and sd.parent in (%s)"""
+		% (", ".join(["%s"] * len(salary_slips))),
+		tuple([d.name for d in salary_slips]),
+		as_dict=1,
+	):
 		salary_components[_(component.type)].append(component.salary_component)
 
-	columns = columns + [(e + ":Currency:120") for e in salary_components[_("Earning")]] + \
-		[_("Gross Pay") + ":Currency:120"] + [(d + ":Currency:120") for d in salary_components[_("Deduction")]] + \
-		[_("Loan Repayment") + ":Currency:120", _("Total Deduction") + ":Currency:120", _("Net Pay") + ":Currency:120"]
+	columns = (
+		columns
+		+ [(e + ":Currency:120") for e in salary_components[_("Earning")]]
+		+ [_("Gross Pay") + ":Currency:120"]
+		+ [(d + ":Currency:120") for d in salary_components[_("Deduction")]]
+		+ [
+			_("Loan Repayment") + ":Currency:120",
+			_("Total Deduction") + ":Currency:120",
+			_("Net Pay") + ":Currency:120",
+		]
+	)
 
 	return columns, salary_components[_("Earning")], salary_components[_("Deduction")]
 
+
 def get_salary_slips(filters, company_currency):
-	filters.update({"from_date": filters.get("from_date"), "to_date":filters.get("to_date")})
+	filters.update({"from_date": filters.get("from_date"), "to_date": filters.get("to_date")})
 	conditions, filters = get_conditions(filters, company_currency)
-	salary_slips = frappe.db.sql("""select * from `tabSalary Slip` where %s
-		order by employee""" % conditions, filters, as_dict=1)
+	salary_slips = frappe.db.sql(
+		"""select * from `tabSalary Slip` where %s
+		order by employee"""
+		% conditions,
+		filters,
+		as_dict=1,
+	)
 
 	return salary_slips or []
 
+
 def get_conditions(filters, company_currency):
 	conditions = ""
 	doc_status = {"Draft": 0, "Submitted": 1, "Cancelled": 2}
@@ -110,48 +158,71 @@
 	if filters.get("docstatus"):
 		conditions += "docstatus = {0}".format(doc_status[filters.get("docstatus")])
 
-	if filters.get("from_date"): conditions += " and start_date >= %(from_date)s"
-	if filters.get("to_date"): conditions += " and end_date <= %(to_date)s"
-	if filters.get("company"): conditions += " and company = %(company)s"
-	if filters.get("employee"): conditions += " and employee = %(employee)s"
+	if filters.get("from_date"):
+		conditions += " and start_date >= %(from_date)s"
+	if filters.get("to_date"):
+		conditions += " and end_date <= %(to_date)s"
+	if filters.get("company"):
+		conditions += " and company = %(company)s"
+	if filters.get("employee"):
+		conditions += " and employee = %(employee)s"
 	if filters.get("currency") and filters.get("currency") != company_currency:
 		conditions += " and currency = %(currency)s"
 
 	return conditions, filters
 
+
 def get_employee_doj_map():
-	return	frappe._dict(frappe.db.sql("""
+	return frappe._dict(
+		frappe.db.sql(
+			"""
 				SELECT
 					employee,
 					date_of_joining
 				FROM `tabEmployee`
-				"""))
+				"""
+		)
+	)
+
 
 def get_ss_earning_map(salary_slips, currency, company_currency):
-	ss_earnings = frappe.db.sql("""select sd.parent, sd.salary_component, sd.amount, ss.exchange_rate, ss.name
-		from `tabSalary Detail` sd, `tabSalary Slip` ss where sd.parent=ss.name and sd.parent in (%s)""" %
-		(', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=1)
+	ss_earnings = frappe.db.sql(
+		"""select sd.parent, sd.salary_component, sd.amount, ss.exchange_rate, ss.name
+		from `tabSalary Detail` sd, `tabSalary Slip` ss where sd.parent=ss.name and sd.parent in (%s)"""
+		% (", ".join(["%s"] * len(salary_slips))),
+		tuple([d.name for d in salary_slips]),
+		as_dict=1,
+	)
 
 	ss_earning_map = {}
 	for d in ss_earnings:
 		ss_earning_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, 0.0)
 		if currency == company_currency:
-			ss_earning_map[d.parent][d.salary_component] += flt(d.amount) * flt(d.exchange_rate if d.exchange_rate else 1)
+			ss_earning_map[d.parent][d.salary_component] += flt(d.amount) * flt(
+				d.exchange_rate if d.exchange_rate else 1
+			)
 		else:
 			ss_earning_map[d.parent][d.salary_component] += flt(d.amount)
 
 	return ss_earning_map
 
+
 def get_ss_ded_map(salary_slips, currency, company_currency):
-	ss_deductions = frappe.db.sql("""select sd.parent, sd.salary_component, sd.amount, ss.exchange_rate, ss.name
-		from `tabSalary Detail` sd, `tabSalary Slip` ss where sd.parent=ss.name and sd.parent in (%s)""" %
-		(', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=1)
+	ss_deductions = frappe.db.sql(
+		"""select sd.parent, sd.salary_component, sd.amount, ss.exchange_rate, ss.name
+		from `tabSalary Detail` sd, `tabSalary Slip` ss where sd.parent=ss.name and sd.parent in (%s)"""
+		% (", ".join(["%s"] * len(salary_slips))),
+		tuple([d.name for d in salary_slips]),
+		as_dict=1,
+	)
 
 	ss_ded_map = {}
 	for d in ss_deductions:
 		ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, 0.0)
 		if currency == company_currency:
-			ss_ded_map[d.parent][d.salary_component] += flt(d.amount) * flt(d.exchange_rate if d.exchange_rate else 1)
+			ss_ded_map[d.parent][d.salary_component] += flt(d.amount) * flt(
+				d.exchange_rate if d.exchange_rate else 1
+			)
 		else:
 			ss_ded_map[d.parent][d.salary_component] += flt(d.amount)
 
diff --git a/erpnext/payroll/workspace/payroll/payroll.json b/erpnext/payroll/workspace/payroll/payroll.json
index 762bea0..5629e63 100644
--- a/erpnext/payroll/workspace/payroll/payroll.json
+++ b/erpnext/payroll/workspace/payroll/payroll.json
@@ -246,6 +246,17 @@
    "type": "Link"
   },
   {
+    "dependencies": "Salary Structure",
+    "hidden": 0,
+    "is_query_report": 1,
+    "label": "Income Tax Computation",
+    "link_count": 0,
+    "link_to": "Income Tax Computation",
+    "link_type": "Report",
+    "onboard": 0,
+    "type": "Link"
+   },
+  {
    "dependencies": "Salary Slip",
    "hidden": 0,
    "is_query_report": 1,
@@ -312,7 +323,7 @@
    "type": "Link"
   }
  ],
- "modified": "2022-01-13 17:41:19.098813",
+ "modified": "2022-02-23 17:41:19.098813",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Payroll",
diff --git a/erpnext/portal/doctype/homepage/homepage.py b/erpnext/portal/doctype/homepage/homepage.py
index 8092ba2..5bb05f0 100644
--- a/erpnext/portal/doctype/homepage/homepage.py
+++ b/erpnext/portal/doctype/homepage/homepage.py
@@ -11,17 +11,27 @@
 	def validate(self):
 		if not self.description:
 			self.description = frappe._("This is an example website auto-generated from ERPNext")
-		delete_page_cache('home')
+		delete_page_cache("home")
 
 	def setup_items(self):
-		for d in frappe.get_all('Website Item', fields=['name', 'item_name', 'description', 'image', 'route'],
-			filters={'published': 1}, limit=3):
+		for d in frappe.get_all(
+			"Website Item",
+			fields=["name", "item_name", "description", "image", "route"],
+			filters={"published": 1},
+			limit=3,
+		):
 
-			doc = frappe.get_doc('Website Item', d.name)
+			doc = frappe.get_doc("Website Item", d.name)
 			if not doc.route:
 				# set missing route
 				doc.save()
-			self.append('products', dict(item_code=d.name,
-				item_name=d.item_name, description=d.description,
-				image=d.image, route=d.route))
-
+			self.append(
+				"products",
+				dict(
+					item_code=d.name,
+					item_name=d.item_name,
+					description=d.description,
+					image=d.image,
+					route=d.route,
+				),
+			)
diff --git a/erpnext/portal/doctype/homepage/test_homepage.py b/erpnext/portal/doctype/homepage/test_homepage.py
index fb0367f..e153223 100644
--- a/erpnext/portal/doctype/homepage/test_homepage.py
+++ b/erpnext/portal/doctype/homepage/test_homepage.py
@@ -10,7 +10,7 @@
 
 class TestHomepage(unittest.TestCase):
 	def test_homepage_load(self):
-		set_request(method='GET', path='home')
+		set_request(method="GET", path="home")
 		response = get_response()
 
 		self.assertEqual(response.status_code, 200)
diff --git a/erpnext/portal/doctype/homepage_section/test_homepage_section.py b/erpnext/portal/doctype/homepage_section/test_homepage_section.py
index c3be146..27b0169 100644
--- a/erpnext/portal/doctype/homepage_section/test_homepage_section.py
+++ b/erpnext/portal/doctype/homepage_section/test_homepage_section.py
@@ -12,65 +12,79 @@
 class TestHomepageSection(unittest.TestCase):
 	def test_homepage_section_card(self):
 		try:
-			frappe.get_doc({
-				'doctype': 'Homepage Section',
-				'name': 'Card Section',
-				'section_based_on': 'Cards',
-				'section_cards': [
-					{'title': 'Card 1', 'subtitle': 'Subtitle 1', 'content': 'This is test card 1', 'route': '/card-1'},
-					{'title': 'Card 2', 'subtitle': 'Subtitle 2', 'content': 'This is test card 2', 'image': 'test.jpg'},
-				],
-				'no_of_columns': 3
-			}).insert(ignore_if_duplicate=True)
+			frappe.get_doc(
+				{
+					"doctype": "Homepage Section",
+					"name": "Card Section",
+					"section_based_on": "Cards",
+					"section_cards": [
+						{
+							"title": "Card 1",
+							"subtitle": "Subtitle 1",
+							"content": "This is test card 1",
+							"route": "/card-1",
+						},
+						{
+							"title": "Card 2",
+							"subtitle": "Subtitle 2",
+							"content": "This is test card 2",
+							"image": "test.jpg",
+						},
+					],
+					"no_of_columns": 3,
+				}
+			).insert(ignore_if_duplicate=True)
 		except frappe.DuplicateEntryError:
 			pass
 
-		set_request(method='GET', path='home')
+		set_request(method="GET", path="home")
 		response = get_response()
 
 		self.assertEqual(response.status_code, 200)
 
 		html = frappe.safe_decode(response.get_data())
 
-		soup = BeautifulSoup(html, 'html.parser')
-		sections = soup.find('main').find_all('section')
+		soup = BeautifulSoup(html, "html.parser")
+		sections = soup.find("main").find_all("section")
 		self.assertEqual(len(sections), 3)
 
 		homepage_section = sections[2]
-		self.assertEqual(homepage_section.h3.text, 'Card Section')
+		self.assertEqual(homepage_section.h3.text, "Card Section")
 
 		cards = homepage_section.find_all(class_="card")
 
 		self.assertEqual(len(cards), 2)
-		self.assertEqual(cards[0].h5.text, 'Card 1')
-		self.assertEqual(cards[0].a['href'], '/card-1')
-		self.assertEqual(cards[1].p.text, 'Subtitle 2')
-		self.assertEqual(cards[1].find(class_='website-image-lazy')['data-src'], 'test.jpg')
+		self.assertEqual(cards[0].h5.text, "Card 1")
+		self.assertEqual(cards[0].a["href"], "/card-1")
+		self.assertEqual(cards[1].p.text, "Subtitle 2")
+		self.assertEqual(cards[1].find(class_="website-image-lazy")["data-src"], "test.jpg")
 
 		# cleanup
 		frappe.db.rollback()
 
 	def test_homepage_section_custom_html(self):
-		frappe.get_doc({
-			'doctype': 'Homepage Section',
-			'name': 'Custom HTML Section',
-			'section_based_on': 'Custom HTML',
-			'section_html': '<div class="custom-section">My custom html</div>',
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Homepage Section",
+				"name": "Custom HTML Section",
+				"section_based_on": "Custom HTML",
+				"section_html": '<div class="custom-section">My custom html</div>',
+			}
+		).insert()
 
-		set_request(method='GET', path='home')
+		set_request(method="GET", path="home")
 		response = get_response()
 
 		self.assertEqual(response.status_code, 200)
 
 		html = frappe.safe_decode(response.get_data())
 
-		soup = BeautifulSoup(html, 'html.parser')
-		sections = soup.find('main').find_all(class_='custom-section')
+		soup = BeautifulSoup(html, "html.parser")
+		sections = soup.find("main").find_all(class_="custom-section")
 		self.assertEqual(len(sections), 1)
 
 		homepage_section = sections[0]
-		self.assertEqual(homepage_section.text, 'My custom html')
+		self.assertEqual(homepage_section.text, "My custom html")
 
 		# cleanup
 		frappe.db.rollback()
diff --git a/erpnext/portal/doctype/website_filter_field/website_filter_field.json b/erpnext/portal/doctype/website_filter_field/website_filter_field.json
index 67c0d0a..45543a6 100644
--- a/erpnext/portal/doctype/website_filter_field/website_filter_field.json
+++ b/erpnext/portal/doctype/website_filter_field/website_filter_field.json
@@ -1,76 +1,31 @@
 {
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2018-12-31 17:06:08.716134", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "actions": [],
+ "creation": "2018-12-31 17:06:08.716134",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "fieldname"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "fieldname", 
-   "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": "Fieldname", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "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
+   "fieldname": "fieldname",
+   "fieldtype": "Autocomplete",
+   "in_list_view": 1,
+   "label": "Fieldname"
   }
- ], 
- "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": "2019-01-01 18:26:11.550380", 
- "modified_by": "Administrator", 
- "module": "Portal", 
- "name": "Website Filter Field", 
- "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
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2022-04-18 18:55:17.835666",
+ "modified_by": "Administrator",
+ "module": "Portal",
+ "name": "Website Filter Field",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": [],
+ "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/portal/utils.py b/erpnext/portal/utils.py
index 24bcab4..7be8c5d 100644
--- a/erpnext/portal/utils.py
+++ b/erpnext/portal/utils.py
@@ -8,40 +8,37 @@
 
 
 def set_default_role(doc, method):
-	'''Set customer, supplier, student, guardian based on email'''
+	"""Set customer, supplier, student, guardian based on email"""
 	if frappe.flags.setting_role or frappe.flags.in_migrate:
 		return
 
 	roles = frappe.get_roles(doc.name)
 
-	contact_name = frappe.get_value('Contact', dict(email_id=doc.email))
+	contact_name = frappe.get_value("Contact", dict(email_id=doc.email))
 	if contact_name:
-		contact = frappe.get_doc('Contact', contact_name)
+		contact = frappe.get_doc("Contact", contact_name)
 		for link in contact.links:
 			frappe.flags.setting_role = True
-			if link.link_doctype=='Customer' and 'Customer' not in roles:
-				doc.add_roles('Customer')
-			elif link.link_doctype=='Supplier' and 'Supplier' not in roles:
-				doc.add_roles('Supplier')
-	elif frappe.get_value('Student', dict(student_email_id=doc.email)) and 'Student' not in roles:
-		doc.add_roles('Student')
-	elif frappe.get_value('Guardian', dict(email_address=doc.email)) and 'Guardian' not in roles:
-		doc.add_roles('Guardian')
+			if link.link_doctype == "Customer" and "Customer" not in roles:
+				doc.add_roles("Customer")
+			elif link.link_doctype == "Supplier" and "Supplier" not in roles:
+				doc.add_roles("Supplier")
+
 
 def create_customer_or_supplier():
-	'''Based on the default Role (Customer, Supplier), create a Customer / Supplier.
+	"""Based on the default Role (Customer, Supplier), create a Customer / Supplier.
 	Called on_session_creation hook.
-	'''
+	"""
 	user = frappe.session.user
 
-	if frappe.db.get_value('User', user, 'user_type') != 'Website User':
+	if frappe.db.get_value("User", user, "user_type") != "Website User":
 		return
 
 	user_roles = frappe.get_roles()
-	portal_settings = frappe.get_single('Portal Settings')
+	portal_settings = frappe.get_single("Portal Settings")
 	default_role = portal_settings.default_role
 
-	if default_role not in ['Customer', 'Supplier']:
+	if default_role not in ["Customer", "Supplier"]:
 		return
 
 	# create customer / supplier if the user has that role
@@ -59,34 +56,33 @@
 	party = frappe.new_doc(doctype)
 	fullname = frappe.utils.get_fullname(user)
 
-	if doctype == 'Customer':
+	if doctype == "Customer":
 		cart_settings = get_shopping_cart_settings()
 
 		if cart_settings.enable_checkout:
 			debtors_account = get_debtors_account(cart_settings)
 		else:
-			debtors_account = ''
+			debtors_account = ""
 
-		party.update({
-			"customer_name": fullname,
-			"customer_type": "Individual",
-			"customer_group": cart_settings.default_customer_group,
-			"territory": get_root_of("Territory")
-		})
+		party.update(
+			{
+				"customer_name": fullname,
+				"customer_type": "Individual",
+				"customer_group": cart_settings.default_customer_group,
+				"territory": get_root_of("Territory"),
+			}
+		)
 
 		if debtors_account:
-			party.update({
-				"accounts": [{
-					"company": cart_settings.company,
-					"account": debtors_account
-				}]
-			})
+			party.update({"accounts": [{"company": cart_settings.company, "account": debtors_account}]})
 	else:
-		party.update({
-			"supplier_name": fullname,
-			"supplier_group": "All Supplier Groups",
-			"supplier_type": "Individual"
-		})
+		party.update(
+			{
+				"supplier_name": fullname,
+				"supplier_group": "All Supplier Groups",
+				"supplier_type": "Individual",
+			}
+		)
 
 	party.flags.ignore_mandatory = True
 	party.insert(ignore_permissions=True)
@@ -95,28 +91,27 @@
 
 	if party_exists(alternate_doctype, user):
 		# if user is both customer and supplier, alter fullname to avoid contact name duplication
-		fullname +=  "-" + doctype
+		fullname += "-" + doctype
 
 	create_party_contact(doctype, fullname, user, party.name)
 
 	return party
 
+
 def create_party_contact(doctype, fullname, user, party_name):
 	contact = frappe.new_doc("Contact")
-	contact.update({
-		"first_name": fullname,
-		"email_id": user
-	})
-	contact.append('links', dict(link_doctype=doctype, link_name=party_name))
-	contact.append('email_ids', dict(email_id=user))
+	contact.update({"first_name": fullname, "email_id": user})
+	contact.append("links", dict(link_doctype=doctype, link_name=party_name))
+	contact.append("email_ids", dict(email_id=user))
 	contact.flags.ignore_mandatory = True
 	contact.insert(ignore_permissions=True)
 
+
 def party_exists(doctype, user):
 	# check if contact exists against party and if it is linked to the doctype
 	contact_name = frappe.db.get_value("Contact", {"email_id": user})
 	if contact_name:
-		contact = frappe.get_doc('Contact', contact_name)
+		contact = frappe.get_doc("Contact", contact_name)
 		doctypes = [d.link_doctype for d in contact.links]
 		return doctype in doctypes
 
diff --git a/erpnext/projects/doctype/activity_cost/activity_cost.py b/erpnext/projects/doctype/activity_cost/activity_cost.py
index bc4bb9d..b99aa1e 100644
--- a/erpnext/projects/doctype/activity_cost/activity_cost.py
+++ b/erpnext/projects/doctype/activity_cost/activity_cost.py
@@ -7,7 +7,9 @@
 from frappe.model.document import Document
 
 
-class DuplicationError(frappe.ValidationError): pass
+class DuplicationError(frappe.ValidationError):
+	pass
+
 
 class ActivityCost(Document):
 	def validate(self):
@@ -24,12 +26,22 @@
 
 	def check_unique(self):
 		if self.employee:
-			if frappe.db.sql("""select name from `tabActivity Cost` where employee_name= %s and activity_type= %s and name != %s""",
-				(self.employee_name, self.activity_type, self.name)):
-					frappe.throw(_("Activity Cost exists for Employee {0} against Activity Type - {1}")
-						.format(self.employee, self.activity_type), DuplicationError)
+			if frappe.db.sql(
+				"""select name from `tabActivity Cost` where employee_name= %s and activity_type= %s and name != %s""",
+				(self.employee_name, self.activity_type, self.name),
+			):
+				frappe.throw(
+					_("Activity Cost exists for Employee {0} against Activity Type - {1}").format(
+						self.employee, self.activity_type
+					),
+					DuplicationError,
+				)
 		else:
-			if frappe.db.sql("""select name from `tabActivity Cost` where ifnull(employee, '')='' and activity_type= %s and name != %s""",
-				(self.activity_type, self.name)):
-					frappe.throw(_("Default Activity Cost exists for Activity Type - {0}")
-						.format(self.activity_type), DuplicationError)
+			if frappe.db.sql(
+				"""select name from `tabActivity Cost` where ifnull(employee, '')='' and activity_type= %s and name != %s""",
+				(self.activity_type, self.name),
+			):
+				frappe.throw(
+					_("Default Activity Cost exists for Activity Type - {0}").format(self.activity_type),
+					DuplicationError,
+				)
diff --git a/erpnext/projects/doctype/activity_cost/test_activity_cost.py b/erpnext/projects/doctype/activity_cost/test_activity_cost.py
index d53e582..8da797e 100644
--- a/erpnext/projects/doctype/activity_cost/test_activity_cost.py
+++ b/erpnext/projects/doctype/activity_cost/test_activity_cost.py
@@ -11,15 +11,17 @@
 class TestActivityCost(unittest.TestCase):
 	def test_duplication(self):
 		frappe.db.sql("delete from `tabActivity Cost`")
-		activity_cost1 = frappe.new_doc('Activity Cost')
-		activity_cost1.update({
-			"employee": "_T-Employee-00001",
-			"employee_name": "_Test Employee",
-			"activity_type": "_Test Activity Type 1",
-			"billing_rate": 100,
-			"costing_rate": 50
-		})
+		activity_cost1 = frappe.new_doc("Activity Cost")
+		activity_cost1.update(
+			{
+				"employee": "_T-Employee-00001",
+				"employee_name": "_Test Employee",
+				"activity_type": "_Test Activity Type 1",
+				"billing_rate": 100,
+				"costing_rate": 50,
+			}
+		)
 		activity_cost1.insert()
 		activity_cost2 = frappe.copy_doc(activity_cost1)
-		self.assertRaises(DuplicationError, activity_cost2.insert )
+		self.assertRaises(DuplicationError, activity_cost2.insert)
 		frappe.db.sql("delete from `tabActivity Cost`")
diff --git a/erpnext/projects/doctype/activity_type/test_activity_type.py b/erpnext/projects/doctype/activity_type/test_activity_type.py
index bb74b88..d51439e 100644
--- a/erpnext/projects/doctype/activity_type/test_activity_type.py
+++ b/erpnext/projects/doctype/activity_type/test_activity_type.py
@@ -3,4 +3,4 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Activity Type')
+test_records = frappe.get_test_records("Activity Type")
diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json
index 1cda0a0..1790da4 100644
--- a/erpnext/projects/doctype/project/project.json
+++ b/erpnext/projects/doctype/project/project.json
@@ -234,7 +234,7 @@
   },
   {
    "fieldname": "actual_start_date",
-   "fieldtype": "Data",
+   "fieldtype": "Date",
    "label": "Actual Start Date (via Time Sheet)",
    "read_only": 1
   },
@@ -458,7 +458,7 @@
  "index_web_pages_for_search": 1,
  "links": [],
  "max_attachments": 4,
- "modified": "2022-01-29 13:58:27.712714",
+ "modified": "2022-05-25 22:45:06.108499",
  "modified_by": "Administrator",
  "module": "Projects",
  "name": "Project",
@@ -504,4 +504,4 @@
  "timeline_field": "customer",
  "title_field": "project_name",
  "track_seen": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index 6281c1a..7aa56de 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -9,29 +9,35 @@
 from frappe.model.document import Document
 from frappe.utils import add_days, flt, get_datetime, get_time, get_url, nowtime, today
 
+from erpnext import get_default_company
 from erpnext.controllers.employee_boarding_controller import update_employee_boarding_status
 from erpnext.controllers.queries import get_filters_cond
-from erpnext.education.doctype.student_attendance.student_attendance import get_holiday_list
 from erpnext.hr.doctype.daily_work_summary.daily_work_summary import get_users_email
 from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
 
 
 class Project(Document):
 	def get_feed(self):
-		return '{0}: {1}'.format(_(self.status), frappe.safe_decode(self.project_name))
+		return "{0}: {1}".format(_(self.status), frappe.safe_decode(self.project_name))
 
 	def onload(self):
-		self.set_onload('activity_summary', frappe.db.sql('''select activity_type,
+		self.set_onload(
+			"activity_summary",
+			frappe.db.sql(
+				"""select activity_type,
 			sum(hours) as total_hours
 			from `tabTimesheet Detail` where project=%s and docstatus < 2 group by activity_type
-			order by total_hours desc''', self.name, as_dict=True))
+			order by total_hours desc""",
+				self.name,
+				as_dict=True,
+			),
+		)
 
 		self.update_costing()
 
 	def before_print(self, settings=None):
 		self.onload()
 
-
 	def validate(self):
 		if not self.is_new():
 			self.copy_from_template()
@@ -41,17 +47,17 @@
 		update_employee_boarding_status(self)
 
 	def copy_from_template(self):
-		'''
+		"""
 		Copy tasks from template
-		'''
-		if self.project_template and not frappe.db.get_all('Task', dict(project = self.name), limit=1):
+		"""
+		if self.project_template and not frappe.db.get_all("Task", dict(project=self.name), limit=1):
 
 			# has a template, and no loaded tasks, so lets create
 			if not self.expected_start_date:
 				# project starts today
 				self.expected_start_date = today()
 
-			template = frappe.get_doc('Project Template', self.project_template)
+			template = frappe.get_doc("Project Template", self.project_template)
 
 			if not self.project_type:
 				self.project_type = template.project_type
@@ -67,19 +73,22 @@
 			self.dependency_mapping(tmp_task_details, project_tasks)
 
 	def create_task_from_template(self, task_details):
-		return frappe.get_doc(dict(
-				doctype = 'Task',
-				subject = task_details.subject,
-				project = self.name,
-				status = 'Open',
-				exp_start_date = self.calculate_start_date(task_details),
-				exp_end_date = self.calculate_end_date(task_details),
-				description = task_details.description,
-				task_weight = task_details.task_weight,
-				type = task_details.type,
-				issue = task_details.issue,
-				is_group = task_details.is_group
-			)).insert()
+		return frappe.get_doc(
+			dict(
+				doctype="Task",
+				subject=task_details.subject,
+				project=self.name,
+				status="Open",
+				exp_start_date=self.calculate_start_date(task_details),
+				exp_end_date=self.calculate_end_date(task_details),
+				description=task_details.description,
+				task_weight=task_details.task_weight,
+				type=task_details.type,
+				issue=task_details.issue,
+				is_group=task_details.is_group,
+				color=task_details.color,
+			)
+		).insert()
 
 	def calculate_start_date(self, task_details):
 		self.start_date = add_days(self.expected_start_date, task_details.start)
@@ -107,23 +116,26 @@
 		if template_task.get("depends_on") and not project_task.get("depends_on"):
 			for child_task in template_task.get("depends_on"):
 				child_task_subject = frappe.db.get_value("Task", child_task.task, "subject")
-				corresponding_project_task = list(filter(lambda x: x.subject == child_task_subject, project_tasks))
+				corresponding_project_task = list(
+					filter(lambda x: x.subject == child_task_subject, project_tasks)
+				)
 				if len(corresponding_project_task):
-					project_task.append("depends_on",{
-						"task": corresponding_project_task[0].name
-					})
+					project_task.append("depends_on", {"task": corresponding_project_task[0].name})
 					project_task.save()
 
 	def check_for_parent_tasks(self, template_task, project_task, project_tasks):
 		if template_task.get("parent_task") and not project_task.get("parent_task"):
 			parent_task_subject = frappe.db.get_value("Task", template_task.get("parent_task"), "subject")
-			corresponding_project_task = list(filter(lambda x: x.subject == parent_task_subject, project_tasks))
+			corresponding_project_task = list(
+				filter(lambda x: x.subject == parent_task_subject, project_tasks)
+			)
 			if len(corresponding_project_task):
 				project_task.parent_task = corresponding_project_task[0].name
 				project_task.save()
 
 	def is_row_updated(self, row, existing_task_data, fields):
-		if self.get("__islocal") or not existing_task_data: return True
+		if self.get("__islocal") or not existing_task_data:
+			return True
 
 		d = existing_task_data.get(row.task_id, {})
 
@@ -132,7 +144,7 @@
 				return True
 
 	def update_project(self):
-		'''Called externally by Task'''
+		"""Called externally by Task"""
 		self.update_percent_complete()
 		update_employee_boarding_status(self)
 		self.update_costing()
@@ -152,52 +164,74 @@
 				self.percent_complete = 100
 			return
 
-		total = frappe.db.count('Task', dict(project=self.name))
+		total = frappe.db.count("Task", dict(project=self.name))
 
 		if not total:
 			self.percent_complete = 0
 		else:
 			if (self.percent_complete_method == "Task Completion" and total > 0) or (
-				not self.percent_complete_method and total > 0):
-				completed = frappe.db.sql("""select count(name) from tabTask where
-					project=%s and status in ('Cancelled', 'Completed')""", self.name)[0][0]
+				not self.percent_complete_method and total > 0
+			):
+				completed = frappe.db.sql(
+					"""select count(name) from tabTask where
+					project=%s and status in ('Cancelled', 'Completed')""",
+					self.name,
+				)[0][0]
 				self.percent_complete = flt(flt(completed) / total * 100, 2)
 
-			if (self.percent_complete_method == "Task Progress" and total > 0):
-				progress = frappe.db.sql("""select sum(progress) from tabTask where
-					project=%s""", self.name)[0][0]
+			if self.percent_complete_method == "Task Progress" and total > 0:
+				progress = frappe.db.sql(
+					"""select sum(progress) from tabTask where
+					project=%s""",
+					self.name,
+				)[0][0]
 				self.percent_complete = flt(flt(progress) / total, 2)
 
-			if (self.percent_complete_method == "Task Weight" and total > 0):
-				weight_sum = frappe.db.sql("""select sum(task_weight) from tabTask where
-					project=%s""", self.name)[0][0]
-				weighted_progress = frappe.db.sql("""select progress, task_weight from tabTask where
-					project=%s""", self.name, as_dict=1)
+			if self.percent_complete_method == "Task Weight" and total > 0:
+				weight_sum = frappe.db.sql(
+					"""select sum(task_weight) from tabTask where
+					project=%s""",
+					self.name,
+				)[0][0]
+				weighted_progress = frappe.db.sql(
+					"""select progress, task_weight from tabTask where
+					project=%s""",
+					self.name,
+					as_dict=1,
+				)
 				pct_complete = 0
 				for row in weighted_progress:
 					pct_complete += row["progress"] * frappe.utils.safe_div(row["task_weight"], weight_sum)
 				self.percent_complete = flt(flt(pct_complete), 2)
 
 		# don't update status if it is cancelled
-		if self.status == 'Cancelled':
+		if self.status == "Cancelled":
 			return
 
 		if self.percent_complete == 100:
 			self.status = "Completed"
 
 	def update_costing(self):
-		from_time_sheet = frappe.db.sql("""select
+		from_time_sheet = frappe.db.sql(
+			"""select
 			sum(costing_amount) as costing_amount,
 			sum(billing_amount) as billing_amount,
 			min(from_time) as start_date,
 			max(to_time) as end_date,
 			sum(hours) as time
-			from `tabTimesheet Detail` where project = %s and docstatus = 1""", self.name, as_dict=1)[0]
+			from `tabTimesheet Detail` where project = %s and docstatus = 1""",
+			self.name,
+			as_dict=1,
+		)[0]
 
-		from_expense_claim = frappe.db.sql("""select
+		from_expense_claim = frappe.db.sql(
+			"""select
 			sum(total_sanctioned_amount) as total_sanctioned_amount
 			from `tabExpense Claim` where project = %s
-			and docstatus = 1""", self.name, as_dict=1)[0]
+			and docstatus = 1""",
+			self.name,
+			as_dict=1,
+		)[0]
 
 		self.actual_start_date = from_time_sheet.start_date
 		self.actual_end_date = from_time_sheet.end_date
@@ -213,41 +247,54 @@
 		self.calculate_gross_margin()
 
 	def calculate_gross_margin(self):
-		expense_amount = (flt(self.total_costing_amount) + flt(self.total_expense_claim)
-			+ flt(self.total_purchase_cost) + flt(self.get('total_consumed_material_cost', 0)))
+		expense_amount = (
+			flt(self.total_costing_amount)
+			+ flt(self.total_expense_claim)
+			+ flt(self.total_purchase_cost)
+			+ flt(self.get("total_consumed_material_cost", 0))
+		)
 
 		self.gross_margin = flt(self.total_billed_amount) - expense_amount
 		if self.total_billed_amount:
 			self.per_gross_margin = (self.gross_margin / flt(self.total_billed_amount)) * 100
 
 	def update_purchase_costing(self):
-		total_purchase_cost = frappe.db.sql("""select sum(base_net_amount)
-			from `tabPurchase Invoice Item` where project = %s and docstatus=1""", self.name)
+		total_purchase_cost = frappe.db.sql(
+			"""select sum(base_net_amount)
+			from `tabPurchase Invoice Item` where project = %s and docstatus=1""",
+			self.name,
+		)
 
 		self.total_purchase_cost = total_purchase_cost and total_purchase_cost[0][0] or 0
 
 	def update_sales_amount(self):
-		total_sales_amount = frappe.db.sql("""select sum(base_net_total)
-			from `tabSales Order` where project = %s and docstatus=1""", self.name)
+		total_sales_amount = frappe.db.sql(
+			"""select sum(base_net_total)
+			from `tabSales Order` where project = %s and docstatus=1""",
+			self.name,
+		)
 
 		self.total_sales_amount = total_sales_amount and total_sales_amount[0][0] or 0
 
 	def update_billed_amount(self):
-		total_billed_amount = frappe.db.sql("""select sum(base_net_total)
-			from `tabSales Invoice` where project = %s and docstatus=1""", self.name)
+		total_billed_amount = frappe.db.sql(
+			"""select sum(base_net_total)
+			from `tabSales Invoice` where project = %s and docstatus=1""",
+			self.name,
+		)
 
 		self.total_billed_amount = total_billed_amount and total_billed_amount[0][0] or 0
 
 	def after_rename(self, old_name, new_name, merge=False):
 		if old_name == self.copied_from:
-			frappe.db.set_value('Project', new_name, 'copied_from', new_name)
+			frappe.db.set_value("Project", new_name, "copied_from", new_name)
 
 	def send_welcome_email(self):
 		url = get_url("/project/?name={0}".format(self.name))
 		messages = (
 			_("You have been invited to collaborate on the project: {0}").format(self.name),
 			url,
-			_("Join")
+			_("Join"),
 		)
 
 		content = """
@@ -257,49 +304,82 @@
 
 		for user in self.users:
 			if user.welcome_email_sent == 0:
-				frappe.sendmail(user.user, subject=_("Project Collaboration Invitation"),
-								content=content.format(*messages))
+				frappe.sendmail(
+					user.user, subject=_("Project Collaboration Invitation"), content=content.format(*messages)
+				)
 				user.welcome_email_sent = 1
 
+
 def get_timeline_data(doctype, name):
-	'''Return timeline for attendance'''
-	return dict(frappe.db.sql('''select unix_timestamp(from_time), count(*)
+	"""Return timeline for attendance"""
+	return dict(
+		frappe.db.sql(
+			"""select unix_timestamp(from_time), count(*)
 		from `tabTimesheet Detail` where project=%s
 			and from_time > date_sub(curdate(), interval 1 year)
 			and docstatus < 2
-			group by date(from_time)''', name))
+			group by date(from_time)""",
+			name,
+		)
+	)
 
 
-def get_project_list(doctype, txt, filters, limit_start, limit_page_length=20, order_by="modified"):
-	return frappe.db.sql('''select distinct project.*
-		from tabProject project, `tabProject User` project_user
-		where
-			(project_user.user = %(user)s
-			and project_user.parent = project.name)
-			or project.owner = %(user)s
-			order by project.modified desc
-			limit {0}, {1}
-		'''.format(limit_start, limit_page_length),
-						 {'user': frappe.session.user},
-						 as_dict=True,
-						 update={'doctype': 'Project'})
+def get_project_list(
+	doctype, txt, filters, limit_start, limit_page_length=20, order_by="modified"
+):
+	meta = frappe.get_meta(doctype)
+	if not filters:
+		filters = []
+
+	fields = "distinct *"
+
+	or_filters = []
+
+	if txt:
+		if meta.search_fields:
+			for f in meta.get_search_fields():
+				if f == "name" or meta.get_field(f).fieldtype in (
+					"Data",
+					"Text",
+					"Small Text",
+					"Text Editor",
+					"select",
+				):
+					or_filters.append([doctype, f, "like", "%" + txt + "%"])
+		else:
+			if isinstance(filters, dict):
+				filters["name"] = ("like", "%" + txt + "%")
+			else:
+				filters.append([doctype, "name", "like", "%" + txt + "%"])
+
+	return frappe.get_list(
+		doctype,
+		fields=fields,
+		filters=filters,
+		or_filters=or_filters,
+		limit_start=limit_start,
+		limit_page_length=limit_page_length,
+		order_by=order_by,
+	)
 
 
 def get_list_context(context=None):
 	return {
 		"show_sidebar": True,
 		"show_search": True,
-		'no_breadcrumbs': True,
+		"no_breadcrumbs": True,
 		"title": _("Projects"),
 		"get_list": get_project_list,
-		"row_template": "templates/includes/projects/project_row.html"
+		"row_template": "templates/includes/projects/project_row.html",
 	}
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_users_for_project(doctype, txt, searchfield, start, page_len, filters):
 	conditions = []
-	return frappe.db.sql("""select name, concat_ws(' ', first_name, middle_name, last_name)
+	return frappe.db.sql(
+		"""select name, concat_ws(' ', first_name, middle_name, last_name)
 		from `tabUser`
 		where enabled=1
 			and name not in ("Guest", "Administrator")
@@ -307,51 +387,55 @@
 				or full_name like %(txt)s)
 			{fcond} {mcond}
 		order by
-			if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
-			if(locate(%(_txt)s, full_name), locate(%(_txt)s, full_name), 99999),
+			(case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end),
+			(case when locate(%(_txt)s, full_name) > 0 then locate(%(_txt)s, full_name) else 99999 end)
 			idx desc,
 			name, full_name
-		limit %(start)s, %(page_len)s""".format(**{
-		'key': searchfield,
-		'fcond': get_filters_cond(doctype, filters, conditions),
-		'mcond': get_match_cond(doctype)
-	}), {
-							 'txt': "%%%s%%" % txt,
-							 '_txt': txt.replace("%", ""),
-							 'start': start,
-							 'page_len': page_len
-						 })
+		limit %(page_len)s offset %(start)s""".format(
+			**{
+				"key": searchfield,
+				"fcond": get_filters_cond(doctype, filters, conditions),
+				"mcond": get_match_cond(doctype),
+			}
+		),
+		{"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len},
+	)
 
 
 @frappe.whitelist()
 def get_cost_center_name(project):
 	return frappe.db.get_value("Project", project, "cost_center")
 
+
 def hourly_reminder():
 	fields = ["from_time", "to_time"]
 	projects = get_projects_for_collect_progress("Hourly", fields)
 
 	for project in projects:
-		if (get_time(nowtime()) >= get_time(project.from_time) or
-			get_time(nowtime()) <= get_time(project.to_time)):
+		if get_time(nowtime()) >= get_time(project.from_time) or get_time(nowtime()) <= get_time(
+			project.to_time
+		):
 			send_project_update_email_to_users(project.name)
 
+
 def project_status_update_reminder():
 	daily_reminder()
 	twice_daily_reminder()
 	weekly_reminder()
 
+
 def daily_reminder():
 	fields = ["daily_time_to_send"]
-	projects =  get_projects_for_collect_progress("Daily", fields)
+	projects = get_projects_for_collect_progress("Daily", fields)
 
 	for project in projects:
 		if allow_to_make_project_update(project.name, project.get("daily_time_to_send"), "Daily"):
 			send_project_update_email_to_users(project.name)
 
+
 def twice_daily_reminder():
 	fields = ["first_email", "second_email"]
-	projects =  get_projects_for_collect_progress("Twice Daily", fields)
+	projects = get_projects_for_collect_progress("Twice Daily", fields)
 	fields.remove("name")
 
 	for project in projects:
@@ -359,9 +443,10 @@
 			if allow_to_make_project_update(project.name, project.get(d), "Twicely"):
 				send_project_update_email_to_users(project.name)
 
+
 def weekly_reminder():
 	fields = ["day_to_send", "weekly_time_to_send"]
-	projects =  get_projects_for_collect_progress("Weekly", fields)
+	projects = get_projects_for_collect_progress("Weekly", fields)
 
 	current_day = get_datetime().strftime("%A")
 	for project in projects:
@@ -371,12 +456,16 @@
 		if allow_to_make_project_update(project.name, project.get("weekly_time_to_send"), "Weekly"):
 			send_project_update_email_to_users(project.name)
 
+
 def allow_to_make_project_update(project, time, frequency):
-	data = frappe.db.sql(""" SELECT name from `tabProject Update`
-		WHERE project = %s and date = %s """, (project, today()))
+	data = frappe.db.sql(
+		""" SELECT name from `tabProject Update`
+		WHERE project = %s and date = %s """,
+		(project, today()),
+	)
 
 	# len(data) > 1 condition is checked for twicely frequency
-	if data and (frequency in ['Daily', 'Weekly'] or len(data) > 1):
+	if data and (frequency in ["Daily", "Weekly"] or len(data) > 1):
 		return False
 
 	if get_time(nowtime()) >= get_time(time):
@@ -385,138 +474,162 @@
 
 @frappe.whitelist()
 def create_duplicate_project(prev_doc, project_name):
-	''' Create duplicate project based on the old project '''
+	"""Create duplicate project based on the old project"""
 	import json
+
 	prev_doc = json.loads(prev_doc)
 
-	if project_name == prev_doc.get('name'):
+	if project_name == prev_doc.get("name"):
 		frappe.throw(_("Use a name that is different from previous project name"))
 
 	# change the copied doc name to new project name
 	project = frappe.copy_doc(prev_doc)
 	project.name = project_name
-	project.project_template = ''
+	project.project_template = ""
 	project.project_name = project_name
 	project.insert()
 
 	# fetch all the task linked with the old project
-	task_list = frappe.get_all("Task", filters={
-		'project': prev_doc.get('name')
-	}, fields=['name'])
+	task_list = frappe.get_all("Task", filters={"project": prev_doc.get("name")}, fields=["name"])
 
 	# Create duplicate task for all the task
 	for task in task_list:
-		task = frappe.get_doc('Task', task)
+		task = frappe.get_doc("Task", task)
 		new_task = frappe.copy_doc(task)
 		new_task.project = project.name
 		new_task.insert()
 
-	project.db_set('project_template', prev_doc.get('project_template'))
+	project.db_set("project_template", prev_doc.get("project_template"))
+
 
 def get_projects_for_collect_progress(frequency, fields):
 	fields.extend(["name"])
 
-	return frappe.get_all("Project", fields = fields,
-		filters = {'collect_progress': 1, 'frequency': frequency, 'status': 'Open'})
+	return frappe.get_all(
+		"Project",
+		fields=fields,
+		filters={"collect_progress": 1, "frequency": frequency, "status": "Open"},
+	)
+
 
 def send_project_update_email_to_users(project):
-	doc = frappe.get_doc('Project', project)
+	doc = frappe.get_doc("Project", project)
 
-	if is_holiday(doc.holiday_list) or not doc.users: return
+	if is_holiday(doc.holiday_list) or not doc.users:
+		return
 
-	project_update = frappe.get_doc({
-		"doctype" : "Project Update",
-		"project" : project,
-		"sent": 0,
-		"date": today(),
-		"time": nowtime(),
-		"naming_series": "UPDATE-.project.-.YY.MM.DD.-",
-	}).insert()
+	project_update = frappe.get_doc(
+		{
+			"doctype": "Project Update",
+			"project": project,
+			"sent": 0,
+			"date": today(),
+			"time": nowtime(),
+			"naming_series": "UPDATE-.project.-.YY.MM.DD.-",
+		}
+	).insert()
 
 	subject = "For project %s, update your status" % (project)
 
-	incoming_email_account = frappe.db.get_value('Email Account',
-		dict(enable_incoming=1, default_incoming=1), 'email_id')
+	incoming_email_account = frappe.db.get_value(
+		"Email Account", dict(enable_incoming=1, default_incoming=1), "email_id"
+	)
 
-	frappe.sendmail(recipients=get_users_email(doc),
+	frappe.sendmail(
+		recipients=get_users_email(doc),
 		message=doc.message,
 		subject=_(subject),
 		reference_doctype=project_update.doctype,
 		reference_name=project_update.name,
-		reply_to=incoming_email_account
+		reply_to=incoming_email_account,
 	)
 
+
 def collect_project_status():
-	for data in frappe.get_all("Project Update",
-		{'date': today(), 'sent': 0}):
-		replies = frappe.get_all('Communication',
-			fields=['content', 'text_content', 'sender'],
-			filters=dict(reference_doctype="Project Update",
+	for data in frappe.get_all("Project Update", {"date": today(), "sent": 0}):
+		replies = frappe.get_all(
+			"Communication",
+			fields=["content", "text_content", "sender"],
+			filters=dict(
+				reference_doctype="Project Update",
 				reference_name=data.name,
-				communication_type='Communication',
-				sent_or_received='Received'),
-			order_by='creation asc')
+				communication_type="Communication",
+				sent_or_received="Received",
+			),
+			order_by="creation asc",
+		)
 
 		for d in replies:
 			doc = frappe.get_doc("Project Update", data.name)
-			user_data = frappe.db.get_values("User", {"email": d.sender},
-				["full_name", "user_image", "name"], as_dict=True)[0]
+			user_data = frappe.db.get_values(
+				"User", {"email": d.sender}, ["full_name", "user_image", "name"], as_dict=True
+			)[0]
 
-			doc.append("users", {
-				'user': user_data.name,
-				'full_name': user_data.full_name,
-				'image': user_data.user_image,
-				'project_status': frappe.utils.md_to_html(
-					EmailReplyParser.parse_reply(d.text_content) or d.content
-				)
-			})
+			doc.append(
+				"users",
+				{
+					"user": user_data.name,
+					"full_name": user_data.full_name,
+					"image": user_data.user_image,
+					"project_status": frappe.utils.md_to_html(
+						EmailReplyParser.parse_reply(d.text_content) or d.content
+					),
+				},
+			)
 
 			doc.save(ignore_permissions=True)
 
+
 def send_project_status_email_to_users():
 	yesterday = add_days(today(), -1)
 
-	for d in frappe.get_all("Project Update",
-		{'date': yesterday, 'sent': 0}):
+	for d in frappe.get_all("Project Update", {"date": yesterday, "sent": 0}):
 		doc = frappe.get_doc("Project Update", d.name)
 
-		project_doc = frappe.get_doc('Project', doc.project)
+		project_doc = frappe.get_doc("Project", doc.project)
 
-		args = {
-			"users": doc.users,
-			"title": _("Project Summary for {0}").format(yesterday)
-		}
+		args = {"users": doc.users, "title": _("Project Summary for {0}").format(yesterday)}
 
-		frappe.sendmail(recipients=get_users_email(project_doc),
-			template='daily_project_summary',
+		frappe.sendmail(
+			recipients=get_users_email(project_doc),
+			template="daily_project_summary",
 			args=args,
 			subject=_("Daily Project Summary for {0}").format(d.name),
 			reference_doctype="Project Update",
-			reference_name=d.name)
+			reference_name=d.name,
+		)
 
-		doc.db_set('sent', 1)
+		doc.db_set("sent", 1)
+
 
 def update_project_sales_billing():
 	sales_update_frequency = frappe.db.get_single_value("Selling Settings", "sales_update_frequency")
 	if sales_update_frequency == "Each Transaction":
 		return
-	elif (sales_update_frequency == "Monthly" and frappe.utils.now_datetime().day != 1):
+	elif sales_update_frequency == "Monthly" and frappe.utils.now_datetime().day != 1:
 		return
 
-	#Else simply fallback to Daily
-	exists_query = '(SELECT 1 from `tab{doctype}` where docstatus = 1 and project = `tabProject`.name)'
+	# Else simply fallback to Daily
+	exists_query = (
+		"(SELECT 1 from `tab{doctype}` where docstatus = 1 and project = `tabProject`.name)"
+	)
 	project_map = {}
-	for project_details in frappe.db.sql('''
+	for project_details in frappe.db.sql(
+		"""
 			SELECT name, 1 as order_exists, null as invoice_exists from `tabProject` where
 			exists {order_exists}
 			union
 			SELECT name, null as order_exists, 1 as invoice_exists from `tabProject` where
 			exists {invoice_exists}
-		'''.format(
+		""".format(
 			order_exists=exists_query.format(doctype="Sales Order"),
 			invoice_exists=exists_query.format(doctype="Sales Invoice"),
-		), as_dict=True):
-		project = project_map.setdefault(project_details.name, frappe.get_doc('Project', project_details.name))
+		),
+		as_dict=True,
+	):
+		project = project_map.setdefault(
+			project_details.name, frappe.get_doc("Project", project_details.name)
+		)
 		if project_details.order_exists:
 			project.update_sales_amount()
 		if project_details.invoice_exists:
@@ -525,29 +638,45 @@
 	for project in project_map.values():
 		project.save()
 
+
 @frappe.whitelist()
 def create_kanban_board_if_not_exists(project):
 	from frappe.desk.doctype.kanban_board.kanban_board import quick_kanban_board
 
-	project = frappe.get_doc('Project', project)
-	if not frappe.db.exists('Kanban Board', project.project_name):
-		quick_kanban_board('Task', project.project_name, 'status', project.name)
+	project = frappe.get_doc("Project", project)
+	if not frappe.db.exists("Kanban Board", project.project_name):
+		quick_kanban_board("Task", project.project_name, "status", project.name)
 
 	return True
 
+
 @frappe.whitelist()
 def set_project_status(project, status):
-	'''
+	"""
 	set status for project and all related tasks
-	'''
-	if not status in ('Completed', 'Cancelled'):
-		frappe.throw(_('Status must be Cancelled or Completed'))
+	"""
+	if not status in ("Completed", "Cancelled"):
+		frappe.throw(_("Status must be Cancelled or Completed"))
 
-	project = frappe.get_doc('Project', project)
-	frappe.has_permission(doc = project, throw = True)
+	project = frappe.get_doc("Project", project)
+	frappe.has_permission(doc=project, throw=True)
 
-	for task in frappe.get_all('Task', dict(project = project.name)):
-		frappe.db.set_value('Task', task.name, 'status', status)
+	for task in frappe.get_all("Task", dict(project=project.name)):
+		frappe.db.set_value("Task", task.name, "status", status)
 
 	project.status = status
 	project.save()
+
+
+def get_holiday_list(company=None):
+	if not company:
+		company = get_default_company() or frappe.get_all("Company")[0].name
+
+	holiday_list = frappe.get_cached_value("Company", company, "default_holiday_list")
+	if not holiday_list:
+		frappe.throw(
+			_("Please set a default Holiday List for Company {0}").format(
+				frappe.bold(get_default_company())
+			)
+		)
+	return holiday_list
diff --git a/erpnext/projects/doctype/project/project_dashboard.py b/erpnext/projects/doctype/project/project_dashboard.py
index a7d1835..b6c74f9 100644
--- a/erpnext/projects/doctype/project/project_dashboard.py
+++ b/erpnext/projects/doctype/project/project_dashboard.py
@@ -3,25 +3,16 @@
 
 def get_data():
 	return {
-		'heatmap': True,
-		'heatmap_message': _('This is based on the Time Sheets created against this project'),
-		'fieldname': 'project',
-		'transactions': [
+		"heatmap": True,
+		"heatmap_message": _("This is based on the Time Sheets created against this project"),
+		"fieldname": "project",
+		"transactions": [
 			{
-				'label': _('Project'),
-				'items': ['Task', 'Timesheet', 'Expense Claim', 'Issue' , 'Project Update']
+				"label": _("Project"),
+				"items": ["Task", "Timesheet", "Expense Claim", "Issue", "Project Update"],
 			},
-			{
-				'label': _('Material'),
-				'items': ['Material Request', 'BOM', 'Stock Entry']
-			},
-			{
-				'label': _('Sales'),
-				'items': ['Sales Order', 'Delivery Note', 'Sales Invoice']
-			},
-			{
-				'label': _('Purchase'),
-				'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
-			},
-		]
+			{"label": _("Material"), "items": ["Material Request", "BOM", "Stock Entry"]},
+			{"label": _("Sales"), "items": ["Sales Order", "Delivery Note", "Sales Invoice"]},
+			{"label": _("Purchase"), "items": ["Purchase Order", "Purchase Receipt", "Purchase Invoice"]},
+		],
 	}
diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py
index df42e82..8a599ce 100644
--- a/erpnext/projects/doctype/project/test_project.py
+++ b/erpnext/projects/doctype/project/test_project.py
@@ -11,7 +11,7 @@
 from erpnext.selling.doctype.sales_order.sales_order import make_project as make_project_from_so
 from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
 
-test_records = frappe.get_test_records('Project')
+test_records = frappe.get_test_records("Project")
 test_ignore = ["Sales Order"]
 
 
@@ -19,53 +19,83 @@
 	def test_project_with_template_having_no_parent_and_depend_tasks(self):
 		project_name = "Test Project with Template - No Parent and Dependend Tasks"
 		frappe.db.sql(""" delete from tabTask where project = %s """, project_name)
-		frappe.delete_doc('Project', project_name)
+		frappe.delete_doc("Project", project_name)
 
 		task1 = task_exists("Test Template Task with No Parent and Dependency")
 		if not task1:
-			task1 = create_task(subject="Test Template Task with No Parent and Dependency", is_template=1, begin=5, duration=3)
+			task1 = create_task(
+				subject="Test Template Task with No Parent and Dependency", is_template=1, begin=5, duration=3
+			)
 
-		template = make_project_template("Test Project Template - No Parent and Dependend Tasks", [task1])
+		template = make_project_template(
+			"Test Project Template - No Parent and Dependend Tasks", [task1]
+		)
 		project = get_project(project_name, template)
-		tasks = frappe.get_all('Task', ['subject','exp_end_date','depends_on_tasks'], dict(project=project.name), order_by='creation asc')
+		tasks = frappe.get_all(
+			"Task",
+			["subject", "exp_end_date", "depends_on_tasks"],
+			dict(project=project.name),
+			order_by="creation asc",
+		)
 
-		self.assertEqual(tasks[0].subject, 'Test Template Task with No Parent and Dependency')
+		self.assertEqual(tasks[0].subject, "Test Template Task with No Parent and Dependency")
 		self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 5, 3))
 		self.assertEqual(len(tasks), 1)
 
 	def test_project_template_having_parent_child_tasks(self):
 		project_name = "Test Project with Template - Tasks with Parent-Child Relation"
 
-		if frappe.db.get_value('Project', {'project_name': project_name}, 'name'):
-			project_name = frappe.db.get_value('Project', {'project_name': project_name}, 'name')
+		if frappe.db.get_value("Project", {"project_name": project_name}, "name"):
+			project_name = frappe.db.get_value("Project", {"project_name": project_name}, "name")
 
 		frappe.db.sql(""" delete from tabTask where project = %s """, project_name)
-		frappe.delete_doc('Project', project_name)
+		frappe.delete_doc("Project", project_name)
 
 		task1 = task_exists("Test Template Task Parent")
 		if not task1:
-			task1 = create_task(subject="Test Template Task Parent", is_group=1, is_template=1, begin=1, duration=10)
+			task1 = create_task(
+				subject="Test Template Task Parent", is_group=1, is_template=1, begin=1, duration=10
+			)
 
 		task2 = task_exists("Test Template Task Child 1")
 		if not task2:
-			task2 = create_task(subject="Test Template Task Child 1", parent_task=task1.name, is_template=1, begin=1, duration=3)
+			task2 = create_task(
+				subject="Test Template Task Child 1",
+				parent_task=task1.name,
+				is_template=1,
+				begin=1,
+				duration=3,
+			)
 
 		task3 = task_exists("Test Template Task Child 2")
 		if not task3:
-			task3 = create_task(subject="Test Template Task Child 2", parent_task=task1.name, is_template=1, begin=2, duration=3)
+			task3 = create_task(
+				subject="Test Template Task Child 2",
+				parent_task=task1.name,
+				is_template=1,
+				begin=2,
+				duration=3,
+			)
 
-		template = make_project_template("Test Project Template  - Tasks with Parent-Child Relation", [task1, task2, task3])
+		template = make_project_template(
+			"Test Project Template  - Tasks with Parent-Child Relation", [task1, task2, task3]
+		)
 		project = get_project(project_name, template)
-		tasks = frappe.get_all('Task', ['subject','exp_end_date','depends_on_tasks', 'name', 'parent_task'], dict(project=project.name), order_by='creation asc')
+		tasks = frappe.get_all(
+			"Task",
+			["subject", "exp_end_date", "depends_on_tasks", "name", "parent_task"],
+			dict(project=project.name),
+			order_by="creation asc",
+		)
 
-		self.assertEqual(tasks[0].subject, 'Test Template Task Parent')
+		self.assertEqual(tasks[0].subject, "Test Template Task Parent")
 		self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 1, 10))
 
-		self.assertEqual(tasks[1].subject, 'Test Template Task Child 1')
+		self.assertEqual(tasks[1].subject, "Test Template Task Child 1")
 		self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, 1, 3))
 		self.assertEqual(tasks[1].parent_task, tasks[0].name)
 
-		self.assertEqual(tasks[2].subject, 'Test Template Task Child 2')
+		self.assertEqual(tasks[2].subject, "Test Template Task Child 2")
 		self.assertEqual(getdate(tasks[2].exp_end_date), calculate_end_date(project, 2, 3))
 		self.assertEqual(tasks[2].parent_task, tasks[0].name)
 
@@ -74,26 +104,39 @@
 	def test_project_template_having_dependent_tasks(self):
 		project_name = "Test Project with Template - Dependent Tasks"
 		frappe.db.sql(""" delete from tabTask where project = %s  """, project_name)
-		frappe.delete_doc('Project', project_name)
+		frappe.delete_doc("Project", project_name)
 
 		task1 = task_exists("Test Template Task for Dependency")
 		if not task1:
-			task1 = create_task(subject="Test Template Task for Dependency", is_template=1, begin=3, duration=1)
+			task1 = create_task(
+				subject="Test Template Task for Dependency", is_template=1, begin=3, duration=1
+			)
 
 		task2 = task_exists("Test Template Task with Dependency")
 		if not task2:
-			task2 = create_task(subject="Test Template Task with Dependency", depends_on=task1.name, is_template=1, begin=2, duration=2)
+			task2 = create_task(
+				subject="Test Template Task with Dependency",
+				depends_on=task1.name,
+				is_template=1,
+				begin=2,
+				duration=2,
+			)
 
 		template = make_project_template("Test Project with Template - Dependent Tasks", [task1, task2])
 		project = get_project(project_name, template)
-		tasks = frappe.get_all('Task', ['subject','exp_end_date','depends_on_tasks', 'name'], dict(project=project.name), order_by='creation asc')
+		tasks = frappe.get_all(
+			"Task",
+			["subject", "exp_end_date", "depends_on_tasks", "name"],
+			dict(project=project.name),
+			order_by="creation asc",
+		)
 
-		self.assertEqual(tasks[1].subject, 'Test Template Task with Dependency')
+		self.assertEqual(tasks[1].subject, "Test Template Task with Dependency")
 		self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, 2, 2))
-		self.assertTrue(tasks[1].depends_on_tasks.find(tasks[0].name) >= 0 )
+		self.assertTrue(tasks[1].depends_on_tasks.find(tasks[0].name) >= 0)
 
-		self.assertEqual(tasks[0].subject, 'Test Template Task for Dependency')
-		self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 3, 1) )
+		self.assertEqual(tasks[0].subject, "Test Template Task for Dependency")
+		self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 3, 1))
 
 		self.assertEqual(len(tasks), 2)
 
@@ -112,32 +155,38 @@
 		so.reload()
 		self.assertFalse(so.project)
 
+
 def get_project(name, template):
 
-	project = frappe.get_doc(dict(
-		doctype = 'Project',
-		project_name = name,
-		status = 'Open',
-		project_template = template.name,
-		expected_start_date = nowdate(),
-		company="_Test Company"
-	)).insert()
+	project = frappe.get_doc(
+		dict(
+			doctype="Project",
+			project_name=name,
+			status="Open",
+			project_template=template.name,
+			expected_start_date=nowdate(),
+			company="_Test Company",
+		)
+	).insert()
 
 	return project
 
+
 def make_project(args):
 	args = frappe._dict(args)
 
 	if args.project_name and frappe.db.exists("Project", {"project_name": args.project_name}):
 		return frappe.get_doc("Project", {"project_name": args.project_name})
 
-	project = frappe.get_doc(dict(
-		doctype = 'Project',
-		project_name = args.project_name,
-		status = 'Open',
-		expected_start_date = args.start_date,
-		company= args.company or '_Test Company'
-	))
+	project = frappe.get_doc(
+		dict(
+			doctype="Project",
+			project_name=args.project_name,
+			status="Open",
+			expected_start_date=args.start_date,
+			company=args.company or "_Test Company",
+		)
+	)
 
 	if args.project_template_name:
 		template = make_project_template(args.project_template_name)
@@ -147,12 +196,14 @@
 
 	return project
 
+
 def task_exists(subject):
-	result = frappe.db.get_list("Task", filters={"subject": subject},fields=["name"])
+	result = frappe.db.get_list("Task", filters={"subject": subject}, fields=["name"])
 	if not len(result):
 		return False
 	return frappe.get_doc("Task", result[0].name)
 
+
 def calculate_end_date(project, start, duration):
 	start = add_days(project.expected_start_date, start)
 	start = project.update_if_holiday(start)
diff --git a/erpnext/projects/doctype/project_template/project_template.py b/erpnext/projects/doctype/project_template/project_template.py
index 3cc8d68..89afb1b 100644
--- a/erpnext/projects/doctype/project_template/project_template.py
+++ b/erpnext/projects/doctype/project_template/project_template.py
@@ -9,7 +9,6 @@
 
 
 class ProjectTemplate(Document):
-
 	def validate(self):
 		self.validate_dependencies()
 
@@ -19,9 +18,13 @@
 			if task_details.depends_on:
 				for dependency_task in task_details.depends_on:
 					if not self.check_dependent_task_presence(dependency_task.task):
-						task_details_format = get_link_to_form("Task",task_details.name)
+						task_details_format = get_link_to_form("Task", task_details.name)
 						dependency_task_format = get_link_to_form("Task", dependency_task.task)
-						frappe.throw(_("Task {0} depends on Task {1}. Please add Task {1} to the Tasks list.").format(frappe.bold(task_details_format), frappe.bold(dependency_task_format)))
+						frappe.throw(
+							_("Task {0} depends on Task {1}. Please add Task {1} to the Tasks list.").format(
+								frappe.bold(task_details_format), frappe.bold(dependency_task_format)
+							)
+						)
 
 	def check_dependent_task_presence(self, task):
 		for task_details in self.tasks:
diff --git a/erpnext/projects/doctype/project_template/project_template_dashboard.py b/erpnext/projects/doctype/project_template/project_template_dashboard.py
index a03a57d..0c567c1 100644
--- a/erpnext/projects/doctype/project_template/project_template_dashboard.py
+++ b/erpnext/projects/doctype/project_template/project_template_dashboard.py
@@ -1,9 +1,2 @@
 def get_data():
-	return {
-		'fieldname': 'project_template',
-		'transactions': [
-			{
-				'items': ['Project']
-			}
-		]
-	}
+	return {"fieldname": "project_template", "transactions": [{"items": ["Project"]}]}
diff --git a/erpnext/projects/doctype/project_template/test_project_template.py b/erpnext/projects/doctype/project_template/test_project_template.py
index 8424833..4fd24bf 100644
--- a/erpnext/projects/doctype/project_template/test_project_template.py
+++ b/erpnext/projects/doctype/project_template/test_project_template.py
@@ -11,20 +11,16 @@
 class TestProjectTemplate(unittest.TestCase):
 	pass
 
+
 def make_project_template(project_template_name, project_tasks=[]):
-	if not frappe.db.exists('Project Template', project_template_name):
+	if not frappe.db.exists("Project Template", project_template_name):
 		project_tasks = project_tasks or [
-				create_task(subject="_Test Template Task 1", is_template=1, begin=0, duration=3),
-				create_task(subject="_Test Template Task 2", is_template=1, begin=0, duration=2),
-			]
-		doc = frappe.get_doc(dict(
-			doctype = 'Project Template',
-			name = project_template_name
-		))
+			create_task(subject="_Test Template Task 1", is_template=1, begin=0, duration=3),
+			create_task(subject="_Test Template Task 2", is_template=1, begin=0, duration=2),
+		]
+		doc = frappe.get_doc(dict(doctype="Project Template", name=project_template_name))
 		for task in project_tasks:
-			doc.append("tasks",{
-				"task": task.name
-			})
+			doc.append("tasks", {"task": task.name})
 		doc.insert()
 
-	return frappe.get_doc('Project Template', project_template_name)
+	return frappe.get_doc("Project Template", project_template_name)
diff --git a/erpnext/projects/doctype/project_update/project_update.py b/erpnext/projects/doctype/project_update/project_update.py
index 42ba5f6..175f787 100644
--- a/erpnext/projects/doctype/project_update/project_update.py
+++ b/erpnext/projects/doctype/project_update/project_update.py
@@ -7,36 +7,88 @@
 
 
 class ProjectUpdate(Document):
-    pass
+	pass
+
 
 @frappe.whitelist()
 def daily_reminder():
-    project = frappe.db.sql("""SELECT `tabProject`.project_name,`tabProject`.frequency,`tabProject`.expected_start_date,`tabProject`.expected_end_date,`tabProject`.percent_complete FROM `tabProject`;""")
-    for projects in project:
-        project_name = projects[0]
-        frequency = projects[1]
-        date_start = projects[2]
-        date_end = projects [3]
-        progress = projects [4]
-        draft = frappe.db.sql("""SELECT count(docstatus) from `tabProject Update` WHERE `tabProject Update`.project = %s AND `tabProject Update`.docstatus = 0;""",project_name)
-        for drafts in draft:
-            number_of_drafts = drafts[0]
-        update = frappe.db.sql("""SELECT name,date,time,progress,progress_details FROM `tabProject Update` WHERE `tabProject Update`.project = %s AND date = DATE_ADD(CURDATE(), INTERVAL -1 DAY);""",project_name)
-        email_sending(project_name,frequency,date_start,date_end,progress,number_of_drafts,update)
+	project = frappe.db.sql(
+		"""SELECT `tabProject`.project_name,`tabProject`.frequency,`tabProject`.expected_start_date,`tabProject`.expected_end_date,`tabProject`.percent_complete FROM `tabProject`;"""
+	)
+	for projects in project:
+		project_name = projects[0]
+		frequency = projects[1]
+		date_start = projects[2]
+		date_end = projects[3]
+		progress = projects[4]
+		draft = frappe.db.sql(
+			"""SELECT count(docstatus) from `tabProject Update` WHERE `tabProject Update`.project = %s AND `tabProject Update`.docstatus = 0;""",
+			project_name,
+		)
+		for drafts in draft:
+			number_of_drafts = drafts[0]
+		update = frappe.db.sql(
+			"""SELECT name,date,time,progress,progress_details FROM `tabProject Update` WHERE `tabProject Update`.project = %s AND date = DATE_ADD(CURRENT_DATE, INTERVAL -1 DAY);""",
+			project_name,
+		)
+		email_sending(project_name, frequency, date_start, date_end, progress, number_of_drafts, update)
 
-def email_sending(project_name,frequency,date_start,date_end,progress,number_of_drafts,update):
 
-    holiday = frappe.db.sql("""SELECT holiday_date FROM `tabHoliday` where holiday_date = CURDATE();""")
-    msg = "<p>Project Name: " + project_name + "</p><p>Frequency: " + " " + frequency + "</p><p>Update Reminder:" + " " + str(date_start) + "</p><p>Expected Date End:" + " " + str(date_end) + "</p><p>Percent Progress:" + " " + str(progress) + "</p><p>Number of Updates:" + " " + str(len(update)) + "</p>" + "</p><p>Number of drafts:" + " " + str(number_of_drafts) + "</p>"
-    msg += """</u></b></p><table class='table table-bordered'><tr>
+def email_sending(
+	project_name, frequency, date_start, date_end, progress, number_of_drafts, update
+):
+
+	holiday = frappe.db.sql(
+		"""SELECT holiday_date FROM `tabHoliday` where holiday_date = CURRENT_DATE;"""
+	)
+	msg = (
+		"<p>Project Name: "
+		+ project_name
+		+ "</p><p>Frequency: "
+		+ " "
+		+ frequency
+		+ "</p><p>Update Reminder:"
+		+ " "
+		+ str(date_start)
+		+ "</p><p>Expected Date End:"
+		+ " "
+		+ str(date_end)
+		+ "</p><p>Percent Progress:"
+		+ " "
+		+ str(progress)
+		+ "</p><p>Number of Updates:"
+		+ " "
+		+ str(len(update))
+		+ "</p>"
+		+ "</p><p>Number of drafts:"
+		+ " "
+		+ str(number_of_drafts)
+		+ "</p>"
+	)
+	msg += """</u></b></p><table class='table table-bordered'><tr>
                 <th>Project ID</th><th>Date Updated</th><th>Time Updated</th><th>Project Status</th><th>Notes</th>"""
-    for updates in update:
-        msg += "<tr><td>" + str(updates[0]) + "</td><td>" + str(updates[1]) + "</td><td>" + str(updates[2]) + "</td><td>" + str(updates[3]) + "</td>" + "</td><td>" + str(updates[4]) + "</td></tr>"
+	for updates in update:
+		msg += (
+			"<tr><td>"
+			+ str(updates[0])
+			+ "</td><td>"
+			+ str(updates[1])
+			+ "</td><td>"
+			+ str(updates[2])
+			+ "</td><td>"
+			+ str(updates[3])
+			+ "</td>"
+			+ "</td><td>"
+			+ str(updates[4])
+			+ "</td></tr>"
+		)
 
-    msg += "</table>"
-    if len(holiday) == 0:
-        email = frappe.db.sql("""SELECT user from `tabProject User` WHERE parent = %s;""", project_name)
-        for emails in email:
-            frappe.sendmail(recipients=emails,subject=frappe._(project_name + ' ' + 'Summary'),message = msg)
-    else:
-    	pass
+	msg += "</table>"
+	if len(holiday) == 0:
+		email = frappe.db.sql("""SELECT user from `tabProject User` WHERE parent = %s;""", project_name)
+		for emails in email:
+			frappe.sendmail(
+				recipients=emails, subject=frappe._(project_name + " " + "Summary"), message=msg
+			)
+	else:
+		pass
diff --git a/erpnext/projects/doctype/project_update/test_project_update.py b/erpnext/projects/doctype/project_update/test_project_update.py
index f29c931..8663350 100644
--- a/erpnext/projects/doctype/project_update/test_project_update.py
+++ b/erpnext/projects/doctype/project_update/test_project_update.py
@@ -9,5 +9,6 @@
 class TestProjectUpdate(unittest.TestCase):
 	pass
 
-test_records = frappe.get_test_records('Project Update')
+
+test_records = frappe.get_test_records("Project Update")
 test_ignore = ["Sales Order"]
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index 8fa0538..0e409fc 100755
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -12,19 +12,24 @@
 from frappe.utils.nestedset import NestedSet
 
 
-class CircularReferenceError(frappe.ValidationError): pass
-class EndDateCannotBeGreaterThanProjectEndDateError(frappe.ValidationError): pass
+class CircularReferenceError(frappe.ValidationError):
+	pass
+
+
+class EndDateCannotBeGreaterThanProjectEndDateError(frappe.ValidationError):
+	pass
+
 
 class Task(NestedSet):
-	nsm_parent_field = 'parent_task'
+	nsm_parent_field = "parent_task"
 
 	def get_feed(self):
-		return '{0}: {1}'.format(_(self.status), self.subject)
+		return "{0}: {1}".format(_(self.status), self.subject)
 
 	def get_customer_details(self):
 		cust = frappe.db.sql("select customer_name from `tabCustomer` where name=%s", self.customer)
 		if cust:
-			ret = {'customer_name': cust and cust[0][0] or ''}
+			ret = {"customer_name": cust and cust[0][0] or ""}
 			return ret
 
 	def validate(self):
@@ -38,19 +43,37 @@
 		self.validate_completed_on()
 
 	def validate_dates(self):
-		if self.exp_start_date and self.exp_end_date and getdate(self.exp_start_date) > getdate(self.exp_end_date):
-			frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Expected Start Date"), \
-				frappe.bold("Expected End Date")))
+		if (
+			self.exp_start_date
+			and self.exp_end_date
+			and getdate(self.exp_start_date) > getdate(self.exp_end_date)
+		):
+			frappe.throw(
+				_("{0} can not be greater than {1}").format(
+					frappe.bold("Expected Start Date"), frappe.bold("Expected End Date")
+				)
+			)
 
-		if self.act_start_date and self.act_end_date and getdate(self.act_start_date) > getdate(self.act_end_date):
-			frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Actual Start Date"), \
-				frappe.bold("Actual End Date")))
+		if (
+			self.act_start_date
+			and self.act_end_date
+			and getdate(self.act_start_date) > getdate(self.act_end_date)
+		):
+			frappe.throw(
+				_("{0} can not be greater than {1}").format(
+					frappe.bold("Actual Start Date"), frappe.bold("Actual End Date")
+				)
+			)
 
 	def validate_parent_expected_end_date(self):
 		if self.parent_task:
 			parent_exp_end_date = frappe.db.get_value("Task", self.parent_task, "exp_end_date")
 			if parent_exp_end_date and getdate(self.get("exp_end_date")) > getdate(parent_exp_end_date):
-				frappe.throw(_("Expected End Date should be less than or equal to parent task's Expected End Date {0}.").format(getdate(parent_exp_end_date)))
+				frappe.throw(
+					_(
+						"Expected End Date should be less than or equal to parent task's Expected End Date {0}."
+					).format(getdate(parent_exp_end_date))
+				)
 
 	def validate_parent_project_dates(self):
 		if not self.project or frappe.flags.in_test:
@@ -59,16 +82,24 @@
 		expected_end_date = frappe.db.get_value("Project", self.project, "expected_end_date")
 
 		if expected_end_date:
-			validate_project_dates(getdate(expected_end_date), self, "exp_start_date", "exp_end_date", "Expected")
-			validate_project_dates(getdate(expected_end_date), self, "act_start_date", "act_end_date", "Actual")
+			validate_project_dates(
+				getdate(expected_end_date), self, "exp_start_date", "exp_end_date", "Expected"
+			)
+			validate_project_dates(
+				getdate(expected_end_date), self, "act_start_date", "act_end_date", "Actual"
+			)
 
 	def validate_status(self):
 		if self.is_template and self.status != "Template":
 			self.status = "Template"
-		if self.status!=self.get_db_value("status") and self.status == "Completed":
+		if self.status != self.get_db_value("status") and self.status == "Completed":
 			for d in self.depends_on:
 				if frappe.db.get_value("Task", d.task, "status") not in ("Completed", "Cancelled"):
-					frappe.throw(_("Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.").format(frappe.bold(self.name), frappe.bold(d.task)))
+					frappe.throw(
+						_(
+							"Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled."
+						).format(frappe.bold(self.name), frappe.bold(d.task))
+					)
 
 			close_all_assignments(self.doctype, self.name)
 
@@ -76,7 +107,7 @@
 		if flt(self.progress or 0) > 100:
 			frappe.throw(_("Progress % for a task cannot be more than 100."))
 
-		if self.status == 'Completed':
+		if self.status == "Completed":
 			self.progress = 100
 
 	def validate_dependencies_for_template_task(self):
@@ -126,34 +157,43 @@
 			clear(self.doctype, self.name)
 
 	def update_total_expense_claim(self):
-		self.total_expense_claim = frappe.db.sql("""select sum(total_sanctioned_amount) from `tabExpense Claim`
-			where project = %s and task = %s and docstatus=1""",(self.project, self.name))[0][0]
+		self.total_expense_claim = frappe.db.sql(
+			"""select sum(total_sanctioned_amount) from `tabExpense Claim`
+			where project = %s and task = %s and docstatus=1""",
+			(self.project, self.name),
+		)[0][0]
 
 	def update_time_and_costing(self):
-		tl = frappe.db.sql("""select min(from_time) as start_date, max(to_time) as end_date,
+		tl = frappe.db.sql(
+			"""select min(from_time) as start_date, max(to_time) as end_date,
 			sum(billing_amount) as total_billing_amount, sum(costing_amount) as total_costing_amount,
-			sum(hours) as time from `tabTimesheet Detail` where task = %s and docstatus=1"""
-			,self.name, as_dict=1)[0]
+			sum(hours) as time from `tabTimesheet Detail` where task = %s and docstatus=1""",
+			self.name,
+			as_dict=1,
+		)[0]
 		if self.status == "Open":
 			self.status = "Working"
-		self.total_costing_amount= tl.total_costing_amount
-		self.total_billing_amount= tl.total_billing_amount
-		self.actual_time= tl.time
-		self.act_start_date= tl.start_date
-		self.act_end_date= tl.end_date
+		self.total_costing_amount = tl.total_costing_amount
+		self.total_billing_amount = tl.total_billing_amount
+		self.actual_time = tl.time
+		self.act_start_date = tl.start_date
+		self.act_end_date = tl.end_date
 
 	def update_project(self):
 		if self.project and not self.flags.from_project:
 			frappe.get_cached_doc("Project", self.project).update_project()
 
 	def check_recursion(self):
-		if self.flags.ignore_recursion_check: return
-		check_list = [['task', 'parent'], ['parent', 'task']]
+		if self.flags.ignore_recursion_check:
+			return
+		check_list = [["task", "parent"], ["parent", "task"]]
 		for d in check_list:
 			task_list, count = [self.name], 0
-			while (len(task_list) > count ):
-				tasks = frappe.db.sql(" select %s from `tabTask Depends On` where %s = %s " %
-					(d[0], d[1], '%s'), cstr(task_list[count]))
+			while len(task_list) > count:
+				tasks = frappe.db.sql(
+					" select %s from `tabTask Depends On` where %s = %s " % (d[0], d[1], "%s"),
+					cstr(task_list[count]),
+				)
 				count = count + 1
 				for b in tasks:
 					if b[0] == self.name:
@@ -167,15 +207,24 @@
 	def reschedule_dependent_tasks(self):
 		end_date = self.exp_end_date or self.act_end_date
 		if end_date:
-			for task_name in frappe.db.sql("""
+			for task_name in frappe.db.sql(
+				"""
 				select name from `tabTask` as parent
 				where parent.project = %(project)s
 					and parent.name in (
 						select parent from `tabTask Depends On` as child
 						where child.task = %(task)s and child.project = %(project)s)
-			""", {'project': self.project, 'task':self.name }, as_dict=1):
+			""",
+				{"project": self.project, "task": self.name},
+				as_dict=1,
+			):
 				task = frappe.get_doc("Task", task_name.name)
-				if task.exp_start_date and task.exp_end_date and task.exp_start_date < getdate(end_date) and task.status == "Open":
+				if (
+					task.exp_start_date
+					and task.exp_end_date
+					and task.exp_start_date < getdate(end_date)
+					and task.status == "Open"
+				):
 					task_duration = date_diff(task.exp_end_date, task.exp_start_date)
 					task.exp_start_date = add_days(end_date, 1)
 					task.exp_end_date = add_days(task.exp_start_date, task_duration)
@@ -183,19 +232,19 @@
 					task.save()
 
 	def has_webform_permission(self):
-		project_user = frappe.db.get_value("Project User", {"parent": self.project, "user":frappe.session.user} , "user")
+		project_user = frappe.db.get_value(
+			"Project User", {"parent": self.project, "user": frappe.session.user}, "user"
+		)
 		if project_user:
 			return True
 
 	def populate_depends_on(self):
 		if self.parent_task:
-			parent = frappe.get_doc('Task', self.parent_task)
+			parent = frappe.get_doc("Task", self.parent_task)
 			if self.name not in [row.task for row in parent.depends_on]:
-				parent.append("depends_on", {
-					"doctype": "Task Depends On",
-					"task": self.name,
-					"subject": self.subject
-				})
+				parent.append(
+					"depends_on", {"doctype": "Task Depends On", "task": self.name, "subject": self.subject}
+				)
 				parent.save()
 
 	def on_trash(self):
@@ -208,12 +257,14 @@
 		self.update_project()
 
 	def update_status(self):
-		if self.status not in ('Cancelled', 'Completed') and self.exp_end_date:
+		if self.status not in ("Cancelled", "Completed") and self.exp_end_date:
 			from datetime import datetime
+
 			if self.exp_end_date < datetime.now().date():
-				self.db_set('status', 'Overdue', update_modified=False)
+				self.db_set("status", "Overdue", update_modified=False)
 				self.update_project()
 
+
 @frappe.whitelist()
 def check_if_child_exists(name):
 	child_tasks = frappe.get_all("Task", filters={"parent_task": name})
@@ -225,24 +276,29 @@
 @frappe.validate_and_sanitize_search_inputs
 def get_project(doctype, txt, searchfield, start, page_len, filters):
 	from erpnext.controllers.queries import get_match_cond
+
 	meta = frappe.get_meta(doctype)
 	searchfields = meta.get_search_fields()
-	search_columns = ", " + ", ".join(searchfields) if searchfields else ''
+	search_columns = ", " + ", ".join(searchfields) if searchfields else ""
 	search_cond = " or " + " or ".join(field + " like %(txt)s" for field in searchfields)
 
-	return frappe.db.sql(""" select name {search_columns} from `tabProject`
+	return frappe.db.sql(
+		""" select name {search_columns} from `tabProject`
 		where %(key)s like %(txt)s
 			%(mcond)s
 			{search_condition}
 		order by name
-		limit %(start)s, %(page_len)s""".format(search_columns = search_columns,
-			search_condition=search_cond), {
-			'key': searchfield,
-			'txt': '%' + txt + '%',
-			'mcond':get_match_cond(doctype),
-			'start': start,
-			'page_len': page_len
-		})
+		limit %(page_len)s offset %(start)s""".format(
+			search_columns=search_columns, search_condition=search_cond
+		),
+		{
+			"key": searchfield,
+			"txt": "%" + txt + "%",
+			"mcond": get_match_cond(doctype),
+			"start": start,
+			"page_len": page_len,
+		},
+	)
 
 
 @frappe.whitelist()
@@ -253,8 +309,13 @@
 		task.status = status
 		task.save()
 
+
 def set_tasks_as_overdue():
-	tasks = frappe.get_all("Task", filters={"status": ["not in", ["Cancelled", "Completed"]]}, fields=["name", "status", "review_date"])
+	tasks = frappe.get_all(
+		"Task",
+		filters={"status": ["not in", ["Cancelled", "Completed"]]},
+		fields=["name", "status", "review_date"],
+	)
 	for task in tasks:
 		if task.status == "Pending Review":
 			if getdate(task.review_date) > getdate(today()):
@@ -265,18 +326,24 @@
 @frappe.whitelist()
 def make_timesheet(source_name, target_doc=None, ignore_permissions=False):
 	def set_missing_values(source, target):
-		target.append("time_logs", {
-			"hours": source.actual_time,
-			"completed": source.status == "Completed",
-			"project": source.project,
-			"task": source.name
-		})
+		target.append(
+			"time_logs",
+			{
+				"hours": source.actual_time,
+				"completed": source.status == "Completed",
+				"project": source.project,
+				"task": source.name,
+			},
+		)
 
-	doclist = get_mapped_doc("Task", source_name, {
-			"Task": {
-				"doctype": "Timesheet"
-			}
-		}, target_doc, postprocess=set_missing_values, ignore_permissions=ignore_permissions)
+	doclist = get_mapped_doc(
+		"Task",
+		source_name,
+		{"Task": {"doctype": "Timesheet"}},
+		target_doc,
+		postprocess=set_missing_values,
+		ignore_permissions=ignore_permissions,
+	)
 
 	return doclist
 
@@ -284,60 +351,69 @@
 @frappe.whitelist()
 def get_children(doctype, parent, task=None, project=None, is_root=False):
 
-	filters = [['docstatus', '<', '2']]
+	filters = [["docstatus", "<", "2"]]
 
 	if task:
-		filters.append(['parent_task', '=', task])
+		filters.append(["parent_task", "=", task])
 	elif parent and not is_root:
 		# via expand child
-		filters.append(['parent_task', '=', parent])
+		filters.append(["parent_task", "=", parent])
 	else:
-		filters.append(['ifnull(`parent_task`, "")', '=', ''])
+		filters.append(['ifnull(`parent_task`, "")', "=", ""])
 
 	if project:
-		filters.append(['project', '=', project])
+		filters.append(["project", "=", project])
 
-	tasks = frappe.get_list(doctype, fields=[
-		'name as value',
-		'subject as title',
-		'is_group as expandable'
-	], filters=filters, order_by='name')
+	tasks = frappe.get_list(
+		doctype,
+		fields=["name as value", "subject as title", "is_group as expandable"],
+		filters=filters,
+		order_by="name",
+	)
 
 	# return tasks
 	return tasks
 
+
 @frappe.whitelist()
 def add_node():
 	from frappe.desk.treeview import make_tree_args
+
 	args = frappe.form_dict
-	args.update({
-		"name_field": "subject"
-	})
+	args.update({"name_field": "subject"})
 	args = make_tree_args(**args)
 
-	if args.parent_task == 'All Tasks' or args.parent_task == args.project:
+	if args.parent_task == "All Tasks" or args.parent_task == args.project:
 		args.parent_task = None
 
 	frappe.get_doc(args).insert()
 
+
 @frappe.whitelist()
 def add_multiple_tasks(data, parent):
 	data = json.loads(data)
-	new_doc = {'doctype': 'Task', 'parent_task': parent if parent!="All Tasks" else ""}
-	new_doc['project'] = frappe.db.get_value('Task', {"name": parent}, 'project') or ""
+	new_doc = {"doctype": "Task", "parent_task": parent if parent != "All Tasks" else ""}
+	new_doc["project"] = frappe.db.get_value("Task", {"name": parent}, "project") or ""
 
 	for d in data:
-		if not d.get("subject"): continue
-		new_doc['subject'] = d.get("subject")
+		if not d.get("subject"):
+			continue
+		new_doc["subject"] = d.get("subject")
 		new_task = frappe.get_doc(new_doc)
 		new_task.insert()
 
+
 def on_doctype_update():
 	frappe.db.add_index("Task", ["lft", "rgt"])
 
+
 def validate_project_dates(project_end_date, task, task_start, task_end, actual_or_expected_date):
 	if task.get(task_start) and date_diff(project_end_date, getdate(task.get(task_start))) < 0:
-		frappe.throw(_("Task's {0} Start Date cannot be after Project's End Date.").format(actual_or_expected_date))
+		frappe.throw(
+			_("Task's {0} Start Date cannot be after Project's End Date.").format(actual_or_expected_date)
+		)
 
 	if task.get(task_end) and date_diff(project_end_date, getdate(task.get(task_end))) < 0:
-		frappe.throw(_("Task's {0} End Date cannot be after Project's End Date.").format(actual_or_expected_date))
+		frappe.throw(
+			_("Task's {0} End Date cannot be after Project's End Date.").format(actual_or_expected_date)
+		)
diff --git a/erpnext/projects/doctype/task/task_dashboard.py b/erpnext/projects/doctype/task/task_dashboard.py
index f7470f8..07477da 100644
--- a/erpnext/projects/doctype/task/task_dashboard.py
+++ b/erpnext/projects/doctype/task/task_dashboard.py
@@ -3,15 +3,9 @@
 
 def get_data():
 	return {
-		'fieldname': 'task',
-		'transactions': [
-			{
-				'label': _('Activity'),
-				'items': ['Timesheet']
-			},
-			{
-				'label': _('Accounting'),
-				'items': ['Expense Claim']
-			}
-		]
+		"fieldname": "task",
+		"transactions": [
+			{"label": _("Activity"), "items": ["Timesheet"]},
+			{"label": _("Accounting"), "items": ["Expense Claim"]},
+		],
 	}
diff --git a/erpnext/projects/doctype/task/test_task.py b/erpnext/projects/doctype/task/test_task.py
index 5f5b519..c0333f8 100644
--- a/erpnext/projects/doctype/task/test_task.py
+++ b/erpnext/projects/doctype/task/test_task.py
@@ -16,9 +16,7 @@
 		task3 = create_task("_Test Task 3", add_days(nowdate(), 11), add_days(nowdate(), 15), task2.name)
 
 		task1.reload()
-		task1.append("depends_on", {
-			"task": task3.name
-		})
+		task1.append("depends_on", {"task": task3.name})
 
 		self.assertRaises(CircularReferenceError, task1.save)
 
@@ -27,9 +25,7 @@
 
 		task4 = create_task("_Test Task 4", nowdate(), add_days(nowdate(), 15), task1.name)
 
-		task3.append("depends_on", {
-			"task": task4.name
-		})
+		task3.append("depends_on", {"task": task4.name})
 
 	def test_reschedule_dependent_task(self):
 		project = frappe.get_value("Project", {"project_name": "_Test Project"})
@@ -44,20 +40,22 @@
 		task3.get("depends_on")[0].project = project
 		task3.save()
 
-		task1.update({
-			"exp_end_date": add_days(nowdate(), 20)
-		})
+		task1.update({"exp_end_date": add_days(nowdate(), 20)})
 		task1.save()
 
-		self.assertEqual(frappe.db.get_value("Task", task2.name, "exp_start_date"),
-			getdate(add_days(nowdate(), 21)))
-		self.assertEqual(frappe.db.get_value("Task", task2.name, "exp_end_date"),
-			getdate(add_days(nowdate(), 25)))
+		self.assertEqual(
+			frappe.db.get_value("Task", task2.name, "exp_start_date"), getdate(add_days(nowdate(), 21))
+		)
+		self.assertEqual(
+			frappe.db.get_value("Task", task2.name, "exp_end_date"), getdate(add_days(nowdate(), 25))
+		)
 
-		self.assertEqual(frappe.db.get_value("Task", task3.name, "exp_start_date"),
-			getdate(add_days(nowdate(), 26)))
-		self.assertEqual(frappe.db.get_value("Task", task3.name, "exp_end_date"),
-			getdate(add_days(nowdate(), 30)))
+		self.assertEqual(
+			frappe.db.get_value("Task", task3.name, "exp_start_date"), getdate(add_days(nowdate(), 26))
+		)
+		self.assertEqual(
+			frappe.db.get_value("Task", task3.name, "exp_end_date"), getdate(add_days(nowdate(), 30))
+		)
 
 	def test_close_assignment(self):
 		if not frappe.db.exists("Task", "Test Close Assignment"):
@@ -67,18 +65,27 @@
 
 		def assign():
 			from frappe.desk.form import assign_to
-			assign_to.add({
-				"assign_to": ["test@example.com"],
-				"doctype": task.doctype,
-				"name": task.name,
-				"description": "Close this task"
-			})
+
+			assign_to.add(
+				{
+					"assign_to": ["test@example.com"],
+					"doctype": task.doctype,
+					"name": task.name,
+					"description": "Close this task",
+				}
+			)
 
 		def get_owner_and_status():
-			return frappe.db.get_value("ToDo",
-				filters={"reference_type": task.doctype, "reference_name": task.name,
-					"description": "Close this task"},
-				fieldname=("allocated_to", "status"), as_dict=True)
+			return frappe.db.get_value(
+				"ToDo",
+				filters={
+					"reference_type": task.doctype,
+					"reference_name": task.name,
+					"description": "Close this task",
+				},
+				fieldname=("allocated_to", "status"),
+				as_dict=True,
+			)
 
 		assign()
 		todo = get_owner_and_status()
@@ -97,18 +104,36 @@
 		task = create_task("Testing Overdue", add_days(nowdate(), -10), add_days(nowdate(), -5))
 
 		from erpnext.projects.doctype.task.task import set_tasks_as_overdue
+
 		set_tasks_as_overdue()
 
 		self.assertEqual(frappe.db.get_value("Task", task.name, "status"), "Overdue")
 
-def create_task(subject, start=None, end=None, depends_on=None, project=None, parent_task=None, is_group=0, is_template=0, begin=0, duration=0, save=True):
+
+def create_task(
+	subject,
+	start=None,
+	end=None,
+	depends_on=None,
+	project=None,
+	parent_task=None,
+	is_group=0,
+	is_template=0,
+	begin=0,
+	duration=0,
+	save=True,
+):
 	if not frappe.db.exists("Task", subject):
-		task = frappe.new_doc('Task')
+		task = frappe.new_doc("Task")
 		task.status = "Open"
 		task.subject = subject
 		task.exp_start_date = start or nowdate()
 		task.exp_end_date = end or nowdate()
-		task.project = project or None if is_template else frappe.get_value("Project", {"project_name": "_Test Project"})
+		task.project = (
+			project or None
+			if is_template
+			else frappe.get_value("Project", {"project_name": "_Test Project"})
+		)
 		task.is_template = is_template
 		task.start = begin
 		task.duration = duration
@@ -120,9 +145,7 @@
 		task = frappe.get_doc("Task", subject)
 
 	if depends_on:
-		task.append("depends_on", {
-			"task": depends_on
-		})
+		task.append("depends_on", {"task": depends_on})
 		if save:
 			task.save()
 	return task
diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py
index 8b60357..57bfd5b 100644
--- a/erpnext/projects/doctype/timesheet/test_timesheet.py
+++ b/erpnext/projects/doctype/timesheet/test_timesheet.py
@@ -27,8 +27,8 @@
 class TestTimesheet(unittest.TestCase):
 	@classmethod
 	def setUpClass(cls):
-		make_earning_salary_component(setup=True, company_list=['_Test Company'])
-		make_deduction_salary_component(setup=True, company_list=['_Test Company'])
+		make_earning_salary_component(setup=True, company_list=["_Test Company"])
+		make_deduction_salary_component(setup=True, company_list=["_Test Company"])
 
 	def setUp(self):
 		for dt in ["Salary Slip", "Salary Structure", "Salary Structure Assignment", "Timesheet"]:
@@ -62,7 +62,7 @@
 		emp = make_employee("test_employee_6@salary.com", company="_Test Company")
 
 		salary_structure = make_salary_structure_for_timesheet(emp)
-		timesheet = make_timesheet(emp, simulate = True, is_billable=1)
+		timesheet = make_timesheet(emp, simulate=True, is_billable=1)
 		salary_slip = make_salary_slip(timesheet.name)
 		salary_slip.submit()
 
@@ -73,27 +73,27 @@
 		self.assertEqual(salary_slip.timesheets[0].time_sheet, timesheet.name)
 		self.assertEqual(salary_slip.timesheets[0].working_hours, 2)
 
-		timesheet = frappe.get_doc('Timesheet', timesheet.name)
-		self.assertEqual(timesheet.status, 'Payslip')
+		timesheet = frappe.get_doc("Timesheet", timesheet.name)
+		self.assertEqual(timesheet.status, "Payslip")
 		salary_slip.cancel()
 
-		timesheet = frappe.get_doc('Timesheet', timesheet.name)
-		self.assertEqual(timesheet.status, 'Submitted')
+		timesheet = frappe.get_doc("Timesheet", timesheet.name)
+		self.assertEqual(timesheet.status, "Submitted")
 
 	def test_sales_invoice_from_timesheet(self):
 		emp = make_employee("test_employee_6@salary.com")
 
 		timesheet = make_timesheet(emp, simulate=True, is_billable=1)
-		sales_invoice = make_sales_invoice(timesheet.name, '_Test Item', '_Test Customer')
+		sales_invoice = make_sales_invoice(timesheet.name, "_Test Item", "_Test Customer")
 		sales_invoice.due_date = nowdate()
 		sales_invoice.submit()
-		timesheet = frappe.get_doc('Timesheet', timesheet.name)
+		timesheet = frappe.get_doc("Timesheet", timesheet.name)
 		self.assertEqual(sales_invoice.total_billing_amount, 100)
-		self.assertEqual(timesheet.status, 'Billed')
-		self.assertEqual(sales_invoice.customer, '_Test Customer')
+		self.assertEqual(timesheet.status, "Billed")
+		self.assertEqual(sales_invoice.customer, "_Test Customer")
 
 		item = sales_invoice.items[0]
-		self.assertEqual(item.item_code, '_Test Item')
+		self.assertEqual(item.item_code, "_Test Item")
 		self.assertEqual(item.qty, 2.00)
 		self.assertEqual(item.rate, 50.00)
 
@@ -101,19 +101,21 @@
 		emp = make_employee("test_employee_6@salary.com")
 		project = frappe.get_value("Project", {"project_name": "_Test Project"})
 
-		timesheet = make_timesheet(emp, simulate=True, is_billable=1, project=project, company='_Test Company')
+		timesheet = make_timesheet(
+			emp, simulate=True, is_billable=1, project=project, company="_Test Company"
+		)
 		sales_invoice = create_sales_invoice(do_not_save=True)
 		sales_invoice.project = project
 		sales_invoice.submit()
 
-		ts = frappe.get_doc('Timesheet', timesheet.name)
+		ts = frappe.get_doc("Timesheet", timesheet.name)
 		self.assertEqual(ts.per_billed, 100)
 		self.assertEqual(ts.time_logs[0].sales_invoice, sales_invoice.name)
 
 	def test_timesheet_time_overlap(self):
 		emp = make_employee("test_employee_6@salary.com")
 
-		settings = frappe.get_single('Projects Settings')
+		settings = frappe.get_single("Projects Settings")
 		initial_setting = settings.ignore_employee_time_overlap
 		settings.ignore_employee_time_overlap = 0
 		settings.save()
@@ -122,24 +124,24 @@
 		timesheet = frappe.new_doc("Timesheet")
 		timesheet.employee = emp
 		timesheet.append(
-			'time_logs',
+			"time_logs",
 			{
 				"billable": 1,
 				"activity_type": "_Test Activity Type",
 				"from_time": now_datetime(),
 				"to_time": now_datetime() + datetime.timedelta(hours=3),
-				"company": "_Test Company"
-			}
+				"company": "_Test Company",
+			},
 		)
 		timesheet.append(
-			'time_logs',
+			"time_logs",
 			{
 				"billable": 1,
 				"activity_type": "_Test Activity Type",
 				"from_time": now_datetime(),
 				"to_time": now_datetime() + datetime.timedelta(hours=3),
-				"company": "_Test Company"
-			}
+				"company": "_Test Company",
+			},
 		)
 
 		self.assertRaises(frappe.ValidationError, timesheet.save)
@@ -158,27 +160,27 @@
 		timesheet = frappe.new_doc("Timesheet")
 		timesheet.employee = emp
 		timesheet.append(
-			'time_logs',
+			"time_logs",
 			{
 				"billable": 1,
 				"activity_type": "_Test Activity Type",
 				"from_time": now_datetime(),
 				"to_time": now_datetime() + datetime.timedelta(hours=3),
-				"company": "_Test Company"
-			}
+				"company": "_Test Company",
+			},
 		)
 		timesheet.append(
-			'time_logs',
+			"time_logs",
 			{
 				"billable": 1,
 				"activity_type": "_Test Activity Type",
 				"from_time": now_datetime() + datetime.timedelta(hours=3),
 				"to_time": now_datetime() + datetime.timedelta(hours=4),
-				"company": "_Test Company"
-			}
+				"company": "_Test Company",
+			},
 		)
 
-		timesheet.save() # should not throw an error
+		timesheet.save()  # should not throw an error
 
 	def test_to_time(self):
 		emp = make_employee("test_employee_6@salary.com")
@@ -187,14 +189,14 @@
 		timesheet = frappe.new_doc("Timesheet")
 		timesheet.employee = emp
 		timesheet.append(
-			'time_logs',
+			"time_logs",
 			{
 				"billable": 1,
 				"activity_type": "_Test Activity Type",
 				"from_time": from_time,
 				"hours": 2,
-				"company": "_Test Company"
-			}
+				"company": "_Test Company",
+			},
 		)
 		timesheet.save()
 
@@ -207,39 +209,51 @@
 	frequency = "Monthly"
 
 	if not frappe.db.exists("Salary Component", "Timesheet Component"):
-		frappe.get_doc({"doctype": "Salary Component", "salary_component": "Timesheet Component"}).insert()
+		frappe.get_doc(
+			{"doctype": "Salary Component", "salary_component": "Timesheet Component"}
+		).insert()
 
-	salary_structure = make_salary_structure(salary_structure_name, frequency, company=company, dont_submit=True)
+	salary_structure = make_salary_structure(
+		salary_structure_name, frequency, company=company, dont_submit=True
+	)
 	salary_structure.salary_component = "Timesheet Component"
 	salary_structure.salary_slip_based_on_timesheet = 1
 	salary_structure.hour_rate = 50.0
 	salary_structure.save()
 	salary_structure.submit()
 
-	if not frappe.db.get_value("Salary Structure Assignment",
-		{'employee':employee, 'docstatus': 1}):
-			frappe.db.set_value('Employee', employee, 'date_of_joining',
-				add_months(nowdate(), -5))
-			create_salary_structure_assignment(employee, salary_structure.name)
+	if not frappe.db.get_value("Salary Structure Assignment", {"employee": employee, "docstatus": 1}):
+		frappe.db.set_value("Employee", employee, "date_of_joining", add_months(nowdate(), -5))
+		create_salary_structure_assignment(employee, salary_structure.name)
 
 	return salary_structure
 
 
-def make_timesheet(employee, simulate=False, is_billable = 0, activity_type="_Test Activity Type", project=None, task=None, company=None):
+def make_timesheet(
+	employee,
+	simulate=False,
+	is_billable=0,
+	activity_type="_Test Activity Type",
+	project=None,
+	task=None,
+	company=None,
+):
 	update_activity_type(activity_type)
 	timesheet = frappe.new_doc("Timesheet")
 	timesheet.employee = employee
-	timesheet.company = company or '_Test Company'
-	timesheet_detail = timesheet.append('time_logs', {})
+	timesheet.company = company or "_Test Company"
+	timesheet_detail = timesheet.append("time_logs", {})
 	timesheet_detail.is_billable = is_billable
 	timesheet_detail.activity_type = activity_type
 	timesheet_detail.from_time = now_datetime()
 	timesheet_detail.hours = 2
-	timesheet_detail.to_time = timesheet_detail.from_time + datetime.timedelta(hours= timesheet_detail.hours)
+	timesheet_detail.to_time = timesheet_detail.from_time + datetime.timedelta(
+		hours=timesheet_detail.hours
+	)
 	timesheet_detail.project = project
 	timesheet_detail.task = task
 
-	for data in timesheet.get('time_logs'):
+	for data in timesheet.get("time_logs"):
 		if simulate:
 			while True:
 				try:
@@ -247,7 +261,7 @@
 					break
 				except OverlapError:
 					data.from_time = data.from_time + datetime.timedelta(minutes=10)
-					data.to_time = data.from_time + datetime.timedelta(hours= data.hours)
+					data.to_time = data.from_time + datetime.timedelta(hours=data.hours)
 		else:
 			timesheet.save(ignore_permissions=True)
 
@@ -255,7 +269,8 @@
 
 	return timesheet
 
+
 def update_activity_type(activity_type):
-	activity_type = frappe.get_doc('Activity Type',activity_type)
+	activity_type = frappe.get_doc("Activity Type", activity_type)
 	activity_type.billing_rate = 50.0
 	activity_type.save(ignore_permissions=True)
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index b44d501..88d5bee 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -14,8 +14,13 @@
 from erpnext.setup.utils import get_exchange_rate
 
 
-class OverlapError(frappe.ValidationError): pass
-class OverWorkLoggedError(frappe.ValidationError): pass
+class OverlapError(frappe.ValidationError):
+	pass
+
+
+class OverWorkLoggedError(frappe.ValidationError):
+	pass
+
 
 class Timesheet(Document):
 	def validate(self):
@@ -32,7 +37,7 @@
 
 	def set_employee_name(self):
 		if self.employee and not self.employee_name:
-			self.employee_name = frappe.db.get_value('Employee', self.employee, 'employee_name')
+			self.employee_name = frappe.db.get_value("Employee", self.employee, "employee_name")
 
 	def calculate_total_amounts(self):
 		self.total_hours = 0.0
@@ -70,11 +75,7 @@
 			args.billing_hours = 0
 
 	def set_status(self):
-		self.status = {
-			"0": "Draft",
-			"1": "Submitted",
-			"2": "Cancelled"
-		}[str(self.docstatus or 0)]
+		self.status = {"0": "Draft", "1": "Submitted", "2": "Cancelled"}[str(self.docstatus or 0)]
 
 		if self.per_billed == 100:
 			self.status = "Billed"
@@ -135,7 +136,7 @@
 				frappe.throw(_("To date cannot be before from date"))
 
 	def validate_time_logs(self):
-		for data in self.get('time_logs'):
+		for data in self.get("time_logs"):
 			self.set_to_time(data)
 			self.validate_overlap(data)
 			self.set_project(data)
@@ -150,7 +151,7 @@
 			data.to_time = _to_time
 
 	def validate_overlap(self, data):
-		settings = frappe.get_single('Projects Settings')
+		settings = frappe.get_single("Projects Settings")
 		self.validate_overlap_for("user", data, self.user, settings.ignore_user_time_overlap)
 		self.validate_overlap_for("employee", data, self.employee, settings.ignore_employee_time_overlap)
 
@@ -159,7 +160,11 @@
 
 	def validate_project(self, data):
 		if self.parent_project and self.parent_project != data.project:
-			frappe.throw(_("Row {0}: Project must be same as the one set in the Timesheet: {1}.").format(data.idx, self.parent_project))
+			frappe.throw(
+				_("Row {0}: Project must be same as the one set in the Timesheet: {1}.").format(
+					data.idx, self.parent_project
+				)
+			)
 
 	def validate_overlap_for(self, fieldname, args, value, ignore_validation=False):
 		if not value or ignore_validation:
@@ -167,8 +172,12 @@
 
 		existing = self.get_overlap_for(fieldname, args, value)
 		if existing:
-			frappe.throw(_("Row {0}: From Time and To Time of {1} is overlapping with {2}")
-				.format(args.idx, self.name, existing.name), OverlapError)
+			frappe.throw(
+				_("Row {0}: From Time and To Time of {1} is overlapping with {2}").format(
+					args.idx, self.name, existing.name
+				),
+				OverlapError,
+			)
 
 	def get_overlap_for(self, fieldname, args, value):
 		timesheet = frappe.qb.DocType("Timesheet")
@@ -179,20 +188,22 @@
 
 		existing = (
 			frappe.qb.from_(timesheet)
-				.join(timelog)
-				.on(timelog.parent == timesheet.name)
-				.select(timesheet.name.as_('name'), timelog.from_time.as_('from_time'), timelog.to_time.as_('to_time'))
-				.where(
-					(timelog.name != (args.name or "No Name"))
-					& (timesheet.name != (args.parent or "No Name"))
-					& (timesheet.docstatus < 2)
-					& (timesheet[fieldname] == value)
-					& (
-						((from_time > timelog.from_time) & (from_time < timelog.to_time))
-						| ((to_time > timelog.from_time) & (to_time < timelog.to_time))
-						| ((from_time <= timelog.from_time) & (to_time >= timelog.to_time))
-					)
+			.join(timelog)
+			.on(timelog.parent == timesheet.name)
+			.select(
+				timesheet.name.as_("name"), timelog.from_time.as_("from_time"), timelog.to_time.as_("to_time")
+			)
+			.where(
+				(timelog.name != (args.name or "No Name"))
+				& (timesheet.name != (args.parent or "No Name"))
+				& (timesheet.docstatus < 2)
+				& (timesheet[fieldname] == value)
+				& (
+					((from_time > timelog.from_time) & (from_time < timelog.to_time))
+					| ((to_time > timelog.from_time) & (to_time < timelog.to_time))
+					| ((from_time <= timelog.from_time) & (to_time >= timelog.to_time))
 				)
+			)
 		).run(as_dict=True)
 
 		if self.check_internal_overlap(fieldname, args):
@@ -202,8 +213,7 @@
 
 	def check_internal_overlap(self, fieldname, args):
 		for time_log in self.time_logs:
-			if not (time_log.from_time and time_log.to_time
-				and args.from_time and args.to_time):
+			if not (time_log.from_time and time_log.to_time and args.from_time and args.to_time):
 				continue
 
 			from_time = get_datetime(time_log.from_time)
@@ -211,10 +221,14 @@
 			args_from_time = get_datetime(args.from_time)
 			args_to_time = get_datetime(args.to_time)
 
-			if (args.get(fieldname) == time_log.get(fieldname)) and (args.idx != time_log.idx) and (
-				(args_from_time > from_time and args_from_time < to_time)
-				or (args_to_time > from_time and args_to_time < to_time)
-				or (args_from_time <= from_time and args_to_time >= to_time)
+			if (
+				(args.get(fieldname) == time_log.get(fieldname))
+				and (args.idx != time_log.idx)
+				and (
+					(args_from_time > from_time and args_from_time < to_time)
+					or (args_to_time > from_time and args_to_time < to_time)
+					or (args_from_time <= from_time and args_to_time >= to_time)
+				)
 			):
 				return True
 		return False
@@ -226,8 +240,12 @@
 				hours = data.billing_hours or 0
 				costing_hours = data.billing_hours or data.hours or 0
 				if rate:
-					data.billing_rate = flt(rate.get('billing_rate')) if flt(data.billing_rate) == 0 else data.billing_rate
-					data.costing_rate = flt(rate.get('costing_rate')) if flt(data.costing_rate) == 0 else data.costing_rate
+					data.billing_rate = (
+						flt(rate.get("billing_rate")) if flt(data.billing_rate) == 0 else data.billing_rate
+					)
+					data.costing_rate = (
+						flt(rate.get("costing_rate")) if flt(data.costing_rate) == 0 else data.costing_rate
+					)
 					data.billing_amount = data.billing_rate * hours
 					data.costing_amount = data.costing_rate * costing_hours
 
@@ -235,6 +253,7 @@
 		if not ts_detail.is_billable:
 			ts_detail.billing_rate = 0.0
 
+
 @frappe.whitelist()
 def get_projectwise_timesheet_data(project=None, parent=None, from_time=None, to_time=None):
 	condition = ""
@@ -269,22 +288,22 @@
 		ORDER BY tsd.from_time ASC
 	"""
 
-	filters = {
-		"project": project,
-		"parent": parent,
-		"from_time": from_time,
-		"to_time": to_time
-	}
+	filters = {"project": project, "parent": parent, "from_time": from_time, "to_time": to_time}
 
 	return frappe.db.sql(query, filters, as_dict=1)
 
 
 @frappe.whitelist()
 def get_timesheet_detail_rate(timelog, currency):
-	timelog_detail = frappe.db.sql("""SELECT tsd.billing_amount as billing_amount,
+	timelog_detail = frappe.db.sql(
+		"""SELECT tsd.billing_amount as billing_amount,
 		ts.currency as currency FROM `tabTimesheet Detail` tsd
 		INNER JOIN `tabTimesheet` ts ON ts.name=tsd.parent
-		WHERE tsd.name = '{0}'""".format(timelog), as_dict = 1)[0]
+		WHERE tsd.name = '{0}'""".format(
+			timelog
+		),
+		as_dict=1,
+	)[0]
 
 	if timelog_detail.currency:
 		exchange_rate = get_exchange_rate(timelog_detail.currency, currency)
@@ -292,44 +311,60 @@
 		return timelog_detail.billing_amount * exchange_rate
 	return timelog_detail.billing_amount
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_timesheet(doctype, txt, searchfield, start, page_len, filters):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	condition = ""
 	if filters.get("project"):
 		condition = "and tsd.project = %(project)s"
 
-	return frappe.db.sql("""select distinct tsd.parent from `tabTimesheet Detail` tsd,
+	return frappe.db.sql(
+		"""select distinct tsd.parent from `tabTimesheet Detail` tsd,
 			`tabTimesheet` ts where
 			ts.status in ('Submitted', 'Payslip') and tsd.parent = ts.name and
 			tsd.docstatus = 1 and ts.total_billable_amount > 0
 			and tsd.parent LIKE %(txt)s {condition}
-			order by tsd.parent limit %(start)s, %(page_len)s"""
-			.format(condition=condition), {
-				'txt': '%' + txt + '%',
-				"start": start, "page_len": page_len, 'project': filters.get("project")
-			})
+			order by tsd.parent limit %(page_len)s offset %(start)s""".format(
+			condition=condition
+		),
+		{
+			"txt": "%" + txt + "%",
+			"start": start,
+			"page_len": page_len,
+			"project": filters.get("project"),
+		},
+	)
+
 
 @frappe.whitelist()
 def get_timesheet_data(name, project):
 	data = None
-	if project and project!='':
+	if project and project != "":
 		data = get_projectwise_timesheet_data(project, name)
 	else:
-		data = frappe.get_all('Timesheet',
-			fields = ["(total_billable_amount - total_billed_amount) as billing_amt", "total_billable_hours as billing_hours"], filters = {'name': name})
+		data = frappe.get_all(
+			"Timesheet",
+			fields=[
+				"(total_billable_amount - total_billed_amount) as billing_amt",
+				"total_billable_hours as billing_hours",
+			],
+			filters={"name": name},
+		)
 	return {
-		'billing_hours': data[0].billing_hours if data else None,
-		'billing_amount': data[0].billing_amt if data else None,
-		'timesheet_detail': data[0].name if data and project and project!= '' else None
+		"billing_hours": data[0].billing_hours if data else None,
+		"billing_amount": data[0].billing_amt if data else None,
+		"timesheet_detail": data[0].name if data and project and project != "" else None,
 	}
 
+
 @frappe.whitelist()
 def make_sales_invoice(source_name, item_code=None, customer=None, currency=None):
 	target = frappe.new_doc("Sales Invoice")
-	timesheet = frappe.get_doc('Timesheet', source_name)
+	timesheet = frappe.get_doc("Timesheet", source_name)
 
 	if not timesheet.total_billable_hours:
 		frappe.throw(_("Invoice can't be made for zero billing hour"))
@@ -349,28 +384,28 @@
 		target.currency = currency
 
 	if item_code:
-		target.append('items', {
-			'item_code': item_code,
-			'qty': hours,
-			'rate': billing_rate
-		})
+		target.append("items", {"item_code": item_code, "qty": hours, "rate": billing_rate})
 
 	for time_log in timesheet.time_logs:
 		if time_log.is_billable:
-			target.append('timesheets', {
-				'time_sheet': timesheet.name,
-				'billing_hours': time_log.billing_hours,
-				'billing_amount': time_log.billing_amount,
-				'timesheet_detail': time_log.name,
-				'activity_type': time_log.activity_type,
-				'description': time_log.description
-			})
+			target.append(
+				"timesheets",
+				{
+					"time_sheet": timesheet.name,
+					"billing_hours": time_log.billing_hours,
+					"billing_amount": time_log.billing_amount,
+					"timesheet_detail": time_log.name,
+					"activity_type": time_log.activity_type,
+					"description": time_log.description,
+				},
+			)
 
 	target.run_method("calculate_billing_amount_for_timesheet")
 	target.run_method("set_missing_values")
 
 	return target
 
+
 @frappe.whitelist()
 def make_salary_slip(source_name, target_doc=None):
 	target = frappe.new_doc("Salary Slip")
@@ -379,8 +414,9 @@
 
 	return target
 
+
 def set_missing_values(time_sheet, target):
-	doc = frappe.get_doc('Timesheet', time_sheet)
+	doc = frappe.get_doc("Timesheet", time_sheet)
 	target.employee = doc.employee
 	target.employee_name = doc.employee_name
 	target.salary_slip_based_on_timesheet = 1
@@ -388,26 +424,33 @@
 	target.end_date = doc.end_date
 	target.posting_date = doc.modified
 	target.total_working_hours = doc.total_hours
-	target.append('timesheets', {
-		'time_sheet': doc.name,
-		'working_hours': doc.total_hours
-	})
+	target.append("timesheets", {"time_sheet": doc.name, "working_hours": doc.total_hours})
+
 
 @frappe.whitelist()
 def get_activity_cost(employee=None, activity_type=None, currency=None):
-	base_currency = frappe.defaults.get_global_default('currency')
-	rate = frappe.db.get_values("Activity Cost", {"employee": employee,
-		"activity_type": activity_type}, ["costing_rate", "billing_rate"], as_dict=True)
+	base_currency = frappe.defaults.get_global_default("currency")
+	rate = frappe.db.get_values(
+		"Activity Cost",
+		{"employee": employee, "activity_type": activity_type},
+		["costing_rate", "billing_rate"],
+		as_dict=True,
+	)
 	if not rate:
-		rate = frappe.db.get_values("Activity Type", {"activity_type": activity_type},
-			["costing_rate", "billing_rate"], as_dict=True)
-		if rate and currency and currency!=base_currency:
+		rate = frappe.db.get_values(
+			"Activity Type",
+			{"activity_type": activity_type},
+			["costing_rate", "billing_rate"],
+			as_dict=True,
+		)
+		if rate and currency and currency != base_currency:
 			exchange_rate = get_exchange_rate(base_currency, currency)
 			rate[0]["costing_rate"] = rate[0]["costing_rate"] * exchange_rate
 			rate[0]["billing_rate"] = rate[0]["billing_rate"] * exchange_rate
 
 	return rate[0] if rate else {}
 
+
 @frappe.whitelist()
 def get_events(start, end, filters=None):
 	"""Returns events for Gantt / Calendar view rendering.
@@ -417,9 +460,11 @@
 	"""
 	filters = json.loads(filters)
 	from frappe.desk.calendar import get_event_conditions
+
 	conditions = get_event_conditions("Timesheet", filters)
 
-	return frappe.db.sql("""select `tabTimesheet Detail`.name as name,
+	return frappe.db.sql(
+		"""select `tabTimesheet Detail`.name as name,
 			`tabTimesheet Detail`.docstatus as status, `tabTimesheet Detail`.parent as parent,
 			from_time as start_date, hours, activity_type,
 			`tabTimesheet Detail`.project, to_time as end_date,
@@ -428,29 +473,37 @@
 		where `tabTimesheet Detail`.parent = `tabTimesheet`.name
 			and `tabTimesheet`.docstatus < 2
 			and (from_time <= %(end)s and to_time >= %(start)s) {conditions} {match_cond}
-		""".format(conditions=conditions, match_cond = get_match_cond('Timesheet')),
-		{
-			"start": start,
-			"end": end
-		}, as_dict=True, update={"allDay": 0})
+		""".format(
+			conditions=conditions, match_cond=get_match_cond("Timesheet")
+		),
+		{"start": start, "end": end},
+		as_dict=True,
+		update={"allDay": 0},
+	)
 
-def get_timesheets_list(doctype, txt, filters, limit_start, limit_page_length=20, order_by="modified"):
+
+def get_timesheets_list(
+	doctype, txt, filters, limit_start, limit_page_length=20, order_by="modified"
+):
 	user = frappe.session.user
 	# find customer name from contact.
-	customer = ''
+	customer = ""
 	timesheets = []
 
-	contact = frappe.db.exists('Contact', {'user': user})
+	contact = frappe.db.exists("Contact", {"user": user})
 	if contact:
 		# find customer
-		contact = frappe.get_doc('Contact', contact)
-		customer = contact.get_link_for('Customer')
+		contact = frappe.get_doc("Contact", contact)
+		customer = contact.get_link_for("Customer")
 
 	if customer:
-		sales_invoices = [d.name for d in frappe.get_all('Sales Invoice', filters={'customer': customer})] or [None]
-		projects = [d.name for d in frappe.get_all('Project', filters={'customer': customer})]
+		sales_invoices = [
+			d.name for d in frappe.get_all("Sales Invoice", filters={"customer": customer})
+		] or [None]
+		projects = [d.name for d in frappe.get_all("Project", filters={"customer": customer})]
 		# Return timesheet related data to web portal.
-		timesheets = frappe.db.sql('''
+		timesheets = frappe.db.sql(
+			"""
 			SELECT
 				ts.name, tsd.activity_type, ts.status, ts.total_billable_hours,
 				COALESCE(ts.sales_invoice, tsd.sales_invoice) AS sales_invoice, tsd.project
@@ -462,17 +515,23 @@
 					tsd.project IN %(projects)s
 				)
 			ORDER BY `end_date` ASC
-			LIMIT {0}, {1}
-		'''.format(limit_start, limit_page_length), dict(sales_invoices=sales_invoices, projects=projects), as_dict=True) #nosec
+			LIMIT {1} offset {0}
+		""".format(
+				limit_start, limit_page_length
+			),
+			dict(sales_invoices=sales_invoices, projects=projects),
+			as_dict=True,
+		)  # nosec
 
 	return timesheets
 
+
 def get_list_context(context=None):
 	return {
 		"show_sidebar": True,
 		"show_search": True,
-		'no_breadcrumbs': True,
+		"no_breadcrumbs": True,
 		"title": _("Timesheets"),
 		"get_list": get_timesheets_list,
-		"row_template": "templates/includes/timesheet/timesheet_row.html"
+		"row_template": "templates/includes/timesheet/timesheet_row.html",
 	}
diff --git a/erpnext/projects/doctype/timesheet/timesheet_dashboard.py b/erpnext/projects/doctype/timesheet/timesheet_dashboard.py
index 15fe797..6d6b57b 100644
--- a/erpnext/projects/doctype/timesheet/timesheet_dashboard.py
+++ b/erpnext/projects/doctype/timesheet/timesheet_dashboard.py
@@ -3,11 +3,6 @@
 
 def get_data():
 	return {
-		'fieldname': 'time_sheet',
-		'transactions': [
-			{
-				'label': _('References'),
-				'items': ['Sales Invoice', 'Salary Slip']
-			}
-		]
+		"fieldname": "time_sheet",
+		"transactions": [{"label": _("References"), "items": ["Sales Invoice", "Salary Slip"]}],
 	}
diff --git a/erpnext/projects/report/billing_summary.py b/erpnext/projects/report/billing_summary.py
index 46479d0..bc8f2af 100644
--- a/erpnext/projects/report/billing_summary.py
+++ b/erpnext/projects/report/billing_summary.py
@@ -2,7 +2,6 @@
 # For license information, please see license.txt
 
 
-
 import frappe
 from frappe import _
 from frappe.utils import flt, time_diff_in_hours
@@ -15,52 +14,45 @@
 			"fieldtype": "Link",
 			"fieldname": "employee",
 			"options": "Employee",
-			"width": 300
+			"width": 300,
 		},
 		{
 			"label": _("Employee Name"),
 			"fieldtype": "data",
 			"fieldname": "employee_name",
 			"hidden": 1,
-			"width": 200
+			"width": 200,
 		},
 		{
 			"label": _("Timesheet"),
 			"fieldtype": "Link",
 			"fieldname": "timesheet",
 			"options": "Timesheet",
-			"width": 150
+			"width": 150,
 		},
-		{
-			"label": _("Working Hours"),
-			"fieldtype": "Float",
-			"fieldname": "total_hours",
-			"width": 150
-		},
+		{"label": _("Working Hours"), "fieldtype": "Float", "fieldname": "total_hours", "width": 150},
 		{
 			"label": _("Billable Hours"),
 			"fieldtype": "Float",
 			"fieldname": "total_billable_hours",
-			"width": 150
+			"width": 150,
 		},
-		{
-			"label": _("Billing Amount"),
-			"fieldtype": "Currency",
-			"fieldname": "amount",
-			"width": 150
-		}
+		{"label": _("Billing Amount"), "fieldtype": "Currency", "fieldname": "amount", "width": 150},
 	]
 
+
 def get_data(filters):
 	data = []
-	if(filters.from_date > filters.to_date):
+	if filters.from_date > filters.to_date:
 		frappe.msgprint(_("From Date can not be greater than To Date"))
 		return data
 
 	timesheets = get_timesheets(filters)
 
 	filters.from_date = frappe.utils.get_datetime(filters.from_date)
-	filters.to_date = frappe.utils.add_to_date(frappe.utils.get_datetime(filters.to_date), days=1, seconds=-1)
+	filters.to_date = frappe.utils.add_to_date(
+		frappe.utils.get_datetime(filters.to_date), days=1, seconds=-1
+	)
 
 	timesheet_details = get_timesheet_details(filters, timesheets.keys())
 
@@ -88,46 +80,58 @@
 			total_amount += billing_duration * flt(row.billing_rate)
 
 		if total_hours:
-			data.append({
-				"employee": timesheets.get(ts).employee,
-				"employee_name": timesheets.get(ts).employee_name,
-				"timesheet": ts,
-				"total_billable_hours": total_billing_hours,
-				"total_hours": total_hours,
-				"amount": total_amount
-			})
+			data.append(
+				{
+					"employee": timesheets.get(ts).employee,
+					"employee_name": timesheets.get(ts).employee_name,
+					"timesheet": ts,
+					"total_billable_hours": total_billing_hours,
+					"total_hours": total_hours,
+					"amount": total_amount,
+				}
+			)
 
 	return data
 
+
 def get_timesheets(filters):
 	record_filters = [
-			["start_date", "<=", filters.to_date],
-			["end_date", ">=", filters.from_date],
-			["docstatus", "=", 1]
-		]
+		["start_date", "<=", filters.to_date],
+		["end_date", ">=", filters.from_date],
+		["docstatus", "=", 1],
+	]
 
 	if "employee" in filters:
 		record_filters.append(["employee", "=", filters.employee])
 
-	timesheets = frappe.get_all("Timesheet", filters=record_filters, fields=["employee", "employee_name", "name"])
+	timesheets = frappe.get_all(
+		"Timesheet", filters=record_filters, fields=["employee", "employee_name", "name"]
+	)
 	timesheet_map = frappe._dict()
 	for d in timesheets:
 		timesheet_map.setdefault(d.name, d)
 
 	return timesheet_map
 
+
 def get_timesheet_details(filters, timesheet_list):
-	timesheet_details_filter = {
-		"parent": ["in", timesheet_list]
-	}
+	timesheet_details_filter = {"parent": ["in", timesheet_list]}
 
 	if "project" in filters:
 		timesheet_details_filter["project"] = filters.project
 
 	timesheet_details = frappe.get_all(
 		"Timesheet Detail",
-		filters = timesheet_details_filter,
-		fields=["from_time", "to_time", "hours", "is_billable", "billing_hours", "billing_rate", "parent"]
+		filters=timesheet_details_filter,
+		fields=[
+			"from_time",
+			"to_time",
+			"hours",
+			"is_billable",
+			"billing_hours",
+			"billing_rate",
+			"parent",
+		],
 	)
 
 	timesheet_details_map = frappe._dict()
@@ -136,6 +140,7 @@
 
 	return timesheet_details_map
 
+
 def get_billable_and_total_duration(activity, start_time, end_time):
 	precision = frappe.get_precision("Timesheet Detail", "hours")
 	activity_duration = time_diff_in_hours(end_time, start_time)
diff --git a/erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.py b/erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.py
index f733768..b31a063 100644
--- a/erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.py
+++ b/erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.py
@@ -20,21 +20,37 @@
 
 	return columns, data
 
+
 def get_column():
-	return [_("Timesheet") + ":Link/Timesheet:120", _("Employee") + "::150", _("Employee Name") + "::150",
-		_("From Datetime") + "::140", _("To Datetime") + "::140", _("Hours") + "::70",
-		_("Activity Type") + "::120", _("Task") + ":Link/Task:150",
-		_("Project") + ":Link/Project:120", _("Status") + "::70"]
+	return [
+		_("Timesheet") + ":Link/Timesheet:120",
+		_("Employee") + "::150",
+		_("Employee Name") + "::150",
+		_("From Datetime") + "::140",
+		_("To Datetime") + "::140",
+		_("Hours") + "::70",
+		_("Activity Type") + "::120",
+		_("Task") + ":Link/Task:150",
+		_("Project") + ":Link/Project:120",
+		_("Status") + "::70",
+	]
+
 
 def get_data(conditions, filters):
-	time_sheet = frappe.db.sql(""" select `tabTimesheet`.name, `tabTimesheet`.employee, `tabTimesheet`.employee_name,
+	time_sheet = frappe.db.sql(
+		""" select `tabTimesheet`.name, `tabTimesheet`.employee, `tabTimesheet`.employee_name,
 		`tabTimesheet Detail`.from_time, `tabTimesheet Detail`.to_time, `tabTimesheet Detail`.hours,
 		`tabTimesheet Detail`.activity_type, `tabTimesheet Detail`.task, `tabTimesheet Detail`.project,
 		`tabTimesheet`.status from `tabTimesheet Detail`, `tabTimesheet` where
-		`tabTimesheet Detail`.parent = `tabTimesheet`.name and %s order by `tabTimesheet`.name"""%(conditions), filters, as_list=1)
+		`tabTimesheet Detail`.parent = `tabTimesheet`.name and %s order by `tabTimesheet`.name"""
+		% (conditions),
+		filters,
+		as_list=1,
+	)
 
 	return time_sheet
 
+
 def get_conditions(filters):
 	conditions = "`tabTimesheet`.docstatus = 1"
 	if filters.get("from_date"):
diff --git a/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py
index 3ab2bb6..17e3155 100644
--- a/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py
+++ b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py
@@ -3,6 +3,7 @@
 
 
 import frappe
+from frappe import _
 from frappe.utils import date_diff, nowdate
 
 
@@ -13,14 +14,24 @@
 	charts = get_chart_data(data)
 	return columns, data, None, charts
 
+
 def get_data(filters):
 	conditions = get_conditions(filters)
-	tasks = frappe.get_all("Task",
-			filters = conditions,
-			fields = ["name", "subject", "exp_start_date", "exp_end_date",
-					"status", "priority", "completed_on", "progress"],
-			order_by="creation"
-		)
+	tasks = frappe.get_all(
+		"Task",
+		filters=conditions,
+		fields=[
+			"name",
+			"subject",
+			"exp_start_date",
+			"exp_end_date",
+			"status",
+			"priority",
+			"completed_on",
+			"progress",
+		],
+		order_by="creation",
+	)
 	for task in tasks:
 		if task.exp_end_date:
 			if task.completed_on:
@@ -39,6 +50,7 @@
 	tasks.sort(key=lambda x: x["delay"], reverse=True)
 	return tasks
 
+
 def get_conditions(filters):
 	conditions = frappe._dict()
 	keys = ["priority", "status"]
@@ -51,6 +63,7 @@
 		conditions.exp_start_date = ["<=", filters.get("to_date")]
 	return conditions
 
+
 def get_chart_data(data):
 	delay, on_track = 0, 0
 	for entry in data:
@@ -61,74 +74,34 @@
 	charts = {
 		"data": {
 			"labels": ["On Track", "Delayed"],
-			"datasets": [
-				{
-					"name": "Delayed",
-					"values": [on_track, delay]
-				}
-			]
+			"datasets": [{"name": "Delayed", "values": [on_track, delay]}],
 		},
 		"type": "percentage",
-		"colors": ["#84D5BA", "#CB4B5F"]
+		"colors": ["#84D5BA", "#CB4B5F"],
 	}
 	return charts
 
+
 def get_columns():
 	columns = [
-		{
-			"fieldname": "name",
-			"fieldtype": "Link",
-			"label": "Task",
-			"options": "Task",
-			"width": 150
-		},
-		{
-			"fieldname": "subject",
-			"fieldtype": "Data",
-			"label": "Subject",
-			"width": 200
-		},
-		{
-			"fieldname": "status",
-			"fieldtype": "Data",
-			"label": "Status",
-			"width": 100
-		},
-		{
-			"fieldname": "priority",
-			"fieldtype": "Data",
-			"label": "Priority",
-			"width": 80
-		},
-		{
-			"fieldname": "progress",
-			"fieldtype": "Data",
-			"label": "Progress (%)",
-			"width": 120
-		},
+		{"fieldname": "name", "fieldtype": "Link", "label": _("Task"), "options": "Task", "width": 150},
+		{"fieldname": "subject", "fieldtype": "Data", "label": _("Subject"), "width": 200},
+		{"fieldname": "status", "fieldtype": "Data", "label": _("Status"), "width": 100},
+		{"fieldname": "priority", "fieldtype": "Data", "label": _("Priority"), "width": 80},
+		{"fieldname": "progress", "fieldtype": "Data", "label": _("Progress (%)"), "width": 120},
 		{
 			"fieldname": "exp_start_date",
 			"fieldtype": "Date",
-			"label": "Expected Start Date",
-			"width": 150
+			"label": _("Expected Start Date"),
+			"width": 150,
 		},
 		{
 			"fieldname": "exp_end_date",
 			"fieldtype": "Date",
-			"label": "Expected End Date",
-			"width": 150
+			"label": _("Expected End Date"),
+			"width": 150,
 		},
-		{
-			"fieldname": "completed_on",
-			"fieldtype": "Date",
-			"label": "Actual End Date",
-			"width": 130
-		},
-		{
-			"fieldname": "delay",
-			"fieldtype": "Data",
-			"label": "Delay (In Days)",
-			"width": 120
-		}
+		{"fieldname": "completed_on", "fieldtype": "Date", "label": _("Actual End Date"), "width": 130},
+		{"fieldname": "delay", "fieldtype": "Data", "label": _("Delay (In Days)"), "width": 120},
 	]
 	return columns
diff --git a/erpnext/projects/report/delayed_tasks_summary/test_delayed_tasks_summary.py b/erpnext/projects/report/delayed_tasks_summary/test_delayed_tasks_summary.py
index e2ba7c2..91a0607 100644
--- a/erpnext/projects/report/delayed_tasks_summary/test_delayed_tasks_summary.py
+++ b/erpnext/projects/report/delayed_tasks_summary/test_delayed_tasks_summary.py
@@ -18,25 +18,17 @@
 		task1.save()
 
 	def test_delayed_tasks_summary(self):
-		filters = frappe._dict({
-			"from_date": add_months(nowdate(), -1),
-			"to_date": nowdate(),
-			"priority": "Low",
-			"status": "Open"
-		})
-		expected_data = [
+		filters = frappe._dict(
 			{
-				"subject": "_Test Task 99",
+				"from_date": add_months(nowdate(), -1),
+				"to_date": nowdate(),
+				"priority": "Low",
 				"status": "Open",
-				"priority": "Low",
-				"delay": 1
-			},
-			{
-				"subject": "_Test Task 98",
-				"status": "Completed",
-				"priority": "Low",
-				"delay": -1
 			}
+		)
+		expected_data = [
+			{"subject": "_Test Task 99", "status": "Open", "priority": "Low", "delay": 1},
+			{"subject": "_Test Task 98", "status": "Completed", "priority": "Low", "delay": -1},
 		]
 		report = execute(filters)
 		data = list(filter(lambda x: x.subject == "_Test Task 99", report[1]))[0]
diff --git a/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py b/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py
index dd4f8ea..a89e6f0 100644
--- a/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py
+++ b/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py
@@ -10,8 +10,10 @@
 def execute(filters=None):
 	return EmployeeHoursReport(filters).run()
 
+
 class EmployeeHoursReport:
-	'''Employee Hours Utilization Report Based On Timesheet'''
+	"""Employee Hours Utilization Report Based On Timesheet"""
+
 	def __init__(self, filters=None):
 		self.filters = frappe._dict(filters or {})
 
@@ -25,13 +27,17 @@
 		self.day_span = (self.to_date - self.from_date).days
 
 		if self.day_span <= 0:
-			frappe.throw(_('From Date must come before To Date'))
+			frappe.throw(_("From Date must come before To Date"))
 
 	def validate_standard_working_hours(self):
-		self.standard_working_hours = frappe.db.get_single_value('HR Settings', 'standard_working_hours')
+		self.standard_working_hours = frappe.db.get_single_value("HR Settings", "standard_working_hours")
 		if not self.standard_working_hours:
-			msg = _('The metrics for this report are calculated based on the Standard Working Hours. Please set {0} in {1}.').format(
-				frappe.bold('Standard Working Hours'), frappe.utils.get_link_to_form('HR Settings', 'HR Settings'))
+			msg = _(
+				"The metrics for this report are calculated based on the Standard Working Hours. Please set {0} in {1}."
+			).format(
+				frappe.bold("Standard Working Hours"),
+				frappe.utils.get_link_to_form("HR Settings", "HR Settings"),
+			)
 
 			frappe.throw(msg)
 
@@ -46,55 +52,50 @@
 	def generate_columns(self):
 		self.columns = [
 			{
-				'label': _('Employee'),
-				'options': 'Employee',
-				'fieldname': 'employee',
-				'fieldtype': 'Link',
-				'width': 230
+				"label": _("Employee"),
+				"options": "Employee",
+				"fieldname": "employee",
+				"fieldtype": "Link",
+				"width": 230,
 			},
 			{
-				'label': _('Department'),
-				'options': 'Department',
-				'fieldname': 'department',
-				'fieldtype': 'Link',
-				'width': 120
+				"label": _("Department"),
+				"options": "Department",
+				"fieldname": "department",
+				"fieldtype": "Link",
+				"width": 120,
+			},
+			{"label": _("Total Hours (T)"), "fieldname": "total_hours", "fieldtype": "Float", "width": 120},
+			{
+				"label": _("Billed Hours (B)"),
+				"fieldname": "billed_hours",
+				"fieldtype": "Float",
+				"width": 170,
 			},
 			{
-				'label': _('Total Hours (T)'),
-				'fieldname': 'total_hours',
-				'fieldtype': 'Float',
-				'width': 120
+				"label": _("Non-Billed Hours (NB)"),
+				"fieldname": "non_billed_hours",
+				"fieldtype": "Float",
+				"width": 170,
 			},
 			{
-				'label': _('Billed Hours (B)'),
-				'fieldname': 'billed_hours',
-				'fieldtype': 'Float',
-				'width': 170
+				"label": _("Untracked Hours (U)"),
+				"fieldname": "untracked_hours",
+				"fieldtype": "Float",
+				"width": 170,
 			},
 			{
-				'label': _('Non-Billed Hours (NB)'),
-				'fieldname': 'non_billed_hours',
-				'fieldtype': 'Float',
-				'width': 170
+				"label": _("% Utilization (B + NB) / T"),
+				"fieldname": "per_util",
+				"fieldtype": "Percentage",
+				"width": 200,
 			},
 			{
-				'label': _('Untracked Hours (U)'),
-				'fieldname': 'untracked_hours',
-				'fieldtype': 'Float',
-				'width': 170
+				"label": _("% Utilization (B / T)"),
+				"fieldname": "per_util_billed_only",
+				"fieldtype": "Percentage",
+				"width": 200,
 			},
-			{
-				'label': _('% Utilization (B + NB) / T'),
-				'fieldname': 'per_util',
-				'fieldtype': 'Percentage',
-				'width': 200
-			},
-			{
-				'label': _('% Utilization (B / T)'),
-				'fieldname': 'per_util_billed_only',
-				'fieldtype': 'Percentage',
-				'width': 200
-			}
 		]
 
 	def generate_data(self):
@@ -111,35 +112,36 @@
 
 		for emp, data in self.stats_by_employee.items():
 			row = frappe._dict()
-			row['employee'] = emp
+			row["employee"] = emp
 			row.update(data)
 			self.data.append(row)
 
 		#  Sort by descending order of percentage utilization
-		self.data.sort(key=lambda x: x['per_util'], reverse=True)
+		self.data.sort(key=lambda x: x["per_util"], reverse=True)
 
 	def filter_stats_by_department(self):
 		filtered_data = frappe._dict()
 		for emp, data in self.stats_by_employee.items():
-			if data['department'] == self.filters.department:
+			if data["department"] == self.filters.department:
 				filtered_data[emp] = data
 
 		# Update stats
 		self.stats_by_employee = filtered_data
 
 	def generate_filtered_time_logs(self):
-		additional_filters = ''
+		additional_filters = ""
 
-		filter_fields = ['employee', 'project', 'company']
+		filter_fields = ["employee", "project", "company"]
 
 		for field in filter_fields:
 			if self.filters.get(field):
-				if field == 'project':
+				if field == "project":
 					additional_filters += f"AND ttd.{field} = '{self.filters.get(field)}'"
 				else:
 					additional_filters += f"AND tt.{field} = '{self.filters.get(field)}'"
 
-		self.filtered_time_logs = frappe.db.sql('''
+		self.filtered_time_logs = frappe.db.sql(
+			"""
 			SELECT tt.employee AS employee, ttd.hours AS hours, ttd.is_billable AS is_billable, ttd.project AS project
 			FROM `tabTimesheet Detail` AS ttd
 			JOIN `tabTimesheet` AS tt
@@ -148,47 +150,46 @@
 			AND tt.start_date BETWEEN '{0}' AND '{1}'
 			AND tt.end_date BETWEEN '{0}' AND '{1}'
 			{2}
-		'''.format(self.filters.from_date, self.filters.to_date, additional_filters))
+		""".format(
+				self.filters.from_date, self.filters.to_date, additional_filters
+			)
+		)
 
 	def generate_stats_by_employee(self):
 		self.stats_by_employee = frappe._dict()
 
 		for emp, hours, is_billable, project in self.filtered_time_logs:
-			self.stats_by_employee.setdefault(
-				emp, frappe._dict()
-			).setdefault('billed_hours', 0.0)
+			self.stats_by_employee.setdefault(emp, frappe._dict()).setdefault("billed_hours", 0.0)
 
-			self.stats_by_employee[emp].setdefault('non_billed_hours', 0.0)
+			self.stats_by_employee[emp].setdefault("non_billed_hours", 0.0)
 
 			if is_billable:
-				self.stats_by_employee[emp]['billed_hours'] += flt(hours, 2)
+				self.stats_by_employee[emp]["billed_hours"] += flt(hours, 2)
 			else:
-				self.stats_by_employee[emp]['non_billed_hours'] += flt(hours, 2)
+				self.stats_by_employee[emp]["non_billed_hours"] += flt(hours, 2)
 
 	def set_employee_department_and_name(self):
 		for emp in self.stats_by_employee:
-			emp_name = frappe.db.get_value(
-				'Employee', emp, 'employee_name'
-			)
-			emp_dept = frappe.db.get_value(
-				'Employee', emp, 'department'
-			)
+			emp_name = frappe.db.get_value("Employee", emp, "employee_name")
+			emp_dept = frappe.db.get_value("Employee", emp, "department")
 
-			self.stats_by_employee[emp]['department'] = emp_dept
-			self.stats_by_employee[emp]['employee_name'] = emp_name
+			self.stats_by_employee[emp]["department"] = emp_dept
+			self.stats_by_employee[emp]["employee_name"] = emp_name
 
 	def calculate_utilizations(self):
 		TOTAL_HOURS = flt(self.standard_working_hours * self.day_span, 2)
 		for emp, data in self.stats_by_employee.items():
-			data['total_hours'] = TOTAL_HOURS
-			data['untracked_hours'] = flt(TOTAL_HOURS - data['billed_hours'] - data['non_billed_hours'], 2)
+			data["total_hours"] = TOTAL_HOURS
+			data["untracked_hours"] = flt(TOTAL_HOURS - data["billed_hours"] - data["non_billed_hours"], 2)
 
 			# To handle overtime edge-case
-			if data['untracked_hours'] < 0:
-				data['untracked_hours'] = 0.0
+			if data["untracked_hours"] < 0:
+				data["untracked_hours"] = 0.0
 
-			data['per_util'] = flt(((data['billed_hours'] + data['non_billed_hours']) / TOTAL_HOURS) * 100, 2)
-			data['per_util_billed_only'] = flt((data['billed_hours'] / TOTAL_HOURS) * 100, 2)
+			data["per_util"] = flt(
+				((data["billed_hours"] + data["non_billed_hours"]) / TOTAL_HOURS) * 100, 2
+			)
+			data["per_util_billed_only"] = flt((data["billed_hours"] / TOTAL_HOURS) * 100, 2)
 
 	def generate_report_summary(self):
 		self.report_summary = []
@@ -202,11 +203,11 @@
 		total_untracked = 0.0
 
 		for row in self.data:
-			avg_utilization += row['per_util']
-			avg_utilization_billed_only += row['per_util_billed_only']
-			total_billed += row['billed_hours']
-			total_non_billed += row['non_billed_hours']
-			total_untracked += row['untracked_hours']
+			avg_utilization += row["per_util"]
+			avg_utilization_billed_only += row["per_util_billed_only"]
+			total_billed += row["billed_hours"]
+			total_non_billed += row["non_billed_hours"]
+			total_untracked += row["untracked_hours"]
 
 		avg_utilization /= len(self.data)
 		avg_utilization = flt(avg_utilization, 2)
@@ -217,27 +218,19 @@
 		THRESHOLD_PERCENTAGE = 70.0
 		self.report_summary = [
 			{
-				'value': f'{avg_utilization}%',
-				'indicator': 'Red' if avg_utilization < THRESHOLD_PERCENTAGE else 'Green',
-				'label': _('Avg Utilization'),
-				'datatype': 'Percentage'
+				"value": f"{avg_utilization}%",
+				"indicator": "Red" if avg_utilization < THRESHOLD_PERCENTAGE else "Green",
+				"label": _("Avg Utilization"),
+				"datatype": "Percentage",
 			},
 			{
-				'value': f'{avg_utilization_billed_only}%',
-				'indicator': 'Red' if avg_utilization_billed_only < THRESHOLD_PERCENTAGE else 'Green',
-				'label': _('Avg Utilization (Billed Only)'),
-				'datatype': 'Percentage'
+				"value": f"{avg_utilization_billed_only}%",
+				"indicator": "Red" if avg_utilization_billed_only < THRESHOLD_PERCENTAGE else "Green",
+				"label": _("Avg Utilization (Billed Only)"),
+				"datatype": "Percentage",
 			},
-			{
-				'value': total_billed,
-				'label': _('Total Billed Hours'),
-				'datatype': 'Float'
-			},
-			{
-				'value': total_non_billed,
-				'label': _('Total Non-Billed Hours'),
-				'datatype': 'Float'
-			}
+			{"value": total_billed, "label": _("Total Billed Hours"), "datatype": "Float"},
+			{"value": total_non_billed, "label": _("Total Non-Billed Hours"), "datatype": "Float"},
 		]
 
 	def generate_chart_data(self):
@@ -248,33 +241,21 @@
 		non_billed_hours = []
 		untracked_hours = []
 
-
 		for row in self.data:
-			labels.append(row.get('employee_name'))
-			billed_hours.append(row.get('billed_hours'))
-			non_billed_hours.append(row.get('non_billed_hours'))
-			untracked_hours.append(row.get('untracked_hours'))
+			labels.append(row.get("employee_name"))
+			billed_hours.append(row.get("billed_hours"))
+			non_billed_hours.append(row.get("non_billed_hours"))
+			untracked_hours.append(row.get("untracked_hours"))
 
 		self.chart = {
-			'data': {
-				'labels': labels[:30],
-				'datasets': [
-					{
-						'name': _('Billed Hours'),
-						'values': billed_hours[:30]
-					},
-					{
-						'name': _('Non-Billed Hours'),
-						'values': non_billed_hours[:30]
-					},
-					{
-						'name': _('Untracked Hours'),
-						'values': untracked_hours[:30]
-					}
-				]
+			"data": {
+				"labels": labels[:30],
+				"datasets": [
+					{"name": _("Billed Hours"), "values": billed_hours[:30]},
+					{"name": _("Non-Billed Hours"), "values": non_billed_hours[:30]},
+					{"name": _("Untracked Hours"), "values": untracked_hours[:30]},
+				],
 			},
-			'type': 'bar',
-			'barOptions': {
-				'stacked': True
-			}
+			"type": "bar",
+			"barOptions": {"stacked": True},
 		}
diff --git a/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py b/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py
index b500bc8..4cddc4a 100644
--- a/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py
+++ b/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py
@@ -11,191 +11,189 @@
 
 
 class TestEmployeeUtilization(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-        # Create test employee
-        cls.test_emp1 = make_employee("test1@employeeutil.com", "_Test Company")
-        cls.test_emp2 = make_employee("test2@employeeutil.com", "_Test Company")
+	@classmethod
+	def setUpClass(cls):
+		# Create test employee
+		cls.test_emp1 = make_employee("test1@employeeutil.com", "_Test Company")
+		cls.test_emp2 = make_employee("test2@employeeutil.com", "_Test Company")
 
-        # Create test project
-        cls.test_project = make_project({"project_name": "_Test Project"})
+		# Create test project
+		cls.test_project = make_project({"project_name": "_Test Project"})
 
-        # Create test timesheets
-        cls.create_test_timesheets()
+		# Create test timesheets
+		cls.create_test_timesheets()
 
-        frappe.db.set_value("HR Settings", "HR Settings", "standard_working_hours", 9)
+		frappe.db.set_value("HR Settings", "HR Settings", "standard_working_hours", 9)
 
-    @classmethod
-    def create_test_timesheets(cls):
-        timesheet1 = frappe.new_doc("Timesheet")
-        timesheet1.employee = cls.test_emp1
-        timesheet1.company = '_Test Company'
+	@classmethod
+	def create_test_timesheets(cls):
+		timesheet1 = frappe.new_doc("Timesheet")
+		timesheet1.employee = cls.test_emp1
+		timesheet1.company = "_Test Company"
 
-        timesheet1.append("time_logs", {
-            "activity_type": get_random("Activity Type"),
-            "hours": 5,
-            "is_billable": 1,
-            "from_time": '2021-04-01 13:30:00.000000',
-            "to_time": '2021-04-01 18:30:00.000000'
-        })
+		timesheet1.append(
+			"time_logs",
+			{
+				"activity_type": get_random("Activity Type"),
+				"hours": 5,
+				"is_billable": 1,
+				"from_time": "2021-04-01 13:30:00.000000",
+				"to_time": "2021-04-01 18:30:00.000000",
+			},
+		)
 
-        timesheet1.save()
-        timesheet1.submit()
+		timesheet1.save()
+		timesheet1.submit()
 
-        timesheet2 = frappe.new_doc("Timesheet")
-        timesheet2.employee = cls.test_emp2
-        timesheet2.company = '_Test Company'
+		timesheet2 = frappe.new_doc("Timesheet")
+		timesheet2.employee = cls.test_emp2
+		timesheet2.company = "_Test Company"
 
-        timesheet2.append("time_logs", {
-            "activity_type": get_random("Activity Type"),
-            "hours": 10,
-            "is_billable": 0,
-            "from_time": '2021-04-01 13:30:00.000000',
-            "to_time": '2021-04-01 23:30:00.000000',
-            "project": cls.test_project.name
-        })
+		timesheet2.append(
+			"time_logs",
+			{
+				"activity_type": get_random("Activity Type"),
+				"hours": 10,
+				"is_billable": 0,
+				"from_time": "2021-04-01 13:30:00.000000",
+				"to_time": "2021-04-01 23:30:00.000000",
+				"project": cls.test_project.name,
+			},
+		)
 
-        timesheet2.save()
-        timesheet2.submit()
+		timesheet2.save()
+		timesheet2.submit()
 
-    @classmethod
-    def tearDownClass(cls):
-        # Delete time logs
-        frappe.db.sql("""
+	@classmethod
+	def tearDownClass(cls):
+		# Delete time logs
+		frappe.db.sql(
+			"""
             DELETE FROM `tabTimesheet Detail`
             WHERE parent IN (
                 SELECT name
                 FROM `tabTimesheet`
                 WHERE company = '_Test Company'
             )
-        """)
+        """
+		)
 
-        frappe.db.sql("DELETE FROM `tabTimesheet` WHERE company='_Test Company'")
-        frappe.db.sql(f"DELETE FROM `tabProject` WHERE name='{cls.test_project.name}'")
+		frappe.db.sql("DELETE FROM `tabTimesheet` WHERE company='_Test Company'")
+		frappe.db.sql(f"DELETE FROM `tabProject` WHERE name='{cls.test_project.name}'")
 
-    def test_utilization_report_with_required_filters_only(self):
-        filters = {
-            "company": "_Test Company",
-            "from_date": "2021-04-01",
-            "to_date": "2021-04-03"
-        }
+	def test_utilization_report_with_required_filters_only(self):
+		filters = {"company": "_Test Company", "from_date": "2021-04-01", "to_date": "2021-04-03"}
 
-        report = execute(filters)
+		report = execute(filters)
 
-        expected_data = self.get_expected_data_for_test_employees()
-        self.assertEqual(report[1], expected_data)
+		expected_data = self.get_expected_data_for_test_employees()
+		self.assertEqual(report[1], expected_data)
 
-    def test_utilization_report_for_single_employee(self):
-        filters = {
-            "company": "_Test Company",
-            "from_date": "2021-04-01",
-            "to_date": "2021-04-03",
-            "employee": self.test_emp1
-        }
+	def test_utilization_report_for_single_employee(self):
+		filters = {
+			"company": "_Test Company",
+			"from_date": "2021-04-01",
+			"to_date": "2021-04-03",
+			"employee": self.test_emp1,
+		}
 
-        report = execute(filters)
+		report = execute(filters)
 
-        emp1_data = frappe.get_doc('Employee', self.test_emp1)
-        expected_data = [
-            {
-                'employee': self.test_emp1,
-                'employee_name': 'test1@employeeutil.com',
-                'billed_hours': 5.0,
-                'non_billed_hours': 0.0,
-                'department': emp1_data.department,
-                'total_hours': 18.0,
-                'untracked_hours': 13.0,
-                'per_util': 27.78,
-                'per_util_billed_only': 27.78
-            }
-        ]
+		emp1_data = frappe.get_doc("Employee", self.test_emp1)
+		expected_data = [
+			{
+				"employee": self.test_emp1,
+				"employee_name": "test1@employeeutil.com",
+				"billed_hours": 5.0,
+				"non_billed_hours": 0.0,
+				"department": emp1_data.department,
+				"total_hours": 18.0,
+				"untracked_hours": 13.0,
+				"per_util": 27.78,
+				"per_util_billed_only": 27.78,
+			}
+		]
 
-        self.assertEqual(report[1], expected_data)
+		self.assertEqual(report[1], expected_data)
 
-    def test_utilization_report_for_project(self):
-        filters = {
-            "company": "_Test Company",
-            "from_date": "2021-04-01",
-            "to_date": "2021-04-03",
-            "project": self.test_project.name
-        }
+	def test_utilization_report_for_project(self):
+		filters = {
+			"company": "_Test Company",
+			"from_date": "2021-04-01",
+			"to_date": "2021-04-03",
+			"project": self.test_project.name,
+		}
 
-        report = execute(filters)
+		report = execute(filters)
 
-        emp2_data = frappe.get_doc('Employee', self.test_emp2)
-        expected_data = [
-            {
-                'employee': self.test_emp2,
-                'employee_name': 'test2@employeeutil.com',
-                'billed_hours': 0.0,
-                'non_billed_hours': 10.0,
-                'department': emp2_data.department,
-                'total_hours': 18.0,
-                'untracked_hours': 8.0,
-                'per_util': 55.56,
-                'per_util_billed_only': 0.0
-            }
-        ]
+		emp2_data = frappe.get_doc("Employee", self.test_emp2)
+		expected_data = [
+			{
+				"employee": self.test_emp2,
+				"employee_name": "test2@employeeutil.com",
+				"billed_hours": 0.0,
+				"non_billed_hours": 10.0,
+				"department": emp2_data.department,
+				"total_hours": 18.0,
+				"untracked_hours": 8.0,
+				"per_util": 55.56,
+				"per_util_billed_only": 0.0,
+			}
+		]
 
-        self.assertEqual(report[1], expected_data)
+		self.assertEqual(report[1], expected_data)
 
-    def test_utilization_report_for_department(self):
-        emp1_data = frappe.get_doc('Employee', self.test_emp1)
-        filters = {
-            "company": "_Test Company",
-            "from_date": "2021-04-01",
-            "to_date": "2021-04-03",
-            "department": emp1_data.department
-        }
+	def test_utilization_report_for_department(self):
+		emp1_data = frappe.get_doc("Employee", self.test_emp1)
+		filters = {
+			"company": "_Test Company",
+			"from_date": "2021-04-01",
+			"to_date": "2021-04-03",
+			"department": emp1_data.department,
+		}
 
-        report = execute(filters)
+		report = execute(filters)
 
-        expected_data = self.get_expected_data_for_test_employees()
-        self.assertEqual(report[1], expected_data)
+		expected_data = self.get_expected_data_for_test_employees()
+		self.assertEqual(report[1], expected_data)
 
-    def test_report_summary_data(self):
-        filters = {
-            "company": "_Test Company",
-            "from_date": "2021-04-01",
-            "to_date": "2021-04-03"
-        }
+	def test_report_summary_data(self):
+		filters = {"company": "_Test Company", "from_date": "2021-04-01", "to_date": "2021-04-03"}
 
-        report = execute(filters)
-        summary = report[4]
-        expected_summary_values = ['41.67%', '13.89%', 5.0, 10.0]
+		report = execute(filters)
+		summary = report[4]
+		expected_summary_values = ["41.67%", "13.89%", 5.0, 10.0]
 
-        self.assertEqual(len(summary), 4)
+		self.assertEqual(len(summary), 4)
 
-        for i in range(4):
-            self.assertEqual(
-                summary[i]['value'], expected_summary_values[i]
-            )
+		for i in range(4):
+			self.assertEqual(summary[i]["value"], expected_summary_values[i])
 
-    def get_expected_data_for_test_employees(self):
-        emp1_data = frappe.get_doc('Employee', self.test_emp1)
-        emp2_data = frappe.get_doc('Employee', self.test_emp2)
+	def get_expected_data_for_test_employees(self):
+		emp1_data = frappe.get_doc("Employee", self.test_emp1)
+		emp2_data = frappe.get_doc("Employee", self.test_emp2)
 
-        return [
-            {
-                'employee': self.test_emp2,
-                'employee_name': 'test2@employeeutil.com',
-                'billed_hours': 0.0,
-                'non_billed_hours': 10.0,
-                'department': emp2_data.department,
-                'total_hours': 18.0,
-                'untracked_hours': 8.0,
-                'per_util': 55.56,
-                'per_util_billed_only': 0.0
-            },
-            {
-                'employee': self.test_emp1,
-                'employee_name': 'test1@employeeutil.com',
-                'billed_hours': 5.0,
-                'non_billed_hours': 0.0,
-                'department': emp1_data.department,
-                'total_hours': 18.0,
-                'untracked_hours': 13.0,
-                'per_util': 27.78,
-                'per_util_billed_only': 27.78
-            }
-        ]
+		return [
+			{
+				"employee": self.test_emp2,
+				"employee_name": "test2@employeeutil.com",
+				"billed_hours": 0.0,
+				"non_billed_hours": 10.0,
+				"department": emp2_data.department,
+				"total_hours": 18.0,
+				"untracked_hours": 8.0,
+				"per_util": 55.56,
+				"per_util_billed_only": 0.0,
+			},
+			{
+				"employee": self.test_emp1,
+				"employee_name": "test1@employeeutil.com",
+				"billed_hours": 5.0,
+				"non_billed_hours": 0.0,
+				"department": emp1_data.department,
+				"total_hours": 18.0,
+				"untracked_hours": 13.0,
+				"per_util": 27.78,
+				"per_util_billed_only": 27.78,
+			},
+		]
diff --git a/erpnext/projects/report/project_profitability/project_profitability.py b/erpnext/projects/report/project_profitability/project_profitability.py
index 9520cd1..aa955bc 100644
--- a/erpnext/projects/report/project_profitability/project_profitability.py
+++ b/erpnext/projects/report/project_profitability/project_profitability.py
@@ -1,30 +1,34 @@
 # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
 
-
 import frappe
 from frappe import _
 from frappe.utils import flt
 
 
 def execute(filters=None):
-	columns, data = [], []
 	data = get_data(filters)
 	columns = get_columns()
 	charts = get_chart_data(data)
 	return columns, data, None, charts
 
+
 def get_data(filters):
 	data = get_rows(filters)
 	data = calculate_cost_and_profit(data)
 	return data
 
+
 def get_rows(filters):
 	conditions = get_conditions(filters)
 	standard_working_hours = frappe.db.get_single_value("HR Settings", "standard_working_hours")
 	if not standard_working_hours:
-		msg = _("The metrics for this report are calculated based on the Standard Working Hours. Please set {0} in {1}.").format(
-			frappe.bold("Standard Working Hours"), frappe.utils.get_link_to_form("HR Settings", "HR Settings"))
+		msg = _(
+			"The metrics for this report are calculated based on the Standard Working Hours. Please set {0} in {1}."
+		).format(
+			frappe.bold("Standard Working Hours"),
+			frappe.utils.get_link_to_form("HR Settings", "HR Settings"),
+		)
 
 		frappe.msgprint(msg)
 		return []
@@ -35,22 +39,27 @@
 			FROM
 				(SELECT
 					si.customer_name,si.base_grand_total,
-					si.name as voucher_no,tabTimesheet.employee,
-					tabTimesheet.title as employee_name,tabTimesheet.parent_project as project,
-					tabTimesheet.start_date,tabTimesheet.end_date,
-					tabTimesheet.total_billed_hours,tabTimesheet.name as timesheet,
+					si.name as voucher_no,`tabTimesheet`.employee,
+					`tabTimesheet`.title as employee_name,`tabTimesheet`.parent_project as project,
+					`tabTimesheet`.start_date,`tabTimesheet`.end_date,
+					`tabTimesheet`.total_billed_hours,`tabTimesheet`.name as timesheet,
 					ss.base_gross_pay,ss.total_working_days,
-					tabTimesheet.total_billed_hours/(ss.total_working_days * {0}) as utilization
+					`tabTimesheet`.total_billed_hours/(ss.total_working_days * {0}) as utilization
 					FROM
-						`tabSalary Slip Timesheet` as sst join `tabTimesheet` on tabTimesheet.name = sst.time_sheet
-						join `tabSales Invoice Timesheet` as sit on sit.time_sheet = tabTimesheet.name
-						join `tabSales Invoice` as si on si.name = sit.parent and si.status != "Cancelled"
-						join `tabSalary Slip` as ss on ss.name = sst.parent and ss.status != "Cancelled" """.format(standard_working_hours)
+						`tabSalary Slip Timesheet` as sst join `tabTimesheet` on `tabTimesheet`.name = sst.time_sheet
+						join `tabSales Invoice Timesheet` as sit on sit.time_sheet = `tabTimesheet`.name
+						join `tabSales Invoice` as si on si.name = sit.parent and si.status != 'Cancelled'
+						join `tabSalary Slip` as ss on ss.name = sst.parent and ss.status != 'Cancelled' """.format(
+		standard_working_hours
+	)
 	if conditions:
 		sql += """
 				WHERE
-					{0}) as t""".format(conditions)
-	return frappe.db.sql(sql,filters, as_dict=True)
+					{0}) as t""".format(
+			conditions
+		)
+	return frappe.db.sql(sql, filters, as_dict=True)
+
 
 def calculate_cost_and_profit(data):
 	for row in data:
@@ -58,30 +67,36 @@
 		row.profit = flt(row.base_grand_total) - flt(row.base_gross_pay) * flt(row.utilization)
 	return data
 
+
 def get_conditions(filters):
 	conditions = []
 
 	if filters.get("company"):
-		conditions.append("tabTimesheet.company={0}".format(frappe.db.escape(filters.get("company"))))
+		conditions.append("`tabTimesheet`.company={0}".format(frappe.db.escape(filters.get("company"))))
 
 	if filters.get("start_date"):
-		conditions.append("tabTimesheet.start_date>='{0}'".format(filters.get("start_date")))
+		conditions.append("`tabTimesheet`.start_date>='{0}'".format(filters.get("start_date")))
 
 	if filters.get("end_date"):
-		conditions.append("tabTimesheet.end_date<='{0}'".format(filters.get("end_date")))
+		conditions.append("`tabTimesheet`.end_date<='{0}'".format(filters.get("end_date")))
 
 	if filters.get("customer_name"):
 		conditions.append("si.customer_name={0}".format(frappe.db.escape(filters.get("customer_name"))))
 
 	if filters.get("employee"):
-		conditions.append("tabTimesheet.employee={0}".format(frappe.db.escape(filters.get("employee"))))
+		conditions.append(
+			"`tabTimesheet`.employee={0}".format(frappe.db.escape(filters.get("employee")))
+		)
 
 	if filters.get("project"):
-		conditions.append("tabTimesheet.parent_project={0}".format(frappe.db.escape(filters.get("project"))))
+		conditions.append(
+			"`tabTimesheet`.parent_project={0}".format(frappe.db.escape(filters.get("project")))
+		)
 
 	conditions = " and ".join(conditions)
 	return conditions
 
+
 def get_chart_data(data):
 	if not data:
 		return None
@@ -94,20 +109,13 @@
 		utilization.append(entry.get("utilization"))
 
 	charts = {
-		"data": {
-			"labels": labels,
-			"datasets": [
-				{
-					"name": "Utilization",
-					"values": utilization
-				}
-			]
-		},
+		"data": {"labels": labels, "datasets": [{"name": "Utilization", "values": utilization}]},
 		"type": "bar",
-		"colors": ["#84BDD5"]
+		"colors": ["#84BDD5"],
 	}
 	return charts
 
+
 def get_columns():
 	return [
 		{
@@ -115,98 +123,78 @@
 			"label": _("Customer"),
 			"fieldtype": "Link",
 			"options": "Customer",
-			"width": 150
+			"width": 150,
 		},
 		{
 			"fieldname": "employee",
 			"label": _("Employee"),
 			"fieldtype": "Link",
 			"options": "Employee",
-			"width": 130
+			"width": 130,
 		},
-		{
-			"fieldname": "employee_name",
-			"label": _("Employee Name"),
-			"fieldtype": "Data",
-			"width": 120
-		},
+		{"fieldname": "employee_name", "label": _("Employee Name"), "fieldtype": "Data", "width": 120},
 		{
 			"fieldname": "voucher_no",
 			"label": _("Sales Invoice"),
 			"fieldtype": "Link",
 			"options": "Sales Invoice",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"fieldname": "timesheet",
 			"label": _("Timesheet"),
 			"fieldtype": "Link",
 			"options": "Timesheet",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"fieldname": "project",
 			"label": _("Project"),
 			"fieldtype": "Link",
 			"options": "Project",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"fieldname": "base_grand_total",
 			"label": _("Bill Amount"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"fieldname": "base_gross_pay",
 			"label": _("Cost"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"fieldname": "profit",
 			"label": _("Profit"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 100
+			"width": 100,
 		},
-		{
-			"fieldname": "utilization",
-			"label": _("Utilization"),
-			"fieldtype": "Percentage",
-			"width": 100
-		},
+		{"fieldname": "utilization", "label": _("Utilization"), "fieldtype": "Percentage", "width": 100},
 		{
 			"fieldname": "fractional_cost",
 			"label": _("Fractional Cost"),
 			"fieldtype": "Int",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"fieldname": "total_billed_hours",
 			"label": _("Total Billed Hours"),
 			"fieldtype": "Int",
-			"width": 150
+			"width": 150,
 		},
-		{
-			"fieldname": "start_date",
-			"label": _("Start Date"),
-			"fieldtype": "Date",
-			"width": 100
-		},
-		{
-			"fieldname": "end_date",
-			"label": _("End Date"),
-			"fieldtype": "Date",
-			"width": 100
-		},
+		{"fieldname": "start_date", "label": _("Start Date"), "fieldtype": "Date", "width": 100},
+		{"fieldname": "end_date", "label": _("End Date"), "fieldtype": "Date", "width": 100},
 		{
 			"label": _("Currency"),
 			"fieldname": "currency",
 			"fieldtype": "Link",
 			"options": "Currency",
-			"width": 80
-		}
+			"width": 80,
+		},
 	]
diff --git a/erpnext/projects/report/project_profitability/test_project_profitability.py b/erpnext/projects/report/project_profitability/test_project_profitability.py
index 1eb3d0d..91a5447 100644
--- a/erpnext/projects/report/project_profitability/test_project_profitability.py
+++ b/erpnext/projects/report/project_profitability/test_project_profitability.py
@@ -1,6 +1,5 @@
-import unittest
-
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import add_days, getdate
 
 from erpnext.hr.doctype.employee.test_employee import make_employee
@@ -12,15 +11,17 @@
 from erpnext.projects.report.project_profitability.project_profitability import execute
 
 
-class TestProjectProfitability(unittest.TestCase):
+class TestProjectProfitability(FrappeTestCase):
 	def setUp(self):
-		frappe.db.sql('delete from `tabTimesheet`')
-		emp = make_employee('test_employee_9@salary.com', company='_Test Company')
+		frappe.db.sql("delete from `tabTimesheet`")
+		emp = make_employee("test_employee_9@salary.com", company="_Test Company")
 
-		if not frappe.db.exists('Salary Component', 'Timesheet Component'):
-			frappe.get_doc({'doctype': 'Salary Component', 'salary_component': 'Timesheet Component'}).insert()
+		if not frappe.db.exists("Salary Component", "Timesheet Component"):
+			frappe.get_doc(
+				{"doctype": "Salary Component", "salary_component": "Timesheet Component"}
+			).insert()
 
-		make_salary_structure_for_timesheet(emp, company='_Test Company')
+		make_salary_structure_for_timesheet(emp, company="_Test Company")
 		date = getdate()
 
 		self.timesheet = make_timesheet(emp, is_billable=1)
@@ -29,21 +30,21 @@
 
 		holidays = self.salary_slip.get_holidays_for_employee(date, date)
 		if holidays:
-			frappe.db.set_value('Payroll Settings', None, 'include_holidays_in_total_working_days', 1)
+			frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 1)
 
 		self.salary_slip.submit()
-		self.sales_invoice = make_sales_invoice(self.timesheet.name, '_Test Item', '_Test Customer')
+		self.sales_invoice = make_sales_invoice(self.timesheet.name, "_Test Item", "_Test Customer")
 		self.sales_invoice.due_date = date
 		self.sales_invoice.submit()
 
-		frappe.db.set_value('HR Settings', None, 'standard_working_hours', 8)
-		frappe.db.set_value('Payroll Settings', None, 'include_holidays_in_total_working_days', 0)
+		frappe.db.set_value("HR Settings", None, "standard_working_hours", 8)
+		frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0)
 
 	def test_project_profitability(self):
 		filters = {
-			'company': '_Test Company',
-			'start_date': add_days(self.timesheet.start_date, -3),
-			'end_date': self.timesheet.start_date
+			"company": "_Test Company",
+			"start_date": add_days(self.timesheet.start_date, -3),
+			"end_date": self.timesheet.start_date,
 		}
 
 		report = execute(filters)
@@ -59,7 +60,9 @@
 		self.assertEqual(self.salary_slip.total_working_days, row.total_working_days)
 
 		standard_working_hours = frappe.db.get_single_value("HR Settings", "standard_working_hours")
-		utilization = timesheet.total_billed_hours/(self.salary_slip.total_working_days * standard_working_hours)
+		utilization = timesheet.total_billed_hours / (
+			self.salary_slip.total_working_days * standard_working_hours
+		)
 		self.assertEqual(utilization, row.utilization)
 
 		profit = self.sales_invoice.base_grand_total - self.salary_slip.base_gross_pay * utilization
@@ -67,6 +70,3 @@
 
 		fractional_cost = self.salary_slip.base_gross_pay * utilization
 		self.assertEqual(fractional_cost, row.fractional_cost)
-
-	def tearDown(self):
-		frappe.db.rollback()
diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py
index ce1b701..606c0c2 100644
--- a/erpnext/projects/report/project_summary/project_summary.py
+++ b/erpnext/projects/report/project_summary/project_summary.py
@@ -10,18 +10,35 @@
 	columns = get_columns()
 	data = []
 
-	data = frappe.db.get_all("Project", filters=filters, fields=["name", 'status', "percent_complete", "expected_start_date", "expected_end_date", "project_type"], order_by="expected_end_date")
+	data = frappe.db.get_all(
+		"Project",
+		filters=filters,
+		fields=[
+			"name",
+			"status",
+			"percent_complete",
+			"expected_start_date",
+			"expected_end_date",
+			"project_type",
+		],
+		order_by="expected_end_date",
+	)
 
 	for project in data:
 		project["total_tasks"] = frappe.db.count("Task", filters={"project": project.name})
-		project["completed_tasks"] = frappe.db.count("Task", filters={"project": project.name, "status": "Completed"})
-		project["overdue_tasks"] = frappe.db.count("Task", filters={"project": project.name, "status": "Overdue"})
+		project["completed_tasks"] = frappe.db.count(
+			"Task", filters={"project": project.name, "status": "Completed"}
+		)
+		project["overdue_tasks"] = frappe.db.count(
+			"Task", filters={"project": project.name, "status": "Overdue"}
+		)
 
 	chart = get_chart_data(data)
 	report_summary = get_report_summary(data)
 
 	return columns, data, None, chart, report_summary
 
+
 def get_columns():
 	return [
 		{
@@ -29,59 +46,35 @@
 			"label": _("Project"),
 			"fieldtype": "Link",
 			"options": "Project",
-			"width": 200
+			"width": 200,
 		},
 		{
 			"fieldname": "project_type",
 			"label": _("Type"),
 			"fieldtype": "Link",
 			"options": "Project Type",
-			"width": 120
+			"width": 120,
 		},
-		{
-			"fieldname": "status",
-			"label": _("Status"),
-			"fieldtype": "Data",
-			"width": 120
-		},
-		{
-			"fieldname": "total_tasks",
-			"label": _("Total Tasks"),
-			"fieldtype": "Data",
-			"width": 120
-		},
+		{"fieldname": "status", "label": _("Status"), "fieldtype": "Data", "width": 120},
+		{"fieldname": "total_tasks", "label": _("Total Tasks"), "fieldtype": "Data", "width": 120},
 		{
 			"fieldname": "completed_tasks",
 			"label": _("Tasks Completed"),
 			"fieldtype": "Data",
-			"width": 120
+			"width": 120,
 		},
-		{
-			"fieldname": "overdue_tasks",
-			"label": _("Tasks Overdue"),
-			"fieldtype": "Data",
-			"width": 120
-		},
-		{
-			"fieldname": "percent_complete",
-			"label": _("Completion"),
-			"fieldtype": "Data",
-			"width": 120
-		},
+		{"fieldname": "overdue_tasks", "label": _("Tasks Overdue"), "fieldtype": "Data", "width": 120},
+		{"fieldname": "percent_complete", "label": _("Completion"), "fieldtype": "Data", "width": 120},
 		{
 			"fieldname": "expected_start_date",
 			"label": _("Start Date"),
 			"fieldtype": "Date",
-			"width": 120
+			"width": 120,
 		},
-		{
-			"fieldname": "expected_end_date",
-			"label": _("End Date"),
-			"fieldtype": "Date",
-			"width": 120
-		},
+		{"fieldname": "expected_end_date", "label": _("End Date"), "fieldtype": "Date", "width": 120},
 	]
 
+
 def get_chart_data(data):
 	labels = []
 	total = []
@@ -96,29 +89,19 @@
 
 	return {
 		"data": {
-			'labels': labels[:30],
-			'datasets': [
-				{
-					"name": "Overdue",
-					"values": overdue[:30]
-				},
-				{
-					"name": "Completed",
-					"values": completed[:30]
-				},
-				{
-					"name": "Total Tasks",
-					"values": total[:30]
-				},
-			]
+			"labels": labels[:30],
+			"datasets": [
+				{"name": "Overdue", "values": overdue[:30]},
+				{"name": "Completed", "values": completed[:30]},
+				{"name": "Total Tasks", "values": total[:30]},
+			],
 		},
 		"type": "bar",
 		"colors": ["#fc4f51", "#78d6ff", "#7575ff"],
-		"barOptions": {
-			"stacked": True
-		}
+		"barOptions": {"stacked": True},
 	}
 
+
 def get_report_summary(data):
 	if not data:
 		return None
@@ -152,5 +135,5 @@
 			"indicator": "Green" if total_overdue == 0 else "Red",
 			"label": _("Overdue Tasks"),
 			"datatype": "Int",
-		}
+		},
 	]
diff --git a/erpnext/projects/report/project_wise_stock_tracking/project_wise_stock_tracking.py b/erpnext/projects/report/project_wise_stock_tracking/project_wise_stock_tracking.py
index 31bcc3b..da609ca 100644
--- a/erpnext/projects/report/project_wise_stock_tracking/project_wise_stock_tracking.py
+++ b/erpnext/projects/report/project_wise_stock_tracking/project_wise_stock_tracking.py
@@ -14,29 +14,56 @@
 
 	data = []
 	for project in proj_details:
-		data.append([project.name, pr_item_map.get(project.name, 0),
-			se_item_map.get(project.name, 0), dn_item_map.get(project.name, 0),
-			project.project_name, project.status, project.company,
-			project.customer, project.estimated_costing, project.expected_start_date,
-			project.expected_end_date])
+		data.append(
+			[
+				project.name,
+				pr_item_map.get(project.name, 0),
+				se_item_map.get(project.name, 0),
+				dn_item_map.get(project.name, 0),
+				project.project_name,
+				project.status,
+				project.company,
+				project.customer,
+				project.estimated_costing,
+				project.expected_start_date,
+				project.expected_end_date,
+			]
+		)
 
 	return columns, data
 
+
 def get_columns():
-	return [_("Project Id") + ":Link/Project:140", _("Cost of Purchased Items") + ":Currency:160",
-		_("Cost of Issued Items") + ":Currency:160", _("Cost of Delivered Items") + ":Currency:160",
-		_("Project Name") + "::120", _("Project Status") + "::120", _("Company") + ":Link/Company:100",
-		_("Customer") + ":Link/Customer:140", _("Project Value") + ":Currency:120",
-		_("Project Start Date") + ":Date:120", _("Completion Date") + ":Date:120"]
+	return [
+		_("Project Id") + ":Link/Project:140",
+		_("Cost of Purchased Items") + ":Currency:160",
+		_("Cost of Issued Items") + ":Currency:160",
+		_("Cost of Delivered Items") + ":Currency:160",
+		_("Project Name") + "::120",
+		_("Project Status") + "::120",
+		_("Company") + ":Link/Company:100",
+		_("Customer") + ":Link/Customer:140",
+		_("Project Value") + ":Currency:120",
+		_("Project Start Date") + ":Date:120",
+		_("Completion Date") + ":Date:120",
+	]
+
 
 def get_project_details():
-	return frappe.db.sql(""" select name, project_name, status, company, customer, estimated_costing,
-		expected_start_date, expected_end_date from tabProject where docstatus < 2""", as_dict=1)
+	return frappe.db.sql(
+		""" select name, project_name, status, company, customer, estimated_costing,
+		expected_start_date, expected_end_date from tabProject where docstatus < 2""",
+		as_dict=1,
+	)
+
 
 def get_purchased_items_cost():
-	pr_items = frappe.db.sql("""select project, sum(base_net_amount) as amount
+	pr_items = frappe.db.sql(
+		"""select project, sum(base_net_amount) as amount
 		from `tabPurchase Receipt Item` where ifnull(project, '') != ''
-		and docstatus = 1 group by project""", as_dict=1)
+		and docstatus = 1 group by project""",
+		as_dict=1,
+	)
 
 	pr_item_map = {}
 	for item in pr_items:
@@ -44,11 +71,15 @@
 
 	return pr_item_map
 
+
 def get_issued_items_cost():
-	se_items = frappe.db.sql("""select se.project, sum(se_item.amount) as amount
+	se_items = frappe.db.sql(
+		"""select se.project, sum(se_item.amount) as amount
 		from `tabStock Entry` se, `tabStock Entry Detail` se_item
 		where se.name = se_item.parent and se.docstatus = 1 and ifnull(se_item.t_warehouse, '') = ''
-		and ifnull(se.project, '') != '' group by se.project""", as_dict=1)
+		and ifnull(se.project, '') != '' group by se.project""",
+		as_dict=1,
+	)
 
 	se_item_map = {}
 	for item in se_items:
@@ -56,18 +87,24 @@
 
 	return se_item_map
 
+
 def get_delivered_items_cost():
-	dn_items = frappe.db.sql("""select dn.project, sum(dn_item.base_net_amount) as amount
+	dn_items = frappe.db.sql(
+		"""select dn.project, sum(dn_item.base_net_amount) as amount
 		from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item
 		where dn.name = dn_item.parent and dn.docstatus = 1 and ifnull(dn.project, '') != ''
-		group by dn.project""", as_dict=1)
+		group by dn.project""",
+		as_dict=1,
+	)
 
-	si_items = frappe.db.sql("""select si.project, sum(si_item.base_net_amount) as amount
+	si_items = frappe.db.sql(
+		"""select si.project, sum(si_item.base_net_amount) as amount
 		from `tabSales Invoice` si, `tabSales Invoice Item` si_item
 		where si.name = si_item.parent and si.docstatus = 1 and si.update_stock = 1
 		and si.is_pos = 1 and ifnull(si.project, '') != ''
-		group by si.project""", as_dict=1)
-
+		group by si.project""",
+		as_dict=1,
+	)
 
 	dn_item_map = {}
 	for item in dn_items:
diff --git a/erpnext/projects/utils.py b/erpnext/projects/utils.py
index 5d74550..3cc4da4 100644
--- a/erpnext/projects/utils.py
+++ b/erpnext/projects/utils.py
@@ -17,14 +17,15 @@
 	match_conditions = build_match_conditions("Task")
 	match_conditions = ("and" + match_conditions) if match_conditions else ""
 
-	return frappe.db.sql("""select name, subject from `tabTask`
+	return frappe.db.sql(
+		"""select name, subject from `tabTask`
 		where (`%s` like %s or `subject` like %s) %s
 		order by
 			case when `subject` like %s then 0 else 1 end,
 			case when `%s` like %s then 0 else 1 end,
 			`%s`,
 			subject
-		limit %s, %s""" %
-		(searchfield, "%s", "%s", match_conditions, "%s",
-			searchfield, "%s", searchfield, "%s", "%s"),
-		(search_string, search_string, order_by_string, order_by_string, start, page_len))
+		limit %s offset %s"""
+		% (searchfield, "%s", "%s", match_conditions, "%s", searchfield, "%s", searchfield, "%s", "%s"),
+		(search_string, search_string, order_by_string, order_by_string, page_len, start),
+	)
diff --git a/erpnext/projects/web_form/tasks/tasks.py b/erpnext/projects/web_form/tasks/tasks.py
index 99249ed..b422973 100644
--- a/erpnext/projects/web_form/tasks/tasks.py
+++ b/erpnext/projects/web_form/tasks/tasks.py
@@ -3,9 +3,13 @@
 
 def get_context(context):
 	if frappe.form_dict.project:
-		context.parents = [{'title': frappe.form_dict.project, 'route': '/projects?project='+ frappe.form_dict.project}]
+		context.parents = [
+			{"title": frappe.form_dict.project, "route": "/projects?project=" + frappe.form_dict.project}
+		]
 		context.success_url = "/projects?project=" + frappe.form_dict.project
 
-	elif context.doc and context.doc.get('project'):
-		context.parents = [{'title': context.doc.project, 'route': '/projects?project='+ context.doc.project}]
+	elif context.doc and context.doc.get("project"):
+		context.parents = [
+			{"title": context.doc.project, "route": "/projects?project=" + context.doc.project}
+		]
 		context.success_url = "/projects?project=" + context.doc.project
diff --git a/erpnext/public/js/call_popup/call_popup.js b/erpnext/public/js/call_popup/call_popup.js
index c954f12..2dbe999 100644
--- a/erpnext/public/js/call_popup/call_popup.js
+++ b/erpnext/public/js/call_popup/call_popup.js
@@ -141,6 +141,14 @@
 				'fieldtype': 'Section Break',
 				'hide_border': 1,
 			}, {
+				'fieldname': 'call_type',
+				'label': 'Call Type',
+				'fieldtype': 'Link',
+				'options': 'Telephony Call Type',
+			}, {
+				'fieldtype': 'Section Break',
+				'hide_border': 1,
+			}, {
 				'fieldtype': 'Small Text',
 				'label': __('Call Summary'),
 				'fieldname': 'call_summary',
@@ -149,10 +157,12 @@
 				'label': __('Save'),
 				'click': () => {
 					const call_summary = this.call_details.get_value('call_summary');
+					const call_type = this.call_details.get_value('call_type');
 					if (!call_summary) return;
-					frappe.xcall('erpnext.telephony.doctype.call_log.call_log.add_call_summary', {
+					frappe.xcall('erpnext.telephony.doctype.call_log.call_log.add_call_summary_and_call_type', {
 						'call_log': this.call_log.name,
 						'summary': call_summary,
+						'call_type': call_type,
 					}).then(() => {
 						this.close_modal();
 						frappe.show_alert({
diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js
index 84c7176..c1fe72b 100644
--- a/erpnext/public/js/controllers/accounts.js
+++ b/erpnext/public/js/controllers/accounts.js
@@ -27,7 +27,8 @@
 					query: "erpnext.controllers.queries.tax_account_query",
 					filters: {
 						"account_type": account_type,
-						"company": doc.company
+						"company": doc.company,
+						"disabled": 0
 					}
 				}
 			});
diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js
index 54e5daa..a5b7699 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -74,6 +74,7 @@
 		me.frm.set_query('supplier_address', erpnext.queries.address_query);
 
 		me.frm.set_query('billing_address', erpnext.queries.company_address_query);
+		erpnext.accounts.dimensions.setup_dimension_filters(me.frm, me.frm.doctype);
 
 		if(this.frm.fields_dict.supplier) {
 			this.frm.set_query("supplier", function() {
@@ -81,7 +82,7 @@
 		}
 
 		this.frm.set_query("item_code", "items", function() {
-			if (me.frm.doc.is_subcontracted == "Yes") {
+			if (me.frm.doc.is_subcontracted) {
 				return{
 					query: "erpnext.controllers.queries.item_query",
 					filters:{ 'supplier': me.frm.doc.supplier, 'is_sub_contracted_item': 1 }
@@ -90,7 +91,7 @@
 			else {
 				return{
 					query: "erpnext.controllers.queries.item_query",
-					filters: { 'supplier': me.frm.doc.supplier, 'is_purchase_item': 1 }
+					filters: { 'supplier': me.frm.doc.supplier, 'is_purchase_item': 1, 'has_variants': 0}
 				}
 			}
 		});
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index ae0e2a3..16b0b4a 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -34,11 +34,13 @@
 		frappe.model.set_value(item.doctype, item.name, "rate", item_rate);
 	}
 
-	calculate_taxes_and_totals(update_paid_amount) {
+	async calculate_taxes_and_totals(update_paid_amount) {
 		this.discount_amount_applied = false;
 		this._calculate_taxes_and_totals();
 		this.calculate_discount_amount();
 
+		await this.calculate_shipping_charges();
+
 		// Advance calculation applicable to Sales /Purchase Invoice
 		if(in_list(["Sales Invoice", "POS Invoice", "Purchase Invoice"], this.frm.doc.doctype)
 			&& this.frm.doc.docstatus < 2 && !this.frm.doc.is_return) {
@@ -81,7 +83,6 @@
 		this.initialize_taxes();
 		this.determine_exclusive_rate();
 		this.calculate_net_total();
-		this.calculate_shipping_charges();
 		this.calculate_taxes();
 		this.manipulate_grand_total_for_inclusive_tax();
 		this.calculate_totals();
@@ -272,9 +273,14 @@
 	}
 
 	calculate_shipping_charges() {
+		// Do not apply shipping rule for POS
+		if (this.frm.doc.is_pos) {
+			return;
+		}
+
 		frappe.model.round_floats_in(this.frm.doc, ["total", "base_total", "net_total", "base_net_total"]);
 		if (frappe.meta.get_docfield(this.frm.doc.doctype, "shipping_rule", this.frm.doc.name)) {
-			this.shipping_rule();
+			return this.shipping_rule();
 		}
 	}
 
@@ -687,7 +693,12 @@
 		}));
 		this.frm.doc.total_advance = flt(total_allocated_amount, precision("total_advance"));
 
+		if (this.frm.doc.write_off_outstanding_amount_automatically) {
+			this.frm.doc.write_off_amount = 0;
+		}
+
 		this.calculate_outstanding_amount(update_paid_amount);
+		this.calculate_write_off_amount();
 	}
 
 	is_internal_invoice() {
@@ -778,11 +789,23 @@
 		if(this.frm.doc.is_pos && (update_paid_amount===undefined || update_paid_amount)) {
 			$.each(this.frm.doc['payments'] || [], function(index, data) {
 				if(data.default && payment_status && total_amount_to_pay > 0) {
-					let base_amount = flt(total_amount_to_pay, precision("base_amount", data));
+					let base_amount, amount;
+
+					if (me.frm.doc.party_account_currency == me.frm.doc.currency) {
+						// if customer/supplier currency is same as company currency
+						// total_amount_to_pay is already in customer/supplier currency
+						// so base_amount has to be calculated using total_amount_to_pay
+						base_amount = flt(total_amount_to_pay * me.frm.doc.conversion_rate, precision("base_amount", data));
+						amount = flt(total_amount_to_pay, precision("amount", data));
+					} else {
+						base_amount = flt(total_amount_to_pay, precision("base_amount", data));
+						amount = flt(total_amount_to_pay / me.frm.doc.conversion_rate, precision("amount", data));
+					}
+
 					frappe.model.set_value(data.doctype, data.name, "base_amount", base_amount);
-					let amount = flt(total_amount_to_pay / me.frm.doc.conversion_rate, precision("amount", data));
 					frappe.model.set_value(data.doctype, data.name, "amount", amount);
 					payment_status = false;
+
 				} else if(me.frm.doc.paid_amount) {
 					frappe.model.set_value(data.doctype, data.name, "amount", 0.0);
 				}
@@ -823,26 +846,23 @@
 				var grand_total = this.frm.doc.rounded_total || this.frm.doc.grand_total;
 				var base_grand_total = this.frm.doc.base_rounded_total || this.frm.doc.base_grand_total;
 
-				this.frm.doc.change_amount = flt(this.frm.doc.paid_amount - grand_total +
-					this.frm.doc.write_off_amount, precision("change_amount"));
+				this.frm.doc.change_amount = flt(this.frm.doc.paid_amount - grand_total,
+					precision("change_amount"));
 
 				this.frm.doc.base_change_amount = flt(this.frm.doc.base_paid_amount -
-					base_grand_total + this.frm.doc.base_write_off_amount,
-					precision("base_change_amount"));
+					base_grand_total, precision("base_change_amount"));
 			}
 		}
 	}
 
-	calculate_write_off_amount(){
-		if(this.frm.doc.paid_amount > this.frm.doc.grand_total){
-			this.frm.doc.write_off_amount = flt(this.frm.doc.grand_total - this.frm.doc.paid_amount
-				+ this.frm.doc.change_amount, precision("write_off_amount"));
-
+	calculate_write_off_amount() {
+		if(this.frm.doc.write_off_outstanding_amount_automatically) {
+			this.frm.doc.write_off_amount = flt(this.frm.doc.outstanding_amount, precision("write_off_amount"));
 			this.frm.doc.base_write_off_amount = flt(this.frm.doc.write_off_amount * this.frm.doc.conversion_rate,
 				precision("base_write_off_amount"));
-		}else{
-			this.frm.doc.paid_amount = 0.0;
+
+			this.calculate_outstanding_amount(false);
 		}
-		this.calculate_outstanding_amount(false);
+
 	}
 };
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 00373a6..01f72ad 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -239,7 +239,7 @@
 				() => set_value('currency', currency),
 				() => set_value('price_list_currency', currency),
 				() => set_value('status', 'Draft'),
-				() => set_value('is_subcontracted', 'No'),
+				() => set_value('is_subcontracted', 0),
 				() => {
 					if(this.frm.doc.company && !this.frm.doc.amended_from) {
 						this.frm.trigger("company");
@@ -345,112 +345,8 @@
 	}
 
 	scan_barcode() {
-		let me = this;
-
-		if(this.frm.doc.scan_barcode) {
-			frappe.call({
-				method: "erpnext.selling.page.point_of_sale.point_of_sale.search_for_serial_or_batch_or_barcode_number",
-				args: {
-					search_value: this.frm.doc.scan_barcode
-				}
-			}).then(r => {
-				const data = r && r.message;
-				if (!data || Object.keys(data).length === 0) {
-					frappe.show_alert({
-						message: __('Cannot find Item with this Barcode'),
-						indicator: 'red'
-					});
-					return;
-				}
-
-				me.modify_table_after_scan(data);
-			});
-		}
-		return false;
-	}
-
-	modify_table_after_scan(data) {
-		let scan_barcode_field = this.frm.fields_dict["scan_barcode"];
-		let cur_grid = this.frm.fields_dict.items.grid;
-		let row_to_modify = null;
-
-		// Check if batch is scanned and table has batch no field
-		let batch_no_scan = Boolean(data.batch_no) && frappe.meta.has_field(cur_grid.doctype, "batch_no");
-
-		if (batch_no_scan) {
-			row_to_modify = this.get_batch_row_to_modify(data.batch_no);
-		} else {
-			// serial or barcode scan
-			row_to_modify = this.get_row_to_modify_on_scan(row_to_modify, data);
-		}
-
-		if (!row_to_modify) {
-			// add new row if new item/batch is scanned
-			row_to_modify = frappe.model.add_child(this.frm.doc, cur_grid.doctype, 'items');
-		}
-
-		this.show_scan_message(row_to_modify.idx, row_to_modify.item_code);
-		this.set_scanned_values(row_to_modify, data, scan_barcode_field);
-	}
-
-	set_scanned_values(row_to_modify, data, scan_barcode_field) {
-		// increase qty and set scanned value and item in row
-		this.frm.from_barcode = this.frm.from_barcode ? this.frm.from_barcode + 1 : 1;
-		frappe.model.set_value(row_to_modify.doctype, row_to_modify.name, {
-			item_code: data.item_code,
-			qty: (row_to_modify.qty || 0) + 1
-		});
-
-		['serial_no', 'batch_no', 'barcode'].forEach(field => {
-			if (data[field] && frappe.meta.has_field(row_to_modify.doctype, field)) {
-				let is_serial_no = row_to_modify[field] && field === "serial_no";
-				let value = data[field];
-
-				if (is_serial_no) {
-					value = row_to_modify[field] + '\n' + data[field];
-				}
-
-				frappe.model.set_value(row_to_modify.doctype, row_to_modify.name, field, value);
-			}
-		});
-
-		scan_barcode_field.set_value('');
-		refresh_field("items");
-	}
-
-	get_row_to_modify_on_scan(row_to_modify, data) {
-		// get an existing item row to increment or blank row to modify
-		const existing_item_row = this.frm.doc.items.find(d => d.item_code === data.item_code);
-		const blank_item_row = this.frm.doc.items.find(d => !d.item_code);
-
-		if (existing_item_row) {
-			row_to_modify = existing_item_row;
-		} else if (blank_item_row) {
-			row_to_modify = blank_item_row;
-		}
-
-		return row_to_modify;
-	}
-
-	get_batch_row_to_modify(batch_no) {
-		// get row if batch already exists in table
-		const existing_batch_row = this.frm.doc.items.find(d => d.batch_no === batch_no);
-		return existing_batch_row || null;
-	}
-
-	show_scan_message (idx, exist = null) {
-		// show new row or qty increase toast
-		if (exist) {
-			frappe.show_alert({
-				message: __('Row #{0}: Qty increased by 1', [idx]),
-				indicator: 'green'
-			});
-		} else {
-			frappe.show_alert({
-				message: __('Row #{0}: Item added', [idx]),
-				indicator: 'green'
-			});
-		}
+		const barcode_scanner = new erpnext.utils.BarcodeScanner({frm:this.frm});
+		barcode_scanner.process_scan();
 	}
 
 	apply_default_taxes() {
@@ -507,17 +403,6 @@
 		var sms_man = new erpnext.SMSManager(this.frm.doc);
 	}
 
-	barcode(doc, cdt, cdn) {
-		var d = locals[cdt][cdn];
-		if(d.barcode=="" || d.barcode==null) {
-			// barcode cleared, remove item
-			d.item_code = "";
-		}
-
-		this.frm.from_barcode = this.frm.from_barcode ? this.frm.from_barcode + 1 : 1;
-		this.item_code(doc, cdt, cdn);
-	}
-
 	item_code(doc, cdt, cdn) {
 		var me = this;
 		var item = frappe.get_doc(cdt, cdn);
@@ -535,14 +420,10 @@
 			this.frm.doc.doctype === 'Delivery Note') {
 			show_batch_dialog = 1;
 		}
-		// clear barcode if setting item (else barcode will take priority)
-		if (this.frm.from_barcode == 0) {
-			item.barcode = null;
-		}
-		this.frm.from_barcode = this.frm.from_barcode - 1 >= 0 ? this.frm.from_barcode - 1 : 0;
+		item.barcode = null;
 
 
-		if(item.item_code || item.barcode || item.serial_no) {
+		if(item.item_code || item.serial_no) {
 			if(!this.validate_company_and_party()) {
 				this.frm.fields_dict["items"].grid.grid_rows[item.idx - 1].remove();
 			} else {
@@ -572,7 +453,6 @@
 							is_pos: cint(me.frm.doc.is_pos),
 							is_return: cint(me.frm.doc.is_return),
 							is_subcontracted: me.frm.doc.is_subcontracted,
-							transaction_date: me.frm.doc.transaction_date || me.frm.doc.posting_date,
 							ignore_pricing_rule: me.frm.doc.ignore_pricing_rule,
 							doctype: me.frm.doc.doctype,
 							name: me.frm.doc.name,
@@ -582,6 +462,7 @@
 							stock_qty: item.stock_qty,
 							conversion_factor: item.conversion_factor,
 							weight_per_unit: item.weight_per_unit,
+							uom: item.uom,
 							weight_uom: item.weight_uom,
 							manufacturer: item.manufacturer,
 							stock_uom: item.stock_uom,
@@ -1036,15 +917,15 @@
 	}
 
 	currency() {
-		/* manqala 19/09/2016: let the translation date be whichever of the transaction_date or posting_date is available */
-		var transaction_date = this.frm.doc.transaction_date || this.frm.doc.posting_date;
-		/* end manqala */
-		var me = this;
+		// The transaction date be either transaction_date (from orders) or posting_date (from invoices)
+		let transaction_date = this.frm.doc.transaction_date || this.frm.doc.posting_date;
+
+		let me = this;
 		this.set_dynamic_labels();
-		var company_currency = this.get_company_currency();
-		// Added `ignore_pricing_rule` to determine if document is loading after mapping from another doc
+		let company_currency = this.get_company_currency();
+		// Added `ignore_price_list` to determine if document is loading after mapping from another doc
 		if(this.frm.doc.currency && this.frm.doc.currency !== company_currency
-				&& !this.frm.doc.ignore_pricing_rule) {
+				&& !(this.frm.doc.__onload && this.frm.doc.__onload.ignore_price_list)) {
 
 			this.get_exchange_rate(transaction_date, this.frm.doc.currency, company_currency,
 				function(exchange_rate) {
@@ -1055,7 +936,13 @@
 					}
 				});
 		} else {
-			this.conversion_rate();
+			// company currency and doc currency is same
+			// this will prevent unnecessary conversion rate triggers
+			if(this.frm.doc.currency === this.get_company_currency()) {
+				this.frm.set_value("conversion_rate", 1.0);
+			} else {
+				this.conversion_rate();
+			}
 		}
 	}
 
@@ -1070,7 +957,7 @@
 		}
 
 		if(flt(this.frm.doc.conversion_rate)>0.0) {
-			if(this.frm.doc.ignore_pricing_rule) {
+			if(this.frm.doc.__onload && this.frm.doc.__onload.ignore_price_list) {
 				this.calculate_taxes_and_totals();
 			} else if (!this.in_apply_price_list){
 				this.apply_price_list();
@@ -1087,6 +974,9 @@
 			return this.frm.call({
 				doc: this.frm.doc,
 				method: "apply_shipping_rule",
+				callback: function(r) {
+					me._calculate_taxes_and_totals();
+				}
 			}).fail(() => this.frm.set_value('shipping_rule', ''));
 		}
 	}
@@ -1144,8 +1034,9 @@
 		this.set_dynamic_labels();
 
 		var company_currency = this.get_company_currency();
-		// Added `ignore_pricing_rule` to determine if document is loading after mapping from another doc
-		if(this.frm.doc.price_list_currency !== company_currency  && !this.frm.doc.ignore_pricing_rule) {
+		// Added `ignore_price_list` to determine if document is loading after mapping from another doc
+		if(this.frm.doc.price_list_currency !== company_currency  &&
+				!(this.frm.doc.__onload && this.frm.doc.__onload.ignore_price_list)) {
 			this.get_exchange_rate(this.frm.doc.posting_date, this.frm.doc.price_list_currency, company_currency,
 				function(exchange_rate) {
 					me.frm.set_value("plc_conversion_rate", exchange_rate);
@@ -1493,7 +1384,15 @@
 		var me = this;
 		var args = this._get_args(item);
 		if (!(args.items && args.items.length)) {
-			if(calculate_taxes_and_totals) me.calculate_taxes_and_totals();
+			if (calculate_taxes_and_totals) me.calculate_taxes_and_totals();
+			return;
+		}
+
+		// Target doc created from a mapped doc
+		if (this.frm.doc.__onload && this.frm.doc.__onload.ignore_price_list) {
+			// Calculate totals even though pricing rule is not applied.
+			// `apply_pricing_rule` is triggered due to change in data which most likely contributes to Total.
+			if (calculate_taxes_and_totals) me.calculate_taxes_and_totals();
 			return;
 		}
 
@@ -1613,7 +1512,7 @@
 				me.remove_pricing_rule(frappe.get_doc(d.doctype, d.name));
 			}
 
-			if (d.free_item_data) {
+			if (d.free_item_data.length > 0) {
 				me.apply_product_discount(d);
 			}
 
@@ -1884,6 +1783,7 @@
 				callback: function(r) {
 					if(!r.exc) {
 						item.item_tax_rate = r.message;
+						me.add_taxes_from_item_tax_template(item.item_tax_rate);
 						me.calculate_taxes_and_totals();
 					}
 				}
diff --git a/erpnext/public/js/education/assessment_result_tool.html b/erpnext/public/js/education/assessment_result_tool.html
deleted file mode 100644
index f7d1ab3..0000000
--- a/erpnext/public/js/education/assessment_result_tool.html
+++ /dev/null
@@ -1,72 +0,0 @@
-<table class="table table-bordered assessment-result-tool">
-	<thead>
-		<tr>
-			<th style="width: 90px" rowspan="2">Student</th>
-			<th style="width: 170px" rowspan="2">Student Name</th>
-			{% for c in criteria %}
-			<th class="score" style="width: 100px">{{ c.assessment_criteria }}</th>
-			{% endfor %}
-			<th class="score" style="width: 170px" rowspan="2">Comments</th>
-			<th class="score" style="width: 100px">Total Marks</th>
-			<!--criteria-->
-		</tr>
-		<tr>
-			{% for c in criteria %}
-			<th class="score" style="width: 100px">Score ({{ c.maximum_score }})</th>
-			{% endfor %}
-			<th class="score" style="width: 100px">Score ({{max_total_score}})</th>
-		</tr>
-	</thead>
-	<tbody>
-		{% for s in students %}
-		<tr
-			{% if(s.assessment_details && s.docstatus && s.docstatus == 1) { %} class="text-muted" {% } %}
-			data-student="{{s.student}}">
-
-			<td>{{ s.student }}</td>
-			<td>{{ s.student_name }}</td>
-			{% for c in criteria %}
-			<td>
-				<span data-student="{{s.student}}" data-criteria="{{c.assessment_criteria}}" class="student-result-grade badge" >
-					{% if(s.assessment_details) { %}
-						{{s.assessment_details[c.assessment_criteria][1]}}
-					{% } %}
-				</span>
-				<input type="number" class="student-result-data" style="width:70%; float:right;"
-					data-max-score="{{c.maximum_score}}"
-					data-criteria="{{c.assessment_criteria}}"
-					data-student="{{s.student}}"
-					{% if(s.assessment_details && s.docstatus && s.docstatus == 1) { %} disabled {% } %}
-					{% if(s.assessment_details) { %}
-						value="{{s.assessment_details[c.assessment_criteria][0]}}"
-					{% } %}/>
-			</td>
-			{% endfor %}
-			<td>
-				<input type="text" class="result-comment" data-student="{{s.student}}"
-				{% if(s.assessment_details && s.docstatus && s.docstatus == 1) { %} disabled {% } %}
-				{% if(s.assessment_details) { %}
-					value="{{s.assessment_details.comment}}"
-				{% } %}
-			</td>
-			<td>
-				<span data-student="{{s.student}}" class="total-score-grade badge" style="width:30%; float:left;">
-				{% if(s.assessment_details) { %}
-				{{s.assessment_details.total_score[1]}}
-				{% } %}
-				</span>
-				<span data-student="{{s.student}}" class="total-score" style="width:60%; float:center;">
-				{% if(s.assessment_details) { %}
-				{{s.assessment_details.total_score[0]}}
-				{% } %}
-				</span>
-				<span data-student="{{s.student}}" class="total-result-link" style="width: 10%; display:{% if(!s.assessment_details) { %}None{% } %}; float:right;">
-					<a class="btn-open no-decoration" title="Open Link" href="/app/Form/Assessment Result/{% if(s.assessment_details) { %}{{s.name}}{% } %}">
-						<i class="octicon octicon-arrow-right"></i>
-					</a>
-				</span>
-			</td>
-		</tr>
-		{% endfor %}
-	</tbody>
-</table>
diff --git a/erpnext/public/js/education/lms/quiz.js b/erpnext/public/js/education/lms/quiz.js
deleted file mode 100644
index 66160a7..0000000
--- a/erpnext/public/js/education/lms/quiz.js
+++ /dev/null
@@ -1,238 +0,0 @@
-class Quiz {
-	constructor(wrapper, options) {
-		this.wrapper = wrapper;
-		Object.assign(this, options);
-		this.questions = []
-		this.refresh();
-	}
-
-	refresh() {
-		this.get_quiz();
-	}
-
-	get_quiz() {
-		frappe.call('erpnext.education.utils.get_quiz', {
-			quiz_name: this.name,
-			course: this.course
-		}).then(res => {
-			this.make(res.message)
-		});
-	}
-
-	make(data) {
-		if (data.is_time_bound) {
-			$(".lms-timer").removeClass("hide");
-			if (!data.activity || (data.activity && !data.activity.is_complete)) {
-				this.initialiseTimer(data.duration);
-				this.is_time_bound = true;
-				this.time_taken = 0;
-			}
-		}
-		data.questions.forEach(question_data => {
-			let question_wrapper = document.createElement('div');
-			let question = new Question({
-				wrapper: question_wrapper,
-				...question_data
-			});
-			this.questions.push(question)
-			this.wrapper.appendChild(question_wrapper);
-		})
-		if (data.activity && data.activity.is_complete) {
-			this.disable()
-			let indicator = 'red'
-			let message = 'Your are not allowed to attempt the quiz again.'
-			if (data.activity.result == 'Pass') {
-				indicator = 'green'
-				message = 'You have already cleared the quiz.'
-			}
-			if (data.activity.time_taken) {
-				this.calculate_and_display_time(data.activity.time_taken, "Time Taken - ");
-			}
-			this.set_quiz_footer(message, indicator, data.activity.score)
-		}
-		else {
-			this.make_actions();
-		}
-		window.addEventListener('beforeunload', (event) => {
-			event.preventDefault();
-			event.returnValue = '';
-		});
-	}
-
-	initialiseTimer(duration) {
-		this.time_left = duration;
-		var self = this;
-		var old_diff;
-		this.calculate_and_display_time(this.time_left, "Time Left - ");
-		this.start_time = new Date().getTime();
-		this.timer = setInterval(function () {
-			var diff = (new Date().getTime() - self.start_time)/1000;
-			var variation = old_diff ? diff - old_diff : diff;
-			old_diff = diff;
-			self.time_left -= variation;
-			self.time_taken += variation;
-			self.calculate_and_display_time(self.time_left, "Time Left - ");
-			if (self.time_left <= 0) {
-				clearInterval(self.timer);
-				self.time_taken -= 1;
-				self.submit();
-			}
-		}, 1000);
-	}
-
-	calculate_and_display_time(second, text) {
-		var timer_display = document.getElementsByClassName("lms-timer")[0];
-		var hours = this.append_zero(Math.floor(second / 3600));
-		var minutes = this.append_zero(Math.floor(second % 3600 / 60));
-		var seconds = this.append_zero(Math.ceil(second % 3600 % 60));
-		timer_display.innerText = text + hours + ":" + minutes + ":" + seconds;
-	}
-
-	append_zero(time) {
-		return time > 9 ? time : "0" + time;
-	}
-
-	make_actions() {
-		const button = document.createElement("button");
-		button.classList.add("btn", "btn-primary", "mt-5", "mr-2");
-
-		button.id = 'submit-button';
-		button.innerText = 'Submit';
-		button.onclick = () => this.submit();
-		this.submit_btn = button
-		this.wrapper.appendChild(button);
-	}
-
-	submit() {
-		if (this.is_time_bound) {
-			clearInterval(this.timer);
-			$(".lms-timer").text("");
-		}
-		this.submit_btn.innerText = 'Evaluating..'
-		this.submit_btn.disabled = true
-		this.disable()
-		frappe.call('erpnext.education.utils.evaluate_quiz', {
-			quiz_name: this.name,
-			quiz_response: this.get_selected(),
-			course: this.course,
-			program: this.program,
-			time_taken: this.is_time_bound ? this.time_taken : 0
-		}).then(res => {
-			this.submit_btn.remove()
-			if (!res.message) {
-				frappe.throw(__("Something went wrong while evaluating the quiz."))
-			}
-
-			let indicator = 'red'
-			let message = 'Fail'
-			if (res.message.status == 'Pass') {
-				indicator = 'green'
-				message = 'Congratulations, you cleared the quiz.'
-			}
-
-			this.set_quiz_footer(message, indicator, res.message.score)
-		});
-	}
-
-	set_quiz_footer(message, indicator, score) {
-		const div = document.createElement("div");
-		div.classList.add("mt-5");
-		div.innerHTML = `<div class="row">
-							<div class="col-md-8">
-								<h4>${message}</h4>
-								<h5 class="text-muted"><span class="indicator ${indicator}">Score: ${score}/100</span></h5>
-							</div>
-							<div class="col-md-4">
-								<a href="${this.next_url}" class="btn btn-primary pull-right">${this.quiz_exit_button}</a>
-							</div>
-						</div>`
-
-		this.wrapper.appendChild(div)
-	}
-
-	disable() {
-		this.questions.forEach(que => que.disable())
-	}
-
-	get_selected() {
-		let que = {}
-		this.questions.forEach(question => {
-			que[question.name] = question.get_selected()
-		})
-		return que
-	}
-}
-
-class Question {
-	constructor(opts) {
-		Object.assign(this, opts);
-		this.make();
-	}
-
-	make() {
-		this.make_question()
-		this.make_options()
-	}
-
-	get_selected() {
-		let selected = this.options.filter(opt => opt.input.checked)
-		if (this.type == 'Single Correct Answer') {
-			if (selected[0]) return selected[0].name
-		}
-		if (this.type == 'Multiple Correct Answer') {
-			return selected.map(opt => opt.name)
-		}
-		return null
-	}
-
-	disable() {
-		let selected = this.options.forEach(opt => opt.input.disabled = true)
-	}
-
-	make_question() {
-		let question_wrapper = document.createElement('h5');
-		question_wrapper.classList.add('mt-3');
-		question_wrapper.innerHTML = this.question;
-		this.wrapper.appendChild(question_wrapper);
-	}
-
-	make_options() {
-		let make_input = (name, value) => {
-			let input = document.createElement('input');
-			input.id = name;
-			input.name = this.name;
-			input.value = value;
-			input.type = 'radio';
-			if (this.type == 'Multiple Correct Answer')
-				input.type = 'checkbox';
-			input.classList.add('form-check-input');
-			return input;
-		}
-
-		let make_label = function (name, value) {
-			let label = document.createElement('label');
-			label.classList.add('form-check-label');
-			label.htmlFor = name;
-			label.innerText = value;
-			return label
-		}
-
-		let make_option = function (wrapper, option) {
-			let option_div = document.createElement('div');
-			option_div.classList.add('form-check', 'pb-1');
-			let input = make_input(option.name, option.option);
-			let label = make_label(option.name, option.option);
-			option_div.appendChild(input);
-			option_div.appendChild(label);
-			wrapper.appendChild(option_div);
-			return { input: input, ...option };
-		}
-
-		let options_wrapper = document.createElement('div')
-		options_wrapper.classList.add('ml-2')
-		let option_list = []
-		this.options.forEach(opt => option_list.push(make_option(options_wrapper, opt)))
-		this.options = option_list
-		this.wrapper.appendChild(options_wrapper)
-	}
-}
diff --git a/erpnext/public/js/erpnext.bundle.js b/erpnext/public/js/erpnext.bundle.js
index 8409e78..3dae6d4 100644
--- a/erpnext/public/js/erpnext.bundle.js
+++ b/erpnext/public/js/erpnext.bundle.js
@@ -16,9 +16,9 @@
 import "./utils/item_quick_entry";
 import "./utils/customer_quick_entry";
 import "./utils/supplier_quick_entry";
-import "./education/assessment_result_tool.html";
 import "./call_popup/call_popup";
 import "./utils/dimension_tree_filter";
+import "./utils/barcode_scanner";
 import "./telephony";
 import "./templates/call_link.html";
 import "./bulk_transaction_processing";
diff --git a/erpnext/public/js/setup_wizard.js b/erpnext/public/js/setup_wizard.js
index 83b69ae..9288f51 100644
--- a/erpnext/public/js/setup_wizard.js
+++ b/erpnext/public/js/setup_wizard.js
@@ -13,37 +13,6 @@
 
 erpnext.setup.slides_settings = [
 	{
-		// Domain
-		name: 'domain',
-		title: __('Select your Domains'),
-		fields: [
-			{
-				fieldname: 'domains',
-				label: __('Domains'),
-				fieldtype: 'MultiCheck',
-				options: [
-					{ "label": __("Distribution"), "value": "Distribution" },
-					{ "label": __("Education"), "value": "Education" },
-					{ "label": __("Manufacturing"), "value": "Manufacturing" },
-					{ "label": __("Retail"), "value": "Retail" },
-					{ "label": __("Services"), "value": "Services" },
-					{ "label": __("Healthcare (beta)"), "value": "Healthcare" },
-					{ "label": __("Non Profit (beta)"), "value": "Non Profit" }
-				], reqd: 1
-			},
-		],
-		// help: __('Select the nature of your business.'),
-		validate: function () {
-			if (this.values.domains.length === 0) {
-				frappe.msgprint(__("Please select at least one domain."));
-				return false;
-			}
-			frappe.setup.domains = this.values.domains;
-			return true;
-		},
-	},
-
-	{
 		// Brand
 		name: 'brand',
 		icon: "fa fa-bookmark",
@@ -59,15 +28,13 @@
 			},
 			{
 				fieldname: 'company_name',
-				label: frappe.setup.domains.includes('Education') ?
-					__('Institute Name') : __('Company Name'),
+				label: __('Company Name'),
 				fieldtype: 'Data',
 				reqd: 1
 			},
 			{
 				fieldname: 'company_abbr',
-				label: frappe.setup.domains.includes('Education') ?
-					__('Institute Abbreviation') : __('Company Abbreviation'),
+				label: __('Company Abbreviation'),
 				fieldtype: 'Data'
 			}
 		],
@@ -107,17 +74,12 @@
 		name: 'organisation',
 		title: __("Your Organization"),
 		icon: "fa fa-building",
-		// help: frappe.setup.domains.includes('Education') ?
-		// 	__('The name of the institute for which you are setting up this system.') :
-		// 	__('The name of your company for which you are setting up this system.')),
 		fields: [
 			{
 				fieldname: 'company_tagline',
 				label: __('What does it do?'),
 				fieldtype: 'Data',
-				placeholder: frappe.setup.domains.includes('Education') ?
-					__('e.g. "Primary School" or "University"') :
-					__('e.g. "Build tools for builders"'),
+				placeholder: __('e.g. "Build tools for builders"'),
 				reqd: 1
 			},
 			{ fieldname: 'bank_account', label: __('Bank Name'), fieldtype: 'Data', reqd: 1 },
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 9339c5d..01710f1 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -125,7 +125,7 @@
 	},
 
 	add_indicator_for_multicompany: function(frm, info) {
-		frm.dashboard.stats_area.removeClass('hidden');
+		frm.dashboard.stats_area.show();
 		frm.dashboard.stats_area_row.addClass('flex');
 		frm.dashboard.stats_area_row.css('flex-wrap', 'wrap');
 
@@ -213,8 +213,10 @@
 						filters.splice(index, 0, {
 							"fieldname": dimension["fieldname"],
 							"label": __(dimension["label"]),
-							"fieldtype": "Link",
-							"options": dimension["document_type"]
+							"fieldtype": "MultiSelectList",
+							get_data: function(txt) {
+								return frappe.db.get_link_options(dimension["document_type"], txt);
+							},
 						});
 					}
 				});
@@ -483,7 +485,7 @@
 			if (frm.doc.doctype == 'Sales Order') {
 				filters = {"is_sales_item": 1};
 			} else if (frm.doc.doctype == 'Purchase Order') {
-				if (frm.doc.is_subcontracted == "Yes") {
+				if (frm.doc.is_subcontracted) {
 					filters = {"is_sub_contracted_item": 1};
 				} else {
 					filters = {"is_purchase_item": 1};
diff --git a/erpnext/public/js/utils/barcode_scanner.js b/erpnext/public/js/utils/barcode_scanner.js
new file mode 100644
index 0000000..a6bff2c
--- /dev/null
+++ b/erpnext/public/js/utils/barcode_scanner.js
@@ -0,0 +1,229 @@
+erpnext.utils.BarcodeScanner = class BarcodeScanner {
+	constructor(opts) {
+		this.frm = opts.frm;
+
+		// field from which to capture input of scanned data
+		this.scan_field_name = opts.scan_field_name || "scan_barcode";
+		this.scan_barcode_field = this.frm.fields_dict[this.scan_field_name];
+
+		this.barcode_field = opts.barcode_field || "barcode";
+		this.serial_no_field = opts.serial_no_field || "serial_no";
+		this.batch_no_field = opts.batch_no_field || "batch_no";
+		this.uom_field = opts.uom_field || "uom";
+		this.qty_field = opts.qty_field || "qty";
+		// field name on row which defines max quantity to be scanned e.g. picklist
+		this.max_qty_field = opts.max_qty_field;
+		// scanner won't add a new row if this flag is set.
+		this.dont_allow_new_row = opts.dont_allow_new_row;
+		// scanner will ask user to type the quantity instead of incrementing by 1
+		this.prompt_qty = opts.prompt_qty;
+
+		this.items_table_name = opts.items_table_name || "items";
+		this.items_table = this.frm.doc[this.items_table_name];
+
+		// any API that takes `search_value` as input and returns dictionary as follows
+		// {
+		//     item_code: "HORSESHOE", // present if any item was found
+		//     bar_code: "123456", // present if barcode was scanned
+		//     batch_no: "LOT12", // present if batch was scanned
+		//     serial_no: "987XYZ", // present if serial no was scanned
+		//     uom: "Kg", // present if barcode UOM is different from default
+		// }
+		this.scan_api = opts.scan_api || "erpnext.stock.utils.scan_barcode";
+	}
+
+	process_scan() {
+		return new Promise((resolve, reject) => {
+			let me = this;
+
+			const input = this.scan_barcode_field.value;
+			this.scan_barcode_field.set_value("");
+			if (!input) {
+				return;
+			}
+
+			frappe
+				.call({
+					method: this.scan_api,
+					args: {
+						search_value: input,
+					},
+				})
+				.then((r) => {
+					const data = r && r.message;
+					if (!data || Object.keys(data).length === 0) {
+						this.show_alert(__("Cannot find Item with this Barcode"), "red");
+						this.clean_up();
+						reject();
+						return;
+					}
+
+					me.update_table(data).then(row => {
+						row ? resolve(row) : reject();
+					});
+				});
+		});
+	}
+
+	update_table(data) {
+		return new Promise(resolve => {
+			let cur_grid = this.frm.fields_dict[this.items_table_name].grid;
+
+			const {item_code, barcode, batch_no, serial_no, uom} = data;
+
+			let row = this.get_row_to_modify_on_scan(item_code, batch_no, uom);
+
+			if (!row) {
+				if (this.dont_allow_new_row) {
+					this.show_alert(__("Maximum quantity scanned for item {0}.", [item_code]), "red");
+					this.clean_up();
+					return;
+				}
+
+				// add new row if new item/batch is scanned
+				row = frappe.model.add_child(this.frm.doc, cur_grid.doctype, this.items_table_name);
+				// trigger any row add triggers defined on child table.
+				this.frm.script_manager.trigger(`${this.items_table_name}_add`, row.doctype, row.name);
+			}
+
+			if (this.is_duplicate_serial_no(row, serial_no)) {
+				this.clean_up();
+				return;
+			}
+
+			frappe.run_serially([
+				() => this.set_selector_trigger_flag(data),
+				() => this.set_item(row, item_code).then(qty => {
+					this.show_scan_message(row.idx, row.item_code, qty);
+				}),
+				() => this.set_barcode_uom(row, uom),
+				() => this.set_serial_no(row, serial_no),
+				() => this.set_batch_no(row, batch_no),
+				() => this.set_barcode(row, barcode),
+				() => this.clean_up(),
+				() => this.revert_selector_flag(),
+				() => resolve(row)
+			]);
+		});
+	}
+
+	// batch and serial selector is reduandant when all info can be added by scan
+	// this flag on item row is used by transaction.js to avoid triggering selector
+	set_selector_trigger_flag(data) {
+		const {batch_no, serial_no, has_batch_no, has_serial_no} = data;
+
+		const require_selecting_batch = has_batch_no && !batch_no;
+		const require_selecting_serial = has_serial_no && !serial_no;
+
+		if (!(require_selecting_batch || require_selecting_serial)) {
+			frappe.flags.hide_serial_batch_dialog = true;
+		}
+	}
+
+	revert_selector_flag() {
+		frappe.flags.hide_serial_batch_dialog = false;
+	}
+
+	set_item(row, item_code) {
+		return new Promise(resolve => {
+			const increment = async (value = 1) => {
+				const item_data = {item_code: item_code};
+				item_data[this.qty_field] = Number((row[this.qty_field] || 0)) + Number(value);
+				await frappe.model.set_value(row.doctype, row.name, item_data);
+				return value;
+			};
+
+			if (this.prompt_qty) {
+				frappe.prompt(__("Please enter quantity for item {0}", [item_code]), ({value}) => {
+					increment(value).then((value) => resolve(value));
+				});
+			} else {
+				increment().then((value) => resolve(value));
+			}
+		});
+	}
+
+	async set_serial_no(row, serial_no) {
+		if (serial_no && frappe.meta.has_field(row.doctype, this.serial_no_field)) {
+			const existing_serial_nos = row[this.serial_no_field];
+			let new_serial_nos = "";
+
+			if (!!existing_serial_nos) {
+				new_serial_nos = existing_serial_nos + "\n" + serial_no;
+			} else {
+				new_serial_nos = serial_no;
+			}
+			await frappe.model.set_value(row.doctype, row.name, this.serial_no_field, new_serial_nos);
+		}
+	}
+
+	async set_barcode_uom(row, uom) {
+		if (uom && frappe.meta.has_field(row.doctype, this.uom_field)) {
+			await frappe.model.set_value(row.doctype, row.name, this.uom_field, uom);
+		}
+	}
+
+	async set_batch_no(row, batch_no) {
+		if (batch_no && frappe.meta.has_field(row.doctype, this.batch_no_field)) {
+			await frappe.model.set_value(row.doctype, row.name, this.batch_no_field, batch_no);
+		}
+	}
+
+	async set_barcode(row, barcode) {
+		if (barcode && frappe.meta.has_field(row.doctype, this.barcode_field)) {
+			await frappe.model.set_value(row.doctype, row.name, this.barcode_field, barcode);
+		}
+	}
+
+	show_scan_message(idx, exist = null, qty = 1) {
+		// show new row or qty increase toast
+		if (exist) {
+			this.show_alert(__("Row #{0}: Qty increased by {1}", [idx, qty]), "green");
+		} else {
+			this.show_alert(__("Row #{0}: Item added", [idx]), "green")
+		}
+	}
+
+	is_duplicate_serial_no(row, serial_no) {
+		const is_duplicate = row[this.serial_no_field]?.includes(serial_no);
+
+		if (is_duplicate) {
+			this.show_alert(__("Serial No {0} is already added", [serial_no]), "orange");
+		}
+		return is_duplicate;
+	}
+
+	get_row_to_modify_on_scan(item_code, batch_no, uom) {
+		let cur_grid = this.frm.fields_dict[this.items_table_name].grid;
+
+		// Check if batch is scanned and table has batch no field
+		let is_batch_no_scan = batch_no && frappe.meta.has_field(cur_grid.doctype, this.batch_no_field);
+		let check_max_qty = this.max_qty_field && frappe.meta.has_field(cur_grid.doctype, this.max_qty_field);
+
+		const matching_row = (row) => {
+			const item_match = row.item_code == item_code;
+			const batch_match = row[this.batch_no_field] == batch_no;
+			const uom_match = !uom || row[this.uom_field] == uom;
+			const qty_in_limit = flt(row[this.qty_field]) < flt(row[this.max_qty_field]);
+
+			return item_match
+				&& uom_match
+				&& (!is_batch_no_scan || batch_match)
+				&& (!check_max_qty || qty_in_limit)
+		}
+
+		return this.items_table.find(matching_row) || this.get_existing_blank_row();
+	}
+
+	get_existing_blank_row() {
+		return this.items_table.find((d) => !d.item_code);
+	}
+
+	clean_up() {
+		this.scan_barcode_field.set_value("");
+		refresh_field(this.items_table_name);
+	}
+	show_alert(msg, indicator, duration=3) {
+		frappe.show_alert({message: msg, indicator: indicator}, duration);
+	}
+};
diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js
index 08270bd..64c5ee5 100644
--- a/erpnext/public/js/utils/serial_no_batch_selector.js
+++ b/erpnext/public/js/utils/serial_no_batch_selector.js
@@ -75,7 +75,7 @@
 				fieldtype:'Float',
 				read_only: me.has_batch && !me.has_serial_no,
 				label: __(me.has_batch && !me.has_serial_no ? 'Selected Qty' : 'Qty'),
-				default: flt(me.item.stock_qty),
+				default: flt(me.item.stock_qty) || flt(me.item.transfer_qty),
 			},
 			...get_pending_qty_fields(me),
 			{
@@ -94,14 +94,16 @@
 				description: __('Fetch Serial Numbers based on FIFO'),
 				click: () => {
 					let qty = this.dialog.fields_dict.qty.get_value();
+					let already_selected_serial_nos = get_selected_serial_nos(me);
 					let numbers = frappe.call({
 						method: "erpnext.stock.doctype.serial_no.serial_no.auto_fetch_serial_number",
 						args: {
 							qty: qty,
 							item_code: me.item_code,
 							warehouse: typeof me.warehouse_details.name == "string" ? me.warehouse_details.name : '',
-							batch_no: me.item.batch_no || null,
-							posting_date: me.frm.doc.posting_date || me.frm.doc.transaction_date
+							batch_nos: me.item.batch_no || null,
+							posting_date: me.frm.doc.posting_date || me.frm.doc.transaction_date,
+							exclude_sr_nos: already_selected_serial_nos
 						}
 					});
 
@@ -577,21 +579,40 @@
 	return pending_qty_fields;
 }
 
-function calc_total_selected_qty(me) {
+// get all items with same item code except row for which selector is open.
+function get_rows_with_same_item_code(me) {
 	const { frm: { doc: { items }}, item: { name, item_code }} = me;
-	const totalSelectedQty = items
-		.filter( item => ( item.name !== name ) && ( item.item_code === item_code ) )
-		.map( item => flt(item.qty) )
-		.reduce( (i, j) => i + j, 0);
+	return items.filter(item => (item.name !== name) && (item.item_code === item_code))
+}
+
+function calc_total_selected_qty(me) {
+	const totalSelectedQty = get_rows_with_same_item_code(me)
+		.map(item => flt(item.qty))
+		.reduce((i, j) => i + j, 0);
 	return totalSelectedQty;
 }
 
+function get_selected_serial_nos(me) {
+	const selected_serial_nos = get_rows_with_same_item_code(me)
+		.map(item => item.serial_no)
+		.filter(serial => serial)
+		.map(sr_no_string => sr_no_string.split('\n'))
+		.reduce((acc, arr) => acc.concat(arr), [])
+		.filter(serial => serial);
+	return selected_serial_nos;
+};
+
 function check_can_calculate_pending_qty(me) {
 	const { frm: { doc }, item } = me;
 	const docChecks = doc.bom_no
 		&& doc.fg_completed_qty
 		&& erpnext.stock.bom
 		&& erpnext.stock.bom.name === doc.bom_no;
-	const itemChecks = !!item  && !item.allow_alternative_item;
+	const itemChecks = !!item
+		&& !item.original_item
+		&& erpnext.stock.bom && erpnext.stock.bom.items
+		&& (item.item_code in erpnext.stock.bom.items);
 	return docChecks && itemChecks;
 }
+
+//# sourceURL=serial_no_batch_selector.js
diff --git a/erpnext/public/scss/shopping_cart.scss b/erpnext/public/scss/shopping_cart.scss
index 666043b..6ae464d 100644
--- a/erpnext/public/scss/shopping_cart.scss
+++ b/erpnext/public/scss/shopping_cart.scss
@@ -264,6 +264,15 @@
 			font-size: 13px;
 		}
 
+		.filter-lookup-input {
+			background-color: white;
+			border: 1px solid var(--gray-300);
+
+			&:focus {
+				border: 1px solid var(--primary);
+			}
+		}
+
 		.filter-label {
 			font-size: 11px;
 			font-weight: 600;
@@ -569,15 +578,12 @@
 }
 
 .scroll-categories {
-	white-space: nowrap;
-	overflow-x: auto;
-
 	.category-pill {
-		margin: 0px 4px;
 		display: inline-block;
-		padding: 6px 12px;
-		background-color: #ecf5fe;
 		width: fit-content;
+		padding: 6px 12px;
+		margin-bottom: 8px;
+		background-color: #ecf5fe;
 		font-size: 14px;
 		border-radius: 18px;
 		color: var(--blue-500);
diff --git a/erpnext/quality_management/doctype/quality_action/quality_action.py b/erpnext/quality_management/doctype/quality_action/quality_action.py
index 87245f9..f7cd94d 100644
--- a/erpnext/quality_management/doctype/quality_action/quality_action.py
+++ b/erpnext/quality_management/doctype/quality_action/quality_action.py
@@ -7,4 +7,4 @@
 
 class QualityAction(Document):
 	def validate(self):
-		self.status = 'Open' if any([d.status=='Open' for d in self.resolutions]) else 'Completed'
+		self.status = "Open" if any([d.status == "Open" for d in self.resolutions]) else "Completed"
diff --git a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py
index ec5d67f..cc8ce26 100644
--- a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py
+++ b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py
@@ -9,15 +9,12 @@
 class QualityFeedback(Document):
 	@frappe.whitelist()
 	def set_parameters(self):
-		if self.template and not getattr(self, 'parameters', []):
-			for d in frappe.get_doc('Quality Feedback Template', self.template).parameters:
-				self.append('parameters', dict(
-					parameter = d.parameter,
-					rating = 1
-				))
+		if self.template and not getattr(self, "parameters", []):
+			for d in frappe.get_doc("Quality Feedback Template", self.template).parameters:
+				self.append("parameters", dict(parameter=d.parameter, rating=1))
 
 	def validate(self):
 		if not self.document_name:
-			self.document_type ='User'
+			self.document_type = "User"
 			self.document_name = frappe.session.user
 		self.set_parameters()
diff --git a/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py b/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py
index fe36cc6..58d0632 100644
--- a/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py
+++ b/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py
@@ -8,21 +8,22 @@
 
 class TestQualityFeedback(unittest.TestCase):
 	def test_quality_feedback(self):
-		template = frappe.get_doc(dict(
-			doctype = 'Quality Feedback Template',
-			template = 'Test Template',
-			parameters = [
-				dict(parameter='Test Parameter 1'),
-				dict(parameter='Test Parameter 2')
-			]
-		)).insert()
+		template = frappe.get_doc(
+			dict(
+				doctype="Quality Feedback Template",
+				template="Test Template",
+				parameters=[dict(parameter="Test Parameter 1"), dict(parameter="Test Parameter 2")],
+			)
+		).insert()
 
-		feedback = frappe.get_doc(dict(
-			doctype = 'Quality Feedback',
-			template = template.name,
-			document_type = 'User',
-			document_name = frappe.session.user
-		)).insert()
+		feedback = frappe.get_doc(
+			dict(
+				doctype="Quality Feedback",
+				template=template.name,
+				document_type="User",
+				document_name=frappe.session.user,
+			)
+		).insert()
 
 		self.assertEqual(template.parameters[0].parameter, feedback.parameters[0].parameter)
 
diff --git a/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py b/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py
index 67fdaca..40606cd 100644
--- a/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py
+++ b/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py
@@ -13,12 +13,13 @@
 		self.assertTrue(goal)
 		goal.delete()
 
+
 def get_quality_goal():
-	return frappe.get_doc(dict(
-		doctype = 'Quality Goal',
-		goal = 'Test Quality Module',
-		frequency = 'Daily',
-		objectives = [
-			dict(objective = 'Check test cases', target='100', uom='Percent')
-		]
-	)).insert()
+	return frappe.get_doc(
+		dict(
+			doctype="Quality Goal",
+			goal="Test Quality Module",
+			frequency="Daily",
+			objectives=[dict(objective="Check test cases", target="100", uom="Percent")],
+		)
+	).insert()
diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
index 0f535ba..72f9e6d 100644
--- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
+++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
@@ -8,7 +8,7 @@
 
 
 class QualityProcedure(NestedSet):
-	nsm_parent_field = 'parent_quality_procedure'
+	nsm_parent_field = "parent_quality_procedure"
 
 	def before_save(self):
 		self.check_for_incorrect_child()
@@ -29,14 +29,19 @@
 
 	def on_trash(self):
 		# clear from child table (sub procedures)
-		frappe.db.sql('''update `tabQuality Procedure Process`
-			set `procedure`='' where `procedure`=%s''', self.name)
+		frappe.db.sql(
+			"""update `tabQuality Procedure Process`
+			set `procedure`='' where `procedure`=%s""",
+			self.name,
+		)
 		NestedSet.on_trash(self, allow_root_deletion=True)
 
 	def set_parent(self):
 		for process in self.processes:
 			# Set parent for only those children who don't have a parent
-			has_parent = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure")
+			has_parent = frappe.db.get_value(
+				"Quality Procedure", process.procedure, "parent_quality_procedure"
+			)
 			if not has_parent and process.procedure:
 				frappe.db.set_value(self.doctype, process.procedure, "parent_quality_procedure", self.name)
 
@@ -45,10 +50,17 @@
 			if process.procedure:
 				self.is_group = 1
 				# Check if any child process belongs to another parent.
-				parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure")
+				parent_quality_procedure = frappe.db.get_value(
+					"Quality Procedure", process.procedure, "parent_quality_procedure"
+				)
 				if parent_quality_procedure and parent_quality_procedure != self.name:
-					frappe.throw(_("{0} already has a Parent Procedure {1}.").format(frappe.bold(process.procedure), frappe.bold(parent_quality_procedure)),
-						title=_("Invalid Child Procedure"))
+					frappe.throw(
+						_("{0} already has a Parent Procedure {1}.").format(
+							frappe.bold(process.procedure), frappe.bold(parent_quality_procedure)
+						),
+						title=_("Invalid Child Procedure"),
+					)
+
 
 @frappe.whitelist()
 def get_children(doctype, parent=None, parent_quality_procedure=None, is_root=False):
@@ -56,16 +68,23 @@
 		parent = ""
 
 	if parent:
-		parent_procedure = frappe.get_doc('Quality Procedure', parent)
+		parent_procedure = frappe.get_doc("Quality Procedure", parent)
 		# return the list in order
-		return [dict(
-				value=d.procedure,
-				expandable=frappe.db.get_value('Quality Procedure', d.procedure, 'is_group'))
-				for d in parent_procedure.processes if d.procedure
-			]
+		return [
+			dict(
+				value=d.procedure, expandable=frappe.db.get_value("Quality Procedure", d.procedure, "is_group")
+			)
+			for d in parent_procedure.processes
+			if d.procedure
+		]
 	else:
-		return frappe.get_all(doctype, fields=['name as value', 'is_group as expandable'],
-			filters = dict(parent_quality_procedure = parent), order_by='name asc')
+		return frappe.get_all(
+			doctype,
+			fields=["name as value", "is_group as expandable"],
+			filters=dict(parent_quality_procedure=parent),
+			order_by="name asc",
+		)
+
 
 @frappe.whitelist()
 def add_node():
@@ -74,7 +93,7 @@
 	args = frappe.form_dict
 	args = make_tree_args(**args)
 
-	if args.parent_quality_procedure == 'All Quality Procedures':
+	if args.parent_quality_procedure == "All Quality Procedures":
 		args.parent_quality_procedure = None
 
 	return frappe.get_doc(args).insert()
diff --git a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
index 6130895..04e8211 100644
--- a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
+++ b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
@@ -11,16 +11,21 @@
 class TestQualityProcedure(unittest.TestCase):
 	def test_add_node(self):
 		try:
-			procedure = frappe.get_doc(dict(
-				doctype = 'Quality Procedure',
-				quality_procedure_name = 'Test Procedure 1',
-				processes = [
-					dict(process_description = 'Test Step 1')
-				]
-			)).insert()
+			procedure = frappe.get_doc(
+				dict(
+					doctype="Quality Procedure",
+					quality_procedure_name="Test Procedure 1",
+					processes=[dict(process_description="Test Step 1")],
+				)
+			).insert()
 
-			frappe.form_dict = dict(doctype = 'Quality Procedure', quality_procedure_name = 'Test Child 1',
-				parent_quality_procedure = procedure.name, cmd='test', is_root='false')
+			frappe.local.form_dict = frappe._dict(
+				doctype="Quality Procedure",
+				quality_procedure_name="Test Child 1",
+				parent_quality_procedure=procedure.name,
+				cmd="test",
+				is_root="false",
+			)
 			node = add_node()
 
 			procedure.reload()
@@ -39,12 +44,13 @@
 		finally:
 			procedure.delete()
 
+
 def create_procedure():
-	return frappe.get_doc(dict(
-		doctype = 'Quality Procedure',
-		quality_procedure_name = 'Test Procedure 1',
-		is_group = 1,
-		processes = [
-			dict(process_description = 'Test Step 1')
-		]
-	)).insert()
+	return frappe.get_doc(
+		dict(
+			doctype="Quality Procedure",
+			quality_procedure_name="Test Procedure 1",
+			is_group=1,
+			processes=[dict(process_description="Test Step 1")],
+		)
+	).insert()
diff --git a/erpnext/quality_management/doctype/quality_review/quality_review.py b/erpnext/quality_management/doctype/quality_review/quality_review.py
index b896f8d..f691005 100644
--- a/erpnext/quality_management/doctype/quality_review/quality_review.py
+++ b/erpnext/quality_management/doctype/quality_review/quality_review.py
@@ -10,55 +10,52 @@
 	def validate(self):
 		# fetch targets from goal
 		if not self.reviews:
-			for d in frappe.get_doc('Quality Goal', self.goal).objectives:
-				self.append('reviews', dict(
-					objective = d.objective,
-					target = d.target,
-					uom = d.uom
-				))
+			for d in frappe.get_doc("Quality Goal", self.goal).objectives:
+				self.append("reviews", dict(objective=d.objective, target=d.target, uom=d.uom))
 
 		self.set_status()
 
 	def set_status(self):
 		# if any child item is failed, fail the parent
-		if not len(self.reviews or []) or any([d.status=='Open' for d in self.reviews]):
-			self.status = 'Open'
-		elif any([d.status=='Failed' for d in self.reviews]):
-			self.status = 'Failed'
+		if not len(self.reviews or []) or any([d.status == "Open" for d in self.reviews]):
+			self.status = "Open"
+		elif any([d.status == "Failed" for d in self.reviews]):
+			self.status = "Failed"
 		else:
-			self.status = 'Passed'
+			self.status = "Passed"
+
 
 def review():
 	day = frappe.utils.getdate().day
 	weekday = frappe.utils.getdate().strftime("%A")
 	month = frappe.utils.getdate().strftime("%B")
 
-	for goal in frappe.get_list("Quality Goal", fields=['name', 'frequency', 'date', 'weekday']):
-		if goal.frequency == 'Daily':
+	for goal in frappe.get_list("Quality Goal", fields=["name", "frequency", "date", "weekday"]):
+		if goal.frequency == "Daily":
 			create_review(goal.name)
 
-		elif goal.frequency == 'Weekly' and goal.weekday == weekday:
+		elif goal.frequency == "Weekly" and goal.weekday == weekday:
 			create_review(goal.name)
 
-		elif goal.frequency == 'Monthly' and goal.date == str(day):
+		elif goal.frequency == "Monthly" and goal.date == str(day):
 			create_review(goal.name)
 
-		elif goal.frequency == 'Quarterly' and day==1 and get_quarter(month):
+		elif goal.frequency == "Quarterly" and day == 1 and get_quarter(month):
 			create_review(goal.name)
 
+
 def create_review(goal):
 	goal = frappe.get_doc("Quality Goal", goal)
 
-	review = frappe.get_doc({
-		"doctype": "Quality Review",
-		"goal": goal.name,
-		"date": frappe.utils.getdate()
-	})
+	review = frappe.get_doc(
+		{"doctype": "Quality Review", "goal": goal.name, "date": frappe.utils.getdate()}
+	)
 
 	review.insert(ignore_permissions=True)
 
+
 def get_quarter(month):
-	if month in  ["January", "April", "July", "October"]:
+	if month in ["January", "April", "July", "October"]:
 		return True
 	else:
 		return False
diff --git a/erpnext/quality_management/doctype/quality_review/test_quality_review.py b/erpnext/quality_management/doctype/quality_review/test_quality_review.py
index 8a254db..c76e7f2 100644
--- a/erpnext/quality_management/doctype/quality_review/test_quality_review.py
+++ b/erpnext/quality_management/doctype/quality_review/test_quality_review.py
@@ -15,7 +15,7 @@
 		review()
 
 		# check if review exists
-		quality_review = frappe.get_doc('Quality Review', dict(goal = quality_goal.name))
+		quality_review = frappe.get_doc("Quality Review", dict(goal=quality_goal.name))
 		self.assertEqual(quality_goal.objectives[0].target, quality_review.reviews[0].target)
 		quality_review.delete()
 
diff --git a/erpnext/regional/__init__.py b/erpnext/regional/__init__.py
index c460286..ec2db81 100644
--- a/erpnext/regional/__init__.py
+++ b/erpnext/regional/__init__.py
@@ -13,6 +13,7 @@
 	if region in ["Nepal", "France"] and doc.docstatus != 0:
 		frappe.throw(_("Deletion is not permitted for country {0}").format(region))
 
+
 def create_transaction_log(doc, method):
 	"""
 	Appends the transaction to a chain of hashed logs for legal resons.
@@ -24,10 +25,11 @@
 
 	data = str(doc.as_dict())
 
-	frappe.get_doc({
-		"doctype": "Transaction Log",
-		"reference_doctype": doc.doctype,
-		"document_name": doc.name,
-		"data": data
-	}).insert(ignore_permissions=True)
-
+	frappe.get_doc(
+		{
+			"doctype": "Transaction Log",
+			"reference_doctype": doc.doctype,
+			"document_name": doc.name,
+			"data": data,
+		}
+	).insert(ignore_permissions=True)
diff --git a/erpnext/regional/address_template/setup.py b/erpnext/regional/address_template/setup.py
index 1b4087d..fd1dfa7 100644
--- a/erpnext/regional/address_template/setup.py
+++ b/erpnext/regional/address_template/setup.py
@@ -2,17 +2,20 @@
 import os
 import frappe
 
+
 def set_up_address_templates(default_country=None):
 	for country, html in get_address_templates():
 		is_default = 1 if country == default_country else 0
 		update_address_template(country, html, is_default)
 
+
 def get_address_templates():
 	"""
 	Return country and path for all HTML files in this directory.
 
 	Returns a list of dicts.
 	"""
+
 	def country(file_name):
 		"""Convert 'united_states.html' to 'United States'."""
 		suffix_pos = file_name.find(".html")
@@ -45,9 +48,6 @@
 		frappe.db.set_value("Address Template", country, "template", html)
 		frappe.db.set_value("Address Template", country, "is_default", is_default)
 	else:
-		frappe.get_doc(dict(
-			doctype="Address Template",
-			country=country,
-			is_default=is_default,
-			template=html
-		)).insert()
+		frappe.get_doc(
+			dict(doctype="Address Template", country=country, is_default=is_default, template=html)
+		).insert()
diff --git a/erpnext/regional/address_template/test_regional_address_template.py b/erpnext/regional/address_template/test_regional_address_template.py
index 780db40..523653b 100644
--- a/erpnext/regional/address_template/test_regional_address_template.py
+++ b/erpnext/regional/address_template/test_regional_address_template.py
@@ -9,13 +9,11 @@
 	if frappe.db.exists("Country", country):
 		return frappe.get_doc("Country", country)
 	else:
-		c = frappe.get_doc({
-			"doctype": "Country",
-			"country_name": country
-		})
+		c = frappe.get_doc({"doctype": "Country", "country_name": country})
 		c.insert()
 		return c
 
+
 class TestRegionalAddressTemplate(TestCase):
 	def test_get_address_templates(self):
 		"""Get the countries and paths from the templates directory."""
@@ -34,11 +32,9 @@
 		"""Update an existing Address Template."""
 		country = ensure_country("Germany")
 		if not frappe.db.exists("Address Template", country.name):
-			template = frappe.get_doc({
-				"doctype": "Address Template",
-				"country": country.name,
-				"template": "EXISTING"
-			}).insert()
+			template = frappe.get_doc(
+				{"doctype": "Address Template", "country": country.name, "template": "EXISTING"}
+			).insert()
 
 		update_address_template(country.name, "NEW")
 		doc = frappe.get_doc("Address Template", country.name)
diff --git a/erpnext/regional/doctype/datev_settings/datev_settings.js b/erpnext/regional/doctype/datev_settings/datev_settings.js
deleted file mode 100644
index f047059..0000000
--- a/erpnext/regional/doctype/datev_settings/datev_settings.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('DATEV Settings', {
-	refresh: function(frm) {
-		frm.add_custom_button('Show Report', () => frappe.set_route('query-report', 'DATEV'), "fa fa-table");
-	}
-});
diff --git a/erpnext/regional/doctype/datev_settings/datev_settings.json b/erpnext/regional/doctype/datev_settings/datev_settings.json
deleted file mode 100644
index f60de4c..0000000
--- a/erpnext/regional/doctype/datev_settings/datev_settings.json
+++ /dev/null
@@ -1,125 +0,0 @@
-{
- "actions": [],
- "autoname": "field:client",
- "creation": "2019-08-13 23:56:34.259906",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "client",
-  "client_number",
-  "column_break_2",
-  "consultant_number",
-  "consultant",
-  "section_break_4",
-  "account_number_length",
-  "column_break_6",
-  "temporary_against_account_number"
- ],
- "fields": [
-  {
-   "fieldname": "client",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Client",
-   "options": "Company",
-   "reqd": 1,
-   "unique": 1
-  },
-  {
-   "fieldname": "client_number",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Client ID",
-   "length": 5,
-   "reqd": 1
-  },
-  {
-   "fieldname": "consultant",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Consultant",
-   "options": "Supplier"
-  },
-  {
-   "fieldname": "consultant_number",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Consultant ID",
-   "length": 7,
-   "reqd": 1
-  },
-  {
-   "fieldname": "column_break_2",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "section_break_4",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fieldname": "column_break_6",
-   "fieldtype": "Column Break"
-  },
-  {
-   "default": "4",
-   "fieldname": "account_number_length",
-   "fieldtype": "Int",
-   "label": "Account Number Length",
-   "reqd": 1
-  },
-  {
-   "allow_in_quick_entry": 1,
-   "fieldname": "temporary_against_account_number",
-   "fieldtype": "Data",
-   "label": "Temporary Against Account Number",
-   "reqd": 1
-  }
- ],
- "links": [],
- "modified": "2020-11-19 19:00:09.088816",
- "modified_by": "Administrator",
- "module": "Regional",
- "name": "DATEV Settings",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "System Manager",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Accounts Manager",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Accounts User",
-   "share": 1
-  }
- ],
- "quick_entry": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/datev_settings/datev_settings.py b/erpnext/regional/doctype/datev_settings/datev_settings.py
deleted file mode 100644
index 686a93e..0000000
--- a/erpnext/regional/doctype/datev_settings/datev_settings.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-# import frappe
-from frappe.model.document import Document
-
-
-class DATEVSettings(Document):
-	pass
diff --git a/erpnext/regional/doctype/datev_settings/test_datev_settings.py b/erpnext/regional/doctype/datev_settings/test_datev_settings.py
deleted file mode 100644
index ba70eb4..0000000
--- a/erpnext/regional/doctype/datev_settings/test_datev_settings.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-# import frappe
-import unittest
-
-
-class TestDATEVSettings(unittest.TestCase):
-	pass
diff --git a/erpnext/regional/doctype/gst_settings/hsn_code_data.json b/erpnext/regional/doctype/gst_settings/hsn_code_data.json
new file mode 100644
index 0000000..b615394
--- /dev/null
+++ b/erpnext/regional/doctype/gst_settings/hsn_code_data.json
@@ -0,0 +1,144352 @@
+[
+    {
+        "description": "OTHER KNITTED OR CROCHETED FABRICS",
+        "hsn_code": "6006",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER KNITTED OR CROCHETED FABRICS- OTHER",
+        "hsn_code": "60069000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER KNITTED OR CROCHETED FABRICS- OF ARTIFICIAL FIBRES : PRINTED",
+        "hsn_code": "60064400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER KNITTED OR CROCHETED FABRICS- OF ARTIFICIAL FIBRES : OF YARNS OF DIFFERENT\nCOLOURS",
+        "hsn_code": "60064300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER KNITTED OR CROCHETED FABRICS- OF ARTIFICIAL FIBRES : DYED",
+        "hsn_code": "60064200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER KNITTED OR CROCHETED FABRICS- OF ARTIFICIAL FIBRES : UNBLEACHED OR BLEACHED",
+        "hsn_code": "60064100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER KNITTED OR CROCHETED FABRICS- OF SYNTHETIC FIBRES: PRINTED",
+        "hsn_code": "60063400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER KNITTED OR CROCHETED FABRICS- OF SYNTHETIC FIBRES: OF YARNS OF DIFFERENT\nCOLOURS",
+        "hsn_code": "60063300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER KNITTED OR CROCHETED FABRICS- OF SYNTHETIC FIBRES: DYED",
+        "hsn_code": "60063200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER KNITTED OR CROCHETED FABRICS- OF SYNTHETIC FIBRES: UNBLEACHED OR BLEACHED",
+        "hsn_code": "60063100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER KNITTED OR CROCHETED FABRICS- OF COTTON: PRINTED",
+        "hsn_code": "60062400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER KNITTED OR CROCHETED FABRICS- OF COTTON: OF YARNS OF DIFFERENT COLOURS",
+        "hsn_code": "60062300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER KNITTED OR CROCHETED FABRICS- OF COTTON: DYED",
+        "hsn_code": "60062200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER KNITTED OR CROCHETED FABRICS- OF COTTON: UNBLEACHED OR BLEACHED",
+        "hsn_code": "60062100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER KNITTED OR CROCHETED FABRICS - OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "60061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR; OTHER GARMENTS",
+        "hsn_code": "6211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR; OTHER GARMENTS - OTHER GARMENTS, WOMENS OR\nGIRLS --OF OTHER TEXTILE MATERIALS---OTHER",
+        "hsn_code": "62114990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR; OTHER GARMENTS - OTHER GARMENTS, WOMENS OR\nGIRLS --OF OTHER TEXTILE MATERIALS---OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "62114910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR; OTHER GARMENTS - OTHER GARMENTS, WOMENS OR\nGIRLS : OF MAN-MADE FIBRES",
+        "hsn_code": "62114300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR; OTHER GARMENTS - OTHER GARMENTS, WOMENS OR\nGIRLS : OF COTTON : OTHER",
+        "hsn_code": "62114290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR; OTHER GARMENTS - OTHER GARMENTS, WOMENS OR\nGIRLS : OF COTTON : KURTA AND SALWAR WITH OR WITHOUT DUPPATTA",
+        "hsn_code": "62114210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR; OTHER GARMENTS - OTHER GARMENTS, MENS OR\nBOYS : OF OTHER TEXTILE MATERIALS",
+        "hsn_code": "62113900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR; OTHER GARMENTS - OTHER GARMENTS, MENS OR\nBOYS : OF MAN-MADE FIBRES",
+        "hsn_code": "62113300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR; OTHER GARMENTS - OTHER GARMENTS, MENS OR\nBOYS : OF COTTON",
+        "hsn_code": "62113200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR; OTHER GARMENTS - OTHER GARMENTS, MENS OR\nBOYS: OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "62113100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR; OTHER GARMENTS - SKI SUITS",
+        "hsn_code": "62112000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR; OTHER GARMENTS - SWIMWEAR : WOMENS OR GIRLS",
+        "hsn_code": "62111200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR; OTHER GARMENTS - SWIMWEAR : MENS OR BOYS",
+        "hsn_code": "62111100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH COMPRESSION IGNITION INTERNAL COMBUSTION PISTON ENGINE\n(DIESEL OR SEMI-DIESEL) : OF A CYLINDER CAPACITY NOT EXCEEDING 1,500 CC : VEHICLES\nPRINCIPALLY DESIGNED FOR THE TRANSPORT OF MORE THAN SEVEN PERSONS, INCLUDING THE\nDRIVER",
+        "hsn_code": "87033110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH COMPRESSION IGNITION INTERNAL COMBUSTION PISTON ENGINE\n(DIESEL OR SEMI-DIESEL) : OF A CYLINDER CAPACITY NOT EXCEEDING 1,500 CC : THREE-WHEELED\nVEHICLES",
+        "hsn_code": "87033120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH COMPRESSION IGNITION INTERNAL COMBUSTION PISTON ENGINE\n(DIESEL OR SEMI-DIESEL) : OF A CYLINDER CAPACITY NOT EXCEEDING 1,500 CC : OTHER MOTOR\nCARS",
+        "hsn_code": "87033191",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH COMPRESSION IGNITION INTERNAL COMBUSTION PISTON ENGINE\n(DIESEL OR SEMI-DIESEL) : OF A CYLINDER CAPACITY NOT EXCEEDING 1,500 CC : OTHER\nSPECIALISED TRANSPORT VEHICLES SUCH AS AMBULANCES, PRISON VANS AND THE LIKE",
+        "hsn_code": "87033192",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH COMPRESSION IGNITION INTERNAL COMBUSTION PISTON ENGINE\n(DIESEL OR SEMI-DIESEL) : OF A CYLINDER CAPACITY NOT EXCEEDING 1,500 CC : OTHER",
+        "hsn_code": "87033199",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF JEWELLERY AND PARTS THEREOF, OF PRECIOUS METAL OR OF METAL CLAD WITH\nPRECIOUS METAL - OF PRECIOUS METAL WHETHER OR NOT PLATED OR CLAD WITH PRECIOUS\nMETAL - OF SILVER, WHETHER OR NOT PLATED OR CLAD WITH OTHER PRECIOUS METAL:\nJEWELLERY WITH FILIGREE WORK",
+        "hsn_code": "71131110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "WATERS, INCLUDING MINERAL WATERS ANDAERATED WATERS, CONTAINING ADDED SUGAROR\nOTHER SWEETENING MATTER OR FLAVOURED,AND OTHER NON - ALCOHOLIC BEVERAGES,\nNOTINCLUDING FRUIT OR VEGETABLE JUICES OFHEADING 2009 - WATERS, INCLUDING MINERAL\nWATERS AND AERATEDWATERS, CONTAINING ADDED SUGAR OR OTHERSWEETENING MATTER\nOR FLAVOURED : AERATED WATERS",
+        "hsn_code": "22021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "WATERS, INCLUDING MINERAL WATERS ANDAERATED WATERS, CONTAINING ADDED SUGAROR\nOTHER SWEETENING MATTER OR FLAVOURED,AND OTHER NON - ALCOHOLIC BEVERAGES,\nNOTINCLUDING FRUIT OR VEGETABLE JUICES OFHEADING 2009 - WATERS, INCLUDING MINERAL\nWATERS AND AERATEDWATERS, CONTAINING ADDED SUGAR OR OTHERSWEETENING MATTER\nOR FLAVOURED : LEMONADE",
+        "hsn_code": "22021020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "WATERS, INCLUDING MINERAL WATERS ANDAERATED WATERS, CONTAINING ADDED SUGAROR\nOTHER SWEETENING MATTER OR FLAVOURED,AND OTHER NON - ALCOHOLIC BEVERAGES,\nNOTINCLUDING FRUIT OR VEGETABLE JUICES OFHEADING 2009 - WATERS, INCLUDING MINERAL\nWATERS AND AERATEDWATERS, CONTAINING ADDED SUGAR OR OTHERSWEETENING MATTER\nOR FLAVOURED : OTHER",
+        "hsn_code": "22021090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 - OTHER PARTS\nAND ACCESSORIES -- OTHER",
+        "hsn_code": "87089900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 - OTHER PARTS\nAND ACCESSORIES -- STEERING WHEELS, STEERING COLUMNS AND STEERING BOXES, PARTS\nTHEREOF",
+        "hsn_code": "87089400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 - OTHER PARTS\nAND ACCESSORIES -- CLUTCHES AND PARTS THEREOF",
+        "hsn_code": "87089300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 - OTHER PARTS\nAND ACCESSORIES -- SILENCERS (MUFFLERS) AND EXHAUST PIPES; PARTS THEREOF",
+        "hsn_code": "87089200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 - OTHER PARTS\nAND ACCESSORIES -- RADIATORS AND PARTS THEREOF",
+        "hsn_code": "87089100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 - ROAD WHEELS\nAND PARTS AND ACCESSORIES THEREOF",
+        "hsn_code": "87087000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 - DRIVE-AXLES\nWITH DIFFERENTIAL, WHETHER OR NOT PROVIDED WITH OTHER TRANSMISSION COMPONENTS,\nAND NON-DRIVING AXLES, PARTS THEREOF",
+        "hsn_code": "87085000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 - GEAR BOXES\nAND PARTS THEREOF",
+        "hsn_code": "87084000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 - BRAKES AND\nSERVO-BRAKES ; PARTS THEREOF",
+        "hsn_code": "87083000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 BUMPERS AND\nPARTS THEREOF FOR TRACTORS",
+        "hsn_code": "87081010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER PUMPS-LIQUID ELEVATORS: 8413 81 - PUMPS : OTHER",
+        "hsn_code": "84138190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPRESSION-IGNITION INTERNAL COMBUSTION PISTON ENGINES (DIESEL OR SEMI-DIESEL\nENGINES) - ENGINES OF A KIND USED FOR THE PROPULSION OF VEHICLES OF CHAPTER 87:\nENGINES OF CYLINDER CAPACITY EXCEEDING 250 CC",
+        "hsn_code": "84082020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INNER TUBES, OF RUBBER - OTHER : - FOR TRACTORS : OTHER",
+        "hsn_code": "40139049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND SIEVES AND HAND RIDDLES",
+        "hsn_code": "96040000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BROOMS, BRUSHES (INCLUDING BRUSHES CONSTITUTING PARTS OF MACHINES, APPLIANCES OR VEHICLES), HAND-OPERATED MECHANICAL FLOOR SWEEPERS, NOT MOTORISED, MOPS AND FEATHER DUSTERS; PREPARED KNOTS AND TUFTS FOR BROOM OR BRUSH MAKING; PAINT PADS AND ROLLERS; SQUEEGEES (OTHER THAN ROLLER SQUEEGEES)",
+        "hsn_code": "9603",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BROOMS, BRUSHES (INCLUDING BRUSHES CONSTITUTING PARTS OF MACHINES, APPLIANCES OR\nVEHICLES), HAND-OPERATED MECHANICAL FLOOR SWEEPERS, NOT MOTORISED, MOPS AND\nFEATHER DUSTERS; PREPARED KNOTS AND TUFTS FOR BROOM OR BRUSH MAKING; PAINT PADS\nAND ROLLERS; SQUEEGEES (OTHER THAN ROLLER SQUEEGEES) Other",
+        "hsn_code": "96039000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BROOMS, BRUSHES (INCLUDING BRUSHES CONSTITUTING PARTS OF MACHINES, APPLIANCES OR\nVEHICLES), HAND-OPERATED MECHANICAL FLOOR SWEEPERS, NOT MOTORISED, MOPS AND\nFEATHER DUSTERS; PREPARED KNOTS AND TUFTS FOR BROOM OR BRUSH MAKING; PAINT PADS\nAND ROLLERS; SQUEEGEES (OTHER THAN ROLLER SQUEEGEES) Other brushes constituting parts\nof machines, appliances or vehicles",
+        "hsn_code": "96035000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BROOMS, BRUSHES (INCLUDING BRUSHES CONSTITUTING PARTS OF MACHINES, APPLIANCES OR\nVEHICLES), HAND-OPERATED MECHANICAL FLOOR SWEEPERS, NOT MOTORISED, MOPS AND\nFEATHER DUSTERS; PREPARED KNOTS AND TUFTS FOR BROOM OR BRUSH MAKING; PAINT PADS\nAND ROLLERS; SQUEEGEES (OTHER THAN ROLLER SQUEEGEES) - Paint, distemper, varnish or\nsimilar brushes (other than brushes of sub-heading 9603 30); paint pads and rollers:Paint pads\nand rollers",
+        "hsn_code": "96034020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BROOMS, BRUSHES (INCLUDING BRUSHES CONSTITUTING PARTS OF MACHINES, APPLIANCES OR\nVEHICLES), HAND-OPERATED MECHANICAL FLOOR SWEEPERS, NOT MOTORISED, MOPS AND\nFEATHER DUSTERS; PREPARED KNOTS AND TUFTS FOR BROOM OR BRUSH MAKING; PAINT PADS\nAND ROLLERS; SQUEEGEES (OTHER THAN ROLLER SQUEEGEES) - Paint, distemper, varnish or\nsimilar brushes (other than brushes of sub-heading 9603 30); paint pads and rollers:Paint,\ndistemper, varnish or similar brushes (other than brushes of sub-heading 9603 30);",
+        "hsn_code": "96034010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BROOMS, BRUSHES (INCLUDING BRUSHES CONSTITUTING PARTS OF MACHINES, APPLIANCES OR\nVEHICLES), HAND-OPERATED MECHANICAL FLOOR SWEEPERS, NOT MOTORISED, MOPS AND\nFEATHER DUSTERS; PREPARED KNOTS AND TUFTS FOR BROOM OR BRUSH MAKING; PAINT PADS\nAND ROLLERS; SQUEEGEES (OTHER THAN ROLLER SQUEEGEES) - Artists' brushes, writing brushes\nand similar brushes for the application of cosmetics: Other",
+        "hsn_code": "96033090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BROOMS, BRUSHES (INCLUDING BRUSHES CONSTITUTING PARTS OF MACHINES, APPLIANCES OR\nVEHICLES), HAND-OPERATED MECHANICAL FLOOR SWEEPERS, NOT MOTORISED, MOPS AND\nFEATHER DUSTERS; PREPARED KNOTS AND TUFTS FOR BROOM OR BRUSH MAKING; PAINT PADS\nAND ROLLERS; SQUEEGEES (OTHER THAN ROLLER SQUEEGEES) - Artists' brushes, writing brushes\nand similar brushes for the application of cosmetics: Brushes for the application of cosmetics",
+        "hsn_code": "96033020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BROOMS, BRUSHES (INCLUDING BRUSHES CONSTITUTING PARTS OF MACHINES, APPLIANCES OR\nVEHICLES), HAND-OPERATED MECHANICAL FLOOR SWEEPERS, NOT MOTORISED, MOPS AND\nFEATHER DUSTERS; PREPARED KNOTS AND TUFTS FOR BROOM OR BRUSH MAKING; PAINT PADS\nAND ROLLERS; SQUEEGEES (OTHER THAN ROLLER SQUEEGEES) - Artists' brushes, writing brushes\nand similar brushes for the application of cosmetics: Artist brushes",
+        "hsn_code": "96033010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BROOMS, BRUSHES (INCLUDING BRUSHES CONSTITUTING PARTS OF MACHINES, APPLIANCES OR\nVEHICLES), HAND-OPERATED MECHANICAL FLOOR SWEEPERS, NOT MOTORISED, MOPS AND\nFEATHER DUSTERS; PREPARED KNOTS AND TUFTS FOR BROOM OR BRUSH MAKING; PAINT PADS\nAND ROLLERS; SQUEEGEES (OTHER THAN ROLLER SQUEEGEES) - Tooth brushes, shaving brushes,\nhair brushes, nail brushes, eyelash brushes and other toilet brushes for use on the person,\nincluding such brushes constituting parts of appliances: Other",
+        "hsn_code": "96032900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BROOMS, BRUSHES (INCLUDING BRUSHES CONSTITUTING PARTS OF MACHINES, APPLIANCES OR\nVEHICLES), HAND-OPERATED MECHANICAL FLOOR SWEEPERS, NOT MOTORISED, MOPS AND\nFEATHER DUSTERS; PREPARED KNOTS AND TUFTS FOR BROOM OR BRUSH MAKING; PAINT PADS\nAND ROLLERS; SQUEEGEES (OTHER THAN ROLLER SQUEEGEES) - Tooth brushes, shaving brushes,\nhair brushes, nail brushes, eyelash brushes and other toilet brushes for use on the person,\nincluding such brushes constituting parts of appliances: Tooth brushes including dental-plate\nbrushes",
+        "hsn_code": "96032100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BROOMS, BRUSHES (INCLUDING BRUSHES CONSTITUTING PARTS OF MACHINES, APPLIANCES OR\nVEHICLES), HAND-OPERATED MECHANICAL FLOOR SWEEPERS, NOT MOTORISED, MOPS AND\nFEATHER DUSTERS; PREPARED KNOTS AND TUFTS FOR BROOM OR BRUSH MAKING; PAINT PADS\nAND ROLLERS; SQUEEGEES (OTHER THAN ROLLER SQUEEGEES) Brooms and brushes, consisting\nof twigs or other vegetable materials, bound together, with or without handles",
+        "hsn_code": "96031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUTTONS, PRESS-FASTENERS, SNAP-FASTENERS AND PRESSSTUDS, BUTTON MOULDS AND\nOTHER PARTS OF THESE ARTICLES; BUTTON BLANKS - Button moulds and other parts of buttons;\nbutton blanks : Other",
+        "hsn_code": "96063090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUTTONS, PRESS-FASTENERS, SNAP-FASTENERS AND PRESSSTUDS, BUTTON MOULDS AND\nOTHER PARTS OF THESE ARTICLES; BUTTON BLANKS - Button moulds and other parts of buttons;\nbutton blanks : Button blanks",
+        "hsn_code": "96063010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUTTONS, PRESS-FASTENERS, SNAP-FASTENERS AND PRESSSTUDS, BUTTON MOULDS AND\nOTHER PARTS OF THESE ARTICLES; BUTTON BLANKS - Buttons : - Other : Other",
+        "hsn_code": "96062990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUTTONS, PRESS-FASTENERS, SNAP-FASTENERS AND PRESSSTUDS, BUTTON MOULDS AND\nOTHER PARTS OF THESE ARTICLES; BUTTON BLANKS - Buttons : - Other : Button of coconut shell\nor wood",
+        "hsn_code": "96062910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUTTONS, PRESS-FASTENERS, SNAP-FASTENERS AND PRESSSTUDS, BUTTON MOULDS AND\nOTHER PARTS OF THESE ARTICLES; BUTTON BLANKS - Buttons : Of base metals, not covered with\ntextile material",
+        "hsn_code": "96062200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUTTONS, PRESS-FASTENERS, SNAP-FASTENERS AND PRESSSTUDS, BUTTON MOULDS AND\nOTHER PARTS OF THESE ARTICLES; BUTTON BLANKS - Buttons : Of plastics, not covered with\ntextile material",
+        "hsn_code": "96062100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEATS (OTHER THAN THOSE OF HEADING 9402), WHETHER OR NOT CONVERTIBLE INTO BEDS,\nAND PARTS THEREOF Seats of a kind used for aircraft",
+        "hsn_code": "94011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SEATS (OTHER THAN THOSE OF HEADING 9402), WHETHER OR NOT CONVERTIBLE INTO BEDS, AND PARTS THEREOF",
+        "hsn_code": "9401",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEATS (OTHER THAN THOSE OF HEADING 9402), WHETHER OR NOT CONVERTIBLE INTO BEDS,\nAND PARTS THEREOF Parts",
+        "hsn_code": "94019000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEATS (OTHER THAN THOSE OF HEADING 9402), WHETHER OR NOT CONVERTIBLE INTO BEDS,\nAND PARTS THEREOF Other seats",
+        "hsn_code": "94018000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEATS (OTHER THAN THOSE OF HEADING 9402), WHETHER OR NOT CONVERTIBLE INTO BEDS,\nAND PARTS THEREOF - Other seats, with metal frames: Other",
+        "hsn_code": "94017900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEATS (OTHER THAN THOSE OF HEADING 9402), WHETHER OR NOT CONVERTIBLE INTO BEDS,\nAND PARTS THEREOF - Other seats, with metal frames: Upholstered",
+        "hsn_code": "94017100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEATS (OTHER THAN THOSE OF HEADING 9402), WHETHER OR NOT CONVERTIBLE INTO BEDS,\nAND PARTS THEREOF - Other seats, with wooden frames: Other",
+        "hsn_code": "94016900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEATS (OTHER THAN THOSE OF HEADING 9402), WHETHER OR NOT CONVERTIBLE INTO BEDS,\nAND PARTS THEREOF - Other seats, with wooden frames: Upholstered",
+        "hsn_code": "94016100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEATS (OTHER THAN THOSE OF HEADING 9402), WHETHER OR NOT CONVERTIBLE INTO BEDS,\nAND PARTS THEREOF - SEATS OF CANE, OSIER, BAMBOO OR SIMILAR MATERIALS: -- OTHER",
+        "hsn_code": "94015900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEATS (OTHER THAN THOSE OF HEADING 9402), WHETHER OR NOT CONVERTIBLE INTO BEDS,\nAND PARTS THEREOF -SEATS OF CANE, OSIER, BAMBOO OR SIMILAR MATERIALS: -- OF BAMBOO\nOR RATTAN",
+        "hsn_code": "94015100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEATS (OTHER THAN THOSE OF HEADING 9402), WHETHER OR NOT CONVERTIBLE INTO BEDS,\nAND PARTS THEREOF Seats other than garden seats or camping equipment, convertible into\nbeds",
+        "hsn_code": "94014000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEATS (OTHER THAN THOSE OF HEADING 9402), WHETHER OR NOT CONVERTIBLE INTO BEDS,\nAND PARTS THEREOF Swivel seats and variable height adjustment",
+        "hsn_code": "94013000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEATS (OTHER THAN THOSE OF HEADING 9402), WHETHER OR NOT CONVERTIBLE INTO BEDS,\nAND PARTS THEREOF Seats of a kind used for motor vehicles",
+        "hsn_code": "94012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEATS (OTHER THAN THOSE OF HEADING 9402), WHETHER OR NOT CONVERTIBLE INTO BEDS,\nAND PARTS THEREOF SEATS OF CANE, OSIER, BAMBOO OR SIMILAR MATERIALS",
+        "hsn_code": "94015000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PERCUSSION MUSICAL INSTRUMENTS (FOR EXAMPLE, DRUMS, XYLOPHONES, CYMBOLS,\nCASTANETS, MARACAS)",
+        "hsn_code": "92060000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VESSELS AND OTHER FLOATING STRUCTURES FORBREAKING UP",
+        "hsn_code": "89080000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE AND SCRAP OF PRIMARY CELLS, PRIMARY BATTERIES AND ELECTRIC ACCUMULATORS; SPENT PRIMARY CELLS, SPENT PRIMARY BATTERIES AND SPENT ELECTRIC ACCUMULATORS; ELECTRICAL PARTS OF MACHINERY OR APPARATUS, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER",
+        "hsn_code": "8548",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE AND SCRAP OF PRIMARY CELLS, PRIMARY BATTERIES AND ELECTRIC ACCUMULATORS;\nSPENT PRIMARY CELLS, SPENT PRIMARY BATTERIES AND SPENT ELECTRIC CCUMULATORS;\nELECTRICAL PARTS OF MACHINERY OR APPARATUS, NOT SPECIFIED OR INCLUDED ELSEWHERE IN\nTHIS CHAPTER - OTHER",
+        "hsn_code": "85489000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE AND SCRAP OF PRIMARY CELLS, PRIMARY BATTERIES AND ELECTRIC ACCUMULATORS;\nSPENT PRIMARY CELLS, SPENT PRIMARY BATTERIES AND SPENT ELECTRIC CCUMULATORS;\nELECTRICAL PARTS OF MACHINERY OR APPARATUS, NOT SPECIFIED OR INCLUDED ELSEWHERE IN\nTHIS CHAPTER - WASTE AND SCRAP OF PRIMARY CELLS, PRIMARY BATTERIES AND ELECTRIC\nACCUMULATORS; SPENT PRIMARY CELLS, SPENT PRIMARY BATTERIES AND SPENT ELECTRIC\nACCUMULATORS: OTHER WASTE AND SCRAP",
+        "hsn_code": "85481090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE AND SCRAP OF PRIMARY CELLS, PRIMARY BATTERIES AND ELECTRIC ACCUMULATORS;\nSPENT PRIMARY CELLS, SPENT PRIMARY BATTERIES AND SPENT ELECTRIC CCUMULATORS;\nELECTRICAL PARTS OF MACHINERY OR APPARATUS, NOT SPECIFIED OR INCLUDED ELSEWHERE IN\nTHIS CHAPTER - WASTE AND SCRAP OF PRIMARY CELLS, PRIMARY BATTERIES AND ELECTRIC\nACCUMULATORS; SPENT PRIMARY CELLS, SPENT PRIMARY BATTERIES AND SPENT ELECTRIC\nACCUMULATORS: BATTERY WASTE, NAMELY THE FOLLOWING: SCRAP DRAINED OR DRY WHILE\nINTACT, LEAD BATTERIES COVERED BY ISRI CODE WORD RAINS; SCRAP WET WHOLE INTACT LEAD\nBATTERIES COVERED BY ISRI CODE WORD RINK; SCRAP INDUSTRIAL INTACT LEAD CELLS\nCOVERED BY ISRI CODE WORD RONO; SCRAP WHOLE INTACT INDUSTRIAL LEAD BATTERIES\nCOVERED BY ISRI CODE WORD ROPER; EDISON BATTERIES COVERED BY ISRI CODE WORD VAUNT",
+        "hsn_code": "85481020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE AND SCRAP OF PRIMARY CELLS, PRIMARY BATTERIES AND ELECTRIC ACCUMULATORS;\nSPENT PRIMARY CELLS, SPENT PRIMARY BATTERIES AND SPENT ELECTRIC CCUMULATORS;\nELECTRICAL PARTS OF MACHINERY OR APPARATUS, NOT SPECIFIED OR INCLUDED ELSEWHERE IN\nTHIS CHAPTER - WASTE AND SCRAP OF PRIMARY CELLS, PRIMARY BATTERIES AND ELECTRIC\nACCUMULATORS; SPENT PRIMARY CELLS, SPENT PRIMARY BATTERIES AND SPENT ELECTRIC\nACCUMULATORS: BATTERY SCRAP, NAMELY THE FOLLOWING: LEAD BATTERY PLATES COVERED\nBY ISRI CODE WORD RAILS; BATTERY LUGS COVERED BY ISRI CODE WORD RAKES.",
+        "hsn_code": "85481010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTED CIRCUITS",
+        "hsn_code": "85340000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Transmission apparatus for radio-broadcasting or television, whether or not incorporating reception apparatus or sound recording or reproducing apparatus; television cameras, digital cameras and video camera recorders",
+        "hsn_code": "8525",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO BROADCASTING OR TELEVISION, WHETHER OR NOT\nINCORPORATING RECEPTION APPARATUS OR SOUND RECORDING OR REPRODUCING\nAPPARATUS; TELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS -\nTELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS --- OTHER",
+        "hsn_code": "85258090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO BROADCASTING OR TELEVISION, WHETHER OR NOT\nINCORPORATING RECEPTION APPARATUS OR SOUND RECORDING OR REPRODUCING\nAPPARATUS; TELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS -\nTELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS --- VIDEO CAMERA\nRECORDERS",
+        "hsn_code": "85258030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO BROADCASTING OR TELEVISION, WHETHER OR NOT\nINCORPORATING RECEPTION APPARATUS OR SOUND RECORDING OR REPRODUCING\nAPPARATUS; TELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS -\nTELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS --- DIGITAL\nCAMERAS",
+        "hsn_code": "85258020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO BROADCASTING OR TELEVISION, WHETHER OR NOT\nINCORPORATING RECEPTION APPARATUS OR SOUND RECORDING OR REPRODUCING\nAPPARATUS; TELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS -\nTELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS --- TELEVISION\nCAMERAS",
+        "hsn_code": "85258010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO BROADCASTING OR TELEVISION, WHETHER OR NOT\nINCORPORATING RECEPTION APPARATUS OR SOUND RECORDING OR REPRODUCING\nAPPARATUS; TELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS -\nTRANSMISSION APPARATUS --- OTHER",
+        "hsn_code": "85255090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO BROADCASTING OR TELEVISION, WHETHER OR NOT\nINCORPORATING RECEPTION APPARATUS OR SOUND RECORDING OR REPRODUCING\nAPPARATUS; TELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS -\nTRANSMISSION APPARATUS --- COMMUNICATION JAMMING EQUIPMENT",
+        "hsn_code": "85255040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO BROADCASTING OR TELEVISION, WHETHER OR NOT\nINCORPORATING RECEPTION APPARATUS OR SOUND RECORDING OR REPRODUCING\nAPPARATUS; TELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS -\nTRANSMISSION APPARATUS --- BROADCAST EQUIPMENT SUB-SYSTEM",
+        "hsn_code": "85255030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO BROADCASTING OR TELEVISION, WHETHER OR NOT\nINCORPORATING RECEPTION APPARATUS OR SOUND RECORDING OR REPRODUCING\nAPPARATUS; TELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS -\nTRANSMISSION APPARATUS --- TV BROADCAST TRANSMITTER",
+        "hsn_code": "85255020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO BROADCASTING OR TELEVISION, WHETHER OR NOT\nINCORPORATING RECEPTION APPARATUS OR SOUND RECORDING OR REPRODUCING\nAPPARATUS; TELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS -\nTRANSMISSION APPARATUS --- RADIO BROADCAST TRANSMITTER",
+        "hsn_code": "85255010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY, RADIO\nBROADCASTING OR TELEVISION, WHETHER OR NOT INCORPORATING RECEPTION APPARATUS\nOR SOUND RECORDING OR REPRODUCING APPARATUS; TELEVISION CAMERAS; STILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERASSTILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERAS",
+        "hsn_code": "85254000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY, RADIO\nBROADCASTING OR TELEVISION, WHETHER OR NOT INCORPORATING RECEPTION APPARATUS\nOR SOUND RECORDING OR REPRODUCING APPARATUS; TELEVISION CAMERAS; STILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERASTELEVISION\nCAMERAS",
+        "hsn_code": "85253000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY, RADIO\nBROADCASTING OR TELEVISION, WHETHER OR NOT INCORPORATING RECEPTION APPARATUS\nOR SOUND RECORDING OR REPRODUCING APPARATUS; TELEVISION CAMERAS; STILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERAS - TRANSMISSION\nAPPARATUS INCORPORATING RECEPTION APPARATUS: - OTHER: OTHER",
+        "hsn_code": "85252099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY, RADIO\nBROADCASTING OR TELEVISION, WHETHER OR NOT INCORPORATING RECEPTION APPARATUS\nOR SOUND RECORDING OR REPRODUCING APPARATUS; TELEVISION CAMERAS; STILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERAS - TRANSMISSION\nAPPARATUS INCORPORATING RECEPTION APPARATUS: - OTHER: OTHER SATELLITE\nCOMMUNICATION EQUIPMENT",
+        "hsn_code": "85252092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY, RADIO\nBROADCASTING OR TELEVISION, WHETHER OR NOT INCORPORATING RECEPTION APPARATUS\nOR SOUND RECORDING OR REPRODUCING APPARATUS; TELEVISION CAMERAS; STILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERAS - TRANSMISSION\nAPPARATUS INCORPORATING RECEPTION APPARATUS: - OTHER: VSAT TERMINALS",
+        "hsn_code": "85252091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY, RADIO\nBROADCASTING OR TELEVISION, WHETHER OR NOT INCORPORATING RECEPTION APPARATUS\nOR SOUND RECORDING OR REPRODUCING APPARATUS; TELEVISION CAMERAS; STILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERAS - TRANSMISSION\nAPPARATUS INCORPORATING RECEPTION APPARATUS: - TWO WAY RADIO COMMUNICATION\nEQUIPMENT: OTHER",
+        "hsn_code": "85252019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY, RADIO\nBROADCASTING OR TELEVISION, WHETHER OR NOT INCORPORATING RECEPTION APPARATUS\nOR SOUND RECORDING OR REPRODUCING APPARATUS; TELEVISION CAMERAS; STILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERAS - TRANSMISSION\nAPPARATUS INCORPORATING RECEPTION APPARATUS: - TWO WAY RADIO COMMUNICATION\nEQUIPMENT: CELLULAR TELEPHONE",
+        "hsn_code": "85252017",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY, RADIO\nBROADCASTING OR TELEVISION, WHETHER OR NOT INCORPORATING RECEPTION APPARATUS\nOR SOUND RECORDING OR REPRODUCING APPARATUS; TELEVISION CAMERAS; STILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERAS - TRANSMISSION\nAPPARATUS INCORPORATING RECEPTION APPARATUS: - TWO WAY RADIO COMMUNICATION\nEQUIPMENT: AMATEUR RADIO EQUIPMENT",
+        "hsn_code": "85252016",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY, RADIO\nBROADCASTING OR TELEVISION, WHETHER OR NOT INCORPORATING RECEPTION APPARATUS\nOR SOUND RECORDING OR REPRODUCING APPARATUS; TELEVISION CAMERAS; STILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERAS - TRANSMISSION\nAPPARATUS INCORPORATING RECEPTION APPARATUS: - TWO WAY RADIO COMMUNICATION\nEQUIPMENT: MARINE RADIO COMMUNICATION EQUIPMENT",
+        "hsn_code": "85252015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY, RADIO\nBROADCASTING OR TELEVISION, WHETHER OR NOT INCORPORATING RECEPTION APPARATUS\nOR SOUND RECORDING OR REPRODUCING APPARATUS; TELEVISION CAMERAS; STILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERAS - TRANSMISSION\nAPPARATUS INCORPORATING RECEPTION APPARATUS: - TWO WAY RADIO COMMUNICATION\nEQUIPMENT: TRANSPORTABLE TELEPHONE",
+        "hsn_code": "85252014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY, RADIO\nBROADCASTING OR TELEVISION, WHETHER OR NOT INCORPORATING RECEPTION APPARATUS\nOR SOUND RECORDING OR REPRODUCING APPARATUS; TELEVISION CAMERAS; STILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERAS - TRANSMISSION\nAPPARATUS INCORPORATING RECEPTION APPARATUS: - TWO WAY RADIO COMMUNICATION\nEQUIPMENT: CAR TELEPHONE",
+        "hsn_code": "85252013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY, RADIO\nBROADCASTING OR TELEVISION, WHETHER OR NOT INCORPORATING RECEPTION APPARATUS\nOR SOUND RECORDING OR REPRODUCING APPARATUS; TELEVISION CAMERAS; STILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERAS - TRANSMISSION\nAPPARATUS INCORPORATING RECEPTION APPARATUS: - TWO WAY RADIO COMMUNICATION\nEQUIPMENT: CORDLESS HANDSET",
+        "hsn_code": "85252012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY, RADIO\nBROADCASTING OR TELEVISION, WHETHER OR NOT INCORPORATING RECEPTION APPARATUS\nOR SOUND RECORDING OR REPRODUCING APPARATUS; TELEVISION CAMERAS; STILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERAS - TRANSMISSION\nAPPARATUS INCORPORATING RECEPTION APPARATUS: - TWO WAY RADIO COMMUNICATION\nEQUIPMENT: WALKIE TALKIE SET",
+        "hsn_code": "85252011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY, RADIO\nBROADCASTING OR TELEVISION, WHETHER OR NOT INCORPORATING RECEPTION APPARATUS\nOR SOUND RECORDING OR REPRODUCING APPARATUS; TELEVISION CAMERAS; STILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERAS - TRANSMISSION\nAPPARATUS: OTHER",
+        "hsn_code": "85251090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY, RADIO\nBROADCASTING OR TELEVISION, WHETHER OR NOT INCORPORATING RECEPTION APPARATUS\nOR SOUND RECORDING OR REPRODUCING APPARATUS; TELEVISION CAMERAS; STILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERAS - TRANSMISSION\nAPPARATUS: WIRELESS MICROPHONE",
+        "hsn_code": "85251050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY, RADIO\nBROADCASTING OR TELEVISION, WHETHER OR NOT INCORPORATING RECEPTION APPARATUS\nOR SOUND RECORDING OR REPRODUCING APPARATUS; TELEVISION CAMERAS; STILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERAS - TRANSMISSION\nAPPARATUS: COMMUNICATION JAMMING EQUIPMENT",
+        "hsn_code": "85251040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY, RADIO\nBROADCASTING OR TELEVISION, WHETHER OR NOT INCORPORATING RECEPTION APPARATUS\nOR SOUND RECORDING OR REPRODUCING APPARATUS; TELEVISION CAMERAS; STILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERAS - TRANSMISSION\nAPPARATUS: BROADCAST EQUIPMENT SUB-SYSTEM",
+        "hsn_code": "85251030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY, RADIO\nBROADCASTING OR TELEVISION, WHETHER OR NOT INCORPORATING RECEPTION APPARATUS\nOR SOUND RECORDING OR REPRODUCING APPARATUS; TELEVISION CAMERAS; STILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERAS - TRANSMISSION\nAPPARATUS: TV BROADCAST TRANSMITTER",
+        "hsn_code": "85251020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY, RADIO\nBROADCASTING OR TELEVISION, WHETHER OR NOT INCORPORATING RECEPTION APPARATUS\nOR SOUND RECORDING OR REPRODUCING APPARATUS; TELEVISION CAMERAS; STILL IMAGE\nVIDEO CAMERAS AND OTHER VIDEO CAMERA RECORDERS; DIGITAL CAMERAS - TRANSMISSION\nAPPARATUS: RADIO BROADCAST TRANSMITTER",
+        "hsn_code": "85251010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO BROADCASTING OR TELEVISION, WHETHER OR NOT\nINCORPORATING RECEPTION APPARATUS OR SOUND RECORDING OR REPRODUCING\nAPPARATUS; TELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS -\nTRANSMISSION APPARATUS INCORPORATING RECEPTION APPARATUS --- OTHER ---- OTHER",
+        "hsn_code": "85256099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO BROADCASTING OR TELEVISION, WHETHER OR NOT\nINCORPORATING RECEPTION APPARATUS OR SOUND RECORDING OR REPRODUCING\nAPPARATUS; TELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS -\nTRANSMISSION APPARATUS INCORPORATING RECEPTION APPARATUS --- OTHER ---- OTHER\nSATELLITE COMMUNICATION EQUIPMENT",
+        "hsn_code": "85256092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO BROADCASTING OR TELEVISION, WHETHER OR NOT\nINCORPORATING RECEPTION APPARATUS OR SOUND RECORDING OR REPRODUCING\nAPPARATUS; TELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS -\nTRANSMISSION APPARATUS INCORPORATING RECEPTION APPARATUS --- OTHER ---- VSAT\nTERMINALS",
+        "hsn_code": "85256091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO BROADCASTING OR TELEVISION, WHETHER OR NOT\nINCORPORATING RECEPTION APPARATUS OR SOUND RECORDING OR REPRODUCING\nAPPARATUS; TELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS -\nTRANSMISSION APPARATUS INCORPORATING RECEPTION APPARATUS --- TWO WAY RADIO\nCOMMUNICATION EQUIPMENT ---- OTHER",
+        "hsn_code": "85256019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO BROADCASTING OR TELEVISION, WHETHER OR NOT\nINCORPORATING RECEPTION APPARATUS OR SOUND RECORDING OR REPRODUCING\nAPPARATUS; TELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS -\nTRANSMISSION APPARATUS INCORPORATING RECEPTION APPARATUS --- TWO WAY RADIO\nCOMMUNICATION EQUIPMENT ---- AMATEUR RADIO EQUIPMENT",
+        "hsn_code": "85256013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO BROADCASTING OR TELEVISION, WHETHER OR NOT\nINCORPORATING RECEPTION APPARATUS OR SOUND RECORDING OR REPRODUCING\nAPPARATUS; TELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS -\nTRANSMISSION APPARATUS INCORPORATING RECEPTION APPARATUS --- TWO WAY RADIO\nCOMMUNICATION EQUIPMENT ---- MARINE RADIO COMMUNICATION EQUIPMENT",
+        "hsn_code": "85256012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION APPARATUS FOR RADIO BROADCASTING OR TELEVISION, WHETHER OR NOT\nINCORPORATING RECEPTION APPARATUS OR SOUND RECORDING OR REPRODUCING\nAPPARATUS; TELEVISION CAMERAS, DIGITAL CAMERAS AND VIDEO CAMERA RECORDERS -\nTRANSMISSION APPARATUS INCORPORATING RECEPTION APPARATUS --- TWO WAY RADIO\nCOMMUNICATION EQUIPMENT ---- WALKIE TALKIE SET",
+        "hsn_code": "85256011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LITHIUM-ION",
+        "hsn_code": "85076000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISH WASHING MACHINES; MACHINERY FOR CLEANING OR DRYING BOTTLES OR OTHER\nCONTAINERS; MACHINERY FOR FILLING, CLOSING, SEALING OR LABELLING BOTTLES, CANS,\nBOXES, BAGS OR OTHER CONTAINERS; MACHINERY FOR CAPSULING BOTTLES, JARS, TUBES AND\nSIMILAR CONTAINERS; OTHER PACKING OR WRAPPING MACHINERY (INCLUDING HEAT-SHRINK\nWRAPPING MACHINERY); MACHINERY FOR AERATING BEVERAGES - PARTS: OF MACHINERY FOR\nCLEANING OR DRYING BOTTLES OR OTHER CONTAINERS",
+        "hsn_code": "84229010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISH WASHING MACHINES; MACHINERY FOR CLEANING OR DRYING BOTTLES OR OTHER\nCONTAINERS; MACHINERY FOR FILLING, CLOSING, SEALING OR LABELLING BOTTLES, CANS,\nBOXES, BAGS OR OTHER CONTAINERS; MACHINERY FOR CAPSULING BOTTLES, JARS, TUBES AND\nSIMILAR CONTAINERS; OTHER PACKING OR WRAPPING MACHINERY (INCLUDING HEAT-SHRINK\nWRAPPING MACHINERY); MACHINERY FOR AERATING BEVERAGES - PARTS: OF DISH WASHING\nMACHINES OF HOUSEHOLD TYPE",
+        "hsn_code": "84229020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISH WASHING MACHINES; MACHINERY FOR CLEANING OR DRYING BOTTLES OR OTHER\nCONTAINERS; MACHINERY FOR FILLING, CLOSING, SEALING OR LABELLING BOTTLES, CANS,\nBOXES, BAGS OR OTHER CONTAINERS; MACHINERY FOR CAPSULING BOTTLES, JARS, TUBES AND\nSIMILAR CONTAINERS; OTHER PACKING OR WRAPPING MACHINERY (INCLUDING HEAT-SHRINK\nWRAPPING MACHINERY); MACHINERY FOR AERATING BEVERAGES - PARTS: OF OTHER\nMACHINERY",
+        "hsn_code": "84229090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISH WASHING MACHINES; MACHINERY FOR CLEANING OR DRYING BOTTLES OR OTHER\nCONTAINERS; MACHINERY FOR FILLING, CLOSING, SEALING OR LABELLING BOTTLES, CANS,\nBOXES, BAGS OR OTHER CONTAINERS; MACHINERY FOR CAPSULING BOTTLES, JARS, TUBES AND\nSIMILAR CONTAINERS; OTHER PACKING OR WRAPPING MACHINERY (INCLUDING HEAT-SHRINK\nWRAPPING MACHINERY); MACHINERY FOR AERATING BEVERAGES OTHER PACKING OR\nWRAPPING MACHINERY (INCLUDING HEAT SHRINK WRAPPING MACHINERY)",
+        "hsn_code": "84224000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISH WASHING MACHINES; MACHINERY FOR CLEANING OR DRYING BOTTLES OR OTHER\nCONTAINERS; MACHINERY FOR FILLING, CLOSING, SEALING OR LABELLING BOTTLES, CANS,\nBOXES, BAGS OR OTHER CONTAINERS; MACHINERY FOR CAPSULING BOTTLES, JARS, TUBES AND\nSIMILAR CONTAINERS; OTHER PACKING OR WRAPPING MACHINERY (INCLUDING HEAT-SHRINK\nWRAPPING MACHINERY); MACHINERY FOR AERATING BEVERAGES MACHINERY FOR FILLING,\nCLOSING, SEALING OR LABELLING BOTTLES, CANS, BOXES, BAGS OR OTHER CONTAINERS;\nMACHINERY FOR CAPSULING BOTTLES, JARS, TUBES AND SIMILAR CONTAINERS; MACHINERY\nFOR AERATING BEVERAGES",
+        "hsn_code": "84223000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISH WASHING MACHINES; MACHINERY FOR CLEANING OR DRYING BOTTLES OR OTHER\nCONTAINERS; MACHINERY FOR FILLING, CLOSING, SEALING OR LABELLING BOTTLES, CANS,\nBOXES, BAGS OR OTHER CONTAINERS; MACHINERY FOR CAPSULING BOTTLES, JARS, TUBES AND\nSIMILAR CONTAINERS; OTHER PACKING OR WRAPPING MACHINERY (INCLUDING HEAT-SHRINK\nWRAPPING MACHINERY); MACHINERY FOR AERATING BEVERAGES MACHINERY FOR CLEANING\nOR DRYING BOTTLES OR OTHER CONTAINERS",
+        "hsn_code": "84222000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - INSTANTANEOUS OR STORAGE WATER HEATERS\nNON-ELECTRIC: 8419 11 - INSTANTANEOUS GAS WATER HEATERS : DOMESTIC TYPE",
+        "hsn_code": "84191110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - INSTANTANEOUS OR STORAGE WATER HEATERS\nNON-ELECTRIC: 8419 11 - INSTANTANEOUS GAS WATER HEATERS : OTHER",
+        "hsn_code": "84191190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - INSTANTANEOUS GAS WATER HEATERS : OTHER:\nDOMESTIC TYPE",
+        "hsn_code": "84191910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - INSTANTANEOUS GAS WATER HEATERS : OTHER:\nOTHER",
+        "hsn_code": "84191920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC- MEDICAL, SURGICAL OR LABORATORY\nSTERILISERS:AUTO CLAVES",
+        "hsn_code": "84192010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC- MEDICAL, SURGICAL OR LABORATORY STERILISERS:\nOTHER",
+        "hsn_code": "84192090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - DRYERS: FOR AGRICULTURAL PRODUCTS",
+        "hsn_code": "84193100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - DRYERS: FOR WOOD, PAPER PULP, PAPER OR\nPAPER, BOARD",
+        "hsn_code": "84193200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - DRYERS: OTHER",
+        "hsn_code": "84193900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - DISTILLING OR RECTIFYING PLANT: FOR PETROLEUM\nREFINING",
+        "hsn_code": "84194010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - DISTILLING OR RECTIFYING PLANT: OTHER\nDISTILLING EQUIPMENT",
+        "hsn_code": "84194020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - DISTILLING OR RECTIFYING PLANT: OTHER",
+        "hsn_code": "84194090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - HEAT EXCHANGE UNITS: SHELL AND TUBE TYPE",
+        "hsn_code": "84195010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - HEAT EXCHANGE UNITS: PLATE TYPE",
+        "hsn_code": "84195020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - HEAT EXCHANGE UNITS: SPIRAL TYPE",
+        "hsn_code": "84195030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - HEAT EXCHANGE UNITS: OTHER",
+        "hsn_code": "84195090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC MACHINERY FOR LIQUEFYING AIR OR OTHER GASES",
+        "hsn_code": "84196000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - OTHER MACHINERY, PLANT AND EQUIPMENT: FOR\nMAKING HOT DRINKS OR FOR COOKING OR HEATING FOOD: FRIERS",
+        "hsn_code": "84198110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - OTHER MACHINERY, PLANT AND EQUIPMENT: FOR\nMAKING HOT DRINKS OR FOR COOKING OR HEATING FOOD: OTHER KITCHEN MACHINES",
+        "hsn_code": "84198120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - OTHER MACHINERY, PLANT AND EQUIPMENT: FOR\nMAKING HOT DRINKS OR FOR COOKING OR HEATING FOOD: OTHER",
+        "hsn_code": "84198190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - OTHER MACHINERY, PLANT AND EQUIPMENT -\nOTHER: PRESSURE VESSELS, REACTORS, COLUMNS OR TOWERS OR CHEMICAL STORAGE TANKS",
+        "hsn_code": "84198910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - OTHER MACHINERY, PLANT AND EQUIPMENT -\nOTHER: GLASS LINED EQUIPMENT",
+        "hsn_code": "84198920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - OTHER MACHINERY, PLANT AND EQUIPMENT -\nOTHER: AUTO CLAVES OTHER THAN FOR COOKING OR HEATING FOOD, NOT ELSEWHERE\nSPECIFIED OR INCLUDED",
+        "hsn_code": "84198930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - OTHER MACHINERY, PLANT AND EQUIPMENT -\nOTHER: COOLING TOWERS AND SIMILAR PLANTS FOR DIRECT COOLING (WITHOUT A\nSEPARATING WALL) BY MEANS OF RE-CIRCULATED WATER",
+        "hsn_code": "84198940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - OTHER MACHINERY, PLANT AND EQUIPMENT -\nOTHER: PASTEURIZERS",
+        "hsn_code": "84198950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - OTHER MACHINERY, PLANT AND EQUIPMENT -\nOTHER: PLANT GROWTH CHAMBERS AND ROOMS AND TISSUE CULTURE CHAMBERS AND\nROOMS HAVING TEMPERATURE, HUMIDITY OR LIGHT CONTROL",
+        "hsn_code": "84198960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - OTHER MACHINERY, PLANT AND EQUIPMENT -\nOTHER: APPARATUS FOR RAPID HEATING OF SEMI CONDUCTOR DEVICES; APPARATUS FOR\nCHEMICAL OR PHYSICAL VAPOUR DEPOSITION ON SEMICONDUCTOR WAFERS; APPARATUS FOR\nCHEMICAL VAPOUR DEPOSITION ON LCD SUBSTRATUS",
+        "hsn_code": "84198970",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - OTHER MACHINERY, PLANT AND EQUIPMENT -\nOTHER: VACUUM VAPOUR PLANT FOR DEPOSITION OF METALS",
+        "hsn_code": "84198980",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - OTHER MACHINERY, PLANT AND EQUIPMENT -\nOTHER: OTHER",
+        "hsn_code": "84198990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - PARTS: PARTS OF INSTANTANEOUS OR STORAGE\nWATER HEATERS (DOMESTIC TYPE)",
+        "hsn_code": "84199010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED\n(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE\nTREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS\nHEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING, PASTEURISING,\nSTEAMING, DRYING, EVAPORATING, VAPORISING, CONDENSING OR COOLING, OTHER THAN\nMACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES; INSTANTANEOUS OR\nSTORAGE WATER HEATERS, NON-ELECTRIC - PARTS: OTHER",
+        "hsn_code": "84199090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, PLANT OR LABORATORY EQUIPMENT, WHETHER OR NOT ELECTRICALLY HEATED,(EXCLUDING FURNACES, OVENS AND OTHER EQUIPMENT OF HEADING 8514), FOR THE TREATMENT OF MATERIALS BY A PROCESS INVOLVING A CHANGE OF TEMPERATURE SUCH AS HEATING, COOKING, ROASTING, DISTILLING, RECTIFYING, STERILISING PASTEURISING, STEAMING, DRYING, EVAPORATING, VAPOURISING, CONDENSING OR COOLING, OTHER THAN MACHINERY OR PLANT OF A KIND USED FOR DOMESTIC PURPOSES",
+        "hsn_code": "8419",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCISSORS, TAILORS SHEARS AND SIMILAR SHEARS, AND BLADES THEREFOR",
+        "hsn_code": "82130000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND-OPERATED MECHANICAL APPLIANCES, WEIGHING 10 KG OR LESS, USED IN THE\nPREPARATION, CONDITIONING OR SERVING OF FOOD OR DRINK",
+        "hsn_code": "82100000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM (COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE AND SCRAP",
+        "hsn_code": "8112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM\n(COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE\nAND SCRAP - OTHER: OTHER",
+        "hsn_code": "81129900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM\n(COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE\nAND SCRAP - OTHER: UNWROUGHT; WASTE AND SCRAP; POWDERS",
+        "hsn_code": "81129200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM\n(COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE\nAND SCRAP - THALLIUM: OTHER",
+        "hsn_code": "81125900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM\n(COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE\nAND SCRAP - THALLIUM: WASTE AND SCRAP",
+        "hsn_code": "81125200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM\n(COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE\nAND SCRAP - THALLIUM: UNWROUGHT; POWDERS",
+        "hsn_code": "81125100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM\n(COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE\nAND SCRAP - VANADIUM:OTHER",
+        "hsn_code": "81124090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM\n(COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE\nAND SCRAP - VANADIUM: WROUGHT",
+        "hsn_code": "81124030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM\n(COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE\nAND SCRAP - VANADIUM:WASTE AND SCRAP",
+        "hsn_code": "81124020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM\n(COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE\nAND SCRAP - VANADIUM:UNWROUGHT",
+        "hsn_code": "81124010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM\n(COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE\nAND SCRAP - GERMANIUM:OTHER",
+        "hsn_code": "81123090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM\n(COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE\nAND SCRAP - GERMANIUM:WROUGHT",
+        "hsn_code": "81123030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM\n(COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE\nAND SCRAP - GERMANIUM: WASTE AND SCRAP",
+        "hsn_code": "81123020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM\n(COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE\nAND SCRAP - GERMANIUM:UNWROUGHT",
+        "hsn_code": "81123010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM\n(COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE\nAND SCRAP - CHROMIUM: OTHER",
+        "hsn_code": "81122900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM\n(COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE\nAND SCRAP - CHROMIUM: WASTE AND SCRAP",
+        "hsn_code": "81122200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM\n(COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE\nAND SCRAP - CHROMIUM: UNWROUGHT; POWDERS",
+        "hsn_code": "81122100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM\n(COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE\nAND SCRAP - BERYLLIUM: OTHER",
+        "hsn_code": "81121900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM\n(COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE\nAND SCRAP - BERYLLIUM: WASTE AND SCRAP",
+        "hsn_code": "81121300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BERYLLIUM, CHROMIUM, GERMANIUM, VANADIUM, GALLIUM, HAFNIUM, INDIUM, NIOBIUM\n(COLUMBIUM), RHENIUM AND THALLIUM, AND ARTICLES OF THESE METALS, INCLUDING WASTE\nAND SCRAP - BERYLLIUM: UNWROUGHT; POWDERS",
+        "hsn_code": "81121200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FRUIT AND NUTS, UNCOOKED OR COOKED BYSTEAMING OR BOILING IN WATER,\nFROZEN,WHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER\nRASPBERRIES, BLACKBERRIES, MULBERRIES,LOGANBERRIES, BLACK, WHITE OR RED\nCURRANTSAND GOOSEBERRIES : OTHER",
+        "hsn_code": "8112090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FRUIT AND NUTS, UNCOOKED OR COOKED BYSTEAMING OR BOILING IN WATER,\nFROZEN,WHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER\nRASPBERRIES, BLACKBERRIES, MULBERRIES,LOGANBERRIES, BLACK, WHITE OR RED\nCURRANTSAND GOOSEBERRIES : NOT CONTAINING ADDED SUGAR",
+        "hsn_code": "8112020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FRUIT AND NUTS, UNCOOKED OR COOKED BYSTEAMING OR BOILING IN WATER,\nFROZEN,WHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER\nRASPBERRIES, BLACKBERRIES, MULBERRIES,LOGANBERRIES, BLACK, WHITE OR RED\nCURRANTSAND GOOSEBERRIES : CONTAINING ADDED SUGAR",
+        "hsn_code": "8112010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGANESE AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP",
+        "hsn_code": "8111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MANGANESE AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - MANGANESE AND\nARTICLES THEREOF, INCLUDING WASTE AND SCRAP:OTHER",
+        "hsn_code": "81110090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MANGANESE AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - MANGANESE AND\nARTICLES THEREOF, INCLUDING WASTE AND SCRAP:WROUGHT MANGANESE",
+        "hsn_code": "81110030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MANGANESE AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - MANGANESE AND\nARTICLES THEREOF, INCLUDING WASTE AND SCRAP:WASTE AND SCRAP OF MANGANESE BASE\nALLOYS",
+        "hsn_code": "81110020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MANGANESE AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - MANGANESE AND\nARTICLES THEREOF, INCLUDING WASTE AND SCRAP:UNWROUGHT MANGANESE AND\nMANGANESE BASE ALLOYS",
+        "hsn_code": "81110010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FRUIT AND NUTS, UNCOOKED OR COOKED BYSTEAMING OR BOILING IN WATER,\nFROZEN,WHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER\nSTRAWBERRIES : OTHER",
+        "hsn_code": "8111090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FRUIT AND NUTS, UNCOOKED OR COOKED BYSTEAMING OR BOILING IN WATER,\nFROZEN,WHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER\nSTRAWBERRIES : NOT CONTAINING ADDED SUGAR",
+        "hsn_code": "8111020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FRUIT AND NUTS, UNCOOKED OR COOKED BYSTEAMING OR BOILING IN WATER,\nFROZEN,WHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER\nSTRAWBERRIES : CONTAINING ADDED SUGAR",
+        "hsn_code": "8111010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANTIMONY AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP",
+        "hsn_code": "8110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANTIMONY AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP OTHER",
+        "hsn_code": "81109000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANTIMONY AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP WASTE AND SCRAP",
+        "hsn_code": "81102000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANTIMONY AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP UNWROUGHT ANTIMONY;\nPOWDERS",
+        "hsn_code": "81101000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZIRCONIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP",
+        "hsn_code": "8109",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZIRCONIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP OTHER",
+        "hsn_code": "81099000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZIRCONIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP WASTE AND SCRAP",
+        "hsn_code": "81093000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZIRCONIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP UNWROUGHT ZIRCONIUM;\nPOWDERS",
+        "hsn_code": "81092000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FRUIT, FRESH OTHER : OTHER",
+        "hsn_code": "8109090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FRUIT, FRESH OTHER : LICHI",
+        "hsn_code": "8109060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FRUIT, FRESH OTHER : BORE",
+        "hsn_code": "8109050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FRUIT, FRESH OTHER : CUSTARD-APPLE (ATA)",
+        "hsn_code": "8109040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FRUIT, FRESH OTHER : SAPOTA (CHICO)",
+        "hsn_code": "8109030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FRUIT, FRESH OTHER : TAMARIND, FRESH",
+        "hsn_code": "8109020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FRUIT, FRESH OTHER : POMEGRANATES",
+        "hsn_code": "8109010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TITANIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP",
+        "hsn_code": "8108",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TITANIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER:OTHER",
+        "hsn_code": "81089090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TITANIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER:TITANIUM,\nWROUGHT",
+        "hsn_code": "81089010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TITANIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP WASTE AND SCRAP",
+        "hsn_code": "81083000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TITANIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP UNWROUGHT TITANIUM;\nPOWDERS",
+        "hsn_code": "81082000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CADMIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP",
+        "hsn_code": "8107",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CADMIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER:OTHER",
+        "hsn_code": "81079090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CADMIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER:CADMIUM,\nWROUGHT",
+        "hsn_code": "81079010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CADMIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - WASTE AND SCRAP",
+        "hsn_code": "81073000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CADMIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - UNWROUGHT CADMIUM;\nPOWDERS",
+        "hsn_code": "81072000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FRUIT, FRESH - PERSIMMONS",
+        "hsn_code": "8107000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BISMUTH AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP",
+        "hsn_code": "8106",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BISMUTH AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - BISMUTH AND ARTICLES\nTHEREOF, INCLUDING WASTE AND SCRAP:OTHER",
+        "hsn_code": "81060090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BISMUTH AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - BISMUTH AND ARTICLES\nTHEREOF, INCLUDING WASTE AND SCRAP:BISMUTH, WROUGHT",
+        "hsn_code": "81060030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BISMUTH AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - BISMUTH AND ARTICLES\nTHEREOF, INCLUDING WASTE AND SCRAP:WASTE AND SCRAP OF BISMUTH AND BISMUTH\nALLOYS",
+        "hsn_code": "81060020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BISMUTH AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - BISMUTH AND ARTICLES\nTHEREOF, INCLUDING WASTE AND SCRAP:BISMUTH, UNWROUGHT",
+        "hsn_code": "81060010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FRUIT, FRESH DURIANS",
+        "hsn_code": "8106000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COBALT MATTES AND OTHER INTERMEDIATE PRODUCTS OF COBALT METALLURGY; COBALT AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP",
+        "hsn_code": "8105",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COBALT MATTES AND OTHER INTERMEDIATE PRODUCTS OF COBALT METALLURGY; COBALT AND\nARTICLES THEREOF, INCLUDING WASTE AND SCRAP OTHER",
+        "hsn_code": "81059000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COBALT MATTES AND OTHER INTERMEDIATE PRODUCTS OF COBALT METALLURGY; COBALT AND\nARTICLES THEREOF, INCLUDING WASTE AND SCRAP WASTE AND SCRAP",
+        "hsn_code": "81053000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COBALT MATTES AND OTHER INTERMEDIATE PRODUCTS OF COBALT METALLURGY; COBALT AND\nARTICLES THEREOF, INCLUDING WASTE AND SCRAP - COBALT MATTES AND OTHER\nINTERMEDIATE PRODUCTS OF COBALT METALLURGY; UNWROUGHT COBALT;\nPOWDERS:POWDERS",
+        "hsn_code": "81052030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COBALT MATTES AND OTHER INTERMEDIATE PRODUCTS OF COBALT METALLURGY; COBALT AND\nARTICLES THEREOF, INCLUDING WASTE AND SCRAP - COBALT MATTES AND OTHER\nINTERMEDIATE PRODUCTS OF COBALT METALLURGY; UNWROUGHT COBALT; POWDERS:COBALT\nUNWROUGHT",
+        "hsn_code": "81052020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COBALT MATTES AND OTHER INTERMEDIATE PRODUCTS OF COBALT METALLURGY; COBALT AND\nARTICLES THEREOF, INCLUDING WASTE AND SCRAP - COBALT MATTES AND OTHER\nINTERMEDIATE PRODUCTS OF COBALT METALLURGY; UNWROUGHT COBALT; POWDERS: COBALT\nMATTES AND OTHER INTERMEDIATE PRODUCTS OF COBALT METALLURGY",
+        "hsn_code": "81052010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FRUIT, FRESH KIWI FRUIT",
+        "hsn_code": "8105000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNESIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP",
+        "hsn_code": "8104",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNESIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER:OTHER",
+        "hsn_code": "81049090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNESIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER:WIRE",
+        "hsn_code": "81049030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNESIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER:FLAKES",
+        "hsn_code": "81049020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNESIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER:OTHER\nMAGNESIUM AND MAGNESIUM BASE ALLOYS, WROUGHT",
+        "hsn_code": "81049010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNESIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - RASPINGS, TURNINGS\nAND GRANULES, GRADED ACCORDING TO SIZE; POWDERS:POWDERS",
+        "hsn_code": "81043020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNESIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - RASPINGS, TURNINGS\nAND GRANULES, GRADED ACCORDING TO SIZE; POWDERS:RASPINGS, TURNINGS AND\nGRANULES, GRADED ACCORDING TO SIZE",
+        "hsn_code": "81043010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNESIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - WASTE AND SCRAP:\nOTHER",
+        "hsn_code": "81042090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNESIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - WASTE AND\nSCRAP:MAGNESIUM SCRAP, NAMELY THE FOLLOWING: MAGNESIUM CLIPS COVERED BY ISRI\nCODE WORD `WAFER; MAGNESIUM SCRAP COVERED BY ISRI CODE WORD `WALNUT;\nMAGNESIUM ENGRAVER PLATES COVERED BY ISRI CODE WORD `WINE; MAGNESIUM DOCK\nBOARDS COVERED BY ISRI CODE WORD `WOOD; MAGNESIUM TURNINGS COVERED BY ISRI CODE\nWORD `WORLD",
+        "hsn_code": "81042010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNESIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - UNWROUGHT\nMAGNESIUM:OTHER",
+        "hsn_code": "81041900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNESIUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - UNWROUGHT\nMAGNESIUM:CONTAINING AT LEAST 99.8% BY WEIGHT OF MAGNESIUM",
+        "hsn_code": "81041100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FRUIT, FRESH CRANBERRIES, BILBERRIES AND OTHER FRUITS OF THE GENUS VACCINIUM",
+        "hsn_code": "8104000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANTALUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP",
+        "hsn_code": "8103",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANTALUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER",
+        "hsn_code": "81039000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANTALUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - WASTE AND SCRAP",
+        "hsn_code": "81033000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANTALUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - UNWROUGHT\nTANTALUM, INCLUDING BARS AND RODS OBTAINED SIMPLY BY SINTERING; POWDERS: OTHER",
+        "hsn_code": "81032090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANTALUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - UNWROUGHT\nTANTALUM, INCLUDING BARS AND RODS OBTAINED SIMPLY BY SINTERING; POWDERS: HOLLOW\nBARS",
+        "hsn_code": "81032010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FRUIT, FRESH BLACK, WHITE OR RED CURRANTS AND GOOSEBERRIES",
+        "hsn_code": "8103000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MOLYBDENUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP",
+        "hsn_code": "8102",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MOLYBDENUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER: OTHER",
+        "hsn_code": "81029900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MOLYBDENUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER: WASTE AND\nSCRAP",
+        "hsn_code": "81029700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MOLYBDENUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER: WIRE",
+        "hsn_code": "81029600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MOLYBDENUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER: - BARS AND\nRODS, OTHER THAN THOSE OBTAINED SIMPLY BY SINTERING, PROFILES, PLATES, SHEETS, STRIP\nAND FOIL: OTHER",
+        "hsn_code": "81029590",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MOLYBDENUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER: - BARS AND\nRODS, OTHER THAN THOSE OBTAINED SIMPLY BY SINTERING, PROFILES, PLATES, SHEETS, STRIP\nAND FOIL: HOLLOW BARS",
+        "hsn_code": "81029510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MOLYBDENUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER: UNWROUGHT\nMOLYBDENUM, INCLUDING BARS AND RODS OBTAINED SIMPLY BY SINTERING",
+        "hsn_code": "81029400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MOLYBDENUM AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP POWDERS",
+        "hsn_code": "81021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FRUIT, FRESH RASPBERRIES, BLACKBERRIES, MULBERRIES AND LOGANBERRIES",
+        "hsn_code": "8102000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUNGSTEN (WOLFRAM) AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP",
+        "hsn_code": "8101",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUNGSTEN (WOLFRAM) AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER: -\nOTHER: OTHER",
+        "hsn_code": "81019990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUNGSTEN (WOLFRAM) AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER: -\nOTHER: TUNGSTEN FILAMENT",
+        "hsn_code": "81019910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUNGSTEN (WOLFRAM) AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER:\nWASTE AND SCRAP",
+        "hsn_code": "81019700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUNGSTEN (WOLFRAM) AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER: WIRE",
+        "hsn_code": "81019600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUNGSTEN (WOLFRAM) AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER: -\nBARS AND RODS, OTHER THAN THOSE OBTAINED SIMPLY BY SINTERING, PROFILES, PLATES,\nSHEETS, STRIP AND FOIL: OTHER",
+        "hsn_code": "81019590",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUNGSTEN (WOLFRAM) AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER: -\nBARS AND RODS, OTHER THAN THOSE OBTAINED SIMPLY BY SINTERING, PROFILES, PLATES,\nSHEETS, STRIP AND FOIL: HOLLOW BARS AND RODS",
+        "hsn_code": "81019510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUNGSTEN (WOLFRAM) AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - OTHER:\nUNWROUGHT TUNGSTEN, INCLUDING BARS AND RODS OBTAINED SIMPLY BY SINTERING",
+        "hsn_code": "81019400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUNGSTEN (WOLFRAM) AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP POWDERS",
+        "hsn_code": "81011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FRUIT, FRESH STRAWBERRIES",
+        "hsn_code": "8101000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF IRON OR STEEL;\nIRON OR STEEL WOOL; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE, OF IRON OR STEEL - OTHER : OF IRON (OTHER THAN CAST IRON) OR STEEL, ENAMELLED :\nGHAMELLAS",
+        "hsn_code": "73239410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - BARS AND\nRODS, OF HIGH SPEED STEEL : BRIGHT BARS",
+        "hsn_code": "72281010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - BARS AND\nRODS, OF HIGH SPEED STEEL : OTHER",
+        "hsn_code": "72281090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - BARS AND\nRODS, OF SILICO-MANGANESE STEEL",
+        "hsn_code": "72282000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - OTHER BARS\nAND RODS, NOT FURTHER WORKED THAN HOT-ROLLED, HOT-DRAWN OR EXTRUDED : BRIGHT\nBARS : OF ALLOY TOOL STEEL",
+        "hsn_code": "72283011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - OTHER BARS\nAND RODS, NOT FURTHER WORKED THAN HOT-ROLLED, HOT-DRAWN OR EXTRUDED : BRIGHT\nBARS : OTHER",
+        "hsn_code": "72283019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - OTHER BARS\nAND RODS, NOT FURTHER WORKED THAN HOT-ROLLED, HOT-DRAWN OR EXTRUDED : OTHER :\nLEAD BEARING STEEL",
+        "hsn_code": "72283021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - OTHER BARS\nAND RODS, NOT FURTHER WORKED THAN HOT-ROLLED, HOT-DRAWN OR EXTRUDED : OTHER :\nSPRING STEEL",
+        "hsn_code": "72283022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - OTHER BARS\nAND RODS, NOT FURTHER WORKED THAN HOT-ROLLED, HOT-DRAWN OR EXTRUDED : OTHER :\nSULPHUR BEARING STEEL",
+        "hsn_code": "72283023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - OTHER BARS\nAND RODS, NOT FURTHER WORKED THAN HOT-ROLLED, HOT-DRAWN OR EXTRUDED : OTHER :\nTOOL AND DIE STEEL",
+        "hsn_code": "72283024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - OTHER BARS\nAND RODS, NOT FURTHER WORKED THAN HOT-ROLLED, HOT-DRAWN OR EXTRUDED : OTHER :\nOTHER",
+        "hsn_code": "72283029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - OTHER BARS\nAND RODS, NOT FURTHER WORKED THAN FORGED",
+        "hsn_code": "72284000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - OTHER BARS\nAND RODS, NOT FURTHER WORKED THAN COLD-FORMED OR COLD-FINISHED : OF ENGINE\nVALVES AND COLD HEADING STEEL",
+        "hsn_code": "72285010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - OTHER BARS\nAND RODS, NOT FURTHER WORKED THAN COLD-FORMED OR COLD-FINISHED : OTHER",
+        "hsn_code": "72285090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - OTHER BARS\nAND RODS : BRIGHT BARS : OF ALLOY TOOL STEEL",
+        "hsn_code": "72286011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - OTHER BARS\nAND RODS : BRIGHT BARS : OTHER",
+        "hsn_code": "72286012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - OTHER BARS\nAND RODS : OTHER : LEAD BEARING STEEL",
+        "hsn_code": "72286091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - OTHER BARS\nAND RODS : OTHER : SPRING STEEL",
+        "hsn_code": "72286092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - OTHER BARS\nAND RODS : OTHER : SULPHUR BEARING STEEL",
+        "hsn_code": "72286093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - OTHER BARS\nAND RODS : OTHER : TOOL AND DIE STEEL",
+        "hsn_code": "72286094",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - OTHER BARS\nAND RODS : OTHER : OTHER",
+        "hsn_code": "72286099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - ANGLES,\nSHAPES AND SECTIONS : NOT FURTHER WORKED THAN HOT-ROLLED, HOT-DRAWN OR\nEXTRUDED : OF 80 MM OR MORE",
+        "hsn_code": "72287011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - ANGLES,\nSHAPES AND SECTIONS : NOT FURTHER WORKED THAN HOT-ROLLED, HOT-DRAWN OR\nEXTRUDED : OF LESS THAN 80 MM",
+        "hsn_code": "72287012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - ANGLES,\nSHAPES AND SECTIONS : NOT FURTHER WORKED THAN COLD-FORMED OR COLD FINISHED: OF\n80 MM OR MORE",
+        "hsn_code": "72287021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - ANGLES,\nSHAPES AND SECTIONS : NOT FURTHER WORKED THAN COLD-FORMED OR COLD FINISHED: OF\nLESS THAN 80 MM",
+        "hsn_code": "72287022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - HOLLOW DRILL\nBARS AND RODS : OF ALLOY STEEL",
+        "hsn_code": "72288010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - HOLLOW DRILL\nBARS AND RODS : OF NON ALLOY STEEL, FORGED",
+        "hsn_code": "72288020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER\nALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL - HOLLOW DRILL\nBARS AND RODS : OTHER",
+        "hsn_code": "72288090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF OTHER ALLOY STEEL; ANGLES, SHAPES AND SECTIONS, OF OTHER ALLOY STEEL; HOLLOW DRILL BARS AND RODS, OF ALLOY OR NON-ALLOY STEEL",
+        "hsn_code": "7228",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF OTHER ALLOY STEEL - OF\nHIGH SPEED STEEL",
+        "hsn_code": "72271000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF OTHER ALLOY STEEL - OF\nSILICO-MANGANESE STEEL",
+        "hsn_code": "72272000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF OTHER ALLOY STEEL - OTHER\n: VALVE SPRING STEEL QUALITY",
+        "hsn_code": "72279010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF OTHER ALLOY STEEL - OTHER\n: OTHER SPRING STEEL QUALITY",
+        "hsn_code": "72279020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF OTHER ALLOY STEEL - OTHER\n: BALL BEARING QUALITY",
+        "hsn_code": "72279030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF OTHER ALLOY STEEL - OTHER\n: COLD HEADING QUALITY",
+        "hsn_code": "72279040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF OTHER ALLOY STEEL - OTHER\n: LEAD-FREE CUTTING QUALITY",
+        "hsn_code": "72279050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF OTHER ALLOY STEEL - OTHER\n: SULPHUR FREE CUTTING QUALITY",
+        "hsn_code": "72279060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF OTHER ALLOY STEEL - OTHER\n: OTHER",
+        "hsn_code": "72279090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF OTHER ALLOY STEEL",
+        "hsn_code": "7227",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OF\nSILICON-ELECTRICAL STEEL : GRAIN-ORIENTED",
+        "hsn_code": "72261100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM- OF\nSILICON-ELECTRICAL STEEL - OTHER THAN GRAIN ORIENTED - HOT ROLLED",
+        "hsn_code": "72261910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM- OF\nSILICON-ELECTRICAL STEEL - OTHER THAN GRAIN ORIENTED - COLD ROLLED",
+        "hsn_code": "72261920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM- OF\nSILICON-ELECTRICAL STEEL - OTHER THAN GRAIN ORIENTED - OTHER THAN HOT ROLLED OR\nCOLD ROLLED",
+        "hsn_code": "72261990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OF HIGH\nSPEED STEEL : HOT-ROLLED : IN COILS",
+        "hsn_code": "72262011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OF HIGH\nSPEED STEEL : HOT-ROLLED : OTHER",
+        "hsn_code": "72262012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OF HIGH\nSPEED STEEL : COLD-ROLLED : IN COILS",
+        "hsn_code": "72262021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OF HIGH\nSPEED STEEL : COLD-ROLLED : OTHER",
+        "hsn_code": "72262022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OF HIGH\nSPEED STEEL : HOOPS AND STRIPS",
+        "hsn_code": "72262030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER:\nNOT FURTHER WORKED THAN HOT-ROLLED : OF A THICKNESS OF BELOW 3 MM",
+        "hsn_code": "72269110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER:\nNOT FURTHER WORKED THAN HOT-ROLLED : OF A THICKNESS OF 3 MM AND ABOVE BUT NOT\nEXCEEDING 4.75 MM",
+        "hsn_code": "72269120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER:\nNOT FURTHER WORKED THAN HOT-ROLLED : HIGH TENSILE QUALITY OF A THICKNESS OF ABOVE\n4.75 MM",
+        "hsn_code": "72269130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER:\nNOT FURTHER WORKED THAN HOT-ROLLED : OTHER",
+        "hsn_code": "72269190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER:\nNOT FURTHER WORKED THAN COLD-ROLLED (COLDREDUCED): OF A THICKNESS OF BELOW 3\nMM",
+        "hsn_code": "72269210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER:\nNOT FURTHER WORKED THAN COLD-ROLLED (COLDREDUCED): OF A THICKNESS OF 3 MM AND\nABOVE BUT NOT EXCEEDING 4.75 MM",
+        "hsn_code": "72269220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER:\nNOT FURTHER WORKED THAN COLD-ROLLED (COLDREDUCED): OF A THICKNESS OF ABOVE 4.75\nMM",
+        "hsn_code": "72269230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER:\nELECTROLYTICALLY PLATED OR COATED WITH ZINC",
+        "hsn_code": "72269300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER:\nOTHERWISE PLATED OR COATED WITH ZINC",
+        "hsn_code": "72269400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER:\nOTHER : OF A THICKNESS OF ABOVE 4.75 MM",
+        "hsn_code": "72269910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER:\nOTHER : OF A THICKNESS OF ABOVE 3 MM AND BUT NOT EXCEEDING 4.75 MM",
+        "hsn_code": "72269920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER:\nOTHER : OF A THICKNESS OF BELOW 3 MM",
+        "hsn_code": "72269930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER:\nOTHER : SKELP (STRIPS FOR PIPES AND TUBES)",
+        "hsn_code": "72269940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER:\nOTHER : HOOPS AND STRIPS : HOT ROLLED",
+        "hsn_code": "72269951",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER:\nOTHER : HOOPS AND STRIPS : COLD ROLLED",
+        "hsn_code": "72269952",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER:\nOTHER : HOOPS AND STRIPS : OF SPRING STEEL, OTHER THAN SKELP",
+        "hsn_code": "72269953",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER:\nOTHER : HOOPS AND STRIPS : SKELPS AND STRIPS FOR RAZOR BLADES AND SAW BLADES",
+        "hsn_code": "72269960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER:\nOTHER : HOOPS AND STRIPS : OTHER",
+        "hsn_code": "72269990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM",
+        "hsn_code": "7226",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OF\nSILICON-ELECTRICAL STEEL : GRAIN-ORIENTED",
+        "hsn_code": "72251100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OF\nSILICON-ELECTRICAL STEEL - OTHER THAN GRAIN ORIENTED - HOT ROLLED",
+        "hsn_code": "72251910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OF\nSILICON-ELECTRICAL STEEL - OTHER THAN GRAIN ORIENTED - COLD ROLLED",
+        "hsn_code": "72251920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OF\nSILICON-ELECTRICAL STEEL - OTHER THAN GRAIN ORIENTED - OTHER THAN HOT OR COLD\nROLLED",
+        "hsn_code": "72251990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OF HIGH\nSPEED STEEL : HOT-ROLLED : IN COILS",
+        "hsn_code": "72252011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OF HIGH\nSPEED STEEL : HOT-ROLLED : NOT IN COILS",
+        "hsn_code": "72252019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OF HIGH\nSPEED STEEL : COLD-ROLLED : IN COILS",
+        "hsn_code": "72252021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OF HIGH\nSPEED STEEL : COLD-ROLLED : NOT IN COILS",
+        "hsn_code": "72252029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OTHER,\nNOT FURTHER WORKED THAN HOT-ROLLED, IN COILS : OF SPRING STEEL QUALITY",
+        "hsn_code": "72253010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OTHER,\nNOT FURTHER WORKED THAN HOT-ROLLED, IN COILS : OTHER",
+        "hsn_code": "72253090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OTHER,\nNOT FURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF ABOVE 4.75 MM\n: BOILER QUALITY",
+        "hsn_code": "72254011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OTHER,\nNOT FURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF ABOVE 4.75 MM\n: PRESSURE VESSEL QUALITY",
+        "hsn_code": "72254012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OTHER,\nNOT FURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF ABOVE 4.75 MM\n: HIGH TENSILE QUALITY",
+        "hsn_code": "72254013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OTHER,\nNOT FURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF ABOVE 4.75 MM\n: OTHER",
+        "hsn_code": "72254019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OTHER,\nNOT FURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF 3 MM AND\nABOVE BUT NOT EXCEEDING 4.75 MM",
+        "hsn_code": "72254020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OTHER,\nNOT FURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF BELOW 3 MM",
+        "hsn_code": "72254030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OTHER,\nNOT FURTHER WORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF LESS THAN\n3 MM",
+        "hsn_code": "72255010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OTHER,\nNOT FURTHER WORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF 3 MM TO\n4.75 MM",
+        "hsn_code": "72255020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OTHER,\nNOT FURTHER WORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF ABOVE 4.75\nMM",
+        "hsn_code": "72255030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OTHER :\nELECTROLYTICALLY PLATED OR COATED WITH ZINC",
+        "hsn_code": "72259100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OTHER :\nOTHERWISE PLATED OR COATED WITH ZINC",
+        "hsn_code": "72259200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE - OTHER :\nOTHER",
+        "hsn_code": "72259900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF OTHER ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE",
+        "hsn_code": "7225",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - BARS AND RODS, NOT FURTHER WORKED THAN HOT ROLLED, HOT-DRAWN OR\nEXTRUDED: OF CIRCULAR CROSS SECTION : BRIGHT BARS : CHROMIUM TYPE",
+        "hsn_code": "72221111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - BARS AND RODS, NOT FURTHER WORKED THAN HOT ROLLED, HOT-DRAWN OR\nEXTRUDED: OF CIRCULAR CROSS SECTION : BRIGHT BARS : NICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72221112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - BARS AND RODS, NOT FURTHER WORKED THAN HOT ROLLED, HOT-DRAWN OR\nEXTRUDED: OF CIRCULAR CROSS SECTION : BRIGHT BARS : OTHER",
+        "hsn_code": "72221119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - BARS AND RODS, NOT FURTHER WORKED THAN HOT ROLLED, HOT-DRAWN OR\nEXTRUDED: OF CIRCULAR CROSS SECTION : OTHER : CHROMIUM TYPE",
+        "hsn_code": "72221191",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - BARS AND RODS, NOT FURTHER WORKED THAN HOT ROLLED, HOT-DRAWN OR\nEXTRUDED: OF CIRCULAR CROSS SECTION : OTHER : NICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72221192",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - BARS AND RODS, NOT FURTHER WORKED THAN HOT ROLLED, HOT-DRAWN OR\nEXTRUDED: OF CIRCULAR CROSS SECTION : OTHER : OTHER",
+        "hsn_code": "72221199",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - BARS AND RODS, NOT FURTHER WORKED THAN HOT ROLLED, HOT-DRAWN OR\nEXTRUDED: OTHER : BRIGHT BARS : CHROMIUM TYPE",
+        "hsn_code": "72221911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - BARS AND RODS, NOT FURTHER WORKED THAN HOT ROLLED, HOT-DRAWN OR\nEXTRUDED: OTHER : BRIGHT BARS : NICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72221912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - BARS AND RODS, NOT FURTHER WORKED THAN HOT ROLLED, HOT-DRAWN OR\nEXTRUDED: OTHER : BRIGHT BARS : OTHER",
+        "hsn_code": "72221919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - BARS AND RODS, NOT FURTHER WORKED THAN HOT ROLLED, HOT-DRAWN OR\nEXTRUDED: OTHER : OTHER : CHROMIUM TYPE",
+        "hsn_code": "72221991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - BARS AND RODS, NOT FURTHER WORKED THAN HOT ROLLED, HOT-DRAWN OR\nEXTRUDED: OTHER : OTHER : NICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72221992",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - BARS AND RODS, NOT FURTHER WORKED THAN HOT ROLLED, HOT-DRAWN OR\nEXTRUDED: OTHER : OTHER : OTHER",
+        "hsn_code": "72221999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - BARS AND RODS, NOT FURTHER WORKED THAN COLD FORMED OR COLD-FINISHED :\nBRIGHT BARS : CHROMIUM TYPE",
+        "hsn_code": "72222011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - BARS AND RODS, NOT FURTHER WORKED THAN COLD FORMED OR COLD-FINISHED :\nBRIGHT BARS : NICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72222012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - BARS AND RODS, NOT FURTHER WORKED THAN COLD FORMED OR COLD-FINISHED :\nBRIGHT BARS : OTHER",
+        "hsn_code": "72222019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - BARS AND RODS, NOT FURTHER WORKED THAN COLD FORMED OR COLD-FINISHED :\nOTHER : CHROMIUM TYPE",
+        "hsn_code": "72222091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - BARS AND RODS, NOT FURTHER WORKED THAN COLD FORMED OR COLD-FINISHED :\nOTHER : NICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72222092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - BARS AND RODS, NOT FURTHER WORKED THAN COLD FORMED OR COLD-FINISHED :\nOTHER : OTHER",
+        "hsn_code": "72222099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - OTHER BARS AND RODS : BRIGHT BARS : CHROMIUM TYPE",
+        "hsn_code": "72223011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - OTHER BARS AND RODS : BRIGHT BARS : NICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72223012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - OTHER BARS AND RODS : BRIGHT BARS : OTHER",
+        "hsn_code": "72223019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - OTHER BARS AND RODS : OTHER : CHROMIUM TYPE",
+        "hsn_code": "72223091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - OTHER BARS AND RODS : OTHER : NICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72223092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - OTHER BARS AND RODS : OTHER : OTHER",
+        "hsn_code": "72223099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - ANGLES, SHAPES AND SECTIONS : OF THICKNESS OF 80 MM AND ABOVE",
+        "hsn_code": "72224010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS\nSTEEL - ANGLES, SHAPES AND SECTIONS : OF BELOW 80 MM",
+        "hsn_code": "72224020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF STAINLESS STEEL; ANGLES, SHAPES AND SECTIONS OF STAINLESS STEEL",
+        "hsn_code": "7222",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF STAINLESS STEEL - BARS AND\nRODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF STAINLESS STEEL : BRIGHT BARS :\nCHROMIUM TYPE",
+        "hsn_code": "72210011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF STAINLESS STEEL - BARS AND\nRODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF STAINLESS STEEL : BRIGHT BARS :\nNICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72210012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF STAINLESS STEEL - BARS AND\nRODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF STAINLESS STEEL : BRIGHT BARS :\nOTHER",
+        "hsn_code": "72210019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF STAINLESS STEEL - OTHER",
+        "hsn_code": "72210090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF STAINLESS STEEL",
+        "hsn_code": "7221",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - NOT\nFURTHER WORKED THAN HOT-ROLLED : OF A THICKNESS OF 4.75 MM OR MORE : SKELP FOR\nPIPES AND TUBES",
+        "hsn_code": "72201110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - NOT\nFURTHER WORKED THAN HOT-ROLLED : OF A THICKNESS OF 4.75 MM OR MORE : STRIPS FOR\nPIPES AND TUBES (OTHER THAN SKELP) : CHROMIUM TYPE",
+        "hsn_code": "72201121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - NOT\nFURTHER WORKED THAN HOT-ROLLED : OF A THICKNESS OF 4.75 MM OR MORE : STRIPS FOR\nPIPES AND TUBES (OTHER THAN SKELP) : NICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72201122",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - NOT\nFURTHER WORKED THAN HOT-ROLLED : OF A THICKNESS OF 4.75 MM OR MORE : STRIPS FOR\nPIPES AND TUBES (OTHER THAN SKELP) : OTHER",
+        "hsn_code": "72201129",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - NOT\nFURTHER WORKED THAN HOT-ROLLED : OF A THICKNESS OF 4.75 MM OR MORE : OTHER",
+        "hsn_code": "72201190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - NOT\nFURTHER WORKED THAN HOT-ROLLED : OF A THICKNESS OF LESS THAN 4.75 MM : SKELP FOR\nPIPES AND TUBES",
+        "hsn_code": "72201210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - NOT\nFURTHER WORKED THAN HOT-ROLLED : OF A THICKNESS OF LESS THAN 4.75 MM : STRIPS FOR\nPIPES AND TUBES (OTHER THAN SKELP) : CHROMIUM TYPE",
+        "hsn_code": "72201221",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - NOT\nFURTHER WORKED THAN HOT-ROLLED : OF A THICKNESS OF LESS THAN 4.75 MM : STRIPS FOR\nPIPES AND TUBES (OTHER THAN SKELP) : NICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72201222",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - NOT\nFURTHER WORKED THAN HOT-ROLLED : OF A THICKNESS OF LESS THAN 4.75 MM : STRIPS FOR\nPIPES AND TUBES (OTHER THAN SKELP) : OTHER",
+        "hsn_code": "72201229",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - NOT\nFURTHER WORKED THAN HOT-ROLLED : OF A THICKNESS OF LESS THAN 4.75 MM : OTHER",
+        "hsn_code": "72201290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED): SKELP FOR PIPES AND TUBES",
+        "hsn_code": "72202010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED): STRIPS FOR PIPES AND TUBES (OTHER\nTHAN SKELP) : CHROMIUM TYPE",
+        "hsn_code": "72202021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED): STRIPS FOR PIPES AND TUBES (OTHER\nTHAN SKELP) : NICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72202022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED): STRIPS FOR PIPES AND TUBES (OTHER\nTHAN SKELP) : OTHER",
+        "hsn_code": "72202029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED): OTHER",
+        "hsn_code": "72202090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER :\nSKELP (STRIPS FOR PIPES AND TUBES)",
+        "hsn_code": "72209010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER :\nSTRIPS FOR PIPES AND TUBES (OTHER THAN SKELP) : CHROMIUM TYPE",
+        "hsn_code": "72209021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER :\nSTRIPS FOR PIPES AND TUBES (OTHER THAN SKELP) : NICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72209022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER :\nSTRIPS FOR PIPES AND TUBES (OTHER THAN SKELP) : OTHER",
+        "hsn_code": "72209029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM - OTHER\n:OTHER",
+        "hsn_code": "72209090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF LESS THAN 600 MM",
+        "hsn_code": "7220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, IN COILS : OF A THICKNESS EXCEEDING 10 MM :\nCHROMIUM TYPE : NOT EXCEEDING 14 MM",
+        "hsn_code": "72191111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, IN COILS : OF A THICKNESS EXCEEDING 10 MM :\nCHROMIUM TYPE : EXCEEDING 14 MM",
+        "hsn_code": "72191112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, IN COILS : OF A THICKNESS EXCEEDING 10 MM :\nCHROMIUM TYPE : OTHER",
+        "hsn_code": "72191190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, IN COILS : OF A THICKNESS OF 4.75 MM OR MORE BUT\nNOT EXCEEDING 10 MM",
+        "hsn_code": "72191200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, IN COILS : OF A THICKNESS OF 3 MM OR MORE BUT LESS\nTHAN 4.75 MM",
+        "hsn_code": "72191300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, IN COILS : OF A THICKNESS OF LESS THAN 3 MM",
+        "hsn_code": "72191400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS EXCEEDING 10 MM :\nUNIVERSAL PLATES OF STAINLESS STEEL OR HEAT RESISTING STEEL AND CHROMIUM TYPE : NOT\nEXCEEDING 14 MM",
+        "hsn_code": "72192111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS EXCEEDING 10 MM :\nUNIVERSAL PLATES OF STAINLESS STEEL OR HEAT RESISTING STEEL AND CHROMIUM TYPE :\nEXCEEDING 14 MM",
+        "hsn_code": "72192112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS EXCEEDING 10 MM :\nUNIVERSAL PLATES OF STAINLESS STEEL HEAT RESISTING STEEL, NICKEL CHROMIUM AUSTENITIC\nTYPE: NOT EXCEEDING 14 MM",
+        "hsn_code": "72192121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS EXCEEDING 10 MM :\nUNIVERSAL PLATES OF STAINLESS STEEL HEAT RESISTING STEEL, NICKEL CHROMIUM AUSTENITIC\nTYPE: EXCEEDING 14 MM",
+        "hsn_code": "72192122",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS EXCEEDING 10 MM :\nOTHER CHROMIUM TYPE : NOT EXCEEDING 14 MM",
+        "hsn_code": "72192131",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS EXCEEDING 10 MM :\nOTHER CHROMIUM TYPE : EXCEEDING 14 MM",
+        "hsn_code": "72192132",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS EXCEEDING 10 MM :\nOTHER NICKEL CHROMIUM AUSTENITIC TYPE : NOT EXCEEDING 14 MM",
+        "hsn_code": "72192141",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS EXCEEDING 10 MM :\nOTHER NICKEL CHROMIUM AUSTENITIC TYPE : EXCEEDING 14 MM",
+        "hsn_code": "72192142",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS EXCEEDING 10 MM :\nOTHER",
+        "hsn_code": "72192190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF 4.75 MM OR MORE\nBUT NOT EXCEEDING 10 MM : UNIVERSAL PLATES OF STAINLESS STEEL OR HEAT RESISTING STEEL\n: CHROMIUM TYPE",
+        "hsn_code": "72192211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF 4.75 MM OR MORE\nBUT NOT EXCEEDING 10 MM : UNIVERSAL PLATES OF STAINLESS STEEL OR HEAT RESISTING STEEL\n: NICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72192212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF 4.75 MM OR MORE\nBUT NOT EXCEEDING 10 MM : UNIVERSAL PLATES OF STAINLESS STEEL OR HEAT RESISTING STEEL\n: OTHER",
+        "hsn_code": "72192219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF 4.75 MM OR MORE\nBUT NOT EXCEEDING 10 MM : OTHER : CHROMIUM TYPE",
+        "hsn_code": "72192291",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF 4.75 MM OR MORE\nBUT NOT EXCEEDING 10 MM : OTHER : NICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72192292",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF 4.75 MM OR MORE\nBUT NOT EXCEEDING 10 MM : OTHER : OTHER",
+        "hsn_code": "72192299",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF 3 MM OR MORE BUT\nLESS THAN 4.75 MM : CHROMIUM TYPE",
+        "hsn_code": "72192310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF 3 MM OR MORE BUT\nLESS THAN 4.75 MM : NICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72192320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF 3 MM OR MORE BUT\nLESS THAN 4.75 MM : OTHER",
+        "hsn_code": "72192390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF LESS THAN 3 MM :\nCHROMIUM TYPE, OF A THICKNESS : BELOW 0.35 MM",
+        "hsn_code": "72192411",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF LESS THAN 3 MM :\nCHROMIUM TYPE, OF A THICKNESS : 0.35 MM AND ABOVE BUT BELOW 0.56 MM",
+        "hsn_code": "72192412",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF LESS THAN 3 MM :\nCHROMIUM TYPE, OF A THICKNESS : 0.56 MM AND ABOVE BUT BELOW 0.90 MM",
+        "hsn_code": "72192413",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF LESS THAN 3 MM :\nCHROMIUM TYPE, OF A THICKNESS : OTHER",
+        "hsn_code": "72192419",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF LESS THAN 3 MM :\nNICKEL CHROMIUM AUSTENITIC TYPE, OF A THICKNESS : BELOW 0.35 MM",
+        "hsn_code": "72192421",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF LESS THAN 3 MM :\nNICKEL CHROMIUM AUSTENITIC TYPE, OF A THICKNESS : 0.35 MM AND ABOVE BUT BELOW 0.56\nMM",
+        "hsn_code": "72192422",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF LESS THAN 3 MM :\nNICKEL CHROMIUM AUSTENITIC TYPE, OF A THICKNESS : 0.56 MM AND ABOVE BUT BELOW 0.90\nMM",
+        "hsn_code": "72192423",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF LESS THAN 3 MM :\nNICKEL CHROMIUM AUSTENITIC TYPE, OF A THICKNESS : OTHER",
+        "hsn_code": "72192429",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN HOT-ROLLED, NOT IN COILS : OF A THICKNESS OF LESS THAN 3 MM :\nOTHER",
+        "hsn_code": "72192490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED) : OF A THICKNESS OF 4.75 MM OR\nMORE : CHROMIUM TYPE : NOT EXCEEDING 14 MM",
+        "hsn_code": "72193111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED) : OF A THICKNESS OF 4.75 MM OR\nMORE : CHROMIUM TYPE : EXCEEDING 14 MM",
+        "hsn_code": "72193112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED) : OF A THICKNESS OF 4.75 MM OR\nMORE : NICKEL CHROMIUM AUSTENITIC TYPE : NOT EXCEEDING 14 MM",
+        "hsn_code": "72193121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED) : OF A THICKNESS OF 4.75 MM OR\nMORE : NICKEL CHROMIUM AUSTENITIC TYPE : EXCEEDING 14 MM",
+        "hsn_code": "72193122",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED) : OF A THICKNESS OF 4.75 MM OR\nMORE : NICKEL CHROMIUM AUSTENITIC TYPE : OTHER",
+        "hsn_code": "72193190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED) : OF A THICKNESS OF 3 MM OR MORE\nBUT LESS THAN 4.75 MM : CHROMIUM TYPE",
+        "hsn_code": "72193210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED) : OF A THICKNESS OF 3 MM OR MORE\nBUT LESS THAN 4.75 MM : NICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72193220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED) : OF A THICKNESS OF 3 MM OR MORE\nBUT LESS THAN 4.75 MM : OTHER",
+        "hsn_code": "72193290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED) : OF A THICKNESS EXCEEDING 1 MM\nBUT LESS THAN 3 MM : CHROMIUM TYPE",
+        "hsn_code": "72193310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED) : OF A THICKNESS EXCEEDING 1 MM\nBUT LESS THAN 3 MM : NICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72193320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED) : OF A THICKNESS EXCEEDING 1 MM\nBUT LESS THAN 3 MM : OTHER",
+        "hsn_code": "72193390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED) : OF A THICKNESS OF 0.5 MM OR\nMORE BUT NOT EXCEEDING 1 MM : CHROMIUM TYPE",
+        "hsn_code": "72193410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED) : OF A THICKNESS OF 0.5 MM OR\nMORE BUT NOT EXCEEDING 1 MM : NICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72193420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED) : OF A THICKNESS OF 0.5 MM OR\nMORE BUT NOT EXCEEDING 1 MM : OTHER",
+        "hsn_code": "72193490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED) : OF A THICKNESS OF LESS THAN 0.5\nMM : CHROMIUM TYPE",
+        "hsn_code": "72193510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED) : OF A THICKNESS OF LESS THAN 0.5\nMM : NICKEL CHROMIUM AUSTENITIC TYPE",
+        "hsn_code": "72193520",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - NOT\nFURTHER WORKED THAN COLD-ROLLED (COLD REDUCED) : OF A THICKNESS OF LESS THAN 0.5\nMM : OTHER",
+        "hsn_code": "72193590",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - OTHER :\nSHEETS AND PLATES : THICKNESS MORE THAN 4.75 MM",
+        "hsn_code": "72199011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - OTHER :\nSHEETS AND PLATES : THICKNESS 3 MM TO 4.75 MM",
+        "hsn_code": "72199012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - OTHER :\nSHEETS AND PLATES : THICKNESS LESS THAN 3 MM",
+        "hsn_code": "72199013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE - OTHER :\nOTHER",
+        "hsn_code": "72199090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF STAINLESS STEEL, OF A WIDTH OF 600 MM OR MORE",
+        "hsn_code": "7219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF IRON OR NON-ALLOY STEEL - OF FREE-CUTTING STEEL, NOT FURTHER\nWORKED THAN COLD-FORMED OR COLD-FINISHED",
+        "hsn_code": "72151000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF IRON OR NON-ALLOY STEEL - OTHER, NOT FURTHER WORKED THAN\nCOLD-FORMED OR COLD-FINISHED : MILD STEEL BRIGHT BAR",
+        "hsn_code": "72155010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF IRON OR NON-ALLOY STEEL - OTHER, NOT FURTHER WORKED THAN\nCOLD-FORMED OR COLD-FINISHED : OTHER",
+        "hsn_code": "72155090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF IRON OR NON-ALLOY STEEL - OTHER : PLATED OR COATED WITH ZINC",
+        "hsn_code": "72159010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF IRON OR NON-ALLOY STEEL - OTHER : PLATED OR COATED WITH\nOTHER BASE METALS",
+        "hsn_code": "72159020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF IRON OR NON-ALLOY STEEL - OTHER : OTHER",
+        "hsn_code": "72159090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF IRON OR NON-ALLOY STEEL",
+        "hsn_code": "7215",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF IRON OR NON-ALLOY STEEL, NOT FURTHER WORKED THAN FORGED,\nHOT-ROLLED, HOT-DRAWN OR HOT-EXTRUDED, BUT INCLUDING THOSE TWISTED AFTER\nROLLING - FORGED : SPRING STEEL QUALITY",
+        "hsn_code": "72141010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF IRON OR NON-ALLOY STEEL, NOT FURTHER WORKED THAN FORGED,\nHOT-ROLLED, HOT-DRAWN OR HOT-EXTRUDED, BUT INCLUDING THOSE TWISTED AFTER\nROLLING - FORGED : OTHER",
+        "hsn_code": "72141090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF IRON OR NON-ALLOY STEEL, NOT FURTHER WORKED THAN FORGED,\nHOT-ROLLED, HOT-DRAWN OR HOT-EXTRUDED, BUT INCLUDING THOSE TWISTED AFTER\nROLLING - CONTAINING INDENTATIONS, RIBS, GROOVES OR OTHER DEFORMATIONS PRODUCED\nDURING THE ROLLING PROCESS OR TWISTED AFTER ROLLING : SPRING STEEL QUALITY",
+        "hsn_code": "72142010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF IRON OR NON-ALLOY STEEL, NOT FURTHER WORKED THAN FORGED,\nHOT-ROLLED, HOT-DRAWN OR HOT-EXTRUDED, BUT INCLUDING THOSE TWISTED AFTER\nROLLING - CONTAINING INDENTATIONS, RIBS, GROOVES OR OTHER DEFORMATIONS PRODUCED\nDURING THE ROLLING PROCESS OR TWISTED AFTER ROLLING : OTHER",
+        "hsn_code": "72142090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF IRON OR NON-ALLOY STEEL, NOT FURTHER WORKED THAN FORGED,\nHOT-ROLLED, HOT-DRAWN OR HOT-EXTRUDED, BUT INCLUDING THOSE TWISTED AFTER\nROLLING - OTHER, OF FREE CUTTING STEEL",
+        "hsn_code": "72143000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF IRON OR NON-ALLOY STEEL, NOT FURTHER WORKED THAN FORGED,\nHOT-ROLLED, HOT-DRAWN OR HOT-EXTRUDED, BUT INCLUDING THOSE TWISTED AFTER\nROLLING - OTHER : OF RECTANGULAR (OTHER THAN SQUARE) CROSS-SECTION : MILD STEEL\nBRIGHT BAR",
+        "hsn_code": "72149110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF IRON OR NON-ALLOY STEEL, NOT FURTHER WORKED THAN FORGED,\nHOT-ROLLED, HOT-DRAWN OR HOT-EXTRUDED, BUT INCLUDING THOSE TWISTED AFTER\nROLLING - OTHER : OF RECTANGULAR (OTHER THAN SQUARE) CROSS-SECTION : OTHER",
+        "hsn_code": "72149190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF IRON OR NON-ALLOY STEEL, NOT FURTHER WORKED THAN FORGED,\nHOT-ROLLED, HOT-DRAWN OR HOT-EXTRUDED, BUT INCLUDING THOSE TWISTED AFTER\nROLLING - OTHER : OTHER : OF SPRING STEEL QUALITY",
+        "hsn_code": "72149910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF IRON OR NON-ALLOY STEEL, NOT FURTHER WORKED THAN FORGED,\nHOT-ROLLED, HOT-DRAWN OR HOT-EXTRUDED, BUT INCLUDING THOSE TWISTED AFTER\nROLLING - OTHER : OTHER : OTHER",
+        "hsn_code": "72149990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER BARS AND RODS OF IRON OR NON-ALLOY STEEL, NOT FURTHER WORKED THAN FORGED, HOT-ROLLED, HOT-DRAWN OR HOT-EXTRUDED, BUT INCLUDING THOSE TWISTED AFTER ROLLING",
+        "hsn_code": "7214",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF IRON OR NON-ALLOY STEEL -\nCONTAINING INDENTATIONS, RIBS, GROOVES OR OTHER DEFORMATIONS PRODUCED DURING\nTHE ROLLING PROCESS : OF FREE CUTTING STEEL",
+        "hsn_code": "72131010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF IRON OR NON-ALLOY STEEL -\nCONTAINING INDENTATIONS, RIBS, GROOVES OR OTHER DEFORMATIONS PRODUCED DURING\nTHE ROLLING PROCESS : OTHER",
+        "hsn_code": "72131090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF IRON OR NON-ALLOY STEEL -\nOTHER, OF FREE-CUTTING STEEL : ELECTRODE QUALITY",
+        "hsn_code": "72132010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF IRON OR NON-ALLOY STEEL -\nOTHER, OF FREE-CUTTING STEEL : COLD HEADING QUALITY",
+        "hsn_code": "72132020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF IRON OR NON-ALLOY STEEL -\nOTHER, OF FREE-CUTTING STEEL : OTHER",
+        "hsn_code": "72132090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF IRON OR NON-ALLOY STEEL -\nOTHER : OF CIRCULAR CROSS-SECTION MEASURING LESS THAN 14 MM IN DIAMETER :\nELECTRODE QUALITY",
+        "hsn_code": "72139110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF IRON OR NON-ALLOY STEEL -\nOTHER : OF CIRCULAR CROSS-SECTION MEASURING LESS THAN 14 MM IN DIAMETER : COLD\nHEADING QUALITY",
+        "hsn_code": "72139120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF IRON OR NON-ALLOY STEEL -\nOTHER : OF CIRCULAR CROSS-SECTION MEASURING LESS THAN 14 MM IN DIAMETER : OTHER",
+        "hsn_code": "72139190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF IRON OR NON-ALLOY STEEL -\nOTHER : OTHER : ELECTRODE QUALITY",
+        "hsn_code": "72139910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF IRON OR NON-ALLOY STEEL -\nOTHER : OTHER : COLD HEADING QUALITY",
+        "hsn_code": "72139920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF IRON OR NON-ALLOY STEEL -\nOTHER : OTHER : OTHER",
+        "hsn_code": "72139990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARS AND RODS, HOT-ROLLED, IN IRREGULARLY WOUND COILS, OF IRON OR NON-ALLOY STEEL",
+        "hsn_code": "7213",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS OF HEADING 7003, 7004 OR 7005, BENT, EDGE-WORKED, ENGRAVED, DRILLED,\nENAMELLED OR OTHERWISE WORKED, BUT NOT FRAMED OR FITTED WITH OTHER MATERIALS",
+        "hsn_code": "70060000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HATS AND OTHER HEADGEAR, PLAITED OR MADE BY ASSEMBLING STRIPS OF ANY MATERIAL,\nWHETHER ORNOT LINED OR TRIMMED",
+        "hsn_code": "65040000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SACKS AND BAGS, OF A KIND USED FOR THE PACKING OF GOODS",
+        "hsn_code": "6305",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SACKS AND BAGS, OF A KIND USED FOR THE PACKING OF GOODS OF OTHER TEXTILE MATERIALS",
+        "hsn_code": "63059000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SACKS AND BAGS, OF A KIND USED FOR THE PACKING OF GOODS OF MAN-MADE TEXTILE\nMATERIALS: OTHER",
+        "hsn_code": "63053900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SACKS AND BAGS, OF A KIND USED FOR THE PACKING OF GOODS OF MAN-MADE TEXTILE\nMATERIALS: OTHER, OF POLYETHYLENE OR POLYPROPYLENE STRIP OR THE LIKE",
+        "hsn_code": "63053300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SACKS AND BAGS, OF A KIND USED FOR THE PACKING OF GOODS OF COTTON",
+        "hsn_code": "63052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SACKS AND BAGS, OF A KIND USED FOR THE PACKING OF GOODS OF JUTE OR OF OTHER TEXTILE\nBAST FIBRES OF HEADING 5303: OTHER",
+        "hsn_code": "63051090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SACKS AND BAGS, OF A KIND USED FOR THE PACKING OF GOODS OF JUTE OR OF OTHER TEXTILE\nBAST FIBRES OF HEADING 5303: JUTE SOIL SAVERS",
+        "hsn_code": "63051080",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SACKS AND BAGS, OF A KIND USED FOR THE PACKING OF GOODS OF JUTE OR OF OTHER TEXTILE\nBAST FIBRES OF HEADING 5303: PAPER LAMINATED HESSIAN JUTE",
+        "hsn_code": "63051070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SACKS AND BAGS, OF A KIND USED FOR THE PACKING OF GOODS OF JUTE OR OF OTHER TEXTILE\nBAST FIBRES OF HEADING 5303: PLASTIC COATED OR PAPER CUM POLYTHENE LINED JUTE BAGS\nAND SACKS",
+        "hsn_code": "63051060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SACKS AND BAGS, OF A KIND USED FOR THE PACKING OF GOODS OF JUTE OR OF OTHER TEXTILE\nBAST FIBRES OF HEADING 5303: JUTE WOOL SACKS",
+        "hsn_code": "63051050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SACKS AND BAGS, OF A KIND USED FOR THE PACKING OF GOODS OF JUTE OR OF OTHER TEXTILE\nBAST FIBRES OF HEADING 5303: JUTE SACKING BAGS",
+        "hsn_code": "63051040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SACKS AND BAGS, OF A KIND USED FOR THE PACKING OF GOODS OF JUTE OR OF OTHER TEXTILE\nBAST FIBRES OF HEADING 5303: JUTE HESSIAN BAGS",
+        "hsn_code": "63051030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SACKS AND BAGS, OF A KIND USED FOR THE PACKING OF GOODS OF JUTE OR OF OTHER TEXTILE\nBAST FIBRES OF HEADING 5303: JUTE CORN (GRAINS) SACKS",
+        "hsn_code": "63051020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SACKS AND BAGS, OF A KIND USED FOR THE PACKING OF GOODS OF JUTE OR OF OTHER TEXTILE\nBAST FIBRES OF HEADING 5303: JUTE BAGGING FOR RAW COTTON",
+        "hsn_code": "63051010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SACKS AND BAGS, OF A KIND USED FOR THE PACKING OF GOODS OF MAN-MADE TEXTILE\nMATERIALS: FLEXIBLE INTERMEDIATE BULK CONTAINERS",
+        "hsn_code": "63053200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR THE CONVEYANCE OR PACKING OF GOODS OF PLASTICS; STOPPERS, LIDS, CAPS AND OTHER CLOSURES, OF PLASTICS",
+        "hsn_code": "3923",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR THE CONVEYANCE OR PACKING OF GOODS, OF PLASTICS; STOPPERS, LIDS, CAPS\nAND OTHER CLOSURES, OF PLASTICS - OTHER : OTHER",
+        "hsn_code": "39239090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR THE CONVEYANCE OR PACKING OF GOODS, OF PLASTICS; STOPPERS, LIDS, CAPS\nAND OTHER CLOSURES, OF PLASTICS - OTHER : ASEPTIC BAGS",
+        "hsn_code": "39239020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR THE CONVEYANCE OR PACKING OF GOODS, OF PLASTICS; STOPPERS, LIDS, CAPS\nAND OTHER CLOSURES, OF PLASTICS - OTHER : INSULATED WARE",
+        "hsn_code": "39239010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR THE CONVEYANCE OR PACKING OF GOODS, OF PLASTICS; STOPPERS, LIDS, CAPS\nAND OTHER CLOSURES, OF PLASTICS - STOPPERS, LIDS, CAPS AND OTHER CLOSURES : OTHER",
+        "hsn_code": "39235090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR THE CONVEYANCE OR PACKING OF GOODS, OF PLASTICS; STOPPERS, LIDS, CAPS\nAND OTHER CLOSURES, OF PLASTICS - STOPPERS, LIDS, CAPS AND OTHER CLOSURES : CAPS AND\nCLOSURES FOR BOTTLES",
+        "hsn_code": "39235010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR THE CONVEYANCE OR PACKING OF GOODS, OF PLASTICS; STOPPERS, LIDS, CAPS\nAND OTHER CLOSURES, OF PLASTICS - SPOOLS, COPS, BOBBINS AND SIMILAR SUPPORTS",
+        "hsn_code": "39234000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR THE CONVEYANCE OR PACKING OF GOODS, OF PLASTICS; STOPPERS, LIDS, CAPS\nAND OTHER CLOSURES, OF PLASTICS - CARBOYS, BOTTLES, FLASKS AND SIMILAR ARTICLES:\nOTHER",
+        "hsn_code": "39233090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR THE CONVEYANCE OR PACKING OF GOODS, OF PLASTICS; STOPPERS, LIDS, CAPS\nAND OTHER CLOSURES, OF PLASTICS - CARBOYS, BOTTLES, FLASKS AND SIMILAR ARTICLES:\nINSULATED WARE",
+        "hsn_code": "39233010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR THE CONVEYANCE OR PACKING OF GOODS, OF PLASTICS; STOPPERS, LIDS, CAPS\nAND OTHER CLOSURES, OF PLASTICS - SACKS AND BAGS (INCLUDING CONES)- OF OTHER\nPLASTICS: OTHER",
+        "hsn_code": "39232990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR THE CONVEYANCE OR PACKING OF GOODS, OF PLASTICS; STOPPERS, LIDS, CAPS\nAND OTHER CLOSURES, OF PLASTICS - SACKS AND BAGS (INCLUDING CONES)- OF OTHER\nPLASTICS: OF POLY (VINYL CHLORIDE)",
+        "hsn_code": "39232910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR THE CONVEYANCE OR PACKING OF GOODS, OF PLASTICS; STOPPERS, LIDS, CAPS\nAND OTHER CLOSURES, OF PLASTICS - SACKS AND BAGS (INCLUDING CONES)OF POLYMERS OF\nETHYLENE",
+        "hsn_code": "39232100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR THE CONVEYANCE OR PACKING OF GOODS, OF PLASTICS; STOPPERS, LIDS, CAPS\nAND OTHER CLOSURES, OF PLASTICS - BOXES, CASES, CRATES AND SIMILAR ARTICLES: OTHER",
+        "hsn_code": "39231090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR THE CONVEYANCE OR PACKING OF GOODS, OF PLASTICS; STOPPERS, LIDS, CAPS\nAND OTHER CLOSURES, OF PLASTICS - BOXES, CASES, CRATES AND SIMILAR ARTICLES: PACKING\nFOR ACCOMMODATING CONNECTORS",
+        "hsn_code": "39231040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR THE CONVEYANCE OR PACKING OF GOODS, OF PLASTICS; STOPPERS, LIDS, CAPS\nAND OTHER CLOSURES, OF PLASTICS - BOXES, CASES, CRATES AND SIMILAR ARTICLES:\nINSULATED WARE",
+        "hsn_code": "39231030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR THE CONVEYANCE OR PACKING OF GOODS, OF PLASTICS; STOPPERS, LIDS, CAPS\nAND OTHER CLOSURES, OF PLASTICS - BOXES, CASES, CRATES AND SIMILAR ARTICLES: WATCH -\nBOX, JEWELLERY BOX AND SIMILAR CONTAINERS OF PLASTICS",
+        "hsn_code": "39231020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR THE CONVEYANCE OR PACKING OF GOODS, OF PLASTICS; STOPPERS, LIDS, CAPS\nAND OTHER CLOSURES, OF PLASTICS - BOXES, CASES, CRATES AND SIMILAR ARTICLES: PLASTIC\nCONTAINERS FOR AUDIO OR VIDEO CASSETTES, CASSETTE TAPES, FLOPPY DISK AND SIMILAR\nARTICLES",
+        "hsn_code": "39231010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL STAPLE FIBRES, CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING",
+        "hsn_code": "5507",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL STAPLE FIBRES, CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nARTIFICIAL STAPLE FIBRES, CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING :\nOTHER",
+        "hsn_code": "55070090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL STAPLE FIBRES, CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nARTIFICIAL STAPLE FIBRES, CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING : HIGH\nWET MODULUS TOPS",
+        "hsn_code": "55070040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL STAPLE FIBRES, CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nARTIFICIAL STAPLE FIBRES, CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING :\nPOLYNOSIC TOPS",
+        "hsn_code": "55070030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL STAPLE FIBRES, CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nARTIFICIAL STAPLE FIBRES, CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING :\nVISCOSE TOPS",
+        "hsn_code": "55070020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL STAPLE FIBRES, CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nARTIFICIAL STAPLE FIBRES, CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING :\nACETATE RAYON TOPS",
+        "hsn_code": "55070010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC STAPLE FIBRES, CARDED COMBED OR OTHERWISE PROCESSED FOR SPINNING",
+        "hsn_code": "5506",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC STAPLE FIBRES, CARDED COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nOTHER : OTHER",
+        "hsn_code": "55069090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC STAPLE FIBRES, CARDED COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nOTHER : POLYPROPYLENE TOPS",
+        "hsn_code": "55069010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC STAPLE FIBRES, CARDED COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nACRYLIC OR MODACRYLIC",
+        "hsn_code": "55063000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC STAPLE FIBRES, CARDED COMBED OR OTHERWISE PROCESSED FOR SPINNING - OF\nPOLYESTERS",
+        "hsn_code": "55062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC STAPLE FIBRES, CARDED COMBED OR OTHERWISE PROCESSED FOR SPINNING - OF\nNYLON OR OTHER POLYAMIDES",
+        "hsn_code": "55061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL STAPLE FIBRES, NOT CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING",
+        "hsn_code": "5504",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL STAPLE FIBRES, NOT CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nOTHER OTHER",
+        "hsn_code": "55049090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL STAPLE FIBRES, NOT CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nOTHER HIGH WET MODULUS STAPLE FIBRE",
+        "hsn_code": "55049030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL STAPLE FIBRES, NOT CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nOTHER POLYNOSIC STAPLE FIBRE",
+        "hsn_code": "55049020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL STAPLE FIBRES, NOT CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nOTHER ACETATE RAYON STAPLE FIBRE",
+        "hsn_code": "55049010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL STAPLE FIBRES, NOT CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nOF VISCOSE RAYON",
+        "hsn_code": "55041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC STAPLE FIBRES, NOT CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING",
+        "hsn_code": "5503",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC STAPLE FIBRES, NOT CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nOTHER : OTHER",
+        "hsn_code": "55039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC STAPLE FIBRES, NOT CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nOTHER : POLYVINYL CHLORIDE STAPLE FIBRE",
+        "hsn_code": "55039020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC STAPLE FIBRES, NOT CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nOTHER : POLYVINYL STAPLE FIBRE",
+        "hsn_code": "55039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC STAPLE FIBRES, NOT CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nOF POLYPROPYLENE",
+        "hsn_code": "55034000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC STAPLE FIBRES, NOT CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nACRYLIC OR MODACRYLIC",
+        "hsn_code": "55033000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC STAPLE FIBRES, NOT CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nOF POLYESTERS",
+        "hsn_code": "55032000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC STAPLE FIBRES, NOT CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nOF NYLON OR OTHER POLYAMIDES-OTHERS",
+        "hsn_code": "55031900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC STAPLE FIBRES, NOT CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nOF NYLON OR OTHER POLYAMIDES - OF ARAMIDES",
+        "hsn_code": "55031100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC STAPLE FIBRES, NOT CARDED, COMBED OR OTHERWISE PROCESSED FOR SPINNING -\nOF NYLON OR OTHER POLYAMIDES",
+        "hsn_code": "55031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT TOW",
+        "hsn_code": "5502",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT TOW - ARTIFICIAL FILAMENT TOW : OTHER",
+        "hsn_code": "55020090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT TOW - ARTIFICIAL FILAMENT TOW : VISCOSE RAYON TOW",
+        "hsn_code": "55020020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT TOW - ARTIFICIAL FILAMENT TOW : ACETATE RAYON TOW",
+        "hsn_code": "55020010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOL, NOT CARDED OR COMBED",
+        "hsn_code": "5501",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT TOW - OTHER : OTHER",
+        "hsn_code": "55019090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT TOW - OTHER : OF POLYPROPYLENE",
+        "hsn_code": "55019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT TOW - OF POLYPROPYLENE",
+        "hsn_code": "55014000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT TOW - ACRYLIC OR MODACRYLIC",
+        "hsn_code": "55013000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT TOW - OF POLYESTERS",
+        "hsn_code": "55012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT TOW - OF NYLON OR OTHER POLYAMIDES",
+        "hsn_code": "55011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC MONOFILAMENT OF 67 DECITEX OR MORE AND OF WHICH NO CROSS-SECTIONAL DIMENSION EXCEEDS 1 MM; STRIP AND THE LIKE (FOR EXAMPLE, ARTIFICIAL STRAW) OF SYNTHETIC TEXTILE MATERIALS OF AN APPARENT WIDTH NOT EXCEEDING 5 MM",
+        "hsn_code": "5406",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAN-MADE FILAMENT YARN (OTHER THAN SEWING THREAD), PUT UP FOR RETAIL SALE\nARTIFICIAL FILAMENT YARN",
+        "hsn_code": "54062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAN-MADE FILAMENT YARN (OTHER THAN SEWING THREAD), PUT UP FOR RETAIL SALE\nSYNTHETIC FILAMENT YARN",
+        "hsn_code": "54061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAN-MADE FILAMENT YARN (OTHER THAN SEWING THREAD), PUT UP FOR RETAIL SALE,\nARTIFICIAL FILAMENT YARN",
+        "hsn_code": "54060020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAN-MADE FILAMENT YARN (OTHER THAN SEWING THREAD), PUT UP FOR RETAIL SALE,\nSYNTHETIC FILAMENT YARN",
+        "hsn_code": "54060010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Artificial monofilament of 67 decitex or more and of which no cross-sectional dimension exceeds 1 mm; strip and the like (for example, artificial straw) of artificial textile materials of an apparent width not exceeding 5 mm",
+        "hsn_code": "5405",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL MONOFILAMENT OF 67 DECITEX OR MORE AND OF WHICH NO CROSS-SECTIONAL\nDIMENSION EXCEEDS 1 MM; STRIP AND THE LIKE (FOR EXAMPLE, ARTIFICIAL STRAW) OF\nARTIFICIAL TEXTILE MATERIALS OF AN APPARENT WIDTH NOT EXCEEDING 5 MM",
+        "hsn_code": "54050000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE, INCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX",
+        "hsn_code": "5403",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED :OTHER : OTHER",
+        "hsn_code": "54034990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED :OTHER : CUPRAMMONIUM FILAMENT YARN : OTHER",
+        "hsn_code": "54034919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED :OTHER : CUPRAMMONIUM FILAMENT YARN : OF 89 DECITEX",
+        "hsn_code": "54034915",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED :OTHER : CUPRAMMONIUM FILAMENT YARN : OF 83 DECITEX",
+        "hsn_code": "54034914",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED :OTHER : CUPRAMMONIUM FILAMENT YARN : OF 67 DECITEX",
+        "hsn_code": "54034913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED :OTHER : CUPRAMMONIUM FILAMENT YARN : OF 44 DECITEX",
+        "hsn_code": "54034912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED :OTHER : CUPRAMMONIUM FILAMENT YARN : OF 33 DECITEX",
+        "hsn_code": "54034911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED :OF CELLULOSE ACETATE :OTHER",
+        "hsn_code": "54034290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED :OF CELLULOSE ACETATE :ACETATE RAYON FILAMENT YARN, 333 DECITEX",
+        "hsn_code": "54034250",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED :OF CELLULOSE ACETATE :ACETATE RAYON FILAMENT YARN, 167 DECITEX",
+        "hsn_code": "54034240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED :OF CELLULOSE ACETATE :ACETATE RAYON FILAMENT YARN, 133 DECITEX",
+        "hsn_code": "54034230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED :OF CELLULOSE ACETATE :ACETATE RAYON FILAMENT YARN, 111 DECITEX",
+        "hsn_code": "54034220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED :OF CELLULOSE ACETATE :ACETATE RAYON FILAMENT YARN, 83 DECITEX",
+        "hsn_code": "54034210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED : OF VISCOSE RAYON : OTHER",
+        "hsn_code": "54034190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED : OF VISCOSE RAYON : OF 167 DECITEX, DULL",
+        "hsn_code": "54034180",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED : OF VISCOSE RAYON : OF 167 DECITEX, BRIGHT",
+        "hsn_code": "54034170",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED : OF VISCOSE RAYON : OF 133 DECITEX, DULL",
+        "hsn_code": "54034160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED : OF VISCOSE RAYON : OF 133 DECITEX, BRIGHT",
+        "hsn_code": "54034150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED : OF VISCOSE RAYON : OF 111 DECITEX, DULL",
+        "hsn_code": "54034140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED : OF VISCOSE RAYON : OF 111 DECITEX, BRIGHT",
+        "hsn_code": "54034130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED : OF VISCOSE RAYON : OF 83 DECITEX",
+        "hsn_code": "54034120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED : OF VISCOSE RAYON : UP TO 67 DECITEX",
+        "hsn_code": "54034110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER :OTHER",
+        "hsn_code": "54033990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER : CUPRAMMONIUM\nRAYON",
+        "hsn_code": "54033910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, SINGLE :OF\nCELLULOSE ACETATE",
+        "hsn_code": "54033300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, SINGLE :OF\nVISCOSE RAYON, WITH A TWIST EXCEEDING 120 TURNS PER METRE",
+        "hsn_code": "54033200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX OTHER YARN, SINGLE :OF\nVISCOSE RAYON, UNTWISTED OR WITH A TWIST NOT EXCEEDING 120 TURNS PER METRE",
+        "hsn_code": "54033100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX TEXTURED YARN :",
+        "hsn_code": "54032000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX HIGH TENACITY YARN OF\nVISCOSE RAYON : OTHER",
+        "hsn_code": "54031090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX HIGH TENACITY YARN OF\nVISCOSE RAYON : VISCOSE RAYON TYRE YARN 1,833 DECITEX",
+        "hsn_code": "54031020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT FOR RETAIL SALE,\nINCLUDING ARTIFICIAL MONO FILAMENT OF LESS THAN 67 DECITEX HIGH TENACITY YARN OF\nVISCOSE RAYON : VISCOSE RAYON TYRE YARN 1,233 DECITEX",
+        "hsn_code": "54031010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC MONOFILAMENT OF 67 DECITEX OR MORE AND OF WHICH NO CROSS-SECTIONAL DIMENSION EXCEEDS 1 MM; STRIP AND THE LIKE (FOR EXAMPLE, ARTIFICIAL STRAW) OF SYNTHETIC TEXTILE MATERIALS OF AN APPARENT WIDTH NOT EXCEEDING 5 MM",
+        "hsn_code": "5404",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC MONOFILAMENT OF 67 DECITEX OR MORE AND OF WHICH NO CROSS-SECTIONAL\nDIMENSION EXCEEDS 1 MM; STRIP AND THE LIKE (FOR EXAMPLE, ARTIFICIAL STRAW) OF\nSYNTHETIC TEXTILE MATERIALS OF AN APPARENT WIDTH NOT EXCEEDING 5 MM OTHER : OTHER",
+        "hsn_code": "54049090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC MONOFILAMENT OF 67 DECITEX OR MORE AND OF WHICH NO CROSS-SECTIONAL\nDIMENSION EXCEEDS 1 MM; STRIP AND THE LIKE (FOR EXAMPLE, ARTIFICIAL STRAW) OF\nSYNTHETIC TEXTILE MATERIALS OF AN APPARENT WIDTH NOT EXCEEDING 5 MM OTHER : STRIP\nAND THE LIKE OF SYNTHETIC FIBRE MATERIALS",
+        "hsn_code": "54049020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC MONOFILAMENT OF 67 DECITEX OR MORE AND OF WHICH NO CROSS-SECTIONAL\nDIMENSION EXCEEDS 1 MM; STRIP AND THE LIKE (FOR EXAMPLE, ARTIFICIAL STRAW) OF\nSYNTHETIC TEXTILE MATERIALS OF AN APPARENT WIDTH NOT EXCEEDING 5 MM OTHER :\nCATGUT IMITATION OF SYNTHETIC YARN, NONSTERILE",
+        "hsn_code": "54049010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MONOFILAMENTS - NOT SPECIFIED ELSEWHERE",
+        "hsn_code": "54041990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENTS",
+        "hsn_code": "54041920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MONOFILAMENTS - CATGUT IMITATION OF NON-STERILE SYNTHETIC YARN",
+        "hsn_code": "54041910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MONOFILAMENT OF POLYPROPYLENE",
+        "hsn_code": "54041200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELASTOMETIC MONOFILAMENT",
+        "hsn_code": "54041100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC MONOFILAMENT OF 67 DECITEX OR MORE AND OF WHICH NO CROSS-SECTIONAL\nDIMENSION EXCEEDS 1 MM; STRIP AND THE LIKE (FOR EXAMPLE, ARTIFICIAL STRAW) OF\nSYNTHETIC TEXTILE MATERIALS OF AN APPARENT WIDTH NOT EXCEEDING 5 MM\nMONOFILAMENT",
+        "hsn_code": "54041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE, INCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX",
+        "hsn_code": "5402",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED : OTHER :OTHER",
+        "hsn_code": "54026990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED : OTHER :POLYTETRAFLUOROETHYLENE YARN",
+        "hsn_code": "54026950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED : OTHER :ACRYLIC FILAMENT YARN",
+        "hsn_code": "54026940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED : OTHER :POLYPROPYLENE FILAMENT YARN",
+        "hsn_code": "54026930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED : OTHER : POLYVINYL CHLORIDE FILAMENT YARN",
+        "hsn_code": "54026920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED : OTHER : POLYVINYL ACETATE FILAMENT YARN",
+        "hsn_code": "54026910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED :OF POLYESTERS",
+        "hsn_code": "54026200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, MULTIPLE\n(FOLDED) OR CABLED : OF NYLON OR OTHER POLYAMIDES",
+        "hsn_code": "54026100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, SINGLE, WITH\nA TWIST EXCEEDING 50 TURNS PER METRE : OTHER : OTHER",
+        "hsn_code": "54025990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, SINGLE, WITH\nA TWIST EXCEEDING 50 TURNS PER METRE : OTHER : POLYPROPYLENE FILAMENT YARN",
+        "hsn_code": "54025910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, SINGLE, WITH\nA TWIST EXCEEDING 50 TURNS PER METRE : OF POLYESTERS",
+        "hsn_code": "54025200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, SINGLE, WITH\nA TWIST EXCEEDING 50 TURNS PER METRE : OF NYLON OR OTHER POLYAMIDES",
+        "hsn_code": "54025100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER HIGH TENACITY YARN OF NYLON OR OTHER POLYAMIDES, NOT ELSEWHERE SPECIFIED",
+        "hsn_code": "54024900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL\nSALE, INCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, SINGLE,\nUNTWISTED OR WITH A TWIST NOT EXCEEDING 50 TURNS PER METRE : OF POLYPROPYLENE",
+        "hsn_code": "54024800",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL\nSALE, INCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, SINGLE,\nUNTWISTED OR WITH A TWIST NOT EXCEEDING 50 TURNS PER METRE : OF POLYESTERS",
+        "hsn_code": "54024700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL\nSALE, INCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, SINGLE,\nUNTWISTED OR WITH A TWIST NOT EXCEEDING 50 TURNS PER METRE : OF POLYESTERS,\nPARTIALLY ORIENTED",
+        "hsn_code": "54024600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL\nSALE, INCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, SINGLE,\nUNTWISTED OR WITH A TWIST NOT EXCEEDING 50 TURNS PER METRE : OF NYLON OR OTHER\nPOLYAMIDES",
+        "hsn_code": "54024500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, SINGLE,\nUNTWISTED OR WITH A TWIST NOT EXCEEDING 50 TURNS PER METRE : OF ELASTOMERS",
+        "hsn_code": "54024400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, SINGLE,\nUNTWISTED OR WITH A TWIST NOT EXCEEDING 50 TURNS PER METRE : OF POLYESTERS, OTHER",
+        "hsn_code": "54024300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, SINGLE,\nUNTWISTED OR WITH A TWIST NOT EXCEEDING 50 TURNS PER METRE : OF POLYESTERS,\nPARTIALLY ORIENTED",
+        "hsn_code": "54024200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX OTHER YARN, SINGLE,\nUNTWISTED OR WITH A TWIST NOT EXCEEDING 50 TURNS PER METRE : OF NYLON OR OTHER\nPOLYAMIDES",
+        "hsn_code": "54024100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX TEXTURED YARN :OTHER\n:OTHER",
+        "hsn_code": "54023990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX TEXTURED YARN :OTHER\n:ACRYLIC FILAMENT YARN",
+        "hsn_code": "54023920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX TEXTURED YARN :OTHER :\nPOLYPROPYLENE FILAMENT YARN",
+        "hsn_code": "54023910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX TEXTURED YARN :OF\nPOLYPROPYLENE",
+        "hsn_code": "54023400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX TEXTURED YARN :OF\nPOLYESTERS",
+        "hsn_code": "54023300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX TEXTURED YARN :OF NYLON\nOR OTHER POLYAMIDES, MEASURING PER SINGLE YARN MORE THAN 50 TEX",
+        "hsn_code": "54023200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX TEXTURED YARN : OF NYLON\nOR OTHER POLYAMIDES, MEASURING PER SINGLE YARN NOT MORE THAN 50 TEX",
+        "hsn_code": "54023100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX HIGH TENACITY YARN OF\nPOLYESTERS :OTHER",
+        "hsn_code": "54022090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX HIGH TENACITY YARN OF\nPOLYESTERS :OF TERYLENE DACRON",
+        "hsn_code": "54022010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER HIGH TENACITY YARN OF NYLON OR OTHER POLYAMIDES",
+        "hsn_code": "54021990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER HIGH TENACITY YARN OF NYLON OR OTHER POLYAMIDES",
+        "hsn_code": "54021910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HIGH TENACITY YARN OF NYLON OR OTHER POLYAMIDES",
+        "hsn_code": "54021110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES : OTHER",
+        "hsn_code": "54021090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC FILAMENT YARN (OTHER THAN SEWING THREAD), NOT PUT UP FOR RETAIL SALE,\nINCLUDING SYNTHETIC MONOFILAMENT OF LESS THAN 67 DECITEX HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES : NYLON TYRE YARN",
+        "hsn_code": "54021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLANS AND DRAWINGS FORARCHITECTURAL,ENGINEERING,INDUSTRIAL,\nCOMMERCIAL,TOPOGRAPHICAL OR SIMILARPURPOSES,BEING ORIGINALS RAWN BY HAND;\nHANDWRITTENTEXTS; PHOTOGRAPHIC REPRODUCTIONS NSENSITISED PAPER AND CARBON\nCOPIES OF THE FOREGOING",
+        "hsn_code": "49060000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENVELOPES, LETTER CARDS, PLAIN POSTCARDS AND CORRESPONDENCE CARDS, OF PAPER OR\nPAPERBOARD; BOXES, POUCHES, WALLETS AND WRITING COMPENDIUMS, OF PAPER OR\nPAPERBOARD, CONTAINING AN ASSORTMENT OF PAPER STATIONERY - BOXES, POUCHES,\nWALLETS AND WRITING COMPENDIUMS, OF PAPER OR PAPERBOARD, CONTAINING AN\nASSORTMENT OF PAPER STATIONERY: WRITING BLOCKS",
+        "hsn_code": "48173010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ENVELOPES, LETTER CARDS, PLAIN POSTCARDS AND CORRESPONDENCE CARDS, OF PAPER OR\nPAPERBOARD; BOXES, POUCHES, WALLETS AND WRITING COMPENDIUMS, OF PAPER OR\nPAPERBOARD, CONTAINING AN ASSORTMENT OF PAPER STATIONERY - BOXES, POUCHES,\nWALLETS AND WRITING COMPENDIUMS, OF PAPER OR PAPERBOARD, CONTAINING AN\nASSORTMENT OF PAPER STATIONERY: OTHER",
+        "hsn_code": "48173090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ENVELOPES, LETTER CARDS, PLAIN POSTCARDS AND CORRESPONDENCE CARDS, OF PAPER OR\nPAPERBOARD; BOXES, POUCHES, WALLETS AND WRITING COMPENDIUMS, OF PAPER OR\nPAPERBOARD, CONTAINING AN ASSORTMENT OF PAPER STATIONERY - ENVELOPES",
+        "hsn_code": "48171000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENVELOPES, LETTER CARDS, PLAIN POSTCARDS AND CORRESPONDENCE CARDS, OF PAPER OR\nPAPERBOARD; BOXES, POUCHES, WALLETS AND WRITING COMPENDIUMS, OF PAPER OR\nPAPERBOARD, CONTAINING AN ASSORTMENT OF PAPER STATIONERY - LETTER CARDS, PLAIN\nPOSTCARDS AND CORRESPONDENCE CARDS",
+        "hsn_code": "48172000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENVELOPES, LETTER CARDS, PLAIN POSTCARDS AND CORRESPONDENCE CARDS, OF PAPER OR PAPERBOARD; BOXES, POUCHES, WALLETS AND WRITING COMPENDIUMS, OF PAPER OR PAPERBOARD, CONTAINING AN ASSORTMENT OF PAPER STATIONERY",
+        "hsn_code": "4817",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PARCHMENT, GREASEPROOF PAPERS, TRACING PAPERS AND GLASSINE AND OTHER\nGLAZED TRANSPARENT OR TRANSLUCENT PAPERS, IN ROLLS OR SHEETS - GLASSINE AND OTHER\nGLAZED TRANSPARENT OR TRANSLUCENT PAPERS : GLASSINE PAPERS",
+        "hsn_code": "48064010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PARCHMENT, GREASEPROOF PAPERS, TRACING PAPERS AND GLASSINE AND OTHER\nGLAZED TRANSPARENT OR TRANSLUCENT PAPERS, IN ROLLS OR SHEETS GREASEPROOF PAPERS",
+        "hsn_code": "48062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PARCHMENT, GREASEPROOF PAPERS, TRACING PAPERS AND GLASSINE AND OTHER\nGLAZED TRANSPARENT OR TRANSLUCENT PAPERS, IN ROLLS OR SHEETS VEGETABLE PARCHMENT",
+        "hsn_code": "48061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PARCHMENT, GREASEPROOF PAPERS, TRACING PAPERS AND GLASSINE AND OTHER\nGLAZED TRANSPARENT OR TRANSLUCENT PAPERS, IN ROLLS OR SHEETS TRACING PAPERS",
+        "hsn_code": "48063000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PARCHMENT, GREASEPROOF PAPERS, TRACING PAPERS AND GLASSINE AND OTHER\nGLAZED TRANSPARENT OR TRANSLUCENT PAPERS, IN ROLLS OR SHEETS - GLASSINE AND OTHER\nGLAZED TRANSPARENT OR TRANSLUCENT PAPERS : OTHER",
+        "hsn_code": "48064090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PARCHMENT, GREASEPROOF PAPERS, TRACING PAPERS AND GLASSINE AND OTHER GLAZED TRANSPARENT OR TRANSLUCENT PAPERS, IN ROLLS OR SHEETS",
+        "hsn_code": "4806",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TALL OIL, WHETHER OR NOT REFINED",
+        "hsn_code": "38030000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS FOR ORAL OR DENTAL HYGIENE, INCLUDING DENTURE FIXATIVE PASTES AND POWDERS; YARN USED TO CLEAN BETWEEN THE TEETH (DENTAL FLOSS), IN INDIVIDUAL RETAIL PACKAGES",
+        "hsn_code": "3306",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS FOR ORAL OR DENTAL HYGIENE, INCLUDING DENTURE FIXATIVE PASTES AND\nPOWDERS; YARN USED TO CLEAN BETWEEN THE TEETH (DENTAL FLOSS), IN INDIVIDUAL RETAIL\nPACKAGES OTHER",
+        "hsn_code": "33069000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS FOR ORAL OR DENTAL HYGIENE, INCLUDING DENTURE FIXATIVE PASTES AND\nPOWDERS; YARN USED TO CLEAN BETWEEN THE TEETH (DENTAL FLOSS), IN INDIVIDUAL RETAIL\nPACKAGES YARN USED TO CLEAN BETWEEN THE TEETH (DENTAL FLOSS)",
+        "hsn_code": "33062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS FOR ORAL OR DENTAL HYGIENE, INCLUDING DENTURE FIXATIVE PASTES AND\nPOWDERS; YARN USED TO CLEAN BETWEEN THE TEETH (DENTAL FLOSS), IN INDIVIDUAL RETAIL\nPACKAGES DENTIFRICES: OTHER",
+        "hsn_code": "33061090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS FOR ORAL OR DENTAL HYGIENE, INCLUDING DENTURE FIXATIVE PASTES AND\nPOWDERS; YARN USED TO CLEAN BETWEEN THE TEETH (DENTAL FLOSS), IN INDIVIDUAL RETAIL\nPACKAGES DENTIFRICES: IN PASTE",
+        "hsn_code": "33061020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS FOR ORAL OR DENTAL HYGIENE, INCLUDING DENTURE FIXATIVE PASTES AND\nPOWDERS; YARN USED TO CLEAN BETWEEN THE TEETH (DENTAL FLOSS), IN INDIVIDUAL RETAIL\nPACKAGES DENTIFRICES: IN POWDER",
+        "hsn_code": "33061010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED DRIERS",
+        "hsn_code": "32110000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GRANITE, PORPHYRY, BASALT, SANDSTONE AND OTHER MONUMENTAL OR BUILDING STONE,\nWHETHER OR NOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO\nBLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE) SHAPE - GRANITE : MERELY CUT, BY\nSAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE)\nSHAPE",
+        "hsn_code": "25161200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MARBLE, TRAVERTINE, ECAUSSINE AND OTHER CALCAREOUS MONUMENTAL OR BUILDING\nSTONE OF AN APPARENT SPECIFIC GRAVITY OF 2.5 OR MORE, AND ALABASTER, WHETHER OR\nNOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS\nOF A RECTANGULAR (INCLUDING SQUARE) SHAPE - MARBLE AND TRAVERTINE: MERELY CUT, BY\nSAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE)\nSHAPE: OTHER",
+        "hsn_code": "25151290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MARBLE, TRAVERTINE, ECAUSSINE AND OTHER CALCAREOUS MONUMENTAL OR BUILDING\nSTONE OF AN APPARENT SPECIFIC GRAVITY OF 2.5 OR MORE, AND ALABASTER, WHETHER OR\nNOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS\nOF A RECTANGULAR (INCLUDING SQUARE) SHAPE - MARBLE AND TRAVERTINE: MERELY CUT, BY\nSAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE)\nSHAPE: SLABS",
+        "hsn_code": "25151220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ICECREAM AND OTHER EDIBLE ICE, WHETHER OR NOT CONTAINING COCOA",
+        "hsn_code": "21050000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "EXTRACTS, ESSENCES AND CONCENTRATES, OF COFFEE, TEA OR MATE AND PREPARATIONSWITH\nA BASIS OF THESE PRODUCTS OR WITH ABASIS OF COFFEE, TEA OR MATE; ROASTEDCHICORY\nAND OTHER ROASTED COFFEESUBSTITUTES, AND EXTRACTS, ESSENCESAND CONCENTRATES\nTHEREOF - EXTRACTS, ESSENCES AND CONCENTRATES, OF TEAOR MATE, AND PREPARATIONS\nWITH A BASIS OFTHESE EXTRACTS, ESSENCES OR CONCENTRATES ORWITH A BASIS OF TEA OR\nMATE : OTHER",
+        "hsn_code": "21012090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "EXTRACTS, ESSENCES AND CONCENTRATES, OF COFFEE, TEA OR MATE AND PREPARATIONSWITH\nA BASIS OF THESE PRODUCTS OR WITH ABASIS OF COFFEE, TEA OR MATE; ROASTEDCHICORY\nAND OTHER ROASTED COFFEESUBSTITUTES, AND EXTRACTS, ESSENCESAND CONCENTRATES\nTHEREOF - EXTRACTS, ESSENCES AND CONCENTRATES, OF TEAOR MATE, AND PREPARATIONS\nWITH A BASIS OFTHESE EXTRACTS, ESSENCES OR CONCENTRATES ORWITH A BASIS OF TEA OR\nMATE : TEA AROMA",
+        "hsn_code": "21012030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "EXTRACTS, ESSENCES AND CONCENTRATES, OF COFFEE, TEA OR MATE AND PREPARATIONSWITH\nA BASIS OF THESE PRODUCTS OR WITH ABASIS OF COFFEE, TEA OR MATE; ROASTEDCHICORY\nAND OTHER ROASTED COFFEESUBSTITUTES, AND EXTRACTS, ESSENCESAND CONCENTRATES\nTHEREOF - EXTRACTS, ESSENCES AND CONCENTRATES, OF TEAOR MATE, AND PREPARATIONS\nWITH A BASIS OFTHESE EXTRACTS, ESSENCES OR CONCENTRATES ORWITH A BASIS OF TEA OR\nMATE : QUICK BREWING BLACK TEA",
+        "hsn_code": "21012020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "EXTRACTS, ESSENCES AND CONCENTRATES, OF COFFEE, TEA OR MATE AND PREPARATIONSWITH\nA BASIS OF THESE PRODUCTS OR WITH ABASIS OF COFFEE, TEA OR MATE; ROASTEDCHICORY\nAND OTHER ROASTED COFFEESUBSTITUTES, AND EXTRACTS, ESSENCESAND CONCENTRATES\nTHEREOF - EXTRACTS, ESSENCES AND CONCENTRATES, OF TEAOR MATE, AND PREPARATIONS\nWITH A BASIS OFTHESE EXTRACTS, ESSENCES OR CONCENTRATES ORWITH A BASIS OF TEA OR\nMATE : INSTANT TEA",
+        "hsn_code": "21012010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MEAT AND EDIBLEMEAT OFFAL, SALTED, INBRINE, DRIED OR SMOKED;EDIBLEFLOURS AND\nMEALS OF MEAT OR MEAT OFFALMEAT OF SWINE : BELLIES(STREAKY) AND CUTS THERE OF",
+        "hsn_code": "2101200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "EXTRACTS, ESSENCES AND CONCENTRATES, OF COFFEE, TEA OR MATE AND PREPARATIONSWITH\nA BASIS OF THESE PRODUCTS OR WITH ABASIS OF COFFEE, TEA OR MATE; ROASTEDCHICORY\nAND OTHER ROASTED COFFEESUBSTITUTES, AND EXTRACTS, ESSENCESAND CONCENTRATES\nTHEREOF - EXTRACTS, ESSENCES AND CONCENTRATES OF COFFEE, AND PREPARATIONS WITH A\nBASIS OF THESE EXTRACTS, ESSENCES OR CONCENTRATES OR WITHA BASIS OF COFFEE :\nPREPARATIONS WITH BASIS OF EXTRACTS, ESSENCES, CONCENTRATES OR WITH A BASIS OF\nCOFFEE",
+        "hsn_code": "21011200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "EXTRACTS, ESSENCES AND CONCENTRATES, OF COFFEE, TEA OR MATE AND PREPARATIONSWITH\nA BASIS OF THESE PRODUCTS OR WITH ABASIS OF COFFEE, TEA OR MATE; ROASTEDCHICORY\nAND OTHER ROASTED COFFEESUBSTITUTES, AND EXTRACTS, ESSENCESAND CONCENTRATES\nTHEREOF - EXTRACTS, ESSENCES AND CONCENTRATES OF COFFEE, AND PREPARATIONS WITH A\nBASIS OF THESE EXTRACTS, ESSENCES OR CONCENTRATES OR WITHA BASIS OF COFFEE :\nEXTRACTS, ESSENCES AND CONCENTRATES : OTHER",
+        "hsn_code": "21011190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "EXTRACTS, ESSENCES AND CONCENTRATES, OF COFFEE, TEA OR MATE AND PREPARATIONSWITH\nA BASIS OF THESE PRODUCTS OR WITH ABASIS OF COFFEE, TEA OR MATE; ROASTEDCHICORY\nAND OTHER ROASTED COFFEESUBSTITUTES, AND EXTRACTS, ESSENCESAND CONCENTRATES\nTHEREOF - EXTRACTS, ESSENCES AND CONCENTRATES OF COFFEE, AND PREPARATIONS WITH A\nBASIS OF THESE EXTRACTS, ESSENCES OR CONCENTRATES OR WITHA BASIS OF COFFEE :\nEXTRACTS, ESSENCES AND CONCENTRATES : COFFEE AROMA",
+        "hsn_code": "21011130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "EXTRACTS, ESSENCES AND CONCENTRATES, OF COFFEE, TEA OR MATE AND PREPARATIONSWITH\nA BASIS OF THESE PRODUCTS OR WITH ABASIS OF COFFEE, TEA OR MATE; ROASTEDCHICORY\nAND OTHER ROASTED COFFEESUBSTITUTES, AND EXTRACTS, ESSENCESAND CONCENTRATES\nTHEREOF - EXTRACTS, ESSENCES AND CONCENTRATES OF COFFEE, AND PREPARATIONS WITH A\nBASIS OF THESE EXTRACTS, ESSENCES OR CONCENTRATES OR WITHA BASIS OF COFFEE :\nEXTRACTS, ESSENCES AND CONCENTRATES : INSTANT COFFEE, NOT FLOVERED",
+        "hsn_code": "21011120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "EXTRACTS, ESSENCES AND CONCENTRATES, OF COFFEE, TEA OR MATE AND PREPARATIONSWITH\nA BASIS OF THESE PRODUCTS OR WITH ABASIS OF COFFEE, TEA OR MATE; ROASTEDCHICORY\nAND OTHER ROASTED COFFEESUBSTITUTES, AND EXTRACTS, ESSENCESAND CONCENTRATES\nTHEREOF - EXTRACTS, ESSENCES AND CONCENTRATES OF COFFEE, AND PREPARATIONS WITH A\nBASIS OF THESE EXTRACTS, ESSENCES OR CONCENTRATES OR WITHA BASIS OF COFFEE :\nEXTRACTS, ESSENCES AND CONCENTRATES : INSTANT COFFEE, FLAVOURED",
+        "hsn_code": "21011110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED FOODS OBTAINED BY THE SWELLING OR ROASTING OF CEREALS OR CEREAL PRODUCTS (FOR EXAMPLE, CORN FLAKES); CEREALS (OTHER THAN MAIZE (CORN) ) IN GRAIN FORM OR IN THE FORM OF FLAKES OR OTHER WORKED GRAINS (EXCEPT FLOUR, GROATS AND MEAL), PRE-COOKED, OR OTHERWISE PREPARED, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "1904",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED FOODS OBTAINED BY THE SWELLING OR ROASTING OF CEREALS OR CEREAL\nPRODUCTS (FOR EXAMPLE, CORN FLAKES); CEREALS [OTHER THAN MAIZE (CORN) ] IN GRAIN\nFORM OR IN THE FORM OF FLAKES OR OTHER WORKED GRAINS (EXCEPT FLOUR, GROATS AND\nMEAL), PRE - COOKED OR OTHERWISE PREPARED, NOT ELSEWHERE SPECIFIED OR INCLUDED\nOTHER",
+        "hsn_code": "19049000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED FOODS OBTAINED BY THE SWELLING OR ROASTING OF CEREALS OR CEREAL\nPRODUCTS (FOR EXAMPLE, CORN FLAKES); CEREALS [OTHER THAN MAIZE (CORN) ] IN GRAIN\nFORM OR IN THE FORM OF FLAKES OR OTHER WORKED GRAINS (EXCEPT FLOUR, GROATS AND\nMEAL), PRE - COOKED OR OTHERWISE PREPARED, NOT ELSEWHERE SPECIFIED OR INCLUDED\nBULGUR WHEAT",
+        "hsn_code": "19043000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED FOODS OBTAINED BY THE SWELLING OR ROASTING OF CEREALS OR CEREAL\nPRODUCTS (FOR EXAMPLE, CORN FLAKES); CEREALS [OTHER THAN MAIZE (CORN) ] IN GRAIN\nFORM OR IN THE FORM OF FLAKES OR OTHER WORKED GRAINS (EXCEPT FLOUR, GROATS AND\nMEAL), PRE - COOKED OR OTHERWISE PREPARED, NOT ELSEWHERE SPECIFIED OR INCLUDED\nPREPARED FOODS OBTAINED FROM UNROASTED CEREAL FLAKES OR FROM MIXTURES OF\nUNROASTED CEREAL FLAKES AND ROASTED CEREAL FLAKES OR SWELLED CEREALS",
+        "hsn_code": "19042000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED FOODS OBTAINED BY THE SWELLING OR ROASTING OF CEREALS OR CEREAL\nPRODUCTS (FOR EXAMPLE, CORN FLAKES); CEREALS [OTHER THAN MAIZE (CORN) ] IN GRAIN\nFORM OR IN THE FORM OF FLAKES OR OTHER WORKED GRAINS (EXCEPT FLOUR, GROATS AND\nMEAL), PRE - COOKED OR OTHERWISE PREPARED, NOT ELSEWHERE SPECIFIED OR INCLUDED -\nPREPARED FOODS OBTAINED BY THE SWELLING OR ROASTING OF CEREALS OR CEREAL\nPRODUCTS : OTHER",
+        "hsn_code": "19041090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED FOODS OBTAINED BY THE SWELLING OR ROASTING OF CEREALS OR CEREAL\nPRODUCTS (FOR EXAMPLE, CORN FLAKES); CEREALS [OTHER THAN MAIZE (CORN) ] IN GRAIN\nFORM OR IN THE FORM OF FLAKES OR OTHER WORKED GRAINS (EXCEPT FLOUR, GROATS AND\nMEAL), PRE - COOKED OR OTHERWISE PREPARED, NOT ELSEWHERE SPECIFIED OR INCLUDED -\nPREPARED FOODS OBTAINED BY THE SWELLING OR ROASTING OF CEREALS OR CEREAL\nPRODUCTS : BULGUR WHEAT",
+        "hsn_code": "19041030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED FOODS OBTAINED BY THE SWELLING OR ROASTING OF CEREALS OR CEREAL\nPRODUCTS (FOR EXAMPLE, CORN FLAKES); CEREALS [OTHER THAN MAIZE (CORN) ] IN GRAIN\nFORM OR IN THE FORM OF FLAKES OR OTHER WORKED GRAINS (EXCEPT FLOUR, GROATS AND\nMEAL), PRE - COOKED OR OTHERWISE PREPARED, NOT ELSEWHERE SPECIFIED OR INCLUDED -\nPREPARED FOODS OBTAINED BY THE SWELLING OR ROASTING OF CEREALS OR CEREAL\nPRODUCTS : PAWS, MUDI AND THE LIKE",
+        "hsn_code": "19041020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED FOODS OBTAINED BY THE SWELLING OR ROASTING OF CEREALS OR CEREAL\nPRODUCTS (FOR EXAMPLE, CORN FLAKES); CEREALS [OTHER THAN MAIZE (CORN) ] IN GRAIN\nFORM OR IN THE FORM OF FLAKES OR OTHER WORKED GRAINS (EXCEPT FLOUR, GROATS AND\nMEAL), PRE - COOKED OR OTHERWISE PREPARED, NOT ELSEWHERE SPECIFIED OR INCLUDED -\nPREPARED FOODS OBTAINED BY THE SWELLING OR ROASTING OF CEREALS OR CEREAL\nPRODUCTS : CORN FLAKES",
+        "hsn_code": "19041010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MALT EXTRACT; FOOD PREPARATIONS OF FLOUR, GROATS, MEAL, STARCH OR MALT EXTRACT,\nNOT CONTAINING COCOA OR CONTAINING LESS THAN 40% BY WEIGHT OF COCOA CALCULATED\nON A TOTALLY DEFATTED BASIS, NOT ELSEWHERE SPECIFIED OR INCLUDED; FOOD\nPREPARATIONS OF GOODS OF HEADINGS 0401 TO 0404, NOT CONTAINING COCOA OR\nCONTAINING LESS THAN 5% BY WEIGHT OF COCOA CALCULATED ON A TOTALLY DEFATTED\nBASIS, NOT ELSEWHERE SPECIFIED OR INCLUDED - MIXES AND DOUGHS FOR THE PREPARATION\nOF BAKERS WARES OF HEADING 1905",
+        "hsn_code": "19012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MALT EXTRACT; FOOD PREPARATIONS OF FLOUR, GROATS, MEAL, STARCH OR MALT EXTRACT, NOT CONTAINING COCOA OR CONTAINING LESS THAN 40% BY WEIGHT OF COCOA CALCULATED ON A TOTALLY DEFATTED BASIS, NOT ELSEWHERE SPECIFIED OR INCLUDED; FOOD PREPARATIONS OF GOODS OF HEADINGS 0401 TO 0404, NOT CONTAINING COCOA OR CONTAINING LESS THAN 5% BY WEIGHT OF COCOA CALCULATED ON A TOTALLY DEFATTED BASIS NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "1901",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MALT EXTRACT; FOOD PREPARATIONS OF FLOUR, GROATS, MEAL, STARCH OR MALT EXTRACT,\nNOT CONTAINING COCOA OR CONTAINING LESS THAN 40% BY WEIGHT OF COCOA CALCULATED\nON A TOTALLY DEFATTED BASIS, NOT ELSEWHERE SPECIFIED OR INCLUDED; FOOD\nPREPARATIONS OF GOODS OF HEADINGS 0401 TO 0404, NOT CONTAINING COCOA OR\nCONTAINING LESS THAN 5% BY WEIGHT OF COCOA CALCULATED ON A TOTALLY DEFATTED\nBASIS, NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER : OTHER",
+        "hsn_code": "19019090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MALT EXTRACT; FOOD PREPARATIONS OF FLOUR, GROATS, MEAL, STARCH OR MALT EXTRACT,\nNOT CONTAINING COCOA OR CONTAINING LESS THAN 40% BY WEIGHT OF COCOA CALCULATED\nON A TOTALLY DEFATTED BASIS, NOT ELSEWHERE SPECIFIED OR INCLUDED; FOOD\nPREPARATIONS OF GOODS OF HEADINGS 0401 TO 0404, NOT CONTAINING COCOA OR\nCONTAINING LESS THAN 5% BY WEIGHT OF COCOA CALCULATED ON A TOTALLY DEFATTED\nBASIS, NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER : MALT EXTRACT",
+        "hsn_code": "19019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MALT EXTRACT; FOOD PREPARATIONS OF FLOUR, GROATS, MEAL, STARCH OR MALT EXTRACT,\nNOT CONTAINING COCOA OR CONTAINING LESS THAN 40% BY WEIGHT OF COCOA CALCULATED\nON A TOTALLY DEFATTED BASIS, NOT ELSEWHERE SPECIFIED OR INCLUDED; FOOD\nPREPARATIONS OF GOODS OF HEADINGS 0401 TO 0404, NOT CONTAINING COCOA OR\nCONTAINING LESS THAN 5% BY WEIGHT OF COCOA CALCULATED ON A TOTALLY DEFATTED\nBASIS, NOT ELSEWHERE SPECIFIED OR INCLUDED - PREPARATIONS FOR INFANT USE, PUT UP FOR\nRETAIL SALE : OTHER",
+        "hsn_code": "19011090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MALT EXTRACT; FOOD PREPARATIONS OF FLOUR, GROATS, MEAL, STARCH OR MALT EXTRACT,\nNOT CONTAINING COCOA OR CONTAINING LESS THAN 40% BY WEIGHT OF COCOA CALCULATED\nON A TOTALLY DEFATTED BASIS, NOT ELSEWHERE SPECIFIED OR INCLUDED; FOOD\nPREPARATIONS OF GOODS OF HEADINGS 0401 TO 0404, NOT CONTAINING COCOA OR\nCONTAINING LESS THAN 5% BY WEIGHT OF COCOA CALCULATED ON A TOTALLY DEFATTED\nBASIS, NOT ELSEWHERE SPECIFIED OR INCLUDED - PREPARATIONS FOR INFANT USE, PUT UP FOR\nRETAIL SALE : MALTED MILK (INCLUDING POWDER)",
+        "hsn_code": "19011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLYCEROL, CRUDE; GLYCEROL WATERS AND GLYCEROL LYES",
+        "hsn_code": "15200000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MARGARINE; EDIBLE MIXTURE OR PREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR\nOF FRACTIONS OF DIFFERENT FATS OR OILS OF THIS CHAPTER, OTHER THAN EDIBLE FATS OR\nOILS OR THEIR FRACTIONS OF HEADING 1516 MARGARINE, EXCLUDING LIQUID MARGARINE : OF\nANIMAL ORIGIN",
+        "hsn_code": "15171010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MARGARINE; EDIBLE MIXTURE OR PREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR\nOF FRACTIONS OF DIFFERENT FATS OR OILS OF THIS CHAPTER, OTHER THAN EDIBLE FATS OR\nOILS OR THEIR FRACTIONS OF HEADING 1516 MARGARINE, EXCLUDING LIQUID MARGARINE : OF\nVEGETABLE ORIGIN : EDIBLE GRADE",
+        "hsn_code": "15171021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MARGARINE; EDIBLE MIXTURE OR PREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR\nOF FRACTIONS OF DIFFERENT FATS OR OILS OF THIS CHAPTER, OTHER THAN EDIBLE FATS OR\nOILS OR THEIR FRACTIONS OF HEADING 1516 MARGARINE, EXCLUDING LIQUID MARGARINE : OF\nVEGETABLE ORIGIN : LINOXYN",
+        "hsn_code": "15171022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MARGARINE; EDIBLE MIXTURE OR PREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR\nOF FRACTIONS OF DIFFERENT FATS OR OILS OF THIS CHAPTER, OTHER THAN EDIBLE FATS OR\nOILS OR THEIR FRACTIONS OF HEADING 1516 MARGARINE, EXCLUDING LIQUID MARGARINE : OF\nVEGETABLE ORIGIN : OTHER",
+        "hsn_code": "15171029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER : INDIAN KATHA",
+        "hsn_code": "14049050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER : BIDI WRAPPER\nLEAVES (TENDU)",
+        "hsn_code": "14049010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNITURE AND PARTS THEREOF - FURNITURE OF OTHER MATERIALS, INCLUDING CANE,\nOSIER, BAMBOO OR SIMILAR MATERIALS: FURNITURE OF WICKER WORK OR BAMBOO",
+        "hsn_code": "94038010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNITURE AND PARTS THEREOF - FURNITURE OF OTHER MATERIALS, INCLUDING CANE,\nOSIER, BAMBOO OR SIMILAR MATERIALS: OTHER",
+        "hsn_code": "94038090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDED - Chandeliers and other electric ceiling or wall lighting\nfittings, excluding those of a kind used for lighting public open spaces or thorough fares: Hanging\nlamps, complete fittings",
+        "hsn_code": "94051010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDED - Chandeliers and other electric ceiling or wall lighting\nfittings, excluding those of a kind used for lighting public open spaces or thorough fares: Wall\nlamps",
+        "hsn_code": "94051020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDED - Chandeliers and other electric ceiling or wall lighting\nfittings, excluding those of a kind used for lighting public open spaces or thorough fares: Other",
+        "hsn_code": "94051090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF ALUMINIUM - OTHER : OTHER : OTHER",
+        "hsn_code": "76169990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF IRON OR STEEL - OTHER : OTHER : OTHER",
+        "hsn_code": "73269099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF GLASS OTHER ARTICLES OF GLASS: OTHER",
+        "hsn_code": "70200090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GLASS BEADS, IMITATION PEARLS, IMITATION PRECIOUS OR SEMI-PRECIOUS STONES AND\nSIMILAR GLASS SMALLWARES, AND ARTICLES THEREOF OTHER THAN IMITATION JEWELLERY,\nGLASS EYES OTHER THAN PROSTHETIC ARTICLES; STATUETTES AND OTHER ORNAMENTS OF\nLAMP-WORKED GLASS, OTHER THAN IMITATION JEWELLERY; GLASS MICROSPHERES NOT\nEXCEEDING 1 MM IN DIAMETER - OTHER: GLASS STATUES",
+        "hsn_code": "70189010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GLASS MIRRORS, WHETHER OR NOT FRAMED, INCLUDING REAR-VIEW MIRRORS - OTHER :\nFRAMED",
+        "hsn_code": "70099200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "STATUETTES AND OTHER ORNAMENTAL CERAMIC ARTICLES - OTHER",
+        "hsn_code": "69139000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF STONE OR OF OTHER MINERAL SUBSTANCES (INCLUDING CARBON FIBRES, ARTICLES\nOF CARBON FIBRES AND ARTICLES OF PEAT), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER\nARTICLES: OTHER: OTHER",
+        "hsn_code": "68159990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CASKS, BARRELS, VATS, TUBS AND OTHER COOPERS PRODUCTS AND PARTS THEREOF, OF WOOD,\nINCLUDING STAVES - CASKS, BARRELS, VATS, TUBS AND OTHER COOPERS PRODUCTS AND PARTS\nTHEREOF, OF WOOD, INCLUDING STAVES: CASKS, BARRELS, VATS AND TUBS",
+        "hsn_code": "44160010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CASKS, BARRELS, VATS, TUBS AND OTHER COOPERS PRODUCTS AND PARTS THEREOF, OF WOOD,\nINCLUDING STAVES - CASKS, BARRELS, VATS, TUBS AND OTHER COOPERS PRODUCTS AND PARTS\nTHEREOF, OF WOOD, INCLUDING STAVES: OTHER COOPERS PRODUCTS",
+        "hsn_code": "44160020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CASKS, BARRELS, VATS, TUBS AND OTHER COOPERS PRODUCTS AND PARTS THEREOF, OF WOOD,\nINCLUDING STAVES - PARTS (OF WOOD): RIVEN OR SAWN STAVES OF WOOD NOT FURTHER\nPREPARED",
+        "hsn_code": "44160091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CASKS, BARRELS, VATS, TUBS AND OTHER COOPERS PRODUCTS AND PARTS THEREOF, OF WOOD,\nINCLUDING STAVES - PARTS (OF WOOD): OTHER",
+        "hsn_code": "44160099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CASKS, BARRELS, VATS, TUBS AND OTHER COOPERS’ PRODUCTS AND PARTS THEREOF, OF WOOD, INCLUDING STAVES",
+        "hsn_code": "4416",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - ARTICLES OF A KIND NORMALLY CARRIED IN THE POCKET OR IN THE HANDBAG : OTHER :\nJEWELLERY BOX",
+        "hsn_code": "42023910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - ARTICLES OF A KIND NORMALLY CARRIED IN THE POCKET OR IN THE HANDBAG : OTHER :\nOTHER",
+        "hsn_code": "42023990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - ARTICLES OF A KIND NORMALLY CARRIED IN THE POCKET OR IN THE HANDBAG : WITH\nOUTER SURFACE OF PLASTIC SHEETING OR OF TEXTILE MATERIALS : JEWELLERY BOX",
+        "hsn_code": "42023210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - ARTICLES OF A KIND NORMALLY CARRIED IN THE POCKET OR IN THE HANDBAG : WITH\nOUTER SURFACE OF PLASTIC SHEETING OR OF TEXTILE MATERIALS : OTHER",
+        "hsn_code": "42023290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - ARTICLES OF A KIND NORMALLY CARRIED IN THE POCKET OR IN THE HANDBAG : WITH\nOUTER SURFACE OF LEATHER, OF COMPOSITION LEATHER OR OF PATENT LEATHER : OTHER",
+        "hsn_code": "42023190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - ARTICLES OF A KIND NORMALLY CARRIED IN THE POCKET OR IN THE HANDBAG : WITH\nOUTER SURFACE OF LEATHER, OF COMPOSITION LEATHER OR OF PATENT LEATHER : JEWELLERY\nBOX",
+        "hsn_code": "42023110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - HAND-BAGS, WHETHER OR NOT WITH SHOULDER STRAP, INCLUDING THOSE WITHOUT\nHANDLE : OTHER HAND BAGS OF OTHER MATERIALS EXCLUDING WICKER-WORK OR BASKET\nWORK",
+        "hsn_code": "42022910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - HAND-BAGS, WHETHER OR NOT WITH SHOULDER STRAP, INCLUDING THOSE WITHOUT\nHANDLE : OTHER OTHER",
+        "hsn_code": "42022990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - HAND-BAGS, WHETHER OR NOT WITH SHOULDER STRAP, INCLUDING THOSE WITHOUT\nHANDLE : WITH OUTER SURFACE OF PLASTIC SHEETING OR OF TEXTILE MATERIALS : HAND-BAGS\nAND SHOPPING BAGS, OF ARTIFICIAL PLASTIC MATERIAL",
+        "hsn_code": "42022210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - HAND-BAGS, WHETHER OR NOT WITH SHOULDER STRAP, INCLUDING THOSE WITHOUT\nHANDLE : WITH OUTER SURFACE OF PLASTIC SHEETING OR OF TEXTILE MATERIALS : VANITY\nBAGS",
+        "hsn_code": "42022240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - HAND-BAGS, WHETHER OR NOT WITH SHOULDER STRAP, INCLUDING THOSE WITHOUT\nHANDLE : WITH OUTER SURFACE OF PLASTIC SHEETING OR OF TEXTILE MATERIALS : OTHER",
+        "hsn_code": "42022290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - HAND-BAGS, WHETHER OR NOT WITH SHOULDER STRAP, INCLUDING THOSE WITHOUT\nHANDLE : WITH OUTER SURFACE OF PLASTIC SHEETING OR OF TEXTILE MATERIALS : HAND-BAGS\nAND SHOPPING BAGS, OF COTTON",
+        "hsn_code": "42022220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - HAND-BAGS, WHETHER OR NOT WITH SHOULDER STRAP, INCLUDING THOSE WITHOUT\nHANDLE : WITH OUTER SURFACE OF PLASTIC SHEETING OR OF TEXTILE MATERIALS : HAND-BAGS\nAND SHOPPING BAGS, OF JUTE",
+        "hsn_code": "42022230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "96190090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CLINICAL DIAPERS",
+        "hsn_code": "96190040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NAPKINS AND NAPKIN LINERS FOR BABIES",
+        "hsn_code": "96190030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PENCILS (OTHER THAN PENCILS OF HEADING 9608), CRAYONS, PENCIL LEADS, PASTELS, DRAWING CHARCOALS, WRITING OR DRAWING CHALKS AND TAILORS’ CHALKS",
+        "hsn_code": "9609",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PENCILS (OTHER THAN PENCILS OF HEADING 9608 ), CRAYONS, PENCIL LEADS, PASTELS,\nDRAWING CHARCOALS, WRITING OR DRAWING CHALKS AND TAILORS' CHALKS - Other : Other",
+        "hsn_code": "96099090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PENCILS (OTHER THAN PENCILS OF HEADING 9608 ), CRAYONS, PENCIL LEADS, PASTELS,\nDRAWING CHARCOALS, WRITING OR DRAWING CHALKS AND TAILORS' CHALKS - Other : Pastels,\ndrawing charcoals and writing or drawing chalks and tailors chalks",
+        "hsn_code": "96099030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PENCILS (OTHER THAN PENCILS OF HEADING 9608 ), CRAYONS, PENCIL LEADS, PASTELS,\nDRAWING CHARCOALS, WRITING OR DRAWING CHALKS AND TAILORS' CHALKS - Other : Other\npencils",
+        "hsn_code": "96099020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PENCILS (OTHER THAN PENCILS OF HEADING 9608 ), CRAYONS, PENCIL LEADS, PASTELS,\nDRAWING CHARCOALS, WRITING OR DRAWING CHALKS AND TAILORS' CHALKS - Other : Slate\npencils",
+        "hsn_code": "96099010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PENCILS (OTHER THAN PENCILS OF HEADING 9608 ), CRAYONS, PENCIL LEADS, PASTELS,\nDRAWING CHARCOALS, WRITING OR DRAWING CHALKS AND TAILORS' CHALKS - Pencil leads,\nblack or coloured",
+        "hsn_code": "96092000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PENCILS (OTHER THAN PENCILS OF HEADING 9608 ), CRAYONS, PENCIL LEADS, PASTELS,\nDRAWING CHARCOALS, WRITING OR DRAWING CHALKS AND TAILORS' CHALKS - Pencils and\ncrayons, with leads encased in a rigid sheath",
+        "hsn_code": "96091000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BALL POINT PENS; FELT TIPPED AND OTHER POROUS-TIPPED PENS AND MARKERS; FOUNTAIN PENS; STYLOGRAPH PENS AND OTHER PENS; DUPLICATING STYLOS; PROPELLING OR SLIDING PENCILS; PEN HOLDERS, PENCIL HOLDERS AND SIMILAR HOLDERS; PARTS (INCLUDING CAPS AND CLIPS) OF THE FOREGOING ARTICLES, OTHER THAN THOSE OF HEADING 9609",
+        "hsn_code": "9608",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BALL POINT PENS; FELT TIPPED AND OTHER POROUS-TIPPED PENS AND MARKERS; FOUNTAIN\nPENS; STYLOGRAPH PENS AND OTHER PENS; DUPLICATING STYLOS; PROPELLING OR SLIDING\nPENCILS; PEN HOLDERS, PENCIL HOLDERS AND SIMILAR HOLDERS; PARTS (INCLUDING CAPS AND\nCLIPS) OF THE FOREGOING ARTICLES, OTHER THAN THOSE OF HEADING 9609 - Refills for ball\npoint pens, comprising the ball point and ink-reservoir : - Other : - Other : Other",
+        "hsn_code": "96089990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BALL POINT PENS; FELT TIPPED AND OTHER POROUS-TIPPED PENS AND MARKERS; FOUNTAIN\nPENS; STYLOGRAPH PENS AND OTHER PENS; DUPLICATING STYLOS; PROPELLING OR SLIDING\nPENCILS; PEN HOLDERS, PENCIL HOLDERS AND SIMILAR HOLDERS; PARTS (INCLUDING CAPS AND\nCLIPS) OF THE FOREGOING ARTICLES, OTHER THAN THOSE OF HEADING 9609 - Refills for ball\npoint pens, comprising the ball point and ink-reservoir : - Other : - Other : Pen holders, pencil\nholders and similar holders",
+        "hsn_code": "96089910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BALL POINT PENS; FELT TIPPED AND OTHER POROUS-TIPPED PENS AND MARKERS; FOUNTAIN\nPENS; STYLOGRAPH PENS AND OTHER PENS; DUPLICATING STYLOS; PROPELLING OR SLIDING\nPENCILS; PEN HOLDERS, PENCIL HOLDERS AND SIMILAR HOLDERS; PARTS (INCLUDING CAPS AND\nCLIPS) OF THE FOREGOING ARTICLES, OTHER THAN THOSE OF HEADING 9609 - Refills for ball\npoint pens, comprising the ball point and ink-reservoir : - Other : - Pen nibs and nib points : -\nOther : Other",
+        "hsn_code": "96089199",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BALL POINT PENS; FELT TIPPED AND OTHER POROUS-TIPPED PENS AND MARKERS; FOUNTAIN\nPENS; STYLOGRAPH PENS AND OTHER PENS; DUPLICATING STYLOS; PROPELLING OR SLIDING\nPENCILS; PEN HOLDERS, PENCIL HOLDERS AND SIMILAR HOLDERS; PARTS (INCLUDING CAPS AND\nCLIPS) OF THE FOREGOING ARTICLES, OTHER THAN THOSE OF HEADING 9609 - Refills for ball\npoint pens, comprising the ball point and ink-reservoir : - Other : - Pen nibs and nib points : -\nOther : Of metal",
+        "hsn_code": "96089191",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BALL POINT PENS; FELT TIPPED AND OTHER POROUS-TIPPED PENS AND MARKERS; FOUNTAIN\nPENS; STYLOGRAPH PENS AND OTHER PENS; DUPLICATING STYLOS; PROPELLING OR SLIDING\nPENCILS; PEN HOLDERS, PENCIL HOLDERS AND SIMILAR HOLDERS; PARTS (INCLUDING CAPS AND\nCLIPS) OF THE FOREGOING ARTICLES, OTHER THAN THOSE OF HEADING 9609 - Refills for ball\npoint pens, comprising the ball point and ink-reservoir : - Other : - Pen nibs and nib points :\nOther pen nibs",
+        "hsn_code": "96089130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BALL POINT PENS; FELT TIPPED AND OTHER POROUS-TIPPED PENS AND MARKERS; FOUNTAIN\nPENS; STYLOGRAPH PENS AND OTHER PENS; DUPLICATING STYLOS; PROPELLING OR SLIDING\nPENCILS; PEN HOLDERS, PENCIL HOLDERS AND SIMILAR HOLDERS; PARTS (INCLUDING CAPS AND\nCLIPS) OF THE FOREGOING ARTICLES, OTHER THAN THOSE OF HEADING 9609 - Refills for ball\npoint pens, comprising the ball point and ink-reservoir : - Other : - Pen nibs and nib points : Nibs\nof wool felt or plastics for use in the manufacture of porous tip pen or markers",
+        "hsn_code": "96089120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BALL POINT PENS; FELT TIPPED AND OTHER POROUS-TIPPED PENS AND MARKERS; FOUNTAIN\nPENS; STYLOGRAPH PENS AND OTHER PENS; DUPLICATING STYLOS; PROPELLING OR SLIDING\nPENCILS; PEN HOLDERS, PENCIL HOLDERS AND SIMILAR HOLDERS; PARTS (INCLUDING CAPS AND\nCLIPS) OF THE FOREGOING ARTICLES, OTHER THAN THOSE OF HEADING 9609 - Refills for ball\npoint pens, comprising the ball point and ink-reservoir : - Other : - Pen nibs and nib points : Nib\npoints for pen",
+        "hsn_code": "96089110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BALL POINT PENS; FELT TIPPED AND OTHER POROUS-TIPPED PENS AND MARKERS; FOUNTAIN\nPENS; STYLOGRAPH PENS AND OTHER PENS; DUPLICATING STYLOS; PROPELLING OR SLIDING\nPENCILS; PEN HOLDERS, PENCIL HOLDERS AND SIMILAR HOLDERS; PARTS (INCLUDING CAPS AND\nCLIPS) OF THE FOREGOING ARTICLES, OTHER THAN THOSE OF HEADING 9609 - Refills for ball\npoint pens, comprising the ball point and ink-reservoir : Other",
+        "hsn_code": "96086090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BALL POINT PENS; FELT TIPPED AND OTHER POROUS-TIPPED PENS AND MARKERS; FOUNTAIN\nPENS; STYLOGRAPH PENS AND OTHER PENS; DUPLICATING STYLOS; PROPELLING OR SLIDING\nPENCILS; PEN HOLDERS, PENCIL HOLDERS AND SIMILAR HOLDERS; PARTS (INCLUDING CAPS AND\nCLIPS) OF THE FOREGOING ARTICLES, OTHER THAN THOSE OF HEADING 9609 - Refills for ball\npoint pens, comprising the ball point and ink-reservoir : With liquid ink (for rolling ball-pen)",
+        "hsn_code": "96086010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BALL POINT PENS; FELT TIPPED AND OTHER POROUS-TIPPED PENS AND MARKERS; FOUNTAIN\nPENS; STYLOGRAPH PENS AND OTHER PENS; DUPLICATING STYLOS; PROPELLING OR SLIDING\nPENCILS; PEN HOLDERS, PENCIL HOLDERS AND SIMILAR HOLDERS; PARTS (INCLUDING CAPS AND\nCLIPS) OF THE FOREGOING ARTICLES, OTHER THAN THOSE OF HEADING 9609 Sets of articles\nfrom two or more of the foregoing sub-headings",
+        "hsn_code": "96085000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BALL POINT PENS; FELT TIPPED AND OTHER POROUS-TIPPED PENS AND MARKERS; FOUNTAIN\nPENS; STYLOGRAPH PENS AND OTHER PENS; DUPLICATING STYLOS; PROPELLING OR SLIDING\nPENCILS; PEN HOLDERS, PENCIL HOLDERS AND SIMILAR HOLDERS; PARTS (INCLUDING CAPS AND\nCLIPS) OF THE FOREGOING ARTICLES, OTHER THAN THOSE OF HEADING 9609 Propelling or\nsliding pencils",
+        "hsn_code": "96084000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "96083099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "With body or cap of precious metal or rolled precious metal",
+        "hsn_code": "96083092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HIGH VALUE PENS",
+        "hsn_code": "96083091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "96083029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "With body or cap of precious metal or rolled precious metal",
+        "hsn_code": "96083022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HIGH VALUE PENS",
+        "hsn_code": "96083021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "96083019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "With body or cap of precious metal or rolled precious metal",
+        "hsn_code": "96083012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HIGH VALUE FOUNTAIN PENS",
+        "hsn_code": "96083011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BALL POINT PENS; FELT TIPPED AND OTHER POROUS-TIPPED PENS AND MARKERS; FOUNTAIN\nPENS; STYLOGRAPH PENS AND OTHER PENS; DUPLICATING STYLOS; PROPELLING OR SLIDING\nPENCILS; PEN HOLDERS, PENCIL HOLDERS AND SIMILAR HOLDERS; PARTS (INCLUDING CAPS AND\nCLIPS) OF THE FOREGOING ARTICLES, OTHER THAN THOSE OF HEADING 9609 Felt tipped and\nother porous-tipped pens and markers",
+        "hsn_code": "96082000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "other",
+        "hsn_code": "96081099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BALL POINT PENS With body or cap of precious metal or rolled precious metal",
+        "hsn_code": "96081092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HIGH VALU BALL POINT PENS",
+        "hsn_code": "96081091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "96081019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BALL POINT PENS With body or cap of precious metal or rolled precious metal",
+        "hsn_code": "96081012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HIGH VALU BALL POINT PENS",
+        "hsn_code": "96081011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DRAWING, MARKING-OUT OR MATHEMATICAL CALCULATING INSTRUMENTS (FOR EXAMPLE,\nDRAFTING MACHINES, PANTOGRAPHS, PROTRACTORS, DRAWING SETS, SLIDE RULES, DISC\nCALCULATORS); INSTRUMENTS FOR MEASURING LENGTH, FOR USE IN THE HAND (FOR EXAMPLE,\nMEASURING RODS AND TAPES, MICROMETERS, CALLIPERS), NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER - OTHER DRAWING, MARKING-OUT OR MATHEMATICAL\nCALCULATING INSTRUMENTS: OTHER",
+        "hsn_code": "90172090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DRAWING, MARKING-OUT OR MATHEMATICAL CALCULATING INSTRUMENTS (FOR EXAMPLE,\nDRAFTING MACHINES, PANTOGRAPHS, PROTRACTORS, DRAWING SETS, SLIDE RULES, DISC\nCALCULATORS); INSTRUMENTS FOR MEASURING LENGTH, FOR USE IN THE HAND (FOR EXAMPLE,\nMEASURING RODS AND TAPES, MICROMETERS, CALLIPERS), NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER - OTHER DRAWING, MARKING-OUT OR MATHEMATICAL\nCALCULATING INSTRUMENTS: PANTOGRAPH",
+        "hsn_code": "90172030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DRAWING, MARKING-OUT OR MATHEMATICAL CALCULATING INSTRUMENTS (FOR EXAMPLE,\nDRAFTING MACHINES, PANTOGRAPHS, PROTRACTORS, DRAWING SETS, SLIDE RULES, DISC\nCALCULATORS); INSTRUMENTS FOR MEASURING LENGTH, FOR USE IN THE HAND (FOR EXAMPLE,\nMEASURING RODS AND TAPES, MICROMETERS, CALLIPERS), NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER - OTHER DRAWING, MARKING-OUT OR MATHEMATICAL\nCALCULATING INSTRUMENTS: MATHEMATICAL CALCULATING INSTRUMENTS",
+        "hsn_code": "90172020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DRAWING, MARKING-OUT OR MATHEMATICAL CALCULATING INSTRUMENTS (FOR EXAMPLE,\nDRAFTING MACHINES, PANTOGRAPHS, PROTRACTORS, DRAWING SETS, SLIDE RULES, DISC\nCALCULATORS); INSTRUMENTS FOR MEASURING LENGTH, FOR USE IN THE HAND (FOR EXAMPLE,\nMEASURING RODS AND TAPES, MICROMETERS, CALLIPERS), NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER - OTHER DRAWING, MARKING-OUT OR MATHEMATICAL\nCALCULATING INSTRUMENTS: DRAWING AND MARKING-OUT INSTRUMENTS",
+        "hsn_code": "90172010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRAMES AND MOUNTINGS FOR SPECTACLES, GOGGLES OR THE LIKE, AND PARTS THEREOF",
+        "hsn_code": "9003",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRAMES AND MOUNTINGS FOR SPECTACLES, GOGGLES OR THE LIKE, AND PARTS THEREOF PARTS",
+        "hsn_code": "90039000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRAMES AND MOUNTINGS FOR SPECTACLES, GOGGLES OR THE LIKE, AND PARTS THEREOF -\nFRAMES AND MOUNTINGS: OF OTHER MATERIALS",
+        "hsn_code": "90031900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRAMES AND MOUNTINGS FOR SPECTACLES, GOGGLES OR THE LIKE, AND PARTS THEREOF -\nFRAMES AND MOUNTINGS: OF PLASTICS",
+        "hsn_code": "90031100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRAILERS AND SEMI-TRAILERS; OTHER VEHICLES, NOT MECHANICALLY PROPELLED; PARTS\nTHEREOF SELF-LOADING OR SELF-UNLOADING TRAILERS AND SEMI-TRAILERS FOR\nAGRICULTURAL PURPOSES",
+        "hsn_code": "87162000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - PARTS :\nOF AIR OR VACUUM PUMPS AND COMPRESSORS: OF BICYCLE PUMPS",
+        "hsn_code": "84149012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - HAND OR\nFOOT-OPERATED AIR PUMPS : OTHER HAND PUMPS",
+        "hsn_code": "84142020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - HAND OR\nFOOT-OPERATED AIR PUMPS : BICYCLE PUMPS",
+        "hsn_code": "84142010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF IRON OR STEEL - FORGED OR STAMPED, BUT NOT FURTHER WORKED :\nGRINDING BALLS AND SIMILAR ARTICLES FOR MILLS",
+        "hsn_code": "73261100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF IRON OR STEEL - FORGED OR STAMPED, BUT NOT FURTHER WORKED :\nOTHER : FOR AUTOMOBILES AND EARTH MOVING EQUIPMENTS",
+        "hsn_code": "73261910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF IRON OR STEEL - FORGED OR STAMPED, BUT NOT FURTHER WORKED :\nOTHER : OTHER",
+        "hsn_code": "73261990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF IRON OR STEEL - ARTICLES OF IRON OR STEEL WIRE : TYRE BEAD WIRE RINGS\nINTENDED FOR USE IN THE MANUFACTURE OF TYRES FOR CYCLES AND CYCLERICKSHAWS",
+        "hsn_code": "73262010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF IRON OR STEEL - ARTICLES OF IRON OR STEEL WIRE : OTHER",
+        "hsn_code": "73262090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF IRON OR STEEL - OTHER :BELT LACING OF STEEL",
+        "hsn_code": "73269010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF IRON OR STEEL - OTHER :BELT FASTENERS FOR MACHINERY BELTS",
+        "hsn_code": "73269020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF IRON OR STEEL - OTHER :DRAIN COVERS, PLATES, AND FRAMES FOR\nSEWAGES, WATER OR SIMILAR SYSTEM",
+        "hsn_code": "73269030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF IRON OR STEEL - OTHER : ENAMELLED IRON WARE",
+        "hsn_code": "73269040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF IRON OR STEEL - OTHER : GRINDING MEDIA BALLS AND CYLPEBS",
+        "hsn_code": "73269050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF IRON OR STEEL - OTHER : MANUFACTURES OF STAINLESS STEEL",
+        "hsn_code": "73269060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF IRON OR STEEL - OTHER :ARTICLES OF CLAD METAL",
+        "hsn_code": "73269070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF IRON OR STEEL - OTHER :PARTS OF SHIPS, FLOATING STRUCTURE AND\nVESSELS (EXCLUDING HULL, PROPELLERS AND PADDLE-WHEELS)",
+        "hsn_code": "73269080",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF IRON OR STEEL - OTHER : OTHER : SHANKS",
+        "hsn_code": "73269091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF IRON OR STEEL",
+        "hsn_code": "7326",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TANKS, CASKS, DRUMS, CANS, BOXES AND SIMILAR CONTAINERS, FOR ANY MATERIAL (OTHER\nTHAN COMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY NOT EXCEEDING 300\nL, WHETHER OR NOT LINED OR HEATINSULATED, BUT NOT FITTED WITH MECHANICAL OR\nTHERMAL EQUIPMENT- OF A CAPACITY OF 50 L OR MORE : TIN PLATE CONTAINERS",
+        "hsn_code": "73101010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TANKS, CASKS, DRUMS, CANS, BOXES AND SIMILAR CONTAINERS, FOR ANY MATERIAL (OTHER\nTHAN COMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY NOT EXCEEDING 300\nL, WHETHER OR NOT LINED OR HEATINSULATED, BUT NOT FITTED WITH MECHANICAL OR\nTHERMAL EQUIPMENT- OF A CAPACITY OF 50 L OR MORE : TRUNKS AND CASES",
+        "hsn_code": "73101020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TANKS, CASKS, DRUMS, CANS, BOXES AND SIMILAR CONTAINERS, FOR ANY MATERIAL (OTHER\nTHAN COMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY NOT EXCEEDING 300\nL, WHETHER OR NOT LINED OR HEATINSULATED, BUT NOT FITTED WITH MECHANICAL OR\nTHERMAL EQUIPMENT- OF A CAPACITY OF 50 L OR MORE : OTHER",
+        "hsn_code": "73101090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TANKS, CASKS, DRUMS, CANS, BOXES AND SIMILAR CONTAINERS, FOR ANY MATERIAL (OTHER\nTHAN COMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY NOT EXCEEDING 300\nL, WHETHER OR NOT LINED OR HEATINSULATED, BUT NOT FITTED WITH MECHANICAL OR\nTHERMAL EQUIPMENT - OF A CAPACITY OF LESS THAN 50 L : CANS WHICH ARE TO BE CLOSED BY\nSOLDERING OR CRIMPING : TIN PLATE CONTAINERS",
+        "hsn_code": "73102110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TANKS, CASKS, DRUMS, CANS, BOXES AND SIMILAR CONTAINERS, FOR ANY MATERIAL (OTHER\nTHAN COMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY NOT EXCEEDING 300\nL, WHETHER OR NOT LINED OR HEATINSULATED, BUT NOT FITTED WITH MECHANICAL OR\nTHERMAL EQUIPMENT - OF A CAPACITY OF LESS THAN 50 L : CANS WHICH ARE TO BE CLOSED BY\nSOLDERING OR CRIMPING : OTHER",
+        "hsn_code": "73102190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TANKS, CASKS, DRUMS, CANS, BOXES AND SIMILAR CONTAINERS, FOR ANY MATERIAL (OTHER\nTHAN COMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY NOT EXCEEDING 300\nL, WHETHER OR NOT LINED OR HEATINSULATED, BUT NOT FITTED WITH MECHANICAL OR\nTHERMAL EQUIPMENT - OF A CAPACITY OF LESS THAN 50 L : OTHER : TIN PLATE CONTAINERS",
+        "hsn_code": "73102910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TANKS, CASKS, DRUMS, CANS, BOXES AND SIMILAR CONTAINERS, FOR ANY MATERIAL (OTHER\nTHAN COMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY NOT EXCEEDING 300\nL, WHETHER OR NOT LINED OR HEATINSULATED, BUT NOT FITTED WITH MECHANICAL OR\nTHERMAL EQUIPMENT - OF A CAPACITY OF LESS THAN 50 L : OTHER : TRUNKS AND CASES",
+        "hsn_code": "73102920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TANKS, CASKS, DRUMS, CANS, BOXES AND SIMILAR CONTAINERS, FOR ANY MATERIAL (OTHER\nTHAN COMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY NOT EXCEEDING 300\nL, WHETHER OR NOT LINED OR HEATINSULATED, BUT NOT FITTED WITH MECHANICAL OR\nTHERMAL EQUIPMENT - OF A CAPACITY OF LESS THAN 50 L : OTHER : OTHER",
+        "hsn_code": "73102990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TANKS, CASKS, DRUMS, CANS, BOXES AND SIMILAR CONTAINERS, FOR ANY MATERIAL (OTHER THAN COMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY NOT EXCEEDING 300 L, WHETHER OR NOT LINED OR HEAT-INSULATED, BUT NOT FITTED WITH MECHANICAL OR THERMAL EQUIPMENT",
+        "hsn_code": "7310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CLOCK OR WATCH GLASSES AND SIMILAR GLASSES, GLASSES FOR NON-CORRECTIVE OR\nCORRECTIVE SPECTACLES, CURVED, BENT, HOLLOWED OR THE LIKE; NOT OPTICALLY WORKED;\nHOLLOW GLASS SPHERES AND THEIR SEGMENTS, FOR THE MANUFACTURE OF SUCH GLASSES -\nGLASSES FOR CORRECTIVE SPECTACLES: OPHTHALMIC ROUGH BLANKS",
+        "hsn_code": "70151010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CLOCK OR WATCH GLASSES AND SIMILAR GLASSES, GLASSES FOR NON-CORRECTIVE OR\nCORRECTIVE SPECTACLES, CURVED, BENT, HOLLOWED OR THE LIKE; NOT OPTICALLY WORKED;\nHOLLOW GLASS SPHERES AND THEIR SEGMENTS, FOR THE MANUFACTURE OF SUCH GLASSES -\nGLASSES FOR CORRECTIVE SPECTACLES: FLINT BUTTON",
+        "hsn_code": "70151020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CLOCK OR WATCH GLASSES AND SIMILAR GLASSES, GLASSES FOR NON-CORRECTIVE OR\nCORRECTIVE SPECTACLES, CURVED, BENT, HOLLOWED OR THE LIKE; NOT OPTICALLY WORKED;\nHOLLOW GLASS SPHERES AND THEIR SEGMENTS, FOR THE MANUFACTURE OF SUCH GLASSES -\nGLASSES FOR CORRECTIVE SPECTACLES: OTHER",
+        "hsn_code": "70151090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ROOFING TILES, CHIMNEY-POTS, COWLS, CHIMNEY LINERS, ARCHITECTURAL ORNAMENTS AND\nOTHER CERAMIC CONSTRUCTIONAL GOODS - ROOFING TILES",
+        "hsn_code": "69051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CERAMIC BUILDING BRICKS, FLOORING BLOCKS, SUPPORT OR FILLER TILES AND THE LIKE -\nBUILDING BRICKS",
+        "hsn_code": "69041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BRICKS, BLOCKS, TILES AND OTHER CERAMIC GOODS OF SILICEOUS FOSSIL MEALS (FOR\nEXAMPLE, KIESELGUHR, TRIPOLITE OR DIATOMITE) OR OF SIMILAR SILICEOUS EARTHS - BRICKS,\nBLOCKS, TILES AND OTHER CERAMIC GOODS OF SILICEOUS FOSSIL MEALS (FOR EXAMPLE,\nKIESELGUHR, TRIPOLITE OR DIATOMITE) OR OF SIMILAR SILICEOUS EARTHS:BRICKS",
+        "hsn_code": "69010010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF MAN-MADE STAPLE FIBRES, PUT UP FOR RETAIL SALE",
+        "hsn_code": "5511",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF MANMADE STAPLE FIBRES, PUT UP FOR RETAIL SALE -\nOF ARTIFICIAL STAPLE FIBRES : OTHER",
+        "hsn_code": "55113090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF MANMADE STAPLE FIBRES, PUT UP FOR RETAIL SALE -\nOF ARTIFICIAL STAPLE FIBRES : CONTAINING MORE THAN 85% BY WEIGHT OF STAPLE FIBRE",
+        "hsn_code": "55113010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF MANMADE STAPLE FIBRES, PUT UP FOR RETAIL SALE -\nOF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF SUCH FIBRES",
+        "hsn_code": "55112000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF MANMADE STAPLE FIBRES, PUT UP FOR RETAIL SALE -\nOF SYNTHETIC STAPLE FIBRES, CONTAINING 85% OR MORE BY WEIGHT OF SUCH FIBRES",
+        "hsn_code": "55111000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF ARTIFICIAL STAPLE FIBRES, NOT PUT UP FOR RETAIL SALE",
+        "hsn_code": "5510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF ARTIFICIAL STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN : OTHER",
+        "hsn_code": "55109090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF ARTIFICIAL STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN : ACETATE RAYON SPUN YARN",
+        "hsn_code": "55109020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF ARTIFICIAL STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN : VISCOSE RAYON SPUN YARN",
+        "hsn_code": "55109010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF ARTIFICIAL STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, MIXED MAINLY OR SOLELY WITH COTTON : OTHER",
+        "hsn_code": "55103090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF ARTIFICIAL STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, MIXED MAINLY OR SOLELY WITH COTTON : ACETATE RAYON SPUN YARN",
+        "hsn_code": "55103020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF ARTIFICIAL STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, MIXED MAINLY OR SOLELY WITH COTTON : VISCOSE RAYON SPUN YARN",
+        "hsn_code": "55103010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF ARTIFICIAL STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, MIXED MAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : OTHER",
+        "hsn_code": "55102090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF ARTIFICIAL STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, MIXED MAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : ACETATE\nRAYON SPUN YARN",
+        "hsn_code": "55102020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF ARTIFICIAL STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, MIXED MAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : VISCOSE\nRAYON SPUN YARN",
+        "hsn_code": "55102010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF ARTIFICIAL STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - CONTAINING 85% OR MORE BY WEIGHT OF ARTIFICIAL STAPLE FIBRES : MULTIPLE\n(FOLDED) OR CABLED YARN : OTHER",
+        "hsn_code": "55101290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF ARTIFICIAL STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - CONTAINING 85% OR MORE BY WEIGHT OF ARTIFICIAL STAPLE FIBRES : MULTIPLE\n(FOLDED) OR CABLED YARN : ACETATE RAYON SPUN YARN",
+        "hsn_code": "55101220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF ARTIFICIAL STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - CONTAINING 85% OR MORE BY WEIGHT OF ARTIFICIAL STAPLE FIBRES : MULTIPLE\n(FOLDED) OR CABLED YARN : VISCOSE RAYON SPUN YARN",
+        "hsn_code": "55101210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF ARTIFICIAL STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - CONTAINING 85% OR MORE BY WEIGHT OF ARTIFICIAL STAPLE FIBRES : SINGLE YARN :\nOTHER",
+        "hsn_code": "55101190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF ARTIFICIAL STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - CONTAINING 85% OR MORE BY WEIGHT OF ARTIFICIAL STAPLE FIBRES : SINGLE YARN :\nACETATE RAYON SPUN YARN",
+        "hsn_code": "55101120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF ARTIFICIAL STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - CONTAINING 85% OR MORE BY WEIGHT OF ARTIFICIAL STAPLE FIBRES : SINGLE YARN :\nVISCOSE RAYON SPUN YARN",
+        "hsn_code": "55101110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL SALE",
+        "hsn_code": "5509",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN : OTHER",
+        "hsn_code": "55099900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN : MIXED MAINLY OR SOLELY WITH COTTON",
+        "hsn_code": "55099200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN : MIXED MAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "55099100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, OF ACRYLIC OR MODACRYLIC STAPLE FIBRES : OTHER",
+        "hsn_code": "55096900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, OF ACRYLIC OR MODACRYLIC STAPLE FIBRES : MIXED MAINLY OR SOLELY\nWITH COTTON",
+        "hsn_code": "55096200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, OF ACRYLIC OR MODACRYLIC STAPLE FIBRES : MIXED MAINLY OR SOLELY\nWITH WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "55096100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, OF POLYESTER STAPLE FIBRES : OTHER",
+        "hsn_code": "55095900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, OF POLYESTER STAPLE FIBRES : MIXED MAINLY OR SOLELY WITH COTTON",
+        "hsn_code": "55095300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, OF POLYESTER STAPLE FIBRES : MIXED MAINLY OR SOLELY WITH WOOL OR\nFINE ANIMAL HAIR",
+        "hsn_code": "55095200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, OF POLYESTER STAPLE FIBRES : MIXED MAINLY OR SOLELY WITH ARTIFICIAL\nSTAPLE FIBRES",
+        "hsn_code": "55095100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, CONTAINING 85% OR MORE BY WEIGHT OF SYNTHETIC STAPLE FIBRES :\nMULTIPLE (FOLDED) OR CABLED YARN : OTHER",
+        "hsn_code": "55094290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, CONTAINING 85% OR MORE BY WEIGHT OF SYNTHETIC STAPLE FIBRES :\nMULTIPLE (FOLDED) OR CABLED YARN : POLYVINYL CHLORIDE (PVC) SPUN YARN",
+        "hsn_code": "55094230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, CONTAINING 85% OR MORE BY WEIGHT OF SYNTHETIC STAPLE FIBRES :\nMULTIPLE (FOLDED) OR CABLED YARN : POLYVINYL ACETATE (PVA) SPUN YARN",
+        "hsn_code": "55094220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, CONTAINING 85% OR MORE BY WEIGHT OF SYNTHETIC STAPLE FIBRES :\nMULTIPLE (FOLDED) OR CABLED YARN : POLYPROPYLENE SPUN YARN",
+        "hsn_code": "55094210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, CONTAINING 85% OR MORE BY WEIGHT OF SYNTHETIC STAPLE FIBRES :\nSINGLE YARN : OTHER",
+        "hsn_code": "55094190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, CONTAINING 85% OR MORE BY WEIGHT OF SYNTHETIC STAPLE FIBRES :\nSINGLE YARN : POLYVINYL CHLORIDE SPUN YARN",
+        "hsn_code": "55094130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, CONTAINING 85% OR MORE BY WEIGHT OF SYNTHETIC STAPLE FIBRES :\nSINGLE YARN : POLYVINYL ACETATE SPUN YARN",
+        "hsn_code": "55094120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - OTHER YARN, CONTAINING 85% OR MORE BY WEIGHT OF SYNTHETIC STAPLE FIBRES :\nSINGLE YARN : POLYPROPYLENE SPUN YARN",
+        "hsn_code": "55094110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - CONTAINING 85% OR MORE BY WEIGHT OF ACRYLIC OR MODACRYLIC STAPLE FIBRES :\nMULTIPLE (FOLDED) OR CABLED YARN",
+        "hsn_code": "55093200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - CONTAINING 85% OR MORE BY WEIGHT OF ACRYLIC OR MODACRYLIC STAPLE FIBRES :\nSINGLE YARN",
+        "hsn_code": "55093100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - CONTAINING 85% OR MORE BY WEIGHT OF POLYESTER STAPLE FIBRES : MULTIPLE\n(FOLDED) OR CABLED YARN",
+        "hsn_code": "55092200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - CONTAINING 85% OR MORE BY WEIGHT OF POLYESTER STAPLE FIBRES : SINGLE YARN",
+        "hsn_code": "55092100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - CONTAINING 85% OR MORE BY WEIGHT OF STAPLE FIBRES OF NYLON OR OTHER\nPOLYAMIDES : MULTIPLE (FOLDED) OR CABLED YARN",
+        "hsn_code": "55091200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YARN (OTHER THAN SEWING THREAD) OF SYNTHETIC STAPLE FIBRES, NOT PUT UP FOR RETAIL\nSALE - CONTAINING 85% OR MORE BY WEIGHT OF STAPLE FIBRES OF NYLON OR OTHER\nPOLYAMIDES : SINGLE YARN",
+        "hsn_code": "55091100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NATURAL CORK, DEBACKED OR ROUGHLY SQUARED, OR IN RECTANGULAR (INCLUDING SQUARE)\nBLOCKS, PLATES, SHEETS OR STRIP (INCLUDING SHARP -EDGED BLANKS FOR CORKS OR\nSTOPPERS)",
+        "hsn_code": "45020000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MATCHES, OTHER THAN PYROTECHNIC ARTICLES OF HEADING 3604 MATCHES, OTHER THAN\nPYROTECHNIC ARTICLES OF HEADING 3604: SAFETY MATCHES",
+        "hsn_code": "36050010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE, SULPHUR\nTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-- HERBICIDES, ANTI-SPROUTING\nPRODUCTS AND PLANT-GROWTH REGULATORS:--- OTHER",
+        "hsn_code": "38089390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE, SULPHUR\nTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-- HERBICIDES, ANTI-SPROUTING\nPRODUCTS AND PLANT-GROWTH REGULATORS:--- WEEDICIDES AND WEED KILLING AGENTS",
+        "hsn_code": "38089350",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE, SULPHUR\nTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-- HERBICIDES, ANTI-SPROUTING\nPRODUCTS AND PLANT-GROWTH REGULATORS:--- PLANT GROWTH REGULATORS",
+        "hsn_code": "38089340",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE, SULPHUR\nTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-- HERBICIDES, ANTI-SPROUTING\nPRODUCTS AND PLANT-GROWTH REGULATORS:---GIBBERELLIC ACID",
+        "hsn_code": "38089330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE, SULPHUR\nTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-- HERBICIDES, ANTI-SPROUTING\nPRODUCTS AND PLANT-GROWTH REGULATORS: --- 2:4 DICHLORO PHENOY ACETIC ACID AND ITS\nESTERS",
+        "hsn_code": "38089320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE, SULPHUR\nTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-- HERBICIDES, ANTI-SPROUTING\nPRODUCTS AND PLANT-GROWTH REGULATORS:--- CHLOROMETHYL PHENOZY ACETIC ACID\n(M.C.P.A)",
+        "hsn_code": "38089310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN, RARE GASES AND OTHER NON-METALS - OXYGEN :MEDICINAL GRADE",
+        "hsn_code": "28044010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FLUORINE, CHLORINE, BROMINE AND IODINE - IODINE",
+        "hsn_code": "28012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MARBLE, TRAVERTINE, ECAUSSINE AND OTHER CALCAREOUS MONUMENTAL OR BUILDING\nSTONE OF AN APPARENT SPECIFIC GRAVITY OF 2.5 OR MORE, AND ALABASTER, WHETHER OR\nNOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS\nOF A RECTANGULAR (INCLUDING SQUARE) SHAPE - MARBLE AND TRAVERTINE: MERELY CUT, BY\nSAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE)\nSHAPE: BLOCKS",
+        "hsn_code": "25151210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FOOD PREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER : OTHER : DIABETIC\nFOODS",
+        "hsn_code": "21069091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "EXTRACTS, ESSENCES AND CONCENTRATES, OF COFFEE, TEA OR MATE AND PREPARATIONSWITH\nA BASIS OF THESE PRODUCTS OR WITH ABASIS OF COFFEE, TEA OR MATE; ROASTEDCHICORY\nAND OTHER ROASTED COFFEESUBSTITUTES, AND EXTRACTS, ESSENCESAND CONCENTRATES\nTHEREOF - ROASTED CHICORY AND OTHER ROASTED COFFEESUBSTITUTES, AND EXTRACTS,\nESSENCES ANDCONCENTRATES THEREOF : OTHER",
+        "hsn_code": "21013090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "EXTRACTS, ESSENCES AND CONCENTRATES, OF COFFEE, TEA OR MATE AND PREPARATIONSWITH\nA BASIS OF THESE PRODUCTS OR WITH ABASIS OF COFFEE, TEA OR MATE; ROASTEDCHICORY\nAND OTHER ROASTED COFFEESUBSTITUTES, AND EXTRACTS, ESSENCESAND CONCENTRATES\nTHEREOF - ROASTED CHICORY AND OTHER ROASTED COFFEESUBSTITUTES, AND EXTRACTS,\nESSENCES ANDCONCENTRATES THEREOF : ROASTED COFFEE SUBSTITUTES",
+        "hsn_code": "21013020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "EXTRACTS, ESSENCES AND CONCENTRATES, OF COFFEE, TEA OR MATE AND PREPARATIONSWITH\nA BASIS OF THESE PRODUCTS OR WITH ABASIS OF COFFEE, TEA OR MATE; ROASTEDCHICORY\nAND OTHER ROASTED COFFEESUBSTITUTES, AND EXTRACTS, ESSENCESAND CONCENTRATES\nTHEREOF - ROASTED CHICORY AND OTHER ROASTED COFFEESUBSTITUTES, AND EXTRACTS,\nESSENCES ANDCONCENTRATES THEREOF : ROASTED CHICORY",
+        "hsn_code": "21013010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CANE OR BEET SUGAR AND CHEMICALLY PURE SUCROSE, IN SOLID FORM - OTHER : OTHER :\nSUGAR CUBES",
+        "hsn_code": "17019910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CANE OR BEET SUGAR AND CHEMICALLY PURE SUCROSE, IN SOLID FORM - OTHER : OTHER :\nSUGAR CUBES",
+        "hsn_code": "17019990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CANE OR BEET SUGAR AND CHEMICALLY PURE SUCROSE, IN SOLID FORM - OTHER : REFINED\nSUGAR CONTAINING ADDED FLAVOURING OR COLOURING MATTER",
+        "hsn_code": "17019100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BUTTER AND OTHER FATS AND OILS DERIVED FROM MILK; DAIRY SPREADS",
+        "hsn_code": "0405",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GLASS BEADS, IMITATION PEARLS, IMITATION PRECIOUS OR SEMI-PRECIOUS STONES AND\nSIMILAR GLASS SMALLWARES, AND ARTICLES THEREOF OTHER THAN IMITATION JEWELLERY,\nGLASS EYES OTHER THAN PROSTHETIC ARTICLES; STATUETTES AND OTHER ORNAMENTS OF\nLAMP-WORKED GLASS, OTHER THAN IMITATION JEWELLERY; GLASS MICROSPHERES NOT\nEXCEEDING 1 MM IN DIAMETER - GLASS BEADS, IMITATION PEARLS, IMITATION PRECIOUS OR\nSEMI-PRECIOUS STONES AND SIMILAR GLASS SMALLWARES: BANGLES",
+        "hsn_code": "70181010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLASS BEADS, IMITATION PEARLS, IMITATION PRECIOUS OR SEMI-PRECIOUS STONES AND\nSIMILAR GLASS SMALLWARES, AND ARTICLES THEREOF OTHER THAN IMITATION JEWELLERY,\nGLASS EYES OTHER THAN PROSTHETIC ARTICLES; STATUETTES AND OTHER ORNAMENTS OF\nLAMP-WORKED GLASS, OTHER THAN IMITATION JEWELLERY; GLASS MICROSPHERES NOT\nEXCEEDING 1 MM IN DIAMETER - GLASS BEADS, IMITATION PEARLS, IMITATION PRECIOUS OR\nSEMI-PRECIOUS STONES AND SIMILAR GLASS SMALLWARES: BEADS",
+        "hsn_code": "70181020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLASS BEADS, IMITATION PEARLS, IMITATION PRECIOUS OR SEMI-PRECIOUS STONES AND\nSIMILAR GLASS SMALLWARES, AND ARTICLES THEREOF OTHER THAN IMITATION JEWELLERY,\nGLASS EYES OTHER THAN PROSTHETIC ARTICLES; STATUETTES AND OTHER ORNAMENTS OF\nLAMP-WORKED GLASS, OTHER THAN IMITATION JEWELLERY; GLASS MICROSPHERES NOT\nEXCEEDING 1 MM IN DIAMETER - GLASS BEADS, IMITATION PEARLS, IMITATION PRECIOUS OR\nSEMI-PRECIOUS STONES AND SIMILAR GLASS SMALLWARES: OTHER",
+        "hsn_code": "70181090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE",
+        "hsn_code": "6214",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF OTHER TEXTILE MATERIALS\n: OTHER",
+        "hsn_code": "62149090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF OTHER TEXTILE MATERIALS\n: SHAWLS, MUFFLERS AND THE LIKE OF MAN-MADE FIBRES",
+        "hsn_code": "62149060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF OTHER TEXTILE MATERIALS\n: SHAWLS, MUFFLERS AND THE LIKE, OF COTTON",
+        "hsn_code": "62149050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF OTHER TEXTILE MATERIALS\n: SCARVES, OF COTTON",
+        "hsn_code": "62149040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF OTHER TEXTILE MATERIALS\n: ODHANI, OF COTTON : OTHER",
+        "hsn_code": "62149039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF OTHER TEXTILE MATERIALS\n: ODHANI, OF COTTON : WHITE BLEACHED",
+        "hsn_code": "62149032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF OTHER TEXTILE MATERIALS\n: ODHANI, OF COTTON : GREY",
+        "hsn_code": "62149031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF OTHER TEXTILE MATERIALS\n: CHADARS, OF COTTON : OTHER",
+        "hsn_code": "62149029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF OTHER TEXTILE MATERIALS\n: CHADARS, OF COTTON : WHITE BLEACHED",
+        "hsn_code": "62149022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF OTHER TEXTILE MATERIALS\n: CHADARS, OF COTTON : GREY",
+        "hsn_code": "62149021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF OTHER TEXTILE MATERIALS\n: ABRABROOMAL, COTTON",
+        "hsn_code": "62149010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF ARTIFICIAL FIBRES",
+        "hsn_code": "62144000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF SYNTHETIC FIBRES",
+        "hsn_code": "62143000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF WOOL OR FINE ANIMAL\nHAIR : OTHER",
+        "hsn_code": "62142090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF WOOL OR FINE ANIMAL\nHAIR : MUFFLERS",
+        "hsn_code": "62142030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF WOOL OR FINE ANIMAL\nHAIR : SCARVES",
+        "hsn_code": "62142020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF WOOL OR FINE ANIMAL\nHAIR : SHAWLS",
+        "hsn_code": "62142010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF SILK OR SILK WASTE :\nOTHER",
+        "hsn_code": "62141090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF HANDLOOM",
+        "hsn_code": "62141030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF SILK OR SILK WASTE :\nSHAWLS, SCARVES (EXCEEDING 60 C SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE\nLIKE - OF SILK OR SILK WASTE : MS) AND THE LIKE",
+        "hsn_code": "62141020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SHAWLS, SCARVES, MUFFLERS, MANTILLAS, VEILS AND THE LIKE - OF SILK OR SILK WASTE :\nSCARVES OF SILK MEASURING 60 CMS OR LESS",
+        "hsn_code": "62141010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES, KNITTED OR CROCHETED; KNITTED OR CROCHETED PARTS OF GARMENTS OR OF CLOTHING ACCESSORIES",
+        "hsn_code": "6117",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES, KNITTED OR CROCHETED; KNITTED OR CROCHETED\nPARTS OF GARMENTS OR OF CLOTHING ACCESSORIES - PARTS",
+        "hsn_code": "61179000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES, KNITTED OR CROCHETED; KNITTED OR CROCHETED\nPARTS OF GARMENTS OR OF CLOTHING ACCESSORIES - OTHER ACCESSORIES : OTHER",
+        "hsn_code": "61178090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES, KNITTED OR CROCHETED; KNITTED OR CROCHETED\nPARTS OF GARMENTS OR OF CLOTHING ACCESSORIES - OTHER ACCESSORIES : OF MAN-MADE\nFIBRES",
+        "hsn_code": "61178040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES, KNITTED OR CROCHETED; KNITTED OR CROCHETED\nPARTS OF GARMENTS OR OF CLOTHING ACCESSORIES - OTHER ACCESSORIES : OF COTTON",
+        "hsn_code": "61178030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES, KNITTED OR CROCHETED; KNITTED OR CROCHETED\nPARTS OF GARMENTS OR OF CLOTHING ACCESSORIES - OTHER ACCESSORIES : OF WOOL",
+        "hsn_code": "61178020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES, KNITTED OR CROCHETED; KNITTED OR CROCHETED\nPARTS OF GARMENTS OR OF CLOTHING ACCESSORIES - OTHER ACCESSORIES : OF SILK",
+        "hsn_code": "61178010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES, KNITTED OR CROCHETED; KNITTED OR CROCHETED\nPARTS OF GARMENTS OR OF CLOTHING ACCESSORIES TIES, BOW TIES AND CRAVATS : OF OTHER\nFIBRES",
+        "hsn_code": "61172090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES, KNITTED OR CROCHETED; KNITTED OR CROCHETED\nPARTS OF GARMENTS OR OF CLOTHING ACCESSORIES TIES, BOW TIES AND CRAVATS : OF MAN\nMADE FIBRES",
+        "hsn_code": "61172040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES, KNITTED OR CROCHETED; KNITTED OR CROCHETED\nPARTS OF GARMENTS OR OF CLOTHING ACCESSORIES TIES, BOW TIES AND CRAVATS : OF\nCOTTON",
+        "hsn_code": "61172030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES, KNITTED OR CROCHETED; KNITTED OR CROCHETED\nPARTS OF GARMENTS OR OF CLOTHING ACCESSORIES TIES, BOW TIES AND CRAVATS : OF WOOL",
+        "hsn_code": "61172020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES, KNITTED OR CROCHETED; KNITTED OR CROCHETED\nPARTS OF GARMENTS OR OF CLOTHING ACCESSORIES TIES, BOW TIES AND CRAVATS : OF SILK",
+        "hsn_code": "61172010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES, KNITTED OR CROCHETED; KNITTED OR CROCHETED\nPARTS OF GARMENTS OR OF CLOTHING ACCESSORIES - SHAWLS, SCARVES, MUFFLERS,\nMANTILLAS, VEILS AND THE LIKE : OTHER",
+        "hsn_code": "61171090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES, KNITTED OR CROCHETED; KNITTED OR CROCHETED\nPARTS OF GARMENTS OR OF CLOTHING ACCESSORIES - SHAWLS, SCARVES, MUFFLERS,\nMANTILLAS, VEILS AND THE LIKE : OF MAN-MADE FIBRES",
+        "hsn_code": "61171040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES, KNITTED OR CROCHETED; KNITTED OR CROCHETED\nPARTS OF GARMENTS OR OF CLOTHING ACCESSORIES - SHAWLS, SCARVES, MUFFLERS,\nMANTILLAS, VEILS AND THE LIKE : OF COTTON",
+        "hsn_code": "61171030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES, KNITTED OR CROCHETED; KNITTED OR CROCHETED\nPARTS OF GARMENTS OR OF CLOTHING ACCESSORIES - SHAWLS, SCARVES, MUFFLERS,\nMANTILLAS, VEILS AND THE LIKE : OF WOOL",
+        "hsn_code": "61171020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES, KNITTED OR CROCHETED; KNITTED OR CROCHETED\nPARTS OF GARMENTS OR OF CLOTHING ACCESSORIES - SHAWLS, SCARVES, MUFFLERS,\nMANTILLAS, VEILS AND THE LIKE : OF SILK",
+        "hsn_code": "61171010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BRAIDS IN THE PIECE; ORNAMENTAL TRIMMINGS IN THE PIECE, WITHOUT EMBROIDERY, OTHER\nTHAN KNITTED OR CROCHETED; TASSELS, POMPONS AND SIMILAR ARTICLES - BRAIDS, IN THE\nPIECE:OTHER",
+        "hsn_code": "58081090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BRAIDS IN THE PIECE; ORNAMENTAL TRIMMINGS IN THE PIECE, WITHOUT EMBROIDERY, OTHER\nTHAN KNITTED OR CROCHETED; TASSELS, POMPONS AND SIMILAR ARTICLES - BRAIDS, IN THE\nPIECE:OF COTTON",
+        "hsn_code": "58081010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TULLES AND OTHER NET FABRICS, NOT INCLUDING WOVEN, KNITTED OR CROCHETED FABRICS;\nLACE IN THE PIECE, IN STRIPS OR IN MOTIFS, OTHER THAN FABRICS OF HEADINGS 6002 TO 6006 -\nHAND-MADE LACE",
+        "hsn_code": "58043000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF YARN, STRIP OR THE LIKE OF HEADING 5404 OR 5405, TWINE, CORDAGE, ROPE OR\nCABLES, NOT ELSEWHERE SPECIFIED OR INCLUDED - ARTICLES OF YARN, STRIP OR THE LIKE OF\nHEADING 5404 OR 5405, TWINE, CORDAGE, ROPE OR CABLES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED : OTHER",
+        "hsn_code": "56090090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF YARN, STRIP OR THE LIKE OF HEADING 5404 OR 5405, TWINE, CORDAGE, ROPE OR\nCABLES, NOT ELSEWHERE SPECIFIED OR INCLUDED - ARTICLES OF YARN, STRIP OR THE LIKE OF\nHEADING 5404 OR 5405, TWINE, CORDAGE, ROPE OR CABLES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED : ARTICLES MADE UP OF COTTON YARN",
+        "hsn_code": "56090020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF YARN, STRIP OR THE LIKE OF HEADING 5404 OR 5405, TWINE, CORDAGE, ROPE OR CABLES, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "5609",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF YARN, STRIP OR THE LIKE OF HEADING 5404 OR 5405, TWINE, CORDAGE, ROPE OR\nCABLES, NOT ELSEWHERE SPECIFIED OR INCLUDED - ARTICLES OF YARN, STRIP OR THE LIKE OF\nHEADING 5404 OR 5405, TWINE, CORDAGE, ROPE OR CABLES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED : ARTICLES MADE UP OF JUTE",
+        "hsn_code": "56090030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF YARN, STRIP OR THE LIKE OF HEADING 5404 OR 5405, TWINE, CORDAGE, ROPE OR\nCABLES, NOT ELSEWHERE SPECIFIED OR INCLUDED - ARTICLES OF YARN, STRIP OR THE LIKE OF\nHEADING 5404 OR 5405, TWINE, CORDAGE, ROPE OR CABLES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED : PRODUCTS OF COIR",
+        "hsn_code": "56090010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TWINE, CORDAGE, ROPES AND CABLES, WHETHER OR NOT PLAITED OR BRAIDED AND WHETHER OR NOT IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR PLASTICS",
+        "hsn_code": "5607",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TWINE, CORDAGE, ROPES AND CABLES, WHETHER OR NOT PLAITED OR BRAIDED AND WHETHER\nOR NOT IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR PLASTICS - OTHER :\nOTHER",
+        "hsn_code": "56079090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TWINE, CORDAGE, ROPES AND CABLES, WHETHER OR NOT PLAITED OR BRAIDED AND WHETHER\nOR NOT IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR PLASTICS - OTHER :\nCORDAGE, CABLE, ROPES AND TWINE, OF COTTON",
+        "hsn_code": "56079020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TWINE, CORDAGE, ROPES AND CABLES, WHETHER OR NOT PLAITED OR BRAIDED AND WHETHER\nOR NOT IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR PLASTICS - OTHER :\nCOIR, CORDAGE AND ROPES, OTHER THAN OF COTTON",
+        "hsn_code": "56079010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TWINE, CORDAGE, ROPES AND CABLES, WHETHER OR NOT PLAITED OR BRAIDED AND WHETHER\nOR NOT IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR PLASTICS - OF\nOTHER SYNTHETIC FIBRES : OTHER",
+        "hsn_code": "56075090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TWINE, CORDAGE, ROPES AND CABLES, WHETHER OR NOT PLAITED OR BRAIDED AND WHETHER\nOR NOT IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR PLASTICS - OF\nOTHER SYNTHETIC FIBRES : NYLON ROPE",
+        "hsn_code": "56075040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TWINE, CORDAGE, ROPES AND CABLES, WHETHER OR NOT PLAITED OR BRAIDED AND WHETHER\nOR NOT IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR PLASTICS - OF\nOTHER SYNTHETIC FIBRES : VISCOSE TYRE CORD",
+        "hsn_code": "56075030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TWINE, CORDAGE, ROPES AND CABLES, WHETHER OR NOT PLAITED OR BRAIDED AND WHETHER\nOR NOT IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR PLASTICS - OF\nOTHER SYNTHETIC FIBRES : NYLON TYRE CORD",
+        "hsn_code": "56075020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TWINE, CORDAGE, ROPES AND CABLES, WHETHER OR NOT PLAITED OR BRAIDED AND WHETHER\nOR NOT IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR PLASTICS - OF\nOTHER SYNTHETIC FIBRES : NYLON FISH NET TWINE",
+        "hsn_code": "56075010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TWINE, CORDAGE, ROPES AND CABLES, WHETHER OR NOT PLAITED OR BRAIDED AND WHETHER\nOR NOT IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR PLASTICS - OF\nPOLYETHYLENE OR POLYPROPYLENE : OTHER",
+        "hsn_code": "56074900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TWINE, CORDAGE, ROPES AND CABLES, WHETHER OR NOT PLAITED OR BRAIDED AND WHETHER\nOR NOT IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR PLASTICS - OF\nPOLYETHYLENE OR POLYPROPYLENE : BINDER OR BALER TWINE",
+        "hsn_code": "56074100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TWINE, CORDAGE, ROPES AND CABLES, WHETHER OR NOT PLAITED OR BRAIDED AND WHETHER\nOR NOT IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR PLASTICS OF\nABACA (MANILA HEMP OR MUSA TEXTILLIS NEE) OR OTHER HARD (LEAF) FIBRES",
+        "hsn_code": "56073000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TWINE, CORDAGE, ROPES AND CABLES, WHETHER OR NOT PLAITED OR BRAIDED AND WHETHER\nOR NOT IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR PLASTICS - OF SISAL\nOR OTHER TEXTILE FIBRES OF THE GENUS AGAVE : OTHER",
+        "hsn_code": "56072900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TWINE, CORDAGE, ROPES AND CABLES, WHETHER OR NOT PLAITED OR BRAIDED AND WHETHER\nOR NOT IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR PLASTICS - OF SISAL\nOR OTHER TEXTILE FIBRES OF THE GENUS AGAVE : BINDER OR BALER TWINE",
+        "hsn_code": "56072100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TWINE, CORDAGE, ROPES AND CABLES, WHETHER OR NOT PLAITED OR BRAIDED AND WHETHER\nOR NOT IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR PLASTICS - OF JUTE\nOR OTHER TEXTILE BAST FIBRES OF HEADING 5303 : OTHER",
+        "hsn_code": "56071090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TWINE, CORDAGE, ROPES AND CABLES, WHETHER OR NOT PLAITED OR BRAIDED AND WHETHER\nOR NOT IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR PLASTICS - OF JUTE\nOR OTHER TEXTILE BAST FIBRES OF HEADING 5303 : CORDAGE, CABLE, ROPE AND TWINE OF\nJUTE",
+        "hsn_code": "56071010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDED - Parts: Other",
+        "hsn_code": "94059900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDED - Parts: Of plastics",
+        "hsn_code": "94059200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDED - Parts: Of glass",
+        "hsn_code": "94059100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER AIRCRAFT (FOR EXAMPLE, HELICOPTERS, AEROPLANES); SPACECRAFT (INCLUDING\nSATELLITES) AND SUBORBITAL AND SPACECRAFT LAUNCH VEHICLES SPACECRAFT (INCLUDING\nSATELLITES) AND SUBORBITAL AND SPACECRAFT LAUNCH VEHICLES",
+        "hsn_code": "88026000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER AIRCRAFT (FOR EXAMPLE, HELICOPTORS, AEROPLANES); SPACECRAFT (INCLUDING SATELLITES) AND SUBORBITAL AND SPACECRAFT LAUNCH VEHICLES",
+        "hsn_code": "8802",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER AIRCRAFT (FOR EXAMPLE, HELICOPTERS, AEROPLANES); SPACECRAFT (INCLUDING\nSATELLITES) AND SUBORBITAL AND SPACECRAFT LAUNCH VEHICLES AEROPLANES AND OTHER\nAIRCRAFT, OF AN UNLADEN WEIGHT EXCEEDING 15,000 KG",
+        "hsn_code": "88024000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER AIRCRAFT (FOR EXAMPLE, HELICOPTERS, AEROPLANES); SPACECRAFT (INCLUDING\nSATELLITES) AND SUBORBITAL AND SPACECRAFT LAUNCH VEHICLES AEROPLANES AND OTHER\nAIRCRAFT, OF AN UNLADEN WEIGHT EXCEEDING 2,000 KG BUT NOT EXCEEDING 15,000 KG",
+        "hsn_code": "88023000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER AIRCRAFT (FOR EXAMPLE, HELICOPTERS, AEROPLANES); SPACECRAFT (INCLUDING\nSATELLITES) AND SUBORBITAL AND SPACECRAFT LAUNCH VEHICLES AEROPLANES AND OTHER\nAIRCRAFT, OF AN UNLADEN WEIGHT NOT EXCEEDING 2,000 KG",
+        "hsn_code": "88022000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER AIRCRAFT (FOR EXAMPLE, HELICOPTERS, AEROPLANES); SPACECRAFT (INCLUDING\nSATELLITES) AND SUBORBITAL AND SPACECRAFT LAUNCH VEHICLES - HELICOPTERS: OF AN\nUNLADEN WEIGHT EXCEEDING 2,000 KG",
+        "hsn_code": "88021200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER AIRCRAFT (FOR EXAMPLE, HELICOPTERS, AEROPLANES); SPACECRAFT (INCLUDING\nSATELLITES) AND SUBORBITAL AND SPACECRAFT LAUNCH VEHICLES - HELICOPTERS: OF AN\nUNLADEN WEIGHT NOT EXCEEDING 2,000 KG",
+        "hsn_code": "88021100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF VEHICLES OF HEADINGS 8711TO 8713 OF CARRIAGES FOR DISABLED\nPERSONS OTHER",
+        "hsn_code": "87142090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF VEHICLES OF HEADINGS 8711TO 8713 OF CARRIAGES FOR DISABLED\nPERSONS NON-MECHANICALLY PROPELLED",
+        "hsn_code": "87142020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF VEHICLES OF HEADINGS 8711TO 8713 OF CARRIAGES FOR DISABLED\nPERSONS MECHANICALLY PROPELLED",
+        "hsn_code": "87142010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - PARTS: 8413 91 - OF PUMPS: OF RECIPROCATING PUMPS",
+        "hsn_code": "84139110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - PARTS: 8413 91 - OF PUMPS: OF CENTRIFUGAL PUMPS",
+        "hsn_code": "84139120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - PARTS: 8413 91 - OF PUMPS: OF DEEP WELL TURBINE PUMPS AND OF OTHER\nROTARY PUMPS",
+        "hsn_code": "84139130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - PARTS: 8413 91 - OF PUMPS: OF HAND PUMP FOR HANDLING WATER",
+        "hsn_code": "84139140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - PARTS: 8413 91 - OF PUMPS: OTHER",
+        "hsn_code": "84139190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - PUMPS FITTED OR DESIGNED TO BE FITTED WITH A MEASURING DEVICE: 8413 11 -\nPUMPS FOR DISPENSING FUEL OR LUBRICANTS, OF THE TYPE USED IN FILLING STATIONS OR IN\nGARAGES: HAND PUMPS",
+        "hsn_code": "84131110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - PUMPS FITTED OR DESIGNED TO BE FITTED WITH A MEASURING DEVICE: 8413 11 -\nPUMPS FOR DISPENSING FUEL OR LUBRICANTS, OF THE TYPE USED IN FILLING STATIONS OR IN\nGARAGES: OTHER :PUMPS FOR DISPENSING FUEL",
+        "hsn_code": "84131191",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - PUMPS FITTED OR DESIGNED TO BE FITTED WITH A MEASURING DEVICE: 8413 11 -\nPUMPS FOR DISPENSING FUEL OR LUBRICANTS, OF THE TYPE USED IN FILLING STATIONS OR IN\nGARAGES: OTHER :OTHER",
+        "hsn_code": "84131199",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - PUMPS FITTED OR DESIGNED TO BE FITTED WITH A MEASURING DEVICE: OTHER :-\nOTHER: HAND PUMPS",
+        "hsn_code": "84131910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - PUMPS FITTED OR DESIGNED TO BE FITTED WITH A MEASURING DEVICE: OTHER :-\nOTHER: OTHER",
+        "hsn_code": "84131990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - HAND PUMPS, OTHER THAN THOSE OF SUB-HEADING 8413 11 OR 8413 19",
+        "hsn_code": "84132000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - FUEL, LUBRICATING OR COOLING MEDIUM PUMPS FOR INTERNAL COMBUSTION\nPISTON ENGINES: INJECTION PUMPS FOR DIESEL ENGINES",
+        "hsn_code": "84133010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "- PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - FUEL, LUBRICATING OR COOLING MEDIUM PUMPS FOR INTERNAL COMBUSTION\nPISTON ENGINES: OIL PUMP",
+        "hsn_code": "84133020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "- PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - FUEL, LUBRICATING OR COOLING MEDIUM PUMPS FOR INTERNAL COMBUSTION\nPISTON ENGINES: WATER PUMP",
+        "hsn_code": "84133030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "- PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - FUEL, LUBRICATING OR COOLING MEDIUM PUMPS FOR INTERNAL COMBUSTION\nPISTON ENGINES: OTHER",
+        "hsn_code": "84133090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS CONCRETE PUMPS",
+        "hsn_code": "84134000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER RECIPROCATING POSITIVE DISPLACEMENT PUMPS: METERING AND DOSING\nPUMPS",
+        "hsn_code": "84135010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER RECIPROCATING POSITIVE DISPLACEMENT PUMPS: PRIMARILY DESIGNED\nFOR HANDLING WATER : DEEP TUBE WELL TURBINE PUMP",
+        "hsn_code": "84135021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER RECIPROCATING POSITIVE DISPLACEMENT PUMPS: PRIMARILY DESIGNED\nFOR HANDLING WATER : OTHER",
+        "hsn_code": "84135029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER RECIPROCATING POSITIVE DISPLACEMENT PUMPS: OTHER",
+        "hsn_code": "84135090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER ROTARY POSITIVE DISPLACEMENT PUMPS: GEAR TYPE PUMPS",
+        "hsn_code": "84136010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER ROTARY POSITIVE DISPLACEMENT PUMPS: SCREW TYPE PUMPS",
+        "hsn_code": "84136020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER ROTARY POSITIVE DISPLACEMENT PUMPS: OTHER",
+        "hsn_code": "84136090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER CENTRIFUGAL PUMPS: PRIMARILY DESIGNED TO HANDLE WATER",
+        "hsn_code": "84137010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER CENTRIFUGAL PUMPS: OTHER: SINGLE AND MULTISTAGE CHEMICAL\nPROCESS PUMPS",
+        "hsn_code": "84137091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER CENTRIFUGAL PUMPS: OTHER: HORIZONTAL SPLIT CASING PUMPS",
+        "hsn_code": "84137092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER CENTRIFUGAL PUMPS: OTHER: HORIZONTAL SELF PRIMING PUMPS",
+        "hsn_code": "84137093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER CENTRIFUGAL PUMPS: OTHER: VERTICAL TURBINE DRIVEN PUMPS",
+        "hsn_code": "84137094",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER CENTRIFUGAL PUMPS: OTHER: BOILER FEED PUMPS",
+        "hsn_code": "84137095",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER CENTRIFUGAL PUMPS: OTHER: SLURRY PUMPS",
+        "hsn_code": "84137096",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER CENTRIFUGAL PUMPS: OTHER: DREDGER PUMPS",
+        "hsn_code": "84137097",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER CENTRIFUGAL PUMPS: OTHER: OTHER",
+        "hsn_code": "84137099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER PUMPS-LIQUID ELEVATORS: 8413 81 - PUMPS : GAS PUMPS",
+        "hsn_code": "84138110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER PUMPS-LIQUID ELEVATORS: 8413 81 - PUMPS : HYDRAULIC RAM",
+        "hsn_code": "84138120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER PUMPS-LIQUID ELEVATORS: 8413 81 - PUMPS : AXIAL FLOW AND MIXED\nFLOW VERTICAL PUMP DESIGNED PRIMARILY FOR HANDLING WATER",
+        "hsn_code": "84138130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - OTHER PUMPS-LIQUID ELEVATORS: 8413 81 - LIQUID ELEVATORS",
+        "hsn_code": "84138200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID\nELEVATORS - PARTS: OF LIQUID ELEVATORS",
+        "hsn_code": "84139200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMPS FOR LIQUIDS, WHETHER OR NOT FITTED WITH A MEASURING DEVICE; LIQUID ELEVATORS",
+        "hsn_code": "8413",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TURBO-JETS, TURBO-PROPELLERS AND OTHER GAS TURBINES - TURBO-JETS: OF A THRUST NOT\nEXCEEDING 25KN",
+        "hsn_code": "84111100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TURBO-JETS, TURBO-PROPELLERS AND OTHER GAS TURBINES - TURBO-JETS: OF A THRUST\nEXCEEDING 25 KN",
+        "hsn_code": "84111200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TURBO-JETS, TURBO-PROPELLERS AND OTHER GAS TURBINES - TURBO-PROPELLERS: OF A\nPOWER NOT EXCEEDING 1,100 KW",
+        "hsn_code": "84112100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TURBO-JETS, TURBO-PROPELLERS AND OTHER GAS TURBINES - TURBO-PROPELLERS: OF A\nPOWER EXCEEDING 1,100 KW",
+        "hsn_code": "84112200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TURBO-JETS, TURBO-PROPELLERS AND OTHER GAS TURBINES - OTHER GAS TURBINES: OF A\nPOWER NOT EXCEEDING 5,000 KW",
+        "hsn_code": "84118100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TURBO-JETS, TURBO-PROPELLERS AND OTHER GAS TURBINES - OTHER GAS TURBINES:- OF A\nPOWER EXCEEDING 5,000 KW: OF POWER EXCEEDING 5,000 KW BUT NOT EXCEEDING 15,000\nKW",
+        "hsn_code": "84118210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TURBO-JETS, TURBO-PROPELLERS AND OTHER GAS TURBINES - OTHER GAS TURBINES:- OF A\nPOWER EXCEEDING 5,000 KW: OF POWER EXCEEDING 15,000 KW BUT NOT EXCEEDING 30,000\nKW",
+        "hsn_code": "84118220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TURBO-JETS, TURBO-PROPELLERS AND OTHER GAS TURBINES - OTHER GAS TURBINES:- OF A\nPOWER EXCEEDING 5,000 KW: OF POWER EXCEEDING 30,000 KW BUT NOT EXCEEDING 60,000\nKW",
+        "hsn_code": "84118230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TURBO-JETS, TURBO-PROPELLERS AND OTHER GAS TURBINES - OTHER GAS TURBINES:- OF A\nPOWER EXCEEDING 5,000 KW: OF POWER EXCEEDING 60,000 KW BUT NOT EXCEEDING 90,000\nKW",
+        "hsn_code": "84118240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TURBO-JETS, TURBO-PROPELLERS AND OTHER GAS TURBINES - OTHER GAS TURBINES:- OF A\nPOWER EXCEEDING 5,000 KW: OF POWER EXCEEDING 90,000 KW BUT NOT EXCEEDING 1,15,000\nKW",
+        "hsn_code": "84118250",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TURBO-JETS, TURBO-PROPELLERS AND OTHER GAS TURBINES - OTHER GAS TURBINES:- OF A\nPOWER EXCEEDING 5,000 KW: OF POWER EXCEEDING 1,15,000 KW",
+        "hsn_code": "84118260",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TURBO-JETS, TURBO-PROPELLERS AND OTHER GAS TURBINES - PARTS: OF TURBO-JETS OR TURBO\nPROPELLERS",
+        "hsn_code": "84119100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TURBO-JETS, TURBO-PROPELLERS AND OTHER GAS TURBINES - PARTS: OTHER",
+        "hsn_code": "84119900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "Turbo-jets, turbo-propellers and other gas turbines",
+        "hsn_code": "8411",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SPARK-IGNITION RECIPROCATING OR ROTARY INTERNAL COMBUSTION PISTON ENGINES\nAIRCRAFT ENGINES",
+        "hsn_code": "84071000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WALKING-STICKS, SEAT-STICKS, WHIPS, RIDING CROPS AND THE LIKE",
+        "hsn_code": "66020000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "USED OR NEW RAGS, SCRAP TWINE, CORDAGE, ROPE AND CABLES AND WORN OUT ARTICLES OF TWINE, CORDAGE, ROPE OR CABLES, OF TEXTILE MATERIALS",
+        "hsn_code": "6310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "USED OR NEW RAGS, SCRAP TWINE, CORDAGE, ROPE AND CABLES AND WORN OUT ARTICLES OF\nTWINE, CORDAGE, ROPE OR CABLES, OF TEXTILE MATERIALS OTHER: OTHER",
+        "hsn_code": "63109090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "USED OR NEW RAGS, SCRAP TWINE, CORDAGE, ROPE AND CABLES AND WORN OUT ARTICLES OF\nTWINE, CORDAGE, ROPE OR CABLES, OF TEXTILE MATERIALS OTHER: SYNTHETIC RAGS .",
+        "hsn_code": "63109040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "USED OR NEW RAGS, SCRAP TWINE, CORDAGE, ROPE AND CABLES AND WORN OUT ARTICLES OF\nTWINE, CORDAGE, ROPE OR CABLES, OF TEXTILE MATERIALS OTHER: GUNNY CUTTINGS",
+        "hsn_code": "63109030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "USED OR NEW RAGS, SCRAP TWINE, CORDAGE, ROPE AND CABLES AND WORN OUT ARTICLES OF\nTWINE, CORDAGE, ROPE OR CABLES, OF TEXTILE MATERIALS OTHER: COTTON RAGS",
+        "hsn_code": "63109020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "USED OR NEW RAGS, SCRAP TWINE, CORDAGE, ROPE AND CABLES AND WORN OUT ARTICLES OF\nTWINE, CORDAGE, ROPE OR CABLES, OF TEXTILE MATERIALS OTHER: WOOLEN RAGS",
+        "hsn_code": "63109010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "USED OR NEW RAGS, SCRAP TWINE, CORDAGE, ROPE AND CABLES AND WORN OUT ARTICLES OF\nTWINE, CORDAGE, ROPE OR CABLES, OF TEXTILE MATERIALS SORTED: OTHER",
+        "hsn_code": "63101090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "USED OR NEW RAGS, SCRAP TWINE, CORDAGE, ROPE AND CABLES AND WORN OUT ARTICLES OF\nTWINE, CORDAGE, ROPE OR CABLES, OF TEXTILE MATERIALS SORTED: GUNNY CUTTINGS",
+        "hsn_code": "63101030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "USED OR NEW RAGS, SCRAP TWINE, CORDAGE, ROPE AND CABLES AND WORN OUT ARTICLES OF\nTWINE, CORDAGE, ROPE OR CABLES, OF TEXTILE MATERIALS SORTED: COTTON RAGS",
+        "hsn_code": "63101020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "USED OR NEW RAGS, SCRAP TWINE, CORDAGE, ROPE AND CABLES AND WORN OUT ARTICLES OF\nTWINE, CORDAGE, ROPE OR CABLES, OF TEXTILE MATERIALS SORTED: WOOLLEN RAGS",
+        "hsn_code": "63101010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "Worn clothing and other worn articles",
+        "hsn_code": "6309",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WORN CLOTHING AND OTHER WORN ARTICLES",
+        "hsn_code": "63090000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "Sets consisting of woven fabric and yarn, whether or not with accessories, for making up into rugs, tapestries, embroidered table cloths or serviettes, or similar textile articles, put up in packings for retail sale",
+        "hsn_code": "6308",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP ARTICLES, INCLUDING DRESS PATTERNS",
+        "hsn_code": "6307",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TARPAULINS, AWNINGS AND SUNBLINDS; TENTS; SAILS FOR BOATS, SAILBOARDS OR LANDCRAFT; CAMPING GOODS",
+        "hsn_code": "6306",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNISHING ARTICLES, EXCLUDING THOSE OF HEADING 9404",
+        "hsn_code": "6304",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CURTAINS (INCLUDING DRAPES) AND INTERIOR BLINDS: CURTAIN OR BED VALANCES",
+        "hsn_code": "6303",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN",
+        "hsn_code": "6302",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BLANKETS AND TRAVELLING RUGS",
+        "hsn_code": "6301",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SETS CONSISTING OF WOVEN FABRIC AND YARN, WHETHER OR NOT WITH ACCESSORIES, FOR\nMAKING UP INTO RUGS,TAPESTRIES, EMBROIDERED TABLE CLOTHS OR SERVIETTES,OR SIMILAR\nTEXTILE ARTICLES, PUT UP IN PACKINGSFOR RETAIL SALE",
+        "hsn_code": "63080000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP ARTICLES, INCLUDING DRESS PATTERNS OTHER: OTHER",
+        "hsn_code": "63079090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP ARTICLES, INCLUDING DRESS PATTERNS OTHER:MADE UP ARTICLES OF COTTON",
+        "hsn_code": "63079020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP ARTICLES, INCLUDING DRESS PATTERNS OTHER: DRESS MATERIALS HAND\nPRINTED:OTHER",
+        "hsn_code": "63079019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP ARTICLES, INCLUDING DRESS PATTERNS OTHER: DRESS MATERIALS HAND\nPRINTED: OF MAN-MADE FIBRES",
+        "hsn_code": "63079013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP ARTICLES, INCLUDING DRESS PATTERNS OTHER: DRESS MATERIALS HAND\nPRINTED:OF SILK",
+        "hsn_code": "63079012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP ARTICLES, INCLUDING DRESS PATTERNS OTHER: DRESS MATERIALS HAND\nPRINTED: OF COTTON",
+        "hsn_code": "63079011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP ARTICLES, INCLUDING DRESS PATTERNS LIFE-JACKETS AND LIFE-BELTS:OTHER",
+        "hsn_code": "63072090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP ARTICLES, INCLUDING DRESS PATTERNS LIFE-JACKETS AND LIFE-BELTS:OF\nCOTTON",
+        "hsn_code": "63072010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP ARTICLES, INCLUDING DRESS PATTERNS FLOOR-CLOTHS, DISH-CLOTHS,\nDUSTERS AND SIMILAR CLEANING CLOTHS: OTHER",
+        "hsn_code": "63071090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FLOOR CLOTHES, DISH CLOTHES, DUSTERS AND SIMILAR CLEANING CLOTHES - OF\nCOTTON,HANDLOOM",
+        "hsn_code": "63071030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP ARTICLES, INCLUDING DRESS PATTERNS FLOOR-CLOTHS, DISH-CLOTHS,\nDUSTERS AND SIMILAR CLEANING CLOTHS: OF MAN-MADE FIBRES",
+        "hsn_code": "63071020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP ARTICLES, INCLUDING DRESS PATTERNS FLOOR-CLOTHS, DISH-CLOTHS,\nDUSTERS AND SIMILAR CLEANING CLOTHS: OF COTTON",
+        "hsn_code": "63071010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TARPAULINS, AWNINGS AND SUNBLINDS;TENTS;SAILS FOR BOATS,SAILBOARDS OR\nLANDCRAFT;CAMPING GOODS-OTHER---OF OTHER TEXTILE MATERIALS",
+        "hsn_code": "63069090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TARPAULINS, AWNINGS AND SUNBLINDS;TENTS;SAILS FOR BOATS,SAILBOARDS OR\nLANDCRAFT;CAMPING GOODS-OTHER---OF COTTON",
+        "hsn_code": "63069010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TARPAULINS, AWNINGS AND SUNBLINDS; TENTS; SAILS FOR BOATS, SAILBOARDS OR\nLANDCRAFT; CAMPING GOODS PNEUMATIC MATTRESSES: OF OTHER TEXTILE MATERIALS",
+        "hsn_code": "63064900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TARPAULINS, AWNINGS AND SUNBLINDS; TENTS; SAILS FOR BOATS, SAILBOARDS OR\nLANDCRAFT; CAMPING GOODS PNEUMATIC MATTRESSES: OF COTTON",
+        "hsn_code": "63064100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PNEUMATIC MATTRESSES",
+        "hsn_code": "63064000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TARPAULINS, AWNINGS AND SUNBLINDS; TENTS; SAILS FOR BOATS, SAILBOARDS OR\nLANDCRAFT; CAMPING GOODS SAILS: OF OTHER TEXTILE MATERIALS: OTHER",
+        "hsn_code": "63063990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TARPAULINS, AWNINGS AND SUNBLINDS; TENTS; SAILS FOR BOATS, SAILBOARDS OR\nLANDCRAFT; CAMPING GOODS SAILS: OF OTHER TEXTILE MATERIALS: OF COTTON",
+        "hsn_code": "63063910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TARPAULINS, AWNINGS AND SUNBLINDS; TENTS; SAILS FOR BOATS, SAILBOARDS OR\nLANDCRAFT; CAMPING GOODS SAILS: OF SYNTHETIC FIBRES",
+        "hsn_code": "63063100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SAILS",
+        "hsn_code": "63063000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHERS",
+        "hsn_code": "63062990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TARPAULINS, AWNINGS AND SUNBLINDS; TENTS; SAILS FOR BOATS, SAILBOARDS OR\nLANDCRAFT; CAMPING GOODS TENTS: OF OTHER TEXTILE MATERIALS: OF JUTE",
+        "hsn_code": "63062910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TARPAULINS, AWNINGS AND SUNBLINDS; TENTS; SAILS FOR BOATS, SAILBOARDS OR\nLANDCRAFT; CAMPING GOODS TENTS: OF SYNTHETIC FIBRES",
+        "hsn_code": "63062200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TARPAULINS, AWNINGS AND SUNBLINDS; TENTS; SAILS FOR BOATS, SAILBOARDS OR\nLANDCRAFT; CAMPING GOODS TENTS: OF COTTON",
+        "hsn_code": "63062100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TARPAULINS, AWNINGS AND SUNBLINDS; TENTS; SAILS FOR BOATS, SAILBOARDS OR\nLANDCRAFT; CAMPING GOODS TARPAULINS, AWNINGS AND SUNBLINDS:OF OTHER TEXTILE\nMATERIALS: OTHER",
+        "hsn_code": "63061990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TARPAULINS, AWNINGS AND SUNBLINDS; TENTS; SAILS FOR BOATS, SAILBOARDS OR\nLANDCRAFT; CAMPING GOODS TARPAULINS, AWNINGS AND SUNBLINDS:OF OTHER TEXTILE\nMATERIALS: VENETIAN OR AUSTRIAN BLINDS",
+        "hsn_code": "63061930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TARPAULINS, AWNINGS AND SUNBLINDS; TENTS; SAILS FOR BOATS, SAILBOARDS OR\nLANDCRAFT; CAMPING GOODS TARPAULINS, AWNINGS AND SUNBLINDS:OF OTHER TEXTILE\nMATERIALS: BLINDS OR AWNINGS OF COIR",
+        "hsn_code": "63061920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TARPAULINS, AWNINGS AND SUNBLINDS; TENTS; SAILS FOR BOATS, SAILBOARDS OR\nLANDCRAFT; CAMPING GOODS TARPAULINS, AWNINGS AND SUNBLINDS:OF OTHER TEXTILE\nMATERIALS: JUTE TARPAULINS (INCLUDING DW TARPAULIN)",
+        "hsn_code": "63061910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TARPAULINS, AWNINGS AND SUNBLINDS; TENTS; SAILS FOR BOATS, SAILBOARDS OR\nLANDCRAFT; CAMPING GOODS TARPAULINS, AWNINGS AND SUNBLINDS: OF SYNTHETIC FIBRES",
+        "hsn_code": "63061200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TARPAULINS, AWNINGS AND SUNBLINDS; TENTS; SAILS FOR BOATS, SAILBOARDS OR\nLANDCRAFT; CAMPING GOODS TARPAULINS, AWNINGS AND SUNBLINDS: OF COTTON",
+        "hsn_code": "63061100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNISHING ARTICLES - NOT KNITTED OR CROCHETED, OF OTHER TEXTILE MATERIAL -\nOTHER THAN OF SILK, HANDLOOM OR",
+        "hsn_code": "63049999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNISHING ARTICLES - NOT KNITTED OR CROCHETED, OF OTHER TEXTILE MATERIAL - OF\nWOOL, HANDLOOM",
+        "hsn_code": "63049992",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNISHING ARTICLES - NOT KNITTED OR CROCHETED, OF OTHER TEXTILE MATERIAL - OF\nSILK, HANDLOOM",
+        "hsn_code": "63049991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNISHING ARTICLES, EXCLUDING THOSE OF HEADING 9404 OTHER: NOT KNITTED OR\nCROCHETED, OF OTHER TEXTILE MATERIAL: SILK CUSHION COVERS",
+        "hsn_code": "63049910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNISHING ARTICLES, EXCLUDING THOSE OF HEADING 9404 OTHER: NOT KNITTED OR\nCROCHETED, OF SYNTHETIC FIBRES",
+        "hsn_code": "63049300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNISHING ARTICLES - OTHER THAN HANDLOOM",
+        "hsn_code": "63049299",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNISHING ARTICLES OF HANDLOOM",
+        "hsn_code": "63049291",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CUSHION COVERS - OTHER THAN HANDLOOM",
+        "hsn_code": "63049289",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CUSHION COVERS OF HANDLOOM",
+        "hsn_code": "63049281",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNISHING ARTICLES, EXCLUDING THOSE OF HEADING 9404 OTHER: NOT KNITTED OR\nCROCHETED, OF COTTON: MOSQUITO NETS",
+        "hsn_code": "63049270",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNISHING ARTICLES, EXCLUDING THOSE OF HEADING 9404 OTHER: NOT KNITTED OR\nCROCHETED, OF COTTON: TOWELS, OTHER THAN TERRY TOWEL",
+        "hsn_code": "63049260",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNISHING ARTICLES, EXCLUDING THOSE OF HEADING 9404 OTHER: NOT KNITTED OR\nCROCHETED, OF COTTON: TERRY TOWEL",
+        "hsn_code": "63049250",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TABLE CLOTH AND TABLE COVERS - OTHER THAN HANDLOOM",
+        "hsn_code": "63049249",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TABLE CLOTH AND TABLE COVERS - OF HANDLOOM",
+        "hsn_code": "63049241",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PILLOW CASES AND PILLOW SLIPS - OTHER THAN HANDLOOM",
+        "hsn_code": "63049239",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PILLOW CASES AND PILLOW SLIPS - OF HANDLOOM",
+        "hsn_code": "63049231",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NAPKINS - OTHER THAN HANDLOOM",
+        "hsn_code": "63049229",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NAPKINS OF HANDLOOM",
+        "hsn_code": "63049221",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COUNTERPANES - OTHER THAN HANDLOOM",
+        "hsn_code": "63049219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COUNTERPANES OF HANDLOOM",
+        "hsn_code": "63049211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNISHING ARTICLES, EXCLUDING THOSE OF HEADING 9404 OTHER: KNITTED OR\nCROCHETED: OTHER",
+        "hsn_code": "63049190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNISHING ARTICLES, EXCLUDING THOSE OF HEADING 9404 OTHER: KNITTED OR\nCROCHETED:WOOLLEN CUSHION COVER",
+        "hsn_code": "63049120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNISHING ARTICLES, EXCLUDING THOSE OF HEADING 9404 OTHER: KNITTED OR\nCROCHETED: SILK BELT",
+        "hsn_code": "63049110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNISHING ARTICLES, EXCLUDING THOSE OF HEADING 9404 BEDSPREADS: OTHER:\nOTHER",
+        "hsn_code": "63041990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED SHEETS AND BED COVERS OF COTTON, HANDLOOM",
+        "hsn_code": "63041940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNISHING ARTICLES, EXCLUDING THOSE OF HEADING 9404 BEDSPREADS: OTHER:\nBEDSHEETS AND BEDCOVERS OF MAN-MADE FIBRES",
+        "hsn_code": "63041930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNISHING ARTICLES, EXCLUDING THOSE OF HEADING 9404 BEDSPREADS: OTHER:\nBEDSPREADS OF SILK",
+        "hsn_code": "63041920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNISHING ARTICLES, EXCLUDING THOSE OF HEADING 9404 BEDSPREADS: OTHER:\nBEDSHEETS AND BEDCOVERS, OF COTTON",
+        "hsn_code": "63041910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNISHING ARTICLES, EXCLUDING THOSE OF HEADING 9404 BEDSPREADS: KNITTED OR\nCROCHETED",
+        "hsn_code": "63041100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CURTAINS (INCLUDING DRAPES) AND INTERIOR BLINDS; CURTAIN OR BED VALANCES OTHER: OF\nOTHER TEXTILE MATERIALS: OTHER",
+        "hsn_code": "63039990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CURTAINS (INCLUDING DRAPES) AND INTERIOR BLINDS; CURTAIN OR BED VALANCES OTHER: OF\nOTHER TEXTILE MATERIALS: SILK SHOWER CURTAINS",
+        "hsn_code": "63039910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CURTAINS (INCLUDING DRAPES) AND INTERIOR BLINDS; CURTAIN OR BED VALANCES OTHER: OF\nSYNTHETIC FIBRES",
+        "hsn_code": "63039200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CURTAINS (INCLUDING DRAPES) AND INTERIOR BLINDS; CURTAIN OR BED VALANCES OTHER: OF\nCOTTON",
+        "hsn_code": "63039100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CURTAINS (INCLUDING DRAPES) AND INTERIOR BLINDS; CURTAIN OR BED VALANCES KNITTED OR\nCROCHETED: OF OTHER TEXTILE MATERIALS",
+        "hsn_code": "63031900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CURTAINS (INCLUDING DRAPES) AND INTERIOR BLINDS; CURTAIN OR BED VALANCES KNITTED OR\nCROCHETED: OF SYNTHETIC FIBRES",
+        "hsn_code": "63031200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CURTAINS (INCLUDING DRAPES) AND INTERIOR BLINDS; CURTAIN OR BED VALANCES KNITTED OR\nCROCHETED: OF COTTON",
+        "hsn_code": "63031100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN OTHER: OF OTHER TEXTILE\nMATERIALS",
+        "hsn_code": "63029900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN OTHER: OF MAN-MADE FIBRES",
+        "hsn_code": "63029300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN OTHER: OF FLAX",
+        "hsn_code": "63029200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN - OF COTTON - OTHER THAN\nHANDLOOM",
+        "hsn_code": "63029190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN - OF COTTON - HANDLOOM",
+        "hsn_code": "63029110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TOILET LINEN AND KITCHEN LINEN, OF TERRY TOWELLING OR SIMILAR TERRY FABRICS, OF\nCOTTON - OTHER",
+        "hsn_code": "63026090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TOILET LINEN AND KITCHEN LINEN, OF TERRY TOWELLING OR SIMILAR TERRY FABRICS, OF\nCOTTON - OF HANDLOOM",
+        "hsn_code": "63026010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN OTHER TABLE LINEN: OF OTHER\nTEXTILE MATERIALS",
+        "hsn_code": "63025900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN OTHER TABLE LINEN: OF MAN\nMADE FIBRES",
+        "hsn_code": "63025300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN OTHER TABLE LINEN: OF FLAX",
+        "hsn_code": "63025200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER TABLE LINEN- OF COTTON - OTHER",
+        "hsn_code": "63025190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER TABLE LINEN - OF COTTON - OF HANDLOOM",
+        "hsn_code": "63025110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN TABLE LINEN, KNITTED OR\nCROCHETED: OTHER",
+        "hsn_code": "63024090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN TABLE LINEN, KNITTED OR\nCROCHETED: OF MAN-MADE FIBRES",
+        "hsn_code": "63024040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN TABLE LINEN, KNITTED OR\nCROCHETED: OF COTTON",
+        "hsn_code": "63024030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN TABLE LINEN, KNITTED OR\nCROCHETED: OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "63024020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN TABLE LINEN, KNITTED OR\nCROCHETED: OF SILK",
+        "hsn_code": "63024010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN OTHER BED LINEN: OF OTHER\nTEXTILE MATERIALS",
+        "hsn_code": "63023900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN OTHER BED LINEN: OF MAN-MADE\nFIBRES",
+        "hsn_code": "63023200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN OTHER BED LINEN: OF COTTON",
+        "hsn_code": "63023100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN OTHER BED LINEN, PRINTED: OF\nOTHER TEXTILE MATERIALS",
+        "hsn_code": "63022900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN OTHER BED LINEN, PRINTED: OF\nMAN-MADE FIBRES",
+        "hsn_code": "63022200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN - OF COTTON - OTHER",
+        "hsn_code": "63022190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN - OF COTTON - OF HANDLOOM",
+        "hsn_code": "63022110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN BED LINEN, KNITTED OR\nCROCHETED: OTHER",
+        "hsn_code": "63021090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BED LINEN, TABLE LINEN, TOILET LINEN AND KITCHEN LINEN BED LINEN, KNITTED OR\nCROCHETED: OF COTTON",
+        "hsn_code": "63021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BLANKETS AND TRAVELLING RUGS OTHER BLANKETS AND TRAVELLING RUGS: OTHER",
+        "hsn_code": "63019090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BLANKETS AND TRAVELLING RUGS OTHER BLANKETS AND TRAVELLING RUGS:JUTE BLANKETS\nINCLUDING BLANKETS OF BLENDED JUTE",
+        "hsn_code": "63019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BLANKETS AND TRAVELLING RUGS BLANKETS (OTHER THAN ELECTRIC BLANKETS) AND\nTRAVELLING RUGS, OF SYNTHETIC FIBRES",
+        "hsn_code": "63014000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BLANKETS AND TRAVELLING RUGS BLANKETS (OTHER THAN ELECTRIC BLANKETS) AND\nTRAVELLING RUGS, OF COTTON",
+        "hsn_code": "63013000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BLANKETS AND TRAVELLING RUGS BLANKETS (OTHER THAN ELECTRIC BLANKETS) AND\nTRAVELLING RUGS, OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "63012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BLANKETS AND TRAVELLING RUGS ELECTRIC BLANKETS",
+        "hsn_code": "63011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HATS AND OTHER HEADGEAR, KNITTED OR  CROCHETED, OR MADE UP FROM LACE, FELT OR OTHER TEXTILE FABRIC, IN THE PIECE  (BUT NOT IN STRIPS), WHETHER OR NOT LINED  OR TRIMMED; HAIR-NETS OF ANY MATERIAL, WHETHER OR NOT LINED OR TRIMMED",
+        "hsn_code": "6505",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HATS AND OTHER HEADGEAR, KNITTED OR CROCHETED, OR MADE UP FROM LACE, FELT OR\nOTHER TEXTILE FABRIC, IN THE PIECE (BUT NOT IN STRIPS), WHETHER OR NOT LINED OR\nTRIMMED; HAIR-NETS OF ANY MATERIAL, WHETHER OR NOT LINED OR TRIMME---OTHER",
+        "hsn_code": "65050090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HATS AND OTHER HEADGEAR, KNITTED OR CROCHETED, OR MADE UP FROM LACE, FELT OR\nOTHER TEXTILE FABRIC, IN THE PIECE (BUT NOT IN STRIPS), WHETHER OR NOT LINED OR\nTRIMMED; HAIR-NETS OF ANY MATERIAL, WHETHER OR NOT LINED OR TRIMMED--- HAIR-NETS",
+        "hsn_code": "65050010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HAT-FORMS, HAT BODIES AND HOODS OF FELT, NEITHER BLOCKED TO SHAPE NOR WITH MADE\nBRIMS; PLATEAUX AND MANCHONS (INCLUDING SLIT MANCHONS), OF FELT HAT-FORMS, HAT\nBODIES AND HOODS OF FELT, NEITHER BLOCKED TO SHAPE NOR WITH MADE BRIMS; PLATEAUX\nAND MANCHONS (INCLUDING SLIT MANCHONS), OF FELT: OTHER",
+        "hsn_code": "65010090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HAT-FORMS, HAT BODIES AND HOODS OF FELT, NEITHER BLOCKED TO SHAPE NOR WITH MADE\nBRIMS; PLATEAUX AND MANCHONS (INCLUDING SLIT MANCHONS), OF FELT HAT-FORMS, HAT\nBODIES AND HOODS OF FELT, NEITHER BLOCKED TO SHAPE NOR WITH MADE BRIMS; PLATEAUX\nAND MANCHONS (INCLUDING SLIT MANCHONS), OF FELT: OF WOOL AND FUR FELT",
+        "hsn_code": "65010020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HAT-FORMS, HAT BODIES AND HOODS OF FELT, NEITHER BLOCKED TO SHAPE NOR WITH MADE\nBRIMS; PLATEAUX AND MANCHONS (INCLUDING SLIT MANCHONS), OF FELT HAT-FORMS, HAT\nBODIES AND HOODS OF FELT, NEITHER BLOCKED TO SHAPE NOR WITH MADE BRIMS; PLATEAUX\nAND MANCHONS (INCLUDING SLIT MANCHONS), OF FELT: OF COTTON",
+        "hsn_code": "65010010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CARCOATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWIND-CHEATERS, WINDJACKETS AND SIMILAR ARTICLES, KNITTED OR CROCHETED, OTHER THAN\nTHOSE OF HEADING 6103 - OF WOOL OR FINE ANIMAL HAIR : OF WOOL",
+        "hsn_code": "61011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CARCOATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWIND-CHEATERS, WINDJACKETS AND SIMILAR ARTICLES, KNITTED OR CROCHETED, OTHER THAN\nTHOSE OF HEADING 6103 - OF WOOL OR FINE ANIMAL HAIR : OF KASHMIR (CASHMERE) GOATS",
+        "hsn_code": "61011020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CARCOATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWIND-CHEATERS, WINDJACKETS AND SIMILAR ARTICLES, KNITTED OR CROCHETED, OTHER THAN\nTHOSE OF HEADING 6103 - OF WOOL OR FINE ANIMAL HAIR : OTHER",
+        "hsn_code": "61011090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CARCOATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWIND-CHEATERS, WINDJACKETS AND SIMILAR ARTICLES, KNITTED OR CROCHETED, OTHER THAN\nTHOSE OF HEADING 6103 - OF COTTON",
+        "hsn_code": "61012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CARCOATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWIND-CHEATERS, WINDJACKETS AND SIMILAR ARTICLES, KNITTED OR CROCHETED, OTHER THAN\nTHOSE OF HEADING 6103 - OF MAN-MADE FIBRES : OF SYNTHETIC FIBRES",
+        "hsn_code": "61013010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CARCOATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWIND-CHEATERS, WINDJACKETS AND SIMILAR ARTICLES, KNITTED OR CROCHETED, OTHER THAN\nTHOSE OF HEADING 6103 - OF MAN-MADE FIBRES : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61013020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CARCOATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWIND-CHEATERS, WINDJACKETS AND SIMILAR ARTICLES, KNITTED OR CROCHETED, OTHER THAN\nTHOSE OF HEADING 6103 - OTHER : OF SILK",
+        "hsn_code": "61019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CARCOATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWIND-CHEATERS, WINDJACKETS AND SIMILAR ARTICLES, KNITTED OR CROCHETED, OTHER THAN\nTHOSE OF HEADING 6103 - OTHER : OTHER",
+        "hsn_code": "61019090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING\nSKIJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, KNITTED OR CROCHETED,\nOTHER THAN THOSE OF HEADING 6104 - OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING\nSKIJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, KNITTED OR CROCHETED,\nOTHER THAN THOSE OF HEADING 6104 - OF COTTON",
+        "hsn_code": "61022000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING\nSKIJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, KNITTED OR CROCHETED,\nOTHER THAN THOSE OF HEADING 6104 - OF MAN-MADE FIBRES : OF SYNTHETIC FIBRES",
+        "hsn_code": "61023010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING\nSKIJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, KNITTED OR CROCHETED,\nOTHER THAN THOSE OF HEADING 6104 - OF MAN-MADE FIBRES : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61023020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING\nSKIJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, KNITTED OR CROCHETED,\nOTHER THAN THOSE OF HEADING 6104 - OTHER : OF SILK",
+        "hsn_code": "61029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING\nSKIJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, KNITTED OR CROCHETED,\nOTHER THAN THOSE OF HEADING 6104 - OTHER : OTHER",
+        "hsn_code": "61029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUITS OF SILK",
+        "hsn_code": "61031010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUITS OF COTTON",
+        "hsn_code": "61031020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUITS OF ARTIFICIAL FIBRES",
+        "hsn_code": "61031030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUITS",
+        "hsn_code": "61031090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - SUITS : OF\nWOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61031100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - SUITS : OF\nSYNTHETIC FIBRES",
+        "hsn_code": "61031200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - SUITS :OTHER :\nOF SILK",
+        "hsn_code": "61031910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - SUITS :OTHER :\nOF COTTON",
+        "hsn_code": "61031920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - SUITS :OTHER :\nOF ARTIFICIAL FIBRES",
+        "hsn_code": "61031930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - SUITS :OTHER :\nOTHER",
+        "hsn_code": "61031990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - ENSEMBLES : OF\nWOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61032100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEN OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - ENSEMBLES : OF\nCOTTON",
+        "hsn_code": "61032200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - ENSEMBLES : OF\nSYNTHETIC FIBRES",
+        "hsn_code": "61032300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - ENSEMBLES :OF\nOTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61032910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - ENSEMBLES :OF\nOTHER TEXTILE MATERIALS : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61032920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - ENSEMBLES :OF\nOTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "61032990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - JACKETS AND\nBLAZERS : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61033100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - JACKETS AND\nBLAZERS : OF COTTON",
+        "hsn_code": "61033200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - JACKETS AND\nBLAZERS : OF SYNTHETIC FIBRES",
+        "hsn_code": "61033300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - JACKETS AND\nBLAZERS :OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61033910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - JACKETS AND\nBLAZERS :OF OTHER TEXTILE MATERIALS : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61033920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - JACKETS AND\nBLAZERS :OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "61033990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - TROUSERS, BIB\nAND BRACE OVERALLS, BREECHES AND SHORTS : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61034100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - TROUSERS, BIB\nAND BRACE OVERALLS, BREECHES AND SHORTS : OF COTTON",
+        "hsn_code": "61034200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - TROUSERS, BIB\nAND BRACE OVERALLS, BREECHES AND SHORTS : OF SYNTHETIC FIBRES",
+        "hsn_code": "61034300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - TROUSERS, BIB\nAND BRACE OVERALLS, BREECHES AND SHORTS :OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61034910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - TROUSERS, BIB\nAND BRACE OVERALLS, BREECHES AND SHORTS :OF OTHER TEXTILE MATERIALS : OF ARTIFICIAL\nFIBRES",
+        "hsn_code": "61034920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED - TROUSERS, BIB\nAND BRACE OVERALLS, BREECHES AND SHORTS :OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "61034990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - SUITS : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61041100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - SUITS : OF COTTON",
+        "hsn_code": "61041200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - SUITS : OF SYNTHETIC FIBRES",
+        "hsn_code": "61041300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - SUITS :OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61041910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - SUITS :OF OTHER TEXTILE MATERIALS : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61041920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - SUITS :OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "61041990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - ENSEMBLES : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61042100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - ENSEMBLES : OF COTTON",
+        "hsn_code": "61042200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - ENSEMBLES : OF SYNTHETIC FIBRES",
+        "hsn_code": "61042300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - ENSEMBLES :OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61042910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - ENSEMBLES :OF OTHER TEXTILE MATERIALS : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61042920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - ENSEMBLES :OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "61042990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - JACKETS AND BLAZERS : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61043100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - JACKETS AND BLAZERS : OF COTTON",
+        "hsn_code": "61043200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - JACKETS AND BLAZERS : OF SYNTHETIC FIBRES",
+        "hsn_code": "61043300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - JACKETS AND BLAZERS :OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61043910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - JACKETS AND BLAZERS :OF OTHER TEXTILE MATERIALS : OF ARTIFICIAL\nFIBRES",
+        "hsn_code": "61043920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - JACKETS AND BLAZERS :OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "61043990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - DRESSES : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61044100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - DRESSES : OF COTTON",
+        "hsn_code": "61044200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - DRESSES : OF SYNTHETIC FIBRES",
+        "hsn_code": "61044300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - DRESSES : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61044400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - DRESSES :OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61044910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - DRESSES :OF OTHER TEXTILE MATERIALS : OF OTHER FIBRES",
+        "hsn_code": "61044990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - SKIRTS AND DIVIDED SKIRTS : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61045100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - SKIRTS AND DIVIDED SKIRTS : OF COTTON",
+        "hsn_code": "61045200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - SKIRTS AND DIVIDED SKIRTS : OF SYNTHETIC FIBRES",
+        "hsn_code": "61045300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - SKIRTS AND DIVIDED SKIRTS :OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61045910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - SKIRTS AND DIVIDED SKIRTS :OF OTHER TEXTILE MATERIALS : OF\nARTIFICIAL FIBRES",
+        "hsn_code": "61045920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - SKIRTS AND DIVIDED SKIRTS :OF OTHER TEXTILE MATERIALS : OF\nOTHER FIBRES",
+        "hsn_code": "61045990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - TROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS : OF\nWOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61046100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - TROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS : OF\nCOTTON",
+        "hsn_code": "61046200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - TROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS : OF\nSYNTHETIC FIBRES",
+        "hsn_code": "61046300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - TROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS :OF\nOTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61046910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - TROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS :OF\nOTHER TEXTILE MATERIALS : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61046920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR),\nKNITTED OR CROCHETED - TROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS :OF\nOTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "61046990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SHIRTS, KNITTED OR CROCHETED - OF COTTON : SHIRTS, HAND CROCHETED",
+        "hsn_code": "61051010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SHIRTS, KNITTED OR CROCHETED - OF COTTON : KNIT SHIRTS (OTHER THAN T\nSHIRTS) AND SWEAT SHIRTS, OTHER THAN HAND CROCHETED",
+        "hsn_code": "61051020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SHIRTS, KNITTED OR CROCHETED - OF COTTON : OTHER",
+        "hsn_code": "61051090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SHIRTS, KNITTED OR CROCHETED - OF MAN-MADE FIBRES : OF SYNTHETIC FIBRES",
+        "hsn_code": "61052010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SHIRTS, KNITTED OR CROCHETED - OF MAN-MADE FIBRES : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61052020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SHIRTS, KNITTED OR CROCHETED - OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61059010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SHIRTS, KNITTED OR CROCHETED - OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "61059090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS BLOUSES, SHIRTS AND SHIRT-BLOUSES, KNITTED OR CROCHETED - OF\nCOTTON",
+        "hsn_code": "61061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS BLOUSES, SHIRTS AND SHIRT-BLOUSES, KNITTED OR CROCHETED - OF MAN\nMADE FIBRES : OF SYNTHETIC FIBRES",
+        "hsn_code": "61062010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS BLOUSES, SHIRTS AND SHIRT-BLOUSES, KNITTED OR CROCHETED - OF MAN\nMADE FIBRES : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61062020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS BLOUSES, SHIRTS AND SHIRT-BLOUSES, KNITTED OR CROCHETED - OF OTHER\nTEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61069010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS BLOUSES, SHIRTS AND SHIRT-BLOUSES, KNITTED OR CROCHETED - OF OTHER\nTEXTILE MATERIALS : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61069020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS BLOUSES, SHIRTS AND SHIRT-BLOUSES, KNITTED OR CROCHETED - OF OTHER\nTEXTILE MATERIALS : OTHER",
+        "hsn_code": "61069090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS\nAND SIMILAR ARTICLES, KNITTED OR CROCHETED - UNDERPANTS AND BRIEFS : OF COTTON",
+        "hsn_code": "61071100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS\nAND SIMILAR ARTICLES, KNITTED OR CROCHETED - UNDERPANTS AND BRIEFS : OF MAN-MADE\nFIBRES : OF SYNTHETIC FIBRES",
+        "hsn_code": "61071210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS\nAND SIMILAR ARTICLES, KNITTED OR CROCHETED - UNDERPANTS AND BRIEFS : OF MAN-MADE\nFIBRES : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61071220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS\nAND SIMILAR ARTICLES, KNITTED OR CROCHETED - UNDERPANTS AND BRIEFS :OF OTHER TEXTILE\nMATERIALS : OF SILK",
+        "hsn_code": "61071910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS\nAND SIMILAR ARTICLES, KNITTED OR CROCHETED - UNDERPANTS AND BRIEFS :OF OTHER TEXTILE\nMATERIALS : OTHER",
+        "hsn_code": "61071990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS\nAND SIMILAR ARTICLES, KNITTED OR CROCHETED - NIGHT SHIRTS AND PYJAMAS : OF COTTON",
+        "hsn_code": "61072100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS\nAND SIMILAR ARTICLES, KNITTED OR CROCHETED - NIGHT SHIRTS AND PYJAMAS :OF MAN-MADE\nFIBRES : OF SYNTHETIC FIBRES",
+        "hsn_code": "61072210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS\nAND SIMILAR ARTICLES, KNITTED OR CROCHETED - NIGHT SHIRTS AND PYJAMAS :OF MAN-MADE\nFIBRES : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61072220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS\nAND SIMILAR ARTICLES, KNITTED OR CROCHETED - NIGHT SHIRTS AND PYJAMAS :OF OTHER\nTEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61072910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS\nAND SIMILAR ARTICLES, KNITTED OR CROCHETED - NIGHT SHIRTS AND PYJAMAS :OF OTHER\nTEXTILE MATERIALS : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61072920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS\nAND SIMILAR ARTICLES, KNITTED OR CROCHETED - NIGHT SHIRTS AND PYJAMAS :OF OTHER\nTEXTILE MATERIALS : OTHER",
+        "hsn_code": "61072990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS\nAND SIMILAR ARTICLES, KNITTED OR CROCHETED - OTHER :OF COTTON : GENGIS (VESTS), OTHER\nTHAN HAND CROCHETED",
+        "hsn_code": "61079110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS\nAND SIMILAR ARTICLES, KNITTED OR CROCHETED - OTHER :OF COTTON : OTHER",
+        "hsn_code": "61079190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS\nAND SIMILAR ARTICLES, KNITTED OR CROCHETED - OTHER :OF MAN-MADE FIBRES : OF\nSYNTHETIC FIBRES",
+        "hsn_code": "61079210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS\nAND SIMILAR ARTICLES, KNITTED OR CROCHETED - OTHER :OF MAN-MADE FIBRES : OF\nARTIFICIAL FIBRES",
+        "hsn_code": "61079220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS\nAND SIMILAR ARTICLES, KNITTED OR CROCHETED - OTHER :OF OTHER TEXTILE MATERIALS : OF\nSILK",
+        "hsn_code": "61079910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS\nAND SIMILAR ARTICLES, KNITTED OR CROCHETED - OTHER :OF OTHER TEXTILE MATERIALS : OF\nWOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61079920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS\nAND SIMILAR ARTICLES, KNITTED OR CROCHETED - OTHER :OF OTHER TEXTILE MATERIALS :\nOTHER",
+        "hsn_code": "61079990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - SLIPS AND\nPETTICOATS :OF MAN-MADE FIBRES : OF SYNTHETIC FIBRES",
+        "hsn_code": "61081110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - SLIPS AND\nPETTICOATS :OF MAN-MADE FIBRES : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61081120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - SLIPS AND\nPETTICOATS :OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61081910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - SLIPS AND\nPETTICOATS :OF OTHER TEXTILE MATERIALS : OF COTTON",
+        "hsn_code": "61081920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - SLIPS AND\nPETTICOATS :OF OTHER TEXTILE MATERIALS : OF OTHER FIBRES",
+        "hsn_code": "61081990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - BRIEFS AND\nPANTIES : OF COTTON",
+        "hsn_code": "61082100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - BRIEFS AND\nPANTIES :OF MAN-MADE FIBRES : OF SYNTHETIC FIBRES",
+        "hsn_code": "61082210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - BRIEFS AND\nPANTIES :OF MAN-MADE FIBRES : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61082220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - BRIEFS AND\nPANTIES :OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61082910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - BRIEFS AND\nPANTIES :OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "61082990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - NIGHT\nDRESSES AND PYJAMAS : OF COTTON",
+        "hsn_code": "61083100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - NIGHT\nDRESSES AND PYJAMAS :OF MAN-MADE FIBRES : OF SYNTHETIC FIBRES",
+        "hsn_code": "61083210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - NIGHT\nDRESSES AND PYJAMAS :OF MAN-MADE FIBRES : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61083220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - NIGHT\nDRESSES AND PYJAMAS :OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61083910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - NIGHT\nDRESSES AND PYJAMAS :OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "61083990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - OTHER : OF\nCOTTON",
+        "hsn_code": "61089100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - OTHER :OF\nMAN-MADE FIBRES : OF SYNTHETIC FIBRES",
+        "hsn_code": "61089210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - OTHER :OF\nMAN-MADE FIBRES : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61089220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - OTHER :OF\nOTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61089910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - OTHER :OF\nOTHER TEXTILE MATERIALS : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61089920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHT DRESSES, PYJAMAS, NEGLIGEES,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED - OTHER :OF\nOTHER TEXTILE MATERIALS : OF OTHER TEXTILE MATERIALS",
+        "hsn_code": "61089990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "T-SHIRTS, SINGLETS AND OTHER VESTS, KNITTED OR CROCHETED - OF COTTON",
+        "hsn_code": "61091000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "T-SHIRTS, SINGLETS AND OTHER VESTS, KNITTED OR CROCHETED -OF OTHER TEXTILE MATERIALS\n: OF SYNTHETIC FIBRES",
+        "hsn_code": "61099010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "T-SHIRTS, SINGLETS AND OTHER VESTS, KNITTED OR CROCHETED -OF OTHER TEXTILE MATERIALS\n: OF ARTIFICIAL FIBRES",
+        "hsn_code": "61099020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "T-SHIRTS, SINGLETS AND OTHER VESTS, KNITTED OR CROCHETED -OF OTHER TEXTILE MATERIALS\n: OF SILK",
+        "hsn_code": "61099030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "T-SHIRTS, SINGLETS AND OTHER VESTS, KNITTED OR CROCHETED -OF OTHER TEXTILE MATERIALS\n: OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61099040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "T-SHIRTS, SINGLETS AND OTHER VESTS, KNITTED OR CROCHETED -OF OTHER TEXTILE MATERIALS\n: OTHER",
+        "hsn_code": "61099090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "JERSEYS, PULLOVERS, CARDIGANS, WAISTCOATS AND SIMILAR ARTICLES, KNITTED OR\nCROCHETED - OF WOOL OR FINE ANIMAL HAIR :OF WOOL : JERSEYS",
+        "hsn_code": "61101110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "JERSEYS, PULLOVERS, CARDIGANS, WAISTCOATS AND SIMILAR ARTICLES, KNITTED OR\nCROCHETED - OF WOOL OR FINE ANIMAL HAIR :OF WOOL : SWEATERS AND CARDIGANS",
+        "hsn_code": "61101120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "JERSEYS, PULLOVERS, CARDIGANS, WAISTCOATS AND SIMILAR ARTICLES, KNITTED OR\nCROCHETED - OF WOOL OR FINE ANIMAL HAIR :OF WOOL : OTHER",
+        "hsn_code": "61101190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "JERSEYS, PULLOVERS, CARDIGANS, WAISTCOATS AND SIMILAR ARTICLES, KNITTED OR\nCROCHETED - OF WOOL OR FINE ANIMAL HAIR : OF KASHMIR (CASHMERE) GOATS",
+        "hsn_code": "61101200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "JERSEYS, PULLOVERS, CARDIGANS, WAISTCOATS AND SIMILAR ARTICLES, KNITTED OR\nCROCHETED - OF WOOL OR FINE ANIMAL HAIR : OTHER",
+        "hsn_code": "61101900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "JERSEYS, PULLOVERS, CARDIGANS, WAISTCOATS AND SIMILAR ARTICLES, KNITTED OR\nCROCHETED - OF COTTON",
+        "hsn_code": "61102000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "JERSEYS, PULLOVERS, CARDIGANS, WAISTCOATS AND SIMILAR ARTICLES, KNITTED OR\nCROCHETED - OF MAN-MADE FIBRES : OF SYNTHETIC FIBRES",
+        "hsn_code": "61103010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "JERSEYS, PULLOVERS, CARDIGANS, WAISTCOATS AND SIMILAR ARTICLES, KNITTED OR\nCROCHETED - OF MAN-MADE FIBRES : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61103020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "JERSEYS, PULLOVERS, CARDIGANS, WAISTCOATS AND SIMILAR ARTICLES, KNITTED OR\nCROCHETED - OF OTHER TEXTILE MATERIALS",
+        "hsn_code": "61109000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BABIES GARMENTS AND CLOTHING ACCESSORIES, KNITTED OR CROCHETED - OF WOOL OR FINE\nANIMAL HAIR",
+        "hsn_code": "61111000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BABIES GARMENTS AND CLOTHING ACCESSORIES, KNITTED OR CROCHETED - OF COTTON",
+        "hsn_code": "61112000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BABIES GARMENTS AND CLOTHING ACCESSORIES, KNITTED OR CROCHETED - OF SYNTHETIC\nFIBRES",
+        "hsn_code": "61113000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BABIES GARMENTS AND CLOTHING ACCESSORIES, KNITTED OR CROCHETED - OF OTHER TEXTILE\nMATERIALS : OF SILK",
+        "hsn_code": "61119010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BABIES GARMENTS AND CLOTHING ACCESSORIES, KNITTED OR CROCHETED - OF OTHER TEXTILE\nMATERIALS : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61119020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BABIES GARMENTS AND CLOTHING ACCESSORIES, KNITTED OR CROCHETED - OF OTHER TEXTILE\nMATERIALS : OTHER",
+        "hsn_code": "61119090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED - TRACK SUITS : OF COTTON",
+        "hsn_code": "61121100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED - TRACK SUITS : OF\nSYNTHETIC FIBRES",
+        "hsn_code": "61121200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED - TRACK SUITS :OF OTHER\nTEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61121910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED - TRACK SUITS :OF OTHER\nTEXTILE MATERIALS : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61121920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED - TRACK SUITS :OF OTHER\nTEXTILE MATERIALS : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61121930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED - TRACK SUITS :OF OTHER\nTEXTILE MATERIALS : OTHER",
+        "hsn_code": "61121990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED - SKI SUITS : OF SILK",
+        "hsn_code": "61122010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED - SKI SUITS : OF WOOL OR\nFINE ANIMAL HAIR",
+        "hsn_code": "61122020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED - SKI SUITS : OF COTTON",
+        "hsn_code": "61122030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED - SKI SUITS : OF SYNTHETIC\nFIBRES",
+        "hsn_code": "61122040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED - SKI SUITS : OF ARTIFICIAL\nFIBRES",
+        "hsn_code": "61122050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED - SKI SUITS : OTHER",
+        "hsn_code": "61122090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED - MENS OR BOYS SWIMWEAR\n: OF SYNTHETIC FIBRES",
+        "hsn_code": "61123100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED - MENS OR BOYS SWIMWEAR\n:OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61123910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED - MENS OR BOYS SWIMWEAR\n:OF OTHER TEXTILE MATERIALS : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61123920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED - MENS OR BOYS SWIMWEAR\n:OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "61123990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED WOMENS OR GIRLS\nSWIMWEAR : OF SYNTHETIC FIBRE",
+        "hsn_code": "61124100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED WOMENS OR GIRLS\nSWIMWEAR :OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61124910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED WOMENS OR GIRLS\nSWIMWEAR :OF OTHER TEXTILE MATERIALS : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61124920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED WOMENS OR GIRLS\nSWIMWEAR :OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "61124990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GARMENTS, MADE UP OF KNITTED OR CROCHETED FABRICS OF HEADING 5903, 5906 OR 5907",
+        "hsn_code": "61130000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER GARMENTS, KNITTED OR CROCHETED - OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61141000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER GARMENTS, KNITTED OR CROCHETED - OF COTTON",
+        "hsn_code": "61142000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER GARMENTS, KNITTED OR CROCHETED - OF MAN-MADE FIBRES : OF SYNTHETIC FIBRES",
+        "hsn_code": "61143010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER GARMENTS, KNITTED OR CROCHETED - OF MAN-MADE FIBRES : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61143020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER GARMENTS, KNITTED OR CROCHETED - OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61149010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER GARMENTS, KNITTED OR CROCHETED - OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "61149090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GRADUATED COMPRESSION HOSIERY(EX.FOR VARICOSE VEINS)",
+        "hsn_code": "61151000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTY HOSE, TIGHTS, STOCKINGS, SOCKS AND OTHER HOSIERY, INCLUDING STOCKINGS FOR\nVARICOSE VEINS AND FOOTWEAR WITHOUT APPLIED SOLES, KNITTED OR CROCHETED - PANTY\nHOSE AND TIGHTS : OF SYNTHETIC FIBRES, MEASURING PER SINGLE YARN LESS THAN 67 DECITEX",
+        "hsn_code": "61151100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTY HOSE, TIGHTS, STOCKINGS, SOCKS AND OTHER HOSIERY, INCLUDING STOCKINGS FOR\nVARICOSE VEINS AND FOOTWEAR WITHOUT APPLIED SOLES, KNITTED OR CROCHETED - PANTY\nHOSE AND TIGHTS : OF SYNTHETIC FIBRES MEASURING PER SINGLE YARN 67 DECITEX OR MORE",
+        "hsn_code": "61151200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTY HOSE, TIGHTS, STOCKINGS, SOCKS AND OTHER HOSIERY, INCLUDING STOCKINGS FOR\nVARICOSE VEINS AND FOOTWEAR WITHOUT APPLIED SOLES, KNITTED OR CROCHETED - PANTY\nHOSE AND TIGHTS :OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "61151910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTY HOSE, TIGHTS, STOCKINGS, SOCKS AND OTHER HOSIERY, INCLUDING STOCKINGS FOR\nVARICOSE VEINS AND FOOTWEAR WITHOUT APPLIED SOLES, KNITTED OR CROCHETED - PANTY\nHOSE AND TIGHTS :OF OTHER TEXTILE MATERIALS : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61151920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTY HOSE, TIGHTS, STOCKINGS, SOCKS AND OTHER HOSIERY, INCLUDING STOCKINGS FOR\nVARICOSE VEINS AND FOOTWEAR WITHOUT APPLIED SOLES, KNITTED OR CROCHETED - PANTY\nHOSE AND TIGHTS :OF OTHER TEXTILE MATERIALS : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61151930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTY HOSE, TIGHTS, STOCKINGS, SOCKS AND OTHER HOSIERY, INCLUDING STOCKINGS FOR\nVARICOSE VEINS AND FOOTWEAR WITHOUT APPLIED SOLES, KNITTED OR CROCHETED - PANTY\nHOSE AND TIGHTS :OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "61151990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTY HOSE, TIGHTS, STOCKINGS, SOCKS AND OTHER HOSIERY, INCLUDING STOCKINGS FOR\nVARICOSE VEINS AND FOOTWEAR WITHOUT APPLIED SOLES, KNITTED OR CROCHETED WOMENS\nFULL-LENGTH OR KNEE-LENGTH HOSIERY MEASURING PER SINGLE YARN LESS THAN 67 DECITEX :\nOF COTTON",
+        "hsn_code": "61152010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTY HOSE, TIGHTS, STOCKINGS, SOCKS AND OTHER HOSIERY, INCLUDING STOCKINGS FOR\nVARICOSE VEINS AND FOOTWEAR WITHOUT APPLIED SOLES, KNITTED OR CROCHETED WOMENS\nFULL-LENGTH OR KNEE-LENGTH HOSIERY MEASURING PER SINGLE YARN LESS THAN 67 DECITEX :\nOTHER",
+        "hsn_code": "61152090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER PANTY HOSE AND TIGHTS, OF SYN. FIBRES",
+        "hsn_code": "61152100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER PANTY HOSE AND TIGHTS, OF SYN. FIBRES",
+        "hsn_code": "61152200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTY HOSE AND TIGHTS OF SILK",
+        "hsn_code": "61152910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTYHOSE AND TIGHTS OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61152920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTY HOSE AND TIGHTS OF ARTIFICIAL FIBRES",
+        "hsn_code": "61152930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTY HOSE AND TIGHTS- NOT SPECIFIED EARLIER",
+        "hsn_code": "61152990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOMENS FULL OR KNEE LENGHT HOSIERY",
+        "hsn_code": "61153000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTY HOSE, TIGHTS, STOCKINGS, SOCKS AND OTHER HOSIERY, INCLUDING STOCKINGS FOR\nVARICOSE VEINS AND FOOTWEAR WITHOUT APPLIED SOLES, KNITTED OR CROCHETED - OTHER :\nOF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61159100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTY HOSE, TIGHTS, STOCKINGS, SOCKS AND OTHER HOSIERY, INCLUDING STOCKINGS FOR\nVARICOSE VEINS AND FOOTWEAR WITHOUT APPLIED SOLES, KNITTED OR CROCHETED - OTHER :\nOF COTTON",
+        "hsn_code": "61159200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTY HOSE, TIGHTS, STOCKINGS, SOCKS AND OTHER HOSIERY, INCLUDING STOCKINGS FOR\nVARICOSE VEINS AND FOOTWEAR WITHOUT APPLIED SOLES, KNITTED OR CROCHETED - OTHER :\nOF SYNTHETIC FIBRES",
+        "hsn_code": "61159300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTY HOSE, TIGHTS, STOCKINGS, SOCKS AND OTHER HOSIERY, INCLUDING STOCKINGS FOR\nVARICOSE VEINS AND FOOTWEAR WITHOUT APPLIED SOLES, KNITTED OR CROCHETED - OTHER\n:OF OTHER TEXTILE MATERIALS : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "61159400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTY HOSE, TIGHTS, STOCKINGS, SOCKS AND OTHER HOSIERY, INCLUDING STOCKINGS FOR\nVARICOSE VEINS AND FOOTWEAR WITHOUT APPLIED SOLES, KNITTED OR CROCHETED - OTHER\n:OF OTHER TEXTILE MATERIALS : OF COTTON",
+        "hsn_code": "61159500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTY HOSE, TIGHTS, STOCKINGS, SOCKS AND OTHER HOSIERY, INCLUDING STOCKINGS FOR\nVARICOSE VEINS AND FOOTWEAR WITHOUT APPLIED SOLES, KNITTED OR CROCHETED - OTHER\n:OF OTHER TEXTILE MATERIALS : OF SYNTHETIC FIBRES",
+        "hsn_code": "61159600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTY HOSE, TIGHTS, STOCKINGS, SOCKS AND OTHER HOSIERY, INCLUDING STOCKINGS FOR\nVARICOSE VEINS AND FOOTWEAR WITHOUT APPLIED SOLES, KNITTED OR CROCHETED - OTHER\n:OF OTHER TEXTILE MATERIALS : OF ARTIFICIAL FIBRES",
+        "hsn_code": "61159910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTY HOSE, TIGHTS, STOCKINGS, SOCKS AND OTHER HOSIERY, INCLUDING STOCKINGS FOR\nVARICOSE VEINS AND FOOTWEAR WITHOUT APPLIED SOLES, KNITTED OR CROCHETED - OTHER\n:OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "61159990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLOVES, MITTENS AND MITTS, KNITTED OR CROCHETED -IMPREGNATED, COATED OR COVERED\nWITH PLASTICS OR RUBBER",
+        "hsn_code": "61161000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLOVES, MITTENS AND MITTS, KNITTED OR CROCHETED - OTHER : OF WOOL OR FINE ANIMAL\nHAIR",
+        "hsn_code": "61169100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLOVES, MITTENS AND MITTS, KNITTED OR CROCHETED - OTHER : OF COTTON",
+        "hsn_code": "61169200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLOVES, MITTENS AND MITTS, KNITTED OR CROCHETED - OTHER : OF SYNTHETIC FIBRES",
+        "hsn_code": "61169300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLOVES, MITTENS AND MITTS, KNITTED OR CROCHETED - OTHER :OF OTHER TEXTILE MATERIALS\n: OF ARTIFICIAL FIBRES",
+        "hsn_code": "61169910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLOVES, MITTENS AND MITTS, KNITTED OR CROCHETED - OTHER :OF OTHER TEXTILE MATERIALS\n: OTHER",
+        "hsn_code": "61169990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEN’S OR BOYS’ OVERCOATS, CARCOATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, KNITTED OR CROCHETED, OTHER THAN THOSE OF HEADING 6103",
+        "hsn_code": "6101",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMEN’S OR GIRLS’ OVERCOATS, CARCOATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, KNITTED OR CROCHETED, OTHER THAN THOSE OF HEADING 6104",
+        "hsn_code": "6102",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEN’S OR BOYS’ SUITS,ENSEMBLES, JACKETS, BLAZERS, TROUSERS,BIB AND BRACE OVERALLS, BREECHES AND SHORTS(OTHER THAN SWIM WEAR), KNITTED OR CROCHETED",
+        "hsn_code": "6103",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMEN’S OR GIRLS’ SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS, TROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIM WEAR), KNITTED OR CROCHETED",
+        "hsn_code": "6104",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEN’S OR BOYS’ SHIRTS, KNITTED OR CROCHETED",
+        "hsn_code": "6105",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOMEN’S OR GIRLS’ BLOUSES, SHIRTS AND SHIRT-BLOUSES, KNITTED OR CROCHETED",
+        "hsn_code": "6106",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEN’S OR BOYS’ UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES, KNITTED OR CROCHETED",
+        "hsn_code": "6107",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "Slips, petticoats, briefs, panties, nightdresses, pyjamas, negligees, bathrobes, dressing gowns and similar articles; women’s or girls’, knitted or crocheted",
+        "hsn_code": "6108",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "T-SHIRTS, SINGLETS AND OTHER VESTS, KNITTED OR CROCHETED",
+        "hsn_code": "6109",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "JERSEYS, PULLOVERS, CARDIGANS, WAISTCOATS AND SIMILAR ARTICLES, KNITTED OR CROCHETED",
+        "hsn_code": "6110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BABIES’ GARMENTS AND CLOTHING ACCESSORIES, KNITTED OR CROCHETED",
+        "hsn_code": "6111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRACK SUITS, SKI SUITS AND SWIMWEAR, KNITTED OR CROCHETED",
+        "hsn_code": "6112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "Garments, made up of knitted or crocheted fabrics of heading 5903, 5906 or 5907",
+        "hsn_code": "6113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER GARMENTS, KNITTED OR CROCHETED",
+        "hsn_code": "6114",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PANTYHOSE, TIGHTS, STOCKINGS, SOCKS AND OTHER HOSIERY, INCLUDING GRADUATED COMPRESSION HOSIERY (FOR EXAMPLE, STOCKINGS FOR VARICOSE VEINS) AND FOOTWEAR WITHOUT APPLIED SOLES, KNITTED OR CROCHETED",
+        "hsn_code": "6115",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLOVES, MITTENS AND MITTS, KNITTED OR CROCHETED",
+        "hsn_code": "6116",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "EMBROIDERY IN THE PIECE, IN STRIPS OR IN MOTIFS",
+        "hsn_code": "5810",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "EMBROIDERY IN THE PIECE, IN STRIPS OR IN MOTIFS - OTHER EMBROIDERY: OF OTHER TEXTILE\nMATERIALS",
+        "hsn_code": "58109900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "EMBROIDERY IN THE PIECE, IN STRIPS OR IN MOTIFS - OTHER EMBROIDERY: - OF MAN-MADE\nFIBRES :OTHER",
+        "hsn_code": "58109290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "EMBROIDERY IN THE PIECE, IN STRIPS OR IN MOTIFS - OTHER EMBROIDERY: - OF MAN-MADE\nFIBRES :EMBROIDERED BADGES, MOTIFS AND THE LIKE",
+        "hsn_code": "58109210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "EMBROIDERY IN THE PIECE, IN STRIPS OR IN MOTIFS - OTHER EMBROIDERY: OF COTTON",
+        "hsn_code": "58109100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "EMBROIDERY IN THE PIECE, IN STRIPS OR IN MOTIFS - EMBROIDERY WITHOUT VISIBLE GROUND",
+        "hsn_code": "58101000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF METAL THREAD AND WOVEN FABRICS OF METALLISED YARN OF HEADING 5605, OF A KIND USED IN APPAREL, AS FURNISHING FABRICS OR FOR SIMILAR PURPOSES, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "5809",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF METAL THREAD AND WOVEN FABRICS OF METALLISED YARN OF HEADING\n5605, OF A KIND USED IN APPAREL, AS FURNISHING FABRICS OR FOR SIMILAR PURPOSES, NOT\nELSEWHERE SPECIFIED OR INCLUDED - WOVEN FABRICS OF METAL THREAD AND WOVEN\nFABRICS OF METALLISED YARN OF HEADING 5605, OF A KIND USED IN APPAREL, AS FURNISHING\nFABRICS OR FOR SIMILAR PURPOSES, NOT ELSEWHERE SPECIFIED OR INCLUDED:OTHER",
+        "hsn_code": "58090090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF METAL THREAD AND WOVEN FABRICS OF METALLISED YARN OF HEADING\n5605, OF A KIND USED IN APPAREL, AS FURNISHING FABRICS OR FOR SIMILAR PURPOSES, NOT\nELSEWHERE SPECIFIED OR INCLUDED - WOVEN FABRICS OF METAL THREAD AND WOVEN\nFABRICS OF METALLISED YARN OF HEADING 5605, OF A KIND USED IN APPAREL, AS FURNISHING\nFABRICS OR FOR SIMILAR PURPOSES, NOT ELSEWHERE SPECIFIED OR INCLUDED:ZARI BORDER",
+        "hsn_code": "58090010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP - OTHER\nCARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP : CARPETS : OF\nSILK",
+        "hsn_code": "57050011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP - OTHER\nCARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP : CARPETS :\nOTHER",
+        "hsn_code": "57050019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP - OTHER\nCARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP : DURRIES :\nDURRIES COTTON",
+        "hsn_code": "57050021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP - OTHER\nCARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP : DURRIES :\nDURRIES OF MAN-MADE FIBRES",
+        "hsn_code": "57050022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP - OTHER\nCARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP : DURRIES :\nDURRIES OF WOOL",
+        "hsn_code": "57050023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COTTON DURRIES OF HANDLOOM (INCLUDING CHINDI DURRIES, COTTON CHENILLE DURRIES,\nRAG RUG DURRIE, PRINTED DURRIES, DRUGGETS)",
+        "hsn_code": "57050024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP - OTHER\nCARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP : DURRIES :\nOTHER",
+        "hsn_code": "57050029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP - OTHER\nCARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP : OF JUTE : OF\nBLENDED JUTE",
+        "hsn_code": "57050031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP - OTHER\nCARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP : OF JUTE : OF\nCOIR JUTE",
+        "hsn_code": "57050032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP - OTHER\nCARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP : OF JUTE :\nOTHER",
+        "hsn_code": "57050039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP - OTHER\nCARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP : CARPETS,\nCARPETING, RUGS, MATS AND MATTINGS : KNITTED",
+        "hsn_code": "57050041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MATS AND MATTINGS INCLUDING BATH MATS, WHERE COTTON PREDOMINATES BY WEIGHT, OF\nHANDLOOM, COTTON RUGS OF HANDLOOM",
+        "hsn_code": "57050042",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP - OTHER\nCARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP : CARPETS,\nCARPETING, RUGS, MATS AND MATTINGS : OTHER",
+        "hsn_code": "57050049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP - OTHER\nCARPETS AND OTHER TEXTILE FLOOR COVERINGS, WHETHER OR NOT MADE UP : OTHER",
+        "hsn_code": "57050090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CARPETS AND OTHER TEXTILE FLOOR COVERINGS , WHETHER OR NOT MADE UP",
+        "hsn_code": "5705",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, TUFTED, WHETHER OR NOT MADE UP - OF\nWOOL OR FINE ANIMAL HAIR : CARPETS",
+        "hsn_code": "57031010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, TUFTED, WHETHER OR NOT MADE UP - OF\nWOOL OR FINE ANIMAL HAIR : MATS AND MATTING",
+        "hsn_code": "57031020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, TUFTED, WHETHER OR NOT MADE UP - OF\nWOOL OR FINE ANIMAL HAIR : OTHER",
+        "hsn_code": "57031090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, TUFTED, WHETHER OR NOT MADE UP - OF\nNYLON OR OTHER POLYAMIDES : CARPETS, CARPETING AND RUGS",
+        "hsn_code": "57032010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, TUFTED, WHETHER OR NOT MADE UP - OF\nNYLON OR OTHER POLYAMIDES : 100% POLYAMIDE TUFTED VELOUR, CUT PILE OR LOOP PILE\nCARPET MATS WITH JUTE, RUBBER LATEX OR PU FOAM BACKING",
+        "hsn_code": "57032020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, TUFTED, WHETHER OR NOT MADE UP - OF\nNYLON OR OTHER POLYAMIDES : OTHER",
+        "hsn_code": "57032090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, TUFTED, WHETHER OR NOT MADE UP - OF\nOTHER MAN - MADE TEXTILE MATERIALS : CARPETS, CARPETING AND RUGS",
+        "hsn_code": "57033010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, TUFTED, WHETHER OR NOT MADE UP - OF\nOTHER MAN - MADE TEXTILE MATERIALS : 100% POLYPROPYLENE CARPET MATS WITH JUTE,\nRUBBER, LATEX OR PU FOAM BACKING",
+        "hsn_code": "57033020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, TUFTED, WHETHER OR NOT MADE UP - OF\nOTHER MAN - MADE TEXTILE MATERIALS : OTHER",
+        "hsn_code": "57033090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, TUFTED, WHETHER OR NOT MADE UP - OF\nOTHER TEXTILE MATERIALS : CARPETS AND OTHER FLOOR COVERINGS, OF COTTON, OTHER\nTHAN DURRIES",
+        "hsn_code": "57039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND FLOOR COVERINGS OF COIR",
+        "hsn_code": "57039020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, TUFTED, WHETHER OR NOT MADE UP - OF\nOTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "57039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, TUFTED, WHETHER OR NOT MADE UP",
+        "hsn_code": "5703",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND-WOVEN RUGS",
+        "hsn_code": "57021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - FLOOR COVERINGS OF COCONUT FIBRES (COIR) : COIR MATTING, WOVEN",
+        "hsn_code": "57022010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - FLOOR COVERINGS OF COCONUT FIBRES (COIR) : COIR CARPETS AND OTHER RUGS",
+        "hsn_code": "57022020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - FLOOR COVERINGS OF COCONUT FIBRES (COIR) : OTHER",
+        "hsn_code": "57022090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, NOT MADE UP : OF WOOL OR FINE ANIMAL HAIR :\nCARPETS",
+        "hsn_code": "57023110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, NOT MADE UP : OF WOOL OR FINE ANIMAL HAIR :\nDRUGGETS",
+        "hsn_code": "57023120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, NOT MADE UP : OF WOOL OR FINE ANIMAL HAIR :\nMATS AND MATTING",
+        "hsn_code": "57023130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, NOT MADE UP : OF WOOL OR FINE ANIMAL HAIR :\nCARPETING , FLOOR RUGS AND THE LIKE",
+        "hsn_code": "57023140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, NOT MADE UP : OF WOOL OR FINE ANIMAL HAIR :\nOTHER",
+        "hsn_code": "57023190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, NOT MADE UP : OF MAN - MADE TEXTILE MATERIAL :\nCARPETS, CARPETING AND RUGS AND THE LIKE",
+        "hsn_code": "57023210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, NOT MADE UP : OF MAN - MADE TEXTILE MATERIAL :\nMATS AND MATTING",
+        "hsn_code": "57023220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, NOT MADE UP : OF MAN - MADE TEXTILE MATERIAL :\nOTHER",
+        "hsn_code": "57023290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, NOT MADE UP : OF OTHER TEXTILE MATERIALS :\nCARPETS AND OTHER FLOOR COVERINGS OTHER THAN DURRIES OF COTTON",
+        "hsn_code": "57023910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, NOT MADE UP : OF OTHER TEXTILE MATERIALS :\nCARPETS AND OTHER FLOOR COVERINGS, OF SILK",
+        "hsn_code": "57023920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, MADE UP : OF WOOL OR FINE ANIMAL HAIR : CARPETS",
+        "hsn_code": "57024110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, MADE UP : OF WOOL OR FINE ANIMAL HAIR :\nDRUGGETS",
+        "hsn_code": "57024120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, MADE UP : OF WOOL OR FINE ANIMAL HAIR : MATS\nAND MATTING",
+        "hsn_code": "57024130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, MADE UP : OF WOOL OR FINE ANIMAL HAIR : OTHER",
+        "hsn_code": "57024140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, MADE UP : OF MAN MADE TEXTILE MATERIALS :\nCARPETS, CARPETING AND RUGS",
+        "hsn_code": "57024210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, MADE UP : OF MAN MADE TEXTILE MATERIALS : MATS\nAND MATTINGS",
+        "hsn_code": "57024220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS, RUGS AND MATS OF HANDLOOM",
+        "hsn_code": "57024230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, MADE UP : OF MAN MADE TEXTILE MATERIALS : OTHER",
+        "hsn_code": "57024290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, MADE UP : OF OTHER TEXTILE MATERIALS : CARPETS\nAND OTHER FLOOR COVERINGS OTHER THAN DURRIES OF COTTON",
+        "hsn_code": "57024910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, MADE UP : OF OTHER TEXTILE MATERIALS : CARPETS\nAND OTHER FLOOR COVERINGS, OF SILK",
+        "hsn_code": "57024920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, OF PILE CONSTRUCTION, MADE UP : OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "57024990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS, CARPETING AND RUGS - OF MAN MADE TEXTILE MATERIALS",
+        "hsn_code": "57025021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MATS AND MATTING - OF MAN-MADE TEXTILE MATERIALS",
+        "hsn_code": "57025022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHERS - NOT ELSEWHERE SPECIFIED - OF MAN-MADE TEXTILE MATERIALS",
+        "hsn_code": "57025029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER FLOOR COVERINGS, OF COTTON OTHER THAN DURRIES - OF OTHER\nTEXTILE MATERIALS",
+        "hsn_code": "57025031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER FLOOR COVERINGS, OF SILK",
+        "hsn_code": "57025032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PLACE MAT AND OTHER SIMILAR GOODS - OF OTHER TEXTILE MATERIALS",
+        "hsn_code": "57025033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHERS NOT ELSEWHERE SPECIFIED - OF OTHER TEXTILE MATERIALS",
+        "hsn_code": "57025039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, NOT MADE UP : OF WOOL OR FINE ANIMAL HAIR :\nCARPETS",
+        "hsn_code": "57025110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, NOT MADE UP : OF WOOL OR FINE ANIMAL HAIR :\nDRUGGETS",
+        "hsn_code": "57025120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, NOT MADE UP : OF WOOL OR FINE ANIMAL HAIR :\nMATS AND MATTING",
+        "hsn_code": "57025130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, NOT MADE UP : OF WOOL OR FINE ANIMAL HAIR :\nOTHER",
+        "hsn_code": "57025140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, NOT MADE UP : OF MAN - MADE TEXTILE\nMATERIALS : CARPETS, CARPETING AND RUGS",
+        "hsn_code": "57025210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, NOT MADE UP : OF MAN - MADE TEXTILE\nMATERIALS : MATS AND MATTINGS",
+        "hsn_code": "57025220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, NOT MADE UP : OF MAN - MADE TEXTILE\nMATERIALS : OTHER",
+        "hsn_code": "57025290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, NOT MADE UP : OF OTHER TEXTILE MATERIALS :\nCARPETS AND OTHER FLOOR COVERINGS, OF COTTON OTHER THAN DURRIE",
+        "hsn_code": "57025910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, NOT MADE UP : OF OTHER TEXTILE MATERIALS :\nCARPETS AND OTHER FLOOR COVERINGS, OF SILK",
+        "hsn_code": "57025920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, NOT MADE UP : OF OTHER TEXTILE MATERIALS :\nPLACE MAT AND OTHER SIMILAR GOODS",
+        "hsn_code": "57025930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, NOT MADE UP : OF OTHER TEXTILE MATERIALS :\nOTHER",
+        "hsn_code": "57025990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, MADE UP : OF WOOL OR FINE ANIMAL HAIR :\nCARPETS",
+        "hsn_code": "57029110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, MADE UP : OF WOOL OR FINE ANIMAL HAIR :\nDRUGGETS",
+        "hsn_code": "57029120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, MADE UP : OF WOOL OR FINE ANIMAL HAIR :\nMATS AND MATTING",
+        "hsn_code": "57029130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, MADE UP : OF WOOL OR FINE ANIMAL HAIR :\nOTHER",
+        "hsn_code": "57029140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, MADE UP : OF MAN - MADE TEXTILE MATERIALS :\nCARPETS, CARPETING AND RUGS",
+        "hsn_code": "57029210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, MADE UP : OF MAN - MADE TEXTILE MATERIALS :\nMATS AND MATTINGS",
+        "hsn_code": "57029220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, MADE UP : OF MAN - MADE TEXTILE MATERIALS :\nOTHER",
+        "hsn_code": "57029290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, MADE UP : OF OTHER TEXTILE MATERIALS :\nCARPETS AND OTHER FLOOR COVERINGS, OF COTTON OTHER THAN DURRIES",
+        "hsn_code": "57029910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, MADE UP : OF OTHER TEXTILE MATERIALS :\nCARPETS AND OTHER FLOOR COVERINGS, OF SILK",
+        "hsn_code": "57029920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND - WOVEN\nRUGS - OTHER, NOT OF PILE CONSTRUCTION, MADE UP : OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "57029990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, WOVEN, NOT TUFTED OR FLOCKED, WHETHER OR NOT MADE UP, INCLUDING KELEM, SCHUMACKS, KARAMANIE AND SIMILAR HAND-WOVEN RUGS",
+        "hsn_code": "5702",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "METALLISED YARN, WHETHER OR NOT GIMPED BEING TEXTILE YARN, OR STRIP OR THE LIKE OF\nHEADING 5404 OR 5405, COMBINED WITH METAL IN THE FORM OF THREAD, STRIP OR POWDER\nOR COVERED WITH METAL - METALLISED YARN, WHETHER OR NOT GIMPED, BEING TEXTILE\nYARN, OR STRIP OR THE LIKE OF HEADING 5404 OR 5405, COMBINED WITH METAL IN THE FORM\nOF THREAD, STRIP OR POWDER OR COVERED WITH METAL : REAL ZARI THREAD (GOLD) AND\nSILVER THREAD COMBINED WITH TEXTILE THREAD",
+        "hsn_code": "56050010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM MATERIALS OF HEADING 5405",
+        "hsn_code": "5408",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : PRINTED :OTHER",
+        "hsn_code": "54083490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : PRINTED : FABRICS OF CONTINUOUS\nFILAMENT, OTHER THAN RAYON",
+        "hsn_code": "54083420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : PRINTED : FABRIC OF RAYON : OTHER",
+        "hsn_code": "54083419",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : PRINTED : FABRIC OF RAYON : RAYON\nSAREES",
+        "hsn_code": "54083418",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : PRINTED : FABRIC OF RAYON : RAYON\nSHIRTINGS",
+        "hsn_code": "54083417",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : PRINTED : FABRIC OF RAYON : RAYON\nSUITINGS",
+        "hsn_code": "54083416",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : PRINTED : FABRIC OF RAYON : RAYON\nTAFETTA",
+        "hsn_code": "54083415",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : PRINTED : FABRIC OF RAYON : RAYON\nGEORGETTE",
+        "hsn_code": "54083414",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : PRINTED : FABRIC OF RAYON : RAYON\nBROCADES",
+        "hsn_code": "54083413",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : PRINTED : FABRIC OF RAYON : RAYON\nJACQUARDS",
+        "hsn_code": "54083412",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : PRINTED : FABRIC OF RAYON : RAYON\nCREPE FABRICS",
+        "hsn_code": "54083411",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : OF YARNS OF DIFFERENT COLOURS",
+        "hsn_code": "54083300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : DYED : OTHER",
+        "hsn_code": "54083290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : DYED : FABRICS OF RAYON : OTHER",
+        "hsn_code": "54083219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : DYED : FABRICS OF RAYON : RAYON\nSHIRTINGS",
+        "hsn_code": "54083215",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : DYED : FABRICS OF RAYON : RAYON\nSUITINGS",
+        "hsn_code": "54083214",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : DYED : FABRICS OF RAYON : RAYON\nTAFETTA",
+        "hsn_code": "54083213",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : DYED : FABRICS OF RAYON : RAYON\nGEORGETTE",
+        "hsn_code": "54083212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : DYED : FABRICS OF RAYON : RAYON\nBROCADES",
+        "hsn_code": "54083211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : UNBLEACHED OR BLEACHED :\nBLEACHED",
+        "hsn_code": "54083120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS : UNBLEACHED OR BLEACHED :\nUNBLEACHED",
+        "hsn_code": "54083110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : PRINTED : OTHER",
+        "hsn_code": "54082490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : PRINTED : OF RAYON : OTHER",
+        "hsn_code": "54082419",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : PRINTED : OF RAYON : RAYON SAREES",
+        "hsn_code": "54082418",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : PRINTED : OF RAYON : RAYON SHIRTINGS",
+        "hsn_code": "54082417",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : PRINTED : OF RAYON : RAYON SUITINGS",
+        "hsn_code": "54082416",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : PRINTED : OF RAYON : RAYON TAFETTA",
+        "hsn_code": "54082415",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : PRINTED : OF RAYON : RAYON GEORGETTE",
+        "hsn_code": "54082414",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : PRINTED : OF RAYON : RAYON BROCADES",
+        "hsn_code": "54082413",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : PRINTED : OF RAYON : RAYON JACQUARDS",
+        "hsn_code": "54082412",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : PRINTED : OF RAYON : RAYON CREPE FABRICS",
+        "hsn_code": "54082411",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : OF YARNS OF DIFFERENT COLOURS",
+        "hsn_code": "54082300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : DYED : OTHER",
+        "hsn_code": "54082290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : DYED : FABRICS OF CONTINUOUS FILAMENT,\nOTHER THAN RAYON",
+        "hsn_code": "54082220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : DYED : FABRICS OF RAYON : OTHER",
+        "hsn_code": "54082219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : DYED : FABRICS OF RAYON : RAYON SAREES",
+        "hsn_code": "54082218",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : DYED : FABRICS OF RAYON : RAYON SHIRTINGS",
+        "hsn_code": "54082217",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : DYED : FABRICS OF RAYON : RAYON SUITINGS",
+        "hsn_code": "54082216",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : DYED : FABRICS OF RAYON : RAYON TAFETTA",
+        "hsn_code": "54082215",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : DYED : FABRICS OF RAYON : RAYON GEORGETTE",
+        "hsn_code": "54082214",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : DYED : FABRICS OF RAYON : RAYON BROCADES",
+        "hsn_code": "54082213",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : DYED : FABRICS OF RAYON : RAYON JACQUARDS",
+        "hsn_code": "54082212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : DYED : FABRICS OF RAYON : RAYON CREPE\nFABRICS",
+        "hsn_code": "54082211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : UNBLEACHED OR BLEACHED : BLEACHED",
+        "hsn_code": "54082120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF ARTIFICIAL FILAMENT OR STRIP OR THE LIKE : UNBLEACHED OR BLEACHED : UNBLEACHED",
+        "hsn_code": "54082110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5405WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nVISCOSE RAYON",
+        "hsn_code": "54081000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "Woven fabrics of synthetic filament yarn, including woven fabrics obtained from materials of heading 5404",
+        "hsn_code": "5407",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS : PRINTED",
+        "hsn_code": "54079400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS : OF YARNS OF DIFFERENT COLOURS",
+        "hsn_code": "54079300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS : DYED",
+        "hsn_code": "54079200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS : UNBLEACHED OR BLEACHED :\nBLEACHED",
+        "hsn_code": "54079120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS : UNBLEACHED OR BLEACHED :\nUNBLEACHED",
+        "hsn_code": "54079110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85% BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : PRINTED :OTHER",
+        "hsn_code": "54078490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : PRINTED\n:POLYESTER SAREES",
+        "hsn_code": "54078470",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : PRINTED\n:LUNGIES",
+        "hsn_code": "54078460",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : PRINTED\n:TERYLENE AND DACRON SAREES",
+        "hsn_code": "54078450",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : PRINTED\n:POLYESTER SUITINGS",
+        "hsn_code": "54078440",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : PRINTED\n:POLYESTER SHIRTINGS",
+        "hsn_code": "54078430",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : PRINTED :NYLON\nSAREES",
+        "hsn_code": "54078420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : PRINTED :\nNYLON GEORGETTE",
+        "hsn_code": "54078410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85% BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : DYED :OF YARNS\nOF DIFFERENT COLOURS",
+        "hsn_code": "54078300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85% BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : DYED :OTHER",
+        "hsn_code": "54078290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : DYED :LUNGIES",
+        "hsn_code": "54078260",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : DYED :TERYLENE\nAND DACRON SAREES",
+        "hsn_code": "54078250",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : DYED\n:POLYESTER SUITINGS",
+        "hsn_code": "54078240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : DYED\n:POLYESTER SHIRTINGS",
+        "hsn_code": "54078230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : DYED :NYLON\nSAREES",
+        "hsn_code": "54078220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : DYED : NYLON\nGEORGETTE",
+        "hsn_code": "54078210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85% BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : UNBLEACHED OR\nBLEACHED : BLEACHED : OTHER",
+        "hsn_code": "54078129",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : UNBLEACHED OR\nBLEACHED : BLEACHED : POLYESTER DHOTI",
+        "hsn_code": "54078126",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : UNBLEACHED OR\nBLEACHED : BLEACHED : TERYLENE AND DACRON SAREES",
+        "hsn_code": "54078125",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : UNBLEACHED OR\nBLEACHED : BLEACHED : POLYESTER SUITINGS",
+        "hsn_code": "54078124",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : UNBLEACHED OR\nBLEACHED : BLEACHED : POLYESTER SHIRTINGS",
+        "hsn_code": "54078123",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : UNBLEACHED OR\nBLEACHED : BLEACHED : NYLON SAREES",
+        "hsn_code": "54078122",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : UNBLEACHED OR\nBLEACHED : BLEACHED : NYLON GEORGETTE",
+        "hsn_code": "54078121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85% BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : UNBLEACHED OR\nBLEACHED : UNBLEACHED : OTHER",
+        "hsn_code": "54078119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : UNBLEACHED OR\nBLEACHED : UNBLEACHED : POLYESTER DHOTI",
+        "hsn_code": "54078116",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : UNBLEACHED OR\nBLEACHED : UNBLEACHED : TERYLENE AND DACRON SAREES",
+        "hsn_code": "54078115",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : UNBLEACHED OR\nBLEACHED : UNBLEACHED : POLYESTER SUITINGS",
+        "hsn_code": "54078114",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : UNBLEACHED OR\nBLEACHED : UNBLEACHED : POLYESTER SHIRTINGS",
+        "hsn_code": "54078113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : UNBLEACHED OR\nBLEACHED : UNBLEACHED : NYLON SAREES",
+        "hsn_code": "54078112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF SYNTHETIC FILAMENTS, MIXED MAINLY OR SOLELY WITH COTTON : UNBLEACHED OR\nBLEACHED : UNBLEACHED : NYLON GEORGETTE",
+        "hsn_code": "54078111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF SYNTHETIC FILAMENTS : PRINTED",
+        "hsn_code": "54077400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF SYNTHETIC FILAMENTS : OF YARNS OF DIFFERENT COLOURS",
+        "hsn_code": "54077300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF SYNTHETIC FILAMENTS : DYED",
+        "hsn_code": "54077200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF SYNTHETIC FILAMENTS : UNBLEACHED OR BLEACHED : BLEACHED",
+        "hsn_code": "54077120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF SYNTHETIC FILAMENTS : UNBLEACHED OR BLEACHED : UNBLEACHED",
+        "hsn_code": "54077110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF POLYESTER FILAMENTS : OTHER",
+        "hsn_code": "54076900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF POLYESTER FILAMENTS : CONTAINING 85 PERCENT OR MORE BY WEIGHT OF\nNONTEXTURED POLYESTER FILAMENTS : OTHER",
+        "hsn_code": "54076190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF POLYESTER FILAMENTS : CONTAINING 85 PERCENT OR MORE BY WEIGHT OF\nNONTEXTURED POLYESTER FILAMENTS : POLYESTER SUITINGS",
+        "hsn_code": "54076120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF POLYESTER FILAMENTS : CONTAINING 85 PERCENT OR MORE BY WEIGHT OF\nNONTEXTURED POLYESTER FILAMENTS : POLYESTER SHIRTINGS",
+        "hsn_code": "54076110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nWOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF TEXTURED POLYESTER FILAMENTS: PRINTED :OTHER",
+        "hsn_code": "54075490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF TEXTURED POLYESTER FILAMENTS: PRINTED :POLYESTER SAREES",
+        "hsn_code": "54075430",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF TEXTURED POLYESTER FILAMENTS: PRINTED :POLYESTER SHIRTINGS",
+        "hsn_code": "54075420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF TEXTURED POLYESTER FILAMENTS: PRINTED : TERYLENE AND DACRON SAREES",
+        "hsn_code": "54075410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF TEXTURED POLYESTER FILAMENTS: DYED :OF YARNS OF DIFFERENT COLOURS",
+        "hsn_code": "54075300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF TEXTURED POLYESTER FILAMENTS: DYED :OTHER",
+        "hsn_code": "54075290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF TEXTURED POLYESTER FILAMENTS: DYED :POLYESTER SAREES",
+        "hsn_code": "54075240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF TEXTURED POLYESTER FILAMENTS: DYED :TERYLENE AND DACRON SAREES",
+        "hsn_code": "54075230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF TEXTURED POLYESTER FILAMENTS: DYED :POLYESTER SUITINGS",
+        "hsn_code": "54075220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 ERCENT OR MORE BY\nWEIGHT OF TEXTURED POLYESTER FILAMENTS: DYED : POLYESTER SHIRTINGS",
+        "hsn_code": "54075210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF TEXTURED POLYESTER FILAMENTS: UNBLEACHED OR BLEACHED : BLEACHED : OTHER",
+        "hsn_code": "54075129",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF TEXTURED POLYESTER FILAMENTS: UNBLEACHED OR BLEACHED : BLEACHED : POLYESTER\nSHIRTINGS",
+        "hsn_code": "54075121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF TEXTURED POLYESTER FILAMENTS: UNBLEACHED OR BLEACHED : UNBLEACHED :\nOTHER",
+        "hsn_code": "54075119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF TEXTURED POLYESTER FILAMENTS: UNBLEACHED OR BLEACHED : UNBLEACHED :\nPOLYESTER SHIRTINGS",
+        "hsn_code": "54075111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF FILAMENTS OF NYLON OR OTHER POLYAMIDES : PRINTED :OTHER",
+        "hsn_code": "54074490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF FILAMENTS OF NYLON OR OTHER POLYAMIDES : PRINTED :NYLON SAREES,",
+        "hsn_code": "54074440",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF FILAMENTS OF NYLON OR OTHER POLYAMIDES : PRINTED :NYLON TAFETTA",
+        "hsn_code": "54074430",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF FILAMENTS OF NYLON OR OTHER POLYAMIDES : PRINTED :NYLON GEORGETTE",
+        "hsn_code": "54074420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF FILAMENTS OF NYLON OR OTHER POLYAMIDES : PRINTED : NYLON BRASSO",
+        "hsn_code": "54074410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF FILAMENTS OF NYLON OR OTHER POLYAMIDES : DYED :OF YARN OF DIFFERENT COLOURS",
+        "hsn_code": "54074300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF FILAMENTS OF NYLON OR OTHER POLYAMIDES : DYED :OTHER",
+        "hsn_code": "54074290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF FILAMENTS OF NYLON OR OTHER POLYAMIDES : DYED :NYLON SAREES",
+        "hsn_code": "54074240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF FILAMENTS OF NYLON OR OTHER POLYAMIDES : DYED :NYLON TAFETTA",
+        "hsn_code": "54074230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF FILAMENTS OF NYLON OR OTHER POLYAMIDES : DYED :NYLON GEORGETTE",
+        "hsn_code": "54074220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF FILAMENTS OF NYLON OR OTHER POLYAMIDES : DYED : NYLON BRASSO",
+        "hsn_code": "54074210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF FILAMENTS OF NYLON OR OTHER POLYAMIDES : UNBLEACHED OR BLEACHED : BLEACHED :\nOTHER",
+        "hsn_code": "54074129",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF FILAMENTS OF NYLON OR OTHER POLYAMIDES : UNBLEACHED OR BLEACHED :\nBLEACHED : NYLON SAREES",
+        "hsn_code": "54074124",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF FILAMENTS OF NYLON OR OTHER POLYAMIDES : UNBLEACHED OR BLEACHED :\nBLEACHED : NYLON TAFETTA",
+        "hsn_code": "54074123",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF FILAMENTS OF NYLON OR OTHER POLYAMIDES : UNBLEACHED OR BLEACHED :\nBLEACHED : NYLON GEORGETTE",
+        "hsn_code": "54074122",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF FILAMENTS OF NYLON OR OTHER POLYAMIDES : UNBLEACHED OR BLEACHED :\nBLEACHED : NYLON BRASSO",
+        "hsn_code": "54074121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85% OR MORE BY WEIGHT\nOF FILAMENTS OF NYLON OR OTHER POLYAMIDES : UNBLEACHED OR BLEACHED : UNBLEACHED :\nOTHER",
+        "hsn_code": "54074119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF FILAMENTS OF NYLON OR OTHER POLYAMIDES : UNBLEACHED OR BLEACHED :\nUNBLEACHED : NYLON SAREES",
+        "hsn_code": "54074114",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85PERCENT OR MORE BY\nWEIGHT OF FILAMENTS OF NYLON OR OTHER POLYAMIDES : UNBLEACHED OR BLEACHED :\nUNBLEACHED : NYLON TAFETTA",
+        "hsn_code": "54074113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF FILAMENTS OF NYLON OR OTHER POLYAMIDES : UNBLEACHED OR BLEACHED :\nUNBLEACHED : NYLON GEORGETTE",
+        "hsn_code": "54074112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 OTHER WOVEN FABRICS, CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF FILAMENTS OF NYLON OR OTHER POLYAMIDES : UNBLEACHED OR BLEACHED :\nUNBLEACHED : NYLON BRASSO",
+        "hsn_code": "54074111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 FABRICS SPECIFIED IN NOTE 9 TO SECTION XI OTHER",
+        "hsn_code": "54073090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 FABRICS SPECIFIED IN NOTE 9 TO SECTION XI PRINTED",
+        "hsn_code": "54073040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 FABRICS SPECIFIED IN NOTE 9 TO SECTION XI DYED",
+        "hsn_code": "54073030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 FABRICS SPECIFIED IN NOTE 9 TO SECTION XI BLEACHED",
+        "hsn_code": "54073020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 FABRICS SPECIFIED IN NOTE 9 TO SECTION XI UNBLEACHED",
+        "hsn_code": "54073010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM STRIP OR THE LIKE :OTHER",
+        "hsn_code": "54072090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM STRIP OR THE LIKE : PRINTED",
+        "hsn_code": "54072040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM STRIP OR THE LIKE :DYED",
+        "hsn_code": "54072030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM STRIP OR THE LIKE : BLEACHED",
+        "hsn_code": "54072020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM STRIP OR THE LIKE\n:UNBLEACHED",
+        "hsn_code": "54072010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : OTHER :OTHER",
+        "hsn_code": "54071099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : OTHER :OTHER NYLON AND POLYAMIDE\nFABRICS OF FILAMENT YARN",
+        "hsn_code": "54071096",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : OTHER :OTHER NYLON AND POLYAMIDE\nFABRICS OF FILAMENT YARN",
+        "hsn_code": "54071095",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : OTHER :UMBRELLA CLOTH PANEL FABRICS",
+        "hsn_code": "54071094",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : OTHER :NYLON FURNISHING FABRICS",
+        "hsn_code": "54071093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : OTHER :TENT FABRICS",
+        "hsn_code": "54071092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : OTHER : PARACHUTE FABRICS",
+        "hsn_code": "54071091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : PRINTED :POLYESTER SUITINGS",
+        "hsn_code": "54071049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : PRINTED :POLYESTER SUITINGS",
+        "hsn_code": "54071046",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : PRINTED :OTHER NYLON AND POLYAMIDE\nFABRICS (FILAMENT)",
+        "hsn_code": "54071045",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : PRINTED :UMBRELLA CLOTH PANEL\nFABRICS",
+        "hsn_code": "54071044",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : PRINTED :NYLON FURNISHING FABRICS",
+        "hsn_code": "54071043",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : PRINTED :TENT FABRICS",
+        "hsn_code": "54071042",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : PRINTED : PARACHUTE FABRICS",
+        "hsn_code": "54071041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : DYED :OTHER",
+        "hsn_code": "54071039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : DYED :POLYESTER SUITINGS",
+        "hsn_code": "54071036",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : DYED :OTHER NYLON AND POLYAMIDE\nFABRICS (FILAMENT)",
+        "hsn_code": "54071035",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : DYED :UMBRELLA CLOTH PANEL FABRICS",
+        "hsn_code": "54071034",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : DYED :NYLON FURNISHING FABRICS",
+        "hsn_code": "54071033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : DYED :TENT FABRICS",
+        "hsn_code": "54071032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : DYED :PARACHUTE FABRICS",
+        "hsn_code": "54071031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : BLEACHED : OTHER",
+        "hsn_code": "54071029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : BLEACHED : POLYESTER SUITINGS",
+        "hsn_code": "54071026",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : BLEACHED : OTHER NYLON AND\nPOLYAMIDE FABRICS OF FILAMENT YARN",
+        "hsn_code": "54071025",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : BLEACHED : UMBRELLA CLOTH PANEL\nFABRICS",
+        "hsn_code": "54071024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : BLEACHED : NYLON FURNISHING FABRICS",
+        "hsn_code": "54071023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : BLEACHED : TENT FABRICS",
+        "hsn_code": "54071022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : BLEACHED : PARACHUTE FABRICS",
+        "hsn_code": "54071021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : UNBLEACHED : OTHER POLYESTER FABRICS",
+        "hsn_code": "54071019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : UNBLEACHED : POLYESTER SUITINGS",
+        "hsn_code": "54071016",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : UNBLEACHED : OTHER NYLON AND\nPOLYAMIDE FABRICS (FILAMENT)",
+        "hsn_code": "54071015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : UNBLEACHED : UMBRELLA CLOTH PANEL\nFABRICS",
+        "hsn_code": "54071014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : UNBLEACHED : NYLON FURNISHING\nFABRICS",
+        "hsn_code": "54071013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : UNBLEACHED : TENT FABRICS",
+        "hsn_code": "54071012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC FILAMENT YARN, INCLUDING WOVEN FABRICS OBTAINED FROM\nMATERIALS OF HEADING 5404 WOVEN FABRICS OBTAINED FROM HIGH TENACITY YARN OF\nNYLON OR OTHER POLYAMIDES OR OF POLYESTERS : UNBLEACHED : PARACHUTE FABRICS",
+        "hsn_code": "54071011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BASKETWORK, WICKERWORK AND OTHER ARTICLES, MADE DIRECTLY TO SHAPE FROM PLAITING MATERIALS OR MADE UP FROM GOODS OF HEADING 4601; ARTICLES OF LOOFAH",
+        "hsn_code": "4602",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BASKETWORK, WICKERWORK AND OTHER ARTICLES, MADE DIRECTLY TO SHAPE FROM PLAITING\nMATERIALS OR MADE UP FROM GOODS OF HEADING 46 01; ARTICLES OF LOOFAH - OTHER",
+        "hsn_code": "46029000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BASKETWORK, WICKERWORK AND OTHER ARTICLES, MADE DIRECTLY TO SHAPE FROM PLAITING\nMATERIALS OR MADE UP FROM GOODS OF HEADING 46 01; ARTICLES OF LOOFAH OF\nVEGETABLE MATERIALS : -- OTHER: --- OTHER",
+        "hsn_code": "46021990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BASKETWORK, WICKERWORK AND OTHER ARTICLES, MADE DIRECTLY TO SHAPE FROM PLAITING\nMATERIALS OR MADE UP FROM GOODS OF HEADING 46 01; ARTICLES OF LOOFAH OF\nVEGETABLE MATERIALS : -- OTHER: --- OF PALM LEAVES : ---- OTHER",
+        "hsn_code": "46021919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BASKETWORK, WICKERWORK AND OTHER ARTICLES, MADE DIRECTLY TO SHAPE FROM PLAITING\nMATERIALS OR MADE UP FROM GOODS OF HEADING 46 01; ARTICLES OF LOOFAH OF\nVEGETABLE MATERIALS : -- OTHER: --- OF PALM LEAVES : ---- BASKET",
+        "hsn_code": "46021911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BASKETWORK, WICKERWORK AND OTHER ARTICLES, MADE DIRECTLY TO SHAPE FROM PLAITING\nMATERIALS OR MADE UP FROM GOODS OF HEADING 46 01; ARTICLES OF LOOFAH OF\nVEGETABLE MATERIALS : -- OF RATTAN",
+        "hsn_code": "46021200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BASKETWORK, WICKERWORK AND OTHER ARTICLES, MADE DIRECTLY TO SHAPE FROM PLAITING\nMATERIALS OR MADE UP FROM GOODS OF HEADING 46 01; ARTICLES OF LOOFAH - OF\nVEGETABLE MATERIALS :--OF BAMBOO",
+        "hsn_code": "46021100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BASKETWORK, WICKERWORK AND OTHER ARTICLES, MADE DIRECTLY TO SHAPE FROM PLAITING\nMATERIALS OR MADE UP FROM GOODS OF HEADING 46 01; ARTICLES OF LOOFAH OF\nVEGETABLE MATERIALS : OTHER",
+        "hsn_code": "46021090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BASKETWORK, WICKERWORK AND OTHER ARTICLES, MADE DIRECTLY TO SHAPE FROM PLAITING\nMATERIALS OR MADE UP FROM GOODS OF HEADING 46 01; ARTICLES OF LOOFAH OF\nVEGETABLE MATERIALS : OF PALM LEAVES : OTHER",
+        "hsn_code": "46021019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BASKETWORK, WICKERWORK AND OTHER ARTICLES, MADE DIRECTLY TO SHAPE FROM PLAITING\nMATERIALS OR MADE UP FROM GOODS OF HEADING 46 01; ARTICLES OF LOOFAH OF\nVEGETABLE MATERIALS : OF PALM LEAVES : BASKETS",
+        "hsn_code": "46021011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, WHETHER OR NOT ASSEMBLED INTO STRIPS; PLAITING MATERIALS, PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, BOUND TOGETHER IN PARALLEL STRANDS OR WOVEN, IN SHEET FORM, WHETHER OR NOT BEING FINISHED ARTICLES (FOR EXAMPLE, MATS, MATTING, SCREENS)",
+        "hsn_code": "4601",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, WHETHER OR NOT ASSEMBLED INTO\nSTRIPS ; PLAITING MATERIALS, PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, BOUND\nTOGETHER IN PARALLEL STRANDS OR WOVEN, IN SHEET FORM, WHETHER OR NOT BEING\nFINISHED ARTICLES (FOR EXAMPLE, MATS, MATTING, SCREENS) - OTHER : OTHER",
+        "hsn_code": "46019900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, WHETHER OR NOT ASSEMBLED INTO\nSTRIPS ; PLAITING MATERIALS, PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, BOUND\nTOGETHER IN PARALLEL STRANDS OR WOVEN, IN SHEET FORM, WHETHER OR NOT BEING\nFINISHED ARTICLES (FOR EXAMPLE, MATS, MATTING, SCREENS) - OTHER -- OF OTHER VEGETABLE\nMATERIALS",
+        "hsn_code": "46019400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, WHETHER OR NOT ASSEMBLED INTO\nSTRIPS ; PLAITING MATERIALS, PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, BOUND\nTOGETHER IN PARALLEL STRANDS OR WOVEN, IN SHEET FORM, WHETHER OR NOT BEING\nFINISHED ARTICLES (FOR EXAMPLE, MATS, MATTING, SCREENS) - OTHER --OF RATTAN",
+        "hsn_code": "46019300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, WHETHER OR NOT ASSEMBLED INTO\nSTRIPS ; PLAITING MATERIALS, PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, BOUND\nTOGETHER IN PARALLEL STRANDS OR WOVEN, IN SHEET FORM, WHETHER OR NOT BEING\nFINISHED ARTICLES (FOR EXAMPLE, MATS, MATTING, SCREENS) - OTHER -- OF BAMBOO",
+        "hsn_code": "46019200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, WHETHER OR NOT ASSEMBLED INTO\nSTRIPS ; PLAITING MATERIALS, PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, BOUND\nTOGETHER IN PARALLEL STRANDS OR WOVEN, IN SHEET FORM, WHETHER OR NOT BEING\nFINISHED ARTICLES (FOR EXAMPLE, MATS, MATTING, SCREENS) - OTHER : OF VEGETABLE\nMATERIALS",
+        "hsn_code": "46019100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, WHETHER OR NOT ASSEMBLED INTO\nSTRIPS ; PLAITING MATERIALS, PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, BOUND\nTOGETHER IN PARALLEL STRANDS OR WOVEN, IN SHEET FORM, WHETHER OR NOT BEING\nFINISHED ARTICLES (FOR EXAMPLE, MATS, MATTING, SCREENS) - MATS, MATTING AND SCREENS\nOF VEGETABLE MATERIALS:-- OTHER",
+        "hsn_code": "46012900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, WHETHER OR NOT ASSEMBLED INTO\nSTRIPS ; PLAITING MATERIALS, PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, BOUND\nTOGETHER IN PARALLEL STRANDS OR WOVEN, IN SHEET FORM, WHETHER OR NOT BEING\nFINISHED ARTICLES (FOR EXAMPLE, MATS, MATTING, SCREENS) - MATS, MATTING AND SCREENS\nOF VEGETABLE MATERIALS:-- OF RATTAN",
+        "hsn_code": "46012200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, WHETHER OR NOT ASSEMBLED INTO\nSTRIPS ; PLAITING MATERIALS, PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, BOUND\nTOGETHER IN PARALLEL STRANDS OR WOVEN, IN SHEET FORM, WHETHER OR NOT BEING\nFINISHED ARTICLES (FOR EXAMPLE, MATS, MATTING, SCREENS) - MATS, MATTING AND SCREENS\nOF VEGETABLE MATERIALS:-- OF BAMBOO",
+        "hsn_code": "46012100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, WHETHER OR NOT ASSEMBLED INTO\nSTRIPS ; PLAITING MATERIALS, PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, BOUND\nTOGETHER IN PARALLEL STRANDS OR WOVEN, IN SHEET FORM, WHETHER OR NOT BEING\nFINISHED ARTICLES (FOR EXAMPLE, MATS, MATTING, SCREENS) - MATS, MATTING AND SCREENS\nOF VEGETABLE MATERIALS : OTHER",
+        "hsn_code": "46012090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, WHETHER OR NOT ASSEMBLED INTO\nSTRIPS ; PLAITING MATERIALS, PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, BOUND\nTOGETHER IN PARALLEL STRANDS OR WOVEN, IN SHEET FORM, WHETHER OR NOT BEING\nFINISHED ARTICLES (FOR EXAMPLE, MATS, MATTING, SCREENS) - MATS, MATTING AND SCREENS\nOF VEGETABLE MATERIALS : MATS AND MATTING NOT ELSEWHERE INCLUDED OR SPECIFIED\n(FOR EXAMPLE, GRASS MATS)",
+        "hsn_code": "46012020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, WHETHER OR NOT ASSEMBLED INTO\nSTRIPS ; PLAITING MATERIALS, PLAITS AND SIMILAR PRODUCTS OF PLAITING MATERIALS, BOUND\nTOGETHER IN PARALLEL STRANDS OR WOVEN, IN SHEET FORM, WHETHER OR NOT BEING\nFINISHED ARTICLES (FOR EXAMPLE, MATS, MATTING, SCREENS) - MATS, MATTING AND SCREENS\nOF VEGETABLE MATERIALS : MATS AND MATTING OF COIR, BOUND IN PARALLEL STRANDS",
+        "hsn_code": "46012010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "INNER TUBES, OF RUBBER",
+        "hsn_code": "4013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "INNER TUBES, OF RUBBER - OTHER : OTHER",
+        "hsn_code": "40139090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "INNER TUBES, OF RUBBER - OTHER : OF A KIND USED IN TYRES OF CYCLE RICKSHAWS AND THREE\nWHEELED POWERED CYCLE-RICKSHAWS",
+        "hsn_code": "40139050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "INNER TUBES, OF RUBBER - OTHER : - FOR TRACTORS : REAR TYRES",
+        "hsn_code": "40139041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "INNER TUBES, OF RUBBER - OTHER : FOR OFF THE ROAD VEHICLES, NOT ELSEWHERE SPECIFIED\nOR INCLUDED",
+        "hsn_code": "40139030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "INNER TUBES, OF RUBBER - OTHER : FOR MOTOR CYCLE",
+        "hsn_code": "40139020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "INNER TUBES, OF RUBBER - OTHER : FOR AIRCRAFT",
+        "hsn_code": "40139010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "INNER TUBES, OF RUBBER OF A KIND USED ON BICYCLES",
+        "hsn_code": "40132000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "INNER TUBES, OF RUBBER - OF A KIND USED ON MOTOR CARS (INCLUDING STATION WAGONS\nAND RACING CARS), BUSES OR LORRIES : FOR LORRIES AND BUSES",
+        "hsn_code": "40131020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "INNER TUBES, OF RUBBER - OF A KIND USED ON MOTOR CARS (INCLUDING STATION WAGONS\nAND RACING CARS), BUSES OR LORRIES : FOR MOTOR CARS",
+        "hsn_code": "40131010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEW PNEUMATIC TYRES, OF RUBBER",
+        "hsn_code": "4011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEW PNEUMATIC TYRES, OF RUBBER - OTHER : OTHER",
+        "hsn_code": "40119900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEW PNEUMATIC TYRES, OF RUBBER - OTHER : OF A KIND USED ON CONSTRUCTION OR\nINDUSTRIAL HANDLING VEHICLES AND MACHINES AND HAVING A RIM SIZE EXCEEDING 61 CM",
+        "hsn_code": "40119400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEW PNEUMATIC TYRES, OF RUBBER - OTHER : OF A KIND USED ON CONSTRUCTION OR\nINDUSTRIAL HANDLING VEHICLES AND MACHINES AND HAVING A RIM SIZE NOT EXCEEDING 61\nCM",
+        "hsn_code": "40119300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEW PNEUMATIC TYRES, OF RUBBER - OTHER : OF A KIND USED ON AGRICULTURAL OR\nFORESTRY VEHICLES AND MACHINES",
+        "hsn_code": "40119200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEW PNEUMATIC TYRES, OF RUBBER - OTHER, HAVING A HERRING-BONE OR SIMILAR TREAD :\nOTHER",
+        "hsn_code": "40116900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEW PNEUMATIC TYRES, OF RUBBER - OTHER, HAVING A HERRING-BONE OR SIMILAR TREAD :\nOF A KIND USED ON CONSTRUCTION OR INDUSTRIAL HANDLING VEHICLES AND MACHINES AND\nHAVING A RIM SIZE EXCEEDING 61 CM",
+        "hsn_code": "40116300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEW PNEUMATIC TYRES, OF RUBBER - OTHER, HAVING A HERRING-BONE OR SIMILAR TREAD :\nOF A KIND USED ON CONSTRUCTION OR INDUSTRIAL HANDLING VEHICLES AND MACHINES AND\nHAVING A RIM SIZE NOT EXCEEDING 61 CM",
+        "hsn_code": "40116200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEW PNEUMATIC TYRES, OF RUBBER - OTHER, HAVING A HERRING-BONE OR SIMILAR TREAD :\nOF A KIND USED ON AGRICULTURAL OR FORESTRY VEHICLES AND MACHINES",
+        "hsn_code": "40116100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEW PNEUMATIC TYRES, OF RUBBER - OF A KIND USED ON BICYCLES : OTHER, HAVING A\nHERRING-BONE ORSIMILAR TREAD",
+        "hsn_code": "40115090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEW PNEUMATIC TYRES, OF RUBBER - OF A KIND USED ON BICYCLES : MULTI-CELLULAR\nPOLYURETHANE (MCP) TUBELESS TYRES",
+        "hsn_code": "40115010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEW PNEUMATIC TYRES, OF RUBBER - OF A KIND USED ON MOTOR CYCLES : OTHER",
+        "hsn_code": "40114090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEW PNEUMATIC TYRES, OF RUBBER - OF A KIND USED ON MOTOR CYCLES : FOR MOTOR\nSCOOTERS",
+        "hsn_code": "40114020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEW PNEUMATIC TYRES, OF RUBBER - OF A KIND USED ON MOTOR CYCLES : FOR MOTOR CYCLES",
+        "hsn_code": "40114010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEW PNEUMATIC TYRES, OF RUBBER - OF A KIND USED ON BUSES OR LORRIES : OTHER",
+        "hsn_code": "40112090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEW PNEUMATIC TYRES, OF RUBBER - OF A KIND USED ON BUSES OR LORRIES : RADIALS",
+        "hsn_code": "40112010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEW PNEUMATIC TYRES, OF RUBBER - OF A KIND USED ON MOTOR CARS (INCLUDING STATION\nWAGONS AND RACING CARS) : OTHER",
+        "hsn_code": "40111090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEW PNEUMATIC TYRES, OF RUBBER - OF A KIND USED ON MOTOR CARS (INCLUDING STATION\nWAGONS AND RACING CARS) : RADIALS",
+        "hsn_code": "40111010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MILK AND CREAM-FAT CONTENT, BY WEIGHT, &lt;= 1%",
+        "hsn_code": "4011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEW PNEUMATIC TYRES, OF RUBBER OF A KIND USED ON AIRCRAFT",
+        "hsn_code": "40113000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP OF RUBBER (OTHER THAN HARD RUBBER) AND POWDERS AND\nGRANULES OBTAINED THEREFROM",
+        "hsn_code": "40040000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PRE-SHAVE, SHAVING OR AFTER-SHAVE PREPARATIONS, PERSONAL DEODORANTS, BATH\nPREPARATIONS, DEPILATORIES AND OTHER PERFUMERY, COSMETIC OR TOILET PREPARATIONS,\nNOT ELSEWHERE SPECIFIED OR INCLUDED, PREPARED ROOM DEODORISERS, WHETHER OR NOT\nPERFUMED OR HAVING DISINFECTANT PROPERTIES PREPARATIONS FOR PERFUMING OR\nDEODORIZING ROOMS, INCLUDING ODORIFEROUS PREPARATIONS USED DURING RELIGIOUS\nRITES: AGARBATTI AND OTHER ODORIFEROUS PREPARATIONS WHICH OPERATE BY BURNING",
+        "hsn_code": "33074100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LIVE FISH -ORNAMENTAL FISH--FRESHWATER",
+        "hsn_code": "3011100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIVE FISH -ORNAMENTAL FISH--OTHER",
+        "hsn_code": "3011900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIVE FISHOTHER LIVE FISH TROUT (SALMO TRUTTA, ONCORHYNCHUS MYKISS, ONCORHYNCHUS\nCLARKI, ONCORHYNCHUSAGUABONITA, ONCORHYNCHUS GILAE,ONCORHYNCHUS APACHE AND\nONCORHYNCHUSCHRYSOGASTER",
+        "hsn_code": "3019100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIVE FISHOTHER LIVE FISH EELS (ANGUILLA SPP.)",
+        "hsn_code": "3019200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIVE FISH-OTHER LIVE FISH-- BLUEFIN TUNAS (THUNNUS THYNNUS)",
+        "hsn_code": "3019400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIVE FISH-OTHER LIVE FISH--SOUTHERN BLUEFIN TUNAS (THUNNUS MACCOYII)",
+        "hsn_code": "3019500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIVE FISHOTHER LIVE FISH OTHER",
+        "hsn_code": "3019900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING\n0304SALMONIDAE, EXCLUDING LIVERS AND ROES : TROUT (SALMO TRUTTA, ONCORHYNCHUS\nMYKISS, ONCORHYNCHUS CLARKI, ONCORHYNCHUSAGUABONITA, ONCORHYNCHUS\nGILAE,ONCORHYNCHUS APACHE AND ONCORHYNCHUSCHRYSOGASTER)",
+        "hsn_code": "3021100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING 0304-\nSALMONIDAE, EXCLUDING LIVERS AND ROES : --PACIFIC SALMON (ONCORHYNCHUS NERKA,\nONCORHYNCHUS GORBUSCHA, ONCORHYNCHUSKETA, ONCORHYNCHUS\nTSCHAWYTSCHA,ONCORHYNCHUS KISUTCH, ONCORHYNCHUSMASOU AND ONCORHYNCHUS\nRHODURUS)",
+        "hsn_code": "3021300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING 0304-\nSALMONIDAE, EXCLUDING LIVERS AND ROES : --ATLANTIC SALMON (SALMO SALAR) AND\nDANUBE SALMON (HUCHO HUCHO)",
+        "hsn_code": "3021400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING\n0304SALMONIDAE, EXCLUDING LIVERS AND ROES : OTHER",
+        "hsn_code": "3021900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING 0304FLAT\nFISH (PLEUTRONECTIDAE, BOTHIDAE,CYNOGLOSSIDAE, SOLEIDAE, SCOPHTHALMIDAEAND\nCITHARIDAE), EXCLUDING LIVERS AND ROES : HALIBUT (REINHARDTIUS HIPPOGLOSSOIDES,\nHIPPOGLOSSUS HIPPOGLOSSUS, HIPPOGLOSSUSSTENOLEPIS)",
+        "hsn_code": "3022100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING 0304FLAT\nFISH (PLEUTRONECTIDAE, BOTHIDAE,CYNOGLOSSIDAE, SOLEIDAE, SCOPHTHALMIDAEAND\nCITHARIDAE), EXCLUDING LIVERS AND ROES : PLAICE (PLEURONECTES PLATESSA)",
+        "hsn_code": "3022200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING 0304FLAT\nFISH (PLEUTRONECTIDAE, BOTHIDAE,CYNOGLOSSIDAE, SOLEIDAE, SCOPHTHALMIDAEAND\nCITHARIDAE), EXCLUDING LIVERS AND ROES : SOLE (SOLEA SPP.)",
+        "hsn_code": "3022300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING 0304FLAT\nFISH (PLEUTRONECTIDAE, BOTHIDAE,CYNOGLOSSIDAE, SOLEIDAE, SCOPHTHALMIDAEAND\nCITHARIDAE), EXCLUDING LIVERS AND ROES : OTHER",
+        "hsn_code": "3022900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING\n0304TUNAS (OF THE GENUS THUNNUS), SKIPJACK ORSTRIPE?BELLIED BONITO [EUTHYNNUS\n(KATSUWONUS)PELAMIS], EXCLUDING LIVERS AND ROES : ALBACORE OR LONGFINNED TUNAS",
+        "hsn_code": "3023100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING\n0304TUNAS (OF THE GENUS THUNNUS), SKIPJACK ORSTRIPE?BELLIED BONITO [EUTHYNNUS\n(KATSUWONUS)PELAMIS], EXCLUDING LIVERS AND ROES : YELLOWFIN TUNAS (THUNNUS\nALBACARES)",
+        "hsn_code": "3023200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING\n0304TUNAS (OF THE GENUS THUNNUS), SKIPJACK ORSTRIPE?BELLIED BONITO [EUTHYNNUS\n(KATSUWONUS)PELAMIS], EXCLUDING LIVERS AND ROES : SKIPJACK OR STRIPE BELLIED BONITO",
+        "hsn_code": "3023300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING\n0304TUNAS (OF THE GENUS THUNNUS), SKIPJACK ORSTRIPE?BELLIED BONITO [EUTHYNNUS\n(KATSUWONUS)PELAMIS], EXCLUDING LIVERS AND ROES : BIGEYE TUNAS (THUNNUS OBESUS)",
+        "hsn_code": "3023400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING\n0304TUNAS (OF THE GENUS THUNNUS), SKIPJACK ORSTRIPE?BELLIED BONITO [EUTHYNNUS\n(KATSUWONUS)PELAMIS], EXCLUDING LIVERS AND ROES : BLUEFIN TUNAS (THUNNUS THYNNUS)",
+        "hsn_code": "3023500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING\n0304TUNAS (OF THE GENUS THUNNUS), SKIPJACK ORSTRIPE?BELLIED BONITO [EUTHYNNUS\n(KATSUWONUS)PELAMIS], EXCLUDING LIVERS AND ROES : SOUTHERN BLUEFIN TUNAS\n(THUNNUS MACCOYII)",
+        "hsn_code": "3023600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING\n0304TUNAS (OF THE GENUS THUNNUS), SKIPJACK ORSTRIPE?BELLIED BONITO [EUTHYNNUS\n(KATSUWONUS)PELAMIS], EXCLUDING LIVERS AND ROES : OTHER",
+        "hsn_code": "3023900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING\n0304HERRINGS (CLUPEA HARENGUS, CLUPEA PALLASII) EXCLUDING LIVERS AND ROES",
+        "hsn_code": "3024000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISH FILLETS AND OTHER FISH MEAT OF HEADING 0304-\nHERRINGS (CLUPEA HARENGUS, CLUPEA PALLASII), ANCHOVIES (ENGRAULIS SPP.), SARDINES\n(SARDINA PILCHARDUS, SARDINOPS SPP.), SARDINELLA (SARDINELLA SPP.) BRISLING OR\nSPRATS(SPRATTUS SPRATTUS) , MACKEREL (SCOMBER SCOMBRUS, SCOMBER AUSTRALASICUS,\nSCOMBER JAPONICUS), JACK AND ORSE MACKEREL (TRACHURUS SPP.), COBIA (RACHYCENTRON\nCANADUM) AND SWORDFISH (XIPHIAS GLADIUS), EXCLUDING LIVERS AND ROES:-- HERRINGS\n(CLUPEA HARENGUS, CLUPEA PALLASII)",
+        "hsn_code": "3024100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISH FILLETS AND OTHER FISH MEAT OF HEADING 0304-\nHERRINGS (CLUPEA HARENGUS, CLUPEA PALLASII), ANCHOVIES (ENGRAULIS SPP.), SARDINES\n(SARDINA PILCHARDUS, SARDINOPS SPP.), SARDINELLA (SARDINELLA SPP.) BRISLING OR\nSPRATS(SPRATTUS SPRATTUS) , MACKEREL (SCOMBER SCOMBRUS, SCOMBER AUSTRALASICUS,\nSCOMBER JAPONICUS), JACK AND ORSE MACKEREL (TRACHURUS SPP.), COBIA (RACHYCENTRON\nCANADUM) AND SWORDFISH (XIPHIAS GLADIUS), EXCLUDING LIVERS AND ROES:--\nANCHOVIESB(ENGRAULIS SPP.)",
+        "hsn_code": "3024200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISH FILLETS AND OTHER FISH MEAT OF HEADING 0304-\nHERRINGS (CLUPEA HARENGUS, CLUPEA PALLASII), ANCHOVIES (ENGRAULIS SPP.), SARDINES\n(SARDINA PILCHARDUS, SARDINOPS SPP.), SARDINELLA (SARDINELLA SPP.) BRISLING OR\nSPRATS(SPRATTUS SPRATTUS) , MACKEREL (SCOMBER SCOMBRUS, SCOMBER AUSTRALASICUS,\nSCOMBER JAPONICUS), JACK AND ORSE MACKEREL (TRACHURUS SPP.), COBIA (RACHYCENTRON\nCANADUM) AND SWORDFISH (XIPHIAS GLADIUS), EXCLUDING LIVERS AND ROES:-- SARDINES\n(SARDINA PILCHARDUS, SARDINOPS SPP.), SARDINELLA (SARDINELLA SPP.) BRISLING OR\nSPRATS(SPRATTUS SPRATTUS)",
+        "hsn_code": "3024300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISH FILLETS AND OTHER FISH MEAT OF HEADING 0304-\nHERRINGS (CLUPEA HARENGUS, CLUPEA PALLASII), ANCHOVIES (ENGRAULIS SPP.), SARDINES\n(SARDINA PILCHARDUS, SARDINOPS SPP.), SARDINELLA (SARDINELLA SPP.) BRISLING OR\nSPRATS(SPRATTUS SPRATTUS) , MACKEREL (SCOMBER SCOMBRUS, SCOMBER AUSTRALASICUS,\nSCOMBER JAPONICUS), JACK AND ORSE MACKEREL (TRACHURUS SPP.), COBIA (RACHYCENTRON\nCANADUM) AND SWORDFISH (XIPHIAS GLADIUS), EXCLUDING LIVERS AND ROES:-- MACKEREL\n(SCOMBER SCOMBRUS, SCOMBER AUSTRALASICUS, SCOMBER JAPONICUS)",
+        "hsn_code": "3024400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISH FILLETS AND OTHER FISH MEAT OF HEADING 0304-\nHERRINGS (CLUPEA HARENGUS, CLUPEA PALLASII), ANCHOVIES (ENGRAULIS SPP.), SARDINES\n(SARDINA PILCHARDUS, SARDINOPS SPP.), SARDINELLA (SARDINELLA SPP.) BRISLING OR\nSPRATS(SPRATTUS SPRATTUS) , MACKEREL (SCOMBER SCOMBRUS, SCOMBER AUSTRALASICUS,\nSCOMBER JAPONICUS), JACK AND ORSE MACKEREL (TRACHURUS SPP.), COBIA (RACHYCENTRON\nCANADUM) AND SWORDFISH (XIPHIAS GLADIUS), EXCLUDING LIVERS AND ROES:-- JACK AND\nORSE MACKEREL (TRACHURUS SPP.)",
+        "hsn_code": "3024500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISH FILLETS AND OTHER FISH MEAT OF HEADING 0304-\nHERRINGS (CLUPEA HARENGUS, CLUPEA PALLASII), ANCHOVIES (ENGRAULIS SPP.), SARDINES\n(SARDINA PILCHARDUS, SARDINOPS SPP.), SARDINELLA (SARDINELLA SPP.) BRISLING OR\nSPRATS(SPRATTUS SPRATTUS) , MACKEREL (SCOMBER SCOMBRUS, SCOMBER AUSTRALASICUS,\nSCOMBER JAPONICUS), JACK AND HORSE MACKEREL (TRACHURUS SPP.), COBIA\n(RACHYCENTRON CANADUM) AND SWORDFISH (XIPHIAS GLADIUS), EXCLUDING LIVERS AND\nROES:-- COBIA (RACHYCENTRON CANADUM)",
+        "hsn_code": "3024600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISH FILLETS AND OTHER FISH MEAT OF HEADING 0304-\nHERRINGS (CLUPEA HARENGUS, CLUPEA PALLASII), ANCHOVIES (ENGRAULIS SPP.), SARDINES\n(SARDINA PILCHARDUS, SARDINOPS SPP.), SARDINELLA (SARDINELLA SPP.) BRISLING OR\nSPRATS(SPRATTUS SPRATTUS) , MACKEREL (SCOMBER SCOMBRUS, SCOMBER AUSTRALASICUS,\nSCOMBER JAPONICUS), JACK AND ORSE MACKEREL (TRACHURUS SPP.), COBIA (RACHYCENTRON\nCANADUM) AND SWORDFISH (XIPHIAS GLADIUS), EXCLUDING LIVERS AND ROES:-- SWORDFISH\n(XIPHIAS GLADIUS)",
+        "hsn_code": "3024700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING 0304COD\n(GADUS MORHUA, GADUS OGAC, GADUS MACROCEPHALUS), EXCLUDING LIVERS AND ROES",
+        "hsn_code": "3025000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COD (GADUS MORHUA, GADUS OGAS, GADUS MACROCEPHALUS)",
+        "hsn_code": "3025100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HADDOCK (MELANOGRAMMUS AEGLEFINUS)",
+        "hsn_code": "3025200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COALFISH (POLLACHIUS VIRENS)",
+        "hsn_code": "3025300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HAKE (MERLUCCIUS SPP., UROPHYCIS SPP.)",
+        "hsn_code": "3025400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ALASKA POLLACK(THERAGRA CHALCORRAMMA)",
+        "hsn_code": "3025500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BLUE WHITINGS (MICROMEISITIUS POTASSOU, MICROMEISITIUS AUSTRALIS)",
+        "hsn_code": "3025600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3025900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING 0304\nOTHER FISH, EXCLUDING LIVERS AND ROES : SARDINES (SARDINA PILCHARDUS, SARDINOPS\nSPP.), SARDINELLA (SARDINELLA SPP.) BRISLING OR SPRATS(SPRATTUS SPRATTUS)",
+        "hsn_code": "3026100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING 0304\nOTHER FISH, EXCLUDING LIVERS AND ROES HADDOCK (MELANOGRAMMUS AEGLEFINUS)",
+        "hsn_code": "3026200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING 0304\nOTHER FISH, EXCLUDING LIVERS AND ROES COALFISH (POLLACHIUS VIRENS)",
+        "hsn_code": "3026300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING 0304\nOTHER FISH, EXCLUDING LIVERS AND ROES MACKEREL (SCOMBER SCOMBRUS, SCOMBER\nAUSTRALASICUS, SCOMBER JAPONICUS)",
+        "hsn_code": "3026400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING 0304\nOTHER FISH, EXCLUDING LIVERS AND ROES DOGFISH AND OTHER SHARKS",
+        "hsn_code": "3026500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISH FILLETS AND OTHER FISH MEAT OF HEADING 0304-\nOTHER FISH, EXCLUDING LIVERS AND ROES:-- EELS (ANGUILLA SPP.)",
+        "hsn_code": "3026600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISH FILLETS AND OTHER FISH MEAT OF HEADING 0304-\nOTHER FISH, EXCLUDING LIVERS AND ROES:-- SWORDFISH (XIPHIAS GLADIUS)",
+        "hsn_code": "3026700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISH FILLETS AND OTHER FISH MEAT OF HEADING 0304-\nOTHER FISH, EXCLUDING LIVERS AND ROES:-- TOOTHFISH (DISSOSTICHUS SPP.)",
+        "hsn_code": "3026800",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING 0304\nOTHER FISH, EXCLUDING LIVERS AND ROES OTHER : HILSA",
+        "hsn_code": "3026910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING 0304\nOTHER FISH, EXCLUDING LIVERS AND ROES OTHER DARA",
+        "hsn_code": "3026920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING 0304\nOTHER FISH, EXCLUDING LIVERS AND ROES OTHER POMFRET",
+        "hsn_code": "3026930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING 0304\nOTHER FISH, EXCLUDING LIVERS AND ROES OTHER OTHER",
+        "hsn_code": "3026990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISHFILLETS AND OTHER FISH MEAT OF HEADING\n0304LIVERS AND ROES",
+        "hsn_code": "3027000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TILAPIAS (OREOCHROMIS SPP.P",
+        "hsn_code": "3027100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CATFISH (PANGALASIUS SPP.,SILURUS SPP.,CLARIAS SPP., ICTALURUS SPP.)",
+        "hsn_code": "3027200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARP(CYPRINUS CARPIO, CARASSIUS CARASSIUS, CLENPHARYNGADON IDELLUS, HYPOTASPP.,\nMYLOPHARYNGODON PICEUS)LMICHTHYS SPP., CIRRHINUS",
+        "hsn_code": "3027300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "EELS (ANGUILLA SPP.)",
+        "hsn_code": "3027400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3027900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "DOGFISH AND OTHER SHARKS",
+        "hsn_code": "3028100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAYS AND SKATES (RAJIDAE)",
+        "hsn_code": "3028200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TOOTHFISH (DISSOSTICHUS SPP.)",
+        "hsn_code": "3028300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SEA BASS (DICENTRARCHUS SPP.)",
+        "hsn_code": "3028400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SEABREAM(SPARIDAE)",
+        "hsn_code": "3028500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HILSA",
+        "hsn_code": "3028910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "DARA",
+        "hsn_code": "3028920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "POMFRET",
+        "hsn_code": "3028930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3028990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIVERS AND ROES",
+        "hsn_code": "3029000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 PACIFIC\nSALMON (ONCORHYNCHUS NERKA,ONCORHYNCHUS GORBUSCHA, ONCORHYNCHUSKETA,\nONCORHYNCHUS TSCHAWYTSCHA,ONCORHYNCHUS KISUTCH, ONCORHYNCHUSMASOU AND\nONCORHYNCHUS RHODURUS),EXCLUDING LIVERS AND ROES : SOCKEYE SALMON (RED-SALMON)\n(ONCORHYNCHUS NERKA)",
+        "hsn_code": "3031100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER PACIFIC NSALMON(ONCORHYNCHUS GORBUSCHA, ONCORHYNCHUSKETA,\nONCORHYNCHUS TSCHAWYTSCHA,ONCORHYNCHUS KISUTCH, ONCORHYNCHUSMASOU AND\nONCORHYNCHUS RHODURUS),",
+        "hsn_code": "3031200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ATLANTIC SALMON (SALMO SALAR) AND DANUBE SALMON (HUCHO HUCHO)",
+        "hsn_code": "3031300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TROUT (SALMO TRUTTA, ONCORHYNCHUS MYKISS, ONCORHYNCHUS CLARKI,ONCORHYNCHUS\nAGUABONITA,ONCORHYNCHUS GILAE, ONCORHYNCHUS APACHE ANDONCORHYNCHUS\nCHRYSOGASTER)",
+        "hsn_code": "3031400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304? PACIFIC\nSALMON (ONCORHYNCHUS NERKA,ONCORHYNCHUS GORBUSCHA, ONCORHYNCHUSKETA,\nONCORHYNCHUS TSCHAWYTSCHA,ONCORHYNCHUS KISUTCH, ONCORHYNCHUSMASOU AND\nONCORHYNCHUS RHODURUS),EXCLUDING LIVERS AND ROES : OTHER",
+        "hsn_code": "3031900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER\nSALMONIDAE, EXCLUDING LIVERS AND ROES TROUT (SALMO TRUTTA, ONCORHYNCHUS\nMYKISS, ONCORHYNCHUS CLARKI,ONCORHYNCHUS AGUABONITA,ONCORHYNCHUS GILAE,\nONCORHYNCHUS APACHE ANDONCORHYNCHUS CHRYSOGASTER)",
+        "hsn_code": "3032100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER\nSALMONIDAE, EXCLUDING LIVERS AND ROES ATLANTIC SALMON (SALMO SALAR) AND\nDANUBE SALMON (HUCHO HUCHO)",
+        "hsn_code": "3032200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "tilapias(preochromia spp.)",
+        "hsn_code": "3032300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CATFISH (PANGASIUS SPP., SILURUS SPP., CLARIAS SPP., ICTALURUS SPP.)",
+        "hsn_code": "3032400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARP(CYPRINUS CARPIO, CARASSIUS CARASSIUS, CLENPHARYNGADON IDELLUS,\nHYPOPHTHALMICTHYS SPP. CIRRHINUS SPP.",
+        "hsn_code": "3032500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "EELS (ANGUILLA SPP.)",
+        "hsn_code": "3032600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER\nSALMONIDAE, EXCLUDING LIVERS AND ROES OTHER",
+        "hsn_code": "3032900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 FLAT FISH\n(PLEURONECTIDAE, BOTHIDAE,CYNOGLOSSIDAE, SOLEIDAE, SCOPHTHALMIDAEAND\nCITHARIDAE), EXCLUDING LIVERS AND ROES : HALIBUT (REINHARDTIUS HIPPOGLOSSOIDES,\nHIPPOGLOSSUS HIPPOGLOSSUS, HIPPOGLOSSUSSTENOLEPIS)",
+        "hsn_code": "3033100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304FLAT FISH\n(PLEURONECTIDAE, BOTHIDAE,CYNOGLOSSIDAE, SOLEIDAE, SCOPHTHALMIDAEAND\nCITHARIDAE), EXCLUDING LIVERS AND ROES : PLAICE (PLEURONECTES PLATESSA)",
+        "hsn_code": "3033200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304FLAT FISH\n(PLEURONECTIDAE, BOTHIDAE,CYNOGLOSSIDAE, SOLEIDAE, SCOPHTHALMIDAEAND\nCITHARIDAE), EXCLUDING LIVERS AND ROES : SOLE (SOLEA SPP.)",
+        "hsn_code": "3033300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 FLAT FISH\n(PLEURONECTIDAE, BOTHIDAE,CYNOGLOSSIDAE, SOLEIDAE, SCOPHTHALMIDAEAND\nCITHARIDAE), EXCLUDING LIVERS AND ROES : OTHER",
+        "hsn_code": "3033900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304TUNAS (OF THE\nGENUS THUNNUS), SKIPJACKOR STRIPE?BELLIED BONITO [EUTHYNNUS(KATSUWONUS)\nPELAMIS], EXCLUDING LIVERSAND ROES : FLAT FISH ALBACORE OR LONGFINNED TUNAS\n(THUNNUS ALALUNGA)",
+        "hsn_code": "3034100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304TUNAS (OF THE\nGENUS THUNNUS), SKIPJACKOR STRIPE?BELLIED BONITO [EUTHYNNUS(KATSUWONUS)\nPELAMIS], EXCLUDING LIVERSAND ROES : YELLOWFIN TUNAS (THUNNUS ALBACARES)",
+        "hsn_code": "3034200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304TUNAS (OF THE\nGENUS THUNNUS), SKIPJACKOR STRIPE?BELLIED BONITO [EUTHYNNUS(KATSUWONUS)\nPELAMIS], EXCLUDING LIVERSAND ROES : SKIPJACK OR STRIPE-BELLIED BONITO",
+        "hsn_code": "3034300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304TUNAS (OF THE\nGENUS THUNNUS), SKIPJACKOR STRIPE?BELLIED BONITO [EUTHYNNUS(KATSUWONUS)\nPELAMIS], EXCLUDING LIVERSAND ROES : BIGEYE TUNAS (THUNNUS OBESUS)",
+        "hsn_code": "3034400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304TUNAS (OF THE\nGENUS THUNNUS), SKIPJACKOR STRIPE?BELLIED BONITO [EUTHYNNUS(KATSUWONUS)\nPELAMIS], EXCLUDING LIVERSAND ROES : BLUEFIN TUNAS (THUNNUS THYNNUS)",
+        "hsn_code": "3034500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304TUNAS (OF THE\nGENUS THUNNUS), SKIPJACKOR STRIPE?BELLIED BONITO [EUTHYNNUS(KATSUWONUS)\nPELAMIS], EXCLUDING LIVERSAND ROES : SOUTHERN BLUEFIN TUNAS (THUNNUS MACCOYII)",
+        "hsn_code": "3034600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304TUNAS (OF THE\nGENUS THUNNUS), SKIPJACKOR STRIPE?BELLIED BONITO [EUTHYNNUS(KATSUWONUS)\nPELAMIS], EXCLUDING LIVERSAND ROES : OTHER",
+        "hsn_code": "3034900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304HERRINGS\n(CLUPEA HARENGUS, CLUPEA PALLASII), EXCLUDING LIVERS AND ROES",
+        "hsn_code": "3035000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 -HERRINGS\n(CLUPEA HARENGUS, CLUPEA PALLASII) AND COD (GADUS MORHUA, GADUS OGAC, GADUS\nMACROCEPHALUS) EXCLUDING LIVERS AND ROES: --HERRINGS (CLUPEA HARENGUS, CLUPEA\nPALLASII)",
+        "hsn_code": "3035100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 -HERRINGS\n(CLUPEA HARENGUS, CLUPEA PALLASII) AND COD (GADUS MORHUA, GADUS OGAC, GADUS\nMACROCEPHALUS) EXCLUDING LIVERS AND ROES: -- COD (GADUS MORHUA, GADUS OGAC,\nGADUS MACROCEPHALUS)",
+        "hsn_code": "3035200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SARDINES (SARDINA PILCHARDUS, SARDINOPS SPP.), SARDINELLA (SARDINELLA SPP.) BRISLING\nOR SPRATS(SPRATTUS SPRATTUS)",
+        "hsn_code": "3035300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MACKEREL (SCOMBER SCOMBRUS, SCOMBER AUSTRALASICUS, SCOMBER JAPONICUS),",
+        "hsn_code": "3035400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ACK AND ORSE MACKEREL (TRACHURUS SPP.)",
+        "hsn_code": "3035500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COBIA (RACHYCENTRON CANADUM)",
+        "hsn_code": "3035600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SWORDFISH (XIPHIAS GLADIUS)",
+        "hsn_code": "3035700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 COD (GADUS\nMORHUA, GADUS OGAC, GADUS MACROCEPHALUS), EXCLUDING LIVERS AND ROES",
+        "hsn_code": "3036000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 -\nSWORDFISH(XIPHIA GLADIUS) AND TOOTHFISH (DISSOSTICHUS SPP.) EXCLUDING LIVERS AND\nROES: -- SWORDFISH(XIPHIA GLADIUS)",
+        "hsn_code": "3036100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 -\nSWORDFISH(XIPHIA GLADIUS) AND TOOTHFISH (DISSOSTICHUS SPP.) EXCLUDING LIVERS AND\nROES: -- TOOTHFISH (DISSOSTICHUS SPP.)",
+        "hsn_code": "3036200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COD (GADUS MORHUA, GADUS OGAS, GADUS MACROCEPHALUS)",
+        "hsn_code": "3036300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COALFISH (POLLACHIUS VIRENS)",
+        "hsn_code": "3036500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HAKE (MERLUCCIUS SPP., UROPHYCIS SPP.)",
+        "hsn_code": "3036600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ALASKA POLLACK(THERAGRA CHALCORRAMMA)",
+        "hsn_code": "3036700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BLUE WHITINGS (MICROMEISITIUS POTASSOU, MICROMEISITIUS AUSTRALIS)",
+        "hsn_code": "3036800",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "other",
+        "hsn_code": "3036900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER FISH,\nEXCLUDING LIVERS AND ROES : SARDINES (SARDINA PILCHARDUS, SARDINOPS SPP.), SARDINELLA\n(SARDINELLA SPP.), BRISLING OR SPRATS(SPRATTUS SPRATTUS)",
+        "hsn_code": "3037100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER FISH,\nEXCLUDING LIVERS AND ROES : HADDOCK (MELANOGRAMMUS AEGLEFINUS)",
+        "hsn_code": "3037200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER FISH,\nEXCLUDING LIVERS AND ROES : COALFISH (POLLACHIUS VIRENS)",
+        "hsn_code": "3037300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER FISH,\nEXCLUDING LIVERS AND ROES : MACKEREL (SCOMBER SCOMBRUS, SCOMBER AUSTRALASICUS,\nSCOMBER JAPONICUS)",
+        "hsn_code": "3037400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER FISH,\nEXCLUDING LIVERS AND ROES : DOGFISH AND OTHER SHARKS",
+        "hsn_code": "3037500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER FISH,\nEXCLUDING LIVERS AND ROES : EELS (ANGUILLA SPP.)",
+        "hsn_code": "3037600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER FISH,\nEXCLUDING LIVERS AND ROES : SEA BASS (DICENTRARCHUS LABRAX, DICENTRARCHUS\nPUNCTATUS)",
+        "hsn_code": "3037700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER FISH,\nEXCLUDING LIVERS AND ROES : HAKE (MERLUCCIUS SPP., UROPHYCIS SPP.)",
+        "hsn_code": "3037800",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER : HILSA",
+        "hsn_code": "3037910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER : DARA",
+        "hsn_code": "3037920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER :\nRIBBON FISH",
+        "hsn_code": "3037930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER : SEER",
+        "hsn_code": "3037940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER :\nPOMFRET (WHITE OR SILVER OR BLACK)",
+        "hsn_code": "3037950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER :\nGHOLE",
+        "hsn_code": "3037960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER :\nTHREADFIN",
+        "hsn_code": "3037970",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER :\nCROACKER, GROUPER, HOUNDER",
+        "hsn_code": "3037980",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER :\nEDIBLE FISHMAWS OF WILD LIFE",
+        "hsn_code": "3037991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER :\nEDIBLE SHARK FINS OF WILD LIFE",
+        "hsn_code": "3037992",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304 OTHER :\nOTHER",
+        "hsn_code": "3037999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304LIVERS AND\nROES EGG OR EGG YOLK OF FISH INCLUDING SHRIMPS",
+        "hsn_code": "3038010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS ANDOTHER FISH MEAT OF HEADING 0304LIVERS AND\nROES OTHER",
+        "hsn_code": "3038090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "DOGFISH",
+        "hsn_code": "3038110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SHARKS",
+        "hsn_code": "3038190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAYS AND SKATES (RAJIDAE)",
+        "hsn_code": "3038200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TOOTHFISH (DISSOSTICHUS SPP.)",
+        "hsn_code": "3038300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SEA BASS (DICENTRARCHUS SPP.)",
+        "hsn_code": "3038400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HILSA",
+        "hsn_code": "3038910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "DARA",
+        "hsn_code": "3038920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RIBBON FISH",
+        "hsn_code": "3038930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SEER",
+        "hsn_code": "3038940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "POMFRET (WHITE OR SILVER OR BLACK)",
+        "hsn_code": "3038950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GHOLE",
+        "hsn_code": "3038960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "THR3ADFIN",
+        "hsn_code": "3038970",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CROACKERS GROUPERS, FLOUNDERS",
+        "hsn_code": "3038980",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "EDIBLE FISHMAWS OF WILD LIFE",
+        "hsn_code": "3038991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "EDIBLE SHARK FINS OF WILD LIFE",
+        "hsn_code": "3038992",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3038999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "EGG OR EGG YOLK OF FISH",
+        "hsn_code": "3039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN\nFRESH OR CHILLED",
+        "hsn_code": "3041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN -\nFRESH OR CHILLED: -- SWORDFISH (XIPHIA GLADIUS)",
+        "hsn_code": "3041100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN\nFRESH OR CHILLED: -- TOOTHFISH (DISSOSTICHUS SPP.)",
+        "hsn_code": "3041200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN -\nFRESH OR CHILLED: -- OTHER",
+        "hsn_code": "3041900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN\nFROZEN FILLETS HILSA",
+        "hsn_code": "3042010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN\nFROZEN FILLETS SHARK",
+        "hsn_code": "3042020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN\nFROZEN FILLETS SEER",
+        "hsn_code": "3042030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN\nFROZEN FILLETS TUNA",
+        "hsn_code": "3042040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN\nFROZEN FILLETS CUTTLEFISH",
+        "hsn_code": "3042050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN\nFROZEN FILLETS OTHER",
+        "hsn_code": "3042090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN\nFROZEN FILLETS: SWORDFISH (XIPHIAS GLADIUS)",
+        "hsn_code": "3042100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN\nFROZEN FILLETS: TOOTHFISH (DISSOSTICHUS SPP.)",
+        "hsn_code": "3042200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN\nFROZEN FILLETS:-- OTHER: --- HILSA",
+        "hsn_code": "3042910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN\nFROZEN FILLETS: --OTHER: --- SHARK",
+        "hsn_code": "3042920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN\nFROZEN FILLETS: --OTHER:--- SEER",
+        "hsn_code": "3042930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN\nFROZEN FILLETS: --OTHER --- TUNA",
+        "hsn_code": "3042940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN -\nFROZEN FILLETS: --OTHER: --- CUTTLEFISH",
+        "hsn_code": "3042950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN\nFROZEN FILLETS: --OTHER: --- OTHER",
+        "hsn_code": "3042990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TILAPIAS(OREOCHROMIS SPP.P",
+        "hsn_code": "3043100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CATFISH (PANGASIUS SPP., SILURUS SPP., CLARIAS SPP., ICTALURUS SPP.)",
+        "hsn_code": "3043200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NILE PERCH (LATES NILOTICUS)",
+        "hsn_code": "3043300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3043900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PACIFIC SALMON(ONCORHYNCHUS NERKA, ONCORHYNCHUS GORBUSCHA,\nONCORHYNCHUSKETA, ONCORHYNCHUS TSCHAWYTSCHA,ONCORHYNCHUS KISUTCH,\nONCORHYNCHUSMASOU AND ONCORHYNCHUS RHODURUS),ATLANTIC SALMON (SALMO SALAR)\nAND DANUBE SALMON (HUCHO HUCHO)",
+        "hsn_code": "3044100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TROUT (SALMO TRUTTA, ONCORHYNCHUS MYKISS, ONCORHYNCHUS CLARKI,ONCORHYNCHUS\nAGUABONITA,ONCORHYNCHUS GILAE, ONCORHYNCHUS APACHE ANDONCORHYNCHUS\nCHRYSOGASTER)",
+        "hsn_code": "3044200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FLAT FISH (PLEURONECTIDAE, BOTHIDAE,CYNOGLOSSIDAE, SOLEIDAE, SCOPHTHALMIDAEAND\nCITHARIDAE),",
+        "hsn_code": "3044300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH OF THE FAMILIES BREGMACEROLIDAE, EUCLICHTHYIDAE, GADIDAE, MACROURIDAE,\nMELANONIDAE, MERLUCCIIDAE, MORIDAE AND MURAENOLEPIDIDAE",
+        "hsn_code": "3044400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SWORDFISH (XIPHIAS GLADIUS)",
+        "hsn_code": "3044500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TOOTHFISH (DISSOSTICHUS SPP.)",
+        "hsn_code": "3044600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HILSA",
+        "hsn_code": "3044910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SHARK",
+        "hsn_code": "3044920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SEER",
+        "hsn_code": "3044930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TUNA",
+        "hsn_code": "3044940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3044990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TILAPIAS (OREOCHROMIS SPP) ,CATFISH (PANGALASIUS SPP.,SILURUS SPP.,CLARIAS SPP.,\nICTALURUS SPP.) CARP(CYPRINUS CARPIO, CARASSIUS CARASSIUS, CTENPHARYNGODON\nIDELLUS, HYPOPHTHALMICHTHYS SPP., CIRRHINUS, MYLOPHARYNGODON PICEUS) ,\nEELS(ANGUILLA SPP.),NILE PERCH,(LATES NILOTICUS) AND SNAKEHEADS(CHANNA SPP.)",
+        "hsn_code": "3045100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SALMONIDAE",
+        "hsn_code": "3045200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH OF THE FAMILIES BREGMACEROLIDAE, EUCLICHTHYIDAE, GADIDAE, MACROURIDAE,\nMELANONIDAE, MERLUCCIIDAE, MORIDAE AND MURAENOLEPIDIDAE",
+        "hsn_code": "3045300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SWORDFISH (XIPHIAS GLADIUS)",
+        "hsn_code": "3045400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TOOTHFISH (DISSOSTICHUS SPP.)",
+        "hsn_code": "3045500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HILSA",
+        "hsn_code": "3045910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SHARK",
+        "hsn_code": "3045920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SEER",
+        "hsn_code": "3045930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TUNA",
+        "hsn_code": "3045940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3045990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "tilapias(Oreochromia spp.)",
+        "hsn_code": "3046100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CATFISH (PANGASIUS SPP., SILURUS SPP., CLARIAS SPP., ICTALURUS SPP.)",
+        "hsn_code": "3046200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NILE PERCH (LATES NILOTICUS)",
+        "hsn_code": "3046300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3046900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COD (GADUS MORHUA, GADUS OGAS, GADUS MACROCEPHALUS)",
+        "hsn_code": "3047100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HADDOCK (MELANOGRAMMUS AEGLEFINUS)",
+        "hsn_code": "3047200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COALFISH (POLLACHIUS VIRENS)",
+        "hsn_code": "3047300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HAKE (MERLUCCIUS SPP., UROPHYCIS SPP.)",
+        "hsn_code": "3047400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ALASKA POLLACK(THERAGRA CHALCORRAMMA)",
+        "hsn_code": "3047500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3047900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PACIFIC SALMON(ONCORHYNCHUS NERKA, ONCORHYNCHUS GORBUSCHA,\nONCORHYNCHUSKETA, ONCORHYNCHUS TSCHAWYTSCHA,ONCORHYNCHUS KISUTCH,\nONCORHYNCHUSMASOU AND ONCORHYNCHUS RHODURUS),ATLANTIC SALMON (SALMO SALAR)\nAND DANUBE SALMON (HUCHO HUCHO)",
+        "hsn_code": "3048100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TROUT (SALMO TRUTTA, ONCORHYNCHUS MYKISS, ONCORHYNCHUS CLARKI,ONCORHYNCHUS\nAGUABONITA,ONCORHYNCHUS GILAE, ONCORHYNCHUS APACHE ANDONCORHYNCHUS\nCHRYSOGASTER)",
+        "hsn_code": "3048200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FLAT FISH (PLEURONECTIDAE, BOTHIDAE,CYNOGLOSSIDAE, SOLEIDAE, SCOPHTHALMIDAEAND\nCITHARIDAE),",
+        "hsn_code": "3048300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SWORDFISH (XIPHIAS GLADIUS)",
+        "hsn_code": "3048400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TOOTHFISH (DISSOSTICHUS SPP.)",
+        "hsn_code": "3048500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HERRINGS (CLUPEA HARENGUS, CLUPEA PALLASII)",
+        "hsn_code": "3048600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TUNAS (OF THE GENUS THUNNUS), SKIPJACK ORSTRIPE-BELLIED BONITO [EUTHYNNUS\n(KATSUWONUS)PELAMIS],",
+        "hsn_code": "3048700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HILSA",
+        "hsn_code": "3048910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SHARK",
+        "hsn_code": "3048920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SEER",
+        "hsn_code": "3048930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3048990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN\nOTHER",
+        "hsn_code": "3049000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN\nOTHER: --SWORDFISH (XIPHIAS GLADIUS)",
+        "hsn_code": "3049100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN\nOTHER: --TOOTHFISH (DISSOSTICHUS SPP.)",
+        "hsn_code": "3049200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TILAPIAS (OREOCHROMIS SPP) ,CATFISH (PANGALASIUS SPP.,SILURUS SPP.,CLARIAS SPP.,\nICTALURUS SPP.) CARP(CYPRINUS CARPIO, CARASSIUS CARASSIUS, CTENPHARYNGODON\nIDELLUS, HYPOPHTHALMICHTHYS SPP., CIRRHINUS, MYLOPHARYNGODON PICEUS) ,\nEELS(ANGUILLA SPP.),NILE PERCH,(LATES NILOTICUS) AND SNAKEHEADS(CHANNA SPP.)",
+        "hsn_code": "3049300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ALASKA POLLACK(THERAGRA CHALCORRAMMA)",
+        "hsn_code": "3049400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH OF THE FAMILIES BREGMACEROLIDAE, EUCLICHTHYIDAE, GADIDAE, MACROURIDAE,\nMELANONIDAE, MERLUCCIIDAE, MORIDAE AND MURAENOLEPIDIDAE , OTHER THAN ALASKA\nPOLLACK(THERAGRA CHALCORRAMMA)",
+        "hsn_code": "3049500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT(WHETHER OR NOT MINCED), FRESH,CHILLED OR FROZEN\nOTHER : -- OTHER",
+        "hsn_code": "3049900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, DRIED, SALTED OR IN BRINE; SMOKEDFISH, WHETHER OR NOT COOKED BEFORE ORDURING\nTHE SMOKING PROCESS; FLOURS,MEALS AND PELLETS, OF FISH FIT FOR\nHUMANCONSUMPTIONFLOURS, MEALS AND PELLETS, OF FISH FIT FOR HUMAN CONSUMPTION",
+        "hsn_code": "3051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, DRIED, SALTED OR IN BRINE; SMOKEDFISH, WHETHER OR NOT COOKED BEFORE ORDURING\nTHE SMOKING PROCESS; FLOURS,MEALS AND PELLETS, OF FISH FIT FOR HUMANCONSUMPTION\nLIVERS AND ROES OF FISH, DRIED, SMOKED, SALTED OR IN BRINE",
+        "hsn_code": "3052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS, DRIED, SALTED OR IN BRINE, BUT NOT SMOKED",
+        "hsn_code": "3053000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TILAPIAS (OREOCHROMIS SPP) ,CATFISH (PANGALASIUS SPP.,SILURUS SPP.,CLARIAS SPP.,\nICTALURUS SPP.) CARP(CYPRINUS CARPIO, CARASSIUS CARASSIUS, CTENPHARYNGODON\nIDELLUS, HYPOPHTHALMICHTHYS SPP., CIRRHINUS, MYLOPHARYNGODON PICEUS) ,\nEELS(ANGUILLA SPP.),NILE PERCH,(LATES NILOTICUS) AND SNAKEHEADS(CHANNA SPP.)",
+        "hsn_code": "3053100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH OF THE FAMILIES BREGMACEROLIDAE, EUCLICHTHYIDAE, GADIDAE, MACROURIDAE,\nMELANONIDAE, MERLUCCIIDAE, MORIDAE AND MURAENOLEPIDIDAE",
+        "hsn_code": "3053200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3053900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, DRIED, SALTED OR IN BRINE; SMOKEDFISH, WHETHER OR NOT COOKED BEFORE ORDURING\nTHE SMOKING PROCESS; FLOURS,MEALS AND PELLETS, OF FISH FIT FOR HUMANCONSUMPTION\nSMOKED FISH, INCLUDING FILLETS : PACIFIC SALMON (ONCORHYNCHUS NERKA,\nONCORHYNCHUS GORBUSCHA, ONCORHYNCHUSKETA, ONCORHYNCHUS\nTSCHAWYTSCHA,ONCORHYNCHUS KISUTCH, ONCORHYNCHUSMASOU AND ONCORHYNCHUS\nRHODURUS),ATLANTIC SALMON (SALMO SALAR)AND DANUBE SALMON (HUCHO HUCHO)",
+        "hsn_code": "3054100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, DRIED, SALTED OR IN BRINE; SMOKEDFISH, WHETHER OR NOT COOKED BEFORE ORDURING\nTHE SMOKING PROCESS; FLOURS,MEALS AND PELLETS, OF FISH FIT FOR HUMANCONSUMPTION\nSMOKED FISH, INCLUDING FILLETS : HERRINGS (CLUPEA HARENGUS, CLUPEA PALLASII )",
+        "hsn_code": "3054200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TROUT (SALMO TRUTTA, ONCORHYNCHUS MYKISS, ONCORHYNCHUS CLARKI,ONCORHYNCHUS\nAGUABONITA,ONCORHYNCHUS GILAE, ONCORHYNCHUS APACHE ANDONCORHYNCHUS\nCHRYSOGASTER)",
+        "hsn_code": "3054300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TILAPIAS (OREOCHROMIS SPP) ,CATFISH (PANGALASIUS SPP.,SILURUS SPP.,CLARIAS SPP.,\nICTALURUS SPP.) CARP(CYPRINUS CARPIO, CARASSIUS CARASSIUS, CTENPHARYNGODON\nIDELLUS, HYPOPHTHALMICHTHYS SPP., CIRRHINUS, MYLOPHARYNGODON PICEUS) ,\nEELS(ANGUILLA SPP.),NILE PERCH,(LATES NILOTICUS) AND SNAKEHEADS(CHANNA SPP.)",
+        "hsn_code": "3054400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, DRIED, SALTED OR IN BRINE; SMOKEDFISH, WHETHER OR NOT COOKED BEFORE ORDURING\nTHE SMOKING PROCESS; FLOURS,MEALS AND PELLETS, OF FISH FIT FOR HUMANCONSUMPTION\nSMOKED FISH, INCLUDING FILLETS : OTHER",
+        "hsn_code": "3054900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, DRIED, SALTED OR IN BRINE; SMOKEDFISH, WHETHER OR NOT COOKED BEFORE ORDURING\nTHE SMOKING PROCESS; FLOURS,MEALS AND PELLETS, OF FISH FIT FOR HUMANCONSUMPTION\nDRIED FISH, WHETHER OR NOT SALTED BUT NOTSMOKED : COD (GADUS MORHUA, GADUS\nOGAC, GADUS MACROCEPHALUS)",
+        "hsn_code": "3055100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, DRIED, SALTED OR IN BRINE; SMOKEDFISH, WHETHER OR NOT COOKED BEFORE ORDURING\nTHE SMOKING PROCESS; FLOURS,MEALS AND PELLETS, OF FISH FIT FOR HUMANCONSUMPTION\nDRIED FISH, WHETHER OR NOT SALTED BUT NOTSMOKED : OTHER : MUMBAI DUCK",
+        "hsn_code": "3055910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, DRIED, SALTED OR IN BRINE; SMOKEDFISH, WHETHER OR NOT COOKED BEFORE ORDURING\nTHE SMOKING PROCESS; FLOURS,MEALS AND PELLETS, OF FISH FIT FOR HUMANCONSUMPTION\nDRIED FISH, WHETHER OR NOT SALTED BUT NOTSMOKED : OTHER : SEER WITHOUT HEAD",
+        "hsn_code": "3055920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, DRIED, SALTED OR IN BRINE; SMOKEDFISH, WHETHER OR NOT COOKED BEFORE ORDURING\nTHE SMOKING PROCESS; FLOURS,MEALS AND PELLETS, OF FISH FIT FOR HUMANCONSUMPTION\nDRIED FISH, WHETHER OR NOT SALTED BUT NOTSMOKED : OTHER : SPRATS",
+        "hsn_code": "3055930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, DRIED, SALTED OR IN BRINE; SMOKEDFISH, WHETHER OR NOT COOKED BEFORE ORDURING\nTHE SMOKING PROCESS; FLOURS,MEALS AND PELLETS, OF FISH FIT FOR HUMANCONSUMPTION\nDRIED FISH, WHETHER OR NOT SALTED BUT NOTSMOKED : OTHER : OTHER",
+        "hsn_code": "3055990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, DRIED, SALTED OR IN BRINE; SMOKEDFISH, WHETHER OR NOT COOKED BEFORE ORDURING\nTHE SMOKING PROCESS; FLOURS,MEALS AND PELLETS, OF FISH FIT FOR HUMANCONSUMPTION\nFISH, SALTED BUT NOT DRIED OR SMOKED ANDFISH IN BRINE : HERRINGS (CLUPEA HARENGUS,\nCLUPEA PALLASII )",
+        "hsn_code": "3056100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, DRIED, SALTED OR IN BRINE; SMOKEDFISH, WHETHER OR NOT COOKED BEFORE ORDURING\nTHE SMOKING PROCESS; FLOURS,MEALS AND PELLETS, OF FISH FIT FOR HUMANCONSUMPTION\nFISH, SALTED BUT NOT DRIED OR SMOKED ANDFISH IN BRINE : COD (GADUS MORHUA, GADUS\nOGAC, GADUS MACROCEPHALUS)",
+        "hsn_code": "3056200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, DRIED, SALTED OR IN BRINE; SMOKEDFISH, WHETHER OR NOT COOKED BEFORE ORDURING\nTHE SMOKING PROCESS; FLOURS,MEALS AND PELLETS, OF FISH FIT FOR HUMANCONSUMPTION\nFISH, SALTED BUT NOT DRIED OR SMOKED ANDFISH IN BRINE : ANCHOVIES (ENGRAULIS SPP. )",
+        "hsn_code": "3056300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TILAPIAS (OREOCHROMIS SPP) ,CATFISH (PANGALASIUS SPP.,SILURUS SPP.,CLARIAS SPP.,\nICTALURUS SPP.) CARP(CYPRINUS CARPIO, CARASSIUS CARASSIUS, CTENPHARYNGODON\nIDELLUS, HYPOPHTHALMICHTHYS SPP., CIRRHINUS, MYLOPHARYNGODON PICEUS) ,\nEELS(ANGUILLA SPP.),NILE PERCH,(LATES NILOTICUS) AND SNAKEHEADS(CHANNA SPP.)",
+        "hsn_code": "3056400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, DRIED, SALTED OR IN BRINE; SMOKEDFISH, WHETHER OR NOT COOKED BEFORE ORDURING\nTHE SMOKING PROCESS; FLOURS,MEALS AND PELLETS, OF FISH FIT FOR HUMANCONSUMPTION\nOTHER : MUMBAI DUCK",
+        "hsn_code": "3056910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, DRIED, SALTED OR IN BRINE; SMOKEDFISH, WHETHER OR NOT COOKED BEFORE ORDURING\nTHE SMOKING PROCESS; FLOURS,MEALS AND PELLETS, OF FISH FIT FOR HUMANCONSUMPTION\nOTHER : SEER WITHOUT HEAD",
+        "hsn_code": "3056920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, DRIED, SALTED OR IN BRINE; SMOKEDFISH, WHETHER OR NOT COOKED BEFORE ORDURING\nTHE SMOKING PROCESS; FLOURS,MEALS AND PELLETS, OF FISH FIT FOR HUMANCONSUMPTION\nOTHER : SPRATS",
+        "hsn_code": "3056930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, DRIED, SALTED OR IN BRINE; SMOKEDFISH, WHETHER OR NOT COOKED BEFORE ORDURING\nTHE SMOKING PROCESS; FLOURS,MEALS AND PELLETS, OF FISH FIT FOR HUMANCONSUMPTION\nOTHER : OTHER",
+        "hsn_code": "3056990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SHARK FINS",
+        "hsn_code": "3057100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH HEADS,TAILS AND MAWS",
+        "hsn_code": "3057200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3057900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, WHETHER IN SHELL OR NOT,LIVE, FRESH, CHILLED, FROZEN, DRIED, SALTEDOR IN\nBRINE; CRUSTACEANS, IN SHELL, COOKEDBY STEAMING OR BY BOILING IN WATER,WHETHER OR\nNOT CHILLED, FROZEN, DRIED,SALTED OR IN BRINE; FLOURS, MEALS ANDPELLETS, OF\nCRUSTACEANS, FIT FOR HUMANCONSUMPTION FROZEN : ROCK LOBSTER AND OTHER SEA CRAW\nFISH(PALINURUS SPP., PANULIRUS SPP., JASUS SPP.)",
+        "hsn_code": "3061100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, WHETHER IN SHELL OR NOT,LIVE, FRESH, CHILLED, FROZEN, DRIED, SALTEDOR IN\nBRINE; CRUSTACEANS, IN SHELL, COOKEDBY STEAMING OR BY BOILING IN WATER,WHETHER OR\nNOT CHILLED, FROZEN, DRIED,SALTED OR IN BRINE; FLOURS, MEALS ANDPELLETS, OF\nCRUSTACEANS, FIT FOR HUMANCONSUMPTIONFROZEN : LOBSTERS (HOMARUS SPP.) WHOLE,\nCOOKED",
+        "hsn_code": "3061210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, WHETHER IN SHELL OR NOT,LIVE, FRESH, CHILLED, FROZEN, DRIED, SALTEDOR IN\nBRINE; CRUSTACEANS, IN SHELL, COOKEDBY STEAMING OR BY BOILING IN WATER,WHETHER OR\nNOT CHILLED, FROZEN, DRIED,SALTED OR IN BRINE; FLOURS, MEALS ANDPELLETS, OF\nCRUSTACEANS, FIT FOR HUMANCONSUMPTIONFROZEN LOBSTERS (HOMARUS SPP.) OTHER",
+        "hsn_code": "3061290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, WHETHER IN SHELL OR NOT,LIVE, FRESH, CHILLED, FROZEN, DRIED, SALTEDOR IN\nBRINE; CRUSTACEANS, IN SHELL, COOKEDBY STEAMING OR BY BOILING IN WATER,WHETHER OR\nNOT CHILLED, FROZEN, DRIED,SALTED OR IN BRINE; FLOURS, MEALS ANDPELLETS, OF\nCRUSTACEANS, FIT FOR HUMANCONSUMPTIONFROZEN : CRABS",
+        "hsn_code": "3061400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NORWAY LOBSTERS(NEPHOPS NORVEGICUS)",
+        "hsn_code": "3061500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ACCELERATED FREEZE DRIED(AFD)",
+        "hsn_code": "3061610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3061690",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ACCELERATED FREEZE DRIED(AFD)",
+        "hsn_code": "3061711",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3061719",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3061790",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, WHETHER IN SHELL OR NOT,LIVE, FRESH, CHILLED, FROZEN, DRIED, SALTEDOR IN\nBRINE; CRUSTACEANS, IN SHELL, COOKEDBY STEAMING OR BY BOILING IN WATER,WHETHER OR\nNOT CHILLED, FROZEN, DRIED,SALTED OR IN BRINE; FLOURS, MEALS ANDPELLETS, OF\nCRUSTACEANS, FIT FOR HUMANCONSUMPTIONFROZEN : OTHER, INCLUDING FLOURS, MEALS\nAND PELLETS, OF CRUSTACEANS, FIT FOR HUMAN CONSUMPTION",
+        "hsn_code": "3061900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, WHETHER IN SHELL OR NOT,LIVE, FRESH, CHILLED, FROZEN, DRIED, SALTEDOR IN\nBRINE; CRUSTACEANS, IN SHELL, COOKEDBY STEAMING OR BY BOILING IN WATER,WHETHER OR\nNOT CHILLED, FROZEN, DRIED,SALTED OR IN BRINE; FLOURS, MEALS ANDPELLETS, OF\nCRUSTACEANS, FIT FOR HUMANCONSUMPTIONNOT FROZEN : ROCK LOBSTER AND OTHER SEA\nCRAW FISH(PALINURUS SPP., PANULIRUS SPP., JASUS SPP.)",
+        "hsn_code": "3062100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, WHETHER IN SHELL OR NOT,LIVE, FRESH, CHILLED, FROZEN, DRIED, SALTEDOR IN\nBRINE; CRUSTACEANS, IN SHELL, COOKEDBY STEAMING OR BY BOILING IN WATER,WHETHER OR\nNOT CHILLED, FROZEN, DRIED,SALTED OR IN BRINE; FLOURS, MEALS ANDPELLETS, OF\nCRUSTACEANS, FIT FOR HUMANCONSUMPTIONNOT FROZEN : LOBSTERS (HOMARUS SPP.)",
+        "hsn_code": "3062200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, WHETHER IN SHELL OR NOT,LIVE, FRESH, CHILLED, FROZEN, DRIED, SALTEDOR IN\nBRINE; CRUSTACEANS, IN SHELL, COOKEDBY STEAMING OR BY BOILING IN WATER,WHETHER OR\nNOT CHILLED, FROZEN, DRIED,SALTED OR IN BRINE; FLOURS, MEALS ANDPELLETS, OF\nCRUSTACEANS, FIT FOR HUMANCONSUMPTIONNOT FROZEN : CRABS",
+        "hsn_code": "3062400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NORWAY LOBSTERS(NEPHOPS NORVEGICUS)",
+        "hsn_code": "3062500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COLD-WATER SHRIMPS AND PRAWNS (PANDALUS SPP, CRANGON CRANGON)",
+        "hsn_code": "3062600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "POWDERED",
+        "hsn_code": "3062710",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3062790",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, WHETHER IN SHELL OR NOT,LIVE, FRESH, CHILLED, FROZEN, DRIED, SALTEDOR IN\nBRINE; CRUSTACEANS, IN SHELL, COOKEDBY STEAMING OR BY BOILING IN WATER,WHETHER OR\nNOT CHILLED, FROZEN, DRIED,SALTED OR IN BRINE; FLOURS, MEALS ANDPELLETS, OF\nCRUSTACEANS, FIT FOR HUMANCONSUMPTIONNOT FROZEN : OTHER, INCLUDING FLOURS,\nMEALS AND PELLETS, OF CRUSTACEANS, FIT FOR HUMAN CONSUMPTION",
+        "hsn_code": "3062900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIVE, FRESH OR CHILLED",
+        "hsn_code": "3071100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3071900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MOLLUSCS, WHETHER IN SHELL OR NOT, LIVE,FRESH, CHILLED, FROZEN, DRIED, SALTED OR\nINBRINE; AQUATIC INVERTEBRATES OTHER THANCRUSTACEANS AND MOLLUSCS, LIVE,\nFRESH,CHILLED, FROZEN, DRIED, SALTED OR IN BRINE;FLOURS, MEALS AND PELLETS OF\nAQUATICINVERTEBRATES OTHER THAN CRUSTACEANS,FIT FOR HUMAN CONSUMPTION\nSCALLOPS, INCLUDING QUEEN SCALLOPS, OF THEGENERA PECTEN, CHLAMYS OR PLACOPECTEN\nLIVE, FRESH OR CHILLED",
+        "hsn_code": "3072100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MOLLUSCS, WHETHER IN SHELL OR NOT, LIVE,FRESH, CHILLED, FROZEN, DRIED, SALTED OR\nINBRINE; AQUATIC INVERTEBRATES OTHER THAN CRUSTACEANS AND MOLLUSCS, LIVE,\nFRESH,CHILLED, FROZEN, DRIED, SALTED OR IN BRINE;FLOURS, MEALS AND PELLETS OF\nAQUATICINVERTEBRATES OTHER THAN CRUSTACEANS,FIT FOR HUMAN CONSUMPTION\nSCALLOPS, INCLUDING QUEEN SCALLOPS, OF THEGENERA PECTEN, CHLAMYS OR PLACOPECTEN\nOTHER",
+        "hsn_code": "3072900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MOLLUSCS, WHETHER IN SHELL OR NOT, LIVE,FRESH, CHILLED, FROZEN, DRIED, SALTED OR\nINBRINE; AQUATIC INVERTEBRATES OTHER THAN CRUSTACEANS AND MOLLUSCS, LIVE,\nFRESH,CHILLED, FROZEN, DRIED, SALTED OR IN BRINE;FLOURS, MEALS AND PELLETS OF\nAQUATICINVERTEBRATES OTHER THAN CRUSTACEANS,FIT FOR HUMAN CONSUMPTIONMUSSELS\n(MYTILUS SPP., PERNA SPP.) LIVE, FRESH OR CHILLED",
+        "hsn_code": "3073100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MOLLUSCS, WHETHER IN SHELL OR NOT, LIVE,FRESH, CHILLED, FROZEN, DRIED, SALTED OR\nINBRINE; AQUATIC INVERTEBRATES OTHER THAN CRUSTACEANS AND MOLLUSCS, LIVE,\nFRESH,CHILLED, FROZEN, DRIED, SALTED OR IN BRINE;FLOURS, MEALS AND PELLETS OF\nAQUATICINVERTEBRATES OTHER THAN CRUSTACEANS,FIT FOR HUMAN CONSUMPTIONOTHER :\nCLAMS, CLAM MEAT (BIVALVES-VICTORITA, SPP., MERTRIX SPP. AND KATALYSIA SPP.)",
+        "hsn_code": "3073910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MOLLUSCS, WHETHER IN SHELL OR NOT, LIVE,FRESH, CHILLED, FROZEN, DRIED, SALTED OR\nINBRINE; AQUATIC INVERTEBRATES OTHER THAN CRUSTACEANS AND MOLLUSCS, LIVE,\nFRESH,CHILLED, FROZEN, DRIED, SALTED OR IN BRINE;FLOURS, MEALS AND PELLETS OF\nAQUATICINVERTEBRATES OTHER THAN CRUSTACEANS,FIT FOR HUMAN CONSUMPTIONOTHER :\nOTHER",
+        "hsn_code": "3073990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MOLLUSCS, WHETHER IN SHELL OR NOT, LIVE,FRESH, CHILLED, FROZEN, DRIED, SALTED OR\nINBRINE; AQUATIC INVERTEBRATES OTHER THAN CRUSTACEANS AND MOLLUSCS, LIVE,\nFRESH,CHILLED, FROZEN, DRIED, SALTED OR IN BRINE;FLOURS, MEALS AND PELLETS OF\nAQUATICINVERTEBRATES OTHER THAN CRUSTACEANS,FIT FOR HUMAN CONSUMPTIONCUTTLE\nFISH (SEPIA OFFICINALIS, ROSSIAMACROSOMA, SEPTIOLA SPP.) AND SQUID(OMMASTREPHES\nSPP., LOLIGO SPP., NOTOTODARUSSPP., SEPIOTEUTHIS SPP.) : LIVE, FRESH AND CHILLED CUTTLE\nFISH",
+        "hsn_code": "3074110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "INVERTEBRATES OTHER THAN CRUSTACEANS AND MOLLUSCS, LIVE, FRESH,CHILLED, FROZEN,\nDRIED, SALTED OR IN BRINE;FLOURS, MEALS AND PELLETS OF AQUATICINVERTEBRATES OTHER\nTHAN CRUSTACEANS,FIT FOR HUMAN CONSUMPTIONCUTTLE FISH (SEPIA OFFICINALIS,\nROSSIAMACROSOMA, SEPTIOLA SPP.) AND SQUID(OMMASTREPHES SPP., LOLIGO SPP.,\nNOTOTODARUSSPP., SEPIOTEUTHIS SPP.) : LIVE, FRESH AND CHILLED SQUID",
+        "hsn_code": "3074120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "INVERTEBRATES OTHER THAN CRUSTACEANS AND MOLLUSCS, LIVE, FRESH,CHILLED, FROZEN,\nDRIED, SALTED OR IN BRINE;FLOURS, MEALS AND PELLETS OF AQUATICINVERTEBRATES OTHER\nTHAN CRUSTACEANS,FIT FOR HUMAN CONSUMPTIONCUTTLE FISH (SEPIA OFFICINALIS,\nROSSIAMACROSOMA, SEPTIOLA SPP.) AND SQUID(OMMASTREPHES SPP., LOLIGO SPP.,\nNOTOTODARUSSPP., SEPIOTEUTHIS SPP.) : LIVE, FRESH AND CHILLED OTHER : SQUID TUBES,\nFROZEN",
+        "hsn_code": "3074910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "INVERTEBRATES OTHER THAN CRUSTACEANS AND MOLLUSCS, LIVE, FRESH,CHILLED, FROZEN,\nDRIED, SALTED OR IN BRINE;FLOURS, MEALS AND PELLETS OF AQUATICINVERTEBRATES OTHER\nTHAN CRUSTACEANS,FIT FOR HUMAN CONSUMPTIONCUTTLE FISH (SEPIA OFFICINALIS,\nROSSIAMACROSOMA, SEPTIOLA SPP.) AND SQUID(OMMASTREPHES SPP., LOLIGO SPP.,\nNOTOTODARUSSPP., SEPIOTEUTHIS SPP.) : LIVE, FRESH AND CHILLED OTHER : WHOLE SQUIDS,\nFROZEN",
+        "hsn_code": "3074920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "INVERTEBRATES OTHER THAN CRUSTACEANS AND MOLLUSCS, LIVE, FRESH,CHILLED, FROZEN,\nDRIED, SALTED OR IN BRINE;FLOURS, MEALS AND PELLETS OF AQUATICINVERTEBRATES OTHER\nTHAN CRUSTACEANS,FIT FOR HUMAN CONSUMPTIONCUTTLE FISH (SEPIA OFFICINALIS,\nROSSIAMACROSOMA, SEPTIOLA SPP.) AND SQUID(OMMASTREPHES SPP., LOLIGO SPP.,\nNOTOTODARUSSPP., SEPIOTEUTHIS SPP.) : LIVE, FRESH AND CHILLED OTHER : DRIED SQUIDS",
+        "hsn_code": "3074930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "INVERTEBRATES OTHER THAN CRUSTACEANS AND MOLLUSCS, LIVE, FRESH,CHILLED, FROZEN,\nDRIED, SALTED OR IN BRINE;FLOURS, MEALS AND PELLETS OF AQUATICINVERTEBRATES OTHER\nTHAN CRUSTACEANS,FIT FOR HUMAN CONSUMPTIONCUTTLE FISH (SEPIA OFFICINALIS,\nROSSIAMACROSOMA, SEPTIOLA SPP.) AND SQUID(OMMASTREPHES SPP., LOLIGO SPP.,\nNOTOTODARUSSPP., SEPIOTEUTHIS SPP.) : LIVE, FRESH AND CHILLED OTHER : OTHER",
+        "hsn_code": "3074990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CHILLED, FROZEN, DRIED, SALTED OR IN BRINE;FLOURS, MEALS AND PELLETS OF\nAQUATICINVERTEBRATES OTHER THAN CRUSTACEANS,FIT FOR HUMAN CONSUMPTIONOCTOPUS\n(OCTOPUS SPP.) LIVE, FRESH OR CHILLED",
+        "hsn_code": "3075100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CHILLED, FROZEN, DRIED, SALTED OR IN BRINE;FLOURS, MEALS AND PELLETS OF\nAQUATICINVERTEBRATES OTHER THAN CRUSTACEANS,FIT FOR HUMAN CONSUMPTIONOCTOPUS\n(OCTOPUS SPP.) OTHER",
+        "hsn_code": "3075900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CHILLED, FROZEN, DRIED, SALTED OR IN BRINE;FLOURS, MEALS AND PELLETS OF\nAQUATICINVERTEBRATES OTHER THAN CRUSTACEANS,FIT FOR HUMAN CONSUMPTION SNAILS,\nOTHER THAN SEA SNAILS",
+        "hsn_code": "3076000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIVE, FRESH OR CHILLED",
+        "hsn_code": "3077100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3077900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIVE, FRESH OR CHILLED",
+        "hsn_code": "3078100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3078900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CHILLED, FROZEN, DRIED, SALTED OR IN BRINE;FLOURS, MEALS AND PELLETS OF\nAQUATICINVERTEBRATES OTHER THAN CRUSTACEANS,FIT FOR HUMAN CONSUMPTIONOTHER,\nINCLUDING FLOURS, MEALS AND PELLETS,OF AQUATIC INVERTEBRATES OTHER\nTHANCRUSTACEANS, FIT FOR HUMAN CONSUMPTION : LIVE, FRESH OR CHILLED",
+        "hsn_code": "3079100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3079900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CHILLED, FROZEN, DRIED, SALTED OR IN BRINE;FLOURS, MEALS AND PELLETS OF\nAQUATICINVERTEBRATES OTHER THAN CRUSTACEANS,FIT FOR HUMAN CONSUMPTION OTHER :\nSEA SHELL FLESH",
+        "hsn_code": "3079910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CHILLED, FROZEN, DRIED, SALTED OR IN BRINE;FLOURS, MEALS AND PELLETS OF\nAQUATICINVERTEBRATES OTHER THAN CRUSTACEANS,FIT FOR HUMAN CONSUMPTION OTHER :\nJELLY FISH (RHOPELINA SPP.), DRIED SALTED OR FROZEN",
+        "hsn_code": "3079920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CHILLED, FROZEN, DRIED, SALTED OR IN BRINE;FLOURS, MEALS AND PELLETS OF\nAQUATICINVERTEBRATES OTHER THAN CRUSTACEANS,FIT FOR HUMAN CONSUMPTION OTHER :\nOTHER",
+        "hsn_code": "3079990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIVE, FRESH OR CHILLED",
+        "hsn_code": "3081100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3081900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIVE, FRESH OR CHILLED",
+        "hsn_code": "3082100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3082900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIVE, FRESH OR CHILLED",
+        "hsn_code": "3083010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "DRIED, SALTED OR FROZEN",
+        "hsn_code": "3083020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "3089000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLANDS AND OTHER ORGANS FOR ORGANO-THERAPEUTIC USES, DRIED, WHETHER OR NOT\nPOWDERED; EXTRACTS OF GLANDS OR OTHER ORGANS OR OF THEIR SECRETIONS FOR ORGANO\nTHERAPEUTIC USES; HEPARIN AND ITS SALTS; OTHER HUMAN OR ANIMAL SUBSTANCES\nPREPARED FOR THERAPEUTIC OR PROPHYLACTIC USES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED GLANDS AND OTHER ORGANS, DRIED, WHETHER OR NOT POWDERED : PANCREATIN\nAND DRIED POWDER OF PANCREAS",
+        "hsn_code": "30011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLANDS AND OTHER ORGANS FOR ORGANO-THERAPEUTIC USES, DRIED, WHETHER OR NOT\nPOWDERED; EXTRACTS OF GLANDS OR OTHER ORGANS OR OF THEIR SECRETIONS FOR ORGANO\nTHERAPEUTIC USES; HEPARIN AND ITS SALTS; OTHER HUMAN OR ANIMAL SUBSTANCES\nPREPARED FOR THERAPEUTIC OR PROPHYLACTIC USES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED GLANDS AND OTHER ORGANS, DRIED, WHETHER OR NOT POWDERED : OTHER :\nPOWDERED",
+        "hsn_code": "30011091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLANDS AND OTHER ORGANS FOR ORGANO-THERAPEUTIC USES, DRIED, WHETHER OR NOT\nPOWDERED; EXTRACTS OF GLANDS OR OTHER ORGANS OR OF THEIR SECRETIONS FOR ORGANO\nTHERAPEUTIC USES; HEPARIN AND ITS SALTS; OTHER HUMAN OR ANIMAL SUBSTANCES\nPREPARED FOR THERAPEUTIC OR PROPHYLACTIC USES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED GLANDS AND OTHER ORGANS, DRIED, WHETHER OR NOT POWDERED : OTHER :\nOTHER",
+        "hsn_code": "30011099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLANDS AND OTHER ORGANS FOR ORGANO-THERAPEUTIC USES, DRIED, WHETHER OR NOT\nPOWDERED; EXTRACTS OF GLANDS OR OTHER ORGANS OR OF THEIR SECRETIONS FOR ORGANO\nTHERAPEUTIC USES; HEPARIN AND ITS SALTS; OTHER HUMAN OR ANIMAL SUBSTANCES\nPREPARED FOR THERAPEUTIC OR PROPHYLACTIC USES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED - EXTRACTS OF GLANDS OR OTHER ORGANS OR OF THEIR SECRETIONS: LIQUID\nEXTRACTS OF LIVER",
+        "hsn_code": "30012010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLANDS AND OTHER ORGANS FOR ORGANO-THERAPEUTIC USES, DRIED, WHETHER OR NOT\nPOWDERED; EXTRACTS OF GLANDS OR OTHER ORGANS OR OF THEIR SECRETIONS FOR ORGANO\nTHERAPEUTIC USES; HEPARIN AND ITS SALTS; OTHER HUMAN OR ANIMAL SUBSTANCES\nPREPARED FOR THERAPEUTIC OR PROPHYLACTIC USES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED - EXTRACTS OF GLANDS OR OTHER ORGANS OR OF THEIR SECRETIONS: LIVER\nEXTRACTS, DRY",
+        "hsn_code": "30012020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLANDS AND OTHER ORGANS FOR ORGANO-THERAPEUTIC USES, DRIED, WHETHER OR NOT\nPOWDERED; EXTRACTS OF GLANDS OR OTHER ORGANS OR OF THEIR SECRETIONS FOR ORGANO\nTHERAPEUTIC USES; HEPARIN AND ITS SALTS; OTHER HUMAN OR ANIMAL SUBSTANCES\nPREPARED FOR THERAPEUTIC OR PROPHYLACTIC USES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED - EXTRACTS OF GLANDS OR OTHER ORGANS OR OF THEIR SECRETIONS: SNAKE VENOM",
+        "hsn_code": "30012030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLANDS AND OTHER ORGANS FOR ORGANO-THERAPEUTIC USES, DRIED, WHETHER OR NOT\nPOWDERED; EXTRACTS OF GLANDS OR OTHER ORGANS OR OF THEIR SECRETIONS FOR ORGANO\nTHERAPEUTIC USES; HEPARIN AND ITS SALTS; OTHER HUMAN OR ANIMAL SUBSTANCES\nPREPARED FOR THERAPEUTIC OR PROPHYLACTIC USES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED - EXTRACTS OF GLANDS OR OTHER ORGANS OR OF THEIR SECRETIONS: OTHER",
+        "hsn_code": "30012090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLANDS AND OTHER ORGANS FOR ORGANO-THERAPEUTIC USES, DRIED, WHETHER OR NOT\nPOWDERED; EXTRACTS OF GLANDS OR OTHER ORGANS OR OF THEIR SECRETIONS FOR ORGANO\nTHERAPEUTIC USES; HEPARIN AND ITS SALTS; OTHER HUMAN OR ANIMAL SUBSTANCES\nPREPARED FOR THERAPEUTIC OR PROPHYLACTIC USES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED OTHER : OF HUMAN ORIGIN",
+        "hsn_code": "30019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLANDS AND OTHER ORGANS FOR ORGANO-THERAPEUTIC USES, DRIED, WHETHER OR NOT\nPOWDERED; EXTRACTS OF GLANDS OR OTHER ORGANS OR OF THEIR SECRETIONS FOR ORGANO\nTHERAPEUTIC USES; HEPARIN AND ITS SALTS; OTHER HUMAN OR ANIMAL SUBSTANCES\nPREPARED FOR THERAPEUTIC OR PROPHYLACTIC USES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED OTHER : HEPARIN AND ITS SALTS",
+        "hsn_code": "30019091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLANDS AND OTHER ORGANS FOR ORGANO-THERAPEUTIC USES, DRIED, WHETHER OR NOT\nPOWDERED; EXTRACTS OF GLANDS OR OTHER ORGANS OR OF THEIR SECRETIONS FOR ORGANO\nTHERAPEUTIC USES; HEPARIN AND ITS SALTS; OTHER HUMAN OR ANIMAL SUBSTANCES\nPREPARED FOR THERAPEUTIC OR PROPHYLACTIC USES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED OTHER : OTHER : OTHER",
+        "hsn_code": "30019099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GLANDS AND OTHER ORGANS FOR ORGANO-THERAPEUTIC USES, DRIED, WHETHER OR NOT POWDERED; EXTRACTS OF GLANDS OR OTHER ORGANS OR OF THEIR SECRETIONS FOR ORGANO-THERAPEUTIC USES; HEPARIN AND ITS SALTS; OTHER HUMAN OR ANIMAL SUBSTANCES PREPARED FOR THERAPEUTIC OR PROPHYLACTIC USES, NOTELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "3001",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF TWO OR\nMORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR\nPROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL\nSALE CONTAINING PENICILLINS OR DERIVATIVES THEREOF, WITH A PENICILLANIC ACID\nSTRUCTURE, OR STREPTOMYCINS OR THEIR DERIVATIVES",
+        "hsn_code": "30031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF TWO OR\nMORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR\nPROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL\nSALE CONTAINING OTHER ANTIBIOTICS",
+        "hsn_code": "30032000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF TWO OR\nMORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR\nPROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL\nSALE CONTAINING HORMONES OR OTHER PRODUCTS OF HEADING 2937 BUT NOT CONTAINING\nANTIBIOTICS : CONTAINING INSULIN",
+        "hsn_code": "30033100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF TWO OR\nMORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR\nPROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL\nSALE CONTAINING HORMONES OR OTHER PRODUCTS OF HEADING 2937 BUT NOT CONTAINING\nOTHER",
+        "hsn_code": "30033900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF TWO OR\nMORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR\nPROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL\nSALE CONTAINING ALKALOIDS OR DERIVATIVES THEREOF BUT NOT CONTAINING HORMONES OR\nOTHER PRODUCTS OF HEADING 2937 OR ANTIBIOTICS",
+        "hsn_code": "30034000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF TWO OR\nMORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR\nPROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL\nSALE OTHER : AYURVEDIC, UNANI, SIDDHA, HOMOEOPATHIC OR BIO-CHEMIC SYSTEMS\nMEDICAMENTS : OF AYURVEDIC SYSTEM",
+        "hsn_code": "30039011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF TWO OR\nMORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR\nPROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL\nSALE OTHER : AYURVEDIC, UNANI, SIDDHA, HOMOEOPATHIC OR BIO-CHEMIC SYSTEMS\nMEDICAMENTS : OF UNANI SYSTEM",
+        "hsn_code": "30039012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF TWO OR\nMORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR\nPROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL\nSALE OTHER : AYURVEDIC, UNANI, SIDDHA, HOMOEOPATHIC OR BIO-CHEMIC SYSTEMS\nMEDICAMENTS : OF SIDDHA SYSTEM",
+        "hsn_code": "30039013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF TWO OR\nMORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR\nPROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL\nSALE OTHER : AYURVEDIC, UNANI, SIDDHA, HOMOEOPATHIC OR BIO-CHEMIC SYSTEMS\nMEDICAMENTS : OF HOMOEOPATHIC SYSTEM",
+        "hsn_code": "30039014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF TWO OR\nMORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR\nPROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL\nSALE OTHER : AYURVEDIC, UNANI, SIDDHA, HOMOEOPATHIC OR BIO-CHEMIC SYSTEMS\nMEDICAMENTS : OF BIO-CHEMIC SYSTEM",
+        "hsn_code": "30039015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF TWO OR\nMORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR\nPROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL\nSALE OTHER : MENTHOL CRYSTALS AND MILK OF MAGNESIA : MENTHOL CRYSTALS",
+        "hsn_code": "30039021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF TWO OR\nMORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR\nPROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL\nSALEOTHER : MENTHOL CRYSTALS AND MILK OF MAGNESIA : MILK OF MAGNESIA",
+        "hsn_code": "30039022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF TWO OR\nMORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR\nPROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL\nSALE OTHER : BOVINE ALBUMIN AND DRUGS OF ANIMAL ORIGIN, MERBROMINE NATIONAL\nFORMULARY XII (MERCUROCHROME), CALCIUM SENNOSIDE, ANAESTHETIC AGENTS USED IN\nHUMAN OR VETERINARY MEDICINE OR SURGERY, ALUMINIUM HYDROXIDE GEL : BOVINE\nALBUMIN AND DRUGS OF ANIMAL ORIGIN",
+        "hsn_code": "30039031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF TWO OR\nMORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR\nPROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL\nSALE OTHER : BOVINE ALBUMIN AND DRUGS OF ANIMAL ORIGIN, MERBROMINE NATIONAL\nFORMULARY XII (MERCUROCHROME), CALCIUM SENNOSIDE, ANAESTHETIC AGENTS USED IN\nHUMAN OR VETERINARY MEDICINE OR SURGERY, ALUMINIUM HYDROXIDE GEL : MERBROMINE\nNATIONAL FORMULARY XII (MERCUROCHROME)",
+        "hsn_code": "30039032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF TWO OR\nMORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR\nPROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL\nSALE OTHER : BOVINE ALBUMIN AND DRUGS OF ANIMAL ORIGIN, MERBROMINE NATIONAL\nFORMULARY XII (MERCUROCHROME), CALCIUM SENNOSIDE, ANAESTHETIC AGENTS USED IN\nHUMAN OR VETERINARY MEDICINE OR SURGERY, ALUMINIUM HYDROXIDE GEL : CALCIUM\nSENNOSIDE",
+        "hsn_code": "30039033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF TWO OR\nMORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR\nPROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL\nSALE OTHER : BOVINE ALBUMIN AND DRUGS OF ANIMAL ORIGIN, MERBROMINE NATIONAL\nFORMULARY XII (MERCUROCHROME), CALCIUM SENNOSIDE, ANAESTHETIC AGENTS USED IN\nHUMAN OR VETERINARY MEDICINE OR SURGERY, ALUMINIUM HYDROXIDE GEL : ANAESTHETIC\nAGENTS USED IN HUMAN OR VETERINARY MEDICINE OR SURGERY",
+        "hsn_code": "30039034",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF TWO OR\nMORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR\nPROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL\nSALE OTHER : BOVINE ALBUMIN AND DRUGS OF ANIMAL ORIGIN, MERBROMINE NATIONAL\nFORMULARY XII (MERCUROCHROME), CALCIUM SENNOSIDE, ANAESTHETIC AGENTS USED IN\nHUMAN OR VETERINARY MEDICINE OR SURGERY, ALUMINIUM HYDROXIDE GEL : ALUMINIUM\nHYDROXIDE GEL",
+        "hsn_code": "30039035",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF TWO OR\nMORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR\nPROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL\nSALE OTHER : --- BOVINE ALBUMIN AND DRUGS OF ANIMAL ORIGIN, MERBROMINE NATIONAL\nFORMULARY XII (MERCUROCHROME), CALCIUM SENNOSIDE, ANAESTHETIC AGENTS USED IN\nHUMAN OR VETERINARY MEDICINE OR SURGERY, ALUMINIUM HYDROXIDE GEL : ---- KETAMINE",
+        "hsn_code": "30039036",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF TWO OR\nMORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR\nPROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL\nSALE OTHER : OTHER",
+        "hsn_code": "30039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS(EXCLUDING GOODS OF HEADING 3002,3005 OR 3006) CONSISTING OF TWO OR MORE CONSTITUENTS WHICH HAVE BEEN MIXED TOGETHER FOR THERAPEUTIC OR PROPHYLACTIC USES, NOT PUT UP IN MEASURED DOSES OR IN FORMS OR PACKINGS FOR RETAIL SALE",
+        "hsn_code": "3003",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING PENICILLINS OR DERIVATIVES THEREOF,\nWITH A PENICILLANIC ACID STRUCTURE, OR STREPTOMYCINS OR THEIR DERIVATIVES :\nPENICILLINS",
+        "hsn_code": "30041010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING PENICILLINS OR DERIVATIVES THEREOF,\nWITH A PENICILLANIC ACID STRUCTURE, OR STREPTOMYCINS OR THEIR DERIVATIVES AMPICILLIN",
+        "hsn_code": "30041020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING PENICILLINS OR DERIVATIVES THEREOF,\nWITH A PENICILLANIC ACID STRUCTURE, OR STREPTOMYCINS OR THEIR DERIVATIVES\nAMOXYCILLIN",
+        "hsn_code": "30041030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING PENICILLINS OR DERIVATIVES THEREOF,\nWITH A PENICILLANIC ACID STRUCTURE, OR STREPTOMYCINS OR THEIR DERIVATIVES\nBECAMPICILLIN",
+        "hsn_code": "30041040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING PENICILLINS OR DERIVATIVES THEREOF,\nWITH A PENICILLANIC ACID STRUCTURE, OR STREPTOMYCINS OR THEIR DERIVATIVES\nCLOXACILLIN",
+        "hsn_code": "30041050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING PENICILLINS OR DERIVATIVES THEREOF,\nWITH A PENICILLANIC ACID STRUCTURE, OR STREPTOMYCINS OR THEIR DERIVATIVES AMPICILLIN\nAND CLOXACILLIN COMBINATIONS",
+        "hsn_code": "30041060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING PENICILLINS OR DERIVATIVES THEREOF,\nWITH A PENICILLANIC ACID STRUCTURE, OR STREPTOMYCINS OR THEIR DERIVATIVES\nSTREPTOMYCIN",
+        "hsn_code": "30041070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING PENICILLINS OR DERIVATIVES THEREOF,\nWITH A PENICILLANIC ACID STRUCTURE, OR STREPTOMYCINS OR THEIR DERIVATIVES OTHER",
+        "hsn_code": "30041090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS : CEPHALOSPORINS\nAND THEIR DERIVATIVES : CEFAZOLIN",
+        "hsn_code": "30042011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS : CEPHALOSPORINS\nAND THEIR DERIVATIVES :CEPHALEXIN",
+        "hsn_code": "30042012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS : CEPHALOSPORINS\nAND THEIR DERIVATIVES :CIPROFLOXACIN",
+        "hsn_code": "30042013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS : CEPHALOSPORINS\nAND THEIR DERIVATIVES :CEFOXITIN",
+        "hsn_code": "30042014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS : CEPHALOSPORINS\nAND THEIR DERIVATIVES : OTHER",
+        "hsn_code": "30042019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS : SULFONAMIDES AND\nCOTRIMOXAZOLE",
+        "hsn_code": "30042020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS: FLUOROQUINOLONES\n: NORFLOXACIN",
+        "hsn_code": "30042031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS: FLUOROQUINOLONES\n: NALIDIXIC ACID",
+        "hsn_code": "30042032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS: FLUOROQUINOLONES\n: CIPROFLOXACIN",
+        "hsn_code": "30042033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS: FLUOROQUINOLONES\n: OFLOXACIN",
+        "hsn_code": "30042034",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS: FLUOROQUINOLONES\n: OTHER",
+        "hsn_code": "30042039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS TETRACYCLINES :\nCHLORTETRACYCLINE",
+        "hsn_code": "30042041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS TETRACYCLINES :\nOXYTETRACYCLINE",
+        "hsn_code": "30042042",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS TETRACYCLINES :\nOTHER",
+        "hsn_code": "30042049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS CHLORAMPHENICOL",
+        "hsn_code": "30042050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS MACROLIDE :\nERYTHROMYCIN",
+        "hsn_code": "30042061",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS MACROLIDE :\nROXITHROMYCIN",
+        "hsn_code": "30042062",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS MACROLIDE :\nCLARITHROMYCIN",
+        "hsn_code": "30042063",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS MACROLIDE :\nAZITHROMYCIN",
+        "hsn_code": "30042064",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS MACROLIDE : OTHER",
+        "hsn_code": "30042069",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS MACROLIDE :\nCEFADROXIL",
+        "hsn_code": "30042070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS: OTHER : ISONIAZID",
+        "hsn_code": "30042091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS: OTHER : RIFAMPICIN",
+        "hsn_code": "30042092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS: OTHER :\nPYRAZINAMIDE",
+        "hsn_code": "30042093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS: OTHER :\nETHAMBUTOL",
+        "hsn_code": "30042094",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS: OTHER :\nCLINDAMYCIN",
+        "hsn_code": "30042095",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS: OTHER :\nVANCOMYCIN",
+        "hsn_code": "30042096",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS: OTHER : POLYMYXIN B\nAND COLISTIN",
+        "hsn_code": "30042097",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING OTHER ANTIBIOTICS: OTHER : OTHER",
+        "hsn_code": "30042099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING HORMONES OR OTHER PRODUCTS OF\nHEADING 2937 BUT NOT CONTAINING ANTIBIOTICS : CONTAINING INSULIN : INSULIN INJECTION",
+        "hsn_code": "30043110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING HORMONES OR OTHER PRODUCTS OF\nHEADING 2937 BUT NOT CONTAINING ANTIBIOTICS : CONTAINING INSULIN : OTHER",
+        "hsn_code": "30043190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING HORMONES OR OTHER PRODUCTS OF\nHEADING 2937 BUT NOT CONTAINING ANTIBIOTICS : CONTAINING CORTICOSTEROID\nHORMONES, THEIR DERIVATIVES OR STRUCTURAL ANALOGUES",
+        "hsn_code": "30043200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING HORMONES OR OTHER PRODUCTS OF\nHEADING 2937 BUT NOT CONTAINING ANTIBIOTICS : OTHER : PITUITARY HORMONES;\nPREDNISOLONE; DEXAMETHASONE; DANAZOL; OTHER PROGESTOGEN AND OESTOGEN GROUP\nHORMONES: PITUITARY HORMONES",
+        "hsn_code": "30043911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING HORMONES OR OTHER PRODUCTS OF\nHEADING 2937 BUT NOT CONTAINING ANTIBIOTICS : OTHER : PITUITARY HORMONES;\nPREDNISOLONE; DEXAMETHASONE; DANAZOL; OTHER PROGESTOGEN AND OESTOGEN GROUP\nHORMONES: PREDNISOLONE",
+        "hsn_code": "30043912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING HORMONES OR OTHER PRODUCTS OF\nHEADING 2937 BUT NOT CONTAINING ANTIBIOTICS : OTHER : PITUITARY HORMONES;\nPREDNISOLONE; DEXAMETHASONE; DANAZOL; OTHER PROGESTOGEN AND OESTOGEN GROUP\nHORMONES: DEXAMETHASONE",
+        "hsn_code": "30043913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING HORMONES OR OTHER PRODUCTS OF\nHEADING 2937 BUT NOT CONTAINING ANTIBIOTICS : OTHER : PITUITARY HORMONES;\nPREDNISOLONE; DEXAMETHASONE; DANAZOL; OTHER PROGESTOGEN AND OESTOGEN GROUP\nHORMONES: DANAZOL",
+        "hsn_code": "30043914",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING HORMONES OR OTHER PRODUCTS OF\nHEADING 2937 BUT NOT CONTAINING ANTIBIOTICS : OTHER : PITUITARY HORMONES;\nPREDNISOLONE; DEXAMETHASONE; DANAZOL; OTHER PROGESTOGEN AND OESTOGEN GROUP\nHORMONES: OTHER PROGESTOGEN AND OESTOGEN GROUP HORMONES",
+        "hsn_code": "30043919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING HORMONES OR OTHER PRODUCTS OF\nHEADING 2937 BUT NOT CONTAINING ANTIBIOTICS : OTHER : GONADOTROPHINS AND\nLUTEINISING HORMONE : GONADOTROPHINS",
+        "hsn_code": "30043921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING HORMONES OR OTHER PRODUCTS OF\nHEADING 2937 BUT NOT CONTAINING ANTIBIOTICS : OTHER : GONADOTROPHINS AND\nLUTEINISING HORMONE : LUTEINISING HORMONE",
+        "hsn_code": "30043922",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING HORMONES OR OTHER PRODUCTS OF\nHEADING 2937 BUT NOT CONTAINING ANTIBIOTICS : OTHER : OTHER",
+        "hsn_code": "30043990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING ALKALOIDS OR DERIVATIVES THEREOF BUT\nNOT CONTAINING HORMONES, OTHER PRODUCTS OF HEADING 2937 OR ANTIBIOTICS : ATROPIN\nAND SALTS THEREOF",
+        "hsn_code": "30044010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING ALKALOIDS OR DERIVATIVES THEREOF BUT\nNOT CONTAINING HORMONES, OTHERPRODUCTS OF HEADING 2937 OR ANTIBIOTICS CAFFEIN\nAND SALTS THEREOF",
+        "hsn_code": "30044020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "30044030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING ALKALOIDS OR DERIVATIVES THEREOF BUT\nNOT CONTAINING HORMONES, OTHER PRODUCTS OF HEADING 2937 OR ANTIBIOTICS: ERGOT\nPREPERATIONS, ERGOTAMINE AND SALTS THEREOF",
+        "hsn_code": "30044040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING ALKALOIDS OR DERIVATIVES THEREOF BUT\nNOT CONTAINING HORMONES, OTHER PRODUCTS OF HEADING 2937 OR ANTIBIOTICS\nPAPAVERINE HYDROCHLORIDE",
+        "hsn_code": "30044050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING ALKALOIDS OR DERIVATIVES THEREOF BUT\nNOT CONTAINING HORMONES, OTHER PRODUCTS OF HEADING 2937 OR ANTIBIOTICS\nBROMOHEXIN AND SOLBUTAMOL",
+        "hsn_code": "30044060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING ALKALOIDS OR DERIVATIVES THEREOF BUT\nNOT CONTAINING HORMONES, OTHER PRODUCTS OF HEADING 2937 OR ANTIBIOTICS\nTHEOPHYLLINE AND EPHEDRINE",
+        "hsn_code": "30044070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE CONTAINING ALKALOIDS OR DERIVATIVES THEREOF BUT\nNOT CONTAINING HORMONES, OTHER PRODUCTS OF HEADING 2937 OR ANTIBIOTICS OTHER",
+        "hsn_code": "30044090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER MEDICAMENTS CONTAINING VITAMINS OR\nOTHER PRODUCTS OF HEADING 2936 : HEAMATINICS AND ERYTHROPOIETIN PREPARATIONS",
+        "hsn_code": "30045010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER MEDICAMENTS CONTAINING VITAMINS OR\nOTHER PRODUCTS OF HEADING 2936 : PREPARATIONS OF MINERALS AND THEIR SUPPLEMENTS",
+        "hsn_code": "30045020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER MEDICAMENTS CONTAINING VITAMINS OR\nOTHER PRODUCTS OF HEADING 2936 : PREPARATIONS OF VITAMINS : OF VITAMIN A",
+        "hsn_code": "30045031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER MEDICAMENTS CONTAINING VITAMINS OR\nOTHER PRODUCTS OF HEADING 2936 : PREPARATIONS OF VITAMINS : OF VITAMIN B1 AND B2\nAND SALTS THEREOF",
+        "hsn_code": "30045032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER MEDICAMENTS CONTAINING VITAMINS OR\nOTHER PRODUCTS OF HEADING 2936 : PREPARATIONS OF VITAMINS : OF VITAMIN B9",
+        "hsn_code": "30045033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER MEDICAMENTS CONTAINING VITAMINS OR\nOTHER PRODUCTS OF HEADING 2936 : PREPARATIONS OF VITAMINS : OF VITAMIN B12",
+        "hsn_code": "30045034",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER MEDICAMENTS CONTAINING VITAMINS OR\nOTHER PRODUCTS OF HEADING 2936 : PREPARATIONS OF VITAMINS : OF VITAMIN C",
+        "hsn_code": "30045035",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER MEDICAMENTS CONTAINING VITAMINS OR\nOTHER PRODUCTS OF HEADING 2936 : PREPARATIONS OF VITAMINS : OF VITAMIN D",
+        "hsn_code": "30045036",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER MEDICAMENTS CONTAINING VITAMINS OR\nOTHER PRODUCTS OF HEADING 2936 : PREPARATIONS OF VITAMINS : OF VITAMIN E",
+        "hsn_code": "30045037",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER MEDICAMENTS CONTAINING VITAMINS OR\nOTHER PRODUCTS OF HEADING 2936 : PREPARATIONS OF VITAMINS : OTHER",
+        "hsn_code": "30045039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER MEDICAMENTS CONTAINING VITAMINS OR\nOTHER PRODUCTS OF HEADING 2936 : OTHER",
+        "hsn_code": "30045090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : AYURVEDIC, UNANI, HOMOEOPATHIC, SIDDHA\nOR BIO-CHEMIC SYSTEMS MEDICAMENTS, PUT UP FOR RETAIL SALE : OF AYURVEDIC SYSTEM",
+        "hsn_code": "30049011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : AYURVEDIC, UNANI, HOMOEOPATHIC, SIDDHA\nOR BIO-CHEMIC SYSTEMS MEDICAMENTS, PUT UP FOR RETAIL SALE : OF UNANI SYSTEM",
+        "hsn_code": "30049012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : AYURVEDIC, UNANI, HOMOEOPATHIC, SIDDHA\nOR BIO-CHEMIC SYSTEMS MEDICAMENTS, PUT UP FOR RETAIL SALE : OF SIDDHA SYSTEM",
+        "hsn_code": "30049013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : AYURVEDIC, UNANI, HOMOEOPATHIC, SIDDHA\nOR BIO-CHEMIC SYSTEMS MEDICAMENTS, PUT UP FOR RETAIL SALE : OF HOMOEOPATHIC\nSYSTEM",
+        "hsn_code": "30049014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : AYURVEDIC, UNANI, HOMOEOPATHIC, SIDDHA\nOR BIO-CHEMIC SYSTEMS MEDICAMENTS, PUT UP FOR RETAIL SALE : OF BIO-CHEMIC SYSTEM",
+        "hsn_code": "30049015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTHELMINTICS DRUGS; ANTIAMOEBIC AND\nOTHER ANTIPROTOZAL DRUGS; ANTIFUNGAL DRUGS : ANTHELMINTICS AND PREPARATIONS\nTHEREOF",
+        "hsn_code": "30049021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTHELMINTICS DRUGS; ANTIAMOEBIC AND\nOTHER ANTIPROTOZAL DRUGS; ANTIFUNGAL DRUGS : METRONIDAZOLE",
+        "hsn_code": "30049022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTHELMINTICS DRUGS; ANTIAMOEBIC AND\nOTHER ANTIPROTOZAL DRUGS; ANTIFUNGAL DRUGS : TINIDAZOLE",
+        "hsn_code": "30049023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTHELMINTICS DRUGS; ANTIAMOEBIC AND\nOTHER ANTIPROTOZAL DRUGS; ANTIFUNGAL DRUGS : SECNIDAZOLE",
+        "hsn_code": "30049024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTHELMINTICS DRUGS; ANTIAMOEBIC AND\nOTHER ANTIPROTOZAL DRUGS; ANTIFUNGAL DRUGS : DILUXAMIDE FUROATE",
+        "hsn_code": "30049025",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTHELMINTICS DRUGS; ANTIAMOEBIC AND\nOTHER ANTIPROTOZAL DRUGS; ANTIFUNGAL DRUGS : SODIUM STIBOGLUCONATE",
+        "hsn_code": "30049026",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTHELMINTICS DRUGS; ANTIAMOEBIC AND\nOTHER ANTIPROTOZAL DRUGS; ANTIFUNGAL DRUGS : PENTAMIDINE",
+        "hsn_code": "30049027",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTHELMINTICS DRUGS; ANTIAMOEBIC AND\nOTHER ANTIPROTOZAL DRUGS; ANTIFUNGAL DRUGS : OTHER",
+        "hsn_code": "30049029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIHISTAMINICS DRUGS; ANTACIDS\nPREPARATIONS; ANTIULCER DRUGS; ANTIEMITICS AND OTHER GASTROINTESTINAL DRUGS :\nPROMETHAZINE, CHLORPHENIRAMINE, ASTEMIZOLE AND CETEIRIZINE",
+        "hsn_code": "30049031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIHISTAMINICS DRUGS; ANTACIDS\nPREPARATIONS; ANTIULCER DRUGS; ANTIEMITICS AND OTHER GASTROINTESTINAL DRUGS :\nSODIUM BICARBONATE, MAGNESIUM HYDROXIDE (MILK OF MAGNESIA), MAGNESIUM\nCARBONATE, MAGNESIUM TRISILICATE, ALUMINIUM HYDROXIDE GEL, MAGALDARATE AND\nCOMBINATIONS THEREOF",
+        "hsn_code": "30049032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIHISTAMINICS DRUGS; ANTACIDS\nPREPARATIONS; ANTIULCER DRUGS; ANTIEMITICS AND OTHER GASTROINTESTINAL DRUGS\nCIMETIDINE, RANTIDINE, NIZATIDINE AND ROXATIDINE",
+        "hsn_code": "30049033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIHISTAMINICS DRUGS; ANTACIDS\nPREPARATIONS; ANTIULCER DRUGS; ANTIEMITICS AND OTHER GASTROINTESTINAL DRUGS\nOMEPRAZOLE AND LANSOPRAZOLE",
+        "hsn_code": "30049034",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIHISTAMINICS DRUGS; ANTACIDS\nPREPARATIONS; ANTIULCER DRUGS; ANTIEMITICS AND OTHER GASTROINTESTINAL DRUGS\nDICYCLOMINE, METOCLOPRAMIDE AND DEXAMETHASONE AND ONDANSETRON",
+        "hsn_code": "30049035",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE ANTIHISTAMINICS DRUGS; ANTACIDS PREPARATIONS;\nANTIULCER DRUGS; ANTIEMITICS AND OTHER GASTROINTESTINAL DRUGS CHENODIOL AND\nURSODIOL",
+        "hsn_code": "30049036",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIHISTAMINICS DRUGS; ANTACIDS\nPREPARATIONS; ANTIULCER DRUGS; ANTIEMITICS AND OTHER GASTROINTESTINAL DRUGS\nOTHER",
+        "hsn_code": "30049039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTICANCER DRUGS : CYCLOPHOSPHAMIDE",
+        "hsn_code": "30049041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTICANCER DRUGS : METHOTREXATE, 5-\nFLUOROURACIL(5-FU) AND FTORAFUR",
+        "hsn_code": "30049042",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTICANCER DRUGS : BINCRISTINE AND\nVINBLASTINE",
+        "hsn_code": "30049043",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTICANCER DRUGS : PACLITAXEL AND\nDOCETAXEL",
+        "hsn_code": "30049044",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTICANCER DRUGS : ETOPOSIDE",
+        "hsn_code": "30049045",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTICANCER DRUGS : ACTINOMYCIN D\nDACTINOMYCIN AND DOXORUBICIN",
+        "hsn_code": "30049046",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTICANCER DRUGS : L-ASPARAGINASE,\nCISPLATIN AND CARBOPLATIN",
+        "hsn_code": "30049047",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTICANCER DRUGS : TAMOXIFEN",
+        "hsn_code": "30049048",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTICANCER DRUGS : OTHER",
+        "hsn_code": "30049049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTITUBERCULAR DRUGS; ANTILEPROTIC\nDRUGS; ANTIMALARIAL DRUGS : ISONIAZID",
+        "hsn_code": "30049051",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTITUBERCULAR DRUGS; ANTILEPROTIC\nDRUGS; ANTIMALARIAL DRUGS :RIFAMPICIN",
+        "hsn_code": "30049052",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTITUBERCULAR DRUGS; ANTILEPROTIC\nDRUGS; ANTIMALARIAL DRUGS :PYRAZINAMIDE AND ETHAMBUTOL",
+        "hsn_code": "30049053",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTITUBERCULAR DRUGS; ANTILEPROTIC\nDRUGS; ANTIMALARIAL DRUGS :STREPTOMYCIN",
+        "hsn_code": "30049054",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTITUBERCULAR DRUGS; ANTILEPROTIC\nDRUGS; ANTIMALARIAL DRUGS : DAPSONE (DDS),ACEDAPSONE (DADDS), SOLOPSONE AND\nCLOFAZIMINE",
+        "hsn_code": "30049055",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTITUBERCULAR DRUGS; ANTILEPROTIC\nDRUGS; ANTIMALARIAL DRUGS :CHLOROQUINE, AMODIAQUINE, MEFLOQUINE, QUININE,\nCHLOROGUAMIDE, PYRIMETHAMINE",
+        "hsn_code": "30049056",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTITUBERCULAR DRUGS; ANTILEPROTIC\nDRUGS; ANTIMALARIAL DRUGS : OTHER ANTITUBERCULAR DRUGS",
+        "hsn_code": "30049057",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTITUBERCULAR DRUGS; ANTILEPROTIC\nDRUGS; ANTIMALARIAL DRUGS : OTHER ANTILEPROTIC DRUGS",
+        "hsn_code": "30049058",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTITUBERCULAR DRUGS; ANTILEPROTIC\nDRUGS; ANTIMALARIAL DRUGS : OTHER ANTIMALARIAL DRUGS",
+        "hsn_code": "30049059",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : NONSTEROIDAL ANTIINFLAMMATORY,\nANALGESICS AND ANTIPYRATIC DRUGS : ANALGIN WITH OR WITHOUT OTHER COMPOUNDS\nSUCH AS PARACETAMOL",
+        "hsn_code": "30049061",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : NONSTEROIDAL ANTIINFLAMMATORY,\nANALGESICS AND ANTIPYRATIC DRUGS : ACETYL SALICYLIC ACID (ASPIRIN) AND FORMULATIONS\nTHEREOF",
+        "hsn_code": "30049062",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : NONSTEROIDAL ANTIINFLAMMATORY,\nANALGESICS AND ANTIPYRATIC DRUGS : IBUPROFEN WITH OR WITHOUT PARACETAMOL OR\nOTHER COMPOUNDS",
+        "hsn_code": "30049063",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : NONSTEROIDAL ANTIINFLAMMATORY,\nANALGESICS AND ANTIPYRATIC DRUGS : OXYPHEN BUTAZONE, PHENYL BUTAZONE AND\nFORMULATIONS THEREOF",
+        "hsn_code": "30049064",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : NONSTEROIDAL ANTIINFLAMMATORY,\nANALGESICS AND ANTIPYRATIC DRUGS : INDOMETHACIN",
+        "hsn_code": "30049065",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : NONSTEROIDAL ANTIINFLAMMATORY,\nANALGESICS AND ANTIPYRATIC DRUGS : MEPHENAMIC ACID, DACTOFENAC SODIUM,\nPIROXICAM, TENOXICAM AND MELOXICAM",
+        "hsn_code": "30049066",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : NONSTEROIDAL ANTIINFLAMMATORY,\nANALGESICS AND ANTIPYRATIC DRUGS : KETOROLAC, NIMESULIDE, NABUMETONE AND\nNEFOPAM",
+        "hsn_code": "30049067",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : NONSTEROIDAL ANTIINFLAMMATORY,\nANALGESICS AND ANTIPYRATIC DRUGS : OTHER",
+        "hsn_code": "30049069",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIHYPERTENSIVE DRUGS :CAPTOPRIL,\nENALAPRIL, LISINOPRIL, PERINDOPRIL AND RAMIPRIL",
+        "hsn_code": "30049071",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIHYPERTENSIVE DRUGS : VERAPAMIL,\nNIFEDIPINE, AMLODIPINE AND LACIDIPINE",
+        "hsn_code": "30049072",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIHYPERTENSIVE DRUGS : LOSARTAN",
+        "hsn_code": "30049073",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIHYPERTENSIVE DRUGS :PROPRANOLOL,\nMETOPROLOL, ATENOLOL AND LABETALOL",
+        "hsn_code": "30049074",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIHYPERTENSIVE DRUGS :PRAZOSIN,\nTERAZOSIN, PHENTOLAMINE AND PHENOXYBENZAMINE",
+        "hsn_code": "30049075",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIHYPERTENSIVE DRUGS :CLONIDINE,\nMETHYLDOPA",
+        "hsn_code": "30049076",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIHYPERTENSIVE DRUGS :HYDRALAZINE,\nMINOXIDIL AND DIAZOXIDE",
+        "hsn_code": "30049077",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIHYPERTENSIVE DRUGS : OTHER",
+        "hsn_code": "30049079",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIEPILIPTIC DRUGS; SULFA DRUGS NOT\nELSEWHERE SPECIFIED OR INCLUDED, PREPARATIONS OF ENZYMES; VETERINARY MEDICINAL\nPREPARATIONS, NOT FOR HUMAN USE,NOT ELSEWHERE SPECIFIED OR INCLUDED; ORAL\nREHYDRATION SALTS; ANTIBACTERIALFORMULATIONS NOT ELSEWHERE SPECIFIED OR\nINCLUDED, SEDATIVES AND TRANQUILIZERS : PHENOBARBITONE, MEPHOBARBITONE,\nPRIMIDONE, PHENYTOIN, CARBAMAZEPINE, ETHOSUCCIMIDE, VALPORIC ACID ( SODIUM\nVALPORATE), DIAZEPAM, LAMOTRIGINE, GABAPENTIN, BIGABATRIN, PHENACEMIDE,\nTRIMETHADIONE AND ACETAZOLAMIDE",
+        "hsn_code": "30049081",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIEPILIPTIC DRUGS; SULFA DRUGS NOT\nELSEWHERE SPECIFIED OR INCLUDED, PREPARATIONS OF ENZYMES; VETERINARY MEDICINAL\nPREPARATIONS, NOT FOR HUMAN USE,NOT ELSEWHERE SPECIFIED OR INCLUDED; ORAL\nREHYDRATION SALTS; ANTIBACTERIALFORMULATIONS NOT ELSEWHERE SPECIFIED OR\nINCLUDED, SEDATIVES AND TRANQUILIZERS : OTHER ANTIEPILEPTIC DRUGS",
+        "hsn_code": "30049082",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIEPILIPTIC DRUGS; SULFA DRUGS NOT\nELSEWHERE SPECIFIED OR INCLUDED, PREPARATIONS OF ENZYMES; VETERINARY MEDICINAL\nPREPARATIONS, NOT FOR HUMAN USE,NOT ELSEWHERE SPECIFIED OR INCLUDED; ORAL\nREHYDRATION SALTS; ANTIBACTERIALFORMULATIONS NOT ELSEWHERE SPECIFIED OR\nINCLUDED, SEDATIVES AND TRANQUILIZERS : SULPHA DRUGS NOT ELSEWHERE SPECIFIED OR\nINCLUDED",
+        "hsn_code": "30049083",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIEPILIPTIC DRUGS; SULFA DRUGS NOT\nELSEWHERE SPECIFIED OR INCLUDED, PREPARATIONS OF ENZYMES; VETERINARY MEDICINAL\nPREPARATIONS, NOT FOR HUMAN USE,NOT ELSEWHERE SPECIFIED OR INCLUDED; ORAL\nREHYDRATION SALTS; ANTIBACTERIALFORMULATIONS NOT ELSEWHERE SPECIFIED OR\nINCLUDED, SEDATIVES AND TRANQUILIZERS : PREPARATIONS OF ENZYMES",
+        "hsn_code": "30049084",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIEPILIPTIC DRUGS; SULFA DRUGS NOT\nELSEWHERE SPECIFIED OR INCLUDED, PREPARATIONS OF ENZYMES; VETERINARY MEDICINAL\nPREPARATIONS, NOT FOR HUMAN USE,NOT ELSEWHERE SPECIFIED OR INCLUDED; ORAL\nREHYDRATION SALTS; ANTIBACTERIALFORMULATIONS NOT ELSEWHERE SPECIFIED OR\nINCLUDED, SEDATIVES AND TRANQUILIZERS : VETERINARY MEDICINAL PREPARATIONS, NOT FOR\nHUMAN USE, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "30049085",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIEPILIPTIC DRUGS; SULFA DRUGS NOT\nELSEWHERE SPECIFIED OR INCLUDED, PREPARATIONS OF ENZYMES; VETERINARY MEDICINAL\nPREPARATIONS, NOT FOR HUMAN USE,NOT ELSEWHERE SPECIFIED OR INCLUDED; ORAL\nREHYDRATION SALTS; ANTIBACTERIALFORMULATIONS NOT ELSEWHERE SPECIFIED OR\nINCLUDED, SEDATIVES AND TRANQUILIZERS : ORAL REHYDRATION SALTS",
+        "hsn_code": "30049086",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIEPILIPTIC DRUGS; SULFA DRUGS NOT\nELSEWHERE SPECIFIED OR INCLUDED, PREPARATIONS OF ENZYMES; VETERINARY MEDICINAL\nPREPARATIONS, NOT FOR HUMAN USE,NOT ELSEWHERE SPECIFIED OR INCLUDED; ORAL\nREHYDRATION SALTS; ANTIBACTERIALFORMULATIONS NOT ELSEWHERE SPECIFIED OR\nINCLUDED, SEDATIVES AND TRANQUILIZERS : ANTIBACTERIAL FORMULATIONS, NOT ELSEWHERE\nSPECIFIED OR INCLUDED",
+        "hsn_code": "30049087",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIEPILIPTIC DRUGS; SULFA DRUGS NOT\nELSEWHERE SPECIFIED OR INCLUDED, PREPARATIONS OF ENZYMES; VETERINARY MEDICINAL\nPREPARATIONS, NOT FOR HUMAN USE,NOT ELSEWHERE SPECIFIED OR INCLUDED; ORAL\nREHYDRATION SALTS; ANTIBACTERIALFORMULATIONS NOT ELSEWHERE SPECIFIED OR\nINCLUDED, SEDATIVES AND TRANQUILIZERS : SEDATIVES",
+        "hsn_code": "30049088",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : ANTIEPILIPTIC DRUGS; SULFA DRUGS NOT\nELSEWHERE SPECIFIED OR INCLUDED, PREPARATIONS OF ENZYMES; VETERINARY MEDICINAL\nPREPARATIONS, NOT FOR HUMAN USE,NOT ELSEWHERE SPECIFIED OR INCLUDED; ORAL\nREHYDRATION SALTS; ANTIBACTERIALFORMULATIONS NOT ELSEWHERE SPECIFIED OR\nINCLUDED, SEDATIVES AND TRANQUILIZERS : TRANQUILIZERS",
+        "hsn_code": "30049089",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : OTHER : SALBUTAMOL, TERBUTALINE,\nEPHEDRINE, SALMETEROL AND METHYL XANTHIMES",
+        "hsn_code": "30049091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : OTHER : PLASMA EXPANDERS",
+        "hsn_code": "30049092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : OTHER : CHLOROPHENIRAMINE MALEATE,WITH\nOR WITHOUT OTHER COMPOUNDS (EXCLUDING STERIODS AND ALKALOIDS)",
+        "hsn_code": "30049093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : OTHER : THEOPHYLLINE, AMINOPHYLLINE AND\nOTHER BRONCHO DILATORS",
+        "hsn_code": "30049094",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : OTHER : CARCINO-CHEMOTHERAPEUTIC DRUGS\nNOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "30049095",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : OTHER : --- KETAMINE",
+        "hsn_code": "30049096",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS (EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED\nOR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED\nDOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN\nFORMS OR PACKINGS FOR RETAIL SALE OTHER : OTHER : OTHER",
+        "hsn_code": "30049099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEDICAMENTS(EXCLUDING GOODS OF HEADING 3002, 3005 OR 3006) CONSISTING OF MIXED OR UNMIXED PRODUCTS FOR THERAPEUTIC OR PROPHYLACTIC USES, PUT UP IN MEASURED DOSES (INCLUDING THOSE IN THE FORM OF TRANSDERMAL ADMINISTRATION SYSTEMS) OR IN FORMS OR PACKINGS FOR RETAIL SALE",
+        "hsn_code": "3004",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WADDING, GAUZE, BANDAGES AND SIMILAR ARTICLES (FOR EXAMPLE, DRESSINGS, ADHESIVE\nPLASTERS, POULTICES), IMPREGNATED OR COATED WITH PHARMACEUTICAL SUBSTANCES OR\nPUT UP IN FORMS OR PACKINGS FOR RETAIL SALE FOR MEDICAL, SURGICAL,DENTAL OR\nVETERINARY PURPOSES ADHESIVE DRESSINGS AND OTHER ARTICLES HAVING AN ADHESIVE\nLAYER : ADHESIVE GUAZE",
+        "hsn_code": "30051010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WADDING, GAUZE, BANDAGES AND SIMILAR ARTICLES (FOR EXAMPLE, DRESSINGS, ADHESIVE\nPLASTERS, POULTICES), IMPREGNATED OR COATED WITH PHARMACEUTICAL SUBSTANCES OR\nPUT UP IN FORMS OR PACKINGS FOR RETAIL SALE FOR MEDICAL, SURGICAL,DENTAL OR\nVETERINARY PURPOSES ADHESIVE DRESSINGS AND OTHER ARTICLES HAVING AN ADHESIVE\nLAYER : ADHESIVE TAPE",
+        "hsn_code": "30051020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WADDING, GAUZE, BANDAGES AND SIMILAR ARTICLES (FOR EXAMPLE, DRESSINGS, ADHESIVE\nPLASTERS, POULTICES), IMPREGNATED OR COATED WITH PHARMACEUTICAL SUBSTANCES OR\nPUT UP IN FORMS OR PACKINGS FOR RETAIL SALE FOR MEDICAL, SURGICAL,DENTAL OR\nVETERINARY PURPOSES ADHESIVE DRESSINGS AND OTHER ARTICLES HAVING AN ADHESIVE\nLAYER : OTHER",
+        "hsn_code": "30051090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WADDING, GAUZE, BANDAGES AND SIMILAR ARTICLES (FOR EXAMPLE, DRESSINGS, ADHESIVE\nPLASTERS, POULTICES), IMPREGNATED OR COATED WITH PHARMACEUTICAL SUBSTANCES OR\nPUT UP IN FORMS OR PACKINGS FOR RETAIL SALE FOR MEDICAL, SURGICAL,DENTAL OR\nVETERINARY PURPOSES OTHER : COTTON WOOL, MEDICATED",
+        "hsn_code": "30059010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WADDING, GAUZE, BANDAGES AND SIMILAR ARTICLES (FOR EXAMPLE, DRESSINGS, ADHESIVE\nPLASTERS, POULTICES), IMPREGNATED OR COATED WITH PHARMACEUTICAL SUBSTANCES OR\nPUT UP IN FORMS OR PACKINGS FOR RETAIL SALE FOR MEDICAL, SURGICAL,DENTAL OR\nVETERINARY PURPOSES OTHER : POULTICE OF KAOLIN",
+        "hsn_code": "30059020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WADDING, GAUZE, BANDAGES AND SIMILAR ARTICLES (FOR EXAMPLE, DRESSINGS, ADHESIVE\nPLASTERS, POULTICES), IMPREGNATED OR COATED WITH PHARMACEUTICAL SUBSTANCES OR\nPUT UP IN FORMS OR PACKINGS FOR RETAIL SALE FOR MEDICAL, SURGICAL,DENTAL OR\nVETERINARY PURPOSES OTHER : LINT, MEDICATED",
+        "hsn_code": "30059030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WADDING, GAUZE, BANDAGES AND SIMILAR ARTICLES (FOR EXAMPLE, DRESSINGS, ADHESIVE\nPLASTERS, POULTICES), IMPREGNATED OR COATED WITH PHARMACEUTICAL SUBSTANCES OR\nPUT UP IN FORMS OR PACKINGS FOR RETAIL SALE FOR MEDICAL, SURGICAL,DENTAL OR\nVETERINARY PURPOSES OTHER : BANDAGES",
+        "hsn_code": "30059040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WADDING, GAUZE, BANDAGES AND SIMILAR ARTICLES (FOR EXAMPLE, DRESSINGS, ADHESIVE\nPLASTERS, POULTICES), IMPREGNATED OR COATED WITH PHARMACEUTICAL SUBSTANCES OR\nPUT UP IN FORMS OR PACKINGS FOR RETAIL SALE FOR MEDICAL, SURGICAL,DENTAL OR\nVETERINARY PURPOSES OTHER : BURN THERAPY DRESSING SOAKED IN PROTECTIVE GEL",
+        "hsn_code": "30059050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WADDING, GAUZE, BANDAGES AND SIMILAR ARTICLES (FOR EXAMPLE, DRESSINGS, ADHESIVE\nPLASTERS, POULTICES), IMPREGNATED OR COATED WITH PHARMACEUTICAL SUBSTANCES OR\nPUT UP IN FORMS OR PACKINGS FOR RETAIL SALE FOR MEDICAL, SURGICAL,DENTAL OR\nVETERINARY PURPOSES OTHER : MICRO PORES SURGICAL TAPES",
+        "hsn_code": "30059060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WADDING, GAUZE, BANDAGES AND SIMILAR ARTICLES (FOR EXAMPLE, DRESSINGS, ADHESIVE\nPLASTERS, POULTICES), IMPREGNATED OR COATED WITH PHARMACEUTICAL SUBSTANCES OR\nPUT UP IN FORMS OR PACKINGS FOR RETAIL SALE FOR MEDICAL, SURGICAL,DENTAL OR\nVETERINARY PURPOSES OTHER : CORN REMOVERS AND CALLOUS REMOVERS",
+        "hsn_code": "30059070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WADDING, GAUZE, BANDAGES AND SIMILAR ARTICLES (FOR EXAMPLE, DRESSINGS, ADHESIVE\nPLASTERS, POULTICES), IMPREGNATED OR COATED WITH PHARMACEUTICAL SUBSTANCES OR\nPUT UP IN FORMS OR PACKINGS FOR RETAIL SALE FOR MEDICAL, SURGICAL,DENTAL OR\nVETERINARY PURPOSES OTHER : OTHER",
+        "hsn_code": "30059090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WADDING, GAUZE, BANDAGES AND SIMILAR ARTICLES (FOR EXAMPLE, DRESSINGS, ADHESIVE PLASTERS, POULTICES), IMPREGNATED OR COATED WITH PHARMACEUTICAL SUBSTANCES OR PUT UP IN FORMS OR PACKINGS FOR RETAIL SALE FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY PURPOSES",
+        "hsn_code": "3005",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES : ANTISERA : FOR\nDIPTHERIA",
+        "hsn_code": "30021011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES : ANTISERA : FOR\nTETANUS",
+        "hsn_code": "30021012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES : ANTISERA : FOR\nRABIES",
+        "hsn_code": "30021013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES : ANTISERA : FOR\nSNAKE VENOM",
+        "hsn_code": "30021014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES : ANTISERA :\nOTHER",
+        "hsn_code": "30021019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES : HEMOGLOBIN\nBLOOD GLOBULINS AND SERUM GLOBULINS",
+        "hsn_code": "30021020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES : OTHER : OF\nHUMAN ORIGIN",
+        "hsn_code": "30021091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES : OTHER : OTHER",
+        "hsn_code": "30021099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nVACCINES FOR HUMAN MEDICINE : SINGLE VACCINES : FOR CHOLERA AND TYPHOID",
+        "hsn_code": "30022011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nVACCINES FOR HUMAN MEDICINE : SINGLE VACCINES : FOR HEPATITIS",
+        "hsn_code": "30022012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nVACCINES FOR HUMAN MEDICINE : SINGLE VACCINES : FOR TETANUS",
+        "hsn_code": "30022013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nVACCINES FOR HUMAN MEDICINE : SINGLE VACCINES : FOR POLIO",
+        "hsn_code": "30022014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nVACCINES FOR HUMAN MEDICINE : SINGLE VACCINES : FOR TUBERCULOSIS",
+        "hsn_code": "30022015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nVACCINES FOR HUMAN MEDICINE : SINGLE VACCINES : FOR RABIES",
+        "hsn_code": "30022016",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nVACCINES FOR HUMAN MEDICINE : SINGLE VACCINES : FOR JAPANESE ENCEPHALITIS",
+        "hsn_code": "30022017",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nVACCINES FOR HUMAN MEDICINE : SINGLE VACCINES : FOR WHOPPING COUGH (PERTUSIS)",
+        "hsn_code": "30022018",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nVACCINES FOR HUMAN MEDICINE : SINGLE VACCINES : OTHER",
+        "hsn_code": "30022019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nVACCINES FOR HUMAN MEDICINE MIXED VACCINES : FOR DIPHTHERIA, PERTUSIS AND TETANUS\n(DPT)",
+        "hsn_code": "30022021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nVACCINES FOR HUMAN MEDICINE MIXED VACCINES : FOR DIPHTHERIA AND TETANUS (DT)",
+        "hsn_code": "30022022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nVACCINES FOR HUMAN MEDICINE MIXED VACCINES : FOR MEASLES, MUMPS AND RUBELLA\n(MMR)",
+        "hsn_code": "30022023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nVACCINES FOR HUMAN MEDICINE MIXED VACCINES : FOR TYPHOID-PARATYPHOID (TAB) OR\nTYPHOID- PARATYPHOID-CHOLERA (TABC)",
+        "hsn_code": "30022024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nVACCINES FOR HUMAN MEDICINE MIXED VACCINES : OTHER",
+        "hsn_code": "30022029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nVACCINES FOR VETERINARY MEDICINE",
+        "hsn_code": "30023000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nOTHER : HUMAN BLOOD",
+        "hsn_code": "30029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nOTHER : ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC USES",
+        "hsn_code": "30029020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nOTHER : CULTURES OF MICRO-ORGANISMS (EXCLUDING YEAST)",
+        "hsn_code": "30029030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nOTHER : TOXINS",
+        "hsn_code": "30029040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC\nUSES; ANTISERA AND OTHER BLOOD FRACTIONS AND MODIFIED IMMUNOLOGICAL PRODUCTS,\nWHETHER OR NOT OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES,\nTOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS\nOTHER : OTHER",
+        "hsn_code": "30029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HUMAN BLOOD; ANIMAL BLOOD PREPARED FOR THERAPEUTIC, PROPHYLACTIC OR DIAGNOSTIC USES; ANTISERA AND OTHER BLOOD FRACTIONS AND IMMUNOLOGICAL PRODUCTS, WHETHER OR NOT MODIFIED OR OBTAINED BY MEANS OF BIOTECHNOLOGICAL PROCESSES; VACCINES, TOXINS, CULTURES OF MICRO-ORGANISMS (EXCLUDING YEASTS) AND SIMILAR PRODUCTS",
+        "hsn_code": "3002",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PHARMACEUTICAL GOODS SPECIFIED IN NOTE 4 TO THIS CHAPTER STERILE SURGICAL CATGUT,\nSIMILAR STERILE SUTURE MATERIALS (INCLUDING STERILE ABSORBABALE SURGICAL OR DENTAL\nYARNS)AND STERILE TISSUE ADHESIVES FOR SURGICAL WOUND CLOSURE; STERILE LAMINARIA\nAND STERILE LAMINARIA TENTS; STERILE ABSORBABLE SURGICAL OR DENTAL\nHAEMOSTATICS;STERILE SURGICAL OR DENTAL ADHESION BARRIERS, WHETHER OR NOT\nABSORBABLE:STERILE SURGICAL CATGUT, SIMILAR STERILE SUTURE MATERIALS (INCLUDING\nSTERILE ABSORBABALE SURGICAL OR DENTAL YARNS)AND STERILE TISSUE ADHESIVES FOR\nSURGICAL WOUND CLOSURE",
+        "hsn_code": "30061010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PHARMACEUTICAL GOODS SPECIFIED IN NOTE 4 TO THIS CHAPTER STERILE SURGICAL CATGUT,\nSIMILAR STERILE SUTURE MATERIALS (INCLUDING STERILE ABSORBABALE SURGICAL OR DENTAL\nYARNS)AND STERILE TISSUE ADHESIVES FOR SURGICAL WOUND CLOSURE; STERILE LAMINARIA\nAND STERILE LAMINARIA TENTS; STERILE ABSORBABLE SURGICAL OR DENTAL\nHAEMOSTATICS;STERILE SURGICAL OR DENTAL ADHESION BARRIERS, WHETHER OR NOT\nABSORBABLE: STERILE LAMINARIA AND STERILE LAMINARIA TENTS; STERILE ABSORBABLE\nSURGICAL OR DENTAL HAEMOSTATICS;STERILE SURGICAL OR DENTAL ADHESION BARRIERS,\nWHETHER OR NOT ABSORBABLE",
+        "hsn_code": "30061020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PHARMACEUTICAL GOODS SPECIFIED IN NOTE 4 TO THIS CHAPTER BLOOD GROUPING REAGENTS",
+        "hsn_code": "30062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PHARMACEUTICAL GOODS SPECIFIED IN NOTE 4 TO THIS CHAPTER OPACIFYING PREPRATIONS\nFOR X-RAY EXAMINATIONS; DIAGNOSTIC REAGENTS DESIGNED TO BE ADMINISTERED TO THE\nPATIENT",
+        "hsn_code": "30063000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PHARMACEUTICAL GOODS SPECIFIED IN NOTE 4 TO THIS CHAPTER DENTAL CEMENTS AND\nOTHER DENTAL FILLINGS;BONE RECONSTRUCTION CEMENTS",
+        "hsn_code": "30064000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PHARMACEUTICAL GOODS SPECIFIED IN NOTE 4 TO THIS CHAPTER FIRST-AID BOXES AND KITS",
+        "hsn_code": "30065000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PHARMACEUTICAL GOODS SPECIFIED IN NOTE 4 TO THIS CHAPTER CHEMICAL CONTRACEPTIVE\nPREPARATIONS BASED ON HORMONES, OR OTHER PRODUCTS OF HEADING 2937 OR ON\nSPERMICIDES :BASED ON HORMONES",
+        "hsn_code": "30066010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PHARMACEUTICAL GOODS SPECIFIED IN NOTE 4 TO THIS CHAPTER CHEMICAL CONTRACEPTIVE\nPREPARATIONS BASED ON HORMONES, OR OTHER PRODUCTS OF HEADING 2937 OR ON\nSPERMICIDES :BASED ON OTHER PRODUCTS OF HEADING 2937",
+        "hsn_code": "30066020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PHARMACEUTICAL GOODS SPECIFIED IN NOTE 4 TO THIS CHAPTER CHEMICAL CONTRACEPTIVE\nPREPARATIONS BASED ON HORMONES, OR OTHER PRODUCTS OF HEADING 2937 OR ON\nSPERMICIDES :BASED ON SPERMICIDES",
+        "hsn_code": "30066030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PHARMACEUTICAL GOODS SPECIFIED IN NOTE 4 TO THIS CHAPTER GEL PREPARATIONS\nDESIGNED TO BE USED IN HUMAN OR VETERINARY MEDICINE AS A LUBRICANT FOR PARTS OF\nTHE BODY FOR SURGICAL OPERATIONS OR PHYSICAL EXAMINATIONS OR AS A COUPLING AGENT\nBETWEEN THE BODY AND MEDICAL INSTRUMENTS",
+        "hsn_code": "30067000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PHARMACEUTICAL GOODS SPECIFIED IN NOTE 4 TO THIS CHAPTER WASTE PHARMACEUTICALS",
+        "hsn_code": "30068000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "APLLIANCES IDENTIFIED FOR OSOTOMY USE",
+        "hsn_code": "30069100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WASTE PHARMACEUTICALS",
+        "hsn_code": "30069200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PHARMACEUTICAL GOODS SPECIFIED IN NOTE 4 TO THIS CHAPTER",
+        "hsn_code": "3006",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ALKALI OR ALKALINE-EARTH METALS; RARE-EARTH METALS, SCANDIUM AND YTTRIUM,\nWHETHER OR NOT INTERMIXED OR INTERALLOYED; MERCURY - ALKALI OR ALKALINE-EARTH\nMETALS : SODIUM",
+        "hsn_code": "28051100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM GASES AND OTHER GASEOUS HYDROCARBONS - LIQUIFIED: OTHER",
+        "hsn_code": "27111900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM GASES AND OTHER GASEOUS HYDROCARBONS - LIQUIFIED: BUTANE",
+        "hsn_code": "27111300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM GASES AND OTHER GASEOUS HYDROCARBONS - LIQUIFIED: PROPANE",
+        "hsn_code": "27111200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GRANITE, PORPHYRY, BASALT, SANDSTONE AND OTHER MONUMENTAL OR BUILDING STONE,\nWHETHER OR NOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO\nBLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE) SHAPE - GRANITE : CRUDE OR\nROUGHLY TRIMMED",
+        "hsn_code": "25161100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GRANITE, PORPHYRY, BASALT, SANDSTONE AND OTHER MONUMENTAL OR BUILDING STONE, WHETHER OR NOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE) SHAPE",
+        "hsn_code": "2516",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GRANITE, PORPHYRY, BASALT, SANDSTONE AND OTHER MONUMENTAL OR BUILDING STONE,\nWHETHER OR NOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO\nBLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE) SHAPE - OTHER MONUMENTAL OR\nBUILDING STONE: OTHER",
+        "hsn_code": "25169090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GRANITE, PORPHYRY, BASALT, SANDSTONE AND OTHER MONUMENTAL OR BUILDING STONE,\nWHETHER OR NOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO\nBLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE) SHAPE - OTHER MONUMENTAL OR\nBUILDING STONE: STONE BOULDERS",
+        "hsn_code": "25169020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GRANITE, PORPHYRY, BASALT, SANDSTONE AND OTHER MONUMENTAL OR BUILDING STONE,\nWHETHER OR NOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO\nBLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE) SHAPE - OTHER MONUMENTAL OR\nBUILDING STONE: PAKUR STONE",
+        "hsn_code": "25169010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GRANITE, PORPHYRY, BASALT, SANDSTONE AND OTHER MONUMENTAL OR BUILDING STONE,\nWHETHER OR NOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO\nBLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE) SHAPE - SANDSTONE: MERELY CUT,\nBY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE)\nSHAPE",
+        "hsn_code": "25162200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GRANITE, PORPHYRY, BASALT, SANDSTONE AND OTHER MONUMENTAL OR BUILDING STONE,\nWHETHER OR NOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO\nBLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE) SHAPE - SANDSTONE: CRUDE OR\nROUGHLY TRIMMED",
+        "hsn_code": "25162100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GRANITE, PORPHYRY, BASALT, SANDSTONE AND OTHER MONUMENTAL OR BUILDING STONE,\nWHETHER OR NOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO\nBLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE) SHAPE - SANDSTONE",
+        "hsn_code": "25162000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MARBLE, TRAVERTINE, ECAUSSINE AND OTHER CALCAREOUS MONUMENTAL OR BUILDING\nSTONE OF AN APPARENT SPECIFIC GRAVITY OF 2.5 OR MORE, AND ALABASTER, WHETHER OR\nNOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS\nOF A RECTANGULAR (INCLUDING SQUARE) SHAPE - MARBLE AND TRAVERTINE: CRUDE OR\nROUGHLY TRIMMED",
+        "hsn_code": "25151100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MARBLE, TRAVERTINE, ECAUSSINE AND OTHER CALCAREOUS MONUMENTAL OR BUILDING STONE OF AN APPARENT SPECIFIC GRAVITY OF 2.5 OR MORE, AND ALABASTER, WHETHER OR NOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE) SHAPE",
+        "hsn_code": "2515",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MARBLE, TRAVERTINE, ECAUSSINE AND OTHER CALCAREOUS MONUMENTAL OR BUILDING\nSTONE OF AN APPARENT SPECIFIC GRAVITY OF 2.5 OR MORE, AND ALABASTER, WHETHER OR\nNOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS\nOF A RECTANGULAR (INCLUDING SQUARE) SHAPE - ECAUSSINE AND OTHER CALCAREOUS\nMONUMENTAL OR BUILDING STONE; ALABASTER: OTHER",
+        "hsn_code": "25152090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MARBLE, TRAVERTINE, ECAUSSINE AND OTHER CALCAREOUS MONUMENTAL OR BUILDING\nSTONE OF AN APPARENT SPECIFIC GRAVITY OF 2.5 OR MORE, AND ALABASTER, WHETHER OR\nNOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS\nOF A RECTANGULAR (INCLUDING SQUARE) SHAPE - ECAUSSINE AND OTHER CALCAREOUS\nMONUMENTAL OR BUILDING STONE; ALABASTER: ALABASTER",
+        "hsn_code": "25152010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WATERS, INCLUDING NATURAL OR ARTIFICIALMINERAL WATERS AND AERATED WATERS,\nNOTCONTAINING ADDED SUGAR OR OTHERSWEETENING MATTER NOR FLAVOURED; ICEAND\nSNOW - OTHER : ICE AND SNOW",
+        "hsn_code": "22019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BREAD, PASTRY, CAKES, BISCUITS AND OTHER BAKERS WARES, WHETHER OR NOT CONTAINING\nCOCOA; COMMUNION WAFERS, EMPTY CACHETS OF A KIND SUITABLE FOR PHARMACEUTICAL\nUSE, SEALING WAFERS, RICE PAPER AND SIMILAR PRODUCTS - RUSKS, TOASTED BREAD AND\nSIMILAR TOASTED PRODUCTS",
+        "hsn_code": "19054000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FOOD PREPARATIONS, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "2106",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PROTEIN CONCENTRATES AND TEXTURED PROTEIN SUBSTANCES",
+        "hsn_code": "21061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FOOD PREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER : PAN MASALA",
+        "hsn_code": "21069020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FOOD PREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER : OTHER : OTHER",
+        "hsn_code": "21069099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FOOD PREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER : OTHER : STERILIZED\nOR PASTEURIZED MILTONE",
+        "hsn_code": "21069092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FOOD PREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER : CUSTARD POWDER",
+        "hsn_code": "21069080",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FOOD PREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER : CHURNA FOR PAN",
+        "hsn_code": "21069070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FOOD PREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER : FOOD FLAVOURING\nMATERIAL",
+        "hsn_code": "21069060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FOOD PREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER : COMPOUND\nPREPARATIONS FOR MAKING NON-ALCOHOLIC BEVERAGES",
+        "hsn_code": "21069050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FOOD PREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER : SUGAR-SYRUPS\nCONTAINING ADDED FLAVOURING OR COLOURING MATTER, NOT ELSEWHERE SPECIFIED\nORINCLUDED; LACTOSE SYRUP; GLUCOSE SYRUP ANDMALTO DEXTRINE SYRUP",
+        "hsn_code": "21069040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FOOD PREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER : BETEL NUT PRODUCT\nKNOWN AS SUPARI",
+        "hsn_code": "21069030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FOOD PREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER : SOFT DRINK\nCONCENTRATES : OTHER",
+        "hsn_code": "21069019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FOOD PREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER : SOFT DRINK\nCONCENTRATES : SHARBAT",
+        "hsn_code": "21069011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BREAD, PASTRY, CAKES, BISCUITS AND OTHER BAKERS WARES, WHETHER OR NOT CONTAINING COCOA; COMMUNION WAFERS, EMPTY CACHETS OF A KIND SUITABLE FOR PHARMACEUTICAL USE, SEALING WAFERS, RICE PAPER AND SIMILAR PRODUCTS",
+        "hsn_code": "1905",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BREAD, PASTRY, CAKES, BISCUITS AND OTHER BAKERS WARES, WHETHER OR NOT CONTAINING\nCOCOA; COMMUNION WAFERS, EMPTY CACHETS OF A KIND SUITABLE FOR PHARMACEUTICAL\nUSE, SEALING WAFERS, RICE PAPER AND SIMILAR PRODUCTS - OTHER : OTHER",
+        "hsn_code": "19059090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BREAD, PASTRY, CAKES, BISCUITS AND OTHER BAKERS WARES, WHETHER OR NOT CONTAINING\nCOCOA; COMMUNION WAFERS, EMPTY CACHETS OF A KIND SUITABLE FOR PHARMACEUTICAL\nUSE, SEALING WAFERS, RICE PAPER AND SIMILAR PRODUCTS - OTHER : PAPAD",
+        "hsn_code": "19059040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BREAD, PASTRY, CAKES, BISCUITS AND OTHER BAKERS WARES, WHETHER OR NOT CONTAINING\nCOCOA; COMMUNION WAFERS, EMPTY CACHETS OF A KIND SUITABLE FOR PHARMACEUTICAL\nUSE, SEALING WAFERS, RICE PAPER AND SIMILAR PRODUCTS - OTHER : EXTRUDED OR EXPANDED\nPRODUCTS, SAVOURY OR SALTED",
+        "hsn_code": "19059030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BREAD, PASTRY, CAKES, BISCUITS AND OTHER BAKERS WARES, WHETHER OR NOT CONTAINING\nCOCOA; COMMUNION WAFERS, EMPTY CACHETS OF A KIND SUITABLE FOR PHARMACEUTICAL\nUSE, SEALING WAFERS, RICE PAPER AND SIMILAR PRODUCTS - OTHER : BISCUITS NOT\nELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "19059020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BREAD, PASTRY, CAKES, BISCUITS AND OTHER BAKERS WARES, WHETHER OR NOT CONTAINING\nCOCOA; COMMUNION WAFERS, EMPTY CACHETS OF A KIND SUITABLE FOR PHARMACEUTICAL\nUSE, SEALING WAFERS, RICE PAPER AND SIMILAR PRODUCTS - OTHER : PASTRIES AND CAKES",
+        "hsn_code": "19059010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BREAD, PASTRY, CAKES, BISCUITS AND OTHER BAKERS WARES, WHETHER OR NOT CONTAINING\nCOCOA; COMMUNION WAFERS, EMPTY CACHETS OF A KIND SUITABLE FOR PHARMACEUTICAL\nUSE, SEALING WAFERS, RICE PAPER AND SIMILAR PRODUCTS - SWEET BISCUITS; WAFFLES AND\nWAFERS --WAFFLES AND WAFERS : OTHER",
+        "hsn_code": "19053290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BREAD, PASTRY, CAKES, BISCUITS AND OTHER BAKERS WARES, WHETHER OR NOT CONTAINING\nCOCOA; COMMUNION WAFERS, EMPTY CACHETS OF A KIND SUITABLE FOR PHARMACEUTICAL\nUSE, SEALING WAFERS, RICE PAPER AND SIMILAR PRODUCTS - - SWEET BISCUITS; WAFFLES AND\nWAFERS --WAFFLES AND WAFERS : - COMMUNION WAFERS : OTHER",
+        "hsn_code": "19053219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BREAD, PASTRY, CAKES, BISCUITS AND OTHER BAKERS WARES, WHETHER OR NOT CONTAINING\nCOCOA; COMMUNION WAFERS, EMPTY CACHETS OF A KIND SUITABLE FOR PHARMACEUTICAL\nUSE, SEALING WAFERS, RICE PAPER AND SIMILAR PRODUCTS - - SWEET BISCUITS; WAFFLES AND\nWAFERS-- WAFFLES AND WAFERS : --- COMMUNION WAFERS : COATED WITH CHOCOLATE OR\nCONTAINING CHOCOLATE",
+        "hsn_code": "19053211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BREAD, PASTRY, CAKES, BISCUITS AND OTHER BAKERR WARES, WHETHER OR NOT CONTAINING\nCOCOA; COMMUNION WAFERS, EMPTY CACHETS OF A KIND SUITABLE FOR PHARMACEUTICAL\nUSE, SEALING WAFERS, RICE PAPER AND SIMILAR PRODUCTS- SWEET BISCUITS; WAFFLES AND\nWAFERS--SWEET BISCUITS",
+        "hsn_code": "19053100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BREAD, PASTRY, CAKES, BISCUITS AND OTHER BAKERS WARES, WHETHER OR NOT CONTAINING\nCOCOA; COMMUNION WAFERS, EMPTY CACHETS OF A KIND SUITABLE FOR PHARMACEUTICAL\nUSE, SEALING WAFERS, RICE PAPER AND SIMILAR PRODUCTS-GINGERBREAD AND THE LIKE",
+        "hsn_code": "19052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BREAD, PASTRY, CAKES, BISCUITS AND OTHER BAKERS WARES, WHETHER OR NOT CONTAINING\nCOCOA; COMMUNION WAFERS, EMPTY CACHETS OF A KIND SUITABLE FOR PHARMACEUTICAL\nUSE, SEALING WAFERS, RICE PAPER AND SIMILAR PRODUCTS -CRISPBREAD",
+        "hsn_code": "19051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUGAR CONFECTIONERY (INCLUDING WHITE CHOCOLATE), NOT CONTAINIG COCOA - CHEWING\nGUM, WHETHER OR NOT SUGAR COATED",
+        "hsn_code": "17041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUGAR CONFECTIONARY (INCLUDING WHITE CHOCOLATE), NOT CONTAINING COCOA - OTHER :\nJELLY CONFECTIONARY",
+        "hsn_code": "17049010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUGAR CONFECTIONARY (INCLUDING WHITE CHOCOLATE), NOT CONTAINING COCOA - OTHER :\nBOILED SWEETS, WHETHER OR NOT FILLED",
+        "hsn_code": "17049020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUGAR CONFECTIONARY (INCLUDING WHITE CHOCOLATE), NOT CONTAINING COCOA - OTHER :\nTOFFEES, CARAMELS AND SIMILAR SWEETS",
+        "hsn_code": "17049030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUGAR CONFECTIONARY (INCLUDING WHITE CHOCOLATE), NOT CONTAINING COCOA - OTHER :\nOTHER",
+        "hsn_code": "17049090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUGAR CONFECTIONERY (INCLUDING WHITE CHOCOLATE), NOT CONTAINING COCOA",
+        "hsn_code": "1704",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL -\nLACTOSE AND LACTOSE SYRUP : - CONTAINING BY WEIGHT 99% OR MORE LACTOSE, EXPRESSED\nAS ANHYDROUS LACTOSE, CALCULATED ON THE DRY MATTER : IN SOLID FORM",
+        "hsn_code": "17021110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL\nLACTOSE AND LACTOSE SYRUP : - CONTAINING BY WEIGHT 99% OR MORE LACTOSE, EXPRESSED\nAS ANHYDROUS LACTOSE, CALCULATED ON THE DRY MATTER : OTHER",
+        "hsn_code": "17021190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL -\nLACTOSE AND LACTOSE SYRUP : OTHER : IN SOLID FORM",
+        "hsn_code": "17021910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL -\nLACTOSE AND LACTOSE SYRUP : OTHER : OTHER",
+        "hsn_code": "17021990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL -\nMAPLE SUGAR AND MAPLE SYRUP : IN SOLID FORM",
+        "hsn_code": "17022010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL -\nMAPLE SUGAR AND MAPLE SYRUP : OTHER",
+        "hsn_code": "17022090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL\nGLUCOSE AND GLUCOSE SYRUP, NOT CONTAINING FRUCTOSE OR CONTAINING IN THE DRY\nSTATE LESS THAN 20% BY WEIGHT OF FRUCTOSE : GLUCOSE, LIQUID",
+        "hsn_code": "17023010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL\nGLUCOSE AND GLUCOSE SYRUP, NOT CONTAINING FRUCTOSE OR CONTAINING IN THE DRY\nSTATE LESS THAN 20% BY WEIGHT OF FRUCTOSE : GLUCOSE, SOLID",
+        "hsn_code": "17023020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL\nGLUCOSE AND GLUCOSE SYRUP, NOT CONTAINING FRUCTOSE OR CONTAINING IN THE DRY\nSTATE LESS THAN 20% BY WEIGHT OF FRUCTOSE : - DEXTROSE : IN SOLID FORM",
+        "hsn_code": "17023031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL\nGLUCOSE AND GLUCOSE SYRUP, NOT CONTAINING FRUCTOSE OR CONTAINING IN THE DRY\nSTATE LESS THAN 20% BY WEIGHT OF FRUCTOSE : - DEXTROSE : OTHER",
+        "hsn_code": "17023039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL\nGLUCOSE AND GLUCOSE SYRUP, CONTAINING IN THE DRY STATE AT LEAST 20% BUT LESS THAN\n50% BY WEIGHT OF FRUCTOSE, EXCLUDING INVERT SUGAR : GLUCOSE, LIQUID",
+        "hsn_code": "17024010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL\nGLUCOSE AND GLUCOSE SYRUP, CONTAINING IN THE DRY STATE AT LEAST 20% BUT LESS THAN\n50% BY WEIGHT OF FRUCTOSE, EXCLUDING INVERT SUGAR : GLUCOSE, SOLID",
+        "hsn_code": "17024020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL\nGLUCOSE AND GLUCOSE SYRUP, CONTAINING IN THE DRY STATE AT LEAST 20% BUT LESS THAN\n50% BY WEIGHT OF FRUCTOSE, EXCLUDING INVERT SUGAR : DEXTROSE : IN SOLID FORM",
+        "hsn_code": "17024031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL\nGLUCOSE AND GLUCOSE SYRUP, CONTAINING IN THE DRY STATE AT LEAST 20% BUT LESS THAN\n50% BY WEIGHT OF FRUCTOSE, EXCLUDING INVERT SUGAR : DEXTROSE : OTHER",
+        "hsn_code": "17024039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL\nCHEMICALLY PURE FRUCTOSE",
+        "hsn_code": "17025000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL\nOTHER FRUCTOSE AND FRUCTOSE SYRUP, CONTAINING IN THE DRY STATE MORE THAN 50% BY\nWEIGHT OF FRUCTOSE, EXCLUDING INVERT SUGAR : IN SOLID FORM",
+        "hsn_code": "17026010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL\nOTHER FRUCTOSE AND FRUCTOSE SYRUP, CONTAINING IN THE DRY STATE MORE THAN 50% BY\nWEIGHT OF FRUCTOSE, EXCLUDING INVERT SUGAR : OTHER",
+        "hsn_code": "17026090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL -\nOTHER FRUCTOSE AND FRUCTOSE SYRUP, CONTAINING IN THE DRY STATE MORE THAN 50% BY\nWEIGHT OF FRUCTOSE, EXCLUDING INVERT SUGAR : OTHER, INCLUDING INVERT SUGAR AND\nOTHER SUGAR AND SUGAR SYRUP BLENDS CONTAINING IN THE DRY STATE 50% BY WEIGHT OF\nFRUCTOSE : PALMYRA SUGAR",
+        "hsn_code": "17029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL -\nOTHER FRUCTOSE AND FRUCTOSE SYRUP, CONTAINING IN THE DRY STATE MORE THAN 50% BY\nWEIGHT OF FRUCTOSE, EXCLUDING INVERT SUGAR : OTHER, INCLUDING INVERT SUGAR AND\nOTHER SUGAR AND SUGAR SYRUP BLENDS CONTAINING IN THE DRY STATE 50% BY WEIGHT OF\nFRUCTOSE : CHEMICALLY PURE MALTOSE",
+        "hsn_code": "17029020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL -\nOTHER FRUCTOSE AND FRUCTOSE SYRUP, CONTAINING IN THE DRY STATE MORE THAN 50% BY\nWEIGHT OF FRUCTOSE, EXCLUDING INVERT SUGAR : OTHER, INCLUDING INVERT SUGAR AND\nOTHER SUGAR AND SUGAR SYRUP BLENDS CONTAINING IN THE DRY STATE 50% BY WEIGHT OF\nFRUCTOSE : ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY",
+        "hsn_code": "17029030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL -\nOTHER FRUCTOSE AND FRUCTOSE SYRUP, CONTAINING IN THE DRY STATE MORE THAN 50% BY\nWEIGHT OF FRUCTOSE, EXCLUDING INVERT SUGAR : OTHER, INCLUDING INVERT SUGAR AND\nOTHER SUGAR AND SUGAR SYRUP BLENDS CONTAINING IN THE DRY STATE 50% BY WEIGHT OF\nFRUCTOSE : CARAMEL",
+        "hsn_code": "17029040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL -\nOTHER FRUCTOSE AND FRUCTOSE SYRUP, CONTAINING IN THE DRY STATE MORE THAN 50% BY\nWEIGHT OF FRUCTOSE, EXCLUDING INVERT SUGAR : OTHER, INCLUDING INVERT SUGAR AND\nOTHER SUGAR AND SUGAR SYRUP BLENDS CONTAINING IN THE DRY STATE 50% BY WEIGHT OF\nFRUCTOSE : INSULIN SYRUP",
+        "hsn_code": "17029050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE,\nIN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING\nMATTER; ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL -\nOTHER FRUCTOSE AND FRUCTOSE SYRUP, CONTAINING IN THE DRY STATE MORE THAN 50% BY\nWEIGHT OF FRUCTOSE, EXCLUDING INVERT SUGAR : OTHER, INCLUDING INVERT SUGAR AND\nOTHER SUGAR AND SUGAR SYRUP BLENDS CONTAINING IN THE DRY STATE 50% BY WEIGHT OF\nFRUCTOSE : OTHER",
+        "hsn_code": "17029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SUGARS, INCLUDING CHEMICALLY PURE LACTOSE, MALTOSE, GLUCOSE AND FRUCTOSE, IN SOLID FORM; SUGAR SYRUPS NOT CONTAINING ADDED FLAVOURING OR COLOURING MATTER, ARTIFICIAL HONEY, WHETHER OR NOT MIXED WITH NATURAL HONEY; CARAMEL",
+        "hsn_code": "1702",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "Preparations for use on the hair",
+        "hsn_code": "3305",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS FOR USE ON THE HAIR OTHER: OTHER",
+        "hsn_code": "33059090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS FOR USE ON THE HAIR OTHER: HAIR FIXERS",
+        "hsn_code": "33059050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS FOR USE ON THE HAIR OTHER: HAIR DYES (NATURAL, HERBAL OR SYNTHETIC)",
+        "hsn_code": "33059040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS FOR USE ON THE HAIR OTHER: HAIR CREAM",
+        "hsn_code": "33059030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS FOR USE ON THE HAIR OTHER: BRILLIANTINES (SPIRITUOUS)",
+        "hsn_code": "33059020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS FOR USE ON THE HAIR OTHER: HAIR OIL: OTHER",
+        "hsn_code": "33059019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS FOR USE ON THE HAIR OTHER: HAIR OIL: PERFUMED",
+        "hsn_code": "33059011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS FOR USE ON THE HAIR HAIR LACQUERS",
+        "hsn_code": "33053000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS FOR USE ON THE HAIR PREPARATIONS FOR PERMANENT WAVING OR\nSTRAIGHTENING",
+        "hsn_code": "33052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS FOR USE ON THE HAIR SHAMPOOS: OTHER",
+        "hsn_code": "33051090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS FOR USE ON THE HAIR SHAMPOOS: CONTAINING SPIRIT",
+        "hsn_code": "33051010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER : OTHER",
+        "hsn_code": "14049090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER : COCONUT SHELL,\nUNWORKED",
+        "hsn_code": "14049060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "1404",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER : RUDRAKSHA SEEDS",
+        "hsn_code": "14049070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER : HARD SEEDS, PIPS,\nHULLS AND NUTS, OF A KIND USED PRIMARILY FOR CARVING",
+        "hsn_code": "14049030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER : SOAP-NUTS : OTHER",
+        "hsn_code": "14049029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER : SOAP-NUTS :\nPOWDER",
+        "hsn_code": "14049021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED COTTON LINTERS",
+        "hsn_code": "14042000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED RAW VEGETABLE MATERIALS\nOF A KIND USED PRIMARILY IN DYEING OR TANNING : OTHER TANNING SUBSTANCES",
+        "hsn_code": "14041090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED RAW VEGETABLE MATERIALS\nOF A KIND USED PRIMARILY IN DYEING OR TANNING : WATTLE BARK (MIMOSA BARK)",
+        "hsn_code": "14041070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED RAW VEGETABLE MATERIALS\nOF A KIND USED PRIMARILY IN DYEING OR TANNING : MYROBALANS : OTHER",
+        "hsn_code": "14041069",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED RAW VEGETABLE MATERIALS\nOF A KIND USED PRIMARILY IN DYEING OR TANNING : MYROBALANS : AMLA (EMBELICA LINN)",
+        "hsn_code": "14041061",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED RAW VEGETABLE MATERIALS\nOF A KIND USED PRIMARILY IN DYEING OR TANNING : GALLNUTS",
+        "hsn_code": "14041050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED RAW VEGETABLE MATERIALS\nOF A KIND USED PRIMARILY IN DYEING OR TANNING : DYEING SUBSTANCES, EXCLUDING\nTURMERIC",
+        "hsn_code": "14041040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED RAW VEGETABLE MATERIALS\nOF A KIND USED PRIMARILY IN DYEING OR TANNING : SAFFLOWER (INCLUDING BASTARD\nSAFFRON)",
+        "hsn_code": "14041030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED RAW VEGETABLE MATERIALS\nOF A KIND USED PRIMARILY IN DYEING OR TANNING : RED SANDALWOOD POWDER",
+        "hsn_code": "14041020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED RAW VEGETABLE MATERIALS\nOF A KIND USED PRIMARILY IN DYEING OR TANNING : HENNA : HENNA POWDER",
+        "hsn_code": "14041019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED RAW VEGETABLE MATERIALS\nOF A KIND USED PRIMARILY IN DYEING OR TANNING : HENNA : HENNA LEAVES",
+        "hsn_code": "14041011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER : BETEL LEAVES",
+        "hsn_code": "14049040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HOP CONES, FRESH OR DRIED, WHETHER OR NOT GROUND, POWDERED OR IN THE FORM OF\nPELLETS; LUPULIN HOP CONES, GROUND, POWDERED OR IN THE FORM OF PELLETS; LUPULIN",
+        "hsn_code": "12102000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WHEAT GLUTEN, WHETHER OR NOT DRIED",
+        "hsn_code": "11090000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES",
+        "hsn_code": "0910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NUTMEG, MACE AND CARDAMOMS",
+        "hsn_code": "0908",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CLOVES (WHOLE FRUIT, CLOVES AND STEMS)",
+        "hsn_code": "0907",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CINNAMON AND CINNAMON-TREE FLOWERS",
+        "hsn_code": "0906",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VANILLA",
+        "hsn_code": "0905",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PEPPER OF THE GENUS PIPER; DRIED OR CRUSHED OR GROUND FRUITS OF THE GENUS CAPSICUM OR OF THE GENUS PIMENTA",
+        "hsn_code": "0904",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "Mate",
+        "hsn_code": "0903",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FRUIT AND NUTS PROVISIONALLY PRESERVED (FOR EXAMPLE, BY SULPHUR DIOXIDE GAS, IN BRINE, IN SULPHUR WATER OR IN OTHER PRESERVATIVE SOLUTIONS), BUT UNSUITABLE IN THAT STATE FOR IMMEDIATE CONSUMPTION",
+        "hsn_code": "0812",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FRUIT AND NUTS, UNCOOKED OR COOKED BY STEAMING OR BOILING IN WATER, FROZEN, WHETHER OR NOT CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER",
+        "hsn_code": "0811",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, DRIED, OTHER THAN THAT OF HEADINGS 0801 TO 0806; MIXTURES OF NUTS OR DRIED FRUITS OF THIS CHAPTER",
+        "hsn_code": "0813",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER NUTS, FRESH OR DRIED, WHETHER OR NOT SHELLED OR PEELED",
+        "hsn_code": "0802",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "AMBERGRIS, CASTOREUM, CIVET AND MUSK; CANTHARIDES; BILE, WHETHER OR NOT DRIED; GLANDS AND OTHER ANIMAL PRODUCTS USED IN THE PREPARATION OF PHARMACEUTICAL PRODUCTS, FRESH, CHILLED, FROZEN OR OTHERWISE PROVISIONALLY PRESERVED",
+        "hsn_code": "0510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CORAL AND SIMILAR MATERIALS, UNWORKED OR SIMPLY PREPARED BUT NOT OTHERWISE WORKED; SHELLS OF MOLLUSCS, CRUSTACEANS OR ECHINODERMS AND CUTTLE-BONE, UNWORKED OR SIMPLY PREPARED BUT NOT CUT TO SHAPE, POWDER AND WASTE THEREOF",
+        "hsn_code": "0508",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "IVORY, TORTOISE-SHELL, WHALEBONE AND WHALE-BONE HAIR, HORNS, ANTLERS, HOOVES, NAILS, CLAWS AND BEAKS, UNWORKED OR SIMPLY PREPARED BUT NOT CUT TO SHAPE; POWDER AND WASTE OF THESE PRODUCTS",
+        "hsn_code": "0507",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SKINS AND OTHER PARTS OF BIRDS, WITH THEIR FEATHERS OR DOWN, FEATHERS AND PARTS OF FEATHERS (WHETHER OR NOT WITH TRIMMED EDGES) AND DOWN, NOT FURTHER WORKED THAN CLEANED, DISINFECTED OR TREATED FOR PRESERVATION; POWDER AND WASTE OF FEATHERS OR PARTS OF FEATHERS",
+        "hsn_code": "0505",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PIGS, HOGS OR BOARS BRISTLES AND HAIR; BADGER HAIR AND OTHER BRUSH MAKING HAIR; WASTE OF SUCH BRISTLES OR HAIR",
+        "hsn_code": "0502",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "EDIBLE PRODUCTS OF ANIMAL ORIGIN, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "0410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BIRDS EGGS, NOT IN SHELL, AND EGG YOLKS, FRESH, DRIED, COOKED BY STEAMING OR BY BOILING IN WATER, MOULDED, FROZEN OR OTHERWISE PRESERVED, WHETHER OR NOT CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER",
+        "hsn_code": "0408",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WHEY, WHETHER OR NOT CONCENTRATED OR CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER; PRODUCTS CONSISTING OF NATURAL MILK CONSTITUENTS, WHETHER OR NOT CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "0404",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MILK AND CREAM, CONCENTRATED OR CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER",
+        "hsn_code": "0402",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "AQUATIC INVERTEBRATES OTHER THAN CRUSTACEANS AND MOLLUSCS, LIVE,FRESH, CHILLED,  FROZEN, DRIED, SALTED OR IN BRINE; SMOKED AQUATIC INVERTEBRATES OTHER THAN CRUSTACEANS AND MOLLUSCS, WHETHER OR NOT COOKED BEFORE OR DURING THE SMOKING PROCESS; FLOURS, MEALS AND PELLETS OF AQUATIC INVERTEBRATES OTHER THAN CRUSTACEANS AND MOLLUSCS, FIT FOR HUMAN CONSUMPTION",
+        "hsn_code": "0308",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MOLLUSCS, WHETHER IN SHELL OR NOT, LIVE, FRESH, CHILLED, FROZEN, DRIED, SALTED OR IN BRINE; SMOKED MOLLUSCS, WHETHER IN SHELL OR NOT, WHETHER OR NOT COOKED BEFORE OR DURING THE SMOKING PROCESS; FLOURS, MEALS AND PELLETS OF MOLLUSCS, FIT FOR HUMAN CONSUMPTION",
+        "hsn_code": "0307",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, WHETHER IN SHELL OR NOT, LIVE, FRESH, CHILLED, FROZEN, DRIED, SALTED OR IN BRINE; SMOKED CRUSTACEANS, WHETHER IN SHELL OR NOT, WHETHER OR NOT COOKED BEFORE OR DURING THE SMOKING PROCESS; CRUSTACEANS, IN SHELL, COOKED BY STEAMING OR BY BOILING IN WATER, WHETHER OR NOT CHILLED, FROZEN, DRIED, SALTED OR IN BRINE; FLOURS, MEALS AND PELLETS OF CRUSTACEANS, FIT FOR HUMAN CONSUMPTION",
+        "hsn_code": "0306",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, DRIED, SALTED OR IN BRINE; SMOKED FISH, WHETHER OR NOT COOKED BEFORE OR DURING THE SMOKING PROCESS; FLOURS, MEALS AND PELLETS, OF FISH FIT FOR HUMAN CONSUMPTION",
+        "hsn_code": "0305",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH FILLETS AND OTHER FISH MEAT (WHETHER OR NOT MINCED), FRESH, CHILLED OR FROZEN",
+        "hsn_code": "0304",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISH, FROZEN, EXCLUDING FISH FILLETS AND OTHER FISH MEAT OF HEADING 0304",
+        "hsn_code": "0303",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEAT AND EDIBLE MEAT OFFAL, SALTED, IN BRINE, DRIED OR SMOKED; EDIBLE FLOURS AND MEALS OF MEAT OR MEAT OFFAL",
+        "hsn_code": "0210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PIG FAT, FREE OF LEAN MEAT AND POULTRY FAT, NOT RENDERED OR OTHERWISE EXTRACTED, FRESH, CHILLED, FROZEN, SALTED, IN BRINE, DRIED OR SMOKED",
+        "hsn_code": "0209",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER MEAT AND EDIBLE MEAT OFFAL, FRESH, CHILLED OR FROZEN",
+        "hsn_code": "0208",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEAT AND EDIBLE OFFAL, OF THE POULTRY OF HEADING 0105, FRESH, CHILLED OR FROZEN",
+        "hsn_code": "0207",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "EDIBLE OFFAL OF BOVINE ANIMALS, SWINE, SHEEP, GOATS, HORSES, ASSES, MULES OR HINNIES, FRESH, CHILLED OR FROZEN",
+        "hsn_code": "0206",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF SHEEP OR GOATS, FRESH, CHILLED OR FROZEN",
+        "hsn_code": "0205",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF SHEEP OR GOATS, FRESH, CHILLED OR FROZEN",
+        "hsn_code": "0204",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF SWINE, FRESH, CHILLED OR FROZEN",
+        "hsn_code": "0203",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF BOVINE ANIMALS, FROZEN",
+        "hsn_code": "0202",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WADDING OF TEXTILE MATERIALS AND ARTICLES THEREOF; TEXTILE FIBRES, NOT EXCEEDING 5\nMM IN LENGTH (FLOCK), TEXTILE DUST AND MILL NEPS - WADDING; OTHER ARTICLES OF\nWADDING : OF MAN-MADE FIBRES",
+        "hsn_code": "56012200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRAILERS AND SEMI-TRAILERS; OTHER VEHICLES, NOT MECHANICALLY PROPELLED; PARTS\nTHEREOF OTHER VEHICLES OTHER",
+        "hsn_code": "87168090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRAILERS AND SEMI-TRAILERS; OTHER VEHICLES, NOT MECHANICALLY PROPELLED; PARTS\nTHEREOF OTHER VEHICLES ANIMAL DRAWN VEHICLES",
+        "hsn_code": "87168020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRAILERS AND SEMI-TRAILERS; OTHER VEHICLES, NOT MECHANICALLY PROPELLED; PARTS\nTHEREOF OTHER VEHICLES HAND PROPELLED VEHICLES (E.G. HAND CARTS, RICKSHAWS AND THE\nLIKE)",
+        "hsn_code": "87168010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALL DUTIABLE ARTICLES, INTENDED FOR PERSONAL USE, IMPORTED BY POST OR AIR, AND EXEMPTED FROM ANY PROHIBITION IN RESPECT OF THE IMPORTS THEREOF UNDER THE FOREIGN TRADE (DEVELOPMENT & REGULATION) ACT, 1992 BUT EXCLUDING ARTICLES FALLING UNDER HEADING 9803",
+        "hsn_code": "9804",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "Laboratory Chemicals",
+        "hsn_code": "9802",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALL ITEMS OF MACHINERY INCLUDING PRIME MOVERS, INSTRUMENTS, APPARATUS AND APPLIANCES, CONTROL GEAR AND TRANSMISSION EQUIPMENT, AUXILLIARY EQUIPMENT (INCLUDING THOSE REQUIRED FOR RESEARCH AND DEVELOPMENT PURPOSES, TESTING AND QUALITY CONTROL), AS WELL AS ALL COMPONENTS (WHETHER FINISHED OR NOT) OR RAW MATERIALS FOR THE MANUFACTURE OF THE AFORESAID ITEMS AND THEIR COMPONENTS, REQUIRED FOR THE INITIAL SETTING UP OF A UNIT, OR THE SUBSTANTIAL EXPANSION OF AN EXISTING UNIT, OF A SPECIFIED: (1) INDUSTRIAL PLANT, (2) IRRIGATION PROJECT, (3) POWER PROJECT, (4) MINING PROJECT, (5) PROJECT FOR THE EXPLORATION FOR OIL OR OTHER MINERALS, AND (6) SUCH OTHER PROJECTS AS THE CENTRAL GOVERNMENT MAY, HAVING REGARD TO THE ECONOMIC DEVELOPMENT OF THE COUNTRY NOTIFY IN THE OFFICIAL GAZETTE IN THIS BEHALF; AND SPARE PARTS, OTHER RAW MATERIALS (INCLUDING SEMI-FINISHED MATERIAL) OR CONSUMABLE STORES NOT EXCEEDING 10% OF THE VALUE OF THE GOODS SPECIFIED ABOVE PROVIDED THAT SUCH SPARE PARTS, RAW MATERIALS OR C",
+        "hsn_code": "9801",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Antiques of an age exceeding one hundred years",
+        "hsn_code": "9706",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COLLECTIONS AND COLLECTORS’ PIECES OF ZOOLOGICAL, BOTANICAL, MINERALOGICAL, ANATOMICAL, HISTORICAL, ARCHAEOLOGICAL, PALAEONTOLOGICAL, ETHNOGRAPHIC OR NUMISMATIC INTEREST:",
+        "hsn_code": "9705",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POSTAGE OR REVENUE STAMPS, STAMP-POST MARKS, FIRST-DAY COVERS, POSTAL STATIONERY (STAMPED PAPER), AND THE LIKE, USED OR UNUSED, OTHER THAN THOSE OF HEADING 4907",
+        "hsn_code": "9704",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ORIGINAL SCULPTURES AND STATUARY, IN ANY MATERIAL",
+        "hsn_code": "9703",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Original engravings, prints and lithographs",
+        "hsn_code": "9702",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAINTINGS, DRAWINGS AND PASTELS, EXECUTED ENTIRELY BY HAND, OTHER THAN DRAWINGS OF HEADING 4906 AND OTHER THAN HAND-PAINTED OR HAND-DECORATED MANUFACTURED ARTICLES; COLLAGES AND SIMILAR DECORATIVE PLAQUES",
+        "hsn_code": "9701",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Tailors’ dummies and other lay figures; automata and other animated displays, used for shop window dressing",
+        "hsn_code": "9618",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TAILORS' DUMMIES AND OTHER LAY FIGURES; AUTOMATA AND OTHER ANIMATED DISPLAYS,\nUSED FOR SHOP WINDOW DRESSING",
+        "hsn_code": "96180000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VACUUM FLASKS AND OTHER VACUUM VESSELS, COMPLETE WITH CASES; PARTS THEREOF OTHER THAN GLASS INNERS",
+        "hsn_code": "9617",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VACUUM FLASKS AND OTHER VACUUM VESSELS, COMPLETE WITH CASES; PARTS THEREOF\nOTHER THAN GLASS INNERS - Vacuum flasks and other vacuum vessels, complete with cases;\nparts thereof other than glass inners: Parts (other than glass inners)",
+        "hsn_code": "96170090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VACUUM FLASKS AND OTHER VACUUM VESSELS, COMPLETE WITH CASES; PARTS THEREOF\nOTHER THAN GLASS INNERS - Vacuum flasks and other vacuum vessels, complete with cases;\nparts thereof other than glass inners: - Vacuum flasks and other vacuum vessels, complete with\ncase: Other",
+        "hsn_code": "96170019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VACUUM FLASKS AND OTHER VACUUM VESSELS, COMPLETE WITH CASES; PARTS THEREOF\nOTHER THAN GLASS INNERS - Vacuum flasks and other vacuum vessels, complete with cases;\nparts thereof other than glass inners: - Vacuum flasks and other vacuum vessels, complete with\ncase: Casserol and other vacuum containers",
+        "hsn_code": "96170013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VACUUM FLASKS AND OTHER VACUUM VESSELS, COMPLETE WITH CASES; PARTS THEREOF\nOTHER THAN GLASS INNERS - Vacuum flasks and other vacuum vessels, complete with cases;\nparts thereof other than glass inners: - Vacuum flasks and other vacuum vessels, complete with\ncase: Vacuum flasks having a capacity exceeding 0.75 l",
+        "hsn_code": "96170012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VACUUM FLASKS AND OTHER VACUUM VESSELS, COMPLETE WITH CASES; PARTS THEREOF\nOTHER THAN GLASS INNERS - Vacuum flasks and other vacuum vessels, complete with cases;\nparts thereof other than glass inners: - Vacuum flasks and other vacuum vessels, complete with\ncase: Vacuum flasks having a capacity not exceeding 0.75 l",
+        "hsn_code": "96170011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCENT SPRAYS AND SIMILAR TOILET SPRAYS, AND MOUNTS AND HEADS THEREFOR; POWDER-PUFFS AND PADS FOR THE APPLICATION OF COSMETICS OR TOILET PREPARATIONS",
+        "hsn_code": "9616",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCENT SPRAYS AND SIMILAR TOILET SPRAYS, AND MOUNTS AND HEADS THEREFOR; POWDER\nPUFFS AND PADS FOR THE APPLICATION OF COSMETICS OR TOILET PREPARATIONS Powder-puffs\nand pads for the application of cosmetics or toilet preparations",
+        "hsn_code": "96162000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCENT SPRAYS AND SIMILAR TOILET SPRAYS, AND MOUNTS AND HEADS THEREFOR; POWDER\nPUFFS AND PADS FOR THE APPLICATION OF COSMETICS OR TOILET PREPARATIONS - Scent sprays\nand similar toilet sprays, and mounts and heads therefor : Mounts and heads",
+        "hsn_code": "96161020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCENT SPRAYS AND SIMILAR TOILET SPRAYS, AND MOUNTS AND HEADS THEREFOR; POWDER\nPUFFS AND PADS FOR THE APPLICATION OF COSMETICS OR TOILET PREPARATIONS - Scent sprays\nand similar toilet sprays, and mounts and heads therefor : Scent sprays and similar toilet sprays",
+        "hsn_code": "96161010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMBS, HAIR-SLIDES AND THE LIKE, HAIRPINS, CURLING PINS, CURLING GRIPS, HAIR-CURLERS AND THE LIKE, OTHER THAN THOSE OF HEADING 8516, AND PARTS THEREOF",
+        "hsn_code": "9615",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COMBS, HAIR-SLIDES AND THE LIKE, HAIRPINS, CURLING PINS, CURLING GRIPS, HAIR-CURLERS\nAND THE LIKE, OTHER THAN THOSE OF HEADING 8516, AND PARTS THEREOF Other",
+        "hsn_code": "96159000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COMBS, HAIR-SLIDES AND THE LIKE, HAIRPINS, CURLING PINS, CURLING GRIPS, HAIR-CURLERS\nAND THE LIKE, OTHER THAN THOSE OF HEADING 8516, AND PARTS THEREOF - Combs, hair-slides\nand the like: Other",
+        "hsn_code": "96151900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COMBS, HAIR-SLIDES AND THE LIKE, HAIRPINS, CURLING PINS, CURLING GRIPS, HAIR-CURLERS\nAND THE LIKE, OTHER THAN THOSE OF HEADING 8516, AND PARTS THEREOF - Combs, hair-slides\nand the like: Of hard rubber or plastics",
+        "hsn_code": "96151100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Smoking pipes (including pipe bowls) and cigar or cigarette holders and parts thereof",
+        "hsn_code": "9614",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SMOKING PIPES (INCLUDING PIPE BOWLS) AND CIGAR OR CIGARETTE HOLDERS, AND PARTS\nTHEREOF - OTHER : OTHER",
+        "hsn_code": "96149090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SMOKING PIPES (INCLUDING PIPE BOWLS) AND CIGAR OR CIGARETTE HOLDERS, AND PARTS\nTHEREOF - Other : Hookah of coconut shell",
+        "hsn_code": "96149020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SMOKING PIPES (INCLUDING PIPE BOWLS) AND CIGAR OR CIGARETTE HOLDERS, AND PARTS\nTHEREOF - OTHER : HUBLE AND BUBLE OF BASE METAL",
+        "hsn_code": "96149010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SMOKING PIPES (INCLUDING PIPE BOWLS) AND CIGAR OR CIGARETTE HOLDERS, AND PARTS\nTHEREOF PIPES AND PIPE BOWLS",
+        "hsn_code": "96142000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SMOKING PIPES (INCLUDING PIPE BOWLS) AND CIGAR OR CIGARETTE HOLDERS, AND PARTS\nTHEREOF",
+        "hsn_code": "96140000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "CIGARETTE LIGHTERS AND OTHER LIGHTERS, WHETHER OR NOT MECHANICAL OR ELECTRICAL AND PARTS THEREOF OTHER THAN FLINTS AND WICKS",
+        "hsn_code": "9613",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CIGARETTE LIGHTERS AND OTHER LIGHTERS, WHETHER OR NOT MECHANICAL OR ELECTRICAL,\nAND PARTS THEREOF OTHER THAN FLINTS AND WICKS - Parts",
+        "hsn_code": "96139000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CIGARETTE LIGHTERS AND OTHER LIGHTERS, WHETHER OR NOT MECHANICAL OR ELECTRICAL,\nAND PARTS THEREOF OTHER THAN FLINTS AND WICKS - Other lighters:Other",
+        "hsn_code": "96138090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CIGARETTE LIGHTERS AND OTHER LIGHTERS, WHETHER OR NOT MECHANICAL OR ELECTRICAL,\nAND PARTS THEREOF OTHER THAN FLINTS AND WICKS - Other lighters:Electronic",
+        "hsn_code": "96138010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CIGARETTE LIGHTERS AND OTHER LIGHTERS, WHETHER OR NOT MECHANICAL OR ELECTRICAL,\nAND PARTS THEREOF OTHER THAN FLINTS AND WICKS - Pocket lighters, gas fuelled, refillable",
+        "hsn_code": "96132000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CIGARETTE LIGHTERS AND OTHER LIGHTERS, WHETHER OR NOT MECHANICAL OR ELECTRICAL,\nAND PARTS THEREOF OTHER THAN FLINTS AND WICKS - Pocket lighters, gas fuelled, non\nrefillable",
+        "hsn_code": "96131000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TYPEWRITER OR SIMILAR RIBBONS, INKED OR OTHER WISE PREPARED FOR GIVING IMPRESIONS, WHETHER OR NOT ON SPOOLS OR IN CARTRIDGES; INK-PADS, WHETHER OR NOT INKED, WITH OR WITHOUT BOXES",
+        "hsn_code": "9612",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TYPEWRITER OR SIMILAR RIBBONS, INKED OR OTHERWISE PREPARED FOR GIVING IMPRESSIONS,\nWHETHER OR NOT ON SPOOLS OR IN CARTRIDGES; INK-PADS, WHETHER OR NOT INKED, WITH\nOR WITHOUT BOXES - Ink-pads",
+        "hsn_code": "96122000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TYPEWRITER OR SIMILAR RIBBONS, INKED OR OTHERWISE PREPARED FOR GIVING IMPRESSIONS,\nWHETHER OR NOT ON SPOOLS OR IN CARTRIDGES; INK-PADS, WHETHER OR NOT INKED, WITH\nOR WITHOUT BOXES - Ribbons: Other",
+        "hsn_code": "96121090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TYPEWRITER OR SIMILAR RIBBONS, INKED OR OTHERWISE PREPARED FOR GIVING IMPRESSIONS,\nWHETHER OR NOT ON SPOOLS OR IN CARTRIDGES; INK-PADS, WHETHER OR NOT INKED, WITH\nOR WITHOUT BOXES - Ribbons: Ribbon for electronic typewriter",
+        "hsn_code": "96121030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TYPEWRITER OR SIMILAR RIBBONS, INKED OR OTHERWISE PREPARED FOR GIVING IMPRESSIONS,\nWHETHER OR NOT ON SPOOLS OR IN CARTRIDGES; INK-PADS, WHETHER OR NOT INKED, WITH\nOR WITHOUT BOXES - Ribbons: Ribbon for typewriters, other than electronic and similar\nmachines",
+        "hsn_code": "96121020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TYPEWRITER OR SIMILAR RIBBONS, INKED OR OTHERWISE PREPARED FOR GIVING IMPRESSIONS,\nWHETHER OR NOT ON SPOOLS OR IN CARTRIDGES; INK-PADS, WHETHER OR NOT INKED, WITH\nOR WITHOUT BOXES - Ribbons: Computer printer ribbon",
+        "hsn_code": "96121010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PENCILS (OTHER THAN PENCILS OF HEADING 9608), CRAYONS, PENCIL LEADS, PASTELS, DRAWING CHARCOALS, WRITING OR DRAWING CHALKS AND TAILORS’ CHALKS",
+        "hsn_code": "9611",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DATE, SEALING OR NUMBERING STAMPS, AND THE LIKE (INCLUDING DEVICES FOR PRINTING OR\nEMBOSSING LABELS), DESIGNED FOR OPERATING IN THE HAND; HAND-OPERATED COMPOSING\nSTICKS AND HAND PRINTING SETS INCORPORATING SUCH COMPOSING STICKS",
+        "hsn_code": "96110000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLIDE FASTENERS AND PARTS THEREOF",
+        "hsn_code": "9607",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SLIDE FASTENERS AND PARTS THEREOF Parts",
+        "hsn_code": "96072000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SLIDE FASTENERS AND PARTS THEREOF - Slide fasteners : - Other : Other",
+        "hsn_code": "96071990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SLIDE FASTENERS AND PARTS THEREOF - Slide fasteners : - Other : Zip fasteners",
+        "hsn_code": "96071910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SLIDE FASTENERS AND PARTS THEREOF - Slide fasteners : - Fitted with chain scoops of base metal\n: Other",
+        "hsn_code": "96071190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SLIDE FASTENERS AND PARTS THEREOF - Slide fasteners : - Fitted with chain scoops of base metal\n: Zip fasteners",
+        "hsn_code": "96071110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRAVEL SETS FOR PERSONAL TOILET, SEWING OR SHOE OR CLOTHES CLEANING",
+        "hsn_code": "9605",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRAVEL SETS FOR PERSONAL TOILET, SEWING OR SHOE OR CLOTHES CLEANING - Travel sets for\npersonal toilet, sewing or shoe or clothes cleaning : Other",
+        "hsn_code": "96050090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRAVEL SETS FOR PERSONAL TOILET, SEWING OR SHOE OR CLOTHES CLEANING - Travel sets for\npersonal toilet, sewing or shoe or clothes cleaning : For personal toilet",
+        "hsn_code": "96050010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED VEGETABLE OR MINERAL CARVING MATERIAL AND ARTICLES OF THESE MATERIALS MOULDED OR CARVED ARTICLES OF WAX, OF STEARIN, OF NATURAL GUMS OR NATURAL RESINS OR OF MODELLING PASTES, AND OTHER MOULDED OR CARVED ARTICLES, NOT ELSEWHERE SPECIFIED OR INLCLUDED; WORKED, UNHARDENED GELATIN (EXCEPT GELATIN OF HEADING 3503) AND ARTICLES OF UNHARDENED GELATIN",
+        "hsn_code": "9602",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED VEGETABLE OR MINERAL CARVING MATERIAL AND ARTICLES OF THESE MATERIALS\nMOULDED OR CARVED ARTICLES OF WAX, OF STEARIN, OF NATURAL GUMS OR NATURAL RESINS\nOR OF MODELLING PASTES, AND OTHER MOULDED OR CARVED ARTICLES, NOT ELSEWHERE\nSPECIFIED OR INCLUDED; WORKED, UNHARDENED GELATIN (EXCEPT GELATIN OF HEADING 3503)\nAND ARTICLES OF UNHARDENED GELATIN - Worked vegetable or mineral carving material and\narticles of these materials moulded or carved articles of wax, of stearin, of natural gums or\nnatural resins or of modelling pastes, and other moulded or carved articles, not elsewhere\nspecified or included; worked, unhardened gelatin (except gelatin of heading 3503) and articles\nof unhardened gelatin: Other",
+        "hsn_code": "96020090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED VEGETABLE OR MINERAL CARVING MATERIAL AND ARTICLES OF THESE MATERIALS\nMOULDED OR CARVED ARTICLES OF WAX, OF STEARIN, OF NATURAL GUMS OR NATURAL RESINS\nOR OF MODELLING PASTES, AND OTHER MOULDED OR CARVED ARTICLES, NOT ELSEWHERE\nSPECIFIED OR INCLUDED; WORKED, UNHARDENED GELATIN (EXCEPT GELATIN OF HEADING 3503)\nAND ARTICLES OF UNHARDENED GELATIN - Worked vegetable or mineral carving material and\narticles of these materials moulded or carved articles of wax, of stearin, of natural gums or\nnatural resins or of modelling pastes, and other moulded or carved articles, not elsewhere\nspecified or included; worked, unhardened gelatin (except gelatin of heading 3503) and articles\nof unhardened gelatin: Other articles of unhardened gelatin",
+        "hsn_code": "96020040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED VEGETABLE OR MINERAL CARVING MATERIAL AND ARTICLES OF THESE MATERIALS\nMOULDED OR CARVED ARTICLES OF WAX, OF STEARIN, OF NATURAL GUMS OR NATURAL RESINS\nOR OF MODELLING PASTES, AND OTHER MOULDED OR CARVED ARTICLES, NOT ELSEWHERE\nSPECIFIED OR INCLUDED; WORKED, UNHARDENED GELATIN (EXCEPT GELATIN OF HEADING 3503)\nAND ARTICLES OF UNHARDENED GELATIN - Worked vegetable or mineral carving material and\narticles of these materials moulded or carved articles of wax, of stearin, of natural gums or\nnatural resins or of modelling pastes, and other moulded or carved articles, not elsewhere\nspecified or included; worked, unhardened gelatin (except gelatin of heading 3503) and articles\nof unhardened gelatin: Gelatin capsules, empty",
+        "hsn_code": "96020030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED VEGETABLE OR MINERAL CARVING MATERIAL AND ARTICLES OF THESE MATERIALS\nMOULDED OR CARVED ARTICLES OF WAX, OF STEARIN, OF NATURAL GUMS OR NATURAL RESINS\nOR OF MODELLING PASTES, AND OTHER MOULDED OR CARVED ARTICLES, NOT ELSEWHERE\nSPECIFIED OR INCLUDED; WORKED, UNHARDENED GELATIN (EXCEPT GELATIN OF HEADING 3503)\nAND ARTICLES OF UNHARDENED GELATIN - Worked vegetable or mineral carving material and\narticles of these materials moulded or carved articles of wax, of stearin, of natural gums or\nnatural resins or of modelling pastes, and other moulded or carved articles, not elsewhere\nspecified or included; worked, unhardened gelatin (except gelatin of heading 3503) and articles\nof unhardened gelatin: Moulded or carved articles of wax, stearin, natural gums and resins and\nother moulded or carved articles",
+        "hsn_code": "96020020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED VEGETABLE OR MINERAL CARVING MATERIAL AND ARTICLES OF THESE MATERIALS\nMOULDED OR CARVED ARTICLES OF WAX, OF STEARIN, OF NATURAL GUMS OR NATURAL RESINS\nOR OF MODELLING PASTES, AND OTHER MOULDED OR CARVED ARTICLES, NOT ELSEWHERE\nSPECIFIED OR INCLUDED; WORKED, UNHARDENED GELATIN (EXCEPT GELATIN OF HEADING 3503)\nAND ARTICLES OF UNHARDENED GELATIN - Worked vegetable or mineral carving material and\narticles of these materials moulded or carved articles of wax, of stearin, of natural gums or\nnatural resins or of modelling pastes, and other moulded or carved articles, not elsewhere\nspecified or included; worked, unhardened gelatin (except gelatin of heading 3503) and articles\nof unhardened gelatin: Worked vegetable carving material and articles thereof",
+        "hsn_code": "96020010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED IVORY, BONE, TORTOISE-SHELL, HORN, ANTLERS, CORAL, MOTHER-OF-PEARL AND\nOTHER ANIMAL CARVING MATERIAL, AND ARTICLES OF THESE MATERIALS (INCLUDING ARTICLES\nOBTAINED BY MOULDING) Worked ivory and articles of ivory",
+        "hsn_code": "96011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WORKED IVORY, BONE, TORTOISE-SHELL, HORN, ANTLERS, CORAL, MOTHER-OF-PEARL AND\nOTHER ANIMAL CARVING MATERIAL, AND ARTICLES OF THESE MATERIALS (INCLUDING ARTICLES\nOBTAINED BY MOULDING) - Other : Worked tortoise-shell and articles thereof",
+        "hsn_code": "96019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WORKED IVORY, BONE, TORTOISE-SHELL, HORN, ANTLERS, CORAL, MOTHER-OF-PEARL AND\nOTHER ANIMAL CARVING MATERIAL, AND ARTICLES OF THESE MATERIALS (INCLUDING ARTICLES\nOBTAINED BY MOULDING) - Other : Worked mother-of-pearl and articles thereof",
+        "hsn_code": "96019020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WORKED IVORY, BONE, TORTOISE-SHELL, HORN, ANTLERS, CORAL, MOTHER-OF-PEARL AND\nOTHER ANIMAL CARVING MATERIAL, AND ARTICLES OF THESE MATERIALS (INCLUDING ARTICLES\nOBTAINED BY MOULDING) - Other : Worked bone (excluding whale bone) and articles thereof",
+        "hsn_code": "96019030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WORKED IVORY, BONE, TORTOISE-SHELL, HORN, ANTLERS, CORAL, MOTHER-OF-PEARL AND\nOTHER ANIMAL CARVING MATERIAL, AND ARTICLES OF THESE MATERIALS (INCLUDING ARTICLES\nOBTAINED BY MOULDING) - Other : Worked horn, coral and other animal carving material and\narticles thereof",
+        "hsn_code": "96019040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WORKED IVORY, BONE, TORTOISE-SHELL, HORN, ANTLERS, CORAL, MOTHER-OF-PEARL AND\nOTHER ANIMAL CARVING MATERIAL, AND ARTICLES OF THESE MATERIALS (INCLUDING ARTICLES\nOBTAINED BY MOULDING) - Other : Other",
+        "hsn_code": "96019090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WORKED IVORY, BONE, TORTOISE-SHELL, HORN, ANTLERS, CORAL, MOTHER-OF-PEARL AND OTHER ANIMAL CARVING MATERIAL, AND ARTICLES OF THESE MATERIALS (INCLUDING ARTICLES OBTAINED BY MOULDING)",
+        "hsn_code": "9601",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ROUNDABOUTS, SWINGS, SHOOTING GALLERIES AND OTHER FAIRGROUND AMUSEMENTS; TRAVELING CIRCUSES, TRAVELING MENAGERIES AND TRAVELING THEATRES",
+        "hsn_code": "9508",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ROUNDABOUTS, SWINGS, SHOOTING GALLERIES AND OTHER FAIRGROUND AMUSEMENTS;\nTRAVELLING CIRCUSES, TRAVELLING MENAGERIES AND TRAVELLING THEATRES Other",
+        "hsn_code": "95089000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ROUNDABOUTS, SWINGS, SHOOTING GALLERIES AND OTHER FAIRGROUND AMUSEMENTS;\nTRAVELLING CIRCUSES, TRAVELLING MENAGERIES AND TRAVELLING THEATRES Travelling\ncircuses and travelling menageries",
+        "hsn_code": "95081000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FISHING RODS, FISH-HOOKS AND OTHER LINE FISHING TACKLE; FISH LANDING NETS, BUTTERFLY NETS AND SIMILAR NETS; DECOY “BIRDS” (OTHER THAN THOSE OF HEADING 9208 OR 9705) AND SIMILAR HUNTING OR SHOOTING REQUISITES",
+        "hsn_code": "9507",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FISHING RODS, FISH-HOOKS AND OTHER LINE FISHING TACKLE; FISH LANDING NETS, BUTTERFLY\nNETS AND SIMILAR NETS; DECOY BIRDS (OTHER THAN THOSE OF HEADING 9208 OR 9705) AND\nSIMILAR HUNTING OR SHOOTING REQUISITES - Other : Other",
+        "hsn_code": "95079090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FISHING RODS, FISH-HOOKS AND OTHER LINE FISHING TACKLE; FISH LANDING NETS, BUTTERFLY\nNETS AND SIMILAR NETS; DECOY BIRDS (OTHER THAN THOSE OF HEADING 9208 OR 9705) AND\nSIMILAR HUNTING OR SHOOTING REQUISITES - Other : Fish landing and butterfly nets",
+        "hsn_code": "95079010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FISHING RODS, FISH-HOOKS AND OTHER LINE FISHING TACKLE; FISH LANDING NETS, BUTTERFLY\nNETS AND SIMILAR NETS; DECOY BIRDS (OTHER THAN THOSE OF HEADING 9208 OR 9705) AND\nSIMILAR HUNTING OR SHOOTING REQUISITES Fishing reels",
+        "hsn_code": "95073000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FISHING RODS, FISH-HOOKS AND OTHER LINE FISHING TACKLE; FISH LANDING NETS, BUTTERFLY\nNETS AND SIMILAR NETS; DECOY BIRDS (OTHER THAN THOSE OF HEADING 9208 OR 9705) AND\nSIMILAR HUNTING OR SHOOTING REQUISITES Fish-hooks, whether or not snelled",
+        "hsn_code": "95072000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FISHING RODS, FISH-HOOKS AND OTHER LINE FISHING TACKLE; FISH LANDING NETS, BUTTERFLY\nNETS AND SIMILAR NETS; DECOY BIRDS (OTHER THAN THOSE OF HEADING 9208 OR 9705) AND\nSIMILAR HUNTING OR SHOOTING REQUISITES Fishing rods",
+        "hsn_code": "95071000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER SPORTS (INCLUDING TABLE-TENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS",
+        "hsn_code": "9506",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Other : - Other :\nOther",
+        "hsn_code": "95069990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Other : - Other : Shin\nguards and elbow or shoulders pads excluding those for football; waist, thigh and hip protective\nequipment",
+        "hsn_code": "95069980",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Other : - Other :\nTennis and badminton racket pressures",
+        "hsn_code": "95069970",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Other : - Other :\nSports net",
+        "hsn_code": "95069960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Other : - Other : Polo\nsticks including blades, shafts and heads",
+        "hsn_code": "95069950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Other : - Other :\nHockey sticks and blades",
+        "hsn_code": "95069940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Other : - Other :\nShoulder pads for football",
+        "hsn_code": "95069930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Other : - Other : Leg\npads and bats for cricket",
+        "hsn_code": "95069920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Other : - Other :\nBadminton shuttle cocks",
+        "hsn_code": "95069910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Other : - Articles and\nequipment for general physical exercise, gymnastics or athletics: Other",
+        "hsn_code": "95069190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Other : - Articles and\nequipment for general physical exercise, gymnastics or athletics: Boxing equipment",
+        "hsn_code": "95069110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS Ice skates and roller\nskates, including skating boots with skates attached",
+        "hsn_code": "95067000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Balls, other than golf\nballs and table-tennis balls: - Other : Other",
+        "hsn_code": "95066990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Balls, other than golf\nballs and table-tennis balls: - Other : Rugby ball",
+        "hsn_code": "95066940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Balls, other than golf\nballs and table-tennis balls: - Other : Golf ball",
+        "hsn_code": "95066930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Balls, other than golf\nballs and table-tennis balls: - Other : Cricket ball",
+        "hsn_code": "95066920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Balls, other than golf\nballs and table-tennis balls: - Other : Hockey ball",
+        "hsn_code": "95066910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Balls, other than golf\nballs and table-tennis balls: - Inflatable: Other",
+        "hsn_code": "95066290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Balls, other than golf\nballs and table-tennis balls: - Inflatable: Basket ball",
+        "hsn_code": "95066230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Balls, other than golf\nballs and table-tennis balls: - Inflatable: Volley ball",
+        "hsn_code": "95066220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Balls, other than golf\nballs and table-tennis balls: - Inflatable: Football",
+        "hsn_code": "95066210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Balls, other than golf\nballs and table-tennis balls: Lawn-tennis balls",
+        "hsn_code": "95066100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Tennis, badminton or\nsimilar rackets, whether or not strung: - Other : Other",
+        "hsn_code": "95065990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Tennis, badminton or\nsimilar rackets, whether or not strung: - Other : Squash or racketball badminton rackets, whether\nor not strung",
+        "hsn_code": "95065910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Tennis, badminton or\nsimilar rackets, whether or not strung: Lawn-tennis rackets, whether or not strung",
+        "hsn_code": "95065100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS Articles and\nequipment for table-tennis",
+        "hsn_code": "95064000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Golf clubs and other\ngolf equipment: Other",
+        "hsn_code": "95063900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Golf clubs and other\ngolf equipment: Balls",
+        "hsn_code": "95063200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Golf clubs and other\ngolf equipment: Clubs, complete",
+        "hsn_code": "95063100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Water-skis, surf\nboards, sailboards and other water-sport equipment: Other",
+        "hsn_code": "95062900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Water-skis, surf\nboards, sailboards and other water-sport equipment: Sailboards",
+        "hsn_code": "95062100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Snow-skis and other\nsnow-ski equipment : Other",
+        "hsn_code": "95061900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Snow-skis and other\nsnow-ski equipment : Ski-fastenings (ski-bindings)",
+        "hsn_code": "95061200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES AND EQUIPMENT FOR GENERAL PHYSICAL EXERCISE, GYMNASTICS, ATHLETICS, OTHER\nSPORTS (INCLUDING TABLETENNIS) OR OUT-DOOR GAMES, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; SWIMMING POOLS AND PADDLING POOLS - Snow-skis and other\nsnow-ski equipment : Skis",
+        "hsn_code": "95061100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FESTIVE, CARNIVAL OR OTHER ENTERTAINMENT ARTICLES, INCLUDING CONJURING TRICKS AND NOVELTY JOKES",
+        "hsn_code": "9505",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FESTIVE, CARNIVAL OR OTHER ENTERTAINMENT ARTICLES, INCLUDING CONJURING TRICKS AND\nNOVELTY JOKES - Other : Other",
+        "hsn_code": "95059090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FESTIVE, CARNIVAL OR OTHER ENTERTAINMENT ARTICLES, INCLUDING CONJURING TRICKS AND\nNOVELTY JOKES - Other : Magical equipments",
+        "hsn_code": "95059010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FESTIVE, CARNIVAL OR OTHER ENTERTAINMENT ARTICLES, INCLUDING CONJURING TRICKS AND\nNOVELTY JOKES - Articles for Christmas festivities",
+        "hsn_code": "95051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VIDEO GAME CONSOLES AND MACHINES, ARTICLES FOR FUNFAIR, TABLE OR PARLOUR GAMES, INCLUDING PINTABLES, BILLIARDS, SPECIAL TABLES FOR CASINO GAMES AND AUTOMATIC BOWLING ALLEY EQUIPMENT",
+        "hsn_code": "9504",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR FUNFAIR, TABLE OR PARLOUR GAMES, INCLUDING PINTABLES, BILLIARDS, SPECIAL\nTABLES FOR CASINO GAMES AND AUTOMATIC BOWLING ALLEY EQUIPMENT - Other : Other",
+        "hsn_code": "95049090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CAROM BOARD, WITH OR WITHOUT COINS AND STRIKERS",
+        "hsn_code": "95049020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR FUNFAIR, TABLE OR PARLOUR GAMES, INCLUDING PINTABLES, BILLIARDS, SPECIAL\nTABLES FOR CASINO GAMES AND AUTOMATIC BOWLING ALLEY EQUIPMENT - Other : Chess set,\nall types",
+        "hsn_code": "95049010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VIDEO GAME CONSOLES AND MACHINES OTHER THAN SUB-HEADING 9504 30",
+        "hsn_code": "95045000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR FUNFAIR, TABLE OR PARLOUR GAMES, INCLUDING PINTABLES, BILLIARDS, SPECIAL\nTABLES FOR CASINO GAMES AND AUTOMATIC BOWLING ALLEY EQUIPMENT - PLAYING CARDS",
+        "hsn_code": "95044000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR FUNFAIR, TABLE OR PARLOUR GAMES, INCLUDING PINTABLES, BILLIARDS, SPECIAL\nTABLES FOR CASINO GAMES AND AUTOMATIC BOWLING ALLEY EQUIPMENT - OTHER GAMES,\nOPERATED BY COINS, BANK NOTES, BANK CARDS, TOKENS OR BY OTHER MEANS OF PAYMENT,\nOTHER THAN BOWLING ALLEY EQUIPMENT",
+        "hsn_code": "95043000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES FOR FUNFAIR, TABLE OR PARLOUR GAMES, INCLUDING PINTABLES, BILLIARDS, SPECIAL\nTABLES FOR CASINO GAMES AND AUTOMATIC BOWLING ALLEY EQUIPMENT- ARTICLES AND\nACCESSORIES FOR BILLIARDS OF ALL KINDS",
+        "hsn_code": "95042000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRICYCLES, SCOOTERS, PEDAL CARS AND SIMILAR WHEELED TOYS; DOLLS’ CARRIAGES; DOLLS; OTHER  TOYS; REDUCED-SIZE (SCALE) MODELS AND SIMILAR RECREATIONAL MODELS, WORKING OR NOT; PUZZLES OF ALL KINDS.",
+        "hsn_code": "9503",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS - OTHER: PARTS",
+        "hsn_code": "95039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS - OTHER: OTHER TOYS",
+        "hsn_code": "95039020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS - OTHER: TOY WEAPONS",
+        "hsn_code": "95039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS - OTHER TOYS AND MODELS, INCORPORATING A\nMOTOR: PARTS",
+        "hsn_code": "95038090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS - OTHER TOYS AND MODELS, INCORPORATING A\nMOTOR: TOYS AND MODELS",
+        "hsn_code": "95038010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS - OTHER TOYS, PUT UP IN SETS OR OUTFITS : OTHER",
+        "hsn_code": "95037090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS - OTHER TOYS, PUT UP IN SETS OR OUTFITS : OF\nPLASTICS",
+        "hsn_code": "95037010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS - PUZZLES: OTHER",
+        "hsn_code": "95036090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS - PUZZLES: OF WOOD",
+        "hsn_code": "95036010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS - TOY MUSICAL INSTRUMENTS AND APPARATUS:\nPARTS",
+        "hsn_code": "95035090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS - TOY MUSICAL INSTRUMENTS AND APPARATUS:\nINSTRUMENTS AND APPARATUS",
+        "hsn_code": "95035010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS - TOYS REPRESENTING ANIMALS OR NON-HUMAN\nCREATURES: - OTHER : OTHER",
+        "hsn_code": "95034990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS - TOYS REPRESENTING ANIMALS OR NON-HUMAN\nCREATURES: - OTHER : OF PLASTICS",
+        "hsn_code": "95034930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS - TOYS REPRESENTING ANIMALS OR NON-HUMAN\nCREATURES: - OTHER : OF METAL",
+        "hsn_code": "95034920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TOYS; REDUCED-SIZE (SCALE) MODELS AND SIMILAR RECREATIONAL MODELS, WORKING\nOR NOT; PUZZLES OF ALL KINDS - TOYS REPRESENTING ANIMALS OR NON-HUMAN CREATURES: -\nOTHER : OF WOOD",
+        "hsn_code": "95034910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS - TOYS REPRESENTING ANIMALS OR NON-HUMAN\nCREATURES: STUFFED",
+        "hsn_code": "95034100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS OTHER CONSTRUCTION SETS AND CONSTRUCTIONAL\nTOYS",
+        "hsn_code": "95033000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TOYS; REDUCED-SIZE (SCALE) MODELS AND SIMILAR RECREATIONAL MODELS, WORKING\nOR NOT; PUZZLES OF ALL KINDS REDUCED-SIZE (SCALE) MODEL ASSEMBLY KITS, WHETHER OR\nNOT WORKING MODELS, EXCLUDING THOSE OF SUB-HEADING 9503 10",
+        "hsn_code": "95032000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS ELECTRIC TRAINS, INCLUDING TRACKS, SIGNALS AND\nOTHER ACCESSORIES THEREFORE",
+        "hsn_code": "95031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRICYCLES, SCOOTERS, PEDAL CARS AND SIMILAR WHEELED TOYS; DOLLS CARRIAGES; DOLLS;\nOTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS -TRICYCLES, SCOOTERS, PEDAL CARS AND SIMILAR\nWHEELED TOYS; DOLLS CARRIAGES; DOLLS; OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS\nAND SIMILAR RECREATIONAL MODELS, WORKING OR NOT; PUZZLES OF ALL KINDS --- OTHER",
+        "hsn_code": "95030090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRICYCLES, SCOOTERS, PEDAL CARS AND SIMILAR WHEELED TOYS; DOLLS CARRIAGES; DOLLS;\nOTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS - TRICYCLES, SCOOTERS, PEDAL CARS AND SIMILAR\nWHEELED TOYS; DOLLS CARRIAGES; DOLLS; OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND\nSIMILAR RECREATIONAL MODELS, WORKING OR NOT; PUZZLES OF ALL KINDS --- OF PLASTICS",
+        "hsn_code": "95030030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRICYCLES, SCOOTERS, PEDAL CARS AND SIMILAR WHEELED TOYS; DOLLS CARRIAGES; DOLLS;\nOTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS - TRICYCLES, SCOOTERS, PEDAL CARS AND SIMILAR\nWHEELED TOYS; DOLLS CARRIAGES; DOLLS; OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND\nSIMILAR RECREATIONAL MODELS, WORKING OR NOT; PUZZLES OF ALL KINDS --- OF METAL",
+        "hsn_code": "95030020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRICYCLES, SCOOTERS, PEDAL CARS AND SIMILAR WHEELED TOYS; DOLLS CARRIAGES; DOLLS;\nOTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS AND SIMILAR RECREATIONAL MODELS,\nWORKING OR NOT; PUZZLES OF ALL KINDS - TRICYCLES, SCOOTERS, PEDAL CARS AND SIMILAR\nWHEELED TOYS; DOLLS` CARRIAGES ; DOLLS` OTHER TOYS; REDUCED-SIZE (*SCALE*) MODELS\nAND SIMILAR RECREATIONAL MODELS, WORKING OR NOT; PUZZLES OF ALL KINDS --- OF WOOD",
+        "hsn_code": "95030010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREFABRICATED BUILDINGS",
+        "hsn_code": "9406",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREFABRICATED BUILDINGS - Other : Other",
+        "hsn_code": "94060099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREFABRICATED BUILDINGS - Other : Silos for storing ensilage",
+        "hsn_code": "94060093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREFABRICATED BUILDINGS - Other : Prefabricated construction for cold storage",
+        "hsn_code": "94060092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREFABRICATED BUILDINGS - Other : Prefabricated housing material",
+        "hsn_code": "94060091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREFABRICATED BUILDINGS - Green houses: Other",
+        "hsn_code": "94060019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREFABRICATED BUILDINGS - Green houses: Green house-in ready to assemble sets",
+        "hsn_code": "94060011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS THEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME-PLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "9405",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDED- Illuminated signs, illuminated name-plates and the\nlike : Of other materials",
+        "hsn_code": "94056090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDED- Illuminated signs, illuminated name-plates and the\nlike : Of plastic",
+        "hsn_code": "94056010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDED- Non-electrical lamps and lighting fittings: - Other oil\nlamps: Other",
+        "hsn_code": "94055059",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDED- Non-electrical lamps and lighting fittings: - Other oil\nlamps: Metal",
+        "hsn_code": "94055051",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDED- Non-electrical lamps and lighting fittings: Solar\nlanterns or lamps",
+        "hsn_code": "94055040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDED- Non-electrical lamps and lighting fittings: - Oil\npressure lamps: Other",
+        "hsn_code": "94055039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDED- Non-electrical lamps and lighting fittings: - Oil\npressure lamps: Kerosene pressure lanterns",
+        "hsn_code": "94055031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDED- Non-electrical lamps and lighting fittings: Miner's\nsafety lamps",
+        "hsn_code": "94055020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDED- Non-electrical lamps and lighting fittings: Hurricane\nlanterns",
+        "hsn_code": "94055010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDED - Other electric lamps and lighting fittings: Other",
+        "hsn_code": "94054090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDED - Other electric lamps and lighting fittings:\nSearchlights and sportlights",
+        "hsn_code": "94054010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDEDLighting sets of a kind used for Christmas trees",
+        "hsn_code": "94053000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDED - Electric table, desk, bedside or floor-standing lamps:\nOther",
+        "hsn_code": "94052090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LAMPS AND LIGHTING FITTINGS INCLUDING SEARCHLIGHTS AND SPOTLIGHTS AND PARTS\nTHEREOF, NOT ELSEWHERE SPECIFIED OR INCLUDED; ILLUMINATED SIGNS, ILLUMINATED NAME\nPLATES AND THE LIKE, HAVING A PERMANENTLY FIXED LIGHT SOURCE, AND PARTS THEREOF\nNOT ELSEWHERE SPECIFIED OR INCLUDED - Electric table, desk, bedside or floor-standing lamps:\nTable lamps, complete fittings",
+        "hsn_code": "94052010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MATTRESS SUPPORTS; ARTICLES OF BEDDING AND SIMILAR FURNISHING (FOR EXAMPLE, MATTRESSES, QUILTS, EIDERDOWNS, CUSHIONS, POUFFES AND PILLOWS) FITTED WITH SPRINGS OR STUFFED OR INTERNALLY FITTED WITH ANY MATERIAL OR OF CELLULAR RUBBER OR PLASTICS, WHETHER OR NOT COVERED",
+        "hsn_code": "9404",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MATTRESS SUPPORTS; ARTICLES OF BEDDING AND SIMILAR FURNISHING (FOR EXAMPLE,\nMATTRESSES, QUILTS, EIDERDOWNS, CUSHIONS, POUFFES AND PILLOWS) FITTED WITH SPRINGS\nOR STUFFED OR INTERNALLY FITTED WITH ANY MATERIAL OR OF CELLULAR RUBBER OR\nPLASTICS, WHETHER OR NOT COVERED - Other : - Other : Other",
+        "hsn_code": "94049099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MATTRESS SUPPORTS; ARTICLES OF BEDDING AND SIMILAR FURNISHING (FOR EXAMPLE,\nMATTRESSES, QUILTS, EIDERDOWNS, CUSHIONS, POUFFES AND PILLOWS) FITTED WITH SPRINGS\nOR STUFFED OR INTERNALLY FITTED WITH ANY MATERIAL OR OF CELLULAR RUBBER OR\nPLASTICS, WHETHER OR NOT COVERED - Other : - Other : Filled with feathers or down",
+        "hsn_code": "94049091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MATTRESS SUPPORTS; ARTICLES OF BEDDING AND SIMILAR FURNISHING (FOR EXAMPLE,\nMATTRESSES, QUILTS, EIDERDOWNS, CUSHIONS, POUFFES AND PILLOWS) FITTED WITH SPRINGS\nOR STUFFED OR INTERNALLY FITTED WITH ANY MATERIAL OR OF CELLULAR RUBBER OR\nPLASTICS, WHETHER OR NOT COVERED - Other : - Quilts: Other",
+        "hsn_code": "94049019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MATTRESS SUPPORTS; ARTICLES OF BEDDING AND SIMILAR FURNISHING (FOR EXAMPLE,\nMATTRESSES, QUILTS, EIDERDOWNS, CUSHIONS, POUFFES AND PILLOWS) FITTED WITH SPRINGS\nOR STUFFED OR INTERNALLY FITTED WITH ANY MATERIAL OR OF CELLULAR RUBBER OR\nPLASTICS, WHETHER OR NOT COVERED - Other : - Quilts: Filled with feathers or down",
+        "hsn_code": "94049011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MATTRESS SUPPORTS; ARTICLES OF BEDDING AND SIMILAR FURNISHING (FOR EXAMPLE,\nMATTRESSES, QUILTS, EIDERDOWNS, CUSHIONS, POUFFES AND PILLOWS) FITTED WITH SPRINGS\nOR STUFFED OR INTERNALLY FITTED WITH ANY MATERIAL OR OF CELLULAR RUBBER OR\nPLASTICS, WHETHER OR NOT COVERED - Sleeping bags: Other",
+        "hsn_code": "94043090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MATTRESS SUPPORTS; ARTICLES OF BEDDING AND SIMILAR FURNISHING (FOR EXAMPLE,\nMATTRESSES, QUILTS, EIDERDOWNS, CUSHIONS, POUFFES AND PILLOWS) FITTED WITH SPRINGS\nOR STUFFED OR INTERNALLY FITTED WITH ANY MATERIAL OR OF CELLULAR RUBBER OR\nPLASTICS, WHETHER OR NOT COVERED - Sleeping bags: Filled with feathers or down",
+        "hsn_code": "94043010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MATTRESS SUPPORTS; ARTICLES OF BEDDING AND SIMILAR FURNISHING (FOR EXAMPLE,\nMATTRESSES, QUILTS, EIDERDOWNS, CUSHIONS, POUFFES AND PILLOWS) FITTED WITH SPRINGS\nOR STUFFED OR INTERNALLY FITTED WITH ANY MATERIAL OR OF CELLULAR RUBBER OR\nPLASTICS, WHETHER OR NOT COVERED - Mattresses: Of other materials: other",
+        "hsn_code": "94042990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MATTRESS SUPPORTS;ARTICLES OF BEDDING AND SIMILAR FURNISHING - OF RUBBERISED COIR\nWITH OR WITHOUT COMBINATION OF OTHER MATERIALS, WHETHER OR NOT WITH METALLIC\nSPRINGS",
+        "hsn_code": "94042920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MATTRESS SUPPORTS; ARTICLES OF BEDDING AND SIMILAR FURNISHING (FOR EXAMPLE,\nMATTRESSES, QUILTS, EIDERDOWNS, CUSHIONS, POUFFES AND PILLOWS) FITTED WITH SPRINGS\nOR STUFFED OR INTERNALLY FITTED WITH ANY MATERIAL OR OF CELLULAR RUBBER OR\nPLASTICS, WHETHER OR NOT COVERED - Mattresses: Of other materials: Spring interior",
+        "hsn_code": "94042910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MATTRESS SUPPORTS; ARTICLES OF BEDDING AND SIMILAR FURNISHING (FOR EXAMPLE,\nMATTRESSES, QUILTS, EIDERDOWNS, CUSHIONS, POUFFES AND PILLOWS) FITTED WITH SPRINGS\nOR STUFFED OR INTERNALLY FITTED WITH ANY MATERIAL OR OF CELLULAR RUBBER OR\nPLASTICS, WHETHER OR NOT COVERED - Mattresses: Of cellular rubber or plastics, whether or\nnot covered: Of plastic",
+        "hsn_code": "94042190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MATTRESS SUPPORTS; ARTICLES OF BEDDING AND SIMILAR FURNISHING (FOR EXAMPLE,\nMATTRESSES, QUILTS, EIDERDOWNS, CUSHIONS, POUFFES AND PILLOWS) FITTED WITH SPRINGS\nOR STUFFED OR INTERNALLY FITTED WITH ANY MATERIAL OR OF CELLULAR RUBBER OR\nPLASTICS, WHETHER OR NOT COVERED - Mattresses: Of cellular rubber or plastics, whether or\nnot covered: Of rubber",
+        "hsn_code": "94042110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MATTRESS SUPPORTS; ARTICLES OF BEDDING AND SIMILAR FURNISHING (FOR EXAMPLE,\nMATTRESSES, QUILTS, EIDERDOWNS, CUSHIONS, POUFFES AND PILLOWS) FITTED WITH SPRINGS\nOR STUFFED OR INTERNALLY FITTED WITH ANY MATERIAL OR OF CELLULAR RUBBER OR\nPLASTICS, WHETHER OR NOT COVERED Mattress supports",
+        "hsn_code": "94041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNITURE AND PARTS THEREOF",
+        "hsn_code": "9403",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNITURE AND PARTS THEREOF -Parts",
+        "hsn_code": "94039000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNITURE AND PARTS THEREOF - FURNITURE OF OTHER MATERIALS, INCLUDING CANE,\nOSIER, BAMBOO OR SIMILAR MATERIALS: OTHER",
+        "hsn_code": "94038900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNITURE AND PARTS THEREOF - FURNITURE OF OTHER MATERIALS, INCLUDING CANE,\nOSIER, BAMBOO OR SIMILAR MATERIALS: OF BAMBOO OR RATTAN",
+        "hsn_code": "94038100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNITURE AND PARTS THEREOF -Furniture of plastics",
+        "hsn_code": "94037000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNITURE AND PARTS THEREOF -Other wooden furniture",
+        "hsn_code": "94036000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNITURE AND PARTS THEREOF - - Wooden furniture of a kind used in the bed room:\nOther",
+        "hsn_code": "94035090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNITURE AND PARTS THEREOF - - Wooden furniture of a kind used in the bed room:\nBed stead",
+        "hsn_code": "94035010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNITURE AND PARTS THEREOF - Wooden furniture of a kind used in the kitchen",
+        "hsn_code": "94034000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNITURE AND PARTS THEREOF - Wooden furniture of a kind used in offices: Other",
+        "hsn_code": "94033090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNITURE AND PARTS THEREOF - Wooden furniture of a kind used in\noffices:Cabinetware",
+        "hsn_code": "94033010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNITURE AND PARTS THEREOF - Other metal furniture:Other",
+        "hsn_code": "94032090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNITURE AND PARTS THEREOF - Other metal furniture:Of steel",
+        "hsn_code": "94032010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNITURE AND PARTS THEREOF - Metal furniture of a kind used in offices: Other",
+        "hsn_code": "94031090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FURNITURE AND PARTS THEREOF - Metal furniture of a kind used in offices: Of steel",
+        "hsn_code": "94031010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MEDICAL, SURGICAL, DENTAL OR VETERINARY FURNITURE (FOR EXAMPLE, OPERATING TABLES, EXAMINATION TABLES, HOSPITAL BEDS WITH MECHANICAL FITTINGS, DENTISTS’ CHAIRS); BARBERS’ CHAIRS AND SIMILAR CHAIRS, HAVING ROTATING AS WELL AS BOTH RECLINING AND ELEVATING MOVEMENTS; PARTS OF THE FOREGOING ARTICLES",
+        "hsn_code": "9402",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MEDICAL, SURGICAL, DENTAL OR VETERINARY FURNITURE (FOR EXAMPLE, OPERATING TABLES,\nEXAMINATION TABLES, HOSPITAL BEDS WITH MECHANICAL FITTINGS, DENTISTS' CHAIRS);\nBARBERS' CHAIRS AND SIMILAR CHAIRS, HAVING ROTATING AS WELL AS BOTH RECLINING AND\nELEVATING MOVEMENTS; PARTS OF THE FOREGOING ARTICLES- Other : Other",
+        "hsn_code": "94029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MEDICAL, SURGICAL, DENTAL OR VETERINARY FURNITURE (FOR EXAMPLE, OPERATING TABLES,\nEXAMINATION TABLES, HOSPITAL BEDS WITH MECHANICAL FITTINGS, DENTISTS' CHAIRS);\nBARBERS' CHAIRS AND SIMILAR CHAIRS, HAVING ROTATING AS WELL AS BOTH RECLINING AND\nELEVATING MOVEMENTS; PARTS OF THE FOREGOING ARTICLES- Other : Parts",
+        "hsn_code": "94029020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MEDICAL, SURGICAL, DENTAL OR VETERINARY FURNITURE (FOR EXAMPLE, OPERATING TABLES,\nEXAMINATION TABLES, HOSPITAL BEDS WITH MECHANICAL FITTINGS, DENTISTS' CHAIRS);\nBARBERS' CHAIRS AND SIMILAR CHAIRS, HAVING ROTATING AS WELL AS BOTH RECLINING AND\nELEVATING MOVEMENTS; PARTS OF THE FOREGOING ARTICLES- Other : Hospital beds with\nmechanical fittings",
+        "hsn_code": "94029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MEDICAL, SURGICAL, DENTAL OR VETERINARY FURNITURE (FOR EXAMPLE, OPERATING TABLES,\nEXAMINATION TABLES, HOSPITAL BEDS WITH MECHANICAL FITTINGS, DENTISTS' CHAIRS);\nBARBERS' CHAIRS AND SIMILAR CHAIRS, HAVING ROTATING AS WELL AS BOTH RECLINING AND\nELEVATING MOVEMENTS; PARTS OF THE FOREGOING ARTICLES - Dentists', barbers' or similar\nchairs and parts thereof: Other",
+        "hsn_code": "94021090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MEDICAL, SURGICAL, DENTAL OR VETERINARY FURNITURE (FOR EXAMPLE, OPERATING TABLES,\nEXAMINATION TABLES, HOSPITAL BEDS WITH MECHANICAL FITTINGS, DENTISTS' CHAIRS);\nBARBERS' CHAIRS AND SIMILAR CHAIRS, HAVING ROTATING AS WELL AS BOTH RECLINING AND\nELEVATING MOVEMENTS; PARTS OF THE FOREGOING ARTICLES - Dentists', barbers' or similar\nchairs and parts thereof: Dentists' chairs and parts thereof",
+        "hsn_code": "94021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SWORDS, CUT LASSES, BAYONETS, LANCES AND SIMILAR ARMS AND PARTS THEREOF AND SCABBARDS AND SHEATHS THEREOF",
+        "hsn_code": "9307",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SWORDS, CUT LASSES, BAYONETS, LANCES AND SIMILAR ARMS AND PARTS THEREOF AND\nSCABBARDS AND SHEATHS THEREFOR",
+        "hsn_code": "93070000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BOMBS, GRENADES, TORPEDOES, MINES, MISSILES AND SIMILAR MUNITIONS OF WAR AND PARTS THEREOF; CARTRIDGES AND OTHER AMMUNITION AND PROJECTILES AND PARTS THEREOF, INCLUDING SHOT AND CARTRIDGE WADS",
+        "hsn_code": "9306",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BOMBS , GRENADES, TORPEDOES, MINES, MISSILES, AND SIMILAR MUNITIONS OF WAR AND\nPARTS THEREOF; CARTRIDGES AND OTHER AMMUNITION AND PROJECTILES AND PARTS\nTHEREOF, INCLUDING SHOT AND CARTRIDGE WADS Other",
+        "hsn_code": "93069000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BOMBS , GRENADES, TORPEDOES, MINES, MISSILES, AND SIMILAR MUNITIONS OF WAR AND\nPARTS THEREOF; CARTRIDGES AND OTHER AMMUNITION AND PROJECTILES AND PARTS\nTHEREOF, INCLUDING SHOT AND CARTRIDGE WADS Other cartridges and parts thereof",
+        "hsn_code": "93063000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BOMBS , GRENADES, TORPEDOES, MINES, MISSILES, AND SIMILAR MUNITIONS OF WAR AND\nPARTS THEREOF; CARTRIDGES AND OTHER AMMUNITION AND PROJECTILES AND PARTS\nTHEREOF, INCLUDING SHOT AND CARTRIDGE WADS - Shotgun cartridges and parts thereof; air\ngun pellets: Other",
+        "hsn_code": "93062900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BOMBS , GRENADES, TORPEDOES, MINES, MISSILES, AND SIMILAR MUNITIONS OF WAR AND\nPARTS THEREOF; CARTRIDGES AND OTHER AMMUNITION AND PROJECTILES AND PARTS\nTHEREOF, INCLUDING SHOT AND CARTRIDGE WADS - Shotgun cartridges and parts thereof; air\ngun pellets: Cartridges",
+        "hsn_code": "93062100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BOMBS , GRENADES, TORPEDOES, MINES, MISSILES, AND SIMILAR MUNITIONS OF WAR AND\nPARTS THEREOF; CARTRIDGES AND OTHER AMMUNITION AND PROJECTILES AND PARTS\nTHEREOF, INCLUDING SHOT AND CARTRIDGE WADS CARTRIDGES FOR RIVETING OR SIMILAR\nTOOLS OR FOR CAPTIVE-BOLT HUMANE KILLERS AND PARTS THEREOF",
+        "hsn_code": "93061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF ARTICLES OF HEADINGS 9301 TO 9304",
+        "hsn_code": "9305",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF ARTICLES OF HEADINGS 9301 TO 9304 - Other : Other",
+        "hsn_code": "93059900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF ARTICLES OF HEADINGS 9301 TO 9304 - Other : Of military weapons\nof heading 9301",
+        "hsn_code": "93059100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "93052090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHOTGUN BARRELS",
+        "hsn_code": "93052010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF ARTICLES OF HEADINGS 9301 TO 9304 Of revolvers or pistols",
+        "hsn_code": "93051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Other Arms (for example, spring, air  or gas guns and pistols,truncheons), excluding those ofheading 9307",
+        "hsn_code": "9304",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARMS (FOR EXAMPLE, SPRING, AIR OR GAS GUNS AND PISTOLS, TRUNCHEONS),\nEXCLUDING THOSE OF HEADING 9307",
+        "hsn_code": "93040000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIREARMS AND SIMILAR DEVICES WHICH OPERATE BY THE FIRING OF AN EXPLOSIVE CHARGE (FOR EXAMPLE, SPORTING SHORTGUNS AND RIFLES, MUZZLE-LOADING FIREARMS, VERY PISTOLS AND OTHER DEVICES DESIGNED TO PROJECT ONLY SIGNAL FLARES, PISTOLS AND REVOLVERS FOR FIRING BLANK AMMUNITION, CAPTIVE-BOLT HUMANE KILLERS, LINE-THROWING GUNS)",
+        "hsn_code": "9303",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIREARMS AND SIMILAR DEVICES WHICH OPERATE BY THE FIRING OF AN EXPLOSIVE\nCHARGE (FOR EXAMPLE, SPORTING SHOTGUNS AND RIFLES, MUZZLE-LOADING FIREARMS, VERY\nPISTOLS AND OTHER DEVICES DESIGNED TO PROJECT ONLY SIGNAL FLARES, PISTOLS AND\nREVOLVERS FOR FIRING BLANK AMMUNITION, CAPTIVE-BOLT HUMANE KILLERS, LINE\nTHROWING GUNS) Other",
+        "hsn_code": "93039000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIREARMS AND SIMILAR DEVICES WHICH OPERATE BY THE FIRING OF AN EXPLOSIVE\nCHARGE (FOR EXAMPLE, SPORTING SHOTGUNS AND RIFLES, MUZZLE-LOADING FIREARMS, VERY\nPISTOLS AND OTHER DEVICES DESIGNED TO PROJECT ONLY SIGNAL FLARES, PISTOLS AND\nREVOLVERS FOR FIRING BLANK AMMUNITION, CAPTIVE-BOLT HUMANE KILLERS, LINE\nTHROWING GUNS) Other sporting, hunting or target-shooting rifles",
+        "hsn_code": "93033000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIREARMS AND SIMILAR DEVICES WHICH OPERATE BY THE FIRING OF AN EXPLOSIVE\nCHARGE (FOR EXAMPLE, SPORTING SHOTGUNS AND RIFLES, MUZZLE-LOADING FIREARMS, VERY\nPISTOLS AND OTHER DEVICES DESIGNED TO PROJECT ONLY SIGNAL FLARES, PISTOLS AND\nREVOLVERS FOR FIRING BLANK AMMUNITION, CAPTIVE-BOLT HUMANE KILLERS, LINE\nTHROWING GUNS) Other sporting, hunting or target-shooting shotguns, including combination\nshotgun-rifles",
+        "hsn_code": "93032000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIREARMS AND SIMILAR DEVICES WHICH OPERATE BY THE FIRING OF AN EXPLOSIVE\nCHARGE (FOR EXAMPLE, SPORTING SHOTGUNS AND RIFLES, MUZZLE-LOADING FIREARMS, VERY\nPISTOLS AND OTHER DEVICES DESIGNED TO PROJECT ONLY SIGNAL FLARES, PISTOLS AND\nREVOLVERS FOR FIRING BLANK AMMUNITION, CAPTIVE-BOLT HUMANE KILLERS, LINE\nTHROWING GUNS) Muzzle-loading firearms",
+        "hsn_code": "93031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Revolvers and Pistols, other than those of heading 9303 or 9304",
+        "hsn_code": "9302",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "REVOLVERS AND PISTOLS, OTHER THAN THOSE OF HEADING 9303 OR 9304",
+        "hsn_code": "93020000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MILITARY WEAPONS, OTHER THAN REVOLVERS, PISTOLS AND THE ARMS OF HEADING 9307",
+        "hsn_code": "9301",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MILITARY WEAPONS, OTHER THAN REVOLVERS, PISTOLS AND THE ARMS OF HEADING 9307 -\nOther",
+        "hsn_code": "93019000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MILITARY WEAPONS, OTHER THAN REVOLVERS, PISTOLS AND THE ARMS OF HEADING 9307 -\nRocket launchers; flame-throwers; grenade launchers; torpedo tubes and similar projectors",
+        "hsn_code": "93012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "93011090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF PROPELLED",
+        "hsn_code": "93011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS (FOR EXAMPLE, MECHANISMS FOR MUSICAL BOXES) AND ACCESSORIES (FOR EXAMPLE, CARDS, DISCS AND ROLLS FOR MECHANICAL INSTRUMENTS) OF MUSICAL INSTRUMENTS; METRONOMES, TUNING FORKS AND PITCH PIPES OF ALL KINDS",
+        "hsn_code": "9209",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS (FOR EXAMPLE, MECHANISMS FOR MUSICAL BOXES) AND ACCESSORIES (FOR EXAMPLE,\nCARDS, DISCS AND ROLLS FOR MECHANICAL INSTRUMENTS) OF MUSICAL INSTRUMENTS;\nMETRONOMES, TUNING FORKS AND PITCH PIPES OF ALL KINDS- Other: Other",
+        "hsn_code": "92099900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS (FOR EXAMPLE, MECHANISMS FOR MUSICAL BOXES) AND ACCESSORIES (FOR EXAMPLE,\nCARDS, DISCS AND ROLLS FOR MECHANICAL INSTRUMENTS) OF MUSICAL INSTRUMENTS;\nMETRONOMES, TUNING FORKS AND PITCH PIPES OF ALL KINDS- Other: Parts and accessories for\nthe musical instruments of heading 9207",
+        "hsn_code": "92099400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS (FOR EXAMPLE, MECHANISMS FOR MUSICAL BOXES) AND ACCESSORIES (FOR EXAMPLE,\nCARDS, DISCS AND ROLLS FOR MECHANICAL INSTRUMENTS) OF MUSICAL INSTRUMENTS;\nMETRONOMES, TUNING FORKS AND PITCH PIPES OF ALL KINDS- OTHER: PARTS AND\nACCESSORIES FOR THE MUSICAL INSTRUMENTS OF HEADING 9203",
+        "hsn_code": "92099300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS (FOR EXAMPLE, MECHANISMS FOR MUSICAL BOXES) AND ACCESSORIES (FOR EXAMPLE,\nCARDS, DISCS AND ROLLS FOR MECHANICAL INSTRUMENTS) OF MUSICAL INSTRUMENTS;\nMETRONOMES, TUNING FORKS AND PITCH PIPES OF ALL KINDS- Other: Parts and accessories for\nthe musical instruments of heading 9202",
+        "hsn_code": "92099200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS (FOR EXAMPLE, MECHANISMS FOR MUSICAL BOXES) AND ACCESSORIES (FOR EXAMPLE,\nCARDS, DISCS AND ROLLS FOR MECHANICAL INSTRUMENTS) OF MUSICAL INSTRUMENTS;\nMETRONOMES, TUNING FORKS AND PITCH PIPES OF ALL KINDS- Other: Parts and accessories for\npianos",
+        "hsn_code": "92099100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS (FOR EXAMPLE, MECHANISMS FOR MUSICAL BOXES) AND ACCESSORIES (FOR EXAMPLE,\nCARDS, DISCS AND ROLLS FOR MECHANICAL INSTRUMENTS) OF MUSICAL INSTRUMENTS;\nMETRONOMES, TUNING FORKS AND PITCH PIPES OF ALL KINDSMusical instrument strings",
+        "hsn_code": "92093000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS (FOR EXAMPLE, MECHANISMS FOR MUSICAL BOXES) AND ACCESSORIES (FOR EXAMPLE,\nCARDS, DISCS AND ROLLS FOR MECHANICAL INSTRUMENTS) OF MUSICAL INSTRUMENTS;\nMETRONOMES, TUNING FORKS AND PITCH PIPES OF ALL KINDSMECHANISMS FOR MUSICAL\nBOXES",
+        "hsn_code": "92092000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS (FOR EXAMPLE, MECHANISMS FOR MUSICAL BOXES) AND ACCESSORIES (FOR EXAMPLE,\nCARDS, DISCS AND ROLLS FOR MECHANICAL INSTRUMENTS) OF MUSICAL INSTRUMENTS;\nMETRONOMES, TUNING FORKS AND PITCH PIPES OF ALL KINDSMETRONOMES, TUNING FORKS\nAND PITCH PIPES",
+        "hsn_code": "92091000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MUSICAL BOXES, FAIRGROUND ORGANS, MECHANICAL STREET ORGANS, MECHANICAL SINGING BIRDS, MUSICAL SAWS AND OTHER MUSICAL INSTRUMENTS NOT FALLING WITHIN ANY OTHER HEADING OF THIS CHAPTER; DECOY CALLS OF ALL KINDS; WHISTLES, CALL HORNS AND OTHER MOUTH-BLOWN SOUND SIGNALLING INSTRUMENTS",
+        "hsn_code": "9208",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MUSICAL BOXES, FAIRGROUND ORGANS, MECHANICAL STREET ORGANS, MECHANICAL SINGING\nBIRDS, MUSICAL SAWS AND OTHER MUSICAL INSTRUMENTS NOT FALLING WITHIN ANY OTHER\nHEADING OF THIS CHAPTER; DECOY CALLS OF ALL KINDS; WHISTLES, CALL HORNS AND OTHER\nMOUTH-BLOWN SOUND SIGNALLING INSTRUMENTS OTHER",
+        "hsn_code": "92089000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MUSICAL BOXES, FAIRGROUND ORGANS, MECHANICAL STREET ORGANS, MECHANICAL SINGING\nBIRDS, MUSICAL SAWS AND OTHER MUSICAL INSTRUMENTS NOT FALLING WITHIN ANY OTHER\nHEADING OF THIS CHAPTER; DECOY CALLS OF ALL KINDS; WHISTLES, CALL HORNS AND OTHER\nMOUTH-BLOWN SOUND SIGNALLING INSTRUMENTS OTHER",
+        "hsn_code": "92082000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MUSICAL BOXES, FAIRGROUND ORGANS, MECHANICAL STREET ORGANS, MECHANICAL SINGING\nBIRDS, MUSICAL SAWS AND OTHER MUSICAL INSTRUMENTS NOT FALLING WITHIN ANY OTHER\nHEADING OF THIS CHAPTER; DECOY CALLS OF ALL KINDS; WHISTLES, CALL HORNS AND OTHER\nMOUTH-BLOWN SOUND SIGNALLING INSTRUMENTS Musical boxes",
+        "hsn_code": "92081000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MUSICAL INSTRUMENTS, THE SOUND OF WHICH IS PRODUCED, OR MUST BE AMPLIFIED, ELECTRICALLY (FOR EXAMPLE, ORGANS, GUITARS, ACCORDIONS)",
+        "hsn_code": "9207",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MUSICAL INSTRUMENTS, THE SOUND OF WHICH IS PRODUCED, OR MUST BE AMPLIFIED,\nELECTRICALLY (FOR EXAMPLE, ORGANS, GUITARS, ACCORDIONS)Other",
+        "hsn_code": "92079000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MUSICAL INSTRUMENTS, THE SOUND OF WHICH IS PRODUCED, OR MUST BE AMPLIFIED,\nELECTRICALLY (FOR EXAMPLE, ORGANS, GUITARS, ACCORDIONS)Keyboard instruments, other\nthan accordions",
+        "hsn_code": "92071000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIND MUSICAL INSTRUMENTS (FOR EXAMPLE, KEYBOARD PIPE ORGANS, ACCORDIONS, CLARINETS, TRUMPETS, BAGPIPES) OTHER THAN FAIRGROUND ORGANS AND MECHANICAL STREET ORGANS",
+        "hsn_code": "9205",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER WIND MUSICAL INSTRUMENTS (FOR EXAMPLE, CLARINETS, TRUMPETS, BAGPIPES) -\nOther: Other",
+        "hsn_code": "92059090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER WIND MUSICAL INSTRUMENTS (FOR EXAMPLE, CLARINETS, TRUMPETS, BAGPIPES) -\nOther: Clarinets",
+        "hsn_code": "92059020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER WIND MUSICAL INSTRUMENTS (FOR EXAMPLE, CLARINETS, TRUMPETS, BAGPIPES) -\nOther: Flutes",
+        "hsn_code": "92059010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER WIND MUSICAL INSTRUMENTS (FOR EXAMPLE, CLARINETS, TRUMPETS, BAGPIPES) Brass\nwind instruments",
+        "hsn_code": "92051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER STRING MUSICAL INSTRUMENTS (FOR EXAMPLE, GUITARS, VIOLINS, HARPS)",
+        "hsn_code": "9202",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER STRING MUSICAL INSTRUMENTS (FOR EXAMPLE, GUITARS, VIOLINS, HARPS) Other",
+        "hsn_code": "92029000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER STRING MUSICAL INSTRUMENTS (FOR EXAMPLE, GUITARS, VIOLINS, HARPS) Played with a\nbow",
+        "hsn_code": "92021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PIANOS, INCLUDING AUTOMATIC PIANOS; HARPSI-CHORDS AND OTHER KEYBOARD STRINGED INSTRUMENTS",
+        "hsn_code": "9201",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PIANOS, INCLUDING AUTOMATIC PIANOS; HARPSI-CHORDS AND OTHER KEYBOARD STRINGED\nINSTRUMENTS Other",
+        "hsn_code": "92019000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PIANOS, INCLUDING AUTOMATIC PIANOS; HARPSI-CHORDS AND OTHER KEYBOARD STRINGED\nINSTRUMENTS Grand pianos",
+        "hsn_code": "92012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PIANOS, INCLUDING AUTOMATIC PIANOS; HARPSI-CHORDS AND OTHER KEYBOARD STRINGED\nINSTRUMENTS Upright pianos",
+        "hsn_code": "92011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLOCK OR WATCH PARTS",
+        "hsn_code": "9114",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOR CLOCKS",
+        "hsn_code": "91149092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOR WATCHES",
+        "hsn_code": "91149091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "JEWELS",
+        "hsn_code": "91149030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLOCK OR WATCH PARTS- PLATES AND BRIDGES:FOR CLOCKS",
+        "hsn_code": "91144020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLOCK OR WATCH PARTS- PLATES AND BRIDGES:FOR WATCHES",
+        "hsn_code": "91144010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLOCK OR WATCH PARTS- DIALS:FOR CLOCKS",
+        "hsn_code": "91143020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLOCK OR WATCH PARTS- DIALS:FOR WATCHES",
+        "hsn_code": "91143010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLOCK OR WATCH PARTS SPRINGS, INCLUDING HAIR-SPRINGS:FOR CLOCKS",
+        "hsn_code": "91141020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLOCK OR WATCH PARTS SPRINGS, INCLUDING HAIR-SPRINGS:FOR WATCHES",
+        "hsn_code": "91141010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATCH STRAPS, WATCH BANDS AND WATCH BRACELETS, AND PARTS THEREOF",
+        "hsn_code": "9113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATCH STRAPS, WATCH BANDS AND WATCH BRACELETS, AND PARTS THEREOF - OTHER: OTHER",
+        "hsn_code": "91139090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATCH STRAPS, WATCH BANDS AND WATCH BRACELETS, AND PARTS THEREOF - OTHER: PARTS",
+        "hsn_code": "91139010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATCH STRAPS, WATCH BANDS AND WATCH BRACELETS, AND PARTS THEREOF OF BASE METAL,\nWHETHER OR NOT GOLD- OR SILVERPLATED:OTHER",
+        "hsn_code": "91132090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATCH STRAPS, WATCH BANDS AND WATCH BRACELETS, AND PARTS THEREOF OF BASE METAL,\nWHETHER OR NOT GOLD- OR SILVERPLATED:PARTS",
+        "hsn_code": "91132010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATCH STRAPS, WATCH BANDS AND WATCH BRACELETS, AND PARTS THEREOF OF PRECIOUS\nMETAL OR OF METAL CLAD WITH PRECIOUS METAL",
+        "hsn_code": "91131000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOCK CASES AND CASES OF A SIMILAR TYPE FOR OTHER GOODS OF THIS CHAPTER, AND PARTS THEREOF",
+        "hsn_code": "9112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOCK CASES AND CASES OF A SIMILAR TYPE FOR OTHER GOODS OF THIS CHAPTER, AND PARTS\nTHEREOF PARTS",
+        "hsn_code": "91129000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOCK CASES AND CASES OF A SIMILAR TYPE FOR OTHER GOODS OF THIS CHAPTER, AND PARTS\nTHEREOF CASES",
+        "hsn_code": "91122000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATCH CASES AND PARTS THEREOF",
+        "hsn_code": "9111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS",
+        "hsn_code": "91119000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATCH CASES AND PARTS THEREOF WATCH CASES AND PARTS THEREOF OTHER CASES",
+        "hsn_code": "91118000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATCH CASES AND PARTS THEREOF CASES OF BASE METAL, WHETHER OR NOT GOLD- OR SILVER\nPLATED",
+        "hsn_code": "91112000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATCH CASES AND PARTS THEREOF CASES OF PRECIOUS METAL OR OF METAL CLAD WITH\nPRECIOUS METAL",
+        "hsn_code": "91111000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPLETE WATCH OR CLOCK MOVEMENTS, UNASSEMBLED OR PARTLY ASSEMBLED (MOVEMENT SETS); INCOMPLETE WATCH OR CLOCK MOVEMENTS, ASSEMBLED; ROUGH WATCH OR CLOCK MOVEMENTS",
+        "hsn_code": "9110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPLETE WATCH OR CLOCK MOVEMENTS, UNASSEMBLED OR PARTLY ASSEMBLED\n(MOVEMENT SETS); INCOMPLETE WATCH OR CLOCK MOVEMENTS, ASSEMBLED; ROUGH WATCH\nOR CLOCK MOVEMENTS - OTHER",
+        "hsn_code": "91109000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPLETE WATCH OR CLOCK MOVEMENTS, UNASSEMBLED OR PARTLY ASSEMBLED\n(MOVEMENT SETS); INCOMPLETE WATCH OR CLOCK MOVEMENTS, ASSEMBLED; ROUGH WATCH\nOR CLOCK MOVEMENTS - OF WATCHES: ROUGH MOVEMENTS",
+        "hsn_code": "91101900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPLETE WATCH OR CLOCK MOVEMENTS, UNASSEMBLED OR PARTLY ASSEMBLED\n(MOVEMENT SETS); INCOMPLETE WATCH OR CLOCK MOVEMENTS, ASSEMBLED; ROUGH WATCH\nOR CLOCK MOVEMENTS - OF WATCHES: INCOMPLETE MOVEMENTS, ASSEMBLED",
+        "hsn_code": "91101200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPLETE WATCH OR CLOCK MOVEMENTS, UNASSEMBLED OR PARTLY ASSEMBLED\n(MOVEMENT SETS); INCOMPLETE WATCH OR CLOCK MOVEMENTS, ASSEMBLED; ROUGH WATCH\nOR CLOCK MOVEMENTS - OF WATCHES: COMPLETE MOVEMENTS, UNASSEMBLED OR PARTLY\nASSEMBLED (MOVEMENT SETS)",
+        "hsn_code": "91101100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOCK MOVEMENTS, COMPLETE AND ASSEMBLED",
+        "hsn_code": "9109",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOCK MOVEMENTS, COMPLETE AND ASSEMBLED - OTHER",
+        "hsn_code": "91099000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOCK MOVEMENTS, COMPLETE AND ASSEMBLED - ELECTRICALLY OPERATED ---OTHER",
+        "hsn_code": "91091090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "of alarm clocks",
+        "hsn_code": "91091010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nOther spices : Other : Other",
+        "hsn_code": "9109990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nOther spices : Other : Husk : Other",
+        "hsn_code": "9109939",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nOther spices : Other : Powder : Other",
+        "hsn_code": "9109929",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nOther spices : Other : Powder : Mustard",
+        "hsn_code": "9109927",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nOther spices : Other : Powder : Poppy",
+        "hsn_code": "9109926",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nOther spices : Other : Powder : Dill",
+        "hsn_code": "9109925",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nOther spices : Other : Powder : Fenugreek",
+        "hsn_code": "9109924",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nOther spices : Other : Powder : Celery",
+        "hsn_code": "9109923",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nOther spices : Other : Powder : Cassia",
+        "hsn_code": "9109921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nOther spices : Other : Seed : Other",
+        "hsn_code": "9109919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nOther spices : Other : Seed : Cassia torea",
+        "hsn_code": "9109915",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nOther spices : Other : Seed : Ajwain",
+        "hsn_code": "9109914",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nOther spices : Other : Seed : Dill",
+        "hsn_code": "9109913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nOther spices : Other : Seed : Fenugreek",
+        "hsn_code": "9109912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nOther spices : Other : Seed : Celery",
+        "hsn_code": "9109911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nOther spices : Mixtures referred to in Note 1(b) to this Chapter",
+        "hsn_code": "9109100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATCH MOVEMENTS, COMPLETE AND ASSEMBLED",
+        "hsn_code": "9108",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATCH MOVEMENTS, COMPLETE AND ASSEMBLED OTHER",
+        "hsn_code": "91089000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATCH MOVEMENTS, COMPLETE AND ASSEMBLED WITH AUTOMATIC WINDING",
+        "hsn_code": "91082000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATCH MOVEMENTS, COMPLETE AND ASSEMBLED - ELECTRICALLY OPERATED: OTHER",
+        "hsn_code": "91081900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATCH MOVEMENTS, COMPLETE AND ASSEMBLED - ELECTRICALLY OPERATED: WITH OPTO\nELECTRONIC DISPLAY ONLY",
+        "hsn_code": "91081200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATCH MOVEMENTS, COMPLETE AND ASSEMBLED - ELECTRICALLY OPERATED: WITH\nMECHANICAL DISPLAY ONLY OR WITH A DEVICE TO WHICH A MECHANICAL DISPLAY CAN BE\nINCORPORATED",
+        "hsn_code": "91081100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Time switches with clock or watch movement or with synchronous motor",
+        "hsn_code": "9107",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TIME SWITCHES WITH CLOCK OR WATCH MOVEMENT OR WITH SYNCHRONOUS MOTOR",
+        "hsn_code": "91070000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TIME OF DAY RECORDING APPARATUS AND APPARATUS FOR MEASURING, RECORDING OR OTHERWISE INDICATING INTERVALS OF TIME, WITH CLOCK OR WATCH MOVEMENT OR WITH SYNCHRONOUS MOTOR (FOR EXAMPLE, TIME-REGISTERS, TIME-RECORDERS)",
+        "hsn_code": "9106",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TIME OF DAY RECORDING APPARATUS AND APPARATUS FOR MEASURING, RECORDING OR\nOTHERWISE INDICATING INTERVALS OF TIME, WITH CLOCK OR WATCH MOVEMENT OR WITH\nSYNCHRONOUS MOTOR (FOR EXAMPLE, TIME-REGISTERS, TIMERECORDERS) OTHER",
+        "hsn_code": "91069000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TIME OF DAY RECORDING APPARATUS AND APPARATUS FOR MEASURING, RECORDING OR\nOTHERWISE INDICATING INTERVALS OF TIME, WITH CLOCK OR WATCH MOVEMENT OR WITH\nSYNCHRONOUS MOTOR (FOR EXAMPLE, TIME-REGISTERS, TIMERECORDERS) PARKING METERS",
+        "hsn_code": "91062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TIME OF DAY RECORDING APPARATUS AND APPARATUS FOR MEASURING, RECORDING OR\nOTHERWISE INDICATING INTERVALS OF TIME, WITH CLOCK OR WATCH MOVEMENT OR WITH\nSYNCHRONOUS MOTOR (FOR EXAMPLE, TIME-REGISTERS, TIMERECORDERS) TIME-REGISTERS;\nTIME-RECORDERS",
+        "hsn_code": "91061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLOCKS",
+        "hsn_code": "9105",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLOCKS - OTHER: OTHER: OTHER",
+        "hsn_code": "91059990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLOCKS - OTHER: OTHER: TIME PIECES",
+        "hsn_code": "91059910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLOCKS - OTHER: ELECTRICALLY OPERATED",
+        "hsn_code": "91059100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLOCKS - WALL CLOCKS: OTHER",
+        "hsn_code": "91052900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLOCKS - WALL CLOCKS: ELECTRICALLY OPERATED",
+        "hsn_code": "91052100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLOCKS - ALARM CLOCKS: OTHER",
+        "hsn_code": "91051900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLOCKS - ALARM CLOCKS: ELECTRICALLY OPERATED",
+        "hsn_code": "91051100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "other",
+        "hsn_code": "91051090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nCURRY",
+        "hsn_code": "9105000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Instrument panel clocks and clocks of a similar type for vehicles, aircraft, spacecraft or vessels",
+        "hsn_code": "9104",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENT PANEL CLOCKS AND CLOCKS OF A SIMILAR TYPE FOR VEHICLES, AIRCRAFT,\nSPACECRAFT OR VESSELS",
+        "hsn_code": "91040000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nTHYME; BAY LEAVES : BAY LEAVES, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "9104030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nTHYME; BAY LEAVES : THYMES, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "9104020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nTHYME; BAY `LEAVES : TEJPAT (LEAVES OF CASSIA LIGNEA)",
+        "hsn_code": "9104010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOCKS WITH WATCH MOVEMENTS, EXCLUDING CLOCKS OF HEADING 9104",
+        "hsn_code": "9103",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOCKS WITH WATCH MOVEMENTS, EXCLUDING CLOCKS OF HEADING 9104 - OTHER",
+        "hsn_code": "91039000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOCKS WITH WATCH MOVEMENTS, EXCLUDING CLOCKS OF HEADING 9104 - ELECTRICALLY\nOPERATED",
+        "hsn_code": "91031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nTurmeric (Curcuma) : Other",
+        "hsn_code": "9103090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nTurmeric (Curcuma) : Powder",
+        "hsn_code": "9103030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nTurmeric (Curcuma) : Dried",
+        "hsn_code": "9103020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nTurmeric (Curcuma) : Fresh",
+        "hsn_code": "9103010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP WATCHES, OTHER THAN THOSE OF HEADING 9101",
+        "hsn_code": "9102",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP WATCHES, OTHER\nTHAN THOSE OF HEADING - OTHER - OTHER: OTHER",
+        "hsn_code": "91029990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP WATCHES, OTHER\nTHAN THOSE OF HEADING - OTHER - OTHER: STOP WATCHES",
+        "hsn_code": "91029920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP WATCHES, OTHER\nTHAN THOSE OF HEADING - OTHER - OTHER: POCKET WATCHES",
+        "hsn_code": "91029910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP WATCHES, OTHER\nTHAN THOSE OF HEADING - OTHER: ELECTRICALLY OPERATED: OTHER",
+        "hsn_code": "91029190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP WATCHES, OTHER\nTHAN THOSE OF HEADING - OTHER: ELECTRICALLY OPERATED: STOP WATCHES",
+        "hsn_code": "91029120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP WATCHES, OTHER\nTHAN THOSE OF HEADING - OTHER: ELECTRICALLY OPERATED: POCKET WATCHES",
+        "hsn_code": "91029110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP WATCHES, OTHER\nTHAN THOSE OF HEADING 9101- OTHER WRIST-WATCHES, WHETHER OR NOT INCORPORATING\nA STOP-WATCH FACILITY: OTHER",
+        "hsn_code": "91022900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP WATCHES, OTHER\nTHAN THOSE OF HEADING 9101- OTHER WRIST-WATCHES, WHETHER OR NOT INCORPORATING\nA STOP-WATCH FACILITY: WITH AUTOMATIC WINDING",
+        "hsn_code": "91022100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP WATCHES, OTHER\nTHAN THOSE OF HEADING 9101 - WRIST-WATCHES, ELECTRICALLY OPERATED, WHETHER OR NOT\nINCORPORATING A STOP-WATCH FACILITY: OTHER",
+        "hsn_code": "91021900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP WATCHES, OTHER\nTHAN THOSE OF HEADING 9101 - WRIST-WATCHES, ELECTRICALLY OPERATED, WHETHER OR NOT\nINCORPORATING A STOP-WATCH FACILITY: WITH OPTO-ELECTRONIC DISPLAY ONLY",
+        "hsn_code": "91021200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP WATCHES, OTHER\nTHAN THOSE OF HEADING 9101 - WRIST-WATCHES, ELECTRICALLY OPERATED, WHETHER OR NOT\nINCORPORATING A STOP-WATCH FACILITY: WITH MECHANICAL DISPLAY ONLY",
+        "hsn_code": "91021100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nSaffron : Other",
+        "hsn_code": "9102090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nSaffron : Saffron stamen",
+        "hsn_code": "9102020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC (CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nSaffron : Saffron stigma",
+        "hsn_code": "9102010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP-WATCHES, WITH CASE OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL",
+        "hsn_code": "9101",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP-WATCHES, WITH\nCASE OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL - OTHER : OTHER: OTHER",
+        "hsn_code": "91019990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP-WATCHES, WITH\nCASE OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL - OTHER : OTHER: STOP\nWATCHES",
+        "hsn_code": "91019920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP-WATCHES, WITH\nCASE OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL - OTHER : OTHER:\nPOCKET WATCHES",
+        "hsn_code": "91019910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP-WATCHES, WITH\nCASE OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL - OTHER : - ELECTRICALLY\nOPERATED: OTHER",
+        "hsn_code": "91019190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP-WATCHES, WITH\nCASE OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL - OTHER : - ELECTRICALLY\nOPERATED: STOP WATCHES",
+        "hsn_code": "91019120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP-WATCHES, WITH\nCASE OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL - OTHER : - ELECTRICALLY\nOPERATED: POCKET WATCHES",
+        "hsn_code": "91019110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP-WATCHES, WITH\nCASE OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL - OTHER WRIST\nWATCHES, WHETHER OR NOT INCORPORATING A STOP-WATCH FACILITY: OTHER",
+        "hsn_code": "91012900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP-WATCHES, WITH\nCASE OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL - OTHER WRIST\nWATCHES, WHETHER OR NOT INCORPORATING A STOP-WATCH FACILITY: WITH AUTOMATIC\nWINDING",
+        "hsn_code": "91012100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP-WATCHES, WITH\nCASE OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL - WRIST-WATCHES,\nELECTRICALLY OPERATED WHETHER OR NOT INCORPORATING A STOP-WATCH FACILITY: OTHER",
+        "hsn_code": "91011900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP-WATCHES, WITH\nCASE OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL - WRIST-WATCHES,\nELECTRICALLY OPERATED WHETHER OR NOT INCORPORATING A STOP-WATCH FACILITY: WITH\nOPTO-ELECTRONIC DISPLAY ONLY",
+        "hsn_code": "91011200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WRIST-WATCHES, POCKET-WATCHES AND OTHER WATCHES, INCLUDING STOP-WATCHES, WITH\nCASE OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL - WRIST-WATCHES,\nELECTRICALLY OPERATED WHETHER OR NOT INCORPORATING A STOP-WATCH FACILITY: WITH\nMECHANICAL DISPLAY ONLY",
+        "hsn_code": "91011100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC(CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nGINGER--CRUSHED OR GROUND---OTHER",
+        "hsn_code": "9101290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC(CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nGINGER--CRUSHED OR GROUND---POWDER",
+        "hsn_code": "9101210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC(CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nGINGER--NEITHER CRUSHED NOR GROUND---OTHER",
+        "hsn_code": "9101190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC(CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nGINGER--NEITHER CRUSHED NOR GROUND---DRIED, BLEACHED",
+        "hsn_code": "9101130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC(CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nGINGER--NEITHER CRUSHED NOR GROUND---DRIED, UNBLEACHED",
+        "hsn_code": "9101120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GINGER, SAFFRON, TURMERIC(CURCUMA), THYME, BAY LEAVES, CURRY AND OTHER SPICES\nGINGER--NEITHER CRUSHED NOR GROUND---FRESH",
+        "hsn_code": "9101110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Parts and accessories (not specified or included elsewhere in this Chapter) for machines, appliances, instruments or apparatus of Chapter 90",
+        "hsn_code": "9033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES (NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER) FOR\nMACHINES, APPLIANCES, INSTRUMENTS OR APPARATUS OF CHAPTER 90",
+        "hsn_code": "90330000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC REGULATING OR CONTROLLIING INSTRUMENTS AND APPARATUS",
+        "hsn_code": "9032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC REGULATING OR CONTROLLING INSTRUMENTS AND APPARATUS PARTS AND\nACCESSORIES",
+        "hsn_code": "90329000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC REGULATING OR CONTROLLING INSTRUMENTS AND APPARATUS - OTHER\nINSTRUMENTS AND APPARATUS: - OTHER: OTHER",
+        "hsn_code": "90328990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC REGULATING OR CONTROLLING INSTRUMENTS AND APPARATUS - OTHER\nINSTRUMENTS AND APPARATUS: - OTHER: ELECTRONIC AUTOMATIC REGULATORS",
+        "hsn_code": "90328910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC REGULATING OR CONTROLLING INSTRUMENTS AND APPARATUS - OTHER\nINSTRUMENTS AND APPARATUS: HYDRAULIC OR PNEUMATIC",
+        "hsn_code": "90328100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC REGULATING OR CONTROLLING INSTRUMENTS AND APPARATUS - MANOSTATS:\nOTHER",
+        "hsn_code": "90322090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC REGULATING OR CONTROLLING INSTRUMENTS AND APPARATUS - MANOSTATS:\nFOR REFRIGERATING AND AIR-CONDITIONING APPLIANCES AND MACHINERY",
+        "hsn_code": "90322010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC REGULATING OR CONTROLLING INSTRUMENTS AND APPARATUS 9032 10\nAUTOMATIC REGULATING OR CONTROLLING INSTRUMENTS AND APPARATUS - THERMOSTATS:\nOTHER",
+        "hsn_code": "90321090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC REGULATING OR CONTROLLING INSTRUMENTS AND APPARATUS 9032 10\nAUTOMATIC REGULATING OR CONTROLLING INSTRUMENTS AND APPARATUS - THERMOSTATS:\nFOR REFRIGERATING AND AIR-CONDITIONING APPLIANCES AND MACHINERY",
+        "hsn_code": "90321010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MEASURING OR CHECKING INSTRUMENTS, APPLIANCES AND MACHINES, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER; PROFILE PROJECTORS",
+        "hsn_code": "9031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MEASURING OR CHECKING INSTRUMENTS, APPLIANCES AND MACHINES, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER; PROFILE PROJECTORS PARTS AND ACCESSORIES",
+        "hsn_code": "90319000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MEASURING OR CHECKING INSTRUMENTS, APPLIANCES AND MACHINES, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER; PROFILE PROJECTORS OTHER INSTRUMENTS,\nAPPLIANCES AND MACHINES",
+        "hsn_code": "90318000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MEASURING OR CHECKING INSTRUMENTS, APPLIANCES AND MACHINES, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER; PROFILE PROJECTORS - OTHER OPTICAL INSTRUMENTS\nAND APPLIANCES: OTHER",
+        "hsn_code": "90314900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MEASURING OR CHECKING INSTRUMENTS, APPLIANCES AND MACHINES, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER; PROFILE PROJECTORS - OTHER OPTICAL INSTRUMENTS\nAND APPLIANCES: FOR INSPECTING SEMICONDUCTOR WAFERS, OR DEVICES OR FOR INSPECTING\nPHOTO-MASKS OR RETICLES USED IN MANUFACTURING SEMICONDUCTOR DEVICES",
+        "hsn_code": "90314100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MEASURING OR CHECKING INSTRUMENTS, APPLIANCES AND MACHINES, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER; PROFILE PROJECTORS PROFILE PROJECTORS",
+        "hsn_code": "90313000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MEASURING OR CHECKING INSTRUMENTS, APPLIANCES AND MACHINES, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER; PROFILE PROJECTORS TEST BENCHES",
+        "hsn_code": "90312000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MEASURING OR CHECKING INSTRUMENTS, APPLIANCES AND MACHINES, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER; PROFILE PROJECTORS MACHINES FOR BALANCING\nMECHANICAL PARTS",
+        "hsn_code": "90311000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028; INSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY COSMIC OR OTHER IONISING RADIATIONS",
+        "hsn_code": "9030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS-PARTS AND ACCESSORIES: OTHER",
+        "hsn_code": "90309090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS-PARTS AND ACCESSORIES:OF METERS AND COUNTERS",
+        "hsn_code": "90309010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS- OTHER INSTRUMENTS AND APPARATUS: - OTHER:\nOTHER",
+        "hsn_code": "90308990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS- OTHER INSTRUMENTS AND APPARATUS: - OTHER:\nVECTROSCOPE",
+        "hsn_code": "90308920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS- OTHER INSTRUMENTS AND APPARATUS: - OTHER:\nSCINTILLATOR COUNTERS",
+        "hsn_code": "90308910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS- OTHER INSTRUMENTS AND APPARATUS: OTHER,\nWITH A RECORDING DEVICE",
+        "hsn_code": "90308400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS- OTHER INSTRUMENTS AND APPARATUS: OTHER,\nWITH A RECORDING DEVICE",
+        "hsn_code": "90308300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS- OTHER INSTRUMENTS AND APPARATUS: FOR\nMEASURING OR CHECKING SEMICONDUCTOR WAFERS OR DEVICES",
+        "hsn_code": "90308200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS OTHER INSTRUMENTS AND APPARATUS, SPECIALLY\nDESIGNED FOR TELECOMMUNICATIONS (FOR EXAMPLE, CROSS-TALK METERS, GAIN MEASURING\nINSTRUMENTS, DISTORTION FACTOR METERS, PSOPHOMETERS)",
+        "hsn_code": "90304000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS - OTHER INSTRUMENTS AND APPARATUS, FOR\nMEASURING OR CHECKING VOLTAGE, CURRENT, RESISTANCE OR POWER, WITHOUT A\nRECORDING DEVICE: - OTHER: OTHER",
+        "hsn_code": "90303990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS - OTHER INSTRUMENTS AND APPARATUS, FOR\nMEASURING OR CHECKING VOLTAGE, CURRENT, RESISTANCE OR POWER, WITHOUT A\nRECORDING DEVICE: - OTHER: MEGA METERS",
+        "hsn_code": "90303950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS - OTHER INSTRUMENTS AND APPARATUS, FOR\nMEASURING OR CHECKING VOLTAGE, CURRENT, RESISTANCE OR POWER, WITHOUT A\nRECORDING DEVICE: - OTHER: FREQUENCY MEASURING APPARATUS",
+        "hsn_code": "90303940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS - OTHER INSTRUMENTS AND APPARATUS, FOR\nMEASURING OR CHECKING VOLTAGE, CURRENT, RESISTANCE OR POWER, WITHOUT A\nRECORDING DEVICE: - OTHER: CAPACITANCE METER",
+        "hsn_code": "90303930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS - OTHER INSTRUMENTS AND APPARATUS, FOR\nMEASURING OR CHECKING VOLTAGE, CURRENT, RESISTANCE OR POWER, WITHOUT A\nRECORDING DEVICE: - OTHER: SPECTRUM RESISTANCE METERS",
+        "hsn_code": "90303920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS - OTHER INSTRUMENTS AND APPARATUS, FOR\nMEASURING OR CHECKING VOLTAGE, CURRENT, RESISTANCE OR POWER, WITHOUT A\nRECORDING DEVICE: - OTHER: AMMETERS, VOLT METERS AND WATT METERS",
+        "hsn_code": "90303910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS - OTHER INSTRUMENTS AND APPARATUS, FOR\nMEASURING OR CHECKING VOLTAGE, CURRENT, RESISTANCE OR POWER: -- OTHER, WITH A\nRECORDING DEVICE",
+        "hsn_code": "90303900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS - OTHER INSTRUMENTS AND APPARATUS, FOR\nMEASURING OR CHECKING VOLTAGE, CURRENT, RESISTANCE OR POWER: -- OTHER, WITHOUT A\nRECORDING DEVICE:OTHER",
+        "hsn_code": "90303390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS - OTHER INSTRUMENTS AND APPARATUS, FOR\nMEASURING OR CHECKING VOLTAGE, CURRENT, RESISTANCE OR POWER: -- OTHER, WITHOUT A\nRECORDING DEVICE:MEGA METERS",
+        "hsn_code": "90303350",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS - OTHER INSTRUMENTS AND APPARATUS, FOR\nMEASURING OR CHECKING VOLTAGE, CURRENT, RESISTANCE OR POWER: -- OTHER, WITHOUT A\nRECORDING DEVICE:FREQUENCY MEASURING APPARATUS",
+        "hsn_code": "90303340",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS - OTHER INSTRUMENTS AND APPARATUS, FOR\nMEASURING OR CHECKING VOLTAGE, CURRENT, RESISTANCE OR POWER: -- OTHER, WITHOUT A\nRECORDING DEVICE:CAPACITANCE METER",
+        "hsn_code": "90303330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS - OTHER INSTRUMENTS AND APPARATUS, FOR\nMEASURING OR CHECKING VOLTAGE, CURRENT, RESISTANCE OR POWER: -- OTHER, WITHOUT A\nRECORDING DEVICE:SPECTRUM RESISTANCE METERS",
+        "hsn_code": "90303320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS - OTHER INSTRUMENTS AND APPARATUS, FOR\nMEASURING OR CHECKING VOLTAGE, CURRENT, RESISTANCE OR POWER: -- OTHER, WITHOUT A\nRECORDING DEVICE:AMMETERS, VOLT METERS AND WATT METERS",
+        "hsn_code": "90303310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS - OTHER INSTRUMENTS AND APPARATUS, FOR\nMEASURING OR CHECKING VOLTAGE, CURRENT, RESISTANCE OR POWER: -- MULTIMETERS WITH\nA RECORDING DEVICE",
+        "hsn_code": "90303200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS - OTHER INSTRUMENTS AND APPARATUS, FOR\nMEASURING OR CHECKING VOLTAGE, CURRENT, RESISTANCE OR POWER: -- MULTIMETERS\nWITHOUT A RECORDING DEVICE",
+        "hsn_code": "90303100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS - OSCILLOSCOPES AND OSCILLOGRAPHS",
+        "hsn_code": "90302000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OSCILLOSCOPES, SPECTRUM ANALYSERS AND OTHER INSTRUMENTS AND APPARATUS FOR\nMEASURING OR CHECKING ELECTRICAL QUANTITIES, EXCLUDING METERS OF HEADING 9028;\nINSTRUMENTS AND APPARATUS FOR MEASURING OR DETECTING ALPHA, BETA, GAMMA, X-RAY,\nCOSMIC OR OTHER IONISING RADIATIONS INSTRUMENTS AND APPARATUS FOR MEASURING OR\nDETECTING IONISING RADIATIONS",
+        "hsn_code": "90301000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MATE",
+        "hsn_code": "9030000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REVOLUTION COUNTERS, PRODUCTION COUNTERS, TAXIMETERS, MILO METERS, PEDOMETERS AND THE LIKE; SPEED INDICATORS AND TACHO METERS, OTHER THAN THOSE OF HEADING 9014 OR 9015; STROBOSCOPES",
+        "hsn_code": "9029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REVOLUTION COUNTERS, PRODUCTION COUNTERS, TAXIMETERS, MILEOMETERS, PEDOMETERS\nAND THE LIKE; SPEED INDICATORS AND TACHOMETERS, OTHER THAN THOSE OF HEADING 9014\nOR 9015; STROBOSCOPES PARTS AND ACCESSORIES",
+        "hsn_code": "90299000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REVOLUTION COUNTERS, PRODUCTION COUNTERS, TAXIMETERS, MILEOMETERS, PEDOMETERS\nAND THE LIKE; SPEED INDICATORS AND TACHOMETERS, OTHER THAN THOSE OF HEADING 9014\nOR 9015; STROBOSCOPES - SPEED INDICATORS AND TACHOMETERS; STROBOSCOPES: OTHER",
+        "hsn_code": "90292090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REVOLUTION COUNTERS, PRODUCTION COUNTERS, TAXIMETERS, MILEOMETERS, PEDOMETERS\nAND THE LIKE; SPEED INDICATORS AND TACHOMETERS, OTHER THAN THOSE OF HEADING 9014\nOR 9015; STROBOSCOPES - SPEED INDICATORS AND TACHOMETERS; STROBOSCOPES:\nSTROBOSCOPES",
+        "hsn_code": "90292030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REVOLUTION COUNTERS, PRODUCTION COUNTERS, TAXIMETERS, MILEOMETERS, PEDOMETERS\nAND THE LIKE; SPEED INDICATORS AND TACHOMETERS, OTHER THAN THOSE OF HEADING 9014\nOR 9015; STROBOSCOPES - SPEED INDICATORS AND TACHOMETERS; STROBOSCOPES:\nSPEEDOMETERS, NON-ELECTRICAL",
+        "hsn_code": "90292020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REVOLUTION COUNTERS, PRODUCTION COUNTERS, TAXIMETERS, MILEOMETERS, PEDOMETERS\nAND THE LIKE; SPEED INDICATORS AND TACHOMETERS, OTHER THAN THOSE OF HEADING 9014\nOR 9015; STROBOSCOPES - SPEED INDICATORS AND TACHOMETERS; STROBOSCOPES:\nTACHOMETERS, NON-ELECTRICAL",
+        "hsn_code": "90292010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REVOLUTION COUNTERS, PRODUCTION COUNTERS, TAXIMETERS, MILEOMETERS, PEDOMETERS\nAND THE LIKE; SPEED INDICATORS AND TACHOMETERS, OTHER THAN THOSE OF HEADING 9014\nOR 9015; STROBOSCOPES - REVOLUTION COUNTERS, PRODUCTION COUNTERS, TAXIMETERS,\nMILEOMETERS, PEDOMETERS AND THE LIKE: OTHER",
+        "hsn_code": "90291090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REVOLUTION COUNTERS, PRODUCTION COUNTERS, TAXIMETERS, MILEOMETERS, PEDOMETERS\nAND THE LIKE; SPEED INDICATORS AND TACHOMETERS, OTHER THAN THOSE OF HEADING 9014\nOR 9015; STROBOSCOPES - REVOLUTION COUNTERS, PRODUCTION COUNTERS, TAXIMETERS,\nMILEOMETERS, PEDOMETERS AND THE LIKE: TAXIMETERS",
+        "hsn_code": "90291010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GAS, LIQUID OR ELECTRICITY SUPPLY OR PRODUCTION METERS, INCLUDING CALIBRATING METERS THEREFOR",
+        "hsn_code": "9028",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GAS, LIQUID OR ELECTRICITY SUPPLY OR PRODUCTION METERS, INCLUDING CALIBRATING\nMETERS THEREFOR - PARTS AND ACCESSORIES : OTHER",
+        "hsn_code": "90289090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GAS, LIQUID OR ELECTRICITY SUPPLY OR PRODUCTION METERS, INCLUDING CALIBRATING\nMETERS THEREFOR - PARTS AND ACCESSORIES : FOR ELECTRICITY METERS",
+        "hsn_code": "90289010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GAS, LIQUID OR ELECTRICITY SUPPLY OR PRODUCTION METERS, INCLUDING CALIBRATING\nMETERS THEREFOR - ELECTRICITY METERS: OTHER",
+        "hsn_code": "90283090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GAS, LIQUID OR ELECTRICITY SUPPLY OR PRODUCTION METERS, INCLUDING CALIBRATING\nMETERS THEREFOR - ELECTRICITY METERS: FOR ALTERNATING CURRENT",
+        "hsn_code": "90283010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GAS, LIQUID OR ELECTRICITY SUPPLY OR PRODUCTION METERS, INCLUDING CALIBRATING\nMETERS THEREFOR LIQUID METERS",
+        "hsn_code": "90282000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GAS, LIQUID OR ELECTRICITY SUPPLY OR PRODUCTION METERS, INCLUDING CALIBRATING\nMETERS THEREFOR GAS METERS",
+        "hsn_code": "90281000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR PHYSICAL OR CHEMICAL ANALYSIS (FOR EXAMPLE, POLARIMETERS, REFRACTOMETERS, SPECTROMETERS, GAS OR SMOKE ANALYSIS APPARATUS); INSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING VISCOSITY, POROSITY, EXPANSION, SURFACE TENSION OR THE LIKE’ INSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING QUANTITIES OF HEAT, SOUND OR LIGHT (INCLUDING EXPOSURE METERS); MICROTOMES",
+        "hsn_code": "9027",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR PHYSICAL OR CHEMICAL ANALYSIS (FOR EXAMPLE,\nPOLARIMETERS, REFRACTOMETERS, SPECTROMETERS, GAS OR SMOKE ANALYSIS APPARATUS);\nINSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING VISCOSITY, POROSITY,\nEXPANSION, SURFACE TENSION OR THE LIKE; INSTRUMENTS AND APPARATUS FOR MEASURING\nOR CHECKING QUANTITIES OF HEAT, SOUND OR LIGHT (INCLUDING EXPOSURE METERS);\nMICROTOMES - MICROTOMES; PARTS AND ACCESSORIES : OTHER",
+        "hsn_code": "90279090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR PHYSICAL OR CHEMICAL ANALYSIS (FOR EXAMPLE,\nPOLARIMETERS, REFRACTOMETERS, SPECTROMETERS, GAS OR SMOKE ANALYSIS APPARATUS);\nINSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING VISCOSITY, POROSITY,\nEXPANSION, SURFACE TENSION OR THE LIKE; INSTRUMENTS AND APPARATUS FOR MEASURING\nOR CHECKING QUANTITIES OF HEAT, SOUND OR LIGHT (INCLUDING EXPOSURE METERS);\nMICROTOMES - MICROTOMES; PARTS AND ACCESSORIES : PRINTED CIRCUIT ASSEMBLIES FOR\nTHE GOODS OF SUB-HEADING 9027 80",
+        "hsn_code": "90279020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR PHYSICAL OR CHEMICAL ANALYSIS (FOR EXAMPLE,\nPOLARIMETERS, REFRACTOMETERS, SPECTROMETERS, GAS OR SMOKE ANALYSIS APPARATUS);\nINSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING VISCOSITY, POROSITY,\nEXPANSION, SURFACE TENSION OR THE LIKE; INSTRUMENTS AND APPARATUS FOR MEASURING\nOR CHECKING QUANTITIES OF HEAT, SOUND OR LIGHT (INCLUDING EXPOSURE METERS);\nMICROTOMES - MICROTOMES; PARTS AND ACCESSORIES : MICROTOMES, INCLUDING PARTS\nAND ACCESSORIES THEREOF",
+        "hsn_code": "90279010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR PHYSICAL OR CHEMICAL ANALYSIS (FOR EXAMPLE,\nPOLARIMETERS, REFRACTOMETERS, SPECTROMETERS, GAS OR SMOKE ANALYSIS APPARATUS);\nINSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING VISCOSITY, POROSITY,\nEXPANSION, SURFACE TENSION OR THE LIKE; INSTRUMENTS AND APPARATUS FOR MEASURING\nOR CHECKING QUANTITIES OF HEAT, SOUND OR LIGHT (INCLUDING EXPOSURE METERS);\nMICROTOMES - OTHER INSTRUMENTS AND APPARATUS: OTHER",
+        "hsn_code": "90278090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR PHYSICAL OR CHEMICAL ANALYSIS (FOR EXAMPLE,\nPOLARIMETERS, REFRACTOMETERS, SPECTROMETERS, GAS OR SMOKE ANALYSIS APPARATUS);\nINSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING VISCOSITY, POROSITY,\nEXPANSION, SURFACE TENSION OR THE LIKE; INSTRUMENTS AND APPARATUS FOR MEASURING\nOR CHECKING QUANTITIES OF HEAT, SOUND OR LIGHT (INCLUDING EXPOSURE METERS);\nMICROTOMES - OTHER INSTRUMENTS AND APPARATUS: NUCLEAR MAGNETIC RESONANCE\nINSTRUMENTS",
+        "hsn_code": "90278040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR PHYSICAL OR CHEMICAL ANALYSIS (FOR EXAMPLE,\nPOLARIMETERS, REFRACTOMETERS, SPECTROMETERS, GAS OR SMOKE ANALYSIS APPARATUS);\nINSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING VISCOSITY, POROSITY,\nEXPANSION, SURFACE TENSION OR THE LIKE; INSTRUMENTS AND APPARATUS FOR MEASURING\nOR CHECKING QUANTITIES OF HEAT, SOUND OR LIGHT (INCLUDING EXPOSURE METERS);\nMICROTOMES - OTHER INSTRUMENTS AND APPARATUS: INSTRUMENTS AND APPARATUS FOR\nMEASURING THE SURFACE OR INTERFOCIAL TENSION OF LIQUIDS",
+        "hsn_code": "90278030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR PHYSICAL OR CHEMICAL ANALYSIS (FOR EXAMPLE,\nPOLARIMETERS, REFRACTOMETERS, SPECTROMETERS, GAS OR SMOKE ANALYSIS APPARATUS);\nINSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING VISCOSITY, POROSITY,\nEXPANSION, SURFACE TENSION OR THE LIKE; INSTRUMENTS AND APPARATUS FOR MEASURING\nOR CHECKING QUANTITIES OF HEAT, SOUND OR LIGHT (INCLUDING EXPOSURE METERS);\nMICROTOMES - OTHER INSTRUMENTS AND APPARATUS: CALORIMETERS",
+        "hsn_code": "90278020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR PHYSICAL OR CHEMICAL ANALYSIS (FOR EXAMPLE,\nPOLARIMETERS, REFRACTOMETERS, SPECTROMETERS, GAS OR SMOKE ANALYSIS APPARATUS);\nINSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING VISCOSITY, POROSITY,\nEXPANSION, SURFACE TENSION OR THE LIKE; INSTRUMENTS AND APPARATUS FOR MEASURING\nOR CHECKING QUANTITIES OF HEAT, SOUND OR LIGHT (INCLUDING EXPOSURE METERS);\nMICROTOMES - OTHER INSTRUMENTS AND APPARATUS: VISCOMETERS",
+        "hsn_code": "90278010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR PHYSICAL OR CHEMICAL ANALYSIS (FOR EXAMPLE,\nPOLARIMETERS, REFRACTOMETERS, SPECTROMETERS, GAS OR SMOKE ANALYSIS APPARATUS);\nINSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING VISCOSITY, POROSITY,\nEXPANSION, SURFACE TENSION OR THE LIKE; INSTRUMENTS AND APPARATUS FOR MEASURING\nOR CHECKING QUANTITIES OF HEAT, SOUND OR LIGHT (INCLUDING EXPOSURE METERS);\nMICROTOMES - OTHER INSTRUMENTS AND APPARATUS USING OPTICAL RADIATIONS (UV,\nVISIBLE, IR): OTHER",
+        "hsn_code": "90275090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR PHYSICAL OR CHEMICAL ANALYSIS (FOR EXAMPLE,\nPOLARIMETERS, REFRACTOMETERS, SPECTROMETERS, GAS OR SMOKE ANALYSIS APPARATUS);\nINSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING VISCOSITY, POROSITY,\nEXPANSION, SURFACE TENSION OR THE LIKE; INSTRUMENTS AND APPARATUS FOR MEASURING\nOR CHECKING QUANTITIES OF HEAT, SOUND OR LIGHT (INCLUDING EXPOSURE METERS);\nMICROTOMES - OTHER INSTRUMENTS AND APPARATUS USING OPTICAL RADIATIONS (UV,\nVISIBLE, IR): POLARIMETERS",
+        "hsn_code": "90275030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR PHYSICAL OR CHEMICAL ANALYSIS (FOR EXAMPLE,\nPOLARIMETERS, REFRACTOMETERS, SPECTROMETERS, GAS OR SMOKE ANALYSIS APPARATUS);\nINSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING VISCOSITY, POROSITY,\nEXPANSION, SURFACE TENSION OR THE LIKE; INSTRUMENTS AND APPARATUS FOR MEASURING\nOR CHECKING QUANTITIES OF HEAT, SOUND OR LIGHT (INCLUDING EXPOSURE METERS);\nMICROTOMES - OTHER INSTRUMENTS AND APPARATUS USING OPTICAL RADIATIONS (UV,\nVISIBLE, IR): REFRACTOMETERS",
+        "hsn_code": "90275020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR PHYSICAL OR CHEMICAL ANALYSIS (FOR EXAMPLE,\nPOLARIMETERS, REFRACTOMETERS, SPECTROMETERS, GAS OR SMOKE ANALYSIS APPARATUS);\nINSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING VISCOSITY, POROSITY,\nEXPANSION, SURFACE TENSION OR THE LIKE; INSTRUMENTS AND APPARATUS FOR MEASURING\nOR CHECKING QUANTITIES OF HEAT, SOUND OR LIGHT (INCLUDING EXPOSURE METERS);\nMICROTOMES - OTHER INSTRUMENTS AND APPARATUS USING OPTICAL RADIATIONS (UV,\nVISIBLE, IR): PHOTOMETERS",
+        "hsn_code": "90275010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR PHYSICAL OR CHEMICAL ANALYSIS (FOR EXAMPLE,\nPOLARIMETERS, REFRACTOMETERS, SPECTROMETERS, GAS OR SMOKE ANALYSIS APPARATUS);\nINSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING VISCOSITY, POROSITY,\nEXPANSION, SURFACE TENSION OR THE LIKE; INSTRUMENTS AND APPARATUS FOR MEASURING\nOR CHECKING QUANTITIES OF HEAT, SOUND OR LIGHT (INCLUDING EXPOSURE METERS);\nMICROTOMES EXPOSURE METERS",
+        "hsn_code": "90274000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR PHYSICAL OR CHEMICAL ANALYSIS (FOR EXAMPLE,\nPOLARIMETERS, REFRACTOMETERS, SPECTROMETERS, GAS OR SMOKE ANALYSIS APPARATUS);\nINSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING VISCOSITY, POROSITY,\nEXPANSION, SURFACE TENSION OR THE LIKE; INSTRUMENTS AND APPARATUS FOR MEASURING\nOR CHECKING QUANTITIES OF HEAT, SOUND OR LIGHT (INCLUDING EXPOSURE METERS);\nMICROTOMES - SPECTROMETERS, SPECTROPHOTOMETERS AND SPECTROGRAPHS USING\nOPTICAL RADIATIONS (UV, VISIBLE, IR): OTHER",
+        "hsn_code": "90273090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR PHYSICAL OR CHEMICAL ANALYSIS (FOR EXAMPLE,\nPOLARIMETERS, REFRACTOMETERS, SPECTROMETERS, GAS OR SMOKE ANALYSIS APPARATUS);\nINSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING VISCOSITY, POROSITY,\nEXPANSION, SURFACE TENSION OR THE LIKE; INSTRUMENTS AND APPARATUS FOR MEASURING\nOR CHECKING QUANTITIES OF HEAT, SOUND OR LIGHT (INCLUDING EXPOSURE METERS);\nMICROTOMES - SPECTROMETERS, SPECTROPHOTOMETERS AND SPECTROGRAPHS USING\nOPTICAL RADIATIONS (UV, VISIBLE, IR): SPECTROPHOTOMETERS",
+        "hsn_code": "90273020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR PHYSICAL OR CHEMICAL ANALYSIS (FOR EXAMPLE,\nPOLARIMETERS, REFRACTOMETERS, SPECTROMETERS, GAS OR SMOKE ANALYSIS APPARATUS);\nINSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING VISCOSITY, POROSITY,\nEXPANSION, SURFACE TENSION OR THE LIKE; INSTRUMENTS AND APPARATUS FOR MEASURING\nOR CHECKING QUANTITIES OF HEAT, SOUND OR LIGHT (INCLUDING EXPOSURE METERS);\nMICROTOMES - SPECTROMETERS, SPECTROPHOTOMETERS AND SPECTROGRAPHS USING\nOPTICAL RADIATIONS (UV, VISIBLE, IR): SPECTROMETERS",
+        "hsn_code": "90273010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR PHYSICAL OR CHEMICAL ANALYSIS (FOR EXAMPLE,\nPOLARIMETERS, REFRACTOMETERS, SPECTROMETERS, GAS OR SMOKE ANALYSIS APPARATUS);\nINSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING VISCOSITY, POROSITY,\nEXPANSION, SURFACE TENSION OR THE LIKE; INSTRUMENTS AND APPARATUS FOR MEASURING\nOR CHECKING QUANTITIES OF HEAT, SOUND OR LIGHT (INCLUDING EXPOSURE METERS);\nMICROTOMES CHROMATOGRAPHS AND ELECTROPHORESIS INSTRUMENTS",
+        "hsn_code": "90272000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR PHYSICAL OR CHEMICAL ANALYSIS (FOR EXAMPLE,\nPOLARIMETERS, REFRACTOMETERS, SPECTROMETERS, GAS OR SMOKE ANALYSIS APPARATUS);\nINSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING VISCOSITY, POROSITY,\nEXPANSION, SURFACE TENSION OR THE LIKE; INSTRUMENTS AND APPARATUS FOR MEASURING\nOR CHECKING QUANTITIES OF HEAT, SOUND OR LIGHT (INCLUDING EXPOSURE METERS);\nMICROTOMES GAS OR SMOKE ANALYSIS APPARATUS",
+        "hsn_code": "90271000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING THE FLOW, LEVEL, PRESSURE OR OTHER VARIABLES OF LIQUIDS OR GASES (FOR EXAMPLE, FLOW METERS, LEVEL GAUGES, MANOMETERS, HEAT METERS), EXCLUDING INSTRUMENTS AND APPARATUS OF HEADING 9014, 9015, 9028 OR 9032",
+        "hsn_code": "9026",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING THE FLOW, LEVEL, PRESSURE\nOR OTHER VARIABLES OF LIQUIDS OR GASES (FOR EXAMPLE, FLOW METERS, LEVEL GAUGES,\nMANOMETERS, HEAT METERS), EXCLUDING INSTRUMENTS AND APPARATUS OF HEADING 9014,\n9015, 9028 OR 9032 PARTS AND ACCESSORIES",
+        "hsn_code": "90269000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING THE FLOW, LEVEL, PRESSURE\nOR OTHER VARIABLES OF LIQUIDS OR GASES (FOR EXAMPLE, FLOW METERS, LEVEL GAUGES,\nMANOMETERS, HEAT METERS), EXCLUDING INSTRUMENTS AND APPARATUS OF HEADING 9014,\n9015, 9028 OR 9032 - OTHER INSTRUMENTS OR APPARATUS: OTHER",
+        "hsn_code": "90268090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING THE FLOW, LEVEL, PRESSURE\nOR OTHER VARIABLES OF LIQUIDS OR GASES (FOR EXAMPLE, FLOW METERS, LEVEL GAUGES,\nMANOMETERS, HEAT METERS), EXCLUDING INSTRUMENTS AND APPARATUS OF HEADING 9014,\n9015, 9028 OR 9032 - OTHER INSTRUMENTS OR APPARATUS: HEAT METERS",
+        "hsn_code": "90268010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING THE FLOW, LEVEL, PRESSURE\nOR OTHER VARIABLES OF LIQUIDS OR GASES (FOR EXAMPLE, FLOW METERS, LEVEL GAUGES,\nMANOMETERS, HEAT METERS), EXCLUDING INSTRUMENTS AND APPARATUS OF HEADING 9014,\n9015, 9028 OR 9032 FOR MEASURING OR CHECKING PRESSURE",
+        "hsn_code": "90262000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING THE FLOW, LEVEL, PRESSURE\nOR OTHER VARIABLES OF LIQUIDS OR GASES (FOR EXAMPLE, FLOW METERS, LEVEL GAUGES,\nMANOMETERS, HEAT METERS), EXCLUDING INSTRUMENTS AND APPARATUS OF HEADING 9014,\n9015, 9028 OR 9032 - FOR MEASURING OR CHECKING THE FLOW OR LEVEL OF LIQUIDS: OTHER",
+        "hsn_code": "90261090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING THE FLOW, LEVEL, PRESSURE\nOR OTHER VARIABLES OF LIQUIDS OR GASES (FOR EXAMPLE, FLOW METERS, LEVEL GAUGES,\nMANOMETERS, HEAT METERS), EXCLUDING INSTRUMENTS AND APPARATUS OF HEADING 9014,\n9015, 9028 OR 9032 - FOR MEASURING OR CHECKING THE FLOW OR LEVEL OF LIQUIDS: LEVEL\nGAUGES",
+        "hsn_code": "90261020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPARATUS FOR MEASURING OR CHECKING THE FLOW, LEVEL, PRESSURE\nOR OTHER VARIABLES OF LIQUIDS OR GASES (FOR EXAMPLE, FLOW METERS, LEVEL GAUGES,\nMANOMETERS, HEAT METERS), EXCLUDING INSTRUMENTS AND APPARATUS OF HEADING 9014,\n9015, 9028 OR 9032 - FOR MEASURING OR CHECKING THE FLOW OR LEVEL OF LIQUIDS: FLOW\nMETERS",
+        "hsn_code": "90261010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDROMETERS, AND SIMILAR FLOATING INSTRUMENTS, THERMOMETERS, PYROMETERS, BAROMETERS, HYGROMETERS AND PSYCHROMETERS, RECORDING OR NOT, AND ANY COMBINATION OF THESE INSTRUMENTS",
+        "hsn_code": "9025",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDROMETERS AND SIMILAR FLOATING INSTRUMENTS, THERMOMETERS, PYROMETERS,\nBAROMETERS, HYGROMETERS AND PSYCHROMETERS, RECORDING OR NOT, AND ANY\nCOMBINATION OF THESE INSTRUMENTS PARTS AND ACCESSORIES",
+        "hsn_code": "90259000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDROMETERS AND SIMILAR FLOATING INSTRUMENTS, THERMOMETERS, PYROMETERS,\nBAROMETERS, HYGROMETERS AND PSYCHROMETERS, RECORDING OR NOT, AND ANY\nCOMBINATION OF THESE INSTRUMENTS - OTHER INSTRUMENTS: OTHER",
+        "hsn_code": "90258090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDROMETERS AND SIMILAR FLOATING INSTRUMENTS, THERMOMETERS, PYROMETERS,\nBAROMETERS, HYGROMETERS AND PSYCHROMETERS, RECORDING OR NOT, AND ANY\nCOMBINATION OF THESE INSTRUMENTS - OTHER INSTRUMENTS: LACTOMETER",
+        "hsn_code": "90258030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDROMETERS AND SIMILAR FLOATING INSTRUMENTS, THERMOMETERS, PYROMETERS,\nBAROMETERS, HYGROMETERS AND PSYCHROMETERS, RECORDING OR NOT, AND ANY\nCOMBINATION OF THESE INSTRUMENTS - OTHER INSTRUMENTS: BAROMETERS, NOT COMBINED\nWITH OTHER INSTRUMENTS",
+        "hsn_code": "90258020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDROMETERS AND SIMILAR FLOATING INSTRUMENTS, THERMOMETERS, PYROMETERS,\nBAROMETERS, HYGROMETERS AND PSYCHROMETERS, RECORDING OR NOT, AND ANY\nCOMBINATION OF THESE INSTRUMENTS - OTHER INSTRUMENTS: HYDROMETERS AND SIMILAR\nFLOATING INSTRUMENTS",
+        "hsn_code": "90258010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDROMETERS AND SIMILAR FLOATING INSTRUMENTS, THERMOMETERS, PYROMETERS,\nBAROMETERS, HYGROMETERS AND PSYCHROMETERS, RECORDING OR NOT, AND ANY\nCOMBINATION OF THESE INSTRUMENTS - THERMOMETERS AND PYROMETERS, NOT COMBINED\nWITH OTHER INSTRUMENTS: - OTHER: OTHER",
+        "hsn_code": "90251990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDROMETERS AND SIMILAR FLOATING INSTRUMENTS, THERMOMETERS, PYROMETERS,\nBAROMETERS, HYGROMETERS AND PSYCHROMETERS, RECORDING OR NOT, AND ANY\nCOMBINATION OF THESE INSTRUMENTS - THERMOMETERS AND PYROMETERS, NOT COMBINED\nWITH OTHER INSTRUMENTS: - OTHER: PYROMETERS",
+        "hsn_code": "90251920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDROMETERS AND SIMILAR FLOATING INSTRUMENTS, THERMOMETERS, PYROMETERS,\nBAROMETERS, HYGROMETERS AND PSYCHROMETERS, RECORDING OR NOT, AND ANY\nCOMBINATION OF THESE INSTRUMENTS - THERMOMETERS AND PYROMETERS, NOT COMBINED\nWITH OTHER INSTRUMENTS: - OTHER: DIGITAL THERMOMETERS",
+        "hsn_code": "90251910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDROMETERS AND SIMILAR FLOATING INSTRUMENTS, THERMOMETERS, PYROMETERS,\nBAROMETERS, HYGROMETERS AND PSYCHROMETERS, RECORDING OR NOT, AND ANY\nCOMBINATION OF THESE INSTRUMENTS - THERMOMETERS AND PYROMETERS, NOT COMBINED\nWITH OTHER INSTRUMENTS: - LIQUID-FILLED, FOR DIRECT READING: OTHER",
+        "hsn_code": "90251190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDROMETERS AND SIMILAR FLOATING INSTRUMENTS, THERMOMETERS, PYROMETERS,\nBAROMETERS, HYGROMETERS AND PSYCHROMETERS, RECORDING OR NOT, AND ANY\nCOMBINATION OF THESE INSTRUMENTS - THERMOMETERS AND PYROMETERS, NOT COMBINED\nWITH OTHER INSTRUMENTS: - LIQUID-FILLED, FOR DIRECT READING: CLINICAL THERMOMETERS",
+        "hsn_code": "90251110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND APPLIANCES FOR TESTING THE HARDNESS, STRENGTH, COMPRESSIBILITY, ELEASTICITY OR OTHER MECHANICAL PROPERTIES OF MATERIALS (FOR EXAMPLE, METALS, WOOD, TEXTILES, PAPER, PLASTICS)",
+        "hsn_code": "9024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND APPLIANCES FOR TESTING THE HARDNESS, STRENGTH, COMPRESSIBILITY,\nELASTICITY OR OTHER MECHANICAL PROPERTIES OF MATERIALS (FOR EXAMPLE, METALS,\nWOOD, TEXTILES, PAPER, PLASTICS) - OTHER MACHINES AND APPLIANCES: - OTHER: PARTS AND\nACCESSORIES",
+        "hsn_code": "90249000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND APPLIANCES FOR TESTING THE HARDNESS, STRENGTH, COMPRESSIBILITY,\nELASTICITY OR OTHER MECHANICAL PROPERTIES OF MATERIALS (FOR EXAMPLE, METALS,\nWOOD, TEXTILES, PAPER, PLASTICS) - OTHER MACHINES AND APPLIANCES: - OTHER: OTHER",
+        "hsn_code": "90248099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND APPLIANCES FOR TESTING THE HARDNESS, STRENGTH, COMPRESSIBILITY,\nELASTICITY OR OTHER MECHANICAL PROPERTIES OF MATERIALS (FOR EXAMPLE, METALS,\nWOOD, TEXTILES, PAPER, PLASTICS) - OTHER MACHINES AND APPLIANCES: - OTHER: FOR\nTESTING HARDNESS",
+        "hsn_code": "90248091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND APPLIANCES FOR TESTING THE HARDNESS, STRENGTH, COMPRESSIBILITY,\nELASTICITY OR OTHER MECHANICAL PROPERTIES OF MATERIALS (FOR EXAMPLE, METALS,\nWOOD, TEXTILES, PAPER, PLASTICS) - OTHER MACHINES AND APPLIANCES: FOR TESTING\nTEXTILES, PAPER AND PAPERBOARD",
+        "hsn_code": "90248010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND APPLIANCES FOR TESTING THE HARDNESS, STRENGTH, COMPRESSIBILITY,\nELASTICITY OR OTHER MECHANICAL PROPERTIES OF MATERIALS (FOR EXAMPLE, METALS,\nWOOD, TEXTILES, PAPER, PLASTICS) MACHINES AND APPLIANCES FOR TESTING METALS",
+        "hsn_code": "90241000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED OTHER BLACK TEA (FERMENTED) AND OTHER PARTLY\nFERMENTED TEA : OTHER",
+        "hsn_code": "9024090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED OTHER BLACK TEA (FERMENTED) AND OTHER PARTLY\nFERMENTED TEA : BLACK TEA, WASTE",
+        "hsn_code": "9024060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED OTHER BLACK TEA (FERMENTED) AND OTHER PARTLY\nFERMENTED TEA : BLACK TEA, AGGLOMERATED IN FORMS SUCH AS BALL, BRICK AND TABLETS",
+        "hsn_code": "9024050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED OTHER BLACK TEA (FERMENTED) AND OTHER PARTLY\nFERMENTED TEA : TEA BAGS",
+        "hsn_code": "9024040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED OTHER BLACK TEA (FERMENTED) AND OTHER PARTLY\nFERMENTED TEA : BLACK TEA, DUST IN BULK",
+        "hsn_code": "9024030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED OTHER BLACK TEA (FERMENTED) AND OTHER PARTLY\nFERMENTED TEA : BLACK TEA, LEAF IN BULK",
+        "hsn_code": "9024020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED OTHER BLACK TEA (FERMENTED) AND OTHER PARTLY\nFERMENTED TEA : CONTENT EXCEEDING 3 KG. BUT NOT EXCEEDING 20 KG.",
+        "hsn_code": "9024010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS, APPARATUS AND MODELS, DESIGNED FOR DEMONSTRATIONAL PURPOSES (FOR EXAMPLE, IN EDUCATION OR EXHIBITIONS), UNSUITABLE FOR OTHER USES",
+        "hsn_code": "9023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS, APPARATUS AND MODELS, DESIGNED FOR DEMONSTRATIONAL PURPOSES (FOR\nEXAMPLE, IN EDUCATION OR EXHIBITIONS), UNSUITABLE FOR OTHER USES - INSTRUMENTS,\nAPPARATUS AND MODELS, DESIGNED FOR DEMONSTRATIONAL PURPOSES (FOR EXAMPLE, IN\nEDUCATION OR EXHIBITIONS), UNSUITABLE FOR OTHER USES: OTHER",
+        "hsn_code": "90230090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS, APPARATUS AND MODELS, DESIGNED FOR DEMONSTRATIONAL PURPOSES (FOR\nEXAMPLE, IN EDUCATION OR EXHIBITIONS), UNSUITABLE FOR OTHER USES - INSTRUMENTS,\nAPPARATUS AND MODELS, DESIGNED FOR DEMONSTRATIONAL PURPOSES (FOR EXAMPLE, IN\nEDUCATION OR EXHIBITIONS), UNSUITABLE FOR OTHER USES: TEACHING AIDS",
+        "hsn_code": "90230010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED BLACK TEA (FERMENTED) AND PARTLY FERMENTED TEA, IN\nIMMEDIATE PACKINGS OF A CONTENT NOT EXCEEDING 3 KG. : OTHER",
+        "hsn_code": "9023090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED BLACK TEA (FERMENTED) AND PARTLY FERMENTED TEA, IN\nIMMEDIATE PACKINGS OF A CONTENT NOT EXCEEDING 3 KG. : CONTENT EXCEEDING 1 KG. BUT\nNOT EXCEEDING 3 KG.",
+        "hsn_code": "9023030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED BLACK TEA (FERMENTED) AND PARTLY FERMENTED TEA, IN\nIMMEDIATE PACKINGS OF A CONTENT NOT EXCEEDING 3 KG. : CONTENT EXCEEDING 25 G. BUT\nNOT EXCEEDING 1 KG.",
+        "hsn_code": "9023020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED BLACK TEA (FERMENTED) AND PARTLY FERMENTED TEA, IN\nIMMEDIATE PACKINGS OF A CONTENT NOT EXCEEDING 3 KG. : CONTENT NOT EXCEEDING 25 G.",
+        "hsn_code": "9023010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS BASED ON THE USE OF X-RAYS OR OF ALPHA, BETA OR GAMMA RADIATIONS, WHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING RADIOGRAPHY OR RADIO-THERAPY APPRATUS, X-RAY TUBES AND OTHER X-RAY GENERATORS, HIGH TENSION GENERATORS, CONTROL PANELS AND DESKS, SCREENS, EXAMINATION OR TREATMENT TABLES, CHAIRS AND THE LIKE;",
+        "hsn_code": "9022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS BASED ON THE USE OF X-RAYS OR OF ALPHA, BETA OR GAMMA RADIATIONS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS, X-RAY TUBES AND OTHER X-RAY GENERATORS,\nHIGH TENSION GENERATORS, CONTROL PANELS AND DESKS, SCREENS, EXAMINATION OR\nTREATMENT TABLES, CHAIRS AND THE LIKE - OTHER, INCLUDING PARTS AND ACCESSORIES:\nOTHER",
+        "hsn_code": "90229090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS BASED ON THE USE OF X-RAYS OR OF ALPHA, BETA OR GAMMA RADIATIONS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS, X-RAY TUBES AND OTHER X-RAY GENERATORS,\nHIGH TENSION GENERATORS, CONTROL PANELS AND DESKS, SCREENS, EXAMINATION OR\nTREATMENT TABLES, CHAIRS AND THE LIKE - OTHER, INCLUDING PARTS AND ACCESSORIES: X\nRAY EXAMINATION OR TREATMENT TABLE, CHAIRS AND THE LIKE",
+        "hsn_code": "90229040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS BASED ON THE USE OF X-RAYS OR OF ALPHA, BETA OR GAMMA RADIATIONS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS, X-RAY TUBES AND OTHER X-RAY GENERATORS,\nHIGH TENSION GENERATORS, CONTROL PANELS AND DESKS, SCREENS, EXAMINATION OR\nTREATMENT TABLES, CHAIRS AND THE LIKE - OTHER, INCLUDING PARTS AND ACCESSORIES:\nRADIATION BEAM DELIVERY UNITS",
+        "hsn_code": "90229030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS BASED ON THE USE OF X-RAYS OR OF ALPHA, BETA OR GAMMA RADIATIONS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS, X-RAY TUBES AND OTHER X-RAY GENERATORS,\nHIGH TENSION GENERATORS, CONTROL PANELS AND DESKS, SCREENS, EXAMINATION OR\nTREATMENT TABLES, CHAIRS AND THE LIKE - OTHER, INCLUDING PARTS AND ACCESSORIES:\nRADIATION GENERATION UNITS",
+        "hsn_code": "90229020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS BASED ON THE USE OF X-RAYS OR OF ALPHA, BETA OR GAMMA RADIATIONS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS, X-RAY TUBES AND OTHER X-RAY GENERATORS,\nHIGH TENSION GENERATORS, CONTROL PANELS AND DESKS, SCREENS, EXAMINATION OR\nTREATMENT TABLES, CHAIRS AND THE LIKE - OTHER, INCLUDING PARTS AND ACCESSORIES: X\nRAY VALVES",
+        "hsn_code": "90229010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS BASED ON THE USE OF X-RAYS OR OF ALPHA, BETA OR GAMMA RADIATIONS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS, X-RAY TUBES AND OTHER X-RAY GENERATORS,\nHIGH TENSION GENERATORS, CONTROL PANELS AND DESKS, SCREENS, EXAMINATION OR\nTREATMENT TABLES, CHAIRS AND THE LIKE X-RAY TUBES",
+        "hsn_code": "90223000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS BASED ON THE USE OF X-RAYS OR OF ALPHA, BETA OR GAMMA RADIATIONS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS, X-RAY TUBES AND OTHER X-RAY GENERATORS,\nHIGH TENSION GENERATORS, CONTROL PANELS AND DESKS, SCREENS, EXAMINATION OR\nTREATMENT TABLES, CHAIRS AND THE LIKE - APPARATUS BASED ON THE USE OF ALPHA, BETA\nOR GAMMA RADIATION, WHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY\nUSES, INCLUDING RADIOGRAPHY OR RADIOTHERAPY APPARATUS: FOR OTHER USES",
+        "hsn_code": "90222900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS BASED ON THE USE OF X-RAYS OR OF ALPHA, BETA OR GAMMA RADIATIONS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS, X-RAY TUBES AND OTHER X-RAY GENERATORS,\nHIGH TENSION GENERATORS, CONTROL PANELS AND DESKS, SCREENS, EXAMINATION OR\nTREATMENT TABLES, CHAIRS AND THE LIKE - APPARATUS BASED ON THE USE OF ALPHA, BETA\nOR GAMMA RADIATION, WHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY\nUSES, INCLUDING RADIOGRAPHY OR RADIOTHERAPY APPARATUS: FOR MEDICAL, SURGICAL,\nDENTAL OR VETERINARY USES",
+        "hsn_code": "90222100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS BASED ON THE USE OF X-RAYS OR OF ALPHA, BETA OR GAMMA RADIATIONS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS, X-RAY TUBES AND OTHER X-RAY GENERATORS,\nHIGH TENSION GENERATORS, CONTROL PANELS AND DESKS, SCREENS, EXAMINATION OR\nTREATMENT TABLES, CHAIRS AND THE LIKE - APPARATUS BASED ON THE USE OF X-RAYS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS: FOR OTHER USES",
+        "hsn_code": "90221900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "14 APPARATUS BASED ON THE USE OF X-RAYS OR OF ALPHA, BETA OR GAMMA RADIATIONS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS, X-RAY TUBES AND OTHER X-RAY GENERATORS,\nHIGH TENSION GENERATORS, CONTROL PANELS AND DESKS, SCREENS, EXAMINATION OR\nTREATMENT TABLES, CHAIRS AND THE LIKE - APPARATUS BASED ON THE USE OF X-RAYS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS: - OTHER, FOR MEDICAL, SURGICAL OR\nVETERINARY USES: OTHER",
+        "hsn_code": "90221490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "14 APPARATUS BASED ON THE USE OF X-RAYS OR OF ALPHA, BETA OR GAMMA RADIATIONS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS, X-RAY TUBES AND OTHER X-RAY GENERATORS,\nHIGH TENSION GENERATORS, CONTROL PANELS AND DESKS, SCREENS, EXAMINATION OR\nTREATMENT TABLES, CHAIRS AND THE LIKE - APPARATUS BASED ON THE USE OF X-RAYS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS: - OTHER, FOR MEDICAL, SURGICAL OR\nVETERINARY USES: PORTABLE X-RAY MACHINE",
+        "hsn_code": "90221420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "14 APPARATUS BASED ON THE USE OF X-RAYS OR OF ALPHA, BETA OR GAMMA RADIATIONS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS, X-RAY TUBES AND OTHER X-RAY GENERATORS,\nHIGH TENSION GENERATORS, CONTROL PANELS AND DESKS, SCREENS, EXAMINATION OR\nTREATMENT TABLES, CHAIRS AND THE LIKE - APPARATUS BASED ON THE USE OF X-RAYS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS: - OTHER, FOR MEDICAL, SURGICAL OR\nVETERINARY USES: X-RAY GENERATORS AND APPARATUS (NON-PORTABLE)",
+        "hsn_code": "90221410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS BASED ON THE USE OF X-RAYS OR OF ALPHA, BETA OR GAMMA RADIATIONS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS, X-RAY TUBES AND OTHER X-RAY GENERATORS,\nHIGH TENSION GENERATORS, CONTROL PANELS AND DESKS, SCREENS, EXAMINATION OR\nTREATMENT TABLES, CHAIRS AND THE LIKE - APPARATUS BASED ON THE USE OF X-RAYS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS: OTHER, FOR DENTAL USES",
+        "hsn_code": "90221300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS BASED ON THE USE OF X-RAYS OR OF ALPHA, BETA OR GAMMA RADIATIONS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS, X-RAY TUBES AND OTHER X-RAY GENERATORS,\nHIGH TENSION GENERATORS, CONTROL PANELS AND DESKS, SCREENS, EXAMINATION OR\nTREATMENT TABLES, CHAIRS AND THE LIKE - APPARATUS BASED ON THE USE OF X-RAYS,\nWHETHER OR NOT FOR MEDICAL, SURGICAL, DENTAL OR VETERINARY USES, INCLUDING\nRADIOGRAPHY OR RADIOTHERAPY APPARATUS: COMPUTED TOMOGRAPHY APPARATUS",
+        "hsn_code": "90221200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED OTHER GREEN TEA (NOT FERMENTED) : OTHER",
+        "hsn_code": "9022090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED OTHER GREEN TEA (NOT FERMENTED) : GREEN TEA WASTE",
+        "hsn_code": "9022040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED OTHER GREEN TEA (NOT FERMENTED) : GREEN TEA\nAGGLOMERATED IN FORMS SUCH AS BALL,BRICK AND TABLETS",
+        "hsn_code": "9022030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED OTHER GREEN TEA (NOT FERMENTED) : GREEN TEA IN BULK",
+        "hsn_code": "9022020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED OTHER GREEN TEA (NOT FERMENTED) : GREEN TEA IN\nPACKETS WITH CONTENTS EXCEEDING 3 KG BUT NOT EXCEEDING 20 KG",
+        "hsn_code": "9022010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Other breathing appliances and gas masks, excluding protective masks having neither mechanical parts nor replaceable filters",
+        "hsn_code": "9020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER BREATHING APPLIANCES AND GAS MASKS, EXCLUDING PROTECTIVE MASKS HAVING\nNEITHER MECHANICAL PARTS NOR REPLACEABLE FILTERS",
+        "hsn_code": "90200000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MECHANO-THERAPY APPLIANCES; MASSAGE APPARATUS; PSYCHOLOGICAL APTITUDE-TESTING APPARATUS; OZONE THERAPY, OXYGEN THERAPY, AEROSOL THERAPY, ARTIFICIAL RESPIRATION OR OTHER THERAPEUTIC RESPIRATION APPARATUS",
+        "hsn_code": "9019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "20 MECHANO-THERAPY APPLIANCES; MASSAGE APPARATUS; PSYCHOLOGICAL APTITUDE\nTESTING APPARATUS; OZONE THERAPY, OXYGEN THERAPY, AEROSOL THERAPY, ARTIFICIAL\nRESPIRATION OR OTHER THERAPEUTIC RESPIRATION APPARATUS- OZONE THERAPY, OXYGEN\nTHERAPY, AEROSOL THERAPY, ARTIFICIAL RESPIRATION OR OTHER THERAPEUTIC RESPIRATION\nAPPARATUS: OTHER",
+        "hsn_code": "90192090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "20 MECHANO-THERAPY APPLIANCES; MASSAGE APPARATUS; PSYCHOLOGICAL APTITUDE\nTESTING APPARATUS; OZONE THERAPY, OXYGEN THERAPY, AEROSOL THERAPY, ARTIFICIAL\nRESPIRATION OR OTHER THERAPEUTIC RESPIRATION APPARATUS- OZONE THERAPY, OXYGEN\nTHERAPY, AEROSOL THERAPY, ARTIFICIAL RESPIRATION OR OTHER THERAPEUTIC RESPIRATION\nAPPARATUS: OXYGEN THERAPY APPARATUS",
+        "hsn_code": "90192010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MECHANO-THERAPY APPLIANCES; MASSAGE APPARATUS; PSYCHOLOGICAL APTITUDE-TESTING\nAPPARATUS; OZONE THERAPY, OXYGEN THERAPY, AEROSOL THERAPY, ARTIFICIAL RESPIRATION\nOR OTHER THERAPEUTIC RESPIRATION APPARATUS- MECHANO-THERAPY APPLIANCES;\nMASSAGE APPARATUS; PSYCHOLOGICAL APTITUDE-TESTING APPARATUS: OTHER",
+        "hsn_code": "90191090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MECHANO-THERAPY APPLIANCES; MASSAGE APPARATUS; PSYCHOLOGICAL APTITUDE-TESTING\nAPPARATUS; OZONE THERAPY, OXYGEN THERAPY, AEROSOL THERAPY, ARTIFICIAL RESPIRATION\nOR OTHER THERAPEUTIC RESPIRATION APPARATUS- MECHANO-THERAPY APPLIANCES;\nMASSAGE APPARATUS; PSYCHOLOGICAL APTITUDE-TESTING APPARATUS: MASSAGE APPARATUS",
+        "hsn_code": "90191020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MECHANO-THERAPY APPLIANCES; MASSAGE APPARATUS; PSYCHOLOGICAL APTITUDE-TESTING\nAPPARATUS; OZONE THERAPY, OXYGEN THERAPY, AEROSOL THERAPY, ARTIFICIAL RESPIRATION\nOR OTHER THERAPEUTIC RESPIRATION APPARATUS- MECHANO-THERAPY APPLIANCES;\nMASSAGE APPARATUS; PSYCHOLOGICAL APTITUDE-TESTING APPARATUS: MECHANO-THERAPY\nAPPLIANCES",
+        "hsn_code": "90191010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Other : Other",
+        "hsn_code": "9019090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Other : Coffee substitutes containing\ncoffee",
+        "hsn_code": "9019020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Other : Coffee husks and skins",
+        "hsn_code": "9019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY SCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND SIGHT-TESTING INSTRUMENTS",
+        "hsn_code": "9018",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - OTHER: OTHER",
+        "hsn_code": "90189099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - OTHER: APPARATUS\nFOR NERVE STIMULATION",
+        "hsn_code": "90189098",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - OTHER: VETRASONIC\nLITHOTRIPSY INSTRUMENTS",
+        "hsn_code": "90189097",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - OTHER:\nLAPROSCOPES",
+        "hsn_code": "90189096",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - OTHER: FIBRESCOPES",
+        "hsn_code": "90189095",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - OTHER:\nDEFIBRILLATORS",
+        "hsn_code": "90189094",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - OTHER: HEART-LUNG\nMACHINES",
+        "hsn_code": "90189093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - OTHER: BABY\nINCUBATORS",
+        "hsn_code": "90189092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - OTHER: HILERIAL OR\nVENOUS SHUNTS",
+        "hsn_code": "90189091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - ANESTHETIC\nAPPARATUS AND INSTRUMENTS, ENT PRECISION INSTRUMENTS, ACUPUNCTURE APPARATUS,\nAND ENDOSCOPES: ENDOSCOPES",
+        "hsn_code": "90189044",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - ANESTHETIC\nAPPARATUS AND INSTRUMENTS, ENT PRECISION INSTRUMENTS, ACUPUNCTURE APPARATUS,\nAND ENDOSCOPES: ACUPUNCTURE APPARATUS",
+        "hsn_code": "90189043",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - ANESTHETIC\nAPPARATUS AND INSTRUMENTS, ENT PRECISION INSTRUMENTS, ACUPUNCTURE APPARATUS,\nAND ENDOSCOPES: ENT PRECISION INSTRUMENTS",
+        "hsn_code": "90189042",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - ANESTHETIC\nAPPARATUS AND INSTRUMENTS, ENT PRECISION INSTRUMENTS, ACUPUNCTURE APPARATUS,\nAND ENDOSCOPES: ANESTHETIC APPARATUS AND INSTRUMENTS",
+        "hsn_code": "90189041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - RENAL DIALYSIS\nEQUIPMENT, BLOOD TRANSFUSION APPARATUS AND HAEMOFILTRATION INSTRUMENTS:\nHAEMOFILTRATION INSTRUMENTS",
+        "hsn_code": "90189033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - RENAL DIALYSIS\nEQUIPMENT, BLOOD TRANSFUSION APPARATUS AND HAEMOFILTRATION INSTRUMENTS: BLOOD\nTRANFUSION APPARATUS",
+        "hsn_code": "90189032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - RENAL DIALYSIS\nEQUIPMENT, BLOOD TRANSFUSION APPARATUS AND HAEMOFILTRATION INSTRUMENTS: RENAL\nDIALYSIS EQUIPMENT(ARTIFICIAL KIDNEYS, KIDNEY MACHINES AND DIALYSERS)",
+        "hsn_code": "90189031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - SURGICAL TOOLS:\nOTHER",
+        "hsn_code": "90189029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - SURGICAL TOOLS:\nRETRACTORS, SPATULAPROBES, HOOKS DIALATORS, SOUNDS, MALLETS",
+        "hsn_code": "90189025",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - SURGICAL TOOLS:\nCHISEL, GAUGES, ELEVATORS, RASPATONES, OSTEOTOME, CRANIOTOME, BONE CUTTERS",
+        "hsn_code": "90189024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - SURGICAL TOOLS:\nFORCEPS, FORCEP CLAMPS, CLIPS, NEEDLE HOLDERS, INTRODUCERS, CEPHALOTRIBE BONE\nHOLDING AND OTHER HOLDING INSTRUMENTS",
+        "hsn_code": "90189023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - SURGICAL TOOLS:\nKNIVES, SCISSORS AND BLADES",
+        "hsn_code": "90189022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - SURGICAL TOOLS:\nBONE SAWS, DRILLS AND TREPHINES",
+        "hsn_code": "90189021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - DIAGNOSTIC\nINSTRUMENTS AND APPARATUS: OTHER",
+        "hsn_code": "90189019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - DIAGNOSTIC\nINSTRUMENTS AND APPARATUS: STETHOSCOPES",
+        "hsn_code": "90189012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES: - DIAGNOSTIC\nINSTRUMENTS AND APPARATUS: INSTRUMENT AND APPARATUS FOR MEASURING BLOOD\nPRESSURE",
+        "hsn_code": "90189011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER OPHTHALMIC INSTRUMENTS AND APPLIANCES: OTHER",
+        "hsn_code": "90185090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER OPHTHALMIC INSTRUMENTS AND APPLIANCES:\nOPHTHALMIC LASERS",
+        "hsn_code": "90185030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER OPHTHALMIC INSTRUMENTS AND APPLIANCES:\nTONOMETERS",
+        "hsn_code": "90185020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER OPHTHALMIC INSTRUMENTS AND APPLIANCES:\nOPHTHALMOSCOPES",
+        "hsn_code": "90185010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES, USED IN DENTAL\nSCIENCES: OTHER",
+        "hsn_code": "90184900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - OTHER INSTRUMENTS AND APPLIANCES, USED IN DENTAL\nSCIENCES: DENTAL DRILL ENGINES, WHETHER OR NOT COMBINED ON A SINGLE BASE WITH\nOTHER DENTAL EQUIPMENT",
+        "hsn_code": "90184100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - SYRINGES, NEEDLES, CATHETERS, CANNULAE AND THE LIKE: -\nOTHER: OTHER",
+        "hsn_code": "90183990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - SYRINGES, NEEDLES, CATHETERS, CANNULAE AND THE LIKE: -\nOTHER: CANNULAE",
+        "hsn_code": "90183930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - SYRINGES, NEEDLES, CATHETERS, CANNULAE AND THE LIKE: -\nOTHER: CARDIAC CATHETERS",
+        "hsn_code": "90183920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - SYRINGES, NEEDLES, CATHETERS, CANNULAE AND THE LIKE: -\nOTHER: CATHETERS (FOR URINE, STOOL)",
+        "hsn_code": "90183910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - SYRINGES, NEEDLES, CATHETERS, CANNULAE AND THE LIKE: -\nTUBULAR METAL NEEDLES AND NEEDLES FOR SUTURES: OTHER",
+        "hsn_code": "90183290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - SYRINGES, NEEDLES, CATHETERS, CANNULAE AND THE LIKE: -\nTUBULAR METAL NEEDLES AND NEEDLES FOR SUTURES: HILERIO VENUS FISTULA NEEDLES",
+        "hsn_code": "90183230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - SYRINGES, NEEDLES, CATHETERS, CANNULAE AND THE LIKE: -\nTUBULAR METAL NEEDLES AND NEEDLES FOR SUTURES: HOLLOW NEEDLES FOR INJECTION,\nASPIRATION, BIOPSY AND TRANSFUSION",
+        "hsn_code": "90183220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - SYRINGES, NEEDLES, CATHETERS, CANNULAE AND THE LIKE: -\nTUBULAR METAL NEEDLES AND NEEDLES FOR SUTURES: NEEDLES FOR SUTURE",
+        "hsn_code": "90183210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - SYRINGES, NEEDLES, CATHETERS, CANNULAE AND THE LIKE:\nSYRINGES, WITH OR WITHOUT NEEDLES",
+        "hsn_code": "90183100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS ULTRA-VIOLET OR INFRA-RED RAY APPARATUS",
+        "hsn_code": "90182000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - ELECTRO-DIAGNOSTIC APPARATUS (INCLUDING APPARATUS FOR\nFUNCTIONAL EXPLORATORY EXAMINATIONS OR FOR CHECKING PHYSIOLOGICAL PARAMETERS): -\nOTHER: OTHER",
+        "hsn_code": "90181990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - ELECTRO-DIAGNOSTIC APPARATUS (INCLUDING APPARATUS FOR\nFUNCTIONAL EXPLORATORY EXAMINATIONS OR FOR CHECKING PHYSIOLOGICAL PARAMETERS): -\nOTHER: ECHO CARDIOGRAPH",
+        "hsn_code": "90181920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - ELECTRO-DIAGNOSTIC APPARATUS (INCLUDING APPARATUS FOR\nFUNCTIONAL EXPLORATORY EXAMINATIONS OR FOR CHECKING PHYSIOLOGICAL PARAMETERS): -\nOTHER: ELECTRO ENCEPHALOGRAPHS",
+        "hsn_code": "90181910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - ELECTRO-DIAGNOSTIC APPARATUS (INCLUDING APPARATUS FOR\nFUNCTIONAL EXPLORATORY EXAMINATIONS OR FOR CHECKING PHYSIOLOGICAL PARAMETERS):\nSCIENTIGRAPHIC APPARATUS",
+        "hsn_code": "90181400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - ELECTRO-DIAGNOSTIC APPARATUS (INCLUDING APPARATUS FOR\nFUNCTIONAL EXPLORATORY EXAMINATIONS OR FOR CHECKING PHYSIOLOGICAL PARAMETERS):\nMAGNETIC RESONANCE IMAGING APPARATUS",
+        "hsn_code": "90181300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - ELECTRO-DIAGNOSTIC APPARATUS (INCLUDING APPARATUS FOR\nFUNCTIONAL EXPLORATORY EXAMINATIONS OR FOR CHECKING PHYSIOLOGICAL PARAMETERS): -\nULTRASONIC SCANNING APPARATUS: OTHER",
+        "hsn_code": "90181290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - ELECTRO-DIAGNOSTIC APPARATUS (INCLUDING APPARATUS FOR\nFUNCTIONAL EXPLORATORY EXAMINATIONS OR FOR CHECKING PHYSIOLOGICAL PARAMETERS): -\nULTRASONIC SCANNING APPARATUS: LINEAR ULTRASOUND SCANNER",
+        "hsn_code": "90181210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INSTRUMENTS AND APPLIANCES USED IN MEDICAL, SURGICAL, DENTAL OR VETERINARY\nSCIENCES, INCLUDING SCIENTIGRAPHIC APPARATUS, OTHER ELECTROMEDICAL APPARATUS AND\nSIGHT-TESTING INSTRUMENTS - ELECTRO-DIAGNOSTIC APPARATUS (INCLUDING APPARATUS FOR\nFUNCTIONAL EXPLORATORY EXAMINATIONS OR FOR CHECKING PHYSIOLOGICAL PARAMETERS):\nELECTRO-CARDIOGRAPHS",
+        "hsn_code": "90181100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DRAWING, MARKING-OUT OR MATHEMATICAL CALCULATING INSTRUMENTS (FOR EXAMPLE, DRAFTING MACHINES, PANTOGRAPHS, PROTRACTORS, DRAWING SETS, SLIDE RULES, DISC CALCULATORS); INSTRUMENTS FOR MEASURING LENGTH FOR USE IN THE HAND (FOR EXAMPLE, MEASURING RODS AND TAPES, MICROMETERS, CALLIPERS), NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER",
+        "hsn_code": "9017",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DRAWING, MARKING-OUT OR MATHEMATICAL CALCULATING INSTRUMENTS (FOR EXAMPLE,\nDRAFTING MACHINES, PANTOGRAPHS, PROTRACTORS, DRAWING SETS, SLIDE RULES, DISC\nCALCULATORS); INSTRUMENTS FOR MEASURING LENGTH, FOR USE IN THE HAND (FOR EXAMPLE,\nMEASURING RODS AND TAPES, MICROMETERS, CALLIPERS), NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER PARTS AND ACCESSORIES",
+        "hsn_code": "90179000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DRAWING, MARKING-OUT OR MATHEMATICAL CALCULATING INSTRUMENTS (FOR EXAMPLE,\nDRAFTING MACHINES, PANTOGRAPHS, PROTRACTORS, DRAWING SETS, SLIDE RULES, DISC\nCALCULATORS); INSTRUMENTS FOR MEASURING LENGTH, FOR USE IN THE HAND (FOR EXAMPLE,\nMEASURING RODS AND TAPES, MICROMETERS, CALLIPERS), NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER - OTHER INSTRUMENTS: OTHER",
+        "hsn_code": "90178090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DRAWING, MARKING-OUT OR MATHEMATICAL CALCULATING INSTRUMENTS (FOR EXAMPLE,\nDRAFTING MACHINES, PANTOGRAPHS, PROTRACTORS, DRAWING SETS, SLIDE RULES, DISC\nCALCULATORS); INSTRUMENTS FOR MEASURING LENGTH, FOR USE IN THE HAND (FOR EXAMPLE,\nMEASURING RODS AND TAPES, MICROMETERS, CALLIPERS), NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER - OTHER INSTRUMENTS: MEASURING RODS AND TAPES AND\nDIVIDED SCALES",
+        "hsn_code": "90178010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DRAWING, MARKING-OUT OR MATHEMATICAL CALCULATING INSTRUMENTS (FOR EXAMPLE,\nDRAFTING MACHINES, PANTOGRAPHS, PROTRACTORS, DRAWING SETS, SLIDE RULES, DISC\nCALCULATORS); INSTRUMENTS FOR MEASURING LENGTH, FOR USE IN THE HAND (FOR EXAMPLE,\nMEASURING RODS AND TAPES, MICROMETERS, CALLIPERS), NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER - MICROMETERS, CALIPERS AND GAUGES: - GAUGES: OTHER",
+        "hsn_code": "90173029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DRAWING, MARKING-OUT OR MATHEMATICAL CALCULATING INSTRUMENTS (FOR EXAMPLE,\nDRAFTING MACHINES, PANTOGRAPHS, PROTRACTORS, DRAWING SETS, SLIDE RULES, DISC\nCALCULATORS); INSTRUMENTS FOR MEASURING LENGTH, FOR USE IN THE HAND (FOR EXAMPLE,\nMEASURING RODS AND TAPES, MICROMETERS, CALLIPERS), NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER - MICROMETERS, CALIPERS AND GAUGES: - GAUGES: SLIP",
+        "hsn_code": "90173023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DRAWING, MARKING-OUT OR MATHEMATICAL CALCULATING INSTRUMENTS (FOR EXAMPLE,\nDRAFTING MACHINES, PANTOGRAPHS, PROTRACTORS, DRAWING SETS, SLIDE RULES, DISC\nCALCULATORS); INSTRUMENTS FOR MEASURING LENGTH, FOR USE IN THE HAND (FOR EXAMPLE,\nMEASURING RODS AND TAPES, MICROMETERS, CALLIPERS), NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER - MICROMETERS, CALIPERS AND GAUGES: - GAUGES: RING",
+        "hsn_code": "90173022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DRAWING, MARKING-OUT OR MATHEMATICAL CALCULATING INSTRUMENTS (FOR EXAMPLE,\nDRAFTING MACHINES, PANTOGRAPHS, PROTRACTORS, DRAWING SETS, SLIDE RULES, DISC\nCALCULATORS); INSTRUMENTS FOR MEASURING LENGTH, FOR USE IN THE HAND (FOR EXAMPLE,\nMEASURING RODS AND TAPES, MICROMETERS, CALLIPERS), NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER - MICROMETERS, CALIPERS AND GAUGES: - GAUGES: PLUG",
+        "hsn_code": "90173021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DRAWING, MARKING-OUT OR MATHEMATICAL CALCULATING INSTRUMENTS (FOR EXAMPLE,\nDRAFTING MACHINES, PANTOGRAPHS, PROTRACTORS, DRAWING SETS, SLIDE RULES, DISC\nCALCULATORS); INSTRUMENTS FOR MEASURING LENGTH, FOR USE IN THE HAND (FOR EXAMPLE,\nMEASURING RODS AND TAPES, MICROMETERS, CALLIPERS), NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER - MICROMETERS, CALIPERS AND GAUGES: MICRO-METERS AND\nCALIPERS",
+        "hsn_code": "90173010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DRAWING, MARKING-OUT OR MATHEMATICAL CALCULATING INSTRUMENTS (FOR EXAMPLE,\nDRAFTING MACHINES, PANTOGRAPHS, PROTRACTORS, DRAWING SETS, SLIDE RULES, DISC\nCALCULATORS); INSTRUMENTS FOR MEASURING LENGTH, FOR USE IN THE HAND (FOR EXAMPLE,\nMEASURING RODS AND TAPES, MICROMETERS, CALLIPERS), NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER DRAFTING TABLES AND MACHINES, WHETHER OR NOT\nAUTOMATIC",
+        "hsn_code": "90171000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALANCES OF A SENSITIVITY OF 5cg OR BETTER, WITH OR WITHOUT WEIGHTS",
+        "hsn_code": "9016",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALANCES OF A SENSITIVITY OF 5 CG OR BETTER, WITH OR WITHOUT WEIGHTS - BALANCES OF A\nSENSITIVITY OF 5CG OR BETTER, WITH OR WITHOUT WEIGHTS: PARTS",
+        "hsn_code": "90160090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALANCES OF A SENSITIVITY OF 5 CG OR BETTER, WITH OR WITHOUT WEIGHTS - BALANCES OF A\nSENSITIVITY OF 5CG OR BETTER, WITH OR WITHOUT WEIGHTS: OTHER BALANCES",
+        "hsn_code": "90160020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALANCES OF A SENSITIVITY OF 5 CG OR BETTER, WITH OR WITHOUT WEIGHTS - BALANCES OF A\nSENSITIVITY OF 5CG OR BETTER, WITH OR WITHOUT WEIGHTS: ELECTRIC BALANCES",
+        "hsn_code": "90160010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SURVEYING (INCLUDING PHOTOGRAMMETRICAL SURVEYING), HYDROGRAPHIC OCEANOGRAPHIC, HYDROLOGICAL, METEROLOGICAL OR GEO-PHYSCIAL INSTRUMENTS AND APPLIANCES, EXCLUDING COMPASSES; RANGEFINDERS",
+        "hsn_code": "9015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SURVEYING (INCLUDING PHOTOGRAMMETRICAL SURVEYING), HYDROGRAPHIC,\nOCEANOGRAPHIC, HYDROLOGICAL, METEOROLOGICAL OR GEOPHYSICAL INSTRUMENTS AND\nAPPLIANCES, EXCLUDING COMPASSES; RANGEFINDERS PARTS AND ACCESSORIES",
+        "hsn_code": "90159000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SURVEYING (INCLUDING PHOTOGRAMMETRICAL SURVEYING), HYDROGRAPHIC,\nOCEANOGRAPHIC, HYDROLOGICAL, METEOROLOGICAL OR GEOPHYSICAL INSTRUMENTS AND\nAPPLIANCES, EXCLUDING COMPASSES; RANGEFINDERS - OTHER INSTRUMENTS AND\nAPPLIANCES: OTHER",
+        "hsn_code": "90158090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SURVEYING (INCLUDING PHOTOGRAMMETRICAL SURVEYING), HYDROGRAPHIC,\nOCEANOGRAPHIC, HYDROLOGICAL, METEOROLOGICAL OR GEOPHYSICAL INSTRUMENTS AND\nAPPLIANCES, EXCLUDING COMPASSES; RANGEFINDERS - OTHER INSTRUMENTS AND\nAPPLIANCES: GEOPHYSICAL INSTRUMENTS",
+        "hsn_code": "90158030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SURVEYING (INCLUDING PHOTOGRAMMETRICAL SURVEYING), HYDROGRAPHIC,\nOCEANOGRAPHIC, HYDROLOGICAL, METEOROLOGICAL OR GEOPHYSICAL INSTRUMENTS AND\nAPPLIANCES, EXCLUDING COMPASSES; RANGEFINDERS - OTHER INSTRUMENTS AND\nAPPLIANCES: METEOROLOGICAL INSTRUMENTS",
+        "hsn_code": "90158020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SURVEYING (INCLUDING PHOTOGRAMMETRICAL SURVEYING), HYDROGRAPHIC,\nOCEANOGRAPHIC, HYDROLOGICAL, METEOROLOGICAL OR GEOPHYSICAL INSTRUMENTS AND\nAPPLIANCES, EXCLUDING COMPASSES; RANGEFINDERS - OTHER INSTRUMENTS AND\nAPPLIANCES: HYDROGRAPHIC INSTRUMENTS",
+        "hsn_code": "90158010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SURVEYING (INCLUDING PHOTOGRAMMETRICAL SURVEYING), HYDROGRAPHIC,\nOCEANOGRAPHIC, HYDROLOGICAL, METEOROLOGICAL OR GEOPHYSICAL INSTRUMENTS AND\nAPPLIANCES, EXCLUDING COMPASSES; RANGEFINDERS PHOTOGRAMMETRICAL SURVEYING\nINSTRUMENTS AND APPLIANCES",
+        "hsn_code": "90154000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SURVEYING (INCLUDING PHOTOGRAMMETRICAL SURVEYING), HYDROGRAPHIC,\nOCEANOGRAPHIC, HYDROLOGICAL, METEOROLOGICAL OR GEOPHYSICAL INSTRUMENTS AND\nAPPLIANCES, EXCLUDING COMPASSES; RANGEFINDERS - LEVELS: OTHER",
+        "hsn_code": "90153090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SURVEYING (INCLUDING PHOTOGRAMMETRICAL SURVEYING), HYDROGRAPHIC,\nOCEANOGRAPHIC, HYDROLOGICAL, METEOROLOGICAL OR GEOPHYSICAL INSTRUMENTS AND\nAPPLIANCES, EXCLUDING COMPASSES; RANGEFINDERS - LEVELS: DUMPY LEVELS OR ENGINEER'S\nLEVELS OR BUILDERS LEVELS (NOT AUTOMATIC) AND QUICK SET LEVELS WITH OR WITHOUT\nHORIZONTAL CIRCLES",
+        "hsn_code": "90153010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SURVEYING (INCLUDING PHOTOGRAMMETRICAL SURVEYING), HYDROGRAPHIC,\nOCEANOGRAPHIC, HYDROLOGICAL, METEOROLOGICAL OR GEOPHYSICAL INSTRUMENTS AND\nAPPLIANCES, EXCLUDING COMPASSES; RANGEFINDERS THEODOLITIES AND TACHYMETERS\n(TACHEOMETERS)",
+        "hsn_code": "90152000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SURVEYING (INCLUDING PHOTOGRAMMETRICAL SURVEYING), HYDROGRAPHIC,\nOCEANOGRAPHIC, HYDROLOGICAL, METEOROLOGICAL OR GEOPHYSICAL INSTRUMENTS AND\nAPPLIANCES, EXCLUDING COMPASSES; RANGEFINDERS RANGEFINDERS",
+        "hsn_code": "90151000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIRECTION FINDING COMPASSES; OTHER NAVIGATIONAL INSTRUMENTS AND APPLIANCES",
+        "hsn_code": "9014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIRECTION FINDING COMPASSES; OTHER NAVIGATIONAL INSTRUMENTS AND APPLIANCES\nPARTS AND ACCESSORIES",
+        "hsn_code": "90149000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIRECTION FINDING COMPASSES; OTHER NAVIGATIONAL INSTRUMENTS AND APPLIANCES -\nOTHER INSTRUMENTS AND APPLIANCES: OTHER",
+        "hsn_code": "90148090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIRECTION FINDING COMPASSES; OTHER NAVIGATIONAL INSTRUMENTS AND APPLIANCES -\nOTHER INSTRUMENTS AND APPLIANCES: SEXTANTS",
+        "hsn_code": "90148020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIRECTION FINDING COMPASSES; OTHER NAVIGATIONAL INSTRUMENTS AND APPLIANCES -\nOTHER INSTRUMENTS AND APPLIANCES: ECHO SOUNDING INSTRUMENT",
+        "hsn_code": "90148010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIRECTION FINDING COMPASSES; OTHER NAVIGATIONAL INSTRUMENTS AND APPLIANCES\nINSTRUMENTS AND APPLIANCES FOR AERONAUTICAL OR SPACE NAVIGATION (OTHER THAN\nCOMPASSES)",
+        "hsn_code": "90142000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIRECTION FINDING COMPASSES; OTHER NAVIGATIONAL INSTRUMENTS AND APPLIANCES\nDIRECTION FINDING COMPASSES",
+        "hsn_code": "90141000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LIQUID CRYSTAL DEVICES NOT CONSTITUTING ARTICLES PROVIDED FOR MORE SPECIFICALLY IN OTHER HEADINGS; LASERS, OTHER THAN LASER DIODES; OTHER OPTICAL APPLIANCES AND INSTRUMENTS, NOT SPECIFIED OR INCLUDED ELSE WHERE IN THIS CHAPTER",
+        "hsn_code": "9013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LIQUID CRYSTAL DEVICES NOT CONSTITUTING ARTICLES PROVIDED FOR MORE SPECIFICALLY IN\nOTHER HEADINGS; LASERS, OTHER THAN LASER DIODES; OTHER OPTICAL APPLIANCES AND\nINSTRUMENTS, NOT SPECIFIED OR INCLUDED ELSE WHERE IN THIS CHAPTER - PARTS AND\nACCESSORIES: OTHER",
+        "hsn_code": "90139090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LIQUID CRYSTAL DEVICES NOT CONSTITUTING ARTICLES PROVIDED FOR MORE SPECIFICALLY IN\nOTHER HEADINGS; LASERS, OTHER THAN LASER DIODES; OTHER OPTICAL APPLIANCES AND\nINSTRUMENTS, NOT SPECIFIED OR INCLUDED ELSE WHERE IN THIS CHAPTER - PARTS AND\nACCESSORIES: FOR LIQUID CRYSTAL DEVICES (LCD)",
+        "hsn_code": "90139010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LIQUID CRYSTAL DEVICES NOT CONSTITUTING ARTICLES PROVIDED FOR MORE SPECIFICALLY IN\nOTHER HEADINGS; LASERS, OTHER THAN LASER DIODES; OTHER OPTICAL APPLIANCES AND\nINSTRUMENTS, NOT SPECIFIED OR INCLUDED ELSE WHERE IN THIS CHAPTER - OTHER DEVICES,\nAPPLIANCES AND INSTRUMENTS: OTHER",
+        "hsn_code": "90138090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LIQUID CRYSTAL DEVICES NOT CONSTITUTING ARTICLES PROVIDED FOR MORE SPECIFICALLY IN\nOTHER HEADINGS; LASERS, OTHER THAN LASER DIODES; OTHER OPTICAL APPLIANCES AND\nINSTRUMENTS, NOT SPECIFIED OR INCLUDED ELSE WHERE IN THIS CHAPTER - OTHER DEVICES,\nAPPLIANCES AND INSTRUMENTS: LIQUID CRYSTAL DEVICES (LCD)",
+        "hsn_code": "90138010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LIQUID CRYSTAL DEVICES NOT CONSTITUTING ARTICLES PROVIDED FOR MORE SPECIFICALLY IN\nOTHER HEADINGS; LASERS, OTHER THAN LASER DIODES; OTHER OPTICAL APPLIANCES AND\nINSTRUMENTS, NOT SPECIFIED OR INCLUDED ELSE WHERE IN THIS CHAPTER LASERS, OTHER\nTHAN LASER DIODES",
+        "hsn_code": "90132000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LIQUID CRYSTAL DEVICES NOT CONSTITUTING ARTICLES PROVIDED FOR MORE SPECIFICALLY IN\nOTHER HEADINGS; LASERS, OTHER THAN LASER DIODES; OTHER OPTICAL APPLIANCES AND\nINSTRUMENTS, NOT SPECIFIED OR INCLUDED ELSE WHERE IN THIS CHAPTER - TELESCOPIC\nSIGHTS FOR FITTING TO ARMS; PERISCOPES; TELESCOPES DESIGNED TO FORM PARTS OF\nMACHINES, APPLIANCES, INSTRUMENTS OR APPARATUS OF THIS CHAPTER OR SECTION XVI:\nOTHER",
+        "hsn_code": "90131090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LIQUID CRYSTAL DEVICES NOT CONSTITUTING ARTICLES PROVIDED FOR MORE SPECIFICALLY IN\nOTHER HEADINGS; LASERS, OTHER THAN LASER DIODES; OTHER OPTICAL APPLIANCES AND\nINSTRUMENTS, NOT SPECIFIED OR INCLUDED ELSE WHERE IN THIS CHAPTER - TELESCOPIC\nSIGHTS FOR FITTING TO ARMS; PERISCOPES; TELESCOPES DESIGNED TO FORM PARTS OF\nMACHINES, APPLIANCES, INSTRUMENTS OR APPARATUS OF THIS CHAPTER OR SECTION XVI:\nPERISCOPES",
+        "hsn_code": "90131020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LIQUID CRYSTAL DEVICES NOT CONSTITUTING ARTICLES PROVIDED FOR MORE SPECIFICALLY IN\nOTHER HEADINGS; LASERS, OTHER THAN LASER DIODES; OTHER OPTICAL APPLIANCES AND\nINSTRUMENTS, NOT SPECIFIED OR INCLUDED ELSE WHERE IN THIS CHAPTER - TELESCOPIC\nSIGHTS FOR FITTING TO ARMS; PERISCOPES; TELESCOPES DESIGNED TO FORM PARTS OF\nMACHINES, APPLIANCES, INSTRUMENTS OR APPARATUS OF THIS CHAPTER OR SECTION XVI:\nTELESCOPIC SIGHTS FOR FITTING TO ARMS",
+        "hsn_code": "90131010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MICROSCOPES OTHER THAN OPTICAL MICROSCOPES; DIFFRACTION APPARATUS",
+        "hsn_code": "9012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MICROSCOPES OTHER THAN OPTICAL MICROSCOPES; DIFFRACTION APPARATUS PARTS AND\nACCESSORIES",
+        "hsn_code": "90129000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MICROSCOPES OTHER THAN OPTICAL MICROSCOPES; DIFFRACTION APPARATUS - MICROSCOPES\nOTHER THAN OPTICAL MICROSCOPES; DIFFRACTION APPARATUS: OTHER",
+        "hsn_code": "90121090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MICROSCOPES OTHER THAN OPTICAL MICROSCOPES; DIFFRACTION APPARATUS - MICROSCOPES\nOTHER THAN OPTICAL MICROSCOPES; DIFFRACTION APPARATUS: ELECTRON MICROSCOPES\nFITTED WITH EQUIPMENT SPECIFICALLY DESIGNED FOR THE HANDLING AND TRANSPORT OF\nSEMICONDUCTOR WAFERS OR RETICLES",
+        "hsn_code": "90121010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, roasted : Decaffeinated : Other",
+        "hsn_code": "9012290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, roasted : Decaffeinated : In bulk\npacking",
+        "hsn_code": "9012210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, roasted : Not decaffeinated :\nOther",
+        "hsn_code": "9012190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, roasted : Not decaffeinated : In\nbulk packing",
+        "hsn_code": "9012110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPOUND OPTICAL MICROSCOPES, INCLUDING THOSE FOR PHOTOMICRO-GRAPHY, CINEPHOTOMICROGRAPHY OR MICROPROJECTION",
+        "hsn_code": "9011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPOUND OPTICAL MICROSCOPES, INCLUDING THOSE FOR PHOTOMICRO-GRAPHY,\nCINEPHOTOMICROGRAPHY OR MICROPROJECTION - PARTS AND ACCESSORIES",
+        "hsn_code": "90119000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPOUND OPTICAL MICROSCOPES, INCLUDING THOSE FOR PHOTOMICRO-GRAPHY,\nCINEPHOTOMICROGRAPHY OR MICROPROJECTION - OTHER MICROSCOPES",
+        "hsn_code": "90118000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPOUND OPTICAL MICROSCOPES, INCLUDING THOSE FOR PHOTOMICRO-GRAPHY,\nCINEPHOTOMICROGRAPHY OR MICROPROJECTION - OTHER MICROSCOPES, FOR\nPHOTOMICROGRAPHY, CINEPHOTOMICROGRAPHY OR MICROPROJECTION",
+        "hsn_code": "90112000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPOUND OPTICAL MICROSCOPES, INCLUDING THOSE FOR PHOTOMICRO-GRAPHY,\nCINEPHOTOMICROGRAPHY OR MICROPROJECTION - STEREOSCOPIC MICROSCOPES",
+        "hsn_code": "90111000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Decaffeinated",
+        "hsn_code": "9011200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Other",
+        "hsn_code": "9011190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Rob cherry : Other",
+        "hsn_code": "9011149",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Rob cherry : Bulk",
+        "hsn_code": "9011145",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Rob cherry : B/B/B Grade",
+        "hsn_code": "9011144",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Rob cherry : C Grade",
+        "hsn_code": "9011143",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Rob cherry : PB Grade",
+        "hsn_code": "9011142",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Rob cherry : AB Grade",
+        "hsn_code": "9011141",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Rob Parchment : Other",
+        "hsn_code": "9011139",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Rob Parchment : C Grade",
+        "hsn_code": "9011133",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Rob Parchment : PB Grade",
+        "hsn_code": "9011132",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Rob Parchment : AB Grade",
+        "hsn_code": "9011131",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Arabica cherry : Other",
+        "hsn_code": "9011129",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Arabica cherry : B/B/B Grade",
+        "hsn_code": "9011124",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Arabica cherry : C Grade",
+        "hsn_code": "9011123",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Arabica cherry : PB Grade",
+        "hsn_code": "9011122",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Arabica cherry : AB Grade",
+        "hsn_code": "9011121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Arabica plantation : Other",
+        "hsn_code": "9011119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Arabica plantation : C Grade",
+        "hsn_code": "9011113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Arabica plantation : B Grade",
+        "hsn_code": "9011112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DACAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE\nSUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION Coffee, not roasted : Not decaffeinated\n: Arabica plantation : A Grade",
+        "hsn_code": "9011111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS AND EQUIPMENT FOR PHOTOGRAPHIC (INCLUDING CINEMATOGRAPHIC) LABORATORIES  NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER; NEGATOSCOPES; PROJECTION SCREENS",
+        "hsn_code": "9010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS AND EQUIPMENT FOR PHOTOGRAPHIC (INCLUDING CINEMATOGRAPHIC)\nLABORATORIES (INCLUDING APPARATUS FOR THE PROJECTION OR DRAWING OF CIRCUIT\nPATTERNS ON SENSITISED SEMICONDUCTOR MATERIALS), NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; NEGATOSCOPES; PROJECTION SCREENS PARTS AND ACCESSORIES",
+        "hsn_code": "90109000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS AND EQUIPMENT FOR PHOTOGRAPHIC (INCLUDING CINEMATOGRAPHIC)\nLABORATORIES (INCLUDING APPARATUS FOR THE PROJECTION OR DRAWING OF CIRCUIT\nPATTERNS ON SENSITISED SEMICONDUCTOR MATERIALS), NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; NEGATOSCOPES; PROJECTION SCREENS PROJECTION SCREENS",
+        "hsn_code": "90106000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS AND EQUIPMENT FOR PHOTOGRAPHIC (INCLUDING CINEMATOGRAPHIC)\nLABORATORIES (INCLUDING APPARATUS FOR THE PROJECTION OR DRAWING OF CIRCUIT\nPATTERNS ON SENSITISED SEMICONDUCTOR MATERIALS), NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; NEGATOSCOPES; PROJECTION SCREENS OTHER APPARATUS AND\nEQUIPMENT FOR PHOTOGRAPHIC (INCLUDING CINEMATOGRAPHIC) LABORATORIES;\nNEGATOSCOPES",
+        "hsn_code": "90105000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS AND EQUIPMENT FOR PHOTOGRAPHIC (INCLUDING CINEMATOGRAPHIC)\nLABORATORIES (INCLUDING APPARATUS FOR THE PROJECTION OR DRAWING OF CIRCUIT\nPATTERNS ON SENSITISED SEMICONDUCTOR MATERIALS), NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; NEGATOSCOPES; PROJECTION SCREENS - APPARATUS FOR THE\nPROJECTION OR DRAWING OF CIRCUIT PATTERNS ON SENSITISED SEMICONDUCTOR MATERIALS:\nOTHER",
+        "hsn_code": "90104900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS AND EQUIPMENT FOR PHOTOGRAPHIC (INCLUDING CINEMATOGRAPHIC)\nLABORATORIES (INCLUDING APPARATUS FOR THE PROJECTION OR DRAWING OF CIRCUIT\nPATTERNS ON SENSITISED SEMICONDUCTOR MATERIALS), NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; NEGATOSCOPES; PROJECTION SCREENS - APPARATUS FOR THE\nPROJECTION OR DRAWING OF CIRCUIT PATTERNS ON SENSITISED SEMICONDUCTOR MATERIALS:\nSTEP AND REPEAT ALIGNERS",
+        "hsn_code": "90104200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS AND EQUIPMENT FOR PHOTOGRAPHIC (INCLUDING CINEMATOGRAPHIC)\nLABORATORIES (INCLUDING APPARATUS FOR THE PROJECTION OR DRAWING OF CIRCUIT\nPATTERNS ON SENSITISED SEMICONDUCTOR MATERIALS), NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER; NEGATOSCOPES; PROJECTION SCREENS - APPARATUS FOR THE\nPROJECTION OR DRAWING OF CIRCUIT PATTERNS ON SENSITISED SEMICONDUCTOR MATERIALS:\nDIRECT WRITE-ON-WAFER APPARATUS",
+        "hsn_code": "90104100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "APPARATUS AND EQUIPMENT FOR PHOTOGRAPHIC (INCLUDING CINEMATOGRAPHIC)\nLABORATORIES, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER; NEGATOSCOPES;\nPROJECTION SCREENS - APPARATUS AND EQUIPMENT FOR AUTOMATICALLY DEVELOPING\nPHOTOGRAPHIC (INCLUDING CINEMATOGRAPHIC) FILM OR PAPER IN ROLLS OR FOR\nAUTOMATICALLY EXPOSING DEVELOPED FILM TO ROLLS OF PHOTOGRAPHIC PAPER",
+        "hsn_code": "90101000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IMAGE PROJECTORS, OTHER THAN CINEMATOGRAPHIC; PHOTOGRAPHIC (OTHER THAN CINEMATOGRAPHIC) ENLARGERS AND REDUCERS",
+        "hsn_code": "9008",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IMAGE PROJECTORS, OTHER THAN CINEMATOGRAPHIC; PHOTOGRAPHIC (OTHER THAN\nCINEMATOGRAPHIC) ENLARGERS AND REDUCERS PARTS AND ACCESSORIES",
+        "hsn_code": "90089000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC (OTHER THAN CINEMATOGRAPHIC) ENLARGERS AND REDUCERS",
+        "hsn_code": "90085040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER IMAGE PROJECTORS",
+        "hsn_code": "90085030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MICROFILM, MICROFICHE OR OTHER MICROFORM READERS, WHETHER OR NOT CAPABLE OF\nPRODUCING COPIES",
+        "hsn_code": "90085020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLIDE PROJECTORS",
+        "hsn_code": "90085010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC CAMERAS AND PROJECTORS WHETHER OR NOT INCORPORATING SOUND RECORDING OR REPRODUCING APPARATUS",
+        "hsn_code": "9007",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC CAMERAS AND PROJECTORS, WHETHER OR NOT INCORPORATING SOUND\nRECORDING OR REPRODUCING APPARATUS - PARTS AND ACCESSORIES: FOR PROJECTORS",
+        "hsn_code": "90079200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC CAMERAS AND PROJECTORS, WHETHER OR NOT INCORPORATING SOUND\nRECORDING OR REPRODUCING APPARATUS - PARTS AND ACCESSORIES: FOR CAMERAS",
+        "hsn_code": "90079100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC CAMERAS AND PROJECTORS, WHETHER OR NOT INCORPORATING SOUND\nRECORDING OR REPRODUCING APPARATUS - PROJECTORS: OTHER",
+        "hsn_code": "90072090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC CAMERAS AND PROJECTORS, WHETHER OR NOT INCORPORATING SOUND\nRECORDING OR REPRODUCING APPARATUS - PROJECTORS: FOR FILM OF LESS THAN 16 MM\nWIDTH",
+        "hsn_code": "90072010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "90071090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOR FILM OF LESS THAN 16 MM WIDTH OR FOR DOUBLE- 8 MM FILM",
+        "hsn_code": "90071010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC (OTHER THAN CINEMATOGRAPHIC) CAMERAS; PHOTOGRAPHIC FLASHLIGHT APPARATUS AND FLASH BULBS OTHER THAN DISCHARGE LAMPS OF HEADING 8539",
+        "hsn_code": "9006",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC (OTHER THAN CINEMATOGRAPHIC) CAMERAS; PHOTOGRAPHIC FLASHLIGHT\nAPPARATUS AND FLASH BULBS OTHER THAN DISCHARGE LAMPS OF HEADING 8539 - PARTS AND\nACCESSORIES: OTHER",
+        "hsn_code": "90069900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC (OTHER THAN CINEMATOGRAPHIC) CAMERAS; PHOTOGRAPHIC FLASHLIGHT\nAPPARATUS AND FLASH BULBS OTHER THAN DISCHARGE LAMPS OF HEADING 8539 - PARTS AND\nACCESSORIES: FOR CAMERAS",
+        "hsn_code": "90069100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC (OTHER THAN CINEMATOGRAPHIC) CAMERAS; PHOTOGRAPHIC FLASHLIGHT\nAPPARATUS AND FLASH BULBS OTHER THAN DISCHARGE LAMPS OF HEADING 8539 -\nPHOTOGRAPHIC FLASHLIGHT APPARATUS AND FLASHBULBS: OTHER",
+        "hsn_code": "90066900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC (OTHER THAN CINEMATOGRAPHIC) CAMERAS; PHOTOGRAPHIC FLASHLIGHT\nAPPARATUS AND FLASH BULBS OTHER THAN DISCHARGE LAMPS OF HEADING 8539 -\nPHOTOGRAPHIC FLASHLIGHT APPARATUS AND FLASHBULBS: FLASHBULBS, FLASHCUBES AND\nTHE LIKE",
+        "hsn_code": "90066200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC (OTHER THAN CINEMATOGRAPHIC) CAMERAS; PHOTOGRAPHIC FLASHLIGHT\nAPPARATUS AND FLASH BULBS OTHER THAN DISCHARGE LAMPS OF HEADING 8539 -\nPHOTOGRAPHIC FLASHLIGHT APPARATUS AND FLASHBULBS: DISCHARGE LAMP (\"ELECTRONIC\")\nFLASHLIGHT APPARATUS",
+        "hsn_code": "90066100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC (OTHER THAN CINEMATOGRAPHIC) CAMERAS; PHOTOGRAPHIC FLASHLIGHT\nAPPARATUS AND FLASH BULBS OTHER THAN DISCHARGE LAMPS OF HEADING 8539 - OTHER\nCAMERAS: - OTHER: OTHER",
+        "hsn_code": "90065990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC (OTHER THAN CINEMATOGRAPHIC) CAMERAS; PHOTOGRAPHIC FLASHLIGHT\nAPPARATUS AND FLASH BULBS OTHER THAN DISCHARGE LAMPS OF HEADING 8539 - OTHER\nCAMERAS: - OTHER: FIXED FOCUS 110 MM CAMERAS",
+        "hsn_code": "90065910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC (OTHER THAN CINEMATOGRAPHIC) CAMERAS; PHOTOGRAPHIC FLASHLIGHT\nAPPARATUS AND FLASH BULBS OTHER THAN DISCHARGE LAMPS OF HEADING 8539 - OTHER\nCAMERAS: - OTHER, FOR ROLL FILM OF A WIDTH OF 35 MM: OTHER",
+        "hsn_code": "90065390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC (OTHER THAN CINEMATOGRAPHIC) CAMERAS; PHOTOGRAPHIC FLASHLIGHT\nAPPARATUS AND FLASH BULBS OTHER THAN DISCHARGE LAMPS OF HEADING 8539 - OTHER\nCAMERAS: - OTHER, FOR ROLL FILM OF A WIDTH OF 35 MM: FIXED FOCUS 35 MM CAMERAS",
+        "hsn_code": "90065310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC (OTHER THAN CINEMATOGRAPHIC) CAMERAS; PHOTOGRAPHIC FLASHLIGHT\nAPPARATUS AND FLASH BULBS OTHER THAN DISCHARGE LAMPS OF HEADING 8539 - OTHER\nCAMERAS: OTHER, FOR ROLL FILM OF A WIDTH LESS THAN 35 MM",
+        "hsn_code": "90065200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC (OTHER THAN CINEMATOGRAPHIC) CAMERAS; PHOTOGRAPHIC FLASHLIGHT\nAPPARATUS AND FLASH BULBS OTHER THAN DISCHARGE LAMPS OF HEADING 8539 - OTHER\nCAMERAS: WITH A THROUGH-THE-LENS VIEW-FINDER [SINGLE LENS REFLEX (SLR)] FOR ROLL\nFILM OF A WIDTH NOT EXCEEDING 35 MM",
+        "hsn_code": "90065100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC (OTHER THAN CINEMATOGRAPHIC) CAMERAS; PHOTOGRAPHIC FLASHLIGHT\nAPPARATUS AND FLASH BULBS OTHER THAN DISCHARGE LAMPS OF HEADING 8539 INSTANT\nPRINT CAMERAS",
+        "hsn_code": "90064000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC (OTHER THAN CINEMATOGRAPHIC) CAMERAS; PHOTOGRAPHIC FLASHLIGHT\nAPPARATUS AND FLASH BULBS OTHER THAN DISCHARGE LAMPS OF HEADING 8539 CAMERAS\nSPECIALLY DESIGNED FOR UNDERWATER USE, FOR AERIAL SURVEY OR FOR MEDICAL OR\nSURGICAL EXAMINATION OF INTERNAL ORGANS; COMPARISON CAMERAS FOR FORENSIC OR\nCRIMINOLOGICAL PURPOSES",
+        "hsn_code": "90063000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC (OTHER THAN CINEMATOGRAPHIC) CAMERAS; PHOTOGRAPHIC FLASHLIGHT\nAPPARATUS AND FLASH BULBS OTHER THAN DISCHARGE LAMPS OF HEADING 8539 CAMERAS OF\nA KIND USED FOR RECORDING DOCUMENTS ON MICROFILM, MICROFICHE OR OTHER\nMICROFORMS",
+        "hsn_code": "90062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC (OTHER THAN CINEMATOGRAPHIC) CAMERAS; PHOTOGRAPHIC FLASHLIGHT\nAPPARATUS AND FLASH BULBS OTHER THAN DISCHARGE LAMPS OF HEADING 8539 CAMERAS OF\nA KIND USED FOR PREPARING PRINTING PLATES OR CYLINDERS",
+        "hsn_code": "90061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BINOCULARS, MONOCULARS, OTHER OPTICAL TELESCOPES AND MOUNTINGS THEREFOR; OTHER ASTRONOMICAL INSTRUMENTS AND MOUNTINGS THEREFOR, BUT NOT INCLUDING INSTRUMENTS FOR RADIO-ASTRONOMY",
+        "hsn_code": "9005",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BINOCULARS, MONOCULARS, OTHER OPTICAL TELESCOPES, AND MOUNTINGS THEREFOR;\nOTHER ASTRONOMICAL INSTRUMENTS AND MOUNTINGS THEREFOR, BUT NOT INCLUDING\nINSTRUMENTS FOR RADIO-ASTRONOMY PARTS AND ACCESSORIES (INCLUDING MOUNTINGS):\nOTHER",
+        "hsn_code": "90059090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BINOCULARS, MONOCULARS, OTHER OPTICAL TELESCOPES, AND MOUNTINGS THEREFOR;\nOTHER ASTRONOMICAL INSTRUMENTS AND MOUNTINGS THEREFOR, BUT NOT INCLUDING\nINSTRUMENTS FOR RADIO-ASTRONOMY PARTS AND ACCESSORIES (INCLUDING MOUNTINGS):\nMOUNTINGS FOR ASTRONOMICAL INSTRUMENTS",
+        "hsn_code": "90059020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BINOCULARS, MONOCULARS, OTHER OPTICAL TELESCOPES, AND MOUNTINGS THEREFOR;\nOTHER ASTRONOMICAL INSTRUMENTS AND MOUNTINGS THEREFOR, BUT NOT INCLUDING\nINSTRUMENTS FOR RADIO-ASTRONOMY PARTS AND ACCESSORIES (INCLUDING MOUNTINGS): OF\nBINOCULAR OR TELESCOPES OF HEADING 9005, OTHER THAN MOUNTINGS",
+        "hsn_code": "90059010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BINOCULARS, MONOCULARS, OTHER OPTICAL TELESCOPES, AND MOUNTINGS THEREFOR;\nOTHER ASTRONOMICAL INSTRUMENTS AND MOUNTINGS THEREFOR, BUT NOT INCLUDING\nINSTRUMENTS FOR RADIO-ASTRONOMY - OTHER INSTRUMENTS: OTHER",
+        "hsn_code": "90058090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BINOCULARS, MONOCULARS, OTHER OPTICAL TELESCOPES, AND MOUNTINGS THEREFOR;\nOTHER ASTRONOMICAL INSTRUMENTS AND MOUNTINGS THEREFOR, BUT NOT INCLUDING\nINSTRUMENTS FOR RADIO-ASTRONOMY - OTHER INSTRUMENTS: ASTRONOMICAL INSTRUMENTS",
+        "hsn_code": "90058020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BINOCULARS, MONOCULARS, OTHER OPTICAL TELESCOPES, AND MOUNTINGS THEREFOR;\nOTHER ASTRONOMICAL INSTRUMENTS AND MOUNTINGS THEREFOR, BUT NOT INCLUDING\nINSTRUMENTS FOR RADIO-ASTRONOMY - OTHER INSTRUMENTS: MONOCULAR AND\nREFRACTING TELESCOPES",
+        "hsn_code": "90058010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BINOCULARS, MONOCULARS, OTHER OPTICAL TELESCOPES, AND MOUNTINGS THEREFOR;\nOTHER ASTRONOMICAL INSTRUMENTS AND MOUNTINGS THEREFOR, BUT NOT INCLUDING\nINSTRUMENTS FOR RADIO-ASTRONOMY BINOCULARS",
+        "hsn_code": "90051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SPECTACLES, GOGGLES AND THE LIKE, CORRECTIVE, PROTECTIVE OR OTHER",
+        "hsn_code": "9004",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SPECTACLES, GOGGLES AND THE LIKE, CORRECTIVE, PROTECTIVE OR OTHER - OTHER: OTHER",
+        "hsn_code": "90049090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SPECTACLES, GOGGLES AND THE LIKE, CORRECTIVE, PROTECTIVE OR OTHER - OTHER: PRISMATIC\nEYEGLASSES FOR READING",
+        "hsn_code": "90049020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SPECTACLES, GOGGLES AND THE LIKE, CORRECTIVE, PROTECTIVE OR OTHER - OTHER: PASSIVE\nNIGHT VISION GOGGLES",
+        "hsn_code": "90049010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SPECTACLES, GOGGLES AND THE LIKE, CORRECTIVE, PROTECTIVE OR OTHER SUNGLASSES",
+        "hsn_code": "90041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LENSES, PRISMS, MIRRORS AND OTHER OPTICAL ELEMENTS, OF ANY MATERIAL, MOUNTED, BEING PARTS OF OR FITTINGS FOR INSTRUMENTS OR APPARATUS, OTHER THAN SUCH ELEMENTS OF GLASS NOT OPTICALLY WORKED",
+        "hsn_code": "9002",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LENSES, PRISMS, MIRRORS AND OTHER OPTICAL ELEMENTS, OF ANY MATERIAL, MOUNTED,\nBEING PARTS OF OR FITTINGS FOR INSTRUMENTS OR APPARATUS, OTHER THAN SUCH\nELEMENTS OF GLASS NOT OPTICALLY WORKED OTHER",
+        "hsn_code": "90029000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LENSES, PRISMS, MIRRORS AND OTHER OPTICAL ELEMENTS, OF ANY MATERIAL, MOUNTED,\nBEING PARTS OF OR FITTINGS FOR INSTRUMENTS OR APPARATUS, OTHER THAN SUCH\nELEMENTS OF GLASS NOT OPTICALLY WORKED - OBJECTIVE LENSES: FILTERS",
+        "hsn_code": "90022000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LENSES, PRISMS, MIRRORS AND OTHER OPTICAL ELEMENTS, OF ANY MATERIAL, MOUNTED,\nBEING PARTS OF OR FITTINGS FOR INSTRUMENTS OR APPARATUS, OTHER THAN SUCH\nELEMENTS OF GLASS NOT OPTICALLY WORKED - OBJECTIVE LENSES: OTHER",
+        "hsn_code": "90021900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LENSES, PRISMS, MIRRORS AND OTHER OPTICAL ELEMENTS, OF ANY MATERIAL, MOUNTED,\nBEING PARTS OF OR FITTINGS FOR INSTRUMENTS OR APPARATUS, OTHER THAN SUCH\nELEMENTS OF GLASS NOT OPTICALLY WORKED - OBJECTIVE LENSES: FOR CAMERAS, PROJECTORS\nOR PHOTOGRAPHIC ENLARGERS OR REDUCERS",
+        "hsn_code": "90021100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OPTICAL FIBRES AND OPTICAL FIBRE BUNDLES; OPTICAL FIBRE CABLES OTHER THAN THOSE OF HEADING NO 8544; SHEETS AND PLATES OF POLARISING MATERIAL; LENSES (INCLUDING CONTACT LENSES), PRISMS, MIRRORS AND OTHER OPTICAL ELEMENTS, OF ANY MATERIAL, UNMOUNTED, OTHER THAN SUCH ELEMENTS OF GLASS NOT OPTICALLY WORKED",
+        "hsn_code": "9001",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OPTICAL FIBRES AND OPTICAL FIBRE BUNDLES; OPTICAL FIBRE CABLES OTHER THAN THOSE OF\nHEADING 8544; SHEETS AND PLATES OF POLARISING MATERIAL; LENSES (INCLUDING CONTACT\nLENSES), PRISMS, MIRRORS AND OTHER OPTICAL ELEMENTS, OF ANY MATERIAL, UNMOUNTED,\nOTHER THAN SUCH ELEMENTS OF GLASS NOT OPTICALLY WORKED - OTHER: OTHER",
+        "hsn_code": "90019090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OPTICAL FIBRES AND OPTICAL FIBRE BUNDLES; OPTICAL FIBRE CABLES OTHER THAN THOSE OF\nHEADING 8544; SHEETS AND PLATES OF POLARISING MATERIAL; LENSES (INCLUDING CONTACT\nLENSES), PRISMS, MIRRORS AND OTHER OPTICAL ELEMENTS, OF ANY MATERIAL, UNMOUNTED,\nOTHER THAN SUCH ELEMENTS OF GLASS NOT OPTICALLY WORKED - OTHER: OPTICAL CALCITE\nCRYSTAL",
+        "hsn_code": "90019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OPTICAL FIBRES AND OPTICAL FIBRE BUNDLES; OPTICAL FIBRE CABLES OTHER THAN THOSE OF\nHEADING 8544; SHEETS AND PLATES OF POLARISING MATERIAL; LENSES (INCLUDING CONTACT\nLENSES), PRISMS, MIRRORS AND OTHER OPTICAL ELEMENTS, OF ANY MATERIAL, UNMOUNTED,\nOTHER THAN SUCH ELEMENTS OF GLASS NOT OPTICALLY WORKED SPECTACLE LENSES OF OTHER\nMATERIALS",
+        "hsn_code": "90015000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OPTICAL FIBRES AND OPTICAL FIBRE BUNDLES; OPTICAL FIBRE CABLES OTHER THAN THOSE OF\nHEADING 8544; SHEETS AND PLATES OF POLARISING MATERIAL; LENSES (INCLUDING CONTACT\nLENSES), PRISMS, MIRRORS AND OTHER OPTICAL ELEMENTS, OF ANY MATERIAL, UNMOUNTED,\nOTHER THAN SUCH ELEMENTS OF GLASS NOT OPTICALLY WORKED - SPECTACLE LENSES OF\nGLASS : OTHER",
+        "hsn_code": "90014090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OPTICAL FIBRES AND OPTICAL FIBRE BUNDLES; OPTICAL FIBRE CABLES OTHER THAN THOSE OF\nHEADING 8544; SHEETS AND PLATES OF POLARISING MATERIAL; LENSES (INCLUDING CONTACT\nLENSES), PRISMS, MIRRORS AND OTHER OPTICAL ELEMENTS, OF ANY MATERIAL, UNMOUNTED,\nOTHER THAN SUCH ELEMENTS OF GLASS NOT OPTICALLY WORKED - SPECTACLE LENSES OF\nGLASS : POLARISED GLASS",
+        "hsn_code": "90014010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OPTICAL FIBRES AND OPTICAL FIBRE BUNDLES; OPTICAL FIBRE CABLES OTHER THAN THOSE OF\nHEADING 8544; SHEETS AND PLATES OF POLARISING MATERIAL; LENSES (INCLUDING CONTACT\nLENSES), PRISMS, MIRRORS AND OTHER OPTICAL ELEMENTS, OF ANY MATERIAL, UNMOUNTED,\nOTHER THAN SUCH ELEMENTS OF GLASS NOT OPTICALLY WORKED CONTACT LENSES",
+        "hsn_code": "90013000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OPTICAL FIBRES AND OPTICAL FIBRE BUNDLES; OPTICAL FIBRE CABLES OTHER THAN THOSE OF\nHEADING 8544; SHEETS AND PLATES OF POLARISING MATERIAL; LENSES (INCLUDING CONTACT\nLENSES), PRISMS, MIRRORS AND OTHER OPTICAL ELEMENTS, OF ANY MATERIAL, UNMOUNTED,\nOTHER THAN SUCH ELEMENTS OF GLASS NOT OPTICALLY WORKED SHEETS AND PLATES OF\nPOLARISING MATERIAL",
+        "hsn_code": "90012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OPTICAL FIBRES AND OPTICAL FIBRE BUNDLES; OPTICAL FIBRE CABLES OTHER THAN THOSE OF\nHEADING 8544; SHEETS AND PLATES OF POLARISING MATERIAL; LENSES (INCLUDING CONTACT\nLENSES), PRISMS, MIRRORS AND OTHER OPTICAL ELEMENTS, OF ANY MATERIAL, UNMOUNTED,\nOTHER THAN SUCH ELEMENTS OF GLASS NOT OPTICALLY WORKED OPTICAL FIBRES, OPTICAL\nFIBRES BUNDLES AND CABLES",
+        "hsn_code": "90011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Boats, floating structures, other (for e.g. rafts, tanks, coffer-dams, landing stages, buoys and beacons)",
+        "hsn_code": "8907",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FLOATING STRUCTURES ( FOR EXAMPLE, RAFTS, TANKS, COFFER-DAMS, LANDING\nSTAGES, BUOYS AND BEACONS) OTHER",
+        "hsn_code": "89079000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FLOATING STRUCTURES ( FOR EXAMPLE, RAFTS, TANKS, COFFER-DAMS, LANDING\nSTAGES, BUOYS AND BEACONS) INFLATABLE RAFTS",
+        "hsn_code": "89071000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER VESSELS, INCLUDING WARSHIPS AND LIFEBOATS OTHER THAN ROWING BOATS",
+        "hsn_code": "8906",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER VESSELS, INCLUDING WARSHIPS AND LIFEBOATS OTHER THAN ROWING BOATS OTHER",
+        "hsn_code": "89069000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER VESSELS, INCLUDING WARSHIPS AND LIFEBOATS OTHER THAN ROWING BOATS\nWARSHIPS",
+        "hsn_code": "89061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIGHT-VESSELS, FIRE-FLOATS, DREDGERS, FLOATING CRANES, AND OTHER VESSELS THE NAVIGABILITY OF WHICH IS SUBSIDIARY TO THEIR MAIN FUNCTION; FLOATING DOCKS; FLOATING OR SUBMERSIBLE DRILLING OR PRODUCTION PLATFORMS",
+        "hsn_code": "8905",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIGHT-VESSELS, FIRE-FLOATS, DREDGERS, FLOATING CRANES, AND OTHER VESSELS THE\nNAVIGABILITY OF WHICH IS SUBSIDIARY TO THEIR MAIN FUNCTION; FLOATING DOCKS; FLOATING\nOR SUBMERSIBLE DRILLING OR PRODUCTION PLATFORMS - OTHER : OTHER",
+        "hsn_code": "89059090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIGHT-VESSELS, FIRE-FLOATS, DREDGERS, FLOATING CRANES, AND OTHER VESSELS THE\nNAVIGABILITY OF WHICH IS SUBSIDIARY TO THEIR MAIN FUNCTION; FLOATING DOCKS; FLOATING\nOR SUBMERSIBLE DRILLING OR PRODUCTION PLATFORMS - OTHER : FLOATING DOCKS",
+        "hsn_code": "89059010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIGHT-VESSELS, FIRE-FLOATS, DREDGERS, FLOATING CRANES, AND OTHER VESSELS THE\nNAVIGABILITY OF WHICH IS SUBSIDIARY TO THEIR MAIN FUNCTION; FLOATING DOCKS; FLOATING\nOR SUBMERSIBLE DRILLING OR PRODUCTION PLATFORMS FLOATING OR SUBMERSIBLE DRILLING\nOR PRODUCTION PLATFORMS",
+        "hsn_code": "89052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIGHT-VESSELS, FIRE-FLOATS, DREDGERS, FLOATING CRANES, AND OTHER VESSELS THE\nNAVIGABILITY OF WHICH IS SUBSIDIARY TO THEIR MAIN FUNCTION; FLOATING DOCKS; FLOATING\nOR SUBMERSIBLE DRILLING OR PRODUCTION PLATFORMS DREDGERS",
+        "hsn_code": "89051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "Tugs and Pusher craft",
+        "hsn_code": "8904",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TUGS AND PUSHER CRAFT",
+        "hsn_code": "89040000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISHING VESSELS; FACTORY SHIPS AND OTHER VESSELS FOR PROCESSING OR PRESERVING FISHERY PRODUCTS",
+        "hsn_code": "8902",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISHING VESSELS; FACTORY SHIPS AND OTHER VESSELS FOR PROCESSING OR PRESERVING\nFISHERY PRODUCTS 8902 00 - FISHING VESSELS; FACTORY SHIPS AND OTHER VESSELS FOR\nPROCESSING OR PRESERVING FISHERY PRODUCTS : OTHER",
+        "hsn_code": "89020090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FISHING VESSELS; FACTORY SHIPS AND OTHER VESSELS FOR PROCESSING OR PRESERVING\nFISHERY PRODUCTS 8902 00 - FISHING VESSELS; FACTORY SHIPS AND OTHER VESSELS FOR\nPROCESSING OR PRESERVING FISHERY PRODUCTS : TRAWLERS AND OTHER FISHING VESSELS",
+        "hsn_code": "89020010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUISE SHIPS, EXCURSION BOATS, FERRY-BOATS, CARGO SHIPS, BARGES AND SIMILAR VESSELS FOR THE TRANSPORT OF PERSONS OR GOODS",
+        "hsn_code": "8901",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUISE SHIPS, EXCURSION BOATS , FERRY-BOATS, CARGO SHIPS, BARGES AND SIMILAR VESSELS\nFOR THE TRANSPORT OF PERSONS OR GOODS - OTHER VESSELS FOR TRANSPORT OF THE GOODS\nAND OTHER VESSELS FOR THE TRANSPORT OF BOTH PERSONS AND GOODS",
+        "hsn_code": "89019000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUISE SHIPS, EXCURSION BOATS , FERRY-BOATS, CARGO SHIPS, BARGES AND SIMILAR VESSELS\nFOR THE TRANSPORT OF PERSONS OR GOODS - REFRIGERATED VESSELS, OTHER THAN THOSE OF\nSUB-HEADING 8901 20",
+        "hsn_code": "89013000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUISE SHIPS, EXCURSION BOATS , FERRY-BOATS, CARGO SHIPS, BARGES AND SIMILAR VESSELS\nFOR THE TRANSPORT OF PERSONS OR GOODS - TANKERS",
+        "hsn_code": "89012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUISE SHIPS, EXCURSION BOATS , FERRY-BOATS, CARGO SHIPS, BARGES AND SIMILAR VESSELS\nFOR THE TRANSPORT OF PERSONS OR GOODS - CRUISE SHIPS, EXCURSION BOATS AND SIMILAR\nVESSELS PRINCIPALLY DESIGNED FOR THE TRANSPORT OF PERSONS; FERRY-BOATS OF ALL KINDS:\nOTHER",
+        "hsn_code": "89011090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUISE SHIPS, EXCURSION BOATS , FERRY-BOATS, CARGO SHIPS, BARGES AND SIMILAR VESSELS\nFOR THE TRANSPORT OF PERSONS OR GOODS - CRUISE SHIPS, EXCURSION BOATS AND SIMILAR\nVESSELS PRINCIPALLY DESIGNED FOR THE TRANSPORT OF PERSONS; FERRY-BOATS OF ALL KINDS:\nBARGES",
+        "hsn_code": "89011040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUISE SHIPS, EXCURSION BOATS , FERRY-BOATS, CARGO SHIPS, BARGES AND SIMILAR VESSELS\nFOR THE TRANSPORT OF PERSONS OR GOODS - CRUISE SHIPS, EXCURSION BOATS AND SIMILAR\nVESSELS PRINCIPALLY DESIGNED FOR THE TRANSPORT OF PERSONS; FERRY-BOATS OF ALL KINDS:\nBOATS",
+        "hsn_code": "89011030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUISE SHIPS, EXCURSION BOATS , FERRY-BOATS, CARGO SHIPS, BARGES AND SIMILAR VESSELS\nFOR THE TRANSPORT OF PERSONS OR GOODS - CRUISE SHIPS, EXCURSION BOATS AND SIMILAR\nVESSELS PRINCIPALLY DESIGNED FOR THE TRANSPORT OF PERSONS; FERRY-BOATS OF ALL KINDS:\nLAUNCHES",
+        "hsn_code": "89011020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUISE SHIPS, EXCURSION BOATS , FERRY-BOATS, CARGO SHIPS, BARGES AND SIMILAR VESSELS\nFOR THE TRANSPORT OF PERSONS OR GOODS - CRUISE SHIPS, EXCURSION BOATS AND SIMILAR\nVESSELS PRINCIPALLY DESIGNED FOR THE TRANSPORT OF PERSONS; FERRY-BOATS OF ALL KINDS:\nSHIPS",
+        "hsn_code": "89011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "AIRCRAFT LAUNCHING GEAR; DECK-ARRESTOR OR SIMILAR GEAR; GROUND FLYING TRAINERS; PARTS OF THE FOREGOING ARTICLES",
+        "hsn_code": "8805",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIRCRAFT LAUNCHING GEAR; DECK-ARRESTOR OR SIMILAR GEAR; GROUND FLYING TRAINERS;\nPARTS OF THE FOREGOING ARTICLES - GROUND FLYING TRAINERS AND PARTS THEREOF: OTHER",
+        "hsn_code": "88052900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIRCRAFT LAUNCHING GEAR; DECK-ARRESTOR OR SIMILAR GEAR; GROUND FLYING TRAINERS;\nPARTS OF THE FOREGOING ARTICLES - GROUND FLYING TRAINERS AND PARTS THEREOF: AIR\nCOMBAT SIMULATORS AND PARTS THEREOF",
+        "hsn_code": "88052100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIRCRAFT LAUNCHING GEAR; DECK-ARRESTOR OR SIMILAR GEAR; GROUND FLYING TRAINERS;\nPARTS OF THE FOREGOING ARTICLES - AIRCRAFT LAUNCHING GEAR AND PARTS THEREOF; DECK\nARRESTOR OR SIMILAR GEAR AND PARTS THEREOF: PART OF AIRCRAFT LAUNCHING GEAR AND\nDECK-ARRESTOR OR SIMILAR GEAR",
+        "hsn_code": "88051030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIRCRAFT LAUNCHING GEAR; DECK-ARRESTOR OR SIMILAR GEAR; GROUND FLYING TRAINERS;\nPARTS OF THE FOREGOING ARTICLES - AIRCRAFT LAUNCHING GEAR AND PARTS THEREOF; DECK\nARRESTOR OR SIMILAR GEAR AND PARTS THEREOF: DECK ARRESTOR OR SIMILAR GEAR",
+        "hsn_code": "88051020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIRCRAFT LAUNCHING GEAR; DECK-ARRESTOR OR SIMILAR GEAR; GROUND FLYING TRAINERS;\nPARTS OF THE FOREGOING ARTICLES - AIRCRAFT LAUNCHING GEAR AND PARTS THEREOF; DECK\nARRESTOR OR SIMILAR GEAR AND PARTS THEREOF: AIRCRAFT LAUNCHING GEAR",
+        "hsn_code": "88051010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARACHUTES (INCLUDING DIRIGIBLE PARACHUTES AND PARAGLIDERS) AND ROTOCHUTES; PARTS THEREOF AND ACCESSORIES THERETO",
+        "hsn_code": "8804",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARACHUTES (INCLUDING DIRIGIBLE PARACHUTES AND PARAGLIDERS) AND ROTOCHUTES;\nPARTS THEREOF AND ACCESSORIES THERETO - PARACHUTES (INCLUDING DIRIGIBLE\nPARACHUTES AND PARAGLIDERS) AND ROTOCHUTES; PARTS THEREOF AND ACCESSORIES\nTHERETO: ROTOCHUTES; PARTS AND ACCESSORIES THEREOF",
+        "hsn_code": "88040020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARACHUTES (INCLUDING DIRIGIBLE PARACHUTES AND PARAGLIDERS) AND ROTOCHUTES;\nPARTS THEREOF AND ACCESSORIES THERETO - PARACHUTES (INCLUDING DIRIGIBLE\nPARACHUTES AND PARAGLIDERS) AND ROTOCHUTES; PARTS THEREOF AND ACCESSORIES\nTHERETO: PARACHUTES (INCLUDING DIRIGIBLE PARACHUTES AND PARAGLIDERS) AND PARTS\nAND ACCESSORIES THEREOF",
+        "hsn_code": "88040010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALLOONS AND DIRIGIBLES, GLIDERS, HANG GLIDERS AND OTHER NON-POWERED AIRCRAFT",
+        "hsn_code": "8801",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALLOONS AND DIRIGIBLES; GLIDERS, HANG GLIDERS AND OTHER NON-POWERED AIRCRAFT -\nOTHER: OTHER",
+        "hsn_code": "88019090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALLOONS AND DIRIGIBLES; GLIDERS, HANG GLIDERS AND OTHER NON-POWERED AIRCRAFT -\nOTHER: BALLOONS",
+        "hsn_code": "88019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALLOONS AND DIRIGIBLES; GLIDERS, HANG GLIDERS AND OTHER NON-POWERED AIRCRAFT\nGLIDERS AND HANG GLIDERS",
+        "hsn_code": "88011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALLOONS AND DIRIGIBLES; GLIDERS, HANG GLIDERS AND OTHER NON-POWERED AIRCRAFT\nBALLOONS AND DIRIGIBLES; GLIDERS, HANG GLIDERS AND OTHER NON-POWERED AIRCRAFT:---\nOTHER",
+        "hsn_code": "88010090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALLOONS AND DIRIGIBLES; GLIDERS, HANG GLIDERS AND OTHER NON-POWERED AIRCRAFT\nBALLOONS AND DIRIGIBLES; GLIDERS, HANG GLIDERS AND OTHER NON-POWERED AIRCRAFT: --\nBALLOONS",
+        "hsn_code": "88010020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALLOONS AND DIRIGIBLES; GLIDERS, HANG GLIDERS AND OTHER NON-POWERED AIRCRAFT -\nBALLOONS AND DIRIGIBLES; GLIDERS, HANG GLIDERS AND OTHER NON-POWERED AIRCRAFT:---\nGLIDERS AND HANG GLIDERS",
+        "hsn_code": "88010010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRAILERS AND SEMI-TRAILERS; OTHER VEHICLES, NOT MECHANICALLY PROPELLED; PARTS THEREOF",
+        "hsn_code": "8716",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRAILERS AND SEMI-TRAILERS; OTHER VEHICLES, NOT MECHANICALLY PROPELLED; PARTS\nTHEREOF PARTS OTHER",
+        "hsn_code": "87169090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRAILERS AND SEMI-TRAILERS; OTHER VEHICLES, NOT MECHANICALLY PROPELLED; PARTS\nTHEREOF PARTS : PARTS AND ACCESSORIES OF TRAILERS",
+        "hsn_code": "87169010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRAILERS AND SEMI-TRAILERS; OTHER VEHICLES, NOT MECHANICALLY PROPELLED; PARTS\nTHEREOF OTHER TRAILERS AND SEMI-TRAILERS FOR THE TRANSPORT OF GOODS : OTHER\nTRAILERS AND SEMI-TRAILERS",
+        "hsn_code": "87164000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRAILERS AND SEMI-TRAILERS; OTHER VEHICLES, NOT MECHANICALLY PROPELLED; PARTS\nTHEREOF OTHER TRAILERS AND SEMI-TRAILERS FOR THE TRANSPORT OF GOODS : OTHER",
+        "hsn_code": "87163900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRAILERS AND SEMI-TRAILERS; OTHER VEHICLES, NOT MECHANICALLY PROPELLED; PARTS\nTHEREOF OTHER TRAILERS AND SEMI-TRAILERS FOR THE TRANSPORT OF GOODS : TANKER\nTRAILERS AND TANKER SEMI-TRAILERS",
+        "hsn_code": "87163100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRAILERS AND SEMI-TRAILERS; OTHER VEHICLES, NOT MECHANICALLY PROPELLED; PARTS\nTHEREOF TRAILERS AND SEMI-TRAILERS OF THE CARAVAN TYPE, FOR HOUSING OR CAMPING",
+        "hsn_code": "87161000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BABY CARRIAGES AND PARTS THEREOF",
+        "hsn_code": "8715",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BABY CARRIAGES AND PARTS THEREOF BABY CARRIAGES AND PARTS THEREOF PARTS",
+        "hsn_code": "87150020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BABY CARRIAGES AND PARTS THEREOF BABY CARRIAGES AND PARTS THEREOF: BABY CARRIAGES",
+        "hsn_code": "87150010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF VEHICLES OF HEADINGS 8711 TO 8713",
+        "hsn_code": "8714",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF VEHICLES OF HEADINGS 8711TO 8713 OTHER OTHER",
+        "hsn_code": "87149990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF VEHICLES OF HEADINGS 8711TO 8713 OTHER BICYCLE WHEELS",
+        "hsn_code": "87149920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF VEHICLES OF HEADINGS 8711TO 8713 OTHER : BICYCLE CHAINS",
+        "hsn_code": "87149910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF VEHICLES OF HEADINGS 8711TO 8713 PEDALS AND CRANK-GEAR,\nAND PARTS THEREOF",
+        "hsn_code": "87149600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF VEHICLES OF HEADINGS 8711TO 8713 SADDLES OTHER",
+        "hsn_code": "87149590",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF VEHICLES OF HEADINGS 8711TO 8713 SADDLES : BICYCLE SADDLES",
+        "hsn_code": "87149510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF VEHICLES OF HEADINGS 8711TO 8713 BRAKES, INCLUDING\nCOASTER BRAKING HUBS AND HUB BRAKES, AND PARTS THEREOF",
+        "hsn_code": "87149400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF VEHICLES OF HEADINGS 8711TO 8713 HUBS, OTHER THAN\nCOASTER BRAKING HUBS AND HUB BRAKES, AND FREE-WHEEL SPROCKET-WHEELS : OTHER",
+        "hsn_code": "87149390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF VEHICLES OF HEADINGS 8711TO 8713 HUBS, OTHER THAN\nCOASTER BRAKING HUBS AND HUB BRAKES, AND FREE-WHEEL SPROCKET-WHEELS : BICYCLE\nFREE-WHEELS",
+        "hsn_code": "87149320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF VEHICLES OF HEADINGS 8711TO 8713 HUBS, OTHER THAN\nCOASTER BRAKING HUBS AND HUB BRAKES, AND FREE-WHEEL SPROCKET-WHEELS : BICYCLE\nHUBS",
+        "hsn_code": "87149310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF VEHICLES OF HEADINGS 8711TO 8713 WHEEL RIMS AND SPOKES\nOTHER",
+        "hsn_code": "87149290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF VEHICLES OF HEADINGS 8711TO 8713 WHEEL RIMS AND SPOKES\nBICYCLE SPOKES",
+        "hsn_code": "87149220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF VEHICLES OF HEADINGS 8711TO 8713 WHEEL RIMS AND SPOKES :\nBICYCLE RIMS",
+        "hsn_code": "87149210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF VEHICLES OF HEADINGS 8711TO 8713 OTHER : FRAMES AND\nFORKS, AND PARTS THEREOF",
+        "hsn_code": "87149100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "87141090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SADDLES",
+        "hsn_code": "87141010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "CARRIAGES FOR DISABLED PERSONS, WHETHER OR NOT MOTORISED OR OTHERWISE MECHANICALLY PROPELLED",
+        "hsn_code": "8713",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARRIAGES FOR DISABLED PERSONS, WHETHER OR NOT MOTORISED OR OTHERWISE\nMECHANICALLY PROPELLED OTHER OTHER",
+        "hsn_code": "87139090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARRIAGES FOR DISABLED PERSONS, WHETHER OR NOT MOTORISED OR OTHERWISE\nMECHANICALLY PROPELLED OTHER WHEEL CHAIRS FOR INVALID",
+        "hsn_code": "87139010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARRIAGES FOR DISABLED PERSONS, WHETHER OR NOT MOTORISED OR OTHERWISE\nMECHANICALLY PROPELLED NOT MECHANICALLY PROPELLED OTHER",
+        "hsn_code": "87131090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CARRIAGES FOR DISABLED PERSONS, WHETHER OR NOT MOTORISED OR OTHERWISE\nMECHANICALLY PROPELLED NOT MECHANICALLY PROPELLED WHEEL CHAIRS FOR INVALID",
+        "hsn_code": "87131010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BICYCLES AND OTHER CYCLES (INCLUDING DELIVERY TRICYCLES), NOT MOTORISED",
+        "hsn_code": "8712",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BICYCLES AND OTHER CYCLES (INCLUDING DELIVERY TRICYCLES), NOT MOTORISED BICYCLES AND\nOTHER CYCLES (INCLUDING DELIVERY TRICYCLES), NOT MOTORIZED OTHER",
+        "hsn_code": "87120090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BICYCLES AND OTHER CYCLES (INCLUDING DELIVERY TRICYCLES), NOT MOTORISED BICYCLES AND\nOTHER CYCLES (INCLUDING DELIVERY TRICYCLES), NOT MOTORISED BICYCLES",
+        "hsn_code": "87120010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Tanks and other armoured fighting vehicles, motorised, whether or not fitted with weapons, and parts of such vehicles",
+        "hsn_code": "8710",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TANKS AND OTHER ARMOURED FIGHTING VEHICLES, MOTORISED, WHETHER OR NOT FITTED\nWITH WEAPONS, AND PARTS OF SUCH VEHICLES",
+        "hsn_code": "87100000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WORKS TRUCKS, SELF-PROPELLED, NOT FITTED WITH LIFTING OR HANDLING EQUIPMENT, OF THE TYPE USED IN FACTORIES, WAREHOUSES, DOCK AREAS OR AIRPORTS FOR SHORT DISTANCE TRANSPORT OF GOODS; TRACTORS OF THE TYPE USED ON RAILWAY STATION PLATFORMS; PARTS OF THE FOREGOING VEHICLES",
+        "hsn_code": "8709",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKS TRUCKS, SELF-PROPELLED, NOT FITTED WITH LIFTING OR HANDLING EQUIPMENT, OF\nTHE TYPE USED IN FACTORIES, WAREHOUSES, DOCK AREAS OR AIRPORTS FOR SHORT DISTANCE\nTRANSPORT OF GOODS; TRACTORS OF THE TYPE USED ON RAILWAY STATION PLATFORMS;\nPARTS OF THE FOREGOING VEHICLES PARTS",
+        "hsn_code": "87099000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKS TRUCKS, SELF-PROPELLED, NOT FITTED WITH LIFTING OR HANDLING EQUIPMENT, OF\nTHE TYPE USED IN FACTORIES, WAREHOUSES, DOCK AREAS OR AIRPORTS FOR SHORT DISTANCE\nTRANSPORT OF GOODS; TRACTORS OF THE TYPE USED ON RAILWAY STATION PLATFORMS;\nPARTS OF THE FOREGOING VEHICLES VEHICLES : OTHER",
+        "hsn_code": "87091900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKS TRUCKS, SELF-PROPELLED, NOT FITTED WITH LIFTING OR HANDLING EQUIPMENT, OF\nTHE TYPE USED IN FACTORIES, WAREHOUSES, DOCK AREAS OR AIRPORTS FOR SHORT DISTANCE\nTRANSPORT OF GOODS; TRACTORS OF THE TYPE USED ON RAILWAY STATION PLATFORMS;\nPARTS OF THE FOREGOING VEHICLES VEHICLES : ELECTRICAL",
+        "hsn_code": "87091100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705",
+        "hsn_code": "8708",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 - OTHER PARTS\nAND ACCESSORIES -- SAFETY AIRBAGS WITH INFLATER SYSTEM; PARTS THEREOF",
+        "hsn_code": "87089500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 - SUSPENSION\nSYSTEMS AND PARTS THEREOF (INCLUDING SHOCK ABSORBERS)",
+        "hsn_code": "87088000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 BRAKES AND\nSERVO-BRAKES AND PARTS THEREOF NON-DRIVING AXLES AND PARTS THEREOF",
+        "hsn_code": "87086000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 BRAKES AND\nSERVO-BRAKES AND PARTS THEREOF OTHER",
+        "hsn_code": "87083900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 BRAKES AND\nSERVO-BRAKES AND PARTS THEREOF MOUNTED BRAKE LININGS",
+        "hsn_code": "87083100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 - OTHER PARTS\nAND ACCESSORIES OF BODIES (INCLUDING CABS) -- OTHER",
+        "hsn_code": "87082900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 OTHER PARTS\nAND ACCESSORIES OF BODIES (INCLUDING CABS) : SAFETY SEAT BELTS",
+        "hsn_code": "87082100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES OF THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 BUMPERS AND\nPARTS THEREOF OTHER",
+        "hsn_code": "87081090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "BODIES (INCLUDING CABS), FOR THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705",
+        "hsn_code": "8707",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "BODIES (INCLUDING CABS), FOR THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 OTHER",
+        "hsn_code": "87079000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "BODIES (INCLUDING CABS), FOR THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705 FOR THE\nVEHICLES OF HEADING 8703",
+        "hsn_code": "87071000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "CHASSIS FITTED WITH ENGINES, FOR THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705",
+        "hsn_code": "8706",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "8706 CHASSIS FITTED WITH ENGINES, FOR THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705\nFOR THE MOTOR VEHICLES OF HEADING 8705",
+        "hsn_code": "87060050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "8706 CHASSIS FITTED WITH ENGINES, FOR THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705\nFOR THE MOTOR VEHICLES OF HEADING 8704: OTHER",
+        "hsn_code": "87060049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "8706 CHASSIS FITTED WITH ENGINES, FOR THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705\nFOR THE MOTOR VEHICLES OF HEADING 8704: FOR DUMPERS COVERED IN THE HEADING 8704",
+        "hsn_code": "87060043",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "8706 CHASSIS FITTED WITH ENGINES, FOR THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705\nFOR THE MOTOR VEHICLES OF HEADING 8704: FOR VEHICLES, OTHER THAN PETROL DRIVEN",
+        "hsn_code": "87060042",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "8706 CHASSIS FITTED WITH ENGINES, FOR THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705\nFOR THE MOTOR VEHICLES OF HEADING 8704: FOR THREE-WHEELED MOTOR VEHICLE",
+        "hsn_code": "87060041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "8706 CHASSIS FITTED WITH ENGINES, FOR THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705\nFOR THE MOTOR VEHICLES OF HEADING 8703 OTHER",
+        "hsn_code": "87060039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "8706 CHASSIS FITTED WITH ENGINES, FOR THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705\nFOR THE MOTOR VEHICLES OF HEADING 8703 FOR THREE-WHEELED VEHICLES",
+        "hsn_code": "87060031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "8706 CHASSIS FITTED WITH ENGINES, FOR THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705\nFOR THE VEHICLES OF HEADING 8702 OTHER",
+        "hsn_code": "87060029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "8706 CHASSIS FITTED WITH ENGINES, FOR THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705\nFOR THE VEHICLES OF HEADING 8702 FOR TRANSPORT OF NOT MORE THAN THIRTEEN PERSONS,\nINCLUDING THE DRIVER",
+        "hsn_code": "87060021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "8706 CHASSIS FITTED WITH ENGINES, FOR THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705\nCHASSIS FITTED WITH ENGINES, FOR THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705: ? FOR\nTHE TRACTORS OF HEADING 8701 : OTHER",
+        "hsn_code": "87060019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "8706 CHASSIS FITTED WITH ENGINES, FOR THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705\nCHASSIS FITTED WITH ENGINES, FOR THE MOTOR VEHICLES OF HEADINGS 8701 TO 8705: ? FOR\nTHE TRACTORS OF HEADING 8701 : OF ENGINE CAPACITY NOT EXCEEDING 1,800 CC",
+        "hsn_code": "87060011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SPECIAL PURPOSE MOTOR VEHICLES, OTHER THAN THOSE PRINCIPALLY DESIGNED FOR THE TRANSPORT OR PERSONS OR GOODS (FOR EXAMPLE BREAKDOWN LORRIES, CRANE LORRIES, FIRE FIGHTING VEHICLES, CONCRETE-MIXERS LORRIES, SPRAYING LORRIES, MOBILE WORKSHOPS, MOBILE RADIOLOGICAL UNITS)",
+        "hsn_code": "8705",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "8705 SPECIAL PURPOSE MOTOR VEHICLES, OTHER THAN THOSE PRINCIPALLY DESIGNED FOR THE\nTRANSPORT OF PERSONS OR GOODS (FOR EXAMPLE, BREAKDOWN LORRIES, CRANE LORRIES,\nFIRE FIGHTING VEHICLES, CONCRETE-MIXERS LORRIES, SPRAYING LORRIES, MOBILE\nWORKSHOPS, MOBILE RADIOLOGICAL UNITS) OTHER",
+        "hsn_code": "87059000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "8705 SPECIAL PURPOSE MOTOR VEHICLES, OTHER THAN THOSE PRINCIPALLY DESIGNED FOR THE\nTRANSPORT OF PERSONS OR GOODS (FOR EXAMPLE, BREAKDOWN LORRIES, CRANE LORRIES,\nFIRE FIGHTING VEHICLES, CONCRETE-MIXERS LORRIES, SPRAYING LORRIES, MOBILE\nWORKSHOPS, MOBILE RADIOLOGICAL UNITS) CONCRETE-MIXER LORRIES",
+        "hsn_code": "87054000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "8705 SPECIAL PURPOSE MOTOR VEHICLES, OTHER THAN THOSE PRINCIPALLY DESIGNED FOR THE\nTRANSPORT OF PERSONS OR GOODS (FOR EXAMPLE, BREAKDOWN LORRIES, CRANE LORRIES,\nFIRE FIGHTING VEHICLES, CONCRETE-MIXERS LORRIES, SPRAYING LORRIES, MOBILE\nWORKSHOPS, MOBILE RADIOLOGICAL UNITS) FIRE FIGHTING VEHICLES",
+        "hsn_code": "87053000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "8705 SPECIAL PURPOSE MOTOR VEHICLES, OTHER THAN THOSE PRINCIPALLY DESIGNED FOR THE\nTRANSPORT OF PERSONS OR GOODS (FOR EXAMPLE, BREAKDOWN LORRIES, CRANE LORRIES,\nFIRE FIGHTING VEHICLES, CONCRETE-MIXERS LORRIES, SPRAYING LORRIES, MOBILE\nWORKSHOPS, MOBILE RADIOLOGICAL UNITS) MOBILE DRILLING DERRICKS",
+        "hsn_code": "87052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "8705 SPECIAL PURPOSE MOTOR VEHICLES, OTHER THAN THOSE PRINCIPALLY DESIGNED FOR THE\nTRANSPORT OF PERSONS OR GOODS (FOR EXAMPLE, BREAKDOWN LORRIES, CRANE LORRIES,\nFIRE FIGHTING VEHICLES, CONCRETE-MIXERS LORRIES, SPRAYING LORRIES, MOBILE\nWORKSHOPS, MOBILE RADIOLOGICAL UNITS) CRANE LORRIES",
+        "hsn_code": "87051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS",
+        "hsn_code": "8704",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS OTHER : LORRIES AND TRUCKS: OTHER\nOTHER",
+        "hsn_code": "87049090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS OTHER : LORRIES AND TRUCKS: OTHER",
+        "hsn_code": "87049019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS OTHER : LORRIES AND TRUCKS: ELECTRICALLY\nOPERATED",
+        "hsn_code": "87049012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS OTHER : LORRIES AND TRUCKS:\nREFRIGERATED",
+        "hsn_code": "87049011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS OTHER, WITH SPARK-IGNITION INTERNAL\nCOMBUSTION PISTON ENGINE G.V.W. EXCEEDING 5 TONNES: OTHER",
+        "hsn_code": "87043290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS OTHER, WITH SPARK-IGNITION INTERNAL\nCOMBUSTION PISTON ENGINE G.V.W. EXCEEDING 5 TONNES: LORRIES AND TRUCKS OTHER",
+        "hsn_code": "87043219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS OTHER, WITH SPARK-IGNITION INTERNAL\nCOMBUSTION PISTON ENGINE G.V.W. EXCEEDING 5 TONNES: LORRIES AND TRUCKS\nREFRIGERATED",
+        "hsn_code": "87043211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS OTHER, WITH SPARK-IGNITION INTERNAL\nCOMBUSTION PISTON ENGINE : G.V.W. NOT EXCEEDING 5 TONNES OTHER",
+        "hsn_code": "87043190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS OTHER, WITH SPARK-IGNITION INTERNAL\nCOMBUSTION PISTON ENGINE : G.V.W. NOT EXCEEDING 5 TONNES THREE-WHEELED MOTOR\nVEHICLES",
+        "hsn_code": "87043120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS OTHER, WITH SPARK-IGNITION INTERNAL\nCOMBUSTION PISTON ENGINE : G.V.W. NOT EXCEEDING 5 TONNES : REFRIGERATED",
+        "hsn_code": "87043110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS OTHER, WITH COMPRESSION-IGNITION\nINTERNAL COMBUSTION PISTON ENGINE (DIESEL OR SEMI-DIESEL) : G.V.W. EXCEEDING 20\nTONNES OTHER",
+        "hsn_code": "87042390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS OTHER, WITH COMPRESSION-IGNITION\nINTERNAL COMBUSTION PISTON ENGINE (DIESEL OR SEMI-DIESEL) : G.V.W. EXCEEDING 20\nTONNES : LORRIES AND TRUCKS: OTHER",
+        "hsn_code": "87042319",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS OTHER, WITH COMPRESSION-IGNITION\nINTERNAL COMBUSTION PISTON ENGINE (DIESEL OR SEMI-DIESEL) : G.V.W. EXCEEDING 20\nTONNES : LORRIES AND TRUCKS: REFRIGERATED",
+        "hsn_code": "87042311",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS OTHER, WITH COMPRESSION-IGNITION\nINTERNAL COMBUSTION PISTON ENGINE (DIESEL OR SEMI-DIESEL) : G.V.W. EXCEEDING 5\nTONNES BUT NOT EXCEEDING 20 TONNES OTHER",
+        "hsn_code": "87042290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS OTHER, WITH COMPRESSION-IGNITION\nINTERNAL COMBUSTION PISTON ENGINE (DIESEL OR SEMI-DIESEL) : G.V.W. EXCEEDING 5\nTONNES BUT NOT EXCEEDING 20 TONNES LORRIES AND TRUCKS OTHER",
+        "hsn_code": "87042219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS OTHER, WITH COMPRESSION-IGNITION\nINTERNAL COMBUSTION PISTON ENGINE (DIESEL OR SEMI-DIESEL) : G.V.W. EXCEEDING 5\nTONNES BUT NOT EXCEEDING 20 TONNES LORRIES AND TRUCKS REFRIGERATED",
+        "hsn_code": "87042211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS OTHER, WITH COMPRESSION-IGNITION\nINTERNAL COMBUSTION PISTON ENGINE (DIESEL OR SEMI-DIESEL) : G.V.W. NOT EXCEEDING 5\nTONNES OTHER",
+        "hsn_code": "87042190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS OTHER, WITH COMPRESSION-IGNITION\nINTERNAL COMBUSTION PISTON ENGINE (DIESEL OR SEMI-DIESEL) : G.V.W. NOT EXCEEDING 5\nTONNES THREE-WHEELED MOTOR VEHICLES",
+        "hsn_code": "87042120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS OTHER, WITH COMPRESSION-IGNITION\nINTERNAL COMBUSTION PISTON ENGINE (DIESEL OR SEMI-DIESEL) : G.V.W. NOT EXCEEDING 5\nTONNES REFRIGERATED",
+        "hsn_code": "87042110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS DUMPERS DESIGNED FOR OFF-HIGHWAY\nUSE: OTHER",
+        "hsn_code": "87041090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF GOODS DUMPERS DESIGNED FOR OFF-HIGHWAY\nUSE: :WITH NET WEIGHT (EXCLUDING PAY-LOAD) EXCEEDING 8 TONNES AND MAXIMUM PAY\nLOAD CAPACITY NOT LESS THAN 10 TONNES",
+        "hsn_code": "87041010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF TEN OR MORE PERSONS, INCLUDING THE DRIVER",
+        "hsn_code": "8702",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF TEN OR MORE PERSONS, INCLUDING THE DRIVER\nOTHER :OTHER",
+        "hsn_code": "87029099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF TEN OR MORE PERSONS, INCLUDING THE DRIVER\nOTHER :AIR-CONDITIONED VEHICLE",
+        "hsn_code": "87029092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF TEN OR MORE PERSONS, INCLUDING THE DRIVER\nOTHER INTEGRATED MONOCOQUE VEHICLE",
+        "hsn_code": "87029091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF TEN OR MORE PERSONS, INCLUDING THE DRIVER\nOTHER VEHICLES FOR TRANSPORT OF NOT MORE THAN13 PERSONS, INCLUDING THE\nDRIVER:ELECTRICALLY OPERATED VEHICLES NOT ELSEWHERE INCLUDED OR SPECIFIED",
+        "hsn_code": "87029020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF TEN OR MORE PERSONS, INCLUDING THE DRIVER\nOTHER VEHICLES FOR TRANSPORT OF NOT MORE THAN13 PERSONS, INCLUDING THE DRIVER\nOTHER",
+        "hsn_code": "87029019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF TEN OR MORE PERSONS, INCLUDING THE DRIVER\nOTHER : VEHICLES FOR TRANSPORT OF NOT MORE THAN13 PERSONS, INCLUDING THE DRIVER:\nELECTRICALLY OPERATED",
+        "hsn_code": "87029013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF TEN OR MORE PERSONS, INCLUDING THE DRIVER\nOTHER : VEHICLES FOR TRANSPORT OF NOT MORE THAN13 PERSONS, INCLUDING THE\nDRIVER:AIR-CONDITIONED VEHICLE",
+        "hsn_code": "87029012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF TEN OR MORE PERSONS, INCLUDING THE DRIVER\nOTHER VEHICLES FOR TRANSPORT OF NOT MORE THAN13 PERSONS, INCLUDING THE DRIVER:\nINTEGRATED MONOCOQUE VEHICLE",
+        "hsn_code": "87029011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF TEN OR MORE PERSONS, INCLUDING THE DRIVER\nWITH COMPRESSION-IGNITION INTERNALCOMBUSTION PISTON ENGINE (DIESEL OR\nSEMIDIESEL)OTHER :OTHER",
+        "hsn_code": "87021099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF TEN OR MORE PERSONS, INCLUDING THE DRIVER\nWITH COMPRESSION-IGNITION INTERNALCOMBUSTION PISTON ENGINE (DIESEL OR\nSEMIDIESEL)OTHER : AIR-CONDITIONED VEHICLE",
+        "hsn_code": "87021092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF TEN OR MORE PERSONS, INCLUDING THE\nDRIVERWITH COMPRESSION-IGNITION INTERNALCOMBUSTION PISTON ENGINE (DIESEL OR\nSEMIDIESEL)OTHER : INTEGRATED MONCOQUE VEHICLE",
+        "hsn_code": "87021091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF TEN OR MORE PERSONS, INCLUDING THE\nDRIVERWITH COMPRESSION-IGNITION INTERNALCOMBUSTION PISTON ENGINE (DIESEL OR\nSEMIDIESEL) VEHICLES FOR TRANSPORT OF NOT MORE THAN 13 PERSONS, INCLUDING THE\nDRIVER:OTHER",
+        "hsn_code": "87021019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF TEN OR MORE PERSONS, INCLUDING THE DRIVER\nWITH COMPRESSION-IGNITION INTERNALCOMBUSTION PISTON ENGINE (DIESEL OR SEMIDIESEL)\nVEHICLES FOR TRANSPORT OF NOT MORE THAN 13 PERSONS, INCLUDING THE DRIVER:AIR\nCONDITIONED VEHICLE",
+        "hsn_code": "87021012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOTOR VEHICLES FOR THE TRANSPORT OF TEN OR MORE PERSONS, INCLUDING THE DRIVER\nWITH COMPRESSION-IGNITION INTERNALCOMBUSTION PISTON ENGINE (DIESEL OR SEMIDIESEL)\nVEHICLES FOR TRANSPORT OF NOT MORE THAN3 PERSONS, INCLUDING THE DRIVER:\nINTEGRATED MONOCOQUE VEHICLE",
+        "hsn_code": "87021011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "TRACTORS (OTHER THAN TRACTORS OF HEADING 8709)",
+        "hsn_code": "8701",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "TRACTORS (OTHER THAN TRACTORS OFHEADING 8709 OTHER :TRACTORS (OTHER THAN\nTRACTORS OFHEADING 8709 OTHER",
+        "hsn_code": "87019090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "TRACTORS (OTHER THAN TRACTORS OF HEADING 8709 OTHER : OF ENGINE CAPACITY NOT\nEXCEEDING 1,800 CC",
+        "hsn_code": "87019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "TRACTORS (OTHER THAN TRACTORS OFHEADING 8709 OTHER OTHER",
+        "hsn_code": "87013099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "TRACTORS (OTHER THAN TRACTORS OF HEADING 8709 OTHER :OF ENGINE CAPACITY NOT\nEXCEEDING 1,800 CC",
+        "hsn_code": "87013091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "TRACTORS (OTHER THAN TRACTORS OFHEADING 8709 TRACK-LAYING TRACTORS GARDEN\nTRACTORS: OTHER",
+        "hsn_code": "87013019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "TRACTORS (OTHER THAN TRACTORS OFHEADING 8709 TRACK-LAYING TRACTORS : GARDEN\nTRACTORS:OF ENGINE CAPACITY NOT EXCEEDING 1,800 CC",
+        "hsn_code": "87013011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "TRACTORS (OTHER THAN TRACTORS OFHEADING 8709) ROAD TRACTORS FOR SEMI-TRAILERS\nOTHER",
+        "hsn_code": "87012090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "TRACTORS (OTHER THAN TRACTORS OF HEADING 8709) ROAD TRACTORS FOR SEMI-TRAILERS OF\nENGINE CAPACITY NOT EXCEEDING 1,800 CC",
+        "hsn_code": "87012010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "TRACTORS (OTHER THAN TRACTORS OF HEADING 8709) PEDESTRIAN CONTROLLED TRACTORS",
+        "hsn_code": "87011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "Containers (including containers for the transport of fluids) specially designed and equipped for carriage by one or more modes of transport",
+        "hsn_code": "8609",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONTAINERS (INCLUDING CONTAINERS FOR THE TRANSPORT OF FLUIDS) SPECIALLY DESIGNED\nAND EQUIPPED FOR CARRIAGE BY ONE OR MORE MODES OF TRANSPORT",
+        "hsn_code": "86090000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY TRACK FIXTURES AND FITTINGS; MECHANICAL (INCLUDING ELECTRO-MECHANICAL) SIGNALLING, SAFETY OR TRAFFIC CONTROL EQUIPMENT FOR RAILWAY, TRAMWAYS, RAODS, INLAND WATERWAYS, PARKING FACILITIES, PORT INSTALLATION OR AIR-FIELDS; PARTS OF THE FOREGOING",
+        "hsn_code": "8608",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY TRACK FIXTURES AND FITTINGS; MECHANICAL (INCLUDING ELECTO\nMECHANICAL) SIGNALLING, SAFETY OR TRAFFIC CONTROL EQUIPMENT FOR RAILWAY,\nTRAMWAYS, ROADS, INLAND WATERWAYS, PARKING FACILITIES, PORT INSTALLATION OR AIR\nFIELDS; PARTS OF THE FOREGOING - RAILWAY OR TRAMWAY TRACK FIXTURES AND FITTINGS;\nMECHANICAL (INCLUDING ELECTO-MECHANICAL) SIGNALLING, SAFETY OR TRAFFIC CONTROL\nEQUIPMENT FOR RAILWAY, TRAMWAYS, ROADS, INLAND WATERWAYS, PARKING FACILITIES,\nPORT INSTALLATION OR AIR-FIELDS; PARTS OF THE FOREGOING : OTHER",
+        "hsn_code": "86080090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY TRACK FIXTURES AND FITTINGS; MECHANICAL (INCLUDING ELECTO\nMECHANICAL) SIGNALLING, SAFETY OR TRAFFIC CONTROL EQUIPMENT FOR RAILWAY,\nTRAMWAYS, ROADS, INLAND WATERWAYS, PARKING FACILITIES, PORT INSTALLATION OR AIR\nFIELDS; PARTS OF THE FOREGOING - RAILWAY OR TRAMWAY TRACK FIXTURES AND FITTINGS;\nMECHANICAL (INCLUDING ELECTO-MECHANICAL) SIGNALLING, SAFETY OR TRAFFIC CONTROL\nEQUIPMENT FOR RAILWAY, TRAMWAYS, ROADS, INLAND WATERWAYS, PARKING FACILITIES,\nPORT INSTALLATION OR AIR-FIELDS; PARTS OF THE FOREGOING : OTHER TRAFFIC CONTROL\nEQUIPMENT FOR ROADS OR INLAND WATERWAYS INCLUDING AUTOMATIC TRAFFIC CONTROL\nEQUIPMENT FOR USE AT PORTS AND AIRPORTS",
+        "hsn_code": "86080040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY TRACK FIXTURES AND FITTINGS; MECHANICAL (INCLUDING ELECTO\nMECHANICAL) SIGNALLING, SAFETY OR TRAFFIC CONTROL EQUIPMENT FOR RAILWAY,\nTRAMWAYS, ROADS, INLAND WATERWAYS, PARKING FACILITIES, PORT INSTALLATION OR AIR\nFIELDS; PARTS OF THE FOREGOING - RAILWAY OR TRAMWAY TRACK FIXTURES AND FITTINGS;\nMECHANICAL (INCLUDING ELECTO-MECHANICAL) SIGNALLING, SAFETY OR TRAFFIC CONTROL\nEQUIPMENT FOR RAILWAY, TRAMWAYS, ROADS, INLAND WATERWAYS, PARKING FACILITIES,\nPORT INSTALLATION OR AIR-FIELDS; PARTS OF THE FOREGOING : OTHER TRAFFIC CONTROL\nEQUIPMENT FOR RAILWAYS",
+        "hsn_code": "86080030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY TRACK FIXTURES AND FITTINGS; MECHANICAL (INCLUDING ELECTO\nMECHANICAL) SIGNALLING, SAFETY OR TRAFFIC CONTROL EQUIPMENT FOR RAILWAY,\nTRAMWAYS, ROADS, INLAND WATERWAYS, PARKING FACILITIES, PORT INSTALLATION OR AIR\nFIELDS; PARTS OF THE FOREGOING - RAILWAY OR TRAMWAY TRACK FIXTURES AND FITTINGS;\nMECHANICAL (INCLUDING ELECTO-MECHANICAL) SIGNALLING, SAFETY OR TRAFFIC CONTROL\nEQUIPMENT FOR RAILWAY, TRAMWAYS, ROADS, INLAND WATERWAYS, PARKING FACILITIES,\nPORT INSTALLATION OR AIR-FIELDS; PARTS OF THE FOREGOING : MECHANICAL EQUIPMENT,\nNOT ELECTRICALLY POWERED FOR SIGNALLING TO, OR CONTROLLING, ROAD RAIL OR OTHER\nVEHICLES, SHIPS OR AIRCRAFT",
+        "hsn_code": "86080020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY TRACK FIXTURES AND FITTINGS; MECHANICAL (INCLUDING ELECTO\nMECHANICAL) SIGNALLING, SAFETY OR TRAFFIC CONTROL EQUIPMENT FOR RAILWAY,\nTRAMWAYS, ROADS, INLAND WATERWAYS, PARKING FACILITIES, PORT INSTALLATION OR AIR\nFIELDS; PARTS OF THE FOREGOING - RAILWAY OR TRAMWAY TRACK FIXTURES AND FITTINGS;\nMECHANICAL (INCLUDING ELECTO-MECHANICAL) SIGNALLING, SAFETY OR TRAFFIC CONTROL\nEQUIPMENT FOR RAILWAY, TRAMWAYS, ROADS, INLAND WATERWAYS, PARKING FACILITIES,\nPORT INSTALLATION OR AIR-FIELDS; PARTS OF THE FOREGOING : RAILWAY AND TRAMWAY\nTRACK FIXTURES AND FITTINGS",
+        "hsn_code": "86080010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF RAILWAY OR TRAMWAY LOCOMOTIVES OR ROLLING-STOCK",
+        "hsn_code": "8607",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF RAILWAY OR TRAMWAY LOCOMOTIVES OR ROLLING-STOCK - OTHER : OTHER : OTHER",
+        "hsn_code": "86079990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF RAILWAY OR TRAMWAY LOCOMOTIVES OR ROLLING-STOCK - OTHER : OTHER :\nHYDRAULIC SHOCK ABSORBERS FOR RAILWAY BOGIES",
+        "hsn_code": "86079930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF RAILWAY OR TRAMWAY LOCOMOTIVES OR ROLLING-STOCK - OTHER : OTHER : PARTS\nOF TRAMWAY, LOCOMOTIVES AND RUNNING STOCK",
+        "hsn_code": "86079920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF RAILWAY OR TRAMWAY LOCOMOTIVES OR ROLLING-STOCK - OTHER : OTHER : PARTS\nOF COACH WORK OF RAILWAY RUNNING STOCK",
+        "hsn_code": "86079910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF RAILWAY OR TRAMWAY LOCOMOTIVES OR ROLLING-STOCK - OTHER : OF\nLOCOMOTIVES",
+        "hsn_code": "86079100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF RAILWAY OR TRAMWAY LOCOMOTIVES OR ROLLING-STOCK HOOKS AND OTHER\nCOUPLING DEVICES, BUFFERS AND PARTS THEREOF: OTHER",
+        "hsn_code": "86073090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF RAILWAY OR TRAMWAY LOCOMOTIVES OR ROLLING-STOCK HOOKS AND OTHER\nCOUPLING DEVICES, BUFFERS AND PARTS THEREOF: BUFFERS AND COUPLING DEVICES",
+        "hsn_code": "86073010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF RAILWAY OR TRAMWAY LOCOMOTIVES OR ROLLING-STOCK - BRAKES AND PARTS\nTHEREOF : OTHER",
+        "hsn_code": "86072900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF RAILWAY OR TRAMWAY LOCOMOTIVES OR ROLLING-STOCK - BRAKES AND PARTS\nTHEREOF : AIR BRAKES AND PARTS THEREOF",
+        "hsn_code": "86072100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF RAILWAY OR TRAMWAY LOCOMOTIVES OR ROLLING-STOCK - BOGIES, BISSEL-BOGIES,\nAXLES AND WHEELS, AND PARTS THEREOF : OTHER INCLUDING PARTS : OTHER PARTS OF AXLES\nAND WHEELS",
+        "hsn_code": "86071990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF RAILWAY OR TRAMWAY LOCOMOTIVES OR ROLLING-STOCK - BOGIES, BISSEL-BOGIES,\nAXLES AND WHEELS, AND PARTS THEREOF : OTHER INCLUDING PARTS : AXLE BOXES\n(LUBRICATING OR GREASE BOX)",
+        "hsn_code": "86071930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF RAILWAY OR TRAMWAY LOCOMOTIVES OR ROLLING-STOCK - BOGIES, BISSEL-BOGIES,\nAXLES AND WHEELS, AND PARTS THEREOF : OTHER INCLUDING PARTS : AXLES AND WHEELS FOR\nLOCOMOTIVES",
+        "hsn_code": "86071920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF RAILWAY OR TRAMWAY LOCOMOTIVES OR ROLLING-STOCK - BOGIES, BISSEL-BOGIES,\nAXLES AND WHEELS, AND PARTS THEREOF : OTHER INCLUDING PARTS : AXLES, WHEELS FOR\nCOACHES, VAN AND WAGONS (ROLLING-STOCK)",
+        "hsn_code": "86071910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF RAILWAY OR TRAMWAY LOCOMOTIVES OR ROLLING-STOCK - BOGIES, BISSEL-BOGIES,\nAXLES AND WHEELS, AND PARTS THEREOF : OTHER BOGIES AND BISSEL-BOGIES",
+        "hsn_code": "86071200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF RAILWAY OR TRAMWAY LOCOMOTIVES OR ROLLING-STOCK - BOGIES, BISSEL-BOGIES,\nAXLES AND WHEELS, AND PARTS THEREOF : DRIVING BOGIES AND BISSEL-BOGIES",
+        "hsn_code": "86071100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY GOODS VANS AND WAGONS, NOT SELF-PROPELLED",
+        "hsn_code": "8606",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY GOODS VANS AND WAGONS, NOT SELF-PROPELLED - OTHER: OTHER",
+        "hsn_code": "86069900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY GOODS VANS AND WAGONS, NOT SELF-PROPELLED - OTHER: OPEN\nWITH NON-REMOVABLE SIDES OF A HEIGHT EXCEEDING 60 CMS: OTHER",
+        "hsn_code": "86069290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY GOODS VANS AND WAGONS, NOT SELF-PROPELLED - OTHER: OPEN\nWITH NON-REMOVABLE SIDES OF A HEIGHT EXCEEDING 60 CMS: BROAD GUAGE BOGIE EIGHT\nWHEELER WAGONS OF PAY-LOAD EXCEEDING 60 TONNES BUT NOT EXCEEDING 67 TONNES",
+        "hsn_code": "86069220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY GOODS VANS AND WAGONS, NOT SELF-PROPELLED - OTHER: OPEN\nWITH NON-REMOVABLE SIDES OF A HEIGHT EXCEEDING 60 CMS: BOGIE EIGHT WHEELER\nWAGONS OF PAY-LOAD NOT EXCEEDING 60 TONNES",
+        "hsn_code": "86069210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY GOODS VANS AND WAGONS, NOT SELF-PROPELLED - OTHER: COVERED\nAND CLOSED : OTHER",
+        "hsn_code": "86069190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY GOODS VANS AND WAGONS, NOT SELF-PROPELLED - OTHER: COVERED\nAND CLOSED : BROAD GUAGE EIGHT WHEELER COVERED WAGONS OF PAY-LOAD NOT\nEXCEEDING 60 TONNES",
+        "hsn_code": "86069120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY GOODS VANS AND WAGONS, NOT SELF-PROPELLED - OTHER: COVERED\nAND CLOSED : METER GUAGE EIGHT WHEELER COVERED WAGONS OF PAY-LOAD NOT\nEXCEEDING 38 TONNES",
+        "hsn_code": "86069110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY GOODS VANS AND WAGONS, NOT SELF-PROPELLED - SELF\nDISCHARGING VANS AND WAGONS, OTHER THAN THOSE OF SUB-HEADING 8606 10",
+        "hsn_code": "86063000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY GOODS VANS AND WAGONS, NOT SELF-PROPELLED - INSULATED OR\nREFRIGERATED VANS AND WAGONS, OTHER THAN THOSE OF SUB-HEADING 8606 10",
+        "hsn_code": "86062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY GOODS VANS AND WAGONS, NOT SELF-PROPELLED - RAILWAY OR\nTRAMWAY GOODS VANS AND WAGONS, NOT SELF-PROPELLED - TANK WAGONS AND THE LIKE :\nOTHER",
+        "hsn_code": "86061090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY GOODS VANS AND WAGONS, NOT SELF-PROPELLED - TANK WAGONS\nAND THE LIKE : EIGHT WHEELER TANK WAGONS OF PAY-LOAD NOT EXCEEDING 60 TONNES",
+        "hsn_code": "86061020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY GOODS VANS AND WAGONS, NOT SELF-PROPELLED - TANK WAGONS\nAND THE LIKE : FOUR WHEELER TANK WAGONS OF PAY-LOAD EXCEEDING 23 TONNES",
+        "hsn_code": "86061010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Railway or tramway passenger coaches, not self-propelled; luggage vans, post office coaches and other special purpose railway or tramway coaches, not self-propelled (excluding those of heading 8604)",
+        "hsn_code": "8605",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY PASSENGER COACHES, NOT SELF-PROPELLED; LUGGAGE VANS, POST\nOFFICE COACHES AND OTHER SPECIAL PURPOSE RAILWAY OR TRAMWAY COACHES, NOT SELF\nPROPELLED (EXCLUDING THOSE OF HEADING 8604)",
+        "hsn_code": "86050000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Railway or tramway maintenance or service vehicles whether or not self-propelled (for example, workshops, cranes, ballast tampers, track-liners, testing coaches and track inspection vehicles)",
+        "hsn_code": "8604",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY MAINTENANCE OR SERVICE VEHICLES WHETHER OR NOT SELF\nPROPELLED (FOR EXAMPLE, WORKSHOPS, CRANES, BALLAST TAMPERS, TRACK-LINERS, TESTING\nCOACHES AND TRACK INSPECTION VEHICLES)",
+        "hsn_code": "86040000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF-PROPELLED RAILWAY OR TRAMWAY COACHES, VANS AND TRUCKS, OTHER THAN THOSE OF HEADING 8604",
+        "hsn_code": "8603",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF-PROPELLED RAILWAY OR TRAMWAY COACHES, VANS AND TRUCKS, OTHER THAN THOSE OF\nHEADING 8604 OTHER",
+        "hsn_code": "86039000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF-PROPELLED RAILWAY OR TRAMWAY COACHES, VANS AND TRUCKS, OTHER THAN THOSE OF\nHEADING 8604 POWERED FROM AN EXTERNAL SOURCE OF ELECTRICITY",
+        "hsn_code": "86031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER RAIL LOCOMOTIVES; LOCOMOTIVE TENDERS",
+        "hsn_code": "8602",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER RAIL LOCOMOTIVES; LOCOMOTIVE TENDERS - OTHER : OTHER",
+        "hsn_code": "86029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER RAIL LOCOMOTIVES; LOCOMOTIVE TENDERS - OTHER : STEAM LOCOMOTIVES AND\nTENDERS THEREOF",
+        "hsn_code": "86029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER RAIL LOCOMOTIVES; LOCOMOTIVE TENDERS DIESEL-ELECTRIC LOCOMOTIVES",
+        "hsn_code": "86021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAIL LOCOMOTIVES POWERED FROM AN EXTERNAL SOURCE OF ELECTRICITY OR BY ELECTRIC ACCUMULATORS",
+        "hsn_code": "8601",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAIL LOCOMOTIVES POWERED FROM AN EXTERNAL SOURCE OF ELECTRICITY OR BY ELECTRIC\nACCUMULATORS POWERED BY ELECTRIC ACCUMULATORS",
+        "hsn_code": "86012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAIL LOCOMOTIVES POWERED FROM AN EXTERNAL SOURCE OF ELECTRICITY OR BY ELECTRIC\nACCUMULATORS POWERED FROM AN EXTERNAL SOURCE OF ELECTRICITY",
+        "hsn_code": "86011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATING FITTINGS FOR ELECTRICAL MACHINES, APPLIANCES OR EQUIPMENT, BEING FITTINGS WHOLLY OF INSULATING MATERIAL APART FROM ANY MINOR COMPONENTS OF METAL (FOR EXAMPLE, THREADED SOCKETS) INCORPORATED DURING MOULDING SOLELY FOR THE PURPOSES OF ASSEMBLY, OTHER THAN INSULATORS OF HEADING 8546; ELECTRICAL CONDUIT TUBING AND JOINTS THEREFOR, OF BASE METAL LINED WITH INSULATING MATERIAL",
+        "hsn_code": "8547",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "85479090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL CONDUIT TUBING AND JOINS THEREFOR",
+        "hsn_code": "85479020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATING FITTINGS FOR ELECTRICAL MACHINES, APPLIANCES OR EQUIPMENT, BEING\nFITTINGS WHOLLY OF INSULATING MATERIAL APART FROM ANY MINOR COMPONENTS OF\nMETAL (FOR EXAMPLE, THREADED SOCKETS) INCORPORATED DURING MOULDING SOLELY FOR\nTHE PURPOSES OF ASSEMBLY, OTHER THAN INSULATORS OF HEADING 8546; ELECTRICAL\nCONDUIT TUBING AND JOINTS THEREFOR, OF BASE METAL LINED WITH INSULATING MATERIAL -\nOTHER : ELECTRICAL INSULATING FITTINGS OF GLASS",
+        "hsn_code": "85479010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATING FITTINGS FOR ELECTRICAL MACHINES, APPLIANCES OR EQUIPMENT, BEING\nFITTINGS WHOLLY OF INSULATING MATERIAL APART FROM ANY MINOR COMPONENTS OF\nMETAL (FOR EXAMPLE, THREADED SOCKETS) INCORPORATED DURING MOULDING SOLELY FOR\nTHE PURPOSES OF ASSEMBLY, OTHER THAN INSULATORS OF HEADING 8546; ELECTRICAL\nCONDUIT TUBING AND JOINTS THEREFOR, OF BASE METAL LINED WITH INSULATING MATERIAL -\nINSULATING FITTINGS OF PLASTICS",
+        "hsn_code": "85472000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATING FITTINGS FOR ELECTRICAL MACHINES, APPLIANCES OR EQUIPMENT, BEING\nFITTINGS WHOLLY OF INSULATING MATERIAL APART FROM ANY MINOR COMPONENTS OF\nMETAL (FOR EXAMPLE, THREADED SOCKETS) INCORPORATED DURING MOULDING SOLELY FOR\nTHE PURPOSES OF ASSEMBLY, OTHER THAN INSULATORS OF HEADING 8546; ELECTRICAL\nCONDUIT TUBING AND JOINTS THEREFOR, OF BASE METAL LINED WITH INSULATING MATERIAL -\nINSULATING FITTINGS OF CERAMICS: OTHER",
+        "hsn_code": "85471090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATING FITTINGS FOR ELECTRICAL MACHINES, APPLIANCES OR EQUIPMENT, BEING\nFITTINGS WHOLLY OF INSULATING MATERIAL APART FROM ANY MINOR COMPONENTS OF\nMETAL (FOR EXAMPLE, THREADED SOCKETS) INCORPORATED DURING MOULDING SOLELY FOR\nTHE PURPOSES OF ASSEMBLY, OTHER THAN INSULATORS OF HEADING 8546; ELECTRICAL\nCONDUIT TUBING AND JOINTS THEREFOR, OF BASE METAL LINED WITH INSULATING MATERIAL -\nINSULATING FITTINGS OF CERAMICS: PORCELAIN BUSHINGS FOR VOLTAGE 66 KV OR ABOVE",
+        "hsn_code": "85471040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATING FITTINGS FOR ELECTRICAL MACHINES, APPLIANCES OR EQUIPMENT, BEING\nFITTINGS WHOLLY OF INSULATING MATERIAL APART FROM ANY MINOR COMPONENTS OF\nMETAL (FOR EXAMPLE, THREADED SOCKETS) INCORPORATED DURING MOULDING SOLELY FOR\nTHE PURPOSES OF ASSEMBLY, OTHER THAN INSULATORS OF HEADING 8546; ELECTRICAL\nCONDUIT TUBING AND JOINTS THEREFOR, OF BASE METAL LINED WITH INSULATING MATERIAL -\nINSULATING FITTINGS OF CERAMICS: PORCELAIN BUSHINGS FOR VOLTAGE 11 KV OR ABOVE BUT\nUPTO 66 KV",
+        "hsn_code": "85471030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATING FITTINGS FOR ELECTRICAL MACHINES, APPLIANCES OR EQUIPMENT, BEING\nFITTINGS WHOLLY OF INSULATING MATERIAL APART FROM ANY MINOR COMPONENTS OF\nMETAL (FOR EXAMPLE, THREADED SOCKETS) INCORPORATED DURING MOULDING SOLELY FOR\nTHE PURPOSES OF ASSEMBLY, OTHER THAN INSULATORS OF HEADING 8546; ELECTRICAL\nCONDUIT TUBING AND JOINTS THEREFOR, OF BASE METAL LINED WITH INSULATING MATERIAL -\nINSULATING FITTINGS OF CERAMICS: PORCELAIN BUSHINGS FOR VOLTAGE 6.6 KV OR ABOVE BUT\nBELOW 11 KV",
+        "hsn_code": "85471020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATING FITTINGS FOR ELECTRICAL MACHINES, APPLIANCES OR EQUIPMENT, BEING\nFITTINGS WHOLLY OF INSULATING MATERIAL APART FROM ANY MINOR COMPONENTS OF\nMETAL (FOR EXAMPLE, THREADED SOCKETS) INCORPORATED DURING MOULDING SOLELY FOR\nTHE PURPOSES OF ASSEMBLY, OTHER THAN INSULATORS OF HEADING 8546; ELECTRICAL\nCONDUIT TUBING AND JOINTS THEREFOR, OF BASE METAL LINED WITH INSULATING MATERIAL -\nINSULATING FITTINGS OF CERAMICS: PORCELAIN BUSHING BELOW 6.6 KV",
+        "hsn_code": "85471010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL INSULATORS OF ANY MATERIAL",
+        "hsn_code": "8546",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL INSULATORS OF ANY MATERIAL OTHER : OTHER",
+        "hsn_code": "85469090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL INSULATORS OF ANY MATERIAL OTHER : HEAT SHRINKABLE COMPONENTS",
+        "hsn_code": "85469010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL INSULATORS OF ANY MATERIAL - OF CERAMICS - PORCELAIN PIN INSULATORS:\nOTHER",
+        "hsn_code": "85462090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL INSULATORS OF ANY MATERIAL - OF CERAMICS - OTHER LOW TENSION PROCELAIN\nINSULATORS INCLUDING TELEGRAPH AND TELEPHONE INSULATORS",
+        "hsn_code": "85462050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL INSULATORS OF ANY MATERIAL - OF CERAMICS - OTHER HIGH TENSION PROCELAIN\nSOLID CORE INSULATORS",
+        "hsn_code": "85462040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL INSULATORS OF ANY MATERIAL - OF CERAMICS - PORCELAIN PIN INSULATORS:\nABOVE 66 KV",
+        "hsn_code": "85462039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL INSULATORS OF ANY MATERIAL - OF CERAMICS - PORCELAIN PIN INSULATORS:\nABOVE 11 KV BUT UPTO 66 KV",
+        "hsn_code": "85462033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL INSULATORS OF ANY MATERIAL - OF CERAMICS - PORCELAIN PIN INSULATORS: 6.6\nKV OR ABOVE BUT UP TO 11 KV",
+        "hsn_code": "85462032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL INSULATORS OF ANY MATERIAL - OF CERAMICS - PORCELAIN PIN INSULATORS:\nBELOW 6.6 KV",
+        "hsn_code": "85462031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL INSULATORS OF ANY MATERIAL - OF CERAMICS:- PORCELAIN POST INSULATORS :\nABOVE 132 KV",
+        "hsn_code": "85462029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL INSULATORS OF ANY MATERIAL - OF CERAMICS:- PORCELAIN POST INSULATORS :\nABOVE 66 KV BUT UPTO 132 KV",
+        "hsn_code": "85462024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL INSULATORS OF ANY MATERIAL - OF CERAMICS:- PORCELAIN POST INSULATORS :\nABOVE 11 KV BUT UPTO 66 KV",
+        "hsn_code": "85462023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL INSULATORS OF ANY MATERIAL - OF CERAMICS:- PORCELAIN POST INSULATORS : 6.6\nKV OR ABOVE BUT UPTO 11 KV",
+        "hsn_code": "85462022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL INSULATORS OF ANY MATERIAL - OF CERAMICS:- PORCELAIN POST INSULATORS :\nBELOW 6.6 KV",
+        "hsn_code": "85462021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL INSULATORS OF ANY MATERIAL - OF CERAMICS: - PORCELAIN DISCS AND STRINGS :\nOTHER",
+        "hsn_code": "85462019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL INSULATORS OF ANY MATERIAL - OF CERAMICS: - PORCELAIN DISCS AND STRINGS :\nPORCELAIN BELOW 6.6 KV",
+        "hsn_code": "85462011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL INSULATORS OF ANY MATERIAL - OF GLASS",
+        "hsn_code": "85461000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON ELECTRODES, CARBON BRUSHES, LAMP CARBONS, BATTERY CARBONS AND OTHER ARTICLES OF GRAPHITE OR OTHER CARBON, WITH OR WITHOUT METAL, OF A KIND USED FOR ELECTRICAL PURPOSES",
+        "hsn_code": "8545",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON ELECTRODES, CARBON BRUSHES, LAMP CARBONS, BATTERY CARBONS AND OTHER\nARTICLES OF GRAPHITE OR OTHER CARBON, WITH OR WITHOUT METAL, OF A KIND USED FOR\nELECTRICAL PURPOSES OTHER",
+        "hsn_code": "85459090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON ELECTRODES, CARBON BRUSHES, LAMP CARBONS, BATTERY CARBONS AND OTHER\nARTICLES OF GRAPHITE OR OTHER CARBON, WITH OR WITHOUT METAL, OF A KIND USED FOR\nELECTRICAL PURPOSES BATTERY CARBON",
+        "hsn_code": "85459020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON ELECTRODES, CARBON BRUSHES, LAMP CARBONS, BATTERY CARBONS AND OTHER\nARTICLES OF GRAPHITE OR OTHER CARBON, WITH OR WITHOUT METAL, OF A KIND USED FOR\nELECTRICAL PURPOSES ARC-LAMP CARBON",
+        "hsn_code": "85459010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON ELECTRODES, CARBON BRUSHES, LAMP CARBONS, BATTERY CARBONS AND OTHER\nARTICLES OF GRAPHITE OR OTHER CARBON, WITH OR WITHOUT METAL, OF A KIND USED FOR\nELECTRICAL PURPOSES BRUSHES",
+        "hsn_code": "85452000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON ELECTRODES, CARBON BRUSHES, LAMP CARBONS, BATTERY CARBONS AND OTHER\nARTICLES OF GRAPHITE OR OTHER CARBON, WITH OR WITHOUT METAL, OF A KIND USED FOR\nELECTRICAL PURPOSES - ELECTRODES: OTHER",
+        "hsn_code": "85451900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON ELECTRODES, CARBON BRUSHES, LAMP CARBONS, BATTERY CARBONS AND OTHER\nARTICLES OF GRAPHITE OR OTHER CARBON, WITH OR WITHOUT METAL, OF A KIND USED FOR\nELECTRICAL PURPOSES - ELECTRODES: OF A KIND USED FOR FURNACES",
+        "hsn_code": "85451100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE) AND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS; OPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT ASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS",
+        "hsn_code": "8544",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OPTICAL FIBRE\nCABLES: OTHER",
+        "hsn_code": "85447090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OPTICAL FIBRE\nCABLES: LEAD ALLOY SHEATHED CABLES FOR LIGHTING PURPOSES",
+        "hsn_code": "85447010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE EXCEEDING 1000 V: OTHER",
+        "hsn_code": "85446090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE EXCEEDING 1000 V: RUBBER INSULATED",
+        "hsn_code": "85446030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE EXCEEDING 1000 V: PLASTIC INSULATED",
+        "hsn_code": "85446020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE EXCEEDING 1000 V: PAPER INSULATED",
+        "hsn_code": "85446010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE EXCEEDING 80 V BUT NOT EXCEEDING 1,000 V: - OTHER : OTHER",
+        "hsn_code": "85445990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE EXCEEDING 80 V BUT NOT EXCEEDING 1,000 V: - OTHER : OTHER\nRUBBER INSULATED",
+        "hsn_code": "85445950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE EXCEEDING 80 V BUT NOT EXCEEDING 1,000 V: - OTHER :\nRUBBER INSULATED OF A KIND USED FOR TELECOMMUNICATION",
+        "hsn_code": "85445940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE EXCEEDING 80 V BUT NOT EXCEEDING 1,000 V: - OTHER : OTHER\nPLASTIC INSULATED",
+        "hsn_code": "85445930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE EXCEEDING 80 V BUT NOT EXCEEDING 1,000 V: - OTHER :\nPLASTIC INSULATED OF A KIND USED FOR TELECOMMUNICATION",
+        "hsn_code": "85445920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE EXCEEDING 80 V BUT NOT EXCEEDING 1,000 V: - OTHER : PAPER\nINSULATED",
+        "hsn_code": "85445910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE EXCEEDING 80 V BUT NOT EXCEEDING 1,000 V: - FITTED WITH\nCONNECTORS: OTHER",
+        "hsn_code": "85445190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE EXCEEDING 80 V BUT NOT EXCEEDING 1,000 V: - FITTED WITH\nCONNECTORS: OTHER RUBBER INSULATED",
+        "hsn_code": "85445150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE EXCEEDING 80 V BUT NOT EXCEEDING 1,000 V: - FITTED WITH\nCONNECTORS: RUBBER INSULATED OF A KIND USED FOR TELECOMMUNICATION",
+        "hsn_code": "85445140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE EXCEEDING 80 V BUT NOT EXCEEDING 1,000 V: - FITTED WITH\nCONNECTORS: OTHER PLASTIC INSULATED",
+        "hsn_code": "85445130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE EXCEEDING 80 V BUT NOT EXCEEDING 1,000 V: - FITTED WITH\nCONNECTORS: PLASTIC INSULATED, OF A KIND USED FOR TELECOMMUNICATION",
+        "hsn_code": "85445120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE EXCEEDING 80 V BUT NOT EXCEEDING 1,000 V: - FITTED WITH\nCONNECTORS: PAPER INSULATED",
+        "hsn_code": "85445110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 80 V -- OTHER --- OTHER ---- OTHER",
+        "hsn_code": "85444999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 80 V -- OTHER --- OTHER ----RUBBER INSULATED\nOF A KIND USED IN TELECOMMUNICATION",
+        "hsn_code": "85444993",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 80 V -- OTHER --- OTHER : ---- PLASTIC\nINSULATED OF A KIND USED IN TELECOMMUNICATION",
+        "hsn_code": "85444992",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 80 V -- OTHER --- OTHER ----PAPER INSULATED\nOF A KIND USED IN TELECOMMUNICATION",
+        "hsn_code": "85444991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 80 V: - OTHER : OTHER",
+        "hsn_code": "85444990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 80 V -- OTHER --- RUBBER INSULATED",
+        "hsn_code": "85444930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 80 V -- OTHER --- PLASTIC INSULATED",
+        "hsn_code": "85444920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 80 V: - OTHER : - TELEPHONE CABLES : OTHER",
+        "hsn_code": "85444919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 80 V: - OTHER : - TELEPHONE CABLES : DRY\nCORE PAPER INSULATED",
+        "hsn_code": "85444911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 80 V -- OTHER --- PAPER INSULATED",
+        "hsn_code": "85444910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 1000 V -- FITTED WITH CONNECTORS --- OTHER\n: ----OTHER",
+        "hsn_code": "85444299",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 1000 V -- FITTED WITH CONNECTORS --- OTHER\n: RUBBER INSULATED , OF A KIND USED IN TELECOMMUNICATION",
+        "hsn_code": "85444293",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 1000 V -- FITTED WITH CONNECTORS --- OTHER\n: PLASTIC INSULATED OF A KIND USED IN TELECOMMUNICATION",
+        "hsn_code": "85444292",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 1000 V -- FITTED WITH CONNECTORS --- OTHER\n: PAPER INSULATED OF A KIND USED IN TELECOMMUNICATION",
+        "hsn_code": "85444291",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 1000 V -- FITTED WITH CONNECTORS ---\nRUBBER INSULATED",
+        "hsn_code": "85444230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 1000 V -- FITTED WITH CONNECTORS ---\nPLASTIC INSULATED",
+        "hsn_code": "85444220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 1000 V -- FITTED WITH CONNECTORS --- PAPER\nINSULATED",
+        "hsn_code": "85444210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 80 V: - FITTED WITH CONNECTORS: OTHER",
+        "hsn_code": "85444190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 80 V: - FITTED WITH CONNECTORS: RUBBER\nINSULATED",
+        "hsn_code": "85444140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 80 V: - FITTED WITH CONNECTORS: PLASTIC\nINSULATED",
+        "hsn_code": "85444130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 80 V: - FITTED WITH CONNECTORS: PAPER\nINSULATED",
+        "hsn_code": "85444120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 80 V: - FITTED WITH CONNECTORS: -\nTELEPHONE CABLES: OTHER",
+        "hsn_code": "85444119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - OTHER ELECTRIC\nCONDUCTORS, FOR A VOLTAGE NOT EXCEEDING 80 V: - FITTED WITH CONNECTORS: -\nTELEPHONE CABLES: DRY CORE PAPER INSULATED",
+        "hsn_code": "85444111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - IGNITION WIRING\nSETS AND OTHER WIRING SETS OF A KIND USED IN VEHICLES, AIRCRAFT OR SHIPS",
+        "hsn_code": "85443000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS- CO-AXIAL CABLE AND\nOTHER CO-AXIAL ELECTRIC CONDUCTORS: OTHER",
+        "hsn_code": "85442090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS- CO-AXIAL CABLE AND\nOTHER CO-AXIAL ELECTRIC CONDUCTORS: CO-AXIAL CABLE",
+        "hsn_code": "85442010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - WINDING WIRE: -\nOTHER: OTHER",
+        "hsn_code": "85441990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - WINDING WIRE: -\nOTHER: RUBBER INSULATED",
+        "hsn_code": "85441930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - WINDING WIRE: -\nOTHER: PLASTIC INSULATED",
+        "hsn_code": "85441920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - WINDING WIRE: -\nOTHER: ASBESTOS COVERED",
+        "hsn_code": "85441910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - WINDING WIRE: - OF\nCOPPER: OTHER",
+        "hsn_code": "85441190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSULATED (INCLUDING ENAMELLED OR ANODISED) WIRE, CABLE (INCLUDING CO-AXIAL CABLE)\nAND OTHER INSULATED ELECTRIC CONDUCTORS, WHETHER OR NOT FITTED WITH CONNECTORS;\nOPTICAL FIBRE CABLES, MADE UP OF INDIVIDUALLY SHEATHED FIBRES, WHETHER OR NOT\nASSEMBLED WITH ELECTRIC CONDUCTORS OR FITTED WITH CONNECTORS - WINDING WIRE: - OF\nCOPPER: ENAMELLED",
+        "hsn_code": "85441110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER",
+        "hsn_code": "8543",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - PARTS",
+        "hsn_code": "85439000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: -\nOTHER : OTHER",
+        "hsn_code": "85438999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: -\nOTHER : TIME CODE GENERATOR",
+        "hsn_code": "85438995",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: -\nOTHER : AUDIO VIDEO STEREO ENCODERS",
+        "hsn_code": "85438994",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: -\nOTHER : PROFESSIONAL BEAUTY CARE EQUIPMENT",
+        "hsn_code": "85438993",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: -\nOTHER : EQUIPMENT OR GADGETS BASED ON SOLAR ENERGY",
+        "hsn_code": "85438992",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: -\nOTHER : RF (RADIO FREQUENCY) POWER AMPLIFIERS AND NOISE GENERATORS FOR\nCOMMUNICATION JAMMING EQUIPMENT, STATIC OR MOBILE OR MANPORTABLE",
+        "hsn_code": "85438991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: -\nGRAPHIC EQUALISER AND SYNTHESISED RECEIVERS: SYNTHESISED RECEIVERS",
+        "hsn_code": "85438982",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: -\nGRAPHIC EQUALISER AND SYNTHESISED RECEIVERS: GRAPHIC EQUALISERS",
+        "hsn_code": "85438981",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: -\nAMPLIFIER: OTHER",
+        "hsn_code": "85438979",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: -\nAMPLIFIER: LIMITING AMPLIFIER, VIDEO DISTRIBUTION AMPLIFIER AND STABILISING AMPLIFIERS",
+        "hsn_code": "85438972",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: -\nAMPLIFIER: BROADCAST AMPLIFIER",
+        "hsn_code": "85438971",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER:\nCOLOUR CORRECTORS",
+        "hsn_code": "85438960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: - EDIT\nCONTROL UNIT: OTHER",
+        "hsn_code": "85438959",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: - EDIT\nCONTROL UNIT: OTHER VIDEO CONTROL UNITS",
+        "hsn_code": "85438952",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: - EDIT\nCONTROL UNIT: COMPUTERISED EDITING SYSTEM CONTROLLING MORE THAN THREE VIDEO\nEDITING MACHINES",
+        "hsn_code": "85438951",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: - VIDEO\nSPECIAL EFFECT EQUIPMENTS: OTHER",
+        "hsn_code": "85438949",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: - VIDEO\nSPECIAL EFFECT EQUIPMENTS: VIDEO MATTING MACHINES",
+        "hsn_code": "85438946",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: - VIDEO\nSPECIAL EFFECT EQUIPMENTS: VIDEO TYPEWRITER",
+        "hsn_code": "85438945",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: - VIDEO\nSPECIAL EFFECT EQUIPMENTS: PAINT BOX",
+        "hsn_code": "85438944",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: - VIDEO\nSPECIAL EFFECT EQUIPMENTS: DIGITAL LAYERING MACHINE",
+        "hsn_code": "85438943",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: - VIDEO\nSPECIAL EFFECT EQUIPMENTS: VIDEO EFFECT SYSTEM",
+        "hsn_code": "85438942",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: - VIDEO\nSPECIAL EFFECT EQUIPMENTS: VIDEO MIXING SYSTEM OR CONSOLES",
+        "hsn_code": "85438941",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: -\nAUDIO SPECIAL EFFECT EQUIPMENTS: OTHER",
+        "hsn_code": "85438939",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: -\nAUDIO SPECIAL EFFECT EQUIPMENTS: MIXING SYSTEMS OR CONSOLES",
+        "hsn_code": "85438932",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: -\nAUDIO SPECIAL EFFECT EQUIPMENTS: DIGITAL REVERBERATORS",
+        "hsn_code": "85438931",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: MINE\nDETECTORS",
+        "hsn_code": "85438920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: - OTHER: METAL\nDETECTORS",
+        "hsn_code": "85438910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS: PROXIMITY\nCARDS AND TAGS",
+        "hsn_code": "85438100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- OTHER ----\nOTHER",
+        "hsn_code": "85437099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- OTHER ----\nTIME CODE GENERATOR",
+        "hsn_code": "85437095",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- OTHER ----\nAUDIO VISUAL STEREO ENCODERS",
+        "hsn_code": "85437094",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- OTHER ----\nPROFESSIONAL BEAUTY CARE EQUIPMENT",
+        "hsn_code": "85437093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- OTHER ----\nEQUIPMENT GADGETS BASED ON SOLAR ENERGY",
+        "hsn_code": "85437092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- OTHER ---- RF\n(RADIO FREQUENCY) POWER AMPLIFIER AND NOISE GENERATORS FOR COMMUNICATION\nJAMMING EQUIPMENT STATIC AND MOBILE OR MAN-PORTABLE",
+        "hsn_code": "85437091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- GRAPHIC\nEQUALIZER AND SYNTHESIZED RECEIVERS ---- SYNTHESISED , RECEIVERS",
+        "hsn_code": "85437072",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- GRAPHIC\nEQUALIZER AND SYNTHESIZED RECEIVERS ---- GRAPHIC EQUALIZER",
+        "hsn_code": "85437071",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- AMPLIFIER ----\nOTHER",
+        "hsn_code": "85437069",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- AMPLIFIER ----\nLIMITING AMPLIFIER, VIDEO DISTRIBUTION AMPLIFIER AND STABILIZING AMPLIFIERS",
+        "hsn_code": "85437062",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- AMPLIFIER ----\nBROADCAST AMPLIFIER",
+        "hsn_code": "85437061",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- EDIT CONTROL\nUNIT ---- COLOUR CORRECTOR",
+        "hsn_code": "85437050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- EDIT CONTROL\nUNIT ---- OTHER",
+        "hsn_code": "85437049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- EDIT CONTROL\nUNIT ---- OTHER VIDEO CONTROL UNIT",
+        "hsn_code": "85437042",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- EDIT CONTROL\nUNIT ---- COMPUTERISED EDITING SYSTEM CONTROLLING MORE THAN THREE VIDEO EDITING\nMACHINES",
+        "hsn_code": "85437041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- VIDEO SPECIAL\nEFFECT EQUIPMENTS ---- OTHER",
+        "hsn_code": "85437039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- VIDEO SPECIAL\nEFFECT EQUIPMENTS ---- VIDEO MATTING MACHINE",
+        "hsn_code": "85437036",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- VIDEO SPECIAL\nEFFECT EQUIPMENTS ---- VIDEO TYPEWRITER",
+        "hsn_code": "85437035",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- VIDEO SPECIAL\nEFFECT EQUIPMENTS ---- PAINT BOX",
+        "hsn_code": "85437034",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- VIDEO SPECIAL\nEFFECT EQUIPMENTS ---- DIGITAL LAYERING MACHINE",
+        "hsn_code": "85437033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- VIDEO SPECIAL\nEFFECT EQUIPMENTS ---- VIDEO EFFECT SYSTEM",
+        "hsn_code": "85437032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- VIDEO SPECIAL\nEFFECT EQUIPMENTS ---- VIDEO MIXING SYSTEM OR CONSOLES",
+        "hsn_code": "85437031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- AUDIO SPECIAL\nEFFECT EQUIPMENT ---- OTHER",
+        "hsn_code": "85437029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- AUDIO SPECIAL\nEFFECT EQUIPMENT ---- MIXING SYSTEM OR CONSOLES",
+        "hsn_code": "85437022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS --- AUDIO SPECIAL\nEFFECT EQUIPMENT ---- DIGITAL REVERBERATORS",
+        "hsn_code": "85437021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS ---- OTHER",
+        "hsn_code": "85437019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS ---- MINE\nDETECTOR",
+        "hsn_code": "85437013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND APPARATUS ---- METAL\nDETECTOR",
+        "hsn_code": "85437012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER ELECTRIC FENCE ENERGISERS",
+        "hsn_code": "85434000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER MACHINES AND APPARATUS FOR ELECTROPLATING,\nELECTROLYSIS OR ELECTROPHORESIS",
+        "hsn_code": "85433000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - SIGNAL GENERATORS: OTHER",
+        "hsn_code": "85432090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - SIGNAL GENERATORS: TACHO GENERATORS",
+        "hsn_code": "85432030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - SIGNAL GENERATORS: IMPULSE GENERATORS",
+        "hsn_code": "85432020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - SIGNAL GENERATORS: SWEEP GENERATORS",
+        "hsn_code": "85432010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - PARTICLE ACCELERATORS: - OTHER: OTHER\nINCLUDING CYCLOTRONS",
+        "hsn_code": "85431990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - PARTICLE ACCELERATORS: - OTHER:\nSYNCHROCYCLOTRONS, SYNCHROTRONS",
+        "hsn_code": "85431920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - PARTICLE ACCELERATORS: - OTHER: VANE GRAFF,\nCOCK-CROFT, WALTON ACCELERATORS",
+        "hsn_code": "85431910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - PARTICLE ACCELERATORS: ION IMPLANTERS FOR\nDOPING SEMI-CONDUCTOR MATERIALS",
+        "hsn_code": "85431100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - PARTICLE ACCELERATORS --- OTHER INCLUDING\nCYCLOTRONS",
+        "hsn_code": "85431090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - PARTICLE ACCELERATORS --- SYNCHROCYCLOTRONS,\nSYNCHROTRONS",
+        "hsn_code": "85431030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - PARTICLE ACCELERATORS --- VANE GRAFF, COCK\nCROFT, WALTON ACCELERATORS",
+        "hsn_code": "85431020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL MACHINES AND APPARATUS HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - PARTICLE ACCELERATORS --- ION IMPLANTERS FOR\nDOPING SEMI CONDUCTOR MATERIAL",
+        "hsn_code": "85431010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRONIC INTEGRATED CIRCUITS",
+        "hsn_code": "8542",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRONIC INTEGRATED CIRCUITS - PARTS",
+        "hsn_code": "85429000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRONIC INTEGRATED CIRCUITS AND MICRO-ASSEMBLIES ELECTRONIC MICROASSEMBLIES",
+        "hsn_code": "85427000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRONIC INTEGRATED CIRCUITS AND MICRO-ASSEMBLIES HYBRID INTEGRATED CIRCUITS",
+        "hsn_code": "85426000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRONIC INTEGRATED CIRCUITS - ELECTRONIC INTEGRATED CIRCUITS -- OTHER",
+        "hsn_code": "85423900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRONIC INTEGRATED CIRCUITS - ELECTRONIC INTEGRATED CIRCUITS - AMPLIFIERS",
+        "hsn_code": "85423300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRONIC INTEGRATED CIRCUITS - ELECTRONIC INTEGRATED CIRCUITS -- MEMORIES",
+        "hsn_code": "85423200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRONIC INTEGRATED CIRCUITS - ELECTRONIC INTEGRATED CIRCUITS -- PROCESSORS AND\nCONTROLLERS, WHETHER OR NOT COMBINED WITH MEMORIES, CONVERTERS, LOGIC\nCIRCUITS,AMPLIFIERS, CLOCK AND TIMING CIRCUITS, OR OTHER CIRCUITS",
+        "hsn_code": "85423100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRONIC INTEGRATED CIRCUITS AND MICRO-ASSEMBLIES - MONOLITHIC INTEGRATED\nCIRCUITS: - OTHER: OTHER",
+        "hsn_code": "85422990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRONIC INTEGRATED CIRCUITS AND MICRO-ASSEMBLIES - MONOLITHIC INTEGRATED\nCIRCUITS: - OTHER: CARDS INCORPORATING ONLY A SINGLE ELECTRONIC INTEGRATED CIRCUIT\nWITH OPTICAL STRIP",
+        "hsn_code": "85422910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRONIC INTEGRATED CIRCUITS AND MICRO-ASSEMBLIES - MONOLITHIC INTEGRATED\nCIRCUITS: DIGITAL",
+        "hsn_code": "85422100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRONIC INTEGRATED CIRCUITS AND MICRO-ASSEMBLIES -CARDS INCORPORATING AN\nELECTRONIC INTEGRATED CIRCUIT *SMARV CARDS): OTHER",
+        "hsn_code": "85421090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRONIC INTEGRATED CIRCUITS AND MICRO-ASSEMBLIES -CARDS INCORPORATING AN\nELECTRONIC INTEGRATED CIRCUIT *SMARV CARDS): MEMORY CARDS",
+        "hsn_code": "85421020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRONIC INTEGRATED CIRCUITS AND MICRO-ASSEMBLIES -CARDS INCORPORATING AN\nELECTRONIC INTEGRATED CIRCUIT *SMARV CARDS): SIM CARDS",
+        "hsn_code": "85421010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIODES, TRANSISTORS AND SIMILAR SEMI-CONDUCTOR DEVICES; PHOTOSENSITIVE SEMI-CONDUCTOR DEVICES, INCLUDING PHOTOVOLTAIC CELLS WHETHER OR NOT ASSEMBLED IN MODULES OR MADE-UP INTO PANELS; LIGHT EMITTING DIODES; MOUNTED PIEZO-ELECTRIC CRYSTAL",
+        "hsn_code": "8541",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIODES, TRANSISTORS AND SIMILAR SEMI-CONDUCTOR DEVICES; PHOTOSENSITIVE SEMI\nCONDUCTOR DEVICES, INCLUDING PHOTOVOLTAIC CELLS WHETHER OR NOT ASSEMBLED IN\nMODULES OR MADE UP INTO PANELS; LIGHT EMITTING DIODES; MOUNTED PIEZO-ELECTRIC\nCRYSTALS PARTS",
+        "hsn_code": "85419000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIODES, TRANSISTORS AND SIMILAR SEMI-CONDUCTOR DEVICES; PHOTOSENSITIVE SEMI\nCONDUCTOR DEVICES, INCLUDING PHOTOVOLTAIC CELLS WHETHER OR NOT ASSEMBLED IN\nMODULES OR MADE UP INTO PANELS; LIGHT EMITTING DIODES; MOUNTED PIEZO-ELECTRIC\nCRYSTALS MOUNTED PIEZO-ELECTRIC CRYSTALS",
+        "hsn_code": "85416000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIODES, TRANSISTORS AND SIMILAR SEMI-CONDUCTOR DEVICES; PHOTOSENSITIVE SEMI\nCONDUCTOR DEVICES, INCLUDING PHOTOVOLTAIC CELLS WHETHER OR NOT ASSEMBLED IN\nMODULES OR MADE UP INTO PANELS; LIGHT EMITTING DIODES; MOUNTED PIEZO-ELECTRIC\nCRYSTALS OTHER SEMI-CONDUCTORS DEVICES",
+        "hsn_code": "85415000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIODES, TRANSISTORS AND SIMILAR SEMI-CONDUCTOR DEVICES; PHOTOSENSITIVE SEMI\nCONDUCTOR DEVICES, INCLUDING PHOTOVOLTAIC CELLS WHETHER OR NOT ASSEMBLED IN\nMODULES OR MADE UP INTO PANELS; LIGHT EMITTING DIODES; MOUNTED PIEZO-ELECTRIC\nCRYSTALS -PHOTOSENSITIVE SEMI-CONDUCTOR DEVICES, INCLUDING PHOTOVOLTAIC CELLS\nWHETHER OR NOT ASSEMBLED IN MODULES OR MADE UP INTO PANELS; LIGHT EMITTING\nDIODES: OTHER",
+        "hsn_code": "85414090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIODES, TRANSISTORS AND SIMILAR SEMI-CONDUCTOR DEVICES; PHOTOSENSITIVE SEMI\nCONDUCTOR DEVICES, INCLUDING PHOTOVOLTAIC CELLS WHETHER OR NOT ASSEMBLED IN\nMODULES OR MADE UP INTO PANELS; LIGHT EMITTING DIODES; MOUNTED PIEZO-ELECTRIC\nCRYSTALS -PHOTOSENSITIVE SEMI-CONDUCTOR DEVICES, INCLUDING PHOTOVOLTAIC CELLS\nWHETHER OR NOT ASSEMBLED IN MODULES OR MADE UP INTO PANELS; LIGHT EMITTING\nDIODES: LIGHT EMITTING DIODES (ELECTRO-LUMINESCENT)",
+        "hsn_code": "85414020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIODES, TRANSISTORS AND SIMILAR SEMI-CONDUCTOR DEVICES; PHOTOSENSITIVE SEMI\nCONDUCTOR DEVICES, INCLUDING PHOTOVOLTAIC CELLS WHETHER OR NOT ASSEMBLED IN\nMODULES OR MADE UP INTO PANELS; LIGHT EMITTING DIODES; MOUNTED PIEZO-ELECTRIC\nCRYSTALS -PHOTOSENSITIVE SEMI-CONDUCTOR DEVICES, INCLUDING PHOTOVOLTAIC CELLS\nWHETHER OR NOT ASSEMBLED IN MODULES OR MADE UP INTO PANELS; LIGHT EMITTING\nDIODES: - PHOTOCELLS: OTHER",
+        "hsn_code": "85414019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIODES, TRANSISTORS AND SIMILAR SEMI-CONDUCTOR DEVICES; PHOTOSENSITIVE SEMI\nCONDUCTOR DEVICES, INCLUDING PHOTOVOLTAIC CELLS WHETHER OR NOT ASSEMBLED IN\nMODULES OR MADE UP INTO PANELS; LIGHT EMITTING DIODES; MOUNTED PIEZO-ELECTRIC\nCRYSTALS -PHOTOSENSITIVE SEMI-CONDUCTOR DEVICES, INCLUDING PHOTOVOLTAIC CELLS\nWHETHER OR NOT ASSEMBLED IN MODULES OR MADE UP INTO PANELS; LIGHT EMITTING\nDIODES: - PHOTOCELLS: SOLAR CELLS WHETHER OR NOT ASSEMBLED IN MODULES OR PANELS",
+        "hsn_code": "85414011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIODES, TRANSISTORS AND SIMILAR SEMI-CONDUCTOR DEVICES; PHOTOSENSITIVE SEMI\nCONDUCTOR DEVICES, INCLUDING PHOTOVOLTAIC CELLS WHETHER OR NOT ASSEMBLED IN\nMODULES OR MADE UP INTO PANELS; LIGHT EMITTING DIODES; MOUNTED PIEZO-ELECTRIC\nCRYSTALS -THYRISTORS, DIACS AND TRIACS, OTHER THAN PHOTOSENSITIVE DEVICES: OTHER",
+        "hsn_code": "85413090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIODES, TRANSISTORS AND SIMILAR SEMI-CONDUCTOR DEVICES; PHOTOSENSITIVE SEMI\nCONDUCTOR DEVICES, INCLUDING PHOTOVOLTAIC CELLS WHETHER OR NOT ASSEMBLED IN\nMODULES OR MADE UP INTO PANELS; LIGHT EMITTING DIODES; MOUNTED PIEZO-ELECTRIC\nCRYSTALS -THYRISTORS, DIACS AND TRIACS, OTHER THAN PHOTOSENSITIVE DEVICES:\nTHYRISTORS",
+        "hsn_code": "85413010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIODES, TRANSISTORS AND SIMILAR SEMI-CONDUCTOR DEVICES; PHOTOSENSITIVE SEMI\nCONDUCTOR DEVICES, INCLUDING PHOTOVOLTAIC CELLS WHETHER OR NOT ASSEMBLED IN\nMODULES OR MADE UP INTO PANELS; LIGHT EMITTING DIODES; MOUNTED PIEZO-ELECTRIC\nCRYSTALS - TRANSISTORS, OTHER THAN PHOTOSENSITIVE TRANSISTORSOTHER",
+        "hsn_code": "85412900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIODES, TRANSISTORS AND SIMILAR SEMI-CONDUCTOR DEVICES; PHOTOSENSITIVE SEMI\nCONDUCTOR DEVICES, INCLUDING PHOTOVOLTAIC CELLS WHETHER OR NOT ASSEMBLED IN\nMODULES OR MADE UP INTO PANELS; LIGHT EMITTING DIODES; MOUNTED PIEZO-ELECTRIC\nCRYSTALS - TRANSISTORS, OTHER THAN PHOTOSENSITIVE TRANSISTORSWITH A DISSIPATION\nRATE OF LESS THAN 1 W",
+        "hsn_code": "85412100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIODES, TRANSISTORS AND SIMILAR SEMI-CONDUCTOR DEVICES; PHOTOSENSITIVE SEMI\nCONDUCTOR DEVICES, INCLUDING PHOTOVOLTAIC CELLS WHETHER OR NOT ASSEMBLED IN\nMODULES OR MADE UP INTO PANELS; LIGHT EMITTING DIODES; MOUNTED PIEZO-ELECTRIC\nCRYSTALS DIODES, OTHER THAN PHOTOSENSITIVE OR LIGHT EMITTING DIODES",
+        "hsn_code": "85411000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "THERMIONIC, COLD CATHODE OR PHOTO-CATHODE VALVES AND TUBES (FOR EXAMPLE, VACUUM OR VAPOUR OR GAS FILLED VALVES AND TUBES, MERCURY ARC RECTIFYING VALVES AND TUBES, CATHODE-RAY TUBES, TELEVISION CAMERA TUBES)",
+        "hsn_code": "8540",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "THERMIONIC, COLD CATHODE OR PHOTO-CATHODE VALVES AND TUBES (FOR EXAMPLE,\nVACUUM OR VAPOUR OR GAS FILLED VALVES AND TUBES, MERCURY ARC RECTIFYING VALVES\nAND TUBES, CATHODE-RAY TUBES, TELEVISION CAMERA TUBES) - PARTS: OTHER",
+        "hsn_code": "85409900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "THERMIONIC, COLD CATHODE OR PHOTO-CATHODE VALVES AND TUBES (FOR EXAMPLE,\nVACUUM OR VAPOUR OR GAS FILLED VALVES AND TUBES, MERCURY ARC RECTIFYING VALVES\nAND TUBES, CATHODE-RAY TUBES, TELEVISION CAMERA TUBES) - PARTS: OF CATHODE-RAY\nTUBES",
+        "hsn_code": "85409100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "THERMIONIC, COLD CATHODE OR PHOTO-CATHODE VALVES AND TUBES (FOR EXAMPLE,\nVACUUM OR VAPOUR OR GAS FILLED VALVES AND TUBES, MERCURY ARC RECTIFYING VALVES\nAND TUBES, CATHODE-RAY TUBES, TELEVISION CAMERA TUBES) - OTHER VALVES AND TUBES:\nOTHER",
+        "hsn_code": "85408900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "THERMIONIC, COLD CATHODE OR PHOTO-CATHODE VALVES AND TUBES (FOR EXAMPLE,\nVACUUM OR VAPOUR OR GAS FILLED VALVES AND TUBES, MERCURY ARC RECTIFYING VALVES\nAND TUBES, CATHODE-RAY TUBES, TELEVISION CAMERA TUBES) - OTHER VALVES AND TUBES:\nRECEIVER OR AMPLIFIER VALVES AND TUBES",
+        "hsn_code": "85408100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "THERMIONIC, COLD CATHODE OR PHOTO-CATHODE VALVES AND TUBES (FOR EXAMPLE,\nVACUUM OR VAPOUR OR GAS FILLED VALVES AND TUBES, MERCURY ARC RECTIFYING VALVES\nAND TUBES, CATHODE-RAY TUBES, TELEVISION CAMERA TUBES) - MICROWAVE TUBES (FOR\nEXAMPLE, MAGNETRONS, KLYSTRONS, TRAVELLING WAVE TUBES, CARCINOTRONS), EXCLUDING\nGRID-CONTROLLED TUBES: OTHER",
+        "hsn_code": "85407900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "THERMIONIC, COLD CATHODE OR PHOTO-CATHODE VALVES AND TUBES (FOR EXAMPLE,\nVACUUM OR VAPOUR OR GAS FILLED VALVES AND TUBES, MERCURY ARC RECTIFYING VALVES\nAND TUBES, CATHODE-RAY TUBES, TELEVISION CAMERA TUBES) - MICROWAVE TUBES (FOR\nEXAMPLE, MAGNETRONS, KLYSTRONS, TRAVELLING WAVE TUBES, CARCINOTRONS), EXCLUDING\nGRID-CONTROLLED TUBES: MAGNETRONS",
+        "hsn_code": "85407100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "THERMIONIC, COLD CATHODE OR PHOTO-CATHODE VALVES AND TUBES (FOR EXAMPLE,\nVACUUM OR VAPOUR OR GAS FILLED VALVES AND TUBES, MERCURY ARC RECTIFYING VALVES\nAND TUBES, CATHODE-RAY TUBES, TELEVISION CAMERA TUBES) OTHER CATHODE-RAY TUBES",
+        "hsn_code": "85406000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DATA OR GRAPHIC DISPLAY TUBES, COLOUR, WITH A PHOSPHOR DOT SCREEN PITCH SMALLER\nTHAN 0.4 MM",
+        "hsn_code": "85404020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DATA OR GRAPHIC DISPLAY TUBES, MONOCHROME",
+        "hsn_code": "85404010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "THERMIONIC, COLD CATHODE OR PHOTO-CATHODE VALVES AND TUBES (FOR EXAMPLE,\nVACUUM OR VAPOUR OR GAS FILLED VALVES AND TUBES, MERCURY ARC RECTIFYING VALVES\nAND TUBES, CATHODE-RAY TUBES, TELEVISION CAMERA TUBES) TELEVISION CAMERA TUBES;\nIMAGE CONVERTERS AND INTENSIFIERS; OTHER PHOTOS-CATHODE TUBES",
+        "hsn_code": "85402000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "THERMIONIC, COLD CATHODE OR PHOTO-CATHODE VALVES AND TUBES (FOR EXAMPLE,\nVACUUM OR VAPOUR OR GAS FILLED VALVES AND TUBES, MERCURY ARC RECTIFYING VALVES\nAND TUBES, CATHODE-RAY TUBES, TELEVISION CAMERA TUBES) - CATHODE-RAY TELEVISION\nPICTURE TUBES, INCLUDING VIDEO MONITOR CATHODE-RAY TUBES: BLACK AND WHITE OR\nOTHER MONOCHROME",
+        "hsn_code": "85401200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "THERMIONIC, COLD CATHODE OR PHOTO-CATHODE VALVES AND TUBES (FOR EXAMPLE,\nVACUUM OR VAPOUR OR GAS FILLED VALVES AND TUBES, MERCURY ARC RECTIFYING VALVES\nAND TUBES, CATHODE-RAY TUBES, TELEVISION CAMERA TUBES) - CATHODE-RAY TELEVISION\nPICTURE TUBES, INCLUDING VIDEO MONITOR CATHODE-RAY TUBES: - COLOUR: OTHER",
+        "hsn_code": "85401190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "THERMIONIC, COLD CATHODE OR PHOTO-CATHODE VALVES AND TUBES (FOR EXAMPLE,\nVACUUM OR VAPOUR OR GAS FILLED VALVES AND TUBES, MERCURY ARC RECTIFYING VALVES\nAND TUBES, CATHODE-RAY TUBES, TELEVISION CAMERA TUBES) - CATHODE-RAY TELEVISION\nPICTURE TUBES, INCLUDING VIDEO MONITOR CATHODE-RAY TUBES: - COLOUR: VIDEO MONITOR\nCATHODE-RAY TUBES",
+        "hsn_code": "85401120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "THERMIONIC, COLD CATHODE OR PHOTO-CATHODE VALVES AND TUBES (FOR EXAMPLE,\nVACUUM OR VAPOUR OR GAS FILLED VALVES AND TUBES, MERCURY ARC RECTIFYING VALVES\nAND TUBES, CATHODE-RAY TUBES, TELEVISION CAMERA TUBES) - CATHODE-RAY TELEVISION\nPICTURE TUBES, INCLUDING VIDEO MONITOR CATHODE-RAY TUBES: - COLOUR: TELEVISION\nPICTURE TUBES OF 20 AND 21 SIZE, EXCEPT 21 FLAT AND FULL SQUARE (F AND FST) COLOUR TV\nPICTURE TUBES",
+        "hsn_code": "85401110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA-VIOLET OR INFRA-RED LAMPS; ARC-LAMPS",
+        "hsn_code": "8539",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - PARTS: OTHER",
+        "hsn_code": "85399090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - PARTS: PARTS OF ARC-LAMPS",
+        "hsn_code": "85399020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - PARTS: PARTS OF FLUORESCENT TUBE LAMPS",
+        "hsn_code": "85399010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - ULTRA-VIOLET OR INFRA-RED LAMPS; ARC-LAMPS :\nOTHER",
+        "hsn_code": "85394900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - ULTRA-VIOLET OR INFRA-RED LAMPS; ARC-LAMPS :\nARC-LAMPS",
+        "hsn_code": "85394100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - DISCHARGE LAMPS, OTHER THAN ULTRA-VIOLET\nLAMPS: - OTHER: OTHER",
+        "hsn_code": "85393990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - DISCHARGE LAMPS, OTHER THAN ULTRA-VIOLET\nLAMPS: - OTHER: ENERGY EFFICIENT TRIPHOSPHOR FLUORESCENT LAMPS",
+        "hsn_code": "85393910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - DISCHARGE LAMPS, OTHER THAN ULTRA-VIOLET\nLAMPS: - MERCURY OR SODIUM VAPOUR LAMPS; METAL HALIDE LAMPS: METAL HALIDE LAMPS",
+        "hsn_code": "85393230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - DISCHARGE LAMPS, OTHER THAN ULTRA-VIOLET\nLAMPS: - MERCURY OR SODIUM VAPOUR LAMPS; METAL HALIDE LAMPS: SODIUM VAPOUR\nLAMPS",
+        "hsn_code": "85393220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - DISCHARGE LAMPS, OTHER THAN ULTRA-VIOLET\nLAMPS: - MERCURY OR SODIUM VAPOUR LAMPS; METAL HALIDE LAMPS: MERCURY VAPOUR\nLAMPS",
+        "hsn_code": "85393210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - DISCHARGE LAMPS, OTHER THAN ULTRA-VIOLET\nLAMPS: - FLUORESCENT, HOT CATHODE: OTHER",
+        "hsn_code": "85393190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - DISCHARGE LAMPS, OTHER THAN ULTRA-VIOLET\nLAMPS: - FLUORESCENT, HOT CATHODE: COMPACT FLUORESCENT LAMPS",
+        "hsn_code": "85393110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - OTHER FILAMENT LAMPS, EXCLUDING ULTRA\nVIOLET OR INFRA-RED LAMPS: - OTHER: OTHER",
+        "hsn_code": "85392990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - OTHER FILAMENT LAMPS, EXCLUDING ULTRA\nVIOLET OR INFRA-RED LAMPS: - OTHER: OTHER FOR AUTOMOBILE LAMPS",
+        "hsn_code": "85392940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - OTHER FILAMENT LAMPS, EXCLUDING ULTRA\nVIOLET OR INFRA-RED LAMPS: - OTHER: MINIATURE BULBS",
+        "hsn_code": "85392930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - OTHER FILAMENT LAMPS, EXCLUDING ULTRA\nVIOLET OR INFRA-RED LAMPS: - OTHER: BULB, FOR TORCHES",
+        "hsn_code": "85392920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - OTHER FILAMENT LAMPS, EXCLUDING ULTRA\nVIOLET OR INFRA-RED LAMPS: - OTHER: OF RETAIL SALE PRICE NOT EXCEEDING RUPEES 20 PER\nBULB",
+        "hsn_code": "85392910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - OTHER FILAMENT LAMPS, EXCLUDING ULTRA\nVIOLET OR INFRA-RED LAMPS: OTHER, OF A POWER NOT EXCEEDING 200 W AND FOR A\nVOLTAGE EXCEEDING 100 V",
+        "hsn_code": "85392200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - OTHER FILAMENT LAMPS, EXCLUDING ULTRA\nVIOLET OR INFRA-RED LAMPS: - TUNGSTEN HALOGEN: - OTHER: OTHER",
+        "hsn_code": "85392190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - OTHER FILAMENT LAMPS, EXCLUDING ULTRA\nVIOLET OR INFRA-RED LAMPS: - TUNGSTEN HALOGEN: - OTHER: OTHER FOR AUTOMOBILES",
+        "hsn_code": "85392120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS - OTHER FILAMENT LAMPS, EXCLUDING ULTRA\nVIOLET OR INFRA-RED LAMPS: - TUNGSTEN HALOGEN: - OTHER: MINIATURE HALOGEN LAMPS\nWITH FITTINGS",
+        "hsn_code": "85392110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC FILAMENT OR DISCHARGE LAMPS, INCLUDING SEALED BEAM LAMP UNITS AND ULTRA\nVIOLET OR INFRA RED LAMPS; ARC-LAMPS SEALED BEAM LAMP UNITS",
+        "hsn_code": "85391000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF HEADINGS 8535, 8536 OR 8537",
+        "hsn_code": "8538",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF HEADINGS 8535,\n8536 OR 8537 OTHER",
+        "hsn_code": "85389000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF HEADINGS 8535,\n8536 OR 8537 -BOARDS, PANELS, CONSOLES, DESKS, CABINETS AND OTHER BASES FOR THE\nGOODS OF HEADING 8537, NOT EQUIPPED WITH THEIR APPARATUS: OTHER",
+        "hsn_code": "85381090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF HEADINGS 8535,\n8536 OR 8537 -BOARDS, PANELS, CONSOLES, DESKS, CABINETS AND OTHER BASES FOR THE\nGOODS OF HEADING 8537, NOT EQUIPPED WITH THEIR APPARATUS: FOR INDUSTRIAL USE",
+        "hsn_code": "85381010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BOARDS, PANELS, CONSOLES DESKS, CABINETS AND OTHER BASES, EQUIPPED WITH TWO OR MORE APPARATUS OF HEADING 8535 OR 8536, FOR ELECTRIC CONTROL OR THE DISTRIBUTION OF ELECTRICITY, INCLUDING THOSE INCORPORATING INSTRUMENTS OR APPARATUS OF CHAPTER 90, AND NUMERICAL CONTROL APPARATUS, OTHER THAN SWITCHING APPARATUS OF HEADING 8517",
+        "hsn_code": "8537",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BOARDS, PANELS, CONSOLES, DESKS, CABINETS AND OTHER BASES, EQUIPPED WITH TWO OR\nMORE APPARATUS OF HEADING 8535 OR 8536, FOR ELECTRIC CONTROL OR THE DISTRIBUTION\nOF ELECTRICITY, INCLUDING THOSE INCORPORATING INSTRUMENTS OR APPARATUS OF\nCHAPTER 90, AND NUMERICAL CONTROL APPARATUS, OTHER THAN SWITCHING APPARATUS OF\nHEADING 8517 FOR A VOLTAGE EXCEEDING 1,000 V",
+        "hsn_code": "85372000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BOARDS, PANELS, CONSOLES, DESKS, CABINETS AND OTHER BASES, EQUIPPED WITH TWO OR\nMORE APPARATUS OF HEADING 8535 OR 8536, FOR ELECTRIC CONTROL OR THE DISTRIBUTION\nOF ELECTRICITY, INCLUDING THOSE INCORPORATING INSTRUMENTS OR APPARATUS OF\nCHAPTER 90, AND NUMERICAL CONTROL APPARATUS, OTHER THAN SWITCHING APPARATUS OF\nHEADING 8517 FOR A VOLTAGE NOT EXCEEDING 1,000 V",
+        "hsn_code": "85371000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR MAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS, FUSES, SURGE SUPPRESSORS, PLUGS,  SOCKETS, LAMP-HOLDERS AND OTHER CONNECTORS,  JUNCTION BOXES), FOR A VOLTAGE NOT EXCEEDING 1,000 VOLTS: CONNECTORS FOR OPTICAL FIBRES, OPTICAL FIBRE BUNDLES OR CABLES",
+        "hsn_code": "8536",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - OTHER APPARATUS: OTHER",
+        "hsn_code": "85369090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - OTHER APPARATUS: JUNCTION BOXES",
+        "hsn_code": "85369030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - OTHER APPARATUS: MOTOR STARTERS FOR DC\nMOTORS",
+        "hsn_code": "85369020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - OTHER APPARATUS: MOTOR STARTERS FOR AC\nMOTORS",
+        "hsn_code": "85369010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - CONNECTORS FOR OPTICAL FIBRES,OPTICAL FIBRE\nBUNDLES OR CABLES",
+        "hsn_code": "85367000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - LAMP-HOLDERS, PLUGS AND SOCKETS: - OTHER: OF\nOTHER MATERIALS",
+        "hsn_code": "85366990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - LAMP-HOLDERS, PLUGS AND SOCKETS: - OTHER: OF\nPLASTIC",
+        "hsn_code": "85366910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - LAMP-HOLDERS, PLUGS AND SOCKETS: - LAMP\nHOLDERS: OF OTHER MATERIALS",
+        "hsn_code": "85366190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - LAMP-HOLDERS, PLUGS AND SOCKETS: - LAMP\nHOLDERS: OF PLASTIC",
+        "hsn_code": "85366110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - OTHER SWITCHES: OTHER",
+        "hsn_code": "85365090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - OTHER SWITCHES: OTHER SWITCHES OF PLASTIC",
+        "hsn_code": "85365020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - OTHER SWITCHES: CONTROL AND SWITCH GEARS",
+        "hsn_code": "85365010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - RELAYS: OTHER",
+        "hsn_code": "85364900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - RELAYS: FOR A VOLTAGE NOT EXCEEDING 60 V",
+        "hsn_code": "85364100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS OTHER APPARATUS FOR PROTECTING ELECTRICAL\nCIRCUITS",
+        "hsn_code": "85363000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS -AUTOMATIC CIRCUIT BREAKERS: OTHER",
+        "hsn_code": "85362090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS -AUTOMATIC CIRCUIT BREAKERS: EARTH LEAK CIRCUIT\nBREAKERS",
+        "hsn_code": "85362040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS -AUTOMATIC CIRCUIT BREAKERS: MINIATURE CIRCUIT\nBREAKERS",
+        "hsn_code": "85362030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS -AUTOMATIC CIRCUIT BREAKERS: MOULDED CASE\nCIRCUIT BREAKERS",
+        "hsn_code": "85362020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS -AUTOMATIC CIRCUIT BREAKERS: AIR CIRCUIT BREAKERS",
+        "hsn_code": "85362010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - FUSES: OTHER",
+        "hsn_code": "85361090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - FUSES: ELECTRONIC FUSES",
+        "hsn_code": "85361060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - FUSES: FUSES GEAR",
+        "hsn_code": "85361050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - FUSES: OTHER HIGH RUPTURING CAPACITY FUSES",
+        "hsn_code": "85361040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - FUSES: OTHER REWIREABLE FUSES",
+        "hsn_code": "85361030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - FUSES: FOR SWITCHES HAVING RATING ABOVE 15\nAMPS, HIGH RUPTURING CAPACITY OR REWIREABLE",
+        "hsn_code": "85361020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS,\nFUSES, SURGE SUPPRESSORS, PLUGS, SOCKETS, LAMP-HOLDERS, JUNCTION BOXES), FOR A\nVOLTAGE NOT EXCEEDING 1,000 VOLTS - FUSES: FOR SWITCHES HAVING RATING UPTO 15 AMPS,\nREWIREABLE",
+        "hsn_code": "85361010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR MAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS AND OTHER CONNECTORS, JUNCTION BOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS",
+        "hsn_code": "8535",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - OTHER : OTHER",
+        "hsn_code": "85359090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - OTHER : JUNCTION BOXES",
+        "hsn_code": "85359040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - OTHER : OTHER CONTROL AND\nSWITCHGEARS",
+        "hsn_code": "85359030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - OTHER : CONTROL GEAR AND STARTERS FOR\nDC MOTORS",
+        "hsn_code": "85359020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - OTHER : MOTOR STARTERS FOR AC MOTORS",
+        "hsn_code": "85359010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - LIGHTNING ARRESTERS, VOLTAGE LIMITERS\nAND SURGE SUPPRESSORS: SURGE SUPPRESSORS",
+        "hsn_code": "85354030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - LIGHTNING ARRESTERS, VOLTAGE LIMITERS\nAND SURGE SUPPRESSORS: VOLTAGE LIMITERS",
+        "hsn_code": "85354020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - LIGHTNING ARRESTERS, VOLTAGE LIMITERS\nAND SURGE SUPPRESSORS: LIGHTNING ARRESTERS",
+        "hsn_code": "85354010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - ISOLATING SWITCHES AND MAKE-AND\nBREAK SWITCHES: OTHER",
+        "hsn_code": "85353090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - ISOLATING SWITCHES AND MAKE-AND\nBREAK SWITCHES: OF PLASTIC",
+        "hsn_code": "85353010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - AUTOMATIC CIRCUIT BREAKERS: - OTHER :\nOTHER",
+        "hsn_code": "85352990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - AUTOMATIC CIRCUIT BREAKERS: - OTHER : -\nVACUUM CIRCUIT BREAKERS : OTHER",
+        "hsn_code": "85352929",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - AUTOMATIC CIRCUIT BREAKERS: - OTHER : -\nVACUUM CIRCUIT BREAKERS : FOR A VOLTAGE OF 400 KV",
+        "hsn_code": "85352923",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - AUTOMATIC CIRCUIT BREAKERS: - OTHER : -\nVACUUM CIRCUIT BREAKERS : FOR A VOLTAGE OF 220 KV",
+        "hsn_code": "85352922",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - AUTOMATIC CIRCUIT BREAKERS: - OTHER : -\nVACUUM CIRCUIT BREAKERS : FOR A VOLTAGE OF 132 KV",
+        "hsn_code": "85352921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - AUTOMATIC CIRCUIT BREAKERS: - OTHER : -\nSF6 CIRCUITS BREAKERS : OTHER",
+        "hsn_code": "85352919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - AUTOMATIC CIRCUIT BREAKERS: - OTHER : -\nSF6 CIRCUITS BREAKERS : FOR A VOLTAGE OF 400 KV",
+        "hsn_code": "85352913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - AUTOMATIC CIRCUIT BREAKERS: - OTHER : -\nSF6 CIRCUITS BREAKERS : FOR A VOLTAGE OF 220 KV",
+        "hsn_code": "85352912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - AUTOMATIC CIRCUIT BREAKERS: - OTHER : -\nSF6 CIRCUITS BREAKERS : FOR A VOLTAGE OF 132 KV",
+        "hsn_code": "85352911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - AUTOMATIC CIRCUIT BREAKERS: - FOR A\nVOLTAGE OF LESS THAN 72.5 KV: OTHER",
+        "hsn_code": "85352190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - AUTOMATIC CIRCUIT BREAKERS: - FOR A\nVOLTAGE OF LESS THAN 72.5 KV: - VACUUM CIRCUIT BREAKERS : OTHER",
+        "hsn_code": "85352129",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - AUTOMATIC CIRCUIT BREAKERS: - FOR A\nVOLTAGE OF LESS THAN 72.5 KV: - VACUUM CIRCUIT BREAKERS : FOR A VOLTAGE OF 66 KV",
+        "hsn_code": "85352123",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - AUTOMATIC CIRCUIT BREAKERS: - FOR A\nVOLTAGE OF LESS THAN 72.5 KV: - VACUUM CIRCUIT BREAKERS : FOR A VOLTAGE OF 33 KV",
+        "hsn_code": "85352122",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUSES, LIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION\nBOXES), FOR A VOLTAGE EXCEEDING 1,000 VOLTS - AUTOMATIC CIRCUIT BREAKERS: - FOR A\nVOLTAGE OF LESS THAN 72.5 KV: - VACUUM CIRCUIT BREAKERS : FOR A VOLTAGE OF 11 KV",
+        "hsn_code": "85352121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, FUSES,\nLIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION BOXES),\nFOR A VOLTAGE EXCEEDING 1,000 VOLTS - AUTOMATIC CIRCUIT BREAKERS: - FOR A VOLTAGE OF\nLESS THAN 72.5 KV: - SF6 CIRCUIT BREAKERS : OTHER",
+        "hsn_code": "85352119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, FUSES,\nLIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION BOXES),\nFOR A VOLTAGE EXCEEDING 1,000 VOLTS - AUTOMATIC CIRCUIT BREAKERS: - FOR A VOLTAGE OF\nLESS THAN 72.5 KV: - SF6 CIRCUIT BREAKERS : FOR A VOLTAGE OF 66 KV",
+        "hsn_code": "85352113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, FUSES,\nLIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION BOXES),\nFOR A VOLTAGE EXCEEDING 1,000 VOLTS - AUTOMATIC CIRCUIT BREAKERS: - FOR A VOLTAGE OF\nLESS THAN 72.5 KV: - SF6 CIRCUIT BREAKERS : FOR A VOLTAGE OF 33 KV",
+        "hsn_code": "85352112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, FUSES,\nLIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION BOXES),\nFOR A VOLTAGE EXCEEDING 1,000 VOLTS - AUTOMATIC CIRCUIT BREAKERS: - FOR A VOLTAGE OF\nLESS THAN 72.5 KV: - SF6 CIRCUIT BREAKERS : FOR A VOLTAGE OF 11 KV",
+        "hsn_code": "85352111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, FUSES,\nLIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION BOXES),\nFOR A VOLTAGE EXCEEDING 1,000 VOLTS -FUSES: OTHER",
+        "hsn_code": "85351090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, FUSES,\nLIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION BOXES),\nFOR A VOLTAGE EXCEEDING 1,000 VOLTS -FUSES: FUSES GEAR",
+        "hsn_code": "85351050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, FUSES,\nLIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION BOXES),\nFOR A VOLTAGE EXCEEDING 1,000 VOLTS -FUSES: OTHER HIGH RUPTURING CAPACITY FUSES",
+        "hsn_code": "85351040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, FUSES,\nLIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION BOXES),\nFOR A VOLTAGE EXCEEDING 1,000 VOLTS -FUSES: OTHER REWIREABLE FUSES",
+        "hsn_code": "85351030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, FUSES,\nLIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION BOXES),\nFOR A VOLTAGE EXCEEDING 1,000 VOLTS -FUSES: FOR SWITCHES HAVING RATING ABOVE 15\nAMPS, HIGH RUPTURING CAPACITY OR REWIREABLE",
+        "hsn_code": "85351020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR\nMAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, FUSES,\nLIGHTNING ARRESTERS, VOLTAGE LIMITERS, SURGE SUPPRESSORS, PLUGS, JUNCTION BOXES),\nFOR A VOLTAGE EXCEEDING 1,000 VOLTS -FUSES: FOR SWITCHES HAVING RATING UPTO 15\nAMPS, REWIREABLE",
+        "hsn_code": "85351010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING RESISTORS",
+        "hsn_code": "8533",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS - PARTS",
+        "hsn_code": "85339000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS - OTHER VARIABLE RESISTORS, INCLUDING RHEOSTATS AND POTENTIOMETERS:\nOTHER",
+        "hsn_code": "85334090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS - OTHER VARIABLE RESISTORS, INCLUDING RHEOSTATS AND POTENTIOMETERS:\nTHERMISTORS",
+        "hsn_code": "85334030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS - OTHER VARIABLE RESISTORS, INCLUDING RHEOSTATS AND POTENTIOMETERS:\nRHEOSTATS",
+        "hsn_code": "85334020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS - OTHER VARIABLE RESISTORS, INCLUDING RHEOSTATS AND POTENTIOMETERS:\nPOTENTIOMETERS",
+        "hsn_code": "85334010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS - WIREWOUND VARIABLE RESISTORS, INCLUDING RHEOSTATS AND\nPOTENTIOMETERS: - OTHER: OTHER",
+        "hsn_code": "85333990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS - WIREWOUND VARIABLE RESISTORS, INCLUDING RHEOSTATS AND\nPOTENTIOMETERS: - OTHER: RHEOSTATS",
+        "hsn_code": "85333920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS - WIREWOUND VARIABLE RESISTORS, INCLUDING RHEOSTATS AND\nPOTENTIOMETERS: - OTHER: POTENTIOMETERS",
+        "hsn_code": "85333910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS - WIREWOUND VARIABLE RESISTORS, INCLUDING RHEOSTATS AND\nPOTENTIOMETERS: - FOR A POWER HANDLING CAPACITY NOT EXCEEDING 20 W: OTHER",
+        "hsn_code": "85333190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS - WIREWOUND VARIABLE RESISTORS, INCLUDING RHEOSTATS AND\nPOTENTIOMETERS: - FOR A POWER HANDLING CAPACITY NOT EXCEEDING 20 W: RHEOSTATS",
+        "hsn_code": "85333120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS - WIREWOUND VARIABLE RESISTORS, INCLUDING RHEOSTATS AND\nPOTENTIOMETERS: - FOR A POWER HANDLING CAPACITY NOT EXCEEDING 20 W:\nPOTENTIOMETERS",
+        "hsn_code": "85333110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS - OTHER FIXED RESISTORS: - OTHER: - OF INSULATED WIRE : OTHER",
+        "hsn_code": "85332929",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS - OTHER FIXED RESISTORS: - OTHER: - OF INSULATED WIRE : OF NICHROME",
+        "hsn_code": "85332921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS - OTHER FIXED RESISTORS: - OTHER: - OF BARE WIRE: OTHER",
+        "hsn_code": "85332919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS - OTHER FIXED RESISTORS: - OTHER: - OF BARE WIRE: OF NICHROME",
+        "hsn_code": "85332911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS - OTHER FIXED RESISTORS: - FOR A POWER HANDLING CAPACITY NOT EXCEEDING 20\nW: - OF INSULATED WIRE : OTHER",
+        "hsn_code": "85332129",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS - OTHER FIXED RESISTORS: - FOR A POWER HANDLING CAPACITY NOT EXCEEDING 20\nW: - OF INSULATED WIRE : OF NICHROME",
+        "hsn_code": "85332121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS - OTHER FIXED RESISTORS: - FOR A POWER HANDLING CAPACITY NOT EXCEEDING 20\nW: - OF BARE WIRE : OTHER",
+        "hsn_code": "85332119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS - OTHER FIXED RESISTORS: - FOR A POWER HANDLING CAPACITY NOT EXCEEDING 20\nW: - OF BARE WIRE : OF NICHROME",
+        "hsn_code": "85332111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL RESISTORS (INCLUDING RHEOSTATS AND POTENTIOMETERS), OTHER THAN HEATING\nRESISTORS FIXED CARBON RESISTORS, COMPOSITION OR FILM TYPES",
+        "hsn_code": "85331000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL CAPACITORS, FIXED, VARIABLE OR ADJUSTABLE (PRE-SET)",
+        "hsn_code": "8532",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL CAPACITORS, FIXED, VARIABLE OR ADJUSTABLE (PRE-SET) PARTS",
+        "hsn_code": "85329000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL CAPACITORS, FIXED, VARIABLE OR ADJUSTABLE (PRE-SET) VARIABLE OR ADJUSTABLE\n(PRE-SET) CAPACITORS",
+        "hsn_code": "85323000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL CAPACITORS, FIXED, VARIABLE OR ADJUSTABLE (PRE-SET) - OTHER FIXED\nCAPACITORS: - OTHER: OTHER",
+        "hsn_code": "85322990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL CAPACITORS, FIXED, VARIABLE OR ADJUSTABLE (PRE-SET) - OTHER FIXED\nCAPACITORS: - OTHER: OF DIELECTRIC OF MICA",
+        "hsn_code": "85322910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL CAPACITORS, FIXED, VARIABLE OR ADJUSTABLE (PRE-SET) - OTHER FIXED\nCAPACITORS: DIELECTRIC OF PAPER OR PLASTICS",
+        "hsn_code": "85322500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL CAPACITORS, FIXED, VARIABLE OR ADJUSTABLE (PRE-SET) - OTHER FIXED\nCAPACITORS: CERAMIC DIELECTRIC, MULTILAYER",
+        "hsn_code": "85322400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL CAPACITORS, FIXED, VARIABLE OR ADJUSTABLE (PRE-SET) - OTHER FIXED\nCAPACITORS: CERAMIC DIELECTRIC, SINGLE LAYER",
+        "hsn_code": "85322300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL CAPACITORS, FIXED, VARIABLE OR ADJUSTABLE (PRE-SET) - OTHER FIXED\nCAPACITORS: ALUMINIUM ELECTROLYTIC",
+        "hsn_code": "85322200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL CAPACITORS, FIXED, VARIABLE OR ADJUSTABLE (PRE-SET) - OTHER FIXED\nCAPACITORS: TANTALUM",
+        "hsn_code": "85322100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL CAPACITORS, FIXED, VARIABLE OR ADJUSTABLE (PRE-SET) FIXED CAPACITORS\nDESIGNED FOR USE IN 50 OR 60 HZ CIRCUITS AND HAVING A REACTIVE POWER HANDLING\nCAPACITY OF NOT LESS THAN 0.5 KVAR (POWER CAPACITORS)",
+        "hsn_code": "85321000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC SOUND OR VISUAL SIGNALLING APPARATUS (FOR EXAMPLE, BELLS, SIRENS, INDICATOR PANELS, BURGLAR OR FIRE ALARMS), OTHER THAN THOSE OF HEADING 8512 OR 8530",
+        "hsn_code": "8531",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC SOUND OR VISUAL SIGNALLING APPARATUS (FOR EXAMPLE, BELLS, SIRENS, INDICATOR\nPANELS, BURGLAR OR FIRE ALARMS), OTHER THAN THOSE OF HEADING 8512 OR 8530 PARTS",
+        "hsn_code": "85319000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC SOUND OR VISUAL SIGNALLING APPARATUS (FOR EXAMPLE, BELLS, SIRENS, INDICATOR\nPANELS, BURGLAR OR FIRE ALARMS), OTHER THAN THOSE OF HEADING 8512 OR 8530 OTHER\nAPPARATUS",
+        "hsn_code": "85318000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC SOUND OR VISUAL SIGNALLING APPARATUS (FOR EXAMPLE, BELLS, SIRENS, INDICATOR\nPANELS, BURGLAR OR FIRE ALARMS), OTHER THAN THOSE OF HEADING 8512 OR 8530\nINDICATOR PANELS INCORPORATING LIQUID CRYSTALDEVICES (LCD) OR LIGHT EMITTING DIODES\n(LED)",
+        "hsn_code": "85312000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC SOUND OR VISUAL SIGNALLING APPARATUS (FOR EXAMPLE, BELLS, SIRENS, INDICATOR\nPANELS, BURGLAR OR FIRE ALARMS), OTHER THAN THOSE OF HEADING 8512 OR 8530 -\nBURGLAR OR FIRE ALARMS AND SIMILAR APPARATUS: OTHER",
+        "hsn_code": "85311090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC SOUND OR VISUAL SIGNALLING APPARATUS (FOR EXAMPLE, BELLS, SIRENS, INDICATOR\nPANELS, BURGLAR OR FIRE ALARMS), OTHER THAN THOSE OF HEADING 8512 OR 8530 -BURGLAR\nOR FIRE ALARMS AND SIMILAR APPARATUS: FIRE ALARM",
+        "hsn_code": "85311020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC SOUND OR VISUAL SIGNALLING APPARATUS (FOR EXAMPLE, BELLS, SIRENS, INDICATOR\nPANELS, BURGLAR OR FIRE ALARMS), OTHER THAN THOSE OF HEADING 8512 OR 8530 -BURGLAR\nOR FIRE ALARMS AND SIMILAR APPARATUS: BURGLAR ALARM",
+        "hsn_code": "85311010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL SIGNALING, SAFETY OR TRAFFIC CONTROL EQUIPMENT FOR RAILWAYS, TRAMWAYS, ROADS, INLAND WATERWAYS, PARKING FACILITIES, PORT INSTALLATIONS OR AIRFIELDS (OTHER THAN THOSE OF HEADING 8608)",
+        "hsn_code": "8530",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL SIGNALLING, SAFETY OR TRAFFIC CONTROL EQUIPMENT FOR RAILWAYS,\nTRAMWAYS, ROADS, INLAND WATERWAYS, PARKING FACILITIES, PORT INSTALLATIONS OR\nAIRFIELDS (OTHER THAN THOSE OF HEADING 8608) - PARTS",
+        "hsn_code": "85309000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL SIGNALLING, SAFETY OR TRAFFIC CONTROL EQUIPMENT FOR RAILWAYS,\nTRAMWAYS, ROADS, INLAND WATERWAYS, PARKING FACILITIES, PORT INSTALLATIONS OR\nAIRFIELDS (OTHER THAN THOSE OF HEADING 8608) - OTHER EQUIPMENT",
+        "hsn_code": "85308000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL SIGNALLING, SAFETY OR TRAFFIC CONTROL EQUIPMENT FOR RAILWAYS,\nTRAMWAYS, ROADS, INLAND WATERWAYS, PARKING FACILITIES, PORT INSTALLATIONS OR\nAIRFIELDS (OTHER THAN THOSE OF HEADING 8608) -EQUIPMENT FOR RAILWAYS OR\nTRAMWAYS: FOR TRAMWAYS",
+        "hsn_code": "85301020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL SIGNALLING, SAFETY OR TRAFFIC CONTROL EQUIPMENT FOR RAILWAYS,\nTRAMWAYS, ROADS, INLAND WATERWAYS, PARKING FACILITIES, PORT INSTALLATIONS OR\nAIRFIELDS (OTHER THAN THOSE OF HEADING 8608) -EQUIPMENT FOR RAILWAYS OR\nTRAMWAYS: FOR RAILWAYS",
+        "hsn_code": "85301010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF HEADINGS 8525 TO 8528",
+        "hsn_code": "8529",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF HEADINGS 8525 TO\n8528 - OTHER: OTHER",
+        "hsn_code": "85299090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF HEADINGS 8525 TO\n8528 - OTHER: FOR AMATEUR RADIO COMMUNICATION EQUIPMENT",
+        "hsn_code": "85299020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF HEADINGS 8525 TO\n8528 - OTHER: FOR COMMUNICATION JAMMING EQUIPMENT",
+        "hsn_code": "85299010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF HEADINGS 8525 TO\n8528 -AERIALS AND AERIAL REFLECTORS OF ALL KINDS; PARTS SUITABLE FOR USE THEREWITH:\nOTHER: OTHER",
+        "hsn_code": "85291099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF HEADINGS 8525 TO\n8528 -AERIALS AND AERIAL REFLECTORS OF ALL KINDS; PARTS SUITABLE FOR USE THEREWITH:\nOTHER: FOR AMATEUR RADIO COMMUNICATION EQUIPMENT",
+        "hsn_code": "85291092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF HEADINGS 8525 TO\n8528 -AERIALS AND AERIAL REFLECTORS OF ALL KINDS; PARTS SUITABLE FOR USE THEREWITH:\nOTHER: FOR COMMUNICATION JAMMING EQUIPMENT",
+        "hsn_code": "85291091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF HEADINGS 8525 TO\n8528 -AERIALS AND AERIAL REFLECTORS OF ALL KINDS; PARTS SUITABLE FOR USE THEREWITH: -\nOTHER AERIALS OR ANTENNA: OTHER",
+        "hsn_code": "85291029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF HEADINGS 8525 TO\n8528 -AERIALS AND AERIAL REFLECTORS OF ALL KINDS; PARTS SUITABLE FOR USE THEREWITH: -\nOTHER AERIALS OR ANTENNA: FOR AMATEUR RADIO COMMUNICATION EQUIPMENT",
+        "hsn_code": "85291022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF HEADINGS 8525 TO\n8528 -AERIALS AND AERIAL REFLECTORS OF ALL KINDS; PARTS SUITABLE FOR USE THEREWITH: -\nOTHER AERIALS OR ANTENNA: FOR COMMUNICATION JAMMING EQUIPMENT",
+        "hsn_code": "85291021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF HEADINGS 8525 TO\n8528 -AERIALS AND AERIAL REFLECTORS OF ALL KINDS; PARTS SUITABLE FOR USE THEREWITH: -\nDISH ANTENNA: OTHER",
+        "hsn_code": "85291019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF HEADINGS 8525 TO\n8528 -AERIALS AND AERIAL REFLECTORS OF ALL KINDS; PARTS SUITABLE FOR USE THEREWITH: -\nDISH ANTENNA: FOR AMATEUR RADIO COMMUNICATION EQUIPMENT",
+        "hsn_code": "85291012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF HEADINGS 8525 TO\n8528 -AERIALS AND AERIAL REFLECTORS OF ALL KINDS; PARTS SUITABLE FOR USE THEREWITH: -\nDISH ANTENNA: FOR COMMUNICATION JAMMING EQUIPMENT",
+        "hsn_code": "85291011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONITORS AND PROJECTORS, NOT INCORPORATING TELEVISION RECEPTION APPARATUS, RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO-BROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS",
+        "hsn_code": "8528",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MONITORS AND PROJECTORS, NOT INCORPORATING TELEVISION RECEPTION APPARATUS.\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVER OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS -\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING ON REPRODUCING APPARATUS --\nOTHER",
+        "hsn_code": "85287390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MONITORS AND PROJECTORS, NOT INCORPORATING TELEVISION RECEPTION APPARATUS.\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVER OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS -\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING ON REPRODUCING APPARATUS --\nOTHER, BLACK AND WHITE OR OTHER MONOCHROME ---LIQUID CRYSTAL DISPLAY\nTELEVISIONSET OF SCREEN SIZE BELOW 25 CM",
+        "hsn_code": "85287310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MONITORS AND PROJECTORS, NOT INCORPORATING TELEVISION RECEPTION APPARATUS.\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVER OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS -\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING ON REPRODUCING APPARATUS --\nOTHER, COLOUR --- OTHER",
+        "hsn_code": "85287219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MONITORS AND PROJECTORS, NOT INCORPORATING TELEVISION RECEPTION APPARATUS.\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVER OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS -\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING ON REPRODUCING APPARATUS --\nOTHER, COLOUR --- LIQUID CRYSTAL DISPLAY TELEVISION SET OF SCREEN SIZE BELOW 63 CM",
+        "hsn_code": "85287218",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS - RECEPTION APPARATUS FOR\nTELEVISION, WHETHER OR NOT INCORPORATING RADIO-BROADCAST RECEIVERS OR SOUND OR\nVIDEO RECORDING ON REPRODUCING APPARATUS -- OTHER, COLOUR --- TELEVISION SET OF\nSCREEN SIZE EXCEEDING 105 CM",
+        "hsn_code": "85287217",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MONITORS AND PROJECTORS, NOT INCORPORATING TELEVISION RECEPTION APPARATUS.\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVER OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS -\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING ON REPRODUCING APPARATUS --\nOTHER, COLOUR --- TELEVISION SET OF SCREEN SIZE EXCEEDING 87 CM BUT NOT EXCEEDING 105\nCM",
+        "hsn_code": "85287216",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MONITORS AND PROJECTORS, NOT INCORPORATING TELEVISION RECEPTION APPARATUS.\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVER OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS -\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING ON REPRODUCING APPARATUS --\nOTHER, COLOUR --- TELEVISION SET OF SCREEN SIZE EXCEEDING 74 CM BUT NOT EXCEEDING 87\nCM",
+        "hsn_code": "85287215",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MONITORS AND PROJECTORS, NOT INCORPORATING TELEVISION RECEPTION APPARATUS.\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVER OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS -\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING ON REPRODUCING APPARATUS --\nOTHER, COLOUR --- TELEVISION SET OF SCREEN SIZE EXCEEDING 68 CM BUT NOT EXCEEDING 74\nCM",
+        "hsn_code": "85287214",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MONITORS AND PROJECTORS, NOT INCORPORATING TELEVISION RECEPTION APPARATUS.\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVER OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS -\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING ON REPRODUCING APPARATUS --\nOTHER, COLOUR --- TELEVISION SET OF SCREEN SIZE EXCEEDING 54 CM BUT NOT EXCEEDING 68\nCM",
+        "hsn_code": "85287213",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MONITORS AND PROJECTORS, NOT INCORPORATING TELEVISION RECEPTION APPARATUS.\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVER OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS -\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECO",
+        "hsn_code": "85287212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MONITORS AND PROJECTORS, NOT INCORPORATING TELEVISION RECEPTION APPARATUS.\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVER OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS -\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING ON REPRODUCING APPARATUS --\nOTHER, COLOUR --- TELEVISION SET OF SCREEN SIZE UPTO 36 CM",
+        "hsn_code": "85287211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MONITORS AND PROJECTORS, NOT INCORPORATING TELEVISION RECEPTION APPARATUS.\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVER OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS -\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS -- NOT\nDESIGNED TO INCORPORATE A VIDEO DISPLAY OR SCREEN",
+        "hsn_code": "85287100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MONITORS AND PROJECTORS, NOT INCORPORATING TELEVISION RECEPTION APPARATUS.\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVER OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS -\nPROJECTORS -- OTHER",
+        "hsn_code": "85286900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MONITORS AND PROJECTORS, NOT INCORPORATING TELEVISION RECEPTION APPARATUS.\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVER OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS -\nPROJECTORS",
+        "hsn_code": "85286100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MONITORS AND PROJECTORS, NOT INCORPORATING TELEVISION RECEPTION APPARATUS.\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVER OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS -\nOTHER MONITORS -- OTHER",
+        "hsn_code": "85285900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MONITORS AND PROJECTORS, NOT INCORPORATING TELEVISION RECEPTION APPARATUS.\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVER OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS -\nOTHER MONITORS -- OF A KIND SOLELY OR PRINCIPALLY USED IN AN AUTOMATIC DATA\nPROCESSING SYSTEM OF HEADING 8471",
+        "hsn_code": "85285100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MONITORS AND PROJECTORS, NOT INCORPORATING TELEVISION RECEPTION APPARATUS.\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVER OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS -\nCATHODE-RAY TUBE MONITORS -- OTHER",
+        "hsn_code": "85284900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MONITORS AND PROJECTORS, NOT INCORPORATING TELEVISION RECEPTION APPARATUS.\nRECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVER OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS -\nCATHODE-RAY TUBE MONITORS -- OF A KIND SOLELY OR PRINCIPALLY USED IN AN AUTOMATIC\nDATA PROCESSING SYSTEM OF HEADING 8471",
+        "hsn_code": "85284100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS;\nVIDEO MONITORS AND VIDEO PROJECTORS - VIDEO PROJECTORS: BLACK AND WHITE OR OTHER\nMONOCHOROME",
+        "hsn_code": "85283030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS;\nVIDEO MONITORS AND VIDEO PROJECTORS - VIDEO PROJECTORS: COLOUR",
+        "hsn_code": "85283020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS;\nVIDEO MONITORS AND VIDEO PROJECTORS - VIDEO PROJECTORS: COLOUR, WITH FLAT PANEL\nSCREEN",
+        "hsn_code": "85283010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS;\nVIDEO MONITORS AND VIDEO PROJECTORS - VIDEO MONITORS: BLACK AND WHITE OR OTHER\nMONOCHROME",
+        "hsn_code": "85282200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS;\nVIDEO MONITORS AND VIDEO PROJECTORS - VIDEO MONITORS: - COLOUR: OTHER",
+        "hsn_code": "85282190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS;\nVIDEO MONITORS AND VIDEO PROJECTORS - VIDEO MONITORS: - COLOUR: PROFESSIONAL\nMONITORS FOR STUDIO USE (OF RESOLUTION 800 LINES AND ABOVE)",
+        "hsn_code": "85282110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS;\nVIDEO MONITORS AND VIDEO PROJECTORS - RECEPTION APPARATUS FOR TELEVISION,\nWHETHER OR NOT INCORPORATING RADIO-BROADCAST RECEIVERS OR SOUND OR VIDEO\nRECORDING OR REPRODUCING APPARATUS: BLACK AND WHITE OR OTHER MONOCHROME:\nOTHER",
+        "hsn_code": "85281390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS;\nVIDEO MONITORS AND VIDEO PROJECTORS - RECEPTION APPARATUS FOR TELEVISION,\nWHETHER OR NOT INCORPORATING RADIO-BROADCAST RECEIVERS OR SOUND OR VIDEO\nRECORDING OR REPRODUCING APPARATUS: BLACK AND WHITE OR OTHER MONOCHROME:\nLIQUID CRYSTAL DISPLAY TELEVISION SET OF SCREEN SIZE BELOW 25 CM",
+        "hsn_code": "85281310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS;\nVIDEO MONITORS AND VIDEO PROJECTORS - RECEPTION APPARATUS FOR TELEVISION,\nWHETHER OR NOT INCORPORATING RADIO-BROADCAST RECEIVERS OR SOUND OR VIDEO\nRECORDING OR REPRODUCING APPARATUS: - COLOUR : OTHER - OTHER",
+        "hsn_code": "85281299",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS;\nVIDEO MONITORS AND VIDEO PROJECTORS - RECEPTION APPARATUS FOR TELEVISION,\nWHETHER OR NOT INCORPORATING RADIO-BROADCAST RECEIVERS OR SOUND OR VIDEO\nRECORDING OR REPRODUCING APPARATUS: - COLOUR : OTHER - SATELLITE RECEIVERS",
+        "hsn_code": "85281291",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS;\nVIDEO MONITORS AND VIDEO PROJECTORS - RECEPTION APPARATUS FOR TELEVISION,\nWHETHER OR NOT INCORPORATING RADIO-BROADCAST RECEIVERS OR SOUND OR VIDEO\nRECORDING OR REPRODUCING APPARATUS: - COLOUR : OTHER",
+        "hsn_code": "85281219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS;\nVIDEO MONITORS AND VIDEO PROJECTORS - RECEPTION APPARATUS FOR TELEVISION,\nWHETHER OR NOT INCORPORATING RADIO-BROADCAST RECEIVERS OR SOUND OR VIDEO\nRECORDING OR REPRODUCING APPARATUS: - COLOUR : LIQUID CRYSTAL DISPLAY TELEVISION\nSET OF SCREEN SIZE BELOW 63 CM",
+        "hsn_code": "85281218",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS;\nVIDEO MONITORS AND VIDEO PROJECTORS - RECEPTION APPARATUS FOR TELEVISION,\nWHETHER OR NOT INCORPORATING RADIO-BROADCAST RECEIVERS OR SOUND OR VIDEO\nRECORDING OR REPRODUCING APPARATUS: - COLOUR : TELEVISION SET OF SCREEN SIZE\nEXCEEDING 105 CM",
+        "hsn_code": "85281217",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS;\nVIDEO MONITORS AND VIDEO PROJECTORS - RECEPTION APPARATUS FOR TELEVISION,\nWHETHER OR NOT INCORPORATING RADIO-BROADCAST RECEIVERS OR SOUND OR VIDEO\nRECORDING OR REPRODUCING APPARATUS: - COLOUR : TELEVISION SET OF SCREEN SIZE\nEXCEEDING 87 CM BUT NOT EXCEEDING 105 CM",
+        "hsn_code": "85281216",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS;\nVIDEO MONITORS AND VIDEO PROJECTORS - RECEPTION APPARATUS FOR TELEVISION,\nWHETHER OR NOT INCORPORATING RADIO-BROADCAST RECEIVERS OR SOUND OR VIDEO\nRECORDING OR REPRODUCING APPARATUS: - COLOUR : TELEVISION SET OF SCREEN SIZE\nEXCEEDING 74 CM BUT NOT EXCEEDING 87 CM",
+        "hsn_code": "85281215",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS;\nVIDEO MONITORS AND VIDEO PROJECTORS - RECEPTION APPARATUS FOR TELEVISION,\nWHETHER OR NOT INCORPORATING RADIO-BROADCAST RECEIVERS OR SOUND OR VIDEO\nRECORDING OR REPRODUCING APPARATUS: - COLOUR : TELEVISION SET OF SCREEN SIZE\nEXCEEDING 68 CM BUT NOT EXCEEDING 74 CM",
+        "hsn_code": "85281214",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS;\nVIDEO MONITORS AND VIDEO PROJECTORS - RECEPTION APPARATUS FOR TELEVISION,\nWHETHER OR NOT INCORPORATING RADIO-BROADCAST RECEIVERS OR SOUND OR VIDEO\nRECORDING OR REPRODUCING APPARATUS: - COLOUR : TELEVISION SET OF SCREEN SIZE\nEXCEEDING 54 CM BUT NOT EXCEEDING 68 CM",
+        "hsn_code": "85281213",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS;\nVIDEO MONITORS AND VIDEO PROJECTORS - RECEPTION APPARATUS FOR TELEVISION,\nWHETHER OR NOT INCORPORATING RADIO-BROADCAST RECEIVERS OR SOUND OR VIDEO\nRECORDING OR REPRODUCING APPARATUS: - COLOUR : TELEVISION SET OF SCREEN SIZE\nEXCEEDING 36 CM BUT NOT EXCEEDING 54 CM",
+        "hsn_code": "85281212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR TELEVISION, WHETHER OR NOT INCORPORATING RADIO\nBROADCAST RECEIVERS OR SOUND OR VIDEO RECORDING OR REPRODUCING APPARATUS;\nVIDEO MONITORS AND VIDEO PROJECTORS - RECEPTION APPARATUS FOR TELEVISION,\nWHETHER OR NOT INCORPORATING RADIO-BROADCAST RECEIVERS OR SOUND OR VIDEO\nRECORDING OR REPRODUCING APPARATUS: - COLOUR : TELEVISION SET OF SCREEN SIZE UPTO\n36 CM",
+        "hsn_code": "85281211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR RADIO-BROADCASTING WHETHER OR NOT COMBINED, IN THE SAME HOUSING, WITH SOUND RECORDING OR REPRODUCING APPARATUS OR A CLOCK",
+        "hsn_code": "8527",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR RADIO-BROADCASTING, WHETHER OR NOT COMBINED, IN THE\nSAME HOUSING, WITH SOUND RECORDING OR REPRODUCING APPARATUS OR A CLOCK - OTHER -\n- OTHER --- OTHER",
+        "hsn_code": "85279990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR RADIO-BROADCASTING, WHETHER OR NOT COMBINED, IN THE\nSAME HOUSING, WITH SOUND RECORDING OR REPRODUCING APPARATUS OR A CLOCK - OTHER -\n- OTHER --- RADIO COMMUNICATION RECEIVERS ---- OTHER",
+        "hsn_code": "85279919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR RADIO-BROADCASTING, WHETHER OR NOT COMBINED, IN THE\nSAME HOUSING, WITH SOUND RECORDING OR REPRODUCING APPARATUS OR A CLOCK - OTHER -\n- OTHER --- RADIO COMMUNICATION RECEIVERS ---- DEMODULATORS",
+        "hsn_code": "85279912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR RADIO-BROADCASTING, WHETHER OR NOT COMBINED, IN THE\nSAME HOUSING, WITH SOUND RECORDING OR REPRODUCING APPARATUS OR A CLOCK - OTHER -\n- OTHER --- RADIO COMMUNICATION RECEIVERS ---- RADIO PAGERS",
+        "hsn_code": "85279911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR RADIO-BROADCASTING, WHETHER OR NOT COMBINED, IN THE\nSAME HOUSING, WITH SOUND RECORDING OR REPRODUCING APPARATUS OR A CLOCK - OTHER -\n- NOT COMBINED WITH SOUND RECORDING OR REPRODUCING APPARATUS BUT COMBINED\nWITH A CLOCK",
+        "hsn_code": "85279200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR RADIO-BROADCASTING, WHETHER OR NOT COMBINED, IN THE\nSAME HOUSING, WITH SOUND RECORDING OR REPRODUCING APPARATUS OR A CLOCK - OTHER -\n- COMBINED WITH SOUND RECORDING OR REPRODUCING APPARATUS",
+        "hsn_code": "85279100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY OR RADIO-BROADCASTING,\nWHETHER OR NOT COMBINED, IN THE SAME HOUSING, WITH SOUND RECORDING OR\nREPRODUCING APPARATUS OR A CLOCK - OTHER APPARATUS: OTHER",
+        "hsn_code": "85279090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY OR RADIO-BROADCASTING,\nWHETHER OR NOT COMBINED, IN THE SAME HOUSING, WITH SOUND RECORDING OR\nREPRODUCING APPARATUS OR A CLOCK - OTHER APPARATUS: - RADIO COMMUNICATION\nRECEIVERS: OTHER",
+        "hsn_code": "85279019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY OR RADIO-BROADCASTING,\nWHETHER OR NOT COMBINED, IN THE SAME HOUSING, WITH SOUND RECORDING OR\nREPRODUCING APPARATUS OR A CLOCK - OTHER APPARATUS: - RADIO COMMUNICATION\nRECEIVERS: DEMODULATORS",
+        "hsn_code": "85279012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY OR RADIO-BROADCASTING,\nWHETHER OR NOT COMBINED, IN THE SAME HOUSING, WITH SOUND RECORDING OR\nREPRODUCING APPARATUS OR A CLOCK - OTHER APPARATUS: - RADIO COMMUNICATION\nRECEIVERS: RADIO PAGERS",
+        "hsn_code": "85279011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY OR RADIO-BROADCASTING,\nWHETHER OR NOT COMBINED, IN THE SAME HOUSING, WITH SOUND RECORDING OR\nREPRODUCING APPARATUS OR A CLOCK - OTHER RADIO-BROADCAST RECEIVERS, INCLUDING\nAPPARATUS CAPABLE OF RECEIVING ALSO RADIOTELEPHONY OR RADIO-TELEGRAPHY: OTHER",
+        "hsn_code": "85273900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY OR RADIO-BROADCASTING,\nWHETHER OR NOT COMBINED, IN THE SAME HOUSING, WITH SOUND RECORDING OR\nREPRODUCING APPARATUS OR A CLOCK - OTHER RADIO-BROADCAST RECEIVERS, INCLUDING\nAPPARATUS CAPABLE OF RECEIVING ALSO RADIOTELEPHONY OR RADIO-TELEGRAPHY: NOT\nCOMBINED WITH SOUND RECORDING OR REPRODUCING APPARATUS BUT COMBINED WITH A\nCLOCK",
+        "hsn_code": "85273200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR RADIO-TELEPHONY, RADIOTELEGRAPHY OR RADIO-BROADCASTING,\nWHETHER OR NOT COMBINED, IN THE SAME HOUSING, WITH SOUND RECORDING OR\nREPRODUCING APPARATUS OR A CLOCK - OTHER RADIO-BROADCAST RECEIVERS, INCLUDING\nAPPARATUS CAPABLE OF RECEIVING ALSO RADIOTELEPHONY OR RADIO-TELEGRAPHY:\nCOMBINED WITH SOUND RECORDING OR REPRODUCING APPARATUS",
+        "hsn_code": "85273100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR RADIO-BROADCASTING, WHETHER OR NOT COMBINED, IN THE\nSAME HOUSING, WITH SOUND RECORDING OR REPRODUCING APPARATUS OR A CLOCK -- OTHER",
+        "hsn_code": "85272900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR RADIO-BROADCASTING, WHETHER OR NOT COMBINED, IN THE\nSAME HOUSING, WITH SOUND RECORDING OR REPRODUCING APPARATUS OR A CLOCK - RADIO\nRECEIVERS NOT CAPABLE OF OPERATING WITHOUT AN EXTERNAL SOURCE OF POWER, OF A\nKIND USED IN MOTOR VEHICLES -- COMBINED WITH SOUND RECORDING OR REPRODUCING\nAPPARATUS",
+        "hsn_code": "85272100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR RADIO-BROADCASTING, WHETHER OR NOT COMBINED, IN THE\nSAME HOUSING, WITH SOUND RECORDING OR REPRODUCING APPARATUS OR A CLOCK - RADIO\nBROADCAST RECEIVERS CAPABLE OF OPERATING WITHOUT AN EXTERNAL SOURCE OF POWER --\nOTHER",
+        "hsn_code": "85271900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR RADIO-BROADCASTING, WHETHER OR NOT COMBINED, IN THE\nSAME HOUSING, WITH SOUND RECORDING OR REPRODUCING APPARATUS OR A CLOCK - RADIO\nBROADCAST RECEIVERS CAPABLE OF OPERATING WITHOUT AN EXTERNAL SOURCE OF POWER --\nOTHER APPARATUS COMBINED WITH SOUND RECORDING OR REPRODUCING APPARATUS",
+        "hsn_code": "85271300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECEPTION APPARATUS FOR RADIO-BROADCASTING, WHETHER OR NOT COMBINED, IN THE\nSAME HOUSING, WITH SOUND RECORDING OR REPRODUCING APPARATUS OR A CLOCK - RADIO\nBROADCAST RECEIVERS CAPABLE OF OPERATING WITHOUT AN EXTERNAL SOURCE OF POWER --\nPOCKET-SIZE RADIO CASSETTE-PLAYERS",
+        "hsn_code": "85271200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RADAR APPARATUS, RADIO NAVIGATIONAL AID APPARATUS AND RADIO REMOTE CONTROL APPARATUS",
+        "hsn_code": "8526",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RADAR APPARATUS, RADIO NAVIGATIONAL AID APPARATUS AND RADIO REMOTE CONTROL\nAPPARATUS - OTHER: RADIO REMOTE CONTROL APPARATUS",
+        "hsn_code": "85269200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RADAR APPARATUS, RADIO NAVIGATIONAL AID APPARATUS AND RADIO REMOTE CONTROL\nAPPARATUS - OTHER: - RADIO NAVIGATIONAL AID APPARATUS: OTHER",
+        "hsn_code": "85269190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VHF OMNI RANGE EQUIPMENT",
+        "hsn_code": "85269150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NON-DIRECTIONAL BEACON",
+        "hsn_code": "85269140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RADAR APPARATUS, RADIO NAVIGATIONAL AID APPARATUS AND RADIO REMOTE CONTROL\nAPPARATUS - OTHER: - RADIO NAVIGATIONAL AID APPARATUS: DIRECTION FINDING EQUIPMENT",
+        "hsn_code": "85269130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RADAR APPARATUS, RADIO NAVIGATIONAL AID APPARATUS AND RADIO REMOTE CONTROL\nAPPARATUS - OTHER: - RADIO NAVIGATIONAL AID APPARATUS: INSTRUMENT LANDING SYSTEM",
+        "hsn_code": "85269120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RADAR APPARATUS, RADIO NAVIGATIONAL AID APPARATUS AND RADIO REMOTE CONTROL\nAPPARATUS - OTHER: - RADIO NAVIGATIONAL AID APPARATUS: DIRECTION MEASURING\nEQUIPMENT",
+        "hsn_code": "85269110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RADAR APPARATUS, RADIO NAVIGATIONAL AID APPARATUS AND RADIO REMOTE CONTROL\nAPPARATUS RADAR APPARATUS",
+        "hsn_code": "85261000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER: - OTHER : - OTHER: OTHER",
+        "hsn_code": "85249999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER: - OTHER : - OTHER: MATRICES FOR THE\nPRODUCTION OF RECORDS; PREPARED RECORD BLANKS",
+        "hsn_code": "85249991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER: - OTHER : STAMPER FOR CD AUDIO, CD VIDEO\nAND CD-ROM",
+        "hsn_code": "85249930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER: - OTHER : 2-D/3D COMPUTER GRAPHICS",
+        "hsn_code": "85249920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER: - OTHER : AUDIO-VISUAL NEWS OR AUDIO\nVISUAL VIEWS MATERIAL INCLUDING NEWS CLIPPINGS",
+        "hsn_code": "85249910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER: - FOR REPRODUCING PHENOMENA OTHER\nTHAN SOUND OR IMAGE: OTHER",
+        "hsn_code": "85249190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER: - FOR REPRODUCING PHENOMENA OTHER\nTHAN SOUND OR IMAGE: - SOFTWARE: OTHER",
+        "hsn_code": "85249119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER: - FOR REPRODUCING PHENOMENA OTHER\nTHAN SOUND OR IMAGE: - SOFTWARE: INFORMATION TECHNOLOGY SOFTWARE ON OTHER\nMEDIA",
+        "hsn_code": "85249113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER: - FOR REPRODUCING PHENOMENA OTHER\nTHAN SOUND OR IMAGE: - SOFTWARE: INFORMATION TECHNOLOGY SOFTWARE ON DISC OR ON\nCD ROM",
+        "hsn_code": "85249112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER: - FOR REPRODUCING PHENOMENA OTHER\nTHAN SOUND OR IMAGE: - SOFTWARE: INFORMATION TECHNOLOGY SOFTWARE ON FLOPPY\nDISC OR CARTRIDGE TAPE",
+        "hsn_code": "85249111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - CARDS INCORPORATING A MAGNETIC STRIPE",
+        "hsn_code": "85246000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH EXCEEDING 6.5\nMM : OTHER",
+        "hsn_code": "85245390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH EXCEEDING 6.5\nMM : OTHER VIDEO FILMS",
+        "hsn_code": "85245340",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH EXCEEDING 6.5\nMM : CHILDREMS VIDEO FILMS",
+        "hsn_code": "85245330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH EXCEEDING 6.5\nMM : AUDIO-VISUAL NEWS OR AUDIO-VISUAL MATERIAL INCLUDING NEWS CLIPPINGS",
+        "hsn_code": "85245320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH EXCEEDING 6.5\nMM : - LEARNING AIDS: OTHER",
+        "hsn_code": "85245319",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH EXCEEDING 6.5\nMM : - LEARNING AIDS: VIDEO TAPES OF EDUCATIONAL NATURE",
+        "hsn_code": "85245312",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH EXCEEDING 6.5\nMM : - LEARNING AIDS: AUDIO CASSETTES",
+        "hsn_code": "85245311",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH EXCEEDING 4\nMM BUT NOT EXCEEDING 6.5 MM: OTHER",
+        "hsn_code": "85245290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH EXCEEDING 4\nMM BUT NOT EXCEEDING 6.5 MM: OTHER VIDEO FILMS",
+        "hsn_code": "85245240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH EXCEEDING 4\nMM BUT NOT EXCEEDING 6.5 MM: CHILDRENS VIDEO FILMS",
+        "hsn_code": "85245230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH EXCEEDING 4\nMM BUT NOT EXCEEDING 6.5 MM: AUDIO-VISUAL NEWS OR AUDIO-VISUAL VIEWS MATERIAL\nINCLUDING NEWS CLIPPINGS",
+        "hsn_code": "85245220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH EXCEEDING 4\nMM BUT NOT EXCEEDING 6.5 MM: - LEARNING AIDS: OTHER",
+        "hsn_code": "85245219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH EXCEEDING 4\nMM BUT NOT EXCEEDING 6.5 MM: - LEARNING AIDS: VIDEO TAPES OF EDUCATIONAL NATURE",
+        "hsn_code": "85245212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH EXCEEDING 4\nMM BUT NOT EXCEEDING 6.5 MM: - LEARNING AIDS: AUDIO CASSETTES",
+        "hsn_code": "85245211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH NOT\nEXCEEDING 4 MM: OTHER",
+        "hsn_code": "85245190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH NOT\nEXCEEDING 4 MM: OTHER VIDEO FILMS",
+        "hsn_code": "85245140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH NOT\nEXCEEDING 4 MM: CHILDRENS VIDEO FILMS",
+        "hsn_code": "85245130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH NOT\nEXCEEDING 4 MM: AUDIO-VISUAL NEWS OR AUDIO-VISUAL VIEWS MATERIAL INCLUDING NEWS\nCLIPPINGS",
+        "hsn_code": "85245120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH NOT\nEXCEEDING 4 MM: - LEARNING AIDS: OTHER",
+        "hsn_code": "85245119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH NOT\nEXCEEDING 4 MM: - LEARNING AIDS: VIDEO TAPES OF EDUCATIONAL NATURE",
+        "hsn_code": "85245112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - OTHER MAGNETIC TAPES: - OF A WIDTH NOT\nEXCEEDING 4 MM: - LEARNING AIDS: AUDIO CASSETTES",
+        "hsn_code": "85245111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 -MAGNETIC TAPES FOR REPRODUCING PHENOMENA\nOTHER THAN SOUND OR IMAGE: OTHER",
+        "hsn_code": "85244090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 -MAGNETIC TAPES FOR REPRODUCING PHENOMENA\nOTHER THAN SOUND OR IMAGE: - SOFTWARE: OTHER",
+        "hsn_code": "85244019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 -MAGNETIC TAPES FOR REPRODUCING PHENOMENA\nOTHER THAN SOUND OR IMAGE: - SOFTWARE: INFORMATION TECHNOLOGY SOFTWARE",
+        "hsn_code": "85244011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - DISCS FOR LASER READING SYSTEMS: - OTHER: OTHER",
+        "hsn_code": "85243990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - DISCS FOR LASER READING SYSTEMS: - OTHER: DIGITAL\nVIDEO DISCS",
+        "hsn_code": "85243930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - DISCS FOR LASER READING SYSTEMS: - OTHER: OTHER\nVIDEO COMPACT DISCS",
+        "hsn_code": "85243920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - DISCS FOR LASER READING SYSTEMS: - OTHER: VIDEO\nCOMPACT DISC OF EDUCATIONAL NATURE",
+        "hsn_code": "85243910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - DISCS FOR LASER READING SYSTEMS: - FOR\nREPRODUCING SOUND ONLY: OTHER",
+        "hsn_code": "85243290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - DISCS FOR LASER READING SYSTEMS: - FOR\nREPRODUCING SOUND ONLY: PRE-RECORDED AUDIO COMPACT DISCS OR PRE-RECORDED AUDIO\nMINI DISCS",
+        "hsn_code": "85243210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - DISCS FOR LASER READING SYSTEMS: - FOR\nREPRODUCING PHENOMENA OTHER THAN SOUND OR IMAGE: OTHER",
+        "hsn_code": "85243190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - DISCS FOR LASER READING SYSTEMS: - FOR\nREPRODUCING PHENOMENA OTHER THAN SOUND OR IMAGE: - SOFTWARE: OTHER",
+        "hsn_code": "85243119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - DISCS FOR LASER READING SYSTEMS: - FOR\nREPRODUCING PHENOMENA OTHER THAN SOUND OR IMAGE: - SOFTWARE: INFORMATION\nTECHNOLOGY SOFTWARE",
+        "hsn_code": "85243111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - GRAMOPHONE RECORDS: OTHER",
+        "hsn_code": "85241090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECORDS, TAPES AND OTHER RECORDED MEDIA FOR SOUND OR OTHER SIMILARLY RECORDED\nPHENOMENA, INCLUDING MATRICES AND MASTERS FOR THE PRODUCTION OF RECORDS, BUT\nEXCLUDING PRODUCTS OF CHAPTER 37 - GRAMOPHONE RECORDS: LEARNING AIDS, SUCH AS,\nLANGUAGE RECORDS",
+        "hsn_code": "85241010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR SWITCHING OR PROTECTING ELECTRICAL CIRCUITS, OR FOR MAKING CONNECTIONS TO OR IN ELECTRICAL CIRCUITS (FOR EXAMPLE, SWITCHES, RELAYS, FUSES, SURGE SUPPRESSORS, PLUGS,  SOCKETS, LAMP-HOLDERS AND OTHER CONNECTORS,  JUNCTION BOXES), FOR A VOLTAGE NOT EXCEEDING 1,000 VOLTS: CONNECTORS FOR OPTICAL FIBRES, OPTICAL FIBRE BUNDLES OR CABLES",
+        "hsn_code": "8523",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - OTHER: OTHER",
+        "hsn_code": "85239090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - OTHER: BLANK MASTER DISC (I.E.\nSUBSTRATE) FOR PRODUCING STAMPER FOR COMPACT DISC",
+        "hsn_code": "85239060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - OTHER: COMPACT DISC RECORDABLE",
+        "hsn_code": "85239050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - OTHER: UNRECORDED COMPACT DISC\n(AUDIO)",
+        "hsn_code": "85239040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - OTHER: VIDEO CASSETTE SUITABLE TO\nWORK WITH DIGITAL TYPE VCR",
+        "hsn_code": "85239030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - OTHER: CARTRIDGE TAPE",
+        "hsn_code": "85239020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - OTHER: MATRICES FOR THE\nPRODUCTION OF RECORDS; PREPARED RECORD BLANKS",
+        "hsn_code": "85239010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - OTHER --- OTHER",
+        "hsn_code": "85238090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - OTHER --- 2-D/3-D COMPUTER GRAPHICS",
+        "hsn_code": "85238060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - OTHER --- VIDEO TAPES OF EDUCATIONAL NATURE",
+        "hsn_code": "85238050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - OTHER --- CHILDRENS VIDEO FILMS",
+        "hsn_code": "85238040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - OTHER --- AUDIO-VISUAL NEWS OR AUDIO VISUAL VIEWS",
+        "hsn_code": "85238030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - OTHER --- INFORMATION TECHNOLOGY SOFTWARE",
+        "hsn_code": "85238020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - OTHER --- GRAMAPHONE RECORDS",
+        "hsn_code": "85238010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - SEMI-CONDUCTOR MEDIA -- OTHER --- OTHER",
+        "hsn_code": "85235990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - SEMI-CONDUCTOR MEDIA -- OTHER --- PROXIMITY CARDS AND\nTAGS",
+        "hsn_code": "85235910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - SEMI-CONDUCTOR MEDIA -- SMART CARDS --- OTHER",
+        "hsn_code": "85235290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - SEMI-CONDUCTOR MEDIA -- SMART CARDS --- MEMORY CARDS",
+        "hsn_code": "85235220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - SEMI-CONDUCTOR MEDIA -- SMART CARDS --- SIM CARDS",
+        "hsn_code": "85235210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - SEMI-CONDUCTOR MEDIA -- SOLID-STATE NON-VOLATILE STORAGE\nDEVICES",
+        "hsn_code": "85235100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "85234990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "1/2 INCH VIDEO CASSETTE SUITABLE TO WORK WITH DIGITAL VCR",
+        "hsn_code": "85234970",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARTRIDGE TAPE",
+        "hsn_code": "85234960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MATRICES FOR PRODUCTION OF RECORDS; PREPARED RECORD BLANKS",
+        "hsn_code": "85234950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DVD",
+        "hsn_code": "85234940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STAMPER FOR CD AUDIO, CD VIDEO AND CD-ROM",
+        "hsn_code": "85234930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPACT DISC (VIDEO)",
+        "hsn_code": "85234920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPACT DISC (AUDIO)",
+        "hsn_code": "85234910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "85234190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DVD",
+        "hsn_code": "85234160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "1/2 INCH VIDEO CASSETTE SUITABLE TO WORK WITH DIGITAL VCR",
+        "hsn_code": "85234150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARTRIDGE TAPE",
+        "hsn_code": "85234140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MATRICES FOR PRODUCTION OF RECORDS; PREPARED RECORD BLANKS",
+        "hsn_code": "85234130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BLANK MASTER DISCS (THAT IS, SUBSTRATE) FOR PRODUCING STAMPER FOR COMPACT DISC",
+        "hsn_code": "85234120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPACT DISC (AUDIO/VIDEO)",
+        "hsn_code": "85234110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 CARDS INCORPORATING A MAGNETIC\nSTRIPE",
+        "hsn_code": "85233000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - MAGNETIC MEDIA -- OTHER --- OTHER",
+        "hsn_code": "85232990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - MAGNETIC MEDIA -- OTHER --- CARTRIDGE TAPE",
+        "hsn_code": "85232980",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - MAGNETIC MEDIA -- OTHER --- ALL KINDS OF MAGNETIC DISCS",
+        "hsn_code": "85232970",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - MAGNETIC MEDIA -- OTHER --- OTHER VIDEO CASSETTES AND\nTAPES 3/4 INCH AND 1 INCH VIDEO CASSETTES",
+        "hsn_code": "85232960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - MAGNETIC MEDIA -- OTHER --- 1/2 INCH VIDEO CASSETTES\nSUITABLE TO WORK WITH BETACAM, BETACAM SP/M II AND VHS TYPE VCR",
+        "hsn_code": "85232950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - MAGNETIC MEDIA -- OTHER --- 3/4 INCH AND 1 INCH VIDEO\nCASSETTES",
+        "hsn_code": "85232940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - MAGNETIC MEDIA -- OTHER --- VIDEO MAGNETIC TAPE INCLUDING\nTHOSE IN HUBS AND REELS, ROLLS, PANCAKES AND JUMBO ROLLS",
+        "hsn_code": "85232930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - MAGNETIC MEDIA -- OTHER --- VIDEO CASSETTES",
+        "hsn_code": "85232920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - MAGNETIC MEDIA -- OTHER --- AUDIO CASSETTES",
+        "hsn_code": "85232910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISCS, TAPES, SOLID-STATE NON-VOLATILE STORAGE DEVICES, SMART CARDS AND OTHER\nMEDIA FOR THE RECORDING OF SOUND OR OF OTHER PHENOMENA, WHETHER OR NOT\nRECORDED, INCLUDING MATRICES AND MASTERS PRODUCTION OF DISCS, BUT EXCLUDING\nPRODUCTS OF CHAPTER 37 - MAGNETIC MEDIA -- CARDS INCORPORATING A MAGNETIC STRIPE",
+        "hsn_code": "85232100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC DISCS: OTHER",
+        "hsn_code": "85232090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC DISCS: FLOPPY DISC OR\nDISKETTES",
+        "hsn_code": "85232020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC DISCS: HARD DISC PACK",
+        "hsn_code": "85232010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH\nEXCEEDING 6.5 MM: - FOR RECORDING PHENOMENA OTHER THAN SOUND : OTHER",
+        "hsn_code": "85231329",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH\nEXCEEDING 6.5 MM: - FOR RECORDING PHENOMENA OTHER THAN SOUND : OTHER VIDEO\nTAPES",
+        "hsn_code": "85231325",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH\nEXCEEDING 6.5 MM: - FOR RECORDING PHENOMENA OTHER THAN SOUND : AND 1 VIDEO\nTAPES",
+        "hsn_code": "85231324",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH\nEXCEEDING 6.5 MM: - FOR RECORDING PHENOMENA OTHER THAN SOUND : OTHER VIDEO\nCASSETTES",
+        "hsn_code": "85231323",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH\nEXCEEDING 6.5 MM: - FOR RECORDING PHENOMENA OTHER THAN SOUND :# VIDEO CASSETTE\nSUITABLE TO WORK WITH BETACAM, BETACAM SP/M II AND VHS TYPE VCR",
+        "hsn_code": "85231322",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH\nEXCEEDING 6.5 MM: - FOR RECORDING PHENOMENA OTHER THAN SOUND : AND 1 VIDEO\nCASSETTES",
+        "hsn_code": "85231321",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH\nEXCEEDING 6.5 MM: - FOR RECORDING SOUND : OTHER",
+        "hsn_code": "85231319",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH\nEXCEEDING 6.5 MM: - FOR RECORDING SOUND : OTHER AUDIO MAGNETIC TAPE OF WIDTH\nEXCEEDING6.5 MM BUT NOT EXCEEDING 35 MM, AND 16 MM SPROCKET TAPES",
+        "hsn_code": "85231313",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH\nEXCEEDING 6.5 MM: - FOR RECORDING SOUND : OTHER AUDIO CASSETTE TAPE",
+        "hsn_code": "85231312",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH\nEXCEEDING 6.5 MM: - FOR RECORDING SOUND : AUDIO CASSETTE TAPE OF WIDTH EXCEEDING\n6.5 MM BUT NOT EXCEEDING 35 MM, AND 16 MM SPROCKET TAPES",
+        "hsn_code": "85231311",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH\nEXCEEDING 4 MM BUT NOT EXCEEDING 6.5 MM: - FOR RECORDING PHENOMENA OTHER THAN\nSOUND : OTHER",
+        "hsn_code": "85231229",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH\nEXCEEDING 4 MM BUT NOT EXCEEDING 6.5 MM: - FOR RECORDING PHENOMENA OTHER THAN\nSOUND : OTHER VIDEO MAGNETIC TAPE INCLUDING THOSE IN HUBS AND REELS, ROLLS,\nPANCAKES AND JUMBO ROLLS",
+        "hsn_code": "85231222",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH\nEXCEEDING 4 MM BUT NOT EXCEEDING 6.5 MM: - FOR RECORDING PHENOMENA OTHER THAN\nSOUND : VIDEO CASSETTES",
+        "hsn_code": "85231221",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH\nEXCEEDING 4 MM BUT NOT EXCEEDING 6.5 MM: - FOR RECORDING SOUND: OTHER",
+        "hsn_code": "85231219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH\nEXCEEDING 4 MM BUT NOT EXCEEDING 6.5 MM: - FOR RECORDING SOUND: AUDIO CASSETTES",
+        "hsn_code": "85231211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH NOT\nEXCEEDING 4 MM : - FOR RECORDING PHENOMENA OTHER THAN SOUND: OTHER",
+        "hsn_code": "85231129",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH NOT\nEXCEEDING 4 MM : - FOR RECORDING PHENOMENA OTHER THAN SOUND: OTHER VIDEO\nMAGNETIC TAPE INCLUDING THOSE IN HUBS AND REELS, ROLLS, PANCAKES AND JUMBO ROLLS",
+        "hsn_code": "85231122",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH NOT\nEXCEEDING 4 MM : - FOR RECORDING PHENOMENA OTHER THAN SOUND: VIDEO CASSETTES",
+        "hsn_code": "85231121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH NOT\nEXCEEDING 4 MM : - FOR RECORDING SOUND : OTHER",
+        "hsn_code": "85231119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED UNRECORDED MEDIA FOR SOUND RECORDING OR SIMILAR RECORDING OF OTHER\nPHENOMENA, OTHER THAN PRODUCTS OF CHAPTER 37 - MAGNETIC TAPES: - OF A WIDTH NOT\nEXCEEDING 4 MM : - FOR RECORDING SOUND : AUDIO CASSETTES",
+        "hsn_code": "85231111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF HEADINGS 8519 OR 8521",
+        "hsn_code": "8522",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF\nHEADINGS 8519 TO 8521- OTHER",
+        "hsn_code": "85229000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE APPARATUS OF\nHEADINGS 8519 TO 8521PICK-UP CARTRIDGES",
+        "hsn_code": "85221000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VIDEO RECORDING OR REPRODUCING APPARATUS, WHETHER OR NOT INCORPORATING A VIDEO TUNER",
+        "hsn_code": "8521",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VIDEO RECORDING OR REPRODUCING APPARATUS, WHETHER OR NOT INCORPORATING A VIDEO\nTUNER OTHER : OTHER",
+        "hsn_code": "85219090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VIDEO RECORDING OR REPRODUCING APPARATUS, WHETHER OR NOT INCORPORATING A VIDEO\nTUNER OTHER : DVD PLAYER",
+        "hsn_code": "85219020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VIDEO RECORDING OR REPRODUCING APPARATUS, WHETHER OR NOT INCORPORATING A VIDEO\nTUNER OTHER : VIDEO DUPLICATING SYSTEM WITH MASTER AND SLAVE CONTROL",
+        "hsn_code": "85219010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VIDEO RECORDING OR REPRODUCING APPARATUS, WHETHER OR NOT INCORPORATING A VIDEO\nTUNER -MAGNETIC TAPE-TYPE: - OTHER: OTHER",
+        "hsn_code": "85211099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VIDEO RECORDING OR REPRODUCING APPARATUS, WHETHER OR NOT INCORPORATING A VIDEO\nTUNER -MAGNETIC TAPE-TYPE: - OTHER: VIDEO RECORDERS BETACAM OR BETACAM SP OR\nDIGITAL BETACAM S-VHS OR DIGITAL-S",
+        "hsn_code": "85211092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VIDEO RECORDING OR REPRODUCING APPARATUS, WHETHER OR NOT INCORPORATING A VIDEO\nTUNER -MAGNETIC TAPE-TYPE: - OTHER: PROFESSIONAL VIDEO TAPE RECORDERS WITH OR 1\nTAPE SOLID STATE OR OTHERWISE",
+        "hsn_code": "85211091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VIDEO RECORDING OR REPRODUCING APPARATUS, WHETHER OR NOT INCORPORATING A VIDEO\nTUNER -MAGNETIC TAPE-TYPE: - SPOOL TYPE: OTHER",
+        "hsn_code": "85211029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VIDEO RECORDING OR REPRODUCING APPARATUS, WHETHER OR NOT INCORPORATING A VIDEO\nTUNER -MAGNETIC TAPE-TYPE: - SPOOL TYPE: VIDEO RECORDERS BETACAM OR BETACAM SP OR\nDIGITAL BETACAM S-VHS OR DIGITAL-S",
+        "hsn_code": "85211022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VIDEO RECORDING OR REPRODUCING APPARATUS, WHETHER OR NOT INCORPORATING A VIDEO\nTUNER -MAGNETIC TAPE-TYPE: - SPOOL TYPE: PROFESSIONAL VIDEO TAPE RECORDERS WITH OR\n1 TAPE",
+        "hsn_code": "85211021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VIDEO RECORDING OR REPRODUCING APPARATUS, WHETHER OR NOT INCORPORATING A VIDEO\nTUNER -MAGNETIC TAPE-TYPE: - CASSETTE TAPE-TYPE: OTHER",
+        "hsn_code": "85211019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VIDEO RECORDING OR REPRODUCING APPARATUS, WHETHER OR NOT INCORPORATING A VIDEO\nTUNER -MAGNETIC TAPE-TYPE: - CASSETTE TAPE-TYPE: VIDEO RECORDERS BETACAM OR\nBETACAM SP OR DIGITAL BETACAM S-VHS OR DIGITAL-S",
+        "hsn_code": "85211012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VIDEO RECORDING OR REPRODUCING APPARATUS, WHETHER OR NOT INCORPORATING A VIDEO\nTUNER -MAGNETIC TAPE-TYPE: - CASSETTE TAPE-TYPE: PROFESSIONAL VIDEO TAPE RECORDERS\nWITH OR 1 TAPE",
+        "hsn_code": "85211011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOUND RECORDING OR REPRODUCING APPARATUS",
+        "hsn_code": "8519",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TURNTABLES (RECORD-DECKS), RECORD-PLAYERS, CASSETTE-PLAYERS AND OTHER SOUND\nREPRODUCING APPARATUS, NOT INCORPORATING A SOUND RECORDING DEVICE - OTHER : -\nOTHER SOUND REPRODUCING APPARATUS: OTHER",
+        "hsn_code": "85199990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TURNTABLES (RECORD-DECKS), RECORD-PLAYERS, CASSETTE-PLAYERS AND OTHER SOUND\nREPRODUCING APPARATUS, NOT INCORPORATING A SOUND RECORDING DEVICE - OTHER\nSOUND REPRODUCING APPARATUS: - OTHER : MP-3 PLAYER",
+        "hsn_code": "85199940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TURNTABLES (RECORD-DECKS), RECORD-PLAYERS, CASSETTE-PLAYERS AND OTHER SOUND\nREPRODUCING APPARATUS, NOT INCORPORATING A SOUND RECORDING DEVICE - OTHER : -\nOTHER SOUND REPRODUCING APPARATUS: TIME CODE READERS",
+        "hsn_code": "85199930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TURNTABLES (RECORD-DECKS), RECORD-PLAYERS, CASSETTE-PLAYERS AND OTHER SOUND\nREPRODUCING APPARATUS, NOT INCORPORATING A SOUND RECORDING DEVICE - OTHER : -\nOTHER SOUND REPRODUCING APPARATUS: COMPACT DISC CHANGER INCLUDING MINI DISC\nPLAYER OR LASER DISC PLAYER",
+        "hsn_code": "85199920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TURNTABLES (RECORD-DECKS), RECORD-PLAYERS, CASSETTE-PLAYERS AND OTHER SOUND\nREPRODUCING APPARATUS, NOT INCORPORATING A SOUND RECORDING DEVICE - OTHER : -\nOTHER SOUND REPRODUCING APPARATUS: AUDIO COMPACT DISC PLAYERS",
+        "hsn_code": "85199910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TURNTABLES (RECORD-DECKS), RECORD-PLAYERS, CASSETTE-PLAYERS AND OTHER SOUND\nREPRODUCING APPARATUS, NOT INCORPORATING A SOUND RECORDING DEVICE - OTHER\nSOUND REPRODUCING APPARATUS: OTHER, CASSETTE-TYPE",
+        "hsn_code": "85199300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TURNTABLES (RECORD-DECKS), RECORD-PLAYERS, CASSETTE-PLAYERS AND OTHER SOUND\nREPRODUCING APPARATUS, NOT INCORPORATING A SOUND RECORDING DEVICE - OTHER\nSOUND REPRODUCING APPARATUS: POCKET-SIZE CASSETTE PLAYERS",
+        "hsn_code": "85199200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOUND RECORDING OR REPRODUCING APPARATUS - OTHER APPARATUS -- OTHER --- OTHER",
+        "hsn_code": "85198990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOUND RECORDING OR REPRODUCING APPARATUS - OTHER APPARATUS -- OTHER --- MP-3\nPLAYER",
+        "hsn_code": "85198940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOUND RECORDING OR REPRODUCING APPARATUS - OTHER APPARATUS -- OTHER --- TIME CODE\nRECORDER",
+        "hsn_code": "85198930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOUND RECORDING OR REPRODUCING APPARATUS - OTHER APPARATUS -- OTHER --- COMPACT\nDISC CHANGER INCLUDING MINI DISC PLAYER OR LASER DISC PLAYER",
+        "hsn_code": "85198920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOUND RECORDING OR REPRODUCING APPARATUS - OTHER APPARATUS -- OTHER --- AUDIO\nCOMPACT DISC PLAYER",
+        "hsn_code": "85198910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOUND RECORDING OR REPRODUCING APPARATUS - OTHER APPARATUS -- USING MAGNETIC,\nOPTICAL OR SEMICONDUCTOR MEDIA",
+        "hsn_code": "85198100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOUND RECORDING OR REPRODUCING APPARATUS -TELEPHONE ANSWERING MACHINES",
+        "hsn_code": "85195000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TURNTABLES (RECORD-DECKS), RECORD-PLAYERS, CASSETTE-PLAYERS AND OTHER SOUND\nREPRODUCING APPARATUS, NOT INCORPORATING A SOUND RECORDING DEVICE TRANSCRIBING\nMACHINES",
+        "hsn_code": "85194000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TURNTABLES (RECORD-DECKS), RECORD-PLAYERS, CASSETTE-PLAYERS AND OTHER SOUND\nREPRODUCING APPARATUS, NOT INCORPORATING A SOUND RECORDING DEVICE - TURNTABLES\n(RECORD-DECKS): OTHER",
+        "hsn_code": "85193900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TURNTABLES (RECORD-DECKS), RECORD-PLAYERS, CASSETTE-PLAYERS AND OTHER SOUND\nREPRODUCING APPARATUS, NOT INCORPORATING A SOUND RECORDING DEVICE - TURNTABLES\n(RECORD-DECKS): WITH AUTOMATIC RECORD CHANGING MECHANISM",
+        "hsn_code": "85193100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOUND RECORDING OR REPRODUCING APPARATUS -TURNTABLES (RECORD-DECKS)",
+        "hsn_code": "85193000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TURNTABLES (RECORD-DECKS), RECORD-PLAYERS, CASSETTE-PLAYERS AND OTHER SOUND\nREPRODUCING APPARATUS, NOT INCORPORATING A SOUND RECORDING DEVICE - OTHER\nRECORD-PLAYERS: OTHER",
+        "hsn_code": "85192900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TURNTABLES (RECORD-DECKS), RECORD-PLAYERS, CASSETTE-PLAYERS AND OTHER SOUND\nREPRODUCING APPARATUS, NOT INCORPORATING A SOUND RECORDING DEVICE - OTHER\nRECORD-PLAYERS: WITHOUT LOUDSPEAKER",
+        "hsn_code": "85192100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOUND RECORDING OR REPRODUCING APPARATUS - APPARATUS OPERATED BY COINS, BANK\nNOTES, BANK CARDS, TOKENS OR BY OTHER MEANS OF PAYMENT",
+        "hsn_code": "85192000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TURNTABLES (RECORD-DECKS), RECORD-PLAYERS, CASSETTE-PLAYERS AND OTHER SOUND\nREPRODUCING APPARATUS, NOT INCORPORATING A SOUND RECORDING DEVICE COIN OR DISC\nOPERATED RECORD-PLAYERS",
+        "hsn_code": "85191000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MICROPHONES AND STANDS THEREFOR; LOUDSPEAKERS, WHETHER OR NOT MOUNTED IN THEIR ENCLOSURES; HEADPHONES AND EARPHONES, WHETHER OR NOT COMBINED WITH A MICROPHONE, AND SETS CONSISTING OF A MICROPHONE AND ONE OR MORE LOUDSPEAKERS; AUDIO-FREQUENCY ELECTRIC AMPLIFIERS; ELECTRIC SOUND AMPLIFIER SETS",
+        "hsn_code": "8518",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MICROPHONES AND STANDS THEREFOR; LOUDSPEAKERS, WHETHER OR NOT MOUNTED IN\nTHEIR ENCLOSURES; HEADPHONES AND EARPHONES, WHETHER OR NOT COMBINED WITH A\nMICROPHONE, AND SETS CONSISTING OF A MICROPHONE AND ONE OR MORE LOUDSPEAKERS;\nAUDIOFREQUENCY ELECTRIC AMPLIFIERS; ELECTRIC SOUND AMPLIFIER SET PARTS",
+        "hsn_code": "85189000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MICROPHONES AND STANDS THEREFOR; LOUDSPEAKERS, WHETHER OR NOT MOUNTED IN\nTHEIR ENCLOSURES; HEADPHONES AND EARPHONES, WHETHER OR NOT COMBINED WITH A\nMICROPHONE, AND SETS CONSISTING OF A MICROPHONE AND ONE OR MORE LOUDSPEAKERS;\nAUDIOFREQUENCY ELECTRIC AMPLIFIERS; ELECTRIC SOUND AMPLIFIER SET ELECTRIC SOUND\nAMPLIFIER SETS",
+        "hsn_code": "85185000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MICROPHONES AND STANDS THEREFOR; LOUDSPEAKERS, WHETHER OR NOT MOUNTED IN\nTHEIR ENCLOSURES; HEADPHONES AND EARPHONES, WHETHER OR NOT COMBINED WITH A\nMICROPHONE, AND SETS CONSISTING OF A MICROPHONE AND ONE OR MORE LOUDSPEAKERS;\nAUDIOFREQUENCY ELECTRIC AMPLIFIERS; ELECTRIC SOUND AMPLIFIER SET AUDIO-FREQUENCY\nELECTRIC AMPLIFIERS",
+        "hsn_code": "85184000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MICROPHONES AND STANDS THEREFOR; LOUDSPEAKERS, WHETHER OR NOT MOUNTED IN\nTHEIR ENCLOSURES; HEADPHONES AND EARPHONES, WHETHER OR NOT COMBINED WITH A\nMICROPHONE, AND SETS CONSISTING OF A MICROPHONE AND ONE OR MORE LOUDSPEAKERS;\nAUDIOFREQUENCY ELECTRIC AMPLIFIERS; ELECTRIC SOUND AMPLIFIER SET HEADPHONES AND\nEARPHONES, WHETHER OR NOT COMBINED WITH A MICROPHONE, AND SETS CONSISTING OF A\nMICROPHONE AND ONE OR MORE LOUDSPEAKERS",
+        "hsn_code": "85183000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MICROPHONES AND STANDS THEREFOR; LOUDSPEAKERS, WHETHER OR NOT MOUNTED IN\nTHEIR ENCLOSURES; HEADPHONES AND EARPHONES, WHETHER OR NOT COMBINED WITH A\nMICROPHONE, AND SETS CONSISTING OF A MICROPHONE AND ONE OR MORE LOUDSPEAKERS;\nAUDIOFREQUENCY ELECTRIC AMPLIFIERS; ELECTRIC SOUND AMPLIFIER SET - LOUDSPEAKERS,\nWHETHER OR NOT MOUNTED IN THEIR ENCLOSURES: OTHER",
+        "hsn_code": "85182900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MICROPHONES AND STANDS THEREFOR; LOUDSPEAKERS, WHETHER OR NOT MOUNTED IN\nTHEIR ENCLOSURES; HEADPHONES AND EARPHONES, WHETHER OR NOT COMBINED WITH A\nMICROPHONE, AND SETS CONSISTING OF A MICROPHONE AND ONE OR MORE LOUDSPEAKERS;\nAUDIOFREQUENCY ELECTRIC AMPLIFIERS; ELECTRIC SOUND AMPLIFIER SET - LOUDSPEAKERS,\nWHETHER OR NOT MOUNTED IN THEIR ENCLOSURES: MULTIPLE LOUDSPEAKERS, MOUNTED IN\nTHE SAME ENCLOSURE",
+        "hsn_code": "85182200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MICROPHONES AND STANDS THEREFOR; LOUDSPEAKERS, WHETHER OR NOT MOUNTED IN\nTHEIR ENCLOSURES; HEADPHONES AND EARPHONES, WHETHER OR NOT COMBINED WITH A\nMICROPHONE, AND SETS CONSISTING OF A MICROPHONE AND ONE OR MORE LOUDSPEAKERS;\nAUDIOFREQUENCY ELECTRIC AMPLIFIERS; ELECTRIC SOUND AMPLIFIER SET - LOUDSPEAKERS,\nWHETHER OR NOT MOUNTED IN THEIR ENCLOSURES: SINGLE LOUDSPEAKERS, MOUNTED IN\nTHEIR ENCLOSURES",
+        "hsn_code": "85182100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MICROPHONES AND STANDS THEREFOR; LOUDSPEAKERS, WHETHER OR NOT MOUNTED IN\nTHEIR ENCLOSURES; HEADPHONES AND EARPHONES, WHETHER OR NOT COMBINED WITH A\nMICROPHONE, AND SETS CONSISTING OF A MICROPHONE AND ONE OR MORE LOUDSPEAKERS;\nAUDIOFREQUENCY ELECTRIC AMPLIFIERS; ELECTRIC SOUND AMPLIFIER SET MICROPHONES AND\nSTANDS THEREFOR",
+        "hsn_code": "85181000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS NETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR OTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS NETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR RECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528.",
+        "hsn_code": "8517",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - PARTS: OTHER",
+        "hsn_code": "85179090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - PARTS:\nPOPULATED, LOADED OR STUFFED PRINTED CIRCUIT BOARDS",
+        "hsn_code": "85179010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - OTHER\nAPPARATUS: OTHER",
+        "hsn_code": "85178090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - OTHER\nAPPARATUS: SET TOP BOXES FOR GAINING ACCESS TO THE INTERNET",
+        "hsn_code": "85178030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - OTHER\nAPPARATUS: SUBSCRIBER END EQUIPMENT",
+        "hsn_code": "85178020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - OTHER\nAPPARATUS: ATTACHMENTS FOR TELEPHONES",
+        "hsn_code": "85178010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - PARTS --- OTHER",
+        "hsn_code": "85177090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - PARTS --- POPULATED,\nLOADED OR STUFFED PRINTED CIRCUIT BOARDS",
+        "hsn_code": "85177010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - OTHER APPARATUS FOR THE\nTRANSMISSION OR RECEPTION OF VOICE, IMAGES OR OTHER DATA, INCLUDING APPARATUS FOR\nCOMMUNICATION IN A WIRED OR WIRELESS NETWORK (SUCH AS A LOCAL OR WIDE AREA\nNETWORK) -- OTHER: --- OTHER",
+        "hsn_code": "85176990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - OTHER APPARATUS FOR THE\nTRANSMISSION OR RECEPTION OF VOICE, IMAGES OR OTHER DATA, INCLUDING APPARATUS FOR\nCOMMUNICATION IN A WIRED OR WIRELESS NETWORK (SUCH AS A LOCAL OR WIDE AREA\nNETWORK) -- OTHER: --- ATTACHMENT FOR TELEPHONES",
+        "hsn_code": "85176970",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - OTHER APPARATUS FOR THE\nTRANSMISSION OR RECEPTION OF VOICE, IMAGES OR OTHER DATA, INCLUDING APPARATUS FOR\nCOMMUNICATION IN A WIRED OR WIRELESS NETWORK (SUCH AS A LOCAL OR WIDE AREA\nNETWORK) -- OTHER: --- SET TOP BOXES FOR GAINING ACCESS TO INTERNET",
+        "hsn_code": "85176960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - OTHER APPARATUS FOR THE\nTRANSMISSION OR RECEPTION OF VOICE, IMAGES OR OTHER DATA, INCLUDING APPARATUS FOR\nCOMMUNICATION IN A WIRED OR WIRELESS NETWORK (SUCH AS A LOCAL OR WIDE AREA\nNETWORK) -- OTHER: --- SUBSCRIBER END EQUIPMENT",
+        "hsn_code": "85176950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - OTHER APPARATUS FOR THE\nTRANSMISSION OR RECEPTION OF VOICE, IMAGES OR OTHER DATA, INCLUDING APPARATUS FOR\nCOMMUNICATION IN A WIRED OR WIRELESS NETWORK (SUCH AS A LOCAL OR WIDE AREA\nNETWORK) -- OTHER: --- X25 PADS",
+        "hsn_code": "85176940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - OTHER APPARATUS FOR THE\nTRANSMISSION OR RECEPTION OF VOICE, IMAGES OR OTHER DATA, INCLUDING APPARATUS FOR\nCOMMUNICATION IN A WIRED OR WIRELESS NETWORK (SUCH AS A LOCAL OR WIDE AREA\nNETWORK) -- OTHER: --- ROUTERS",
+        "hsn_code": "85176930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - OTHER APPARATUS FOR THE\nTRANSMISSION OR RECEPTION OF VOICE, IMAGES OR OTHER DATA, INCLUDING APPARATUS FOR\nCOMMUNICATION IN A WIRED OR WIRELESS NETWORK (SUCH AS A LOCAL OR WIDE AREA\nNETWORK) -- OTHER: --- ISDN TERMINAL ADAPTOR",
+        "hsn_code": "85176920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - OTHER APPARATUS FOR THE\nTRANSMISSION OR RECEPTION OF VOICE, IMAGES OR OTHER DATA, INCLUDING APPARATUS FOR\nCOMMUNICATION IN A WIRED OR WIRELESS NETWORK (SUCH AS A LOCAL OR WIDE AREA\nNETWORK) -- OTHER: --- ISDN SYSTEM",
+        "hsn_code": "85176910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - OTHER APPARATUS FOR THE\nTRANSMISSION OR RECEPTION OF VOICE, IMAGES OR OTHER DATA, INCLUDING APPARATUS FOR\nCOMMUNICATION IN A WIRED OR WIRELESS NETWORK (SUCH AS A LOCAL OR WIDE AREA\nNETWORK) -- MACHINES FOR THE RECEPTION, CONVERSION AND TRANSMISSION OR\nREGENRATION OF VOICE, IMAGES OR OTHER DATA, INCLUDING SWITCHING AND ROUTING\nAPPARATUS: --- OTHER",
+        "hsn_code": "85176290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - OTHER APPARATUS FOR THE\nTRANSMISSION OR RECEPTION OF VOICE, IMAGES OR OTHER DATA, INCLUDING APPARATUS FOR\nCOMMUNICATION IN A WIRED OR WIRELESS NETWORK (SUCH AS A LOCAL OR WIDE AREA\nNETWORK) -- MACHINES FOR THE RECEPTION, CONVERSION AND TRANSMISSION OR\nREGENRATION OF VOICE, IMAGES OR OTHER DATA, INCLUDING SWITCHING AND ROUTING\nAPPARATUS: --- MULTIPLEXERS, STATISTICAL MULTIPLEXERS",
+        "hsn_code": "85176270",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - OTHER APPARATUS FOR THE\nTRANSMISSION OR RECEPTION OF VOICE, IMAGES OR OTHER DATA, INCLUDING APPARATUS FOR\nCOMMUNICATION IN A WIRED OR WIRELESS NETWORK (SUCH AS A LOCAL OR WIDE AREA\nNETWORK) -- MACHINES FOR THE RECEPTION, CONVERSION AND TRANSMISSION OR\nREGENRATION OF VOICE, IMAGES OR OTHER DATA, INCLUDING SWITCHING AND ROUTING\nAPPARATUS: --- SYNCHRONOUS DIGITAL HIERARCHY SYSTEM (SDH)",
+        "hsn_code": "85176260",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - OTHER APPARATUS FOR THE\nTRANSMISSION OR RECEPTION OF VOICE, IMAGES OR OTHER DATA, INCLUDING APPARATUS FOR\nCOMMUNICATION IN A WIRED OR WIRELESS NETWORK (SUCH AS A LOCAL OR WIDE AREA\nNETWORK) -- MACHINES FOR THE RECEPTION, CONVERSION AND TRANSMISSION OR\nREGENRATION OF VOICE, IMAGES OR OTHER DATA, INCLUDING SWITCHING AND ROUTING\nAPPARATUS: --- DIGIAL LOOP CARRIER SYSTEM (DLC)",
+        "hsn_code": "85176250",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - OTHER APPARATUS FOR THE\nTRANSMISSION OR RECEPTION OF VOICE, IMAGES OR OTHER DATA, INCLUDING APPARATUS FOR\nCOMMUNICATION IN A WIRED OR WIRELESS NETWORK (SUCH AS A LOCAL OR WIDE AREA\nNETWORK) -- MACHINES FOR THE RECEPTION, CONVERSION AND TRANSMISSION OR\nREGENRATION OF VOICE, IMAGES OR OTHER DATA, INCLUDING SWITCHING AND ROUTING\nAPPARATUS: --- HIGH BIT RATE DIGITAL SUBSCRIBER LINE SYSTEM (HSDL)",
+        "hsn_code": "85176240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - OTHER APPARATUS FOR THE\nTRANSMISSION OR RECEPTION OF VOICE, IMAGES OR OTHER DATA, INCLUDING APPARATUS FOR\nCOMMUNICATION IN A WIRED OR WIRELESS NETWORK (SUCH AS A LOCAL OR WIDE AREA\nNETWORK) -- MACHINES FOR THE RECEPTION, CONVERSION AND TRANSMISSION OR\nREGENRATION OF VOICE, IMAGES OR OTHER DATA, INCLUDING SWITCHING AND ROUTING\nAPPARATUS: --- MODEMS (MODULATORS-DEMODULATORS)",
+        "hsn_code": "85176230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - OTHER APPARATUS FOR THE\nTRANSMISSION OR RECEPTION OF VOICE, IMAGES OR OTHER DATA, INCLUDING APPARATUS FOR\nCOMMUNICATION IN A WIRED OR WIRELESS NETWORK (SUCH AS A LOCAL OR WIDE AREA\nNETWORK) -- MACHINES FOR THE RECEPTION, CONVERSION AND TRANSMISSION OR\nREGENRATION OF VOICE, IMAGES OR OTHER DATA, INCLUDING SWITCHING AND ROUTING\nAPPARATUS: --- VOICE FREQUENCY TELEGRAPHY",
+        "hsn_code": "85176220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - OTHER APPARATUS FOR THE\nTRANSMISSION OR RECEPTION OF VOICE, IMAGES OR OTHER DATA, INCLUDING APPARATUS FOR\nCOMMUNICATION IN A WIRED OR WIRELESS NETWORK (SUCH AS A LOCAL OR WIDE AREA\nNETWORK) -- MACHINES FOR THE RECEPTION, CONVERSION AND TRANSMISSION OR\nREGENRATION OF VOICE, IMAGES OR OTHER DATA, INCLUDING SWITCHING AND ROUTING\nAPPARATUS: --- PLCC EQUIPMENT",
+        "hsn_code": "85176210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - OTHER APPARATUS FOR THE\nTRANSMISSION OR RECEPTION OF VOICE, IMAGES OR OTHER DATA, INCLUDING APPARATUS FOR\nCOMMUNICATION IN A WIRED OR WIRELESS NETWORK (SUCH AS A LOCAL OR WIDE AREA\nNETWORK) -- BASE STATIONS",
+        "hsn_code": "85176100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - OTHER\nAPPARATUS, FOR CARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS: - OTHER:\nOTHER",
+        "hsn_code": "85175099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - OTHER\nAPPARATUS, FOR CARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS: - OTHER: X25\nPADS",
+        "hsn_code": "85175094",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - OTHER\nAPPARATUS, FOR CARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS: - OTHER:\nROUTERS",
+        "hsn_code": "85175093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - OTHER\nAPPARATUS, FOR CARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS: - OTHER:\nISDN TERMINAL ADAPTERS",
+        "hsn_code": "85175092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - OTHER\nAPPARATUS, FOR CARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS: - OTHER:\nISDN TERMINALS",
+        "hsn_code": "85175091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - OTHER\nAPPARATUS, FOR CARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS:\nMULTIPLEXER, STATISTICAL MULTIPLEXER",
+        "hsn_code": "85175070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - OTHER\nAPPARATUS, FOR CARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS:\nSYNCHRONOUS DIGITAL HIERARCHY SYSTEM (SDH)",
+        "hsn_code": "85175060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - OTHER\nAPPARATUS, FOR CARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS: DIGITAL\nLOOP CARRIER SYSTEM (DLC)",
+        "hsn_code": "85175050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - OTHER\nAPPARATUS, FOR CARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS: HIGH BIT\nRATE DIGITAL SUBSCRIBER LINE SYSTEM (HDSL)",
+        "hsn_code": "85175040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - OTHER\nAPPARATUS, FOR CARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS: MODEMS\n(MODULATORS-DEMODULATORS)",
+        "hsn_code": "85175030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - OTHER\nAPPARATUS, FOR CARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS: VOICE\nFREQUENCY TELEGRAPHY",
+        "hsn_code": "85175020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - OTHER\nAPPARATUS, FOR CARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS: PLCC\nEQUIPMENT",
+        "hsn_code": "85175010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES TELEPHONIC\nOR TELEGRAPHIC SWITCHING APPARATUS",
+        "hsn_code": "85173000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - FACSIMILE\nMACHINES AND TELEPRINTERS: TELEPRINTERS",
+        "hsn_code": "85172200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - FACSIMILE\nMACHINES AND TELEPRINTERS: FASCIMILE MACHINES",
+        "hsn_code": "85172100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - TELEPHONE\nSETS; VIDEOPHONES: - OTHER: VIDEOPHONES",
+        "hsn_code": "85171920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - TELEPHONE\nSETS; VIDEOPHONES: - OTHER: - TELEPHONE SETS: OTHER",
+        "hsn_code": "85171919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - TELEPHONE\nSETS; VIDEOPHONES: - OTHER: - TELEPHONE SETS: ROTARY DIAL TYPE",
+        "hsn_code": "85171912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL APPARATUS FOR LINE TELEPHONY OR LINE TELEGRAPHY, INCLUDING LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS AND TELECOMMUNICATION APPARATUS FOR\nCARRIER-CURRENT LINE SYSTEMS OR FOR DIGITAL LINE SYSTEMS; VIDEOPHONES - TELEPHONE\nSETS; VIDEOPHONES: - OTHER: - TELEPHONE SETS: PUSH BUTTON TYPE",
+        "hsn_code": "85171911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - TELEPHONE SETS, INCLUDING\nTELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS NETWORKS: -- OTHER: ---\nOTHER",
+        "hsn_code": "85171890",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - TELEPHONE SETS, INCLUDING\nTELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS NETWORKS: -- OTHER: ---\nPUSH BUTTON TYPE",
+        "hsn_code": "85171810",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - TELEPHONE SETS, INCLUDING\nTELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS NETWORKS: -- TELEPHONES\nFOR CELLULAR NETWORKS OR FOR OTHER WIRELESS NETWORKS: --- OTHER",
+        "hsn_code": "85171290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - TELEPHONE SETS, INCLUDING\nTELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS NETWORKS: -- TELEPHONES\nFOR CELLULAR NETWORKS OR FOR OTHER WIRELESS NETWORKS: --- PUSH BUTTON TYPE",
+        "hsn_code": "85171210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - TELEPHONE SETS, INCLUDING\nTELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS NETWORKS: -- LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS: --- OTHER",
+        "hsn_code": "85171190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TELEPHONE SETS, INCLUDING TELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS\nNETWORKS; OTHER APPARATUS FOR THE TRANSMISSION OR RECEPTION OF VOICE, IMAGES OR\nOTHER DATA, INCLUDING APPARATUS FOR COMMUNICATION IN A WIRED OR WIRELESS\nNETWORK (SUCH AS A LOCAL OR WIDE AREA NETWORK), OTHER THAN TRANSMISSION OR\nRECEPTION APPARATUS OF HEADING 8443, 8525, 8527 OR 8528 - TELEPHONE SETS, INCLUDING\nTELEPHONES FOR CELLULAR NETWORKS OR FOR OTHER WIRELESS NETWORKS: -- LINE\nTELEPHONE SETS WITH CORDLESS HANDSETS: --- PUSH BUTTON TYPE",
+        "hsn_code": "85171110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC INSTANTANEOUS OR STORAGE WATER HEATERS AND IMMERSION HEATERS; ELECTRIC SPACE HEATING APPARATUS AND SOIL HEATING APPARATUS; ELECTRO-THERMIC HAIR-DRESSING APPARATUS (FOR EXAMPLE, HAIR DRYERS, HAIR CURLERS, CURLING TONG HEATERS) AND HAND DRYERS, ELECTRIC SMOOTHING IRONS; OTHER ELECTRO-THERMIC APPLIANCES OF A KIND USED FOR DOMESTIC PURPOSES; ELECTRIC HEATING RESISTORS, OTHER THAN THOSE OF HEADING 8545",
+        "hsn_code": "8516",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC INSTANTANEOUS OR STORAGE WATER HEATERS AND IMMERSION HEATERS; ELECTRIC\nSPACE HEATING APPARATUS AND SOIL HEATING APPARATUS; ELECTROTHERMIC HAIR-DRESSING\nAPPARATUS (FOR EXAMPLE, HAIR DRYERS, HAIR CURLERS, CURLING TONG HEATERS) AND HAND\nDRYERS; ELECTRIC SMOOTHING IRONS; OTHER ELECTRO-THERMIC APPLIANCES OF A KIND USED\nFOR DOMESTIC PURPOSES; ELECTRIC HEATING RESISTORS, OTHER THAN THOSE OF HEADING\n8545 PARTS",
+        "hsn_code": "85169000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC INSTANTANEOUS OR STORAGE WATER HEATERS AND IMMERSION HEATERS; ELECTRIC\nSPACE HEATING APPARATUS AND SOIL HEATING APPARATUS; ELECTROTHERMIC HAIR-DRESSING\nAPPARATUS (FOR EXAMPLE, HAIR DRYERS, HAIR CURLERS, CURLING TONG HEATERS) AND HAND\nDRYERS; ELECTRIC SMOOTHING IRONS; OTHER ELECTRO-THERMIC APPLIANCES OF A KIND USED\nFOR DOMESTIC PURPOSES; ELECTRIC HEATING RESISTORS, OTHER THAN THOSE OF HEADING\n8545 ELECTRIC HEATING RESISTORS",
+        "hsn_code": "85168000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC INSTANTANEOUS OR STORAGE WATER HEATERS AND IMMERSION HEATERS; ELECTRIC\nSPACE HEATING APPARATUS AND SOIL HEATING APPARATUS; ELECTROTHERMIC HAIR-DRESSING\nAPPARATUS (FOR EXAMPLE, HAIR DRYERS, HAIR CURLERS, CURLING TONG HEATERS) AND HAND\nDRYERS; ELECTRIC SMOOTHING IRONS; OTHER ELECTRO-THERMIC APPLIANCES OF A KIND USED\nFOR DOMESTIC PURPOSES; ELECTRIC HEATING RESISTORS, OTHER THAN THOSE OF HEADING\n8545 - OTHER ELECTRO-THERMIC APPLIANCES: - OTHER: OTHER",
+        "hsn_code": "85167990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC INSTANTANEOUS OR STORAGE WATER HEATERS AND IMMERSION HEATERS; ELECTRIC\nSPACE HEATING APPARATUS AND SOIL HEATING APPARATUS; ELECTROTHERMIC HAIR-DRESSING\nAPPARATUS (FOR EXAMPLE, HAIR DRYERS, HAIR CURLERS, CURLING TONG HEATERS) AND HAND\nDRYERS; ELECTRIC SMOOTHING IRONS; OTHER ELECTRO-THERMIC APPLIANCES OF A KIND USED\nFOR DOMESTIC PURPOSES; ELECTRIC HEATING RESISTORS, OTHER THAN THOSE OF HEADING\n8545 - OTHER ELECTRO-THERMIC APPLIANCES: - OTHER: ELECTRICAL OR ELECTRONIC DEVICES\nFOR REPELLING INSECTS (FOR EXAMPLE, MOSQUITOES OR OTHER SIMILAR KIND OF INSECTS)",
+        "hsn_code": "85167920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC INSTANTANEOUS OR STORAGE WATER HEATERS AND IMMERSION HEATERS; ELECTRIC\nSPACE HEATING APPARATUS AND SOIL HEATING APPARATUS; ELECTROTHERMIC HAIR-DRESSING\nAPPARATUS (FOR EXAMPLE, HAIR DRYERS, HAIR CURLERS, CURLING TONG HEATERS) AND HAND\nDRYERS; ELECTRIC SMOOTHING IRONS; OTHER ELECTRO-THERMIC APPLIANCES OF A KIND USED\nFOR DOMESTIC PURPOSES; ELECTRIC HEATING RESISTORS, OTHER THAN THOSE OF HEADING\n8545 - OTHER ELECTRO-THERMIC APPLIANCES: - OTHER: ELECTRO-THERMIC FLUID HEATERS",
+        "hsn_code": "85167910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC INSTANTANEOUS OR STORAGE WATER HEATERS AND IMMERSION HEATERS; ELECTRIC\nSPACE HEATING APPARATUS AND SOIL HEATING APPARATUS; ELECTROTHERMIC HAIR-DRESSING\nAPPARATUS (FOR EXAMPLE, HAIR DRYERS, HAIR CURLERS, CURLING TONG HEATERS) AND HAND\nDRYERS; ELECTRIC SMOOTHING IRONS; OTHER ELECTRO-THERMIC APPLIANCES OF A KIND USED\nFOR DOMESTIC PURPOSES; ELECTRIC HEATING RESISTORS, OTHER THAN THOSE OF HEADING\n8545 - OTHER ELECTRO-THERMIC APPLIANCES: TOASTERS",
+        "hsn_code": "85167200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC INSTANTANEOUS OR STORAGE WATER HEATERS AND IMMERSION HEATERS; ELECTRIC\nSPACE HEATING APPARATUS AND SOIL HEATING APPARATUS; ELECTROTHERMIC HAIR-DRESSING\nAPPARATUS (FOR EXAMPLE, HAIR DRYERS, HAIR CURLERS, CURLING TONG HEATERS) AND HAND\nDRYERS; ELECTRIC SMOOTHING IRONS; OTHER ELECTRO-THERMIC APPLIANCES OF A KIND USED\nFOR DOMESTIC PURPOSES; ELECTRIC HEATING RESISTORS, OTHER THAN THOSE OF HEADING\n8545 - OTHER ELECTRO-THERMIC APPLIANCES: COFFEE OR TEA MAKERS",
+        "hsn_code": "85167100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC INSTANTANEOUS OR STORAGE WATER HEATERS AND IMMERSION HEATERS; ELECTRIC\nSPACE HEATING APPARATUS AND SOIL HEATING APPARATUS; ELECTROTHERMIC HAIR-DRESSING\nAPPARATUS (FOR EXAMPLE, HAIR DRYERS, HAIR CURLERS, CURLING TONG HEATERS) AND HAND\nDRYERS; ELECTRIC SMOOTHING IRONS; OTHER ELECTRO-THERMIC APPLIANCES OF A KIND USED\nFOR DOMESTIC PURPOSES; ELECTRIC HEATING RESISTORS, OTHER THAN THOSE OF HEADING\n8545 OTHER OVENS; COOKERS, COOKING PLATES, BOILING RINGS, GRILLERS AND ROASTERS",
+        "hsn_code": "85166000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC INSTANTANEOUS OR STORAGE WATER HEATERS AND IMMERSION HEATERS; ELECTRIC\nSPACE HEATING APPARATUS AND SOIL HEATING APPARATUS; ELECTROTHERMIC HAIR-DRESSING\nAPPARATUS (FOR EXAMPLE, HAIR DRYERS, HAIR CURLERS, CURLING TONG HEATERS) AND HAND\nDRYERS; ELECTRIC SMOOTHING IRONS; OTHER ELECTRO-THERMIC APPLIANCES OF A KIND USED\nFOR DOMESTIC PURPOSES; ELECTRIC HEATING RESISTORS, OTHER THAN THOSE OF HEADING\n8545 MICROWAVE OVENS",
+        "hsn_code": "85165000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC INSTANTANEOUS OR STORAGE WATER HEATERS AND IMMERSION HEATERS; ELECTRIC\nSPACE HEATING APPARATUS AND SOIL HEATING APPARATUS; ELECTROTHERMIC HAIR-DRESSING\nAPPARATUS (FOR EXAMPLE, HAIR DRYERS, HAIR CURLERS, CURLING TONG HEATERS) AND HAND\nDRYERS; ELECTRIC SMOOTHING IRONS; OTHER ELECTRO-THERMIC APPLIANCES OF A KIND USED\nFOR DOMESTIC PURPOSES; ELECTRIC HEATING RESISTORS, OTHER THAN THOSE OF HEADING\n8545 ELECTRIC SMOOTHING IRONS",
+        "hsn_code": "85164000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC INSTANTANEOUS OR STORAGE WATER HEATERS AND IMMERSION HEATERS; ELECTRIC\nSPACE HEATING APPARATUS AND SOIL HEATING APPARATUS; ELECTROTHERMIC HAIR-DRESSING\nAPPARATUS (FOR EXAMPLE, HAIR DRYERS, HAIR CURLERS, CURLING TONG HEATERS) AND HAND\nDRYERS; ELECTRIC SMOOTHING IRONS; OTHER ELECTRO-THERMIC APPLIANCES OF A KIND USED\nFOR DOMESTIC PURPOSES; ELECTRIC HEATING RESISTORS, OTHER THAN THOSE OF HEADING\n8545 - ELECTRO-THERMIC HAIR-DRESSING OR HAND-DRYING APPARATUS : HAND-DRYING\nAPPARATUS",
+        "hsn_code": "85163300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC INSTANTANEOUS OR STORAGE WATER HEATERS AND IMMERSION HEATERS; ELECTRIC\nSPACE HEATING APPARATUS AND SOIL HEATING APPARATUS; ELECTROTHERMIC HAIR-DRESSING\nAPPARATUS (FOR EXAMPLE, HAIR DRYERS, HAIR CURLERS, CURLING TONG HEATERS) AND HAND\nDRYERS; ELECTRIC SMOOTHING IRONS; OTHER ELECTRO-THERMIC APPLIANCES OF A KIND USED\nFOR DOMESTIC PURPOSES; ELECTRIC HEATING RESISTORS, OTHER THAN THOSE OF HEADING\n8545 - ELECTRO-THERMIC HAIR-DRESSING OR HAND-DRYING APPARATUS : OTHER HAIR\nDRESSING APPARATUS",
+        "hsn_code": "85163200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC INSTANTANEOUS OR STORAGE WATER HEATERS AND IMMERSION HEATERS; ELECTRIC\nSPACE HEATING APPARATUS AND SOIL HEATING APPARATUS; ELECTROTHERMIC HAIR-DRESSING\nAPPARATUS (FOR EXAMPLE, HAIR DRYERS, HAIR CURLERS, CURLING TONG HEATERS) AND HAND\nDRYERS; ELECTRIC SMOOTHING IRONS; OTHER ELECTRO-THERMIC APPLIANCES OF A KIND USED\nFOR DOMESTIC PURPOSES; ELECTRIC HEATING RESISTORS, OTHER THAN THOSE OF HEADING\n8545 - ELECTRO-THERMIC HAIR-DRESSING OR HAND-DRYING APPARATUS : HAIR DRYERS",
+        "hsn_code": "85163100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC INSTANTANEOUS OR STORAGE WATER HEATERS AND IMMERSION HEATERS; ELECTRIC\nSPACE HEATING APPARATUS AND SOIL HEATING APPARATUS; ELECTROTHERMIC HAIR-DRESSING\nAPPARATUS (FOR EXAMPLE, HAIR DRYERS, HAIR CURLERS, CURLING TONG HEATERS) AND HAND\nDRYERS; ELECTRIC SMOOTHING IRONS; OTHER ELECTRO-THERMIC APPLIANCES OF A KIND USED\nFOR DOMESTIC PURPOSES; ELECTRIC HEATING RESISTORS, OTHER THAN THOSE OF HEADING\n8545 - ELECTRIC SPACE HEATING APPARATUS AND ELECTRIC SOIL HEATING APPARATUS: OTHER",
+        "hsn_code": "85162900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC INSTANTANEOUS OR STORAGE WATER HEATERS AND IMMERSION HEATERS; ELECTRIC\nSPACE HEATING APPARATUS AND SOIL HEATING APPARATUS; ELECTROTHERMIC HAIR-DRESSING\nAPPARATUS (FOR EXAMPLE, HAIR DRYERS, HAIR CURLERS, CURLING TONG HEATERS) AND HAND\nDRYERS; ELECTRIC SMOOTHING IRONS; OTHER ELECTRO-THERMIC APPLIANCES OF A KIND USED\nFOR DOMESTIC PURPOSES; ELECTRIC HEATING RESISTORS, OTHER THAN THOSE OF HEADING\n8545 - ELECTRIC SPACE HEATING APPARATUS AND ELECTRIC SOIL HEATING APPARATUS:\nSTORAGE HEATING RADIATORS",
+        "hsn_code": "85162100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC INSTANTANEOUS OR STORAGE WATER HEATERS AND IMMERSION HEATERS; ELECTRIC\nSPACE HEATING APPARATUS AND SOIL HEATING APPARATUS; ELECTROTHERMIC HAIR-DRESSING\nAPPARATUS (FOR EXAMPLE, HAIR DRYERS, HAIR CURLERS, CURLING TONG HEATERS) AND HAND\nDRYERS; ELECTRIC SMOOTHING IRONS; OTHER ELECTRO-THERMIC APPLIANCES OF A KIND USED\nFOR DOMESTIC PURPOSES; ELECTRIC HEATING RESISTORS, OTHER THAN THOSE OF HEADING\n8545 ELECTRIC INSTANTANEOUS OR STORAGE WATER HEATERS AND IMMERSION HEATERS",
+        "hsn_code": "85161000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC (INCLUDING ELECTRICALLY HEATED GAS), LASER OR OTHER LIGHT OR PHOTO BEAM, ULTRASONIC, ELECTRON BEAM, MAGNETIC PULSE OR PLASMA ARC SOLDERING, BRAZING OR WELDING MACHINES AND APPARATUS, WHETHER OR NOT CAPABLE OF CUTTING; ELECTRIC MACHINES AND APPARATUS FOR HOT SPRAYING OF METALS OR CERMETS",
+        "hsn_code": "8515",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC (INCLUDING ELECTRICALLY HEATED GAS), LASER OR OTHER LIGHT OR PHOTO BEAM,\nULTRASONIC, ELECTRON BEAM, MAGNETIC PULSE OR PLASMA ARC SOLDERING, BRAZING OR\nWELDING MACHINES AND APPARATUS, WHETHER OR NOT CAPABLE OF CUTTING; ELECTRIC\nMACHINES AND APPARATUS FOR HOT SPRAYING OF METALS OR CERMETS PARTS",
+        "hsn_code": "85159000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC (INCLUDING ELECTRICALLY HEATED GAS), LASER OR OTHER LIGHT OR PHOTO BEAM,\nULTRASONIC, ELECTRON BEAM, MAGNETIC PULSE OR PLASMA ARC SOLDERING, BRAZING OR\nWELDING MACHINES AND APPARATUS, WHETHER OR NOT CAPABLE OF CUTTING; ELECTRIC\nMACHINES AND APPARATUS FOR HOT SPRAYING OF METALS OR CERMETS - OTHER MACHINES\nAND APPARATUS: OTHER",
+        "hsn_code": "85158090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC (INCLUDING ELECTRICALLY HEATED GAS), LASER OR OTHER LIGHT OR PHOTO BEAM,\nULTRASONIC, ELECTRON BEAM, MAGNETIC PULSE OR PLASMA ARC SOLDERING, BRAZING OR\nWELDING MACHINES AND APPARATUS, WHETHER OR NOT CAPABLE OF CUTTING; ELECTRIC\nMACHINES AND APPARATUS FOR HOT SPRAYING OF METALS OR CERMETS - OTHER MACHINES\nAND APPARATUS: HIGH-FREQUENCY PLASTIC WELDING MACHINE",
+        "hsn_code": "85158010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC (INCLUDING ELECTRICALLY HEATED GAS), LASER OR OTHER LIGHT OR PHOTO BEAM,\nULTRASONIC, ELECTRON BEAM, MAGNETIC PULSE OR PLASMA ARC SOLDERING, BRAZING OR\nWELDING MACHINES AND APPARATUS, WHETHER OR NOT CAPABLE OF CUTTING; ELECTRIC\nMACHINES AND APPARATUS FOR HOT SPRAYING OF METALS OR CERMETS - MACHINES AND\nAPPARATUS FOR ARC (INCLUDING PLASMA ARC) WELDING OF METALS: - OTHER: OTHER",
+        "hsn_code": "85153990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "39 ELECTRIC (INCLUDING ELECTRICALLY HEATED GAS), LASER OR OTHER LIGHT OR PHOTO BEAM,\nULTRASONIC, ELECTRON BEAM, MAGNETIC PULSE OR PLASMA ARC SOLDERING, BRAZING OR\nWELDING MACHINES AND APPARATUS, WHETHER OR NOT CAPABLE OF CUTTING; ELECTRIC\nMACHINES AND APPARATUS FOR HOT SPRAYING OF METALS OR CERMETS - MACHINES AND\nAPPARATUS FOR ARC (INCLUDING PLASMA ARC) WELDING OF METALS: - OTHER: ARGON ARC\nWELDING MACHINERY",
+        "hsn_code": "85153920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "39 ELECTRIC (INCLUDING ELECTRICALLY HEATED GAS), LASER OR OTHER LIGHT OR PHOTO BEAM,\nULTRASONIC, ELECTRON BEAM, MAGNETIC PULSE OR PLASMA ARC SOLDERING, BRAZING OR\nWELDING MACHINES AND APPARATUS, WHETHER OR NOT CAPABLE OF CUTTING; ELECTRIC\nMACHINES AND APPARATUS FOR HOT SPRAYING OF METALS OR CERMETS - MACHINES AND\nAPPARATUS FOR ARC (INCLUDING PLASMA ARC) WELDING OF METALS: - OTHER: AC ARC\nWELDING MACHINERY",
+        "hsn_code": "85153910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC (INCLUDING ELECTRICALLY HEATED GAS), LASER OR OTHER LIGHT OR PHOTO BEAM,\nULTRASONIC, ELECTRON BEAM, MAGNETIC PULSE OR PLASMA ARC SOLDERING, BRAZING OR\nWELDING MACHINES AND APPARATUS, WHETHER OR NOT CAPABLE OF CUTTING; ELECTRIC\nMACHINES AND APPARATUS FOR HOT SPRAYING OF METALS OR CERMETS - MACHINES AND\nAPPARATUS FOR ARC (INCLUDING PLASMA ARC) WELDING OF METALS: FULLY OR PARTLY\nAUTOMATIC",
+        "hsn_code": "85153100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC (INCLUDING ELECTRICALLY HEATED GAS), LASER OR OTHER LIGHT OR PHOTO BEAM,\nULTRASONIC, ELECTRON BEAM, MAGNETIC PULSE OR PLASMA ARC SOLDERING, BRAZING OR\nWELDING MACHINES AND APPARATUS, WHETHER OR NOT CAPABLE OF CUTTING; ELECTRIC\nMACHINES AND APPARATUS FOR HOT SPRAYING OF METALS OR CERMETS - MACHINES AND\nAPPARATUS FOR RESISTANCE WELDING OF METAL: OTHER",
+        "hsn_code": "85152900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC (INCLUDING ELECTRICALLY HEATED GAS), LASER OR OTHER LIGHT OR PHOTO BEAM,\nULTRASONIC, ELECTRON BEAM, MAGNETIC PULSE OR PLASMA ARC SOLDERING, BRAZING OR\nWELDING MACHINES AND APPARATUS, WHETHER OR NOT CAPABLE OF CUTTING; ELECTRIC\nMACHINES AND APPARATUS FOR HOT SPRAYING OF METALS OR CERMETS - MACHINES AND\nAPPARATUS FOR RESISTANCE WELDING OF METAL: - FULLY OR PARTLY AUTOMATIC: OTHER",
+        "hsn_code": "85152190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC (INCLUDING ELECTRICALLY HEATED GAS), LASER OR OTHER LIGHT OR PHOTO BEAM,\nULTRASONIC, ELECTRON BEAM, MAGNETIC PULSE OR PLASMA ARC SOLDERING, BRAZING OR\nWELDING MACHINES AND APPARATUS, WHETHER OR NOT CAPABLE OF CUTTING; ELECTRIC\nMACHINES AND APPARATUS FOR HOT SPRAYING OF METALS OR CERMETS - MACHINES AND\nAPPARATUS FOR RESISTANCE WELDING OF METAL: - FULLY OR PARTLY AUTOMATIC: AUTOMATIC\nBUTT WELDING MACHINERY",
+        "hsn_code": "85152120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC (INCLUDING ELECTRICALLY HEATED GAS), LASER OR OTHER LIGHT OR PHOTO BEAM,\nULTRASONIC, ELECTRON BEAM, MAGNETIC PULSE OR PLASMA ARC SOLDERING, BRAZING OR\nWELDING MACHINES AND APPARATUS, WHETHER OR NOT CAPABLE OF CUTTING; ELECTRIC\nMACHINES AND APPARATUS FOR HOT SPRAYING OF METALS OR CERMETS - MACHINES AND\nAPPARATUS FOR RESISTANCE WELDING OF METAL: - FULLY OR PARTLY AUTOMATIC: AUTOMATIC\nSPOT WELDING MACHINERY",
+        "hsn_code": "85152110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC (INCLUDING ELECTRICALLY HEATED GAS), LASER OR OTHER LIGHT OR PHOTO BEAM,\nULTRASONIC, ELECTRON BEAM, MAGNETIC PULSE OR PLASMA ARC SOLDERING, BRAZING OR\nWELDING MACHINES AND APPARATUS, WHETHER OR NOT CAPABLE OF CUTTING; ELECTRIC\nMACHINES AND APPARATUS FOR HOT SPRAYING OF METALS OR CERMETS - BRAZING OR\nSOLDERING MACHINES AND APPARATUS: OTHER",
+        "hsn_code": "85151900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC (INCLUDING ELECTRICALLY HEATED GAS), LASER OR OTHER LIGHT OR PHOTO BEAM,\nULTRASONIC, ELECTRON BEAM, MAGNETIC PULSE OR PLASMA ARC SOLDERING, BRAZING OR\nWELDING MACHINES AND APPARATUS, WHETHER OR NOT CAPABLE OF CUTTING; ELECTRIC\nMACHINES AND APPARATUS FOR HOT SPRAYING OF METALS OR CERMETS - BRAZING OR\nSOLDERING MACHINES AND APPARATUS: SOLDERING IRONS AND GUNS",
+        "hsn_code": "85151100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL OR LABORATORY ELECTRIC FURNACES AND OVENS (INCLUDING THOSE FUNCTIONING BY INDUCTION OR DIELECTRIC LOSS); OTHER INDUSTRIAL OR LABORATORY EQUIPMENT FOR THE HEAT TREATMENT OF MATERIALS BY INDUCTION OR DIELECTRIC LOSS",
+        "hsn_code": "8514",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL OR LABORATORY ELECTRIC FURNACES AND OVENS (INCLUDING THOSE\nFUNCTIONING BY INDUCTION OR DIELECTRIC LOSS); OTHER INDUSTRIAL OR LABORATORY\nEQUIPMENT FOR THE HEAT TREATMENT OF MATERIALS BY INDUCTION OR DIELECTRIC LOSS\nPARTS",
+        "hsn_code": "85149000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL OR LABORATORY ELECTRIC FURNACES AND OVENS (INCLUDING THOSE\nFUNCTIONING BY INDUCTION OR DIELECTRIC LOSS); OTHER INDUSTRIAL OR LABORATORY\nEQUIPMENT FOR THE HEAT TREATMENT OF MATERIALS BY INDUCTION OR DIELECTRIC LOSS\nOTHER EQUIPMENT FOR THE HEAT TREATMENT OF MATERIALS BY INDUCTION OR DIELECTRIC\nLOSS",
+        "hsn_code": "85144000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL OR LABORATORY ELECTRIC FURNACES AND OVENS (INCLUDING THOSE\nFUNCTIONING BY INDUCTION OR DIELECTRIC LOSS); OTHER INDUSTRIAL OR LABORATORY\nEQUIPMENT FOR THE HEAT TREATMENT OF MATERIALS BY INDUCTION OR DIELECTRIC LOSS -\nOTHER FURNACES AND OVENS: OTHER",
+        "hsn_code": "85143090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL OR LABORATORY ELECTRIC FURNACES AND OVENS (INCLUDING THOSE\nFUNCTIONING BY INDUCTION OR DIELECTRIC LOSS); OTHER INDUSTRIAL OR LABORATORY\nEQUIPMENT FOR THE HEAT TREATMENT OF MATERIALS BY INDUCTION OR DIELECTRIC LOSS -\nOTHER FURNACES AND OVENS: FOR MELTING",
+        "hsn_code": "85143010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL OR LABORATORY ELECTRIC FURNACES AND OVENS (INCLUDING THOSE\nFUNCTIONING BY INDUCTION OR DIELECTRIC LOSS); OTHER INDUSTRIAL OR LABORATORY\nEQUIPMENT FOR THE HEAT TREATMENT OF MATERIALS BY INDUCTION OR DIELECTRIC LOSS\nFURNACES AND OVENS FUNCTIONING BY INDUCTION OR DIELECTRIC LOSS",
+        "hsn_code": "85142000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL OR LABORATORY ELECTRIC FURNACES AND OVENS (INCLUDING THOSE\nFUNCTIONING BY INDUCTION OR DIELECTRIC LOSS); OTHER INDUSTRIAL OR LABORATORY\nEQUIPMENT FOR THE HEAT TREATMENT OF MATERIALS BY INDUCTION OR DIELECTRIC LOSS\nRESISTANCE HEATED FURNACES AND OVENS",
+        "hsn_code": "85141000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PORTABLE ELECTRIC LAMPS DESIGNED TO FUNCTION BY THEIR OWN SOURCE OF ENERGY (FOR EXAMPLE, DRY BATTERIES, ACCUMULATORS, MAGNETOS), OTHER THAN LIGHTING EQUIPMENT OF HEADING 8512",
+        "hsn_code": "8513",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PORTABLE ELECTRIC LAMPS DESIGNED TO FUNCTION BY THEIR OWN SOURCE OF ENERGY (FOR\nEXAMPLE, DRY BATTERIES, ACCUMULATORS, MAGNETOS), OTHER THAN LIGHTING EQUIPMENT\nOF HEADING 8512 8513 10 PORTABLE ELECTRIC LAMPS DESIGNED TO FUNCTION BY THEIR OWN\nSOURCE OF ENERGY (FOR EXAMPLE, DRY BATTERIES, ACCUMULATORS, MAGNETOS), OTHER\nTHAN LIGHTING EQUIPMENT OF HEADING 8512 - PARTS",
+        "hsn_code": "85139000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PORTABLE ELECTRIC LAMPS DESIGNED TO FUNCTION BY THEIR OWN SOURCE OF ENERGY (FOR\nEXAMPLE, DRY BATTERIES, ACCUMULATORS, MAGNETOS), OTHER THAN LIGHTING EQUIPMENT\nOF HEADING 8512 8513 10 PORTABLE ELECTRIC LAMPS DESIGNED TO FUNCTION BY THEIR OWN\nSOURCE OF ENERGY (FOR EXAMPLE, DRY BATTERIES, ACCUMULATORS, MAGNETOS), OTHER\nTHAN LIGHTING EQUIPMENT OF HEADING 8512 -LAMPS: OTHER",
+        "hsn_code": "85131090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PORTABLE ELECTRIC LAMPS DESIGNED TO FUNCTION BY THEIR OWN SOURCE OF ENERGY (FOR\nEXAMPLE, DRY BATTERIES, ACCUMULATORS, MAGNETOS), OTHER THAN LIGHTING EQUIPMENT\nOF HEADING 8512 8513 10 PORTABLE ELECTRIC LAMPS DESIGNED TO FUNCTION BY THEIR OWN\nSOURCE OF ENERGY (FOR EXAMPLE, DRY BATTERIES, ACCUMULATORS, MAGNETOS), OTHER\nTHAN LIGHTING EQUIPMENT OF HEADING 8512 -LAMPS: MAGNETO LAMPS",
+        "hsn_code": "85131040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PORTABLE ELECTRIC LAMPS DESIGNED TO FUNCTION BY THEIR OWN SOURCE OF ENERGY (FOR\nEXAMPLE, DRY BATTERIES, ACCUMULATORS, MAGNETOS), OTHER THAN LIGHTING EQUIPMENT\nOF HEADING 8512 8513 10 PORTABLE ELECTRIC LAMPS DESIGNED TO FUNCTION BY THEIR OWN\nSOURCE OF ENERGY (FOR EXAMPLE, DRY BATTERIES, ACCUMULATORS, MAGNETOS), OTHER\nTHAN LIGHTING EQUIPMENT OF HEADING 8512 -LAMPS: MINERS SAFETY LAMPS",
+        "hsn_code": "85131030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PORTABLE ELECTRIC LAMPS DESIGNED TO FUNCTION BY THEIR OWN SOURCE OF ENERGY (FOR\nEXAMPLE, DRY BATTERIES, ACCUMULATORS, MAGNETOS), OTHER THAN LIGHTING EQUIPMENT\nOF HEADING 8512 8513 10 PORTABLE ELECTRIC LAMPS DESIGNED TO FUNCTION BY THEIR OWN\nSOURCE OF ENERGY (FOR EXAMPLE, DRY BATTERIES, ACCUMULATORS, MAGNETOS), OTHER\nTHAN LIGHTING EQUIPMENT OF HEADING 8512 -LAMPS: OTHER FLASH-LIGHTS EXCLUDING\nTHOSE FOR PHOTOGRAPHIC PURPOSES",
+        "hsn_code": "85131020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PORTABLE ELECTRIC LAMPS DESIGNED TO FUNCTION BY THEIR OWN SOURCE OF ENERGY (FOR\nEXAMPLE, DRY BATTERIES, ACCUMULATORS, MAGNETOS), OTHER THAN LIGHTING EQUIPMENT\nOF HEADING 8512 8513 10 PORTABLE ELECTRIC LAMPS DESIGNED TO FUNCTION BY THEIR OWN\nSOURCE OF ENERGY (FOR EXAMPLE, DRY BATTERIES, ACCUMULATORS, MAGNETOS), OTHER\nTHAN LIGHTING EQUIPMENT OF HEADING 8512 -LAMPS: TORCH",
+        "hsn_code": "85131010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL LIGHTING OR SIGNALLING EQUIPMENT (EXCLUDING ARTICLES OF HEADING 8539), WINDSCREEN WIPERS, DEFROSTERS AND DEMISTERS, OF A KIND USED FOR CYCLES OR MOTOR VEHICLES",
+        "hsn_code": "8512",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL LIGHTING OR SIGNALLING EQUIPMENT (EXCLUDING ARTICLES OF HEADING 8539),\nWINDSCREEN WIPERS, DEFROSTERS AND DEMISTERS, OF A KIND USED FOR CYCLES OR MOTOR\nVEHICLES PARTS",
+        "hsn_code": "85129000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL LIGHTING OR SIGNALLING EQUIPMENT (EXCLUDING ARTICLES OF HEADING 8539),\nWINDSCREEN WIPERS, DEFROSTERS AND DEMISTERS, OF A KIND USED FOR CYCLES OR MOTOR\nVEHICLES WINDSCREEN WIPERS, DEFROSTERS AND DEMISTERS",
+        "hsn_code": "85124000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL LIGHTING OR SIGNALLING EQUIPMENT (EXCLUDING ARTICLES OF HEADING 8539),\nWINDSCREEN WIPERS, DEFROSTERS AND DEMISTERS, OF A KIND USED FOR CYCLES OR MOTOR\nVEHICLES -SOUND SIGNALLING EQUIPMENT: OTHER",
+        "hsn_code": "85123090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL LIGHTING OR SIGNALLING EQUIPMENT (EXCLUDING ARTICLES OF HEADING 8539),\nWINDSCREEN WIPERS, DEFROSTERS AND DEMISTERS, OF A KIND USED FOR CYCLES OR MOTOR\nVEHICLES -SOUND SIGNALLING EQUIPMENT: HORNS",
+        "hsn_code": "85123010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL LIGHTING OR SIGNALLING EQUIPMENT (EXCLUDING ARTICLES OF HEADING 8539),\nWINDSCREEN WIPERS, DEFROSTERS AND DEMISTERS, OF A KIND USED FOR CYCLES OR MOTOR\nVEHICLES - OTHER LIGHTING OR VISUAL SIGNALLING EQUIPMENT: OTHER",
+        "hsn_code": "85122090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL LIGHTING OR SIGNALLING EQUIPMENT (EXCLUDING ARTICLES OF HEADING 8539),\nWINDSCREEN WIPERS, DEFROSTERS AND DEMISTERS, OF A KIND USED FOR CYCLES OR MOTOR\nVEHICLES - OTHER LIGHTING OR VISUAL SIGNALLING EQUIPMENT: OTHER AUTOMOBILE\nLIGHTING EQUIPMENT",
+        "hsn_code": "85122020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL LIGHTING OR SIGNALLING EQUIPMENT (EXCLUDING ARTICLES OF HEADING 8539),\nWINDSCREEN WIPERS, DEFROSTERS AND DEMISTERS, OF A KIND USED FOR CYCLES OR MOTOR\nVEHICLES - OTHER LIGHTING OR VISUAL SIGNALLING EQUIPMENT: HEAD LAMPS, TAIL LAMPS,\nSTOP LAMPS, SIDE LAMPS AND BLINKERS",
+        "hsn_code": "85122010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL LIGHTING OR SIGNALLING EQUIPMENT (EXCLUDING ARTICLES OF HEADING 8539),\nWINDSCREEN WIPERS, DEFROSTERS AND DEMISTERS, OF A KIND USED FOR CYCLES OR MOTOR\nVEHICLES LIGHTING OR VISUAL SIGNALLING EQUIPMENT OF A KIND USED ON BICYCLES",
+        "hsn_code": "85121000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL IGNITION OR STARTING EQUIPMENT OF A KIND USED FOR SPARK-IGNITION OR COMPRESSION-IGNITION INTERNAL COMBUSTION ENGINES (FOR EXAMPLE, IGNITION MAGNETOS, MAGNETO-DYNAMOS, IGNITION COILS, SPARKING PLUGS AND GLOW PLUGS, STARTER MOTORS); GENERATORS (FOR EXAMPLE, DYNAMOS, ALTERNATORS) AND CUT-OUTS OF A KIND USED IN CONJUNCTION WITH SUCH ENGINES",
+        "hsn_code": "8511",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL IGNITION OR STARTING EQUIPMENT OF A KIND USED FOR SPARK-IGNITION OR\nCOMPRESSION-IGNITION INTERNAL COMBUSTION ENGINES (FOR EXAMPLE, IGNITION\nMAGNETOS, MAGNETO-DYNAMOS, IGNITION COILS, SPARKING PLUGS AND GLOW PLUGS,\nSTARTER MOTORS); GENERATORS (FOR EXAMPLE, DYNAMOS, ALTERNATORS) AND CUTOUTS OF\nA KIND USED IN CONJUNCTION WITH SUCH ENGINES PARTS",
+        "hsn_code": "85119000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL IGNITION OR STARTING EQUIPMENT OF A KIND USED FOR SPARK-IGNITION OR\nCOMPRESSION-IGNITION INTERNAL COMBUSTION ENGINES (FOR EXAMPLE, IGNITION\nMAGNETOS, MAGNETO-DYNAMOS, IGNITION COILS, SPARKING PLUGS AND GLOW PLUGS,\nSTARTER MOTORS); GENERATORS (FOR EXAMPLE, DYNAMOS, ALTERNATORS) AND CUTOUTS OF\nA KIND USED IN CONJUNCTION WITH SUCH ENGINES OTHER EQUIPMENT",
+        "hsn_code": "85118000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL IGNITION OR STARTING EQUIPMENT OF A KIND USED FOR SPARK-IGNITION OR\nCOMPRESSION-IGNITION INTERNAL COMBUSTION ENGINES (FOR EXAMPLE, IGNITION\nMAGNETOS, MAGNETO-DYNAMOS, IGNITION COILS, SPARKING PLUGS AND GLOW PLUGS,\nSTARTER MOTORS); GENERATORS (FOR EXAMPLE, DYNAMOS, ALTERNATORS) AND CUTOUTS OF\nA KIND USED IN CONJUNCTION WITH SUCH ENGINES OTHER GENERATORS",
+        "hsn_code": "85115000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL IGNITION OR STARTING EQUIPMENT OF A KIND USED FOR SPARK-IGNITION OR\nCOMPRESSION-IGNITION INTERNAL COMBUSTION ENGINES (FOR EXAMPLE, IGNITION\nMAGNETOS, MAGNETO-DYNAMOS, IGNITION COILS, SPARKING PLUGS AND GLOW PLUGS,\nSTARTER MOTORS); GENERATORS (FOR EXAMPLE, DYNAMOS, ALTERNATORS) AND CUTOUTS OF\nA KIND USED IN CONJUNCTION WITH SUCH ENGINES STARTER MOTORS AND DUAL PURPOSE\nSTARTER- GENERATORS",
+        "hsn_code": "85114000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL IGNITION OR STARTING EQUIPMENT OF A KIND USED FOR SPARK-IGNITION OR\nCOMPRESSION-IGNITION INTERNAL COMBUSTION ENGINES (FOR EXAMPLE, IGNITION\nMAGNETOS, MAGNETO-DYNAMOS, IGNITION COILS, SPARKING PLUGS AND GLOW PLUGS,\nSTARTER MOTORS); GENERATORS (FOR EXAMPLE, DYNAMOS, ALTERNATORS) AND CUTOUTS OF\nA KIND USED IN CONJUNCTION WITH SUCH ENGINES -DISTRIBUTORS; IGNITION COILS: IGNITION\nCOILS",
+        "hsn_code": "85113020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL IGNITION OR STARTING EQUIPMENT OF A KIND USED FOR SPARK-IGNITION OR\nCOMPRESSION-IGNITION INTERNAL COMBUSTION ENGINES (FOR EXAMPLE, IGNITION\nMAGNETOS, MAGNETO-DYNAMOS, IGNITION COILS, SPARKING PLUGS AND GLOW PLUGS,\nSTARTER MOTORS); GENERATORS (FOR EXAMPLE, DYNAMOS, ALTERNATORS) AND CUTOUTS OF\nA KIND USED IN CONJUNCTION WITH SUCH ENGINES -DISTRIBUTORS; IGNITION COILS:\nDISTRIBUTORS",
+        "hsn_code": "85113010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL IGNITION OR STARTING EQUIPMENT OF A KIND USED FOR SPARK-IGNITION OR\nCOMPRESSION-IGNITION INTERNAL COMBUSTION ENGINES (FOR EXAMPLE, IGNITION\nMAGNETOS, MAGNETO-DYNAMOS, IGNITION COILS, SPARKING PLUGS AND GLOW PLUGS,\nSTARTER MOTORS); GENERATORS (FOR EXAMPLE, DYNAMOS, ALTERNATORS) AND CUTOUTS OF\nA KIND USED IN CONJUNCTION WITH SUCH ENGINES -IGNITION MAGNETOS; MAGNETO\nDYNAMOS; MAGNETIC FLYWHEELS: OTHER",
+        "hsn_code": "85112090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL IGNITION OR STARTING EQUIPMENT OF A KIND USED FOR SPARK-IGNITION OR\nCOMPRESSION-IGNITION INTERNAL COMBUSTION ENGINES (FOR EXAMPLE, IGNITION\nMAGNETOS, MAGNETO-DYNAMOS, IGNITION COILS, SPARKING PLUGS AND GLOW PLUGS,\nSTARTER MOTORS); GENERATORS (FOR EXAMPLE, DYNAMOS, ALTERNATORS) AND CUTOUTS OF\nA KIND USED IN CONJUNCTION WITH SUCH ENGINES -IGNITION MAGNETOS; MAGNETO\nDYNAMOS; MAGNETIC FLYWHEELS: ELECTRONIC IGNITION MAGNETOS",
+        "hsn_code": "85112010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL IGNITION OR STARTING EQUIPMENT OF A KIND USED FOR SPARK-IGNITION OR\nCOMPRESSION-IGNITION INTERNAL COMBUSTION ENGINES (FOR EXAMPLE, IGNITION\nMAGNETOS, MAGNETO-DYNAMOS, IGNITION COILS, SPARKING PLUGS AND GLOW PLUGS,\nSTARTER MOTORS); GENERATORS (FOR EXAMPLE, DYNAMOS, ALTERNATORS) AND CUTOUTS OF\nA KIND USED IN CONJUNCTION WITH SUCH ENGINES SPARKING PLUGS",
+        "hsn_code": "85111000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SHAVERS, HAIR CLIPPERS AND HAIR-REMOVING APPLIANCES, WITH SELF-CONTAINED ELECTRIC MOTOR",
+        "hsn_code": "8510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHAVERS, HAIR CLIPPERS AND HAIR-REMOVING APPLIANCES, WITH SELF-CONTAINED ELECTRIC\nMOTOR PARTS",
+        "hsn_code": "85109000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHAVERS, HAIR CLIPPERS AND HAIR-REMOVING APPLIANCES, WITH SELF-CONTAINED ELECTRIC\nMOTOR HAIR-REMOVING APPLIANCES",
+        "hsn_code": "85103000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHAVERS, HAIR CLIPPERS AND HAIR-REMOVING APPLIANCES, WITH SELF-CONTAINED ELECTRIC\nMOTOR HAIR CLIPPERS",
+        "hsn_code": "85102000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHAVERS, HAIR CLIPPERS AND HAIR-REMOVING APPLIANCES, WITH SELF-CONTAINED ELECTRIC\nMOTOR SHAVERS",
+        "hsn_code": "85101000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRO-MECHANICAL DOMESTIC APPLIANCES, WITH SELF-CONTAINED ELECTRIC MOTOR, OTHER THAN VACUUM CLEANERS OF HEADING 8508",
+        "hsn_code": "8509",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRO-MECHANICAL DOMESTIC APPLIANCES, WITH SELF-CONTAINED ELECTRIC MOTOR,\nOTHER THAN VACUUM CLEANERS OF HEADING 8508 - PARTS",
+        "hsn_code": "85099000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRO-MECHANICAL DOMESTIC APPLIANCES, WITH SELF-CONTAINED ELECTRIC MOTOR,\nOTHER THAN VACUUM CLEANERS OF HEADING 8508 - OTHER APPLIANCES",
+        "hsn_code": "85098000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRO-MECHANICAL DOMESTIC APPLIANCES, WITH SELF-CONTAINED ELECTRIC MOTOR,\nOTHER THAN VACUUM CLEANERS OF HEADING 8508 -FOOD GRINDERS AND MIXERS; FRUIT OR\nVEGETABLE JUICE EXTRACTORS: --- OTHER",
+        "hsn_code": "85094090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRO-MECHANICAL DOMESTIC APPLIANCES, WITH SELF-CONTAINED ELECTRIC MOTOR,\nOTHER THAN VACUUM CLEANERS OF HEADING 8508 -FOOD GRINDERS AND MIXERS; FRUIT OR\nVEGETABLE JUICE EXTRACTORS: --- FOOD GRINDERS",
+        "hsn_code": "85094010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRO-MECHANICAL DOMESTIC APPLIANCES, WITH SELF-CONTAINED ELECTRIC MOTOR\nKITCHEN WASTE DISPOSERS",
+        "hsn_code": "85093000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRO-MECHANICAL DOMESTIC APPLIANCES, WITH SELF-CONTAINED ELECTRIC MOTOR\nFLOOR POLISHERS",
+        "hsn_code": "85092000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRO-MECHANICAL DOMESTIC APPLIANCES, WITH SELF-CONTAINED ELECTRIC MOTOR\nVACUUM CLEANERS, INCLUDING DRY AND WET VACUUM CLEANERS",
+        "hsn_code": "85091000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VACCUM CLEANERS",
+        "hsn_code": "8508",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VACCUM CLEANERS - PARTS",
+        "hsn_code": "85087000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VACCUM CLEANERS - OTHER VACCUM CLEANERS",
+        "hsn_code": "85086000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VACCUM CLEANERS - WITH SELF-CONTAINED ELECTIC MOTOR: -- OTHER",
+        "hsn_code": "85081900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VACCUM CLEANERS - WITH SELF-CONTAINED ELECTIC MOTOR: -- OF A POWER NOT EXCEEDING\n1500 W AND HAVING A DUST BAG OR OTHER RECEPTACLE CAPACITY NOT EXCEEDING 20 L",
+        "hsn_code": "85081100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC ACCUMULATORS, INCLUDING SEPARATORS THEREFOR, WHETHER OR NOT RECTANGULAR (INCLUDING SQUARE)",
+        "hsn_code": "8507",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC ACCUMULATORS, INCLUDING SEPARATORS THEREFOR, WHETHER OR NOT\nRECTANGULAR (INCLUDING SQUARE) -PARTS: OTHER",
+        "hsn_code": "85079090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC ACCUMULATORS, INCLUDING SEPARATORS THEREFOR, WHETHER OR NOT\nRECTANGULAR (INCLUDING SQUARE) -PARTS: ACCUMULATOR CASES MADE OF HARD RUBBER\nAND SEPARATORS",
+        "hsn_code": "85079010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC ACCUMULATORS, INCLUDING SEPARATORS THEREFOR, WHETHER OR NOT\nRECTANGULAR (INCLUDING SQUARE) OTHER ACCUMULATORS",
+        "hsn_code": "85078000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "NICKEL-METAL HYDRIDE",
+        "hsn_code": "85075000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC ACCUMULATORS, INCLUDING SEPARATORS THEREFOR, WHETHER OR NOT\nRECTANGULAR (INCLUDING SQUARE) NICKEL-IRON",
+        "hsn_code": "85074000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC ACCUMULATORS, INCLUDING SEPARATORS THEREFOR, WHETHER OR NOT\nRECTANGULAR (INCLUDING SQUARE) NICKEL-CADMIUM",
+        "hsn_code": "85073000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC ACCUMULATORS, INCLUDING SEPARATORS THEREFOR, WHETHER OR NOT\nRECTANGULAR (INCLUDING SQUARE) OTHER LEAD-ACID ACCUMULATORS",
+        "hsn_code": "85072000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC ACCUMULATORS, INCLUDING SEPARATORS THEREFOR, WHETHER OR NOT\nRECTANGULAR (INCLUDING SQUARE) LEAD-ACID, OF A KIND USED FOR STARTING PISTON\nENGINES",
+        "hsn_code": "85071000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PRIMARY CELLS AND PRIMARY BATTERIES",
+        "hsn_code": "8506",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRIMARY CELLS AND PRIMARY BATTERIES -PARTS",
+        "hsn_code": "85069000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRIMARY CELLS AND PRIMARY BATTERIES - OTHER PRIMARY CELLS AND PRIMARY\nBATTERIES:OTHER",
+        "hsn_code": "85068090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRIMARY CELLS AND PRIMARY BATTERIES - OTHER PRIMARY CELLS AND PRIMARY\nBATTERIES:BUTTON CELLS",
+        "hsn_code": "85068010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRIMARY CELLS AND PRIMARY BATTERIES AIR-ZINC",
+        "hsn_code": "85066000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRIMARY CELLS AND PRIMARY BATTERIES LITHIUM",
+        "hsn_code": "85065000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRIMARY CELLS AND PRIMARY BATTERIES SILVER OXIDE",
+        "hsn_code": "85064000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRIMARY CELLS AND PRIMARY BATTERIES MERCURIC OXIDE",
+        "hsn_code": "85063000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRIMARY CELLS AND PRIMARY BATTERIES MANGANESE DIOXIDE",
+        "hsn_code": "85061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRO-MAGNETS; PERMANENT MAGNETS AND ARTICLES INTENDED TO BECOME PERMANENT MAGNETS AFTER MAGNETISATION; ELECTRO-MAGNETIC OR PERMANENT MAGNET CHUCKS, CLAMPS AND SIMILAR HOLDING DEVICES; ELECTRO-MAGNETIC COUPLINGS, CLUTCHES AND BRAKES; ELECTRO-MAGNETIC LIFTING HEADS",
+        "hsn_code": "8505",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRO-MAGNETS; PERMANENT MAGNETS AND ARTICLES INTENDED TO BECOME PERMANENT\nMAGNETS AFTER MAGNETISATION; ELECTRO-MAGNETIC OR PERMANENT MAGNET CHUCKS,\nCLAMPS AND SIMILAR HOLDING DEVICES; ELECTRO-MAGNETIC COUPLINGS, CLUTCHES AND\nBRAKES; ELECTRO-MAGNETIC LIFTING HEADS - OTHER, INCLUDING PARTS",
+        "hsn_code": "85059000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRO-MAGNETS; PERMANENT MAGNETS AND ARTICLES INTENDED TO BECOME PERMANENT\nMAGNETS AFTER MAGNETISATION; ELECTRO-MAGNETIC OR PERMANENT MAGNET CHUCKS,\nCLAMPS AND SIMILAR HOLDING DEVICES; ELECTRO-MAGNETIC COUPLINGS, CLUTCHES AND\nBRAKES; ELECTRO-MAGNETIC LIFTING HEADS - ELECTRO-MAGNETIC LIFTING HEADS",
+        "hsn_code": "85053000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRO-MAGNETS; PERMANENT MAGNETS AND ARTICLES INTENDED TO BECOME PERMANENT\nMAGNETS AFTER MAGNETISATION; ELECTRO-MAGNETIC OR PERMANENT MAGNET CHUCKS,\nCLAMPS AND SIMILAR HOLDING DEVICES; ELECTRO-MAGNETIC COUPLINGS, CLUTCHES AND\nBRAKES; ELECTRO-MAGNETIC LIFTING HEADS - ELECTRO-MAGNETIC COUPLINGS, CLUTCHES AND\nBRAKES",
+        "hsn_code": "85052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRO-MAGNETS; PERMANENT MAGNETS AND ARTICLES INTENDED TO BECOME PERMANENT\nMAGNETS AFTER MAGNETISATION; ELECTRO-MAGNETIC OR PERMANENT MAGNET CHUCKS,\nCLAMPS AND SIMILAR HOLDING DEVICES; ELECTRO-MAGNETIC COUPLINGS, CLUTCHES AND\nBRAKES; ELECTRO-MAGNETIC LIFTING HEADS - PERMANENT MAGNETS AND ARTICLES INTENDED\nTO BECOME PERMANENT MAGNETS AFTER MAGNETISATION: - OTHER",
+        "hsn_code": "85051900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRO-MAGNETS; PERMANENT MAGNETS AND ARTICLES INTENDED TO BECOME PERMANENT\nMAGNETS AFTER MAGNETISATION; ELECTRO-MAGNETIC OR PERMANENT MAGNET CHUCKS,\nCLAMPS AND SIMILAR HOLDING DEVICES; ELECTRO-MAGNETIC COUPLINGS, CLUTCHES AND\nBRAKES; ELECTRO-MAGNETIC LIFTING HEADS - PERMANENT MAGNETS AND ARTICLES INTENDED\nTO BECOME PERMANENT MAGNETS AFTER MAGNETISATION: - OF METAL:OTHER",
+        "hsn_code": "85051190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRO-MAGNETS; PERMANENT MAGNETS AND ARTICLES INTENDED TO BECOME PERMANENT\nMAGNETS AFTER MAGNETISATION; ELECTRO-MAGNETIC OR PERMANENT MAGNET CHUCKS,\nCLAMPS AND SIMILAR HOLDING DEVICES; ELECTRO-MAGNETIC COUPLINGS, CLUTCHES AND\nBRAKES; ELECTRO-MAGNETIC LIFTING HEADS - PERMANENT MAGNETS AND ARTICLES INTENDED\nTO BECOME PERMANENT MAGNETS AFTER MAGNETISATION: - OF METAL:FERRITE CORES",
+        "hsn_code": "85051110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS",
+        "hsn_code": "8504",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nOTHER INDUCTORS: -PARTS : OTHER",
+        "hsn_code": "85049090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nOTHER INDUCTORS: -PARTS : OF TRANSFORMERS",
+        "hsn_code": "85049010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nOTHER INDUCTORS: OTHER",
+        "hsn_code": "85045090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nOTHER INDUCTORS: CHOKE COILS (CHOKES)",
+        "hsn_code": "85045010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nSTATIC CONVERTERS: OTHER",
+        "hsn_code": "85044090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nSTATIC CONVERTERS: VOLTAGE REGULATOR AND STABILIZERS (OTHER THAN AUTOMATIC)",
+        "hsn_code": "85044040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nSTATIC CONVERTERS: BATTERY CHARGERS",
+        "hsn_code": "85044030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nSTATIC CONVERTERS: - RECTIFIER: OTHER",
+        "hsn_code": "85044029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nSTATIC CONVERTERS: - RECTIFIER: DIP BRIDGE RECTIFIER",
+        "hsn_code": "85044021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nSTATIC CONVERTERS: ELECTRIC INVERTER",
+        "hsn_code": "85044010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nOTHER TRANSFORMERS: HAVING A POWER HANDLING CAPACITY EXCEEDING 500 KVA",
+        "hsn_code": "85043400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nOTHER TRANSFORMERS: HAVING A POWER HANDLING CAPACITY EXCEEDING 16 KVA BUT NOT\nEXCEEDING 500 KVA",
+        "hsn_code": "85043300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nOTHER TRANSFORMERS: HAVING A POWER HANDLING CAPACITY EXCEEDING 1 KVA BUT NOT\nEXCEEDING 16 KVA",
+        "hsn_code": "85043200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nOTHER TRANSFORMERS: HAVING A POWER HANDLING CAPACITY NOT EXCEEDING 1 KVA",
+        "hsn_code": "85043100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nLIQUID DIELECTRIC TRANSFORMERS: - HAVING A POWER HANDLING CAPACITY EXCEEDING\n10,000 KVA: HAVING A POWER HANDLING CAPACITY EXCEEDING 2,50,000 KVA",
+        "hsn_code": "85042340",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nLIQUID DIELECTRIC TRANSFORMERS: - HAVING A POWER HANDLING CAPACITY EXCEEDING\n10,000 KVA: HAVING A POWER HANDLING CAPACITY EXCEEDING 1,00,000 KVA BUT NOT\nEXCEEDING 2,50,000 KVA",
+        "hsn_code": "85042330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nLIQUID DIELECTRIC TRANSFORMERS: - HAVING A POWER HANDLING CAPACITY EXCEEDING\n10,000 KVA: HAVING A POWER HANDLING CAPACITY EXCEEDING 50,000 KVA BUT NOT\nEXCEEDING 1,00,000 KVA",
+        "hsn_code": "85042320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nLIQUID DIELECTRIC TRANSFORMERS: - HAVING A POWER HANDLING CAPACITY EXCEEDING\n10,000 KVA: HAVING A POWER HANDLING CAPACITY EXCEEDING 10,000 KVA BUT NOT\nEXCEEDING 50,000 KVA",
+        "hsn_code": "85042310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nLIQUID DIELECTRIC TRANSFORMERS: HAVING A POWER HANDLING CAPACITY EXCEEDING 650\nKVA BUT NOT EXCEEDING 10,000 KVA",
+        "hsn_code": "85042200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND INDUCTORS -\nLIQUID DIELECTRIC TRANSFORMERS: HAVING A POWER HANDLING CAPACITY NOT EXCEEDING\n650 KVA",
+        "hsn_code": "85042100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "8504 ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND\nINDUCTORS -BALLASTS FOR DISCHARGE LAMPS OR TUBES: OTHER",
+        "hsn_code": "85041090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "8504 ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND\nINDUCTORS -BALLASTS FOR DISCHARGE LAMPS OR TUBES: FOR COMPACT FLUORESCENT LAMPS",
+        "hsn_code": "85041020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "8504 ELECTRICAL TRANSFORMERS, STATIC CONVERTERS (FOR EXAMPLE, RECTIFIERS) AND\nINDUCTORS -BALLASTS FOR DISCHARGE LAMPS OR TUBES: CONVENTIONAL TYPE",
+        "hsn_code": "85041010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF HEADING 8501 OR 8502",
+        "hsn_code": "8503",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF HEADING 8501 OR\n8502 - OTHER",
+        "hsn_code": "85030090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF HEADING 8501 OR\n8502 - PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF HEADING\n8501 OR 8502:- PARTS OF ELECTRIC MOTOR : OTHER",
+        "hsn_code": "85030029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF HEADING 8501 OR\n8502 - PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF HEADING\n8501 OR 8502:- PARTS OF ELECTRIC MOTOR : OF DC MOTOR",
+        "hsn_code": "85030021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF HEADING 8501 OR\n8502 - PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF HEADING\n8501 OR 8502:PARTS OF GENERATOR (AC OR DC)",
+        "hsn_code": "85030010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC GENERATING SETS AND ROTARY CONVERTERS",
+        "hsn_code": "8502",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC GENERATING SETS AND ROTARY CONVERTERS - ELECTRIC ROTARY CONVERTERS",
+        "hsn_code": "85024000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC GENERATING SETS AND ROTARY CONVERTERS - OTHER GENERATING SETS : - OTHER:\nOTHER",
+        "hsn_code": "85023990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC GENERATING SETS AND ROTARY CONVERTERS - OTHER GENERATING SETS : - OTHER:\nPOWERED BY WATER TURBINE",
+        "hsn_code": "85023920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC GENERATING SETS AND ROTARY CONVERTERS - OTHER GENERATING SETS : - OTHER:\nPOWERED BY STEAM ENGINE",
+        "hsn_code": "85023910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC GENERATING SETS AND ROTARY CONVERTERS - OTHER GENERATING SETS : WIND\nPOWERED",
+        "hsn_code": "85023100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC GENERATING SETS AND ROTARY CONVERTERS -GENERATING SETS WITH SPARK\nIGNITION INTERNAL COMBUSTION PISTON ENGINES:OTHER",
+        "hsn_code": "85022090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC GENERATING SETS AND ROTARY CONVERTERS -GENERATING SETS WITH SPARK\nIGNITION INTERNAL COMBUSTION PISTON ENGINES:ELECTRIC PORTABLE GENERATORS OF AN\nOUTPUT NOT EXCEEDING 3.5 KVA",
+        "hsn_code": "85022010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC GENERATING SETS AND ROTARY CONVERTERS - GENERATING SETS WITH\nCOMPRESSION-IGNITION INTERNAL COMBUSTION PISTON ENGINES (DIESEL OR SEMI-DIESEL\nENGINES): - OF AN OUTPUT EXCEEDING 375 KVA : OF AN OUTPUT EXCEEDING 10,000 KVA",
+        "hsn_code": "85021360",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC GENERATING SETS AND ROTARY CONVERTERS - GENERATING SETS WITH\nCOMPRESSION-IGNITION INTERNAL COMBUSTION PISTON ENGINES (DIESEL OR SEMI-DIESEL\nENGINES): - OF AN OUTPUT EXCEEDING 375 KVA : OF AN OUTPUT EXCEEDING 5,000 KVA BUT\nNOT EXCEEDING 10,000 KVA",
+        "hsn_code": "85021350",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC GENERATING SETS AND ROTARY CONVERTERS - GENERATING SETS WITH\nCOMPRESSION-IGNITION INTERNAL COMBUSTION PISTON ENGINES (DIESEL OR SEMI-DIESEL\nENGINES): - OF AN OUTPUT EXCEEDING 375 KVA : OF AN OUTPUT EXCEEDING 2,000 KVA BUT\nNOT EXCEEDING 5,000 KVA",
+        "hsn_code": "85021340",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC GENERATING SETS AND ROTARY CONVERTERS - GENERATING SETS WITH\nCOMPRESSION-IGNITION INTERNAL COMBUSTION PISTON ENGINES (DIESEL OR SEMI-DIESEL\nENGINES): - OF AN OUTPUT EXCEEDING 375 KVA : OF AN OUTPUT EXCEEDING 1,500 KVA BUT\nNOT EXCEEDING 2,000 KVA",
+        "hsn_code": "85021330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC GENERATING SETS AND ROTARY CONVERTERS - GENERATING SETS WITH\nCOMPRESSION-IGNITION INTERNAL COMBUSTION PISTON ENGINES (DIESEL OR SEMI-DIESEL\nENGINES): - OF AN OUTPUT EXCEEDING 375 KVA : OF AN OUTPUT EXCEEDING 1,000 KVA BUT\nNOT EXCEEDING 1,500 KVA",
+        "hsn_code": "85021320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC GENERATING SETS AND ROTARY CONVERTERS - GENERATING SETS WITH\nCOMPRESSION-IGNITION INTERNAL COMBUSTION PISTON ENGINES (DIESEL OR SEMI-DIESEL\nENGINES): - OF AN OUTPUT EXCEEDING 375 KVA : OF AN OUTPUT EXCEEDING 375 KVA BUT NOT\nEXCEEDING 1,000 KVA",
+        "hsn_code": "85021310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC GENERATING SETS AND ROTARY CONVERTERS - GENERATING SETS WITH\nCOMPRESSION-IGNITION INTERNAL COMBUSTION PISTON ENGINES (DIESEL OR SEMI-DIESEL\nENGINES): OF AN OUTPUT EXCEEDING 75 KVA BUT NOT EXCEEDING 375 KVA",
+        "hsn_code": "85021200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC GENERATING SETS AND ROTARY CONVERTERS - GENERATING SETS WITH\nCOMPRESSION-IGNITION INTERNAL COMBUSTION PISTON ENGINES (DIESEL OR SEMI-DIESEL\nENGINES): OF AN OUTPUT NOT EXCEEDING 75 KVA",
+        "hsn_code": "85021100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS)",
+        "hsn_code": "8501",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - AC GENERATORS\n(ALTERNATORS): - OF AN OUTPUT EXCEEDING 750 KVA: OF AN OUTPUT EXCEEDING 3,12,500\nKVA",
+        "hsn_code": "85016480",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - AC GENERATORS\n(ALTERNATORS): - OF AN OUTPUT EXCEEDING 750 KVA: OF AN OUTPUT EXCEEDING 1,37,500\nKVA BUT NOT EXCEEDING 3,12,500 KVA",
+        "hsn_code": "85016470",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - AC GENERATORS\n(ALTERNATORS): - OF AN OUTPUT EXCEEDING 750 KVA: OF AN OUTPUT EXCEEDING 75,000 KVA\nBUT NOT EXCEEDING 1,37,500 KVA",
+        "hsn_code": "85016460",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - AC GENERATORS\n(ALTERNATORS): - OF AN OUTPUT EXCEEDING 750 KVA: OF AN OUTPUT EXCEEDING 37,500 KVA\nBUT NOT EXCEEDING 75,000 KVA",
+        "hsn_code": "85016450",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - AC GENERATORS\n(ALTERNATORS): - OF AN OUTPUT EXCEEDING 750 KVA: OF AN OUTPUT EXCEEDING 15,000 KVA\nBUT NOT EXCEEDING 37,500 KVA",
+        "hsn_code": "85016440",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - AC GENERATORS\n(ALTERNATORS): - OF AN OUTPUT EXCEEDING 750 KVA: OF AN OUTPUT EXCEEDING 5,000 KVA\nBUT NOT EXCEEDING 15,000 KVA",
+        "hsn_code": "85016430",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - AC GENERATORS\n(ALTERNATORS): - OF AN OUTPUT EXCEEDING 750 KVA: OF AN OUTPUT EXCEEDING 2,000 KVA\nBUT NOT EXCEEDING 5,000 KVA",
+        "hsn_code": "85016420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - AC GENERATORS\n(ALTERNATORS): - OF AN OUTPUT EXCEEDING 750 KVA: OF AN OUTPUT EXCEEDING 750 KVA BUT\nNOT EXCEEDING 2,000 KVA",
+        "hsn_code": "85016410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - AC GENERATORS\n(ALTERNATORS): OF AN OUTPUT EXCEEDING 375 KVA BUT NOT EXCEEDING 750 KVA",
+        "hsn_code": "85016300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - AC GENERATORS\n(ALTERNATORS): OF AN OUTPUT EXCEEDING 75 KVA BUT NOT EXCEEDING 375 KVA",
+        "hsn_code": "85016200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - AC GENERATORS\n(ALTERNATORS): OF AN OUTPUT NOT EXCEEDING 75 KVA",
+        "hsn_code": "85016100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER AC MOTORS,\nSINGLE-PHASE:- OTHER AC MOTORS, MULTI-PHASE:-- OF AN OUTPUT EXCEEDING 75 KW: OTHER",
+        "hsn_code": "85015390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER AC MOTORS,\nSINGLE-PHASE:- OTHER AC MOTORS, MULTI-PHASE:-- OF AN OUTPUT EXCEEDING 75 KW:\nTRACTION MOTOR",
+        "hsn_code": "85015330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER AC MOTORS,\nSINGLE-PHASE:- OTHER AC MOTORS, MULTI-PHASE:-- OF AN OUTPUT EXCEEDING 75 KW:\nSLIPRING MOTOR",
+        "hsn_code": "85015320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER AC MOTORS,\nSINGLE-PHASE:- OTHER AC MOTORS, MULTI-PHASE:-- OF AN OUTPUT EXCEEDING 75 KW:\nSQUIRREL CAGE INDUCTION MOTOR, 3 PHASE TYPE",
+        "hsn_code": "85015310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER AC MOTORS,\nSINGLE-PHASE:- OTHER AC MOTORS, MULTI-PHASE:- OF AN OUTPUT EXCEEDING 750 W BUT NOT\nEXCEEDING 75 KW: OTHER",
+        "hsn_code": "85015290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER AC MOTORS,\nSINGLE-PHASE:- OTHER AC MOTORS, MULTI-PHASE:- OF AN OUTPUT EXCEEDING 750 W BUT NOT\nEXCEEDING 75 KW: SLIPRING MOTOR",
+        "hsn_code": "85015220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER AC MOTORS,\nSINGLE-PHASE:- OTHER AC MOTORS, MULTI-PHASE:- OF AN OUTPUT EXCEEDING 750 W BUT NOT\nEXCEEDING 75 KW: SQUIRREL CAGE INDUCTION MOTOR, 3 PHASE TYPE",
+        "hsn_code": "85015210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER AC MOTORS,\nSINGLE-PHASE:- OTHER AC MOTORS, MULTI-PHASE: - OF AN OUTPUT NOT EXCEEDING 750 W:\nOTHER",
+        "hsn_code": "85015190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER AC MOTORS,\nSINGLE-PHASE:- OTHER AC MOTORS, MULTI-PHASE: - OF AN OUTPUT NOT EXCEEDING 750 W:\nSLIPRING MOTOR",
+        "hsn_code": "85015120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER AC MOTORS,\nSINGLE-PHASE:- OTHER AC MOTORS, MULTI-PHASE: - OF AN OUTPUT NOT EXCEEDING 750 W:\nSQUIRREL CAGE INDUCTION MOTOR, 3 PHASE TYPE",
+        "hsn_code": "85015110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER AC MOTORS,\nSINGLE-PHASE:THER",
+        "hsn_code": "85014090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER AC MOTORS,\nSINGLE-PHASE:FRACTIONAL HORSE POWER MOTOR",
+        "hsn_code": "85014010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER DC MOTORS; DC\nGENERATORS:- OF AN OUTPUT EXCEEDING 375 KW: OF AN OUTPUT EXCEEDING 10,000 KW",
+        "hsn_code": "85013450",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER DC MOTORS; DC\nGENERATORS:- OF AN OUTPUT EXCEEDING 375 KW:OF AN OUTPUT EXCEEDING 5,000 KW BUT\nNOT EXCEEDING 10,000 KW",
+        "hsn_code": "85013440",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER DC MOTORS; DC\nGENERATORS:- OF AN OUTPUT EXCEEDING 375 KW:OF AN OUTPUT EXCEEDING 2,000 KW BUT\nNOT EXCEEDING 5,000 KW",
+        "hsn_code": "85013430",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER DC MOTORS; DC\nGENERATORS:- OF AN OUTPUT EXCEEDING 375 KW:OF AN OUTPUT EXCEEDING 1,000 KW BUT\nNOT EXCEEDING 2,000 KW",
+        "hsn_code": "85013420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER DC MOTORS; DC\nGENERATORS:- OF AN OUTPUT EXCEEDING 375 KW:OF AN OUTPUT EXCEEDING 375 KW BUT NOT\nEXCEEDING 1,000 KW",
+        "hsn_code": "85013410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER DC MOTORS; DC\nGENERATORS:- OF AN OUTPUT EXCEEDING 75 KW BUT NOT EXCEEDING 375 KW: DC\nGENERATORS",
+        "hsn_code": "85013320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER DC MOTORS; DC\nGENERATORS:- OF AN OUTPUT EXCEEDING 75 KW BUT NOT EXCEEDING 375 KW: DC MOTORS",
+        "hsn_code": "85013310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER DC MOTORS; DC\nGENERATORS: - OF AN OUTPUT EXCEEDING 750 W BUT NOT EXCEEDING 75 KW: DC\nGENERATORS",
+        "hsn_code": "85013220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER DC MOTORS; DC\nGENERATORS: - OF AN OUTPUT EXCEEDING 750 W BUT NOT EXCEEDING 75 KW: DC MOTOR",
+        "hsn_code": "85013210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER DC MOTORS; DC\nGENERATORS: - OF AN OUTPUT NOT EXCEEDING 750 W: DC GENERATORS",
+        "hsn_code": "85013120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER DC MOTORS; DC\nGENERATORS: - OF AN OUTPUT NOT EXCEEDING 750 W: - DC MOTORS: OTHER",
+        "hsn_code": "85013119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER DC MOTORS; DC\nGENERATORS: - OF AN OUTPUT NOT EXCEEDING 750 W: - DC MOTORS: WIPER MOTOR",
+        "hsn_code": "85013113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER DC MOTORS; DC\nGENERATORS: - OF AN OUTPUT NOT EXCEEDING 750 W: - DC MOTORS: STEPPER MOTOR",
+        "hsn_code": "85013112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) - OTHER DC MOTORS; DC\nGENERATORS: - OF AN OUTPUT NOT EXCEEDING 750 W: - DC MOTORS: MICRO MOTOR",
+        "hsn_code": "85013111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) UNIVERSAL AC OR DC\nMOTORS OF AN OUTPUT EXCEEDING 37.5 W",
+        "hsn_code": "85012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) MOTORS OF AN OUTPUT\nNOT EXCEEDING 37.5 W:AC MOTOR",
+        "hsn_code": "85011020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) MOTORS OF AN OUTPUT\nNOT EXCEEDING 37.5 W: - DC MOTOR: OTHER",
+        "hsn_code": "85011019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) MOTORS OF AN OUTPUT\nNOT EXCEEDING 37.5 W: - DC MOTOR: WIPER MOTOR",
+        "hsn_code": "85011013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) MOTORS OF AN OUTPUT\nNOT EXCEEDING 37.5 W: - DC MOTOR: STEPPER MOTOR",
+        "hsn_code": "85011012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ELECTRIC MOTORS AND GENERATORS (EXCLUDING GENERATING SETS) MOTORS OF AN OUTPUT\nNOT EXCEEDING 37.5 W: - DC MOTOR: MICRO MOTOR",
+        "hsn_code": "85011011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY PARTS, NOT CONTAINING ELECTRICAL CONNECTORS,INSULATORS,COILS, CONTACTS\nOR OTHER ELECTRICAL FEATURES NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER; -\nSHIPS OR BOATS PROPELLERS",
+        "hsn_code": "84871000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY PARTS, NOT CONTAINING ELECTRICAL CONNECTORS,INSULATORS,COILS, CONTACTS\nOR OTHER ELECTRICAL FEATURES NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER;-\nOTHER",
+        "hsn_code": "84879000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE PARTS, NOT CONTAINING ELECTRICAL CONNECTORS, INSULATORS, COILS, CONTACTS OR OTHER ELECTRICAL FEATURES, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER",
+        "hsn_code": "8487",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND APPARUTUS OF A KIND USED SOLELY OR PRINCIPALLY FOR THE MANUFACTURE\nOF SEMI CONDUCTOR BOULES OR WAFERS,SEMI CONDUCTOR DEVICES,ELECTRONIC\nINTEGRATED CIRCUITS OR FLAT PANEL DISPLAYS; MACHINES AND APPARUTUS SPECIFIED IN\nNOTE 9(C) TO THIS CHAPTER ; PARTS AND ACCESSORIES - MACHINES AND APPARUTUS FOR THE\nMANUFACTURE OF BOULES OR WAFERS",
+        "hsn_code": "84861000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND APPARUTUS OF A KIND USED SOLELY OR PRINCIPALLY FOR THE MANUFACTURE\nOF SEMI CONDUCTOR BOULES OR WAFERS,SEMI CONDUCTOR DEVICES,ELECTRONIC\nINTEGRATED CIRCUITS OR FLAT PANEL DISPLAYS; MACHINES AND APPARUTUS SPECIFIED IN\nNOTE 9(C) TO THIS CHAPTER ; PARTS AND ACCESSORIES - MACHINES AND APPARUTUS FOR THE\nMANUFACTURE OF SEMI CONDUCTOR DEVICES OR OF ELECTRONIC INTEGRATED CIRCUITS",
+        "hsn_code": "84862000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND APPARUTUS OF A KIND USED SOLELY OR PRINCIPALLY FOR THE MANUFACTURE\nOF SEMI CONDUCTOR BOULES OR WAFERS,SEMI CONDUCTOR DEVICES,ELECTRONIC\nINTEGRATED CIRCUITS OR FLAT PANEL DISPLAYS; MACHINES AND APPARUTUS SPECIFIED IN\nNOTE 9(C) TO THIS CHAPTER ; PARTS AND ACCESSORIES - MACHINES AND APPARUTUS FOR THE\nMANUFACTURE OF FLAT PANEL DISPLAYS",
+        "hsn_code": "84863000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND APPARUTUS OF A KIND USED SOLELY OR PRINCIPALLY FOR THE MANUFACTURE\nOF SEMI CONDUCTOR BOULES OR WAFERS,SEMI CONDUCTOR DEVICES,ELECTRONIC\nINTEGRATED CIRCUITS OR FLAT PANEL DISPLAYS; MACHINES AND APPARUTUS SPECIFIED IN\nNOTE 9(C) TO THIS CHAPTER ; PARTS AND ACCESSORIES - MACHINES AND APPARUTUS\nSPECIFIED IN NOTE 9(C) TO THIS CHAPTER",
+        "hsn_code": "84864000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND APPARUTUS OF A KIND USED SOLELY OR PRINCIPALLY FOR THE MANUFACTURE\nOF SEMI CONDUCTOR BOULES OR WAFERS,SEMI CONDUCTOR DEVICES,ELECTRONIC\nINTEGRATED CIRCUITS OR FLAT PANEL DISPLAYS; MACHINES AND APPARUTUS SPECIFIED IN\nNOTE 9(C) TO THIS CHAPTER ; PARTS AND ACCESSORIES - PARTS AND ACCESSORIES",
+        "hsn_code": "84869000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND APPARATUS OF A KIND USED SOLELY OR PRINCIPALLY FOR THE MANUFACTURE OF SEMICONDUCTOR BOULES OR WAFERS, SEMICONDUCTOR DEVICES, ELECTRONIC INTEGRATED CIRCUITS OR FLAT PANEL DISPLAYS; MACHINES AND APPARATUS SPECIFIED IN NOTE 9 (C) TO THIS CHAPTER; PARTS AND  ACCESSORIES",
+        "hsn_code": "8486",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY PARTS, NOT CONTAINING ELECTRICAL CONNECTORS, INSULATORS, COILS,\nCONTACTS OR OTHER ELECTRICAL FEATURES, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS\nCHAPTER SHIPR OR BOATR PROPELLERS AND BLADES THEREFOR",
+        "hsn_code": "84851000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY PARTS, NOT CONTAINING ELECTRICAL CONNECTORS, INSULATORS, COILS,\nCONTACTS OR OTHER ELECTRICAL FEATURES, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS\nCHAPTER - OTHER",
+        "hsn_code": "84859000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GASKETS AND SIMILAR JOINTS OF METAL SHEETING COMBINED WITH OTHER MATERIAL OR OF\nTWO OR MORE LAYERS OF METAL; SETS OR ASSORTMENTS OF GASKETS AND SIMILAR JOINTS,\nDISSIMILAR IN COMPOSITION, PUT UP IN POUCHES, ENVELOPES OR SIMILAR PACKINGS;\nMECHANICAL SEALS - GASKETS AND SIMILAR JOINTS OF METAL SHEETING COMBINED WITH\nOTHER MATERIAL OR OF TWO OR MORE LAYERS OF METAL: ASBESTOS METALLIC PACKINGS AND\nGASKETS (EXCLUDING GASKETS OF ASBESTOS BOARD REINFORCED WITH METAL GAUZE OR\nWIRE)",
+        "hsn_code": "84841010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GASKETS AND SIMILAR JOINTS OF METAL SHEETING COMBINED WITH OTHER MATERIAL OR OF\nTWO OR MORE LAYERS OF METAL; SETS OR ASSORTMENTS OF GASKETS AND SIMILAR JOINTS,\nDISSIMILAR IN COMPOSITION, PUT UP IN POUCHES, ENVELOPES OR SIMILAR PACKINGS;\nMECHANICAL SEALS - GASKETS AND SIMILAR JOINTS OF METAL SHEETING COMBINED WITH\nOTHER MATERIAL OR OF TWO OR MORE LAYERS OF METAL: OTHER",
+        "hsn_code": "84841090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GASKETS AND SIMILAR JOINTS OF METAL SHEETING COMBINED WITH OTHER MATERIAL OR OF\nTWO OR MORE LAYERS OF METAL; SETS OR ASSORTMENTS OF GASKETS AND SIMILAR JOINTS,\nDISSIMILAR IN COMPOSITION, PUT UP IN POUCHES, ENVELOPES OR SIMILAR PACKINGS;\nMECHANICAL SEALS - MECHANICAL SEALS",
+        "hsn_code": "84842000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GASKETS AND SIMILAR JOINTS OF METAL SHEETING COMBINED WITH OTHER MATERIAL OR OF\nTWO OR MORE LAYERS OF METAL; SETS OR ASSORTMENTS OF GASKETS AND SIMILAR JOINTS,\nDISSIMILAR IN COMPOSITION, PUT UP IN POUCHES, ENVELOPES OR SIMILAR PACKINGS;\nMECHANICAL SEALS - OTHER",
+        "hsn_code": "84849000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GASKETS AND SIMILAR JOINTS OF METAL SHEETING COMBINED WITH OTHER MATERIAL OR OF TWO OR MORE LAYERS OF METAL; SETS OR ASSORTMENTS OF GASKETS AND SIMILAR JOINTS, DISSIMILAR IN COMPOSITION, PUT UP IN POUCHES, ENVELOPES OR SIMILAR PACKINGS; MECHANICAL SEALS",
+        "hsn_code": "8484",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION SHAFTS (INCLUDING CAM SHAFTS AND CRANK SHAFTS) AND CRANKS; BEARING\nHOUSINGS AND PLAIN SHAFT BEARINGS; GEARS AND GEARING; BALL OR ROLLER SCREWS; GEAR\nBOXES AND OTHER SPEED CHANGERS, INCLUDING TORQUE CONVERTERS; FLYWHEELS AND\nPULLEYS, INCLUDING PULLEY BLOCKS; CLUTCHES AND SHAFT COUPLINGS (INCLUDING\nUNIVERSAL JOINTS) - TRANSMISSION SHAFTS (INCLUDING CAM SHAFTS AND CRANK SHAFTS)\nAND CRANKS : CRANK SHAFTS FOR SEWING MACHINES",
+        "hsn_code": "84831010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION SHAFTS (INCLUDING CAM SHAFTS AND CRANK SHAFTS) AND CRANKS; BEARING\nHOUSINGS AND PLAIN SHAFT BEARINGS; GEARS AND GEARING; BALL OR ROLLER SCREWS; GEAR\nBOXES AND OTHER SPEED CHANGERS, INCLUDING TORQUE CONVERTERS; FLYWHEELS AND\nPULLEYS, INCLUDING PULLEY BLOCKS; CLUTCHES AND SHAFT COUPLINGS (INCLUDING\nUNIVERSAL JOINTS) - OTHER : CRANK SHAFT FOR ENGINES OF HEADING 8407",
+        "hsn_code": "84831091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION SHAFTS (INCLUDING CAM SHAFTS AND CRANK SHAFTS) AND CRANKS; BEARING\nHOUSINGS AND PLAIN SHAFT BEARINGS; GEARS AND GEARING; BALL OR ROLLER SCREWS; GEAR\nBOXES AND OTHER SPEED CHANGERS, INCLUDING TORQUE CONVERTERS; FLYWHEELS AND\nPULLEYS, INCLUDING PULLEY BLOCKS; CLUTCHES AND SHAFT COUPLINGS (INCLUDING\nUNIVERSAL JOINTS) - OTHER : CRANK SHAFT FOR ENGINES OF HEADING 8408",
+        "hsn_code": "84831092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION SHAFTS (INCLUDING CAM SHAFTS AND CRANK SHAFTS) AND CRANKS; BEARING\nHOUSINGS AND PLAIN SHAFT BEARINGS; GEARS AND GEARING; BALL OR ROLLER SCREWS; GEAR\nBOXES AND OTHER SPEED CHANGERS, INCLUDING TORQUE CONVERTERS; FLYWHEELS AND\nPULLEYS, INCLUDING PULLEY BLOCKS; CLUTCHES AND SHAFT COUPLINGS (INCLUDING\nUNIVERSAL JOINTS) - OTHER : OTHER",
+        "hsn_code": "84831099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION SHAFTS (INCLUDING CAM SHAFTS AND CRANK SHAFTS) AND CRANKS; BEARING\nHOUSINGS AND PLAIN SHAFT BEARINGS; GEARS AND GEARING; BALL OR ROLLER SCREWS; GEAR\nBOXES AND OTHER SPEED CHANGERS, INCLUDING TORQUE CONVERTERS; FLYWHEELS AND\nPULLEYS, INCLUDING PULLEY BLOCKS; CLUTCHES AND SHAFT COUPLINGS (INCLUDING\nUNIVERSAL JOINTS) -BEARING HOUSINGS, INCORPORATING BALL OR ROLLER BEARINGS",
+        "hsn_code": "84832000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION SHAFTS (INCLUDING CAM SHAFTS AND CRANK SHAFTS) AND CRANKS; BEARING\nHOUSINGS AND PLAIN SHAFT BEARINGS; GEARS AND GEARING; BALL OR ROLLER SCREWS; GEAR\nBOXES AND OTHER SPEED CHANGERS, INCLUDING TORQUE CONVERTERS; FLYWHEELS AND\nPULLEYS, INCLUDING PULLEY BLOCKS; CLUTCHES AND SHAFT COUPLINGS (INCLUDING\nUNIVERSAL JOINTS) -BEARING HOUSINGS, NOT INCORPORATING BALL OR ROLLER BEARINGS;\nPLAIN SHAFT BEARINGS",
+        "hsn_code": "84833000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION SHAFTS (INCLUDING CAM SHAFTS AND CRANK SHAFTS) AND CRANKS; BEARING\nHOUSINGS AND PLAIN SHAFT BEARINGS; GEARS AND GEARING; BALL OR ROLLER SCREWS; GEAR\nBOXES AND OTHER SPEED CHANGERS, INCLUDING TORQUE CONVERTERS; FLYWHEELS AND\nPULLEYS, INCLUDING PULLEY BLOCKS; CLUTCHES AND SHAFT COUPLINGS (INCLUDING\nUNIVERSAL JOINTS) -GEARS AND GEARING, OTHER THAN TOOTHED WHEELS, CHAIN SPROCKETS\nAND OTHER TRANSMISSION ELEMENTS PRESENTED SEPARATELY; BALL OR ROLLER SCREWS;\nGEAR BOXES AND OTHER SPEED CHANGERS, INCLUDING TORQUE CONVERTERS",
+        "hsn_code": "84834000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION SHAFTS (INCLUDING CAM SHAFTS AND CRANK SHAFTS) AND CRANKS; BEARING\nHOUSINGS AND PLAIN SHAFT BEARINGS; GEARS AND GEARING; BALL OR ROLLER SCREWS; GEAR\nBOXES AND OTHER SPEED CHANGERS, INCLUDING TORQUE CONVERTERS; FLYWHEELS AND\nPULLEYS, INCLUDING PULLEY BLOCKS; CLUTCHES AND SHAFT COUPLINGS (INCLUDING\nUNIVERSAL JOINTS) - FLYWHEELS AND PULLEYS, INCLUDING PULLEY BLOCKS: PULLEYS, POWER\nTRANSMISSION",
+        "hsn_code": "84835010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION SHAFTS (INCLUDING CAM SHAFTS AND CRANK SHAFTS) AND CRANKS; BEARING\nHOUSINGS AND PLAIN SHAFT BEARINGS; GEARS AND GEARING; BALL OR ROLLER SCREWS; GEAR\nBOXES AND OTHER SPEED CHANGERS, INCLUDING TORQUE CONVERTERS; FLYWHEELS AND\nPULLEYS, INCLUDING PULLEY BLOCKS; CLUTCHES AND SHAFT COUPLINGS (INCLUDING\nUNIVERSAL JOINTS) - FLYWHEELS AND PULLEYS, INCLUDING PULLEY BLOCKS: OTHER",
+        "hsn_code": "84835090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION SHAFTS (INCLUDING CAM SHAFTS AND CRANK SHAFTS) AND CRANKS; BEARING\nHOUSINGS AND PLAIN SHAFT BEARINGS; GEARS AND GEARING; BALL OR ROLLER SCREWS; GEAR\nBOXES AND OTHER SPEED CHANGERS, INCLUDING TORQUE CONVERTERS; FLYWHEELS AND\nPULLEYS, INCLUDING PULLEY BLOCKS; CLUTCHES AND SHAFT COUPLINGS (INCLUDING\nUNIVERSAL JOINTS) ? CLUTCHES AND SHAFT COUPLINGS (INCLUDING UNIVERSAL JOINTS):\nFLEXIBLE COUPLING",
+        "hsn_code": "84836010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION SHAFTS (INCLUDING CAM SHAFTS AND CRANK SHAFTS) AND CRANKS; BEARING\nHOUSINGS AND PLAIN SHAFT BEARINGS; GEARS AND GEARING; BALL OR ROLLER SCREWS; GEAR\nBOXES AND OTHER SPEED CHANGERS, INCLUDING TORQUE CONVERTERS; FLYWHEELS AND\nPULLEYS, INCLUDING PULLEY BLOCKS; CLUTCHES AND SHAFT COUPLINGS (INCLUDING\nUNIVERSAL JOINTS) ? CLUTCHES AND SHAFT COUPLINGS (INCLUDING UNIVERSAL JOINTS): FLUID\nCOUPLING",
+        "hsn_code": "84836020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION SHAFTS (INCLUDING CAM SHAFTS AND CRANK SHAFTS) AND CRANKS; BEARING\nHOUSINGS AND PLAIN SHAFT BEARINGS; GEARS AND GEARING; BALL OR ROLLER SCREWS; GEAR\nBOXES AND OTHER SPEED CHANGERS, INCLUDING TORQUE CONVERTERS; FLYWHEELS AND\nPULLEYS, INCLUDING PULLEY BLOCKS; CLUTCHES AND SHAFT COUPLINGS (INCLUDING\nUNIVERSAL JOINTS) ? CLUTCHES AND SHAFT COUPLINGS (INCLUDING UNIVERSAL JOINTS):\nOTHER",
+        "hsn_code": "84836090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION SHAFTS (INCLUDING CAM SHAFTS AND CRANK SHAFTS) AND CRANKS; BEARING\nHOUSINGS AND PLAIN SHAFT BEARINGS; GEARS AND GEARING; BALL OR ROLLER SCREWS; GEAR\nBOXES AND OTHER SPEED CHANGERS, INCLUDING TORQUE CONVERTERS; FLYWHEELS AND\nPULLEYS, INCLUDING PULLEY BLOCKS; CLUTCHES AND SHAFT COUPLINGS (INCLUDING\nUNIVERSAL JOINTS) - TOOTHED WHEELS, CHAIN SPROCKETS AND OTHER TRANSMISSION\nELEMENTS PARTS PRESENTED SEPARATELY",
+        "hsn_code": "84839000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION SHAFTS (INCLUDING CAM SHAFTS AND CRANK SHAFTS) AND CRANKS; BEARINGS HOUSINGS AND PLAIN SHAFT BEARINGS; GEARS AND GEARING; BALL OR ROLLER SCREWS; GEAR BOXES AND OTHER SPEED CHANGERS, INCLUDING TORQUE CONVERTERS; FLYWHEELS AND PULLEYS, INCLUDING PULLEY BLOCKS; CLUTCHES AND SHAFT COUPLINGS (INCLUDING UNIVERSAL JOINTS)",
+        "hsn_code": "8483",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - BALL BEARINGS: ADAPTER BALL BEARINGS (RADIAL TYPE) :NOT\nEXCEEDING 50MM OF BORE DIAMETER",
+        "hsn_code": "84821011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - BALL BEARINGS: ADAPTER BALL BEARINGS (RADIAL TYPE) :OF BORE\nDIAMETER EXCEEDING 50 MM BUT NOT EXCEEDING 100 MM",
+        "hsn_code": "84821012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - BALL BEARINGS: ADAPTER BALL BEARINGS (RADIAL TYPE) :OF BORE\nDIAMETER EXCEEDING 100 MM",
+        "hsn_code": "84821013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - BALL BEARINGS:OTHER BALL BEARING (RADIAL TYPE) OF BORE\nDIAMETER NOT EXCEEDING 50 MM",
+        "hsn_code": "84821020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - BALL BEARINGS - OTHER BALL BEARING (RADIAL TYPE) OF BORE\nDIAMETER EXCEEDING 50 MM BUT NOT EXCEEDING 100 MM",
+        "hsn_code": "84821030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - BALL BEARINGS OF BORE DIAMETER EXCEEDING 100 MM",
+        "hsn_code": "84821040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - BALL BEARINGS - THRUST BALL BEARINGS :OF BORE DIAMETER NOT\nEXCEEDING 50 MM",
+        "hsn_code": "84821051",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - BALL BEARINGS - THRUST BALL BEARINGS :OF BORE DIAMETER\nEXCEEDING 50 MM BUT NOT EXCEEDING 100 MM",
+        "hsn_code": "84821052",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - BALL BEARINGS - THRUST BALL BEARINGS :OF BORE DIAMETER\nEXCEEDING 100 MM",
+        "hsn_code": "84821053",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - BALL BEARINGS - THRUST BALL BEARINGS :OTHER",
+        "hsn_code": "84821090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - TAPERED ROLLER BEARINGS, INCLUDING CONE AND TAPERED\nROLLER ASSEMBLIES: TAPERED ROLLER BEARINGS (RADIAL TYPE) : OF BORE DIAMETER NOT\nEXCEEDING 50 MM",
+        "hsn_code": "84822011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - TAPERED ROLLER BEARINGS, INCLUDING CONE AND TAPERED\nROLLER ASSEMBLIES: TAPERED ROLLER BEARINGS (RADIAL TYPE) : OF BORE DIAMETER\nEXCEEDING 50 MM BUT NOT EXCEEDING 100 MM",
+        "hsn_code": "84822012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - TAPERED ROLLER BEARINGS, INCLUDING CONE AND TAPERED\nROLLER ASSEMBLIES: TAPERED ROLLER BEARINGS (RADIAL TYPE) : OF BORE DIAMETER\nEXCEEDING 100 MM",
+        "hsn_code": "84822013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - TAPERED ROLLER BEARINGS, INCLUDING CONE AND TAPERED\nROLLER ASSEMBLIES: OTHER",
+        "hsn_code": "84822090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS SPHERICAL ROLLER BEARINGS",
+        "hsn_code": "84823000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS NEEDLE ROLLER BEARINGS",
+        "hsn_code": "84824000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - OTHER CYLINDRICAL ROLLER BEARINGS: RADIAL TYPE : OF BORE\nDIAMETER NOT EXCEEDING 50 MM",
+        "hsn_code": "84825011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - OTHER CYLINDRICAL ROLLER BEARINGS: RADIAL TYPE : OF BORE\nDIAMETER EXCEEDING 50 MM BUT NOT EXCEEDING 100 MM",
+        "hsn_code": "84825012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - OTHER CYLINDRICAL ROLLER BEARINGS: RADIAL TYPE : OF BORE\nDIAMETER EXCEEDING 100 MM",
+        "hsn_code": "84825013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - OTHER CYLINDRICAL ROLLER BEARINGS:- THRUST ROLLER\nBEARINGS: OF BORE DIAMETER NOT EXCEEDING 50 MM",
+        "hsn_code": "84825021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - OTHER CYLINDRICAL ROLLER BEARINGS:- THRUST ROLLER\nBEARINGS: OF BORE DIAMETER EXCEEDING 50 MM BUT NOT EXCEEDING 100 MM",
+        "hsn_code": "84825022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - OTHER CYLINDRICAL ROLLER BEARINGS:- THRUST ROLLER\nBEARINGS: OF BORE DIAMETER EXCEEDING 100 MM",
+        "hsn_code": "84825023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - OTHER, INCLUDING COMBINED BALL OR ROLLER BEARINGS",
+        "hsn_code": "84828000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - PARTS: BALLS, NEEDLES AND ROLLERS: BALLS: OF NICKEL ALLOYS",
+        "hsn_code": "84829111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - PARTS: BALLS, NEEDLES AND ROLLERS: BALLS: OF TUNGSTEN\nCARBIDE",
+        "hsn_code": "84829112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - PARTS: BALLS, NEEDLES AND ROLLERS: BALLS: OF SPECIAL\nSTAINLESS STEEL",
+        "hsn_code": "84829113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - PARTS: BALLS, NEEDLES AND ROLLERS: BALLS: OF HIGH SPEED STEEL",
+        "hsn_code": "84829114",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - PARTS: BALLS, NEEDLES AND ROLLERS: BALLS: OTHER",
+        "hsn_code": "84829119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - PARTS NEEDLES",
+        "hsn_code": "84829120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - PARTS ROLLERS",
+        "hsn_code": "84829130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS - PARTS OTHER",
+        "hsn_code": "84829900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BALL OR ROLLER BEARINGS",
+        "hsn_code": "8482",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TAPS, COCKS, VALVES AND SIMILAR APPLIANCES FOR PIPES, BOILER SHELLS, TANKS, VATS OR THE\nLIKE, INCLUDING PRESSURE-REDUCING VALVES AND THERMOSTATICALLY CONTROLLED VALVES\nPRESSURE-REDUCING VALVES",
+        "hsn_code": "84811000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TAPS, COCKS, VALVES AND SIMILAR APPLIANCES FOR PIPES, BOILER SHELLS, TANKS, VATS OR THE\nLIKE, INCLUDING PRESSURE-REDUCING VALVES AND THERMOSTATICALLY CONTROLLED VALVES\nVALVES FOR OLEOHYDRAULIC OR PNEUMATIC TRANSMISSIONS",
+        "hsn_code": "84812000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TAPS, COCKS, VALVES AND SIMILAR APPLIANCES FOR PIPES, BOILER SHELLS, TANKS, VATS OR THE\nLIKE, INCLUDING PRESSURE-REDUCING VALVES AND THERMOSTATICALLY CONTROLLED VALVES\nCHECK (NON-RETURN) VALVES",
+        "hsn_code": "84813000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TAPS, COCKS, VALVES AND SIMILAR APPLIANCES FOR PIPES, BOILER SHELLS, TANKS, VATS OR THE\nLIKE, INCLUDING PRESSURE-REDUCING VALVES AND THERMOSTATICALLY CONTROLLED VALVES\nSAFETY OR RELIEF VALVES",
+        "hsn_code": "84814000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TAPS, COCKS, VALVES AND SIMILAR APPLIANCES FOR PIPES, BOILER SHELLS, TANKS, VATS OR THE\nLIKE, INCLUDING PRESSURE-REDUCING VALVES AND THERMOSTATICALLY CONTROLLED VALVES -\nOTHER APPLIANCES: TAPS, COCKS AND SIMILAR APPLIANCES OF IRON OR STEEL",
+        "hsn_code": "84818010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TAPS, COCKS, VALVES AND SIMILAR APPLIANCES FOR PIPES, BOILER SHELLS, TANKS, VATS OR THE\nLIKE, INCLUDING PRESSURE-REDUCING VALVES AND THERMOSTATICALLY CONTROLLED VALVES -\nOTHER APPLIANCES: TAPS, COCKS AND SIMILAR APPLIANCES OF NON-FERROUS METAL",
+        "hsn_code": "84818020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TAPS, COCKS, VALVES AND SIMILAR APPLIANCES FOR PIPES, BOILER SHELLS, TANKS, VATS OR THE\nLIKE, INCLUDING PRESSURE-REDUCING VALVES AND THERMOSTATICALLY CONTROLLED VALVES -\nOTHER APPLIANCES: INDUSTRIAL VALVES (EXCLUDING PRESSURE-REDUCING VALVES, AND\nTHERMOSTATICALLY CONTROLLED VALVES)",
+        "hsn_code": "84818030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TAPS, COCKS, VALVES AND SIMILAR APPLIANCES FOR PIPES, BOILER SHELLS, TANKS, VATS OR THE\nLIKE, INCLUDING PRESSURE-REDUCING VALVES AND THERMOSTATICALLY CONTROLLED VALVES -\nOTHER APPLIANCES: INNER TUBE VALVES: FOR BICYCLES",
+        "hsn_code": "84818041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TAPS, COCKS, VALVES AND SIMILAR APPLIANCES FOR PIPES, BOILER SHELLS, TANKS, VATS OR THE\nLIKE, INCLUDING PRESSURE-REDUCING VALVES AND THERMOSTATICALLY CONTROLLED VALVES -\nOTHER APPLIANCES: INNER TUBE VALVES: OTHER",
+        "hsn_code": "84818049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TAPS, COCKS, VALVES AND SIMILAR APPLIANCES FOR PIPES, BOILER SHELLS, TANKS, VATS OR THE\nLIKE, INCLUDING PRESSURE-REDUCING VALVES AND THERMOSTATICALLY CONTROLLED VALVES -\nOTHER APPLIANCES: EXPANSION VALVES AND SOLENOID VALVES FOR REFRIGERATING AND AIR\nCONDITIONING APPLIANCES AND MACHINERY",
+        "hsn_code": "84818050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TAPS, COCKS, VALVES AND SIMILAR APPLIANCES FOR PIPES, BOILER SHELLS, TANKS, VATS OR THE\nLIKE, INCLUDING PRESSURE-REDUCING VALVES AND THERMOSTATICALLY CONTROLLED VALVES -\nOTHER APPLIANCES: OTHER",
+        "hsn_code": "84818090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TAPS, COCKS, VALVES AND SIMILAR APPLIANCES FOR PIPES, BOILER SHELLS, TANKS, VATS OR THE\nLIKE, INCLUDING PRESSURE-REDUCING VALVES AND THERMOSTATICALLY CONTROLLED VALVES -\nPARTS : BICYCLES VALVES",
+        "hsn_code": "84819010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TAPS, COCKS, VALVES AND SIMILAR APPLIANCES FOR PIPES, BOILER SHELLS, TANKS, VATS OR THE\nLIKE, INCLUDING PRESSURE-REDUCING VALVES AND THERMOSTATICALLY CONTROLLED VALVES -\nPARTS : OTHER",
+        "hsn_code": "84819090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TAPS, COCKS, VALVES AND SIMILAR APPLIANCES FOR PIPES, BOILER SHELLS, TANKS, VATS OR THE LIKE, INCLUDING PRESSURE-REDUCING VALVES AND THERMOSTATICALLY CONTROLLED VALVES",
+        "hsn_code": "8481",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MOULDING BOXES FOR METAL FOUNDRY; MOULD BASES; MOULDING PATTERNS; MOULDS FOR\nMETAL (OTHER THAN INGOT MOULDS), METAL CARBIDES, GLASS, MINERAL MATERIALS, RUBBER\nOR PLASTICS MOULDING BOXES FOR METAL FOUNDRY",
+        "hsn_code": "84801000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MOULDING BOXES FOR METAL FOUNDRY; MOULD BASES; MOULDING PATTERNS; MOULDS FOR\nMETAL (OTHER THAN INGOT MOULDS), METAL CARBIDES, GLASS, MINERAL MATERIALS, RUBBER\nOR PLASTICS MOULD BASES",
+        "hsn_code": "84802000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MOULDING BOXES FOR METAL FOUNDRY; MOULD BASES; MOULDING PATTERNS; MOULDS FOR\nMETAL (OTHER THAN INGOT MOULDS), METAL CARBIDES, GLASS, MINERAL MATERIALS, RUBBER\nOR PLASTICS MOULDING PATTERNS",
+        "hsn_code": "84803000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MOULDING BOXES FOR METAL FOUNDRY; MOULD BASES; MOULDING PATTERNS; MOULDS FOR\nMETAL (OTHER THAN INGOT MOULDS), METAL CARBIDES, GLASS, MINERAL MATERIALS, RUBBER\nOR PLASTICS - MOULDS FOR METAL OR METAL CARBIDES: INJECTION OR COMPRESSION TYPES",
+        "hsn_code": "84804100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MOULDING BOXES FOR METAL FOUNDRY; MOULD BASES; MOULDING PATTERNS; MOULDS FOR\nMETAL (OTHER THAN INGOT MOULDS), METAL CARBIDES, GLASS, MINERAL MATERIALS, RUBBER\nOR PLASTICS - MOULDS FOR METAL OR METAL CARBIDES: OTHER",
+        "hsn_code": "84804900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MOULDING BOXES FOR METAL FOUNDRY; MOULD BASES; MOULDING PATTERNS; MOULDS FOR\nMETAL (OTHER THAN INGOT MOULDS), METAL CARBIDES, GLASS, MINERAL MATERIALS, RUBBER\nOR PLASTICS MOULDS FOR GLASS",
+        "hsn_code": "84805000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MOULDING BOXES FOR METAL FOUNDRY; MOULD BASES; MOULDING PATTERNS; MOULDS FOR\nMETAL (OTHER THAN INGOT MOULDS), METAL CARBIDES, GLASS, MINERAL MATERIALS, RUBBER\nOR PLASTICS MOULDS FOR MINERAL MATERIALS",
+        "hsn_code": "84806000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MOULDING BOXES FOR METAL FOUNDRY; MOULD BASES; MOULDING PATTERNS; MOULDS FOR\nMETAL (OTHER THAN INGOT MOULDS), METAL CARBIDES, GLASS, MINERAL MATERIALS, RUBBER\nOR PLASTICS - MOULDS FOR RUBBER OR PLASTICS: INJECTION OR COMPRESSION TYPES",
+        "hsn_code": "84807100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MOULDING BOXES FOR METAL FOUNDRY; MOULD BASES; MOULDING PATTERNS; MOULDS FOR\nMETAL (OTHER THAN INGOT MOULDS), METAL CARBIDES, GLASS, MINERAL MATERIALS, RUBBER\nOR PLASTICS - MOULDS FOR RUBBER OR PLASTICS: OTHER",
+        "hsn_code": "84807900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MOULDING BOXES FOR METAL FOUNDRY; MOULD BASES; MOULDING PATTERNS; MOULDS FOR METAL (OTHER THAN INGOT MOULDS), METAL CARBIDES, GLASS, MINERAL MATERIALS, RUBBER OR PLASTICS",
+        "hsn_code": "8480",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER MACHINERY FOR PUBLIC WORKS, BUILDING OR THE\nLIKE",
+        "hsn_code": "84791000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - MACHINERY FOR THE EXTRACTION OR PREPARATION\nOF ANIMAL OR FIXED VEGETABLE FATS OR OILS: OIL-SEED CRUSHING OR GRINDING MACHINERY\nINCLUDING PURIFYING TANKS",
+        "hsn_code": "84792010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - MACHINERY FOR THE EXTRACTION OR PREPARATION\nOF ANIMAL OR FIXED VEGETABLE FATS OR OILS: OTHER",
+        "hsn_code": "84792090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER PRESSES FOR THE MANUFACTURE OF PARTICLE BOARD\nOR FIBRE BUILDING BOARD OF WOOD OR OTHER LIGNEOUS MATERIALS AND OTHER\nMACHINERY FOR TREATING WOOD OR CORK",
+        "hsn_code": "84793000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER ROPE OR CABLE-MAKING MACHINES",
+        "hsn_code": "84794000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER INDUSTRIAL ROBOTS, NOT ELSEWHERE SPECIFIED OR\nINCLUDED",
+        "hsn_code": "84795000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER EVAPORATIVE AIR COOLERS",
+        "hsn_code": "84796000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OF A KIND USED IN AIRPORTS",
+        "hsn_code": "84797100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "84797900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND MECHANICAL APPLIANCES:\nFOR TREATING METAL, INCLUDING ELECTRIC WIRE COIL-WINDERS",
+        "hsn_code": "84798100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND MECHANICAL APPLIANCES:\nMIXING, KNEADING, CRUSHING, GRINDING, SCREENING, SIFTING, HOMOGENISING,\nEMULSIFYING OR STIRRING MACHINES",
+        "hsn_code": "84798200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND MECHANICAL APPLIANCES:\nOTHER: SOAP CUTTING OR MOULDING MACHINERY",
+        "hsn_code": "84798910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND MECHANICAL APPLIANCES:\nOTHER: AIR HUMIDIFIERS OR DEHUMIDIFIERS (OTHER THAN THOSE FALLING UNDER HEADING\n8415 OR 8424)",
+        "hsn_code": "84798920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND MECHANICAL APPLIANCES:\nOTHER: MECHANICAL SHIFTING MACHINES",
+        "hsn_code": "84798930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND MECHANICAL APPLIANCES:\nOTHER: ULTRASONIC TRANSDUCERS",
+        "hsn_code": "84798940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND MECHANICAL APPLIANCES:\nOTHER: CAR WASHING MACHINES AND RELATED APPLIANCES",
+        "hsn_code": "84798950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND MECHANICAL APPLIANCES:\nOTHER: COKE OVEN PLANTS",
+        "hsn_code": "84798960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND MECHANICAL APPLIANCES:\nOTHER: MACHINERY FOR THE MANUFACTURE OF CHEMICAL AND PHARMACEUTICALS GOODS",
+        "hsn_code": "84798970",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND MECHANICAL APPLIANCES:\nOTHER: OTHER : INDUSTRIAL VACUUM CLEANERS",
+        "hsn_code": "84798991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND MECHANICAL APPLIANCES:\nOTHER: OTHER : BRIQUETTING PLANT AND MACHINERY INTENDED FOR MANUFACTURE OF\nBRIQUETTES FROM AGRICULTURAL AND MUNICIPAL WASTE",
+        "hsn_code": "84798992",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - OTHER MACHINES AND MECHANICAL APPLIANCES:\nOTHER: OTHER : OTHER",
+        "hsn_code": "84798999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - PARTS: OF MACHINES FOR PUBLIC WORKS, BUILDING\nOR THE LIKE",
+        "hsn_code": "84799010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - PARTS: OF MACHINES FOR THE EXTRACTION OF\nANIMAL OR FRUIT AND VEGETABLE FATS OR OIL",
+        "hsn_code": "84799020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - PARTS: OF MACHINES AND MECHANICAL APPLIANCES\nFOR TREATING WOOD",
+        "hsn_code": "84799030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - PARTS: OF MACHINERY USED FOR MANUFACTURE OF\nCHEMICALS AND PHARMACEUTICALS",
+        "hsn_code": "84799040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR\nINCLUDED ELSEWHERE IN THIS CHAPTER - PARTS: OTHER",
+        "hsn_code": "84799090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES AND MECHANICAL APPLIANCES HAVING INDIVIDUAL FUNCTIONS, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER",
+        "hsn_code": "8479",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR PREPARING OR MAKING UP TOBACCO, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER - MACHINERY: CIGAR MAKING MACHINERY",
+        "hsn_code": "84781010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR PREPARING OR MAKING UP TOBACCO, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER - MACHINERY: CIGARETTE MAKING MACHINERY",
+        "hsn_code": "84781020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR PREPARING OR MAKING UP TOBACCO, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER - MACHINERY: OTHER",
+        "hsn_code": "84781090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR PREPARING OR MAKING UP TOBACCO, NOT SPECIFIED OR INCLUDED\nELSEWHERE IN THIS CHAPTER - PARTS",
+        "hsn_code": "84789000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR PREPARING OR MAKING UP TOBACCO, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER",
+        "hsn_code": "8478",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR WORKING RUBBER OR PLASTICS OR FOR THE MANUFACTURE OF PRODUCTS\nFROM THESE MATERIALS, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER INJECTION\nMOULDING MACHINES",
+        "hsn_code": "84771000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR WORKING RUBBER OR PLASTICS OR FOR THE MANUFACTURE OF PRODUCTS\nFROM THESE MATERIALS, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER\nEXTRUDERS",
+        "hsn_code": "84772000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR WORKING RUBBER OR PLASTICS OR FOR THE MANUFACTURE OF PRODUCTS\nFROM THESE MATERIALS, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER BLOW\nMOULDING MACHINES",
+        "hsn_code": "84773000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR WORKING RUBBER OR PLASTICS OR FOR THE MANUFACTURE OF PRODUCTS\nFROM THESE MATERIALS, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER VACUUM\nMOULDING MACHINES AND OTHER THERMOFORMING MACHINES",
+        "hsn_code": "84774000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR WORKING RUBBER OR PLASTICS OR FOR THE MANUFACTURE OF PRODUCTS\nFROM THESE MATERIALS, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER - OTHER\nMACHINERY FOR MOULDING OR OTHERWISE FORMING: FOR MOULDING OR RETREADING\nPNEUMATIC TYRES OR FOR MOULDING OR OTHERWISE FORMING INNER TUBES",
+        "hsn_code": "84775100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR WORKING RUBBER OR PLASTICS OR FOR THE MANUFACTURE OF PRODUCTS\nFROM THESE MATERIALS, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER - OTHER\nMACHINERY FOR MOULDING OR OTHERWISE FORMING: OTHER",
+        "hsn_code": "84775900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR WORKING RUBBER OR PLASTICS OR FOR THE MANUFACTURE OF PRODUCTS\nFROM THESE MATERIALS, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER - OTHER\nMACHINERY: MACHINERY FOR MAKING RUBBER GOODS",
+        "hsn_code": "84778010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR WORKING RUBBER OR PLASTICS OR FOR THE MANUFACTURE OF PRODUCTS\nFROM THESE MATERIALS, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER - OTHER\nMACHINERY: OTHER",
+        "hsn_code": "84778090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR WORKING RUBBER OR PLASTICS OR FOR THE MANUFACTURE OF PRODUCTS\nFROM THESE MATERIALS, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER PARTS",
+        "hsn_code": "84779000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR WORKING RUBBER OR PLASTICS OR FOR THE MANUFACTURE OF PRODUCTS FROM THESE MATERIALS, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER",
+        "hsn_code": "8477",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC GOODS-VENDING MACHINES (FOR EXAMPLE, POSTAGE STAMPS, CIGARETTE, FOOD\nOR BEVERAGE MACHINES), INCLUDING MONEY CHANGING MACHINES - AUTOMATIC BEVERAGE\nVENDING MACHINE - INCORPORATING HEATING OR REFRIGERATING DEVICES: INCORPORATING\nREFRIGERATING DEVICES",
+        "hsn_code": "84762110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC GOODS-VENDING MACHINES (FOR EXAMPLE, POSTAGE STAMPS, CIGARETTE, FOOD\nOR BEVERAGE MACHINES), INCLUDING MONEY CHANGING MACHINES - AUTOMATIC BEVERAGE\nVENDING MACHINE - INCORPORATING HEATING OR REFRIGERATING DEVICES: NCORPORATING\nHEATING DEVICES",
+        "hsn_code": "84762120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC GOODS-VENDING MACHINES (FOR EXAMPLE, POSTAGE STAMPS, CIGARETTE, FOOD\nOR BEVERAGE MACHINES), INCLUDING MONEY CHANGING MACHINES - AUTOMATIC BEVERAGE\nVENDING MACHINE - OTHER",
+        "hsn_code": "84762900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC GOODS-VENDING MACHINES (FOR EXAMPLE, POSTAGE STAMPS, CIGARETTE, FOOD\nOR BEVERAGE MACHINES), INCLUDING MONEY CHANGING MACHINES - OTHER MACHINES -\nINCORPORATING HEATING OR REFRIGERATING DEVICES: INCORPORATING REFRIGERATING\nDEVICES",
+        "hsn_code": "84768110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC GOODS-VENDING MACHINES (FOR EXAMPLE, POSTAGE STAMPS, CIGARETTE, FOOD\nOR BEVERAGE MACHINES), INCLUDING MONEY CHANGING MACHINES - OTHER MACHINES -\nINCORPORATING HEATING OR REFRIGERATING DEVICES: INCORPORATING HEATING DEVICES",
+        "hsn_code": "84768120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC GOODS-VENDING MACHINES (FOR EXAMPLE, POSTAGE STAMPS, CIGARETTE, FOOD\nOR BEVERAGE MACHINES), INCLUDING MONEY CHANGING MACHINES - OTHER MACHINES -\nOTHER: MONEY CHANGING MACHINES",
+        "hsn_code": "84768910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC GOODS-VENDING MACHINES (FOR EXAMPLE, POSTAGE STAMPS, CIGARETTE, FOOD\nOR BEVERAGE MACHINES), INCLUDING MONEY CHANGING MACHINES - OTHER MACHINES -\nOTHER: POSTAGE STAMPS VENDING MACHINES",
+        "hsn_code": "84768920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC GOODS-VENDING MACHINES (FOR EXAMPLE, POSTAGE STAMPS, CIGARETTE, FOOD\nOR BEVERAGE MACHINES), INCLUDING MONEY CHANGING MACHINES - OTHER MACHINES -\nOTHER: CIGARETTE VENDING MACHINES",
+        "hsn_code": "84768930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC GOODS-VENDING MACHINES (FOR EXAMPLE, POSTAGE STAMPS, CIGARETTE, FOOD\nOR BEVERAGE MACHINES), INCLUDING MONEY CHANGING MACHINES - OTHER MACHINES -\nOTHER: OTHER",
+        "hsn_code": "84768990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC GOODS-VENDING MACHINES (FOR EXAMPLE, POSTAGE STAMPS, CIGARETTE, FOOD\nOR BEVERAGE MACHINES), INCLUDING MONEY CHANGING MACHINES - PARTS:OF MACHINES OF\nSUB-HEADING 8476 21",
+        "hsn_code": "84769010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC GOODS-VENDING MACHINES (FOR EXAMPLE, POSTAGE STAMPS, CIGARETTE, FOOD\nOR BEVERAGE MACHINES), INCLUDING MONEY CHANGING MACHINES - PARTS:OTHER",
+        "hsn_code": "84769090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC GOODS-VENDING MACHINES (FOR EXAMPLE, POSTAGE STAMPS, CIGARETTE, FOOD OR BEVERAGE MACHINES), INCLUDING MONEY-CHANGING MACHINES",
+        "hsn_code": "8476",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR ASSEMBLING ELECTRIC OR ELECTRONIC LAMPS, TUBES OR VALVES OR FLASH\nBULBS, IN GLASS ENVELOPES; MACHINES OR MANUFACTURING OR HOT WORKING GLASS OR\nGLASSWARE MACHINES FOR ASSEMBLING ELECTRIC OR ELECTRONIC LAMPS, TUBES OR VALVES\nOR FLASH-BULBS, IN GLASS ENVELOPES",
+        "hsn_code": "84751000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR ASSEMBLING ELECTRIC OR ELECTRONIC LAMPS, TUBES OR VALVES OR FLASH\nBULBS, IN GLASS ENVELOPES; MACHINES OR MANUFACTURING OR HOT WORKING GLASS OR\nGLASSWARE - MACHINES FOR MANUFACTURING OR HOT WORKING GLASS OR GLASSWARE:\nMACHINES FOR MAKING OPTICAL FIBRES AND PREFORMS THEREOF",
+        "hsn_code": "84752100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR ASSEMBLING ELECTRIC OR ELECTRONIC LAMPS, TUBES OR VALVES OR FLASH\nBULBS, IN GLASS ENVELOPES; MACHINES OR MANUFACTURING OR HOT WORKING GLASS OR\nGLASSWARE - MACHINES FOR MANUFACTURING OR HOT WORKING GLASS OR GLASSWARE:\nOTHER",
+        "hsn_code": "84752900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR ASSEMBLING ELECTRIC OR ELECTRONIC LAMPS, TUBES OR VALVES OR FLASH\nBULBS, IN GLASS ENVELOPES; MACHINES OR MANUFACTURING OR HOT WORKING GLASS OR\nGLASSWARE - PARTS",
+        "hsn_code": "84759000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR ASSEMBLING ELECTRIC OR ELECTRONIC LAMPS, TUBES OR VALVES OR FLASH-BULBS, IN GLASS ENVELOPES; MACHINES OF MANUFACTURING OR HOT WORKING GLASS OR GLASSWARE",
+        "hsn_code": "8475",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR SORTING, SCREENING, SEPARATING, WASHING, CRUSHING, GRINDING, MIXING\nOR KNEADING EARTH, STONE, ORES OR OTHER MINERAL SUBSTANCES, IN SOLID (INCLUDING\nPOWDER OR PASTE) FORM; MACHINERY FOR AGGLOMERATING, SHAPING OR MOULDING SOLID\nMINERAL FUELS, CERAMIC PASTE, UNHARDENED CEMENTS, PLASTERING MATERIALS OR OTHER\nMINERAL PRODUCTS IN POWDER OR PASTE FORM; MACHINES FOR FORMING FOUNDRY\nMOULDS OF SAND - SORTING, SCREENING, SEPARATING OR WASHING MACHINES: FOR COAL",
+        "hsn_code": "84741010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR SORTING, SCREENING, SEPARATING, WASHING, CRUSHING, GRINDING, MIXING\nOR KNEADING EARTH, STONE, ORES OR OTHER MINERAL SUBSTANCES, IN SOLID (INCLUDING\nPOWDER OR PASTE) FORM; MACHINERY FOR AGGLOMERATING, SHAPING OR MOULDING SOLID\nMINERAL FUELS, CERAMIC PASTE, UNHARDENED CEMENTS, PLASTERING MATERIALS OR OTHER\nMINERAL PRODUCTS IN POWDER OR PASTE FORM; MACHINES FOR FORMING FOUNDRY\nMOULDS OF SAND - SORTING, SCREENING, SEPARATING OR WASHING MACHINES: OTHER",
+        "hsn_code": "84741090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR SORTING, SCREENING, SEPARATING, WASHING, CRUSHING, GRINDING, MIXING\nOR KNEADING EARTH, STONE, ORES OR OTHER MINERAL SUBSTANCES, IN SOLID (INCLUDING\nPOWDER OR PASTE) FORM; MACHINERY FOR AGGLOMERATING, SHAPING OR MOULDING SOLID\nMINERAL FUELS, CERAMIC PASTE, UNHARDENED CEMENTS, PLASTERING MATERIALS OR OTHER\nMINERAL PRODUCTS IN POWDER OR PASTE FORM; MACHINES FOR FORMING FOUNDRY\nMOULDS OF SAND - CRUSHING OR GRINDING MACHINES: FOR STONE AND MINERAL",
+        "hsn_code": "84742010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR SORTING, SCREENING, SEPARATING, WASHING, CRUSHING, GRINDING, MIXING\nOR KNEADING EARTH, STONE, ORES OR OTHER MINERAL SUBSTANCES, IN SOLID (INCLUDING\nPOWDER OR PASTE) FORM; MACHINERY FOR AGGLOMERATING, SHAPING OR MOULDING SOLID\nMINERAL FUELS, CERAMIC PASTE, UNHARDENED CEMENTS, PLASTERING MATERIALS OR OTHER\nMINERAL PRODUCTS IN POWDER OR PASTE FORM; MACHINES FOR FORMING FOUNDRY\nMOULDS OF SAND - CRUSHING OR GRINDING MACHINES: FOR COAL",
+        "hsn_code": "84742020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR SORTING, SCREENING, SEPARATING, WASHING, CRUSHING, GRINDING, MIXING\nOR KNEADING EARTH, STONE, ORES OR OTHER MINERAL SUBSTANCES, IN SOLID (INCLUDING\nPOWDER OR PASTE) FORM; MACHINERY FOR AGGLOMERATING, SHAPING OR MOULDING SOLID\nMINERAL FUELS, CERAMIC PASTE, UNHARDENED CEMENTS, PLASTERING MATERIALS OR OTHER\nMINERAL PRODUCTS IN POWDER OR PASTE FORM; MACHINES FOR FORMING FOUNDRY\nMOULDS OF SAND - CRUSHING OR GRINDING MACHINES: OTHER",
+        "hsn_code": "84742090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR SORTING, SCREENING, SEPARATING, WASHING, CRUSHING, GRINDING, MIXING\nOR KNEADING EARTH, STONE, ORES OR OTHER MINERAL SUBSTANCES, IN SOLID (INCLUDING\nPOWDER OR PASTE) FORM; MACHINERY FOR AGGLOMERATING, SHAPING OR MOULDING SOLID\nMINERAL FUELS, CERAMIC PASTE, UNHARDENED CEMENTS, PLASTERING MATERIALS OR OTHER\nMINERAL PRODUCTS IN POWDER OR PASTE FORM; MACHINES FOR FORMING FOUNDRY\nMOULDS OF SAND - MIXING OR KNEADING MACHINES - CONCRETE OR MORTAR MIXERS :\nCONCRETE MIXERS",
+        "hsn_code": "84743110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR SORTING, SCREENING, SEPARATING, WASHING, CRUSHING, GRINDING, MIXING\nOR KNEADING EARTH, STONE, ORES OR OTHER MINERAL SUBSTANCES, IN SOLID (INCLUDING\nPOWDER OR PASTE) FORM; MACHINERY FOR AGGLOMERATING, SHAPING OR MOULDING SOLID\nMINERAL FUELS, CERAMIC PASTE, UNHARDENED CEMENTS, PLASTERING MATERIALS OR OTHER\nMINERAL PRODUCTS IN POWDER OR PASTE FORM; MACHINES FOR FORMING FOUNDRY\nMOULDS OF SAND - MIXING OR KNEADING MACHINES - CONCRETE OR MORTAR MIXERS :\nMORTAR MIXERS",
+        "hsn_code": "84743120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR SORTING, SCREENING, SEPARATING, WASHING, CRUSHING, GRINDING, MIXING\nOR KNEADING EARTH, STONE, ORES OR OTHER MINERAL SUBSTANCES, IN SOLID (INCLUDING\nPOWDER OR PASTE) FORM; MACHINERY FOR AGGLOMERATING, SHAPING OR MOULDING SOLID\nMINERAL FUELS, CERAMIC PASTE, UNHARDENED CEMENTS, PLASTERING MATERIALS OR OTHER\nMINERAL PRODUCTS IN POWDER OR PASTE FORM; MACHINES FOR FORMING FOUNDRY\nMOULDS OF SAND - MIXING OR KNEADING MACHINES - MACHINES FOR MIXING MINERAL\nSUBSTANCES WITH BITUMEN",
+        "hsn_code": "84743200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR SORTING, SCREENING, SEPARATING, WASHING, CRUSHING, GRINDING, MIXING\nOR KNEADING EARTH, STONE, ORES OR OTHER MINERAL SUBSTANCES, IN SOLID (INCLUDING\nPOWDER OR PASTE) FORM; MACHINERY FOR AGGLOMERATING, SHAPING OR MOULDING SOLID\nMINERAL FUELS, CERAMIC PASTE, UNHARDENED CEMENTS, PLASTERING MATERIALS OR OTHER\nMINERAL PRODUCTS IN POWDER OR PASTE FORM; MACHINES FOR FORMING FOUNDRY\nMOULDS OF SAND - MIXING OR KNEADING MACHINES - OTHER",
+        "hsn_code": "84743900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR SORTING, SCREENING, SEPARATING, WASHING, CRUSHING, GRINDING, MIXING\nOR KNEADING EARTH, STONE, ORES OR OTHER MINERAL SUBSTANCES, IN SOLID (INCLUDING\nPOWDER OR PASTE) FORM; MACHINERY FOR AGGLOMERATING, SHAPING OR MOULDING SOLID\nMINERAL FUELS, CERAMIC PASTE, UNHARDENED CEMENTS, PLASTERING MATERIALS OR OTHER\nMINERAL PRODUCTS IN POWDER OR PASTE FORM; MACHINES FOR FORMING FOUNDRY\nMOULDS OF SAND - OTHER MACHINERY: BRICK AND TILE MAKING MACHINERY",
+        "hsn_code": "84748010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR SORTING, SCREENING, SEPARATING, WASHING, CRUSHING, GRINDING, MIXING\nOR KNEADING EARTH, STONE, ORES OR OTHER MINERAL SUBSTANCES, IN SOLID (INCLUDING\nPOWDER OR PASTE) FORM; MACHINERY FOR AGGLOMERATING, SHAPING OR MOULDING SOLID\nMINERAL FUELS, CERAMIC PASTE, UNHARDENED CEMENTS, PLASTERING MATERIALS OR OTHER\nMINERAL PRODUCTS IN POWDER OR PASTE FORM; MACHINES FOR FORMING FOUNDRY\nMOULDS OF SAND - OTHER MACHINERY: CERAMIC AND CLAY MAKING MACHINERY",
+        "hsn_code": "84748020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR SORTING, SCREENING, SEPARATING, WASHING, CRUSHING, GRINDING, MIXING\nOR KNEADING EARTH, STONE, ORES OR OTHER MINERAL SUBSTANCES, IN SOLID (INCLUDING\nPOWDER OR PASTE) FORM; MACHINERY FOR AGGLOMERATING, SHAPING OR MOULDING SOLID\nMINERAL FUELS, CERAMIC PASTE, UNHARDENED CEMENTS, PLASTERING MATERIALS OR OTHER\nMINERAL PRODUCTS IN POWDER OR PASTE FORM; MACHINES FOR FORMING FOUNDRY\nMOULDS OF SAND - OTHER MACHINERY: MACHINERY FOR FORMING FOUNDRY MOULDS OF\nSAND",
+        "hsn_code": "84748030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR SORTING, SCREENING, SEPARATING, WASHING, CRUSHING, GRINDING, MIXING\nOR KNEADING EARTH, STONE, ORES OR OTHER MINERAL SUBSTANCES, IN SOLID (INCLUDING\nPOWDER OR PASTE) FORM; MACHINERY FOR AGGLOMERATING, SHAPING OR MOULDING SOLID\nMINERAL FUELS, CERAMIC PASTE, UNHARDENED CEMENTS, PLASTERING MATERIALS OR OTHER\nMINERAL PRODUCTS IN POWDER OR PASTE FORM; MACHINES FOR FORMING FOUNDRY\nMOULDS OF SAND - OTHER MACHINERY: OTHER",
+        "hsn_code": "84748090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR SORTING, SCREENING, SEPARATING, WASHING, CRUSHING, GRINDING, MIXING\nOR KNEADING EARTH, STONE, ORES OR OTHER MINERAL SUBSTANCES, IN SOLID (INCLUDING\nPOWDER OR PASTE) FORM; MACHINERY FOR AGGLOMERATING, SHAPING OR MOULDING SOLID\nMINERAL FUELS, CERAMIC PASTE, UNHARDENED CEMENTS, PLASTERING MATERIALS OR OTHER\nMINERAL PRODUCTS IN POWDER OR PASTE FORM; MACHINES FOR FORMING FOUNDRY\nMOULDS OF SAND - PARTS",
+        "hsn_code": "84749000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR SORTING, SCREENING, SEPARATING, WASHING, CRUSHING, GRINDING, MIXING OR KNEADING EARTH, STONE, ORES OR OTHER MINERAL SUBSTANCES, IN SOLID (INCLUDING POWDER OR PASTE) FORM; MACHINERY FOR AGGLOMERATING, SHAPING OR MOULDING SOLID MINERAL FUELS, CERAMIC PASTE, UNHARDENED CEMENTS, PLASTERING MATERIALS OR OTHER MINERAL PRODUCTS IN POWDER OR PASTE FORM; MACHINES FOR FORMING FOUNDRY MOULDS OF SAND",
+        "hsn_code": "8474",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES (OTHER THAN COVERS, CARRYING CASES AND THE LIKE) SUITABLE FOR\nUSE SOLELY OR PRINCIPALLY WITH MACHINES OF HEADINGS 8469 TO 8472 PARTS AND\nACCESSORIES OF THE MACHINES OF HEADING 8469",
+        "hsn_code": "84731000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES (OTHER THAN COVERS, CARRYING CASES AND THE LIKE) SUITABLE FOR\nUSE SOLELY OR PRINCIPALLY WITH MACHINES OF HEADINGS 8469 TO 8472 - PARTS AND\nACCESSORIES OF THE MACHINES OF HEADING 8470: OF THE ELECTRONIC CALCULATING\nMACHINES OF SUB-HEADING 8470 10, 8470 21 OR 8471 29",
+        "hsn_code": "84732100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES (OTHER THAN COVERS, CARRYING CASES AND THE LIKE) SUITABLE FOR\nUSE SOLELY OR PRINCIPALLY WITH MACHINES OF HEADINGS 8469 TO 8472 - PARTS AND\nACCESSORIES OF THE MACHINES OF HEADING 8470: OTHER",
+        "hsn_code": "84732900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES (OTHER THAN COVERS, CARRYING CASES AND THE LIKE) SUITABLE FOR\nUSE SOLELY OR PRINCIPALLY WITH MACHINES OF HEADINGS 8469 TO 8472 - PARTS AND\nACCESSORIES OF THE MACHINES OF HEADING 8471: MICROPROCESSORS",
+        "hsn_code": "84733010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES (OTHER THAN COVERS, CARRYING CASES AND THE LIKE) SUITABLE FOR\nUSE SOLELY OR PRINCIPALLY WITH MACHINES OF HEADINGS 8469 TO 8472 - PARTS AND\nACCESSORIES OF THE MACHINES OF HEADING 8471: MOTHERBOARDS",
+        "hsn_code": "84733020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES (OTHER THAN COVERS, CARRYING CASES AND THE LIKE) SUITABLE FOR\nUSE SOLELY OR PRINCIPALLY WITH MACHINES OF HEADINGS 8469 TO 8472 - PARTS AND\nACCESSORIES OF THE MACHINES OF HEADING 8471: OTHER MOUNTED PRINTED CIRCUIT\nBOARDS",
+        "hsn_code": "84733030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES (OTHER THAN COVERS, CARRYING CASES AND THE LIKE) SUITABLE FOR\nUSE SOLELY OR PRINCIPALLY WITH MACHINES OF HEADINGS 8469 TO 8472 - PARTS AND\nACCESSORIES OF THE MACHINES OF HEADING 8471: HEAD STACK",
+        "hsn_code": "84733040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES (OTHER THAN COVERS, CARRYING CASES AND THE LIKE) SUITABLE FOR\nUSE SOLELY OR PRINCIPALLY WITH MACHINES OF HEADINGS 8469 TO 8472 - PARTS AND\nACCESSORIES OF THE MACHINES OF HEADING 8471: INK CARTRIDGES WITH PRINT HEAD\nASSEMBLY",
+        "hsn_code": "84733050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES (OTHER THAN COVERS, CARRYING CASES AND THE LIKE) SUITABLE FOR\nUSE SOLELY OR PRINCIPALLY WITH MACHINES OF HEADINGS 8469 TO 8472 - PARTS AND\nACCESSORIES OF THE MACHINES OF HEADING 8471: INK SPRAY NOZZLE",
+        "hsn_code": "84733060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES (OTHER THAN COVERS, CARRYING CASES AND THE LIKE) SUITABLE FOR\nUSE SOLELY OR PRINCIPALLY WITH MACHINES OF HEADINGS 8469 TO 8472 - PARTS AND\nACCESSORIES OF THE MACHINES OF HEADING 8471:- OTHER : NETWORK ACCESS CONTROLLERS",
+        "hsn_code": "84733091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES (OTHER THAN COVERS, CARRYING CASES AND THE LIKE) SUITABLE FOR\nUSE SOLELY OR PRINCIPALLY WITH MACHINES OF HEADINGS 8469 TO 8472 - PARTS AND\nACCESSORIES OF THE MACHINES OF HEADING 8471:- OTHER :GRAPHIC AND INTELLIGENCE\nBASED SCRIPT TECHNOLOGY (GIST) CARDS FOR MULTILINGUAL COMPUTERS",
+        "hsn_code": "84733092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES (OTHER THAN COVERS, CARRYING CASES AND THE LIKE) SUITABLE FOR\nUSE SOLELY OR PRINCIPALLY WITH MACHINES OF HEADINGS 8469 TO 8472 - PARTS AND\nACCESSORIES OF THE MACHINES OF HEADING 8471:- OTHER :OTHER",
+        "hsn_code": "84733099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES (OTHER THAN COVERS, CARRYING CASES AND THE LIKE) SUITABLE FOR\nUSE SOLELY OR PRINCIPALLY WITH MACHINES OF HEADINGS 8469 TO 8472 - PARTS AND\nACCESSORIES OF THE MACHINES OF HEADING 8472: PARTS OF DUPLICATING, HECTOGRAPH OR\nSTENCIL MACHINES",
+        "hsn_code": "84734010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES (OTHER THAN COVERS, CARRYING CASES AND THE LIKE) SUITABLE FOR\nUSE SOLELY OR PRINCIPALLY WITH MACHINES OF HEADINGS 8469 TO 8472 - PARTS AND\nACCESSORIES OF THE MACHINES OF HEADING 8472: OTHER",
+        "hsn_code": "84734090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES (OTHER THAN COVERS, CARRYING CASES AND THE LIKE) SUITABLE FOR\nUSE SOLELY OR PRINCIPALLY WITH MACHINES OF HEADINGS 8469 TO 8472 PARTS AND\nACCESSORIES EQUALLY SUITABLE FOR USE WITH MACHINES OF TWO OR MORE OF THE\nHEADINGS 8469 TO 8472",
+        "hsn_code": "84735000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES (OTHER THAN COVERS, CARRYING CASES AND THE LIKE) SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH MACHINES OF HEADINGS 8469 TO 8472",
+        "hsn_code": "8473",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER OFFICE MACHINES (FOR EXAMPLE, HECTOGRAPH OR STENCIL DUPLICATING MACHINES,\nADDRESSING MACHINES, AUTOMATIC BANKNOTE DISPENSERS, COIN SORTING MACHINES, COIN\nCOUNTING OR WRAPPING MACHINES, PENCIL-SHARPENING MACHINES, PERFORATING OR\nSTAPLING MACHINES) DUPLICATING MACHINES",
+        "hsn_code": "84721000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER OFFICE MACHINES (FOR EXAMPLE, HECTOGRAPH OR STENCIL DUPLICATING MACHINES,\nADDRESSING MACHINES, AUTOMATIC BANKNOTE DISPENSERS, COIN SORTING MACHINES, COIN\nCOUNTING OR WRAPPING MACHINES, PENCIL-SHARPENING MACHINES, PERFORATING OR\nSTAPLING MACHINES) ADDRESSING MACHINES AND ADDRESS PLATE EMBOSSING MACHINES",
+        "hsn_code": "84722000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER OFFICE MACHINES (FOR EXAMPLE, HECTOGRAPH OR STENCIL DUPLICATING MACHINES,\nADDRESSING MACHINES, AUTOMATIC BANKNOTE DISPENSERS, COIN SORTING MACHINES, COIN\nCOUNTING OR WRAPPING MACHINES, PENCIL-SHARPENING MACHINES, PERFORATING OR\nSTAPLING MACHINES) MACHINES FOR SORTING OR FOLDING MAIL OR FOR INSERTING MAIL IN\nENVELOPES OR BANDS, MACHINES FOR OPENING, CLOSING OR SEALING MAIL AND MACHINES\nFOR AFFIXING OR CANCELING POSTAGE STAMPS",
+        "hsn_code": "84723000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER OFFICE MACHINES (FOR EXAMPLE, HECTOGRAPH OR STENCIL DUPLICATING MACHINES,\nADDRESSING MACHINES, AUTOMATIC BANKNOTE DISPENSERS, COIN SORTING MACHINES, COIN\nCOUNTING OR WRAPPING MACHINES, PENCIL-SHARPENING MACHINES, PERFORATING OR\nSTAPLING MACHINES) - OTHER: STAPLING MACHINES (STAPLERS)",
+        "hsn_code": "84729010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER OFFICE MACHINES (FOR EXAMPLE, HECTOGRAPH OR STENCIL DUPLICATING MACHINES,\nADDRESSING MACHINES, AUTOMATIC BANKNOTE DISPENSERS, COIN SORTING MACHINES, COIN\nCOUNTING OR WRAPPING MACHINES, PENCIL-SHARPENING MACHINES, PERFORATING OR\nSTAPLING MACHINES) - OTHER: DIGITAL DUPLICATOR",
+        "hsn_code": "84729020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER OFFICE MACHINES (FOR EXAMPLE, HECTOGRAPH OR STENCIL DUPLICATING MACHINES,\nADDRESSING MACHINES, AUTOMATIC BANKNOTE DISPENSERS, COIN SORTING MACHINES, COIN\nCOUNTING OR WRAPPING MACHINES, PENCIL-SHARPENING MACHINES, PERFORATING OR\nSTAPLING MACHINES) - OTHER: AUTOMATIC BANK NOTE DISPENSERS",
+        "hsn_code": "84729030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER OFFICE MACHINES (FOR EXAMPLE, HECTOGRAPH OR STENCIL DUPLICATING MACHINES,\nADDRESSING MACHINES, AUTOMATIC BANKNOTE DISPENSERS, COIN SORTING MACHINES, COIN\nCOUNTING OR WRAPPING MACHINES, PENCIL-SHARPENING MACHINES, PERFORATING OR\nSTAPLING MACHINES) - OTHER: COIN SORTING MACHINES, COIN-COUNTING OR WRAPPING\nMACHINES",
+        "hsn_code": "84729040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER OFFICE MACHINES (FOR EXAMPLE, HECTOGRAPH OR STENCIL DUPLICATING MACHINES,\nADDRESSING MACHINES, AUTOMATIC BANKNOTE DISPENSERS, COIN SORTING MACHINES, COIN\nCOUNTING OR WRAPPING MACHINES, PENCIL-SHARPENING MACHINES, PERFORATING OR\nSTAPLING MACHINES) - OTHER: OTHER",
+        "hsn_code": "84729090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER OFFICE MACHINES (FOR EXAMPLE, HECTOGRAPH OR STENCIL DUPLICATING MACHINES, ADDRESSING MACHINES, AUTOMATIC BANK NOTE DISPENSERS, COIN SORTING MACHINES, COIN COUNTING OR WRAPPING MACHINES, PENCIL-SHARPENING MACHINES, PERFORATING OR STAPLING MACHINE)",
+        "hsn_code": "8472",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED ANALOGUE\nOR HYBRID AUTOMATIC DATA PROCESSING MACHINES",
+        "hsn_code": "84711000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - PORTABLE\nAUTOMATIC DATA PROCESSING MACHINES, WEIGHING NOT MORE THAN 10 KG, CONSISTING OF\nAT LEAST A CENTRAL PROCESSING UNIT, A KEYBOARD AND A DISPLAY:PERSONAL COMPUTER",
+        "hsn_code": "84713010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - PORTABLE\nAUTOMATIC DATA PROCESSING MACHINES, WEIGHING NOT MORE THAN 10 KG, CONSISTING OF\nAT LEAST A CENTRAL PROCESSING UNIT, A KEYBOARD AND A DISPLAY:OTHER",
+        "hsn_code": "84713090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER\nDIGITAL AUTOMATIC DATA PROCESSING MACHINES - COMPRISING IN THE SAME HOUSING AT\nLEAST A CENTRAL PROCESSING UNIT AND AN INPUT AND OUTPUT UNIT, WHETHER OR NOT\nCOMBINED :MICRO COMPUTER",
+        "hsn_code": "84714110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER\nDIGITAL AUTOMATIC DATA PROCESSING MACHINES - COMPRISING IN THE SAME HOUSING AT\nLEAST A CENTRAL PROCESSING UNIT AND AN INPUT AND OUTPUT UNIT, WHETHER OR NOT\nCOMBINED :LARGE OR MAIN FRAME COMPUTER",
+        "hsn_code": "84714120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER\nDIGITAL AUTOMATIC DATA PROCESSING MACHINES - COMPRISING IN THE SAME HOUSING AT\nLEAST A CENTRAL PROCESSING UNIT AND AN INPUT AND OUTPUT UNIT, WHETHER OR NOT\nCOMBINED :OTHER",
+        "hsn_code": "84714190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER\nDIGITAL AUTOMATIC DATA PROCESSING MACHINES - OTHER, PRESENTED IN THE FORM OF\nSYSTEMS",
+        "hsn_code": "84714900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED -\nPROCESSING UNITS OTHER THAN THOSE OFSUB-HEADINGS 8471 41 OR 8471 49, WHETHER OR\nNOT CONTAINING IN THE SAME HOUSING ONE OR TWO OF THE FOLLOWING TYPES OF UNIT:\nSTORAGE UNITS, INPUT UNITS, OUTPUT UNITS",
+        "hsn_code": "84715000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - INPUT OR\nOUTPUT UNITS, WHETHER OR NOT CONTAINING STORAGE UNITS IN THE SAME HOUSING:\nCOMBINED INPUT OR OUT PUT UNITS",
+        "hsn_code": "84716010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - INPUT OR\nOUTPUT UNITS, WHETHER OR NOT CONTAINING STORAGE UNITS IN THE SAME HOUSING:\nPRINTER: LINE PRINTER",
+        "hsn_code": "84716021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - INPUT OR\nOUTPUT UNITS, WHETHER OR NOT CONTAINING STORAGE UNITS IN THE SAME HOUSING:\nPRINTER: DOT MATRIX PRINTER",
+        "hsn_code": "84716022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - INPUT OR\nOUTPUT UNITS, WHETHER OR NOT CONTAINING STORAGE UNITS IN THE SAME HOUSING:\nPRINTER: LETTER QUALITY DAISY WHEEL PRINTER",
+        "hsn_code": "84716023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - INPUT OR\nOUTPUT UNITS, WHETHER OR NOT CONTAINING STORAGE UNITS IN THE SAME HOUSING:\nPRINTER: GRAPHIC PRINTER",
+        "hsn_code": "84716024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - INPUT OR\nOUTPUT UNITS, WHETHER OR NOT CONTAINING STORAGE UNITS IN THE SAME HOUSING:\nPRINTER: PLOTTER",
+        "hsn_code": "84716025",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - INPUT OR\nOUTPUT UNITS, WHETHER OR NOT CONTAINING STORAGE UNITS IN THE SAME HOUSING:\nPRINTER: LASER JET PRINTER",
+        "hsn_code": "84716026",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - INPUT OR\nOUTPUT UNITS, WHETHER OR NOT CONTAINING STORAGE UNITS IN THE SAME HOUSING:\nPRINTER: INK JET PRINTER",
+        "hsn_code": "84716027",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - INPUT OR\nOUTPUT UNITS, WHETHER OR NOT CONTAINING STORAGE UNITS IN THE SAME HOUSING:\nPRINTER: OTHER",
+        "hsn_code": "84716029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - INPUT OR\nOUTPUT UNITS, WHETHER OR NOT CONTAINING STORAGE UNITS IN THE SAME HOUSING:\nMONITOR",
+        "hsn_code": "84716030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - INPUT OR\nOUTPUT UNITS, WHETHER OR NOT CONTAINING STORAGE UNITS IN THE SAME HOUSING:\nKEYBOARD",
+        "hsn_code": "84716040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - INPUT OR\nOUTPUT UNITS, WHETHER OR NOT CONTAINING STORAGE UNITS IN THE SAME HOUSING:\nSCANNERS",
+        "hsn_code": "84716050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - INPUT OR\nOUTPUT UNITS, WHETHER OR NOT CONTAINING STORAGE UNITS IN THE SAME HOUSING:\nMOUSE",
+        "hsn_code": "84716060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - INPUT OR\nOUTPUT UNITS, WHETHER OR NOT CONTAINING STORAGE UNITS IN THE SAME HOUSING:\nOTHER",
+        "hsn_code": "84716090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - STORAGE\nUNITS: FLOPPY DISC DRIVES",
+        "hsn_code": "84717010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - STORAGE\nUNITS: HARD DISC DRIVES",
+        "hsn_code": "84717020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - STORAGE\nUNITS: REMOVABLE OR EXCHANGEABLE DISC DRIVES",
+        "hsn_code": "84717030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - STORAGE\nUNITS: MAGNETIC TAPE DRIVES",
+        "hsn_code": "84717040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - STORAGE\nUNITS: CARTRIDGE TAPE DRIVE",
+        "hsn_code": "84717050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - STORAGE\nUNITS: CD-ROM DRIVE",
+        "hsn_code": "84717060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - STORAGE\nUNITS: DIGITAL VIDEO DISC DRIVE",
+        "hsn_code": "84717070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - STORAGE\nUNITS: OTHER",
+        "hsn_code": "84717090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER\nUNITS OF AUTOMATIC DATA PROCESSING MACHINES",
+        "hsn_code": "84718000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL\nREADERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND\nMACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER",
+        "hsn_code": "84719000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUTOMATIC DATA PROCESSING MACHINES AND UNITS THEREOF; MAGNETIC OR OPTICAL READERS, MACHINES FOR TRANSCRIBING DATA ON TO DATA MEDIA IN CODED FORM AND MACHINES FOR PROCESSING SUCH DATA, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "8471",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CALCULATING MACHINES AND POCKET-SIZE DATA RECORDING, REPRODUCING AND DISPLAYING\nMACHINES WITH CALCULATING FUNCTIONS; ACCOUNTING MACHINES, POSTAGE-FRANKING\nMACHINES, TICKET-ISSUING MACHINES AND SIMILAR MACHINES, INCORPORATING A\nCALCULATING DEVICE; CASH REGISTERS ELECTRONIC CALCULATORS CAPABLE OF\nOPERATIONWITHOUT AN EXTERNAL SOURCE OF ELECTRIC POWER AND POCKET-SIZE DATA\nRECORDING, REPRODUCING AND DISPLAYING MACHINES WITH CALCULATING FUNCTIONS",
+        "hsn_code": "84701000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CALCULATING MACHINES AND POCKET-SIZE DATA RECORDING, REPRODUCING AND DISPLAYING\nMACHINES WITH CALCULATING FUNCTIONS; ACCOUNTING MACHINES, POSTAGE-FRANKING\nMACHINES, TICKET-ISSUING MACHINES AND SIMILAR MACHINES, INCORPORATING A\nCALCULATING DEVICE; CASH REGISTERS - OTHER ELECTRONIC CALCULATING MACHINES:\nINCORPORATING A PRINTING DEVICE",
+        "hsn_code": "84702100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CALCULATING MACHINES AND POCKET-SIZE DATA RECORDING, REPRODUCING AND DISPLAYING\nMACHINES WITH CALCULATING FUNCTIONS; ACCOUNTING MACHINES, POSTAGE-FRANKING\nMACHINES, TICKET-ISSUING MACHINES AND SIMILAR MACHINES, INCORPORATING A\nCALCULATING DEVICE; CASH REGISTERS - OTHER ELECTRONIC CALCULATING MACHINES: OTHER",
+        "hsn_code": "84702900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CALCULATING MACHINES AND POCKET-SIZE DATA RECORDING, REPRODUCING AND DISPLAYING\nMACHINES WITH CALCULATING FUNCTIONS; ACCOUNTING MACHINES, POSTAGE-FRANKING\nMACHINES, TICKET-ISSUING MACHINES AND SIMILAR MACHINES, INCORPORATING A\nCALCULATING DEVICE; CASH REGISTERS OTHER CALCULATING MACHINES",
+        "hsn_code": "84703000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CALCULATING MACHINES AND POCKET-SIZE DATA RECORDING, REPRODUCING AND DISPLAYING\nMACHINES WITH CALCULATING FUNCTIONS; ACCOUNTING MACHINES, POSTAGE-FRANKING\nMACHINES, TICKET-ISSUING MACHINES AND SIMILAR MACHINES, INCORPORATING A\nCALCULATING DEVICE; CASH REGISTERS - ACCOUNTING MACHINES : ELECTRICALLY OPERATED",
+        "hsn_code": "84704010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CALCULATING MACHINES AND POCKET-SIZE DATA RECORDING, REPRODUCING AND DISPLAYING\nMACHINES WITH CALCULATING FUNCTIONS; ACCOUNTING MACHINES, POSTAGE-FRANKING\nMACHINES, TICKET-ISSUING MACHINES AND SIMILAR MACHINES, INCORPORATING A\nCALCULATING DEVICE; CASH REGISTERS - ACCOUNTING MACHINES : MANUALLY OPERATED",
+        "hsn_code": "84704020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CALCULATING MACHINES AND POCKET-SIZE DATA RECORDING, REPRODUCING AND DISPLAYING\nMACHINES WITH CALCULATING FUNCTIONS; ACCOUNTING MACHINES, POSTAGE-FRANKING\nMACHINES, TICKET-ISSUING MACHINES AND SIMILAR MACHINES, INCORPORATING A\nCALCULATING DEVICE; CASH REGISTERS - CASH REGISTERS: ELECTRICALLY OPERATED",
+        "hsn_code": "84705010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CALCULATING MACHINES AND POCKET-SIZE DATA RECORDING, REPRODUCING AND DISPLAYING\nMACHINES WITH CALCULATING FUNCTIONS; ACCOUNTING MACHINES, POSTAGE-FRANKING\nMACHINES, TICKET-ISSUING MACHINES AND SIMILAR MACHINES, INCORPORATING A\nCALCULATING DEVICE; CASH REGISTERS - CASH REGISTERS: MANUALLY OPERATED",
+        "hsn_code": "84705020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CALCULATING MACHINES AND POCKET-SIZE DATA RECORDING, REPRODUCING AND DISPLAYING\nMACHINES WITH CALCULATING FUNCTIONS; ACCOUNTING MACHINES, POSTAGE-FRANKING\nMACHINES, TICKET-ISSUING MACHINES AND SIMILAR MACHINES, INCORPORATING A\nCALCULATING DEVICE; CASH REGISTERS - OTHER: ELECTRICALLY OPERATED",
+        "hsn_code": "84709010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CALCULATING MACHINES AND POCKET-SIZE DATA RECORDING, REPRODUCING AND DISPLAYING\nMACHINES WITH CALCULATING FUNCTIONS; ACCOUNTING MACHINES, POSTAGE-FRANKING\nMACHINES, TICKET-ISSUING MACHINES AND SIMILAR MACHINES, INCORPORATING A\nCALCULATING DEVICE; CASH REGISTERS - OTHER: MANUALLY OPERATED",
+        "hsn_code": "84709020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CALCULATING MACHINES AND POCKET-SIZE DATA RECORDING, REPRODUCING AND DISPLAYING MACHINES WITH CALCULATING FUNCTIONS; ACCOUNTING MACHINES, POSTAGE-FRANKING MACHINES, TICKET-ISSUING MACHINES AND SIMILAR MACHINES, INCORPORATING A CALCULATING DEVICE; CASH REGISTERS",
+        "hsn_code": "8470",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "8468 MACHINERY AND APPARATUS FOR SOLDERING, BRAZING OR WELDING, WHETHER OR NOT\nCAPABLE OF CUTTING, OTHER THAN THOSE OF HEADING 8515; GAS-OPERATED SURFACE\nTEMPERING MACHINES AND APPLIANCES HAND-HELD BLOW PIPES",
+        "hsn_code": "84681000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY AND APPARATUS FOR SOLDERING, BRAZING OR WELDING, WHETHER OR NOT\nCAPABLE OF CUTTING, OTHER THAN THOSE OF HEADING 8515; GAS-OPERATED SURFACE\nTEMPERING MACHINES AND APPLIANCES - OTHER GAS-OPERATED MACHINERY AND\nAPPARATUS: WELDING OR CUTTING MACHINES",
+        "hsn_code": "84682010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY AND APPARATUS FOR SOLDERING, BRAZING OR WELDING, WHETHER OR NOT\nCAPABLE OF CUTTING, OTHER THAN THOSE OF HEADING 8515; GAS-OPERATED SURFACE\nTEMPERING MACHINES AND APPLIANCES - OTHER GAS-OPERATED MACHINERY AND\nAPPARATUS: OTHER",
+        "hsn_code": "84682090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY AND APPARATUS FOR SOLDERING, BRAZING OR WELDING, WHETHER OR NOT\nCAPABLE OF CUTTING, OTHER THAN THOSE OF HEADING 8515; GAS-OPERATED SURFACE\nTEMPERING MACHINES AND APPLIANCES OTHER MACHINERY AND APPARATUS",
+        "hsn_code": "84688000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY AND APPARATUS FOR SOLDERING, BRAZING OR WELDING, WHETHER OR NOT\nCAPABLE OF CUTTING, OTHER THAN THOSE OF HEADING 8515; GAS-OPERATED SURFACE\nTEMPERING MACHINES AND APPLIANCES PARTS",
+        "hsn_code": "84689000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY AND APPARATUS FOR SOLDERING, BRAZING OR WELDING WHETHER OR NOT CAPABLE OF CUTTING, OTHER THAN THOSE OF HEADING 8415; GAS-OPERATED SURFACE TEMPERING MACHINES AND APPLIANCES",
+        "hsn_code": "8468",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOOLS FOR WORKING IN THE HAND, PNEUMATIC, HYDRAULIC OR WITH SELF-CONTAINED\nELECTRIC OR NON-ELECTRIC MOTOR - PNEUMATIC - ROTARY TYPE (INCLUDING COMBINED\nROTARY PERCUSSION): DRILLS",
+        "hsn_code": "84671110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOOLS FOR WORKING IN THE HAND, PNEUMATIC, HYDRAULIC OR WITH SELF-CONTAINED\nELECTRIC OR NON-ELECTRIC MOTOR - PNEUMATIC - ROTARY TYPE (INCLUDING COMBINED\nROTARY PERCUSSION): HAMMERS",
+        "hsn_code": "84671120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOOLS FOR WORKING IN THE HAND, PNEUMATIC, HYDRAULIC OR WITH SELF-CONTAINED\nELECTRIC OR NON-ELECTRIC MOTOR - PNEUMATIC - ROTARY TYPE (INCLUDING COMBINED\nROTARY PERCUSSION): OTHER",
+        "hsn_code": "84671190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOOLS FOR WORKING IN THE HAND, PNEUMATIC, HYDRAULIC OR WITH SELF-CONTAINED\nELECTRIC OR NON-ELECTRIC MOTOR - PNEUMATIC - OTHER",
+        "hsn_code": "84671900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOOLS FOR WORKING IN THE HAND, PNEUMATIC, HYDRAULIC OR WITH SELF-CONTAINED\nELECTRIC OR NON-ELECTRIC MOTOR - PNEUMATIC - WITH SELF-CONTAINED ELECTRIC MOTOR:\nDRILLS OF ALL KINDS",
+        "hsn_code": "84672100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOOLS FOR WORKING IN THE HAND, PNEUMATIC, HYDRAULIC OR WITH SELF-CONTAINED\nELECTRIC OR NON-ELECTRIC MOTOR - PNEUMATIC - WITH SELF-CONTAINED ELECTRIC MOTOR:\nSAWS",
+        "hsn_code": "84672200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOOLS FOR WORKING IN THE HAND, PNEUMATIC, HYDRAULIC OR WITH SELF-CONTAINED\nELECTRIC OR NON-ELECTRIC MOTOR - PNEUMATIC - WITH SELF-CONTAINED ELECTRIC MOTOR:\nOTHER",
+        "hsn_code": "84672900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOOLS FOR WORKING IN THE HAND, PNEUMATIC, HYDRAULIC OR WITH SELF-CONTAINED\nELECTRIC OR NON-ELECTRIC MOTOR - OTHER TOOLS: CHAIN SAWS",
+        "hsn_code": "84678100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOOLS FOR WORKING IN THE HAND, PNEUMATIC, HYDRAULIC OR WITH SELF-CONTAINED\nELECTRIC OR NON-ELECTRIC MOTOR - OTHER TOOLS: OTHER: COMPRESSED AIR GREASE GUNS,\nLUBRICATORS AND SIMILAR APPLIANCES",
+        "hsn_code": "84678910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOOLS FOR WORKING IN THE HAND, PNEUMATIC, HYDRAULIC OR WITH SELF-CONTAINED\nELECTRIC OR NON-ELECTRIC MOTOR - OTHER TOOLS: OTHER: VIBRATORS",
+        "hsn_code": "84678920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOOLS FOR WORKING IN THE HAND, PNEUMATIC, HYDRAULIC OR WITH SELF-CONTAINED\nELECTRIC OR NON-ELECTRIC MOTOR - OTHER TOOLS: OTHER: OTHER",
+        "hsn_code": "84678990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOOLS FOR WORKING IN THE HAND, PNEUMATIC, HYDRAULIC OR WITH SELF-CONTAINED\nELECTRIC OR NON-ELECTRIC MOTOR - PARTS: OF CHAIN SAWS",
+        "hsn_code": "84679100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOOLS FOR WORKING IN THE HAND, PNEUMATIC, HYDRAULIC OR WITH SELF-CONTAINED\nELECTRIC OR NON-ELECTRIC MOTOR - PARTS: OF PNEUMATIC TOOLS",
+        "hsn_code": "84679200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOOLS FOR WORKING IN THE HAND, PNEUMATIC, HYDRAULIC OR WITH SELF-CONTAINED\nELECTRIC OR NON-ELECTRIC MOTOR - PARTS: OTHER",
+        "hsn_code": "84679900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOOLS FOR WORKING IN THE HAND, PNEUMATIC, HYDRAULIC OR WITH SELF-CONTAINED ELECTRIC OR NON-ELECTRIC MOTOR",
+        "hsn_code": "8467",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF\nHEADINGS 8456 TO 8465, INCLUDING WORK OR TOOL HOLDERS, SELF-OPENING DIEHEADS,\nDIVIDING HEADS AND OTHER SPECIAL ATTACHMENTS FOR MACHINE-TOOLS; TOOL HOLDERS\nFOR ANY TYPE OF TOOL, FOR WORKING IN THE HAND - TOOL HOLDERS AND SELF-OPENING\nDIEHEADS: TOOL HOLDERS",
+        "hsn_code": "84661010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF\nHEADINGS 8456 TO 8465, INCLUDING WORK OR TOOL HOLDERS, SELF-OPENING DIEHEADS,\nDIVIDING HEADS AND OTHER SPECIAL ATTACHMENTS FOR MACHINE-TOOLS; TOOL HOLDERS\nFOR ANY TYPE OF TOOL, FOR WORKING IN THE HAND - TOOL HOLDERS AND SELF-OPENING\nDIEHEADS: SELF-OPENING DIEHEADS",
+        "hsn_code": "84661020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF\nHEADINGS 8456 TO 8465, INCLUDING WORK OR TOOL HOLDERS, SELF-OPENING DIEHEADS,\nDIVIDING HEADS AND OTHER SPECIAL ATTACHMENTS FOR MACHINE-TOOLS; TOOL HOLDERS\nFOR ANY TYPE OF TOOL, FOR WORKING IN THE HAND WORK HOLDERS",
+        "hsn_code": "84662000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF\nHEADINGS 8456 TO 8465, INCLUDING WORK OR TOOL HOLDERS, SELF-OPENING DIEHEADS,\nDIVIDING HEADS AND OTHER SPECIAL ATTACHMENTS FOR MACHINE-TOOLS; TOOL HOLDERS\nFOR ANY TYPE OF TOOL, FOR WORKING IN THE HAND - DIVIDING HEADS AND OTHER SPECIAL\nATTACHMENTS FOR MACHINE-TOOLS: CHUCKS",
+        "hsn_code": "84663010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF\nHEADINGS 8456 TO 8465, INCLUDING WORK OR TOOL HOLDERS, SELF-OPENING DIEHEADS,\nDIVIDING HEADS AND OTHER SPECIAL ATTACHMENTS FOR MACHINE-TOOLS; TOOL HOLDERS\nFOR ANY TYPE OF TOOL, FOR WORKING IN THE HAND - DIVIDING HEADS AND OTHER SPECIAL\nATTACHMENTS FOR MACHINE-TOOLS: JIGS AND FIXTURES",
+        "hsn_code": "84663020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF\nHEADINGS 8456 TO 8465, INCLUDING WORK OR TOOL HOLDERS, SELF-OPENING DIEHEADS,\nDIVIDING HEADS AND OTHER SPECIAL ATTACHMENTS FOR MACHINE-TOOLS; TOOL HOLDERS\nFOR ANY TYPE OF TOOL, FOR WORKING IN THE HAND - DIVIDING HEADS AND OTHER SPECIAL\nATTACHMENTS FOR MACHINE-TOOLS: OTHER",
+        "hsn_code": "84663090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF\nHEADINGS 8456 TO 8465, INCLUDING WORK OR TOOL HOLDERS, SELF-OPENING DIEHEADS,\nDIVIDING HEADS AND OTHER SPECIAL ATTACHMENTS FOR MACHINE-TOOLS; TOOL HOLDERS\nFOR ANY TYPE OF TOOL, FOR WORKING IN THE HAND - OTHER:FOR MACHINES OF HEADING\n8464",
+        "hsn_code": "84669100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF\nHEADINGS 8456 TO 8465, INCLUDING WORK OR TOOL HOLDERS, SELF-OPENING DIEHEADS,\nDIVIDING HEADS AND OTHER SPECIAL ATTACHMENTS FOR MACHINE-TOOLS; TOOL HOLDERS\nFOR ANY TYPE OF TOOL, FOR WORKING IN THE HAND - OTHER:FOR MACHINES OF HEADING\n8465",
+        "hsn_code": "84669200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF\nHEADINGS 8456 TO 8465, INCLUDING WORK OR TOOL HOLDERS, SELF-OPENING DIEHEADS,\nDIVIDING HEADS AND OTHER SPECIAL ATTACHMENTS FOR MACHINE-TOOLS; TOOL HOLDERS\nFOR ANY TYPE OF TOOL, FOR WORKING IN THE HAND - OTHER - FOR MACHINES OF HEADINGS\n8456 TO 8461: PARTS AND ACCESSORIES OF MACHINE TOOLS, FOR WORKING METALS",
+        "hsn_code": "84669310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF\nHEADINGS 8456 TO 8465, INCLUDING WORK OR TOOL HOLDERS, SELF-OPENING DIEHEADS,\nDIVIDING HEADS AND OTHER SPECIAL ATTACHMENTS FOR MACHINE-TOOLS; TOOL HOLDERS\nFOR ANY TYPE OF TOOL, FOR WORKING IN THE HAND - OTHER - FOR MACHINES OF HEADINGS\n8456 TO 8461:OTHER",
+        "hsn_code": "84669390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF\nHEADINGS 8456 TO 8465, INCLUDING WORK OR TOOL HOLDERS, SELF-OPENING DIEHEADS,\nDIVIDING HEADS AND OTHER SPECIAL ATTACHMENTS FOR MACHINE-TOOLS; TOOL HOLDERS\nFOR ANY TYPE OF TOOL, FOR WORKING IN THE HAND - OTHER: FOR MACHINES OF HEADING\n8462 OR 8463",
+        "hsn_code": "84669400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF HEADINGS 8456 TO 8465, INCLUDING WORK OR TOOL HOLDERS, SELF-OPENING DIEHEADS, DIVIDING HEADS AND OTHER SPECIAL ATTACHMENTS FOR MACHINE-TOOLS; TOOL HOLDERS FOR ANY TYPE OF TOOL, FOR WORKING IN THE HAND",
+        "hsn_code": "8466",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE TOOLS (INCLUDING MACHINES FOR NAILING, STAPLING, GLUEING OR OTHERWISE\nASSEMBLING) FOR WORKING WOOD, CORK, BONE, HARD RUBBER, HARD PLASTICS OR SIMILAR\nHARD MATERIALS - MACHINES WHICH CAN CARRY OUT DIFFERENT TYPES OF MACHINING\nOPERATIONS WITHOUT TOOL CHANGE BETWEEN SUCH OPERATIONS",
+        "hsn_code": "84651000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE TOOLS (INCLUDING MACHINES FOR NAILING, STAPLING, GLUEING OR OTHERWISE\nASSEMBLING) FOR WORKING WOOD, CORK, BONE, HARD RUBBER, HARD PLASTICS OR SIMILAR\nHARD MATERIALS - OTHER : SAWING MACHINES",
+        "hsn_code": "84659100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE TOOLS (INCLUDING MACHINES FOR NAILING, STAPLING, GLUEING OR OTHERWISE\nASSEMBLING) FOR WORKING WOOD, CORK, BONE, HARD RUBBER, HARD PLASTICS OR SIMILAR\nHARD MATERIALS - OTHER : PLANING, MILLING OR MOULDING (BY CUTTING) MACHINES",
+        "hsn_code": "84659200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE TOOLS (INCLUDING MACHINES FOR NAILING, STAPLING, GLUEING OR OTHERWISE\nASSEMBLING) FOR WORKING WOOD, CORK, BONE, HARD RUBBER, HARD PLASTICS OR SIMILAR\nHARD MATERIALS - OTHER : GRINDING, SANDING OR POLISHING MACHINES",
+        "hsn_code": "84659300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE TOOLS (INCLUDING MACHINES FOR NAILING, STAPLING, GLUEING OR OTHERWISE\nASSEMBLING) FOR WORKING WOOD, CORK, BONE, HARD RUBBER, HARD PLASTICS OR SIMILAR\nHARD MATERIALS - OTHER : BENDING OR ASSEMBLING MACHINES",
+        "hsn_code": "84659400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE TOOLS (INCLUDING MACHINES FOR NAILING, STAPLING, GLUEING OR OTHERWISE\nASSEMBLING) FOR WORKING WOOD, CORK, BONE, HARD RUBBER, HARD PLASTICS OR SIMILAR\nHARD MATERIALS - OTHER : DRILLING OR MORTICING MACHINES",
+        "hsn_code": "84659500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE TOOLS (INCLUDING MACHINES FOR NAILING, STAPLING, GLUEING OR OTHERWISE\nASSEMBLING) FOR WORKING WOOD, CORK, BONE, HARD RUBBER, HARD PLASTICS OR SIMILAR\nHARD MATERIALS - OTHER : SPLITTING, SLICING OR PARING MACHINES",
+        "hsn_code": "84659600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE TOOLS (INCLUDING MACHINES FOR NAILING, STAPLING, GLUEING OR OTHERWISE\nASSEMBLING) FOR WORKING WOOD, CORK, BONE, HARD RUBBER, HARD PLASTICS OR SIMILAR\nHARD MATERIALS - OTHER : OTHER: LATHES",
+        "hsn_code": "84659910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE TOOLS (INCLUDING MACHINES FOR NAILING, STAPLING, GLUEING OR OTHERWISE\nASSEMBLING) FOR WORKING WOOD, CORK, BONE, HARD RUBBER, HARD PLASTICS OR SIMILAR\nHARD MATERIALS - OTHER : OTHER: OTHER",
+        "hsn_code": "84659990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING MACHINES FOR NAILING, STAPLING, GLUEING OR OTHERWISE ASSEMBLING) FOR WORKING WOOD, CORK, BONE, HARD RUBBER, HARD PLASTICS OR SIMILAR HARD MATERIALS",
+        "hsn_code": "8465",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR WORKING STONE, CERAMICS, CONCRETE, ASBESTOS-CEMENT OR LIKE\nMINERAL MATERIALS OR FOR COLD WORKING GLASS - SAWING MACHINE: GRANITE CUTTING\nMACHINES OR EQUIPMENT",
+        "hsn_code": "84641010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR WORKING STONE, CERAMICS, CONCRETE, ASBESTOS-CEMENT OR LIKE\nMINERAL MATERIALS OR FOR COLD WORKING GLASS - SAWING MACHINE: OTHER",
+        "hsn_code": "84641090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR WORKING STONE, CERAMICS, CONCRETE, ASBESTOS-CEMENT OR LIKE\nMINERAL MATERIALS OR FOR COLD WORKING GLASS GRINDING OR POLISHING MACHINES",
+        "hsn_code": "84642000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR WORKING STONE, CERAMICS, CONCRETE, ASBESTOS-CEMENT OR LIKE\nMINERAL MATERIALS OR FOR COLD WORKING GLASS OTHER",
+        "hsn_code": "84649000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE TOOLS FOR WORKING STONE, CERAMICS, CONCRETE, ASBESTOS CEMENT OR LIKE MINERAL MATERIALS OR FOR COLD WORKING GLASS",
+        "hsn_code": "8464",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINE-TOOLS FOR WORKING METAL, OR CERMETS, WITHOUT REMOVING MATERIAL -\nDRAW-BENCHES FOR BARS, TUBES, PROFILES, WIRE OR THE LIKE:WIRE AND METAL RIBBON\nDRAWING MACHINES",
+        "hsn_code": "84631010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINE-TOOLS FOR WORKING METAL, OR CERMETS, WITHOUT REMOVING MATERIAL -\nDRAW-BENCHES FOR BARS, TUBES, PROFILES, WIRE OR THE LIKE:OTHER WIRE MAKING\nMACHINES",
+        "hsn_code": "84631020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINE-TOOLS FOR WORKING METAL, OR CERMETS, WITHOUT REMOVING MATERIAL -\nDRAW-BENCHES FOR BARS, TUBES, PROFILES, WIRE OR THE LIKE:TUBE DRAWING MACHINES",
+        "hsn_code": "84631030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINE-TOOLS FOR WORKING METAL, OR CERMETS, WITHOUT REMOVING MATERIAL -\nDRAW-BENCHES FOR BARS, TUBES, PROFILES, WIRE OR THE LIKE:OTHER",
+        "hsn_code": "84631090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINE-TOOLS FOR WORKING METAL, OR CERMETS, WITHOUT REMOVING MATERIAL\nTHREAD ROLLING MACHINES",
+        "hsn_code": "84632000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINE-TOOLS FOR WORKING METAL, OR CERMETS, WITHOUT REMOVING MATERIAL -\nMACHINES FOR WORKING WIRE : WIRE GRILL OR KNITTING MACHINE",
+        "hsn_code": "84633010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINE-TOOLS FOR WORKING METAL, OR CERMETS, WITHOUT REMOVING MATERIAL -\nMACHINES FOR WORKING WIRE : SPRING COILING",
+        "hsn_code": "84633020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINE-TOOLS FOR WORKING METAL, OR CERMETS, WITHOUT REMOVING MATERIAL -\nMACHINES FOR WORKING WIRE : CHAIN MAKING",
+        "hsn_code": "84633030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINE-TOOLS FOR WORKING METAL, OR CERMETS, WITHOUT REMOVING MATERIAL -\nMACHINES FOR WORKING WIRE : NAIL MAKING MACHINE",
+        "hsn_code": "84633040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINE-TOOLS FOR WORKING METAL, OR CERMETS, WITHOUT REMOVING MATERIAL -\nOTHER: RIVETING MACHINES (EXCLUDING PORTABLE HAND OPERATED MACHINE)",
+        "hsn_code": "84639010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINE-TOOLS FOR WORKING METAL, OR CERMETS, WITHOUT REMOVING MATERIAL -\nOTHER: STRIP PROFILING",
+        "hsn_code": "84639020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINE-TOOLS FOR WORKING METAL, OR CERMETS, WITHOUT REMOVING MATERIAL -\nOTHER: SEAMING MACHINE FOR EXAMPLE FOR CANS",
+        "hsn_code": "84639030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINE-TOOLS FOR WORKING METAL, OR CERMETS, WITHOUT REMOVING MATERIAL -\nOTHER: OTHER",
+        "hsn_code": "84639090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINE TOOLS FOR WORKING METAL OR CERMETS WITHOUT REMOVING MATERIAL",
+        "hsn_code": "8463",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - FORGING OR DIE-STAMPING\nMACHINES (INCLUDING PRESSES) AND HAMMERS: HAMMERS: STEAM OR AIR, SINGLE FRAME",
+        "hsn_code": "84621011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - FORGING OR DIE-STAMPING\nMACHINES (INCLUDING PRESSES) AND HAMMERS: HAMMERS: STEAM OR AIR, DOUBLE FRAME",
+        "hsn_code": "84621012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - FORGING OR DIE-STAMPING\nMACHINES (INCLUDING PRESSES) AND HAMMERS: HAMMERS: HEADERS AND UPSETTERS",
+        "hsn_code": "84621013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - FORGING OR DIE-STAMPING\nMACHINES (INCLUDING PRESSES) AND HAMMERS: HAMMERS: DOUBLE ACTING COUNTER\nBLOW, AIR OR STEAM",
+        "hsn_code": "84621014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - FORGING OR DIE-STAMPING\nMACHINES (INCLUDING PRESSES) AND HAMMERS: HAMMERS: OTHER",
+        "hsn_code": "84621019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - FORGING OR DIE-STAMPING\nMACHINES (INCLUDING PRESSES) AND HAMMERS: FORGING MACHINES",
+        "hsn_code": "84621020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - FORGING OR DIE-STAMPING\nMACHINES (INCLUDING PRESSES) AND HAMMERS: DIE STAMPING MACHINES",
+        "hsn_code": "84621030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - BENDING, FOLDING,\nSTRAIGHTENING OR FLATTENING MACHINES (INCLUDING PRESSES): NUMERICALLY CONTROLLED",
+        "hsn_code": "84622100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - BENDING, FOLDING,\nSTRAIGHTENING OR FLATTENING MACHINES (INCLUDING PRESSES): OTHER: BENDING AND\nSTRAIGHTENING MACHINES",
+        "hsn_code": "84622910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - BENDING, FOLDING,\nSTRAIGHTENING OR FLATTENING MACHINES (INCLUDING PRESSES): OTHER: PRESS BRAKES",
+        "hsn_code": "84622920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - BENDING, FOLDING,\nSTRAIGHTENING OR FLATTENING MACHINES (INCLUDING PRESSES): OTHER: OTHER ROTARY\nHEAD AND RAM TYPE",
+        "hsn_code": "84622930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - BENDING, FOLDING,\nSTRAIGHTENING OR FLATTENING MACHINES (INCLUDING PRESSES): OTHER: OTHER",
+        "hsn_code": "84622990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE- SHEARING MACHINES\n(INCLUDING PRESSES), OTHER THAN COMBINED PUNCHING AND SHEARING MACHINES :\nNUMERICALLY CONTROLLED",
+        "hsn_code": "84623100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE- SHEARING MACHINES\n(INCLUDING PRESSES), OTHER THAN COMBINED PUNCHING AND SHEARING MACHINES : OTHER:\nPLATE AND SHEET SHEARS (GUILLOTINE)",
+        "hsn_code": "84623910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE- SHEARING MACHINES\n(INCLUDING PRESSES), OTHER THAN COMBINED PUNCHING AND SHEARING MACHINES : OTHER:\nBAR AND ANGLE SHEARING AND CROPPING",
+        "hsn_code": "84623920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE- SHEARING MACHINES\n(INCLUDING PRESSES), OTHER THAN COMBINED PUNCHING AND SHEARING MACHINES : OTHER:\nOTHER",
+        "hsn_code": "84623990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE- PUNCHING OR NOTCHING\nMACHINES (INCLUDING PRESSES), INCLUDING COMBINED PUNCHING AND SHEARING\nMACHINES: NUMERICALLY CONTROLLED",
+        "hsn_code": "84624100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE- PUNCHING OR NOTCHING\nMACHINES (INCLUDING PRESSES), INCLUDING COMBINED PUNCHING AND SHEARING\nMACHINES: OTHER: PUNCHING MACHINES (INCLUDING TURRET)",
+        "hsn_code": "84624910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE- PUNCHING OR NOTCHING\nMACHINES (INCLUDING PRESSES), INCLUDING COMBINED PUNCHING AND SHEARING\nMACHINES: OTHER: COMBINATION OF PUNCHING, SHEARING AND CROPPING MACHINES",
+        "hsn_code": "84624920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE- PUNCHING OR NOTCHING\nMACHINES (INCLUDING PRESSES), INCLUDING COMBINED PUNCHING AND SHEARING\nMACHINES: OTHER: NIBBLING MACHINES",
+        "hsn_code": "84624930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE- PUNCHING OR NOTCHING\nMACHINES (INCLUDING PRESSES), INCLUDING COMBINED PUNCHING AND SHEARING\nMACHINES: OTHER: OTHER",
+        "hsn_code": "84624990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - OTHER - HYDRAULIC PRESSES:\nHYDRAULIC EXTENSION",
+        "hsn_code": "84629110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - OTHER - HYDRAULIC\nPRESSES:OTHER",
+        "hsn_code": "84629190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - OTHER - PNEUMATIC,\nINCLINABLE AND VERTICAL PRESSES: PNEUMATIC PRESSES",
+        "hsn_code": "84629911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - OTHER - PNEUMATIC,\nINCLINABLE AND VERTICAL PRESSES: INCLINABLE PRESSES",
+        "hsn_code": "84629912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - OTHER - PNEUMATIC,\nINCLINABLE AND VERTICAL PRESSES: VERTICAL GAP OF FRAME PRESSES",
+        "hsn_code": "84629913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - OTHER - PNEUMATIC,\nINCLINABLE AND VERTICAL PRESSES: VERTICAL STRAIGHT PRESSES",
+        "hsn_code": "84629914",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - OTHER - PNEUMATIC,\nINCLINABLE AND VERTICAL PRESSES: VERTICAL FORGING PRESSES",
+        "hsn_code": "84629915",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - OTHER - PNEUMATIC,\nINCLINABLE AND VERTICAL PRESSES: OTHER",
+        "hsn_code": "84629919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - OTHER -DIEING OR LOBBING\nMACHINE PRESSES",
+        "hsn_code": "84629920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - OTHER -TRANSFER AND\nMULTIPLE PRESSES",
+        "hsn_code": "84629930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - OTHER - HORIZONTAL PRESSES",
+        "hsn_code": "84629940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - OTHER - FRICTION SCREW\nPRESSES",
+        "hsn_code": "84629950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - OTHER - KNUCKLE JOINT\nPRESSES",
+        "hsn_code": "84629960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - OTHER -COINING JOINT PRESSES",
+        "hsn_code": "84629970",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR\nDIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING,\nFOLDING, STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR\nWORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE - OTHER - OTHER",
+        "hsn_code": "84629990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY FORGING, HAMMERING OR DIE-STAMPING; MACHINE-TOOLS (INCLUDING PRESSES) FOR WORKING METAL BY BENDING, FOLDING STRAIGHTENING, FLATTENING, SHEARING, PUNCHING OR NOTCHING; PRESSES FOR WORKING METAL OR METAL CARBIDES, NOT SPECIFIED ABOVE",
+        "hsn_code": "8462",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - SHAPING OR\nSLOTTING MACHINES: SHAPING MACHINES: DIE AND PUNCH SHAPING MACHINES",
+        "hsn_code": "84612011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - SHAPING OR\nSLOTTING MACHINES: SHAPING MACHINES: OTHER",
+        "hsn_code": "84612019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - SHAPING OR\nSLOTTING MACHINES: SLOTTING MACHINES",
+        "hsn_code": "84612020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - BROACHING\nMACHINES: VERTICAL",
+        "hsn_code": "84613010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - BROACHING\nMACHINES: HORIZONTAL",
+        "hsn_code": "84613020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - BROACHING\nMACHINES: OTHER",
+        "hsn_code": "84613090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - GEAR CUTTING,\nGEAR GRINDING OR GEAR FINISHING MACHINES: GEAR CUTTING MACHINES: BEVEL GEAR\nCUTTING",
+        "hsn_code": "84614011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - GEAR CUTTING,\nGEAR GRINDING OR GEAR FINISHING MACHINES: GEAR CUTTING MACHINES: GEAR CUTTING\nSPIRAL BEVEL AND /OR HYPOID",
+        "hsn_code": "84614012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - GEAR CUTTING,\nGEAR GRINDING OR GEAR FINISHING MACHINES: GEAR CUTTING MACHINES: GEAR SLOTTER OR\nPLANAR FORMED CUTTER TYPE",
+        "hsn_code": "84614013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - GEAR CUTTING,\nGEAR GRINDING OR GEAR FINISHING MACHINES: GEAR CUTTING MACHINES: GEAR MILLING\nFORMED DISC CUTTER TYPE",
+        "hsn_code": "84614014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - GEAR CUTTING,\nGEAR GRINDING OR GEAR FINISHING MACHINES: GEAR CUTTING MACHINES: OTHER",
+        "hsn_code": "84614019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - GEAR CUTTING,\nGEAR GRINDING OR GEAR FINISHING MACHINES- GEAR GRINDING OR GEAR FINISHING\nMACHINES : SINGLE OR DOUBLE WHEEL DISC TYPE GEAR GRINDER",
+        "hsn_code": "84614021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - GEAR CUTTING,\nGEAR GRINDING OR GEAR FINISHING MACHINES- GEAR GRINDING OR GEAR FINISHING\nMACHINES : FORMED WHEEL GEAR GRINDER",
+        "hsn_code": "84614022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - GEAR CUTTING,\nGEAR GRINDING OR GEAR FINISHING MACHINES- GEAR GRINDING OR GEAR FINISHING\nMACHINES : GEAR SHAVER",
+        "hsn_code": "84614023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - GEAR CUTTING,\nGEAR GRINDING OR GEAR FINISHING MACHINES- GEAR GRINDING OR GEAR FINISHING\nMACHINES : GEAR TOOTH, ROUNDING, CHAMFERING OR BURRING",
+        "hsn_code": "84614024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - GEAR CUTTING,\nGEAR GRINDING OR GEAR FINISHING MACHINES- GEAR GRINDING OR GEAR FINISHING\nMACHINES : GEAR SHAPER, SPUR AND HELICAL",
+        "hsn_code": "84614025",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - GEAR CUTTING,\nGEAR GRINDING OR GEAR FINISHING MACHINES- GEAR GRINDING OR GEAR FINISHING\nMACHINES : GEAR HOBBER, SPUR AND HELICAL",
+        "hsn_code": "84614026",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - GEAR CUTTING,\nGEAR GRINDING OR GEAR FINISHING MACHINES- GEAR GRINDING OR GEAR FINISHING\nMACHINES : OTHER",
+        "hsn_code": "84614029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - SAWING OR\nCUTTING-OFF MACHINES: SAWING MACHINES: BAND SAW, HORIZONTAL",
+        "hsn_code": "84615011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - SAWING OR\nCUTTING-OFF MACHINES: SAWING MACHINES: BAND SAW, VERTICAL",
+        "hsn_code": "84615012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - SAWING OR\nCUTTING-OFF MACHINES: SAWING MACHINES: CIRCULAR SAW, COLD",
+        "hsn_code": "84615013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - SAWING OR\nCUTTING-OFF MACHINES: SAWING MACHINES: CIRCULAR SAW, HOT",
+        "hsn_code": "84615014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - SAWING OR\nCUTTING-OFF MACHINES: SAWING MACHINES: HACK SAW",
+        "hsn_code": "84615015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - SAWING OR\nCUTTING-OFF MACHINES: SAWING MACHINES: OTHER",
+        "hsn_code": "84615019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - SAWING OR\nCUTTING-OFF MACHINES: CUTTING-OFF MACHINES: ABRASIVE WHEEL CUTTING-OFF MACHINES",
+        "hsn_code": "84615021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - SAWING OR\nCUTTING-OFF MACHINES: CUTTING-OFF MACHINES: OTHER",
+        "hsn_code": "84615029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR\nGRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING\nBY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER",
+        "hsn_code": "84619000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR PLANING, SHAPING, SLOTTING, BROACHING, GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING, SAWING, CUTTING-OFF AND OTHER MACHINE TOOLS WORKING BY REMOVING METAL, OR CERMETS, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "8461",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR DEBURRING, SHARPENING, GRINDING, HONING, LAPPING, POLISHING OR\nOTHERWISE FINISHING METAL, OR CERMETS BY MEANS OF GRINDING STONES, ABRASIVES OR\nPOLISHING PRODUCTS, OTHER THAN GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING\nMACHINES OF HEADING 8461 - FLAT-SURFACE GRINDING MACHINES, IN WHICH THE\nPOSITIONING IN ANY ONE AXIS CAN BE SET UP TO AN ACCURACY OF AT LEAST 0.01\nMM:NUMERICALLY CONTROLLED",
+        "hsn_code": "84601100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR DEBURRING, SHARPENING, GRINDING, HONING, LAPPING, POLISHING OR\nOTHERWISE FINISHING METAL, OR CERMETS BY MEANS OF GRINDING STONES, ABRASIVES OR\nPOLISHING PRODUCTS, OTHER THAN GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING\nMACHINES OF HEADING 8461 - FLAT-SURFACE GRINDING MACHINES, IN WHICH THE\nPOSITIONING IN ANY ONE AXIS CAN BE SET UP TO AN ACCURACY OF AT LEAST 0.01 MM:OTHER",
+        "hsn_code": "84601900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR DEBURRING, SHARPENING, GRINDING, HONING, LAPPING, POLISHING OR\nOTHERWISE FINISHING METAL, OR CERMETS BY MEANS OF GRINDING STONES, ABRASIVES OR\nPOLISHING PRODUCTS, OTHER THAN GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING\nMACHINES OF HEADING 8461 - OTHER GRINDING MACHINES, IN WHICH THE POSITIONING IN\nANY ONE AXIS CAN BE SET UP TO AN ACCURACY OF AT LEAST 0.01 MM: NUMERICALLY\nCONTROLLED",
+        "hsn_code": "84602100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR DEBURRING, SHARPENING, GRINDING, HONING, LAPPING, POLISHING OR\nOTHERWISE FINISHING METAL, OR CERMETS BY MEANS OF GRINDING STONES, ABRASIVES OR\nPOLISHING PRODUCTS, OTHER THAN GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING\nMACHINES OF HEADING 8461 - OTHER GRINDING MACHINES, IN WHICH THE POSITIONING IN\nANY ONE AXIS CAN BE SET UP TO AN ACCURACY OF AT LEAST 0.01 MM: OTHER: CYLINDRICAL\nGRINDERS",
+        "hsn_code": "84602910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR DEBURRING, SHARPENING, GRINDING, HONING, LAPPING, POLISHING OR\nOTHERWISE FINISHING METAL, OR CERMETS BY MEANS OF GRINDING STONES, ABRASIVES OR\nPOLISHING PRODUCTS, OTHER THAN GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING\nMACHINES OF HEADING 8461 - OTHER GRINDING MACHINES, IN WHICH THE POSITIONING IN\nANY ONE AXIS CAN BE SET UP TO AN ACCURACY OF AT LEAST 0.01 MM: OTHER: INTERNAL\nGRINDERS",
+        "hsn_code": "84602920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR DEBURRING, SHARPENING, GRINDING, HONING, LAPPING, POLISHING OR\nOTHERWISE FINISHING METAL, OR CERMETS BY MEANS OF GRINDING STONES, ABRASIVES OR\nPOLISHING PRODUCTS, OTHER THAN GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING\nMACHINES OF HEADING 8461 - OTHER GRINDING MACHINES, IN WHICH THE POSITIONING IN\nANY ONE AXIS CAN BE SET UP TO AN ACCURACY OF AT LEAST 0.01 MM: OTHER: CENTRELESS\nGRINDERS",
+        "hsn_code": "84602930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR DEBURRING, SHARPENING, GRINDING, HONING, LAPPING, POLISHING OR\nOTHERWISE FINISHING METAL, OR CERMETS BY MEANS OF GRINDING STONES, ABRASIVES OR\nPOLISHING PRODUCTS, OTHER THAN GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING\nMACHINES OF HEADING 8461 - OTHER GRINDING MACHINES, IN WHICH THE POSITIONING IN\nANY ONE AXIS CAN BE SET UP TO AN ACCURACY OF AT LEAST 0.01 MM: OTHER: PROFILE\nGRINDERS",
+        "hsn_code": "84602940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR DEBURRING, SHARPENING, GRINDING, HONING, LAPPING, POLISHING OR\nOTHERWISE FINISHING METAL, OR CERMETS BY MEANS OF GRINDING STONES, ABRASIVES OR\nPOLISHING PRODUCTS, OTHER THAN GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING\nMACHINES OF HEADING 8461 - OTHER GRINDING MACHINES, IN WHICH THE POSITIONING IN\nANY ONE AXIS CAN BE SET UP TO AN ACCURACY OF AT LEAST 0.01 MM: OTHER: OTHER",
+        "hsn_code": "84602990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR DEBURRING, SHARPENING, GRINDING, HONING, LAPPING, POLISHING OR\nOTHERWISE FINISHING METAL, OR CERMETS BY MEANS OF GRINDING STONES, ABRASIVES OR\nPOLISHING PRODUCTS, OTHER THAN GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING\nMACHINES OF HEADING 8461 - SHARPENING (TOOL OR CUTTER GRINDING) MACHINES:\nNUMERICALLY CONTROLLED",
+        "hsn_code": "84603100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR DEBURRING, SHARPENING, GRINDING, HONING, LAPPING, POLISHING OR\nOTHERWISE FINISHING METAL, OR CERMETS BY MEANS OF GRINDING STONES, ABRASIVES OR\nPOLISHING PRODUCTS, OTHER THAN GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING\nMACHINES OF HEADING 8461 - SHARPENING (TOOL OR CUTTER GRINDING) MACHINES: OTHER:\nGRINDER, TOOL OR CUTTER",
+        "hsn_code": "84603910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR DEBURRING, SHARPENING, GRINDING, HONING, LAPPING, POLISHING OR\nOTHERWISE FINISHING METAL, OR CERMETS BY MEANS OF GRINDING STONES, ABRASIVES OR\nPOLISHING PRODUCTS, OTHER THAN GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING\nMACHINES OF HEADING 8461 - SHARPENING (TOOL OR CUTTER GRINDING) MACHINES: OTHER:\nOTHER",
+        "hsn_code": "84603990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR DEBURRING, SHARPENING, GRINDING, HONING, LAPPING, POLISHING OR\nOTHERWISE FINISHING METAL, OR CERMETS BY MEANS OF GRINDING STONES, ABRASIVES OR\nPOLISHING PRODUCTS, OTHER THAN GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING\nMACHINES OF HEADING 8461 - HONING OR LAPPING MACHINES: HONING MACHINES: VERTICAL,\nSINGLE SPINDLE",
+        "hsn_code": "84604011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR DEBURRING, SHARPENING, GRINDING, HONING, LAPPING, POLISHING OR\nOTHERWISE FINISHING METAL, OR CERMETS BY MEANS OF GRINDING STONES, ABRASIVES OR\nPOLISHING PRODUCTS, OTHER THAN GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING\nMACHINES OF HEADING 8461 - HONING OR LAPPING MACHINES: HONING MACHINES: VERTICAL,\nMULTI SPINDLE",
+        "hsn_code": "84604012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR DEBURRING, SHARPENING, GRINDING, HONING, LAPPING, POLISHING OR\nOTHERWISE FINISHING METAL, OR CERMETS BY MEANS OF GRINDING STONES, ABRASIVES OR\nPOLISHING PRODUCTS, OTHER THAN GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING\nMACHINES OF HEADING 8461 - HONING OR LAPPING MACHINES: - HONING MACHINES:\nHORIZONTAL",
+        "hsn_code": "84604013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR DEBURRING, SHARPENING, GRINDING, HONING, LAPPING, POLISHING OR\nOTHERWISE FINISHING METAL, OR CERMETS BY MEANS OF GRINDING STONES, ABRASIVES OR\nPOLISHING PRODUCTS, OTHER THAN GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING\nMACHINES OF HEADING 8461 - HONING OR LAPPING MACHINES: HONING MACHINES: OTHER",
+        "hsn_code": "84604019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR DEBURRING, SHARPENING, GRINDING, HONING, LAPPING, POLISHING OR\nOTHERWISE FINISHING METAL, OR CERMETS BY MEANS OF GRINDING STONES, ABRASIVES OR\nPOLISHING PRODUCTS, OTHER THAN GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING\nMACHINES OF HEADING 8461 - HONING OR LAPPING MACHINES: LAPPING MACHINES",
+        "hsn_code": "84604020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR DEBURRING, SHARPENING, GRINDING, HONING, LAPPING, POLISHING OR\nOTHERWISE FINISHING METAL, OR CERMETS BY MEANS OF GRINDING STONES, ABRASIVES OR\nPOLISHING PRODUCTS, OTHER THAN GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING\nMACHINES OF HEADING 8461 - OTHER: POLISHING AND BUFFING MACHINES",
+        "hsn_code": "84609010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR DEBURRING, SHARPENING, GRINDING, HONING, LAPPING, POLISHING OR\nOTHERWISE FINISHING METAL, OR CERMETS BY MEANS OF GRINDING STONES, ABRASIVES OR\nPOLISHING PRODUCTS, OTHER THAN GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING\nMACHINES OF HEADING 8461 - OTHER: OTHER",
+        "hsn_code": "84609090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR DEBURRING, SHARPENING, GRINDING, HONING, LAPPING, POLISHING OR OTHERWISE FINISHING METAL OR CERMETS BY MEANS OF GRINDING STONES, ABRASIVES OR POLISHING PRODUCTS, OTHER THAN GEAR CUTTING, GEAR GRINDING OR GEAR FINISHING MACHINES OF HEADING 8461",
+        "hsn_code": "8460",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 WAY TYPE UNIT HEAD MACHINES",
+        "hsn_code": "84591000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - OTHER DRILLING MACHINES:NUMERICALLY\nCONTROLLED",
+        "hsn_code": "84592100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - OTHER DRILLING MACHINES: OTHER: BENCH AND\nPILLAR",
+        "hsn_code": "84592910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - OTHER DRILLING MACHINES: OTHER: PILLAR OR\nCOLUMNS, MULTI SPINDLE",
+        "hsn_code": "84592920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - OTHER DRILLING MACHINES: OTHER: RADIAL",
+        "hsn_code": "84592930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - OTHER DRILLING MACHINES: OTHER: DEEP HOLE",
+        "hsn_code": "84592940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - OTHER DRILLING MACHINES: OTHER: MULTI HEAD\nDRILLING MACHINES",
+        "hsn_code": "84592950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - OTHER DRILLING MACHINES: OTHER: OTHER",
+        "hsn_code": "84592990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - OTHER BORING-MILLING MACHINES: NUMERICALLY\nCONTROLLED",
+        "hsn_code": "84593100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - OTHER BORING-MILLING MACHINES: OTHER: VERTICAL\nTURNING OR BORING",
+        "hsn_code": "84593910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "84593990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - OTHER BORING MACHINES : JIG BORING MACHINES\nHORIZONTAL",
+        "hsn_code": "84594010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - OTHER BORING MACHINES : FINE BORING MACHINES,\nHORIZONTAL",
+        "hsn_code": "84594020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - OTHER BORING MACHINES : FINE BORING MACHINES,\nVERTICAL",
+        "hsn_code": "84594030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - OTHER BORING MACHINES : OTHER",
+        "hsn_code": "84594090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - MILLING MACHINE, KNEE TYPE: NUMERICALLY\nCONTROLLED: HORIZONTAL",
+        "hsn_code": "84595110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - MILLING MACHINE, KNEE TYPE: NUMERICALLY\nCONTROLLED: VERTICAL",
+        "hsn_code": "84595120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - MILLING MACHINE, KNEE TYPE: NUMERICALLY\nCONTROLLED: UNIVERSAL",
+        "hsn_code": "84595130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - MILLING MACHINE, KNEE TYPE: NUMERICALLY\nCONTROLLED: OTHER",
+        "hsn_code": "84595190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - MILLING MACHINE, KNEE TYPE: OTHER: HORIZONTAL",
+        "hsn_code": "84595910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - MILLING MACHINE, KNEE TYPE: OTHER: VERTICAL",
+        "hsn_code": "84595920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - MILLING MACHINE, KNEE TYPE: OTHER: UNIVERSAL",
+        "hsn_code": "84595930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - MILLING MACHINE, KNEE TYPE: OTHER: RAM TYPE",
+        "hsn_code": "84595940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - MILLING MACHINE, KNEE TYPE: OTHER: DIE-SINKING OR\nPANTOGRAPH",
+        "hsn_code": "84595950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - MILLING MACHINE, KNEE TYPE: OTHER: OTHER",
+        "hsn_code": "84595990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - OTHER MILLING MACHINES: NUMERICALLY\nCONTROLLED: PIANO MILLING",
+        "hsn_code": "84596110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458 - OTHER MILLING MACHINES: NUMERICALLY\nCONTROLLED: OTHER",
+        "hsn_code": "84596190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458- OTHER: BED TYPE, HORIZONTAL",
+        "hsn_code": "84596910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458- OTHER: BED TYPE, VERTICAL",
+        "hsn_code": "84596920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458- OTHER: PIANO MILLING, SINGLE COLUMN",
+        "hsn_code": "84596930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458- OTHER: PIANO MILLING, DOUBLE COLUMN",
+        "hsn_code": "84596940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458- OTHER: OTHER",
+        "hsn_code": "84596990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458- OTHER THREADING OR TAPPING MACHINES :THREADING\nMACHINES",
+        "hsn_code": "84597010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING,\nMILLING, THREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING\nTURNING CENTRES) OF HEADING 8458- OTHER THREADING OR TAPPING MACHINES :TAPPING\nMACHINES",
+        "hsn_code": "84597020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS (INCLUDING WAY-TYPE UNIT HEAD MACHINES) FOR DRILLING, BORING, MILLING, TREADING OR TAPPING BY REMOVING METAL, OTHER THAN LATHES (INCLUDING TURNING CENTRES) OF HEADING 8458",
+        "hsn_code": "8459",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - HORIZONTAL\nLATHES:NUMERICALLY CONTROLLED",
+        "hsn_code": "84581100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - HORIZONTAL LATHES:-\nOTHER: AUTOMATIC, SINGLE SPINDLE: HORIZONTAL BAR, SWISS TYPE",
+        "hsn_code": "84581911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - HORIZONTAL LATHES:-\nOTHER: AUTOMATIC, SINGLE SPINDLE: BASE SLIDING HEAD TYPE",
+        "hsn_code": "84581912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - HORIZONTAL LATHES:-\nOTHER: AUTOMATIC, SINGLE SPINDLE: HORIZONTAL CHUCKING",
+        "hsn_code": "84581913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - HORIZONTAL LATHES:-\nOTHER: AUTOMATIC, SINGLE SPINDLE: OTHER",
+        "hsn_code": "84581919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - HORIZONTAL LATHES:-\nOTHER: OTHER",
+        "hsn_code": "84581990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - OTHER LATHES: NUMERICALLY\nCONTROLLED",
+        "hsn_code": "84589100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - OTHER LATHES: OTHER:\nAUTOMATIC, MULTI SPINDLE BAR",
+        "hsn_code": "84589910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - OTHER LATHES: OTHER:\nAUTOMATIC, MULTI SPINDLE CHUCKING",
+        "hsn_code": "84589920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - OTHER LATHES: OTHER:\nCAPSTANS, TURRETS, CAPSTAN AND TURRET COMBINATION, COPING, MULTI TOOL AND\nPRODUCTION LATHES: CAPSTANS LATHES",
+        "hsn_code": "84589931",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - OTHER LATHES: OTHER:\nCAPSTANS, TURRETS, CAPSTAN AND TURRET COMBINATION, COPING, MULTI TOOL AND\nPRODUCTION LATHES: TURRETS LATHES",
+        "hsn_code": "84589932",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - OTHER LATHES: OTHER:\nCAPSTANS, TURRETS, CAPSTAN AND TURRET COMBINATION, COPING, MULTI TOOL AND\nPRODUCTION LATHES: CAPSTAN AND TURRET COMBINATION LATHES",
+        "hsn_code": "84589933",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - OTHER LATHES: OTHER:\nCAPSTANS, TURRETS, CAPSTAN AND TURRET COMBINATION, COPING, MULTI TOOL AND\nPRODUCTION LATHES: COPYING LATHES",
+        "hsn_code": "84589934",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - OTHER LATHES: OTHER:\nCAPSTANS, TURRETS, CAPSTAN AND TURRET COMBINATION, COPING, MULTI TOOL AND\nPRODUCTION LATHES: MULTI-TOOL AND PRODUCTION LATHES",
+        "hsn_code": "84589935",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - OTHER LATHES: OTHER:\nCRANKSHAFT, RELIEVING, WHEEL AND AXLE LATHES: CRANKSHAFT LATHES",
+        "hsn_code": "84589941",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - OTHER LATHES: OTHER:\nCRANKSHAFT, RELIEVING, WHEEL AND AXLE LATHES: RELIEVING LATHES",
+        "hsn_code": "84589942",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - OTHER LATHES: OTHER:\nCRANKSHAFT, RELIEVING, WHEEL AND AXLE LATHES: WHEEL AND AXLE LATHES",
+        "hsn_code": "84589943",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - OTHER LATHES: OTHER:\nCENTRE LATHES: TOOL-ROOM TYPE",
+        "hsn_code": "84589951",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - OTHER LATHES: OTHER:\nCENTRE LATHES: OTHER",
+        "hsn_code": "84589959",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL - OTHER LATHES: OTHER:\nOTHER",
+        "hsn_code": "84589990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LATHES (INCLUDING TURNING CENTRES) FOR REMOVING METAL",
+        "hsn_code": "8458",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINING CENTRES, UNIT CONSTRUCTION MACHINES (SINGLE STATION) AND MULTI-STATION\nTRANSFER MACHINES FOR WORKING METAL - MACHINING CENTRES:HORIZONTAL",
+        "hsn_code": "84571010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINING CENTRES, UNIT CONSTRUCTION MACHINES (SINGLE STATION) AND MULTI-STATION\nTRANSFER MACHINES FOR WORKING METAL - MACHINING CENTRES:VERTICAL",
+        "hsn_code": "84571020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINING CENTRES, UNIT CONSTRUCTION MACHINES (SINGLE STATION) AND MULTI-STATION\nTRANSFER MACHINES FOR WORKING METAL - UNIT CONSTRUCTION MACHINES (SINGLE\nSTATION): UNIT HEAD BORING",
+        "hsn_code": "84572010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINING CENTRES, UNIT CONSTRUCTION MACHINES (SINGLE STATION) AND MULTI-STATION\nTRANSFER MACHINES FOR WORKING METAL - UNIT CONSTRUCTION MACHINES (SINGLE\nSTATION): UNIT HEAD DRILLING",
+        "hsn_code": "84572020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINING CENTRES, UNIT CONSTRUCTION MACHINES (SINGLE STATION) AND MULTI-STATION\nTRANSFER MACHINES FOR WORKING METAL - UNIT CONSTRUCTION MACHINES (SINGLE\nSTATION): OTHER",
+        "hsn_code": "84572090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINING CENTRES, UNIT CONSTRUCTION MACHINES (SINGLE STATION) AND MULTI-STATION\nTRANSFER MACHINES FOR WORKING METAL - MULTI-STATION TRANSFER MACHINES:ROTARY\nTYPE",
+        "hsn_code": "84573010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINING CENTRES, UNIT CONSTRUCTION MACHINES (SINGLE STATION) AND MULTI-STATION\nTRANSFER MACHINES FOR WORKING METAL - MULTI-STATION TRANSFER MACHINES: IN-LINE\nTYPE",
+        "hsn_code": "84573020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINING CENTRES, UNIT CONSTRUCTION MACHINES (SINGLE STATION) AND MULTI-STATION\nTRANSFER MACHINES FOR WORKING METAL - MULTI-STATION TRANSFER MACHINES:OTHER",
+        "hsn_code": "84573090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINING CENTRES, UNIT CONSTRUCTION MACHINES (SINGLE STATION) AND MULTI-STATION TRANSFER MACHINES FOR WORKING METAL",
+        "hsn_code": "8457",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR WORKING ANY MATERIAL BY REMOVAL OF MATERIAL, BY LASER OR\nOTHER LIGHT OR PHOTON BEAM, ULTRA-SONIC, ELECTRO-DISCHARGE, ELECTROCHEMICAL,\nELECTRON BEAM, IONIC-BEAM OR PLASMA ARC PROCESSES - OPERATED BY LASER OR OTHER\nLIGHT OR PHOTONBEAM PROCESSES",
+        "hsn_code": "84561000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR WORKING ANY MATERIAL BY REMOVAL OF MATERIAL, BY LASER OR\nOTHER LIGHT OR PHOTON BEAM, ULTRA-SONIC, ELECTRO-DISCHARGE, ELECTROCHEMICAL,\nELECTRON BEAM, IONIC-BEAM OR PLASMA ARC PROCESSES - OPERATED BY ULTRASONIC\nPROCESSES",
+        "hsn_code": "84562000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR WORKING ANY MATERIAL BY REMOVAL OF MATERIAL, BY LASER OR\nOTHER LIGHT OR PHOTON BEAM, ULTRA-SONIC, ELECTRO-DISCHARGE, ELECTROCHEMICAL,\nELECTRON BEAM, IONIC-BEAM OR PLASMA ARC PROCESSES - OPERATED BY ELECTRO\nDISCHARGE PROCESSES",
+        "hsn_code": "84563000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR WORKING ANY MATERIAL BY REMOVAL OF MATERIAL, BY LASER OR\nOTHER LIGHT OR PHOTON BEAM, ULTRA-SONIC, ELECTRO-DISCHARGE, ELECTROCHEMICAL,\nELECTRON BEAM, IONIC-BEAM OR PLASMA ARC PROCESSES - OTHER: --- FOR DRY ETCH PATTERN\nON SEMI CONDUCTOR MATERIAL",
+        "hsn_code": "84569010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR WORKING ANY MATERIAL BY REMOVAL OF MATERIAL, BY LASER OR\nOTHER LIGHT OR PHOTON BEAM, ULTRA-SONIC, ELECTRO-DISCHARGE, ELECTROCHEMICAL,\nELECTRON BEAM, IONIC-BEAM OR PLASMA ARC PROCESSES - OTHER: --- ELECTRO CHEMICAL\nMACHINES",
+        "hsn_code": "84569020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR WORKING ANY MATERIAL BY REMOVAL OF MATERIAL, BY LASER OR\nOTHER LIGHT OR PHOTON BEAM, ULTRA-SONIC, ELECTRO-DISCHARGE, ELECTROCHEMICAL,\nELECTRON BEAM, IONIC-BEAM OR PLASMA ARC PROCESSES - OTHER: --- OTHER",
+        "hsn_code": "84569090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR WORKING ANY MATERIAL BY REMOVAL OF MATERIAL, BY LASER OR\nOTHER LIGHT OR PHOTON BEAM, ULTRA-SONIC, ELECTRO-DISCHARGE, ELECTROCHEMICAL,\nELECTRON BEAM, IONIC-BEAM OR PLASMA ARC PROCESSES - OTHER: FOR DRY-ETCHING\nPATTERNS ON SEMI-CONDUCTOR MATERIALS",
+        "hsn_code": "84569100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR WORKING ANY MATERIAL BY REMOVAL OF MATERIAL, BY LASER OR\nOTHER LIGHT OR PHOTON BEAM, ULTRA-SONIC, ELECTRO-DISCHARGE, ELECTROCHEMICAL,\nELECTRON BEAM, IONIC-BEAM OR PLASMA ARC PROCESSES - OTHER: OTHER - ELECTRO\nCHEMICAL MACHINES",
+        "hsn_code": "84569910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR WORKING ANY MATERIAL BY REMOVAL OF MATERIAL, BY LASER OR\nOTHER LIGHT OR PHOTON BEAM, ULTRA-SONIC, ELECTRO-DISCHARGE, ELECTROCHEMICAL,\nELECTRON BEAM, IONIC-BEAM OR PLASMA ARC PROCESSES - OTHER: OTHER - OTHER",
+        "hsn_code": "84569990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINE-TOOLS FOR WORKING ANY MATERIAL BY REMOVAL OF MATERIAL, BY LASER OR OTHER LIGHT OR PHOTON BEAM, ULTRA-SONIC, ELECTRO-DISCHARGE, ELECTRO-CHEMICAL, ELECTRON BEAM, IONIC-BEAM OR PLASMA ARC PROCESS; WATER-JET CUTTING MACHINES:",
+        "hsn_code": "8456",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "METAL-ROLLING MILLS AND ROLLS THEREFOR TUBE MILLS",
+        "hsn_code": "84551000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "METAL-ROLLING MILLS AND ROLLS THEREFOR - OTHER ROLLING MILLS: HOT OR COMBINATION\nHOT AND COLD: HOT",
+        "hsn_code": "84552110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "METAL-ROLLING MILLS AND ROLLS THEREFOR - OTHER ROLLING MILLS: HOT OR COMBINATION\nHOT AND COLD: COMBINATION HOT AND COLD",
+        "hsn_code": "84552120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "METAL-ROLLING MILLS AND ROLLS THEREFOR - OTHER ROLLING MILLS: COLD",
+        "hsn_code": "84552200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "METAL-ROLLING MILLS AND ROLLS THEREFOR - ROLLS FOR ROLLING MILLS",
+        "hsn_code": "84553000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "METAL-ROLLING MILLS AND ROLLS THEREFOR - OTHER PARTS",
+        "hsn_code": "84559000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "METAL-ROLLING MILLS AND ROLLS THEREFOR",
+        "hsn_code": "8455",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVERTERS, LADLES, INGOT MOULDS AND CASTING MACHINES, OF A KIND USED IN\nMETALLURGY OR IN METAL FOUNDRIES CONVERTERS",
+        "hsn_code": "84541000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVERTERS, LADLES, INGOT MOULDS AND CASTING MACHINES, OF A KIND USED IN\nMETALLURGY OR IN METAL FOUNDRIES - INGOT MOULDS AND LADLES : LADLES",
+        "hsn_code": "84542010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVERTERS, LADLES, INGOT MOULDS AND CASTING MACHINES, OF A KIND USED IN\nMETALLURGY OR IN METAL FOUNDRIES - INGOT MOULDS AND LADLES : INGOT MOULDS",
+        "hsn_code": "84542020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVERTERS, LADLES, INGOT MOULDS AND CASTING MACHINES, OF A KIND USED IN\nMETALLURGY OR IN METAL FOUNDRIES - CASTING MACHINES: DIE-CASTING MACHINES",
+        "hsn_code": "84543010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVERTERS, LADLES, INGOT MOULDS AND CASTING MACHINES, OF A KIND USED IN\nMETALLURGY OR IN METAL FOUNDRIES - CASTING MACHINES: CONTINUOUS CASTING\nMACHINES",
+        "hsn_code": "84543020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVERTERS, LADLES, INGOT MOULDS AND CASTING MACHINES, OF A KIND USED IN\nMETALLURGY OR IN METAL FOUNDRIES - CASTING MACHINES: OTHER",
+        "hsn_code": "84543090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVERTERS, LADLES, INGOT MOULDS AND CASTING MACHINES, OF A KIND USED IN\nMETALLURGY OR IN METAL FOUNDRIES PARTS",
+        "hsn_code": "84549000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVERTERS, LADLES, INGOT MOULDS AND CASTING MACHINES, OF A KIND USED IN METALLURGY OR IN METAL FOUNDRIES",
+        "hsn_code": "8454",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR PREPARING, TANNING OR WORKING HIDES, SKINS OR LEATHER OR FOR\nMAKING OR REPAIRING FOOTWEAR OR OTHER ARTICLES OF HIDES, SKINS OR LEATHER, OTHER\nTHAN SEWING MACHINES - MACHINERY FOR PREPARING, TANNING OR WORKINGHIDES, SKINS\nOR LEATHER",
+        "hsn_code": "84531000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR PREPARING, TANNING OR WORKING HIDES, SKINS OR LEATHER OR FOR\nMAKING OR REPAIRING FOOTWEAR OR OTHER ARTICLES OF HIDES, SKINS OR LEATHER, OTHER\nTHAN SEWING MACHINES - MACHINERY FOR MAKING OR REPAIRING FOOTWEAR",
+        "hsn_code": "84532000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR PREPARING, TANNING OR WORKING HIDES, SKINS OR LEATHER OR FOR\nMAKING OR REPAIRING FOOTWEAR OR OTHER ARTICLES OF HIDES, SKINS OR LEATHER, OTHER\nTHAN SEWING MACHINES - OTHER MACHINERY",
+        "hsn_code": "84538000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR PREPARING, TANNING OR WORKING HIDES, SKINS OR LEATHER OR FOR\nMAKING OR REPAIRING FOOTWEAR OR OTHER ARTICLES OF HIDES, SKINS OR LEATHER, OTHER\nTHAN SEWING MACHINES - PARTS : OF BOOT AND SHOE MANUFACTURING MACHINERY",
+        "hsn_code": "84539010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR PREPARING, TANNING OR WORKING HIDES, SKINS OR LEATHER OR FOR\nMAKING OR REPAIRING FOOTWEAR OR OTHER ARTICLES OF HIDES, SKINS OR LEATHER, OTHER\nTHAN SEWING MACHINES - PARTS : OTHER",
+        "hsn_code": "84539090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR PREPARING, TANNING OR WORKING HIDES, SKINS OR LEATHER OR FOR MAKING OR REPAIRING FOOTWEAR OR OTHER ARTICLES OF HIDES, SKINS OR LEATHER, OTHER THAN SEWING MACHINES",
+        "hsn_code": "8453",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEWING MACHINES, OTHER THAN BOOK-SEWING MACHINES OF HEADING 8440; FURNITURE,\nBASES AND COVERS SPECIALLY DESIGNED FOR SEWING MACHINES; SEWING MACHINE NEEDLES -\nSEWING MACHINES OF THE HOUSEHOLD TYPE: COMPLETE, WITH STAND OR TABLE:WITH\nELECTRONIC CONTROLS OR ELECTRIC MOTORS",
+        "hsn_code": "84521011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEWING MACHINES, OTHER THAN BOOK-SEWING MACHINES OF HEADING 8440; FURNITURE,\nBASES AND COVERS SPECIALLY DESIGNED FOR SEWING MACHINES; SEWING MACHINE NEEDLES -\nSEWING MACHINES OF THE HOUSEHOLD TYPE: COMPLETE, WITH STAND OR TABLE:HAND\nOPERATED",
+        "hsn_code": "84521012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEWING MACHINES, OTHER THAN BOOK-SEWING MACHINES OF HEADING 8440; FURNITURE,\nBASES AND COVERS SPECIALLY DESIGNED FOR SEWING MACHINES; SEWING MACHINE NEEDLES -\nSEWING MACHINES OF THE HOUSEHOLD TYPE: COMPLETE, WITH STAND OR TABLE:OTHER",
+        "hsn_code": "84521019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEWING MACHINES, OTHER THAN BOOK-SEWING MACHINES OF HEADING 8440; FURNITURE,\nBASES AND COVERS SPECIALLY DESIGNED FOR SEWING MACHINES; SEWING MACHINE NEEDLES\n8452 10 - SEWING MACHINES OF THE HOUSEHOLD TYPE:- WITHOUT STAND OR TABLE (HEADS):\nWITH ELECTRONIC CONTROLS OR ELECTRIC MOTORS",
+        "hsn_code": "84521021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEWING MACHINES, OTHER THAN BOOK-SEWING MACHINES OF HEADING 8440; FURNITURE,\nBASES AND COVERS SPECIALLY DESIGNED FOR SEWING MACHINES; SEWING MACHINE NEEDLES\n8452 10 - SEWING MACHINES OF THE HOUSEHOLD TYPE:- WITHOUT STAND OR TABLE (HEADS):\nHAND OPERATED",
+        "hsn_code": "84521022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEWING MACHINES, OTHER THAN BOOK-SEWING MACHINES OF HEADING 8440; FURNITURE,\nBASES AND COVERS SPECIALLY DESIGNED FOR SEWING MACHINES; SEWING MACHINE NEEDLES\n8452 10 - SEWING MACHINES OF THE HOUSEHOLD TYPE:- WITHOUT STAND OR TABLE (HEADS):\nOTHER",
+        "hsn_code": "84521029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEWING MACHINES, OTHER THAN BOOK-SEWING MACHINES OF HEADING 8440; FURNITURE,\nBASES AND COVERS SPECIALLY DESIGNED FOR SEWING MACHINES; SEWING MACHINE NEEDLES -\nOTHER SEWING MACHINES: AUTOMATIC UNITS: INDUSTRIAL SEWING MACHINES HAVING A\nMOTOR OF 150 WATTS CAPACITY AND ABOVE AND HAVING A SPEED OF 1,500 STITCHES PER\nMINUTE OR MORE",
+        "hsn_code": "84522110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEWING MACHINES, OTHER THAN BOOK-SEWING MACHINES OF HEADING 8440; FURNITURE,\nBASES AND COVERS SPECIALLY DESIGNED FOR SEWING MACHINES; SEWING MACHINE NEEDLES -\nOTHER SEWING MACHINES: AUTOMATIC UNITS: OTHER WITH ELECTRONIC CONTROLS OR\nELECTRIC MOTORS",
+        "hsn_code": "84522120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEWING MACHINES, OTHER THAN BOOK-SEWING MACHINES OF HEADING 8440; FURNITURE,\nBASES AND COVERS SPECIALLY DESIGNED FOR SEWING MACHINES; SEWING MACHINE NEEDLES -\nOTHER SEWING MACHINES: AUTOMATIC UNITS: OTHER",
+        "hsn_code": "84522190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEWING MACHINES, OTHER THAN BOOK-SEWING MACHINES OF HEADING 8440; FURNITURE,\nBASES AND COVERS SPECIALLY DESIGNED FOR SEWING MACHINES; SEWING MACHINE NEEDLES\nOTHER",
+        "hsn_code": "84522900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEWING MACHINES, OTHER THAN BOOK-SEWING MACHINES OF HEADING 8440; FURNITURE,\nBASES AND COVERS SPECIALLY DESIGNED FOR SEWING MACHINES; SEWING MACHINE NEEDLES -\nSEWING MACHINE NEEDLES : FOR HOUSEHOLD TYPE SEWING MACHINES",
+        "hsn_code": "84523010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEWING MACHINES, OTHER THAN BOOK-SEWING MACHINES OF HEADING 8440; FURNITURE,\nBASES AND COVERS SPECIALLY DESIGNED FOR SEWING MACHINES; SEWING MACHINE NEEDLES -\nSEWING MACHINE NEEDLES : OTHER",
+        "hsn_code": "84523090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FURNITURE, BASES AND COVERS",
+        "hsn_code": "84529011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF FURNITURE, BASES AND COVER FOR SEWING MACHINES",
+        "hsn_code": "84529019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OF HOUSEHOLD SEWING MACHINES",
+        "hsn_code": "84529091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "84529099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEWING MACHINES, OTHER THAN BOOK-SEWING MACHINES OF HEADING 8440; FURNITURE, BASES AND COVERS SPECIALLY DESIGNED FOR SEWING MACHINES; SEWING MACHINE NEEDLES",
+        "hsn_code": "8452",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS - DRY-CLEANING\nMACHINES: DRY-CLEANING MACHINES FOR COTTON TEXTILE",
+        "hsn_code": "84511010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS - DRY-CLEANING\nMACHINES:OTHER",
+        "hsn_code": "84511090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS- DRYING\nMACHINES:EACH OF A DRY LINEN CAPACITY NOT EXCEEDING10 KG",
+        "hsn_code": "84512100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS- DRYING\nMACHINES:OTHER",
+        "hsn_code": "84512900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS - IRONING\nMACHINES AND PRESSES (INCLUDING FUSING PRESSES): HAND IRONING PRESS",
+        "hsn_code": "84513010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS - IRONING\nMACHINES AND PRESSES (INCLUDING FUSING PRESSES): OTHER",
+        "hsn_code": "84513090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS- WASHING,\nBLEACHING OR DYEING MACHINES: BLEACHING MACHINE:FOR COTTON TEXTILE",
+        "hsn_code": "84514011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS- WASHING,\nBLEACHING OR DYEING MACHINES: BLEACHING MACHINE:OTHER",
+        "hsn_code": "84514019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS- WASHING,\nBLEACHING OR DYEING MACHINES: DYEING MACHINES: FOR COTTON TEXTILE",
+        "hsn_code": "84514021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS- WASHING,\nBLEACHING OR DYEING MACHINES: DYEING MACHINES: FOR OTHER TEXTILE",
+        "hsn_code": "84514029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS - WASHING,\nBLEACHING OR DYEING MACHINES: OTHER: FOR WASHING AND CLEANING FOR WOOLLEN\nTEXTILE",
+        "hsn_code": "84514091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS - WASHING,\nBLEACHING OR DYEING MACHINES: OTHER: OTHER",
+        "hsn_code": "84514099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS - MACHINES FOR\nREELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS",
+        "hsn_code": "84515000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS - OTHER\nMACHINERY: SIZING AND DRESSING MACHINES: FOR COTTON TEXTILE",
+        "hsn_code": "84518011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS - OTHER\nMACHINERY: SIZING AND DRESSING MACHINES: OTHER",
+        "hsn_code": "84518019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS - OTHER\nMACHINERY: FINISHING MACHINES: FOR COATING OR IMPREGNATING YARNOR FABRICS",
+        "hsn_code": "84518021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS - OTHER\nMACHINERY: FINISHING MACHINES: OTHER FINISHING PROCESSES FOR COTTON TEXTILE",
+        "hsn_code": "84518022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS - OTHER\nMACHINERY: FINISHING MACHINES: OTHER",
+        "hsn_code": "84518029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS - OTHER\nMACHINERY - OTHER",
+        "hsn_code": "84518090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING,\nWRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING,\nDRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP\nTEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER\nSUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES\nFOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS PARTS",
+        "hsn_code": "84519000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY (OTHER THAN MACHINES OF HEADING 8450) FOR WASHING, CLEANING, WRINGING, DRYING, IRONING, PRESSING (INCLUDING FUSING PRESSES), BLEACHING, DYEING, DRESSING, FINISHING, COATING OR IMPREGNATING TEXTILE YARNS, FABRICS OR MADE UP TEXTILE ARTICLES AND MACHINES FOR APPLYING THE PASTE TO THE BASE FABRIC OR OTHER SUPPORT USED IN THE MANUFACTURE OF FLOOR COVERINGS SUCH AS LINOLEUM; MACHINES FOR REELING, UNREELING, FOLDING, CUTTING OR PINKING TEXTILE FABRICS",
+        "hsn_code": "8451",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HOUSEHOLD OR LAUNDRY-TYPE WASHING MACHINES, INCLUDING MACHINES WHICH BOTH\nWASH AND DRY - MACHINES, EACH OF A DRY LINEN CAPACITY NOT EXCEEDING 10 KG: FULLY\nAUTOMATIC MACHINES",
+        "hsn_code": "84501100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HOUSEHOLD OR LAUNDRY-TYPE WASHING MACHINES, INCLUDING MACHINES WHICH BOTH\nWASH AND DRY - MACHINES, EACH OF A DRY LINEN CAPACITY NOT EXCEEDING 10 KG: OTHER\nMACHINES, WITH BUILT-IN CENTRIFUGAL DRIER",
+        "hsn_code": "84501200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HOUSEHOLD OR LAUNDRY-TYPE WASHING MACHINES, INCLUDING MACHINES WHICH BOTH\nWASH AND DRY - MACHINES, EACH OF A DRY LINEN CAPACITY NOT EXCEEDING 10 KG: OTHER",
+        "hsn_code": "84501900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HOUSEHOLD OR LAUNDRY-TYPE WASHING MACHINES, INCLUDING MACHINES WHICH BOTH\nWASH AND DRY MACHINES, EACH OF A DRY LINEN CAPACITY EXCEEDING 10 KG",
+        "hsn_code": "84502000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HOUSEHOLD OR LAUNDRY-TYPE WASHING MACHINES, INCLUDING MACHINES WHICH BOTH\nWASH AND DRY - PARTS: PARTS OF HOUSEHOLD TYPE MACHINES",
+        "hsn_code": "84509010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HOUSEHOLD OR LAUNDRY-TYPE WASHING MACHINES, INCLUDING MACHINES WHICH BOTH\nWASH AND DRY - PARTS: OTHER",
+        "hsn_code": "84509090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HOUSEHOLD OR LAUNDRY-TYPE WASHING MACHINES, INCLUDING MACHINES WHICH BOTH WASH AND DRY",
+        "hsn_code": "8450",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR THE MANUFACTURE OR FINISHING OF FELT OR NONWOVENS IN THE PIECE OR\nIN SHAPES, INCLUDING MACHINERY FOR MAKING FELT HATS; BLOCKS FOR MAKING HATS 8449\n00 - MACHINERY FOR THE MANUFACTURE OR FINISHING OF FELT OR NONWOVENS IN THE PIECE\nOR IN SHAPES, INCLUDING MACHINERY FOR MAKING FELT HATS; BLOCKS FOR MAKING HATS:\nMACHINERY FOR MANUFACTURE OR FINISHING OF FELT IN PIECE OR IN SHAPES (INCLUDING\nFELT HAT-MAKING MACHINES AND HAT MAKING BLOCKS)",
+        "hsn_code": "84490010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR THE MANUFACTURE OR FINISHING OF FELT OR NONWOVENS IN THE PIECE OR\nIN SHAPES, INCLUDING MACHINERY FOR MAKING FELT HATS; BLOCKS FOR MAKING HATS 8449\n00 - MACHINERY FOR THE MANUFACTURE OR FINISHING OF FELT OR NONWOVENS IN THE PIECE\nOR IN SHAPES, INCLUDING MACHINERY FOR MAKING FELT HATS; BLOCKS FOR MAKING HATS:\nOTHER",
+        "hsn_code": "84490090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR THE MANUFACTURE OR FINISHING OF FELT OR NONWOVENS IN THE PIECE OR IN SHAPES, INCLUDING MACHINERY FOR MAKING FELT HATS; BLOCKS FOR MAKING HATS",
+        "hsn_code": "8449",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - AUXILIARY MACHINERY FOR MACHINES OF\nHEADING 8444, 8445, 8446 OR 8447: DOBBIES AND JACQUARDS; CARD REDUCING, COPYING,\nPUNCHING OR ASSEMBLING MACHINES FOR USE THEREWITH: JACQUARDS AND HARNESS LINER\nCARDS FOR COTTON TEXTILE MACHINERY",
+        "hsn_code": "84481110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - AUXILIARY MACHINERY FOR MACHINES OF\nHEADING 8444, 8445, 8446 OR 8447: DOBBIES AND JACQUARDS; CARD REDUCING, COPYING,\nPUNCHING OR ASSEMBLING MACHINES FOR USE THEREWITH: OTHER",
+        "hsn_code": "84481190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - AUXILIARY MACHINERY FOR MACHINES OF\nHEADING 8444, 8445, 8446 OR 8447: OTHER",
+        "hsn_code": "84481900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8444 OR OF THEIR AUXILIARY MACHINERY",
+        "hsn_code": "84482000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8445 OR OF THEIR AUXILIARY MACHINERY: CARD CLOTHING",
+        "hsn_code": "84483100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8445 OR OF THEIR AUXILIARY MACHINERY: OF MACHINES FOR PREPARING TEXTILE\nFIBRES, OTHER THAN CARD CLOTHING: FOR COTTON PROCESSING MACHINES",
+        "hsn_code": "84483210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8445 OR OF THEIR AUXILIARY MACHINERY: OF MACHINES FOR PREPARING TEXTILE\nFIBRES, OTHER THAN CARD CLOTHING: FOR JUTE PROCESSING MACHINES",
+        "hsn_code": "84483220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8445 OR OF THEIR AUXILIARY MACHINERY: OF MACHINES FOR PREPARING TEXTILE\nFIBRES, OTHER THAN CARD CLOTHING: FOR SILK AND MANMADE (REGENERATED AND\nSYNTHETIC FIBRES PROCESSING MACHINES)",
+        "hsn_code": "84483230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8445 OR OF THEIR AUXILIARY MACHINERY: OF MACHINES FOR PREPARING TEXTILE\nFIBRES, OTHER THAN CARD CLOTHING: FOR WOOL PROCESSING MACHINES",
+        "hsn_code": "84483240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8445 OR OF THEIR AUXILIARY MACHINERY: OF MACHINES FOR PREPARING TEXTILE\nFIBRES, OTHER THAN CARD CLOTHING: OTHER",
+        "hsn_code": "84483290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8445 OR OF THEIR AUXILIARY MACHINERY: SPINDLES, SPINDLE FLYERS, SPINNING\nRINGS AND RING TRAVELLERS: FOR COTTON SPINNING MACHINES",
+        "hsn_code": "84483310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8445 OR OF THEIR AUXILIARY MACHINERY: SPINDLES, SPINDLE FLYERS, SPINNING\nRINGS AND RING TRAVELLERS:FOR JUTE SPINNING MACHINES",
+        "hsn_code": "84483320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8445 OR OF THEIR AUXILIARY MACHINERY: SPINDLES, SPINDLE FLYERS, SPINNING\nRINGS AND RING TRAVELLERS:FOR SILK AND MAN- MADE (REGENERATED AND SYNTHETIC) FIBRE\nSPINNING MACHINES",
+        "hsn_code": "84483330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8445 OR OF THEIR AUXILIARY MACHINERY: SPINDLES, SPINDLE FLYERS, SPINNING\nRINGS AND RING TRAVELLERS:FOR WOOL SPINNING MACHINES",
+        "hsn_code": "84483340",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8445 OR OF THEIR AUXILIARY MACHINERY: SPINDLES, SPINDLE FLYERS, SPINNING\nRINGS AND RING TRAVELLERS:FOR OTHER TEXTILE FIBRE SPINNING MACHINES",
+        "hsn_code": "84483390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8445 OR OF THEIR AUXILIARY MACHINERY: OTHER: COMBS FOR COTTON TEXTILE\nMACHINERY",
+        "hsn_code": "84483910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8445 OR OF THEIR AUXILIARY MACHINERY: OTHER: GILLS FOR GILL BOXES",
+        "hsn_code": "84483920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8445 OR OF THEIR AUXILIARY MACHINERY: OTHER: OTHER",
+        "hsn_code": "84483990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF WEAVING\nMACHINES (LOOMS) OR OF THEIR AUXILIARY MACHINERY : 8448 41 - SHUTTLES: OF COTTON\nWEAVING MACHINERY",
+        "hsn_code": "84484110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF WEAVING\nMACHINES (LOOMS) OR OF THEIR AUXILIARY MACHINERY : 8448 41 - SHUTTLES:OF JUTE\nWEAVING MACHINERY",
+        "hsn_code": "84484120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF WEAVING\nMACHINES (LOOMS) OR OF THEIR AUXILIARY MACHINERY : 8448 41 - SHUTTLES: OF SILK AND\nMAN-MADE FIBRES WEAVING MACHINERY",
+        "hsn_code": "84484130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF WEAVING\nMACHINES (LOOMS) OR OF THEIR AUXILIARY MACHINERY : 8448 41 - SHUTTLES: OF WOOL\nWEAVING MACHINERY",
+        "hsn_code": "84484140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF WEAVING\nMACHINES (LOOMS) OR OF THEIR AUXILIARY MACHINERY : 8448 41 - SHUTTLES:OF OTHER\nTEXTILE FIBRES WEAVING MACHINERY",
+        "hsn_code": "84484150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF WEAVING\nMACHINES (LOOMS) OR OF THEIR AUXILIARY MACHINERY : 8448 41 - SHUTTLES: OF AUXILIARY\nMACHINERY OF HEADING 8448",
+        "hsn_code": "84484160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF WEAVING\nMACHINES (LOOMS) OR OF THEIR AUXILIARY MACHINERY : 8448 41 - SHUTTLES:OTHER",
+        "hsn_code": "84484190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "42 AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447\n(FOR EXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF WEAVING\nMACHINES (LOOMS) OR OF THEIR AUXILIARY MACHINERY - REEDS FOR LOOMS, HEALDS AND\nHEALD-FRAMES: HEALDS (EXCLUDING WIRE HEALDS) AND REEDS FOR COTTON MACHINERY",
+        "hsn_code": "84484210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF WEAVING\nMACHINES (LOOMS) OR OF THEIR AUXILIARY MACHINERY - REEDS FOR LOOMS, HEALDS AND\nHEALD-FRAMES: HEALDS, WIRE",
+        "hsn_code": "84484220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF WEAVING\nMACHINES (LOOMS) OR OF THEIR AUXILIARY MACHINERY - REEDS FOR LOOMS, HEALDS AND\nHEALD-FRAMES: OTHER",
+        "hsn_code": "84484290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF WEAVING\nMACHINES (LOOMS) OR OF THEIR AUXILIARY MACHINERY - OTHER: PARTS OF COTTON WEAVING\nMACHINERY",
+        "hsn_code": "84484910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF WEAVING\nMACHINES (LOOMS) OR OF THEIR AUXILIARY MACHINERY - OTHER: PARTS OF JUTE WEAVING\nMACHINERY",
+        "hsn_code": "84484920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF WEAVING\nMACHINES (LOOMS) OR OF THEIR AUXILIARY MACHINERY - OTHER: PARTS OF SILK AND MAN\nMADE FIBRES WEAVING MACHINERY",
+        "hsn_code": "84484930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF WEAVING\nMACHINES (LOOMS) OR OF THEIR AUXILIARY MACHINERY - OTHER: PARTS OF WOOL WEAVING\nMACHINERY",
+        "hsn_code": "84484940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF WEAVING\nMACHINES (LOOMS) OR OF THEIR AUXILIARY MACHINERY - OTHER: PARTS OF OTHER TEXTILE\nWEAVING MACHINERY",
+        "hsn_code": "84484950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF WEAVING\nMACHINES (LOOMS) OR OF THEIR AUXILIARY MACHINERY - OTHER: OTHER",
+        "hsn_code": "84484990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8447 OR OF THEIR AUXILIARY MACHINERY: 8448 51 - SINKERS, NEEDLES AND OTHER\nARTICLES USED IN FORMING STITCHES: OF COTTON HOSIERY MACHINE",
+        "hsn_code": "84485110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8447 OR OF THEIR AUXILIARY MACHINERY: 8448 51 - SINKERS, NEEDLES AND OTHER\nARTICLES USED IN FORMING STITCHES: OF WOOL KNITTING MACHINES",
+        "hsn_code": "84485120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8447 OR OF THEIR AUXILIARY MACHINERY: 8448 51 - SINKERS, NEEDLES AND OTHER\nARTICLES USED IN FORMING STITCHES: OF MACHINES FOR TULLE, LACE",
+        "hsn_code": "84485130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8447 OR OF THEIR AUXILIARY MACHINERY: 8448 51 - SINKERS, NEEDLES AND OTHER\nARTICLES USED IN FORMING STITCHES: OTHER",
+        "hsn_code": "84485190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADING 8444, 8445, 8446 OR 8447 (FOR\nEXAMPLE, DOBBIES, JACQUARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING\nMECHANISMS); PARTS AND ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE\nMACHINES OF THIS HEADING OR OF HEADING 8444, 8445, 8446 OR 8447 (FOR EXAMPLE,\nSPINDLES AND SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES, SHUTTLES,\nHEALDS AND HEALD FRAMES, HOSIERY NEEDLES) - PARTS AND ACCESSORIES OF MACHINES OF\nHEADING 8447 OR OF THEIR AUXILIARY MACHINERY: 8448 51 OTHER",
+        "hsn_code": "84485900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY MACHINERY FOR USE WITH MACHINES OF HEADINGS 8444, 8445, 8446 OR 8447 (FOR EXAMPLE, DOBBIES, JACQUAARDS, AUTOMATIC STOP MOTIONS, SHUTTLE CHANGING MECHANISMS); PARTS & ACCESSORIES SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINES OF THIS HEADING OR OF HEADINGS 8444, 8445, 8446 OR 8447 (FOR EXAMPLE, SPINDLES & SPINDLE FLYERS, CARD CLOTHING, COMBS, EXTRUDING NIPPLES SHUTTLES, HEALDS & HEALD FRAME, HOSIERY NEEDLES)",
+        "hsn_code": "8448",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNITTING MACHINES, STITCH-BONDING MACHINES AND MACHINES FOR MAKING GIMPED YARN,\nTULLE, LACE, EMBROIDERY, TRIMMINGS, BRAID OR NET AND MACHINES FOR TUFTING -\nCIRCULAR KNITTING MACHINES - WITH CYLINDER DIAMETER NOT EXCEEDING 165 MM: WOOL\nKNITTING MACHINES : HAND KNITTING MACHINES",
+        "hsn_code": "84471111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNITTING MACHINES, STITCH-BONDING MACHINES AND MACHINES FOR MAKING GIMPED YARN,\nTULLE, LACE, EMBROIDERY, TRIMMINGS, BRAID OR NET AND MACHINES FOR TUFTING -\nCIRCULAR KNITTING MACHINES - WITH CYLINDER DIAMETER NOT EXCEEDING 165 MM: WOOL\nKNITTING MACHINES : OTHER",
+        "hsn_code": "84471119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNITTING MACHINES, STITCH-BONDING MACHINES AND MACHINES FOR MAKING GIMPED YARN,\nTULLE, LACE, EMBROIDERY, TRIMMINGS, BRAID OR NET AND MACHINES FOR TUFTING -\nCIRCULAR KNITTING MACHINES - WITH CYLINDER DIAMETER NOT EXCEEDING 165 MM: COTTON\nHOSIERY MACHINES",
+        "hsn_code": "84471120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNITTING MACHINES, STITCH-BONDING MACHINES AND MACHINES FOR MAKING GIMPED YARN,\nTULLE, LACE, EMBROIDERY, TRIMMINGS, BRAID OR NET AND MACHINES FOR TUFTING -\nCIRCULAR KNITTING MACHINES - WITH CYLINDER DIAMETER NOT EXCEEDING 165 MM: OTHER",
+        "hsn_code": "84471190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNITTING MACHINES, STITCH-BONDING MACHINES AND MACHINES FOR MAKING GIMPED YARN,\nTULLE, LACE, EMBROIDERY, TRIMMINGS, BRAID OR NET AND MACHINES FOR TUFTING -\nCIRCULAR KNITTING MACHINES - WITH CYLINDER DIAMETER EXCEEDING 165 MM: WOOL\nKNITTING MACHINES: HAND KNITTING MACHINES",
+        "hsn_code": "84471211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNITTING MACHINES, STITCH-BONDING MACHINES AND MACHINES FOR MAKING GIMPED YARN,\nTULLE, LACE, EMBROIDERY, TRIMMINGS, BRAID OR NET AND MACHINES FOR TUFTING -\nCIRCULAR KNITTING MACHINES - WITH CYLINDER DIAMETER EXCEEDING 165 MM: WOOL\nKNITTING MACHINES: OTHER",
+        "hsn_code": "84471219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNITTING MACHINES, STITCH-BONDING MACHINES AND MACHINES FOR MAKING GIMPED YARN,\nTULLE, LACE, EMBROIDERY, TRIMMINGS, BRAID OR NET AND MACHINES FOR TUFTING -\nCIRCULAR KNITTING MACHINES - WITH CYLINDER DIAMETER EXCEEDING 165 MM: COTTON\nHOSIERY MACHINES",
+        "hsn_code": "84471220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNITTING MACHINES, STITCH-BONDING MACHINES AND MACHINES FOR MAKING GIMPED YARN,\nTULLE, LACE, EMBROIDERY, TRIMMINGS, BRAID OR NET AND MACHINES FOR TUFTING -\nCIRCULAR KNITTING MACHINES - WITH CYLINDER DIAMETER EXCEEDING 165 MM: OTHER",
+        "hsn_code": "84471290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNITTING MACHINES, STITCH-BONDING MACHINES AND MACHINES FOR MAKING GIMPED YARN,\nTULLE, LACE, EMBROIDERY, TRIMMINGS, BRAID OR NET AND MACHINES FOR TUFTING - FLAT\nKNITING MACHINES; STITCH-BONDING MACHINES: HAND KNITTING MACHINES FOR WOOL",
+        "hsn_code": "84472010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNITTING MACHINES, STITCH-BONDING MACHINES AND MACHINES FOR MAKING GIMPED YARN,\nTULLE, LACE, EMBROIDERY, TRIMMINGS, BRAID OR NET AND MACHINES FOR TUFTING - FLAT\nKNITING MACHINES; STITCH-BONDING MACHINES: OTHER KNITTING MACHINES FOR WOOL",
+        "hsn_code": "84472020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNITTING MACHINES, STITCH-BONDING MACHINES AND MACHINES FOR MAKING GIMPED YARN,\nTULLE, LACE, EMBROIDERY, TRIMMINGS, BRAID OR NET AND MACHINES FOR TUFTING - FLAT\nKNITING MACHINES; STITCH-BONDING MACHINES: COTTON HOSIERY MACHINES",
+        "hsn_code": "84472030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNITTING MACHINES, STITCH-BONDING MACHINES AND MACHINES FOR MAKING GIMPED YARN,\nTULLE, LACE, EMBROIDERY, TRIMMINGS, BRAID OR NET AND MACHINES FOR TUFTING - FLAT\nKNITING MACHINES; STITCH-BONDING MACHINES: OTHER",
+        "hsn_code": "84472090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNITTING MACHINES, STITCH-BONDING MACHINES AND MACHINES FOR MAKING GIMPED YARN,\nTULLE, LACE, EMBROIDERY, TRIMMINGS, BRAID OR NET AND MACHINES FOR TUFTING - OTHER:\nMACHINES FOR MAKING OF TULLE AND LACE",
+        "hsn_code": "84479010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNITTING MACHINES, STITCH-BONDING MACHINES AND MACHINES FOR MAKING GIMPED YARN,\nTULLE, LACE, EMBROIDERY, TRIMMINGS, BRAID OR NET AND MACHINES FOR TUFTING - OTHER:\nMACHINES FOR MAKING EMBROIDERY",
+        "hsn_code": "84479020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNITTING MACHINES, STITCH-BONDING MACHINES AND MACHINES FOR MAKING GIMPED YARN,\nTULLE, LACE, EMBROIDERY, TRIMMINGS, BRAID OR NET AND MACHINES FOR TUFTING - OTHER:\nOTHER",
+        "hsn_code": "84479030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEAVING MACHINES (LOOMS)",
+        "hsn_code": "8447",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR EXTRUDING, DRAWING, TEXTURING OR CUTTING MAN-MADE TEXTILE\nMATERIALS 8444 00 - MACHINES FOR EXTRUDING, DRAWING, TEXTURING OR CUTTING MAN\nMADE TEXTILE MATERIALS: MACHINES FOR EXTRUDING MAN-MADE TEXTILE MATERIALS",
+        "hsn_code": "84440010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR EXTRUDING, DRAWING, TEXTURING OR CUTTING MAN-MADE TEXTILE\nMATERIALS 8444 00 - MACHINES FOR EXTRUDING, DRAWING, TEXTURING OR CUTTING MAN\nMADE TEXTILE MATERIALS: OTHER",
+        "hsn_code": "84440090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR EXTRUDING, DRAWING, TEXTURING OR CUTTING MAN-MADE TEXTILE MATERIALS",
+        "hsn_code": "8444",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 :-- OFFSET PRINTING MACHINERY, REEL-FED",
+        "hsn_code": "84431100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 :-- OFFSET PRINTING MACHINERY, SHEET-FED\nOFFICE TYPE (USING SHEETS WITH ONE SIDE NOT EXCEEDING 22 CM AND THE OTHER SIDE NOT\nEXCEEDING 36 CM IN THE UNFOLDED STATE)",
+        "hsn_code": "84431200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 :-- OTHER OFFSET PRINTING MACHINERY",
+        "hsn_code": "84431300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 :-- LETTERPRESS PRINTING MACHINERY, REEL FED,\nEXCLUDING FLEXOGRAPHIC PRINTING",
+        "hsn_code": "84431400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 :-- LETTERPRESS PRINTING MACHINERY, OTHER\nTHAN REEL FED, EXCLUDING FLEXOGRAPHIC PRINTING",
+        "hsn_code": "84431500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 :-- FLEXOGRAPHIC PRINTING MACHINERY",
+        "hsn_code": "84431600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 :-- GRAVURE PRINTING MACHINERY",
+        "hsn_code": "84431700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF THE PRINTING TYPE, BLOCKS, PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS OF HEADING 8442; INK-JET PRINTING\nMACHINES, OTHER THAN THOSE OF HEADING 8471; MACHINES FOR USES ANCILLARY TO\nPRINTING - OFFSET PRINTING MACHINERY: OTHER",
+        "hsn_code": "84431900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 :-- OTHER:---- FLAT BED PRINTING PRESSES",
+        "hsn_code": "84431910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 :-- OTHER: --- PLATEN PRINTING PRESSES",
+        "hsn_code": "84431920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 :-- OTHER:--- PROOF PRESSES",
+        "hsn_code": "84431930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 : --- MACHINERY FOR PRINTING REPETITIVE WORD\nOR DESIGN OR COLOUR: ---- ON COTTON TEXTILE",
+        "hsn_code": "84431941",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 : --- MACHINERY FOR PRINTING REPETITIVE WORD\nOR DESIGN OR COLOUR: ---- OTHER",
+        "hsn_code": "84431949",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 : --- MACHINERY FOR PRINTING REPETITIVE WORD\nOR DESIGN OR COLOUR: --- OTHER",
+        "hsn_code": "84431990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF THE PRINTING TYPE, BLOCKS, PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS OF HEADING 8442; INK-JET PRINTING\nMACHINES, OTHER THAN THOSE OF HEADING 8471; MACHINES FOR USES ANCILLARY TO\nPRINTING - LETTERPRESS PRINTING MACHINERY, EXCLUDING FLEXOGRAPHIC PRINTING: REEL\nFED",
+        "hsn_code": "84432100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF THE PRINTING TYPE, BLOCKS, PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS OF HEADING 8442; INK-JET PRINTING\nMACHINES, OTHER THAN THOSE OF HEADING 8471; MACHINES FOR USES ANCILLARY TO\nPRINTING - LETTERPRESS PRINTING MACHINERY, EXCLUDING FLEXOGRAPHIC PRINTING: OTHER",
+        "hsn_code": "84432900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF THE PRINTING TYPE, BLOCKS, PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS OF HEADING 8442; INK-JET PRINTING\nMACHINES, OTHER THAN THOSE OF HEADING 8471; MACHINES FOR USES ANCILLARY TO\nPRINTING FLEXOGRAPHIC PRINTING MACHINERY",
+        "hsn_code": "84433000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 :- OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED:-- MACHINES WHICH PERFORM TWO\nOR MORE OF THE FUNCTIONS OF PRINTING, COPYING OR FACSIMILE TRANSMISSION, CAPABLE\nOF CONNECTING TO AN AUTOMATIC DATA PROCESSING MACHINE OR TO A NETWORK",
+        "hsn_code": "84433100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 :-- OTHER, CAPABLE OF CONNECTING TO AN\nAUTOMATIC DATA PROCESSING MACHINE OR TO A NETWORK:--- LINE PRINTER",
+        "hsn_code": "84433210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 :-- OTHER, CAPABLE OF CONNECTING TO AN\nAUTOMATIC DATA PROCESSING MACHINE OR TO A NETWORK:--- DOT MATRIX PRINTER",
+        "hsn_code": "84433220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 :OTHER, CAPABLE OF CONNECTING TO AN\nAUTOMATIC DATA PROCESSING MACHINE OR TO A NETWORK: --- LETTER QUALITY DAISY\nWHEEL PRINTER",
+        "hsn_code": "84433230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 :OTHER, CAPABLE OF CONNECTING TO AN\nAUTOMATIC DATA PROCESSING MACHINE OR TO A NETWORK: --- LASER JET PRINTER",
+        "hsn_code": "84433240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 : OTHER, CAPABLE OF CONNECTING TO AN\nAUTOMATIC DATA PROCESSING MACHINE OR TO A NETWORK: --- INK JET PRINTER",
+        "hsn_code": "84433250",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 : OTHER, CAPABLE OF CONNECTING TO AN\nAUTOMATIC DATA PROCESSING MACHINE OR TO A NETWORK: --- FACSIMILE MACHINE",
+        "hsn_code": "84433260",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 :OTHER, CAPABLE OF CONNECTING TO AN\nAUTOMATIC DATA PROCESSING MACHINE OR TO A NETWORK: --- OTHER",
+        "hsn_code": "84433290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 :OTHER, CAPABLE OF CONNECTING TO AN\nAUTOMATIC DATA PROCESSING MACHINE OR TO A NETWORK: --- OTHER",
+        "hsn_code": "84433910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 : -- OTHER --- ELECTROSTATIC PHOTOCOPYING\nAPPARATUS OPERATED BY REPRODUCING THE ORIGINAL IMAGE DIRECTLY ONTO THE COPY\n(DIRECT PROCESS)",
+        "hsn_code": "84433920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 : -- OTHER --- ELECTROSTATIC PHOTOCOPYING\nAPPARATUS OPERATED BY REPRODUCING THE ORIGINAL IMAGE VIA AN INTERMEDIATE ONTO\nTHE COPY (INDIRECT PROCESS)",
+        "hsn_code": "84433930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 : -- OTHER --- OTHER PHOTOCOPYING APPARATUS\nINCORPORATING AN OPTICAL SYSTEM",
+        "hsn_code": "84433940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 : -- OTHER --- OTHER PHOTOCOPYING APPARATUS\nOF THE CONTACT TYPE",
+        "hsn_code": "84433950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 : -- OTHER --- THERMO-COPYING APPARATUS",
+        "hsn_code": "84433960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 : -- OTHER --- FACSIMILE MACHINE NOT CAPABLE OF\nGETTING CONNECTED TO AUTOMATIC DATA PROCESSING MACHINE",
+        "hsn_code": "84433970",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 : -- OTHER --- OTHER",
+        "hsn_code": "84433990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF THE PRINTING TYPE, BLOCKS, PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS OF HEADING 8442; INK-JET PRINTING\nMACHINES, OTHER THAN THOSE OF HEADING 8471; MACHINES FOR USES ANCILLARY TO\nPRINTING:- GRAVURE PRINTING MACHINERY",
+        "hsn_code": "84434000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF THE PRINTING TYPE, BLOCKS, PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS OF HEADING 8442; INK-JET PRINTING\nMACHINES, OTHER THAN THOSE OF HEADING 8471; MACHINES FOR USES ANCILLARY TO\nPRINTING - OTHER PRINTING MACHINERY: INK-JET PRINTING MACHINES",
+        "hsn_code": "84435100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF THE PRINTING TYPE, BLOCKS, PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS OF HEADING 8442; INK-JET PRINTING\nMACHINES, OTHER THAN THOSE OF HEADING 8471; MACHINES FOR USES ANCILLARY TO\nPRINTING - OTHER PRINTING MACHINERY: OTHER: FLAT BED PRINTING PRESSES",
+        "hsn_code": "84435910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF THE PRINTING TYPE, BLOCKS, PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS OF HEADING 8442; INK-JET PRINTING\nMACHINES, OTHER THAN THOSE OF HEADING 8471; MACHINES FOR USES ANCILLARY TO\nPRINTING - OTHER PRINTING MACHINERY: OTHER: PLATEN PRINTING PRESSES",
+        "hsn_code": "84435920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF THE PRINTING TYPE, BLOCKS, PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS OF HEADING 8442; INK-JET PRINTING\nMACHINES, OTHER THAN THOSE OF HEADING 8471; MACHINES FOR USES ANCILLARY TO\nPRINTING - OTHER PRINTING MACHINERY: OTHER: PROOF PRESSES",
+        "hsn_code": "84435930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF THE PRINTING TYPE, BLOCKS, PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS OF HEADING 8442; INK-JET PRINTING\nMACHINES, OTHER THAN THOSE OF HEADING 8471; MACHINES FOR USES ANCILLARY TO\nPRINTING - OTHER PRINTING MACHINERY: OTHER: MACHINERY FOR PRINTING REPETITIVE\nWORD OR DESIGN OR COLOUR: ON COTTON TEXTILE",
+        "hsn_code": "84435941",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF THE PRINTING TYPE, BLOCKS, PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS OF HEADING 8442; INK-JET PRINTING\nMACHINES, OTHER THAN THOSE OF HEADING 8471; MACHINES FOR USES ANCILLARY TO\nPRINTING - OTHER PRINTING MACHINERY: OTHER: MACHINERY FOR PRINTING REPETITIVE\nWORD OR DESIGN OR COLOUR: OTHER",
+        "hsn_code": "84435949",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF THE PRINTING TYPE, BLOCKS, PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS OF HEADING 8442; INK-JET PRINTING\nMACHINES, OTHER THAN THOSE OF HEADING 8471; MACHINES FOR USES ANCILLARY TO\nPRINTING - OTHER PRINTING MACHINERY: OTHER: OTHER",
+        "hsn_code": "84435990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF THE PRINTING TYPE, BLOCKS, PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS OF HEADING 8442; INK-JET PRINTING\nMACHINES, OTHER THAN THOSE OF HEADING 8471; MACHINES FOR USES ANCILLARY TO\nPRINTING - OTHER PRINTING MACHINERY: MACHINES FOR USES ANCILLARY TO PRINTING:\nAUTOMATIC FEEDERS AND SHEET DELIVERING MACHINE",
+        "hsn_code": "84436010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF THE PRINTING TYPE, BLOCKS, PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS OF HEADING 8442; INK-JET PRINTING\nMACHINES, OTHER THAN THOSE OF HEADING 8471; MACHINES FOR USES ANCILLARY TO\nPRINTING - OTHER PRINTING MACHINERY: MACHINES FOR USES ANCILLARY TO PRINTING:\nSERIAL NUMBERING MACHINES",
+        "hsn_code": "84436020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF THE PRINTING TYPE, BLOCKS, PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS OF HEADING 8442; INK-JET PRINTING\nMACHINES, OTHER THAN THOSE OF HEADING 8471; MACHINES FOR USES ANCILLARY TO\nPRINTING - OTHER PRINTING MACHINERY: MACHINES FOR USES ANCILLARY TO PRINTING:\nFOLDERS, GUZMNOS PERFORATORS AND STAPLERS",
+        "hsn_code": "84436030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF THE PRINTING TYPE, BLOCKS, PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS OF HEADING 8442; INK-JET PRINTING\nMACHINES, OTHER THAN THOSE OF HEADING 8471; MACHINES FOR USES ANCILLARY TO\nPRINTING - OTHER PRINTING MACHINERY: MACHINES FOR USES ANCILLARY TO PRINTING:\nOTHER",
+        "hsn_code": "84436090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF THE PRINTING TYPE, BLOCKS, PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS OF HEADING 8442; INK-JET PRINTING\nMACHINES, OTHER THAN THOSE OF HEADING 8471; MACHINES FOR USES ANCILLARY TO\nPRINTING - - PARTS: PARTS OF PRINTING PRESSES",
+        "hsn_code": "84439010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF THE PRINTING TYPE, BLOCKS, PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS OF HEADING 8442; INK-JET PRINTING\nMACHINES, OTHER THAN THOSE OF HEADING 8471; MACHINES FOR USES ANCILLARY TO\nPRINTING - - PARTS: OTHER",
+        "hsn_code": "84439090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 :-- PARTS AND ACCESSORIES OF PRINTING\nMACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER PRINTING\nCOMPONENTS OF HEADING 8442",
+        "hsn_code": "84439100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 : -- OTHER :--- AUTOMATIC DOCUMENT FEEDERES\nOF COPYING MACHINES",
+        "hsn_code": "84439910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 : -- OTHER --- PAPER FEEDERS OF COPYING\nMACHINES",
+        "hsn_code": "84439920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 : -- OTHER : --- SORTERS OF COPYING MACHINES",
+        "hsn_code": "84439930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 : -- OTHER :--- OTHER PARTS OF COPYING MACHINES",
+        "hsn_code": "84439940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 : -- OTHER :--- PARTS AND ACCESSORIES OF GOODS\nOF SUBHEADINGS 844331,844332",
+        "hsn_code": "84439950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPARTS AND ACCESSORIES -- OTHER --- OF SUBHEADING 8443 31, 8443 32 ---- INK CARTRIDGES,\nWITH PRINT HEAD ASSEMBLY",
+        "hsn_code": "84439951",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES AND\nFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPARTS AND ACCESSORIES -- OTHER --- OF SUBHEADING 8443 31, 8443 32 ---- INK SPRAY NOZZLE",
+        "hsn_code": "84439952",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INK SPRAY NOZZLE",
+        "hsn_code": "84439953",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPARTS AND ACCESSORIES -- OTHER --- OF SUBHEADING 8443 31, 8443 32 ---- OTHER",
+        "hsn_code": "84439959",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPARTS AND ACCESSORIES -- OTHER --- OF SUBHEADING 8443 39",
+        "hsn_code": "84439960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 : -- OTHER : --- PARTS AND ACCESSORIES OF GOODS\nOF SUBHEADING 844339",
+        "hsn_code": "84439970",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND\nOTHERPRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES\nANDFACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF -\nPRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER\nPRINTING COMPONENTS OF HEADING 8442 : -- OTHER :--- OTHER",
+        "hsn_code": "84439990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING MACHINERY USED FOR PRINTING BY MEANS OF PLATES, CYLINDERS AND OTHER  PRINTING COMPONENTS OF HEADING 8442; OTHER PRINTERS, COPYING MACHINES AND  FACSIMILE MACHINES, WHETHER OR NOT COMBINED; PARTS AND ACCESSORIES THEREOF",
+        "hsn_code": "8443",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, APPARATUS AND EQUIPMENT (OTHER THAN THE MACHINE-TOOLS OF HEADINGS\n8456 TO 8465), FOR TYPE-FOUNDING OR TYPE-SETTING, FOR PREPARING OR MAKING PRINTING\nBLOCKS, PLATES, CYLINDERS OR OTHER PRINTING COMPONENTS; PRINTING TYPE, BLOCKS,\nPLATES, CYLINDERS AND OTHER PRINTING COMPONENTS; BLOCKS, PLATES, CYLINDERS AND\nLITHOGRAPHIC STONES, PREPARED FOR PRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED\nOR POLISHED) PHOTOTYPE-SETTING AND COMPOSING MACHINES",
+        "hsn_code": "84421000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, APPARATUS AND EQUIPMENT (OTHER THAN THE MACHINE-TOOLS OF HEADINGS\n8456 TO 8465), FOR TYPE-FOUNDING OR TYPE-SETTING, FOR PREPARING OR MAKING PRINTING\nBLOCKS, PLATES, CYLINDERS OR OTHER PRINTING COMPONENTS; PRINTING TYPE, BLOCKS,\nPLATES, CYLINDERS AND OTHER PRINTING COMPONENTS; BLOCKS, PLATES, CYLINDERS AND\nLITHOGRAPHIC STONES, PREPARED FOR PRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED\nOR POLISHED) MACHINERY, APPARATUS AND EQUIPMENT FOR TYPE-SETTING OR COMPOSING\nBY OTHER PROCESSES, WITH OR WITHOUT FOUNDING DEVICE: STEREO-TYPING APPARATUS",
+        "hsn_code": "84422010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, APPARATUS AND EQUIPMENT (OTHER THAN THE MACHINE-TOOLS OF HEADINGS\n8456 TO 8465), FOR TYPE-FOUNDING OR TYPE-SETTING, FOR PREPARING OR MAKING PRINTING\nBLOCKS, PLATES, CYLINDERS OR OTHER PRINTING COMPONENTS; PRINTING TYPE, BLOCKS,\nPLATES, CYLINDERS AND OTHER PRINTING COMPONENTS; BLOCKS, PLATES, CYLINDERS AND\nLITHOGRAPHIC STONES, PREPARED FOR PRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED\nOR POLISHED) - MACHINERY, APPARATUS AND EQUIPMENT FOR TYPE-SETTING OR COMPOSING\nBY OTHER PROCESSES, WITH OR WITHOUT FOUNDING DEVICE: TYPE-CASTING (FOUNDING)\nMACHINES",
+        "hsn_code": "84422020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, APPARATUS AND EQUIPMENT (OTHER THAN THE MACHINE-TOOLS OF HEADINGS\n8456 TO 8465), FOR TYPE-FOUNDING OR TYPE-SETTING, FOR PREPARING OR MAKING PRINTING\nBLOCKS, PLATES, CYLINDERS OR OTHER PRINTING COMPONENTS; PRINTING TYPE, BLOCKS,\nPLATES, CYLINDERS AND OTHER PRINTING COMPONENTS; BLOCKS, PLATES, CYLINDERS AND\nLITHOGRAPHIC STONES, PREPARED FOR PRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED\nOR POLISHED) - MACHINERY, APPARATUS AND EQUIPMENT FOR TYPE-SETTING OR COMPOSING\nBY OTHER PROCESSES, WITH OR WITHOUT FOUNDING DEVICE: TYPE-SETTING MACHINES",
+        "hsn_code": "84422030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, APPARATUS AND EQUIPMENT (OTHER THAN THE MACHINE-TOOLS OF HEADINGS\n8456 TO 8465), FOR TYPE-FOUNDING OR TYPE-SETTING, FOR PREPARING OR MAKING PRINTING\nBLOCKS, PLATES, CYLINDERS OR OTHER PRINTING COMPONENTS; PRINTING TYPE, BLOCKS,\nPLATES, CYLINDERS AND OTHER PRINTING COMPONENTS; BLOCKS, PLATES, CYLINDERS AND\nLITHOGRAPHIC STONES, PREPARED FOR PRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED\nOR POLISHED) - MACHINERY, APPARATUS AND EQUIPMENT FOR TYPE-SETTING OR COMPOSING\nBY OTHER PROCESSES, WITH OR WITHOUT FOUNDING DEVICE: OTHER",
+        "hsn_code": "84422090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, APPARATUS AND EQUIPMENT (OTHER THAN THE MACHINE TOOLS OF HEADINGS\n8456 TO 8465) FOR PREPARING OR MAKING PLATES, PRINTING COMPONENTS; PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS; PLATES, CYLINDERS AND LITHOGRAPHIC\nSTONES, PREPARED FOR PRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED OR\nPOLISHED);MACHINERY, APPARATUS AND EQUIPMENT:--- BRASS RULES",
+        "hsn_code": "84423010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, APPARATUS AND EQUIPMENT (OTHER THAN THE MACHINE TOOLS OF HEADINGS\n8456 TO 8465) FOR PREPARING OR MAKING PLATES, PRINTING COMPONENTS; PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS; PLATES, CYLINDERS AND LITHOGRAPHIC\nSTONES, PREPARED FOR PRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED OR\nPOLISHED);MACHINERY, APPARATUS AND EQUIPMENT:---CHASES",
+        "hsn_code": "84423020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, APPARATUS AND EQUIPMENT (OTHER THAN THE MACHINE TOOLS OF HEADINGS\n8456 TO 8465) FOR PREPARING OR MAKING PLATES, PRINTING COMPONENTS; PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS; PLATES, CYLINDERS AND LITHOGRAPHIC\nSTONES, PREPARED FOR PRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED OR\nPOLISHED);MACHINERY, APPARATUS AND EQUIPMENT:---OTHER",
+        "hsn_code": "84423090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, APPARATUS AND EQUIPMENT (OTHER THAN THE MACHINE TOOLS OF HEADINGS\n8456 TO 8465) FOR PREPARING OR MAKING PLATES, PRINTING COMPONENTS; PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS; PLATES, CYLINDERS AND LITHOGRAPHIC\nSTONES, PREPARED FOR PRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED OR POLISHED);-\nPARTS OF THE FOREGOING MACHINERY, APPARATUS OR EQUIPMENT",
+        "hsn_code": "84424000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, APPARATUS AND EQUIPMENT (OTHER THAN THE MACHINE TOOLS OF HEADINGS\n8456 TO 8465) FOR PREPARING OR MAKING PLATES, PRINTING COMPONENTS; PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS; PLATES, CYLINDERS AND LITHOGRAPHIC\nSTONES, PREPARED FOR PRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED OR POLISHED); -\nPLATES, CYLINDERS AND OTHER PRINTING COMPONENTS; PLATES, CYLINDERS AND\nLITHOGRAPHIC STONES, PREPARED FOR PRINTING PURPOSES ( FOR EXAMPLE, PLANED, GRAINED\nOR POLISHED):--- PLATES AND CYLINDERS",
+        "hsn_code": "84425010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, APPARATUS AND EQUIPMENT (OTHER THAN THE MACHINE TOOLS OF HEADINGS\n8456 TO 8465) FOR PREPARING OR MAKING PLATES, PRINTING COMPONENTS; PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS; PLATES, CYLINDERS AND LITHOGRAPHIC\nSTONES, PREPARED FOR PRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED OR POLISHED); -\nPLATES, CYLINDERS AND OTHER PRINTING COMPONENTS; PLATES, CYLINDERS AND\nLITHOGRAPHIC STONES, PREPARED FOR PRINTING PURPOSES ( FOR EXAMPLE, PLANED, GRAINED\nOR POLISHED):-- LITHOGRAPHIC PLATES",
+        "hsn_code": "84425020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, APPARATUS AND EQUIPMENT (OTHER THAN THE MACHINE TOOLS OF HEADINGS\n8456 TO 8465) FOR PREPARING OR MAKING PLATES, PRINTING COMPONENTS; PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS; PLATES, CYLINDERS AND LITHOGRAPHIC\nSTONES, PREPARED FOR PRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED OR POLISHED); -\nPLATES, CYLINDERS AND OTHER PRINTING COMPONENTS; PLATES, CYLINDERS AND\nLITHOGRAPHIC STONES, PREPARED FOR PRINTING PURPOSES ( FOR EXAMPLE, PLANED, GRAINED\nOR POLISHED):--- PLATE, CYLINDER AND LITHOGRAPHIC STONES PREPARED FOR PRINTING\nPURPOSES:---- PLATE AND CYLINDER FOR TEXTILE PRINTING MACHINE",
+        "hsn_code": "84425031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, APPARATUS AND EQUIPMENT (OTHER THAN THE MACHINE-TOOLS OF HEADINGS\n8456 TO 8465), FOR TYPE-FOUNDING OR TYPE-SETTING, FOR PREPARING OR MAKING PRINTING\nBLOCKS, PLATES, CYLINDERS OR OTHER PRINTING COMPONENTS; PRINTING TYPE, BLOCKS,\nPLATES, CYLINDERS AND OTHER PRINTING COMPONENTS; BLOCKS, PLATES, CYLINDERS AND\nLITHOGRAPHIC STONES, PREPARED FOR PRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED\nOR POLISHED) - PRINTING TYPE, BLOCKS, PLATES, CYLINDERS AND OTHER PRINTING\nCOMPONENTS; BLOCKS, PLATES, CYLINDERS AND LITHOGRAPHIC STONES, PREPARED FOR\nPRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED OR POLISHED) - PLATE, BLOCK,\nCYLINDER AND LITHOGRAPHIC STONES PREPARED FOR PRINTING PURPOSES: PRINTING BLOCKS",
+        "hsn_code": "84425032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, APPARATUS AND EQUIPMENT (OTHER THAN THE MACHINE TOOLS OF HEADINGS\n8456 TO 8465) FOR PREPARING OR MAKING PLATES, PRINTING COMPONENTS; PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS; PLATES, CYLINDERS AND LITHOGRAPHIC\nSTONES, PREPARED FOR PRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED OR POLISHED); -\nPLATES, CYLINDERS AND OTHER PRINTING COMPONENTS; PLATES, CYLINDERS AND\nLITHOGRAPHIC STONES, PREPARED FOR PRINTING PURPOSES ( FOR EXAMPLE, PLANED, GRAINED\nOR POLISHED):--- PLATE, CYLINDER AND LITHOGRAPHIC STONES PREPARED FOR PRINTING\nPURPOSES:---- OTHER",
+        "hsn_code": "84425039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, APPARATUS AND EQUIPMENT (OTHER THAN THE MACHINE TOOLS OF HEADINGS\n8456 TO 8465) FOR PREPARING OR MAKING PLATES, PRINTING COMPONENTS; PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS; PLATES, CYLINDERS AND LITHOGRAPHIC\nSTONES, PREPARED FOR PRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED OR POLISHED); -\nPLATES, CYLINDERS AND OTHER PRINTING COMPONENTS; PLATES, CYLINDERS AND\nLITHOGRAPHIC STONES, PREPARED FOR PRINTING PURPOSES ( FOR EXAMPLE, PLANED, GRAINED\nOR POLISHED): --- HIGHLY POLISHED COPPER SHEETS FOR MAKING BLOCKS",
+        "hsn_code": "84425040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, APPARATUS AND EQUIPMENT (OTHER THAN THE MACHINE TOOLS OF HEADINGS\n8456 TO 8465) FOR PREPARING OR MAKING PLATES, PRINTING COMPONENTS; PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS; PLATES, CYLINDERS AND LITHOGRAPHIC\nSTONES, PREPARED FOR PRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED OR POLISHED); -\nPLATES, CYLINDERS AND OTHER PRINTING COMPONENTS; PLATES, CYLINDERS AND\nLITHOGRAPHIC STONES, PREPARED FOR PRINTING PURPOSES ( FOR EXAMPLE, PLANED, GRAINED\nOR POLISHED):--- HIGHLY POLISHED ZINC SHEETS FOR MAKING PROCESS BLOCKS",
+        "hsn_code": "84425050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, APPARATUS AND EQUIPMENT (OTHER THAN THE MACHINE-TOOLS OF HEADINGS\n8456 TO 8465), FOR TYPE-FOUNDING OR TYPE-SETTING, FOR PREPARING OR MAKING PRINTING\nBLOCKS, PLATES, CYLINDERS OR OTHER PRINTING COMPONENTS; PRINTING TYPE, BLOCKS,\nPLATES, CYLINDERS AND OTHER PRINTING COMPONENTS; BLOCKS, PLATES, CYLINDERS AND\nLITHOGRAPHIC STONES, PREPARED FOR PRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED\nOR POLISHED) - PRINTING TYPE, BLOCKS, PLATES, CYLINDERS AND OTHER PRINTING\nCOMPONENTS; BLOCKS, PLATES, CYLINDERS AND LITHOGRAPHIC STONES, PREPARED FOR\nPRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED OR POLISHED): HIGHLY POLISHED ZINC\nSHEETS FOR MAKING PROCESS BLOCKS",
+        "hsn_code": "84425060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, APPARATUS AND EQUIPMENT (OTHER THAN THE MACHINE TOOLS OF HEADINGS\n8456 TO 8465) FOR PREPARING OR MAKING PLATES, PRINTING COMPONENTS; PLATES,\nCYLINDERS AND OTHER PRINTING COMPONENTS; PLATES, CYLINDERS AND LITHOGRAPHIC\nSTONES, PREPARED FOR PRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED OR POLISHED); -\nPLATES, CYLINDERS AND OTHER PRINTING COMPONENTS; PLATES, CYLINDERS AND\nLITHOGRAPHIC STONES, PREPARED FOR PRINTING PURPOSES ( FOR EXAMPLE, PLANED, GRAINED\nOR POLISHED):--- OTHER",
+        "hsn_code": "84425090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, APPARATUS AND EQUIPMENT (OTHER THAN THE MACHINE TOOLS OF HEADINGS 8456 TO 8465) FOR PREPARING OR MAKING PLATES, PRINTING COMPONENTS; PLATES, CYLINDERS AND OTHER PRINTING COMPONENTS; PLATES, CYLINDERS AND LITHO GRAPHIC STONES, PREPARED FOR PRINTING PURPOSES (FOR EXAMPLE, PLANED, GRAINED OR POLISHED)",
+        "hsn_code": "8442",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINERY FOR MAKING UP PAPER PULP, PAPER OR PAPERBOARD, INCLUDING\nCUTTING MACHINES OF ALL KINDS - CUTTING MACHINES: PAPER CUTTING MACHINES,\nEXCLUDING MACHINES WITH DEVICES SUCH AS AUTOMATIC PROGRAMME CUTTING OR THREE\nKNIFE TRIMMERS",
+        "hsn_code": "84411010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINERY FOR MAKING UP PAPER PULP, PAPER OR PAPERBOARD, INCLUDING\nCUTTING MACHINES OF ALL KINDS - CUTTING MACHINES: OTHER",
+        "hsn_code": "84411090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINERY FOR MAKING UP PAPER PULP, PAPER OR PAPERBOARD, INCLUDING\nCUTTING MACHINES OF ALL KINDS - MACHINES FOR MAKING BAGS, SACKS OR ENVELOPES",
+        "hsn_code": "84412000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINERY FOR MAKING UP PAPER PULP, PAPER OR PAPERBOARD, INCLUDING\nCUTTING MACHINES OF ALL KINDS - MACHINES FOR MAKING CARTONS, BOXES, CASES, TUBES,\nDRUMS OR SIMILAR CONTAINERS, OTHER THAN BY MOULDING",
+        "hsn_code": "84413000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINERY FOR MAKING UP PAPER PULP, PAPER OR PAPERBOARD, INCLUDING\nCUTTING MACHINES OF ALL KINDS - MACHINES FOR MOULDING ARTICLES IN PAPER PULP, PAPER\nOR PAPERBOARD",
+        "hsn_code": "84414000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINERY FOR MAKING UP PAPER PULP, PAPER OR PAPERBOARD, INCLUDING\nCUTTING MACHINES OF ALL KINDS - OTHER MACHINERY",
+        "hsn_code": "84418000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINERY FOR MAKING UP PAPER PULP, PAPER OR PAPERBOARD, INCLUDING\nCUTTING MACHINES OF ALL KINDS - PARTS",
+        "hsn_code": "84419000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MACHINERY FOR MAKING UP PAPER PULP, PAPER OR PAPER BOARD, INCLUDING CUTTING MACHINES OF ALL KINDS",
+        "hsn_code": "8441",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BOOK-BINDING MACHINERY, INCLUDING BOOK-SEWING MACHINES - MACHINERY: WIRE\nSTITCHING MACHINERY, SINGLE HEADED",
+        "hsn_code": "84401010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BOOK-BINDING MACHINERY, INCLUDING BOOK-SEWING MACHINES - MACHINERY: OTHER",
+        "hsn_code": "84401090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BOOK-BINDING MACHINERY, INCLUDING BOOK-SEWING MACHINES - PARTS",
+        "hsn_code": "84409000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BOOK-BINDING MACHINERY, INCLUDING BOOK-SEWING MACHINES",
+        "hsn_code": "8440",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR MAKING PULP OF FIBROUS CELLULOSIC MATERIAL OR FOR MAKING OR\nFINISHING PAPER OR PAPERBOARD MACHINERY FOR MAKING PULP OF FIBROUS CELLULOSIC\nMATERIAL",
+        "hsn_code": "84391000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR MAKING PULP OF FIBROUS CELLULOSIC MATERIAL OR FOR MAKING OR\nFINISHING PAPER OR PAPERBOARD MACHINERY FOR MAKING PAPER OR PAPERBOARD",
+        "hsn_code": "84392000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR MAKING PULP OF FIBROUS CELLULOSIC MATERIAL OR FOR MAKING OR\nFINISHING PAPER OR PAPERBOARD - MACHINERY FOR FINISHING PAPER OR PAPERBOARD:\nPAPER LAMINATING MACHINE",
+        "hsn_code": "84393010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR MAKING PULP OF FIBROUS CELLULOSIC MATERIAL OR FOR MAKING OR\nFINISHING PAPER OR PAPERBOARD - MACHINERY FOR FINISHING PAPER OR PAPERBOARD:\nOTHER",
+        "hsn_code": "84393090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR MAKING PULP OF FIBROUS CELLULOSIC MATERIAL OR FOR MAKING OR\nFINISHING PAPER OR PAPERBOARD - PARTS: OF MACHINERY FOR MAKING PULP OF FIBROUS\nCELLULOSIC MATERIAL",
+        "hsn_code": "84399100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR MAKING PULP OF FIBROUS CELLULOSIC MATERIAL OR FOR MAKING OR\nFINISHING PAPER OR PAPERBOARD - PARTS: OTHER",
+        "hsn_code": "84399900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY FOR MAKING PULP OF FIBROUS CELLULOSIC MATERIAL OR FOR MAKING OR FINISHING PAPER OR PAPERBOARD",
+        "hsn_code": "8439",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER, FOR THE INDUSTRIAL\nPREPARATION OR MANUFACTURE OF FOOD OR DRINK, OTHER THAN MACHINERY FOR THE\nEXTRACTION OR PREPARATION OF ANIMAL OR FIXED VEGETABLE FATS OR OILS 8438 10 -\nBAKERY MACHINERY AND MACHINERY FOR THE MANUFACTURE OF MACARONI, SPAGHETTI OR\nSIMILAR PRODUCTS: BAKERY MACHINERY",
+        "hsn_code": "84381010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER, FOR THE INDUSTRIAL\nPREPARATION OR MANUFACTURE OF FOOD OR DRINK, OTHER THAN MACHINERY FOR THE\nEXTRACTION OR PREPARATION OF ANIMAL OR FIXED VEGETABLE FATS OR OILS 8438 10 -\nBAKERY MACHINERY AND MACHINERY FOR THE MANUFACTURE OF MACARONI, SPAGHETTI OR\nSIMILAR PRODUCTS: MACHINERY FOR MANUFACTURE OF MACARONI OR SPAGHETTI OR SIMILAR\nPRODUCTS",
+        "hsn_code": "84381020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER, FOR THE INDUSTRIAL\nPREPARATION OR MANUFACTURE OF FOOD OR DRINK, OTHER THAN MACHINERY FOR THE\nEXTRACTION OR PREPARATION OF ANIMAL OR FIXED VEGETABLE FATS OR OILS 8438 10 -\nMACHINERY FOR THE MANUFACTURE OF CONFECTIONERY, COCOA OR CHOCOLATE",
+        "hsn_code": "84382000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER, FOR THE INDUSTRIAL\nPREPARATION OR MANUFACTURE OF FOOD OR DRINK, OTHER THAN MACHINERY FOR THE\nEXTRACTION OR PREPARATION OF ANIMAL OR FIXED VEGETABLE FATS OR OILS 8438 10 - -\nMACHINERY FOR SUGAR MANUFACTURE: SUGAR CANE CRUSHERS",
+        "hsn_code": "84383010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER, FOR THE INDUSTRIAL\nPREPARATION OR MANUFACTURE OF FOOD OR DRINK, OTHER THAN MACHINERY FOR THE\nEXTRACTION OR PREPARATION OF ANIMAL OR FIXED VEGETABLE FATS OR OILS 8438 10 - -\nMACHINERY FOR SUGAR MANUFACTURE: OTHER",
+        "hsn_code": "84383090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER, FOR THE INDUSTRIAL\nPREPARATION OR MANUFACTURE OF FOOD OR DRINK, OTHER THAN MACHINERY FOR THE\nEXTRACTION OR PREPARATION OF ANIMAL OR FIXED VEGETABLE FATS OR OILS 8438 10 -\nBREWERY MACHINERY",
+        "hsn_code": "84384000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER, FOR THE INDUSTRIAL\nPREPARATION OR MANUFACTURE OF FOOD OR DRINK, OTHER THAN MACHINERY FOR THE\nEXTRACTION OR PREPARATION OF ANIMAL OR FIXED VEGETABLE FATS OR OILS 8438 10 -\nMACHINERY FOR THE PREPARATION OF MEAT OR POULTRY",
+        "hsn_code": "84385000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER, FOR THE INDUSTRIAL\nPREPARATION OR MANUFACTURE OF FOOD OR DRINK, OTHER THAN MACHINERY FOR THE\nEXTRACTION OR PREPARATION OF ANIMAL OR FIXED VEGETABLE FATS OR OILS 8438 10 -\nMACHINERY FOR THE PREPARATION OF FRUITS, NUTS OR VEGETABLES",
+        "hsn_code": "84386000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER, FOR THE INDUSTRIAL\nPREPARATION OR MANUFACTURE OF FOOD OR DRINK, OTHER THAN MACHINERY FOR THE\nEXTRACTION OR PREPARATION OF ANIMAL OR FIXED VEGETABLE FATS OR OILS 8438 10 - -\nOTHER MACHINERY: AUXILIARY EQUIPMENT FOR EXTRUSION COOKING PLANT",
+        "hsn_code": "84388010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER, FOR THE INDUSTRIAL\nPREPARATION OR MANUFACTURE OF FOOD OR DRINK, OTHER THAN MACHINERY FOR THE\nEXTRACTION OR PREPARATION OF ANIMAL OR FIXED VEGETABLE FATS OR OILS 8438 10 - -\nOTHER MACHINERY: FOR PRODUCTION OF SOYA MILK OR OTHER SOYA PRODUCTS (OTHER THAN\nSOYA OIL)",
+        "hsn_code": "84388020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER, FOR THE INDUSTRIAL\nPREPARATION OR MANUFACTURE OF FOOD OR DRINK, OTHER THAN MACHINERY FOR THE\nEXTRACTION OR PREPARATION OF ANIMAL OR FIXED VEGETABLE FATS OR OILS 8438 10 - -\nOTHER MACHINERY: DIFFUSING MACHINES (DIFFUSERS)",
+        "hsn_code": "84388030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER, FOR THE INDUSTRIAL\nPREPARATION OR MANUFACTURE OF FOOD OR DRINK, OTHER THAN MACHINERY FOR THE\nEXTRACTION OR PREPARATION OF ANIMAL OR FIXED VEGETABLE FATS OR OILS 8438 10 - -\nOTHER MACHINERY: TEA LEAF ROLLING OR CUTTING MACHINE",
+        "hsn_code": "84388040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER, FOR THE INDUSTRIAL\nPREPARATION OR MANUFACTURE OF FOOD OR DRINK, OTHER THAN MACHINERY FOR THE\nEXTRACTION OR PREPARATION OF ANIMAL OR FIXED VEGETABLE FATS OR OILS 8438 10 - -\nOTHER MACHINERY: OTHER",
+        "hsn_code": "84388090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER, FOR THE INDUSTRIAL\nPREPARATION OR MANUFACTURE OF FOOD OR DRINK, OTHER THAN MACHINERY FOR THE\nEXTRACTION OR PREPARATION OF ANIMAL OR FIXED VEGETABLE FATS OR OILS 8438 10 - PARTS:\nOF SUGAR MANUFACTURING MACHINERY",
+        "hsn_code": "84389010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER, FOR THE INDUSTRIAL\nPREPARATION OR MANUFACTURE OF FOOD OR DRINK, OTHER THAN MACHINERY FOR THE\nEXTRACTION OR PREPARATION OF ANIMAL OR FIXED VEGETABLE FATS OR OILS 8438 10 - PARTS:\nOF OTHER MACHINERY",
+        "hsn_code": "84389090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINERY, NOT SPECIFIED OR INCLUDED ELSEWHERE IN THIS CHAPTER, FOR THE INDUSTRIAL PREPARATION OR MANUFACTURE OF FOOD OR DRINK, OTHER THAN MACHINERY FOR THE EXTRACTION OR PREPARATION OF ANIMAL OR FIXED VEGETABLE FATS OR OILS",
+        "hsn_code": "8438",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR CLEANING, SORTING OR GRADING SEED, GRAIN OR DRIED LEGUMINOUS\nVEGETABLES; MACHINERY USED IN THE MILLING INDUSTRY OR FOR THE WORKING OF CEREALS\nOR DRIED LEGUMINOUS VEGETABLES, OTHER THAN FARM-TYPE MACHINERY MACHINES FOR\nCLEANING, SORTING OR GRADING SEED, GRAIN OR DRIED LEGUMINOUS VEGETABLES",
+        "hsn_code": "84371000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR CLEANING, SORTING OR GRADING SEED, GRAIN OR DRIED LEGUMINOUS\nVEGETABLES; MACHINERY USED IN THE MILLING INDUSTRY OR FOR THE WORKING OF CEREALS\nOR DRIED LEGUMINOUS VEGETABLES, OTHER THAN FARM-TYPE MACHINERY - OTHER\nMACHINERY: FLOUR MILL MACHINERY",
+        "hsn_code": "84378010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR CLEANING, SORTING OR GRADING SEED, GRAIN OR DRIED LEGUMINOUS\nVEGETABLES; MACHINERY USED IN THE MILLING INDUSTRY OR FOR THE WORKING OF CEREALS\nOR DRIED LEGUMINOUS VEGETABLES, OTHER THAN FARM-TYPE MACHINERY - OTHER\nMACHINERY: RICE MILL MACHINERY",
+        "hsn_code": "84378020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR CLEANING, SORTING OR GRADING SEED, GRAIN OR DRIED LEGUMINOUS\nVEGETABLES; MACHINERY USED IN THE MILLING INDUSTRY OR FOR THE WORKING OF CEREALS\nOR DRIED LEGUMINOUS VEGETABLES, OTHER THAN FARM-TYPE MACHINERY - OTHER\nMACHINERY: OTHER",
+        "hsn_code": "84378090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR CLEANING, SORTING OR GRADING SEED, GRAIN OR DRIED LEGUMINOUS\nVEGETABLES; MACHINERY USED IN THE MILLING INDUSTRY OR FOR THE WORKING OF CEREALS\nOR DRIED LEGUMINOUS VEGETABLES, OTHER THAN FARM-TYPE MACHINERY - PARTS : OF FLOUR\nMILL MACHINERY",
+        "hsn_code": "84379010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR CLEANING, SORTING OR GRADING SEED, GRAIN OR DRIED LEGUMINOUS\nVEGETABLES; MACHINERY USED IN THE MILLING INDUSTRY OR FOR THE WORKING OF CEREALS\nOR DRIED LEGUMINOUS VEGETABLES, OTHER THAN FARM-TYPE MACHINERY - PARTS : OF RICE\nMILL MACHINERY",
+        "hsn_code": "84379020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR CLEANING, SORTING OR GRADING SEED, GRAIN OR DRIED LEGUMINOUS\nVEGETABLES; MACHINERY USED IN THE MILLING INDUSTRY OR FOR THE WORKING OF CEREALS\nOR DRIED LEGUMINOUS VEGETABLES, OTHER THAN FARM-TYPE MACHINERY - PARTS : OTHER",
+        "hsn_code": "84379090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR CLEANING, SORTING OR GRADING SEED, GRAIN OR DRIED LEGUMINOUS VEGETABLES; MACHINERY USED IN THE MILLING INDUSTRY OR FOR THE WORKING OF CEREALS OR DRIED LEGUMINOUS VEGETABLES, OTHER THAN FARM-TYPE MACHINERY",
+        "hsn_code": "8437",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER AGRICULTURAL, HORTICULTURAL, FORESTRY, POULTRY-KEEPING OR BEE-KEEPING\nMACHINERY, INCLUDING GERMINATION PLANT FITTED WITH MECHANICAL OR THERMAL\nEQUIPMENT; POULTRY INCUBATORS AND BROODERS - MACHINERY FOR PREPARING ANIMAL\nFEEDING STUFFS",
+        "hsn_code": "84361000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER AGRICULTURAL, HORTICULTURAL, FORESTRY, POULTRY-KEEPING OR BEE-KEEPING\nMACHINERY, INCLUDING GERMINATION PLANT FITTED WITH MECHANICAL OR THERMAL\nEQUIPMENT; POULTRY INCUBATORS AND BROODERS - POULTRY-KEEPING MACHINERY; POULTRY\nINCUBATORS AND BROODERS :POULTRY INCUBATORS AND BROODERS",
+        "hsn_code": "84362100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER AGRICULTURAL, HORTICULTURAL, FORESTRY, POULTRY-KEEPING OR BEE-KEEPING\nMACHINERY, INCLUDING GERMINATION PLANT FITTED WITH MECHANICAL OR THERMAL\nEQUIPMENT; POULTRY INCUBATORS AND BROODERS - POULTRY-KEEPING MACHINERY; POULTRY\nINCUBATORS AND BROODERS: OTHER",
+        "hsn_code": "84362900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER AGRICULTURAL, HORTICULTURAL, FORESTRY, POULTRY-KEEPING OR BEE-KEEPING\nMACHINERY, INCLUDING GERMINATION PLANT FITTED WITH MECHANICAL OR THERMAL\nEQUIPMENT; POULTRY INCUBATORS AND BROODERS - OTHER MACHINERY: GERMINATION\nPLANT FITTED WITH MECHANICAL AND THERMAL EQUIPMENT",
+        "hsn_code": "84368010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER AGRICULTURAL, HORTICULTURAL, FORESTRY, POULTRY-KEEPING OR BEE-KEEPING\nMACHINERY, INCLUDING GERMINATION PLANT FITTED WITH MECHANICAL OR THERMAL\nEQUIPMENT; POULTRY INCUBATORS AND BROODERS - OTHER MACHINERY: OTHER",
+        "hsn_code": "84368090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER AGRICULTURAL, HORTICULTURAL, FORESTRY, POULTRY-KEEPING OR BEE-KEEPING\nMACHINERY, INCLUDING GERMINATION PLANT FITTED WITH MECHANICAL OR THERMAL\nEQUIPMENT; POULTRY INCUBATORS AND BROODERS - PARTS: OF POULTRY-KEEPING\nMACHINERY OR POULTRY INCUBATORS AND BROODERS",
+        "hsn_code": "84369100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER AGRICULTURAL, HORTICULTURAL, FORESTRY, POULTRY-KEEPING OR BEE-KEEPING\nMACHINERY, INCLUDING GERMINATION PLANT FITTED WITH MECHANICAL OR THERMAL\nEQUIPMENT; POULTRY INCUBATORS AND BROODERS - PARTS: OTHER",
+        "hsn_code": "84369900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER AGRICULTURAL, HORTICULTURAL, FORESTRY, POULTRY-KEEPING OR BEE-KEEPING MACHINERY, INCLUDING GERMINATION PLANT FITTED WITH MECHANICAL OR THERMAL EQUIPMENT; POULTRY INCUBATORS AND BROODERS",
+        "hsn_code": "8436",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PRESSES, CRUSHERS AND SIMILAR MACHINERY USED IN THE MANUFACTURE OF WINE, CIDER,\nFRUIT JUICES OR SIMILAR BEVERAGES -MACHINERY",
+        "hsn_code": "84351000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRESSES, CRUSHERS AND SIMILAR MACHINERY USED IN THE MANUFACTURE OF WINE, CIDER,\nFRUIT JUICES OR SIMILAR BEVERAGES - PARTS",
+        "hsn_code": "84359000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRESSES, CRUSHERS & SIMILAR MACHINERY USED IN THE MANUFACTURE OF WINE, CIDER, FRUIT JUICES OR SIMILAR BEVERAGES",
+        "hsn_code": "8435",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MILKING MACHINES AND DAIRY MACHINERY MILKING MACHINES",
+        "hsn_code": "84341000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MILKING MACHINES AND DAIRY MACHINERY DAIRY MACHINERY",
+        "hsn_code": "84342000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MILKING MACHINES AND DAIRY MACHINERY - PARTS :OF MILKING MACHINERY",
+        "hsn_code": "84349010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MILKING MACHINES AND DAIRY MACHINERY - PARTS :OF DAIRY MACHINERY",
+        "hsn_code": "84349020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MILKING MACHINES AND DAIRY MACHINERY",
+        "hsn_code": "8434",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HARVESTING OR THRESHING MACHINERY, INCLUDING STRAW OR FODDER BALERS; GRASS OR\nHAY MOWERS; MACHINES FOR CLEANING, SORTING OR GRADING EGGS, FRUIT OR OTHER\nAGRICULTURAL PRODUCE, OTHER THAN MACHINERY OF HEADING 8437 - MOWERS FOR LAWNS,\nPARKS OR SPORTS-GROUNDS - POWERED WITH THE CUTTING DEVICE ROTATING IN A\nHORIZONTAL PLANE: POWERED WITH 3 HP OR MORE",
+        "hsn_code": "84331110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HARVESTING OR THRESHING MACHINERY, INCLUDING STRAW OR FODDER BALERS; GRASS OR\nHAY MOWERS; MACHINES FOR CLEANING, SORTING OR GRADING EGGS, FRUIT OR OTHER\nAGRICULTURAL PRODUCE, OTHER THAN MACHINERY OF HEADING 8437 - MOWERS FOR LAWNS,\nPARKS OR SPORTS-GROUNDS - POWERED WITH THE CUTTING DEVICE ROTATING IN A\nHORIZONTAL PLANE: OTHER",
+        "hsn_code": "84331190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HARVESTING OR THRESHING MACHINERY, INCLUDING STRAW OR FODDER BALERS; GRASS OR\nHAY MOWERS; MACHINES FOR CLEANING, SORTING OR GRADING EGGS, FRUIT OR OTHER\nAGRICULTURAL PRODUCE, OTHER THAN MACHINERY OF HEADING 8437 - MOWERS FOR LAWNS,\nPARKS OR SPORTS-GROUNDS - OTHER: NON-POWERED MOWERS, HAVING WIDTH OF 75 CM OR\nMORE",
+        "hsn_code": "84331910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HARVESTING OR THRESHING MACHINERY, INCLUDING STRAW OR FODDER BALERS; GRASS OR\nHAY MOWERS; MACHINES FOR CLEANING, SORTING OR GRADING EGGS, FRUIT OR OTHER\nAGRICULTURAL PRODUCE, OTHER THAN MACHINERY OF HEADING 8437 - MOWERS FOR LAWNS,\nPARKS OR SPORTS-GROUNDS - OTHER: OTHER",
+        "hsn_code": "84331990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HARVESTING OR THRESHING MACHINERY, INCLUDING STRAW OR FODDER BALERS; GRASS OR\nHAY MOWERS; MACHINES FOR CLEANING, SORTING OR GRADING EGGS, FRUIT OR OTHER\nAGRICULTURAL PRODUCE, OTHER THAN MACHINERY OF HEADING 8437 - OTHER MOWERS,\nINCLUDING CUTTER BARS FOR TRACTOR MOUNTING",
+        "hsn_code": "84332000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HARVESTING OR THRESHING MACHINERY, INCLUDING STRAW OR FODDER BALERS; GRASS OR\nHAY MOWERS; MACHINES FOR CLEANING, SORTING OR GRADING EGGS, FRUIT OR OTHER\nAGRICULTURAL PRODUCE, OTHER THAN MACHINERY OF HEADING 8437 - OTHER HAYMAKING\nMACHINERY",
+        "hsn_code": "84333000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HARVESTING OR THRESHING MACHINERY, INCLUDING STRAW OR FODDER BALERS; GRASS OR\nHAY MOWERS; MACHINES FOR CLEANING, SORTING OR GRADING EGGS, FRUIT OR OTHER\nAGRICULTURAL PRODUCE, OTHER THAN MACHINERY OF HEADING 8437 - STRAW OR FODDER\nBALERS, INCLUDING PICK-UP BALERS",
+        "hsn_code": "84334000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HARVESTING OR THRESHING MACHINERY, INCLUDING STRAW OR FODDER BALERS; GRASS OR\nHAY MOWERS; MACHINES FOR CLEANING, SORTING OR GRADING EGGS, FRUIT OR OTHER\nAGRICULTURAL PRODUCE, OTHER THAN MACHINERY OF HEADING 8437- OTHER HARVESTING\nMACHINERY; THRESHING MACHINERY: COMBINE HARVESTER- THRESHERS",
+        "hsn_code": "84335100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HARVESTING OR THRESHING MACHINERY, INCLUDING STRAW OR FODDER BALERS; GRASS OR\nHAY MOWERS; MACHINES FOR CLEANING, SORTING OR GRADING EGGS, FRUIT OR OTHER\nAGRICULTURAL PRODUCE, OTHER THAN MACHINERY OF HEADING 8437- OTHER HARVESTING\nMACHINERY; THRESHING MACHINERY : OTHER THRESHING MACHINERY",
+        "hsn_code": "84335200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HARVESTING OR THRESHING MACHINERY, INCLUDING STRAW OR FODDER BALERS; GRASS OR\nHAY MOWERS; MACHINES FOR CLEANING, SORTING OR GRADING EGGS, FRUIT OR OTHER\nAGRICULTURAL PRODUCE, OTHER THAN MACHINERY OF HEADING 8437- OTHER HARVESTING\nMACHINERY; THRESHING MACHINERY :ROOT OR TUBER HARVESTING MACHINES",
+        "hsn_code": "84335300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HARVESTING OR THRESHING MACHINERY, INCLUDING STRAW OR FODDER BALERS; GRASS OR\nHAY MOWERS; MACHINES FOR CLEANING, SORTING OR GRADING EGGS, FRUIT OR OTHER\nAGRICULTURAL PRODUCE, OTHER THAN MACHINERY OF HEADING 8437- OTHER HARVESTING\nMACHINERY; THRESHING MACHINERY: OTHER",
+        "hsn_code": "84335900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "60 HARVESTING OR THRESHING MACHINERY, INCLUDING STRAW OR FODDER BALERS; GRASS OR\nHAY MOWERS; MACHINES FOR CLEANING, SORTING OR GRADING EGGS, FRUIT OR OTHER\nAGRICULTURAL PRODUCE, OTHER THAN MACHINERY OF HEADING 8437- MACHINES FOR\nCLEANING, SORTING OR GRADING EGGS, FRUIT OR OTHER AGRICULTURAL PRODUCE: MACHINES\nFOR CLEANING",
+        "hsn_code": "84336010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "60 HARVESTING OR THRESHING MACHINERY, INCLUDING STRAW OR FODDER BALERS; GRASS OR\nHAY MOWERS; MACHINES FOR CLEANING, SORTING OR GRADING EGGS, FRUIT OR OTHER\nAGRICULTURAL PRODUCE, OTHER THAN MACHINERY OF HEADING 8437- MACHINES FOR\nCLEANING, SORTING OR GRADING EGGS, FRUIT OR OTHER AGRICULTURAL PRODUCE: MACHINES\nFOR SORTING OR GRADING",
+        "hsn_code": "84336020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "60 HARVESTING OR THRESHING MACHINERY, INCLUDING STRAW OR FODDER BALERS; GRASS OR\nHAY MOWERS; MACHINES FOR CLEANING, SORTING OR GRADING EGGS, FRUIT OR OTHER\nAGRICULTURAL PRODUCE, OTHER THAN MACHINERY OF HEADING 8437 - PARTS",
+        "hsn_code": "84339000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HARVESTING OR THRESHING MACHINERY, INCLUDING STRAW OR FODDER BALERS; GRASS OR HAY MOWERS; MACHINES FOR CLEANING, SORTING OR GRADING EGGS, FRUIT OR OTHER AGRICULTURAL PRODUCE, OTHER THAN MACHINERY OF HEADING 8437",
+        "hsn_code": "8433",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AGRICULTURAL, HORTICULTURAL OR FORESTRY MACHINERY FOR SOIL PREPARATION OR\nCULTIVATION; LAWN OR SPORTSGROUND ROLLERS 8432 10 - PLOUGHS: DISC PLOUGHS",
+        "hsn_code": "84321010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AGRICULTURAL, HORTICULTURAL OR FORESTRY MACHINERY FOR SOIL PREPARATION OR\nCULTIVATION; LAWN OR SPORTSGROUND ROLLERS 8432 10 - PLOUGHS: OTHER TRACTOR\nPLOUGHS",
+        "hsn_code": "84321020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AGRICULTURAL, HORTICULTURAL OR FORESTRY MACHINERY FOR SOIL PREPARATION OR\nCULTIVATION; LAWN OR SPORTSGROUND ROLLERS 8432 10 - PLOUGHS: OTHER",
+        "hsn_code": "84321090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AGRICULTURAL, HORTICULTURAL OR FORESTRY MACHINERY FOR SOIL PREPARATION OR\nCULTIVATION; LAWN OR SPORTSGROUND ROLLERS - HARROWS, SCARIFIERS, CULTIVATORS,\nWEEDERS AND HOES: DISC HARROWS",
+        "hsn_code": "84322100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AGRICULTURAL, HORTICULTURAL OR FORESTRY MACHINERY FOR SOIL PREPARATION OR\nCULTIVATION; LAWN OR SPORTSGROUND ROLLERS - HARROWS, SCARIFIERS, CULTIVATORS,\nWEEDERS AND HOES: OTHER: ROTARY HOES",
+        "hsn_code": "84322910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AGRICULTURAL, HORTICULTURAL OR FORESTRY MACHINERY FOR SOIL PREPARATION OR\nCULTIVATION; LAWN OR SPORTSGROUND ROLLERS - HARROWS, SCARIFIERS, CULTIVATORS,\nWEEDERS AND HOES: OTHER: OTHER",
+        "hsn_code": "84322990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AGRICULTURAL, HORTICULTURAL OR FORESTRY MACHINERY FOR SOIL PREPARATION OR\nCULTIVATION; LAWN OR SPORTSGROUND ROLLERS SEEDERS, PLANTERS AND TRANSPLANTERS",
+        "hsn_code": "84323000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AGRICULTURAL, HORTICULTURAL OR FORESTRY MACHINERY FOR SOIL PREPARATION OR\nCULTIVATION; LAWN OR SPORTSGROUND ROLLERS MANURE SPREADERS AND FERTILIZER\nDISTRIBUTORS",
+        "hsn_code": "84324000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AGRICULTURAL, HORTICULTURAL OR FORESTRY MACHINERY FOR SOIL PREPARATION OR\nCULTIVATION; LAWN OR SPORTSGROUND ROLLERS - OTHER MACHINERY: LAWN OR SPORTS\nGROUND ROLLERS",
+        "hsn_code": "84328010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AGRICULTURAL, HORTICULTURAL OR FORESTRY MACHINERY FOR SOIL PREPARATION OR\nCULTIVATION; LAWN OR SPORTSGROUND ROLLERS - OTHER MACHINERY: ROTARY TILLER",
+        "hsn_code": "84328020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AGRICULTURAL, HORTICULTURAL OR FORESTRY MACHINERY FOR SOIL PREPARATION OR\nCULTIVATION; LAWN OR SPORTSGROUND ROLLERS - OTHER MACHINERY: OTHER",
+        "hsn_code": "84328090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "90 AGRICULTURAL, HORTICULTURAL OR FORESTRY MACHINERY FOR SOIL PREPARATION OR\nCULTIVATION; LAWN OR SPORTSGROUND ROLLERS - PARTS: PARTS OF AGRICULTURAL\nMACHINERY FALLING WITHIN HEADINGS 8432 10, 8432 21, 8432 29, 8432 30 AND 8432 40",
+        "hsn_code": "84329010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "90 AGRICULTURAL, HORTICULTURAL OR FORESTRY MACHINERY FOR SOIL PREPARATION OR\nCULTIVATION; LAWN OR SPORTSGROUND ROLLERS - PARTS: OTHER",
+        "hsn_code": "84329090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AGRICULTURAL, HORTICULTURAL OR FORESTRY MACHINERY OF SOIL PREPARATION OR CULTIVATION; LAWN OR SPORTS-GROUND ROLLERS",
+        "hsn_code": "8432",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINERY OF HEADINGS 8425 TO\n8430 - OF MACHINERY OF HEADING 8425 :OF PULLEY TACKLE AND HOISTS, OTHER THAN SHIP\nHOISTS, WINCHES OR CAPSTANS",
+        "hsn_code": "84311010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINERY OF HEADINGS 8425 TO\n8430 - OF MACHINERY OF HEADING 8425 :OTHER",
+        "hsn_code": "84311090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINERY OF HEADINGS 8425 TO\n8430 - OF MACHINERY OF HEADING 8427 :OF FORK LIFT TRUCKS",
+        "hsn_code": "84312010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINERY OF HEADINGS 8425 TO\n8430 - OF MACHINERY OF HEADING 8427 :OTHER",
+        "hsn_code": "84312090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINERY OF HEADINGS 8425 TO\n8430 - OF MACHINERY OF HEADING 8428: OF LIFTS, SKIP HOISTS OR ESCALATORS",
+        "hsn_code": "84313100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINERY OF HEADINGS 8425 TO\n8430 - OF MACHINERY OF HEADING 8428: OTHER : OF ELEVATORS, CONVEYORS AND MOVING\nEQUIPMENTS",
+        "hsn_code": "84313910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINERY OF HEADINGS 8425 TO\n8430 - OF MACHINERY OF HEADING 8428: OTHER : OTHER",
+        "hsn_code": "84313990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINERY OF HEADINGS 8425 TO\n8430 - OF MACHINERY OF HEADING 8426, 8429 OR 8430 :BUCKETS, SHOVELS, GRABS AND GRIPS",
+        "hsn_code": "84314100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINERY OF HEADINGS 8425 TO\n8430 - OF MACHINERY OF HEADING 8426, 8429 OR 8430 :BULLDOZERS OR ANGLEDOZER BLADES",
+        "hsn_code": "84314200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINERY OF HEADINGS 8425 TO\n8430 - OF MACHINERY OF HEADING 8426, 8429 OR 8430- PARTS OF BORING OR SINKING\nMACHINERY OF SUB-HEADING 8430 41 OR 8430 49: OF BORING OR SINKING MACHINERY, SELF\nPROPELLED",
+        "hsn_code": "84314310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINERY OF HEADINGS 8425 TO\n8430 - OF MACHINERY OF HEADING 8426, 8429 OR 8430- PARTS OF BORING OR SINKING\nMACHINERY OF SUB-HEADING 8430 41 OR 8430 49: OTHER",
+        "hsn_code": "84314390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINERY OF HEADINGS 8425 TO\n8430 - OF MACHINERY OF HEADING 8426, 8429 OR 8430- OTHER : OF ROAD ROLLERS,\nMECHANICALLY PROPELLED",
+        "hsn_code": "84314910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINERY OF HEADINGS 8425 TO\n8430 - OF MACHINERY OF HEADING 8426, 8429 OR 8430- OTHER : OF SHIPS DERRICKS AND\nCRANES",
+        "hsn_code": "84314920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINERY OF HEADINGS 8425 TO\n8430 - OF MACHINERY OF HEADING 8426, 8429 OR 8430- OTHER : OF OTHER EXCAVATING,\nLEVELLING, TAMPING OR EXCAVATING MACHINERY FOR EARTH, MINERAL OR ORES",
+        "hsn_code": "84314930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINERY OF HEADINGS 8425 TO\n8430 - OF MACHINERY OF HEADING 8426, 8429 OR 8430- OTHER : OF PILE DRIVER, SNOW\nPLOUGH, NOT SELF- PROPELLED",
+        "hsn_code": "84314940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINERY OF HEADINGS 8425 TO\n8430 - OF MACHINERY OF HEADING 8426, 8429 OR 8430- OTHER : OTHER",
+        "hsn_code": "84314990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE MACHINERY OF HEADINGS 8425 TO 8430",
+        "hsn_code": "8431",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MOVING, GRADING, LEVELLING, SCRAPING, EXCAVATING, TAMPING, COMPACTING,\nEXTRACTING OR BORING MACHINERY, FOR EARTH, MINERALS OR ORES; PILEDRIVERS AND PILE\nEXTRACTORS; SNOW-PLOUGHS AND SNOW-BLOWERS - PILE-DRIVERS AND PILE-EXTRACTORS:\nPILE-DRIVERS",
+        "hsn_code": "84301010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MOVING, GRADING, LEVELLING, SCRAPING, EXCAVATING, TAMPING, COMPACTING,\nEXTRACTING OR BORING MACHINERY, FOR EARTH, MINERALS OR ORES; PILEDRIVERS AND PILE\nEXTRACTORS; SNOW-PLOUGHS AND SNOW-BLOWERS - PILE-DRIVERS AND PILE-EXTRACTORS:\nPILE-EXTRACTORS",
+        "hsn_code": "84301020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MOVING, GRADING, LEVELLING, SCRAPING, EXCAVATING, TAMPING, COMPACTING,\nEXTRACTING OR BORING MACHINERY, FOR EARTH, MINERALS OR ORES; PILEDRIVERS AND PILE\nEXTRACTORS; SNOW-PLOUGHS AND SNOW-BLOWERS - SNOW-PLOUGHS AND SNOW-BLOWERS",
+        "hsn_code": "84302000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MOVING, GRADING, LEVELLING, SCRAPING, EXCAVATING, TAMPING, COMPACTING,\nEXTRACTING OR BORING MACHINERY, FOR EARTH, MINERALS OR ORES; PILEDRIVERS AND PILE\nEXTRACTORS; SNOW-PLOUGHS AND SNOW-BLOWERS - COAL OR ROCK CUTTERS AND\nTUNNELING MACHINERY: SELF-PROPELLED: COAL CUTTERS",
+        "hsn_code": "84303110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MOVING, GRADING, LEVELLING, SCRAPING, EXCAVATING, TAMPING, COMPACTING,\nEXTRACTING OR BORING MACHINERY, FOR EARTH, MINERALS OR ORES; PILEDRIVERS AND PILE\nEXTRACTORS; SNOW-PLOUGHS AND SNOW-BLOWERS - COAL OR ROCK CUTTERS AND\nTUNNELING MACHINERY: SELF-PROPELLED: TUNNELING MACHINERY",
+        "hsn_code": "84303120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MOVING, GRADING, LEVELLING, SCRAPING, EXCAVATING, TAMPING, COMPACTING,\nEXTRACTING OR BORING MACHINERY, FOR EARTH, MINERALS OR ORES; PILEDRIVERS AND PILE\nEXTRACTORS; SNOW-PLOUGHS AND SNOW-BLOWERS - COAL OR ROCK CUTTERS AND\nTUNNELING MACHINERY: SELF-PROPELLED: OTHER",
+        "hsn_code": "84303190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MOVING, GRADING, LEVELLING, SCRAPING, EXCAVATING, TAMPING, COMPACTING,\nEXTRACTING OR BORING MACHINERY, FOR EARTH, MINERALS OR ORES; PILEDRIVERS AND PILE\nEXTRACTORS; SNOW-PLOUGHS AND SNOW-BLOWERS - COAL OR ROCK CUTTERS AND\nTUNNELING MACHINERY: OTHER",
+        "hsn_code": "84303900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MOVING, GRADING, LEVELLING, SCRAPING, EXCAVATING, TAMPING, COMPACTING,\nEXTRACTING OR BORING MACHINERY, FOR EARTH, MINERALS OR ORES; PILEDRIVERS AND PILE\nEXTRACTORS; SNOW-PLOUGHS AND SNOW-BLOWERS - - OTHER BORING OR SINKING\nMACHINERY - SELF-PROPELLED: TUBE WELL DRILLING AND CORE DRILLING MACHINERY",
+        "hsn_code": "84304110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MOVING, GRADING, LEVELLING, SCRAPING, EXCAVATING, TAMPING, COMPACTING,\nEXTRACTING OR BORING MACHINERY, FOR EARTH, MINERALS OR ORES; PILEDRIVERS AND PILE\nEXTRACTORS; SNOW-PLOUGHS AND SNOW-BLOWERS - - OTHER BORING OR SINKING\nMACHINERY - SELF-PROPELLED: PETROLEUM AND GAS WELL DRILLING MACHINERY",
+        "hsn_code": "84304120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MOVING, GRADING, LEVELLING, SCRAPING, EXCAVATING, TAMPING, COMPACTING,\nEXTRACTING OR BORING MACHINERY, FOR EARTH, MINERALS OR ORES; PILEDRIVERS AND PILE\nEXTRACTORS; SNOW-PLOUGHS AND SNOW-BLOWERS - - OTHER BORING OR SINKING\nMACHINERY - SELF-PROPELLED: ROCK DRILLING MACHINERY",
+        "hsn_code": "84304130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MOVING, GRADING, LEVELLING, SCRAPING, EXCAVATING, TAMPING, COMPACTING,\nEXTRACTING OR BORING MACHINERY, FOR EARTH, MINERALS OR ORES; PILEDRIVERS AND PILE\nEXTRACTORS; SNOW-PLOUGHS AND SNOW-BLOWERS - - OTHER BORING OR SINKING\nMACHINERY - SELF-PROPELLED: OTHER",
+        "hsn_code": "84304190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MOVING, GRADING, LEVELLING, SCRAPING, EXCAVATING, TAMPING, COMPACTING,\nEXTRACTING OR BORING MACHINERY, FOR EARTH, MINERALS OR ORES; PILEDRIVERS AND PILE\nEXTRACTORS; SNOW-PLOUGHS AND SNOW-BLOWERS - OTHER BORING OR SINKING MACHINERY\n- OTHER",
+        "hsn_code": "84304900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MOVING, GRADING, LEVELLING, SCRAPING, EXCAVATING, TAMPING, COMPACTING,\nEXTRACTING OR BORING MACHINERY, FOR EARTH, MINERALS OR ORES; PILEDRIVERS AND PILE\nEXTRACTORS; SNOW-PLOUGHS AND SNOW-BLOWERS - OTHER MACHINERY, SELF-PROPELLED:\nMINING MACHINERY (EXCLUDING COAL MINING)",
+        "hsn_code": "84305010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MOVING, GRADING, LEVELLING, SCRAPING, EXCAVATING, TAMPING, COMPACTING,\nEXTRACTING OR BORING MACHINERY, FOR EARTH, MINERALS OR ORES; PILEDRIVERS AND PILE\nEXTRACTORS; SNOW-PLOUGHS AND SNOW-BLOWERS - OTHER MACHINERY, SELF-PROPELLED:\nOTHER",
+        "hsn_code": "84305090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MOVING, GRADING, LEVELLING, SCRAPING, EXCAVATING, TAMPING, COMPACTING,\nEXTRACTING OR BORING MACHINERY, FOR EARTH, MINERALS OR ORES; PILEDRIVERS AND PILE\nEXTRACTORS; SNOW-PLOUGHS AND SNOW-BLOWERS - OTHER MACHINERY, NOT SELF\nPROPELLED: TAMPING OR COMPACTING MACHINERY",
+        "hsn_code": "84306100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MOVING, GRADING, LEVELLING, SCRAPING, EXCAVATING, TAMPING, COMPACTING,\nEXTRACTING OR BORING MACHINERY, FOR EARTH, MINERALS OR ORES; PILEDRIVERS AND PILE\nEXTRACTORS; SNOW-PLOUGHS AND SNOW-BLOWERS - OTHER MACHINERY, NOT SELF\nPROPELLED: OTHER",
+        "hsn_code": "84306900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MOVING, GRADING, LEVELLING, SCRAPPING, EXCAVATING, TAMPING, COMPACTING, EXTRACTING OR BORING MACHINERY, FOR EARTH, MINERALS OR ORES; PILE-DRIVERS AND PILE-EXTRACTORS; SNOW-PLOUGHS AND SNOW-BLOWERS",
+        "hsn_code": "8430",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF-PROPELLED BULLDOZERS, ANGLEDOZERS, GRADERS, LEVELLERS, SCRAPERS, MECHANICAL\nSHOVELS, EXCAVATORS, SHOVEL LOADERS, TAMPING MACHINES AND ROAD ROLLERS -\nBULLDOZERS AND ANGLEDOZERS - TRACK LAYING :ANGLEDOZERS",
+        "hsn_code": "84291110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF-PROPELLED BULLDOZERS, ANGLEDOZERS, GRADERS, LEVELLERS, SCRAPERS, MECHANICAL\nSHOVELS, EXCAVATORS, SHOVEL LOADERS, TAMPING MACHINES AND ROAD ROLLERS -\nBULLDOZERS AND ANGLEDOZERS - TRACK LAYING : BULLDOZERS",
+        "hsn_code": "84291120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF-PROPELLED BULLDOZERS, ANGLEDOZERS, GRADERS, LEVELLERS, SCRAPERS, MECHANICAL\nSHOVELS, EXCAVATORS, SHOVEL LOADERS, TAMPING MACHINES AND ROAD ROLLERS -\nBULLDOZERS AND ANGLEDOZERS - OTHER: ANGLEDOZERS",
+        "hsn_code": "84291910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF-PROPELLED BULLDOZERS, ANGLEDOZERS, GRADERS, LEVELLERS, SCRAPERS, MECHANICAL\nSHOVELS, EXCAVATORS, SHOVEL LOADERS, TAMPING MACHINES AND ROAD ROLLERS -\nBULLDOZERS AND ANGLEDOZERS - OTHER:BULLDOZERS",
+        "hsn_code": "84291920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF-PROPELLED BULLDOZERS, ANGLEDOZERS, GRADERS, LEVELLERS, SCRAPERS, MECHANICAL\nSHOVELS, EXCAVATORS, SHOVEL LOADERS, TAMPING MACHINES AND ROAD ROLLERS - GRADERS\nAND LEVELLERS",
+        "hsn_code": "84292000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF-PROPELLED BULLDOZERS, ANGLEDOZERS, GRADERS, LEVELLERS, SCRAPERS, MECHANICAL\nSHOVELS, EXCAVATORS, SHOVEL LOADERS, TAMPING MACHINES AND ROAD ROLLERS -\nSCRAPPERS",
+        "hsn_code": "84293000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF-PROPELLED BULLDOZERS, ANGLEDOZERS, GRADERS, LEVELLERS, SCRAPERS, MECHANICAL\nSHOVELS, EXCAVATORS, SHOVEL LOADERS, TAMPING MACHINES AND ROAD ROLLERS -\nTAMPING MACHINES AND ROAD ROLLERS :ROAD ROLLERS UPTO 5 TONS CAPACITY",
+        "hsn_code": "84294010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF-PROPELLED BULLDOZERS, ANGLEDOZERS, GRADERS, LEVELLERS, SCRAPERS, MECHANICAL\nSHOVELS, EXCAVATORS, SHOVEL LOADERS, TAMPING MACHINES AND ROAD ROLLERS -\nTAMPING MACHINES AND ROAD ROLLERS :ROAD ROLLERS ABOVE 5 TONS CAPACITY",
+        "hsn_code": "84294020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF-PROPELLED BULLDOZERS, ANGLEDOZERS, GRADERS, LEVELLERS, SCRAPERS, MECHANICAL\nSHOVELS, EXCAVATORS, SHOVEL LOADERS, TAMPING MACHINES AND ROAD ROLLERS -\nTAMPING MACHINES AND ROAD ROLLERS :TAMPING MACHINES",
+        "hsn_code": "84294030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF-PROPELLED BULLDOZERS, ANGLEDOZERS, GRADERS, LEVELLERS, SCRAPERS, MECHANICAL\nSHOVELS, EXCAVATORS, SHOVEL LOADERS, TAMPING MACHINES AND ROAD ROLLERS -\nMECHANICAL SHOVELS, EXCAVATORS AND SHOVEL LOADERS: FRONT-END SHOVEL LOADERS",
+        "hsn_code": "84295100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF-PROPELLED BULLDOZERS, ANGLEDOZERS, GRADERS, LEVELLERS, SCRAPERS, MECHANICAL\nSHOVELS, EXCAVATORS, SHOVEL LOADERS, TAMPING MACHINES AND ROAD ROLLERS -\nMECHANICAL SHOVELS, EXCAVATORS AND SHOVEL LOADERS: MACHINERY WITH A 360 DEGREES\nREVOLVING SUPERSTRUCTURE",
+        "hsn_code": "84295200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF-PROPELLED BULLDOZERS, ANGLEDOZERS, GRADERS, LEVELLERS, SCRAPERS, MECHANICAL\nSHOVELS, EXCAVATORS, SHOVEL LOADERS, TAMPING MACHINES AND ROAD ROLLERS -\nMECHANICAL SHOVELS, EXCAVATORS AND SHOVEL LOADERS: OTHER",
+        "hsn_code": "84295900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF-PROPELLED BULLDOZERS, ANGLEDOZERS, GRADERS, LEVELLERS, SCRAPERS, MECHANICAL SHOVELS, EXCAVATORS, SHOVEL LOADERS, TAMPING MACHINES AND ROAD ROLLERS",
+        "hsn_code": "8429",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIFTING, HANDLING, LOADING OR UNLOADING MACHINERY (FOR EXAMPLE, LIFTS,\nESCALATORS, CONVEYORS, TELEFERICS) 8428 10 - LIFTS AND SKIP HOISTS: LIFTS: LIFTS OF A KIND\nUSED IN BUILDINGS",
+        "hsn_code": "84281011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIFTING, HANDLING, LOADING OR UNLOADING MACHINERY (FOR EXAMPLE, LIFTS,\nESCALATORS, CONVEYORS, TELEFERICS) 8428 10 - LIFTS AND SKIP HOISTS: LIFTS: OTHER",
+        "hsn_code": "84281019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIFTING, HANDLING, LOADING OR UNLOADING MACHINERY (FOR EXAMPLE, LIFTS,\nESCALATORS, CONVEYORS, TELEFERICS) - LIFTS AND SKIP HOISTS: SKIP HOISTS",
+        "hsn_code": "84281020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIFTING, HANDLING, LOADING OR UNLOADING MACHINERY (FOR EXAMPLE, LIFTS,\nESCALATORS, CONVEYORS, TELEFERICS) - PNEUMATIC ELEVATORS AND CONVEYORS:\nCONVEYORS: BELT CONVEYORS",
+        "hsn_code": "84282011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIFTING, HANDLING, LOADING OR UNLOADING MACHINERY (FOR EXAMPLE, LIFTS,\nESCALATORS, CONVEYORS, TELEFERICS) - PNEUMATIC ELEVATORS AND CONVEYORS:\nCONVEYORS: OTHER",
+        "hsn_code": "84282019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIFTING, HANDLING, LOADING OR UNLOADING MACHINERY (FOR EXAMPLE, LIFTS,\nESCALATORS, CONVEYORS, TELEFERICS) - PNEUMATIC ELEVATORS AND CONVEYORS PNEUMATIC\nELEVATORS",
+        "hsn_code": "84282020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIFTING, HANDLING, LOADING OR UNLOADING MACHINERY (FOR EXAMPLE, LIFTS,\nESCALATORS, CONVEYORS, TELEFERICS) - OTHER CONTINUOUS-ACTION ELEVATORS AND\nCONVEYORS, FOR GOODS OR MATERIALS: SPECIALLY DESIGNED FOR UNDERGROUND USE",
+        "hsn_code": "84283100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIFTING, HANDLING, LOADING OR UNLOADING MACHINERY (FOR EXAMPLE, LIFTS,\nESCALATORS, CONVEYORS, TELEFERICS) - OTHER CONTINUOUS-ACTION ELEVATORS AND\nCONVEYORS, FOR GOODS OR MATERIALS: OTHER, BUCKET TYPE",
+        "hsn_code": "84283200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIFTING, HANDLING, LOADING OR UNLOADING MACHINERY (FOR EXAMPLE, LIFTS,\nESCALATORS, CONVEYORS, TELEFERICS) - OTHER CONTINUOUS-ACTION ELEVATORS AND\nCONVEYORS, FOR GOODS OR MATERIALS: OTHER, BELT TYPE",
+        "hsn_code": "84283300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIFTING, HANDLING, LOADING OR UNLOADING MACHINERY (FOR EXAMPLE, LIFTS,\nESCALATORS, CONVEYORS, TELEFERICS) - OTHER CONTINUOUS-ACTION ELEVATORS AND\nCONVEYORS, FOR GOODS OR MATERIALS: OTHER",
+        "hsn_code": "84283900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIFTING, HANDLING, LOADING OR UNLOADING MACHINERY (FOR EXAMPLE, LIFTS,\nESCALATORS, CONVEYORS, TELEFERICS) - ESCALATORS AND MOVING WALKWAYS",
+        "hsn_code": "84284000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIFTING, HANDLING, LOADING OR UNLOADING MACHINERY (FOR EXAMPLE, LIFTS,\nESCALATORS, CONVEYORS, TELEFERICS) - MINE WAGON PUSHERS, LOCOMOTIVE OR WAGON\nTRAVERSERS, WAGON TIPPERS AND SIMILAR RAILWAY WAGON HANDLING EQUIPMENT:\nWAGON MARSHALLING EQUIPMENT",
+        "hsn_code": "84285010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIFTING, HANDLING, LOADING OR UNLOADING MACHINERY (FOR EXAMPLE, LIFTS,\nESCALATORS, CONVEYORS, TELEFERICS) - MINE WAGON PUSHERS, LOCOMOTIVE OR WAGON\nTRAVERSERS, WAGON TIPPERS AND SIMILAR RAILWAY WAGON HANDLING EQUIPMENT:\nWAGON TIPPERS",
+        "hsn_code": "84285020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIFTING, HANDLING, LOADING OR UNLOADING MACHINERY (FOR EXAMPLE, LIFTS,\nESCALATORS, CONVEYORS, TELEFERICS) - MINE WAGON PUSHERS, LOCOMOTIVE OR WAGON\nTRAVERSERS, WAGON TIPPERS AND SIMILAR RAILWAY WAGON HANDLING EQUIPMENT: OTHER",
+        "hsn_code": "84285090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIFTING, HANDLING, LOADING OR UNLOADING MACHINERY (FOR EXAMPLE, LIFTS,\nESCALATORS, CONVEYORS, TELEFERICS) - TELEFERICS, CHAIR-LIFTS, SKI-RAGLINES, TRACTION\nMECHANISMS FOR FUNICULARS",
+        "hsn_code": "84286000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIFTING, HANDLING, LOADING OR UNLOADING MACHINERY (FOR EXAMPLE, LIFTS,\nESCALATORS, CONVEYORS, TELEFERICS) - OTHER MACHINERY : FOR COAL HANDLING",
+        "hsn_code": "84289010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIFTING, HANDLING, LOADING OR UNLOADING MACHINERY (FOR EXAMPLE, LIFTS,\nESCALATORS, CONVEYORS, TELEFERICS) - OTHER MACHINERY : FOR ASH HANDLING",
+        "hsn_code": "84289020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIFTING, HANDLING, LOADING OR UNLOADING MACHINERY (FOR EXAMPLE, LIFTS,\nESCALATORS, CONVEYORS, TELEFERICS) - OTHER MACHINERY : OTHER",
+        "hsn_code": "84289090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIFTING, HANDLING, LOADING OR UNLOADING MACHINERY ( FOR EXAMPLE, LIFTS, ESCALATORS, CONVEYORS, TELEFERICS)",
+        "hsn_code": "8428",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FORK-LIFT TRUCKS; OTHER WORKS TRUCKS FITTED WITH LIFTING OR HANDLING EQUIPMENT\nSELF-PROPELLED TRUCKS POWERED BY AN ELECTRIC MOTOR",
+        "hsn_code": "84271000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "8427 FORK-LIFT TRUCKS; OTHER WORKS TRUCKS FITTED WITH LIFTING OR HANDLING\nEQUIPMENT OTHER SELF-PROPELLED TRUCKS",
+        "hsn_code": "84272000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "8427 FORK-LIFT TRUCKS; OTHER WORKS TRUCKS FITTED WITH LIFTING OR HANDLING\nEQUIPMENT OTHER TRUCKS",
+        "hsn_code": "84279000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FORK-LIFT TRUCKS; OTHER WORKS TRUCKS FITTED WITH LIFTING OR HANDLING EQUIPMENT",
+        "hsn_code": "8427",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHIPRRICKS;CRANESINCLUDINGCABLECRANES;MOBILELIFTINGFRAMES,STRADDLECARRIERSAND\nWORKSTRUCKSFITTEDWITHACRANE\nOVERHEADTRAVELLINGCRANES,TRANSPORTERCRANES,GANTRYCRANES,BRIDGECRANES,MOBILE\nLIFTINGFRAMESANDSTRADDLECARRIERS",
+        "hsn_code": "84261100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHIPS DERRICKS; CRANES INCLUDING CABLE CRANES; MOBILE LIFTING FRAMES, STRADDLE\nCARRIERS AND WORKS TRUCKS FITTED WITH A CRANE - OVERHEAD TRAVELLING CRANES,\nTRANSPORTER CRANES, GANTRY CRANES, BRIDGE CRANES, MOBILE LIFTING FRAMES AND\nSTRADDLE CARRIERS: MOBILE LIFTING FRAMES ON TYRES AND STRADDLE CARRIERS",
+        "hsn_code": "84261200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHIPS DERRICKS; CRANES INCLUDING CABLE CRANES; MOBILE LIFTING FRAMES, STRADDLE\nCARRIERS AND WORKS TRUCKS FITTED WITH A CRANE - OVERHEAD TRAVELLING CRANES,\nTRANSPORTER CRANES, GANTRY CRANES, BRIDGE CRANES, MOBILE LIFTING FRAMES AND\nSTRADDLE CARRIERS: OTHER",
+        "hsn_code": "84261900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHIPS DERRICKS; CRANES INCLUDING CABLE CRANES; MOBILE LIFTING FRAMES, STRADDLE\nCARRIERS AND WORKS TRUCKS FITTED WITH A CRANE - TOWER CRANES",
+        "hsn_code": "84262000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHIPS DERRICKS; CRANES INCLUDING CABLE CRANES; MOBILE LIFTING FRAMES, STRADDLE\nCARRIERS AND WORKS TRUCKS FITTED WITH A CRANE - PORTAL OR PEDESTAL JIB CRANES",
+        "hsn_code": "84263000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHIPS DERRICKS; CRANES INCLUDING CABLE CRANES; MOBILE LIFTING FRAMES, STRADDLE\nCARRIERS AND WORKS TRUCKS FITTED WITH A CRANE - OTHER MACHINERY, SELF-PROPELLED:\nON TYRES",
+        "hsn_code": "84264100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHIPS DERRICKS; CRANES INCLUDING CABLE CRANES; MOBILE LIFTING FRAMES, STRADDLE\nCARRIERS AND WORKS TRUCKS FITTED WITH A CRANE - OTHER MACHINERY, SELF-PROPELLED:\nOTHER",
+        "hsn_code": "84264900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHIPS DERRICKS; CRANES INCLUDING CABLE CRANES; MOBILE LIFTING FRAMES, STRADDLE\nCARRIERS AND WORKS TRUCKS FITTED WITH A CRANE - OTHER MACHINERY : DESIGNED FOR\nMOUNTING ON ROAD VEHICLES",
+        "hsn_code": "84269100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHIPS DERRICKS; CRANES INCLUDING CABLE CRANES; MOBILE LIFTING FRAMES, STRADDLE\nCARRIERS AND WORKS TRUCKS FITTED WITH A CRANE - OTHER MACHINERY : OTHER: ROPEWAY\nAND TELPHERS",
+        "hsn_code": "84269910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHIPS DERRICKS; CRANES INCLUDING CABLE CRANES; MOBILE LIFTING FRAMES, STRADDLE\nCARRIERS AND WORKS TRUCKS FITTED WITH A CRANE - OTHER MACHINERY : OTHER: OTHER",
+        "hsn_code": "84269990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHIP’S DERRICKS; CRANES, INCLUDING CABLE CRANES; MOBILE LIFTING FRAMES, STRADDLE CARRIERS AND WORKS TRUCKS FITTED WITH A CRANE",
+        "hsn_code": "8426",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PULLEY TACKLE AND HOISTS OTHER THAN SKIP HOISTS; WINCHES AND CAPSTANS; JACKS -\nPULLEY TACKLES AND HOISTS OTHER THAN SKIP HOISTS OR HOISTS OF A KIND USED FOR\nRAISING VEHICLES: POWERED BY ELECTRIC MOTOR: HOISTS",
+        "hsn_code": "84251110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PULLEY TACKLE AND HOISTS OTHER THAN SKIP HOISTS; WINCHES AND CAPSTANS; JACKS -\nPULLEY TACKLES AND HOISTS OTHER THAN SKIP HOISTS OR HOISTS OF A KIND USED FOR\nRAISING VEHICLES: POWERED BY ELECTRIC MOTOR: PULLEY TACKLE",
+        "hsn_code": "84251120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PULLEY TACKLE AND HOISTS OTHER THAN SKIP HOISTS; WINCHES AND CAPSTANS; JACKS -\nPULLEY TACKLES AND HOISTS OTHER THAN SKIP HOISTS OR HOISTS OF A KIND USED FOR\nRAISING VEHICLES: OTHER : HOISTS MACHINE",
+        "hsn_code": "84251910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PULLEY TACKLE AND HOISTS OTHER THAN SKIP HOISTS; WINCHES AND CAPSTANS; JACKS -\nPULLEY TACKLES AND HOISTS OTHER THAN SKIP HOISTS OR HOISTS OF A KIND USED FOR\nRAISING VEHICLES: OTHER : PULLEY TACKLE",
+        "hsn_code": "84251920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PULLEY TACKLE AND HOISTS OTHER THAN SKIP HOISTS; WINCHES AND CAPSTANS; JACKS PIT\nHEAD WINDING GEAR; WINCHES SPECIALLY DESIGNED FOR USE UNDERGROUND",
+        "hsn_code": "84252000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PULLEY TACKLE AND HOISTS OTHER THAN SKIP HOISTS; WINCHES AND CAPSTANS; JACKS -\nOTHER WINCHES; CAPSTANS: POWERED BY ELECTRIC MOTOR",
+        "hsn_code": "84253100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PULLEY TACKLE AND HOISTS OTHER THAN SKIP HOISTS; WINCHES AND CAPSTANS; JACKS -\nOTHER WINCHES; CAPSTANS: OTHER",
+        "hsn_code": "84253900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PULLEY TACKLE AND HOISTS OTHER THAN SKIP HOISTS; WINCHES AND CAPSTANS; JACKS - JACKS;\nHOISTS OF A KIND USED FOR RAISING VEHICLES: BUILT-IN JACKING SYSTEM OF A TYPE USED IN\nGARAGES",
+        "hsn_code": "84254100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PULLEY TACKLE AND HOISTS OTHER THAN SKIP HOISTS; WINCHES AND CAPSTANS; JACKS - JACKS;\nHOISTS OF A KIND USED FOR RAISING VEHICLES: OTHER JACKS AND HOISTS, HYDRAULIC",
+        "hsn_code": "84254200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PULLEY TACKLE AND HOISTS OTHER THAN SKIP HOISTS; WINCHES AND CAPSTANS; JACKS - JACKS;\nHOISTS OF A KIND USED FOR RAISING VEHICLES: OTHER",
+        "hsn_code": "84254900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PULLEY TACKLE AND HOISTS OTHER THAN SKIP HOISTS; WINCHES AND CAPSTANS; JACKS",
+        "hsn_code": "8425",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MECHANICAL APPLIANCES (WHETHER OR NOT HANDOPERATED) FOR PROJECTING, DISPERSING\nOR SPRAYING LIQUIDS OR POWDERS; FIRE EXTINGUISHERS, WHETHER OR NOT CHARGED; SPRAY\nGUNS AND SIMILAR APPLIANCES; STEAM OR SAND BLASTING MACHINES AND SIMILAR JET\nPROJECTING MACHINES FIRE EXTINGUISHERS, WHETHER OR NOT CHARGED",
+        "hsn_code": "84241000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MECHANICAL APPLIANCES (WHETHER OR NOT HANDOPERATED) FOR PROJECTING, DISPERSING\nOR SPRAYING LIQUIDS OR POWDERS; FIRE EXTINGUISHERS, WHETHER OR NOT CHARGED; SPRAY\nGUNS AND SIMILAR APPLIANCES; STEAM OR SAND BLASTING MACHINES AND SIMILAR JET\nPROJECTING MACHINES SPRAY GUNS AND SIMILAR APPLIANCES",
+        "hsn_code": "84242000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MECHANICAL APPLIANCES (WHETHER OR NOT HANDOPERATED) FOR PROJECTING, DISPERSING\nOR SPRAYING LIQUIDS OR POWDERS; FIRE EXTINGUISHERS, WHETHER OR NOT CHARGED; SPRAY\nGUNS AND SIMILAR APPLIANCES; STEAM OR SAND BLASTING MACHINES AND SIMILAR JET\nPROJECTING MACHINES STEAM OR SAND BLASTING MACHINES AND SIMILAR JET PROJECTING\nMACHINES",
+        "hsn_code": "84243000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MECHANICAL APPLIANCES (WHETHER OR NOT HANDOPERATED) FOR PROJECTING, DISPERSING\nOR SPRAYING LIQUIDS OR POWDERS; FIRE EXTINGUISHERS, WHETHER OR NOT CHARGED; SPRAY\nGUNS AND SIMILAR APPLIANCES; STEAM OR SAND BLASTING MACHINES AND SIMILAR JET\nPROJECTING MACHINES- OTHER APPLIANCES: AGRICULTURAL OR HORTICULTURAL",
+        "hsn_code": "84248100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MECHANICAL APPLIANCES (WHETHER OR NOT HANDOPERATED) FOR PROJECTING, DISPERSING\nOR SPRAYING LIQUIDS OR POWDERS; FIRE EXTINGUISHERS, WHETHER OR NOT CHARGED; SPRAY\nGUNS AND SIMILAR APPLIANCES; STEAM OR SAND BLASTING MACHINES AND SIMILAR JET\nPROJECTING MACHINES- OTHER APPLIANCES: OTHER: PAINTING EQUIPMENT, INCLUDING\nELECTROSTATIC PHOSPHATING AND POWDER COATING EQUIPMENT",
+        "hsn_code": "84248910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MECHANICAL APPLIANCES (WHETHER OR NOT HANDOPERATED) FOR PROJECTING, DISPERSING\nOR SPRAYING LIQUIDS OR POWDERS; FIRE EXTINGUISHERS, WHETHER OR NOT CHARGED; SPRAY\nGUNS AND SIMILAR APPLIANCES; STEAM OR SAND BLASTING MACHINES AND SIMILAR JET\nPROJECTING MACHINES- OTHER APPLIANCES: OTHER: INDUSTRIAL BELLOWS",
+        "hsn_code": "84248920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MECHANICAL APPLIANCES (WHETHER OR NOT HANDOPERATED) FOR PROJECTING, DISPERSING\nOR SPRAYING LIQUIDS OR POWDERS; FIRE EXTINGUISHERS, WHETHER OR NOT CHARGED; SPRAY\nGUNS AND SIMILAR APPLIANCES; STEAM OR SAND BLASTING MACHINES AND SIMILAR JET\nPROJECTING MACHINES- OTHER APPLIANCES: OTHER: OTHER",
+        "hsn_code": "84248990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MECHANICAL APPLIANCES (WHETHER OR NOT HANDOPERATED) FOR PROJECTING, DISPERSING\nOR SPRAYING LIQUIDS OR POWDERS; FIRE EXTINGUISHERS, WHETHER OR NOT CHARGED; SPRAY\nGUNS AND SIMILAR APPLIANCES; STEAM OR SAND BLASTING MACHINES AND SIMILAR JET\nPROJECTING MACHINES- PARTS",
+        "hsn_code": "84249000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MECHANICAL APPLIANCES (WHETHER OR NOT HAND-OPERATED) FOR PROJECTING, DISPERSING OR SPRAYING LIQUIDS OR POWDERS; FIRE EXTINGUISHERS, WHETHER OR NOT CHARGED; SPRAY GUNS AND SIMILAR APPLIANCES; STEAM OR SAND BLASTING MACHINES AND SIMILAR JET PROJECTING MACHINES",
+        "hsn_code": "8424",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEIGHING MACHINERY (EXCLUDING BALANCES OF A SENSITIVITY OF 5 CENTIGRAMS OR\nBETTER), INCLUDING WEIGHT OPERATED COUNTING OR CHECKING MACHINES; WEIGHING\nMACHINE WEIGHTS OF ALL KINDS PERSONAL WEIGHING MACHINES, INCLUDING BABY SCALES;\nHOUSEHOLD SCALES",
+        "hsn_code": "84231000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEIGHING MACHINERY (EXCLUDING BALANCES OF A SENSITIVITY OF 5 CENTIGRAMS OR\nBETTER), INCLUDING WEIGHT OPERATED COUNTING OR CHECKING MACHINES; WEIGHING\nMACHINE WEIGHTS OF ALL KINDS SCALES FOR CONTINUOUS WEIGHING OF GOODS ON\nCONVEYORS",
+        "hsn_code": "84232000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEIGHING MACHINERY (EXCLUDING BALANCES OF A SENSITIVITY OF 5 CENTIGRAMS OR\nBETTER), INCLUDING WEIGHT OPERATED COUNTING OR CHECKING MACHINES; WEIGHING\nMACHINE WEIGHTS OF ALL KINDS CONSTANT WEIGHT SCALES AND SCALES FORDISCHARGING A\nPREDETERMINED WEIGHT OF MATERIAL INTO A BAG OR CONTAINER, INCLUDING HOPPER\nSCALES",
+        "hsn_code": "84233000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEIGHING MACHINERY (EXCLUDING BALANCES OF A SENSITIVITY OF 5 CENTIGRAMS OR\nBETTER), INCLUDING WEIGHT OPERATED COUNTING OR CHECKING MACHINES; WEIGHING\nMACHINE WEIGHTS OF ALL KINDS - OTHER WEIGHING MACHINERY- HAVING A MAXIMUM\nWEIGHING CAPACITY NOT EXCEEDING 30 KG : BEAM SCALE",
+        "hsn_code": "84238110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEIGHING MACHINERY (EXCLUDING BALANCES OF A SENSITIVITY OF 5 CENTIGRAMS OR\nBETTER), INCLUDING WEIGHT OPERATED COUNTING OR CHECKING MACHINES; WEIGHING\nMACHINE WEIGHTS OF ALL KINDS - OTHER WEIGHING MACHINERY- HAVING A MAXIMUM\nWEIGHING CAPACITY NOT EXCEEDING 30 KG : OTHER",
+        "hsn_code": "84238190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEIGHING MACHINERY (EXCLUDING BALANCES OF A SENSITIVITY OF 5 CENTIGRAMS OR\nBETTER), INCLUDING WEIGHT OPERATED COUNTING OR CHECKING MACHINES; WEIGHING\nMACHINE WEIGHTS OF ALL KINDS - OTHER WEIGHING MACHINERY- HAVING A MAXIMUM\nWEIGHING CAPACITY EXCEEDING 30 KG BUT NOT EXCEEDING 5,000 KG: BEAM SCALE",
+        "hsn_code": "84238210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEIGHING MACHINERY (EXCLUDING BALANCES OF A SENSITIVITY OF 5 CENTIGRAMS OR\nBETTER), INCLUDING WEIGHT OPERATED COUNTING OR CHECKING MACHINES; WEIGHING\nMACHINE WEIGHTS OF ALL KINDS - OTHER WEIGHING MACHINERY- HAVING A MAXIMUM\nWEIGHING CAPACITY EXCEEDING 30 KG BUT NOT EXCEEDING 5,000 KG: OTHER",
+        "hsn_code": "84238290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEIGHING MACHINERY (EXCLUDING BALANCES OF A SENSITIVITY OF 5 CENTIGRAMS OR\nBETTER), INCLUDING WEIGHT OPERATED COUNTING OR CHECKING MACHINES; WEIGHING\nMACHINE WEIGHTS OF ALL KINDS - OTHER WEIGHING MACHINERY- OTHER",
+        "hsn_code": "84238900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEIGHING MACHINERY (EXCLUDING BALANCES OF A SENSITIVITY OF 5 CENTIGRAMS OR\nBETTER), INCLUDING WEIGHT OPERATED COUNTING OR CHECKING MACHINES; WEIGHING\nMACHINE WEIGHTS OF ALL KINDS - WEIGHING MACHINE WEIGHTS OF ALL KINDS; PARTS OF\nWEIGHING MACHINERY: WEIGHING MACHINE WEIGHTS OF ALL KINDS",
+        "hsn_code": "84239010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEIGHING MACHINERY (EXCLUDING BALANCES OF A SENSITIVITY OF 5 CENTIGRAMS OR\nBETTER), INCLUDING WEIGHT OPERATED COUNTING OR CHECKING MACHINES; WEIGHING\nMACHINE WEIGHTS OF ALL KINDS - WEIGHING MACHINE WEIGHTS OF ALL KINDS; PARTS OF\nWEIGHING MACHINERY: PARTS OF WEIGHING MACHINERY",
+        "hsn_code": "84239020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEIGHTING MACHINERY (EXCLUDING BALANCES OF A SENSITIVITY OF 5 CENTIGRAMS OR BETTER), INCLUDING WEIGHT OPERATED COUNTING OR CHECKING MACHINES; WEIGHING MACHINE WEIGHTS OF ALL KINDS",
+        "hsn_code": "8423",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DISH WASHING MACHINES; MACHINERY FOR CLEANING OR DRYING BOTTLES OR OTHER\nCONTAINERS; MACHINERY FOR FILLING, CLOSING, SEALING OR LABELLING BOTTLES, CANS,\nBOXES, BAGS OR OTHER CONTAINERS; MACHINERY FOR CAPSULING BOTTLES, JARS, TUBES AND\nSIMILAR CONTAINERS; OTHER PACKING OR WRAPPING MACHINERY (INCLUDING HEAT-SHRINK\nWRAPPING MACHINERY); MACHINERY FOR AERATING BEVERAGES - DISH WASHING MACHINES:\nOF THE HOUSEHOLD TYPE",
+        "hsn_code": "84221100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "DISH WASHING MACHINES; MACHINERY FOR CLEANING OR DRYING BOTTLES OR OTHER\nCONTAINERS; MACHINERY FOR FILLING, CLOSING, SEALING OR LABELLING BOTTLES, CANS,\nBOXES, BAGS OR OTHER CONTAINERS; MACHINERY FOR CAPSULING BOTTLES, JARS, TUBES AND\nSIMILAR CONTAINERS; OTHER PACKING OR WRAPPING MACHINERY (INCLUDING HEAT-SHRINK\nWRAPPING MACHINERY); MACHINERY FOR AERATING BEVERAGES - DISH WASHING MACHINES:\nOTHER",
+        "hsn_code": "84221900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "DISH WASHING MACHINES; MACHINERY FOR CLEANING OR DRYING BOTTLES OR OTHER CONTAINERS; MACHINERY FOR FILLING, CLOSING, SEALING OR LABELLING BOTTLES, CANES, BOXES, BAGS OR OTHER CONTAINERS; MACHINERY FOR CAPSULING BOTTLES JARS, TUBES AND SIMILAR CONTAINERS; OTHER PACKING OR WRAPPING MACHINERY (INCLUDING HEAT-SHRINK WRAPPING MACHINERY); MACHINERY FOR AERATING BEVERAGES",
+        "hsn_code": "8422",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES - CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS: CREAM\nSEPARATORS",
+        "hsn_code": "84211100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES - CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS:\nCLOTHES DRYERS",
+        "hsn_code": "84211200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES - CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS:- OTHER:\nBOWL CENTRIFUGES",
+        "hsn_code": "84211910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES - CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS:- OTHER:\nBASKET CENTRIFUGES",
+        "hsn_code": "84211920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES - CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS:- OTHER:\nCONTINUOUS AUTOMATIC CENTRIFUGES",
+        "hsn_code": "84211930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES - CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS:- OTHER:\nSELF CLEANING CENTRIFUGES",
+        "hsn_code": "84211940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES - CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS:- OTHER:\nDECANTER CENTRIFUGES HORIZONTAL BOWL",
+        "hsn_code": "84211950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES - CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS:- OTHER:\nSCREW CONVEYOR CENTRIFUGES",
+        "hsn_code": "84211960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES - CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS:- OTHER:-\nOTHER : FOR CHEMICAL INDUSTRIES",
+        "hsn_code": "84211991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES - CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS:- OTHER:-\nOTHER : OTHER",
+        "hsn_code": "84211999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES - - FILTERING OR PURIFYING MACHINERY AND APPARATUS\nFOR LIQUIDS - FOR FILTERING OR PURIFYING WATER : ION EXCHANGER PLANT OR APPARATUS",
+        "hsn_code": "84212110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES - - FILTERING OR PURIFYING MACHINERY AND APPARATUS\nFOR LIQUIDS - FOR FILTERING OR PURIFYING WATER :HOUSEHOLD TYPE FILTERS",
+        "hsn_code": "84212120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES - - FILTERING OR PURIFYING MACHINERY AND APPARATUS\nFOR LIQUIDS - FOR FILTERING OR PURIFYING WATER : OTHER",
+        "hsn_code": "84212190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES FILTERING OR PURIFYING MACHINERY AND APPARATUS\nFOR LIQUIDS: FOR FILTERING OR PURIFYING BEVERAGES OTHER THAN WATER",
+        "hsn_code": "84212200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES FILTERING OR PURIFYING MACHINERY AND APPARATUS\nFOR LIQUIDS: OIL OR PETROL-FILTERS FOR INTERNAL COMBUSTION ENGINES",
+        "hsn_code": "84212300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES FILTERING OR PURIFYING MACHINERY AND APPARATUS\nFOR LIQUIDS: OTHER",
+        "hsn_code": "84212900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES FILTERING OR PURIFYING MACHINERY AND APPARATUS\nFOR GASES: INTAKE AIR FILTERS FOR INTERNAL COMBUSTION ENGINES",
+        "hsn_code": "84213100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES FILTERING OR PURIFYING MACHINERY AND APPARATUS\nFOR GASES: OTHER: AIR SEPARATORS TO BE EMPLOYED IN THE PROCESSING, SMELTING OR\nREFINING OF MINERALS, ORES OR METALS; AIR STRIPPERS",
+        "hsn_code": "84213910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES FILTERING OR PURIFYING MACHINERY AND APPARATUS\nFOR GASES: OTHER: AIR PURIFIERS OR CLEANERS",
+        "hsn_code": "84213920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES FILTERING OR PURIFYING MACHINERY AND APPARATUS\nFOR GASES: OTHER: OTHER",
+        "hsn_code": "84213990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES - PARTS: OF CENTRIFUGES, INCLUDING CENTRIFUGAL\nDRYERS",
+        "hsn_code": "84219100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND\nAPPARATUS, FOR LIQUIDS OR GASES - PARTS: OTHER",
+        "hsn_code": "84219900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRIFUGES, INCLUDING CENTRIFUGAL DRYERS; FILTERING OR PURIFYING MACHINERY AND APPARATUS, FOR LIQUIDS OR GASES",
+        "hsn_code": "8421",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CALENDERING OR OTHER ROLLING MACHINES, OTHER THAN FOR METALS OR GLASS, AND\nCYLINDERS THEREFOR CALENDERING OR OTHER ROLLING MACHINES",
+        "hsn_code": "84201000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CALENDERING OR OTHER ROLLING MACHINES, OTHER THAN FOR METALS OR GLASS, AND\nCYLINDERS THEREFOR - PARTS: CYLINDERS",
+        "hsn_code": "84209100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CALENDERING OR OTHER ROLLING MACHINES, OTHER THAN FOR METALS OR GLASS, AND\nCYLINDERS THEREFOR - PARTS: OTHER",
+        "hsn_code": "84209900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CALENDERING OR OTHER ROLLING MACHINES, OTHER THAN FOR METALS OR GLASS, AND CYLINDERS THEREFOR",
+        "hsn_code": "8420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC\nOR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415 8418\n10 - COMBINED REFRIGERATOR-FREEZERS, FITTED WITH SEPARATE EXTERNAL DOORS:\nCOMMERCIAL TYPE",
+        "hsn_code": "84181010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC\nOR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415 8418\n10 - COMBINED REFRIGERATOR-FREEZERS, FITTED WITH SEPARATE EXTERNAL DOORS: OTHER",
+        "hsn_code": "84181090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC\nOR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415 -\nREFRIGERATORS, HOUSEHOLD TYPE: COMPRESSION TYPE",
+        "hsn_code": "84182100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC\nOR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415 -\nREFRIGERATORS, HOUSEHOLD TYPE: ABSORPTION/TYPE, ELECTRICAL",
+        "hsn_code": "84182200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC\nOR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415 -\nREFRIGERATORS, HOUSEHOLD TYPE: OTHER",
+        "hsn_code": "84182900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC\nOR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415 -\nFREEZERS OF THE CHEST TYPE, NOT EXCEEDING 800 L CAPACITY: COMMERCIAL TYPE ELECTRICAL",
+        "hsn_code": "84183010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC\nOR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415 -\nFREEZERS OF THE CHEST TYPE, NOT EXCEEDING 800 L CAPACITY: OTHER",
+        "hsn_code": "84183090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC\nOR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415-\nFREEZERS OF THE UPRIGHT TYPE, NOT EXCEEDING 900 L CAPACITY: ELECTRICAL",
+        "hsn_code": "84184010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC\nOR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415-\nFREEZERS OF THE UPRIGHT TYPE, NOT EXCEEDING 900 L CAPACITY: OTHER",
+        "hsn_code": "84184090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC\nOR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415 -\nOTHER FURNITURE (CHESTS, CABINETS, DISPLAY COUNTERS, SHOWCASES AND THE LIKE) FOR\nSTORAGE AND DISPLAY,INCORPORATING REFRIGERATING OR FREEZING EQUIPMENT? ;",
+        "hsn_code": "84185000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC\nOR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415- OTHER\nREFRIGERATING OR FREEZING EQUIPMENT; HEAT PUMPS:--HEAT PUMPS OTHER THAN AIR\nCONDITIONING MACHINES OF HEADING 8415",
+        "hsn_code": "84186100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC\nOR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415- OTHER\nREFRIGERATING OR FREEZING EQUIPMENT; HEAT PUMPS: OTHER: ICE MAKING MACHINERY",
+        "hsn_code": "84186910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC\nOR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415- OTHER\nREFRIGERATING OR FREEZING EQUIPMENT; HEAT PUMPS: OTHER: WATER COOLER",
+        "hsn_code": "84186920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC\nOR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415- OTHER\nREFRIGERATING OR FREEZING EQUIPMENT; HEAT PUMPS: OTHER: VENDING MACHINE, OTHER\nTHAN AUTOMATIC VENDING MACHINE",
+        "hsn_code": "84186930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC\nOR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415- OTHER\nREFRIGERATING OR FREEZING EQUIPMENT; HEAT PUMPS: OTHER: REFRIGERATION EQUIPMENT\nOR DEVICES SPECIALLY USED IN LEATHER INDUSTRIES FOR MANUFACTURING OF LEATHER\nARTICLES",
+        "hsn_code": "84186940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC\nOR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415- OTHER\nREFRIGERATING OR FREEZING EQUIPMENT; HEAT PUMPS: OTHER: REFRIGERATED FARM TANKS,\nINDUSTRIAL ICE CREAM FREEZER",
+        "hsn_code": "84186950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC\nOR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415- OTHER\nREFRIGERATING OR FREEZING EQUIPMENT; HEAT PUMPS: OTHER: OTHER",
+        "hsn_code": "84186990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC\nOR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415-\nPARTS: FURNITURE DESIGNED TO RECEIVE REFRIGERATING OR FREEZING EQUIPMENT",
+        "hsn_code": "84189100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC\nOR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415-\nPARTS: OTHER",
+        "hsn_code": "84189900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRIGERATORS, FREEZERS AND OTHER REFRIGERATING OR FREEZING EQUIPMENT, ELECTRIC OR OTHER; HEAT PUMPS OTHER THAN AIR CONDITIONING MACHINES OF HEADING 8415",
+        "hsn_code": "8418",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL OR LABORATORY FURNACES AND OVENS, INCLUDING INCINERATORS, NON\nELECTRIC - FURNACES AND OVENS FOR THE ROASTING, MELTING OR OTHER HEAT TREATMENT\nOF ORES, PYRITES OR OF METALS",
+        "hsn_code": "84171000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL OR LABORATORY FURNACES AND OVENS, INCLUDING INCINERATORS, NON\nELECTRIC - BAKERY OVENS, INCLUDING BISCUIT OVENS",
+        "hsn_code": "84172000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL OR LABORATORY FURNACES AND OVENS, INCLUDING INCINERATORS, NON\nELECTRIC - OTHER : FOR CEMENT INDUSTRY",
+        "hsn_code": "84178010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL OR LABORATORY FURNACES AND OVENS, INCLUDING INCINERATORS, NON\nELECTRIC - OTHER : OTHER",
+        "hsn_code": "84178090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL OR LABORATORY FURNACES AND OVENS, INCLUDING INCINERATORS, NON\nELECTRIC - PARTS",
+        "hsn_code": "84179000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL OR LABORATORY FURNACES AND OVENS, INCLUDING INCINERATORS, NON-ELECTRIC",
+        "hsn_code": "8417",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FURNACE BURNERS FOR LIQUID FUEL, FOR PULVERISED SOLID FUEL OR FOR GAS; MECHANICAL\nSTOKERS, INCLUDING THEIR MECHANICAL GRATES, MECHANICAL ASH DISCHARGERS AND\nSIMILAR APPLIANCES - FURNACE BURNERS FOR LIQUID FUEL",
+        "hsn_code": "84161000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FURNACE BURNERS FOR LIQUID FUEL, FOR PULVERISED SOLID FUEL OR FOR GAS; MECHANICAL\nSTOKERS, INCLUDING THEIR MECHANICAL GRATES, MECHANICAL ASH DISCHARGERS AND\nSIMILAR APPLIANCES - OTHER FURNACE BURNERS, INCLUDING COMBINATION BURNERS",
+        "hsn_code": "84162000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FURNACE BURNERS FOR LIQUID FUEL, FOR PULVERISED SOLID FUEL OR FOR GAS; MECHANICAL\nSTOKERS, INCLUDING THEIR MECHANICAL GRATES, MECHANICAL ASH DISCHARGERS AND\nSIMILAR APPLIANCES - MECHANICAL STOKERS, MECHANICAL GRATES, MECHANICAL ASH\nDISCHARGERS AND SIMILAR APPLIANCES",
+        "hsn_code": "84163000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FURNACE BURNERS FOR LIQUID FUEL, FOR PULVERISED SOLID FUEL OR FOR GAS; MECHANICAL\nSTOKERS, INCLUDING THEIR MECHANICAL GRATES, MECHANICAL ASH DISCHARGERS AND\nSIMILAR APPLIANCES - PARTS",
+        "hsn_code": "84169000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FURNACE BURNERS FOR LIQUID FUEL, FOR PULVERISED SOLID FUEL OR FOR GAS; MECHANICAL STOKERS, INCLUDING THEIR MECHANICAL GRATES, MECHANICAL ASH DISCHARGERS AND SIMILAR APPLIANCES",
+        "hsn_code": "8416",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR CONDITIONING MACHINES, COMPRISING A MOTORDRIVEN FAN AND ELEMENTS FOR\nCHANGING THE TEMPERATURE AND HUMIDITY, INCLUDING THOSE MACHINES IN WHICH THE\nHUMIDITY CANNOT BE SEPARATELY REGULATED 8415 10 - WINDOW OR WALL TYPES, SELF\nCONTAINED OR SPLIT SYSTEM: SPLIT SYSTEM",
+        "hsn_code": "84151010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "AIR CONDITIONING MACHINES, COMPRISING A MOTORDRIVEN FAN AND ELEMENTS FOR\nCHANGING THE TEMPERATURE AND HUMIDITY, INCLUDING THOSE MACHINES IN WHICH THE\nHUMIDITY CANNOT BE SEPARATELY REGULATED 8415 10 - WINDOW OR WALL TYPES, SELF\nCONTAINED OR SPLIT SYSTEM: OTHER",
+        "hsn_code": "84151090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "AIR CONDITIONING MACHINES, COMPRISING A MOTORDRIVEN FAN AND ELEMENTS FOR\nCHANGING THE TEMPERATURE AND HUMIDITY, INCLUDING THOSE MACHINES IN WHICH THE\nHUMIDITY CANNOT BE SEPARATELY REGULATED - OF A KIND USED FOR PERSONS IN MOTOR\nVEHICLES : FOR BUSES",
+        "hsn_code": "84152010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "AIR CONDITIONING MACHINES, COMPRISING A MOTORDRIVEN FAN AND ELEMENTS FOR\nCHANGING THE TEMPERATURE AND HUMIDITY, INCLUDING THOSE MACHINES IN WHICH THE\nHUMIDITY CANNOT BE SEPARATELY REGULATED - OF A KIND USED FOR PERSONS IN MOTOR\nVEHICLES : OTHER",
+        "hsn_code": "84152090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "AIR CONDITIONING MACHINES, COMPRISING A MOTORDRIVEN FAN AND ELEMENTS FOR\nCHANGING THE TEMPERATURE AND HUMIDITY, INCLUDING THOSE MACHINES IN WHICH THE\nHUMIDITY CANNOT BE SEPARATELY REGULATED - OTHER : INCORPORATING A REFRIGERATING\nUNIT AND A VALVE FOR REVERSAL OF THE COOLING OR HEAT CYCLE (REVERSIBLE HEAT PUMPS):\nSPLIT AIR CONDITIONER TWO TONNES AND ABOVE",
+        "hsn_code": "84158110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "AIR CONDITIONING MACHINES, COMPRISING A MOTORDRIVEN FAN AND ELEMENTS FOR\nCHANGING THE TEMPERATURE AND HUMIDITY, INCLUDING THOSE MACHINES IN WHICH THE\nHUMIDITY CANNOT BE SEPARATELY REGULATED - OTHER : INCORPORATING A REFRIGERATING\nUNIT AND A VALVE FOR REVERSAL OF THE COOLING OR HEAT CYCLE (REVERSIBLE HEAT PUMPS):\nOTHER",
+        "hsn_code": "84158190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "AIR CONDITIONING MACHINES, COMPRISING A MOTORDRIVEN FAN AND ELEMENTS FOR\nCHANGING THE TEMPERATURE AND HUMIDITY, INCLUDING THOSE MACHINES IN WHICH THE\nHUMIDITY CANNOT BE SEPARATELY REGULATED - OTHER : OTHER, INCORPORATING A\nREFRIGERATING UNIT: SPLIT AIR CONDITIONER TWO TONNES AND ABOVE",
+        "hsn_code": "84158210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "AIR CONDITIONING MACHINES, COMPRISING A MOTORDRIVEN FAN AND ELEMENTS FOR\nCHANGING THE TEMPERATURE AND HUMIDITY, INCLUDING THOSE MACHINES IN WHICH THE\nHUMIDITY CANNOT BE SEPARATELY REGULATED - OTHER : OTHER, INCORPORATING A\nREFRIGERATING UNIT: OTHER",
+        "hsn_code": "84158290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "AIR CONDITIONING MACHINES, COMPRISING A MOTORDRIVEN FAN AND ELEMENTS FOR\nCHANGING THE TEMPERATURE AND HUMIDITY, INCLUDING THOSE MACHINES IN WHICH THE\nHUMIDITY CANNOT BE SEPARATELY REGULATED - OTHER : NOT INCORPORATING A\nREFRIGERATING UNIT : SPLIT AIR CONDITIONER TWO TONNES AND ABOVE",
+        "hsn_code": "84158310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "AIR CONDITIONING MACHINES, COMPRISING A MOTORDRIVEN FAN AND ELEMENTS FOR\nCHANGING THE TEMPERATURE AND HUMIDITY, INCLUDING THOSE MACHINES IN WHICH THE\nHUMIDITY CANNOT BE SEPARATELY REGULATED - OTHER : NOT INCORPORATING A\nREFRIGERATING UNIT : OTHER",
+        "hsn_code": "84158390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "AIR CONDITIONING MACHINES, COMPRISING A MOTORDRIVEN FAN AND ELEMENTS FOR\nCHANGING THE TEMPERATURE AND HUMIDITY, INCLUDING THOSE MACHINES IN WHICH THE\nHUMIDITY CANNOT BE SEPARATELY REGULATED - PARTS",
+        "hsn_code": "84159000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "AIR CONDITIONING MACHINES, COMPRISING A MOTOR DRIVEN FAN AND ELEMENTS FOR CHANGING THE TEMPERATURE AND HUMIDITY, INCLUDING THOSE MACHINES IN WHICH THE HUMIDITY CANNOT BE SEPARATELY REGULATED.",
+        "hsn_code": "8415",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS VACUUM\nPUMPS",
+        "hsn_code": "84141000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - HAND OR\nFOOT-OPERATED AIR PUMPS : OTHER",
+        "hsn_code": "84142090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS\nCOMPRESSORS OF A KIND USED IN REFRIGERATING EQUIPMENT",
+        "hsn_code": "84143000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - AIR\nCOMPRESSORS MOUNTED ON A WHEELED CHASSIS FOR TOWING: RECIPROCATING AIR\nCOMPRESSORS",
+        "hsn_code": "84144010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - AIR\nCOMPRESSORS MOUNTED ON A WHEELED CHASSIS FOR TOWING: CENTRIFUGAL AIR\nCOMPRESSORS",
+        "hsn_code": "84144020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - AIR\nCOMPRESSORS MOUNTED ON A WHEELED CHASSIS FOR TOWING: SCREW AIR COMPRESSORS",
+        "hsn_code": "84144030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - AIR\nCOMPRESSORS MOUNTED ON A WHEELED CHASSIS FOR TOWING: OTHER",
+        "hsn_code": "84144090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS FANS :\n8414 51 - TABLE, FLOOR, WALL, WINDOW, CEILING OR ROOF FANS, WITH A SELF-CONTAINED\nELECTRIC MOTOR OF AN OUTPUT NOT EXCEEDING 125 W: TABLE FANS",
+        "hsn_code": "84145110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - FANS :\n8414 51 - TABLE, FLOOR, WALL, WINDOW, CEILING OR ROOF FANS, WITH A SELF-CONTAINED\nELECTRIC MOTOR OF AN OUTPUT NOT EXCEEDING 125 W: CEILING FANS",
+        "hsn_code": "84145120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - FANS :\n8414 51 - TABLE, FLOOR, WALL, WINDOW, CEILING OR ROOF FANS, WITH A SELF-CONTAINED\nELECTRIC MOTOR OF AN OUTPUT NOT EXCEEDING 125 W: PEDESTAL FANS",
+        "hsn_code": "84145130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - FANS :\n8414 51 - TABLE, FLOOR, WALL, WINDOW, CEILING OR ROOF FANS, WITH A SELF-CONTAINED\nELECTRIC MOTOR OF AN OUTPUT NOT EXCEEDING 125 W: RAILWAY CARRIAGE FANS",
+        "hsn_code": "84145140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - FANS :\n8414 51 - TABLE, FLOOR, WALL, WINDOW, CEILING OR ROOF FANS, WITH A SELF-CONTAINED\nELECTRIC MOTOR OF AN OUTPUT NOT EXCEEDING 125 W: OTHER",
+        "hsn_code": "84145190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - FANS :\nOTHER: AIR CIRCULATOR",
+        "hsn_code": "84145910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - FANS :\nOTHER: BLOWERS, PORTABLE",
+        "hsn_code": "84145920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - FANS :\nOTHER: INDUSTRIAL FANS AND BLOWERS",
+        "hsn_code": "84145930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - FANS :\nOTHER: OTHER",
+        "hsn_code": "84145990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS HOODS\nHAVING A MAXIMUM HORIZONTAL SIDE NOT EXCEEDING 120 CM",
+        "hsn_code": "84146000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - OTHER:\nGAS COMPRESSORS: OF A KIND USED IN AIR CONDITIONING EQUIPMENT",
+        "hsn_code": "84148011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - OTHER:\nGAS COMPRESSORS: OTHER",
+        "hsn_code": "84148019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - OTHER:\nFREE PISTON GENERATORS FOR GAS TURBINE",
+        "hsn_code": "84148020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - OTHER:\nTURBO CHARGER",
+        "hsn_code": "84148030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - OTHER:\nOTHER",
+        "hsn_code": "84148090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - PARTS :\nOF AIR OR VACUUM PUMPS AND COMPRESSORS: OF GAS COMPRESSORS OF A KIND USED IN\nREFRIGERATING AND AIR CONDITIONING APPLIANCES AND MACHINERY",
+        "hsn_code": "84149011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - PARTS :\nOF AIR OR VACUUM PUMPS AND COMPRESSORS: OTHER",
+        "hsn_code": "84149019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - PARTS :\nOF FREE PISTON GENERATORS",
+        "hsn_code": "84149020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - PARTS :\nOF ELECTRIC FANS",
+        "hsn_code": "84149030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - PARTS :\nOF INDUSTRIAL FANS, BLOWERS",
+        "hsn_code": "84149040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR\nRECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS - PARTS :\nOTHER",
+        "hsn_code": "84149090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AIR OR VACUUM PUMPS, AIR OR OTHER GAS COMPRESSORS AND FANS; VENTILATING OR RECYCLING HOODS INCORPORATING A FAN, WHETHER OR NOT FITTED WITH FILTERS",
+        "hsn_code": "8414",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ENGINES AND MOTORS REACTION ENGINES OTHER THAN TURBO JETS",
+        "hsn_code": "84121000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ENGINES AND MOTORS - HYDRAULIC POWER ENGINES AND MOTORS: LINEAR ACTING\n(CYLINDERS)",
+        "hsn_code": "84122100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ENGINES AND MOTORS HYDRAULIC POWER ENGINES AND MOTORS: OTHER : HYDROJET\n(HYDRAULIC JET ENGINES)",
+        "hsn_code": "84122910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ENGINES AND MOTORS HYDRAULIC POWER ENGINES AND MOTORS: OTHER : OTHER",
+        "hsn_code": "84122990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ENGINES AND MOTORS - PNEUMATIC POWER ENGINES AND MOTORS: LINEAR ACTING\n(CYLINDERS)",
+        "hsn_code": "84123100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ENGINES AND MOTORS - PNEUMATIC POWER ENGINES AND MOTORS: OTHER",
+        "hsn_code": "84123900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ENGINES AND MOTORS OTHER: STEAM OR OTHER VAPOUR POWER : STATIONARY",
+        "hsn_code": "84128011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ENGINES AND MOTORS OTHER: STEAM OR OTHER VAPOUR POWER : OTHER",
+        "hsn_code": "84128019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ENGINES AND MOTORS OTHER: MOTORS, SPRING OPERATED EXCLUDING CLOCK AND\nWATCH MOVEMENTS",
+        "hsn_code": "84128020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ENGINES AND MOTORS OTHER: WIND TURBINE OR ENGINE",
+        "hsn_code": "84128030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ENGINES AND MOTORS OTHER: OTHER",
+        "hsn_code": "84128090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ENGINES AND MOTORS PARTS: OF STEAM ENGINES INCORPORATING BOILERS",
+        "hsn_code": "84129010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ENGINES AND MOTORS PARTS: OF OTHER STEAM ENGINES AND OTHER VAPOUR POWER\nUNITS NOT INCORPORATING BOILERS",
+        "hsn_code": "84129020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ENGINES AND MOTORS PARTS: OF HYDRAULIC ENGINES AND MOTORS",
+        "hsn_code": "84129030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ENGINES AND MOTORS PARTS: OTHER",
+        "hsn_code": "84129090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ENGINES AND MOTORS",
+        "hsn_code": "8412",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDRAULIC TURBINES, WATER WHEELS, AND REGULATORS THEREFOR - HYDRAULIC TURBINES\nAND WATER WHEELS : OF A POWER NOT EXCEEDING 1,000 KW",
+        "hsn_code": "84101100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDRAULIC TURBINES, WATER WHEELS, AND REGULATORS THEREFOR - HYDRAULIC TURBINES\nAND WATER WHEELS : OF A POWER EXCEEDING 1,000 KW BUT NOT EXCEEDING 10,000 KW: OF\nPOWER EXCEEDING 1,000 KW BUT NOT EXCEEDING 5,000 KW",
+        "hsn_code": "84101210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDRAULIC TURBINES, WATER WHEELS, AND REGULATORS THEREFOR - HYDRAULIC TURBINES\nAND WATER WHEELS : OF A POWER EXCEEDING 1,000 KW BUT NOT EXCEEDING 10,000 KW: OF\nPOWER EXCEEDING 5,000 KW BUT NOT EXCEEDING 10,000 KW",
+        "hsn_code": "84101220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDRAULIC TURBINES, WATER WHEELS, AND REGULATORS THEREFOR - OF A POWER EXCEEDING\n10,000 KW: OF POWER EXCEEDING 10,000 KW BUT NOT EXCEEDING 30,000 KW",
+        "hsn_code": "84101310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDRAULIC TURBINES, WATER WHEELS, AND REGULATORS THEREFOR - OF A POWER EXCEEDING\n10,000 KW: OF POWER EXCEEDING 30,000 KW BUT NOT EXCEEDING 80,000 KW",
+        "hsn_code": "84101320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDRAULIC TURBINES, WATER WHEELS, AND REGULATORS THEREFOR - OF A POWER EXCEEDING\n10,000 KW: OF POWER EXCEEDING 80,000 KW",
+        "hsn_code": "84101390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDRAULIC TURBINES, WATER WHEELS, AND REGULATORS THEREFOR - PARTS, INCLUDING\nREGULATORS",
+        "hsn_code": "84109000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDRAULIC TURBINES, WATER WHEELS AND REGULATORS THEREFOR HYDRAULIC TURBINS AND WATER WHEELS",
+        "hsn_code": "8410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 FOR AIRCRAFT ENGINES",
+        "hsn_code": "84091000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : 8409 91 - SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH SPARK IGNITION\nINTERNAL COMBUSTION PISTON ENGINES: VALVES, INLET AND EXHAUST, PISTON, PISTON\nRINGS, PISTON ASSEMBLIES : VALVES, INLET AND EXHAUST",
+        "hsn_code": "84099111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : 8409 91 - SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH SPARK IGNITION\nINTERNAL COMBUSTION PISTON ENGINES: VALVES, INLET AND EXHAUST, PISTON, PISTON\nRINGS, PISTON ASSEMBLIES : PISTONS",
+        "hsn_code": "84099112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : 8409 91 - SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH SPARK IGNITION\nINTERNAL COMBUSTION PISTON ENGINES: VALVES, INLET AND EXHAUST, PISTON, PISTON\nRINGS, PISTON ASSEMBLIES : PISTON RINGS",
+        "hsn_code": "84099113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : 8409 91 - SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH SPARK IGNITION\nINTERNAL COMBUSTION PISTON ENGINES: VALVES, INLET AND EXHAUST, PISTON, PISTON\nRINGS, PISTON ASSEMBLIES : PISTON ASSEMBLIES",
+        "hsn_code": "84099114",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : 8409 91 - SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH SPARK IGNITION\nINTERNAL COMBUSTION PISTON ENGINES: FUEL INJECTION EQUIPMENT EXCLUDING INJECTION\nPUMPS",
+        "hsn_code": "84099120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : 8409 91 - SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH SPARK IGNITION\nINTERNAL COMBUSTION PISTON ENGINES: OTHER : OF PETROL ENGINES FOR MOTOR VEHICLES",
+        "hsn_code": "84099191",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : 8409 91 - SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH SPARK IGNITION\nINTERNAL COMBUSTION PISTON ENGINES: OTHER : OF OTHER PETROL ENGINES",
+        "hsn_code": "84099192",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : 8409 91 - SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH SPARK IGNITION\nINTERNAL COMBUSTION PISTON ENGINES: OTHER : OF KEROSENE ENGINES",
+        "hsn_code": "84099193",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : 8409 91 - SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH SPARK IGNITION\nINTERNAL COMBUSTION PISTON ENGINES: OTHER : OF GAS ENGINES",
+        "hsn_code": "84099194",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : 8409 91 - SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH SPARK IGNITION\nINTERNAL COMBUSTION PISTON ENGINES: OTHER : OTHER",
+        "hsn_code": "84099199",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : OTHER : VALVES, INLET AND EXHAUST, PISTON, PISTON RINGS, PISTON\nASSEMBLIES : VALVE, INLET AND EXHAUST",
+        "hsn_code": "84099911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : OTHER : VALVES, INLET AND EXHAUST, PISTON, PISTON RINGS, PISTON\nASSEMBLIES :PISTONS",
+        "hsn_code": "84099912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : OTHER : VALVES, INLET AND EXHAUST, PISTON, PISTON RINGS, PISTON\nASSEMBLIES :PISTON RINGS",
+        "hsn_code": "84099913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : OTHER : VALVES, INLET AND EXHAUST, PISTON, PISTON RINGS, PISTON\nASSEMBLIES : PISTON ASSEMBLIES",
+        "hsn_code": "84099914",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : OTHER : FUEL NOZZLES",
+        "hsn_code": "84099920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : OTHER : FUEL INJECTION EQUIPMENT EXCLUDING INJECTION PUMPS",
+        "hsn_code": "84099930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : OTHER : OTHER PARTS OF DIESEL ENGINE: OF DIESEL ENGINES FOR MOTOR\nVEHICLES",
+        "hsn_code": "84099941",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : OTHER : OTHER PARTS OF DIESEL ENGINE: OF OUTBOARD ENGINE",
+        "hsn_code": "84099942",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : OTHER : OTHER PARTS OF DIESEL ENGINE: OTHER",
+        "hsn_code": "84099949",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR\n8408 - OTHER : OTHER : OTHER",
+        "hsn_code": "84099990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PARTS SUITABLE FOR USE SOLELY OR PRINCIPALLY WITH THE ENGINES OF HEADING 8407 OR 8408",
+        "hsn_code": "8409",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "COMPRESSION-IGNITION INTERNAL COMBUSTION PISTON ENGINES (DIESEL OR SEMI-DIESEL\nENGINES) - MARINE PROPULSION ENGINES:OUTBOARD ENGINES",
+        "hsn_code": "84081010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "COMPRESSION-IGNITION INTERNAL COMBUSTION PISTON ENGINES (DIESEL OR SEMI-DIESEL\nENGINES) - MARINE PROPULSION ENGINES: OTHER OF A CYLINDER CAPACITY NOT EXCEEDING\n100 CC",
+        "hsn_code": "84081091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "COMPRESSION-IGNITION INTERNAL COMBUSTION PISTON ENGINES (DIESEL OR SEMI-DIESEL\nENGINES) - MARINE PROPULSION ENGINES: OTHER OF A CYLINDER CAPACITY EXCEEDING 100 CC\nBUT NOT EXCEEDING 250 CC",
+        "hsn_code": "84081092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "COMPRESSION-IGNITION INTERNAL COMBUSTION PISTON ENGINES (DIESEL OR SEMI-DIESEL\nENGINES) - MARINE PROPULSION ENGINES: OTHER OF A CYLINDER CAPACITY EXCEEDING 250 CC",
+        "hsn_code": "84081093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "COMPRESSION-IGNITION INTERNAL COMBUSTION PISTON ENGINES (DIESEL OR SEMI-DIESEL\nENGINES) - ENGINES OF A KIND USED FOR THE PROPULSION OF VEHICLES OF CHAPTER 87: OF\nCYLINDER CAPACITY NOT EXCEEDING 250 CC",
+        "hsn_code": "84082010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "COMPRESSION-IGNITION INTERNAL COMBUSTION PISTON ENGINES (DIESEL OR SEMI-DIESEL\nENGINES) - OTHER ENGINES: STATIONARY ENGINES OF CYLINDER CAPACITY EXCEEDING 50 CC",
+        "hsn_code": "84089010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "COMPRESSION-IGNITION INTERNAL COMBUSTION PISTON ENGINES (DIESEL OR SEMI-DIESEL\nENGINES) - OTHER ENGINES: OTHER",
+        "hsn_code": "84089090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "COMPRESSION-IGNITION INTERNAL COMBUSTION PISTON ENGINES (DIESEL OR SEMI-DIESEL ENGINES)",
+        "hsn_code": "8408",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SPARK-IGNITION RECIPROCATING OR ROTARY INTERNAL COMBUSTION PISTON ENGINES -\nMARINE PROPULSION ENGINES: OUTBOARD MOTORS",
+        "hsn_code": "84072100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SPARK-IGNITION RECIPROCATING OR ROTARY INTERNAL COMBUSTION PISTON ENGINES -\nMARINE PROPULSION ENGINES: OTHER",
+        "hsn_code": "84072900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SPARK-IGNITION RECIPROCATING OR ROTARY INTERNAL COMBUSTION PISTON ENGINES -\nRECIPROCATING PISTON ENGINES OF A KIND USED FOR THE PROPULSION OF VEHICLES OF\nCHAPTER 87: OF A CYLINDER CAPACITY NOT EXCEEDING 50 CC: FOR MOTOR CYCLES",
+        "hsn_code": "84073110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SPARK-IGNITION RECIPROCATING OR ROTARY INTERNAL COMBUSTION PISTON ENGINES -\nRECIPROCATING PISTON ENGINES OF A KIND USED FOR THE PROPULSION OF VEHICLES OF\nCHAPTER 87: OF A CYLINDER CAPACITY NOT EXCEEDING 50 CC: OTHER",
+        "hsn_code": "84073190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SPARK-IGNITION RECIPROCATING OR ROTARY INTERNAL COMBUSTION PISTON ENGINES -\nRECIPROCATING PISTON ENGINES OF A KIND USED FOR THE PROPULSION OF VEHICLES OF\nCHAPTER 87: OF A CYLINDER CAPACITY EXCEEDING 50 CC BUT NOT EXCEEDING 250 CC: FOR\nMOTOR CYCLES",
+        "hsn_code": "84073210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SPARK-IGNITION RECIPROCATING OR ROTARY INTERNAL COMBUSTION PISTON ENGINES -\nRECIPROCATING PISTON ENGINES OF A KIND USED FOR THE PROPULSION OF VEHICLES OF\nCHAPTER 87: OF A CYLINDER CAPACITY EXCEEDING 50 CC BUT NOT EXCEEDING 250 CC: OTHER",
+        "hsn_code": "84073290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SPARK-IGNITION RECIPROCATING OR ROTARY INTERNAL COMBUSTION PISTON ENGINES -\nRECIPROCATING PISTON ENGINES OF A KIND USED FOR THE PROPULSION OF VEHICLES OF\nCHAPTER 87: OF A CYLINDER CAPACITY EXCEEDING 250 CC BUT NOT EXCEEDING 1,000 CC : FOR\nMOTOR CARS",
+        "hsn_code": "84073310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SPARK-IGNITION RECIPROCATING OR ROTARY INTERNAL COMBUSTION PISTON ENGINES -\nRECIPROCATING PISTON ENGINES OF A KIND USED FOR THE PROPULSION OF VEHICLES OF\nCHAPTER 87: OF A CYLINDER CAPACITY EXCEEDING 250 CC BUT NOT EXCEEDING 1,000 CC : FOR\nMOTOR CYCLES",
+        "hsn_code": "84073320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SPARK-IGNITION RECIPROCATING OR ROTARY INTERNAL COMBUSTION PISTON ENGINES -\nRECIPROCATING PISTON ENGINES OF A KIND USED FOR THE PROPULSION OF VEHICLES OF\nCHAPTER 87: OF A CYLINDER CAPACITY EXCEEDING 250 CC BUT NOT EXCEEDING 1,000 CC :\nOTHER",
+        "hsn_code": "84073390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SPARK-IGNITION RECIPROCATING OR ROTARY INTERNAL COMBUSTION PISTON ENGINES -\nRECIPROCATING PISTON ENGINES OF A KIND USED FOR THE PROPULSION OF VEHICLES OF\nCHAPTER 87: OF A CYLINDER CAPACITY EXCEEDING 1,000 CC:FOR MOTOR CARS",
+        "hsn_code": "84073410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SPARK-IGNITION RECIPROCATING OR ROTARY INTERNAL COMBUSTION PISTON ENGINES -\nRECIPROCATING PISTON ENGINES OF A KIND USED FOR THE PROPULSION OF VEHICLES OF\nCHAPTER 87: OF A CYLINDER CAPACITY EXCEEDING 1,000 CC:OTHER",
+        "hsn_code": "84073490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SPARK-IGNITION RECIPROCATING OR ROTARY INTERNAL COMBUSTION PISTON ENGINES - OTHER\nENGINES: PETROL ENGINES",
+        "hsn_code": "84079010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SPARK-IGNITION RECIPROCATING OR ROTARY INTERNAL COMBUSTION PISTON ENGINES - OTHER\nENGINES: KEROSENE ENGINES",
+        "hsn_code": "84079020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SPARK-IGNITION RECIPROCATING OR ROTARY INTERNAL COMBUSTION PISTON ENGINES - OTHER\nENGINES: OTHER",
+        "hsn_code": "84079090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SPARK-IGNITION RECIPROCATING OR ROTARY INTERNAL COMBUSTION PISTON ENGINES",
+        "hsn_code": "8407",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "STEAM TURBINES AND OTHER VAPOUR TURBINES TURBINES FOR MARINE PROPULSION",
+        "hsn_code": "84061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STEAM TURBINES AND OTHER VAPOUR TURBINES - OTHER TURBINES: OF AN OUTPUT\nEXCEEDING 40 MW",
+        "hsn_code": "84068100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STEAM TURBINES AND OTHER VAPOUR TURBINES - OTHER TURBINES: OF AN OUTPUT NOT\nEXCEEDING 40 MW",
+        "hsn_code": "84068200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STEAM TURBINES AND OTHER VAPOUR TURBINES - PARTS",
+        "hsn_code": "84069000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STEAM TURBINES AND OTHER VAPOUR TURBINES",
+        "hsn_code": "8406",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRODUCER GAS OR WATER GAS GENERATORS, WITH OR WITHOUT THEIR PURIFIERS; ACETYLENE\nGAS GENERATORS AND SIMILAR WATER PROCESS GAS GENERATORS, WITH OR WITHOUT THEIR\nPURIFIERS - PRODUCER GAS OR WATER GAS GENERATORS, WITH OR WITHOUT THEIR PURIFIERS;\nACETYLENE GAS GENERATORS AND SIMILAR WATER PROCESS GAS GENERATORS, WITH OR\nWITHOUT THEIR PURIFIERS: PRODUCER GAS OR WATER GAS GENERATORS",
+        "hsn_code": "84051010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRODUCER GAS OR WATER GAS GENERATORS, WITH OR WITHOUT THEIR PURIFIERS; ACETYLENE\nGAS GENERATORS AND SIMILAR WATER PROCESS GAS GENERATORS, WITH OR WITHOUT THEIR\nPURIFIERS - PRODUCER GAS OR WATER GAS GENERATORS, WITH OR WITHOUT THEIR PURIFIERS;\nACETYLENE GAS GENERATORS AND SIMILAR WATER PROCESS GAS GENERATORS, WITH OR\nWITHOUT THEIR PURIFIERS:ACETYLENE GAS GENERATORS",
+        "hsn_code": "84051020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRODUCER GAS OR WATER GAS GENERATORS, WITH OR WITHOUT THEIR PURIFIERS; ACETYLENE\nGAS GENERATORS AND SIMILAR WATER PROCESS GAS GENERATORS, WITH OR WITHOUT THEIR\nPURIFIERS - PRODUCER GAS OR WATER GAS GENERATORS, WITH OR WITHOUT THEIR PURIFIERS;\nACETYLENE GAS GENERATORS AND SIMILAR WATER PROCESS GAS GENERATORS, WITH OR\nWITHOUT THEIR PURIFIERS: OTHER",
+        "hsn_code": "84051090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRODUCER GAS OR WATER GAS GENERATORS, WITH OR WITHOUT THEIR PURIFIERS; ACETYLENE\nGAS GENERATORS AND SIMILAR WATER PROCESS GAS GENERATORS, WITH OR WITHOUT THEIR\nPURIFIERS - PARTS",
+        "hsn_code": "84059000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRODUCER GAS OR WATER GAS GENERATORS, WITH OR WITHOUT THEIR PURIFIERS; ACETYLENE GAS GENERATORS AND SIMILAR WATER PROCESS GAS GENERATORS, WITH OR WITHOUT THEIR PURIFIERS",
+        "hsn_code": "8405",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY PLANT FOR USE WITH BOILERS OF HEADING 8402 OR 8403 (FOR EXAMPLE,\nECONOMISERS, SUPERHEATERS, SOOT REMOVERS, GAS RECOVERERS); CONDENSERS FOR STEAM\nOR OTHER VAPOUR POWER UNITS AUXILIARY PLANTS FOR USE WITH BOILERS OF HEADING 8402\nOR 8403",
+        "hsn_code": "84041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY PLANT FOR USE WITH BOILERS OF HEADING 8402 OR 8403 (FOR EXAMPLE,\nECONOMISERS, SUPERHEATERS, SOOT REMOVERS, GAS RECOVERERS); CONDENSERS FOR STEAM\nOR OTHER VAPOUR POWER UNITS CONDENSERS FOR STEAM OR OTHER VAPOUR POWER UNITS",
+        "hsn_code": "84042000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY PLANT FOR USE WITH BOILERS OF HEADING 8402 OR 8403 (FOR EXAMPLE,\nECONOMISERS, SUPERHEATERS, SOOT REMOVERS, GAS RECOVERERS); CONDENSERS FOR STEAM\nOR OTHER VAPOUR POWER UNITS PARTS",
+        "hsn_code": "84049000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "AUXILIARY PLANT FOR USE WITH BOILERS OF HEADING 8402 OR 8403 (FOR EXAMPLE, ECONOMISERS, SUPER-HEATERS, SOOT REMOVERS, GAS RECOVERS); CONDENSERS FOR STEAM OR OTHER VAPOUR POWER UNITS",
+        "hsn_code": "8404",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRAL HEATING BOILERS OTHER THAN THOSE OF HEADING 8402 - BOILERS",
+        "hsn_code": "84031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRAL HEATING BOILERS OTHER THAN THOSE OF HEADING 8402 - PARTS",
+        "hsn_code": "84039000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CENTRAL HEATING BOILERS OTHER THAN THOSE OF HEADING 8402",
+        "hsn_code": "8403",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STEAM OR OTHER VAPOUR GENERATING BOILERS (OTHER THAN CENTRAL HEATING HOT WATER\nBOILERS CAPABLE ALSO OF PRODUCING LOW PRESSURE STEAM); SUPERHEATED WATER BOILERS\n- STEAM OR OTHER VAPOUR GENERATING BOILERS: WATERTUBE BOILERS WITH A STEAM\nPRODUCTION EXCEEDING 45 T PER HOUR",
+        "hsn_code": "84021100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STEAM OR OTHER VAPOUR GENERATING BOILERS (OTHER THAN CENTRAL HEATING HOT WATER\nBOILERS CAPABLE ALSO OF PRODUCING LOW PRESSURE STEAM); SUPERHEATED WATER BOILERS\n- STEAM OR OTHER VAPOUR GENERATING BOILERS: WATER TUBE BOILERS WITH A STEAM\nPRODUCTION NOT EXCEEDING 45 T PER HOUR",
+        "hsn_code": "84021200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STEAM OR OTHER VAPOUR GENERATING BOILERS (OTHER THAN CENTRAL HEATING HOT WATER\nBOILERS CAPABLE ALSO OF PRODUCING LOW PRESSURE STEAM); SUPERHEATED WATER BOILERS\n- STEAM OR OTHER VAPOUR GENERATING BOILERS: OTHER VAPOUR GENERATING BOILERS,\nINCLUDING HYBRID BOILERS: FIRE TUBE HORIZONTAL (LANCASHIRE) BOILERS",
+        "hsn_code": "84021910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STEAM OR OTHER VAPOUR GENERATING BOILERS (OTHER THAN CENTRAL HEATING HOT WATER\nBOILERS CAPABLE ALSO OF PRODUCING LOW PRESSURE STEAM); SUPERHEATED WATER BOILERS\n- STEAM OR OTHER VAPOUR GENERATING BOILERS: OTHER VAPOUR GENERATING BOILERS,\nINCLUDING HYBRID BOILERS:FIRE TUBE BOILERS VERTICAL",
+        "hsn_code": "84021920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STEAM OR OTHER VAPOUR GENERATING BOILERS (OTHER THAN CENTRAL HEATING HOT WATER\nBOILERS CAPABLE ALSO OF PRODUCING LOW PRESSURE STEAM); SUPERHEATED WATER BOILERS\n- STEAM OR OTHER VAPOUR GENERATING BOILERS: OTHER VAPOUR GENERATING BOILERS,\nINCLUDING HYBRID BOILERS:OTHER",
+        "hsn_code": "84021990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STEAM OR OTHER VAPOUR GENERATING BOILERS (OTHER THAN CENTRAL HEATING HOT WATER\nBOILERS CAPABLE ALSO OF PRODUCING LOW PRESSURE STEAM); SUPERHEATED WATER BOILERS\n- SUPER-HEATED WATER BOILERS",
+        "hsn_code": "84022000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STEAM OR OTHER VAPOUR GENERATING BOILERS (OTHER THAN CENTRAL HEATING HOT WATER\nBOILERS CAPABLE ALSO OF PRODUCING LOW PRESSURE STEAM); SUPERHEATED WATER BOILERS\n- PARTS: PARTS OF FIRE TUBE BOILERS",
+        "hsn_code": "84029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STEAM OR OTHER VAPOUR GENERATING BOILERS (OTHER THAN CENTRAL HEATING HOT WATER\nBOILERS CAPABLE ALSO OF PRODUCING LOW PRESSURE STEAM); SUPERHEATED WATER BOILERS\n- PARTS: PARTS OF WATERTUBE BOILERS",
+        "hsn_code": "84029020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STEAM OR OTHER VAPOUR GENERATING BOILERS (OTHER THAN CENTRAL HEATING HOT WATER\nBOILERS CAPABLE ALSO OF PRODUCING LOW PRESSURE STEAM); SUPERHEATED WATER BOILERS\n- PARTS: OTHER",
+        "hsn_code": "84029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STEAM OR OTHER VAPOUR GENERATING BOILERS (OTHER THAN CENTRAL HEATING HOT WATER BOILERS CAPABLE ALSO OF PRODUCING LOW PRESSURE STEAM): SUPER-HEATED WATER BOILERS",
+        "hsn_code": "8402",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NUCLEAR REACTORS; FUEL ELEMENTS (CARTRIDGES), NON-IRRADIATED, FOR NUCLEAR\nREACTORS; MACHINERY AND APPARATUS FOR ISOTOPIC SEPARATION NUCLEAR REACTORS",
+        "hsn_code": "84011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NUCLEAR REACTORS; FUEL ELEMENTS (CARTRIDGES), NON-IRRADIATED, FOR NUCLEAR\nREACTORS; MACHINERY AND APPARATUS FOR ISOTOPIC SEPARATION MACHINERY AND\nAPPARATUS FOR ISOTOPIC SEPARATION, AND PARTS THEREOF",
+        "hsn_code": "84012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NUCLEAR REACTORS; FUEL ELEMENTS (CARTRIDGES), NON-IRRADIATED, FOR NUCLEAR\nREACTORS; MACHINERY AND APPARATUS FOR ISOTOPIC SEPARATION FUEL ELEMENTS\n(CARTRIDGES), NON-IRRADIATED",
+        "hsn_code": "84013000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NUCLEAR REACTORS; FUEL ELEMENTS (CARTRIDGES), NON-IRRADIATED, FOR NUCLEAR\nREACTORS; MACHINERY AND APPARATUS FOR ISOTOPIC SEPARATION PARTS OF NUCLEAR\nREACTORS",
+        "hsn_code": "84014000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NUCLEAR REACTORS; FUEL ELEMENTS (CARTRIDGES), NON-IRRADIATED, FOR NUCLEAR REACTORS, MACHINERY AND APPARATUS FOR ISOTOPIC SEPARATION",
+        "hsn_code": "8401",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE, RODS, TUBES, PLATES, ELECTRODES AND SIMILAR PRODUCTS, OF BASE METAL OR OF METAL CARBIDES, COATED OR CORED WITH FLUX MATERIAL, OF A KIND USED FOR SOLDERING, BRAZING, WELDING OR DEPOSITION OF METAL OR OF METAL CARBIDES; WIRE AND RODS, OF AGGLOMERATED BASE METAL POWDER, USED FOR METAL SPRAYING",
+        "hsn_code": "8311",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE, RODS, TUBES, PLATES, ELECTRODES AND SIMILAR PRODUCTS, OF BASE METAL OR OF\nMETAL CARBIDES, COATED OR CORED WITH FLUX MATERIAL, OF A KIND USED FOR SOLDERING,\nBRAZING, WELDING OR DEPOSITION OF METAL OR OF METAL CARBIDES; WIRE AND RODS, OF\nAGGLOMERATED BASE METAL POWDER, USED FOR METAL SPRAYING OTHER",
+        "hsn_code": "83119000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE, RODS, TUBES, PLATES, ELECTRODES AND SIMILAR PRODUCTS, OF BASE METAL OR OF\nMETAL CARBIDES, COATED OR CORED WITH FLUX MATERIAL, OF A KIND USED FOR SOLDERING,\nBRAZING, WELDING OR DEPOSITION OF METAL OR OF METAL CARBIDES; WIRE AND RODS, OF\nAGGLOMERATED BASE METAL POWDER, USED FOR METAL SPRAYING - COATED RODS AND\nCORED WIRE, OF BASE METAL, FOR SOLDERING, BRAZING OR WELDING BY FLAME:OTHER",
+        "hsn_code": "83113090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE, RODS, TUBES, PLATES, ELECTRODES AND SIMILAR PRODUCTS, OF BASE METAL OR OF\nMETAL CARBIDES, COATED OR CORED WITH FLUX MATERIAL, OF A KIND USED FOR SOLDERING,\nBRAZING, WELDING OR DEPOSITION OF METAL OR OF METAL CARBIDES; WIRE AND RODS, OF\nAGGLOMERATED BASE METAL POWDER, USED FOR METAL SPRAYING - COATED RODS AND\nCORED WIRE, OF BASE METAL, FOR SOLDERING, BRAZING OR WELDING BY FLAME:WIRE AND\nRODS OF AGGLOMERATED BASE METAL",
+        "hsn_code": "83113010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE, RODS, TUBES, PLATES, ELECTRODES AND SIMILAR PRODUCTS, OF BASE METAL OR OF\nMETAL CARBIDES, COATED OR CORED WITH FLUX MATERIAL, OF A KIND USED FOR SOLDERING,\nBRAZING, WELDING OR DEPOSITION OF METAL OR OF METAL CARBIDES; WIRE AND RODS, OF\nAGGLOMERATED BASE METAL POWDER, USED FOR METAL SPRAYING - CORED WIRE OF BASE\nMETAL, FOR ELECTRIC ARC - WELDING",
+        "hsn_code": "83112000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE, RODS, TUBES, PLATES, ELECTRODES AND SIMILAR PRODUCTS, OF BASE METAL OR OF\nMETAL CARBIDES, COATED OR CORED WITH FLUX MATERIAL, OF A KIND USED FOR SOLDERING,\nBRAZING, WELDING OR DEPOSITION OF METAL OR OF METAL CARBIDES; WIRE AND RODS, OF\nAGGLOMERATED BASE METAL POWDER, USED FOR METAL SPRAYING - COATED ELECTRODES OF\nBASE METAL, FOR ELECTRIC ARC - WELDING",
+        "hsn_code": "83111000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SIGN-PLATES, NAME-PLATES, ADDRESS-PLATES AND SIMILAR PLATES, NUMBERS, LETTERS AND OTHER SYMBOLS, OF BASE METAL, EXCLUDING THOSE OF HEADING 9405",
+        "hsn_code": "8310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SIGN - PLATES, NAME - PLATES, ADDRESS - PLATES AND SIMILAR PLATES, NUMBERS, LETTERS\nAND OTHER SYMBOLS, OF BASE METAL, EXCLUDING THOSE OF HEADING 9405 - SIGN - PLATES,\nNAME - PLATES, ADDRESS - PLATES AND SIMILAR PLATES, NUMBERS, LETTERS AND OTHER\nSYMBOLS, OF BASE METAL, EXCLUDING THOSE OF HEADING 9405:OTHER",
+        "hsn_code": "83100090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SIGN - PLATES, NAME - PLATES, ADDRESS - PLATES AND SIMILAR PLATES, NUMBERS, LETTERS\nAND OTHER SYMBOLS, OF BASE METAL, EXCLUDING THOSE OF HEADING 9405 - SIGN - PLATES,\nNAME - PLATES, ADDRESS - PLATES AND SIMILAR PLATES, NUMBERS, LETTERS AND OTHER\nSYMBOLS, OF BASE METAL, EXCLUDING THOSE OF HEADING 9405:ENAMEL IRON SIGNBOARD",
+        "hsn_code": "83100010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOPPERS, CAPS AND LIDS (INCLUDING CROWN CORKS, SCREW CAPS AND POURING STOPPERS), CAPSULES FOR BOTTLES, THREADED BUNGS, BUNG COVERS, SEALS AND OTHER PACKING ACCESSORIES, OF BASE METAL",
+        "hsn_code": "8309",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOPPERS, CAPS AND LIDS (INCLUDING CROWN CORKS, SCREW CAPS AND POURING STOPPERS),\nCAPSULES FOR BOTTLES, THREADED BUNGS, BUNG COVERS, SEALS AND OTHER PACKING\nACCESSORIES, OF BASE METAL - OTHER:OTHER",
+        "hsn_code": "83099090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOPPERS, CAPS AND LIDS (INCLUDING CROWN CORKS, SCREW CAPS AND POURING STOPPERS),\nCAPSULES FOR BOTTLES, THREADED BUNGS, BUNG COVERS, SEALS AND OTHER PACKING\nACCESSORIES, OF BASE METAL - OTHER:OTHER SEALS",
+        "hsn_code": "83099030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOPPERS, CAPS AND LIDS (INCLUDING CROWN CORKS, SCREW CAPS AND POURING STOPPERS),\nCAPSULES FOR BOTTLES, THREADED BUNGS, BUNG COVERS, SEALS AND OTHER PACKING\nACCESSORIES, OF BASE METAL - OTHER:ALUMINIUM CAPS, SEALS, CAPSULES AND CLOSERS",
+        "hsn_code": "83099020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOPPERS, CAPS AND LIDS (INCLUDING CROWN CORKS, SCREW CAPS AND POURING STOPPERS),\nCAPSULES FOR BOTTLES, THREADED BUNGS, BUNG COVERS, SEALS AND OTHER PACKING\nACCESSORIES, OF BASE METAL - OTHER : PILFER PROOF CAPS FOR PACKAGING, ALL SORTS, WITH\nOR WITHOUT WASHERS OR OTHER FITTINGS, OF CORK, RUBBER, POLYETHYLENE OR ANY OTHER\nMATERIAL",
+        "hsn_code": "83099010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOPPERS, CAPS AND LIDS (INCLUDING CROWN CORKS, SCREW CAPS AND POURING STOPPERS),\nCAPSULES FOR BOTTLES, THREADED BUNGS, BUNG COVERS, SEALS AND OTHER PACKING\nACCESSORIES, OF BASE METAL CROWN CORKS",
+        "hsn_code": "83091000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLASPS, FRAMES WITH CLASPS, BUCKLES, BUCKLE-CLASPS, HOOKS, EYES, EYELETS AND THE LIKE, OF BASE METAL, OF A KIND USED FOR CLOTHING, FOOTWEAR, AWNINGS, HANDBAGS, TRAVEL GOODS OR OTHER MADE UP ARTICLES; TUBULAR OR BIFURCATED RIVETS, OF BASE METAL; BEADS AND SPANGLES, OF BASE METALS",
+        "hsn_code": "8308",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLASPS, FRAMES WITH CLASPS, BUCKLES, BUCKLE-CLASPS, HOOKS, EYES, EYELETS AND THE LIKE,\nOF BASE METAL, OF A KIND USED FOR CLOTHING, FOOTWEAR, AWNINGS, HANDBAGS, TRAVEL\nGOODS OR OTHER MADE UP ARTICLES; TUBULAR OR BIFURCATED RIVETS, OF BASE METAL;\nBEADS AND SPANGLES, OF BASE METALS - OTHER, INCLUDING PARTS: - OTHER: OTHER",
+        "hsn_code": "83089099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLASPS, FRAMES WITH CLASPS, BUCKLES, BUCKLE-CLASPS, HOOKS, EYES, EYELETS AND THE LIKE,\nOF BASE METAL, OF A KIND USED FOR CLOTHING, FOOTWEAR, AWNINGS, HANDBAGS, TRAVEL\nGOODS OR OTHER MADE UP ARTICLES; TUBULAR OR BIFURCATED RIVETS, OF BASE METAL;\nBEADS AND SPANGLES, OF BASE METALS - OTHER, INCLUDING PARTS: - OTHER: FOR GARMENTS,\nMADE UPS, KNITWEAR, PLASTIC AND LEATHER GOODS",
+        "hsn_code": "83089091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLASPS, FRAMES WITH CLASPS, BUCKLES, BUCKLE-CLASPS, HOOKS, EYES, EYELETS AND THE LIKE,\nOF BASE METAL, OF A KIND USED FOR CLOTHING, FOOTWEAR, AWNINGS, HANDBAGS, TRAVEL\nGOODS OR OTHER MADE UP ARTICLES; TUBULAR OR BIFURCATED RIVETS, OF BASE METAL;\nBEADS AND SPANGLES, OF BASE METALS - OTHER, INCLUDING PARTS: FITTINGS FOR TRAVEL\nREQUISITES AND LEATHER GOODS",
+        "hsn_code": "83089040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLASPS, FRAMES WITH CLASPS, BUCKLES, BUCKLE-CLASPS, HOOKS, EYES, EYELETS AND THE LIKE,\nOF BASE METAL, OF A KIND USED FOR CLOTHING, FOOTWEAR, AWNINGS, HANDBAGS, TRAVEL\nGOODS OR OTHER MADE UP ARTICLES; TUBULAR OR BIFURCATED RIVETS, OF BASE METAL;\nBEADS AND SPANGLES, OF BASE METALS - OTHER, INCLUDING PARTS: - BEADS AND SPANGLES\nOF BASE METAL: OTHER",
+        "hsn_code": "83089039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLASPS, FRAMES WITH CLASPS, BUCKLES, BUCKLE-CLASPS, HOOKS, EYES, EYELETS AND THE LIKE,\nOF BASE METAL, OF A KIND USED FOR CLOTHING, FOOTWEAR, AWNINGS, HANDBAGS, TRAVEL\nGOODS OR OTHER MADE UP ARTICLES; TUBULAR OR BIFURCATED RIVETS, OF BASE METAL;\nBEADS AND SPANGLES, OF BASE METALS - OTHER, INCLUDING PARTS: - BEADS AND SPANGLES\nOF BASE METAL: FOR GARMENTS, MADE UPS, KNITWEAR, PLASTIC AND LEATHER GOODS",
+        "hsn_code": "83089031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLASPS, FRAMES WITH CLASPS, BUCKLES, BUCKLE-CLASPS, HOOKS, EYES, EYELETS AND THE LIKE,\nOF BASE METAL, OF A KIND USED FOR CLOTHING, FOOTWEAR, AWNINGS, HANDBAGS, TRAVEL\nGOODS OR OTHER MADE UP ARTICLES; TUBULAR OR BIFURCATED RIVETS, OF BASE METAL;\nBEADS AND SPANGLES, OF BASE METALS - OTHER, INCLUDING PARTS: IMITATION ZARI\nSPANGLES",
+        "hsn_code": "83089020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLASPS, FRAMES WITH CLASPS, BUCKLES, BUCKLE-CLASPS, HOOKS, EYES, EYELETS AND THE LIKE,\nOF BASE METAL, OF A KIND USED FOR CLOTHING, FOOTWEAR, AWNINGS, HANDBAGS, TRAVEL\nGOODS OR OTHER MADE UP ARTICLES; TUBULAR OR BIFURCATED RIVETS, OF BASE METAL;\nBEADS AND SPANGLES, OF BASE METALS - OTHER, INCLUDING PARTS: - BUCKLES:OTHER",
+        "hsn_code": "83089019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLASPS, FRAMES WITH CLASPS, BUCKLES, BUCKLE-CLASPS, HOOKS, EYES, EYELETS AND THE LIKE,\nOF BASE METAL, OF A KIND USED FOR CLOTHING, FOOTWEAR, AWNINGS, HANDBAGS, TRAVEL\nGOODS OR OTHER MADE UP ARTICLES; TUBULAR OR BIFURCATED RIVETS, OF BASE METAL;\nBEADS AND SPANGLES, OF BASE METALS - OTHER, INCLUDING PARTS: - BUCKLES:FOR\nFOOTWEAR",
+        "hsn_code": "83089011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLASPS, FRAMES WITH CLASPS, BUCKLES, BUCKLE-CLASPS, HOOKS, EYES, EYELETS AND THE LIKE,\nOF BASE METAL, OF A KIND USED FOR CLOTHING, FOOTWEAR, AWNINGS, HANDBAGS, TRAVEL\nGOODS OR OTHER MADE UP ARTICLES; TUBULAR OR BIFURCATED RIVETS, OF BASE METAL;\nBEADS AND SPANGLES, OF BASE METALS - TUBULAR OR BIFURCATED RIVETS",
+        "hsn_code": "83082000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLASPS, FRAMES WITH CLASPS, BUCKLES, BUCKLE-CLASPS, HOOKS, EYES, EYELETS AND THE LIKE,\nOF BASE METAL, OF A KIND USED FOR CLOTHING, FOOTWEAR, AWNINGS, HANDBAGS, TRAVEL\nGOODS OR OTHER MADE UP ARTICLES; TUBULAR OR BIFURCATED RIVETS, OF BASE METAL;\nBEADS AND SPANGLES, OF BASE METALS - HOOKS, EYES AND EYELETS: - EYELETS: OTHER",
+        "hsn_code": "83081029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLASPS, FRAMES WITH CLASPS, BUCKLES, BUCKLE-CLASPS, HOOKS, EYES, EYELETS AND THE LIKE,\nOF BASE METAL, OF A KIND USED FOR CLOTHING, FOOTWEAR, AWNINGS, HANDBAGS, TRAVEL\nGOODS OR OTHER MADE UP ARTICLES; TUBULAR OR BIFURCATED RIVETS, OF BASE METAL;\nBEADS AND SPANGLES, OF BASE METALS - HOOKS, EYES AND EYELETS: - EYELETS: FOR\nFOOTWEAR",
+        "hsn_code": "83081021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLASPS, FRAMES WITH CLASPS, BUCKLES, BUCKLE-CLASPS, HOOKS, EYES, EYELETS AND THE LIKE,\nOF BASE METAL, OF A KIND USED FOR CLOTHING, FOOTWEAR, AWNINGS, HANDBAGS, TRAVEL\nGOODS OR OTHER MADE UP ARTICLES; TUBULAR OR BIFURCATED RIVETS, OF BASE METAL;\nBEADS AND SPANGLES, OF BASE METALS - HOOKS, EYES AND EYELETS:HOOKS AND EYES",
+        "hsn_code": "83081010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLEXIBLE TUBING OF BASE METAL, WITH OR WITHOUT FITTINGS",
+        "hsn_code": "8307",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLEXIBLE TUBING OF BASE METAL, WITH OR WITHOUT FITTINGS OF OTHER BASE METAL",
+        "hsn_code": "83079000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLEXIBLE TUBING OF BASE METAL, WITH OR WITHOUT FITTINGS OF IRON OR STEEL",
+        "hsn_code": "83071000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BELLS, GONGS AND THE LIKE, NON-ELECTRIC, OF BASE METAL; STATUETTES AND OTHER\nORNAMENTS, OF BASE METAL; PHOTOGRAPH, PICTURE OR SIMILAR FRAMES, OF BASE METAL;\nMIRRORS OF BASE METAL - BELLS, GONGS AND THE LIKE",
+        "hsn_code": "83061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BELLS, GONGS AND THE LIKE, NON-ELECTRIC, OF BASE METAL; STATUETTES AND OTHER\nORNAMENTS, OF BASE METAL; PHOTOGRAPH, PICTURE OR SIMILAR FRAMES, OF BASE METAL;\nMIRRORS OF BASE METAL - STATUETTES AND OTHER ORNAMENTS: - PLATED WITH PRECIOUS\nMETAL:STATUETTES",
+        "hsn_code": "83062110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BELLS, GONGS AND THE LIKE, NON-ELECTRIC, OF BASE METAL; STATUETTES AND OTHER\nORNAMENTS, OF BASE METAL; PHOTOGRAPH, PICTURE OR SIMILAR FRAMES, OF BASE METAL;\nMIRRORS OF BASE METAL - STATUETTES AND OTHER ORNAMENTS: - PLATED WITH PRECIOUS\nMETAL:TROPHIES",
+        "hsn_code": "83062120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BELLS, GONGS AND THE LIKE, NON-ELECTRIC, OF BASE METAL; STATUETTES AND OTHER\nORNAMENTS, OF BASE METAL; PHOTOGRAPH, PICTURE OR SIMILAR FRAMES, OF BASE METAL;\nMIRRORS OF BASE METAL - STATUETTES AND OTHER ORNAMENTS: - PLATED WITH PRECIOUS\nMETAL:OTHER",
+        "hsn_code": "83062190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BELLS, GONGS AND THE LIKE, NON-ELECTRIC, OF BASE METAL; STATUETTES AND OTHER\nORNAMENTS, OF BASE METAL; PHOTOGRAPH, PICTURE OR SIMILAR FRAMES, OF BASE METAL;\nMIRRORS OF BASE METAL - STATUETTES AND OTHER ORNAMENTS: - OTHER:STATUETTES",
+        "hsn_code": "83062910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BELLS, GONGS AND THE LIKE, NON-ELECTRIC, OF BASE METAL; STATUETTES AND OTHER\nORNAMENTS, OF BASE METAL; PHOTOGRAPH, PICTURE OR SIMILAR FRAMES, OF BASE METAL;\nMIRRORS OF BASE METAL - STATUETTES AND OTHER ORNAMENTS: - OTHER:TROPHIES",
+        "hsn_code": "83062920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BELLS, GONGS AND THE LIKE, NON-ELECTRIC, OF BASE METAL; STATUETTES AND OTHER\nORNAMENTS, OF BASE METAL; PHOTOGRAPH, PICTURE OR SIMILAR FRAMES, OF BASE METAL;\nMIRRORS OF BASE METAL - STATUETTES AND OTHER ORNAMENTS: - OTHER:OTHER",
+        "hsn_code": "83062990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BELLS, GONGS AND THE LIKE, NON-ELECTRIC, OF BASE METAL; STATUETTES AND OTHER\nORNAMENTS, OF BASE METAL; PHOTOGRAPH, PICTURE OR SIMILAR FRAMES, OF BASE METAL;\nMIRRORS OF BASE METAL - PHOTOGRAPH, PICTURE OR SIMILAR FRAMES; MIRRORS",
+        "hsn_code": "83063000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BELLS, GONGS AND THE LIKE, NON-ELECTRIC, OF BASE METAL; STATUETTES AND OTHER ORNAMENTS, OF BASE METAL; PHOTOGRAPH, PICTURE OR SIMILAR FRAMES, OF BASE METAL; MIRRORS OF BASE METAL",
+        "hsn_code": "8306",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Stationery; fittings for loose-leaf binders or files, letter clips, letter corners, paper clips, indexing tags and the like, staples in strips (for offices, upholstery, packaging), of base metal",
+        "hsn_code": "8305",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FITTINGS FOR LOOSE-LEAF BINDERS OR FILES, LETTER CLIPS, LETTER CORNERS, PAPER CLIPS,\nINDEXING TAGS AND SIMILAR OFFICE ARTICLES, OF BASE METAL; STAPLES IN STRIPS (FOR\nEXAMPLE, FOR OFFICES, UPHOLSTERY, PACKAGING), OF BASE METAL - OTHER, INCLUDING\nPARTS:OTHER",
+        "hsn_code": "83059090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FITTINGS FOR LOOSE-LEAF BINDERS OR FILES, LETTER CLIPS, LETTER CORNERS, PAPER CLIPS,\nINDEXING TAGS AND SIMILAR OFFICE ARTICLES, OF BASE METAL; STAPLES IN STRIPS (FOR\nEXAMPLE, FOR OFFICES, UPHOLSTERY, PACKAGING), OF BASE METAL - OTHER, INCLUDING PARTS\n: CLIPS",
+        "hsn_code": "83059020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FITTINGS FOR LOOSE-LEAF BINDERS OR FILES, LETTER CLIPS, LETTER CORNERS, PAPER CLIPS,\nINDEXING TAGS AND SIMILAR OFFICE ARTICLES, OF BASE METAL; STAPLES IN STRIPS (FOR\nEXAMPLE, FOR OFFICES, UPHOLSTERY, PACKAGING), OF BASE METAL - OTHER, INCLUDING\nPARTS:PINS (OTHER THAN THOSE OF HEADING 7317)",
+        "hsn_code": "83059010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FITTINGS FOR LOOSE-LEAF BINDERS OR FILES, LETTER CLIPS, LETTER CORNERS, PAPER CLIPS,\nINDEXING TAGS AND SIMILAR OFFICE ARTICLES, OF BASE METAL; STAPLES IN STRIPS (FOR\nEXAMPLE, FOR OFFICES, UPHOLSTERY, PACKAGING), OF BASE METAL - STAPLES IN STRIPS",
+        "hsn_code": "83052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FITTINGS FOR LOOSE-LEAF BINDERS OR FILES, LETTER CLIPS, LETTER CORNERS, PAPER CLIPS,\nINDEXING TAGS AND SIMILAR OFFICE ARTICLES, OF BASE METAL; STAPLES IN STRIPS (FOR\nEXAMPLE, FOR OFFICES, UPHOLSTERY, PACKAGING), OF BASE METAL - FITTINGS FOR LOOSE-LEAF\nBINDERS OR FILES",
+        "hsn_code": "83051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Filing, cabinets, card-index cabinets, paper trays, paper rests, pen trays, office-stamp stands and similar office or desk equipment, of base metal, other than office furniture of heading 9403",
+        "hsn_code": "8304",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FILING, CABINETS, CARD - INDEX CABINETS, PAPER TRAYS, PAPER RESTS, PEN TRAYS, OFFICE\nSTAMP STANDS AND SIMILAR OFFICE OR DESK EQUIPMENT, OF BASE METAL, OTHER THAN\nOFFICE FURNITURE OF HEADING 9403",
+        "hsn_code": "83040000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Armoured or reinforced safes, strong-boxes and doors and safe deposit lockers for strong rooms, cash or deed boxes and the like, of base metal",
+        "hsn_code": "8303",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARMOURED OR REINFORCED SAFES, STRONG - BOXES - AND DOORS AND SAFE DEPOSIT LOCKERS\nFOR STRONG - ROOMS, CASH OR DEED BOXES AND THE LIKE, OF BASE METAL",
+        "hsn_code": "83030000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BASE METAL MOUNTINGS, FITTINGS AND SIMILAR ARTICLES SUITABLE FOR FURNITURE, DOORS, STAIRCASES, WINDOWS, BLINDS, COACHWORK, SADDLERY, TRUNKS, CHESTS, CASKETS OR THE LIKE; BASE METAL HAT-RACKS, HAT-PEGS, BRACKETS AND SIMILAR FIXTURES; CASTORS WITH MOUNTINGS OF BASE METAL; AUTOMATIC DOOR CLOSURES OF BASE METAL",
+        "hsn_code": "8302",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BASE METAL MOUNTINGS, FITTINGS AND SIMILAR ARTICLES SUITABLE FOR FURNITURE, DOORS,\nSTAIRCASES, WINDOWS, BLINDS, COACHWORK, SADDLERY, TRUNKS, CHESTS, CASKETS OR THE\nLIKE; BASE METAL HAT - RACKS, HAT - PEGS, BRACKETS AND SIMILAR FIXTURES; CASTORS WITH\nMOUNTINGS OF BASE METAL; AUTOMATIC DOOR CLOSERS OF BASE METAL AUTOMATIC DOOR\nCLOSERS",
+        "hsn_code": "83026000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BASE METAL MOUNTINGS, FITTINGS AND SIMILAR ARTICLES SUITABLE FOR FURNITURE, DOORS,\nSTAIRCASES, WINDOWS, BLINDS, COACHWORK, SADDLERY, TRUNKS, CHESTS, CASKETS OR THE\nLIKE; BASE METAL HAT - RACKS, HAT - PEGS, BRACKETS AND SIMILAR FIXTURES; CASTORS WITH\nMOUNTINGS OF BASE METAL; AUTOMATIC DOOR CLOSERS OF BASE METAL HAT - RACKS, HAT -\nPEGS, BRACKETS AND SIMILAR FIXTURES",
+        "hsn_code": "83025000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BASE METAL MOUNTINGS, FITTINGS AND SIMILAR ARTICLES SUITABLE FOR FURNITURE, DOORS,\nSTAIRCASES, WINDOWS, BLINDS, COACHWORK, SADDLERY, TRUNKS, CHESTS, CASKETS OR THE\nLIKE; BASE METAL HAT - RACKS, HAT - PEGS, BRACKETS AND SIMILAR FIXTURES; CASTORS WITH\nMOUNTINGS OF BASE METAL; AUTOMATIC DOOR CLOSERS OF BASE METAL - OTHER\nMOUNTINGS, FITTINGS AND SIMILAR ARTICLES: OTHER",
+        "hsn_code": "83024900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BASE METAL MOUNTINGS, FITTINGS AND SIMILAR ARTICLES SUITABLE FOR FURNITURE, DOORS,\nSTAIRCASES, WINDOWS, BLINDS, COACHWORK, SADDLERY, TRUNKS, CHESTS, CASKETS OR THE\nLIKE; BASE METAL HAT - RACKS, HAT - PEGS, BRACKETS AND SIMILAR FIXTURES; CASTORS WITH\nMOUNTINGS OF BASE METAL; AUTOMATIC DOOR CLOSERS OF BASE METAL - OTHER\nMOUNTINGS, FITTINGS AND SIMILAR ARTICLES: OTHER, SUITABLE FOR FURNITURE",
+        "hsn_code": "83024200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BASE METAL MOUNTINGS, FITTINGS AND SIMILAR ARTICLES SUITABLE FOR FURNITURE, DOORS,\nSTAIRCASES, WINDOWS, BLINDS, COACHWORK, SADDLERY, TRUNKS, CHESTS, CASKETS OR THE\nLIKE; BASE METAL HAT - RACKS, HAT - PEGS, BRACKETS AND SIMILAR FIXTURES; CASTORS WITH\nMOUNTINGS OF BASE METAL; AUTOMATIC DOOR CLOSERS OF BASE METAL - OTHER\nMOUNTINGS, FITTINGS AND SIMILAR ARTICLES: - SUITABLE FOR BUILDINGS:OTHER",
+        "hsn_code": "83024190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BASE METAL MOUNTINGS, FITTINGS AND SIMILAR ARTICLES SUITABLE FOR FURNITURE, DOORS,\nSTAIRCASES, WINDOWS, BLINDS, COACHWORK, SADDLERY, TRUNKS, CHESTS, CASKETS OR THE\nLIKE; BASE METAL HAT - RACKS, HAT - PEGS, BRACKETS AND SIMILAR FIXTURES; CASTORS WITH\nMOUNTINGS OF BASE METAL; AUTOMATIC DOOR CLOSERS OF BASE METAL - OTHER\nMOUNTINGS, FITTINGS AND SIMILAR ARTICLES: - SUITABLE FOR BUILDINGS:TOWER BOLTS",
+        "hsn_code": "83024120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BASE METAL MOUNTINGS, FITTINGS AND SIMILAR ARTICLES SUITABLE FOR FURNITURE, DOORS,\nSTAIRCASES, WINDOWS, BLINDS, COACHWORK, SADDLERY, TRUNKS, CHESTS, CASKETS OR THE\nLIKE; BASE METAL HAT - RACKS, HAT - PEGS, BRACKETS AND SIMILAR FIXTURES; CASTORS WITH\nMOUNTINGS OF BASE METAL; AUTOMATIC DOOR CLOSERS OF BASE METAL - OTHER\nMOUNTINGS, FITTINGS AND SIMILAR ARTICLES: - SUITABLE FOR BUILDINGS:FITTINGS FOR DOORS\nAND WINDOWS",
+        "hsn_code": "83024110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BASE METAL MOUNTINGS, FITTINGS AND SIMILAR ARTICLES SUITABLE FOR FURNITURE, DOORS,\nSTAIRCASES, WINDOWS, BLINDS, COACHWORK, SADDLERY, TRUNKS, CHESTS, CASKETS OR THE\nLIKE; BASE METAL HAT - RACKS, HAT - PEGS, BRACKETS AND SIMILAR FIXTURES; CASTORS WITH\nMOUNTINGS OF BASE METAL; AUTOMATIC DOOR CLOSERS OF BASE METAL - OTHER\nMOUNTINGS, FITTINGS AND SIMILAR ARTICLES SUITABLE FOR MOTOR VEHICLES:OTHER",
+        "hsn_code": "83023090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BASE METAL MOUNTINGS, FITTINGS AND SIMILAR ARTICLES SUITABLE FOR FURNITURE, DOORS,\nSTAIRCASES, WINDOWS, BLINDS, COACHWORK, SADDLERY, TRUNKS, CHESTS, CASKETS OR THE\nLIKE; BASE METAL HAT - RACKS, HAT - PEGS, BRACKETS AND SIMILAR FIXTURES; CASTORS WITH\nMOUNTINGS OF BASE METAL; AUTOMATIC DOOR CLOSERS OF BASE METAL - OTHER\nMOUNTINGS, FITTINGS AND SIMILAR ARTICLES SUITABLE FOR MOTOR VEHICLES:CURVE DRIVE\nSTAKES",
+        "hsn_code": "83023010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BASE METAL MOUNTINGS, FITTINGS AND SIMILAR ARTICLES SUITABLE FOR FURNITURE, DOORS,\nSTAIRCASES, WINDOWS, BLINDS, COACHWORK, SADDLERY, TRUNKS, CHESTS, CASKETS OR THE\nLIKE; BASE METAL HAT - RACKS, HAT - PEGS, BRACKETS AND SIMILAR FIXTURES; CASTORS WITH\nMOUNTINGS OF BASE METAL; AUTOMATIC DOOR CLOSERS OF BASE METAL CASTORS",
+        "hsn_code": "83022000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BASE METAL MOUNTINGS, FITTINGS AND SIMILAR ARTICLES SUITABLE FOR FURNITURE, DOORS,\nSTAIRCASES, WINDOWS, BLINDS, COACHWORK, SADDLERY, TRUNKS, CHESTS, CASKETS OR THE\nLIKE; BASE METAL HAT - RACKS, HAT - PEGS, BRACKETS AND SIMILAR FIXTURES; CASTORS WITH\nMOUNTINGS OF BASE METAL; AUTOMATIC DOOR CLOSERS OF BASE METAL - HINGES:OTHER",
+        "hsn_code": "83021090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BASE METAL MOUNTINGS, FITTINGS AND SIMILAR ARTICLES SUITABLE FOR FURNITURE, DOORS,\nSTAIRCASES, WINDOWS, BLINDS, COACHWORK, SADDLERY, TRUNKS, CHESTS, CASKETS OR THE\nLIKE; BASE METAL HAT - RACKS, HAT - PEGS, BRACKETS AND SIMILAR FIXTURES; CASTORS WITH\nMOUNTINGS OF BASE METAL; AUTOMATIC DOOR CLOSERS OF BASE METAL - HINGES:OF BRASS",
+        "hsn_code": "83021020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BASE METAL MOUNTINGS, FITTINGS AND SIMILAR ARTICLES SUITABLE FOR FURNITURE, DOORS,\nSTAIRCASES, WINDOWS, BLINDS, COACHWORK, SADDLERY, TRUNKS, CHESTS, CASKETS OR THE\nLIKE; BASE METAL HAT - RACKS, HAT - PEGS, BRACKETS AND SIMILAR FIXTURES; CASTORS WITH\nMOUNTINGS OF BASE METAL; AUTOMATIC DOOR CLOSERS OF BASE METAL - HINGES:OF STEEL",
+        "hsn_code": "83021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PADLOCKS AND LOCKS (KEY, COMBINATION OR ELECTRICALLY OPERATED), OF BASE METAL; CLASPS AND FRAMES WITH CLASPS, INCORPORATING LOCKS, OF BASE METAL; KEYS FOR ANY OF THE FOREGOING ARTICLES, OF BASE METAL",
+        "hsn_code": "8301",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PADLOCKS AND LOCKS (KEY, COMBINATION OR ELECTRICALLY OPERATED), OF BASE METAL;\nCLASPS AND FRAMES WITH CLASPS, INCORPORATING LOCKS, OF BASE METAL; KEYS FOR ANY OF\nTHE FOREGOING ARTICLES, OF BASE METAL - KEYS PRESENTED SEPARATELY",
+        "hsn_code": "83017000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PADLOCKS AND LOCKS (KEY, COMBINATION OR ELECTRICALLY OPERATED), OF BASE METAL;\nCLASPS AND FRAMES WITH CLASPS, INCORPORATING LOCKS, OF BASE METAL; KEYS FOR ANY OF\nTHE FOREGOING ARTICLES, OF BASE METAL - PARTS",
+        "hsn_code": "83016000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PADLOCKS AND LOCKS (KEY, COMBINATION OR ELECTRICALLY OPERATED), OF BASE METAL;\nCLASPS AND FRAMES WITH CLASPS, INCORPORATING LOCKS, OF BASE METAL; KEYS FOR ANY OF\nTHE FOREGOING ARTICLES, OF BASE METAL - CLASPS AND FRAMES WITH CLASPS,\nINCORPORATING LOCKS",
+        "hsn_code": "83015000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PADLOCKS AND LOCKS (KEY, COMBINATION OR ELECTRICALLY OPERATED), OF BASE METAL;\nCLASPS AND FRAMES WITH CLASPS, INCORPORATING LOCKS, OF BASE METAL; KEYS FOR ANY OF\nTHE FOREGOING ARTICLES, OF BASE METAL - OTHER LOCKS: OTHER",
+        "hsn_code": "83014090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PADLOCKS AND LOCKS (KEY, COMBINATION OR ELECTRICALLY OPERATED), OF BASE METAL;\nCLASPS AND FRAMES WITH CLASPS, INCORPORATING LOCKS, OF BASE METAL; KEYS FOR ANY OF\nTHE FOREGOING ARTICLES, OF BASE METAL - OTHER LOCKS: COMBINATION LOCKS",
+        "hsn_code": "83014010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PADLOCKS AND LOCKS (KEY, COMBINATION OR ELECTRICALLY OPERATED), OF BASE METAL;\nCLASPS AND FRAMES WITH CLASPS, INCORPORATING LOCKS, OF BASE METAL; KEYS FOR ANY OF\nTHE FOREGOING ARTICLES, OF BASE METAL - LOCKS OF A KIND USED FOR FURNITURE",
+        "hsn_code": "83013000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PADLOCKS AND LOCKS (KEY, COMBINATION OR ELECTRICALLY OPERATED), OF BASE METAL;\nCLASPS AND FRAMES WITH CLASPS, INCORPORATING LOCKS, OF BASE METAL; KEYS FOR ANY OF\nTHE FOREGOING ARTICLES, OF BASE METAL - LOCKS OF A KIND USED FOR MOTOR VEHICLES",
+        "hsn_code": "83012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PADLOCKS AND LOCKS (KEY, COMBINATION OR ELECTRICALLY OPERATED), OF BASE METAL;\nCLASPS AND FRAMES WITH CLASPS, INCORPORATING LOCKS, OF BASE METAL; KEYS FOR ANY OF\nTHE FOREGOING ARTICLES, OF BASE METAL - PADLOCKS",
+        "hsn_code": "83011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SPOONS, FORKS, LADLES, SKIMMERS, CAKE-SERVERS, FISH-KNIVES, BUTTER-KNIVES, SUGAR TONGS AND SIMILAR KITCHEN OR TABLEWARE",
+        "hsn_code": "8215",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SPOONS, FORKS, LADLES, SKIMMERS, CAKE- SERVERS, FISHKNIVES, BUTTER- KNIVES, SUGAR\nTONGS AND SIMILAR KITCHEN OR TABLEWARE - OTHER : OTHER",
+        "hsn_code": "82159900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SPOONS, FORKS, LADLES, SKIMMERS, CAKE- SERVERS, FISHKNIVES, BUTTER- KNIVES, SUGAR\nTONGS AND SIMILAR KITCHEN OR TABLEWARE - OTHER : PLATED WITH PRECIOUS METAL",
+        "hsn_code": "82159100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SPOONS, FORKS, LADLES, SKIMMERS, CAKE- SERVERS, FISHKNIVES, BUTTER- KNIVES, SUGAR\nTONGS AND SIMILAR KITCHEN OR TABLEWARE OTHER SETS OF ASSORTED ARTICLES",
+        "hsn_code": "82152000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SPOONS, FORKS, LADLES, SKIMMERS, CAKE- SERVERS, FISHKNIVES, BUTTER- KNIVES, SUGAR\nTONGS AND SIMILAR KITCHEN OR TABLEWARE SETS OF ASSORTED ARTICLES CONTAINING AT\nLEAST ONE ARTICLE PLATED WITH PRECIOUS METAL",
+        "hsn_code": "82151000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF CUTLERY (FOR EXAMPLE, HAIR CLIPPERS, BUTCHERS’ OR KITCHEN CLEAVERS, CHOPPERS AND MINCING KNIVES, PAPER KNIVES); MANICURE OR PEDICURE SETS AND INSTRUMENTS (INCLUDING NAIL FILES)",
+        "hsn_code": "8214",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF CUTLERY (FOR EXAMPLE, HAIR CLIPPERS, BUTCHERS OR KITCHEN CLEAVERS,\nCHOPPERS AND MINCING KNIVES, PAPER KNIVES); MANICURE OR PEDICURE SETS AND\nINSTRUMENTS (INCLUDING NAIL FILES) - OTHER : OTHER",
+        "hsn_code": "82149090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF CUTLERY (FOR EXAMPLE, HAIR CLIPPERS, BUTCHERS OR KITCHEN CLEAVERS,\nCHOPPERS AND MINCING KNIVES, PAPER KNIVES); MANICURE OR PEDICURE SETS AND\nINSTRUMENTS (INCLUDING NAIL FILES) - OTHER : HANDLES OF CUTLERY OF BASE METAL",
+        "hsn_code": "82149010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF CUTLERY (FOR EXAMPLE, HAIR CLIPPERS, BUTCHERS OR KITCHEN CLEAVERS,\nCHOPPERS AND MINCING KNIVES, PAPER KNIVES); MANICURE OR PEDICURE SETS AND\nINSTRUMENTS (INCLUDING NAIL FILES) - MANICURE OR PEDICURE SETS AND INSTRUMENTS\n(INCLUDING NAIL FILES) : OTHER",
+        "hsn_code": "82142090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF CUTLERY (FOR EXAMPLE, HAIR CLIPPERS, BUTCHERS OR KITCHEN CLEAVERS,\nCHOPPERS AND MINCING KNIVES, PAPER KNIVES); MANICURE OR PEDICURE SETS AND\nINSTRUMENTS (INCLUDING NAIL FILES) - MANICURE OR PEDICURE SETS AND INSTRUMENTS\n(INCLUDING NAIL FILES) : NAIL CUTTERS",
+        "hsn_code": "82142010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF CUTLERY (FOR EXAMPLE, HAIR CLIPPERS, BUTCHERS OR KITCHEN CLEAVERS,\nCHOPPERS AND MINCING KNIVES, PAPER KNIVES); MANICURE OR PEDICURE SETS AND\nINSTRUMENTS (INCLUDING NAIL FILES) - PAPER KNIVES, LETTER OPENERS, ERASING KNIVES,\nPENCIL SHARPENERS AND BLADES THEREFOR : BLADES",
+        "hsn_code": "82141090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF CUTLERY (FOR EXAMPLE, HAIR CLIPPERS, BUTCHERS OR KITCHEN CLEAVERS,\nCHOPPERS AND MINCING KNIVES, PAPER KNIVES); MANICURE OR PEDICURE SETS AND\nINSTRUMENTS (INCLUDING NAIL FILES) - PAPER KNIVES, LETTER OPENERS, ERASING KNIVES,\nPENCIL SHARPENERS AND BLADES THEREFOR : PAPER KNIVES, LETTER OPENERS, ERASING\nKNIVES, PENCIL SHARPENERS",
+        "hsn_code": "82141010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAZORS AND RAZOR BLADES (INCLUDING RAZOR BLADES BLANKS IN STRIPS)",
+        "hsn_code": "8212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAZORS AND RAZOR BLADES (INCLUDING RAZOR BLADE BLANKS IN STRIPS)OTHER PARTS",
+        "hsn_code": "82129000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAZORS AND RAZOR BLADES (INCLUDING RAZOR BLADE BLANKS IN STRIPS)- SAFETY RAZOR\nBLADES, INCLUDING RAZOR BLADE BLANKS IN STRIPS : SAFETY RAZOR BLADE BLANKS, IN STRIPS",
+        "hsn_code": "82122020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAZORS AND RAZOR BLADES (INCLUDING RAZOR BLADE BLANKS IN STRIPS)- SAFETY RAZOR\nBLADES, INCLUDING RAZOR BLADE BLANKS IN STRIPS : - SAFETY RAZOR BLADES : OTHER",
+        "hsn_code": "82122019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAZORS AND RAZOR BLADES (INCLUDING RAZOR BLADE BLANKS IN STRIPS)- SAFETY RAZOR\nBLADES, INCLUDING RAZOR BLADE BLANKS IN STRIPS : - SAFETY RAZOR BLADES : DISPOSABLE\nCATRIDGE BLADES",
+        "hsn_code": "82122011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAZORS AND RAZOR BLADES (INCLUDING RAZOR BLADE BLANKS IN STRIPS)- RAZORS : OTHER",
+        "hsn_code": "82121090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAZORS AND RAZOR BLADES (INCLUDING RAZOR BLADE BLANKS IN STRIPS)- RAZORS : TWIN TYPE\nSHAVING",
+        "hsn_code": "82121010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNIVES WITH CUTTING BLADES, SERRATED OR NOT (INCLUDING PRUNING KNIVES), OTHER THAN KNIVES OF HEADING 8208, AND BLADES THEREFOR",
+        "hsn_code": "8211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KNIVES WITH CUTTING BLADES, SERRATED OR NOT (INCLUDING PRUNING KNIVES), OTHER THAN\nKNIVES OF HEADING 8208, AND BLADES THEREFOR - OTHER : HANDLES OF BASE METAL",
+        "hsn_code": "82119500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KNIVES WITH CUTTING BLADES, SERRATED OR NOT (INCLUDING PRUNING KNIVES), OTHER THAN\nKNIVES OF HEADING 8208, AND BLADES THEREFOR - OTHER : BLADES",
+        "hsn_code": "82119400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KNIVES WITH CUTTING BLADES, SERRATED OR NOT (INCLUDING PRUNING KNIVES), OTHER THAN\nKNIVES OF HEADING 8208, AND BLADES THEREFOR - OTHER : - KNIVES HAVING OTHER THAN\nFIXED BLADES : OTHER",
+        "hsn_code": "82119390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KNIVES WITH CUTTING BLADES, SERRATED OR NOT (INCLUDING PRUNING KNIVES), OTHER THAN\nKNIVES OF HEADING 8208, AND BLADES THEREFOR - OTHER : - KNIVES HAVING OTHER THAN\nFIXED BLADES : POCKET KNIVES",
+        "hsn_code": "82119310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KNIVES WITH CUTTING BLADES, SERRATED OR NOT (INCLUDING PRUNING KNIVES), OTHER THAN\nKNIVES OF HEADING 8208, AND BLADES THEREFOR - OTHER : OTHER KNIVES HAVING FIXED\nBLADES",
+        "hsn_code": "82119200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KNIVES WITH CUTTING BLADES, SERRATED OR NOT (INCLUDING PRUNING KNIVES), OTHER THAN\nKNIVES OF HEADING 8208, AND BLADES THEREFOR - OTHER : TABLE KNIVES HAVING FIXED\nBLADES",
+        "hsn_code": "82119100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KNIVES WITH CUTTING BLADES, SERRATED OR NOT (INCLUDING PRUNING KNIVES), OTHER THAN\nKNIVES OF HEADING 8208, AND BLADES THEREFOR - SETS OF ASSORTED ARTICLES",
+        "hsn_code": "82111000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PLATES, STICKS, TIPS AND THE LIKE FOR TOOLS, UNMOUNTED, OF CERMETS",
+        "hsn_code": "8209",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLATES, STICKS, TIPS AND THE LIKE FOR TOOLS, UNMOUNTED, OF CERMETS - PLATES, STICKS,\nTIPS AND THE LIKE FOR TOOLS, UNMOUNTED, OF CERMETS : OTHER",
+        "hsn_code": "82090090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLATES, STICKS, TIPS AND THE LIKE FOR TOOLS, UNMOUNTED, OF CERMETS - PLATES, STICKS,\nTIPS AND THE LIKE FOR TOOLS, UNMOUNTED, OF CERMETS : TUNGSTEN CARBIDE TIPS",
+        "hsn_code": "82090010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNIVES AND CUTTING BLADES, FOR MACHINES OR FOR MECHANICAL APPLIANCES",
+        "hsn_code": "8208",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNIVES AND CUTTING BLADES, FOR MACHINES OR FOR MECHANICAL APPLIANCES - OTHER :\nOTHER",
+        "hsn_code": "82089090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNIVES AND CUTTING BLADES, FOR MACHINES OR FOR MECHANICAL APPLIANCES - OTHER :\nCUTTING AND CLICKING DIES",
+        "hsn_code": "82089040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNIVES AND CUTTING BLADES, FOR MACHINES OR FOR MECHANICAL APPLIANCES - OTHER :\nBAND KNIVES FOR SPLITTING MACHINE",
+        "hsn_code": "82089030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNIVES AND CUTTING BLADES, FOR MACHINES OR FOR MECHANICAL APPLIANCES - OTHER : BELL\nSKIVING KNIVES",
+        "hsn_code": "82089020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNIVES AND CUTTING BLADES, FOR MACHINES OR FOR MECHANICAL APPLIANCES - OTHER :\nKNIVES AND CUTTING BLADES FOR PAPER CUTTING MACHINES",
+        "hsn_code": "82089010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNIVES AND CUTTING BLADES, FOR MACHINES OR FOR MECHANICAL APPLIANCES - FOR\nAGRICULTURAL, HORTICULTURAL OR FORESTRY MACHINES",
+        "hsn_code": "82084000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNIVES AND CUTTING BLADES, FOR MACHINES OR FOR MECHANICAL APPLIANCES - FOR KITCHEN\nAPPLIANCES OR FOR MACHINES USED BY THE FOOD INDUSTRY",
+        "hsn_code": "82083000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNIVES AND CUTTING BLADES, FOR MACHINES OR FOR MECHANICAL APPLIANCES - FOR WOOD\nWORKING",
+        "hsn_code": "82082000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "KNIVES AND CUTTING BLADES, FOR MACHINES OR FOR MECHANICAL APPLIANCES - FOR METAL\nWORKING",
+        "hsn_code": "82081000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INTERCHANGEABLE TOOLS FOR HAND TOOLS, WHETHER OR NOT POWER-OPERATED, OR FOR MACHINE – TOOLS (FOR EXAMPLE, FOR PRESSING, STAMPING, PUNCHING, TAPPING, THREADING, DRILLING, BORING, BROACHING, MILLING, TURNING OR SCREW DRIVING), INCLUDING DIES FOR DRAWING OR EXTRUDING METAL, AND ROCK DRILLING OR EARTH BORING TOOLS",
+        "hsn_code": "8207",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INTERCHANGEABLE TOOLS FOR HAND TOOLS, WHETHER OR NOT POWER- OPERATED, OR FOR\nMACHINE- TOOLS (FOR EXAMPLE, FOR PRESSING, STAMPING, PUNCHING, TAPPING, THREADING,\nDRILLING, BORING, BROACHING, MILLING, TURNING OR SCREW DRIVING), INCLUDING DIES FOR\nDRAWING OR EXTRUDING METAL, AND ROCK DRILLING OR EARTH BORING TOOLS - OTHER\nINTERCHANGEABLE TOOLS : OTHER",
+        "hsn_code": "82079090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INTERCHANGEABLE TOOLS FOR HAND TOOLS, WHETHER OR NOT POWER- OPERATED, OR FOR\nMACHINE- TOOLS (FOR EXAMPLE, FOR PRESSING, STAMPING, PUNCHING, TAPPING, THREADING,\nDRILLING, BORING, BROACHING, MILLING, TURNING OR SCREW DRIVING), INCLUDING DIES FOR\nDRAWING OR EXTRUDING METAL, AND ROCK DRILLING OR EARTH BORING TOOLS - OTHER\nINTERCHANGEABLE TOOLS : LATHE TOOLS AND TOOL BELTS",
+        "hsn_code": "82079030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INTERCHANGEABLE TOOLS FOR HAND TOOLS, WHETHER OR NOT POWER- OPERATED, OR FOR\nMACHINE- TOOLS (FOR EXAMPLE, FOR PRESSING, STAMPING, PUNCHING, TAPPING, THREADING,\nDRILLING, BORING, BROACHING, MILLING, TURNING OR SCREW DRIVING), INCLUDING DIES FOR\nDRAWING OR EXTRUDING METAL, AND ROCK DRILLING OR EARTH BORING TOOLS - OTHER\nINTERCHANGEABLE TOOLS : FOR WOOD WORKING HAND TOOLS",
+        "hsn_code": "82079020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INTERCHANGEABLE TOOLS FOR HAND TOOLS, WHETHER OR NOT POWER- OPERATED, OR FOR\nMACHINE- TOOLS (FOR EXAMPLE, FOR PRESSING, STAMPING, PUNCHING, TAPPING, THREADING,\nDRILLING, BORING, BROACHING, MILLING, TURNING OR SCREW DRIVING), INCLUDING DIES FOR\nDRAWING OR EXTRUDING METAL, AND ROCK DRILLING OR EARTH BORING TOOLS - OTHER\nINTERCHANGEABLE TOOLS : FOR METAL WORKING HAND TOOLS",
+        "hsn_code": "82079010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INTERCHANGEABLE TOOLS FOR HAND TOOLS, WHETHER OR NOT POWER- OPERATED, OR FOR\nMACHINE- TOOLS (FOR EXAMPLE, FOR PRESSING, STAMPING, PUNCHING, TAPPING, THREADING,\nDRILLING, BORING, BROACHING, MILLING, TURNING OR SCREW DRIVING), INCLUDING DIES FOR\nDRAWING OR EXTRUDING METAL, AND ROCK DRILLING OR EARTH BORING TOOLS - TOOLS FOR\nTURNING",
+        "hsn_code": "82078000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INTERCHANGEABLE TOOLS FOR HAND TOOLS, WHETHER OR NOT POWER- OPERATED, OR FOR\nMACHINE- TOOLS (FOR EXAMPLE, FOR PRESSING, STAMPING, PUNCHING, TAPPING, THREADING,\nDRILLING, BORING, BROACHING, MILLING, TURNING OR SCREW DRIVING), INCLUDING DIES FOR\nDRAWING OR EXTRUDING METAL, AND ROCK DRILLING OR EARTH BORING TOOLS- TOOLS FOR\nMILLING : OTHER",
+        "hsn_code": "82077090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INTERCHANGEABLE TOOLS FOR HAND TOOLS, WHETHER OR NOT POWER- OPERATED, OR FOR\nMACHINE- TOOLS (FOR EXAMPLE, FOR PRESSING, STAMPING, PUNCHING, TAPPING, THREADING,\nDRILLING, BORING, BROACHING, MILLING, TURNING OR SCREW DRIVING), INCLUDING DIES FOR\nDRAWING OR EXTRUDING METAL, AND ROCK DRILLING OR EARTH BORING TOOLS- TOOLS FOR\nMILLING : CUTTERS",
+        "hsn_code": "82077010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INTERCHANGEABLE TOOLS FOR HAND TOOLS, WHETHER OR NOT POWER- OPERATED, OR FOR\nMACHINE- TOOLS (FOR EXAMPLE, FOR PRESSING, STAMPING, PUNCHING, TAPPING, THREADING,\nDRILLING, BORING, BROACHING, MILLING, TURNING OR SCREW DRIVING), INCLUDING DIES FOR\nDRAWING OR EXTRUDING METAL, AND ROCK DRILLING OR EARTH BORING TOOLS- TOOLS FOR\nBORING OR BROACHING : OTHER",
+        "hsn_code": "82076090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INTERCHANGEABLE TOOLS FOR HAND TOOLS, WHETHER OR NOT POWER- OPERATED, OR FOR\nMACHINE- TOOLS (FOR EXAMPLE, FOR PRESSING, STAMPING, PUNCHING, TAPPING, THREADING,\nDRILLING, BORING, BROACHING, MILLING, TURNING OR SCREW DRIVING), INCLUDING DIES FOR\nDRAWING OR EXTRUDING METAL, AND ROCK DRILLING OR EARTH BORING TOOLS- TOOLS FOR\nBORING OR BROACHING : REAMERS",
+        "hsn_code": "82076010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INTERCHANGEABLE TOOLS FOR HAND TOOLS, WHETHER OR NOT POWER- OPERATED, OR FOR\nMACHINE- TOOLS (FOR EXAMPLE, FOR PRESSING, STAMPING, PUNCHING, TAPPING, THREADING,\nDRILLING, BORING, BROACHING, MILLING, TURNING OR SCREW DRIVING), INCLUDING DIES FOR\nDRAWING OR EXTRUDING METAL, AND ROCK DRILLING OR EARTH BORING TOOLS TOOLS FOR\nDRILLING, OTHER THAN FOR ROCK DRILLING",
+        "hsn_code": "82075000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INTERCHANGEABLE TOOLS FOR HAND TOOLS, WHETHER OR NOT POWER- OPERATED, OR FOR\nMACHINE- TOOLS (FOR EXAMPLE, FOR PRESSING, STAMPING, PUNCHING, TAPPING, THREADING,\nDRILLING, BORING, BROACHING, MILLING, TURNING OR SCREW DRIVING), INCLUDING DIES FOR\nDRAWING OR EXTRUDING METAL, AND ROCK DRILLING OR EARTH BORING TOOLS- TOOLS FOR\nTAPPING OR THREADING : OTHER",
+        "hsn_code": "82074090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INTERCHANGEABLE TOOLS FOR HAND TOOLS, WHETHER OR NOT POWER- OPERATED, OR FOR\nMACHINE- TOOLS (FOR EXAMPLE, FOR PRESSING, STAMPING, PUNCHING, TAPPING, THREADING,\nDRILLING, BORING, BROACHING, MILLING, TURNING OR SCREW DRIVING), INCLUDING DIES FOR\nDRAWING OR EXTRUDING METAL, AND ROCK DRILLING OR EARTH BORING TOOLS- TOOLS FOR\nTAPPING OR THREADING : CHASERS",
+        "hsn_code": "82074010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INTERCHANGEABLE TOOLS FOR HAND TOOLS, WHETHER OR NOT POWER- OPERATED, OR FOR\nMACHINE- TOOLS (FOR EXAMPLE, FOR PRESSING, STAMPING, PUNCHING, TAPPING, THREADING,\nDRILLING, BORING, BROACHING, MILLING, TURNING OR SCREW DRIVING), INCLUDING DIES FOR\nDRAWING OR EXTRUDING METAL, AND ROCK DRILLING OR EARTH BORING TOOLS - TOOLS FOR\nPRESSING, STAMPING OR PUNCHING",
+        "hsn_code": "82073000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INTERCHANGEABLE TOOLS FOR HAND TOOLS, WHETHER OR NOT POWER- OPERATED, OR FOR\nMACHINE- TOOLS (FOR EXAMPLE, FOR PRESSING, STAMPING, PUNCHING, TAPPING, THREADING,\nDRILLING, BORING, BROACHING, MILLING, TURNING OR SCREW DRIVING), INCLUDING DIES FOR\nDRAWING OR EXTRUDING METAL, AND ROCK DRILLING OR EARTH BORING TOOLS - DIES FOR\nDRAWING OR EXTRUDING METAL",
+        "hsn_code": "82072000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INTERCHANGEABLE TOOLS FOR HAND TOOLS, WHETHER OR NOT POWER- OPERATED, OR FOR\nMACHINE- TOOLS (FOR EXAMPLE, FOR PRESSING, STAMPING, PUNCHING, TAPPING, THREADING,\nDRILLING, BORING, BROACHING, MILLING, TURNING OR SCREW DRIVING), INCLUDING DIES FOR\nDRAWING OR EXTRUDING METAL, AND ROCK DRILLING OR EARTH BORING TOOLS- ROCK\nDRILLING OR EARTH BORING TOOLS : OTHER, INCLUDING PARTS",
+        "hsn_code": "82071900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INTERCHANGEABLE TOOLS FOR HAND TOOLS, WHETHER OR NOT POWER- OPERATED, OR FOR\nMACHINE- TOOLS (FOR EXAMPLE, FOR PRESSING, STAMPING, PUNCHING, TAPPING, THREADING,\nDRILLING, BORING, BROACHING, MILLING, TURNING OR SCREW DRIVING), INCLUDING DIES FOR\nDRAWING OR EXTRUDING METAL, AND ROCK DRILLING OR EARTH BORING TOOLS- ROCK\nDRILLING OR EARTH BORING TOOLS : WITH WORKING PART OF CERMETS",
+        "hsn_code": "82071300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOOLS OF TWO OR MORE OF THE HEADINGS 8202 TO 8205, PUT UP IN SETS FOR RETAIL SALE",
+        "hsn_code": "8206",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOOLS OF TWO OR MORE OF THE HEADINGS 8202 TO 8205, PUT UP IN SETS FOR RETAIL SALE -\nTOOLS OF TWO OR MORE OF THE HEADINGS 8202 TO 8205, PUT UP IN SETS FOR RETAIL SALE :\nOTHER",
+        "hsn_code": "82060090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOOLS OF TWO OR MORE OF THE HEADINGS 8202 TO 8205, PUT UP IN SETS FOR RETAIL SALE -\nTOOLS OF TWO OR MORE OF THE HEADINGS 8202 TO 8205, PUT UP IN SETS FOR RETAIL SALE :\nGARAGE TOOLS IN SETS",
+        "hsn_code": "82060010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS (INCLUDING GLAZIERS DIAMONDS), NOT ELSEWHERE SPECIFIED OR INCLUDED; BLOW LAMPS; VICES; CLAMPS AND THE LIKE, OTHER THAN ACCESSORIES FOR AND PARTS OF, MACHINE TOOLS; ANVILS; PORTABLE FORGES; HAND-OR PEDAL-OPERATED GRINDING WHEELS WITH FRAMEWORKS",
+        "hsn_code": "8205",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "82059090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SETS OF ARTICLES OF TWO OR MORE OF THE FOREGOING SUB-HEADINGS",
+        "hsn_code": "82059030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GRINDING WHEELS WITH FRAME, HAND-OR- PEDAL-OPERATED",
+        "hsn_code": "82059020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANVILS AND PORTABLE FORGES",
+        "hsn_code": "82059010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS (INCLUDING GLAZIERS DIAMONDS), NOT ELSEWHERE SPECIFIED OR INCLUDED;\nBLOW LAMPS; VICES; CLAMPS AND THE LIKE, OTHER THAN ACCESSORIES FOR AND PARTS OF,\nMACHINE TOOLS; ANVILS; PORTABLE FORGES; HAND- OR PEDAL- OPERATED GRINDING WHEELS\nWITH FRAMEWORKS - VICES, CLAMPS AND THE LIKE",
+        "hsn_code": "82057000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS (INCLUDING GLAZIERS DIAMONDS), NOT ELSEWHERE SPECIFIED OR INCLUDED;\nBLOW LAMPS; VICES; CLAMPS AND THE LIKE, OTHER THAN ACCESSORIES FOR AND PARTS OF,\nMACHINE TOOLS; ANVILS; PORTABLE FORGES; HAND- OR PEDAL- OPERATED GRINDING WHEELS\nWITH FRAMEWORKS - BLOW LAMPS",
+        "hsn_code": "82056000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS (INCLUDING GLAZIERS DIAMONDS)- OTHER HAND TOOLS (INCLUDING GLAZIERS\nDIAMONDS) : OTHER : OTHER",
+        "hsn_code": "82055990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FORKS OTHER THAN THOSE OF HEADING 8201 AND 8215",
+        "hsn_code": "82055940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS (INCLUDING GLAZIERS DIAMONDS), NOT ELSEWHERE SPECIFIED OR INCLUDED;\nBLOW LAMPS; VICES; CLAMPS AND THE LIKE, OTHER THAN ACCESSORIES FOR AND PARTS OF,\nMACHINE TOOLS; ANVILS; PORTABLE FORGES; HAND- OR PEDAL- OPERATED GRINDING WHEELS\nWITH FRAMEWORKS- OTHER HAND TOOLS (INCLUDING GLAZIERS DIAMONDS) : OTHER : HAND\nTOOLS FOR SPECIFIED USES, SUCH AS WATCH MAKING TOOLS, GOLDSMITH TOOLS",
+        "hsn_code": "82055930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS (INCLUDING GLAZIERS DIAMONDS), NOT ELSEWHERE SPECIFIED OR INCLUDED;\nBLOW LAMPS; VICES; CLAMPS AND THE LIKE, OTHER THAN ACCESSORIES FOR AND PARTS OF,\nMACHINE TOOLS; ANVILS; PORTABLE FORGES; HAND- OR PEDAL- OPERATED GRINDING WHEELS\nWITH FRAMEWORKS- OTHER HAND TOOLS (INCLUDING GLAZIERS DIAMONDS) : OTHER : METAL\nWORKING HAND TOOLS",
+        "hsn_code": "82055920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS (INCLUDING GLAZIERS DIAMONDS), NOT ELSEWHERE SPECIFIED OR INCLUDED;\nBLOW LAMPS; VICES; CLAMPS AND THE LIKE, OTHER THAN ACCESSORIES FOR AND PARTS OF,\nMACHINE TOOLS; ANVILS; PORTABLE FORGES; HAND- OR PEDAL- OPERATED GRINDING WHEELS\nWITH FRAMEWORKS- OTHER HAND TOOLS (INCLUDING GLAZIERS DIAMONDS) : OTHER : GREASE\nGUNS (EXCLUDING COMPRESSED AIR TYPE)",
+        "hsn_code": "82055910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS (INCLUDING GLAZIERS DIAMONDS), NOT ELSEWHERE SPECIFIED OR INCLUDED;\nBLOW LAMPS; VICES; CLAMPS AND THE LIKE, OTHER THAN ACCESSORIES FOR AND PARTS OF,\nMACHINE TOOLS; ANVILS; PORTABLE FORGES; HAND- OR PEDAL- OPERATED GRINDING WHEELS\nWITH FRAMEWORKS- OTHER HAND TOOLS (INCLUDING GLAZIERS DIAMONDS) : - HOUSEHOLD\nTOOLS : OTHER",
+        "hsn_code": "82055190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS (INCLUDING GLAZIERS DIAMONDS), NOT ELSEWHERE SPECIFIED OR INCLUDED;\nBLOW LAMPS; VICES; CLAMPS AND THE LIKE, OTHER THAN ACCESSORIES FOR AND PARTS OF,\nMACHINE TOOLS; ANVILS; PORTABLE FORGES; HAND- OR PEDAL- OPERATED GRINDING WHEELS\nWITH FRAMEWORKS- OTHER HAND TOOLS (INCLUDING GLAZIERS DIAMONDS) : - HOUSEHOLD\nTOOLS : CAN OR CORK OPENERS",
+        "hsn_code": "82055110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS (INCLUDING GLAZIERS DIAMONDS), NOT ELSEWHERE SPECIFIED OR INCLUDED;\nBLOW LAMPS; VICES; CLAMPS AND THE LIKE, OTHER THAN ACCESSORIES FOR AND PARTS OF,\nMACHINE TOOLS; ANVILS; PORTABLE FORGES; HAND- OR PEDAL- OPERATED GRINDING WHEELS\nWITH FRAMEWORKS - SCREWDRIVERS",
+        "hsn_code": "82054000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS (INCLUDING GLAZIERS DIAMONDS), NOT ELSEWHERE SPECIFIED OR INCLUDED;\nBLOW LAMPS; VICES; CLAMPS AND THE LIKE, OTHER THAN ACCESSORIES FOR AND PARTS OF,\nMACHINE TOOLS; ANVILS; PORTABLE FORGES; HAND- OR PEDAL- OPERATED GRINDING WHEELS\nWITH FRAMEWORKS - PLANES, CHISELS, GOUGES AND SIMILAR CUTTING TOOLS FOR WORKING\nWOOD",
+        "hsn_code": "82053000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS (INCLUDING GLAZIERS DIAMONDS), NOT ELSEWHERE SPECIFIED OR INCLUDED;\nBLOW LAMPS; VICES; CLAMPS AND THE LIKE, OTHER THAN ACCESSORIES FOR AND PARTS OF,\nMACHINE TOOLS; ANVILS; PORTABLE FORGES; HAND- OR PEDAL- OPERATED GRINDING WHEELS\nWITH FRAMEWORKS - HAMMERS AND SLEDGE HAMMERS",
+        "hsn_code": "82052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS (INCLUDING GLAZIERS DIAMONDS), NOT ELSEWHERE SPECIFIED OR INCLUDED;\nBLOW LAMPS; VICES; CLAMPS AND THE LIKE, OTHER THAN ACCESSORIES FOR AND PARTS OF,\nMACHINE TOOLS; ANVILS; PORTABLE FORGES; HAND- OR PEDAL- OPERATED GRINDING WHEELS\nWITH FRAMEWORKS - DRILLING, THREADING OR TAPPING TOOLS",
+        "hsn_code": "82051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND-OPERATED SPANNERS AND WRENCHES (INCLUDING TORQUE METER WRENCHES BUT NOT INCLUDING TAP WRENCHES); INTERCHANGEABLE SPANNER SOCKETS, WITH OR WITHOUT HANDLES",
+        "hsn_code": "8204",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND- OPERATED SPANNERS AND WRENCHES (INCLUDING TORQUE METER WRENCHES BUT\nNOT INCLUDING TAP WRENCHES); INTERCHANGEABLE SPANNER SOCKETS, WITH OR WITHOUT\nHANDLES - INTERCHANGEABLE SPANNER SOCKETS, WITH OR WITHOUT HANDLES",
+        "hsn_code": "82042000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND- OPERATED SPANNERS AND WRENCHES (INCLUDING TORQUE METER WRENCHES BUT\nNOT INCLUDING TAP WRENCHES); INTERCHANGEABLE SPANNER SOCKETS, WITH OR WITHOUT\nHANDLES - HAND- OPERATED SPANNERS AND WRENCHES : ADJUSTABLE : WRENCHES",
+        "hsn_code": "82041220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND- OPERATED SPANNERS AND WRENCHES (INCLUDING TORQUE METER WRENCHES BUT\nNOT INCLUDING TAP WRENCHES); INTERCHANGEABLE SPANNER SOCKETS, WITH OR WITHOUT\nHANDLES - HAND- OPERATED SPANNERS AND WRENCHES : ADJUSTABLE : SPANNERS",
+        "hsn_code": "82041210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND- OPERATED SPANNERS AND WRENCHES (INCLUDING TORQUE METER WRENCHES BUT\nNOT INCLUDING TAP WRENCHES); INTERCHANGEABLE SPANNER SOCKETS, WITH OR WITHOUT\nHANDLES - HAND- OPERATED SPANNERS AND WRENCHES : NON- ADJUSTABLE : WRENCHES",
+        "hsn_code": "82041120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND- OPERATED SPANNERS AND WRENCHES (INCLUDING TORQUE METER WRENCHES BUT\nNOT INCLUDING TAP WRENCHES); INTERCHANGEABLE SPANNER SOCKETS, WITH OR WITHOUT\nHANDLES - HAND- OPERATED SPANNERS AND WRENCHES : NON- ADJUSTABLE : SPANNERS",
+        "hsn_code": "82041110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FILES, RASPS, PLIERS (INCLUDING CUTTING PLIERS), PINCERS, TWEEZERS, METAL CUTTING SHEARS, PIPE-CUTTERS, BOLT CROPPERS, PERFORATING PUNCHES AND SIMILAR HAND TOOLS",
+        "hsn_code": "8203",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FILES, RASPS, PLIERS (INCLUDING CUTTING PLIERS), PINCERS, TWEEZERS, METAL CUTTING\nSHEARS, PIPE- CUTTERS, BOLT CROPPERS, PERFORATING PUNCHES AND SIMILAR HAND TOOLS -\nPIPE- CUTTERS, BOLT CROPPERS, PERFORATING PUNCHES AND SIMILAR TOOLS : OTHER",
+        "hsn_code": "82034090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FILES, RASPS, PLIERS (INCLUDING CUTTING PLIERS), PINCERS, TWEEZERS, METAL CUTTING\nSHEARS, PIPE- CUTTERS, BOLT CROPPERS, PERFORATING PUNCHES AND SIMILAR HAND TOOLS -\nPIPE- CUTTERS, BOLT CROPPERS, PERFORATING PUNCHES AND SIMILAR TOOLS : PERFORATING\nPUNCHES AND PIPE CUTTERS",
+        "hsn_code": "82034010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FILES, RASPS, PLIERS (INCLUDING CUTTING PLIERS), PINCERS, TWEEZERS, METAL CUTTING\nSHEARS, PIPE- CUTTERS, BOLT CROPPERS, PERFORATING PUNCHES AND SIMILAR HAND TOOLS -\nMETAL CUTTING SHEARS AND SIMILAR TOOLS",
+        "hsn_code": "82033000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FILES, RASPS, PLIERS (INCLUDING CUTTING PLIERS), PINCERS, TWEEZERS, METAL CUTTING\nSHEARS, PIPE- CUTTERS, BOLT CROPPERS, PERFORATING PUNCHES AND SIMILAR HAND TOOLS -\nPLIERS (INCLUDING CUTTING PLIERS), PINCERS, TWEEZERS AND SIMILAR TOOLS",
+        "hsn_code": "82032000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FILES, RASPS, PLIERS (INCLUDING CUTTING PLIERS), PINCERS, TWEEZERS, METAL CUTTING\nSHEARS, PIPE- CUTTERS, BOLT CROPPERS, PERFORATING PUNCHES AND SIMILAR HAND TOOLS -\nFILES, RASPS AND SIMILAR TOOLS",
+        "hsn_code": "82031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND SAWS; BLADES FOR SAWS OF ALL KINDS (INCLUDING SLITTING, SLOTTING OR TOOTHLESS SAW BLADES)",
+        "hsn_code": "8202",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND SAWS; BLADES FOR SAWS OF ALL KINDS (INCLUDING SLITTING, SLOTTING OR TOOTHLESS\nSAW BLADES)- OTHER SAW BLADES : - OTHER : OTHER",
+        "hsn_code": "82029990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND SAWS; BLADES FOR SAWS OF ALL KINDS (INCLUDING SLITTING, SLOTTING OR TOOTHLESS\nSAW BLADES)- OTHER SAW BLADES : - OTHER : HACKSAW FRAMES",
+        "hsn_code": "82029910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND SAWS; BLADES FOR SAWS OF ALL KINDS (INCLUDING SLITTING, SLOTTING OR TOOTHLESS\nSAW BLADES)- OTHER SAW BLADES : - STRAIGHT SAW BLADES, FOR WORKING METAL : HAND\nOPERATED",
+        "hsn_code": "82029120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND SAWS; BLADES FOR SAWS OF ALL KINDS (INCLUDING SLITTING, SLOTTING OR TOOTHLESS\nSAW BLADES)- OTHER SAW BLADES : - STRAIGHT SAW BLADES, FOR WORKING METAL : MACHINE\nOPERATED",
+        "hsn_code": "82029110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND SAWS; BLADES FOR SAWS OF ALL KINDS (INCLUDING SLITTING, SLOTTING OR TOOTHLESS\nSAW BLADES)CHAIN SAW BLADES",
+        "hsn_code": "82024000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND SAWS; BLADES FOR SAWS OF ALL KINDS (INCLUDING SLITTING, SLOTTING OR TOOTHLESS\nSAW BLADES)- CIRCULAR SAW BLADES (INCLUDING SLITTING OR SLOTTING SAW BLADES) :\nOTHER, INCLUDING PARTS",
+        "hsn_code": "82023900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND SAWS; BLADES FOR SAWS OF ALL KINDS (INCLUDING SLITTING, SLOTTING OR TOOTHLESS\nSAW BLADES)- CIRCULAR SAW BLADES (INCLUDING SLITTING OR SLOTTING SAW BLADES) : WITH\nWORKING PART OF STEEL",
+        "hsn_code": "82023100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND SAWS; BLADES FOR SAWS OF ALL KINDS (INCLUDING SLITTING, SLOTTING OR TOOTHLESS\nSAW BLADES)BAND SAW BLADES",
+        "hsn_code": "82022000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND SAWS; BLADES FOR SAWS OF ALL KINDS (INCLUDING SLITTING, SLOTTING OR TOOTHLESS\nSAW BLADES) - HAND SAWS : OTHER",
+        "hsn_code": "82021090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND SAWS; BLADES FOR SAWS OF ALL KINDS (INCLUDING SLITTING, SLOTTING OR TOOTHLESS\nSAW BLADES) - HAND SAWS : WOOD WORKING AND SIMILAR HAND SAWS",
+        "hsn_code": "82021020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAND SAWS; BLADES FOR SAWS OF ALL KINDS (INCLUDING SLITTING, SLOTTING OR TOOTHLESS\nSAW BLADES) - HAND SAWS : METAL WORKING HAND SAWS",
+        "hsn_code": "82021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CERMETS AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP",
+        "hsn_code": "8113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CERMETS AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - CERMETS AND ARTICLES\nTHEREOF, INCLUDING WASTE AND SCRAP:OTHER",
+        "hsn_code": "81130090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CERMETS AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - CERMETS AND ARTICLES\nTHEREOF, INCLUDING WASTE AND SCRAP:ARTICLES OF CERMETS",
+        "hsn_code": "81130030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CERMETS AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - CERMETS AND ARTICLES\nTHEREOF, INCLUDING WASTE AND SCRAP:WASTE AND SCRAP OF CERMETS",
+        "hsn_code": "81130020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CERMETS AND ARTICLES THEREOF, INCLUDING WASTE AND SCRAP - CERMETS AND ARTICLES\nTHEREOF, INCLUDING WASTE AND SCRAP:UNWROUGHT CERMETS",
+        "hsn_code": "81130010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF TIN",
+        "hsn_code": "8007",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF TIN - OTHER ARTICLES OF TIN : OTHER",
+        "hsn_code": "80070090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF TIN - OTHER ARTICLES OF TIN : BLANKS",
+        "hsn_code": "80070010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Tin bars, rods, profiles and wire",
+        "hsn_code": "8003",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TIN BARS, RODS, PROFILES AND WIRE - TIN BARS, RODS, PROFILES AND WIRE : WIRE",
+        "hsn_code": "80030040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TIN BARS, RODS, PROFILES AND WIRE - TIN BARS, RODS, PROFILES AND WIRE : PROFILES",
+        "hsn_code": "80030030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TIN BARS, RODS, PROFILES AND WIRE - TIN BARS, RODS, PROFILES AND WIRE : BARS, OTHER\nTHAN HOLLOW BARS; RODS",
+        "hsn_code": "80030020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TIN BARS, RODS, PROFILES AND WIRE - TIN BARS, RODS, PROFILES AND WIRE : HOLLOW BARS",
+        "hsn_code": "80030010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TIN WASTE AND SCRAP",
+        "hsn_code": "8002",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TIN WASTE AND SCRAP - TIN WASTE AND SCRAP : OTHER",
+        "hsn_code": "80020090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TIN WASTE AND SCRAP - TIN WASTE AND SCRAP : TIN SCRAP, NAMELY THE FOLLOWING : BLOCK\nTIN COVERED BY ISRI CODE WORD `RANCH; HIGH TIN BASE BABBIT COVERED BY ISRI CODE\nWORD `RAVES; PEWTER COVERED BY ISRI CODE WORD `RANKS",
+        "hsn_code": "80020010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT TIN",
+        "hsn_code": "8001",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT TIN TIN ALLOYS",
+        "hsn_code": "80012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT TIN - TIN, NOT ALLOYED : INGOTS, PIGS, SLABS AND OTHER PRIMARY FORMS OF\nTIN",
+        "hsn_code": "80011090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT TIN - TIN, NOT ALLOYED : BLOCKS",
+        "hsn_code": "80011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF ZINC : SANITARY FIXTURES",
+        "hsn_code": "79070010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF ZINC : OTHER",
+        "hsn_code": "79070090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF ZINC",
+        "hsn_code": "7907",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC PLATES, SHEETS, STRIP AND FOIL - ZINC PLATES, SHEETS, STRIP AND FOIL :CALOTS",
+        "hsn_code": "79050010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC PLATES, SHEETS, STRIP AND FOIL - ZINC PLATES, SHEETS, STRIP AND FOIL :PLATES",
+        "hsn_code": "79050020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC PLATES, SHEETS, STRIP AND FOIL - ZINC PLATES, SHEETS, STRIP AND FOIL :SHEETS, STRIP\nAND CIRCLES",
+        "hsn_code": "79050030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC PLATES, SHEETS, STRIP AND FOIL - ZINC PLATES, SHEETS, STRIP AND FOIL :FOIL",
+        "hsn_code": "79050040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC PLATES, SHEETS, STRIP AND FOIL",
+        "hsn_code": "7905",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC BARS, RODS, PROFILES AND WIRE - ZINC BARS, RODS, PROFILES AND WIRE : BARS AND\nRODS :HOLLOW BARS",
+        "hsn_code": "79040011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC BARS, RODS, PROFILES AND WIRE - ZINC BARS, RODS, PROFILES AND WIRE : BARS AND\nRODS :RODS, INCLUDING WIRE RODS",
+        "hsn_code": "79040012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC BARS, RODS, PROFILES AND WIRE - ZINC BARS, RODS, PROFILES AND WIRE : BARS AND\nRODS :OTHER",
+        "hsn_code": "79040019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC BARS, RODS, PROFILES AND WIRE - ZINC BARS, RODS, PROFILES AND WIRE : PROFILES :\nHOLLOW",
+        "hsn_code": "79040021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC BARS, RODS, PROFILES AND WIRE - ZINC BARS, RODS, PROFILES AND WIRE : PROFILES :\nANGLES, SHAPES AND SECTIONS",
+        "hsn_code": "79040022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC BARS, RODS, PROFILES AND WIRE - ZINC BARS, RODS, PROFILES AND WIRE : PROFILES :\nOTHER",
+        "hsn_code": "79040029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC BARS, RODS, PROFILES AND WIRE - ZINC BARS, RODS, PROFILES AND WIRE : WIRE",
+        "hsn_code": "79040030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC BARS, RODS, PROFILES AND WIRE",
+        "hsn_code": "7904",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC DUST, POWDERS AND FLAKES : ZINC DUST",
+        "hsn_code": "79031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC DUST, POWDERS AND FLAKES : OTHER",
+        "hsn_code": "79039000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC DUST, POWDERS AND FLAKES",
+        "hsn_code": "7903",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC WASTE AND SCRAP - ZINC WASTE AND SCRAP : ZINC SCRAP, NAMELY THE FOLLOWING :\nOLD ZINC DIE CAST SCRAP COVERED BY ISRI CODE WORD `SAVES; NEW ZINC DIE CAST SCRAP\nCOVERED BY ISRI CODE WORD `SCABS; NEW PLATED ZINC DIE CAST SCRAP COVERED BY ISRI\nCODE WORD `SCOPE; ZINC DIE CAST AUTOMOTIVE GRILLS COVERED BY ISRI CODE WORD\n`SCOOT; OLD SCRAP ZINC (ISRI) `SCORE; NEW ZINC CLIPPINGS (ISRI) `SCREEN; ZINC DIE CAST\nSLABS OR PIGS COVERED BY ISRI CODE WORD `SCULL; CRUSHED CLEAN SORTED FRAGMENTIZERS\nDIE CAST SCRAP, AS PRODUCED FROM AUTOMOBILE FRAGMENTIZERS (ISRI) `SCRIBE; HOT DIP\nGALVANIZERS SLAB ZINC DROSS (BATCH PROCESS) (ISRI) `SCRUB (MINIMUM 92% ZINC) FREE OF\nSKIMMINGS; CONTINUOUS LINE GALVANIZING SLAB ZINC TOP DROSS (ISRI) `SEAL (MINIMUM\n90% ZINC) FREE OF SKIMMINGS; CONTINUOUS LINE GALVANIZING SLAB ZINC BOTTOM DROSS\n(ISRI) `SEAM (MINIMUM 92% ZINC) FREE OF SKIMMINGS; PRIME ZINC DIE CAST (ISRI) `SHELF\n(85% ZINC) FREE FROM CORROSION OR OXIDATION",
+        "hsn_code": "79020010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC WASTE AND SCRAP - ZINC WASTE AND SCRAP :OTHER",
+        "hsn_code": "79020090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC WASTE AND SCRAP",
+        "hsn_code": "7902",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT ZINC - ZINC, NOT ALLOYED : CONTAINING BY WEIGHT 99.99% OR MORE OF ZINC",
+        "hsn_code": "79011100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT ZINC - ZINC, NOT ALLOYED : CONTAINING BY WEIGHT LESS THAN 99.99% OF ZINC",
+        "hsn_code": "79011200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT ZINC - ZINC ALLOYS :MOZAK OR ALLOYS OF ZINC AND ALUMINIUM CONTAINING\nNOT LESS THAN 94% BY WEIGHT OF ZINC",
+        "hsn_code": "79012010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT ZINC - ZINC ALLOYS : OTHER",
+        "hsn_code": "79012090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT ZINC",
+        "hsn_code": "7901",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF LEAD - OTHER ARTICLES OF LEAD : SANITARY FIXTURES",
+        "hsn_code": "78060010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF LEAD - OTHER ARTICLES OF LEAD : INDIAN LEAD SEALS",
+        "hsn_code": "78060020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF LEAD - OTHER ARTICLES OF LEAD : BLANKS",
+        "hsn_code": "78060030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF LEAD - OTHER ARTICLES OF LEAD : OTHER",
+        "hsn_code": "78060090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF LEAD",
+        "hsn_code": "7806",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LEAD PLATES, SHEETS, STRIP AND FOIL; LEAD POWDERS AND FLAKES - PLATES, SHEETS, STRIP\nAND FOIL: SHEETS, STRIP AND FOIL OF A THICKNESS (EXCLUDING ANY BACKING) NOT EXCEEDING\n0.2 MM: SHEETS AND STRIP",
+        "hsn_code": "78041110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LEAD PLATES, SHEETS, STRIP AND FOIL; LEAD POWDERS AND FLAKES - PLATES, SHEETS, STRIP\nAND FOIL: SHEETS, STRIP AND FOIL OF A THICKNESS (EXCLUDING ANY BACKING) NOT EXCEEDING\n0.2 MM: FOIL",
+        "hsn_code": "78041120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LEAD PLATES, SHEETS, STRIP AND FOIL; LEAD POWDERS AND FLAKES - PLATES, SHEETS, STRIP\nAND FOIL: OTHER: PLATES",
+        "hsn_code": "78041910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LEAD PLATES, SHEETS, STRIP AND FOIL; LEAD POWDERS AND FLAKES - PLATES, SHEETS, STRIP\nAND FOIL: OTHER: OTHER",
+        "hsn_code": "78041990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LEAD PLATES, SHEETS, STRIP AND FOIL; LEAD POWDERS AND FLAKES : POWDERS AND FLAKES",
+        "hsn_code": "78042000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LEAD PLATES, SHEETS, STRIP AND FOIL; LEAD POWDERS AND FLAKES",
+        "hsn_code": "7804",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LEAD WASTE AND SCRAP - LEAD WASTE AND SCRAP: LEAD SCRAP, NAMELY THE FOLLOWING:\nSCRAP LEAD-SOFT COVERED BY ISRI CODE WORD `RACKS; MIXED HARD OR SOFT SCRAP LEAD\nCOVERED BY ISRI CODE WORD `RADIO; LEAD COVERED COPPER CABLE COVERED BY ISRI CODE\nWORD `RELAY; WHEEL WEIGHTS COVERED BY ISRI CODE WORD `ROPES; MIXED COMMON\nBABBIT COVERED BY ISRI CODE WORD `ROSES",
+        "hsn_code": "78020010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LEAD WASTE AND SCRAP - LEAD WASTE AND SCRAP: OTHER",
+        "hsn_code": "78020090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT LEAD : REFINED LEAD",
+        "hsn_code": "78011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT LEAD - OTHER: CONTAINING BY WEIGHT ANTIMONY AS THE PRINCIPAL OTHER\nELEMENT",
+        "hsn_code": "78019100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT LEAD - OTHER: OTHER: PIG LEAD",
+        "hsn_code": "78019910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT LEAD - OTHER: OTHER: UNREFINED LEAD",
+        "hsn_code": "78019920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT LEAD - OTHER: OTHER: UNREFINED LEAD ALLOYS",
+        "hsn_code": "78019930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT LEAD - OTHER: OTHER: OTHER",
+        "hsn_code": "78019990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT LEAD",
+        "hsn_code": "7801",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF ALUMINIUM - NAILS, TACKS, STAPLES (OTHER THAN THOSE OF HEADING\n8305), SCREWS, BOLTS, NUTS, SCREW HOOKS, RIVETS, COTTERS, COTTER-PMILAR ARTICLES",
+        "hsn_code": "76161000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF ALUMINIUM - OTHER : CLOTH, GRILL, NETTING AND FENCING, OF\nALUMINIUM WIRE",
+        "hsn_code": "76169100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF ALUMINIUM - OTHER : OTHER : EXPANDED METAL OF ALUMINIUM AND\nALUMINIUM ALLOYS",
+        "hsn_code": "76169910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF ALUMINIUM - OTHER : OTHER :CHAINS",
+        "hsn_code": "76169920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF ALUMINIUM - OTHER : OTHER : BOBBINS",
+        "hsn_code": "76169930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF ALUMINIUM",
+        "hsn_code": "7616",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRESSURE COOKERS",
+        "hsn_code": "76151011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOLAR COLLECTORS",
+        "hsn_code": "76151012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NON-STICK",
+        "hsn_code": "76151021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "76151029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TABLE,KITCHEN OR HOUSEHOLD ARTICLES",
+        "hsn_code": "76151030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE",
+        "hsn_code": "76151040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS",
+        "hsn_code": "76151090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF ALUMINIUM; POT\nSCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE, OF ALUMINIUM;\nSANITARY WARE AND PARTS THEREOF, OF ALUMINIUM - TABLE, KITCHEN OR OTHER\nHOUSEHOLD ARTICLES AND PARTS THEREOF; POT SCOURERS AND SCOURING OR POLISHING\nPADS, GLOVES AND THE LIKE : POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES\nAND THE LIKE",
+        "hsn_code": "76151100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF ALUMINIUM; POT\nSCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE, OF ALUMINIUM;\nSANITARY WARE AND PARTS THEREOF, OF ALUMINIUM- SANITARY WARE AND PARTS THEREOF :\nSANITARY WARE OF ALUMINIUM AND ALUMINIUM ALLOYS FOR INDOOR USE",
+        "hsn_code": "76152010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF ALUMINIUM; POT\nSCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE, OF ALUMINIUM;\nSANITARY WARE AND PARTS THEREOF, OF ALUMINIUM- SANITARY WARE AND PARTS THEREOF :\nPARTS",
+        "hsn_code": "76152020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF ALUMINIUM; POT\nSCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE, OF ALUMINIUM;\nSANITARY WARE AND PARTS THEREOF, OF ALUMINIUM- SANITARY WARE AND PARTS THEREOF :\nOTHER",
+        "hsn_code": "76152090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF ALUMINIUM; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE, OF ALUMINIUM; SANITARY WARE AND PARTS THEREOF, OF ALUMINIUM",
+        "hsn_code": "7615",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRANDED WIRE, CABLES, PLAITED BANDS AND THE LIKE, OF ALUMINIUM, NOT ELECTRICALLY\nINSULATED - WITH STEEL CORE",
+        "hsn_code": "76141000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRANDED WIRE, CABLES, PLAITED BANDS AND THE LIKE, OF ALUMINIUM, NOT ELECTRICALLY\nINSULATED - OTHER",
+        "hsn_code": "76149000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRANDED WIRE, CABLES, PLAITED BANDS AND THE LIKE, OF ALUMINIUM, NOT ELECTRICALLY INSULATED",
+        "hsn_code": "7614",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CONTAINERS FOR COMPRESSED OR LIQUEFIED GAS - ALUMINIUM CONTAINERS\nFOR COMPRESSED OR LIQUEFIED GAS : LOW PRESSURE CYLINDERS : PLAIN",
+        "hsn_code": "76130011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CONTAINERS FOR COMPRESSED OR LIQUEFIED GAS - ALUMINIUM CONTAINERS\nFOR COMPRESSED OR LIQUEFIED GAS : LOW PRESSURE CYLINDERS : LACQUERED",
+        "hsn_code": "76130012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CONTAINERS FOR COMPRESSED OR LIQUEFIED GAS - ALUMINIUM CONTAINERS\nFOR COMPRESSED OR LIQUEFIED GAS : LOW PRESSURE CYLINDERS : PRINTED",
+        "hsn_code": "76130013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CONTAINERS FOR COMPRESSED OR LIQUEFIED GAS - ALUMINIUM CONTAINERS\nFOR COMPRESSED OR LIQUEFIED GAS : LOW PRESSURE CYLINDERS : OTHER",
+        "hsn_code": "76130019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CONTAINERS FOR COMPRESSED OR LIQUEFIED GAS - ALUMINIUM CONTAINERS\nFOR COMPRESSED OR LIQUEFIED GAS : HIGH PRESSURE CYLINDERS : PLAIN",
+        "hsn_code": "76130021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CONTAINERS FOR COMPRESSED OR LIQUEFIED GAS - ALUMINIUM CONTAINERS\nFOR COMPRESSED OR LIQUEFIED GAS : HIGH PRESSURE CYLINDERS : LACQUERED",
+        "hsn_code": "76130022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CONTAINERS FOR COMPRESSED OR LIQUEFIED GAS - ALUMINIUM CONTAINERS\nFOR COMPRESSED OR LIQUEFIED GAS : HIGH PRESSURE CYLINDERS : PRINTED",
+        "hsn_code": "76130023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CONTAINERS FOR COMPRESSED OR LIQUEFIED GAS - ALUMINIUM CONTAINERS\nFOR COMPRESSED OR LIQUEFIED GAS : HIGH PRESSURE CYLINDERS : OTHER",
+        "hsn_code": "76130029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CONTAINERS FOR COMPRESSED OR LIQUEFIED GAS - ALUMINIUM CONTAINERS\nFOR COMPRESSED OR LIQUEFIED GAS : OTHER : PLAIN",
+        "hsn_code": "76130091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CONTAINERS FOR COMPRESSED OR LIQUEFIED GAS - ALUMINIUM CONTAINERS\nFOR COMPRESSED OR LIQUEFIED GAS : OTHER : LACQUERED",
+        "hsn_code": "76130092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CONTAINERS FOR COMPRESSED OR LIQUEFIED GAS - ALUMINIUM CONTAINERS\nFOR COMPRESSED OR LIQUEFIED GAS : OTHER : PRINTED",
+        "hsn_code": "76130093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CONTAINERS FOR COMPRESSED OR LIQUEFIED GAS - ALUMINIUM CONTAINERS\nFOR COMPRESSED OR LIQUEFIED GAS : OTHER : OTHER",
+        "hsn_code": "76130099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CONTAINERS FOR COMPRESSED OR LIQUEFIED GAS",
+        "hsn_code": "7613",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CASKS, DRUMS, CANS, BOXES AND SIMILAR CONTAINERS (INCLUDING RIGID OR\nCOLLAPSIBLE TUBULAR CONTAINERS), FOR ANY MATERIAL (OTHER THAN COMPRESSED OR\nLIQUEFIED GAS), OF A CAPACITY NOT EXCEEDING 300 L, WHETHER OR NOT LINED OR HEAT\nINSULATED, BUT NOT FITTED WITH MECHANICAL OR THERMAL EQUIPMENT - COLLAPSIBLE\nTUBULAR CONTAINERS : PLAIN",
+        "hsn_code": "76121010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CASKS, DRUMS, CANS, BOXES AND SIMILAR CONTAINERS (INCLUDING RIGID OR\nCOLLAPSIBLE TUBULAR CONTAINERS), FOR ANY MATERIAL (OTHER THAN COMPRESSED OR\nLIQUEFIED GAS), OF A CAPACITY NOT EXCEEDING 300 L, WHETHER OR NOT LINED OR HEAT\nINSULATED, BUT NOT FITTED WITH MECHANICAL OR THERMAL EQUIPMENT - COLLAPSIBLE\nTUBULAR CONTAINERS : LACQUERED",
+        "hsn_code": "76121020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CASKS, DRUMS, CANS, BOXES AND SIMILAR CONTAINERS (INCLUDING RIGID OR\nCOLLAPSIBLE TUBULAR CONTAINERS), FOR ANY MATERIAL (OTHER THAN COMPRESSED OR\nLIQUEFIED GAS), OF A CAPACITY NOT EXCEEDING 300 L, WHETHER OR NOT LINED OR HEAT\nINSULATED, BUT NOT FITTED WITH MECHANICAL OR THERMAL EQUIPMENT - COLLAPSIBLE\nTUBULAR CONTAINERS : PRINTED",
+        "hsn_code": "76121030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CASKS, DRUMS, CANS, BOXES AND SIMILAR CONTAINERS (INCLUDING RIGID OR\nCOLLAPSIBLE TUBULAR CONTAINERS), FOR ANY MATERIAL (OTHER THAN COMPRESSED OR\nLIQUEFIED GAS), OF A CAPACITY NOT EXCEEDING 300 L, WHETHER OR NOT LINED OR HEAT\nINSULATED, BUT NOT FITTED WITH MECHANICAL OR THERMAL EQUIPMENT - COLLAPSIBLE\nTUBULAR CONTAINERS : OTHER",
+        "hsn_code": "76121090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CASKS, DRUMS, CANS, BOXES AND SIMILAR CONTAINERS (INCLUDING RIGID OR\nCOLLAPSIBLE TUBULAR CONTAINERS), FOR ANY MATERIAL (OTHER THAN COMPRESSED OR\nLIQUEFIED GAS), OF A CAPACITY NOT EXCEEDING 300 L, WHETHER OR NOT LINED OR HEAT\nINSULATED, BUT NOT FITTED WITH MECHANICAL OR THERMAL EQUIPMENT - OTHER : PLAIN",
+        "hsn_code": "76129010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CASKS, DRUMS, CANS, BOXES AND SIMILAR CONTAINERS (INCLUDING RIGID OR\nCOLLAPSIBLE TUBULAR CONTAINERS), FOR ANY MATERIAL (OTHER THAN COMPRESSED OR\nLIQUEFIED GAS), OF A CAPACITY NOT EXCEEDING 300 L, WHETHER OR NOT LINED OR HEAT\nINSULATED, BUT NOT FITTED WITH MECHANICAL OR THERMAL EQUIPMENT - OTHER :\nLACQUERED",
+        "hsn_code": "76129020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CASKS, DRUMS, CANS, BOXES AND SIMILAR CONTAINERS (INCLUDING RIGID OR\nCOLLAPSIBLE TUBULAR CONTAINERS), FOR ANY MATERIAL (OTHER THAN COMPRESSED OR\nLIQUEFIED GAS), OF A CAPACITY NOT EXCEEDING 300 L, WHETHER OR NOT LINED OR HEAT\nINSULATED, BUT NOT FITTED WITH MECHANICAL OR THERMAL EQUIPMENT - OTHER : PRINTED",
+        "hsn_code": "76129030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CASKS, DRUMS, CANS, BOXES AND SIMILAR CONTAINERS (INCLUDING RIGID OR\nCOLLAPSIBLE TUBULAR CONTAINERS), FOR ANY MATERIAL (OTHER THAN COMPRESSED OR\nLIQUEFIED GAS), OF A CAPACITY NOT EXCEEDING 300 L, WHETHER OR NOT LINED OR HEAT\nINSULATED, BUT NOT FITTED WITH MECHANICAL OR THERMAL EQUIPMENT - OTHER : OTHER",
+        "hsn_code": "76129090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM CASKS, DRUMS, CANS, BOXES AND SIMILAR CONTAINERS (INCLUDING RIGID OR COLLAPSIBLE TUBULAR CONTAINERS), FOR ANY MATERIAL (OTHER THAN COMPRESSED OR LIQUEFIED GAS), OF A CAPACITY NOT EXCEEDING 300L, WHETHER OR NOT LINED OR HEAT-INSULATED, BUT NOT FITTED WITH MECHANICAL OR THERMAL EQUIPMENT",
+        "hsn_code": "7612",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM RESERVOIRS, TANKS, VATS AND SIMILAR CONTAINERS, FOR ANY MATERIAL (OTHER\nTHAN COMPRESSED OR LIQUEFIED GAS), OF A CAPACITY EXCEEDING 300 L, WHETHER OR NOT\nLINED OR HEAT-INSULATED, BUT NOT FITTED WITH MECHANICAL OR THERMAL EQUIPMENT",
+        "hsn_code": "76110000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Aluminium reservoirs, tanks, vats and similar containers, for any material (other than compressed or liquefied gas), of a capacity exceeding 300L, whether or not lined or heat-insulated, but not fitted with mechanical or thermal equipment",
+        "hsn_code": "7611",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND\nPARTS OF STRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, TOWERS, LATTICE\nMASTS, ROOFS, ROOFING FRAMEWORKS, DOORS AND WINDOWS AND THEIR FRAMES AND\nTHRESHOLDS FOR DOORS, BALUSTRADES, PILLARS AND COLUMNS); ALUMINIUM PLATES, RODS,\nPROFILES, TUBES AND THE LIKE, PREPARED FOR USE IN STRUCTURES - DOORS, WINDOWS AND\nTHEIR FRAMES AND THRESHOLDS FOR DOORS",
+        "hsn_code": "76101000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND\nPARTS OF STRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, TOWERS, LATTICE\nMASTS, ROOFS, ROOFING FRAMEWORKS, DOORS AND WINDOWS AND THEIR FRAMES AND\nTHRESHOLDS FOR DOORS, BALUSTRADES, PILLARS AND COLUMNS); ALUMINIUM PLATES, RODS,\nPROFILES, TUBES AND THE LIKE, PREPARED FOR USE IN STRUCTURES - OTHER : STRUCTURES",
+        "hsn_code": "76109010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND\nPARTS OF STRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, TOWERS, LATTICE\nMASTS, ROOFS, ROOFING FRAMEWORKS, DOORS AND WINDOWS AND THEIR FRAMES AND\nTHRESHOLDS FOR DOORS, BALUSTRADES, PILLARS AND COLUMNS); ALUMINIUM PLATES, RODS,\nPROFILES, TUBES AND THE LIKE, PREPARED FOR USE IN STRUCTURES - OTHER : PARTS OF\nSTRUCTURES, NOT ELSEWHERE SPECIFIED",
+        "hsn_code": "76109020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND\nPARTS OF STRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, TOWERS, LATTICE\nMASTS, ROOFS, ROOFING FRAMEWORKS, DOORS AND WINDOWS AND THEIR FRAMES AND\nTHRESHOLDS FOR DOORS, BALUSTRADES, PILLARS AND COLUMNS); ALUMINIUM PLATES, RODS,\nPROFILES, TUBES AND THE LIKE, PREPARED FOR USE IN STRUCTURES - OTHER : ALUMINIUM\nPLATES , RODS, PROFILES, TUBES AND THE LIKE, PREPARED FOR USE IN STRUCTURE",
+        "hsn_code": "76109030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND\nPARTS OF STRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, TOWERS, LATTICE\nMASTS, ROOFS, ROOFING FRAMEWORKS, DOORS AND WINDOWS AND THEIR FRAMES AND\nTHRESHOLDS FOR DOORS, BALUSTRADES, PILLARS AND COLUMNS); ALUMINIUM PLATES, RODS,\nPROFILES, TUBES AND THE LIKE, PREPARED FOR USE IN STRUCTURES - OTHER : OTHER",
+        "hsn_code": "76109090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND PARTS OF STRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, TOWERS, LATTICE MASTS, ROOFS, ROOFING FRAMEWORKS, DOORS AND WINDOWS AND THEIR FRAMES AND THRESHOLDS FOR DOORS, BALUSTRADES, PILLARS AND COLUMNS); ALUMINIUM PLATES, RODS, PROFILES, TUBES AND THE LIKE, PREPARED FOR USE IN STRUCTURES",
+        "hsn_code": "7610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES)",
+        "hsn_code": "76090000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Aluminium tube or pipe fittings (for example, couplings, elbows, sleeves)",
+        "hsn_code": "7609",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM TUBES AND PIPE - OF ALUMINIUM, NOT ALLOYED",
+        "hsn_code": "76081000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM TUBES AND PIPE - OF ALUMINIUM ALLOYS",
+        "hsn_code": "76082000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM TUBES AND PIPES",
+        "hsn_code": "7608",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM FOIL (WHETHER OR NOT PRINTED OR BACKED WITH PAPER, PAPERBOARD,\nPLASTICS OR SIMILAR BACKING MATERIALS) OF A THICKNESS (EXCLUDING ANY BACKING) NOT\nEXCEEDING 0.2 MM - NOT BACKED : ROLLED BUT NOT FURTHER WORKED : ORDINARILY USED\nFOR TEA CHEST LINING",
+        "hsn_code": "76071110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM FOIL (WHETHER OR NOT PRINTED OR BACKED WITH PAPER, PAPERBOARD,\nPLASTICS OR SIMILAR BACKING MATERIALS) OF A THICKNESS (EXCLUDING ANY BACKING) NOT\nEXCEEDING 0.2 MM - NOT BACKED : ROLLED BUT NOT FURTHER WORKED : OTHER",
+        "hsn_code": "76071190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM FOIL (WHETHER OR NOT PRINTED OR BACKED WITH PAPER, PAPERBOARD,\nPLASTICS OR SIMILAR BACKING MATERIALS) OF A THICKNESS (EXCLUDING ANY BACKING) NOT\nEXCEEDING 0.2 MM - NOT BACKED : OTHER : ORDINARILY USED FOR TEA CHEST LINING",
+        "hsn_code": "76071910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM FOIL (WHETHER OR NOT PRINTED OR BACKED WITH PAPER, PAPERBOARD,\nPLASTICS OR SIMILAR BACKING MATERIALS) OF A THICKNESS (EXCLUDING ANY BACKING) NOT\nEXCEEDING 0.2 MM - NOT BACKED : OTHER : OTHER : PLAIN",
+        "hsn_code": "76071991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM FOIL (WHETHER OR NOT PRINTED OR BACKED WITH PAPER, PAPERBOARD,\nPLASTICS OR SIMILAR BACKING MATERIALS) OF A THICKNESS (EXCLUDING ANY BACKING) NOT\nEXCEEDING 0.2 MM - NOT BACKED : OTHER : OTHER : EMBOSSED",
+        "hsn_code": "76071992",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM FOIL (WHETHER OR NOT PRINTED OR BACKED WITH PAPER, PAPERBOARD,\nPLASTICS OR SIMILAR BACKING MATERIALS) OF A THICKNESS (EXCLUDING ANY BACKING) NOT\nEXCEEDING 0.2 MM - NOT BACKED : OTHER : OTHER : PERFORATED OR CUT-TO-SHAPE",
+        "hsn_code": "76071993",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM FOIL (WHETHER OR NOT PRINTED OR BACKED WITH PAPER, PAPERBOARD,\nPLASTICS OR SIMILAR BACKING MATERIALS) OF A THICKNESS (EXCLUDING ANY BACKING) NOT\nEXCEEDING 0.2 MM - NOT BACKED : OTHER : OTHER : COATED",
+        "hsn_code": "76071994",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM FOIL (WHETHER OR NOT PRINTED OR BACKED WITH PAPER, PAPERBOARD,\nPLASTICS OR SIMILAR BACKING MATERIALS) OF A THICKNESS (EXCLUDING ANY BACKING) NOT\nEXCEEDING 0.2 MM - NOT BACKED : OTHER : OTHER : PRINTED",
+        "hsn_code": "76071995",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM FOIL (WHETHER OR NOT PRINTED OR BACKED WITH PAPER, PAPERBOARD,\nPLASTICS OR SIMILAR BACKING MATERIALS) OF A THICKNESS (EXCLUDING ANY BACKING) NOT\nEXCEEDING 0.2 MM - NOT BACKED : OTHER : OTHER : OTHER",
+        "hsn_code": "76071999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM FOIL (WHETHER OR NOT PRINTED OR BACKED WITH PAPER, PAPERBOARD,\nPLASTICS OR SIMILAR BACKING MATERIALS) OF A THICKNESS (EXCLUDING ANY BACKING) NOT\nEXCEEDING 0.2 MM - BACKED : ORDINARILY USED FOR TEA CHEST LINING",
+        "hsn_code": "76072010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM FOIL (WHETHER OR NOT PRINTED OR BACKED WITH PAPER, PAPERBOARD,\nPLASTICS OR SIMILAR BACKING MATERIALS) OF A THICKNESS (EXCLUDING ANY BACKING) NOT\nEXCEEDING 0.2 MM - BACKED : OTHER",
+        "hsn_code": "76072090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM FOIL (WHETHER OR NOT PRINTED OR BACKED WITH PAPER, PAPERBOARD, PLASTICS OR SIMILAR BACKING MATERIALS) OF A THICKNESS (EXCLUDING ANY BACKING) NOT EXCEEDING 0.2MM",
+        "hsn_code": "7607",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.2 MM - RECTANGULAR\n(INCLUDING SQUARE) : OF ALUMINIUM, NOT ALLOYED : ELECTROLYTIC PLATES OR SHEETS",
+        "hsn_code": "76061110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.2 MM - RECTANGULAR\n(INCLUDING SQUARE) : OF ALUMINIUM, NOT ALLOYED : OTHER",
+        "hsn_code": "76061190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.2 MM - RECTANGULAR\n(INCLUDING SQUARE) : OF ALUMINIUM ALLOYS",
+        "hsn_code": "76061200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.2 MM - OTHER : OF\nALUMINIUM, NOT ALLOYED : CIRCLES",
+        "hsn_code": "76069110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.2 MM - OTHER : OF\nALUMINIUM, NOT ALLOYED : ELECTROLYTIC PLATES OR SHEETS",
+        "hsn_code": "76069120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.2 MM - OTHER : OF\nALUMINIUM, NOT ALLOYED : OTHER",
+        "hsn_code": "76069190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.2 MM - OTHER : OF\nALUMINIUM ALLOYS : CIRCLES",
+        "hsn_code": "76069210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.2 MM - OTHER : OF\nALUMINIUM ALLOYS : OTHER",
+        "hsn_code": "76069290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.2 MM",
+        "hsn_code": "7606",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM WIRE - OF ALUMINIUM, NOT ALLOYED : OF WHICH THE MAXIMUM CROSS\nSECTIONAL DIMENSION EXCEEDS 7 MM",
+        "hsn_code": "76051100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM WIRE - OF ALUMINIUM, NOT ALLOYED : OTHER : OF WHICH THE MAXIMUM CROSS\nSECTIONAL DIMENSION EXCEEDS 6 MM BUT DOES NOT EXCEED 7 MM",
+        "hsn_code": "76051910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM WIRE - OF ALUMINIUM, NOT ALLOYED : OTHER : OTHER : HARD DRAWN BARE\nSOLID",
+        "hsn_code": "76051991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM WIRE - OF ALUMINIUM, NOT ALLOYED : OTHER : OTHER : OTHER",
+        "hsn_code": "76051999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM WIRE - OF ALUMINIUM ALLOYS : OF WHICH THE MAXIMUM CROSS-SECTIONAL\nDIMENSION EXCEEDS 7 MM",
+        "hsn_code": "76052100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM WIRE - OF ALUMINIUM ALLOYS : OTHER : OF WHICH THE MAXIMUM CROSS\nSECTIONAL DIMENSION EXCEEDS 6 MM BUT DOES NOT EXCEED 7 MM",
+        "hsn_code": "76052910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM WIRE - OF ALUMINIUM ALLOYS : OTHER : OTHER",
+        "hsn_code": "76052990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM WIRE",
+        "hsn_code": "7605",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM BARS, RODS AND PROFILES - OF ALUMINIUM, NOT ALLOYED : WIRE RODS",
+        "hsn_code": "76041010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM BARS, RODS AND PROFILES - OF ALUMINIUM, NOT ALLOYED : BARS AND RODS,\nOTHER THAN WIRE RODS",
+        "hsn_code": "76041020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM BARS, RODS AND PROFILES - OF ALUMINIUM, NOT ALLOYED : PROFILES : HOLLOW",
+        "hsn_code": "76041031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM BARS, RODS AND PROFILES - OF ALUMINIUM, NOT ALLOYED : PROFILES : OTHER",
+        "hsn_code": "76041039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM BARS, RODS AND PROFILES - OF ALUMINIUM ALLOYS : HOLLOW PROFILES",
+        "hsn_code": "76042100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM BARS, RODS AND PROFILES - OF ALUMINIUM ALLOYS : OTHER : HARD DRAWN BARE\nALUMINIUM CONDUCTORS STEEL RE-INFORCED (A.C.S.R.)",
+        "hsn_code": "76042910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM BARS, RODS AND PROFILES - OF ALUMINIUM ALLOYS : OTHER : WIRE RODS",
+        "hsn_code": "76042920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM BARS, RODS AND PROFILES - OF ALUMINIUM ALLOYS : OTHER : BARS AND RODS,\nOTHER THAN WIRE RODS",
+        "hsn_code": "76042930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM BARS, RODS AND PROFILES - OF ALUMINIUM ALLOYS : OTHER : OTHER",
+        "hsn_code": "76042990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM BARS, RODS AND PROFILES",
+        "hsn_code": "7604",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM POWDERS AND FLAKES - POWDERS OF NON-LAMELLAR STRUCTURE : ALUMINIUM\nPOWDER FOR THERMIT PROCESS",
+        "hsn_code": "76031010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM POWDERS AND FLAKES - POWDERS OF NON-LAMELLAR STRUCTURE : OTHER",
+        "hsn_code": "76031090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM POWDERS AND FLAKES - POWDERS OF LAMELLAR STRUCTURE; FLAKES",
+        "hsn_code": "76032000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM POWDERS AND FLAKES",
+        "hsn_code": "7603",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM WASTE AND SCRAP - ALUMINIUM WASTE AND SCRAP : ALUMINIUM SCRAP,\nNAMELY THE FOLLOWING : CLEAN ALUMINIUM LITHOGRAPHIC SHEETS (ISRI) NEW, CLEAN\nALUMINIUM LITHOGRAPHIC SHEETS (ISRI)MIXED LOW COPPER ALUMINIUM CLIPPINGS AND\nSOLIDS (ISRI) CLEAN MIXED OLD ALLOY SHEET ALUMINIUM (ISRI) NEW ALUMINIUM CAN STOCK\n(ISRI) OLD CAN STOCK (ISRI) SHREDDED ALUMINIUM USED BEVERAGES CAN (U) SCRAP (ISRI)\nDENSIFIED ALUMINIUM USED BEVERAGES CAN (UBC) SCRAP (ISRI)BALED ALUMINIUM USED\nBEVERAGE CAN (UBC) SCRAP (ISRI) BRIQUETTED ALUMINIUM USED BEVERAGE CAN (UBC) SCRAP\n(ISRI) PAINTED SIDING (ISRI) COATED SCRAP (ISRI) ALUMINIUM SCRAP RADIATORS (ISRI) E.C.\nALUMINIUM NODULES (ISRI) NEW PURE ALUMINIUM WIRE AND CABLE (ISRI) NEW MIXED\nALUMINIUM WIRE AND CABLE (ISRI) OLD PURE ALUMINIUM WIRE AND CABLE (ISRI) OLD MIXED\nALUMINIUM WIRE AND CABLE (ISRI) ALUMINIUM PISTONS (ISRI) SEGREGATED ALUMINIUM\nBORINGS AND TURNINGS (ISRI) MIXED ALUMINIUM CASTINGS (ISRI) MIXED ALUMINIUM\nCASTINGS (ISRI) WRECKED AIRPLANE SHEET ALUMINIUM (ISRI) NEW ALUMINIUM FOIL (ISRI)\nOLD ALUMINIUM FOIL (ISRI) ALUMINIUM GRINDINGS (ISRI) SWEATED ALUMINIUM (ISRI)\nSEGREGATED NEW ALUMINIUM ALLOY CLIPPINGS AND SOLIDS (ISRI) MIXED NEW ALUMINIUM\nALLOY CLIPPINGS AND SOLIDS (ISRI) SEGREGATED NEW ALUMINIUM CASTINGS, FORGINGS AND\nEXTRUSIONS (ISRI) ALUMINIUM AUTO CASTINGS (ISRI) INSULATED ALUMINIUM WIRE SCRAP\n(ISRI) ALUMINIUM AIRPLANE CASTINGS (ISRI) FRAGMENTIZER ALUMINIUM SCRAP",
+        "hsn_code": "76020010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM WASTE AND SCRAP - ALUMINIUM WASTE AND SCRAP : OTHER WASTE AND SCRAP",
+        "hsn_code": "76020090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM WASTE AND SCRAP",
+        "hsn_code": "7602",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT ALUMINIUM - ALUMINIUM, NOT ALLOYED : INGOTS",
+        "hsn_code": "76011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT ALUMINIUM - ALUMINIUM, NOT ALLOYED : BILLETS",
+        "hsn_code": "76011020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT ALUMINIUM - ALUMINIUM, NOT ALLOYED : WIRE BARS",
+        "hsn_code": "76011030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT ALUMINIUM - ALUMINIUM, NOT ALLOYED : WIRE RODS",
+        "hsn_code": "76011040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT ALUMINIUM - ALUMINIUM, NOT ALLOYED : OTHER",
+        "hsn_code": "76011090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT ALUMINIUM - ALUMINIUM ALLOYS : INGOTS",
+        "hsn_code": "76012010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT ALUMINIUM - ALUMINIUM ALLOYS : BILLETS",
+        "hsn_code": "76012020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT ALUMINIUM - ALUMINIUM ALLOYS : WIRE BARS",
+        "hsn_code": "76012030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT ALUMINIUM - ALUMINIUM ALLOYS : WIRE RODS",
+        "hsn_code": "76012040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT ALUMINIUM - ALUMINIUM ALLOYS : OTHER",
+        "hsn_code": "76012090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT ALUMINIUM",
+        "hsn_code": "7601",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF NICKEL - CLOTH, GRILL AND NETTING, OF NICKEL WIRE",
+        "hsn_code": "75081000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF NICKEL - OTHER : ELECTROPLATING ANODES OF NICKEL",
+        "hsn_code": "75089010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF NICKEL - OTHER : BLANKS ORDINARILY USED FOR MANUFACTURING TUBES\nAND PIPES OF NICKEL",
+        "hsn_code": "75089020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF NICKEL - OTHER : NICKEL SCREEN",
+        "hsn_code": "75089030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF NICKEL - OTHER : OTHER ARTICLES OF NICKEL AND NICKEL ALLOY",
+        "hsn_code": "75089090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF NICKEL",
+        "hsn_code": "7508",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL TUBES, PIPES AND TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS,\nSLEEVES) - TUBES AND PIPES : OF NICKEL, NOT ALLOYED",
+        "hsn_code": "75071100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL TUBES, PIPES AND TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS,\nSLEEVES) - TUBES AND PIPES : OF NICKEL ALLOYS",
+        "hsn_code": "75071200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL TUBES, PIPES AND TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS,\nSLEEVES) - TUBE OR PIPE FITTINGS",
+        "hsn_code": "75072000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL TUBES, PIPES AND TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES)",
+        "hsn_code": "7507",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL PLATES, SHEETS, STRIP AND FOIL - OF NICKEL, NOT ALLOYED",
+        "hsn_code": "75061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL PLATES, SHEETS, STRIP AND FOIL - OF NICKEL ALLOYS",
+        "hsn_code": "75062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL PLATES, SHEETS, STRIP AND FOIL",
+        "hsn_code": "7506",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL BARS, RODS, PROFILES AND WIRE - BARS, RODS AND PROFILES : OF NICKEL, NOT\nALLOYED : HOLLOW BARS",
+        "hsn_code": "75051110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL BARS, RODS, PROFILES AND WIRE - BARS, RODS AND PROFILES : OF NICKEL, NOT\nALLOYED : OTHER BARS; RODS AND PROFILES",
+        "hsn_code": "75051120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL BARS, RODS, PROFILES AND WIRE - BARS, RODS AND PROFILES : OF NICKEL ALLOYS :\nHOLLOW BARS",
+        "hsn_code": "75051210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL BARS, RODS, PROFILES AND WIRE - BARS, RODS AND PROFILES : OF NICKEL ALLOYS :\nOTHER BARS; RODS AND PROFILES",
+        "hsn_code": "75051220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL BARS, RODS, PROFILES AND WIRE - WIRE : OF NICKEL, NOT ALLOYED",
+        "hsn_code": "75052100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL BARS, RODS, PROFILES AND WIRE - WIRE : OF NICKEL ALLOYS",
+        "hsn_code": "75052200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL BARS, RODS, PROFILES AND WIRE",
+        "hsn_code": "7505",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL POWDERS AND FLAKES",
+        "hsn_code": "75040000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Nickel powders and flakes",
+        "hsn_code": "7504",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL WASTE AND SCRAP - NICKEL WASTE AND SCRAP : NICKEL SCRAP, NAMELY THE\nFOLLOWING : NEW NICKEL SCRAP COVERED BY ISRI CODE WORD `AROMA; OLD NICKEL SCRAP\nCOVERED BY ISRI CODE WORD `BURLY; NEW CUPRO NICKEL CLIPS AND SOLIDS COVERED BY ISRI\nCODE WORD `DANDY; CUPRO NICKEL SOLIDS COVERED BY ISRI CODE WORD `DAUNT; SOLDERED\nCUPRO-NICKEL SOLIDS COVERED BY ISRI CODE WORD `DELTA; CUPRO NICKEL SPINNINGS,\nTURNINGS, BORINGS COVERED BY ISRI CODE WORD `DECOY; MISCELLANEOUS NICKEL-COPPER\nAND NICKEL-COPPER IRON COVERED BY ISRI CODE WORD `DEPTH; MIXED NEW NICKEL SILVER\nCLIPPINGS COVERED BY ISRI CODE WORD `MAIZE; MIXED NEW NICKEL SILVER CLIPPINGS\nCOVERED BY ISRI CODE WORD `MAJOR; NEW SEGREGATED NICKEL SILVER CLIPPINGS COVERED\nBY ISRI CODE WORD `MALAR; OLD NICKEL SILVER COVERED BY ISRI CODE WORD `MALIC; NICKEL\nSILVER CASTINGS COVERED BY ISRI CODE WORD `NAGGY; NICKEL SILVER TURNINGS COVERED BY\nISRI CODE WORD `NIECE; NEW R-MONEL CLIPPINGS SOLIDS COVERED BY ISRI CODE WORD\n`HITCH; NEW MIXED MONEL SOLIDS AND CLIPPINGS COVERED BY ISRI CODE WORD `HOUSE; OLD\nMONEL SHEET AND SOLIDS COVERED BY ISRI CODE WORD `IDEAL; K-MONEL SOLIDS COVERED BY\nISRI CODE WORD `INDIAN; SOLDERED MONEL SHEET AND SOLIDS COVERED BY ISRI CODE WORD\n`JUNTO; MONEL CASTINGS COVERED BY ISRI CODE`LEMON; MONEL TURNINGS COVERED BY ISRI\nCODE WORD `LEMUR; NICKEL SCRAP OBTAINED BY BREAKING UP OF SHIPS, BOATS AND OTHER\nFLOATING STRUCTURES",
+        "hsn_code": "75030010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL WASTE AND SCRAP - NICKEL WASTE AND SCRAP : OTHER",
+        "hsn_code": "75030090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL WASTE AND SCRAP",
+        "hsn_code": "7503",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT NICKEL - NICKEL, NOT ALLOYED",
+        "hsn_code": "75021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT NICKEL - NICKEL ALLOYS :CUPRO-NICKEL CONTAINING MORE THAN 40% BY\nWEIGHT OF NICKEL",
+        "hsn_code": "75022010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT NICKEL - NICKEL ALLOYS :MONEL METAL INCLUDING `K MONEL",
+        "hsn_code": "75022020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT NICKEL - NICKEL ALLOYS :NICKEL ALLOYS CONTAINING MORE THAN 40% BY\nWEIGHT OF NICKEL",
+        "hsn_code": "75022030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT NICKEL - NICKEL ALLOYS :NICKEL ALLOYS CONTAINING MORE THAN 10 % BUT NOT\nMORE THAN 40 % BY WEIGHT OF NICKEL",
+        "hsn_code": "75022040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT NICKEL - NICKEL ALLOYS :OTHER",
+        "hsn_code": "75022090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNWROUGHT NICKEL",
+        "hsn_code": "7502",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL MATTES, NICKEL OXIDE SINTERS AND OTHER INTERMEDIATE PRODUCTS OF NICKEL\nMETALLURGY - NICKEL MATTES",
+        "hsn_code": "75011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL MATTES, NICKEL OXIDE SINTERS AND OTHER INTERMEDIATE PRODUCTS OF NICKEL\nMETALLURGY - NICKEL OXIDE SINTERS AND OTHER INTERMEDIATE PRODUCTS OF NICKEL\nMETALLURGY",
+        "hsn_code": "75012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL MATTES, NICKEL OXIDE SINTERS AND OTHER INTERMEDIATE PRODUCTS OF NICKEL METALLURGY",
+        "hsn_code": "7501",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF COPPER - CHAIN AND PARTS THEREOF : CHAIN",
+        "hsn_code": "74191010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF COPPER - CHAIN AND PARTS THEREOF : PARTS : OF COPPER CHAINS",
+        "hsn_code": "74191021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF COPPER - CHAIN AND PARTS THEREOF : PARTS : OTHER",
+        "hsn_code": "74191029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF COPPER - OTHER : CAST, MOULDED, STAMPED OR FORGED, BUT NOT\nFURTHER WORKED",
+        "hsn_code": "74199100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF COPPER - OTHER : OTHER :RESERVOIRS, TANKS, VATS AND SIMILAR\nCONTAINERS OF A CAPCITY ABOVE 300 L",
+        "hsn_code": "74199910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF COPPER - OTHER : OTHER :ARTICLES OF COPPER ALLOYS ELECTRO-PLATED\nWITH NICKEL-SILVER",
+        "hsn_code": "74199920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF COPPER - OTHER : OTHER :ARTICLES OF BRASS",
+        "hsn_code": "74199930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF COPPER - OTHER : OTHER :COPPER WORKED ARTICLES",
+        "hsn_code": "74199940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF COPPER - OTHER : OTHER : OTHER ARTICLES OF COPPER",
+        "hsn_code": "74199990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF COPPER",
+        "hsn_code": "7419",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF COPPER; POT\nSCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE, OF COPPER; SANITARY\nWARE AND PARTS THEREOF, OF COPPER - TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES\nAND PARTS THEREOF; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE ---POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE",
+        "hsn_code": "74181010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF COPPER; POT\nSCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE, OF COPPER; SANITARY\nWARE AND PARTS THEREOF, OF COPPER - TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES\nAND PARTS THEREOF; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE ---UTENSILS----OF BRASS",
+        "hsn_code": "74181021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF COPPER; POT\nSCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE, OF COPPER; SANITARY\nWARE AND PARTS THEREOF, OF COPPER - TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES\nAND PARTS THEREOF; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE ---UTENSILS----OF COPPER",
+        "hsn_code": "74181022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF COPPER; POT\nSCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE, OF COPPER; SANITARY\nWARE AND PARTS THEREOF, OF COPPER - TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES\nAND PARTS THEREOF; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE ---UTENSILS----OF OTHER COPPER ALLOYS",
+        "hsn_code": "74181023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF COPPER; POT\nSCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE, OF COPPER; SANITARY\nWARE AND PARTS THEREOF, OF COPPER - TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES\nAND PARTS THEREOF; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE ---UTENSILS----E.P.N/\\.S. WARE",
+        "hsn_code": "74181024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF COPPER; POT\nSCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE, OF COPPER; SANITARY\nWARE AND PARTS THEREOF, OF COPPER - TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES\nAND PARTS THEREOF; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE ---OTHER----OF E.P.N.S.",
+        "hsn_code": "74181031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF COPPER; POT\nSCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE, OF COPPER; SANITARY\nWARE AND PARTS THEREOF, OF COPPER - TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES\nAND PARTS THEREOF; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE ----OTHER----OTHER",
+        "hsn_code": "74181039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF COPPER; POT\nSCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE, OF COPPER; SANITARY\nWARE AND PARTS THEREOF, OF COPPER - TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES\nAND PARTS THEREOF; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE ---OTHER----PARTS",
+        "hsn_code": "74181090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF COPPER; POT\nSCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE, OF COPPER; SANITARY\nWARE AND PARTS THEREOF, OF COPPER - TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES\nAND PARTS THEREOF; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE : POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE",
+        "hsn_code": "74181100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF COPPER; POT\nSCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE, OF COPPER; SANITARY\nWARE AND PARTS THEREOF, OF COPPER - SANITARY WARE AND PARTS THEREOF : SANITARY\nWARE",
+        "hsn_code": "74182010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF COPPER; POT\nSCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE, OF COPPER; SANITARY\nWARE AND PARTS THEREOF, OF COPPER - SANITARY WARE AND PARTS THEREOF : PARTS OF\nSANITARY WARE",
+        "hsn_code": "74182020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF COPPER; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE, OF COPPER; SANITARY WARE AND PARTS THEREOF, OF COPPER",
+        "hsn_code": "7418",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NAILS, TACKS, DRAWING PINS, STAPLES (OTHER THAN THOSE OF HEADING 8305) AND SIMILAR\nARTICLES, OF COPPER OR OF IRON OR STEEL WITH HEADS OF COPPER; SCREWS, BOLTS, NUTS,\nSCREW HOOKS, RIVETS, COTTERS, COTTERPINS, WASHERS (INCLUDING SPRING WASHERS) AND\nSIMILAR ARTICLES, OF COPPER - NAILS AND TACKS, DRAWING PINS, STAPLES AND SIMILAR\nARTICLES",
+        "hsn_code": "74151000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NAILS, TACKS, DRAWING PINS, STAPLES (OTHER THAN THOSE OF HEADING 8305) AND SIMILAR\nARTICLES, OF COPPER OR OF IRON OR STEEL WITH HEADS OF COPPER; SCREWS, BOLTS, NUTS,\nSCREW HOOKS, RIVETS, COTTERS, COTTERPINS, WASHERS (INCLUDING SPRING WASHERS) AND\nSIMILAR ARTICLES, OF COPPER - OTHER ARTICLES, NOT THREADED : WASHERS (INCLUDING\nSPRING WASHERS)",
+        "hsn_code": "74152100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NAILS, TACKS, DRAWING PINS, STAPLES (OTHER THAN THOSE OF HEADING 8305) AND SIMILAR\nARTICLES, OF COPPER OR OF IRON OR STEEL WITH HEADS OF COPPER; SCREWS, BOLTS, NUTS,\nSCREW HOOKS, RIVETS, COTTERS, COTTERPINS, WASHERS (INCLUDING SPRING WASHERS) AND\nSIMILAR ARTICLES, OF COPPER - OTHER ARTICLES, NOT THREADED : OTHER",
+        "hsn_code": "74152900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NAILS, TACKS, DRAWING PINS, STAPLES (OTHER THAN THOSE OF HEADING 8305) AND SIMILAR\nARTICLES, OF COPPER OR OF IRON OR STEEL WITH HEADS OF COPPER; SCREWS, BOLTS, NUTS,\nSCREW HOOKS, RIVETS, COTTERS, COTTERPINS, WASHERS (INCLUDING SPRING WASHERS) AND\nSIMILAR ARTICLES, OF COPPER - OTHER THREADED ARTICLES : SCREWS; BOLTS AND NUTS :\nSCREWS FOR WOOD",
+        "hsn_code": "74153310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NAILS, TACKS, DRAWING PINS, STAPLES (OTHER THAN THOSE OF HEADING 8305) AND SIMILAR\nARTICLES, OF COPPER OR OF IRON OR STEEL WITH HEADS OF COPPER; SCREWS, BOLTS, NUTS,\nSCREW HOOKS, RIVETS, COTTERS, COTTERPINS, WASHERS (INCLUDING SPRING WASHERS) AND\nSIMILAR ARTICLES, OF COPPER - OTHER THREADED ARTICLES : SCREWS; BOLTS AND NUTS :\nOTHER",
+        "hsn_code": "74153390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NAILS, TACKS, DRAWING PINS, STAPLES (OTHER THAN THOSE OF HEADING 8305) AND SIMILAR\nARTICLES, OF COPPER OR OF IRON OR STEEL WITH HEADS OF COPPER; SCREWS, BOLTS, NUTS,\nSCREW HOOKS, RIVETS, COTTERS, COTTERPINS, WASHERS (INCLUDING SPRING WASHERS) AND\nSIMILAR ARTICLES, OF COPPER - OTHER THREADED ARTICLES : OTHER : RIVETS (EXCLUDING\nTUBULAR OR BIFURCATED)",
+        "hsn_code": "74153910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NAILS, TACKS, DRAWING PINS, STAPLES (OTHER THAN THOSE OF HEADING 8305) AND SIMILAR\nARTICLES, OF COPPER OR OF IRON OR STEEL WITH HEADS OF COPPER; SCREWS, BOLTS, NUTS,\nSCREW HOOKS, RIVETS, COTTERS, COTTERPINS, WASHERS (INCLUDING SPRING WASHERS) AND\nSIMILAR ARTICLES, OF COPPER - OTHER THREADED ARTICLES : OTHER : OTHER",
+        "hsn_code": "74153990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NAILS, TACKS, DRAWING PINS, STAPLES (OTHER THAN THOSE OF HEADING 8305) AND SIMILAR ARTICLES, OF COPPER OR OF IRON OR STEEL WITH HEADS OF COPPER; SCREWS, BOLTS, NUTS, SCREW HOOKS, RIVETS, COTTERS, COTTER-PINS, WASHERS (INCLUDING SPRING WASHERS) AND SIMILAR ARTICLES, OF COPPER",
+        "hsn_code": "7415",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRANDED WIRE, CABLES, PLATED BANDS AND THE LIKE, OF COPPER, NOT ELECTRICALLY\nINSULATED",
+        "hsn_code": "74130000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Stranded wire, cables, plated bands and the like, of copper, not electrically insulated",
+        "hsn_code": "7413",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES) OF REFINED\nCOPPER",
+        "hsn_code": "74121000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES) - OF COPPER\nALLOYS : BRASS : TUBE-WELL STRAINER",
+        "hsn_code": "74122011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES) - OF COPPER\nALLOYS : BRASS : HOSE CONNECTORS",
+        "hsn_code": "74122012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES) - OF COPPER\nALLOYS : BRASS : OTHER",
+        "hsn_code": "74122019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES) - OF COPPER\nALLOYS : FITTINGS OF BRONZE OR OTHER ALLOYS OF COPPER",
+        "hsn_code": "74122090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES)",
+        "hsn_code": "7412",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER TUBES AND PIPES - OF REFINED COPPER",
+        "hsn_code": "74111000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER TUBES AND PIPES - OF COPPER ALLOYS : OF COPPER-ZINC BASE ALLOYS (BRASS)",
+        "hsn_code": "74112100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER TUBES AND PIPES - OF COPPER ALLOYS : OF COPPER-NICKEL BASE ALLOYS (CUPRO\nNICKEL) OR COPPER-NICKEL-ZINC BASE ALLOYS (NICKEL SILVER)",
+        "hsn_code": "74112200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER TUBES AND PIPES - OF COPPER ALLOYS : OTHER",
+        "hsn_code": "74112900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER TUBES AND PIPES",
+        "hsn_code": "7411",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER FOIL (WHETHER OR NOT PRINTED OR BACKED WITH PAPER, PAPERBOARD, PLASTICS OR\nSIMILAR BACKING MATERIALS) OF A THICKNESS (EXCLUDING ANY BACKING) NOT EXCEEDING\n0.15 MM - NOT BACKED : OF REFINED COPPER",
+        "hsn_code": "74101100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER FOIL (WHETHER OR NOT PRINTED OR BACKED WITH PAPER, PAPERBOARD, PLASTICS OR\nSIMILAR BACKING MATERIALS) OF A THICKNESS (EXCLUDING ANY BACKING) NOT EXCEEDING\n0.15 MM - NOT BACKED : OF COPPER ALLOYS",
+        "hsn_code": "74101200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER FOIL (WHETHER OR NOT PRINTED OR BACKED WITH PAPER, PAPERBOARD, PLASTICS OR\nSIMILAR BACKING MATERIALS) OF A THICKNESS (EXCLUDING ANY BACKING) NOT EXCEEDING\n0.15 MM - BACKED : OF REFINED COPPER",
+        "hsn_code": "74102100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER FOIL (WHETHER OR NOT PRINTED OR BACKED WITH PAPER, PAPERBOARD, PLASTICS OR\nSIMILAR BACKING MATERIALS) OF A THICKNESS (EXCLUDING ANY BACKING) NOT EXCEEDING\n0.15 MM - BACKED : OF COPPER ALLOYS",
+        "hsn_code": "74102200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER FOIL (WHETHER OR NOT PRINTED OR BACKED WITH PAPER, PAPERBOARD, PLASTICS OR SIMILAR BACKING MATERIALS) OF A THICKNESS (EXCLUDING ANY BACKING) NOT EXCEEDING 0.15 MM",
+        "hsn_code": "7410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.15 MM - OF REFINED COPPER\nIN COILS",
+        "hsn_code": "74091100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.15 MM - OF REFINED COPPER\nOTHER",
+        "hsn_code": "74091900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.15 MM - OF COPPER-ZINC\nBASE ALLOYS (BRASS) : IN COILS",
+        "hsn_code": "74092100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.15 MM - OF COPPER-ZINC\nBASE ALLOYS (BRASS) : OTHER",
+        "hsn_code": "74092900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.15 MM - OF COPPER-TIN\nBASE ALLOYS (BRONZE) : IN COILS",
+        "hsn_code": "74093100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.15 MM - OF COPPER-TIN\nBASE ALLOYS (BRONZE) : OTHER",
+        "hsn_code": "74093900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.15 MM - OF COPPER-NICKEL\nBASE ALLOYS (CUPRO-NICKEL) OR COPPER-NICKEL-ZINC BASE ALLOYS (NICKEL SILVER)",
+        "hsn_code": "74094000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.15 MM - OF OTHER COPPER\nALLOYS",
+        "hsn_code": "74099000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.15 MM",
+        "hsn_code": "7409",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WIRE - OF REFINED COPPER : OF WHICH THE MAXIMUM CROSS-SECTIONAL DIMENSION\nEXCEEDS 6 MM : COPPER WELD WIRE",
+        "hsn_code": "74081110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WIRE - OF REFINED COPPER : OF WHICH THE MAXIMUM CROSS-SECTIONAL DIMENSION\nEXCEEDS 6 MM : OTHER",
+        "hsn_code": "74081190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WIRE - OF REFINED COPPER : OTHER : COPPER WELD WIRE",
+        "hsn_code": "74081910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WIRE - OF REFINED COPPER : OTHER : WELDING WIRE",
+        "hsn_code": "74081920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WIRE - OF REFINED COPPER : OTHER : OTHER",
+        "hsn_code": "74081990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WIRE - OF COPPER ALLOYS : OF COPPER-ZINC BASE ALLOYS (BRASS) : OF WHICH THE\nMAXIMUM CROSS-SECTIONAL DIMENSION EXCEEDS 6 MM",
+        "hsn_code": "74082110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WIRE - OF COPPER ALLOYS : OF COPPER-ZINC BASE ALLOYS (BRASS) : OTHER",
+        "hsn_code": "74082190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WIRE - OF COPPER ALLOYS : OF COPPER-NICKEL BASE ALLOYS (CUPRO-NICKEL) OR\nCOPPER-NICKEL-ZINC BASE ALLOYS (NICKEL SILVER) : SILVER PLATED FLATTENED WIRE OF\nCOPPER (LAMETTA)",
+        "hsn_code": "74082210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WIRE - OF COPPER ALLOYS : OF COPPER-NICKEL BASE ALLOYS (CUPRO-NICKEL) OR\nCOPPER-NICKEL-ZINC BASE ALLOYS (NICKEL SILVER) : OTHER OF WHICH THE MAXIMUM CROSS\nSECTIONAL DIMENSION EXCEEDS 6 MM",
+        "hsn_code": "74082220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WIRE - OF COPPER ALLOYS : OF COPPER-NICKEL BASE ALLOYS (CUPRO-NICKEL) OR\nCOPPER-NICKEL-ZINC BASE ALLOYS (NICKEL SILVER) : OTHER",
+        "hsn_code": "74082290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WIRE - OF COPPER ALLOYS : OTHER : WIRE OF BRONZE AND SIMILAR ALLOYS",
+        "hsn_code": "74082910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WIRE - OF COPPER ALLOYS : OTHER : OTHER",
+        "hsn_code": "74082990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WIRE",
+        "hsn_code": "7408",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER BARS, RODS AND PROFILES - OF REFINED COPPER :ELECTROLYTIC COPPER RODS OR\nBLACK COPPER RODS",
+        "hsn_code": "74071010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER BARS, RODS AND PROFILES - OF REFINED COPPER :OTHER COPPER RODS",
+        "hsn_code": "74071020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER BARS, RODS AND PROFILES - OF REFINED COPPER : COPPER BARS (EXCLUDING HOLLOW\nBARS)",
+        "hsn_code": "74071030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER BARS, RODS AND PROFILES - OF REFINED COPPER :HOLLOW BARS OF COPPER",
+        "hsn_code": "74071040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER BARS, RODS AND PROFILES - OF REFINED COPPER : PROFILES : HOLLOW PROFILES",
+        "hsn_code": "74071051",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER BARS, RODS AND PROFILES - OF REFINED COPPER : PROFILES : OTHER",
+        "hsn_code": "74071059",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER BARS, RODS AND PROFILES - OF REFINED COPPER :OTHER",
+        "hsn_code": "74071090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER BARS, RODS AND PROFILES - OF COPPER ALLOYS : OF COPPER-ZINC BASE ALLOYS\n(BRASS) : BARS",
+        "hsn_code": "74072110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER BARS, RODS AND PROFILES - OF COPPER ALLOYS : OF COPPER-ZINC BASE ALLOYS\n(BRASS) : RODS",
+        "hsn_code": "74072120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER BARS, RODS AND PROFILES - OF COPPER ALLOYS : OF COPPER-ZINC BASE ALLOYS\n(BRASS) : HOLLOW BARS",
+        "hsn_code": "74072130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER BARS, RODS AND PROFILES - OF COPPER ALLOYS : OF COPPER-ZINC BASE ALLOYS\n(BRASS) : OTHER",
+        "hsn_code": "74072190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER BARS, RODS AND PROFILES - OF COPPER ALLOYS : OF COPPER-NICKEL BASE ALLOYS\n(CUPRO-NICKEL) OR COPPER-NICKEL-ZINC BASE ALLOYS (NICKEL SILVER) : HOLLOW BARS",
+        "hsn_code": "74072210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER BARS, RODS AND PROFILES - OF COPPER ALLOYS : OF COPPER-NICKEL BASE ALLOYS\n(CUPRO-NICKEL) OR COPPER-NICKEL-ZINC BASE ALLOYS (NICKEL SILVER) : BARS AND RODS",
+        "hsn_code": "74072220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER BARS, RODS AND PROFILES - OF COPPER ALLOYS : OTHER : RODS OF BRONZE AND\nSIMILAR ALLOYS",
+        "hsn_code": "74072910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER BARS, RODS AND PROFILES - OF COPPER ALLOYS : OTHER : PROFILES : HOLLOW",
+        "hsn_code": "74072921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER BARS, RODS AND PROFILES - OF COPPER ALLOYS : OTHER : PROFILES : OTHER",
+        "hsn_code": "74072929",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER BARS, RODS AND PROFILES - OF COPPER ALLOYS : OTHER : OTHER",
+        "hsn_code": "74072990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER BARS, RODS AND PROFILES",
+        "hsn_code": "7407",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER POWDERS AND FLAKES - POWDERS OF NON-LAMELLAR STRUCTURE",
+        "hsn_code": "74061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER POWDERS AND FLAKES - POWDERS OF LAMELLAR STRUCTURE; FLAKES",
+        "hsn_code": "74062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER POWDERS AND FLAKES",
+        "hsn_code": "7406",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MASTER ALLOYS OF COPPER",
+        "hsn_code": "74050000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Master alloys of copper",
+        "hsn_code": "7405",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WASTE AND SCRAP - COPPER WASTE AND SCRAP: OF COPPER : EMPTY OR DISCHARGED\nCARTRIDGES OF ALL BORES AND SIZES",
+        "hsn_code": "74040011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WASTE AND SCRAP - COPPER WASTE AND SCRAP: OF COPPER :COPPER SCRAP, NAMELY\nTHE FOLLOWING : COPPER WIRE SCRAP COVERED BY ISRI CODE WORDS BARLEY, BERRY AND\nBIRCH; HEAVY COPPER SCRAP COVERED BY ISRI CODE WORD CANDY; UNALLOYED COPPER SCRAP\nCOVERED BY ISRI CODE WORD CLIFF; COPPER WIRE NODULES SCRAP COVERED BY ISRI CODE\nWORDS CLOVE, COBRA AND COCOA; LIGHT COPPER SCRAP COVERED BY ISRI CODE WORD\nDREAM; MUNTZ METAL TUBES COVERED BY ISRI CODE WORD PALMS",
+        "hsn_code": "74040012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WASTE AND SCRAP - COPPER WASTE AND SCRAP: OF COPPER :OTHER",
+        "hsn_code": "74040019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WASTE AND SCRAP - COPPER WASTE AND SCRAP: OF COPPER ALLOYS : EMPTY OR\nDISCHARGED CARTRIDGES OF ALL BORES AND SIZES",
+        "hsn_code": "74040021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WASTE AND SCRAP - COPPER WASTE AND SCRAP: OF COPPER ALLOYS : BRASS SCRAP,\nNAMELY THE FOLLOWING : REFINERY BRASS SCRAP COVERED BY ISRI CODE WORD DRINK;\nCOMPOSITION OF RED BRASS SCRAP COVERED BY ISRI CODE WORD EBONY; RED BRASS\nCOMPOSITION TURNINGS SCRAP COVERED BY ISRI CODE WORD ENERV; GENUINE BABBIT-LINED\nBRASS BUSHINGS SCRAP COVERED BY ISRI CODE WORD ELDER; MACHINERY OR HARD BRASS\nSOLIDS SCRAP COVERED BY ISRI CODE WORD ENGEL; MACHINERY OR HAND BRASS SOLIDS\nSCRAP COVERED BY ISRI CODE WORD ERIN; COCKS AND FAUCETS SCRAP COVERED BY ISRI CODE\nWORD GRAPE; YELLOW BRASS SCRAP COVERED BY ISRI CODE WORD HONEY; YELLOW BRASS\nCASTINGS COVERED BY ISRI CODE WORD IVORY; NEW BRASS CLIPPINGS COVERED BY ISRI CODE\nWORD LABEL; YELLOW BRASS PRIMER COVERED BY ISRI CODE WORD LARK; BRASS PIPE\nCOVERED BY ISRI CODE WORD MELON; YELLOW BRASS ROD TURNINGS COVERED BY ISRI CODE\nWORD NIGHT; NEW YELLOW BRASS ROD ENDS COVERED BY ISRI CODE WORD NOBLE; YELLOW\nBRASS TURNINGS COVERED BY ISRI CODE WORD NORMAD; MIXED UNSWEATED AUTO\nRADIATORS COVERED BY ISRI CODE WORD OCEAN; ADMIRALTY BRASS CONDENSER TUBES\nCOVERED BY ISRI CODE WORD PALES; ALUMINIUM BRASS CONDENSER TUBES COVERED BY ISRI\nCODE WORD PALLU; MANGANESE BRONZE SOLIDS COVERED BY ISRI CODE WORD PARCH",
+        "hsn_code": "74040022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WASTE AND SCRAP - COPPER WASTE AND SCRAP--- OF COPPER ALLOYS ----NICKEL\nSILVER SCRAP NAMELY THE FOLLOWING: MIXED NEW NICKEL SILVER CLIPPINGS COVERED BY ISRI\nCODE WORD 'MAIZE', NEW NICKEL SILVER CLIPPINGS COVERED BY ISRI CODE WORD 'MAJOR',\nNEW SEGREGATED NICKEL SILVER CLIPPINGS COVERED BY ISRI CODE WORD 'MALAR', OLD\nNICKEL SILVER COVERED BY ISRI CODE WORD 'MALIC', NICKEL SILVER CASTINGS COVERED BY ISRI\nCODE WORD 'NAGGY', NICKEL SILVER TURNINGS COVERED BY ISRI CODE WORD 'NIECE'",
+        "hsn_code": "74040023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WASTE AND SCRAP - COPPER WASTE AND SCRAP: OF COPPER ALLOYS : OTHER",
+        "hsn_code": "74040029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER WASTE AND SCRAP",
+        "hsn_code": "7404",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFINED COPPER AND COPPER ALLOYS, UNWROUGHT - REFINED COPPER : CATHODES AND\nSECTIONS OF CATHODES",
+        "hsn_code": "74031100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFINED COPPER AND COPPER ALLOYS, UNWROUGHT - REFINED COPPER : WIRE-BARS",
+        "hsn_code": "74031200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFINED COPPER AND COPPER ALLOYS, UNWROUGHT - REFINED COPPER : BILLETS",
+        "hsn_code": "74031300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFINED COPPER AND COPPER ALLOYS, UNWROUGHT - REFINED COPPER : OTHER",
+        "hsn_code": "74031900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFINED COPPER AND COPPER ALLOYS, UNWROUGHT - COPPER ALLOYS : COPPER-ZINC BASE\nALLOYS (BRASS)",
+        "hsn_code": "74032100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFINED COPPER AND COPPER ALLOYS, UNWROUGHT - COPPER ALLOYS : COPPER-TIN BASE\nALLOYS (BRONZE) : PHOSPHOR BRONZE",
+        "hsn_code": "74032210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFINED COPPER AND COPPER ALLOYS, UNWROUGHT - COPPER ALLOYS : COPPER-TIN BASE\nALLOYS (BRONZE) : OTHER",
+        "hsn_code": "74032290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFINED COPPER AND COPPER ALLOYS, UNWROUGHT - COPPER ALLOYS : COPPER-NICKEL BASE\nALLOYS( CUPRO-NICKEL) OR COPPER-NICKEL-ZINC BASE ALLOYS (NICKEL SILVER) : COPPER\nNICKEL BASE ALLOYS (CUPRO-NICKEL)",
+        "hsn_code": "74032310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFINED COPPER AND COPPER ALLOYS, UNWROUGHT - COPPER ALLOYS : COPPER-NICKEL BASE\nALLOYS( CUPRO-NICKEL) OR COPPER-NICKEL-ZINC BASE ALLOYS (NICKEL SILVER) : COPPER\nNICKEL-ZINC BASE ALLOYS(NICKEL SILVER)",
+        "hsn_code": "74032320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFINED COPPER AND COPPER ALLOYS, UNWROUGHT - COPPER ALLOYS : OTHER COPPER\nALLOYS (OTHER THAN MASTER ALLOYS OF HEADING 7405)",
+        "hsn_code": "74032900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFINED COPPER AND COPPER ALLOYS, UNWROUGHT",
+        "hsn_code": "7403",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNREFINED COPPER; COPPER ANODES FOR ELECTROLYTIC REFINING - UNREFINED COPPER;\nCOPPER ANODES FOR ELECTROLYTIC REFINING : BLISTER COPPER",
+        "hsn_code": "74020010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNREFINED COPPER; COPPER ANODES FOR ELECTROLYTIC REFINING - UNREFINED COPPER;\nCOPPER ANODES FOR ELECTROLYTIC REFINING : OTHER",
+        "hsn_code": "74020090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNREFINED COPPER; COPPER ANODES FOR ELECTROLYTIC REFINING",
+        "hsn_code": "7402",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER MATTES; CEMENT COPPER (PRECIPITATED COPPER) - COPPER MATTES",
+        "hsn_code": "74010010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER MATTES; CEMENT COPPER (PRECIPITATED COPPER) - CEMENT COPPER (PRECIPITATED\nCOPPER)",
+        "hsn_code": "74010090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER MATTES; CEMENT COPPER (PRECIPITATED COPPER) - COPPER MATTES",
+        "hsn_code": "74011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER MATTES; CEMENT COPPER (PRECIPITATED COPPER) - CEMENT COPPER (PRECIPITATED\nCOPPER)",
+        "hsn_code": "74012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER MATTES; CEMENT COPPER (PRECIPITATED COPPER)",
+        "hsn_code": "7401",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CAST ARTICLES OF IRON OR STEEL - OF NON-MALLEABLE CAST IRON",
+        "hsn_code": "73251000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CAST ARTICLES OF IRON OR STEEL - OTHER : GRINDING BALLS AND SIMILAR ARTICLES FOR\nMILLS",
+        "hsn_code": "73259100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CAST ARTICLES OF IRON OR STEEL - OTHER : OTHER : OF IRON",
+        "hsn_code": "73259910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CAST ARTICLES OF IRON OR STEEL - OTHER : OTHER : OF ALLOY STEEL",
+        "hsn_code": "73259920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CAST ARTICLES OF IRON OR STEEL - OTHER : OTHER : OF STAINLESS STEEL",
+        "hsn_code": "73259930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CAST ARTICLES OF IRON OR STEEL - OTHER : OTHER : OTHER : RUDDERS FOR SHIPS OR\nBOATS",
+        "hsn_code": "73259991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CAST ARTICLES OF IRON OR STEEL - OTHER : OTHER : OTHER : DRAIN COVERS",
+        "hsn_code": "73259992",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CAST ARTICLES OF IRON OR STEEL - OTHER : OTHER : OTHER : PLATES AND FRAMES FOR\nSEWAGE WATER OR SIMILAR SYSTEM",
+        "hsn_code": "73259993",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CAST ARTICLES OF IRON OR STEEL - OTHER : OTHER : OTHER : OTHER",
+        "hsn_code": "73259999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CAST ARTICLES OF IRON OR STEEL",
+        "hsn_code": "7325",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SANITARY WARE AND PARTS THEREOF, OF IRON OR STEEL - SINKS AND WASH BASINS, OF\nSTAINLESS STEEL",
+        "hsn_code": "73241000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SANITARY WARE AND PARTS THEREOF, OF IRON OR STEEL - BATHS : OF CAST IRON, WHETHER OR\nNOT ENAMELLED",
+        "hsn_code": "73242100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SANITARY WARE AND PARTS THEREOF, OF IRON OR STEEL - BATHS : OTHER",
+        "hsn_code": "73242900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SANITARY WARE AND PARTS THEREOF, OF IRON OR STEEL - OTHER, INCLUDING PARTS",
+        "hsn_code": "73249000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SANITARY WARE AND PARTS THEREOF, OF IRON OR STEEL",
+        "hsn_code": "7324",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF IRON OR STEEL;\nIRON OR STEEL WOOL; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE, OF IRON OR STEEL IRON OR STEEL WOOL; POT SCOURERS AND SCOURING OR POLISHING\nPADS, GLOVES AND THE LIKE",
+        "hsn_code": "73231000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF IRON OR STEEL;\nIRON OR STEEL WOOL; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE, OF IRON OR STEEL - OTHER : OF CAST IRON, NOT ENAMELLED : PANS",
+        "hsn_code": "73239110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF IRON OR STEEL;\nIRON OR STEEL WOOL; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE, OF IRON OR STEEL - OTHER : OF CAST IRON, NOT ENAMELLED : OTHER",
+        "hsn_code": "73239190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF IRON OR STEEL;\nIRON OR STEEL WOOL; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE, OF IRON OR STEEL - OTHER : OF CAST IRON, ENAMELLED",
+        "hsn_code": "73239200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF IRON OR STEEL;\nIRON OR STEEL WOOL; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE, OF IRON OR STEEL - OTHER : OF STAINLESS STEEL : PRESSURE COOKERS",
+        "hsn_code": "73239310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF IRON OR STEEL;\nIRON OR STEEL WOOL; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE, OF IRON OR STEEL - OTHER : OF STAINLESS STEEL : OTHER",
+        "hsn_code": "73239390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF IRON OR STEEL;\nIRON OR STEEL WOOL; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE, OF IRON OR STEEL - OTHER : OF IRON (OTHER THAN CAST IRON) OR STEEL, ENAMELLED :\nUTENSILS",
+        "hsn_code": "73239420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF IRON OR STEEL;\nIRON OR STEEL WOOL; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE, OF IRON OR STEEL - OTHER : OF IRON (OTHER THAN CAST IRON) OR STEEL, ENAMELLED :\nOTHER",
+        "hsn_code": "73239490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF IRON OR STEEL;\nIRON OR STEEL WOOL; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE, OF IRON OR STEEL - OTHER : OTHER : UTENSILS OF GALVANISED IRON",
+        "hsn_code": "73239910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF IRON OR STEEL;\nIRON OR STEEL WOOL; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE, OF IRON OR STEEL - OTHER : OTHER : OTHER UTENSILS",
+        "hsn_code": "73239920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF IRON OR STEEL;\nIRON OR STEEL WOOL; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE\nLIKE, OF IRON OR STEEL - OTHER : OTHER : OTHER",
+        "hsn_code": "73239990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLE, KITCHEN OR OTHER HOUSEHOLD ARTICLES AND PARTS THEREOF, OF IRON OR STEEL; IRON OR STEEL WOOL; POT SCOURERS AND SCOURING OR POLISHING PADS, GLOVES AND THE LIKE, OF IRON OR STEEL",
+        "hsn_code": "7323",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RADIATORS FOR CENTRAL HEATING, NOT ELECTRICALLY HEATED, AND PARTS THEREOF, OF IRON\nOR STEEL; AIR HEATERS AND HOT AIR DISTRIBUTORS (INCLUDING DISTRIBUTORS WHICH CAN\nALSO DISTRIBUTE FRESH OR CONDITIONED AIR), NOT ELECTRICALLY HEATED, INCORPORATING A\nMOTOR-DRIVEN FAN OR BLOWER, AND PARTS THEREOF, OF IRON OR STEEL - RADIATORS AND\nPARTS THEREOF : OF CAST IRON",
+        "hsn_code": "73221100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RADIATORS FOR CENTRAL HEATING, NOT ELECTRICALLY HEATED, AND PARTS THEREOF, OF IRON\nOR STEEL; AIR HEATERS AND HOT AIR DISTRIBUTORS (INCLUDING DISTRIBUTORS WHICH CAN\nALSO DISTRIBUTE FRESH OR CONDITIONED AIR), NOT ELECTRICALLY HEATED, INCORPORATING A\nMOTOR-DRIVEN FAN OR BLOWER, AND PARTS THEREOF, OF IRON OR STEEL - RADIATORS AND\nPARTS THEREOF : OTHER",
+        "hsn_code": "73221900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RADIATORS FOR CENTRAL HEATING, NOT ELECTRICALLY HEATED, AND PARTS THEREOF, OF IRON\nOR STEEL; AIR HEATERS AND HOT AIR DISTRIBUTORS (INCLUDING DISTRIBUTORS WHICH CAN\nALSO DISTRIBUTE FRESH OR CONDITIONED AIR), NOT ELECTRICALLY HEATED, INCORPORATING A\nMOTOR-DRIVEN FAN OR BLOWER, AND PARTS THEREOF, OF IRON OR STEEL - OTHER : AIR\nHEATERS AND HOT AIR DISTRIBUTORS",
+        "hsn_code": "73229010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RADIATORS FOR CENTRAL HEATING, NOT ELECTRICALLY HEATED, AND PARTS THEREOF, OF IRON\nOR STEEL; AIR HEATERS AND HOT AIR DISTRIBUTORS (INCLUDING DISTRIBUTORS WHICH CAN\nALSO DISTRIBUTE FRESH OR CONDITIONED AIR), NOT ELECTRICALLY HEATED, INCORPORATING A\nMOTOR-DRIVEN FAN OR BLOWER, AND PARTS THEREOF, OF IRON OR STEEL - OTHER : PARTS OF\nAIR HEATERS AND HOT AIR DISTRIBUTORS",
+        "hsn_code": "73229090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Radiators for central heating, not electrically heated and parts thereof, of iron or steel; air heaters, hot air distributors not electrically heated, with motor fan or blower",
+        "hsn_code": "7322",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOVES, RANGES, GRATES, COOKERS (INCLUDING THOSE WITH SUBSIDIARY BOILERS FOR\nCENTRAL HEATING), BARBECUES, BRAZIERS, GAS-RINGS, PLATE WARMERS AND SIMILAR NON\nELECTRIC DOMESTIC APPLIANCES, AND PARTS THEREOF, OF IRON OR STEEL - COOKING\nAPPLIANCES AND PLATE WARMERS: FOR GAS FUEL OR FOR BOTH GAS AND OTHER FUELS :\nCOOKERS AND KITCHEN STOVES",
+        "hsn_code": "73211110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOVES, RANGES, GRATES, COOKERS (INCLUDING THOSE WITH SUBSIDIARY BOILERS FOR\nCENTRAL HEATING), BARBECUES, BRAZIERS, GAS-RINGS, PLATE WARMERS AND SIMILAR NON\nELECTRIC DOMESTIC APPLIANCES, AND PARTS THEREOF, OF IRON OR STEEL - COOKING\nAPPLIANCES AND PLATE WARMERS: FOR GAS FUEL OR FOR BOTH GAS AND OTHER FUELS :\nOTHER STOVES",
+        "hsn_code": "73211120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOVES, RANGES, GRATES, COOKERS (INCLUDING THOSE WITH SUBSIDIARY BOILERS FOR\nCENTRAL HEATING), BARBECUES, BRAZIERS, GAS-RINGS, PLATE WARMERS AND SIMILAR NON\nELECTRIC DOMESTIC APPLIANCES, AND PARTS THEREOF, OF IRON OR STEEL - COOKING\nAPPLIANCES AND PLATE WARMERS: FOR GAS FUEL OR FOR BOTH GAS AND OTHER FUELS :\nOTHER",
+        "hsn_code": "73211190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOVES, RANGES, GRATES, COOKERS (INCLUDING THOSE WITH SUBSIDIARY BOILERS FOR\nCENTRAL HEATING), BARBECUES, BRAZIERS, GAS-RINGS, PLATE WARMERS AND SIMILAR NON\nELECTRIC DOMESTIC APPLIANCES, AND PARTS THEREOF, OF IRON OR STEEL - COOKING\nAPPLIANCES AND PLATE WARMERS: FOR LIQUID FUEL: COOKERS AND KITCHEN STOVES",
+        "hsn_code": "73211210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOVES, RANGES, GRATES, COOKERS (INCLUDING THOSE WITH SUBSIDIARY BOILERS FOR\nCENTRAL HEATING), BARBECUES, BRAZIERS, GAS-RINGS, PLATE WARMERS AND SIMILAR NON\nELECTRIC DOMESTIC APPLIANCES, AND PARTS THEREOF, OF IRON OR STEEL - COOKING\nAPPLIANCES AND PLATE WARMERS: FOR LIQUID FUEL: OTHER STOVES",
+        "hsn_code": "73211220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOVES, RANGES, GRATES, COOKERS (INCLUDING THOSE WITH SUBSIDIARY BOILERS FOR\nCENTRAL HEATING), BARBECUES, BRAZIERS, GAS-RINGS, PLATE WARMERS AND SIMILAR NON\nELECTRIC DOMESTIC APPLIANCES, AND PARTS THEREOF, OF IRON OR STEEL - COOKING\nAPPLIANCES AND PLATE WARMERS: FOR LIQUID FUEL: OTHER",
+        "hsn_code": "73211290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOVES, RANGES, GRATES, COOKERS (INCLUDING THOSE WITH SUBSIDIARY BOILERS FOR\nCENTRAL HEATING), BARBECUES, BRAZIERS, GAS-RINGS, PLATE WARMERS AND SIMILAR NON\nELECTRIC DOMESTIC APPLIANCES, AND PARTS THEREOF, OF IRON OR STEEL - COOKING\nAPPLIANCES AND PLATE WARMERS: FOR SOLID FUEL: COOKERS AND KITCHEN STOVES",
+        "hsn_code": "73211310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOVES, RANGES, GRATES, COOKERS (INCLUDING THOSE WITH SUBSIDIARY BOILERS FOR\nCENTRAL HEATING), BARBECUES, BRAZIERS, GAS-RINGS, PLATE WARMERS AND SIMILAR NON\nELECTRIC DOMESTIC APPLIANCES, AND PARTS THEREOF, OF IRON OR STEEL - COOKING\nAPPLIANCES AND PLATE WARMERS: FOR SOLID FUEL: OTHER STOVES",
+        "hsn_code": "73211320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOVES, RANGES, GRATES, COOKERS (INCLUDING THOSE WITH SUBSIDIARY BOILERS FOR\nCENTRAL HEATING), BARBECUES, BRAZIERS, GAS-RINGS, PLATE WARMERS AND SIMILAR NON\nELECTRIC DOMESTIC APPLIANCES, AND PARTS THEREOF, OF IRON OR STEEL - COOKING\nAPPLIANCES AND PLATE WARMERS: FOR SOLID FUEL: OTHER",
+        "hsn_code": "73211390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOVES, RANGES, GRATES, COOKERS (INCLUDING THOSE WITH SUBSIDIARY BOILERS FOR\nCENTRAL HEATING), BARBECUES, BRAZIERS, GAS-RINGS, PLATE WARMERS AND SIMILAR NON\nELECTRIC DOMESTIC APPLIANCES, AND PARTS THEREOF, OF IRON OR STEEL - COOKING\nAPPLIANCES AND PLATE WARMERS: -- OTHER, INCLUDING APPLIANCES FOR SOLID FUEL ---\nCOOKERS AND KITCHEN STOVES",
+        "hsn_code": "73211910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOVES, RANGES, GRATES, COOKERS (INCLUDING THOSE WITH SUBSIDIARY BOILERS FOR\nCENTRAL HEATING), BARBECUES, BRAZIERS, GAS-RINGS, PLATE WARMERS AND SIMILAR NON\nELECTRIC DOMESTIC APPLIANCES, AND PARTS THEREOF, OF IRON OR STEEL - COOKING\nAPPLIANCES AND PLATE WARMERS: -- OTHER, INCLUDING APPLIANCES FOR SOLID FUEL ---\nOTHER STOVES AND APPLIANCES",
+        "hsn_code": "73211990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOVES, RANGES, GRATES, COOKERS (INCLUDING THOSE WITH SUBSIDIARY BOILERS FOR\nCENTRAL HEATING), BARBECUES, BRAZIERS, GAS-RINGS, PLATE WARMERS AND SIMILAR NON\nELECTRIC DOMESTIC APPLIANCES, AND PARTS THEREOF, OF IRON OR STEEL - OTHER\nAPPLIANCES: FOR GAS FUEL OR FOR BOTH GAS AND OTHER FUELS",
+        "hsn_code": "73218100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOVES, RANGES, GRATES, COOKERS (INCLUDING THOSE WITH SUBSIDIARY BOILERS FOR\nCENTRAL HEATING), BARBECUES, BRAZIERS, GAS-RINGS, PLATE WARMERS AND SIMILAR NON\nELECTRIC DOMESTIC APPLIANCES, AND PARTS THEREOF, OF IRON OR STEEL - OTHER\nAPPLIANCES: FOR LIQUID FUEL",
+        "hsn_code": "73218200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOVES, RANGES, GRATES, COOKERS (INCLUDING THOSE WITH SUBSIDIARY BOILERS FOR\nCENTRAL HEATING), BARBECUES, BRAZIERS, GAS-RINGS, PLATE WARMERS AND SIMILAR NON\nELECTRIC DOMESTIC APPLIANCES, AND PARTS THEREOF, OF IRON OR STEEL - OTHER\nAPPLIANCES: FOR SOLID FUEL : CLAY TANDOOR (OVEN WITH IRON OR STEEL BODY AND\nEARTHEN GRATES)",
+        "hsn_code": "73218310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOVES, RANGES, GRATES, COOKERS (INCLUDING THOSE WITH SUBSIDIARY BOILERS FOR\nCENTRAL HEATING), BARBECUES, BRAZIERS, GAS-RINGS, PLATE WARMERS AND SIMILAR NON\nELECTRIC DOMESTIC APPLIANCES, AND PARTS THEREOF, OF IRON OR STEEL - OTHER\nAPPLIANCES: FOR SOLID FUEL : OTHER",
+        "hsn_code": "73218390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOVES, RANGES, GRATES, COOKERS (INCLUDING THOSE WITH SUBSIDIARY BOILERS FOR\nCENTRAL HEATING), BARBECUES, BRAZIERS, GAS-RINGS, PLATE WARMERS AND SIMILAR NON\nELECTRIC DOMESTIC APPLIANCES, AND PARTS THEREOF, OF IRON OR STEEL - COOKING\nAPPLIANCES AND PLATE WARMERS: -- OTHER, INCLUDING APPLIANCES FOR SOLID FUEL --- CLAY\nTANDOOR (OVEN WITH IRON OR STEEL BODY AND EARTHEN GRATES)",
+        "hsn_code": "73218910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOVES, RANGES, GRATES, COOKERS (INCLUDING THOSE WITH SUBSIDIARY BOILERS FOR\nCENTRAL HEATING), BARBECUES, BRAZIERS, GAS-RINGS, PLATE WARMERS AND SIMILAR NON\nELECTRIC DOMESTIC APPLIANCES, AND PARTS THEREOF, OF IRON OR STEEL - COOKING\nAPPLIANCES AND PLATE WARMERS: -- OTHER, INCLUDING APPLIANCES FOR SOLID FUEL ---\nOTHER",
+        "hsn_code": "73218990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOVES, RANGES, GRATES, COOKERS (INCLUDING THOSE WITH SUBSIDIARY BOILERS FOR\nCENTRAL HEATING), BARBECUES, BRAZIERS, GAS-RINGS, PLATE WARMERS AND SIMILAR NON\nELECTRIC DOMESTIC APPLIANCES, AND PARTS THEREOF, OF IRON OR STEEL - PARTS",
+        "hsn_code": "73219000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STOVES, RANGES, GRATES, COOKERS (INCLUDING THOSE WITH SUBSIDIARY BOILERS FOR CENTRAL HEATING), BARBECUES, BRAZIERS, GAS-RINGS, PLATE WARMERS AND SIMILAR NON-ELECTRIC DOMESTIC APPLIANCES, AND PARTS THEREOF, OF IRON OR STEEL",
+        "hsn_code": "7321",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SPRINGS AND LEAVES FOR SPRINGS, OF IRON OR STEEL - LEAF-SPRINGS AND LEAVES THEREFOR :\nLEAF-SPRINGS : FOR MOTOR VEHICLES",
+        "hsn_code": "73201011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SPRINGS AND LEAVES FOR SPRINGS, OF IRON OR STEEL - LEAF-SPRINGS AND LEAVES THEREFOR :\nLEAF-SPRINGS : FOR RAILWAYS AND TRAMWAYS",
+        "hsn_code": "73201012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SPRINGS AND LEAVES FOR SPRINGS, OF IRON OR STEEL - LEAF-SPRINGS AND LEAVES THEREFOR :\nLEAF-SPRINGS : OTHER",
+        "hsn_code": "73201019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SPRINGS AND LEAVES FOR SPRINGS, OF IRON OR STEEL - LEAF-SPRINGS AND LEAVES THEREFOR: --\nLEAF SPRINGS --- LEAVES FOR SPRINGS",
+        "hsn_code": "73201020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SPRINGS AND LEAVES FOR SPRINGS, OF IRON OR STEEL - HELICAL SPRINGS",
+        "hsn_code": "73202000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SPRINGS AND LEAVES FOR SPRINGS, OF IRON OR STEEL - OTHER : COIL SPRING FOR RAILWAYS,\nTRAMWAYS",
+        "hsn_code": "73209010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SPRINGS AND LEAVES FOR SPRINGS, OF IRON OR STEEL - OTHER : SPRING PINS",
+        "hsn_code": "73209020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SPRINGS AND LEAVES FOR SPRINGS, OF IRON OR STEEL - OTHER : OTHER",
+        "hsn_code": "73209090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SPRINGS AND LEAVES FOR SPRINGS, OF IRON OR STEEL",
+        "hsn_code": "7320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEWING NEEDLES, KNITTING NEEDLES, BODKINS, CROCHET HOOKS, EMBROIDERY STILETTOS\nAND SIMILAR ARTICLES, FOR USE IN THE HAND, OF IRON OR STEEL; SAFETY PINS AND OTHER\nPINS, OF IRON OR STEEL, NOT ELSEWHERE SPECIFIED OR INCLUDED - SEWING, DARNING OR\nEMBROIDERY NEEDLES : HARNESS NEEDLES",
+        "hsn_code": "73191010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEWING NEEDLES, KNITTING NEEDLES, BODKINS, CROCHET HOOKS, EMBROIDERY STILETTOS\nAND SIMILAR ARTICLES, FOR USE IN THE HAND, OF IRON OR STEEL; SAFETY PINS AND OTHER\nPINS, OF IRON OR STEEL, NOT ELSEWHERE SPECIFIED OR INCLUDED - SEWING, DARNING OR\nEMBROIDERY NEEDLES : OTHER SEWING NEEDLES",
+        "hsn_code": "73191020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEWING NEEDLES, KNITTING NEEDLES, BODKINS, CROCHET HOOKS, EMBROIDERY STILETTOS\nAND SIMILAR ARTICLES, FOR USE IN THE HAND, OF IRON OR STEEL; SAFETY PINS AND OTHER\nPINS, OF IRON OR STEEL, NOT ELSEWHERE SPECIFIED OR INCLUDED - SEWING, DARNING OR\nEMBROIDERY NEEDLES : OTHER",
+        "hsn_code": "73191090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEWING NEEDLES, KNITTING NEEDLES, BODKINS, CROCHET HOOKS, EMBROIDERY STILETTOS\nAND SIMILAR ARTICLES, FOR USE IN THE HAND, OF IRON OR STEEL; SAFETY PINS AND OTHER\nPINS, OF IRON OR STEEL, NOT ELSEWHERE SPECIFIED OR INCLUDED - SAFETY PINS AND OTHER\nPINS --- SAFETY PINS",
+        "hsn_code": "73194010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEWING NEEDLES, KNITTING NEEDLES, BODKINS, CROCHET HOOKS, EMBROIDERY STILETTOS\nAND SIMILAR ARTICLES, FOR USE IN THE HAND, OF IRON OR STEEL; SAFETY PINS AND OTHER\nPINS, OF IRON OR STEEL, NOT ELSEWHERE SPECIFIED OR INCLUDED - SAFETY PINS AND OTHER\nPINS --- OTHER PINS",
+        "hsn_code": "73194090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEWING NEEDLES, KNITTING NEEDLES, BODKINS, CROCHET HOOKS, EMBROIDERY STILETTOS\nAND SIMILAR ARTICLES, FOR USE IN THE HAND, OF IRON OR STEEL; SAFETY PINS AND OTHER\nPINS, OF IRON OR STEEL, NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER",
+        "hsn_code": "73199000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEWING NEEDLES, KNITTING NEEDLES, BODKINS, CROCHET HOOKS, EMBROIDERY STILETTOS AND SIMILAR ARTICLES, FOR USE IN THE HAND, OF IRON OR STEEL; SAFETY PINS AND OTHER PINS, OF IRON OR STEEL, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "7319",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCREWS, BOLTS, NUTS, COACH-SCREWS, SCREW HOOKS, RIVETS, COTTERS, COTTER-PINS,\nWASHERS (INCLUDING SPRING WASHERS) AND SIMILAR ARTICLES, OF IRON OR STEEL -\nTHREADED ARTICLES : COACH SCREWS : MACHINE SCREWS",
+        "hsn_code": "73181110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCREWS, BOLTS, NUTS, COACH-SCREWS, SCREW HOOKS, RIVETS, COTTERS, COTTER-PINS,\nWASHERS (INCLUDING SPRING WASHERS) AND SIMILAR ARTICLES, OF IRON OR STEEL -\nTHREADED ARTICLES : COACH SCREWS : OTHER",
+        "hsn_code": "73181190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCREWS, BOLTS, NUTS, COACH-SCREWS, SCREW HOOKS, RIVETS, COTTERS, COTTER-PINS,\nWASHERS (INCLUDING SPRING WASHERS) AND SIMILAR ARTICLES, OF IRON OR STEEL -\nTHREADED ARTICLES : OTHER WOOD SCREWS",
+        "hsn_code": "73181200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCREWS, BOLTS, NUTS, COACH-SCREWS, SCREW HOOKS, RIVETS, COTTERS, COTTER-PINS,\nWASHERS (INCLUDING SPRING WASHERS) AND SIMILAR ARTICLES, OF IRON OR STEEL -\nTHREADED ARTICLES : SCREW HOOKS AND SCREW RINGS",
+        "hsn_code": "73181300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCREWS, BOLTS, NUTS, COACH-SCREWS, SCREW HOOKS, RIVETS, COTTERS, COTTER-PINS,\nWASHERS (INCLUDING SPRING WASHERS) AND SIMILAR ARTICLES, OF IRON OR STEEL -\nTHREADED ARTICLES : SELF-TAPPING SCREWS",
+        "hsn_code": "73181400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCREWS, BOLTS, NUTS, COACH-SCREWS, SCREW HOOKS, RIVETS, COTTERS, COTTER-PINS,\nWASHERS (INCLUDING SPRING WASHERS) AND SIMILAR ARTICLES, OF IRON OR STEEL -\nTHREADED ARTICLES : OTHER SCREWS AND BOLTS, WHETHER OR NOT WITH THEIR NUTS OR\nWASHERS",
+        "hsn_code": "73181500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCREWS, BOLTS, NUTS, COACH-SCREWS, SCREW HOOKS, RIVETS, COTTERS, COTTER-PINS,\nWASHERS (INCLUDING SPRING WASHERS) AND SIMILAR ARTICLES, OF IRON OR STEEL -\nTHREADED ARTICLES : NUTS",
+        "hsn_code": "73181600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCREWS, BOLTS, NUTS, COACH-SCREWS, SCREW HOOKS, RIVETS, COTTERS, COTTER-PINS,\nWASHERS (INCLUDING SPRING WASHERS) AND SIMILAR ARTICLES, OF IRON OR STEEL -\nTHREADED ARTICLES : OTHER",
+        "hsn_code": "73181900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCREWS, BOLTS, NUTS, COACH-SCREWS, SCREW HOOKS, RIVETS, COTTERS, COTTER-PINS,\nWASHERS (INCLUDING SPRING WASHERS) AND SIMILAR ARTICLES, OF IRON OR STEEL - NON\nTHREADED ARTICLES : SPRING WASHERS AND OTHER LOCK WASHERS",
+        "hsn_code": "73182100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCREWS, BOLTS, NUTS, COACH-SCREWS, SCREW HOOKS, RIVETS, COTTERS, COTTER-PINS,\nWASHERS (INCLUDING SPRING WASHERS) AND SIMILAR ARTICLES, OF IRON OR STEEL - NON\nTHREADED ARTICLES : OTHER WASHERS",
+        "hsn_code": "73182200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCREWS, BOLTS, NUTS, COACH-SCREWS, SCREW HOOKS, RIVETS, COTTERS, COTTER-PINS,\nWASHERS (INCLUDING SPRING WASHERS) AND SIMILAR ARTICLES, OF IRON OR STEEL - NON\nTHREADED ARTICLES : RIVETS",
+        "hsn_code": "73182300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCREWS, BOLTS, NUTS, COACH-SCREWS, SCREW HOOKS, RIVETS, COTTERS, COTTER-PINS,\nWASHERS (INCLUDING SPRING WASHERS) AND SIMILAR ARTICLES, OF IRON OR STEEL - NON\nTHREADED ARTICLES : COTTERS AND COTTER-PINS",
+        "hsn_code": "73182400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCREWS, BOLTS, NUTS, COACH-SCREWS, SCREW HOOKS, RIVETS, COTTERS, COTTER-PINS,\nWASHERS (INCLUDING SPRING WASHERS) AND SIMILAR ARTICLES, OF IRON OR STEEL - NON\nTHREADED ARTICLES : OTHER : CIRCLIPS",
+        "hsn_code": "73182910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCREWS, BOLTS, NUTS, COACH-SCREWS, SCREW HOOKS, RIVETS, COTTERS, COTTER-PINS,\nWASHERS (INCLUDING SPRING WASHERS) AND SIMILAR ARTICLES, OF IRON OR STEEL - NON\nTHREADED ARTICLES : OTHER : OTHER",
+        "hsn_code": "73182990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SCREWS, BOLTS, NUTS, COACH-SCREWS, SCREW HOOKS, RIVETS, COTTERS, COTTER-PINS, WASHERS (INCLUDING SPRING WASHERS) AND SIMILAR ARTICLES, OF IRON OR STEEL",
+        "hsn_code": "7318",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NAILS, TACKS, DRAWING PINS, CORRUGATED NAILS, STAPLES (OTHER THAN THOSE OF HEADING\n8305) AND SIMILAR ARTICLES, OF IRON OR STEEL, WHETHER OR NOT WITH HEADS OF OTHER\nMATERIAL, BUT EXCLUDING SUCH ARTICLES WITH HEADS OF COPPER - NAILS, TACKS, DRAWING\nPINS, CORRUGATED NAILS, STAPLES (OTHER THAN THOSE OF HEADING 8305) AND SIMILAR\nARTICLES, OF IRON OR STEEL, WHETHER OR NOT WITH HEADS OF OTHER MATERIAL, BUT\nEXCLUDING SUCH ARTICLES WITH HEADS OF COPPER : NAILS : FOR ANIMAL SHOES",
+        "hsn_code": "73170011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NAILS, TACKS, DRAWING PINS, CORRUGATED NAILS, STAPLES (OTHER THAN THOSE OF HEADING\n8305) AND SIMILAR ARTICLES, OF IRON OR STEEL, WHETHER OR NOT WITH HEADS OF OTHER\nMATERIAL, BUT EXCLUDING SUCH ARTICLES WITH HEADS OF COPPER - NAILS, TACKS, DRAWING\nPINS, CORRUGATED NAILS, STAPLES (OTHER THAN THOSE OF HEADING 8305) AND SIMILAR\nARTICLES, OF IRON OR STEEL, WHETHER OR NOT WITH HEADS OF OTHER MATERIAL, BUT\nEXCLUDING SUCH ARTICLES WITH HEADS OF COPPER : NAILS : FOR FIXING HEEL STRIPS AND TOE\nPLATES",
+        "hsn_code": "73170012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NAILS, TACKS, DRAWING PINS, CORRUGATED NAILS, STAPLES (OTHER THAN THOSE OF HEADING\n8305) AND SIMILAR ARTICLES, OF IRON OR STEEL, WHETHER OR NOT WITH HEADS OF OTHER\nMATERIAL, BUT EXCLUDING SUCH ARTICLES WITH HEADS OF COPPER - NAILS, TACKS, DRAWING\nPINS, CORRUGATED NAILS, STAPLES (OTHER THAN THOSE OF HEADING 8305) AND SIMILAR\nARTICLES, OF IRON OR STEEL, WHETHER OR NOT WITH HEADS OF OTHER MATERIAL, BUT\nEXCLUDING SUCH ARTICLES WITH HEADS OF COPPER : NAILS : WIRE NAILS",
+        "hsn_code": "73170013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NAILS, TACKS, DRAWING PINS, CORRUGATED NAILS, STAPLES (OTHER THAN THOSE OF HEADING\n8305) AND SIMILAR ARTICLES, OF IRON OR STEEL, WHETHER OR NOT WITH HEADS OF OTHER\nMATERIAL, BUT EXCLUDING SUCH ARTICLES WITH HEADS OF COPPER - NAILS, TACKS, DRAWING\nPINS, CORRUGATED NAILS, STAPLES (OTHER THAN THOSE OF HEADING 8305) AND SIMILAR\nARTICLES, OF IRON OR STEEL, WHETHER OR NOT WITH HEADS OF OTHER MATERIAL, BUT\nEXCLUDING SUCH ARTICLES WITH HEADS OF COPPER : NAILS : OTHER",
+        "hsn_code": "73170019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NAILS, TACKS, DRAWING PINS, CORRUGATED NAILS, STAPLES (OTHER THAN THOSE OF HEADING\n8305) AND SIMILAR ARTICLES, OF IRON OR STEEL, WHETHER OR NOT WITH HEADS OF OTHER\nMATERIAL, BUT EXCLUDING SUCH ARTICLES WITH HEADS OF COPPER - NAILS, TACKS, DRAWING\nPINS, CORRUGATED NAILS, STAPLES (OTHER THAN THOSE OF HEADING 8305) AND SIMILAR\nARTICLES, OF IRON OR STEEL, WHETHER OR NOT WITH HEADS OF OTHER MATERIAL, BUT\nEXCLUDING SUCH ARTICLES WITH HEADS OF COPPER : SPIKES : DOG SPIKES",
+        "hsn_code": "73170021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NAILS, TACKS, DRAWING PINS, CORRUGATED NAILS, STAPLES (OTHER THAN THOSE OF HEADING\n8305) AND SIMILAR ARTICLES, OF IRON OR STEEL, WHETHER OR NOT WITH HEADS OF OTHER\nMATERIAL, BUT EXCLUDING SUCH ARTICLES WITH HEADS OF COPPER - NAILS, TACKS, DRAWING\nPINS, CORRUGATED NAILS, STAPLES (OTHER THAN THOSE OF HEADING 8305) AND SIMILAR\nARTICLES, OF IRON OR STEEL, WHETHER OR NOT WITH HEADS OF OTHER MATERIAL, BUT\nEXCLUDING SUCH ARTICLES WITH HEADS OF COPPER : SPIKES : OTHER",
+        "hsn_code": "73170029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NAILS, TACKS, DRAWING PINS, CORRUGATED NAILS, STAPLES (OTHER THAN THOSE OF HEADING\n8305) AND SIMILAR ARTICLES, OF IRON OR STEEL, WHETHER OR NOT WITH HEADS OF OTHER\nMATERIAL, BUT EXCLUDING SUCH ARTICLES WITH HEADS OF COPPER - NAILS, TACKS, DRAWING\nPINS, CORRUGATED NAILS, STAPLES (OTHER THAN THOSE OF HEADING 8305) AND SIMILAR\nARTICLES, OF IRON OR STEEL, WHETHER OR NOT WITH HEADS OF OTHER MATERIAL, BUT\nEXCLUDING SUCH ARTICLES WITH HEADS OF COPPER : TACKS",
+        "hsn_code": "73170030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NAILS, TACKS, DRAWING PINS, CORRUGATED NAILS, STAPLES (OTHER THAN THOSE OF HEADING\n8305) AND SIMILAR ARTICLES, OF IRON OR STEEL, WHETHER OR NOT WITH HEADS OF OTHER\nMATERIAL, BUT EXCLUDING SUCH ARTICLES WITH HEADS OF COPPER - NAILS, TACKS, DRAWING\nPINS, CORRUGATED NAILS, STAPLES (OTHER THAN THOSE OF HEADING 8305) AND SIMILAR\nARTICLES, OF IRON OR STEEL, WHETHER OR NOT WITH HEADS OF OTHER MATERIAL, BUT\nEXCLUDING SUCH ARTICLES WITH HEADS OF COPPER : OTHER : STAPLES OTHER THAN IN STRIPS,\nAND DRAWING PINS",
+        "hsn_code": "73170091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NAILS, TACKS, DRAWING PINS, CORRUGATED NAILS, STAPLES (OTHER THAN THOSE OF HEADING\n8305) AND SIMILAR ARTICLES, OF IRON OR STEEL, WHETHER OR NOT WITH HEADS OF OTHER\nMATERIAL, BUT EXCLUDING SUCH ARTICLES WITH HEADS OF COPPER - NAILS, TACKS, DRAWING\nPINS, CORRUGATED NAILS, STAPLES (OTHER THAN THOSE OF HEADING 8305) AND SIMILAR\nARTICLES, OF IRON OR STEEL, WHETHER OR NOT WITH HEADS OF OTHER MATERIAL, BUT\nEXCLUDING SUCH ARTICLES WITH HEADS OF COPPER : OTHER : OTHER",
+        "hsn_code": "73170099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NAILS, TACKS, DRAWING PINS, CORRUGATED NAILS, STAPLES (OTHER THAN THOSE OF HEADING 8305) AND SIMILAR ARTICLES, OF IRON OR STEEL, WHETHER OR NOT WITH HEADS OF OTHER MATERIAL, BUT EXCLUDING SUCH ARTICLES WITH HEADS OF COPPER",
+        "hsn_code": "7317",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANCHORS, GRAPNELS AND PARTS THEREOF, OF IRON OR STEEL - ANCHORS, GRAPNELS AND\nPARTS THEREOF, OF IRON OR STEEL : ANCHORS AND GRAPNELS",
+        "hsn_code": "73160010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANCHORS, GRAPNELS AND PARTS THEREOF, OF IRON OR STEEL - ANCHORS, GRAPNELS AND\nPARTS THEREOF, OF IRON OR STEEL : PARTS",
+        "hsn_code": "73160090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANCHORS, GRAPNELS AND PARTS THEREOF, OF IRON OR STEEL",
+        "hsn_code": "7316",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ROLLER CHAIN",
+        "hsn_code": "73151100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LIFTING AND HOISTING CHAIN",
+        "hsn_code": "73151210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHIP CHAIN",
+        "hsn_code": "73151220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "73151290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS",
+        "hsn_code": "73151900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHAIN AND PARTS THEREOF, OF IRON OR STEEL - ARTICULATED LINK CHAIN AND PARTS\nTHEREOF : SKID CHAIN",
+        "hsn_code": "73152000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHAIN AND PARTS THEREOF, OF IRON OR STEEL - OTHER CHAIN : STUD-LINK",
+        "hsn_code": "73158100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHAIN AND PARTS THEREOF, OF IRON OR STEEL - OTHER CHAIN : OTHER, WELDED LINK",
+        "hsn_code": "73158200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHAIN AND PARTS THEREOF, OF IRON OR STEEL - OTHER CHAIN : OTHER",
+        "hsn_code": "73158900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHAIN AND PARTS THEREOF, OF IRON OR STEEL - OTHER PARTS",
+        "hsn_code": "73159000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHAIN AND PARTS THEREOF, OF IRON OR STEEL",
+        "hsn_code": "7315",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL, NETTING AND FENCING, OF IRON OR STEEL WIRE;\nEXPANDED METAL OF IRON OR STEEL - WOVEN CLOTH : ENDLESS BANDS FOR MACHINERY, OF\nSTAINLESS STEEL",
+        "hsn_code": "73141200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL, NETTING AND FENCING, OF IRON OR STEEL WIRE;\nEXPANDED METAL OF IRON OR STEEL - WOVEN CLOTH : OTHER ENDLESS BANDS FOR\nMACHINERY",
+        "hsn_code": "73141300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL, NETTING AND FENCING, OF IRON OR STEEL WIRE;\nEXPANDED METAL OF IRON OR STEEL - WOVEN CLOTH : OTHER WOVEN CLOTH, OF STAINLESS\nSTEEL : WIRE GAUZE (WIRE CLOTH, WIRE MESH)",
+        "hsn_code": "73141410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL, NETTING AND FENCING, OF IRON OR STEEL WIRE;\nEXPANDED METAL OF IRON OR STEEL - WOVEN CLOTH : OTHER WOVEN CLOTH, OF STAINLESS\nSTEEL : OTHER",
+        "hsn_code": "73141490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL, NETTING AND FENCING, OF IRON OR STEEL WIRE;\nEXPANDED METAL OF IRON OR STEEL - WOVEN CLOTH : OTHER : WIRE GAUZE (WIRE CLOTH,\nWIRE MESH)",
+        "hsn_code": "73141910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL, NETTING AND FENCING, OF IRON OR STEEL WIRE;\nEXPANDED METAL OF IRON OR STEEL - WOVEN CLOTH : OTHER : OTHER",
+        "hsn_code": "73141990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL, NETTING AND FENCING, OF IRON OR STEEL WIRE;\nEXPANDED METAL OF IRON OR STEEL - GRILL, NETTING AND FENCING, WELDED AT THE\nINTERSECTION, OF WIRE WITH A MAXIMUM CROSSSECTIONAL DIMENSION OF 3 MM OR MORE\nAND HAVING A MESH SIZE OF 100 CM2 OR MORE :WIRE NETTING",
+        "hsn_code": "73142010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL, NETTING AND FENCING, OF IRON OR STEEL WIRE;\nEXPANDED METAL OF IRON OR STEEL - GRILL, NETTING AND FENCING, WELDED AT THE\nINTERSECTION, OF WIRE WITH A MAXIMUM CROSSSECTIONAL DIMENSION OF 3 MM OR MORE\nAND HAVING A MESH SIZE OF 100 CM2 OR MORE :OTHER",
+        "hsn_code": "73142090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL, NETTING AND FENCING, OF IRON OR STEEL WIRE;\nEXPANDED METAL OF IRON OR STEEL - OTHER GRILL, NETTING AND FENCING, WELDED AT THE\nINTERSECTION : PLATED OR COATED WITH ZINC",
+        "hsn_code": "73143100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL, NETTING AND FENCING, OF IRON OR STEEL WIRE;\nEXPANDED METAL OF IRON OR STEEL - OTHER GRILL, NETTING AND FENCING, WELDED AT THE\nINTERSECTION : OTHER",
+        "hsn_code": "73143900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL, NETTING AND FENCING, OF IRON OR STEEL WIRE;\nEXPANDED METAL OF IRON OR STEEL - OTHER CLOTH, GRILL, NETTING AND FENCING : PLATED\nOR COATED WITH ZINC : WIRE NETTING",
+        "hsn_code": "73144110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL, NETTING AND FENCING, OF IRON OR STEEL WIRE;\nEXPANDED METAL OF IRON OR STEEL - OTHER CLOTH, GRILL, NETTING AND FENCING : PLATED\nOR COATED WITH ZINC : OTHER",
+        "hsn_code": "73144190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL, NETTING AND FENCING, OF IRON OR STEEL WIRE;\nEXPANDED METAL OF IRON OR STEEL - OTHER CLOTH, GRILL, NETTING AND FENCING : COATED\nWITH PLASTICS : WIRE NETTING",
+        "hsn_code": "73144210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL, NETTING AND FENCING, OF IRON OR STEEL WIRE;\nEXPANDED METAL OF IRON OR STEEL - OTHER CLOTH, GRILL, NETTING AND FENCING : COATED\nWITH PLASTICS : OTHER",
+        "hsn_code": "73144290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL, NETTING AND FENCING, OF IRON OR STEEL WIRE;\nEXPANDED METAL OF IRON OR STEEL - OTHER CLOTH, GRILL, NETTING AND FENCING : OTHER :\nWIRE NETTING",
+        "hsn_code": "73144910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL, NETTING AND FENCING, OF IRON OR STEEL WIRE;\nEXPANDED METAL OF IRON OR STEEL - OTHER CLOTH, GRILL, NETTING AND FENCING : OTHER :\nOTHER",
+        "hsn_code": "73144990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL, NETTING AND FENCING, OF IRON OR STEEL WIRE;\nEXPANDED METAL OF IRON OR STEEL - EXPANDED METAL",
+        "hsn_code": "73145000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL, NETTING AND FENCING, OF IRON OR STEEL WIRE; EXPANDED METAL OF IRON OR STEEL",
+        "hsn_code": "7314",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARBED WIRE OF IRON OR STEEL; TWISTED HOOP OR SINGLE FLAT WIRE, BARBED OR NOT, AND\nLOOSELY TWISTED DOUBLE WIRE, OF A KIND USED FOR FENCING, OF IRON OR STEEL - BARBED\nWIRE OF IRON OR STEEL; TWISTED HOOP OR SINGLE FLAT WIRE, BARBED OR NOT, AND LOOSELY\nTWISTED DOUBLE WIRE, OF A KIND USED FOR FENCING, OF IRON OR STEEL : BARBED WIRE",
+        "hsn_code": "73130010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARBED WIRE OF IRON OR STEEL; TWISTED HOOP OR SINGLE FLAT WIRE, BARBED OR NOT, AND\nLOOSELY TWISTED DOUBLE WIRE, OF A KIND USED FOR FENCING, OF IRON OR STEEL - BARBED\nWIRE OF IRON OR STEEL; TWISTED HOOP OR SINGLE FLAT WIRE, BARBED OR NOT, AND LOOSELY\nTWISTED DOUBLE WIRE, OF A KIND USED FOR FENCING, OF IRON OR STEEL : TWISTED HOOP OR\nSINGLE FLAT WIRE, BARBED OR NOT, AND LOOSELY TWISTED DOUBLE WIRE, OF A KIND USED\nFOR FENCING",
+        "hsn_code": "73130020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BARBED WIRE OF IRON OR STEEL; TWISTED HOOP OR SINGLE FLAT WIRE, BARBED OR NOT, AND LOOSELY TWISTED DOUBLE WIRE, OF A KIND USED FOR FENCING, OF IRON OR STEEL",
+        "hsn_code": "7313",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRANDED WIRE, ROPES, CABLES, PLAITED BANDS, SLINGS AND THE LIKE, OF IRON OR STEEL,\nNOT ELECTRICALLY INSULATED - STRANDED WIRE, ROPES AND CABLES : WIRE ROPES, BLACK",
+        "hsn_code": "73121010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRANDED WIRE, ROPES, CABLES, PLAITED BANDS, SLINGS AND THE LIKE, OF IRON OR STEEL,\nNOT ELECTRICALLY INSULATED - STRANDED WIRE, ROPES AND CABLES : WIRE ROPES,\nGALVANISED",
+        "hsn_code": "73121020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRANDED WIRE, ROPES, CABLES, PLAITED BANDS, SLINGS AND THE LIKE, OF IRON OR STEEL,\nNOT ELECTRICALLY INSULATED - STRANDED WIRE, ROPES AND CABLES : STRANDED WIRE",
+        "hsn_code": "73121030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRANDED WIRE, ROPES, CABLES, PLAITED BANDS, SLINGS AND THE LIKE, OF IRON OR STEEL,\nNOT ELECTRICALLY INSULATED - STRANDED WIRE, ROPES AND CABLES : OTHER",
+        "hsn_code": "73121090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRANDED WIRE, ROPES, CABLES, PLAITED BANDS, SLINGS AND THE LIKE, OF IRON OR STEEL,\nNOT ELECTRICALLY INSULATED - OTHER",
+        "hsn_code": "73129000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRANDED WIRE, ROPES, CABLES, PLAITED BANDS, SLINGS AND THE LIKE, OF IRON OR STEEL, NOT ELECTRICALLY INSULATED",
+        "hsn_code": "7312",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONTAINERS FOR COMPRESSED OR LIQUEFIED GAS, OF IRON OR STEEL - CONTAINERS FOR\nCOMPRESSED OR LIQUEFIED GAS, OF IRON OR STEEL : LIQUEFIED PETROLEUM GAS (LPG)\nCYLINDER",
+        "hsn_code": "73110010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONTAINERS FOR COMPRESSED OR LIQUEFIED GAS, OF IRON OR STEEL - CONTAINERS FOR\nCOMPRESSED OR LIQUEFIED GAS, OF IRON OR STEEL : LOW PRESSURE CYLINDER (WORKING\nPRESSURE UP TO 35.2 KG/SQ.CM OTHER THAN LPG)",
+        "hsn_code": "73110020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONTAINERS FOR COMPRESSED OR LIQUEFIED GAS, OF IRON OR STEEL - CONTAINERS FOR\nCOMPRESSED OR LIQUEFIED GAS, OF IRON OR STEEL : HIGH PRESSURE CYLINDER (WORKING\nPRESSURE EXCEEDING 35.2 KG/SQ.CM)",
+        "hsn_code": "73110030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONTAINERS FOR COMPRESSED OR LIQUEFIED GAS, OF IRON OR STEEL - CONTAINERS FOR\nCOMPRESSED OR LIQUEFIED GAS, OF IRON OR STEEL : OTHER",
+        "hsn_code": "73110090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONTAINERS FOR COMPRESSED OR LIQUEFIED GAS, OF IRON OR STEEL",
+        "hsn_code": "7311",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RESERVOIRS, TANKS, VATS AND SIMILAR CONTAINERS FOR ANY MATERIAL (OTHER THAN\nCOMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY EXCEEDING 300 L,\nWHETHER OR NOT LINED OR HEAT-INSULATED, BUT NOT FITTED WITH MECHANICAL OR\nTHERMAL EQUIPMENT - RESERVOIRS, TANKS, VATS AND SIMILAR CONTAINERS FOR ANY\nMATERIAL (OTHER THAN COMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY\nEXCEEDING 300 L, WHETHER OR NOT LINED OR HEAT-INSULATED, BUT NOT FITTED WITH\nMECHANICAL OR THERMAL EQUIPMENT : GALVANIZED IRON TANKS",
+        "hsn_code": "73090010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RESERVOIRS, TANKS, VATS AND SIMILAR CONTAINERS FOR ANY MATERIAL (OTHER THAN\nCOMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY EXCEEDING 300 L,\nWHETHER OR NOT LINED OR HEAT-INSULATED, BUT NOT FITTED WITH MECHANICAL OR\nTHERMAL EQUIPMENT - RESERVOIRS, TANKS, VATS AND SIMILAR CONTAINERS FOR ANY\nMATERIAL (OTHER THAN COMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY\nEXCEEDING 300 L, WHETHER OR NOT LINED OR HEAT-INSULATED, BUT NOT FITTED WITH\nMECHANICAL OR THERMAL EQUIPMENT : GALVANIZED IRON BARRELS AND DRUMS",
+        "hsn_code": "73090020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RESERVOIRS, TANKS, VATS AND SIMILAR CONTAINERS FOR ANY MATERIAL (OTHER THAN\nCOMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY EXCEEDING 300 L,\nWHETHER OR NOT LINED OR HEAT-INSULATED, BUT NOT FITTED WITH MECHANICAL OR\nTHERMAL EQUIPMENT - RESERVOIRS, TANKS, VATS AND SIMILAR CONTAINERS FOR ANY\nMATERIAL (OTHER THAN COMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY\nEXCEEDING 300 L, WHETHER OR NOT LINED OR HEAT-INSULATED, BUT NOT FITTED WITH\nMECHANICAL OR THERMAL EQUIPMENT : PRESSED STEEL TANKS",
+        "hsn_code": "73090030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RESERVOIRS, TANKS, VATS AND SIMILAR CONTAINERS FOR ANY MATERIAL (OTHER THAN\nCOMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY EXCEEDING 300 L,\nWHETHER OR NOT LINED OR HEAT-INSULATED, BUT NOT FITTED WITH MECHANICAL OR\nTHERMAL EQUIPMENT - RESERVOIRS, TANKS, VATS AND SIMILAR CONTAINERS FOR ANY\nMATERIAL (OTHER THAN COMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY\nEXCEEDING 300 L, WHETHER OR NOT LINED OR HEAT-INSULATED, BUT NOT FITTED WITH\nMECHANICAL OR THERMAL EQUIPMENT : PRESSURE VESSELS",
+        "hsn_code": "73090040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RESERVOIRS, TANKS, VATS AND SIMILAR CONTAINERS FOR ANY MATERIAL (OTHER THAN\nCOMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY EXCEEDING 300 L,\nWHETHER OR NOT LINED OR HEAT-INSULATED, BUT NOT FITTED WITH MECHANICAL OR\nTHERMAL EQUIPMENT - RESERVOIRS, TANKS, VATS AND SIMILAR CONTAINERS FOR ANY\nMATERIAL (OTHER THAN COMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY\nEXCEEDING 300 L, WHETHER OR NOT LINED OR HEAT-INSULATED, BUT NOT FITTED WITH\nMECHANICAL OR THERMAL EQUIPMENT : OTHER",
+        "hsn_code": "73090090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RESERVOIRS, TANKS, VATS AND SIMILAR CONTAINERS FOR ANY MATERIAL (OTHER THAN COMPRESSED OR LIQUEFIED GAS), OF IRON OR STEEL, OF A CAPACITY EXCEEDING 300 L, WHETHER OR NOT LINED OR HEAT-INSULATED, BUT NOT FITTED WITH MECHANICAL OR THERMAL EQUIPMENT",
+        "hsn_code": "7309",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND PARTS OF\nSTRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, LOCK-GATES, TOWERS, LATTICE\nMASTS, ROOFS, ROOFING FRAME-WORKS, DOORS AND WINDOWS AND THEIR FRAMES AND\nTHRESHOLDS FOR DOORS, SHUTTERS, BALUSTRADES, PILLARS AND COLUMNS), OF IRON OR\nSTEEL; PLATES, RODS, ANGLES, SHAPES, SECTIONS, TUBES AND THE LIKE, PREPARED FOR USE IN\nSTRUCTURES, OF IRON OR STEEL - BRIDGES AND BRIDGE-SECTIONS",
+        "hsn_code": "73081000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND PARTS OF\nSTRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, LOCK-GATES, TOWERS, LATTICE\nMASTS, ROOFS, ROOFING FRAME-WORKS, DOORS AND WINDOWS AND THEIR FRAMES AND\nTHRESHOLDS FOR DOORS, SHUTTERS, BALUSTRADES, PILLARS AND COLUMNS), OF IRON OR\nSTEEL; PLATES, RODS, ANGLES, SHAPES, SECTIONS, TUBES AND THE LIKE, PREPARED FOR USE IN\nSTRUCTURES, OF IRON OR STEEL - TOWERS AND LATTICE MASTS : TOWERS, WHETHER OR NOT\nASSEMBLED : FOR TRANSMISSION LINE",
+        "hsn_code": "73082011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND PARTS OF\nSTRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, LOCK-GATES, TOWERS, LATTICE\nMASTS, ROOFS, ROOFING FRAME-WORKS, DOORS AND WINDOWS AND THEIR FRAMES AND\nTHRESHOLDS FOR DOORS, SHUTTERS, BALUSTRADES, PILLARS AND COLUMNS), OF IRON OR\nSTEEL; PLATES, RODS, ANGLES, SHAPES, SECTIONS, TUBES AND THE LIKE, PREPARED FOR USE IN\nSTRUCTURES, OF IRON OR STEEL - TOWERS AND LATTICE MASTS : TOWERS, WHETHER OR NOT\nASSEMBLED : OTHER",
+        "hsn_code": "73082019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND PARTS OF\nSTRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, LOCK-GATES, TOWERS, LATTICE\nMASTS, ROOFS, ROOFING FRAME-WORKS, DOORS AND WINDOWS AND THEIR FRAMES AND\nTHRESHOLDS FOR DOORS, SHUTTERS, BALUSTRADES, PILLARS AND COLUMNS), OF IRON OR\nSTEEL; PLATES, RODS, ANGLES, SHAPES, SECTIONS, TUBES AND THE LIKE, PREPARED FOR USE IN\nSTRUCTURES, OF IRON OR STEEL - TOWERS AND LATTICE MASTS : LATTICE MASTS",
+        "hsn_code": "73082020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND PARTS OF\nSTRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, LOCK-GATES, TOWERS, LATTICE\nMASTS, ROOFS, ROOFING FRAME-WORKS, DOORS AND WINDOWS AND THEIR FRAMES AND\nTHRESHOLDS FOR DOORS, SHUTTERS, BALUSTRADES, PILLARS AND COLUMNS), OF IRON OR\nSTEEL; PLATES, RODS, ANGLES, SHAPES, SECTIONS, TUBES AND THE LIKE, PREPARED FOR USE IN\nSTRUCTURES, OF IRON OR STEEL - DOORS, WINDOWS AND THEIR FRAMES AND THRESHOLDS\nFOR DOORS",
+        "hsn_code": "73083000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND PARTS OF\nSTRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, LOCK-GATES, TOWERS, LATTICE\nMASTS, ROOFS, ROOFING FRAME-WORKS, DOORS AND WINDOWS AND THEIR FRAMES AND\nTHRESHOLDS FOR DOORS, SHUTTERS, BALUSTRADES, PILLARS AND COLUMNS), OF IRON OR\nSTEEL; PLATES, RODS, ANGLES, SHAPES, SECTIONS, TUBES AND THE LIKE, PREPARED FOR USE IN\nSTRUCTURES, OF IRON OR STEEL - EQUIPMENT FOR SCAFFOLDING, SHUTTERING, PROPPING OR\nPIT-PROPPING",
+        "hsn_code": "73084000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND PARTS OF\nSTRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, LOCK-GATES, TOWERS, LATTICE\nMASTS, ROOFS, ROOFING FRAME-WORKS, DOORS AND WINDOWS AND THEIR FRAMES AND\nTHRESHOLDS FOR DOORS, SHUTTERS, BALUSTRADES, PILLARS AND COLUMNS), OF IRON OR\nSTEEL; PLATES, RODS, ANGLES, SHAPES, SECTIONS, TUBES AND THE LIKE, PREPARED FOR USE IN\nSTRUCTURES, OF IRON OR STEEL - OTHER : BEAMS, CHANNELS, PILLARS AND GIRDERS PREPARED\nFOR USE IN STRUCTURES",
+        "hsn_code": "73089010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND PARTS OF\nSTRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, LOCK-GATES, TOWERS, LATTICE\nMASTS, ROOFS, ROOFING FRAME-WORKS, DOORS AND WINDOWS AND THEIR FRAMES AND\nTHRESHOLDS FOR DOORS, SHUTTERS, BALUSTRADES, PILLARS AND COLUMNS), OF IRON OR\nSTEEL; PLATES, RODS, ANGLES, SHAPES, SECTIONS, TUBES AND THE LIKE, PREPARED FOR USE IN\nSTRUCTURES, OF IRON OR STEEL - OTHER : DROP RODS",
+        "hsn_code": "73089020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND PARTS OF\nSTRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, LOCK-GATES, TOWERS, LATTICE\nMASTS, ROOFS, ROOFING FRAME-WORKS, DOORS AND WINDOWS AND THEIR FRAMES AND\nTHRESHOLDS FOR DOORS, SHUTTERS, BALUSTRADES, PILLARS AND COLUMNS), OF IRON OR\nSTEEL; PLATES, RODS, ANGLES, SHAPES, SECTIONS, TUBES AND THE LIKE, PREPARED FOR USE IN\nSTRUCTURES, OF IRON OR STEEL - OTHER : HATCHWAY, RAILS AND BULKHEADS FOR SHIPS OR\nBOATS AND PARTS OF HULL",
+        "hsn_code": "73089030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND PARTS OF\nSTRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, LOCK-GATES, TOWERS, LATTICE\nMASTS, ROOFS, ROOFING FRAME-WORKS, DOORS AND WINDOWS AND THEIR FRAMES AND\nTHRESHOLDS FOR DOORS, SHUTTERS, BALUSTRADES, PILLARS AND COLUMNS), OF IRON OR\nSTEEL; PLATES, RODS, ANGLES, SHAPES, SECTIONS, TUBES AND THE LIKE, PREPARED FOR USE IN\nSTRUCTURES, OF IRON OR STEEL - OTHER : GALVANISED TENSION BARS",
+        "hsn_code": "73089040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND PARTS OF\nSTRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, LOCK-GATES, TOWERS, LATTICE\nMASTS, ROOFS, ROOFING FRAME-WORKS, DOORS AND WINDOWS AND THEIR FRAMES AND\nTHRESHOLDS FOR DOORS, SHUTTERS, BALUSTRADES, PILLARS AND COLUMNS), OF IRON OR\nSTEEL; PLATES, RODS, ANGLES, SHAPES, SECTIONS, TUBES AND THE LIKE, PREPARED FOR USE IN\nSTRUCTURES, OF IRON OR STEEL - OTHER : STRUCTURES AND SUPER STRUCTURES FOR MINING",
+        "hsn_code": "73089050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND PARTS OF\nSTRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, LOCK-GATES, TOWERS, LATTICE\nMASTS, ROOFS, ROOFING FRAME-WORKS, DOORS AND WINDOWS AND THEIR FRAMES AND\nTHRESHOLDS FOR DOORS, SHUTTERS, BALUSTRADES, PILLARS AND COLUMNS), OF IRON OR\nSTEEL; PLATES, RODS, ANGLES, SHAPES, SECTIONS, TUBES AND THE LIKE, PREPARED FOR USE IN\nSTRUCTURES, OF IRON OR STEEL - OTHER : TRUSS RODS",
+        "hsn_code": "73089060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND PARTS OF\nSTRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, LOCK-GATES, TOWERS, LATTICE\nMASTS, ROOFS, ROOFING FRAME-WORKS, DOORS AND WINDOWS AND THEIR FRAMES AND\nTHRESHOLDS FOR DOORS, SHUTTERS, BALUSTRADES, PILLARS AND COLUMNS), OF IRON OR\nSTEEL; PLATES, RODS, ANGLES, SHAPES, SECTIONS, TUBES AND THE LIKE, PREPARED FOR USE IN\nSTRUCTURES, OF IRON OR STEEL - OTHER : TUBULAR STEEL POLES FOR ELECTRIC TRANSMISSION\nAND DISTRIBUTION LINES",
+        "hsn_code": "73089070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND PARTS OF\nSTRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, LOCK-GATES, TOWERS, LATTICE\nMASTS, ROOFS, ROOFING FRAME-WORKS, DOORS AND WINDOWS AND THEIR FRAMES AND\nTHRESHOLDS FOR DOORS, SHUTTERS, BALUSTRADES, PILLARS AND COLUMNS), OF IRON OR\nSTEEL; PLATES, RODS, ANGLES, SHAPES, SECTIONS, TUBES AND THE LIKE, PREPARED FOR USE IN\nSTRUCTURES, OF IRON OR STEEL - OTHER : OTHER",
+        "hsn_code": "73089090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STRUCTURES (EXCLUDING PREFABRICATED BUILDINGS OF HEADING 9406) AND PARTS OF STRUCTURES (FOR EXAMPLE, BRIDGES AND BRIDGE-SECTIONS, LOCK-GATES, TOWERS, LATTICE MASTS, ROOFS, ROOFING FRAMEWORKS, DOORS AND WINDOWS AND THEIR FRAMES AND THRESHOLDS FOR DOORS, SHUTTERS, BALUSTRADES, PILLARS AND COLUMNS), OF IRON OR STEEL; PLATES, RODS, ANGLES, SHAPES, SECTIONS, TUBES AND THE LIKE, PREPARED FOR USE IN STRUCTURES OF IRON OR STEEL",
+        "hsn_code": "7308",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES), OF IRON OR STEEL -\nCAST FITTINGS : OF NON-MALLEABLE CAST IRON : SPONGE IRON CAST FITTINGS",
+        "hsn_code": "73071110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES), OF IRON OR STEEL -\nCAST FITTINGS : OF NON-MALLEABLE CAST IRON : SG IRON CAST FITTINGS",
+        "hsn_code": "73071120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES), OF IRON OR STEEL -\nCAST FITTINGS : OF NON-MALLEABLE CAST IRON : OTHER",
+        "hsn_code": "73071190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES), OF IRON OR STEEL -\nCAST FITTINGS : OTHER",
+        "hsn_code": "73071900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES), OF IRON OR STEEL -\nOTHER, OF STAINLESS STEEL : FLANGES",
+        "hsn_code": "73072100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES), OF IRON OR STEEL -\nOTHER, OF STAINLESS STEEL : THREADED ELBOWS, BENDS AND SLEEVES",
+        "hsn_code": "73072200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES), OF IRON OR STEEL -\nOTHER, OF STAINLESS STEEL : BUTT WELDING FITTINGS",
+        "hsn_code": "73072300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES), OF IRON OR STEEL -\nOTHER, OF STAINLESS STEEL : OTHER",
+        "hsn_code": "73072900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES), OF IRON OR STEEL -\nOTHER : FLANGES : GALVANISED",
+        "hsn_code": "73079110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES), OF IRON OR STEEL -\nOTHER : FLANGES : OTHER",
+        "hsn_code": "73079190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES), OF IRON OR STEEL -\nOTHER : THREADED ELBOWS, BENDS AND SLEEVES : GALVANISED",
+        "hsn_code": "73079210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES), OF IRON OR STEEL -\nOTHER : THREADED ELBOWS, BENDS AND SLEEVES : OTHER",
+        "hsn_code": "73079290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES), OF IRON OR STEEL -\nOTHER : BUTT WELDING FITTINGS : GALVANISED",
+        "hsn_code": "73079310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES), OF IRON OR STEEL -\nOTHER : BUTT WELDING FITTINGS : OTHER",
+        "hsn_code": "73079390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES), OF IRON OR STEEL -\nOTHER : OTHER : GALVANISED",
+        "hsn_code": "73079910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES), OF IRON OR STEEL -\nOTHER : OTHER : OTHER",
+        "hsn_code": "73079990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES), OF IRON OR STEEL",
+        "hsn_code": "7307",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS\nPIPELINES : GALVANISED PIPES : OF IRON",
+        "hsn_code": "73061011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS\nPIPELINES : GALVANISED PIPES : OTHER",
+        "hsn_code": "73061019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS\nPIPELINES : NON-GALVANISED PIPES : OF IRON",
+        "hsn_code": "73061021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS\nPIPELINES : NON-GALVANISED PIPES : OTHER",
+        "hsn_code": "73061029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINE\n-- WELDED, OF STAILESS STEEL",
+        "hsn_code": "73061100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINE\n-- OTHER --- GALVANIZED PIPES: ---- OF IRON",
+        "hsn_code": "73061911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINE\n-- OTHER --- GALVANIZED PIPES: ---- OTHER",
+        "hsn_code": "73061919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINE\n-- OTHER --- NON-GALVANIZED PIPES: ---- OF IRON",
+        "hsn_code": "73061921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINE\n-- OTHER --- NON-GALVANIZED PIPES: ---- OTHER",
+        "hsn_code": "73061929",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - CASING AND TUBING OF A KIND USED IN DRILLING\nFOR OIL OR GAS : OF IRON",
+        "hsn_code": "73062010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - CASING AND TUBING OF A KIND USED IN DRILLING\nFOR OIL OR GAS : OTHER",
+        "hsn_code": "73062090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - CASING AND TUBING OF A KIND USED IN DRILLING\nFOR OIL OR GAS -- WELDED, OF STAILESS STEEL",
+        "hsn_code": "73062100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - CASING AND TUBING OF A KIND USED IN DRILLING\nFOR OIL OR GAS -- OTHER ---- OF IRON",
+        "hsn_code": "73062911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - CASING AND TUBING OF A KIND USED IN DRILLING\nFOR OIL OR GAS -- OTHER ---- OTHER",
+        "hsn_code": "73062919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - OTHER, WELDED, OF CIRCULAR CROSS-SECTION, OF\nIRON OR NON-ALLOY STEEL: --- OF IRON",
+        "hsn_code": "73063010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - OTHER, WELDED, OF CIRCULAR CROSS-SECTION, OF\nIRON OR NON-ALLOY STEEL: --- OTHER",
+        "hsn_code": "73063090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - OTHER, WELDED, OF CIRCULAR CROSS-SECTION, OF\nSTAILESS STEEL",
+        "hsn_code": "73064000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - OTHER, WELDED, OF CIRCULAR CROSS-SECTION, OF\nOTHER ALLOY STEEL",
+        "hsn_code": "73065000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - OTHER, WELDED, OF NON-CIRCULAR CROSS\nSECTION : OF IRON",
+        "hsn_code": "73066010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - OTHER, WELDED, OF NON-CIRCULAR CROSS\nSECTION : OTHER",
+        "hsn_code": "73066090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - OTHER, WELDED, OF NON-CIRCULAR CROSS\nSECTION: -- OF SQUARE OR RECTANGULAR CROSS-SECTION",
+        "hsn_code": "73066100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - OTHER, WELDED, OF NON-CIRCULAR CROSS\nSECTION: -- OF OTHER NON-CIRCULAR CROSS-SECTION",
+        "hsn_code": "73066900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - OTHER -- ERW PRECISION TUBES: ---- OF IRON",
+        "hsn_code": "73069011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - OTHER -- ERW PRECISION TUBES: ---- OTHER",
+        "hsn_code": "73069019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED\nOR SIMILARLY CLOSED), OF IRON OR STEEL - OTHER -- ERW PRECISION TUBES: --- OTHER",
+        "hsn_code": "73069090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES, PIPES AND HOLLOW PROFILES (FOR EXAMPLE, OPEN SEAM OR WELDED, RIVETED OR SIMILARLY CLOSED), OF IRON OR STEEL",
+        "hsn_code": "7306",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : LONGITUDINALLY\nSUBMERGED ARC WELDED : GALVANISED PIPES : OF IRON",
+        "hsn_code": "73051111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : LONGITUDINALLY\nSUBMERGED ARC WELDED : GALVANISED PIPES : OTHER",
+        "hsn_code": "73051119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : NON-GALVANISED PIPES : OF\nIRON",
+        "hsn_code": "73051121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : NON-GALVANISED PIPES :\nOTHER",
+        "hsn_code": "73051129",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : OTHER, LONGITUDINALLY\nWELDED : GALVANISED PIPES : OF IRON",
+        "hsn_code": "73051211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : OTHER, LONGITUDINALLY\nWELDED : GALVANISED PIPES : OTHER",
+        "hsn_code": "73051219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : OTHER, LONGITUDINALLY\nWELDED : NON-GALVANISED PIPES : OF IRON",
+        "hsn_code": "73051221",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : OTHER, LONGITUDINALLY\nWELDED : NON-GALVANISED PIPES : OTHER",
+        "hsn_code": "73051229",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : OTHER: GALVANISED PIPES :\nOF IRON",
+        "hsn_code": "73051911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : OTHER: GALVANISED PIPES :\nOTHER",
+        "hsn_code": "73051919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : OTHER: NON-GALVANISED\n(BLACK) PIPES : OF IRON",
+        "hsn_code": "73051921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - LINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : OTHER: NON-GALVANISED\n(BLACK) PIPES : OTHER",
+        "hsn_code": "73051929",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - CASING OF A KIND USED IN DRILLING FOR OIL OR GAS : OF IRON",
+        "hsn_code": "73052010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - CASING OF A KIND USED IN DRILLING FOR OIL OR GAS : OTHER",
+        "hsn_code": "73052090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - OTHER, WELDED : LONGITUDINALLY WELDED: OF IRON",
+        "hsn_code": "73053110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - OTHER, WELDED : LONGITUDINALLY WELDED: OTHER",
+        "hsn_code": "73053190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - OTHER, WELDED : OTHER : OF IRON",
+        "hsn_code": "73053910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - OTHER, WELDED : OTHER : OTHER",
+        "hsn_code": "73053990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - OTHER : HIGH PRESSURE HYDROELECTRIC CONDUITS OF STEEL",
+        "hsn_code": "73059010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - OTHER : ERW PRECISION TUBES : OF IRON",
+        "hsn_code": "73059021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - OTHER : ERW PRECISION TUBES : OTHER",
+        "hsn_code": "73059029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - OTHER : OTHER : OF IRON",
+        "hsn_code": "73059091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING\nCIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON\nOR STEEL - OTHER : OTHER : OTHER",
+        "hsn_code": "73059099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER TUBES AND PIPES (FOR EXAMPLE, WELDED, RIVETED OR SIMILARLY CLOSED), HAVING CIRCULAR CROSS-SECTIONS, THE EXTERNAL DIAMETER OF WHICH EXCEEDS 406.4 MM, OF IRON OR STEEL",
+        "hsn_code": "7305",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nLINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : PIPES OF IRON OR STEEL : OF IRON",
+        "hsn_code": "73041011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nLINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : PIPES OF IRON OR STEEL : OTHER",
+        "hsn_code": "73041019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nLINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : BLANKS FOR TUBES AND PIPES: OF IRON",
+        "hsn_code": "73041021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nLINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : BLANKS FOR TUBES AND PIPES: OTHER",
+        "hsn_code": "73041029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nLINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : -- OF STAINLESS STEEL: --- TUBES AND\nPIPES",
+        "hsn_code": "73041110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nLINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : -- OF STAINLESS STEEL: --- BLANKS FOR\nTUBES AND PIPES",
+        "hsn_code": "73041120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nLINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : -- OF STAINLESS STEEL: --- OTHER",
+        "hsn_code": "73041190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nLINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : -- OTHER --- TUBES AND PIPES",
+        "hsn_code": "73041910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nLINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : -- OTHER --- BLANKS FOR TUBES AND\nPIPES",
+        "hsn_code": "73041920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nLINE PIPE OF A KIND USED FOR OIL OR GAS PIPELINES : -- OTHER --- OTHER",
+        "hsn_code": "73041990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nCASING, TUBING AND DRILL PIPE, OF A KIND USED IN DRILLING FOR OIL OR GAS : DRILL PIPE : OF\nIRON",
+        "hsn_code": "73042110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nCASING, TUBING AND DRILL PIPE, OF A KIND USED IN DRILLING FOR OIL OR GAS : DRILL PIPE :\nOTHER",
+        "hsn_code": "73042190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nCASING, TUBING AND DRILL PIPE, OF A KIND USED IN DRILLING FOR OIL OR GAS: -- DRILL PIPE OF\nSTAILESS STEEL",
+        "hsn_code": "73042200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nCASING, TUBING AND DRILL PIPE, OF A KIND USED IN DRILLING FOR OIL OR GAS: -- OTHER DRILL\nPIPE: --- OF IRON",
+        "hsn_code": "73042310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nCASING, TUBING AND DRILL PIPE, OF A KIND USED IN DRILLING FOR OIL OR GAS: -- OTHER DRILL\nPIPE: --- OTHER",
+        "hsn_code": "73042390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nCASING, TUBING AND DRILL PIPE, OF A KIND USED IN DRILLING FOR OIL OR GAS: -- OTHER, OF\nSTAILESS STEEL",
+        "hsn_code": "73042400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nCASING, TUBING AND DRILL PIPE, OF A KIND USED IN DRILLING FOR OIL OR GAS: -- OTHER --- OF\nIRON",
+        "hsn_code": "73042910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nCASING, TUBING AND DRILL PIPE, OF A KIND USED IN DRILLING FOR OIL OR GAS: -- OTHER ---\nOTHER",
+        "hsn_code": "73042990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF IORN OR NON-ALLOY STEEL: -- COLD-DRAWN OR COLD\nROLLED (COLD-REDUCED): --- UP TO 114.3 MM OUTER DIAMETER: ---- OF IRON",
+        "hsn_code": "73043111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF IORN OR NON-ALLOY STEEL: -- COLD-DRAWN OR COLD\nROLLED (COLD-REDUCED): --- UP TO 114.3 MM OUTER DIAMETER: ---- OTHER",
+        "hsn_code": "73043119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF IORN OR NON-ALLOY STEEL: -- COLD-DRAWN OR COLD\nROLLED (COLD-REDUCED): --- ABOVE 114.3 MM BUT UP TO 219.1 MM OUTER DIAMETER: ---- OF\nIRON",
+        "hsn_code": "73043121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF IORN OR NON-ALLOY STEEL: -- COLD-DRAWN OR COLD\nROLLED (COLD-REDUCED): --- ABOVE 114.3 MM BUT UP TO 219.1 MM OUTER DIAMETER: ----\nOTHER",
+        "hsn_code": "73043129",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF IORN OR NON-ALLOY STEEL: -- COLD-DRAWN OR COLD\nROLLED (COLD-REDUCED): --- ABOVE 219.1 MM DIAMETER: ---- OF IRON",
+        "hsn_code": "73043131",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF IORN OR NON-ALLOY STEEL: -- COLD-DRAWN OR COLD\nROLLED (COLD-REDUCED): --- ABOVE 219.1 MM DIAMETER: ---- OTHER",
+        "hsn_code": "73043139",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF IORN OR NON-ALLOY STEEL: -- OTHER --- UP TO 114.3\nMM OUTER DIAMETER -- OF IRON",
+        "hsn_code": "73043911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF IORN OR NON-ALLOY STEEL: -- OTHER --- UP TO 114.3\nMM OUTER DIAMETER ---- OTHER",
+        "hsn_code": "73043919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF IORN OR NON-ALLOY STEEL: -- OTHER --- ABOVE 114.3\nMM BUT UP TO 219.1 MM OUTER DIAMETER: ---- OF IRON",
+        "hsn_code": "73043921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF IORN OR NON-ALLOY STEEL: -- OTHER --- ABOVE 114.3\nMM BUT UP TO 219.1 MM OUTER DIAMETER: ---- OTHER",
+        "hsn_code": "73043929",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF IORN OR NON-ALLOY STEEL: -- OTHER --- ABOVE 219.1\nMM DIAMETER: ---- OF IRON",
+        "hsn_code": "73043931",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF IORN OR NON-ALLOY STEEL: -- OTHER --- ABOVE 219.1\nMM DIAMETER: ---- OTHER",
+        "hsn_code": "73043939",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF STAILESS STEEL: -- COLD DRAWN OR COLD-ROLLED\n(COLD-REDUCED)",
+        "hsn_code": "73044100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF STAILESS STEEL: -- OTHER",
+        "hsn_code": "73044900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF OTHER ALLOY STEEL: -- COLD DRAWN OR COLD\nROLLED (COLD-REDUCED) --- UP TO 114.3 MM DIAMETER",
+        "hsn_code": "73045110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF OTHER ALLOY STEEL: -- COLD DRAWN OR COLD\nROLLED (COLD-REDUCED) --- ABOVE 114.3 MM BUT UP TO 219.1 MM OUTER DIAMETER",
+        "hsn_code": "73045120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF OTHER ALLOY STEEL: -- COLD DRAWN OR COLD\nROLLED (COLD-REDUCED) --- ABOVE 219.1 MM OUTER DIAMETER",
+        "hsn_code": "73045130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF OTHER ALLOY STEEL: -- OTHER --- UPTO 114.3 MM\nDIAMETER",
+        "hsn_code": "73045910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF OTHER ALLOY STEEL: -- OTHER --- ABOVE 114.3 MM\nBUT UP TO 219.1 MM OUTER DIAMETER",
+        "hsn_code": "73045920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER, OF CIRCULAR CROSS-SECTION, OF OTHER ALLOY STEEL: -- OTHER --- ABOVE 219.1 MM\nOUTER DIAMETER",
+        "hsn_code": "73045930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL -\nOTHER",
+        "hsn_code": "73049000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, SEAMLESS, OF IRON (OTHER THAN CAST IRON) OR STEEL",
+        "hsn_code": "7304",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, OF CAST IRON - TUBES, PIPES AND HOLLOW PROFILES, OF\nCAST IRON: RAIN WATER PIPE",
+        "hsn_code": "73030010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, OF CAST IRON - TUBES, PIPES AND HOLLOW PROFILES, OF\nCAST IRON: SOIL PIPE",
+        "hsn_code": "73030020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, OF CAST IRON - TUBES, PIPES AND HOLLOW PROFILES, OF\nCAST IRON: SPUN PIPE",
+        "hsn_code": "73030030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, OF CAST IRON - TUBES, PIPES AND HOLLOW PROFILES, OF\nCAST IRON: OTHER",
+        "hsn_code": "73030090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOLLOW PROFILES, OF CAST IRON",
+        "hsn_code": "7303",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY TRACK CONSTRUCTION MATERIAL OF IRON OR STEEL, THE FOLLOWING:\nRAILS, CHECK-RAILS AND RACK RAILS, SWITCH BLADES, CROSSING FROGS, POINT RODS AND\nOTHER CROSSING PIECES, SLEEPERS (CROSS-TIES), FISH - PLATES, CHAIRS, CHAIR WEDGES, SOLE\nPLATES (BASE PLATES), RAIL CLIPS, BEDPLATES, TIES AND OTHER MATERIAL SPECIALIZED FOR\nJOINTING OR FIXING RAILS- RAILS : FOR RAILWAYS",
+        "hsn_code": "73021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY TRACK CONSTRUCTION MATERIAL OF IRON OR STEEL, THE FOLLOWING:\nRAILS, CHECK-RAILS AND RACK RAILS, SWITCH BLADES, CROSSING FROGS, POINT RODS AND\nOTHER CROSSING PIECES, SLEEPERS (CROSS-TIES), FISH - PLATES, CHAIRS, CHAIR WEDGES, SOLE\nPLATES (BASE PLATES), RAIL CLIPS, BEDPLATES, TIES AND OTHER MATERIAL SPECIALIZED FOR\nJOINTING OR FIXING RAILS- RAILS : FOR TRAMWAYS",
+        "hsn_code": "73021020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY TRACK CONSTRUCTION MATERIAL OF IRON OR STEEL, THE FOLLOWING:\nRAILS, CHECK-RAILS AND RACK RAILS, SWITCH BLADES, CROSSING FROGS, POINT RODS AND\nOTHER CROSSING PIECES, SLEEPERS (CROSS-TIES), FISH - PLATES, CHAIRS, CHAIR WEDGES, SOLE\nPLATES (BASE PLATES), RAIL CLIPS, BEDPLATES, TIES AND OTHER MATERIAL SPECIALIZED FOR\nJOINTING OR FIXING RAILS- RAILS : OTHER",
+        "hsn_code": "73021090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY TRACK CONSTRUCTION MATERIAL OF IRON OR STEEL, THE FOLLOWING:\nRAILS, CHECK-RAILS AND RACK RAILS, SWITCH BLADES, CROSSING FROGS, POINT RODS AND\nOTHER CROSSING PIECES, SLEEPERS (CROSS-TIES), FISH - PLATES, CHAIRS, CHAIR WEDGES, SOLE\nPLATES (BASE PLATES), RAIL CLIPS, BEDPLATES, TIES AND OTHER MATERIAL SPECIALIZED FOR\nJOINTING OR FIXING RAILS- SWITCH BLADES, CROSSING FROGS, POINT RODS AND OTHER\nCROSSING PIECES",
+        "hsn_code": "73023000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY TRACK CONSTRUCTION MATERIAL OF IRON OR STEEL, THE FOLLOWING:\nRAILS, CHECK-RAILS AND RACK RAILS, SWITCH BLADES, CROSSING FROGS, POINT RODS AND\nOTHER CROSSING PIECES, SLEEPERS (CROSS-TIES), FISH - PLATES, CHAIRS, CHAIR WEDGES, SOLE\nPLATES (BASE PLATES), RAIL CLIPS, BEDPLATES, TIES AND OTHER MATERIAL SPECIALIZED FOR\nJOINTING OR FIXING RAILS- FISH-PLATES AND SOLE PLATES",
+        "hsn_code": "73024000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY TRACK CONSTRUCTION MATERIAL OF IRON OR STEEL, THE FOLLOWING:\nRAILS, CHECK-RAILS AND RACK RAILS, SWITCH BLADES, CROSSING FROGS, POINT RODS AND\nOTHER CROSSING PIECES, SLEEPERS (CROSS-TIES), FISH - PLATES, CHAIRS, CHAIR WEDGES, SOLE\nPLATES (BASE PLATES), RAIL CLIPS, BEDPLATES, TIES AND OTHER MATERIAL SPECIALIZED FOR\nJOINTING OR FIXING RAILS- - OTHER : MATERIAL FOR JOINTING OR FIXING RAILS",
+        "hsn_code": "73029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY TRACK CONSTRUCTION MATERIAL OF IRON OR STEEL, THE FOLLOWING:\nRAILS, CHECK-RAILS AND RACK RAILS, SWITCH BLADES, CROSSING FROGS, POINT RODS AND\nOTHER CROSSING PIECES, SLEEPERS (CROSS-TIES), FISH - PLATES, CHAIRS, CHAIR WEDGES, SOLE\nPLATES (BASE PLATES), RAIL CLIPS, BEDPLATES, TIES AND OTHER MATERIAL SPECIALIZED FOR\nJOINTING OR FIXING RAILS- - OTHER : OTHER",
+        "hsn_code": "73029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY TRACK CONSTRUCTION MATERIAL OF IRON OR STEEL, THE FOLLOWING: RAILS, CHECK-RAILS AND RACK RAILS, SWITCH BLADES, CROSSING FROGS, POINT RODS AND OTHER CROSSING PIECES, SLEEPERS (CROSS-TIES), FISH-PLATES, CHAIRS, CHAIR WEDGES, SOLE PLATES (BASE PLATES), RAIL CLIPS, BEDPLATES, TIES AND OTHER MATERIAL SPECIALIZED FOR JOINTING OR FIXING RAILS",
+        "hsn_code": "7302",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHEET PILING OF IRON OR STEEL, WHETHER OR NOT DRILLED, PUNCHED OR MADE FROM\nASSEMBLED ELEMENTS; WELDED ANGLES, SHAPES AND SECTIONS, OF IRON OR STEEL - SHEET\nPILING",
+        "hsn_code": "73011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHEET PILING OF IRON OR STEEL, WHETHER OR NOT DRILLED, PUNCHED OR MADE FROM\nASSEMBLED ELEMENTS; WELDED ANGLES, SHAPES AND SECTIONS, OF IRON OR STEEL - ANGLES,\nSHAPES AND SECTIONS : STEEL SLOTTED ANGLES",
+        "hsn_code": "73012010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHEET PILING OF IRON OR STEEL, WHETHER OR NOT DRILLED, PUNCHED OR MADE FROM\nASSEMBLED ELEMENTS; WELDED ANGLES, SHAPES AND SECTIONS, OF IRON OR STEEL - ANGLES,\nSHAPES AND SECTIONS : OTHER",
+        "hsn_code": "73012090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHEET PILING OF IRON OR STEEL, WHETHER OR NOT DRILLED, PUNCHED OR MADE FROM ASSEMBLED ELEMENTS; WELDED ANGLES, SHAPES AND SECTIONS, OF IRON OR STEEL",
+        "hsn_code": "7301",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL OF HIGH SPEED STEEL",
+        "hsn_code": "72291000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL OF SILICO-MANGANESE STEEL",
+        "hsn_code": "72292000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : TINNED WIRE, BRONZE COATED WIRE, TRAPEZOIDAL\nWIRE, HALF ROUND WIRE, CRIMPED WIRE AND COPPER COATED WIRE, NOT INSULATED :\nTINNED WIRE",
+        "hsn_code": "72299011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : TINNED WIRE, BRONZE COATED WIRE, TRAPEZOIDAL\nWIRE, HALF ROUND WIRE, CRIMPED WIRE AND COPPER COATED WIRE, NOT INSULATED :\nBRONZE COATED WIRE",
+        "hsn_code": "72299012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : TINNED WIRE, BRONZE COATED WIRE, TRAPEZOIDAL\nWIRE, HALF ROUND WIRE, CRIMPED WIRE AND COPPER COATED WIRE, NOT INSULATED :\nTRAPEZOIDAL WIRE",
+        "hsn_code": "72299013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : TINNED WIRE, BRONZE COATED WIRE, TRAPEZOIDAL\nWIRE, HALF ROUND WIRE, CRIMPED WIRE AND COPPER COATED WIRE, NOT INSULATED : HALF\nROUND WIRE",
+        "hsn_code": "72299014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : TINNED WIRE, BRONZE COATED WIRE, TRAPEZOIDAL\nWIRE, HALF ROUND WIRE, CRIMPED WIRE AND COPPER COATED WIRE, NOT INSULATED :\nCRIMPED WIRE",
+        "hsn_code": "72299015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : TINNED WIRE, BRONZE COATED WIRE, TRAPEZOIDAL\nWIRE, HALF ROUND WIRE, CRIMPED WIRE AND COPPER COATED WIRE, NOT INSULATED :\nCOPPER COATED WIRE",
+        "hsn_code": "72299016",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : ELECTRODE QUALITY, WIRE ROPE QUALITY AND ACSR\nQUALITY NOT INSULATED : ELECTRODE QUALITY",
+        "hsn_code": "72299021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : ELECTRODE QUALITY, WIRE ROPE QUALITY AND ACSR\nQUALITY NOT INSULATED : WIRE ROPE QUALITY",
+        "hsn_code": "72299022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : ELECTRODE QUALITY, WIRE ROPE QUALITY AND ACSR\nQUALITY NOT INSULATED : ACSR QUALITY",
+        "hsn_code": "72299023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : WIRE (EXCLUDING WIRE TYPE LEAD), SPRING, HIGH\nTENSILE, HARDENED AND TEMPERED WIRES, NOT INSULATED : WIRE (EXCLUDING WIRE TYPE\nLEAD)",
+        "hsn_code": "72299031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : WIRE (EXCLUDING WIRE TYPE LEAD), SPRING, HIGH\nTENSILE, HARDENED AND TEMPERED WIRES, NOT INSULATED : SPRING WIRE",
+        "hsn_code": "72299032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : WIRE (EXCLUDING WIRE TYPE LEAD), SPRING, HIGH\nTENSILE, HARDENED AND TEMPERED WIRES, NOT INSULATED : HIGH TENSILE WIRE",
+        "hsn_code": "72299033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : WIRE (EXCLUDING WIRE TYPE LEAD), SPRING, HIGH\nTENSILE, HARDENED AND TEMPERED WIRES, NOT INSULATED : HARDENED AND TEMPERED\nWIRE",
+        "hsn_code": "72299034",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : WIRE (EXCLUDING WIRE TYPE LEAD), SPRING, HIGH\nTENSILE, HARDENED AND TEMPERED WIRES, NOT INSULATED : OTHER WIRE",
+        "hsn_code": "72299040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : SHAPED AND PROFILED WIRES OF CROSS-SECTION : HALF\nROUND",
+        "hsn_code": "72299051",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : SHAPED AND PROFILED WIRES OF CROSS-SECTION : FLAT\nAND RECTANGULAR",
+        "hsn_code": "72299052",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : SHAPED AND PROFILED WIRES OF CROSS-SECTION : `L\nSHAPE",
+        "hsn_code": "72299053",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : SHAPED AND PROFILED WIRES OF CROSS-SECTION : `Z\nSHAPE",
+        "hsn_code": "72299054",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : SHAPED AND PROFILED WIRES OF CROSS-SECTION :\nOTHER",
+        "hsn_code": "72299059",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : WIRE (EXCLUDING WIRE TYPE LEAD), SPRING, HIGH\nTENSILE, HARDENED AND TEMPERED WIRES, NOT INSULATED : ELECTRIC RESISTANCE WIRE\n(INCLUDING ELECTRIC RESISTANCE HEATING WIRE)",
+        "hsn_code": "72299060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : WIRE (EXCLUDING WIRE TYPE LEAD), SPRING, HIGH\nTENSILE, HARDENED AND TEMPERED WIRES, NOT INSULATED : CRIMPED WIRE",
+        "hsn_code": "72299070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL - OTHER : WIRE (EXCLUDING WIRE TYPE LEAD), SPRING, HIGH\nTENSILE, HARDENED AND TEMPERED WIRES, NOT INSULATED : OTHER",
+        "hsn_code": "72299090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF OTHER ALLOY STEEL",
+        "hsn_code": "7229",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ALLOY STEEL IN INGOTS OR OTHER PRIMARY FORMS; SEMI-FINISHED PRODUCTS OF\nOTHER ALLOY STEEL INGOTS AND OTHER PRIMARY FORMS",
+        "hsn_code": "72241000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ALLOY STEEL IN INGOTS OR OTHER PRIMARY FORMS; SEMI-FINISHED PRODUCTS OF\nOTHER ALLOY STEEL - OTHER : OF TOOL STEEL QUALITY",
+        "hsn_code": "72249010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ALLOY STEEL IN INGOTS OR OTHER PRIMARY FORMS; SEMI-FINISHED PRODUCTS OF\nOTHER ALLOY STEEL - OTHER : OF DIE STEEL QUALITY",
+        "hsn_code": "72249020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ALLOY STEEL IN INGOTS OR OTHER PRIMARY FORMS; SEMI-FINISHED PRODUCTS OF\nOTHER ALLOY STEEL - OTHER : OF COBALT BEARING HIGH SPEED STEEL QUALITY",
+        "hsn_code": "72249030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ALLOY STEEL IN INGOTS OR OTHER PRIMARY FORMS; SEMI-FINISHED PRODUCTS OF\nOTHER ALLOY STEEL - OTHER : FORGED BLANKS OF ALLOY STEEL",
+        "hsn_code": "72249040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ALLOY STEEL IN INGOTS OR OTHER PRIMARY FORMS; SEMI-FINISHED PRODUCTS OF\nOTHER ALLOY STEEL - OTHER : OTHER : BILLETS",
+        "hsn_code": "72249091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ALLOY STEEL IN INGOTS OR OTHER PRIMARY FORMS; SEMI-FINISHED PRODUCTS OF\nOTHER ALLOY STEEL - OTHER : OTHER : OTHER",
+        "hsn_code": "72249099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ALLOY STEEL IN INGOTS OR OTHER PRIMARY FORMS; SEMI-FINISHED PRODUCTS OF OTHER ALLOY STEEL",
+        "hsn_code": "7224",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF STAINLESS STEEL - WIRE OF STAINLESS STEEL :ELECTRODE QUALITY",
+        "hsn_code": "72230010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF STAINLESS STEEL - WIRE OF STAINLESS STEEL : OTHER : OF THICKNESS OF ABOVE 1.5\nMM",
+        "hsn_code": "72230091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF STAINLESS STEEL - WIRE OF STAINLESS STEEL : OTHER : OF THICKNESS OF 0.46 MM AND\nABOVE BUT NOT EXCEEDING 1.5 MM",
+        "hsn_code": "72230092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF STAINLESS STEEL - WIRE OF STAINLESS STEEL : OTHER : OF THICKNESS OF BELOW 0.46\nMM",
+        "hsn_code": "72230099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF STAINLESS STEEL",
+        "hsn_code": "7223",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STAINLESS STEEL IN INGOTS OR OTHER PRIMARY FORMS; SEMIFINISHED PRODUCTS OF\nSTAINLESS STEEL - INGOTS AND OTHER PRIMARY FORMS",
+        "hsn_code": "72181000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STAINLESS STEEL IN INGOTS OR OTHER PRIMARY FORMS; SEMIFINISHED PRODUCTS OF\nSTAINLESS STEEL - OTHER : OF RECTANGULAR (OTHER THAN SQUARE) CROSS-SECTION",
+        "hsn_code": "72189100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STAINLESS STEEL IN INGOTS OR OTHER PRIMARY FORMS; SEMIFINISHED PRODUCTS OF\nSTAINLESS STEEL - OTHER : OTHER : BILLETS",
+        "hsn_code": "72189910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STAINLESS STEEL IN INGOTS OR OTHER PRIMARY FORMS; SEMIFINISHED PRODUCTS OF\nSTAINLESS STEEL - OTHER : OTHER : OTHER",
+        "hsn_code": "72189990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STAINLESS STEEL IN INGOTS OR OTHER PRIMARY FORMS; SEMI-FINISHED PRODUCTS OF STAINLESS STEEL",
+        "hsn_code": "7218",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF IRON OR NON-ALLOY STEEL - NOT PLATED OR COATED, WHETHER OR NOT POLISHED :\nOF A THICKNESS OF 18 SWG AND BELOW",
+        "hsn_code": "72171010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF IRON OR NON-ALLOY STEEL - NOT PLATED OR COATED, WHETHER OR NOT POLISHED :\nOF A THICKNESS ABOVE 18 SWG BUT UP TO 26 SWG",
+        "hsn_code": "72171020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF IRON OR NON-ALLOY STEEL - NOT PLATED OR COATED, WHETHER OR NOT POLISHED :\nOF A THICKNESS ABOVE 26 SWG",
+        "hsn_code": "72171030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF IRON OR NON-ALLOY STEEL - PLATED OR COATED WITH ZINC : OF A THICKNESS OF 18\nSWG AND BELOW",
+        "hsn_code": "72172010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF IRON OR NON-ALLOY STEEL - PLATED OR COATED WITH ZINC : OF A THICKNESS ABOVE\n18 SWG BUT UP TO 26 SWG",
+        "hsn_code": "72172020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF IRON OR NON-ALLOY STEEL - PLATED OR COATED WITH ZINC : OF A THICKNESS ABOVE\n26 SWG",
+        "hsn_code": "72172030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF IRON OR NON-ALLOY STEEL - PLATED OR COATED WITH OTHER BASE METALS : OF A\nTHICKNESS OF 18 SWG AND BELOW",
+        "hsn_code": "72173010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF IRON OR NON-ALLOY STEEL - PLATED OR COATED WITH OTHER BASE METALS : OF A\nTHICKNESS ABOVE 18 SWG BUT UP TO 26 SWG",
+        "hsn_code": "72173020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF IRON OR NON-ALLOY STEEL - PLATED OR COATED WITH OTHER BASE METALS : OF A\nTHICKNESS ABOVE 26 SWG",
+        "hsn_code": "72173030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF IRON OR NON-ALLOY STEEL - OTHER : SHAPED AND PROFILED WIRE : OF CROSS\nSECTION HALF ROUND",
+        "hsn_code": "72179011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF IRON OR NON-ALLOY STEEL - OTHER : SHAPED AND PROFILED WIRE : OF CROSS\nSECTION FLAT AND RECTANGULAR",
+        "hsn_code": "72179012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF IRON OR NON-ALLOY STEEL - OTHER : SHAPED AND PROFILED WIRE : OF CROSS\nSECTION `Z SHAPED",
+        "hsn_code": "72179013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF IRON OR NON-ALLOY STEEL - OTHER : SHAPED AND PROFILED WIRE : OF CROSS\nSECTION OTHER SHAPES",
+        "hsn_code": "72179019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF IRON OR NON-ALLOY STEEL - OTHER : OTHER : HIGH TENSILE QUALITY",
+        "hsn_code": "72179091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF IRON OR NON-ALLOY STEEL - OTHER : OTHER : ELECTRODE QUALITY",
+        "hsn_code": "72179092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF IRON OR NON-ALLOY STEEL - OTHER : OTHER : ELECTRIC RESISTANCE WIRE (INCLUDING\nELECTRIC RESISTANCE HEATING WIRE)",
+        "hsn_code": "72179093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF IRON OR NON-ALLOY STEEL - OTHER : OTHER : OTHER",
+        "hsn_code": "72179099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIRE OF IRON OR NON-ALLOY STEEL",
+        "hsn_code": "7217",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANGLES, SHAPES AND SECTIONS OF IRON OR NON-ALLOY STEEL U, I OR H SECTIONS, NOT\nFURTHER WORKED THAN HOT-ROLLED, HOT-DRAWN OR EXTRUDED, OF A HEIGHT OF LESS THAN\n80 MM",
+        "hsn_code": "72161000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANGLES, SHAPES AND SECTIONS OF IRON OR NON-ALLOY STEEL - L OR T SECTIONS, NOT\nFURTHER WORKED THAN HOTROLLED, HOT-DRAWN OR EXTRUDED, OF A HEIGHT OF LESS THAN\n80 MM : L SECTIONS",
+        "hsn_code": "72162100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANGLES, SHAPES AND SECTIONS OF IRON OR NON-ALLOY STEEL - L OR T SECTIONS, NOT\nFURTHER WORKED THAN HOTROLLED, HOT-DRAWN OR EXTRUDED, OF A HEIGHT OF LESS THAN\n80 MM : T SECTIONS",
+        "hsn_code": "72162200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANGLES, SHAPES AND SECTIONS OF IRON OR NON-ALLOY STEEL - U, I OR H SECTIONS, NOT\nFURTHER WORKED THAN HOTROLLED, HOT-DRAWN OR EXTRUDED OF A HEIGHT OF 80 MM OR\nMORE : U SECTIONS",
+        "hsn_code": "72163100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANGLES, SHAPES AND SECTIONS OF IRON OR NON-ALLOY STEEL - U, I OR H SECTIONS, NOT\nFURTHER WORKED THAN HOTROLLED, HOT-DRAWN OR EXTRUDED OF A HEIGHT OF 80 MM OR\nMORE : I SECTIONS",
+        "hsn_code": "72163200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANGLES, SHAPES AND SECTIONS OF IRON OR NON-ALLOY STEEL - U, I OR H SECTIONS, NOT\nFURTHER WORKED THAN HOTROLLED, HOT-DRAWN OR EXTRUDED OF A HEIGHT OF 80 MM OR\nMORE : H SECTIONS",
+        "hsn_code": "72163300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANGLES, SHAPES AND SECTIONS OF IRON OR NON-ALLOY STEEL - L OR T SECTIONS, NOT\nFURTHER WORKED THAN HOT-ROLLED, HOT-DRAWN OR EXTRUDED, OF A HEIGHT OF 80 MM OR\nMORE",
+        "hsn_code": "72164000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANGLES, SHAPES AND SECTIONS OF IRON OR NON-ALLOY STEEL - OTHER ANGLES, SHAPES AND\nSECTIONS, NOT FURTHER WORKED THAN HOT-ROLLED, HOT-DRAWN OR EXTRUDED",
+        "hsn_code": "72165000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANGLES, SHAPES AND SECTIONS OF IRON OR NON-ALLOY STEEL - ANGLES, SHAPES AND\nSECTIONS, NOT FURTHER WORKED THAN COLD-FORMED OR COLD-FINISHED : OBTAINED FROM\nFLAT-ROLLED PRODUCTS",
+        "hsn_code": "72166100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANGLES, SHAPES AND SECTIONS OF IRON OR NON-ALLOY STEEL - ANGLES, SHAPES AND\nSECTIONS, NOT FURTHER WORKED THAN COLD-FORMED OR COLD-FINISHED : OTHER",
+        "hsn_code": "72166900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANGLES, SHAPES AND SECTIONS OF IRON OR NON-ALLOY STEEL - OTHER: COLD-FORMED OR\nCOLD-FINISHED FROM FLAT ROLLED PRODUCTS",
+        "hsn_code": "72169100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANGLES, SHAPES AND SECTIONS OF IRON OR NON-ALLOY STEEL - OTHER: OTHER : PLATED OR\nCOATED WITH ZINC",
+        "hsn_code": "72169910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANGLES, SHAPES AND SECTIONS OF IRON OR NON-ALLOY STEEL - OTHER: OTHER : PLATED OR\nCOATED WITH BASE METALS OTHER THAN ZINC",
+        "hsn_code": "72169920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANGLES, SHAPES AND SECTIONS OF IRON OR NON-ALLOY STEEL - OTHER: OTHER : SLOTTED\nANGLES AND SLOTTED CHANNELS",
+        "hsn_code": "72169930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANGLES, SHAPES AND SECTIONS OF IRON OR NON-ALLOY STEEL - OTHER: OTHER : FORGED",
+        "hsn_code": "72169940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANGLES, SHAPES AND SECTIONS OF IRON OR NON-ALLOY STEEL - OTHER: OTHER : OTHER",
+        "hsn_code": "72169990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANGLES, SHAPES AND SECTIONS OF IRON OR NON-ALLOY STEEL",
+        "hsn_code": "7216",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL - CONTAINING BY WEIGHT LESS THAN\n0.25% OF CARBON : 7207 11 SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL -\nCONTAINING BY WEIGHT LESS THAN 0.25% OF CARBON : OF RECTANGULAR (INCLUDING\nSQUARE) CROSS-SECTION, THE WIDTH MEASURING LESS THAN TWICE THE THICKNESS:\nELECTRICAL QUALITY",
+        "hsn_code": "72071110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL - CONTAINING BY WEIGHT LESS THAN\n0.25% OF CARBON : 7207 11 SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL -\nCONTAINING BY WEIGHT LESS THAN 0.25% OF CARBON : OF RECTANGULAR (INCLUDING\nSQUARE) CROSS-SECTION, THE WIDTH MEASURING LESS THAN TWICE THE THICKNESS: FORGING\nQUALITY",
+        "hsn_code": "72071120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL - CONTAINING BY WEIGHT LESS THAN\n0.25% OF CARBON : 7207 11 SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL -\nCONTAINING BY WEIGHT LESS THAN 0.25% OF CARBON : OF RECTANGULAR (INCLUDING\nSQUARE) CROSS-SECTION, THE WIDTH MEASURING LESS THAN TWICE THE THICKNESS:\nSEAMLESS STEEL TUBE QUALITY",
+        "hsn_code": "72071130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL - CONTAINING BY WEIGHT LESS THAN\n0.25% OF CARBON : 7207 11 SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL -\nCONTAINING BY WEIGHT LESS THAN 0.25% OF CARBON : OF RECTANGULAR (INCLUDING\nSQUARE) CROSS-SECTION, THE WIDTH MEASURING LESS THAN TWICE THE THICKNESS: OTHER",
+        "hsn_code": "72071190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL - CONTAINING BY WEIGHT LESS THAN\n0.25% OF CARBON : OTHER, OF RECTANGULAR (OTHER THAN SQUARE) CROSSSECTION :\nELECTRICAL QUALITY",
+        "hsn_code": "72071210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL - CONTAINING BY WEIGHT LESS THAN\n0.25% OF CARBON : OTHER, OF RECTANGULAR (OTHER THAN SQUARE) CROSSSECTION :\nFORGING QUALITY",
+        "hsn_code": "72071220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL - CONTAINING BY WEIGHT LESS THAN\n0.25% OF CARBON : OTHER, OF RECTANGULAR (OTHER THAN SQUARE) CROSSSECTION :\nSEAMLESS STEEL TUBE QUALITY",
+        "hsn_code": "72071230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL - CONTAINING BY WEIGHT LESS THAN\n0.25% OF CARBON : OTHER, OF RECTANGULAR (OTHER THAN SQUARE) CROSSSECTION : OTHER",
+        "hsn_code": "72071290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL - CONTAINING BY WEIGHT LESS THAN\n0.25% OF CARBON : OTHER :FORGED BLANKS OF NON-ALLOY STEEL",
+        "hsn_code": "72071910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL - CONTAINING BY WEIGHT LESS THAN\n0.25% OF CARBON : OTHER :MILD STEEL BILLETS",
+        "hsn_code": "72071920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL - CONTAINING BY WEIGHT LESS THAN\n0.25% OF CARBON : OTHER : OTHER",
+        "hsn_code": "72071990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL - CONTAINING BY WEIGHT 0.25% OR\nMORE OF CARBON : FORGING QUALITY",
+        "hsn_code": "72072010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL - CONTAINING BY WEIGHT 0.25% OR\nMORE OF CARBON : SPRING STEEL QUALITY",
+        "hsn_code": "72072020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL - CONTAINING BY WEIGHT 0.25% OR\nMORE OF CARBON : SEAMLESS STEEL TUBE QUALITY",
+        "hsn_code": "72072030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL - CONTAINING BY WEIGHT 0.25% OR\nMORE OF CARBON : OTHER",
+        "hsn_code": "72072090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEMI-FINISHED PRODUCTS OF IRON OR NON-ALLOY STEEL",
+        "hsn_code": "7207",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IRON AND NON-ALLOY STEEL IN INGOTS OR OTHER PRIMARY FORMS (EXCLUDING IRON OF\nHEADING 7203) - INGOTS : OF IRON",
+        "hsn_code": "72061010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IRON AND NON-ALLOY STEEL IN INGOTS OR OTHER PRIMARY FORMS (EXCLUDING IRON OF\nHEADING 7203) - INGOTS : OF HIGH CARBON STEEL",
+        "hsn_code": "72061020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IRON AND NON-ALLOY STEEL IN INGOTS OR OTHER PRIMARY FORMS (EXCLUDING IRON OF\nHEADING 7203) - INGOTS : OTHER",
+        "hsn_code": "72061090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IRON AND NON-ALLOY STEEL IN INGOTS OR OTHER PRIMARY FORMS (EXCLUDING IRON OF\nHEADING 7203) - OTHER : OF IRON : PUDDLED BARS AND PILINGS",
+        "hsn_code": "72069011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IRON AND NON-ALLOY STEEL IN INGOTS OR OTHER PRIMARY FORMS (EXCLUDING IRON OF\nHEADING 7203) - OTHER : OF IRON : BLOCKS, LUMPS AND SIMILAR FORMS",
+        "hsn_code": "72069012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IRON AND NON-ALLOY STEEL IN INGOTS OR OTHER PRIMARY FORMS (EXCLUDING IRON OF\nHEADING 7203) - OTHER : OF IRON : OTHER",
+        "hsn_code": "72069019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IRON AND NON-ALLOY STEEL IN INGOTS OR OTHER PRIMARY FORMS (EXCLUDING IRON OF\nHEADING 7203) - OTHER : OTHER : PUDDLED BARS AND PILINGS",
+        "hsn_code": "72069091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IRON AND NON-ALLOY STEEL IN INGOTS OR OTHER PRIMARY FORMS (EXCLUDING IRON OF\nHEADING 7203) - OTHER : OTHER : BLOCKS, LUMPS AND SIMILAR FORMS",
+        "hsn_code": "72069092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IRON AND NON-ALLOY STEEL IN INGOTS OR OTHER PRIMARY FORMS (EXCLUDING IRON OF\nHEADING 7203) - OTHER : OTHER : OTHER",
+        "hsn_code": "72069099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IRON AND NON-ALLOY STEEL IN INGOTS OTHER PRIMARY FORMS (EXCLUDING IRON OF HEADING 7203)",
+        "hsn_code": "7206",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GRANULES AND POWDERS, OF PIG IRON, SPIEGELEISEN, IRON OR STEEL - GRANULES : OF IRON :\nSHOT AND ANGULAR GRIT",
+        "hsn_code": "72051011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GRANULES AND POWDERS, OF PIG IRON, SPIEGELEISEN, IRON OR STEEL - GRANULES : OF IRON :\nWIRE PELLETS",
+        "hsn_code": "72051012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GRANULES AND POWDERS, OF PIG IRON, SPIEGELEISEN, IRON OR STEEL - GRANULES : OF IRON :\nOTHER",
+        "hsn_code": "72051019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GRANULES AND POWDERS, OF PIG IRON, SPIEGELEISEN, IRON OR STEEL - GRANULES : OF ALLOY\nSTEEL : SHOT AND ANGULAR GRIT",
+        "hsn_code": "72051021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GRANULES AND POWDERS, OF PIG IRON, SPIEGELEISEN, IRON OR STEEL - GRANULES : OF ALLOY\nSTEEL : WIRE PELLETS",
+        "hsn_code": "72051022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GRANULES AND POWDERS, OF PIG IRON, SPIEGELEISEN, IRON OR STEEL - GRANULES : OF ALLOY\nSTEEL : OTHER",
+        "hsn_code": "72051029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GRANULES AND POWDERS, OF PIG IRON, SPIEGELEISEN, IRON OR STEEL - GRANULES : OTHER",
+        "hsn_code": "72051090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GRANULES AND POWDERS, OF PIG IRON, SPIEGELEISEN, IRON OR STEEL - POWDERS : OF ALLOY\nSTEEL",
+        "hsn_code": "72052100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GRANULES AND POWDERS, OF PIG IRON, SPIEGELEISEN, IRON OR STEEL - POWDERS : OTHER : OF\nIRON",
+        "hsn_code": "72052910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GRANULES AND POWDERS, OF PIG IRON, SPIEGELEISEN, IRON OR STEEL - POWDERS : OTHER :\nOTHER",
+        "hsn_code": "72052990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GRANULES AND POWDERS, OF PIG IRON, SPIEGELEISEN, IRON OR STEEL",
+        "hsn_code": "7205",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERROUS WASTE AND SCRAP; REMELTING SCRAP INGOTS OF IRON OR STEEL - WASTE AND\nSCRAP OF CAST IRON",
+        "hsn_code": "72041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERROUS WASTE AND SCRAP; REMELTING SCRAP INGOTS OF IRON OR STEEL - WASTE AND\nSCRAP OF ALLOY STEEL : OF STAINLESS STEEL : EMPTY OR DISCHARGED CARTRIDGES OF ALL\nBORES AND SIZES",
+        "hsn_code": "72042110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERROUS WASTE AND SCRAP; REMELTING SCRAP INGOTS OF IRON OR STEEL - WASTE AND\nSCRAP OF ALLOY STEEL : OF STAINLESS STEEL : OTHER",
+        "hsn_code": "72042190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERROUS WASTE AND SCRAP; REMELTING SCRAP INGOTS OF IRON OR STEEL - WASTE AND\nSCRAP OF ALLOY STEEL : OTHER : EMPTY OR DISCHARGED CARTRIDGES OF ALL BORES AND SIZES",
+        "hsn_code": "72042910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERROUS WASTE AND SCRAP; REMELTING SCRAP INGOTS OF IRON OR STEEL - WASTE AND\nSCRAP OF ALLOY STEEL : OTHER : OF HIGH SPEED STEEL",
+        "hsn_code": "72042920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERROUS WASTE AND SCRAP; REMELTING SCRAP INGOTS OF IRON OR STEEL - WASTE AND\nSCRAP OF ALLOY STEEL : OTHER : OTHER",
+        "hsn_code": "72042990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERROUS WASTE AND SCRAP; REMELTING SCRAP INGOTS OF IRON OR STEEL - WASTE AND\nSCRAP OF TINNED IRON OR STEEL",
+        "hsn_code": "72043000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERROUS WASTE AND SCRAP; REMELTING SCRAP INGOTS OF IRON OR STEEL - OTHER WASTE\nAND SCRAP : TURNINGS, SHAVINGS, CHIPS, MILLING WASTE, SAW DUST, FILLINGS, TRIMMINGS\nAND STAMPINGS, WHETHER OR NOT ` IN BUNDLES",
+        "hsn_code": "72044100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERROUS WASTE AND SCRAP; REMELTING SCRAP INGOTS OF IRON OR STEEL - OTHER WASTE\nAND SCRAP : OTHER",
+        "hsn_code": "72044900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERROUS WASTE AND SCRAP; REMELTING SCRAP INGOTS OF IRON OR STEEL - REMELTING SCRAP\nINGOTS",
+        "hsn_code": "72045000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERROUS WASTE AND SCRAP; REMELTING SCRAP INGOTS OF IRON OR STEEL",
+        "hsn_code": "7204",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERROUS PRODUCTS OBTAINED BY DIRECT REDUCTION OF IRON ORE AND OTHER SPONGY\nFERROUS PRODUCTS, IN LUMPS, PELLETS OR SIMILAR FORMS; IRON HAVING MINIMUM PURITY\nBY WEIGHT OF 99.94%, IN LUMPS, PELLETS OR SIMILAR FORMS - FERROUS PRODUCTS OBTAINED\nBY DIRECT REDUCTION OF IRON ORE",
+        "hsn_code": "72031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERROUS PRODUCTS OBTAINED BY DIRECT REDUCTION OF IRON ORE AND OTHER SPONGY\nFERROUS PRODUCTS, IN LUMPS, PELLETS OR SIMILAR FORMS; IRON HAVING MINIMUM PURITY\nBY WEIGHT OF 99.94%, IN LUMPS, PELLETS OR SIMILAR FORMS - OTHER",
+        "hsn_code": "72039000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERROUS PRODUCTS OBTAINED BY DIRECT REDUCTION OF IRON ORE AND OTHER SPONGY FERROUS PRODUCTS, IN LUMPS, PELLETS OR SIMILAR FORMS; IRON HAVING MINIMUM PURITY BY WEIGHT OF 99.94%, IN LUMPS, PELLETS OR SIMILAR FORMS",
+        "hsn_code": "7203",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - FERRO-MANGANESE : CONTAINING BY WEIGHT MORE THAN 2% OF CARBON",
+        "hsn_code": "72021100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - FERRO-MANGANESE : OTHER",
+        "hsn_code": "72021900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - FERRO-SILICON: CONTAINING BY WEIGHT MORE THAN 55% OF SILICON",
+        "hsn_code": "72022100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - FERRO-SILICON: OTHER",
+        "hsn_code": "72022900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - FERRO-SILICO-MANGANESE",
+        "hsn_code": "72023000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - FERRO-CHROMIUM : CONTAINING BY WEIGHT MORE THAN 4% OF CARBON",
+        "hsn_code": "72024100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - FERRO-CHROMIUM : OTHER",
+        "hsn_code": "72024900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - FERRO-SILICO-CHROMIUM",
+        "hsn_code": "72025000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - FERRO-NICKEL",
+        "hsn_code": "72026000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - FERRO-MOLYBDENUM",
+        "hsn_code": "72027000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - FERRO TUNGSTEN AND FERRO-SILICO-TUNGSTEN",
+        "hsn_code": "72028000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - OTHER : FERRO-TITANIUM AND FERRO-SILICO-TITANIUM",
+        "hsn_code": "72029100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - OTHER : FERRO-VANADIUM",
+        "hsn_code": "72029200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - OTHER : FERRO-NIOBIUM",
+        "hsn_code": "72029300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - OTHER : OTHER : FERRO-PHOSPHORUS, FERRO-SELENIUM, FERROCOBALT,\nFERRO-COLUMBIUM, FERRO-ZIRCONIUM, FERRO-TANTALUM: FERRO-PHOSPHORUS",
+        "hsn_code": "72029911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - OTHER : OTHER : FERRO-PHOSPHORUS, FERRO-SELENIUM, FERROCOBALT,\nFERRO-COLUMBIUM, FERRO-ZIRCONIUM, FERRO-TANTALUM: FERRO-SELENIUM",
+        "hsn_code": "72029912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - OTHER : OTHER : FERRO-PHOSPHORUS, FERRO-SELENIUM, FERROCOBALT,\nFERRO-COLUMBIUM, FERRO-ZIRCONIUM, FERRO-TANTALUM: FERRO-COBALT",
+        "hsn_code": "72029913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - OTHER : OTHER : FERRO-PHOSPHORUS, FERRO-SELENIUM, FERROCOBALT,\nFERRO-COLUMBIUM, FERRO-ZIRCONIUM, FERRO-TANTALUM: FERRO-COLUMBIUM",
+        "hsn_code": "72029914",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - OTHER : OTHER : FERRO-PHOSPHORUS, FERRO-SELENIUM, FERROCOBALT,\nFERRO-COLUMBIUM, FERRO-ZIRCONIUM, FERRO-TANTALUM: FERRO-ZIRCONIUM",
+        "hsn_code": "72029915",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - OTHER : OTHER : FERRO-PHOSPHORUS, FERRO-SELENIUM, FERROCOBALT,\nFERRO-COLUMBIUM, FERRO-ZIRCONIUM, FERRO-TANTALUM: FERRO-TANTALUM",
+        "hsn_code": "72029916",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - OTHER : OTHER : FERRO-SILICO-ZIRCONIUM, FERRO-SILICO-MAGNESIUM: FERRO\nSILICO-ZIRCONIUM",
+        "hsn_code": "72029921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - OTHER : OTHER : FERRO-SILICO-ZIRCONIUM, FERRO-SILICO-MAGNESIUM: FERRO\nSILICO-MAGNESIUM",
+        "hsn_code": "72029922",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - OTHER : OTHER : FERRO-BORON, CHARGE-CHROME : FERRO-BORON",
+        "hsn_code": "72029931",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - OTHER : OTHER : FERRO-BORON, CHARGE-CHROME : CHARGE-CHROME",
+        "hsn_code": "72029932",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS - OTHER : OTHER : OTHER",
+        "hsn_code": "72029990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-ALLOYS",
+        "hsn_code": "7202",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PIG IRON AND SPIEGELEISEN IN PIGS, BLOCKS OR OTHER PRIMARY FORMS - NON-ALLOY PIG\nIRON CONTAINING BY WEIGHT 0.5% OR LESS OF PHOSPHORUS",
+        "hsn_code": "72011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PIG IRON AND SPIEGELEISEN IN PIGS, BLOCKS OR OTHER PRIMARY FORMS - NON-ALLOY PIG\nIRON CONTAINING BY WEIGHT MORE THAN 0.5% OF PHOSPHORUS",
+        "hsn_code": "72012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PIG IRON AND SPIEGELEISEN IN PIGS, BLOCKS OR OTHER PRIMARY FORMS - ALLOY PIG IRON;\nSPIEGELEISEN : CAST IRON",
+        "hsn_code": "72015010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PIG IRON AND SPIEGELEISEN IN PIGS, BLOCKS OR OTHER PRIMARY FORMS - ALLOY PIG IRON;\nSPIEGELEISEN : OTHER",
+        "hsn_code": "72015090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PIG IRON AND SPIEGELEISEN IN PIGS, BLOCKS OR OTHER PRIMARY FORMS",
+        "hsn_code": "7201",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COIN - COIN (OTHER THAN GOLD COIN), NOT BEING LEGAL TENDER",
+        "hsn_code": "71181000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "COIN - OTHER",
+        "hsn_code": "71189000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "COIN",
+        "hsn_code": "7118",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF NATURAL OR CULTURED PEARLS, PRECIOUS OR SEMI-PRECIOUS STONES(NATURAL,\nSYNTHETIC OR RECONSTRUCTED) - OF NATURAL OR CULTURED PEARLS",
+        "hsn_code": "71161000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF NATURAL OR CULTURED PEARLS, PRECIOUS OR SEMI-PRECIOUS STONES(NATURAL,\nSYNTHETIC OR RECONSTRUCTED) - OF PRECIOUS OR SEMI-PRECIOUS STONES (NATURAL,\nSYNTHETIC OR RECONSTRUCTED)",
+        "hsn_code": "71162000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF NATURAL OR CULTURED PEARLS, PRECIOUS OR SEMI-PRECIOUS STONES (NATURAL, SYNTHETIC OR RECONSTRUCTED)",
+        "hsn_code": "7116",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES PROVISIONALLY PRESERVED(FOR EXAMPLE, BY SULPHUR DIOXIDE GAS,IN BRINE, IN\nSULPHUR WATER OR IN OTHERPRESERVATIVE SOLUTIONS), BUT UNSUITABLEIN THAT STATE FOR\nIMMEDIATE CONSUMPTION MUSHROOMS AND TRUFFLES MUSHROOMS OF THE GENSUS\nAGARICUS",
+        "hsn_code": "7115100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES PROVISIONALLY PRESERVED(FOR EXAMPLE, BY SULPHUR DIOXIDE GAS,IN BRINE, IN\nSULPHUR WATER OR IN OTHERPRESERVATIVE SOLUTIONS), BUT UNSUITABLEIN THAT STATE FOR\nIMMEDIATE CONSUMPTION MUSHROOMS AND TRUFFLES OTHER",
+        "hsn_code": "7115900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL - CATALYSTS\nIN THE FROM OF WIRE CLOTH OR GRILL, OF PLATINUM",
+        "hsn_code": "71151000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL - OTHER:\nLABORATORY AND INDUSTRIAL ARTICLES OF PRECIOUS METAL",
+        "hsn_code": "71159010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL - OTHER:\nSPINNERETS MADE MAINLY OF GOLD",
+        "hsn_code": "71159020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL - OTHER:\nOTHER",
+        "hsn_code": "71159090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL",
+        "hsn_code": "7115",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES PROVISIONALLY PRESERVED(FOR EXAMPLE, BY SULPHUR DIOXIDE GAS,IN BRINE, IN\nSULPHUR WATER OR IN OTHERPRESERVATIVE SOLUTIONS), BUT UNSUITABLEIN THAT STATE FOR\nIMMEDIATE CONSUMPTIONCUCUMBERS AND GHERKINS",
+        "hsn_code": "7114000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF GOLDSMITHS OR SILVERSMITHS WARES AND PARTS THEREOF, OF PRECIOUS METAL\nOR OF METAL CLAD WITH PRECIOUS METAL - OF PRECIOUS METAL, WHETHER OR NOT PLATED\nOR CLAD WITH PRECIOUS METAL: OF SILVER, WHETHER OR NOT PLATED OR CLAD WITH\nPRECIOUS METAL:ARTICLES",
+        "hsn_code": "71141110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF GOLDSMITHS OR SILVERSMITHS WARES AND PARTS THEREOF, OF PRECIOUS METAL\nOR OF METAL CLAD WITH PRECIOUS METAL - OF PRECIOUS METAL, WHETHER OR NOT PLATED\nOR CLAD WITH PRECIOUS METAL: OF SILVER, WHETHER OR NOT PLATED OR CLAD WITH\nPRECIOUS METAL:PARTS",
+        "hsn_code": "71141120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF GOLDSMITHS OR SILVERSMITHS WARES AND PARTS THEREOF, OF PRECIOUS METAL\nOR OF METAL CLAD WITH PRECIOUS METAL - OF PRECIOUS METAL, WHETHER OR NOT PLATED\nOR CLAD WITH PRECIOUS METAL: OF OTHER PRECIOUS METAL, WHETHER OR NOT PLATED OR\nCLAD WITH PRECIOUS METAL: ARTICLES OF GOLD",
+        "hsn_code": "71141910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF GOLDSMITHS OR SILVERSMITHS WARES AND PARTS THEREOF, OF PRECIOUS METAL\nOR OF METAL CLAD WITH PRECIOUS METAL - OF PRECIOUS METAL, WHETHER OR NOT PLATED\nOR CLAD WITH PRECIOUS METAL: OF OTHER PRECIOUS METAL, WHETHER OR NOT PLATED OR\nCLAD WITH PRECIOUS METAL: ARTICLES OF PLATINUM",
+        "hsn_code": "71141920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF GOLDSMITHS OR SILVERSMITHS WARES AND PARTS THEREOF, OF PRECIOUS METAL\nOR OF METAL CLAD WITH PRECIOUS METAL - OF PRECIOUS METAL, WHETHER OR NOT PLATED\nOR CLAD WITH PRECIOUS METAL: OF OTHER PRECIOUS METAL, WHETHER OR NOT PLATED OR\nCLAD WITH PRECIOUS METAL: PARTS",
+        "hsn_code": "71141930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF GOLDSMITHS OR SILVERSMITHS WARES AND PARTS THEREOF, OF PRECIOUS METAL\nOR OF METAL CLAD WITH PRECIOUS METAL - OF BASE METAL CLAD WITH PRECIOUS METAL :\nARTICLES CLAD WITH GOLD",
+        "hsn_code": "71142010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF GOLDSMITHS OR SILVERSMITHS WARES AND PARTS THEREOF, OF PRECIOUS METAL\nOR OF METAL CLAD WITH PRECIOUS METAL - OF BASE METAL CLAD WITH PRECIOUS METAL :\nOTHER ARTICLES",
+        "hsn_code": "71142020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF GOLDSMITHS OR SILVERSMITHS WARES AND PARTS THEREOF, OF PRECIOUS METAL\nOR OF METAL CLAD WITH PRECIOUS METAL - OF BASE METAL CLAD WITH PRECIOUS METAL :\nPARTS",
+        "hsn_code": "71142030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF GOLDSMITHS’ OR SILVERSMITHS’ WARES AND PARTS THEREOF, OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL",
+        "hsn_code": "7114",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES PROVISIONALLY PRESERVED(FOR EXAMPLE, BY SULPHUR DIOXIDE GAS,IN BRINE, IN\nSULPHUR WATER OR IN OTHERPRESERVATIVE SOLUTIONS), BUT UNSUITABLEIN THAT STATE FOR\nIMMEDIATE CONSUMPTION CAPERS",
+        "hsn_code": "7113000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF JEWELLERY AND PARTS THEREOF, OF PRECIOUS METAL OR OF METAL CLAD WITH\nPRECIOUS METAL - OF PRECIOUS METAL WHETHER OR NOT PLATED OR CLAD WITH PRECIOUS\nMETAL - OF SILVER, WHETHER OR NOT PLATED OR CLAD WITH OTHER PRECIOUS METAL:\nJEWELLERY STUDDED WITH GEMS",
+        "hsn_code": "71131120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF JEWELLERY AND PARTS THEREOF, OF PRECIOUS METAL OR OF METAL CLAD WITH\nPRECIOUS METAL - OF PRECIOUS METAL WHETHER OR NOT PLATED OR CLAD WITH PRECIOUS\nMETAL - OF SILVER, WHETHER OR NOT PLATED OR CLAD WITH OTHER PRECIOUS METAL: OTHER\nARTICLES OF JEWELLERY",
+        "hsn_code": "71131130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF JEWELLERY AND PARTS THEREOF, OF PRECIOUS METAL OR OF METAL CLAD WITH\nPRECIOUS METAL - OF PRECIOUS METAL WHETHER OR NOT PLATED OR CLAD WITH PRECIOUS\nMETAL - OF SILVER, WHETHER OR NOT PLATED OR CLAD WITH OTHER PRECIOUS METAL: PARTS",
+        "hsn_code": "71131190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF JEWELLERY AND PARTS THEREOF, OF PRECIOUS METAL OR OF METAL CLAD WITH\nPRECIOUS METAL - OF PRECIOUS METAL WHETHER OR NOT PLATED OR CLAD WITH PRECIOUS\nMETAL - OF OTHER PRECIOUS METAL, WHETHER OR NOT PLATED OR CLAD WITH PRECIOUS\nMETAL: OF GOLD, UNSTUDDED",
+        "hsn_code": "71131910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF JEWELLERY AND PARTS THEREOF, OF PRECIOUS METAL OR OF METAL CLAD WITH\nPRECIOUS METAL - OF PRECIOUS METAL WHETHER OR NOT PLATED OR CLAD WITH PRECIOUS\nMETAL - OF OTHER PRECIOUS METAL, WHETHER OR NOT PLATED OR CLAD WITH PRECIOUS\nMETAL: OF GOLD, SET WITH PEARLS",
+        "hsn_code": "71131920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF JEWELLERY AND PARTS THEREOF, OF PRECIOUS METAL OR OF METAL CLAD WITH\nPRECIOUS METAL - OF PRECIOUS METAL WHETHER OR NOT PLATED OR CLAD WITH PRECIOUS\nMETAL - OF OTHER PRECIOUS METAL, WHETHER OR NOT PLATED OR CLAD WITH PRECIOUS\nMETAL: OF GOLD, SET WITH DIAMONDS",
+        "hsn_code": "71131930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF JEWELLERY AND PARTS THEREOF, OF PRECIOUS METAL OR OF METAL CLAD WITH\nPRECIOUS METAL - OF PRECIOUS METAL WHETHER OR NOT PLATED OR CLAD WITH PRECIOUS\nMETAL - OF OTHER PRECIOUS METAL, WHETHER OR NOT PLATED OR CLAD WITH PRECIOUS\nMETAL: OF GOLD, SET WITH OTHER PRECIOUS AND SEMI- PRECIOUS STONES",
+        "hsn_code": "71131940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF JEWELLERY AND PARTS THEREOF, OF PRECIOUS METAL OR OF METAL CLAD WITH\nPRECIOUS METAL - OF PRECIOUS METAL WHETHER OR NOT PLATED OR CLAD WITH PRECIOUS\nMETAL - OF OTHER PRECIOUS METAL, WHETHER OR NOT PLATED OR CLAD WITH PRECIOUS\nMETAL: OF PLATINUM, UNSTUDDED",
+        "hsn_code": "71131950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF JEWELLERY AND PARTS THEREOF, OF PRECIOUS METAL OR OF METAL CLAD WITH\nPRECIOUS METAL - OF PRECIOUS METAL WHETHER OR NOT PLATED OR CLAD WITH PRECIOUS\nMETAL - OF OTHER PRECIOUS METAL, WHETHER OR NOT PLATED OR CLAD WITH PRECIOUS\nMETAL: PARTS",
+        "hsn_code": "71131960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF JEWELLERY AND PARTS THEREOF, OF PRECIOUS METAL OR OF METAL CLAD WITH\nPRECIOUS METAL - OF PRECIOUS METAL WHETHER OR NOT PLATED OR CLAD WITH PRECIOUS\nMETAL - OF OTHER PRECIOUS METAL, WHETHER OR NOT PLATED OR CLAD WITH PRECIOUS\nMETAL: OTHER",
+        "hsn_code": "71131990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF JEWELLERY AND PARTS THEREOF, OF PRECIOUS METAL OR OF METAL CLAD WITH\nPRECIOUS METAL - OF BASE METAL CLAD WITH PRECIOUS METAL",
+        "hsn_code": "71132000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF JEWELLERY AND PARTS THEREOF, OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL",
+        "hsn_code": "7113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES PROVISIONALLY PRESERVED(FOR EXAMPLE, BY SULPHUR DIOXIDE GAS, BRINE, IN\nSULPHUR WATER OR IN OTHERPRESERVATIVE SOLUTIONS), BUT UNSUITABLEIN THAT STATE FOR\nIMMEDIATE CONSUMPTION OLIVES",
+        "hsn_code": "7112000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "WASTE AND SCRAP OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL; OTHER\nWASTE AND SCRAP CONTAINING PRECIOUS METAL OR PRECIOUS METAL COMPOUNDS, OF A\nKIND USED PRINCIPALLY FOR THE RECOVERY OF PRECIOUS METAL - ASH CONTAINING PRECIOUS\nMETAL OR PRECIOUS METAL COMPOUNDS",
+        "hsn_code": "71123000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "WASTE AND SCRAP OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL; OTHER\nWASTE AND SCRAP CONTAINING PRECIOUS METAL OR PRECIOUS METAL COMPOUNDS, OF A\nKIND USED PRINCIPALLY FOR THE RECOVERY OF PRECIOUS METAL - OTHER : OF GOLD,\nINCLUDING METAL CLAD WITH GOLD BUT EXCLUDING SWEEPINGS CONTAINING OTHER\nPRECIOUS METALS",
+        "hsn_code": "71129100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "WASTE AND SCRAP OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL; OTHER\nWASTE AND SCRAP CONTAINING PRECIOUS METAL OR PRECIOUS METAL COMPOUNDS, OF A\nKIND USED PRINCIPALLY FOR THE RECOVERY OF PRECIOUS METAL - OTHER : OF PLATINUM,\nINCLUDING METAL CLAD WITH PLATINUM BUT EXCLUDING SWEEPINGS CONTAINING OTHER\nPRECIOUS METALS",
+        "hsn_code": "71129200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "WASTE AND SCRAP OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL; OTHER\nWASTE AND SCRAP CONTAINING PRECIOUS METAL OR PRECIOUS METAL COMPOUNDS, OF A\nKIND USED PRINCIPALLY FOR THE RECOVERY OF PRECIOUS METAL - OTHER : OTHER: OF SILVER,\nINCLUDING METAL CLAD WITH SILVER BUT EXCLUDING SWEEPINGS CONTAINING OTHER\nPRECIOUS METALS",
+        "hsn_code": "71129910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "WASTE AND SCRAP OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL; OTHER\nWASTE AND SCRAP CONTAINING PRECIOUS METAL OR PRECIOUS METAL COMPOUNDS, OF A\nKIND USED PRINCIPALLY FOR THE RECOVERY OF PRECIOUS METAL - OTHER : OTHER: SWEEPINGS\nCONTAINING GOLD OR SILVER",
+        "hsn_code": "71129920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "WASTE AND SCRAP OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL; OTHER\nWASTE AND SCRAP CONTAINING PRECIOUS METAL OR PRECIOUS METAL COMPOUNDS, OF A\nKIND USED PRINCIPALLY FOR THE RECOVERY OF PRECIOUS METAL - OTHER : OTHER: OTHER",
+        "hsn_code": "71129990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "WASTE AND SCRAP OF PRECIOUS METAL OR OF METAL CLAD WITH PRECIOUS METAL; OTHER WASTE AND SCRAP CONTAINING PRECIOUS METAL OR PRECIOUS METAL COMPOUNDS, OF A KIND USED PRINCIPALLY FOR THE RECOVERY OF PRECIOUS METAL",
+        "hsn_code": "7112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "BASE METALS, SILVER OR GOLD, CLAD WITH PLATINUM, NOT FURTHER WORKED THAN SEMI\nMANUFACTURED",
+        "hsn_code": "71110000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "Base metals, silver or gold, clad with platinum, not further worked than semi-manufactured",
+        "hsn_code": "7111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "PLATINUM, UNWROUGHT OR IN SEMI-MANUFACTURED FORM, OR IN POWDER FORM -\nPLATINUM : UNWROUGHT OR IN POWDER FORM: UNWROUGHT FORM",
+        "hsn_code": "71101110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "PLATINUM, UNWROUGHT OR IN SEMI-MANUFACTURED FORM, OR IN POWDER FORM -\nPLATINUM : UNWROUGHT OR IN POWDER FORM: IN POWDER FORM",
+        "hsn_code": "71101120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "PLATINUM, UNWROUGHT OR IN SEMI-MANUFACTURED FORM, OR IN POWDER FORM -\nPLATINUM : OTHER",
+        "hsn_code": "71101900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "PLATINUM, UNWROUGHT OR IN SEMI-MANUFACTURED FORM, OR IN POWDER FORM -\nPALLADIUM: UNWROUGHT OR IN POWDER FORM",
+        "hsn_code": "71102100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "PLATINUM, UNWROUGHT OR IN SEMI-MANUFACTURED FORM, OR IN POWDER FORM -\nPALLADIUM: OTHER",
+        "hsn_code": "71102900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "PLATINUM, UNWROUGHT OR IN SEMI-MANUFACTURED FORM, OR IN POWDER FORM -\nRHODIUM: UNWROUGHT OR IN POWDER FROM",
+        "hsn_code": "71103100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "PLATINUM, UNWROUGHT OR IN SEMI-MANUFACTURED FORM, OR IN POWDER FORM -\nRHODIUM: OTHER",
+        "hsn_code": "71103900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "PLATINUM, UNWROUGHT OR IN SEMI-MANUFACTURED FORM, OR IN POWDER FORM - IRIDIUM,\nOSMIUM AND RUTHENIUM: UNWROUGHT OR IN POWDER FROM",
+        "hsn_code": "71104100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "PLATINUM, UNWROUGHT OR IN SEMI-MANUFACTURED FORM, OR IN POWDER FORM - IRIDIUM,\nOSMIUM AND RUTHENIUM: OTHER",
+        "hsn_code": "71104900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "PLATINUM, UNWROUGHT OR IN SEMI- MANUFACTURED FORM, OR IN POWDER FORM",
+        "hsn_code": "7110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES (UNCOOKED OR COOKED BYSTEAMING OR BOILING IN WATER), FROZEN MIXTURES\nOF VEGETABLES",
+        "hsn_code": "7109000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "BASE METALS OR SILVER, CLAD WITH GOLD, NOT FURTHER WORKED THAN SEMI\nMANUFACTURED",
+        "hsn_code": "71090000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "Base metals or silver, clad with gold, not further worked than semi-manufactured",
+        "hsn_code": "7109",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES (UNCOOKED OR COOKED BYSTEAMING OR BOILING IN WATER), FROZEN OTHER\nVEGETABLES TERRAGON",
+        "hsn_code": "7108010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES (UNCOOKED OR COOKED BYSTEAMING OR BOILING IN WATER), FROZEN OTHER\nVEGETABLES OTHER",
+        "hsn_code": "7108090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "GOLD (INCLUDING GOLD PLATED WITH PLATINUM) UNWROUGHT OR IN SEMI-MANUFACTURED\nFORMS, OR IN POWDER FORM - NON-MONETARY : POWDER",
+        "hsn_code": "71081100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "GOLD (INCLUDING GOLD PLATED WITH PLATINUM) UNWROUGHT OR IN SEMI-MANUFACTURED\nFORMS, OR IN POWDER FORM - NON-MONETARY : OTHER UNWROUGHT FORMS",
+        "hsn_code": "71081200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "GOLD (INCLUDING GOLD PLATED WITH PLATINUM) UNWROUGHT OR IN SEMI-MANUFACTURED\nFORMS, OR IN POWDER FORM - NON-MONETARY : OTHER SEMI-MANUFACTURED FORMS",
+        "hsn_code": "71081300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "GOLD (INCLUDING GOLD PLATED WITH PLATINUM) UNWROUGHT OR IN SEMI-MANUFACTURED\nFORMS, OR IN POWDER FORM MONETARY",
+        "hsn_code": "71082000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "GOLD (INCLUDING GOLD PLATED WITH PLATINUM) UNWROUGHT OR IN SEMI-MANUFACTURED FORMS, OR IN POWDER FORM",
+        "hsn_code": "7108",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "BASE METALS CLAD WITH SILVER, NOT FURTHER WORKED THAN SEMI-MANUFACTURED",
+        "hsn_code": "71070000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "Base metals clad with silver, not further worked than semi-manufactured",
+        "hsn_code": "7107",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "SILVER (INCLUDING SILVER PLATED WITH GOLD OR PLATINUM), UNWROUGHT OR IN SEMI\nMANUFACTURED FORMS, OR IN POWDER FORM - POWDER",
+        "hsn_code": "71061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "SILVER (INCLUDING SILVER PLATED WITH GOLD OR PLATINUM), UNWROUGHT OR IN SEMI\nMANUFACTURED FORMS, OR IN POWDER FORM - OTHER : UNWROUGHT",
+        "hsn_code": "71069100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "SILVER (INCLUDING SILVER PLATED WITH GOLD OR PLATINUM), UNWROUGHT OR IN SEMI\nMANUFACTURED FORMS, OR IN POWDER FORM SEMI-MANUFACTURED : SHEETS, PLATES,\nSTRIPS, TUBES AND PIPES",
+        "hsn_code": "71069210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "SILVER (INCLUDING SILVER PLATED WITH GOLD OR PLATINUM), UNWROUGHT OR IN SEMI\nMANUFACTURED FORMS, OR IN POWDER FORM SEMI-MANUFACTURED : OTHER",
+        "hsn_code": "71069290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "SILVER (INCLUDING SILVER PLATED WITH GOLD OR PLATINUM), UNWROUGHT OR IN SEMI-MANUFACTURED FORMS, OR IN POWDER FORM",
+        "hsn_code": "7106",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "DUST AND POWDER OF NATURAL OR SYNTHETIC PRECIOUS OR SEMI-PRECIOUS STONES - OF\nDIAMOND",
+        "hsn_code": "71051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "DUST AND POWDER OF NATURAL OR SYNTHETIC PRECIOUS OR SEMI-PRECIOUS STONES - OTHER",
+        "hsn_code": "71059000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "DUST AND POWDER OF NATURAL OR SYNTHETIC PRECIOUS OR SEMI-PRECIOUS STONES",
+        "hsn_code": "7105",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC OR RECONSTRUCTED PRECIOUS OR SEMI-PRECIOUS STONES, WHETHER OR NOT WORKED OR GRADED BUT NOT STRUNG, MOUNTED OR SET; UNGRADED SYNTHETIC OR RECONSTRUCTED PRECIOUS OR SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR CONVENIENCE OF TRANSPORT",
+        "hsn_code": "7104",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC OR RECONSTRUCTED PRECIOUS OR SEMI-PRECIOUS STONES, WHETHER OR NOT\nWORKED OR GRADED BUT NOT STRUNG,MOUNTED OR SET; UNGRADED SYNTHETIC OR\nRECONSTRUCTED PRECIOUS OR SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR\nCONVENIENCE OF TRANSPORT-OTHER--LABORATORY CREATED OR LABORATORY GROWN OR\nMANMADE OR CULTURED OR SYNTHETIC DIAMONDS--OTHER",
+        "hsn_code": "71049090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC OR RECONSTRUCTED PRECIOUS OR SEMI-PRECIOUS STONES, WHETHER OR NOT\nWORKED OR GRADED BUT NOT STRUNG,MOUNTED OR SET; UNGRADED SYNTHETIC OR\nRECONSTRUCTED PRECIOUS OR SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR\nCONVENIENCE OF TRANSPORT-OTHER--LABORATORY CREATED OR LABORATORY GROWN OR\nMANMADE OR CULTURED OR SYNTHETIC DIAMONDS",
+        "hsn_code": "71049010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC OR RECONSTRUCTED PRECIOUS OR SEMIPRECIOUS STONES, WHETHER OR NOT\nWORKED OR GRADED BUT NOT STRUNG, MOUNTED OR SET; UNGRADED SYNTHETIC OR\nRECONSTRUCTED PRECIOUS OR SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR\nCONVENIENCE OF TRANSPORT - OTHER, UNWORKED OR SIMPLY SAWN OR ROUGHLY SHAPED",
+        "hsn_code": "71042000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC OR RECONSTRUCTED PRECIOUS OR SEMIPRECIOUS STONES, WHETHER OR NOT\nWORKED OR GRADED BUT NOT STRUNG, MOUNTED OR SET; UNGRADED SYNTHETIC OR\nRECONSTRUCTED PRECIOUS OR SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR\nCONVENIENCE OF TRANSPORT - PIEZO-ELECTRIC QUARTZ",
+        "hsn_code": "71041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES (UNCOOKED OR COOKED BYSTEAMING OR BOILING IN WATER), FROZEN SWEET\nCORN",
+        "hsn_code": "7104000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "PRECIOUS STONES (OTHER THAN DIAMONDS) AND SEMI-PRECIOUS STONES, WHETHER OR NOT WORKED OR GRADED BUT NOT STRUNG, MOUNTED OR SET; UNGRADED PRECIOUS STONES (OTHER THAN DIAMONDS) AND SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR CONVENIENCE OF TRANSPORT",
+        "hsn_code": "7103",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "PRECIOUS STONES (OTHER THAN DIAMONDS) AND SEMIPRECIOUS STONES, WHETHER OR NOT\nWORKED OR GRADED BUT NOT STRUNG, MOUNTED OR SET; UNGRADED PRECIOUS STONES\n(OTHER THAN DIAMONDS) AND SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR\nCONVENIENCE OF TRANSPORT - OTHERWISE WORKED: OTHER: OTHER",
+        "hsn_code": "71039990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "PRECIOUS STONES (OTHER THAN DIAMONDS) AND SEMIPRECIOUS STONES, WHETHER OR NOT\nWORKED OR GRADED BUT NOT STRUNG, MOUNTED OR SET; UNGRADED PRECIOUS STONES\n(OTHER THAN DIAMONDS) AND SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR\nCONVENIENCE OF TRANSPORT - OTHERWISE WORKED: OTHER: CHALCEDONY",
+        "hsn_code": "71039940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "PRECIOUS STONES (OTHER THAN DIAMONDS) AND SEMIPRECIOUS STONES, WHETHER OR NOT\nWORKED OR GRADED BUT NOT STRUNG, MOUNTED OR SET; UNGRADED PRECIOUS STONES\n(OTHER THAN DIAMONDS) AND SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR\nCONVENIENCE OF TRANSPORT - OTHERWISE WORKED: OTHER: AGATE",
+        "hsn_code": "71039930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "PRECIOUS STONES (OTHER THAN DIAMONDS) AND SEMIPRECIOUS STONES, WHETHER OR NOT\nWORKED OR GRADED BUT NOT STRUNG, MOUNTED OR SET; UNGRADED PRECIOUS STONES\n(OTHER THAN DIAMONDS) AND SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR\nCONVENIENCE OF TRANSPORT - OTHERWISE WORKED: OTHER: GARNET",
+        "hsn_code": "71039920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "PRECIOUS STONES (OTHER THAN DIAMONDS) AND SEMIPRECIOUS STONES, WHETHER OR NOT\nWORKED OR GRADED BUT NOT STRUNG, MOUNTED OR SET; UNGRADED PRECIOUS STONES\n(OTHER THAN DIAMONDS) AND SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR\nCONVENIENCE OF TRANSPORT - OTHERWISE WORKED: OTHER: FELDSPAR (MOON STONE)",
+        "hsn_code": "71039910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "PRECIOUS STONES (OTHER THAN DIAMONDS) AND SEMIPRECIOUS STONES, WHETHER OR NOT\nWORKED OR GRADED BUT NOT STRUNG, MOUNTED OR SET; UNGRADED PRECIOUS STONES\n(OTHER THAN DIAMONDS) AND SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR\nCONVENIENCE OF TRANSPORT - OTHERWISE WORKED: RUBY, SAPPHIRE AND EMERALDS",
+        "hsn_code": "71039100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "PRECIOUS STONES (OTHER THAN DIAMONDS) AND SEMIPRECIOUS STONES, WHETHER OR NOT\nWORKED OR GRADED BUT NOT STRUNG, MOUNTED OR SET; UNGRADED PRECIOUS STONES\n(OTHER THAN DIAMONDS) AND SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR\nCONVENIENCE OF TRANSPORT - UNWORKED OR SIMPLY SAWN OR ROUGHLY SHAPED: SEMI\nPRECIOUS STONES: OTHER",
+        "hsn_code": "71031029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "PRECIOUS STONES (OTHER THAN DIAMONDS) AND SEMIPRECIOUS STONES, WHETHER OR NOT\nWORKED OR GRADED BUT NOT STRUNG, MOUNTED OR SET; UNGRADED PRECIOUS STONES\n(OTHER THAN DIAMONDS) AND SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR\nCONVENIENCE OF TRANSPORT - UNWORKED OR SIMPLY SAWN OR ROUGHLY SHAPED: SEMI\nPRECIOUS STONES: GREEN AVENTURINE",
+        "hsn_code": "71031024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "PRECIOUS STONES (OTHER THAN DIAMONDS) AND SEMIPRECIOUS STONES, WHETHER OR NOT\nWORKED OR GRADED BUT NOT STRUNG, MOUNTED OR SET; UNGRADED PRECIOUS STONES\n(OTHER THAN DIAMONDS) AND SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR\nCONVENIENCE OF TRANSPORT - UNWORKED OR SIMPLY SAWN OR ROUGHLY SHAPED: SEMI\nPRECIOUS STONES: AGATE",
+        "hsn_code": "71031023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "PRECIOUS STONES (OTHER THAN DIAMONDS) AND SEMIPRECIOUS STONES, WHETHER OR NOT\nWORKED OR GRADED BUT NOT STRUNG, MOUNTED OR SET; UNGRADED PRECIOUS STONES\n(OTHER THAN DIAMONDS) AND SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR\nCONVENIENCE OF TRANSPORT - UNWORKED OR SIMPLY SAWN OR ROUGHLY SHAPED: SEMI\nPRECIOUS STONES: GARNET",
+        "hsn_code": "71031022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "PRECIOUS STONES (OTHER THAN DIAMONDS) AND SEMIPRECIOUS STONES, WHETHER OR NOT\nWORKED OR GRADED BUT NOT STRUNG, MOUNTED OR SET; UNGRADED PRECIOUS STONES\n(OTHER THAN DIAMONDS) AND SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR\nCONVENIENCE OF TRANSPORT - UNWORKED OR SIMPLY SAWN OR ROUGHLY SHAPED: SEMI\nPRECIOUS STONES: FELDSPAR (MOON STONE)",
+        "hsn_code": "71031021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "PRECIOUS STONES (OTHER THAN DIAMONDS) AND SEMIPRECIOUS STONES, WHETHER OR NOT\nWORKED OR GRADED BUT NOT STRUNG, MOUNTED OR SET; UNGRADED PRECIOUS STONES\n(OTHER THAN DIAMONDS) AND SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR\nCONVENIENCE OF TRANSPORT - UNWORKED OR SIMPLY SAWN OR ROUGHLY SHAPED: PRECIOUS\nSTONES: OTHER",
+        "hsn_code": "71031019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "PRECIOUS STONES (OTHER THAN DIAMONDS) AND SEMIPRECIOUS STONES, WHETHER OR NOT\nWORKED OR GRADED BUT NOT STRUNG, MOUNTED OR SET; UNGRADED PRECIOUS STONES\n(OTHER THAN DIAMONDS) AND SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR\nCONVENIENCE OF TRANSPORT - UNWORKED OR SIMPLY SAWN OR ROUGHLY SHAPED: PRECIOUS\nSTONES: RUBY AND SAPPHIRE",
+        "hsn_code": "71031012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "PRECIOUS STONES (OTHER THAN DIAMONDS) AND SEMIPRECIOUS STONES, WHETHER OR NOT\nWORKED OR GRADED BUT NOT STRUNG, MOUNTED OR SET; UNGRADED PRECIOUS STONES\n(OTHER THAN DIAMONDS) AND SEMI-PRECIOUS STONES, TEMPORARILY STRUNG FOR\nCONVENIENCE OF TRANSPORT - UNWORKED OR SIMPLY SAWN OR ROUGHLY SHAPED: PRECIOUS\nSTONES: EMERALD",
+        "hsn_code": "71031011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES (UNCOOKED OR COOKED BYSTEAMING OR BOILING IN WATER), FROZEN SPINACH,\nNEW ZEALAND SPINACH AND ORACHE SPINACH (GARDEN SPINACH)",
+        "hsn_code": "7103000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "DIAMONDS, WHETHER OR NOT WORKED, BUT NOT MOUNTED OR SET",
+        "hsn_code": "7102",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "DIAMONDS, WHETHER OR NOT WORKED, BUT NOT MOUNTED OR SET - NON-INDUSTRIAL:\nOTHERS: OTHER",
+        "hsn_code": "71023990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "DIAMONDS, WHETHER OR NOT WORKED, BUT NOT MOUNTED OR SET - NON-INDUSTRIAL:\nOTHERS: DIAMOND, CUT OR OTHERWISE WORKED BUT NOT MOUNTED OR SET",
+        "hsn_code": "71023910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "DIAMONDS, WHETHER OR NOT WORKED, BUT NOT MOUNTED OR SET - NON-INDUSTRIAL:\nUNWORKED OR SIMPLY SAWN, CLEAVED OR BRUTED",
+        "hsn_code": "71023100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "DIAMONDS, WHETHER OR NOT WORKED, BUT NOT MOUNTED OR SET - INDUSTRIAL : OTHER:\nOTHER",
+        "hsn_code": "71022990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "DIAMONDS, WHETHER OR NOT WORKED, BUT NOT MOUNTED OR SET - INDUSTRIAL : OTHER:\nCRUSHED",
+        "hsn_code": "71022910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "21 DIAMONDS, WHETHER OR NOT WORKED, BUT NOT MOUNTED OR SET - INDUSTRIAL :\nUNWORKED OR SIMPLY SAWN, CLEAVED OR BRUTED: UNSORTED",
+        "hsn_code": "71022120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "21 DIAMONDS, WHETHER OR NOT WORKED, BUT NOT MOUNTED OR SET - INDUSTRIAL :\nUNWORKED OR SIMPLY SAWN, CLEAVED OR BRUTED: SORTED",
+        "hsn_code": "71022110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "DIAMONDS, WHETHER OR NOT WORKED, BUT NOT MOUNTED OR SET - UNSORTED",
+        "hsn_code": "71021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES (UNCOOKED OR COOKED BYSTEAMING OR BOILING IN WATER), FROZEN\nLEGUMINOUS VEGETABLES, SHELLED OR UNSHELLED OTHER",
+        "hsn_code": "7102900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES (UNCOOKED OR COOKED BYSTEAMING OR BOILING IN WATER), FROZEN\nLEGUMINOUS VEGETABLES, SHELLED OR UNSHELLED BEANS (VIGNA SPP., PHASEOLUS SPP.)",
+        "hsn_code": "7102200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES (UNCOOKED OR COOKED BYSTEAMING OR BOILING IN WATER), FROZEN\nLEGUMINOUS VEGETABLES, SHELLED OR UNSHELLED : PEAS (PISUM SATIVUM)",
+        "hsn_code": "7102100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0.25
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES (UNCOOKED OR COOKED BYSTEAMING OR BOILING IN WATER), FROZEN POTATOES",
+        "hsn_code": "7101000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "PEARLS, NATURAL OR CULTURED, WHETHER OR NOT WORKED OR GRADED BUT NOT STRUNG,\nMOUNTED OR SET; PEARLS, NATURAL OR CULTURED, TEMPORARILY STRUNG FOR CONVENIENCE\nOF TRANSPORT - NATURAL PEARLS: UNWORKED",
+        "hsn_code": "71011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "PEARLS, NATURAL OR CULTURED, WHETHER OR NOT WORKED OR GRADED BUT NOT STRUNG,\nMOUNTED OR SET; PEARLS, NATURAL OR CULTURED, TEMPORARILY STRUNG FOR CONVENIENCE\nOF TRANSPORT - NATURAL PEARLS: WORKED",
+        "hsn_code": "71011020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "PEARLS, NATURAL OR CULTURED, WHETHER OR NOT WORKED OR GRADED BUT NOT STRUNG,\nMOUNTED OR SET; PEARLS, NATURAL OR CULTURED, TEMPORARILY STRUNG FOR CONVENIENCE\nOF TRANSPORT - CULTURED PEARLS: UNWORKED",
+        "hsn_code": "71012100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "PEARLS, NATURAL OR CULTURED, WHETHER OR NOT WORKED OR GRADED BUT NOT STRUNG,\nMOUNTED OR SET; PEARLS, NATURAL OR CULTURED, TEMPORARILY STRUNG FOR CONVENIENCE\nOF TRANSPORT - CULTURED PEARLS: WORKED",
+        "hsn_code": "71012200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "PEARLS, NATURAL OR CULTURED, WHETHER OR NOT WORKED OR GRADED BUT NOT STRUNG, MOUNTED OR SET; PEARLS, NATURAL OR CULTURED, TEMPORARILY STRUNG FOR CONVENIENCE OF TRANSPORT",
+        "hsn_code": "7101",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 3
+            }
+        ]
+    },
+    {
+        "description": "TOMATOES, FRESH OR CHILLED",
+        "hsn_code": "7020000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF GLASS OTHER ARTICLES OF GLASS: GLASS SHELLS, GLASS GLOBES AND GLASS\nFOUNTS: GLOBES FOR LAMPS AND LANTERNS",
+        "hsn_code": "70200011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF GLASS OTHER ARTICLES OF GLASS: GLASS SHELLS, GLASS GLOBES AND GLASS\nFOUNTS: FOUNTS FOR KEROSENE WICK LAMPS",
+        "hsn_code": "70200012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF GLASS OTHER ARTICLES OF GLASS: GLASS SHELLS, GLASS GLOBES AND GLASS\nFOUNTS: OTHER",
+        "hsn_code": "70200019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF GLASS OTHER ARTICLES OF GLASS: GLASS CHIMNEYS: FOR LAMPS AND\nLANTERNS",
+        "hsn_code": "70200021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF GLASS OTHER ARTICLES OF GLASS: GLASS CHIMNEYS: OTHER",
+        "hsn_code": "70200029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF GLASS",
+        "hsn_code": "7020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "POTATOES, FRESH OR CHILLED OTHER",
+        "hsn_code": "7019000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS FIBRES (INCLUDING GLASS WOOL) AND ARTICLES THEREOF (FOR EXAMPLE, YARN, WOVEN\nFABRICS) - SLIVERS, ROVINGS, YARN AND CHOPPED STRANDS : CHOPPED STRANDS, OF A LENGTH\nOF NOT MORE THAN 50 MM",
+        "hsn_code": "70191100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS FIBRES (INCLUDING GLASS WOOL) AND ARTICLES THEREOF (FOR EXAMPLE, YARN, WOVEN\nFABRICS) - SLIVERS, ROVINGS, YARN AND CHOPPED STRANDS : ROVINGS",
+        "hsn_code": "70191200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS FIBRES (INCLUDING GLASS WOOL) AND ARTICLES THEREOF (FOR EXAMPLE, YARN, WOVEN\nFABRICS) - SLIVERS, ROVINGS, YARN AND CHOPPED STRANDS : OTHER",
+        "hsn_code": "70191900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS FIBRES (INCLUDING GLASS WOOL) AND ARTICLES THEREOF (FOR EXAMPLE, YARN, WOVEN\nFABRICS) - THIN SHEETS (VOILES), WEBS, MATS, MATTRESSES, BOARDS AND SIMILAR NON\nWOVEN PRODUCTS : MATS",
+        "hsn_code": "70193100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS FIBRES (INCLUDING GLASS WOOL) AND ARTICLES THEREOF (FOR EXAMPLE, YARN, WOVEN\nFABRICS) - THIN SHEETS (VOILES), WEBS, MATS, MATTRESSES, BOARDS AND SIMILAR NON\nWOVEN PRODUCTS : THIN SHEETS (VOILES)",
+        "hsn_code": "70193200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS FIBRES (INCLUDING GLASS WOOL) AND ARTICLES THEREOF (FOR EXAMPLE, YARN, WOVEN\nFABRICS) - THIN SHEETS (VOILES), WEBS, MATS, MATTRESSES, BOARDS AND SIMILAR NON\nWOVEN PRODUCTS : OTHER",
+        "hsn_code": "70193900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS FIBRES (INCLUDING GLASS WOOL) AND ARTICLES THEREOF (FOR EXAMPLE, YARN, WOVEN\nFABRICS) - WOVEN FABRICS OF ROVINGS",
+        "hsn_code": "70194000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS FIBRES (INCLUDING GLASS WOOL) AND ARTICLES THEREOF (FOR EXAMPLE, YARN, WOVEN\nFABRICS) - OTHER WOVEN FABRICS : OF A WIDTH NOT EXCEEDING 30 CM",
+        "hsn_code": "70195100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS FIBRES (INCLUDING GLASS WOOL) AND ARTICLES THEREOF (FOR EXAMPLE, YARN, WOVEN\nFABRICS) - OTHER WOVEN FABRICS : OF A WIDTH EXCEEDING 30 CM, PLAIN WEAVE, WEIGHING\nLESS THAN 250 G/SQ. METRE, OF FILAMENTS MEASURING PER SINGLE YARN NOT MORE THAN\n136 TEX",
+        "hsn_code": "70195200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS FIBRES (INCLUDING GLASS WOOL) AND ARTICLES THEREOF (FOR EXAMPLE, YARN, WOVEN\nFABRICS) - OTHER WOVEN FABRICS : OTHER",
+        "hsn_code": "70195900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS FIBRES (INCLUDING GLASS WOOL) AND ARTICLES THEREOF (FOR EXAMPLE, YARN, WOVEN\nFABRICS) - OTHER: GLASS WOOL OR GLASS FIBRE",
+        "hsn_code": "70199010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS FIBRES (INCLUDING GLASS WOOL) AND ARTICLES THEREOF (FOR EXAMPLE, YARN, WOVEN\nFABRICS) - OTHER: OTHER",
+        "hsn_code": "70199090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS FIBRES (INCLUDING GLASS WOOL) AND ARTICLES THEREOF (FOR EXAMPLE, YARN, WOVEN FABRICS)",
+        "hsn_code": "7019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LABORATORY, HYGIENIC OR PHARMACEUTICAL GLASSWARE, WHETHER OR NOT GRADUATED OR\nCALIBRATED - OF FUSED QUARTZ OR OTHER FUSED SILICA",
+        "hsn_code": "70171000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LABORATORY, HYGIENIC OR PHARMACEUTICAL GLASSWARE, WHETHER OR NOT GRADUATED OR\nCALIBRATED - OF OTHER GLASS HAVING A LINEAR COEFFICIENT OF EXPANSION NOT EXCEEDING\n5 X 10-6 PER KELVIN WITHIN A TEMPERATURE RANGE OF 00C TO 3000C",
+        "hsn_code": "70172000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LABORATORY, HYGIENIC OR PHARMACEUTICAL GLASSWARE, WHETHER OR NOT GRADUATED OR\nCALIBRATED - OTHER: GRADUATED OR CALIBRATED LABORATORY GLASSWARE",
+        "hsn_code": "70179010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LABORATORY, HYGIENIC OR PHARMACEUTICAL GLASSWARE, WHETHER OR NOT GRADUATED OR\nCALIBRATED - OTHER: PHARMACEUTICAL GLASSWARE",
+        "hsn_code": "70179020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LABORATORY, HYGIENIC OR PHARMACEUTICAL GLASSWARE, WHETHER OR NOT GRADUATED OR\nCALIBRATED - OTHER: HYGIENIC GLASSWARE",
+        "hsn_code": "70179030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LABORATORY, HYGIENIC OR PHARMACEUTICAL GLASSWARE, WHETHER OR NOT GRADUATED OR\nCALIBRATED - OTHER: OTHER",
+        "hsn_code": "70179090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LABORATORY, HYGIENIC OR PHARMACEUTICAL GLASSWARE, WHETHER OR NOT GRADUATED OR CALIBRATED",
+        "hsn_code": "7017",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAVING BLOCKS, SLABS, BRICKS, SQUARES, TILES AND OTHER ARTICLES OF PRESSED OR\nMOULDED GLASS, WHETHER OR NOT WIRED, OF A KIND USED FOR BUILDING OR\nCONSTRUCTION PURPOSES; GLASS CUBES AND OTHER GLASS SMALL WARES, WHETHER OR NOT\nON A BACKING, FOR MOSAICS OR SIMILAR DECORATIVE PURPOSES; LEADED LIGHTS AND THE\nLIKE; MULTI-CELLULAR OR FOAM GLASS IN BLOCKS, PANELS, PLATES, SHELLS OR SIMILAR FORMS -\nGLASS CUBES AND OTHER GLASS SMALLWARES, WHETHER OR NOT ON A BACKING, FOR\nMOSAICS OR SIMILAR DECORATIVE PURPOSES",
+        "hsn_code": "70161000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAVING BLOCKS, SLABS, BRICKS, SQUARES, TILES AND OTHER ARTICLES OF PRESSED OR\nMOULDED GLASS, WHETHER OR NOT WIRED, OF A KIND USED FOR BUILDING OR\nCONSTRUCTION PURPOSES; GLASS CUBES AND OTHER GLASS SMALL WARES, WHETHER OR NOT\nON A BACKING, FOR MOSAICS OR SIMILAR DECORATIVE PURPOSES; LEADED LIGHTS AND THE\nLIKE; MULTI-CELLULAR OR FOAM GLASS IN BLOCKS, PANELS, PLATES, SHELLS OR SIMILAR FORMS -\nOTHER",
+        "hsn_code": "70169000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAVING BLOCKS, SLABS, BRICKS, SQUARES, TILES AND OTHER ARTICLES OF PRESSED OR MOULDED GLASS, WHETHER OR NOT WIRED, OF A KIND USED FOR BUILDING OR CONSTRUCTION PURPOSES; GLASS CUBES AND OTHER GLASS SMALL WARES, WHETHER OR NOT ON A BACKING, FOR MOSAICS OR SIMILAR DECORATIVE PURPOSES; LEADED LIGHTS AND THE LIKE; MULTI-CELLULAR OR FOAM GLASS IN BLOCKS, PANELS, PLATES, SHELLS OR SIMILAR FORMS",
+        "hsn_code": "7016",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOCK OR WATCH GLASSES AND SIMILAR GLASSES, GLASSES FOR NON-CORRECTIVE OR\nCORRECTIVE SPECTACLES, CURVED, BENT, HOLLOWED OR THE LIKE; NOT OPTICALLY WORKED;\nHOLLOW GLASS SPHERES AND THEIR SEGMENTS, FOR THE MANUFACTURE OF SUCH GLASSES -\nOTHER: CLOCK AND WATCH GLASSES AND SIMILAR GLASSES, CURVED, BENT, HOLLOWED AND\nTHE LIKE, GLASS SPHERES AND SEGMENTS OF SPHERES FOR THE MANUFACTURE OF SUCH\nGLASSES",
+        "hsn_code": "70159010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOCK OR WATCH GLASSES AND SIMILAR GLASSES, GLASSES FOR NON-CORRECTIVE OR\nCORRECTIVE SPECTACLES, CURVED, BENT, HOLLOWED OR THE LIKE; NOT OPTICALLY WORKED;\nHOLLOW GLASS SPHERES AND THEIR SEGMENTS, FOR THE MANUFACTURE OF SUCH GLASSES -\nOTHER: GLASS FOR SUN GLASSES",
+        "hsn_code": "70159020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOCK OR WATCH GLASSES AND SIMILAR GLASSES, GLASSES FOR NON-CORRECTIVE OR\nCORRECTIVE SPECTACLES, CURVED, BENT, HOLLOWED OR THE LIKE; NOT OPTICALLY WORKED;\nHOLLOW GLASS SPHERES AND THEIR SEGMENTS, FOR THE MANUFACTURE OF SUCH GLASSES -\nOTHER: OTHER",
+        "hsn_code": "70159090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOCK OR WATCH GLASSES AND SIMILAR GLASSES, FOR NON-CORRECTIVE OR CORRECTIVE SPECTACLES, CURVED, BENT, HOLLOWED OR THE LIKE; NOT OPTICALLY WORKED; HOLLOW GLASS SPHERES AND THEIR SEGMENTS, FOR THE MANUFACTURE OF SUCH GLASSES",
+        "hsn_code": "7015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SIGNALLING GLASSWARE AND OPTICAL ELEMENTS OF GLASS (OTHER THAN THOSE OF HEADING\n7015), NOT OPTICALLY WORKED - SIGNALLING GLASSWARE AND OPTICAL ELEMENTS OF GLASS\n(OTHER THAN THOSE OF HEADING 7015), NOT OPTICALLY WORKED: SIGNALLING GLASSWARE",
+        "hsn_code": "70140010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SIGNALLING GLASSWARE AND OPTICAL ELEMENTS OF GLASS (OTHER THAN THOSE OF HEADING\n7015), NOT OPTICALLY WORKED - SIGNALLING GLASSWARE AND OPTICAL ELEMENTS OF GLASS\n(OTHER THAN THOSE OF HEADING 7015), NOT OPTICALLY WORKED: OPTICAL ELEMENTS",
+        "hsn_code": "70140020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SIGNALLING GLASSWARE AND OPTICAL ELEMENTS OF GLASS (OTHER THAN THOSE OF HEADING 7015), NOT OPTICALLY WORKED",
+        "hsn_code": "7014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR\nSIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018) OF GLASS-CERAMICS",
+        "hsn_code": "70131000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR\nSIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018) - DRINKING GLASSES OTHER\nTHAN OF GLASS-CERAMICS : OF LEAD CRYSTAL",
+        "hsn_code": "70132100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR\nSIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018) - STEMWARE DRINKING\nGLASSES, OTHER THAN OF GLASS-CERAMICS: -- OF LEAD CRYSTAL",
+        "hsn_code": "70132200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR\nSIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018) - STEMWARE DRINKING\nGLASSES, OTHER THAN OF GLASS-CERAMICS: -- OTHER",
+        "hsn_code": "70132800",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR\nSIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018) - DRINKING GLASSES OTHER\nTHAN OF GLASS-CERAMICS : OTHER",
+        "hsn_code": "70132900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR\nSIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018) - GLASSWARE OF A KIND\nUSED FOR TABLE (OTHER THAN DRINKING GLASSES) OR KITCHEN PURPOSES OTHER THAN OF\nGLASS-CERAMICS : OF LEAD CRYSTAL",
+        "hsn_code": "70133100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR\nSIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018) - GLASSWARE OF A KIND\nUSED FOR TABLE (OTHER THAN DRINKING GLASSES) OR KITCHEN PURPOSES OTHER THAN OF\nGLASS-CERAMICS : OF GLASS HAVING A LINEAR COEFFICIENT OF EXPANSION NOT EXCEEDING 5 X\n10-6 PER KELVIN WITHIN A TEMPERATURE RANGE OF 00C TO 3000C",
+        "hsn_code": "70133200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR\nSIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018) - OTHER DRINKING\nGLASSES, OTHER THAN OF GLASS-CERAMICS: -- OF LEAD CRYSTAL",
+        "hsn_code": "70133300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR\nSIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018) - OTHER DRINKING\nGLASSES, OTHER THAN OF GLASS-CERAMICS: -- OTHER",
+        "hsn_code": "70133700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR\nSIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018) - GLASSWARE OF A KIND\nUSED FOR TABLE (OTHER THAN DRINKING GLASSES) OR KITCHEN PURPOSES OTHER THAN OF\nGLASS-CERAMICS : OTHER",
+        "hsn_code": "70133900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR\nSIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018) - GLASSWARE OF A KIND\nUSED FOR TABLE (OTHER THAN DRINKING GLASSES) OR KITCHEN PURPOSED, OTHER THAN OF\nGLASS-CERAMICS: -- OF LEAD CRYSTAL",
+        "hsn_code": "70134100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR\nSIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018) - GLASSWARE OF A KIND\nUSED FOR TABLE (OTHER THAN DRINKING GLASSES) OR KITCHEN PURPOSED, OTHER THAN OF\nGLASS-CERAMICS: -- OF GLASS HAVING A LINEAR COEFICIENT OF EXPANSION NOT EXCEEDING 5\nX 10 TO THE POWER OF MINUS 6 PER KELVIN WITHIN A TEMPERATURE RANGE OF 0 DEGREE C\nTO 300 DEGREEE C",
+        "hsn_code": "70134200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR\nSIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018) - GLASSWARE OF A KIND\nUSED FOR TABLE (OTHER THAN DRINKING GLASSES) OR KITCHEN PURPOSED, OTHER THAN OF\nGLASS-CERAMICS: -- OTHER",
+        "hsn_code": "70134900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR\nSIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018) - GLASSWARE OF A KIND\nUSED FOR TABLE (OTHER THAN DRINKING GLASSES) OR KITCHEN PURPOSED, OTHER THAN OF\nGLASS-CERAMICS: - OTHER -- OF LEAD CRYSTAL",
+        "hsn_code": "70139100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR\nSIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018) - OTHER GLASSWARE : OF\nLEAD CRYSTAL: GLASS STATUES",
+        "hsn_code": "70139110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR\nSIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018) - OTHER GLASSWARE : OF\nLEAD CRYSTAL: OTHER",
+        "hsn_code": "70139190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR\nSIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018) - GLASSWARE OF A KIND\nUSED FOR TABLE (OTHER THAN DRINKING GLASSES) OR KITCHEN PURPOSED, OTHER THAN OF\nGLASS-CERAMICS: - OTHER -- OTHER",
+        "hsn_code": "70139900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR\nSIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018) - OTHER GLASSWARE :\nOTHER: GLASS STATUES",
+        "hsn_code": "70139910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR\nSIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018) - OTHER GLASSWARE :\nOTHER: OTHER",
+        "hsn_code": "70139990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASSWARE OF A KIND USED FOR TABLE, KITCHEN, TOILET, OFFICE, INDOOR DECORATION OR SIMILAR PURPOSES (OTHER THAN THAT OF HEADING 7010 OR 7018)",
+        "hsn_code": "7013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "POTATOES, FRESH OR CHILLED SEED",
+        "hsn_code": "7011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS ENVELOPES (INCLUDING BULBS AND TUBES), OPEN, AND GLASS PARTS THEREOF,\nWITHOUT FITTINGS, FOR ELECTRIC LAMPS, CATHODE-RAY TUBES OR THE LIKE - FOR ELECTRIC\nLIGHTING: GLASS ENVELOPES FOR FLUORESCENT LAMPS",
+        "hsn_code": "70111010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS ENVELOPES (INCLUDING BULBS AND TUBES), OPEN, AND GLASS PARTS THEREOF,\nWITHOUT FITTINGS, FOR ELECTRIC LAMPS, CATHODE-RAY TUBES OR THE LIKE - FOR ELECTRIC\nLIGHTING: GLASS ENVELOPES FOR FILAMENT LAMPS",
+        "hsn_code": "70111020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS ENVELOPES (INCLUDING BULBS AND TUBES), OPEN, AND GLASS PARTS THEREOF,\nWITHOUT FITTINGS, FOR ELECTRIC LAMPS, CATHODE-RAY TUBES OR THE LIKE - FOR ELECTRIC\nLIGHTING: OTHER",
+        "hsn_code": "70111090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS ENVELOPES (INCLUDING BULBS AND TUBES), OPEN, AND GLASS PARTS THEREOF,\nWITHOUT FITTINGS, FOR ELECTRIC LAMPS, CATHODE-RAY TUBES OR THE LIKE - FOR CATHODE\nRAY TUBES",
+        "hsn_code": "70112000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS ENVELOPES (INCLUDING BULBS AND TUBES), OPEN, AND GLASS PARTS THEREOF,\nWITHOUT FITTINGS, FOR ELECTRIC LAMPS, CATHODE-RAY TUBES OR THE LIKE - OTHER: GLASS\nENVELOPES FOR ELECTRONIC VALVES",
+        "hsn_code": "70119010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS ENVELOPES (INCLUDING BULBS AND TUBES), OPEN, AND GLASS PARTS THEREOF,\nWITHOUT FITTINGS, FOR ELECTRIC LAMPS, CATHODE-RAY TUBES OR THE LIKE - OTHER: OTHER",
+        "hsn_code": "70119090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS ENVELOPES (INCLUDING BULBS AND TUBES), OPEN, AND GLASS PARTS THEREOF, WITHOUT FITTINGS, FOR ELECTRIC LAMPS, CATHODE-RAY TUBES OR THE LIKE",
+        "hsn_code": "7011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBOYS, BOTTLES, FLASKS, JARS, POTS, PHIALS, AMPOULES AND OTHER CONTAINERS, OF\nGLASS, OF A KIND USED FOR THE CONVEYANCE OR PACKING OF GOODS; PRESERVING JARS OF\nGLASS; STOPPERS, LIDS AND OTHER CLOSURES, OF GLASS - AMPOULES",
+        "hsn_code": "70101000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBOYS, BOTTLES, FLASKS, JARS, POTS, PHIALS, AMPOULES AND OTHER CONTAINERS, OF\nGLASS, OF A KIND USED FOR THE CONVEYANCE OR PACKING OF GOODS; PRESERVING JARS OF\nGLASS; STOPPERS, LIDS AND OTHER CLOSURES, OF GLASS - STOPPERS, LIDS AND OTHER\nCLOSURES",
+        "hsn_code": "70102000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBOYS, BOTTLES, FLASKS, JARS, POTS, PHIALS, AMPOULES AND OTHER CONTAINERS, OF\nGLASS, OF A KIND USED FOR THE CONVEYANCE OR PACKING OF GOODS; PRESERVING JARS OF\nGLASS; STOPPERS, LIDS AND OTHER CLOSURES, OF GLASS - OTHER",
+        "hsn_code": "70109000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBOYS, BOTTLES, FLASKS, JARS, POTS, PHIALS, AMPOULES AND OTHER CONTAINERS, OF GLASS, OF A KIND USED FOR THE CONVEYANCE OR PACKING OF GOODS; PRESERVING JARS OF GLASS; STOPPERS, LIDS AND OTHER CLOSURES, OF GLASS",
+        "hsn_code": "7010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS MIRRORS, WHETHER OR NOT FRAMED, INCLUDING REAR-VIEW MIRRORS - REAR-VIEW\nMIRRORS FOR VEHICLES: PRISMATIC REAR-VIEW MIRROR FOR VEHICLES",
+        "hsn_code": "70091010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS MIRRORS, WHETHER OR NOT FRAMED, INCLUDING REAR-VIEW MIRRORS - REAR-VIEW\nMIRRORS FOR VEHICLES: OTHER",
+        "hsn_code": "70091090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS MIRRORS, WHETHER OR NOT FRAMED, INCLUDING REAR-VIEW MIRRORS - OTHER :\nUNFRAMED",
+        "hsn_code": "70099100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS MIRRORS, WHETHER OR NOT FRAMED, INCLUDING REAR-VIEW MIRRORS",
+        "hsn_code": "7009",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MULTIPLE-WALLED INSULATING UNITS OF GLASS - MULTIPLE-WALLED INSULATING UNITS OF\nGLASS: GLAZED GLASS, DOUBLE WALLED",
+        "hsn_code": "70080010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MULTIPLE-WALLED INSULATING UNITS OF GLASS - MULTIPLE-WALLED INSULATING UNITS OF\nGLASS: GLAZED GLASS, MULTIPLE WALLED",
+        "hsn_code": "70080020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MULTIPLE-WALLED INSULATING UNITS OF GLASS - MULTIPLE-WALLED INSULATING UNITS OF\nGLASS: OTHER",
+        "hsn_code": "70080090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MULTIPLE-WALLED INSULATING UNITS OF GLASS",
+        "hsn_code": "7008",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SAFETY GLASS, CONSISTING OF TOUGHENED (TEMPERED) OR LAMINATED GLASS - TOUGHENED\n(TEMPERED) SAFETY GLASS : OF SIZE AND SHAPE SUITABLE FOR INCORPORATION IN VEHICLES,\nAIRCRAFT, SPACECRAFT OR VESSELS",
+        "hsn_code": "70071100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SAFETY GLASS, CONSISTING OF TOUGHENED (TEMPERED) OR LAMINATED GLASS - TOUGHENED\n(TEMPERED) SAFETY GLASS : OTHER",
+        "hsn_code": "70071900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SAFETY GLASS, CONSISTING OF TOUGHENED (TEMPERED) OR LAMINATED GLASS - LAMINATED\nSAFETY GLASS : OF SIZE AND SHAPE SUITABLE FOR INCORPORATION IN VEHICLES, AIRCRAFT,\nSPACECRAFT OR VESSELS: BULLET PROOF GLASS",
+        "hsn_code": "70072110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SAFETY GLASS, CONSISTING OF TOUGHENED (TEMPERED) OR LAMINATED GLASS - LAMINATED\nSAFETY GLASS : OF SIZE AND SHAPE SUITABLE FOR INCORPORATION IN VEHICLES, AIRCRAFT,\nSPACECRAFT OR VESSELS: OTHER",
+        "hsn_code": "70072190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SAFETY GLASS, CONSISTING OF TOUGHENED (TEMPERED) OR LAMINATED GLASS - LAMINATED\nSAFETY GLASS : OTHER",
+        "hsn_code": "70072900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SAFETY GLASS, CONSISTING OF TOUGHENED (TEMPERED) OR LAMINATED GLASS",
+        "hsn_code": "7007",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLOAT GLASS AND SURFACE GROUND OR POLISHED GLASS, IN SHEETS, WHETHER OR NOT\nHAVING AN ABSORBENT, REFLECTING OR NON-REFLECTING LAYER, BUT NOT OTHERWISE\nWORKED - NON-WIRED GLASS, HAVING AN ABSORBENT, REFLECTING OR NON-REFLECTING\nLAYER: TINTED",
+        "hsn_code": "70051010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLOAT GLASS AND SURFACE GROUND OR POLISHED GLASS, IN SHEETS, WHETHER OR NOT\nHAVING AN ABSORBENT, REFLECTING OR NON-REFLECTING LAYER, BUT NOT OTHERWISE\nWORKED - NON-WIRED GLASS, HAVING AN ABSORBENT, REFLECTING OR NON-REFLECTING\nLAYER: OTHER",
+        "hsn_code": "70051090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLOAT GLASS AND SURFACE GROUND OR POLISHED GLASS, IN SHEETS, WHETHER OR NOT\nHAVING AN ABSORBENT, REFLECTING OR NON-REFLECTING LAYER, BUT NOT OTHERWISE\nWORKED - OTHER NON-WIRED GLASS : COLOURED THROUGHOUT THE MASS (BODY TINTED)\nOPACIFIED, FLASHED OR MERELY SURFACE GROUND: TINTED",
+        "hsn_code": "70052110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLOAT GLASS AND SURFACE GROUND OR POLISHED GLASS, IN SHEETS, WHETHER OR NOT\nHAVING AN ABSORBENT, REFLECTING OR NON-REFLECTING LAYER, BUT NOT OTHERWISE\nWORKED - OTHER NON-WIRED GLASS : COLOURED THROUGHOUT THE MASS (BODY TINTED)\nOPACIFIED, FLASHED OR MERELY SURFACE GROUND: OTHER",
+        "hsn_code": "70052190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLOAT GLASS AND SURFACE GROUND OR POLISHED GLASS, IN SHEETS, WHETHER OR NOT\nHAVING AN ABSORBENT, REFLECTING OR NON-REFLECTING LAYER, BUT NOT OTHERWISE\nWORKED - OTHER NON-WIRED GLASS : OTHER: TINTED",
+        "hsn_code": "70052910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLOAT GLASS AND SURFACE GROUND OR POLISHED GLASS, IN SHEETS, WHETHER OR NOT\nHAVING AN ABSORBENT, REFLECTING OR NON-REFLECTING LAYER, BUT NOT OTHERWISE\nWORKED - OTHER NON-WIRED GLASS : OTHER: OTHER",
+        "hsn_code": "70052990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLOAT GLASS AND SURFACE GROUND OR POLISHED GLASS, IN SHEETS, WHETHER OR NOT\nHAVING AN ABSORBENT, REFLECTING OR NON-REFLECTING LAYER, BUT NOT OTHERWISE\nWORKED - WIRED GLASS: TINTED",
+        "hsn_code": "70053010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLOAT GLASS AND SURFACE GROUND OR POLISHED GLASS, IN SHEETS, WHETHER OR NOT\nHAVING AN ABSORBENT, REFLECTING OR NON-REFLECTING LAYER, BUT NOT OTHERWISE\nWORKED - WIRED GLASS: OTHER",
+        "hsn_code": "70053090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLOAT GLASS AND SURFACE GROUND OR POLISHED GLASS, IN SHEETS, WHETHER OR NOT HAVING AN ABSORBENT, REFLECTING OR NON-REFLECTING LAYER, BUT NOT OTHERWISE WORKED",
+        "hsn_code": "7005",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DRAWN GLASS AND BLOWN GLASS, IN SHEETS, WHETHER OR NOT HAVING AN ABSORBENT,\nREFLECTING OR NONREFLECTING LAYER, BUT NOT OTHERWISE WORKED - GLASS, COLOURED\nTHROUGHOUT THE MASS (BODY TINTED), OPACIFIED, FLASHED OR HAVING AN ABSORBENT,\nREFLECTING OR NON-REFLECTING LAYER: WINDOW GLASS (SHEET GLASS): TINTED",
+        "hsn_code": "70042011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DRAWN GLASS AND BLOWN GLASS, IN SHEETS, WHETHER OR NOT HAVING AN ABSORBENT,\nREFLECTING OR NONREFLECTING LAYER, BUT NOT OTHERWISE WORKED - GLASS, COLOURED\nTHROUGHOUT THE MASS (BODY TINTED), OPACIFIED, FLASHED OR HAVING AN ABSORBENT,\nREFLECTING OR NON-REFLECTING LAYER: WINDOW GLASS (SHEET GLASS): OTHER",
+        "hsn_code": "70042019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DRAWN GLASS AND BLOWN GLASS, IN SHEETS, WHETHER OR NOT HAVING AN ABSORBENT,\nREFLECTING OR NONREFLECTING LAYER, BUT NOT OTHERWISE WORKED - GLASS, COLOURED\nTHROUGHOUT THE MASS (BODY TINTED), OPACIFIED, FLASHED OR HAVING AN ABSORBENT,\nREFLECTING OR NON-REFLECTING LAYER: WINDOW GLASS (SHEET GLASS): OTHER: TINTED",
+        "hsn_code": "70042091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DRAWN GLASS AND BLOWN GLASS, IN SHEETS, WHETHER OR NOT HAVING AN ABSORBENT,\nREFLECTING OR NONREFLECTING LAYER, BUT NOT OTHERWISE WORKED - GLASS, COLOURED\nTHROUGHOUT THE MASS (BODY TINTED), OPACIFIED, FLASHED OR HAVING AN ABSORBENT,\nREFLECTING OR NON-REFLECTING LAYER: WINDOW GLASS (SHEET GLASS): OTHER: OTHER",
+        "hsn_code": "70042099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DRAWN GLASS AND BLOWN GLASS, IN SHEETS, WHETHER OR NOT HAVING AN ABSORBENT,\nREFLECTING OR NONREFLECTING LAYER, BUT NOT OTHERWISE WORKED - OTHER GLASS:\nWINDOW GLASS (SHEET GLASS): TINTED",
+        "hsn_code": "70049011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DRAWN GLASS AND BLOWN GLASS, IN SHEETS, WHETHER OR NOT HAVING AN ABSORBENT,\nREFLECTING OR NONREFLECTING LAYER, BUT NOT OTHERWISE WORKED - OTHER GLASS:\nWINDOW GLASS (SHEET GLASS): OTHER",
+        "hsn_code": "70049019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DRAWN GLASS AND BLOWN GLASS, IN SHEETS, WHETHER OR NOT HAVING AN ABSORBENT,\nREFLECTING OR NONREFLECTING LAYER, BUT NOT OTHERWISE WORKED - OTHER GLASS: OTHER:\nTINTED",
+        "hsn_code": "70049091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DRAWN GLASS AND BLOWN GLASS, IN SHEETS, WHETHER OR NOT HAVING AN ABSORBENT,\nREFLECTING OR NONREFLECTING LAYER, BUT NOT OTHERWISE WORKED - OTHER GLASS: OTHER:\nOTHER",
+        "hsn_code": "70049099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DRAWN GLASS AND BLOWN GLASS, IN SHEETS, WHETHER OR NOT HAVING AN ABSORBENT, REFLECTING OR NON-REFLECTING LAYER BUT NOT OTHERWISE WORKED",
+        "hsn_code": "7004",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CAST GLASS AND ROLLED GLASS, IN SHEETS OR PROFILES, WHETHER OR NOT HAVING AN\nABSORBENT, REFLECTING OR NON-REFLECTING LAYER, BUT NOT OTHERWISE WORKED - NON\nWIRED SHEETS : COLOURED THROUGHOUT THE MASS (BODY-TINTED), OPACIFIED, FLASHED OR\nHAVING AN ABSORBENT, REFLECTING OR NON-REFLECTING LAYER: TINTED",
+        "hsn_code": "70031210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CAST GLASS AND ROLLED GLASS, IN SHEETS OR PROFILES, WHETHER OR NOT HAVING AN\nABSORBENT, REFLECTING OR NON-REFLECTING LAYER, BUT NOT OTHERWISE WORKED - NON\nWIRED SHEETS : COLOURED THROUGHOUT THE MASS (BODY-TINTED), OPACIFIED, FLASHED OR\nHAVING AN ABSORBENT, REFLECTING OR NON-REFLECTING LAYER: OTHER",
+        "hsn_code": "70031290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CAST GLASS AND ROLLED GLASS, IN SHEETS OR PROFILES, WHETHER OR NOT HAVING AN\nABSORBENT, REFLECTING OR NON-REFLECTING LAYER, BUT NOT OTHERWISE WORKED - NON\nWIRED SHEETS : OTHER: TINTED",
+        "hsn_code": "70031910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CAST GLASS AND ROLLED GLASS, IN SHEETS OR PROFILES, WHETHER OR NOT HAVING AN\nABSORBENT, REFLECTING OR NON-REFLECTING LAYER, BUT NOT OTHERWISE WORKED - NON\nWIRED SHEETS : OTHER: OTHER",
+        "hsn_code": "70031990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CAST GLASS AND ROLLED GLASS, IN SHEETS OR PROFILES, WHETHER OR NOT HAVING AN\nABSORBENT, REFLECTING OR NON-REFLECTING LAYER, BUT NOT OTHERWISE WORKED - WIRED\nSHEETS: TINTED",
+        "hsn_code": "70032010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CAST GLASS AND ROLLED GLASS, IN SHEETS OR PROFILES, WHETHER OR NOT HAVING AN\nABSORBENT, REFLECTING OR NON-REFLECTING LAYER, BUT NOT OTHERWISE WORKED - WIRED\nSHEETS: OTHER",
+        "hsn_code": "70032090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CAST GLASS AND ROLLED GLASS, IN SHEETS OR PROFILES, WHETHER OR NOT HAVING AN\nABSORBENT, REFLECTING OR NON-REFLECTING LAYER, BUT NOT OTHERWISE WORKED -\nPROFILES: TINTED",
+        "hsn_code": "70033010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CAST GLASS AND ROLLED GLASS, IN SHEETS OR PROFILES, WHETHER OR NOT HAVING AN\nABSORBENT, REFLECTING OR NON-REFLECTING LAYER, BUT NOT OTHERWISE WORKED -\nPROFILES: OTHER",
+        "hsn_code": "70033090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CAST GLASS AND ROLLED GLASS, IN SHEETS OR PROFILES, WHETHER OR NOT HAVING AN ABSORBENT, REFLECTING OR NON-REFLECTING LAYER, BUT NOT OTHERWISE WORKED",
+        "hsn_code": "7003",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS IN BALLS (OTHER THAN MICROSPHERES OF HEADING 7018), RODS OR TUBES, UNWORKED\n- BALLS",
+        "hsn_code": "70021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS IN BALLS (OTHER THAN MICROSPHERES OF HEADING 7018), RODS OR TUBES, UNWORKED\n- RODS: ENAMEL GLASS RODS",
+        "hsn_code": "70022010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS IN BALLS (OTHER THAN MICROSPHERES OF HEADING 7018), RODS OR TUBES, UNWORKED\n- RODS: OTHER",
+        "hsn_code": "70022090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS IN BALLS (OTHER THAN MICROSPHERES OF HEADING 7018), RODS OR TUBES, UNWORKED\n- TUBES: OF FUSED QUARTZ OR OTHER FUSED SILICA",
+        "hsn_code": "70023100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS IN BALLS (OTHER THAN MICROSPHERES OF HEADING 7018), RODS OR TUBES, UNWORKED\n- TUBES: OF OTHER GLASS HAVING A LINEAR COEFFICIENT OF EXPANSION NOT EXCEEDING 5 X\n10-6 PER KELVIN WITHIN A TEMPERATURE RANGE OF 00C TO 3000C",
+        "hsn_code": "70023200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS IN BALLS (OTHER THAN MICROSPHERES OF HEADING 7018), RODS OR TUBES, UNWORKED\n- TUBES: OTHER",
+        "hsn_code": "70023900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLASS IN BALLS (OTHER THAN MICROSPHERES OF HEADING 7018), RODS OR TUBES, UNWORKED",
+        "hsn_code": "7002",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CULLET AND OTHER WASTE AND SCRAP OF GLASS; GLASS IN THE MASS - CULLET AND OTHER\nWASTE AND SCRAP OF GLASS; GLASS IN THE MASS: CULLET AND OTHER WASTE AND SCRAP OF\nGLASS",
+        "hsn_code": "70010010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CULLET AND OTHER WASTE AND SCRAP OF GLASS; GLASS IN THE MASS - CULLET AND OTHER\nWASTE AND SCRAP OF GLASS; GLASS IN THE MASS: ENAMEL GLASS IN THE MASS",
+        "hsn_code": "70010020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CULLET AND OTHER WASTE AND SCRAP OF GLASS; GLASS IN THE MASS - CULLET AND OTHER\nWASTE AND SCRAP OF GLASS; GLASS IN THE MASS: OTHER",
+        "hsn_code": "70010090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CULLET AND OTHER WASTE AND SCRAP OF GLASS; GLASS IN THE MASS",
+        "hsn_code": "7001",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CERAMIC ARTICLES",
+        "hsn_code": "6914",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CERAMIC ARTICLES - OTHER",
+        "hsn_code": "69149000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER CERAMIC ARTICLES - OF PORCELAIN OR CHINA",
+        "hsn_code": "69141000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "STATUETTES AND OTHER ORNAMENTAL CERAMIC ARTICLES",
+        "hsn_code": "6913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "STATUETTES AND OTHER ORNAMENTAL CERAMIC ARTICLES - OF PORCELAIN OR CHINA",
+        "hsn_code": "69131000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CERAMIC TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND TOILET ARTICLES,\nOTHER THAN OF PORCELAIN OR CHINA - CERAMIC TABLEWARE, KITCHENWARE, OTHER\nHOUSEHOLD ARTICLES AND TOILET ARTICLES, OTHER THAN OF PORCELAIN OR CHINA: CLAY\nARTICLES",
+        "hsn_code": "69120040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CERAMIC TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND TOILET ARTICLES, OTHER THAN OF PORCELAIN OR CHINA",
+        "hsn_code": "6912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CERAMIC TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND TOILET ARTICLES,\nOTHER THAN OF PORCELAIN OR CHINA - CERAMIC TABLEWARE, KITCHENWARE, OTHER\nHOUSEHOLD ARTICLES AND TOILET ARTICLES, OTHER THAN OF PORCELAIN OR CHINA: OTHER",
+        "hsn_code": "69120090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CERAMIC TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND TOILET ARTICLES,\nOTHER THAN OF PORCELAIN OR CHINA - CERAMIC TABLEWARE, KITCHENWARE, OTHER\nHOUSEHOLD ARTICLES AND TOILET ARTICLES, OTHER THAN OF PORCELAIN OR CHINA: TOILET\nARTICLES",
+        "hsn_code": "69120030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CERAMIC TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND TOILET ARTICLES,\nOTHER THAN OF PORCELAIN OR CHINA - CERAMIC TABLEWARE, KITCHENWARE, OTHER\nHOUSEHOLD ARTICLES AND TOILET ARTICLES, OTHER THAN OF PORCELAIN OR CHINA:\nKITCHENWARE",
+        "hsn_code": "69120020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CERAMIC TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND TOILET ARTICLES,\nOTHER THAN OF PORCELAIN OR CHINA - CERAMIC TABLEWARE, KITCHENWARE, OTHER\nHOUSEHOLD ARTICLES AND TOILET ARTICLES, OTHER THAN OF PORCELAIN OR CHINA:\nTABLEWARE",
+        "hsn_code": "69120010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND TOILET ARTICLES, OF PORCELAIN OR CHINA",
+        "hsn_code": "6911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND TOILET ARTICLES, OF\nPORCELAIN OR CHINA - OTHER: OTHER",
+        "hsn_code": "69119090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND TOILET ARTICLES, OF\nPORCELAIN OR CHINA - OTHER: WATER FILTERS OF A CAPACITY NOT EXCEEDING 40 LITRES",
+        "hsn_code": "69119020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND TOILET ARTICLES, OF\nPORCELAIN OR CHINA - OTHER: TOILET ARTICLES",
+        "hsn_code": "69119010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND TOILET ARTICLES, OF\nPORCELAIN OR CHINA 6911 10 TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND\nTOILET ARTICLES, OF PORCELAIN OR CHINA - TABLEWARE AND KITCHENWARE:KITCHENWARE:\nOTHER",
+        "hsn_code": "69111029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND TOILET ARTICLES, OF\nPORCELAIN OR CHINA 6911 10 TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND\nTOILET ARTICLES, OF PORCELAIN OR CHINA - TABLEWARE AND KITCHENWARE:KITCHENWARE:\nOF BONE CHINA AND SOFT PORCELAIN",
+        "hsn_code": "69111021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND TOILET ARTICLES, OF\nPORCELAIN OR CHINA 6911 10 TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND\nTOILET ARTICLES, OF PORCELAIN OR CHINA - TABLEWARE AND KITCHENWARE:TABLEWARE:\nOTHER",
+        "hsn_code": "69111019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND TOILET ARTICLES, OF\nPORCELAIN OR CHINA 6911 10 TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND\nTOILET ARTICLES, OF PORCELAIN OR CHINA - TABLEWARE AND KITCHENWARE:TABLEWARE: OF\nBONE CHINA AND SOFT PORCELAIN",
+        "hsn_code": "69111011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CERAMIC SINKS, WASH BASINS, WASH BASIN PEDESTALS, BATHS, BIDETS, WATER CLOSET PANS, FLUSHING CISTERNS, URINALS AND SIMILAR SANITARY FIXTURES",
+        "hsn_code": "6910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CERAMIC SINKS, WASH BASINS, WASH BASIN PEDESTALS, BATHS, BIDETS, WATER CLOSET PANS,\nFLUSHING CISTERNS, URINALS AND SIMILAR SANITARY FIXTURES - OTHER",
+        "hsn_code": "69109000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CERAMIC SINKS, WASH BASINS, WASH BASIN PEDESTALS, BATHS, BIDETS, WATER CLOSET PANS,\nFLUSHING CISTERNS, URINALS AND SIMILAR SANITARY FIXTURES - OF PORCELAIN OR CHINA",
+        "hsn_code": "69101000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CERAMIC WARES FOR LABORATORY, CHEMICAL OR OTHER TECHNICAL USES; CERAMIC TROUGHS, TUBS AND SIMILAR RECEPTACLES OF A KIND USED IN AGRICULTURE; CERAMIC POTS, JARS AND SIMILAR ARTICLES OF A KIND USED FOR THE CONVEYANCE OR PACKING OF GOODS",
+        "hsn_code": "6909",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CERAMIC WARES FOR LABORATORY, CHEMICAL OR OTHER TECHNICAL USES; CERAMIC\nTROUGHS, TUBS AND SIMILAR RECEPTACLES OF A KIND USED IN AGRICULTURE; CERAMIC POTS,\nJARS AND SIMILAR ARTICLES OF A KIND USED FOR THE CONVEYANCE OR PACKING OF GOODS\nOTHER",
+        "hsn_code": "69099000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CERAMIC WARES FOR LABORATORY, CHEMICAL OR OTHER TECHNICAL USES; CERAMIC\nTROUGHS, TUBS AND SIMILAR RECEPTACLES OF A KIND USED IN AGRICULTURE; CERAMIC POTS,\nJARS AND SIMILAR ARTICLES OF A KIND USED FOR THE CONVEYANCE OR PACKING OF GOODS -\nCERAMIC WARES FOR LABORATORY, CHEMICAL OR OTHER TECHNICAL USES : OTHER: OTHER",
+        "hsn_code": "69091990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CERAMIC WARES FOR LABORATORY, CHEMICAL OR OTHER TECHNICAL USES; CERAMIC\nTROUGHS, TUBS AND SIMILAR RECEPTACLES OF A KIND USED IN AGRICULTURE; CERAMIC POTS,\nJARS AND SIMILAR ARTICLES OF A KIND USED FOR THE CONVEYANCE OR PACKING OF GOODS -\nCERAMIC WARES FOR LABORATORY, CHEMICAL OR OTHER TECHNICAL USES : OTHER: CERAMIC\nFILTER CANDLE",
+        "hsn_code": "69091910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CERAMIC WARES FOR LABORATORY, CHEMICAL OR OTHER TECHNICAL USES; CERAMIC\nTROUGHS, TUBS AND SIMILAR RECEPTACLES OF A KIND USED IN AGRICULTURE; CERAMIC POTS,\nJARS AND SIMILAR ARTICLES OF A KIND USED FOR THE CONVEYANCE OR PACKING OF GOODS -\nCERAMIC WARES FOR LABORATORY, CHEMICAL OR OTHER TECHNICAL USES : ARTICLES HAVING\nA HARDNESS EQUIVALENT TO 9 OR MORE ON THE MOHS SCALE",
+        "hsn_code": "69091200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CERAMIC WARES FOR LABORATORY, CHEMICAL OR OTHER TECHNICAL USES; CERAMIC\nTROUGHS, TUBS AND SIMILAR RECEPTACLES OF A KIND USED IN AGRICULTURE; CERAMIC POTS,\nJARS AND SIMILAR ARTICLES OF A KIND USED FOR THE CONVEYANCE OR PACKING OF GOODS -\nCERAMIC WARES FOR LABORATORY, CHEMICAL OR OTHER TECHNICAL USES : OF PORCELAIN OR\nCHINA",
+        "hsn_code": "69091100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNGLAZED CERAMIC FLAGS AND PAVING, HEARTH OR WALL TILES; UNGLAZED CERAMIC MOSAIC CUBES AND THE LIKE, WHETHER OR NOT ON A BACKING",
+        "hsn_code": "6907",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNGLAZED CERAMIC FLAGS AND PAVING, HEARTH OR WALL TILES; UNGLAZED CERAMIC MOSAIC\nCUBES AND THE LIKE, WHETHER OR NOT ON A BACKING - OTHER: OTHER",
+        "hsn_code": "69079090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNGLAZED CERAMIC FLAGS AND PAVING, HEARTH OR WALL TILES; UNGLAZED CERAMIC MOSAIC\nCUBES AND THE LIKE, WHETHER OR NOT ON A BACKING - OTHER: VITRIFIED TILES, WHETHER\nPOLISHED OR NOT",
+        "hsn_code": "69079010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNGLAZED CERAMIC FLAGS AND PAVING, HEARTH OR WALL TILES; UNGLAZED CERAMIC MOSAIC\nCUBES AND THE LIKE, WHETHER OR NOT ON A BACKING - TILES, CUBES AND SIMILAR ARTICLES,\nWHETHER OR NOT RECTANGULAR, THE LARGEST SURFACE AREA OF WHICH IS CAPABLE OF\nBEING ENCLOSED IN A SQUARE THE SIDE OF WHICH IS LESS THAN 7 CM: OTHER",
+        "hsn_code": "69071090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNGLAZED CERAMIC FLAGS AND PAVING, HEARTH OR WALL TILES; UNGLAZED CERAMIC MOSAIC\nCUBES AND THE LIKE, WHETHER OR NOT ON A BACKING - TILES, CUBES AND SIMILAR ARTICLES,\nWHETHER OR NOT RECTANGULAR, THE LARGEST SURFACE AREA OF WHICH IS CAPABLE OF\nBEING ENCLOSED IN A SQUARE THE SIDE OF WHICH IS LESS THAN 7 CM: VITRIFIED TILES,\nWHETHER POLISHED OR NOT",
+        "hsn_code": "69071010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Ceramic pipes, conduits, guttering and pipe fittings",
+        "hsn_code": "6906",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CERAMIC PIPES, CONDUITS, GUTTERING AND PIPE FITTINGS",
+        "hsn_code": "69060000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ROOFING TILES, CHIMNEY-POTS, COWLS, CHIMNEY LINERS, ARCHITECTURAL ORNAMENTS AND OTHER CERAMIC CONSTRUCTIONAL GOODS",
+        "hsn_code": "6905",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ROOFING TILES, CHIMNEY-POTS, COWLS, CHIMNEY LINERS, ARCHITECTURAL ORNAMENTS AND\nOTHER CERAMIC CONSTRUCTIONAL GOODS - OTHER",
+        "hsn_code": "69059000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CERAMIC BUILDING BRICKS, FLOORING BLOCKS, SUPPORT OR FILLER TILES AND THE LIKE",
+        "hsn_code": "6904",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CERAMIC BUILDING BRICKS, FLOORING BLOCKS, SUPPORT OR FILLER TILES AND THE LIKE - OTHER",
+        "hsn_code": "69049000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER REFRACTORY CERAMIC GOODS (FOR EXAMPLE, RETORTS, CRUCIBLES, MUFFLES, NOZZLES, PLUGS, SUPPORTS, CUPELS, TUBES, PIPES, SHEATHS AND RODS), OTHER THAN THOSE OF SILICEOUS FOSSIL MEALS OR OF SIMILAR SILICEOUS EARTHS",
+        "hsn_code": "6903",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER REFRACTORY CERAMIC GOODS (FOR EXAMPLE, RETORTS, CRUCIBLES, MUFFLES,\nNOZZLES, PLUGS, SUPPORTS, CUPELS, TUBES, PIPES, SHEATHS AND RODS), OTHER THAN THOSE\nOF SILICEOUS FOSSIL MEALS OR OF SIMILAR SILICEOUS EARTHS - OTHER: OTHER",
+        "hsn_code": "69039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER REFRACTORY CERAMIC GOODS (FOR EXAMPLE, RETORTS, CRUCIBLES, MUFFLES,\nNOZZLES, PLUGS, SUPPORTS, CUPELS, TUBES, PIPES, SHEATHS AND RODS), OTHER THAN THOSE\nOF SILICEOUS FOSSIL MEALS OR OF SIMILAR SILICEOUS EARTHS - OTHER: MONOLITHICS OR\nCASTABLES (FIRE-CLAY, BASIC, SILICA, HIGH ALUMINA, INSULATING)",
+        "hsn_code": "69039040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER REFRACTORY CERAMIC GOODS (FOR EXAMPLE, RETORTS, CRUCIBLES, MUFFLES,\nNOZZLES, PLUGS, SUPPORTS, CUPELS, TUBES, PIPES, SHEATHS AND RODS), OTHER THAN THOSE\nOF SILICEOUS FOSSIL MEALS OR OF SIMILAR SILICEOUS EARTHS - OTHER: CERAMIC FIBRES",
+        "hsn_code": "69039030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER REFRACTORY CERAMIC GOODS (FOR EXAMPLE, RETORTS, CRUCIBLES, MUFFLES,\nNOZZLES, PLUGS, SUPPORTS, CUPELS, TUBES, PIPES, SHEATHS AND RODS), OTHER THAN THOSE\nOF SILICEOUS FOSSIL MEALS OR OF SIMILAR SILICEOUS EARTHS - OTHER: BASALT TILES",
+        "hsn_code": "69039020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER REFRACTORY CERAMIC GOODS (FOR EXAMPLE, RETORTS, CRUCIBLES, MUFFLES,\nNOZZLES, PLUGS, SUPPORTS, CUPELS, TUBES, PIPES, SHEATHS AND RODS), OTHER THAN THOSE\nOF SILICEOUS FOSSIL MEALS OR OF SIMILAR SILICEOUS EARTHS - OTHER: ZIRCON OR ZIRCON\nMULLITE REFRACTORIES",
+        "hsn_code": "69039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER REFRACTORY CERAMIC GOODS (FOR EXAMPLE, RETORTS, CRUCIBLES, MUFFLES,\nNOZZLES, PLUGS, SUPPORTS, CUPELS, TUBES, PIPES, SHEATHS AND RODS), OTHER THAN THOSE\nOF SILICEOUS FOSSIL MEALS OR OF SIMILAR SILICEOUS EARTHS - CONTAINING BY WEIGHT MORE\nTHAN 50% OF ALUMINA (AL2O3) OR OF A MIXTURE OR COMPOUND OF ALUMINA AND OF SILICA\n(SIO2): OTHER",
+        "hsn_code": "69032090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER REFRACTORY CERAMIC GOODS (FOR EXAMPLE, RETORTS, CRUCIBLES, MUFFLES,\nNOZZLES, PLUGS, SUPPORTS, CUPELS, TUBES, PIPES, SHEATHS AND RODS), OTHER THAN THOSE\nOF SILICEOUS FOSSIL MEALS OR OF SIMILAR SILICEOUS EARTHS - CONTAINING BY WEIGHT MORE\nTHAN 50% OF ALUMINA (AL2O3) OR OF A MIXTURE OR COMPOUND OF ALUMINA AND OF SILICA\n(SIO2): SILICON CARBIDE CRUCIBLES",
+        "hsn_code": "69032010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER REFRACTORY CERAMIC GOODS (FOR EXAMPLE, RETORTS, CRUCIBLES, MUFFLES,\nNOZZLES, PLUGS, SUPPORTS, CUPELS, TUBES, PIPES, SHEATHS AND RODS), OTHER THAN THOSE\nOF SILICEOUS FOSSIL MEALS OR OF SIMILAR SILICEOUS EARTHS - CONTAINING BY WEIGHT MORE\nTHAN 50% OF GRAPHITE OR OTHER CARBON OR OF A MIXTURE OF THESE PRODUCTS :OTHER",
+        "hsn_code": "69031090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER REFRACTORY CERAMIC GOODS (FOR EXAMPLE, RETORTS, CRUCIBLES, MUFFLES,\nNOZZLES, PLUGS, SUPPORTS, CUPELS, TUBES, PIPES, SHEATHS AND RODS), OTHER THAN THOSE\nOF SILICEOUS FOSSIL MEALS OR OF SIMILAR SILICEOUS EARTHS - CONTAINING BY WEIGHT MORE\nTHAN 50% OF GRAPHITE OR OTHER CARBON OR OF A MIXTURE OF THESE PRODUCTS :\nMAGNESIA CARBON BRICKS, SHAPES AND GRAPHETISED ALUMINA",
+        "hsn_code": "69031010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRACTORY BRICKS, BLOCKS, TILES AND SIMILAR REFRACTORY CERAMIC CONSTRUCTIONAL GOODS, OTHER THAN THOSE OF SILICEOUS FOSSIL MEALS OR SIMILAR SILICEOUS EARTHS",
+        "hsn_code": "6902",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRACTORY BRICKS, BLOCKS, TILES AND SIMILAR REFRACTORY CERAMIC CONSTRUCTIONAL\nGOODS, OTHER THAN THOSE OF SILICEOUS FOSSIL MEALS OR SIMILAR SILICEOUS EARTHS -\nOTHER: OTHER",
+        "hsn_code": "69029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRACTORY BRICKS, BLOCKS, TILES AND SIMILAR REFRACTORY CERAMIC CONSTRUCTIONAL\nGOODS, OTHER THAN THOSE OF SILICEOUS FOSSIL MEALS OR SIMILAR SILICEOUS EARTHS -\nOTHER: CLAY GRAPHITE STOPPER HEADS",
+        "hsn_code": "69029040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRACTORY BRICKS, BLOCKS, TILES AND SIMILAR REFRACTORY CERAMIC CONSTRUCTIONAL\nGOODS, OTHER THAN THOSE OF SILICEOUS FOSSIL MEALS OR SIMILAR SILICEOUS EARTHS -\nOTHER: VERMICULITE INSULATION BRICKS",
+        "hsn_code": "69029030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRACTORY BRICKS, BLOCKS, TILES AND SIMILAR REFRACTORY CERAMIC CONSTRUCTIONAL\nGOODS, OTHER THAN THOSE OF SILICEOUS FOSSIL MEALS OR SIMILAR SILICEOUS EARTHS -\nOTHER: GRAPHITE BRICKS AND SHAPES",
+        "hsn_code": "69029020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRACTORY BRICKS, BLOCKS, TILES AND SIMILAR REFRACTORY CERAMIC CONSTRUCTIONAL\nGOODS, OTHER THAN THOSE OF SILICEOUS FOSSIL MEALS OR SIMILAR SILICEOUS EARTHS -\nOTHER: FIRE CLAY BRICKS AND SHAPES",
+        "hsn_code": "69029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRACTORY BRICKS, BLOCKS, TILES AND SIMILAR REFRACTORY CERAMIC CONSTRUCTIONAL\nGOODS, OTHER THAN THOSE OF SILICEOUS FOSSIL MEALS OR SIMILAR SILICEOUS EARTHS -\nCONTAINING BY WEIGHT MORE THAN 50% OF ALUMINA (AL2O3), OF SILICA (SIO2) OR OF A\nMIXTURE OR COMPOUND OF THESE PRODUCTS: OTHER",
+        "hsn_code": "69022090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRACTORY BRICKS, BLOCKS, TILES AND SIMILAR REFRACTORY CERAMIC CONSTRUCTIONAL\nGOODS, OTHER THAN THOSE OF SILICEOUS FOSSIL MEALS OR SIMILAR SILICEOUS EARTHS -\nCONTAINING BY WEIGHT MORE THAN 50% OF ALUMINA (AL2O3), OF SILICA (SIO2) OR OF A\nMIXTURE OR COMPOUND OF THESE PRODUCTS: MULLITE BRICKS",
+        "hsn_code": "69022050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRACTORY BRICKS, BLOCKS, TILES AND SIMILAR REFRACTORY CERAMIC CONSTRUCTIONAL\nGOODS, OTHER THAN THOSE OF SILICEOUS FOSSIL MEALS OR SIMILAR SILICEOUS EARTHS -\nCONTAINING BY WEIGHT MORE THAN 50% OF ALUMINA (AL2O3), OF SILICA (SIO2) OR OF A\nMIXTURE OR COMPOUND OF THESE PRODUCTS: SILICON CARBIDE BRICKS AND SHAPES",
+        "hsn_code": "69022040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRACTORY BRICKS, BLOCKS, TILES AND SIMILAR REFRACTORY CERAMIC CONSTRUCTIONAL\nGOODS, OTHER THAN THOSE OF SILICEOUS FOSSIL MEALS OR SIMILAR SILICEOUS EARTHS -\nCONTAINING BY WEIGHT MORE THAN 50% OF ALUMINA (AL2O3), OF SILICA (SIO2) OR OF A\nMIXTURE OR COMPOUND OF THESE PRODUCTS: ALUMINA CARBON BRICKS AND SHAPES",
+        "hsn_code": "69022030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRACTORY BRICKS, BLOCKS, TILES AND SIMILAR REFRACTORY CERAMIC CONSTRUCTIONAL\nGOODS, OTHER THAN THOSE OF SILICEOUS FOSSIL MEALS OR SIMILAR SILICEOUS EARTHS -\nCONTAINING BY WEIGHT MORE THAN 50% OF ALUMINA (AL2O3), OF SILICA (SIO2) OR OF A\nMIXTURE OR COMPOUND OF THESE PRODUCTS: HIGH ALUMINA BRICKS AND SHAPES",
+        "hsn_code": "69022020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRACTORY BRICKS, BLOCKS, TILES AND SIMILAR REFRACTORY CERAMIC CONSTRUCTIONAL\nGOODS, OTHER THAN THOSE OF SILICEOUS FOSSIL MEALS OR SIMILAR SILICEOUS EARTHS -\nCONTAINING BY WEIGHT MORE THAN 50% OF ALUMINA (AL2O3), OF SILICA (SIO2) OR OF A\nMIXTURE OR COMPOUND OF THESE PRODUCTS: SILICA BRICKS AND SHAPES",
+        "hsn_code": "69022010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRACTORY BRICKS, BLOCKS, TILES AND SIMILAR REFRACTORY CERAMIC CONSTRUCTIONAL\nGOODS, OTHER THAN THOSE OF SILICEOUS FOSSIL MEALS OR SIMILAR SILICEOUS EARTHS -\nCONTAINING BY WEIGHT, SINGLY OR TOGETHER, MORE THAN 50% OF THE ELEMENTS MG, CA\nOR CR, EXPRESSED AS MGO, CAO OR CR2O3: OTHER",
+        "hsn_code": "69021090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRACTORY BRICKS, BLOCKS, TILES AND SIMILAR REFRACTORY CERAMIC CONSTRUCTIONAL\nGOODS, OTHER THAN THOSE OF SILICEOUS FOSSIL MEALS OR SIMILAR SILICEOUS EARTHS -\nCONTAINING BY WEIGHT, SINGLY OR TOGETHER, MORE THAN 50% OF THE ELEMENTS MG, CA\nOR CR, EXPRESSED AS MGO, CAO OR CR2O3: DIRECT BONDED BASIC BRICKS AND SHAPES",
+        "hsn_code": "69021050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRACTORY BRICKS, BLOCKS, TILES AND SIMILAR REFRACTORY CERAMIC CONSTRUCTIONAL\nGOODS, OTHER THAN THOSE OF SILICEOUS FOSSIL MEALS OR SIMILAR SILICEOUS EARTHS -\nCONTAINING BY WEIGHT, SINGLY OR TOGETHER, MORE THAN 50% OF THE ELEMENTS MG, CA\nOR CR, EXPRESSED AS MGO, CAO OR CR2O3: MAGNESIA CARBON BRICKS AND SHAPES",
+        "hsn_code": "69021040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRACTORY BRICKS, BLOCKS, TILES AND SIMILAR REFRACTORY CERAMIC CONSTRUCTIONAL\nGOODS, OTHER THAN THOSE OF SILICEOUS FOSSIL MEALS OR SIMILAR SILICEOUS EARTHS -\nCONTAINING BY WEIGHT, SINGLY OR TOGETHER, MORE THAN 50% OF THE ELEMENTS MG, CA\nOR CR, EXPRESSED AS MGO, CAO OR CR2O3: MAGNESITE CHROME BRICKS AND SHAPES",
+        "hsn_code": "69021030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRACTORY BRICKS, BLOCKS, TILES AND SIMILAR REFRACTORY CERAMIC CONSTRUCTIONAL\nGOODS, OTHER THAN THOSE OF SILICEOUS FOSSIL MEALS OR SIMILAR SILICEOUS EARTHS -\nCONTAINING BY WEIGHT, SINGLY OR TOGETHER, MORE THAN 50% OF THE ELEMENTS MG, CA\nOR CR, EXPRESSED AS MGO, CAO OR CR2O3: CHROME MAGNESITE BRICKS",
+        "hsn_code": "69021020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRACTORY BRICKS, BLOCKS, TILES AND SIMILAR REFRACTORY CERAMIC CONSTRUCTIONAL\nGOODS, OTHER THAN THOSE OF SILICEOUS FOSSIL MEALS OR SIMILAR SILICEOUS EARTHS -\nCONTAINING BY WEIGHT, SINGLY OR TOGETHER, MORE THAN 50% OF THE ELEMENTS MG, CA\nOR CR, EXPRESSED AS MGO, CAO OR CR2O3: MAGNESITE BRICKS AND SHAPES",
+        "hsn_code": "69021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BRICKS, BLOCKS, TILES AND OTHER CERAMIC GOODS, OF SILICEOUS FOSSIL MEALS (FOR EXAMPLE, KIESELGUHR, TRIPOLITE OR DIATOMITE) OR OF SIMILAR SILICEOUS EARTHS",
+        "hsn_code": "6901",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BRICKS, BLOCKS, TILES AND OTHER CERAMIC GOODS OF SILICEOUS FOSSIL MEALS (FOR\nEXAMPLE, KIESELGUHR, TRIPOLITE OR DIATOMITE) OR OF SIMILAR SILICEOUS EARTHS - BRICKS,\nBLOCKS, TILES AND OTHER CERAMIC GOODS OF SILICEOUS FOSSIL MEALS (FOR EXAMPLE,\nKIESELGUHR, TRIPOLITE OR DIATOMITE) OR OF SIMILAR SILICEOUS EARTHS:OTHER",
+        "hsn_code": "69010090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BRICKS, BLOCKS, TILES AND OTHER CERAMIC GOODS OF SILICEOUS FOSSIL MEALS (FOR\nEXAMPLE, KIESELGUHR, TRIPOLITE OR DIATOMITE) OR OF SIMILAR SILICEOUS EARTHS - BRICKS,\nBLOCKS, TILES AND OTHER CERAMIC GOODS OF SILICEOUS FOSSIL MEALS (FOR EXAMPLE,\nKIESELGUHR, TRIPOLITE OR DIATOMITE) OR OF SIMILAR SILICEOUS EARTHS:TILES",
+        "hsn_code": "69010030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BRICKS, BLOCKS, TILES AND OTHER CERAMIC GOODS OF SILICEOUS FOSSIL MEALS (FOR\nEXAMPLE, KIESELGUHR, TRIPOLITE OR DIATOMITE) OR OF SIMILAR SILICEOUS EARTHS - BRICKS,\nBLOCKS, TILES AND OTHER CERAMIC GOODS OF SILICEOUS FOSSIL MEALS (FOR EXAMPLE,\nKIESELGUHR, TRIPOLITE OR DIATOMITE) OR OF SIMILAR SILICEOUS EARTHS:BLOCKS",
+        "hsn_code": "69010020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF STONE OR OF OTHER MINERAL SUBSTANCES (INCLUDING CARBON FIBRES, ARTICLES\nOF CARBON FIBRES AND ARTICLES OF PEAT), NOT ELSEWHERE SPECIFIED OR INCLUDED NON\nELECTRICAL ARTICLES OF GRAPHITE OR OTHER CARBON: GRAPHITE FILTER CANDLE",
+        "hsn_code": "68151010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF STONE OR OF OTHER MINERAL SUBSTANCES (INCLUDING CARBON FIBRES, ARTICLES\nOF CARBON FIBRES AND ARTICLES OF PEAT), NOT ELSEWHERE SPECIFIED OR INCLUDED NON\nELECTRICAL ARTICLES OF GRAPHITE OR OTHER CARBON: NON-ELECTRICAL ARTICLES OF\nGRAPHITE",
+        "hsn_code": "68151020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF STONE OR OF OTHER MINERAL SUBSTANCES (INCLUDING CARBON FIBRES, ARTICLES\nOF CARBON FIBRES AND ARTICLES OF PEAT), NOT ELSEWHERE SPECIFIED OR INCLUDED NON\nELECTRICAL ARTICLES OF GRAPHITE OR OTHER CARBON: OTHER",
+        "hsn_code": "68151090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF STONE OR OF OTHER MINERAL SUBSTANCES (INCLUDING CARBON FIBRES, ARTICLES\nOF CARBON FIBRES AND ARTICLES OF PEAT), NOT ELSEWHERE SPECIFIED OR INCLUDED NON\nELECTRICAL ARTICLES OF GRAPHITE OR OTHER CARBON: ARTICLES OF PEAT",
+        "hsn_code": "68152000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF STONE OR OF OTHER MINERAL SUBSTANCES (INCLUDING CARBON FIBRES, ARTICLES\nOF CARBON FIBRES AND ARTICLES OF PEAT), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER\nARTICLES: CONTAINING MAGNESITE, DOLOMITE OR CHROMITE",
+        "hsn_code": "68159100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF STONE OR OF OTHER MINERAL SUBSTANCES (INCLUDING CARBON FIBRES, ARTICLES\nOF CARBON FIBRES AND ARTICLES OF PEAT), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER\nARTICLES: OTHER: BRICKS AND TILES OF FLY ASH",
+        "hsn_code": "68159910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF STONE OR OF OTHER MINERAL SUBSTANCES (INCLUDING CARBON FIBRES, ARTICLES\nOF CARBON FIBRES AND ARTICLES OF PEAT), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER\nARTICLES: OTHER: SANITARY WARES, KITCHEN WARES AND OTHER MADE UP ARTICLES OF FLY\nASH",
+        "hsn_code": "68159920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF STONE OR OF OTHER MINERAL SUBSTANCES (INCLUDING CARBON FIBRES, ARTICLES OF CARBON FIBRES AND ARTICLES OF PEAT), NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "6815",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MICA AND ARTICLES OF MICA, INCLUDING AGGLOMERATED OR RECONSTITUTED MICA,\nWHETHER OR NOT ON A SUPPORT OF PAPER, PAPERBOARD OR OTHER MATERIALS PLATES,\nSHEETS AND STRIPS OF AGGLOMERATED OR RECONSTITUTED MICA, WHETHER OR NOT ON A\nSUPPORT: CUT MICA CONDENSER FILMS OR PLATES",
+        "hsn_code": "68141010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MICA AND ARTICLES OF MICA, INCLUDING AGGLOMERATED OR RECONSTITUTED MICA,\nWHETHER OR NOT ON A SUPPORT OF PAPER, PAPERBOARD OR OTHER MATERIALS PLATES,\nSHEETS AND STRIPS OF AGGLOMERATED OR RECONSTITUTED MICA, WHETHER OR NOT ON A\nSUPPORT: SHEETS AND STRIPS CUT TO SHAPE",
+        "hsn_code": "68141020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MICA AND ARTICLES OF MICA, INCLUDING AGGLOMERATED OR RECONSTITUTED MICA,\nWHETHER OR NOT ON A SUPPORT OF PAPER, PAPERBOARD OR OTHER MATERIALS PLATES,\nSHEETS AND STRIPS OF AGGLOMERATED OR RECONSTITUTED MICA, WHETHER OR NOT ON A\nSUPPORT: WASHERS AND DISCS",
+        "hsn_code": "68141030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MICA AND ARTICLES OF MICA, INCLUDING AGGLOMERATED OR RECONSTITUTED MICA,\nWHETHER OR NOT ON A SUPPORT OF PAPER, PAPERBOARD OR OTHER MATERIALS PLATES,\nSHEETS AND STRIPS OF AGGLOMERATED OR RECONSTITUTED MICA, WHETHER OR NOT ON A\nSUPPORT: OTHER",
+        "hsn_code": "68141090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MICA AND ARTICLES OF MICA, INCLUDING AGGLOMERATED OR RECONSTITUTED MICA,\nWHETHER OR NOT ON A SUPPORT OF PAPER, PAPERBOARD OR OTHER MATERIALS OTHER: MICA\nSTACKED UNITS",
+        "hsn_code": "68149010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MICA AND ARTICLES OF MICA, INCLUDING AGGLOMERATED OR RECONSTITUTED MICA,\nWHETHER OR NOT ON A SUPPORT OF PAPER, PAPERBOARD OR OTHER MATERIALS OTHER:\nSILVERED MICA, CAPACITOR PLATES OR SILVERED MICA PLATES",
+        "hsn_code": "68149020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MICA AND ARTICLES OF MICA, INCLUDING AGGLOMERATED OR RECONSTITUTED MICA,\nWHETHER OR NOT ON A SUPPORT OF PAPER, PAPERBOARD OR OTHER MATERIALS OTHER:\nMICANITE AND ALL SORTS OF BUILT UP MICA",
+        "hsn_code": "68149030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MICA AND ARTICLES OF MICA, INCLUDING AGGLOMERATED OR RECONSTITUTED MICA,\nWHETHER OR NOT ON A SUPPORT OF PAPER, PAPERBOARD OR OTHER MATERIALS OTHER:\nMICAPAPER OR RECONSTITUTED MICA PAPER",
+        "hsn_code": "68149040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MICA AND ARTICLES OF MICA, INCLUDING AGGLOMERATED OR RECONSTITUTED MICA,\nWHETHER OR NOT ON A SUPPORT OF PAPER, PAPERBOARD OR OTHER MATERIALS OTHER:\nMOULDED GLASS BONDED OR GLASS BONDED MICA",
+        "hsn_code": "68149050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MICA AND ARTICLES OF MICA, INCLUDING AGGLOMERATED OR RECONSTITUTED MICA,\nWHETHER OR NOT ON A SUPPORT OF PAPER, PAPERBOARD OR OTHER MATERIALS OTHER: MICA\nBRICKS",
+        "hsn_code": "68149060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MICA AND ARTICLES OF MICA, INCLUDING AGGLOMERATED OR RECONSTITUTED MICA,\nWHETHER OR NOT ON A SUPPORT OF PAPER, PAPERBOARD OR OTHER MATERIALS OTHER:\nOTHER",
+        "hsn_code": "68149090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MICA AND ARTICLES OF MICA, INCLUDING AGGLOMERATED OR RECONSTITUTED MICA, WHETHER OR NOT ON A SUPPORT OF PAPER, PAPERBOARD OR OTHER MATERIALS",
+        "hsn_code": "6814",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FRICTION MATERIAL AND ARTICLES THEREOF (FOR EXAMPLE, SHEETS, ROLLS, STRIPS, SEGMENTS,\nDISCS, WASHERS, PADS), NOT MOUNTED, FOR BRAKES, FOR CLUTCHES OR THE LIKE, WITH A\nBASIS OF ASBESTOS, OF OTHER MINERAL SUBSTANCES OR OF CELLULOSE, WHETHER OR NOT\nCOMBINED WITH TEXTILE OR OTHER MATERIALS BRAKE LININGS AND PADS",
+        "hsn_code": "68131000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BRAKE LINING AND PADS CONTAINING ASBESTOS",
+        "hsn_code": "68132010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS FRICTION MATERIALS",
+        "hsn_code": "68132090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FRICTION MATERIAL AND ARTICLES THEREOF (FOR EXAMPLE, SHEETS, ROLLS, STRIPS, SEGMENTS,\nDISCS, WASHERS, PADS), NOT MOUNTED, FOR BRAKES, FOR CLUTCHES OR THE LIKE, WITH A\nBASIS OF ASBESTOS, OF OTHER MINERAL SUBSTANCES OR OF CELLULOSE, WHETHER OR NOT\nCOMBINED WITH TEXTILE OR OTHER MATERIALS OTHER: -- NOT CONTAINING ASBESTOS --\nBRAKE LINING AND PADS",
+        "hsn_code": "68138100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER MATERIALS NOT CONTAINING ASBESTOS",
+        "hsn_code": "68138900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FRICTION MATERIAL AND ARTICLES THEREOF (FOR EXAMPLE, SHEETS, ROLLS, STRIPS, SEGMENTS,\nDISCS, WASHERS, PADS), NOT MOUNTED, FOR BRAKES, FOR CLUTCHES OR THE LIKE, WITH A\nBASIS OF ASBESTOS, OF OTHER MINERAL SUBSTANCES OR OF CELLULOSE, WHETHER OR NOT\nCOMBINED WITH TEXTILE OR OTHER MATERIALS OTHER: ASBESTOS FRICTION MATERIALS",
+        "hsn_code": "68139010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FRICTION MATERIAL AND ARTICLES THEREOF (FOR EXAMPLE, SHEETS, ROLLS, STRIPS, SEGMENTS,\nDISCS, WASHERS, PADS), NOT MOUNTED, FOR BRAKES, FOR CLUTCHES OR THE LIKE, WITH A\nBASIS OF ASBESTOS, OF OTHER MINERAL SUBSTANCES OR OF CELLULOSE, WHETHER OR NOT\nCOMBINED WITH TEXTILE OR OTHER MATERIALS OTHER: OTHER",
+        "hsn_code": "68139090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FRICTION MATERIAL AND ARTICLES THEREOF (FOR EXAMPLE, SHEETS, ROLLS, STRIPS, SEGMENTS,\nDISCS, WASHERS, PADS), NOT MOUNTED, FOR BRAKES, FOR CLUTCHES OR THE LIKE, WITH A\nBASIS OF ASBESTOS, OF OTHER MINERAL SUBSTANCES OR OF CELLULOSE, WHETHER OR NOT\nCOMBINED WITH TEXTILE OR OTHER MATERIALS OTHER: -- NOT CONTAINING ASBESTOS --\nOTHER",
+        "hsn_code": "68139100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FRICTION MATERIAL AND ARTICLES THEREOF (FOR EXAMPLE, SHEETS, ROLLS, STRIPS, SEGMENTS, DISCS, WASHERS, PADS ), NOT MOUNTED, FOR BRAKES, FOR CLUTCHES OR THE LIKE, WITH A BASIS OF ASBESTOS, OF OTHER MINERAL SUBSTANCES OR OF CELLULOSE, WHETHER OR NOT COMBINED WITH TEXTILE OR OTHER MATERIALS",
+        "hsn_code": "6813",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FABRICATED ASBESTOS FIBRES; MIXTURES WITH A BASIS OF ASBESTOS OR WITH A BASIS OF\nASBESTOS AND MAGNESIUM CARBONATE; ARTICLES OF SUCH MIXTURES OR OF ASBESTOS (FOR\nEXAMPLE, THREAD, WOVEN FABRIC, CLOTHING, HEADGEAR, FOOTWEAR, GASKETS), WHETHER\nOR NOT REINFORCED, OTHER THAN GOODS OF HEADING 6811 OR 6813 CLOTHING, CLOTHING\nACCESSORIES, FOOTWEAR AND HEADGEAR",
+        "hsn_code": "68125000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FABRICATED ASBESTOS FIBRES; MIXTURES WITH A BASIS OF ASBESTOS OR WITH A BASIS OF\nASBESTOS AND MAGNESIUM CARBONATE; ARTICLES OF SUCH MIXTURES OR OF ASBESTOS (FOR\nEXAMPLE, THREAD, WOVEN FABRIC, CLOTHING, HEADGEAR, FOOTWEAR, GASKETS), WHETHER\nOR NOT REINFORCED, OTHER THAN GOODS OF HEADING 6811 OR 6813 PAPER, MILLBOARD AND\nFELT: MILLBOARD: ASBESTOS",
+        "hsn_code": "68126011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FABRICATED ASBESTOS FIBRES; MIXTURES WITH A BASIS OF ASBESTOS OR WITH A BASIS OF\nASBESTOS AND MAGNESIUM CARBONATE; ARTICLES OF SUCH MIXTURES OR OF ASBESTOS (FOR\nEXAMPLE, THREAD, WOVEN FABRIC, CLOTHING, HEADGEAR, FOOTWEAR, GASKETS), WHETHER\nOR NOT REINFORCED, OTHER THAN GOODS OF HEADING 6811 OR 6813 PAPER, MILLBOARD AND\nFELT: MILLBOARD: OTHER",
+        "hsn_code": "68126019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FABRICATED ASBESTOS FIBRES; MIXTURES WITH A BASIS OF ASBESTOS OR WITH A BASIS OF\nASBESTOS AND MAGNESIUM CARBONATE; ARTICLES OF SUCH MIXTURES OR OF ASBESTOS (FOR\nEXAMPLE, THREAD, WOVEN FABRIC, CLOTHING, HEADGEAR, FOOTWEAR, GASKETS), WHETHER\nOR NOT REINFORCED, OTHER THAN GOODS OF HEADING 6811 OR 6813 PAPER, MILLBOARD AND\nFELT: OTHER",
+        "hsn_code": "68126090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FABRICATED ASBESTOS FIBRES; MIXTURES WITH A BASIS OF ASBESTOS OR WITH A BASIS OF\nASBESTOS AND MAGNESIUM CARBONATE; ARTICLES OF SUCH MIXTURES OR OF ASBESTOS (FOR\nEXAMPLE, THREAD, WOVEN FABRIC, CLOTHING, HEADGEAR, FOOTWEAR, GASKETS), WHETHER\nOR NOT REINFORCED, OTHER THAN GOODS OF HEADING 6811 OR 6813 COMPRESSED ASBESTOS\nFIBRE JOINTING, IN SHEETS OR ROLLS",
+        "hsn_code": "68127000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GOODS, OF CROCIDOLITE",
+        "hsn_code": "68128000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FABRICATED ASBESTOS FIBRES; MIXTURES WITH A BASIS OF ASBESTOS OR WITH A BASIS OF\nASBESTOS AND MAGNESIUM CARBONATE; ARTICLES OF SUCH MIXTURES OR OF ASBESTOS (FOR\nEXAMPLE, THREAD, WOVEN FABRIC, CLOTHING, HEADGEAR, FOOTWEAR, GASKETS), WHETHER\nOR NOT REINFORCED, OTHER THAN GOODS OF HEADING 6811 OR 6813 OTHER: LAGGING\nCOMPOUNDS: ASBESTOS",
+        "hsn_code": "68129011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FABRICATED ASBESTOS FIBRES; MIXTURES WITH A BASIS OF ASBESTOS OR WITH A BASIS OF\nASBESTOS AND MAGNESIUM CARBONATE; ARTICLES OF SUCH MIXTURES OR OF ASBESTOS (FOR\nEXAMPLE, THREAD, WOVEN FABRIC, CLOTHING, HEADGEAR, FOOTWEAR, GASKETS), WHETHER\nOR NOT REINFORCED, OTHER THAN GOODS OF HEADING 6811 OR 6813 OTHER: LAGGING\nCOMPOUNDS: OTHER",
+        "hsn_code": "68129019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FABRICATED ASBESTOS FIBRES; MIXTURES WITH A BASIS OF ASBESTOS OR WITH A BASIS OF\nASBESTOS AND MAGNESIUM CARBONATE; ARTICLES OF SUCH MIXTURES OR OF ASBESTOS (FOR\nEXAMPLE, THREAD, WOVEN FABRIC, CLOTHING, HEADGEAR, FOOTWEAR, GASKETS), WHETHER\nOR NOT REINFORCED, OTHER THAN GOODS OF HEADING 6811 OR 6813 OTHER: ASBESTOS\nPACKING JOINTS AND GASKETS: PACKING JOINTS",
+        "hsn_code": "68129021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FABRICATED ASBESTOS FIBRES; MIXTURES WITH A BASIS OF ASBESTOS OR WITH A BASIS OF\nASBESTOS AND MAGNESIUM CARBONATE; ARTICLES OF SUCH MIXTURES OR OF ASBESTOS (FOR\nEXAMPLE, THREAD, WOVEN FABRIC, CLOTHING, HEADGEAR, FOOTWEAR, GASKETS), WHETHER\nOR NOT REINFORCED, OTHER THAN GOODS OF HEADING 6811 OR 6813 OTHER: ASBESTOS\nPACKING JOINTS AND GASKETS: GASKETS",
+        "hsn_code": "68129022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FABRICATED ASBESTOS FIBRES; MIXTURES WITH A BASIS OF ASBESTOS OR WITH A BASIS OF\nASBESTOS AND MAGNESIUM CARBONATE; ARTICLES OF SUCH MIXTURES OR OF ASBESTOS (FOR\nEXAMPLE, THREAD, WOVEN FABRIC, CLOTHING, HEADGEAR, FOOTWEAR, GASKETS), WHETHER\nOR NOT REINFORCED, OTHER THAN GOODS OF HEADING 6811 OR 6813 OTHER: ASBESTOS\nPACKING JOINTS AND GASKETS: OTHER",
+        "hsn_code": "68129090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CLOTHING, CLOTHING ACCESSORIES, FOOTWEAR AND HEADWEAR",
+        "hsn_code": "68129100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS MILLBOARD",
+        "hsn_code": "68129211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, MILLBOARD AND FELT",
+        "hsn_code": "68129219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, MILLBOARD AND FELT --- OTHERS",
+        "hsn_code": "68129290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPRESSED ASBESTOS FIBRE JOINTING IN SHEETS OR ROLLS",
+        "hsn_code": "68129300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS LAGGING COMPOUNDS",
+        "hsn_code": "68129911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LAGGING COMPOUNDS - OTHERS",
+        "hsn_code": "68129919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS PACKING JOINTS",
+        "hsn_code": "68129921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS GASKETS",
+        "hsn_code": "68129922",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS PACKING JOINTS AND GASKETS - OTHERS",
+        "hsn_code": "68129990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FABRICATED ASBESTOS FIBRES; MIXTURES WITH A BASIS OF ASBESTOS OR WITH A BASIS OF ASBESTOS AND MAGNESIUM CARBONATE; ARTICLES OF SUCH MIXTURES OR OF ASBESTOS (FOR EXAMPLE, THREAD, WOVEN FABRIC, CLOTHING, HEAD-GEAR, FOOTWEAR, GASKETS), WHETHER OR NOT REINFORCED, OTHER THAN GOODS OF HEADING 6811 OR 6813",
+        "hsn_code": "6812",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF ASBESTOS-CEMENT, OF CELLULOSE FIBRECEMENT OR THE LIKE CORRUGATED\nSHEETS",
+        "hsn_code": "68111000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF ASBESTOS-CEMENT, OF CELLULOSE FIBRECEMENT OR THE LIKE OTHER SHEETS,\nPANELS, TILES AND SIMILAR ARTICLES: ASBESTOS-CEMENT SHEETS",
+        "hsn_code": "68112010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF ASBESTOS-CEMENT, OF CELLULOSE FIBRECEMENT OR THE LIKE OTHER SHEETS,\nPANELS, TILES AND SIMILAR ARTICLES: ASBESTOS-CEMENT TILES",
+        "hsn_code": "68112020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF ASBESTOS-CEMENT, OF CELLULOSE FIBRECEMENT OR THE LIKE OTHER SHEETS,\nPANELS, TILES AND SIMILAR ARTICLES: OTHER",
+        "hsn_code": "68112090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF ASBESTOS-CEMENT, OF CELLULOSE FIBRECEMENT OR THE LIKE TUBES, PIPES AND\nTUBE OR PIPE FITTINGS: ASBESTOS-CEMENT PIPES",
+        "hsn_code": "68113010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF ASBESTOS-CEMENT, OF CELLULOSE FIBRECEMENT OR THE LIKE TUBES, PIPES AND\nTUBE OR PIPE FITTINGS: OTHER",
+        "hsn_code": "68113090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS CEMENT SHEETS",
+        "hsn_code": "68114010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS CEMENT TILES",
+        "hsn_code": "68114020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHERS",
+        "hsn_code": "68114090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CORRUGATED SHEETS NOT CONTAINING ASBESTOS",
+        "hsn_code": "68118100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER SHEETS, TILES ETC. NOT CONTAINING ASBESTOS",
+        "hsn_code": "68118200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF ASBESTOS-CEMENT, OF CELLULOSE FIBRE-CEMENT OR THE LIKE-NOT CONTAINING\nASBESTOS--OTHER ARTICLES---TUBES,PIPES AND TUBE OR PIPE FITTINGS",
+        "hsn_code": "68118910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF ASBESTOS-CEMENT, OF CELLULOSE FIBRE-CEMENT OR THE LIKE-NOT CONTAINING\nASBESTOS--OTHER ARTICLES---OTHER",
+        "hsn_code": "68118990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF ASBESTOS-CEMENT, OF CELLULOSE FIBRECEMENT OR THE LIKE OTHER ARTICLES",
+        "hsn_code": "68119000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF ASBESTOS-CEMENT, OF CELLULOSE FIBRE-CEMENT OR THE LIKE",
+        "hsn_code": "6811",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF CEMENT, OF CONCRETE OR OF ARTIFICIAL STONE, WHETHER OR NOT REINFORCED -\nTILES, FLAGSTONES, BRICKS AND SIMILAR ARTICLES: BUILDING BLOCKS AND BRICKS: CEMENT\nBRICKS",
+        "hsn_code": "68101110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF CEMENT, OF CONCRETE OR OF ARTIFICIAL STONE, WHETHER OR NOT REINFORCED -\nTILES, FLAGSTONES, BRICKS AND SIMILAR ARTICLES: BUILDING BLOCKS AND BRICKS: OTHER",
+        "hsn_code": "68101190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF CEMENT, OF CONCRETE OR OF ARTIFICIAL STONE, WHETHER OR NOT REINFORCED -\nTILES, FLAGSTONES, BRICKS AND SIMILAR ARTICLES: OTHER: CEMENT TILES FOR MOSAIC",
+        "hsn_code": "68101910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF CEMENT, OF CONCRETE OR OF ARTIFICIAL STONE, WHETHER OR NOT REINFORCED -\nTILES, FLAGSTONES, BRICKS AND SIMILAR ARTICLES: OTHER: OTHER",
+        "hsn_code": "68101990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF CEMENT, OF CONCRETE OR OF ARTIFICIAL STONE, WHETHER OR NOT REINFORCED\nOTHER ARTICLES: PREFABRICATED STRUCTURAL COMPONENTS FOR BUILDING OR CIVIL\nENGINEERING",
+        "hsn_code": "68109100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF CEMENT, OF CONCRETE OR OF ARTIFICIAL STONE, WHETHER OR NOT REINFORCED\nOTHER ARTICLES: OTHER:CONCRETE BOULDER",
+        "hsn_code": "68109910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF CEMENT, OF CONCRETE OR OF ARTIFICIAL STONE, WHETHER OR NOT REINFORCED\nOTHER ARTICLES: OTHER: OTHER",
+        "hsn_code": "68109990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF CEMENT, OF CONCRETE OR OF ARTIFICIAL STONE, WHETHER OR NOT REINFORCED",
+        "hsn_code": "6810",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF PLASTER OR OF COMPOSITIONS BASED ON PLASTER - BOARDS, SHEETS, PANELS,\nTILES AND SIMILAR ARTICLES, NOT ORNAMENTED : FACED OR REINFORCED WITH PAPER OR\nPAPERBOARD ONLY",
+        "hsn_code": "68091100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF PLASTER OR OF COMPOSITIONS BASED ON PLASTER - BOARDS, SHEETS, PANELS,\nTILES AND SIMILAR ARTICLES, NOT ORNAMENTED : OTHER",
+        "hsn_code": "68091900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF PLASTER OR OF COMPOSITIONS BASED ON PLASTER - BOARDS, SHEETS, PANELS,\nTILES AND SIMILAR ARTICLES, NOT ORNAMENTED : OTHER ARTICLES",
+        "hsn_code": "68099000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF PLASTER OR OF COMPOSITIONS BASED ON PLASTER",
+        "hsn_code": "6809",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PANELS, BOARDS, TILES, BLOCKS AND SIMILAR ARTICLES OF VEGETABLE FIBRE, OF STRAW OR OF\nSHAVINGS, CHIPS,PARTICLES, SAWDUST OR OTHER WASTE, OF WOOD,AGGLOMERATED WITH\nCEMENT, PLASTER OR OTHER MINERAL BINDERS",
+        "hsn_code": "68080000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF ASPHALT OR OF SIMILAR MATERIAL (FOR EXAMPLE, PETROLEUM BITUMEN OR COAL TAR PITCH)",
+        "hsn_code": "6808",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF ASPHALT OR OF SIMILAR MATERIAL (FOR EXAMPLE, PETROLEUM BITUMEN OR\nCOAL TAR PITCH) IN-ROLLS: TARFELT ROOFING",
+        "hsn_code": "68071010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF ASPHALT OR OF SIMILAR MATERIAL (FOR EXAMPLE, PETROLEUM BITUMEN OR\nCOAL TAR PITCH) IN-ROLLS: OTHER",
+        "hsn_code": "68071090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF ASPHALT OR OF SIMILAR MATERIAL (FOR EXAMPLE, PETROLEUM BITUMEN OR\nCOAL TAR PITCH) OTHER: TARFELT ROOFING",
+        "hsn_code": "68079010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF ASPHALT OR OF SIMILAR MATERIAL (FOR EXAMPLE, PETROLEUM BITUMEN OR\nCOAL TAR PITCH) OTHER: OTHER",
+        "hsn_code": "68079090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF ASPHALT OR OF SIMILAR MATERIAL (FOR EXAMPLE, PETROLEUM BITUMEN OR COAL TAR PITCH)",
+        "hsn_code": "6807",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLAG WOOL, ROCK WOOL AND SIMILAR MINERAL WOOLS; EXFOLIATED VERMICULITE,\nEXPANDED CLAYS, FOAMED SLAG AND SIMILAR EXPANDED MINERAL MATERIALS; MIXTURES\nAND ARTICLES OF HEAT-INSULATING, SOUND-INSULATING OR SOUND-ABSORBING MINERAL\nMATERIALS, OTHER THAN THOSE OF HEADING 6811 OR 6812 OR OF CHAPTER 69 SLAG WOOL,\nROCK WOOL AND SIMILAR MINERAL WOOLS (INCLUDING INTERMIXTURES THEREOF), IN BULK,\nSHEETS OR ROLLS",
+        "hsn_code": "68061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLAG WOOL, ROCK WOOL AND SIMILAR MINERAL WOOLS; EXFOLIATED VERMICULITE,\nEXPANDED CLAYS, FOAMED SLAG AND SIMILAR EXPANDED MINERAL MATERIALS; MIXTURES\nAND ARTICLES OF HEAT-INSULATING, SOUND-INSULATING OR SOUND-ABSORBING MINERAL\nMATERIALS, OTHER THAN THOSE OF HEADING 6811 OR 6812 OR OF CHAPTER 69 EXFOLIATED\nVERMICULITE, EXPANDED CLAYS, FOAMED SLAG AND SIMILAR EXPANDED MINERAL MATERIALS\n(INCLUDING INTERMIXTURES THEREOF)",
+        "hsn_code": "68062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLAG WOOL, ROCK WOOL AND SIMILAR MINERAL WOOLS; EXFOLIATED VERMICULITE,\nEXPANDED CLAYS, FOAMED SLAG AND SIMILAR EXPANDED MINERAL MATERIALS; MIXTURES\nAND ARTICLES OF HEAT-INSULATING, SOUND-INSULATING OR SOUND-ABSORBING MINERAL\nMATERIALS, OTHER THAN THOSE OF HEADING 6811 OR 6812 OR OF CHAPTER 69 OTHER",
+        "hsn_code": "68069000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLAG WOOL, ROCK WOOL AND SIMILAR MINERAL WOOLS; EXFOLIATED VERMICULATE, EXPANDED CLAYS, FOAMED SLAG AND SIMILAR EXPANDED MINERAL MATERIALS; MIXTURES AND ARTICLES OF HEAT INSULATING, SOUND-INSULATING OR SOUND-ABSORBING MINERAL MATERIALS, OTHER THAN THOSE OF HEADING 6811 OR 6812 OR OF CHAPTER 69",
+        "hsn_code": "6806",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NATURAL OR ARTIFICIAL ABRASIVE POWDER OR GRAIN, ON A BASE OF TEXTILE MATERIAL, OF\nPAPER, OF PAPERBOARD OR OF OTHER MATERIALS, WHETHER OR NOT CUT TO SHAPE OR SEWN\nOR OTHERWISE MADE UP ON A BASE OF WOVEN TEXTILE FABRIC ONLY: ABRASIVE CLOTH",
+        "hsn_code": "68051010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NATURAL OR ARTIFICIAL ABRASIVE POWDER OR GRAIN, ON A BASE OF TEXTILE MATERIAL, OF\nPAPER, OF PAPERBOARD OR OF OTHER MATERIALS, WHETHER OR NOT CUT TO SHAPE OR SEWN\nOR OTHERWISE MADE UP ON A BASE OF WOVEN TEXTILE FABRIC ONLY: OTHER",
+        "hsn_code": "68051090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NATURAL OR ARTIFICIAL ABRASIVE POWDER OR GRAIN, ON A BASE OF TEXTILE MATERIAL, OF\nPAPER, OF PAPERBOARD OR OF OTHER MATERIALS, WHETHER OR NOT CUT TO SHAPE OR SEWN\nOR OTHERWISE MADE UP ON A BASE OF PAPER OR PAPERBOARD ONLY: EMERY OR CORUNDUM\nCOATED PAPER",
+        "hsn_code": "68052010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NATURAL OR ARTIFICIAL ABRASIVE POWDER OR GRAIN, ON A BASE OF TEXTILE MATERIAL, OF\nPAPER, OF PAPERBOARD OR OF OTHER MATERIALS, WHETHER OR NOT CUT TO SHAPE OR SEWN\nOR OTHERWISE MADE UP ON A BASE OF PAPER OR PAPERBOARD ONLY: FLINT COATED PAPER",
+        "hsn_code": "68052020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NATURAL OR ARTIFICIAL ABRASIVE POWDER OR GRAIN, ON A BASE OF TEXTILE MATERIAL, OF\nPAPER, OF PAPERBOARD OR OF OTHER MATERIALS, WHETHER OR NOT CUT TO SHAPE OR SEWN\nOR OTHERWISE MADE UP ON A BASE OF PAPER OR PAPERBOARD ONLY: GLASS OR SAND\nCOATED PAPER",
+        "hsn_code": "68052030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NATURAL OR ARTIFICIAL ABRASIVE POWDER OR GRAIN, ON A BASE OF TEXTILE MATERIAL, OF\nPAPER, OF PAPERBOARD OR OF OTHER MATERIALS, WHETHER OR NOT CUT TO SHAPE OR SEWN\nOR OTHERWISE MADE UP ON A BASE OF PAPER OR PAPERBOARD ONLY: OTHER ABRASIVE PAPER",
+        "hsn_code": "68052040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NATURAL OR ARTIFICIAL ABRASIVE POWDER OR GRAIN, ON A BASE OF TEXTILE MATERIAL, OF\nPAPER, OF PAPERBOARD OR OF OTHER MATERIALS, WHETHER OR NOT CUT TO SHAPE OR SEWN\nOR OTHERWISE MADE UP ON A BASE OF PAPER OR PAPERBOARD ONLY: OTHER",
+        "hsn_code": "68052090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NATURAL OR ARTIFICIAL ABRASIVE POWDER OR GRAIN, ON A BASE OF TEXTILE MATERIAL, OF\nPAPER, OF PAPERBOARD OR OF OTHER MATERIALS, WHETHER OR NOT CUT TO SHAPE OR SEWN\nOR OTHERWISE MADE UP ON A BASE OF OTHER MATERIALS",
+        "hsn_code": "68053000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NATURAL OR ARTIFICIAL ABRASIVE POWDER OR GRAIN, ON A BASE OF TEXTILE MATERIAL, OF PAPER, OF PAPERBOARD OR OF OTHER MATERIALS, WHETHER OR NOT CUT TO SHAPE OR SEWN OR OTHERWISE MADE UP",
+        "hsn_code": "6805",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MILLSTONES, GRINDSTONES, GRINDING WHEELS AND THE LIKE, WITHOUT FRAMEWORKS, FOR\nGRINDING, SHARPENING, POLISHING, TRUEING OR CUTTING, HAND SHARPENING OR POLISHING\nSTONES, AND PARTS THEREOF, OF NATURAL STONE, OF AGGLOMERATED NATURAL OR\nARTIFICIAL ABRASIVES, OR OF CERAMICS, WITH OR WITHOUT PARTS OF OTHER MATERIALS\nMILLSTONES AND GRINDSTONES FOR MILLING, GRINDING OR PULPING",
+        "hsn_code": "68041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MILLSTONES, GRINDSTONES, GRINDING WHEELS AND THE LIKE, WITHOUT FRAMEWORKS, FOR\nGRINDING, SHARPENING, POLISHING, TRUEING OR CUTTING, HAND SHARPENING OR POLISHING\nSTONES, AND PARTS THEREOF, OF NATURAL STONE, OF AGGLOMERATED NATURAL OR\nARTIFICIAL ABRASIVES, OR OF CERAMICS, WITH OR WITHOUT PARTS OF OTHER MATERIALS -\nOTHER MILLSTONES, GRINDSTONES, GRINDING WHEELS AND THE LIKE: OF AGGLOMERATED\nSYNTHETIC OR NATURAL DIAMOND: DIAMOND IMPREGNATED WHEELS",
+        "hsn_code": "68042110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MILLSTONES, GRINDSTONES, GRINDING WHEELS AND THE LIKE, WITHOUT FRAMEWORKS, FOR\nGRINDING, SHARPENING, POLISHING, TRUEING OR CUTTING, HAND SHARPENING OR POLISHING\nSTONES, AND PARTS THEREOF, OF NATURAL STONE, OF AGGLOMERATED NATURAL OR\nARTIFICIAL ABRASIVES, OR OF CERAMICS, WITH OR WITHOUT PARTS OF OTHER MATERIALS -\nOTHER MILLSTONES, GRINDSTONES, GRINDING WHEELS AND THE LIKE: OF AGGLOMERATED\nSYNTHETIC OR NATURAL DIAMOND: OTHER",
+        "hsn_code": "68042190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MILLSTONES, GRINDSTONES, GRINDING WHEELS AND THE LIKE, WITHOUT FRAMEWORKS, FOR\nGRINDING, SHARPENING, POLISHING, TRUEING OR CUTTING, HAND SHARPENING OR POLISHING\nSTONES, AND PARTS THEREOF, OF NATURAL STONE, OF AGGLOMERATED NATURAL OR\nARTIFICIAL ABRASIVES, OR OF CERAMICS, WITH OR WITHOUT PARTS OF OTHER MATERIALS OF\nOTHER AGGLOMERATED ABRASIVES OR OF CERAMICS: GRINDING WHEELS OF SYNTHETIC\nABRASIVES",
+        "hsn_code": "68042210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MILLSTONES, GRINDSTONES, GRINDING WHEELS AND THE LIKE, WITHOUT FRAMEWORKS, FOR\nGRINDING, SHARPENING, POLISHING, TRUEING OR CUTTING, HAND SHARPENING OR POLISHING\nSTONES, AND PARTS THEREOF, OF NATURAL STONE, OF AGGLOMERATED NATURAL OR\nARTIFICIAL ABRASIVES, OR OF CERAMICS, WITH OR WITHOUT PARTS OF OTHER MATERIALS -\nOTHER MILLSTONES, GRINDSTONES, GRINDING WHEELS AND THE LIKE: OF OTHER\nAGGLOMERATED ABRASIVES OR OF CERAMICS: GRINDING WHEELS OF OTHER MATERIALS",
+        "hsn_code": "68042220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MILLSTONES, GRINDSTONES, GRINDING WHEELS AND THE LIKE, WITHOUT FRAMEWORKS, FOR\nGRINDING, SHARPENING, POLISHING, TRUEING OR CUTTING, HAND SHARPENING OR POLISHING\nSTONES, AND PARTS THEREOF, OF NATURAL STONE, OF AGGLOMERATED NATURAL OR\nARTIFICIAL ABRASIVES, OR OF CERAMICS, WITH OR WITHOUT PARTS OF OTHER MATERIALS -\nOTHER MILLSTONES, GRINDSTONES, GRINDING WHEELS AND THE LIKE: OF OTHER\nAGGLOMERATED ABRASIVES OR OF CERAMICS: OTHER",
+        "hsn_code": "68042290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MILLSTONES, GRINDSTONES, GRINDING WHEELS AND THE LIKE, WITHOUT FRAMEWORKS, FOR\nGRINDING, SHARPENING, POLISHING, TRUEING OR CUTTING, HAND SHARPENING OR POLISHING\nSTONES, AND PARTS THEREOF, OF NATURAL STONE, OF AGGLOMERATED NATURAL OR\nARTIFICIAL ABRASIVES, OR OF CERAMICS, WITH OR WITHOUT PARTS OF OTHER MATERIALS -\nOTHER MILLSTONES, GRINDSTONES, GRINDING WHEELS AND THE LIKE: OF NATURAL STONE:\nGRINDING WHEELS MADE OF NATURAL STONE",
+        "hsn_code": "68042310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MILLSTONES, GRINDSTONES, GRINDING WHEELS AND THE LIKE, WITHOUT FRAMEWORKS, FOR\nGRINDING, SHARPENING, POLISHING, TRUEING OR CUTTING, HAND SHARPENING OR POLISHING\nSTONES, AND PARTS THEREOF, OF NATURAL STONE, OF AGGLOMERATED NATURAL OR\nARTIFICIAL ABRASIVES, OR OF CERAMICS, WITH OR WITHOUT PARTS OF OTHER MATERIALS -\nOTHER MILLSTONES, GRINDSTONES, GRINDING WHEELS AND THE LIKE: OF NATURAL STONE:\nOTHER",
+        "hsn_code": "68042390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MILLSTONES, GRINDSTONES, GRINDING WHEELS AND THE LIKE, WITHOUT FRAMEWORKS, FOR\nGRINDING, SHARPENING, POLISHING, TRUEING OR CUTTING, HAND SHARPENING OR POLISHING\nSTONES, AND PARTS THEREOF, OF NATURAL STONE, OF AGGLOMERATED NATURAL OR\nARTIFICIAL ABRASIVES, OR OF CERAMICS, WITH OR WITHOUT PARTS OF OTHER MATERIALS\nHAND SHARPENING OR POLISHING STONES: POLISHING STONES",
+        "hsn_code": "68043010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MILLSTONES, GRINDSTONES, GRINDING WHEELS AND THE LIKE, WITHOUT FRAMEWORKS, FOR\nGRINDING, SHARPENING, POLISHING, TRUEING OR CUTTING, HAND SHARPENING OR POLISHING\nSTONES, AND PARTS THEREOF, OF NATURAL STONE, OF AGGLOMERATED NATURAL OR\nARTIFICIAL ABRASIVES, OR OF CERAMICS, WITH OR WITHOUT PARTS OF OTHER MATERIALS\nHAND SHARPENING OR POLISHING STONES: SHARPENING STONES",
+        "hsn_code": "68043020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MILLSTONES, GRINDSTONES, GRINDING, WHEELS AND THE LIKE, WITHOUT FRAMEWORKS, FOR GRINDING, SHARPENING, POLISHING TRUEING OR CUTTING, HAND SHARPENING OR POLISHING STONES, AND PARTS THEREOF, OF NATURAL STONE, OF AGGLOMERATED NATURAL OR ARTIFICIAL ABRASIVES, OR OF CERAMICS, WITH OR WITHOUT PARTS OF OTHER MATERIALS",
+        "hsn_code": "6804",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED SLATE AND ARTICLES OF SLATE OR OF AGGLOMERATED SLATE",
+        "hsn_code": "68030000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Worked slate and articles of slate or of agglomerated slate",
+        "hsn_code": "6803",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MONUMENTAL OR BUILDING STONE (EXCEPT SLATE) AND ARTICLES THEREOF, OTHER\nTHAN GOODS OF HEADING 6801; MOSAIC CUBES AND THE LIKE, OF NATURAL STONE\n(INCLUDING SLATE), WHETHER OR NOT ON A BACKING; ARTIFICIALLY COLOURED GRANULES,\nCHIPPINGS AND POWDER, OF NATURAL STONE (INCLUDING SLATE) TILES, CUBES AND SIMILAR\nARTICLES, WHETHER OR NOT RECTANGULAR (INCLUDING SQUARE), THE LARGEST SURFACE AREA\nOF WHICH IS CAPABLE OF BEING ENCLOSED IN A SQUARE THE SIDE OF WHICH IS LESS THAN 7\nCM; ARTIFICIALLY COLOURED GRANULES,",
+        "hsn_code": "68021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MONUMENTAL OR BUILDING STONE (EXCEPT SLATE) AND ARTICLES THEREOF, OTHER\nTHAN GOODS OF HEADING 6801; MOSAIC CUBES AND THE LIKE, OF NATURAL STONE\n(INCLUDING SLATE), WHETHER OR NOT ON A BACKING; ARTIFICIALLY COLOURED GRANULES,\nCHIPPINGS AND POWDER, OF NATURAL STONE (INCLUDING SLATE) - OTHER MONUMENTAL OR\nBUILDING STONE AND ARTICLES THEREOF, SIMPLY CUT OR SAWN, WITH A FLAT OR EVEN\nSURFACE : MARBLE, TRAVERTINE AND ALABASTER: MARBLE BLOCKS OR TILES",
+        "hsn_code": "68022110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MONUMENTAL OR BUILDING STONE (EXCEPT SLATE) AND ARTICLES THEREOF, OTHER\nTHAN GOODS OF HEADING 6801; MOSAIC CUBES AND THE LIKE, OF NATURAL STONE\n(INCLUDING SLATE), WHETHER OR NOT ON A BACKING; ARTIFICIALLY COLOURED GRANULES,\nCHIPPINGS AND POWDER, OF NATURAL STONE (INCLUDING SLATE) - OTHER MONUMENTAL OR\nBUILDING STONE AND ARTICLES THEREOF, SIMPLY CUT OR SAWN, WITH A FLAT OR EVEN\nSURFACE : MARBLE, TRAVERTINE AND ALABASTER: MARBLE MONUMENTAL STONE",
+        "hsn_code": "68022120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MONUMENTAL OR BUILDING STONE (EXCEPT SLATE) AND ARTICLES THEREOF, OTHER\nTHAN GOODS OF HEADING 6801; MOSAIC CUBES AND THE LIKE, OF NATURAL STONE\n(INCLUDING SLATE), WHETHER OR NOT ON A BACKING; ARTIFICIALLY COLOURED GRANULES,\nCHIPPINGS AND POWDER, OF NATURAL STONE (INCLUDING SLATE) - OTHER MONUMENTAL OR\nBUILDING STONE AND ARTICLES THEREOF, SIMPLY CUT OR SAWN, WITH A FLAT OR EVEN\nSURFACE : MARBLE, TRAVERTINE AND ALABASTER: OTHER",
+        "hsn_code": "68022190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MONUMENTAL OR BUILDING STONE (EXCEPT SLATE) AND ARTICLES THEREOF, OTHER\nTHAN GOODS OF HEADING 6801; MOSAIC CUBES AND THE LIKE, OF NATURAL STONE\n(INCLUDING SLATE), WHETHER OR NOT ON A BACKING; ARTIFICIALLY COLOURED GRANULES,\nCHIPPINGS AND POWDER, OF NATURAL STONE (INCLUDING SLATE) - OTHER MONUMENTAL OR\nBUILDING STONE AND ARTICLES THEREOF, SIMPLY CUT OR SAWN, WITH A FLAT OR EVEN\nSURFACE : OTHER CALCAREOUS STONE",
+        "hsn_code": "68022200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MONUMENTAL OR BUILDING STONE (EXCEPT SLATE) AND ARTICLES THEREOF, OTHER\nTHAN GOODS OF HEADING 6801; MOSAIC CUBES AND THE LIKE, OF NATURAL STONE\n(INCLUDING SLATE), WHETHER OR NOT ON A BACKING; ARTIFICIALLY COLOURED GRANULES,\nCHIPPINGS AND POWDER, OF NATURAL STONE (INCLUDING SLATE) - OTHER MONUMENTAL OR\nBUILDING STONE AND ARTICLES THEREOF, SIMPLY CUT OR SAWN, WITH A FLAT OR EVEN\nSURFACE : GRANITE: GRANITE BLOCKS OR TILES",
+        "hsn_code": "68022310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MONUMENTAL OR BUILDING STONE (EXCEPT SLATE) AND ARTICLES THEREOF, OTHER\nTHAN GOODS OF HEADING 6801; MOSAIC CUBES AND THE LIKE, OF NATURAL STONE\n(INCLUDING SLATE), WHETHER OR NOT ON A BACKING; ARTIFICIALLY COLOURED GRANULES,\nCHIPPINGS AND POWDER, OF NATURAL STONE (INCLUDING SLATE) - OTHER MONUMENTAL OR\nBUILDING STONE AND ARTICLES THEREOF, SIMPLY CUT OR SAWN, WITH A FLAT OR EVEN\nSURFACE : GRANITE: OTHER",
+        "hsn_code": "68022390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MONUMENTAL OR BUILDING STONE (EXCEPT SLATE) AND ARTICLES THEREOF, OTHER\nTHAN GOODS OF HEADING 6801; MOSAIC CUBES AND THE LIKE, OF NATURAL STONE\n(INCLUDING SLATE), WHETHER OR NOT ON A BACKING; ARTIFICIALLY COLOURED GRANULES,\nCHIPPINGS AND POWDER, OF NATURAL STONE (INCLUDING SLATE) - OTHER MONUMENTAL OR\nBUILDING STONE AND ARTICLES THEREOF, SIMPLY CUT OR SAWN, WITH A FLAT OR EVEN\nSURFACE : OTHER STONE",
+        "hsn_code": "68022900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MONUMENTAL OR BUILDING STONE (EXCEPT SLATE) AND ARTICLES THEREOF, OTHER\nTHAN GOODS OF HEADING 6801; MOSAIC CUBES AND THE LIKE, OF NATURAL STONE\n(INCLUDING SLATE), WHETHER OR NOT ON A BACKING; ARTIFICIALLY COLOURED GRANULES,\nCHIPPINGS AND POWDER, OF NATURAL STONE (INCLUDING SLATE) - OTHER: MARBLE,\nTRAVERTINE AND ALABASTER",
+        "hsn_code": "68029100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MONUMENTAL OR BUILDING STONE (EXCEPT SLATE) AND ARTICLES THEREOF, OTHER\nTHAN GOODS OF HEADING 6801; MOSAIC CUBES AND THE LIKE, OF NATURAL STONE\n(INCLUDING SLATE), WHETHER OR NOT ON A BACKING; ARTIFICIALLY COLOURED GRANULES,\nCHIPPINGS AND POWDER, OF NATURAL STONE (INCLUDING SLATE) - OTHER: OTHER\nCALCAREOUS STONE",
+        "hsn_code": "68029200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MONUMENTAL OR BUILDING STONE (EXCEPT SLATE) AND ARTICLES THEREOF, OTHER\nTHAN GOODS OF HEADING 6801; MOSAIC CUBES AND THE LIKE, OF NATURAL STONE\n(INCLUDING SLATE), WHETHER OR NOT ON A BACKING; ARTIFICIALLY COLOURED GRANULES,\nCHIPPINGS AND POWDER, OF NATURAL STONE (INCLUDING SLATE) - OTHER: GRANITE",
+        "hsn_code": "68029300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MONUMENTAL OR BUILDING STONE (EXCEPT SLATE) AND ARTICLES THEREOF, OTHER\nTHAN GOODS OF HEADING 6801; MOSAIC CUBES AND THE LIKE, OF NATURAL STONE\n(INCLUDING SLATE), WHETHER OR NOT ON A BACKING; ARTIFICIALLY COLOURED GRANULES,\nCHIPPINGS AND POWDER, OF NATURAL STONE (INCLUDING SLATE) - OTHER: OTHER STONE",
+        "hsn_code": "68029900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WORKED MONUMENTAL OR BUILDING STONE (EXCEPT SLATE) AND ARTICLES THEREOF, OTHER THAN GOODS OF HEADING 6801; MOSAIC CUBES AND THE LIKE, OF NATURAL STONE (INCLUDING SLATE), WHETHER OR NOT ON A BACKING; ARTIFICIALLY COLOURED GRANULES, CHIPPINGS AND POWDER, OF NATURAL STONE (INCLUDING SLATE)",
+        "hsn_code": "6802",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SETTS, CURBSTONES AND FLAGSTONES, OF NATURAL STONE (EXCEPT SLATE)",
+        "hsn_code": "68010000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Sets, curbstones and flagstones, of natural stone (except slate)",
+        "hsn_code": "6801",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIGS, FALSE BEARDS, EYEBROWS AND EYELASHES, SWITCHES AND THE LIKE, OF HUMAN OR ANIMAL HAIR OR OF TEXTILE MATERIALS; ARTICLES OF HUMAN HAIR NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "6704",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIGS, FALSE BEARDS, EYEBROWS AND EYELASHES, SWITCHES AND THE LIKE, OF HUMAN OR\nANIMAL HAIR OR OF TEXTILE MATERIALS; ARTICLES OF HUMAN HAIR NOT ELSEWHERE SPECIFIED\nOR INCLUDED OF OTHER MATERIALS",
+        "hsn_code": "67049000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIGS, FALSE BEARDS, EYEBROWS AND EYELASHES, SWITCHES AND THE LIKE, OF HUMAN OR\nANIMAL HAIR OR OF TEXTILE MATERIALS; ARTICLES OF HUMAN HAIR NOT ELSEWHERE SPECIFIED\nOR INCLUDED OF HUMAN HAIR: OTHER",
+        "hsn_code": "67042090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIGS, FALSE BEARDS, EYEBROWS AND EYELASHES, SWITCHES AND THE LIKE, OF HUMAN OR\nANIMAL HAIR OR OF TEXTILE MATERIALS; ARTICLES OF HUMAN HAIR NOT ELSEWHERE SPECIFIED\nOR INCLUDED OF HUMAN HAIR: HAIR NETS",
+        "hsn_code": "67042020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIGS, FALSE BEARDS, EYEBROWS AND EYELASHES, SWITCHES AND THE LIKE, OF HUMAN OR\nANIMAL HAIR OR OF TEXTILE MATERIALS; ARTICLES OF HUMAN HAIR NOT ELSEWHERE SPECIFIED\nOR INCLUDED OF HUMAN HAIR: WIGS",
+        "hsn_code": "67042010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIGS, FALSE BEARDS, EYEBROWS AND EYELASHES, SWITCHES AND THE LIKE, OF HUMAN OR\nANIMAL HAIR OR OF TEXTILE MATERIALS; ARTICLES OF HUMAN HAIR NOT ELSEWHERE SPECIFIED\nOR INCLUDED - OF SYNTHETIC TEXTILE MATERIALS: OTHER: OTHER",
+        "hsn_code": "67041990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIGS, FALSE BEARDS, EYEBROWS AND EYELASHES, SWITCHES AND THE LIKE, OF HUMAN OR\nANIMAL HAIR OR OF TEXTILE MATERIALS; ARTICLES OF HUMAN HAIR NOT ELSEWHERE SPECIFIED\nOR INCLUDED - OF SYNTHETIC TEXTILE MATERIALS: OTHER: HAIR NETS",
+        "hsn_code": "67041910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WIGS, FALSE BEARDS, EYEBROWS AND EYELASHES,SWITCHES AND THE LIKE, OF HUMAN OR\nANIMAL HAIR OR OF TEXTILE MATERIALS; ARTICLES OF HUMAN HAIR NOT ELSEWHERE SPECIFIED\nOR INCLUDED - OF SYNTHETIC TEXTILE MATERIALS: COMPLETE WIGS",
+        "hsn_code": "67041100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FLOWERS, FOLIAGE AND FRUIT AND PARTS THEREOF; ARTICLES MADE OF ARTIFICIAL FLOWERS, FOLIAGE OR FRUIT",
+        "hsn_code": "6702",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FLOWERS, FOLIAGE AND FRUIT AND PARTS THEREOF; ARTICLES MADE OF ARTIFICIAL\nFLOWERS, FOLIAGE OR FRUIT OF OTHER MATERIALS: OTHER",
+        "hsn_code": "67029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FLOWERS, FOLIAGE AND FRUIT AND PARTS THEREOF; ARTICLES MADE OF ARTIFICIAL\nFLOWERS, FOLIAGE OR FRUIT OF OTHER MATERIALS: OF JUTE",
+        "hsn_code": "67029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FLOWERS, FOLIAGE AND FRUIT AND PARTS THEREOF; ARTICLES MADE OF ARTIFICIAL\nFLOWERS, FOLIAGE OR FRUIT OF PLASTICS: OTHER",
+        "hsn_code": "67021090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FLOWERS, FOLIAGE AND FRUIT AND PARTS THEREOF; ARTICLES MADE OF ARTIFICIAL\nFLOWERS, FOLIAGE OR FRUIT OF PLASTICS: DECORATIVE PLANTS",
+        "hsn_code": "67021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SKINS AND OTHER PARTS OF BIRDS WITH THEIR FEATHERS OR DOWN, FEATHERS, PARTS OF FEATHERS, DOWN AND ARTICLES THEREOF (OTHER THAN GOODS OF HEADING 0505 AND WORKED QUILLS AND SCAPES)",
+        "hsn_code": "6701",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SKINS AND OTHER PARTS OF BIRDS WITH THEIR FEATHERS OR DOWN, FEATHERS, PARTS OF\nFEATHERS, DOWN AND ARTICLES THEREOF (OTHER THAN GOODS OF HEADING 0505 AND\nWORKED QUILLS AND SCAPES) SKINS AND OTHER PARTS OF BIRDS WITH THEIR FEATHERS OR\nDOWN, FEATHERS, PARTS OF FEATHERS, DOWN AND ARTICLES THEREOF (OTHER THAN GOODS\nOF HEADING 0505 AND WORKED QUILLS AND SCAPES): OTHER",
+        "hsn_code": "67010090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SKINS AND OTHER PARTS OF BIRDS WITH THEIR FEATHERS OR DOWN, FEATHERS, PARTS OF\nFEATHERS, DOWN AND ARTICLES THEREOF (OTHER THAN GOODS OF HEADING 0505 AND\nWORKED QUILLS AND SCAPES) SKINS AND OTHER PARTS OF BIRDS WITH THEIR FEATHERS OR\nDOWN, FEATHERS, PARTS OF FEATHERS, DOWN AND ARTICLES THEREOF (OTHER THAN GOODS\nOF HEADING 0505 AND WORKED QUILLS AND SCAPES): FEATHER DUSTERS",
+        "hsn_code": "67010010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PARTS, TRIMMINGS AND ACCESSORIES OF ARTICLES OF HEADING 6601 TO 6602",
+        "hsn_code": "6603",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PARTS, TRIMMINGS AND ACCESSORIES OF ARTICLES OF HEADING 6601 TO 6602- OTHER:OTHER",
+        "hsn_code": "66039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PARTS, TRIMMINGS AND ACCESSORIES OF ARTICLES OF HEADING 6601 TO 6602-\nOTHER:UMBRELLA RIBS",
+        "hsn_code": "66039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PARTS, TRIMMINGS AND ACCESSORIES OF ARTICLES OF HEADING 6601 TO 6602 - UMBRELLA\nFRAMES, INCLUDING FRAMES MOUNTED ON SHAFTS (STICKS)",
+        "hsn_code": "66032000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PARTS, TRIMMINGS AND ACCESSORIES OF ARTICLES OF HEADING 6601 TO 6602- HANDLES AND\nKNOBS:OTHER",
+        "hsn_code": "66031090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PARTS, TRIMMINGS AND ACCESSORIES OF ARTICLES OF HEADING 6601 TO 6602- HANDLES AND\nKNOBS:UMBRELLA HANDLES WITH OR WITHOUT STEMS, PLASTIC",
+        "hsn_code": "66031010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Walking-sticks, seat-sticks, whips, riding-crops and the like",
+        "hsn_code": "6602",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UMBRELLAS AND SUN UMBRELLAS (INCLUDING WALKING-STICK UMBRELLAS, GARDEN UMBRELLAS AND SIMILAR UMBRELLAS)",
+        "hsn_code": "6601",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UMBRELLAS AND SUN UMBRELLAS (INCLUDING WALKINGSTICK UMBRELLAS, GARDEN\nUMBRELLAS AND SIMILAR UMBRELLAS)- OTHER: OTHER",
+        "hsn_code": "66019900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UMBRELLAS AND SUN UMBRELLAS (INCLUDING WALKINGSTICK UMBRELLAS, GARDEN\nUMBRELLAS AND SIMILAR UMBRELLAS)- OTHER: HAVING A TELESCOPIC SHAFT",
+        "hsn_code": "66019100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UMBRELLAS AND SUN UMBRELLAS (INCLUDING WALKINGSTICK UMBRELLAS, GARDEN\nUMBRELLAS AND SIMILAR UMBRELLAS) - GARDEN OR SIMILAR UMBRELLAS",
+        "hsn_code": "66011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Head-bands, linings, covers, hat foundations, hat frames, peaks and chinstraps, for headgear",
+        "hsn_code": "6507",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HEAD-BANDS, LININGS, COVERS, HAT FOUNDATIONS, HAT FRAMES, PEAKS AND CHINSTRAPS,\nFOR HEADGEAR",
+        "hsn_code": "65070000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER HEADGEAR, WHETHER OR NOT LINED OR TRIMMED",
+        "hsn_code": "6506",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER HEADGEAR, WHETHER OR NOT LINED OR TRIMMED OTHER:OF OTHER MATERIALS",
+        "hsn_code": "65069900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER HEADGEAR, WHETHER OR NOT LINED OR TRIMMED OTHER:OF FURSKIN",
+        "hsn_code": "65069200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER HEADGEAR, WHETHER OR NOT LINED OR TRIMMED OTHER:OF RUBBER OR OF PLASTICS",
+        "hsn_code": "65069100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER HEADGEAR, WHETHER OR NOT LINED OR TRIMMED SAFETY HEADGEAR: OTHER",
+        "hsn_code": "65061090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER HEADGEAR, WHETHER OR NOT LINED OR TRIMMED SAFETY HEADGEAR: SPEED GLASS\nWELDING HELMETS OR OTHER HELMETS MEANT FOR INDUSTRIAL USE",
+        "hsn_code": "65061010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAT-SHAPES, PLAITED OR MADE BY ASSEMBLING STRIPS OF ANY MATERIAL, NEITHER BLOCKED\nTO SHAPE, NOR WITH MADE BRIMS, NOR LINED, NOR TRIMMED HAT-SHAPES, PLAITED OR\nMADE BY ASSEMBLING STRIPS OF ANY MATERIAL, NEITHER BLOCKED TO SHAPE, NOR WITH\nMADE BRIMS, NOR LINED, NOR TRIMMED: OTHER",
+        "hsn_code": "65020090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAT-SHAPES, PLAITED OR MADE BY ASSEMBLING STRIPS OF ANY MATERIAL, NEITHER BLOCKED\nTO SHAPE, NOR WITH MADE BRIMS, NOR LINED, NOR TRIMMED HAT-SHAPES, PLAITED OR\nMADE BY ASSEMBLING STRIPS OF ANY MATERIAL, NEITHER BLOCKED TO SHAPE, NOR WITH\nMADE BRIMS, NOR LINED, NOR TRIMMED: OF WOOL",
+        "hsn_code": "65020020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HAT-SHAPES, PLAITED OR MADE BY ASSEMBLING STRIPS OF ANY MATERIAL, NEITHER BLOCKED\nTO SHAPE, NOR WITH MADE BRIMS, NOR LINED, NOR TRIMMED HAT-SHAPES, PLAITED OR\nMADE BY ASSEMBLING STRIPS OF ANY MATERIAL, NEITHER BLOCKED TO SHAPE, NOR WITH\nMADE BRIMS, NOR LINED, NOR TRIMMED: OF COTTON",
+        "hsn_code": "65020010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF FOOTWEAR (INCLUDING UPPERS WHETHER OR NOT ATTACHED TO SOLES OTHER\nTHAN OUTER SOLES); REMOVABLE IN-SOLES, HEEL CUSHIONS AND SIMILAR ARTICLES; GAITERS,\nLEGGINGS AND SIMILAR ARTICLES, AND PARTS THEREOF UPPERS AND PARTS THEREOF, OTHER\nTHAN STIFFENERS: EMBROIDERED UPPERS OF TEXTILE MATERIALS",
+        "hsn_code": "64061010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF FOOTWEAR (INCLUDING UPPERS WHETHER OR NOT ATTACHED TO SOLES OTHER\nTHAN OUTER SOLES); REMOVABLE IN-SOLES, HEEL CUSHIONS AND SIMILAR ARTICLES; GAITERS,\nLEGGINGS AND SIMILAR ARTICLES, AND PARTS THEREOF UPPERS AND PARTS THEREOF, OTHER\nTHAN STIFFENERS:LEATHER UPPERS (PREPARED)",
+        "hsn_code": "64061020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF FOOTWEAR (INCLUDING UPPERS WHETHER OR NOT ATTACHED TO SOLES OTHER\nTHAN OUTER SOLES); REMOVABLE IN-SOLES, HEEL CUSHIONS AND SIMILAR ARTICLES; GAITERS,\nLEGGINGS AND SIMILAR ARTICLES, AND PARTS THEREOF UPPERS AND PARTS THEREOF, OTHER\nTHAN STIFFENERS:GOAT LINING",
+        "hsn_code": "64061030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF FOOTWEAR (INCLUDING UPPERS WHETHER OR NOT ATTACHED TO SOLES OTHER\nTHAN OUTER SOLES); REMOVABLE IN-SOLES, HEEL CUSHIONS AND SIMILAR ARTICLES; GAITERS,\nLEGGINGS AND SIMILAR ARTICLES, AND PARTS THEREOF UPPERS AND PARTS THEREOF, OTHER\nTHAN STIFFENERS: SHEEP LINING",
+        "hsn_code": "64061040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF FOOTWEAR (INCLUDING UPPERS WHETHER OR NOT ATTACHED TO SOLES OTHER\nTHAN OUTER SOLES); REMOVABLE IN-SOLES, HEEL CUSHIONS AND SIMILAR ARTICLES; GAITERS,\nLEGGINGS AND SIMILAR ARTICLES, AND PARTS THEREOF UPPERS AND PARTS THEREOF, OTHER\nTHAN STIFFENERS:OTHER",
+        "hsn_code": "64061090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF FOOTWEAR (INCLUDING UPPERS WHETHER OR NOT ATTACHED TO SOLES OTHER\nTHAN OUTER SOLES); REMOVABLE IN-SOLES, HEEL CUSHIONS AND SIMILAR ARTICLES; GAITERS,\nLEGGINGS AND SIMILAR ARTICLES, AND PARTS THEREOF OUTER SOLES AND HEELS, OF RUBBER\nOR PLASTICS",
+        "hsn_code": "64062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF FOOTWEAR (INCLUDING UPPERS WHETHER OR NOT ATTACHED TO SOLES OTHER\nTHAN OUTER SOLES); REMOVABLE IN-SOLES, HEEL CUSHIONS AND SIMILAR ARTICLES; GAITERS,\nLEGGINGS AND SIMILAR ARTICLES, AND PARTS THEREOF- OTHER---OF WOOD",
+        "hsn_code": "64069010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF FOOTWEAR (INCLUDING UPPERS WHETHER OR NOT ATTACHED TO SOLES OTHER\nTHAN OUTER SOLES); REMOVABLE IN-SOLES, HEEL CUSHIONS AND SIMILAR ARTICLES; GAITERS,\nLEGGINGS AND SIMILAR ARTICLES, AND PARTS THEREOF- OTHER---LEATHER PARTS OTHER THAN\nSOLES AND PREPARED UPPERS",
+        "hsn_code": "64069020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF FOOTWEAR (INCLUDING UPPERS WHETHER OR NOT ATTACHED TO SOLES OTHER\nTHAN OUTER SOLES); REMOVABLE IN-SOLES, HEEL CUSHIONS AND SIMILAR ARTICLES; GAITERS,\nLEGGINGS AND SIMILAR ARTICLES, AND PARTS THEREOF- OTHER---LEATHER SOLES",
+        "hsn_code": "64069030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF FOOTWEAR (INCLUDING UPPERS WHETHER OR NOT ATTACHED TO SOLES OTHER\nTHAN OUTER SOLES); REMOVABLE IN-SOLES, HEEL CUSHIONS AND SIMILAR ARTICLES; GAITERS,\nLEGGINGS AND SIMILAR ARTICLES, AND PARTS THEREOF- OTHER---GAITERS, LEGGINGS AND\nSIMILAR ARTICLES",
+        "hsn_code": "64069040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF FOOTWEAR (INCLUDING UPPERS WHETHER OR NOT ATTACHED TO SOLES OTHER\nTHAN OUTER SOLES); REMOVABLE IN-SOLES, HEEL CUSHIONS AND SIMILAR ARTICLES; GAITERS,\nLEGGINGS AND SIMILAR ARTICLES, AND PARTS THEREOF- OTHER---PARTS OF GAITERS, LEGGINGS\nAND SIMILAR ARTICLES",
+        "hsn_code": "64069050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF FOOTWEAR (INCLUDING UPPERS WHETHER OR NOT ATTACHED TO SOLES OTHER\nTHAN OUTER SOLES); REMOVABLE IN-SOLES, HEEL CUSHIONS AND SIMILAR ARTICLES; GAITERS,\nLEGGINGS AND SIMILAR ARTICLES, AND PARTS THEREOF- OTHER---OTHER",
+        "hsn_code": "64069090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FOOTWEAR WITH UPPERS OF LEATHER OR COMPOSITION LEATHER",
+        "hsn_code": "64051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FOOTWEAR WITH UPPERS OF TEXTILE MATERIALS",
+        "hsn_code": "64052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FOOTWEAR OTHER",
+        "hsn_code": "64059000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF TEXTILE MATERIALS FOOTWEAR WITH OUTER SOLES OF RUBBER OR PLASTICS :\nSPORTS FOOTWEAR; TENNIS SHOES, BASKETBALL SHOES, GYM SHOES, TRAINING SHOES AND\nTHE LIKE: OF RUBBER SOLE WITH CANVAS UPPER",
+        "hsn_code": "64041110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF TEXTILE MATERIALS FOOTWEAR WITH OUTER SOLES OF RUBBER OR PLASTICS :\nSPORTS FOOTWEAR; TENNIS SHOES, BASKETBALL SHOES, GYM SHOES, TRAINING SHOES AND\nTHE LIKE: OF RUBBER SOLE WITH LEATHER CLOTH UPPERS",
+        "hsn_code": "64041120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF TEXTILE MATERIALS FOOTWEAR WITH OUTER SOLES OF RUBBER OR PLASTICS :\nSPORTS FOOTWEAR; TENNIS SHOES, BASKETBALL SHOES, GYM SHOES, TRAINING SHOES AND\nTHE LIKE: OTHER",
+        "hsn_code": "64041190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF TEXTILE MATERIALS FOOTWEAR WITH OUTER SOLES OF RUBBER OR PLASTICS :\nOTHER: OF RUBBER SOLE WITH CANVAS UPPER",
+        "hsn_code": "64041910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF TEXTILE MATERIALS FOOTWEAR WITH OUTER SOLES OF RUBBER OR PLASTICS :\nOTHER: OF RUBBER SOLE WITH LEATHER CLOTH UPPERS",
+        "hsn_code": "64041920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF TEXTILE MATERIALS FOOTWEAR WITH OUTER SOLES OF RUBBER OR PLASTICS :\nOTHER: OTHER",
+        "hsn_code": "64041990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF TEXTILE MATERIALS FOOTWEAR WITH OUTER SOLES OF LEATHER OR\nCOMPOSITION LEATHER",
+        "hsn_code": "64042000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER SPORTS FOOTWEAR : SKI-BOOTS, CROSS-COUNTRY SKI FOOTWEAR\nAND SNOWBOARD BOOTS",
+        "hsn_code": "64031200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER SPORTS FOOTWEAR : OTHER: WITH OUTER SOLES OF LEATHER",
+        "hsn_code": "64031910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER SPORTS FOOTWEAR : OTHER: WITH OUTER SOLES OF RUBBER",
+        "hsn_code": "64031920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER SPORTS FOOTWEAR :OTHER: OTHER",
+        "hsn_code": "64031990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER FOOTWEAR WITH OUTER SOLES OF LEATHER, AND UPPERS WHICH\nCONSIST OF LEATHER STRAPS ACROSS THE INSTEP AND AROUND THE BIG TOE: ALL LEATHER,\nCLOSED TOE: FOR MEN",
+        "hsn_code": "64032011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER FOOTWEAR WITH OUTER SOLES OF LEATHER, AND UPPERS WHICH\nCONSIST OF LEATHER STRAPS ACROSS THE INSTEP AND AROUND THE BIG TOE: ALL LEATHER,\nCLOSED TOE: FOR WOMEN",
+        "hsn_code": "64032012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER FOOTWEAR WITH OUTER SOLES OF LEATHER, AND UPPERS WHICH\nCONSIST OF LEATHER STRAPS ACROSS THE INSTEP AND AROUND THE BIG TOE: ALL LEATHER,\nCLOSED TOE: FOR CHILDREN",
+        "hsn_code": "64032013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER FOOTWEAR WITH OUTER SOLES OF LEATHER, AND UPPERS WHICH\nCONSIST OF LEATHER STRAPS ACROSS THE INSTEP AND AROUND THE BIG TOE: ALL LEATHER,\nCLOSED TOE: OTHER",
+        "hsn_code": "64032019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER FOOTWEAR WITH OUTER SOLES OF LEATHER, AND UPPERS WHICH\nCONSIST OF LEATHER STRAPS ACROSS THE INSTEP AND AROUND THE BIG TOE: ALL LEATHER,\nOPEN TOE: FOR MEN",
+        "hsn_code": "64032021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER FOOTWEAR WITH OUTER SOLES OF LEATHER, AND UPPERS WHICH\nCONSIST OF LEATHER STRAPS ACROSS THE INSTEP AND AROUND THE BIG TOE: ALL LEATHER,\nOPEN TOE:FOR WOMEN",
+        "hsn_code": "64032022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER FOOTWEAR WITH OUTER SOLES OF LEATHER, AND UPPERS WHICH\nCONSIST OF LEATHER STRAPS ACROSS THE INSTEP AND AROUND THE BIG TOE: ALL LEATHER,\nOPEN TOE:FOR CHILDREN",
+        "hsn_code": "64032023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER FOOTWEAR WITH OUTER SOLES OF LEATHER, AND UPPERS WHICH\nCONSIST OF LEATHER STRAPS ACROSS THE INSTEP AND AROUND THE BIG TOE: ALL LEATHER,\nOPEN TOE:OTHERS",
+        "hsn_code": "64032029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER FOOTWEAR WITH OUTER SOLES OF LEATHER, AND UPPERS WHICH\nCONSIST OF LEATHER STRAPS ACROSS THE INSTEP AND AROUND THE BIG TOE: OF LEATHER\nSOLES WITH EMBROIDERED UPPERS",
+        "hsn_code": "64032030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER FOOTWEAR WITH OUTER SOLES OF LEATHER, AND UPPERS WHICH\nCONSIST OF LEATHER STRAPS ACROSS THE INSTEP AND AROUND THE BIG TOE: KOLAPURI\nCHAPPALS AND SIMILAR FOOTWEAR",
+        "hsn_code": "64032040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER FOOTWEAR WITH OUTER SOLES OF LEATHER, AND UPPERS WHICH\nCONSIST OF LEATHER STRAPS ACROSS THE INSTEP AND AROUND THE BIG TOE: OTHER",
+        "hsn_code": "64032090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER FOOTWEAR MADE ON A BASE OR PLATFORM OF WOOD, NOT HAVING\nAN INNER SOLE OR A PROTECTIVE METAL TOE-CAP",
+        "hsn_code": "64033000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER OTHER FOOTWEAR, INCORPORATING A PROTECTIVE METAL TOE-CAP",
+        "hsn_code": "64034000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER OTHER FOOTWEAR, WITH OUTER SOLES OF LEATHER : COVERING THE\nANKLE: ALL LEATHER SHOES: FOR MEN",
+        "hsn_code": "64035111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER OTHER FOOTWEAR, WITH OUTER SOLES OF LEATHER : COVERING THE\nANKLE: ALL LEATHER SHOES: FOR WOMEN",
+        "hsn_code": "64035112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER OTHER FOOTWEAR, WITH OUTER SOLES OF LEATHER : COVERING THE\nANKLE: ALL LEATHER SHOES: FOR CHILDREN",
+        "hsn_code": "64035113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER OTHER FOOTWEAR, WITH OUTER SOLES OF LEATHER : COVERING THE\nANKLE: ALL LEATHER SHOES: OTHER",
+        "hsn_code": "64035119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER OTHER FOOTWEAR, WITH OUTER SOLES OF LEATHER : COVERING THE\nANKLE: OTHER",
+        "hsn_code": "64035190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER OTHER FOOTWEAR, WITH OUTER SOLES OF LEATHER : OTHER: FOR\nMEN",
+        "hsn_code": "64035910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER OTHER FOOTWEAR, WITH OUTER SOLES OF LEATHER : OTHER: FOR\nWOMEN",
+        "hsn_code": "64035920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER OTHER FOOTWEAR, WITH OUTER SOLES OF LEATHER : OTHER: FOR\nCHILDREN",
+        "hsn_code": "64035930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER OTHER FOOTWEAR, WITH OUTER SOLES OF LEATHER : OTHER: OTHER",
+        "hsn_code": "64035990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER OTHER FOOTWEAR: COVERING THE ANKLE: LEATHER BOOTS AND\nOTHER FOOTWEAR WITH RUBBER SOLE",
+        "hsn_code": "64039110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER OTHER FOOTWEAR: COVERING THE ANKLE: LEATHER FOOTWEAR\nWITH PLASTIC AND SYNTHETIC SOLE",
+        "hsn_code": "64039120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER OTHER FOOTWEAR: COVERING THE ANKLE: OTHER",
+        "hsn_code": "64039190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER OTHER FOOTWEAR: OTHER: LEATHER SANDALS WITH RUBBER SOLE",
+        "hsn_code": "64039910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER OTHER FOOTWEAR: OTHER: LEATHER SANDALS WITH PLASTIC OR\nSYNTHETIC SOLE",
+        "hsn_code": "64039920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FOOTWEAR WITH OUTER SOLES OF RUBBER, PLASTICS, LEATHER OR COMPOSITION LEATHER\nAND UPPERS OF LEATHER OTHER FOOTWEAR: OTHER: OTHER",
+        "hsn_code": "64039990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR PLASTICS SPORTS\nFOOTWEAR : SKI-BOOTS, CROSS-COUNTRY SKI FOOTWEAR AND SNOWBOARD BOOTS: OF\nRUBBER",
+        "hsn_code": "64021210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR PLASTICS SPORTS\nFOOTWEAR : SKI-BOOTS, CROSS-COUNTRY SKI FOOTWEAR AND SNOWBOARD BOOTS: OTHER",
+        "hsn_code": "64021290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR PLASTICS SPORTS\nFOOTWEAR : OTHER : OF RUBBER",
+        "hsn_code": "64021910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR PLASTICS SPORTS\nFOOTWEAR : OTHER : OTHER",
+        "hsn_code": "64021990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR PLASTICS FOOTWEAR WITH\nUPPER STRAPS OR THONGS ASSEMBLED TO THE SOLE BY MEANS OF PLUGS: OF RUBBER",
+        "hsn_code": "64022010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR PLASTICS FOOTWEAR WITH\nUPPER STRAPS OR THONGS ASSEMBLED TO THE SOLE BY MEANS OF PLUGS: OTHER",
+        "hsn_code": "64022090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR PLASTICS OTHER\nFOOTWEAR, INCORPORATING A PROTECTIVE METAL TOE-CAP: OF RUBBER",
+        "hsn_code": "64023010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR PLASTICS OTHER\nFOOTWEAR, INCORPORATING A PROTECTIVE METAL TOE-CAP: OTHER",
+        "hsn_code": "64023090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR PLASTICS OTHER\nFOOTWEAR :COVERING THE ANKLE:OF RUBBER",
+        "hsn_code": "64029110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR PLASTICS OTHER\nFOOTWEAR :COVERING THE ANKLE:OTHER",
+        "hsn_code": "64029190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR PLASTICS OTHER\nFOOTWEAR :OTHER: OF RUBBER",
+        "hsn_code": "64029910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR PLASTICS OTHER\nFOOTWEAR :OTHER: OTHER",
+        "hsn_code": "64029990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATERPROOF FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR OF PLASTICS, THE\nUPPERS OF WHICH ARE NEITHER FIXED TO THE SOLE NOR ASSEMBLED BY STITCHING, RIVETING,\nNAILING, SCREWING, PLUGGING OR SIMILAR PROCESSES FOOTWEAR INCORPORATING A\nPROTECTIVE METAL TOE-CAP: OF RUBBER",
+        "hsn_code": "64011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATERPROOF FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR OF PLASTICS, THE\nUPPERS OF WHICH ARE NEITHER FIXED TO THE SOLE NOR ASSEMBLED BY STITCHING, RIVETING,\nNAILING, SCREWING, PLUGGING OR SIMILAR PROCESSES FOOTWEAR INCORPORATING A\nPROTECTIVE METAL TOE-CAP: OTHER",
+        "hsn_code": "64011090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATERPROOF FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR OF PLASTICS, THE\nUPPERS OF WHICH ARE NEITHER FIXED TO THE SOLE NOR ASSEMBLED BY STITCHING, RIVETING,\nNAILING, SCREWING, PLUGGING OR SIMILAR PROCESSES OTHER FOOTWEAR : COVERING THE\nKNEE: OF RUBBER",
+        "hsn_code": "64019110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATERPROOF FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR OF PLASTICS, THE\nUPPERS OF WHICH ARE NEITHER FIXED TO THE SOLE NOR ASSEMBLED BY STITCHING, RIVETING,\nNAILING, SCREWING, PLUGGING OR SIMILAR PROCESSES OTHER FOOTWEAR : COVERING THE\nKNEE: OTHER",
+        "hsn_code": "64019190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATERPROOF FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR OF PLASTICS, THE\nUPPERS OF WHICH ARE NEITHER FIXED TO THE SOLE NOR ASSEMBLED BY STITCHING, RIVETING,\nNAILING, SCREWING, PLUGGING OR SIMILAR PROCESSES OTHER FOOTWEAR : COVERING THE\nANKLE BUT NOT COVERING THE KNEE: OF RUBBER",
+        "hsn_code": "64019210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATERPROOF FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR OF PLASTICS, THE\nUPPERS OF WHICH ARE NEITHER FIXED TO THE SOLE NOR ASSEMBLED BY STITCHING, RIVETING,\nNAILING, SCREWING, PLUGGING OR SIMILAR PROCESSES OTHER FOOTWEAR : COVERING THE\nANKLE BUT NOT COVERING THE KNEE: OTHER",
+        "hsn_code": "64019290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATERPROOF FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR OF PLASTICS, THE\nUPPERS OF WHICH ARE NEITHER FIXED TO THE SOLE NOR ASSEMBLED BY STITCHING, RIVETING,\nNAILING, SCREWING, PLUGGING OR SIMILAR PROCESSES OTHER FOOTWEAR : OTHER: OF\nRUBBER",
+        "hsn_code": "64019910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATERPROOF FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR OF PLASTICS, THE\nUPPERS OF WHICH ARE NEITHER FIXED TO THE SOLE NOR ASSEMBLED BY STITCHING, RIVETING,\nNAILING, SCREWING, PLUGGING OR SIMILAR PROCESSES OTHER FOOTWEAR : OTHER: OTHER",
+        "hsn_code": "64019990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATERPROOF FOOTWEAR WITH OUTER SOLES AND UPPERS OF RUBBER OR OF PLASTICS, THE UPPERS OF WHICH ARE NEITHER FIXED TO THE SOLE NOR ASSEMBLED BY STITCHING, RIVETING, NAILING, SCREWING, PLUGGING OR SIMILAR PROCESSES",
+        "hsn_code": "6401",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE PRODUCTS AND ARTICLES, FOR TECHNICAL USES, SPECIFIED IN NOTE 7 TO THIS CHAPTER",
+        "hsn_code": "5911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE PRODUCTS AND ARTICLES, FOR TECHNICAL USES, SPECIFIED IN NOTE 7 TO THIS CHAPTER\n- OTHER : OTHER",
+        "hsn_code": "59119090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE PRODUCTS AND ARTICLES, FOR TECHNICAL USES, SPECIFIED IN NOTE 7 TO THIS CHAPTER\n- OTHER : GASKETS, WASHERS, POLISHING DISCS AND OTHER MACHINERY PARTS OF TEXTILE\nARTICLES",
+        "hsn_code": "59119020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE PRODUCTS AND ARTICLES, FOR TECHNICAL USES, SPECIFIED IN NOTE 7 TO THIS CHAPTER\n- OTHER : PAPER MAKERS FELT, WOVEN",
+        "hsn_code": "59119010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE PRODUCTS AND ARTICLES, FOR TECHNICAL USES, SPECIFIED IN NOTE 7 TO THIS CHAPTER\n- STRAINING CLOTH OF A KIND USED IN OIL PRESSES OR THE LIKE, INCLUDING THAT OF HUMAN\nHAIR",
+        "hsn_code": "59114000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE PRODUCTS AND ARTICLES, FOR TECHNICAL USES, SPECIFIED IN NOTE 7 TO THIS CHAPTER\n- TEXTILE FABRICS AND FELTS, ENDLESS OR FITTED WITH LINKING DEVICES, OF A KIND USED IN\nPAPERMAKING OR SIMILAR MACHINES (FOR EXAMPLE, FOR PULP OR ASBESTOS - CEMENT) : -\nWEIGHING 650 G/ M2OR MORE : OTHER",
+        "hsn_code": "59113290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE PRODUCTS AND ARTICLES, FOR TECHNICAL USES, SPECIFIED IN NOTE 7 TO THIS CHAPTER\n- TEXTILE FABRICS AND FELTS, ENDLESS OR FITTED WITH LINKING DEVICES, OF A KIND USED IN\nPAPERMAKING OR SIMILAR MACHINES (FOR EXAMPLE, FOR PULP OR ASBESTOS - CEMENT) : -\nWEIGHING 650 G/ M2OR MORE : TEXTILE FABRICS OF METALISED YARN OF A KIND COMMONLY\nUSED IN PAPER MAKING OR OTHER MACHINERY",
+        "hsn_code": "59113250",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE PRODUCTS AND ARTICLES, FOR TECHNICAL USES, SPECIFIED IN NOTE 7 TO THIS CHAPTER\n- TEXTILE FABRICS AND FELTS, ENDLESS OR FITTED WITH LINKING DEVICES, OF A KIND USED IN\nPAPERMAKING OR SIMILAR MACHINES (FOR EXAMPLE, FOR PULP OR ASBESTOS - CEMENT) : -\nWEIGHING 650 G/ M2OR MORE : JUTE FABRICS AND ARTICLES USED IN MACHINERY OR PLAN",
+        "hsn_code": "59113240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE PRODUCTS AND ARTICLES, FOR TECHNICAL USES, SPECIFIED IN NOTE 7 TO THIS CHAPTER\n- TEXTILE FABRICS AND FELTS, ENDLESS OR FITTED WITH LINKING DEVICES, OF A KIND USED IN\nPAPERMAKING OR SIMILAR MACHINES (FOR EXAMPLE, FOR PULP OR ASBESTOS - CEMENT) : -\nWEIGHING 650 G/ M2OR MORE : COTTON FABRICS AND ARTICLES USED IN MACHINERY AND\nPLANT",
+        "hsn_code": "59113230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE PRODUCTS AND ARTICLES, FOR TECHNICAL USES, SPECIFIED IN NOTE 7 TO THIS CHAPTER\n- TEXTILE FABRICS AND FELTS, ENDLESS OR FITTED WITH LINKING DEVICES, OF A KIND USED IN\nPAPERMAKING OR SIMILAR MACHINES (FOR EXAMPLE, FOR PULP OR ASBESTOS - CEMENT) : -\nWEIGHING 650 G/ M2OR MORE : WOVEN TEXTILES FELT, WHETHER OR NOT IMPREGNATED OR\nCOATED, OF A KIND COMMONLY USED IN OTHER MACHINES",
+        "hsn_code": "59113220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE PRODUCTS AND ARTICLES, FOR TECHNICAL USES, SPECIFIED IN NOTE 7 TO THIS CHAPTER\n- TEXTILE FABRICS AND FELTS, ENDLESS OR FITTED WITH LINKING DEVICES, OF A KIND USED IN\nPAPERMAKING OR SIMILAR MACHINES (FOR EXAMPLE, FOR PULP OR ASBESTOS - CEMENT) : -\nWEIGHING 650 G/ M2OR MORE : FELT FOR COTTON TEXTILE INDUSTRIES, WOVEN",
+        "hsn_code": "59113210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE PRODUCTS AND ARTICLES, FOR TECHNICAL USES, SPECIFIED IN NOTE 7 TO THIS CHAPTER\n- TEXTILE FABRICS AND FELTS, ENDLESS OR FITTED WITH LINKING DEVICES, OF A KIND USED IN\nPAPERMAKING OR SIMILAR MACHINES (FOR EXAMPLE, FOR PULP OR ASBESTOS - CEMENT) : -\nWEIGHING LESS THAN 650 G/ M2 : OTHER",
+        "hsn_code": "59113190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE PRODUCTS AND ARTICLES, FOR TECHNICAL USES, SPECIFIED IN NOTE 7 TO THIS CHAPTER\n- TEXTILE FABRICS AND FELTS, ENDLESS OR FITTED WITH LINKING DEVICES, OF A KIND USED IN\nPAPERMAKING OR SIMILAR MACHINES (FOR EXAMPLE, FOR PULP OR ASBESTOS - CEMENT) : -\nWEIGHING LESS THAN 650 G/ M2 : TEXTILE FABRICS OF METALISED YARN OF A KIND COMMONLY\nUSED IN PAPER MAKING OR OTHERMACHINERY",
+        "hsn_code": "59113150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE PRODUCTS AND ARTICLES, FOR TECHNICAL USES, SPECIFIED IN NOTE 7 TO THIS CHAPTER\n- TEXTILE FABRICS AND FELTS, ENDLESS OR FITTED WITH LINKING DEVICES, OF A KIND USED IN\nPAPERMAKING OR SIMILAR MACHINES (FOR EXAMPLE, FOR PULP OR ASBESTOS - CEMENT) : -\nWEIGHING LESS THAN 650 G/ M2 : JUTE FABRICS AND ARTICLES USED IN MACHINERY OR PLANT",
+        "hsn_code": "59113140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE PRODUCTS AND ARTICLES, FOR TECHNICAL USES, SPECIFIED IN NOTE 7 TO THIS CHAPTER\n- TEXTILE FABRICS AND FELTS, ENDLESS OR FITTED WITH LINKING DEVICES, OF A KIND USED IN\nPAPERMAKING OR SIMILAR MACHINES (FOR EXAMPLE, FOR PULP OR ASBESTOS - CEMENT) : -\nWEIGHING LESS THAN 650 G/ M2 : COTTON FABRICS AND ARTICLES USED IN MACHINERY AND\nPLANT",
+        "hsn_code": "59113130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE PRODUCTS AND ARTICLES, FOR TECHNICAL USES, SPECIFIED IN NOTE 7 TO THIS CHAPTER\n- TEXTILE FABRICS AND FELTS, ENDLESS OR FITTED WITH LINKING DEVICES, OF A KIND USED IN\nPAPERMAKING OR SIMILAR MACHINES (FOR EXAMPLE, FOR PULP OR ASBESTOS - CEMENT) : -\nWEIGHING LESS THAN 650 G/ M2 : WOVEN TEXTILES FELT, WHETHER OR NOT IMPREGNATED OR\nCOATED, OF A KIND COMMONLY USED IN OTHER MACHINES",
+        "hsn_code": "59113120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE PRODUCTS AND ARTICLES, FOR TECHNICAL USES, SPECIFIED IN NOTE 7 TO THIS CHAPTER\n- TEXTILE FABRICS AND FELTS, ENDLESS OR FITTED WITH LINKING DEVICES, OF A KIND USED IN\nPAPERMAKING OR SIMILAR MACHINES (FOR EXAMPLE, FOR PULP OR ASBESTOS - CEMENT) : -\nWEIGHING LESS THAN 650 G/ M2 : FELT FOR COTTON TEXTILE INDUSTRIES, WOVEN",
+        "hsn_code": "59113110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE PRODUCTS AND ARTICLES, FOR TECHNICAL USES, SPECIFIED IN NOTE 7 TO THIS CHAPTER\n- BOLTING CLOTH, WHETHER OR NOT MADE UP",
+        "hsn_code": "59112000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE PRODUCTS AND ARTICLES, FOR TECHNICAL USES, SPECIFIED IN NOTE 7 TO THIS CHAPTER\n- TEXTILE FABRICS, FELT AND FELT-LINED WOVEN FABRICS, COATED, COVERED OR LAMINATED\nWITH RUBBER, LEATHER OR OTHER MATERIAL, OF A KIND USED FOR CARD CLOTHING, AND\nSIMILAR FABRICS OF A KIND USED FOR OTHER TECHNICAL PURPOSES, INCLUDING NARROW\nFABRICS MADE OF VELVET IMPREGNATED WITH RUBBER, FOR COVERING WEAVING SPINDLES\n(WEAVING BEAMS)",
+        "hsn_code": "59111000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION OR CONVEYOR BELTS OR BELTING, OF TEXTILE MATERIAL, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED WITH PLASTICS, OR REINFORCED WITH METAL OR OTHER MATERIAL",
+        "hsn_code": "5910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION OR CONVEYOR BELTS OR BELTING, OF TEXTILE MATERIAL, WHETHER OR NOT\nIMPREGNATED, COATED, COVERED OR LAMINATED WITH PLASTICS, OR REINFORCED WITH\nMETAL OR OTHER MATERIAL - TRANSMISSION OR CONVEYOR BELTS OR BELTING, OF TEXTILE\nMATERIAL, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED WITH\nPLASTICS, OR REINFORCED WITH METAL OR OTHER MATERIAL : OTHER",
+        "hsn_code": "59100090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION OR CONVEYOR BELTS OR BELTING, OF TEXTILE MATERIAL, WHETHER OR NOT\nIMPREGNATED, COATED, COVERED OR LAMINATED WITH PLASTICS, OR REINFORCED WITH\nMETAL OR OTHER MATERIAL - TRANSMISSION OR CONVEYOR BELTS OR BELTING, OF TEXTILE\nMATERIAL, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED WITH\nPLASTICS, OR REINFORCED WITH METAL OR OTHER MATERIAL : FIBRE BELT CONVEYOR",
+        "hsn_code": "59100060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION OR CONVEYOR BELTS OR BELTING, OF TEXTILE MATERIAL, WHETHER OR NOT\nIMPREGNATED, COATED, COVERED OR LAMINATED WITH PLASTICS, OR REINFORCED WITH\nMETAL OR OTHER MATERIAL - TRANSMISSION OR CONVEYOR BELTS OR BELTING, OF TEXTILE\nMATERIAL, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED WITH\nPLASTICS, OR REINFORCED WITH METAL OR OTHER MATERIAL : FLAX CANVAS PLY BELTING",
+        "hsn_code": "59100050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION OR CONVEYOR BELTS OR BELTING, OF TEXTILE MATERIAL, WHETHER OR NOT\nIMPREGNATED, COATED, COVERED OR LAMINATED WITH PLASTICS, OR REINFORCED WITH\nMETAL OR OTHER MATERIAL - TRANSMISSION OR CONVEYOR BELTS OR BELTING, OF TEXTILE\nMATERIAL, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED WITH\nPLASTICS, OR REINFORCED WITH METAL OR OTHER MATERIAL : HAIR BELTING",
+        "hsn_code": "59100040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION OR CONVEYOR BELTS OR BELTING, OF TEXTILE MATERIAL, WHETHER OR NOT\nIMPREGNATED, COATED, COVERED OR LAMINATED WITH PLASTICS, OR REINFORCED WITH\nMETAL OR OTHER MATERIAL - TRANSMISSION OR CONVEYOR BELTS OR BELTING, OF TEXTILE\nMATERIAL, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED WITH\nPLASTICS, OR REINFORCED WITH METAL OR OTHER MATERIAL : OTHER TRANSMISSION,\nCONVEYER OR ELEVATOR BELTS OR BELTING OF COTTON",
+        "hsn_code": "59100030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION OR CONVEYOR BELTS OR BELTING, OF TEXTILE MATERIAL, WHETHER OR NOT\nIMPREGNATED, COATED, COVERED OR LAMINATED WITH PLASTICS, OR REINFORCED WITH\nMETAL OR OTHER MATERIAL - TRANSMISSION OR CONVEYOR BELTS OR BELTING, OF TEXTILE\nMATERIAL, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED WITH\nPLASTICS, OR REINFORCED WITH METAL OR OTHER MATERIAL : RUBBERISED COTTON BELTING",
+        "hsn_code": "59100020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRANSMISSION OR CONVEYOR BELTS OR BELTING, OF TEXTILE MATERIAL, WHETHER OR NOT\nIMPREGNATED, COATED, COVERED OR LAMINATED WITH PLASTICS, OR REINFORCED WITH\nMETAL OR OTHER MATERIAL - TRANSMISSION OR CONVEYOR BELTS OR BELTING, OF TEXTILE\nMATERIAL, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED WITH\nPLASTICS, OR REINFORCED WITH METAL OR OTHER MATERIAL : COTTON CANVAS PLY BELTING",
+        "hsn_code": "59100010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE HOSE PIPING AND SIMILAR TEXTILE TUBING, WITH OR WITHOUT LINING, ARMOUR OR ACCESSORIES OF OTHER MATERIALS",
+        "hsn_code": "5909",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE HOSE PIPING AND SIMILAR TEXTILE TUBING, WITH OR WITHOUT LINING, ARMOUR OR\nACCESSORIES OF OTHER MATERIALS - TEXTILE HOSE PIPING AND SIMILAR TEXTILE TUBING, WITH\nOR WITHOUT LINING, ARMOUR OR ACCESSORIES OF OTHER MATERIALS : OF OTHER TEXTILE\nMATERIALS",
+        "hsn_code": "59090090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE HOSE PIPING AND SIMILAR TEXTILE TUBING, WITH OR WITHOUT LINING, ARMOUR OR\nACCESSORIES OF OTHER MATERIALS - TEXTILE HOSE PIPING AND SIMILAR TEXTILE TUBING, WITH\nOR WITHOUT LINING, ARMOUR OR ACCESSORIES OF OTHER MATERIALS : OF MAN-MADE FIBRE",
+        "hsn_code": "59090020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE HOSE PIPING AND SIMILAR TEXTILE TUBING, WITH OR WITHOUT LINING, ARMOUR OR\nACCESSORIES OF OTHER MATERIALS - TEXTILE HOSE PIPING AND SIMILAR TEXTILE TUBING, WITH\nOR WITHOUT LINING, ARMOUR OR ACCESSORIES OF OTHER MATERIALS : OF COTTON",
+        "hsn_code": "59090010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE WICKS, WOVEN, PLAITED OR KNITTED, FOR LAMPS, STOVES, LIGHTERS, CANDLES OR THE LIKE; INCANDESCENT GAS MANTLES AND TUBULAR KNITTED GAS MANTLE FABRIC THEREFOR, WHETHER OR NOT IMPREGNATED",
+        "hsn_code": "5908",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE WICKS, WOVEN, PLAITED OR KNITTED, FOR LAMPS, STOVES, LIGHTERS, CANDLES OR THE\nLIKE; INCANDESCENT GAS MANTLES AND TUBULAR KNITTED GAS MANTLE FABRIC THEREFOR,\nWHETHER OR NOT IMPREGNATED - TEXTILE WICKS, WOVEN, PLAITED OR KNITTED, FOR LAMPS,\nSTOVES, LIGHTERS, CANDLES OR THE LIKE; INCANDESCENT GAS MANTLES AND TUBULAR\nKNITTED GAS MANTLE FABRIC THEREFOR, WHETHER OR NOT IMPREGNATED : OTHER",
+        "hsn_code": "59080090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE WICKS, WOVEN, PLAITED OR KNITTED, FOR LAMPS, STOVES, LIGHTERS, CANDLES OR THE\nLIKE; INCANDESCENT GAS MANTLES AND TUBULAR KNITTED GAS MANTLE FABRIC THEREFOR,\nWHETHER OR NOT IMPREGNATED - TEXTILE WICKS, WOVEN, PLAITED OR KNITTED, FOR LAMPS,\nSTOVES, LIGHTERS, CANDLES OR THE LIKE; INCANDESCENT GAS MANTLES AND TUBULAR\nKNITTED GAS MANTLE FABRIC THEREFOR, WHETHER OR NOT IMPREGNATED : GAS MANTLES OF\nRAYON",
+        "hsn_code": "59080020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE WICKS, WOVEN, PLAITED OR KNITTED, FOR LAMPS, STOVES, LIGHTERS, CANDLES OR THE\nLIKE; INCANDESCENT GAS MANTLES AND TUBULAR KNITTED GAS MANTLE FABRIC THEREFOR,\nWHETHER OR NOT IMPREGNATED - TEXTILE WICKS, WOVEN, PLAITED OR KNITTED, FOR LAMPS,\nSTOVES, LIGHTERS, CANDLES OR THE LIKE; INCANDESCENT GAS MANTLES AND TUBULAR\nKNITTED GAS MANTLE FABRIC THEREFOR, WHETHER OR NOT IMPREGNATED : WICKS AND GAS\nMANTLE FABRICS, OF COTTON",
+        "hsn_code": "59080010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Textile fabrics; otherwise impregnated, coated or covered; painted canvas being theatrical scenery, studio back-cloths or the like",
+        "hsn_code": "5907",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS OTHERWISE IMPREGNATED, COATED OR COVERED; PAINTED CANVAS BEING\nTHEATRICAL SCENERY, STUDIO BACK - CLOTHS OR THE LIKE - TEXTILE FABRICS OTHERWISE\nIMPREGNATED, COATED OR COVERED; PAINTED CANVAS BEING THEATRICAL SCENERY, STUDIO\nBACK - CLOTHS OR THE LIKE : - OTHER : OTHER",
+        "hsn_code": "59070099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS OTHERWISE IMPREGNATED, COATED OR COVERED; PAINTED CANVAS BEING\nTHEATRICAL SCENERY, STUDIO BACK - CLOTHS OR THE LIKE - TEXTILE FABRICS OTHERWISE\nIMPREGNATED, COATED OR COVERED; PAINTED CANVAS BEING THEATRICAL SCENERY, STUDIO\nBACK - CLOTHS OR THE LIKE : - OTHER : JUTE FABRICS OTHERWISE IMPREGNATED OR COATED",
+        "hsn_code": "59070093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS OTHERWISE IMPREGNATED, COATED OR COVERED; PAINTED CANVAS BEING\nTHEATRICAL SCENERY, STUDIO BACK - CLOTHS OR THE LIKE - TEXTILE FABRICS OTHERWISE\nIMPREGNATED, COATED OR COVERED; PAINTED CANVAS BEING THEATRICAL SCENERY, STUDIO\nBACK - CLOTHS OR THE LIKE : - OTHER : OTHER TEXTILE FABRICS COATED OR IMPREGNATED\nWITH OIL OR OIL PREPARATIONS",
+        "hsn_code": "59070092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS OTHERWISE IMPREGNATED, COATED OR COVERED; PAINTED CANVAS BEING\nTHEATRICAL SCENERY, STUDIO BACK - CLOTHS OR THE LIKE - TEXTILE FABRICS OTHERWISE\nIMPREGNATED, COATED OR COVERED; PAINTED CANVAS BEING THEATRICAL SCENERY, STUDIO\nBACK - CLOTHS OR THE LIKE : - OTHER : COTTON FABRICS COATED OR IMPREGNATED WITH OIL\nOR PREPARATIONS WITH BASIS OF DRYING OIL",
+        "hsn_code": "59070091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS OTHERWISE IMPREGNATED, COATED OR COVERED; PAINTED CANVAS BEING\nTHEATRICAL SCENERY, STUDIO BACK - CLOTHS OR THE LIKE - TEXTILE FABRICS OTHERWISE\nIMPREGNATED, COATED OR COVERED; PAINTED CANVAS BEING THEATRICAL SCENERY, STUDIO\nBACK - CLOTHS OR THE LIKE : - FABRICS COVERED PARTIALLY OR FULLY WITH TEXTILE FLOCKS, OR\nWITH PREPARATION CONTAINING TEXTILE FLOCKS : ON BASE FABRICS OF OTHER TEXTILE\nMATERIALS",
+        "hsn_code": "59070019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS OTHERWISE IMPREGNATED, COATED OR COVERED; PAINTED CANVAS BEING\nTHEATRICAL SCENERY, STUDIO BACK - CLOTHS OR THE LIKE - TEXTILE FABRICS OTHERWISE\nIMPREGNATED, COATED OR COVERED; PAINTED CANVAS BEING THEATRICAL SCENERY, STUDIO\nBACK - CLOTHS OR THE LIKE : - FABRICS COVERED PARTIALLY OR FULLY WITH TEXTILE FLOCKS, OR\nWITH PREPARATION CONTAINING TEXTILE FLOCKS : ON THE BASE FABRICS OF MAN-MADE\nTEXTILE MATERIAL",
+        "hsn_code": "59070012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS OTHERWISE IMPREGNATED, COATED OR COVERED; PAINTED CANVAS BEING\nTHEATRICAL SCENERY, STUDIO BACK - CLOTHS OR THE LIKE - TEXTILE FABRICS OTHERWISE\nIMPREGNATED, COATED OR COVERED; PAINTED CANVAS BEING THEATRICAL SCENERY, STUDIO\nBACK - CLOTHS OR THE LIKE : - FABRICS COVERED PARTIALLY OR FULLY WITH TEXTILE FLOCKS, OR\nWITH PREPARATION CONTAINING TEXTILE FLOCKS : ON THE BASE FABRICS OF COTTON",
+        "hsn_code": "59070011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RUBBERISED TEXTILE FABRICS, OTHER THAN THOSE OF HEADING 5902",
+        "hsn_code": "5906",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RUBBERISED TEXTILE FABRICS, OTHER THAN THOSE OF HEADING 5902 - OTHER : - OTHER :\nOTHER",
+        "hsn_code": "59069990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RUBBERISED TEXTILE FABRICS, OTHER THAN THOSE OF HEADING 5902 - OTHER : - OTHER :\nRUBBERISED COTTON FABRICS, OTHER THAN KNITTED OR CROCHETED",
+        "hsn_code": "59069920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RUBBERISED TEXTILE FABRICS, OTHER THAN THOSE OF HEADING 5902 - OTHER : - OTHER :\nINSULATING TAPE, ELECTRICAL OF COTTON",
+        "hsn_code": "59069910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RUBBERISED TEXTILE FABRICS, OTHER THAN THOSE OF HEADING 5902 - OTHER : - KNITTED OR\nCROCHETED : OF OTHER TEXTILE MATERIALS",
+        "hsn_code": "59069190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RUBBERISED TEXTILE FABRICS, OTHER THAN THOSE OF HEADING 5902 - OTHER : - KNITTED OR\nCROCHETED : OF COTTON",
+        "hsn_code": "59069110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RUBBERISED TEXTILE FABRICS, OTHER THAN THOSE OF HEADING 5902 - ADHESIVE TAPE OF A\nWIDTH NOT EXCEEDING 20 CM",
+        "hsn_code": "59061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE WALL COVERINGS",
+        "hsn_code": "5905",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE WALL COVERINGS - TEXTILE WALL COVERINGS : OTHER",
+        "hsn_code": "59051090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE WALL COVERINGS - TEXTILE WALL COVERINGS : FIXED ON THE BACKING OF ANY\nMATERIAL",
+        "hsn_code": "59051010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE WALL COVERINGS - TEXTILE WALL COVERINGS : OTHER",
+        "hsn_code": "59050090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE WALL COVERINGS - TEXTILE WALL COVERINGS : FIXED ON THE BACKING OF ANY\nMATERIAL",
+        "hsn_code": "59050010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LINOLEUM, WHETHER OR NOT CUT TO SHAPE; FLOOR COVERINGS CONSISTING OF A COATING OR COVERING APPLIED ON A TEXTILE BACKING, WHETHER OR NOT CUT TO SHAPE",
+        "hsn_code": "5904",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LINOLEUM, WHETHER OR NOT CUT TO SHAPE; FLOOR COVERINGS CONSISTING OF A COATING\nOR COVERING APPLIED ON A TEXTILE BACKING, WHETHER OR NOT CUT TO SHAPE - OTHER :\nOTHER",
+        "hsn_code": "59049090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LINOLEUM, WHETHER OR NOT CUT TO SHAPE; FLOOR COVERINGS CONSISTING OF A COATING\nOR COVERING APPLIED ON A TEXTILE BACKING, WHETHER OR NOT CUT TO SHAPE - OTHER :\nFLOOR COVERINGS WITH JUTE BASE",
+        "hsn_code": "59049010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LINOLEUM, WHETHER OR NOT CUT TO SHAPE; FLOOR COVERINGS CONSISTING OF A COATING\nOR COVERING APPLIED ON A TEXTILE BACKING, WHETHER OR NOT CUT TO SHAPE - LINOLEUM",
+        "hsn_code": "59041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS, IMPREGNATED, COATED, COVERED OR LAMINATED WITH PLASTICS, OTHER THAN THOSE OF HEADING 5902",
+        "hsn_code": "5903",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS, IMPREGNATED, COATED, COVERED OR LAMINATED WITH PLASTICS, OTHER\nTHAN THOSE OF HEADING 5902 - OTHER : OTHER",
+        "hsn_code": "59039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS, IMPREGNATED, COATED, COVERED OR LAMINATED WITH PLASTICS, OTHER\nTHAN THOSE OF HEADING 5902 - OTHER : POLYETHYLENE LAMINATED JUTE FABRICS",
+        "hsn_code": "59039020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS, IMPREGNATED, COATED, COVERED OR LAMINATED WITH PLASTICS, OTHER\nTHAN THOSE OF HEADING 5902 - OTHER : OF COTTON",
+        "hsn_code": "59039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS, IMPREGNATED, COATED, COVERED OR LAMINATED WITH PLASTICS, OTHER\nTHAN THOSE OF HEADING 5902- WITH POLYURETHANE : OTHER",
+        "hsn_code": "59032090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS, IMPREGNATED, COATED, COVERED OR LAMINATED WITH PLASTICS, OTHER\nTHAN THOSE OF HEADING 5902- WITH POLYURETHANE : IMITATION LEATHER FABRICS, OF\nCOTTON",
+        "hsn_code": "59032010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS, IMPREGNATED, COATED, COVERED OR LAMINATED WITH PLASTICS, OTHER\nTHAN THOSE OF HEADING 5902- WITH POLYVINYL CHLORIDE : OTHER",
+        "hsn_code": "59031090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS, IMPREGNATED, COATED, COVERED OR LAMINATED WITH PLASTICS, OTHER\nTHAN THOSE OF HEADING 5902- WITH POLYVINYL CHLORIDE : IMITATION LEATHER FABRICS OF\nCOTTON",
+        "hsn_code": "59031010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TYRE CORD FABRIC OF HIGH TENACITY YARN OF NYLON OR OTHER POLYAMIDES, POLYESTERS OR VISCOSE RAYON",
+        "hsn_code": "5902",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TYRE CORD FABRIC OF HIGH TENACITY YARN OF NYLON OR OTHER POLYAMIDES, POLYESTERS OR\nVISCOSE RAYON - OTHER : OTHER",
+        "hsn_code": "59029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TYRE CORD FABRIC OF HIGH TENACITY YARN OF NYLON OR OTHER POLYAMIDES, POLYESTERS OR\nVISCOSE RAYON - OTHER : IMPREGNATED WITH RUBBER",
+        "hsn_code": "59029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TYRE CORD FABRIC OF HIGH TENACITY YARN OF NYLON OR OTHER POLYAMIDES, POLYESTERS OR\nVISCOSE RAYON - OTHER : OTHER",
+        "hsn_code": "59023090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TYRE CORD FABRIC OF HIGH TENACITY YARN OF NYLON OR OTHER POLYAMIDES, POLYESTERS OR\nVISCOSE RAYON - OTHER : IMPREGNATED WITH RUBBER",
+        "hsn_code": "59023010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TYRE CORD FABRIC OF HIGH TENACITY YARN OF NYLON OR OTHER POLYAMIDES, POLYESTERS OR\nVISCOSE RAYON - OF POLYESTERS : OTHER",
+        "hsn_code": "59022090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TYRE CORD FABRIC OF HIGH TENACITY YARN OF NYLON OR OTHER POLYAMIDES, POLYESTERS OR\nVISCOSE RAYON - OF POLYESTERS : IMPREGNATED WITH RUBBER",
+        "hsn_code": "59022010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TYRE CORD FABRIC OF HIGH TENACITY YARN OF NYLON OR OTHER POLYAMIDES, POLYESTERS OR\nVISCOSE RAYON - OF NYLON OR OTHER POLYAMIDES : OTHER",
+        "hsn_code": "59021090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TYRE CORD FABRIC OF HIGH TENACITY YARN OF NYLON OR OTHER POLYAMIDES, POLYESTERS OR\nVISCOSE RAYON - OF NYLON OR OTHER POLYAMIDES : IMPREGNATED WITH RUBBER",
+        "hsn_code": "59021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS COATED WITH GUM OR AMYLACEOUS SUBSTANCES, OF A KIND USED FOR THE OUTER COVERS OF BOOKS OR THE LIKE; TRACING CLOTH; PREPARED PAINTING CANVAS; BUCKRAM AND SIMILAR STIFFENED TEXTILE FABRICS OF A KIND USED FOR HAT FOUNDATIONS",
+        "hsn_code": "5901",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS COATED WITH GUM OR AMYLACEOUS SUBSTANCES, OF A KIND USED FOR THE\nOUTER COVERS OF BOOKS OR THE LIKE; TRACING CLOTH; PREPARED PAINTING CANVAS;\nBUCKRAM AND SIMILAR STIFFENED TEXTILE FABRICS OF A KIND USED FOR HAT FOUNDATIONS -\nOTHER : OTHER",
+        "hsn_code": "59019090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS COATED WITH GUM OR AMYLACEOUS SUBSTANCES, OF A KIND USED FOR THE\nOUTER COVERS OF BOOKS OR THE LIKE; TRACING CLOTH; PREPARED PAINTING CANVAS;\nBUCKRAM AND SIMILAR STIFFENED TEXTILE FABRICS OF A KIND USED FOR HAT FOUNDATIONS -\nOTHER : VARNISHED CAMBRIC FABRICS (EMPIRE FABRICS) TAPES",
+        "hsn_code": "59019020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS COATED WITH GUM OR AMYLACEOUS SUBSTANCES, OF A KIND USED FOR THE\nOUTER COVERS OF BOOKS OR THE LIKE; TRACING CLOTH; PREPARED PAINTING CANVAS;\nBUCKRAM AND SIMILAR STIFFENED TEXTILE FABRICS OF A KIND USED FOR HAT FOUNDATIONS -\nOTHER : TRACING CLOTH OF COTTON",
+        "hsn_code": "59019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS COATED WITH GUM OR AMYLACEOUS SUBSTANCES, OF A KIND USED FOR THE\nOUTER COVERS OF BOOKS OR THE LIKE; TRACING CLOTH; PREPARED PAINTING CANVAS;\nBUCKRAM AND SIMILAR STIFFENED TEXTILE FABRICS OF A KIND USED FOR HAT FOUNDATIONS -\nTEXTILE FABRICS COATED WITH GUM OR AMYLACEOUS SUBSTANCES, OF A KIND USED FOR THE\nOUTER COVERS OF BOOKS OR THE LIKE : OTHER",
+        "hsn_code": "59011090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS COATED WITH GUM OR AMYLACEOUS SUBSTANCES, OF A KIND USED FOR THE\nOUTER COVERS OF BOOKS OR THE LIKE; TRACING CLOTH; PREPARED PAINTING CANVAS;\nBUCKRAM AND SIMILAR STIFFENED TEXTILE FABRICS OF A KIND USED FOR HAT FOUNDATIONS -\nTEXTILE FABRICS COATED WITH GUM OR AMYLACEOUS SUBSTANCES, OF A KIND USED FOR THE\nOUTER COVERS OF BOOKS OR THE LIKE : PREPARED PAINTING CANVAS",
+        "hsn_code": "59011020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEXTILE FABRICS COATED WITH GUM OR AMYLACEOUS SUBSTANCES, OF A KIND USED FOR THE\nOUTER COVERS OF BOOKS OR THE LIKE; TRACING CLOTH; PREPARED PAINTING CANVAS;\nBUCKRAM AND SIMILAR STIFFENED TEXTILE FABRICS OF A KIND USED FOR HAT FOUNDATIONS -\nTEXTILE FABRICS COATED WITH GUM OR AMYLACEOUS SUBSTANCES, OF A KIND USED FOR THE\nOUTER COVERS OF BOOKS OR THE LIKE : OF COTTON",
+        "hsn_code": "59011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "QUILTED TEXTILE PRODUCTS IN THE PIECE, COMPOSED OF ONE OR MORE LAYERS OF TEXTILE MATERIALS ASSEMBLED WITH PADDING BY STITCHING OR OTHERWISE, OTHER THAN EMBROIDERY OF HEADING 5810",
+        "hsn_code": "5811",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "QUILTED TEXTILE PRODUCTS IN THE PIECE, COMPOSED OF ONE OR MORE LAYERS OF TEXTILE\nMATERIALS ASSEMBLED WITH PADDING BY STITCHING OR OTHERWISE, OTHER THAN\nEMBROIDERY OF HEADING 5810 - QUILTED TEXTILE PRODUCTS IN THE PIECE, COMPOSED OF\nONE OR MORE LAYERS OF TEXTILE MATERIALS ASSEMBLED WITH PADDING BY STITCHING OR\nOTHERWISE, OTHER THAN EMBROIDERY OF HEADING 5810:OTHER",
+        "hsn_code": "58110090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "QUILTED TEXTILE PRODUCTS IN THE PIECE, COMPOSED OF ONE OR MORE LAYERS OF TEXTILE\nMATERIALS ASSEMBLED WITH PADDING BY STITCHING OR OTHERWISE, OTHER THAN\nEMBROIDERY OF HEADING 5810 - QUILTED TEXTILE PRODUCTS IN THE PIECE, COMPOSED OF\nONE OR MORE LAYERS OF TEXTILE MATERIALS ASSEMBLED WITH PADDING BY STITCHING OR\nOTHERWISE, OTHER THAN EMBROIDERY OF HEADING 5810:QUILTED WADDING",
+        "hsn_code": "58110020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "QUILTED TEXTILE PRODUCTS IN THE PIECE, COMPOSED OF ONE OR MORE LAYERS OF TEXTILE\nMATERIALS ASSEMBLED WITH PADDING BY STITCHING OR OTHERWISE, OTHER THAN\nEMBROIDERY OF HEADING 5810 - QUILTED TEXTILE PRODUCTS IN THE PIECE, COMPOSED OF\nONE OR MORE LAYERS OF TEXTILE MATERIALS ASSEMBLED WITH PADDING BY STITCHING OR\nOTHERWISE, OTHER THAN EMBROIDERY OF HEADING 5810:KANTHA (MULTILAYER STITCHED\nTEXTILE FABRICS IN PIECE USED FOR BEDDING, MATTRESS PADS OR CLOTHING)",
+        "hsn_code": "58110010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BRAIDS IN THE PIECE; ORNAMENTAL TRIMMINGS IN THE PIECE, WITHOUT EMBROIDERY, OTHER THAN KNITTED OR CROCHETED; TASSELS, POMPONS AND SIMILAR ARTICLES",
+        "hsn_code": "5808",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BRAIDS IN THE PIECE; ORNAMENTAL TRIMMINGS IN THE PIECE, WITHOUT EMBROIDERY, OTHER\nTHAN KNITTED OR CROCHETED; TASSELS, POMPONS AND SIMILAR ARTICLES - OTHER: OTHER",
+        "hsn_code": "58089090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BRAIDS IN THE PIECE; ORNAMENTAL TRIMMINGS IN THE PIECE, WITHOUT EMBROIDERY, OTHER\nTHAN KNITTED OR CROCHETED; TASSELS, POMPONS AND SIMILAR ARTICLES - OTHER: SAREE\nFALLS, BORDERS (OTHER THAN ZARI), FRINGS OF MAN-MADE FIBRE",
+        "hsn_code": "58089060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BRAIDS IN THE PIECE; ORNAMENTAL TRIMMINGS IN THE PIECE, WITHOUT EMBROIDERY, OTHER\nTHAN KNITTED OR CROCHETED; TASSELS, POMPONS AND SIMILAR ARTICLES - OTHER:SAREE\nFALLS, BORDERS (OTHER THAN ZARI), FRINGS OF COTTON",
+        "hsn_code": "58089050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BRAIDS IN THE PIECE; ORNAMENTAL TRIMMINGS IN THE PIECE, WITHOUT EMBROIDERY, OTHER\nTHAN KNITTED OR CROCHETED; TASSELS, POMPONS AND SIMILAR ARTICLES - OTHER:RIBBONS\nOF RAYON WITH ORNAMENTAL TRIMMINGS",
+        "hsn_code": "58089040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BRAIDS IN THE PIECE; ORNAMENTAL TRIMMINGS IN THE PIECE, WITHOUT EMBROIDERY, OTHER\nTHAN KNITTED OR CROCHETED; TASSELS, POMPONS AND SIMILAR ARTICLES - OTHER:OTHER\nBRAIDS",
+        "hsn_code": "58089030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BRAIDS IN THE PIECE; ORNAMENTAL TRIMMINGS IN THE PIECE, WITHOUT EMBROIDERY, OTHER\nTHAN KNITTED OR CROCHETED; TASSELS, POMPONS AND SIMILAR ARTICLES - OTHER:HAIR BAND\nOF NARROW FABRICS",
+        "hsn_code": "58089020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BRAIDS IN THE PIECE; ORNAMENTAL TRIMMINGS IN THE PIECE, WITHOUT EMBROIDERY, OTHER\nTHAN KNITTED OR CROCHETED; TASSELS, POMPONS AND SIMILAR ARTICLES - OTHER:TAPES,\nORNAMENTAL OR COTTON",
+        "hsn_code": "58089010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LABELS, BADGES AND SIMILAR ARTICLES OF TEXTILE MATERIALS, IN THE PIECE, IN STRIPS OR CUT TO SHAPE OR SIZE NOT EMBROIDERED",
+        "hsn_code": "5807",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LABELS, BADGES AND SIMILAR ARTICLES OF TEXTILE MATERIALS, IN THE PIECE, IN STRIPS OR CUT\nTO SHAPE OR SIZE, NOT EMBROIDERED - OTHER:OTHER",
+        "hsn_code": "58079090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LABELS, BADGES AND SIMILAR ARTICLES OF TEXTILE MATERIALS, IN THE PIECE, IN STRIPS OR CUT\nTO SHAPE OR SIZE, NOT EMBROIDERED - OTHER:FELT OR NON-WOVEN",
+        "hsn_code": "58079010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LABELS, BADGES AND SIMILAR ARTICLES OF TEXTILE MATERIALS, IN THE PIECE, IN STRIPS OR CUT\nTO SHAPE OR SIZE, NOT EMBROIDERED - WOVEN:OTHER",
+        "hsn_code": "58071090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LABELS, BADGES AND SIMILAR ARTICLES OF TEXTILE MATERIALS, IN THE PIECE, IN STRIPS OR CUT\nTO SHAPE OR SIZE, NOT EMBROIDERED - WOVEN:OF MAN-MADE FIBRE",
+        "hsn_code": "58071020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LABELS, BADGES AND SIMILAR ARTICLES OF TEXTILE MATERIALS, IN THE PIECE, IN STRIPS OR CUT\nTO SHAPE OR SIZE, NOT EMBROIDERED - WOVEN:OF COTTON",
+        "hsn_code": "58071010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NARROW WOVEN FABRICS OTHER THAN GOODS OF HEADING 5807; NARROW FABRICS CONSISTING OF WARP WITHOUT WEFT ASSEMBLED BY MEANS OF AN ADHESIVE (BOLDUCS)",
+        "hsn_code": "5806",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NARROW WOVEN FABRICS OTHER THAN GOODS OF HEADING 5807; NARROW FABRICS\nCONSISTING OF WARP WITHOUT WEFT ASSEMBLED BY MEANS OF AN ADHESIVE (BOLDUCS) -\nFABRICS CONSISTING OF WARP WITHOUT WEFT ASSEMBLED BY MEANS OF AN ADHESIVE\n(BOLDUCS)",
+        "hsn_code": "58064000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NARROW WOVEN FABRICS OTHER THAN GOODS OF HEADING 5807; NARROW FABRICS\nCONSISTING OF WARP WITHOUT WEFT ASSEMBLED BY MEANS OF AN ADHESIVE (BOLDUCS)-\nOTHER WOVEN FABRICS: - OF OTHER TEXTILE MATERIALS:OTHER",
+        "hsn_code": "58063990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NARROW WOVEN FABRICS OTHER THAN GOODS OF HEADING 5807; NARROW FABRICS\nCONSISTING OF WARP WITHOUT WEFT ASSEMBLED BY MEANS OF AN ADHESIVE (BOLDUCS)-\nOTHER WOVEN FABRICS: - OF OTHER TEXTILE MATERIALS: OTHER NARROW FABRICS OF JUTE",
+        "hsn_code": "58063930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NARROW WOVEN FABRICS OTHER THAN GOODS OF HEADING 5807; NARROW FABRICS\nCONSISTING OF WARP WITHOUT WEFT ASSEMBLED BY MEANS OF AN ADHESIVE (BOLDUCS)-\nOTHER WOVEN FABRICS: - OF OTHER TEXTILE MATERIALS: JUTE WEBBING",
+        "hsn_code": "58063920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NARROW WOVEN FABRICS OTHER THAN GOODS OF HEADING 5807; NARROW FABRICS\nCONSISTING OF WARP WITHOUT WEFT ASSEMBLED BY MEANS OF AN ADHESIVE (BOLDUCS)-\nOTHER WOVEN FABRICS: - OF OTHER TEXTILE MATERIALS:GOAT HAIR PUTTIS TAPE",
+        "hsn_code": "58063910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NARROW WOVEN FABRICS OTHER THAN GOODS OF HEADING 5807; NARROW FABRICS\nCONSISTING OF WARP WITHOUT WEFT ASSEMBLED BY MEANS OF AN ADHESIVE (BOLDUCS)-\nOTHER WOVEN FABRICS: OF MAN-MADE FIBRES",
+        "hsn_code": "58063200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NARROW WOVEN FABRICS OTHER THAN GOODS OF HEADING 5807; NARROW FABRICS\nCONSISTING OF WARP WITHOUT WEFT ASSEMBLED BY MEANS OF AN ADHESIVE (BOLDUCS)-\nOTHER WOVEN FABRICS: - OF COTTON:OTHER",
+        "hsn_code": "58063190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NARROW WOVEN FABRICS OTHER THAN GOODS OF HEADING 5807; NARROW FABRICS\nCONSISTING OF WARP WITHOUT WEFT ASSEMBLED BY MEANS OF AN ADHESIVE (BOLDUCS)-\nOTHER WOVEN FABRICS: - OF COTTON:NEWAR COTTON",
+        "hsn_code": "58063120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NARROW WOVEN FABRICS OTHER THAN GOODS OF HEADING 5807; NARROW FABRICS\nCONSISTING OF WARP WITHOUT WEFT ASSEMBLED BY MEANS OF AN ADHESIVE (BOLDUCS)-\nOTHER WOVEN FABRICS: - OF COTTON:TYPEWRITER RIBBON CLOTH",
+        "hsn_code": "58063110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NARROW WOVEN FABRICS OTHER THAN GOODS OF HEADING 5807; NARROW FABRICS\nCONSISTING OF WARP WITHOUT WEFT ASSEMBLED BY MEANS OF AN ADHESIVE (BOLDUCS) -\nOTHER WOVEN FABRICS, CONTAINING BY WEIGHT 5% OR MORE OF ELASTOMERIC YARN OR\nRUBBER THREAD",
+        "hsn_code": "58062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NARROW WOVEN FABRICS OTHER THAN GOODS OF HEADING 5807; NARROW FABRICS\nCONSISTING OF WARP WITHOUT WEFT ASSEMBLED BY MEANS OF AN ADHESIVE (BOLDUCS) -\nWOVEN PILE FABRICS (INCLUDING TERRY TOWELLING AND SIMILAR TERRY FABRICS) AND\nCHENILLE FABRICS",
+        "hsn_code": "58061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HAND-WOVEN TAPESTRIES OF THE TYPE GOBELINS, FLANDERS, AUBUSSON, BEAUVAIS AND THE LIKE, AND NEEDLE-WORKED TAPESTRIES (FOR EXAMPLE, PETIT POINT, CROSS STITCH), WHETHER OR NOT MADE UP",
+        "hsn_code": "5805",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HAND - WOVEN TAPESTRIES OF THE TYPE GOBELINS, FLANDERS, AUBUSSON, BEAUVAIS AND THE\nLIKE, AND NEEDLE - WORKED TAPESTRIES (FOR EXAMPLE, PETIT POINT, CROSS STITCH),\nWHETHER OR NOT MADE UP 5805 00 - HAND - WOVEN TAPESTRIES OF THE TYPE GOBELINS,\nFLANDERS, AUBUSSON, BEAUVAIS AND THE LIKE, AND NEEDLE - WORKED TAPESTRIES (FOR\nEXAMPLE, PETIT POINT, CROSS STITCH), WHETHER OR NOT MADE UP:OTHER",
+        "hsn_code": "58050090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HAND - WOVEN TAPESTRIES OF THE TYPE GOBELINS, FLANDERS, AUBUSSON, BEAUVAIS AND THE\nLIKE, AND NEEDLE - WORKED TAPESTRIES (FOR EXAMPLE, PETIT POINT, CROSS STITCH),\nWHETHER OR NOT MADE UP 5805 00 - HAND - WOVEN TAPESTRIES OF THE TYPE GOBELINS,\nFLANDERS, AUBUSSON, BEAUVAIS AND THE LIKE, AND NEEDLE - WORKED TAPESTRIES (FOR\nEXAMPLE, PETIT POINT, CROSS STITCH), WHETHER OR NOT MADE UP:TAPESTRIES OF JUTE",
+        "hsn_code": "58050020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HAND - WOVEN TAPESTRIES OF THE TYPE GOBELINS, FLANDERS, AUBUSSON, BEAUVAIS AND THE\nLIKE, AND NEEDLE - WORKED TAPESTRIES (FOR EXAMPLE, PETIT POINT, CROSS STITCH),\nWHETHER OR NOT MADE UP 5805 00 - HAND - WOVEN TAPESTRIES OF THE TYPE GOBELINS,\nFLANDERS, AUBUSSON, BEAUVAIS AND THE LIKE, AND NEEDLE - WORKED TAPESTRIES (FOR\nEXAMPLE, PETIT POINT, CROSS STITCH), WHETHER OR NOT MADE UP:TAPESTRIES HAND MADE\nOR NEEDLE WORKED BY HAND, OF COTTON",
+        "hsn_code": "58050010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TULLES AND OTHER NET FABRICS, NOT INCLUDING WOVEN, KNITTED OR CROCHETED FABRICS; LACE IN THE PIECE, IN STRIPS OR IN MOTIFS, OTHER THAN FABRICS OF HEADING 6002 TO 6006.",
+        "hsn_code": "5804",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TULLES AND OTHER NET FABRICS, NOT INCLUDING WOVEN, KNITTED OR CROCHETED FABRICS;\nLACE IN THE PIECE, IN STRIPS OR IN MOTIFS, OTHER THAN FABRICS OF HEADINGS 6002 TO 6006-\nMECHANICALLY MADE LACE:- OF OTHER TEXTILE MATERIALS:OTHER",
+        "hsn_code": "58042990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TULLES AND OTHER NET FABRICS, NOT INCLUDING WOVEN, KNITTED OR CROCHETED FABRICS;\nLACE IN THE PIECE, IN STRIPS OR IN MOTIFS, OTHER THAN FABRICS OF HEADINGS 6002 TO 6006-\nMECHANICALLY MADE LACE:- OF OTHER TEXTILE MATERIALS:OF COTTON",
+        "hsn_code": "58042910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TULLES AND OTHER NET FABRICS, NOT INCLUDING WOVEN, KNITTED OR CROCHETED FABRICS;\nLACE IN THE PIECE, IN STRIPS OR IN MOTIFS, OTHER THAN FABRICS OF HEADINGS 6002 TO 6006-\nMECHANICALLY MADE LACE: OF MAN-MADE FIBRES",
+        "hsn_code": "58042100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TULLES AND OTHER NET FABRICS, NOT INCLUDING WOVEN, KNITTED OR CROCHETED FABRICS;\nLACE IN THE PIECE, IN STRIPS OR IN MOTIFS, OTHER THAN FABRICS OF HEADINGS 6002 TO\n6006TULLES AND OTHER NET FABRICS:OTHER",
+        "hsn_code": "58041090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TULLES AND OTHER NET FABRICS, NOT INCLUDING WOVEN, KNITTED OR CROCHETED FABRICS;\nLACE IN THE PIECE, IN STRIPS OR IN MOTIFS, OTHER THAN FABRICS OF HEADINGS 6002 TO\n6006TULLES AND OTHER NET FABRICS:OF COTTON",
+        "hsn_code": "58041010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Gauze; other than narrow fabrics of heading no. 5806",
+        "hsn_code": "5803",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE, OTHER THAN NARROW FABRICS OF HEADING 5806 5803 10 - OF OTHER TEXTILE\nMATERIALS:OTHER",
+        "hsn_code": "58039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE, OTHER THAN NARROW FABRICS OF HEADING 5806 5803 10 - OF OTHER TEXTILE\nMATERIALS:OF ARTIFICIAL FIBRE",
+        "hsn_code": "58039030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE, OTHER THAN NARROW FABRICS OF HEADING 5806 5803 10 - OF OTHER TEXTILE\nMATERIALS:OF SYNTHETIC FIBER",
+        "hsn_code": "58039020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE, OTHER THAN NARROW FABRICS OF HEADING 5806 5803 10 - OF OTHER TEXTILE\nMATERIALS:OF SILK OR SILK WASTE",
+        "hsn_code": "58039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE, OTHER THAN NARROW FABRICS OF HEADING 5806 5803 10 - OF COTTON:OTHER",
+        "hsn_code": "58031090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE, OTHER THAN NARROW FABRICS OF HEADING 5806 5803 10 - OF COTTON:PRINTED",
+        "hsn_code": "58031050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE, OTHER THAN NARROW FABRICS OF HEADING 5806 5803 10 - OF COTTON:YARN DYED",
+        "hsn_code": "58031040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE, OTHER THAN NARROW FABRICS OF HEADING 5806 5803 10 - OF COTTON:PIECE DYED",
+        "hsn_code": "58031030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE, OTHER THAN NARROW FABRICS OF HEADING 5806 5803 10 - OF COTTON:BLEACHED",
+        "hsn_code": "58031020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE, OTHER THAN NARROW FABRICS OF HEADING 5806 5803 10 - OF COTTON:UNBLEACHED",
+        "hsn_code": "58031010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE,OTHER THAN NARROW FABRICS OF HEADING 5806 - GAUZE, OTHER THAN NARROW\nFABRICS OF HEADING 5806 --- OF OTHER TEXTILE MATERIALS : ---- OTHER",
+        "hsn_code": "58030099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE,OTHER THAN NARROW FABRICS OF HEADING 5806 - GAUZE, OTHER THAN NARROW\nFABRICS OF HEADING 5806 --- OF OTHER TEXTILE MATERIALS: ----OF ARTIFICIAL FIBRE",
+        "hsn_code": "58030093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE,OTHER THAN NARROW FABRICS OF HEADING 5806 - GAUZE, OTHER THAN NARROW\nFABRICS OF HEADING 5806 --- OF OTHER TEXTILE MATERIALS :---- OF SYNTHETIC FIBRE",
+        "hsn_code": "58030092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE,OTHER THAN NARROW FABRICS OF HEADING 5806 - GAUZE, OTHER THAN NARROW\nFABRICS OF HEADING 5806 --- OF OTHER TEXTILE MATERIALS: ---- OF SILK OR SILK WASTE",
+        "hsn_code": "58030091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE,OTHER THAN NARROW FABRICS OF HEADING 5806 - GAUZE, OTHER THAN NARROW\nFABRICS OF HEADING 5806 --- OF COTTON: ---- OTHER",
+        "hsn_code": "58030019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE,OTHER THAN NARROW FABRICS OF HEADING 5806 - GAUZE, OTHER THAN NARROW\nFABRICS OF HEADING 5806 --- OF COTTON: ---- PRINTED",
+        "hsn_code": "58030015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE,OTHER THAN NARROW FABRICS OF HEADING 5806 - GAUZE, OTHER THAN NARROW\nFABRICS OF HEADING 5806 --- OF COTTON: ---- YARN DYED",
+        "hsn_code": "58030014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE,OTHER THAN NARROW FABRICS OF HEADING 5806 - GAUZE, OTHER THAN NARROW\nFABRICS OF HEADING 5806 --- OF COTTON: ---- PIECE DYED",
+        "hsn_code": "58030013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE,OTHER THAN NARROW FABRICS OF HEADING 5806 - GAUZE, OTHER THAN NARROW\nFABRICS OF HEADING 5806 --- OF COTTON: ---- BLEACHED",
+        "hsn_code": "58030012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GAUZE,OTHER THAN NARROW FABRICS OF HEADING 5806 - GAUZE, OTHER THAN NARROW\nFABRICS OF HEADING 5806 --- OF COTTON:---- UNBLEACHED",
+        "hsn_code": "58030011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TERRY TOWELLING AND SIMILAR WOVEN TERRY FABRICS, OTHER THAN NARROW FABRICS OF HEADING 5806; TUFTED TEXTILE FABRICS, OTHER THAN PRODUCTS OF HEADING 5703",
+        "hsn_code": "5802",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TERRY TOWELLING AND SIMILAR WOVEN TERRY FABRICS, OTHER THAN NARROW FABRICS OF\nHEADING 5806; TUFTED TEXTILE FABRICS, OTHER THAN PRODUCTS OF HEADING 5703 - TUFTED\nTEXTILE FABRICS",
+        "hsn_code": "58023000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TERRY TOWELLING AND SIMILAR WOVEN TERRY FABRICS, OTHER THAN NARROW FABRICS OF\nHEADING 5806; TUFTED TEXTILE FABRICS, OTHER THAN PRODUCTS OF HEADING 5703 - TERRY\nTOWELLING AND SIMILAR WOVEN TERRY FABRICS, OF OTHER TEXTILE MATERIALS",
+        "hsn_code": "58022000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TERRY TOWELLING AND SIMILAR WOVEN TERRY FABRICS, OTHER THAN NARROW FABRICS OF\nHEADING 5806; TUFTED TEXTILE FABRICS, OTHER THAN PRODUCTS OF HEADING 5703 - TERRY\nTOWELLING AND SIMILAR WOVEN TERRY FABRICS, OF COTTON: - OTHER:OTHER",
+        "hsn_code": "58021990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TERRY TOWELLING AND SIMILAR WOVEN TERRY FABRICS, OF COTTON - OF HANDLOOM",
+        "hsn_code": "58021950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TERRY TOWELLING AND SIMILAR WOVEN TERRY FABRICS, OTHER THAN NARROW FABRICS OF\nHEADING 5806; TUFTED TEXTILE FABRICS, OTHER THAN PRODUCTS OF HEADING 5703 - TERRY\nTOWELLING AND SIMILAR WOVEN TERRY FABRICS, OF COTTON: - OTHER:PRINTED",
+        "hsn_code": "58021940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TERRY TOWELLING AND SIMILAR WOVEN TERRY FABRICS, OTHER THAN NARROW FABRICS OF\nHEADING 5806; TUFTED TEXTILE FABRICS, OTHER THAN PRODUCTS OF HEADING 5703 - TERRY\nTOWELLING AND SIMILAR WOVEN TERRY FABRICS, OF COTTON: - OTHER:YARN DYED",
+        "hsn_code": "58021930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TERRY TOWELLING AND SIMILAR WOVEN TERRY FABRICS, OTHER THAN NARROW FABRICS OF\nHEADING 5806; TUFTED TEXTILE FABRICS, OTHER THAN PRODUCTS OF HEADING 5703 - TERRY\nTOWELLING AND SIMILAR WOVEN TERRY FABRICS, OF COTTON: - OTHER:PIECE DYED",
+        "hsn_code": "58021920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TERRY TOWELLING AND SIMILAR WOVEN TERRY FABRICS, OTHER THAN NARROW FABRICS OF\nHEADING 5806; TUFTED TEXTILE FABRICS, OTHER THAN PRODUCTS OF HEADING 5703 - TERRY\nTOWELLING AND SIMILAR WOVEN TERRY FABRICS, OF COTTON: - OTHER:BLEACHED",
+        "hsn_code": "58021910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TERRY TOWELLING AND SIMILAR WOVEN TERRY FABRICS, OTHER THAN NARROW FABRICS OF\nHEADING 5806; TUFTED TEXTILE FABRICS, OTHER THAN PRODUCTS OF HEADING 5703 - TERRY\nTOWELLING AND SIMILAR WOVEN TERRY FABRICS, OF COTTON:UNBLEACHED",
+        "hsn_code": "58021100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, OF FELT, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP - TILES, HAVING A MAXIMUM SURFACE AREA OF 0.3 M2",
+        "hsn_code": "57041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, OF FELT, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP - OTHER : COTTON",
+        "hsn_code": "57049010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, OF FELT, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP - OTHER : WOOLLEN, OTHER THAN ARTWARE",
+        "hsn_code": "57049020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, OF FELT, NOT TUFTED OR FLOCKED, WHETHER\nOR NOT MADE UP - OTHER : OTHER",
+        "hsn_code": "57049090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, OF FELT, NOT TUFTED OR FLOCKED, WHETHER OR NOT MADE UP",
+        "hsn_code": "5704",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, KNOTTED, WHETHER OR NOT MADE UP - OF\nWOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "57011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, KNOTTED, WHETHER OR NOT MADE UP - OF\nOTHER TEXTILE MATERIALS : OF COTTON",
+        "hsn_code": "57019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, KNOTTED, WHETHER OR NOT MADE UP - OF\nCOIR INCLUDING GEO TEXTILE",
+        "hsn_code": "57019020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, KNOTTED, WHETHER OR NOT MADE UP - OF\nOTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "57019090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARPETS AND OTHER TEXTILE FLOOR COVERINGS, KNOTTED, WHETHER OR NOT MADE UP",
+        "hsn_code": "5701",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KNOTTED NETTING OF TWINE, CORDAGE OR ROPE; MADE UP FISHING NETS AND OTHER MADE UP NETS, OF TEXTILE MATERIALS",
+        "hsn_code": "5608",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "KNOTTED NETTING OF TWINE, CORDAGE OR ROPE; MADE UP FISHING NETS AND OTHER MADE\nUP NETS, OF TEXTILE MATERIALS - OTHER : OTHER",
+        "hsn_code": "56089090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "KNOTTED NETTING OF TWINE, CORDAGE OR ROPE; MADE UP FISHING NETS AND OTHER MADE\nUP NETS, OF TEXTILE MATERIALS - OTHER : OF JUTE",
+        "hsn_code": "56089020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "KNOTTED NETTING OF TWINE, CORDAGE OR ROPE; MADE UP FISHING NETS AND OTHER MADE\nUP NETS, OF TEXTILE MATERIALS - OTHER : OF COTTON",
+        "hsn_code": "56089010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "KNOTTED NETTING OF TWINE, CORDAGE OR ROPE; MADE UP FISHING NETS AND OTHER MADE\nUP NETS, OF TEXTILE MATERIALS - OF MAN-MADE TEXTILE MATERIALS : OTHER",
+        "hsn_code": "56081900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "KNOTTED NETTING OF TWINE, CORDAGE OR ROPE; MADE UP FISHING NETS AND OTHER MADE\nUP NETS, OF TEXTILE MATERIALS - OF MAN-MADE TEXTILE MATERIALS : - MADE UP FISHING\nNETS : OTHER",
+        "hsn_code": "56081190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "KNOTTED NETTING OF TWINE, CORDAGE OR ROPE; MADE UP FISHING NETS AND OTHER MADE\nUP NETS, OF TEXTILE MATERIALS - OF MAN-MADE TEXTILE MATERIALS : - MADE UP FISHING\nNETS : MADE UP FISHING NETS OF NYLON",
+        "hsn_code": "56081110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "Yarn and strip and the like of heading no. 5404 or 5405, gimped (other than those of heading no. 5606 and gimped horsehair yarn); chenille yarn (including flock chenille yarn); loop wale-yarn",
+        "hsn_code": "5606",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GIMPED YARN, AND STRIP AND THE LIKE OF HEADING 5404 OR 5405, GIMPED (OTHER THAN\nTHOSE OF HEADING 5605 AND GIMPED HORSEHAIR YARN); CHENILLE YARN (INCLUDING FLOCK\nCHENILLE YARN); LOOP WALE-YARN - GIMPED YARN, AND STRIP AND THE LIKE OF HEADING 5404\nOR 5405, GIMPED (OTHER THAN THOSE OF HEADING 5605 AND GIMPED HORSEHAIR YARN);\nCHENILLE YARN (INCLUDING FLOCK CHENILLE YARN); LOOP WALE-YARN : OTHER",
+        "hsn_code": "56060090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GIMPED YARN, AND STRIP AND THE LIKE OF HEADING 5404 OR 5405, GIMPED (OTHER THAN\nTHOSE OF HEADING 5605 AND GIMPED HORSEHAIR YARN); CHENILLE YARN (INCLUDING FLOCK\nCHENILLE YARN); LOOP WALE-YARN - GIMPED YARN, AND STRIP AND THE LIKE OF HEADING 5404\nOR 5405, GIMPED (OTHER THAN THOSE OF HEADING 5605 AND GIMPED HORSEHAIR YARN);\nCHENILLE YARN (INCLUDING FLOCK CHENILLE YARN); LOOP WALE-YARN : TRIMMINGS, OF ZARI",
+        "hsn_code": "56060030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GIMPED YARN, AND STRIP AND THE LIKE OF HEADING 5404 OR 5405, GIMPED (OTHER THAN\nTHOSE OF HEADING 5605 AND GIMPED HORSEHAIR YARN); CHENILLE YARN (INCLUDING FLOCK\nCHENILLE YARN); LOOP WALE-YARN - GIMPED YARN, AND STRIP AND THE LIKE OF HEADING 5404\nOR 5405, GIMPED (OTHER THAN THOSE OF HEADING 5605 AND GIMPED HORSEHAIR YARN);\nCHENILLE YARN (INCLUDING FLOCK CHENILLE YARN); LOOP WALE-YARN : TRIMMINGS, OF MAN\nMADE FIBRES",
+        "hsn_code": "56060020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GIMPED YARN, AND STRIP AND THE LIKE OF HEADING 5404 OR 5405, GIMPED (OTHER THAN\nTHOSE OF HEADING 5605 AND GIMPED HORSEHAIR YARN); CHENILLE YARN (INCLUDING FLOCK\nCHENILLE YARN); LOOP WALE-YARN - GIMPED YARN, AND STRIP AND THE LIKE OF HEADING 5404\nOR 5405, GIMPED (OTHER THAN THOSE OF HEADING 5605 AND GIMPED HORSEHAIR YARN);\nCHENILLE YARN (INCLUDING FLOCK CHENILLE YARN); LOOP WALE-YARN : TRIMMINGS, OF\nCOTTON",
+        "hsn_code": "56060010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "METALLISED YARN, WHETHER OR NOT GIMPED BEING TEXTILE YARN, OR STRIP OR THE LIKE OF HEADING 5404 OR 5405, COMBINED WITH METAL IN THE FORM OF THREAD, STRIP OR POWDER OR COVERED WITH METAL",
+        "hsn_code": "5605",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "METALLISED YARN, WHETHER OR NOT GIMPED BEING TEXTILE YARN, OR STRIP OR THE LIKE OF\nHEADING 5404 OR 5405, COMBINED WITH METAL IN THE FORM OF THREAD, STRIP OR POWDER\nOR COVERED WITH METAL - METALLISED YARN, WHETHER OR NOT GIMPED, BEING TEXTILE\nYARN, OR STRIP OR THE LIKE OF HEADING 5404 OR 5405, COMBINED WITH METAL IN THE FORM\nOF THREAD, STRIP OR POWDER OR COVERED WITH METAL : OTHER",
+        "hsn_code": "56050090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "METALLISED YARN, WHETHER OR NOT GIMPED BEING TEXTILE YARN, OR STRIP OR THE LIKE OF\nHEADING 5404 OR 5405, COMBINED WITH METAL IN THE FORM OF THREAD, STRIP OR POWDER\nOR COVERED WITH METAL - METALLISED YARN, WHETHER OR NOT GIMPED, BEING TEXTILE\nYARN, OR STRIP OR THE LIKE OF HEADING 5404 OR 5405, COMBINED WITH METAL IN THE FORM\nOF THREAD, STRIP OR POWDER OR COVERED WITH METAL : IMITATION ZARI THREAD",
+        "hsn_code": "56050020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RUBBER THREAD & CORD, TEXTILE COVERED; TEXTILE YARN, AND STRIP AND THE LIKE OF HEADING 5404 OR 5405, IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR PLASTICS",
+        "hsn_code": "5604",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RUBBER THREAD AND CORD, TEXTILE COVERED; TEXTILE YARN, AND STRIP AND THE LIKE OF\nHEADING 5404 OR 5405, IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR\nPLASTICS - OTHER",
+        "hsn_code": "56049000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RUBBER THREAD AND CORD, TEXTILE COVERED; TEXTILE YARN, AND STRIP AND THE LIKE OF\nHEADING 5404 OR 5405, IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR\nPLASTICS - HIGH TENACITY YARN OF POLYESTERS, OF NYLON OR OTHER POLYAMIDES OR OF\nVISCOSE RAYON, IMPREGNATED OR COATED : OTHER",
+        "hsn_code": "56042090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RUBBER THREAD AND CORD, TEXTILE COVERED; TEXTILE YARN, AND STRIP AND THE LIKE OF\nHEADING 5404 OR 5405, IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR\nPLASTICS - HIGH TENACITY YARN OF POLYESTERS, OF NYLON OR OTHER POLYAMIDES OR OF\nVISCOSE RAYON, IMPREGNATED OR COATED : IMITATION GUT (NYLON) FOR RACKETS",
+        "hsn_code": "56042010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RUBBER THREAD AND CORD, TEXTILE COVERED; TEXTILE YARN, AND STRIP AND THE LIKE OF\nHEADING 5404 OR 5405, IMPREGNATED, COATED, COVERED OR SHEATHED WITH RUBBER OR\nPLASTICS - RUBBER THREAD AND CORD, TEXTILE COVERED",
+        "hsn_code": "56041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NONWOVENS, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED",
+        "hsn_code": "5603",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NONWOVENS, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED - OTHER :\nWEIGHING MORE THAN 150 G/M2",
+        "hsn_code": "56039400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NONWOVENS, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED - OTHER :\nWEIGHING MORE THAN 70 G/M2 BUT NOT MORE THAN 150 G/M2",
+        "hsn_code": "56039300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NONWOVENS, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED - OTHER :\nWEIGHING MORE THAN 25 G/M2 BUT NOT MORE THAN 70 G/M2",
+        "hsn_code": "56039200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NONWOVENS, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED - OTHER :\nWEIGHING NOT MORE THAN 25 G/M2",
+        "hsn_code": "56039100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NONWOVENS, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED - OF MAN\nMADE FILAMENTS : WEIGHING MORE THAN 150 G/M2",
+        "hsn_code": "56031400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NONWOVENS, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED - OF MAN\nMADE FILAMENTS : WEIGHING MORE THAN 70 G/M2 BUT NOT MORE THAN 150 G/M2",
+        "hsn_code": "56031300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NONWOVENS, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED - OF MAN\nMADE FILAMENTS : WEIGHING MORE THAN 25 G/M2 BUT NOT MORE THAN 70 G/M2",
+        "hsn_code": "56031200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NONWOVENS, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED - OF MAN\nMADE FILAMENTS : WEIGHING NOT MORE THAN 25 G/M2",
+        "hsn_code": "56031100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FELT, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED",
+        "hsn_code": "5602",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FELT,WHETHER OR NOT IMPREGNATED, COATED,COVERED OR LAMINATED -OTHER - - - OTHER",
+        "hsn_code": "56029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FELT,WHETHER OR NOT IMPREGNATED, COATED,COVERED OR LAMINATED -OTHER -OF\nRUBBERISED COIR NEEDLED FELT",
+        "hsn_code": "56029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FELT, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED - OTHER FELT, NOT\nIMPREGNATED, COATED, COVERED OR LAMINATED : - OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "56022990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FELT, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED - OTHER FELT, NOT\nIMPREGNATED, COATED, COVERED OR LAMINATED : - OF OTHER TEXTILE MATERIALS : OF JUTE\n(INCLUDING BLENDED OR UNION JUTE), OTHER THAN FOR MACHINERY",
+        "hsn_code": "56022920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FELT, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED - OTHER FELT, NOT\nIMPREGNATED, COATED, COVERED OR LAMINATED : - OF OTHER TEXTILE MATERIALS : FOR\nMACHINES OTHER THAN COTTON MACHINERY",
+        "hsn_code": "56022910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FELT, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED - OTHER FELT, NOT\nIMPREGNATED, COATED, COVERED OR LAMINATED : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "56022100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FELT, WHETHER OR NOT IMPREGNATED, COATED, COVERED OR LAMINATED NEEDLELOOM FELT\nAND STITCH-BONDED FIBRE FABRICS",
+        "hsn_code": "56021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WADDING OF TEXTILE MATERIALS AND ARTICLES THEREOF; TEXTILE FIBRES, NOT EXCEEDING 5 MM IN LENGTH (FLOCK), TEXTILE DUST AND MILL NEPS",
+        "hsn_code": "5601",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WADDING OF TEXTILE MATERIALS AND ARTICLES THEREOF; TEXTILE FIBRES, NOT EXCEEDING 5\nMM IN LENGTH (FLOCK), TEXTILE DUST AND MILL NEPS TEXTILE FLOCK AND DUST AND MILL\nNEPS",
+        "hsn_code": "56013000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WADDING OF TEXTILE MATERIALS AND ARTICLES THEREOF; TEXTILE FIBRES, NOT EXCEEDING 5\nMM IN LENGTH (FLOCK), TEXTILE DUST AND MILL NEPS - WADDING; OTHER ARTICLES OF\nWADDING : OTHER",
+        "hsn_code": "56012900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WADDING OF TEXTILE MATERIALS AND ARTICLES THEREOF; TEXTILE FIBRES, NOT EXCEEDING 5\nMM IN LENGTH (FLOCK), TEXTILE DUST AND MILL NEPS - WADDING; OTHER ARTICLES OF\nWADDING : OF COTTON : OTHER",
+        "hsn_code": "56012190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WADDING OF TEXTILE MATERIALS AND ARTICLES THEREOF; TEXTILE FIBRES, NOT EXCEEDING 5\nMM IN LENGTH (FLOCK), TEXTILE DUST AND MILL NEPS - WADDING; OTHER ARTICLES OF\nWADDING : OF COTTON : ABSORBENT COTTON WOOL",
+        "hsn_code": "56012110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WADDING OF TEXTILE MATERIALS AND ARTICLES THEREOF; TEXTILE FIBRES, NOT EXCEEDING 5\nMM IN LENGTH (FLOCK), TEXTILE DUST AND MILL NEPS SANITARY TOWELS AND TAMPONS,\nNAPKINS AND NAPKIN LINERS FOR BABIES AND SIMILAR SANITARY ARTICLES, OF WADDING",
+        "hsn_code": "56011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEWING THREAD OF MAN-MADE STAPLE FIBRES, WHETHER OR NOT PUT UP FOR RETAIL SALE",
+        "hsn_code": "5508",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEWING THREAD OF MAN-MADE STAPLE FIBRES, WHETHER OR NOT PUT UP FOR RETAIL SALE -\nOF ARTIFICIAL STAPLE FIBRES",
+        "hsn_code": "55082000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEWING THREAD OF MAN-MADE STAPLE FIBRES, WHETHER OR NOT PUT UP FOR RETAIL SALE -\nOF SYNTHETIC STAPLE FIBRES",
+        "hsn_code": "55081000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WASTE (INCLUDING NOILS, YARN WASTE AND GARNETTED STOCK) OF MAN-MADE FIBRES",
+        "hsn_code": "5505",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE (INCLUDING NOILS, YARN WASTE AND GARNETTED STOCK) OF MAN-MADE FIBRES OF\nARTIFICIAL FIBRES",
+        "hsn_code": "55052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE (INCLUDING NOILS, YARN WASTE AND GARNETTED STOCK) OF MAN-MADE FIBRES - OF\nSYNTHETIC FIBERS : OTHER",
+        "hsn_code": "55051090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE (INCLUDING NOILS, YARN WASTE AND GARNETTED STOCK) OF MAN-MADE FIBRES - OF\nSYNTHETIC FIBERS : OF ACRYLIC",
+        "hsn_code": "55051010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SEWING THREAD OF MAN-MADE FILAMENTS, WHETHER OR NOT PUT UP FOR RETAIL SALE",
+        "hsn_code": "5401",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEWING THREAD OF MAN-MADE FILAMENTS, WHETHER OR NOT PUT UP FOR RETAIL SALE OF\nARTIFICIAL FILAMENTS",
+        "hsn_code": "54012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEWING THREAD OF MAN-MADE FILAMENTS, WHETHER OR NOT PUT UP FOR RETAIL SALE OF\nSYNTHETIC FILAMENTS",
+        "hsn_code": "54011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRUE HEMP (CANNABIS SATIVA L ), RAW OR PROCESSED BUT NOT SPUN; TOW AND WASTE OF TRUE HEMP (INCLUDING YARN WASTE AND GARNETTED STOCK)",
+        "hsn_code": "5302",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRUE HEMP (CANNABIS SATIVA L ), RAW OR PROCESSED BUT NOT SPUN; TOW AND WASTE OF\nTRUE HEMP (INCLUDING YARN WASTE AND GARNETTED STOCK) OTHER",
+        "hsn_code": "53029000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TRUE HEMP (CANNABIS SATIVA L ), RAW OR PROCESSED BUT NOT SPUN; TOW AND WASTE OF\nTRUE HEMP (INCLUDING YARN WASTE AND GARNETTED STOCK) TRUE HEMP, RAW OR RETTED",
+        "hsn_code": "53021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FLAX, RAW OR PROCESSED BUT NOT SPUN; FLAX TOW AND WASTE (INCLUDING YARN WASTE AND GARNETTED STOCK)",
+        "hsn_code": "5301",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FLAX, RAW OR PROCESSED BUT NOT SPUN; FLAX TOW AND WASTE (INCLUDING YARN WASTE\nAND GARNETTED STOCK) FLAX TOW AND WASTE",
+        "hsn_code": "53013000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FLAX, RAW OR PROCESSED BUT NOT SPUN; FLAX TOW AND WASTE (INCLUDING YARN WASTE\nAND GARNETTED STOCK) FLAX, BROKEN, SCUTCHED, HACKLED OR OTHERWISE PROCESSED, BUT\nNOT SPUN :OTHER",
+        "hsn_code": "53012900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FLAX, RAW OR PROCESSED BUT NOT SPUN; FLAX TOW AND WASTE (INCLUDING YARN WASTE\nAND GARNETTED STOCK) FLAX, BROKEN, SCUTCHED, HACKLED OR OTHERWISE PROCESSED, BUT\nNOT SPUN :BROKEN OR SCUTCHED",
+        "hsn_code": "53012100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FLAX, RAW OR PROCESSED BUT NOT SPUN; FLAX TOW AND WASTE (INCLUDING YARN WASTE\nAND GARNETTED STOCK) FLAX, RAW OR RETTED",
+        "hsn_code": "53011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COTTON SEWING THREAD, WHETHER OR NOT PUT UP FOR RETAIL SALE",
+        "hsn_code": "5204",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COTTON SEWING THREAD, WHETHER OR NOT PUT UP FOR RETAIL SALE - PUT UP FOR RETAIL\nSALE : OTHER",
+        "hsn_code": "52042090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COTTON SEWING THREAD, WHETHER OR NOT PUT UP FOR RETAIL SALE - PUT UP FOR RETAIL\nSALE : COTTON SEWING THREAD, NOT CONTAINING ANY SYNTHETIC STAPLE FIBRE",
+        "hsn_code": "52042040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COTTON SEWING THREAD, WHETHER OR NOT PUT UP FOR RETAIL SALE - PUT UP FOR RETAIL\nSALE : EMBROIDERY COTTON THREAD",
+        "hsn_code": "52042030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COTTON SEWING THREAD, WHETHER OR NOT PUT UP FOR RETAIL SALE - PUT UP FOR RETAIL\nSALE : COTTON THREAD, DARNING",
+        "hsn_code": "52042020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COTTON SEWING THREAD, WHETHER OR NOT PUT UP FOR RETAIL SALE - PUT UP FOR RETAIL\nSALE : COTTON THREAD, SEWING, CONTAINING ANY SYNTHETIC STAPLE FIBRE",
+        "hsn_code": "52042010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COTTON SEWING THREAD, WHETHER OR NOT PUT UP FOR RETAIL SALE - NOT PUT UP FOR\nRETAIL SALE : OTHER",
+        "hsn_code": "52041900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COTTON SEWING THREAD, WHETHER OR NOT PUT UP FOR RETAIL SALE - NOT PUT UP FOR\nRETAIL SALE : CONTAINING 85% OR MORE BY WEIGHT OF COTTON : OTHER",
+        "hsn_code": "52041190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COTTON SEWING THREAD, WHETHER OR NOT PUT UP FOR RETAIL SALE - NOT PUT UP FOR\nRETAIL SALE : CONTAINING 85% OR MORE BY WEIGHT OF COTTON : COTTON SEWING THREAD,\nNOT CONTAINING ANY SYNTHETIC STAPLE FIBRE",
+        "hsn_code": "52041140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COTTON SEWING THREAD, WHETHER OR NOT PUT UP FOR RETAIL SALE - NOT PUT UP FOR\nRETAIL SALE : CONTAINING 85% OR MORE BY WEIGHT OF COTTON : EMBROIDERY COTTON\nTHREAD",
+        "hsn_code": "52041130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COTTON SEWING THREAD, WHETHER OR NOT PUTUP FOR RETAIL SALE - NOT PUT UP FOR RETAIL\nSALE : CONTAINING 85% OR MORE BY WEIGHT OF COTTON : COTTON THREAD, DARNING",
+        "hsn_code": "52041120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COTTON SEWING THREAD, WHETHER OR NOT PUTUP FOR RETAIL SALE - NOT PUT UP FOR RETAIL\nSALE : CONTAINING 85% OR MORE BY WEIGHT OFCOTTON : COTTON THREAD,\nSEWING,CONTAINING ANY SYNTHETIC STAPLE FIBRE",
+        "hsn_code": "52041110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOOL AND FINE OR COARSE ANIMAL HAIR, CARDED OR COMBED (INCLUDING COMBED WOOL IN FRAGMENTS)",
+        "hsn_code": "5105",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOOL AND FINE OR COARSE ANIMAL HAIR, CARDED OR COMBED (INCLUDING COMBED WOOL\nIN FRAGMENTS) COARSE ANIMAL HAIR, CARDED OR COMBED",
+        "hsn_code": "51054000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOOL AND FINE OR COARSE ANIMAL HAIR, CARDED OR COMBED (INCLUDING COMBED WOOL\nIN FRAGMENTS) - FINE ANIMAL HAIR, CARDED OR COMBED : OTHER",
+        "hsn_code": "51053900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOOL AND FINE OR COARSE ANIMAL HAIR, CARDED OR COMBED (INCLUDING COMBED WOOL\nIN FRAGMENTS) - FINE ANIMAL HAIR, CARDED OR COMBED : OF KASHMIR (CASHMERE) GOATS",
+        "hsn_code": "51053100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OOL AND FINE OR COARSE ANIMAL HAIR, CARDED OR COMBED (INCLUDING COMBED WOOL IN\nFRAGMENTS) - WOOL TOPS AND OTHER COMBED WOOL : OTHER : OTHER",
+        "hsn_code": "51052990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOOL AND FINE OR COARSE ANIMAL HAIR, CARDED OR COMBED (INCLUDING COMBED WOOL\nIN FRAGMENTS) - WOOL TOPS AND OTHER COMBED WOOL : OTHER : WOOL TOPS",
+        "hsn_code": "51052910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOOL AND FINE OR COARSE ANIMAL HAIR, CARDED OR COMBED (INCLUDING COMBED WOOL\nIN FRAGMENTS) - WOOL TOPS AND OTHER COMBED WOOL : COMBED WOOL IN FRAGMENTS",
+        "hsn_code": "51052100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOOL AND FINE OR COARSE ANIMAL HAIR, CARDED OR COMBED (INCLUDING COMBED WOOL\nIN FRAGMENTS) CARDED WOOL",
+        "hsn_code": "51051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GARNETTED STOCK OF WOOL OR OF FINE OR COARSE ANIMAL HAIR",
+        "hsn_code": "5104",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GARNETTED STOCK OF WOOL OR OF FINE OR COARSE ANIMAL HAIR - GARNETTED STOCK OF\nWOOL OR OF FINE OR COARSE ANIMAL HAIR : OTHER",
+        "hsn_code": "51040090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GARNETTED STOCK OF WOOL OR OF FINE OR COARSE ANIMAL HAIR - GARNETTED STOCK OF\nWOOL OR OF FINE OR COARSE ANIMAL HAIR : SHODDY WOOL",
+        "hsn_code": "51040010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SILK OR OF SILK WASTE",
+        "hsn_code": "5007",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SILK OR OF SILK WASTE - OTHER FABRICS",
+        "hsn_code": "50079090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER HANDLOOM WOVEN FABRICS",
+        "hsn_code": "50079010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SILK OR OF SILK WASTE OTHER FABRICS, CONTAINING 85 PERCENT OR\nMORE BY WEIGHT OF SILK OR OF SILK WASTE OTHER THAN NOIL SILK: OTHER",
+        "hsn_code": "50072090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SILK OR OF SILK WASTE OTHER FABRICS, CONTAINING 85 PERCENT OR\nMORE BY WEIGHT OF SILK OR OF SILK WASTE OTHER THAN NOIL SILK: SAREES",
+        "hsn_code": "50072010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SILK OR OF SILK WASTE FABRICS OF NOIL SILK",
+        "hsn_code": "50071000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER PRINTED MATTER, INCLUDING PRINTED PICTURES AND PHOTOGRAPHS",
+        "hsn_code": "4911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PRINTED MATTER, INCLUDING PRINTED PICTURES AND PHOTOGRAPHS - OTHER : OTHER :\nOTHER",
+        "hsn_code": "49119990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PRINTED MATTER, INCLUDING PRINTED PICTURES AND PHOTOGRAPHS - OTHER : OTHER :\nPLAN AND DRAWINGS FOR ARCHITECTURAL ENGINEERING, INDUSTRIAL, COMMERCIAL,\nTOPOGRAPHICAL OR SIMILAR PURPOSES REPRODUCED WITH THE AID OF COMPUTER OR ANY\nOTHER DEVICES",
+        "hsn_code": "49119920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PRINTED MATTER, INCLUDING PRINTED PICTURES AND PHOTOGRAPHS - OTHER : OTHER :\nHARD COPY (PRINTED) OF COMPUTER SOFTWARE",
+        "hsn_code": "49119910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PRINTED MATTER, INCLUDING PRINTED PICTURES AND PHOTOGRAPHS - OTHER :\nPICTURES, DESIGNS AND PHOTOGRAPHS",
+        "hsn_code": "49119100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PRINTED MATTER, INCLUDING PRINTED PICTURES AND PHOTOGRAPHS TRADE\nADVERTISING MATERIAL, COMMERCIAL CATALOGUES AND THE LIKE : OTHER",
+        "hsn_code": "49111090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PRINTED MATTER, INCLUDING PRINTED PICTURES AND PHOTOGRAPHS TRADE\nADVERTISING MATERIAL, COMMERCIAL CATALOGUES AND THE LIKE : PRINTED INLAY CARDS",
+        "hsn_code": "49111030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PRINTED MATTER, INCLUDING PRINTED PICTURES AND PHOTOGRAPHS TRADE\nADVERTISING MATERIAL, COMMERCIAL CATALOGUES AND THE LIKE : COMMERCIAL\nCATALOGUES",
+        "hsn_code": "49111020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PRINTED MATTER, INCLUDING PRINTED PICTURES AND PHOTOGRAPHS TRADE\nADVERTISING MATERIAL, COMMERCIAL CATALOGUES AND THE LIKE : POSTERS, PRINTED",
+        "hsn_code": "49111010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CALENDARS OF ANY KIND, PRINTED, INCLUDING CALENDAR BLOCKS",
+        "hsn_code": "4910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CALENDARS OF ANY KIND, PRINTED, INCLUDING CALENDAR BLOCKS - CALENDARS OF ANY KIND,\nPRINTED, INCLUDING CALENDAR BLOCKS : OTHER",
+        "hsn_code": "49100090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CALENDARS OF ANY KIND, PRINTED, INCLUDING CALENDAR BLOCKS - CALENDARS OF ANY KIND,\nPRINTED, INCLUDING CALENDAR BLOCKS : ADVERTISING CALENDAR",
+        "hsn_code": "49100010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTED OR ILLUSTRATED POSTCARDS; PRINTED CARDS BEARING PERSONAL GREETINGS, MESSAGES OR ANNOUNCEMENTS, WHETHER OR NOT ILLUSTRATED, WITH OR WITHOUT ENVELOPES OR TRIMMINGS",
+        "hsn_code": "4909",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTED OR ILLUSTRATED POSTCARDS; PRINTED CARDS BEARING PERSONAL GREETINGS,\nMESSAGES OR ANNOUNCEMENTS, WHETHER OR NOT ILLUSTRATED, WITH OR WITHOUT\nENVELOPES OR TRIMMINGS PRINTED OR ILLUSTRATED POSTCARDS; PRINTED CARDS BEARING\nPERSONAL GREETINGS, MESSAGES OR ANNOUNCEMENTS, WHETHER OR NOT ILLUSTRATED,\nWITH OR WITHOUT ENVELOPES OR TRIMMINGS : OTHER",
+        "hsn_code": "49090090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTED OR ILLUSTRATED POSTCARDS; PRINTED CARDS BEARING PERSONAL GREETINGS,\nMESSAGES OR ANNOUNCEMENTS, WHETHER OR NOT ILLUSTRATED, WITH OR WITHOUT\nENVELOPES OR TRIMMINGS PRINTED OR ILLUSTRATED POSTCARDS; PRINTED CARDS BEARING\nPERSONAL GREETINGS, MESSAGES OR ANNOUNCEMENTS, WHETHER OR NOT ILLUSTRATED,\nWITH OR WITHOUT ENVELOPES OR TRIMMINGS : GREETING OR WEDDING CARDS",
+        "hsn_code": "49090010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSFERS (DECALCOMANIAS)",
+        "hsn_code": "4908",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSFERS (DECALCOMANIAS) OTHER",
+        "hsn_code": "49089000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRANSFERS (DECALCOMANIAS) TRANSFERS (DECALCOMANIAS), VITRIFIABLE",
+        "hsn_code": "49081000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - GUMMED OR ADHESIVE PAPER, IN STRIPS OR ROLLS : SELF\nADHESIVE",
+        "hsn_code": "48231200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - GUMMED OR ADHESIVE PAPER, IN STRIPS OR ROLLS : OTHER",
+        "hsn_code": "48231900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - FILTER PAPER AND PAPERBOARD",
+        "hsn_code": "48232000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - ROLLS, SHEETS AND DIALS, PRINTED FOR SELF-RECORDING\nAPPARATUS",
+        "hsn_code": "48234000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - TRAYS, DISHES, PLATES, CUPS AND THE LIKE, OF PAPER AND\nPAPERBOARD",
+        "hsn_code": "48236000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - TRAYS,DISHES,PLATES, CUPS AND THE LIKE, OF PAPER OR\nPAPERBOARD : OF BAMBOO",
+        "hsn_code": "48236100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - TRAYS,DISHES,PLATES, CUPS AND THE LIKE, OF PAPER OR\nPAPERBOARD : OTHER",
+        "hsn_code": "48236900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - MOULDED OR PRESSED ARTICLE OF PAPER PULP : PAPER PULP\nMOULDED TRAYS",
+        "hsn_code": "48237010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - MOULDED OR PRESSED ARTICLE OF PAPER PULP : WOOD PULP\nBOARD",
+        "hsn_code": "48237020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - MOULDED OR PRESSED ARTICLE OF PAPER PULP : ARTICLES\nMADE OF PAPER MACHE OTHER THAN ARTWARE AND MOULDED OR PRESSED GOODS OF WOOD\nPULP",
+        "hsn_code": "48237030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - MOULDED OR PRESSED ARTICLE OF PAPER PULP : OTHER",
+        "hsn_code": "48237090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - OTHER : BRAILLE PAPER, CELLULOSE IN SOLE BOARD OR SHEET;\nPACKING AND WRAPPING PAPER; PAPER FOR CIGARETTE FILTER TIPS; PAPER CONE FOR LOUD\nSPEAKER; PATTERNS MADE OF PAPERS FOR LEATHER FOOTWEAR, LEATHER GARMENTS AND\nGOODS; PATTERNS MADE OF PAPER FOR ARTICLES OF APPAREL AND CLOTHING ACCESSORIES,\nPRODUCTS CONSISTING OF SHEETS OF PAPER OR PAPERBOARD, IMPREGNATED, COATED OR\nCOVERED WITH PLASTICS (INCLUDING THERMOSET RESINS OR MIXTURES THEREOF OR\nCHEMICAL FORMULATIONS, CONTAINING MELAMINE PHENOL OR UREA FORMALDEHYDE WITH\nOR WITHOUT CURING AGENTS OR CATALYSTS), COMPRESSED TOGETHER IN ONE OR MORE\nOPERATIONS; DECORATIVE LAMINATES : BRAILLE PAPER",
+        "hsn_code": "48239011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - OTHER : BRAILLE PAPER, CELLULOSE IN SOLE BOARD OR SHEET;\nPACKING AND WRAPPING PAPER; PAPER FOR CIGARETTE FILTER TIPS; PAPER CONE FOR LOUD\nSPEAKER; PATTERNS MADE OF PAPERS FOR LEATHER FOOTWEAR, LEATHER GARMENTS AND\nGOODS; PATTERNS MADE OF PAPER FOR ARTICLES OF APPAREL AND CLOTHING ACCESSORIES,\nPRODUCTS CONSISTING OF SHEETS OF PAPER OR PAPERBOARD, IMPREGNATED, COATED OR\nCOVERED WITH PLASTICS (INCLUDING THERMOSET RESINS OR MIXTURES THEREOF OR\nCHEMICAL FORMULATIONS, CONTAINING MELAMINE PHENOL OR UREA FORMALDEHYDE WITH\nOR WITHOUT CURING AGENTS OR CATALYSTS), COMPRESSED TOGETHER IN ONE OR MORE\nOPERATIONS; DECORATIVE LAMINATES : CELLULOSE IN SOLE BOARD OR SHEET",
+        "hsn_code": "48239012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - OTHER : BRAILLE PAPER, CELLULOSE IN SOLE BOARD OR SHEET;\nPACKING AND WRAPPING PAPER; PAPER FOR CIGARETTE FILTER TIPS; PAPER CONE FOR LOUD\nSPEAKER; PATTERNS MADE OF PAPERS FOR LEATHER FOOTWEAR, LEATHER GARMENTS AND\nGOODS; PATTERNS MADE OF PAPER FOR ARTICLES OF APPAREL AND CLOTHING ACCESSORIES,\nPRODUCTS CONSISTING OF SHEETS OF PAPER OR PAPERBOARD, IMPREGNATED, COATED OR\nCOVERED WITH PLASTICS (INCLUDING THERMOSET RESINS OR MIXTURES THEREOF OR\nCHEMICAL FORMULATIONS, CONTAINING MELAMINE PHENOL OR UREA FORMALDEHYDE WITH\nOR WITHOUT CURING AGENTS OR CATALYSTS), COMPRESSED TOGETHER IN ONE OR MORE\nOPERATIONS; DECORATIVE LAMINATES : PACKING AND WRAPPING PAPER",
+        "hsn_code": "48239013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - OTHER : BRAILLE PAPER, CELLULOSE IN SOLE BOARD OR SHEET;\nPACKING AND WRAPPING PAPER; PAPER FOR CIGARETTE FILTER TIPS; PAPER CONE FOR LOUD\nSPEAKER; PATTERNS MADE OF PAPERS FOR LEATHER FOOTWEAR, LEATHER GARMENTS AND\nGOODS; PATTERNS MADE OF PAPER FOR ARTICLES OF APPAREL AND CLOTHING ACCESSORIES,\nPRODUCTS CONSISTING OF SHEETS OF PAPER OR PAPERBOARD, IMPREGNATED, COATED OR\nCOVERED WITH PLASTICS (INCLUDING THERMOSET RESINS OR MIXTURES THEREOF OR\nCHEMICAL FORMULATIONS, CONTAINING MELAMINE PHENOL OR UREA FORMALDEHYDE WITH\nOR WITHOUT CURING AGENTS OR CATALYSTS), COMPRESSED TOGETHER IN ONE OR MORE\nOPERATIONS; DECORATIVE LAMINATES : PAPER FOR CIGARETTE FILTER TIPS",
+        "hsn_code": "48239014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - OTHER : BRAILLE PAPER, CELLULOSE IN SOLE BOARD OR SHEET;\nPACKING AND WRAPPING PAPER; PAPER FOR CIGARETTE FILTER TIPS; PAPER CONE FOR LOUD\nSPEAKER; PATTERNS MADE OF PAPERS FOR LEATHER FOOTWEAR, LEATHER GARMENTS AND\nGOODS; PATTERNS MADE OF PAPER FOR ARTICLES OF APPAREL AND CLOTHING ACCESSORIES,\nPRODUCTS CONSISTING OF SHEETS OF PAPER OR PAPERBOARD, IMPREGNATED, COATED OR\nCOVERED WITH PLASTICS (INCLUDING THERMOSET RESINS OR MIXTURES THEREOF OR\nCHEMICAL FORMULATIONS, CONTAINING MELAMINE PHENOL OR UREA FORMALDEHYDE WITH\nOR WITHOUT CURING AGENTS OR CATALYSTS), COMPRESSED TOGETHER IN ONE OR MORE\nOPERATIONS; DECORATIVE LAMINATES : PAPER CONE FOR LOUD SPEAKER",
+        "hsn_code": "48239015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - OTHER : BRAILLE PAPER, CELLULOSE IN SOLE BOARD OR SHEET;\nPACKING AND WRAPPING PAPER; PAPER FOR CIGARETTE FILTER TIPS; PAPER CONE FOR LOUD\nSPEAKER; PATTERNS MADE OF PAPERS FOR LEATHER FOOTWEAR, LEATHER GARMENTS AND\nGOODS; PATTERNS MADE OF PAPER FOR ARTICLES OF APPAREL AND CLOTHING ACCESSORIES,\nPRODUCTS CONSISTING OF SHEETS OF PAPER OR PAPERBOARD, IMPREGNATED, COATED OR\nCOVERED WITH PLASTICS (INCLUDING THERMOSET RESINS OR MIXTURES THEREOF OR\nCHEMICAL FORMULATIONS, CONTAINING MELAMINE PHENOL OR UREA FORMALDEHYDE WITH\nOR WITHOUT CURING AGENTS OR CATALYSTS), COMPRESSED TOGETHER IN ONE OR MORE\nOPERATIONS; DECORATIVE LAMINATES : PATTERNS MADE OF PAPERS FOR LEATHER FOOTWEAR,\nLEATHER GARMENTS AND GOODS",
+        "hsn_code": "48239016",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - OTHER : BRAILLE PAPER, CELLULOSE IN SOLE BOARD OR SHEET;\nPACKING AND WRAPPING PAPER; PAPER FOR CIGARETTE FILTER TIPS; PAPER CONE FOR LOUD\nSPEAKER; PATTERNS MADE OF PAPERS FOR LEATHER FOOTWEAR, LEATHER GARMENTS AND\nGOODS; PATTERNS MADE OF PAPER FOR ARTICLES OF APPAREL AND CLOTHING ACCESSORIES,\nPRODUCTS CONSISTING OF SHEETS OF PAPER OR PAPERBOARD, IMPREGNATED, COATED OR\nCOVERED WITH PLASTICS (INCLUDING THERMOSET RESINS OR MIXTURES THEREOF OR\nCHEMICAL FORMULATIONS, CONTAINING MELAMINE PHENOL OR UREA FORMALDEHYDE WITH\nOR WITHOUT CURING AGENTS OR CATALYSTS), COMPRESSED TOGETHER IN ONE OR MORE\nOPERATIONS; DECORATIVE LAMINATES : PATTERNS MADE OF PAPER FOR ARTICLES OF APPAREL\nAND CLOTHING ACCESSORIES",
+        "hsn_code": "48239017",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - OTHER : BRAILLE PAPER, CELLULOSE IN SOLE BOARD OR SHEET;\nPACKING AND WRAPPING PAPER; PAPER FOR CIGARETTE FILTER TIPS; PAPER CONE FOR LOUD\nSPEAKER; PATTERNS MADE OF PAPERS FOR LEATHER FOOTWEAR, LEATHER GARMENTS AND\nGOODS; PATTERNS MADE OF PAPER FOR ARTICLES OF APPAREL AND CLOTHING ACCESSORIES,\nPRODUCTS CONSISTING OF SHEETS OF PAPER OR PAPERBOARD, IMPREGNATED, COATED OR\nCOVERED WITH PLASTICS (INCLUDING THERMOSET RESINS OR MIXTURES THEREOF OR\nCHEMICAL FORMULATIONS, CONTAINING MELAMINE PHENOL OR UREA FORMALDEHYDE WITH\nOR WITHOUT CURING AGENTS OR CATALYSTS), COMPRESSED TOGETHER IN ONE OR MORE\nOPERATIONS; DECORATIVE LAMINATES : PRODUCTS CONSISTING OF SHEETS OF PAPER OR\nPAPERBOARD, IMPREGNATED, COATED OR COVERED WITH PLASTICS (INCLUDING THERMOSET\nRESINS OR MIXTURES THEREOF OR CHEMICAL FORMULATIONS CONTAINING MELAMINE,\nPHENOL OR UREA FORMALDEHYDE WITH OR WITHOUT CURING AGENTS OR CATALYSTS),\nCOMPRESSED TOGETHER IN ONE OR MORE OPERATIONS",
+        "hsn_code": "48239018",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - OTHER : BRAILLE PAPER, CELLULOSE IN SOLE BOARD OR SHEET;\nPACKING AND WRAPPING PAPER; PAPER FOR CIGARETTE FILTER TIPS; PAPER CONE FOR LOUD\nSPEAKER; PATTERNS MADE OF PAPERS FOR LEATHER FOOTWEAR, LEATHER GARMENTS AND\nGOODS; PATTERNS MADE OF PAPER FOR ARTICLES OF APPAREL AND CLOTHING ACCESSORIES,\nPRODUCTS CONSISTING OF SHEETS OF PAPER OR PAPERBOARD, IMPREGNATED, COATED OR\nCOVERED WITH PLASTICS (INCLUDING THERMOSET RESINS OR MIXTURES THEREOF OR\nCHEMICAL FORMULATIONS, CONTAINING MELAMINE PHENOL OR UREA FORMALDEHYDE WITH\nOR WITHOUT CURING AGENTS OR CATALYSTS), COMPRESSED TOGETHER IN ONE OR MORE\nOPERATIONS; DECORATIVE LAMINATES : DECORATIVE LAMINATES",
+        "hsn_code": "48239019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - OTHER : PRE-PUNCHED CARDS; MONOTYPE AND NEWSTAPE\nPAPER IN STRIPS WITH PERFORATED EDGES, NOT EXCEEDING 15 CM IN WIDTH; TYPEWRITING\nPAPER CUT TO SIZE AND THE LIKE : PRE-PUNCHED CARDS",
+        "hsn_code": "48239021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - OTHER : PRE-PUNCHED CARDS; MONOTYPE AND NEWSTAPE\nPAPER IN STRIPS WITH PERFORATED EDGES, NOT EXCEEDING 15 CM IN WIDTH; TYPEWRITING\nPAPER CUT TO SIZE AND THE LIKE : MONOTYPE AND NEWSTAPE PAPER IN STRIPS WITH\nPERFORATED EDGES, NOT EXCEEDING 15CM IN WIDTH",
+        "hsn_code": "48239022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - OTHER : PRE-PUNCHED CARDS; MONOTYPE AND NEWSTAPE\nPAPER IN STRIPS WITH PERFORATED EDGES, NOT EXCEEDING 15 CM IN WIDTH; TYPEWRITING\nPAPER CUT TO SIZE AND THE LIKE : TYPEWRITING PAPER CUT TO SIZE",
+        "hsn_code": "48239023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - OTHER : PLAIN OR EMBOSSED SEALS MADE OF PAPER,\nLAMINATED PAPER OR PAPER GASKETS",
+        "hsn_code": "48239030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO\nSIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING\nOR WEBS OF CELLULOSE FIBRES - OTHER : OTHER",
+        "hsn_code": "48239090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, CUT TO SIZE OR SHAPE; OTHER ARTICLES OF PAPER PULP, PAPER, PAPERBOARD, CELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES",
+        "hsn_code": "4823",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BOBBINS, SPOOLS, COPS AND SIMILAR SUPPORTS OF PAPER PULP, PAPER OR PAPERBOARD\n(WHETHER OR NOT PERFORATED OR HARDENED) - OF A KIND USED FOR WINDING TEXTILE YARN",
+        "hsn_code": "48221000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BOBBINS, SPOOLS, COPS AND SIMILAR SUPPORTS OF PAPER PULP, PAPER OR PAPERBOARD\n(WHETHER OR NOT PERFORATED OR HARDENED) - OTHER : PAPER TUBES",
+        "hsn_code": "48229010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BOBBINS, SPOOLS, COPS AND SIMILAR SUPPORTS OF PAPER PULP, PAPER OR PAPERBOARD\n(WHETHER OR NOT PERFORATED OR HARDENED) - OTHER : OTHER",
+        "hsn_code": "48229090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BOBBINS, SPOOLS, COPS AND SIMILAR SUPPORTS OF PAPER PULP, PAPER OR PAPERBOARD (WHETHER OR NOT PERFORATED OR HARDENED)",
+        "hsn_code": "4822",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER OR PAPERBOARD LABELS OF ALL KINDS, WHETHER OR NOT PRINTED - PRINTED : PAPER\nTAGS",
+        "hsn_code": "48211010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER OR PAPERBOARD LABELS OF ALL KINDS, WHETHER OR NOT PRINTED - PRINTED : LABELS",
+        "hsn_code": "48211020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER OR PAPERBOARD LABELS OF ALL KINDS, WHETHER OR NOT PRINTED - PRINTED : OTHER",
+        "hsn_code": "48211090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER OR PAPERBOARD LABELS OF ALL KINDS, WHETHER OR NOT PRINTED - OTHER: LABELS",
+        "hsn_code": "48219010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER OR PAPERBOARD LABELS OF ALL KINDS, WHETHER OR NOT PRINTED - OTHER: OTHER",
+        "hsn_code": "48219090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER OR PAPERBOARD LABELS OF ALL KINDS, WHETHER OR NOT PRINTED",
+        "hsn_code": "4821",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REGISTERS, ACCOUNT BOOKS, NOTE BOOKS, ORDER BOOKS, RECEIPT BOOKS, LETTER PADS,\nMEMORANDUM PADS, DIARIES AND SIMILAR ARTICLES, EXCISE BOOKS, BLOTTING-PADS,\nBINDERS (LOOSE-LEAF OR OTHER), FOLDERS, FILE COVERS, MANIFOLD BUSINESS FORMS,\nINTERLEAVED CARBON SETS AND OTHER ARTICLES OF STATIONERY, OF PAPER OR PAPERBOARD;\nALBUMS FOR SAMPLES OR FOR COLLECTIONS AND BOOK COVERS, OF PAPER OR PAPERBOARD\nREGISTERS, ACCOUNT BOOKS, NOTE BOOKS, ORDER BOOKS, RECEIPT BOOKS, LETTER PADS,\nMEMORANDUM PADS, DIARIES AND SIMILAR ARTICLES : REGISTERS, ACCOUNT BOOKS",
+        "hsn_code": "48201010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REGISTERS, ACCOUNT BOOKS, NOTE BOOKS, ORDER BOOKS, RECEIPT BOOKS, LETTER PADS,\nMEMORANDUM PADS, DIARIES AND SIMILAR ARTICLES, EXCISE BOOKS, BLOTTING-PADS,\nBINDERS (LOOSE-LEAF OR OTHER), FOLDERS, FILE COVERS, MANIFOLD BUSINESS FORMS,\nINTERLEAVED CARBON SETS AND OTHER ARTICLES OF STATIONERY, OF PAPER OR PAPERBOARD;\nALBUMS FOR SAMPLES OR FOR COLLECTIONS AND BOOK COVERS, OF PAPER OR PAPERBOARD\nREGISTERS, ACCOUNT BOOKS, NOTE BOOKS, ORDER BOOKS, RECEIPT BOOKS, LETTER PADS,\nMEMORANDUM PADS, DIARIES AND SIMILAR ARTICLES : LETTER PADS",
+        "hsn_code": "48201020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REGISTERS, ACCOUNT BOOKS, NOTE BOOKS, ORDER BOOKS, RECEIPT BOOKS, LETTER PADS,\nMEMORANDUM PADS, DIARIES AND SIMILAR ARTICLES, EXCISE BOOKS, BLOTTING-PADS,\nBINDERS (LOOSE-LEAF OR OTHER), FOLDERS, FILE COVERS, MANIFOLD BUSINESS FORMS,\nINTERLEAVED CARBON SETS AND OTHER ARTICLES OF STATIONERY, OF PAPER OR PAPERBOARD;\nALBUMS FOR SAMPLES OR FOR COLLECTIONS AND BOOK COVERS, OF PAPER OR PAPERBOARD\nREGISTERS, ACCOUNT BOOKS, NOTE BOOKS, ORDER BOOKS, RECEIPT BOOKS, LETTER PADS,\nMEMORANDUM PADS, DIARIES AND SIMILAR ARTICLES : OTHER",
+        "hsn_code": "48201090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REGISTERS, ACCOUNT BOOKS, NOTE BOOKS, ORDER BOOKS, RECEIPT BOOKS, LETTER PADS,\nMEMORANDUM PADS, DIARIES AND SIMILAR ARTICLES, EXCISE BOOKS, BLOTTING-PADS,\nBINDERS (LOOSE-LEAF OR OTHER), FOLDERS, FILE COVERS, MANIFOLD BUSINESS FORMS,\nINTERLEAVED CARBON SETS AND OTHER ARTICLES OF STATIONERY, OF PAPER OR PAPERBOARD;\nALBUMS FOR SAMPLES OR FOR COLLECTIONS AND BOOK COVERS, OF PAPER OR\nPAPERBOARDEXERCISE BOOKS",
+        "hsn_code": "48202000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REGISTERS, ACCOUNT BOOKS, NOTE BOOKS, ORDER BOOKS, RECEIPT BOOKS, LETTER PADS,\nMEMORANDUM PADS, DIARIES AND SIMILAR ARTICLES, EXCISE BOOKS, BLOTTING-PADS,\nBINDERS (LOOSE-LEAF OR OTHER), FOLDERS, FILE COVERS, MANIFOLD BUSINESS FORMS,\nINTERLEAVED CARBON SETS AND OTHER ARTICLES OF STATIONERY, OF PAPER OR PAPERBOARD;\nALBUMS FOR SAMPLES OR FOR COLLECTIONS AND BOOK COVERS, OF PAPER OR\nPAPERBOARDBINDERS (OTHER THAN BOOK COVERS), FOLDERS AND FILE COVERS",
+        "hsn_code": "48203000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REGISTERS, ACCOUNT BOOKS, NOTE BOOKS, ORDER BOOKS, RECEIPT BOOKS, LETTER PADS,\nMEMORANDUM PADS, DIARIES AND SIMILAR ARTICLES, EXCISE BOOKS, BLOTTING-PADS,\nBINDERS (LOOSE-LEAF OR OTHER), FOLDERS, FILE COVERS, MANIFOLD BUSINESS FORMS,\nINTERLEAVED CARBON SETS AND OTHER ARTICLES OF STATIONERY, OF PAPER OR PAPERBOARD;\nALBUMS FOR SAMPLES OR FOR COLLECTIONS AND BOOK COVERS, OF PAPER OR\nPAPERBOARDMANIFOLD BUSINESS FORMS AND INTERLEAVED CARBON SETS",
+        "hsn_code": "48204000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REGISTERS, ACCOUNT BOOKS, NOTE BOOKS, ORDER BOOKS, RECEIPT BOOKS, LETTER PADS,\nMEMORANDUM PADS, DIARIES AND SIMILAR ARTICLES, EXCISE BOOKS, BLOTTING-PADS,\nBINDERS (LOOSE-LEAF OR OTHER), FOLDERS, FILE COVERS, MANIFOLD BUSINESS FORMS,\nINTERLEAVED CARBON SETS AND OTHER ARTICLES OF STATIONERY, OF PAPER OR PAPERBOARD;\nALBUMS FOR SAMPLES OR FOR COLLECTIONS AND BOOK COVERS, OF PAPER OR\nPAPERBOARDALBUMS FOR SAMPLES OR FOR COLLECTIONS",
+        "hsn_code": "48205000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REGISTERS, ACCOUNT BOOKS, NOTE BOOKS, ORDER BOOKS, RECEIPT BOOKS, LETTER PADS,\nMEMORANDUM PADS, DIARIES AND SIMILAR ARTICLES, EXCISE BOOKS, BLOTTING-PADS,\nBINDERS (LOOSE-LEAF OR OTHER), FOLDERS, FILE COVERS, MANIFOLD BUSINESS FORMS,\nINTERLEAVED CARBON SETS AND OTHER ARTICLES OF STATIONERY, OF PAPER OR PAPERBOARD;\nALBUMS FOR SAMPLES OR FOR COLLECTIONS AND BOOK COVERS, OF PAPER OR PAPERBOARD -\nOTHER: BLOTTING PAPERS CUT TO SIZE",
+        "hsn_code": "48209010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REGISTERS, ACCOUNT BOOKS, NOTE BOOKS, ORDER BOOKS, RECEIPT BOOKS, LETTER PADS,\nMEMORANDUM PADS, DIARIES AND SIMILAR ARTICLES, EXCISE BOOKS, BLOTTING-PADS,\nBINDERS (LOOSE-LEAF OR OTHER), FOLDERS, FILE COVERS, MANIFOLD BUSINESS FORMS,\nINTERLEAVED CARBON SETS AND OTHER ARTICLES OF STATIONERY, OF PAPER OR PAPERBOARD;\nALBUMS FOR SAMPLES OR FOR COLLECTIONS AND BOOK COVERS, OF PAPER OR PAPERBOARD -\nOTHER: OTHER",
+        "hsn_code": "48209090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REGISTERS, ACCOUNT BOOKS, NOTE BOOKS, ORDER BOOKS, RECEIPT BOOKS, LETTER PADS, MEMORANDUM PADS, DIARIES AND SIMILAR ARTICLES, EXCISE BOOKS, BLOTTING-PADS, BINDERS (LOOSE-LEAF OR OTHER), FOLDERS, FILE COVERS, MANIFOLD BUSINESS FORMS, INTER LEAVED CARBON SETS AND OTHER ARTICLES OF STATIONERY, OF PAPER OR PAPERBOARD; ALBUMS FOR SAMPLES OR FOR COLLECTIONS AND BOOK COVERS, OF PAPER OR PAPERBOARD",
+        "hsn_code": "4820",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARTONS, BOXES, CASES, BAGS AND OTHER PACKING CONTAINERS, OF PAPER, PAPERBOARD,\nCELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES; BOX FILES, LETTER TRAYS, AND SIMILAR\nARTICLES, OF PAPER OR PAPERBOARD OF A KIND USED IN OFFICES, SHOPS OR THE LIKE 4819 10\nCARTONS, BOXES, CASES, BAGS AND OTHER PACKING CONTAINERS, OF PAPER, PAPERBOARD,\nCELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES; BOX FILES, LETTER TRAYS, AND SIMILAR\nARTICLES, OF PAPER OR PAPERBOARD OF A KIND USED IN OFFICES, SHOPS OR THE LIKE\nCARTONS, BOXES AND CASES, OF CORRUGATED PAPER OR PAPERBOARD : BOXES",
+        "hsn_code": "48191010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARTONS, BOXES, CASES, BAGS AND OTHER PACKING CONTAINERS, OF PAPER, PAPERBOARD,\nCELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES; BOX FILES, LETTER TRAYS, AND SIMILAR\nARTICLES, OF PAPER OR PAPERBOARD OF A KIND USED IN OFFICES, SHOPS OR THE LIKE 4819 10\nCARTONS, BOXES, CASES, BAGS AND OTHER PACKING CONTAINERS, OF PAPER, PAPERBOARD,\nCELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES; BOX FILES, LETTER TRAYS, AND SIMILAR\nARTICLES, OF PAPER OR PAPERBOARD OF A KIND USED IN OFFICES, SHOPS OR THE LIKE\nCARTONS, BOXES AND CASES, OF CORRUGATED PAPER OR PAPERBOARD : OTHER",
+        "hsn_code": "48191090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARTONS, BOXES, CASES, BAGS AND OTHER PACKING CONTAINERS, OF PAPER, PAPERBOARD,\nCELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES; BOX FILES, LETTER TRAYS, AND SIMILAR\nARTICLES, OF PAPER OR PAPERBOARD OF A KIND USED IN OFFICES, SHOPS OR THE LIKE\nFOLDING CARTONS, BOXES AND CASES, OF NONCORRUGATED PAPER AND PAPERBOARD :\nCARTONS, BOXES, CASES, INTENDED FOR THE PACKING OF MATCH STICKS",
+        "hsn_code": "48192010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARTONS, BOXES, CASES, BAGS AND OTHER PACKING CONTAINERS, OF PAPER, PAPERBOARD,\nCELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES; BOX FILES, LETTER TRAYS, AND SIMILAR\nARTICLES, OF PAPER OR PAPERBOARD OF A KIND USED IN OFFICES, SHOPS OR THE LIKE\nFOLDING CARTONS, BOXES AND CASES, OF NONCORRUGATED PAPER AND PAPERBOARD : BOXES",
+        "hsn_code": "48192020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARTONS, BOXES, CASES, BAGS AND OTHER PACKING CONTAINERS, OF PAPER, PAPERBOARD,\nCELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES; BOX FILES, LETTER TRAYS, AND SIMILAR\nARTICLES, OF PAPER OR PAPERBOARD OF A KIND USED IN OFFICES, SHOPS OR THE LIKE\nFOLDING CARTONS, BOXES AND CASES, OF NONCORRUGATED PAPER AND PAPERBOARD :\nOTHER",
+        "hsn_code": "48192090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARTONS, BOXES, CASES, BAGS AND OTHER PACKING CONTAINERS, OF PAPER, PAPERBOARD,\nCELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES; BOX FILES, LETTER TRAYS, AND SIMILAR\nARTICLES, OF PAPER OR PAPERBOARD OF A KIND USED IN OFFICES, SHOPS OR THE LIKE - SACKS\nAND BAGS, HAVING A BASE OF A WIDTH OF 40 CM OR MORE",
+        "hsn_code": "48193000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARTONS, BOXES, CASES, BAGS AND OTHER PACKING CONTAINERS, OF PAPER, PAPERBOARD,\nCELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES; BOX FILES, LETTER TRAYS, AND SIMILAR\nARTICLES, OF PAPER OR PAPERBOARD OF A KIND USED IN OFFICES, SHOPS OR THE LIKE - OTHER\nSACKS AND BAGS, INCLUDING CONES",
+        "hsn_code": "48194000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARTONS, BOXES, CASES, BAGS AND OTHER PACKING CONTAINERS, OF PAPER, PAPERBOARD,\nCELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES; BOX FILES, LETTER TRAYS, AND SIMILAR\nARTICLES, OF PAPER OR PAPERBOARD OF A KIND USED IN OFFICES, SHOPS OR THE LIKE - OTHER\nPACKING CONTAINERS, INCLUDING RECORD SLEEVES : MADE OF CORRUGATED PAPER OR\nPAPERBOARD",
+        "hsn_code": "48195010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARTONS, BOXES, CASES, BAGS AND OTHER PACKING CONTAINERS, OF PAPER, PAPERBOARD,\nCELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES; BOX FILES, LETTER TRAYS, AND SIMILAR\nARTICLES, OF PAPER OR PAPERBOARD OF A KIND USED IN OFFICES, SHOPS OR THE LIKE - OTHER\nPACKING CONTAINERS, INCLUDING RECORD SLEEVES : OTHER",
+        "hsn_code": "48195090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARTONS, BOXES, CASES, BAGS AND OTHER PACKING CONTAINERS, OF PAPER, PAPERBOARD,\nCELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES; BOX FILES, LETTER TRAYS, AND SIMILAR\nARTICLES, OF PAPER OR PAPERBOARD OF A KIND USED IN OFFICES, SHOPS OR THE LIKE - BOX\nFILES, LETTER TRAYS, STORAGE BOXES AND SIMILAR ARTICLES, OF A KIND USED IN OFFICES,\nSHOPS OR THE LIKE",
+        "hsn_code": "48196000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARTONS, BOXES, CASES, BAGS AND OTHER PACKING CONTAINERS, OF PAPER, PAPERBOARD, CELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES; BOX FILES, LETTER TRAYS, AND SIMILAR ARTICLES, OF PAPER OR PAPERBOARD OF A KIND USED IN OFFICES, SHOPS OR THE LIKE",
+        "hsn_code": "4819",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOILET PAPER AND SIMILAR PAPER, CELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES, OF A\nKIND USED FOR HOUSEHOLD OR SANITARY PURPOSES, IN ROLLS OF A WIDTH NOT EXCEEDING 36\nCM, OR CUT TO SIZE OR SHAPE; HANDKERCHIEFS, CLEANSING TISSUES, TOWELS, TABLE CLOTHS,\nSERVIETTES, NAPKINS FOR BABIES, TAMPONS, BED SHEETS AND SIMILAR HOUSEHOLD, SANITARY\nOR HOSPITAL ARTICLES, ARTICLES OF APPAREL AND CLOTHING ACCESSORIES, OF PAPER PULP,\nPAPER, CELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES - TOILET PAPER",
+        "hsn_code": "48181000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOILET PAPER AND SIMILAR PAPER, CELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES, OF A\nKIND USED FOR HOUSEHOLD OR SANITARY PURPOSES, IN ROLLS OF A WIDTH NOT EXCEEDING 36\nCM, OR CUT TO SIZE OR SHAPE; HANDKERCHIEFS, CLEANSING TISSUES, TOWELS, TABLE CLOTHS,\nSERVIETTES, NAPKINS FOR BABIES, TAMPONS, BED SHEETS AND SIMILAR HOUSEHOLD, SANITARY\nOR HOSPITAL ARTICLES, ARTICLES OF APPAREL AND CLOTHING ACCESSORIES, OF PAPER PULP,\nPAPER, CELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES - HANDKERCHIEFS, CLEANING OR\nFACIAL TISSUES AND TOWEL",
+        "hsn_code": "48182000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOILET PAPER AND SIMILAR PAPER, CELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES, OF A\nKIND USED FOR HOUSEHOLD OR SANITARY PURPOSES, IN ROLLS OF A WIDTH NOT EXCEEDING 36\nCM, OR CUT TO SIZE OR SHAPE; HANDKERCHIEFS, CLEANSING TISSUES, TOWELS, TABLE CLOTHS,\nSERVIETTES, NAPKINS FOR BABIES, TAMPONS, BED SHEETS AND SIMILAR HOUSEHOLD, SANITARY\nOR HOSPITAL ARTICLES, ARTICLES OF APPAREL AND CLOTHING ACCESSORIES, OF PAPER PULP,\nPAPER, CELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES - TABLECLOTHS AND SERVIETTES",
+        "hsn_code": "48183000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOILET PAPER AND SIMILAR PAPER, CELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES, OF A\nKIND USED FOR HOUSEHOLD OR SANITARY PURPOSES, IN ROLLS OF A WIDTH NOT EXCEEDING 36\nCM, OR CUT TO SIZE OR SHAPE; HANDKERCHIEFS, CLEANSING TISSUES, TOWELS, TABLE CLOTHS,\nSERVIETTES, NAPKINS FOR BABIES, TAMPONS, BED SHEETS AND SIMILAR HOUSEHOLD, SANITARY\nOR HOSPITAL ARTICLES, ARTICLES OF APPAREL AND CLOTHING ACCESSORIES, OF PAPER PULP,\nPAPER, CELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES ARTICLES OF APPAREL AND\nCLOTHING ACCESSORIES",
+        "hsn_code": "48185000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOILET PAPER AND SIMILAR PAPER, CELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES, OF A\nKIND USED FOR HOUSEHOLD OR SANITARY PURPOSES, IN ROLLS OF A WIDTH NOT EXCEEDING 36\nCM, OR CUT TO SIZE OR SHAPE; HANDKERCHIEFS, CLEANSING TISSUES, TOWELS, TABLE CLOTHS,\nSERVIETTES, NAPKINS FOR BABIES, TAMPONS, BED SHEETS AND SIMILAR HOUSEHOLD, SANITARY\nOR HOSPITAL ARTICLES, ARTICLES OF APPAREL AND CLOTHING ACCESSORIES, OF PAPER PULP,\nPAPER, CELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES OTHER",
+        "hsn_code": "48189000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOILET PAPER AND SIMILAR PAPER, CELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES, OF A KIND USED FOR HOUSEHOLD OR SANITARY PURPOSES, IN ROLLS OF A WIDTH NOT EXCEEDING 36 CM, OR CUT TO SIZE OR SHAPE; HANDKERCHIEFS, CLEANSING TISSUES, TOWELS, TABLE CLOTHS, SERVIETTES, BED SHEETS AND SIMILAR HOUSEHOLD, SANITARY OR HOSPITAL ARTICLES, ARTICLES OF APPAREL AND CLOTHING ACCESSORIES, OF PAPER PULP, PAPER, CELLULOSE WADDING OR WEBS OF CELLULOSE FIBRES",
+        "hsn_code": "4818",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON-PAPER, SELF-COPY PAPER AND OTHER COPYING OR TRANSFER PAPERS (OTHER THAN\nTHOSE OF HEADING 4809), DUPLICATOR STENCILS AND OFFSET PLATES, OF PAPER, WHETHER OR\nNOT PUT UP IN BOXES - CARBON OR SIMILAR COPYING PAPERS",
+        "hsn_code": "48161000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON-PAPER, SELF-COPY PAPER AND OTHER COPYING OR TRANSFER PAPERS (OTHER THAN\nTHOSE OF HEADING 4809), DUPLICATOR STENCILS AND OFFSET PLATES, OF PAPER, WHETHER OR\nNOT PUT UP IN BOXES - SELF-COPY PAPER : DUPLICATING PAPER, CUT TO SIZE",
+        "hsn_code": "48162010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON-PAPER, SELF-COPY PAPER AND OTHER COPYING OR TRANSFER PAPERS (OTHER THAN\nTHOSE OF HEADING 4809), DUPLICATOR STENCILS AND OFFSET PLATES, OF PAPER, WHETHER OR\nNOT PUT UP IN BOXES - SELF-COPY PAPER : PAPER FOR FAX MACHINE",
+        "hsn_code": "48162020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON-PAPER, SELF-COPY PAPER AND OTHER COPYING OR TRANSFER PAPERS (OTHER THAN\nTHOSE OF HEADING 4809), DUPLICATOR STENCILS AND OFFSET PLATES, OF PAPER, WHETHER OR\nNOT PUT UP IN BOXES - SELF-COPY PAPER : OTHER",
+        "hsn_code": "48162090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON-PAPER, SELF-COPY PAPER AND OTHER COPYING OR TRANSFER PAPERS (OTHER THAN\nTHOSE OF HEADING 4809), DUPLICATOR STENCILS AND OFFSET PLATES, OF PAPER, WHETHER OR\nNOT PUT UP IN BOXES DUPLICATOR STENCILS",
+        "hsn_code": "48163000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON-PAPER, SELF-COPY PAPER AND OTHER COPYING OR TRANSFER PAPERS (OTHER THAN\nTHOSE OF HEADING 4809), DUPLICATOR STENCILS AND OFFSET PLATES, OF PAPER, WHETHER OR\nNOT PUT UP IN BOXES - OTHER : OTHER COPYING OR TRANSFER PAPERS (EXCLUDING PRINTED\nTRANSFER) CUT TO SIZE WHETHER OR NOT PUT UP IN BOXES",
+        "hsn_code": "48169010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON-PAPER, SELF-COPY PAPER AND OTHER COPYING OR TRANSFER PAPERS (OTHER THAN\nTHOSE OF HEADING 4809), DUPLICATOR STENCILS AND OFFSET PLATES, OF PAPER, WHETHER OR\nNOT PUT UP IN BOXES - OTHER : CALCULATING MACHINE PAPER IN ROLLS AND STRIPS NOT\nEXCEEDING 15 CM IN WIDTH",
+        "hsn_code": "48169020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON-PAPER, SELF-COPY PAPER AND OTHER COPYING OR TRANSFER PAPERS (OTHER THAN\nTHOSE OF HEADING 4809), DUPLICATOR STENCILS AND OFFSET PLATES, OF PAPER, WHETHER OR\nNOT PUT UP IN BOXES - OTHER : OTHER",
+        "hsn_code": "48169090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON-PAPER, SELF-COPY PAPER AND OTHER COPYING OR TRANSFER PAPERS (OTHER THAN THOSE OF HEADING 4809), DUPLICATOR STENCILS AND OFFSET PLATES, OF PAPER, WHETHER OR NOT PUT UP IN BOXES",
+        "hsn_code": "4816",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WALLPAPER AND SIMILAR WALL COVERINGS; WINDOW TRANSPARENCIES OF PAPER -\nWALLPAPER AND SIMILAR WALL COVERINGS, CONSISTING OF PAPER COATED OR COVERED, ON\nTHE FACE SIDE, WITH A GRAINED, EMBOSSED, COLOURED, DESIGN-PRINTED OR OTHERWISE\nDECORATED LAYER OF PLASTICS",
+        "hsn_code": "48142000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WALLPAPER AND SIMILAR WALL COVERINGS; WINDOW TRANSPARENCIES OF PAPER -\nWALLPAPER AND SIMILAR WALL COVERINGS, CONSISTING OF PAPER COVERED, ON THE FACE\nSIDE, WITH PLAITING MATERIAL, WHETHER OR NOT BOUND TOGETHER IN PARALLEL STRANDS\nOR WOVEN",
+        "hsn_code": "48143000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WALLPAPER AND SIMILAR WALL COVERINGS; WINDOW TRANSPARENCIES OF PAPER - OTHER",
+        "hsn_code": "48149000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WALLPAPER AND SIMILAR WALL COVERINGS; WINDOW TRANSPARENCIES OF PAPER",
+        "hsn_code": "4814",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CIGARETTE PAPER, WHETHER OR NOT CUT TO SIZE OR IN THE FORM OF BOOKLETS OR TUBES IN\nTHE FORM OF BOOKLETS OR TUBES",
+        "hsn_code": "48131000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CIGARETTE PAPER, WHETHER OR NOT CUT TO SIZE OR IN THE FORM OF BOOKLETS OR TUBES IN\nROLLS OF A WIDTH NOT EXCEEDING 5 CM",
+        "hsn_code": "48132000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CIGARETTE PAPER, WHETHER OR NOT CUT TO SIZE OR IN THE FORM OF BOOKLETS OR TUBES -\nOTHER : CIGARETTE PAPER IN BULK, OR IN SHEETS",
+        "hsn_code": "48139010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CIGARETTE PAPER, WHETHER OR NOT CUT TO SIZE OR IN THE FORM OF BOOKLETS OR TUBES -\nOTHER : OTHER",
+        "hsn_code": "48139090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CIGARETTE PAPER, WHETHER OR NOT CUT TO SIZE OR IN THE FORM OF BOOKLETS OR TUBES",
+        "hsn_code": "4813",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FILTER BLOCKS, SLABS AND PLATES, OF PAPER PULP",
+        "hsn_code": "48120000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Filter blocks, slabs and plates, of paper pulp",
+        "hsn_code": "4812",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810 - TARRED, BITUMINISED OR ASPHALTED\nPAPER AND PAPERBOARD",
+        "hsn_code": "48111000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810- GUMMED OR ADHESIVE PAPER AND\nPAPERBOARD : SELF-ADHESIVE",
+        "hsn_code": "48114100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810- GUMMED OR ADHESIVE PAPER AND\nPAPERBOARD : OTHER",
+        "hsn_code": "48114900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810- PAPER AND PAPERBOARD, COATED,\nIMPREGNATED OR COVERED WITH PLASTICS (EXCLUDING ADHESIVES)--BLEACHED, WEIGHING\nMORE THAN 150 G/M---ASEPTIC PACKAGING PAPER",
+        "hsn_code": "48115110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810- PAPER AND PAPERBOARD, COATED,\nIMPREGNATED OR COVERED WITH PLASTICS (EXCLUDING ADHESIVES)--BLEACHED, WEIGHING\nMORE THAN 150 G/M---OTHER",
+        "hsn_code": "48115190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810- PAPER AND PAPERBOARD, COATED,\nIMPREGNATED OR COVERED WITH PLASTICS (EXCLUDING ADHESIVES)--OTHER---ASEPTIC\nPACKAGING PAPER",
+        "hsn_code": "48115910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810- PAPER AND PAPERBOARD, COATED,\nIMPREGNATED OR COVERED WITH PLASTICS (EXCLUDING ADHESIVES)--OTHER---OTHER",
+        "hsn_code": "48115990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810 - PAPER AND PAPERBOARD, COATED,\nIMPREGNATED OR COVERED WITH WAX, PARAFFIN WAX, STEARINE, OIL OR GLYCEROL",
+        "hsn_code": "48116000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810- OTHER PAPER, PAPERBOARD, CELLULOSE\nWADDING AND WEBS OF CELLULOSE FIBRES : HANDMADE PAPER AND PAPERBOARD, RULES,\nLINED OR SQUARED BUT NOT OTHERWISE PRINTED; CHROMO AND ART PAPER, COATED,\nBUILDING BOARD OF PAPER OR PULP, IMPREGNATED; CHROMO BOARD; RAW BASE PAPER FOR\nSENSITISING, COATED; SURFACE MARBLED PAPER; LEATHER BOARD AND IMITATION LEATHER\nBOARD; AND MATRIX BOARD : HANDMADE PAPER AND PAPERBOARD, RULES, LINED OR\nSQUARED BUT NOT OTHERWISE PRINTED",
+        "hsn_code": "48119011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810- OTHER PAPER, PAPERBOARD, CELLULOSE\nWADDING AND WEBS OF CELLULOSE FIBRES : HANDMADE PAPER AND PAPERBOARD, RULES,\nLINED OR SQUARED BUT NOT OTHERWISE PRINTED; CHROMO AND ART PAPER, COATED,\nBUILDING BOARD OF PAPER OR PULP, IMPREGNATED; CHROMO BOARD; RAW BASE PAPER FOR\nSENSITISING, COATED; SURFACE MARBLED PAPER; LEATHER BOARD AND IMITATION LEATHER\nBOARD; AND MATRIX BOARD : CHROMO AND ART PAPER, COATED",
+        "hsn_code": "48119012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810- OTHER PAPER, PAPERBOARD, CELLULOSE\nWADDING AND WEBS OF CELLULOSE FIBRES : HANDMADE PAPER AND PAPERBOARD, RULES,\nLINED OR SQUARED BUT NOT OTHERWISE PRINTED; CHROMO AND ART PAPER, COATED,\nBUILDING BOARD OF PAPER OR PULP, IMPREGNATED; CHROMO BOARD; RAW BASE PAPER FOR\nSENSITISING, COATED; SURFACE MARBLED PAPER; LEATHER BOARD AND IMITATION LEATHER\nBOARD; AND MATRIX BOARD : BUILDING BOARD OF PAPER OR PULP, IMPREGNATED",
+        "hsn_code": "48119013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810- OTHER PAPER, PAPERBOARD, CELLULOSE\nWADDING AND WEBS OF CELLULOSE FIBRES : HANDMADE PAPER AND PAPERBOARD, RULES,\nLINED OR SQUARED BUT NOT OTHERWISE PRINTED; CHROMO AND ART PAPER, COATED,\nBUILDING BOARD OF PAPER OR PULP, IMPREGNATED; CHROMO BOARD; RAW BASE PAPER FOR\nSENSITISING, COATED; SURFACE MARBLED PAPER; LEATHER BOARD AND IMITATION LEATHER\nBOARD; AND MATRIX BOARD : CHROMO BOARD",
+        "hsn_code": "48119014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810- OTHER PAPER, PAPERBOARD, CELLULOSE\nWADDING AND WEBS OF CELLULOSE FIBRES : HANDMADE PAPER AND PAPERBOARD, RULES,\nLINED OR SQUARED BUT NOT OTHERWISE PRINTED; CHROMO AND ART PAPER, COATED,\nBUILDING BOARD OF PAPER OR PULP, IMPREGNATED; CHROMO BOARD; RAW BASE PAPER FOR\nSENSITISING, COATED; SURFACE MARBLED PAPER; LEATHER BOARD AND IMITATION LEATHER\nBOARD; AND MATRIX BOARD : RAW BASE PAPER FOR SENSITISING, COATED",
+        "hsn_code": "48119015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810- OTHER PAPER, PAPERBOARD, CELLULOSE\nWADDING AND WEBS OF CELLULOSE FIBRES : HANDMADE PAPER AND PAPERBOARD, RULES,\nLINED OR SQUARED BUT NOT OTHERWISE PRINTED; CHROMO AND ART PAPER, COATED,\nBUILDING BOARD OF PAPER OR PULP, IMPREGNATED; CHROMO BOARD; RAW BASE PAPER FOR\nSENSITISING, COATED; SURFACE MARBLED PAPER; LEATHER BOARD AND IMITATION LEATHER\nBOARD; AND MATRIX BOARD : SURFACE MARBLED PAPER",
+        "hsn_code": "48119016",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810- OTHER PAPER, PAPERBOARD, CELLULOSE\nWADDING AND WEBS OF CELLULOSE FIBRES : HANDMADE PAPER AND PAPERBOARD, RULES,\nLINED OR SQUARED BUT NOT OTHERWISE PRINTED; CHROMO AND ART PAPER, COATED,\nBUILDING BOARD OF PAPER OR PULP, IMPREGNATED; CHROMO BOARD; RAW BASE PAPER FOR\nSENSITISING, COATED; SURFACE MARBLED PAPER; LEATHER BOARD AND IMITATION LEATHER\nBOARD; AND MATRIX BOARD : LEATHER BOARD AND IMITATION LEATHER BOARD",
+        "hsn_code": "48119017",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810- OTHER PAPER, PAPERBOARD, CELLULOSE\nWADDING AND WEBS OF CELLULOSE FIBRES : HANDMADE PAPER AND PAPERBOARD, RULES,\nLINED OR SQUARED BUT NOT OTHERWISE PRINTED; CHROMO AND ART PAPER, COATED,\nBUILDING BOARD OF PAPER OR PULP, IMPREGNATED; CHROMO BOARD; RAW BASE PAPER FOR\nSENSITISING, COATED; SURFACE MARBLED PAPER; LEATHER BOARD AND IMITATION LEATHER\nBOARD; AND MATRIX BOARD : MATRIX BOARD",
+        "hsn_code": "48119018",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810- OTHER PAPER, PAPERBOARD, CELLULOSE\nWADDING AND WEBS OF CELLULOSE FIBRES : OTHER : GRAPE GUARD PAPER",
+        "hsn_code": "48119091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810- OTHER PAPER, PAPERBOARD, CELLULOSE\nWADDING AND WEBS OF CELLULOSE FIBRES : OTHER : THERMAL PAPER FOR FAX MACHINES",
+        "hsn_code": "48119093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810- OTHER PAPER, PAPERBOARD, CELLULOSE\nWADDING AND WEBS OF CELLULOSE FIBRES : OTHER : THERMAL PAPER IN JUMBO ROLLS (SIZE 1\nMT AND ABOVE IN WIDTH AND 5,000 MT AND ABOVE IN LENGTH",
+        "hsn_code": "48119094",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, COATED,\nIMPREGNATED, COVERED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE\nKIND DESCRIBED IN HEADING 4803, 4809 OR 4810- OTHER PAPER, PAPERBOARD, CELLULOSE\nWADDING AND WEBS OF CELLULOSE FIBRES : OTHER : OTHER",
+        "hsn_code": "48119099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER, PAPERBOARD, CELLULOSE WADING AND WEBS OF CELLULOSE FIBRES, COATED, IMPREGNATED, COVERED, SURFACE – COLOURED , SURFACE-DECORATED OR PRINTED, IN ROLLS OR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE, OTHER THAN GOODS OF THE KIND DESCRIBED IN HEADING 4803, 4809 OR 4810",
+        "hsn_code": "4811",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - PAPER AND PAPERBOARD OF A\nKIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES, NOT CONTAINING FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF WHICH NOT MORE THAN\n10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH FIBRES : IN ROLLS :\nIMITATION ART PAPER",
+        "hsn_code": "48101310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - PAPER AND PAPERBOARD OF A\nKIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES, NOT CONTAINING FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF WHICH NOT MORE THAN\n10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH FIBRES : IN ROLLS : ART\nPAPER",
+        "hsn_code": "48101320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - PAPER AND PAPERBOARD OF A\nKIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES, NOT CONTAINING FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF WHICH NOT MORE THAN\n10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH FIBRES : IN ROLLS : CHROME\nPAPER OR PAPERBOARD",
+        "hsn_code": "48101330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - PAPER AND PAPERBOARD OF A\nKIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES, NOT CONTAINING FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF WHICH NOT MORE THAN\n10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH FIBRES : IN ROLLS : OTHER",
+        "hsn_code": "48101390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - PAPER AND PAPERBOARD OF A\nKIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES, NOT CONTAINING FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF WHICH NOT MORE THAN\n10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH FIBRES : IN SHEETS WITH\nONE SIDE NOT EXCEEDING 435 MM AND THE OTHER SIDE NOT EXCEEDING 297 MM IN THE\nUNFOLDED STATE : IMITATION ART PAPER",
+        "hsn_code": "48101410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - PAPER AND PAPERBOARD OF A\nKIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES, NOT CONTAINING FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF WHICH NOT MORE THAN\n10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH FIBRES : IN SHEETS WITH\nONE SIDE NOT EXCEEDING 435 MM AND THE OTHER SIDE NOT EXCEEDING 297 MM IN THE\nUNFOLDED STATE : ART PAPER",
+        "hsn_code": "48101420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - PAPER AND PAPERBOARD OF A\nKIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES, NOT CONTAINING FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF WHICH NOT MORE THAN\n10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH FIBRES : IN SHEETS WITH\nONE SIDE NOT EXCEEDING 435 MM AND THE OTHER SIDE NOT EXCEEDING 297 MM IN THE\nUNFOLDED STATE : CHROME PAPER OR PAPERBOARD",
+        "hsn_code": "48101430",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - PAPER AND PAPERBOARD OF A\nKIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES, NOT CONTAINING FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF WHICH NOT MORE THAN\n10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH FIBRES : IN SHEETS WITH\nONE SIDE NOT EXCEEDING 435 MM AND THE OTHER SIDE NOT EXCEEDING 297 MM IN THE\nUNFOLDED STATE : OTHER",
+        "hsn_code": "48101490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - PAPER AND PAPERBOARD OF A\nKIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES, NOT CONTAINING FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF WHICH NOT MORE THAN\n10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH FIBRES : OTHER : IMITATION\nART PAPER",
+        "hsn_code": "48101910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - PAPER AND PAPERBOARD OF A\nKIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES, NOT CONTAINING FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF WHICH NOT MORE THAN\n10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH FIBRES : OTHER : ART PAPER",
+        "hsn_code": "48101920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - PAPER AND PAPERBOARD OF A\nKIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES, NOT CONTAINING FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF WHICH NOT MORE THAN\n10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH FIBRES : OTHER : CHROME\nPAPER OR PAPERBOARD",
+        "hsn_code": "48101930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - PAPER AND PAPERBOARD OF A\nKIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES, NOT CONTAINING FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF WHICH NOT MORE THAN\n10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH FIBRES : OTHER : OTHER",
+        "hsn_code": "48101990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - PAPER AND PAPERBOARD OF A\nKIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES, OF WHICH MORE THAN\n10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES OBTAINED BY A\nMECHANICAL OR CHEMI-MECHANICAL PROCESS : LIGHT-WEIGHT COATED PAPER",
+        "hsn_code": "48102200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - PAPER AND PAPERBOARD OF A\nKIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES, OF WHICH MORE THAN\n10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES OBTAINED BY A\nMECHANICAL OR CHEMI-MECHANICAL PROCESS : OTHER",
+        "hsn_code": "48102900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - KRAFT PAPER AND PAPERBOARD,\nOTHER THAN THAT OF A KIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES :\nBLEACHED UNIFORMLY THROUGHOUT THE MASS AND OF WHICH MORE THAN 95% BY WEIGHT\nOF THE TOTAL FIBRE CONTENT CONSISTS OF WOOD FIBRES OBTAINED BY A CHEMICAL PROCESS,\nAND WEIGHING 150 G/M2OR LESS",
+        "hsn_code": "48103100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - KRAFT PAPER AND PAPERBOARD,\nOTHER THAN THAT OF A KIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES :\nBLEACHED UNIFORMLY THROUGHOUT THE MASS AND OF WHICH MORE THAN 95% BY WEIGHT\nOF THE TOTAL FIBRE CONTENT CONSISTS OF WOOD FIBRES OBTAINED BY A CHEMICAL PROCESS,\nAND WEIGHING MORE THAN 150 G/M2",
+        "hsn_code": "48103200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - KRAFT PAPER AND PAPERBOARD,\nOTHER THAN THAT OF A KIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES :\nOTHER : INSULATING PAPER",
+        "hsn_code": "48103910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - KRAFT PAPER AND PAPERBOARD,\nOTHER THAN THAT OF A KIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES :\nOTHER : ELECTRIC INSULATING PRESS BOARD",
+        "hsn_code": "48103920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - KRAFT PAPER AND PAPERBOARD,\nOTHER THAN THAT OF A KIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES :\nOTHER : INSULATION BOARDS (HOMOGENOUS)",
+        "hsn_code": "48103930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - KRAFT PAPER AND PAPERBOARD,\nOTHER THAN THAT OF A KIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES :\nOTHER : OTHER",
+        "hsn_code": "48103990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - OTHER PAPER AND PAPERBOARD :\nMULTI-PLY",
+        "hsn_code": "48109200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR\nOTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING,\nWHETHER OR NOT SURFACE - COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR\nRECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE - OTHER PAPER AND PAPERBOARD :\nOTHER",
+        "hsn_code": "48109900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, COATED ON ONE OR BOTH SIDES WITH KAOLIN (CHINA CLAY) OR OTHER INORGANIC SUBSTANCES, WITH OR WITHOUT A BINDER, AND WITH NO OTHER COATING, WHETHER OR NOT SURFACE – COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR RECTANGULAR (INCLUDING SQUARE) SHEETS, OF ANY SIZE",
+        "hsn_code": "4810",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBON PAPER, SELF-COPY PAPER AND OTHER COPYING OR TRANSFER PAPERS (INCLUDING\nCOATED OR IMPREGNATED PAPER FOR DUPLICATOR STENCILS OR OFFSET PLATES), WHETHER\nOR NOT PRINTED, IN ROLLS OR SHEETS - CARBON OR SIMILAR COPYING PAPERS : MANIFOLD\nPAPER",
+        "hsn_code": "48091010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON PAPER, SELF-COPY PAPER AND OTHER COPYING OR TRANSFER PAPERS (INCLUDING\nCOATED OR IMPREGNATED PAPER FOR DUPLICATOR STENCILS OR OFFSET PLATES), WHETHER\nOR NOT PRINTED, IN ROLLS OR SHEETS - CARBON OR SIMILAR COPYING PAPERS : OTHER",
+        "hsn_code": "48091090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON PAPER, SELF-COPY PAPER AND OTHER COPYING OR TRANSFER PAPERS (INCLUDING\nCOATED OR IMPREGNATED PAPER FOR DUPLICATOR STENCILS OR OFFSET PLATES), WHETHER\nOR NOT PRINTED, IN ROLLS OR SHEETS SELF-COPY PAPER",
+        "hsn_code": "48092000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON PAPER, SELF-COPY PAPER AND OTHER COPYING OR TRANSFER PAPERS (INCLUDING\nCOATED OR IMPREGNATED PAPER FOR DUPLICATOR STENCILS OR OFFSET PLATES), WHETHER\nOR NOT PRINTED, IN ROLLS OR SHEETS OTHER",
+        "hsn_code": "48099000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CARBON PAPER, SELF-COPY PAPER AND OTHER COPYING OR TRANSFER PAPERS (INCLUDING COATED OR IMPREGNATED PAPER FOR DUPLICATOR STENCILS OR OFFSET PLATES), WHETHER OR NOT PRINTED, IN ROLLS OR SHEETS",
+        "hsn_code": "4809",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, CORRUGATED (WITH OR WITHOUT GLUED FLAT SURFACE SHEETS),\nCREPED, CRINKLED, EMBOSSED OR PERFORATED, IN ROLLS OR SHEETS, OTHER THAN PAPER OF\nTHE KIND DESCRIBED IN HEADING 4803 - CORRUGATED PAPER AND PAPERBOARD, WHETHER OR\nNOT PERFORATE",
+        "hsn_code": "48081000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, CORRUGATED (WITH OR WITHOUT GLUED FLAT SURFACE SHEETS),\nCREPED, CRINKLED, EMBOSSED OR PERFORATED, IN ROLLS OR SHEETS, OTHER THAN PAPER OF\nTHE KIND DESCRIBED IN HEADING 4803 -KRAFT PAPER, CREPED OR CRINKLED, WHETHER OR NOT\nEMBOSSED OR PERFORATED--- SACK KRAFT PAPER, CREPED OR CRINKLED, WHETHER OR NOT\nEMBOSSED OR PERFORATED",
+        "hsn_code": "48084010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, CORRUGATED (WITH OR WITHOUT GLUED FLAT SURFACE SHEETS),\nCREPED, CRINKLED, EMBOSSED OR PERFORATED, IN ROLLS OR SHEETS, OTHER THAN PAPER OF\nTHE KIND DESCRIBED IN HEADING 4803 -KRAFT PAPER, CREPED OR CRINKLED, WHETHER OR NOT\nEMBOSSED OR PERFORATED--- OTHER KRAFT PAPER, CREPED OR CRINKLED, WHETHER OR NOT\nEMBOSSED OR PERFORATED",
+        "hsn_code": "48084090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, CORRUGATED (WITH OR WITHOUT GLUED FLAT SURFACE SHEETS),\nCREPED, CRINKLED, EMBOSSED OR PERFORATED, IN ROLLS OR SHEETS, OTHER THAN PAPER OF\nTHE KIND DESCRIBED IN HEADING 4803 - OTHER",
+        "hsn_code": "48089000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PAPER AND PAPERBOARD, CORRUGATED (WITH OR WITHOUT GLUED FLAT SURFACE SHEETS), CREPED, CRINKLED, EMBOSSED OR PERFORATED, IN ROLLS OR SHEETS, OTHER THAN PAPER OF THE KIND DESCRIBED IN HEADING 4803",
+        "hsn_code": "4808",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COMPOSITE PAPER AND PAPERBOARD (MADE BY STICKING FLAT LAYERS OF PAPER OR\nPAPERBOARD TOGETHER WITH AN ADHESIVE), NOT SURFACE-COATED OR IMPREGNATED,\nWHETHER OR NOT INTERNALLY REINFORCED, IN ROLLS OR SHEETS- COMPOSITE PAPER AND\nPAPERBOARD (MADE BY STICKING FLAT LAYERS OF PAPER OR PAPERBOARD TOGETHER WITH AN\nADHESIVE), NOT SURFACE-COATED OR IMPREGNATED, WHETHER OR NOT INTERNALLY\nREINFORCED, IN ROLLS OR SHEETS : STRAW PAPER AND OTHER STRAW BOARD, WHETHER OR\nNOT COVERED WITH PAPER OTHER THAN STRAW PAPER",
+        "hsn_code": "48070010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COMPOSITE PAPER AND PAPERBOARD (MADE BY STICKING FLAT LAYERS OF PAPER OR\nPAPERBOARD TOGETHER WITH AN ADHESIVE), NOT SURFACE-COATED OR IMPREGNATED,\nWHETHER OR NOT INTERNALLY REINFORCED, IN ROLLS OR SHEETS- COMPOSITE PAPER AND\nPAPERBOARD (MADE BY STICKING FLAT LAYERS OF PAPER OR PAPERBOARD TOGETHER WITH AN\nADHESIVE), NOT SURFACE-COATED OR IMPREGNATED, WHETHER OR NOT INTERNALLY\nREINFORCED, IN ROLLS OR SHEETS : OTHER",
+        "hsn_code": "48070090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COMPOSITE PAPER AND PAPERBOARD (MADE BY STICKING FLAT LAYERS OF PAPER OR PAPERBOARD TOGETHER WITH AN ADHESIVE), NOT SURFACE-COATED OR IMPREGNATED, WHETHER OR NOT INTERNALLY REINFORCED, IN ROLLS OR SHEETS",
+        "hsn_code": "4807",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER UNCOATED PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, NOT FURTHER WORKED OR\nPROCESSED THAN AS SPECIFIED IN NOTE 3 TO THIS CHAPTER - FLUTING PAPER : SEMI-CHEMICAL\nFLUTING PAPER",
+        "hsn_code": "48051100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER UNCOATED PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, NOT FURTHER WORKED OR\nPROCESSED THAN AS SPECIFIED IN NOTE 3 TO THIS CHAPTER - FLUTING PAPER : STRAW FLUTING\nPAPER",
+        "hsn_code": "48051200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER UNCOATED PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, NOT FURTHER WORKED OR\nPROCESSED THAN AS SPECIFIED IN NOTE 3 TO THIS CHAPTER - FLUTING PAPER : OTHER",
+        "hsn_code": "48051900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER UNCOATED PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, NOT FURTHER WORKED OR\nPROCESSED THAN AS SPECIFIED IN NOTE 3 TO THIS CHAPTER - TESTLINER (RECYCLED LINER\nBOARD) : WEIGHING 150 G/M2OR LESS",
+        "hsn_code": "48052400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER UNCOATED PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, NOT FURTHER WORKED OR\nPROCESSED THAN AS SPECIFIED IN NOTE 3 TO THIS CHAPTER - TESTLINER (RECYCLED LINER\nBOARD) : WEIGHING MORE THAN 150 G/M2",
+        "hsn_code": "48052500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER UNCOATED PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, NOT FURTHER WORKED OR\nPROCESSED THAN AS SPECIFIED IN NOTE 3 TO THIS CHAPTER - TESTLINER (RECYCLED LINER\nBOARD) : SULPHITE WRAPPING PAPER",
+        "hsn_code": "48053000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER UNCOATED PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, NOT FURTHER WORKED OR\nPROCESSED THAN AS SPECIFIED IN NOTE 3 TO THIS CHAPTER - TESTLINER (RECYCLED LINER\nBOARD) : FILTER PAPER AND PAPERBOARD",
+        "hsn_code": "48054000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER UNCOATED PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, NOT FURTHER WORKED OR\nPROCESSED THAN AS SPECIFIED IN NOTE 3 TO THIS CHAPTER - TESTLINER (RECYCLED LINER\nBOARD) : FELT PAPER AND PAPERBOARD",
+        "hsn_code": "48055000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER UNCOATED PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, NOT FURTHER WORKED OR\nPROCESSED THAN AS SPECIFIED IN NOTE 3 TO THIS CHAPTER - OTHER : WEIGHING 150 G/M2OR\nLESS",
+        "hsn_code": "48059100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER UNCOATED PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, NOT FURTHER WORKED OR\nPROCESSED THAN AS SPECIFIED IN NOTE 3 TO THIS CHAPTER - OTHER : WEIGHING MORE THAN\n150 G/M2BUT LESS THAN 225 G/M2",
+        "hsn_code": "48059200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER UNCOATED PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, NOT FURTHER WORKED OR\nPROCESSED THAN AS SPECIFIED IN NOTE 3 TO THIS CHAPTER - OTHER : WEIGHING 225 G/M2OR\nMORE",
+        "hsn_code": "48059300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER UNCOATED PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, NOT FURTHER WORKED OR PROCESSED THAN AS SPECIFIED IN NOTE 3 TO THIS CHAPTER",
+        "hsn_code": "4805",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED KRAFT PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, OTHER THAN THAT OF\nHEADING 4802 OR 4803 - KRAFTLINER : UNBLEACHED",
+        "hsn_code": "48041100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED KRAFT PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, OTHER THAN THAT OF\nHEADING 4802 OR 4803 - KRAFTLINER : OTHER",
+        "hsn_code": "48041900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED KRAFT PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, OTHER THAN THAT OF\nHEADING 4802 OR 4803 - SACK KRAFT PAPER : UNBLEACHED",
+        "hsn_code": "48042100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED KRAFT PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, OTHER THAN THAT OF\nHEADING 4802 OR 4803 - SACK KRAFT PAPER : OTHER",
+        "hsn_code": "48042900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED KRAFT PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, OTHER THAN THAT OF\nHEADING 4802 OR 4803 - OTHER KRAFT PAPER AND PAPERBOARD WEIGHING 150 G/M2OR LESS:\nUNBLEACHED",
+        "hsn_code": "48043100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED KRAFT PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, OTHER THAN THAT OF\nHEADING 4802 OR 4803 - OTHER KRAFT PAPER AND PAPERBOARD WEIGHING 150 G/M2OR LESS:\nOTHER",
+        "hsn_code": "48043900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED KRAFT PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, OTHER THAN THAT OF\nHEADING 4802 OR 4803 - OTHER KRAFT PAPER AND PAPERBOARD WEIGHING MORE THAN 150\nG/M2BUT LESS THAN 225 G/M2: UNBLEACHED",
+        "hsn_code": "48044100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED KRAFT PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, OTHER THAN THAT OF\nHEADING 4802 OR 4803 - OTHER KRAFT PAPER AND PAPERBOARD WEIGHING MORE THAN 150\nG/M2BUT LESS THAN 225 G/M2: BLEACHED UNIFORMLY THROUGHOUT THE MASS AND OF\nWHICH MORE THAN 95% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF WOOD FIBRES\nOBTAINED BY A CHEMICAL PROCESS",
+        "hsn_code": "48044200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED KRAFT PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, OTHER THAN THAT OF\nHEADING 4802 OR 4803 - OTHER KRAFT PAPER AND PAPERBOARD WEIGHING MORE THAN 150\nG/M2BUT LESS THAN 225 G/M2: OTHER",
+        "hsn_code": "48044900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED KRAFT PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, OTHER THAN THAT OF\nHEADING 4802 OR 4803 - OTHER KRAFT PAPER AND PAPERBOARD WEIGHING 225 G/M2OR\nMORE UNBLEACHED",
+        "hsn_code": "48045100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED KRAFT PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, OTHER THAN THAT OF\nHEADING 4802 OR 4803 - OTHER KRAFT PAPER AND PAPERBOARD WEIGHING 225 G/M2OR\nMORE BLEACHED UNIFORMLY THROUGHOUT THE MASS AND OF WHICH MORE THAN 95% BY\nWEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF WOOD FIBRES OBTAINED BY A CHEMICAL\nPROCESS",
+        "hsn_code": "48045200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED KRAFT PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, OTHER THAN THAT OF\nHEADING 4802 OR 4803 - OTHER KRAFT PAPER AND PAPERBOARD WEIGHING 225 G/M2OR\nMORE OTHER",
+        "hsn_code": "48045900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED KRAFT PAPER AND PAPERBOARD, IN ROLLS OR SHEETS, OTHER THAN THAT OF HEADING 4802 OR 4803",
+        "hsn_code": "4804",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TOILET OR FACIAL TISSUE STOCK, TOWEL OR NAPKIN STOCK AND SIMILAR PAPER OF A KIND\nUSED FOR HOUSEHOLD OR SANITARY PURPOSES, CELLULOSE WADDING AND WEBS OF\nCELLULOSE FIBRES, WHETHER OR NOT CREPED, CRINKLED, EMBOSSED, PERFORATED, SURFACE\nCOLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR SHEETS - TOILET OR FACIAL TISSUE\nSTOCK, TOWEL OR NAPKIN STOCK AND SIMILAR PAPER OF A KIND USED FOR HOUSEHOLD OR\nSANITARY PURPOSES, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, WHETHER OR\nNOT CREPED, CRINKLED, EMBOSSED, PERFORATED, SURFACE-COLOURED, SURFACEDECORATED\nOR PRINTED, IN ROLLS OR SHEETS : IN COMMERCIAL SIZEROLLS OF A WIDTH 36 CM AND ABOVE",
+        "hsn_code": "48030010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOILET OR FACIAL TISSUE STOCK, TOWEL OR NAPKIN STOCK AND SIMILAR PAPER OF A KIND\nUSED FOR HOUSEHOLD OR SANITARY PURPOSES, CELLULOSE WADDING AND WEBS OF\nCELLULOSE FIBRES, WHETHER OR NOT CREPED, CRINKLED, EMBOSSED, PERFORATED, SURFACE\nCOLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR SHEETS - TOILET OR FACIAL TISSUE\nSTOCK, TOWEL OR NAPKIN STOCK AND SIMILAR PAPER OF A KIND USED FOR HOUSEHOLD OR\nSANITARY PURPOSES, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, WHETHER OR\nNOT CREPED, CRINKLED, EMBOSSED, PERFORATED, SURFACE-COLOURED, SURFACEDECORATED\nOR PRINTED, IN ROLLS OR SHEETS : OTHER",
+        "hsn_code": "48030090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TOILET OR FACIAL TISSUE STOCK, TOWEL OR NAPKIN STOCK AND SIMILAR PAPER OF A KIND USED FOR HOUSEHOLD OR SANITARY PURPOSES, CELLULOSE WADDING AND WEBS OF CELLULOSE FIBRES, WHETHER OR NOT CREPED, CRINKLED, EMBOSSED, PERFORATED, SURFACE-COLOURED, SURFACE-DECORATED OR PRINTED, IN ROLLS OR SHEETS",
+        "hsn_code": "4803",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - HAND-MADE PAPER AND PAPERBOARD\n: PAPER",
+        "hsn_code": "48021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - HAND-MADE PAPER AND PAPERBOARD\n: PAPERBOARD",
+        "hsn_code": "48021020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - PAPER AND PAPERBOARD OF A KIND\nUSED AS A BASE FOR PHOTO-SENSITIVE, HEAT-SENSITIVE OR ELECTRO-SENSITIVE PAPER OR\nPAPERBOARD : PHOTOGRAPHIC BASE PAPER, UNCOATED",
+        "hsn_code": "48022010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - PAPER AND PAPERBOARD OF A KIND\nUSED AS A BASE FOR PHOTO-SENSITIVE, HEAT-SENSITIVE OR ELECTRO-SENSITIVE PAPER OR\nPAPERBOARD : OTHER",
+        "hsn_code": "48022090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD CARBONISING BASE PAPER",
+        "hsn_code": "48023000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD WALL PAPER BASE",
+        "hsn_code": "48024000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING LESS THAN 40 G/M2: INDIA PAPER",
+        "hsn_code": "48025410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING LESS THAN 40 G/M2: LITHO AND OFFSET PAPER",
+        "hsn_code": "48025420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING LESS THAN 40 G/M2: DUPLICATING PAPER",
+        "hsn_code": "48025430",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING LESS THAN 40 G/M2: AIRMAIL PAPER",
+        "hsn_code": "48025440",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING LESS THAN 40 G/M2: TISSUE PAPER",
+        "hsn_code": "48025450",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING LESS THAN 40 G/M2: OTHER",
+        "hsn_code": "48025490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2, IN ROLLS : LITHO AND\nOFFSET PAPER",
+        "hsn_code": "48025510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2, IN ROLLS : DRAWING\nPAPER",
+        "hsn_code": "48025520",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2, IN ROLLS :\nDUPLICATING PAPER",
+        "hsn_code": "48025530",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2, IN ROLLS : ACCOUNT\nBOOK PAPER",
+        "hsn_code": "48025540",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2, IN ROLLS : BANK, BOND\nAND CHEQUE PAPER",
+        "hsn_code": "48025550",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2, IN ROLLS : CURRENCY\nNOTE PAPER",
+        "hsn_code": "48025560",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2, IN ROLLS : PAPER FOR\nSECURITY PRINTING, CURRENCY PAPER, STAMP PAPER",
+        "hsn_code": "48025570",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2, IN ROLLS : OTHER",
+        "hsn_code": "48025590",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2, IN SHEETS WITH ONE\nSIDE NOT EXCEEDING 435 MM AND THE OTHER SIDE NOT EXCEEDING 297 MM IN THE\nUNFOLDED STATE : LITHO AND OFFSET PAPER",
+        "hsn_code": "48025610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2, IN SHEETS WITH ONE\nSIDE NOT EXCEEDING 435 MM AND THE OTHER SIDE NOT EXCEEDING 297 MM IN THE\nUNFOLDED STATE : DRAWING PAPER",
+        "hsn_code": "48025620",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2, IN SHEETS WITH ONE\nSIDE NOT EXCEEDING 435 MM AND THE OTHER SIDE NOT EXCEEDING 297 MM IN THE\nUNFOLDED STATE : DUPLICATING PAPER",
+        "hsn_code": "48025630",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2, IN SHEETS WITH ONE\nSIDE NOT EXCEEDING 435 MM AND THE OTHER SIDE NOT EXCEEDING 297 MM IN THE\nUNFOLDED STATE : ACCOUNT BOOK PAPER",
+        "hsn_code": "48025640",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2, IN SHEETS WITH ONE\nSIDE NOT EXCEEDING 435 MM AND THE OTHER SIDE NOT EXCEEDING 297 MM IN THE\nUNFOLDED STATE : BANK, BOND AND CHEQUE PAPER",
+        "hsn_code": "48025650",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2, IN SHEETS WITH ONE\nSIDE NOT EXCEEDING 435 MM AND THE OTHER SIDE NOT EXCEEDING 297 MM IN THE\nUNFOLDED STATE : CURRENCY NOTE PAPER",
+        "hsn_code": "48025660",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2, IN SHEETS WITH ONE\nSIDE NOT EXCEEDING 435 MM AND THE OTHER SIDE NOT EXCEEDING 297 MM IN THE\nUNFOLDED STATE : PAPER FOR SECURITY PRINTING, CURRENCY PAPER, STAMP PAPER",
+        "hsn_code": "48025670",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2, IN SHEETS WITH ONE\nSIDE NOT EXCEEDING 435 MM AND THE OTHER SIDE NOT EXCEEDING 297 MM IN THE\nUNFOLDED STATE : OTHER",
+        "hsn_code": "48025690",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : OTHER, WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2: LITHO AND\nOFFSET PAPER",
+        "hsn_code": "48025710",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : OTHER, WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2: DRAWING\nPAPER",
+        "hsn_code": "48025720",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : OTHER, WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2: DUPLICATING\nPAPER",
+        "hsn_code": "48025730",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : OTHER, WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2: ACCOUNT\nBOOK PAPER",
+        "hsn_code": "48025740",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : OTHER, WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2: BANK, BOND\nAND CHEQUE PAPER",
+        "hsn_code": "48025750",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : OTHER, WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2: CURRENCY\nNOTE PAPER",
+        "hsn_code": "48025760",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : OTHER, WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2: PAPER FOR\nSECURITY PRINTING, CURRENCY PAPER, STAMP PAPER",
+        "hsn_code": "48025770",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : OTHER, WEIGHING 40 G/M2OR MORE BUT NOT MORE THAN 150 G/M2: OTHER",
+        "hsn_code": "48025790",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING MORE THAN 150 G/M2: LITHO AND OFFSET PAPER",
+        "hsn_code": "48025810",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING MORE THAN 150 G/M2: DRAWING PAPER",
+        "hsn_code": "48025820",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING MORE THAN 150 G/M2: DUPLICATING PAPER",
+        "hsn_code": "48025830",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING MORE THAN 150 G/M2: BANK, BOND AND CHEQUE PAPER",
+        "hsn_code": "48025840",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING MORE THAN 150 G/M2: PAPER FOR SECURITY PRINTING, CURRENCY PAPER,\nSTAMP PAPER",
+        "hsn_code": "48025850",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, NOT\nCONTAINING FIBRES OBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS OR OF\nWHICH NOT MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF SUCH\nFIBRES : WEIGHING MORE THAN 150 G/M2: OTHER",
+        "hsn_code": "48025890",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : IN ROLLS : DRAWING PAPER",
+        "hsn_code": "48026110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : IN ROLLS : POSTER PAPER",
+        "hsn_code": "48026120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : IN ROLLS : PRINTING PAPER\nDYED OR MARBLED IN MASS",
+        "hsn_code": "48026130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : IN ROLLS : ACCOUNT BOOK\nPAPER",
+        "hsn_code": "48026140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : IN ROLLS : AUTOMATIC DATA\nPROCESSING MACHINE PAPER",
+        "hsn_code": "48026150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : IN ROLLS : PAPER FOR\nSECURITY PRINTING, CURRENCY PAPER, STAMP PAPER",
+        "hsn_code": "48026160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : IN ROLLS : OTHER",
+        "hsn_code": "48026190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : IN SHEETS WITH ONE SIDE\nNOT EXCEEDING 435 MM AND THE OTHER SIDE NOT EXCEEDING 297 MM IN THE UNFOLDED\nSTATE : DRAWING PAPER",
+        "hsn_code": "48026210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : IN SHEETS WITH ONE SIDE\nNOT EXCEEDING 435 MM AND THE OTHER SIDE NOT EXCEEDING 297 MM IN THE UNFOLDED\nSTATE : POSTER PAPER",
+        "hsn_code": "48026220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : IN SHEETS WITH ONE SIDE\nNOT EXCEEDING 435 MM AND THE OTHER SIDE NOT EXCEEDING 297 MM IN THE UNFOLDED\nSTATE : PRINTING PAPER DYED OR MARBLED IN MASS",
+        "hsn_code": "48026230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : IN SHEETS WITH ONE SIDE\nNOT EXCEEDING 435 MM AND THE OTHER SIDE NOT EXCEEDING 297 MM IN THE UNFOLDED\nSTATE : ACCOUNT BOOK PAPER",
+        "hsn_code": "48026240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : IN SHEETS WITH ONE SIDE\nNOT EXCEEDING 435 MM AND THE OTHER SIDE NOT EXCEEDING 297 MM IN THE UNFOLDED\nSTATE : AUTOMATIC DATA PROCESSING MACHINE PAPER",
+        "hsn_code": "48026250",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : IN SHEETS WITH ONE SIDE\nNOT EXCEEDING 435 MM AND THE OTHER SIDE NOT EXCEEDING 297 MM IN THE UNFOLDED\nSTATE : PAPER FOR SECURITY PRINTING, CURRENCY PAPER, STAMP PAPER",
+        "hsn_code": "48026260",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : IN SHEETS WITH ONE SIDE\nNOT EXCEEDING 435 MM AND THE OTHER SIDE NOT EXCEEDING 297 MM IN THE UNFOLDED\nSTATE : OTHER",
+        "hsn_code": "48026290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : OTHER : DRAWING PAPER",
+        "hsn_code": "48026910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : OTHER : POSTER PAPER",
+        "hsn_code": "48026920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : OTHER : PRINTING PAPER\nDYED OR MARBLED IN MASS",
+        "hsn_code": "48026930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : OTHER : ACCOUNT BOOK\nPAPER",
+        "hsn_code": "48026940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : OTHER : AUTOMATIC DATA\nPROCESSING MACHINE PAPER",
+        "hsn_code": "48026950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : OTHER : PAPER FOR SECURITY\nPRINTING, CURRENCY PAPER, STAMP PAPER",
+        "hsn_code": "48026960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER\nGRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER, IN ROLLS\nOR RECTANGULAR (INCLUDING SQUARE) SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING\n4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD - OTHER PAPER AND PAPERBOARD, OF\nWHICH MORE THAN 10% BY WEIGHT OF THE TOTAL FIBRE CONTENT CONSISTS OF FIBRES\nOBTAINED BY A MECHANICAL OR CHEMI-MECHANICAL PROCESS : OTHER : OTHER",
+        "hsn_code": "48026990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNCOATED PAPER AND PAPERBOARD, OF A KIND USED FOR WRITING, PRINTING OR OTHER GRAPHIC PURPOSES, AND NON-PERFORATED PUNCH CARD AND PUNCH TAPE PAPER , IN ROLLS OR RECTANGULAR (INCLUDING SQUARE)SHEETS OF ANY SIZE, OTHER THAN PAPER OF HEADING 4801 OR 4803; HAND-MADE PAPER AND PAPERBOARD",
+        "hsn_code": "4802",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NEWSPRINT, IN ROLLS OR SHEETS - NEWSPRINT, IN ROLLS OR SHEETS :GLAZED",
+        "hsn_code": "48010010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEWSPRINT, IN ROLLS OR SHEETS - NEWSPRINT, IN ROLLS OR SHEETS :OTHER",
+        "hsn_code": "48010090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NEWSPRINT, IN ROLLS OR SHEETS",
+        "hsn_code": "4801",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RECOVERED (WASTE AND SCRAP) PAPER OR PAPERBOARD",
+        "hsn_code": "4707",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RECOVERED (WASTE AND SCRAP) PAPER OR PAPERBOARD - OTHER, INCLUDING UNSORTED\nWASTE AND SCRAP",
+        "hsn_code": "47079000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RECOVERED (WASTE AND SCRAP) PAPER OR PAPERBOARD - PAPER OR PAPERBOARD MADE\nMAINLY OF MECHANICAL PULP (FOR EXAMPLE, NEWSPAPERS, JOURNALS AND SIMILAR PRINTED\nMATTER)",
+        "hsn_code": "47073000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RECOVERED (WASTE AND SCRAP) PAPER OR PAPERBOARD - OTHER PAPER OR PAPERBOARD\nMADE MAINLY OF BLEACHED CHEMICAL PULP, NOT COLOURED IN THE MASS",
+        "hsn_code": "47072000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RECOVERED (WASTE AND SCRAP) PAPER OR PAPERBOARD - UNBLEACHED KRAFT PAPER OR\nPAPERBOARD OR CORRUGATED PAPER OR PAPERBOARD",
+        "hsn_code": "47071000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PULPS OF FIBRES DERIVED FROM RECOVERED (WASTE AND SCRAP) PAPER OR PAPERBOARD OR OF OTHER FIBROUS CELLULOSIC MATERIAL",
+        "hsn_code": "4706",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PULPS OF FIBRES DERIVED FROM RECOVERED (WASTE AND SCRAP) PAPER OR PAPERBOARD OR\nOF OTHER FIBROUS CELLULOSIC MATERIAL - OTHER : SEMI-CHEMICAL",
+        "hsn_code": "47069300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PULPS OF FIBRES DERIVED FROM RECOVERED (WASTE AND SCRAP) PAPER OR PAPERBOARD OR\nOF OTHER FIBROUS CELLULOSIC MATERIAL - OTHER : CHEMICAL",
+        "hsn_code": "47069200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PULPS OF FIBRES DERIVED FROM RECOVERED (WASTE AND SCRAP) PAPER OR PAPERBOARD OR\nOF OTHER FIBROUS CELLULOSIC MATERIAL - OTHER : MECHANICAL",
+        "hsn_code": "47069100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PULPS OF FIBRES DERIVED FROM RECOVERED (WASTE AND SCRAP) PAPER OR PAPERBOARD OR\nOF OTHER FIBROUS CELLULOSIC MATERIAL - OTHER OF BAMBOO",
+        "hsn_code": "47063000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PULPS OF FIBRES DERIVED FROM RECOVERED (WASTE AND SCRAP) PAPER OR PAPERBOARD OR\nOF OTHER FIBROUS CELLULOSIC MATERIAL - PULPS OF FIBRES DERIVED FROM RECOVERED\n(WASTE AND SCRAP) PAPER OR PAPERBOARD",
+        "hsn_code": "47062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PULPS OF FIBRES DERIVED FROM RECOVERED (WASTE AND SCRAP) PAPER OR PAPERBOARD OR\nOF OTHER FIBROUS CELLULOSIC MATERIAL - COTTON LINTERS PULP",
+        "hsn_code": "47061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Wood pulp obtained by a combination of mechanical and chemical pulping processes",
+        "hsn_code": "4705",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WOOD PULP OBTAINED BY A COMBINATION OF MECHANICAL AND CHEMICAL PULPING\nPROCESSES",
+        "hsn_code": "47050000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHEMICAL WOOD PULP, SULPHITE, OTHER THAN DISSOLVING GRADES",
+        "hsn_code": "4704",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHEMICAL WOOD PULP, SULPHITE, OTHER THAN DISSOLVING GRADES - SEMI-BLEACHED OR\nBLEACHED : NON-CONIFEROUS",
+        "hsn_code": "47042900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHEMICAL WOOD PULP, SULPHITE, OTHER THAN DISSOLVING GRADES - SEMI-BLEACHED OR\nBLEACHED : CONIFEROUS",
+        "hsn_code": "47042100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHEMICAL WOOD PULP, SULPHITE, OTHER THAN DISSOLVING GRADES - UNBLEACHED : NON\nCONIFEROUS",
+        "hsn_code": "47041900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHEMICAL WOOD PULP, SULPHITE, OTHER THAN DISSOLVING GRADES - UNBLEACHED :\nCONIFEROUS",
+        "hsn_code": "47041100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHEMICAL WOOD PULP, SODA OR SULPHATE, OTHER THAN DISSOLVING GRADES",
+        "hsn_code": "4703",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHEMICAL WOOD PULP, SODA OR SULPHATE, OTHERTHAN DISSOLVING GRADES - SEMI\nBLEACHED OR BLEACHED : NON-CONIFEROUS",
+        "hsn_code": "47032900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHEMICAL WOOD PULP, SODA OR SULPHATE, OTHERTHAN DISSOLVING GRADES - SEMI\nBLEACHED OR BLEACHED : CONIFEROUS",
+        "hsn_code": "47032100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHEMICAL WOOD PULP, SODA OR SULPHATE, OTHERTHAN DISSOLVING GRADES - UNBLEACHED :\nNON-CONIFEROUS",
+        "hsn_code": "47031900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHEMICAL WOOD PULP, SODA OR SULPHATE, OTHERTHAN DISSOLVING GRADES - UNBLEACHED :\nCONIFEROUS",
+        "hsn_code": "47031100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Chemical wood pulp, dissolving grades",
+        "hsn_code": "4702",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHEMICAL WOOD PULP, DISSOLVING GRADES",
+        "hsn_code": "47020000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Mechanical wood pulp",
+        "hsn_code": "4701",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MECHANICAL WOOD PULP",
+        "hsn_code": "47010000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AGGLOMERATED CORK (WITH OR WITHOUT A BINDING SUBSTANCE) AND ARTICLES OF AGGLOMERATED CORK",
+        "hsn_code": "4504",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AGGLOMERATED CORK (WITH OR WITHOUT A BINDING SUBSTANCE) AND ARTICLES OF\nAGGLOMERATED CORK - OTHER",
+        "hsn_code": "45049000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AGGLOMERATED CORK (WITH OR WITHOUT A BINDING SUBSTANCE) AND ARTICLES OF\nAGGLOMERATED CORK - BLOCKS, PLATES, SHEETS AND STRIP; TILES OF ANY SHAPE; SOLID\nCYLINDERS, INCLUDING DISCS : OTHER",
+        "hsn_code": "45041090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AGGLOMERATED CORK (WITH OR WITHOUT A BINDING SUBSTANCE) AND ARTICLES OF\nAGGLOMERATED CORK - BLOCKS, PLATES, SHEETS AND STRIP; TILES OF ANY SHAPE; SOLID\nCYLINDERS, INCLUDING DISCS : SLABS",
+        "hsn_code": "45041020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AGGLOMERATED CORK (WITH OR WITHOUT A BINDING SUBSTANCE) AND ARTICLES OF\nAGGLOMERATED CORK - BLOCKS, PLATES, SHEETS AND STRIP; TILES OF ANY SHAPE; SOLID\nCYLINDERS, INCLUDING DISCS : SHEETS",
+        "hsn_code": "45041010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF NATURAL CORK",
+        "hsn_code": "4503",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF NATURAL CORK - OTHER : OTHER",
+        "hsn_code": "45039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF NATURAL CORK - OTHER : SHUTTLECOCK CORK BOTTOM",
+        "hsn_code": "45039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF NATURAL CORK - CORKS AND STOPPERS",
+        "hsn_code": "45031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NATURAL CORK, RAW OR SIMPLY SDF KSDF JKLSD PREPARED; WASTE CORK; CRUSHED, GRANULATED OR GROUND CORK",
+        "hsn_code": "4501",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NATURAL CORK, RAW OR SIMPLY PREPARED; WASTE CORK; CRUSHED, GRANULATED OR\nGROUND CORK - OTHER",
+        "hsn_code": "45019000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NATURAL CORK, RAW OR SIMPLY PREPARED; WASTE CORK; CRUSHED, GRANULATED OR\nGROUND CORK - NATURAL CORK, RAW OR SIMPLY PREPARED",
+        "hsn_code": "45011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF WOOD",
+        "hsn_code": "4421",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF WOOD - OTHER : OTHER",
+        "hsn_code": "44219090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF WOOD - OTHER : ARTICLES OF DENSIFIED WOOD NOT ELSEWHERE\nINCLUDED OR SPECIFIED",
+        "hsn_code": "44219070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF WOOD - OTHER : PARTS OF DOMESTIC DECORATIVE ARTICLES USED AS\nTABLEWARE AND KITCHENWARE",
+        "hsn_code": "44219060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF WOOD - OTHER : PARTS OF WOOD, NAMELY OARS, PADDLES AND RUDDERS\nFOR SHIPS, BOATS AND OTHER SIMILAR FLOATING STRUCTURES",
+        "hsn_code": "44219050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF WOOD - OTHER : PENCIL SLATES",
+        "hsn_code": "44219040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF WOOD - OTHER : MATCH SPLINTS",
+        "hsn_code": "44219030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF WOOD - OTHER : WOOD PAVING BLOCKS",
+        "hsn_code": "44219020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF WOOD - OTHER : SPOOLS, COPS, BOBBINS, SEWING THREAD REELS AND THE\nLIKE, OF TURNED WOOD: OTHER",
+        "hsn_code": "44219019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF WOOD - OTHER : SPOOLS, COPS, BOBBINS, SEWING THREAD REELS AND THE\nLIKE, OF TURNED WOOD: FOR OTHER MACHINERY",
+        "hsn_code": "44219014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF WOOD - OTHER : SPOOLS, COPS, BOBBINS, SEWING THREAD REELS AND THE\nLIKE, OF TURNED WOOD: FOR SILK REGENERATED AND SYNTHETIC FIBRES MACHINERY",
+        "hsn_code": "44219013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF WOOD - OTHER : SPOOLS, COPS, BOBBINS, SEWING THREAD REELS AND THE\nLIKE, OF TURNED WOOD: FOR JUTE MACHINERY",
+        "hsn_code": "44219012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF WOOD - OTHER : SPOOLS, COPS, BOBBINS, SEWING THREAD REELS AND THE\nLIKE, OF TURNED WOOD: FOR COTTON MACHINERY",
+        "hsn_code": "44219011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF WOOD - CLOTHES HANGERS",
+        "hsn_code": "44211000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD MARQUETRY AND INLAID WOOD; CASKETS AND CASES FOR JEWELLERY OR CUTLERY, AND\nSIMILAR ARTICLES, OF WOOD; STATUETTES AND OTHER ORNAMENTS, OF WOOD; WOODEN\nARTICLES OF FURNITURE NOT FALLING IN CHAPTER 94 - STATUETTES AND OTHER ORNAMENTS,\nOF WOOD",
+        "hsn_code": "44201000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WOOD MARQUETRY AND INLAID WOOD; CASKETS AND CASES FOR JEWELLERY OR CUTLERY, AND\nSIMILAR ARTICLES, OF WOOD; STATUETTES AND OTHER ORNAMENTS, OF WOOD; WOODEN\nARTICLES OF FURNITURE NOT FALLING IN CHAPTER 94 - OTHER : WOOD MARQUETRY AND\nINLAID WOOD",
+        "hsn_code": "44209010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WOOD MARQUETRY AND INLAID WOOD; CASKETS AND CASES FOR JEWELLERY OR CUTLERY, AND\nSIMILAR ARTICLES, OF WOOD; STATUETTES AND OTHER ORNAMENTS, OF WOOD; WOODEN\nARTICLES OF FURNITURE NOT FALLING IN CHAPTER 94 - OTHER : OTHER",
+        "hsn_code": "44209090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WOOD MAREQUETRY AND INLAID WOOD; CASKETS AND CASES FOR JEWELLERY OR CUTLERY, AND SIMILAR ARTICLES, OF WOOD; STATUETTES AND OTHER ORNAMENTS, OF WOOD; WOODEN ARTICLES OF FURNITURE NOT FALLING IN CHAPTER 94",
+        "hsn_code": "4420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TABLEWARE AND KITCHENWARE, OF WOOD",
+        "hsn_code": "4419",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TABLEWARE AND KITCHENWARE, OF WOOD - TABLEWARE AND KITCHENWARE, OF WOOD:\nKITCHENWARE",
+        "hsn_code": "44190020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TABLEWARE AND KITCHENWARE, OF WOOD - TABLEWARE AND KITCHENWARE, OF WOOD:\nTABLEWARE",
+        "hsn_code": "44190010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BUILDERS’ JOINERY AND CARPENTRY OF WOOD, INCLUDING CELLULAR WOOD PANELS, ASSEMBLED FLOORING PANELS, SHINGLES AND SHAKES",
+        "hsn_code": "4418",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUILDERS` JOINERY AND CARPENTRY OF WOOD, INCLUDING CELLULAR WOOD PANELS,\nASSEMBLED FLOORING PANELS, SHINGLES AND SHAKES - SHINGLES AND SHAKES",
+        "hsn_code": "44189000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUILDERS` JOINERY AND CARPENTRY OF WOOD, INCLUDING CELLULAR WOOD PANELS,\nASSEMBLED FLOORING PANELS, SHINGLES AND SHAKES - ASSEMBLED FLOORING PANELS :--\nOTHER",
+        "hsn_code": "44187900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUILDERS` JOINERY AND CARPENTRY OF WOOD, INCLUDING CELLULAR WOOD PANELS,\nASSEMBLED FLOORING PANELS, SHINGLES AND SHAKES - ASSEMBLED FLOORING PANELS :--\nOTHER, MULTILAYER",
+        "hsn_code": "44187200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUILDERS` JOINERY AND CARPENTRY OF WOOD, INCLUDING CELLULAR WOOD PANELS,\nASSEMBLED FLOORING PANELS, SHINGLES AND SHAKES - ASSEMBLED FLOORING PANELS :-- FOR\nMOSAIC FLOORS",
+        "hsn_code": "44187100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUILDERS` JOINERY AND CARPENTRY OF WOOD, INCLUDING CELLULAR WOOD PANELS,\nASSEMBLED FLOORING PANELS, SHINGLES AND SHAKES - POSTS AND BEAMS",
+        "hsn_code": "44186000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUILDERS` JOINERY AND CARPENTRY OF WOOD, INCLUDING CELLULAR WOOD PANELS,\nASSEMBLED FLOORING PANELS, SHINGLES AND SHAKES - SHINGLES AND SHAKES",
+        "hsn_code": "44185000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUILDERS` JOINERY AND CARPENTRY OF WOOD, INCLUDING CELLULAR WOOD PANELS,\nASSEMBLED FLOORING PANELS, SHINGLES AND SHAKES - SHUTTERING FOR CONCRETE\nCONSTRUCTIONAL WORK",
+        "hsn_code": "44184000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUILDERR JOINERY AND CARPENTRY OF WOOD, INCLUDING CELLULAR WOOD PANELS,\nASSEMBLED PARQUET PANELS, SHINGLES AND SHAKES - PARQUET PANELS",
+        "hsn_code": "44183000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUILDERS` JOINERY AND CARPENTRY OF WOOD, INCLUDING CELLULAR WOOD PANELS,\nASSEMBLED FLOORING PANELS, SHINGLES AND SHAKES - DOORS AND THEIR FRAMES AND\nTHRESHOLDS: OTHER",
+        "hsn_code": "44182090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUILDERS` JOINERY AND CARPENTRY OF WOOD, INCLUDING CELLULAR WOOD PANELS,\nASSEMBLED FLOORING PANELS, SHINGLES AND SHAKES - DOORS AND THEIR FRAMES AND\nTHRESHOLDS: FRAMES AND THRESHOLDS OF FLUSH DOORS",
+        "hsn_code": "44182020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUILDERS` JOINERY AND CARPENTRY OF WOOD, INCLUDING CELLULAR WOOD PANELS,\nASSEMBLED FLOORING PANELS, SHINGLES AND SHAKES - DOORS AND THEIR FRAMES AND\nTHRESHOLDS: FLUSH DOORS",
+        "hsn_code": "44182010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUILDERS` JOINERY AND CARPENTRY OF WOOD, INCLUDING CELLULAR WOOD PANELS,\nASSEMBLED FLOORING PANELS, SHINGLES AND SHAKES WINDOWS, FRENCH-WINDOWS AND\nTHEIR FRAMES",
+        "hsn_code": "44181000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Tools, tool bodies, tool handles, broom or brush bodies and handles, of wood ; boot or shoe lasts and trees , of wood",
+        "hsn_code": "4417",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TOOLS, TOOL BODIES, TOOL HANDLES, BROOM OR BRUSH BODIES AND HANDLES, OF WOOD;\nBOOT OR SHOE LASTS AND TREES, OF WOOD",
+        "hsn_code": "44170000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PACKING CASES, BOXES, CRATES, DRUMS AND SIMILAR PACKINGS, OF WOOD ; CABLE-DRUMS OF WOOD ; PALLETS, BOX PALLETS AND OTHER LOAD BOARDS, OF WOOD; PALLET COLLARS OF WOOD",
+        "hsn_code": "4415",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PACKING CASES, BOXES, CRATES, DRUMS AND SIMILAR PACKINGS, OF WOOD; CABLE-DRUMS OF\nWOOD; PALLETS, BOX PALLETS AND OTHER LOAD BOARDS, OF WOOD; PALLET COLLARS OF\nWOOD - PALLETS, BOX PALLETS AND OTHER LOAD BOARDS; PALLET COLLARS",
+        "hsn_code": "44152000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PACKING CASES, BOXES, CRATES, DRUMS AND SIMILAR PACKINGS, OF WOOD; CABLE-DRUMS OF\nWOOD; PALLETS, BOX PALLETS AND OTHER LOAD BOARDS, OF WOOD; PALLET COLLARS OF\nWOOD - CASES, BOXES, CRATES, DRUMS AND SIMILAR PACKINGS; CABLE-DRUMS",
+        "hsn_code": "44151000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Wooden frames for paintings, photographs, mirrors or similar objects",
+        "hsn_code": "4414",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOODEN FRAMES FOR PAINTINGS, PHOTOGRAPHS, MIRRORS OR SIMILAR OBJECTS",
+        "hsn_code": "44140000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Densified wood, in blocks, plates, strips, or profile shapes",
+        "hsn_code": "4413",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DENSIFIED WOOD, IN BLOCKS, PLATES, STRIPS, OR PROFILE SHAPES",
+        "hsn_code": "44130000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD",
+        "hsn_code": "4412",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER: -- OTHER:--- OTHER",
+        "hsn_code": "44129990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER : OTHER : CUTTINGS\nAND TRIMMINGS OF PLYWOOD OF WIDTH NOT EXCEEDING 5 CM",
+        "hsn_code": "44129950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER: -- OTHER:--- CUTTINGS\nAND TRIMMINGS OF PLYWOOD OF WIDTH NOT EXCEEDING 5CM",
+        "hsn_code": "44129940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER: -- OTHER:--- MARINE\nAND AIRCRAFT PLYWOOD",
+        "hsn_code": "44129930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER: -- OTHER:--- TEA\nCHEST PANEL OR SHOOKS, PACKED IN SETS",
+        "hsn_code": "44129920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER: -- OTHER:---\nDECORATIVE PLYWOOD",
+        "hsn_code": "44129910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER: -- BLOCKBOARD,\nLAMINBOARD AND BATTENBOARD",
+        "hsn_code": "44129400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER : OTHER, CONTAINING\nAT LEAST ONE LAYER OF PARTICLE BOARD: OTHER",
+        "hsn_code": "44129390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER : OTHER, CONTAINING\nAT LEAST ONE LAYER OF PARTICLE BOARD: CUTTINGS AND TRIMMINGS OF PLYWOOD OF WIDTH\nNOT EXCEEDING 5 CM",
+        "hsn_code": "44129340",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER : OTHER, CONTAINING\nAT LEAST ONE LAYER OF PARTICLE BOARD: MARINE AND AIRCRAFT PLYWOOD",
+        "hsn_code": "44129330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER : OTHER, CONTAINING\nAT LEAST ONE LAYER OF PARTICLE BOARD: TEA CHEST PANELS OR SHOOKS, PACKED IN SETS",
+        "hsn_code": "44129320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER : OTHER, CONTAINING\nAT LEAST ONE LAYER OF PARTICLE BOARD: DECORATIVE PLYWOOD",
+        "hsn_code": "44129310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER : WITH AT LEAST ONE\nPLY OF TROPICAL WOOD SPECIFIED IN SUB-HEADING NOTE 1 TO THIS CHAPTER: OTHER",
+        "hsn_code": "44129290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER : WITH AT LEAST ONE\nPLY OF TROPICAL WOOD SPECIFIED IN SUB-HEADING NOTE 1 TO THIS CHAPTER: CUTTINGS AND\nTRIMMINGS OF PLYWOOD OF WIDTH NOT EXCEEDING 5 CM",
+        "hsn_code": "44129250",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER : WITH AT LEAST ONE\nPLY OF TROPICAL WOOD SPECIFIED IN SUB-HEADING NOTE 1 TO THIS CHAPTER: MARINE AND\nAIRCRAFT PLYWOOD",
+        "hsn_code": "44129240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER : WITH AT LEAST ONE\nPLY OF TROPICAL WOOD SPECIFIED IN SUB-HEADING NOTE 1 TO THIS CHAPTER: TEA CHEST\nPANELS OR SHOOKS, PACKED IN SETS",
+        "hsn_code": "44129230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER : WITH AT LEAST ONE\nPLY OF TROPICAL WOOD SPECIFIED IN SUB-HEADING NOTE 1 TO THIS CHAPTER: DECORATIVE\nPLYWOOD",
+        "hsn_code": "44129220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER : WITH AT LEAST ONE\nPLY OF TROPICAL WOOD SPECIFIED IN SUB-HEADING NOTE 1 TO THIS CHAPTER: ELASTIC\nLAMINATED PLYWOOD",
+        "hsn_code": "44129210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD -- OTHER, WITH AT LEAST ONE\nOUTER PLY OF NON-CONIFEROUS WOOD:-- OTHER:--- OTHER",
+        "hsn_code": "44123990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD -- OTHER, WITH AT LEAST ONE\nOUTER PLY OF NON-CONIFEROUS WOOD:-- OTHER:--- CUTTINGS AND TRIMMINGS OF PLYWOOD\nOF WIDTH NOT EXCEEDING 5CM",
+        "hsn_code": "44123940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD -- OTHER, WITH AT LEAST ONE\nOUTER PLY OF NON-CONIFEROUS WOOD:-- OTHER:--- MARINE AND AIRCRAFT PLYWOOD",
+        "hsn_code": "44123930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD -- OTHER, WITH AT LEAST ONE\nOUTER PLY OF NON-CONIFEROUS WOOD:-- OTHER:--- TEA CHEST PANELS OR SHOOKS, PACKED\nIN SETS",
+        "hsn_code": "44123920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD -- OTHER, WITH AT LEAST ONE\nOUTER PLY OF NON-CONIFEROUS WOOD:-- OTHER:--- DECORATIVE PLYWOOD",
+        "hsn_code": "44123910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD -- OTHER, WITH AT LEAST ONE\nOUTERPLY OF NON-CONIFEROUS WOOD:--- OTHER",
+        "hsn_code": "44123290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD -- OTHER, WITH AT LEAST ONE\nOUTERPLY OF NON-CONIFEROUS WOOD:CUTTINGS AND TRIMMINGS OF PLYWOOD OF WIDTH\nNOT EXCEEDING 5CM",
+        "hsn_code": "44123240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD -- OTHER, WITH AT LEAST ONE\nOUTERPLY OF NON-CONIFEROUS WOOD:--- MARINE AND AIRCRAFT PLYWOOD",
+        "hsn_code": "44123230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD -- OTHER, WITH AT LEAST ONE\nOUTERPLY OF NON-CONIFEROUS WOOD:--- TEA CHEST PANELS OR SHOOKS, PACKED IN SETS",
+        "hsn_code": "44123220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD -- OTHER, WITH AT LEAST ONE\nOUTERPLY OF NON-CONIFEROUS WOOD:--- DECORATIVE PLYWOOD",
+        "hsn_code": "44123210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD -- OTHER PLYWOOD,\nCONSISTING SOLELY OF SHEETS OF WOOD (OTHER THAN BAMBOO), EACH PLY NOT EXCEEDING 6\nMM THICKNESS: WITH AT LEAST ONE OUTER PLY OF TROPICAL WOOD SPECIFIED IN SUB\nHEADING NOTE 1 TO THIS CHAPTER, NAMELY:- ABURA, ACAJOU, D?AFRIQUE, AFRORMOSIA,\nAKO, ALAN, ANDIROBA, ANINGRE, AVODIRE, AZOBE, BALAU, BALSA, BOSSE CLAIR, BOSSE\nFONCE, CATIVO, CEDRO, DABEMA, DARK RED MERANTI, DIBETOU, DOUSSIE, FRAMIRE, FREIJO,\nFROMAGER, FUMA, GERONGGANG, ILOMBA, IMBUIA, IPE, IROKO, JABOTY, JELUTONG,\nJEQUITIBA, JONGKONG, KAPUR, KEMPAS, KERUING, KOSIPO, KOTIBE, KOTO, LIGHT RED\nMERANTI, LIMBA, LOURO, MACARANDUBA, MAHOGONY, MAKORE, MANDIOQUEIRA,\nMANSONIA, MENGKULANG, MERANTI BAKAU, MERAWAN, MERBAU, MERPAUH, MERSAWA,\nMOABI, NIANGON, NYATOH, OBECHE, OKOUME, ONZABILI, OREY, OVENGKOL, OZIGO, PADAUK,\nPALDAO, PALISSANDRE DE GUATEMALA, PALISSANDRE DE PARA, PALISSANDRE DE RIO,\nPALISSANDRE DE ROSE, PAU AMARELO, PAU MARFIM, PULAI, PUNAH, QUARUBA, RAMIN,\nSAPELLI, SAQUI- SAQUI, SEPETIR, SIPO, SUCUPIRA, SUREN, TAUARI,TEAK, TIAMA, TOLA, VIROLA,\nWHITE LAUAN, WHITE MERANTI, WHITE SERAYA, YELLOW MERANTI :--- OTHER",
+        "hsn_code": "44123190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD -- OTHER PLYWOOD,\nCONSISTING SOLELY OF SHEETS OF WOOD (OTHER THAN BAMBOO), EACH PLY NOT EXCEEDING 6\nMM THICKNESS: WITH AT LEAST ONE OUTER PLY OF TROPICAL WOOD SPECIFIED IN SUB\nHEADING NOTE 1 TO THIS CHAPTER, NAMELY:- ABURA, ACAJOU, D?AFRIQUE, AFRORMOSIA,\nAKO, ALAN, ANDIROBA, ANINGRE, AVODIRE, AZOBE, BALAU, BALSA, BOSSE CLAIR, BOSSE\nFONCE, CATIVO, CEDRO, DABEMA, DARK RED MERANTI, DIBETOU, DOUSSIE, FRAMIRE, FREIJO,\nFROMAGER, FUMA, GERONGGANG, ILOMBA, IMBUIA, IPE, IROKO, JABOTY, JELUTONG,\nJEQUITIBA, JONGKONG, KAPUR, KEMPAS, KERUING, KOSIPO, KOTIBE, KOTO, LIGHT RED\nMERANTI, LIMBA, LOURO, MACARANDUBA, MAHOGONY, MAKORE, MANDIOQUEIRA,\nMANSONIA, MENGKULANG, MERANTI BAKAU, MERAWAN, MERBAU, MERPAUH, MERSAWA,\nMOABI, NIANGON, NYATOH, OBECHE, OKOUME, ONZABILI, OREY, OVENGKOL, OZIGO, PADAUK,\nPALDAO, PALISSANDRE DE GUATEMALA, PALISSANDRE DE PARA, PALISSANDRE DE RIO,\nPALISSANDRE DE ROSE, PAU AMARELO, PAU MARFIM, PULAI, PUNAH, QUARUBA, RAMIN,\nSAPELLI, SAQUI- SAQUI, SEPETIR, SIPO, SUCUPIRA, SUREN, TAUARI,TEAK, TIAMA, TOLA, VIROLA,\nWHITE LAUAN, WHITE MERANTI, WHITE SERAYA, YELLOW MERANTI :--- CUTTINGS AND\nTRIMMINGS OF PLYWOOD OF WIDTH NOT EXCEEDING 5CM",
+        "hsn_code": "44123150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD -- OTHER PLYWOOD,\nCONSISTING SOLELY OF SHEETS OF WOOD (OTHER THAN BAMBOO), EACH PLY NOT EXCEEDING 6\nMM THICKNESS: WITH AT LEAST ONE OUTER PLY OF TROPICAL WOOD SPECIFIED IN SUB\nHEADING NOTE 1 TO THIS CHAPTER, NAMELY:- ABURA, ACAJOU, D?AFRIQUE, AFRORMOSIA,\nAKO, ALAN, ANDIROBA, ANINGRE, AVODIRE, AZOBE, BALAU, BALSA, BOSSE CLAIR, BOSSE\nFONCE, CATIVO, CEDRO, DABEMA, DARK RED MERANTI, DIBETOU, DOUSSIE, FRAMIRE, FREIJO,\nFROMAGER, FUMA, GERONGGANG, ILOMBA, IMBUIA, IPE, IROKO, JABOTY, JELUTONG,\nJEQUITIBA, JONGKONG, KAPUR, KEMPAS, KERUING, KOSIPO, KOTIBE, KOTO, LIGHT RED\nMERANTI, LIMBA, LOURO, MACARANDUBA, MAHOGONY, MAKORE, MANDIOQUEIRA,\nMANSONIA, MENGKULANG, MERANTI BAKAU, MERAWAN, MERBAU, MERPAUH, MERSAWA,\nMOABI, NIANGON, NYATOH, OBECHE, OKOUME, ONZABILI, OREY, OVENGKOL, OZIGO, PADAUK,\nPALDAO, PALISSANDRE DE GUATEMALA, PALISSANDRE DE PARA, PALISSANDRE DE RIO,\nPALISSANDRE DE ROSE, PAU AMARELO, PAU MARFIM, PULAI, PUNAH, QUARUBA, RAMIN,\nSAPELLI, SAQUI- SAQUI, SEPETIR, SIPO, SUCUPIRA, SUREN, TAUARI,TEAK, TIAMA, TOLA, VIROLA,\nWHITE LAUAN, WHITE MERANTI, WHITE SERAYA, YELLOW MERANTI :--- MARINE AND AIRCRAFT\nPLYWOOD",
+        "hsn_code": "44123140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD -- OTHER PLYWOOD,\nCONSISTING SOLELY OF SHEETS OF WOOD (OTHER THAN BAMBOO), EACH PLY NOT EXCEEDING 6\nMM THICKNESS: WITH AT LEAST ONE OUTER PLY OF TROPICAL WOOD SPECIFIED IN SUB\nHEADING NOTE 1 TO THIS CHAPTER, NAMELY:- ABURA, ACAJOU, D?AFRIQUE, AFRORMOSIA,\nAKO, ALAN, ANDIROBA, ANINGRE, AVODIRE, AZOBE, BALAU, BALSA, BOSSE CLAIR, BOSSE\nFONCE, CATIVO, CEDRO, DABEMA, DARK RED MERANTI, DIBETOU, DOUSSIE, FRAMIRE, FREIJO,\nFROMAGER, FUMA, GERONGGANG, ILOMBA, IMBUIA, IPE, IROKO, JABOTY, JELUTONG,\nJEQUITIBA, JONGKONG, KAPUR, KEMPAS, KERUING, KOSIPO, KOTIBE, KOTO, LIGHT RED\nMERANTI, LIMBA, LOURO, MACARANDUBA, MAHOGONY, MAKORE, MANDIOQUEIRA,\nMANSONIA, MENGKULANG, MERANTI BAKAU, MERAWAN, MERBAU, MERPAUH, MERSAWA,\nMOABI, NIANGON, NYATOH, OBECHE, OKOUME, ONZABILI, OREY, OVENGKOL, OZIGO, PADAUK,\nPALDAO, PALISSANDRE DE GUATEMALA, PALISSANDRE DE PARA, PALISSANDRE DE RIO,\nPALISSANDRE DE ROSE, PAU AMARELO, PAU MARFIM, PULAI, PUNAH, QUARUBA, RAMIN,\nSAPELLI, SAQUI- SAQUI, SEPETIR, SIPO, SUCUPIRA, SUREN, TAUARI,TEAK, TIAMA, TOLA, VIROLA,\nWHITE LAUAN, WHITE MERANTI, WHITE SERAYA, YELLOW MERANTI :--- OTHER TEA CHEST\nPANELS",
+        "hsn_code": "44123130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD -- OTHER PLYWOOD,\nCONSISTING SOLELY OF SHEETS OF WOOD (OTHER THAN BAMBOO), EACH PLY NOT EXCEEDING 6\nMM THICKNESS: WITH AT LEAST ONE OUTER PLY OF TROPICAL WOOD SPECIFIED IN SUB\nHEADING NOTE 1 TO THIS CHAPTER, NAMELY:- ABURA, ACAJOU, D?AFRIQUE, AFRORMOSIA,\nAKO, ALAN, ANDIROBA, ANINGRE, AVODIRE, AZOBE, BALAU, BALSA, BOSSE CLAIR, BOSSE\nFONCE, CATIVO, CEDRO, DABEMA, DARK RED MERANTI, DIBETOU, DOUSSIE, FRAMIRE, FREIJO,\nFROMAGER, FUMA, GERONGGANG, ILOMBA, IMBUIA, IPE, IROKO, JABOTY, JELUTONG,\nJEQUITIBA, JONGKONG, KAPUR, KEMPAS, KERUING, KOSIPO, KOTIBE, KOTO, LIGHT RED\nMERANTI, LIMBA, LOURO, MACARANDUBA, MAHOGONY, MAKORE, MANDIOQUEIRA,\nMANSONIA, MENGKULANG, MERANTI BAKAU, MERAWAN, MERBAU, MERPAUH, MERSAWA,\nMOABI, NIANGON, NYATOH, OBECHE, OKOUME, ONZABILI, OREY, OVENGKOL, OZIGO, PADAUK,\nPALDAO, PALISSANDRE DE GUATEMALA, PALISSANDRE DE PARA, PALISSANDRE DE RIO,\nPALISSANDRE DE ROSE, PAU AMARELO, PAU MARFIM, PULAI, PUNAH, QUARUBA, RAMIN,\nSAPELLI, SAQUI- SAQUI, SEPETIR, SIPO, SUCUPIRA, SUREN, TAUARI,TEAK, TIAMA, TOLA, VIROLA,\nWHITE LAUAN, WHITE MERANTI, WHITE SERAYA, YELLOW MERANTI :--- TEA CHEST PANELS OR\nSHOOKS, PACKED IN SETS",
+        "hsn_code": "44123120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD -- OTHER PLYWOOD,\nCONSISTING SOLELY OF SHEETS OF WOOD (OTHER THAN BAMBOO), EACH PLY NOT EXCEEDING 6\nMM THICKNESS: WITH AT LEAST ONE OUTER PLY OF TROPICAL WOOD SPECIFIED IN SUB\nHEADING NOTE 1 TO THIS CHAPTER, NAMELY:- ABURA, ACAJOU, D?AFRIQUE, AFRORMOSIA,\nAKO, ALAN, ANDIROBA, ANINGRE, AVODIRE, AZOBE, BALAU, BALSA, BOSSE CLAIR, BOSSE\nFONCE, CATIVO, CEDRO, DABEMA, DARK RED MERANTI, DIBETOU, DOUSSIE, FRAMIRE, FREIJO,\nFROMAGER, FUMA, GERONGGANG, ILOMBA, IMBUIA, IPE, IROKO, JABOTY, JELUTONG,\nJEQUITIBA, JONGKONG, KAPUR, KEMPAS, KERUING, KOSIPO, KOTIBE, KOTO, LIGHT RED\nMERANTI, LIMBA, LOURO, MACARANDUBA, MAHOGONY, MAKORE, MANDIOQUEIRA,\nMANSONIA, MENGKULANG, MERANTI BAKAU, MERAWAN, MERBAU, MERPAUH, MERSAWA,\nMOABI, NIANGON, NYATOH, OBECHE, OKOUME, ONZABILI, OREY, OVENGKOL, OZIGO, PADAUK,\nPALDAO, PALISSANDRE DE GUATEMALA, PALISSANDRE DE PARA, PALISSANDRE DE RIO,\nPALISSANDRE DE ROSE, PAU AMARELO, PAU MARFIM, PULAI, PUNAH, QUARUBA, RAMIN,\nSAPELLI, SAQUI- SAQUI, SEPETIR, SIPO, SUCUPIRA, SUREN, TAUARI,TEAK, TIAMA, TOLA, VIROLA,\nWHITE LAUAN, WHITE MERANTI, WHITE SERAYA, YELLOW MERANTI :--- DECORATIVE PLYWOOD",
+        "hsn_code": "44123110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER, WITH AT LEAST ONE\nOUTER PLY OF NONCONIFEROUS WOOD : OTHER : OTHER",
+        "hsn_code": "44122990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER, WITH AT LEAST ONE\nOUTER PLY OF NONCONIFEROUS WOOD : OTHER : CUTTINGS AND TRIMMINGS OF PLYWOOD OF\nWIDTH NOT EXCEEDING 5 CM",
+        "hsn_code": "44122950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER, WITH AT LEAST ONE\nOUTER PLY OF NONCONIFEROUS WOOD : OTHER : MARINE AND AIRCRAFT PLYWOOD",
+        "hsn_code": "44122940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER, WITH AT LEAST ONE\nOUTER PLY OF NONCONIFEROUS WOOD : OTHER : TEA CHEST PANELS OR SHOOKS, PACKED IN\nSETS",
+        "hsn_code": "44122930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER, WITH AT LEAST ONE\nOUTER PLY OF NONCONIFEROUS WOOD : OTHER : DECORATIVE PLYWOOD",
+        "hsn_code": "44122920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER, WITH AT LEAST ONE\nOUTER PLY OF NONCONIFEROUS WOOD : OTHER : ELASTIC LAMINATED PLYWOOD",
+        "hsn_code": "44122910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER, WITH AT LEAST ONE\nOUTER PLY OF NONCONIFEROUS WOOD : OTHER, CONTAINING AT LEAST ONE LAYER OF\nPARTICLE BOARD: OTHER",
+        "hsn_code": "44122390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER, WITH AT LEAST ONE\nOUTER PLY OF NONCONIFEROUS WOOD : OTHER, CONTAINING AT LEAST ONE LAYER OF\nPARTICLE BOARD: CUTTINGS AND TRIMMINGS OF PLYWOOD OF WIDTH NOT EXCEEDING 5 CM",
+        "hsn_code": "44122340",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER, WITH AT LEAST ONE\nOUTER PLY OF NONCONIFEROUS WOOD : OTHER, CONTAINING AT LEAST ONE LAYER OF\nPARTICLE BOARD: MARINE AND AIRCRAFT PLYWOOD",
+        "hsn_code": "44122330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER, WITH AT LEAST ONE\nOUTER PLY OF NONCONIFEROUS WOOD : OTHER, CONTAINING AT LEAST ONE LAYER OF\nPARTICLE BOARD: TEA CHEST PANELS OR SHOOKS, PACKED IN SETS",
+        "hsn_code": "44122320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER, WITH AT LEAST ONE\nOUTER PLY OF NONCONIFEROUS WOOD : OTHER, CONTAINING AT LEAST ONE LAYER OF\nPARTICLE BOARD: DECORATIVE PLYWOOD",
+        "hsn_code": "44122310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER, WITH AT LEAST ONE\nOUTER PLY OF NONCONIFEROUS WOOD : WITH AT LEAST ONE PLY OF TROPICAL WOOD\nSPECIFIED IN SUB-HEADING NOTE 1 TO THIS CHAPTER - OTHER",
+        "hsn_code": "44122290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER, WITH AT LEAST ONE\nOUTER PLY OF NONCONIFEROUS WOOD : WITH AT LEAST ONE PLY OF TROPICAL WOOD\nSPECIFIED IN SUB-HEADING NOTE 1 TO THIS CHAPTER - CUTTINGS AND TRIMMINGS OF\nPLYWOOD OF WIDTH NOT EXCEEDING 5 CM",
+        "hsn_code": "44122240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER, WITH AT LEAST ONE\nOUTER PLY OF NONCONIFEROUS WOOD : WITH AT LEAST ONE PLY OF TROPICAL WOOD\nSPECIFIED IN SUB-HEADING NOTE 1 TO THIS CHAPTER - MARINE AND AIRCRAFT PLYWOOD",
+        "hsn_code": "44122230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER, WITH AT LEAST ONE\nOUTER PLY OF NONCONIFEROUS WOOD : WITH AT LEAST ONE PLY OF TROPICAL WOOD\nSPECIFIED IN SUB-HEADING NOTE 1 TO THIS CHAPTER - TEA CHEST PANELS OR SHOOKS, PACKED\nIN SETS",
+        "hsn_code": "44122220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OTHER, WITH AT LEAST ONE\nOUTER PLY OF NONCONIFEROUS WOOD : WITH AT LEAST ONE PLY OF TROPICAL WOOD\nSPECIFIED IN SUB-HEADING NOTE 1 TO THIS CHAPTER - DECORATIVE PLYWOOD",
+        "hsn_code": "44122210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - PLYWOOD, CONSISTING\nSOLELY OF SHEETS OF WOOD, EACH PLY NOT EXCEEDING 6 MM THICKNESS : OTHER : OTHER",
+        "hsn_code": "44121990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - PLYWOOD, CONSISTING\nSOLELY OF SHEETS OF WOOD, EACH PLY NOT EXCEEDING 6 MM THICKNESS : OTHER : CUTTINGS\nAND TRIMMINGS OF PLYWOOD OF WIDTH NOT EXCEEDING 5 CM",
+        "hsn_code": "44121940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - PLYWOOD, CONSISTING\nSOLELY OF SHEETS OF WOOD, EACH PLY NOT EXCEEDING 6 MM THICKNESS : OTHER : MARINE\nAND AIRCRAFT PLYWOOD",
+        "hsn_code": "44121930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - PLYWOOD, CONSISTING\nSOLELY OF SHEETS OF WOOD, EACH PLY NOT EXCEEDING 6 MM THICKNESS : OTHER : TEA CHEST\nPANELS OR SHOOKS, PACKED IN SETS",
+        "hsn_code": "44121920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - PLYWOOD, CONSISTING\nSOLELY OF SHEETS OF WOOD, EACH PLY NOT EXCEEDING 6 MM THICKNESS : OTHER :\nDECORATIVE PLYWOOD",
+        "hsn_code": "44121910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - PLYWOOD, CONSISTING\nSOLELY OF SHEETS OF WOOD, EACH PLY NOT EXCEEDING 6 MM THICKNESS : OTHER, WITH AT\nLEAST ONE OUTER PLY OF NON-CONIFEROUS WOOD : OTHER",
+        "hsn_code": "44121490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - PLYWOOD, CONSISTING\nSOLELY OF SHEETS OF WOOD, EACH PLY NOT EXCEEDING 6 MM THICKNESS : OTHER, WITH AT\nLEAST ONE OUTER PLY OF NON-CONIFEROUS WOOD : CUTTINGS AND TRIMMINGS OF PLYWOOD\nOF WIDTH NOT EXCEEDING 5 CM",
+        "hsn_code": "44121440",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - PLYWOOD, CONSISTING\nSOLELY OF SHEETS OF WOOD, EACH PLY NOT EXCEEDING 6 MM THICKNESS : OTHER, WITH AT\nLEAST ONE OUTER PLY OF NON-CONIFEROUS WOOD : MARINE AND AIRCRAFT PLYWOOD",
+        "hsn_code": "44121430",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TEA CHEST PANELS OR SHOOKS, PACKED IN SETS",
+        "hsn_code": "44121420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - PLYWOOD, CONSISTING\nSOLELY OF SHEETS OF WOOD, EACH PLY NOT EXCEEDING 6 MM THICKNESS : OTHER, WITH AT\nLEAST ONE OUTER PLY OF NON-CONIFEROUS WOOD : DECORATIVE PLYWOOD",
+        "hsn_code": "44121410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - PLYWOOD, CONSISTING\nSOLELY OF SHEETS OF WOOD, EACH PLY NOT EXCEEDING 6 MM THICKNESS : WITH AT LEAST ONE\nOUTER PLY OF TROPICAL WOOD SPECIFIED IN SUB-HEADING NOTE 1 TO THIS CHAPTER : OTHER",
+        "hsn_code": "44121390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - PLYWOOD, CONSISTING\nSOLELY OF SHEETS OF WOOD, EACH PLY NOT EXCEEDING 6 MM THICKNESS : WITH AT LEAST ONE\nOUTER PLY OF TROPICAL WOOD SPECIFIED IN SUB-HEADING NOTE 1 TO THIS CHAPTER :\nCUTTINGS AND TRIMMINGS OF PLYWOOD OF WIDTH NOT EXCEEDING 5 CENTIMETRES",
+        "hsn_code": "44121350",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - PLYWOOD, CONSISTING\nSOLELY OF SHEETS OF WOOD, EACH PLY NOT EXCEEDING 6 MM THICKNESS : WITH AT LEAST ONE\nOUTER PLY OF TROPICAL WOOD SPECIFIED IN SUB-HEADING NOTE 1 TO THIS CHAPTER : MARINE\nAND AIRCRAFT PLYWOOD",
+        "hsn_code": "44121340",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - PLYWOOD, CONSISTING\nSOLELY OF SHEETS OF WOOD, EACH PLY NOT EXCEEDING 6 MM THICKNESS : WITH AT LEAST ONE\nOUTER PLY OF TROPICAL WOOD SPECIFIED IN SUB-HEADING NOTE 1 TO THIS CHAPTER : OTHER\nTEA CHEST PANELS",
+        "hsn_code": "44121330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - PLYWOOD, CONSISTING\nSOLELY OF SHEETS OF WOOD, EACH PLY NOT EXCEEDING 6 MM THICKNESS : WITH AT LEAST ONE\nOUTER PLY OF TROPICAL WOOD SPECIFIED IN SUB-HEADING NOTE 1 TO THIS CHAPTER : TEA\nCHEST PANELS OR SHOOKS, PACKED IN SETS",
+        "hsn_code": "44121320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - PLYWOOD, CONSISTING\nSOLELY OF SHEETS OF WOOD, EACH PLY NOT EXCEEDING 6 MM THICKNESS : WITH AT LEAST ONE\nOUTER PLY OF TROPICAL WOOD SPECIFIED IN SUB-HEADING NOTE 1 TO THIS CHAPTER :\nDECORATIVE PLYWOOD",
+        "hsn_code": "44121310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLYWOOD, VENEERED PANELS AND SIMILAR LAMINATED WOOD - OF BAMBOO",
+        "hsn_code": "44121000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBRE BOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH RESINS OR OTHER ORGANIC SUBSTANCES",
+        "hsn_code": "4411",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER : OTHER: OTHER",
+        "hsn_code": "44119990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER : OTHER: OTHER INSULATION BOARD",
+        "hsn_code": "44119930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER : OTHER: ACCOUSTIC ISULATION BOARD",
+        "hsn_code": "44119920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER : OTHER: INSULATION BOARD\n(HOMOGENOUS)",
+        "hsn_code": "44119910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBRE BOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER:OF A DENSITY NOT EXCEEDING 0.5 GM/CM3:--\n- OTHER: ----OTHER",
+        "hsn_code": "44119429",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBRE BOARDS OF JUTE FIBRE",
+        "hsn_code": "44119423",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBRE BOARDS OF COIR",
+        "hsn_code": "44119422",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBRE BOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER:OF A DENSITY NOT EXCEEDING 0.5 GM/CM3:--\n- OTHER: ----INSULATION BOARD",
+        "hsn_code": "44119421",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBRE BOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER:OF A DENSITY NOT EXCEEDING 0.5 GM/CM3:--\n- NOT MECHANICALLY WORKED OR SURFACE COVERED:---- OTHER",
+        "hsn_code": "44119419",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBRE BOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER:OF A DENSITY NOT EXCEEDING 0.5 GM/CM3:--\n- NOT MECHANICALLY WORKED OR SURFACE COVERED:---- INSULATION BOARD",
+        "hsn_code": "44119411",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBRE BOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER:-- OF A DENSITY EXCEEDING 0.5 GM/CM3 BUT\nNOT EXCEEDING 0.8 GM/CM3: --- OTHER:---- OTHER",
+        "hsn_code": "44119329",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBRE BOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER:-- OF A DENSITY EXCEEDING 0.5 GM/CM3 BUT\nNOT EXCEEDING 0.8 GM/CM3: --- OTHER:---- INSULATION BOARD",
+        "hsn_code": "44119321",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBRE BOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER:-- OF A DENSITY EXCEEDING 0.5 GM/CM3 BUT\nNOT EXCEEDING 0.8 GM/CM3:--- NOT MECHANICALLY WORKED OR SURFACE COVERED:----\nOTHER",
+        "hsn_code": "44119319",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBRE BOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER:-- OF A DENSITY EXCEEDING 0.5 GM/CM3 BUT\nNOT EXCEEDING 0.8 GM/CM3:--- NOT MECHANICALLY WORKED OR SURFACE COVERED:----\nINSULATION BOARD",
+        "hsn_code": "44119311",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBRE BOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER: -- OF A DENSITY EXCEEDING 0.8 GM/CM3: ---\nOTHER:---- OTHER",
+        "hsn_code": "44119229",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBRE BOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER: -- OF A DENSITY EXCEEDING 0.8 GM/CM3: ---\nOTHER:---- HARDBOARD",
+        "hsn_code": "44119221",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBRE BOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER: -- OF A DENSITY EXCEEDING 0.8 GM/CM3: ---\nNOT MECHANICALLY WORKED OR SURFACE COVERED:---- OTHER",
+        "hsn_code": "44119219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBRE BOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER: -- OF A DENSITY EXCEEDING 0.8 GM/CM3: ---\nNOT MECHANICALLY WORKED OR SURFACE COVERED:---- HARDBOARD",
+        "hsn_code": "44119211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER : NOT MECHANICALLY WORKED OR SURFACE\nCOVERED: OTHER",
+        "hsn_code": "44119190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER : NOT MECHANICALLY WORKED OR SURFACE\nCOVERED: OTHER INSULATION BOARD",
+        "hsn_code": "44119130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER : NOT MECHANICALLY WORKED OR SURFACE\nCOVERED: ACCOUSTIC INSULATION BOARD",
+        "hsn_code": "44119120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - OTHER : NOT MECHANICALLY WORKED OR SURFACE\nCOVERED: INSULATION BOARD (HOMOGENEOUS)",
+        "hsn_code": "44119110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - FIBRE BOARD OF A DENSITY EXCEEDING 0.35 G/C -\nM3 - BUT NOT EXCEEDING 0.5 G/ C - M3 - : OTHER: OTHER",
+        "hsn_code": "44113990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - FIBRE BOARD OF A DENSITY EXCEEDING 0.35 G/C -\nM3 - BUT NOT EXCEEDING 0.5 G/ C - M3 - : OTHER: INSULATION BOARD",
+        "hsn_code": "44113910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - FIBRE BOARD OF A DENSITY EXCEEDING 0.35 G/C -\nM3 - BUT NOT EXCEEDING 0.5 G/ C - M3 - : NOT MECHANICALLY WORKED OR SURFACE\nCOVERED: OTHER",
+        "hsn_code": "44113190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - FIBRE BOARD OF A DENSITY EXCEEDING 0.35 G/C -\nM3 - BUT NOT EXCEEDING 0.5 G/ C - M3 - : NOT MECHANICALLY WORKED OR SURFACE\nCOVERED: INSULATION BOARD",
+        "hsn_code": "44113110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - FIBREBOARD OF A DENSITY EXCEEDING 0.5 G/C - M3 -\nBUT NOT EXCEEDING 0.8 G/C - M3 - : OTHER : OTHER",
+        "hsn_code": "44112990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - FIBREBOARD OF A DENSITY EXCEEDING 0.5 G/C - M3 -\nBUT NOT EXCEEDING 0.8 G/C - M3 - : OTHER : INSULATION BOARD",
+        "hsn_code": "44112910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - FIBREBOARD OF A DENSITY EXCEEDING 0.5 G/C - M3 -\nBUT NOT EXCEEDING 0.8 G/C - M3 - : NOT MECHANICALLY WORKED OR SURFACE\nCOVERED:OTHER",
+        "hsn_code": "44112190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - FIBREBOARD OF A DENSITY EXCEEDING 0.5 G/C - M3 -\nBUT NOT EXCEEDING 0.8 G/C - M3 - : NOT MECHANICALLY WORKED OR SURFACE\nCOVERED:INSULATION BOARD",
+        "hsn_code": "44112110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - FIBREBOARD OF A DENSITY EXCEEDING 0.8 G/C - M3 -\n: OTHER : OTHER",
+        "hsn_code": "44111990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - FIBREBOARD OF A DENSITY EXCEEDING 0.8 G/C - M3 -\n: OTHER : HARDBOARD",
+        "hsn_code": "44111910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBRE BOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - MEDIUM DENSITY FIBRE BOARD (MDF):-- OF A\nTHICKNESS EXCEEDING 9 MM",
+        "hsn_code": "44111400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBRE BOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - MEDIUM DENSITY FIBRE BOARD (MDF):-- OF A\nTHICKNESS EXCEEDING 5 MM BUT NOT EXCEEDING 9 MM",
+        "hsn_code": "44111300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBRE BOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - MEDIUM DENSITY FIBRE BOARD (MDF):-- OF A\nTHICKNESS NOT EXCEEDING 5 MM",
+        "hsn_code": "44111200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - FIBREBOARD OF A DENSITY EXCEEDING 0.8 G/C - M3 -\n: NOT MECHANICALLY WORKED OR SURFACE COVERED : OTHER",
+        "hsn_code": "44111190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIBREBOARD OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT BONDED WITH\nRESINS OR OTHER ORGANIC SUBSTANCES - FIBREBOARD OF A DENSITY EXCEEDING 0.8 G/C - M3 -\n: NOT MECHANICALLY WORKED OR SURFACE COVERED : HARDBOARD",
+        "hsn_code": "44111110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD, ORIENTED STRAND BOARD (OSB) AND SIMILAR BOARD (FOR EXAMPLE, WAFERBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS,WHETHER OR NOT AGGLOMERATED  WITH RESINS OR OTHER ORGANIC BINDING SUBSTANCES",
+        "hsn_code": "4410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES- OTHER: OTHER : OTHER",
+        "hsn_code": "44109099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES- OTHER: OTHER : VENEERED PARTICLE\nBOARD, NOT HAVING DECORATIVE VENEERS OF ANY FACE",
+        "hsn_code": "44109093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES- OTHER: OTHER : INSULATION BOARD AND\nHARDBOARD",
+        "hsn_code": "44109092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES- OTHER: OTHER : PLAIN PARTICLE BOARDS",
+        "hsn_code": "44109091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD, ORIENTED STRAND BOARD (OSB) AND SIMILAR BOARD (FOR EXAMPLE,\nWAFERBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS,WHETHER OR NOT AGGLOMERATED\nWITH RESINS OR OTHER ORGANIC BINDING SUBSTANCES -- OTHER --- OTHER",
+        "hsn_code": "44109090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BOARD, OF JUTE FIBRE",
+        "hsn_code": "44109050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BOARD, OF COIR",
+        "hsn_code": "44109040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD, ORIENTED STRAND BOARD (OSB) AND SIMILAR BOARD (FOR EXAMPLE,\nWAFERBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS,WHETHER OR NOT AGGLOMERATED\nWITH RESINS OR OTHER ORGANIC BINDING SUBSTANCES -- OTHER --- VENEERED PARTICLE\nBOARD, NOT HAVING DECORATIVE VENEERS ON ANY FACE",
+        "hsn_code": "44109030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD, ORIENTED STRAND BOARD (OSB) AND SIMILAR BOARD (FOR EXAMPLE,\nWAFERBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS,WHETHER OR NOT AGGLOMERATED\nWITH RESINS OR OTHER ORGANIC BINDING SUBSTANCES -- OTHER --- INSULATION BOARD AND\nHARD BOARD",
+        "hsn_code": "44109020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES- OTHER: PLASTIC LAMINATED SHEETS: OTHER",
+        "hsn_code": "44109019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES- OTHER: PLASTIC LAMINATED SHEETS:\nVENEERED PARTICLE BOARD, NOT HAVING DECORATIVE VENEERS OF ANY FACE",
+        "hsn_code": "44109012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES- OTHER: PLASTIC LAMINATED SHEETS:\nINSULATION BOARD AND HARDBOARD",
+        "hsn_code": "44109011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD, ORIENTED STRAND BOARD (OSB) AND SIMILAR BOARD (FOR EXAMPLE,\nWAFERBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS,WHETHER OR NOT AGGLOMERATED\nWITH RESINS OR OTHER ORGANIC BINDING SUBSTANCES -- OTHER --- PLAIN PARTICLE BOARD",
+        "hsn_code": "44109010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES - OTHER, OF WOOD: OTHER: OTHER",
+        "hsn_code": "44103990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES - OTHER, OF WOOD: OTHER: VENEERED\nPARTICLE BOARD, NOT HAVING DECORATIVE VENEERS ON ANY FACE",
+        "hsn_code": "44103930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES - OTHER, OF WOOD: OTHER: INSULATION\nBOARD AND HARDBOARD",
+        "hsn_code": "44103920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES - OTHER, OF WOOD: OTHER: PLAIN PARTICLE\nBOARDS",
+        "hsn_code": "44103910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES - OTHER, OF WOOD: SURFACE-COVERED\nWITH DECORATIVE LAMINATES OF PLASTICS: OTHER",
+        "hsn_code": "44103390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES - OTHER, OF WOOD: SURFACE-COVERED\nWITH DECORATIVE LAMINATES OF PLASTICS: VENEERED PARTICLE BOARD, NOT HAVING\nDECORATIVE VENEERS ON ANY FACE",
+        "hsn_code": "44103330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES - OTHER, OF WOOD: SURFACE-COVERED\nWITH DECORATIVE LAMINATES OF PLASTICS: INSULATION BOARD AND HARDBOARD",
+        "hsn_code": "44103320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES - OTHER, OF WOOD: SURFACE-COVERED\nWITH DECORATIVE LAMINATES OF PLASTICS: PLAIN PARTICLE BOARDS",
+        "hsn_code": "44103310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES - OTHER, OF WOOD: SURFACE-COVERED\nWITH MELAMINE IMPREGNATED PAPER: OTHER",
+        "hsn_code": "44103290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES - OTHER, OF WOOD: SURFACE-COVERED\nWITH MELAMINE IMPREGNATED PAPER: VENEERED PARTICLE BOARD, NOT HAVING\nDECORATIVE VENEERS ON ANY FACE",
+        "hsn_code": "44103230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES - OTHER, OF WOOD: SURFACE-COVERED\nWITH MELAMINE IMPREGNATED PAPER: INSULATION BOARD AND HARDBOARD",
+        "hsn_code": "44103220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES - OTHER, OF WOOD: SURFACE-COVERED\nWITH MELAMINE IMPREGNATED PAPER: PLAIN PARTICLE BOARDS",
+        "hsn_code": "44103210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES - OTHER, OF WOOD: UNWORKED OR NOT\nFURTHER WORKED THAN SANDED: PLAIN OTHER",
+        "hsn_code": "44103190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES - OTHER, OF WOOD: UNWORKED OR NOT\nFURTHER WORKED THAN SANDED: PLAIN VENEERED PARTICLE BOARD, NOT HAVING\nDECORATIVE VENEERS ON ANY FACE",
+        "hsn_code": "44103130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES - OTHER, OF WOOD: UNWORKED OR NOT\nFURTHER WORKED THAN SANDED: PLAIN INSULATION BOARD AND HARDBOARD",
+        "hsn_code": "44103120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES - OTHER, OF WOOD: UNWORKED OR NOT\nFURTHER WORKED THAN SANDED: PLAIN PARTICLE BOARDS",
+        "hsn_code": "44103110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES - ORIENTED STRAND-BOARD AND WAFER\nBOARD, OF WOOD: OTHER",
+        "hsn_code": "44102900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD AND SIMILAR BOARD (FOR EXAMPLE, ORIENTED STRAND BOARD AND WAFER\nBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT AGGLOMERATED WITH\nRESINS OR OTHER ORGANIC BINDING SUBSTANCES - ORIENTED STRAND-BOARD AND WAFER\nBOARD, OF WOOD: UNWORKED OR NOT FURTHER WORKED THAN SANDED",
+        "hsn_code": "44102100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD, ORIENTED STRAND BOARD (OSB) AND SIMILAR BOARD (FOR EXAMPLE,\nWAFERBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS,WHETHER OR NOT AGGLOMERATED\nWITH RESINS OR OTHER ORGANIC BINDING SUBSTANCES - OF WOOD:--OTHER",
+        "hsn_code": "44101900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD, ORIENTED STRAND BOARD (OSB) AND SIMILAR BOARD (FOR EXAMPLE,\nWAFERBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS,WHETHER OR NOT AGGLOMERATED\nWITH RESINS OR OTHER ORGANIC BINDING SUBSTANCES - OF WOOD: ORIENTED STRAND\nBOARD (OSB):OTHER",
+        "hsn_code": "44101290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD, ORIENTED STRAND BOARD (OSB) AND SIMILAR BOARD (FOR EXAMPLE,\nWAFERBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS,WHETHER OR NOT AGGLOMERATED\nWITH RESINS OR OTHER ORGANIC BINDING SUBSTANCES - OF WOOD: ORIENTED STRAND\nBOARD (OSB):--- UNWORKED OR NOT FURTHER WORKED THAN SANDED",
+        "hsn_code": "44101210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD, ORIENTED STRAND BOARD (OSB) AND SIMILAR BOARD (FOR EXAMPLE,\nWAFERBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS,WHETHER OR NOT AGGLOMERATED\nWITH RESINS OR OTHER ORGANIC BINDING SUBSTANCES - OF WOOD: -- PARTICLE BOARD:---\nOTHERS",
+        "hsn_code": "44101190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD, ORIENTED STRAND BOARD (OSB) AND SIMILAR BOARD (FOR EXAMPLE,\nWAFERBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS,WHETHER OR NOT AGGLOMERATED\nWITH RESINS OR OTHER ORGANIC BINDING SUBSTANCES - OF WOOD: -- PARTICLE BOARD:---\nVENEERED PARTICLE BOARD",
+        "hsn_code": "44101130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD, ORIENTED STRAND BOARD (OSB) AND SIMILAR BOARD (FOR EXAMPLE,\nWAFERBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS,WHETHER OR NOT AGGLOMERATED\nWITH RESINS OR OTHER ORGANIC BINDING SUBSTANCES - OF WOOD: -- PARTICLE BOARD:---\nINSULATION BOARD AND HARDBOARD",
+        "hsn_code": "44101120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARTICLE BOARD, ORIENTED STRAND BOARD (OSB) AND SIMILAR BOARD (FOR EXAMPLE,\nWAFERBOARD) OF WOOD OR OTHER LIGNEOUS MATERIALS, WHETHER OR NOT\nAGGLOMERATED WITH RESINS OR OTHER ORGANIC BINDING SUBSTANCES- OF WOOD:--\nPARTICLE BOARD:PLAIN PARTICLE BOARDS",
+        "hsn_code": "44101110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD (INCLUDING STRIPS AND FRIEZES FOR PARQUET FLOORING, NOT ASSEMBLED) CONTINUOUSLY SHAPED (TONGUED, GROOVED, REBATED, CHAMFERED, V-JOINTED, BEADED, MOULDED, ROUNDED OR THE LIKE) ALONG ANY OF ITS EDGES OR FACES, WHETHER OR NOT PLANED, SANDED OR END-JOINTED",
+        "hsn_code": "4409",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD (INCLUDING STRIPS AND FRIEZES FOR PARQUET FLOORING, NOT ASSEMBLED)\nCONTINUOUSLY SHAPED (TONGUED, GROOVED, REBATED, CHAMFERED, V-JOINTED, BEADED,\nMOULDED, ROUNDED OR THE LIKE) ALONG ANY OF ITS EDGES OR FACES, WHETHER OR NOT\nPLANED, SANDED OR END-JOINTED -- OTHER ---OTHER",
+        "hsn_code": "44092990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD (INCLUDING STRIPS AND FRIEZES FOR PARQUET FLOORING, NOT ASSEMBLED)\nCONTINUOUSLY SHAPED (TONGUED, GROOVED, REBATED, CHAMFERED, V-JOINTED, BEADED,\nMOULDED, ROUNDED OR THE LIKE) ALONG ANY OF ITS EDGES OR FACES, WHETHER OR NOT\nPLANED, SANDED OR END-JOINTED - OTHER: ---BEADINGS AND MOULDINGS (INCLUDING\nMOULDED SKIRTING AND OTHER MOULDED BOARDS)",
+        "hsn_code": "44092920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD (INCLUDING STRIPS AND FRIEZES FOR PARQUET FLOORING, NOT ASSEMBLED)\nCONTINUOUSLY SHAPED (TONGUED, GROOVED, REBATED, CHAMFERED, V-JOINTED, BEADED,\nMOULDED, ROUNDED OR THE LIKE) ALONG ANY OF ITS EDGES OR FACES, WHETHER OR NOT\nPLANED, SANDED OR END-JOINTED - NON-CONIFEROUS:--OTHER ---PLANED, TONGUED,\nGROOVED,REBATED, CHAMFERED,V-JOINTED,AND THE LIKE BUT NOT FURTHER MOULDED",
+        "hsn_code": "44092910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD (INCLUDING STRIPS AND FRIEZES FOR PARQUET FLOORING, NOT ASSEMBLED)\nCONTINUOUSLY SHAPED (TONGUED, GROOVED, REBATED, CHAMFERED, V-JOINTED, BEADED,\nMOULDED, ROUNDED OR THE LIKE) ALONG ANY OF ITS EDGES OR FACES, WHETHER OR NOT\nPLANED, SANDED OR END-JOINTED - NON-CONIFEROUS:--OF BAMBOO",
+        "hsn_code": "44092100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD (INCLUDING STRIPS AND FRIEZES FOR PARQUET FLOORING, NOT ASSEMBLED)\nCONTINUOUSLY SHAPED (TONGUED, GROOVED, REBATED, CHAMFERED, V-JOINTED, BEADED,\nMOULDED, ROUNDED OR THE LIKE) ALONG ANY OF ITS EDGES OR FACES, WHETHER OR NOT\nPLANED, SANDED OR END-JOINTED - NON-CONIFEROUS:OTHER",
+        "hsn_code": "44092090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD (INCLUDING STRIPS AND FRIEZES FOR PARQUET FLOORING, NOT ASSEMBLED)\nCONTINUOUSLY SHAPED (TONGUED, GROOVED, REBATED, CHAMFERED, V-JOINTED, BEADED,\nMOULDED, ROUNDED OR THE LIKE) ALONG ANY OF ITS EDGES OR FACES, WHETHER OR NOT\nPLANED, SANDED OR END-JOINTED - NON-CONIFEROUS:BEADINGS AND MOULDINGS\n(INCLUDING MOULDED SKIRTING AND OTHER MOULDED BOARDS)",
+        "hsn_code": "44092020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD (INCLUDING STRIPS AND FRIEZES FOR PARQUET FLOORING, NOT ASSEMBLED)\nCONTINUOUSLY SHAPED (TONGUED, GROOVED, REBATED, CHAMFERED, V-JOINTED, BEADED,\nMOULDED, ROUNDED OR THE LIKE) ALONG ANY OF ITS EDGES OR FACES, WHETHER OR NOT\nPLANED, SANDED OR END-JOINTED - NON-CONIFEROUS:PLANED, TONGUED, GROOVED,\nREBATED, CHAMFERED, V-JOINTED, AND THE LIKE BUT NOT FURTHER MOULDED",
+        "hsn_code": "44092010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD (INCLUDING STRIPS AND FRIEZES FOR PARQUET FLOORING, NOT ASSEMBLED)\nCONTINUOUSLY SHAPED (TONGUED, GROOVED, REBATED, CHAMFERED, V-JOINTED, BEADED,\nMOULDED, ROUNDED OR THE LIKE) ALONG ANY OF ITS EDGES OR FACES, WHETHER OR NOT\nPLANED, SANDED OR END-JOINTED - CONIFEROUS: OTHER",
+        "hsn_code": "44091090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD (INCLUDING STRIPS AND FRIEZES FOR PARQUET FLOORING, NOT ASSEMBLED)\nCONTINUOUSLY SHAPED (TONGUED, GROOVED, REBATED, CHAMFERED, V-JOINTED, BEADED,\nMOULDED, ROUNDED OR THE LIKE) ALONG ANY OF ITS EDGES OR FACES, WHETHER OR NOT\nPLANED, SANDED OR END-JOINTED - CONIFEROUS: BEADINGS, AND MOULDINGS (INCLUDING\nMOULDED, SKIRTING AND OTHER MOULDED BOARDS)",
+        "hsn_code": "44091020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD (INCLUDING STRIPS AND FRIEZES FOR PARQUET FLOORING, NOT ASSEMBLED)\nCONTINUOUSLY SHAPED (TONGUED, GROOVED, REBATED, CHAMFERED, V-JOINTED, BEADED,\nMOULDED, ROUNDED OR THE LIKE) ALONG ANY OF ITS EDGES OR FACES, WHETHER OR NOT\nPLANED, SANDED OR END-JOINTED - CONIFEROUS: PLANED, TONGUED, GROOVED, REBATED,\nCHAMFERED, V-JOINTED, AND THE LIKE BUT NOT FURTHER MOULDED",
+        "hsn_code": "44091010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHEETS FOR VENEERING (INCLUDING THOSE OBTAINED BY SLICING LAMINATED WOOD), FOR PLYWOOD OR FOR  SIMILAR LAMINATED WOOD AND OTHER WOOD, SAWN LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED, SANDED, SPLICED OR END-JOINTED, OF A THICKNESS NOT EXCEEDING 6 MM",
+        "hsn_code": "4408",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHEETS FOR VENEERING (INCLUDING THOSE OBTAINED BY SLICING LAMINATED WOOD), FOR\nPLYWOOD OR FOR SIMILAR LAMINATED WOOD AND OTHER WOOD, SAWN LENGTHWISE, SLICED\nOR PEELED, WHETHER OR NOT PLANED, SANDED, SPLICED OR END-JOINTED, OF A THICKNESS\nNOT EXCEEDING 6 MM- OTHER: OTHER",
+        "hsn_code": "44089090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHEETS FOR VENEERING (INCLUDING THOSE OBTAINED BY SLICING LAMINATED WOOD), FOR\nPLYWOOD OR FOR SIMILAR LAMINATED WOOD AND OTHER WOOD, SAWN LENGTHWISE, SLICED\nOR PEELED, WHETHER OR NOT PLANED, SANDED, SPLICED OR END-JOINTED, OF A THICKNESS\nNOT EXCEEDING 6 MM- OTHER: VENEER SHEETS, FOR MATCH BOXES AND MATCH SPLINTS",
+        "hsn_code": "44089020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHEETS FOR VENEERING (INCLUDING THOSE OBTAINED BY SLICING LAMINATED WOOD), FOR\nPLYWOOD OR FOR SIMILAR LAMINATED WOOD AND OTHER WOOD, SAWN LENGTHWISE, SLICED\nOR PEELED, WHETHER OR NOT PLANED, SANDED, SPLICED OR END-JOINTED, OF A THICKNESS\nNOT EXCEEDING 6 MM- OTHER: SHEETS FOR PLYWOODS",
+        "hsn_code": "44089010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HEETS FOR VENEERING (INCLUDING THOSE OBTAINED BY SLICING LAMINATED WOOD), FOR\nPLYWOOD OR FOR SIMILAR LAMINATED WOOD AND OTHER WOOD, SAWN LENGTHWISE, SLICED\nOR PEELED, WHETHER OR NOT PLANED, SANDED, SPLICED OR END-JOINTED, OF A THICKNESS\nNOT EXCEEDING 6 MM --OTHER ---OTHER",
+        "hsn_code": "44083990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHEETS FOR VENEERING (INCLUDING THOSE OBTAINED BY SLICING LAMINATED WOOD), FOR\nPLYWOOD OR FOR SIMILAR LAMINATED WOOD AND OTHER WOOD, SAWN LENGTHWISE, SLICED\nOR PEELED, WHETHER OR NOT PLANED, SANDED, SPLICED OR END-JOINTED, OF A THICKNESS\nNOT EXCEEDING 6 MM - OF TROPICAL WOOD SPECIFIED IN SUB-HEADING NOTE 1 TO THIS\nCHAPTER : OTHER: VENEER SHEETS, FOR MATCH BOXES AND MATCH SPLINTS",
+        "hsn_code": "44083930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHEETS FOR VENEERING (INCLUDING THOSE OBTAINED BY SLICING LAMINATED WOOD), FOR\nPLYWOOD OR FOR SIMILAR LAMINATED WOOD AND OTHER WOOD, SAWN LENGTHWISE, SLICED\nOR PEELED, WHETHER OR NOT PLANED, SANDED, SPLICED OR END-JOINTED, OF A THICKNESS\nNOT EXCEEDING 6 MM - OF TROPICAL WOOD SPECIFIED IN SUB-HEADING NOTE 1 TO THIS\nCHAPTER : OTHER: VENEER SHEETS (OF ROSE WOOD)",
+        "hsn_code": "44083920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHEETS FOR VENEERING (INCLUDING THOSE OBTAINED BY SLICING LAMINATED WOOD), FOR\nPLYWOOD OR FOR SIMILAR LAMINATED WOOD AND OTHER WOOD, SAWN LENGTHWISE, SLICED\nOR PEELED, WHETHER OR NOT PLANED, SANDED, SPLICED OR END-JOINTED, OF A THICKNESS\nNOT EXCEEDING 6 MM - OF TROPICAL WOOD SPECIFIED IN SUB-HEADING NOTE 1 TO THIS\nCHAPTER : OTHER: SHEETS FOR PLYWOODS",
+        "hsn_code": "44083910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHEETS FOR VENEERING (INCLUDING THOSE OBTAINED BY SLICING LAMINATED WOOD), FOR\nPLYWOOD OR FOR SIMILAR LAMINATED WOOD AND OTHER WOOD, SAWN LENGTHWISE, SLICED\nOR PEELED, WHETHER OR NOT PLANED, SANDED, SPLICED OR END-JOINTED, OF A THICKNESS\nNOT EXCEEDING 6 MM - OF TROPICAL WOOD SPECIFIED IN SUB-HEADING NOTE 1 TO THIS\nCHAPTER : OF DARK RED MERANTI, LIGHT RED MERANTI AND MERANTI BAKAU: OTHER",
+        "hsn_code": "44083190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHEETS FOR VENEERING (INCLUDING THOSE OBTAINED BY SLICING LAMINATED WOOD), FOR\nPLYWOOD OR FOR SIMILAR LAMINATED WOOD AND OTHER WOOD, SAWN LENGTHWISE, SLICED\nOR PEELED, WHETHER OR NOT PLANED, SANDED, SPLICED OR END-JOINTED, OF A THICKNESS\nNOT EXCEEDING 6 MM - OF TROPICAL WOOD SPECIFIED IN SUB-HEADING NOTE 1 TO THIS\nCHAPTER : OF DARK RED MERANTI, LIGHT RED MERANTI AND MERANTI BAKAU: VENEER SHEETS,\nFOR MATCH BOXES AND MATCH SPLINTS",
+        "hsn_code": "44083130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHEETS FOR VENEERING (INCLUDING THOSE OBTAINED BY SLICING LAMINATED WOOD), FOR\nPLYWOOD OR FOR SIMILAR LAMINATED WOOD AND OTHER WOOD, SAWN LENGTHWISE, SLICED\nOR PEELED, WHETHER OR NOT PLANED, SANDED, SPLICED OR END-JOINTED, OF A THICKNESS\nNOT EXCEEDING 6 MM - OF TROPICAL WOOD SPECIFIED IN SUB-HEADING NOTE 1 TO THIS\nCHAPTER : OF DARK RED MERANTI, LIGHT RED MERANTI AND MERANTI BAKAU: VENEER SHEETS\n(OF ROSE WOOD)",
+        "hsn_code": "44083120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHEETS FOR VENEERING (INCLUDING THOSE OBTAINED BY SLICING LAMINATED WOOD), FOR\nPLYWOOD OR FOR SIMILAR LAMINATED WOOD AND OTHER WOOD, SAWN LENGTHWISE, SLICED\nOR PEELED, WHETHER OR NOT PLANED, SANDED, SPLICED OR END-JOINTED, OF A THICKNESS\nNOT EXCEEDING 6 MM - OF TROPICAL WOOD SPECIFIED IN SUB-HEADING NOTE 1 TO THIS\nCHAPTER : OF DARK RED MERANTI, LIGHT RED MERANTI AND MERANTI BAKAU: SHEETS FOR\nPLYWOOD",
+        "hsn_code": "44083110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HEETS FOR VENEERING (INCLUDING THOSE OBTAINED BY SLICING LAMINATED WOOD), FOR\nPLYWOOD OR FOR SIMILAR LAMINATED WOOD AND OTHER WOOD, SAWN LENGTHWISE, SLICED\nOR PEELED, WHETHER OR NOT PLANED, SANDED, SPLICED OR END-JOINTED, OF A THICKNESS\nNOT EXCEEDING 6 MM- CONIFEROUS: OTHER",
+        "hsn_code": "44081090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHEETS FOR VENEERING (INCLUDING THOSE OBTAINED BY SLICING LAMINATED WOOD), FOR\nPLYWOOD OR FOR SIMILAR LAMINATED WOOD AND OTHER WOOD, SAWN LENGTHWISE, SLICED\nOR PEELED, WHETHER OR NOT PLANED, SANDED, SPLICED OR END-JOINTED, OF A THICKNESS\nNOT EXCEEDING 6 MM- CONIFEROUS: VENEER SHEETS, FOR MATCH BOXES AND MATCH SPLINTS",
+        "hsn_code": "44081030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHEETS FOR VENEERING (INCLUDING THOSE OBTAINED BY SLICING LAMINATED WOOD), FOR\nPLYWOOD OR FOR SIMILAR LAMINATED WOOD AND OTHER WOOD, SAWN LENGTHWISE, SLICED\nOR PEELED, WHETHER OR NOT PLANED, SANDED, SPLICED OR END-JOINTED, OF A THICKNESS\nNOT EXCEEDING 6 MM- CONIFEROUS: OAK WOOD VENEER",
+        "hsn_code": "44081020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SHEETS FOR VENEERING (INCLUDING THOSE OBTAINED BY SLICING LAMINATED WOOD), FOR\nPLYWOOD OR FOR SIMILAR LAMINATED WOOD AND OTHER WOOD, SAWN LENGTHWISE, SLICED\nOR PEELED, WHETHER OR NOT PLANED, SANDED, SPLICED OR END-JOINTED, OF A THICKNESS\nNOT EXCEEDING 6 MM- CONIFEROUS: SHEETS FOR PLYWOOD",
+        "hsn_code": "44081010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED, SANDED OR END-JOINTED, OF A THICKNESS EXCEEDING 6 MM",
+        "hsn_code": "4407",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - OTHER : OTHER : OTHER",
+        "hsn_code": "44079990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - OTHER : OTHER : WILLOW",
+        "hsn_code": "44079920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - OTHER : OTHER : OF BIRCH\n(BETULA SPP.)",
+        "hsn_code": "44079910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - OTHER : -- OF ASH(FRAXINUS\nSPP.)",
+        "hsn_code": "44079500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - OTHER : --OF CHERRY (PRUNUS\nSPP.)",
+        "hsn_code": "44079400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - OTHER : OF MAPLE (ACER SPP)",
+        "hsn_code": "44079300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - OTHER : OF BEECH (FAGUS SPP.)",
+        "hsn_code": "44079200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - OTHER : OF OAK (QUERCUS SPP.)",
+        "hsn_code": "44079100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "29 WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - OF TROPICAL WOOD SPECIFIED\nIN SUB-HEADING NOTE 1 TO THIS CHAPTER : OTHER : OTHER",
+        "hsn_code": "44072990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "29 WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - OF TROPICAL WOOD SPECIFIED\nIN SUB-HEADING NOTE 1 TO THIS CHAPTER : OTHER : TEAK WOOD",
+        "hsn_code": "44072910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - OF TROPICAL WOOD SPECIFIED\nIN SUB-HEADING NOTE 1 TO THIS CHAPTER : IROKO",
+        "hsn_code": "44072800",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - OF TROPICAL WOOD SPECIFIED\nIN SUB-HEADING NOTE 1 TO THIS CHAPTER : --SAPELLI",
+        "hsn_code": "44072700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - OF TROPICAL WOOD SPECIFIED\nIN SUB-HEADING NOTE 1 TO THIS CHAPTER : WHITE LAUAN, WHITE MERANTI, WHITE SERAYA,\nYELLOW MERANTI AND ALAN",
+        "hsn_code": "44072600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - OF TROPICAL WOOD SPECIFIED\nIN SUB-HEADING NOTE 1 TO THIS CHAPTER : DARK RED MERANTI, LIGHT RED MERANTI AND\nMERANTI BAKAU",
+        "hsn_code": "44072500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - OF TROPICAL WOOD SPECIFIED\nIN SUB-HEADING NOTE 1 TO THIS CHAPTER : VIROLA, MAHOGANY (SWIETENIA SPP.) IMBUIA\nAND BALSA",
+        "hsn_code": "44072400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - OF TROPICAL WOOD SPECIFIED\nIN SUB-HEADING NOTE 1 TO THIS CHAPTER : VIROLA,IMBUIA AND BALSA",
+        "hsn_code": "44072200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - OF TROPICAL WOOD SPECIFIED\nIN SUB-HEADING NOTE 1 TO THIS CHAPTER : MAHOGANY (SWIETENIA SPP.)",
+        "hsn_code": "44072100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - CONIFERROUS: OTHER",
+        "hsn_code": "44071090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - CONIFERROUS: PINE (PINUS SPP.)",
+        "hsn_code": "44071020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD SAWN OR CHIPPED LENGTHWISE, SLICED OR PEELED, WHETHER OR NOT PLANED,\nSANDED OR ENDJOINTED, OF A THICKNESS EXCEEDING 6 MM - CONIFERROUS: DOGLAS FIR\n(PSCUDOTSUGA MENZIESIE)",
+        "hsn_code": "44071010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY SLEEPERS (CROSSTIES) OF WOOD",
+        "hsn_code": "4406",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY SLEEPERS (CROSSTIES) OF WOOD - OTHER",
+        "hsn_code": "44069000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RAILWAY OR TRAMWAY SLEEPERS (CROSSTIES) OF WOOD - NOT IMPREGNATED",
+        "hsn_code": "44061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Wood wool; wood flour",
+        "hsn_code": "4405",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WOOD WOOL; WOOD FLOUR",
+        "hsn_code": "44050000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HOOPWOOD; SPLIT POLES; PILES, PICKETS AND STAKES OF WOOD, POINTED BUT NOT SAWN LENGTHWISE; WOODEN STICKS, ROUGHLY TRIMMED BUT NOT TURNED, BENT OR OTHERWISE WORKED, SUITABLE FOR THE MANUFACTURE OF WALKING STICKS, UMBRELLAS, TOOL HANDLES OR THE LIKE; CHIPWOOD AND THE LIKE",
+        "hsn_code": "4404",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HOOPWOOD; SPLIT POLES; PILES, PICKETS AND STAKES OF WOOD, POINTED BUT NOT SAWN\nLENGTHWISE; WOODEN STICKS, ROUGHLY TRIMMED BUT NOT TURNED, BENT OR OTHERWISE\nWORKED, SUITABLE FOR THE MANUFACTURE OF WALKING STICKS, UMBRELLAS, TOOL HANDLES\nOR THE LIKE; CHIPWOOD AND THE LIKE - NON-CONIFEROUS : OTHER",
+        "hsn_code": "44042090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HOOPWOOD; SPLIT POLES; PILES, PICKETS AND STAKES OF WOOD, POINTED BUT NOT SAWN\nLENGTHWISE; WOODEN STICKS, ROUGHLY TRIMMED BUT NOT TURNED, BENT OR OTHERWISE\nWORKED, SUITABLE FOR THE MANUFACTURE OF WALKING STICKS, UMBRELLAS, TOOL HANDLES\nOR THE LIKE; CHIPWOOD AND THE LIKE - NON-CONIFEROUS : DRAWN WOOD",
+        "hsn_code": "44042020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HOOPWOOD; SPLIT POLES; PILES, PICKETS AND STAKES OF WOOD, POINTED BUT NOT SAWN\nLENGTHWISE; WOODEN STICKS, ROUGHLY TRIMMED BUT NOT TURNED, BENT OR OTHERWISE\nWORKED, SUITABLE FOR THE MANUFACTURE OF WALKING STICKS, UMBRELLAS, TOOL HANDLES\nOR THE LIKE; CHIPWOOD AND THE LIKE - NON-CONIFEROUS : WOODEN STICKS, ROUGHLY\nTRIMMED BUT NOT TURNED, BENT OR OTHERWISE WORKED, SUITABLE FOR THE\nMANUFACTURE OF WALKING STICKS, TOOL HANDLES, SPLIT POLES, PILES, PICKETS, STAKES AND\nTHE LIKE",
+        "hsn_code": "44042010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HOOPWOOD; SPLIT POLES; PILES, PICKETS AND STAKES OF WOOD, POINTED BUT NOT SAWN\nLENGTHWISE; WOODEN STICKS, ROUGHLY TRIMMED BUT NOT TURNED, BENT OR OTHERWISE\nWORKED, SUITABLE FOR THE MANUFACTURE OF WALKING STICKS, UMBRELLAS, TOOL HANDLES\nOR THE LIKE; CHIPWOOD AND THE LIKE - CONIFEROUS",
+        "hsn_code": "44041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY SQUARED",
+        "hsn_code": "4403",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OTHER : SAL (CHOREA ROBUSTA) SANDAL WOOD (SANTALUM ALBUR)\nSEMUL (BOMBAX CEIBA) WALNUT WOOD (JUGLANS BINATA) ANJAM (HARDWICKIA BINATA)\nBIRCH (BETULA SPP.) SISSOO (DALBERGIA SISSO) AND WHITE CEDAR (DYSOZYLUM) AND THE\nLIKE: OTHER",
+        "hsn_code": "44039929",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OTHER : SAL (CHOREA ROBUSTA) SANDAL WOOD (SANTALUM ALBUR)\nSEMUL (BOMBAX CEIBA) WALNUT WOOD (JUGLANS BINATA) ANJAM (HARDWICKIA BINATA)\nBIRCH (BETULA SPP.) SISSOO (DALBERGIA SISSO) AND WHITE CEDAR (DYSOZYLUM) AND THE\nLIKE: WHITE CEDAR (DYSOZYLUM MALABARICUM)",
+        "hsn_code": "44039928",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OTHER : SAL (CHOREA ROBUSTA) SANDAL WOOD (SANTALUM ALBUR)\nSEMUL (BOMBAX CEIBA) WALNUT WOOD (JUGLANS BINATA) ANJAM (HARDWICKIA BINATA)\nBIRCH (BETULA SPP.) SISSOO (DALBERGIA SISSO) AND WHITE CEDAR (DYSOZYLUM) AND THE\nLIKE: SISSOO (DALBERGIA SISSO)",
+        "hsn_code": "44039927",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OTHER : SAL (CHOREA ROBUSTA) SANDAL WOOD (SANTALUM ALBUR)\nSEMUL (BOMBAX CEIBA) WALNUT WOOD (JUGLANS BINATA) ANJAM (HARDWICKIA BINATA)\nBIRCH (BETULA SPP.) SISSOO (DALBERGIA SISSO) AND WHITE CEDAR (DYSOZYLUM) AND THE\nLIKE: BIRCH (BETULA SPP.)",
+        "hsn_code": "44039926",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OTHER : SAL (CHOREA ROBUSTA) SANDAL WOOD (SANTALUM ALBUR)\nSEMUL (BOMBAX CEIBA) WALNUT WOOD (JUGLANS BINATA) ANJAM (HARDWICKIA BINATA)\nBIRCH (BETULA SPP.) SISSOO (DALBERGIA SISSO) AND WHITE CEDAR (DYSOZYLUM) AND THE\nLIKE: ANJAM (HARDWICKIA BINATA)",
+        "hsn_code": "44039925",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OTHER : SAL (CHOREA ROBUSTA) SANDAL WOOD (SANTALUM ALBUR)\nSEMUL (BOMBAX CEIBA) WALNUT WOOD (JUGLANS BINATA) ANJAM (HARDWICKIA BINATA)\nBIRCH (BETULA SPP.) SISSOO (DALBERGIA SISSO) AND WHITE CEDAR (DYSOZYLUM) AND THE\nLIKE: WALNUT WOOD (JUGLANS BINATA)",
+        "hsn_code": "44039924",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OTHER : SAL (CHOREA ROBUSTA) SANDAL WOOD (SANTALUM ALBUR)\nSEMUL (BOMBAX CEIBA) WALNUT WOOD (JUGLANS BINATA) ANJAM (HARDWICKIA BINATA)\nBIRCH (BETULA SPP.) SISSOO (DALBERGIA SISSO) AND WHITE CEDAR (DYSOZYLUM) AND THE\nLIKE: SEMUL (BOMBAX CEIBA)",
+        "hsn_code": "44039923",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OTHER : SAL (CHOREA ROBUSTA) SANDAL WOOD (SANTALUM ALBUR)\nSEMUL (BOMBAX CEIBA) WALNUT WOOD (JUGLANS BINATA) ANJAM (HARDWICKIA BINATA)\nBIRCH (BETULA SPP.) SISSOO (DALBERGIA SISSO) AND WHITE CEDAR (DYSOZYLUM) AND THE\nLIKE: SANDAL WOOD (SANTALUM ALBURN)",
+        "hsn_code": "44039922",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OTHER : SAL (CHOREA ROBUSTA) SANDAL WOOD (SANTALUM ALBUR)\nSEMUL (BOMBAX CEIBA) WALNUT WOOD (JUGLANS BINATA) ANJAM (HARDWICKIA BINATA)\nBIRCH (BETULA SPP.) SISSOO (DALBERGIA SISSO) AND WHITE CEDAR (DYSOZYLUM) AND THE\nLIKE: SAL (CHOREA ROBUSTA)",
+        "hsn_code": "44039921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OTHER : ANDAMAN PADAUK (PTEROCAROUS DALBAERGIODES) BONSUM\n(PHOEBE GOALPARENSIS) GURGAN (DIPTEROCARPUS ALATUS) KHAIR (ACACIA CATECHU)\nLAMPATI (DUABANGA GRANDIFLORA) LAUREL (TERMINALIA ALATA) PALIWOOD (PALAQUIUM\nELLIPLICUM) AND RED SANDERS (PTEROCAR PUS SAUTANINUS) AND ROSE WOOD (DALBERGEA\nLATIFOLIO): ROSE WOOD (DALBERGEA LATIFOLIO)",
+        "hsn_code": "44039919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OTHER : ANDAMAN PADAUK (PTEROCAROUS DALBAERGIODES) BONSUM\n(PHOEBE GOALPARENSIS) GURGAN (DIPTEROCARPUS ALATUS) KHAIR (ACACIA CATECHU)\nLAMPATI (DUABANGA GRANDIFLORA) LAUREL (TERMINALIA ALATA) PALIWOOD (PALAQUIUM\nELLIPLICUM) AND RED SANDERS (PTEROCAR PUS SAUTANINUS) AND ROSE WOOD (DALBERGEA\nLATIFOLIO): RED SANDERS(PTEROCAR PUS SAUTATINUS)",
+        "hsn_code": "44039918",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OTHER : ANDAMAN PADAUK (PTEROCAROUS DALBAERGIODES) BONSUM\n(PHOEBE GOALPARENSIS) GURGAN (DIPTEROCARPUS ALATUS) KHAIR (ACACIA CATECHU)\nLAMPATI (DUABANGA GRANDIFLORA) LAUREL (TERMINALIA ALATA) PALIWOOD (PALAQUIUM\nELLIPLICUM) AND RED SANDERS (PTEROCAR PUS SAUTANINUS) AND ROSE WOOD (DALBERGEA\nLATIFOLIO): PALIWOOD (PALAQUIUM ELLIPLICUM)",
+        "hsn_code": "44039917",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OTHER : ANDAMAN PADAUK (PTEROCAROUS DALBAERGIODES) BONSUM\n(PHOEBE GOALPARENSIS) GURGAN (DIPTEROCARPUS ALATUS) KHAIR (ACACIA CATECHU)\nLAMPATI (DUABANGA GRANDIFLORA) LAUREL (TERMINALIA ALATA) PALIWOOD (PALAQUIUM\nELLIPLICUM) AND RED SANDERS (PTEROCAR PUS SAUTANINUS) AND ROSE WOOD (DALBERGEA\nLATIFOLIO): LAUREL (TERMINALIA ALATA)",
+        "hsn_code": "44039916",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OTHER : ANDAMAN PADAUK (PTEROCAROUS DALBAERGIODES) BONSUM\n(PHOEBE GOALPARENSIS) GURGAN (DIPTEROCARPUS ALATUS) KHAIR (ACACIA CATECHU)\nLAMPATI (DUABANGA GRANDIFLORA) LAUREL (TERMINALIA ALATA) PALIWOOD (PALAQUIUM\nELLIPLICUM) AND RED SANDERS (PTEROCAR PUS SAUTANINUS) AND ROSE WOOD (DALBERGEA\nLATIFOLIO): LAMPATI (DUABANGA GRANDIFLORA)",
+        "hsn_code": "44039915",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OTHER : ANDAMAN PADAUK (PTEROCAROUS DALBAERGIODES) BONSUM\n(PHOEBE GOALPARENSIS) GURGAN (DIPTEROCARPUS ALATUS) KHAIR (ACACIA CATECHU)\nLAMPATI (DUABANGA GRANDIFLORA) LAUREL (TERMINALIA ALATA) PALIWOOD (PALAQUIUM\nELLIPLICUM) AND RED SANDERS (PTEROCAR PUS SAUTANINUS) AND ROSE WOOD (DALBERGEA\nLATIFOLIO): KHAIR (ACACIA CATECHU)",
+        "hsn_code": "44039914",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OTHER : ANDAMAN PADAUK (PTEROCAROUS DALBAERGIODES) BONSUM\n(PHOEBE GOALPARENSIS) GURGAN (DIPTEROCARPUS ALATUS) KHAIR (ACACIA CATECHU)\nLAMPATI (DUABANGA GRANDIFLORA) LAUREL (TERMINALIA ALATA) PALIWOOD (PALAQUIUM\nELLIPLICUM) AND RED SANDERS (PTEROCAR PUS SAUTANINUS) AND ROSE WOOD (DALBERGEA\nLATIFOLIO): GURGAN (DIPTEROCARPUS ALATUS)",
+        "hsn_code": "44039913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OTHER : ANDAMAN PADAUK (PTEROCAROUS DALBAERGIODES) BONSUM\n(PHOEBE GOALPARENSIS) GURGAN (DIPTEROCARPUS ALATUS) KHAIR (ACACIA CATECHU)\nLAMPATI (DUABANGA GRANDIFLORA) LAUREL (TERMINALIA ALATA) PALIWOOD (PALAQUIUM\nELLIPLICUM) AND RED SANDERS (PTEROCAR PUS SAUTANINUS) AND ROSE WOOD (DALBERGEA\nLATIFOLIO): BONSUM (PHOEBE GOALPARENSIS)",
+        "hsn_code": "44039912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OTHER : ANDAMAN PADAUK (PTEROCAROUS DALBAERGIODES) BONSUM\n(PHOEBE GOALPARENSIS) GURGAN (DIPTEROCARPUS ALATUS) KHAIR (ACACIA CATECHU)\nLAMPATI (DUABANGA GRANDIFLORA) LAUREL (TERMINALIA ALATA) PALIWOOD (PALAQUIUM\nELLIPLICUM) AND RED SANDERS (PTEROCAR PUS SAUTANINUS) AND ROSE WOOD (DALBERGEA\nLATIFOLIO): ANDAMAN PADAUK (PTEROCAROUS DALBAERGIODES)",
+        "hsn_code": "44039911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OF BEECH (FAGUS SPP.)",
+        "hsn_code": "44039200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER : OF OAK (QUERCUS SPP.)",
+        "hsn_code": "44039100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER, OF TROPICAL WOOD SPECIFIED IN SUBHEADING NOTE 1 TO THIS CHAPTER :\nOTHER :OTHER",
+        "hsn_code": "44034990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER, OF TROPICAL WOOD SPECIFIED IN SUBHEADING NOTE 1 TO THIS CHAPTER :\nOTHER :TEAK WOOD IN ROUGH",
+        "hsn_code": "44034910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER, OF TROPICAL WOOD SPECIFIED IN SUBHEADING NOTE 1 TO THIS CHAPTER :\nDARK RED MERANTI, LIGHT RED MERANTI AND MERANTI BAKAU",
+        "hsn_code": "44034100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER, CONIFEROUS : OTHER",
+        "hsn_code": "44032090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER, CONIFEROUS : POLES, PILLING AND POSTS",
+        "hsn_code": "44032020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - OTHER, CONIFEROUS : SAWLOGS AND VENEERLOGS",
+        "hsn_code": "44032010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD IN THE ROUGH, WHETHER OR NOT STRIPPED OF BARK OR SAPWOOD, OR ROUGHLY\nSQUARED - TREATED WITH PAINT, STAINS, CREOSOTE OR OTHER PRESERVATIVES",
+        "hsn_code": "44031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FUR AND ARTICLES THEREOF",
+        "hsn_code": "4304",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FUR AND ARTICLES THEREOF 4304 00 - ARTIFICIAL FUR AND ARTICLES THEREOF:\nARTICLES OF ARTIFICIAL FUR",
+        "hsn_code": "43040020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FUR AND ARTICLES THEREOF 4304 00 - ARTIFICIAL FUR AND ARTICLES THEREOF:\nARTIFICIAL FUR :OTHER",
+        "hsn_code": "43040019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL FUR AND ARTICLES THEREOF 4304 00 - ARTIFICIAL FUR AND ARTICLES THEREOF:\nARTIFICIAL FUR : ARTIFICIAL FUR AS TRIMMINGS AND EMBELLISHMENTS FOR GARMENTS, MADE\nUPS, KNITWEAR, PLASTIC AND LEATHER GOODS",
+        "hsn_code": "43040011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL, CLOTHING ACCESSORIES AND OTHER ARTICLES OF FURSKIN",
+        "hsn_code": "4303",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL, CLOTHING ACCESSORIES AND OTHER ARTICLES OF FURSKIN - OTHER:\nOTHER",
+        "hsn_code": "43039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL, CLOTHING ACCESSORIES AND OTHER ARTICLES OF FURSKIN - OTHER: OF\nANIMALS COVERED UNDER CONVENTION ON INTERNATIONAL TRADE OF ENDANGERED SPECIES\n(CITES), OTHER THAN THOSE OF TARIFF ITEM 4303 90 10",
+        "hsn_code": "43039020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL, CLOTHING ACCESSORIES AND OTHER ARTICLES OF FURSKIN - OTHER: OF\nWILD ANIMALS COVERED UNDER THE WILD LIFE (PROTECTION) ACT, 1972",
+        "hsn_code": "43039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL, CLOTHING ACCESSORIES AND OTHER ARTICLES OF FURSKIN - ARTICLES OF\nAPPAREL AND CLOTHING ACCESSORIES: OTHER",
+        "hsn_code": "43031090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL, CLOTHING ACCESSORIES AND OTHER ARTICLES OF FURSKIN - ARTICLES OF\nAPPAREL AND CLOTHING ACCESSORIES: OF ANIMALS COVERED UNDER CONVENTION ON\nINTERNATIONAL TRADE OF ENDANGERED SPECIES (CITES), OTHER THAN THOSE OF TARIFF ITEM\n4303 10 10",
+        "hsn_code": "43031020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL, CLOTHING ACCESSORIES AND OTHER ARTICLES OF FURSKIN - ARTICLES OF\nAPPAREL AND CLOTHING ACCESSORIES: OF WILD ANIMALS COVERED UNDER THE WILD LIFE\n(PROTECTION) ACT, 1972",
+        "hsn_code": "43031010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR DRESSED FURSKINS (INCLUDING HEADS, TAILS, PAWS AND OTHER PIECES OR CUTTINGS), UNASSEMBLED, OR ASSEMBLED (WITHOUT THE ADDITION OF OTHER MATERIALS) OTHER THAN THOSE OF HEADING 4303",
+        "hsn_code": "4302",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR DRESSED FURSKINS (INCLUDING HEADS, TAILS, PAWS AND OTHER PIECES OR\nCUTTINGS), UNASSEMBLED, OR ASSEMBLED (WITHOUT THE ADDITION OF OTHER MATERIALS)\nOTHER THAN THOSE OF HEADING 4303 WHOLE SKINS AND PIECES OR CUTTINGS THEREOF,\nASSEMBLED",
+        "hsn_code": "43023000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR DRESSED FURSKINS (INCLUDING HEADS, TAILS, PAWS AND OTHER PIECES OR\nCUTTINGS), UNASSEMBLED, OR ASSEMBLED (WITHOUT THE ADDITION OF OTHER MATERIALS)\nOTHER THAN THOSE OF HEADING 4303 HEADS, TAILS, PAWS AND OTHER PIECES OR CUTTINGS,\nNOT ASSEMBLED",
+        "hsn_code": "43022000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR DRESSED FURSKINS (INCLUDING HEADS, TAILS, PAWS AND OTHER PIECES OR\nCUTTINGS), UNASSEMBLED, OR ASSEMBLED (WITHOUT THE ADDITION OF OTHER MATERIALS)\nOTHER THAN THOSE OF HEADING 4303 - WHOLE SKINS, WITH OR WITHOUT HEAD, TAIL OR\nPAWS, NOT ASSEMBLED:- OTHER - HIDES AND SKINS OF OTHER ANIMALS WITH HAIR ON,\nTANNED OR DRESSED",
+        "hsn_code": "43021990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR DRESSED FURSKINS (INCLUDING HEADS, TAILS, PAWS AND OTHER PIECES OR\nCUTTINGS), UNASSEMBLED, OR ASSEMBLED (WITHOUT THE ADDITION OF OTHER MATERIALS)\nOTHER THAN THOSE OF HEADING 4303 - WHOLE SKINS, WITH OR WITHOUT HEAD, TAIL OR\nPAWS, NOT ASSEMBLED:- OTHER - TIGER-CAT SKINS",
+        "hsn_code": "43021940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR DRESSED FURSKINS (INCLUDING HEADS, TAILS, PAWS AND OTHER PIECES OR\nCUTTINGS), UNASSEMBLED, OR ASSEMBLED (WITHOUT THE ADDITION OF OTHER MATERIALS)\nOTHER THAN THOSE OF HEADING 4303 - WHOLE SKINS, WITH OR WITHOUT HEAD, TAIL OR\nPAWS, NOT ASSEMBLED:- OTHER - GOAT (COMMON) AND KID SKINS WITH HAIR ON, TANNED OR\nDRESSED",
+        "hsn_code": "43021930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR DRESSED FURSKINS (INCLUDING HEADS, TAILS, PAWS AND OTHER PIECES OR\nCUTTINGS), UNASSEMBLED, OR ASSEMBLED (WITHOUT THE ADDITION OF OTHER MATERIALS)\nOTHER THAN THOSE OF HEADING 4303 - WHOLE SKINS, WITH OR WITHOUT HEAD, TAIL OR\nPAWS, NOT ASSEMBLED:- OTHER - HIDES OR SKINS OF OTHER BOVINE AND EQUINE ANIMALS\nWITH HAIR ON, TANNED OR DRESSED",
+        "hsn_code": "43021920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR DRESSED FURSKINS (INCLUDING HEADS, TAILS, PAWS AND OTHER PIECES OR\nCUTTINGS), UNASSEMBLED, OR ASSEMBLED (WITHOUT THE ADDITION OF OTHER MATERIALS)\nOTHER THAN THOSE OF HEADING 4303 - WHOLE SKINS, WITH OR WITHOUT HEAD, TAIL OR\nPAWS, NOT ASSEMBLED:- OTHER - CALF SKINS, WITH HAIR ON, TANNED OR DRESSED",
+        "hsn_code": "43021910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR DRESSED FURSKINS (INCLUDING HEADS, TAILS, PAWS AND OTHER PIECES OR\nCUTTINGS), UNASSEMBLED, OR ASSEMBLED (WITHOUT THE ADDITION OF OTHER MATERIALS)\nOTHER THAN THOSE OF HEADING 4303 - WHOLE SKINS, WITH OR WITHOUT HEAD, TAIL OR\nPAWS, NOT ASSEMBLED:OF LAMB, THE FOLLOWING : ASTRAKHAN, BROAD TAIL, CARACUL,\nPERSIAN AND SIMILAR LAMB, INDIAN, CHINESE, MONGOLIAN OR TIBETAN LAMB",
+        "hsn_code": "43021300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR DRESSED FURSKINS (INCLUDING HEADS, TAILS, PAWS AND OTHER PIECES OR\nCUTTINGS), UNASSEMBLED, OR ASSEMBLED (WITHOUT THE ADDITION OF OTHER MATERIALS)\nOTHER THAN THOSE OF HEADING 4303 - WHOLE SKINS, WITH OR WITHOUT HEAD, TAIL OR\nPAWS, NOT ASSEMBLED:OF MINK",
+        "hsn_code": "43021100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAW FURSKINS (INCLUDING HEADS, TAILS, PAWS AND OTHER PIECES OR CUTTINGS, SUITABLE FOR FURRIERS’ USE), OTHER THAN RAW HIDES AND SKINS OF HEADINGS 4101, 4102 OR 4103",
+        "hsn_code": "4301",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAW FURSKINS (INCLUDING HEADS, TAILS, PAWS AND OTHER PIECES OR CUTTINGS, SUITABLE\nFOR FURRIERS USE), OTHER THAN RAW HIDES AND SKINS OF HEADING 4101, 4102 OR 4103 -\nHEADS, TAILS, PAWS AND OTHER PIECES OR CUTTINGS, SUITABLE FOR FURRIERS USE",
+        "hsn_code": "43019000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAW FURSKINS (INCLUDING HEADS, TAILS, PAWS AND OTHER PIECES OR CUTTINGS, SUITABLE\nFOR FURRIERS USE), OTHER THAN RAW HIDES AND SKINS OF HEADING 4101, 4102 OR 4103 -\nOTHER FURSKINS, WHOLE, WITH OR WITHOUT HEAD, TAIL OR PAWS",
+        "hsn_code": "43018000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAW FURSKINS (INCLUDING HEADS, TAILS, PAWS AND OTHER PIECES OR CUTTINGS, SUITABLE\nFOR FURRIERS USE), OTHER THAN RAW HIDES AND SKINS OF HEADING 4101, 4102 OR 4103 - OF\nSEAL, WHOLE, WITH OR WITHOUT HEAD, TAIL OR PAWS",
+        "hsn_code": "43017000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAW FURSKINS (INCLUDING HEADS, TAILS, PAWS AND OTHER PIECES OR CUTTINGS, SUITABLE\nFOR FURRIERS USE), OTHER THAN RAW HIDES AND SKINS OF HEADING 4101, 4102 OR 4103 - OF\nFOX, WHOLE, WITH OR WITHOUT HEAD, TAIL OR PAWS",
+        "hsn_code": "43016000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAW FURSKINS (INCLUDING HEADS, TAILS, PAWS AND OTHER PIECES OR CUTTINGS, SUITABLE\nFOR FURRIERS USE), OTHER THAN RAW HIDES AND SKINS OF HEADING 4101, 4102 OR 4103 - OF\nLAMB, THE FOLLOWING : ASTRAKHAN, BROADTAIL, CARACUL, PERSIAN AND SIMILAR LAMB,\nINDIAN, CHINESE, MONGOLIAN OR TIBETAN LAMB, WHOLE, WITH OR WITHOUT HEAD, TAIL OR\nPAWS",
+        "hsn_code": "43013000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RAW FURSKINS (INCLUDING HEADS, TAILS, PAWS AND OTHER PIECES OR CUTTINGS, SUITABLE\nFOR FURRIERS USE), OTHER THAN RAW HIDES AND SKINS OF HEADING 4101, 4102 OR 4103 - OF\nMINK, WHOLE, WITH OR WITHOUT HEAD, TAIL OR PAWS",
+        "hsn_code": "43011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF GUT (OTHER THAN SILK-WORM GUT), OF GOLDBEATER’S SKIN, OF BLADDERS OR OF TENDONS",
+        "hsn_code": "4206",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF GUT (OTHER THAN SILK-WORM GUT), OF GOLDBEATEQS SKIN, OF BLADDERS OR OF\nTENDONS - OTHER",
+        "hsn_code": "42069000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF GUT (OTHER THAN SILK-WORM GUT), OF GOLDBEATEQS SKIN, OF BLADDERS OR OF\nTENDONS - CATGUT : OTHER",
+        "hsn_code": "42061090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF GUT (OTHER THAN SILK-WORM GUT), OF GOLDBEATEQS SKIN, OF BLADDERS OR OF\nTENDONS - CATGUT : FOR RACKETS",
+        "hsn_code": "42061010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF GUT (OTHER THAN SILK-WORM GUT), OF GOLDBEATEPS SKIN, OF BLADDERS OR OF\nTENDONS --- OTHER",
+        "hsn_code": "42060090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF GUT (OTHER THAN SILK-WORM GUT), OF GOLDBEATER`S SKIN, OF BLADDERS OR OF\nTENDONS --- FOR RACKETS",
+        "hsn_code": "42060010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF LEATHER OR OF COMPOSITION LEATHER",
+        "hsn_code": "4205",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF LEATHER OR OF COMPOSITION LEATHER - OTHER ARTICLES OF LEATHER OR\nOF COMPOSITION LEATHER : OTHER",
+        "hsn_code": "42050090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF LEATHER OR OF COMPOSITION LEATHER - OTHER ARTICLES OF LEATHER OR\nOF COMPOSITION LEATHER : LEATHER SOFA COVER",
+        "hsn_code": "42050020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF LEATHER OR OF COMPOSITION LEATHER - OTHER ARTICLES OF LEATHER OR\nOF COMPOSITION LEATHER : STRAPS OTHER THAN FOR MACHINERY OR HARNESS : OTHER",
+        "hsn_code": "42050019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF LEATHER OR OF COMPOSITION LEATHER - OTHER ARTICLES OF LEATHER OR\nOF COMPOSITION LEATHER : STRAPS OTHER THAN FOR MACHINERY OR HARNESS : WEL T",
+        "hsn_code": "42050011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES, OF LEATHER OR OF COMPOSITION LEATHER",
+        "hsn_code": "4203",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES, OF LEATHER OR OF COMPOSITION LEATHER\n- OTHER CLOTHING ACCESSORIES : OTHER",
+        "hsn_code": "42034090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES, OF LEATHER OR OF COMPOSITION LEATHER\n- OTHER CLOTHING ACCESSORIES : SEMI-CHROME GRAIN GARMENTS",
+        "hsn_code": "42034020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES, OF LEATHER OR OF COMPOSITION LEATHER\n- OTHER CLOTHING ACCESSORIES : APRONS",
+        "hsn_code": "42034010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES, OF LEATHER OR OF COMPOSITION LEATHER\n- BELTS AND BANDOLIERS",
+        "hsn_code": "42033000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES, OF LEATHER OR OF COMPOSITION LEATHER\n- GLOVES, MITTENS AND MITTS : OTHER : MITTENS AND MITTS",
+        "hsn_code": "42032930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES, OF LEATHER OR OF COMPOSITION LEATHER\n- GLOVES, MITTENS AND MITTS : OTHER : OTHER GLOVES",
+        "hsn_code": "42032920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES, OF LEATHER OR OF COMPOSITION LEATHER\n- GLOVES, MITTENS AND MITTS : OTHER : GLOVES FOR USE IN INDUSTRY",
+        "hsn_code": "42032910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES, OF LEATHER OR OF COMPOSITION LEATHER\n- GLOVES, MITTENS AND MITTS : SPECIALLY DESIGNED FOR USE IN SPORTS : MITTENS AND MITTS",
+        "hsn_code": "42032120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES, OF LEATHER OR OF COMPOSITION LEATHER\n- GLOVES, MITTENS AND MITTS : SPECIALLY DESIGNED FOR USE IN SPORTS : GLOVES",
+        "hsn_code": "42032110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES, OF LEATHER OR OF COMPOSITION LEATHER\n- ARTICLES OF APPAREL : OTHER",
+        "hsn_code": "42031090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES, OF LEATHER OR OF COMPOSITION LEATHER\n- ARTICLES OF APPAREL : JACKETS AND JERSEYS",
+        "hsn_code": "42031010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVE-CASES, BRIEF-CASES, SCHOOL SATCHELS, SPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN CASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR BEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES, MAP-CASES, CIGARETTE-CASES, TOBACCO-POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE CASES, JEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR OF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED FIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH PAPER",
+        "hsn_code": "4202",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - OTHER : OTHER",
+        "hsn_code": "42029900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - OTHER : WITH OUTER SURFACE OF PLASTIC SHEETING OR OF TEXTILE MATERIALS",
+        "hsn_code": "42029200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - OTHER : WITH OUTER SURFACE OF LEATHER, OF COMPOSITION LEATHER OR OF PATENT\nLEATHER",
+        "hsn_code": "42029100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - ARTICLES OF A KIND NORMALLY CARRIED IN THE POCKET OR IN THE HANDBAG : WITH\nOUTER SURFACE OF LEATHER, OF COMPOSITION LEATHER OR OF PATENT LEATHER : WALLETS\nAND PURSES, OF LEATHER",
+        "hsn_code": "42023120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - HAND-BAGS, WHETHER OR NOT WITH SHOULDER STRAP, INCLUDING THOSE WITHOUT\nHANDLE : WITH OUTER SURFACE OF LEATHER, OF COMPOSITION LEATHER OR OF PATENT\nLEATHER : OTHER",
+        "hsn_code": "42022190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - HAND-BAGS, WHETHER OR NOT WITH SHOULDER STRAP, INCLUDING THOSE WITHOUT\nHANDLE : WITH OUTER SURFACE OF LEATHER, OF COMPOSITION LEATHER OR OF PATENT\nLEATHER : VANITY-BAGS",
+        "hsn_code": "42022120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - HAND-BAGS, WHETHER OR NOT WITH SHOULDER STRAP, INCLUDING THOSE WITHOUT\nHANDLE : WITH OUTER SURFACE OF LEATHER, OF COMPOSITION LEATHER OR OF PATENT\nLEATHER : HAND-BAGS FOR LADIES",
+        "hsn_code": "42022110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : OTHER : OTHER",
+        "hsn_code": "42021990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : OTHER : VANITY-CASES",
+        "hsn_code": "42021960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : OTHER : EXECUTIVE-CASES",
+        "hsn_code": "42021950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : OTHER : BRIEF-CASES (OTHER THAN PLASTIC MOULDED)",
+        "hsn_code": "42021940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : OTHER : SATCHELS",
+        "hsn_code": "42021930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : OTHER : TOILET-CASES",
+        "hsn_code": "42021920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : OTHER : TRAVEL GOODS (TRUNKS, SUIT-CASES, SPORTS\nBAGS, AND OTHER SIMILAR ITEMS) OF LEATHER",
+        "hsn_code": "42021910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : WITH OUTER SURFACE OF PLASTIC OR OF TEXTILE\nMATERIALS : OTHER",
+        "hsn_code": "42021290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : WITH OUTER SURFACE OF PLASTIC OR OF TEXTILE\nMATERIALS : VANITY-CASES",
+        "hsn_code": "42021280",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : WITH OUTER SURFACE OF PLASTIC OR OF TEXTILE\nMATERIALS : EXECUTIVE-CASES OTHER THAN PLASTIC MOULDED",
+        "hsn_code": "42021270",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : WITH OUTER SURFACE OF PLASTIC OR OF TEXTILE\nMATERIALS : BRIEF-CASES",
+        "hsn_code": "42021260",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : WITH OUTER SURFACE OF PLASTIC OR OF TEXTILE\nMATERIALS : OTHER TRAVEL-GOODS",
+        "hsn_code": "42021250",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : WITH OUTER SURFACE OF PLASTIC OR OF TEXTILE\nMATERIALS : SATCHELS",
+        "hsn_code": "42021240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : WITH OUTER SURFACE OF PLASTIC OR OF TEXTILE\nMATERIALS : PLASTIC MOULDED BRIEF-CASES",
+        "hsn_code": "42021230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : WITH OUTER SURFACE OF PLASTIC OR OF TEXTILE\nMATERIALS : PLASTIC MOULDED SUIT-CASES",
+        "hsn_code": "42021220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : WITH OUTER SURFACE OF PLASTIC OR OF TEXTILE\nMATERIALS : TOILET-CASES",
+        "hsn_code": "42021210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : WITH OUTER SURFACE OF LEATHER, OF COMPOSITION\nLEATHER OR OF PATENT LEATHER : OTHER",
+        "hsn_code": "42021190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : WITH OUTER SURFACE OF LEATHER, OF COMPOSITION\nLEATHER OR OF PATENT LEATHER : ATTACHE-CASES",
+        "hsn_code": "42021170",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : WITH OUTER SURFACE OF LEATHER, OF COMPOSITION\nLEATHER OR OF PATENT LEATHER : VANITY-CASES",
+        "hsn_code": "42021160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : WITH OUTER SURFACE OF LEATHER, OF COMPOSITION\nLEATHER OR OF PATENT LEATHER : EXECUTIVE-CASES",
+        "hsn_code": "42021150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : WITH OUTER SURFACE OF LEATHER, OF COMPOSITION\nLEATHER OR OF PATENT LEATHER : BRIEF-CASES",
+        "hsn_code": "42021140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : WITH OUTER SURFACE OF LEATHER, OF COMPOSITION\nLEATHER OR OF PATENT LEATHER : SATCHELS",
+        "hsn_code": "42021130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : WITH OUTER SURFACE OF LEATHER, OF COMPOSITION\nLEATHER OR OF PATENT LEATHER : TOILET-BAGS AND CASES, OF LEATHER",
+        "hsn_code": "42021120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TRUNKS, SUIT-CASES, VANITY-CASES, EXECUTIVECASES, BRIEF-CASES, SCHOOL SATCHELS,\nSPECTACLE CASES, BINOCULAR CASES, CAMERA CASES, MUSICAL INSTRUMENT CASES, GUN\nCASES, HOLSTERS AND SIMILAR CONTAINERS; TRAVELLING-BAGS, INSULATED FOOD OR\nBEVERAGES BAGS, TOILET BAGS, RUCKSACKS, HANDBAGS, SHOPPING-BAGS, WALLETS, PURSES,\nMAPCASES, CIGARETTE-CASES, TOBACCO- POUCHES, TOOL BAGS, SPORTS BAGS, BOTTLE-CASES,\nJEWELLERY BOXES, POWDER-BOXES, CUTLERY CASES AND SIMILAR CONTAINERS, OF LEATHER OR\nOF COMPOSITION LEATHER, OF SHEETING OF PLASTICS, OF TEXTILE MATERIALS, OF VULCANISED\nFIBRE OR OF PAPERBOARD, OR WHOLLY OR MAINLY COVERED WITH SUCH MATERIALS OR WITH\nPAPER - TRUNK\\S, SUIT-CASES, VANITY-CASES, EXECUTIVE CASES, BRIEF-CASES, SCHOOL\nSATCHELS AND SIMILAR CONTAINERS : WITH OUTER SURFACE OF LEATHER, OF COMPOSITION\nLEATHER OR OF PATENT LEATHER : TRAVEL GOODS (TRUNKS, SUIT-CASES, SPORTS BAGS AND\nOTHER SIMILAR ITEMS ) OF LEATHER",
+        "hsn_code": "42021110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPOSITION LEATHER WITH A BASIS OF LEATHER OR LEATHER FIBER, IN SLABS, SHEETS OR STRIP, WHETHER OR NOT IN ROLLS; PARINGS AND OTHER WASTE OF LEATHER OR OF COMPOSITION LEATHER, NOT SUITABLE FOR THE MANUFACTURE OF LEATHER ARTICLES; LEATHER DUST, POWDER AND FLOUR",
+        "hsn_code": "4201",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SADDLERY AND HARNESS FOR ANY ANIMAL (INCLUDING TRACES, LEADS, KNEE PADS, MUZZLES,\nSADDLE CLOTHS, SADDLE BAGS, DOG COATS AND THE LIKE), OF ANY MATERIAL",
+        "hsn_code": "42010000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPOSITION LEATHER WITH A BASIS OF LEATHER OR LEATHER FIBER, IN SLABS, SHEETS OR STRIP, WHETHER OR NOT IN ROLLS; PARINGS AND OTHER WASTE OF LEATHER OR OF COMPOSITION LEATHER, NOT SUITABLE FOR THE MANUFACTURE OF LEATHER ARTICLES; LEATHER DUST, POWDER AND FLOUR",
+        "hsn_code": "4115",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COMPOSITION LEATHER WITH A BASIS OF LEATHER OR LEATHER FIBER, IN SLABS, SHEETS OR\nSTRIP, WHETHER OR NOT IN ROLLS; PARINGS AND OTHER WASTE OF LEATHER OR OF\nCOMPOSITION LEATHER, NOT SUITABLE FOR THE MANUFACTURE OF LEATHER ARTICLES;\nLEATHER DUST, POWDER AND FLOUR - PARINGS AND OTHER WASTE OF LEATHER OR OF\nCOMPOSITION LEATHER, NOT SUITABLE FOR THE MANUFACTURE OF LEATHER ARTICLES ;\nLEATHER DUST, POWDER AND FLOUR : OTHER",
+        "hsn_code": "41152090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COMPOSITION LEATHER WITH A BASIS OF LEATHER OR LEATHER FIBER, IN SLABS, SHEETS OR\nSTRIP, WHETHER OR NOT IN ROLLS; PARINGS AND OTHER WASTE OF LEATHER OR OF\nCOMPOSITION LEATHER, NOT SUITABLE FOR THE MANUFACTURE OF LEATHER ARTICLES;\nLEATHER DUST, POWDER AND FLOUR - PARINGS AND OTHER WASTE OF LEATHER OR OF\nCOMPOSITION LEATHER, NOT SUITABLE FOR THE MANUFACTURE OF LEATHER ARTICLES ;\nLEATHER DUST, POWDER AND FLOUR: CUTTINGS OF LEATHER",
+        "hsn_code": "41152010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COMPOSITION LEATHER WITH A BASIS OF LEATHER OR LEATHER FIBER, IN SLABS, SHEETS OR\nSTRIP, WHETHER OR NOT IN ROLLS; PARINGS AND OTHER WASTE OF LEATHER OR OF\nCOMPOSITION LEATHER, NOT SUITABLE FOR THE MANUFACTURE OF LEATHER ARTICLES;\nLEATHER DUST, POWDER AND FLOUR - COMPOSITION LEATHER WITH A BASIS OF LEATHER OR\nLEATHER FIBER, IN SLABS, SHEETS OR STRIP, WHETHER OR NOT IN ROLLS",
+        "hsn_code": "41151000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CHAMOIS (INCLUDING COMBINATION CHAMOIS) LEATHER; PATENT LEATHER AND PATENT LAMINATED LEATHER ; METALLISED LEATHER",
+        "hsn_code": "4114",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CHAMOIS (INCLUDING COMBINATION CHAMOIS) LEATHER; PATENT LEATHER AND PATENT\nLAMINATED LEATHER ; METALLISED LEATHER - PATENT LEATHER AND PATENT LAMINATED\nLEATHER; METALLISED LEATHER :METALLISED LEATHER",
+        "hsn_code": "41142020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CHAMOIS (INCLUDING COMBINATION CHAMOIS) LEATHER; PATENT LEATHER AND PATENT\nLAMINATED LEATHER ; METALLISED LEATHER - PATENT LEATHER AND PATENT LAMINATED\nLEATHER; METALLISED LEATHER :PATENT LEATHER AND PATENT LAMINATED LEATHER",
+        "hsn_code": "41142010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CHAMOIS (INCLUDING COMBINATION CHAMOIS) LEATHER; PATENT LEATHER AND PATENT\nLAMINATED LEATHER ; METALLISED LEATHER - CHAMOIS (INCLUDING COMBINATION CHAMOIS)\nLEATHER",
+        "hsn_code": "41141000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LEATHER FURTHER PREPARED AFTER TANNING OR CRUSTING, INCLUDING PARCHMENT-DRESSED LEATHER, OF OTHER ANIMALS, WITHOUT WOOL OR HAIR ON, WHETHER OR NOT SPLIT, OTHER THAN LEATHER OF HEADING 4114",
+        "hsn_code": "4113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LEATHER FURTHER PREPARED AFTER TANNING OR CRUSTING, INCLUDING PARCHMENT-DRESSED\nLEATHER, OF OTHER ANIMALS, WITHOUT WOOL OR HAIR ON, WHETHER OR NOT SPLIT, OTHER\nTHAN LEATHER OF HEADING 4114 - OTHER",
+        "hsn_code": "41139000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LEATHER FURTHER PREPARED AFTER TANNING OR CRUSTING, INCLUDING PARCHMENT-DRESSED\nLEATHER, OF OTHER ANIMALS, WITHOUT WOOL OR HAIR ON, WHETHER OR NOT SPLIT, OTHER\nTHAN LEATHER OF HEADING 4114 - OF REPTILES",
+        "hsn_code": "41133000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LEATHER FURTHER PREPARED AFTER TANNING OR CRUSTING, INCLUDING PARCHMENT-DRESSED\nLEATHER, OF OTHER ANIMALS, WITHOUT WOOL OR HAIR ON, WHETHER OR NOT SPLIT, OTHER\nTHAN LEATHER OF HEADING 4114 - OF SWINE",
+        "hsn_code": "41132000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LEATHER FURTHER PREPARED AFTER TANNING OR CRUSTING, INCLUDING PARCHMENT-DRESSED\nLEATHER, OF OTHER ANIMALS, WITHOUT WOOL OR HAIR ON, WHETHER OR NOT SPLIT, OTHER\nTHAN LEATHER OF HEADING 4114 - OF GOATS OR KIDS",
+        "hsn_code": "41131000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LEATHER FURTHER PREPARED AFTER TANNING OR CRUSTING, INCLUDING PARCHMENT-DRESSED  LEATHER, OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS, WITHOUT HAIR ON, WHETHER OR NOT SPLIT, OTHER THAN LEATHER OF HEADING 4114",
+        "hsn_code": "4112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LEATHER FURTHER PREPARED AFTER TANNING OR CRUSTING, INCLUDING PARCHMENT-DRESSED\nLEATHER, OF SHEEP OR LAMB, WITHOUT WOOL ON, WHETHER OR NOT SPLIT, OTHER THAN\nLEATHER OF HEADING 4114",
+        "hsn_code": "41120000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LEATHER FURTHER PREPARED AFTER TANNING OR CRUSTING, INCLUDING PARCHMENT-DRESSED  LEATHER, OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS, WITHOUT HAIR ON, WHETHER OR NOT SPLIT, OTHER THAN LEATHER OF HEADING 4114",
+        "hsn_code": "4107",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LEATHER FURTHER PREPARED AFTER TANNING OR CRUSTING, INCLUDING PARCHMENT-DRESSED\nLEATHER, OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS, WITHOUT HAIR ON,\nWHETHER OR NOT SPLIT, OTHER THAN LEATHER OF HEADING 4114 - OTHER, INCLUDING SIDES:\nOTHER",
+        "hsn_code": "41079900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LEATHER FURTHER PREPARED AFTER TANNING OR CRUSTING, INCLUDING PARCHMENT-DRESSED\nLEATHER, OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS, WITHOUT HAIR ON,\nWHETHER OR NOT SPLIT, OTHER THAN LEATHER OF HEADING 4114 - OTHER, INCLUDING SIDES:\nGRAIN SPLITS",
+        "hsn_code": "41079200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LEATHER FURTHER PREPARED AFTER TANNING OR CRUSTING, INCLUDING PARCHMENT-DRESSED\nLEATHER, OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS, WITHOUT HAIR ON,\nWHETHER OR NOT SPLIT, OTHER THAN LEATHER OF HEADING 4114 - OTHER, INCLUDING SIDES:\nFULL GRAINS, UNSPLIT",
+        "hsn_code": "41079100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LEATHER FURTHER PREPARED AFTER TANNING OR CRUSTING, INCLUDING PARCHMENT-DRESSED\nLEATHER, OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS, WITHOUT HAIR ON,\nWHETHER OR NOT SPLIT, OTHER THAN LEATHER OF HEADING 4114 - WHOLE HIDES AND SKINS:\nOTHER",
+        "hsn_code": "41071900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LEATHER FURTHER PREPARED AFTER TANNING OR CRUSTING, INCLUDING PARCHMENT-DRESSED\nLEATHER, OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS, WITHOUT HAIR ON,\nWHETHER OR NOT SPLIT, OTHER THAN LEATHER OF HEADING 4114 - WHOLE HIDES AND SKINS:\nGRAIN SPLITS",
+        "hsn_code": "41071200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LEATHER FURTHER PREPARED AFTER TANNING OR CRUSTING, INCLUDING PARCHMENT-DRESSED\nLEATHER, OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS, WITHOUT HAIR ON,\nWHETHER OR NOT SPLIT, OTHER THAN LEATHER OF HEADING 4114 - WHOLE HIDES AND SKINS:\nFULL GRAINS, UNSPLIT",
+        "hsn_code": "41071100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR CRUST HIDES AND SKINS OF OTHER ANIMALS, WITHOUT WOOL OR HAIR ON, WHETHER OR NOT SPLIT BUT NOT FURTHER PREPARED",
+        "hsn_code": "4106",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR CRUST HIDES AND SKINS OF OTHER ANIMALS, WITHOUT WOOL OR HAIR ON,\nWHETHER OR NOT SPLIT BUT NOT FURTHER PREPARED - OTHER: IN THE DRY STATE (CRUST)",
+        "hsn_code": "41069200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR CRUST HIDES AND SKINS OF OTHER ANIMALS, WITHOUT WOOL OR HAIR ON,\nWHETHER OR NOT SPLIT BUT NOT FURTHER PREPARED - OTHER: IN THE WET STATE (INCLUDING\nWET-BLUE)",
+        "hsn_code": "41069100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR CRUST HIDES AND SKINS OF OTHER ANIMALS, WITHOUT WOOL OR HAIR ON,\nWHETHER OR NOT SPLIT BUT NOT FURTHER PREPARED - OF REPTILES",
+        "hsn_code": "41064000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR CRUST HIDES AND SKINS OF OTHER ANIMALS, WITHOUT WOOL OR HAIR ON,\nWHETHER OR NOT SPLIT BUT NOT FURTHER PREPARED - OF SWINE: IN THE DRY STATE (CRUST)",
+        "hsn_code": "41063200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR CRUST HIDES AND SKINS OF OTHER ANIMALS, WITHOUT WOOL OR HAIR ON,\nWHETHER OR NOT SPLIT BUT NOT FURTHER PREPARED - OF SWINE: IN THE WET STATE\n(INCLUDING WET-BLUE)",
+        "hsn_code": "41063100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR CRUST HIDES AND SKINS OF OTHER ANIMALS, WITHOUT WOOL OR HAIR ON,\nWHETHER OR NOT SPLIT BUT NOT FURTHER PREPARED - OF GOATS OR KIDS: IN THE DRY STATE\n(CRUST)",
+        "hsn_code": "41062200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR CRUST HIDES AND SKINS OF OTHER ANIMALS, WITHOUT WOOL OR HAIR ON,\nWHETHER OR NOT SPLIT BUT NOT FURTHER PREPARED - OF GOATS OR KIDS: IN THE WET STATE\n(INCLUDING WET-BLUE)",
+        "hsn_code": "41062100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR CRUST SKINS OF SHEEP OR LAMBS, WITHOUT WOOL ON,WHETHER OR NOT SPLIT, BUT NOT FURTHER PREPARED",
+        "hsn_code": "4105",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR CRUST SKINS OF SHEEP OR LAMBS, WITHOUT WOOL ON, WHETHER OR NOT SPLIT,\nBUT NOT FURTHER PREPARED - IN THE DRY STATE (CRUST)",
+        "hsn_code": "41053000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR CRUST SKINS OF SHEEP OR LAMBS, WITHOUT WOOL ON, WHETHER OR NOT SPLIT,\nBUT NOT FURTHER PREPARED - IN THE WET STATE (INCLUDING WET-BLUE)",
+        "hsn_code": "41051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR CRUST HIDES AND SKINS OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS, WITHOUT HAIR ON, WHETHER OR NOT SPLIT, BUT NOT FURTHER PREPARED",
+        "hsn_code": "4104",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR CRUST HIDES AND SKINS OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS,\nWITHOUT HAIR ON, WHETHER OR NOT SPLIT, BUT NOT FURTHER PREPARED - IN THE DRY STATE\n(CRUST): OTHER",
+        "hsn_code": "41044900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR CRUST HIDES AND SKINS OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS,\nWITHOUT HAIR ON, WHETHER OR NOT SPLIT, BUT NOT FURTHER PREPARED - IN THE DRY STATE\n(CRUST): FULL GRAINS, UNSPLIT; GRAIN SPLITS",
+        "hsn_code": "41044100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR CRUST HIDES AND SKINS OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS,\nWITHOUT HAIR ON, WHETHER OR NOT SPLIT, BUT NOT FURTHER PREPARED - IN THE WET STATE\n(INCLUDING WET-BLUE): OTHER",
+        "hsn_code": "41041900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TANNED OR CRUST HIDES AND SKINS OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS,\nWITHOUT HAIR ON, WHETHER OR NOT SPLIT, BUT NOT FURTHER PREPARED - IN THE WET STATE\n(INCLUDING WET-BLUE): FULL GRAINS, UNSPLIT; GRAIN SPLITS",
+        "hsn_code": "41041100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER RAW HIDES AND SKINS (FRESH, OR SALTED, DRIED, LIMED, PICKLED OR OTHERWISE PRESERVED, BUT NOT TANNED, PARCHMENT-DRESSED OR FURTHER PREPARED), WHETHER OR NOT DEHAIRED OR SPLIT, OTHER THAN THOSE EXCLUDED BY NOTE 1(b) OR 1(c) TO THIS CHAPTER",
+        "hsn_code": "4103",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER RAW HIDES AND SKINS (FRESH, OR SALTED, DRIED, LIMED, PICKLED OR OTHERWISE\nPRESERVED, BUT NOT TANNED, PARCHMENT-DRESSED OR FURTHER PREPARED), WHETHER OR\nNOT DEHAIRED OR SPLIT, OTHER THAN THOSE EXCLUDED BY NOTE 1(B) OR 1(C) TO THIS\nCHAPTER - OTHER",
+        "hsn_code": "41039000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER RAW HIDES AND SKINS (FRESH, OR SALTED, DRIED, LIMED, PICKLED OR OTHERWISE\nPRESERVED, BUT NOT TANNED, PARCHMENT-DRESSED OR FURTHER PREPARED), WHETHER OR\nNOT DEHAIRED OR SPLIT, OTHER THAN THOSE EXCLUDED BY NOTE 1(B) OR 1(C) TO THIS\nCHAPTER - OF SWINE",
+        "hsn_code": "41033000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER RAW HIDES AND SKINS (FRESH, OR SALTED, DRIED, LIMED, PICKLED OR OTHERWISE\nPRESERVED, BUT NOT TANNED, PARCHMENT-DRESSED OR FURTHER PREPARED), WHETHER OR\nNOT DEHAIRED OR SPLIT, OTHER THAN THOSE EXCLUDED BY NOTE 1(B) OR 1(C) TO THIS\nCHAPTER - OF REPTILES",
+        "hsn_code": "41032000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER RAW HIDES AND SKINS (FRESH, OR SALTED, DRIED, LIMED, PICKLED OR OTHERWISE\nPRESERVED, BUT NOT TANNED, PARCHMENT-DRESSED OR FURTHER PREPARED), WHETHER OR\nNOT DEHAIRED OR SPLIT, OTHER THAN THOSE EXCLUDED BY NOTE 1(B) OR 1(C) TO THIS\nCHAPTER - OF GOATS OR KIDS : OTHER",
+        "hsn_code": "41031090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER RAW HIDES AND SKINS (FRESH, OR SALTED, DRIED, LIMED, PICKLED OR OTHERWISE\nPRESERVED, BUT NOT TANNED, PARCHMENT-DRESSED OR FURTHER PREPARED), WHETHER OR\nNOT DEHAIRED OR SPLIT, OTHER THAN THOSE EXCLUDED BY NOTE 1(B) OR 1(C) TO THIS\nCHAPTER - OF GOATS OR KIDS : KID SKINS",
+        "hsn_code": "41031040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER RAW HIDES AND SKINS (FRESH, OR SALTED, DRIED, LIMED, PICKLED OR OTHERWISE\nPRESERVED, BUT NOT TANNED, PARCHMENT-DRESSED OR FURTHER PREPARED), WHETHER OR\nNOT DEHAIRED OR SPLIT, OTHER THAN THOSE EXCLUDED BY NOTE 1(B) OR 1(C) TO THIS\nCHAPTER - OF GOATS OR KIDS : GOAT SKINS, OTHERWISE CURED (DRIED, UNSALTED)",
+        "hsn_code": "41031030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER RAW HIDES AND SKINS (FRESH, OR SALTED, DRIED, LIMED, PICKLED OR OTHERWISE\nPRESERVED, BUT NOT TANNED, PARCHMENT-DRESSED OR FURTHER PREPARED), WHETHER OR\nNOT DEHAIRED OR SPLIT, OTHER THAN THOSE EXCLUDED BY NOTE 1(B) OR 1(C) TO THIS\nCHAPTER - OF GOATS OR KIDS : GOAT SKINS, DRY SALTED",
+        "hsn_code": "41031020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER RAW HIDES AND SKINS (FRESH, OR SALTED, DRIED, LIMED, PICKLED OR OTHERWISE\nPRESERVED, BUT NOT TANNED, PARCHMENT-DRESSED OR FURTHER PREPARED), WHETHER OR\nNOT DEHAIRED OR SPLIT, OTHER THAN THOSE EXCLUDED BY NOTE 1(B) OR 1(C) TO THIS\nCHAPTER - OF GOATS OR KIDS : GOAT SKINS, PICKLED (WET SALTED)",
+        "hsn_code": "41031010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAW SKINS OF SHEEP OR LAMBS (FRESH, OR SALTED, DRIED, LIMED, PICKLED OR OTHERWISE PRESERVED, BUT NOT TANNED, PARCHMENT-DRESSED OR FURTHER PREPARED), WHETHER OR NOT WITH WOOL ON OR SPLIT, OTHER THAN THOSE EXCLUDED BY NOTE 1(c) TO THIS CHAPTER",
+        "hsn_code": "4102",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAW SKINS OF SHEEP OR LAMBS (FRESH, OR SALTED, DRIED, LIMED, PICKLED OR OTHERWISE\nPRESERVED, BUT NOT TANNED, PARCHMENT-DRESSED OR FURTHER PREPARED), WHETHER OR\nNOT WITH WOOL ON OR SPLIT, OTHER THAN THOSE EXCLUDED BY NOTE 1 ( C ) TO THIS\nCHAPTER- WITHOUT WOOL ON : - OTHER : LAMB SKINS",
+        "hsn_code": "41022920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAW SKINS OF SHEEP OR LAMBS (FRESH, OR SALTED, DRIED, LIMED, PICKLED OR OTHERWISE\nPRESERVED, BUT NOT TANNED, PARCHMENT-DRESSED OR FURTHER PREPARED), WHETHER OR\nNOT WITH WOOL ON OR SPLIT, OTHER THAN THOSE EXCLUDED BY NOTE 1 ( C ) TO THIS\nCHAPTER- WITHOUT WOOL ON : - OTHER : SHEEP SKINS",
+        "hsn_code": "41022910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAW SKINS OF SHEEP OR LAMBS (FRESH, OR SALTED, DRIED, LIMED, PICKLED OR OTHERWISE\nPRESERVED, BUT NOT TANNED, PARCHMENT-DRESSED OR FURTHER PREPARED), WHETHER OR\nNOT WITH WOOL ON OR SPLIT, OTHER THAN THOSE EXCLUDED BY NOTE 1 ( C ) TO THIS\nCHAPTER- WITHOUT WOOL ON : - PICKLED : LAMB PELT",
+        "hsn_code": "41022130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAW SKINS OF SHEEP OR LAMBS (FRESH, OR SALTED, DRIED, LIMED, PICKLED OR OTHERWISE\nPRESERVED, BUT NOT TANNED, PARCHMENT-DRESSED OR FURTHER PREPARED), WHETHER OR\nNOT WITH WOOL ON OR SPLIT, OTHER THAN THOSE EXCLUDED BY NOTE 1 ( C ) TO THIS\nCHAPTER- WITHOUT WOOL ON : - PICKLED : LAMB SKINS",
+        "hsn_code": "41022120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAW SKINS OF SHEEP OR LAMBS (FRESH, OR SALTED, DRIED, LIMED, PICKLED OR OTHERWISE\nPRESERVED, BUT NOT TANNED, PARCHMENT-DRESSED OR FURTHER PREPARED), WHETHER OR\nNOT WITH WOOL ON OR SPLIT, OTHER THAN THOSE EXCLUDED BY NOTE 1 ( C ) TO THIS\nCHAPTER- WITHOUT WOOL ON : - PICKLED : SHEEP SKINS",
+        "hsn_code": "41022110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAW SKINS OF SHEEP OR LAMBS (FRESH, OR SALTED, DRIED, LIMED, PICKLED OR OTHERWISE\nPRESERVED, BUT NOT TANNED, PARCHMENT-DRESSED OR FURTHER PREPARED), WHETHER OR\nNOT WITH WOOL ON OR SPLIT, OTHER THAN THOSE EXCLUDED BY NOTE 1 ( C ) TO THIS\nCHAPTER- WITH WOOL ON: LAMB SKINS, OTHER THAN PELTS",
+        "hsn_code": "41021030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAW SKINS OF SHEEP OR LAMBS (FRESH, OR SALTED, DRIED, LIMED, PICKLED OR OTHERWISE\nPRESERVED, BUT NOT TANNED, PARCHMENT-DRESSED OR FURTHER PREPARED), WHETHER OR\nNOT WITH WOOL ON OR SPLIT, OTHER THAN THOSE EXCLUDED BY NOTE 1 ( C ) TO THIS\nCHAPTER- WITH WOOL ON: PELTS OF BABY LAMBS",
+        "hsn_code": "41021020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAW SKINS OF SHEEP OR LAMBS (FRESH, OR SALTED, DRIED, LIMED, PICKLED OR OTHERWISE\nPRESERVED, BUT NOT TANNED, PARCHMENT-DRESSED OR FURTHER PREPARED), WHETHER OR\nNOT WITH WOOL ON OR SPLIT, OTHER THAN THOSE EXCLUDED BY NOTE 1 ( C ) TO THIS\nCHAPTER- WITH WOOL ON: SHEEP SKINS",
+        "hsn_code": "41021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAW HIDES AND SKINS OF BOVINE (INCLUDING BUFFALO)OR EQUINE ANIMALS (FRESH OR SALTED, DRIED, LIMED, PICKLED OR OTHERWISE PRESERVED, BUT NOT TANNED, PARCHMENT-DRESSED OR FURTHER PREPARED), WHETHER OR NOT DEHAIRED OR SPLIT",
+        "hsn_code": "4101",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAW HIDES AND SKINS OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS (FRESH OR\nSALTED, DRIED, LIMED, PICKLED OR OTHERWISE PRESERVED, BUT NOT TANNED, PARCHMENT\nDRESSED OR FURTHER PREPARED), WHETHER OR NOT DEHAIRED OR SPLIT - OTHER, INCLUDING\nBUTTS, BENDS AND BELLIES: OTHER",
+        "hsn_code": "41019090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAW HIDES AND SKINS OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS (FRESH OR\nSALTED, DRIED, LIMED, PICKLED OR OTHERWISE PRESERVED, BUT NOT TANNED, PARCHMENT\nDRESSED OR FURTHER PREPARED), WHETHER OR NOT DEHAIRED OR SPLIT - OTHER, INCLUDING\nBUTTS, BENDS AND BELLIES: OF BUFFALO, INCLUDING BUFFALO CALF",
+        "hsn_code": "41019020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAW HIDES AND SKINS OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS (FRESH OR\nSALTED, DRIED, LIMED, PICKLED OR OTHERWISE PRESERVED, BUT NOT TANNED, PARCHMENT\nDRESSED OR FURTHER PREPARED), WHETHER OR NOT DEHAIRED OR SPLIT - OTHER, INCLUDING\nBUTTS, BENDS AND BELLIES: OF COW, INCLUDING COW CALF",
+        "hsn_code": "41019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAW HIDES AND SKINS OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS (FRESH OR\nSALTED, DRIED, LIMED, PICKLED OR OTHERWISE PRESERVED, BUT NOT TANNED, PARCHMENT\nDRESSED OR FURTHER PREPARED), WHETHER OR NOT DEHAIRED OR SPLIT - WHOLE HIDES AND\nSKINS, OF A WEIGHT EXCEEDING 16 KG : OTHER",
+        "hsn_code": "41015090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAW HIDES AND SKINS OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS (FRESH OR\nSALTED, DRIED, LIMED, PICKLED OR OTHERWISE PRESERVED, BUT NOT TANNED, PARCHMENT\nDRESSED OR FURTHER PREPARED), WHETHER OR NOT DEHAIRED OR SPLIT - WHOLE HIDES AND\nSKINS, OF A WEIGHT EXCEEDING 16 KG : OF BUFFALO, INCLUDING BUFFALO CALF",
+        "hsn_code": "41015020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAW HIDES AND SKINS OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS (FRESH OR\nSALTED, DRIED, LIMED, PICKLED OR OTHERWISE PRESERVED, BUT NOT TANNED, PARCHMENT\nDRESSED OR FURTHER PREPARED), WHETHER OR NOT DEHAIRED OR SPLIT - WHOLE HIDES AND\nSKINS, OF A WEIGHT EXCEEDING 16 KG : OF COW, INCLUDING COW CALF",
+        "hsn_code": "41015010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAW HIDES AND SKINS OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS (FRESH OR\nSALTED, DRIED, LIMED, PICKLED OR OTHERWISE PRESERVED, BUT NOT TANNED, PARCHMENT\nDRESSED OR FURTHER PREPARED), WHETHER OR NOT DEHAIRED OR SPLIT - WHOLE HIDES AND\nSKINS OF A WEIGHT PER SKIN NOT EXCEEDING 8 KG WHEN SIMPLY DRIED, 10 KG WHEN DRY\nSALTED, OR 16 KG WHEN FRESH, WET-SALTED OR OTHERWISE PRESERVED: OTHER",
+        "hsn_code": "41012090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAW HIDES AND SKINS OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS (FRESH OR\nSALTED, DRIED, LIMED, PICKLED OR OTHERWISE PRESERVED, BUT NOT TANNED, PARCHMENT\nDRESSED OR FURTHER PREPARED), WHETHER OR NOT DEHAIRED OR SPLIT - WHOLE HIDES AND\nSKINS OF A WEIGHT PER SKIN NOT EXCEEDING 8 KG WHEN SIMPLY DRIED, 10 KG WHEN DRY\nSALTED, OR 16 KG WHEN FRESH, WET-SALTED OR OTHERWISE PRESERVED: OF BUFFALO,\nINCLUDING BUFFALO CALF",
+        "hsn_code": "41012020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAW HIDES AND SKINS OF BOVINE (INCLUDING BUFFALO) OR EQUINE ANIMALS (FRESH OR\nSALTED, DRIED, LIMED, PICKLED OR OTHERWISE PRESERVED, BUT NOT TANNED, PARCHMENT\nDRESSED OR FURTHER PREPARED), WHETHER OR NOT DEHAIRED OR SPLIT - WHOLE HIDES AND\nSKINS OF A WEIGHT PER SKIN NOT EXCEEDING 8 KG WHEN SIMPLY DRIED, 10 KG WHEN DRY\nSALTED, OR 16 KG WHEN FRESH, WET-SALTED OR OTHERWISE PRESERVED: OF COW, INCLUDING\nCOW CALF",
+        "hsn_code": "41012010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HARD RUBBER (FOR EXAMPLE, EBONITE) IN ALL FORMS, INCLUDING WASTE AND SCRAP; ARTICLES OF HARD RUBBER",
+        "hsn_code": "4017",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HARD RUBBER (FOR EXAMPLE, EBONITE) IN ALL FORMS, INCLUDING WASTE AND SCRAP;\nARTICLES OF HARD RUBBER - HARD RUBBER (FOR EXAMPLE, EBONITE) IN ALL FORMS,\nINCLUDING WASTE AND SCRAP; ARTICLES OF HARD RUBBER : OTHER",
+        "hsn_code": "40170090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HARD RUBBER (FOR EXAMPLE, EBONITE) IN ALL FORMS, INCLUDING WASTE AND SCRAP;\nARTICLES OF HARD RUBBER - HARD RUBBER (FOR EXAMPLE, EBONITE) IN ALL FORMS,\nINCLUDING WASTE AND SCRAP; ARTICLES OF HARD RUBBER : TYPEWRITERS AND CYCLOSTYLING\nROLLERS",
+        "hsn_code": "40170050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HARD RUBBER (FOR EXAMPLE, EBONITE) IN ALL FORMS, INCLUDING WASTE AND SCRAP;\nARTICLES OF HARD RUBBER - HARD RUBBER (FOR EXAMPLE, EBONITE) IN ALL FORMS,\nINCLUDING WASTE AND SCRAP; ARTICLES OF HARD RUBBER : TEXTILE ROLLERS",
+        "hsn_code": "40170040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HARD RUBBER (FOR EXAMPLE, EBONITE) IN ALL FORMS, INCLUDING WASTE AND SCRAP;\nARTICLES OF HARD RUBBER - HARD RUBBER (FOR EXAMPLE, EBONITE) IN ALL FORMS,\nINCLUDING WASTE AND SCRAP; ARTICLES OF HARD RUBBER : PRINTERS ROLLERS",
+        "hsn_code": "40170030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HARD RUBBER (FOR EXAMPLE, EBONITE) IN ALL FORMS, INCLUDING WASTE AND SCRAP;\nARTICLES OF HARD RUBBER - HARD RUBBER (FOR EXAMPLE, EBONITE) IN ALL FORMS,\nINCLUDING WASTE AND SCRAP; ARTICLES OF HARD RUBBER : SCRAP, WASTE AND POWDER OF\nHARDENED RUBBER (EBONITE AND VULCANITE)",
+        "hsn_code": "40170020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HARD RUBBER (FOR EXAMPLE, EBONITE) IN ALL FORMS, INCLUDING WASTE AND SCRAP;\nARTICLES OF HARD RUBBER - HARD RUBBER (FOR EXAMPLE, EBONITE) IN ALL FORMS,\nINCLUDING WASTE AND SCRAP; ARTICLES OF HARD RUBBER : PLATES, SHEETS, RODS AND TUBES\nOF EBONITE AND VULCANITE",
+        "hsn_code": "40170010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER",
+        "hsn_code": "4016",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : OTHER : OTHER",
+        "hsn_code": "40169990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : OTHER :\nSTOPPERS",
+        "hsn_code": "40169980",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : OTHER : EAR\nPLUG",
+        "hsn_code": "40169970",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : OTHER :\nRUBBER BUSHES",
+        "hsn_code": "40169960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : OTHER :\nRUBBER CUSHIONS",
+        "hsn_code": "40169950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : OTHER :\nRUBBER BLANKETS",
+        "hsn_code": "40169940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : OTHER :\nRUBBER THREADS",
+        "hsn_code": "40169930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : OTHER :\nRUBBER BANDS",
+        "hsn_code": "40169920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : OTHER :\nRUBBER COTS FOR TEXTILE INDUSTRY",
+        "hsn_code": "40169910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : OTHER\nINFLATABLE ARTICLES : OTHER",
+        "hsn_code": "40169590",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : OTHER\nINFLATABLE ARTICLES : AIR MATTRESSES",
+        "hsn_code": "40169510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : BOAT OR DOCK\nFENDERS, WHETHER OR NOT INFLATABLE",
+        "hsn_code": "40169400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : GASKETS,\nWASHERS AND OTHER SEALS : OTHER",
+        "hsn_code": "40169390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : GASKETS,\nWASHERS AND OTHER SEALS : PLUGS",
+        "hsn_code": "40169360",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : GASKETS,\nWASHERS AND OTHER SEALS : WASHERS",
+        "hsn_code": "40169350",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : GASKETS,\nWASHERS AND OTHER SEALS : GASKETS",
+        "hsn_code": "40169340",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : GASKETS,\nWASHERS AND OTHER SEALS : RUBBER SEALS (OIL SEALS AND THE LIKE)",
+        "hsn_code": "40169330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : GASKETS,\nWASHERS AND OTHER SEALS : RUBBER RINGS (O-RING)",
+        "hsn_code": "40169320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : GASKETS,\nWASHERS AND OTHER SEALS : PATCHES FOR PUNCTURE REPAIR OF SELF-VULCANISING RUBBER\nOR A RUBBER BACKING",
+        "hsn_code": "40169310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : ERASERS",
+        "hsn_code": "40169200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : FLOOR\nCOVERINGS AND MATS",
+        "hsn_code": "40169100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF VULCANISED RUBBER OTHER THAN HARD RUBBER OF CELLULAR RUBBER",
+        "hsn_code": "40161000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES (INCLUDING GLOVES, MITTENS AND MITTS) FOR ALL PURPOSES, OR VULCANISED RUBBER OTHER THAN HARD RUBBER",
+        "hsn_code": "4015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES (INCLUDING GLOVES, MITTENS AND MITTS)\nFOR ALL PURPOSES, OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : - OTHER :\nOTHER",
+        "hsn_code": "40159099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES (INCLUDING GLOVES, MITTENS AND MITTS)\nFOR ALL PURPOSES, OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : - OTHER :\nDIVING SUITS",
+        "hsn_code": "40159091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES (INCLUDING GLOVES, MITTENS AND MITTS)\nFOR ALL PURPOSES, OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : INDUSTRIAL\nGLOVES",
+        "hsn_code": "40159030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES (INCLUDING GLOVES, MITTENS AND MITTS)\nFOR ALL PURPOSES, OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : LABELS",
+        "hsn_code": "40159020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES (INCLUDING GLOVES, MITTENS AND MITTS)\nFOR ALL PURPOSES, OF VULCANISED RUBBER OTHER THAN HARD RUBBER - OTHER : RUBBER\nAPRON",
+        "hsn_code": "40159010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES (INCLUDING GLOVES, MITTENS AND MITTS)\nFOR ALL PURPOSES, OF VULCANISED RUBBER OTHER THAN HARD RUBBER - GLOVES, MITTENS\nAND MITTS : OTHER",
+        "hsn_code": "40151900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF APPAREL AND CLOTHING ACCESSORIES (INCLUDING GLOVES, MITTENS AND MITTS)\nFOR ALL PURPOSES, OF VULCANISED RUBBER OTHER THAN HARD RUBBER - GLOVES, MITTENS\nAND MITTS : SURGICAL",
+        "hsn_code": "40151100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Milk and cream, not concentrated nor containing added sugar or other sweetening matter - Of a\nfat content, by weight, exceeding 10%",
+        "hsn_code": "4015000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RETREADED OR USED PNEUMATIC TYRES OF RUBBER, SOLID OR CUSHION TYRES, TYRE TREADS AND TYRE FLAPS, OF RUBBER",
+        "hsn_code": "4012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RETREADED OR USED PNEUMATIC TYRES OF RUBBER, SOLID OR CUSHION TYRES, TYRE TREADS\nAND TYRE FLAPS, OF RUBBER - OTHER : OTHER",
+        "hsn_code": "40129090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "T RETREADED OR USED PNEUMATIC TYRES OF RUBBER, SOLID OR CUSHION TYRES, TYRE TREADS\nAND TYRE FLAPS, OF RUBBER - OTHER : YRE TREADS, INTERCHANGEABLE",
+        "hsn_code": "40129050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RETREADED OR USED PNEUMATIC TYRES OF RUBBER, SOLID OR CUSHION TYRES, TYRE TREADS\nAND TYRE FLAPS, OF RUBBER - OTHER : - TYRE FLAPS : OTHER",
+        "hsn_code": "40129049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RETREADED OR USED PNEUMATIC TYRES OF RUBBER, SOLID OR CUSHION TYRES, TYRE TREADS\nAND TYRE FLAPS, OF RUBBER - OTHER : - TYRE FLAPS : OF A KIND USED IN TWO-WHEELED AND\nTHREE-WHEELED MOTOR VEHICLES",
+        "hsn_code": "40129041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RETREADED OR USED PNEUMATIC TYRES OF RUBBER, SOLID OR CUSHION TYRES, TYRE TREADS\nAND TYRE FLAPS, OF RUBBER - OTHER : TYRES WITH METAL FRAMEWORK",
+        "hsn_code": "40129030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RETREADED OR USED PNEUMATIC TYRES OF RUBBER, SOLID OR CUSHION TYRES, TYRE TREADS\nAND TYRE FLAPS, OF RUBBER - OTHER : SOLID RUBBER TYRES FOR OTHER VEHICLES",
+        "hsn_code": "40129020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RETREADED OR USED PNEUMATIC TYRES OF RUBBER, SOLID OR CUSHION TYRES, TYRE TREADS\nAND TYRE FLAPS, OF RUBBER - OTHER : SOLID RUBBER TYRES FOR MOTOR VEHICLES",
+        "hsn_code": "40129010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RETREADED OR USED PNEUMATIC TYRES OF RUBBER, SOLID OR CUSHION TYRES, TYRE TREADS\nAND TYRE FLAPS, OF RUBBER - USED PNEUMATIC TYRES : OTHER",
+        "hsn_code": "40122090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RETREADED OR USED PNEUMATIC TYRES OF RUBBER, SOLID OR CUSHION TYRES, TYRE TREADS\nAND TYRE FLAPS, OF RUBBER - USED PNEUMATIC TYRES : FOR PASSENGER AUTOMOBILE\nVEHICLES, INCLUDING TWO WHEELERS, THREE WHEELERS AND PERSONAL TYPE VEHICLES",
+        "hsn_code": "40122020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RETREADED OR USED PNEUMATIC TYRES OF RUBBER, SOLID OR CUSHION TYRES, TYRE TREADS\nAND TYRE FLAPS, OF RUBBER - USED PNEUMATIC TYRES : FOR BUSES, LORRIES AND EARTH\nMOVING EQUIPMENTS INCLUDING LIGHT COMMERCIAL VEHICLES",
+        "hsn_code": "40122010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RETREADED OR USED PNEUMATIC TYRES OF RUBBER, SOLID OR CUSHION TYRES, TYRE TREADS\nAND TYRE FLAPS, OF RUBBER - RETREADED TYRES : - OTHER : OTHER",
+        "hsn_code": "40121990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RETREADED OR USED PNEUMATIC TYRES OF RUBBER, SOLID OR CUSHION TYRES, TYRE TREADS\nAND TYRE FLAPS, OF RUBBER - RETREADED TYRES : - OTHER : FOR TWO WHEELERS",
+        "hsn_code": "40121910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RETREADED OR USED PNEUMATIC TYRES OF RUBBER, SOLID OR CUSHION TYRES, TYRE TREADS\nAND TYRE FLAPS, OF RUBBER - RETREADED TYRES : OF A KIND USED ON AIRCRAFT",
+        "hsn_code": "40121300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RETREADED OR USED PNEUMATIC TYRES OF RUBBER, SOLID OR CUSHION TYRES, TYRE TREADS\nAND TYRE FLAPS, OF RUBBER - RETREADED TYRES : OF A KIND USED ON BUSES OR LORRIES",
+        "hsn_code": "40121200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RETREADED OR USED PNEUMATIC TYRES OF RUBBER, SOLID OR CUSHION TYRES, TYRE TREADS\nAND TYRE FLAPS, OF RUBBER - RETREADED TYRES : OF A KIND USED ON MOTOR CARS\n(INCLUDING STATION WAGONS AND RACING CARS)",
+        "hsn_code": "40121100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MILK AND CREAM-FAT CONTENT, BY WEIGHT &gt;1% &lt;= 6%",
+        "hsn_code": "4012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER",
+        "hsn_code": "4010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - TRANSMISSION\nBELTS OR BELTING : OTHER : OTHER : OTHER",
+        "hsn_code": "40103999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - TRANSMISSION\nBELTS OR BELTING : OTHER : OTHER : PLY BELTING",
+        "hsn_code": "40103992",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - TRANSMISSION\nBELTS OR BELTING : OTHER : OTHER : ENDLESS FLAT BELT",
+        "hsn_code": "40103991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - TRANSMISSION\nBELTS OR BELTING : OTHER : WHERE THE RUBBER COMPOUND CONTENT IS LESS THAN 25% BY\nWEIGHT : OTHER",
+        "hsn_code": "40103919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - TRANSMISSION\nBELTS OR BELTING : OTHER : WHERE THE RUBBER COMPOUND CONTENT IS LESS THAN 25% BY\nWEIGHT : PLY BELTING",
+        "hsn_code": "40103912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - TRANSMISSION\nBELTS OR BELTING : OTHER : WHERE THE RUBBER COMPOUND CONTENT IS LESS THAN 25% BY\nWEIGHT : ENDLESS FLAT BELT",
+        "hsn_code": "40103911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - TRANSMISSION\nBELTS OR BELTING : ENDLESS SYNCHRONOUS BELTS OF AN OUTSIDE CIRCUMFERENCE\nEXCEEDING 150 CM BUT NOT EXCEEDING 198 CM : OTHER",
+        "hsn_code": "40103690",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - TRANSMISSION\nBELTS OR BELTING : ENDLESS SYNCHRONOUS BELTS OF AN OUTSIDE CIRCUMFERENCE\nEXCEEDING 150 CM BUT NOT EXCEEDING 198 CM : WHERE THE RUBBER COMPOUND CONTENT\nIS LESS THAN 25% BY WEIGHT",
+        "hsn_code": "40103610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - TRANSMISSION\nBELTS OR BELTING : ENDLESS SYNCHRONOUS BELTS OF AN OUTSIDE CIRCUMFERENCE\nEXCEEDING 60 CM BUT NOT EXCEEDING 150 CM : OTHER",
+        "hsn_code": "40103590",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - TRANSMISSION\nBELTS OR BELTING : ENDLESS SYNCHRONOUS BELTS OF AN OUTSIDE CIRCUMFERENCE\nEXCEEDING 60 CM BUT NOT EXCEEDING 150 CM : WHERE THE RUBBER COMPOUND CONTENT IS\nLESS THAN 25% BY WEIGHT",
+        "hsn_code": "40103510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - TRANSMISSION\nBELTS OR BELTING : ENDLESS TRANSMISSION BELTS OF TRAPEZOIDAL CROSS-SECTION (V-BELTS),\nOTHER THAN V-RIBBED, OF AN OUTSIDE CIRCUMFERENCE EXCEEDING 180 CM BUT NOT\nEXCEEDING 240 CM : OTHER",
+        "hsn_code": "40103490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - TRANSMISSION\nBELTS OR BELTING : ENDLESS TRANSMISSION BELTS OF TRAPEZOIDAL CROSS-SECTION (V-BELTS),\nOTHER THAN V-RIBBED, OF AN OUTSIDE CIRCUMFERENCE EXCEEDING 180 CM BUT NOT\nEXCEEDING 240 CM : WHERE THE RUBBER COMPOUND CONTENT IS LESS THAN 25% BY WEIGHT",
+        "hsn_code": "40103410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - TRANSMISSION\nBELTS OR BELTING : ENDLESS TRANSMISSION BELTS OF TRAPEZOIDAL CROSS-SECTION (V-BELTS),\nV-RIBBED, OF AN OUTSIDE CIRCUMFERENCE EXCEEDING 60 CM BUT NOT EXCEEDING 180 CM :\nOTHER",
+        "hsn_code": "40103390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - TRANSMISSION\nBELTS OR BELTING : ENDLESS TRANSMISSION BELTS OF TRAPEZOIDAL CROSS-SECTION (V-BELTS),\nV-RIBBED, OF AN OUTSIDE CIRCUMFERENCE EXCEEDING 60 CM BUT NOT EXCEEDING 180 CM :\nWHERE THE RUBBER COMPOUND CONTENT IS LESS THAN 25% BY WEIGHT",
+        "hsn_code": "40103310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - TRANSMISSION\nBELTS OR BELTING : ENDLESS TRANSMISSION BELTS OF TRAPEZOIDAL CROSS-SECTION (V-BELTS),\nOTHER THAN V-RIBBED, OF AN OUTSIDE CIRCUMFERENCE EXCEEDING 60 CM BUT NOT\nEXCEEDING 180 CM : OTHER",
+        "hsn_code": "40103290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - TRANSMISSION\nBELTS OR BELTING : ENDLESS TRANSMISSION BELTS OF TRAPEZOIDAL CROSS-SECTION (V-BELTS),\nOTHER THAN V-RIBBED, OF AN OUTSIDE CIRCUMFERENCE EXCEEDING 60 CM BUT NOT\nEXCEEDING 180 CM : WHERE THE RUBBER COMPOUND CONTENT IS LESS THAN 25% BY WEIGHT",
+        "hsn_code": "40103210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - TRANSMISSION\nBELTS OR BELTING : ENDLESS TRANSMISSION BELTS OF TRAPEZOIDAL CROSS-SECTION (V-BELTS),\nV-RIBBED, OF AN OUTSIDE CIRCUMFERENCE EXCEEDING 180 CM BUT NOT EXCEEDING 240 CM :\nOTHER",
+        "hsn_code": "40103190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - TRANSMISSION\nBELTS OR BELTING : ENDLESS TRANSMISSION BELTS OF TRAPEZOIDAL CROSS-SECTION (V-BELTS),\nV-RIBBED, OF AN OUTSIDE CIRCUMFERENCE EXCEEDING 180 CM BUT NOT EXCEEDING 240 CM :\nWHERE THE RUBBER COMPOUND CONTENT IS LESS THAN 25% BY WEIGHT",
+        "hsn_code": "40103110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - CONVEYOR BELTS\nOR BELTING : - OTHER : OTHER",
+        "hsn_code": "40101990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - CONVEYOR BELTS\nOR BELTING : - OTHER : WHERE THE RUBBER COMPOUND CONTENT IS LESS THAN 25% BY\nWEIGHT",
+        "hsn_code": "40101910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - CONVEYOR BELTS\nOR BELTING : - REINFORCED ONLY WITH PLASTICS : OTHER",
+        "hsn_code": "40101390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - CONVEYOR BELTS\nOR BELTING : - REINFORCED ONLY WITH PLASTICS : WHERE THE RUBBER COMPOUND CONTENT\nIS LESS THAN 25% BY WEIGHT",
+        "hsn_code": "40101310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - CONVEYOR BELTS\nOR BELTING : - REINFORCED ONLY WITH TEXTILE MATERIALS : OTHER",
+        "hsn_code": "40101290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - CONVEYOR BELTS\nOR BELTING : - REINFORCED ONLY WITH TEXTILE MATERIALS : WHERE THE RUBBER COMPOUND\nCONTENT IS LESS THAN 25% BY WEIGHT",
+        "hsn_code": "40101210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - CONVEYOR BELTS\nOR BELTING : - REINFORCED ONLY WITH METAL : OTHER",
+        "hsn_code": "40101190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CONVEYOR OR TRANSMISSION BELTS OR BELTING OF VULCANISED RUBBER - CONVEYOR BELTS\nOR BELTING : - REINFORCED ONLY WITH METAL : WHERE THE RUBBER COMPOUND CONTENT IS\nLESS THAN 25% BY WEIGHT",
+        "hsn_code": "40101110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, OF VULCANISED RUBBER OTHER THAN HARD RUBBER, WITH OR WITHOUT THEIR FITTINGS (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES)",
+        "hsn_code": "4009",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, OF VULCANISED RUBBER OTHER THAN HARD RUBBER, WITH OR\nWITHOUT THEIR FITTINGS (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES) - REINFORCED OR\nOTHERWISE COMBINED WITH OTHER MATERIALS : WITH FITTINGS",
+        "hsn_code": "40094200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, OF VULCANISED RUBBER OTHER THAN HARD RUBBER, WITH OR\nWITHOUT THEIR FITTINGS (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES) - REINFORCED OR\nOTHERWISE COMBINED WITH OTHER MATERIALS : WITHOUT FITTINGS",
+        "hsn_code": "40094100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, OF VULCANISED RUBBER OTHER THAN HARD RUBBER, WITH OR\nWITHOUT THEIR FITTINGS (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES) - REINFORCED OR\nOTHERWISE COMBINED ONLY WITH TEXTILE MATERIALS : WITH FITTINGS",
+        "hsn_code": "40093200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, OF VULCANISED RUBBER OTHER THAN HARD RUBBER, WITH OR\nWITHOUT THEIR FITTINGS (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES) - REINFORCED OR\nOTHERWISE COMBINED ONLY WITH TEXTILE MATERIALS : WITHOUT FITTINGS",
+        "hsn_code": "40093100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, OF VULCANISED RUBBER OTHER THAN HARD RUBBER, WITH OR\nWITHOUT THEIR FITTINGS (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES) - REINFORCED OR\nOTHERWISE COMBINED ONLY WITH METAL : WITH FITTINGS",
+        "hsn_code": "40092200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, OF VULCANISED RUBBER OTHER THAN HARD RUBBER, WITH OR\nWITHOUT THEIR FITTINGS (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES) - REINFORCED OR\nOTHERWISE COMBINED ONLY WITH METAL : WITHOUT FITTINGS",
+        "hsn_code": "40092100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, OF VULCANISED RUBBER OTHER THAN HARD RUBBER, WITH OR\nWITHOUT THEIR FITTINGS (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES) - NOT REINFORCED OR\nOTHERWISE COMBINED WITH OTHER MATERIALS : WITH FITTINGS",
+        "hsn_code": "40091200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, OF VULCANISED RUBBER OTHER THAN HARD RUBBER, WITH OR\nWITHOUT THEIR FITTINGS (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES) - NOT REINFORCED OR\nOTHERWISE COMBINED WITH OTHER MATERIALS : WITHOUT FITTINGS",
+        "hsn_code": "40091100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLATES, SHEETS, STRIP, RODS AND PROFILE SHAPES, OF VULCANISED RUBBER OTHER THAN HARD RUBBER",
+        "hsn_code": "4008",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLATES, SHEETS, STRIP, RODS AND PROFILE SHAPES, OF VULCANISED RUBBER OTHER THAN\nHARD RUBBER - OF NON-CELLULAR RUBBER : OTHER : OTHER",
+        "hsn_code": "40082990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLATES, SHEETS, STRIP, RODS AND PROFILE SHAPES, OF VULCANISED RUBBER OTHER THAN\nHARD RUBBER - OF NON-CELLULAR RUBBER : OTHER : TREAD RUBBER AND TREAD PACKING\nSTRIP FOR RESOLING OR REPAIRING OR RETREADING RUBBER TYRES",
+        "hsn_code": "40082940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLATES, SHEETS, STRIP, RODS AND PROFILE SHAPES, OF VULCANISED RUBBER OTHER THAN\nHARD RUBBER - OF NON-CELLULAR RUBBER : OTHER : LATEX FOAM SPONGE",
+        "hsn_code": "40082930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLATES, SHEETS, STRIP, RODS AND PROFILE SHAPES, OF VULCANISED RUBBER OTHER THAN\nHARD RUBBER - OF NON-CELLULAR RUBBER : OTHER : BLOCKS USED IN THE MANUFACTURE OF\nSOLES, HEELS OR SOLES AND HEELS COMBINED, FOR FOOTWEAR",
+        "hsn_code": "40082920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLATES, SHEETS, STRIP, RODS AND PROFILE SHAPES, OF VULCANISED RUBBER OTHER THAN\nHARD RUBBER - OF NON-CELLULAR RUBBER : OTHER : RUBBER SHEETS AND RESIN RUBBER\nSHEETS FOR SOLES AND HEELS",
+        "hsn_code": "40082910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLATES, SHEETS, STRIP, RODS AND PROFILE SHAPES, OF VULCANISED RUBBER OTHER THAN\nHARD RUBBER - OF NON-CELLULAR RUBBER : PLATES, SHEETS AND STRIP : OTHER",
+        "hsn_code": "40082190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLATES, SHEETS, STRIP, RODS AND PROFILE SHAPES, OF VULCANISED RUBBER OTHER THAN\nHARD RUBBER - OF NON-CELLULAR RUBBER : PLATES, SHEETS AND STRIP : FOR RESOLING OR\nREPAIRING OR RETREADING RUBBER TYRES",
+        "hsn_code": "40082120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLATES, SHEETS, STRIP, RODS AND PROFILE SHAPES, OF VULCANISED RUBBER OTHER THAN\nHARD RUBBER - OF NON-CELLULAR RUBBER : PLATES, SHEETS AND STRIP : USED IN THE\nMANUFACTURE OF SOLES, HEELS OR SOLES AND HEELS COMBINED, FOR FOOTWEAR",
+        "hsn_code": "40082110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLATES, SHEETS, STRIP, RODS AND PROFILE SHAPES, OF VULCANISED RUBBER OTHER THAN\nHARD RUBBER - OF CELLULAR RUBBER : - OTHER : OTHER",
+        "hsn_code": "40081990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLATES, SHEETS, STRIP, RODS AND PROFILE SHAPES, OF VULCANISED RUBBER OTHER THAN\nHARD RUBBER - OF CELLULAR RUBBER : - OTHER : BLOCKS OF MICRO-CELLULAR RUBBER BUT\nNOT OF LATEX FOAM SPONGE, USED IN THE MANUFACTURE OF SOLES, HEELS OR SOLES AND\nHEELS COMBINED, FOR FOOTWEAR",
+        "hsn_code": "40081910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLATES, SHEETS, STRIP, RODS AND PROFILE SHAPES, OF VULCANISED RUBBER OTHER THAN\nHARD RUBBER - OF CELLULAR RUBBER : - PLATES, SHEETS AND STRIP : OTHER",
+        "hsn_code": "40081190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PLATES, SHEETS, STRIP, RODS AND PROFILE SHAPES, OF VULCANISED RUBBER OTHER THAN\nHARD RUBBER - OF CELLULAR RUBBER : - PLATES, SHEETS AND STRIP : OF MICRO-CELLULAR\nRUBBER",
+        "hsn_code": "40081110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VULCANISED RUBBER THREAD AND CORD",
+        "hsn_code": "4007",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VULCANISED RUBBER THREAD AND CORD - VULCANISED RUBBER THREAD AND CORD : OTHER",
+        "hsn_code": "40070090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VULCANISED RUBBER THREAD AND CORD - VULCANISED RUBBER THREAD AND CORD : CORD,\nNOT COVERED",
+        "hsn_code": "40070020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VULCANISED RUBBER THREAD AND CORD - VULCANISED RUBBER THREAD AND CORD : THREAD,\nNOT COVERED",
+        "hsn_code": "40070010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FORMS (FOR EXAMPLE, RODS, TUBES AND PROFILE SHAPES) AND ARTICLES (FOR EXAMPLE, DISCS AND RINGS), OF UNVULCANISED RUBBER",
+        "hsn_code": "4006",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FORMS (FOR EXAMPLE, RODS, TUBES AND PROFILE SHAPES) AND ARTICLES (FOR\nEXAMPLE, DISCS AND RINGS), OF UNVULCANISED RUBBER - OTHER : OTHER",
+        "hsn_code": "40069090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FORMS (FOR EXAMPLE, RODS, TUBES AND PROFILE SHAPES) AND ARTICLES (FOR\nEXAMPLE, DISCS AND RINGS), OF UNVULCANISED RUBBER - OTHER : THREAD, NOT COVERED",
+        "hsn_code": "40069010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER FORMS (FOR EXAMPLE, RODS, TUBES AND PROFILE SHAPES) AND ARTICLES (FOR\nEXAMPLE, DISCS AND RINGS), OF UNVULCANISED RUBBER STRIPS FOR RETREADING RUBBER\nTYRES",
+        "hsn_code": "40061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDED RUBBER, UNVULCANISED, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP",
+        "hsn_code": "4005",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDED RUBBER, UNVULCANISED, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP -\nOTHER : - OTHER : OTHER",
+        "hsn_code": "40059990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDED RUBBER, UNVULCANISED, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP -\nOTHER : - OTHER : GRANULES OF UNVULCANISED NATURAL OR SYNTHETIC RUBBER,\nCOMPOUNDED, READY FOR VULCANIZATION",
+        "hsn_code": "40059910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDED RUBBER, UNVULCANISED, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP -\nOTHER : PLATES, SHEETS AND STRIP : OTHER",
+        "hsn_code": "40059190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDED RUBBER, UNVULCANISED, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP -\nOTHER : PLATES, SHEETS AND STRIP : HOSPITAL SHEETING",
+        "hsn_code": "40059110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDED RUBBER, UNVULCANISED, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP -\nSOLUTIONS; DISPERSIONS OTHER THAN THOSE OF SUB-HEADING 4005 10 : OTHER",
+        "hsn_code": "40052090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDED RUBBER, UNVULCANISED, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP -\nSOLUTIONS; DISPERSIONS OTHER THAN THOSE OF SUB-HEADING 4005 10 : CAN SEALING\nCOMPOUND",
+        "hsn_code": "40052010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDED RUBBER, UNVULCANISED, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP\nCOMPOUNDED WITH CARBON BLACK OR SILICA",
+        "hsn_code": "40051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Waste, parings and scrap of rubber (other than hard rubber) and powders and granules obtained therefrom",
+        "hsn_code": "4004",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Reclaimed rubber in primary forms or in plates, sheets or strip",
+        "hsn_code": "4003",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RECLAIMED RUBBER IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP",
+        "hsn_code": "40030000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FORM OILS, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS HEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP",
+        "hsn_code": "4002",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP- OTHER : OTHEROTHER",
+        "hsn_code": "40029990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP- OTHER : OTHERTREAD RUBBER\nCOMPOUND, CUSHION COMPOUND, CUSHION GUM AND TREAD GUM FOR RESOLING OR\nREPAIRING OR RETREADING RUBBER TYRES",
+        "hsn_code": "40029920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP- OTHER : OTHER FACTICE\n(RUBBER SUBSTITUTE DERIVED FROM OIL)",
+        "hsn_code": "40029910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP- OTHER : LATEX",
+        "hsn_code": "40029100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP- MIXTURES OF ANY PRODUCT OF\nHEADING 40 01 WITH ANY PRODUCT OF THIS HEADING : OTHER",
+        "hsn_code": "40028090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP- MIXTURES OF ANY PRODUCT OF\nHEADING 40 01 WITH ANY PRODUCT OF THIS HEADING : CHEMICALLY MODIFIED FORM OF\nNATURAL RUBBER INCLUDING GRAFT RUBBER",
+        "hsn_code": "40028020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP- MIXTURES OF ANY PRODUCT OF\nHEADING 40 01 WITH ANY PRODUCT OF THIS HEADING : LATEX",
+        "hsn_code": "40028010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIPETHYLENE-PROPYLENE-NON\nCONJUGATED DIENE RUBBER (EPDM)",
+        "hsn_code": "40027000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIPISOPRENE RUBBER (IR)",
+        "hsn_code": "40026000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP- ACRYLONITRILE-BUTADIENE\nRUBBER (NBR) : OTHER",
+        "hsn_code": "40025900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP- ACRYLONITRILE-BUTADIENE\nRUBBER (NBR) : LATEX",
+        "hsn_code": "40025100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP- CHLORPRENE\n(CHLOROBUTADIENE) RUBBER (CR) : OTHER",
+        "hsn_code": "40024900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP- CHLORPRENE\n(CHLOROBUTADIENE) RUBBER (CR) : LATEX",
+        "hsn_code": "40024100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP- ISOBUTENE-ISOPRENE (BUTYL)\nRUBBER (IIR); HALO-ISOBUTENE-ISOPRENE RUBBER (CIIR OR BIIR) : OTHER",
+        "hsn_code": "40023900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP- ISOBUTENE-ISOPRENE (BUTYL)\nRUBBER (IIR); HALO-ISOBUTENE-ISOPRENE RUBBER (CIIR OR BIIR) : ISOBUTENE-ISOPRENE\n(BUTYL) RUBBER (IIR)",
+        "hsn_code": "40023100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIPBUTADIENE RUBBER (BR)",
+        "hsn_code": "40022000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP - STYRENE-BUTADIENE RUBBER\n(SBR); CARBOXYLATED STYRENE-BUTADIENE RUBBER (XSBR) : - OTHER : OTHER",
+        "hsn_code": "40021990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP - STYRENE-BUTADIENE RUBBER\n(SBR); CARBOXYLATED STYRENE-BUTADIENE RUBBER (XSBR) : - OTHER : STYRENE BUTADIENE\nSTYRENE OIL BOUND COPOLYMER",
+        "hsn_code": "40021930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP - STYRENE-BUTADIENE RUBBER\n(SBR); CARBOXYLATED STYRENE-BUTADIENE RUBBER (XSBR) : - OTHER : STYRENE BUTADIENE\nRUBBER WITH STYRENE CONTENT EXCEEDING 50%",
+        "hsn_code": "40021920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP - STYRENE-BUTADIENE RUBBER\n(SBR); CARBOXYLATED STYRENE-BUTADIENE RUBBER (XSBR) : - OTHER : OIL EXTENDED STYRENE\nBUTADIENE RUBBER",
+        "hsn_code": "40021910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC RUBBER AND FACTICE DERIVED FROM OILS, IN PRIMARY FORMS OR IN PLATES,\nSHEETS OR STRIP; MIXTURES OF ANY PRODUCT OF HEADING 4001 WITH ANY PRODUCT OF THIS\nHEADING, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP - STYRENE-BUTADIENE RUBBER\n(SBR); CARBOXYLATED STYRENE-BUTADIENE RUBBER (XSBR) : LATEX",
+        "hsn_code": "40021100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NATURAL RUBBER, BALATA, GUTTA-PERCHA, GUAYULE, CHICLE AND SIMILAR NATURAL GUMS, IN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP",
+        "hsn_code": "4001",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL RUBBER, BALATA, GUTTA-PERCHA, GUAYULE, CHICLE AND SIMILAR NATURAL GUMS,\nIN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP BALATA, GUTTA-PERCHA, GUAYULE, CHICLE\nAND SIMILAR NATURAL GUMS",
+        "hsn_code": "40013000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL RUBBER, BALATA, GUTTA-PERCHA, GUAYULE, CHICLE AND SIMILAR NATURAL GUMS,\nIN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP - NATURAL RUBBER IN OTHER FORMS :\nOTHER : OTHER",
+        "hsn_code": "40012990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL RUBBER, BALATA, GUTTA-PERCHA, GUAYULE, CHICLE AND SIMILAR NATURAL GUMS,\nIN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP - NATURAL RUBBER IN OTHER FORMS :\nOTHER : OIL EXTENDED NATURAL RUBBER",
+        "hsn_code": "40012940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL RUBBER, BALATA, GUTTA-PERCHA, GUAYULE, CHICLE AND SIMILAR NATURAL GUMS,\nIN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP - NATURAL RUBBER IN OTHER FORMS :\nOTHER : ESTATE BROWN CREPE",
+        "hsn_code": "40012930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL RUBBER, BALATA, GUTTA-PERCHA, GUAYULE, CHICLE AND SIMILAR NATURAL GUMS,\nIN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP - NATURAL RUBBER IN OTHER FORMS :\nOTHER : PALE CREPE",
+        "hsn_code": "40012920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL RUBBER, BALATA, GUTTA-PERCHA, GUAYULE, CHICLE AND SIMILAR NATURAL GUMS,\nIN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP - NATURAL RUBBER IN OTHER FORMS :\nOTHER : HEVEA",
+        "hsn_code": "40012910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL RUBBER, BALATA, GUTTA-PERCHA, GUAYULE, CHICLE AND SIMILAR NATURAL GUMS,\nIN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP - NATURAL RUBBER IN OTHER FORMS :\nTECHNICALLY SPECIFIED NATURAL RUBBER (TSNR)",
+        "hsn_code": "40012200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL RUBBER, BALATA, GUTTA-PERCHA, GUAYULE, CHICLE AND SIMILAR NATURAL GUMS,\nIN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP - NATURAL RUBBER IN OTHER FORMS :\nSMOKED SHEETS",
+        "hsn_code": "40012100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL RUBBER, BALATA, GUTTA-PERCHA, GUAYULE, CHICLE AND SIMILAR NATURAL GUMS,\nIN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP - NATURAL RUBBER LATEX, WHETHER OR\nNOT PREVULCANISED : OTHER THAN PREVULCANISED",
+        "hsn_code": "40011020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL RUBBER, BALATA, GUTTA-PERCHA, GUAYULE, CHICLE AND SIMILAR NATURAL GUMS,\nIN PRIMARY FORMS OR IN PLATES, SHEETS OR STRIP - NATURAL RUBBER LATEX, WHETHER OR\nNOT PREVULCANISED : PREVULCANISED",
+        "hsn_code": "40011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BUILDERS WARE OF PLASTICS, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "3925",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUILDERS WARE OF PLASTICS, NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER : OTHER",
+        "hsn_code": "39259090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUILDERS WARE OF PLASTICS, NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER : OF\nPOLYURETHANE",
+        "hsn_code": "39259010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUILDERS WARE OF PLASTICS, NOT ELSEWHERE SPECIFIED OR INCLUDED - SHUTTERS, BLINDS\n(INCLUDING VENETIAN BLINDS) AND SIMILAR ARTICLES AND PARTS THEREOF",
+        "hsn_code": "39253000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUILDERS WARE OF PLASTICS, NOT ELSEWHERE SPECIFIED OR INCLUDED - DOORS, WINDOWS\nAND THEIR FRAMES AND THRESHOLDS FOR DOORS",
+        "hsn_code": "39252000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BUILDERS WARE OF PLASTICS, NOT ELSEWHERE SPECIFIED OR INCLUDED - RESERVOIRS, TANKS,\nVATS AND SIMILAR CONTAINERS, OF A CAPACITY EXCEEDING 300 L",
+        "hsn_code": "39251000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND HYGIENIC OR  TOILET ARTICLES, OF PLASTICS",
+        "hsn_code": "3924",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND HYGIENIC OR TOILET ARTICLES,\nOF PLASTICS - OTHER : OTHER",
+        "hsn_code": "39249090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND HYGIENIC OR TOILET ARTICLES,\nOF PLASTICS - OTHER : INSULATED WARE",
+        "hsn_code": "39249020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND HYGIENIC OR TOILET ARTICLES,\nOF PLASTICS - OTHER : TOILET ARTICLES",
+        "hsn_code": "39249010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND HYGIENIC OR TOILET ARTICLES,\nOF PLASTICS - TABLEWARE AND KITCHENWARE : OTHER",
+        "hsn_code": "39241090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TABLEWARE, KITCHENWARE, OTHER HOUSEHOLD ARTICLES AND HYGIENIC OR TOILET ARTICLES,\nOF PLASTICS - TABLEWARE AND KITCHENWARE : INSULATED WARE",
+        "hsn_code": "39241010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BATHS, SHOWER-BATHS, SINKS, WASH-BASINS, BIDETS, LAVATORY PANS, SEATS AND COVERS, FLUSHING CISTERNS AND SIMILAR SANITARY WARE, OF PLASTICS",
+        "hsn_code": "3922",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BATHS, SHOWER - BATHS, SINKS, WASH - BASINS, BIDETS, LAVATORY PANS, SEATS AND COVERS,\nFLUSHING CISTERNS AND SIMILAR SANITARY WARE, OF PLASTICS - OTHER",
+        "hsn_code": "39229000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BATHS, SHOWER - BATHS, SINKS, WASH - BASINS, BIDETS, LAVATORY PANS, SEATS AND COVERS,\nFLUSHING CISTERNS AND SIMILAR SANITARY WARE, OF PLASTICS - LAVATORY SEATS AND\nCOVERS",
+        "hsn_code": "39222000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BATHS, SHOWER - BATHS, SINKS, WASH - BASINS, BIDETS, LAVATORY PANS, SEATS AND COVERS,\nFLUSHING CISTERNS AND SIMILAR SANITARY WARE, OF PLASTICS - BATHS, SHOWER - BATHS,\nSINKS AND WASH BASINS",
+        "hsn_code": "39221000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS FILM , FOIL AND STRIP, OF PLASTICS",
+        "hsn_code": "3921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OTHER : OTHER",
+        "hsn_code": "39219099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OTHER : FLEXIBLE,\nLAMINATED",
+        "hsn_code": "39219096",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OTHER : RIGID,\nLAMINATED",
+        "hsn_code": "39219095",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OTHER : FLEXIBLE,\nMETALLISED",
+        "hsn_code": "39219094",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OTHER : RIGID,\nMETALLISED",
+        "hsn_code": "39219093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OTHER : FLEXIBLE,\nLACQUERED",
+        "hsn_code": "39219092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OTHER : RIGID,\nLACQUERED",
+        "hsn_code": "39219091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OF REGENERATED\nCELLULOSE : OTHER",
+        "hsn_code": "39219039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OF REGENERATED\nCELLULOSE : FLEXIBLE, LAMINATED",
+        "hsn_code": "39219036",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OF REGENERATED\nCELLULOSE : RIGID, LAMINATED",
+        "hsn_code": "39219035",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OF REGENERATED\nCELLULOSE : FLEXIBLE, METALLISED",
+        "hsn_code": "39219034",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OF REGENERATED\nCELLULOSE : RIGID, METALLISED",
+        "hsn_code": "39219033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OF REGENERATED\nCELLULOSE : FLEXIBLE, LACQUERED",
+        "hsn_code": "39219032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OF REGENERATED\nCELLULOSE : RIGID, LACQUERED",
+        "hsn_code": "39219031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OF POLYMERS OF VINYL\nCHLORIDE : OTHER",
+        "hsn_code": "39219029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OF POLYMERS OF VINYL\nCHLORIDE : FLEXIBLE, LAMINATED",
+        "hsn_code": "39219026",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OF POLYMERS OF VINYL\nCHLORIDE : RIGID, LAMINATED",
+        "hsn_code": "39219025",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OF POLYMERS OF VINYL\nCHLORIDE : FLEXIBLE, METALLISED",
+        "hsn_code": "39219024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OF POLYMERS OF VINYL\nCHLORIDE : RIGID, METALLISED",
+        "hsn_code": "39219023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OF POLYMERS OF VINYL\nCHLORIDE : FLEXIBLE, LACQUERED",
+        "hsn_code": "39219022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : OF POLYMERS OF VINYL\nCHLORIDE : RIGID, LACQUERED",
+        "hsn_code": "39219021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - OTHER : THERMOCOL",
+        "hsn_code": "39219010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - CELLULAR : OF OTHER PLASTICS",
+        "hsn_code": "39211900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - CELLULAR : OF REGENERATED\nCELLULOSE",
+        "hsn_code": "39211400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - CELLULAR : OF\nPOLYURETHANES:OTHER",
+        "hsn_code": "39211390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - CELLULAR : OF\nPOLYURETHANES:FLEXIBLE",
+        "hsn_code": "39211310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - CELLULAR : OF POLYMERS OF\nVINYL CHLORIDE",
+        "hsn_code": "39211200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS - CELLULAR : OF POLYMERS OF\nSTYRENE",
+        "hsn_code": "39211100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON-CELLULAR AND NOT REINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS",
+        "hsn_code": "3920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: OTHER : OTHER",
+        "hsn_code": "39209999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: OTHER : FLEXIBLE, PLAIN",
+        "hsn_code": "39209992",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: OTHER : RIGID, PLAIN",
+        "hsn_code": "39209991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: CLICKING BOARDS FOR LEATHER MACHINERY",
+        "hsn_code": "39209960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: RETRO REFLECTIVE SHEETING: OTHER",
+        "hsn_code": "39209959",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: RETRO REFLECTIVE SHEETING: FLEXIBLE, PLAIN",
+        "hsn_code": "39209952",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: RETRO REFLECTIVE SHEETING: RIGID, PLAIN",
+        "hsn_code": "39209951",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: SHEET OF POLY (TETRAFLUORO - ETHYLENE) (PTFE): OTHER",
+        "hsn_code": "39209949",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: SHEET OF POLY (TETRAFLUORO - ETHYLENE) (PTFE): FLEXIBLE, PLAIN",
+        "hsn_code": "39209942",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: SHEET OF POLY (TETRAFLUORO - ETHYLENE) (PTFE): RIGID, PLAIN",
+        "hsn_code": "39209941",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: PLATES, SHEETS, STRIP, FILM OR FOIL OF COPOLYMERS OF VINYL CHLORIDE\nAND VINYL ACETATE : OTHER",
+        "hsn_code": "39209939",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: PLATES, SHEETS, STRIP, FILM OR FOIL OF COPOLYMERS OF VINYL CHLORIDE\nAND VINYL ACETATE : FLEXIBLE, PLAIN",
+        "hsn_code": "39209932",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: PLATES, SHEETS, STRIP, FILM OR FOIL OF COPOLYMERS OF VINYL CHLORIDE\nAND VINYL ACETATE : RIGID, PLAIN",
+        "hsn_code": "39209931",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: FILM, SHEETS, STRIP OF VINYL PLASTICS : OTHER",
+        "hsn_code": "39209929",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: FILM, SHEETS, STRIP OF VINYL PLASTICS : FLEXIBLE, PLAIN",
+        "hsn_code": "39209922",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: FILM, SHEETS, STRIP OF VINYL PLASTICS : RIGID, PLAIN",
+        "hsn_code": "39209921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: PLATES, SHEETS, FILM, FOIL AND STRIP OF POLY(VINYL ACETATE) : OTHER",
+        "hsn_code": "39209919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: PLATES, SHEETS, FILM, FOIL AND STRIP OF POLY(VINYL ACETATE) : FLEXIBLE,\nPLAIN",
+        "hsn_code": "39209912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: PLATES, SHEETS, FILM, FOIL AND STRIP OF POLY(VINYL ACETATE) : RIGID, PLAIN",
+        "hsn_code": "39209911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPHENOLIC RESINS : OTHER",
+        "hsn_code": "39209490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPHENOLIC RESINS : FLEXIBLE, PLAIN",
+        "hsn_code": "39209420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPHENOLIC RESINS : RIGID, PLAIN",
+        "hsn_code": "39209410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nAMINO - RESINS : OTHER",
+        "hsn_code": "39209390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nAMINO - RESINS : FLEXIBLE, PLAIN",
+        "hsn_code": "39209320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nAMINO - RESINS : RIGID, PLAIN",
+        "hsn_code": "39209310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYAMIDES: POLY (AMIDE FLUORIDE) FILM: OTHER : OTHER",
+        "hsn_code": "39209299",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYAMIDES: POLY (AMIDE FLUORIDE) FILM: OTHER : FLEXIBLE, PLAIN",
+        "hsn_code": "39209292",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYAMIDES: POLY (AMIDE FLUORIDE) FILM: OTHER : RIGID, PLAIN",
+        "hsn_code": "39209291",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYAMIDES: POLY (AMIDE FLUORIDE) FILM: OTHER",
+        "hsn_code": "39209219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYAMIDES: POLY (AMIDE FLUORIDE) FILM: FLEXIBLE, PLAIN",
+        "hsn_code": "39209212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYAMIDES: POLY (AMIDE FLUORIDE) FILM: RIGID, PLAIN",
+        "hsn_code": "39209211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: OF POLY (VINYL BUTYRAL): OTHER",
+        "hsn_code": "39209119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: OF POLY (VINYL BUTYRAL): FLEXIBLE, PLAIN",
+        "hsn_code": "39209112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nOTHER PLASTICS: OF POLY (VINYL BUTYRAL): RIGID, PLAIN",
+        "hsn_code": "39209111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF OTHER CELLULOSE DERIVATIVES : OTHER :OTHER",
+        "hsn_code": "39207999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF OTHER CELLULOSE DERIVATIVES : OTHER\n:FLEXIBLE, PLAIN",
+        "hsn_code": "39207992",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF OTHER CELLULOSE DERIVATIVES : OTHER :RIGID,\nPLAIN",
+        "hsn_code": "39207991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF OTHER CELLULOSE DERIVATIVES : SHEETS OF\nCELLULOSE NITRATE AND CELLULOID, WHETHER OR NOT PLASTICIZED :OTHER",
+        "hsn_code": "39207919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF OTHER CELLULOSE DERIVATIVES : SHEETS OF\nCELLULOSE NITRATE AND CELLULOID, WHETHER OR NOT PLASTICIZED :FLEXIBLE, PLAIN",
+        "hsn_code": "39207912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF OTHER CELLULOSE DERIVATIVES : SHEETS OF\nCELLULOSE NITRATE AND CELLULOID, WHETHER OR NOT PLASTICIZED :RIGID, PLAIN",
+        "hsn_code": "39207911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF CELLULOSE ACETATE: OTHER :OTHER",
+        "hsn_code": "39207399",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF CELLULOSE ACETATE: OTHER :FLEXIBLE, PLAIN",
+        "hsn_code": "39207392",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF CELLULOSE ACETATE: OTHER :RIGID, PLAIN",
+        "hsn_code": "39207391",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF CELLULOSE ACETATE: SHEETS OF CELLULOSE\nACETATE, PLASTICIZED :OTHER",
+        "hsn_code": "39207329",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF CELLULOSE ACETATE: SHEETS OF CELLULOSE\nACETATE, PLASTICIZED :FLEXIBLE, PLAIN",
+        "hsn_code": "39207322",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF CELLULOSE ACETATE: SHEETS OF CELLULOSE\nACETATE, PLASTICIZED :RIGID, PLAIN",
+        "hsn_code": "39207321",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF CELLULOSE ACETATE: SHEET OF CELLULOSE\nACETATE, NON - PLASTICIZED:OTHER",
+        "hsn_code": "39207319",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF CELLULOSE ACETATE: SHEET OF CELLULOSE\nACETATE, NON - PLASTICIZED: FLEXIBLE, PLAIN",
+        "hsn_code": "39207312",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF CELLULOSE ACETATE: SHEET OF CELLULOSE\nACETATE, NON - PLASTICIZED:RIGID, PLAIN",
+        "hsn_code": "39207311",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF VULCANISED FIBRE : OTHER",
+        "hsn_code": "39207290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF VULCANISED FIBRE :FLEXIBLE, PLAIN",
+        "hsn_code": "39207220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF VULCANISED FIBRE :RIGID, PLAIN",
+        "hsn_code": "39207210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF REGENERATED CELLULOSE: OTHER :OTHER",
+        "hsn_code": "39207199",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF REGENERATED CELLULOSE: OTHER :FLEXIBLE,\nPLAIN",
+        "hsn_code": "39207192",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF REGENERATED CELLULOSE: OTHER : RIGID, PLAIN",
+        "hsn_code": "39207191",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF REGENERATED CELLULOSE: SHEETS OF\nCELLULOSE NITRATE AND CELLULOID, WHETHER OR NOT PLASTICIZED :OTHER",
+        "hsn_code": "39207129",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF REGENERATED CELLULOSE: SHEETS OF\nCELLULOSE NITRATE AND CELLULOID, WHETHER OR NOT PLASTICIZED :PLAIN",
+        "hsn_code": "39207121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF REGENERATED CELLULOSE: CELLO PHANE\nTRANSPARENT:OTHER",
+        "hsn_code": "39207119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nCELLULOSE OR ITS CHEMICAL DERIVATIVES: OF REGENERATED CELLULOSE: CELLO PHANE\nTRANSPARENT:FILM",
+        "hsn_code": "39207111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF OTHER\nPOLYESTERS : OTHER : OTHER",
+        "hsn_code": "39206999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF OTHER\nPOLYESTERS : OTHER : FLEXIBLE, PLAIN",
+        "hsn_code": "39206992",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF OTHER\nPOLYESTERS : OTHER : RIGID, PLAIN",
+        "hsn_code": "39206991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF OTHER\nPOLYESTERS : OTHER FILM : OTHER",
+        "hsn_code": "39206939",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF OTHER\nPOLYESTERS : OTHER FILM : FLEXIBLE, PLAIN",
+        "hsn_code": "39206932",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF OTHER\nPOLYESTERS : OTHER FILM :RIGID, PLAIN",
+        "hsn_code": "39206931",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF OTHER\nPOLYESTERS : SUN AND/OR DUST CONTROL FILM : OTHER",
+        "hsn_code": "39206929",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF OTHER\nPOLYESTERS : SUN AND/OR DUST CONTROL FILM : FLEXIBLE, PLAIN",
+        "hsn_code": "39206922",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF OTHER\nPOLYESTERS : SUN AND/OR DUST CONTROL FILM : RIGID, PLAIN",
+        "hsn_code": "39206921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF OTHER\nPOLYESTERS : PACKAGING FILM:OTHER",
+        "hsn_code": "39206919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF OTHER\nPOLYESTERS : PACKAGING FILM:FLEXIBLE, PLAIN",
+        "hsn_code": "39206912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF OTHER\nPOLYESTERS : PACKAGING FILM:RIGID, PLAIN",
+        "hsn_code": "39206911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF\nUNSATURATED POLYESTERS :OTHER",
+        "hsn_code": "39206390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF\nUNSATURATED POLYESTERS :FLEXIBLE, PLAIN",
+        "hsn_code": "39206320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF\nUNSATURATED POLYESTERS :RIGID, PLAIN",
+        "hsn_code": "39206310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF POLY\n(ETHYLENE TEREPHTHALATE):OTHER",
+        "hsn_code": "39206290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF POLY\n(ETHYLENE TEREPHTHALATE):FLEXIBLE, PLAIN",
+        "hsn_code": "39206220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF POLY\n(ETHYLENE TEREPHTHALATE):RIGID, PLAIN",
+        "hsn_code": "39206210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF\nPOLYCARBONATES :OTHER",
+        "hsn_code": "39206190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF\nPOLYCARBONATES :FLEXIBLE, PLAIN",
+        "hsn_code": "39206120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYCARBONATES, ALKYD RESINS, POLYALLYL ESTERS OR OTHER POLYESTERS : OF\nPOLYCARBONATES :RIGID, PLAIN",
+        "hsn_code": "39206110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nACRYLIC POLYMERS: OTHER : OTHER : OTHER",
+        "hsn_code": "39205999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nACRYLIC POLYMERS: OTHER : OTHER : FLEXIBLE, PLAIN",
+        "hsn_code": "39205992",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nACRYLIC POLYMERS: OTHER : OTHER : RIGID, PLAIN",
+        "hsn_code": "39205991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nACRYLIC POLYMERS: OTHER : POLYACRYLATE SHEETS:OTHER",
+        "hsn_code": "39205919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nACRYLIC POLYMERS: OTHER : POLYACRYLATE SHEETS:FLEXIBLE, PLAIN",
+        "hsn_code": "39205912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nACRYLIC POLYMERS: OTHER : POLYACRYLATE SHEETS:RIGID, PLAIN",
+        "hsn_code": "39205911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nACRYLIC POLYMERS: OF POLY (METHYL METHACRYLATE): SHEETS : OTHER : OTHER",
+        "hsn_code": "39205199",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nACRYLIC POLYMERS: OF POLY (METHYL METHACRYLATE): SHEETS : OTHER : FLEXIBLE, PLAIN",
+        "hsn_code": "39205192",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nACRYLIC POLYMERS: OF POLY (METHYL METHACRYLATE): SHEETS : OTHER : RIGID, PLAIN",
+        "hsn_code": "39205191",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nACRYLIC POLYMERS: OF POLY (METHYL METHACRYLATE): SHEETS :OTHER",
+        "hsn_code": "39205119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nACRYLIC POLYMERS: OF POLY (METHYL METHACRYLATE): SHEETS :FLEXIBLE, PLAIN",
+        "hsn_code": "39205112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nACRYLIC POLYMERS: OF POLY (METHYL METHACRYLATE): SHEETS :RIGID, PLAIN",
+        "hsn_code": "39205111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYMERS OF VINYL CHLORIDE:OTHER",
+        "hsn_code": "39204900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYMERS OF VINYL CHLORIDE:CONTAINING BY WEIGHT NOT LESS THAN 6% OF PLASTICIZERS",
+        "hsn_code": "39204300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYMERS OF STYRENE :OTHER",
+        "hsn_code": "39203090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYMERS OF STYRENE :FLEXIBLE, PLAIN",
+        "hsn_code": "39203020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYMERS OF STYRENE :RIGID, PLAIN",
+        "hsn_code": "39203010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "20 OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYMERS OF PROPYLENE :OTHER",
+        "hsn_code": "39202090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "20 OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYMERS OF PROPYLENE :FLEXIBLE, PLAIN",
+        "hsn_code": "39202020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "20 OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYMERS OF PROPYLENE :RIGID, PLAIN",
+        "hsn_code": "39202010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYMERS OF ETHYLENE: SHEETS OF POLYETHYLENE: OTHER : OTHER",
+        "hsn_code": "39201099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYMERS OF ETHYLENE: SHEETS OF POLYETHYLENE: OTHER : FLEXIBLE, PLAIN",
+        "hsn_code": "39201092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYMERS OF ETHYLENE: SHEETS OF POLYETHYLENE: OTHER : RIGID, PLAIN",
+        "hsn_code": "39201091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYMERS OF ETHYLENE: SHEETS OF POLYETHYLENE: OTHER",
+        "hsn_code": "39201019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYMERS OF ETHYLENE: SHEETS OF POLYETHYLENE: FLEXIBLE, PLAIN",
+        "hsn_code": "39201012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PLATES, SHEETS, FILM, FOIL AND STRIP, OF PLASTICS, NON - CELLULAR AND NOT\nREINFORCED, LAMINATED, SUPPORTED OR SIMILARLY COMBINED WITH OTHER MATERIALS - OF\nPOLYMERS OF ETHYLENE: SHEETS OF POLYETHYLENE: RIGID, PLAIN",
+        "hsn_code": "39201011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF-ADHESIVE PLATES, SHEETS, FILM, FOIL, TAPE, STRIP AND OTHER FLAT SHAPES, OF PLASTICS, WHETHER OR NOT IN ROLLS",
+        "hsn_code": "3919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF - ADHESIVE PLATES, SHEETS, FILM, FOIL, TAPE, STRIP AND OTHER FLAT SHAPES, OF\nPLASTICS, WHETHER OR NOT IN ROLLS - OTHER : OTHER",
+        "hsn_code": "39199090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF - ADHESIVE PLATES, SHEETS, FILM, FOIL, TAPE, STRIP AND OTHER FLAT SHAPES, OF\nPLASTICS, WHETHER OR NOT IN ROLLS - OTHER :CELLULOSE ADHESIVE TAPE",
+        "hsn_code": "39199020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF - ADHESIVE PLATES, SHEETS, FILM, FOIL, TAPE, STRIP AND OTHER FLAT SHAPES, OF\nPLASTICS, WHETHER OR NOT IN ROLLS - OTHER : PLASTIC STICKERS, WHETHER OR NOT PRINTED,\nEMBOSSED, OR IMPREGNATED",
+        "hsn_code": "39199010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SELF - ADHESIVE PLATES, SHEETS, FILM, FOIL, TAPE, STRIP AND OTHER FLAT SHAPES, OF\nPLASTICS, WHETHER OR NOT IN ROLLS - IN ROLLS OF WIDTH NOT EXCEEDING 20 CM",
+        "hsn_code": "39191000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLOOR COVERINGS OF PLASTICS, WHETHER OR NOT SELF-ADHESIVE, IN ROLLS OR IN THE FORM OF TILES; WALL OR CEILING COVERINGS OF PLASTICS, AS DEFINED IN NOTE 9 TO THIS CHAPTER",
+        "hsn_code": "3918",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLOOR COVERINGS OF PLASTICS, WHETHER OR NOT SELF - ADHESIVE, IN ROLLS OR IN THE FORM\nOF TILES; WALL OR CEILING COVERINGS OF PLASTICS, AS DEFINED IN NOTE 9 TO THIS CHAPTER -\nOF OTHER PLASTICS :OTHER",
+        "hsn_code": "39189090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLOOR COVERINGS OF PLASTICS, WHETHER OR NOT SELF - ADHESIVE, IN ROLLS OR IN THE FORM\nOF TILES; WALL OR CEILING COVERINGS OF PLASTICS, AS DEFINED IN NOTE 9 TO THIS CHAPTER -\nOF OTHER PLASTICS :WALL OR CEILING COVERINGS COMBINED WITH KNITTED OR WOVEN\nFABRICS, NONWOVENS OR FELTS",
+        "hsn_code": "39189020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLOOR COVERINGS OF PLASTICS, WHETHER OR NOT SELF - ADHESIVE, IN ROLLS OR IN THE FORM\nOF TILES; WALL OR CEILING COVERINGS OF PLASTICS, AS DEFINED IN NOTE 9 TO THIS CHAPTER -\nOF OTHER PLASTICS :FLOOR COVERINGS OF LINOXYNE",
+        "hsn_code": "39189010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLOOR COVERINGS OF PLASTICS, WHETHER OR NOT SELF - ADHESIVE, IN ROLLS OR IN THE FORM\nOF TILES; WALL OR CEILING COVERINGS OF PLASTICS, AS DEFINED IN NOTE 9 TO THIS CHAPTER -\nOF POLYMERS OF VINYL CHLORIDE :OTHER",
+        "hsn_code": "39181090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLOOR COVERINGS OF PLASTICS, WHETHER OR NOT SELF - ADHESIVE, IN ROLLS OR IN THE FORM\nOF TILES; WALL OR CEILING COVERINGS OF PLASTICS, AS DEFINED IN NOTE 9 TO THIS CHAPTER -\nOF POLYMERS OF VINYL CHLORIDE : WALL OR CEILING COVERINGS COMBINED WITH KNITTED OR\nWOVEN FABRICS, NONWOVENS OR FELTS",
+        "hsn_code": "39181010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES), OF PLASTICS",
+        "hsn_code": "3917",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - FITTINGS",
+        "hsn_code": "39174000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - OTHER TUBES, PIPES AND HOSES: OTHER:OTHER",
+        "hsn_code": "39173990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - OTHER TUBES, PIPES AND HOSES: OTHER:OF ADDITION POLYMERISATION\nPRODUCTS",
+        "hsn_code": "39173920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - OTHER TUBES, PIPES AND HOSES: OTHER:OF CONDENSATION OR\nREARRANGEMENT POLYMERIZATION PRODUCTS, WHETHER OR NOT CHEMICALLY MODIFIED",
+        "hsn_code": "39173910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - OTHER TUBES, PIPES AND HOSES: OTHER, NOT REINFORCED OR OTHERWISE\nCOMBINED WITH OTHER MATERIALS, WITH FITTINGS",
+        "hsn_code": "39173300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - OTHER TUBES, PIPES AND HOSES: OTHER, NOT REINFORCED OR OTHERWISE\nCOMBINED WITH OTHER MATERIALS, WITHOUT FITTINGS:OTHER",
+        "hsn_code": "39173290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - OTHER TUBES, PIPES AND HOSES: OTHER, NOT REINFORCED OR OTHERWISE\nCOMBINED WITH OTHER MATERIALS, WITHOUT FITTINGS:OF ADDITION POLYMERISATION\nPRODUCTS",
+        "hsn_code": "39173220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - OTHER TUBES, PIPES AND HOSES: OTHER, NOT REINFORCED OR OTHERWISE\nCOMBINED WITH OTHER MATERIALS, WITHOUT FITTINGS:OF CONDENSATION OR\nREARRANGEMENT POLYMERIZATION PRODUCTS, WHETHER OR NOT CHEMICALLY MODIFIED",
+        "hsn_code": "39173210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - OTHER TUBES, PIPES AND HOSES: FLEXIBLE TUBES, PIPES AND HOSES, HAVING A\nMINIMUM BURST PRESSURE OF 27.6 MPA",
+        "hsn_code": "39173100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - TUBES, PIPES AND HOSES, RIGID: OF OTHER PLASTICS :OTHER",
+        "hsn_code": "39172990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - TUBES, PIPES AND HOSES, RIGID: OF OTHER PLASTICS :TUBES OF VINYL PLASTICS",
+        "hsn_code": "39172950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - TUBES, PIPES AND HOSES, RIGID: OF OTHER PLASTICS :TUBES OF CELLULOSE\nACETATE OR ACETATE BUTYRATE",
+        "hsn_code": "39172940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - TUBES, PIPES AND HOSES, RIGID: OF OTHER PLASTICS :TUBES OF CELLULOSE\nNITRATE AND CELLULOID, WHETHER OR NOT PLASTICISED",
+        "hsn_code": "39172930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - TUBES, PIPES AND HOSES, RIGID: OF OTHER PLASTICS : SEAMLESS TUBES OF\nPOLYMERS AND COPOLYMERS OF POLYSTYRENE",
+        "hsn_code": "39172920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - TUBES, PIPES AND HOSES, RIGID: OF OTHER PLASTICS : SEAMLESS TUBES OF\nCOPOLYMERS OF VINYL ACETATE AND VINYL CHLORIDE",
+        "hsn_code": "39172910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - TUBES, PIPES AND HOSES, RIGID: OF POLYMERS OF VINYL CHLORIDE :OTHER",
+        "hsn_code": "39172390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - TUBES, PIPES AND HOSES, RIGID: OF POLYMERS OF VINYL CHLORIDE :SEAMLESS\nTUBES",
+        "hsn_code": "39172310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - TUBES, PIPES AND HOSES, RIGID: OF POLYMERS OF PROPYLENE",
+        "hsn_code": "39172200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - TUBES, PIPES AND HOSES, RIGID: OF POLYMERS OF ETHYLENE:OTHER",
+        "hsn_code": "39172190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - TUBES, PIPES AND HOSES, RIGID: OF POLYMERS OF ETHYLENE:TUBES OF\nPOLYETHYLENE",
+        "hsn_code": "39172110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - ARTIFICIAL GUTS (SAUSAGE CASINGS) OF HARDENED PROTEIN OR OF CELLULOSIC\nMATERIALS: OF CELLULOSIC MATERIALS",
+        "hsn_code": "39171020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TUBES, PIPES AND HOSES, AND FITTINGS THEREFOR (FOR EXAMPLE, JOINTS, ELBOWS, FLANGES),\nOF PLASTICS - ARTIFICIAL GUTS (SAUSAGE CASINGS) OF HARDENED PROTEIN OR OF CELLULOSIC\nMATERIALS: OF HARDENED PROTEIN",
+        "hsn_code": "39171010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS-SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS AND PROFILE SHAPES, WHETHER OR NOT SURFACE-WORKED BUT NOT OTHERWISE WORKED, OF PLASTICS",
+        "hsn_code": "3916",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF OTHER PLASTICS :OF OTHER POLYMERISATION AND COPOLYMERISATION\nPRODUCTS",
+        "hsn_code": "39169090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF OTHER PLASTICS : OF VINYL PLASTIC",
+        "hsn_code": "39169080",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF OTHER PLASTICS : OF CELLULOSE ACETATE AND ACETATE BUTYRATE, WHETHER\nOR NOT PLASTICIZED",
+        "hsn_code": "39169070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF OTHER PLASTICS :OF VULCANIZED FIBRE",
+        "hsn_code": "39169060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF OTHER PLASTICS :OF CELLULOSE NITRATE AND CELLULOID, WHETHER OR NOT\nPLASTICIZED",
+        "hsn_code": "39169050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF OTHER PLASTICS :OF REGENERATED CELLULOSE",
+        "hsn_code": "39169040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF OTHER PLASTICS : OF POLYMERISATION AND COPOLYMERISATION PRODUCTS\nOF POLYSTYRENE AND POLYMETHYL METHACRYLATE :OF POLYMETHYL METHACRYLATE",
+        "hsn_code": "39169032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF OTHER PLASTICS : OF POLYMERISATION AND COPOLYMERISATION PRODUCTS\nOF POLYSTYRENE AND POLYMETHYL METHACRYLATE :OF POLYMERISATION AND\nCOPOLYMERISATION PRODUCTS OF POLYSTYRENE",
+        "hsn_code": "39169031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF OTHER PLASTICS : OF PHENOPLAST, AMINOPLAST, ALKYDS AND POLYESTERS,\nPOLYAMIDES, POLYURETHANES, EPOXIDE - RESINS (INCLUDING WASTE AND SCRAP),\nPOLYPROPYLENE AND ACRYLIC, METHACRYLIC AND ACRYLOMETHACRYLIC POLYMERS :OF\nACRYLICMETHACRYLIC AND ACRYLOMETHACRYLIC POLYMERS",
+        "hsn_code": "39169028",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF OTHER PLASTICS : OF PHENOPLAST, AMINOPLAST, ALKYDS AND POLYESTERS,\nPOLYAMIDES, POLYURETHANES, EPOXIDE - RESINS (INCLUDING WASTE AND SCRAP),\nPOLYPROPYLENE AND ACRYLIC, METHACRYLIC AND ACRYLOMETHACRYLIC POLYMERS :OF\nPOLYPROPYLENE",
+        "hsn_code": "39169027",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF OTHER PLASTICS : OF PHENOPLAST, AMINOPLAST, ALKYDS AND POLYESTERS,\nPOLYAMIDES, POLYURETHANES, EPOXIDE - RESINS (INCLUDING WASTE AND SCRAP),\nPOLYPROPYLENE AND ACRYLIC, METHACRYLIC AND ACRYLOMETHACRYLIC POLYMERS :OF\nEPOXIDE - RESINS (INCLUDING WASTE AND SCRAP)",
+        "hsn_code": "39169026",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF OTHER PLASTICS : OF PHENOPLAST, AMINOPLAST, ALKYDS AND POLYESTERS,\nPOLYAMIDES, POLYURETHANES, EPOXIDE - RESINS (INCLUDING WASTE AND SCRAP),\nPOLYPROPYLENE AND ACRYLIC, METHACRYLIC AND ACRYLOMETHACRYLIC POLYMERS :OF\nPOLYURETHANES",
+        "hsn_code": "39169025",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF OTHER PLASTICS : OF PHENOPLAST, AMINOPLAST, ALKYDS AND POLYESTERS,\nPOLYAMIDES, POLYURETHANES, EPOXIDE - RESINS (INCLUDING WASTE AND SCRAP),\nPOLYPROPYLENE AND ACRYLIC, METHACRYLIC AND ACRYLOMETHACRYLIC POLYMERS : OF\nPOLYAMIDES",
+        "hsn_code": "39169024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF OTHER PLASTICS : OF PHENOPLAST, AMINOPLAST, ALKYDS AND POLYESTERS,\nPOLYAMIDES, POLYURETHANES, EPOXIDE - RESINS (INCLUDING WASTE AND SCRAP),\nPOLYPROPYLENE AND ACRYLIC, METHACRYLIC AND ACRYLOMETHACRYLIC POLYMERS :OF\nALKYDS AND POLYSTERS",
+        "hsn_code": "39169023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF OTHER PLASTICS : OF PHENOPLAST, AMINOPLAST, ALKYDS AND POLYESTERS,\nPOLYAMIDES, POLYURETHANES, EPOXIDE - RESINS (INCLUDING WASTE AND SCRAP),\nPOLYPROPYLENE AND ACRYLIC, METHACRYLIC AND ACRYLOMETHACRYLIC POLYMERS :OF\nAMINOPLAST",
+        "hsn_code": "39169022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF OTHER PLASTICS : OF PHENOPLAST, AMINOPLAST, ALKYDS AND POLYESTERS,\nPOLYAMIDES, POLYURETHANES, EPOXIDE - RESINS (INCLUDING WASTE AND SCRAP),\nPOLYPROPYLENE AND ACRYLIC, METHACRYLIC AND ACRYLOMETHACRYLIC POLYMERS :OF\nPHENOPLAST",
+        "hsn_code": "39169021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF OTHER PLASTICS : CANES",
+        "hsn_code": "39169010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF POLYMERS OF VINYL CHLORIDE: OF POLY (VINYL CHLORIDE) COPOLYMERS :\nOTHER: OTHER",
+        "hsn_code": "39162099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF POLYMERS OF VINYL CHLORIDE: OF POLY (VINYL CHLORIDE) COPOLYMERS :\nOTHER: CANES",
+        "hsn_code": "39162091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF POLYMERS OF VINYL CHLORIDE: OF POLY (VINYL CHLORIDE) COPOLYMERS\n:OTHER",
+        "hsn_code": "39162019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF POLYMERS OF VINYL CHLORIDE: OF POLY (VINYL CHLORIDE) COPOLYMERS\n:CANES",
+        "hsn_code": "39162011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF POLYMERS OF ETHYLENE:OTHER",
+        "hsn_code": "39161090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF POLYMERS OF ETHYLENE:CANES",
+        "hsn_code": "39161020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MONOFILAMENT OF WHICH ANY CROSS - SECTIONAL DIMENSION EXCEEDS 1MM, RODS, STICKS\nAND PROFILE SHAPES, WHETHER OR NOT SURFACE - WORKED BUT NOT OTHERWISE WORKED,\nOF PLASTICS - OF POLYMERS OF ETHYLENE: RODS OF POLYETHYLENE",
+        "hsn_code": "39161010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS",
+        "hsn_code": "3915",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF OTHER PLASTICS :OTHER",
+        "hsn_code": "39159090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF OTHER PLASTICS : OF CELLULOSE AND ITS\nCHEMICAL DERIVATIVES: CELLULOSE PLASTIC WASTE SUCH AS CELLULOSE ACETATC FILM SCRAP\nPLASTICIZED",
+        "hsn_code": "39159075",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF OTHER PLASTICS : OF CELLULOSE AND ITS\nCHEMICAL DERIVATIVES: CELLULOSE PLASTIC WASTE SUCH AS CELLULOSE ACETATC FILM SCRAP\nNON - PLASTICISED",
+        "hsn_code": "39159074",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF OTHER PLASTICS : OF CELLULOSE AND ITS\nCHEMICAL DERIVATIVES: CELLULOSE PLASTIC WASTE SUCH AS CELLULOSE NITRATE FILM SCRAP\nPLASTICIZED",
+        "hsn_code": "39159073",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF OTHER PLASTICS : OF CELLULOSE AND ITS\nCHEMICAL DERIVATIVES: CELLULOSE PLASTIC WASTE SUCH AS CELLULOSE NITRATE FILM SCRAP\nNON - PLASTICISED",
+        "hsn_code": "39159072",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF OTHER PLASTICS : OF CELLULOSE AND ITS\nCHEMICAL DERIVATIVES: OF REGENERATED CELLULOSE",
+        "hsn_code": "39159071",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF OTHER PLASTICS : OF AMINO RESINS; PHENOLIC\nRESINS AND POLYURETHANES :OF POLYURETHANES",
+        "hsn_code": "39159063",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF OTHER PLASTICS : OF AMINO RESINS; PHENOLIC\nRESINS AND POLYURETHANES :OF AMINOPLAST",
+        "hsn_code": "39159062",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF OTHER PLASTICS : OF AMINO RESINS; PHENOLIC\nRESINS AND POLYURETHANES :OF PHENOPLAST",
+        "hsn_code": "39159061",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF OTHER PLASTICS :OF POLYAMIDES",
+        "hsn_code": "39159050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF OTHER PLASTICS : OF ALKYDS, POLYESTERS AND\nEPOXIDE RESINS : OF EPOXIDE RESINS",
+        "hsn_code": "39159049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF OTHER PLASTICS : OF ALKYDS, POLYESTERS AND\nEPOXIDE RESINS : OF PET BOTTLES",
+        "hsn_code": "39159042",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF OTHER PLASTICS : OF ALKYDS, POLYESTERS AND\nEPOXIDE RESINS : OF ALKYDS AND POLYESTERS",
+        "hsn_code": "39159041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF OTHER PLASTICS :OF ACRYLIC POLYMERS AND\nMETHLYACRYLIC COPOLYMERS",
+        "hsn_code": "39159030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF OTHER PLASTICS : OF POLYMERS OF VINYL\nACETATE : OTHER",
+        "hsn_code": "39159029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF OTHER PLASTICS : OF POLYMERS OF VINYL\nACETATE : OF COPOLYMERS OF VINYL ACETATE",
+        "hsn_code": "39159021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF OTHER PLASTICS :OF POLYPROPYLENE",
+        "hsn_code": "39159010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF POLYMERS OF VINYL CHLORIDE:OTHER",
+        "hsn_code": "39153090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF POLYMERS OF VINYL CHLORIDE:OF\nCOPOLYMERS OF VINYL CHLORIDE",
+        "hsn_code": "39153010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF POLYMERS OF STYRENE",
+        "hsn_code": "39152000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WASTE, PARINGS AND SCRAP, OF PLASTICS - OF POLYMERS OF ETHYLENE",
+        "hsn_code": "39151000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ION–EXCHANGERS BASED ON POLYMERS OF HEADINGS 3901 TO 3913, IN PRIMARY FORMS",
+        "hsn_code": "3914",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IONEXCHANGERS BASED ON POLYMERS OF HEADINGS 3901 TO 3913, IN PRIMARY FORMS -\nIONEXCHANGERS BASED ON POLYMERS OF HEADINGS 3901 TO 3913, IN PRIMARY\nFORMS:OTHER",
+        "hsn_code": "39140090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IONEXCHANGERS BASED ON POLYMERS OF HEADINGS 3901 TO 3913, IN PRIMARY FORMS -\nIONEXCHANGERS BASED ON POLYMERS OF HEADINGS 3901 TO 3913, IN PRIMARY FORMS:ION -\nEXCHANGERS OF POLYMERISATION OR CO - POLYMERISATION TYPE",
+        "hsn_code": "39140020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IONEXCHANGERS BASED ON POLYMERS OF HEADINGS 3901 TO 3913, IN PRIMARY FORMS -\nIONEXCHANGERS BASED ON POLYMERS OF HEADINGS 3901 TO 3913, IN PRIMARY FORMS:ION -\nEXCHANGERS OF THE CONDENSATION, POLYCONDENSATION OR POLYADDITION TYPE",
+        "hsn_code": "39140010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Biodiesel and mixtures thereof; not containing or containing less than 70% by weight of petroleum oils or oils obtained from bituminous minerals",
+        "hsn_code": "3826",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BIODIESEL AND MIXTURES THEREOF, NOT CONTAINING OR CONTAINING LESS THAN 70% BY\nWEIGHT OF PETROLEUM OILS OR OILS OBTAINED FROM BITUMINOUS MINERALS",
+        "hsn_code": "38260000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND PREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF MIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "3824",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER: - OTHER",
+        "hsn_code": "38249090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHONIC ACID,METHYL COMPOUND WITH (AMINOIMINO METHYL) UREA (1:1)",
+        "hsn_code": "38249038",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nMIXTURE CONTAINING PERHALOGENATED DERIVATIVES OF ACYCLIC HYDROCARBONS\nCONTAINING TWO OR MORE DIFFERENT HALOGENS OTHER THAN CHLORINE AND FLUORINE;\nFERRITE POWDER; CAPACITOR FLUIDS PCB TYPE; DIPPING OIL FOR TREATMENT OF GRAPES;\nPOLY BROMINATED BIPHENYLS, POLY CHLORINATED BIPHENYLS,POLY-CHLORINATED\nTERPHENYLS, CROCIDOLITE; GOODS OF A KIND KNOWN AS HAZARDOUS WASTE;\nPHOSPHOGYPSUM : PHOSPHOGYPSUM",
+        "hsn_code": "38249037",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nMIXTURE CONTAINING PERHALOGENATED DERIVATIVES OF ACYCLIC HYDROCARBONS\nCONTAINING TWO OR MORE DIFFERENT HALOGENS OTHER THAN CHLORINE AND FLUORINE;\nFERRITE POWDER; CAPACITOR FLUIDS PCB TYPE; DIPPING OIL FOR TREATMENT OF GRAPES;\nPOLY BROMINATED BIPHENYLS, POLY CHLORINATED BIPHENYLS,POLY-CHLORINATED\nTERPHENYLS, CROCIDOLITE; GOODS OF A KIND KNOWN AS HAZARDOUS WASTE;\nPHOSPHOGYPSUM : GOODS OF A KIND KNOWN AS HAZARDOUS WASTE",
+        "hsn_code": "38249036",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nMIXTURE CONTAINING PERHALOGENATED DERIVATIVES OF ACYCLIC HYDROCARBONS\nCONTAINING TWO OR MORE DIFFERENT HALOGENS OTHER THAN CHLORINE AND FLUORINE;\nFERRITE POWDER; CAPACITOR FLUIDS PCB TYPE; DIPPING OIL FOR TREATMENT OF GRAPES;\nPOLY BROMINATED BIPHENYLS, POLY CHLORINATED BIPHENYLS,POLY-CHLORINATED\nTERPHENYLS, CROCIDOLITE; GOODS OF A KIND KNOWN AS HAZARDOUS WASTE;\nPHOSPHOGYPSUM : POLY BROMINATED BIPHENYLS, POLY CHLORINATED BIPHENYLS, POLY\nCHLORINATED TERPHENYLS, CROCIDOLITE",
+        "hsn_code": "38249035",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nMIXTURE CONTAINING PERHALOGENATED DERIVATIVES OF ACYCLIC HYDROCARBONS\nCONTAINING TWO OR MORE DIFFERENT HALOGENS OTHER THAN CHLORINE AND FLUORINE;\nFERRITE POWDER; CAPACITOR FLUIDS PCB TYPE; DIPPING OIL FOR TREATMENT OF GRAPES;\nPOLY BROMINATED BIPHENYLS, POLY CHLORINATED BIPHENYLS,POLY-CHLORINATED\nTERPHENYLS, CROCIDOLITE; GOODS OF A KIND KNOWN AS HAZARDOUS WASTE;\nPHOSPHOGYPSUM : DIPPING OIL FOR TREATMENT OF GRAPES",
+        "hsn_code": "38249034",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nMIXTURE CONTAINING PERHALOGENATED DERIVATIVES OF ACYCLIC HYDROCARBONS\nCONTAINING TWO OR MORE DIFFERENT HALOGENS OTHER THAN CHLORINE AND FLUORINE;\nFERRITE POWDER; CAPACITOR FLUIDS PCB TYPE; DIPPING OIL FOR TREATMENT OF GRAPES;\nPOLY BROMINATED BIPHENYLS, POLY CHLORINATED BIPHENYLS,POLY-CHLORINATED\nTERPHENYLS, CROCIDOLITE; GOODS OF A KIND KNOWN AS HAZARDOUS WASTE;\nPHOSPHOGYPSUM : CAPACITOR FLUIDS PCB TYPE",
+        "hsn_code": "38249033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nMIXTURE CONTAINING PERHALOGENATED DERIVATIVES OF ACYCLIC HYDROCARBONS\nCONTAINING TWO OR MORE DIFFERENT HALOGENS OTHER THAN CHLORINE AND FLUORINE;\nFERRITE POWDER; CAPACITOR FLUIDS PCB TYPE; DIPPING OIL FOR TREATMENT OF GRAPES;\nPOLY BROMINATED BIPHENYLS, POLY CHLORINATED BIPHENYLS,POLY-CHLORINATED\nTERPHENYLS, CROCIDOLITE; GOODS OF A KIND KNOWN AS HAZARDOUS WASTE;\nPHOSPHOGYPSUM : FERRITE POWDER",
+        "hsn_code": "38249032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nMIXTURE CONTAINING PERHALOGENATED DERIVATIVES OF ACYCLIC HYDROCARBONS\nCONTAINING TWO OR MORE DIFFERENT HALOGENS OTHER THAN CHLORINE AND FLUORINE;\nFERRITE POWDER; CAPACITOR FLUIDS PCB TYPE; DIPPING OIL FOR TREATMENT OF GRAPES;\nPOLY BROMINATED BIPHENYLS, POLY CHLORINATED BIPHENYLS,POLY-CHLORINATED\nTERPHENYLS, CROCIDOLITE; GOODS OF A KIND KNOWN AS HAZARDOUS WASTE;\nPHOSPHOGYPSUM : MIXTURES CONTAINING PERHALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS OTHER THAN CHLORINE\nAND FLUORINE",
+        "hsn_code": "38249031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nELECTROPLATING SALTS; WATER TREATMENT CHEMICALS; ION EXCHANGER; CORRECTING FLUID;\nPRECIPITATED SILICA AND SILICA GEL; OIL WELL CHEMICAL: OIL WELL CHEMICALS",
+        "hsn_code": "38249026",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nELECTROPLATING SALTS; WATER TREATMENT CHEMICALS; ION EXCHANGER; CORRECTING FLUID;\nPRECIPITATED SILICA AND SILICA GEL; OIL WELL CHEMICAL: PRECIPITATED SILICA AND SILICA GEL",
+        "hsn_code": "38249025",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nELECTROPLATING SALTS; WATER TREATMENT CHEMICALS; ION EXCHANGER; CORRECTING FLUID;\nPRECIPITATED SILICA AND SILICA GEL; OIL WELL CHEMICAL: CORRECTING FLUID",
+        "hsn_code": "38249024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nELECTROPLATING SALTS; WATER TREATMENT CHEMICALS; ION EXCHANGER; CORRECTING FLUID;\nPRECIPITATED SILICA AND SILICA GEL; OIL WELL CHEMICAL: GRAMOPHONE RECORDS MAKING\nMATERIAL",
+        "hsn_code": "38249023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nELECTROPLATING SALTS; WATER TREATMENT CHEMICALS; ION EXCHANGER; CORRECTING FLUID;\nPRECIPITATED SILICA AND SILICA GEL; OIL WELL CHEMICAL: WATER TREATMENT CHEMICALS,\nION EXCHANGER (INN) SUCH AS PERMIUTITS, ZEOLITES)",
+        "hsn_code": "38249022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nELECTROPLATING SALTS; WATER TREATMENT CHEMICALS; ION EXCHANGER; CORRECTING FLUID;\nPRECIPITATED SILICA AND SILICA GEL; OIL WELL CHEMICAL: ELECTROPLATING SALTS",
+        "hsn_code": "38249021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nAMMONIACAL GAS LIQUORS AND SPENT OXIDE PRODUCED IN COAL GAS PURIFICATION, CASE\nHARDENING COMPOUND, HEAT TRANSFER SALTS; MIXTURE OF DIPHENYL AND DIPHENYL OXIDE\nAS HEAT TRANSFER MEDIUM, MIXED POLYETHYLENE GLYCOLS; SALTS FOR CURING OR SALTING,\nSURFACE TENSION REDUCING AGENTS SURFACE TENSION REDUCING AGENTS",
+        "hsn_code": "38249017",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nAMMONIACAL GAS LIQUORS AND SPENT OXIDE PRODUCED IN COAL GAS PURIFICATION, CASE\nHARDENING COMPOUND, HEAT TRANSFER SALTS; MIXTURE OF DIPHENYL AND DIPHENYL OXIDE\nAS HEAT TRANSFER MEDIUM, MIXED POLYETHYLENE GLYCOLS; SALTS FOR CURING OR SALTING,\nSURFACE TENSION REDUCING AGENTS SALTS FOR CURING OR SALTING",
+        "hsn_code": "38249016",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nAMMONIACAL GAS LIQUORS AND SPENT OXIDE PRODUCED IN COAL GAS PURIFICATION, CASE\nHARDENING COMPOUND, HEAT TRANSFER SALTS; MIXTURE OF DIPHENYL AND DIPHENYL OXIDE\nAS HEAT TRANSFER MEDIUM, MIXED POLYETHYLENE GLYCOLS; SALTS FOR CURING OR SALTING,\nSURFACE TENSION REDUCING AGENTS MIXED POLYETHYLENE GLYCOLS",
+        "hsn_code": "38249015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nAMMONIACAL GAS LIQUORS AND SPENT OXIDE PRODUCED IN COAL GAS PURIFICATION, CASE\nHARDENING COMPOUND, HEAT TRANSFER SALTS; MIXTURE OF DIPHENYL AND DIPHENYL OXIDE\nAS HEAT TRANSFER MEDIUM, MIXED POLYETHYLENE GLYCOLS; SALTS FOR CURING OR SALTING,\nSURFACE TENSION REDUCING AGENTS MIXTURE OF DIPHENYL AND DIPHENYL OXIDE AS HEAT\nTRANSFER MEDIUM",
+        "hsn_code": "38249014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nAMMONIACAL GAS LIQUORS AND SPENT OXIDE PRODUCED IN COAL GAS PURIFICATION, CASE\nHARDENING COMPOUND, HEAT TRANSFER SALTS; MIXTURE OF DIPHENYL AND DIPHENYL OXIDE\nAS HEAT TRANSFER MEDIUM, MIXED POLYETHYLENE GLYCOLS; SALTS FOR CURING OR SALTING,\nSURFACE TENSION REDUCING AGENTS HEAT TRANSFER SALTS",
+        "hsn_code": "38249013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nAMMONIACAL GAS LIQUORS AND SPENT OXIDE PRODUCED IN COAL GAS PURIFICATION, CASE\nHARDENING COMPOUND, HEAT TRANSFER SALTS; MIXTURE OF DIPHENYL AND DIPHENYL OXIDE\nAS HEAT TRANSFER MEDIUM, MIXED POLYETHYLENE GLYCOLS; SALTS FOR CURING OR SALTING,\nSURFACE TENSION REDUCING AGENTS: CASE HARDENING COMPOUND",
+        "hsn_code": "38249012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER:\nAMMONIACAL GAS LIQUORS AND SPENT OXIDE PRODUCED IN COAL GAS PURIFICATION, CASE\nHARDENING COMPOUND, HEAT TRANSFER SALTS; MIXTURE OF DIPHENYL AND DIPHENYL OXIDE\nAS HEAT TRANSFER MEDIUM, MIXED POLYETHYLENE GLYCOLS; SALTS FOR CURING OR SALTING,\nSURFACE TENSION REDUCING AGENTS: AMMONIACAL GAS LIQUORS AND SPENT OXIDE\nPRODUCED IN COAL GAS PURIFICATION",
+        "hsn_code": "38249011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED- MIXTURES AND\nPREPARATIONS CONTAINING OXIRANE (ETHYLENE OXIDE), POLYBROMINATED BIPHENYLS\n(PBBS), POLYCHLORINATED BIPHENYLS (PCBS), POLYCHLORINATED TERPHENYLS (PCTS) OR TRIS\n(2,3-DIBROMOPROPYL) PHOSPHATE:-- CONTAINING TRIS (2,3-DIBROMOPROPYL) PHOSPHATE",
+        "hsn_code": "38248300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED- MIXTURES AND\nPREPARATIONS CONTAINING OXIRANE (ETHYLENE OXIDE), POLYBROMINATED BIPHENYLS\n(PBBS), POLYCHLORINATED BIPHENYLS (PCBS), POLYCHLORINATED TERPHENYLS (PCTS) OR TRIS\n(2,3-DIBROMOPROPYL) PHOSPHATE:-- CONTAINING POLYCHLORINATED BIPHENYLS (PCBS),\nPOLYCHLORINATED TERPHENYLS (PCTS) OR POLYBROMINATED BIPHENYLS (PBBS)",
+        "hsn_code": "38248200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED- MIXTURES AND\nPREPARATIONS CONTAINING OXIRANE (ETHYLENE OXIDE), POLYBROMINATED BIPHENYLS\n(PBBS), POLYCHLORINATED BIPHENYLS (PCBS), POLYCHLORINATED TERPHENYLS (PCTS) OR TRIS\n(2,3-DIBROMOPROPYL) PHOSPHATE:-- CONTAINING OXIRANE (ETHYLENE OXIDE)",
+        "hsn_code": "38248100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED MIXTURES\nCONTAINING PERHALOGENATED DERIVATIVES OF ACYCLIC HYDROCARBONS CONTAINING TWO\nOR MORE DIFFERENT HALOGENS OTHER: :OTHER",
+        "hsn_code": "38247990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED MIXTURES\nCONTAINING PERHALOGENATED DERIVATIVES OF ACYCLIC HYDROCARBONS CONTAINING TWO\nOR MORE DIFFERENT HALOGENS OTHER: :CONTAINING OZONE DEPLETING SUBSTANCES",
+        "hsn_code": "38247910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED- MIXTURES\nCONTAINING HALOGENATED DERIVATIVES OF METHANE, ETHANE OR PROPANE:-- OTHER",
+        "hsn_code": "38247900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED- MIXTURES\nCONTAINING HALOGENATED DERIVATIVES OF METHANE, ETHANE OR PROPANE:-- CONTAINING\nPERFLUOROCARBONS (PFCS) OR HYDROFLUOROCARBONS (HFCS), BUT NOT CONTAINING\nCHLOROFLUOROCARBONS (CFCS)OR HYDROCHLOROFLUOROCARBONS (HCFCS)",
+        "hsn_code": "38247800",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED- MIXTURES\nCONTAINING HALOGENATED DERIVATIVES OF METHANE, ETHANE OR PROPANE:-- CONTAINING\nBROMOMETHANE (METHYL BROMIDE) OR BROMOCHLOROMETHANE",
+        "hsn_code": "38247700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED- MIXTURES\nCONTAINING HALOGENATED DERIVATIVES OF METHANE, ETHANE OR PROPANE:-- CONTAINING\n1,1,1-TRICHLOROETHANE (METHYL CHLOROFORM)",
+        "hsn_code": "38247600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED- MIXTURES\nCONTAINING HALOGENATED DERIVATIVES OF METHANE, ETHANE OR PROPANE:-- CONTAINING\nCARBON TETRACHLORIDE",
+        "hsn_code": "38247500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED- MIXTURES\nCONTAINING HALOGENATED DERIVATIVES OF METHANE, ETHANE OR PROPANE:-- CONTAINING\nHYDROCHLOROFLUOROCARBONS(HCFCS), WHETHER OR NOT CONTAINING\nPERFLUOROCARBONS(PFCS) OR HYDROFLUOROCARBONS (HFCS), BUT NOT CONTAINING\nCHLOROFLUOROCARBONS(CFCS)",
+        "hsn_code": "38247400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED- MIXTURES\nCONTAINING HALOGENATED DERIVATIVES OF METHANE, ETHANE OR PROPANE:-- CONTAINING\nHYDROBROMOFLUOROCARBONS(HBFCS)",
+        "hsn_code": "38247300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED- MIXTURES\nCONTAINING HALOGENATED DERIVATIVES OF METHANE, ETHANE OR PROPANE:-- CONTAINING\nBROMOCHLORODIFLUOROMETHANE, BROMOTRIFLUOROMETHANE OR DIBROMOTETRAFLUORO\nETHANES",
+        "hsn_code": "38247200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED MIXTURES\nCONTAINING PERHALOGENATED DERIVATIVES OF ACYCLIC HYDROCARBONS CONTAINING TWO\nOR MORE DIFFERENT HALOGENS :CONTAINING ACYCLIC HYDROCARBONS PERHALOGENATED\nONLY WITH FLUORINE AND CHLORINE: OTHER",
+        "hsn_code": "38247190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED MIXTURES\nCONTAINING PERHALOGENATED DERIVATIVES OF ACYCLIC HYDROCARBONS CONTAINING TWO\nOR MORE DIFFERENT HALOGENS :CONTAINING ACYCLIC HYDROCARBONS PERHALOGENATED\nONLY WITH FLUORINE AND CHLORINE: CONTAINING OZONE DEPLETING SUBSTANCES",
+        "hsn_code": "38247110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED- MIXTURES\nCONTAINING HALOGENATED DERIVATIVES OF METHANE, ETHANE OR PROPANE:-- CONTAINING\nCHLOROFLUOROCARBONS (CFCS), WHETHER OR NOT CONTAINING\nHYDROCHLOROFLUOROCARBONS(HCFCS), PERFLUOROCARBONS (PFCS) OR\nHYDROFLUOROCARBONS(HFCS)",
+        "hsn_code": "38247100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED- SORBITOL\nOTHER THAN THAT OF SUB-HEADING 2905 44 :--- OTHER",
+        "hsn_code": "38246090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED SORBITOL\nOTHER THAN THAT OF SUB-HEADING 2905 44 : IN AQUEOUS SOLUTION",
+        "hsn_code": "38246010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED NON\nREFRACTORY MORTARS AND CONCRETES : OTHER",
+        "hsn_code": "38245090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED NON\nREFRACTORY MORTARS AND CONCRETES : CONCRETES READY TO USE KNOWN AS READY- MIX\nCONCRETE (RMC)",
+        "hsn_code": "38245010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED PREPARED\nADDITIVES FOR CEMENTS, MORTARS OR CONCRETES : OTHER",
+        "hsn_code": "38244090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED PREPARED\nADDITIVES FOR CEMENTS, MORTARS OR CONCRETES: DAMP PROOF OR WATER PROOF\nCOMPOUNDS",
+        "hsn_code": "38244010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED : NON\nAGGLOMERATED METAL CARBIDES MIXED TOGETHER OR WITH METALLIC BINDERS",
+        "hsn_code": "38243000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED NAPHTHENIC\nACIDS, THEIR WATER-INSOLUBLE SALTS AND THEIR ESTERS: OTHER",
+        "hsn_code": "38242090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED NAPHTHENIC\nACIDS, THEIR WATER-INSOLUBLE SALTS AND THEIR ESTERS: NAPHTHENIC ACIDS",
+        "hsn_code": "38242020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED NAPHTHENIC\nACIDS, THEIR WATER-INSOLUBLE SALTS AND THEIR ESTERS: COPPER NAPHTHENATE",
+        "hsn_code": "38242010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED BINDERS FOR FOUNDRY MOULDS OR CORES; CHEMICAL PRODUCTS AND\nPREPARATIONS OF THE CHEMICAL OR ALLIED INDUSTRIES (INCLUDING THOSE CONSISTING OF\nMIXTURES OF NATURAL PRODUCTS), NOT ELSEWHERE SPECIFIED OR INCLUDED PREPARED\nBINDERS FOR FOUNDRY MOULDS OR CORES",
+        "hsn_code": "38241000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING; INDUSTRIAL FATTY ALCOHOLS",
+        "hsn_code": "3823",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING; INDUSTRIAL FATTY\nALCOHOLS INDUSTRIAL FATTY ALCOHOLS: OTHER",
+        "hsn_code": "38237090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING; INDUSTRIAL FATTY\nALCOHOLS INDUSTRIAL FATTY ALCOHOLS: STEARYL ALCOHOL",
+        "hsn_code": "38237040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING; INDUSTRIAL FATTY\nALCOHOLS INDUSTRIAL FATTY ALCOHOLS: OLEYL ALCOHOL",
+        "hsn_code": "38237030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING; INDUSTRIAL FATTY\nALCOHOLS INDUSTRIAL FATTY ALCOHOLS: LAURYL ALCOHOL",
+        "hsn_code": "38237020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING; INDUSTRIAL FATTY\nALCOHOLS INDUSTRIAL FATTY ALCOHOLS: CETYL ALCOHOL",
+        "hsn_code": "38237010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING; INDUSTRIAL FATTY\nALCOHOLS INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING: OTHER",
+        "hsn_code": "38231900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING; INDUSTRIAL FATTY\nALCOHOLS INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING: TALL OIL\nFATTY ACIDS",
+        "hsn_code": "38231300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING; INDUSTRIAL FATTY\nALCOHOLS INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING: OLEIC ACID",
+        "hsn_code": "38231200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING; INDUSTRIAL FATTY\nALCOHOLS INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING: STEARIC\nACID: OTHER STEARIC ACID OR STEARIN",
+        "hsn_code": "38231190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING; INDUSTRIAL FATTY\nALCOHOLS INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING: STEARIC\nACID: PALM STEARIN: OTHER",
+        "hsn_code": "38231119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING; INDUSTRIAL FATTY\nALCOHOLS INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING: STEARIC\nACID: PALM STEARIN: RBD",
+        "hsn_code": "38231112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING; INDUSTRIAL FATTY\nALCOHOLS INDUSTRIAL MONOCARBOXYLIC FATTY ACIDS; ACID OILS FROM REFINING: STEARIC\nACID: PALM STEARIN: CRUDE",
+        "hsn_code": "38231111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DIAGNOSTIC OR LABORATORY REAGENTS ON A BACKING,  PREPARED DIAGNOSTIC OR LABORATORY REAGENTS WHETHER OR NOT ON A BACKING, OTHER THAN THOSE OF HEADING 3002 OR 3006; CERTIFIED REFERENCE MATERIALS",
+        "hsn_code": "3822",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DIAGNOSTIC OR LABORATORY REAGENTS ON A BACKING, PREPARED DIAGNOSTIC OR\nLABORATORY REAGENTS WHETHER OR NOT ON A BACKING, OTHER THAN THOSE OF HEADING\n3002 OR 3006; CERTIFIED REFERENCE MATERIALS DIAGNOSTIC OR LABORATORY REAGENTS ON A\nBACKING, PREPARED DIAGNOSTIC OR LABORATORY REAGENTS WHETHER OR NOT ON A\nBACKING, OTHER THAN THOSE OF HEADING 3002 OR 3006; CERTIFIED REFERENCE MATERIALS:\nOTHER",
+        "hsn_code": "38220090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DIAGNOSTIC OR LABORATORY REAGENTS ON A BACKING, PREPARED DIAGNOSTIC OR\nLABORATORY REAGENTS WHETHER OR NOT ON A BACKING, OTHER THAN THOSE OF HEADING\n3002 OR 3006; CERTIFIED REFERENCE MATERIALS DIAGNOSTIC OR LABORATORY REAGENTS ON A\nBACKING, PREPARED DIAGNOSTIC OR LABORATORY REAGENTS WHETHER OR NOT ON A\nBACKING, OTHER THAN THOSE OF HEADING 3002 OR 3006; CERTIFIED REFERENCE MATERIALS:\nFOR MEDICAL DIAGNOSIS: OTHER",
+        "hsn_code": "38220019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DIAGNOSTIC OR LABORATORY REAGENTS ON A BACKING, PREPARED DIAGNOSTIC OR\nLABORATORY REAGENTS WHETHER OR NOT ON A BACKING, OTHER THAN THOSE OF HEADING\n3002 OR 3006; CERTIFIED REFERENCE MATERIALS DIAGNOSTIC OR LABORATORY REAGENTS ON A\nBACKING, PREPARED DIAGNOSTIC OR LABORATORY REAGENTS WHETHER OR NOT ON A\nBACKING, OTHER THAN THOSE OF HEADING 3002 OR 3006; CERTIFIED REFERENCE MATERIALS:\nFOR MEDICAL DIAGNOSIS: REAGENTS FOR DIAGNOSING AIDS",
+        "hsn_code": "38220012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DIAGNOSTIC OR LABORATORY REAGENTS ON A BACKING, PREPARED DIAGNOSTIC OR\nLABORATORY REAGENTS WHETHER OR NOT ON A BACKING, OTHER THAN THOSE OF HEADING\n3002 OR 3006; CERTIFIED REFERENCE MATERIALS DIAGNOSTIC OR LABORATORY REAGENTS ON A\nBACKING, PREPARED DIAGNOSTIC OR LABORATORY REAGENTS WHETHER OR NOT ON A\nBACKING, OTHER THAN THOSE OF HEADING 3002 OR 3006; CERTIFIED REFERENCE MATERIALS:\nFOR MEDICAL DIAGNOSIS: PREGNANCY CONFIRMATION REAGENTS",
+        "hsn_code": "38220011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAULIC BRAKE FLUIDS AND OTHER PREPARED LIQUIDS FOR HYDRAULIC TRANSMISSION, NOT CONTAINING OR CONTAINING LESS THAN 70% BY WEIGHT OF PETROLEUM OILS OR OILS OBTAINED FROM BITUMINOUS MINERALS",
+        "hsn_code": "3821",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED CULTURE MEDIA FOR DEVELOPMENT OF MICRO-ORGANISMS",
+        "hsn_code": "38210000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDRAULIC BRAKE FLUIDS AND OTHER PREPARED LIQUIDS FOR HYDRAULIC TRANSMISSION, NOT CONTAINING OR CONTAINING LESS THAN 70% BY WEIGHT OF PETROLEUM OILS OR OILS OBTAINED FROM BITUMINOUS MINERALS",
+        "hsn_code": "3820",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANTI-FREEZING PREPARATIONS AND PREPARED DE-ICING FLUIDS",
+        "hsn_code": "38200000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDRAULIC BRAKE FLUIDS AND OTHER PREPARED LIQUIDS FOR HYDRAULIC TRANSMISSION, NOT CONTAINING OR CONTAINING LESS THAN 70% BY WEIGHT OF PETROLEUM OILS OR OILS OBTAINED FROM BITUMINOUS MINERALS",
+        "hsn_code": "3819",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDRAULIC BRAKE FLUIDS AND OTHER PREPAREDLIQUIDS FOR HYDRAULIC TRANSMISSION, NOT\nCONTAINING OR CONTAINING LESS THAN 70% BY WEIGHT OF PETROLEUM OILS OR OILS\nOBTAINED FROM BITUMINOUS MINERALS OTHER",
+        "hsn_code": "38190090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HYDRAULIC BRAKE FLUIDS AND OTHER PREPAREDLIQUIDS FOR HYDRAULIC TRANSMISSION, NOT\nCONTAINING OR CONTAINING LESS THAN 70% BY WEIGHT OF PETROLEUM OILS OR OILS\nOBTAINED FROM BITUMINOUS MINERALS HYDRAULIC BRAKE FLUIDS",
+        "hsn_code": "38190010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHEMICAL ELEMENTS DOPED FOR USE IN ELECTRONICS, IN THE FORM OF DISCS, WAFERS OR SIMILAR FORMS; CHEMICAL COMPOUNDS DOPED FOR USE IN ELECTRONICS",
+        "hsn_code": "3818",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHEMICAL ELEMENTS DOPED FOR USE IN ELECTRONICS, IN THE FORM OF DISCS, WAFERS OR\nSIMILAR FORMS; CHEMICAL COMPOUNDS DOPED FOR USE IN ELECTRONICS CHEMICAL\nELEMENTS DOPED FOR USE IN ELECTRONICS, IN THE FORM OF DISCS, WAFERS OR SIMILAR\nFORMS; CHEMICAL COMPOUNDS DOPED FOR USE IN ELECTRONICS: OTHER",
+        "hsn_code": "38180090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHEMICAL ELEMENTS DOPED FOR USE IN ELECTRONICS, IN THE FORM OF DISCS, WAFERS OR\nSIMILAR FORMS; CHEMICAL COMPOUNDS DOPED FOR USE IN ELECTRONICS CHEMICAL\nELEMENTS DOPED FOR USE IN ELECTRONICS, IN THE FORM OF DISCS, WAFERS OR SIMILAR\nFORMS; CHEMICAL COMPOUNDS DOPED FOR USE IN ELECTRONICS: UNDEFUSED SILICON\nWAFERS",
+        "hsn_code": "38180010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MIXED Alkylbenzenes AND MIXED Alkylnaphthalenes, OTHER THAN THOSE OF HEADING 2707 OR 2902",
+        "hsn_code": "3817",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MIXED ALKYLBENZENES AND MIXED ALKYLNAPHTHALENES, OTHER THAN THOSE OF HEADING\n2707 OR 2902 MIXED ALKYLBENZENES AND MIXED ALKYLNAPHTHALENES, OTHER THAN THOSE\nOF HEADING 2707 OR 2902: MIXED ALKYLBENZENES: MIXED ALKYLNAPHTHALENES",
+        "hsn_code": "38170020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MIXED ALKYLBENZENES AND MIXED ALKYLNAPHTHALENES, OTHER THAN THOSE OF HEADING\n2707 OR 2902 MIXED ALKYLBENZENES AND MIXED ALKYLNAPHTHALENES, OTHER THAN THOSE\nOF HEADING 2707 OR 2902: MIXED ALKYLBENZENES: OTHER",
+        "hsn_code": "38170019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MIXED ALKYLBENZENES AND MIXED ALKYLNAPHTHALENES, OTHER THAN THOSE OF HEADING\n2707 OR 2902 MIXED ALKYLBENZENES AND MIXED ALKYLNAPHTHALENES, OTHER THAN THOSE\nOF HEADING 2707 OR 2902: MIXED ALKYLBENZENES: LINEAR ALKYLBENZENES",
+        "hsn_code": "38170011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REACTION INITIATORS, REACTION ACCELERATORS AND CATALYTIC PREPARATIONS, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "3816",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REFRACTORY CEMENTS, MORTARS, CONCRETES AND SIMILAR COMPOSITIONS, OTHER THAN\nPRODUCTS OF HEADING 3801",
+        "hsn_code": "38160000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REACTION INITIATORS, REACTION ACCELERATORS AND CATALYTIC PREPARATIONS, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "3815",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REACTION INITIATORS, REACTION ACCELERATORS AND CATALYTIC PREPARATIONS, NOT\nELSEWHERE SPECIFIED OR INCLUDED OTHER",
+        "hsn_code": "38159000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REACTION INITIATORS, REACTION ACCELERATORS AND CATALYTIC PREPARATIONS, NOT\nELSEWHERE SPECIFIED OR INCLUDED SUPPORTED CATALYSTS: OTHER",
+        "hsn_code": "38151900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REACTION INITIATORS, REACTION ACCELERATORS AND CATALYTIC PREPARATIONS, NOT\nELSEWHERE SPECIFIED OR INCLUDED SUPPORTED CATALYSTS: WITH PRECIOUS METAL OR\nPRECIOUS METAL COMPOUNDS AS THE ACTIVE SUBSTANCE: OTHER",
+        "hsn_code": "38151290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REACTION INITIATORS, REACTION ACCELERATORS AND CATALYTIC PREPARATIONS, NOT\nELSEWHERE SPECIFIED OR INCLUDED SUPPORTED CATALYSTS: WITH PRECIOUS METAL OR\nPRECIOUS METAL COMPOUNDS AS THE ACTIVE SUBSTANCE: PLATINUM OR PALLADIUM\nCATALYSTS WITH A BASE OF ACTIVATED CARBON",
+        "hsn_code": "38151210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "REACTION INITIATORS, REACTION ACCELERATORS AND CATALYTIC PREPARATIONS, NOT\nELSEWHERE SPECIFIED OR INCLUDED SUPPORTED CATALYSTS: WITH NICKEL OR NICKEL\nCOMPOUNDS AS THE ACTIVE SUBSTANCE",
+        "hsn_code": "38151100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC COMPOSITE SOLVENTS AND THINNERS, NOT ELSEWHERE SPECIFIED OR INCLUDED; PREPARED PAINT OR VARNISH REMOVERS",
+        "hsn_code": "3814",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC COMPOSITE SOLVENTS AND THINNERS, NOT ELSEWHERE SPECIFIED OR INCLUDED;\nPREPARED PAINT OR VARNISH REMOVERS ORGANIC COMPOSITE SOLVENTS AND THINNERS, NOT\nELSEWHERE SPECIFIED OR INCLUDED; PREPARED PAINT OR VARNISH REMOVERS: PREPARED\nPAINT OR VARNISH REMOVERS",
+        "hsn_code": "38140020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC COMPOSITE SOLVENTS AND THINNERS, NOT ELSEWHERE SPECIFIED OR INCLUDED;\nPREPARED PAINT OR VARNISH REMOVERS ORGANIC COMPOSITE SOLVENTS AND THINNERS, NOT\nELSEWHERE SPECIFIED OR INCLUDED; PREPARED PAINT OR VARNISH REMOVERS: ORGANIC\nCOMPOSITE SOLVENTS AND THINNERS, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "38140010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED RUBBER ACCELERATORS; COMPOUND PLASTICISERS FOR RUBBER OR PLASTICS, NOT ELSEWHERE SPECIFIED OR INCLUDED; ANTI-OXIDISING PREPARATIONS AND OTHER COMPOUND STABILISERS FOR RUBBER OR PLASTICS",
+        "hsn_code": "3813",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHARGES FOR FIRE EXTINGUISHERS; PREPARATIONS AND CHARGED FIRE-EXTINGUISHING\nGRENADES",
+        "hsn_code": "38130000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED RUBBER ACCELERATORS; COMPOUND PLASTICISERS FOR RUBBER OR PLASTICS, NOT ELSEWHERE SPECIFIED OR INCLUDED; ANTI-OXIDISING PREPARATIONS AND OTHER COMPOUND STABILISERS FOR RUBBER OR PLASTICS",
+        "hsn_code": "3812",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED RUBBER ACCELERATORS; COMPOUND PLASTICISERS FOR RUBBER OR PLASTICS, NOT\nELSEWHERE SPECIFIED OR INCLUDED; ANTI-OXIDISING PREPARATIONS AND OTHER COMPOUND\nSTABILISERS FOR RUBBER OR PLASTICS ANTI-OXIDISING PREPARATIONS AND OTHER COMPOUND\nSTABILISERS FOR RUBBER OR PLASTICS: OTHER",
+        "hsn_code": "38123090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED RUBBER ACCELERATORS; COMPOUND PLASTICISERS FOR RUBBER OR PLASTICS, NOT\nELSEWHERE SPECIFIED OR INCLUDED; ANTI-OXIDISING PREPARATIONS AND OTHER COMPOUND\nSTABILISERS FOR RUBBER OR PLASTICS ANTI-OXIDISING PREPARATIONS AND OTHER COMPOUND\nSTABILISERS FOR RUBBER OR PLASTICS: VULCANISING AGENTS FOR RUBBER",
+        "hsn_code": "38123030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED RUBBER ACCELERATORS; COMPOUND PLASTICISERS FOR RUBBER OR PLASTICS, NOT\nELSEWHERE SPECIFIED OR INCLUDED; ANTI-OXIDISING PREPARATIONS AND OTHER COMPOUND\nSTABILISERS FOR RUBBER OR PLASTICS ANTI-OXIDISING PREPARATIONS AND OTHER COMPOUND\nSTABILISERS FOR RUBBER OR PLASTICS: SOFTENERS FOR RUBBER",
+        "hsn_code": "38123020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED RUBBER ACCELERATORS; COMPOUND PLASTICISERS FOR RUBBER OR PLASTICS, NOT\nELSEWHERE SPECIFIED OR INCLUDED; ANTI-OXIDISING PREPARATIONS AND OTHER COMPOUND\nSTABILISERS FOR RUBBER OR PLASTICS ANTI-OXIDISING PREPARATIONS AND OTHER COMPOUND\nSTABILISERS FOR RUBBER OR PLASTICS: ANTI-OXIDANTS FOR RUBBER",
+        "hsn_code": "38123010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED RUBBER ACCELERATORS; COMPOUND PLASTICISERS FOR RUBBER OR PLASTICS, NOT\nELSEWHERE SPECIFIED OR INCLUDED; ANTI-OXIDISING PREPARATIONS AND OTHER COMPOUND\nSTABILISERS FOR RUBBER OR PLASTICS COMPOUND PLASTICISERS FOR RUBBER OR PLASTICS:\nOTHER",
+        "hsn_code": "38122090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED RUBBER ACCELERATORS; COMPOUND PLASTICISERS FOR RUBBER OR PLASTICS, NOT\nELSEWHERE SPECIFIED OR INCLUDED; ANTI-OXIDISING PREPARATIONS AND OTHER COMPOUND\nSTABILISERS FOR RUBBER OR PLASTICS COMPOUND PLASTICISERS FOR RUBBER OR PLASTICS:\nPHTHALATE PLASTICISERS",
+        "hsn_code": "38122010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED RUBBER ACCELERATORS; COMPOUND PLASTICISERS FOR RUBBER OR PLASTICS, NOT\nELSEWHERE SPECIFIED OR INCLUDED; ANTI-OXIDISING PREPARATIONS AND OTHER COMPOUND\nSTABILISERS FOR RUBBER OR PLASTICS PREPARED RUBBER ACCELERATORS",
+        "hsn_code": "38121000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANTI-KNOCK PREPARATIONS, OXIDATION INHIBITORS, GUM INHIBITORS, VISCOSITY IMPROVERS, ANTI-CORROSIVE PREPARATIONS AND OTHER PREPARED ADDITIVES, FOR MINERAL OILS (INCLUDING GASOLINE) OR FOR OTHER LIQUIDS USED FOR THE SAME PURPOSES AS MINERAL OILS",
+        "hsn_code": "3811",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANTI-KNOCK PREPARATIONS, OXIDATION INHIBITORS, GUM INHIBITORS, VISCOSITY IMPROVERS,\nANTI-CORROSIVE PREPARATIONS AND OTHER PREPARED ADDITIVES, FOR MINERAL OILS\n(INCLUDING GASOLINE) OR FOR OTHER LIQUIDS USED FOR THE SAME PURPOSES AS MINERAL\nOILS ADDITIVES FOR LUBRICATING OILS: OTHER",
+        "hsn_code": "38119000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANTI-KNOCK PREPARATIONS, OXIDATION INHIBITORS, GUM INHIBITORS, VISCOSITY IMPROVERS,\nANTI-CORROSIVE PREPARATIONS AND OTHER PREPARED ADDITIVES, FOR MINERAL OILS\n(INCLUDING GASOLINE) OR FOR OTHER LIQUIDS USED FOR THE SAME PURPOSES AS MINERAL\nOILS ADDITIVES FOR LUBRICATING OILS: OTHER",
+        "hsn_code": "38112900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANTI-KNOCK PREPARATIONS, OXIDATION INHIBITORS, GUM INHIBITORS, VISCOSITY IMPROVERS,\nANTI-CORROSIVE PREPARATIONS AND OTHER PREPARED ADDITIVES, FOR MINERAL OILS\n(INCLUDING GASOLINE) OR FOR OTHER LIQUIDS USED FOR THE SAME PURPOSES AS MINERAL\nOILS ADDITIVES FOR LUBRICATING OILS: CONTAINING PETROLEUM OILS OR OILS OBTAINED\nFROM BITUMINOUS MINERALS",
+        "hsn_code": "38112100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANTI-KNOCK PREPARATIONS, OXIDATION INHIBITORS, GUM INHIBITORS, VISCOSITY IMPROVERS,\nANTI-CORROSIVE PREPARATIONS AND OTHER PREPARED ADDITIVES, FOR MINERAL OILS\n(INCLUDING GASOLINE) OR FOR OTHER LIQUIDS USED FOR THE SAME PURPOSES AS MINERAL\nOILS ANTI-KNOCK PREPARATIONS: OTHER",
+        "hsn_code": "38111900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANTI-KNOCK PREPARATIONS, OXIDATION INHIBITORS, GUM INHIBITORS, VISCOSITY IMPROVERS,\nANTI-CORROSIVE PREPARATIONS AND OTHER PREPARED ADDITIVES, FOR MINERAL OILS\n(INCLUDING GASOLINE) OR FOR OTHER LIQUIDS USED FOR THE SAME PURPOSES AS MINERAL\nOILS ANTI-KNOCK PREPARATIONS: BASED ON LEAD COMPOUNDS",
+        "hsn_code": "38111100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PICKLING PREPARATIONS FOR METAL SURFACES; FLUXES AND OTHER AUXILIARY PREPARATIONS FOR SOLDERING, BRAZING OR WELDING;SOLDERING, BRAZING OR WELDING POWDERS AND PASTES CONSISTING OF METAL AND OTHER MATERIALS; PREPARATIONS OF A KIND USED AS CORES OR COATINGS FOR WELDING ELECTRODES OR RODS",
+        "hsn_code": "3810",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PICKLING PREPARATIONS FOR METAL SURFACES; FLUXES AND OTHER AUXILIARY PREPARATIONS\nFOR SOLDERING, BRAZING OR WELDING; SOLDERING, BRAZING OR WELDING POWDERS AND\nPASTES CONSISTING OF METAL AND OTHER MATERIALS; PREPARATIONS OF A KIND USED AS\nCORES OR COATINGS FOR WELDING ELECTRODES OR RODS OTHER OTHER",
+        "hsn_code": "38109090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PICKLING PREPARATIONS FOR METAL SURFACES; FLUXES AND OTHER AUXILIARY PREPARATIONS\nFOR SOLDERING, BRAZING OR WELDING; SOLDERING, BRAZING OR WELDING POWDERS AND\nPASTES CONSISTING OF METAL AND OTHER MATERIALS; PREPARATIONS OF A KIND USED AS\nCORES OR COATINGS FOR WELDING ELECTRODES OR RODS OTHER PREPARATIONS OF A KIND\nUSED AS CORES OR COATINGS FOR WELDING ELECTRODES AND RODS",
+        "hsn_code": "38109010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PICKLING PREPARATIONS FOR METAL SURFACES; FLUXES AND OTHER AUXILIARY PREPARATIONS\nFOR SOLDERING, BRAZING OR WELDING; SOLDERING, BRAZING OR WELDING POWDERS AND\nPASTES CONSISTING OF METAL AND OTHER MATERIALS; PREPARATIONS OF A KIND USED AS\nCORES OR COATINGS FOR WELDING ELECTRODES OR RODS PICKLING PREPARATIONS FOR METAL\nSURFACES;SOLDERING, BRAZING OR WELDING POWDERS AND PASTESCONSISTING OF METAL\nAND OTHER MATERIALS: OTHER",
+        "hsn_code": "38101090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PICKLING PREPARATIONS FOR METAL SURFACES; FLUXES AND OTHER AUXILIARY PREPARATIONS\nFOR SOLDERING, BRAZING OR WELDING; SOLDERING, BRAZING OR WELDING POWDERS AND\nPASTES CONSISTING OF METAL AND OTHER MATERIALS; PREPARATIONS OF A KIND USED AS\nCORES OR COATINGS FOR WELDING ELECTRODES OR RODS PICKLING PREPARATIONS FOR METAL\nSURFACES;SOLDERING, BRAZING OR WELDING POWDERS AND PASTESCONSISTING OF METAL\nAND OTHER MATERIALS: THERMITE PORTION FOR WELDING (ALUMINA THERMIC HEAT\nGENERATORS)",
+        "hsn_code": "38101020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PICKLING PREPARATIONS FOR METAL SURFACES; FLUXES AND OTHER AUXILIARY PREPARATIONS\nFOR SOLDERING, BRAZING OR WELDING; SOLDERING, BRAZING OR WELDING POWDERS AND\nPASTES CONSISTING OF METAL AND OTHER MATERIALS; PREPARATIONS OF A KIND USED AS\nCORES OR COATINGS FOR WELDING ELECTRODES OR RODS PICKLING PREPARATIONS FOR METAL\nSURFACES;SOLDERING, BRAZING OR WELDING POWDERS AND PASTESCONSISTING OF METAL\nAND OTHER MATERIALS: PICKLING PREPARATIONS AND OTHER SOLDERING, BRAZING OR\nWELDING POWDERS OR PASTES",
+        "hsn_code": "38101010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FINISHING AGENTS, DYE CARRIERS TO ACCELERATE THE DYEING OR FIXING OF DYE-STUFFS AND OTHER PRODUCTS AND PREPARATIONS (FOR EXAMPLE, DRESSINGS AND MORDANTS), OF A KIND USED IN THE TEXTILE, PAPER, LEATHER OR LIKE INDUSTRIES, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "3809",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FINISHING AGENTS, DYE CARRIERS TO ACCELERATE THE DYEING OR FIXING OF DYE-STUFFS AND\nOTHER PRODUCTS AND PREPARATIONS (FOR EXAMPLE, DRESSINGS AND MORDANTS), OF A KIND\nUSED IN THE TEXTILE, PAPER, LEATHER OR LIKE INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED OTHER: OF A KIND USED IN THE LEATHER OR LIKE INDUSTRIES : OTHER",
+        "hsn_code": "38099900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FINISHING AGENTS, DYE CARRIERS TO ACCELERATE THE DYEING OR FIXING OF DYE-STUFFS AND\nOTHER PRODUCTS AND PREPARATIONS (FOR EXAMPLE, DRESSINGS AND MORDANTS), OF A KIND\nUSED IN THE TEXTILE, PAPER, LEATHER OR LIKE INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED OTHER: OF A KIND USED IN THE LEATHER OR LIKE INDUSTRIES : OTHER",
+        "hsn_code": "38099390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FINISHING AGENTS, DYE CARRIERS TO ACCELERATE THE DYEING OR FIXING OF DYE-STUFFS AND\nOTHER PRODUCTS AND PREPARATIONS (FOR EXAMPLE, DRESSINGS AND MORDANTS), OF A KIND\nUSED IN THE TEXTILE, PAPER, LEATHER OR LIKE INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED OTHER: OF A KIND USED IN THE LEATHER OR LIKE INDUSTRIES : FATTY OIL OR PULL UP\nOIL",
+        "hsn_code": "38099310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FINISHING AGENTS, DYE CARRIERS TO ACCELERATE THE DYEING OR FIXING OF DYE-STUFFS AND\nOTHER PRODUCTS AND PREPARATIONS (FOR EXAMPLE, DRESSINGS AND MORDANTS), OF A KIND\nUSED IN THE TEXTILE, PAPER, LEATHER OR LIKE INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED OTHER: OF A KIND USED IN THE PAPER OR LIKE INDUSTRIES",
+        "hsn_code": "38099200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FINISHING AGENTS, DYE CARRIERS TO ACCELERATE THE DYEING OR FIXING OF DYE-STUFFS AND\nOTHER PRODUCTS AND PREPARATIONS (FOR EXAMPLE, DRESSINGS AND MORDANTS), OF A KIND\nUSED IN THE TEXTILE, PAPER, LEATHER OR LIKE INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED OTHER: OF A KIND USED IN THE TEXTILE OR LIKE INDUSTRIES: OTHER",
+        "hsn_code": "38099190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FINISHING AGENTS, DYE CARRIERS TO ACCELERATE THE DYEING OR FIXING OF DYE-STUFFS AND\nOTHER PRODUCTS AND PREPARATIONS (FOR EXAMPLE, DRESSINGS AND MORDANTS), OF A KIND\nUSED IN THE TEXTILE, PAPER, LEATHER OR LIKE INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED OTHER: OF A KIND USED IN THE TEXTILE OR LIKE INDUSTRIES: PREPARED TEXTILE\nGLAZINGS, DRESSINGS AND MORDANTS",
+        "hsn_code": "38099180",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FINISHING AGENTS, DYE CARRIERS TO ACCELERATE THE DYEING OR FIXING OF DYE-STUFFS AND\nOTHER PRODUCTS AND PREPARATIONS (FOR EXAMPLE, DRESSINGS AND MORDANTS), OF A KIND\nUSED IN THE TEXTILE, PAPER, LEATHER OR LIKE INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED OTHER: OF A KIND USED IN THE TEXTILE OR LIKE INDUSTRIES: TEXTILE ASSISTANTS\nWATER PROOFING AGENTS",
+        "hsn_code": "38099170",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FINISHING AGENTS, DYE CARRIERS TO ACCELERATE THE DYEING OR FIXING OF DYE-STUFFS AND\nOTHER PRODUCTS AND PREPARATIONS (FOR EXAMPLE, DRESSINGS AND MORDANTS), OF A KIND\nUSED IN THE TEXTILE, PAPER, LEATHER OR LIKE INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED OTHER: OF A KIND USED IN THE TEXTILE OR LIKE INDUSTRIES: TEXTILE\nASSISTANTSTEXTILE PRESERVATIVES",
+        "hsn_code": "38099160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FINISHING AGENTS, DYE CARRIERS TO ACCELERATE THE DYEING OR FIXING OF DYE-STUFFS AND\nOTHER PRODUCTS AND PREPARATIONS (FOR EXAMPLE, DRESSINGS AND MORDANTS), OF A KIND\nUSED IN THE TEXTILE, PAPER, LEATHER OR LIKE INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED OTHER: OF A KIND USED IN THE TEXTILE OR LIKE INDUSTRIES: TEXTILE ASSISTANTS\nHYDRO SULPHITE FORMALDEHYDE COMPOUND (RONGALITE OR FORMUSUL)",
+        "hsn_code": "38099150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FINISHING AGENTS, DYE CARRIERS TO ACCELERATE THE DYEING OR FIXING OF DYE-STUFFS AND\nOTHER PRODUCTS AND PREPARATIONS (FOR EXAMPLE, DRESSINGS AND MORDANTS), OF A KIND\nUSED IN THE TEXTILE, PAPER, LEATHER OR LIKE INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED OTHER: OF A KIND USED IN THE TEXTILE OR LIKE INDUSTRIES: TEXTILE ASSISTANTS\nEMULSIFYING AGENTS",
+        "hsn_code": "38099140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FINISHING AGENTS, DYE CARRIERS TO ACCELERATE THE DYEING OR FIXING OF DYE-STUFFS AND\nOTHER PRODUCTS AND PREPARATIONS (FOR EXAMPLE, DRESSINGS AND MORDANTS), OF A KIND\nUSED IN THE TEXTILE, PAPER, LEATHER OR LIKE INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED OTHER: OF A KIND USED IN THE TEXTILE OR LIKE INDUSTRIES: TEXTILE ASSISTANTS\nDISPERSING AGENTS",
+        "hsn_code": "38099130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FINISHING AGENTS, DYE CARRIERS TO ACCELERATE THE DYEING OR FIXING OF DYE-STUFFS AND\nOTHER PRODUCTS AND PREPARATIONS (FOR EXAMPLE, DRESSINGS AND MORDANTS), OF A KIND\nUSED IN THE TEXTILE, PAPER, LEATHER OR LIKE INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED OTHER: OF A KIND USED IN THE TEXTILE OR LIKE INDUSTRIES: TEXTILE ASSISTANTS\nDESIZING AGENTS",
+        "hsn_code": "38099120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FINISHING AGENTS, DYE CARRIERS TO ACCELERATE THE DYEING OR FIXING OF DYE-STUFFS AND\nOTHER PRODUCTS AND PREPARATIONS (FOR EXAMPLE, DRESSINGS AND MORDANTS), OF A KIND\nUSED IN THE TEXTILE, PAPER, LEATHER OR LIKE INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED OTHER: OF A KIND USED IN THE TEXTILE OR LIKE INDUSTRIES: TEXTILE ASSISTANTS\nMORDANTING AGENTS",
+        "hsn_code": "38099110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FINISHING AGENTS, DYE CARRIERS TO ACCELERATE THE DYEING OR FIXING OF DYE-STUFFS AND\nOTHER PRODUCTS AND PREPARATIONS (FOR EXAMPLE, DRESSINGS AND MORDANTS), OF A KIND\nUSED IN THE TEXTILE, PAPER, LEATHER OR LIKE INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED: WITH A BASIS OF AMYLACEOUS SUBSTANCES",
+        "hsn_code": "38091000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND PLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR PACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE, SULPHUR-TREATED BANDS,WICKS AND CANDLES, AND FLY-PAPERS)",
+        "hsn_code": "3808",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE, SULPHUR\nTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-- OTHER:--- OTHER",
+        "hsn_code": "38089990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE, SULPHUR\nTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-- OTHER:--- PESTICIDES, NOT ELSE\nWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "38089910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE, SULPHUR\nTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)--DISINFECTANTS",
+        "hsn_code": "38089400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)--: FUNGICIDES--- OTHER",
+        "hsn_code": "38089290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)--: FUNGICIDES--- COPPER\nOXYCHLORIDE",
+        "hsn_code": "38089250",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)--: FUNGICIDES--- ZINEB",
+        "hsn_code": "38089240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)--: FUNGICIDES--- THIRAM\n(TETRAMETHYL THIURAM DISULPHIDE)",
+        "hsn_code": "38089230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)--: FUNGICIDES--- SODIUM\nPENTA CHLOROPHENATE(SANTROBRITE)",
+        "hsn_code": "38089220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)--: FUNGICIDES--- MANEB",
+        "hsn_code": "38089210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-OTHER-- INSECTICIDES:---\nOTHER:----OTHER",
+        "hsn_code": "38089199",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-OTHER-- INSECTICIDES:---\nOTHER:----PAPER IMPREGNATED OR COATED WITH INSECTICIDES SUCH AS D.D.T. COATED",
+        "hsn_code": "38089192",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-OTHER-- INSECTICIDES:---\nOTHER:----REPELLANTS FOR INSECTS SUCH AS FLIES, MOSQUITO",
+        "hsn_code": "38089191",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-OTHER-- INSECTICIDES:----\nSYNTHETIC PYRETHRUM",
+        "hsn_code": "38089137",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-OTHER-- INSECTICIDES:----\nALLETHRIN",
+        "hsn_code": "38089136",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-OTHER-- INSECTICIDES:----\nCIPERMETHRIN, TECHNICAL GRADE",
+        "hsn_code": "38089135",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-OTHER-- INSECTICIDES:----\nFENTHION",
+        "hsn_code": "38089134",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-OTHER-- INSECTICIDES:----\nISOPROTURON",
+        "hsn_code": "38089133",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-OTHER-- INSECTICIDES:----\nQUINAL PHOS",
+        "hsn_code": "38089132",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-OTHER-- INSECTICIDES:----\nENDOSULPHAN, TECHNICAL GRADE",
+        "hsn_code": "38089131",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-OTHER-- INSECTICIDES:----\nMELATHION",
+        "hsn_code": "38089124",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-OTHER-- INSECTICIDES:----\nDIMETHOATE, TECHNICAL GRADE",
+        "hsn_code": "38089123",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-OTHER-- INSECTICIDES:----\nMETHYL BROMIDE",
+        "hsn_code": "38089122",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-OTHER-- INSECTICIDES:----\nDIAGINAL",
+        "hsn_code": "38089121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-OTHER-- INSECTICIDES:----\nD.D.V.P.(DIMETHYLE-DICHLORO-VINYL- PHOSPHATE)",
+        "hsn_code": "38089113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-OTHER-- INSECTICIDES:----\nCALCIUM CYANIDE",
+        "hsn_code": "38089112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-OTHER-- INSECTICIDES:----\nALUMINIUM PHOSPHITE (FOR EXAMPLE PHOSTOXIN)",
+        "hsn_code": "38089111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) OTHER : OTHER",
+        "hsn_code": "38089090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) OTHER : PESTICIDES, NOT\nELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "38089010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS)-GOODS SPECIFIED IN SUB\nHEADING NOTE 1",
+        "hsn_code": "38085000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) DISINFECTANTS",
+        "hsn_code": "38084000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) HERBICIDES, ANTI\nSPROUTING PRODUCTS AND PLANT-GROWTH REGULATORS: OTHER",
+        "hsn_code": "38083090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) HERBICIDES, ANTI\nSPROUTING PRODUCTS AND PLANT-GROWTH REGULATORS: WEEDICIDES AND WEED KILLING\nAGENTS",
+        "hsn_code": "38083050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) HERBICIDES, ANTI\nSPROUTING PRODUCTS AND PLANT-GROWTH REGULATORS: PLANTGROWTH REGULATORS",
+        "hsn_code": "38083040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) HERBICIDES, ANTI\nSPROUTING PRODUCTS AND PLANT-GROWTH REGULATORS: GIBBERELLIC ACID",
+        "hsn_code": "38083030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) HERBICIDES, ANTI\nSPROUTING PRODUCTS AND PLANT-GROWTH REGULATORS: 2:4 DICHLOROPHENOZY ACETIC\nACID AND ITS ESTERS",
+        "hsn_code": "38083020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) HERBICIDES, ANTI\nSPROUTING PRODUCTS AND PLANT-GROWTH REGULATORS: CHLOROMETHYL PHENOZY ACETIC\nACID (M.C.P.A.)",
+        "hsn_code": "38083010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) FUNGICIDES: OTHER",
+        "hsn_code": "38082090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) FUNGICIDES: COPPER\nOXYCHLORIDE",
+        "hsn_code": "38082050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) FUNGICIDES: ZINEB",
+        "hsn_code": "38082040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) FUNGICIDES: THIRAM\n(TETRAMETHYL THIURAM DISULPHIDE)",
+        "hsn_code": "38082030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) FUNGICIDES: SODIUM PENTA\nCHLOROPHENATE (SANTOBRITE)",
+        "hsn_code": "38082020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) FUNGICIDES: MANEB",
+        "hsn_code": "38082010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES: OTHER:\nOTHER",
+        "hsn_code": "38081099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES: OTHER: PAPER\nIMPREGNATED OR COATED WITH INSECTICIDE SUCH AS D.D.T. COATED PAPER",
+        "hsn_code": "38081092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES: OTHER:\nREPELLANTS FOR INSECTS SUCH AS FLIES, MOSQUITO",
+        "hsn_code": "38081091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES:\nENDOSULPHAN TECHNICAL, QUINAL PHOS, ISOPROTURON, FENTHION, CIPERMETHRIN\nTECHNICAL, ALLETHRIN, SYNTHETIC PYRETHRUM SYNTHETIC PYRETHRUM",
+        "hsn_code": "38081037",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES:\nENDOSULPHAN TECHNICAL, QUINAL PHOS, ISOPROTURON, FENTHION, CIPERMETHRIN\nTECHNICAL, ALLETHRIN, SYNTHETIC PYRETHRUM ALLETHRIN",
+        "hsn_code": "38081036",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES:\nENDOSULPHAN TECHNICAL, QUINAL PHOS, ISOPROTURON, FENTHION, CIPERMETHRIN\nTECHNICAL, ALLETHRIN, SYNTHETIC PYRETHRUM CIPERMETHRIN, TECHNICAL GRADE",
+        "hsn_code": "38081035",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES:\nENDOSULPHAN TECHNICAL, QUINAL PHOS, ISOPROTURON, FENTHION, CIPERMETHRIN\nTECHNICAL, ALLETHRIN, SYNTHETIC PYRETHRUM FENTHION",
+        "hsn_code": "38081034",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES:\nENDOSULPHAN TECHNICAL, QUINAL PHOS, ISOPROTURON, FENTHION, CIPERMETHRIN\nTECHNICAL, ALLETHRIN, SYNTHETIC PYRETHRUM ISOPROTURON",
+        "hsn_code": "38081033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES:\nENDOSULPHAN TECHNICAL, QUINAL PHOS, ISOPROTURON, FENTHION, CIPERMETHRIN\nTECHNICAL, ALLETHRIN, SYNTHETIC PYRETHRUM QUINAL PHOS",
+        "hsn_code": "38081032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES:\nENDOSULPHAN TECHNICAL, QUINAL PHOS, ISOPROTURON, FENTHION, CIPERMETHRIN\nTECHNICAL, ALLETHRIN, SYNTHETIC PYRETHRUM ENDOSULPHAN, TECHNICAL GRADE",
+        "hsn_code": "38081031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES: DIAGINAL,\nHEPTACHLOR, LINDANE, METHYL BROMIDE, PARATHION METHYL, DIMETHOATE TECHNICAL,\nMALATHION: MALATHION",
+        "hsn_code": "38081027",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES: DIAGINAL,\nHEPTACHLOR, LINDANE, METHYL BROMIDE, PARATHION METHYL, DIMETHOATE TECHNICAL,\nMALATHION: DIMETHOATE, TECHNICAL GRADE",
+        "hsn_code": "38081026",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES: DIAGINAL,\nHEPTACHLOR, LINDANE, METHYL BROMIDE, PARATHION METHYL, DIMETHOATE TECHNICAL,\nMALATHION: PARATHION, METHYL",
+        "hsn_code": "38081025",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES: DIAGINAL,\nHEPTACHLOR, LINDANE, METHYL BROMIDE, PARATHION METHYL, DIMETHOATE TECHNICAL,\nMALATHION: METHYL BROMIDE",
+        "hsn_code": "38081024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES: DIAGINAL,\nHEPTACHLOR, LINDANE, METHYL BROMIDE, PARATHION METHYL, DIMETHOATE TECHNICAL,\nMALATHION: LINDANE",
+        "hsn_code": "38081023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES: DIAGINAL,\nHEPTACHLOR, LINDANE, METHYL BROMIDE, PARATHION METHYL, DIMETHOATE TECHNICAL,\nMALATHION: HEPTACHLOR",
+        "hsn_code": "38081022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES: DIAGINAL,\nHEPTACHLOR, LINDANE, METHYL BROMIDE, PARATHION METHYL, DIMETHOATE TECHNICAL,\nMALATHION: DIAGINAL",
+        "hsn_code": "38081021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES: ALDRIN,\nALUMINIUM PHOSPHITE, CALCIUM CYANIDE, CHLORDANE, CHLORO BENZILATE, DDVP,DDT:\nD.D.T. (EXCLUDING D.D.T. OF HEADING 2903 62)",
+        "hsn_code": "38081017",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES: ALDRIN,\nALUMINIUM PHOSPHITE, CALCIUM CYANIDE, CHLORDANE, CHLORO BENZILATE, DDVP,DDT:\nD.D.V.P (DIMETHYL DICHLORO VINYL PHOSPHATE)",
+        "hsn_code": "38081016",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES: ALDRIN,\nALUMINIUM PHOSPHITE, CALCIUM CYANIDE, CHLORDANE, CHLORO BENZILATE, DDVP,DDT:\nCHLORO BENZILATE",
+        "hsn_code": "38081015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES: ALDRIN,\nALUMINIUM PHOSPHITE, CALCIUM CYANIDE, CHLORDANE, CHLORO BENZILATE, DDVP,DDT:\nCHLORDANE",
+        "hsn_code": "38081014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES: ALDRIN,\nALUMINIUM PHOSPHITE, CALCIUM CYANIDE, CHLORDANE, CHLORO BENZILATE, DDVP,DDT:\nCALCIUM CYANIDE",
+        "hsn_code": "38081013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES: ALDRIN,\nALUMINIUM PHOSPHITE, CALCIUM CYANIDE, CHLORDANE, CHLORO BENZILATE, DDVP,DDT:\nALUMINIUM PHOSPHITE (FOR EXAMPLE PHOSTOXIN)",
+        "hsn_code": "38081012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "INSECTICIDES, RODENTICIDES, FUNGICIDES, HERBICIDES, ANTI-SPROUTING PRODUCTS AND\nPLANT-GROWTH REGULATORS, DISINFECTANTS AND SIMILAR PRODUCTS, PUT UP IN FORMS OR\nPACKINGS FOR RETAIL SALE OR AS PREPARATIONS OR ARTICLES (FOR EXAMPLE,\nSULPHURTREATED BANDS, WICKS AND CANDLES, AND FLY-PAPERS) INSECTICIDES: ALDRIN,\nALUMINIUM PHOSPHITE, CALCIUM CYANIDE, CHLORDANE, CHLORO BENZILATE, DDVP,DDT:\nALDRIN",
+        "hsn_code": "38081011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD TAR; WOOD TAR OILS; WOOD CREOSOTE; WOOD NAPHTHA; VEGETABLE PITCH; BREWERS’ PITCH AND SIMILAR PREPARATIONS BASED ON ROSIN, RESIN ACIDS OR ON VEGETABLE PITCH",
+        "hsn_code": "3807",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD TAR; WOOD TAR OILS; WOOD CREOSOTE; WOOD NAPHTHA; VEGETABLE PITCH;\nBREWERS PITCH AND SIMILAR PREPARATIONS BASED ON ROSIN, RESIN ACIDS OR ON VEGETABLE\nPITCH WOOD TAR; WOOD TAR OILS; WOOD CREOSOTE;WOOD NAPHTHA; VEGETABLE PITCH;\nBREWERS PITCH AND SIMILAR PREPARATIONS BASED ON ROSIN, RESIN ACIDS OR ON VEGETABLE\nPITCH: VEGETABLE PITCH, BREWERS PITCH AND SIMILAR PREPARATIONS BASED ON ROSIN,\nRESIN ACIDS OR VEGETABLE PITCH",
+        "hsn_code": "38070030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD TAR; WOOD TAR OILS; WOOD CREOSOTE; WOOD NAPHTHA; VEGETABLE PITCH;\nBREWERS PITCH AND SIMILAR PREPARATIONS BASED ON ROSIN, RESIN ACIDS OR ON VEGETABLE\nPITCH WOOD TAR; WOOD TAR OILS; WOOD CREOSOTE;WOOD NAPHTHA; VEGETABLE PITCH;\nBREWERS PITCH AND SIMILAR PREPARATIONS BASED ON ROSIN, RESIN ACIDS OR ON VEGETABLE\nPITCH: WOOD TAR OILS, WOOD CREOSOTE, WOOD NAPHTHA",
+        "hsn_code": "38070020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD TAR; WOOD TAR OILS; WOOD CREOSOTE; WOOD NAPHTHA; VEGETABLE PITCH;\nBREWERS PITCH AND SIMILAR PREPARATIONS BASED ON ROSIN, RESIN ACIDS OR ON VEGETABLE\nPITCH WOOD TAR; WOOD TAR OILS; WOOD CREOSOTE;WOOD NAPHTHA; VEGETABLE PITCH;\nBREWERS PITCH AND SIMILAR PREPARATIONS BASED ON ROSIN, RESIN ACIDS OR ON VEGETABLE\nPITCH: WOOD TAR",
+        "hsn_code": "38070010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ROSIN AND RESIN ACIDS, AND DERIVATIVES THEREOF; ROSIN SPIRIT AND ROSIN OILS; RUN GUMS",
+        "hsn_code": "3806",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ROSIN AND RESIN ACIDS, AND DERIVATIVES THEREOF; ROSIN SPIRIT AND ROSIN OILS; RUN\nGUMS OTHER: OTHER",
+        "hsn_code": "38069090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ROSIN AND RESIN ACIDS, AND DERIVATIVES THEREOF; ROSIN SPIRIT AND ROSIN OILS; RUN\nGUMS OTHER: RUN GUMS",
+        "hsn_code": "38069010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ROSIN AND RESIN ACIDS, AND DERIVATIVES THEREOF; ROSIN SPIRIT AND ROSIN OILS; RUN\nGUMS ESTER GUMS",
+        "hsn_code": "38063000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ROSIN AND RESIN ACIDS, AND DERIVATIVES THEREOF; ROSIN SPIRIT AND ROSIN OILS; RUN\nGUMS SALTS OF ROSIN, OF RESIN ACIDS OR OF DERIVATIVES OF ROSIN OR RESIN ACIDS, OTHER\nTHAN SALTS OF ROSIN ADDUCTS",
+        "hsn_code": "38062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ROSIN AND RESIN ACIDS, AND DERIVATIVES THEREOF; ROSIN SPIRIT AND ROSIN OILS; RUN\nGUMS ROSIN AND RESIN ACIDS: OTHER",
+        "hsn_code": "38061090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ROSIN AND RESIN ACIDS, AND DERIVATIVES THEREOF; ROSIN SPIRIT AND ROSIN OILS; RUN\nGUMS ROSIN AND RESIN ACIDS: GUM ROSIN",
+        "hsn_code": "38061010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GUM, WOOD OR SULPHATE TURPENTINE AND OTHER TERPENIC OILS PRODUCED BY THE DISTILLATION OR OTHER TREATMENT OF CONIFEROUS WOODS; CRUDE DIPENTENE; SULPHITE TURPENTINE AND OTHER CRUDE PARACYMENE; PINE OIL CONTAINING ALPHA-TERPINEOL AS THE MAIN CONSTITUENT",
+        "hsn_code": "3805",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GUM, WOOD OR SULPHATE TURPENTINE AND OTHER TERPENIC OILS PRODUCED BY THE\nDISTILLATION OR OTHER TREATMENT OF CONIFEROUS WOODS; CRUDE DIPENTENE; SULPHITE\nTURPENTINE AND OTHER CRUDE PARA-CYMENE; PINE OIL CONTAINING ALPHA-TERPINEOL AS\nTHE MAIN CONSTITUENT OTHER: OTHER",
+        "hsn_code": "38059090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GUM, WOOD OR SULPHATE TURPENTINE AND OTHER TERPENIC OILS PRODUCED BY THE\nDISTILLATION OR OTHER TREATMENT OF CONIFEROUS WOODS; CRUDE DIPENTENE; SULPHITE\nTURPENTINE AND OTHER CRUDE PARA-CYMENE; PINE OIL CONTAINING ALPHA-TERPINEOL AS\nTHE MAIN CONSTITUENT OTHER: SULPHITE TURPENTINE",
+        "hsn_code": "38059030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GUM, WOOD OR SULPHATE TURPENTINE AND OTHER TERPENIC OILS PRODUCED BY THE\nDISTILLATION OR OTHER TREATMENT OF CONIFEROUS WOODS; CRUDE DIPENTENE; SULPHITE\nTURPENTINE AND OTHER CRUDE PARA-CYMENE; PINE OIL CONTAINING ALPHA-TERPINEOL AS\nTHE MAIN CONSTITUENT OTHER: CRUDE DIPENTENE",
+        "hsn_code": "38059020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GUM, WOOD OR SULPHATE TURPENTINE AND OTHER TERPENIC OILS PRODUCED BY THE\nDISTILLATION OR OTHER TREATMENT OF CONIFEROUS WOODS; CRUDE DIPENTENE; SULPHITE\nTURPENTINE AND OTHER CRUDE PARA-CYMENE; PINE OIL CONTAINING ALPHA-TERPINEOL AS\nTHE MAIN CONSTITUENT OTHER: TERPENIC OILS PRODUCED BY THE DISTILLATION OR OTHER\nTREATMENT OF CONIFEROUS WOODS",
+        "hsn_code": "38059010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GUM, WOOD OR SULPHATE TURPENTINE AND OTHER TERPENIC OILS PRODUCED BY THE\nDISTILLATION OR OTHER TREATMENT OF CONIFEROUS WOODS; CRUDE DIPENTENE; SULPHITE\nTURPENTINE AND OTHER CRUDE PARA-CYMENE; PINE OIL CONTAINING ALPHA-TERPINEOL AS\nTHE MAIN CONSTITUENT PINE OIL",
+        "hsn_code": "38052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GUM, WOOD OR SULPHATE TURPENTINE AND OTHER TERPENIC OILS PRODUCED BY THE\nDISTILLATION OR OTHER TREATMENT OF CONIFEROUS WOODS; CRUDE DIPENTENE; SULPHITE\nTURPENTINE AND OTHER CRUDE PARA-CYMENE; PINE OIL CONTAINING ALPHA-TERPINEOL AS\nTHE MAIN CONSTITUENT GUM, WOOD OR SULPHATE TURPENTINE OILS: SULPHATE TURPENTINE\nOIL",
+        "hsn_code": "38051030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GUM, WOOD OR SULPHATE TURPENTINE AND OTHER TERPENIC OILS PRODUCED BY THE\nDISTILLATION OR OTHER TREATMENT OF CONIFEROUS WOODS; CRUDE DIPENTENE; SULPHITE\nTURPENTINE AND OTHER CRUDE PARA-CYMENE; PINE OIL CONTAINING ALPHA-TERPINEOL AS\nTHE MAIN CONSTITUENT GUM, WOOD OR SULPHATE TURPENTINE OILS: GUM TURPENTINE OIL",
+        "hsn_code": "38051020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GUM, WOOD OR SULPHATE TURPENTINE AND OTHER TERPENIC OILS PRODUCED BY THE\nDISTILLATION OR OTHER TREATMENT OF CONIFEROUS WOODS; CRUDE DIPENTENE; SULPHITE\nTURPENTINE AND OTHER CRUDE PARA-CYMENE; PINE OIL CONTAINING ALPHA-TERPINEOL AS\nTHE MAIN CONSTITUENT GUM, WOOD OR SULPHATE TURPENTINE OILS: WOOD TURPENTINE OIL\nAND SPIRIT OF TURPENTINE",
+        "hsn_code": "38051010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RESIDUAL LYES FOR THE MANUFACTURE OF WOOD PULP, WHETHER OR NOT CONCENTRATED, DESUGARED OR CHEMICALLY TREATED, INCLUDING LIGNIN SULPHONATES, BUT EXCLUDING TALL OIL OF HEADING 3803",
+        "hsn_code": "3804",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RESIDUAL LYES FOR THE MANUFACTURE OF WOOD PULP, WHETHER OR NOT CONCENTRATED,\nDESUGARED OR CHEMICALLY TREATED, INCLUDING LIGNIN SULPHONATES, BUT EXCLUDING TALL\nOIL OF HEADING 3803 RESIDUAL LYES FOR THE MANUFACTURE OF WOOD PULP, WHETHER OR\nNOT CONCENTRATED, DESUGARED OR CHEMICALLY TREATED, INCLUDING LIGNIN\nSULPHONATES, BUT EXCLUDING TALL OIL OF HEADING 3803: OTHER",
+        "hsn_code": "38040090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RESIDUAL LYES FOR THE MANUFACTURE OF WOOD PULP, WHETHER OR NOT CONCENTRATED,\nDESUGARED OR CHEMICALLY TREATED, INCLUDING LIGNIN SULPHONATES, BUT EXCLUDING TALL\nOIL OF HEADING 3803 RESIDUAL LYES FOR THE MANUFACTURE OF WOOD PULP, WHETHER OR\nNOT CONCENTRATED, DESUGARED OR CHEMICALLY TREATED, INCLUDING LIGNIN\nSULPHONATES, BUT EXCLUDING TALL OIL OF HEADING 3803: CONCENTRATED SULPHATE LYE",
+        "hsn_code": "38040020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "RESIDUAL LYES FOR THE MANUFACTURE OF WOOD PULP, WHETHER OR NOT CONCENTRATED,\nDESUGARED OR CHEMICALLY TREATED, INCLUDING LIGNIN SULPHONATES, BUT EXCLUDING TALL\nOIL OF HEADING 3803 RESIDUAL LYES FOR THE MANUFACTURE OF WOOD PULP, WHETHER OR\nNOT CONCENTRATED, DESUGARED OR CHEMICALLY TREATED, INCLUDING LIGNIN\nSULPHONATES, BUT EXCLUDING TALL OIL OF HEADING 3803: LIGNIN SULPHONATES",
+        "hsn_code": "38040010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ACTIVATED CARBON; ACTIVATED NATURAL MINERAL PRODUCTS; ANIMAL BLACK, INCLUDING SPENT ANIMAL BLACK",
+        "hsn_code": "3802",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ACTIVATED CARBON; ACTIVATED NATURAL MINERAL PRODUCTS; ANIMAL BLACK, INCLUDING\nSPENT ANIMAL BLACK OTHER: ANIMAL BLACK (FOR EXAMPLE BONE BLACK, IVORY BLACK),\nINCLUDING SPENT ANIMAL BLACK",
+        "hsn_code": "38029020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ACTIVATED CARBON; ACTIVATED NATURAL MINERAL PRODUCTS; ANIMAL BLACK, INCLUDING\nSPENT ANIMAL BLACK OTHER: ACTIVATED NATURAL MINERAL PRODUCTS: OTHER",
+        "hsn_code": "38029019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ACTIVATED CARBON; ACTIVATED NATURAL MINERAL PRODUCTS; ANIMAL BLACK, INCLUDING\nSPENT ANIMAL BLACK OTHER: ACTIVATED NATURAL MINERAL PRODUCTS: ACTIVATED BAUXITE",
+        "hsn_code": "38029012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ACTIVATED CARBON; ACTIVATED NATURAL MINERAL PRODUCTS; ANIMAL BLACK, INCLUDING\nSPENT ANIMAL BLACK OTHER: ACTIVATED NATURAL MINERAL PRODUCTS: ACTIVATED ALUMINA",
+        "hsn_code": "38029011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ACTIVATED CARBON; ACTIVATED NATURAL MINERAL PRODUCTS; ANIMAL BLACK, INCLUDING\nSPENT ANIMAL BLACK ACTIVATED CARBON",
+        "hsn_code": "38021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL GRAPHITE; COLLOIDAL OR SEMI-COLLOIDAL GRAPHITE;PREPARATIONS BASED ON GRAPHITE OR OTHER CARBON IN THE FORM OF PASTES, BLOCKS, PLATES OR OTHER SEMI-MANUFACTURES",
+        "hsn_code": "3801",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL GRAPHITE; COLLOIDAL OR SEMI-COLLOIDAL GRAPHITE; PREPARATIONS BASED ON\nGRAPHITE OR OTHER CARBON IN THE FORM OF PASTES, BLOCKS, PLATES OR OTHER SEMI\nMANUFACTURES OTHER",
+        "hsn_code": "38019000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL GRAPHITE; COLLOIDAL OR SEMI-COLLOIDAL GRAPHITE; PREPARATIONS BASED ON\nGRAPHITE OR OTHER CARBON IN THE FORM OF PASTES, BLOCKS, PLATES OR OTHER SEMI\nMANUFACTURES CARBONACEOUS PASTES FOR ELECTRODES AND SIMILAR PASTES FOR FURNACE\nLININGS",
+        "hsn_code": "38013000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL GRAPHITE; COLLOIDAL OR SEMI-COLLOIDAL GRAPHITE; PREPARATIONS BASED ON\nGRAPHITE OR OTHER CARBON IN THE FORM OF PASTES, BLOCKS, PLATES OR OTHER SEMI\nMANUFACTURES COLLOIDAL OR SEMICOLLOIDAL GRAPHITE",
+        "hsn_code": "38012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL GRAPHITE; COLLOIDAL OR SEMI-COLLOIDAL GRAPHITE; PREPARATIONS BASED ON\nGRAPHITE OR OTHER CARBON IN THE FORM OF PASTES, BLOCKS, PLATES OR OTHER SEMI\nMANUFACTURES ARTIFICIAL GRAPHITE",
+        "hsn_code": "38011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHEMICAL PREPARATIONS FOR PHOTOGRAPHIC USES (OTHER THAN VARNISHES, GLUES, ADHESIVES AND SIMILAR PREPARATIONS); UNMIXED PRODUCTS FOR PHOTOGRAPHIC USES, PUT UP IN MEASURED PORTIONS OR PUT UP FOR RETAIL SALE IN A FORM READY FOR USE",
+        "hsn_code": "3707",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHEMICAL PREPARATIONS FOR PHOTOGRAPHIC USES (OTHER THAN VARNISHES, GLUES,\nADHESIVES AND SIMILAR PREPARATIONS); UNMIXED PRODUCTS FOR PHOTOGRAPHIC USES, PUT\nUP IN MEASURED PORTIONS OR PUT UP FOR RETAIL SALE IN A FORM READY FOR USE OTHER:\nOTHER",
+        "hsn_code": "37079090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHEMICAL PREPARATIONS FOR PHOTOGRAPHIC USES (OTHER THAN VARNISHES, GLUES,\nADHESIVES AND SIMILAR PREPARATIONS); UNMIXED PRODUCTS FOR PHOTOGRAPHIC USES, PUT\nUP IN MEASURED PORTIONS OR PUT UP FOR RETAIL SALE IN A FORM READY FOR USE OTHER:\nCHEMICAL PRODUCTS MIXED OR COMPOUNDED FOR PHOTOGRAPHIC USES (FOR EXAMPLE,\nDEVELOPERS AND FIXERS), WHETHER OR NOT IN BULK",
+        "hsn_code": "37079010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHEMICAL PREPARATIONS FOR PHOTOGRAPHIC USES (OTHER THAN VARNISHES, GLUES,\nADHESIVES AND SIMILAR PREPARATIONS); UNMIXED PRODUCTS FOR PHOTOGRAPHIC USES, PUT\nUP IN MEASURED PORTIONS OR PUT UP FOR RETAIL SALE IN A FORM READY FOR USE\nSENSITIZING EMULSIONS",
+        "hsn_code": "37071000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING SOUND TRACK OR CONSISTING ONLY OF SOUND TRACK",
+        "hsn_code": "3706",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: OTHER: OTHER",
+        "hsn_code": "37069099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: OTHER: MASTER POSITIVES,\nEXPOSED NEGATIVES, DUPES AND RUSH PRINTS AS ARE NOT CLEARED FOR PUBLIC EXHIBITIONS",
+        "hsn_code": "37069092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: OTHER: AUDIO VISUAL NEWS OR\nAUDIO VISUAL VIEWS MATERIALS INCLUDING NEWS CLIPPINGS",
+        "hsn_code": "37069091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: SHORT FILM NOT ELSEWHERE\nSPECIFIED",
+        "hsn_code": "37069070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: EDUCATIONAL SHOTS, AND\nFILMS: OTHER",
+        "hsn_code": "37069069",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: EDUCATIONAL SHOTS, AND\nFILMS: CERTIFIED AS PREDOMINANTLY EDUCATIONAL, BY CENTRAL BOARD OF FILM\nCERTIFICATION, OF WIDTH BELOW 30MM",
+        "hsn_code": "37069064",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: EDUCATIONAL SHOTS, AND\nFILMS: TEACHING AIDS INCLUDING FILM STRIPS OF EDUCATIONAL NATURE",
+        "hsn_code": "37069063",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: EDUCATIONAL SHOTS, AND\nFILMS: PATCH PRINTS, INCLUDING LOGOS INTENDED EXCLUSIVELY FOR EDUCATIONAL PURPOSES",
+        "hsn_code": "37069062",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: EDUCATIONAL SHOTS, AND\nFILMS: CERTIFIED AS PREDOMINANTLY EDUCATIONAL BY THE CENTRAL BOARD OF FILM\nCERTIFICATION",
+        "hsn_code": "37069061",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: OTHER CHILDRENS FILM: OTHER",
+        "hsn_code": "37069059",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: OTHER CHILDRENS FILM:\nCHILDRENS FILM CERTIFIED BY THE CENTRAL BOARD OF FILMS CERTIFICATION TO BE CHILDRENS\nFILM",
+        "hsn_code": "37069052",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: OTHER CHILDRENS FILM: PATCH\nPRINTS, INCLUDING LOGOS INTENDED EXCLUSIVELY FOR THE ENTERTAINMENT OF CHILDREN",
+        "hsn_code": "37069051",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: ADVERTISEMENT SHOTS AND\nFILMS: MADE WHOLLY OR PARTLY IN COLOUR",
+        "hsn_code": "37069042",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: ADVERTISEMENT SHOTS AND\nFILMS: MADE WHOLLY IN BLACK AND WHITE",
+        "hsn_code": "37069041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: NEWS REELS AND CLIPPINGS",
+        "hsn_code": "37069030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: DOCUMENTARY SHOTS, AND\nFILMS CERTIFIED AS SUCH BY THE CENTRAL BOARD OF FILM CERTIFICATION",
+        "hsn_code": "37069020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: FEATURE FILMS: CHILDRENS\nFILMS CERTIFIED BY THE CENTRAL BOARD OF FILM CERTIFICATION TO BE CHILDRENS FILM",
+        "hsn_code": "37069015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: FEATURE FILMS: MADE WHOLLY\nOR PARTLY IN COLOUR AND OF A LENGTH EXCEEDING 4,000 M",
+        "hsn_code": "37069014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: FEATURE FILMS: MADE WHOLLY\nOR PARTLY IN COLOUR AND OF A LENGTH NOT EXCEEDING 4,000 M",
+        "hsn_code": "37069013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: FEATURE FILMS: MADE WHOLLY\nIN BLACK AND WHITE AND OF A LENGTH EXCEEDING 4,000 M",
+        "hsn_code": "37069012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OTHER: FEATURE FILMS: MADE WHOLLY\nIN BLACK AND WHITE AND OF A LENGTH NOT EXCEEDING 4,000 M",
+        "hsn_code": "37069011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE:\nOTHER: OTHER",
+        "hsn_code": "37061099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE:\nOTHER: MASTER POSITIVES, EXPOSED NEGATIVES, DUPES AND RUSH PRINTS AS ARE NOT\nCLEARED FOR PUBLIC EXHIBITIONS",
+        "hsn_code": "37061092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE:\nOTHER: AUDIO VISUAL NEWS OR AUDIO VISUAL VIEWS MATERIALS INCLUDING NEWS CLIPPINGS",
+        "hsn_code": "37061091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE:\nSHORT FILMS NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "37061070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE:\nEDUCATIONAL SHORTS, AND FILMS: OTHER",
+        "hsn_code": "37061069",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE:\nEDUCATIONAL SHORTS, AND FILMS: TEACHING AIDS INCLUDING FILM STRIPS OF EDUCATIONAL\nNATURE",
+        "hsn_code": "37061063",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE:\nEDUCATIONAL SHORTS, AND FILMS: PATCH PRINTS, INCLUDING LOGOS INTENDED EXCLUSIVELY\nFOR EDUCATIONAL PURPOSES",
+        "hsn_code": "37061062",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE:\nEDUCATIONAL SHORTS, AND FILMS: CERTIFIED AS PREDOMINANTLY EDUCATIONAL BY THE\nCENTRAL BOARD OF FILM CERTIFICATION",
+        "hsn_code": "37061061",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE:\nOTHER CHILDRENS FILM: OTHER",
+        "hsn_code": "37061059",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE:\nOTHER CHILDRENS FILM: CHILDRENS FILM CERTIFIED BY THE CENTRAL BOARD OF FILMS\nCERTIFICATION TO BE CHILDRENS FILM",
+        "hsn_code": "37061052",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE:\nOTHER CHILDRENS FILM: PATCH PRINTS, INCLUDING LOGOS INTENDED EXCLUSIVELY FOR THE\nENTERTAINMENT OF CHILDREN",
+        "hsn_code": "37061051",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE:\nADVERTISEMENT SHORTS AND FILMS: MADE WHOLLY OR PARTLY IN COLOUR",
+        "hsn_code": "37061042",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE:\nADVERTISEMENT SHORTS AND FILMS: MADE WHOLLY IN BLACK AND WHITE",
+        "hsn_code": "37061041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE: NEWS\nREELS AND CLIPPINGS",
+        "hsn_code": "37061030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE:\nDOCUMENTARY SHORTS, AND FILMS CERTIFIED AS SUCH BY THE CENTRAL BOARD OF FILM\nCERTIFICATION",
+        "hsn_code": "37061020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE:\nFEATURE FILMS: CHILDRENS FILMS CERTIFIED BY THE CENTRAL BOARD OF FILM CERTIFICATION\nTO BE CHILDRENS FILM",
+        "hsn_code": "37061015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE:\nFEATURE FILMS: MADE WHOLLY OR PARTLY IN COLOUR AND OF A LENGTH EXCEEDING 4,000 M",
+        "hsn_code": "37061014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE:\nFEATURE FILMS: MADE WHOLLY OR PARTLY IN COLOUR AND OF A LENGTH NOT EXCEEDING\n4,000 M",
+        "hsn_code": "37061013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE:\nFEATURE FILMS: MADE WHOLLY IN BLACK AND WHITE AND OF A LENGTH EXCEEDING 4,000 M",
+        "hsn_code": "37061012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CINEMATOGRAPHIC FILM, EXPOSED AND DEVELOPED, WHETHER OR NOT INCORPORATING\nSOUND TRACK OR CONSISTING ONLY OF SOUND TRACK OF A WIDTH OF 35 MM OR MORE:\nFEATURE FILMS: MADE WHOLLY IN BLACK AND WHITE AND OF A LENGTH NOT EXCEEDING 4,000\nM",
+        "hsn_code": "37061011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PLATES AND FILM, EXPOSED AND DEVELOPED,OTHER THAN CINEMATOGRAPHIC FILM",
+        "hsn_code": "3705",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PAPER, PAPERBOARD AND TEXTILES SENSITISED, UNEXPOSED OTHER: OTHER",
+        "hsn_code": "37059090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PAPER, PAPERBOARD AND TEXTILES SENSITISED, UNEXPOSED OTHER:\nMICROFICHES",
+        "hsn_code": "37059010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PAPER, PAPERBOARD AND TEXTILES SENSITISED, UNEXPOSED MICROFILMS",
+        "hsn_code": "37052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PAPER, PAPERBOARD AND TEXTILES SENSITISED, UNEXPOSED FOR OFFSET\nREPRODUCTION",
+        "hsn_code": "37051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PLATES, FILM, PAPER, PAPER BOARD AND TEXTILES, EXPOSED BUT NOT DEVELOPED",
+        "hsn_code": "3704",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PAPER, PAPERBOARD AND TEXTILES SENSITISED, UNEXPOSED PHOTOGRAPHIC\nPLATES, FILM, PAPER, PAPER BOARD AND TEXTILES, EXPOSED BUT NOT DEVELOPED: OTHER",
+        "hsn_code": "37040090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PAPER, PAPERBOARD AND TEXTILES SENSITISED, UNEXPOSED PHOTOGRAPHIC\nPLATES, FILM, PAPER, PAPER BOARD AND TEXTILES, EXPOSED BUT NOT DEVELOPED: SENSITISED\nTEXTILES",
+        "hsn_code": "37040030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PAPER, PAPERBOARD AND TEXTILES SENSITISED, UNEXPOSED PHOTOGRAPHIC\nPLATES, FILM, PAPER, PAPER BOARD AND TEXTILES, EXPOSED BUT NOT DEVELOPED:\nCINEMATOGRAPHIC PLATES AND FILM",
+        "hsn_code": "37040020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PAPER, PAPERBOARD AND TEXTILES SENSITISED, UNEXPOSED PHOTOGRAPHIC\nPLATES, FILM, PAPER, PAPER BOARD AND TEXTILES, EXPOSED BUT NOT DEVELOPED:\nPHOTOGRAPHIC PAPER, OR PAPERBOARD",
+        "hsn_code": "37040010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PAPER, PAPER BOARD AND TEXTILES SENSITISED, UNEXPOSED",
+        "hsn_code": "3703",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PAPER, PAPERBOARD AND TEXTILES SENSITISED, UNEXPOSED OTHER: TEXTILES",
+        "hsn_code": "37039020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PAPER, PAPERBOARD AND TEXTILES SENSITISED, UNEXPOSED OTHER:\nPHOTOGRAPHIC PAPER OR PAPERBOARD",
+        "hsn_code": "37039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PAPER, PAPERBOARD AND TEXTILES SENSITISED, UNEXPOSED OTHER, FOR\nCOLOUR PHOTOGRAPHY (POLYCHROME): TEXTILES",
+        "hsn_code": "37032020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PAPER, PAPERBOARD AND TEXTILES SENSITISED, UNEXPOSED OTHER, FOR\nCOLOUR PHOTOGRAPHY (POLYCHROME): PHOTOGRAPHIC PAPER OR PAPERBOARD",
+        "hsn_code": "37032010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PAPER, PAPERBOARD AND TEXTILES SENSITISED, UNEXPOSED IN ROLLS OF A\nWIDTH EXCEEDING 610 MM: TEXTILES",
+        "hsn_code": "37031020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PAPER, PAPERBOARD AND TEXTILES SENSITISED, UNEXPOSED IN ROLLS OF A\nWIDTH EXCEEDING 610 MM: PHOTOGRAPHIC PAPER OR PAPERBOARD",
+        "hsn_code": "37031010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN PAPER, PAPERBOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED",
+        "hsn_code": "3702",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPERBOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER--OF A WIDTH EXCEEDING 35 MM---OTHER",
+        "hsn_code": "37029890",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPERBOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER--OF A WIDTH EXCEEDING 35 MM---CINEMATOGRAPHIC FILM",
+        "hsn_code": "37029810",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPERBOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER--OF A WIDTH NOT EXCEEDING 35 MM AND OF A LENGTH EXCEEDING 30 M ---\nCINEMATOGRAPHIC FILM----OTHER",
+        "hsn_code": "37029719",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPERBOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER--OF A WIDTH NOT EXCEEDING 35 MM AND OF A LENGTH EXCEEDING 30 M ---\nCINEMATOGRAPHIC FILM----NOT EXCEEDING 16 MM",
+        "hsn_code": "37029711",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPERBOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER--OF A WIDTH NOT EXCEEDING 35 MM AND OF A LENGTH NOT EXCEEDING 30M---\nCINEMATOGRAPHIC FILM----OTHER",
+        "hsn_code": "37029619",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPERBOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER--OF A WIDTH NOT EXCEEDING 35 MM AND OF A LENGTH NOT EXCEEDING 30M---\nCINEMATOGRAPHIC FILM----NOT EXCEEDING 16 MM",
+        "hsn_code": "37029611",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, FOR COLOUR PHOTOGRAPHY (POLYCHROME): OF A WIDTH EXCEEDING 35 MM:\nOTHER",
+        "hsn_code": "37025690",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, FOR COLOUR PHOTOGRAPHY (POLYCHROME): OF A WIDTH EXCEEDING 35 MM:\nOTHER CINEMATOGRAPHIC FILM",
+        "hsn_code": "37025620",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, FOR COLOUR PHOTOGRAPHY (POLYCHROME): OF A WIDTH EXCEEDING 35 MM:\nFINISHED ROLLS OF CINEMATOGRAPHIC POSITIVE",
+        "hsn_code": "37025610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, FOR COLOUR PHOTOGRAPHY (POLYCHROME): OF A WIDTH EXCEEDING 16 MM BUT\nNOT EXCEEDING 35 MM AND OF A LENGTH EXCEEDING 30 M: OTHER",
+        "hsn_code": "37025590",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, FOR COLOUR PHOTOGRAPHY (POLYCHROME): OF A WIDTH EXCEEDING 16 MM BUT\nNOT EXCEEDING 35 MM AND OF A LENGTH EXCEEDING 30 M: OTHER CINEMATOGRAPHIC FILM",
+        "hsn_code": "37025520",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, FOR COLOUR PHOTOGRAPHY (POLYCHROME): OF A WIDTH EXCEEDING 16 MM BUT\nNOT EXCEEDING 35 MM AND OF A LENGTH EXCEEDING 30 M: FINISHED ROLLS OF\nCINEMATOGRAPHIC POSITIVE",
+        "hsn_code": "37025510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, FOR COLOUR PHOTOGRAPHY (POLYCHROME): OF A WIDTH EXCEEDING 16 MM BUT\nNOT EXCEEDING 35 MM AND OF A LENGTH NOT EXCEEDING 30 M, OTHER THAN FOR SLIDES:\nOTHER",
+        "hsn_code": "37025490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, FOR COLOUR PHOTOGRAPHY (POLYCHROME): OF A WIDTH EXCEEDING 16 MM BUT\nNOT EXCEEDING 35 MM AND OF A LENGTH NOT EXCEEDING 30 M, OTHER THAN FOR SLIDES:\nOTHER CINEMATOGRAPHIC FILM",
+        "hsn_code": "37025420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, FOR COLOUR PHOTOGRAPHY (POLYCHROME): OF A WIDTH EXCEEDING 16 MM BUT\nNOT EXCEEDING 35 MM AND OF A LENGTH NOT EXCEEDING 30 M, OTHER THAN FOR SLIDES:\nFINISHED ROLLS OF CINEMATOGRAPHIC POSITIVE",
+        "hsn_code": "37025410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, FOR COLOUR PHOTOGRAPHY (POLYCHROME): OF A WIDTH EXCEEDING 16 MM BUT\nNOT EXCEEDING 35 MM AND OF A LENGTH NOT EXCEEDING 30 M, FOR SLIDES",
+        "hsn_code": "37025300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, FOR COLOUR PHOTOGRAPHY (POLYCHROME): OF A WIDTH NOT EXCEEDING 16\nMM AND OF A LENGTH EXCEEDING 14 M: OTHER",
+        "hsn_code": "37025290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, FOR COLOUR PHOTOGRAPHY (POLYCHROME): OF A WIDTH NOT EXCEEDING 16\nMM AND OF A LENGTH EXCEEDING 14 M: OTHER CINEMATOGRAPHIC FILM",
+        "hsn_code": "37025220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, FOR COLOUR PHOTOGRAPHY (POLYCHROME): OF A WIDTH NOT EXCEEDING 16\nMM AND OF A LENGTH EXCEEDING 14 M: FINISHED ROLLS OF CINEMATOGRAPHIC POSITIVE",
+        "hsn_code": "37025210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, WITHOUT PERFORATIONS, OF A WIDTH EXCEEDING 105 MM: OF A WIDTH\nEXCEEDING 105 MM BUT NOT EXCEEDING 610 MM: OTHER",
+        "hsn_code": "37024490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, WITHOUT PERFORATIONS, OF A WIDTH EXCEEDING 105 MM: OF A WIDTH\nEXCEEDING 105 MM BUT NOT EXCEEDING 610 MM: CINEMATOGRAPHIC FILM",
+        "hsn_code": "37024420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, WITHOUT PERFORATIONS, OF A WIDTH EXCEEDING 105 MM: OF A WIDTH\nEXCEEDING 105 MM BUT NOT EXCEEDING 610 MM: PHOTOGRAPHIC FILMS OF A WIDTH 120\nMM IN ROLLS",
+        "hsn_code": "37024410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, WITHOUT PERFORATIONS, OF A WIDTH EXCEEDING 105 MM: OF A WIDTH\nEXCEEDING 610 MM AND OF A LENGTH NOT EXCEEDING 200 M: OTHER",
+        "hsn_code": "37024390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, WITHOUT PERFORATIONS, OF A WIDTH EXCEEDING 105 MM: OF A WIDTH\nEXCEEDING 610 MM AND OF A LENGTH NOT EXCEEDING 200 M: CINEMATOGRAPHIC FILM",
+        "hsn_code": "37024320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, WITHOUT PERFORATIONS, OF A WIDTH EXCEEDING 105 MM: OF A WIDTH\nEXCEEDING 610 MM AND OF A LENGTH NOT EXCEEDING 200 M: PHOTOGRAPHIC FILMS (BLACK\nAND WHITE) OF A WIDTH 620 MM",
+        "hsn_code": "37024310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, WITHOUT PERFORATIONS, OF A WIDTH EXCEEDING 105 MM: OF A WIDTH\nEXCEEDING 610 MM AND OF A LENGTH EXCEEDING 200 M, OTHER THAN FOR COLOUR\nPHOTOGRAPHY: OTHER",
+        "hsn_code": "37024290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, WITHOUT PERFORATIONS, OF A WIDTH EXCEEDING 105 MM: OF A WIDTH\nEXCEEDING 610 MM AND OF A LENGTH EXCEEDING 200 M, OTHER THAN FOR COLOUR\nPHOTOGRAPHY: CINEMATOGRAPHIC FILM",
+        "hsn_code": "37024220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, WITHOUT PERFORATIONS, OF A WIDTH EXCEEDING 105 MM: OF A WIDTH\nEXCEEDING 610 MM AND OF A LENGTH EXCEEDING 200 M, OTHER THAN FOR COLOUR\nPHOTOGRAPHY: PHOTOGRAPHIC FILM OF A WIDTH 620 MM IN ROLLS",
+        "hsn_code": "37024210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, WITHOUT PERFORATIONS, OF A WIDTH EXCEEDING 105 MM: OF A WIDTH\nEXCEEDING 610 MM AND OF A LENGTH EXCEEDING 200 M, FOR COLOUR PHOTOGRAPHY\n(POLYCHROME): OTHER",
+        "hsn_code": "37024190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, WITHOUT PERFORATIONS, OF A WIDTH EXCEEDING 105 MM: OF A WIDTH\nEXCEEDING 610 MM AND OF A LENGTH EXCEEDING 200 M, FOR COLOUR PHOTOGRAPHY\n(POLYCHROME): CINEMATOGRAPHIC FILM",
+        "hsn_code": "37024110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, WITHOUT PERFORATIONS, OF A WIDTH NOT EXCEEDING 105 MM: OTHER: OTHER",
+        "hsn_code": "37023990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, WITHOUT PERFORATIONS, OF A WIDTH NOT EXCEEDING 105 MM: OTHER:\nCINEMATOGRAPHIC FILM",
+        "hsn_code": "37023910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, WITHOUT PERFORATIONS, OF A WIDTH NOT EXCEEDING 105 MM: OTHER, WITH\nSILVER HALIDE EMULSION: OTHER",
+        "hsn_code": "37023290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, WITHOUT PERFORATIONS, OF A WIDTH NOT EXCEEDING 105 MM: OTHER, WITH\nSILVER HALIDE EMULSION: CINEMATOGRAPHIC FILM",
+        "hsn_code": "37023210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, WITHOUT PERFORATIONS, OF A WIDTH NOT EXCEEDING 105 MM: FOR COLOUR\nPHOTOGRAPHY (POLYCHROME): OTHER",
+        "hsn_code": "37023190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nOTHER FILM, WITHOUT PERFORATIONS, OF A WIDTH NOT EXCEEDING 105 MM: FOR COLOUR\nPHOTOGRAPHY (POLYCHROME): CINEMATOGRAPHIC FILM",
+        "hsn_code": "37023110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nINSTANT PRINT FILM",
+        "hsn_code": "37022000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC FILM IN ROLLS, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN\nPAPER, PAPER-BOARD OR TEXTILES; INSTANT PRINT FILM IN ROLLS, SENSITISED, UNEXPOSED\nFOR XRAY",
+        "hsn_code": "37021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PLATES AND FILM IN THE FLAT, SENSITISED, UNEXPOSED, OF ANY MATERIAL OTHER THAN PAPER, PAPERBOARD OR TEXTILES; INSTANT PRINT-FILM IN THE FLAT, SENSITISED, UNEXPOSED, WHETHER OR NOT IN PACKS",
+        "hsn_code": "3701",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PLATES AND FILM IN THE FLAT, SENSITISED, UNEXPOSED, OF ANY MATERIAL\nOTHER THAN PAPER, PAPERBOARD OR TEXTILES; INSTANT PRINT-FILM IN THE FLAT, SENSITISED,\nUNEXPOSED, WHETHER OR NOT IN PACKS OTHER: OTHER: OTHER",
+        "hsn_code": "37019990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PLATES AND FILM IN THE FLAT, SENSITISED, UNEXPOSED, OF ANY MATERIAL\nOTHER THAN PAPER, PAPERBOARD OR TEXTILES; INSTANT PRINT-FILM IN THE FLAT, SENSITISED,\nUNEXPOSED, WHETHER OR NOT IN PACKS OTHER: OTHER: CINEMATOGRAPHIC FILM",
+        "hsn_code": "37019910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PLATES AND FILM IN THE FLAT, SENSITISED, UNEXPOSED, OF ANY MATERIAL\nOTHER THAN PAPER, PAPERBOARD OR TEXTILES; INSTANT PRINT-FILM IN THE FLAT, SENSITISED,\nUNEXPOSED, WHETHER OR NOT IN PACKS OTHER: FOR COLOUR PHOTOGRAPHY (POLYCHROME):\nOTHER",
+        "hsn_code": "37019190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PLATES AND FILM IN THE FLAT, SENSITISED, UNEXPOSED, OF ANY MATERIAL\nOTHER THAN PAPER, PAPERBOARD OR TEXTILES; INSTANT PRINT-FILM IN THE FLAT, SENSITISED,\nUNEXPOSED, WHETHER OR NOT IN PACKS OTHER: FOR COLOUR PHOTOGRAPHY (POLYCHROME):\nCINEMATOGRAPHIC FILM",
+        "hsn_code": "37019110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PLATES AND FILM IN THE FLAT, SENSITISED, UNEXPOSED, OF ANY MATERIAL\nOTHER THAN PAPER, PAPERBOARD OR TEXTILES; INSTANT PRINT-FILM IN THE FLAT, SENSITISED,\nUNEXPOSED, WHETHER OR NOT IN PACKS OTHER PLATES AND FILM, WITH ANY SIDE EXCEEDING\n255 MM",
+        "hsn_code": "37013000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PLATES AND FILM IN THE FLAT, SENSITISED, UNEXPOSED, OF ANY MATERIAL\nOTHER THAN PAPER, PAPERBOARD OR TEXTILES; INSTANT PRINT-FILM IN THE FLAT, SENSITISED,\nUNEXPOSED, WHETHER OR NOT IN PACKS INSTANT PRINT FILM",
+        "hsn_code": "37012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PLATES AND FILM IN THE FLAT, SENSITISED, UNEXPOSED, OF ANY MATERIAL\nOTHER THAN PAPER, PAPERBOARD OR TEXTILES; INSTANT PRINT-FILM IN THE FLAT, SENSITISED,\nUNEXPOSED, WHETHER OR NOT IN PACKS FOR X-RAY: OTHER",
+        "hsn_code": "37011090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PHOTOGRAPHIC PLATES AND FILM IN THE FLAT, SENSITISED, UNEXPOSED, OF ANY MATERIAL\nOTHER THAN PAPER, PAPERBOARD OR TEXTILES; INSTANT PRINT-FILM IN THE FLAT, SENSITISED,\nUNEXPOSED, WHETHER OR NOT IN PACKS FOR X-RAY: MEDICAL",
+        "hsn_code": "37011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-CERIUM AND OTHER PYROPHORIC ALLOYS IN ALL FORMS; ARTICLES OF COMBUSTIBLE MATERIALS AS SPECIFIED IN NOTE 2 TO THIS CHAPTER",
+        "hsn_code": "3606",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-CERIUM AND OTHER PYROPHORIC ALLOYS IN ALL FORMS; ARTICLES OF COMBUSTIBLE\nMATERIALS AS SPECIFIED IN NOTE 2 TO THIS CHAPTER OTHER: OTHER: OTHERS",
+        "hsn_code": "36069099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-CERIUM AND OTHER PYROPHORIC ALLOYS IN ALL FORMS; ARTICLES OF COMBUSTIBLE\nMATERIALS AS SPECIFIED IN NOTE 2 TO THIS CHAPTER OTHER: OTHER: DNPT (DINITROSO PENTA\nMETHYLENE TETRAMINE)",
+        "hsn_code": "36069093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-CERIUM AND OTHER PYROPHORIC ALLOYS IN ALL FORMS; ARTICLES OF COMBUSTIBLE\nMATERIALS AS SPECIFIED IN NOTE 2 TO THIS CHAPTER OTHER: OTHER: PYROPHORIC ALLOYS, IN\nALL FORMS",
+        "hsn_code": "36069092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-CERIUM AND OTHER PYROPHORIC ALLOYS IN ALL FORMS; ARTICLES OF COMBUSTIBLE\nMATERIALS AS SPECIFIED IN NOTE 2 TO THIS CHAPTER OTHER: OTHER: FERROCERIUM, IN ALL\nFORMS",
+        "hsn_code": "36069091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-CERIUM AND OTHER PYROPHORIC ALLOYS IN ALL FORMS; ARTICLES OF COMBUSTIBLE\nMATERIALS AS SPECIFIED IN NOTE 2 TO THIS CHAPTER OTHER: COMBUSTIBLE PREPARATIONS",
+        "hsn_code": "36069010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FERRO-CERIUM AND OTHER PYROPHORIC ALLOYS IN ALL FORMS; ARTICLES OF COMBUSTIBLE\nMATERIALS AS SPECIFIED IN NOTE 2 TO THIS CHAPTER LIQUID OR LIQUEFIED GAS FUELS IN\nCONTAINERS OF A KIND USED FOR FILLING OR REFILLING CIGARETTE OR SIMILAR LIGHTERS AND\nOF A CAPACITY NOT EXCEEDING 300 CM3",
+        "hsn_code": "36061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIREWORKS, SIGNALLING FLARES, RAIN ROCKETS, FOG SIGNALS AND OTHER PYROTECHNIC ARTICLES",
+        "hsn_code": "3604",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIREWORKS, SIGNALLING FLARES, RAIN ROCKETS, FOG SIGNALS AND OTHER PYROTECHNIC\nARTICLES OTHER: OTHER",
+        "hsn_code": "36049090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIREWORKS, SIGNALLING FLARES, RAIN ROCKETS, FOG SIGNALS AND OTHER PYROTECHNIC\nARTICLES OTHER: SHIP SIGNALS",
+        "hsn_code": "36049010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FIREWORKS, SIGNALLING FLARES, RAIN ROCKETS, FOG SIGNALS AND OTHER PYROTECHNIC\nARTICLES FIREWORKS",
+        "hsn_code": "36041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SAFETY FUSES; DETONATING FUSES; PERCUSSION OR DETONATING CAPS; IGNITERS; ELECTRIC DETENATORS",
+        "hsn_code": "3603",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SAFETY FUSES; DETONATING FUSES; PERCUSSION OR DETONATING CAPS; IGNITERS; ELECTRIC\nDETONATORS SAFETY FUSES; DETONATING FUSES; PERCUSSION OR DETONATING CAPS;\nIGNITERS; ELECTRIC DETONATORS: ELECTRIC DETONATORS: OTHER",
+        "hsn_code": "36030059",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SAFETY FUSES; DETONATING FUSES; PERCUSSION OR DETONATING CAPS; IGNITERS; ELECTRIC\nDETONATORS SAFETY FUSES; DETONATING FUSES; PERCUSSION OR DETONATING CAPS;\nIGNITERS; ELECTRIC DETONATORS: ELECTRIC DETONATORS: CONTAINING EXPLOSIVES\nELECTRICALLY IGNITED, NONORDNANCE",
+        "hsn_code": "36030051",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SAFETY FUSES; DETONATING FUSES; PERCUSSION OR DETONATING CAPS; IGNITERS; ELECTRIC\nDETONATORS SAFETY FUSES; DETONATING FUSES; PERCUSSION OR DETONATING CAPS;\nIGNITERS; ELECTRIC DETONATORS: IGNITERS: OTHER",
+        "hsn_code": "36030049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SAFETY FUSES; DETONATING FUSES; PERCUSSION OR DETONATING CAPS; IGNITERS; ELECTRIC\nDETONATORS SAFETY FUSES; DETONATING FUSES; PERCUSSION OR DETONATING CAPS;\nIGNITERS; ELECTRIC DETONATORS: IGNITERS: NONORDNANCE",
+        "hsn_code": "36030041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SAFETY FUSES; DETONATING FUSES; PERCUSSION OR DETONATING CAPS; IGNITERS; ELECTRIC\nDETONATORS SAFETY FUSES; DETONATING FUSES; PERCUSSION OR DETONATING CAPS;\nIGNITERS; ELECTRIC DETONATORS: PERCUSSION AND DETONATING CAPS: OTHER",
+        "hsn_code": "36030039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SAFETY FUSES; DETONATING FUSES; PERCUSSION OR DETONATING CAPS; IGNITERS; ELECTRIC\nDETONATORS SAFETY FUSES; DETONATING FUSES; PERCUSSION OR DETONATING CAPS;\nIGNITERS; ELECTRIC DETONATORS: PERCUSSION AND DETONATING CAPS: NONORDNANCE",
+        "hsn_code": "36030031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SAFETY FUSES; DETONATING FUSES; PERCUSSION OR DETONATING CAPS; IGNITERS; ELECTRIC\nDETONATORS SAFETY FUSES; DETONATING FUSES; PERCUSSION OR DETONATING CAPS;\nIGNITERS; ELECTRIC DETONATORS: DETONATING FUSES",
+        "hsn_code": "36030020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SAFETY FUSES; DETONATING FUSES; PERCUSSION OR DETONATING CAPS; IGNITERS; ELECTRIC\nDETONATORS SAFETY FUSES; DETONATING FUSES; PERCUSSION OR DETONATING CAPS;\nIGNITERS; ELECTRIC DETONATORS: OTHER",
+        "hsn_code": "36030019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SAFETY FUSES; DETONATING FUSES; PERCUSSION OR DETONATING CAPS; IGNITERS; ELECTRIC\nDETONATORS SAFETY FUSES; DETONATING FUSES; PERCUSSION OR DETONATING CAPS;\nIGNITERS; ELECTRIC DETONATORS: SAFETY FUSES: FOR MINE BLASTING",
+        "hsn_code": "36030011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED EXPLOSIVES, OTHER THAN PROPELLANT POWDERS",
+        "hsn_code": "3602",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED EXPLOSIVES, OTHER THAN PROPELLANT POWDERS PREPARED EXPLOSIVES, OTHER\nTHAN PROPELLANT POWDERS: OTHER",
+        "hsn_code": "36020090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED EXPLOSIVES, OTHER THAN PROPELLANT POWDERS PREPARED EXPLOSIVES, OTHER\nTHAN PROPELLANT POWDERS: INDUSTRIAL EXPLOSIVES",
+        "hsn_code": "36020010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PROPELLANT POWDERS",
+        "hsn_code": "3601",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PROPELLANT POWDERS PROPELLANT POWDERS: OTHER",
+        "hsn_code": "36010090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PROPELLANT POWDERS PROPELLANT POWDERS: GUN POWDER",
+        "hsn_code": "36010020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PROPELLANT POWDERS PROPELLANT POWDERS: BLASTING POWDER",
+        "hsn_code": "36010010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENZYMES; PREPARED ENZYMES NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "3507",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENZYMES; PREPARED ENZYMES NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER: OTHER:\nOTHER",
+        "hsn_code": "35079099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENZYMES; PREPARED ENZYMES NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER: OTHER:\nENZYMATIC PREPARATIONS CONTAINING FOOD STUFFS",
+        "hsn_code": "35079091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENZYMES; PREPARED ENZYMES NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER: ENZYMES FOR\nPHARMACEUTICAL USE, OTHER THAN STREPTOKINASE: OTHER",
+        "hsn_code": "35079079",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENZYMES; PREPARED ENZYMES NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER: ENZYMES FOR\nPHARMACEUTICAL USE, OTHER THAN STREPTOKINASE: PAPAIN, PURE, OF PHARMACEUTICAL\nGRADE",
+        "hsn_code": "35079071",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENZYMES; PREPARED ENZYMES NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER: OTHER\nENZYMES OF MICROBIAL ORIGIN: OTHER",
+        "hsn_code": "35079069",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENZYMES; PREPARED ENZYMES NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER: OTHER\nENZYMES OF MICROBIAL ORIGIN: AMYLASES ENZYMES",
+        "hsn_code": "35079062",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENZYMES; PREPARED ENZYMES NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER: OTHER\nENZYMES OF MICROBIAL ORIGIN: STREPTOKINASE",
+        "hsn_code": "35079061",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENZYMES; PREPARED ENZYMES NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER: PECTOLYTIC\nENZYME (PECTIMASE)",
+        "hsn_code": "35079050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENZYMES; PREPARED ENZYMES NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER: PECTIN\nESTERASES PURE",
+        "hsn_code": "35079040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENZYMES; PREPARED ENZYMES NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER: PEPSIN\n(EXCLUDING MEDICAMENT)",
+        "hsn_code": "35079030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENZYMES; PREPARED ENZYMES NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER: PANCRETIN\nPURE (EXCLUDING MEDICAMENT)",
+        "hsn_code": "35079020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENZYMES; PREPARED ENZYMES NOT ELSEWHERE SPECIFIED OR INCLUDED OTHER: INDUSTRIAL\nENZYMES (TEXTILE ASSISTANT)",
+        "hsn_code": "35079010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENZYMES; PREPARED ENZYMES NOT ELSEWHERE SPECIFIED OR INCLUDED RENNET AND\nCONCENTRATES THEREOF: OTHER: OTHER",
+        "hsn_code": "35071099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENZYMES; PREPARED ENZYMES NOT ELSEWHERE SPECIFIED OR INCLUDED RENNET AND\nCONCENTRATES THEREOF: OTHER: ANIMAL RENNET",
+        "hsn_code": "35071091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENZYMES; PREPARED ENZYMES NOT ELSEWHERE SPECIFIED OR INCLUDED RENNET AND\nCONCENTRATES THEREOF: MICROBIAL RENNET: OTHER",
+        "hsn_code": "35071019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ENZYMES; PREPARED ENZYMES NOT ELSEWHERE SPECIFIED OR INCLUDED RENNET AND\nCONCENTRATES THEREOF: MICROBIAL RENNET: ANIMAL RENNET",
+        "hsn_code": "35071011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED GLUES AND OTHER PREPARED ADHESIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED; PRODUCTS SUITABLE FOR USE AS GLUES OR ADHESIVES, PUT UP FOR RETAIL SALE AS GLUES OR ADHESIVES, NOT EXCEEDING A NET WEIGHT OF 1 KG",
+        "hsn_code": "3506",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED GLUES AND OTHER PREPARED ADHESIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED;\nPRODUCTS SUITABLE FOR USE AS GLUES OR ADHESIVES, PUT UP FOR RETAIL SALE AS GLUES OR\nADHESIVES, NOT EXCEEDING A NET WEIGHT OF 1 KG : OTHER: OTHER: PREPARED GLUES AND\nOTHER PREPARED ADHESIVES NOT ELSEWHERE SPECIFIED OR INCLUDED: OTHER",
+        "hsn_code": "35069999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED GLUES AND OTHER PREPARED ADHESIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED;\nPRODUCTS SUITABLE FOR USE AS GLUES OR ADHESIVES, PUT UP FOR RETAIL SALE AS GLUES OR\nADHESIVES, NOT EXCEEDING A NET WEIGHT OF 1 KG : OTHER: OTHER: PREPARED GLUES AND\nOTHER PREPARED ADHESIVES NOT ELSEWHERE SPECIFIED OR INCLUDED: BASED ON STARCH,\nGUM, LATEX, PF, UF AND PVA",
+        "hsn_code": "35069991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED GLUES AND OTHER PREPARED ADHESIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED;\nPRODUCTS SUITABLE FOR USE AS GLUES OR ADHESIVES, PUT UP FOR RETAIL SALE AS GLUES OR\nADHESIVES, NOT EXCEEDING A NET WEIGHT OF 1 KG : OTHER: OTHER: SYNTHETIC GLUE WITH\nPHENOL UREA OR CRESOL (WITH FORMALDEHYDE) AS THE MAIN COMPONENT",
+        "hsn_code": "35069910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED GLUES AND OTHER PREPARED ADHESIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED;\nPRODUCTS SUITABLE FOR USE AS GLUES OR ADHESIVES, PUT UP FOR RETAIL SALE AS GLUES OR\nADHESIVES, NOT EXCEEDING A NET WEIGHT OF 1 KG : OTHER: ADHESIVES BASED ON POLYMERS\nOF HEADINGS 3901 TO 3913 OR ON RUBBER: OTHER",
+        "hsn_code": "35069190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED GLUES AND OTHER PREPARED ADHESIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED;\nPRODUCTS SUITABLE FOR USE AS GLUES OR ADHESIVES, PUT UP FOR RETAIL SALE AS GLUES OR\nADHESIVES, NOT EXCEEDING A NET WEIGHT OF 1 KG : OTHER: ADHESIVES BASED ON POLYMERS\nOF HEADINGS 3901 TO 3913 OR ON RUBBER: BASED ON LATEX, PHENOL FORMALDEHYDE (PF),\nUREA FORMALDEHYDE (UF) AND POLYVINYL ALCOHOL (PVA)",
+        "hsn_code": "35069110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED GLUES AND OTHER PREPARED ADHESIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED;\nPRODUCTS SUITABLE FOR USE AS GLUES OR ADHESIVES, PUT UP FOR RETAIL SALE AS GLUES OR\nADHESIVES, NOT EXCEEDING A NET WEIGHT OF 1KG : PRODUCTS SUITABLE FOR USE AS GLUES\nOR ADHESIVES, PUT UP FOR RETAIL SALE AS GLUES OR ADHESIVES, NOT EXCEEDING A NET\nWEIGHT OF 1KG",
+        "hsn_code": "35061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DEXTRINS AND OTHER MODIFIED STARCHES (FOR EXAMPLE, PREGELATINISED OR ESTERIFIED STARCHES); GLUES BASED ON STARCHES, OR ON DEXTRINS OR OTHER MODIFIED STARCHES",
+        "hsn_code": "3505",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DEXTRINS AND OTHER MODIFIED STARCHES (FOR EXAMPLE, PREGELATINISED OR ESTERIFIED\nSTARCHES); GLUES BASED ON STARCHES, OR ON DEXTRINS OR OTHER MODIFIED STARCHES\nGLUES",
+        "hsn_code": "35052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DEXTRINS AND OTHER MODIFIED STARCHES (FOR EXAMPLE, PREGELATINISED OR ESTERIFIED\nSTARCHES); GLUES BASED ON STARCHES, OR ON DEXTRINS OR OTHER MODIFIED STARCHES\nDEXTRINS AND OTHER MODIFIED STARCHES: OTHER",
+        "hsn_code": "35051090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DEXTRINS AND OTHER MODIFIED STARCHES (FOR EXAMPLE, PREGELATINISED OR ESTERIFIED\nSTARCHES); GLUES BASED ON STARCHES, OR ON DEXTRINS OR OTHER MODIFIED STARCHES\nDEXTRINS AND OTHER MODIFIED STARCHES: ESTERIFIED STARCHES",
+        "hsn_code": "35051010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PEPTONES AND THEIR DERIVATIVES; OTHER PROTEIN SUBSTANCES AND THEIR DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED; HIDE POWDER, WHETHER OR NOT CHROMED",
+        "hsn_code": "3504",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PEPTONES AND THEIR DERIVATIVES; OTHER PROTEIN SUBSTANCES AND THEIR DERIVATIVES,\nNOT ELSEWHERE SPECIFIED OR INCLUDED; HIDE POWDER, WHETHER OR NOT CHROMED\nPEPTONES AND THEIR DERIVATIVES; OTHER PROTEIN SUBSTANCES AND THEIR DERIVATIVES,\nNOT ELSEWHERE SPECIFIED OR INCLUDED; HIDE POWDER, WHETHER OR NOT CHROMED:\nOTHER: OTHERS",
+        "hsn_code": "35040099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PEPTONES AND THEIR DERIVATIVES; OTHER PROTEIN SUBSTANCES AND THEIR DERIVATIVES,\nNOT ELSEWHERE SPECIFIED OR INCLUDED; HIDE POWDER, WHETHER OR NOT CHROMED\nPEPTONES AND THEIR DERIVATIVES; OTHER PROTEIN SUBSTANCES AND THEIR DERIVATIVES,\nNOT ELSEWHERE SPECIFIED OR INCLUDED; HIDE POWDER, WHETHER OR NOT CHROMED:\nOTHER: ISOLATED SOYA PROTEIN",
+        "hsn_code": "35040091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PEPTONES AND THEIR DERIVATIVES; OTHER PROTEIN SUBSTANCES AND THEIR DERIVATIVES,\nNOT ELSEWHERE SPECIFIED OR INCLUDED; HIDE POWDER, WHETHER OR NOT CHROMED\nPEPTONES AND THEIR DERIVATIVES; OTHER PROTEIN SUBSTANCES AND THEIR DERIVATIVES,\nNOT ELSEWHERE SPECIFIED OR INCLUDED; HIDE POWDER, WHETHER OR NOT CHROMED:\nPEPTONES",
+        "hsn_code": "35040010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GELATIN [INCLUDING GELATIN IN RECTANGULAR (INCLUDING SQUARE) SHEETS, WHETHER OR NOT SURFACE-WORKED OR COLOURED] AND GELATIN DERIVATIVES; ISINGLASS; OTHER GLUES OF ANIMAL ORIGIN, EXCLUDING CASEIN GLUES OF HEADING 3501",
+        "hsn_code": "3503",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GELATIN [INCLUDING GELATIN IN RECTANGULAR (INCLUDING SQUARE) SHEETS, WHETHER OR\nNOT SURFACE-WORKED OR COLOURED] AND GELATIN DERIVATIVES; ISINGLASS; OTHER GLUES\nOF ANIMAL ORIGIN, EXCLUDING CASEIN GLUES OF HEADING 3501 GELATIN [INCLUDING GELATIN\nIN RECTANGULAR(INCLUDING SQUARE) SHEETS, WHETHER OR NOT SURFACE-WORKED OR\nCOLOURED] AND GELATIN DERIVATIVES; ISINGLASS; OTHER GLUES OF ANIMAL ORIGIN,\nEXCLUDING CAS EIN GLUES OF HEADING 3501: OTHER",
+        "hsn_code": "35030090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GELATIN [INCLUDING GELATIN IN RECTANGULAR (INCLUDING SQUARE) SHEETS, WHETHER OR\nNOT SURFACE-WORKED OR COLOURED] AND GELATIN DERIVATIVES; ISINGLASS; OTHER GLUES\nOF ANIMAL ORIGIN, EXCLUDING CASEIN GLUES OF HEADING 3501 GELATIN [INCLUDING GELATIN\nIN RECTANGULAR(INCLUDING SQUARE) SHEETS, WHETHER OR NOT SURFACE-WORKED OR\nCOLOURED] AND GELATIN DERIVATIVES; ISINGLASS; OTHER GLUES OF ANIMAL ORIGIN,\nEXCLUDING CAS EIN GLUES OF HEADING 3501: GLUES DERIVED FROM BONES, HIDES AND\nSIMILAR ITEMS; FISH GLUES",
+        "hsn_code": "35030030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GELATIN [INCLUDING GELATIN IN RECTANGULAR (INCLUDING SQUARE) SHEETS, WHETHER OR\nNOT SURFACE-WORKED OR COLOURED] AND GELATIN DERIVATIVES; ISINGLASS; OTHER GLUES\nOF ANIMAL ORIGIN, EXCLUDING CASEIN GLUES OF HEADING 3501 GELATIN [INCLUDING GELATIN\nIN RECTANGULAR(INCLUDING SQUARE) SHEETS, WHETHER OR NOT SURFACE-WORKED OR\nCOLOURED] AND GELATIN DERIVATIVES; ISINGLASS; OTHER GLUES OF ANIMAL ORIGIN,\nEXCLUDING CAS EIN GLUES OF HEADING 3501: GELATIN, EDIBLE GRADE AND NOT ELSEWHERE\nSPECIFIED OR INCLUDED",
+        "hsn_code": "35030020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GELATIN [INCLUDING GELATIN IN RECTANGULAR (INCLUDING SQUARE) SHEETS, WHETHER OR\nNOT SURFACE-WORKED OR COLOURED] AND GELATIN DERIVATIVES; ISINGLASS; OTHER GLUES\nOF ANIMAL ORIGIN, EXCLUDING CASEIN GLUES OF HEADING 3501 GELATIN [INCLUDING GELATIN\nIN RECTANGULAR(INCLUDING SQUARE) SHEETS, WHETHER OR NOT SURFACE-WORKED OR\nCOLOURED] AND GELATIN DERIVATIVES; ISINGLASS; OTHER GLUES OF ANIMAL ORIGIN,\nEXCLUDING CAS EIN GLUES OF HEADING 3501: ISINGLASS",
+        "hsn_code": "35030010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "albumin’s (INCLUDING CONCENTRATES OF TWO OR MORE WHEY PROTEINS, CONTAINING BY WEIGHT MORE THAN 80% WHEY PROTEINS, CALCULATED ON THE DRY MATTER), ALBUMINATES AND OTHER ALBUMIN DERIVATIVES",
+        "hsn_code": "3502",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALBUMINS (INCLUDING CONCENTRATES OF TWO OR MORE WHEY PROTEINS, CONTAINING BY\nWEIGHT MORE THAN 80% WHEY PROTEINS, CALCULATED ON THE DRY MATTER), ALBUMINATES\nAND OTHER ALBUMIN DERIVATIVES OTHER",
+        "hsn_code": "35029000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALBUMINS (INCLUDING CONCENTRATES OF TWO OR MORE WHEY PROTEINS, CONTAINING BY\nWEIGHT MORE THAN 80% WHEY PROTEINS, CALCULATED ON THE DRY MATTER), ALBUMINATES\nAND OTHER ALBUMIN DERIVATIVES MILK ALBUMIN, INCLUDING CONCENTRATES OF TWO OR\nMORE WHEY PROTEINS",
+        "hsn_code": "35022000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALBUMINS (INCLUDING CONCENTRATES OF TWO OR MORE WHEY PROTEINS, CONTAINING BY\nWEIGHT MORE THAN 80% WHEY PROTEINS, CALCULATED ON THE DRY MATTER), ALBUMINATES\nAND OTHER ALBUMIN DERIVATIVES EGG ALBUMIN: OTHER",
+        "hsn_code": "35021900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALBUMINS (INCLUDING CONCENTRATES OF TWO OR MORE WHEY PROTEINS, CONTAINING BY\nWEIGHT MORE THAN 80% WHEY PROTEINS, CALCULATED ON THE DRY MATTER), ALBUMINATES\nAND OTHER ALBUMIN DERIVATIVES EGG ALBUMIN: DRIED",
+        "hsn_code": "35021100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CASEIN, CASEINATES AND OTHER CASEIN DERIVATIVES; CASEIN GLUES",
+        "hsn_code": "3501",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CASEIN, CASEINATES AND OTHER CASEIN DERIVATIVES; CASEIN GLUES OTHER",
+        "hsn_code": "35019000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CASEIN, CASEINATES AND OTHER CASEIN DERIVATIVES; CASEIN GLUES CASEIN",
+        "hsn_code": "35011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MODELLING PASTES, INCLUDING THOSE PUT UP FOR CHILDREN’S AMUSEMENT; PREPARATIONS KNOWN AS “DENTAL WAX” OR AS “DENTAL IMPRESSION COMPOUNDS”, PUT UP IN SETS, IN PACKINGS FOR RETAIL SALE OR IN PLATES, HORSESHOE SHAPES, STICKS OR SIMILAR FORMS; OTHER PREPARATIONS FOR USE IN DENTISTRY, WITH A BASIS OF PLASTER (OF CALCINED GYPSUM OR CALCIUM SULPHATE)",
+        "hsn_code": "3407",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MODELLING PASTES, INCLUDING THOSE PUT UP FOR CHILDRENS AMUSEMENT; PREPARATIONS\nKNOWN AS DENTAL WAX OR AS DENTAL IMPRESSION COMPOUNDS, PUT UP IN SETS, IN\nPACKINGS FOR RETAIL SALE OR IN PLATES, HORSESHOE SHAPES, STICKS OR SIMILAR FORMS;\nOTHER PREPARATIONS FOR USE IN DENTISTRY, WITH A BASIS OF PLASTER (OF CALCINED\nGYPSUM OR CALCIUM SULPHATE) MODELLING PASTES, INCLUDING THOSE PUT UP\nFORCHILDRENS AMUSEMENT; PREPARATIONS KNOWN AS DENTAL WAX OR AS DENTAL\nIMPRESSION COMPOUNDS, PUT UP IN SETS, IN PACKINGS FOR RETAIL SALE OR IN PLATES,\nHORSESHOE SHAPES,STICKS OR SIMILAR FORMS; OTHER PREPARATIONS FOR USE IN DENTISTRY,\nWITH A BASIS OF PLASTER (OF CALCINED GYPSUM OR CALCIUM SULPHATE): OTHER",
+        "hsn_code": "34070090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MODELLING PASTES, INCLUDING THOSE PUT UP FOR CHILDRENS AMUSEMENT; PREPARATIONS\nKNOWN AS DENTAL WAX OR AS DENTAL IMPRESSION COMPOUNDS, PUT UP IN SETS, IN\nPACKINGS FOR RETAIL SALE OR IN PLATES, HORSESHOE SHAPES, STICKS OR SIMILAR FORMS;\nOTHER PREPARATIONS FOR USE IN DENTISTRY, WITH A BASIS OF PLASTER (OF CALCINED\nGYPSUM OR CALCIUM SULPHATE) MODELLING PASTES, INCLUDING THOSE PUT UP\nFORCHILDRENS AMUSEMENT; PREPARATIONS KNOWN AS DENTAL WAX OR AS DENTAL\nIMPRESSION COMPOUNDS, PUT UP IN SETS, IN PACKINGS FOR RETAIL SALE OR IN PLATES,\nHORSESHOE SHAPES,STICKS OR SIMILAR FORMS; OTHER PREPARATIONS FOR USE IN DENTISTRY,\nWITH A BASIS OF PLASTER (OF CALCINED GYPSUM OR CALCIUM SULPHATE): MODELLING\nPASTES, INCLUDING THOSE PUT UP FOR CHILDRENS AMUSEMENT",
+        "hsn_code": "34070010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CANDLES, TAPERS AND THE LIKE CANDLES, TAPERS AND THE LIKE: CANDLES",
+        "hsn_code": "34060010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CANDLES, TAPERS AND THE LIKE CANDLES, TAPERS AND THE LIKE: OTHER",
+        "hsn_code": "34060090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CANDLES, TAPERS AND THE LIKE",
+        "hsn_code": "3406",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLISHES AND CREAMS, FOR FOOTWEAR, FURNITURE,FLOORS, COACHWORK, GLASS OR METAL, SCOURING PASTES AND POWDERS AND SIMILAR PREPARATIONS (WHETHER OR NOT IN THE FORM OF PAPER, WADDING, FELT, NONWOVENS, CELLULAR PLASTICS OR CELLULAR RUBBER, IMPREGNATED, COATED OR COVERED WITH SUCH PREPARATIONS), EXCLUDING WAXES OF HEADING 3404",
+        "hsn_code": "3405",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "POLISHES AND CREAMS, FOR FOOTWEAR, FURNITURE, FLOORS, COACHWORK, GLASS OR METAL,\nSCOURING PASTES AND POWDERS AND SIMILAR PREPARATIONS (WHETHER OR NOT IN THE\nFORM OF PAPER, WADDING, FELT, NONWOVENS,CELLULAR PLASTICS OR CELLULAR RUBBER,\nIMPREGNATED, COATED OR COVERED WITH SUCH PREPARATIONS), EXCLUDING WAXES OF\nHEADING 3404 OTHER: OTHER",
+        "hsn_code": "34059090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "POLISHES AND CREAMS, FOR FOOTWEAR, FURNITURE, FLOORS, COACHWORK, GLASS OR METAL,\nSCOURING PASTES AND POWDERS AND SIMILAR PREPARATIONS (WHETHER OR NOT IN THE\nFORM OF PAPER, WADDING, FELT, NONWOVENS,CELLULAR PLASTICS OR CELLULAR RUBBER,\nIMPREGNATED, COATED OR COVERED WITH SUCH PREPARATIONS), EXCLUDING WAXES OF\nHEADING 3404 OTHER: POLISHES AND COMPOSITIONS FOR APPLICATION TO METAL INCLUDING\nDIAMOND POLISHING POWDER OR PASTE",
+        "hsn_code": "34059010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "POLISHES AND CREAMS, FOR FOOTWEAR, FURNITURE, FLOORS, COACHWORK, GLASS OR METAL,\nSCOURING PASTES AND POWDERS AND SIMILAR PREPARATIONS (WHETHER OR NOT IN THE\nFORM OF PAPER, WADDING, FELT, NONWOVENS,CELLULAR PLASTICS OR CELLULAR RUBBER,\nIMPREGNATED, COATED OR COVERED WITH SUCH PREPARATIONS), EXCLUDING WAXES OF\nHEADING 3404 SCOURING PASTES AND POWDERS AND OTHER",
+        "hsn_code": "34054000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "POLISHES AND CREAMS, FOR FOOTWEAR, FURNITURE, FLOORS, COACHWORK, GLASS OR METAL,\nSCOURING PASTES AND POWDERS AND SIMILAR PREPARATIONS (WHETHER OR NOT IN THE\nFORM OF PAPER, WADDING, FELT, NONWOVENS,CELLULAR PLASTICS OR CELLULAR RUBBER,\nIMPREGNATED, COATED OR COVERED WITH SUCH PREPARATIONS), EXCLUDING WAXES OF\nHEADING 3404 POLISHES AND SIMILAR PREPARATIONS FOR COACH-WORK, OTHER THAN METAL\nPOLISHES",
+        "hsn_code": "34053000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "POLISHES AND CREAMS, FOR FOOTWEAR, FURNITURE, FLOORS, COACHWORK, GLASS OR METAL,\nSCOURING PASTES AND POWDERS AND SIMILAR PREPARATIONS (WHETHER OR NOT IN THE\nFORM OF PAPER, WADDING, FELT, NONWOVENS,CELLULAR PLASTICS OR CELLULAR RUBBER,\nIMPREGNATED, COATED OR COVERED WITH SUCH PREPARATIONS), EXCLUDING WAXES OF\nHEADING 3404 POLISHES, CREAMS AND SIMILAR PREPARATIONS FOR THE MAINTENANCE OF\nWOODEN FURNITURE, FLOORS OR OTHER WOOD WORK",
+        "hsn_code": "34052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "POLISHES AND CREAMS, FOR FOOTWEAR, FURNITURE, FLOORS, COACHWORK, GLASS OR METAL,\nSCOURING PASTES AND POWDERS AND SIMILAR PREPARATIONS (WHETHER OR NOT IN THE\nFORM OF PAPER, WADDING, FELT, NONWOVENS,CELLULAR PLASTICS OR CELLULAR RUBBER,\nIMPREGNATED, COATED OR COVERED WITH SUCH PREPARATIONS), EXCLUDING WAXES OF\nHEADING 3404 POLISHES, CREAMS AND SIMILAR PREPARATIONS FOR FOOTWEAR OR LEATHER",
+        "hsn_code": "34051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL WAXES AND PREPARED WAXES",
+        "hsn_code": "3404",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL WAXES AND PREPARED WAXES OTHER: OTHER",
+        "hsn_code": "34049090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL WAXES AND PREPARED WAXES OTHER: ARTIFICIAL WAXES (INCLUDING WATER\nSOLUBLE WAXES) PREPARED WAXES, NOT EMULSIFIED OR CONTAINING SOLVENTS: OTHER",
+        "hsn_code": "34049039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL WAXES AND PREPARED WAXES OTHER: ARTIFICIAL WAXES (INCLUDING WATER\nSOLUBLE WAXES) PREPARED WAXES, NOT EMULSIFIED OR CONTAINING SOLVENTS: POLY\nCHLORINATED TERPHENYLS",
+        "hsn_code": "34049033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL WAXES AND PREPARED WAXES OTHER: ARTIFICIAL WAXES (INCLUDING WATER\nSOLUBLE WAXES) PREPARED WAXES, NOT EMULSIFIED OR CONTAINING SOLVENTS: POLY\nCHLORINATED BIPHENYLS",
+        "hsn_code": "34049032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL WAXES AND PREPARED WAXES OTHER: ARTIFICIAL WAXES (INCLUDING WATER\nSOLUBLE WAXES) PREPARED WAXES, NOT EMULSIFIED OR CONTAINING SOLVENTS: POLY\nBROMINATED BIPHENYLS",
+        "hsn_code": "34049031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL WAXES AND PREPARED WAXES OTHER: POLYETHYLENE WAX",
+        "hsn_code": "34049020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL WAXES AND PREPARED WAXES OTHER: SEALING WAX (INCLUDING BOTTLE SEALING\nWAX) IN STICKS, CAKES OR SIMILAR FORMS",
+        "hsn_code": "34049010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL WAXES AND PREPARED WAXES OF POLY (OXYETHYLENE) (POLYETHYLENE GLYCOL)",
+        "hsn_code": "34042000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL WAXES AND PREPARED WAXES OF CHEMICALLY MODIFIED LIGNITE",
+        "hsn_code": "34041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LUBRICATING PREPARATIONS (INCLUDING CUTTING-OIL PREPARATIONS, BOLT OR NUT RELEASE PREPARATIONS, ANTI-RUST OR ANTI-CORROSION PREPARATIONS AND MOULD RELEASE PREPARATIONS, BASED ON LUBRICANTS) AND PREPARATIONS OF A KIND USED FOR THE OIL OR GREASE TREATMENT OF TEXTILE MATERIALS, LEATHER, FURSKINS OR OTHER MATERIALS, BUT EXCLUDING PREPARATIONS CONTAINING, AS BASIC CONSTITUENTS, 70 % OR MORE BY WEIGHT OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS",
+        "hsn_code": "3403",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LUBRICATING PREPARATIONS (INCLUDING CUTTING-OIL PREPARATIONS, BOLT OR NUT RELEASE\nPREPARATIONS,ANTI-RUST OR ANTI-CORROSION PREPARATIONS AND MOULD RELEASE\nPREPARATIONS, BASED ON LUBRICANTS) AND PREPARATIONS OF A KIND USED FOR THE OIL OR\nGREASE TREATMENT OF TEXTILE MATERIALS, LEATHER, FURSKINS OR OTHER MATERIALS, BUT\nEXCLUDING PREPARATIONS CONTAINING, AS BASIC CONSTITUENTS, 70 % OR MORE BY WEIGHT\nOF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS OTHER: OTHER",
+        "hsn_code": "34039900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LUBRICATING PREPARATIONS (INCLUDING CUTTING-OIL PREPARATIONS, BOLT OR NUT RELEASE\nPREPARATIONS,ANTI-RUST OR ANTI-CORROSION PREPARATIONS AND MOULD RELEASE\nPREPARATIONS, BASED ON LUBRICANTS) AND PREPARATIONS OF A KIND USED FOR THE OIL OR\nGREASE TREATMENT OF TEXTILE MATERIALS, LEATHER, FURSKINS OR OTHER MATERIALS, BUT\nEXCLUDING PREPARATIONS CONTAINING, AS BASIC CONSTITUENTS, 70 % OR MORE BY WEIGHT\nOF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS OTHER:\nPREPARATIONS FOR THE TREATMENT OF TEXTILE MATERIALS, LEATHER, FURSKINS OR OTHER\nMATERIALS",
+        "hsn_code": "34039100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LUBRICATING PREPARATIONS (INCLUDING CUTTING-OIL PREPARATIONS, BOLT OR NUT RELEASE\nPREPARATIONS,ANTI-RUST OR ANTI-CORROSION PREPARATIONS AND MOULD RELEASE\nPREPARATIONS, BASED ON LUBRICANTS) AND PREPARATIONS OF A KIND USED FOR THE OIL OR\nGREASE TREATMENT OF TEXTILE MATERIALS, LEATHER, FURSKINS OR OTHER MATERIALS, BUT\nEXCLUDING PREPARATIONS CONTAINING, AS BASIC CONSTITUENTS, 70 % OR MORE BY WEIGHT\nOF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS CONTAINING\nPETROLEUM OILS OR OILS OBTAINED FROM BITUMINOUS MINERALS: OTHER",
+        "hsn_code": "34031900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LUBRICATING PREPARATIONS (INCLUDING CUTTING-OIL PREPARATIONS, BOLT OR NUT RELEASE\nPREPARATIONS,ANTI-RUST OR ANTI-CORROSION PREPARATIONS AND MOULD RELEASE\nPREPARATIONS, BASED ON LUBRICANTS) AND PREPARATIONS OF A KIND USED FOR THE OIL OR\nGREASE TREATMENT OF TEXTILE MATERIALS, LEATHER, FURSKINS OR OTHER MATERIALS, BUT\nEXCLUDING PREPARATIONS CONTAINING, AS BASIC CONSTITUENTS, 70 % OR MORE BY WEIGHT\nOF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS CONTAINING\nPETROLEUM OILS OR OILS OBTAINED FROM BITUMINOUS MINERALS: PREPARATIONS FOR THE\nTREATMENT OF TEXTILE MATERIALS, LEATHER, FURSKINS OR OTHER MATERIALS",
+        "hsn_code": "34031100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS, WASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING PREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401",
+        "hsn_code": "3402",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nOTHER: OTHER: OTHER",
+        "hsn_code": "34029099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nOTHER: OTHER: CLEANING OR DEGREASING PREPARATIONS NOT HAVING A BASIS OF SOAP OR\nOTHER ORGANIC SURFACE ACTIVE AGENTS",
+        "hsn_code": "34029092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nOTHER: OTHER: WASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS)\nAND CLEANING PREPARATIONS, HAVING A BASIS OF SOAP OR OTHER ORGANIC SURFACE ACTIVE\nAGENTS",
+        "hsn_code": "34029091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nOTHER: WASHING PREPARATIONS WHETHER OR NOT CONTAINING SOAP : OTHER",
+        "hsn_code": "34029059",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nOTHER: WASHING PREPARATIONS WHETHER OR NOT CONTAINING SOAP : CLEANING OR\nDEGREASING PREPARATIONS NOT HAVING A BASIS OF SOAP OR OTHER ORGANIC SURFACE\nACTIVE AGENTS",
+        "hsn_code": "34029052",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nOTHER: WASHING PREPARATIONS WHETHER OR NOT CONTAINING SOAP : WASHING\nPREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, HAVING A BASIS OF SOAP OR OTHER ORGANIC SURFACE ACTIVE AGENTS",
+        "hsn_code": "34029051",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nOTHER: WETTING AGENTS: OTHER",
+        "hsn_code": "34029049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nOTHER: WETTING AGENTS: CLEANING OR DEGREASING PREPARATIONS NOT HAVING A BASIS OF\nSOAP OR OTHER ORGANIC SURFACE ACTIVE AGENTS",
+        "hsn_code": "34029042",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nOTHER: WETTING AGENTS: WASHING PREPARATIONS (INCLUDING AUXILIARY WASHING\nPREPARATIONS) AND CLEANING PREPARATIONS, HAVING A BASIS OF SOAP OR OTHER ORGANIC\nSURFACE ACTIVE AGENTS",
+        "hsn_code": "34029041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nOTHER: PENETRATORS",
+        "hsn_code": "34029030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nOTHER: SULPHONATED OR SULPHATED OR OXIDIZED OR CHLORINATED CASTOR OIL;\nSULPHONATED OR SULPHATED OR OXIDIZED OR CHLORINATED FISH OIL; SULPHONATED OR\nSULPHATED OR OXIDIZED OR CHLORINATED SPERM OIL; SULPHONATED OR SULPHATED OR\nOXIDIZED OR CHLORINATED NEATS FOOT OIL",
+        "hsn_code": "34029020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nOTHER: SYNTHETIC DETERGENTS: OTHER",
+        "hsn_code": "34029019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nOTHER: SYNTHETIC DETERGENTS: CLEANING OR DEGREASING PREPARATIONS NOT HAVING A\nBASIS OF SOAP OR OTHER ORGANIC SURFACE ACTIVE AGENTS",
+        "hsn_code": "34029012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nOTHER: SYNTHETIC DETERGENTS: WASHING PREPARATIONS (INCLUDING AUXILIARY WASHING\nPREPARATIONS) AND CLEANING PREPARATIONS, HAVING A BASIS OF SOAP OR OTHER ORGANIC\nSURFACE ACTIVE AGENTS",
+        "hsn_code": "34029011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nPREPARATIONS PUT UP FOR RETAIL SALE: OTHER",
+        "hsn_code": "34022090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nPREPARATIONS PUT UP FOR RETAIL SALE: CLEANING OR DEGREASING PREPARATIONS NOT\nHAVING A BASIS OF SOAP OR OTHER ORGANIC SURFACE ACTIVE AGENTS",
+        "hsn_code": "34022020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nPREPARATIONS PUT UP FOR RETAIL SALE: WASHING PREPARATIONS (INCLUDING AUXILIARY\nWASHING PREPARATIONS) AND CLEANING PREPARATIONS, HAVING A BASIS OF SOAP OR OTHER\nORGANIC SURFACE ACTIVE AGENTS",
+        "hsn_code": "34022010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nORGANIC SURFACE-ACTIVE AGENTS, WHETHER OR NOT PUT UP FOR RETAIL SALE: OTHER",
+        "hsn_code": "34021900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nORGANIC SURFACE-ACTIVE AGENTS, WHETHER OR NOT PUT UP FOR RETAIL SALE: NON-IONIC",
+        "hsn_code": "34021300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nORGANIC SURFACE-ACTIVE AGENTS, WHETHER OR NOT PUT UP FOR RETAIL SALE: CATIONIC",
+        "hsn_code": "34021200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nORGANIC SURFACE-ACTIVE AGENTS, WHETHER OR NOT PUT UP FOR RETAIL SALE: ANIONIC:\nOTHER",
+        "hsn_code": "34021190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC SURFACE-ACTIVE AGENTS (OTHER THAN SOAP), SURFACE-ACTIVE PREPARATIONS,\nWASHING PREPARATIONS (INCLUDING AUXILIARY WASHING PREPARATIONS) AND CLEANING\nPREPARATIONS, WHETHER OR NOT CONTAINING SOAP, OTHER THAN THOSE OF HEADING 3401\nORGANIC SURFACE-ACTIVE AGENTS, WHETHER OR NOT PUT UP FOR RETAIL SALE: ANIONIC:\nSILICONE SURFACTANT",
+        "hsn_code": "34021110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOAP; ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR USE AS SOAP, IN THE FORM OF BARS, CAKES, MOULDED PIECES OR SHAPES, WHETHER OR NOT CONTAINING SOAP; ORGANIC SURFACE ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING THE SKIN, IN THE FORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE, WHETHER OR NOT CONTAINING SOAP; PAPER, WADDING, FELT AND NONWOVENS, IMPREGNATED, COATED OR COVERED WITH SOAP OR DETERGENT",
+        "hsn_code": "3401",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOAP; ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR USE AS SOAP, IN THE\nFORM OF BARS,CAKES, MOULDED PIECES OR SHAPES, WHETHER OR NOT CONTAINING SOAP;\nORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING THE SKIN, IN THE\nFORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE, WHETHER OR NOT CONTAINING\nSOAP; PAPER, WADDING, FELT AND NONWOVENS, IMPREGNATED, COATED OR COVERED WITH\nSOAP OR DETERGENT ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING\nTHE SKIN, IN THE FORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE,WHETHER OR NOT\nCONTAINING SOAP : OTHER",
+        "hsn_code": "34013090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOAP; ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR USE AS SOAP, IN THE\nFORM OF BARS,CAKES, MOULDED PIECES OR SHAPES, WHETHER OR NOT CONTAINING SOAP;\nORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING THE SKIN, IN THE\nFORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE, WHETHER OR NOT CONTAINING\nSOAP; PAPER, WADDING, FELT AND NONWOVENS, IMPREGNATED, COATED OR COVERED WITH\nSOAP OR DETERGENT ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING\nTHE SKIN, IN THE FORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE,WHETHER OR NOT\nCONTAINING SOAP : FOR TOILET USE (INCLUDING MEDICATED PRODUCTS): OTHER",
+        "hsn_code": "34013019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOAP; ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR USE AS SOAP, IN THE\nFORM OF BARS,CAKES, MOULDED PIECES OR SHAPES, WHETHER OR NOT CONTAINING SOAP;\nORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING THE SKIN, IN THE\nFORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE, WHETHER OR NOT CONTAINING\nSOAP; PAPER, WADDING, FELT AND NONWOVENS, IMPREGNATED, COATED OR COVERED WITH\nSOAP OR DETERGENT ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING\nTHE SKIN, IN THE FORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE,WHETHER OR NOT\nCONTAINING SOAP : FOR TOILET USE (INCLUDING MEDICATED PRODUCTS): SHAVING CREAM\nAND SHAVING GEL",
+        "hsn_code": "34013012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOAP; ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR USE AS SOAP, IN THE\nFORM OF BARS,CAKES, MOULDED PIECES OR SHAPES, WHETHER OR NOT CONTAINING SOAP;\nORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING THE SKIN, IN THE\nFORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE, WHETHER OR NOT CONTAINING\nSOAP; PAPER, WADDING, FELT AND NONWOVENS, IMPREGNATED, COATED OR COVERED WITH\nSOAP OR DETERGENT ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING\nTHE SKIN, IN THE FORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE,WHETHER OR NOT\nCONTAINING SOAP : FOR TOILET USE (INCLUDING MEDICATED PRODUCTS): MEDICATED TOILET\nSOAPS",
+        "hsn_code": "34013011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOAP; ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR USE AS SOAP, IN THE\nFORM OF BARS,CAKES, MOULDED PIECES OR SHAPES, WHETHER OR NOT CONTAINING SOAP;\nORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING THE SKIN, IN THE\nFORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE, WHETHER OR NOT CONTAINING\nSOAP; PAPER, WADDING, FELT AND NONWOVENS, IMPREGNATED, COATED OR COVERED WITH\nSOAP OR DETERGENT SOAP IN OTHER FORMS",
+        "hsn_code": "34012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOAP; ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR USE AS SOAP, IN THE\nFORM OF BARS,CAKES, MOULDED PIECES OR SHAPES, WHETHER OR NOT CONTAINING SOAP;\nORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING THE SKIN, IN THE\nFORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE, WHETHER OR NOT CONTAINING\nSOAP; PAPER, WADDING, FELT AND NONWOVENS, IMPREGNATED, COATED OR COVERED WITH\nSOAP OR DETERGENT SOAP AND ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS, IN\nTHE FORM OF BARS, CAKES,MOULDED PIECES OR SHAPES, AND PAPER,WADDING, FELT AND\nNONWOVENS, IMPREGNATED,COATED OR COVERED WITH SOAP OR DETERGENT: OTHER: OTHER",
+        "hsn_code": "34011990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOAP; ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR USE AS SOAP, IN THE\nFORM OF BARS,CAKES, MOULDED PIECES OR SHAPES, WHETHER OR NOT CONTAINING SOAP;\nORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING THE SKIN, IN THE\nFORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE, WHETHER OR NOT CONTAINING\nSOAP; PAPER, WADDING, FELT AND NONWOVENS, IMPREGNATED, COATED OR COVERED WITH\nSOAP OR DETERGENT SOAP AND ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS, IN\nTHE FORM OF BARS, CAKES,MOULDED PIECES OR SHAPES, AND PAPER,WADDING, FELT AND\nNONWOVENS, IMPREGNATED,COATED OR COVERED WITH SOAP OR DETERGENT: OTHER:\nHOUSEHOLD AND LAUNDRY SOAPS NOT ELSEWHERE SPECIFIED OR INCLUDED LAUNDRY SOAPS",
+        "hsn_code": "34011942",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOAP; ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR USE AS SOAP, IN THE\nFORM OF BARS,CAKES, MOULDED PIECES OR SHAPES, WHETHER OR NOT CONTAINING SOAP;\nORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING THE SKIN, IN THE\nFORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE, WHETHER OR NOT CONTAINING\nSOAP; PAPER, WADDING, FELT AND NONWOVENS, IMPREGNATED, COATED OR COVERED WITH\nSOAP OR DETERGENT SOAP AND ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS, IN\nTHE FORM OF BARS, CAKES,MOULDED PIECES OR SHAPES, AND PAPER,WADDING, FELT AND\nNONWOVENS, IMPREGNATED,COATED OR COVERED WITH SOAP OR DETERGENT: OTHER:\nHOUSEHOLD AND LAUNDRY SOAPS NOT ELSEWHERE SPECIFIED OR INCLUDED HOUSEHOLD\nSOAPS",
+        "hsn_code": "34011941",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOAP; ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR USE AS SOAP, IN THE\nFORM OF BARS,CAKES, MOULDED PIECES OR SHAPES, WHETHER OR NOT CONTAINING SOAP;\nORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING THE SKIN, IN THE\nFORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE, WHETHER OR NOT CONTAINING\nSOAP; PAPER, WADDING, FELT AND NONWOVENS, IMPREGNATED, COATED OR COVERED WITH\nSOAP OR DETERGENT SOAP AND ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS, IN\nTHE FORM OF BARS, CAKES,MOULDED PIECES OR SHAPES, AND PAPER,WADDING, FELT AND\nNONWOVENS, IMPREGNATED,COATED OR COVERED WITH SOAP OR DETERGENT: OTHER:\nTABLETS AND CAKES",
+        "hsn_code": "34011930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOAP; ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR USE AS SOAP, IN THE\nFORM OF BARS,CAKES, MOULDED PIECES OR SHAPES, WHETHER OR NOT CONTAINING SOAP;\nORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING THE SKIN, IN THE\nFORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE, WHETHER OR NOT CONTAINING\nSOAP; PAPER, WADDING, FELT AND NONWOVENS, IMPREGNATED, COATED OR COVERED WITH\nSOAP OR DETERGENT SOAP AND ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS, IN\nTHE FORM OF BARS, CAKES,MOULDED PIECES OR SHAPES, AND PAPER,WADDING, FELT AND\nNONWOVENS, IMPREGNATED,COATED OR COVERED WITH SOAP OR DETERGENT: OTHER:\nFLAKES, CHIPS AND POWDER",
+        "hsn_code": "34011920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOAP; ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR USE AS SOAP, IN THE\nFORM OF BARS,CAKES, MOULDED PIECES OR SHAPES, WHETHER OR NOT CONTAINING SOAP;\nORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING THE SKIN, IN THE\nFORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE, WHETHER OR NOT CONTAINING\nSOAP; PAPER, WADDING, FELT AND NONWOVENS, IMPREGNATED, COATED OR COVERED WITH\nSOAP OR DETERGENT SOAP AND ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS, IN\nTHE FORM OF BARS, CAKES,MOULDED PIECES OR SHAPES, AND PAPER,WADDING, FELT AND\nNONWOVENS, IMPREGNATED,COATED OR COVERED WITH SOAP OR DETERGENT: OTHER: BARS\nAND BLOCKS OF NOT LESS THAN 500 GM IN WEIGHT: OTHER",
+        "hsn_code": "34011919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOAP; ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR USE AS SOAP, IN THE\nFORM OF BARS,CAKES, MOULDED PIECES OR SHAPES, WHETHER OR NOT CONTAINING SOAP;\nORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING THE SKIN, IN THE\nFORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE, WHETHER OR NOT CONTAINING\nSOAP; PAPER, WADDING, FELT AND NONWOVENS, IMPREGNATED, COATED OR COVERED WITH\nSOAP OR DETERGENT SOAP AND ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS, IN\nTHE FORM OF BARS, CAKES,MOULDED PIECES OR SHAPES, AND PAPER,WADDING, FELT AND\nNONWOVENS, IMPREGNATED,COATED OR COVERED WITH SOAP OR DETERGENT: OTHER: BARS\nAND BLOCKS OF NOT LESS THAN 500 GM IN WEIGHT: INDUSTRIAL SOAP",
+        "hsn_code": "34011911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOAP; ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR USE AS SOAP, IN THE\nFORM OF BARS,CAKES, MOULDED PIECES OR SHAPES, WHETHER OR NOT CONTAINING SOAP;\nORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING THE SKIN, IN THE\nFORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE, WHETHER OR NOT CONTAINING\nSOAP; PAPER, WADDING, FELT AND NONWOVENS, IMPREGNATED, COATED OR COVERED WITH\nSOAP OR DETERGENT SOAP AND ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS, IN\nTHE FORM OF BARS, CAKES,MOULDED PIECES OR SHAPES, AND PAPER,WADDING, FELT AND\nNONWOVENS, IMPREGNATED,COATED OR COVERED WITH SOAP OR DETERGENT: FOR TOILET\nUSE (INCLUDING MEDICATED PRODUCTS): OTHER",
+        "hsn_code": "34011190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOAP; ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR USE AS SOAP, IN THE\nFORM OF BARS,CAKES, MOULDED PIECES OR SHAPES, WHETHER OR NOT CONTAINING SOAP;\nORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING THE SKIN, IN THE\nFORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE, WHETHER OR NOT CONTAINING\nSOAP; PAPER, WADDING, FELT AND NONWOVENS, IMPREGNATED, COATED OR COVERED WITH\nSOAP OR DETERGENT SOAP AND ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS, IN\nTHE FORM OF BARS, CAKES,MOULDED PIECES OR SHAPES, AND PAPER,WADDING, FELT AND\nNONWOVENS, IMPREGNATED,COATED OR COVERED WITH SOAP OR DETERGENT: FOR TOILET\nUSE (INCLUDING MEDICATED PRODUCTS): SHAVING SOAPS OTHER THAN SHAVING CREAM",
+        "hsn_code": "34011120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOAP; ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR USE AS SOAP, IN THE\nFORM OF BARS,CAKES, MOULDED PIECES OR SHAPES, WHETHER OR NOT CONTAINING SOAP;\nORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS FOR WASHING THE SKIN, IN THE\nFORM OF LIQUID OR CREAM AND PUT UP FOR RETAIL SALE, WHETHER OR NOT CONTAINING\nSOAP; PAPER, WADDING, FELT AND NONWOVENS, IMPREGNATED, COATED OR COVERED WITH\nSOAP OR DETERGENT SOAP AND ORGANIC SURFACE-ACTIVE PRODUCTS AND PREPARATIONS, IN\nTHE FORM OF BARS, CAKES,MOULDED PIECES OR SHAPES, AND PAPER,WADDING, FELT AND\nNONWOVENS, IMPREGNATED,COATED OR COVERED WITH SOAP OR DETERGENT: FOR TOILET\nUSE (INCLUDING MEDICATED PRODUCTS): MEDICATED TOILET SOAPS",
+        "hsn_code": "34011110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRE-SHAVE, SHAVING OR AFTER-SHAVE PREPARATIONS, PERSONAL DEODORANTS, BATH PREPARATIONS, DEPILATORIES AND OTHER PERFUMERY, COSMETIC OR TOILET PREPARATIONS, NOT ELSEWHERE SPECIFIED OR INCLUDED, PREPARED ROOM DEODORISERS, WHETHER OR NOT PERFUMED OR HAVING DISINFECTANT PROPERTIES",
+        "hsn_code": "3307",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRE-SHAVE, SHAVING OR AFTER-SHAVE PREPARATIONS, PERSONAL DEODORANTS, BATH\nPREPARATIONS, DEPILATORIES AND OTHER PERFUMERY, COSMETIC OR TOILET PREPARATIONS,\nNOT ELSEWHERE SPECIFIED OR INCLUDED, PREPARED ROOM DEODORISERS, WHETHER OR NOT\nPERFUMED OR HAVING DISINFECTANT PROPERTIES - OTHER: OTHER",
+        "hsn_code": "33079090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRE-SHAVE, SHAVING OR AFTER-SHAVE PREPARATIONS, PERSONAL DEODORANTS, BATH\nPREPARATIONS, DEPILATORIES AND OTHER PERFUMERY, COSMETIC OR TOILET PREPARATIONS,\nNOT ELSEWHERE SPECIFIED OR INCLUDED, PREPARED ROOM DEODORISERS, WHETHER OR NOT\nPERFUMED OR HAVING DISINFECTANT PROPERTIES - OTHER: STERILE CONTACT LENS CARE\nSOLUTION",
+        "hsn_code": "33079020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRE-SHAVE, SHAVING OR AFTER-SHAVE PREPARATIONS, PERSONAL DEODORANTS, BATH\nPREPARATIONS, DEPILATORIES AND OTHER PERFUMERY, COSMETIC OR TOILET PREPARATIONS,\nNOT ELSEWHERE SPECIFIED OR INCLUDED, PREPARED ROOM DEODORISERS, WHETHER OR NOT\nPERFUMED OR HAVING DISINFECTANT PROPERTIES - OTHER: DEPILATORIES",
+        "hsn_code": "33079010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRE-SHAVE, SHAVING OR AFTER-SHAVE PREPARATIONS, PERSONAL DEODORANTS, BATH\nPREPARATIONS, DEPILATORIES AND OTHER PERFUMERY, COSMETIC OR TOILET PREPARATIONS,\nNOT ELSEWHERE SPECIFIED OR INCLUDED, PREPARED ROOM DEODORISERS, WHETHER OR NOT\nPERFUMED OR HAVING DISINFECTANT PROPERTIES PREPARATIONS FOR PERFUMING OR\nDEODORIZING ROOMS, INCLUDING ODORIFEROUS PREPARATIONS USED DURING RELIGIOUS\nRITES: OTHER",
+        "hsn_code": "33074900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRE-SHAVE, SHAVING OR AFTER-SHAVE PREPARATIONS, PERSONAL DEODORANTS, BATH\nPREPARATIONS, DEPILATORIES AND OTHER PERFUMERY, COSMETIC OR TOILET PREPARATIONS,\nNOT ELSEWHERE SPECIFIED OR INCLUDED, PREPARED ROOM DEODORISERS, WHETHER OR NOT\nPERFUMED OR HAVING DISINFECTANT PROPERTIES PERFUMED BATH SALTS AND OTHER BATH\nPREPARATIONS: OTHER",
+        "hsn_code": "33073090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRE-SHAVE, SHAVING OR AFTER-SHAVE PREPARATIONS, PERSONAL DEODORANTS, BATH\nPREPARATIONS, DEPILATORIES AND OTHER PERFUMERY, COSMETIC OR TOILET PREPARATIONS,\nNOT ELSEWHERE SPECIFIED OR INCLUDED, PREPARED ROOM DEODORISERS, WHETHER OR NOT\nPERFUMED OR HAVING DISINFECTANT PROPERTIES PERFUMED BATH SALTS AND OTHER BATH\nPREPARATIONS: BATH OIL (THAILAM)",
+        "hsn_code": "33073010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRE-SHAVE, SHAVING OR AFTER-SHAVE PREPARATIONS, PERSONAL DEODORANTS, BATH\nPREPARATIONS, DEPILATORIES AND OTHER PERFUMERY, COSMETIC OR TOILET PREPARATIONS,\nNOT ELSEWHERE SPECIFIED OR INCLUDED, PREPARED ROOM DEODORISERS, WHETHER OR NOT\nPERFUMED OR HAVING DISINFECTANT PROPERTIES PERSONAL DEODORANTS AND ANTI\nPERSPIRANTS",
+        "hsn_code": "33072000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRE-SHAVE, SHAVING OR AFTER-SHAVE PREPARATIONS, PERSONAL DEODORANTS, BATH\nPREPARATIONS, DEPILATORIES AND OTHER PERFUMERY, COSMETIC OR TOILET PREPARATIONS,\nNOT ELSEWHERE SPECIFIED OR INCLUDED, PREPARED ROOM DEODORISERS, WHETHER OR NOT\nPERFUMED OR HAVING DISINFECTANT PROPERTIES PRE-SHAVE, SHAVING OR AFTER-SHAVE\nPREPARATIONS: OTHER",
+        "hsn_code": "33071090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRE-SHAVE, SHAVING OR AFTER-SHAVE PREPARATIONS, PERSONAL DEODORANTS, BATH\nPREPARATIONS, DEPILATORIES AND OTHER PERFUMERY, COSMETIC OR TOILET PREPARATIONS,\nNOT ELSEWHERE SPECIFIED OR INCLUDED, PREPARED ROOM DEODORISERS, WHETHER OR NOT\nPERFUMED OR HAVING DISINFECTANT PROPERTIES PRE-SHAVE, SHAVING OR AFTER-SHAVE\nPREPARATIONS: SHAVING CREAM",
+        "hsn_code": "33071010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PERFUMES AND TOILET WATERS",
+        "hsn_code": "3303",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PERFUMES AND TOILET WATERS - PERFUMES AND TOILET WATERS: OTHER",
+        "hsn_code": "33030090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PERFUMES AND TOILET WATERS - PERFUMES AND TOILET WATERS: SPIRITUOUS TOILET\nPREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "33030060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PERFUMES AND TOILET WATERS - PERFUMES AND TOILET WATERS: PERFUMES CONTAINING\nSPIRIT",
+        "hsn_code": "33030050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PERFUMES AND TOILET WATERS - PERFUMES AND TOILET WATERS: PERFUMES AND PERFUMERY\nCOMPOUNDS NOT CONTAINING SPIRIT (EXCLUDING AQUEOUS DISTILLATES)",
+        "hsn_code": "33030040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PERFUMES AND TOILET WATERS - PERFUMES AND TOILET WATERS: KEORA WATER",
+        "hsn_code": "33030030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PERFUMES AND TOILET WATERS - PERFUMES AND TOILET WATERS: ROSE WATER",
+        "hsn_code": "33030020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PERFUMES AND TOILET WATERS - PERFUMES AND TOILET WATERS: EAU-DE-COLOGNE",
+        "hsn_code": "33030010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MIXTURES OF ODORIFEROUS SUBSTANCES AND MIXTURES (INCLUDING ALCOHOLIC SOLUTIONS) WITH A BASIS OF ONE OR MORE OF THESE SUBSTANCES, OF A KIND USED AS RAW MATERIALS IN INDUSTRY; OTHER PREPARATIONS BASED ON ODORIFEROUS SUBSTANCES, OF A KIND USED FOR THE MANUFACTURE OF BEVERAGES",
+        "hsn_code": "3302",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MIXTURES OF ODORIFEROUS SUBSTANCES AND MIXTURES (INCLUDING ALCOHOLIC SOLUTIONS)\nWITH A BASIS OF ONE OR MORE OF THESE SUBSTANCES, OF A KIND USED AS RAW MATERIALS IN\nINDUSTRY; OTHER PREPARATIONS BASED ON ODORIFEROUS SUBSTANCES, OF A KIND USED FOR\nTHE MANUFACTURE OF BEVERAGES OTHER:OTHER",
+        "hsn_code": "33029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MIXTURES OF ODORIFEROUS SUBSTANCES AND MIXTURES (INCLUDING ALCOHOLIC SOLUTIONS)\nWITH A BASIS OF ONE OR MORE OF THESE SUBSTANCES, OF A KIND USED AS RAW MATERIALS IN\nINDUSTRY; OTHER PREPARATIONS BASED ON ODORIFEROUS SUBSTANCES, OF A KIND USED FOR\nTHE MANUFACTURE OF BEVERAGES OTHER:ALEURITIC ACID",
+        "hsn_code": "33029020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MIXTURES OF ODORIFEROUS SUBSTANCES AND MIXTURES (INCLUDING ALCOHOLIC SOLUTIONS)\nWITH A BASIS OF ONE OR MORE OF THESE SUBSTANCES, OF A KIND USED AS RAW MATERIALS IN\nINDUSTRY; OTHER PREPARATIONS BASED ON ODORIFEROUS SUBSTANCES, OF A KIND USED FOR\nTHE MANUFACTURE OF BEVERAGES OTHER: MIXTURES OF AROMATIC CHEMICALS AND\nESSENTIAL OILS AS PERFUME BASE: OTHER",
+        "hsn_code": "33029019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MIXTURES OF ODORIFEROUS SUBSTANCES AND MIXTURES (INCLUDING ALCOHOLIC SOLUTIONS)\nWITH A BASIS OF ONE OR MORE OF THESE SUBSTANCES, OF A KIND USED AS RAW MATERIALS IN\nINDUSTRY; OTHER PREPARATIONS BASED ON ODORIFEROUS SUBSTANCES, OF A KIND USED FOR\nTHE MANUFACTURE OF BEVERAGES OTHER: MIXTURES OF AROMATIC CHEMICALS AND\nESSENTIAL OILS AS PERFUME BASE: SYNTHETIC ESSENTIAL OIL",
+        "hsn_code": "33029012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MIXTURES OF ODORIFEROUS SUBSTANCES AND MIXTURES (INCLUDING ALCOHOLIC SOLUTIONS)\nWITH A BASIS OF ONE OR MORE OF THESE SUBSTANCES, OF A KIND USED AS RAW MATERIALS IN\nINDUSTRY; OTHER PREPARATIONS BASED ON ODORIFEROUS SUBSTANCES, OF A KIND USED FOR\nTHE MANUFACTURE OF BEVERAGES OTHER: MIXTURES OF AROMATIC CHEMICALS AND\nESSENTIAL OILS AS PERFUME BASE: SYNTHETIC PERFUMERY COMPOUNDS",
+        "hsn_code": "33029011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MIXTURES OF ODORIFEROUS SUBSTANCES AND MIXTURES (INCLUDING ALCOHOLIC SOLUTIONS)\nWITH A BASIS OF ONE OR MORE OF THESE SUBSTANCES, OF A KIND USED AS RAW MATERIALS IN\nINDUSTRY; OTHER PREPARATIONS BASED ON ODORIFEROUS SUBSTANCES, OF A KIND USED FOR\nTHE MANUFACTURE OF BEVERAGES OF A KIND USED IN THE FOOD OR DRINK INDUSTRIES:\nOTHER",
+        "hsn_code": "33021090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MIXTURES OF ODORIFEROUS SUBSTANCES AND MIXTURES(INCLUDING ALCOHOLIC SOLUTIONS)\nWITH A BASIS OFONE OR MORE OF THESE SUBSTANCES, OF A KIND USEDAS RAW MATERIALS IN\nINDUSTRY; OTHER PREPARATIONS BASED ON ODORIFEROUS SUBSTANCES, OF A KIND USED FOR\nTHE MANUFACTURE OF BEVERAGES OF A KIND USED IN THE FOOD OR DRINK INDUSTRIES:\nSYNTHETIC FLAVOURING ESSENCES",
+        "hsn_code": "33021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS; EXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES OR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE DETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF ESSENTIAL OILS",
+        "hsn_code": "3301",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: OTHER",
+        "hsn_code": "33019090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: AQUEOUS SOLUTIONS OF ESSENTIAL OILS: OTHER",
+        "hsn_code": "33019079",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: AQUEOUS SOLUTIONS OF ESSENTIAL OILS: FLAVOURING ESSENCES, ALL\nTYPES, INCLUDING THOSE FOR LIQUORS",
+        "hsn_code": "33019071",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: AQUEOUS DISTILLATES OF ESSENTIAL OILS, NOT ELSEWHERE SPECIFIED\nOR INCLUDED",
+        "hsn_code": "33019060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: TERPENIC BY-PRODUCTS OF THE DETERPENATION OF ESSENTIAL OILS:\nOTHER",
+        "hsn_code": "33019059",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: TERPENIC BY-PRODUCTS OF THE DETERPENATION OF ESSENTIAL OILS:\nFLAVOURING ESSENCES, ALL TYPES, INCLUDING THOSE FOR LIQUORS",
+        "hsn_code": "33019051",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS OR IN WAXES\nOR THE LIKE, OBTAINED BY COLD ABSORPTION OR BY MACERATION NOT ELSEWHERE SPECIFIED\nOR INCLUDED: OTHER",
+        "hsn_code": "33019049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS OR IN WAXES\nOR THE LIKE, OBTAINED BY COLD ABSORPTION OR BY MACERATION NOT ELSEWHERE SPECIFIED\nOR INCLUDED: FLAVOURING ESSENCES, ALL TYPES, INCLUDING THOSE FOR LIQUORS",
+        "hsn_code": "33019041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: ATTARS OF ALL KINDS IN FIXED OIL BASE; MUSTARD OIL AROMA\nESSENCE OF AMBRETTOLIDE (AMBRETTE SEED OIL ESSENCE): ESSENCE OF AMBRETTOLIDE\n(AMBRETTE SEED OIL ESSENCE)",
+        "hsn_code": "33019033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: ATTARS OF ALL KINDS IN FIXED OIL BASE; MUSTARD OIL AROMA\nESSENCE OF AMBRETTOLIDE (AMBRETTE SEED OIL ESSENCE): MUSTARD OIL AROMA",
+        "hsn_code": "33019032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: ATTARS OF ALL KINDS IN FIXED OIL BASE; MUSTARD OIL AROMA\nESSENCE OF AMBRETTOLIDE (AMBRETTE SEED OIL ESSENCE): ATTARS OF ALL KINDS IN FIXED OIL\nBASE",
+        "hsn_code": "33019031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: CLOVE, CAPSICUM, CORIANDER, CUMIN AND FENNEL OLEOREINS AND\nOLEORESINS OF SPICES NOT ELSEWHERE SPECIFIED OR INCLUDED: OLEORESINS OF SPICES NOT\nELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "33019029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: CLOVE, CAPSICUM, CORIANDER, CUMIN AND FENNEL OLEOREINS AND\nOLEORESINS OF SPICES NOT ELSEWHERE SPECIFIED OR INCLUDED: FENNEL OLEORESINS",
+        "hsn_code": "33019025",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: CLOVE, CAPSICUM, CORIANDER, CUMIN AND FENNEL OLEOREINS AND\nOLEORESINS OF SPICES NOT ELSEWHERE SPECIFIED OR INCLUDED: CUMIN OLEORESINS",
+        "hsn_code": "33019024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: CLOVE, CAPSICUM, CORIANDER, CUMIN AND FENNEL OLEOREINS AND\nOLEORESINS OF SPICES NOT ELSEWHERE SPECIFIED OR INCLUDED: CORIANDER OLEORESINS",
+        "hsn_code": "33019023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: CLOVE, CAPSICUM, CORIANDER, CUMIN AND FENNEL OLEOREINS AND\nOLEORESINS OF SPICES NOT ELSEWHERE SPECIFIED OR INCLUDED: CAPSICUM OLEORESINS",
+        "hsn_code": "33019022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: CLOVE, CAPSICUM, CORIANDER, CUMIN AND FENNEL OLEOREINS AND\nOLEORESINS OF SPICES NOT ELSEWHERE SPECIFIED OR INCLUDED: CLOVE OLEORESINS",
+        "hsn_code": "33019021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: FENUGREEK, GINGER, PEPPER, TURMERIC, CARDAMOM, CELERY SEED\nAND NUTMEG OLEORESINS: NUTMEG OLEORESINS",
+        "hsn_code": "33019017",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: FENUGREEK, GINGER, PEPPER, TURMERIC, CARDAMOM, CELERY SEED\nAND NUTMEG OLEORESINS: CELERY SEED OLEORESINS",
+        "hsn_code": "33019016",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: FENUGREEK, GINGER, PEPPER, TURMERIC, CARDAMOM, CELERY SEED\nAND NUTMEG OLEORESINS: CARDAMOM OLEORESINS",
+        "hsn_code": "33019015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: FENUGREEK, GINGER, PEPPER, TURMERIC, CARDAMOM, CELERY SEED\nAND NUTMEG OLEORESINS: TURMERIC OLEORESINS",
+        "hsn_code": "33019014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: FENUGREEK, GINGER, PEPPER, TURMERIC, CARDAMOM, CELERY SEED\nAND NUTMEG OLEORESINS: PEPPER OLEORESINS",
+        "hsn_code": "33019013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: FENUGREEK, GINGER, PEPPER, TURMERIC, CARDAMOM, CELERY SEED\nAND NUTMEG OLEORESINS: GINGER OLEORESINS",
+        "hsn_code": "33019012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS OTHER: FENUGREEK, GINGER, PEPPER, TURMERIC, CARDAMOM, CELERY SEED\nAND NUTMEG OLEORESINS: FENUGREEK OLEORESINS",
+        "hsn_code": "33019011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS RESINOIDS: OTHER: OTHER",
+        "hsn_code": "33013099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS RESINOIDS: OTHER: FLAVOURING ESSENCES, ALL TYPES, INCLUDING THOSE FOR\nLIQUORS",
+        "hsn_code": "33013091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS RESINOIDS: AGAR OIL",
+        "hsn_code": "33013010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: OTHER",
+        "hsn_code": "33012990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: SPICES OILS NOT\nELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "33012950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: CAMPHOR OIL;\nLEMON GRASS OIL; YLANG YLANG OIL; DAVANA OIL; CUMIN OIL; CELERY SEED OIL, GARLIC OIL,\nPAPRIKA OIL, TURMERIC OIL : TURMERIC OIL",
+        "hsn_code": "33012949",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: CAMPHOR OIL;\nLEMON GRASS OIL; YLANG YLANG OIL; DAVANA OIL; CUMIN OIL; CELERY SEED OIL, GARLIC OIL,\nPAPRIKA OIL, TURMERIC OIL : PAPRIKA OIL",
+        "hsn_code": "33012948",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: CAMPHOR OIL;\nLEMON GRASS OIL; YLANG YLANG OIL; DAVANA OIL; CUMIN OIL; CELERY SEED OIL, GARLIC OIL,\nPAPRIKA OIL, TURMERIC OIL : GARLIC OIL",
+        "hsn_code": "33012947",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: CAMPHOR OIL;\nLEMON GRASS OIL; YLANG YLANG OIL; DAVANA OIL; CUMIN OIL; CELERY SEED OIL, GARLIC OIL,\nPAPRIKA OIL, TURMERIC OIL : CELERY SEED OIL",
+        "hsn_code": "33012946",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: CAMPHOR OIL;\nLEMON GRASS OIL; YLANG YLANG OIL; DAVANA OIL; CUMIN OIL; CELERY SEED OIL, GARLIC OIL,\nPAPRIKA OIL, TURMERIC OIL : CUMIN OIL",
+        "hsn_code": "33012945",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: CAMPHOR OIL;\nLEMON GRASS OIL; YLANG YLANG OIL; DAVANA OIL; CUMIN OIL; CELERY SEED OIL, GARLIC OIL,\nPAPRIKA OIL, TURMERIC OIL : DAVANA OIL",
+        "hsn_code": "33012944",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: CAMPHOR OIL;\nLEMON GRASS OIL; YLANG YLANG OIL; DAVANA OIL; CUMIN OIL; CELERY SEED OIL, GARLIC OIL,\nPAPRIKA OIL, TURMERIC OIL : YLANG YLANG OIL",
+        "hsn_code": "33012943",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: CAMPHOR OIL;\nLEMON GRASS OIL; YLANG YLANG OIL; DAVANA OIL; CUMIN OIL; CELERY SEED OIL, GARLIC OIL,\nPAPRIKA OIL, TURMERIC OIL : LEMON GRASS OIL",
+        "hsn_code": "33012942",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: CAMPHOR OIL;\nLEMON GRASS OIL; YLANG YLANG OIL; DAVANA OIL; CUMIN OIL; CELERY SEED OIL, GARLIC OIL,\nPAPRIKA OIL, TURMERIC OIL : CAMPHOR OIL; LEMON GRASS OIL; YLANG YLANG OIL; DAVANA\nOIL; CUMIN OIL; CELERY SEED OIL, GARLIC OIL, PAPRIKA OIL, TURMERIC OIL : CAMPHOR OIL",
+        "hsn_code": "33012941",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: TUBEROSE\nCONCENTRATE; NUTMEG OIL; PALMAROSA OIL; PATCHOULI OIL; PEPPER OIL; PETITGRAIN OIL;\nSANDALWOOD OIL; ROSE OIL: ROSE OIL",
+        "hsn_code": "33012938",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: TUBEROSE\nCONCENTRATE; NUTMEG OIL; PALMAROSA OIL; PATCHOULI OIL; PEPPER OIL; PETITGRAIN OIL;\nSANDALWOOD OIL; ROSE OIL: SANDALWOOD OIL",
+        "hsn_code": "33012937",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: TUBEROSE\nCONCENTRATE; NUTMEG OIL; PALMAROSA OIL; PATCHOULI OIL; PEPPER OIL; PETITGRAIN OIL;\nSANDALWOOD OIL; ROSE OIL: PETITGRAIN OIL",
+        "hsn_code": "33012936",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: TUBEROSE\nCONCENTRATE; NUTMEG OIL; PALMAROSA OIL; PATCHOULI OIL; PEPPER OIL; PETITGRAIN OIL;\nSANDALWOOD OIL; ROSE OIL: PEPPER OIL",
+        "hsn_code": "33012935",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: TUBEROSE\nCONCENTRATE; NUTMEG OIL; PALMAROSA OIL; PATCHOULI OIL; PEPPER OIL; PETITGRAIN OIL;\nSANDALWOOD OIL; ROSE OIL: PATCHOULI OIL",
+        "hsn_code": "33012934",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: TUBEROSE\nCONCENTRATE; NUTMEG OIL; PALMAROSA OIL; PATCHOULI OIL; PEPPER OIL; PETITGRAIN OIL;\nSANDALWOOD OIL; ROSE OIL: PALMAROSA OIL",
+        "hsn_code": "33012933",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: TUBEROSE\nCONCENTRATE; NUTMEG OIL; PALMAROSA OIL; PATCHOULI OIL; PEPPER OIL; PETITGRAIN OIL;\nSANDALWOOD OIL; ROSE OIL: NUTMEG OIL",
+        "hsn_code": "33012932",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: TUBEROSE\nCONCENTRATE; NUTMEG OIL; PALMAROSA OIL; PATCHOULI OIL; PEPPER OIL; PETITGRAIN OIL;\nSANDALWOOD OIL; ROSE OIL: TUBEROSE CONCENTRATE",
+        "hsn_code": "33012931",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: CLOVE LEAF OR\nSTEM OIL; CORIANDER SEED OIL; DILL OIL; EUCALYPTUS OIL; FENNEL SEED OIL; GINGER OIL;\nGINGER GRASS OIL; CLOVE BUD OIL: CLOVE BUD OIL",
+        "hsn_code": "33012928",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: CLOVE LEAF OR\nSTEM OIL; CORIANDER SEED OIL; DILL OIL; EUCALYPTUS OIL; FENNEL SEED OIL; GINGER OIL;\nGINGER GRASS OIL; CLOVE BUD OIL: GINGER GRASS OIL",
+        "hsn_code": "33012927",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: CLOVE LEAF OR\nSTEM OIL; CORIANDER SEED OIL; DILL OIL; EUCALYPTUS OIL; FENNEL SEED OIL; GINGER OIL;\nGINGER GRASS OIL; CLOVE BUD OIL: GINGER OIL",
+        "hsn_code": "33012926",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: CLOVE LEAF OR\nSTEM OIL; CORIANDER SEED OIL; DILL OIL; EUCALYPTUS OIL; FENNEL SEED OIL; GINGER OIL;\nGINGER GRASS OIL; CLOVE BUD OIL: FENNEL SEED OIL",
+        "hsn_code": "33012925",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER:CLOVE LEAF OR\nSTEM OIL; CORIANDER SEED OIL; DILL OIL; EUCALYPTUS OIL; FENNEL SEED OIL; GINGER OIL;\nGINGER GRASS OIL; CLOVE BUD OIL: EUCALYPTUS OIL",
+        "hsn_code": "33012924",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: CLOVE LEAF OR\nSTEM OIL; CORIANDER SEED OIL; DILL OIL; EUCALYPTUS OIL; FENNEL SEED OIL; GINGER OIL;\nGINGER GRASS OIL; CLOVE BUD OIL: DILL OIL (ANETHUM OIL)",
+        "hsn_code": "33012923",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: CLOVE LEAF OR\nSTEM OIL; CORIANDER SEED OIL; DILL OIL; EUCALYPTUS OIL; FENNEL SEED OIL; GINGER OIL;\nGINGER GRASS OIL; CLOVE BUD OIL: CORIANDER SEED OIL",
+        "hsn_code": "33012922",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: CLOVE LEAF OR\nSTEM OIL; CORIANDER SEED OIL; DILL OIL; EUCALYPTUS OIL; FENNEL SEED OIL; GINGER OIL;\nGINGER GRASS OIL; CLOVE BUD OIL: CLOVE LEAF OR STEM OIL",
+        "hsn_code": "33012921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: ANISE OIL;\nCAJEPUT OIL; CANANGA OIL; CARAWAY OIL; CASSIA OIL; CEDARWOOD OIL; CINNAMON BARK\nOIL; CINNAMON LEAF OIL: CINNAMON LEAF OIL",
+        "hsn_code": "33012918",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: ANISE OIL;\nCAJEPUT OIL; CANANGA OIL; CARAWAY OIL; CASSIA OIL; CEDARWOOD OIL; CINNAMON BARK\nOIL; CINNAMON LEAF OIL: CINNAMON BARK OIL",
+        "hsn_code": "33012917",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: ANISE OIL;\nCAJEPUT OIL; CANANGA OIL; CARAWAY OIL; CASSIA OIL; CEDARWOOD OIL; CINNAMON BARK\nOIL; CINNAMON LEAF OIL: CEDARWOOD OIL",
+        "hsn_code": "33012916",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: ANISE OIL;\nCAJEPUT OIL; CANANGA OIL; CARAWAY OIL; CASSIA OIL; CEDARWOOD OIL; CINNAMON BARK\nOIL; CINNAMON LEAF OIL: CASSIA OIL",
+        "hsn_code": "33012915",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: ANISE OIL;\nCAJEPUT OIL; CANANGA OIL; CARAWAY OIL; CASSIA OIL; CEDARWOOD OIL; CINNAMON BARK\nOIL; CINNAMON LEAF OIL: CARAWAY OIL",
+        "hsn_code": "33012914",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: ANISE OIL;\nCAJEPUT OIL; CANANGA OIL; CARAWAY OIL; CASSIA OIL; CEDARWOOD OIL; CINNAMON BARK\nOIL; CINNAMON LEAF OIL: CANANGA OIL",
+        "hsn_code": "33012913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: ANISE OIL;\nCAJEPUT OIL; CANANGA OIL; CARAWAY OIL; CASSIA OIL; CEDARWOOD OIL; CINNAMON BARK\nOIL; CINNAMON LEAF OIL: CAJEPUT OIL",
+        "hsn_code": "33012912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OTHER: ANISE OIL;\nCAJEPUT OIL; CANANGA OIL; CARAWAY OIL; CASSIA OIL; CEDARWOOD OIL; CINNAMON BARK\nOIL; CINNAMON LEAF OIL: ANISE OIL (ANISEED OIL)",
+        "hsn_code": "33012911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OF VETIVER",
+        "hsn_code": "33012600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OF OTHER MINTS :\nOTHER",
+        "hsn_code": "33012590",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OF OTHER MINTS :\nBERGAMENT OIL (EX-MENTHA CITRATE)",
+        "hsn_code": "33012540",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OF OTHER MINTS :\nHORSEMINT OIL (EX-MENTHA SYLVESTRIES)",
+        "hsn_code": "33012530",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OF OTHER MINTS :\nWATER MINT-OIL (EX-MENTHA AQUATIC)",
+        "hsn_code": "33012520",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OF OTHER MINTS :\nSPEARMINT OIL (EX-MENTHA SPICATA)",
+        "hsn_code": "33012510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OF PEPPERMINT\n(MENTHA PIPERITA)",
+        "hsn_code": "33012400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OF LAVENDER OR OF\nLAVANDIN",
+        "hsn_code": "33012300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OF JASMIN : OTHER",
+        "hsn_code": "33012290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT: OF JASMIN : JASMIN\nCONCENTRATE",
+        "hsn_code": "33012210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OTHER THAN THOSE OF CITRUS FRUIT:OF GERANIUM",
+        "hsn_code": "33012100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OF CITRUS FRUIT : OTHER : OTHER",
+        "hsn_code": "33011990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS ESSENTIAL OILS OF CITRUS FRUIT : OTHER : CITRONELLA OIL",
+        "hsn_code": "33011910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS - ESSENTIAL OILS OF CITRUS FRUIT : OF LIME",
+        "hsn_code": "33011400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS - ESSENTIAL OILS OF CITRUS FRUIT : OF LEMON",
+        "hsn_code": "33011300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS - ESSENTIAL OILS OF CITRUS FRUIT : OF ORANGE",
+        "hsn_code": "33011200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ESSENTIAL OILS (TERPENELESS OR NOT), INCLUDING CONCRETES AND ABSOLUTES; RESINOIDS;\nEXTRACTED OLEORESINS; CONCENTRATES OF ESSENTIAL OILS IN FATS, IN FIXED OILS, IN WAXES\nOR THE LIKE, OBTAINED BY ENFLEURAGE OR MACERATION; TERPENIC BY-PRODUCTS OF THE\nDETERPENATION OF ESSENTIAL OILS; AQUEOUS DISTILLATES AND AQUEOUS SOLUTIONS OF\nESSENTIAL OILS - ESSENTIAL OILS OF CITRUS FRUIT : OF BERGAMOT",
+        "hsn_code": "33011100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PRINTING INK, WRITING OR DRAWING INK AND OTHER INKS, WHETHER OR NOT CONCENTRATED\nOR SOLID - PRINTING INK :- BLACK : LITHOGRAPHIC INK AND JELLY",
+        "hsn_code": "32151110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PRINTING INK, WRITING OR DRAWING INK AND OTHER INKS, WHETHER OR NOT CONCENTRATED\nOR SOLID - PRINTING INK :- BLACK : NEWSPAPER INK",
+        "hsn_code": "32151120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PRINTING INK, WRITING OR DRAWING INK AND OTHER INKS, WHETHER OR NOT CONCENTRATED\nOR SOLID - PRINTING INK :- BLACK : ROTARY INK",
+        "hsn_code": "32151130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PRINTING INK, WRITING OR DRAWING INK AND OTHER INKS, WHETHER OR NOT CONCENTRATED\nOR SOLID - PRINTING INK :- BLACK : SCREEN PRINTING INK",
+        "hsn_code": "32151140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PRINTING INK, WRITING OR DRAWING INK AND OTHER INKS, WHETHER OR NOT CONCENTRATED\nOR SOLID - PRINTING INK :- BLACK : OTHER",
+        "hsn_code": "32151190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PRINTING INK, WRITING OR DRAWING INK AND OTHER INKS, WHETHER OR NOT CONCENTRATED\nOR SOLID - PRINTING INK : OTHER : LITHOGRAPHIC INK AND JELLY",
+        "hsn_code": "32151910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PRINTING INK, WRITING OR DRAWING INK AND OTHER INKS, WHETHER OR NOT CONCENTRATED\nOR SOLID - PRINTING INK : OTHER : NEWSPAPER INK",
+        "hsn_code": "32151920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PRINTING INK, WRITING OR DRAWING INK AND OTHER INKS, WHETHER OR NOT CONCENTRATED\nOR SOLID - PRINTING INK : OTHER : ROTARY INK",
+        "hsn_code": "32151930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PRINTING INK, WRITING OR DRAWING INK AND OTHER INKS, WHETHER OR NOT CONCENTRATED\nOR SOLID - PRINTING INK : OTHER : SCREEN PRINTING INK",
+        "hsn_code": "32151940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PRINTING INK, WRITING OR DRAWING INK AND OTHER INKS, WHETHER OR NOT CONCENTRATED\nOR SOLID - PRINTING INK : OTHER : OTHER",
+        "hsn_code": "32151990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PRINTING INK, WRITING OR DRAWING INK AND OTHER INKS, WHETHER OR NOT CONCENTRATED\nOR SOLID - OTHER : FOUNTAIN PEN INK",
+        "hsn_code": "32159010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PRINTING INK, WRITING OR DRAWING INK AND OTHER INKS, WHETHER OR NOT CONCENTRATED\nOR SOLID - OTHER : BALL PEN INK",
+        "hsn_code": "32159020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PRINTING INK, WRITING OR DRAWING INK AND OTHER INKS, WHETHER OR NOT CONCENTRATED\nOR SOLID - OTHER : INDELIBLE INK",
+        "hsn_code": "32159030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PRINTING INK, WRITING OR DRAWING INK AND OTHER INKS, WHETHER OR NOT CONCENTRATED\nOR SOLID - OTHER : DRAWING INK",
+        "hsn_code": "32159040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PRINTING INK, WRITING OR DRAWING INK AND OTHER INKS, WHETHER OR NOT CONCENTRATED\nOR SOLID - OTHER : OTHER",
+        "hsn_code": "32159090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PRINTING INK, WRITING OR DRAWING INK AND OTHER INKS, WHETHER OR NOT CONCENTRATED OR SOLID",
+        "hsn_code": "3215",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GLAZIERS PUTTY, GRAFTING PUTTY, RESIN CEMENTS, CAULKING COMPOUNDS AND OTHER\nMASTICS; PAINTERS FILLINGS; NON - REFRACTORY SURFACING PREPARTIONS FOR FACADES,\nINDOOR WALLS, FLOORS, CEILINGS OR THE LIKE- GLAZIERS PUTTY, GRAFTING PUTTY, RESIN\nCEMENTS, CAULKING COMPOUNDS AND OTHER MASTICS; PAINTERS FILLINGS",
+        "hsn_code": "32141000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLAZIERS PUTTY, GRAFTING PUTTY, RESIN CEMENTS, CAULKING COMPOUNDS AND OTHER\nMASTICS; PAINTERS FILLINGS; NON - REFRACTORY SURFACING PREPARTIONS FOR FACADES,\nINDOOR WALLS, FLOORS, CEILINGS OR THE LIKE - OTHER : NON - REFRACTORY SURFACING\nPREPARATIONS",
+        "hsn_code": "32149010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLAZIERS PUTTY, GRAFTING PUTTY, RESIN CEMENTS, CAULKING COMPOUNDS AND OTHER\nMASTICS; PAINTERS FILLINGS; NON - REFRACTORY SURFACING PREPARTIONS FOR FACADES,\nINDOOR WALLS, FLOORS, CEILINGS OR THE LIKE - OTHER : RESIN CEMENT",
+        "hsn_code": "32149020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLAZIERS PUTTY, GRAFTING PUTTY, RESIN CEMENTS, CAULKING COMPOUNDS AND OTHER\nMASTICS; PAINTERS FILLINGS; NON - REFRACTORY SURFACING PREPARTIONS FOR FACADES,\nINDOOR WALLS, FLOORS, CEILINGS OR THE LIKE - OTHER : OTHER",
+        "hsn_code": "32149090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "GLAZIERS PUTTY, GRAFTING PUTTY, RESIN CEMENTS, CAULKING COMPOUNDS AND OTHER MASTICS; PAINTERS’ FILLINGS; NON-REFRACTORY SURFACING PREPARATIONS FOR FACADES, INDOOR WALLS, FLOORS, CEILINGS OR THE LIKE",
+        "hsn_code": "3214",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTISTS, STUDENTS,OR SIGNBOARD PAINTER,COLOURS, MODIFIYING TINTS, AMUSEMENT\nCOLOURS AND THE LIKE, IN TABLETS, TUBES, JARS, BOTTLES, PANS OR IN SIMILAR FORMS OR\nPACKINGS - COLOURS IN SETS",
+        "hsn_code": "32131000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTISTS, STUDENTS OR SIGNBOARD PAINTERS COLOURS, MODIFIYING TINTS, AMUSEMENT\nCOLOURS AND THE LIKE, IN TABLETS, TUBES, JARS, BOTTLES, PANS OR IN SIMILAR FORMS OR\nPACKINGS -OTHER",
+        "hsn_code": "32139000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ARTISTS, STUDENTS, SIGNBOARD PAINTERS COLOURS, MODIFYING TINTS, AMUSEMENT COLOURS AND THE LIKE, IN TABLETS, TUBES, JARS, BOTTLES, PANS OR IN SIMILAR FORMS OR PACKINGS",
+        "hsn_code": "3213",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PIGMENTS (INCLUDING METALLIC POWDERS AND FLAKES) DISPERSED IN NON - AQUEOUS\nMEDIA, IN LIQUID OR PASTE FORM, OF A KIND USED IN THE MANUFACTURE OF PAINTS\n(INCLUDING ENAMELS); STAMPING FOILS; DYES AND OTHER COLOURING MATTER PUT UP IN\nFORMS OR PACKINGS FOR RETAIL SALE STAMPING FOILS",
+        "hsn_code": "32121000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PIGMENTS (INCLUDING METALLIC POWDERS AND FLAKES) DISPERSED IN NON - AQUEOUS\nMEDIA, IN LIQUID OR PASTE FORM, OF A KIND USED IN THE MANUFACTURE OF PAINTS\n(INCLUDING ENAMELS); STAMPING FOILS; DYES AND OTHER COLOURING MATTER PUT UP IN\nFORMS OR PACKINGS FOR RETAIL SALE - OTHER : PIGMENTS IN LINSEED OIL, WHITE SPIRIT,\nSPIRIT OF TURPENTINE, VARNISH AND OTHER PAINTS OR ENAMEL MEDIA NOT ELSEWHERE\nSPECIFIED OR INCLUDED",
+        "hsn_code": "32129010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PIGMENTS (INCLUDING METALLIC POWDERS AND FLAKES) DISPERSED IN NON - AQUEOUS\nMEDIA, IN LIQUID OR PASTE FORM, OF A KIND USED IN THE MANUFACTURE OF PAINTS\n(INCLUDING ENAMELS); STAMPING FOILS; DYES AND OTHER COLOURING MATTER PUT UP IN\nFORMS OR PACKINGS FOR RETAIL SALE - OTHER : DYES AND OTHER COLOURING MATTER PUT UP\nIN FORMS OR PACKINGS FOR RETAIL SALE",
+        "hsn_code": "32129020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PIGMENTS (INCLUDING METALLIC POWDERS AND FLAKES) DISPERSED IN NON - AQUEOUS\nMEDIA, IN LIQUID OR PASTE FORM, OF A KIND USED IN THE MANUFACTURE OF PAINTS\n(INCLUDING ENAMELS); STAMPING FOILS; DYES AND OTHER COLOURING MATTER PUT UP IN\nFORMS OR PACKINGS FOR RETAIL SALE - OTHER : ALUMINIUM PASTE",
+        "hsn_code": "32129030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PIGMENTS (INCLUDING METALLIC POWDERS AND FLAKES) DISPERSED IN NON - AQUEOUS\nMEDIA, IN LIQUID OR PASTE FORM, OF A KIND USED IN THE MANUFACTURE OF PAINTS\n(INCLUDING ENAMELS); STAMPING FOILS; DYES AND OTHER COLOURING MATTER PUT UP IN\nFORMS OR PACKINGS FOR RETAIL SALE - OTHER : OTHER",
+        "hsn_code": "32129090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PIGMENTS (INCLUDING METALLIC POWDERS AND FLAKES) DISPERSED IN NON-AQUEOUS MEDIA, IN LIQUID OR PASTE FORM, OF A KIND USED IN THE MANUFACTURE OF PAINTS (INCLUDING ENAMELS); STAMPING FOILS; DYES AND OTHER COLOURING MATTER PUT UP IN FORMS OR PACKINGS FOR RETAIL SALE",
+        "hsn_code": "3212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAINTS AND VARNISHES (INCLUDING ENAMELS, LACQUERS AND DISTEMPERS);\nPREPARED WATER PIGMENTS OF A KIND USED FOR FINISHING LEATHER - OTHER PAINTS AND\nVARNISHES (INCLUDING ENAMELS, LACQUERS AND DISTEMPERS); PREPARED WATER PIGMENTS\nOF A KIND USED FOR FINISHING LEATHER : DISTEMPERS : DRY DISTEMPER, INCLUDING CEMENT\nBASED WATER PAINTS",
+        "hsn_code": "32100011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAINTS AND VARNISHES (INCLUDING ENAMELS, LACQUERS AND DISTEMPERS);\nPREPARED WATER PIGMENTS OF A KIND USED FOR FINISHING LEATHER - OTHER PAINTS AND\nVARNISHES (INCLUDING ENAMELS, LACQUERS AND DISTEMPERS); PREPARED WATER PIGMENTS\nOF A KIND USED FOR FINISHING LEATHER : DISTEMPERS : OIL BOUND DISTEMPER",
+        "hsn_code": "32100012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAINTS AND VARNISHES (INCLUDING ENAMELS, LACQUERS AND DISTEMPERS);\nPREPARED WATER PIGMENTS OF A KIND USED FOR FINISHING LEATHER - OTHER PAINTS AND\nVARNISHES (INCLUDING ENAMELS, LACQUERS AND DISTEMPERS); PREPARED WATER PIGMENTS\nOF A KIND USED FOR FINISHING LEATHER : DISTEMPERS : OTHER",
+        "hsn_code": "32100019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAINTS AND VARNISHES (INCLUDING ENAMELS, LACQUERS AND DISTEMPERS);\nPREPARED WATER PIGMENTS OF A KIND USED FOR FINISHING LEATHER - OTHER PAINTS AND\nVARNISHES (INCLUDING ENAMELS, LACQUERS AND DISTEMPERS); PREPARED WATER PIGMENTS\nOF A KIND USED FOR FINISHING LEATHER : PREPARED WATER PIGMENTS OF A KIND USED FOR\nFINISHING LEATHER",
+        "hsn_code": "32100020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAINTS AND VARNISHES (INCLUDING ENAMELS, LACQUERS AND DISTEMPERS);\nPREPARED WATER PIGMENTS OF A KIND USED FOR FINISHING LEATHER - OTHER PAINTS AND\nVARNISHES (INCLUDING ENAMELS, LACQUERS AND DISTEMPERS); PREPARED WATER PIGMENTS\nOF A KIND USED FOR FINISHING LEATHER : METALLIC POWDER OR FLAKES PREPARED AS PAINTS",
+        "hsn_code": "32100030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAINTS AND VARNISHES (INCLUDING ENAMELS, LACQUERS AND DISTEMPERS);\nPREPARED WATER PIGMENTS OF A KIND USED FOR FINISHING LEATHER - OTHER PAINTS AND\nVARNISHES (INCLUDING ENAMELS, LACQUERS AND DISTEMPERS); PREPARED WATER PIGMENTS\nOF A KIND USED FOR FINISHING LEATHER : POLY TETRA FLUORO ETHYLENE (PTFE) OR SILICON\nRESIN BASED COATING MATERIALS",
+        "hsn_code": "32100040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAINTS AND VARNISHES (INCLUDING ENAMELS, LACQUERS AND DISTEMPERS);\nPREPARED WATER PIGMENTS OF A KIND USED FOR FINISHING LEATHER - OTHER PAINTS AND\nVARNISHES (INCLUDING ENAMELS, LACQUERS AND DISTEMPERS); PREPARED WATER PIGMENTS\nOF A KIND USED FOR FINISHING LEATHER : OTHER",
+        "hsn_code": "32100090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAINTS AND VARNISHES (INCLUDING ENAMELS, LACQUERS AND DISTEMPERS); PREPARED WATER PIGMENTS OF A KIND USED FOR FINISHING LEATHER",
+        "hsn_code": "3210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN AN\nAQUEOUS MEDIUM - BASED ON ACRYLIC OR VINYL POLYMERS : ACRYLIC EMULSION",
+        "hsn_code": "32091010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN AN\nAQUEOUS MEDIUM - BASED ON ACRYLIC OR VINYL POLYMERS : OTHER",
+        "hsn_code": "32091090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN AN\nAQUEOUS MEDIUM - OTHER : DISPERSION PAINTS",
+        "hsn_code": "32099010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN AN\nAQUEOUS MEDIUM - OTHER : EMULSION PAINTS NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "32099020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN AN\nAQUEOUS MEDIUM - OTHER : OTHER",
+        "hsn_code": "32099090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Paints and varnishes (including enamels and lacquers) based on synthetic or chemically modified natural polymers, dispersed or dissolved in an aqueous medium",
+        "hsn_code": "3209",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN A\nNON - AQUEOUS MEDIUM; SOLUTIONS AS DEFINED IN NOTE 4 TO THIS CHAPTER - BASED ON\nPOLYESTERS : ENAMELS",
+        "hsn_code": "32081010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN A\nNON - AQUEOUS MEDIUM; SOLUTIONS AS DEFINED IN NOTE 4 TO THIS CHAPTER - BASED ON\nPOLYESTERS : LACQUERS",
+        "hsn_code": "32081020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN A\nNON - AQUEOUS MEDIUM; SOLUTIONS AS DEFINED IN NOTE 4 TO THIS CHAPTER - BASED ON\nPOLYESTERS : VARNISHES",
+        "hsn_code": "32081030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN A\nNON - AQUEOUS MEDIUM; SOLUTIONS AS DEFINED IN NOTE 4 TO THIS CHAPTER - BASED ON\nPOLYESTERS : OTHER",
+        "hsn_code": "32081090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN A\nNON - AQUEOUS MEDIUM; SOLUTIONS AS DEFINED IN NOTE 4 TO THIS CHAPTER - BASED ON\nACRYLIC OR VINYL POLYMERS : ENAMELS",
+        "hsn_code": "32082010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN A\nNON - AQUEOUS MEDIUM; SOLUTIONS AS DEFINED IN NOTE 4 TO THIS CHAPTER - BASED ON\nACRYLIC OR VINYL POLYMERS : LACQUERS",
+        "hsn_code": "32082020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN A\nNON - AQUEOUS MEDIUM; SOLUTIONS AS DEFINED IN NOTE 4 TO THIS CHAPTER - BASED ON\nACRYLIC OR VINYL POLYMERS : VARNISHES",
+        "hsn_code": "32082030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN A\nNON - AQUEOUS MEDIUM; SOLUTIONS AS DEFINED IN NOTE 4 TO THIS CHAPTER - BASED ON\nACRYLIC OR VINYL POLYMERS : OTHER",
+        "hsn_code": "32082090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN A\nNON - AQUEOUS MEDIUM; SOLUTIONS AS DEFINED IN NOTE 4 TO THIS CHAPTER - OTHER :\nBASED ON CELLULOSE NITRATE OR OTHER CELLULOSE DERIVATIVES : NITROCELLULOSE\nLACQUERS",
+        "hsn_code": "32089011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN A\nNON - AQUEOUS MEDIUM; SOLUTIONS AS DEFINED IN NOTE 4 TO THIS CHAPTER - OTHER :\nBASED ON CELLULOSE NITRATE OR OTHER CELLULOSE DERIVATIVES : OTHER",
+        "hsn_code": "32089019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN A\nNON - AQUEOUS MEDIUM; SOLUTIONS AS DEFINED IN NOTE 4 TO THIS CHAPTER - OTHER :\nENAMELS : SYNTHETIC ENAMEL, ULTRA WHITE PAINTS",
+        "hsn_code": "32089021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN A\nNON - AQUEOUS MEDIUM; SOLUTIONS AS DEFINED IN NOTE 4 TO THIS CHAPTER - OTHER :\nENAMELS : SYNTHETIC ENAMEL, OTHER COLOURS",
+        "hsn_code": "32089022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN A\nNON - AQUEOUS MEDIUM; SOLUTIONS AS DEFINED IN NOTE 4 TO THIS CHAPTER - OTHER :\nENAMELS : OTHER",
+        "hsn_code": "32089029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN A\nNON - AQUEOUS MEDIUM; SOLUTIONS AS DEFINED IN NOTE 4 TO THIS CHAPTER - OTHER :\nLACQUERS",
+        "hsn_code": "32089030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN A\nNON - AQUEOUS MEDIUM; SOLUTIONS AS DEFINED IN NOTE 4 TO THIS CHAPTER - OTHER :\nVARNISHES : INSULATING VARNISH",
+        "hsn_code": "32089041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN A\nNON - AQUEOUS MEDIUM; SOLUTIONS AS DEFINED IN NOTE 4 TO THIS CHAPTER - OTHER :\nVARNISHES : OTHER",
+        "hsn_code": "32089049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN A\nNON - AQUEOUS MEDIUM; SOLUTIONS AS DEFINED IN NOTE 4 TO THIS CHAPTER - OTHER : SLIP\nAGENTS",
+        "hsn_code": "32089050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC\nPOLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN A\nNON - AQUEOUS MEDIUM; SOLUTIONS AS DEFINED IN NOTE 4 TO THIS CHAPTER - OTHER :\nOTHER",
+        "hsn_code": "32089090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PAINTS AND VARNISHES (INCLUDING ENAMELS AND LACQUERS) BASED ON SYNTHETIC POLYMERS OR CHEMICALLY MODIFIED NATURAL POLYMERS, DISPERSED OR DISSOLVED IN A NON-AQUEOUS MEDIUM; SOLUTIONS AS DEFINED IN NOTE 4 TO THIS CHAPTER",
+        "hsn_code": "3208",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED PIGMENTS, PREPARED OPACIFIERS AND PREPARED COLOURS, VITRIFIABLE ENAMELS\nAND GLAZES, ENGOBES (SLIPS), LIQUID LUSTRES AND SIMILAR PREPARATIONS, OF A KIND USED\nIN THE CERAMIC ENAMELLING OR GLASS INDUSTRY; GLASS FRIT AND OTHER GLASS, IN THE\nFORM OF POWDER, GRANULES OR FLAKES - PREPARED PIGMENTS, PREPARED OPACIFIERS,\nPREPARED COLOURS AND SIMILAR PREPARATIONS : PREPARED ORGANIC DYE - STUFF\nPIGMENTS, DRY",
+        "hsn_code": "32071010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED PIGMENTS, PREPARED OPACIFIERS AND PREPARED COLOURS, VITRIFIABLE ENAMELS\nAND GLAZES, ENGOBES (SLIPS), LIQUID LUSTRES AND SIMILAR PREPARATIONS, OF A KIND USED\nIN THE CERAMIC ENAMELLING OR GLASS INDUSTRY; GLASS FRIT AND OTHER GLASS, IN THE\nFORM OF POWDER, GRANULES OR FLAKES - PREPARED PIGMENTS, PREPARED OPACIFIERS,\nPREPARED COLOURS AND SIMILAR PREPARATIONS : PREPARED ORGANIC DYE - STUFF\nPIGMENTS, PASTE",
+        "hsn_code": "32071020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED PIGMENTS, PREPARED OPACIFIERS AND PREPARED COLOURS, VITRIFIABLE ENAMELS\nAND GLAZES, ENGOBES (SLIPS), LIQUID LUSTRES AND SIMILAR PREPARATIONS, OF A KIND USED\nIN THE CERAMIC ENAMELLING OR GLASS INDUSTRY; GLASS FRIT AND OTHER GLASS, IN THE\nFORM OF POWDER, GRANULES OR FLAKES - PREPARED PIGMENTS, PREPARED OPACIFIERS,\nPREPARED COLOURS AND SIMILAR PREPARATIONS : PREPARED INORGANIC PIGMENTS",
+        "hsn_code": "32071030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED PIGMENTS, PREPARED OPACIFIERS AND PREPARED COLOURS, VITRIFIABLE ENAMELS\nAND GLAZES, ENGOBES (SLIPS), LIQUID LUSTRES AND SIMILAR PREPARATIONS, OF A KIND USED\nIN THE CERAMIC ENAMELLING OR GLASS INDUSTRY; GLASS FRIT AND OTHER GLASS, IN THE\nFORM OF POWDER, GRANULES OR FLAKES - PREPARED PIGMENTS, PREPARED OPACIFIERS,\nPREPARED COLOURS AND SIMILAR PREPARATIONS : PREPARED OPACIFIERS PREPARED COLOURS\nAND SIMILAR PREPARATIONS",
+        "hsn_code": "32071040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED PIGMENTS, PREPARED OPACIFIERS AND PREPARED COLOURS, VITRIFIABLE ENAMELS\nAND GLAZES, ENGOBES (SLIPS), LIQUID LUSTRES AND SIMILAR PREPARATIONS, OF A KIND USED\nIN THE CERAMIC ENAMELLING OR GLASS INDUSTRY; GLASS FRIT AND OTHER GLASS, IN THE\nFORM OF POWDER, GRANULES OR FLAKES - PREPARED PIGMENTS, PREPARED OPACIFIERS,\nPREPARED COLOURS AND SIMILAR PREPARATIONS : OTHER",
+        "hsn_code": "32071090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED PIGMENTS, PREPARED OPACIFIERS AND PREPARED COLOURS, VITRIFIABLE ENAMELS\nAND GLAZES, ENGOBES (SLIPS), LIQUID LUSTRES AND SIMILAR PREPARATIONS, OF A KIND USED\nIN THE CERAMIC ENAMELLING OR GLASS INDUSTRY; GLASS FRIT AND OTHER GLASS, IN THE\nFORM OF POWDER, GRANULES OR FLAKES - VITRIFIABLE ENAMELS AND GLAZES, ENGOBES\n(SLIPS) AND SIMILAR PREPARATIONS : VITRIFIABLE ENAMELS AND GLAZES",
+        "hsn_code": "32072010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED PIGMENTS, PREPARED OPACIFIERS AND PREPARED COLOURS, VITRIFIABLE ENAMELS\nAND GLAZES, ENGOBES (SLIPS), LIQUID LUSTRES AND SIMILAR PREPARATIONS, OF A KIND USED\nIN THE CERAMIC ENAMELLING OR GLASS INDUSTRY; GLASS FRIT AND OTHER GLASS, IN THE\nFORM OF POWDER, GRANULES OR FLAKES - VITRIFIABLE ENAMELS AND GLAZES, ENGOBES\n(SLIPS) AND SIMILAR PREPARATIONS : ENGOBES (SLIPS) AND SIMILAR PREPARATIONS",
+        "hsn_code": "32072020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED PIGMENTS, PREPARED OPACIFIERS AND PREPARED COLOURS, VITRIFIABLE ENAMELS\nAND GLAZES, ENGOBES (SLIPS), LIQUID LUSTRES AND SIMILAR PREPARATIONS, OF A KIND USED\nIN THE CERAMIC ENAMELLING OR GLASS INDUSTRY; GLASS FRIT AND OTHER GLASS, IN THE\nFORM OF POWDER, GRANULES OR FLAKES -LIQUID LUSTRES AND SIMILAR PREPARATIONS",
+        "hsn_code": "32073000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED PIGMENTS, PREPARED OPACIFIERS AND PREPARED COLOURS, VITRIFIABLE ENAMELS\nAND GLAZES, ENGOBES (SLIPS), LIQUID LUSTRES AND SIMILAR PREPARATIONS, OF A KIND USED\nIN THE CERAMIC ENAMELLING OR GLASS INDUSTRY; GLASS FRIT AND OTHER GLASS, IN THE\nFORM OF POWDER, GRANULES OR FLAKES - GLASS FRIT AND OTHER GLASS, IN THE FORM OF\nPOWDER, GRANULES OR FLAKES",
+        "hsn_code": "32074000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PREPARED PIGMENTS, PREPARATED OPACIFIERS AND PREPARED COLOURS, VITRIFIABLE ENAMELS AND GLAZES, ENGOBES (SLIPS), LIQUID LUSTRES AND SIMILAR PREPARATIONS, OF A KIND USED IN THE CERAMIC ENAMELLING OR GLASS INDUSTRY; GLASS FRIT AND OTHER GLASS, IN THE FORM OF POWDER, GRANULES OR FLAKES",
+        "hsn_code": "3207",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER COLOURING MATTER; PREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER, OTHER\nTHAN THOSE OF HEADINGS 3203, 3204 OR 3205; INORGANIC PRODUCTS OF A KIND USED AS\nLUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED - PIGMENTS AND PREPARATIONS\nBASED ON TITANIUM DIOXIDE :- CONTAINING 80% OR MORE BY WEIGHT OF TITANIUM DIOXIDE\nCALCULATED ON THE DRY MATTER : PEARLSENT PIGMENT (TITANIUM DIOXIDE, COATED\nMICANANEOUS AND LUSTRES PEARL PIGMENT)",
+        "hsn_code": "32061110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER COLOURING MATTER; PREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER, OTHER\nTHAN THOSE OF HEADINGS 3203, 3204 OR 3205; INORGANIC PRODUCTS OF A KIND USED AS\nLUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED - PIGMENTS AND PREPARATIONS\nBASED ON TITANIUM DIOXIDE :- CONTAINING 80% OR MORE BY WEIGHT OF TITANIUM DIOXIDE\nCALCULATED ON THE DRY MATTER : OTHER",
+        "hsn_code": "32061190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER COLOURING MATTER; PREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER, OTHER\nTHAN THOSE OF HEADINGS 3203, 3204 OR 3205; INORGANIC PRODUCTS OF A KIND USED AS\nLUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED - PIGMENTS AND PREPARATIONS\nBASED ON TITANIUM DIOXIDE : OTHER",
+        "hsn_code": "32061900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER COLOURING MATTER; PREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER, OTHER\nTHAN THOSE OF HEADINGS 3203, 3204 OR 3205; INORGANIC PRODUCTS OF A KIND USED AS\nLUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED- PIGMENTS AND PREPARATIONS\nBASED ON CHROMIUM COMPOUNDS",
+        "hsn_code": "32062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER COLOURING MATTER; PREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER, OTHER\nTHAN THOSE OF HEADINGS 3203, 3204 OR 3205; INORGANIC PRODUCTS OF A KIND USED AS\nLUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED - PIGMENTS AND PREPARATIONS\nBASED ON CADMIUM COMPOUNDS",
+        "hsn_code": "32063000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER COLOURING MATTER; PREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER, OTHER\nTHAN THOSE OF HEADINGS 3203, 3204 OR 3205; INORGANIC PRODUCTS OF A KIND USED AS\nLUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED - OTHER COLOURING MATTER AND\nOTHER PREPARATIONS : ULTRAMARINE AND PREPARATIONS BASED THEREON",
+        "hsn_code": "32064100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER COLOURING MATTER; PREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER, OTHER\nTHAN THOSE OF HEADINGS 3203, 3204 OR 3205; INORGANIC PRODUCTS OF A KIND USED AS\nLUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED - OTHER COLOURING MATTER AND\nOTHER PREPARATIONS : LITHOPONE AND OTHER PIGMENTS AND PREPARATIONS BASED ON\nZINC SULPHIDE",
+        "hsn_code": "32064200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER COLOURING MATTER; PREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER, OTHER\nTHAN THOSE OF HEADINGS 3203, 3204 OR 3205; INORGANIC PRODUCTS OF A KIND USED AS\nLUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED - OTHER COLOURING MATTER AND\nOTHER PREPARATIONS : PIGMENTS AND PREPARATIONS BASED ON HEXACYANOFERRATES\n(FERROCYANIDES AND FERRICYANIDES)",
+        "hsn_code": "32064300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER COLOURING MATTER; PREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER, OTHER\nTHAN THOSE OF HEADINGS 3203, 3204 OR 3205; INORGANIC PRODUCTS OF A KIND USED AS\nLUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED - OTHER COLOURING MATTER AND\nOTHER PREPARATIONS : OTHER : RED OXIDE",
+        "hsn_code": "32064910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER COLOURING MATTER; PREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER, OTHER\nTHAN THOSE OF HEADINGS 3203, 3204 OR 3205; INORGANIC PRODUCTS OF A KIND USED AS\nLUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED - OTHER COLOURING MATTER AND\nOTHER PREPARATIONS : OTHER : PERSIAN RED",
+        "hsn_code": "32064920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER COLOURING MATTER; PREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER, OTHER\nTHAN THOSE OF HEADINGS 3203, 3204 OR 3205; INORGANIC PRODUCTS OF A KIND USED AS\nLUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED - OTHER COLOURING MATTER AND\nOTHER PREPARATIONS : OTHER : YELLOW OCHRE",
+        "hsn_code": "32064930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER COLOURING MATTER; PREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER, OTHER\nTHAN THOSE OF HEADINGS 3203, 3204 OR 3205; INORGANIC PRODUCTS OF A KIND USED AS\nLUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED - OTHER COLOURING MATTER AND\nOTHER PREPARATIONS : OTHER : BRONZE POWDER",
+        "hsn_code": "32064940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER COLOURING MATTER; PREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER, OTHER\nTHAN THOSE OF HEADINGS 3203, 3204 OR 3205; INORGANIC PRODUCTS OF A KIND USED AS\nLUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED - OTHER COLOURING MATTER AND\nOTHER PREPARATIONS : OTHER : OTHER",
+        "hsn_code": "32064990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER COLOURING MATTER; PREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER, OTHER\nTHAN THOSE OF HEADINGS 3203, 3204 OR 3205; INORGANIC PRODUCTS OF A KIND USED AS\nLUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED - INORGANIC PRODUCTS OF A KIND\nUSED AS LUMINOPHORES",
+        "hsn_code": "32065000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER COLOURING MATTER; PREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER, OTHER THAN THOSE OF HEADINGS 3203, 3204 OR 3205; INORGANIC PRODUCTS OF A KIND USED AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED",
+        "hsn_code": "3206",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COLOUR LAKES; PREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON COLOUR\nLAKES",
+        "hsn_code": "32050000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED; PREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC COLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT BRIGHTENING Agens OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED. SYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED IN NOTE 3 TO THIS CHAPTER:",
+        "hsn_code": "3205",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "3204 SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : DISPERSE\nYELLOW : DISPERSE YELLOW 13 (DURANOL BRILL YELLOW 6 G)",
+        "hsn_code": "32041111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "3204 SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : DISPERSE\nYELLOW : OTHER",
+        "hsn_code": "32041119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : DISPERSE\nORANGE : DISPERSE ORANGE 11 (DURANOL ORANGE G)",
+        "hsn_code": "32041121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : DISPERSE\nORANGE : OTHER",
+        "hsn_code": "32041129",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON :- DISPERSE\nRED : DISPERSE RED 3 (SERISOL FAST PINK B)",
+        "hsn_code": "32041131",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON :- DISPERSE\nRED : DISPERSE RED 4 (CELLITON FAST PINK RF)",
+        "hsn_code": "32041132",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON :- DISPERSE\nRED : DISPERSE RED 9 (DURANOL RED GN)",
+        "hsn_code": "32041133",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON :- DISPERSE\nRED : OTHER",
+        "hsn_code": "32041139",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : DISPERSE\nVIOLET : DISPERSE VIOLET 1 (DURANOL VIOLET 2R)",
+        "hsn_code": "32041141",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : DISPERSE\nVIOLET : DISPERSE VIOLET 4 (DURANOL BRILL VIOLET B)",
+        "hsn_code": "32041142",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : DISPERSE\nVIOLET : DISPERSE VIOLET 8 (DURANOL BRILL VIOLET BR)",
+        "hsn_code": "32041143",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : DISPERSE\nVIOLET : OTHER",
+        "hsn_code": "32041149",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : DISPERSE\nBLUE : DISPERSE BLUE 1 (DURANOL BRILL BLUE CB)",
+        "hsn_code": "32041151",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : DISPERSE\nBLUE : DISPERSE BLUE 3 (DURANOL BRILL BLUE BBN)",
+        "hsn_code": "32041152",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : DISPERSE\nBLUE : DISPERSE BLUE 5 (CELLITON FAST BLUE FFB)",
+        "hsn_code": "32041153",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : DISPERSE\nBLUE : DISPERSE BLUE 6 (CELLITON FAST BLUE FFG)",
+        "hsn_code": "32041154",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : DISPERSE\nBLUE : DISPERSE BLUE 14 (DURANOL BRILL BLUE G)",
+        "hsn_code": "32041155",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : DISPERSE\nBLUE : DISPERSE BLUE 24 (DURANOL BLUE 2G)",
+        "hsn_code": "32041156",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : DISPERSE\nBLUE : OTHER",
+        "hsn_code": "32041159",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : OTHER :\nDISPERSE GREENS",
+        "hsn_code": "32041191",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : OTHER :\nDISPERSE BROWNS",
+        "hsn_code": "32041192",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : OTHER :\nDISPERSE BLACKS",
+        "hsn_code": "32041193",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : OTHER :\nDISPERSE BROWN MIXTURES",
+        "hsn_code": "32041194",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : OTHER :\nDISPERSE GREY MIXTURES",
+        "hsn_code": "32041195",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : OTHER :\nDISPERSE BLACK MIXTURES",
+        "hsn_code": "32041196",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- DISPERSE DYES AND PREPARATIONS BASED THEREON : OTHER :\nOTHER",
+        "hsn_code": "32041199",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : AZO\nDYES : ACID YELLOWS",
+        "hsn_code": "32041211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : AZO\nDYES : ACID ORANGES",
+        "hsn_code": "32041212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : AZO\nDYES : ACID RED",
+        "hsn_code": "32041213",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : AZO\nDYES : ACID VIOLETS",
+        "hsn_code": "32041214",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : AZO\nDYES : ACID BLUES",
+        "hsn_code": "32041215",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : AZO\nDYES : ACID GREENS",
+        "hsn_code": "32041216",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : AZO\nDYES : ACID BROWN",
+        "hsn_code": "32041217",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : AZO\nDYES : ACID BLACKS",
+        "hsn_code": "32041218",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : AZO\nDYES : OTHER",
+        "hsn_code": "32041219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nGREENS (NON - AZO) : ACID GREEN 17 (SOLACET FAST GREEN 2G)",
+        "hsn_code": "32041221",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nGREENS (NON - AZO) : ACID GREEN 27 (CARBOLAN GREEN G)",
+        "hsn_code": "32041222",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nGREENS (NON - AZO) : ACID GREEN 28 (CARBOLAN BRILL GREEN 5G)",
+        "hsn_code": "32041223",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nGREENS (NON - AZO) : ACID GREEN 38 (ALIZARINE CYANINE GREEN 3G)",
+        "hsn_code": "32041224",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nGREENS (NON - AZO) : ACID GREEN 44 (ALIZARINE CYANINE GREEN GWA)",
+        "hsn_code": "32041225",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nGREENS (NON - AZO) : OTHER",
+        "hsn_code": "32041229",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nBLACK (NON - AZO) : ACID BLACK 2 (NIGROSINE)",
+        "hsn_code": "32041231",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nBLACK (NON - AZO) : ACID BLACK 48 (COOMASIE FAST GREY 3G)",
+        "hsn_code": "32041232",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nBLACK (NON - AZO) : OTHER",
+        "hsn_code": "32041239",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nBLUES 2, 14, 23, 25, 45, 51, 52 AND 78 (NON - AZO) : ACID BLUE 2 (ALIZARINE BRILL BLUE PFN)",
+        "hsn_code": "32041241",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nBLUES 2, 14, 23, 25, 45, 51, 52 AND 78 (NON - AZO) : ACID BLUE 14 (SOLACET FAST BLUE 4 G1)",
+        "hsn_code": "32041242",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nBLUES 2, 14, 23, 25, 45, 51, 52 AND 78 (NON - AZO) : ACID BLUE 23 (ALIZARINE LIGHT BLUE 4 G1)",
+        "hsn_code": "32041243",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nBLUES 2, 14, 23, 25, 45, 51, 52 AND 78 (NON - AZO) : ACID BLUE 25 (SOLWAY ULTRA BLUE B)",
+        "hsn_code": "32041244",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nBLUES 2, 14, 23, 25, 45, 51, 52 AND 78 (NON - AZO) : ACID BLUE 45 (SOLWAY BLUE RN)",
+        "hsn_code": "32041245",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nBLUES 2, 14, 23, 25, 45, 51, 52 AND 78 (NON - AZO) : ACID BLUE 51 (ALIZARINE SKY BLUE FFB)",
+        "hsn_code": "32041246",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nBLUES 2, 14, 23, 25, 45, 51, 52 AND 78 (NON - AZO) : ACID BLUE 52 (ALIZARINE LIGHT 5GL)",
+        "hsn_code": "32041247",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nBLUES 2, 14, 23, 25, 45, 51, 52 AND 78 (NON - AZO) : ACID BLUE 78 (SOLWAY SKY BLUE B)",
+        "hsn_code": "32041248",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nBLUES 93, 112, 127, 138, 140 AND OTHERS (NON - AZO) : ACID BLUE 93 (INK BLUE)",
+        "hsn_code": "32041251",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nBLUES 93, 112, 127, 138, 140 AND OTHERS (NON - AZO) : ACID BLUE 112 (COOMASIE ULTRA SKY\nSE)",
+        "hsn_code": "32041252",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nBLUES 93, 112, 127, 138, 140 AND OTHERS (NON - AZO) : ACID BLUE 127 (BRILL ALIZARINE\nMILLING BLUE G)",
+        "hsn_code": "32041253",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nBLUES 93, 112, 127, 138, 140 AND OTHERS (NON - AZO) : ACID BLUE 138 (CARBOLAN BLUE B)",
+        "hsn_code": "32041254",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nBLUES 93, 112, 127, 138, 140 AND OTHERS (NON - AZO) : ACID BLUE 140 (CARBOLAN BRILL BLUE\n2R)",
+        "hsn_code": "32041255",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON : ACID\nBLUES 93, 112, 127, 138, 140 AND OTHERS (NON - AZO) : OTHER",
+        "hsn_code": "32041259",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON :\nMORDANT DYES : YELLOWS",
+        "hsn_code": "32041261",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON :\nMORDANT DYES : ORANGES",
+        "hsn_code": "32041262",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON :\nMORDANT DYES : VIOLETS",
+        "hsn_code": "32041263",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON :\nMORDANT DYES : BLUES",
+        "hsn_code": "32041264",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON :\nMORDANT DYES : GREENS",
+        "hsn_code": "32041265",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON :\nMORDANT DYES : BROWNS",
+        "hsn_code": "32041266",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON :\nMORDANT DYES : BLACKS",
+        "hsn_code": "32041267",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON :\nMORDANT DYES : RED II (ALIZARINE RED)",
+        "hsn_code": "32041268",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON :\nMORDANT DYES : OTHER",
+        "hsn_code": "32041269",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON :\nOTHER NON - AZO ACID DYES : ACID YELLOWS",
+        "hsn_code": "32041291",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON :\nOTHER NON - AZO ACID DYES : ACID ORANGES",
+        "hsn_code": "32041292",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON :\nOTHER NON - AZO ACID DYES : ACID RED",
+        "hsn_code": "32041293",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON :\nOTHER NON - AZO ACID DYES : ACID VIOLETS",
+        "hsn_code": "32041294",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON :\nOTHER NON - AZO ACID DYES : ACID BROWNS",
+        "hsn_code": "32041295",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : ACID DYES, WHETHER OR NOT PREMETALLISED, AND\nPREPARATIONS BASED THEREON; MORDANT DYES AND PREPARATIONS BASED THEREON :\nOTHER NON - AZO ACID DYES : OTHER",
+        "hsn_code": "32041299",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : BASIC DYES AND PREPARATIONS BASED THEREON : BASIC AZO\nDYES",
+        "hsn_code": "32041310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : BASIC DYES AND PREPARATIONS BASED THEREON : BASIC YELLOW\n(NON - AZO) : YELLOW 2 (AURAMINE O)",
+        "hsn_code": "32041321",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : BASIC DYES AND PREPARATIONS BASED THEREON : BASIC YELLOW\n(NON - AZO) : OTHER",
+        "hsn_code": "32041329",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : BASIC DYES AND PREPARATIONS BASED THEREON : BASIC RED\n(NON - AZO) : RED 1 (RHODAMINE 6 G)",
+        "hsn_code": "32041331",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : BASIC DYES AND PREPARATIONS BASED THEREON : BASIC RED\n(NON - AZO) : OTHER",
+        "hsn_code": "32041339",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : BASIC DYES AND PREPARATIONS BASED THEREON : BASIC VIOLET\n(NON - AZO) : VIOLET 1 (METHYL VIOLET)",
+        "hsn_code": "32041341",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : BASIC DYES AND PREPARATIONS BASED THEREON : BASIC VIOLET\n(NON - AZO) : VIOLET 10 (RHODAMINE B)",
+        "hsn_code": "32041342",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : BASIC DYES AND PREPARATIONS BASED THEREON : BASIC VIOLET\n(NON - AZO) : VIOLET 14 (MAGENTA)",
+        "hsn_code": "32041343",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : BASIC DYES AND PREPARATIONS BASED THEREON : BASIC VIOLET\n(NON - AZO) : OTHER",
+        "hsn_code": "32041349",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : BASIC DYES AND PREPARATIONS BASED THEREON : BASIC BLUE\n(NON - AZO) : BLUE 9 (METHYLENE BLUE)",
+        "hsn_code": "32041351",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : BASIC DYES AND PREPARATIONS BASED THEREON : BASIC BLUE\n(NON - AZO) : BLUE 16 (VICTORIA BLUE B)",
+        "hsn_code": "32041352",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : BASIC DYES AND PREPARATIONS BASED THEREON : BASIC BLUE\n(NON - AZO) : OTHER",
+        "hsn_code": "32041359",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : BASIC DYES AND PREPARATIONS BASED THEREON : BASIC GREEN\n(NON - AZO) : GREEN 4 (MALACHITE GREEN)",
+        "hsn_code": "32041361",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : BASIC DYES AND PREPARATIONS BASED THEREON : BASIC GREEN\n(NON - AZO) : OTHER",
+        "hsn_code": "32041369",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : BASIC DYES AND PREPARATIONS BASED THEREON :- OTHER NON -\nAZO BASIC DYES : BASIC ORANGES",
+        "hsn_code": "32041391",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : BASIC DYES AND PREPARATIONS BASED THEREON :- OTHER NON -\nAZO BASIC DYES : BASIC BROWNS",
+        "hsn_code": "32041392",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : BASIC DYES AND PREPARATIONS BASED THEREON :- OTHER NON -\nAZO BASIC DYES : BASIC BLACK",
+        "hsn_code": "32041393",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : BASIC DYES AND PREPARATIONS BASED THEREON :- OTHER NON -\nAZO BASIC DYES : OTHER",
+        "hsn_code": "32041399",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : DIRECT\nYELLOW (AZO) : YELLOW 12 (CHRYSOPHENINE G)",
+        "hsn_code": "32041411",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : DIRECT\nYELLOW (AZO) : OTHER",
+        "hsn_code": "32041419",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : DIRECT RED\n(AZO) : CONGO RED",
+        "hsn_code": "32041421",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : DIRECT RED\n(AZO) : OTHER",
+        "hsn_code": "32041429",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : DIRECT BLUE\n(AZO) : BLUE 1 (SKY BLUE FF)",
+        "hsn_code": "32041431",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : DIRECT BLUE\n(AZO) : OTHER",
+        "hsn_code": "32041439",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : DIRECT\nORANGES (AZO)",
+        "hsn_code": "32041440",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : DIRECT\nGREENS (AZO)",
+        "hsn_code": "32041450",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : DIRECT\nBROWNS (AZO)",
+        "hsn_code": "32041460",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : DIRECT\nBLACKS (AZO)",
+        "hsn_code": "32041470",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : DIRECT DYES\n(NON - AZO) : YELLOWS",
+        "hsn_code": "32041481",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : DIRECT DYES\n(NON - AZO) : ORANGES",
+        "hsn_code": "32041482",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : DIRECT DYES\n(NON - AZO) : REDS",
+        "hsn_code": "32041483",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : DIRECT DYES\n(NON - AZO) : VIOLETS",
+        "hsn_code": "32041484",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : DIRECT DYES\n(NON - AZO) : BLUES",
+        "hsn_code": "32041485",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : DIRECT DYES\n(NON - AZO) : GREENS",
+        "hsn_code": "32041486",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : DIRECT DYES\n(NON - AZO) : BROWNS",
+        "hsn_code": "32041487",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : DIRECT DYES\n(NON - AZO) : BLACKS",
+        "hsn_code": "32041488",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : DIRECT DYES\n(NON - AZO) : OTHER",
+        "hsn_code": "32041489",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : DIRECT DYES AND PREPARATIONS BASED THEREON : OTHER",
+        "hsn_code": "32041490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "(SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT YELLOW : VAT YELLOW GC)",
+        "hsn_code": "32041511",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT YELLOW : VAT YELLOW 4 (INDATHRENE\nGOLDEN YELLOW GK)",
+        "hsn_code": "32041512",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT YELLOW : OTHER",
+        "hsn_code": "32041519",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT ORANGE : VAT ORANGES 3 (BRILL ORANGE RK)",
+        "hsn_code": "32041521",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT ORANGE : VAT ORANGES 15 (GOLDEN ORANGE\n3G)",
+        "hsn_code": "32041522",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT ORANGE : OTHER",
+        "hsn_code": "32041529",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT RED : VAT RED (BRILL PINK)",
+        "hsn_code": "32041531",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT RED : OTHER",
+        "hsn_code": "32041539",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT VIOLET : VAT VIOLET 1 (BRILL VIOLET 2R)",
+        "hsn_code": "32041541",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT VIOLET : VAT VIOLET 3 (MAGENTA B)",
+        "hsn_code": "32041542",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT VIOLET : OTHER",
+        "hsn_code": "32041549",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON :- VAT BLUE : VAT BLUE 1 (SYNTHETIC INDIGO)",
+        "hsn_code": "32041551",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON :- VAT BLUE : VAT BLUE 4",
+        "hsn_code": "32041552",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON :- VAT BLUE : VAT BLUE 5 (BLUE 2B)",
+        "hsn_code": "32041553",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON :- VAT BLUE : VAT BLUE 6 (BLUE BC)",
+        "hsn_code": "32041554",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON :- VAT BLUE : VAT BLUE 20 (DARK BLUE 30)",
+        "hsn_code": "32041555",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON :- VAT BLUE : VAT BLUE 29 (INDANTHRENE BRILL\nBLUE 4G)",
+        "hsn_code": "32041556",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON :- VAT BLUE : VAT BLUE 43 (CARBAZOLE BLUE)",
+        "hsn_code": "32041557",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON :- VAT BLUE : REDUCED VAT BLUES",
+        "hsn_code": "32041558",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON :- VAT BLUE : OTHER",
+        "hsn_code": "32041559",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT GREEN : VAT GREEN 1 (INDANTHRENE BRILL\nGREEN BFFB)",
+        "hsn_code": "32041561",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT GREEN : VAT GREEN 2 (INDANTHRENE BRILL\nGREEN GG)",
+        "hsn_code": "32041562",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT GREEN : VAT GREEN 4 (INDANTHRENE BRILL\nGREEN 3B)",
+        "hsn_code": "32041563",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT GREEN : VAT GREEN 9 (BLACK BB)",
+        "hsn_code": "32041564",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT GREEN : OTHER",
+        "hsn_code": "32041569",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT BROWN : VAT BROWN 1 (BROWN BR)",
+        "hsn_code": "32041571",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT BROWN : VAT BROWN 3 (BROWN RGR)",
+        "hsn_code": "32041572",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT BROWN : VAT BROWN 5 (BROWN RRD,G)",
+        "hsn_code": "32041573",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT BROWN : OTHER",
+        "hsn_code": "32041579",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT BLACK : VAT BLACK 9 (BLACK RB)",
+        "hsn_code": "32041581",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT BLACK : VAT BLACK 25 (OLIVE T)",
+        "hsn_code": "32041582",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT BLACK : VAT BACK 27 (OLIVE R)",
+        "hsn_code": "32041583",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT BLACK : VAT BLACK 29 (GREY BG)",
+        "hsn_code": "32041584",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : VAT BLACK : OTHER",
+        "hsn_code": "32041589",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : OTHER : SOLUBILISED VAT YELLOWS",
+        "hsn_code": "32041591",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : OTHER : SOLUBILISED VAT ORANGES",
+        "hsn_code": "32041592",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : OTHER : SOLUBILISED VAT REDS",
+        "hsn_code": "32041593",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : OTHER : SOLUBILISED VAT VIOLETS",
+        "hsn_code": "32041594",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : OTHER : SOLUBILISED VAT BLUES",
+        "hsn_code": "32041595",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : OTHER : SOLUBILISED VAT GREENS",
+        "hsn_code": "32041596",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : OTHER : SOLUBILISED VAT BLACKS",
+        "hsn_code": "32041597",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : VAT DYES (INCLUDING THOSE USABLE IN THAT STATE AS\nPIGMENTS) AND PREPARATIONS THEREON : OTHER : SOLUBILISED VAT BLACKS - OTHER",
+        "hsn_code": "32041599",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- REACTIVE DYES AND PREPARATIONS BASED THEREON : YELLOWS",
+        "hsn_code": "32041610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- REACTIVE DYES AND PREPARATIONS BASED THEREON : ORANGES",
+        "hsn_code": "32041620",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- REACTIVE DYES AND PREPARATIONS BASED THEREON : REDS",
+        "hsn_code": "32041630",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- REACTIVE DYES AND PREPARATIONS BASED THEREON : VIOLETS",
+        "hsn_code": "32041640",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- REACTIVE DYES AND PREPARATIONS BASED THEREON : BLUES",
+        "hsn_code": "32041650",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- REACTIVE DYES AND PREPARATIONS BASED THEREON : GREENS",
+        "hsn_code": "32041660",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- REACTIVE DYES AND PREPARATIONS BASED THEREON : BROWNS",
+        "hsn_code": "32041670",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- REACTIVE DYES AND PREPARATIONS BASED THEREON : BLACKS",
+        "hsn_code": "32041680",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- REACTIVE DYES AND PREPARATIONS BASED THEREON : OTHER",
+        "hsn_code": "32041690",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : PIGMENTS AND PREPARATIONS BASED THEREON : PIGMENT\nYELLOW : YELLOW 1 (HANSA YELLOW)",
+        "hsn_code": "32041711",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : PIGMENTS AND PREPARATIONS BASED THEREON : PIGMENT\nYELLOW : OTHER",
+        "hsn_code": "32041719",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : PIGMENTS AND PREPARATIONS BASED THEREON : PIGMENT\nORANGES",
+        "hsn_code": "32041720",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : PIGMENTS AND PREPARATIONS BASED THEREON : PIGMENT RED :\nTOLUIDINE RED",
+        "hsn_code": "32041731",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : PIGMENTS AND PREPARATIONS BASED THEREON : PIGMENT RED :\nOTHER",
+        "hsn_code": "32041739",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : PIGMENTS AND PREPARATIONS BASED THEREON : PIGMENT\nVIOLETS",
+        "hsn_code": "32041740",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : PIGMENTS AND PREPARATIONS BASED THEREON : PIGMENT\nBLUES : BLUE 15 (PATHALOCYANINE BLUE)",
+        "hsn_code": "32041751",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : PIGMENTS AND PREPARATIONS BASED THEREON : PIGMENT\nBLUES : OTHER",
+        "hsn_code": "32041759",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : PIGMENTS AND PREPARATIONS BASED THEREON :- PIGMENT\nGREENS : GREEN 7 (PATHALOVYANINE GREEN)",
+        "hsn_code": "32041761",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : PIGMENTS AND PREPARATIONS BASED THEREON :- PIGMENT\nGREENS : OTHER",
+        "hsn_code": "32041769",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : PIGMENTS AND PREPARATIONS BASED THEREON : PIGMENT\nBROWNS",
+        "hsn_code": "32041770",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : PIGMENTS AND PREPARATIONS BASED THEREON : PIGMENT\nBLACKS",
+        "hsn_code": "32041780",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER : PIGMENTS AND PREPARATIONS BASED THEREON : OTHER",
+        "hsn_code": "32041790",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC COUPLING COMPONENTS\n2,4,5,7,8,13 : AZOIC COUPLING COMPONENT 2 (NAPHTHOL AS)",
+        "hsn_code": "32041911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC COUPLING COMPONENTS\n2,4,5,7,8,13 : AZOIC COUPLING COMPONENT 4 (NAPHTHOL AS - BO)",
+        "hsn_code": "32041912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC COUPLING COMPONENTS\n2,4,5,7,8,13 : AZOIC COUPLING COMPONENT 5 (NAPHTHOL ASG)",
+        "hsn_code": "32041913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC COUPLING COMPONENTS\n2,4,5,7,8,13 : AZOIC COUPLING COMPONENT 7 (NAPHTHOL ASSW)",
+        "hsn_code": "32041914",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC COUPLING COMPONENTS\n2,4,5,7,8,13 : AZOIC COUPLING COMPONENT 8 (NAPHTHOL ASTR)",
+        "hsn_code": "32041915",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC COUPLING COMPONENTS\n2,4,5,7,8,13 : AZOIC COUPLING COMPONENT 13 (NAPHTHOL ASSG)",
+        "hsn_code": "32041916",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC COUPLING COMPONENTS\n14,15,17,18,20 AND OTHERS : AZOIC COUPLING COMPONENT 14 (NAPHTHOL ASPH)",
+        "hsn_code": "32041921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC COUPLING COMPONENTS\n14,15,17,18,20 AND OTHERS : AZOIC COUPLING COMPONENT 15 (NAPHTHOL ASLB)",
+        "hsn_code": "32041922",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC COUPLING COMPONENTS\n14,15,17,18,20 AND OTHERS : AZOIC COUPLING COMPONENT 17 (NAPHTHOL ASBS)",
+        "hsn_code": "32041923",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC COUPLING COMPONENTS\n14,15,17,18,20 AND OTHERS : AZOIC COUPLING COMPONENT 18 (NAPHTHOL ASD)",
+        "hsn_code": "32041924",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC COUPLING COMPONENTS\n14,15,17,18,20 AND OTHERS : AZOIC COUPLING COMPONENT 20 (NAPHTHOL ASOL)",
+        "hsn_code": "32041925",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC COUPLING COMPONENTS\n14,15,17,18,20 AND OTHERS : OTHER",
+        "hsn_code": "32041929",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC DIAZO COMPONENT\n1,2,3,4,5,6,10,11 : AZOIC DIAZO COMPONENT 1 (FAST BORDEAUX GP BASE)",
+        "hsn_code": "32041931",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC DIAZO COMPONENT\n1,2,3,4,5,6,10,11 : AZOIC DIAZO COMPONENT 2 (FAST ORANGE G/GC BASE)",
+        "hsn_code": "32041932",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC DIAZO COMPONENT\n1,2,3,4,5,6,10,11 : AZOIC DIAZO COMPONENT 3 (FAST SCARLET GGIGGS BASE)",
+        "hsn_code": "32041933",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC DIAZO COMPONENT\n1,2,3,4,5,6,10,11 : AZOIC DIAZO COMPONENT 4 (FAST GARMENT GBC BASE)",
+        "hsn_code": "32041934",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC DIAZO COMPONENT\n1,2,3,4,5,6,10,11 : AZOIC DIAZO COMPONENT 5 (FAST RED B BASE)",
+        "hsn_code": "32041935",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC DIAZO COMPONENT\n1,2,3,4,5,6,10,11 : AZOIC DIAZO COMPONENT 6 (FAST ORANGE GR BASE)",
+        "hsn_code": "32041936",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC DIAZO COMPONENT\n1,2,3,4,5,6,10,11 : AZOIC DIAZO COMPONENT 10 (FAST RED R BASE)",
+        "hsn_code": "32041937",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC DIAZO COMPONENT\n1,2,3,4,5,6,10,11 : AZOIC DIAZO COMPONENT 11 (FAST RED TR BASE)",
+        "hsn_code": "32041938",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC DIAZO COMPONENT\n12,13,20,24,32,41,48 AND OTHERS : AZOIC DIAZO COMPONENT 12 (FAST SCARLET G BASE)",
+        "hsn_code": "32041941",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC DIAZO COMPONENT\n12,13,20,24,32,41,48 AND OTHERS : AZOIC DIAZO COMPONENT 13 (FAST SCARLET R BASE)",
+        "hsn_code": "32041942",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC DIAZO COMPONENT\n12,13,20,24,32,41,48 AND OTHERS : AZOIC DIAZO COMPONENT 20 (FAST BLUE BB BASE)",
+        "hsn_code": "32041943",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC DIAZO COMPONENT\n12,13,20,24,32,41,48 AND OTHERS : AZOIC DIAZO COMPONENT 24 (FAST BLUE RR BASE)",
+        "hsn_code": "32041944",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC DIAZO COMPONENT\n12,13,20,24,32,41,48 AND OTHERS : AZOIC DIAZO COMPONENT 32 (FAST RED KB BASE)",
+        "hsn_code": "32041945",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC DIAZO COMPONENT\n12,13,20,24,32,41,48 AND OTHERS : AZOIC DIAZO COMPONENT 41 (FAST VIOLET B BASE)",
+        "hsn_code": "32041946",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC DIAZO COMPONENT\n12,13,20,24,32,41,48 AND OTHERS : AZOIC DIAZO COMPONENT 48 (FAST BLUE B BASE)",
+        "hsn_code": "32041947",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : AZOIC DIAZO COMPONENT\n12,13,20,24,32,41,48 AND OTHERS : OTHER",
+        "hsn_code": "32041949",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 :- AZOIC COLOURS : YELLOWS",
+        "hsn_code": "32041951",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 :- AZOIC COLOURS : ORANGES",
+        "hsn_code": "32041952",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 :- AZOIC COLOURS : REDS",
+        "hsn_code": "32041953",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 :- AZOIC COLOURS : VIOLETS",
+        "hsn_code": "32041954",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 :- AZOIC COLOURS : BLUES",
+        "hsn_code": "32041955",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 :- AZOIC COLOURS : GREENS",
+        "hsn_code": "32041956",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 :- AZOIC COLOURS : BROWNS",
+        "hsn_code": "32041957",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 :- AZOIC COLOURS : BLACKS",
+        "hsn_code": "32041958",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 :- AZOIC COLOURS : OTHER",
+        "hsn_code": "32041959",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : SULPHUR BASED COLOURING MATTERS\n: YELLOWS",
+        "hsn_code": "32041961",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : SULPHUR BASED COLOURING MATTERS\n: ORANGES",
+        "hsn_code": "32041962",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : SULPHUR BASED COLOURING MATTERS\n: REDS",
+        "hsn_code": "32041963",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : SULPHUR BASED COLOURING MATTERS\n: BLUES",
+        "hsn_code": "32041964",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : SULPHUR BASED COLOURING MATTERS\n: GREENS",
+        "hsn_code": "32041965",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : SULPHUR BASED COLOURING MATTERS\n: BROWNS",
+        "hsn_code": "32041966",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : SULPHUR BASED COLOURING MATTERS\n: BLACKS",
+        "hsn_code": "32041967",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : SULPHUR BASED COLOURING MATTERS\n: OTHER",
+        "hsn_code": "32041969",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : SOLVENT BASED COLOURING MATTERS\n: YELLOWS",
+        "hsn_code": "32041971",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : SOLVENT BASED COLOURING MATTERS\n: ORANGES",
+        "hsn_code": "32041972",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : SOLVENT BASED COLOURING MATTERS\n: REDS",
+        "hsn_code": "32041973",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : SOLVENT BASED COLOURING MATTERS\n: VIOLETS",
+        "hsn_code": "32041974",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : SOLVENT BASED COLOURING MATTERS\n: BLUES",
+        "hsn_code": "32041975",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : SOLVENT BASED COLOURING MATTERS\n: GREENS",
+        "hsn_code": "32041976",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : SOLVENT BASED COLOURING MATTERS\n: BROWNS",
+        "hsn_code": "32041977",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : SOLVENT BASED COLOURING MATTERS\n: BLACKS",
+        "hsn_code": "32041978",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : SOLVENT BASED COLOURING MATTERS\n: OTHER",
+        "hsn_code": "32041979",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : FOOD COLOURING MATTERS : YELLOW\n3 (SUNSET YELLOW)",
+        "hsn_code": "32041981",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : FOOD COLOURING MATTERS : YELLOW\n4 (TARTRAZINE)",
+        "hsn_code": "32041982",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : FOOD COLOURING MATTERS : REDS 5\nTO 8 (PONCEAN)",
+        "hsn_code": "32041983",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : FOOD COLOURING MATTERS : RED 9\n(AMARANTH)",
+        "hsn_code": "32041984",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : FOOD COLOURING MATTERS :\nORANGES",
+        "hsn_code": "32041985",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : FOOD COLOURING MATTERS : VIOLETS",
+        "hsn_code": "32041986",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : FOOD COLOURING MATTERS : GREENS",
+        "hsn_code": "32041987",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : FOOD COLOURING MATTERS : BROWNS",
+        "hsn_code": "32041988",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : FOOD COLOURING MATTERS : OTHER",
+        "hsn_code": "32041989",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED\nIN NOTE 3 TO THIS CHAPTER :- OTHER, INCLUDING MIXTURES OF COLOURING MATTER OF TWO\nOR MORE OF THE SUB - HEADINGS 3204 11 TO 3204 19 : OTHER",
+        "hsn_code": "32041990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT BRIGHTENING AGENTS :\nOPTICAL WHITENING AGENTS",
+        "hsn_code": "32042010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED -\nSYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT BRIGHTENING AGENTS :\nOTHER",
+        "hsn_code": "32042090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC\nCOLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT\nBRIGHTENING AGENTS OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED\nOTHER",
+        "hsn_code": "32049000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC COLOURING MATTER, WHETHER OR NOT CHEMICALLY DEFINED; PREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON SYNTHETIC ORGANIC COLOURING MATTER; SYNTHETIC ORGANIC PRODUCTS OF A KIND USED AS FLUORESCENT BRIGHTENING Agens OR AS LUMINOPHORES, WHETHER OR NOT CHEMICALLY DEFINED. SYNTHETIC ORGANIC COLOURING MATTER AND PREPARATIONS BASED THEREON AS SPECIFIED IN NOTE 3 TO THIS CHAPTER:",
+        "hsn_code": "3204",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COLOURING MATTER OF VEGETABLE OR ANIMAL ORIGIN (INCLUDING DYEING EXTRACTS BUT\nEXCLUDING ANIMAL BLACK), WHETHER OR NOT CHEMICALLY DEFINED; PREPARATIONS AS\nSPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON COLOURING MATTER OF VEGETABLE OR\nANIMAL ORIGIN - COLOURING MATTER OF VEGETABLE OR ANIMAL ORIGIN (INCLUDING DYEING\nEXTRACTS BUT EXCLUDING ANIMAL BLACK), WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON COLOURING MATTER OF\nVEGETABLE OR ANIMAL ORIGIN : CUTCH (CATECHU) EXTRACTS",
+        "hsn_code": "32030010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COLOURING MATTER OF VEGETABLE OR ANIMAL ORIGIN (INCLUDING DYEING EXTRACTS BUT\nEXCLUDING ANIMAL BLACK), WHETHER OR NOT CHEMICALLY DEFINED; PREPARATIONS AS\nSPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON COLOURING MATTER OF VEGETABLE OR\nANIMAL ORIGIN - COLOURING MATTER OF VEGETABLE OR ANIMAL ORIGIN (INCLUDING DYEING\nEXTRACTS BUT EXCLUDING ANIMAL BLACK), WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON COLOURING MATTER OF\nVEGETABLE OR ANIMAL ORIGIN : FOOD COLOURS OTHER THAN SYNTHETIC",
+        "hsn_code": "32030020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COLOURING MATTER OF VEGETABLE OR ANIMAL ORIGIN (INCLUDING DYEING EXTRACTS BUT\nEXCLUDING ANIMAL BLACK), WHETHER OR NOT CHEMICALLY DEFINED; PREPARATIONS AS\nSPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON COLOURING MATTER OF VEGETABLE OR\nANIMAL ORIGIN - COLOURING MATTER OF VEGETABLE OR ANIMAL ORIGIN (INCLUDING DYEING\nEXTRACTS BUT EXCLUDING ANIMAL BLACK), WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON COLOURING MATTER OF\nVEGETABLE OR ANIMAL ORIGIN : LAC - DYE",
+        "hsn_code": "32030030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COLOURING MATTER OF VEGETABLE OR ANIMAL ORIGIN (INCLUDING DYEING EXTRACTS BUT\nEXCLUDING ANIMAL BLACK), WHETHER OR NOT CHEMICALLY DEFINED; PREPARATIONS AS\nSPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON COLOURING MATTER OF VEGETABLE OR\nANIMAL ORIGIN - COLOURING MATTER OF VEGETABLE OR ANIMAL ORIGIN (INCLUDING DYEING\nEXTRACTS BUT EXCLUDING ANIMAL BLACK), WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON COLOURING MATTER OF\nVEGETABLE OR ANIMAL ORIGIN : NATURAL INDIGO",
+        "hsn_code": "32030040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COLOURING MATTER OF VEGETABLE OR ANIMAL ORIGIN (INCLUDING DYEING EXTRACTS BUT\nEXCLUDING ANIMAL BLACK), WHETHER OR NOT CHEMICALLY DEFINED; PREPARATIONS AS\nSPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON COLOURING MATTER OF VEGETABLE OR\nANIMAL ORIGIN - COLOURING MATTER OF VEGETABLE OR ANIMAL ORIGIN (INCLUDING DYEING\nEXTRACTS BUT EXCLUDING ANIMAL BLACK), WHETHER OR NOT CHEMICALLY DEFINED;\nPREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON COLOURING MATTER OF\nVEGETABLE OR ANIMAL ORIGIN : OTHER",
+        "hsn_code": "32030090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COLOURING MATTER OF VEGETABLE OR ANIMAL ORIGIN (INCLUDING DYEING EXTRACTS BUT EXCLUDING ANIMAL BLACK), WHETHER OR NOT CHEMICALLY DEFINED; PREPARATIONS AS SPECIFIED IN NOTE 3 TO THIS CHAPTER BASED ON COLOURING MATTER OF VEGETABLE OR ANIMAL ORIGIN",
+        "hsn_code": "3203",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC TANNING SUBSTANCES; INORGANIC TANNING SUBSTANCES; TANNING\nPREPARATIONS, WHETHER OR NOT CONTAINING NATURAL TANNING SUBSTANCES; ENZYMATIC\nPREPARATIONS FOR PRE - TANNING SYNTHETIC ORGANIC TANNING SUBSTANCES",
+        "hsn_code": "32021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC TANNING SUBSTANCES; INORGANIC TANNING SUBSTANCES; TANNING\nPREPARATIONS, WHETHER OR NOT CONTAINING NATURAL TANNING SUBSTANCES; ENZYMATIC\nPREPARATIONS FOR PRE - TANNING - OTHER : INORGANIC TANNING SUBSTANCES",
+        "hsn_code": "32029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC TANNING SUBSTANCES; INORGANIC TANNING SUBSTANCES; TANNING\nPREPARATIONS, WHETHER OR NOT CONTAINING NATURAL TANNING SUBSTANCES; ENZYMATIC\nPREPARATIONS FOR PRE - TANNING - OTHER : TANNING PREPARATIONS, WHETHER OR NOT\nCONTAINING NATURAL TANNING SUBSTANCES",
+        "hsn_code": "32029020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC TANNING SUBSTANCES; INORGANIC TANNING SUBSTANCES; TANNING\nPREPARATIONS, WHETHER OR NOT CONTAINING NATURAL TANNING SUBSTANCES; ENZYMATIC\nPREPARATIONS FOR PRE - TANNING - OTHER : ENZYMATIC PREPARATIONS FOR PRE - TANNING",
+        "hsn_code": "32029030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC TANNING SUBSTANCES; INORGANIC TANNING SUBSTANCES; TANNING\nPREPARATIONS, WHETHER OR NOT CONTAINING NATURAL TANNING SUBSTANCES; ENZYMATIC\nPREPARATIONS FOR PRE - TANNING - OTHER : OTHER",
+        "hsn_code": "32029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SYNTHETIC ORGANIC TANNING SUBSTANCES; INORGANIC TANNING SUBSTANCES; TANNING PREPARATIONS,WHETHER OR NOT CONTAINING NATURAL TANNING SUBSTANCES; ENZYMATIC PREPARATIONS FOR PRE-TANNING",
+        "hsn_code": "3202",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANNING EXTRACTS OF VEGETABLE ORIGIN; TANNINS AND THEIR SALTS, ETHERS, ESTERS AND\nOTHER DERIVATIVES QUEBRACHO EXTRACT",
+        "hsn_code": "32011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANNING EXTRACTS OF VEGETABLE ORIGIN; TANNINS AND THEIR SALTS, ETHERS, ESTERS AND\nOTHER DERIVATIVES WATTLE EXTRACT",
+        "hsn_code": "32012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANNING EXTRACTS OF VEGETABLE ORIGIN; TANNINS AND THEIR SALTS, ETHERS, ESTERS AND\nOTHER DERIVATIVES - OTHER : GAMBIER EXTRACTS",
+        "hsn_code": "32019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANNING EXTRACTS OF VEGETABLE ORIGIN; TANNINS AND THEIR SALTS, ETHERS, ESTERS AND\nOTHER DERIVATIVES - OTHER : MYROBALAN FRUIT EXTRACT",
+        "hsn_code": "32019020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANNING EXTRACTS OF VEGETABLE ORIGIN; TANNINS AND THEIR SALTS, ETHERS, ESTERS AND\nOTHER DERIVATIVES - OTHER : GALLOTANNIC ACID (TANNIN, DIGALLIC ACID)",
+        "hsn_code": "32019030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANNING EXTRACTS OF VEGETABLE ORIGIN; TANNINS AND THEIR SALTS, ETHERS, ESTERS AND\nOTHER DERIVATIVES - OTHER : OTHER",
+        "hsn_code": "32019090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TANNING EXTRACTS OF VEGETABLE ORIGIN; TANNINS AND THEIR SALTS, ETHERS, ESTERS AND OTHER DERIVATIVES",
+        "hsn_code": "3201",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS CONTAINING TWO OR THREE OF THE FERTILISING ELEMENTS NITROGEN, PHOSPHORUS AND POTASSIUM; OTHER FERTILISERS; GOODS OF THIS CHAPTER IN TABLETS OR SIMILAR FORMS OR IN PACKAGES OF A GROSS WEIGHT NOT EXCEEDING 10 KG",
+        "hsn_code": "3105",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERSCONTAINING TWO OR THREE OF THE FERTILISING\nELEMENTS NITROGEN,PHOSPHORUS AND POTASSIUM; OTHER FERTILISERS;GOODS OF THIS\nCHAPTER IN TABLETS OR SIMILAR FORMS OR IN PACKAGES OF A GROSS WEIGHT NOT\nEXCEEDING 10 KG OTHER : OTHER",
+        "hsn_code": "31059090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERSCONTAINING TWO OR THREE OF THE FERTILISING\nELEMENTS NITROGEN,PHOSPHORUS AND POTASSIUM; OTHER FERTILISERS;GOODS OF THIS\nCHAPTER IN TABLETS OR SIMILAR FORMS OR IN PACKAGES OF A GROSS WEIGHT NOT\nEXCEEDING 10 KG OTHER : MINERAL OR CHEMICAL FERTILISERS CONTAINING TWO FERTILISING\nELEMENTS NAMELY NITROGEN AND POTASSIUM",
+        "hsn_code": "31059010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERSCONTAINING TWO OR THREE OF THE FERTILISING\nELEMENTS NITROGEN,PHOSPHORUS AND POTASSIUM; OTHER FERTILISERS;GOODS OF THIS\nCHAPTER IN TABLETS OR SIMILAR FORMS OR IN PACKAGES OF A GROSS WEIGHT NOT\nEXCEEDING 10 KG MINERAL OR CHEMICAL FERTILISERS CONTAINING THE TWO FERTILISING\nELEMENTS PHOSPHORUS AND POTASSIUM",
+        "hsn_code": "31056000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERSCONTAINING TWO OR THREE OF THE FERTILISING\nELEMENTS NITROGEN,PHOSPHORUS AND POTASSIUM; OTHER FERTILISERS;GOODS OF THIS\nCHAPTER IN TABLETS OR SIMILAR FORMS OR IN PACKAGES OF A GROSS WEIGHT NOT\nEXCEEDING 10 KG OTHER MINERAL OR CHEMICAL FERTILISERS CONTAINING THE TWO\nFERTILISING ELEMENTS NITROGEN AND PHOSPHORUS : OTHER",
+        "hsn_code": "31055900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERSCONTAINING TWO OR THREE OF THE FERTILISING\nELEMENTS NITROGEN,PHOSPHORUS AND POTASSIUM; OTHER FERTILISERS;GOODS OF THIS\nCHAPTER IN TABLETS OR SIMILAR FORMS OR IN PACKAGES OF A GROSS WEIGHT NOT\nEXCEEDING 10 KG OTHER MINERAL OR CHEMICAL FERTILISERS CONTAINING THE TWO\nFERTILISING ELEMENTS NITROGEN AND PHOSPHORUS : CONTAINING NITRATES AND\nPHOSPHATES",
+        "hsn_code": "31055100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERSCONTAINING TWO OR THREE OF THE FERTILISING\nELEMENTS NITROGEN,PHOSPHORUS AND POTASSIUM; OTHER FERTILISERS;GOODS OF THIS\nCHAPTER IN TABLETS OR SIMILAR FORMS OR IN PACKAGES OF A GROSS WEIGHT NOT\nEXCEEDING 10 KG AMMONIUM DIHYDROGEN ORTHO PHOSPHATE (MONOAMMONIUM\nPHOSPHATE) AND MIXTURES THEREOF WITH DIAMMONIUM HYDROGEN ORTHOPHOSPHATE\n(DIAMMONIUM PHOSPHATE)",
+        "hsn_code": "31054000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERSCONTAINING TWO OR THREE OF THE FERTILISING\nELEMENTS NITROGEN,PHOSPHORUS AND POTASSIUM; OTHER FERTILISERS;GOODS OF THIS\nCHAPTER IN TABLETS OR SIMILAR FORMS OR IN PACKAGES OF A GROSS WEIGHT NOT\nEXCEEDING 10 KG DIAMMONIUM HYDROGEN ORTHO PHOSPHATE (DIAMMONIUM PHOSPHATE)",
+        "hsn_code": "31053000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERSCONTAINING TWO OR THREE OF THE FERTILISING\nELEMENTS NITROGEN,PHOSPHORUS AND POTASSIUM; OTHER FERTILISERS;GOODS OF THIS\nCHAPTER IN TABLETS OR SIMILAR FORMS OR IN PACKAGES OF A GROSS WEIGHT NOT\nEXCEEDING 10 KG MINERAL OR CHEMICAL FERTILISERS CONTAINING THE THREE FERTILISING\nELEMENTS NITROGEN, PHOSPHORUS AND POTASSIUM",
+        "hsn_code": "31052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERSCONTAINING TWO OR THREE OF THE FERTILISING\nELEMENTS NITROGEN,PHOSPHORUS AND POTASSIUM; OTHER FERTILISERS;GOODS OF THIS\nCHAPTER IN TABLETS OR SIMILAR FORMS OR IN PACKAGES OF A GROSS WEIGHT NOT\nEXCEEDING 10 KG GOODS OF THIS CHAPTER IN TABLETS OR SIMILAR FORMS OR IN PACKAGES OF\nA GROSS WEIGHT NOT EXCEEDING 10 KG.",
+        "hsn_code": "31051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, POTASSIC",
+        "hsn_code": "3104",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, POTASSIC OTHER",
+        "hsn_code": "31049000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, POTASSIC POTASSIUM SULPHATE",
+        "hsn_code": "31043000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, POTASSIC POTASSIUM CHLORIDE",
+        "hsn_code": "31042000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, POTASSIC CARNALLITE, SYLVITE AND OTHER CRUDE\nNATURAL POTASSIUM SALTS",
+        "hsn_code": "31041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, PHOSPHATIC",
+        "hsn_code": "3103",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, PHOSPHATIC OTHER",
+        "hsn_code": "31039000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, PHOSPHATIC BASIC SLAG",
+        "hsn_code": "31032000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, PHOSPHATIC SUPERPHOSPHATES",
+        "hsn_code": "31031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, NITROGENOUS",
+        "hsn_code": "3102",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, NITROGENOUS OTHER, INCLUDING MIXTURES NOT\nSPECIFIED IN THE FOREGOING SUB-HEADINGS : OTHER",
+        "hsn_code": "31029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, NITROGENOUS OTHER, INCLUDING MIXTURES NOT\nSPECIFIED IN THE FOREGOING SUB-HEADINGS : DOUBLE SALTS OR MIXTURES OF CALCIUM\nNITRATE AND MAGNESIUM NITRATE",
+        "hsn_code": "31029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, NITROGENOUS MIXTURES OF UREA AND AMMONIUM\nNITRATE IN AQUEOUS OR AMMONIACAL SOLUTION",
+        "hsn_code": "31028000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, NITROGENOUS CALCIUM CYANAMIDE",
+        "hsn_code": "31027000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, NITROGENOUS DOUBLE SALTS AND MIXTURES OF\nCALCIUM NITRATE AND AMMONIUM NITRATE",
+        "hsn_code": "31026000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, NITROGENOUS SODIUM NITRATE",
+        "hsn_code": "31025000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, NITROGENOUS MIXTURES OF AMMONIUM NITRATE WITH\nCALCIUM CARBONATE OR OTHER INORGANIC NON-FERTILISING SUBSTANCES",
+        "hsn_code": "31024000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, NITROGENOUS AMMONIUM NITRATE, WHETHER OR NOT\nIN AQUEOUS SOLUTION",
+        "hsn_code": "31023000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, NITROGENOUS AMMONIUM SULPHATE; DOUBLE SALTS\nAND MIXTURES OF AMMONIUM SULPHATE AND AMMONIUM NITRATE: OTHER : OTHER",
+        "hsn_code": "31022990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, NITROGENOUS AMMONIUM SULPHATE; DOUBLE SALTS\nAND MIXTURES OF AMMONIUM SULPHATE AND AMMONIUM NITRATE: OTHER : AMMONIUM\nSULPHONITRATE",
+        "hsn_code": "31022910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, NITROGENOUS AMMONIUM SULPHATE; DOUBLE SALTS\nAND MIXTURES OF AMMONIUM SULPHATE AND AMMONIUM NITRATE: AMMONIUM SULPHATE",
+        "hsn_code": "31022100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL OR CHEMICAL FERTILISERS, NITROGENOUS UREA, WHETHER OR NOT IN AQUEOUS\nSOLUTION",
+        "hsn_code": "31021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER\nOF SIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS: - OTHER\nINORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER OF\nSIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS : AMALGAMS,\nOTHER THAN OF PRECIOUS METALS --- DISTILLED OR CONDUCTIVITY WATER AND WATER OF\nSIMILAR PURITY",
+        "hsn_code": "28530010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER\nOF SIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS: - OTHER\nINORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER OF\nSIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS : AMALGAMS,\nOTHER THAN OF PRECIOUS METALS --- LIQUID AIR, WHETHER OR NOT ANY FRACTION OF RARE\nGASES HAS BEEN REMOVED",
+        "hsn_code": "28530020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER\nOF SIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS: - OTHER\nINORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER OF\nSIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS : AMALGAMS,\nOTHER THAN OF PRECIOUS METALS --- COMPRESSED AIR",
+        "hsn_code": "28530030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER\nOF SIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS: - OTHER\nINORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER OF\nSIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS : AMALGAMS,\nOTHER THAN OF PRECIOUS METALS --- AMALGAMS, OTHER THAN OF PRECIOUS METALS",
+        "hsn_code": "28530040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER\nOF SIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS: - OTHER\nINORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER OF\nSIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS : AMALGAMS,\nOTHER THAN OF PRECIOUS METALS --- OTHER ---- CYANOGEN CHLORIDE [(CN) CL]",
+        "hsn_code": "28530091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER\nOF SIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS: - OTHER\nINORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER OF\nSIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS : AMALGAMS,\nOTHER THAN OF PRECIOUS METALS --- OTHER ---- OTHER",
+        "hsn_code": "28530099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY  WATER AND WATER OF SIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED); COMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS",
+        "hsn_code": "2853",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN PEROXIDE, WHETHER OR NOT SOLIDIFIED WITH UREA",
+        "hsn_code": "28470000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDS, INORGANIC OR ORGANIC, OF RARE-EARTH METALS, OF YTTRIUM OR OF SCANDIUM OR OF MIXTURES OF THESE METALS",
+        "hsn_code": "2847",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ISOTOPES OTHER THAN THOSE OF HEADING 2844; COMPOUNDS, INORGANIC OR ORGANIC, OF\nSUCH ISOTOPES, WHETHER OR NOT CHEMICALLY DEFINED -HEAVY WATER (DEUTERIUM OXIDE)",
+        "hsn_code": "28451000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ISOTOPES OTHER THAN THOSE OF HEADING 2844; COMPOUNDS, INORGANIC OR ORGANIC, OF\nSUCH ISOTOPES, WHETHER OR NOT CHEMICALLY DEFINED - OTHER : NUCLEAR FUELS NOT\nELSEWHERE INCLUDED OR SPECIFIED",
+        "hsn_code": "28459010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ISOTOPES OTHER THAN THOSE OF HEADING 2844; COMPOUNDS, INORGANIC OR ORGANIC, OF\nSUCH ISOTOPES, WHETHER OR NOT CHEMICALLY DEFINED - OTHER : OTHER",
+        "hsn_code": "28459090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ISOTOPES OTHER THAN THOSE OF HEADING 2844; COMPOUNDS, INORGANIC OR ORGANIC, OF SUCH ISOTOPES, WHETHER OR NOT CHEMICALLY DEFINED",
+        "hsn_code": "2845",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "DIPHOSPHORUS PENTAOXIDE; PHOSPHORIC ACID; POLYPHOSPHORIC ACIDS, WHETHER OR NOT\nCHEMICALLY DEFINED - DIPHOSPHORUS PENTAOXIDE",
+        "hsn_code": "28091000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "DIPHOSPHORUS PENTAOXIDE; PHOSPHORIC ACID; POLYPHOSPHORIC ACIDS, WHETHER OR NOT\nCHEMICALLY DEFINED - PHOSPHORIC ACID AND POLYPHOSPHORIC ACIDS : PHOSPORIC ACID",
+        "hsn_code": "28092010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "DIPHOSPHORUS PENTAOXIDE; PHOSPHORIC ACID; POLYPHOSPHORIC ACIDS, WHETHER OR NOT\nCHEMICALLY DEFINED - PHOSPHORIC ACID AND POLYPHOSPHORIC ACIDS : POLYPHOSPHORIC\nACIDS",
+        "hsn_code": "28092020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "DIPHOSPHORUS PENTAOXIDE; PHOSPHORIC ACID; POLYPHOSPHORIC ACIDS WHETHER OR NOT CHEMICALLY DEFINED",
+        "hsn_code": "2809",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BITUMINOUS MIXTURES BASED ON NATURAL ASPHALT, ON NATURAL BITUMEN, ON PETROLEUM BITUMEN, ON MINERAL TAR OR ON MINERAL TAR PITCH (FOR EXAMPLE, BITUMINOUS MASTICS, CUT BACKS)",
+        "hsn_code": "2715",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BITUMINOUS MIXTURES BASED ON NATURAL ASPHALT, ON NATURAL BITUMEN, ON\nPETROLEUM BITUMEN, ON MINERAL TAR OR ON MINERAL TAR PITCH (FOR EXAMPLE,\nBITUMINOUS MASTICS, CUT BACKS) - BITUMINOUS MIXTURES BASED ON NATURAL ASPHALT,\nON NATURAL BITUMEN, ON PETROLEUM BITUMEN, ON MINERAL TAR OR ON MINERAL TAR\nPITCH (FOR EXAMPLE, BITUMINOUS MASTICS, CUT BACKS): OTHER",
+        "hsn_code": "27150090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BITUMINOUS MIXTURES BASED ON NATURAL ASPHALT, ON NATURAL BITUMEN, ON\nPETROLEUM BITUMEN, ON MINERAL TAR OR ON MINERAL TAR PITCH (FOR EXAMPLE,\nBITUMINOUS MASTICS, CUT BACKS) - BITUMINOUS MIXTURES BASED ON NATURAL ASPHALT,\nON NATURAL BITUMEN, ON PETROLEUM BITUMEN, ON MINERAL TAR OR ON MINERAL TAR\nPITCH (FOR EXAMPLE, BITUMINOUS MASTICS, CUT BACKS): CUT BACKS, BITUMINOUS OR\nASPHALT",
+        "hsn_code": "27150010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BITUMEN AND ASPHALT, NATURAL BITUMINOUS OR OIL SHALE AND TAR SANDS; ASPHALTITES AND ASPHALTIC ROCKS",
+        "hsn_code": "2714",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BITUMEN AND ASPHALT, NATURAL; BITUMINOUS OR OIL SHALE AND TAR SANDS; ASPHALTITES\nAND ASPHALTIC ROCKS - OTHER: OTHER",
+        "hsn_code": "27149090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BITUMEN AND ASPHALT, NATURAL; BITUMINOUS OR OIL SHALE AND TAR SANDS; ASPHALTITES\nAND ASPHALTIC ROCKS - OTHER: GILSONETE",
+        "hsn_code": "27149030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BITUMEN AND ASPHALT, NATURAL; BITUMINOUS OR OIL SHALE AND TAR SANDS; ASPHALTITES\nAND ASPHALTIC ROCKS - OTHER: BITUMEN, NATURAL",
+        "hsn_code": "27149020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BITUMEN AND ASPHALT, NATURAL; BITUMINOUS OR OIL SHALE AND TAR SANDS; ASPHALTITES\nAND ASPHALTIC ROCKS - OTHER: ASPHALT, NATURAL",
+        "hsn_code": "27149010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "BITUMEN AND ASPHALT, NATURAL; BITUMINOUS OR OIL SHALE AND TAR SANDS; ASPHALTITES\nAND ASPHALTIC ROCKS -BITUMINOUS OR OIL SHALE AND TAR SANDS",
+        "hsn_code": "27141000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM COKE, PETROLEUM BITUMEN AND OTHER RESIDUES OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS",
+        "hsn_code": "2713",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM COKE, PETROLEUM BITUMEN AND OTHER RESIDUES OF PETROLEUM OILS OR OF\nOILS OBTAINED FROM BITUMINOUS MINERALS - OTHER RESIDUES OF PETROLEUM OILS OR OF\nOILS OBTAINED FROM BITUMINOUS MINERALS",
+        "hsn_code": "27139000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM COKE, PETROLEUM BITUMEN AND OTHER RESIDUES OF PETROLEUM OILS OR OF\nOILS OBTAINED FROM BITUMINOUS MINERALS - PETROLEUM BITUMEN",
+        "hsn_code": "27132000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM COKE, PETROLEUM BITUMEN AND OTHER RESIDUES OF PETROLEUM OILS OR OF\nOILS OBTAINED FROM BITUMINOUS MINERALS - PETROLEUM COKE: CALCINED",
+        "hsn_code": "27131200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM COKE, PETROLEUM BITUMEN AND OTHER RESIDUES OF PETROLEUM OILS OR OF\nOILS OBTAINED FROM BITUMINOUS MINERALS - PETROLEUM COKE: NOT CALCINED",
+        "hsn_code": "27131100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM JELLY, PARAFFIN WAX, MICROCYSTALLINE PETROLEUM WAX, SLACK WAX, OZOKERITE, LIGNITE WAX, PETA WAX, OTHER MINERAL WAXES, AND SIMILAR PRODUCTS OBTAINED BY SYNTHESIS OR BY OTHER PROCESSES, WHETHER OR NOT COLOURED",
+        "hsn_code": "2712",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM JELLY, PARAFFIN WAX, MICROCRYSTALLINE PETROLEUM WAX, SLACK WAX,\nOZOKERITE, LIGNITE WAX, PEAT WAX, OTHER MINERAL WAXES, AND SIMILAR PRODUCTS\nOBTAINED BY SYNTHESIS OR BY OTHER PROCESSES, WHETHER OR NOT COLOURED- OTHER:\nOTHER",
+        "hsn_code": "27129090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARAFFIN WAX CONTAINING BY WEIGHT 0.75 PERCENT OR MORE OF OIL",
+        "hsn_code": "27129040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM JELLY, PARAFFIN WAX, MICROCRYSTALLINE PETROLEUM WAX, SLACK WAX,\nOZOKERITE, LIGNITE WAX, PEAT WAX, OTHER MINERAL WAXES, AND SIMILAR PRODUCTS\nOBTAINED BY SYNTHESIS OR BY OTHER PROCESSES, WHETHER OR NOT COLOURED- OTHER:\nSLACK WAX",
+        "hsn_code": "27129030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM JELLY, PARAFFIN WAX, MICROCRYSTALLINE PETROLEUM WAX, SLACK WAX,\nOZOKERITE, LIGNITE WAX, PEAT WAX, OTHER MINERAL WAXES, AND SIMILAR PRODUCTS\nOBTAINED BY SYNTHESIS OR BY OTHER PROCESSES, WHETHER OR NOT COLOURED- OTHER:\nLIGNITE WAX",
+        "hsn_code": "27129020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM JELLY, PARAFFIN WAX, MICROCRYSTALLINE PETROLEUM WAX, SLACK WAX,\nOZOKERITE, LIGNITE WAX, PEAT WAX, OTHER MINERAL WAXES, AND SIMILAR PRODUCTS\nOBTAINED BY SYNTHESIS OR BY OTHER PROCESSES, WHETHER OR NOT COLOURED- OTHER:\nMICRO-CRYSTALLINE PETROLEUM WAX",
+        "hsn_code": "27129010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM JELLY, PARAFFIN WAX, MICROCRYSTALLINE PETROLEUM WAX, SLACK WAX,\nOZOKERITE, LIGNITE WAX, PEAT WAX, OTHER MINERAL WAXES, AND SIMILAR PRODUCTS\nOBTAINED BY SYNTHESIS OR BY OTHER PROCESSES, WHETHER OR NOT COLOURED - PARAFFIN\nWAX CONTAINING BY WEIGHT LESS THAN 0.75 % OF OIL: OTHER",
+        "hsn_code": "27122090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM JELLY, PARAFFIN WAX, MICROCRYSTALLINE PETROLEUM WAX, SLACK WAX,\nOZOKERITE, LIGNITE WAX, PEAT WAX, OTHER MINERAL WAXES, AND SIMILAR PRODUCTS\nOBTAINED BY SYNTHESIS OR BY OTHER PROCESSES, WHETHER OR NOT COLOURED - PARAFFIN\nWAX CONTAINING BY WEIGHT LESS THAN 0.75 % OF OIL: CHLORINATED PARAFFIN WAX",
+        "hsn_code": "27122010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PARAFFIN WAX CONTAINING BY WEIGHT LESS THAN .75 PERCENT OF OIL",
+        "hsn_code": "27122000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM JELLY, PARAFFIN WAX, MICROCRYSTALLINE PETROLEUM WAX, SLACK WAX,\nOZOKERITE, LIGNITE WAX, PEAT WAX, OTHER MINERAL WAXES, AND SIMILAR PRODUCTS\nOBTAINED BY SYNTHESIS OR BY OTHER PROCESSES, WHETHER OR NOT COLOURED -\nPETROLEUM JELLY: OTHER",
+        "hsn_code": "27121090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM JELLY, PARAFFIN WAX, MICROCRYSTALLINE PETROLEUM WAX, SLACK WAX,\nOZOKERITE, LIGNITE WAX, PEAT WAX, OTHER MINERAL WAXES, AND SIMILAR PRODUCTS\nOBTAINED BY SYNTHESIS OR BY OTHER PROCESSES, WHETHER OR NOT COLOURED -\nPETROLEUM JELLY: CRUDE",
+        "hsn_code": "27121010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM GASES AND OTHER GASEOUS HYDROCARBONS",
+        "hsn_code": "2711",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM GASES AND OTHER GASEOUS HYDROCARBONS - IN GASEOUS STATE: OTHER",
+        "hsn_code": "27112900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM GASES AND OTHER GASEOUS HYDROCARBONS - IN GASEOUS STATE: NATURAL GAS",
+        "hsn_code": "27112100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM GASES AND OTHER GASEOUS HYDROCARBONS - LIQUIFIED: ETHYLENE, PROPYLENE,\nBUTYLENE AND BUTADIENE",
+        "hsn_code": "27111400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM GASES AND OTHER GASEOUS HYDROCARBONS - LIQUIFIED: NATURAL GAS",
+        "hsn_code": "27111100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM OILS AND OILS OBTAINED FROM BITUMINOUS MINERALS (OTHER THAN CRUDE) AND PREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70% OR MORE OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS BEING THE BASIC CONSTITUENTS OF THE PREPARATIONS OTHER THAN THOSE CONTAINING BIODIESEL AND OTHER THAN WASTE OILS",
+        "hsn_code": "2710",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM OILS AND OILS OBTAINED FROM BITUMINOUS MINERALS, OTHER THAN CRUDE;\nPREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70% OR\nMORE OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS\nBEING THE BASIC CONSTITUENTS OF THE PREPARATIONS; WASTE OILS -OTHER",
+        "hsn_code": "27109900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM OILS AND OILS OBTAINED FROM BITUMINOUS MINERALS, OTHER THAN CRUDE;\nPREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70% OR\nMORE OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS\nBEING THE BASIC CONSTITUENTS OF THE PREPARATIONS; WASTE OILS - WASTE OIL:\nCONTAINING POLYCHLORINATED BIPHENYLS (PCBS), POLYCHLORINATED TERPHENYLS (PCTS) OR\nPOLYBROMINATED BIPHENYLS (PBBS)",
+        "hsn_code": "27109100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM OILS AND OILS OBTAINED FROM BITUMINOUS MINERALS (OTHER THAN CRUDE)\nAND PREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70%\nOR MORE OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE\nOILS BEING THE BASIC CONSTITUENTS OF THE PREPARATIONS, CONTAINING BIO-DIESEL, OTHER\nTHAN WASTE OILS",
+        "hsn_code": "27102000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM OILS AND OILS OBTAINED FROM BITUMINOUS MINERALS, OTHER THAN CRUDE;\nPREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70% OR\nMORE OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS\nBEING THE BASIC CONSTITUENTS OF THE PREPARATIONS; WASTE OILS - PETROLEUM OILS AND\nOILS OBTAINED FROM BITUMINOUS MINERALS (OTHER THAN CRUDE) AND PREPARATIONS NOT\nELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70% OR MORE OF PETROLEUM\nOILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS BEING THE BASIC\nCONSTITUENTS OF THE PREPARATIONS, OTHER THAN WASTE OILS: - OTHER: OTHER",
+        "hsn_code": "27101990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM OILS AND OILS OBTAINED FROM BITUMINOUS MINERALS, OTHER THAN CRUDE;\nPREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70% OR\nMORE OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS\nBEING THE BASIC CONSTITUENTS OF THE PREPARATIONS; WASTE OILS - PETROLEUM OILS AND\nOILS OBTAINED FROM BITUMINOUS MINERALS (OTHER THAN CRUDE) AND PREPARATIONS NOT\nELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70% OR MORE OF PETROLEUM\nOILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS BEING THE BASIC\nCONSTITUENTS OF THE PREPARATIONS, OTHER THAN WASTE OILS: - OTHER: LUBRICATING OIL",
+        "hsn_code": "27101980",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM OILS AND OILS OBTAINED FROM BITUMINOUS MINERALS, OTHER THAN CRUDE;\nPREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70% OR\nMORE OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS\nBEING THE BASIC CONSTITUENTS OF THE PREPARATIONS; WASTE OILS - PETROLEUM OILS AND\nOILS OBTAINED FROM BITUMINOUS MINERALS (OTHER THAN CRUDE) AND PREPARATIONS NOT\nELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70% OR MORE OF PETROLEUM\nOILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS BEING THE BASIC\nCONSTITUENTS OF THE PREPARATIONS, OTHER THAN WASTE OILS: - OTHER: JUTE BATCHING\nOIL AND TEXTILE OIL",
+        "hsn_code": "27101970",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM OILS AND OILS OBTAINED FROM BITUMINOUS MINERALS, OTHER THAN CRUDE;\nPREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70% OR\nMORE OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS\nBEING THE BASIC CONSTITUENTS OF THE PREPARATIONS; WASTE OILS - PETROLEUM OILS AND\nOILS OBTAINED FROM BITUMINOUS MINERALS (OTHER THAN CRUDE) AND PREPARATIONS NOT\nELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70% OR MORE OF PETROLEUM\nOILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS BEING THE BASIC\nCONSTITUENTS OF THE PREPARATIONS, OTHER THAN WASTE OILS: - OTHER: BASE OIL",
+        "hsn_code": "27101960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM OILS AND OILS OBTAINED FROM BITUMINOUS MINERALS, OTHER THAN CRUDE;\nPREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70% OR\nMORE OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS\nBEING THE BASIC CONSTITUENTS OF THE PREPARATIONS; WASTE OILS - PETROLEUM OILS AND\nOILS OBTAINED FROM BITUMINOUS MINERALS (OTHER THAN CRUDE) AND PREPARATIONS NOT\nELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70% OR MORE OF PETROLEUM\nOILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS BEING THE BASIC\nCONSTITUENTS OF THE PREPARATIONS, OTHER THAN WASTE OILS: - OTHER: FUEL OIL",
+        "hsn_code": "27101950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM OILS AND OILS OBTAINED FROM BITUMINOUS MINERALS, OTHER THAN CRUDE;\nPREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70% OR\nMORE OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS\nBEING THE BASIC CONSTITUENTS OF THE PREPARATIONS; WASTE OILS - PETROLEUM OILS AND\nOILS OBTAINED FROM BITUMINOUS MINERALS (OTHER THAN CRUDE) AND PREPARATIONS NOT\nELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70% OR MORE OF PETROLEUM\nOILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS BEING THE BASIC\nCONSTITUENTS OF THE PREPARATIONS, OTHER THAN WASTE OILS: - OTHER: LIGHT DIESEL OIL\n(LDO)",
+        "hsn_code": "27101940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "HIGH SPEED DIESEL OIL",
+        "hsn_code": "27101930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM OILS AND OILS OBTAINED FROM BITUMINOUS MINERALS, OTHER THAN CRUDE;\nPREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70% OR\nMORE OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS\nBEING THE BASIC CONSTITUENTS OF THE PREPARATIONS; WASTE OILS - PETROLEUM OILS AND\nOILS OBTAINED FROM BITUMINOUS MINERALS (OTHER THAN CRUDE) AND PREPARATIONS NOT\nELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70% OR MORE OF PETROLEUM\nOILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS BEING THE BASIC\nCONSTITUENTS OF THE PREPARATIONS, OTHER THAN WASTE OILS: - OTHER: AVIATION\nTURBINE FUEL (ATF)",
+        "hsn_code": "27101920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM OILS AND OILS OBTAINED FROM BITUMINOUS MINERALS, OTHER THAN CRUDE;\nPREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70% OR\nMORE OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS\nBEING THE BASIC CONSTITUENTS OF THE PREPARATIONS; WASTE OILS - PETROLEUM OILS AND\nOILS OBTAINED FROM BITUMINOUS MINERALS (OTHER THAN CRUDE) AND PREPARATIONS NOT\nELSEWHERE SPECIFIED OR INCLUDED, CONTAINING BY WEIGHT 70% OR MORE OF PETROLEUM\nOILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS BEING THE BASIC\nCONSTITUENTS OF THE PREPARATIONS, OTHER THAN WASTE OILS: - OTHER: SUPERIOR\nKEROSINE OIL (SKO)",
+        "hsn_code": "27101910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM OILS AND OILS OBTAINED FROM BITUMINOUS MINERALS, OTHER THAN CRUDE;\nPREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED, CONTAINIGN BY WEIGHT 70% OR\nMORE OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS\nBEING THE BASIC CONSTITUENTS OF THE PREPARATIONS; WASTE OILS--LIGHT OILS AND\nPREPARATIONS---OTHER",
+        "hsn_code": "27101290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM OILS AND OILS OBTAINED FROM BITUMINOUS MINERALS, OTHER THAN CRUDE;\nPREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED, CONTAINIGN BY WEIGHT 70% OR\nMORE OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS\nBEING THE BASIC CONSTITUENTS OF THE PREPARATIONS; WASTE OILS--LIGHT OILS AND\nPREPARATIONS---NATURAL GASOLINE LIQUID (NCL)",
+        "hsn_code": "27101220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM OILS AND OILS OBTAINED FROM BITUMINOUS MINERALS, OTHER THAN CRUDE;\nPREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED, CONTAINIGN BY WEIGHT 70% OR\nMORE OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS\nBEING THE BASIC CONSTITUENTS OF THE PREPARATIONS; WASTE OILS--LIGHT OILS AND\nPREPARATIONS---MOTOR SPIRIT----OTHER",
+        "hsn_code": "27101219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM OILS AND OILS OBTAINED FROM BITUMINOUS MINERALS, OTHER THAN CRUDE;\nPREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED, CONTAINIGN BY WEIGHT 70% OR\nMORE OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS\nBEING THE BASIC CONSTITUENTS OF THE PREPARATIONS; WASTE OILS--LIGHT OILS AND\nPREPARATIONS---MOTOR SPIRIT----OTHER SPECIAL BOILING POINT SPIRITS (OTHER THAN\nBENZENE, BENZOL, TOLUENE AND TOLUOL)",
+        "hsn_code": "27101213",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM OILS AND OILS OBTAINED FROM BITUMINOUS MINERALS, OTHER THAN CRUDE;\nPREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED, CONTAINIGN BY WEIGHT 70% OR\nMORE OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS\nBEING THE BASIC CONSTITUENTS OF THE PREPARATIONS; WASTE OILS--LIGHT OILS AND\nPREPARATIONS---MOTOR SPIRIT----SPECIAL BOILING POINT SPIRITS (OTHER THAN\nBENZENE,BENZOL,TOLUENE AND TOLUOL) WITH NOMINAL BOILING POINT RANGE 63-70\nDEGREES CENTIGRADE",
+        "hsn_code": "27101212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM OILS AND OILS OBTAINED FROM BITUMINOUS MINERALS, OTHER THAN CRUDE;\nPREPARATIONS NOT ELSEWHERE SPECIFIED OR INCLUDED, CONTAINIGN BY WEIGHT 70% OR\nMORE OF PETROLEUM OILS OR OF OILS OBTAINED FROM BITUMINOUS MINERALS, THESE OILS\nBEING THE BASIC CONSTITUENTS OF THE PREPARATIONS; WASTE OILS--LIGHT OILS AND\nPREPARATIONS---MOTOR SPIRIT----SPECIAL BOILING POINT SPIRITS (OTHER THAN BENZENE,\nTOLUOL) WITH NOMINAL BOILING POINT RANGE 55-115 DEGREES CENTIGRADE",
+        "hsn_code": "27101211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PITCH AND PITCH COKE, OBTAINED FROM COAL TAR OR FROM OTHER MINERAL TARS",
+        "hsn_code": "2708",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PITCH AND PITCH COKE, OBTAINED FROM COAL TAR OR FROM OTHER MINERAL TARS - PITCH\nCOKE",
+        "hsn_code": "27082000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PITCH AND PITCH COKE, OBTAINED FROM COAL TAR OR FROM OTHER MINERAL TARS - PITCH:\nOTHER",
+        "hsn_code": "27081090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "PITCH AND PITCH COKE, OBTAINED FROM COAL TAR OR FROM OTHER MINERAL TARS - PITCH:\nOBTAINED BY BLENDING WITH CREOSOTE OIL OR OTHER COAL TAR DISTILLATES",
+        "hsn_code": "27081010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OILS AND OTHER PRODUCTS OF THE DISTILLATION OF HIGH TEMPERATURE COAL TAR, SIMILAR PRODUCTS IN WHICH THE WEIGHT OF THE AROMATIC CONSTITUENTS EXCEEDS THAT OF THE NON-AROMATIC CONSTITUENTS",
+        "hsn_code": "2707",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OILS AND OTHER PRODUCTS OF THE DISTILLATION OF HIGH TEMPERATURE COAL TAR SIMILAR\nPRODUCTS IN WHICH THE WEIGHT OF THE AROMATIC CONSTITUENTS EXCEEDS THAT OF THE\nNON-AROMATIC CONSTITUENTS - OTHER: OTHER",
+        "hsn_code": "27079900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OILS AND OTHER PRODUCTS OF THE DISTILLATION OF HIGH TEMPERATURE COAL TAR SIMILAR\nPRODUCTS IN WHICH THE WEIGHT OF THE AROMATIC CONSTITUENTS EXCEEDS THAT OF THE\nNON-AROMATIC CONSTITUENTS - OTHER: CREOSOTE OILS",
+        "hsn_code": "27079100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OILS AND OTHER PRODUCTS OF THE DISTILLATION OF HIGH TEMPERATURE COAL TAR SIMILAR\nPRODUCTS IN WHICH THE WEIGHT OF THE AROMATIC CONSTITUENTS EXCEEDS THAT OF THE\nNON-AROMATIC CONSTITUENTS PHENOLS",
+        "hsn_code": "27076000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OILS AND OTHER PRODUCTS OF THE DISTILLATION OF HIGH TEMPERATURE COAL TAR SIMILAR\nPRODUCTS IN WHICH THE WEIGHT OF THE AROMATIC CONSTITUENTS EXCEEDS THAT OF THE\nNON-AROMATIC CONSTITUENTS OTHER AROMATIC HYDROCARBON MIXTURES OF WHICH 65%\nOR MORE BY VOLUME (INCLUDING LOSSES) DISTILS AT 250OC BY THE ASTM D 86 METHOD",
+        "hsn_code": "27075000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OILS AND OTHER PRODUCTS OF THE DISTILLATION OF HIGH TEMPERATURE COAL TAR SIMILAR\nPRODUCTS IN WHICH THE WEIGHT OF THE AROMATIC CONSTITUENTS EXCEEDS THAT OF THE\nNON-AROMATIC CONSTITUENTS NAPHTHELENE",
+        "hsn_code": "27074000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OILS AND OTHER PRODUCTS OF THE DISTILLATION OF HIGH TEMPERATURE COAL TAR SIMILAR\nPRODUCTS IN WHICH THE WEIGHT OF THE AROMATIC CONSTITUENTS EXCEEDS THAT OF THE\nNON-AROMATIC CONSTITUENTS XYLOL (XYLENES)",
+        "hsn_code": "27073000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OILS AND OTHER PRODUCTS OF THE DISTILLATION OF HIGH TEMPERATURE COAL TAR SIMILAR\nPRODUCTS IN WHICH THE WEIGHT OF THE AROMATIC CONSTITUENTS EXCEEDS THAT OF THE\nNON-AROMATIC CONSTITUENTS TOLUOL (TOLUENE)",
+        "hsn_code": "27072000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OILS AND OTHER PRODUCTS OF THE DISTILLATION OF HIGH TEMPERATURE COAL TAR SIMILAR\nPRODUCTS IN WHICH THE WEIGHT OF THE AROMATIC CONSTITUENTS EXCEEDS THAT OF THE\nNON-AROMATIC CONSTITUENTS BENZOL (BENZENE)",
+        "hsn_code": "27071000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TAR DISTILLED FROM COAL, FROM LIGNITE OR FROM PEAT AND OTHER MINERAL TARS, WHETHER OR NOT DEHYDRATED OR PARTIALLY DISTILLED, INCLUDING RECONSTITUTED TARS",
+        "hsn_code": "2706",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TAR DISTILLED FROM COAL, FROM LIGNITE OR FROM PEAT AND OTHER MINERAL TARS,\nWHETHER OR NOT DEHYDRATED OR PARTIALLY DISTILLED, INCLUDING RECONSTITUTED TARS -\nTAR DISTILLED FROM COAL, FROM LIGNITE OR FROM PEAT AND OTHER MINERAL TARS,\nWHETHER OR NOT DEHYDRATED OR PARTIALLY DISTILLED, INCLUDING RECONSTITUTED TARS:\nOTHER",
+        "hsn_code": "27060090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TAR DISTILLED FROM COAL, FROM LIGNITE OR FROM PEAT AND OTHER MINERAL TARS,\nWHETHER OR NOT DEHYDRATED OR PARTIALLY DISTILLED, INCLUDING RECONSTITUTED TARS -\nTAR DISTILLED FROM COAL, FROM LIGNITE OR FROM PEAT AND OTHER MINERAL TARS,\nWHETHER OR NOT DEHYDRATED OR PARTIALLY DISTILLED, INCLUDING RECONSTITUTED TARS:\nCOAL TAR",
+        "hsn_code": "27060010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COKE AND SEMI-COKE OF COAL, OF LIGNITE OR OF PEAT, WHETHER OR NOT AGGLOMERATED; RETORT CARBON",
+        "hsn_code": "2705",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COAL GAS, WATER GAS, PRODUCER GAS AND SIMILAR GASES, OTHER THAN PETROLEUM GASES\nAND OTHER GASEOUS HYDROCARBONS",
+        "hsn_code": "27050000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COKE AND SEMI-COKE OF COAL, OF LIGNITE OR OF PEAT, WHETHER OR NOT AGGLOMERATED; RETORT CARBON",
+        "hsn_code": "2704",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COKE AND SEMI-COKE OF COAL, OF LIGNITE OR OF PEAT, WHETHER OR NOT AGGLOMERATED;\nRETORT CARBON - COKE AND SEMI-COKE OF COAL, OF LIGNITE OR OF PEAT, WHETHER OR NOT\nAGGLOMERATED; RETORT CARBON: OTHER",
+        "hsn_code": "27040090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COKE AND SEMI-COKE OF COAL, OF LIGNITE OR OF PEAT, WHETHER OR NOT AGGLOMERATED;\nRETORT CARBON - COKE AND SEMI-COKE OF COAL, OF LIGNITE OR OF PEAT, WHETHER OR NOT\nAGGLOMERATED; RETORT CARBON: SOFT COKE OF COAL",
+        "hsn_code": "27040040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COKE AND SEMI-COKE OF COAL, OF LIGNITE OR OF PEAT, WHETHER OR NOT AGGLOMERATED;\nRETORT CARBON - COKE AND SEMI-COKE OF COAL, OF LIGNITE OR OF PEAT, WHETHER OR NOT\nAGGLOMERATED; RETORT CARBON: HARD COKE OF COAL",
+        "hsn_code": "27040030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COKE AND SEMI-COKE OF COAL, OF LIGNITE OR OF PEAT, WHETHER OR NOT AGGLOMERATED;\nRETORT CARBON - COKE AND SEMI-COKE OF COAL, OF LIGNITE OR OF PEAT, WHETHER OR NOT\nAGGLOMERATED; RETORT CARBON: COKE AND SEMI-COKE OF LIGNITE OR OF PEAT",
+        "hsn_code": "27040020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COKE AND SEMI-COKE OF COAL, OF LIGNITE OR OF PEAT, WHETHER OR NOT AGGLOMERATED;\nRETORT CARBON - COKE AND SEMI-COKE OF COAL, OF LIGNITE OR OF PEAT, WHETHER OR NOT\nAGGLOMERATED; RETORT CARBON: RETORT CARBON (GAS CARBON)",
+        "hsn_code": "27040010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER SLAG AND ASH, INLCUDING SEAWEED ASH (KELP); ASH AND RESIDUES FROM THE ICINERATION OF MUNICIPAL WASTE",
+        "hsn_code": "2621",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER SLAG AND ASH, INLCUDING SEAWEED ASH (KELP); ASH AND RESIDUES FROM THE\nINCINERATION OF MUNICIPAL WASTE - OTHER",
+        "hsn_code": "26219000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER SLAG AND ASH, INLCUDING SEAWEED ASH (KELP); ASH AND RESIDUES FROM THE\nINCINERATION OF MUNICIPAL WASTE - ASH AND RESIDUES FROM THE INCINERATION OF\nMUNICIPAL WASTE",
+        "hsn_code": "26211000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLAG, ASH AND RESIDUES (OTHER THAN FROM THE MANUFACTURE OF IRON OR STEEL), CONTAINING ARSENIC, METALS OR THEIR COMPOUNDS",
+        "hsn_code": "2620",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLAG, ASH AND RESIDUES (OTHER THAN FROM THE MANUFACTURE OF IRON OR STEEL),\nCONTAINING ARSENIC, METALS OR THEIR COMPOUNDS - OTHER: OTHER",
+        "hsn_code": "26209900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLAG, ASH AND RESIDUES (OTHER THAN FROM THE MANUFACTURE OF IRON OR STEEL),\nCONTAINING ARSENIC, METALS OR THEIR COMPOUNDS - OTHER: CONTAINING ANTIMONY ,\nBERYLLIUM, CADMIUM, CHROMIUM OR THEIR MIXTURES",
+        "hsn_code": "26209100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLAG, ASH AND RESIDUES (OTHER THAN FROM THE MANUFACTURE OF IRON OR STEEL),\nCONTAINING ARSENIC, METALS OR THEIR COMPOUNDS -CONTAINING ARSENIC, MERCURY,\nTHALLIUM OR THEIR MIXTURES, OF A KIND USED FOR THE EXTRACTION OF ARSENIC OR THOSE\nMETALS OR FOR THE MANUFACTURE OF THEIR CHEMICAL COMPOUNDS",
+        "hsn_code": "26206000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLAG, ASH AND RESIDUES (OTHER THAN FROM THE MANUFACTURE OF IRON OR STEEL),\nCONTAINING ARSENIC, METALS OR THEIR COMPOUNDS - CONTAINING MAINLY ALUMINIUM:\nOTHER",
+        "hsn_code": "26204090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLAG, ASH AND RESIDUES (OTHER THAN FROM THE MANUFACTURE OF IRON OR STEEL),\nCONTAINING ARSENIC, METALS OR THEIR COMPOUNDS - CONTAINING MAINLY ALUMINIUM:\nALUMINIUM DROSS",
+        "hsn_code": "26204010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLAG, ASH AND RESIDUES (OTHER THAN FROM THE MANUFACTURE OF IRON OR STEEL),\nCONTAINING ARSENIC, METALS OR THEIR COMPOUNDS - CONTAINING MAINLY COPPER : OTHER",
+        "hsn_code": "26203090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLAG, ASH AND RESIDUES (OTHER THAN FROM THE MANUFACTURE OF IRON OR STEEL),\nCONTAINING ARSENIC, METALS OR THEIR COMPOUNDS - CONTAINING MAINLY COPPER : BRASS\nDROSS",
+        "hsn_code": "26203010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLAG, ASH AND RESIDUES (OTHER THAN FROM THE MANUFACTURE OF IRON OR STEEL),\nCONTAINING ARSENIC, METALS OR THEIR COMPOUNDS - CONTAINING MAINLY LEAD: OTHER",
+        "hsn_code": "26202900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLAG, ASH AND RESIDUES (OTHER THAN FROM THE MANUFACTURE OF IRON OR STEEL),\nCONTAINING ARSENIC, METALS OR THEIR COMPOUNDS - CONTAINING MAINLY LEAD: LEADED\nGASOLINE SLUDGES AND LEADED ANTI-KNOCK COMPOUND SLUDGES",
+        "hsn_code": "26202100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLAG, ASH AND RESIDUES (OTHER THAN FROM THE MANUFACTURE OF IRON OR STEEL),\nCONTAINING ARSENIC, METALS OR THEIR COMPOUNDS - CONTAINING MAINLY ZINC : OTHER",
+        "hsn_code": "26201900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLAG, ASH AND RESIDUES (OTHER THAN FROM THE MANUFACTURE OF IRON OR STEEL),\nCONTAINING ARSENIC, METALS OR THEIR COMPOUNDS - CONTAINING MAINLY ZINC : HARD\nZINC SPELTER",
+        "hsn_code": "26201100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLAG, DROSS (OTHER THAN GRANULATED SLAG),  SCALINGS AND OTHER WASTE FROM THE MANUFACTURE OF IRON OR STEEL",
+        "hsn_code": "2619",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLAG, DROSS (OTHER THAN GRANULATED SLAG), SCALINGS AND OTHER WASTE FROM THE\nMANUFACTURE OF IRON OR STEEL - SLAG, DROSS (OTHER THAN GRANULATED SLAG), SCALINGS\nAND OTHER WASTE FROM THE MANUFACTURE OF IRON OR STEEL: OTHER",
+        "hsn_code": "26190090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SLAG, DROSS (OTHER THAN GRANULATED SLAG), SCALINGS AND OTHER WASTE FROM THE\nMANUFACTURE OF IRON OR STEEL - SLAG, DROSS (OTHER THAN GRANULATED SLAG), SCALINGS\nAND OTHER WASTE FROM THE MANUFACTURE OF IRON OR STEEL: CONVERTED SLAG (SCULL) OF\nBLAST FURNACE",
+        "hsn_code": "26190010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORES AND CONCENTRATES",
+        "hsn_code": "2618",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GRANULATED SLAG (SLAG SAND) FROM THE MANUFACTURE OF IRON OR STEEL",
+        "hsn_code": "26180000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORES AND CONCENTRATES",
+        "hsn_code": "2617",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORES AND CONCENTRATES - OTHER",
+        "hsn_code": "26179000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORES AND CONCENTRATES - ANTIMONY ORES AND CONCENTRATES",
+        "hsn_code": "26171000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PRECIOUS METAL ORES AND CONCENTRATES",
+        "hsn_code": "2616",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PRECIOUS METAL ORES AND CONCENTRATES - OTHER : OTHER",
+        "hsn_code": "26169090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PRECIOUS METAL ORES AND CONCENTRATES - OTHER : GOLD ORES AND CONCENTRATES",
+        "hsn_code": "26169010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PRECIOUS METAL ORES AND CONCENTRATES SILVER ORES AND CONCENTRATES",
+        "hsn_code": "26161000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NIOBIUM, TANTALUM, VANADIUM OR ZIRCONIUM ORES AND CONCENTRATES",
+        "hsn_code": "2615",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NIOBIUM, TANTALUM, VANADIUM OR ZIRCONIUM ORES AND CONCENTRATES - OTHER :\nNIOBIUM OR TANTALUM ORES AND CONCENTRATES",
+        "hsn_code": "26159020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NIOBIUM, TANTALUM, VANADIUM OR ZIRCONIUM ORES AND CONCENTRATES - OTHER :\nVANADIUM ORES AND CONCENTRATES",
+        "hsn_code": "26159010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NIOBIUM, TANTALUM, VANADIUM OR ZIRCONIUM ORES AND CONCENTRATES ZIRCONIUM ORES\nAND CONCENTRATES",
+        "hsn_code": "26151000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TITANIUM ORES AND CONCENTRATES",
+        "hsn_code": "2614",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TITANIUM ORES AND CONCEN TRATES - TITANIUM ORES AND CONCENTRATES: OTHER",
+        "hsn_code": "26140090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TITANIUM ORES AND CONCEN TRATES - TITANIUM ORES AND CONCENTRATES: RUTILE : OTHER",
+        "hsn_code": "26140039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TITANIUM ORES AND CONCEN TRATES - TITANIUM ORES AND CONCENTRATES: RUTILE : RARE\nEARTH OXIDES INCLUDING RUTILE SAND",
+        "hsn_code": "26140031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TITANIUM ORES AND CONCEN TRATES - TITANIUM ORES AND CONCENTRATES: LIMENITE,\nUPGRADED (BENEFICIATED LIMENITE INCLUDING LIMENITE GROUND)",
+        "hsn_code": "26140020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TITANIUM ORES AND CONCEN TRATES - TITANIUM ORES AND CONCENTRATES: LIMENITE,\nUNPROCESSED",
+        "hsn_code": "26140010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MOLYBDENUM ORES AND CONCENTRATES",
+        "hsn_code": "2613",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MOLYBDENUM ORES AND CONCENTRATES - OTHER",
+        "hsn_code": "26139000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MOLYBDENUM ORES AND CONCENTRATES - ROASTED",
+        "hsn_code": "26131000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "URANIUM OR THORIUM ORES AND CONCENTRATES",
+        "hsn_code": "2612",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "URANIUM OR THORIUM ORES AND CONCENTRATES - THORIUM ORES AND CONCENTRATES",
+        "hsn_code": "26122000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "URANIUM OR THORIUM ORES AND CONCENTRATES - URANIUM ORES AND CONCENTRATES",
+        "hsn_code": "26121000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CHROMIUN ORES AND CONCENTRATES",
+        "hsn_code": "2611",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "TUNGSTEN ORES AND CONCENTRATES",
+        "hsn_code": "26110000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CHROMIUN ORES AND CONCENTRATES",
+        "hsn_code": "2610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHROMIUM ORES AND CONCENTRATES - CHROMIUM ORES AND CONCENTRATES: OTHER",
+        "hsn_code": "26100090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHROMIUM ORES AND CONCENTRATES - CHROMIUM ORES AND CONCENTRATES: CHROME ORE\nFRIABLE AND CONCENTRATES FIXES CONTAINING 47% CR2O3 AND ABOVE",
+        "hsn_code": "26100040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHROMIUM ORES AND CONCENTRATES - CHROMIUM ORES AND CONCENTRATES: CHROME ORE\nLUMPS BELOW 40% CR2O3",
+        "hsn_code": "26100030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHROMIUM ORES AND CONCENTRATES - CHROMIUM ORES AND CONCENTRATES: CHROME ORE\nLUMPS, CONTAINING 40% OR MORE BUT LESS THAN 47% CR2O3",
+        "hsn_code": "26100020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHROMIUM ORES AND CONCENTRATES - CHROMIUM ORES AND CONCENTRATES: CHROME ORE\nLUMPS, CONTAINING 47% CR2O3 AND ABOVE",
+        "hsn_code": "26100010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM ORES AND CONCENTRATES",
+        "hsn_code": "2609",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TIN ORES AND CONCENTRATES",
+        "hsn_code": "26090000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM ORES AND CONCENTRATES",
+        "hsn_code": "2608",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ZINC ORES AND CONCENTRATES",
+        "hsn_code": "26080000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM ORES AND CONCENTRATES",
+        "hsn_code": "2607",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "LEAD ORES AND CONCENTRATES",
+        "hsn_code": "26070000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM ORES AND CONCENTRATES",
+        "hsn_code": "2606",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM ORES AND CONCENTRATES - ALUMINIUM ORES AND CONCENTRATES: OTHER\nALUMINIUM ORES AND CONCENTRATES",
+        "hsn_code": "26060090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM ORES AND CONCENTRATES - ALUMINIUM ORES AND CONCENTRATES: BAUXITE\n(NATURAL), CALCINED",
+        "hsn_code": "26060020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ALUMINIUM ORES AND CONCENTRATES - ALUMINIUM ORES AND CONCENTRATES: BAUXITE\n(NATURAL), NOT CALCINED",
+        "hsn_code": "26060010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE ORES AND CONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON THE DRY WEIGHT",
+        "hsn_code": "2605",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COBALT ORES AND CONCENTRATES",
+        "hsn_code": "26050000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE ORES AND CONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON THE DRY WEIGHT",
+        "hsn_code": "2604",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "NICKEL ORES AND CONCENTRATES",
+        "hsn_code": "26040000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE ORES AND CONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON THE DRY WEIGHT",
+        "hsn_code": "2603",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COPPER ORES AND CONCERTRATES",
+        "hsn_code": "26030000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE ORES AND CONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON THE DRY WEIGHT",
+        "hsn_code": "2602",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE ORES AND\nCONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON THE DRY\nWEIGHT - MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE\nORES AND CONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON\nTHE DRY WEIGHT: OTHER",
+        "hsn_code": "26020090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE ORES AND\nCONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON THE DRY\nWEIGHT - MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE\nORES AND CONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON\nTHE DRY WEIGHT: MANGANESE ORE SINTERS, AGGLOMERATED",
+        "hsn_code": "26020070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE ORES AND\nCONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON THE DRY\nWEIGHT - MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE\nORES AND CONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON\nTHE DRY WEIGHT: FERRUGINOUS (10% OR MORE BUT BELOW 30%)",
+        "hsn_code": "26020060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE ORES AND\nCONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON THE DRY\nWEIGHT - MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE\nORES AND CONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON\nTHE DRY WEIGHT: MANGANESE ORE (30% OR MORE BUT BELOW 35%)",
+        "hsn_code": "26020050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE ORES AND\nCONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON THE DRY\nWEIGHT - MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE\nORES AND CONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON\nTHE DRY WEIGHT: MANGANESE ORE (35% OR MORE BUT BELOW 40%)",
+        "hsn_code": "26020040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE ORES AND\nCONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON THE DRY\nWEIGHT - MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE\nORES AND CONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON\nTHE DRY WEIGHT: MANGANESE ORE (40% OR MORE BUT BELOW 44%)",
+        "hsn_code": "26020030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE ORES AND\nCONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON THE DRY\nWEIGHT - MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE\nORES AND CONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON\nTHE DRY WEIGHT: MANGANESE ORE (44% OR MORE BUT BELOW 46%)",
+        "hsn_code": "26020020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE ORES AND\nCONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON THE DRY\nWEIGHT - MANGANESE ORES AND CONCENTRATES, INCLUDING FERRUGINOUS MANGANESE\nORES AND CONCENTRATES WITH A MANGANESE CONTENT OF 20% OR MORE, CALCULATED ON\nTHE DRY WEIGHT: MANGANESE ORE (46% OR MORE)",
+        "hsn_code": "26020010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IRON ORES AND CONCENTRATES, INCLUDING ROASTED IRON PYRITES",
+        "hsn_code": "2601",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IRON ORES AND CONCENTRATES, INCLUDING ROASTED IRON PYRITES ROASTED IRON PYRITES",
+        "hsn_code": "26012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IRON ORES AND CONCENTRATES, INCLUDING ROASTED IRON PYRITES - IRON ORES AND\nCONCENTRATES, OTHER THAN ROASTED IRON PYRITES : AGGLOMERATED : OTHER",
+        "hsn_code": "26011290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IRON ORES AND CONCENTRATES, INCLUDING ROASTED IRON PYRITES - IRON ORES AND\nCONCENTRATES, OTHER THAN ROASTED IRON PYRITES : AGGLOMERATED : IRON ORE PELLETS",
+        "hsn_code": "26011210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IRON ORES AND CONCENTRATES, INCLUDING ROASTED IRON PYRITES - IRON ORES AND\nCONCENTRATES, OTHER THAN ROASTED IRON PYRITES : NON-AGGLOMERATED : OTHER",
+        "hsn_code": "26011190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "IRON ORES AND CONCENTRATES, INCLUDING ROASTED IRON PYRITES - IRON ORES AND\nCONCENTRATES, OTHER THAN ROASTED IRON PYRITES : NON-AGGLOMERATED : IRON ORE\nCONCENTRATES",
+        "hsn_code": "26011150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "---Iron ore lumps (60% Fe or more)--- 58% Fe or more but below 62% Fe",
+        "hsn_code": "26011149",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "---Iron ore lumps (60% Fe or more)--- 58% Fe or more but below 60% Fe",
+        "hsn_code": "26011143",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "---Iron ore lumps (60% Fe or more)--- 55% Fe or more but below 58% Fe",
+        "hsn_code": "26011142",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "---Iron ore lumps (60% Fe or more)--- below 55% Fe",
+        "hsn_code": "26011141",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "---Iron ore lumps (60% Fe or more)--- above 65% Fe",
+        "hsn_code": "26011139",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "---Iron ore lumps (60% Fe or more)--- 62% Fe or more but below 65% Fe",
+        "hsn_code": "26011131",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "---Iron ore lumps (60% Fe or more)--- 58% Fe or more but below 60% Fe",
+        "hsn_code": "26011129",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "---Iron ore lumps (60% Fe or more)--- 55% Fe or more but below 58% Fe",
+        "hsn_code": "26011122",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "---Iron ore lumps (60% Fe or more)--- below 55% Fe",
+        "hsn_code": "26011121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "---Iron ore lumps (60% Fe or more)--- above 65% Fe",
+        "hsn_code": "26011119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "---Iron ore lumps (60% Fe or more)---62% Fe or more but below 62% Fe",
+        "hsn_code": "26011112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "---Iron ore lumps (60% Fe or more)---60% Fe or more but below 62% Fe",
+        "hsn_code": "26011111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MINERAL SUBSTANCES NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "2530",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MINERAL SUBSTANCES NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER:OTHER: OTHER",
+        "hsn_code": "25309099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MINERAL SUBSTANCES NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER:OTHER: STRONTIUM\nSULPHATE (NATURAL ORE)",
+        "hsn_code": "25309091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MINERAL SUBSTANCES NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER: OTHER PROCESSED\nEARTH COLOUR OCHRE",
+        "hsn_code": "25309070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MINERAL SUBSTANCES NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER: EARTH COLOUR\nOCHRE, CRUDE",
+        "hsn_code": "25309060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MINERAL SUBSTANCES NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER: WOLLASTONITE",
+        "hsn_code": "25309050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MINERAL SUBSTANCES NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER: ORES AND\nCONCENTRATES OF RARE EARTH METALS",
+        "hsn_code": "25309040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MINERAL SUBSTANCES NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER: CALCITE",
+        "hsn_code": "25309030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MINERAL SUBSTANCES NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER: NATURAL ARSENIC\nSULPHIDES (SUCH AS ORPIMENT)",
+        "hsn_code": "25309020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MINERAL SUBSTANCES NOT ELSEWHERE SPECIFIED OR INCLUDED - OTHER: MEERSCHAUM\n(WHETHER OR NOT IN POLISHED PIECES) AND AMBER AGGLOMERATED; MEERACHAUM AND\nAGGLOMERATED AMBER IN PLATES, RODS, ETC., NOT WORKED AFTER MOULDING JET",
+        "hsn_code": "25309010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MINERAL SUBSTANCES NOT ELSEWHERE SPECIFIED OR INCLUDED KIESERITE, EPSOMITE\n(NATURAL MAGNESIUM SULPHATES)",
+        "hsn_code": "25302000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MINERAL SUBSTANCES NOT ELSEWHERE SPECIFIED OR INCLUDED - VERMICULITE, PERLITE AND\nCHLORITES, UNEXPANDED: OTHERS (INCLUDING POWDER)",
+        "hsn_code": "25301090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MINERAL SUBSTANCES NOT ELSEWHERE SPECIFIED OR INCLUDED - VERMICULITE, PERLITE AND\nCHLORITES, UNEXPANDED: PERLITE",
+        "hsn_code": "25301020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MINERAL SUBSTANCES NOT ELSEWHERE SPECIFIED OR INCLUDED - VERMICULITE, PERLITE AND\nCHLORITES, UNEXPANDED: VERMICULITE",
+        "hsn_code": "25301010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FELDSPAR; LEUCITE; NEPHELINE AND NEPHELINE SYENITE; NFLUORSPAR",
+        "hsn_code": "2529",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FELDSPAR; LEUCITE; NEPHELINE AND NEPHELINE SYENITE; FLUORSPAR - LEUCITE; NEPHELINE\nAND NEPHELINE SYENITE",
+        "hsn_code": "25293000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FELDSPAR; LEUCITE; NEPHELINE AND NEPHELINE SYENITE; FLUORSPAR - FLUORSPAR:\nCONTAINING BY WEIGHT MORE THAN 97% OF CALCIUM FLUORIDE",
+        "hsn_code": "25292200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FELDSPAR; LEUCITE; NEPHELINE AND NEPHELINE SYENITE; FLUORSPAR - FLUORSPAR:\nCONTAINING BY WEIGHT 97% OR LESS OF CALCIUM FLUORIDE",
+        "hsn_code": "25292100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FELDSPAR; LEUCITE; NEPHELINE AND NEPHELINE SYENITE; FLUORSPAR - FELDSPAR: POWDER",
+        "hsn_code": "25291020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FELDSPAR; LEUCITE; NEPHELINE AND NEPHELINE SYENITE; FLUORSPAR - FELDSPAR: LUMPS",
+        "hsn_code": "25291010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL BORATES AND CONCENTRATES THEREOF (WHETHER OR NOT CALCINED), BUT NOT INCLUDING BORATES SEPARATED FROM NATURAL BRINE; NATURAL BORIC ACID CONTAINING NOT MORE THAN 85% OF H3BO3 CALCULATED ON THE DRY WEIGHT",
+        "hsn_code": "2528",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL BORATES AND CONCENTRATES THEREOF (WHETHER OR NOT CALCINED), BUT NOT\nINCLUDING BORATES SEPARATED FROM NATURAL BRINE; NATURAL BORIC ACID CONTAINING\nNOT MORE THAN 85% OF H3BO3 CALCULATED ON THE DRY WEIGHT- OTHER: NATURAL\nCALCIUM BORATES AND CONCENTRATES THEREOF (WHETHER OR NOT CALCINED)",
+        "hsn_code": "25289020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL BORATES AND CONCENTRATES THEREOF (WHETHER OR NOT CALCINED), BUT NOT\nINCLUDING BORATES SEPARATED FROM NATURAL BRINE;NATURAL BORIC ACID CONTAINING\nNOT MORE THAN 85% OF H2BO3 CALCULATED ON THE DRY WEIGHT---OTHER",
+        "hsn_code": "25280090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL BORATES AND CONCENTRATES THEREOF (WHETHER OR NOT CALCINED), BUT NOT\nINCLUDING BORATES SEPARATED FROM NATURAL BRINE;NATURAL BORIC ACID CONTAINING\nNOT MORE THAN 85% OF H2BO3 CALCULATED ON THE DRY WEIGHT---NATURAL CALCIUM\nBORATES AND CONCENTRATES THEREOF (WHETHER OR NOT CALCINED)",
+        "hsn_code": "25280030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL BORATES AND CONCENTRATES THEREOF (WHETHER OR NOT CALCINED), BUT NOT\nINCLUDING BORATES SEPARATED FROM NATURAL BRINE;NATURAL BORIC ACID CONTAINING\nNOT MORE THAN 85% OF H2BO3 CALCULATED ON THE DRY WEIGHT---NATURAL BORIC ACID\n(CONTAINING NOT MORE THAN 85% OF H2BO3)",
+        "hsn_code": "25280020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL BORATES AND CONCENTRATES THEREOF (WHETHER OR NOT CALCINED), BUT NOT\nINCLUDING BORATES SEPARATED FROM NATURAL BRINE;NATURAL BORIC ACID CONTAINING\nNOT MORE THAN 85% OF H2BO3 CALCULATED ON THE DRY WEIGHT---NATURAL SODIUM\nBORATES AND CONCENTRATES THEREOF (WHETHER OR NOT CALCINED)",
+        "hsn_code": "25280010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL STEATITE, WHETHER OR NOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE) SHAPE; TALC",
+        "hsn_code": "2526",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL STEATITE, WHETHER OR NOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR\nOTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE) SHAPE; TALC\nCRUSHED OR POWDERED",
+        "hsn_code": "25262000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL STEATITE, WHETHER OR NOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR\nOTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE) SHAPE; TALC -\nNOT CRUSHED, NOT POWDERED: OTHER",
+        "hsn_code": "25261090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL STEATITE, WHETHER OR NOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR\nOTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE) SHAPE; TALC -\nNOT CRUSHED, NOT POWDERED: STEATITE (SOAP STONE, ETC.) LUMPS",
+        "hsn_code": "25261020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL STEATITE, WHETHER OR NOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR\nOTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE) SHAPE; TALC -\nNOT CRUSHED, NOT POWDERED: STEATITE (SOAP STONE, ETC.) BLOCK",
+        "hsn_code": "25261010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MICA, INCLUDING SPLITTINGS; MICA WASTE",
+        "hsn_code": "2525",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MICA, INCLUDING SPLITTINGS; MICA WASTE - MICA WASTE: OTHER",
+        "hsn_code": "25253090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MICA, INCLUDING SPLITTINGS; MICA WASTE - MICA WASTE: MICA CUTTINGS BOOK FORM",
+        "hsn_code": "25253030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MICA, INCLUDING SPLITTINGS; MICA WASTE - MICA WASTE: MICA FACTORY SCRAP",
+        "hsn_code": "25253020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MICA, INCLUDING SPLITTINGS; MICA WASTE - MICA WASTE: MICA MINE SCRAP AND WASTE",
+        "hsn_code": "25253010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MICA, INCLUDING SPLITTINGS; MICA WASTE - MICA POWDER: OTHER",
+        "hsn_code": "25252090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MICA, INCLUDING SPLITTINGS; MICA WASTE - MICA POWDER: MICA POWDER, CALCINED",
+        "hsn_code": "25252050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MICA, INCLUDING SPLITTINGS; MICA WASTE - MICA POWDER: MICA POWDER, WET GROUND",
+        "hsn_code": "25252040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MICA, INCLUDING SPLITTINGS; MICA WASTE - MICA POWDER: MICA POWDER, MICRONISED",
+        "hsn_code": "25252030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MICA, INCLUDING SPLITTINGS; MICA WASTE - MICA POWDER: MICA POWDER, DRY GROUND",
+        "hsn_code": "25252020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MICA, INCLUDING SPLITTINGS; MICA WASTE - MICA POWDER: MICA FLAKES, 2.20 MESH",
+        "hsn_code": "25252010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MICA, INCLUDING SPLITTINGS; MICA WASTE - CRUDE MICA AND MICA RIFTED INTO SHEETS OR\nSPLITTINGS: OTHER",
+        "hsn_code": "25251090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MICA, INCLUDING SPLITTINGS; MICA WASTE - CRUDE MICA AND MICA RIFTED INTO SHEETS OR\nSPLITTINGS: MICA SPLITTINGS, LOOSE",
+        "hsn_code": "25251040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MICA, INCLUDING SPLITTINGS; MICA WASTE - CRUDE MICA AND MICA RIFTED INTO SHEETS OR\nSPLITTINGS: MICA SPLITTINGS, BOOK FORM",
+        "hsn_code": "25251030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MICA, INCLUDING SPLITTINGS; MICA WASTE - CRUDE MICA AND MICA RIFTED INTO SHEETS OR\nSPLITTINGS: CONDENSOR FILMS TRIMMED BUT NOT CUT TO SHAPE",
+        "hsn_code": "25251020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MICA, INCLUDING SPLITTINGS; MICA WASTE - CRUDE MICA AND MICA RIFTED INTO SHEETS OR\nSPLITTINGS: MICA BLOCKS",
+        "hsn_code": "25251010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS",
+        "hsn_code": "2524",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - OTHER --- OTHER ---- OTHER",
+        "hsn_code": "25249099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - OTHER --- OTHER ---- WASTE",
+        "hsn_code": "25249091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - OTHER --- FLAKES OR POWDER ---- OTHER",
+        "hsn_code": "25249039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - OTHER --- FLAKES OR POWDER ---- AMOSITE",
+        "hsn_code": "25249034",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - OTHER --- FLAKES OR POWDER ---- CRYSOLITE",
+        "hsn_code": "25249033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - OTHER --- FLAKES OR POWDER ---- AMPHIBOLE",
+        "hsn_code": "25249032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - OTHER --- FLAKES OR POWDER ---- CHRYSOTILE",
+        "hsn_code": "25249031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - OTHER --- FIBRE RAW, BEATEN OR WASHED OR GRADED TO LENGTH ---- OTHER",
+        "hsn_code": "25249029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - OTHER --- FIBRE RAW, BEATEN OR WASHED OR GRADED TO LENGTH ---- AMOSITE",
+        "hsn_code": "25249024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - OTHER --- FIBRE RAW, BEATEN OR WASHED OR GRADED TO LENGTH ---- CRYSOLITE",
+        "hsn_code": "25249023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - OTHER --- FIBRE RAW, BEATEN OR WASHED OR GRADED TO LENGTH ---- AMPHIBOLE",
+        "hsn_code": "25249022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - OTHER --- FIBRE RAW, BEATEN OR WASHED OR GRADED TO LENGTH ---- CHRYSOTILE",
+        "hsn_code": "25249021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - OTHER --- IN ROCK FORM ---- OTHER",
+        "hsn_code": "25249019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - OTHER --- IN ROCK FORM ---- AMOSITE",
+        "hsn_code": "25249014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - OTHER --- IN ROCK FORM ---- CRYSOLITE",
+        "hsn_code": "25249013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - OTHER --- IN ROCK FORM ---- AMPHIBOLE",
+        "hsn_code": "25249012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - OTHER --- IN ROCK FORM ---- CHRYSOTILE",
+        "hsn_code": "25249011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - CROCIDOLITE",
+        "hsn_code": "25241000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - ASBESTOS: OTHER: OTHER",
+        "hsn_code": "25240099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - ASBESTOS: OTHER: WASTE",
+        "hsn_code": "25240091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - ASBESTOS: FLAKES OR POWDER: OTHER",
+        "hsn_code": "25240039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - ASBESTOS: FLAKES OR POWDER: AMOSITE",
+        "hsn_code": "25240031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - ASBESTOS: FIBRE RAW, BEATEN OR WASHED OR GRADED TO LENGTH : OTHER",
+        "hsn_code": "25240029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - ASBESTOS: FIBRE RAW, BEATEN OR WASHED OR GRADED TO LENGTH : AMOSITE",
+        "hsn_code": "25240022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - ASBESTOS: FIBRE RAW, BEATEN OR WASHED OR GRADED TO LENGTH : CHRYSOLITE",
+        "hsn_code": "25240021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - ASBESTOS: ASBESTOS IN ROCK FORM: OTHER",
+        "hsn_code": "25240019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - ASBESTOS: ASBESTOS IN ROCK FORM: AMPHIBOLE",
+        "hsn_code": "25240012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "ASBESTOS - ASBESTOS: ASBESTOS IN ROCK FORM: CHRYSOTILE",
+        "hsn_code": "25240011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PORTLAND CEMENT, ALUMINOUS CEMENT, SLAG CEMENT, SUPERSULPHATE CEMENT AND SIMILAR HYDRAULIC CEMENTS, WHETHER OR NOT COLOURED OR IN THE FORM OF CLINKERS",
+        "hsn_code": "2523",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PORTLAND CEMENT, ALUMINOUS CEMENT, SLAG CEMENT, SUPERSULPHATE CEMENT AND\nSIMILAR HYDRAULIC CEMENTS, WHETHER OR NOT COLOURED OR IN THE FORM OF CLINKERS -\nOTHER HYDRAULIC CEMENTS: OTHER",
+        "hsn_code": "25239090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PORTLAND CEMENT, ALUMINOUS CEMENT, SLAG CEMENT, SUPERSULPHATE CEMENT AND\nSIMILAR HYDRAULIC CEMENTS, WHETHER OR NOT COLOURED OR IN THE FORM OF CLINKERS -\nOTHER HYDRAULIC CEMENTS: HIGH ALUMINA REFRACTORY CEMENT",
+        "hsn_code": "25239020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PORTLAND CEMENT, ALUMINOUS CEMENT, SLAG CEMENT, SUPERSULPHATE CEMENT AND\nSIMILAR HYDRAULIC CEMENTS, WHETHER OR NOT COLOURED OR IN THE FORM OF CLINKERS -\nOTHER HYDRAULIC CEMENTS: SAGOL; ASHMOH",
+        "hsn_code": "25239010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PORTLAND CEMENT, ALUMINOUS CEMENT, SLAG CEMENT, SUPERSULPHATE CEMENT AND\nSIMILAR HYDRAULIC CEMENTS, WHETHER OR NOT COLOURED OR IN THE FORM OF CLINKERS -\nALUMINOUS CEMENT",
+        "hsn_code": "25233000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PORTLAND CEMENT, ALUMINOUS CEMENT, SLAG CEMENT, SUPERSULPHATE CEMENT AND\nSIMILAR HYDRAULIC CEMENTS, WHETHER OR NOT COLOURED OR IN THE FORM OF CLINKERS -\nPORTLAND CEMENT: OTHER: OTHER",
+        "hsn_code": "25232990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PORTLAND CEMENT, ALUMINOUS CEMENT, SLAG CEMENT, SUPERSULPHATE CEMENT AND\nSIMILAR HYDRAULIC CEMENTS, WHETHER OR NOT COLOURED OR IN THE FORM OF CLINKERS -\nPORTLAND CEMENT: OTHER: PORTLAND SLAG CEMENT",
+        "hsn_code": "25232940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PORTLAND CEMENT, ALUMINOUS CEMENT, SLAG CEMENT, SUPERSULPHATE CEMENT AND\nSIMILAR HYDRAULIC CEMENTS, WHETHER OR NOT COLOURED OR IN THE FORM OF CLINKERS -\nPORTLAND CEMENT: OTHER: PORTLAND POZZOLANA CEMENT",
+        "hsn_code": "25232930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PORTLAND CEMENT, ALUMINOUS CEMENT, SLAG CEMENT, SUPERSULPHATE CEMENT AND\nSIMILAR HYDRAULIC CEMENTS, WHETHER OR NOT COLOURED OR IN THE FORM OF CLINKERS -\nPORTLAND CEMENT: OTHER: ORDINARY PORTLAND CEMENT, COLOURED",
+        "hsn_code": "25232920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PORTLAND CEMENT, ALUMINOUS CEMENT, SLAG CEMENT, SUPERSULPHATE CEMENT AND\nSIMILAR HYDRAULIC CEMENTS, WHETHER OR NOT COLOURED OR IN THE FORM OF CLINKERS -\nPORTLAND CEMENT: OTHER: ORDINARY PORTLAND CEMENT, DRY",
+        "hsn_code": "25232910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PORTLAND CEMENT, ALUMINOUS CEMENT, SLAG CEMENT, SUPERSULPHATE CEMENT AND\nSIMILAR HYDRAULIC CEMENTS, WHETHER OR NOT COLOURED OR IN THE FORM OF CLINKERS -\nPORTLAND CEMENT: WHITE CEMENT, WHETHER OR NOT ARTIFICIALLY COLOURED",
+        "hsn_code": "25232100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "PORTLAND CEMENT, ALUMINOUS CEMENT, SLAG CEMENT, SUPERSULPHATE CEMENT AND\nSIMILAR HYDRAULIC CEMENTS, WHETHER OR NOT COLOURED OR IN THE FORM OF CLINKERS\nCEMENT CLINKERS",
+        "hsn_code": "25231000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "QUICKLIME, SLAKED LIME AND HYDRAULIC LIME, OTHER THAN CALCIUM OXIDE AND HYDROXIDE OF HEADING 2825",
+        "hsn_code": "2522",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "QUICKLIME, SLAKED LIME AND HYDRAULIC LIME, OTHER THAN CALCIUM OXIDE AND HYDROXIDE\nOF HEADING 2825 HYDRAULIC LIME",
+        "hsn_code": "25223000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "QUICKLIME, SLAKED LIME AND HYDRAULIC LIME, OTHER THAN CALCIUM OXIDE AND HYDROXIDE\nOF HEADING 2825 SLAKED LIME",
+        "hsn_code": "25222000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "QUICKLIME, SLAKED LIME AND HYDRAULIC LIME, OTHER THAN CALCIUM OXIDE AND HYDROXIDE\nOF HEADING 2825 QUICKLIME",
+        "hsn_code": "25221000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIMESTONE FLUX; LIMESTONE AND OTHER CALCAREOUS STONES, OF A KIND USED FOR THE MANUFACTURE OF LIME OR CEMENT",
+        "hsn_code": "2521",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIMESTONE FLUX; LIMESTONE AND OTHER CALCAREOUS STONES, OF A KIND USED FOR THE\nMANUFACTURE OF LIME OR CEMENT - LIMESTONE FLUX; LIMESTONE AND OTHER CALCAREOUS\nSTONES, OF A KIND USED FOR THE MANUFACTURE OF LIME OR CEMENT: OTHER",
+        "hsn_code": "25210090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "LIMESTONE FLUX; LIMESTONE AND OTHER CALCAREOUS STONES, OF A KIND USED FOR THE\nMANUFACTURE OF LIME OR CEMENT - LIMESTONE FLUX; LIMESTONE AND OTHER CALCAREOUS\nSTONES, OF A KIND USED FOR THE MANUFACTURE OF LIME OR CEMENT: LIMESTONE FLUX (L.D.,\nBELOW 1% SIO2)",
+        "hsn_code": "25210010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GYPSUM; ANHYDRITE; PLASTERS (CONSISTING OF CALCINED GYPSUM OR CALCIUM SULPHATE) WHETHER OR NOT COLOURED, WITH OR WITHOUT SMALL QUANTITIES OF ACCELERATES OR RETARDERS",
+        "hsn_code": "2520",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GYPSUM; ANHYDRITE; PLASTERS (CONSISTING OF CALCINED GYPSUM OR CALCIUM SULPHATE)\nWHETHER OR NOT COLOURED, WITH OR WITHOUT SMALL QUANTITIES OF ACCELERATORS OR\nRETARDERS - PLASTERS: OTHER",
+        "hsn_code": "25202090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GYPSUM; ANHYDRITE; PLASTERS (CONSISTING OF CALCINED GYPSUM OR CALCIUM SULPHATE)\nWHETHER OR NOT COLOURED, WITH OR WITHOUT SMALL QUANTITIES OF ACCELERATORS OR\nRETARDERS - PLASTERS: CALCINED",
+        "hsn_code": "25202010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GYPSUM; ANHYDRITE; PLASTERS (CONSISTING OF CALCINED GYPSUM OR CALCIUM SULPHATE)\nWHETHER OR NOT COLOURED, WITH OR WITHOUT SMALL QUANTITIES OF ACCELERATORS OR\nRETARDERS - GYPSUM; ANHYDRITE: OTHER",
+        "hsn_code": "25201090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GYPSUM; ANHYDRITE; PLASTERS (CONSISTING OF CALCINED GYPSUM OR CALCIUM SULPHATE)\nWHETHER OR NOT COLOURED, WITH OR WITHOUT SMALL QUANTITIES OF ACCELERATORS OR\nRETARDERS - GYPSUM; ANHYDRITE: MARINE",
+        "hsn_code": "25201020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GYPSUM; ANHYDRITE; PLASTERS (CONSISTING OF CALCINED GYPSUM OR CALCIUM SULPHATE)\nWHETHER OR NOT COLOURED, WITH OR WITHOUT SMALL QUANTITIES OF ACCELERATORS OR\nRETARDERS - GYPSUM; ANHYDRITE: NATURAL",
+        "hsn_code": "25201010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL MAGNESIUM CARBONATE (MAGNESITE); FUSED MAGNESIA; DEAD-BURNED (SINTERED) MAGNESIA, WHETHER OR NOT CONTAINING SMALL QUANTITIES OF OTHER OXIDES ADDED BEFORE SINTERING; OTHER MAGNESIUM OXIDE, WHETHER OR NOT PURE",
+        "hsn_code": "2519",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL MAGNESIUM CARBONATE (MAGNESITE); FUSED MAGNESIA; DEAD-BURNED\n(SINTERED) MAGNESIA, WHETHER OR NOT CONTAINING SMALL QUANTITIES OF OTHER OXIDES\nADDED BEFORE SINTERING; OTHER MAGNESIUM OXIDE, WHETHER OR NOT PURE - OTHER:\nOTHER",
+        "hsn_code": "25199090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL MAGNESIUM CARBONATE (MAGNESITE); FUSED MAGNESIA; DEAD-BURNED\n(SINTERED) MAGNESIA, WHETHER OR NOT CONTAINING SMALL QUANTITIES OF OTHER OXIDES\nADDED BEFORE SINTERING; OTHER MAGNESIUM OXIDE, WHETHER OR NOT PURE - OTHER:\nMAGNESIUM OXIDE",
+        "hsn_code": "25199040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL MAGNESIUM CARBONATE (MAGNESITE); FUSED MAGNESIA; DEAD-BURNED\n(SINTERED) MAGNESIA, WHETHER OR NOT CONTAINING SMALL QUANTITIES OF OTHER OXIDES\nADDED BEFORE SINTERING; OTHER MAGNESIUM OXIDE, WHETHER OR NOT PURE - OTHER:\nMAGNESIUM CALCINED (OTHER THAN DEAD-BURNT) NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "25199030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL MAGNESIUM CARBONATE (MAGNESITE); FUSED MAGNESIA; DEAD-BURNED\n(SINTERED) MAGNESIA, WHETHER OR NOT CONTAINING SMALL QUANTITIES OF OTHER OXIDES\nADDED BEFORE SINTERING; OTHER MAGNESIUM OXIDE, WHETHER OR NOT PURE - OTHER: DEAD\nBURNT (SINTERED) MAGNESIA",
+        "hsn_code": "25199020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL MAGNESIUM CARBONATE (MAGNESITE); FUSED MAGNESIA; DEAD-BURNED\n(SINTERED) MAGNESIA, WHETHER OR NOT CONTAINING SMALL QUANTITIES OF OTHER OXIDES\nADDED BEFORE SINTERING; OTHER MAGNESIUM OXIDE, WHETHER OR NOT PURE - OTHER:\nFUSED MAGNESIA (NATURAL)",
+        "hsn_code": "25199010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL MAGNESIUM CARBONATE (MAGNESITE); FUSED MAGNESIA; DEAD-BURNED\n(SINTERED) MAGNESIA, WHETHER OR NOT CONTAINING SMALL QUANTITIES OF OTHER OXIDES\nADDED BEFORE SINTERING; OTHER MAGNESIUM OXIDE, WHETHER OR NOT PURE - NATURAL\nMAGNESIUM CARBONATE (MAGNESITE)",
+        "hsn_code": "25191000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "DOLOMITE, WHETHER OR NOT CALCINED OR SINTERED, INCLUDING DOLOMITE ROUGHLY\nTRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A\nRECTANGULAR (INCLUDING SQUARE) SHAPE; DOLOMITE RAMMING MIX -DOLOMITE NOT\nCALCINED OR SINTERED",
+        "hsn_code": "25181000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "DOLOMITE, WHETHER OR NOT CALCINED OR SINTERED, INCLUDING DOLOMITE ROUGHLY\nTRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A\nRECTANGULAR (INCLUDING SQUARE) SHAPE; DOLOMITE RAMMING MIX -CALCINED OR\nSINTERED DOLOMITE",
+        "hsn_code": "25182000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "DOLOMITE, WHETHER OR NOT CALCINED OR SINTERED, INCLUDING DOLOMITE ROUGHLY\nTRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A\nRECTANGULAR (INCLUDING SQUARE) SHAPE; DOLOMITE RAMMING MIX -DOLOMITE RAMMING\nMIX",
+        "hsn_code": "25183000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "DOLOMITE, WHETHER OR NOT CALCINED OR SINTERED, INCLUDING DOLOMITE ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE)SHAPE; DOLOMITE RAMMING MIX",
+        "hsn_code": "2518",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PEBBLES, GRAVEL, BROKEN OR CRUSHED STONE, OF A KIND COMMONLY USED FOR CONCRETE\nAGGREGATES, FOR ROAD METALLING OR FOR RAILWAY OR OTHER BALLAST, SHINGLE AND\nFLINT, WHETHER OR NOT HEAT-TREATED; MACADAM OF SLAG, DROSS OR SIMILAR INDUSTRIAL\nWASTE, WHETHER OR NOT INCORPORATING THE MATERIALS CITED IN THE FIRST PART OF THE\nHEADING; TARRED MACADAM; GRANULES, CHIPPINGS AND POWDER, OF STONES OF HEADING\n2515 OR 2516, WHETHER OR NOT HEAT-TREATED - PEBBLES, GRAVEL, BROKEN OR CRUSHED\nSTONE, OF A KIND COMMONLY USED FOR CONCRETE AGGREGATES, FOR ROAD METALLING OR\nFOR RAILWAY OR OTHER BALLAST, SHINGLE AND FLINT, WHETHER OR NOT HEAT-TREATED:\nPAKUR STONE, CRUSHED OR BROKEN",
+        "hsn_code": "25171010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PEBBLES, GRAVEL, BROKEN OR CRUSHED STONE, OF A KIND COMMONLY USED FOR CONCRETE\nAGGREGATES, FOR ROAD METALLING OR FOR RAILWAY OR OTHER BALLAST, SHINGLE AND\nFLINT, WHETHER OR NOT HEAT-TREATED; MACADAM OF SLAG, DROSS OR SIMILAR INDUSTRIAL\nWASTE, WHETHER OR NOT INCORPORATING THE MATERIALS CITED IN THE FIRST PART OF THE\nHEADING; TARRED MACADAM; GRANULES, CHIPPINGS AND POWDER, OF STONES OF HEADING\n2515 OR 2516, WHETHER OR NOT HEAT-TREATED PEBBLES, GRAVEL, BROKEN OR CRUSHED\nSTONE, OF A KIND COMMONLY USED FOR CONCRETE AGGREGATES, FOR ROAD METALLING OR\nFOR RAILWAY OR OTHER BALLAST, SHINGLE AND FLINT, WHETHER OR NOT HEAT-TREATED:\nFLINT",
+        "hsn_code": "25171020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PEBBLES, GRAVEL, BROKEN OR CRUSHED STONE, OF A KIND COMMONLY USED FOR CONCRETE\nAGGREGATES, FOR ROAD METALLING OR FOR RAILWAY OR OTHER BALLAST, SHINGLE AND\nFLINT, WHETHER OR NOT HEAT-TREATED; MACADAM OF SLAG, DROSS OR SIMILAR INDUSTRIAL\nWASTE, WHETHER OR NOT INCORPORATING THE MATERIALS CITED IN THE FIRST PART OF THE\nHEADING; TARRED MACADAM; GRANULES, CHIPPINGS AND POWDER, OF STONES OF HEADING\n2515 OR 2516, WHETHER OR NOT HEAT-TREATED PEBBLES, GRAVEL, BROKEN OR CRUSHED\nSTONE, OF A KIND COMMONLY USED FOR CONCRETE AGGREGATES, FOR ROAD METALLING OR\nFOR RAILWAY OR OTHER BALLAST, SHINGLE AND FLINT, WHETHER OR NOT HEAT-TREATED:\nOTHER",
+        "hsn_code": "25171090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PEBBLES, GRAVEL, BROKEN OR CRUSHED STONE, OF A KIND COMMONLY USED FOR CONCRETE\nAGGREGATES, FOR ROAD METALLING OR FOR RAILWAY OR OTHER BALLAST, SHINGLE AND\nFLINT, WHETHER OR NOT HEAT-TREATED; MACADAM OF SLAG, DROSS OR SIMILAR INDUSTRIAL\nWASTE, WHETHER OR NOT INCORPORATING THE MATERIALS CITED IN THE FIRST PART OF THE\nHEADING; TARRED MACADAM; GRANULES, CHIPPINGS AND POWDER, OF STONES OF HEADING\n2515 OR 2516, WHETHER OR NOT HEAT-TREATED -MACADAM OF SLAG, DROSS OR SIMILAR\nINDUSTRIAL WASTE, WHETHER OR NOT INCORPORATING THE MATERIALS CITED IN SUB\nHEADING 2517 10",
+        "hsn_code": "25172000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PEBBLES, GRAVEL, BROKEN OR CRUSHED STONE, OF A KIND COMMONLY USED FOR CONCRETE\nAGGREGATES, FOR ROAD METALLING OR FOR RAILWAY OR OTHER BALLAST, SHINGLE AND\nFLINT, WHETHER OR NOT HEAT-TREATED; MACADAM OF SLAG, DROSS OR SIMILAR INDUSTRIAL\nWASTE, WHETHER OR NOT INCORPORATING THE MATERIALS CITED IN THE FIRST PART OF THE\nHEADING; TARRED MACADAM; GRANULES, CHIPPINGS AND POWDER, OF STONES OF HEADING\n2515 OR 2516, WHETHER OR NOT HEAT-TREATED TARRED MACADAM",
+        "hsn_code": "25173000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PEBBLES, GRAVEL, BROKEN OR CRUSHED STONE, OF A KIND COMMONLY USED FOR CONCRETE\nAGGREGATES, FOR ROAD METALLING OR FOR RAILWAY OR OTHER BALLAST, SHINGLE AND\nFLINT, WHETHER OR NOT HEAT-TREATED; MACADAM OF SLAG, DROSS OR SIMILAR INDUSTRIAL\nWASTE, WHETHER OR NOT INCORPORATING THE MATERIALS CITED IN THE FIRST PART OF THE\nHEADING; TARRED MACADAM; GRANULES, CHIPPINGS AND POWDER, OF STONES OF HEADING\n2515 OR 2516, WHETHER OR NOT HEAT-TREATED - GRANULES, CHIPPINGS AND POWDER, OF\nSTONE OF HEADING 2515 OR 2516, WHETHER OR NOT HEAT-TREATED: OF MARBLE",
+        "hsn_code": "25174100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PEBBLES, GRAVEL, BROKEN OR CRUSHED STONE, OF A KIND COMMONLY USED FOR CONCRETE\nAGGREGATES, FOR ROAD METALLING OR FOR RAILWAY OR OTHER BALLAST, SHINGLE AND\nFLINT, WHETHER OR NOT HEAT-TREATED; MACADAM OF SLAG, DROSS OR SIMILAR INDUSTRIAL\nWASTE, WHETHER OR NOT INCORPORATING THE MATERIALS CITED IN THE FIRST PART OF THE\nHEADING; TARRED MACADAM; GRANULES, CHIPPINGS AND POWDER, OF STONES OF HEADING\n2515 OR 2516, WHETHER OR NOT HEAT-TREATED - GRANULES, CHIPPINGS AND POWDER, OF\nSTONE OF HEADING 2515 OR 2516, WHETHER OR NOT HEAT-TREATED: OTHER",
+        "hsn_code": "25174900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PEBBLES, GRAVEL, BROKEN OR CRUSHED STONE, OF A KIND COMMONLY USED FOR CONCRETE AGGREGATES, FOR ROAD METALLING OR FOR RAILWAY OR OTHER BALLAST, SHINGLE AND FLINT, WHETHER OR NOT HEAT-TREATED; MACADAM OF SLAG, DROSS OR SIMILAR INDUSTRIAL WASTE, WHETHER OR NOT INCORPORATING THE MATERIALS CITED IN THE FIRST PART OF THE HEADING; TARRED MACADAM; GRANULES, CHIPPINGS AND POWDER, OF STONES OF HEADING 2515 OR 2516, WHETHER OR NOT HEAT-TREATED",
+        "hsn_code": "2517",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SLATE, WHETHER OR NOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE,\nINTO BLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE) SHAPE",
+        "hsn_code": "25140000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMICE STONES; EMERY; NATURAL CORUNDUM, NATURAL GARNET AND OTHER NATURAL ABRASIVES, WHETHER OR NOT HEAT-TREATED",
+        "hsn_code": "2514",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMICE STONE; EMERY; NATURAL CORUNDUM, NATURAL GARNET AND OTHER NATURAL\nABRASIVES, WHETHER OR NOT HEAT-TREATED - PUMICE STONE",
+        "hsn_code": "25131000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMICE STONE; EMERY; NATURAL CORUNDUM, NATURAL GARNET AND OTHER NATURAL\nABRASIVES, WHETHER OR NOT HEAT-TREATED - PUMICE STONE: CRUDE OR IN IRREGULAR\nPIECES, INCLUDING CRUSHED PUMICE (BIMSKIES)",
+        "hsn_code": "25131100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMICE STONE; EMERY; NATURAL CORUNDUM, NATURAL GARNET AND OTHER NATURAL\nABRASIVES, WHETHER OR NOT HEAT-TREATED - PUMICE STONE: OTHER",
+        "hsn_code": "25131900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMICE STONE; EMERY; NATURAL CORUNDUM, NATURAL GARNET AND OTHER NATURAL\nABRASIVES, WHETHER OR NOT HEAT-TREATED - EMERY, NATURAL CORUNDUM, NATURAL\nGARNETAND OTHER NATURAL ABRASIVES: EMERY",
+        "hsn_code": "25132010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMICE STONE; EMERY; NATURAL CORUNDUM, NATURAL GARNET AND OTHER NATURAL\nABRASIVES, WHETHER OR NOT HEAT-TREATED - EMERY, NATURAL CORUNDUM, NATURAL\nGARNETAND OTHER NATURAL ABRASIVES: NATURAL CORUNDUM",
+        "hsn_code": "25132020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMICE STONE; EMERY; NATURAL CORUNDUM, NATURAL GARNET AND OTHER NATURAL\nABRASIVES, WHETHER OR NOT HEAT-TREATED - EMERY, NATURAL CORUNDUM, NATURAL\nGARNETAND OTHER NATURAL ABRASIVES: NATURAL GARNET",
+        "hsn_code": "25132030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMICE STONE; EMERY; NATURAL CORUNDUM, NATURAL GARNET AND OTHER NATURAL\nABRASIVES, WHETHER OR NOT HEAT-TREATED - EMERY, NATURAL CORUNDUM, NATURAL\nGARNETAND OTHER NATURAL ABRASIVES: OTHER",
+        "hsn_code": "25132090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PUMICE STONES; EMERY; NATURAL CORUNDUM, NATURAL GARNET AND OTHER NATURAL ABRASIVES, WHETHER OR NOT HEAT-TREATED",
+        "hsn_code": "2513",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SILICEOUS FOSSIL MEALS (FOR EXAMPLE, KIESELGUHR, TRIPOLITE AND DIATOMITE) AND SIMILAR\nSILICEOUS EARTHS, WHETHER OR NOT CALCINED, OF AN APPARENT SPECIFIC GRAVITY OF 1 OR\nLESS - SILICEOUS FOSSIL MEALS (FOR EXAMPLE, KIESELGUHR, TRIPOLITE AND DIATOMITE) AND\nSIMILAR SILICEOUS EARTHS, WHETHER OR NOT CALCINED, OF AN APPARENT SPECIFIC GRAVITY\nOF 1 OR LESS: KIESELGUHR",
+        "hsn_code": "25120010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SILICEOUS FOSSIL MEALS (FOR EXAMPLE, KIESELGUHR, TRIPOLITE AND DIATOMITE) AND SIMILAR\nSILICEOUS EARTHS, WHETHER OR NOT CALCINED, OF AN APPARENT SPECIFIC GRAVITY OF 1 OR\nLESS - SILICEOUS FOSSIL MEALS (FOR EXAMPLE, KIESELGUHR, TRIPOLITE AND DIATOMITE) AND\nSIMILAR SILICEOUS EARTHS, WHETHER OR NOT CALCINED, OF AN APPARENT SPECIFIC GRAVITY\nOF 1 OR LESS: TRIPOLITE",
+        "hsn_code": "25120020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SILICEOUS FOSSIL MEALS (FOR EXAMPLE, KIESELGUHR, TRIPOLITE AND DIATOMITE) AND SIMILAR\nSILICEOUS EARTHS, WHETHER OR NOT CALCINED, OF AN APPARENT SPECIFIC GRAVITY OF 1 OR\nLESS - SILICEOUS FOSSIL MEALS (FOR EXAMPLE, KIESELGUHR, TRIPOLITE AND DIATOMITE) AND\nSIMILAR SILICEOUS EARTHS, WHETHER OR NOT CALCINED, OF AN APPARENT SPECIFIC GRAVITY\nOF 1 OR LESS: DIATOMITE",
+        "hsn_code": "25120030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SILICEOUS FOSSIL MEALS (FOR EXAMPLE, KIESELGUHR, TRIPOLITE AND DIATOMITE) AND SIMILAR\nSILICEOUS EARTHS, WHETHER OR NOT CALCINED, OF AN APPARENT SPECIFIC GRAVITY OF 1 OR\nLESS - SILICEOUS FOSSIL MEALS (FOR EXAMPLE, KIESELGUHR, TRIPOLITE AND DIATOMITE) AND\nSIMILAR SILICEOUS EARTHS, WHETHER OR NOT CALCINED, OF AN APPARENT SPECIFIC GRAVITY\nOF 1 OR LESS: OTHER",
+        "hsn_code": "25120090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SILICEOUS FOSSIL MEALS (FOR EXAMPLE, KIESELGUHR, TRIPOLITE AND DIATOMITE) AND SIMILAR SILICEOUS EARTHS, WHETHER OR NOT CALCINED, OF AN APPARENT SPECIFIC GRAVITY OF 1 OR LESS",
+        "hsn_code": "2512",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL BARIUM SULPHATE (BARYTES); NATURAL BARIUM CARBONATE (WITHERITE),\nWHETHER OR NOT CALCINED, OTHER THAN BARIUM OXIDE OF HEADING 2816 - NATURAL\nBARIUM SULPHATE (BARYTES): LUMPS",
+        "hsn_code": "25111010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL BARIUM SULPHATE (BARYTES); NATURAL BARIUM CARBONATE (WITHERITE),\nWHETHER OR NOT CALCINED, OTHER THAN BARIUM OXIDE OF HEADING 2816 - NATURAL\nBARIUM SULPHATE (BARYTES): POWDER",
+        "hsn_code": "25111020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL BARIUM SULPHATE (BARYTES); NATURAL BARIUM CARBONATE (WITHERITE),\nWHETHER OR NOT CALCINED, OTHER THAN BARIUM OXIDE OF HEADING 2816 - NATURAL\nBARIUM SULPHATE (BARYTES): OTHER",
+        "hsn_code": "25111090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL BARIUM SULPHATE (BARYTES); NATURAL BARIUM CARBONATE (WITHERITE),\nWHETHER OR NOT CALCINED, OTHER THAN BARIUM OXIDE OF HEADING 2816 - NATURAL\nBARIUM CARBONATE (WITHERITE)",
+        "hsn_code": "25112000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL BARIUM SULPHATE (BARYTES); NATURAL BARIUM CARBONATE (WITHERITE), WHETHER OR NOT CALCINED, OTHER THAN BARIUM OXIDE OF HEADING 2816",
+        "hsn_code": "2511",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL CALCIUM PHOSPHATES, NATURAL ALUMINIUM CALCIUM PHOSPHATES AND\nPHOSPHATIC CHALK - UNGROUND: NATURAL CALCIUM PHOSPHATE",
+        "hsn_code": "25101010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL CALCIUM PHOSPHATES, NATURAL ALUMINIUM CALCIUM PHOSPHATES AND\nPHOSPHATIC CHALK - UNGROUND: NATURAL ALUMINIUM CALCIUM PHOSPHATE",
+        "hsn_code": "25101020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL CALCIUM PHOSPHATES, NATURAL ALUMINIUM CALCIUM PHOSPHATES AND\nPHOSPHATIC CHALK - UNGROUND: NATURAL CALCIUM PHOSPHATE APATITE",
+        "hsn_code": "25101030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL CALCIUM PHOSPHATES, NATURAL ALUMINIUM CALCIUM PHOSPHATES AND\nPHOSPHATIC CHALK - UNGROUND: OTHER",
+        "hsn_code": "25101090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL CALCIUM PHOSPHATES, NATURAL ALUMINIUM CALCIUM PHOSPHATES AND\nPHOSPHATIC CHALK - GROUND: NATURAL CALCIUM PHOSPHATES",
+        "hsn_code": "25102010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL CALCIUM PHOSPHATES, NATURAL ALUMINIUM CALCIUM PHOSPHATES AND\nPHOSPHATIC CHALK - GROUND: NATURAL ALUMINIUM CALCIUM PHOSPHATE",
+        "hsn_code": "25102020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL CALCIUM PHOSPHATES, NATURAL ALUMINIUM CALCIUM PHOSPHATES AND\nPHOSPHATIC CHALK - GROUND: NATURAL CALCIUM PHOSPHATES APATITE",
+        "hsn_code": "25102030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL CALCIUM PHOSPHATES, NATURAL ALUMINIUM CALCIUM PHOSPHATES AND\nPHOSPHATIC CHALK - GROUND: OTHER",
+        "hsn_code": "25102090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL CALCIUM PHOSPHATES, NATURAL ALUMINIUM CALCIUM PHOSPHATES AND PHOSPHATIC CHALK",
+        "hsn_code": "2510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CHALK",
+        "hsn_code": "25090000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "Chalk",
+        "hsn_code": "2509",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS -\nBENTONITE: CRUDE",
+        "hsn_code": "25081010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS -\nBENTONITE: OTHER (INCLUDES PROCESSED AND GROUND)",
+        "hsn_code": "25081090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS -\nDECOLOURISING EARTHS AND FULLERS EARTH: PROCESSED (INCLUDING ACTIVATED)",
+        "hsn_code": "25082010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS -\nDECOLOURISING EARTHS AND FULLERS EARTH: OTHER",
+        "hsn_code": "25082090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS - FIRE\nCLAY: NON-PLASTIC",
+        "hsn_code": "25083010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS - FIRE\nCLAY: SEMI-PLASTIC",
+        "hsn_code": "25083020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS - FIRE\nCLAY: PLASTIC",
+        "hsn_code": "25083030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS - FIRE\nCLAY: OTHER",
+        "hsn_code": "25083090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS -\nOTHER CLAYS: BALL CLAY",
+        "hsn_code": "25084010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS -\nOTHER CLAYS: EARTH CLAY",
+        "hsn_code": "25084020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS -\nOTHER CLAYS: OTHER",
+        "hsn_code": "25084090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS -\nANDALUSITE, KYANITE AND SILLIMANITE: ANDALUSITE",
+        "hsn_code": "25085010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS -\nANDALUSITE, KYANITE AND SILLIMANITE: KYANITE: CRUDE, OTHER THAN CALCINED",
+        "hsn_code": "25085021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS -\nANDALUSITE, KYANITE AND SILLIMANITE: KYANITE: PROCESSED, OTHER THAN CALCINED\n(WASHED OR GROUND OR SCREENED OR BENEFICIATED)",
+        "hsn_code": "25085022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS -\nANDALUSITE, KYANITE AND SILLIMANITE: KYANITE: CALCINED",
+        "hsn_code": "25085023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS -\nANDALUSITE, KYANITE AND SILLIMANITE: SILLIMANITE: LUMPS",
+        "hsn_code": "25085031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS -\nANDALUSITE, KYANITE AND SILLIMANITE: SILLIMANITE: FINES (INCLUDING SAND)",
+        "hsn_code": "25085032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS -\nANDALUSITE, KYANITE AND SILLIMANITE: SILLIMANITE: OTHER",
+        "hsn_code": "25085039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS -\nMULLITE",
+        "hsn_code": "25086000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE\nAND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS -\nCHAMOTTE OR DINAS EARTHS",
+        "hsn_code": "25087000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER CLAYS (NOT INCLUDING EXPANDED CLAYS OF HEADING 6806), ANDALUSITE, KYANITE AND SILLIMANITE, WHETHER OR NOT CALCINED; MULLITE; CHAMOTTE OR DINAS EARTHS",
+        "hsn_code": "2508",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "KAOLIN AND OTHER KAOLINIC CLAYS, WHETHER OR NOT CALCINED - KAOLIN AND OTHER\nKAOLINIC CLAYS, WHETHER OR NOT CALCINED: CRUDE",
+        "hsn_code": "25070010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "KAOLIN AND OTHER KAOLINIC CLAYS, WHETHER OR NOT CALCINED - KAOLIN AND OTHER\nKAOLINIC CLAYS, WHETHER OR NOT CALCINED: OTHER: PHARMACEUTICAL GRADE",
+        "hsn_code": "25070021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "KAOLIN AND OTHER KAOLINIC CLAYS, WHETHER OR NOT CALCINED - KAOLIN AND OTHER\nKAOLINIC CLAYS, WHETHER OR NOT CALCINED: OTHER: CERAMIC GRADE",
+        "hsn_code": "25070022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "KAOLIN AND OTHER KAOLINIC CLAYS, WHETHER OR NOT CALCINED - KAOLIN AND OTHER\nKAOLINIC CLAYS, WHETHER OR NOT CALCINED: OTHER: OTHER",
+        "hsn_code": "25070029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "KAOLIN AND OTHER KAOLINIC CLAYS, WHETHER OR NOT CALCINED",
+        "hsn_code": "2507",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "QUARTZ (OTHER THAN NATURAL SANDS); QUARTZITE, WHETHER OR NOT ROUGHLY TRIMMED\nOR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR\n(INCLUDING SQUARE) SHAPE - QUARTZ: IN LUMPS",
+        "hsn_code": "25061010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "QUARTZ (OTHER THAN NATURAL SANDS); QUARTZITE, WHETHER OR NOT ROUGHLY TRIMMED\nOR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR\n(INCLUDING SQUARE) SHAPE - QUARTZ: IN POWDER",
+        "hsn_code": "25061020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "QUARTZ (OTHER THAN NATURAL SANDS); QUARTZITE, WHETHER OR NOT ROUGHLY TRIMMED\nOR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR\n(INCLUDING SQUARE) SHAPE - QUARTZITE --- IN LUMPS",
+        "hsn_code": "25062010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "QUARTZ (OTHER THAN NATURAL SANDS); QUARTZITE, WHETHER OR NOT ROUGHLY TRIMMED\nOR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR\n(INCLUDING SQUARE) SHAPE - QUARTZITE --- IN POWDER",
+        "hsn_code": "25062020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "QUARTZ (OTHER THAN NATURAL SANDS); QUARTZITE, WHETHER OR NOT ROUGHLY TRIMMED\nOR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR\n(INCLUDING SQUARE) SHAPE - QUARTZITE --- OTHER",
+        "hsn_code": "25062090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "QUARTZ (OTHER THAN NATURAL SANDS); QUARTZITE, WHETHER OR NOT ROUGHLY TRIMMED\nOR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR\n(INCLUDING SQUARE) SHAPE - QUARTZITE : CRUDE OR ROUGHLY TRIMMED: IN LUMPS",
+        "hsn_code": "25062110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "QUARTZ (OTHER THAN NATURAL SANDS); QUARTZITE, WHETHER OR NOT ROUGHLY TRIMMED\nOR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR\n(INCLUDING SQUARE) SHAPE - QUARTZITE : CRUDE OR ROUGHLY TRIMMED: IN POWDER",
+        "hsn_code": "25062120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "QUARTZ (OTHER THAN NATURAL SANDS); QUARTZITE, WHETHER OR NOT ROUGHLY TRIMMED\nOR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR\n(INCLUDING SQUARE) SHAPE - QUARTZITE : CRUDE OR ROUGHLY TRIMMED: OTHER",
+        "hsn_code": "25062190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "QUARTZ (OTHER THAN NATURAL SANDS); QUARTZITE, WHETHER OR NOT ROUGHLY TRIMMED\nOR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR\n(INCLUDING SQUARE) SHAPE - QUARTZITE : OTHER",
+        "hsn_code": "25062900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "QUARTZ (OTHER THAN NATURAL SANDS); QUARTZITE, WHETHER OR NOT ROUGHLY TRIMMED OR MERELY CUT, BY SAWING OR OTHERWISE, INTO BLOCKS OR SLABS OF A RECTANGULAR (INCLUDING SQUARE) SHAPE",
+        "hsn_code": "2506",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL SANDS OF ALL KINDS, WHETHER OR NOT COLOURED, OTHER THAN METAL-BEARING\nSANDS OF CHAPTER 26 - SILICA SANDS AND QUARTZ SANDS: SILICA SANDS: PROCESSED (WHITE)",
+        "hsn_code": "25051011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL SANDS OF ALL KINDS, WHETHER OR NOT COLOURED, OTHER THAN METAL-BEARING\nSANDS OF CHAPTER 26 - SILICA SANDS AND QUARTZ SANDS: SILICA SANDS: PROCESSED\n(BROWN)",
+        "hsn_code": "25051012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL SANDS OF ALL KINDS, WHETHER OR NOT COLOURED, OTHER THAN METAL-BEARING\nSANDS OF CHAPTER 26 - SILICA SANDS AND QUARTZ SANDS: SILICA SANDS: OTHER",
+        "hsn_code": "25051019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL SANDS OF ALL KINDS, WHETHER OR NOT COLOURED, OTHER THAN METAL-BEARING\nSANDS OF CHAPTER 26 - SILICA SANDS AND QUARTZ SANDS: QUARTZ SANDS",
+        "hsn_code": "25051020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL SANDS OF ALL KINDS, WHETHER OR NOT COLOURED, OTHER THAN METAL-BEARING\nSANDS OF CHAPTER 26 OTHER",
+        "hsn_code": "25059000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL SANDS OF ALL KINDS, WHETHER OR NOT COLOURED, OTHER THAN METAL-BEARING SANDS OF CHAPTER 26",
+        "hsn_code": "2505",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL GRAPHITE - IN POWDER OR IN FLAKES:GRAPHITE, CRYSTALLINE",
+        "hsn_code": "25041010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL GRAPHITE - IN POWDER OR IN FLAKES:GRAPHITE, AMORPHOUS",
+        "hsn_code": "25041020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL GRAPHITE - IN POWDER OR IN FLAKES:OTHER",
+        "hsn_code": "25041090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL GRAPHITE - OTHER: GRAPHITE, MICRONISED",
+        "hsn_code": "25049010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL GRAPHITE - OTHER: OTHER",
+        "hsn_code": "25049090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "NATURAL GRAPHITE",
+        "hsn_code": "2504",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SULPHUR OF ALL KINDS, OTHER THAN SUBLIMED SULPHUR, PRECIPITATED SULPHUR AND\nCOLLODIAL SULPHUR - SULPHUR OF ALL KINDS, OTHER THAN SUBLIMED SULPHUR,\nPRECIPITATED SULPHUR AND COLLODIAL SULPHUR: SULPHUR RECOVERED AS BY-PRODUCT IN\nREFINING OF CRUDE OIL",
+        "hsn_code": "25030010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SULPHUR OF ALL KINDS, OTHER THAN SUBLIMED SULPHUR, PRECIPITATED SULPHUR AND\nCOLLODIAL SULPHUR - SULPHUR OF ALL KINDS, OTHER THAN SUBLIMED SULPHUR,\nPRECIPITATED SULPHUR AND COLLODIAL SULPHUR: OTHER",
+        "hsn_code": "25030090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SULPHUR OF ALL KINDS, OTHER THAN SUBLIMED SULPHUR, PRECIPATED SULPHUR AND COLLODIAL SULPHUR",
+        "hsn_code": "2503",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "UNROASTED IRON PYRITES",
+        "hsn_code": "25020000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "Unroasted iron pyrites",
+        "hsn_code": "2502",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER MANUFACTURED TOBACCO AND MANUFACTURED TOBACCO\nSUBSTITUTES;\"HOMOGENISED\" OR \"RECONSTITUTED\" TOBACCO;TOBACCO EXTRACTS AND\nESSENCES-SMOKING TOBACCO, WHETHER OR NTO CONTAINING TOBACCO SUBSTITUTES IN ANY\nPROPORTION--OTHER---BIRIS---OTHER THAN PAPER ROLLED BIRIS, MANUFACTURED WITHOUT\nTHE AID OF TOBACCO",
+        "hsn_code": "24031921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER MANUFACTURED TOBACCO AND MANUFACTURED TOBACCO\nSUBSTITUTES;\"HOMOGENISED\" OR \"RECONSTITUTED\" TOBACCO;TOBACCO EXTRACTS AND\nESSENCES-SMOKING TOBACCO, WHETHER OR NTO CONTAINING TOBACCO SUBSTITUTES IN ANY\nPROPORTION--OTHER---BIRIS----OTHER",
+        "hsn_code": "24031929",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER MANUFACTURE TOBACCO AND MANUFACTURED TOBACCO SUBSTITUTES; HOMOGENIZED OR RECONSTITUTED TOBACCO; TOBACCO EXTRACTS AND ESSENCES",
+        "hsn_code": "2403",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER MANUFACTURED TOBACCO AND MANUFACTURED TOBACCO\nSUBSTITUTES;\"HOMOGENISED\" OR \"RECONSTITUTED\" TOBACCO;TOBACCO EXTRACTS AND\nESSENCES-SMOKING TOBACCO, WHETHER OR NTO CONTAINING TOBACCO SUBSTITUTES IN ANY\nPROPORTION--WATER PIPE TOBACCO SPECIFIED IN SUB-HEADING NOTE TO THIS CHAPTER---\nHUKKAH OR GUDAKU TOBACCO",
+        "hsn_code": "24031110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER MANUFACTURED TOBACCO AND MANUFACTURED TOBACCO\nSUBSTITUTES;\"HOMOGENISED\" OR \"RECONSTITUTED\" TOBACCO;TOBACCO EXTRACTS AND\nESSENCES-SMOKING TOBACCO, WHETHER OR NTO CONTAINING TOBACCO SUBSTITUTES IN ANY\nPROPORTION--WATER PIPE TOBACCO SPECIFIED IN SUB-HEADING NOTE TO THIS CHAPTER---\nOTHER",
+        "hsn_code": "24031190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER MANUFACTURED TOBACCO AND MANUFACTURED TOBACCO\nSUBSTITUTES;\"HOMOGENISED\" OR \"RECONSTITUTED\" TOBACCO;TOBACCO EXTRACTS AND\nESSENCES-SMOKING TOBACCO, WHETHER OR NTO CONTAINING TOBACCO SUBSTITUTES IN ANY\nPROPORTION--OTHER---SMOKING MIXTURES FOR PIPES AND CIGARETTES",
+        "hsn_code": "24031910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER MANUFACTURED TOBACCO AND MANUFACTURED TOBACCO\nSUBSTITUTES;\"HOMOGENISED\" OR \"RECONSTITUTED\" TOBACCO;TOBACCO EXTRACTS AND\nESSENCES-SMOKING TOBACCO, WHETHER OR NTO CONTAINING TOBACCO SUBSTITUTES IN ANY\nPROPORTION--OTHER---OTHER",
+        "hsn_code": "24031990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER MANUFACTURED TOBACCO AND MANUFACTURED TOBACCO\nSUBSTITUTES;HOMOGENISED OR RECONSTITUTED TOBACCO; TOBACCO EXTRACTS AND\nESSENCES - OTHER : HOMOGENISED OR RECONSTITUTED TOBACCO",
+        "hsn_code": "24039100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER MANUFACTURED TOBACCO AND MANUFACTURED TOBACCO\nSUBSTITUTES;HOMOGENISED OR RECONSTITUTED TOBACCO; TOBACCO EXTRACTS AND\nESSENCES - OTHER : OTHER : CHEWING TOBACCO",
+        "hsn_code": "24039910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER MANUFACTURED TOBACCO AND MANUFACTURED TOBACCO\nSUBSTITUTES;HOMOGENISED OR RECONSTITUTED TOBACCO; TOBACCO EXTRACTS AND\nESSENCES - OTHER : OTHER : PREPARATIONS CONTAINING CHEWING TOBACCO",
+        "hsn_code": "24039920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER MANUFACTURED TOBACCO AND MANUFACTURED TOBACCO\nSUBSTITUTES;HOMOGENISED OR RECONSTITUTED TOBACCO; TOBACCO EXTRACTS AND\nESSENCES - OTHER : OTHER : JARDA SCENTED TOBACCO",
+        "hsn_code": "24039930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER MANUFACTURED TOBACCO AND MANUFACTURED TOBACCO\nSUBSTITUTES;HOMOGENISED OR RECONSTITUTED TOBACCO; TOBACCO EXTRACTS AND\nESSENCES - OTHER : OTHER : SNUFF",
+        "hsn_code": "24039940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER MANUFACTURED TOBACCO AND MANUFACTURED TOBACCO\nSUBSTITUTES;HOMOGENISED OR RECONSTITUTED TOBACCO; TOBACCO EXTRACTS AND\nESSENCES - OTHER : OTHER : PREPARATIONS CONTAINING SNUFF",
+        "hsn_code": "24039950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER MANUFACTURED TOBACCO AND MANUFACTURED TOBACCO\nSUBSTITUTES;HOMOGENISED OR RECONSTITUTED TOBACCO; TOBACCO EXTRACTS AND\nESSENCES - OTHER : OTHER : TOBACCO EXTRACTS AND ESSENCE",
+        "hsn_code": "24039960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER MANUFACTURED TOBACCO AND MANUFACTURED TOBACCO\nSUBSTITUTES;HOMOGENISED OR RECONSTITUTED TOBACCO; TOBACCO EXTRACTS AND\nESSENCES - OTHER : OTHER : CUT-TOBACCO",
+        "hsn_code": "24039970",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "OTHER MANUFACTURED TOBACCO AND MANUFACTURED TOBACCO\nSUBSTITUTES;HOMOGENISED OR RECONSTITUTED TOBACCO; TOBACCO EXTRACTS AND\nESSENCES - OTHER : OTHER : OTHER",
+        "hsn_code": "24039990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "CIGARS, CHEROOTS, CIGARILLOS AND CIGARETTES, OF TOBACCO OR OF TOBACCO SUBSTITUTES -\nCIGARS, CHEROOTS AND CIGARILLOS, CONTAINING TOBACCO : CIGARILLOS",
+        "hsn_code": "24021020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "CIGARS, CHEROOTS, CIGARILLOS AND CIGARETTES, OF TOBACCO OR OF TOBACCO SUBSTITUTES",
+        "hsn_code": "2402",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "CIGARS, CHEROOTS, CIGARILLOS AND CIGARETTES, OF TOBACCO OR OF TOBACCO SUBSTITUTES -\nCIGARS, CHEROOTS AND CIGARILLOS, CONTAINING TOBACCO : CIGAR AND CHEROOTS",
+        "hsn_code": "24021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "CIGARS, CHEROOTS, CIGARILLOS AND CIGARETTES, OF TOBACCO OR OF TOBACCO SUBSTITUTES -\nCIGARS, CHEROOTS AND CIGARILLOS, CONTAINING TOBACCO : CIGAR AND CHEROOTS",
+        "hsn_code": "24022010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "CIGARS, CHEROOTS, CIGARILLOS AND CIGARETTES, OF TOBACCO OR OF TOBACCO SUBSTITUTES -\nCIGARS, CHEROOTS AND CIGARILLOS, CONTAINING TOBACCO : CIGARILLOS",
+        "hsn_code": "24022020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "CIGARS, CHEROOTS, CIGARILLOS AND CIGARETTES, OF TOBACCO OR OF TOBACCO SUBSTITUTES -\nCIGARETTES, CONTAINING TOBACCO : FILTER CIGARETTES OF LENGTH (INCLUDING THE LENGTH\nOF THE FILTER, THE LENGTH OF FILTER BEING 11 MILLIMETRES OR ITS ACTUAL LENGTH,\nWHICHEVER IS MORE) NOT EXCEEDING 60 MILLIMETRES",
+        "hsn_code": "24022030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "CIGARS, CHEROOTS, CIGARILLOS AND CIGARETTES, OF TOBACCO OR OF TOBACCO SUBSTITUTES -\nCIGARETTES, CONTAINING TOBACCO : FILTER CIGARETTES OF LENGTH (INCLUDING THE LENGTH\nOF THE FILTER, THE LENGTH OF FILTER BEING 11 MILLIMETRES OR ITS ACTUAL LENGTH,\nWHICHEVER IS MORE) EXCEEDING 60 MILLIMETRES BUT NOT EXCEEDING 70 MILLIMETRES",
+        "hsn_code": "24022040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "CIGARS, CHEROOTS, CIGARILLOS AND CIGARETTES, OF TOBACCO OR OF TOBACCO SUBSTITUTES -\nCIGARETTES, CONTAINING TOBACCO : FILTER CIGARETTES OF LENGTH (INCLUDING THE LENGTH\nOF THE FILTER, THE LENGTH OF FILTER BEING 11 MILLIMETRES OR ITS ACTUAL LENGTH,\nWHICHEVER IS MORE) EXCEEDING 70 MILLIMETRES BUT NOT EXCEEDING 75 MILLIMETRES",
+        "hsn_code": "24022050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "CIGARS, CHEROOTS, CIGARILLOS AND CIGARETTES, OF TOBACCO OR OF TOBACCO SUBSTITUTES -\nCIGARETTES, CONTAINING TOBACCO : OTHER",
+        "hsn_code": "24022090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "CIGARS, CHEROOTS, CIGARILLOS AND CIGARETTES, OF TOBACCO OR OF TOBACCO SUBSTITUTES -\nOTHER : CIGARETTES OF TOBACCO SUBSTITUTES",
+        "hsn_code": "24029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "CIGARS, CHEROOTS, CIGARILLOS AND CIGARETTES, OF TOBACCO OR OF TOBACCO SUBSTITUTES -\nOTHER : CIGARILLOS OF TOBACCO SUBSTITUTES",
+        "hsn_code": "24029020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "CIGARS, CHEROOTS, CIGARILLOS AND CIGARETTES, OF TOBACCO OR OF TOBACCO SUBSTITUTES -\nOTHER : OTHER",
+        "hsn_code": "24029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "WINE LEES; ARGOL",
+        "hsn_code": "23070000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM OF PELLETS, RESULTING FROM THE EXTRACTION OF VEGETABLE FATS OR OILS, OTHER THAN THOSE OF HEADING 2304 OR 2305",
+        "hsn_code": "2307",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHEROR NOT GROUND OR IN THE FORM OF\nPELLETS, RESULTING FROM THE EXTRACTION OFGROUND-NUT OIL - OIL-CAKE AND OTHER SOLID\nRESIDUES, WHETHER ORNOT GROUND OR IN THE FORM OF PELLETS, RESULTINGFROM THE\nEXTRACTION OF GROUND-NUT OIL : OIL-CAKE AND OIL-CAKE MEAL OF GROUND-NUT, EXPELLER\nVARIETY",
+        "hsn_code": "23050010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHEROR NOT GROUND OR IN THE FORM OF\nPELLETS, RESULTING FROM THE EXTRACTION OFGROUND-NUT OIL - OIL-CAKE AND OTHER SOLID\nRESIDUES, WHETHER ORNOT GROUND OR IN THE FORM OF PELLETS, RESULTINGFROM THE\nEXTRACTION OF GROUND-NUT OIL : OIL-CAKE AND OIL-CAKE MEAL OF GROUND-NUT, SOLVENT\nEXTRACTED VARIETY (DEFATTED)",
+        "hsn_code": "23050020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHEROR NOT GROUND OR IN THE FORM OF\nPELLETS, RESULTING FROM THE EXTRACTION OFGROUND-NUT OIL - OIL-CAKE AND OTHER SOLID\nRESIDUES, WHETHER ORNOT GROUND OR IN THE FORM OF PELLETS, RESULTINGFROM THE\nEXTRACTION OF GROUND-NUT OIL : OTHER",
+        "hsn_code": "23050090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM OF PELLETS, RESULTING FROM THE EXTRACTION OF GROUND-NUT OIL",
+        "hsn_code": "2305",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES WHETHEROR NOT GROUND OR IN THE FORM OF PELLETS,\nRESULTING FROM THE EXTRACTION OFSOYABEAN OIL - OIL-CAKE AND OTHER SOLID RESIDUES\nWHETHER OR NOT GROUND OR IN THE FORM OF PELLETS, RESULTING FROM THE EXTRACTION\nOF SOYABEAN OIL : OIL-CAKE AND OIL-CAKE MEAL OF SOYABEAN, EXPELLER VARIETY",
+        "hsn_code": "23040010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES WHETHEROR NOT GROUND OR IN THE FORM OF PELLETS,\nRESULTING FROM THE EXTRACTION OFSOYABEAN OIL - OIL-CAKE AND OTHER SOLID RESIDUES\nWHETHER OR NOT GROUND OR IN THE FORM OF PELLETS, RESULTING FROM THE EXTRACTION\nOF SOYABEAN OIL : OIL-CAKE OF SOYABEAN, SOLVENT EXTRACTED (DEFATTED) VARIETY",
+        "hsn_code": "23040020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES WHETHEROR NOT GROUND OR IN THE FORM OF PELLETS,\nRESULTING FROM THE EXTRACTION OFSOYABEAN OIL - OIL-CAKE AND OTHER SOLID RESIDUES\nWHETHER OR NOT GROUND OR IN THE FORM OF PELLETS, RESULTING FROM THE EXTRACTION\nOF SOYABEAN OIL : MEAL OF SOYABEAN, SOLVENT EXTRACTED (DEFATTED)",
+        "hsn_code": "23040030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES WHETHEROR NOT GROUND OR IN THE FORM OF PELLETS,\nRESULTING FROM THE EXTRACTION OFSOYABEAN OIL - OIL-CAKE AND OTHER SOLID RESIDUES\nWHETHER OR NOT GROUND OR IN THE FORM OF PELLETS, RESULTING FROM THE EXTRACTION\nOF SOYABEAN OIL : OTHER",
+        "hsn_code": "23040090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM OF PELLETS, RESULTING FROM THE EXTRACTION OF SOYABEAN OIL",
+        "hsn_code": "2304",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RESIDUES OF STARCH MANUFACTURE AND SIMILARRESIDUES, BEET-PULP, BAGASSE AND\nOTHERWASTE OF SUGAR MANUFACTURE, BREWING ORDISTILLING DREGS AND WASTE,\nWHETHER ORNOT IN THE FORM OF PELLETS RESIDUES OF STARCH MANUFACTURE AND SIMILAR\nRESIDUES",
+        "hsn_code": "23031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RESIDUES OF STARCH MANUFACTURE AND SIMILARRESIDUES, BEET-PULP, BAGASSE AND\nOTHERWASTE OF SUGAR MANUFACTURE, BREWING ORDISTILLING DREGS AND WASTE,\nWHETHER ORNOT IN THE FORM OF PELLETS BEET-PULP, BAGASSE AND OTHER WASTE OF SUGAR\nMANUFACTURE",
+        "hsn_code": "23032000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RESIDUES OF STARCH MANUFACTURE AND SIMILARRESIDUES, BEET-PULP, BAGASSE AND\nOTHERWASTE OF SUGAR MANUFACTURE, BREWING ORDISTILLING DREGS AND WASTE,\nWHETHER ORNOT IN THE FORM OF PELLETS BREWING OR DISTILLING DREGS AND WASTE",
+        "hsn_code": "23033000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RESIDUES OF STARCH MANUFACTURE AND SIMILAR RESIDUES, BEET-PULP, BAGASSE AND OTHER WASTE OF SUGAR MANUFACTURE, BREWING OR DISTILLING DREGS AND WASTE, WHETHER OR NOT IN THE FORM OF PELLETS",
+        "hsn_code": "2303",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BRAN, SHARPS AND OTHER RESIDUES, WHETHEROR NOT IN THE FORM OF PELLETS, DERIVED\nFROM THE SIFTING, MILLING OR OTHER WORKING OF CEREALS OR OF LEGUMINOUS PLANTS -\nOF MAIZE (CORN) : MAIZE BRAN",
+        "hsn_code": "23021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BRAN, SHARPS AND OTHER RESIDUES, WHETHEROR NOT IN THE FORM OF PELLETS, DERIVED\nFROM THE SIFTING, MILLING OR OTHER WORKING OF CEREALS OR OF LEGUMINOUS PLANTS -\nOF MAIZE (CORN) : OTHER",
+        "hsn_code": "23021090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BRAN, SHARPS AND OTHER RESIDUES, WHETHEROR NOT IN THE FORM OF PELLETS, DERIVED\nFROM THE SIFTING, MILLING OR OTHER WORKING OF CEREALS OR OF LEGUMINOUS PLANTS -\nOF RICE : DE-OILED RICE BRAN",
+        "hsn_code": "23022010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BRAN, SHARPS AND OTHER RESIDUES, WHETHEROR NOT IN THE FORM OF PELLETS, DERIVED\nFROM THE SIFTING, MILLING OR OTHER WORKING OF CEREALS OR OF LEGUMINOUS PLANTS -\nOF RICE : RICE BRAN, RAW",
+        "hsn_code": "23022020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BRAN, SHARPS AND OTHER RESIDUES, WHETHEROR NOT IN THE FORM OF PELLETS, DERIVED\nFROM THE SIFTING, MILLING OR OTHER WORKING OF CEREALS OR OF LEGUMINOUS PLANTS -\nOF RICE : OTHER",
+        "hsn_code": "23022090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BRAN, SHARPS AND OTHER RESIDUES, WHETHEROR NOT IN THE FORM OF PELLETS, DERIVED\nFROM THE SIFTING, MILLING OR OTHER WORKING OF CEREALS OR OF LEGUMINOUS PLANTS -\nOF WHEAT",
+        "hsn_code": "23023000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BRAN, SHARPS AND OTHER RESIDUES, WHETHEROR NOT IN THE FORM OF PELLETS, DERIVED\nFROM THE SIFTING, MILLING OR OTHER WORKING OF CEREALS OR OF LEGUMINOUS PLANTS -\nOF OTHER CEREALS",
+        "hsn_code": "23024000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BRAN, SHARPS AND OTHER RESIDUES, WHETHEROR NOT IN THE FORM OF PELLETS, DERIVED\nFROM THE SIFTING, MILLING OR OTHER WORKING OF CEREALS OR OF LEGUMINOUS PLANTS -\nOF LEGUMINOUS PLANTS",
+        "hsn_code": "23025000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "BRAN, SHARPS AND OTHER RESIDUES, WHETHER OR NOT IN THE FORM OF PELLETS, DERIVED FROM THE SIFTING, MILLING OR OTHER WORKING OF CEREALS OR OF LEGUMINOUS PLANTS",
+        "hsn_code": "2302",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FLOURS, MEALS AND PELLETS, OF MEATOR MEAT OFFAL, OF FISH OR OFCRUSTACEANS,\nMOLLUSCS OR OTHERAQUATIC INVERTEBRATES, UNFIT FOR HUMAN CONSUMPTION; GREAVES -\nFLOURS, MEALS AND PELLETS, OF MEAT OR MEAT OFFAL; GREAVES : MEAT MEALS AND PELLETS\n(INCLUDING TANKAGE)",
+        "hsn_code": "23011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FLOURS, MEALS AND PELLETS, OF MEATOR MEAT OFFAL, OF FISH OR OFCRUSTACEANS,\nMOLLUSCS OR OTHERAQUATIC INVERTEBRATES, UNFIT FOR HUMAN CONSUMPTION; GREAVES -\nFLOURS, MEALS AND PELLETS, OF MEAT OR MEAT OFFAL; GREAVES : OTHER (INCLUDING\nGREAVES)",
+        "hsn_code": "23011090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FLOURS, MEALS AND PELLETS, OF MEATOR MEAT OFFAL, OF FISH OR OFCRUSTACEANS,\nMOLLUSCS OR OTHERAQUATIC INVERTEBRATES, UNFIT FOR HUMAN CONSUMPTION; GREAVES -\nFLOURS, MEALS AND PELLETS, OF FISH OR OF CRUSTACEANS, MOLLUSCS OR OTHER AQUATIC\nINVERTEBRATES : FISH MEAL, UNFIT FOR HUMAN CONSUMPTION : IN POWDER FORM",
+        "hsn_code": "23012011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FLOURS, MEALS AND PELLETS, OF MEATOR MEAT OFFAL, OF FISH OR OFCRUSTACEANS,\nMOLLUSCS OR OTHERAQUATIC INVERTEBRATES, UNFIT FOR HUMAN CONSUMPTION; GREAVES -\nFLOURS, MEALS AND PELLETS, OF FISH OR OF CRUSTACEANS, MOLLUSCS OR OTHER AQUATIC\nINVERTEBRATES : FISH MEAL, UNFIT FOR HUMAN CONSUMPTION : OTHER",
+        "hsn_code": "23012019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FLOURS, MEALS AND PELLETS, OF MEATOR MEAT OFFAL, OF FISH OR OFCRUSTACEANS,\nMOLLUSCS OR OTHERAQUATIC INVERTEBRATES, UNFIT FOR HUMAN CONSUMPTION; GREAVES -\nFLOURS, MEALS AND PELLETS, OF FISH OR OF CRUSTACEANS, MOLLUSCS OR OTHER AQUATIC\nINVERTEBRATES : OTHER",
+        "hsn_code": "23012090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FLOURS, MEALS AND PELLETS, OF MEAT OR MEAT OFFAL, OF FISH OR OF CRUSTACEANS, MOLLUSCS OR OTHER AQUATIC INVERTEBRATES, UNFIT FOR HUMAN CONSUMPTION; GREAVES",
+        "hsn_code": "2301",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "VINEGAR AND SUBSTITUTES FOR VINEGAROBTAINED FROM ACETIC ACID - VINEGAR AND\nSUBSTITUTES FOR VINEGAR OBTAINED FROM ACETIC ACID : BREWED VINEGAR",
+        "hsn_code": "22090010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VINEGAR AND SUBSTITUTES FOR VINEGAROBTAINED FROM ACETIC ACID - VINEGAR AND\nSUBSTITUTES FOR VINEGAR OBTAINED FROM ACETIC ACID : SYNTHETIC VINEGAR",
+        "hsn_code": "22090020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VINEGAR AND SUBSTITUTES FOR VINEGAROBTAINED FROM ACETIC ACID - VINEGAR AND\nSUBSTITUTES FOR VINEGAR OBTAINED FROM ACETIC ACID : OTHER",
+        "hsn_code": "22090090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VINEGAR AND SUBSTITUTES FOR VINEGAR OBTAINED FROM ACETIC ACID",
+        "hsn_code": "2209",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22071011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22071019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22071090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ETHYL ALCOHOL AND OTHER SPIRITS, DENATURED, OF ANY STRENGTH ETHYL ALCOHOL AND\nOTHER SPIRITS, DENATURED, OF ANY STRENGTH",
+        "hsn_code": "22072000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "UNDENATURED ETHYL ALCOHOL OF AN ALCOHOLIC STRENGTH BY VOLUME OF 80% VOL. OR HIGHER; ETHYL ALCOHOL AND OTHER SPIRITS, DENATURED, OF ANY STRENGTH",
+        "hsn_code": "2207",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WATERS, INCLUDING MINERAL WATERS ANDAERATED WATERS, CONTAINING ADDED SUGAROR\nOTHER SWEETENING MATTER OR FLAVOURED,AND OTHER NON - ALCOHOLIC BEVERAGES,\nNOTINCLUDING FRUIT OR VEGETABLE JUICES OFHEADING 2009 - OTHER : SOYA MILK DRINKS,\nWHETHER OR NOT SWEETENDED OR FLAVOURED",
+        "hsn_code": "22029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "WATERS, INCLUDING MINERAL WATERS ANDAERATED WATERS, CONTAINING ADDED SUGAROR\nOTHER SWEETENING MATTER OR FLAVOURED,AND OTHER NON - ALCOHOLIC BEVERAGES,\nNOTINCLUDING FRUIT OR VEGETABLE JUICES OFHEADING 2009 - OTHER : FRUIT PULP OR FRUIT\nJUICE BASED DRINKS",
+        "hsn_code": "22029020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "WATERS, INCLUDING MINERAL WATERS ANDAERATED WATERS, CONTAINING ADDED SUGAROR\nOTHER SWEETENING MATTER OR FLAVOURED,AND OTHER NON - ALCOHOLIC BEVERAGES,\nNOTINCLUDING FRUIT OR VEGETABLE JUICES OFHEADING 2009 - OTHER : BEVERAGES\nCONTAINING MILK",
+        "hsn_code": "22029030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "WATERS, INCLUDING MINERAL WATERS ANDAERATED WATERS, CONTAINING ADDED SUGAROR\nOTHER SWEETENING MATTER OR FLAVOURED,AND OTHER NON - ALCOHOLIC BEVERAGES,\nNOTINCLUDING FRUIT OR VEGETABLE JUICES OFHEADING 2009 - OTHER : OTHER",
+        "hsn_code": "22029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "WATERS, INCLUDING MINERAL WATERS AND AERATED WATERS, CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER OR FLAVOURED, AND OTHER NON-ALCOHOLIC BEVERAGES, NOT INCLUDING FRUIT OR VEGETABLE JUICES OF HEADING 2009",
+        "hsn_code": "2202",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "SOUPS AND BROTHS AND PREPARATIONSTHEREFOR; HOMOGENISED COMPOSITE FOOD\nPREPARATIONS - SOUPS AND BROTHS AND PREPARATIONS THEREFOR : DRIED",
+        "hsn_code": "21041010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOUPS AND BROTHS AND PREPARATIONSTHEREFOR; HOMOGENISED COMPOSITE FOOD\nPREPARATIONS - SOUPS AND BROTHS AND PREPARATIONS THEREFOR : OTHER",
+        "hsn_code": "21041090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOUPS AND BROTHS AND PREPARATIONSTHEREFOR; HOMOGENISED COMPOSITE FOOD\nPREPARATIONS HOMOGENISED COMPOSITE FOOD PREPARATIONS",
+        "hsn_code": "21042000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SOUPS AND BROTHS AND PREPARATIONS THEREFOR; HOMOGENIZED COMPOSITE FOOD PREPARATIONS",
+        "hsn_code": "2104",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "SAUCES AND PREPARATIONS THEREFOR, MIXEDCONDIMENTS AND MIXED\nSEASONINGS;MUSTARD FLOUR AND MEAL AND PREPARED MUSTARD - SOYA SAUCE",
+        "hsn_code": "21031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SAUCES AND PREPARATIONS THEREFOR, MIXEDCONDIMENTS AND MIXED\nSEASONINGS;MUSTARD FLOUR AND MEAL AND PREPARED MUSTARD - TOMATO KETCHUP AND\nOTHER TOMATO SAUCES",
+        "hsn_code": "21032000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SAUCES AND PREPARATIONS THEREFOR, MIXEDCONDIMENTS AND MIXED\nSEASONINGS;MUSTARD FLOUR AND MEAL AND PREPARED MUSTARD - MUSTARD FLOUR AND\nMEAL AND PREPARED MUSTARD",
+        "hsn_code": "21033000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SAUCES AND PREPARATIONS THEREFOR, MIXEDCONDIMENTS AND MIXED\nSEASONINGS;MUSTARD FLOUR AND MEAL AND PREPARED MUSTARD - OTHER : CURRY PASTE",
+        "hsn_code": "21039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SAUCES AND PREPARATIONS THEREFOR, MIXEDCONDIMENTS AND MIXED\nSEASONINGS;MUSTARD FLOUR AND MEAL AND PREPARED MUSTARD - OTHER : CHILLI SAUCE",
+        "hsn_code": "21039020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SAUCES AND PREPARATIONS THEREFOR, MIXEDCONDIMENTS AND MIXED\nSEASONINGS;MUSTARD FLOUR AND MEAL AND PREPARED MUSTARD - OTHER : MAJONNAISE\nAND SALAD DRESSINGS",
+        "hsn_code": "21039030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SAUCES AND PREPARATIONS THEREFOR, MIXEDCONDIMENTS AND MIXED\nSEASONINGS;MUSTARD FLOUR AND MEAL AND PREPARED MUSTARD - OTHER : MIXED,\nCONDIMENTS AND MIXED SEASONING",
+        "hsn_code": "21039040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SAUCES AND PREPARATIONS THEREFOR, MIXEDCONDIMENTS AND MIXED\nSEASONINGS;MUSTARD FLOUR AND MEAL AND PREPARED MUSTARD - OTHER : OTHER",
+        "hsn_code": "21039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SAUCES AND PREPARATIONS THEREFOR; MIXED CONDIMENTS AND MIXED SEASONINGS; MUSTARD FLOUR AND MEAL AND PREPARED MUSTARD",
+        "hsn_code": "2103",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MEAT AND EDIBLEMEAT OFFAL, SALTED, INBRINE, DRIED OR SMOKED;EDIBLEFLOURS AND\nMEALS OF MEAT OR MEAT OFFALMEAT OF BOVINE ANIMALS",
+        "hsn_code": "2102000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YEASTS (ACTIVE OR INACTIVE); OTHER SINGLECELL MICRO - ORGANISMS, DEAD (BUT\nNOTINCLUDING VACCINES OF HEADING 3002);PREPARED BAKING POWDERS - ACTIVE YEASTS :\nCULTURE YEAST",
+        "hsn_code": "21021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YEASTS (ACTIVE OR INACTIVE); OTHER SINGLECELL MICRO - ORGANISMS, DEAD (BUT\nNOTINCLUDING VACCINES OF HEADING 3002);PREPARED BAKING POWDERS - ACTIVE YEASTS :\nBAKERS YEAST",
+        "hsn_code": "21021020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YEASTS (ACTIVE OR INACTIVE); OTHER SINGLECELL MICRO - ORGANISMS, DEAD (BUT\nNOTINCLUDING VACCINES OF HEADING 3002);PREPARED BAKING POWDERS - ACTIVE YEASTS :\nOTHER",
+        "hsn_code": "21021090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YEASTS (ACTIVE OR INACTIVE); OTHER SINGLECELL MICRO - ORGANISMS, DEAD (BUT\nNOTINCLUDING VACCINES OF HEADING 3002);PREPARED BAKING POWDERS - INACTIVE YEASTS,\nOTHER SINGLE-CELL MICRO-ORGANISMS, DEAD",
+        "hsn_code": "21022000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YEASTS (ACTIVE OR INACTIVE); OTHER SINGLECELL MICRO - ORGANISMS, DEAD (BUT\nNOTINCLUDING VACCINES OF HEADING 3002);PREPARED BAKING POWDERS - PREPARED BAKING\nPOWDERS",
+        "hsn_code": "21023000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "YEASTS (ACTIVE OR INACTIVE); OTHER SINGLE CELL MICRO-ORGANISMS, DEAD (BUT NOT INCLUDING VACCINES OF HEADING 3002); PREPARED BAKING POWDERS",
+        "hsn_code": "2102",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT JUICES (INCLUDING GRAPE MUST) ANDVEGETABLE JUICES, UNFERMENTED AND\nNOTCONTAINING ADDED SPIRIT, WHETHER OR NOTCONTAINING ADDED SUGAR OR\nOTHERSWEETENING MATTER - ORANGE JUICE : FROZEN",
+        "hsn_code": "20091100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT JUICES (INCLUDING GRAPE MUST) ANDVEGETABLE JUICES, UNFERMENTED AND\nNOTCONTAINING ADDED SPIRIT, WHETHER OR NOTCONTAINING ADDED SUGAR OR\nOTHERSWEETENING MATTER - ORANGE JUICE : NOT FROZEN, OF A BRIX VALUE NOT\nEXCEEDING 20",
+        "hsn_code": "20091200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT JUICES (INCLUDING GRAPE MUST) ANDVEGETABLE JUICES, UNFERMENTED AND\nNOTCONTAINING ADDED SPIRIT, WHETHER OR NOTCONTAINING ADDED SUGAR OR\nOTHERSWEETENING MATTER - ORANGE JUICE : OTHER",
+        "hsn_code": "20091900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT JUICES (INCLUDING GRAPE MUST) ANDVEGETABLE JUICES, UNFERMENTED AND\nNOTCONTAINING ADDED SPIRIT, WHETHER OR NOTCONTAINING ADDED SUGAR OR\nOTHERSWEETENING MATTER - GRAPEFRUIT (INCLUDING POMELO)JUICE :-- OF A BRIX VALUE\nNOT EXCEEDING 20",
+        "hsn_code": "20092100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT JUICES (INCLUDING GRAPE MUST) ANDVEGETABLE JUICES, UNFERMENTED AND\nNOTCONTAINING ADDED SPIRIT, WHETHER OR NOTCONTAINING ADDED SUGAR OR\nOTHERSWEETENING MATTER - GRAPEFRUIT (INCLUDING POMELO) JUICE : -- OTHER",
+        "hsn_code": "20092900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT JUICES (INCLUDING GRAPE MUST) ANDVEGETABLE JUICES, UNFERMENTED AND\nNOTCONTAINING ADDED SPIRIT, WHETHER OR NOTCONTAINING ADDED SUGAR OR\nOTHERSWEETENING MATTER - JUICE OF ANY OTHER SINGLE CITRUS FRUIT : OF A BRIX VALUE\nNOT EXCEEDING 20",
+        "hsn_code": "20093100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT JUICES (INCLUDING GRAPE MUST) ANDVEGETABLE JUICES, UNFERMENTED AND\nNOTCONTAINING ADDED SPIRIT, WHETHER OR NOTCONTAINING ADDED SUGAR OR\nOTHERSWEETENING MATTER - JUICE OF ANY OTHER SINGLE CITRUS FRUIT : OTHER",
+        "hsn_code": "20093900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT JUICES (INCLUDING GRAPE MUST) ANDVEGETABLE JUICES, UNFERMENTED AND\nNOTCONTAINING ADDED SPIRIT, WHETHER OR NOTCONTAINING ADDED SUGAR OR\nOTHERSWEETENING MATTER - PINEAPPLE JUICE : OF A BRIX VALUE NOT EXCEEDING 20",
+        "hsn_code": "20094100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT JUICES (INCLUDING GRAPE MUST) ANDVEGETABLE JUICES, UNFERMENTED AND\nNOTCONTAINING ADDED SPIRIT, WHETHER OR NOTCONTAINING ADDED SUGAR OR\nOTHERSWEETENING MATTER - PINEAPPLE JUICE : OTHER",
+        "hsn_code": "20094900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT JUICES (INCLUDING GRAPE MUST) ANDVEGETABLE JUICES, UNFERMENTED AND\nNOTCONTAINING ADDED SPIRIT, WHETHER OR NOTCONTAINING ADDED SUGAR OR\nOTHERSWEETENING MATTER TOMATO JUICE",
+        "hsn_code": "20095000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT JUICES (INCLUDING GRAPE MUST) ANDVEGETABLE JUICES, UNFERMENTED AND\nNOTCONTAINING ADDED SPIRIT, WHETHER OR NOTCONTAINING ADDED SUGAR OR\nOTHERSWEETENING MATTER - GRAPE JUICE (INCLUDING GRAPE MUST) : OF A BRIX VALUE NOT\nEXCEEDING 30",
+        "hsn_code": "20096100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT JUICES (INCLUDING GRAPE MUST) ANDVEGETABLE JUICES, UNFERMENTED AND\nNOTCONTAINING ADDED SPIRIT, WHETHER OR NOTCONTAINING ADDED SUGAR OR\nOTHERSWEETENING MATTER - GRAPE JUICE (INCLUDING GRAPE MUST) : OTHER",
+        "hsn_code": "20096900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT JUICES (INCLUDING GRAPE MUST) ANDVEGETABLE JUICES, UNFERMENTED AND\nNOTCONTAINING ADDED SPIRIT, WHETHER OR NOTCONTAINING ADDED SUGAR OR\nOTHERSWEETENING MATTER - APPLE JUICE : OF A BRIX VALUE NOT EXCEEDING 20",
+        "hsn_code": "20097100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT JUICES (INCLUDING GRAPE MUST) ANDVEGETABLE JUICES, UNFERMENTED AND\nNOTCONTAINING ADDED SPIRIT, WHETHER OR NOTCONTAINING ADDED SUGAR OR\nOTHERSWEETENING MATTER - APPLE JUICE : OTHER",
+        "hsn_code": "20097900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT JUICES (INCLUDING GRAPE MUST) AND VEGETABLE JUICES, UNFERMENTED AND NOT\nCONTAINING ADDED SPIRIT, WHETHER OR NOT CONTAINING ADDED SUGAR OR OTHER\nSWEETENING MATTER-JUICE OF ANY OTHER SINGLE FRUIT OR VEGETABLE--CRANBERRY\n(VACCINIUM MACROCARPON,VACCINIUM OXYCOCCOS VACCINIUM VITIS-IDAEA) JUICE",
+        "hsn_code": "20098100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT JUICES (INCLUDING GRAPE MUST) AND VEGETABLE JUICES, UNFERMENTED AND NOT\nCONTAINING ADDED SPIRIT, WHETHER OR NOT CONTAINING ADDED SUGAR OR OTHER\nSWEETENING MATTER-JUICE OF ANY OTHER SINGLE FRUIT OR VEGETABLE--OTHER ---MANGO\nJUICE",
+        "hsn_code": "20098910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT JUICES (INCLUDING GRAPE MUST) AND VEGETABLE JUICES, UNFERMENTED AND\nNOTCONTAINING ADDED SPIRIT, WHETHER OR NOTCONTAINING ADDED SUGAR OR\nOTHERSWEETENING MATTER MIXTURES OF JUICES",
+        "hsn_code": "20099000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT JUICES (INCLUDING GRAPE MUST) AND VEGETABLE JUICES, UNFERMENTED AND NOT CONTAINING ADDED SPIRIT, WHETHER OR NOT CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER",
+        "hsn_code": "2009",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT JUICES (INCLUDING GRAPE MUST) AND VEGETABLE JUICES, UNFERMENTED AND NOT\nCONTAINING ADDED SPIRIT, WHETHER OR NOT CONTAINING ADDED SUGAR OR OTHER\nSWEETENING MATTER-JUICE OF ANY OTHER SINGLE FRUIT OR VEGETABLE--OTHER ---OTHER",
+        "hsn_code": "20098990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - NUTS, GROUND - NUTS AND OTHER SEEDS,\nWHETHER OR NOT MIXED TOGETHER : GROUND-NUTS",
+        "hsn_code": "20081100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - NUTS, GROUND - NUTS AND OTHER SEEDS,\nWHETHER OR NOT MIXED TOGETHER : OTHER, INCLUDING MIXTURES : CASHEW NUT, ROASTED,\nSALTED OR ROASTED AND SALTED",
+        "hsn_code": "20081910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - NUTS, GROUND - NUTS AND OTHER SEEDS,\nWHETHER OR NOT MIXED TOGETHER : OTHER, INCLUDING MIXTURES : OTHER ROASTED NUTS\nAND SEEDS",
+        "hsn_code": "20081920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - NUTS, GROUND - NUTS AND OTHER SEEDS,\nWHETHER OR NOT MIXED TOGETHER : OTHER, INCLUDING MIXTURES : OTHER NUTS,\nOTHERWISE PREPARED OR PRESERVED",
+        "hsn_code": "20081930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - NUTS, GROUND - NUTS AND OTHER SEEDS,\nWHETHER OR NOT MIXED TOGETHER : OTHER, INCLUDING MIXTURES : OTHER ROASTED AND\nFRIED VEGETABLE PRODUCTS",
+        "hsn_code": "20081940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - NUTS, GROUND - NUTS AND OTHER SEEDS,\nWHETHER OR NOT MIXED TOGETHER : OTHER, INCLUDING MIXTURES : OTHER",
+        "hsn_code": "20081990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED PINEAPPLES",
+        "hsn_code": "20082000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - CITRUS FRUIT : ORANGE",
+        "hsn_code": "20083010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - CITRUS FRUIT : OTHER",
+        "hsn_code": "20083090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - PEARS",
+        "hsn_code": "20084000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - APRICOTS",
+        "hsn_code": "20085000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED -CHERRIES",
+        "hsn_code": "20086000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - PEACHES, INCLUDING NECTARINES",
+        "hsn_code": "20087000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - STRAWBERRIES",
+        "hsn_code": "20088000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - - OTHER, INCLUDING MIXTURES OTHER THAN THOSE\nOF SUB-HEADING 2008 19 : PALM HEARTS",
+        "hsn_code": "20089100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OF PLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER OR SPIRIT,\nNOT ELSEWHERE SPECIFIED OR INCLUDED-Other, including mixtures other than those of sub\nheading 2008 19 --CRANBERRIES(VACCINIUM MACROCARPON, VACCINIUM OXYCOCCOS\nVACCINIUM VITIS-IDAEA)",
+        "hsn_code": "20089300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OF PLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER OR SPIRIT,\nNOT ELSEWHERE SPECIFIED OR INCLUDED-Other, including mixtures other than those of sub\nheading 2008 19 --MIXTURES",
+        "hsn_code": "20089700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - - OTHER, INCLUDING MIXTURES OTHER THAN THOSE\nOF SUB-HEADING 2008 19 : OTHER : SQUASH : MANGO",
+        "hsn_code": "20089911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - - OTHER, INCLUDING MIXTURES OTHER THAN THOSE\nOF SUB-HEADING 2008 19 : OTHER : SQUASH : LEMON",
+        "hsn_code": "20089912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - - OTHER, INCLUDING MIXTURES OTHER THAN THOSE\nOF SUB-HEADING 2008 19 : OTHER : SQUASH : ORANGE",
+        "hsn_code": "20089913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - - OTHER, INCLUDING MIXTURES OTHER THAN THOSE\nOF SUB-HEADING 2008 19 : OTHER : SQUASH : PINEAPPLE",
+        "hsn_code": "20089914",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED -- OTHER, INCLUDING MIXTURES OTHER THAN THOSE\nOF SUB-HEADIN",
+        "hsn_code": "20089919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - - OTHER, INCLUDING MIXTURES OTHER THAN THOSE\nOF SUB-HEADING 2008 19 : OTHER : OTHER : FRUIT COCKTAIL",
+        "hsn_code": "20089991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - - OTHER, INCLUDING MIXTURES OTHER THAN THOSE\nOF SUB-HEADING 2008 19 : OTHER : OTHER : GRAPES",
+        "hsn_code": "20089992",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - - OTHER, INCLUDING MIXTURES OTHER THAN THOSE\nOF SUB-HEADING 2008 19 : OTHER : OTHER : APPLES",
+        "hsn_code": "20089993",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED - - OTHER, INCLUDING MIXTURES OTHER THAN THOSE\nOF SUB-HEADING 2008 19 : OTHER : OTHER : GUAVA",
+        "hsn_code": "20089994",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OFPLANTS, OTHERWISE PREPARED OR PRESERVED,\nWHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER OR SPIRIT,\nNOTELSEWHERE SPECIFIED OR INCLUDED -- OTHER, INCLUDING MIXTURES OTHER THAN THOSE\nOF SUB-HEADIN",
+        "hsn_code": "20089999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, NUTS AND OTHER EDIBLE PARTS OF PLANTS, OTHERWISE PREPARED OR PRESERVED, WHETHER OR NOT CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER OR SPIRIT, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "2008",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "JAMS, FRUIT JELLIES, MARMALADES, FRUIT ORNUT PUREE AND FRUIT OR NUT PASTES,\nOBTAINEDBY COOKING, WHETHER OR NOT CONTAININGADDED SUGAR OR OTHER SWEETENING\nMATTER HOMOGENISED PREPARATIONS",
+        "hsn_code": "20071000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "JAMS, FRUIT JELLIES, MARMALADES, FRUIT ORNUT PUREE AND FRUIT OR NUT PASTES,\nOBTAINEDBY COOKING, WHETHER OR NOT CONTAININGADDED SUGAR OR OTHER SWEETENING\nMATTER - OTHER : CITRUS FRUIT",
+        "hsn_code": "20079100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "JAMS, FRUIT JELLIES, MARMALADES, FRUIT ORNUT PUREE AND FRUIT OR NUT PASTES,\nOBTAINEDBY COOKING, WHETHER OR NOT CONTAININGADDED SUGAR OR OTHER SWEETENING\nMATTER - OTHER : OTHER : MANGO",
+        "hsn_code": "20079910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "JAMS, FRUIT JELLIES, MARMALADES, FRUIT ORNUT PUREE AND FRUIT OR NUT PASTES,\nOBTAINEDBY COOKING, WHETHER OR NOT CONTAININGADDED SUGAR OR OTHER SWEETENING\nMATTER - OTHER : OTHER : GUAVA",
+        "hsn_code": "20079920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "JAMS, FRUIT JELLIES, MARMALADES, FRUIT ORNUT PUREE AND FRUIT OR NUT PASTES,\nOBTAINEDBY COOKING, WHETHER OR NOT CONTAININGADDED SUGAR OR OTHER SWEETENING\nMATTER - OTHER : OTHER : PINE APPLE",
+        "hsn_code": "20079930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "JAMS, FRUIT JELLIES, MARMALADES, FRUIT ORNUT PUREE AND FRUIT OR NUT PASTES,\nOBTAINEDBY COOKING, WHETHER OR NOT CONTAININGADDED SUGAR OR OTHER SWEETENING\nMATTER - OTHER : OTHER : APPLE",
+        "hsn_code": "20079940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "JAMS, FRUIT JELLIES, MARMALADES, FRUIT ORNUT PUREE AND FRUIT OR NUT PASTES,\nOBTAINEDBY COOKING, WHETHER OR NOT CONTAININGADDED SUGAR OR OTHER SWEETENING\nMATTER - OTHER : OTHER : OTHER",
+        "hsn_code": "20079990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "JAMS, FRUIT JELLIES, MARMALADE, FRUIT OR NUT PUREE AND FRUIT OR NUT PASTES, OBTAINED BY COOKING, WHETHER OR NOT CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER",
+        "hsn_code": "2007",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES, FRUITS, NUTS, FRUIT-PEEL AND OTHER PARTS OF PLANTS, PRESERVED BY\nSUGAR(DRAINED, GLACE OR CRYSTALLISED)",
+        "hsn_code": "20060000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES PREPARED OR PRESERVEDOTHERWISE THAN BY VINEGAR OR ACETIC ACID,\nNOT FROZEN, OTHER THAN PRODUCTS OF HEADING 2006 HOMOGENISED VEGETABLES",
+        "hsn_code": "20051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES PREPARED OR PRESERVEDOTHERWISE THAN BY VINEGAR OR ACETIC ACID,\nNOT FROZEN, OTHER THAN PRODUCTS OF HEADING 2006 POTATOES",
+        "hsn_code": "20052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES PREPARED OR PRESERVEDOTHERWISE THAN BY VINEGAR OR ACETIC ACID,\nNOT FROZEN, OTHER THAN PRODUCTS OF HEADING 2006 PEAS (PISUM, SATIVUM)",
+        "hsn_code": "20054000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES PREPARED OR PRESERVEDOTHERWISE THAN BY VINEGAR OR ACETIC ACID,\nNOT FROZEN, OTHER THAN PRODUCTS OF HEADING 2006 - BEANS (VIGNA SPP., PHASEOLUS\nSPP.) : BEANS, SHELLED",
+        "hsn_code": "20055100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES PREPARED OR PRESERVEDOTHERWISE THAN BY VINEGAR OR ACETIC ACID,\nNOT FROZEN, OTHER THAN PRODUCTS OF HEADING 2006 - BEANS (VIGNA SPP., PHASEOLUS\nSPP.) : OTHER",
+        "hsn_code": "20055900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES PREPARED OR PRESERVEDOTHERWISE THAN BY VINEGAR OR ACETIC ACID,\nNOT FROZEN, OTHER THAN PRODUCTS OF HEADING 2006 ASPARAGUS",
+        "hsn_code": "20056000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES PREPARED OR PRESERVEDOTHERWISE THAN BY VINEGAR OR ACETIC ACID,\nNOT FROZEN, OTHER THAN PRODUCTS OF HEADING 2006 OLIVES",
+        "hsn_code": "20057000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES PREPARED OR PRESERVEDOTHERWISE THAN BY VINEGAR OR ACETIC ACID,\nNOT FROZEN, OTHER THAN PRODUCTS OF HEADING 2006 SWEET CORN (ZEA MAYS VAR.\nSACCHARATA)",
+        "hsn_code": "20058000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES PREPARED OR PRESERVEDOTHERWISE THAN BY VINEGAR OR ACETIC ACID,\nNOT FROZEN, OTHER THAN PRODUCTS OF HEADING 2006 OTHER VEGETABLES AND MIXTURES\nOF VEGETABLES",
+        "hsn_code": "20059000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES PREPARED OR PRESERVEDOTHERWISE THAN BY VINEGAR OR ACETIC ACID,\nNOT FROZEN, OTHER THAN PRODUCTS OF HEADING 2006 -OTHER VEGETABLES AND MIXTURES\nOF VEGETABLES: -- BAMBOO SHOOTS",
+        "hsn_code": "20059100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES PREPARED OR PRESERVED OTHERWISE THAN BY VINEGAR OR ACETIC ACID,\nNOT FROZEN, OTHER THAN PRODUCTS OF HEADING 2006 -OTHER VEGETABLES AND MIXTURES\nOF VEGETABLES: -- OTHER",
+        "hsn_code": "20059900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES PREPARED OR PRESERVED OTHERWISE THAN BY VINEGAR OR ACETIC ACID, NOT FROZEN, OTHER THAN PRODUCTS OF HEADING 2006",
+        "hsn_code": "2005",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES PREPARED OR PRESERVEDOTHERWISE THAN BY VINEGAR OR ACETIC ACID,\nFROZEN, OTHER THAN PRODUCTS OF HEADING 2006 POTATOES",
+        "hsn_code": "20041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES PREPARED OR PRESERVEDOTHERWISE THAN BY VINEGAR OR ACETIC ACID,\nFROZEN, OTHER THAN PRODUCTS OF HEADING 2006 OTHER VEGETABLES AND MIXTURES OF\nVEGETABLES",
+        "hsn_code": "20049000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES PREPARED OR PRESERVED OTHERWISE THAN BY VINEGAR OR ACETIC ACID, FROZEN, OTHER THAN PRODUCTS OF HEADING 2006",
+        "hsn_code": "2004",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MUSHROOMS AND TRUFFLES, PREPARED ORPRESERVED OTHERWISE THAN BY VINEGAR\nORACETIC ACID MUSHROOMS OF THE GENUS AGARICUS",
+        "hsn_code": "20031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MUSHROOMS AND TRUFFLES, PREPARED OR PRESERVED OTHERWISE THAN BY VINEGAR OR\nACETIC ACID-OTHER---TRUFFLES",
+        "hsn_code": "20039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MUSHROOMS AND TRUFFLES, PREPARED OR PRESERVED OTHERWISE THAN BY VINEGAR OR\nACETIC ACID-OTHER---OTHER",
+        "hsn_code": "20039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MUSHROOMS AND TRUFFLES, PREPARED OR PRESERVED OTHERWISE THAN BY VINEGAR OR ACETIC ACID",
+        "hsn_code": "2003",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TOMATOES PREPARED OR PRESERVED OTHERWISE THAN BY VINEGAR OR ACETIC ACID\nTOMATOES, WHOLE OR IN PIECES",
+        "hsn_code": "20021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TOMATOES PREPARED OR PRESERVED OTHERWISETHAN BY VINEGAR OR ACETIC ACID OTHER",
+        "hsn_code": "20029000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TOMATOES PREPARED OR PRESERVED OTHERWISE THAN BY VINEGAR OR ACETIC ACID",
+        "hsn_code": "2002",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES, FRUIT, NUTS AND OTHER EDIBLEPARTS OF PLANTS, PREPARED OR PRESERVEDBY\nVINEGAR OR ACETIC ACID CUCUMBERS AND GHERKINS",
+        "hsn_code": "20011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES, FRUIT, NUTS AND OTHER EDIBLEPARTS OF PLANTS, PREPARED OR PRESERVEDBY\nVINEGAR OR ACETIC ACID OTHER",
+        "hsn_code": "20019000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES, FRUIT, NUTS AND OTHER EDIBLE PARTS OF PLANTS, PREPARED OR PRESERVED BY VINEGAR OR ACETIC ACID",
+        "hsn_code": "2001",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TAPIOCA AND SUBSTITUTES THEREFOR PREPARED FROM STARCH, IN THE FORM OF FLAKES,\nGRAINS, PEARLS, SIFTINGS OR IN SIMILAR FORMS",
+        "hsn_code": "19030000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "Tapioca and substitutes therefore prepared from starch, in the form of flakes, grains, pearls, shifting or in similar forms",
+        "hsn_code": "1903",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PASTA, WHETHER OR NOT COOKED OR STUFFED (WITH MEAT OR OTHER SUBSTANCES) OR\nOTHERWISE PREPARED, SUCH AS SPAGHETTI, MACARONI, NOODLES, LASAGNE, GNOCCHI,\nRAVIOLI, CANNELLONI; COUSCOUS, WHETHER OR NOT PREPARED - UNCOOKED PASTA, NOT\nSTUFFED OR OTHERWISE PREPARED : CONTAINING EGGS",
+        "hsn_code": "19021100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PASTA, WHETHER OR NOT COOKED OR STUFFED (WITH MEAT OR OTHER SUBSTANCES) OR\nOTHERWISE PREPARED, SUCH AS SPAGHETTI, MACARONI, NOODLES, LASAGNE, GNOCCHI,\nRAVIOLI, CANNELLONI; COUSCOUS, WHETHER OR NOT PREPARED - UNCOOKED PASTA, NOT\nSTUFFED OR OTHERWISE PREPARED : OTHER",
+        "hsn_code": "19021900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PASTA, WHETHER OR NOT COOKED OR STUFFED (WITH MEAT OR OTHER SUBSTANCES) OR\nOTHERWISE PREPARED, SUCH AS SPAGHETTI, MACARONI, NOODLES, LASAGNE, GNOCCHI,\nRAVIOLI, CANNELLONI; COUSCOUS, WHETHER OR NOT PREPARED - STUFFED PASTA, WHETHER\nOR NOT COOKED OR OTHERWISE PREPARED : COOKED",
+        "hsn_code": "19022010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PASTA, WHETHER OR NOT COOKED OR STUFFED (WITH MEAT OR OTHER SUBSTANCES) OR\nOTHERWISE PREPARED, SUCH AS SPAGHETTI, MACARONI, NOODLES, LASAGNE, GNOCCHI,\nRAVIOLI, CANNELLONI; COUSCOUS, WHETHER OR NOT PREPARED - STUFFED PASTA, WHETHER\nOR NOT COOKED OR OTHERWISE PREPARED : OTHER",
+        "hsn_code": "19022090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PASTA, WHETHER OR NOT COOKED OR STUFFED (WITH MEAT OR OTHER SUBSTANCES) OR\nOTHERWISE PREPARED, SUCH AS SPAGHETTI, MACARONI, NOODLES, LASAGNE, GNOCCHI,\nRAVIOLI, CANNELLONI; COUSCOUS, WHETHER OR NOT PREPARED - OTHER PASTA : DRIED",
+        "hsn_code": "19023010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PASTA, WHETHER OR NOT COOKED OR STUFFED (WITH MEAT OR OTHER SUBSTANCES) OR\nOTHERWISE PREPARED, SUCH AS SPAGHETTI, MACARONI, NOODLES, LASAGNE, GNOCCHI,\nRAVIOLI, CANNELLONI; COUSCOUS, WHETHER OR NOT PREPARED - OTHER PASTA : OTHER",
+        "hsn_code": "19023090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PASTA, WHETHER OR NOT COOKED OR STUFFED (WITH MEAT OR OTHER SUBSTANCES) OR\nOTHERWISE PREPARED, SUCH AS SPAGHETTI, MACARONI, NOODLES, LASAGNE, GNOCCHI,\nRAVIOLI, CANNELLONI; COUSCOUS, WHETHER OR NOT PREPARED - COUSCOUS : UNPREPARED",
+        "hsn_code": "19024010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PASTA, WHETHER OR NOT COOKED OR STUFFED (WITH MEAT OR OTHER SUBSTANCES) OR\nOTHERWISE PREPARED, SUCH AS SPAGHETTI, MACARONI, NOODLES, LASAGNE, GNOCCHI,\nRAVIOLI, CANNELLONI; COUSCOUS, WHETHER OR NOT PREPARED - COUSCOUS : OTHER",
+        "hsn_code": "19024090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PASTA, WHETHER OR NOT COOKED OR STUFFED (WITH MEAT OR OTHER SUBSTANCES) OR OTHERWISE PREPARED, SUCH AS SPAGHETTI, MACARONI, NOODLES, LASAGNE, GNOCCHI, RAVIOLI, CANNELLONI; COUSCOUS, WHETHER OR NOT PREPARED",
+        "hsn_code": "1902",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHOCOLATE AND OTHER FOOD PREPARATIONS CONTAINING COCOA - COCOA POWDER,\nCONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER",
+        "hsn_code": "18061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHOCOLATE AND OTHER FOOD PREPARATIONS CONTAINING COCOA - OTHER PREPARATIONS IN\nBLOCKS, SLABS OR BARS WEIGHING MORE THAN 2 KG OR IN LIQUID, PASTE, POWDER,\nGRANULAR OR OTHER BULK FORM IN CONTAINERS OR IMMEDIATE PACKINGS, OF A CONTENT\nEXCEEDING 2 KG",
+        "hsn_code": "18062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHOCOLATE AND OTHER FOOD PREPARATIONS CONTAINING COCOA - OTHER, IN BLOCKS, SLABS\nOR BARS : FILLED",
+        "hsn_code": "18063100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHOCOLATE AND OTHER FOOD PREPARATIONS CONTAINING COCOA - OTHER, IN BLOCKS, SLABS\nOR BARS : NOT FILLED",
+        "hsn_code": "18063200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHOCOLATE AND OTHER FOOD PREPARATIONS CONTAINING COCOA - OTHER : CHOCOLATE AND\nCHOCOLATE PRODUCTS",
+        "hsn_code": "18069010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHOCOLATE AND OTHER FOOD PREPARATIONS CONTAINING COCOA - OTHER : SUGAR\nCONFECTIONARY CONTAINING COCOA",
+        "hsn_code": "18069020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHOCOLATE AND OTHER FOOD PREPARATIONS CONTAINING COCOA - OTHER : SPREADS\nCONTAINING COCOA",
+        "hsn_code": "18069030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHOCOLATE AND OTHER FOOD PREPARATIONS CONTAINING COCOA - OTHER : PREPARATIONS\nCONTAINING COCOA FOR MAKING BEVERAGES",
+        "hsn_code": "18069040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHOCOLATE AND OTHER FOOD PREPARATIONS CONTAINING COCOA - OTHER : OTHER",
+        "hsn_code": "18069090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "CHOCOLATE AND OTHER FOOD PREPARATIONS CONTAINING COCOA",
+        "hsn_code": "1806",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COCOA POWDER, NOT CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER",
+        "hsn_code": "18050000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Cocoa powder, not containing added sugar or other sweetening matter",
+        "hsn_code": "1805",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COCOA BUTTER, FAT AND OIL",
+        "hsn_code": "18040000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Cocoa butter, fat and oil",
+        "hsn_code": "1804",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "COCOA PASTE, WHETHER OR NOT DEFATTED NOT DEFATTED",
+        "hsn_code": "18031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COCOA PASTE, WHETHER OR NOT DEFATTED WHOLLY OR PARTLY DEFATTED",
+        "hsn_code": "18032000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COCOA PASTE, WHETHER OR NOT DEFATTED",
+        "hsn_code": "1803",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COCOA SHELLS, HUSKS, SKINS AND OTHER COCOA WASTE",
+        "hsn_code": "18020000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "Cocoa shells, husks, skins and other cocoa waste",
+        "hsn_code": "1802",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COCOA BEANS, WHOLE OR BROKEN, RAW OR ROASTED",
+        "hsn_code": "18010000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "Cocoa beans, whole or broken, raw or roasted",
+        "hsn_code": "1801",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MOLASSES RESULTING FROM THE EXTRACTION OR REFINING OF SUGAR - CANE MOLASSES",
+        "hsn_code": "17031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOLASSES RESULTING FROM THE EXTRACTION OR REFINING OF SUGAR - OTHER : MOLASSES,\nEDIBLE",
+        "hsn_code": "17039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOLASSES RESULTING FROM THE EXTRACTION OR REFINING OF SUGAR - OTHER : MOLASSES,\nEDIBLE",
+        "hsn_code": "17039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "MOLASSES RESULTING FROM THE EXTRACTION OR REFINING OF SUGAR",
+        "hsn_code": "1703",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 28
+            }
+        ]
+    },
+    {
+        "description": "CANE OR BEET SUGAR AND CHEMICALLY PURE SUCROSE, IN SOLID FORM - RAW SUGAR NOT\nCONTAINING ADDED FLAVOURING OR COLOURING MATTER : BEET SUGAR",
+        "hsn_code": "17011200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CANE OR BEET SUGAR AND CHEMICALLY PURE SUCROSE, IN SOLID FORM - Raw sugar not\ncontaining added flavouring or colouring matter --CANE SUGAR SPECIFIED IN SUB-HEADING\nNOTE 2 TO THIS CHAPTER---CANE JAGGERY",
+        "hsn_code": "17011310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CANE OR BEET SUGAR AND CHEMICALLY PURE SUCROSE, IN SOLID FORM - Raw sugar not\ncontaining added flavouring or colouring matter --CANE SUGAR SPECIFIED IN SUB-HEADING\nNOTE 2 TO THIS CHAPTER---KHANDSARI SUGAR",
+        "hsn_code": "17011320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CANE OR BEET SUGAR AND CHEMICALLY PURE SUCROSE, IN SOLID FORM - Raw sugar not\ncontaining added flavouring or colouring matter --CANE SUGAR SPECIFIED IN SUB-HEADING\nNOTE 2 TO THIS CHAPTER---OTHER",
+        "hsn_code": "17011390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CANE OR BEET SUGAR AND CHEMICALLY PURE SUCROSE, IN SOLID FORM - Raw sugar not\ncontaining added flavouring or colouring matter --OTHER CANE SUGAR--- CANE JAGGERY",
+        "hsn_code": "17011410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CANE OR BEET SUGAR AND CHEMICALLY PURE SUCROSE, IN SOLID FORM - Raw sugar not\ncontaining added flavouring or colouring matter --OTHER CANE SUGAR ---KHANDSARI SUGAR",
+        "hsn_code": "17011420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CANE OR BEET SUGAR AND CHEMICALLY PURE SUCROSE, IN SOLID FORM - Raw sugar not\ncontaining added flavouring or colouring mater--OTHER CANE SUGAR --- OTHER",
+        "hsn_code": "17011490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CANE OR BEET SUGAR AND CHEMICALLY PURE SUCROSE, IN SOLID FORM",
+        "hsn_code": "1701",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES, PREPARED OR PRESERVED\nCRAB",
+        "hsn_code": "16051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES, PREPARED OR PRESERVED\nSHRIMPS - SHRIMPS AND PRAWNS --NOT IN AIRTIGHT CONTAINER",
+        "hsn_code": "16052100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES, PREPARED OR PRESERVED\nSHRIMPS AND PRAWNS-SHRIMPTS AND PRAWNS -- OTHER",
+        "hsn_code": "16052900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES, PREPARED OR PRESERVED\nLOBSTER",
+        "hsn_code": "16053000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES, PREPARED OR PRESERVED\nOTHER CRUSTACEANS",
+        "hsn_code": "16054000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES, PREPARED OR PRESERVED\nMOLLUSCS -- OYSTERS",
+        "hsn_code": "16055100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES, PREPARED OR PRESERVED\nMOLLUSCS-- SCALLOPS, INCLUDING QUEEN SCALLOPS",
+        "hsn_code": "16055200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES, PREPARED OR PRESERVED\nMOLLUSCS--MUSSELS",
+        "hsn_code": "16055300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES, PREPARED OR PRESERVED\nMOLLUSCS--CUTTLE FISH AND SQUID",
+        "hsn_code": "16055400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES, PREPARED OR PRESERVED\nMOLLUSCS- OCTOPUS",
+        "hsn_code": "16055500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES, PREPARED OR PRESERVED\nMOLLUSCS-CLAMS, COCKLES AND ARKSHELLS",
+        "hsn_code": "16055600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES, PREPARED OR PRESERVED\nMOLLUSCS--ABALONE",
+        "hsn_code": "16055700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES, PREPARED OR PRESERVED\nMOLLUSCS--SNAILS, OTHER THAN SEA SNAILS",
+        "hsn_code": "16055800",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES, PREPARED OR PRESERVED\nMOLLUSCS--OTHER",
+        "hsn_code": "16055900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES, PREPARED OR PRESERVED\nOTHER AQUATIC INVERTEBRATES--SEA CUCUMBERS",
+        "hsn_code": "16056100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES, PREPARED OR PRESERVED\nOTHER AQUATIC INVERTEBRATES-- SEA URCHINS",
+        "hsn_code": "16056200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES, PREPARED OR PRESERVED\nOTHER AQUATIC INVERTEBRATES--JELLYFISH",
+        "hsn_code": "16056300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES, PREPARED OR PRESERVED\nOTHER AQUATIC INVERTEBRATES--OTHER",
+        "hsn_code": "16056900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES, PREPARED OR PRESERVED",
+        "hsn_code": "1605",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PREPARED OR PRESERVED FISH; CAVIAR AND CAVIAR SUBSTITUTES PREPARED FROM FISH EGGS\nFISH, WHOLE OR IN PIECES, BUT NOT MINCED : SALMON",
+        "hsn_code": "16041100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PREPARED OR PRESERVED FISH; CAVIAR AND CAVIAR SUBSTITUTES PREPARED FROM FISH EGGS\nFISH, WHOLE OR IN PIECES, BUT NOT MINCED : HERRINGS : PICKLED",
+        "hsn_code": "16041210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PREPARED OR PRESERVED FISH; CAVIAR AND CAVIAR SUBSTITUTES PREPARED FROM FISH EGGS\nFISH, WHOLE OR IN PIECES, BUT NOT MINCED : HERRINGS : OTHER",
+        "hsn_code": "16041290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PREPARED OR PRESERVED FISH; CAVIAR AND CAVIAR SUBSTITUTES PREPARED FROM FISH EGGS\nFISH, WHOLE OR IN PIECES, BUT NOT MINCED : SARDINES, SARDINELLA AND BRISLING OR\nSPRATS : SARDINES, SARDINELLA AND BRISLING",
+        "hsn_code": "16041310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PREPARED OR PRESERVED FISH; CAVIAR AND CAVIAR SUBSTITUTES PREPARED FROM FISH EGGS\nFISH, WHOLE OR IN PIECES, BUT NOT MINCED : SARDINES, SARDINELLA AND BRISLING OR\nSPRATS : SPRATS",
+        "hsn_code": "16041320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PREPARED OR PRESERVED FISH; CAVIAR AND CAVIAR SUBSTITUTES PREPARED FROM FISH EGGS\nFISH, WHOLE OR IN PIECES, BUT NOT MINCED : TUNAS, SKIPJACK AND BONITO (SARDA SPP.) :\nTUNAS",
+        "hsn_code": "16041410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PREPARED OR PRESERVED FISH; CAVIAR AND CAVIAR SUBSTITUTES PREPARED FROM FISH EGGS\nFISH, WHOLE OR IN PIECES, BUT NOT MINCED : TUNAS, SKIPJACK AND BONITO (SARDA SPP.) :\nOTHER",
+        "hsn_code": "16041490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PREPARED OR PRESERVED FISH; CAVIAR AND CAVIAR SUBSTITUTES PREPARED FROM FISH EGGS\nFISH, WHOLE OR IN PIECES, BUT NOT MINCED : MACKEREL",
+        "hsn_code": "16041500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PREPARED OR PRESERVED FISH; CAVIAR AND CAVIAR SUBSTITUTES PREPARED FROM FISH EGGS\nFISH, WHOLE OR IN PIECES, BUT NOT MINCED : ANCHOVIES",
+        "hsn_code": "16041600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PREPARED OR PRESERVED FISH; CAVIAR AND CAVIAR SUBSTITUTES PREPARED FROM FISH EGGS--\nEels",
+        "hsn_code": "16041700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PREPARED OR PRESERVED FISH; CAVIAR AND CAVIAR SUBSTITUTES PREPARED FROM FISH EGGS\nFISH, WHOLE OR IN PIECES, BUT NOT MINCED : OTHER",
+        "hsn_code": "16041900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PREPARED OR PRESERVED FISH; CAVIAR AND CAVIAR SUBSTITUTES PREPARED FROM FISH EGGS\nOTHER PREPARED OR PRESERVED FISH",
+        "hsn_code": "16042000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PREPARED OR PRESERVED FISH; CAVIAR AND CAVIAR SUBSTITUTES PREPARED FROM FISH EGGS\nCAVIAR AND CAVIAR SUBSTITUTES -- CAVIAR",
+        "hsn_code": "16043100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PREPARED OR PRESERVED FISH; CAVIAR AND CAVIAR SUBSTITUTES PREPARED FROM FISH EGGS\nCAVIAR AND CAVIAR SUBSTITUTES -- CAVIAR SUBSTITUTES",
+        "hsn_code": "16043200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PREPARED OR PRESERVED FISH; CAVIAR AND CAVIAR SUBSTITUTES PREPARED FROM FISH EGGS",
+        "hsn_code": "1604",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "EXTRACTS AND JUICES OF MEAT, FISH OR CRUSTACEANS, MOLLUSCS OR OTHER AQUATIC\nINVERTEBRATES EXTRACTS AND JUICES OF MEAT, FISH OR CRUSTACEANS, MOLLUSCS OR OTHER\nAQUATIC INVERTEBRATES : EXTRACTS AND JUICES OF MEAT",
+        "hsn_code": "16030010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "EXTRACTS AND JUICES OF MEAT, FISH OR CRUSTACEANS, MOLLUSCS OR OTHER AQUATIC\nINVERTEBRATES EXTRACTS AND JUICES OF MEAT, FISH OR CRUSTACEANS, MOLLUSCS OR OTHER\nAQUATIC INVERTEBRATES : EXTRACTS OF FISH",
+        "hsn_code": "16030020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "EXTRACTS AND JUICES OF MEAT, FISH OR CRUSTACEANS, MOLLUSCS OR OTHER AQUATIC\nINVERTEBRATESEXTRACTS AND JUICES OF MEAT, FISH OR CRUSTACEANS, MOLLUSCS OR OTHER\nAQUATIC INVERTEBRATES : OTHER",
+        "hsn_code": "16030090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "EXTRACTS AND JUICES OF MEAT, FISH OR CRUSTACEANS, MOLLUSCS OR OTHER AQUATIC INVERTEBRATES",
+        "hsn_code": "1603",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER PREPARED OR PRESERVED MEAT, MEAT OFFAL OR BLOOD HOMOGENISED\nPREPARATIONS",
+        "hsn_code": "16021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER PREPARED OR PRESERVED MEAT, MEAT OFFAL OR BLOOD OF LIVER OF ANY ANIMAL",
+        "hsn_code": "16022000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER PREPARED OR PRESERVED MEAT, MEAT OFFAL OR BLOOD OF POULTRY OF HEADING\n0105 : OF TURKEYS",
+        "hsn_code": "16023100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER PREPARED OR PRESERVED MEAT, MEAT OFFAL OR BLOOD OF POULTRY OF HEADING\n0105 : OF FOWLS OF THE SPECIES GALLUS DOMESTICUS",
+        "hsn_code": "16023200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER PREPARED OR PRESERVED MEAT, MEAT OFFAL OR BLOOD OF POULTRY OF HEADING\n0105 : OTHER",
+        "hsn_code": "16023900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER PREPARED OR PRESERVED MEAT, MEAT OFFAL OR BLOOD OF SWINE : HAMS AND CUTS\nTHEREOF",
+        "hsn_code": "16024100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER PREPARED OR PRESERVED MEAT, MEAT OFFAL OR BLOOD OF SWINE : SHOULDERS AND\nCUTS THEREOF",
+        "hsn_code": "16024200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER PREPARED OR PRESERVED MEAT, MEAT OFFAL OR BLOOD OF SWINE : OTHER,\nINCLUDING MIXTURES",
+        "hsn_code": "16024900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER PREPARED OR PRESERVED MEAT, MEAT OFFAL OR BLOOD OF BOVINE ANIMALS",
+        "hsn_code": "16025000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER PREPARED OR PRESERVED MEAT, MEAT OFFAL OR BLOOD OTHER, INCLUDING\nPREPARATIONS OF BLOOD OF ANY ANIMAL",
+        "hsn_code": "16029000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER PREPARED OR PRESERVED MEAT, MEAT OFFAL OR BLOOD",
+        "hsn_code": "1602",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SAUSAGES AND SIMILAR PRODUCTS, OF MEAT,",
+        "hsn_code": "16010000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Sausages and similar products, of meat, meat offal or blood; food preparations based on these products",
+        "hsn_code": "1601",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DEGRAS : RESIDUES RESULTING FROM THE TREATMENT OF FATTY SUBSTANCES OR ANIMAL OR\nVEGETABLE WAXES DEGRAS : RESIDUES RESULTING FROM THE TREATMENT OF FATTY\nSUBSTANCES OR ANIMAL OR VEGETABLE WAXES : DEGRAS",
+        "hsn_code": "15220010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DEGRAS : RESIDUES RESULTING FROM THE TREATMENT OF FATTY SUBSTANCES OR ANIMAL OR\nVEGETABLE WAXES DEGRAS : RESIDUES RESULTING FROM THE TREATMENT OF FATTY\nSUBSTANCES OR ANIMAL OR VEGETABLE WAXES : SOAP STOCKS",
+        "hsn_code": "15220020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DEGRAS : RESIDUES RESULTING FROM THE TREATMENT OF FATTY SUBSTANCES OR ANIMAL OR\nVEGETABLE WAXES DEGRAS : RESIDUES RESULTING FROM THE TREATMENT OF FATTY\nSUBSTANCES OR ANIMAL OR VEGETABLE WAXES : OTHER",
+        "hsn_code": "15220090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "DEGRAS: RESIDUES RESULTING FROM THE TREATMENT OF FATTY SUBSTANCES OR ANIMAL OR VEGETABLE WAXES",
+        "hsn_code": "1522",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE WAXES (OTHER THAN TRIGLYCERIDES), BEESWAX, OTHER INSECT WAXES AND\nSPERMACETI, WHETHER OR NOT REFINED OR COLOURED VEGETABLE WAXES : CARNAUBA\nWAXES : EDIBLE WAX FOR WAXING FRESH FRUITS AND VEGETABLES",
+        "hsn_code": "15211011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE WAXES (OTHER THAN TRIGLYCERIDES), BEESWAX, OTHER INSECT WAXES AND\nSPERMACETI, WHETHER OR NOT REFINED OR COLOURED VEGETABLE WAXES : CARNAUBA\nWAXES : OTHER",
+        "hsn_code": "15211019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE WAXES (OTHER THAN TRIGLYCERIDES), BEESWAX, OTHER INSECT WAXES AND\nSPERMACETI, WHETHER OR NOT REFINED OR COLOURED VEGETABLE WAXES : OTHER",
+        "hsn_code": "15211090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE WAXES (OTHER THAN TRIGLYCERIDES), BEESWAX, OTHER INSECT WAXES AND\nSPERMACETI, WHETHER OR NOT REFINED OR COLOURED OTHER : BEEWAX WHETHER OR NOT\nCOLOURED",
+        "hsn_code": "15219010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE WAXES (OTHER THAN TRIGLYCERIDES), BEESWAX, OTHER INSECT WAXES AND\nSPERMACETI, WHETHER OR NOT REFINED OR COLOURED OTHER : SHELLAC WAX WHETHER OR\nNOT COLOURED",
+        "hsn_code": "15219020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE WAXES (OTHER THAN TRIGLYCERIDES), BEESWAX, OTHER INSECT WAXES AND\nSPERMACETI, WHETHER OR NOT REFINED OR COLOURED OTHER : OTHER",
+        "hsn_code": "15219090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE WAXES (OTHER THAN TRIGLYCERIDES),BEESWAX, OTHER INSECT WAXES AND SPERMACETI, WHETHER OR NOT REFINED OR COLOURED",
+        "hsn_code": "1521",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FATS AND OILS AND THEIR FRACTIONS, BOILED, OXIDISED,\nDEHYDRATED, SULPHURISED, BLOWN, POLYMERISED BY HEAT IN VACUUM OR IN INERT GAS OR\nOTHERWISE CHEMICALLY MODIFIED, EXCLUDING THOSE OF HEADING 1516 ; INEDIBLE MIXTURES\nOR PREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR OF FRACTIONS OF DIFFERENT\nFATS OR OILS OF THIS CHAPTER, NOT ELSEWHERE SPECIFIED OR INCLUDED ANIMAL OR\nVEGETABLE FATS AND OILS AND THEIR FRACTIONS, BOILED, OXIDIZED, DEHYDRATED,\nSULPHURISED, BLOWN, POLYMERIZED BY HEAT IN VACUUM OR IN INERT GAS OR OTHERWISE\nCHEMICALLY MODIFIED, EXCLUDING THOSE OF HEADING 1516; INEDIBLE MIXTURES OR\nPREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR OF FRACTIONS OF DIFFERENT FATS\nOR OILS OF THIS CHAPTER, NOT ELSEWHERE SPECIFIED OR INCLUDED : LINSEED OIL : EDIBLE\nGRADE",
+        "hsn_code": "15180011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FATS AND OILS AND THEIR FRACTIONS, BOILED, OXIDISED,\nDEHYDRATED, SULPHURISED, BLOWN, POLYMERISED BY HEAT IN VACUUM OR IN INERT GAS OR\nOTHERWISE CHEMICALLY MODIFIED, EXCLUDING THOSE OF HEADING 1516 ; INEDIBLE MIXTURES\nOR PREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR OF FRACTIONS OF DIFFERENT\nFATS OR OILS OF THIS CHAPTER, NOT ELSEWHERE SPECIFIED OR INCLUDED ANIMAL OR\nVEGETABLE FATS AND OILS AND THEIR FRACTIONS, BOILED, OXIDIZED, DEHYDRATED,\nSULPHURISED, BLOWN, POLYMERIZED BY HEAT IN VACUUM OR IN INERT GAS OR OTHERWISE\nCHEMICALLY MODIFIED, EXCLUDING THOSE OF HEADING 1516; INEDIBLE MIXTURES OR\nPREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR OF FRACTIONS OF DIFFERENT FATS\nOR OILS OF THIS CHAPTER, NOT ELSEWHERE SPECIFIED OR INCLUDED : LINSEED OIL : OTHER",
+        "hsn_code": "15180019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FATS AND OILS AND THEIR FRACTIONS, BOILED, OXIDISED,\nDEHYDRATED, SULPHURISED, BLOWN, POLYMERISED BY HEAT IN VACUUM OR IN INERT GAS OR\nOTHERWISE CHEMICALLY MODIFIED, EXCLUDING THOSE OF HEADING 1516 ; INEDIBLE MIXTURES\nOR PREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR OF FRACTIONS OF DIFFERENT\nFATS OR OILS OF THIS CHAPTER, NOT ELSEWHERE SPECIFIED OR INCLUDED ? ANIMAL OR\nVEGETABLE FATS AND OILS AND THEIR FRACTIONS, BOILED, OXIDIZED, DEHYDRATED,\nSULPHURISED, BLOWN, POLYMERIZED BY HEAT IN VACUUM OR IN INERT GAS OR OTHERWISE\nCHEMICALLY MODIFIED, EXCLUDING THOSE OF HEADING 1516; INEDIBLE MIXTURES OR\nPREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR OF FRACTIONS OF DIFFERENT FATS\nOR OILS OF THIS CHAPTER, NOT ELSEWHERE SPECIFIED OR INCLUDED : CASTOR OIL,\nDEHYDRATED : EDIBLE GRADE",
+        "hsn_code": "15180021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FATS AND OILS AND THEIR FRACTIONS, BOILED, OXIDISED,\nDEHYDRATED, SULPHURISED, BLOWN, POLYMERISED BY HEAT IN VACUUM OR IN INERT GAS OR\nOTHERWISE CHEMICALLY MODIFIED, EXCLUDING THOSE OF HEADING 1516 ; INEDIBLE MIXTURES\nOR PREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR OF FRACTIONS OF DIFFERENT\nFATS OR OILS OF THIS CHAPTER, NOT ELSEWHERE SPECIFIED OR INCLUDED ? ANIMAL OR\nVEGETABLE FATS AND OILS AND THEIR FRACTIONS, BOILED, OXIDIZED, DEHYDRATED,\nSULPHURISED, BLOWN, POLYMERIZED BY HEAT IN VACUUM OR IN INERT GAS OR OTHERWISE\nCHEMICALLY MODIFIED, EXCLUDING THOSE OF HEADING 1516; INEDIBLE MIXTURES OR\nPREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR OF FRACTIONS OF DIFFERENT FATS\nOR OILS OF THIS CHAPTER, NOT ELSEWHERE SPECIFIED OR INCLUDED : CASTOR OIL,\nDEHYDRATED : OTHER",
+        "hsn_code": "15180029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FATS AND OILS AND THEIR FRACTIONS, BOILED, OXIDISED,\nDEHYDRATED, SULPHURISED, BLOWN, POLYMERISED BY HEAT IN VACUUM OR IN INERT GAS OR\nOTHERWISE CHEMICALLY MODIFIED, EXCLUDING THOSE OF HEADING 1516 ; INEDIBLE MIXTURES\nOR PREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR OF FRACTIONS OF DIFFERENT\nFATS OR OILS OF THIS CHAPTER, NOT ELSEWHERE SPECIFIED OR INCLUDED ? ANIMAL OR\nVEGETABLE FATS AND OILS AND THEIR FRACTIONS, BOILED, OXIDIZED, DEHYDRATED,\nSULPHURISED, BLOWN, POLYMERIZED BY HEAT IN VACUUM OR IN INERT GAS OR OTHERWISE\nCHEMICALLY MODIFIED, EXCLUDING THOSE OF HEADING 1516; INEDIBLE MIXTURES OR\nPREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR OF FRACTIONS OF DIFFERENT FATS\nOR OILS OF THIS CHAPTER, NOT ELSEWHERE SPECIFIED OR INCLUDED : OTHER VEGETABLE OIL\nAND ITS FATS : EDIBLE GRADE",
+        "hsn_code": "15180031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FATS AND OILS AND THEIR FRACTIONS, BOILED, OXIDISED,\nDEHYDRATED, SULPHURISED, BLOWN, POLYMERISED BY HEAT IN VACUUM OR IN INERT GAS OR\nOTHERWISE CHEMICALLY MODIFIED, EXCLUDING THOSE OF HEADING 1516 ; INEDIBLE MIXTURES\nOR PREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR OF FRACTIONS OF DIFFERENT\nFATS OR OILS OF THIS CHAPTER, NOT ELSEWHERE SPECIFIED OR INCLUDED ? ANIMAL OR\nVEGETABLE FATS AND OILS AND THEIR FRACTIONS, BOILED, OXIDIZED, DEHYDRATED,\nSULPHURISED, BLOWN, POLYMERIZED BY HEAT IN VACUUM OR IN INERT GAS OR OTHERWISE\nCHEMICALLY MODIFIED, EXCLUDING THOSE OF HEADING 1516; INEDIBLE MIXTURES OR\nPREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR OF FRACTIONS OF DIFFERENT FATS\nOR OILS OF THIS CHAPTER, NOT ELSEWHERE SPECIFIED OR INCLUDED : OTHER VEGETABLE OIL\nAND ITS FATS : OTHER",
+        "hsn_code": "15180039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FATS AND OILS AND THEIR FRACTIONS, BOILED, OXIDISED,\nDEHYDRATED, SULPHURISED, BLOWN, POLYMERISED BY HEAT IN VACUUM OR IN INERT GAS OR\nOTHERWISE CHEMICALLY MODIFIED, EXCLUDING THOSE OF HEADING 1516 ; INEDIBLE MIXTURES\nOR PREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR OF FRACTIONS OF DIFFERENT\nFATS OR OILS OF THIS CHAPTER, NOT ELSEWHERE SPECIFIED OR INCLUDED ANIMAL OR\nVEGETABLE FATS AND OILS AND THEIR FRACTIONS, BOILED, OXIDIZED, DEHYDRATED,\nSULPHURISED, BLOWN, POLYMERIZED BY HEAT IN VACUUM OR IN INERT GAS OR OTHERWISE\nCHEMICALLY MODIFIED, EXCLUDING THOSE OF HEADING 1516; INEDIBLE MIXTURES OR\nPREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR OF FRACTIONS OF DIFFERENT FATS\nOR OILS OF THIS CHAPTER, NOT ELSEWHERE SPECIFIED OR INCLUDED : OTHER",
+        "hsn_code": "15180040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED",
+        "hsn_code": "1518",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MARGARINE; EDIBLE MIXTURE OR PREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR\nOF FRACTIONS OF DIFFERENT FATS OR OILS OF THIS CHAPTER, OTHER THAN EDIBLE FATS OR\nOILS OR THEIR FRACTIONS OF HEADING 1516 OTHER : SAL FAT (PROCESSED OR REFINED)",
+        "hsn_code": "15179010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MARGARINE; EDIBLE MIXTURE OR PREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR\nOF FRACTIONS OF DIFFERENT FATS OR OILS OF THIS CHAPTER, OTHER THAN EDIBLE FATS OR\nOILS OR THEIR FRACTIONS OF HEADING 1516 OTHER : IMITATION LARD OF ANIMAL ORIGIN",
+        "hsn_code": "15179030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MARGARINE; EDIBLE MIXTURE OR PREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR\nOF FRACTIONS OF DIFFERENT FATS OR OILS OF THIS CHAPTER, OTHER THAN EDIBLE FATS OR\nOILS OR THEIR FRACTIONS OF HEADING 1516 OTHER : IMITATION LARD OF VEGETABLE ORIGIN",
+        "hsn_code": "15179040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MARGARINE; EDIBLE MIXTURE OR PREPARATIONS OF ANIMAL OR VEGETABLE FATS OR OILS OR\nOF FRACTIONS OF DIFFERENT FATS OR OILS OF THIS CHAPTER, OTHER THAN EDIBLE FATS OR\nOILS OR THEIR FRACTIONS OF HEADING 1516 OTHER : OTHER",
+        "hsn_code": "15179090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL FATS AND OILS AND THEIR FRACTIONS",
+        "hsn_code": "15161000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FATS AND OILS AND THEIR FRACTIONS, PARTLY OR WHOLLY\nHYDROGENATED, INTER-ESTERIFIED, RE-ESTERIFIED OR ELAIDINISED, WHETHER OR NOT\nREFINED, BUT NOT FURTHER PREPARED VEGETABLE FATS AND OILS AND THEIR FRACTIONS :\nCOTTON SEED OIL : EDIBLE GRADE",
+        "hsn_code": "15162011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FATS AND OILS AND THEIR FRACTIONS, PARTLY OR WHOLLY\nHYDROGENATED, INTER-ESTERIFIED, RE-ESTERIFIED OR ELAIDINISED, WHETHER OR NOT\nREFINED, BUT NOT FURTHER PREPARED VEGETABLE FATS AND OILS AND THEIR FRACTIONS :\nCOTTON SEED OIL : OTHER",
+        "hsn_code": "15162019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FATS AND OILS AND THEIR FRACTIONS, PARTLY OR WHOLLY\nHYDROGENATED, INTER-ESTERIFIED, RE-ESTERIFIED OR ELAIDINISED, WHETHER OR NOT\nREFINED, BUT NOT FURTHER PREPARED VEGETABLE FATS AND OILS AND THEIR FRACTIONS :\nGROUNDNUT OIL : EDIBLE GRADE",
+        "hsn_code": "15162021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FATS AND OILS AND THEIR FRACTIONS, PARTLY OR WHOLLY\nHYDROGENATED, INTER-ESTERIFIED, RE-ESTERIFIED OR ELAIDINISED, WHETHER OR NOT\nREFINED, BUT NOT FURTHER PREPARED VEGETABLE FATS AND OILS AND THEIR FRACTIONS :\nGROUNDNUT OIL : OTHER",
+        "hsn_code": "15162029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FATS AND OILS AND THEIR FRACTIONS, PARTLY OR WHOLLY\nHYDROGENATED, INTER-ESTERIFIED, RE-ESTERIFIED OR ELAIDINISED, WHETHER OR NOT\nREFINED, BUT NOT FURTHER PREPARED VEGETABLE FATS AND OILS AND THEIR FRACTIONS :\nHYDROGENATED CASTOR OIL (OPAL-WAX) : EDIBLE GRADE",
+        "hsn_code": "15162031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FATS AND OILS AND THEIR FRACTIONS, PARTLY OR WHOLLY\nHYDROGENATED, INTER-ESTERIFIED, RE-ESTERIFIED OR ELAIDINISED, WHETHER OR NOT\nREFINED, BUT NOT FURTHER PREPARED VEGETABLE FATS AND OILS AND THEIR FRACTIONS :\nHYDROGENATED CASTOR OIL (OPAL-WAX) : OTHER",
+        "hsn_code": "15162039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FATS AND OILS AND THEIR FRACTIONS, PARTLY OR WHOLLY\nHYDROGENATED, INTER-ESTERIFIED, RE-ESTERIFIED OR ELAIDINISED, WHETHER OR NOT\nREFINED, BUT NOT FURTHER PREPARED VEGETABLE FATS AND OILS AND THEIR FRACTIONS :\nOTHER : EDIBLE GRADE",
+        "hsn_code": "15162091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FATS AND OILS AND THEIR FRACTIONS, PARTLY OR WHOLLY\nHYDROGENATED, INTER-ESTERIFIED, RE-ESTERIFIED OR ELAIDINISED, WHETHER OR NOT\nREFINED, BUT NOT FURTHER PREPARED VEGETABLE FATS AND OILS AND THEIR FRACTIONS :\nOTHER : OTHER",
+        "hsn_code": "15162099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS,\nWHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED LINSEED OIL AND ITS FRACTIONS :\nCRUDE OIL",
+        "hsn_code": "15151100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS,\nWHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED LINSEED OIL AND ITS FRACTIONS :\nOTHER : EDIBLE GRADE",
+        "hsn_code": "15151910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS,\nWHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED LINSEED OIL AND ITS FRACTIONS :\nOTHER : OTHER",
+        "hsn_code": "15151990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS,\nWHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED MAIZE (CORN) OIL AND ITS\nFRACTIONS : CRUDE OIL",
+        "hsn_code": "15152100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS,\nWHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED MAIZE (CORN) OIL AND ITS\nFRACTIONS : OTHER : EDIBLE GRADE",
+        "hsn_code": "15152910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS,\nWHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED MAIZE (CORN) OIL AND ITS\nFRACTIONS : OTHER : OTHER",
+        "hsn_code": "15152990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS,\nWHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED CASTOR OIL AND ITS FRACTIONS :\nEDIBLE GRADE",
+        "hsn_code": "15153010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS,\nWHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED CASTOR OIL AND ITS FRACTIONS :\nOTHER",
+        "hsn_code": "15153090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS,\nWHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED - TUNG OIL AND ITS FRACTIONS",
+        "hsn_code": "15154000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS,\nWHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED SEASAME OIL AND ITS FRACTIONS\n: CRUDE OIL",
+        "hsn_code": "15155010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS,\nWHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED SEASAME OIL AND ITS FRACTIONS\n: OTHER : EDIBLE GRADE",
+        "hsn_code": "15155091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS,\nWHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED SEASAME OIL AND ITS FRACTIONS\n: OTHER : OTHER",
+        "hsn_code": "15155099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS,\nWHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED OTHER : FIXED VEGETABLE OILS,\nNAMELY THE FOLLOWING : CHUL MOOGRA OIL, MAWRA OIL, KOKAM OIL,TOBACCO SEED OIL,\nSAL OIL",
+        "hsn_code": "15159010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS,\nWHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED OTHER : FIXED VEGETABLE OILS,\nNAMELY THE FOLLOWING : NEEM SEED OIL, KARANJ OIL, SILK COTTON SEED OIL,KHAKHON OIL,\nWATER MELON OIL, KUSUM OIL, RUBBERSEED OIL, DHUP OIL, UNDI OIL, MAROTI OIL, PISA\nOIL,NAHAR OIL",
+        "hsn_code": "15159020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS,\nWHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED OTHER : FIXED VEGETABLE OILS,\nNAMELY THE FOLLOWING : CARDAMOM OIL, CHILLIES OR CAPSICUM OIL, TURMERICOIL, AJWAIN\nSEED OIL, NIGER SEED OIL, GARLIC OIL",
+        "hsn_code": "15159030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS,\nWHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED OTHER : FIXED VEGETABLE OILS\nOF EDIBLE GRADE NAMELY THE FOLLOWING : MANGO KERNEL OIL, MAHUA OIL, RICE BRAN OIL",
+        "hsn_code": "15159040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS,\nWHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED OTHER : OTHER : EDIBLE GRADE",
+        "hsn_code": "15159091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS,\nWHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED OTHER : OTHER : OTHER",
+        "hsn_code": "15159099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER FIXED VEGETABLE FATS AND OILS (INCLUDING JOJOBA OIL) AND THEIR FRACTIONS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED",
+        "hsn_code": "1515",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAPE, COLZA OR MUSTARD OIL AND THEIR FRACTIONS, WHETHER OR NOT REFINED , BUT NOT\nCHEMICALLY MODIFIED LOW ERUCIC ACID RAPE OR COLZA OIL AND ITS FRACTIONS : CRUDE OIL :\nCOLZA OIL",
+        "hsn_code": "15141110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAPE, COLZA OR MUSTARD OIL AND THEIR FRACTIONS, WHETHER OR NOT REFINED , BUT NOT\nCHEMICALLY MODIFIED LOW ERUCIC ACID RAPE OR COLZA OIL AND ITS FRACTIONS : CRUDE OIL :\nRAPE OIL",
+        "hsn_code": "15141120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAPE, COLZA OR MUSTARD OIL AND THEIR FRACTIONS, WHETHER OR NOT REFINED , BUT NOT\nCHEMICALLY MODIFIED LOW ERUCIC ACID RAPE OR COLZA OIL AND ITS FRACTIONS : CRUDE OIL :\nOTHER",
+        "hsn_code": "15141190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAPE, COLZA OR MUSTARD OIL AND THEIR FRACTIONS, WHETHER OR NOT REFINED , BUT NOT\nCHEMICALLY MODIFIED LOW ERUCIC ACID RAPE OR COLZA OIL AND ITS FRACTIONS : OTHER :\nREFINED COLZA OIL OF EDIBLE GRADE",
+        "hsn_code": "15141910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAPE, COLZA OR MUSTARD OIL AND THEIR FRACTIONS, WHETHER OR NOT REFINED , BUT NOT\nCHEMICALLY MODIFIED LOW ERUCIC ACID RAPE OR COLZA OIL AND ITS FRACTIONS : OTHER :\nREFINED RAPESEED OIL OF EDIBLE GRADE",
+        "hsn_code": "15141920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAPE, COLZA OR MUSTARD OIL AND THEIR FRACTIONS, WHETHER OR NOT REFINED , BUT NOT\nCHEMICALLY MODIFIED LOW ERUCIC ACID RAPE OR COLZA OIL AND ITS FRACTIONS : OTHER :\nOTHER",
+        "hsn_code": "15141990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAPE, COLZA OR MUSTARD OIL AND THEIR FRACTIONS, WHETHER OR NOT REFINED , BUT NOT\nCHEMICALLY MODIFIED OTHER : CRUDE OIL : COLZA OIL",
+        "hsn_code": "15149110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAPE, COLZA OR MUSTARD OIL AND THEIR FRACTIONS, WHETHER OR NOT REFINED , BUT NOT\nCHEMICALLY MODIFIED OTHER : CRUDE OIL : MUSTARD OIL",
+        "hsn_code": "15149120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAPE, COLZA OR MUSTARD OIL AND THEIR FRACTIONS, WHETHER OR NOT REFINED , BUT NOT\nCHEMICALLY MODIFIED OTHER : CRUDE OIL : RAPESEED OIL",
+        "hsn_code": "15149190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAPE, COLZA OR MUSTARD OIL AND THEIR FRACTIONS, WHETHER OR NOT REFINED , BUT NOT\nCHEMICALLY MODIFIED OTHER : OTHER : REFINED COLZA OIL OF EDIBLE GRADE",
+        "hsn_code": "15149910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAPE, COLZA OR MUSTARD OIL AND THEIR FRACTIONS, WHETHER OR NOT REFINED , BUT NOT\nCHEMICALLY MODIFIED OTHER : OTHER : REFINED MUSTARD OIL OF EDIBLE GRADE",
+        "hsn_code": "15149920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAPE, COLZA OR MUSTARD OIL AND THEIR FRACTIONS, WHETHER OR NOT REFINED , BUT NOT\nCHEMICALLY MODIFIED OTHER : OTHER : REFINED RAPESEED OIL OF EDIBLE GRADE",
+        "hsn_code": "15149930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAPE, COLZA OR MUSTARD OIL AND THEIR FRACTIONS, WHETHER OR NOT REFINED , BUT NOT\nCHEMICALLY MODIFIED OTHER : OTHER : OTHER",
+        "hsn_code": "15149990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "RAPE, COLZA OR MUSTARD OIL AND ITS FRACTIONS THEREOF, WHETHER OR NOT REFINED , BUT NOT CHEMICALLY MODIFIED",
+        "hsn_code": "1514",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COCONUT (COPRA), PALM KERNEL OR BABASSU OIL AND FRACTIONS THEREOF, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED COCONUT (COPRA) OIL AND ITS FRACTIONS :\nCRUDE OIL",
+        "hsn_code": "15131100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COCONUT (COPRA), PALM KERNEL OR BABASSU OIL AND FRACTIONS THEREOF, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED COCONUT (COPRA) OIL AND ITS FRACTIONS :\nOTHER",
+        "hsn_code": "15131900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COCONUT (COPRA), PALM KERNEL OR BABASSU OIL AND FRACTIONS THEREOF, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED PALM KERNEL OR BABASSU OIL AND FRACTIONS\nTHEREOF : CRUDE OIL : PALM KERNEL OIL",
+        "hsn_code": "15132110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COCONUT (COPRA), PALM KERNEL OR BABASSU OIL AND FRACTIONS THEREOF, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED PALM KERNEL OR BABASSU OIL AND FRACTIONS\nTHEREOF : CRUDE OIL : BABASSU OIL",
+        "hsn_code": "15132120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COCONUT (COPRA), PALM KERNEL OR BABASSU OIL AND FRACTIONS THEREOF, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED PALM KERNEL OR BABASSU OIL AND FRACTIONS\nTHEREOF : OTHER : PALM KERNEL OIL AND ITS FRACTIONS",
+        "hsn_code": "15132910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COCONUT (COPRA), PALM KERNEL OR BABASSU OIL AND FRACTIONS THEREOF, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED PALM KERNEL OR BABASSU OIL AND FRACTIONS\nTHEREOF : OTHER : BABASSU OIL AND ITS FRACTIONS EDIBLE GRADE",
+        "hsn_code": "15132920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COCONUT (COPRA), PALM KERNEL OR BABASSU OIL AND FRACTIONS THEREOF, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED PALM KERNEL OR BABASSU OIL AND FRACTIONS\nTHEREOF : OTHER : BABASSU OIL AND ITS FRACTIONS, OTHER THAN EDIBLE GRADE",
+        "hsn_code": "15132930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COCONUT (COPRA), PALM KERNEL OR BABASSU OIL AND FRACTIONS THEREOF, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED PALM KERNEL OR BABASSU OIL AND FRACTIONS\nTHEREOF : OTHER : OTHER",
+        "hsn_code": "15132990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COCONUT (COPRA), PALM KERNEL OR BABASSU OIL AND FRACTIONS THEREOF, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED",
+        "hsn_code": "1513",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUNFLOWER SEED, SAFFLOWER OR COTTON SEED OIL AND FRACTIONS THEREOF, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED SUNFLOWER SEED OR SAFFLOWER OIL AND\nFRACTIONS THEREOF : CRUDE OIL : SUNFLOWER SEED OIL",
+        "hsn_code": "15121110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUNFLOWER SEED, SAFFLOWER OR COTTON SEED OIL AND FRACTIONS THEREOF, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED SUNFLOWER SEED OR SAFFLOWER OIL AND\nFRACTIONS THEREOF : CRUDE OIL : SAFFLOWER SEED OIL (KARDI SEED OIL)",
+        "hsn_code": "15121120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUNFLOWER SEED, SAFFLOWER OR COTTON SEED OIL AND FRACTIONS THEREOF, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED SUNFLOWER SEED OR SAFFLOWER OIL AND\nFRACTIONS THEREOF : OTHER : SUNFLOWER OIL, EDIBLE GRADE",
+        "hsn_code": "15121910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUNFLOWER SEED, SAFFLOWER OR COTTON SEED OIL AND FRACTIONS THEREOF, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED SUNFLOWER SEED OR SAFFLOWER OIL AND\nFRACTIONS THEREOF : OTHER : SUNFLOWER OIL, NON-EDIBLE GRADE (OTHER THAN CRUDE OIL)",
+        "hsn_code": "15121920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUNFLOWER SEED, SAFFLOWER OR COTTON SEED OIL AND FRACTIONS THEREOF, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED SUNFLOWER SEED OR SAFFLOWER OIL AND\nFRACTIONS THEREOF : OTHER : SAFFOLA OIL, EDIBLE GRADE",
+        "hsn_code": "15121930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUNFLOWER SEED, SAFFLOWER OR COTTON SEED OIL AND FRACTIONS THEREOF, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED SUNFLOWER SEED OR SAFFLOWER OIL AND\nFRACTIONS THEREOF : OTHER : SAFFOLA OIL, NON-EDIBLE GRADE",
+        "hsn_code": "15121940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUNFLOWER SEED, SAFFLOWER OR COTTON SEED OIL AND FRACTIONS THEREOF, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED SUNFLOWER SEED OR SAFFLOWER OIL AND\nFRACTIONS THEREOF : OTHER : OTHER",
+        "hsn_code": "15121990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUNFLOWER SEED, SAFFLOWER OR COTTON SEED OIL AND FRACTIONS THEREOF, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED COTTON-SEED OIL AND ITS FRACTIONS : CRUDE\nOIL, WHETHER OR NOT GOSSYPOL HAS BEEN REMOVED",
+        "hsn_code": "15122100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUNFLOWER SEED, SAFFLOWER OR COTTON SEED OIL AND FRACTIONS THEREOF, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED COTTON-SEED OIL AND ITS FRACTIONS : OTHER :\nEDIBLE GRADE",
+        "hsn_code": "15122910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUNFLOWER SEED, SAFFLOWER OR COTTON SEED OIL AND FRACTIONS THEREOF, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED COTTON-SEED OIL AND ITS FRACTIONS : OTHER :\nOTHER",
+        "hsn_code": "15122990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SUNFLOWER SEED, SAFFLOWER OR COTTON SEED OIL AND THEIR FRACTIONS THEREOF, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED",
+        "hsn_code": "1512",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PALM OIL AND ITS FRACTIONS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED\nCRUDE OIL",
+        "hsn_code": "15111000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PALM OIL AND ITS FRACTIONS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED\nOTHER : REFINED BLEACHED DEODORISED PALM OIL",
+        "hsn_code": "15119010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PALM OIL AND ITS FRACTIONS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED\nOTHER : REFINED BLEACHED DEODORISED PALMOLEIN",
+        "hsn_code": "15119020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PALM OIL AND ITS FRACTIONS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED\nOTHER : OTHER",
+        "hsn_code": "15119090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PALM OIL AND ITS FRACTIONS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED",
+        "hsn_code": "1511",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER OILS AND THEIR FRACTIONS OBTAINED SOLELY FROM OLIVES, WHETHER OR NOT\nREFINED, BUT NOT CHEMICALLY MODIFIED, INCLUDING BLENDS OF THESE OILS OR FRACTIONS\nWITH OILS OR FRACTIONS OF HEADING 1509OTHER OILS AND THEIR FRACTIONS OBTAINED\nSOLELY FROM OLIVES, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED,\nINCLUDING BLENDS OF THESE OILS OR FRACTIONS WITH OILS OR FRACTIONS OF HEADING 1509 :\nCRUDE OIL",
+        "hsn_code": "15100010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER OILS AND THEIR FRACTIONS OBTAINED SOLELY FROM OLIVES, WHETHER OR NOT\nREFINED, BUT NOT CHEMICALLY MODIFIED, INCLUDING BLENDS OF THESE OILS OR FRACTIONS\nWITH OILS OR FRACTIONS OF HEADING 1509 OTHER OILS AND THEIR FRACTIONS OBTAINED\nSOLELY FROM OLIVES, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED,\nINCLUDING BLENDS OF THESE OILS OR FRACTIONS WITH OILS OR FRACTIONS OF HEADING 1509 :\nOTHER : EDIBLE GRADE",
+        "hsn_code": "15100091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER OILS AND THEIR FRACTIONS OBTAINED SOLELY FROM OLIVES, WHETHER OR NOT\nREFINED, BUT NOT CHEMICALLY MODIFIED, INCLUDING BLENDS OF THESE OILS OR FRACTIONS\nWITH OILS OR FRACTIONS OF HEADING 1509 OTHER OILS AND THEIR FRACTIONS OBTAINED\nSOLELY FROM OLIVES, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED,\nINCLUDING BLENDS OF THESE OILS OR FRACTIONS WITH OILS OR FRACTIONS OF HEADING 1509 :\nOTHER : OTHER",
+        "hsn_code": "15100099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER OILS AND THEIR FRACTIONS OBTAINED SOLELY FROM OLIVES, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED, INCLUDING BLENDS OF THESE OILS OR FRACTIONS WITH OILS OR FRACTIONS OF HEADING 1509",
+        "hsn_code": "1510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OLIVE OIL AND ITS FRACTIONS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED\nVIRGIN",
+        "hsn_code": "15091000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OLIVE OIL AND ITS FRACTIONS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED\nOTHER : EDIBLE GRADE",
+        "hsn_code": "15099010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OLIVE OIL AND ITS FRACTIONS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED\nOTHER : OTHER",
+        "hsn_code": "15099090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OLIVE OIL AND ITS FRACTIONS, WHETHER OR NOT REFINED BUT NOT CHEMICALLY MODIFIED",
+        "hsn_code": "1509",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GROUND-NUT OIL AND ITS FRACTIONS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY\nMODIFIED CRUDE OIL",
+        "hsn_code": "15081000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GROUND-NUT OIL AND ITS FRACTIONS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY\nMODIFIED OTHER : DEODORIZED (SALAD OIL)",
+        "hsn_code": "15089010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GROUND-NUT OIL AND ITS FRACTIONS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY\nMODIFIED OTHER : OTHER : EDIBLE GRADE",
+        "hsn_code": "15089091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GROUND-NUT OIL AND ITS FRACTIONS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY\nMODIFIED OTHER : OTHER : OTHER",
+        "hsn_code": "15089099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "GROUNDNUT OIL AND ITS FRACTIONS, WHETHER OR NOT REFINED BUT NOT CHEMICALLY MODIFIED",
+        "hsn_code": "1508",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SOYA-BEAN OIL AND ITS FRACTIONS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY\nMODIFIED CRUDE OIL, WHETHER OR NOT DEGUMMED",
+        "hsn_code": "15071000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SOYA-BEAN OIL AND ITS FRACTIONS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY\nMODIFIED OTHER : EDIBLE GRADE",
+        "hsn_code": "15079010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SOYA-BEAN OIL AND ITS FRACTIONS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY\nMODIFIED OTHER : OTHER",
+        "hsn_code": "15079090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "SOYA-BEAN OIL AND ITS FRACTIONS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED",
+        "hsn_code": "1507",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "OTHER ANIMAL FATS AND OILS AND THEIR FRACTIONS, WHETHER OR NOT REFINED, BUT NOT\nCHEMICALLY MODIFIED OTHER ANIMAL FATS AND OILS AND THEIR FRACTIONS, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED : NEATS FOOT OIL AND FATS FROM BONE OR\nWASTE",
+        "hsn_code": "15060010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ANIMAL FATS AND OILS AND THEIR FRACTIONS, WHETHER OR NOT REFINED, BUT NOT\nCHEMICALLY MODIFIED OTHER ANIMAL FATS AND OILS AND THEIR FRACTIONS, WHETHER OR\nNOT REFINED, BUT NOT CHEMICALLY MODIFIED : OTHER",
+        "hsn_code": "15060090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ANIMAL FATS AND OILS AND THEIR FRACTIONS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED",
+        "hsn_code": "1506",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WOOL GREASE AND FATTY SUBSTANCES DERIVED THEREFROM (INCLUDING LANOLIN) WOOL\nGREASE AND FATTY SUBSTANCES DERIVED THEREFROM (INCLUDING LANOLIN) : WOOL ALCOHOL\n(INCLUDING LANOLIN ALCOHOL)",
+        "hsn_code": "15050010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WOOL GREASE AND FATTY SUBSTANCES DERIVED THEREFROM (INCLUDING LANOLIN) WOOL\nGREASE AND FATTY SUBSTANCES DERIVED THEREFROM (INCLUDING LANOLIN) : WOOL GREASE,\nCRUDE",
+        "hsn_code": "15050020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WOOL GREASE AND FATTY SUBSTANCES DERIVED THEREFROM (INCLUDING LANOLIN) WOOL\nGREASE AND FATTY SUBSTANCES DERIVED THEREFROM (INCLUDING LANOLIN) : OTHER",
+        "hsn_code": "15050090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "WOOL GREASE AND FATTY SUBSTANCES DERIVED THEREFROM (INCLUDING LANOLIN)",
+        "hsn_code": "1505",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FATS AND OILS AND THEIR FRACTIONS, OF FISH OR MARINE MAMMALS, WHETHER OR NOT\nREFINED, BUT NOT CHEMICALLY MODIFIED FISH LIVER OILS AND THEIR FRACTIONS : COD LIVER\nOIL",
+        "hsn_code": "15041010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FATS AND OILS AND THEIR FRACTIONS, OF FISH OR MARINE MAMMALS, WHETHER OR NOT\nREFINED, BUT NOT CHEMICALLY MODIFIED FISH LIVER OILS AND THEIR FRACTIONS : OTHER :\nSQUID LIVER OIL",
+        "hsn_code": "15041091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FATS AND OILS AND THEIR FRACTIONS, OF FISH OR MARINE MAMMALS, WHETHER OR NOT\nREFINED, BUT NOT CHEMICALLY MODIFIED FISH LIVER OILS AND THEIR FRACTIONS : OTHER :\nOTHER",
+        "hsn_code": "15041099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FATS AND OILS AND THEIR FRACTIONS, OF FISH OR MARINE MAMMALS, WHETHER OR NOT\nREFINED, BUT NOT CHEMICALLY MODIFIED FATS AND OILS AND THEIR FRACTIONS OF FISH,\nOTHER THAN LIVER OILS : FISH BODY OIL",
+        "hsn_code": "15042010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FATS AND OILS AND THEIR FRACTIONS, OF FISH OR MARINE MAMMALS, WHETHER OR NOT\nREFINED, BUT NOT CHEMICALLY MODIFIED FATS AND OILS AND THEIR FRACTIONS OF FISH,\nOTHER THAN LIVER OILS : FISH LIPID OIL",
+        "hsn_code": "15042020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FATS AND OILS AND THEIR FRACTIONS, OF FISH OR MARINE MAMMALS, WHETHER OR NOT\nREFINED, BUT NOT CHEMICALLY MODIFIED FATS AND OILS AND THEIR FRACTIONS OF FISH,\nOTHER THAN LIVER OILS : SPERM OIL",
+        "hsn_code": "15042030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FATS AND OILS AND THEIR FRACTIONS, OF FISH OR MARINE MAMMALS, WHETHER OR NOT\nREFINED, BUT NOT CHEMICALLY MODIFIED FATS AND OILS AND THEIR FRACTIONS OF FISH,\nOTHER THAN LIVER OILS : OTHER",
+        "hsn_code": "15042090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FATS AND OILS AND THEIR FRACTIONS, OF FISH OR MARINE MAMMALS, WHETHER OR NOT\nREFINED, BUT NOT CHEMICALLY MODIFIED FATS AND OILS AND THEIR FRACTIONS, OF MARINE\nMAMMALS",
+        "hsn_code": "15043000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FATS AND OILS AND THEIR FRACTIONS, OF FISH OR MARINE MAMMALS, WHETHER OR NOT REFINED, BUT NOT CHEMICALLY MODIFIED",
+        "hsn_code": "1504",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LARD STEARIN, LARD OIL, OLEOSTEARIN, OLEO?OIL AND TALLOW OIL, NOT EMULSIFIED OR\nMIXED OR OTHERWISE PREPARED",
+        "hsn_code": "15030000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Lard Stearin, Lard Oil, Oleostearin, Oleo-Oil and Tallow Oil, not emulsified or mixed or otherwise prepared",
+        "hsn_code": "1503",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FATS OF BOVINE ANIMALS, SHEEP OR GOATS, OTHER THAN THOSE OF HEADING 1503-TALLOW---\nMUTTON TALLOW",
+        "hsn_code": "15021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FATS OF BOVINE ANIMALS, SHEEP OR GOATS, OTHER THAN THOSE OF HEADING 1503-TALLOW---\nOTHER THAN MUTTON TALLOW",
+        "hsn_code": "15021090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FATS OF BOVINE ANIMALS, SHEEP OR GOATS, OTHER THAN THOSE OF HEADING 1503-OTHER---\nUNRENDERED FATS",
+        "hsn_code": "15029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FATS OF BOVINE ANIMALS, SHEEP OR GOATS, OTHER THAN THOSE OF HEADING 1503-OTHER---\nRENDERED FATS OR SOLVENT EXTRACTION FATS",
+        "hsn_code": "15029020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FATS OF BOVINE ANIMALS, SHEEP OR GOATS, OTHER THAN THOSE OF HEADING 1503-OTHER---\nOTHER",
+        "hsn_code": "15029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FATS OF BOVINE ANIMALS, SHEEP OR GOATS, OTHER THAN THOSE OF HEADING 1503",
+        "hsn_code": "1502",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PIG FAT (INCLUDING LARD) AND POULTRY FAT, OTHER THAN THAT OF HEADING 0209 OR 1503 -\nLARD",
+        "hsn_code": "15011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PIG FAT (INCLUDING LARD) AND POULTRY FAT, OTHER THAN THAT OF HEADING 0209 OR 1503-\nOTHER PIG FAT",
+        "hsn_code": "15012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PIG FAT (INCLUDING LARD) AND POULTRY FAT, OTHER THAN THAT OF HEADING 0209 OR 1503-\nOTHER",
+        "hsn_code": "15019000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PIG FAT (INCLUDING LARD) AND POULTRY FAT,OTHER THAN THAT OF HEADING 0209 OR 1503",
+        "hsn_code": "1501",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "EXTRACTS, ESSENCES AND CONCENTRATES OF COFFEE, TEA OR MATE AND PREPARATIONS WITH A BASIS OF THESE PRODUCTS OR WITH A BASIS OF COFFEE, TEA OR MATE; ROASTED CHICORY & OTHER ROASTED COFFEE SUBSTITUTES, AND EXTRACTS, ESSENCES AND CONCENTRATES THEREOF",
+        "hsn_code": "1302",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR-AGAR\nAND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED\nFROM VEGETABLE PRODUCTS : OTHER",
+        "hsn_code": "13023900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR-AGAR\nAND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED\nFROM VEGETABLE PRODUCTS : MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED,\nDERIVED FROM LOCUST BEANS, LOCUST BEAN SEEDS OR GUAR SEEDS : OTHER",
+        "hsn_code": "13023290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR-AGAR\nAND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED\nFROM VEGETABLE PRODUCTS : MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED,\nDERIVED FROM LOCUST BEANS, LOCUST BEAN SEEDS OR GUAR SEEDS : KAPPA CARRAGEENAN",
+        "hsn_code": "13023240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR-AGAR\nAND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED\nFROM VEGETABLE PRODUCTS : MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED,\nDERIVED FROM LOCUST BEANS, LOCUST BEAN SEEDS OR GUAR SEEDS : GUARGUM TREATED\nAND PULVERISED",
+        "hsn_code": "13023230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR-AGAR\nAND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED\nFROM VEGETABLE PRODUCTS : MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED,\nDERIVED FROM LOCUST BEANS, LOCUST BEAN SEEDS OR GUAR SEEDS : GUARGUM REFINED\nSPLIT",
+        "hsn_code": "13023220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR-AGAR\nAND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED\nFROM VEGETABLE PRODUCTS : MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED,\nDERIVED FROM LOCUST BEANS, LOCUST BEAN SEEDS OR GUAR SEEDS : GUAR MEAL",
+        "hsn_code": "13023210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR\nAGAR AND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED\nFROM VEGETABLE PRODUCTS : AGAR-AGAR",
+        "hsn_code": "13023100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR-AGAR\nAND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS PECTIC SUBSTANCES, PECTINATES AND PECTATES",
+        "hsn_code": "13022000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR\nAGAR AND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS VEGETABLE SAPS AND EXTRACTS : OTHER : OTHER",
+        "hsn_code": "13021990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR\nAGAR AND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS VEGETABLE SAPS AND EXTRACTS : OTHER : PURIFIED AND DISTILLED CNSL\n(CARDANOL)",
+        "hsn_code": "13021930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR-AGAR\nAND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS VEGETABLE SAPS AND EXTRACTS : OTHER : CASHEW SHELL LIQUID\n(CNSL), CRUDE",
+        "hsn_code": "13021920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR-AGAR\nAND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS VEGETABLE SAPS AND EXTRACTS : OTHER : EXTRACTS : OTHER",
+        "hsn_code": "13021919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR-AGAR\nAND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS VEGETABLE SAPS AND EXTRACTS : OTHER : EXTRACTS : OF GARACENIA\nOR GAMBODGE",
+        "hsn_code": "13021918",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR-AGAR\nAND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS VEGETABLE SAPS AND EXTRACTS : OTHER : EXTRACTS : OF GYMNEMA",
+        "hsn_code": "13021917",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR-AGAR\nAND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS VEGETABLE SAPS AND EXTRACTS : OTHER : EXTRACTS : OF NEEM",
+        "hsn_code": "13021916",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR-AGAR\nAND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS VEGETABLE SAPS AND EXTRACTS : OTHER : EXTRACTS : OF AGAROSE",
+        "hsn_code": "13021915",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR-AGAR\nAND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS VEGETABLE SAPS AND EXTRACTS : OTHER : EXTRACTS : OF GINSENG\n(INCLUDING POWDER)",
+        "hsn_code": "13021914",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR-AGAR\nAND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS VEGETABLE SAPS AND EXTRACTS : OTHER : EXTRACTS : OF NUXVOMICA",
+        "hsn_code": "13021913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR-AGAR\nAND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS VEGETABLE SAPS AND EXTRACTS : OTHER : EXTRACTS : OF CASCARA\nSAGRADA",
+        "hsn_code": "13021912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR-AGAR\nAND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS VEGETABLE SAPS AND EXTRACTS : OTHER : EXTRACTS : OF BELLADONA",
+        "hsn_code": "13021911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR-AGAR\nAND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS VEGETABLE SAPS AND EXTRACTS : OF PYRETHRUM OR OF THE ROOTS OF\nPLANTS CONTAINING ROTENONE",
+        "hsn_code": "13021400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR-AGAR\nAND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS VEGETABLE SAPS AND EXTRACTS : OF HOPS",
+        "hsn_code": "13021300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE SAPS AND EXTRACTS; PECTIC SUBSTANCES, PECTINATES AND PECTATES; AGAR\nAGAR AND OTHER MUCILAGES AND THICKENERS, WHETHER OR NOT MODIFIED, DERIVED FROM\nVEGETABLE PRODUCTS VEGETABLE SAPS AND EXTRACTS : OF LIQUORICE",
+        "hsn_code": "13021200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "13021100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FLOURS AND MEALS OF OIL SEEDS OR OLEAGINOUS FRUITS, OTHER THAN THOSE OF MUSTARD\nOF SOYA BEANS",
+        "hsn_code": "12081000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FLOURS AND MEALS OF OIL SEEDS OR OLEAGINOUS FRUITS, OTHER THAN THOSE OF MUSTARD\nOTHER",
+        "hsn_code": "12089000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "FLOURS AND MEALS OF OIL SEEDS OR OLEAGINOUS FRUITS, OTHER THAN THOSE OF MUSTARD",
+        "hsn_code": "1208",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "COPRA",
+        "hsn_code": "12030000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "Copra",
+        "hsn_code": "1203",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "STARCHES; INULIN STARCHES : WHEAT STARCH",
+        "hsn_code": "11081100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "STARCHES; INULIN STARCHES : MAIZE (CORN) STARCH",
+        "hsn_code": "11081200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "STARCHES; INULIN STARCHES : POTATO STARCH",
+        "hsn_code": "11081300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "STARCHES; INULIN STARCHES : MANIOC (CASSAVA) STARCH",
+        "hsn_code": "11081400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "STARCHES; INULIN STARCHES : OTHER : SAGO",
+        "hsn_code": "11081910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "STARCHES; INULIN STARCHES : OTHER : OTHER",
+        "hsn_code": "11081990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "STARCHES; INULIN INSULIN",
+        "hsn_code": "11082000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "STARCHES; INULIN",
+        "hsn_code": "1108",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MALT, WHETHER OR NOT ROASTED NOT ROASTED",
+        "hsn_code": "11071000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MALT, WHETHER OR NOT ROASTED ROASTED",
+        "hsn_code": "11072000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MALT, WHETHER OR NOT ROASTED",
+        "hsn_code": "1107",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING NOT MORE THAN 200 G/M2",
+        "hsn_code": "5208",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "Printed circuits",
+        "hsn_code": "8534",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNETIC TAPE RECORDERS AND OTHER SOUND RECORDING APPARATUS, WHETHER OR NOT\nINCORPORATING A SOUND REPRODUCING DEVICE - OTHER: OTHER",
+        "hsn_code": "85209090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNETIC TAPE RECORDERS AND OTHER SOUND RECORDING APPARATUS, WHETHER OR NOT\nINCORPORATING A SOUND REPRODUCING DEVICE - OTHER: COMPACT DISC RECORDING\nAPPARATUS",
+        "hsn_code": "85209010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNETIC TAPE RECORDERS AND OTHER SOUND RECORDING APPARATUS, WHETHER OR NOT\nINCORPORATING A SOUND REPRODUCING DEVICE - OTHER MAGNETIC TAPE RECORDERS\nINCORPORATING SOUND REPRODUCING APPARATUS: - OTHER : OTHER",
+        "hsn_code": "85203990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNETIC TAPE RECORDERS AND OTHER SOUND RECORDING APPARATUS, WHETHER OR NOT\nINCORPORATING A SOUND REPRODUCING DEVICE - OTHER MAGNETIC TAPE RECORDERS\nINCORPORATING SOUND REPRODUCING APPARATUS: - OTHER : HEAVY-DUTY TAPE RECORDERS",
+        "hsn_code": "85203910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNETIC TAPE RECORDERS AND OTHER SOUND RECORDING APPARATUS, WHETHER OR NOT\nINCORPORATING A SOUND REPRODUCING DEVICE - OTHER MAGNETIC TAPE RECORDERS\nINCORPORATING SOUND REPRODUCING APPARATUS: - OTHER, CASSETTE-TYPE: OTHER",
+        "hsn_code": "85203390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNETIC TAPE RECORDERS AND OTHER SOUND RECORDING APPARATUS, WHETHER OR NOT\nINCORPORATING A SOUND REPRODUCING DEVICE - OTHER MAGNETIC TAPE RECORDERS\nINCORPORATING SOUND REPRODUCING APPARATUS: - OTHER, CASSETTE-TYPE: HEAVY-DUTY\nTAPE RECORDERS",
+        "hsn_code": "85203310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNETIC TAPE RECORDERS AND OTHER SOUND RECORDING APPARATUS, WHETHER OR NOT\nINCORPORATING A SOUND REPRODUCING DEVICE - OTHER MAGNETIC TAPE RECORDERS\nINCORPORATING SOUND REPRODUCING APPARATUS: DIGITAL AUDIO TYPE",
+        "hsn_code": "85203200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNETIC TAPE RECORDERS AND OTHER SOUND RECORDING APPARATUS, WHETHER OR NOT\nINCORPORATING A SOUND REPRODUCING DEVICE TELEPHONE ANSWERING MACHINES",
+        "hsn_code": "85202000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MAGNETIC TAPE RECORDERS AND OTHER SOUND RECORDING APPARATUS, WHETHER OR NOT\nINCORPORATING A SOUND REPRODUCING DEVICE DICTATING MACHINES NOT CAPABLE OF\nOPERATING WITHOUT AN EXTERNAL SOURCE OF POWER",
+        "hsn_code": "85201000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - MACHINES FOR PREPARING TEXTILE FIBRES\n- CARDING MACHINES : COTTON CARDING MACHINES",
+        "hsn_code": "84451110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - MACHINES FOR PREPARING TEXTILE FIBRES\n- CARDING MACHINES : OTHER",
+        "hsn_code": "84451190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - MACHINES FOR PREPARING TEXTILE FIBRES\n- COMBING MACHINES: COTTON COMBING MACHINES",
+        "hsn_code": "84451210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - MACHINES FOR PREPARING TEXTILE FIBRES\n- COMBING MACHINES: OTHER",
+        "hsn_code": "84451290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - MACHINES FOR PREPARING TEXTILE FIBRES\n- DRAWING OR ROVING MACHINES",
+        "hsn_code": "84451300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - MACHINES FOR PREPARING TEXTILE FIBRES\n- OTHER:COTTON PROCESSING MACHINES (INCLUDING COTTON GINNING MACHINE)",
+        "hsn_code": "84451910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - MACHINES FOR PREPARING TEXTILE FIBRES\n- OTHER:JUTE FIBRE PROCESSING MACHINES",
+        "hsn_code": "84451920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - MACHINES FOR PREPARING TEXTILE FIBRES\n- OTHER:REGENERATED FIBRES AND SYNTHETIC FIBRES PROCESSING MACHINES",
+        "hsn_code": "84451930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - MACHINES FOR PREPARING TEXTILE FIBRES\n- OTHER:SILK PROCESSING MACHINES",
+        "hsn_code": "84451940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - MACHINES FOR PREPARING TEXTILE FIBRES\n- OTHER:WOOL PROCESSING MACHINES",
+        "hsn_code": "84451950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - MACHINES FOR PREPARING TEXTILE FIBRES\n- OTHER:BLOWROOM MACHINES",
+        "hsn_code": "84451960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - MACHINES FOR PREPARING TEXTILE FIBRES\n- OTHER:OTHER",
+        "hsn_code": "84451990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE SPINNING MACHINES : COTTON\nSPINNING MACHINES : DRAWING FRAMES",
+        "hsn_code": "84452011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE SPINNING MACHINES : COTTON\nSPINNING MACHINES : INTERMEDIATE FRAMES",
+        "hsn_code": "84452012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE SPINNING MACHINES : COTTON\nSPINNING MACHINES : RING FRAMES",
+        "hsn_code": "84452013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE SPINNING MACHINES : COTTON\nSPINNING MACHINES : ROVING FRAMES",
+        "hsn_code": "84452014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE SPINNING MACHINES : COTTON\nSPINNING MACHINES : OTHER",
+        "hsn_code": "84452019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE SPINNING MACHINES : JUTE FIBRES\nSPINNING MACHINES",
+        "hsn_code": "84452020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE SPINNING MACHINES :\nREGENERATED FIBRES AND SYNTHETIC FIBRES SPINNING MACHINES",
+        "hsn_code": "84452030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE SPINNING MACHINES : SILK FIBRES\nSPINNING MACHINES",
+        "hsn_code": "84452040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE SPINNING MACHINES : WOOL\nSPINNING MACHINES",
+        "hsn_code": "84452050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE SPINNING MACHINES : OTHER",
+        "hsn_code": "84452090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE DOUBLING OR TWISTING\nMACHINES : COTTON FIBRE DOUBLING OR TWISTING MACHINES : DOUBLING FRAMES",
+        "hsn_code": "84453011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE DOUBLING OR TWISTING\nMACHINES : COTTON FIBRE DOUBLING OR TWISTING MACHINES : OTHER",
+        "hsn_code": "84453019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE DOUBLING OR TWISTING\nMACHINES : JUTE FIBRE DOUBLING OR TWISTING MACHINES",
+        "hsn_code": "84453020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE DOUBLING OR TWISTING\nMACHINES : REGENERATED FIBRE AND SYNTHETIC FIBRE YARN DOUBLING OR TWISTING\nMACHINES",
+        "hsn_code": "84453030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE DOUBLING OR TWISTING\nMACHINES : SILK FIBRE DOUBLING OR TWISTING MACHINES",
+        "hsn_code": "84453040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE DOUBLING OR TWISTING\nMACHINES : WOOL FIBRE DOUBLING OR TWISTING MACHINES",
+        "hsn_code": "84453050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE DOUBLING OR TWISTING\nMACHINES : OTHER FIBRE DOUBLING OR TWISTING MACHINES",
+        "hsn_code": "84453090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE WINDING (INCLUDING WEFT\nWINDING) OR REELING MACHINES: COTTON FIBRE WINDING (INCLUDING WEFT-WINDING) OR\nREELING MACHINES, AUTOMATIC OR OTHERWISE",
+        "hsn_code": "84454010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE WINDING (INCLUDING WEFT\nWINDING) OR REELING MACHINES: JUTE FIBRE REELING (INCLUDING WEFT-WINDING)\nMACHINES",
+        "hsn_code": "84454020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE WINDING (INCLUDING WEFT\nWINDING) OR REELING MACHINES: REGENERATED FIBRES YARN AND SYNTHETIC FIBRES YARN\nREELING (INCLUDING WEFT-WINDING) MACHINES",
+        "hsn_code": "84454030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE WINDING (INCLUDING WEFT\nWINDING) OR REELING MACHINES: SILK FIBRE REELING (INCLUDING WEFT-WINDING) MACHINES",
+        "hsn_code": "84454040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE WINDING (INCLUDING WEFT\nWINDING) OR REELING MACHINES: WOOL FIBRE REELING (INCLUDING WEFT-WINDING)\nMACHINES",
+        "hsn_code": "84454050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - TEXTILE WINDING (INCLUDING WEFT\nWINDING) OR REELING MACHINES: OTHER",
+        "hsn_code": "84454090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES\nAND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING\n(INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR\nUSE ON THE MACHINES OF HEADING 8446 OR 8447 - OTHER",
+        "hsn_code": "84459000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEAVING MACHINES (LOOMS) - FOR WEAVING FABRICS OF A WIDTH NOT EXCEEDING 30 CM:\nCOTTON WEAVING MACHINES : AUTOMATIC, POWERLOOM",
+        "hsn_code": "84461011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEAVING MACHINES (LOOMS) - FOR WEAVING FABRICS OF A WIDTH NOT EXCEEDING 30 CM:\nCOTTON WEAVING MACHINES : PLAIN, POWERLOOM",
+        "hsn_code": "84461012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEAVING MACHINES (LOOMS) - FOR WEAVING FABRICS OF A WIDTH NOT EXCEEDING 30 CM:\nCOTTON WEAVING MACHINES : OTHER",
+        "hsn_code": "84461019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEAVING MACHINES (LOOMS) - FOR WEAVING FABRICS OF A WIDTH NOT EXCEEDING 30 CM:\nOTHER",
+        "hsn_code": "84461090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEAVING MACHINES (LOOMS) - FOR WEAVING FABRICS OF A WIDTH NOT EXCEEDING 30 CM,\nSHUTTLE TYPE - POWER LOOMS: COTTON WEAVING MACHINES, AUTOMATIC",
+        "hsn_code": "84462110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEAVING MACHINES (LOOMS) - FOR WEAVING FABRICS OF A WIDTH NOT EXCEEDING 30 CM,\nSHUTTLE TYPE - POWER LOOMS: OTHER",
+        "hsn_code": "84462190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEAVING MACHINES (LOOMS) - FOR WEAVING FABRICS OF A WIDTH NOT EXCEEDING 30 CM,\nSHUTTLE TYPE - OTHER : COTTON WEAVING MACHINES",
+        "hsn_code": "84462910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEAVING MACHINES (LOOMS) - FOR WEAVING FABRICS OF A WIDTH NOT EXCEEDING 30 CM,\nSHUTTLE TYPE - OTHER : OTHER",
+        "hsn_code": "84462990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEAVING MACHINES (LOOMS) - FOR WEAVING FABRICS OF A WIDTH EXCEEDING 30 CM,\nSHUTTLELESS TYPE: COTTON WEAVING MACHINES : AUTOMATIC, POWERLOOM",
+        "hsn_code": "84463011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEAVING MACHINES (LOOMS) - FOR WEAVING FABRICS OF A WIDTH EXCEEDING 30 CM,\nSHUTTLELESS TYPE: COTTON WEAVING MACHINES :PLAIN, POWERLOOM",
+        "hsn_code": "84463012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEAVING MACHINES (LOOMS) - FOR WEAVING FABRICS OF A WIDTH EXCEEDING 30 CM,\nSHUTTLELESS TYPE: COTTON WEAVING MACHINES : OTHER",
+        "hsn_code": "84463019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEAVING MACHINES (LOOMS) - FOR WEAVING FABRICS OF A WIDTH EXCEEDING 30 CM,\nSHUTTLELESS TYPE: OTHER",
+        "hsn_code": "84463090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TYPEWRITERS OTHER THAN PRINTERS OF HEADING 8443; WORD-PROCESSING MACHINES --\nTYPEWRITERS OTHER THAN PRINTERS OF HEADING 8443; WORD-PROCESSING MACHINES ---\nWORD-PROCESSING MACHINES",
+        "hsn_code": "84690010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TYPEWRITERS OTHER THAN PRINTERS OF HEADING 8471; WORD-PROCESSING MACHINES --\nTYPEWRITERS OTHER THAN PRINTERS OF HEADING 8471; WORD-PROCESSING MACHINES ---\nAUTOMATIC TYPEWRITERS",
+        "hsn_code": "84690020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TYPEWRITERS OTHER THAN PRINTERS OF HEADING 8471; WORD-PROCESSING MACHINES --\nTYPEWRITERS OTHER THAN PRINTERS OF HEADING 8471; WORD-PROCESSING MACHINES ---\nBRAIELLE TYPEWRITERS ELECTRIC",
+        "hsn_code": "84690030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TYPEWRITERS OTHER THAN PRINTERS OF HEADING 8471; WORD-PROCESSING MACHINES --\nTYPEWRITERS OTHER THAN PRINTERS OF HEADING 8471; WORD-PROCESSING MACHINES ---\nBRAIELLE TYPEWRITERS NON-ELECTRIC",
+        "hsn_code": "84690040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TYPEWRITERS OTHER THAN PRINTERS OF HEADING 8471; WORD-PROCESSING MACHINES --\nTYPEWRITERS OTHER THAN PRINTERS OF HEADING 8471; WORD-PROCESSING MACHINES ---\nOTHER TYPEWRITERS, ELECTRIC OR NON-ELECTRIC",
+        "hsn_code": "84690090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TYPEWRITERS OTHER THAN PRINTERS OF HEADING 8471; WORD-PROCESSING MACHINES -\nAUTOMATIC TYPEWRITERS AND WORD-PROCESSING MACHINES: WORD-PROCESSING MACHINES",
+        "hsn_code": "84691100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TYPEWRITERS OTHER THAN PRINTERS OF HEADING 8471; WORD-PROCESSING MACHINES -\nAUTOMATIC TYPEWRITERS AND WORD-PROCESSING MACHINES: AUTOMATIC TYPEWRITERS",
+        "hsn_code": "84691200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TYPEWRITERS OTHER THAN PRINTERS OF HEADING 8471; WORD-PROCESSING MACHINES -\nOTHER TYPEWRITERS ,ELECTRIC : BRAILLE TYPEWRITERS",
+        "hsn_code": "84692010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TYPEWRITERS OTHER THAN PRINTERS OF HEADING 8471; WORD-PROCESSING MACHINES -\nOTHER TYPEWRITERS ,ELECTRIC : OTHER",
+        "hsn_code": "84692090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TYPEWRITERS OTHER THAN PRINTERS OF HEADING 8471; WORD-PROCESSING MACHINES -\nOTHER TYPEWRITERS, NON-ELECTRIC: BRAILLE TYPEWRITERS",
+        "hsn_code": "84693010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TYPEWRITERS OTHER THAN PRINTERS OF HEADING 8471; WORD-PROCESSING MACHINES -\nOTHER TYPEWRITERS, NON-ELECTRIC: OTHER",
+        "hsn_code": "84693090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Machinery / Electrical",
+        "hsn_code": "84-85",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "TYPEWRITERS OTHER THAN PRINTERS OF HEADING 8443; WORD-PROCESSING MACHINES",
+        "hsn_code": "8469",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "MACHINES FOR PREPARING TEXTILE FIBRES; SPINNING, DOUBLING OR TWISTING MACHINES AND OTHER MACHINERY FOR PRODUCING TEXTILE YARNS; TEXTILE REELING OR WINDING (INCLUDING WEFT-WINDING) MACHINES AND MACHINES FOR PREPARING TEXTILE YARNS FOR USE ON THE MACHINES OF HEADING 8446 OR 8447",
+        "hsn_code": "8445",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WEAVING MACHINES (LOOMS)",
+        "hsn_code": "8446",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Stone / Glass",
+        "hsn_code": "68-71",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FUEL WOOD, IN LOGS, IN BILLETS, IN TWIGS, IN FAGGOTS OR IN SIMILAR FORMS; WOOD IN CHIPS OR PARTICLES; SAWDUST AND WOOD WASTE AND SCRAP, WHETHER OR NOT AGGLOMERATED IN LOGS, BRIQUETTES, PELLETS OR SIMILAR FORMS.",
+        "hsn_code": "4401",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "Wood & Wood Products",
+        "hsn_code": "44-49",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD CHARCOAL (INCLUDING SHELL OR NUT CHARCOAL), WHETHER OR NOT AGGLOMERATED:\nOTHER --- OTHER",
+        "hsn_code": "44029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD CHARCOAL (INCLUDING SHELL OR NUT CHARCOAL), WHETHER OR NOT AGGLOMERATED -\nOTHER : --- OF COCUNUT SHELL",
+        "hsn_code": "44029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WHETHER OR NOT AGGLOMERATED - WOOD CHARCOAL (INCLUDING SHELL OR NUT\nCHARCOAL)WOOD CHARCOAL (INCLUDING SHELL OR NUT CHARCOAL), WHETHER OR NOT\nAGGLOMERATED ---OF BAMBOO",
+        "hsn_code": "44021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD CHARCOAL (INCLUDING SHELL OR NUT CHARCOAL), WHETHER OR NOT AGGLOMERATED -\nWOOD CHARCOAL (INCLUDING SHELL OR NUT CHARCOAL), WHETHER OR NOT AGGLOMERATED:\nOTHER",
+        "hsn_code": "44020090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "WOOD CHARCOAL (INCLUDING SHELL OR NUT CHARCOAL), WHETHER OR NOT AGGLOMERATED -\nWOOD CHARCOAL (INCLUDING SHELL OR NUT CHARCOAL), WHETHER OR NOT AGGLOMERATED:\nOF COCONUT SHELL",
+        "hsn_code": "44020010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUEL WOOD, IN LOGS, IN BILLETS, IN TWIGS, IN FAGGOTS OR IN SIMILAR FORMS; WOOD IN\nCHIPS OR PARTICLES; SAWDUST AND WOOD WASTE AND SCRAP, WHETHER OR NOT\nAGGLOMERATED IN LOGS, BRIQUETTES, PELLETS OR SIMILAR FORMS - SAWDUST AND WOOD\nWASTE AND SCRAP, WHETHER OR NOT AGGLOMERATED IN LOGS, BRIQUETTES, PELLETS OR\nSIMILAR FORMS--OTHER",
+        "hsn_code": "44013900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUEL WOOD, IN LOGS, IN BILLETS, IN TWIGS, IN FAGGOTS OR IN SIMILAR FORMS; WOOD IN\nCHIPS OR PARTICLES; SAWDUST AND WOOD WASTE AND SCRAP, WHETHER OR NOT\nAGGLOMERATED IN LOGS, BRIQUETTES, PELLETS OR SIMILAR FORMS - SAWDUST AND WOOD\nWASTE AND SCRAP, WHETHER OR NOT AGGLOMERATED IN LOGS, BRIQUETTES, PELLETS OR\nSIMILAR FORMS--WOOD PELLETS",
+        "hsn_code": "44013100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUEL WOOD, IN LOGS, IN BILLETS, IN TWIGS, IN FAGGOTS OR IN SIMILAR FORMS; WOOD IN\nCHIPS OR PARTICLES; SAWDUST AND WOOD WASTE AND SCRAP, WHETHER OR NOT\nAGGLOMERATED IN LOGS, BRIQUETTES, PELLETS OR SIMILAR FORMS - WOOD IN CHIPS OR\nPARTICLES : NON-CONIFEROUS",
+        "hsn_code": "44012200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUEL WOOD, IN LOGS, IN BILLETS, IN TWIGS, IN FAGGOTS OR IN SIMILAR FORMS; WOOD IN\nCHIPS OR PARTICLES; SAWDUST AND WOOD WASTE AND SCRAP, WHETHER OR NOT\nAGGLOMERATED IN LOGS, BRIQUETTES, PELLETS OR SIMILAR FORMS - WOOD IN CHIPS OR\nPARTICLES : CONIFEROUS",
+        "hsn_code": "44012100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUEL WOOD, IN LOGS, IN BILLETS, IN TWIGS, IN FAGGOTS OR IN SIMILAR FORMS; WOOD IN\nCHIPS OR PARTICLES; SAWDUST AND WOOD WASTE AND SCRAP, WHETHER OR NOT\nAGGLOMERATED IN LOGS, BRIQUETTES, PELLETS OR SIMILAR FORMS- FUEL WOOD, IN LOGS, IN\nBILLETS, IN TWIGS, IN FAGGOTS OR IN SIMILAR FORMS : OTHER",
+        "hsn_code": "44011090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "FUEL WOOD, IN LOGS, IN BILLETS, IN TWIGS, IN FAGGOTS OR IN SIMILAR FORMS; WOOD IN\nCHIPS OR PARTICLES; SAWDUST AND WOOD WASTE AND SCRAP, WHETHER OR NOT\nAGGLOMERATED IN LOGS, BRIQUETTES, PELLETS OR SIMILAR FORMS- FUEL WOOD, IN LOGS, IN\nBILLETS, IN TWIGS, IN FAGGOTS OR IN SIMILAR FORMS : IN LOGS",
+        "hsn_code": "44011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 18
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC HYDROCARBONS - SATURATED",
+        "hsn_code": "29011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC HYDROCARBONS - UNSATURATED : ETHYLENE",
+        "hsn_code": "29012100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC HYDROCARBONS - UNSATURATED : PROPENE (PROPYLENE)",
+        "hsn_code": "29012200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC HYDROCARBONS - UNSATURATED : BUTENE (BUTYLENE) AND ISOMERS THEREOF",
+        "hsn_code": "29012300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC HYDROCARBONS - UNSATURATED : BUTA-1,3-DIENE AND ISOPRENE",
+        "hsn_code": "29012400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC HYDROCARBONS - UNSATURATED :-OTHER : ACETYLENE, WHETHER OR NOT IN\nDISSOLVED CONDITION",
+        "hsn_code": "29012910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC HYDROCARBONS - UNSATURATED :-OTHER : HEPTENE (HEPTYLENE)",
+        "hsn_code": "29012920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC HYDROCARBONS - UNSATURATED :-OTHER : OTHER",
+        "hsn_code": "29012990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC HYDROCARBONS - CYCLANES, CYCLENES AND CYCLOTERPENES : CYCLOHEXANE",
+        "hsn_code": "29021100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC HYDROCARBONS - CYCLANES, CYCLENES AND CYCLOTERPENES : OTHER",
+        "hsn_code": "29021900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC HYDROCARBONS- BENZENE",
+        "hsn_code": "29022000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC HYDROCARBONS -TOLUENE",
+        "hsn_code": "29023000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC HYDROCARBONS - XYLENES : O-XYLENE",
+        "hsn_code": "29024100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC HYDROCARBONS - XYLENES : M-XYLENE",
+        "hsn_code": "29024200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC HYDROCARBONS - XYLENES : P-XYLENE",
+        "hsn_code": "29024300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC HYDROCARBONS - XYLENES : MIXED XYLENE ISOMERS",
+        "hsn_code": "29024400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC HYDROCARBONS - STYRENE",
+        "hsn_code": "29025000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC HYDROCARBONS - ETHYLBENZENE",
+        "hsn_code": "29026000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC HYDROCARBONS - CUMENE",
+        "hsn_code": "29027000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC HYDROCARBONS - OTHER :DIPENTENE",
+        "hsn_code": "29029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC HYDROCARBONS - OTHER :DIPHENYL METHANE",
+        "hsn_code": "29029020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC HYDROCARBONS - OTHER :DODECYCLIC BENZENES (EXCLUDING MIXED ALKYLARENES)",
+        "hsn_code": "29029030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC HYDROCARBONS - OTHER :NAPTHALENE, PURE",
+        "hsn_code": "29029040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC HYDROCARBONS - OTHER :ISOBUTYL BENZENE",
+        "hsn_code": "29029050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC HYDROCARBONS - OTHER :OTHER",
+        "hsn_code": "29029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - SATURATED CHLORINATED DERIVATIVES OF\nACYCLIC HYDROCARBONS :-CHLOROMETHANE (METHYL CHLORIDE) AND CHLOROETHANE (ETHYL\nCHLORIDE) : CHLOROMETHANE (METHYL CHLORIDE)",
+        "hsn_code": "29031110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - SATURATED CHLORINATED DERIVATIVES OF\nACYCLIC HYDROCARBONS :-CHLOROMETHANE (METHYL CHLORIDE) AND CHLOROETHANE (ETHYL\nCHLORIDE) : CHLOROETHANE (ETHYL CHLORIDE)",
+        "hsn_code": "29031120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - SATURATED CHLORINATED DERIVATIVES OF\nACYCLIC HYDROCARBONS : DICHLOROMETHANE (METHYLENE CHLORIDE)",
+        "hsn_code": "29031200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - SATURATED CHLORINATED DERIVATIVES OF\nACYCLIC HYDROCARBONS : CHLOROFORM (TRICHLORO METHANE)",
+        "hsn_code": "29031300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - SATURATED CHLORINATED DERIVATIVES OF\nACYCLIC HYDROCARBONS : CARBON TETRACHLORIDE",
+        "hsn_code": "29031400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - SATURATED CHLORINATED DERIVATIVES OF\nACYCLIC HYDROCARBONS -- ETHYLENE DICHLORIDE (ISO) (1,2-DICHLOROETHANE)",
+        "hsn_code": "29031500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - SATURATED CHLORINATED DERIVATIVES OF\nACYCLIC HYDROCARBONS :-OTHER : TETRACHLOROETHANE",
+        "hsn_code": "29031910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - SATURATED CHLORINATED DERIVATIVES OF\nACYCLIC HYDROCARBONS :-OTHER : TRICHLOROETHANE",
+        "hsn_code": "29031920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - SATURATED CHLORINATED DERIVATIVES OF\nACYCLIC HYDROCARBONS :-OTHER : OTHER",
+        "hsn_code": "29031990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - UNSATURATED CHLORINATED DERIVATIVES\nOF ACYCLIC HYDROCARBONS : VINYL CHLORIDE (CHLOROETHYLENE)",
+        "hsn_code": "29032100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - UNSATURATED CHLORINATED DERIVATIVES\nOF ACYCLIC HYDROCARBONS : TRICHLOROETHYLENE",
+        "hsn_code": "29032200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - UNSATURATED CHLORINATED DERIVATIVES\nOF ACYCLIC HYDROCARBONS : TETRACHLOROETHYLENE (PERCHLOROETHYLENE)",
+        "hsn_code": "29032300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - UNSATURATED CHLORINATED DERIVATIVES\nOF ACYCLIC HYDROCARBONS : OTHER",
+        "hsn_code": "29032900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - FLUORINATED, BROMINATED OR IODINATED\nDERIVATIVES OF ACYCLIC HYDROCARBONS : FLUORINATED DERIVATIVES",
+        "hsn_code": "29033010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "1-PROPENDE, 1,1,3,3,3,-PENTAFLUORO-2(TRIFLUOROMETHYL(PFIB)",
+        "hsn_code": "29033011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FLUORINATED DERIVATIVES - OTHERS",
+        "hsn_code": "29033019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - FLUORINATED, BROMINATED OR IODINATED\nDERIVATIVES OF ACYCLIC HYDROCARBONS : BROMINATED DERIVATIVES",
+        "hsn_code": "29033020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - FLUORINATED, BROMINATED OR IODINATED\nDERIVATIVES OF ACYCLIC HYDROCARBONS : IODINATED DERIVATIVES",
+        "hsn_code": "29033030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - FLUORINATED, BROMINATED OR IODINATED\nDERIVATIVES OF ACYCLIC HYDROCARBONS -- ETHYLENE DIBROMIDE (ISO) (1,2-\nDIBROMOETHANE)",
+        "hsn_code": "29033100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - FLUORINATED, BROMINATED OR IODINATED\nDERIVATIVES OF ACYCLIC HYDROCARBONS -- OTHER --- FLUORINATED DERIVATIVES ---- 1-\nPROPENE, 1, 1, 3,3,3 PENTAFLUORO - 2-(TRIFLUOROMETHYL) (PFIB)",
+        "hsn_code": "29033911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - FLUORINATED, BROMINATED OR IODINATED\nDERIVATIVES OF ACYCLIC HYDROCARBONS -- OTHER --- FLUORINATED DERIVATIVES ---- OTHER",
+        "hsn_code": "29033919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - FLUORINATED, BROMINATED OR IODINATED\nDERIVATIVES OF ACYCLIC HYDROCARBONS -- OTHER --- FLUORINATED DERIVATIVES ----\nBROMINATED DERIVATIVES",
+        "hsn_code": "29033920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - FLUORINATED, BROMINATED OR IODINATED\nDERIVATIVES OF ACYCLIC HYDROCARBONS -- OTHER --- FLUORINATED DERIVATIVES ----\nIODINATED DERIVATIVES",
+        "hsn_code": "29033930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS - FLUORINATED, BROMINATED OR IODINATED\nDERIVATIVES OF ACYCLIC HYDROCARBONS -- OTHER --- OTHER ---- OTHER",
+        "hsn_code": "29033990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nCHLORODIFLUOROMETHANE",
+        "hsn_code": "29037100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nDICHLOROTRIFLUOROETHANES",
+        "hsn_code": "29037200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nDICHLOROFLUOROETHANES",
+        "hsn_code": "29037300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nCHLORODIFLUOROETHANES",
+        "hsn_code": "29037400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nDICHLOROPENTAFLUOROPROPANES",
+        "hsn_code": "29037500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nBROMOCHLORODIFLUOROMETHANE, BROMOTRIFLUOROMETHANE AND\nDIBROMOTETRAFLUOROETHANES; ---BROMOCHLORODIFLUOROMETHANE",
+        "hsn_code": "29037610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nBROMOCHLORODIFLUOROMETHANE, BROMOTRIFLUOROMETHANE AND\nDIBROMOTETRAFLUOROETHANES; ---BROMOTRIFLUOROMETHANE",
+        "hsn_code": "29037620",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nBROMOCHLORODIFLUOROMETHANE, BROMOTRIFLUOROMETHANE AND\nDIBROMOTETRAFLUOROETHANES; ---DIBROMOTETRAFLUOROETHANES",
+        "hsn_code": "29037630",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nOTHER,PERHALOGENATED ONLY WITH FLUORINE AND CHLORINE---CHLOROFLUOROMETHANES--\n--CHLOROTRIFLUOROMETHANE",
+        "hsn_code": "29037711",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nOTHER,PERHALOGENATED ONLY WITH FLUORINE AND CHLORINE---CHLOROFLUOROMETHANES--\n--DICHLORODIFLUOROMETHANE",
+        "hsn_code": "29037712",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nOTHER,PERHALOGENATED ONLY WITH FLUORINE AND CHLORINE---CHLOROFLUOROMETHANES--\n--TRICHLOROFLUOROMETHANE",
+        "hsn_code": "29037713",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nOTHER,PERHALOGENATED ONLY WITH FLUORINE AND CHLORINE---CHLOROFLUOROETHANES----\nCHLOROPENTAFLUOROETHANE",
+        "hsn_code": "29037721",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nOTHER,PERHALOGENATED ONLY WITH FLUORINE AND CHLORINE---CHLOROFLUOROETHANES----\n1,2-DICHLOROTETRAFLUOROETHANE",
+        "hsn_code": "29037722",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nOTHER,PERHALOGENATED ONLY WITH FLUORINE AND CHLORINE---CHLOROFLUOROETHANES----\nTRICHLOROTRIFLUOROETHANE",
+        "hsn_code": "29037723",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nOTHER,PERHALOGENATED ONLY WITH FLUORINE AND CHLORINE---CHLOROFLUOROETHANES----\nTETRACHLORODIFLUOROETHANE",
+        "hsn_code": "29037724",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nOTHER,PERHALOGENATED ONLY WITH FLUORINE AND CHLORINE---CHLOROFLUOROETHANES----\nPENTACHLOROFLUOROETHANE",
+        "hsn_code": "29037725",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nOTHER,PERHALOGENATED ONLY WITH FLUORINE AND CHLORINE---CHLOROFLUOROPROPANES---\n-CHLOROHEPTAFLUOROPROPANE",
+        "hsn_code": "29037731",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nOTHER,PERHALOGENATED ONLY WITH FLUORINE AND CHLORINE---CHLOROFLUOROPROPANES---\n-DICHLOROHEXAFLUOROPROPANE",
+        "hsn_code": "29037732",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nOTHER,PERHALOGENATED ONLY WITH FLUORINE AND CHLORINE---CHLOROFLUOROPROPANES---\n-TRICHLOROPENTAFLUOROPROPANE",
+        "hsn_code": "29037733",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nOTHER,PERHALOGENATED ONLY WITH FLUORINE AND CHLORINE---CHLOROFLUOROPROPANES---\n-TETRACHLOROTETRAFLUOROPROPANE",
+        "hsn_code": "29037734",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nOTHER,PERHALOGENATED ONLY WITH FLUORINE AND CHLORINE---CHLOROFLUOROPROPANES---\n-PENTACHLOROTRIFLUOROPROPANE",
+        "hsn_code": "29037735",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nOTHER,PERHALOGENATED ONLY WITH FLUORINE AND CHLORINE---CHLOROFLUOROPROPANES\nHEXACHLORODIFLUOROPROPANE",
+        "hsn_code": "29037736",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nOTHER,PERHALOGENATED ONLY WITH FLUORINE AND CHLORINE---CHLOROFLUOROPROPANES---\n-HEPTACHLOROFLUOROPROPANE",
+        "hsn_code": "29037737",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nOTHER,PERHALOGENATED ONLY WITH FLUORINE AND CHLORINE---OTHER DERIVATIVES ,\nPERHALOGENATEDONLY WITH FLUORINE AND CHLORINE",
+        "hsn_code": "29037790",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--\nOTHER,PERHALOGENATED DERIVATIVES",
+        "hsn_code": "29037800",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF ACYCLIC\nHYDROCARBONS CONTAINING TWO OR MORE DIFFERENT HALOGENS--OTHER",
+        "hsn_code": "29037900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF CYCLANIC,\nCYCLENIC OR CYCLOTERPENIC HYDROCARBONS--1,2,3,4,5,6-HEXACHLOROCYCLOHEXANE [HCH\n(ISO)], INCLUDING LINDANE(ISO,INN)",
+        "hsn_code": "29038100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF CYCLANIC,\nCYCLENIC OR CYCLOTERPENIC HYDROCARBONS--ALDRIN (ISO),CHLORDANE(ISO)",
+        "hsn_code": "29038200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF CYCLANIC,\nCYCLENIC OR CYCLOTERPENIC HYDROCARBONS--OTHER",
+        "hsn_code": "29038900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF AROMATIC\nHYDROCARBONS--CHLOROBENZENE,O-DICHLOROBENZENE AND P-DICHLOROBENZENE---\nCHLOROBENZENE (MONOCHLORO)",
+        "hsn_code": "29039110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF AROMATIC\nHYDROCARBONS--CHLORBENZENE,O-DICHLOROBENZENE AND P-DICHLOROBENZENE---O\nDICHLOROBENZENE (ORTHODICHLOROBENZENE)",
+        "hsn_code": "29039120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF AROMATIC\nHYDROCARBONS--CHLORBENZENE,O-DICHLOROBENZENE AND P-DICHLOROBENZENE---P\nDICHLOROBENZENE(PARADICHLOROBENZENE)",
+        "hsn_code": "29039130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF AROMATIC\nHYDROCARBONS--HEXACHLOROBENZENE (ISO) AND DDT(ISO)[CLOFENOTANE(INN),1,11-\nTRICHLORO-2,2-BIS (P-CHLOROPHENYL) ETHANE]---HEXACHLOROBENZENE (ISO) AND\nDDT(ISO)[CLOFENOTANE(INN),1,11-TRICHLORO-2,2-BIS (P-CHLOROPHENYL) ETHANE]",
+        "hsn_code": "29039210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF AROMATIC\nHYDROCARBONS--HEXACHLOROBENZENE (ISO) AND DDT(ISO)[CLOFENOTANE(INN),1,11-\nTRICHLORO-2,2-BIS (P-CHLOROPHENYL) ETHANE----DDT TECHNICAL 75 WDP",
+        "hsn_code": "29039221",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF AROMATIC\nHYDROCARBONS--HEXACHLOROBENZENE (ISO) AND DDT(ISO)[CLOFENOTANE(INN),1,11-\nTRICHLORO-2,2-BIS (P-CHLOROPHENYL) ETHANE----OTHER",
+        "hsn_code": "29039229",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF AROMATIC\nHYDROCARBONS---OTHER---CHLOROFLUOROBENZENE",
+        "hsn_code": "29039910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF AROMATIC\nHYDROCARBONS--OTHER---BENZALCHLORIDE (BENZYL DICHLORIDE)",
+        "hsn_code": "29039920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF AROMATIC\nHYDROCARBONS--OTHER---BENZOTRICHLORIDE",
+        "hsn_code": "29039930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF AROMATIC\nHYDROCARBONS--OTHER----BENZYLCHLORIDE",
+        "hsn_code": "29039940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF AROMATIC\nHYDROCARBONS--OTHER----PARACHLOROTOLUENE (4-CHLOROMETHYL BENZENE)",
+        "hsn_code": "29039950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF AROMATIC\nHYDROCARBONS--OTHER----NAPHTHALENE,CHLORINATED",
+        "hsn_code": "29039960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF AROMATIC\nHYDROCARBONS--OTHER----CHLOROFLUORO ANILINE",
+        "hsn_code": "29039970",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERVIATIVES OF HYDROCARBONS-HALOGENATED DERIVATIVES OF AROMATIC\nHYDROCARBONS--OTHER----OTHER",
+        "hsn_code": "29039990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - DERIVATIVES CONTAINING ONLY SULPHO GROUPS, THEIR SALTS AND\nETHYL ESTERS : BENZENE SULPHONIC ACID",
+        "hsn_code": "29041010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - DERIVATIVES CONTAINING ONLY SULPHO GROUPS, THEIR SALTS AND\nETHYL ESTERS : 1,5 NAPTHELENE DISULPHONIC ACID (ARMSTRONGS ACID)",
+        "hsn_code": "29041020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - DERIVATIVES CONTAINING ONLY SULPHO GROUPS, THEIR SALTS AND\nETHYL ESTERS : NAPTHELENE SULPHONIC ACID",
+        "hsn_code": "29041030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - DERIVATIVES CONTAINING ONLY SULPHO GROUPS, THEIR SALTS AND\nETHYL ESTERS : VINYL SULPHONE",
+        "hsn_code": "29041040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - DERIVATIVES CONTAINING ONLY SULPHO GROUPS, THEIR SALTS AND\nETHYL ESTERS : OTHER",
+        "hsn_code": "29041090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - DERIVATIVES CONTAINING ONLY NITRO OR ONLY NITROSO GROUPS :\nNITROBENZENE",
+        "hsn_code": "29042010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - DERIVATIVES CONTAINING ONLY NITRO OR ONLY NITROSO GROUPS :\nMETA DINITROBENZENE",
+        "hsn_code": "29042020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - DERIVATIVES CONTAINING ONLY NITRO OR ONLY NITROSO GROUPS :\nMETA NITROTOLUENE",
+        "hsn_code": "29042030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - DERIVATIVES CONTAINING ONLY NITRO OR ONLY NITROSO GROUPS :\nORTHO NITROTOLUENE",
+        "hsn_code": "29042040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - DERIVATIVES CONTAINING ONLY NITRO OR ONLY NITROSO GROUPS :\nPARA NITROTOLUENE",
+        "hsn_code": "29042050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - DERIVATIVES CONTAINING ONLY NITRO OR ONLY NITROSO GROUPS :\nDINITROTOLUENE",
+        "hsn_code": "29042060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - DERIVATIVES CONTAINING ONLY NITRO OR ONLY NITROSO GROUPS :\nOTHER",
+        "hsn_code": "29042090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - OTHER : 2,5 DICHLORO NITROBENZENE",
+        "hsn_code": "29049010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - OTHER : DINITROCHLOROBENZENE",
+        "hsn_code": "29049020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - OTHER : META NITROCHLOROBENZENE",
+        "hsn_code": "29049030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - OTHER : ORTHO NITROCHLOROBENZENE",
+        "hsn_code": "29049040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - OTHER : PARA NITROCHLOROBENZENE",
+        "hsn_code": "29049050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - OTHER : 2-NITROCHLOROTOLUENE",
+        "hsn_code": "29049060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - OTHER : SODIUM META NITROBENZENE SULPHONATE",
+        "hsn_code": "29049070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLOROPICRIN(TRICHLORONITRO-METHANE)",
+        "hsn_code": "29049080",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR\nNOT HALOGENATED - OTHER : OTHER",
+        "hsn_code": "29049090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - SATURATED MONOHYDRIC ALCOHOLS : METHANOL (METHYL ALCOHOL)",
+        "hsn_code": "29051100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - SATURATED MONOHYDRIC ALCOHOLS :-PROPAN-1-O1 (PROPYL ALCOHOL) AND\nPROPAN- 2-OL (ISOPROPYL ALCOHOL) : PROPYL ALCOHOL",
+        "hsn_code": "29051210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - SATURATED MONOHYDRIC ALCOHOLS :-PROPAN-1-O1 (PROPYL ALCOHOL) AND\nPROPAN- 2-OL (ISOPROPYL ALCOHOL) : ISOPROPYL ALCOHOL",
+        "hsn_code": "29051220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - SATURATED MONOHYDRIC ALCOHOLS : BUTAN-1-OL (N-BUTYL ALCOHOL)",
+        "hsn_code": "29051300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - SATURATED MONOHYDRIC ALCOHOLS :-OTHER BUTANOLS : ETHAMBUTOL,\nETHAMBUTOL HCL",
+        "hsn_code": "29051410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - SATURATED MONOHYDRIC ALCOHOLS :-OTHER BUTANOLS : SALBUTAMOL\nSULPHATE",
+        "hsn_code": "29051420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - SATURATED MONOHYDRIC ALCOHOLS :-OTHER BUTANOLS : AMINO BUTANOL",
+        "hsn_code": "29051430",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - SATURATED MONOHYDRIC ALCOHOLS :-OTHER BUTANOLS : OTHER",
+        "hsn_code": "29051490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - SATURATED MONOHYDRIC ALCOHOLS : PENTANOL (AMYL ALCOHOL) AND\nISOMERS THEREOF",
+        "hsn_code": "29051500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - SATURATED MONOHYDRIC ALCOHOLS : OCTANOL (OCTYL ALCOHOL) AND\nISOMERS THEREOF : DIMETHYL OCTANOL",
+        "hsn_code": "29051610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - SATURATED MONOHYDRIC ALCOHOLS : OCTANOL (OCTYL ALCOHOL) AND\nISOMERS THEREOF : 2-ETHYL HEXANOL",
+        "hsn_code": "29051620",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - SATURATED MONOHYDRIC ALCOHOLS : OCTANOL (OCTYL ALCOHOL) AND\nISOMERS THEREOF : OTHER",
+        "hsn_code": "29051690",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - SATURATED MONOHYDRIC ALCOHOLS : DODECAN-1-OL (LAURYL\nALCOHOL),HEXADECAN-1-OL (CETYL ALCOHOL) AND OCTADECAN-1-OL (STEARYL ALCOHOL)",
+        "hsn_code": "29051700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - SATURATED MONOHYDRIC ALCOHOLS : OTHER",
+        "hsn_code": "29051900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "2-BUTANOL, 3, 3-DIMETHYL-",
+        "hsn_code": "29051910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - SATURATED MONOHYDRIC ALCOHOLS : OTHER",
+        "hsn_code": "29051990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - UNSATURATED MONOHYDRIC ALCOHOLS :-ACYCLIC TERPENE ALCOHOLS :\nCITRANELLOL",
+        "hsn_code": "29052210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - UNSATURATED MONOHYDRIC ALCOHOLS :-ACYCLIC TERPENE ALCOHOLS :\nGERANIOL",
+        "hsn_code": "29052220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - UNSATURATED MONOHYDRIC ALCOHOLS :-ACYCLIC TERPENE ALCOHOLS :\nLINALOOL",
+        "hsn_code": "29052230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - UNSATURATED MONOHYDRIC ALCOHOLS :-ACYCLIC TERPENE ALCOHOLS :\nRHODINOL",
+        "hsn_code": "29052240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - UNSATURATED MONOHYDRIC ALCOHOLS :-ACYCLIC TERPENE ALCOHOLS : OTHER",
+        "hsn_code": "29052290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - UNSATURATED MONOHYDRIC ALCOHOLS : OTHER",
+        "hsn_code": "29052900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - DIOLS : ETHYLENE GLYCOL (ETHANEDIOL)",
+        "hsn_code": "29053100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - DIOLS : PROPYLENE GLYCOL (PROPANE-1,2-DIOL)",
+        "hsn_code": "29053200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - DIOLS :-OTHER : 1,4/1,3/2,3-BUTYLENE GLYCOL",
+        "hsn_code": "29053910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - DIOLS :-OTHER : OTHER",
+        "hsn_code": "29053990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - OTHER POLYHYDRIC ALCOHOLS : 2- ETHYL-2-(HYDROXYMETHYL) PROPANE-1,3-\nDIOL (TRIMETHYLOLROPANE)",
+        "hsn_code": "29054100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - OTHER POLYHYDRIC ALCOHOLS :-PENTAERYTHRITOL : DIPENTAERYTHRITOL",
+        "hsn_code": "29054210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - OTHER POLYHYDRIC ALCOHOLS :-PENTAERYTHRITOL : OTHER",
+        "hsn_code": "29054290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - OTHER POLYHYDRIC ALCOHOLS : MANNITOL",
+        "hsn_code": "29054300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - OTHER POLYHYDRIC ALCOHOLS : D-GLUCITOL (SORBITOL)",
+        "hsn_code": "29054400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - OTHER POLYHYDRIC ALCOHOLS : GLYCEROL",
+        "hsn_code": "29054500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - OTHER POLYHYDRIC ALCOHOLS : OTHER",
+        "hsn_code": "29054900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF\nACYCLIC ALCOHOLS : ETHCHLORVYNOL (INN)",
+        "hsn_code": "29055100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF\nACYCLIC ALCOHOLS : OTHER",
+        "hsn_code": "29055900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - CYCLANIC, CYCLENIC OR CYCLOTERPENIC : MENTHOL",
+        "hsn_code": "29061100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - CYCLANIC, CYCLENIC OR CYCLOTERPENIC : CYCLOHEXANOL,\nMETHYLCYCLOHEXANOLS AND DIMETHYLCYCLOHEXANOLS",
+        "hsn_code": "29061200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - CYCLANIC, CYCLENIC OR CYCLOTERPENIC :-STEROLS AND INOSITOLS :\nCHOLESTEROL",
+        "hsn_code": "29061310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - CYCLANIC, CYCLENIC OR CYCLOTERPENIC :-STEROLS AND INOSITOLS : OTHER",
+        "hsn_code": "29061390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - CYCLANIC, CYCLENIC OR CYCLOTERPENIC : TERPINEOLS",
+        "hsn_code": "29061400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - CYCLANIC, CYCLENIC OR CYCLOTERPENIC :-OTHER : BORNEOL",
+        "hsn_code": "29061910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - CYCLANIC, CYCLENIC OR CYCLOTERPENIC :-OTHER : OTHER",
+        "hsn_code": "29061990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - AROMATIC : BENZYL ALCOHOL",
+        "hsn_code": "29062100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - AROMATIC : OTHER : CINNAMIC ALCOHOL",
+        "hsn_code": "29062910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - AROMATIC : OTHER : PHENYLETHYL ALCOHOL",
+        "hsn_code": "29062920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - AROMATIC : OTHER : OTHER",
+        "hsn_code": "29062990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - MONOPHENOLS :-PHENOL (HYDROXYBENZENE) AND ITS SALTS :\nPHENOL, AS PURE CARBOLIC ACID",
+        "hsn_code": "29071110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - MONOPHENOLS :-PHENOL (HYDROXYBENZENE) AND ITS SALTS :\nOTHER",
+        "hsn_code": "29071190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - MONOPHENOLS :-CRESOLS AND THEIR SALTS : PARA CRESOLS (P\nCRESOLS)",
+        "hsn_code": "29071210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - MONOPHENOLS :-CRESOLS AND THEIR SALTS : CRESYLIC ACID",
+        "hsn_code": "29071220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - MONOPHENOLS :-CRESOLS AND THEIR SALTS : OTHER",
+        "hsn_code": "29071290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - MONOPHENOLS : OCTYLPHENOL, NONYLPHENOL AND THEIR\nISOMERS; SALTS THEREOF",
+        "hsn_code": "29071300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - MONOPHENOLS : XYLENOLS AND THEIR SALTS",
+        "hsn_code": "29071400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - MONOPHENOLS : NAPHTHOLS AND THEIR SALTS : ALPHA\nNAPHTHOLS",
+        "hsn_code": "29071510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - MONOPHENOLS : NAPHTHOLS AND THEIR SALTS : BETA\nNAPHTHOLS",
+        "hsn_code": "29071520",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - MONOPHENOLS : NAPHTHOLS AND THEIR SALTS : OTHER",
+        "hsn_code": "29071590",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - MONOPHENOLS : OTHER : O-PHENYL PHENOLS",
+        "hsn_code": "29071910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - MONOPHENOLS : OTHER : P-PHENYL PHENOLS",
+        "hsn_code": "29071920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - MONOPHENOLS : OTHER : THYMOL",
+        "hsn_code": "29071930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - MONOPHENOLS : OTHER : PARA TERTIARY BUTYL PHENOL",
+        "hsn_code": "29071940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - MONOPHENOLS : OTHER : ALKYL PHENOLS",
+        "hsn_code": "29071950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - MONOPHENOLS : OTHER : OTHER",
+        "hsn_code": "29071990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - POLYPHENOLS ; PHENOL-ALCOHOLS : RESORCINOL AND ITS\nSALTS",
+        "hsn_code": "29072100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - POLYPHENOLS ; PHENOL-ALCOHOLS : HYDROQUINONE\n(QUINOL) AND ITS SALTS",
+        "hsn_code": "29072200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - POLYPHENOLS ; PHENOL-ALCOHOLS : 4,4 -\nISOPROPYLIDENEDIPHENOL (BIS-PHENOL A, DIPHENYLOLPROPANE) AND ITS SALTS",
+        "hsn_code": "29072300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - POLYPHENOLS ; PHENOL-ALCOHOLS : OTHER : 1,5- DIHYDROXY\nNAPHTHALENE",
+        "hsn_code": "29072910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS - POLYPHENOLS ; PHENOL-ALCOHOLS : OTHER : OTHER",
+        "hsn_code": "29072990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS - DERIVATIVES CONTAINING ONLY HALOGEN SUBSTITUENTS AND THEIR\nSALTS",
+        "hsn_code": "29081000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS - DERIVATIVES CONTAINING ONLY HALOGEN SUBSTITUENTS AND THEIR\nSALTS -- PENTACHLOROPHENOL (ISO)",
+        "hsn_code": "29081100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS - DERIVATIVES CONTAINING ONLY HALOGEN SUBSTITUENTS AND THEIR\nSALTS -- OTHER",
+        "hsn_code": "29081900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS - DERIVATIVES CONTAINING ONLY SULPHO GROUPS, THEIR SALTS AND\nESTERS : PHENOL SULPHONIC ACIDS",
+        "hsn_code": "29082010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS - DERIVATIVES CONTAINING ONLY SULPHO GROUPS, THEIR SALTS AND\nESTERS : NAPHTHOL SULPHONIC ACIDS : G ACIDS (2-NAPHTHOL-6,8-DISULPHONIC ACID)",
+        "hsn_code": "29082021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS - DERIVATIVES CONTAINING ONLY SULPHO GROUPS, THEIR SALTS AND\nESTERS : NAPHTHOL SULPHONIC ACIDS : SALTS OF G ACID",
+        "hsn_code": "29082022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS - DERIVATIVES CONTAINING ONLY SULPHO GROUPS, THEIR SALTS AND\nESTERS : NAPHTHOL SULPHONIC ACIDS : BETA NAPHTHOL SULPHONIC ACIDS",
+        "hsn_code": "29082023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS - DERIVATIVES CONTAINING ONLY SULPHO GROUPS, THEIR SALTS AND\nESTERS : NAPHTHOL SULPHONIC ACIDS : NEVILE -WINTHER ACID (1-NAPHTHOL- 4- SULPHONIC\nACID)",
+        "hsn_code": "29082024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS - DERIVATIVES CONTAINING ONLY SULPHO GROUPS, THEIR SALTS AND\nESTERS : NAPHTHOL SULPHONIC ACIDS : SCHAEFFER ACID (2-NAPHTHOL-6-SULPHONIC ACID)",
+        "hsn_code": "29082025",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS - DERIVATIVES CONTAINING ONLY SULPHO GROUPS, THEIR SALTS AND\nESTERS : NAPHTHOL SULPHONIC ACIDS : R ACIDS (2-NAPHTHOL- 3,6- DISULPHONIC ACID) AND\nITS DISODIUM SALT (SALT OF R ACID)",
+        "hsn_code": "29082026",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS - DERIVATIVES CONTAINING ONLY SULPHO GROUPS, THEIR SALTS AND\nESTERS : NAPHTHOL SULPHONIC ACIDS : CHROMOTROPIC ACID (1,8-DIHYDROXYNAPHTHALENE -\n3,6-DISULFONIC ACID)",
+        "hsn_code": "29082027",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS - DERIVATIVES CONTAINING ONLY SULPHO GROUPS, THEIR SALTS AND\nESTERS : NAPHTHOL SULPHONIC ACIDS : OTHER",
+        "hsn_code": "29082029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS - OTHER : PARA NITROPHENOL",
+        "hsn_code": "29089010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS - OTHER : MUSK XYLOL",
+        "hsn_code": "29089020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS - OTHER : OTHER",
+        "hsn_code": "29089090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS - OTHER -- DINOSEB (ISO) AND ITS SALTS",
+        "hsn_code": "29089100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED,SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS-DERIVATIVES CONTAINING ONLY HALOGEN SUBSTITUENTS AND THEIR SALTS-\n-4,6-DINITRO-O-CRESOL [DNOC (ISO)] AND ITS SALTS",
+        "hsn_code": "29089200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS - OTHER -- OTHER --- PARA NITROPHENOL",
+        "hsn_code": "29089910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS - OTHER -- OTHER --- MUSK XYLOL",
+        "hsn_code": "29089920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR\nPHENOL-ALCOHOLS - OTHER -- OTHER --- OTHER",
+        "hsn_code": "29089990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHERALCOHOL- PHENOLS, ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC ETHERS AND\nTHEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES : DIETHYL ETHER",
+        "hsn_code": "29091100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHERALCOHOL- PHENOLS, ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC ETHERS AND\nTHEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES : OTHER",
+        "hsn_code": "29091900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHERALCOHOL- PHENOLS, ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES CYCLANIC, CYCLENIC\nOR CYCLOTERPENIC ETHERS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES",
+        "hsn_code": "29092000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHERALCOHOL- PHENOLS, ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC ETHERS\nAND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES : ANISOLE\nAND THEIR DERIVATIVES : 4-CHLORO-2-NITRO ANISOLE",
+        "hsn_code": "29093011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHERALCOHOL- PHENOLS, ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC ETHERS\nAND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES : ANISOLE\nAND THEIR DERIVATIVES : ORTHO NITRO ANISOLE",
+        "hsn_code": "29093012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHERALCOHOL- PHENOLS, ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC ETHERS\nAND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES : ANISOLE\nAND THEIR DERIVATIVES : OTHER",
+        "hsn_code": "29093019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHERALCOHOL- PHENOLS, ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC ETHERS\nAND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES : DIPHENYL\nOXIDE",
+        "hsn_code": "29093020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHERALCOHOL- PHENOLS, ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC ETHERS\nAND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES : MUSK\nAMBRETTE",
+        "hsn_code": "29093030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHERALCOHOL- PHENOLS, ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC ETHERS\nAND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES : OTHER",
+        "hsn_code": "29093090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHERALCOHOL- PHENOLS, ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ETHER-ALCOHOLS\nAND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES : 2,2-\nOXYDIETHANOL (DIETHYLENE GLYCOL, DIGOL)",
+        "hsn_code": "29094100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHERALCOHOL- PHENOLS, ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ETHER-ALCOHOLS\nAND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES :\nMONOMETHYL ETHERS OF ETHYLENE GLYCOL OR OF DIETHYLENE GLYCOL",
+        "hsn_code": "29094200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHERALCOHOL- PHENOLS, ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ETHER-ALCOHOLS\nAND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES :\nMONOBUTYL ETHERS OF ETHYLENE GLYCOL OR OF DIETHYLENE GLYCOL",
+        "hsn_code": "29094300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHERALCOHOL- PHENOLS, ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ETHER-ALCOHOLS\nAND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES : OTHER\nMONOALKYLETHERS OF ETHYLENE GLYCOL OR OF DIETHYLENE GLYCOL",
+        "hsn_code": "29094400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHERALCOHOL- PHENOLS, ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ETHER-ALCOHOLS\nAND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES : OTHER",
+        "hsn_code": "29094900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHERALCOHOL- PHENOLS, ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ETHER-PHENOLS,\nETHER-ALCOHOL-PHENOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES : GUAIACOL",
+        "hsn_code": "29095010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHERALCOHOL- PHENOLS, ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ETHER-PHENOLS,\nETHER-ALCOHOL-PHENOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES : ISOEUGENOL",
+        "hsn_code": "29095020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHERALCOHOL- PHENOLS, ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ETHER-PHENOLS,\nETHER-ALCOHOL-PHENOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES : POTASSIUM GUAIACOL SULPHONATE",
+        "hsn_code": "29095030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHERALCOHOL- PHENOLS, ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ETHER-PHENOLS,\nETHER-ALCOHOL-PHENOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES : OTHER",
+        "hsn_code": "29095090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHERALCOHOL- PHENOLS, ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES -ALCOHOL PEROXIDES,\nETHER PEROXIDES, KETONE PEROXIDES AND THEIR HALOGENATED, SULPHONATED, NITRATED\nOR NITROSATED DERIVATIVES",
+        "hsn_code": "29096000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "EPOXIDES, EPOXYALCOHOLS, EPOXYPHENOLS AND EXPOXYETHERS, WITH A THREE-MEMBERED\nRING, AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES -\nOXIRANE (ETHYLENE OXIDE)",
+        "hsn_code": "29101000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "EPOXIDES, EPOXYALCOHOLS, EPOXYPHENOLS AND EXPOXYETHERS, WITH A THREE-MEMBERED\nRING, AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES -\nMETHYLOXIRANE (PROPYLENE OXIDE)",
+        "hsn_code": "29102000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "EPOXIDES, EPOXYALCOHOLS, EPOXYPHENOLS AND EXPOXYETHERS, WITH A THREE-MEMBERED\nRING, AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES -1-\nCHLORO-2,3-EXPOXYPROPANE (EPICHLOROHYDRIN)",
+        "hsn_code": "29103000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "EPOXIDES, EPOXYALCOHOLS, EPOXYPHENOLS AND EXPOXYETHERS, WITH A THREE-MEMBERED\nRING, AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES -\nDIELDRIN (ISO, INN)",
+        "hsn_code": "29104000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "EPOXIDES, EPOXYALCOHOLS, EPOXYPHENOLS AND EXPOXYETHERS, WITH A THREE-MEMBERED\nRING, AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES -\nOTHER",
+        "hsn_code": "29109000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACETALS AND HEMIACETALS, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACETALS AND\nHEMIACETALS, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR HALOGENATED,\nSULPHONATED, NITRATED OR NITROSATED DERIVATIVES : ACETALS AND HEMIACETALS,\nWHETHER OR NOT WITH OTHER OXYGEN FUNCTION",
+        "hsn_code": "29110010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACETALS AND HEMIACETALS, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACETALS AND\nHEMIACETALS, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR HALOGENATED,\nSULPHONATED, NITRATED OR NITROSATED DERIVATIVES : OTHER",
+        "hsn_code": "29110090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE - ACYCLIC ALDEHYDES WITHOUT OTHER OXYGEN FUNCTION :\nMETHANAL (FORMALDEHYDE)",
+        "hsn_code": "29121100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE - ACYCLIC ALDEHYDES WITHOUT OTHER OXYGEN FUNCTION :\nETHANAL (ACETALDEHYDE)",
+        "hsn_code": "29121200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE - ACYCLIC ALDEHYDES WITHOUT OTHER OXYGEN FUNCTION :\nBUTANAL (BUTYRALDEHYDE, NORMAL ISOMER)",
+        "hsn_code": "29121300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE - ACYCLIC ALDEHYDES WITHOUT OTHER OXYGEN FUNCTION :\nOTHER : CROTONALDEHYDE",
+        "hsn_code": "29121910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE - ACYCLIC ALDEHYDES WITHOUT OTHER OXYGEN FUNCTION :\nOTHER : HEPTALDEHYDE (HEPTANAL)",
+        "hsn_code": "29121920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE - ACYCLIC ALDEHYDES WITHOUT OTHER OXYGEN FUNCTION :\nOTHER : GLYOXAL",
+        "hsn_code": "29121930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE - ACYCLIC ALDEHYDES WITHOUT OTHER OXYGEN FUNCTION :\nOTHER : OTHER",
+        "hsn_code": "29121990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE - CYCLIC ALDEHYDES WITHOUT OTHER OXYGEN FUNCTION :\nBENZALDEHYDE",
+        "hsn_code": "29122100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE - CYCLIC ALDEHYDES WITHOUT OTHER OXYGEN FUNCTION :\nOTHER : CINNAMIC ALDEHYDE",
+        "hsn_code": "29122910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE - CYCLIC ALDEHYDES WITHOUT OTHER OXYGEN FUNCTION :\nOTHER : PHENYL ACETALDEHYDE",
+        "hsn_code": "29122920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE - CYCLIC ALDEHYDES WITHOUT OTHER OXYGEN FUNCTION :\nOTHER : OTHER",
+        "hsn_code": "29122990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE - ALDEHYDE ETHERS, ALDEHYDE PHENOLS AND ALDEHYDES\nWITH OTHER OXYGEN FUNCTION : VANILLIN (4-HYDROXY-3-METHOXYBENZALDEHYDE)",
+        "hsn_code": "29124100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE - ALDEHYDE ETHERS, ALDEHYDE PHENOLS AND ALDEHYDES\nWITH OTHER OXYGEN FUNCTION : ETHYLVANILLIN (3-ETHOXY-4-HYDROXY-BENZALDEHYDE)",
+        "hsn_code": "29124200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE - ALDEHYDE ETHERS, ALDEHYDE PHENOLS AND ALDEHYDES\nWITH OTHER OXYGEN FUNCTION : OTHER :ANISIC ALDEHYDE (ANISALDEHYDE)",
+        "hsn_code": "29124910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE - ALDEHYDE ETHERS, ALDEHYDE PHENOLS AND ALDEHYDES\nWITH OTHER OXYGEN FUNCTION : OTHER :HELIOTROPIN (PIPERONYL ALDEHYDE)",
+        "hsn_code": "29124920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE - ALDEHYDE ETHERS, ALDEHYDE PHENOLS AND ALDEHYDES\nWITH OTHER OXYGEN FUNCTION : OTHER :THIACETAZONE",
+        "hsn_code": "29124930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE - ALDEHYDE ETHERS, ALDEHYDE PHENOLS AND ALDEHYDES\nWITH OTHER OXYGEN FUNCTION : OTHER :3,4,5-TRIMETHOXY-BENZALDEHYDE",
+        "hsn_code": "29124940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE - ALDEHYDE ETHERS, ALDEHYDE PHENOLS AND ALDEHYDES\nWITH OTHER OXYGEN FUNCTION -- OTHER ---OTHER----ALDEHYDE-ALCOHOLS",
+        "hsn_code": "29124991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE - ALDEHYDE ETHERS, ALDEHYDE PHENOLS AND ALDEHYDES\nWITH OTHER OXYGEN FUNCTION -- OTHER ---OTHER----OTHER",
+        "hsn_code": "29124999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE -CYCLIC POLYMERS OF ALDEHYDES",
+        "hsn_code": "29125000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALDEHYDES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OF\nALDEHYDES; PARAFORMALDEHYDE -PARAFORMALDEHYDE",
+        "hsn_code": "29126000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PRODUCTS OF\nHEADING 2912 2913 00 - HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES OF PRODUCTS OF HEADING 2912 : ORTHO-CHLORO-BENZALDEHYDE",
+        "hsn_code": "29130010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PRODUCTS OF\nHEADING 2912 2913 00 - HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES OF PRODUCTS OF HEADING 2912 : OTHER",
+        "hsn_code": "29130090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC KETONES\nWITHOUT OTHER OXYGEN FUNCTION : ACETONE",
+        "hsn_code": "29141100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC KETONES\nWITHOUT OTHER OXYGEN FUNCTION : BUTANONE (METHYL ETHYL KETONE)",
+        "hsn_code": "29141200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC KETONES\nWITHOUT OTHER OXYGEN FUNCTION : 4-METHYLPENTAN-2-ONE (METHYL ISOBUTYL KETONE)",
+        "hsn_code": "29141300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC KETONES\nWITHOUT OTHER OXYGEN FUNCTION :-OTHER : ISOPHORON",
+        "hsn_code": "29141910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC KETONES\nWITHOUT OTHER OXYGEN FUNCTION :-OTHER : OTHER",
+        "hsn_code": "29141990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - CYCLANIC, CYCLENIC\nOR CYCLOTERPENIC KETONES WITHOUT OTHER OXYGEN FUNCTION : CYCLOHEXANONE AND\nMETHYL-CYCLOHEXANONES",
+        "hsn_code": "29142200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - CYCLANIC, CYCLENIC\nOR CYCLOTERPENIC KETONES WITHOUT OTHER OXYGEN FUNCTION :-IONONES AND\nMETHYLIONONES : BETA-IONONE",
+        "hsn_code": "29142310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - CYCLANIC, CYCLENIC\nOR CYCLOTERPENIC KETONES WITHOUT OTHER OXYGEN FUNCTION :-IONONES AND\nMETHYLIONONES : PSEUDO IONONE",
+        "hsn_code": "29142320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - CYCLANIC, CYCLENIC\nOR CYCLOTERPENIC KETONES WITHOUT OTHER OXYGEN FUNCTION :-IONONES AND\nMETHYLIONONES : OTHER",
+        "hsn_code": "29142390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - CYCLANIC, CYCLENIC\nOR CYCLOTERPENIC KETONES WITHOUT OTHER OXYGEN FUNCTION : OTHER : L-CARAVONE",
+        "hsn_code": "29142910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED,SULPHONATED, NITRATED OR NITROSATED DERIVATIVES--OTHER---CAMPHOR----\nNATURAL",
+        "hsn_code": "29142921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED,SULPHONATED, NITRATED OR NITROSATED DERIVATIVES--OTHER---CAMPHOR----\nSYNTHETIC",
+        "hsn_code": "29142922",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - CYCLANIC, CYCLENIC\nOR CYCLOTERPENIC KETONES WITHOUT OTHER OXYGEN FUNCTION : OTHER : OTHER",
+        "hsn_code": "29142990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC KETONES\nWITHOUT OTHER OXYGEN FUNCTION : PHENYLACETONE (PHENYLPROPAN-2-ONE)",
+        "hsn_code": "29143100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC KETONES\nWITHOUT OTHER OXYGEN FUNCTION : OTHER : ACETO PHENONE",
+        "hsn_code": "29143910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC KETONES\nWITHOUT OTHER OXYGEN FUNCTION : OTHER : BENZANTHRONE",
+        "hsn_code": "29143920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC KETONES\nWITHOUT OTHER OXYGEN FUNCTION : OTHER : BENZOPHENONE",
+        "hsn_code": "29143930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC KETONES\nWITHOUT OTHER OXYGEN FUNCTION : OTHER : DIBENZANTHRONE (VIOLANTHRONE)",
+        "hsn_code": "29143940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC KETONES\nWITHOUT OTHER OXYGEN FUNCTION : OTHER : OTHER",
+        "hsn_code": "29143990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES KETONE-ALCOHOLS\nAND KETONE-ALDEHYDES",
+        "hsn_code": "29144000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES KETONE-PHENOLS\nAND KETONES WITH OTHER OXYGEN FUNCTION",
+        "hsn_code": "29145000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - QUINONES :\nANTHRAQUINONE",
+        "hsn_code": "29146100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - QUINONES :-OTHER :\n1,4- DIHYDROXY ANTHRAQUINONE (QUINIZARIN)",
+        "hsn_code": "29146910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - QUINONES :-OTHER :\nMETHYL ANTHRAQUINONE",
+        "hsn_code": "29146920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - QUINONES :-OTHER :\nOTHER",
+        "hsn_code": "29146990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - HALOGENATED,\nSULPHONATED, NITRATED OR NITROSATED DERIVATIVES : 1-CHLORO ANTHRA QUINONE",
+        "hsn_code": "29147010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - HALOGENATED,\nSULPHONATED, NITRATED OR NITROSATED DERIVATIVES : MUSK KETONE",
+        "hsn_code": "29147020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - HALOGENATED,\nSULPHONATED, NITRATED OR NITROSATED DERIVATIVES : OTHER",
+        "hsn_code": "29147090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - FORMIC ACID, ITS SALTS AND ESTERS : FORMIC ACID",
+        "hsn_code": "29151100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - FORMIC ACID, ITS SALTS AND ESTERS : SALTS OF FORMIC ACID : SODIUM\nFORMATE",
+        "hsn_code": "29151210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - FORMIC ACID, ITS SALTS AND ESTERS : SALTS OF FORMIC ACID : OTHER",
+        "hsn_code": "29151290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - FORMIC ACID, ITS SALTS AND ESTERS : ESTERS OF FORMIC ACID",
+        "hsn_code": "29151300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ACETIC ACID AND ITS SALTS; ACETIC ANHYDRIDE : ACETIC ACID",
+        "hsn_code": "29152100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ACETIC ACID AND ITS SALTS; ACETIC ANHYDRIDE : SODIUM ACETATE",
+        "hsn_code": "29152200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ACETIC ACID AND ITS SALTS; ACETIC ANHYDRIDE : COBALT ACETATES",
+        "hsn_code": "29152300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ACETIC ACID AND ITS SALTS; ACETIC ANHYDRIDE : ACETIC ANHYDRIDE",
+        "hsn_code": "29152400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ACETIC ACID AND ITS SALTS; ACETIC ANHYDRIDE :-OTHER : CALCIUM ACETATE",
+        "hsn_code": "29152910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ACETIC ACID AND ITS SALTS; ACETIC ANHYDRIDE :-OTHER : MAGNESIUM ACETATE",
+        "hsn_code": "29152920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ACETIC ACID AND ITS SALTS; ACETIC ANHYDRIDE :-OTHER : MANGANESE ACETATE",
+        "hsn_code": "29152930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ACETIC ACID AND ITS SALTS; ACETIC ANHYDRIDE :-OTHER : OTHER",
+        "hsn_code": "29152990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ESTERS OF ACETIC ACID : ETHYL ACETATE",
+        "hsn_code": "29153100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ESTERS OF ACETIC ACID : VINYL ACETATE",
+        "hsn_code": "29153200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ESTERS OF ACETIC ACID : N-BUTYL ACETATE",
+        "hsn_code": "29153300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ESTERS OF ACETIC ACID : ISOBUTYL ACETATE",
+        "hsn_code": "29153400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ESTERS OF ACETIC ACID : 2-ETHOXYETHYL ACETATE",
+        "hsn_code": "29153500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ESTERS OF ACETIC ACID -- DINOSEB (ISO) ACETATE",
+        "hsn_code": "29153600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ESTERS OF ACETIC ACID : OTHER : BENZYL ACETATE",
+        "hsn_code": "29153910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ESTERS OF ACETIC ACID : OTHER : BORNYL ACETATE AND ISO BORNYL ACETATE",
+        "hsn_code": "29153920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ESTERS OF ACETIC ACID : OTHER : LINALYL ACETATE",
+        "hsn_code": "29153930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ESTERS OF ACETIC ACID : OTHER : METHYL ACETATE",
+        "hsn_code": "29153940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ESTERS OF ACETIC ACID : OTHER : PHENYL PROPYL ACETATE",
+        "hsn_code": "29153950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ESTERS OF ACETIC ACID : OTHER : TERPINYL ACETATE",
+        "hsn_code": "29153960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - ESTERS OF ACETIC ACID : OTHER : OTHER",
+        "hsn_code": "29153990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - MONO-, DI- OR TRICHLOROACETIC ACIDS, THEIR SALTS AND ESTERS :\nMONOCHLOROACETIC ACID, THEIR SALTS AND ESTERS",
+        "hsn_code": "29154010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - MONO-, DI- OR TRICHLOROACETIC ACIDS, THEIR SALTS AND ESTERS :\nDICHLOROACETIC ACID, THEIR SALTS AND ESTERS",
+        "hsn_code": "29154020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - MONO-, DI- OR TRICHLOROACETIC ACIDS, THEIR SALTS AND ESTERS :\nTRICHLOROACETIC ACID, THEIR SALTS AND ESTERS",
+        "hsn_code": "29154030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - PROPIONIC ACID, ITS SALTS AND ESTERS",
+        "hsn_code": "29155000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - BUTANOIC ACIDS, PENTANOIC ACIDS, THEIR SALTS AND ESTERS : BUTANOIC\nACIDS, THEIR SALTS AND ESTERS",
+        "hsn_code": "29156010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - BUTANOIC ACIDS, PENTANOIC ACIDS, THEIR SALTS AND ESTERS : PENTANOIC\nACIDS, THEIR SALTS AND ESTERS",
+        "hsn_code": "29156020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - PALMITIC ACID, STEARIC ACID, THEIR SALTS AND ESTERS : PALMITIC ACID",
+        "hsn_code": "29157010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - PALMITIC ACID, STEARIC ACID, THEIR SALTS AND ESTERS : STEARIC ACID",
+        "hsn_code": "29157020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - PALMITIC ACID, STEARIC ACID, THEIR SALTS AND ESTERS : GLYCEROL\nMONOSTEARATE",
+        "hsn_code": "29157030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - PALMITIC ACID, STEARIC ACID, THEIR SALTS AND ESTERS : H.C.O.FATTY ACID\n(INCLUDING 12-HYDROXY STEARIC ACID)",
+        "hsn_code": "29157040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - PALMITIC ACID, STEARIC ACID, THEIR SALTS AND ESTERS : D.C.O. FATTY ACID",
+        "hsn_code": "29157050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - PALMITIC ACID, STEARIC ACID, THEIR SALTS AND ESTERS : OTHER",
+        "hsn_code": "29157090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - OTHER : ACETYL CHLORIDE",
+        "hsn_code": "29159010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - OTHER : OCTOIC ACID (CAPRYLIC ACID)",
+        "hsn_code": "29159020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - OTHER : HEXOIC ACID (CAPROIC ACID)",
+        "hsn_code": "29159030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES\nAND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - OTHER : OTHER",
+        "hsn_code": "29159090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS,\nTHEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES : ACRYLIC ACID\nAND ITS SALTS",
+        "hsn_code": "29161100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS,\nTHEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES : ESTERS OF\nACRYLIC ACID : BUTYL ACRYLATE",
+        "hsn_code": "29161210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS,\nTHEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES : ESTERS OF\nACRYLIC ACID : OTHER",
+        "hsn_code": "29161290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS,\nTHEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES :\nMETHACRYLIC ACID AND ITS SALTS : METHACRYLIC ACID",
+        "hsn_code": "29161310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS,\nTHEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES :\nMETHACRYLIC ACID AND ITS SALTS : SALTS OF METHACRYLIC ACID",
+        "hsn_code": "29161320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS,\nTHEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES : ESTERS OF\nMETHACRYLIC ACID",
+        "hsn_code": "29161400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS,\nTHEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES :-OLEIC,\nLINOLEIC OR LINOLENIC ACIDS, THEIR SALTS AND ESTERS : OLEIC ACID",
+        "hsn_code": "29161510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS,\nTHEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES :-OLEIC,\nLINOLEIC OR LINOLENIC ACIDS, THEIR SALTS AND ESTERS : OTHER",
+        "hsn_code": "29161590",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS,CYCLIC MONCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS;THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES-UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS,\nTHEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES--\nBINAPACRYL(ISO)",
+        "hsn_code": "29161600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS,\nTHEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES : OTHER :\nUNDECYLENIC ACID",
+        "hsn_code": "29161910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS,\nTHEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES : OTHER :\nBISMUTH COMPOUNDS OF UNSATURATED ACYCLIC MONOACIDS",
+        "hsn_code": "29161920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS,\nTHEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES : OTHER :\nPOTASSIUM COMPOUNDS OF UNSATURATED ACYCLIC MONOACIDS",
+        "hsn_code": "29161930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS,\nTHEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES : OTHER :\nSODIUM COMPOUNDS OF UNSATURATED ACYCLIC MONOACIDS",
+        "hsn_code": "29161940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS,\nTHEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES : OTHER :\nESTERS OF UNSATURATED ACYCLIC MONOACIDS NOT ELSEWHERE SPECIFIED",
+        "hsn_code": "29161950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS,\nTHEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES : OTHER :\nSORBIC ACID",
+        "hsn_code": "29161960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS,\nTHEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES : OTHER :\nOTHER",
+        "hsn_code": "29161990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - CYCLANIC, CYCLENIC OR CYCLOTERPENIC\nMONOCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES",
+        "hsn_code": "29162000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - AROMATIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES :-BENZOIC ACID, ITS\nSALTS AND ESTERS : BENZOIC ACID",
+        "hsn_code": "29163110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - AROMATIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES :-BENZOIC ACID, ITS\nSALTS AND ESTERS : BENZYL BENZOATE",
+        "hsn_code": "29163120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - AROMATIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES :-BENZOIC ACID, ITS\nSALTS AND ESTERS : METHYL BENZOATE",
+        "hsn_code": "29163130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - AROMATIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES :-BENZOIC ACID, ITS\nSALTS AND ESTERS : SODIUM BENZOATE",
+        "hsn_code": "29163140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - AROMATIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES :-BENZOIC ACID, ITS\nSALTS AND ESTERS : BENZOCAINE (ETHYLPARA-AMINO BENZOATE)",
+        "hsn_code": "29163150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - AROMATIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES :-BENZOIC ACID, ITS\nSALTS AND ESTERS : ORTHOCHLORO BENZOIC ACID",
+        "hsn_code": "29163160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - AROMATIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES :-BENZOIC ACID, ITS\nSALTS AND ESTERS : OTHER",
+        "hsn_code": "29163190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - AROMATIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES : BENZOYL PEROXIDE\nAND BENZOYL CHLORIDE",
+        "hsn_code": "29163200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - AROMATIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES : PHENYLACETIC\nACID AND ITS SALTS",
+        "hsn_code": "29163400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - AROMATIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES : OTHER : CINNAMIC\nACID",
+        "hsn_code": "29163910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - AROMATIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES : OTHER : BISMUTH\nCOMPOUNDS OF AROMATIC MONOACIDS",
+        "hsn_code": "29163920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - AROMATIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES : OTHER :\nPOTASSIUM COMPOUNDS OF AROMATIC MONOACIDS",
+        "hsn_code": "29163930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - AROMATIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES : OTHER : SODIUM\nCOMPOUNDS OF AROMATIC MONOACIDS",
+        "hsn_code": "29163940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - AROMATIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES : OTHER : ESTERS OF\nAROMATIC MONOACIDS NOT ELSEWHERE SPECIFIED",
+        "hsn_code": "29163950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED,\nNITRATED OR NITROSATED DERIVATIVES - AROMATIC MONOCARBOXYLIC ACIDS, THEIR\nANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR DERIVATIVES : OTHER : OTHER",
+        "hsn_code": "29163990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-OXALIC ACID, ITS SALTS AND ESTERS : OXALIC ACID",
+        "hsn_code": "29171110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-OXALIC ACID, ITS SALTS AND ESTERS : CALCIUM OXALATE",
+        "hsn_code": "29171120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-OXALIC ACID, ITS SALTS AND ESTERS : STRONTIUM OXALATE",
+        "hsn_code": "29171130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-OXALIC ACID, ITS SALTS AND ESTERS : DIETHYL OXALATE",
+        "hsn_code": "29171140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-OXALIC ACID, ITS SALTS AND ESTERS : OTHER",
+        "hsn_code": "29171190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : ADIPIC ACID, ITS SALTS AND ESTERS",
+        "hsn_code": "29171200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : AZELAIC ACID, SEBACIC ACID, THEIR SALTS AND ESTERS",
+        "hsn_code": "29171300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : MALEIC ANHYDRIDE",
+        "hsn_code": "29171400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-OTHER : MALEIC ACID",
+        "hsn_code": "29171910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-OTHER : MALONIC ACID",
+        "hsn_code": "29171920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-OTHER : SUCCINIC ACID",
+        "hsn_code": "29171930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-OTHER : FERROUS FUMARATE",
+        "hsn_code": "29171940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-OTHER : FUMARIC ACID",
+        "hsn_code": "29171950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-OTHER : ITACONIC ACID",
+        "hsn_code": "29171960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-OTHER : ETHOXY METHYLENE MALONATE, DIETHYL MALONATE",
+        "hsn_code": "29171970",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - ACYCLIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-OTHER : OTHER",
+        "hsn_code": "29171990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES -CYCLANIC, CYCLENIC\nOR CYCLOTERPENIC, POLYCAROXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES,\nPEROXYACIDS AND THEIR DERIVATIVES",
+        "hsn_code": "29172000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :DIBUTYL ORTHOPHTHALATES",
+        "hsn_code": "29173100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :DIOCTYL ORTHOPHTHALATES",
+        "hsn_code": "29173200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : DINONYL OR DIDECYL ORTHOPHTHALATES",
+        "hsn_code": "29173300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :OTHER ESTERS OF ORTHOPHTHALIC ACID",
+        "hsn_code": "29173400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :PHTHALIC ANHYDRIDE",
+        "hsn_code": "29173500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : TEREPHTHALIC ACID AND ITS SALTS",
+        "hsn_code": "29173600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :DIMETHYL TEREPHTHALATE",
+        "hsn_code": "29173700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : OTHER :DIBUTYL PHTHALATE",
+        "hsn_code": "29173910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : OTHER : DIOCTYL PHTHALATE",
+        "hsn_code": "29173920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : OTHER : PHTHALIC ACID",
+        "hsn_code": "29173930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : OTHER : DIMETHYL PHTHALATE",
+        "hsn_code": "29173940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : OTHER : TRIMELLITIC ANHYDRIDE",
+        "hsn_code": "29173950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : OTHER : ISOPHTHALIC ACID",
+        "hsn_code": "29173960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES - AROMATIC\nPOLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : OTHER : OTHER",
+        "hsn_code": "29173990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALCOHOL FUNCTION, BUT WITHOUT\nOTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-LACTIC ACID, ITS SALTS AND ESTERS : LACTIC ACID",
+        "hsn_code": "29181110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALCOHOL FUNCTION, BUT WITHOUT\nOTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-LACTIC ACID, ITS SALTS AND ESTERS : CALCIUM LACTATE",
+        "hsn_code": "29181120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALCOHOL FUNCTION, BUT WITHOUT\nOTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-LACTIC ACID, ITS SALTS AND ESTERS : OTHER",
+        "hsn_code": "29181190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALCOHOL FUNCTION, BUT WITHOUT\nOTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : TARTARIC ACID",
+        "hsn_code": "29181200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALCOHOL FUNCTION, BUT WITHOUT\nOTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-SALTS AND ESTERS OF TARTARIC ACID : POTASSIUM BITARTRATE",
+        "hsn_code": "29181310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALCOHOL FUNCTION, BUT WITHOUT\nOTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-SALTS AND ESTERS OF TARTARIC ACID : METROPROLOL TARTRATE",
+        "hsn_code": "29181320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALCOHOL FUNCTION, BUT WITHOUT\nOTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-SALTS AND ESTERS OF TARTARIC ACID : OTHER",
+        "hsn_code": "29181390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALCOHOL FUNCTION, BUT WITHOUT\nOTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : CITRIC ACID",
+        "hsn_code": "29181400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALCOHOL FUNCTION, BUT WITHOUT\nOTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-SALTS AND ESTERS OF CITRIC ACID : POTASSIUM CITRATE",
+        "hsn_code": "29181510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALCOHOL FUNCTION, BUT WITHOUT\nOTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-SALTS AND ESTERS OF CITRIC ACID : SODIUM CITRATE",
+        "hsn_code": "29181520",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALCOHOL FUNCTION, BUT WITHOUT\nOTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-SALTS AND ESTERS OF CITRIC ACID : BISMUTH CITRATE",
+        "hsn_code": "29181530",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALCOHOL FUNCTION, BUT WITHOUT\nOTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-SALTS AND ESTERS OF CITRIC ACID : DISODIUM HYDROGEN CITRATE",
+        "hsn_code": "29181540",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALCOHOL FUNCTION, BUT WITHOUT\nOTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-SALTS AND ESTERS OF CITRIC ACID : FERRIC AMMONIUM CITRATE",
+        "hsn_code": "29181550",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALCOHOL FUNCTION, BUT WITHOUT\nOTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-SALTS AND ESTERS OF CITRIC ACID : OTHER",
+        "hsn_code": "29181590",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALCOHOL FUNCTION, BUT WITHOUT\nOTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-GLUCONIC ACID, ITS SALTS AND ESTERS : CALCIUM GLUCONATE",
+        "hsn_code": "29181610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALCOHOL FUNCTION, BUT WITHOUT\nOTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-GLUCONIC ACID, ITS SALTS AND ESTERS : FERROUS GLUCONATE",
+        "hsn_code": "29181620",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALCOHOL FUNCTION, BUT WITHOUT\nOTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES :-GLUCONIC ACID, ITS SALTS AND ESTERS : OTHER",
+        "hsn_code": "29181690",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALCOHOL FUNCTION, BUT WITHOUT\nOTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES -- CHLOROBENZILATE (ISO)",
+        "hsn_code": "29181800",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALCOHOL FUNCTION, BUT WITHOUT\nOTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : OTHER",
+        "hsn_code": "29181900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BENZENEACETIC ACID, ALPHA-HYDROXY-ALPHA-PHENYL-",
+        "hsn_code": "29181910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES: OTHER",
+        "hsn_code": "29181990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH PHENOL FUNCTION BUT WITHOUT OTHER\nOXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : SALICYLIC ACID AND ITS SALTS : SALICYLIC ACID",
+        "hsn_code": "29182110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH PHENOL FUNCTION BUT WITHOUT OTHER\nOXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : SALICYLIC ACID AND ITS SALTS : SODIUM SALICYLATE",
+        "hsn_code": "29182120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH PHENOL FUNCTION BUT WITHOUT OTHER\nOXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : SALICYLIC ACID AND ITS SALTS : OTHER",
+        "hsn_code": "29182190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH PHENOL FUNCTION BUT WITHOUT OTHER\nOXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : O-ACETYLSALICYLIC ACID, ITS SALTS AND ESTERS",
+        "hsn_code": "29182200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH PHENOL FUNCTION BUT WITHOUT OTHER\nOXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : OTHER ESTERS OF SALICYLIC ACID AND THEIR SALTS : METHYL SALICYLATE",
+        "hsn_code": "29182310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH PHENOL FUNCTION BUT WITHOUT OTHER\nOXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : OTHER ESTERS OF SALICYLIC ACID AND THEIR SALTS : AMINO SALICYLATE",
+        "hsn_code": "29182320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH PHENOL FUNCTION BUT WITHOUT OTHER\nOXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : OTHER ESTERS OF SALICYLIC ACID AND THEIR SALTS : SALICYLAMIDE",
+        "hsn_code": "29182330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH PHENOL FUNCTION BUT WITHOUT OTHER\nOXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : OTHER ESTERS OF SALICYLIC ACID AND THEIR SALTS : OTHER",
+        "hsn_code": "29182390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH PHENOL FUNCTION BUT WITHOUT OTHER\nOXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : OTHER : GALLIC ACID",
+        "hsn_code": "29182910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH PHENOL FUNCTION BUT WITHOUT OTHER\nOXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : OTHER : BETA HYDROXY NAPTHOIC ACID",
+        "hsn_code": "29182920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH PHENOL FUNCTION BUT WITHOUT OTHER\nOXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : OTHER : PROPYL GALLATE",
+        "hsn_code": "29182930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH PHENOL FUNCTION BUT WITHOUT OTHER\nOXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS AND THEIR\nDERIVATIVES : OTHER : OTHER",
+        "hsn_code": "29182990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALDEHYDE OR KETONE FUNCTION BUT\nWITHOUT OTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS\nAND THEIR DERIVATIVES : LEVULINIC ACID",
+        "hsn_code": "29183010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALDEHYDE OR KETONE FUNCTION BUT\nWITHOUT OTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS\nAND THEIR DERIVATIVES : ETHYL ACETO ACETATE (ACETOACETIC ESTER)",
+        "hsn_code": "29183020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALDEHYDE OR KETONE FUNCTION BUT\nWITHOUT OTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS\nAND THEIR DERIVATIVES : NALIDIXIC ACID",
+        "hsn_code": "29183030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALDEHYDE OR KETONE FUNCTION BUT\nWITHOUT OTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS\nAND THEIR DERIVATIVES : METHYL ACETO ACETATE",
+        "hsn_code": "29183040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - CARBOXYLIC ACIDS WITH ALDEHYDE OR KETONE FUNCTION BUT\nWITHOUT OTHER OXYGEN FUNCTION, THEIR ANHYDRIDES, HALIDES, PEROXIDES, PEROXYACIDS\nAND THEIR DERIVATIVES : OTHER",
+        "hsn_code": "29183090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - OTHER",
+        "hsn_code": "29189000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - OTHER -- 2, 4, 5-T (ISO) (2, 4, 5-TRICHLOROPHENOXYACETIC ACID)\nITS SALTS AND ESTERS",
+        "hsn_code": "29189100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES,\nPEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES - OTHER -- OTHER",
+        "hsn_code": "29189900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHORIC ESTERS AND THEIR SALTS, INCLUDING LACTOPHOSPHATES; THEIR HALOGENATED,\nSULPHONATED, NITRATED OR NITROSATED DERIVATIVES - PHOSPHORIC ESTERS AND THEIR\nSALTS, INCLUDING LACTO-PHOSPHATES; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES : GLYCEROPHOSPHORIC ACID",
+        "hsn_code": "29190010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHORIC ESTERS AND THEIR SALTS, INCLUDING LACTOPHOSPHATES; THEIR HALOGENATED,\nSULPHONATED, NITRATED OR NITROSATED DERIVATIVES - PHOSPHORIC ESTERS AND THEIR\nSALTS, INCLUDING LACTO-PHOSPHATES; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES : CALCIUM GLYCEROPHOSPHATE",
+        "hsn_code": "29190020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHORIC ESTERS AND THEIR SALTS, INCLUDING LACTOPHOSPHATES; THEIR HALOGENATED,\nSULPHONATED, NITRATED OR NITROSATED DERIVATIVES - PHOSPHORIC ESTERS AND THEIR\nSALTS, INCLUDING LACTO-PHOSPHATES; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES : IRON GLYCEROPHOSPHATE",
+        "hsn_code": "29190030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHORIC ESTERS AND THEIR SALTS, INCLUDING LACTOPHOSPHATES; THEIR HALOGENATED,\nSULPHONATED, NITRATED OR NITROSATED DERIVATIVES - PHOSPHORIC ESTERS AND THEIR\nSALTS, INCLUDING LACTO-PHOSPHATES; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES : SODIUM GLYCEROPHOSPHATE",
+        "hsn_code": "29190040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHORIC ESTERS AND THEIR SALTS, INCLUDING LACTOPHOSPHATES; THEIR HALOGENATED,\nSULPHONATED, NITRATED OR NITROSATED DERIVATIVES - PHOSPHORIC ESTERS AND THEIR\nSALTS, INCLUDING LACTO-PHOSPHATES; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES : TRICRESYL PHOSPHATE",
+        "hsn_code": "29190050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHORIC ESTERS AND THEIR SALTS, INCLUDING LACTOPHOSPHATES; THEIR HALOGENATED,\nSULPHONATED, NITRATED OR NITROSATED DERIVATIVES - PHOSPHORIC ESTERS AND THEIR\nSALTS, INCLUDING LACTO-PHOSPHATES; THEIR HALOGENATED, SULPHONATED, NITRATED OR\nNITROSATED DERIVATIVES : OTHER",
+        "hsn_code": "29190090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHORIC ESTERS AND THEIR SALTS, INCLUDING LACTO PHOSPHATES; THEIR HALOGENATED,\nSULPHONATED, NITRATED OR NITROSATED DERIVATIVES - TRIS (2,3-DIBROMOPROPYL)\nPHOSPHATE",
+        "hsn_code": "29191000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHORIC ESTERS AND THEIR SALTS, INCLUDING LACTO PHOSPHATES; THEIR HALOGENATED,\nSULPHONATED, NITRATED OR NITROSATED DERIVATIVES - OTHER -- GLYCEROPHOSPHORIC ACID",
+        "hsn_code": "29199010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHORIC ESTERS AND THEIR SALTS, INCLUDING LACTO PHOSPHATES; THEIR HALOGENATED,\nSULPHONATED, NITRATED OR NITROSATED DERIVATIVES - OTHER -- CALCIUM\nGLYCEROPHOSPHATE",
+        "hsn_code": "29199020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHORIC ESTERS AND THEIR SALTS, INCLUDING LACTO PHOSPHATES; THEIR HALOGENATED,\nSULPHONATED, NITRATED OR NITROSATED DERIVATIVES - OTHER -- IRON GLYCEROPHOSPHATE",
+        "hsn_code": "29199030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHORIC ESTERS AND THEIR SALTS, INCLUDING LACTO PHOSPHATES; THEIR HALOGENATED,\nSULPHONATED, NITRATED OR NITROSATED DERIVATIVES - OTHER -- SODIUM\nGLYCEROPHOSPHATE",
+        "hsn_code": "29199040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHORIC ESTERS AND THEIR SALTS, INCLUDING LACTO PHOSPHATES; THEIR HALOGENATED,\nSULPHONATED, NITRATED OR NITROSATED DERIVATIVES - OTHER -- TRICRESYL PHOSPHATE",
+        "hsn_code": "29199050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHORIC ESTERS AND THEIR SALTS, INCLUDING LACTO PHOSPHATES; THEIR HALOGENATED,\nSULPHONATED, NITRATED OR NITROSATED DERIVATIVES - OTHER -- OTHER",
+        "hsn_code": "29199090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ESTERS OF OTHER INORGANIC ACIDS OF NON-METALS (EXCLUDING ESTERS OF HYDROGEN\nHALIDES) AND THEIR SALTS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - THIOPHOSPHORIC ESTERS (PHOSPHOROTHIOATES) AND THEIR SALTS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES",
+        "hsn_code": "29201000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHOROTHIOIC ACID, S [2-DIETHYLAMINO)ETHYL] O,O-DIETHYL E",
+        "hsn_code": "29201010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ESTERS OF OTHER INORGANIC ACIDS OF NON-METALS (EXCLUDING ESTERS OF HYDROGEN\nHALIDES) AND THEIR SALTS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES",
+        "hsn_code": "29201020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ESTERS OF OTHER INORGANIC ACIDS OF NON-METALS (EXCLUDING ESTERS OF HYDROGEN\nALLIEDS) AND THEIR SALTS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - THIOPHOSPHORIC ESTERS (PHOSPHOROTHIOATES) AND THEIR SALTS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES: -- PARATHION (ISO)\nAND PARATHION-METHYL (ISO) (METHYLPARATHION)",
+        "hsn_code": "29201100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ESTERS OF OTHER INORGANIC ACIDS OF NON-METALS (EXCLUDING ESTERS OF HYDROGEN\nALLIEDS) AND THEIR SALTS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - THIOPHOSPHORIC ESTERS (PHOSPHOROTHIOATES) AND THEIR SALTS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES: -- OTHER ---\nTHIOPHOSPHORIC ESTER (PHOSPHOROTHIOATES) AND THEIR SALTS, THEIR HALOGENATED,\nSULPHONATED, NITRATED OR NITROSATED DERIVATIVES",
+        "hsn_code": "29201920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ESTERS OF OTHER INORGANIC ACIDS OF NON-METALS (EXCLUDING ESTERS OF HYDROGEN\nALLIEDS) AND THEIR SALTS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - THIOPHOSPHORIC ESTERS (PHOSPHOROTHIOATES) AND THEIR SALTS; THEIR\nHALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES: -- OTHER --- OTHER",
+        "hsn_code": "29201990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ESTERS OF OTHER INORGANIC ACIDS OF NON-METALS (EXCLUDING ESTERS OF HYDROGEN\nHALIDES) AND THEIR SALTS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - OTHER : DIETHYL SULPHATE",
+        "hsn_code": "29209010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ESTERS OF OTHER INORGANIC ACIDS OF NON-METALS (EXCLUDING ESTERS OF HYDROGEN\nHALIDES) AND THEIR SALTS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - OTHER : DIMETHYL SULPHATE",
+        "hsn_code": "29209020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ESTERS OF OTHER INORGANIC ACIDS OF NON-METALS (EXCLUDING ESTERS OF HYDROGEN\nHALIDES) AND THEIR SALTS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - OTHER : TRIS (2,3 DI-BROMOPROPYL) PHOSPHATE",
+        "hsn_code": "29209030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRIMETHYL PHOSPHITE",
+        "hsn_code": "29209041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRIETHYL PHOSPHITE",
+        "hsn_code": "29209042",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DIMETHYL PHOSPHITE",
+        "hsn_code": "29209043",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DIETHYL PHOSPHITE",
+        "hsn_code": "29209044",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ESTERS OF OTHER INORGANIC ACIDS OF NON-METALS (EXCLUDING ESTERS OF HYDROGEN\nHALIDES) AND THEIR SALTS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED\nDERIVATIVES - OTHER : OTHER",
+        "hsn_code": "29209090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "29209099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - ACYCLIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-METHYLAMINE, DI- OR TRIMETHYLAMINE AND THEIR SALTS : DIMETHYL FORMIDE",
+        "hsn_code": "29211110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - ACYCLIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-METHYLAMINE, DI- OR TRIMETHYLAMINE AND THEIR SALTS : OTHER",
+        "hsn_code": "29211190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - ACYCLIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF : DIETHYLAMINE AND ITS SALTS",
+        "hsn_code": "29211200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - ACYCLIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF : OTHER",
+        "hsn_code": "29211900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "2-CHLORO N, N-DI-ISOPROPYL ETHYLAMINE",
+        "hsn_code": "29211911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "2-CHLORO N,N-DI-ISOPROPYL ETHYLAMINE AND ETHANAMINE,2-CHLORO-N,N-DIMETHYL N,N\nDIETHYL AMINO ETHYL CHLORIDE HYDROCHLORIDE, DI-METHYL AMINO ETHYLCHLORIDE\nHYDROCHLORIDE - - - -N,N-DIETHYL AMINO ETHYL CHLORIDE HYDROCHLORIDE",
+        "hsn_code": "29211912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "2-CHLORO N,N-DI-ISOPROPYL ETHYLAMINE AND ETHANAMINE,2-CHLORO-N,N-DIMETHYL N,N\nDIETHYL AMINO ETHYL CHLORIDE HYDROCHLORIDE, DI-METHYL AMINO ETHYLCHLORIDE\nHYDROCHLORIDE - - - - DI-METHYL AMINO ETHYL CHLORIDE HYDROCHLORIDE",
+        "hsn_code": "29211913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHANAMINE, 2-CHLORO-N, N-DIMETHYL",
+        "hsn_code": "29211914",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "29211990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - ACYCLIC POLYAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF : ETHYLENEDIAMINE AND ITS SALTS",
+        "hsn_code": "29212100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - ACYCLIC POLYAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF : HEXAMETHYLENEDIAMINE AND ITS SALTS",
+        "hsn_code": "29212200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - ACYCLIC POLYAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-OTHER : HEXAMETHYLENE TETRAMINE (HEXAMINE) NOT PUT UP AS FUEL OR\nMEDICAMENT",
+        "hsn_code": "29212910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - ACYCLIC POLYAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-OTHER : TRIMETHYLENE TRINIFRAMINE",
+        "hsn_code": "29212920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - ACYCLIC POLYAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-OTHER : OTHER",
+        "hsn_code": "29212990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - -CYCLANIC, CYCLENIC OR CYCLOTERPENIC MONO OR\nPOLYAMINES, AND THEIR DERIVATIVES; SALTS THEREOF : CYCLOHEXYLAMINE",
+        "hsn_code": "29213010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - CYCLANIC, CYCLENIC OR CYCLOTERPENIC MONO OR\nPOLYAMINES, AND THEIR DERIVATIVES; SALTS THEREOF : OTHER",
+        "hsn_code": "29213090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE AND ITS SALTS : ANILINE",
+        "hsn_code": "29214110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE AND ITS SALTS : ANILINE HYDROCHLORIDE",
+        "hsn_code": "29214120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE AND ITS SALTS : OTHER",
+        "hsn_code": "29214190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE DERIVATIVES AND THEIR SALTS : PARA CHLOROANILINE, ORTHO CHLORO\nPARANITROANILINE, DICHLOROANILINE, 2, 6-DICHLORO PARANITROANILINE, 2-4-5-\nTRICHLOROANILINE : PARA CHLOROANILINE",
+        "hsn_code": "29214211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE DERIVATIVES AND THEIR SALTS : PARA CHLOROANILINE, ORTHO CHLORO\nPARANITROANILINE, DICHLOROANILINE, 2, 6-DICHLORO PARANITROANILINE, 2-4-5-\nTRICHLOROANILINE : ORTHO CHLORO PARANITROANILINE",
+        "hsn_code": "29214212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE DERIVATIVES AND THEIR SALTS : PARA CHLOROANILINE, ORTHO CHLORO\nPARANITROANILINE, DICHLOROANILINE, 2, 6-DICHLORO PARANITROANILINE, 2-4-5-\nTRICHLOROANILINE : DICHLOROANILINE",
+        "hsn_code": "29214213",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE DERIVATIVES AND THEIR SALTS : PARA CHLOROANILINE, ORTHO CHLORO\nPARANITROANILINE, DICHLOROANILINE, 2, 6-DICHLORO PARANITROANILINE, 2-4-5-\nTRICHLOROANILINE : 2, 6-DICHLORO PARANITROANILINE",
+        "hsn_code": "29214214",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE DERIVATIVES AND THEIR SALTS : PARA CHLOROANILINE, ORTHO CHLORO\nPARANITROANILINE, DICHLOROANILINE, 2, 6-DICHLORO PARANITROANILINE, 2-4-5-\nTRICHLOROANILINE : 2-4-5-TRICHLOROANILINE",
+        "hsn_code": "29214215",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE DERIVATIVES AND THEIR SALTS : BENZYL ETHYL ANILINE, ETHYL ANILINE,\nDIETHYLANILINE, DIMETHYLANILINE, META NITROANILINE, PARA NITROANILINE : BENZYL ETHYL\nANILINE",
+        "hsn_code": "29214221",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE DERIVATIVES AND THEIR SALTS :-BENZYL ETHYL ANILINE, ETHYL ANILINE,\nDIETHYLANILINE, DIMETHYLANILINE, META NITROANILINE, PARA NITROANILINE :\nDIETHYLANILINE",
+        "hsn_code": "29214222",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE DERIVATIVES AND THEIR SALTS : BENZYL ETHYL ANILINE, ETHYL ANILINE,\nDIETHYLANILINE, DIMETHYLANILINE, META NITROANILINE, PARA NITROANILINE :\nDIMETHYLANILINE",
+        "hsn_code": "29214223",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE DERIVATIVES AND THEIR SALTS : BENZYL ETHYL ANILINE, ETHYL ANILINE,\nDIETHYLANILINE, DIMETHYLANILINE, META NITROANILINE, PARA NITROANILINE : ETHYL ANILINE",
+        "hsn_code": "29214224",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE DERIVATIVES AND THEIR SALTS : BENZYL ETHYL ANILINE, ETHYL ANILINE,\nDIETHYLANILINE, DIMETHYLANILINE, META NITROANILINE, PARA NITROANILINE : META\nNITROANILINE",
+        "hsn_code": "29214225",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE DERIVATIVES AND THEIR SALTS : BENZYL ETHYL ANILINE, ETHYL ANILINE,\nDIETHYLANILINE, DIMETHYLANILINE, META NITROANILINE, PARA NITROANILINE : PARA\nNITROANILINE",
+        "hsn_code": "29214226",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE DERIVATIVES AND THEIR SALTS : 2-AMINO 3, 5 XYLNE SULPHONIC ACID,\nBENZYL ETHYL ANILINE SULPHURIC ACID, METANILLIC ACID (META AMINO BENZENE SULPHONIC\nACID), SULPHANILLIC ACID (PARA AMINOBENZENE SULPHONIC ACID PARA ANILINE SULPHONIC\nACID), ETHYL HYDROXY ETHYLANILINE, METHYL DOPA (1-ALPHA METHYL-3, 4-\nDIHYDROXYPHENYLANILINE) : 2-AMINO 3, 5 XYLNE SULPHONIC ACID",
+        "hsn_code": "29214231",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE DERIVATIVES AND THEIR SALTS : 2-AMINO 3, 5 XYLNE SULPHONIC ACID,\nBENZYL ETHYL ANILINE SULPHURIC ACID, METANILLIC ACID (META AMINO BENZENE SULPHONIC\nACID), SULPHANILLIC ACID (PARA AMINOBENZENE SULPHONIC ACID PARA ANILINE SULPHONIC\nACID), ETHYL HYDROXY ETHYLANILINE, METHYL DOPA (1-ALPHA METHYL-3, 4-\nDIHYDROXYPHENYLANILINE) : BENZYL ETHYL ANILINE SULPHONIC ACID",
+        "hsn_code": "29214232",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE DERIVATIVES AND THEIR SALTS : 2-AMINO 3, 5 XYLNE SULPHONIC ACID,\nBENZYL ETHYL ANILINE SULPHURIC ACID, METANILLIC ACID (META AMINO BENZENE SULPHONIC\nACID), SULPHANILLIC ACID (PARA AMINOBENZENE SULPHONIC ACID PARA ANILINE SULPHONIC\nACID), ETHYL HYDROXY ETHYLANILINE, METHYL DOPA (1-ALPHA METHYL-3, 4-\nDIHYDROXYPHENYLANILINE) : METANILLIC ACID (META AMINO BENZENE SULPHONIC ACID)",
+        "hsn_code": "29214233",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE DERIVATIVES AND THEIR SALTS : 2-AMINO 3, 5 XYLNE SULPHONIC ACID,\nBENZYL ETHYL ANILINE SULPHURIC ACID, METANILLIC ACID (META AMINO BENZENE SULPHONIC\nACID), SULPHANILLIC ACID (PARA AMINOBENZENE SULPHONIC ACID PARA ANILINE SULPHONIC\nACID), ETHYL HYDROXY ETHYLANILINE, METHYL DOPA (1-ALPHA METHYL-3, 4-\nDIHYDROXYPHENYLANILINE) : SULPHANILLIC ACID (PARA AMINOBENZENE SULPHONIC ACID\nPARA ANILINE SULPHONIC ACID)",
+        "hsn_code": "29214234",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE DERIVATIVES AND THEIR SALTS : 2-AMINO 3, 5 XYLNE SULPHONIC ACID,\nBENZYL ETHYL ANILINE SULPHURIC ACID, METANILLIC ACID (META AMINO BENZENE SULPHONIC\nACID), SULPHANILLIC ACID (PARA AMINOBENZENE SULPHONIC ACID PARA ANILINE SULPHONIC\nACID), ETHYL HYDROXY ETHYLANILINE, METHYL DOPA (1-ALPHA METHYL-3, 4-\nDIHYDROXYPHENYLANILINE) : ETHYL HYDROXY ETHYLANILINE",
+        "hsn_code": "29214235",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE DERIVATIVES AND THEIR SALTS : 2-AMINO 3, 5 XYLNE SULPHONIC ACID,\nBENZYL ETHYL ANILINE SULPHURIC ACID, METANILLIC ACID (META AMINO BENZENE SULPHONIC\nACID), SULPHANILLIC ACID (PARA AMINOBENZENE SULPHONIC ACID PARA ANILINE SULPHONIC\nACID), ETHYL HYDROXY ETHYLANILINE, METHYL DOPA (1-ALPHA METHYL-3, 4-\nDIHYDROXYPHENYLANILINE) : METHYL DOPA (1-ALPHA METHYL-3, 4-\nDIHYDROXYPHENYLANILINE)",
+        "hsn_code": "29214236",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ANILINE DERIVATIVES AND THEIR SALTS : 2-AMINO 3, 5 XYLNE SULPHONIC ACID,\nBENZYL ETHYL ANILINE SULPHURIC ACID, METANILLIC ACID (META AMINO BENZENE SULPHONIC\nACID), SULPHANILLIC ACID (PARA AMINOBENZENE SULPHONIC ACID PARA ANILINE SULPHONIC\nACID), ETHYL HYDROXY ETHYLANILINE, METHYL DOPA (1-ALPHA METHYL-3, 4-\nDIHYDROXYPHENYLANILINE) : OTHER",
+        "hsn_code": "29214290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-TOLUIDINES AND THEIR DERIVATIVES; SALTS THEREOF : DIETHYL TOLUIDINE",
+        "hsn_code": "29214310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-TOLUIDINES AND THEIR DERIVATIVES; SALTS THEREOF : DIMETHYL TOLUIDINE",
+        "hsn_code": "29214320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-TOLUIDINES AND THEIR DERIVATIVES; SALTS THEREOF : ORTHO TOLUIDINE",
+        "hsn_code": "29214330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-TOLUIDINES AND THEIR DERIVATIVES; SALTS THEREOF : META TOLUIDINE",
+        "hsn_code": "29214340",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-TOLUIDINES AND THEIR DERIVATIVES; SALTS THEREOF : PARA TOLUIDINE",
+        "hsn_code": "29214350",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-TOLUIDINES AND THEIR DERIVATIVES; SALTS THEREOF : 2-CHLORO-5-TOLUIDINE-4-\nSULPHONIC ACID",
+        "hsn_code": "29214360",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-TOLUIDINES AND THEIR DERIVATIVES; SALTS THEREOF : 2-CHLORO-4-TOLUIDINE-5-\nSULPHONIC ACID (SODIUM SALT)",
+        "hsn_code": "29214370",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-TOLUIDINES AND THEIR DERIVATIVES; SALTS THEREOF : 4-TOLUIDINE-3-SULPHONIC\nACID",
+        "hsn_code": "29214380",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-TOLUIDINES AND THEIR DERIVATIVES; SALTS THEREOF : OTHER",
+        "hsn_code": "29214390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF : DIPHENYLAMINE AND ITS DERIVATIVES; SALTS THEREOF : DIPHENYLAMINE",
+        "hsn_code": "29214410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF : DIPHENYLAMINE AND ITS DERIVATIVES; SALTS THEREOF : OTHER",
+        "hsn_code": "29214490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-1-NAPHTHYLAMINE (ALPHA-NAPHTHYLAMINE), 2- NAPHTHYLAMINE (BETANAPH\nTHYLAMINE) AND THEIR DERIVATIVES; SALTS THEREOF : ALPHA NAPHTHYLAMINE, PHENYL\nALPHA NAPHTHYLAMINE, PHENYL BETA NAPHTHYLAMINE, AMINO F-ACID, AMINOLINELI-R-ACID,\nSODIUM NAPHTHIONATE : ALPHA NAPHTHYLAMINE",
+        "hsn_code": "29214511",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-1-NAPHTHYLAMINE (ALPHA-NAPHTHYLAMINE), 2- NAPHTHYLAMINE (BETANAPH\nTHYLAMINE) AND THEIR DERIVATIVES; SALTS THEREOF : ALPHA NAPHTHYLAMINE, PHENYL\nALPHA NAPHTHYLAMINE, PHENYL BETA NAPHTHYLAMINE, AMINO F-ACID, AMINOLINELI-R-ACID,\nSODIUM NAPHTHIONATE : PHENYL ALPHA NAPHTHYLAMINE",
+        "hsn_code": "29214512",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-1-NAPHTHYLAMINE (ALPHA-NAPHTHYLAMINE), 2- NAPHTHYLAMINE (BETANAPH\nTHYLAMINE) AND THEIR DERIVATIVES; SALTS THEREOF : ALPHA NAPHTHYLAMINE, PHENYL\nALPHA NAPHTHYLAMINE, PHENYL BETA NAPHTHYLAMINE, AMINO F-ACID, AMINOLINELI-R-ACID,\nSODIUM NAPHTHIONATE : PHENYL BETA NAPHTHYLAMINE",
+        "hsn_code": "29214513",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-1-NAPHTHYLAMINE (ALPHA-NAPHTHYLAMINE), 2- NAPHTHYLAMINE (BETANAPH\nTHYLAMINE) AND THEIR DERIVATIVES; SALTS THEREOF : ALPHA NAPHTHYLAMINE, PHENYL\nALPHA NAPHTHYLAMINE, PHENYL BETA NAPHTHYLAMINE, AMINO F-ACID, AMINOLINELI-R-ACID,\nSODIUM NAPHTHIONATE : AMINO F-ACID",
+        "hsn_code": "29214514",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-1-NAPHTHYLAMINE (ALPHA-NAPHTHYLAMINE), 2- NAPHTHYLAMINE (BETANAPH\nTHYLAMINE) AND THEIR DERIVATIVES; SALTS THEREOF : ALPHA NAPHTHYLAMINE, PHENYL\nALPHA NAPHTHYLAMINE, PHENYL BETA NAPHTHYLAMINE, AMINO F-ACID, AMINOLINELI-R-ACID,\nSODIUM NAPHTHIONATE : AMINOLINELI-R-ACID",
+        "hsn_code": "29214515",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-1-NAPHTHYLAMINE (ALPHA-NAPHTHYLAMINE), 2- NAPHTHYLAMINE (BETANAPH\nTHYLAMINE) AND THEIR DERIVATIVES; SALTS THEREOF : ALPHA NAPHTHYLAMINE, PHENYL\nALPHA NAPHTHYLAMINE, PHENYL BETA NAPHTHYLAMINE, AMINO F-ACID, AMINOLINELI-R-ACID,\nSODIUM NAPHTHIONATE : SODIUM NAPHTHIONATE",
+        "hsn_code": "29214516",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-1-NAPHTHYLAMINE (ALPHA-NAPHTHYLAMINE), 2- NAPHTHYLAMINE (BETANAPH\nTHYLAMINE) AND THEIR DERIVATIVES; SALTS THEREOF : BRONNERS ACID (2-NAPHTHYLAMINE-6-\nSULPHONIC ACID), CLEVES ACID (1-NAPHTHYLAMINE-6- SULPHONIC ACID), EPSILON ACID (1-\nNAPHTHYLAMINE-3,8-DISULPHONIC ACID), KOCHS ACID (1-NAPHTHYLAMINE-3,6,8-\nTRISULPHONIC ACID), LAURENTS ACID (1-NAPHTHYLAMINE-5-SULPHONIC ACID), TOBIAS ACID (2-\nNAPHTHYLAMINE-1- SULPHONIC ACID) : BRONNERS ACID (2-NAPHTHYLAMINE-6-SULPHONIC\nACID)",
+        "hsn_code": "29214521",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-1-NAPHTHYLAMINE (ALPHA-NAPHTHYLAMINE), 2- NAPHTHYLAMINE (BETANAPH\nTHYLAMINE) AND THEIR DERIVATIVES; SALTS THEREOF : BRONNERS ACID (2-NAPHTHYLAMINE-6-\nSULPHONIC ACID), CLEVES ACID (1-NAPHTHYLAMINE-6- SULPHONIC ACID), EPSILON ACID (1-\nNAPHTHYLAMINE-3,8-DISULPHONIC ACID), KOCHS ACID (1-NAPHTHYLAMINE-3,6,8-\nTRISULPHONIC ACID), LAURENTS ACID (1-NAPHTHYLAMINE-5-SULPHONIC ACID), TOBIAS ACID (2-\nNAPHTHYLAMINE-1- SULPHONIC ACID) : CLEVES ACID (1-NAPHTHYLAMINE-6-SULPHONIC ACID)",
+        "hsn_code": "29214522",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-1-NAPHTHYLAMINE (ALPHA-NAPHTHYLAMINE), 2- NAPHTHYLAMINE (BETANAPH\nTHYLAMINE) AND THEIR DERIVATIVES; SALTS THEREOF : BRONNERS ACID (2-NAPHTHYLAMINE-6-\nSULPHONIC ACID), CLEVES ACID (1-NAPHTHYLAMINE-6- SULPHONIC ACID), EPSILON ACID (1-\nNAPHTHYLAMINE-3,8-DISULPHONIC ACID), KOCHS ACID (1-NAPHTHYLAMINE-3,6,8-\nTRISULPHONIC ACID), LAURENTS ACID (1-NAPHTHYLAMINE-5-SULPHONIC ACID), TOBIAS ACID (2-\nNAPHTHYLAMINE-1- SULPHONIC ACID) : EPSILON ACID (1-NAPHTHYLAMINE-3,8-DISULPHONIC\nACID)",
+        "hsn_code": "29214523",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-1-NAPHTHYLAMINE (ALPHA-NAPHTHYLAMINE), 2- NAPHTHYLAMINE (BETANAPH\nTHYLAMINE) AND THEIR DERIVATIVES; SALTS THEREOF : BRONNERS ACID (2-NAPHTHYLAMINE-6-\nSULPHONIC ACID), CLEVES ACID (1-NAPHTHYLAMINE-6- SULPHONIC ACID), EPSILON ACID (1-\nNAPHTHYLAMINE-3,8-DISULPHONIC ACID), KOCHS ACID (1-NAPHTHYLAMINE-3,6,8-\nTRISULPHONIC ACID), LAURENTS ACID (1-NAPHTHYLAMINE-5-SULPHONIC ACID), TOBIAS ACID (2-\nNAPHTHYLAMINE-1- SULPHONIC ACID) : KOCHS ACID (1-NAPHTHYLAMINE-3,6,8-TRISULPHONIC\nACID)",
+        "hsn_code": "29214524",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-1-NAPHTHYLAMINE (ALPHA-NAPHTHYLAMINE), 2- NAPHTHYLAMINE (BETANAPH\nTHYLAMINE) AND THEIR DERIVATIVES; SALTS THEREOF : BRONNERS ACID (2-NAPHTHYLAMINE-6-\nSULPHONIC ACID), CLEVES ACID (1-NAPHTHYLAMINE-6- SULPHONIC ACID), EPSILON ACID (1-\nNAPHTHYLAMINE-3,8-DISULPHONIC ACID), KOCHS ACID (1-NAPHTHYLAMINE-3,6,8-\nTRISULPHONIC ACID), LAURENTS ACID (1-NAPHTHYLAMINE-5-SULPHONIC ACID), TOBIAS ACID (2-\nNAPHTHYLAMINE-1- SULPHONIC ACID) : LAURENTS ACID (1-NAPHTHYLAMINE-5-SULPHONIC\nACID)",
+        "hsn_code": "29214525",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-1-NAPHTHYLAMINE (ALPHA-NAPHTHYLAMINE), 2- NAPHTHYLAMINE (BETANAPH\nTHYLAMINE) AND THEIR DERIVATIVES; SALTS THEREOF : BRONNERS ACID (2-NAPHTHYLAMINE-6-\nSULPHONIC ACID), CLEVES ACID (1-NAPHTHYLAMINE-6- SULPHONIC ACID), EPSILON ACID (1-\nNAPHTHYLAMINE-3,8-DISULPHONIC ACID), KOCHS ACID (1-NAPHTHYLAMINE-3,6,8-\nTRISULPHONIC ACID), LAURENTS ACID (1-NAPHTHYLAMINE-5-SULPHONIC ACID), TOBIAS ACID (2-\nNAPHTHYLAMINE-1- SULPHONIC ACID) : TOBIAS ACID (2-NAPHTHYLAMINE-1-SULPHONIC ACID)",
+        "hsn_code": "29214526",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-1-NAPHTHYLAMINE (ALPHA-NAPHTHYLAMINE), 2- NAPHTHYLAMINE (BETANAPH\nTHYLAMINE) AND THEIR DERIVATIVES; SALTS THEREOF : NAPHTHIONIC ACID (1-NAPHTHYLAMINE-\n4- SULPHONIC ACID), PARA TOLYL PERI ACID (PARA TOLYL- 1-NAPHTHYLAMINE-8-SULPHONIC\nACID), PHENYL PERI ACID (PHENYL-1-NAPHTHYLAMINE-8-SULPHONIC ACID) : NAPHTHIONIC ACID\n(1-NAPHTHYLAMINE-4-SULPHONIC ACID)",
+        "hsn_code": "29214531",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-1-NAPHTHYLAMINE (ALPHA-NAPHTHYLAMINE), 2- NAPHTHYLAMINE (BETANAPH\nTHYLAMINE) AND THEIR DERIVATIVES; SALTS THEREOF : NAPHTHIONIC ACID (1-NAPHTHYLAMINE-\n4- SULPHONIC ACID), PARA TOLYL PERI ACID (PARA TOLYL- 1-NAPHTHYLAMINE-8-SULPHONIC\nACID), PHENYL PERI ACID (PHENYL-1-NAPHTHYLAMINE-8-SULPHONIC ACID) : PARA TOLYL PERI\nACID (PARA TOLYL-1-NAPHTHYLAMINE-8-SULPHONIC ACID)",
+        "hsn_code": "29214532",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-1-NAPHTHYLAMINE (ALPHA-NAPHTHYLAMINE), 2- NAPHTHYLAMINE (BETANAPH\nTHYLAMINE) AND THEIR DERIVATIVES; SALTS THEREOF : NAPHTHIONIC ACID (1-NAPHTHYLAMINE-\n4- SULPHONIC ACID), PARA TOLYL PERI ACID (PARA TOLYL- 1-NAPHTHYLAMINE-8-SULPHONIC\nACID), PHENYL PERI ACID (PHENYL-1-NAPHTHYLAMINE-8-SULPHONIC ACID) : PHENYL PERI ACID\n(PHENYL-1-NAPHTHYLAMINE-8-SULPHONIC ACID)",
+        "hsn_code": "29214533",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-1-NAPHTHYLAMINE (ALPHA-NAPHTHYLAMINE), 2- NAPHTHYLAMINE (BETANAPH\nTHYLAMINE) AND THEIR DERIVATIVES; SALTS THEREOF : NAPHTHIONIC ACID (1-NAPHTHYLAMINE-\n4- SULPHONIC ACID), PARA TOLYL PERI ACID (PARA TOLYL- 1-NAPHTHYLAMINE-8-SULPHONIC\nACID), PHENYL PERI ACID (PHENYL-1-NAPHTHYLAMINE-8-SULPHONIC ACID) : OTHER",
+        "hsn_code": "29214590",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF : AMFETAMINE (INN), BENZFETAMINE (INN) DEXAMFETAMINE (INN),\nETILAMFETAMINE (INN) FENCAMFAMIN(INN), LEFETAMINE (INN), LEVAMFETAMINE (INN),\nMEFENOREX (INN) AND PHENTERMINE (INN); SALTS THEREOF",
+        "hsn_code": "29214600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF : OTHER : XYLIDINES",
+        "hsn_code": "29214910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC MONOAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF : OTHER : OTHER",
+        "hsn_code": "29214990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC POLYAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-O-, M-, P-PHENYLENEDIAMINE, DIAMINOTOLUENES, AND THEIR DERIVATIVES; SALTS\nTHEREOF : O-PHENYLENEDIAMINE",
+        "hsn_code": "29215110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC POLYAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-O-, M-, P-PHENYLENEDIAMINE, DIAMINOTOLUENES, AND THEIR DERIVATIVES; SALTS\nTHEREOF : M-PHENYLENEDIAMINE (M-DI AMINOBENZENE)",
+        "hsn_code": "29215120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC POLYAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-O-, M-, P-PHENYLENEDIAMINE, DIAMINOTOLUENES, AND THEIR DERIVATIVES; SALTS\nTHEREOF : P-PHENYLENEDIAMINE",
+        "hsn_code": "29215130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC POLYAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-O-, M-, P-PHENYLENEDIAMINE, DIAMINOTOLUENES, AND THEIR DERIVATIVES; SALTS\nTHEREOF : O-DIAMINOTOLUENE",
+        "hsn_code": "29215140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC POLYAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-O-, M-, P-PHENYLENEDIAMINE, DIAMINOTOLUENES, AND THEIR DERIVATIVES; SALTS\nTHEREOF : M-DIAMINOTOLUENE",
+        "hsn_code": "29215150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC POLYAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-O-, M-, P-PHENYLENEDIAMINE, DIAMINOTOLUENES, AND THEIR DERIVATIVES; SALTS\nTHEREOF : P-DIAMINOTOLUENE",
+        "hsn_code": "29215160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC POLYAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-O-, M-, P-PHENYLENEDIAMINE, DIAMINOTOLUENES, AND THEIR DERIVATIVES; SALTS\nTHEREOF : PARA-AMINO ACETANILIDE",
+        "hsn_code": "29215170",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC POLYAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-O-, M-, P-PHENYLENEDIAMINE, DIAMINOTOLUENES, AND THEIR DERIVATIVES; SALTS\nTHEREOF : META TOLUYLENE DIAMINE",
+        "hsn_code": "29215180",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC POLYAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-O-, M-, P-PHENYLENEDIAMINE, DIAMINOTOLUENES, AND THEIR DERIVATIVES; SALTS\nTHEREOF : OTHER",
+        "hsn_code": "29215190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC POLYAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-OTHER : BENZIDINE",
+        "hsn_code": "29215910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC POLYAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-OTHER : BENZIDINE DIHYDROCHLORIDE",
+        "hsn_code": "29215920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC POLYAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-OTHER : 3, 3 DICHLOROBENZIDINE DIHYDROCHLORIDE SULPHATE",
+        "hsn_code": "29215930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE- FUNCTION COMPOUNDS - AROMATIC POLYAMINES AND THEIR DERIVATIVES; SALTS\nTHEREOF :-OTHER : OTHER",
+        "hsn_code": "29215990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ALCOHOLS, OTHER THAN THOSE\nCONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS AND ESTERS; SALTS\nTHEREOF : MONOETHANOLAMINE AND ITS SALTS",
+        "hsn_code": "29221100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "2-HYDROXYL N,N-DIISOPROPYL ETHYLAMINE, N,N-DIETHYL AMINO ETHYL CHLORIDE\nHYDROCHLORIDE, DI-ETHYL AMINO ETHANETHIOL HYDROCHLORIDE, DI-METHYL AMINO ETHYL\nCHLORIDE HYDROCHLORIDE, DI-METHYL AMINO ETHANETHIOL, DI-METHYL AMINO\nETHANETHIOL HYDROCHLORIDE- - - MONOETHANOLAMINE",
+        "hsn_code": "29221110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "29221190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ALCOHOLS, OTHER THAN THOSE\nCONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS AND ESTERS; SALTS\nTHEREOF : DIETHANOLAMINE AND ITS SALTS",
+        "hsn_code": "29221200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DIETHANOLAMINE",
+        "hsn_code": "29221210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ALCOHOLS, OTHER THAN THOSE\nCONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS AND ESTERS; SALTS\nTHEREOF : DIETHANOLAMINE AND ITS SALTS : ETHYLDIETHANOLAMINE AND\nMETHYLDIETHANOLAMINE :OTHER",
+        "hsn_code": "29221290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TRIETHANOLAMINE",
+        "hsn_code": "29221310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER TRIETHANOLAMINE SALTS",
+        "hsn_code": "29221390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ALCOHOLS, OTHER THAN THOSE\nCONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS AND ESTERS; SALTS\nTHEREOF : DEXTROPROPOXYPHENE (INN) AND ITS SALTS",
+        "hsn_code": "29221400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ALCOHOLS, OTHER THAN THOSE\nCONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS AND ESTERS; SALTS\nTHEREOF : OTHER",
+        "hsn_code": "29221900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "2-HYDROXY N,N-DI-ISOPROPYL ETHYLAMINE",
+        "hsn_code": "29221940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHYLDIETHANOLAMINE",
+        "hsn_code": "29221950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "METHYLDIETHANOLAMINE",
+        "hsn_code": "29221960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "29221990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF :-AMINOHYDROXYNAPHTHALENE SULPHONIC ACIDS AND THEIR\nSALTS : AMINO-G-ACID",
+        "hsn_code": "29222110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF :-AMINOHYDROXYNAPHTHALENE SULPHONIC ACIDS AND THEIR\nSALTS : AMINO-J-ACID",
+        "hsn_code": "29222120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF :-AMINOHYDROXYNAPHTHALENE SULPHONIC ACIDS AND THEIR\nSALTS : 1-AMINO-2-NAPHTHOL-4-SULPHONIC ACID",
+        "hsn_code": "29222130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF :-AMINOHYDROXYNAPHTHALENE SULPHONIC ACIDS AND THEIR\nSALTS : GAMMA ACID",
+        "hsn_code": "29222140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF :-AMINOHYDROXYNAPHTHALENE SULPHONIC ACIDS AND THEIR\nSALTS : J ACID (2-AMINO-5-NAPHTHOL-7-SULPHONIC ACID)",
+        "hsn_code": "29222150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF :-AMINOHYDROXYNAPHTHALENE SULPHONIC ACIDS AND THEIR\nSALTS : H ACID",
+        "hsn_code": "29222160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF :-AMINOHYDROXYNAPHTHALENE SULPHONIC ACIDS AND THEIR\nSALTS : ORTHO PHENYL SULPHONYL H-ACID",
+        "hsn_code": "29222170",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF :-AMINOHYDROXYNAPHTHALENE SULPHONIC ACIDS AND THEIR\nSALTS : CHICAGO ACID",
+        "hsn_code": "29222180",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF :-AMINOHYDROXYNAPHTHALENE SULPHONIC ACIDS AND THEIR\nSALTS : OTHER",
+        "hsn_code": "29222190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : ANISIDINES, DIANISIDINES, PHENETIDINES AND THEIR SALTS :\nORTHO ANISIDINES",
+        "hsn_code": "29222210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : ANISIDINES, DIANISIDINES, PHENETIDINES AND THEIR SALTS :\nPARA ANISIDINES",
+        "hsn_code": "29222220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : ANISIDINES, DIANISIDINES, PHENETIDINES AND THEIR SALTS :\nORTHO PHENETIDINE (2-AMINO-PHENITOLE)",
+        "hsn_code": "29222230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : ANISIDINES, DIANISIDINES, PHENETIDINES AND THEIR SALTS :\nOTHER",
+        "hsn_code": "29222290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : OTHER : 2-AMINO 4-NITROPHENOL, META AMINOPHENOL, PARA\nAMINOPHENOL, META DIETHYL AMINO-PHENOL: 2-AMINO 4-NITROPHENOL",
+        "hsn_code": "29222911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : OTHER : 2-AMINO 4-NITROPHENOL, META AMINOPHENOL, PARA\nAMINOPHENOL, META DIETHYL AMINO-PHENOL: META AMINOPHENOL",
+        "hsn_code": "29222912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : OTHER : 2-AMINO 4-NITROPHENOL, META AMINOPHENOL, PARA\nAMINOPHENOL, META DIETHYL AMINO-PHENOL: PARA AMINOPHENOL",
+        "hsn_code": "29222913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : OTHER : 2-AMINO 4-NITROPHENOL, META AMINOPHENOL, PARA\nAMINOPHENOL, META DIETHYL AMINO-PHENOL: META DIETHYL AMINO-PHENOL",
+        "hsn_code": "29222914",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : OTHER : 2-AMINO-1-PHENOL-4-SULPHONIC ACID, 6-NITRO\nOAMINOPHENOL- 4-SULPHONIC ACID, PHENYL GAMMA ACID (PHENYL 2-AMINO-NAPHTHOL-6-\nSULPHONIC ACID), PHENYL J ACID (PHENYL-2-AMINO-8 NAPHTHOL-7-SULPHONIC ACID), S ACID,\nPERI ACID (1-AMINO-8-NAPHTHOL-4-4-SULPHOXINIC ACID, 1- NAPHTHYLAMINE-8-SULPHONIC\nACID), METAPHENYLENE DIAMINE-4-SULPHONIC ACID : 2-AMINO-1-PHENOL-4-SULPHONIC ACID",
+        "hsn_code": "29222921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : OTHER : 2-AMINO-1-PHENOL-4-SULPHONIC ACID, 6-NITRO\nOAMINOPHENOL- 4-SULPHONIC ACID, PHENYL GAMMA ACID (PHENYL 2-AMINO-NAPHTHOL-6-\nSULPHONIC ACID), PHENYL J ACID (PHENYL-2-AMINO-8 NAPHTHOL-7-SULPHONIC ACID), S ACID,\nPERI ACID (1-AMINO-8-NAPHTHOL-4-4-SULPHOXINIC ACID, 1- NAPHTHYLAMINE-8-SULPHONIC\nACID), METAPHENYLENE DIAMINE-4-SULPHONIC ACID : 6-NITRO-O-AMINOPHENOL-4-\nSULPHONIC ACID",
+        "hsn_code": "29222922",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : OTHER : 2-AMINO-1-PHENOL-4-SULPHONIC ACID, 6-NITRO\nOAMINOPHENOL- 4-SULPHONIC ACID, PHENYL GAMMA ACID (PHENYL 2-AMINO-NAPHTHOL-6-\nSULPHONIC ACID), PHENYL J ACID (PHENYL-2-AMINO-8 NAPHTHOL-7-SULPHONIC ACID), S ACID,\nPERI ACID (1-AMINO-8-NAPHTHOL-4-4-SULPHOXINIC ACID, 1- NAPHTHYLAMINE-8-SULPHONIC\nACID), METAPHENYLENE DIAMINE-4-SULPHONIC ACID : PHENYL GAMMA ACID (PHENYL 2-\nAMINO-NAPHTHOL-6-SULPHONIC ACID)",
+        "hsn_code": "29222923",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : OTHER : 2-AMINO-1-PHENOL-4-SULPHONIC ACID, 6-NITRO\nOAMINOPHENOL- 4-SULPHONIC ACID, PHENYL GAMMA ACID (PHENYL 2-AMINO-NAPHTHOL-6-\nSULPHONIC ACID), PHENYL J ACID (PHENYL-2-AMINO-8 NAPHTHOL-7-SULPHONIC ACID), S ACID,\nPERI ACID (1-AMINO-8-NAPHTHOL-4-4-SULPHOXINIC ACID, 1- NAPHTHYLAMINE-8-SULPHONIC\nACID), METAPHENYLENE DIAMINE-4-SULPHONIC ACID : PHENYL J ACID (PHENYL-2-AMINO-8\nNAPHTHOL-7-SULPHONIC ACID)",
+        "hsn_code": "29222924",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : OTHER : 2-AMINO-1-PHENOL-4-SULPHONIC ACID, 6-NITRO\nOAMINOPHENOL- 4-SULPHONIC ACID, PHENYL GAMMA ACID (PHENYL 2-AMINO-NAPHTHOL-6-\nSULPHONIC ACID), PHENYL J ACID (PHENYL-2-AMINO-8 NAPHTHOL-7-SULPHONIC ACID), S ACID,\nPERI ACID (1-AMINO-8-NAPHTHOL-4-4-SULPHOXINIC ACID, 1- NAPHTHYLAMINE-8-SULPHONIC\nACID), METAPHENYLENE DIAMINE-4-SULPHONIC ACID : S ACID, PERI ACID (1-AMINO-8-\nNAPHTHOL-4-4-SULPHOXINIC ACID, 1-NAPHTHYLAMINE-8-SULPHONIC ACID)",
+        "hsn_code": "29222925",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : OTHER : 2-AMINO-1-PHENOL-4-SULPHONIC ACID, 6-NITRO\nOAMINOPHENOL- 4-SULPHONIC ACID, PHENYL GAMMA ACID (PHENYL 2-AMINO-NAPHTHOL-6-\nSULPHONIC ACID), PHENYL J ACID (PHENYL-2-AMINO-8 NAPHTHOL-7-SULPHONIC ACID), S ACID,\nPERI ACID (1-AMINO-8-NAPHTHOL-4-4-SULPHOXINIC ACID, 1- NAPHTHYLAMINE-8-SULPHONIC\nACID), METAPHENYLENE DIAMINE-4-SULPHONIC ACID : META-PHENYLENE DIAMINE-4-\nSULPHONIC ACID",
+        "hsn_code": "29222926",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : OTHER : N-METHYL-PARA-AMINOPHENOL SULPHATE (MOTOL),2,\n5 DIMETHOXY ANILINE, PARA ACETYL AMINOPHENOL (PARACETAMOL), PARA CRESIDINE,\nPICRAMIC ACID (T-GRADE) : N-METHYL-PARA-AMINOPHENOL SULPHATE (MOTOL)",
+        "hsn_code": "29222931",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : OTHER : N-METHYL-PARA-AMINOPHENOL SULPHATE (MOTOL),2,\n5 DIMETHOXY ANILINE, PARA ACETYL AMINOPHENOL (PARACETAMOL), PARA CRESIDINE,\nPICRAMIC ACID (T-GRADE) : 2, 5 DIMETHOXY ANILINE",
+        "hsn_code": "29222932",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : OTHER : N-METHYL-PARA-AMINOPHENOL SULPHATE (MOTOL),2,\n5 DIMETHOXY ANILINE, PARA ACETYL AMINOPHENOL (PARACETAMOL), PARA CRESIDINE,\nPICRAMIC ACID (T-GRADE) : PARA ACETYL AMINOPHENOL (PARACETAMOL)",
+        "hsn_code": "29222933",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : OTHER : N-METHYL-PARA-AMINOPHENOL SULPHATE (MOTOL),2,\n5 DIMETHOXY ANILINE, PARA ACETYL AMINOPHENOL (PARACETAMOL), PARA CRESIDINE,\nPICRAMIC ACID (T-GRADE) : PARA CRESIDINE",
+        "hsn_code": "29222934",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : OTHER : N-METHYL-PARA-AMINOPHENOL SULPHATE (MOTOL),2,\n5 DIMETHOXY ANILINE, PARA ACETYL AMINOPHENOL (PARACETAMOL), PARA CRESIDINE,\nPICRAMIC ACID (T-GRADE) : PICRAMIC ACID (T-GRADE)",
+        "hsn_code": "29222935",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-NAPHTHOLS AND OTHER AMINO-PHENOLS,\nOTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN FUNCTION, THEIR ETHERS\nAND ESTERS; SALTS THEREOF : OTHER : OTHER",
+        "hsn_code": "29222990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ALDEHYDES , AMINO-KETONES AND\nAMINOQUINONES, OTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN\nFUNCTION; SALTS THEREOF : AMFEPRAMONE (INN) AND SALTS THEREOF",
+        "hsn_code": "29223100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ALDEHYDES , AMINO-KETONES AND\nAMINOQUINONES, OTHER THAN THOSE CONTAINING MORE THAN ONE KIND OF OXYGEN\nFUNCTION; SALTS THEREOF : OTHER",
+        "hsn_code": "29223900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ACIDS, OTHER THAN THOSE CONTAINING\nMORE THAN ONE KIND OF OXYGEN FUNCTION, AND THEIR ESTERS; SALTS THEREOF : LYSINE AND\nITS ESTERS; SALTS THEREOF",
+        "hsn_code": "29224100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ACIDS, OTHER THAN THOSE CONTAINING\nMORE THAN ONE KIND OF OXYGEN FUNCTION, AND THEIR ESTERS; SALTS THEREOF :-GLUTAMIC\nACID AND ITS SALTS : GLUTAMIC ACID",
+        "hsn_code": "29224210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ACIDS, OTHER THAN THOSE CONTAINING\nMORE THAN ONE KIND OF OXYGEN FUNCTION, AND THEIR ESTERS; SALTS THEREOF :-GLUTAMIC\nACID AND ITS SALTS : MONOSODIUM GLUTAMATE (AGINAMOTO)",
+        "hsn_code": "29224220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ACIDS, OTHER THAN THOSE CONTAINING\nMORE THAN ONE KIND OF OXYGEN FUNCTION, AND THEIR ESTERS; SALTS THEREOF :-GLUTAMIC\nACID AND ITS SALTS : OTHER",
+        "hsn_code": "29224290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ACIDS, OTHER THAN THOSE CONTAINING\nMORE THAN ONE KIND OF OXYGEN FUNCTION, AND THEIR ESTERS; SALTS THEREOF :\nANTHRANILIC ACID AND ITS SALTS",
+        "hsn_code": "29224300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ACIDS, OTHER THAN THOSE CONTAINING\nMORE THAN ONE KIND OF OXYGEN FUNCTION, AND THEIR ESTERS; SALTS THEREOF : TILIDINE\n(INN) AND ITS SALTS",
+        "hsn_code": "29224400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ACIDS, OTHER THAN THOSE CONTAINING\nMORE THAN ONE KIND OF OXYGEN FUNCTION, AND THEIR ESTERS; SALTS THEREOF : OTHER :\nAMINO ACETIC ACID (GLYCINE)",
+        "hsn_code": "29224910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ACIDS, OTHER THAN THOSE CONTAINING\nMORE THAN ONE KIND OF OXYGEN FUNCTION, AND THEIR ESTERS; SALTS THEREOF : OTHER : N\nMETHYL TAURINE",
+        "hsn_code": "29224920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ACIDS, OTHER THAN THOSE CONTAINING\nMORE THAN ONE KIND OF OXYGEN FUNCTION, AND THEIR ESTERS; SALTS THEREOF : OTHER :\nOTHER",
+        "hsn_code": "29224990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ALCOHOL-PHENOLS, AMINO-ACID-PHENOLS\nAND OTHER AMINO-COMPOUNDS WITH OXYGEN FUNCTION : PARA-AMINO-SALICYLIC ACID,\nMETHYL ANTHRANILATE, PROCAINE HYDROCHLORIDE, AMINO ANISIC ACID ANILIDE,L-TYROSINE\n(P-HYDROXYPHENYL AMINE) : PARA-AMINO-SALICYLIC ACID",
+        "hsn_code": "29225011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ALCOHOL-PHENOLS, AMINO-ACID-PHENOLS\nAND OTHER AMINO-COMPOUNDS WITH OXYGEN FUNCTION : PARA-AMINO-SALICYLIC ACID,\nMETHYL ANTHRANILATE, PROCAINE HYDROCHLORIDE, AMINO ANISIC ACID ANILIDE,L-TYROSINE\n(P-HYDROXYPHENYL AMINE) : METHYL ANTHRANILATE",
+        "hsn_code": "29225012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ALCOHOL-PHENOLS, AMINO-ACID-PHENOLS\nAND OTHER AMINO-COMPOUNDS WITH OXYGEN FUNCTION : PARA-AMINO-SALICYLIC ACID,\nMETHYL ANTHRANILATE, PROCAINE HYDROCHLORIDE, AMINO ANISIC ACID ANILIDE,L-TYROSINE\n(P-HYDROXYPHENYL AMINE) : PROCAINE HYDROCHLORIDE",
+        "hsn_code": "29225013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ALCOHOL-PHENOLS, AMINO-ACID-PHENOLS\nAND OTHER AMINO-COMPOUNDS WITH OXYGEN FUNCTION : PARA-AMINO-SALICYLIC ACID,\nMETHYL ANTHRANILATE, PROCAINE HYDROCHLORIDE, AMINO ANISIC ACID ANILIDE,L-TYROSINE\n(P-HYDROXYPHENYL AMINE) : AMINO ANISIC ACID ANILIDE",
+        "hsn_code": "29225014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ALCOHOL-PHENOLS, AMINO-ACID-PHENOLS\nAND OTHER AMINO-COMPOUNDS WITH OXYGEN FUNCTION : PARA-AMINO-SALICYLIC ACID,\nMETHYL ANTHRANILATE, PROCAINE HYDROCHLORIDE, AMINO ANISIC ACID ANILIDE,L-TYROSINE\n(P-HYDROXYPHENYL AMINE) : L-TYROSINE (P-HYDROXYPHENYL AMINE)",
+        "hsn_code": "29225015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ALCOHOL-PHENOLS, AMINO-ACID-PHENOLS\nAND OTHER AMINO-COMPOUNDS WITH OXYGEN FUNCTION : FRUSEMIDE, AMINODIAL, N\nACETYL ANTHRANILIC ACID, DOMPERIDONE : FRUSEMIDE",
+        "hsn_code": "29225021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ALCOHOL-PHENOLS, AMINO-ACID-PHENOLS\nAND OTHER AMINO-COMPOUNDS WITH OXYGEN FUNCTION : FRUSEMIDE, AMINODIAL, N\nACETYL ANTHRANILIC ACID, DOMPERIDONE : AMINODIAL",
+        "hsn_code": "29225022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ALCOHOL-PHENOLS, AMINO-ACID-PHENOLS\nAND OTHER AMINO-COMPOUNDS WITH OXYGEN FUNCTION : FRUSEMIDE, AMINODIAL, N\nACETYL ANTHRANILIC ACID, DOMPERIDONE : N-ACETYL ANTHRANILIC ACID",
+        "hsn_code": "29225023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ALCOHOL-PHENOLS, AMINO-ACID-PHENOLS\nAND OTHER AMINO-COMPOUNDS WITH OXYGEN FUNCTION : FRUSEMIDE, AMINODIAL, N\nACETYL ANTHRANILIC ACID, DOMPERIDONE : DOMPERIDONE",
+        "hsn_code": "29225024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS - AMINO-ALCOHOL-PHENOLS, AMINO-ACID-PHENOLS\nAND OTHER AMINO-COMPOUNDS WITH OXYGEN FUNCTION : OTHER",
+        "hsn_code": "29225090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "QUATERNARY AMMONIUM SALTS AND HYDROXIDES; LECITHINS AND OTHER\nPHOSPHOAMINOLIPIDS, WHETHER OR NOT CHEMICALLY DEFINED CHOLINE AND ITS SALTS",
+        "hsn_code": "29231000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "QUATERNARY AMMONIUM SALTS AND HYDROXIDES; LECITHINS AND OTHER\nPHOSPHOAMINOLIPIDS, WHETHER OR NOT CHEMICALLY DEFINED - LECITHINS AND OTHER\nPHOSPHOAMINOLIPIDS : LECITHINS",
+        "hsn_code": "29232010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "QUATERNARY AMMONIUM SALTS AND HYDROXIDES; LECITHINS AND OTHER\nPHOSPHOAMINOLIPIDS, WHETHER OR NOT CHEMICALLY DEFINED - LECITHINS AND OTHER\nPHOSPHOAMINOLIPIDS : OTHER",
+        "hsn_code": "29232090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "QUATERNARY AMMONIUM SALTS AND HYDROXIDES; LECITHINS AND OTHER\nPHOSPHOAMINOLIPIDS, WHETHER OR NOT CHEMICALLY DEFINED OTHER",
+        "hsn_code": "29239000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYAMIDE-FUNCTION COMPOUNDS; AMIDE-FUNCTION COMPOUNDS OF CARBONIC ACID -\nACYCLIC AMIDES (INCLUDING ACYCLIC CARBAMATES) AND THEIR DERIVATIVES; SALTS THEREOF :\nMEPROBAMATE (INN)",
+        "hsn_code": "29241100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYAMIDE-FUNCTION COMPOUNDS; AMIDE-FUNCTION COMPOUNDS OF CARBONIC ACID -\nACYCLIC AMIDES (INCLUDING ACYCLIC CARBAMATES) AND THEIR DERIVATIVES; SALTS THEREOF : -\n- FLUOROACETAMIDE (ISO), MONOCROTOPHOS (ISO) AND PHOSPHAMIDON (IS0)",
+        "hsn_code": "29241200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYAMIDE-FUNCTION COMPOUNDS; AMIDE-FUNCTION COMPOUNDS OF CARBONIC ACID -\nACYCLIC AMIDES (INCLUDING ACYCLIC CARBAMATES) AND THEIR DERIVATIVES; SALTS THEREOF :\nOTHER",
+        "hsn_code": "29241900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYAMIDE-FUNCTION COMPOUNDS; AMIDE-FUNCTION COMPOUNDS OF CARBONIC ACID -\nCYCLIC AMIDES (INCLUDING CYCLIC CARBAMATES) AND THEIR DERIVATIVES; SALTS THEREOF :-\nUREINES AND THEIR DERIVATIVES; SALTS THEREOF : DIETHYL DIPHENYL UREA",
+        "hsn_code": "29242110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYAMIDE-FUNCTION COMPOUNDS; AMIDE-FUNCTION COMPOUNDS OF CARBONIC ACID -\nCYCLIC AMIDES (INCLUDING CYCLIC CARBAMATES) AND THEIR DERIVATIVES; SALTS THEREOF :-\nUREINES AND THEIR DERIVATIVES; SALTS THEREOF : DIMETHYL DIPHENYL UREA (ZENTRALIN)",
+        "hsn_code": "29242120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYAMIDE-FUNCTION COMPOUNDS; AMIDE-FUNCTION COMPOUNDS OF CARBONIC ACID -\nCYCLIC AMIDES (INCLUDING CYCLIC CARBAMATES) AND THEIR DERIVATIVES; SALTS THEREOF :-\nUREINES AND THEIR DERIVATIVES; SALTS THEREOF : PARACHLORO BENZENE SULPHONYL UREA",
+        "hsn_code": "29242130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYAMIDE-FUNCTION COMPOUNDS; AMIDE-FUNCTION COMPOUNDS OF CARBONIC ACID -\nCYCLIC AMIDES (INCLUDING CYCLIC CARBAMATES) AND THEIR DERIVATIVES; SALTS THEREOF :-\nUREINES AND THEIR DERIVATIVES; SALTS THEREOF : OTHER",
+        "hsn_code": "29242190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYAMIDE-FUNCTION COMPOUNDS; AMIDE-FUNCTION COMPOUNDS OF CARBONIC ACID -\nCYCLIC AMIDES (INCLUDING CYCLIC CARBAMATES) AND THEIR DERIVATIVES; SALTS THEREOF : 2-\nACETAMIDOBENZOIC ACID (N-ACETYLANTHRANILIC ACID) AND ITS SALTS",
+        "hsn_code": "29242300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYAMIDE-FUNCTION COMPOUNDS; AMIDE-FUNCTION COMPOUNDS OF CARBONIC ACID -\nCYCLIC AMIDES (INCLUDING CYCLIC CARBAMATES) AND THEIR DERIVATIVES; SALTS THEREOF :\nETHINAMATE (INN)",
+        "hsn_code": "29242400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYAMIDE-FUNCTION COMPOUNDS; AMIDE-FUNCTION COMPOUNDS OF CARBONIC ACID -\nCYCLIC AMIDES (INCLUDING CYCLIC CARBAMATES) AND THEIR DERIVATIVES; SALTS THEREOF :\nOTHER : ACETANILIDE",
+        "hsn_code": "29242910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYAMIDE-FUNCTION COMPOUNDS; AMIDE-FUNCTION COMPOUNDS OF CARBONIC ACID -\nCYCLIC AMIDES (INCLUDING CYCLIC CARBAMATES) AND THEIR DERIVATIVES; SALTS THEREOF :\nOTHER : ACETO ACETANILIDE",
+        "hsn_code": "29242920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYAMIDE-FUNCTION COMPOUNDS; AMIDE-FUNCTION COMPOUNDS OF CARBONIC ACID -\nCYCLIC AMIDES (INCLUDING CYCLIC CARBAMATES) AND THEIR DERIVATIVES; SALTS THEREOF :\nOTHER : ACETO ACETIC ORTHO CHLORANILIDE",
+        "hsn_code": "29242930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYAMIDE-FUNCTION COMPOUNDS; AMIDE-FUNCTION COMPOUNDS OF CARBONIC ACID -\nCYCLIC AMIDES (INCLUDING CYCLIC CARBAMATES) AND THEIR DERIVATIVES; SALTS THEREOF :\nOTHER : ACETO ACETIC PARA CHLORANILIDE",
+        "hsn_code": "29242940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYAMIDE-FUNCTION COMPOUNDS; AMIDE-FUNCTION COMPOUNDS OF CARBONIC ACID -\nCYCLIC AMIDES (INCLUDING CYCLIC CARBAMATES) AND THEIR DERIVATIVES; SALTS THEREOF :\nOTHER : PHENYL ACETAMIDE",
+        "hsn_code": "29242950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYAMIDE-FUNCTION COMPOUNDS; AMIDE-FUNCTION COMPOUNDS OF CARBONIC ACID -\nCYCLIC AMIDES (INCLUDING CYCLIC CARBAMATES) AND THEIR DERIVATIVES; SALTS THEREOF :\nOTHER : PYRAZINAMIDE (PYRAZINE CARBOXAMIDE)",
+        "hsn_code": "29242960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYAMIDE-FUNCTION COMPOUNDS; AMIDE-FUNCTION COMPOUNDS OF CARBONIC ACID -\nCYCLIC AMIDES (INCLUDING CYCLIC CARBAMATES) AND THEIR DERIVATIVES; SALTS THEREOF :\nOTHER : OTHER",
+        "hsn_code": "29242990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYIMIDE-FUNCTION COMPOUNDS (INCLUDING SACCHARIN AND ITS SALTS) AND IMINE\nFUNCTION COMPOUNDS - IMIDES AND THEIR DERIVATIVES; SALTS THEREOF : SACCHARIN AND\nITS SALTS",
+        "hsn_code": "29251100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYIMIDE-FUNCTION COMPOUNDS (INCLUDING SACCHARIN AND ITS SALTS) AND IMINE\nFUNCTION COMPOUNDS - IMIDES AND THEIR DERIVATIVES; SALTS THEREOF : GLUTETHIMIDE\n(INN)",
+        "hsn_code": "29251200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYIMIDE-FUNCTION COMPOUNDS (INCLUDING SACCHARIN AND ITS SALTS) AND IMINE\nFUNCTION COMPOUNDS - IMIDES AND THEIR DERIVATIVES; SALTS THEREOF : OTHER",
+        "hsn_code": "29251900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYIMIDE-FUNCTION COMPOUNDS (INCLUDING SACCHARIN AND ITS SALTS) AND IMINE\nFUNCTION COMPOUNDS - IMINES AND THEIR DERIVATIVES; SALTS THEREOF : GUANIDINE\nNITRATE",
+        "hsn_code": "29252010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYIMIDE-FUNCTION COMPOUNDS (INCLUDING SACCHARIN AND ITS SALTS) AND IMINE\nFUNCTION COMPOUNDS - IMINES AND THEIR DERIVATIVES; SALTS THEREOF : OTHER",
+        "hsn_code": "29252090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYIMIDE-FUNCTION COMPOUNDS (INCLUDING SACCHARIN AND ITS SALTS) AND IMINE\nFUNCTION COMPOUNDS - IMINES AND THEIR DERIVATIVES; SALTS THEREOF : CHLORDIMEFORM\n(ISO)",
+        "hsn_code": "29252100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYIMIDE-FUNCTION COMPOUNDS (INCLUDING SACCHARIN AND ITS SALTS) AND IMINE\nFUNCTION COMPOUNDS - IMINES AND THEIR DERIVATIVES; SALTS THEREOF : -- OTHER ---\nGUANIDINE NITRATE",
+        "hsn_code": "29252910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYIMIDE-FUNCTION COMPOUNDS (INCLUDING SACCHARIN AND ITS SALTS) AND IMINE\nFUNCTION COMPOUNDS - IMINES AND THEIR DERIVATIVES; SALTS THEREOF : -- OTHER --- OTHER",
+        "hsn_code": "29252990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NITRILE-FUNCTION COMPOUNDS ACRYLONITRILE",
+        "hsn_code": "29261000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NITRILE-FUNCTION COMPOUNDS 1-CYANOGUANIDINE (DICYANDIAMIDE)",
+        "hsn_code": "29262000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NITRILE-FUNCTION COMPOUNDS FENPROPOREX (INN) AND ITS SALTS",
+        "hsn_code": "29263000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NITRILE-FUNCTION COMPOUNDS OTHER",
+        "hsn_code": "29269000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DIAZO-, AZO- OR AZOXY-COMPOUNDS - DIAZO-, AZO- OR AZOXY- COMPOUNDS : PARA AMINO\nAZO-BENZENE",
+        "hsn_code": "29270010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DIAZO-, AZO- OR AZOXY-COMPOUNDS - DIAZO-, AZO- OR AZOXY- COMPOUNDS : OTHER",
+        "hsn_code": "29270090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC DERIVATIVES OF HYDRAZINE OR OF HYDROXYLAMINE - ORGANIC DERIVATIVES OF\nHYDRAZINE OR OF HYDROXYLAMINE : ISONIAZID",
+        "hsn_code": "29280010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC DERIVATIVES OF HYDRAZINE OR OF HYDROXYLAMINE - ORGANIC DERIVATIVES OF\nHYDRAZINE OR OF HYDROXYLAMINE : OTHER",
+        "hsn_code": "29280090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDS WITH OTHER NITROGEN FUNCTION - ISOCYANATES : PHENYL ISOCYANATE",
+        "hsn_code": "29291010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDS WITH OTHER NITROGEN FUNCTION - ISOCYANATES : TOLUENE DI-ISOCYANATE",
+        "hsn_code": "29291020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDS WITH OTHER NITROGEN FUNCTION - ISOCYANATES : OTHER",
+        "hsn_code": "29291090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "N,N-Diethylphosphoramidicdichloride",
+        "hsn_code": "29299010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "N,N-Diisopropylphosphoramidicdichloride",
+        "hsn_code": "29299020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "N,N-DIPROPYLPHOSPHORAMIDICDICHLORIDE",
+        "hsn_code": "29299030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "N,N-DIMETHYLPHOSPHORAMIDICDICHLORIDE",
+        "hsn_code": "29299040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DIETHYL N,N-DIMETHYLPHOSPHORAMIDATE",
+        "hsn_code": "29299050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHORAMIDIC ACID, DIETHYL, DIMETHYLESTER",
+        "hsn_code": "29299060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "29299090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORGANO-SULPHUR COMPOUNDS - DITHIOCARBONATES (XANTHATES)",
+        "hsn_code": "29301000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORGANO-SULPHUR COMPOUNDS - THIOCARBAMATES AND DITHIOCARBAMATES",
+        "hsn_code": "29302000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORGANO-SULPHUR COMPOUNDS - THIURAM MONO-, DI OR TETRASULPHIDES",
+        "hsn_code": "29303000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORGANO-SULPHUR COMPOUNDS - METHIONINE",
+        "hsn_code": "29304000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORGANOSULPHUR COMPOUNDS CAPTAFOL ISO AND METHAMIDOPHOS ISO",
+        "hsn_code": "29305000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORGANO-SULPHUR COMPOUNDS - OTHER : THIOUREA (SULPHOUREA)",
+        "hsn_code": "29309010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORGANO-SULPHUR COMPOUNDS - OTHER : CALCIUM SALTS OF METHIONINE",
+        "hsn_code": "29309020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORGANO-SULPHUR COMPOUNDS - OTHER : THIO SULPHONIC ACID",
+        "hsn_code": "29309030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORGANO-SULPHUR COMPOUNDS - OTHER : L-CYSTINE (ALPHA-AMINO BETA-THIOPROPIONIC\nACID)- SULPHUR CONTAINING AMINO ACID",
+        "hsn_code": "29309040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORGANO-SULPHUR COMPOUNDS - OTHER : SULPHINIC ACID",
+        "hsn_code": "29309050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORGANO-SULPHUR COMPOUNDS - OTHER : SULPHOXIDE",
+        "hsn_code": "29309060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORGANO-SULPHUR COMPOUNDS - OTHER : MERCAPTAN",
+        "hsn_code": "29309070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORGANO-SULPHUR COMPOUNDS - OTHER : ALLYL ISOTHIOCYANATE",
+        "hsn_code": "29309080",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORGANO-SULPHUR COMPOUNDS - OTHER : OTHER",
+        "hsn_code": "29309090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHANOL, 2,2-THIOBIS-",
+        "hsn_code": "29309091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DI-METHYL AMINO ETHANETHIOL",
+        "hsn_code": "29309092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DI-METHYL AMINO ETHANETHIOL HYDROCHLORIDE",
+        "hsn_code": "29309093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DIETHYL AMINO ETHANETHIOL",
+        "hsn_code": "29309094",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DI-ETHYL AMINO ETHANETHIOL HYDROCHLORIDE",
+        "hsn_code": "29309095",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "O-ETHYL S-PHENYL ETHYLPHOSPHONOTHIOLOTHIONATE (FONOFOS)",
+        "hsn_code": "29309096",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHOROTHIOIC ACID,S[2-(DIETHYL AMINO) ETHYL]O, O-DIETHYL ESTER",
+        "hsn_code": "29309097",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "29309099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANO-INORGANIC COMPOUNDS - OTHER ORGANO-INORGANIC COMPOUNDS :\nORGANO-MERCURY COMPOUNDS",
+        "hsn_code": "29310010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHONIC ACID,METHYL -",
+        "hsn_code": "29310041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHONIC ACID,ETHYL",
+        "hsn_code": "29310042",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHONIC ACID,PROPYL",
+        "hsn_code": "29310043",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHINIC ACID,METHYL-",
+        "hsn_code": "29310044",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPONIC ACIDE,[METHYL-(5-ETHYL-2-METHYL 2-OXIDO- 1,3,2-DIOXAPHOSPHORINAN-5-YL)\nMETHYL]ESTER",
+        "hsn_code": "29310051",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHONIC ACIDE,[METHYL-BIS (5-ETHYL-2-METHYL 2-OXIDO- 1,3,2-DIOXAPHOSPHORINAN-5-\nYL) METHYL] ESTER",
+        "hsn_code": "29310052",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHONIC ACID,METHYL,DIMETHYL ESTER",
+        "hsn_code": "29310053",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHONIC ACID PROPYL DIMETHYL ESTER",
+        "hsn_code": "29310054",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHONIC ACID ETHYL-DIETHYL ESTER",
+        "hsn_code": "29310055",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHONOCHLORIDIC ACID METHYL-,METHYL ESTER",
+        "hsn_code": "29310056",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHONOUS ACID- METHYL DIETHYL ESTER",
+        "hsn_code": "29310057",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "1 PROPANAMINIUM N,N,N TRIMETHYL 3-[1-OXO-9-OCTADECENYL AMINO] -(Z)-METHYL METHYL\nPHOSPHONATE",
+        "hsn_code": "29310061",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "O,O-DIMETHYL METHYL PHOSPHONATE",
+        "hsn_code": "29310062",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHONOTHIOIC DICHLORIDE,ETHYL-",
+        "hsn_code": "29310071",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHONIC DICHLORIDE,METHYL",
+        "hsn_code": "29310072",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHONOUS DICHLORIDE,METHYL-",
+        "hsn_code": "29310073",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANO-INORGANIC COMPOUNDS-TETRAMETHYL LEAD AND TETRAETHYL LEAD---\nTETRAMETHYL LEAD",
+        "hsn_code": "29311010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANO-INORGANIC COMPOUNDS-TETRAMETHYL LEAD AND TETRAETHYL LEAD---\nTETRAETHYL LEAD",
+        "hsn_code": "29311020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANO-INORGANIC COMPOUNDS -TRIBUTYLTIN COMPOUNDS",
+        "hsn_code": "29312000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANO-INORGANIC COMPOUNDS -OTHER---ORGANO ARSENIC COMPOUNDS",
+        "hsn_code": "29319010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANO-INORGANIC COMPOUNDS -OTHER---OTHER",
+        "hsn_code": "29319090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH OXYGEN HETERO-ATOM (S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED FURAN RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE : TETRAHYDROFURAN",
+        "hsn_code": "29321100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH OXYGEN HETERO-ATOM (S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED FURAN RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE : 2-FURALDEHYDE (FURFURALDEHYDE)",
+        "hsn_code": "29321200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH OXYGEN HETERO-ATOM (S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED FURAN RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE : FURFURYL ALCOHOL AND TETRAHYDROFURFURYL ALCOHOL",
+        "hsn_code": "29321300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH OXYGEN HETERO-ATOM (S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED FURAN RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE : OTHER : HYDROXY DIBENZFURAN CARBOXYLIC ACID",
+        "hsn_code": "29321910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH OXYGEN HETERO-ATOM (S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED FURAN RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE : OTHER : OTHER",
+        "hsn_code": "29321990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH OXYGEN HETERO-ATOM(S) ONLY - LACTONES---COUMARIN,\nMETHYLCOUMARINS AND ETHYLCOUMARINS",
+        "hsn_code": "29322010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH OXYGEN HETERO-ATOM(S) ONLY - LACTONES---\nPHENOLPHTHALEIN",
+        "hsn_code": "29322020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH OXYGEN HETERO-ATOM(S) ONLY - LACTONES---OTHER",
+        "hsn_code": "29322090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH OXYGEN HETERO-ATOM (S) ONLY - OTHER : ISOSAFROLE",
+        "hsn_code": "29329100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH OXYGEN HETERO-ATOM (S) ONLY - OTHER : 1-(1,3-\nBENZODIOXOL-5-YL) PROPAN-2-ONE",
+        "hsn_code": "29329200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH OXYGEN HETERO-ATOM (S) ONLY - OTHER : PIPERONAL",
+        "hsn_code": "29329300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH OXYGEN HETERO-ATOM (S) ONLY - OTHER : SAFROLE",
+        "hsn_code": "29329400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH OXYGEN HETERO-ATOM (S) ONLY - OTHER :\nTETRAHYDROCANNABINOLS (ALL ISOMERS)",
+        "hsn_code": "29329500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH OXYGEN HETERO-ATOM (S) ONLY - OTHER : OTHER",
+        "hsn_code": "29329900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED PYRAZOLE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE : PHENAZONE (ANTIPYRIN) AND ITS DERIVATES",
+        "hsn_code": "29331100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED PYRAZOLE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : 3-CARBOXY (PARA SULPHO-PHENYL)-5- PYRAZOLONE",
+        "hsn_code": "29331910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED PYRAZOLE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : 1 (2,5- DICHLORO-4-SULPHO PHENYL)-3-METHYL-5-PYRAZOLONE",
+        "hsn_code": "29331920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED PYRAZOLE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : 3-METHYL-1(4-SULPHO-O-TOLUYL-5-PYRAZOLONE)",
+        "hsn_code": "29331930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED PYRAZOLE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : PHENYLMETHYLPYRAZOLONE",
+        "hsn_code": "29331940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED PYRAZOLE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : 1-PHENYL-5-PYRAZOLONE-3-CARBOXYLIC ACID ETHYLESTER",
+        "hsn_code": "29331950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED PYRAZOLE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : 1-(M-SULPHOPHENYL)-3-PYRAZOLONE",
+        "hsn_code": "29331960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED PYRAZOLE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : ANALGIN",
+        "hsn_code": "29331970",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED PYRAZOLE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : OXYPHENBUTAZONE",
+        "hsn_code": "29331980",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED PYRAZOLE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : OTHER",
+        "hsn_code": "29331990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED IMIDAZOLE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE : HYDANTOIN AND ITS DERIVATIVES",
+        "hsn_code": "29332100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED IMIDAZOLE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : TINIDAZOLE",
+        "hsn_code": "29332910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED IMIDAZOLE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : METRONIDAZOLE, METRONIDIAZOLE BENZOATE",
+        "hsn_code": "29332920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED IMIDAZOLE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : MEBENDAZOLE",
+        "hsn_code": "29332930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED IMIDAZOLE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : DIMETRIDAZOLE",
+        "hsn_code": "29332940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED IMIDAZOLE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : ALBENDAZOLE",
+        "hsn_code": "29332950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED IMIDAZOLE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : OTHER",
+        "hsn_code": "29332990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAING AN UNFUSED PYRIDINE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE : PYRIDINE AND ITS SALTS",
+        "hsn_code": "29333100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAING AN UNFUSED PYRIDINE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE : PIPERIDINE AND ITS SALTS",
+        "hsn_code": "29333200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAING AN UNFUSED PYRIDINE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE : BROMAZEPAM (INN), METHYLPHENIDATE (INN), PENTAZOCINE (INN),\nPHENCYCLINDINE (INN) (PCP), PIPRADROL (INN), PROPIRAM (INN); SALTS THEREOF",
+        "hsn_code": "29333300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAING AN UNFUSED PYRIDINE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : DERIVATIVES OF PYRIDINE : AMINO PYRIDINE",
+        "hsn_code": "29333911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAING AN UNFUSED PYRIDINE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : DERIVATIVES OF PYRIDINE : ALPHA PICOLINE (2-METHYL PYRIDINE)",
+        "hsn_code": "29333912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAING AN UNFUSED PYRIDINE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : DERIVATIVES OF PYRIDINE : GAMMA PICOLINE (4-METHYL PYRIDINE)",
+        "hsn_code": "29333913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAING AN UNFUSED PYRIDINE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : DERIVATIVES OF PYRIDINE : CHLOROPHENIRAMINE MALEATE",
+        "hsn_code": "29333914",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAING AN UNFUSED PYRIDINE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : - DERIVATIVES OF PYRIDINE :",
+        "hsn_code": "29333915",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAING AN UNFUSED PYRIDINE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : DERIVATIVES OF PYRIDINE : BETA PICOLINE (3-METHYL PYRIDINE)",
+        "hsn_code": "29333916",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAING AN UNFUSED PYRIDINE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : DERIVATIVES OF PYRIDINE : MORPHOLINE",
+        "hsn_code": "29333917",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAING AN UNFUSED PYRIDINE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : DERIVATIVES OF PYRIDINE : LUTIDINE (DIMETHYL PYRIDINE)",
+        "hsn_code": "29333918",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAING AN UNFUSED PYRIDINE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : DERIVATIVES OF PYRIDINE : OTHER",
+        "hsn_code": "29333919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAING AN UNFUSED PYRIDINE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : PIPERIDINE AND ITS DERIVATIVES",
+        "hsn_code": "29333920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "1-AZABICYCLO(2,2,2)OCTAN-3-OL",
+        "hsn_code": "29333930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BENZENE ACETIC ACID,ALPHA -HYDROXY- ALPHA-PHENYL, 1-AZABICYCLO[2.2.2] OCT-3-YL ESTER",
+        "hsn_code": "29333940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAING AN UNFUSED PYRIDINE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : OTHER",
+        "hsn_code": "29333990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING IN THE STRUCTURE A QUINOLINE OR ISOQUINOLINE RING-SYSTEM (WHETHER OR\nNOT HYDROGENATED), NOT FURTHER FUSED :",
+        "hsn_code": "29334100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING IN THE STRUCTURE A QUINOLINE OR ISOQUINOLINE RING-SYSTEM (WHETHER OR\nNOT HYDROGENATED), NOT FURTHER FUSED : OTHER",
+        "hsn_code": "29334900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING A PYRIMIDINE RING (WHETHER OR NOT HYDROGENATED) OR PIPERAZINE RING IN\nTHE STRUCTURE : MALONYLUREA (BARBITURIC ACID) AND ITS SALTS",
+        "hsn_code": "29335200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING A PYRIMIDINE RING (WHETHER OR NOT HYDROGENATED) OR PIPERAZINE RING IN\nTHE STRUCTURE : ALLOBARBITAL (INN), AMOBARBITAL (INN), BARBITAL (INN), BUTALBITAL (INN),\nBUTOBARBITAL (INN), CYCLOBARBITAL (INN), METHYLPHENOBARBITAL (INN), PENTOBARBITAL\n(INN), SECBUTABARBITAL (INN), PHENOBARBITAL (INN), SECOBARBITAL (INN), AND VINYLBITAL\n(INN); SALTS THEREOF",
+        "hsn_code": "29335300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING A PYRIMIDINE RING (WHETHER OR NOT HYDROGENATED) OR PIPERAZINE RING IN\nTHE STRUCTURE : OTHER DERIVATIVES OF MALONYLUREA (BARBITURIC ACID); SALTS THEREOF",
+        "hsn_code": "29335400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING A PYRIMIDINE RING (WHETHER OR NOT HYDROGENATED) OR PIPERAZINE RING IN\nTHE STRUCTURE : LOPRAZOLAM (INN), MECLOQUALONE (INN), METHAQUALONE (INN) AND\nZIPEPROL (INN); SALTS THEREOF",
+        "hsn_code": "29335500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING A PYRIMIDINE RING (WHETHER OR NOT HYDROGENATED) OR PIPERAZINE RING IN\nTHE STRUCTURE :-OTHER : AMINOPHYLLINE (CORDOPHYLIN)",
+        "hsn_code": "29335910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING A PYRIMIDINE RING (WHETHER OR NOT HYDROGENATED) OR PIPERAZINE RING IN\nTHE STRUCTURE :-OTHER : TRIMETHOPRIM",
+        "hsn_code": "29335920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING A PYRIMIDINE RING (WHETHER OR NOT HYDROGENATED) OR PIPERAZINE RING IN\nTHE STRUCTURE :-OTHER : DIETHYL CARBAMAZINE CITRATE",
+        "hsn_code": "29335930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING A PYRIMIDINE RING (WHETHER OR NOT HYDROGENATED) OR PIPERAZINE RING IN\nTHE STRUCTURE :-OTHER : 1-AMINO-4-METHYL PIPERAZINE",
+        "hsn_code": "29335940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING A PYRIMIDINE RING (WHETHER OR NOT HYDROGENATED) OR PIPERAZINE RING IN\nTHE STRUCTURE :-OTHER : OTHER",
+        "hsn_code": "29335990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED TRIAZINE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE : MELAMINE",
+        "hsn_code": "29336100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED TRIAZINE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : CYANURIC ACID AND ITS SALTS",
+        "hsn_code": "29336910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - COMPOUNDS\nCONTAINING AN UNFUSED TRIAZINE RING (WHETHER OR NOT HYDROGENATED) IN THE\nSTRUCTURE :-OTHER : OTHER",
+        "hsn_code": "29336990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - LACTAMS : 6-\nHEXANELACTAM (EPSILON-CAPROLACTAM)",
+        "hsn_code": "29337100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - LACTAMS : CLOBAZAM\n(INN) AND METHYPRYLON (INN)",
+        "hsn_code": "29337200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - LACTAMS : OTHER\nLACTAMS",
+        "hsn_code": "29337900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - OTHER : ALPRAZOLAM\n(INN), CAMAZEPAM (INN) CHLORIDIAZEPOXIDE (INN), CLONAZEPAM (INN), CLORAZEPATE,\nDELORAZEPAM (INN), DIAZEPAM (INN), ESTAZOLAM (INN), ETHYL LOFLAZEPATE (INN),\nFLUDIAZEPAM (INN), FLUNITRZEPAM (INN), FLURAZEPAM (INN), HALAZAEPAM (INN),\nLORAZEPAM (INN), LORMETAZEPAM (INN), MAZINDOL (INN), MEDAZEPAM (INN), MIDAZOLAM\n(INN), NIMETAZEPAM (INN), NITRAZEPAM (INN), NORDAZEPAM (INN), OXAZEPAM (INN),\nPINAZEPAM (INN), PRAZEPAM (INN), PYROVALERONE (INN), TAMAZEPAM (INN), TETRAZEPAM\n(INN) AND TRIAZOLAM (INN); SALTS THEREOF",
+        "hsn_code": "29339100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH NITROGEN HETEROATOM( S) ONLY - OTHER : OTHER",
+        "hsn_code": "29339900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUCLEIC ACIDS AND THEIR SALTS; WHETHER OR NOT CHEMICALLY DEFINED; OTHER\nHETEROCYCLIC COMPOUNDS COMPOUNDS CONTAINING AN UNFUSED THIAZOLE RING\n(WHETHER OR NOT HYDROGENATED) IN THE STRUCTURE",
+        "hsn_code": "29341000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUCLEIC ACIDS AND THEIR SALTS; WHETHER OR NOT CHEMICALLY DEFINED; OTHER\nHETEROCYCLIC COMPOUNDS COMPOUNDS CONTAINING IN THE STRUCTURE A BENZOTHIAZOLE\nRING-SYSTEM (WHETHER OR NOT HYDROGENATED) NOT FURTHER FUSED",
+        "hsn_code": "29342000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUCLEIC ACIDS AND THEIR SALTS; WHETHER OR NOT CHEMICALLY DEFINED; OTHER\nHETEROCYCLIC COMPOUNDS COMPOUNDS CONTAINING IN THE STRUCTURE A PHENOTHIAZINE\nRING-SYSTEM (WHETHER OR NOT HYDROGENATED) NOT FURTHER FUSED",
+        "hsn_code": "29343000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUCLEIC ACIDS AND THEIR SALTS; WHETHER OR NOT CHEMICALLY DEFINED; OTHER\nHETEROCYCLIC COMPOUNDS - OTHER : AMINOREX (INN), BROTIZOLAM (INN), CLOTIAZEPAM\n(INN), CLAXAZOLAM (INN), HALOXAZOLAM (INN), KETAZOLAM (INN), MESOCARB (INN),\nOXAZOLAM (INN), PEMOLINE (INN), PHENDIMETRAZINE (INN) AND PHENMETRAZINE (INN);\nSALTS THEREOF",
+        "hsn_code": "29349100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUCLEIC ACIDS AND THEIR SALTS; WHETHER OR NOT CHEMICALLY DEFINED; OTHER\nHETEROCYCLIC COMPOUNDS - OTHER : OTHER",
+        "hsn_code": "29349900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONAMIDES - SULPHONAMIDES : SULPHAMETHOXAZOLE, SULPHAFURAZOLE,\nSULPHADIAZINE, SULPHADIMIDINE, SULPHACETAMIDE : SULPHAMETHOXAZOLE",
+        "hsn_code": "29350011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONAMIDES - SULPHONAMIDES : SULPHAMETHOXAZOLE, SULPHAFURAZOLE,\nSULPHADIAZINE, SULPHADIMIDINE, SULPHACETAMIDE : SULPHAFURAZOLE",
+        "hsn_code": "29350012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONAMIDES - SULPHONAMIDES : SULPHAMETHOXAZOLE, SULPHAFURAZOLE,\nSULPHADIAZINE, SULPHADIMIDINE, SULPHACETAMIDE : SULPHADIAZINE",
+        "hsn_code": "29350013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONAMIDES - SULPHONAMIDES : SULPHAMETHOXAZOLE, SULPHAFURAZOLE,\nSULPHADIAZINE, SULPHADIMIDINE, SULPHACETAMIDE : SULPHADIMIDINE",
+        "hsn_code": "29350014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONAMIDES - SULPHONAMIDES : SULPHAMETHOXAZOLE, SULPHAFURAZOLE,\nSULPHADIAZINE, SULPHADIMIDINE, SULPHACETAMIDE : SULPHACETAMIDE",
+        "hsn_code": "29350015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONAMIDES - SULPHONAMIDES :-SULPHAMETHOXYPYRIDARINE, SULPHAMETHIAZOLE,\nSULPHAMOXOLE, SULPHAMIDE : SULPHAMETHOXYPYRIDARINE",
+        "hsn_code": "29350021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONAMIDES - SULPHONAMIDES :-SULPHAMETHOXYPYRIDARINE, SULPHAMETHIAZOLE,\nSULPHAMOXOLE, SULPHAMIDE : SULPHAMETHIAZOLE",
+        "hsn_code": "29350022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONAMIDES - SULPHONAMIDES :-SULPHAMETHOXYPYRIDARINE, SULPHAMETHIAZOLE,\nSULPHAMOXOLE, SULPHAMIDE : SULPHAMOXOLE",
+        "hsn_code": "29350023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONAMIDES - SULPHONAMIDES :-SULPHAMETHOXYPYRIDARINE, SULPHAMETHIAZOLE,\nSULPHAMOXOLE, SULPHAMIDE : SULPHAMIDE",
+        "hsn_code": "29350024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONAMIDES - SULPHONAMIDES : OTHER",
+        "hsn_code": "29350090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PROVITAMINS AND VITAMINS, NATURAL OR REPRODUCED BY SYNTHESIS (INCLUDING NATURAL\nCONCENTRATES), DERIVATIVES THEREOF USED PRIMARILY AS VITAMINS, AND INTERMIXTURES\nOF THE FOREGOING, WHETHER OR NOT IN ANY SOLVENT - PROVITAMINS, UNMIXED",
+        "hsn_code": "29361000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PROVITAMINS AND VITAMINS, NATURAL OR REPRODUCED BY SYNTHESIS (INCLUDING NATURAL\nCONCENTRATES), DERIVATIVES THEREOF USED PRIMARILY AS VITAMINS, AND INTERMIXTURES\nOF THE FOREGOING, WHETHER OR NOT IN ANY SOLVENT - VITAMINS AND THEIR DERIVATIVES,\nUNMIXED : VITAMIN A AND THEIR DERIVATIVES",
+        "hsn_code": "29362100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PROVITAMINS AND VITAMINS, NATURAL OR REPRODUCED BY SYNTHESIS (INCLUDING NATURAL\nCONCENTRATES), DERIVATIVES THEREOF USED PRIMARILY AS VITAMINS, AND INTERMIXTURES\nOF THE FOREGOING, WHETHER OR NOT IN ANY SOLVENT - VITAMINS AND THEIR DERIVATIVES,\nUNMIXED :-VITAMIN B1 AND ITS DERIVATIVES : VITAMIN B1 [THIAMINE (INN), ANEURINE] AND\nITS SALTS",
+        "hsn_code": "29362210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PROVITAMINS AND VITAMINS, NATURAL OR REPRODUCED BY SYNTHESIS (INCLUDING NATURAL\nCONCENTRATES), DERIVATIVES THEREOF USED PRIMARILY AS VITAMINS, AND INTERMIXTURES\nOF THE FOREGOING, WHETHER OR NOT IN ANY SOLVENT - VITAMINS AND THEIR DERIVATIVES,\nUNMIXED :-VITAMIN B1 AND ITS DERIVATIVES : OTHER",
+        "hsn_code": "29362290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PROVITAMINS AND VITAMINS, NATURAL OR REPRODUCED BY SYNTHESIS (INCLUDING NATURAL\nCONCENTRATES), DERIVATIVES THEREOF USED PRIMARILY AS VITAMINS, AND INTERMIXTURES\nOF THE FOREGOING, WHETHER OR NOT IN ANY SOLVENT - VITAMINS AND THEIR DERIVATIVES,\nUNMIXED :-VITAMIN B2 AND ITS DERIVATIVES : VITAMIN B2 [RIBOFLAVIN(INN), LACTOFLAVIN]\nAND ITS SALTS",
+        "hsn_code": "29362310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PROVITAMINS AND VITAMINS, NATURAL OR REPRODUCED BY SYNTHESIS (INCLUDING NATURAL\nCONCENTRATES), DERIVATIVES THEREOF USED PRIMARILY AS VITAMINS, AND INTERMIXTURES\nOF THE FOREGOING, WHETHER OR NOT IN ANY SOLVENT - VITAMINS AND THEIR DERIVATIVES,\nUNMIXED :-VITAMIN B2 AND ITS DERIVATIVES : OTHER",
+        "hsn_code": "29362390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PROVITAMINS AND VITAMINS, NATURAL OR REPRODUCED BY SYNTHESIS (INCLUDING NATURAL\nCONCENTRATES), DERIVATIVES THEREOF USED PRIMARILY AS VITAMINS, AND INTERMIXTURES\nOF THE FOREGOING, WHETHER OR NOT IN ANY SOLVENT - VITAMINS AND THEIR DERIVATIVES,\nUNMIXED : D- OR DL-PANTOTHENIC ACID (VITAMIN B3 OR VITAMIN B5) AND ITS DERIVATIVES",
+        "hsn_code": "29362400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PROVITAMINS AND VITAMINS, NATURAL OR REPRODUCED BY SYNTHESIS (INCLUDING NATURAL\nCONCENTRATES), DERIVATIVES THEREOF USED PRIMARILY AS VITAMINS, AND INTERMIXTURES\nOF THE FOREGOING, WHETHER OR NOT IN ANY SOLVENT - VITAMINS AND THEIR DERIVATIVES,\nUNMIXED : VITAMIN B6 AND ITS DERIVATIVES",
+        "hsn_code": "29362500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PROVITAMINS AND VITAMINS, NATURAL OR REPRODUCED BY SYNTHESIS (INCLUDING NATURAL\nCONCENTRATES), DERIVATIVES THEREOF USED PRIMARILY AS VITAMINS, AND INTERMIXTURES\nOF THE FOREGOING, WHETHER OR NOT IN ANY SOLVENT - VITAMINS AND THEIR DERIVATIVES,\nUNMIXED :-VITAMIN B12 AND ITS DERIVATIVES : VITAMIN B12 (CYANOCOBALAMIN (INN))",
+        "hsn_code": "29362610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PROVITAMINS AND VITAMINS, NATURAL OR REPRODUCED BY SYNTHESIS (INCLUDING NATURAL\nCONCENTRATES), DERIVATIVES THEREOF USED PRIMARILY AS VITAMINS, AND INTERMIXTURES\nOF THE FOREGOING, WHETHER OR NOT IN ANY SOLVENT - VITAMINS AND THEIR DERIVATIVES,\nUNMIXED :-VITAMIN B12 AND ITS DERIVATIVES : OTHER",
+        "hsn_code": "29362690",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PROVITAMINS AND VITAMINS, NATURAL OR REPRODUCED BY SYNTHESIS (INCLUDING NATURAL\nCONCENTRATES), DERIVATIVES THEREOF USED PRIMARILY AS VITAMINS, AND INTERMIXTURES\nOF THE FOREGOING, WHETHER OR NOT IN ANY SOLVENT - VITAMINS AND THEIR DERIVATIVES,\nUNMIXED : VITAMIN C (ASCORBIC ACID) AND ITS DERIVATIVES",
+        "hsn_code": "29362700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PROVITAMINS AND VITAMINS, NATURAL OR REPRODUCED BY SYNTHESIS (INCLUDING NATURAL\nCONCENTRATES), DERIVATIVES THEREOF USED PRIMARILY AS VITAMINS, AND INTERMIXTURES\nOF THE FOREGOING, WHETHER OR NOT IN ANY SOLVENT - VITAMINS AND THEIR DERIVATIVES,\nUNMIXED : VITAMIN E AND ITS DERIVATIVES",
+        "hsn_code": "29362800",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PROVITAMINS AND VITAMINS, NATURAL OR REPRODUCED BY SYNTHESIS (INCLUDING NATURAL\nCONCENTRATES), DERIVATIVES THEREOF USED PRIMARILY AS VITAMINS, AND INTERMIXTURES\nOF THE FOREGOING, WHETHER OR NOT IN ANY SOLVENT - VITAMINS AND THEIR DERIVATIVES,\nUNMIXED : OTHER VITAMINS AND THEIR DERIVATIVES : FOLIC ACID (VITAMIN B9)",
+        "hsn_code": "29362910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PROVITAMINS AND VITAMINS, NATURAL OR REPRODUCED BY SYNTHESIS (INCLUDING NATURAL\nCONCENTRATES), DERIVATIVES THEREOF USED PRIMARILY AS VITAMINS, AND INTERMIXTURES\nOF THE FOREGOING, WHETHER OR NOT IN ANY SOLVENT - VITAMINS AND THEIR DERIVATIVES,\nUNMIXED : OTHER VITAMINS AND THEIR DERIVATIVES : NICOTINIC ACID AND NICOTINAMIDE\n(NIACINAMIDE OR NIACINE)",
+        "hsn_code": "29362920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PROVITAMINS AND VITAMINS, NATURAL OR REPRODUCED BY SYNTHESIS (INCLUDING NATURAL\nCONCENTRATES), DERIVATIVES THEREOF USED PRIMARILY AS VITAMINS, AND INTERMIXTURES\nOF THE FOREGOING, WHETHER OR NOT IN ANY SOLVENT - VITAMINS AND THEIR DERIVATIVES,\nUNMIXED : OTHER VITAMINS AND THEIR DERIVATIVES : VITAMIN K (MENAPHTHONUM BP)",
+        "hsn_code": "29362930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PROVITAMINS AND VITAMINS, NATURAL OR REPRODUCED BY SYNTHESIS (INCLUDING NATURAL\nCONCENTRATES), DERIVATIVES THEREOF USED PRIMARILY AS VITAMINS, AND INTERMIXTURES\nOF THE FOREGOING, WHETHER OR NOT IN ANY SOLVENT - VITAMINS AND THEIR DERIVATIVES,\nUNMIXED : OTHER VITAMINS AND THEIR DERIVATIVES : VITAMIN D",
+        "hsn_code": "29362940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PROVITAMINS AND VITAMINS, NATURAL OR REPRODUCED BY SYNTHESIS (INCLUDING NATURAL\nCONCENTRATES), DERIVATIVES THEREOF USED PRIMARILY AS VITAMINS, AND INTERMIXTURES\nOF THE FOREGOING, WHETHER OR NOT IN ANY SOLVENT - VITAMINS AND THEIR DERIVATIVES,\nUNMIXED : OTHER VITAMINS AND THEIR DERIVATIVES : VITAMIN H (BIOTIN)",
+        "hsn_code": "29362950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PROVITAMINS AND VITAMINS, NATURAL OR REPRODUCED BY SYNTHESIS (INCLUDING NATURAL\nCONCENTRATES), DERIVATIVES THEREOF USED PRIMARILY AS VITAMINS, AND INTERMIXTURES\nOF THE FOREGOING, WHETHER OR NOT IN ANY SOLVENT - VITAMINS AND THEIR DERIVATIVES,\nUNMIXED : OTHER VITAMINS AND THEIR DERIVATIVES : OTHER",
+        "hsn_code": "29362990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PROVITAMINS AND VITAMINS, NATURAL OR REPRODUCED BY SYNTHESIS (INCLUDING NATURAL\nCONCENTRATES), DERIVATIVES THEREOF USED PRIMARILY AS VITAMINS, AND INTERMIXTURES\nOF THE FOREGOING, WHETHER OR NOT IN ANY SOLVENT -OTHER, INCLUDING NATURAL\nCONCENTRATES",
+        "hsn_code": "29369000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HORMONES, PROSTAGLANDINS, THROMBOXANES AND LEUKOTRIENES, NATURAL OR\nREPRODUCED BY SYNTHESIS; DERIVATIVES AND STRUCTURAL ANALOGUES THEREOF, INCLUDING\nCHAIN MODIFIED POLYPEPTIDES, USED PRIMARILY AS HORMONES - POLYPEPTIDE HORMONES,\nPROTEIN HORMONES AND GLYCOPROTEIN HORMONES, THEIR DERVATIVES AND STRUCTURAL\nANALOGUES : SOMATOTROPIN, ITS DERIVATIVES AND STRUCTURAL ANALOGUES",
+        "hsn_code": "29371100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HORMONES, PROSTAGLANDINS, THROMBOXANES AND LEUKOTRIENES, NATURAL OR\nREPRODUCED BY SYNTHESIS; DERIVATIVES AND STRUCTURAL ANALOGUES THEREOF, INCLUDING\nCHAIN MODIFIED POLYPEPTIDES, USED PRIMARILY AS HORMONES - POLYPEPTIDE HORMONES,\nPROTEIN HORMONES AND GLYCOPROTEIN HORMONES, THEIR DERVATIVES AND STRUCTURAL\nANALOGUES : INSULIN AND ITS SALTS",
+        "hsn_code": "29371200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HORMONES, PROSTAGLANDINS, THROMBOXANES AND LEUKOTRIENES, NATURAL OR\nREPRODUCED BY SYNTHESIS; DERIVATIVES AND STRUCTURAL ANALOGUES THEREOF, INCLUDING\nCHAIN MODIFIED POLYPEPTIDES, USED PRIMARILY AS HORMONES - POLYPEPTIDE HORMONES,\nPROTEIN HORMONES AND GLYCOPROTEIN HORMONES, THEIR DERVATIVES AND STRUCTURAL\nANALOGUES : OTHER",
+        "hsn_code": "29371900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HORMONES, PROSTAGLANDINS, THROMBOXANES AND LEUKOTRIENES, NATURAL OR\nREPRODUCED BY SYNTHESIS; DERIVATIVES AND STRUCTURAL ANALOGUES THEREOF, INCLUDING\nCHAIN MODIFIED POLYPEPTIDES, USED PRIMARILY AS HORMONES - STEROIDAL HORMONES,\nTHEIR DERIVATIVES AND STRUCTURAL ANALOGUES : CORTISONE, HYDROCORTISONE,\nPREDNISONE, (DEHYDROCORTISONE) AND PREDNISOLONE (DEHYDROHYDROCORTISONE)",
+        "hsn_code": "29372100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HORMONES, PROSTAGLANDINS, THROMBOXANES AND LEUKOTRIENES, NATURAL OR\nREPRODUCED BY SYNTHESIS; DERIVATIVES AND STRUCTURAL ANALOGUES THEREOF, INCLUDING\nCHAIN MODIFIED POLYPEPTIDES, USED PRIMARILY AS HORMONES - STEROIDAL HORMONES,\nTHEIR DERIVATIVES AND STRUCTURAL ANALOGUES : HALOGENATED DERIVATIVES OF\nCORTICOSTEROIDAL HARMONES",
+        "hsn_code": "29372200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HORMONES, PROSTAGLANDINS, THROMBOXANES AND LEUKOTRIENES, NATURAL OR\nREPRODUCED BY SYNTHESIS; DERIVATIVES AND STRUCTURAL ANALOGUES THEREOF, INCLUDING\nCHAIN MODIFIED POLYPEPTIDES, USED PRIMARILY AS HORMONES - STEROIDAL HORMONES,\nTHEIR DERIVATIVES AND STRUCTURAL ANALOGUES : OESTROGENS AND PROGESTOGENS",
+        "hsn_code": "29372300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HORMONES, PROSTAGLANDINS, THROMBOXANES AND LEUKOTRIENES, NATURAL OR\nREPRODUCED BY SYNTHESIS; DERIVATIVES AND STRUCTURAL ANALOGUES THEREOF, INCLUDING\nCHAIN MODIFIED POLYPEPTIDES, USED PRIMARILY AS HORMONES - STEROIDAL HORMONES,\nTHEIR DERIVATIVES AND STRUCTURAL ANALOGUES : OTHER",
+        "hsn_code": "29372900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HORMONES, PROSTAGLANDINS, THROMBOXANES AND LEUKOTRIENES, NATURAL OR\nREPRODUCED BY SYNTHESIS; DERIVATIVES AND STRUCTURAL ANALOGUES THEREOF, INCLUDING\nCHAIN MODIFIED POLYPEPTIDES, USED PRIMARILY AS HORMONES - PROSTAGLANDINS,\nTHROMBOXANES AND LEUKOTRIENES, THEIR DERIVATIVES AND STRUCTURAL ANALOGUES",
+        "hsn_code": "29375000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HORMONES, PROSTAGLANDINS, THROMBOXANES AND LEUKOTRIENES, NATURAL OR\nREPRODUCED BY SYNTHESIS; DERIVATIVES AND STRUCTURAL ANALOGUES THEREOF, INCLUDING\nCHAIN MODIFIED POLYPEPTIDES, USED PRIMARILY AS HORMONES -OTHER---CATECHOLAMINE\nHORMONES, THEIR DERIVATIVES AND STRUCTURAL ANALOGUES----EPINETHRINE",
+        "hsn_code": "29379011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HORMONES, PROSTAGLANDINS, THROMBOXANES AND LEUKOTRIENES, NATURAL OR\nREPRODUCED BY SYNTHESIS; DERIVATIVES AND STRUCTURAL ANALOGUES THEREOF, INCLUDING\nCHAIN MODIFIED POLYPEPTIDES, USED PRIMARILY AS HORMONES -OTHER---CATECHOLAMINE\nHORMONES, THEIR DERIVATIVES AND STRUCTURAL ANALOGUES----OTHER",
+        "hsn_code": "29379019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HORMONES, PROSTAGLANDINS, THROMBOXANES AND LEUKOTRIENES, NATURAL OR\nREPRODUCED BY SYNTHESIS; DERIVATIVES AND STRUCTURAL ANALOGUES THEREOF, INCLUDING\nCHAIN MODIFIED POLYPEPTIDES, USED PRIMARILY AS HORMONES -OTHER---CATECHOLAMINE\nHORMONES, THEIR DERIVATIVES AND STRUCTURAL ANALOGUES----AMINO-ACID DERIVATIVES",
+        "hsn_code": "29379020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HORMONES, PROSTAGLANDINS, THROMBOXANES AND LEUKOTRIENES, NATURAL OR\nREPRODUCED BY SYNTHESIS; DERIVATIVES AND STRUCTURAL ANALOGUES THEREOF, INCLUDING\nCHAIN MODIFIED POLYPEPTIDES, USED PRIMARILY AS HORMONES -OTHER---CATECHOLAMINE\nHORMONES, THEIR DERIVATIVES AND STRUCTURAL ANALOGUES----OTHER",
+        "hsn_code": "29379090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GLYCOSIDES, NATURAL OR REPRODUCED BY SYNTHESIS AND THEIR SALTS, ETHERS, ESTERS AND\nOTHER DERIVATIVES -RUTOSIDE (RUTIN) AND ITS DERIVATIVES",
+        "hsn_code": "29381000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GLYCOSIDES, NATURAL OR REPRODUCED BY SYNTHESIS AND THEIR SALTS, ETHERS, ESTERS AND\nOTHER DERIVATIVES - OTHER : DIGOXIN",
+        "hsn_code": "29389010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GLYCOSIDES, NATURAL OR REPRODUCED BY SYNTHESIS AND THEIR SALTS, ETHERS, ESTERS AND\nOTHER DERIVATIVES - OTHER : DIGITALIS GLYCOSIDES",
+        "hsn_code": "29389020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GLYCOSIDES, NATURAL OR REPRODUCED BY SYNTHESIS AND THEIR SALTS, ETHERS, ESTERS AND\nOTHER DERIVATIVES - OTHER : OTHER",
+        "hsn_code": "29389090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - ALKALOIDS OF OPIUM AND THEIR DERIVATIVES; SALTS\nTHEREOF : BUPRENORPHINE (INN), AND SALTS THEREOF",
+        "hsn_code": "29391100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - ALKALOIDS OF OPIUM AND THEIR DERIVATIVES; SALTS\nTHEREOF : -- OTHER",
+        "hsn_code": "29391900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - ALKALOIDS OF CINCHONA AND THEIR DERIVATIVES; SALTS\nTHEREOF : --- QUININE ALKALOIDS",
+        "hsn_code": "29392010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - ALKALOIDS OF CINCHONA AND THEIR DERIVATIVES; SALTS\nTHEREOF : --- QUININE HYDROCHLORIDE",
+        "hsn_code": "29392020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - ALKALOIDS OF CINCHONA AND THEIR DERIVATIVES; SALTS\nTHEREOF : --- QUININE SULPHATE",
+        "hsn_code": "29392030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - ALKALOIDS OF CINCHONA AND THEIR DERIVATIVES; SALTS\nTHEREOF : --- CHLOROQUINE PHOSPHATE",
+        "hsn_code": "29392040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - ALKALOIDS OF CINCHONA AND THEIR DERIVATIVES; SALTS\nTHEREOF : --- OTHER",
+        "hsn_code": "29392090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - ALKALOIDS OF CINCHONA AND THEIR DERIVATIVES; SALTS\nTHEREOF :-QUININE AND ITS SALTS : QUININE ALKALOIDS",
+        "hsn_code": "29392110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - ALKALOIDS OF CINCHONA AND THEIR DERIVATIVES; SALTS\nTHEREOF :-QUININE AND ITS SALTS : QUININE HYDROCHLORIDE",
+        "hsn_code": "29392120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - ALKALOIDS OF CINCHONA AND THEIR DERIVATIVES; SALTS\nTHEREOF :-QUININE AND ITS SALTS : QUININE SULPHATE",
+        "hsn_code": "29392130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - ALKALOIDS OF CINCHONA AND THEIR DERIVATIVES; SALTS\nTHEREOF :-QUININE AND ITS SALTS : CHLOROQUINE PHOSPHATE",
+        "hsn_code": "29392140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - ALKALOIDS OF CINCHONA AND THEIR DERIVATIVES; SALTS\nTHEREOF :-QUININE AND ITS SALTS : OTHER",
+        "hsn_code": "29392190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - ALKALOIDS OF CINCHONA AND THEIR DERIVATIVES; SALTS\nTHEREOF : OTHER",
+        "hsn_code": "29392900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BENZENEACETIC ACID, ALPHA-HYDROXY-ALPHA-PHENYL, 1-AZABICYCLO",
+        "hsn_code": "29392910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER",
+        "hsn_code": "29392990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - CAFFEINE AND ITS SALTS",
+        "hsn_code": "29393000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - EPHEDRINES AND THEIR SALTS :-EPHEDRINE AND ITS SALTS :\nEPHEDRINE ALKALOIDS",
+        "hsn_code": "29394110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - EPHEDRINES AND THEIR SALTS :-EPHEDRINE AND ITS SALTS :\nEPHEDRINE HYDROCHLORIDE",
+        "hsn_code": "29394120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - EPHEDRINES AND THEIR SALTS :-EPHEDRINE AND ITS SALTS :\nOTHER",
+        "hsn_code": "29394190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - EPHEDRINES AND THEIR SALTS : PSEUDOEPHEDRINE (INN)\nAND ITS SALTS",
+        "hsn_code": "29394200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - EPHEDRINES AND THEIR SALTS : CATHINE (INN) AND ITS\nSALTS",
+        "hsn_code": "29394300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - EPHEDRINES AND THEIR SALTS --NOREPHEDRINE AND ITS\nSALTS",
+        "hsn_code": "29394400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - EPHEDRINES AND THEIR SALTS : OTHER",
+        "hsn_code": "29394900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - THEOPHYLLINE AND\nAMINOPHYLLINE(THEOPHYLLINEETHYLENEDIAMINE) AND THEIR DERIVATIVES; SALTS THEREOF :\nFENETYLLINE (INN) AND ITS SALTS",
+        "hsn_code": "29395100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - THEOPHYLLINE AND\nAMINOPHYLLINE(THEOPHYLLINEETHYLENEDIAMINE) AND THEIR DERIVATIVES; SALTS THEREOF :\nOTHER",
+        "hsn_code": "29395900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - ALKALOIDS OF RYE ERGOT AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ERGOMETRINE (INN) AND ITS SALTS : ERGOMETRINE (INN)",
+        "hsn_code": "29396110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - ALKALOIDS OF RYE ERGOT AND THEIR DERIVATIVES; SALTS\nTHEREOF :-ERGOMETRINE (INN) AND ITS SALTS : OTHER",
+        "hsn_code": "29396190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - ALKALOIDS OF RYE ERGOT AND THEIR DERIVATIVES; SALTS\nTHEREOF : ERGOTAMINE (INN) AND ITS SALTS : ERGOTAMINE TARTRATE",
+        "hsn_code": "29396210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - ALKALOIDS OF RYE ERGOT AND THEIR DERIVATIVES; SALTS\nTHEREOF : ERGOTAMINE (INN) AND ITS SALTS : OTHER",
+        "hsn_code": "29396290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - ALKALOIDS OF RYE ERGOT AND THEIR DERIVATIVES; SALTS\nTHEREOF : LYSERGIC ACID AND ITS SALTS",
+        "hsn_code": "29396300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - ALKALOIDS OF RYE ERGOT AND THEIR DERIVATIVES; SALTS\nTHEREOF : OTHER",
+        "hsn_code": "29396900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - OTHER : LEVOMETAMFETAMINE, METAMFETAMINE(INN),\nMETAMFETAMINE RACEMATE; SALTS, ESTERS AND OTHER DERIVATIVES THEREOF",
+        "hsn_code": "29399100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS,\nESTERS AND OTHER DERIVATIVES - OTHER : OTHER",
+        "hsn_code": "29399900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SUGARS, CHEMICALLY PURE, OTHER THAN SUCROSE, LACTOSE, MALTOSE, GLUCOSE AND\nFRUCTOSE; SUGAR ETHERS, SUGAR ACETALS AND SUGAR ESTERS, AND THEIR SALTS, OTHER\nTHAN PRODUCTS OF HEADINGS 2937, 2938 OR 2939",
+        "hsn_code": "29400000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - PENICILLINS AND THEIR DERIVATIVE WITH A PENICILLANIC ACID STRUCTURE;\nSALTS THEREOF : PENICILLINS AND ITS SALTS ( E.G. PROCAINE PENICILLIN, PENICILLIN G\nPOTASSIUM)",
+        "hsn_code": "29411010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - PENICILLINS AND THEIR DERIVATIVE WITH A PENICILLANIC ACID STRUCTURE;\nSALTS THEREOF : AMPICILLINE AND ITS SALTS",
+        "hsn_code": "29411020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - PENICILLINS AND THEIR DERIVATIVE WITH A PENICILLANIC ACID STRUCTURE;\nSALTS THEREOF : AMOXYCILLINE AND ITS SALTS",
+        "hsn_code": "29411030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - PENICILLINS AND THEIR DERIVATIVE WITH A PENICILLANIC ACID STRUCTURE;\nSALTS THEREOF : CIOXACILLINE AND ITS SALTS",
+        "hsn_code": "29411040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - PENICILLINS AND THEIR DERIVATIVE WITH A PENICILLANIC ACID STRUCTURE;\nSALTS THEREOF : 6-APA",
+        "hsn_code": "29411050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - PENICILLINS AND THEIR DERIVATIVE WITH A PENICILLANIC ACID STRUCTURE;\nSALTS THEREOF : OTHER",
+        "hsn_code": "29411090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - STREPTOMYCINS AND THEIR DERIVATIVES; SALTS THEREOF : STREPTOMYCIN",
+        "hsn_code": "29412010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - STREPTOMYCINS AND THEIR DERIVATIVES; SALTS THEREOF : OTHER",
+        "hsn_code": "29412090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - TETRACYCLINES AND THEIR DERIVATIVES, SALTS THEREOF : DOXYCYCLINE AND ITS\nSALTS",
+        "hsn_code": "29413010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - TETRACYCLINES AND THEIR DERIVATIVES, SALTS THEREOF :\nTETRACYCLINE/OXYTETRA-CYCLINE AND THEIR SALTS",
+        "hsn_code": "29413020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - TETRACYCLINES AND THEIR DERIVATIVES, SALTS THEREOF : OTHER",
+        "hsn_code": "29413090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - CHLORAMPHENICOL AND ITS DERIVATIVES; SALTS THEREOF",
+        "hsn_code": "29414000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - ERYTHROMYCIN AND ITS DERIVATIVES; SALTS THEREOF",
+        "hsn_code": "29415000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - OTHER : RIFAMPICIN AND ITS SALTS : RIFAMPICIN",
+        "hsn_code": "29419011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - OTHER : RIFAMPICIN AND ITS SALTS : 3 FORMYL RIFA S V(RIFA INT)",
+        "hsn_code": "29419012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - OTHER : RIFAMPICIN AND ITS SALTS : RIFA S OR RIFA S SODIUM (RIFAINT)",
+        "hsn_code": "29419013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - OTHER : RIFAMPICIN AND ITS SALTS : 1-AMINO-4-METHYL PIPERAZINE (RIFAINT)",
+        "hsn_code": "29419014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - OTHER : RIFAMPICIN AND ITS SALTS : OTHER",
+        "hsn_code": "29419019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - OTHER : CEPHALEXIN AND ITS SALTS",
+        "hsn_code": "29419020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - OTHER : CIPROFLOXACINE AND ITS SALTS",
+        "hsn_code": "29419030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - OTHER : GENTAMYCIN AND ITS SALTS",
+        "hsn_code": "29419040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - OTHER : NEOMYCIN",
+        "hsn_code": "29419050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - OTHER : NORFLOXACIN AND ITS SALTS",
+        "hsn_code": "29419060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS - OTHER : OTHER",
+        "hsn_code": "29419090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANIC COMPOUNDS - OTHER ORGANIC COMPOUNDS : CEFADROXIL AND ITS SALTS,\nIBUPROFANE, NIFEDIPINE, RANITIDINE, DANES SALT OF D(-) PHENYL GLYCINE, D(-) PARA\nHYDROXY DANES SALTS : CEFADROXIL AND ITS SALTS",
+        "hsn_code": "29420011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANIC COMPOUNDS - OTHER ORGANIC COMPOUNDS : CEFADROXIL AND ITS SALTS,\nIBUPROFANE, NIFEDIPINE, RANITIDINE, DANES SALT OF D(-) PHENYL GLYCINE, D(-) PARA\nHYDROXY DANES SALTS : IBUPROFANE",
+        "hsn_code": "29420012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANIC COMPOUNDS - OTHER ORGANIC COMPOUNDS : CEFADROXIL AND ITS SALTS,\nIBUPROFANE, NIFEDIPINE, RANITIDINE, DANES SALT OF D(-) PHENYL GLYCINE, D(-) PARA\nHYDROXY DANES SALTS : NIFEDIPINE",
+        "hsn_code": "29420013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANIC COMPOUNDS - OTHER ORGANIC COMPOUNDS : CEFADROXIL AND ITS SALTS,\nIBUPROFANE, NIFEDIPINE, RANITIDINE, DANES SALT OF D(-) PHENYL GLYCINE, D(-) PARA\nHYDROXY DANES SALTS : RANITIDINE",
+        "hsn_code": "29420014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANIC COMPOUNDS - OTHER ORGANIC COMPOUNDS : CEFADROXIL AND ITS SALTS,\nIBUPROFANE, NIFEDIPINE, RANITIDINE, DANES SALT OF D(-) PHENYL GLYCINE, D(-) PARA\nHYDROXY DANES SALTS : DANES SALT OF D(-) PHENYL GLYCINE",
+        "hsn_code": "29420015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANIC COMPOUNDS - OTHER ORGANIC COMPOUNDS : CEFADROXIL AND ITS SALTS,\nIBUPROFANE, NIFEDIPINE, RANITIDINE, DANES SALT OF D(-) PHENYL GLYCINE, D(-) PARA\nHYDROXY DANES SALTS : D(-) PARA HYDROXY DANES SALTS",
+        "hsn_code": "29420016",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANIC COMPOUNDS - OTHER ORGANIC COMPOUNDS : TIMOLO MALEATE,\nTERBUTOLINE SULPHATE, D(-)PHENYL GLYCIN CHLORIDE HCL (DPGCH), IMIPRAMINE HCL,\nAMITRYPTYLINE HCL, CYSTEANUNE HCL, ATENOLOL, PROPRONALOL : TIMOLO MALEATE",
+        "hsn_code": "29420021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANIC COMPOUNDS - OTHER ORGANIC COMPOUNDS : TIMOLO MALEATE,\nTERBUTOLINE SULPHATE, D(-)PHENYL GLYCIN CHLORIDE HCL (DPGCH), IMIPRAMINE HCL,\nAMITRYPTYLINE HCL, CYSTEANUNE HCL, ATENOLOL, PROPRONALOL : TERBUTOLINE SULPHATE",
+        "hsn_code": "29420022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANIC COMPOUNDS - OTHER ORGANIC COMPOUNDS : TIMOLO MALEATE,\nTERBUTOLINE SULPHATE, D(-)PHENYL GLYCIN CHLORIDE HCL (DPGCH), IMIPRAMINE HCL,\nAMITRYPTYLINE HCL, CYSTEANUNE HCL, ATENOLOL, PROPRONALOL : D(-) PHENYL GLYCIN\nCHLORIDE HCL (DPGCH)",
+        "hsn_code": "29420023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANIC COMPOUNDS - OTHER ORGANIC COMPOUNDS : TIMOLO MALEATE,\nTERBUTOLINE SULPHATE, D(-)PHENYL GLYCIN CHLORIDE HCL (DPGCH), IMIPRAMINE HCL,\nAMITRYPTYLINE HCL, CYSTEANUNE HCL, ATENOLOL, PROPRONALOL : IMIPRAMINE HCL",
+        "hsn_code": "29420024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANIC COMPOUNDS - OTHER ORGANIC COMPOUNDS : TIMOLO MALEATE,\nTERBUTOLINE SULPHATE, D(-)PHENYL GLYCIN CHLORIDE HCL (DPGCH), IMIPRAMINE HCL,\nAMITRYPTYLINE HCL, CYSTEANUNE HCL, ATENOLOL, PROPRONALOL : AMITRYPTYLINE HCL",
+        "hsn_code": "29420025",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANIC COMPOUNDS - OTHER ORGANIC COMPOUNDS : TIMOLO MALEATE,\nTERBUTOLINE SULPHATE, D(-)PHENYL GLYCIN CHLORIDE HCL (DPGCH), IMIPRAMINE HCL,\nAMITRYPTYLINE HCL, CYSTEANUNE HCL, ATENOLOL, PROPRONALOL : CYSTEANUNE HCL",
+        "hsn_code": "29420026",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANIC COMPOUNDS - OTHER ORGANIC COMPOUNDS : TIMOLO MALEATE,\nTERBUTOLINE SULPHATE, D(-)PHENYL GLYCIN CHLORIDE HCL (DPGCH), IMIPRAMINE HCL,\nAMITRYPTYLINE HCL, CYSTEANUNE HCL, ATENOLOL, PROPRONALOL : ATENOLOL, PROPRONALOL",
+        "hsn_code": "29420027",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANIC COMPOUNDS - OTHER ORGANIC COMPOUNDS : DILOXANIDE FUROATE,\nCIMETIDINE, OXYCLOZANIDE, FAMOTIDINE : DILOXANIDE FUROATE",
+        "hsn_code": "29420031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANIC COMPOUNDS - OTHER ORGANIC COMPOUNDS : DILOXANIDE FUROATE,\nCIMETIDINE, OXYCLOZANIDE, FAMOTIDINE : CIMETIDINE",
+        "hsn_code": "29420032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANIC COMPOUNDS - OTHER ORGANIC COMPOUNDS : DILOXANIDE FUROATE,\nCIMETIDINE, OXYCLOZANIDE, FAMOTIDINE : OXYCLOZANIDE",
+        "hsn_code": "29420033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANIC COMPOUNDS - OTHER ORGANIC COMPOUNDS : DILOXANIDE FUROATE,\nCIMETIDINE, OXYCLOZANIDE, FAMOTIDINE : FAMOTIDINE",
+        "hsn_code": "29420034",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANIC COMPOUNDS - OTHER ORGANIC COMPOUNDS : OTHER",
+        "hsn_code": "29420090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC HYDROCARBONS",
+        "hsn_code": "2901",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC HYDROCARBONS",
+        "hsn_code": "2902",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED DERIVATIVES OF HYDROCARBONS",
+        "hsn_code": "2903",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF HYDROCARBONS, WHETHER OR NOT HALOGENATED",
+        "hsn_code": "2904",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES",
+        "hsn_code": "2905",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYCLIC ALCOHOLS AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES",
+        "hsn_code": "2906",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHENOLS; PHENOL-ALCOHOLS",
+        "hsn_code": "2907",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PHENOLS OR PHENOL-ALCOHOLS",
+        "hsn_code": "2908",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ETHERS, ETHER-ALCOHOLS, ETHER-PHENOLS, ETHER-ALCOHOL-PHENOLS, ALCOHOL PEROXIDES, ETHER PEROXIDES, KETONE PEROXIDES (WHETHER OR NOT CHEMICALLY DEFINED), AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES",
+        "hsn_code": "2909",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "EPOXIDES, EPOXYALCOHOLS, EPOXYPHENOLS AND EXPOXYETHERS, WITH A THREE-MEMBERED RING, AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES",
+        "hsn_code": "2910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ACETALS AND HEMIACETALS, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES",
+        "hsn_code": "2911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Adelaide’s, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION; CYCLIC POLYMERS OFALDEHYDES; PARAFORMALDEHYDE",
+        "hsn_code": "2912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES OF PRODUCTS OF HEADING 2912",
+        "hsn_code": "2913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "KETONES AND QUINONES, WHETHER OR NOT WITH OTHER OXYGEN FUNCTION, AND THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES",
+        "hsn_code": "2914",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SATURATED ACYCLIC MONOCARBOXYLIC ACIDS AND THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES",
+        "hsn_code": "2915",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "UNSATURATED ACYCLIC MONOCARBOXYLIC ACIDS, CYCLIC MONOCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES",
+        "hsn_code": "2916",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "POLYCARBOXYLIC ACIDS, THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES",
+        "hsn_code": "2917",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYLIC ACIDS WITH ADDITIONAL OXYGEN FUNCTION AND THEIR ANHYDRIDES, HALIDES, PEROXIDES AND PEROXYACIDS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES",
+        "hsn_code": "2918",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHORIC ESTERS AND THEIR SALTS, INCLUDING LACTO-PHOSPHATES; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES",
+        "hsn_code": "2919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ESTERS OF OTHER INORGANIC ACIDS OF NON-METALS (EXCLUDING ESTERS OF HYDROGEN HALIDES) AND THEIR SALTS; THEIR HALOGENATED, SULPHONATED, NITRATED OR NITROSATED DERIVATIVES",
+        "hsn_code": "2920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMINE FUNCTION COMPOUNDS",
+        "hsn_code": "2921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXYGEN-FUNCTION AMINO-COMPOUNDS",
+        "hsn_code": "2922",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "QUATERNARY AMMONIUM SALTS AND HYDROXIDE; LECITHINS AND OTHER PHOSPHOAMINOLIPIDS, WHETHER OR NOT CHEMICALLY DEFINED",
+        "hsn_code": "2923",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBOXYAMIDE-FUNCTION COMPOUNDS; AMIDE-FUNCTION COMPOUNDS OF CARBONIC ACID",
+        "hsn_code": "2924",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Carboxyimide-function compounds (including saccharin and its salts) and imine-function compounds",
+        "hsn_code": "2925",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NITRILE-FUNCTION COMPOUNDS",
+        "hsn_code": "2936",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DIAZO-, AZO- OR AZOXY- COMPOUNDS",
+        "hsn_code": "2927",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORGANIC DERIVATIVES OF HYDRAZINE OR OF HYDROXYLAMINE",
+        "hsn_code": "2928",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDS WITH OTHER NITROGEN FUNCTION",
+        "hsn_code": "2929",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORGANO-SULPHUR COMPOUNDS",
+        "hsn_code": "2930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANO-INORGANIC COMPOUNDS",
+        "hsn_code": "2931",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HETEROCYCLIC COMPOUNDS WITH OXYGEN HETERO-ATOM (S) ONLY",
+        "hsn_code": "2932",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Heterocyclic compounds with nitrogen hetero-atom(s) only",
+        "hsn_code": "2933",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUCLEIC ACIDS AND THEIR SALTS;WHETHER OR NOT CHEMICALLY DEFINED; OTHER HETEROCYCLIC COMPOUNDS",
+        "hsn_code": "2934",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHONAMIDES",
+        "hsn_code": "2935",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HORMONES, PROSTAGLANDINS, THROMBOXANES AND LEUKOTRIENES, NATURAL OR REPRODUCED BY SYNTHESIS; DERIVATIVES AND STRUCTURAL ANALOGUES THEREOF, INCLUDING CHAIN MODIFIED POLYPEPTIDES, USED PRIMARILY AS HORMONES",
+        "hsn_code": "2937",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "GLYCOSIDES, NATURAL OR REPRODUCED BY SYNTHESIS AND THEIR SALTS, ETHERS, ESTERS AND OTHER DERIVATIVES",
+        "hsn_code": "2938",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS, ESTERS AND OTHER DERIVATIVES",
+        "hsn_code": "2939",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE ALKALOIDS, NATURAL OR REPRODUCED BY SYNTHESIS, AND THEIR SALTS, ETHERS, ESTERS AND OTHER DERIVATIVES",
+        "hsn_code": "2940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ANTIBIOTICS",
+        "hsn_code": "2941",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER ORGANIC COMPOUNDS",
+        "hsn_code": "2942",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RESIDUAL PRODUCTS OF THE CHEMICAL OR ALLIED INDUSTRIES, NOT ELSEWHERE SPECIFIED OR INCLUDED; MUNICIPAL WASTE; SEWAGE SLUDGE; OTHER WASTES SPECIFIED IN NOTE 6 TO THIS CHAPTER",
+        "hsn_code": "3825",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Tall oil, whether or not refined",
+        "hsn_code": "3803",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RESIDUAL PRODUCTS OF THE CHEMICAL OR ALLIED INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED; MUNICIPAL WASTE; SEWAGE SLUDGE; OTHER WASTES SPECIFIED IN NOTE 6 TO THIS\nCHAPTER OTHER",
+        "hsn_code": "38259000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RESIDUAL PRODUCTS OF THE CHEMICAL OR ALLIED INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED; MUNICIPAL WASTE; SEWAGE SLUDGE; OTHER WASTES SPECIFIED IN NOTE 6 TO THIS\nCHAPTER - OTHER WASTES FROM CHEMICAL OR ALLIED INDUSTRIES:OTHER",
+        "hsn_code": "38256900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RESIDUAL PRODUCTS OF THE CHEMICAL OR ALLIED INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED; MUNICIPAL WASTE; SEWAGE SLUDGE; OTHER WASTES SPECIFIED IN NOTE 6 TO THIS\nCHAPTER - OTHER WASTES FROM CHEMICAL OR ALLIED INDUSTRIES:MAINLY CONTAINING\nORGANIC CONSTITUENTS",
+        "hsn_code": "38256100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RESIDUAL PRODUCTS OF THE CHEMICAL OR ALLIED INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED; MUNICIPAL WASTE; SEWAGE SLUDGE; OTHER WASTES SPECIFIED IN NOTE 6 TO THIS\nCHAPTER WASTES OF METAL PICKLING LIQUORS, HYDRAULIC FLUIDS, BRAKE FLUIDS AND ANTI\nFREEZE FLUIDS",
+        "hsn_code": "38255000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RESIDUAL PRODUCTS OF THE CHEMICAL OR ALLIED INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED; MUNICIPAL WASTE; SEWAGE SLUDGE; OTHER WASTES SPECIFIED IN NOTE 6 TO THIS\nCHAPTER - WASTE ORGANIC SOLVENTS: OTHER",
+        "hsn_code": "38254900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RESIDUAL PRODUCTS OF THE CHEMICAL OR ALLIED INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED; MUNICIPAL WASTE; SEWAGE SLUDGE; OTHER WASTES SPECIFIED IN NOTE 6 TO THIS\nCHAPTER - WASTE ORGANIC SOLVENTS:HALOGENATED",
+        "hsn_code": "38254100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RESIDUAL PRODUCTS OF THE CHEMICAL OR ALLIED INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED; MUNICIPAL WASTE; SEWAGE SLUDGE; OTHER WASTES SPECIFIED IN NOTE 6 TO THIS\nCHAPTER - CLINICAL WASTE",
+        "hsn_code": "38253000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RESIDUAL PRODUCTS OF THE CHEMICAL OR ALLIED INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED; MUNICIPAL WASTE; SEWAGE SLUDGE; OTHER WASTES SPECIFIED IN NOTE 6 TO THIS\nCHAPTER - SEWAGE SLUDGE",
+        "hsn_code": "38252000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RESIDUAL PRODUCTS OF THE CHEMICAL OR ALLIED INDUSTRIES, NOT ELSEWHERE SPECIFIED OR\nINCLUDED; MUNICIPAL WASTE; SEWAGE SLUDGE; OTHER WASTES SPECIFIED IN NOTE 6 TO THIS\nCHAPTER - MUNICIPAL WASTE",
+        "hsn_code": "38251000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FLUORINE, CHLORINE, BROMINE AND IODINE - CHLORINE",
+        "hsn_code": "28011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FLUORINE, CHLORINE, BROMINE AND IODINE - FLUORINE; BROMINE : FLUORINE",
+        "hsn_code": "28013010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FLUORINE, CHLORINE, BROMINE AND IODINE - FLUORINE; BROMINE : BROMINE",
+        "hsn_code": "28013020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHUR, SUBLIMED OR PRECIPITATED; COLLOIDAL SULPHUR - SULPHUR, SUBLIMED OR\nPRECIPITATED; COLLOIDAL SULPHUR : SUBLIMED SULPHUR",
+        "hsn_code": "28020010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHUR, SUBLIMED OR PRECIPITATED; COLLOIDAL SULPHUR - SULPHUR, SUBLIMED OR\nPRECIPITATED; COLLOIDAL SULPHUR : PRECIPITATED SULPHUR",
+        "hsn_code": "28020020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHUR, SUBLIMED OR PRECIPITATED; COLLOIDAL SULPHUR - SULPHUR, SUBLIMED OR\nPRECIPITATED; COLLOIDAL SULPHUR : COLLOIDAL SULPHUR",
+        "hsn_code": "28020030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBON (CARBON BLACKS AND OTHER FORMS OF CARBON NOT ELSEWHERE SPECIFIED OR\nINCLUDED) - CARBON (CARBON BLACKS AND OTHER FORMS OF CARBON NOT ELSEWHERE\nSPECIFIED OR INCLUDED) : CARBON BLACKS",
+        "hsn_code": "28030010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBON (CARBON BLACKS AND OTHER FORMS OF CARBON NOT ELSEWHERE SPECIFIED OR\nINCLUDED) - CARBON (CARBON BLACKS AND OTHER FORMS OF CARBON NOT ELSEWHERE\nSPECIFIED OR INCLUDED) : ACETYLENE BLACK",
+        "hsn_code": "28030020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBON (CARBON BLACKS AND OTHER FORMS OF CARBON NOT ELSEWHERE SPECIFIED OR\nINCLUDED) - CARBON (CARBON BLACKS AND OTHER FORMS OF CARBON NOT ELSEWHERE\nSPECIFIED OR INCLUDED) : OTHER",
+        "hsn_code": "28030090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN, RARE GASES AND OTHER NON-METALS - HYDROGEN",
+        "hsn_code": "28041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN, RARE GASES AND OTHER NON-METALS - RARE GASES : ARGON",
+        "hsn_code": "28042100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN, RARE GASES AND OTHER NON-METALS - RARE GASES : - OTHER : HELIUM",
+        "hsn_code": "28042910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN, RARE GASES AND OTHER NON-METALS - RARE GASES : - OTHER : OTHER",
+        "hsn_code": "28042990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN, RARE GASES AND OTHER NON-METALS - NITROGEN",
+        "hsn_code": "28043000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN, RARE GASES AND OTHER NON-METALS - OXYGEN :OTHER",
+        "hsn_code": "28044090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN, RARE GASES AND OTHER NON-METALS - BORON; TELLURIUM : BORON",
+        "hsn_code": "28045010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN, RARE GASES AND OTHER NON-METALS - BORON; TELLURIUM : TELLURIUM",
+        "hsn_code": "28045020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN, RARE GASES AND OTHER NON-METALS - SILICON : CONTAINING BY WEIGHT NOT\nLESS THAN 99.99% OF SILICON",
+        "hsn_code": "28046100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN, RARE GASES AND OTHER NON-METALS - SILICON : OTHER",
+        "hsn_code": "28046900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN, RARE GASES AND OTHER NON-METALS - PHOSPHORUS : PHOSPHORUS, BLACK",
+        "hsn_code": "28047010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN, RARE GASES AND OTHER NON-METALS - PHOSPHORUS : PHOSPHORUS, RED",
+        "hsn_code": "28047020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN, RARE GASES AND OTHER NON-METALS - PHOSPHORUS : PHOSPHORUS, WHITE OR\nYELLOW",
+        "hsn_code": "28047030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN, RARE GASES AND OTHER NON-METALS- ARSENIC",
+        "hsn_code": "28048000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN, RARE GASES AND OTHER NON-METALS- SELENIUM",
+        "hsn_code": "28049000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALKALI OR ALKALINE-EARTH METALS; RARE-EARTH METALS, SCANDIUM AND YTTRIUM,\nWHETHER OR NOT INTERMIXED OR INTERALLOYED; MERCURY - ALKALI OR ALKALINE-EARTH\nMETALS : CALCIUM",
+        "hsn_code": "28051200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALKALI OR ALKALINE-EARTH METALS; RARE-EARTH METALS, SCANDIUM AND YTTRIUM,\nWHETHER OR NOT INTERMIXED OR INTERALLOYED; MERCURY - ALKALI OR ALKALINE-EARTH\nMETALS : OTHER",
+        "hsn_code": "28051900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALKALI OR ALKALINE-EARTH METALS; RARE-EARTH METALS, SCANDIUM AND YTTRIUM,\nWHETHER OR NOT INTERMIXED OR INTERALLOYED; MERCURY - RARE-EARTH METALS,\nSCANDIUM AND YTTRIUM, WHETHER OR NOT INTERMIXED OR INTERALLOYED",
+        "hsn_code": "28053000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALKALI OR ALKALINE-EARTH METALS; RARE-EARTH METALS, SCANDIUM AND YTTRIUM,\nWHETHER OR NOT INTERMIXED OR INTERALLOYED; MERCURY - MERCURY",
+        "hsn_code": "28054000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN CHLORIDE (HYDROCHLORIC ACID); CHLOROSULPHURIC ACID - HYDROGEN CHLORIDE\n(HYDROCHLORIC ACID)",
+        "hsn_code": "28061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN CHLORIDE (HYDROCHLORIC ACID); CHLOROSULPHURIC ACID - CHLOROSULPHURIC\nACID",
+        "hsn_code": "28062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHURIC ACID; OLEUM SULPHURIC ACID; OLEUM : SULPHURIC ACID",
+        "hsn_code": "28070010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHURIC ACID; OLEUM SULPHURIC ACID; OLEUM : OLEUM",
+        "hsn_code": "28070020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NITRIC ACID; SULPHONITRIC ACIDS - NITRIC ACID; SULPHONITRIC ACIDS : NITRIC ACID",
+        "hsn_code": "28080010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NITRIC ACID; SULPHONITRIC ACIDS - NITRIC ACID; SULPHONITRIC ACIDS : SULPHONITRIC ACIDS",
+        "hsn_code": "28080020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXIDES OF BORON; BORIC ACIDS - OXIDES OF BORON; BORIC ACIDS : OXIDES OF BORON",
+        "hsn_code": "28100010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXIDES OF BORON; BORIC ACIDS - OXIDES OF BORON; BORIC ACIDS : BORIC ACIDS",
+        "hsn_code": "28100020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC ACIDS AND OTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS -\nOTHER INORGANIC ACIDS : HYDROGEN FLUORIDE (HYDROFLUORIC ACID)",
+        "hsn_code": "28111100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC ACIDS AND OTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS -\nOTHER INORGANIC ACIDS : - OTHER : HYDROCYANIC ACID (HYDROGEN CYANIDE, PRUSSIC ACID)",
+        "hsn_code": "28111910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC ACIDS AND OTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS -\nOTHER INORGANIC ACIDS : - OTHER : HYPOPHOSPHORUS ACID (PHOSPHINIC ACID)",
+        "hsn_code": "28111920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC ACIDS AND OTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS -\nOTHER INORGANIC ACIDS : - OTHER : ACIDS OF ARSENIC",
+        "hsn_code": "28111930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC ACIDS AND OTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS -\nOTHER INORGANIC ACIDS : - OTHER : SULPHONIC ACID",
+        "hsn_code": "28111940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC ACIDS AND OTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS -\nOTHER INORGANIC ACIDS : - OTHER : OTHER",
+        "hsn_code": "28111990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC ACIDS AND OTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS -\nOTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS : - CARBON DIOXIDE : DRY ICE",
+        "hsn_code": "28112110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC ACIDS AND OTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS -\nOTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS : - CARBON DIOXIDE : OTHER",
+        "hsn_code": "28112190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC ACIDS AND OTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS -\nOTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS : SILICON DIOXIDE",
+        "hsn_code": "28112200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC ACIDS AND OTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS -\nOTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS : SULPHUR DIOXIDE",
+        "hsn_code": "28112300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC ACIDS AND OTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS -\nOTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS : - OTHER : ARSENIC PENTAOXIDE",
+        "hsn_code": "28112910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC ACIDS AND OTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS -\nOTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS : - OTHER : ARSENIC TRIOXIDE",
+        "hsn_code": "28112920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC ACIDS AND OTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS -\nOTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS : - OTHER : NITROUS OXIDE",
+        "hsn_code": "28112930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC ACIDS AND OTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS -\nOTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS : - OTHER : CARBON MONOXIDE",
+        "hsn_code": "28112940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC ACIDS AND OTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS -\nOTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS : - OTHER : SULPHUR TRIOXIDE\n(SULPHURIC ANHYDRIDE)",
+        "hsn_code": "28112950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC ACIDS AND OTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS -\nOTHER INORGANIC OXYGEN COMPOUNDS OF NON-METALS : - OTHER : OTHER",
+        "hsn_code": "28112990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALIDES AND HALIDE OXIDES OF NON-METALS - CHLORIDES AND CHLORIDE OXIDES : PHOSGENE\n(CARBONYL CHLORIDE, CARBON OXY-CHLORIDE, CHLOROFORMYL CHLORIDE)",
+        "hsn_code": "28121010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALIDES AND HALIDE OXIDES OF NON-METALS - CHLORIDES AND CHLORIDE OXIDES :\nPHOSPHORUS TRICHLORIDE",
+        "hsn_code": "28121020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHORUS TRICHLORIDE",
+        "hsn_code": "28121021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHORUS PENTACHLORIDE",
+        "hsn_code": "28121022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALIDES AND HALIDE OXIDES OF NON-METALS - CHLORIDES AND CHLORIDE OXIDES :\nPHOSPHORUS OXYCHLORIDE",
+        "hsn_code": "28121030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALIDES AND HALIDE OXIDES OF NON-METALS - CHLORIDES AND CHLORIDE OXIDES : SULPHUR\nOXYCHLORIDE, THIONYL CHLORIDE",
+        "hsn_code": "28121040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHUR OXYCHLORIDE",
+        "hsn_code": "28121041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHUR MONOCHLORIDE",
+        "hsn_code": "28121042",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHUR DICHLORIDE",
+        "hsn_code": "28121043",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "THIONYL CHLORIDE",
+        "hsn_code": "28121047",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALIDES AND HALIDE OXIDES OF NON-METALS - CHLORIDES AND CHLORIDE OXIDES : SILICON\nTETRACHLORIDE",
+        "hsn_code": "28121050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ARSENOUS TRICHLORIDE",
+        "hsn_code": "28121060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALIDES AND HALIDE OXIDES OF NON-METALS - CHLORIDES AND CHLORIDE OXIDES : OTHER",
+        "hsn_code": "28121090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALIDES AND HALIDE OXIDES OF NON-METALS - OTHER",
+        "hsn_code": "28129000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHIDES OF NON-METALS; COMMERCIAL PHOSPHORUS TRISULPHIDE -CARBON DISULPHIDE",
+        "hsn_code": "28131000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHIDES OF NON-METALS; COMMERCIAL PHOSPHORUS TRISULPHIDE - OTHER : ARSENIC\nDISULPHIDE (ARTIFICIAL)",
+        "hsn_code": "28139010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHIDES OF NON-METALS; COMMERCIAL PHOSPHORUS TRISULPHIDE - OTHER : COMMERCIAL\nPHOSPHORUS TRISULPHIDE",
+        "hsn_code": "28139020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHIDES OF NON-METALS; COMMERCIAL PHOSPHORUS TRISULPHIDE - OTHER : OTHER",
+        "hsn_code": "28139090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMMONIA, ANHYDROUS OR IN AQUEOUS SOLUTION - ANHYDROUS AMMONIA",
+        "hsn_code": "28141000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMMONIA, ANHYDROUS OR IN AQUEOUS SOLUTION - AMMONIA IN AQUEOUS SOLUTION",
+        "hsn_code": "28142000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SODIUM HYDROXIDE (CAUSTIC SODA); POTASSIUM HYDROXIDE (CAUSTIC POTASH); PEROXIDES\nOF SODIUM OR POTASSIUM - SODIUM HYDROXIDE (CAUSTIC SODA) : - SOLID : FLAKES",
+        "hsn_code": "28151110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SODIUM HYDROXIDE (CAUSTIC SODA); POTASSIUM HYDROXIDE (CAUSTIC POTASH); PEROXIDES\nOF SODIUM OR POTASSIUM - SODIUM HYDROXIDE (CAUSTIC SODA) : - SOLID : OTHER",
+        "hsn_code": "28151190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SODIUM HYDROXIDE (CAUSTIC SODA); POTASSIUM HYDROXIDE (CAUSTIC POTASH); PEROXIDES\nOF SODIUM OR POTASSIUM - SODIUM HYDROXIDE (CAUSTIC SODA) : IN AQUEOUS SOLUTION\n(SODA LYE OR LIQUID SODA)",
+        "hsn_code": "28151200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SODIUM HYDROXIDE (CAUSTIC SODA); POTASSIUM HYDROXIDE (CAUSTIC POTASH); PEROXIDES\nOF SODIUM OR POTASSIUM - POTASSIUM HYDROXIDE (CAUSTIC POTASH)",
+        "hsn_code": "28152000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SODIUM HYDROXIDE (CAUSTIC SODA); POTASSIUM HYDROXIDE (CAUSTIC POTASH); PEROXIDES\nOF SODIUM OR POTASSIUM - PEROXIDES OF SODIUM OR POTASSIUM",
+        "hsn_code": "28153000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROXIDE AND PEROXIDE OF MAGNESIUM; OXIDES, HYDROXIDES AND PEROXIDES, OF\nSTRONTIUM OR BARIUM - HYDROXIDE AND PEROXIDE OF MAGNESIUM : HYDROXIDE OF\nMAGNESIUM",
+        "hsn_code": "28161010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROXIDE AND PEROXIDE OF MAGNESIUM; OXIDES, HYDROXIDES AND PEROXIDES, OF\nSTRONTIUM OR BARIUM - HYDROXIDE AND PEROXIDE OF MAGNESIUM : PEROXIDE OF\nMAGNESIUM",
+        "hsn_code": "28161020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROXIDE AND PEROXIDE OF MAGNESIUM; OXIDES, HYDROXIDES AND PEROXIDES, OF\nSTRONTIUM OR BARIUM - OXIDES, HYDROXIDES AND PEROXIDES, OF STRONTIUM OR BARIUM",
+        "hsn_code": "28164000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ZINC OXIDE; ZINC PEROXIDE - ZINC OXIDE; ZINC PEROXIDE : ZINC OXIDE",
+        "hsn_code": "28170010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ZINC OXIDE; ZINC PEROXIDE - ZINC OXIDE; ZINC PEROXIDE : ZINC PEROXIDE",
+        "hsn_code": "28170020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL CORUNDUM, WHETHER OR NOT CHEMICALLY DEFINED; ALUMINIUM OXIDE;\nALUMINIUM HYDROXIDE ARTIFICIAL CORUNDUM, WHETHER OR NOT CHEMICALLY DEFINED",
+        "hsn_code": "28181000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL CORUNDUM, WHETHER OR NOT CHEMICALLY DEFINED; ALUMINIUM OXIDE;\nALUMINIUM HYDROXIDE - ALUMINIUM OXIDE, OTHER THAN ARTIFICIAL CORUNDUM :\nALUMINA, CALCINED",
+        "hsn_code": "28182010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL CORUNDUM, WHETHER OR NOT CHEMICALLY DEFINED; ALUMINIUM OXIDE;\nALUMINIUM HYDROXIDE - ALUMINIUM OXIDE, OTHER THAN ARTIFICIAL CORUNDUM : OTHER",
+        "hsn_code": "28182090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL CORUNDUM, WHETHER OR NOT CHEMICALLY DEFINED; ALUMINIUM OXIDE;\nALUMINIUM HYDROXIDE -ALUMINIUM HYDROXIDE",
+        "hsn_code": "28183000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHROMIUM OXIDES AND HYDROXIDES - CHROMIUM TRIOXIDE",
+        "hsn_code": "28191000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHROMIUM OXIDES AND HYDROXIDES - OTHER",
+        "hsn_code": "28199000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MANGANESE OXIDES - MANGANESE DIOXIDE",
+        "hsn_code": "28201000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MANGANESE OXIDES - OTHER",
+        "hsn_code": "28209000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "IRON OXIDES AND HYDROXIDES; EARTH COLOURS CONTAINING 70% OR MORE BY WEIGHT OF\nCOMBINED IRON EVALUATED AS Fe2O3 - IRON OXIDES AND HYDROXIDES : IRON OXIDES",
+        "hsn_code": "28211010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "IRON OXIDES AND HYDROXIDES; EARTH COLOURS CONTAINING 70% OR MORE BY WEIGHT OF\nCOMBINED IRON EVALUATED AS Fe2O3 - IRON OXIDES AND HYDROXIDES : IRON HYDROXIDES",
+        "hsn_code": "28211020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "IRON OXIDES AND HYDROXIDES; EARTH COLOURS CONTAINING 70% OR MORE BY WEIGHT OF\nCOMBINED IRON EVALUATED AS Fe2O3 - EARTH COLOURS",
+        "hsn_code": "28212000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COBALT OXIDES AND HYDROXIDES; COMMERCIAL COBALT OXIDES - COBALT OXIDES AND\nHYDROXIDES; COMMERCIAL COBALT OXIDES : COBALT OXIDES",
+        "hsn_code": "28220010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COBALT OXIDES AND HYDROXIDES; COMMERCIAL COBALT OXIDES - COBALT OXIDES AND\nHYDROXIDES; COMMERCIAL COBALT OXIDES : COBALT HYDROXIDES",
+        "hsn_code": "28220020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COBALT OXIDES AND HYDROXIDES; COMMERCIAL COBALT OXIDES - COBALT OXIDES AND\nHYDROXIDES; COMMERCIAL COBALT OXIDES : COMMERCIAL COBALT OXIDES",
+        "hsn_code": "28220030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TITANIUM OXIDES - TITANIUM OXIDES : TITANIUM DIOXIDE",
+        "hsn_code": "28230010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TITANIUM OXIDES - TITANIUM OXIDES : OTHER",
+        "hsn_code": "28230090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LEAD OXIDES; RED LEAD AND ORANGE LEAD - LEAD MONOXIDE (LITHARGE, MASSICOT) :\nLITHARGE",
+        "hsn_code": "28241010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LEAD OXIDES; RED LEAD AND ORANGE LEAD - LEAD MONOXIDE (LITHARGE, MASSICOT) :\nMASSICOT",
+        "hsn_code": "28241020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LEAD OXIDES; RED LEAD AND ORANGE LEAD -RED LEAD AND ORANGE LEAD",
+        "hsn_code": "28242000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LEAD OXIDES; RED LEAD AND ORANGE LEAD - OTHER",
+        "hsn_code": "28249000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - HYDRAZINE AND HYDROXYLAMINE AND\nTHEIR INORGANIC SALTS : HYDRAZINE ANHYDROUS",
+        "hsn_code": "28251010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - HYDRAZINE AND HYDROXYLAMINE AND\nTHEIR INORGANIC SALTS : HYDRAZINE HYDRATE",
+        "hsn_code": "28251020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - HYDRAZINE AND HYDROXYLAMINE AND\nTHEIR INORGANIC SALTS : HYDRAZINE SULPHATE",
+        "hsn_code": "28251030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - HYDRAZINE AND HYDROXYLAMINE AND\nTHEIR INORGANIC SALTS : HYDROXYLAMINE SULPHATE",
+        "hsn_code": "28251040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - HYDRAZINE AND HYDROXYLAMINE AND\nTHEIR INORGANIC SALTS : OTHER",
+        "hsn_code": "28251090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - LITHIUM OXIDE AND HYDROXIDE",
+        "hsn_code": "28252000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - VANADIUM OXIDES AND HYDROXIDES :\nVANADIUM PENTAOXIDE FLAKES",
+        "hsn_code": "28253010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES VANADIUM OXIDES AND HYDROXIDES :\nOTHER",
+        "hsn_code": "28253090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - NICKEL OXIDES AND HYDROXIDES",
+        "hsn_code": "28254000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - COPPER OXIDES AND HYDROXIDES",
+        "hsn_code": "28255000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - GERMANIUM OXIDES AND ZIRCONIUM\nDIOXIDE : GERMANIUM OXIDES",
+        "hsn_code": "28256010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - GERMANIUM OXIDES AND ZIRCONIUM\nDIOXIDE : ZIRCONIUM DIOXIDE",
+        "hsn_code": "28256020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - MOLYBDENUM OXIDES AND HYDROXIDES\n: MOLYBDENUM TRIOXIDE",
+        "hsn_code": "28257010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - MOLYBDENUM OXIDES AND HYDROXIDES\n: MOLYBDIC ACID",
+        "hsn_code": "28257020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - MOLYBDENUM OXIDES AND HYDROXIDES\n: OTHER",
+        "hsn_code": "28257090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES ANTIMONY OXIDES",
+        "hsn_code": "28258000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - OTHER : TIN OXIDE",
+        "hsn_code": "28259010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - OTHER : CADMIUM OXIDE",
+        "hsn_code": "28259020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - OTHER : MERCURY OXIDES (MERCURIC\nOXIDE)",
+        "hsn_code": "28259030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - OTHER : CALCIUM HYDROXIDE",
+        "hsn_code": "28259040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - OTHER : AMMONIUM HYDROXIDE",
+        "hsn_code": "28259050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES;\nOTHER METAL OXIDES, HYDROXIDES AND PEROXIDES - OTHER : OTHER",
+        "hsn_code": "28259090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FLUORIDES; FLUOROSILICATES, FLUOROALUMINATES AND OTHER COMPLEX FLUORINE SALTS -\nFLUORIDES : - OF AMMONIUM OR OF SODIUM : AMMONIUM FLUORIDES",
+        "hsn_code": "28261110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FLUORIDES; FLUOROSILICATES, FLUOROALUMINATES AND OTHER COMPLEX FLUORINE SALTS -\nFLUORIDES : - OF AMMONIUM OR OF SODIUM : SODIUM FLUORIDES",
+        "hsn_code": "28261120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FLUORIDES; FLUOROSILICATES, FLUOROALUMINATES AND OTHER COMPLEX FLUORINE SALTS -\nFLUORIDES : OF ALUMINIUM",
+        "hsn_code": "28261200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FLUORIDES; FLUOROSILICATES, FLUOROALUMINATES AND OTHER COMPLEX FLUORINE SALTS -\nFLUORIDES : - OTHER : MAGNESIUM FLUORIDE",
+        "hsn_code": "28261910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FLUORIDES; FLUOROSILICATES, FLUOROALUMINATES AND OTHER COMPLEX FLUORINE SALTS -\nFLUORIDES : - OTHER : OTHER",
+        "hsn_code": "28261990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FLUORIDES; FLUOROSILICATES, FLUOROALUMINATES AND OTHER COMPLEX FLUORINE SALTS -\nFLUOROSILICATES OF SODIUM OR OF POTASSIUM : FLUOROSILICATES OF SODIUM",
+        "hsn_code": "28262010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FLUORIDES; FLUOROSILICATES, FLUOROALUMINATES AND OTHER COMPLEX FLUORINE SALTS -\nFLUOROSILICATES OF SODIUM OR OF POTASSIUM : FLUOROSILICATES OF POTASSIUM",
+        "hsn_code": "28262020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FLUORIDES; FLUOROSILICATES, FLUOROALUMINATES AND OTHER COMPLEX FLUORINE SALTS -\nSODIUM HEXAFLUOROALUMINATE (SYNTHETIC CRYOLITE)",
+        "hsn_code": "28263000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FLUORIDES; FLUOROSILICATES, FLUOROALUMINATES AND OTHER COMPLEX FLUORINE SALTS -\nOTHER",
+        "hsn_code": "28269000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - AMMONIUM CHLORIDE",
+        "hsn_code": "28271000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - CALCIUM CHLORIDE",
+        "hsn_code": "28272000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - OTHER CHLORIDES : OF MAGNESIUM",
+        "hsn_code": "28273100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - OTHER CHLORIDES : OF ALUMINIUM",
+        "hsn_code": "28273200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - OTHER CHLORIDES : OF IRON",
+        "hsn_code": "28273300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - OTHER CHLORIDES : OF COBALT",
+        "hsn_code": "28273400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - OTHER CHLORIDES : OF NICKEL",
+        "hsn_code": "28273500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - OTHER CHLORIDES : OF ZINC",
+        "hsn_code": "28273600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - OTHER CHLORIDES : - OTHER : MERCURIC CHLORIDE",
+        "hsn_code": "28273910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - OTHER CHLORIDES : - OTHER : MERCUROUS CHLORIDE",
+        "hsn_code": "28273920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - OTHER CHLORIDES : - OTHER : STRONTIUM CHLORIDE",
+        "hsn_code": "28273930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - OTHER CHLORIDES : - OTHER : CUPROUS CHLORIDE",
+        "hsn_code": "28273940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - OTHER CHLORIDES : - OTHER : OTHER",
+        "hsn_code": "28273990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - CHLORIDE OXIDES AND CHLORIDE HYDROXIDES : - OF COPPER :\nCOPPER OXYCHLORIDE",
+        "hsn_code": "28274110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - CHLORIDE OXIDES AND CHLORIDE HYDROXIDES : - OF COPPER :\nOTHER",
+        "hsn_code": "28274190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - CHLORIDE OXIDES AND CHLORIDE HYDROXIDES : OTHER",
+        "hsn_code": "28274900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - BROMIDES AND BROMIDE OXIDES - BROMIDES OF SODIUM OR OF\nPOTASSIUM : BROMIDES OF SODIUM",
+        "hsn_code": "28275110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - BROMIDES AND BROMIDE OXIDES - BROMIDES OF SODIUM OR OF\nPOTASSIUM : BROMIDES OF POTASSIUM",
+        "hsn_code": "28275120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - BROMIDES AND BROMIDE OXIDES - OTHER : MAGNESIUM\nBROMIDE",
+        "hsn_code": "28275910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - BROMIDES AND BROMIDE OXIDES - OTHER : OTHER",
+        "hsn_code": "28275990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - IODIDES AND IODIDE OXIDES - POTASSIUM IODIDE",
+        "hsn_code": "28276010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - IODIDES AND IODIDE OXIDES - SODIUM IODIDE",
+        "hsn_code": "28276020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES;\nIODIDES AND IODIDE OXIDES - IODIDES AND IODIDE OXIDES - OTHER",
+        "hsn_code": "28276090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYPOCHLORITES; COMMERCIAL CALCIUM HYPOCHLORITES; CHLORITES; HYPOBROMITES -\nCOMMERCIAL CALCIUM HYPOCHLORITE AND OTHER CALCIUM HYPOCHLORITES : COMMERCIAL\nCALCIUM HYPOCHLORITE (BLEACHING PASTE OR POWDER)",
+        "hsn_code": "28281010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYPOCHLORITES; COMMERCIAL CALCIUM HYPOCHLORITES; CHLORITES; HYPOBROMITES -\nCOMMERCIAL CALCIUM HYPOCHLORITE AND OTHER CALCIUM HYPOCHLORITES : OTHER",
+        "hsn_code": "28281090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYPOCHLORITES; COMMERCIAL CALCIUM HYPOCHLORITES; CHLORITES; HYPOBROMITES -\nOTHER : - SODIUM HYPOCHLORITES : BLEACHING PASTE OR POWDER",
+        "hsn_code": "28289011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYPOCHLORITES; COMMERCIAL CALCIUM HYPOCHLORITES; CHLORITES; HYPOBROMITES -\nOTHER : - SODIUM HYPOCHLORITES : OTHER",
+        "hsn_code": "28289019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYPOCHLORITES; COMMERCIAL CALCIUM HYPOCHLORITES; CHLORITES; HYPOBROMITES -\nOTHER : POTASSIUM HYPOCHLORITES",
+        "hsn_code": "28289020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYPOCHLORITES; COMMERCIAL CALCIUM HYPOCHLORITES; CHLORITES; HYPOBROMITES -\nOTHER : SODIUM CHLORITE",
+        "hsn_code": "28289030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYPOCHLORITES; COMMERCIAL CALCIUM HYPOCHLORITES; CHLORITES; HYPOBROMITES -\nOTHER : ALUMINIUM CHLORITE",
+        "hsn_code": "28289040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYPOCHLORITES; COMMERCIAL CALCIUM HYPOCHLORITES; CHLORITES; HYPOBROMITES -\nOTHER : HYPOBROMITES",
+        "hsn_code": "28289050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYPOCHLORITES; COMMERCIAL CALCIUM HYPOCHLORITES; CHLORITES; HYPOBROMITES -\nOTHER : BLEACHING PASTE OR POWDER OF OTHER HYPOCHLORITES",
+        "hsn_code": "28289060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYPOCHLORITES; COMMERCIAL CALCIUM HYPOCHLORITES; CHLORITES; HYPOBROMITES -\nOTHER : OTHER",
+        "hsn_code": "28289090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORATES AND PERCHLORATES; BROMATES AND PERBROMATES; IODATES AND PERIODATES -\nCHLORATES : OF SODIUM",
+        "hsn_code": "28291100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORATES AND PERCHLORATES; BROMATES AND PERBROMATES; IODATES AND PERIODATES -\nCHLORATES : - OTHER : BARIUM CHLORATE",
+        "hsn_code": "28291910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORATES AND PERCHLORATES; BROMATES AND PERBROMATES; IODATES AND PERIODATES -\nCHLORATES : - OTHER : POTASSIUM CHLORATE",
+        "hsn_code": "28291920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORATES AND PERCHLORATES; BROMATES AND PERBROMATES; IODATES AND PERIODATES -\nCHLORATES : - OTHER : MAGNESIUM CHLORATE",
+        "hsn_code": "28291930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORATES AND PERCHLORATES; BROMATES AND PERBROMATES; IODATES AND PERIODATES -\nCHLORATES : - OTHER : OTHER",
+        "hsn_code": "28291990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORATES AND PERCHLORATES; BROMATES AND PERBROMATES; IODATES AND PERIODATES -\nOTHER : PERCHLORATES",
+        "hsn_code": "28299010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORATES AND PERCHLORATES; BROMATES AND PERBROMATES; IODATES AND PERIODATES -\nOTHER : BROMATES AND PERBROMATES",
+        "hsn_code": "28299020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORATES AND PERCHLORATES; BROMATES AND PERBROMATES; IODATES AND PERIODATES -\nOTHER : IODATES AND PERIODATES",
+        "hsn_code": "28299030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHIDES; POLYSULPHIDES, WHETHER OR NOT CHEMICALLY DEFINED - SODIUM SULPHIDES",
+        "hsn_code": "28301000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHIDES; POLYSULPHIDES, WHETHER OR NOT CHEMICALLY DEFINED - ZINC SULPHIDE",
+        "hsn_code": "28302000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHIDES; POLYSULPHIDES, WHETHER OR NOT CHEMICALLY DEFINED - CADMIUM SULPHIDE",
+        "hsn_code": "28303000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHIDES; POLYSULPHIDES, WHETHER OR NOT CHEMICALLY DEFINED - OTHER - SULPHIDES",
+        "hsn_code": "28309010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHIDES; POLYSULPHIDES, WHETHER OR NOT CHEMICALLY DEFINED - OTHER -\nPOLYSULPHIDES",
+        "hsn_code": "28309020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DITHIONITES AND SULPHOXYLATES - OF SODIUM : SODIUM DITHIONITES (SODIUM\nHYDROSULPHITE)",
+        "hsn_code": "28311010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DITHIONITES AND SULPHOXYLATES - OF SODIUM : SODIUM SULPHOXYLATES (INCLUDING\nSODIUM FORMALDEHYDE SULPHOXYLATE)",
+        "hsn_code": "28311020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DITHIONITES AND SULPHOXYLATES - OTHER : DITHIONITES",
+        "hsn_code": "28319010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DITHIONITES AND SULPHOXYLATES - OTHER : SULPHOXYLATES",
+        "hsn_code": "28319020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHITES; THIOSULPHATES - SODIUM SULPHITES : SODIUM BISULPHITE",
+        "hsn_code": "28321010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHITES; THIOSULPHATES - SODIUM SULPHITES : SODIUM HYDROSULPHITE",
+        "hsn_code": "28321020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHITES; THIOSULPHATES - SODIUM SULPHITES : OTHER",
+        "hsn_code": "28321090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHITES; THIOSULPHATES - OTHER SULPHITES: POTASSIUM METABISULPHITE",
+        "hsn_code": "28322010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHITES; THIOSULPHATES - OTHER SULPHITES: MAGNESIUM SULPHITE",
+        "hsn_code": "28322020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHITES; THIOSULPHATES - OTHER SULPHITES: OTHER",
+        "hsn_code": "28322090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHITES; THIOSULPHATES - THIOSULPHATES : SODIUM THIOSULPHATE (HYPO)",
+        "hsn_code": "28323010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHITES; THIOSULPHATES - THIOSULPHATES : MAGNESIUM THIOSULPHATE",
+        "hsn_code": "28323020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHITES; THIOSULPHATES - THIOSULPHATES : OTHER",
+        "hsn_code": "28323090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - SODIUM SULPHATES : DISODIUM\nSULPHATE",
+        "hsn_code": "28331100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - SODIUM SULPHATES : - OTHER :\nSODIUM HYDROGEN SULPHATE (ACID SULPHATE)",
+        "hsn_code": "28331910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - SODIUM SULPHATES : - OTHER :\nSODIUM PYROSULPHATE",
+        "hsn_code": "28331920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - SODIUM SULPHATES : - OTHER :\nOTHER",
+        "hsn_code": "28331990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - OTHER SULPHATES : OF\nMAGNESIUM",
+        "hsn_code": "28332100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - OTHER SULPHATES : - OF\nALUMINIUM : ALUMINIUM SULPHATE (IRON FREE)",
+        "hsn_code": "28332210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - OTHER SULPHATES : - OF\nALUMINIUM : OTHER",
+        "hsn_code": "28332290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - OTHER SULPHATES : OF CHROMIUM",
+        "hsn_code": "28332300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - OTHER SULPHATES : OF NICKEL",
+        "hsn_code": "28332400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - OTHER SULPHATES : OF COPPER",
+        "hsn_code": "28332500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - OTHER SULPHATES : - OF ZINC :\nAGRICULTURAL GRADE ZINC SULPHATE ORDINARILY USED AS MICRONUTRIENT",
+        "hsn_code": "28332610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - OTHER SULPHATES : - OF ZINC :\nOTHER",
+        "hsn_code": "28332690",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - OTHER SULPHATES : OF BARIUM",
+        "hsn_code": "28332700",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - OTHER SULPHATES : - OTHER :\nFERROUS SULPHATE",
+        "hsn_code": "28332910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - OTHER SULPHATES : - OTHER :\nMERCURIC SULPHATE",
+        "hsn_code": "28332920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - OTHER SULPHATES : - OTHER :\nQUINIDINE SULPHATE",
+        "hsn_code": "28332930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - OTHER SULPHATES : - OTHER :\nMANGANESE SULPHATE",
+        "hsn_code": "28332940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - OTHER SULPHATES : - OTHER :\nSTRONTIUM SULPHATE",
+        "hsn_code": "28332950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - OTHER SULPHATES : - OTHER :\nOTHER",
+        "hsn_code": "28332990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - ALUMS: AMMONIUM ALUM",
+        "hsn_code": "28333010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - ALUMS: FERRIC AMMONIUM ALUM",
+        "hsn_code": "28333020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - ALUMS: POTASH ALUM",
+        "hsn_code": "28333030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - ALUMS: OTHER",
+        "hsn_code": "28333090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES) - PEROXOSULPHATES\n(PERSULPHATES)",
+        "hsn_code": "28334000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NITRITES; NITRATES - NITRITES : SODIUM NITRITE",
+        "hsn_code": "28341010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NITRITES; NITRATES - NITRITES : OTHER",
+        "hsn_code": "28341090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NITRITES; NITRATES - NITRATES: OF POTASSIUM",
+        "hsn_code": "28342100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NITRITES; NITRATES - NITRATES: - OTHER : STRONTIUM NITRATE",
+        "hsn_code": "28342910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NITRITES; NITRATES - NITRATES: - OTHER : MAGNESIUM NITRATE",
+        "hsn_code": "28342920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NITRITES; NITRATES - NITRATES: - OTHER : BARIUM NITRATE",
+        "hsn_code": "28342930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NITRITES; NITRATES - NITRATES: - OTHER : OTHER",
+        "hsn_code": "28342990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHINATES (HYPOPHOSPHITES), PHOSPHONATES (PHOSPHITES) AND PHOSPHATES;\nPOLYPHOSPHATES, WHETHER OR NOT CHEMICALLY DEFINED PHOSPHINATES\n(HYPOPHOSPHITES) AND PHOSPHONATES (PHOSPHITES) : CALCIUM HYPOPHOSPHITE",
+        "hsn_code": "28351010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHINATES (HYPOPHOSPHITES), PHOSPHONATES (PHOSPHITES) AND PHOSPHATES;\nPOLYPHOSPHATES, WHETHER OR NOT CHEMICALLY DEFINED PHOSPHINATES\n(HYPOPHOSPHITES) AND PHOSPHONATES (PHOSPHITES) : MAGNESIUM HYPOPHOSPHITE",
+        "hsn_code": "28351020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHINATES (HYPOPHOSPHITES), PHOSPHONATES (PHOSPHITES) AND PHOSPHATES;\nPOLYPHOSPHATES, WHETHER OR NOT CHEMICALLY DEFINED PHOSPHINATES\n(HYPOPHOSPHITES) AND PHOSPHONATES (PHOSPHITES) :OTHER",
+        "hsn_code": "28351090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHINATES (HYPOPHOSPHITES), PHOSPHONATES (PHOSPHITES) AND PHOSPHATES;\nPOLYPHOSPHATES, WHETHER OR NOT CHEMICALLY DEFINED - PHOSPHATES : OF MONO-OR\nDISODIUM",
+        "hsn_code": "28352200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHINATES (HYPOPHOSPHITES), PHOSPHONATES (PHOSPHITES) AND PHOSPHATES;\nPOLYPHOSPHATES, WHETHER OR NOT CHEMICALLY DEFINED - PHOSPHATES : OF TRISODIUM",
+        "hsn_code": "28352300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHINATES (HYPOPHOSPHITES), PHOSPHONATES (PHOSPHITES) AND PHOSPHATES;\nPOLYPHOSPHATES, WHETHER OR NOT CHEMICALLY DEFINED - PHOSPHATES : OF POTASSIUM",
+        "hsn_code": "28352400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHINATES (HYPOPHOSPHITES), PHOSPHONATES (PHOSPHITES) AND PHOSPHATES;\nPOLYPHOSPHATES, WHETHER OR NOT CHEMICALLY DEFINED - PHOSPHATES : CALCIUM\nHYDROGENORTHOPHOSPHATE (\"DICALCIUM PHOSPHATE\")",
+        "hsn_code": "28352500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHINATES (HYPOPHOSPHITES), PHOSPHONATES (PHOSPHITES) AND PHOSPHATES;\nPOLYPHOSPHATES, WHETHER OR NOT CHEMICALLY DEFINED - PHOSPHATES : - OTHER\nPHOSPHATES OF CALCIUM : CALCIUM MONOBASIC PHOSPHATE",
+        "hsn_code": "28352610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHINATES (HYPOPHOSPHITES), PHOSPHONATES (PHOSPHITES) AND PHOSPHATES;\nPOLYPHOSPHATES, WHETHER OR NOT CHEMICALLY DEFINED - PHOSPHATES : - OTHER\nPHOSPHATES OF CALCIUM : CALCIUM TRIBASIC PHOSPHATE",
+        "hsn_code": "28352620",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHINATES (HYPOPHOSPHITES), PHOSPHONATES (PHOSPHITES) AND PHOSPHATES;\nPOLYPHOSPHATES, WHETHER OR NOT CHEMICALLY DEFINED - PHOSPHATES : - OTHER\nPHOSPHATES OF CALCIUM : OTHER",
+        "hsn_code": "28352690",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHINATES (HYPOPHOSPHITES), PHOSPHONATES (PHOSPHITES) AND PHOSPHATES;\nPOLYPHOSPHATES, WHETHER OR NOT CHEMICALLY DEFINED - PHOSPHATES : - OTHER :\nMAGNESIUM PHOSPHATE, MONOBASIC",
+        "hsn_code": "28352910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHINATES (HYPOPHOSPHITES), PHOSPHONATES (PHOSPHITES) AND PHOSPHATES;\nPOLYPHOSPHATES, WHETHER OR NOT CHEMICALLY DEFINED - PHOSPHATES : - OTHER :\nMAGNESIUM PHOSPHATE, DIBASIC",
+        "hsn_code": "28352920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHINATES (HYPOPHOSPHITES), PHOSPHONATES (PHOSPHITES) AND PHOSPHATES;\nPOLYPHOSPHATES, WHETHER OR NOT CHEMICALLY DEFINED - PHOSPHATES : - OTHER :\nMAGNESIUM PHOSPHATE, TRIBASIC",
+        "hsn_code": "28352930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHINATES (HYPOPHOSPHITES), PHOSPHONATES (PHOSPHITES) AND PHOSPHATES;\nPOLYPHOSPHATES, WHETHER OR NOT CHEMICALLY DEFINED - PHOSPHATES : - OTHER : SODIUM\nHEXAMETAPHOSPHATE",
+        "hsn_code": "28352940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHINATES (HYPOPHOSPHITES), PHOSPHONATES (PHOSPHITES) AND PHOSPHATES;\nPOLYPHOSPHATES, WHETHER OR NOT CHEMICALLY DEFINED - PHOSPHATES : - OTHER : OTHER",
+        "hsn_code": "28352990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHINATES (HYPOPHOSPHITES), PHOSPHONATES (PHOSPHITES) AND PHOSPHATES;\nPOLYPHOSPHATES, WHETHER OR NOT CHEMICALLY DEFINED - POLYPHOSPHATES : SODIUM\nTRIPHOSPHATE (SODIUM TRIPOLY- PHOSPHATE)",
+        "hsn_code": "28353100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHINATES (HYPOPHOSPHITES), PHOSPHONATES (PHOSPHITES) AND PHOSPHATES;\nPOLYPHOSPHATES, WHETHER OR NOT CHEMICALLY DEFINED - POLYPHOSPHATES : OTHER",
+        "hsn_code": "28353900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBONATES; PEROXOCARBONATES (PERCARBONATES); COMMERCIAL AMMONIUM\nCARBONATE CONTAINING AMMONIUM CARBAMATE COMMERCIAL AMMONIUM CARBONATE\nAND OTHER AMMONIUM CARBONATES",
+        "hsn_code": "28361000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBONATES; PEROXOCARBONATES (PERCARBONATES); COMMERCIAL AMMONIUM\nCARBONATE CONTAINING AMMONIUM CARBAMATE - DISODIUM CARBONATE : DISODIUM\nCARBONATE, DENSE",
+        "hsn_code": "28362010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBONATES; PEROXOCARBONATES (PERCARBONATES); COMMERCIAL AMMONIUM\nCARBONATE CONTAINING AMMONIUM CARBAMATE - DISODIUM CARBONATE : DISODIUM\nCARBONATE, LIGHT",
+        "hsn_code": "28362020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBONATES; PEROXOCARBONATES (PERCARBONATES); COMMERCIAL AMMONIUM\nCARBONATE CONTAINING AMMONIUM CARBAMATE - DISODIUM CARBONATE : OTHER",
+        "hsn_code": "28362090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBONATES; PEROXOCARBONATES (PERCARBONATES); COMMERCIAL AMMONIUM\nCARBONATE CONTAINING AMMONIUM CARBAMATE SODIUM HYDROGENCARBONATE (SODIUM\nBICARBONATE)",
+        "hsn_code": "28363000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBONATES; PEROXOCARBONATES (PERCARBONATES); COMMERCIAL AMMONIUM\nCARBONATE CONTAINING AMMONIUM CARBAMATE POTASSIUM CARBONATES",
+        "hsn_code": "28364000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBONATES; PEROXOCARBONATES (PERCARBONATES); COMMERCIAL AMMONIUM\nCARBONATE CONTAINING AMMONIUM CARBAMATE CALCIUM CARBONATE",
+        "hsn_code": "28365000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBONATES; PEROXOCARBONATES (PERCARBONATES); COMMERCIAL AMMONIUM\nCARBONATE CONTAINING AMMONIUM CARBAMATE BARIUM CARBONATE",
+        "hsn_code": "28366000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBONATES; PEROXOCARBONATES (PERCARBONATES); COMMERCIAL AMMONIUM\nCARBONATE CONTAINING AMMONIUM CARBAMATE LEAD CARBONATE",
+        "hsn_code": "28367000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBONATES; PEROXOCARBONATES (PERCARBONATES); COMMERCIAL AMMONIUM\nCARBONATE CONTAINING AMMONIUM CARBAMATE - OTHER : LITHIUM CARBONATES",
+        "hsn_code": "28369100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBONATES; PEROXOCARBONATES (PERCARBONATES); COMMERCIAL AMMONIUM\nCARBONATE CONTAINING AMMONIUM CARBAMATE - OTHER : STRONTIUM CARBONATE",
+        "hsn_code": "28369200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBONATES; PEROXOCARBONATES (PERCARBONATES); COMMERCIAL AMMONIUM\nCARBONATE CONTAINING AMMONIUM CARBAMATE - OTHER : - OTHER : PERCARBONATES",
+        "hsn_code": "28369910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBONATES; PEROXOCARBONATES (PERCARBONATES); COMMERCIAL AMMONIUM\nCARBONATE CONTAINING AMMONIUM CARBAMATE - OTHER : - OTHER : MAGNESIUM\nCARBONATE",
+        "hsn_code": "28369920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBONATES; PEROXOCARBONATES (PERCARBONATES); COMMERCIAL AMMONIUM\nCARBONATE CONTAINING AMMONIUM CARBAMATE - OTHER : - OTHER : ALUMINIUM\nBICARBONATE",
+        "hsn_code": "28369930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBONATES; PEROXOCARBONATES (PERCARBONATES); COMMERCIAL AMMONIUM\nCARBONATE CONTAINING AMMONIUM CARBAMATE - OTHER : - OTHER : OTHER",
+        "hsn_code": "28369990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYANIDES, CYANIDE OXIDES AND COMPLEX CYANIDES - CYANIDES AND CYANIDE OXIDES : OF\nSODIUM",
+        "hsn_code": "28371100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYANIDES, CYANIDE OXIDES AND COMPLEX CYANIDES - CYANIDES AND CYANIDE OXIDES : -\nOTHER : POTASSIUM CYANIDE",
+        "hsn_code": "28371910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYANIDES, CYANIDE OXIDES AND COMPLEX CYANIDES - CYANIDES AND CYANIDE OXIDES : -\nOTHER : DOUBLE CYANIDE OF POTASSIUM AND SODIUM",
+        "hsn_code": "28371920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYANIDES, CYANIDE OXIDES AND COMPLEX CYANIDES - CYANIDES AND CYANIDE OXIDES : -\nOTHER : OTHER",
+        "hsn_code": "28371990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYANIDES, CYANIDE OXIDES AND COMPLEX CYANIDES - COMPLEX CYANIDES : AMMONIUM\nSULPHOCYANIDE",
+        "hsn_code": "28372010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYANIDES, CYANIDE OXIDES AND COMPLEX CYANIDES - COMPLEX CYANIDES : POTASSIUM\nFERRICYANIDE",
+        "hsn_code": "28372020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYANIDES, CYANIDE OXIDES AND COMPLEX CYANIDES - COMPLEX CYANIDES : POTASSIUM\nFERROCYANIDE",
+        "hsn_code": "28372030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYANIDES, CYANIDE OXIDES AND COMPLEX CYANIDES - COMPLEX CYANIDES : SODIUM\nFERROCYANIDE",
+        "hsn_code": "28372040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYANIDES, CYANIDE OXIDES AND COMPLEX CYANIDES - COMPLEX CYANIDES : SODIUM\nNITROPRUSSIDE (SODIUM NITROFERRICYANIDE)",
+        "hsn_code": "28372050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYANIDES, CYANIDE OXIDES AND COMPLEX CYANIDES - COMPLEX CYANIDES : OTHER",
+        "hsn_code": "28372090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FULMINATES, CYANATES AND THIOCYANATES - FULMINATES, CYANATES AND THIOCYANATES :\nFULMINATES",
+        "hsn_code": "28380010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FULMINATES, CYANATES AND THIOCYANATES - FULMINATES, CYANATES AND THIOCYANATES :\nCYANATES",
+        "hsn_code": "28380020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FULMINATES, CYANATES AND THIOCYANATES - FULMINATES, CYANATES AND THIOCYANATES :\nTHIOCYANATES",
+        "hsn_code": "28380030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SILICATES; COMMERCIAL ALKALI METAL SILICATES - OF SODIUM : SODIUM METASILICATES",
+        "hsn_code": "28391100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SILICATES; COMMERCIAL ALKALI METAL SILICATES - OF SODIUM : OTHER",
+        "hsn_code": "28391900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SILICATES; COMMERCIAL ALKALI METAL SILICATES - OF POTASSIUM",
+        "hsn_code": "28392000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SILICATES; COMMERCIAL ALKALI METAL SILICATES - OTHER : MAGNESIUM TRISILICATE",
+        "hsn_code": "28399010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SILICATES; COMMERCIAL ALKALI METAL SILICATES - OTHER : OTHER",
+        "hsn_code": "28399090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BORATES; PEROXOBORATES (PERBORATES) - DISODIUM TETRABORATE (REFINED BORAX) :\nANHYDROUS",
+        "hsn_code": "28401100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BORATES; PEROXOBORATES (PERBORATES) - DISODIUM TETRABORATE (REFINED BORAX) :\nOTHER",
+        "hsn_code": "28401900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BORATES; PEROXOBORATES (PERBORATES) - OTHER BORATES : MAGNESIUM BORATE",
+        "hsn_code": "28402010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BORATES; PEROXOBORATES (PERBORATES) - OTHER BORATES : OTHER",
+        "hsn_code": "28402090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BORATES; PEROXOBORATES (PERBORATES) PEROXOBORATES (PERBORATES)",
+        "hsn_code": "28403000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SALTS OF OXOMETALLIC OR PEROXOMETALLIC ACIDS - ALUMINATES : SODIUM ALUMINATE",
+        "hsn_code": "28411010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SALTS OF OXOMETALLIC OR PEROXOMETALLIC ACIDS - ALUMINATES : OTHER",
+        "hsn_code": "28411090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SALTS OF OXOMETALLIC OR PEROXOMETALLIC ACIDS - CHROMATES OF ZINC OR OF LEAD :\nCHROMATES OF ZINC",
+        "hsn_code": "28412010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SALTS OF OXOMETALLIC OR PEROXOMETALLIC ACIDS - CHROMATES OF ZINC OR OF LEAD :\nCHROMATES OF LEAD",
+        "hsn_code": "28412020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SALTS OF OXOMETALLIC OR PEROXOMETALLIC ACIDS SODIUM DICHROMATE",
+        "hsn_code": "28413000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SALTS OF OXOMETALLIC OR PEROXOMETALLIC ACIDS - OTHER CHROMATES AND DICHROMATES;\nPEROXOCHROMATES : SODIUM CHROMATE",
+        "hsn_code": "28415010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SALTS OF OXOMETALLIC OR PEROXOMETALLIC ACIDS - OTHER CHROMATES AND DICHROMATES;\nPEROXOCHROMATES : OTHER",
+        "hsn_code": "28415090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SALTS OF OXOMETALLIC OR PEROXOMETALLIC ACIDS - MANGANITES, MANGANATES AND\nPERMANGANATES : POTASSIUM PERMANGANATE",
+        "hsn_code": "28416100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SALTS OF OXOMETALLIC OR PEROXOMETALLIC ACIDS - MANGANITES, MANGANATES AND\nPERMANGANATES : OTHER",
+        "hsn_code": "28416900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SALTS OF OXOMETALLIC OR PEROXOMETALLIC ACIDS - MOLYBDATES : ALUMINIUM MOLYBDATE",
+        "hsn_code": "28417010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SALTS OF OXOMETALLIC OR PEROXOMETALLIC ACIDS - MOLYBDATES : SODIUM MOLYBDATE",
+        "hsn_code": "28417020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SALTS OF OXOMETALLIC OR PEROXOMETALLIC ACIDS - MOLYBDATES : OTHER",
+        "hsn_code": "28417090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SALTS OF OXOMETALLIC OR PEROXOMETALLIC ACIDS - TUNGSTATES (WOLFRAMATES) : SODIUM\nTUNGSTATE",
+        "hsn_code": "28418010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SALTS OF OXOMETALLIC OR PEROXOMETALLIC ACIDS - TUNGSTATES (WOLFRAMATES) :\nMAGNESIUM TUNGSTATE",
+        "hsn_code": "28418020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SALTS OF OXOMETALLIC OR PEROXOMETALLIC ACIDS - TUNGSTATES (WOLFRAMATES) : OTHER",
+        "hsn_code": "28418090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SALTS OF OXOMETALLIC OR PEROXOMETALLIC ACIDS - OTHER",
+        "hsn_code": "28419000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER SALTS OF INORGANIC ACIDS OR PEROXOACIDS, (INCLUDING ALUMINOSILICATES,\nWHETHER OR NOT CHEMICALLY DEFINED), OTHER THAN AZIDES -DOUBLE OR COMPLEX\nSILICATES, INCLUDING ALUMINOSILICATES, WHETHER OR NOT CHEMICALLY DEFINED",
+        "hsn_code": "28421000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER SALTS OF INORGANIC ACIDS OR PEROXOACIDS, (INCLUDING ALUMINOSILICATES,\nWHETHER OR NOT CHEMICALLY DEFINED), OTHER THAN AZIDES - OTHER : ARSENITES AND\nARSENATES",
+        "hsn_code": "28429010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER SALTS OF INORGANIC ACIDS OR PEROXOACIDS, (INCLUDING ALUMINOSILICATES,\nWHETHER OR NOT CHEMICALLY DEFINED), OTHER THAN AZIDES - OTHER : BICHROMATES AND\nDICHROMATES",
+        "hsn_code": "28429020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER SALTS OF INORGANIC ACIDS OR PEROXOACIDS, (INCLUDING ALUMINOSILICATES,\nWHETHER OR NOT CHEMICALLY DEFINED), OTHER THAN AZIDES - OTHER : OTHER",
+        "hsn_code": "28429090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COLLOIDAL PRECIOUS METALS; INORGANIC OR ORGANIC COMPOUNDS OF PRECIOUS METALS,\nWHETHER OR NOT CHEMICALLY DEFINED; AMALGAMS OF PRECIOUS METALS - COLLOIDAL\nPRECIOUS METALS : OF GOLD",
+        "hsn_code": "28431010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COLLOIDAL PRECIOUS METALS; INORGANIC OR ORGANIC COMPOUNDS OF PRECIOUS METALS,\nWHETHER OR NOT CHEMICALLY DEFINED; AMALGAMS OF PRECIOUS METALS - COLLOIDAL\nPRECIOUS METALS : OF SILVER",
+        "hsn_code": "28431020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COLLOIDAL PRECIOUS METALS; INORGANIC OR ORGANIC COMPOUNDS OF PRECIOUS METALS,\nWHETHER OR NOT CHEMICALLY DEFINED; AMALGAMS OF PRECIOUS METALS - COLLOIDAL\nPRECIOUS METALS : OTHER",
+        "hsn_code": "28431090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COLLOIDAL PRECIOUS METALS; INORGANIC OR ORGANIC COMPOUNDS OF PRECIOUS METALS,\nWHETHER OR NOT CHEMICALLY DEFINED; AMALGAMS OF PRECIOUS METALS - SILVER\nCOMPOUNDS : SILVER NITRATE",
+        "hsn_code": "28432100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COLLOIDAL PRECIOUS METALS; INORGANIC OR ORGANIC COMPOUNDS OF PRECIOUS METALS,\nWHETHER OR NOT CHEMICALLY DEFINED; AMALGAMS OF PRECIOUS METALS - SILVER\nCOMPOUNDS : OTHER",
+        "hsn_code": "28432900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COLLOIDAL PRECIOUS METALS; INORGANIC OR ORGANIC COMPOUNDS OF PRECIOUS METALS,\nWHETHER OR NOT CHEMICALLY DEFINED; AMALGAMS OF PRECIOUS METALS - GOLD\nCOMPOUNDS",
+        "hsn_code": "28433000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COLLOIDAL PRECIOUS METALS; INORGANIC OR ORGANIC COMPOUNDS OF PRECIOUS METALS,\nWHETHER OR NOT CHEMICALLY DEFINED; AMALGAMS OF PRECIOUS METALS - OTHER\nCOMPOUNDS; AMALGAMS : - OTHER COMPOUNDS : SODIUM AUROUS THIOSULPHATE",
+        "hsn_code": "28439011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COLLOIDAL PRECIOUS METALS; INORGANIC OR ORGANIC COMPOUNDS OF PRECIOUS METALS,\nWHETHER OR NOT CHEMICALLY DEFINED; AMALGAMS OF PRECIOUS METALS - OTHER\nCOMPOUNDS; AMALGAMS : - OTHER COMPOUNDS : NOBLE METAL SOLUTIONS OF PLATINUM,\nRHODIUM AND PALLADIUM",
+        "hsn_code": "28439012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COLLOIDAL PRECIOUS METALS; INORGANIC OR ORGANIC COMPOUNDS OF PRECIOUS METALS,\nWHETHER OR NOT CHEMICALLY DEFINED; AMALGAMS OF PRECIOUS METALS - OTHER\nCOMPOUNDS; AMALGAMS : - OTHER COMPOUNDS : OTHER",
+        "hsn_code": "28439019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COLLOIDAL PRECIOUS METALS; INORGANIC OR ORGANIC COMPOUNDS OF PRECIOUS METALS,\nWHETHER OR NOT CHEMICALLY DEFINED; AMALGAMS OF PRECIOUS METALS - OTHER\nCOMPOUNDS; AMALGAMS : AMALGAMS",
+        "hsn_code": "28439020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RADIOACTIVE CHEMICAL ELEMENTS AND RADIOACTIVE ISOTOPES (INCLUDING THE FISSILE OR\nFERTILE CHEMICAL ELEMENTS AND ISOTOPES) AND THEIR COMPOUNDS; MIXTURES AND\nRESIDUES CONTAINING THESE PRODUCTS - NATURAL URANIUM AND ITS COMPOUNDS; ALLOYS,\nDISPERSIONS (INCLUDING CERMETS), CERAMIC PRODUCTS AND MIXTURES CONTAINING\nNATURAL URANIUM OR NATURAL URANIUM COMPOUNDS",
+        "hsn_code": "28441000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RADIOACTIVE CHEMICAL ELEMENTS AND RADIOACTIVE ISOTOPES (INCLUDING THE FISSILE OR\nFERTILE CHEMICAL ELEMENTS AND ISOTOPES) AND THEIR COMPOUNDS; MIXTURES AND\nRESIDUES CONTAINING THESE PRODUCTS - URANIUM ENRICHED IN U235 AND ITS COMPOUNDS;\nPLUTONIUM AND ITS COMPOUNDS; ALLOYS, DISPERSIONS (INCLUDING CERMETS), CERAMIC\nPRODUCTS AND MIXTURES CONTAINING URANIUM ENRICHED IN U235, PLUTONIUM OR\nCOMPOUNDS OF THESE PRODUCTS",
+        "hsn_code": "28442000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RADIOACTIVE CHEMICAL ELEMENTS AND RADIOACTIVE ISOTOPES (INCLUDING THE FISSILE OR\nFERTILE CHEMICAL ELEMENTS AND ISOTOPES) AND THEIR COMPOUNDS; MIXTURES AND\nRESIDUES CONTAINING THESE PRODUCTS - URANIUM DEPLETED IN U235 AND ITS COMPOUNDS;\nTHORIUM AND ITS COMPOUNDS; ALLOYS, DISPERSIONS (INCLUDING CERMETS), CERAMIC\nPRODUCTS AND MIXTURES CONTAINING URANIUM DEPLETED IN U235, THORIUM OR\nCOMPOUNDS OF THESE PRODUCTS : URANIUM DEPLETED IN U235 AND THORIUM AND THEIR\nALLOYS, UNWROUGHT OR WROUGHT AND COMPOUNDS THEREOF",
+        "hsn_code": "28443010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RADIOACTIVE CHEMICAL ELEMENTS AND RADIOACTIVE ISOTOPES (INCLUDING THE FISSILE OR\nFERTILE CHEMICAL ELEMENTS AND ISOTOPES) AND THEIR COMPOUNDS; MIXTURES AND\nRESIDUES CONTAINING THESE PRODUCTS - URANIUM DEPLETED IN U235 AND ITS COMPOUNDS;\nTHORIUM AND ITS COMPOUNDS; ALLOYS, DISPERSIONS (INCLUDING CERMETS), CERAMIC\nPRODUCTS AND MIXTURES CONTAINING URANIUM DEPLETED IN U235, THORIUM OR\nCOMPOUNDS OF THESE PRODUCTS : - COMPOUNDS OF THORIUM OR OF URANIUM DEPLETED IN\nU235 : THORIUM OXIDE",
+        "hsn_code": "28443021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RADIOACTIVE CHEMICAL ELEMENTS AND RADIOACTIVE ISOTOPES (INCLUDING THE FISSILE OR\nFERTILE CHEMICAL ELEMENTS AND ISOTOPES) AND THEIR COMPOUNDS; MIXTURES AND\nRESIDUES CONTAINING THESE PRODUCTS - URANIUM DEPLETED IN U235 AND ITS COMPOUNDS;\nTHORIUM AND ITS COMPOUNDS; ALLOYS, DISPERSIONS (INCLUDING CERMETS), CERAMIC\nPRODUCTS AND MIXTURES CONTAINING URANIUM DEPLETED IN U235, THORIUM OR\nCOMPOUNDS OF THESE PRODUCTS : - COMPOUNDS OF THORIUM OR OF URANIUM DEPLETED IN\nU235 : THORIUM HYDROXIDE",
+        "hsn_code": "28443022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RADIOACTIVE CHEMICAL ELEMENTS AND RADIOACTIVE ISOTOPES (INCLUDING THE FISSILE OR\nFERTILE CHEMICAL ELEMENTS AND ISOTOPES) AND THEIR COMPOUNDS; MIXTURES AND\nRESIDUES CONTAINING THESE PRODUCTS - URANIUM DEPLETED IN U235 AND ITS COMPOUNDS;\nTHORIUM AND ITS COMPOUNDS; ALLOYS, DISPERSIONS (INCLUDING CERMETS), CERAMIC\nPRODUCTS AND MIXTURES CONTAINING URANIUM DEPLETED IN U235, THORIUM OR\nCOMPOUNDS OF THESE PRODUCTS : - COMPOUNDS OF THORIUM OR OF URANIUM DEPLETED IN\nU235 : THORIUM NITRATE",
+        "hsn_code": "28443023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RADIOACTIVE CHEMICAL ELEMENTS AND RADIOACTIVE ISOTOPES (INCLUDING THE FISSILE OR\nFERTILE CHEMICAL ELEMENTS AND ISOTOPES) AND THEIR COMPOUNDS; MIXTURES AND\nRESIDUES CONTAINING THESE PRODUCTS - URANIUM DEPLETED IN U235 AND ITS COMPOUNDS;\nTHORIUM AND ITS COMPOUNDS; ALLOYS, DISPERSIONS (INCLUDING CERMETS), CERAMIC\nPRODUCTS AND MIXTURES CONTAINING URANIUM DEPLETED IN U235, THORIUM OR\nCOMPOUNDS OF THESE PRODUCTS : - COMPOUNDS OF THORIUM OR OF URANIUM DEPLETED IN\nU235 : OTHER",
+        "hsn_code": "28443029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RADIOACTIVE CHEMICAL ELEMENTS AND RADIOACTIVE ISOTOPES (INCLUDING THE FISSILE OR\nFERTILE CHEMICAL ELEMENTS AND ISOTOPES) AND THEIR COMPOUNDS; MIXTURES AND\nRESIDUES CONTAINING THESE PRODUCTS - URANIUM DEPLETED IN U235 AND ITS COMPOUNDS;\nTHORIUM AND ITS COMPOUNDS; ALLOYS, DISPERSIONS (INCLUDING CERMETS), CERAMIC\nPRODUCTS AND MIXTURES CONTAINING URANIUM DEPLETED IN U235, THORIUM OR\nCOMPOUNDS OF THESE PRODUCTS : WASTE AND SCRAP OF URANIUM DEPLETED IN U235 OR OF\nTHORIUM",
+        "hsn_code": "28443030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RADIOACTIVE CHEMICAL ELEMENTS AND RADIOACTIVE ISOTOPES (INCLUDING THE FISSILE OR\nFERTILE CHEMICAL ELEMENTS AND ISOTOPES) AND THEIR COMPOUNDS; MIXTURES AND\nRESIDUES CONTAINING THESE PRODUCTS - URANIUM DEPLETED IN U235 AND ITS COMPOUNDS;\nTHORIUM AND ITS COMPOUNDS; ALLOYS, DISPERSIONS (INCLUDING CERMETS), CERAMIC\nPRODUCTS AND MIXTURES CONTAINING URANIUM DEPLETED IN U235, THORIUM OR\nCOMPOUNDS OF THESE PRODUCTS : OTHER",
+        "hsn_code": "28443090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RADIOACTIVE CHEMICAL ELEMENTS AND RADIOACTIVE ISOTOPES (INCLUDING THE FISSILE OR\nFERTILE CHEMICAL ELEMENTS AND ISOTOPES) AND THEIR COMPOUNDS; MIXTURES AND\nRESIDUES CONTAINING THESE PRODUCTS - RADIOACTIVE ELEMENTS AND ISOTOPES AND\nCOMPOUNDS OTHER THAN THOSE OF SUBHEADINGS 2844 10, 2844 20 OR 2844 30; ALLOYS,\nDISPERSIONS (INCLUDING CERMETS), CERAMIC PRODUCTS AND MIXTURES CONTAINING THESE\nELEMENTS, ISOTOPES OR COMPOUNDS; RADIOACTIVE RESIDUES",
+        "hsn_code": "28444000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RADIOACTIVE CHEMICAL ELEMENTS AND RADIOACTIVE ISOTOPES (INCLUDING THE FISSILE OR\nFERTILE CHEMICAL ELEMENTS AND ISOTOPES) AND THEIR COMPOUNDS; MIXTURES AND\nRESIDUES CONTAINING THESE PRODUCTS - SPENT (IRRADIATED) FUEL ELEMENTS (CARTRIDGES)\nOF NUCLEAR REACTORS",
+        "hsn_code": "28445000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDS, INORGANIC OR ORGANIC, OF RARE-EARTH METALS, OF YTTRIUM OR OF\nSCANDIUM OR OF MIXTURES OF THESE METALS - CERIUM COMPOUNDS : CERIUM OXIDES",
+        "hsn_code": "28461010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDS, INORGANIC OR ORGANIC, OF RARE-EARTH METALS, OF YTTRIUM OR OF\nSCANDIUM OR OF MIXTURES OF THESE METALS - CERIUM COMPOUNDS : OTHER",
+        "hsn_code": "28461090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDS, INORGANIC OR ORGANIC, OF RARE-EARTH METALS, OF YTTRIUM OR OF\nSCANDIUM OR OF MIXTURES OF THESE METALS - OTHER : RARE-EARTH OXIDES NOT ELSEWHERE\nINCLUDED OR SPECIFIED",
+        "hsn_code": "28469010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDS, INORGANIC OR ORGANIC, OF RARE-EARTH METALS, OF YTTRIUM OR OF\nSCANDIUM OR OF MIXTURES OF THESE METALS - OTHER : RARE-EARTH FLUORIDES NOT\nELSEWHERE INCLUDED OR SPECIFIED",
+        "hsn_code": "28469020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDS, INORGANIC OR ORGANIC, OF RARE-EARTH METALS, OF YTTRIUM OR OF\nSCANDIUM OR OF MIXTURES OF THESE METALS - OTHER : RARE-EARTH CHLORIDES NOT\nELSEWHERE INCLUDED OR SPECIFIED",
+        "hsn_code": "28469030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDS, INORGANIC OR ORGANIC, OF RARE-EARTH METALS, OF YTTRIUM OR OF\nSCANDIUM OR OF MIXTURES OF THESE METALS - OTHER : OTHER",
+        "hsn_code": "28469090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHIDES, WHETHER OR NOT CHEMICALLY DEFINED, EXCLUDING FERROPHOSPHORUS -\nPHOSPHIDES, WHETHER OR NOT CHEMICALLY DEFINED, EXCLUDING FERROPHOSPHORUS : OF\nCOPPER (PHOSPHOR COPPER), CONTAINING MORE THAN 15% BY WEIGHT OF PHOSPHORUS",
+        "hsn_code": "28480010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHIDES, WHETHER OR NOT CHEMICALLY DEFINED, EXCLUDING FERROPHOSPHORUS -\nPHOSPHIDES, WHETHER OR NOT CHEMICALLY DEFINED, EXCLUDING FERROPHOSPHORUS : OF\nZINC",
+        "hsn_code": "28480020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHIDES, WHETHER OR NOT CHEMICALLY DEFINED, EXCLUDING FERROPHOSPHORUS -\nPHOSPHIDES, WHETHER OR NOT CHEMICALLY DEFINED, EXCLUDING FERROPHOSPHORUS :\nOTHER",
+        "hsn_code": "28480090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBIDES, WHETHER OR NOT CHEMICALLY DEFINED - OF CALCIUM",
+        "hsn_code": "28491000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBIDES, WHETHER OR NOT CHEMICALLY DEFINED - OF SILICON : CARBORUNDUM",
+        "hsn_code": "28492010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBIDES, WHETHER OR NOT CHEMICALLY DEFINED - OF SILICON : OTHER",
+        "hsn_code": "28492090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBIDES, WHETHER OR NOT CHEMICALLY DEFINED - OTHER :BORON CARBIDE",
+        "hsn_code": "28499010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBIDES, WHETHER OR NOT CHEMICALLY DEFINED - OTHER :TUNGSTEN CARBIDE",
+        "hsn_code": "28499020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBIDES, WHETHER OR NOT CHEMICALLY DEFINED - OTHER :OTHER",
+        "hsn_code": "28499090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRIDES, NITRIDES, AZIDES, SILICIDES AND BORIDES, WHETHER OR NOT CHEMICALLY DEFINED,\nOTHER THAN COMPOUNDS WHICH ARE ALSO CARBIDES OF HEADING 2849 - HYDRIDES,\nNITRIDES, AZIDES, SILICIDES AND BORIDES, WHETHER OR NOT CHEMICALLY DEFINED, OTHER\nTHAN COMPOUNDS WHICH ARE ALSO CARBIDES OF HEADING 2849 : HYDRIDES",
+        "hsn_code": "28500010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRIDES, NITRIDES, AZIDES, SILICIDES AND BORIDES, WHETHER OR NOT CHEMICALLY DEFINED,\nOTHER THAN COMPOUNDS WHICH ARE ALSO CARBIDES OF HEADING 2849 - HYDRIDES,\nNITRIDES, AZIDES, SILICIDES AND BORIDES, WHETHER OR NOT CHEMICALLY DEFINED, OTHER\nTHAN COMPOUNDS WHICH ARE ALSO CARBIDES OF HEADING 2849 : NITRIDES",
+        "hsn_code": "28500020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRIDES, NITRIDES, AZIDES, SILICIDES AND BORIDES, WHETHER OR NOT CHEMICALLY DEFINED,\nOTHER THAN COMPOUNDS WHICH ARE ALSO CARBIDES OF HEADING 2849 - HYDRIDES,\nNITRIDES, AZIDES, SILICIDES AND BORIDES, WHETHER OR NOT CHEMICALLY DEFINED, OTHER\nTHAN COMPOUNDS WHICH ARE ALSO CARBIDES OF HEADING 2849 : AZIDES",
+        "hsn_code": "28500030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRIDES, NITRIDES, AZIDES, SILICIDES AND BORIDES, WHETHER OR NOT CHEMICALLY DEFINED,\nOTHER THAN COMPOUNDS WHICH ARE ALSO CARBIDES OF HEADING 2849 - HYDRIDES,\nNITRIDES, AZIDES, SILICIDES AND BORIDES, WHETHER OR NOT CHEMICALLY DEFINED, OTHER\nTHAN COMPOUNDS WHICH ARE ALSO CARBIDES OF HEADING 2849 : - SILICIDES : OF CALCIUM",
+        "hsn_code": "28500041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRIDES, NITRIDES, AZIDES, SILICIDES AND BORIDES, WHETHER OR NOT CHEMICALLY DEFINED,\nOTHER THAN COMPOUNDS WHICH ARE ALSO CARBIDES OF HEADING 2849 - HYDRIDES,\nNITRIDES, AZIDES, SILICIDES AND BORIDES, WHETHER OR NOT CHEMICALLY DEFINED, OTHER\nTHAN COMPOUNDS WHICH ARE ALSO CARBIDES OF HEADING 2849 : - SILICIDES : OTHER",
+        "hsn_code": "28500049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRIDES, NITRIDES, AZIDES, SILICIDES AND BORIDES, WHETHER OR NOT CHEMICALLY DEFINED,\nOTHER THAN COMPOUNDS WHICH ARE ALSO CARBIDES OF HEADING 2849 - HYDRIDES,\nNITRIDES, AZIDES, SILICIDES AND BORIDES, WHETHER OR NOT CHEMICALLY DEFINED, OTHER\nTHAN COMPOUNDS WHICH ARE ALSO CARBIDES OF HEADING 2849 : BORIDES",
+        "hsn_code": "28500050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER\nOF SIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS - OTHER\nINORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER OF\nSIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS : DISTILLED OR\nCONDUCTIVITY WATER AND WATER OF SIMILAR PURITY",
+        "hsn_code": "28510010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER\nOF SIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS - OTHER\nINORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER OF\nSIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS : LIQUID AIR,\nWHETHER OR NOT ANY FRACTION OF RARE GASES HAS BEEN REMOVED",
+        "hsn_code": "28510020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER\nOF SIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS - OTHER\nINORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER OF\nSIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS : COMPRESSED\nAIR",
+        "hsn_code": "28510030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER\nOF SIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS - OTHER\nINORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER OF\nSIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS : AMALGAMS,\nOTHER THAN OF PRECIOUS METALS",
+        "hsn_code": "28510040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER\nOF SIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS - OTHER\nINORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER OF\nSIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS : OTHER",
+        "hsn_code": "28510090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYNOGEN CHLORIDE",
+        "hsn_code": "28510091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC COMPOUNDS (INCLUDING DISTILLED OR CONDUCTIVITY WATER AND WATER\nOF SIMILAR PURITY); LIQUID AIR (WHETHER OR NOT RARE GASES HAVE BEEN REMOVED);\nCOMPRESSED AIR; AMALGAMS, OTHER THAN AMALGAMS OF PRECIOUS METALS: OTHER",
+        "hsn_code": "28510099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INORGANIC OR ORGANIC COMPOUNDS OF MERCURY, WHETHER OR NOT CHEMICALLY DEFINED,\nEXCLUDING AMALGAMS;-CHEMICALLY DEFINED",
+        "hsn_code": "28521000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "INORGANIC OR ORGANIC COMPOUNDS OF MERCURY, WHETHER OR NOT CHEMICALLY DEFINED,\nEXCLUDING AMALGAMS;-OTHER",
+        "hsn_code": "28529000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Chemicals & Allied Industries",
+        "hsn_code": "28-38",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FLUORINE, CHLORINE, BROMINE AND IODINE",
+        "hsn_code": "2801",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHUR, SUBLIMED OR PRECIPITATED; COLLOIDAL SULPHUR",
+        "hsn_code": "2802",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBON (CARBON BLACKS AND OTHER FORMS OF CARBON NOT ELSEWHERE SPECIFIED OR INCLUDED)",
+        "hsn_code": "2803",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN, RARE GASES AND OTHER NON-METALS",
+        "hsn_code": "2804",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ALKALI OR ALKALINE-EARTH METALS; RARE-EARTH METALS, SCANDIUM AND YTTRIUM, WHETHER OR NOT INTERMIXED OR INTERALLOYED; MERCURY",
+        "hsn_code": "2805",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROGEN CHLORIDE (HYDROCHLORIC ACID); CHLOROSULPHURIC ACID",
+        "hsn_code": "2806",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHURIC ACID; OLEUM",
+        "hsn_code": "2807",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NITRIC ACID; SULPHONITRIC ACIDS",
+        "hsn_code": "2808",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OXIDES OF BORON; BORIC ACIDS",
+        "hsn_code": "2810",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER INORGANIC ACIDS AND OTHER IN-ORGANIC OXYGEN COMPOUNDS OF NON-METALS",
+        "hsn_code": "2811",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HALIDES AND HALIDE OXIDES OF NON-METALS",
+        "hsn_code": "2812",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHIDES OF NON-METALS; COMMERCIAL PHOSPHORUS TRISULPHIDE",
+        "hsn_code": "2813",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "AMMONIA, ANHYDROUS OR IN AQUEOUS SOLUTION",
+        "hsn_code": "2814",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SODIUM HYDROXIDE (CAUSTIC SODA); POTASSIUM HYDROXIDE (CAUSTIC POTASH); PEROXIDES OF SODIUM OR POTASSIUM",
+        "hsn_code": "2815",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDROXIDE AND PEROXIDE OF MAGNESIUM; OXIDES, HYDROXIDES AND PEROXIDES, OF STRONTIUM OR BARIUM",
+        "hsn_code": "2816",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ZINC OXIDE; ZINC PEROXIDE",
+        "hsn_code": "2817",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ARTIFICIAL CORUNDUM, WHETHER OR NOT CHEMICALLY DEFINED; ALUMINIUM OXIDE; ALUMINIUM HYDROXIDE",
+        "hsn_code": "2818",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHROMIUM OXIDES AND HYDROXIDES",
+        "hsn_code": "2819",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "MANGANESE OXIDES",
+        "hsn_code": "2820",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "IRON OXIDES AND HYDROXIDES; EARTH COLOURS CONTAINING 70% OR MORE BY WEIGHT OF COMBINED IRON EVALUATED AS FE2O3",
+        "hsn_code": "2821",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COBALT OXIDES AND HYDROXIDES; COMMERCIAL COBALT OXIDES",
+        "hsn_code": "2822",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TITANIUM OXIDES",
+        "hsn_code": "2823",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "LEAD OXIDES; RED LEAD AND ORANGE LEAD",
+        "hsn_code": "2824",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRAZINE AND HYDROXYLAMINE AND THEIR INORGANIC SALTS; OTHER INORGANIC BASES; OTHER METAL OXIDES, HYDROXIDES AND PEROXIDES",
+        "hsn_code": "2825",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "FLUORIDES; FLUOROSILICATES, FLUOROALUMINATES AND OTHER COMPLEX FLUORINE SALTS",
+        "hsn_code": "2826",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORIDES, CHLORIDE OXIDES AND CHLORIDE HYDROXIDES; BROMIDES AND BROMIDE OXIDES; IODIDES AND IODIDE OXIDES",
+        "hsn_code": "2827",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYPOCHLORITES; COMMERCIAL CALCIUM HYPOCHLORITES; CHLORITES; HYPOBROMITES",
+        "hsn_code": "2828",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CHLORATES AND PERCHLORATES; BROMATES AND PERBROMATES; IODATES AND PERIODATES",
+        "hsn_code": "2829",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHIDES; POLYSULPHIDES WHETHER OR NOT CHEMICALLY DEFINED",
+        "hsn_code": "2830",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "DITHIONITES AND SULPHOXYLATES",
+        "hsn_code": "2831",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHITES; THIOSULPHATES",
+        "hsn_code": "2832",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SULPHATES; ALUMS; PEROXOSULPHATES (PERSULPHATES)",
+        "hsn_code": "2833",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NITRITES; NITRATES",
+        "hsn_code": "2834",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CARBONATES; PEROXOCARBONATES (PERCARBONATES); COMMERCIAL AMMONIUM CARBONATE CONTAINING AMMONIUM CARBAMATE",
+        "hsn_code": "2836",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CYANIDES, CYANIDE OXIDES AND COMPLEX CYANIDES",
+        "hsn_code": "2837",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Silicates; commercial alkali metal silicates",
+        "hsn_code": "2839",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "BORATES; PEROXOBORATES (PERBORATES)",
+        "hsn_code": "2840",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SALTS OF OXOMETALLIC OR PEROXOMETALLIC ACIDS",
+        "hsn_code": "2841",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "OTHER SALTS OF INORGANIC ACIDS OR PEROXOACIDS,(INCLUDING ALUMINOSILICATES, WHETHER OR NOT CHEMICALLY DEFINED), OTHER THAN AZIDES",
+        "hsn_code": "2842",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COLLOIDAL PRECIOUS METALS; INORGANIC OR ORGANIC COMPOUNDS OF PRECIOUS METALS, WHETHER OR NOT CHEMICALLY DEFINED; AMALGAMS OF PRECIOUS METALS",
+        "hsn_code": "2843",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "RADIOACTIVE CHEMICAL ELEMENTS AND RADIOACTIVE ISOTOPES (INCLUDING THE FISSILE OR FERTILE CHEMICAL ELEMENTS AND ISOTOPES) AND THEIR COMPOUNDS; MIXTURES AND RESIDUES CONTAINING THESE PRODUCTS",
+        "hsn_code": "2844",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "COMPOUNDS, INORGANIC OR ORGANIC, OF RARE-EARTH METALS, OF YTTRIUM OR OF SCANDIUM OR OF MIXTURES OF THESE METALS",
+        "hsn_code": "2846",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHIDES, WHETHER OR NOT CHEMICALLY DEFINED, EXCLUDING FERROPHOSPHORUS",
+        "hsn_code": "2848",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SILICATES; COMMERCIAL ALKALI METAL SILICATES",
+        "hsn_code": "2849",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "HYDRIDES, NITRIDES, AZIDES, SILICIDES AND BORIDES, WHETHER OR NOT CHEMICALLY DEFINED, OTHER THAN COMPOUNDS WHICH ARE ALSO CARBIDES OF HEADING 2849",
+        "hsn_code": "2850",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Inorganic or organic compounds of mercury, whether or not chemically defined, excluding amalgams",
+        "hsn_code": "2852",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOSPHINATES (HYPOPHOSPHITES), PHOSPHONATES (PHOSPHITES) AND PHOSPHATES; POLYPHOSPHATES WHETHER OR NOT CHEMICALLY DEFINED",
+        "hsn_code": "2835",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORTHOPAEDIC APPLIANCES, INCLUDING CRUTCHES, SURGICAL BELTS AND TRUSES; SPLINTS AND OTHER FRACTURE APPLIANCES; ARTIFICIAL PARTS OF THE BODY; HEARING AIDS AND OTHER APPLIANCES WHICH ARE WORN OR CARRIED, OR IMPLANTED IN THE BODY, TO COMPENSATE FOR A DEFECT OR DISABILITY",
+        "hsn_code": "9021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "Miscellaneous",
+        "hsn_code": "90-97",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORTHOPAEDIC APPLIANCES, INCLUDING CRUTCHES, SURGICAL BELTS AND TRUSSES; SPLINTS\nAND OTHER FRACTURE APPLIANCES; ARTIFICIAL PARTS OF THE BODY; HEARING AIDS AND OTHER\nAPPLIANCES WHICH ARE WORN OR CARRIED, OR IMPLANTED IN THE BODY, TO COMPENSATE\nFOR A DEFECT OR DISABILITY - OTHER: OTHER",
+        "hsn_code": "90219090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORTHOPAEDIC APPLIANCES, INCLUDING CRUTCHES, SURGICAL BELTS AND TRUSSES; SPLINTS\nAND OTHER FRACTURE APPLIANCES; ARTIFICIAL PARTS OF THE BODY; HEARING AIDS AND OTHER\nAPPLIANCES WHICH ARE WORN OR CARRIED, OR IMPLANTED IN THE BODY, TO COMPENSATE\nFOR A DEFECT OR DISABILITY - OTHER: PARTS AND ACCESSORIES OF HEARING AIDS",
+        "hsn_code": "90219010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORTHOPAEDIC APPLIANCES, INCLUDING CRUTCHES, SURGICAL BELTS AND TRUSSES; SPLINTS\nAND OTHER FRACTURE APPLIANCES; ARTIFICIAL PARTS OF THE BODY; HEARING AIDS AND OTHER\nAPPLIANCES WHICH ARE WORN OR CARRIED, OR IMPLANTED IN THE BODY, TO COMPENSATE\nFOR A DEFECT OR DISABILITY PACEMAKERS FOR STIMULATING HEART MUSCLES, EXCLUDING\nPARTS AND ACCESSORIES",
+        "hsn_code": "90215000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORTHOPAEDIC APPLIANCES, INCLUDING CRUTCHES, SURGICAL BELTS AND TRUSSES; SPLINTS\nAND OTHER FRACTURE APPLIANCES; ARTIFICIAL PARTS OF THE BODY; HEARING AIDS AND OTHER\nAPPLIANCES WHICH ARE WORN OR CARRIED, OR IMPLANTED IN THE BODY, TO COMPENSATE\nFOR A DEFECT OR DISABILITY - HEARING AIDS, EXCLUDING PARTS AND ACCESSORIES: OTHER",
+        "hsn_code": "90214090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORTHOPAEDIC APPLIANCES, INCLUDING CRUTCHES, SURGICAL BELTS AND TRUSSES; SPLINTS\nAND OTHER FRACTURE APPLIANCES; ARTIFICIAL PARTS OF THE BODY; HEARING AIDS AND OTHER\nAPPLIANCES WHICH ARE WORN OR CARRIED, OR IMPLANTED IN THE BODY, TO COMPENSATE\nFOR A DEFECT OR DISABILITY - HEARING AIDS, EXCLUDING PARTS AND ACCESSORIES:\nFREQUENCY MODULATED HEARING AID SYSTEM USED FOR HEARING BY HANDICAPPED PERSONS\nIN GROUP SITUATION",
+        "hsn_code": "90214010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORTHOPAEDIC APPLIANCES, INCLUDING CRUTCHES, SURGICAL BELTS AND TRUSSES; SPLINTS\nAND OTHER FRACTURE APPLIANCES; ARTIFICIAL PARTS OF THE BODY; HEARING AIDS AND OTHER\nAPPLIANCES WHICH ARE WORN OR CARRIED, OR IMPLANTED IN THE BODY, TO COMPENSATE\nFOR A DEFECT OR DISABILITY OTHER ARTIFICIAL PARTS OF THE BODY : OTHER",
+        "hsn_code": "90213900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORTHOPAEDIC APPLIANCES, INCLUDING CRUTCHES, SURGICAL BELTS AND TRUSSES; SPLINTS\nAND OTHER FRACTURE APPLIANCES; ARTIFICIAL PARTS OF THE BODY; HEARING AIDS AND OTHER\nAPPLIANCES WHICH ARE WORN OR CARRIED, OR IMPLANTED IN THE BODY, TO COMPENSATE\nFOR A DEFECT OR DISABILITY OTHER ARTIFICIAL PARTS OF THE BODY : ARTIFICIAL JOINTS",
+        "hsn_code": "90213100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORTHOPAEDIC APPLIANCES, INCLUDING CRUTCHES, SURGICAL BELTS AND TRUSSES; SPLINTS\nAND OTHER FRACTURE APPLIANCES; ARTIFICIAL PARTS OF THE BODY; HEARING AIDS AND OTHER\nAPPLIANCES WHICH ARE WORN OR CARRIED, OR IMPLANTED IN THE BODY, TO COMPENSATE\nFOR A DEFECT OR DISABILITY - ARTIFICIAL TEETH AND DENTAL FITTINGS: OTHER",
+        "hsn_code": "90212900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORTHOPAEDIC APPLIANCES, INCLUDING CRUTCHES, SURGICAL BELTS AND TRUSSES; SPLINTS\nAND OTHER FRACTURE APPLIANCES; ARTIFICIAL PARTS OF THE BODY; HEARING AIDS AND OTHER\nAPPLIANCES WHICH ARE WORN OR CARRIED, OR IMPLANTED IN THE BODY, TO COMPENSATE\nFOR A DEFECT OR DISABILITY - ARTIFICIAL TEETH AND DENTAL FITTINGS: ARTIFICIAL TEETH",
+        "hsn_code": "90212100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "ORTHOPAEDIC APPLIANCES, INCLUDING CRUTCHES, SURGICAL BELTS AND TRUSSES; SPLINTS\nAND OTHER FRACTURE APPLIANCES; ARTIFICIAL PARTS OF THE BODY; HEARING AIDS AND OTHER\nAPPLIANCES WHICH ARE WORN OR CARRIED, OR IMPLANTED IN THE BODY, TO COMPENSATE\nFOR A DEFECT OR DISABILITY ORTHOPEAEDIC OR FRACTURE APPLIANCES",
+        "hsn_code": "90211000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOTOCOPYING APPARATUS INCORPORATING AN OPTICAL SYSTEM OR OF THE CONTACT TYPE\nAND THERMO-COPYING APPARATUS - PARTS AND ACCESSORIES: OTHER",
+        "hsn_code": "90099900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOTOCOPYING APPARATUS INCORPORATING AN OPTICAL SYSTEM OR OF THE CONTACT TYPE\nAND THERMO-COPYING APPARATUS - PARTS AND ACCESSORIES: SORTERS",
+        "hsn_code": "90099300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOTOCOPYING APPARATUS INCORPORATING AN OPTICAL SYSTEM OR OF THE CONTACT TYPE\nAND THERMO-COPYING APPARATUS - PARTS AND ACCESSORIES: PAPER FEEDERS",
+        "hsn_code": "90099200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOTOCOPYING APPARATUS INCORPORATING AN OPTICAL SYSTEM OR OF THE CONTACT TYPE\nAND THERMO-COPYING APPARATUS - PARTS AND ACCESSORIES: AUTOMATIC DOCUMENT\nFEEDERS",
+        "hsn_code": "90099100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOTOCOPYING APPARATUS INCORPORATING AN OPTICAL SYSTEM OR OF THE CONTACT TYPE\nAND THERMO-COPYING APPARATUS THERMO-COPYING APPARATUS",
+        "hsn_code": "90093000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOTOCOPYING APPARATUS INCORPORATING AN OPTICAL SYSTEM OR OF THE CONTACT TYPE\nAND THERMO-COPYING APPARATUS - OTHER PHOTOCOPYING APPARATUS: OF THE CONTACT\nTYPE",
+        "hsn_code": "90092200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOTOCOPYING APPARATUS INCORPORATING AN OPTICAL SYSTEM OR OF THE CONTACT TYPE\nAND THERMO-COPYING APPARATUS - OTHER PHOTOCOPYING APPARATUS: INCORPORATING AN\nOPTICAL SYSTEM",
+        "hsn_code": "90092100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOTOCOPYING APPARATUS INCORPORATING AN OPTICAL SYSTEM OR OF THE CONTACT TYPE\nAND THERMO-COPYING APPARATUS - ELECTROSTATIC PHOTOCOPYING APPARATUS: OPERATING\nBY REPRODUCING THE ORIGINAL IMAGE VIA AN INTERMEDIATE ONTO THE COPY (INDIRECT\nPROCESS)",
+        "hsn_code": "90091200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PHOTOCOPYING APPARATUS INCORPORATING AN OPTICAL SYSTEM OR OF THE CONTACT TYPE\nAND THERMO-COPYING APPARATUS - ELECTROSTATIC PHOTOCOPYING APPARATUS: OPERATING\nBY REPRODUCING THE ORIGINAL IMAGE DIRECTLY ONTO THE COPY (DIRECT PROCESS)",
+        "hsn_code": "90091100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEEDS OF ANISE,BADIAN,FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES-SEEDS\nOF ANISE, BADIAN, CARAWAY OR FENNEL; JUNIPER BERRIES--CRUSHED OR GROUND---JUNIPER\nBERRIES",
+        "hsn_code": "9096240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEEDS OF ANISE,BADIAN,FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES-SEEDS\nOF ANISE, BADIAN, CARAWAY OR FENNEL; JUNIPER BERRIES--CRUSHED OR GROUND---CARAWAY\nOR FENNEL",
+        "hsn_code": "9096230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEEDS OF ANISE,BADIAN,FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES-SEEDS\nOF ANISE, BADIAN, CARAWAY OR FENNEL; JUNIPER BERRIES--CRUSHED OR GROUND---BADIAN",
+        "hsn_code": "9096220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEEDS OF ANISE,BADIAN,FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES-SEEDS\nOF ANISE, BADIAN, CARAWAY OR FENNEL; JUNIPER BERRIES--CRUSHED OR GROUND---ANISE",
+        "hsn_code": "9096210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEEDS OF ANISE,BADIAN,FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES-SEEDS\nOF ANISE, BADIAN, CARAWAY OR FENNEL; JUNIPER BERRIES--NEITHER CRUSHED NOR GROUND---\nJUNIPER BERRIES----OTHER",
+        "hsn_code": "9096149",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEEDS OF ANISE,BADIAN,FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES-SEEDS\nOF ANISE, BADIAN, CARAWAY OR FENNEL; JUNIPER BERRIES--NEITHER CRUSHED NOR GROUND---\nJUNIPER BERRIES----OF SEED QUALITY",
+        "hsn_code": "9096141",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEEDS OF ANISE,BADIAN,FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES-SEEDS\nOF ANISE, BADIAN, CARAWAY OR FENNEL; JUNIPER BERRIES--NEITHER CRUSHED NOR GROUND---\nSEEDS OF CARAWAY OR FENNEL----OTHER",
+        "hsn_code": "9096139",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEEDS OF ANISE,BADIAN,FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES-SEEDS\nOF ANISE,BADIAN, CARAWAY OR FENNEL;JUNIPER BERRIES--NEITHER CRUSHED NOR GROUND---\nSEEDS OF CARAWAY OR FENNEL ---- OF SEED QUALITY",
+        "hsn_code": "9096131",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEEDS OF ANISE,BADIAN,FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES-SEEDS\nOF ANISE, BADIAN, CARAWAY OR FENNEL; JUNIPER BERRIES--NEITHER CRUSHED NOR GROUND---\nSEEDS OF BADIAN----OTHER",
+        "hsn_code": "9096129",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEEDS OF ANISE,BADIAN,FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES-SEEDS\nOF ANISE,BADIAN, CARAWAY OR FENNEL; JUNIPER BERRIES--NEITHER CRUSHED NOR GROUND---\nSEEDS OF BADIAN----OF SEED QUALITY",
+        "hsn_code": "9096121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEEDS OF ANISE,BADIAN,FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES-SEEDS\nOF ANISE, BADIAN, CARAWAY OR FENNEL; JUNIPER BERRIES--NEITHER CRUSHED NOR GROUND---\nSEEDS OF ANISE----OTHER",
+        "hsn_code": "9096119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEEDS OF ANISE,BADIAN,FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES-SEEDS\nOF ANISE, BADIAN, CARAWAY OR FENNEL; JUNIPER BERRIES--NEITHER CRUSHED NOR GROUND---\nSEEDS OF ANISE----OF SEED QUALITY",
+        "hsn_code": "9096111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEEDS OF ANISE,BADIAN,FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES-SEEDS\nOF CUMIN-- CRUSHED OR GROUND",
+        "hsn_code": "9093200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEEDS OF ANISE,BADIAN,FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES-SEEDS\nOF CUMIN--NEITHER CRUSHED NOR GROUND---CUMIN,OTHER THAN BLACK----OTHER",
+        "hsn_code": "9093129",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "EEDS OF ANISE,BADIAN,FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES-SEEDS OF\nCUMIN--NEITHER CRUSHED NOR GROUND---CUMIN,OTHER THAN BLACK----OF SEED QUALITY",
+        "hsn_code": "9093121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEEDS OF ANISE,BADIAN,FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES-SEEDS\nOF CUMIN-- NEITHER CRUSHED NOR GROUND---CUMIN, OTHER THAN BLACK----OF SEED\nQUALITY",
+        "hsn_code": "9093111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEEDS OF ANISE,BADIAN,FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES-SEEDS\nOF CORIANDER-- CRUSED OR GROUND",
+        "hsn_code": "9092200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEEDS OF ANISE,BADIAN,FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES-SEEDS\nOF CORIANDER-- NEITHER CRUSED NOR GROUND---OTHER",
+        "hsn_code": "9092190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "SEEDS OF ANISE,BADIAN,FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES-SEEDS\nOF CORIANDER-- NEITHER CRUSED NOR GROUND---OF SEED QUALITY",
+        "hsn_code": "9092110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUTMEG, MACE AND CARDAMOMS - CARDAMOMS -- CRUSHED OR GROUND ---OTHER",
+        "hsn_code": "9083290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUTMEG, MACE AND CARDAMOMS - CARDAMOMS -- CRUSHED OR GROUND ---CARDAMOM\nHUSK",
+        "hsn_code": "9083230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUTMEG, MACE AND CARDAMOMS - CARDAMOMS -- CRUSHED OR GROUND ---SMALL\nCARDAMOM SEEDS",
+        "hsn_code": "9083220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUTMEG, MACE AND CARDAMOMS - CARDAMOMS -- CRUSHED OR GROUND ---POWDER",
+        "hsn_code": "9083210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUTMEG, MACE AND CARDAMOMS - CARDAMOMS -- NEITHER CRUSHED NOR GROUND ---\nOTHER",
+        "hsn_code": "9083190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUTMEG, MACE AND CARDAMOMS - CARDAMOMS -- NEITHER CRUSHED NOR GROUND ---\nSMALL , MIXED",
+        "hsn_code": "9083150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUTMEG, MACE AND CARDAMOMS - CARDAMOMS -- NEITHER CRUSHED NOR GROUND ---\nSMALL , BLEACHED, HALF BLEACHED OR BLEACHABLE",
+        "hsn_code": "9083140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUTMEG, MACE AND CARDAMOMS - CARDAMOMS -- NEITHER CRUSHED NOR GROUND ---\nSMALL , COORG GREEN",
+        "hsn_code": "9083130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUTMEG, MACE AND CARDAMOMS - CARDAMOMS -- NEITHER CRUSHED NOR GROUND ---\nSMALL (ELLETTARIA), ALLEPPEY GREEN",
+        "hsn_code": "9083120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUTMEG, MACE AND CARDAMOMS - CARDAMOMS -- NEITHER CRUSHED NOR GROUND ---\nLARGE (AMOMUM)",
+        "hsn_code": "9083110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUTMEG, MACE AND CARDAMOMS - MACE--CRUSHED OR GROUND",
+        "hsn_code": "9082200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUTMEG, MACE AND CARDAMOMS - MACE--NEITHER CRUSHED NOR GROUND",
+        "hsn_code": "9082100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUTMEG, MACE AND CARDAMOMS - NUTMEG -- CRUSHED OR GROUND",
+        "hsn_code": "9081200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUTMEG, MACE AND CARDAMOMS - NUTMEG -- NEITHER CRUSHED NOR GROUND---SHELLED",
+        "hsn_code": "9081120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "NUTMEG, MACE AND CARDAMOMS - NUTMEG -- NEITHER CRUSHED NOR GROUND --- IN SHELL",
+        "hsn_code": "9081110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CLOVES (WHOLE FRUIT, CLOVES AND STEMS) - CRUSHED OR GROUND",
+        "hsn_code": "9072000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CLOVES (WHOLE FRUIT, CLOVES AND STEMS) - NEITHER CRUSHED NOR GROUND --- OTHER",
+        "hsn_code": "9071090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CLOVES (WHOLE FRUIT, CLOVES AND STEMS) - NEITHER CRUSHED NOR GROUND ---STEM",
+        "hsn_code": "9071030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CLOVES (WHOLE FRUIT, CLOVES AND STEMS) - NEITHER CRUSHED NOR GROUND --- NOT\nEXTRACTED (OTHER THAN STEM)",
+        "hsn_code": "9071020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CLOVES (WHOLE FRUIT, CLOVES AND STEMS) - NEITHER CRUSHED NOR GROUND --- EXTRACTED",
+        "hsn_code": "9071010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CINNAMON AND CINNAMON-TREE FLOWERS Crushed or ground",
+        "hsn_code": "9062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CINNAMON AND CINNAMON-TREE FLOWERS - NEITHER CRUSHED NOR GROUND :-- OTHER : ---\nOTHER",
+        "hsn_code": "9061990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CINNAMON AND CINNAMON-TREE FLOWERS - NEITHER CRUSHED NOR GROUND :-- OTHER ---\nCASSIA",
+        "hsn_code": "9061910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CINNAMON AND CINNAMON-TREE FLOWERS - NEITHER CRUSHED NOR GROUND :-- CINNAMON\n(CINNAMOMUM ZEYLANICUM BLUME): --- OTHER",
+        "hsn_code": "9061190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CINNAMON AND CINNAMON-TREE FLOWERS - NEITHER CRUSHED NOR GROUND :-- CINNAMON\n(CINNAMOMUM ZEYLANICUM BLUME): --- CINNAMON TREE FLOWERS",
+        "hsn_code": "9061120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CINNAMON AND CINNAMON-TREE FLOWERS - NEITHER CRUSHED NOR GROUND :-- CINNAMON\n(CINNAMOMUM ZEYLANICUM BLUME): --- CINNAMON BARK",
+        "hsn_code": "9061110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CINNAMON AND CINNAMON-TREE FLOWERS NEITHER CRUSHED NOR GROUND : OTHER",
+        "hsn_code": "9061090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CINNAMON AND CINNAMON-TREE FLOWERS NEITHER CRUSHED NOR GROUND : CINNAMON\nTREE FLOWERS",
+        "hsn_code": "9061030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CINNAMON AND CINNAMON-TREE FLOWERS NEITHER CRUSHED NOR GROUND : CINNAMON\nBARK",
+        "hsn_code": "9061020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "CINNAMON AND CINNAMON-TREE FLOWERS NEITHER CRUSHED NOR GROUND : CASSIA",
+        "hsn_code": "9061010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VANILLA - CRUSHED OR GROUND",
+        "hsn_code": "9052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "VANILLA - NEITHER CRUSHED NOR GROUND",
+        "hsn_code": "9051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PEPPER OF THE GENUS PIPER; DRIED OR CRUSHED OR GROUND FRUITS OF THE GENUS\nCAPSICUM OF THE GENUS PIMENTA - FRUITS OF THE GENUS CAPSICUM OR OF THE GENUS\nPIMENTA -- CRUSHED OR GROUND --- OF GENUS PIMENTA ----OTHER",
+        "hsn_code": "9042229",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PEPPER OF THE GENUS PIPER; DRIED OR CRUSHED OR GROUND FRUITS OF THE GENUS\nCAPSICUM OF THE GENUS PIMENTA - FRUITS OF THE GENUS CAPSICUM OR OF THE GENUS\nPIMENTA -- CRUSHED OR GROUND --- OF GENUS PIMENTA ---- POWDER",
+        "hsn_code": "9042221",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PEPPER OF THE GENUS PIPER; DRIED OR CRUSHED OR GROUND FRUITS OF THE GENUS\nCAPSICUM OF THE GENUS PIMENTA - FRUITS OF THE GENUS CAPSICUM OR OF THE GENUS\nPIMENTA -- CRUSHED OR GROUND --- OF GENUS CAPSICUM----OTHER",
+        "hsn_code": "9042219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PEPPER OF THE GENUS PIPER; DRIED OR CRUSHED OR GROUND FRUITS OF THE GENUS\nCAPSICUM OF THE GENUS PIMENTA - FRUITS OF THE GENUS CAPSICUM OR OF THE GENUS\nPIMENTA -- CRUSHED OR GROUND --- OF GENUS CAPSICUM---- CHILLY SEEDS",
+        "hsn_code": "9042212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PEPPER OF THE GENUS PIPER; DRIED OR CRUSHED OR GROUND FRUITS OF THE GENUS\nCAPSICUM OF THE GENUS PIMENTA - FRUITS OF THE GENUS CAPSICUM OR OF THE GENUS\nPIMENTA -- CRUSHED OR GROUND --- OF GENUS CAPSICUM ---CHILLY POWDER",
+        "hsn_code": "9042211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PEPPER OF THE GENUS PIPER; DRIED OR CRUSHED OR GROUND FRUITS OF THE GENUS\nCAPSICUM OF THE GENUS PIMENTA - FRUITS OF THE GENUS CAPSICUM OR OF THE GENUS\nPIMENTA -- DRIED NEITHER CRUSHED NOR GROUND ---OF GENUS PIMENTA",
+        "hsn_code": "9042120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PEPPER OF THE GENUS PIPER; DRIED OR CRUSHED OR GROUND FRUITS OF THE GENUS\nCAPSICUM OF THE GENUS PIMENTA - FRUITS OF THE GENUS CAPSICUM OR OF THE GENUS\nPIMENTA -- DRIED NEITHER CRUSHED NOR GROUND ---OF GENUS CAPSICUM",
+        "hsn_code": "9042110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PEPPER OF THE GENUS PIPER; DRIED OR CRUSHED OR GROUND FRUITS OF THE GENUS\nCAPSICUM OR OF THE GENUS PIMENTA Pepper : Crushed or ground",
+        "hsn_code": "9041200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PEPPER OF THE GENUS PIPER; DRIED OR CRUSHED OR GROUND FRUITS OF THE GENUS\nCAPSICUM OR OF THE GENUS PIMENTA Pepper : Neither crushed nor ground : Other",
+        "hsn_code": "9041190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PEPPER OF THE GENUS PIPER; DRIED OR CRUSHED OR GROUND FRUITS OF THE GENUS\nCAPSICUM OR OF THE GENUS PIMENTA Pepper : Neither crushed nor ground : Pepper other\nthan green, frozen",
+        "hsn_code": "9041180",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PEPPER OF THE GENUS PIPER; DRIED OR CRUSHED OR GROUND FRUITS OF THE GENUS\nCAPSICUM OR OF THE GENUS PIMENTA Pepper : Neither crushed nor ground : Green pepper,\nfrozen or dried",
+        "hsn_code": "9041170",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PEPPER OF THE GENUS PIPER; DRIED OR CRUSHED OR GROUND FRUITS OF THE GENUS\nCAPSICUM OR OF THE GENUS PIMENTA Pepper : Neither crushed nor ground : Pepper pinheads",
+        "hsn_code": "9041160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PEPPER OF THE GENUS PIPER; DRIED OR CRUSHEDOR GROUND FRUITS OF THE GENUS\nCAPSICUM OROF THE GENUS PIMENTA Pepper : Neither crushed nor ground : Green pepper,\ndehydrated",
+        "hsn_code": "9041150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PEPPER OF THE GENUS PIPER; DRIED OR CRUSHED OR GROUND FRUITS OF THE GENUS\nCAPSICUM OR OF THE GENUS PIMENTA Pepper : Neither crushed nor ground : Black pepper\nungarbled",
+        "hsn_code": "9041140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PEPPER OF THE GENUS PIPER; DRIED OR CRUSHED OR GROUND FRUITS OF THE GENUS\nCAPSICUM OR OF THE GENUS PIMENTA Pepper : Neither crushed nor ground : Black pepper,\ngarbled",
+        "hsn_code": "9041130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PEPPER OF THE GENUS PIPER; DRIED OR CRUSHED OR GROUND FRUITS OF THE GENUS\nCAPSICUM OR OF THE GENUS PIMENTA Pepper : Neither crushed nor ground : Light black\npepper",
+        "hsn_code": "9041120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PEPPER OF THE GENUS PIPER; DRIED OR CRUSHED OR GROUND FRUITS OF THE GENUS\nCAPSICUM OR OF THE GENUS PIMENTA Pepper : Neither crushed nor ground : Pepper, long",
+        "hsn_code": "9041110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED GREEN TEA (NOT FERMENTED) IN IMMEDIATE PACKINGS OF\nA CONTENT NOT EXCEEDING 3 KG : OTHER",
+        "hsn_code": "9021090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED GREEN TEA (NOT FERMENTED) IN IMMEDIATE PACKINGS OF\nA CONTENT NOT EXCEEDING 3 KG : CONTENT EXCEEDING 1 KG. BUT NOT EXCEEDING 3 KG.",
+        "hsn_code": "9021030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED GREEN TEA (NOT FERMENTED) IN IMMEDIATE PACKINGS OF\nA CONTENT NOT EXCEEDING 3 KG : CONTENT EXCEEDING 25 G. BUT NOT EXCEEDING 1 KG.",
+        "hsn_code": "9021020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED GREEN TEA (NOT FERMENTED) IN IMMEDIATE PACKINGS OF\nA CONTENT NOT EXCEEDING 3 KG : CONTENT NOT EXCEEDING 25 G .",
+        "hsn_code": "9021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 12
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF GOODS OF HEADING NUMBER 8801 OR 8802",
+        "hsn_code": "8803",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF GOODS OF HEADING 8801 OR 8802 - OTHER",
+        "hsn_code": "88039000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF GOODS OF HEADING 8801 OR 8802 - - OTHER PARTS OF AEROPLANES OR HELICOPTERS",
+        "hsn_code": "88033000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF GOODS OF HEADING 8801 OR 8802 - - UNDER-CARRIAGES AND PARTS THEREOF",
+        "hsn_code": "88032000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "PARTS OF GOODS OF HEADING 8801 OR 8802 - - PROPELLERS AND ROTORS AND PARTS THEREOF",
+        "hsn_code": "88031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 5
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THANTHOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS VEHICLES SPECIALLY DESIGNED FOR TRAVELLING ONSNOW; GOLF CARS AND SIMILAR\nVEHICLES ELECTRICALLY OPERATED",
+        "hsn_code": "87031010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Motor cars and other motor vehicles; principally designed for the transport of persons (other than those of heading no. 8702), including station wagons and racing cars",
+        "hsn_code": "8703",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE - TOBACCO REFUSE",
+        "hsn_code": "24013000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "sanitary towels 9pads) or sanitary napkins",
+        "hsn_code": "96190010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNUSED POSTAGE, REVENUE OR SIMILAR STAMPS OF CURRENT OR NEW ISSUE IN THE COUNTRY IN WHICH THEY HAVE, OR WILL HAVE, A RECOGNIZED FACE VALUE; STAMP-IMPRESSED PAPER; BANK NOTES; CHEQUE FORMS; STOCK, SHARE OR BOND CERTIFICATES AND SIMILAR DOCUMENTS OF TITLE",
+        "hsn_code": "4907",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ELECTRICAL ENERGY",
+        "hsn_code": "27160000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS OF A KIND USED IN ANIMAL FEEDING",
+        "hsn_code": "2309",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM OF PELLETS, RESULTING FROM THE EXTRACTION OF VEGETABLE FATS OR OILS, OTHER THAN THOSE OF HEADING 2304 OR 2305",
+        "hsn_code": "2308",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HOP CONES, FRESH OR DRIED, WHETHER OR NOT GROUND, POWDERED OR IN THE FORM OF\nPELLETS; LUPULIN HOP CONES, NEITHER GROUND NOR POWDERED NOR IN THE FORM OF\nPELLETS",
+        "hsn_code": "12101000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DATES, FIGS, PINEAPPLES, AVOCADOS, GUAVAS, MANGOES, AND MANGOSTEENS, FRESH OR DRIED",
+        "hsn_code": "0810",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "APRICOTS, CHERRIES, PEACHES (INCLUDING NECTARINES), PLUMS AND SOLES, FRESH",
+        "hsn_code": "0809",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "APPLES, PEARS AND QUINCES, FRESH",
+        "hsn_code": "0808",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MELONS (INCLUDING WATERMELONS) AND PAPAWS (PAPAYAS), FRESH",
+        "hsn_code": "0807",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BANANAS, INCLUDING PLANTAINS, FRESH OR DRIED",
+        "hsn_code": "0803",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED VEGETABLES, WHOLE, CUT, SLICED, BROKEN OR IN POWDER, BUT NOT FURTHER PREPARED",
+        "hsn_code": "0712",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES PROVISIONALLY PRESERVED (FOR EXAMPLE, BY SULPHUR DIOXIDE GAS, IN BRINE, IN SULPHUR WATER OR IN OTHER PRESERVATIVE SOLUTIONS), BUT UNSUITABLE IN THAT STATE FOR IMMEDIATE CONSUMPTION",
+        "hsn_code": "0711",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES (UNCOOKED OR COOKED BY STEAMING OR BOILING IN WATER), FROZEN",
+        "hsn_code": "0710",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES, FRESH OR CHILLED",
+        "hsn_code": "0709",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LEGUMINOUS VEGETABLES, SHELLED OR UNSHELLED, FRESH OR CHILLED",
+        "hsn_code": "0708",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Cucumbers or gherkins, fresh or chilled",
+        "hsn_code": "0707",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CARROTS, TURNIPS, SALAD BEETROOT, SALSIFY, CELERIAC, RADISHES AND SIMILAR EDIBLE ROOTS, FRESH OR CHILLED",
+        "hsn_code": "0706",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LETTUCE (LACTUCASATIVA) AND CHICORY (CICHORIUM SPP.), FRESH OR CHILLED LETTUCE",
+        "hsn_code": "0705",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ONIONS, SHALLOTS, GARLIC, LEEKS AND OTHER Alliaceous VEGETABLES, FRESH OR CHILLED",
+        "hsn_code": "0703",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Tomatoes, fresh or chilled",
+        "hsn_code": "0702",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POTATOES, FRESH OR CHILLED",
+        "hsn_code": "0701",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BONES AND HORN-CORES, UNWORKED, DEFATTED, SIMPLY PREPARED (BUT NOT CUT TO SHAPE), TREATED WITH ACID OR DEGELATINISED POWDER AND WASTE OF THESE PRODUCTS",
+        "hsn_code": "0506",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HUMAN HAIR, UNWORKED, WHETHER OR NOT WASHED OR SCOURED; WASTE OF HUMAN HAIR",
+        "hsn_code": "0501",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BIRDS’ EGGS, IN SHELL, FRESH, PRESERVED OR COOKED",
+        "hsn_code": "0407",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FISH, FRESH OR CHILLED, EXCLUDING FISH FILLETS AND OTHER FISH MEAT OF HEADING 0304",
+        "hsn_code": "0302",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE FISH",
+        "hsn_code": "0301",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FISH AND CRUSTACEANS, MOLLUSCS AND OTHER AQUATIC INVERTEBRATES",
+        "hsn_code": "03",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF BOVINE ANIMALS, FRESH AND CHILLED",
+        "hsn_code": "0201",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE ANIMALS",
+        "hsn_code": "0106",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE POULTRY, THAT IS TO SAY, FOWLS OF THE SPECIES GALLUS DOMESTICUS, DUCKS, GEESE, TURKEYS AND GUINEA FOWLS",
+        "hsn_code": "0105",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE SHEEP AND GOATS",
+        "hsn_code": "0104",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE SWINE",
+        "hsn_code": "0103",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE BOVINE ANIMALS",
+        "hsn_code": "0102",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SLATES AND BOARDS, WITH WRITING OR DRAWING SURFACES, WHETHER OR NOT FRAMED",
+        "hsn_code": "96100000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CITRUS FRUIT, FRESH OR DRIED",
+        "hsn_code": "0805",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS OF ANISE, BADIAN, FENNEL, CORIANDER, CUMIN OR CARAWAY; JUNIPER BERRIES",
+        "hsn_code": "0909",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "TEA, WHETHER OR NOT FLAVOURED",
+        "hsn_code": "0902",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COFFEE, WHETHER OR NOT ROASTED OR DECAFFEINATED; COFFEE HUSKS AND SKINS; COFFEE SUBSTITUTES CONTAINING COFFEE IN ANY PROPORTION",
+        "hsn_code": "0901",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Peel of citrus fruit or melons (including watermelons), fresh, frozen, dried or provisionally preserved in brine, in sulphur water or in other preservative solutions",
+        "hsn_code": "0814",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GRAPES, FRESH OR DRIED",
+        "hsn_code": "0806",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Dates, figs, pineapples, avocados, guavas, mangoes and mangosteens; fresh or dried",
+        "hsn_code": "0804",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUTS, BRAZIL NUTS AND CASHEW NUTS, FRESH OR DRIED, WHETHER OR NOT SHELLED OR PEELED",
+        "hsn_code": "0801",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MANIOC, ARROWROOT, SALEP, JERUSALEM ARTICHOKES, SWEET POTATOES AND SIMILAR ROOTS AND TUBERS WITH HIGH STARCH OR INULIN CONTENT, FRESH, CHILLED, FROZEN OR DRIED, WHETHER OR NOT SLICED OR IN THE FORM OF PELLETS; SAGO PITH",
+        "hsn_code": "0714",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED LEGUMINOUS VEGETABLES, SHELLED, WHETHER OR NOT SKINNED OR SPLIT",
+        "hsn_code": "0713",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL PRODUCTS NOT ELSEWHERE SPECIFIED OR INCLUDED; DEAD ANIMALS OF CHAPTER 1 OR 3, UNFIT FOR HUMAN CONSUMPTION",
+        "hsn_code": "0511",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GUTS, BLADDERS AND STOMACHS OF ANIMALS (OTHER THAN FISH), WHOLE AND PIECES THEREOF, FRESH, CHILLED, FROZEN, SALTED, IN BRINE, DRIED OR SMOKED",
+        "hsn_code": "0504",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CHEESE AND CURD",
+        "hsn_code": "0406",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUTTERMILK, CURDLED MILK AND CREAM, YOGURT, KEFIR AND OTHER FERMENTED OR ACIDIFIED MILK AND CREAM, WHETHER OR NOT CONCENTRATED OR CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER OR FLAVOURED OR CONTAINING ADDED FRUIT, NUTS OR COCOA",
+        "hsn_code": "0403",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MILK AND CREAM, NOT CONCENTRATED NOR CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER",
+        "hsn_code": "0401",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "All dutiable articles, imported by a passenger or a member of a crew in his baggage",
+        "hsn_code": "9803",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YACHTS AND OTHER VESSELS FOR PLEASURE OR SPORTS; ROWING BOATS AND CANOES",
+        "hsn_code": "8903",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH OR WITHOUT SIDE-CARS",
+        "hsn_code": "8711",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS, THE FOLLOWING: SPADES, SHOVELS, MATTOCKS, PICKS, HOES, FORKS AND RAKES; AXES, BILL HOOKS AND SIMILAR HEWING TOOLS; SECATEURS AND PRUNERS OF ANY KIND; SCYTHES, SICKLES, HAY KNIVES, HEDGE SHEARS, TIMBER WEDGES AND OTHER TOOLS OF A KIND USED IN AGRICULTURE, HORTICULTURE OR FORESTRY",
+        "hsn_code": "8201",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HUMAN HAIR, DRESSED, THINNED, BLEACHED OR OTHERWISE WORKED; WOOL OR OTHER ANIMAL HAIR OR OTHER TEXTILE MATERIALS, PREPARED FOR USE IN MAKING WIGS OR THE LIKE",
+        "hsn_code": "6703",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806",
+        "hsn_code": "5801",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUT, ABACA (MANILA HEMP OR MUSA TEXTILIS NEE), RAMIE AND OTHER VEGETABLE TEXTILE FIBRES, NOT ELSEWHERE SPECIFIED OR INCLUDED, RAW OR PROCESSED BUT NOT SPUN; TOW, NOILS AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND GARNETTED STOCK)",
+        "hsn_code": "5305",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "JUTE AND OTHER TEXTILE BAST FIBRES (EXCLUDING FLAX, TRUE HEMP AND RAMIE), RAW OR PROCESSED BUT NOT SPUN; TOW AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND GARNETTED STOCK)",
+        "hsn_code": "5303",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WASTE OF WOOL OR OF FINE OR COARSE ANIMAL HAIR, INCLUDING YARN WASTE BUT EXCLUDING GARNETTED STOCK",
+        "hsn_code": "5103",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FINE OR COARSE ANIMAL HAIR, NOT CARDED OR COMBED",
+        "hsn_code": "5102",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOOL, NOT CARDED OR COMBED:",
+        "hsn_code": "5101",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK WASTE (INCLUDING COCOONS AND UNSUITABLE FOR REELING, YARN WASTE AND GARNETTED STOCK)",
+        "hsn_code": "5003",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "RAW SILK (NOT THROWN)",
+        "hsn_code": "5002",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Silk worm cocoons suitable for reeling",
+        "hsn_code": "5001",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MAPS AND HYDROGRAPHIC OR SIMILAR CHARTS OF ALL KINDS, INCLUDING ATLASES, WALL MAPS, TOPOGRAPHICAL PLANS AND GLOBES, PRINTED",
+        "hsn_code": "4905",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Music, printed or in manuscript, whether or not bound or illustrated",
+        "hsn_code": "4904",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CHILDREN’S PICTURE, DRAWING OR COLOURING BOOKS",
+        "hsn_code": "4903",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "NEWSPAPERS, JOURNALS AND PERIODICALS, WHETHER OR NOT ILLUSTRATED OR CONTAINING ADVERTISING MATERIAL",
+        "hsn_code": "4902",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PRINTED BOOKS, BROCHURES, LEAFLETS AND SIMILAR PRINTED MATTER, WHETHER OR NOT IN SINGLE SHEETS",
+        "hsn_code": "4901",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HYGIENIC OR PHARMACEUTICAL ARTICLES (INCLUDING TEATS), OF VULCANISED RUBBER OTHER THAN HARD RUBBER, WITH OR WITHOUT FITTINGS OF HARD RUBBER",
+        "hsn_code": "4014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS3901 TO 3914",
+        "hsn_code": "3926",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BEAUTY OR MAKE-UP PREPARATIONS AND PREPARATIONS FOR THE CARE OF THE SKIN (OTHER THAN MEDICAMENTS), INCLUDING SUNSCREEN OR SUNTAN PREPARATIONS; MANICURE OR PEDICURE PREPARATIONS",
+        "hsn_code": "3304",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FERTILISERS, WHETHER OR NOT MIXED TOGETHER OR CHEMICALLY TREATED; FERTILISERS PRODUCED BY THE MIXING OR CHEMICAL TREATMENT OF ANIMAL OR VEGETABLE PRODUCTS",
+        "hsn_code": "3101",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PEAT (INCLUDING PEAT LITTER), WHETHER OR NOT AGGLOMERATED",
+        "hsn_code": "2703",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIGNITE, WHETHER OR NOT AGGLOMERATED, EXCLUDING JET",
+        "hsn_code": "2702",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COAL; BRIQUETTES, OVOIDS AND SIMILAR SOLID FUELS MANUFACTURED FROM COAL",
+        "hsn_code": "2701",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SALT (INCLUDING TABLE SALT AND DENATURED SALT) AND PURE SODIUM CHLORIDE, WHETHER OR NOT IN AQUEOUS SOLUTION OR CONTAINING ADDED ANTI-CAKING OR FREE FLOWING AGENTS; SEA WATER",
+        "hsn_code": "2501",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE",
+        "hsn_code": "2401",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM OF PELLETS, RESULTING FROM THE EXTRACTION OF VEGETABLE FATS OR OILS, OTHER THAN THOSE OF HEADING 2304 OR 2305",
+        "hsn_code": "2306",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Waters, including natural or artificial mineral waters and aerated waters, not containing added sugar or other sweetening matter nor flavoured; ice and snow",
+        "hsn_code": "2201",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE MATERIALS OF A KIND USED PRIMARILY FOR PLAITING (FOR EXAMPLE, BAMBOOS, RATTANS, REEDS, RUSHES, OSIER, RAFFIA, CLEANED, BLEACHED OR DYED CEREAL STRAW, AND LIME BARK)",
+        "hsn_code": "1401",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SWEDES, MANGOLDS, FODDER ROOTS, HAY, LUCERNE (alfalfa), CLOVER, SAINFOIN, FORAGE KALE, LUPINES, VETCHES AND SIMILAR FORAGE PRODUCTS, WHETHER OR NOT IN THE FORM OF PELLETS",
+        "hsn_code": "1214",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Cereal straw and husks, unprepared, whether or not chopped, ground, pressed or in the form of pellets",
+        "hsn_code": "1213",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LOCUST BEANS, SEAWEEDS AND OTHER ALGAE, SUGAR BEET AND SUGARCANE, FRESH, CHILLED, FROZEN OR DRIED, WHETHER OR NOT GROUND; FRUIT STONES AND KERNELS AND OTHER VEGETABLE PRODUCTS (INCLUDING roasted CHICORY ROOTS OF THE VARIETY Ci-chorium intybus sativum) OF A KIND USED PRIMARILY FOR HUMAN CONSUMPTION, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "1212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN PERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR DRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED",
+        "hsn_code": "1211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HOP CONES, FRESH OR DRIED, WHETHER OR NOT GROUND, POWDERED OR IN THE FORM OF PELLETS; LUPULIN",
+        "hsn_code": "1210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING",
+        "hsn_code": "1209",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER OIL SEEDS AND OLEAGINOUS FRUITS, WHETHER OR NOT BROKEN",
+        "hsn_code": "1207",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SUNFLOWER SEEDS, WHETHER OR NOT BROKEN",
+        "hsn_code": "1206",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "RAPE OR COLZA SEEDS, WHETHER OR NOT BROKEN",
+        "hsn_code": "1205",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LINSEED, WHETHER OR NOT BROKEN",
+        "hsn_code": "1204",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GROUND-NUT, NOT ROASTED OR OTHERWISE COOKED, WHETHER OR NOT SHELLED OR BROKEN",
+        "hsn_code": "1202",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SOYA BEANS, WHETHER OR NOT BROKEN",
+        "hsn_code": "1201",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLOUR, MEAL AND POWDER OF THE DRIED LEGUMINOUS VEGETABLES OF HEADING 0713, OF SAGO OR OF ROOTS OR TUBERS OF HEADING 0714 OR OF THE PRODUCTS OF CHAPTER 8",
+        "hsn_code": "1106",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLOUR, MEAL, POWDER, FLAKES, GRANULES AND PELLETS OF POTATOES",
+        "hsn_code": "1105",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CEREAL GRAINS OTHERWISE WORKED (FOR EXAMPLE, HULLED, ROLLED, FLAKED, PEARLED, SLICED, OR KIBBLED), EXCEPT RICE OF HEADING 1006; GERM OF CEREALS, WHOLE, ROLLED, FLAKED OR GROUND",
+        "hsn_code": "1104",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CEREAL GROATS, MEAL AND PELLETS",
+        "hsn_code": "1103",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CEREAL FLOURS OTHER THAN THAT OF WHEAT OR MESLIN",
+        "hsn_code": "1102",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Wheat or meslin flour",
+        "hsn_code": "1101",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": null,
+        "hsn_code": "CYCLORAMALED",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services provided by extraterritorial organizations and bodies",
+        "hsn_code": "999900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services provided by extraterritorial organizations and bodies.",
+        "hsn_code": "9999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Domestic services both part time & full time",
+        "hsn_code": "999800",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Domestic services",
+        "hsn_code": "9998",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other services n.e.c.",
+        "hsn_code": "999799",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Conduct of religious ceremonies/rituals by persons",
+        "hsn_code": "999795",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Agreeing to tolerate an act",
+        "hsn_code": "999794",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Agreeing to refrain from doing an act",
+        "hsn_code": "999793",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Agreeing to do an act",
+        "hsn_code": "999792",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services involving commercial use or exploitation of any event",
+        "hsn_code": "999791",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Undertaking services",
+        "hsn_code": "999732",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Cemeteries and cremation services",
+        "hsn_code": "999731",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other beauty treatment services n.e.c.",
+        "hsn_code": "999729",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Physical well-being services including health club & fitness centre",
+        "hsn_code": "999723",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Cosmetic treatment (including cosmetic/plastic surgery), manicuring and pedicuring services",
+        "hsn_code": "999722",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Hairdressing and barbers services",
+        "hsn_code": "999721",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other washing, cleaning and dyeing services n.e.c",
+        "hsn_code": "999719",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Dyeing and colouring services",
+        "hsn_code": "999715",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Pressing services",
+        "hsn_code": "999714",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other textile cleaning services",
+        "hsn_code": "999713",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Dry-cleaning services (including fur product cleaning services)",
+        "hsn_code": "999712",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Coin-operated laundry services",
+        "hsn_code": "999711",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other services",
+        "hsn_code": "9997",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other recreation and amusement services n.e.c.",
+        "hsn_code": "999699",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Lottery services",
+        "hsn_code": "999694",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Coin-operated amusement machine services",
+        "hsn_code": "999693",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Gambling and betting services including similar online services",
+        "hsn_code": "999692",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Amusement park and similar attraction services",
+        "hsn_code": "999691",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Support services related to sports and recreation",
+        "hsn_code": "999662",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services of athletes",
+        "hsn_code": "999661",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other sports and recreational sports services n.e.c.",
+        "hsn_code": "999659",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Sports and recreational sports facility operation services",
+        "hsn_code": "999652",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Sports and recreational sports event promotion and organization services",
+        "hsn_code": "999651",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Botanical, zoological and nature reserve services",
+        "hsn_code": "999642",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Museum and preservation services of historical sites and buildings",
+        "hsn_code": "999641",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Original works of authors, composers and other artists except performing artists, painters and sculptors",
+        "hsn_code": "999633",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services of authors, composers, sculptors and other artists, except performing artists",
+        "hsn_code": "999632",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services of performing artists including actors, readers, musicians, singers, dancers, TV personalities, independent models etc",
+        "hsn_code": "999631",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other performing arts and live entertainment services n.e.c.",
+        "hsn_code": "999629",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Performing arts facility operation services",
+        "hsn_code": "999623",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Performing arts event production and presentation services",
+        "hsn_code": "999622",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Performing arts event promotion and organization services",
+        "hsn_code": "999621",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Motion picture projection services",
+        "hsn_code": "999615",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Motion picture, videotape and television programme distribution services",
+        "hsn_code": "999614",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Audiovisual post-production services",
+        "hsn_code": "999613",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Motion picture, videotape, television and radio programme production services",
+        "hsn_code": "999612",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Sound recording services",
+        "hsn_code": "999611",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Recreational, cultural and sporting services",
+        "hsn_code": "9996",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services provided by other membership organizations n.e.c.",
+        "hsn_code": "999599",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Home owners associations",
+        "hsn_code": "999598",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other civic and social organizations",
+        "hsn_code": "999597",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services provided by youth associations",
+        "hsn_code": "999596",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services furnished by environmental advocacy groups",
+        "hsn_code": "999595",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Cultural and recreational associations",
+        "hsn_code": "999594",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services furnished by human rights organizations",
+        "hsn_code": "999593",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services furnished by political organizations",
+        "hsn_code": "999592",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Religious services",
+        "hsn_code": "999591",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services furnished by trade unions",
+        "hsn_code": "999520",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services furnished by professional organizations",
+        "hsn_code": "999512",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services furnished by business and employers organizations",
+        "hsn_code": "999511",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services of membership organizations",
+        "hsn_code": "9995",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other environmental protection services n.e.c.",
+        "hsn_code": "999490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other sanitation services n.e.c.",
+        "hsn_code": "999459",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Sweeping and snow removal services",
+        "hsn_code": "999451",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other remediation services n.e.c.",
+        "hsn_code": "999449",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Building remediation services",
+        "hsn_code": "999443",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Containment, control and monitoring services and other site remediation services",
+        "hsn_code": "999442",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Site remediation and clean-up services",
+        "hsn_code": "999441",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Non-hazardous waste treatment and disposal services",
+        "hsn_code": "999433",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Hazardous waste treatment and disposal services",
+        "hsn_code": "999432",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Waste preparation, consolidation and storage services",
+        "hsn_code": "999431",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "General waste collection services, other n.e.c.",
+        "hsn_code": "999424",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "General waste collection services, residential",
+        "hsn_code": "999423",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Collection services of non-hazardous recyclable materials",
+        "hsn_code": "999422",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Collection services of hazardous waste",
+        "hsn_code": "999421",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Septic tank emptying and cleaning services",
+        "hsn_code": "999412",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Sewerage and sewage treatment services",
+        "hsn_code": "999411",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Sewage and waste collection, treatment and disposal and other environmental protection services",
+        "hsn_code": "9994",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other social services without accommodation n.e.c.",
+        "hsn_code": "999359",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Welfare services without accommodation",
+        "hsn_code": "999353",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Guidance and counseling services n.e.c. related to children",
+        "hsn_code": "999352",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Child day-care services",
+        "hsn_code": "999351",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other social services without accommodation for the elderly and disabled n.e.c.",
+        "hsn_code": "999349",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Vocational rehabilitation services",
+        "hsn_code": "999341",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other social services with accommodation for adults",
+        "hsn_code": "999334",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Residential care services for adults suffering from mental retardation, mental health illnesses or substance abuse",
+        "hsn_code": "999333",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other social services with accommodation for children",
+        "hsn_code": "999332",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Residential care services for children suffering from mental retardation, mental health illnesses or substance abuse",
+        "hsn_code": "999331",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Residential care services for the elderly and persons with disabilities",
+        "hsn_code": "999322",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Residential health-care services other than by hospitals",
+        "hsn_code": "999321",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other human health services including homeopathy, unani, ayurveda, naturopathy, acupuncture etc.",
+        "hsn_code": "999319",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Blood, sperm and organ bank services",
+        "hsn_code": "999317",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Medical Laboratory and Diagnostic-imaging services",
+        "hsn_code": "999316",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Ambulance services",
+        "hsn_code": "999315",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Nursing and Physiotherapeutic services",
+        "hsn_code": "999314",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Childbirth and related services",
+        "hsn_code": "999313",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Medical and dental services",
+        "hsn_code": "999312",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Inpatient services",
+        "hsn_code": "999311",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Human health and social care services",
+        "hsn_code": "9993",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other Educational support services",
+        "hsn_code": "999299",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "services involving conduct of examination for admission to educational institutions",
+        "hsn_code": "999295",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other education and training services n.e.c.",
+        "hsn_code": "999294",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Commercial training and coaching services",
+        "hsn_code": "999293",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Sports and recreation education services",
+        "hsn_code": "999292",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Cultural education services",
+        "hsn_code": "999291",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Specialised education services ",
+        "hsn_code": "999259",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other higher education services",
+        "hsn_code": "999249",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Higher education services, vocational",
+        "hsn_code": "999243",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Higher education services, technical",
+        "hsn_code": "999242",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Higher education services, general",
+        "hsn_code": "999241",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Secondary education services, technical and vocational.",
+        "hsn_code": "999232",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Secondary education services, general",
+        "hsn_code": "999231",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Primary education services",
+        "hsn_code": "999220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Pre-primary education services",
+        "hsn_code": "999210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Education services",
+        "hsn_code": "9992",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Administrative services related to family and child allowance programmes",
+        "hsn_code": "999134",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Administrative services related to unemployment compensation benefit schemes",
+        "hsn_code": "999133",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Administrative services related to government employee pension schemes; old-age disability or survivors' benefit schemes, other than for government employees",
+        "hsn_code": "999132",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Administrative services related to sickness, maternity or temporary disablement benefit schemes.",
+        "hsn_code": "999131",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Public administrative services related to other public order and safety affairs n.e.c.",
+        "hsn_code": "999129",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Administrative services related to the detention or rehabilitation of criminals.",
+        "hsn_code": "999128",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Public administrative services related to law courts",
+        "hsn_code": "999127",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Police and fire protection services",
+        "hsn_code": "999126",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Civil defence services",
+        "hsn_code": "999125",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Military defence services",
+        "hsn_code": "999124",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services related to foreign military aid",
+        "hsn_code": "999123",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services related to foreign economic aid",
+        "hsn_code": "999122",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Public administrative services related to external affairs, diplomatic and consular services abroad.",
+        "hsn_code": "999121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other administrative services of the government n.e.c.",
+        "hsn_code": "999119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Public administrative services related to the more efficient operation of business.",
+        "hsn_code": "999113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Public administrative services related to the provision of educational, health care, cultural and other social services, excluding social security service.",
+        "hsn_code": "999112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Overall Government public services",
+        "hsn_code": "999111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Public administration and other services provided to the community as a whole; compulsory social security services",
+        "hsn_code": "9991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Non-metal waste and scrap recovery (recycling) services, on a fee or contract basis",
+        "hsn_code": "998942",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Metal waste and scrap recovery (recycling) services, on a fee or contract basis",
+        "hsn_code": "998941",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Metal forging, pressing, stamping, roll forming and powder metallurgy services",
+        "hsn_code": "998933",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Non-ferrous metal casting services",
+        "hsn_code": "998932",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Iron and steel casting services",
+        "hsn_code": "998931",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Moulding, pressing, stamping, extruding and similar plastic manufacturing services",
+        "hsn_code": "998920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Printing and reproduction services of recorded media, on a fee or contract basis",
+        "hsn_code": "998912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Publishing, on a fee or contract basis",
+        "hsn_code": "998911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other manufacturing services; publishing, printing and reproduction services; materials recovery services",
+        "hsn_code": "9989",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other manufacturing services n.e.c.",
+        "hsn_code": "998898",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Medical and dental instrument and supply manufacturing services",
+        "hsn_code": "998897",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Game and toy manufacturing services",
+        "hsn_code": "998896",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Sports goods manufacturing services",
+        "hsn_code": "998895",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Musical instrument manufacturing services",
+        "hsn_code": "998894",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Imitation jewellery manufacturing services",
+        "hsn_code": "998893",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Jewellery manufacturing services",
+        "hsn_code": "998892",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Furniture manufacturing services",
+        "hsn_code": "998891",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other transport equipment manufacturing services",
+        "hsn_code": "998882",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Motor vehicle and trailer manufacturing services",
+        "hsn_code": "998881",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Special-purpose machinery manufacturing services",
+        "hsn_code": "998877",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "General-purpose machinery manufacturing services n.e.c.",
+        "hsn_code": "998876",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Electrical equipment manufacturing services",
+        "hsn_code": "998875",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Computer, electronic and optical product manufacturing services",
+        "hsn_code": "998874",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other fabricated metal product manufacturing and metal treatment services",
+        "hsn_code": "998873",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Weapon and ammunition manufacturing services",
+        "hsn_code": "998872",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Structural metal product, tank, reservoir and steam generator manufacturing services",
+        "hsn_code": "998871",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Basic metal manufacturing services",
+        "hsn_code": "998860",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other non-metallic mineral product manufacturing services",
+        "hsn_code": "998853",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Plastic product manufacturing services",
+        "hsn_code": "998852",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Rubber and plastic product manufacturing services",
+        "hsn_code": "998851",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Pharmaceutical product manufacturing services",
+        "hsn_code": "998843",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Chemical product manufacturing services",
+        "hsn_code": "998842",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Coke and refined petroleum product manufacturing services",
+        "hsn_code": "998841",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Paper and paper product manufacturing services",
+        "hsn_code": "998832",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Wood and wood product manufacturing services",
+        "hsn_code": "998831",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Leather and leather product manufacturing services",
+        "hsn_code": "998823",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Wearing apparel manufacturing services",
+        "hsn_code": "998822",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Textile manufacturing services",
+        "hsn_code": "998821",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Tobacco manufacturing services n.e.c.",
+        "hsn_code": "998819",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Beverage manufacturing services",
+        "hsn_code": "998818",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Prepared animal feeds manufacturing services",
+        "hsn_code": "998817",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other food product manufacturing services",
+        "hsn_code": "998816",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Dairy product manufacturing services",
+        "hsn_code": "998815",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Vegetable and animal oil and fat manufacturing services",
+        "hsn_code": "998814",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Fruit and vegetables processing services",
+        "hsn_code": "998813",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Fish processing services",
+        "hsn_code": "998812",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Meat processing services",
+        "hsn_code": "998811",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Manufacturing services on physical inputs (goods) owned by others",
+        "hsn_code": "9988",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Installation services of other goods n.e.c.",
+        "hsn_code": "998739",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Installation services of electrical machinery and apparatus n.e.c.",
+        "hsn_code": "998736",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Installation services of professional medical machinery and equipment, and precision and optical instruments.",
+        "hsn_code": "998735",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Installation services of radio, television and communications equipment and apparatus.",
+        "hsn_code": "998734",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Installation services of office and accounting machinery and computers",
+        "hsn_code": "998733",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Installation services of industrial, manufacturing and service industry machinery and equipment.",
+        "hsn_code": "998732",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Installation services of fabricated metal products, except machinery and equipment.",
+        "hsn_code": "998731",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Maintenance and repair services of other goods n.e.c.",
+        "hsn_code": "998729",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Repair services for photographic equipment and cameras",
+        "hsn_code": "998727",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Maintenance and repair services of musical instruments",
+        "hsn_code": "998726",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Repair services of bicycles",
+        "hsn_code": "998725",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Repair services of furniture",
+        "hsn_code": "998724",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Repair services of garments and household textiles",
+        "hsn_code": "998723",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Repair services of watches, clocks and jewellery",
+        "hsn_code": "998722",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Repair services of footwear and leather goods",
+        "hsn_code": "998721",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Maintenance and repair services of other machinery and equipments",
+        "hsn_code": "998719",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Maintenance and repair services of elevators and escalators",
+        "hsn_code": "998718",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Maintenance and repair services of commercial and industrial machinery.",
+        "hsn_code": "998717",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Maintenance and repair services of telecommunication equipments and apparatus",
+        "hsn_code": "998716",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Maintenance and repair services of electrical household appliances",
+        "hsn_code": "998715",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Maintenance and repair services of transport machinery and equipment",
+        "hsn_code": "998714",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Maintenance and repair services of computers and peripheral equipment",
+        "hsn_code": "998713",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Maintenance and repair services of office and accounting machinery",
+        "hsn_code": "998712",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Maintenance and repair services of fabricated metal products, except machinery and equipment.",
+        "hsn_code": "998711",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Maintenance, repair and installation (except construction) services",
+        "hsn_code": "9987",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Support services to Distribution services of steam, hot water and air-conditioning supply",
+        "hsn_code": "998634",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Support services to water distribution",
+        "hsn_code": "998633",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Support services to gas distribution",
+        "hsn_code": "998632",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Support services to electricity transmission and distribution",
+        "hsn_code": "998631",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Support services to other mining n.e.c.",
+        "hsn_code": "998622",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Support services to oil and gas extraction",
+        "hsn_code": "998621",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other support services to agriculture, hunting, forestry and fishing",
+        "hsn_code": "998619",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Support services to fishing",
+        "hsn_code": "998615",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Support services to forestry and logging",
+        "hsn_code": "998614",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Support services to hunting",
+        "hsn_code": "998613",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Animal husbandry services",
+        "hsn_code": "998612",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Support services to crop production",
+        "hsn_code": "998611",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Support services to agriculture, hunting, forestry, fishing, mining and utilities.",
+        "hsn_code": "9986",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other support services n.e.c.",
+        "hsn_code": "998599",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other information services n.e.c.",
+        "hsn_code": "998598",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Landscape care and maintenance services",
+        "hsn_code": "998597",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Events, Exhibitions, Conventions and trade shows organisation and assistance services",
+        "hsn_code": "998596",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Specialized office support services such as duplicating services, mailing services, document preparation etc",
+        "hsn_code": "998595",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Combined office administrative services",
+        "hsn_code": "998594",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Telephone-based support services",
+        "hsn_code": "998593",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Collection agency services",
+        "hsn_code": "998592",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Credit reporting & rating services",
+        "hsn_code": "998591",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other travel arrangement and related services n.e.c",
+        "hsn_code": "998559",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Tourism promotion and visitor information services",
+        "hsn_code": "998557",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Tourist guide services",
+        "hsn_code": "998556",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Tour operator services",
+        "hsn_code": "998555",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Reservation services for event tickets, cinema halls, entertainment and recreational services and other reservation services",
+        "hsn_code": "998554",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Reservation services for convention centres, congress centres and exhibition halls",
+        "hsn_code": "998553",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Reservation services for accommodation, cruises and package tours",
+        "hsn_code": "998552",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Reservation services for transportation",
+        "hsn_code": "998551",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other packaging services n.e.c",
+        "hsn_code": "998549",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Coin and currency packing services",
+        "hsn_code": "998542",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Parcel packing and gift wrapping",
+        "hsn_code": "998541",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Packaging services of goods for others",
+        "hsn_code": "998540",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other cleaning services n.e.c.",
+        "hsn_code": "998539",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Cleaning of transportation equipment",
+        "hsn_code": "998538",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Exterior cleaning of buildings of all types",
+        "hsn_code": "998537",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Furnace and chimney cleaning services",
+        "hsn_code": "998536",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Sterilization of objects or premises (operating rooms)",
+        "hsn_code": "998535",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Specialized cleaning services for reservoirs and tanks",
+        "hsn_code": "998534",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "General cleaning services",
+        "hsn_code": "998533",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Window cleaning services",
+        "hsn_code": "998532",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Disinfecting and exterminating services",
+        "hsn_code": "998531",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other security services n.e.c.",
+        "hsn_code": "998529",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Fingerprinting services",
+        "hsn_code": "998528",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Polygraph services",
+        "hsn_code": "998527",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Training of guard dogs",
+        "hsn_code": "998526",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Guard services",
+        "hsn_code": "998525",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Armoured car services",
+        "hsn_code": "998524",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Security systems services",
+        "hsn_code": "998523",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Security consulting services",
+        "hsn_code": "998522",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Investigation services",
+        "hsn_code": "998521",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other employment & labour supply services n.e.c",
+        "hsn_code": "998519",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Co-employment staffing services",
+        "hsn_code": "998517",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Temporary staffing-to-permanent placement services",
+        "hsn_code": "998516",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Long-term staffing (pay rolling) services",
+        "hsn_code": "998515",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Temporary staffing services",
+        "hsn_code": "998514",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Contract staffing services",
+        "hsn_code": "998513",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Permanent placement services, other than executive search services",
+        "hsn_code": "998512",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Executive/retained personnel search services",
+        "hsn_code": "998511",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Support services",
+        "hsn_code": "9985",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Home programme distribution services",
+        "hsn_code": "998466",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Broadcasting services",
+        "hsn_code": "998465",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Television channel programmes",
+        "hsn_code": "998464",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Radio channel programmes",
+        "hsn_code": "998463",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Television broadcast originals",
+        "hsn_code": "998462",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Radio broadcast originals",
+        "hsn_code": "998461",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Operation services of historical archives including digital archives",
+        "hsn_code": "998453",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Operation services of public archives including digital archives",
+        "hsn_code": "998452",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Library services",
+        "hsn_code": "998451",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "News agency services to audiovisual media",
+        "hsn_code": "998443",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services of independent journalists and press photographers",
+        "hsn_code": "998442",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "News agency services to newspapers and periodicals",
+        "hsn_code": "998441",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other on-line contents n.e.c.",
+        "hsn_code": "998439",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Software downloads",
+        "hsn_code": "998434",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "On-line video content",
+        "hsn_code": "998433",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "On-line audio content",
+        "hsn_code": "998432",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "On-line text based information such as online books, newpapers, periodicals, directories etc",
+        "hsn_code": "998431",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other Internet telecommunications services n.e.c.",
+        "hsn_code": "998429",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Audio conferencing and video conferencing over the Internet",
+        "hsn_code": "998424",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Fax, telephony over the Internet",
+        "hsn_code": "998423",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Internet access services in wired and wireless mode.",
+        "hsn_code": "998422",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Internet backbone services",
+        "hsn_code": "998421",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other telecommunications services including Fax services, Telex services n.e.c.",
+        "hsn_code": "998419",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Data transmission services",
+        "hsn_code": "998415",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Private network services",
+        "hsn_code": "998414",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Mobile telecommunications services",
+        "hsn_code": "998413",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Fixed telephony services",
+        "hsn_code": "998412",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Carrier services",
+        "hsn_code": "998411",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Telecommunications, broadcasting and information supply services",
+        "hsn_code": "9984",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other professional, technical and business services n.e.c.",
+        "hsn_code": "998399",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Sponsorship Services & Brand Promotion Services",
+        "hsn_code": "998397",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Trademarks and franchises",
+        "hsn_code": "998396",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Translation and interpretation services",
+        "hsn_code": "998395",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Original compilations of facts/information",
+        "hsn_code": "998394",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Scientific and technical consulting services",
+        "hsn_code": "998393",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Design originals",
+        "hsn_code": "998392",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Specialty design services including interior design, fashion design, industrial design and other specialty design services",
+        "hsn_code": "998391",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other Photography & Videography and their processing services n.e.c.",
+        "hsn_code": "998387",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Photographic & videographic processing services",
+        "hsn_code": "998386",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Restoration and retouching services of photography",
+        "hsn_code": "998385",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Specialty photography services",
+        "hsn_code": "998384",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Event photography and event videography services",
+        "hsn_code": "998383",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Advertising and related photography services",
+        "hsn_code": "998382",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Portrait photography services",
+        "hsn_code": "998381",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Public opinion polling services",
+        "hsn_code": "998372",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Market research services",
+        "hsn_code": "998371",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Sale of other advertising space or time (except on commission)",
+        "hsn_code": "998366",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Sale of Internet advertising space",
+        "hsn_code": "998365",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Sale of TV and radio advertising time",
+        "hsn_code": "998364",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Sale of advertising space in print media (except on commission)",
+        "hsn_code": "998363",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Purchase or sale of advertising space or time, on commission",
+        "hsn_code": "998362",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Advertising Services",
+        "hsn_code": "998361",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other veterinary services n.e.c.",
+        "hsn_code": "998359",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Veterinary services for livestock",
+        "hsn_code": "998352",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Veterinary services for pet animals",
+        "hsn_code": "998351",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other technical and scientific services n.e.c.",
+        "hsn_code": "998349",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Certification and authentication of works of art",
+        "hsn_code": "998348",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Certification of ships, aircraft, dams, etc.",
+        "hsn_code": "998347",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Technical testing and analysis services",
+        "hsn_code": "998346",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Weather forecasting and meteorological services",
+        "hsn_code": "998345",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Surface surveying and map-making services",
+        "hsn_code": "998344",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Mineral exploration and evaluation",
+        "hsn_code": "998343",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Subsurface surveying services",
+        "hsn_code": "998342",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Geological and geophysical consulting services",
+        "hsn_code": "998341",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Project management services for construction projects",
+        "hsn_code": "998339",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Engineering services for other projects n.e.c.",
+        "hsn_code": "998338",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Engineering services for waste management projects (hazardous and non-hazardous), for water, sewerage and drainage projects.",
+        "hsn_code": "998337",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Engineering services for telecommunications and broadcasting projects",
+        "hsn_code": "998336",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Engineering services for power projects",
+        "hsn_code": "998335",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Engineering services for transportation projects",
+        "hsn_code": "998334",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Engineering services for industrial and manufacturing projects",
+        "hsn_code": "998333",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Engineering services for building projects",
+        "hsn_code": "998332",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Engineering advisory services",
+        "hsn_code": "998331",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Landscape architectural services and advisory services",
+        "hsn_code": "998328",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Project site master planning services",
+        "hsn_code": "998327",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Rural land planning services",
+        "hsn_code": "998326",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Urban planning services",
+        "hsn_code": "998325",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Historical restoration architectural services",
+        "hsn_code": "998324",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Architectural services for non-residential building projects",
+        "hsn_code": "998323",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Architectural services for residential building projects",
+        "hsn_code": "998322",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Architectural advisory services",
+        "hsn_code": "998321",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other information technology services n.e.c",
+        "hsn_code": "998319",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IT infrastructure and network management services",
+        "hsn_code": "998316",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Hosting and information technology (IT) infrastructure provisioning services",
+        "hsn_code": "998315",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Information technology (IT) design and development services",
+        "hsn_code": "998314",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Information technology (IT) consulting and support services",
+        "hsn_code": "998313",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Business consulting services including pubic relations services",
+        "hsn_code": "998312",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Management consulting and management services including financial, strategic, human resources, marketing, operations and supply chain management.",
+        "hsn_code": "998311",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other professional, technical and business services",
+        "hsn_code": "9983",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Insolvency and receivership services",
+        "hsn_code": "998240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Individual tax preparation and planning services",
+        "hsn_code": "998232",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Corporate tax consulting and preparation services",
+        "hsn_code": "998231",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other similar services n.e.c",
+        "hsn_code": "998224",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Payroll services",
+        "hsn_code": "998223",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Accounting and bookkeeping services",
+        "hsn_code": "998222",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Financial auditing services",
+        "hsn_code": "998221",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other legal services n.e.c.",
+        "hsn_code": "998216",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Arbitration and conciliation services",
+        "hsn_code": "998215",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Legal documentation and certification services concerning other documents.",
+        "hsn_code": "998214",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Legal documentation and certification services concerning patents, copyrights and other intellectual property rights.",
+        "hsn_code": "998213",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Legal advisory and representation services concerning other fields of law.",
+        "hsn_code": "998212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Legal advisory and representation services concerning criminal law.",
+        "hsn_code": "998211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Legal and accounting services",
+        "hsn_code": "9982",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Research and development originals in other fields n.e.c.",
+        "hsn_code": "998145",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Research and development originals in computer related sciences",
+        "hsn_code": "998144",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Research and development originals in biotechnology",
+        "hsn_code": "998143",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Research and development originals in agriculture",
+        "hsn_code": "998142",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Research and development originals in pharmaceuticals",
+        "hsn_code": "998141",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Interdisciplinary research and experimental development services.",
+        "hsn_code": "998130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Research and experimental development services in humanities",
+        "hsn_code": "998122",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Research and experimental development services in social sciences.",
+        "hsn_code": "998121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Research and experimental development services in agricultural sciences.",
+        "hsn_code": "998114",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Research and experimental development services in medical sciences and pharmacy.",
+        "hsn_code": "998113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Research and experimental development services in engineering and technology",
+        "hsn_code": "998112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Research and experimental development services in natural sciences",
+        "hsn_code": "998111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Research and development services",
+        "hsn_code": "9981",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Licensing services for the right to use other intellectual property products and other rescources n.e.c",
+        "hsn_code": "997339",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Licensing services for right to use other natural resources including telecommunication spectrum",
+        "hsn_code": "997338",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Licensing services for the right to use minerals including its exploration and evaluation",
+        "hsn_code": "997337",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Licensing services for the right to use trademarks and franchises",
+        "hsn_code": "997336",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Licensing services for the right to use R&D products",
+        "hsn_code": "997335",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Licensing services for the right to reprint and copy manuscripts, books, journals and periodicals.",
+        "hsn_code": "997334",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Licensing services for the right to reproduce original art works",
+        "hsn_code": "997333",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Licensing services for the right to broadcast and show original films, sound recordings, radio and television programme etc.",
+        "hsn_code": "997332",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Licensing services for the right to use computer software and databases.",
+        "hsn_code": "997331",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Leasing or rental services concerning other goods",
+        "hsn_code": "997329",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Leasing or rental services concerning do-it-yourself machinery and equipment",
+        "hsn_code": "997327",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Leasing or rental services concerning textiles, clothing and footwear.",
+        "hsn_code": "997326",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Leasing or rental services concerning household linen.",
+        "hsn_code": "997325",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Leasing or rental services concerning pleasure and leisure equipment.",
+        "hsn_code": "997324",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Leasing or rental services concerning furniture and other household appliances",
+        "hsn_code": "997323",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Leasing or rental services concerning video tapes and disks (Home entertainment equipment )",
+        "hsn_code": "997322",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Leasing or rental services concerning televisions, radios, video cassette recorders, projectors, audio systems and related equipment and accessories (Home entertainment equipment )",
+        "hsn_code": "997321",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Leasing or rental services concerning other machinery and equipments with or without operator",
+        "hsn_code": "997319",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Leasing or rental services concerning telecommunications equipment with or without operator",
+        "hsn_code": "997316",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Leasing or rental services concerning computers with or without operators",
+        "hsn_code": "997315",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Leasing or rental services concerning office machinery and equipment (except computers) with or without operator",
+        "hsn_code": "997314",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Leasing or rental services concerning construction machinery and equipment with or without operator",
+        "hsn_code": "997313",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Leasing or rental services concerning agricultural machinery and equipment with or without operator",
+        "hsn_code": "997312",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Leasing or rental services concerning transport equipments including containers, with or without operator",
+        "hsn_code": "997311",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Leasing or rental services with or without operator",
+        "hsn_code": "9973",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Real estate appraisal services on a fee/commission basis or contract basis",
+        "hsn_code": "997224",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Land sales on a fee/commission basis or contract basis",
+        "hsn_code": "997223",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Building sales on a fee/commission basis or contract basis",
+        "hsn_code": "997222",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Property management services on a fee/commission basis or contract basis",
+        "hsn_code": "997221",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Trade services of vacant and subdivided land",
+        "hsn_code": "997215",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Trade services of time-share properties",
+        "hsn_code": "997214",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Trade services of buildings",
+        "hsn_code": "997213",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Rental or leasing services involving own or leased non-residential property",
+        "hsn_code": "997212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Rental or leasing services involving own or leased residential property",
+        "hsn_code": "997211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Real estate services",
+        "hsn_code": "9972",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services of holding securities and other assets of trusts and funds and similar financial entities",
+        "hsn_code": "997172",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services of holding equity of subsidiary companies",
+        "hsn_code": "997171",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other services auxiliary to insurance and pensions ",
+        "hsn_code": "997169",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Pension fund management services",
+        "hsn_code": "997164",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Actuarial services",
+        "hsn_code": "997163",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Insurance claims adjustment services",
+        "hsn_code": "997162",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Insurance brokerage and agency services",
+        "hsn_code": "997161",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other services auxiliary to financial services",
+        "hsn_code": "997159",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Financial transactions processing and clearing house services",
+        "hsn_code": "997158",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Foreign exchange services",
+        "hsn_code": "997157",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Financial consultancy services",
+        "hsn_code": "997156",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services related to the administration of financial markets",
+        "hsn_code": "997155",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Trust and custody services",
+        "hsn_code": "997154",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Portfolio management services except pension funds",
+        "hsn_code": "997153",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Brokerage and related securities and commodities services including commodity exchange services",
+        "hsn_code": "997152",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services related to investment banking such as mergers & acquisition services, corporate finance & venture capital services",
+        "hsn_code": "997151",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other non-life reinsurance services ",
+        "hsn_code": "997149",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other property reinsurance services",
+        "hsn_code": "997147",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Freight reinsurance services",
+        "hsn_code": "997146",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "services",
+        "hsn_code": "997145",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Marine, aviation and other transport reinsurance ser",
+        "hsn_code": "997144",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Motor vehicle reinsurance services",
+        "hsn_code": "997143",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Accident and health reinsurance services",
+        "hsn_code": "997142",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Life reinsurance services",
+        "hsn_code": "997141",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other non-life insurance services (excluding reinsurance services)",
+        "hsn_code": "997139",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other property insurance services",
+        "hsn_code": "997137",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Freight insurance services & Travel insurance services",
+        "hsn_code": "997136",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Marine, aviation, and other transport insurance services",
+        "hsn_code": "997135",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Motor vehicle insurance services",
+        "hsn_code": "997134",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Accident and health insurance services",
+        "hsn_code": "997133",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Life insurance services  (excluding reinsurance services)",
+        "hsn_code": "997132",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "pension services",
+        "hsn_code": "997131",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Investment banking services ",
+        "hsn_code": "997120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other financial services (except investment banking, insurance services and pension services)",
+        "hsn_code": "997119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Financial leasing services",
+        "hsn_code": "997114",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Credit-granting services including stand-by commitment, guarantees & securities",
+        "hsn_code": "997113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Deposit services",
+        "hsn_code": "997112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Central banking services",
+        "hsn_code": "997111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Financial and related services",
+        "hsn_code": "9971",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other similar services.",
+        "hsn_code": "996929",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services involving distribution of steam, hot water and air conditioning supply etc.",
+        "hsn_code": "996922",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Water distribution services",
+        "hsn_code": "996921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Gas distribution services",
+        "hsn_code": "996913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Electricity distribution services",
+        "hsn_code": "996912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Electricity transmission services",
+        "hsn_code": "996911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Electricity, gas, water and other distribution services",
+        "hsn_code": "9969",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other Delivery Services n.e.c",
+        "hsn_code": "996819",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Local delivery services",
+        "hsn_code": "996813",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Courier services",
+        "hsn_code": "996812",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Postal services including post office counter services, mail box rental services.",
+        "hsn_code": "996811",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Postal and courier services",
+        "hsn_code": "9968",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other supporting transport services n.e.c",
+        "hsn_code": "996799",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other goods transport services ",
+        "hsn_code": "996793",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Goods transport agency services for other modes of transport",
+        "hsn_code": "996792",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Goods transport agency services for road transport",
+        "hsn_code": "996791",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Supporting services for space transport",
+        "hsn_code": "996764",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other supporting services for air transport",
+        "hsn_code": "996763",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Air traffic control services",
+        "hsn_code": "996762",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Airport operation services (excl. cargo handling)",
+        "hsn_code": "996761",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other supporting services for water transport n.e.c.",
+        "hsn_code": "996759",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Vessel salvage and refloating services",
+        "hsn_code": "996753",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Pilotage and berthing services",
+        "hsn_code": "996752",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Port and waterway operation services (excl. cargo handling) such as operation services of ports, docks, light houses, light ships etc",
+        "hsn_code": "996751",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other supporting services for road transport n.e.c.",
+        "hsn_code": "996749",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Towing services for commercial and private vehicles",
+        "hsn_code": "996744",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Parking lot services",
+        "hsn_code": "996743",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Operation services of National Highways, State Highways, Expressways, Roads & streets; bridges and tunnel operation services.",
+        "hsn_code": "996742",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Bus station services",
+        "hsn_code": "996741",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other supporting services for railway transport n.e.c.",
+        "hsn_code": "996739",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Railway pushing or towing services",
+        "hsn_code": "996731",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other storage and warehousing services",
+        "hsn_code": "996729",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Bulk liquid or gas storage services",
+        "hsn_code": "996722",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Refrigerated storage services",
+        "hsn_code": "996721",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other cargo and baggage handling services",
+        "hsn_code": "996719",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Clearing and forwarding services",
+        "hsn_code": "996713",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Customs House Agent services",
+        "hsn_code": "996712",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Container handling services",
+        "hsn_code": "996711",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Supporting services in transport",
+        "hsn_code": "9967",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Rental services of other transport vehicles n.e.c. with or without operator",
+        "hsn_code": "996609",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Rental services of aircraft including passenger aircrafts, freight aircrafts etc with or without operator",
+        "hsn_code": "996603",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Rental services of water vessels including passenger vessels, freight vessels etc with or without operator",
+        "hsn_code": "996602",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Rental services of road vehicles including buses, coaches, cars, trucks and other motor vehicles, with or without operator",
+        "hsn_code": "996601",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Rental services of transport vehicles with or without operators",
+        "hsn_code": "9966",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Space transport services of freight",
+        "hsn_code": "996532",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Air transport services of letters & parcels and other goods",
+        "hsn_code": "996531",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Inland water transport services of goods by refrigerator vessels, tankers and other vessels.",
+        "hsn_code": "996522",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Coastal and transoceanic (overseas) water transport services of goods by refrigerator vessels, tankers, bulk cargo vessels, container ships etc",
+        "hsn_code": "996521",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other land transport services of goods n.e.c.",
+        "hsn_code": "996519",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Transport services of petroleum & natural gas, water, sewerage and other goods via pipeline",
+        "hsn_code": "996513",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Railway transport services of Goods including letters, parcels, live animals, household & office furniture, intermodal containers, bulk cargo etc",
+        "hsn_code": "996512",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Road transport services of Goods including letters, parcels, live animals, household & office furniture, containers etc by refrigerator vehicles, trucks, trailers, man or animal drawn vehicles or any other vehicles.",
+        "hsn_code": "996511",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Goods Transport Services",
+        "hsn_code": "9965",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other long-distance transportation services of passengers n.e.c.",
+        "hsn_code": "996429",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Space transport services of passengers",
+        "hsn_code": "996427",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Domestic/international non-scheduled air transport services of Passengers",
+        "hsn_code": "996426",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Domestic/International Scheduled Air transport services of passengers",
+        "hsn_code": "996425",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Coastal and transoceanic (overseas) water transport services of passengers by Ferries, Cruise Ships etc",
+        "hsn_code": "996424",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Taxi services including radio taxi & other similar services",
+        "hsn_code": "996423",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Long-distance transport services of passengers through Road by Bus, Car, non-scheduled long distance bus and coach services, stage carriage etc",
+        "hsn_code": "996422",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Long-distance transport services of passengers through Rail network by Railways, Metro etc",
+        "hsn_code": "996421",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other local transportation services of passengers n.e.c.",
+        "hsn_code": "996419",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Sightseeing transportation services by rail, land, water & air",
+        "hsn_code": "996416",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Local water transport services of passengers by ferries, cruises etc",
+        "hsn_code": "996415",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other land transportation services of passengers.",
+        "hsn_code": "996414",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Non-scheduled local bus and coach charter services",
+        "hsn_code": "996413",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Taxi services including radio taxi & other similar services; ",
+        "hsn_code": "996412",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Local land transport services of passengers by railways, metro, monorail, bus, tramway, autos, three wheelers, scooters and other motor vehicles",
+        "hsn_code": "996411",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Passenger transport services",
+        "hsn_code": "9964",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other food, edible preparations, alchoholic & non-alchocholic beverages serving services n.e.c.",
+        "hsn_code": "996339",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other contract food services",
+        "hsn_code": "996337",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Preparation and/or supply services of food, edible preparations, alchoholic & non-alchocholic beverages to airlines and other transportation operators",
+        "hsn_code": "996336",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Catering services in trains, flights etc.",
+        "hsn_code": "996335",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Catering Services in Exhibition halls, Events, Marriage Halls and other outdoor/indoor functions.",
+        "hsn_code": "996334",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services provided in Canteen and other similar establishments",
+        "hsn_code": "996333",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services provided by Hotels, INN, Guest House, Club etc including Room services, takeaway services and door delivery of food.",
+        "hsn_code": "996332",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services provided by Restaurants, Cafes and similar eating facilities including takeaway services, Room services and door delivery of food.",
+        "hsn_code": "996331",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other room or unit accommodation services n.e.c.",
+        "hsn_code": "996329",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Room or unit accommodation services provided by Hostels, Camps, Paying Guest etc",
+        "hsn_code": "996322",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Room or unit accommodation services for students in student residences",
+        "hsn_code": "996321",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Recreational and vacation camp services",
+        "hsn_code": "996313",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Camp site services",
+        "hsn_code": "996312",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Room or unit accommodation services provided by Hotels, INN, Guest House, Club etc",
+        "hsn_code": "996311",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Accommodation, Food and beverage services",
+        "hsn_code": "9963",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services provided for a fee/commission or contract basis on retail trade",
+        "hsn_code": "996211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services in retail trade",
+        "hsn_code": "9962",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services provided for a fee/commission or contract basis on wholesale trade",
+        "hsn_code": "996111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services in wholesale trade",
+        "hsn_code": "9961",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services involving Repair, alterations, additions, replacements, maintenance of the completion/finishing works covered above.",
+        "hsn_code": "995479",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other building completion and finishing services n.e.c.",
+        "hsn_code": "995478",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Fencing and railing services",
+        "hsn_code": "995477",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Joinery and carpentry services",
+        "hsn_code": "995476",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other floor laying, wall covering and wall papering services",
+        "hsn_code": "995475",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Floor and wall tiling services",
+        "hsn_code": "995474",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Painting services",
+        "hsn_code": "995473",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Plastering services",
+        "hsn_code": "995472",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Glazing services",
+        "hsn_code": "995471",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services involving Repair, alterations, additions, replacements, maintenance of the installations covered above.",
+        "hsn_code": "995469",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other installation services n.e.c.",
+        "hsn_code": "995468",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Lift and escalator installation services",
+        "hsn_code": "995466",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Insulation services",
+        "hsn_code": "995465",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Gas fitting installation services",
+        "hsn_code": "995464",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Heating, ventilation and air conditioning equipment installation services",
+        "hsn_code": "995463",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Water plumbing and drain laying services",
+        "hsn_code": "995462",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Electrical installation services including Electrical wiring & fitting services, fire alarm installation services, burglar alarm system installation services.",
+        "hsn_code": "995461",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services involving Repair, alterations, additions, replacements, maintenance of the constructions covered above.",
+        "hsn_code": "995459",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other special trade construction services n.e.c.",
+        "hsn_code": "995458",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Scaffolding services",
+        "hsn_code": "995457",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Masonry services",
+        "hsn_code": "995456",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Structural steel erection services",
+        "hsn_code": "995455",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Concrete services",
+        "hsn_code": "995454",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Roofing and waterproofing services",
+        "hsn_code": "995453",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Building framing & Roof Framing services",
+        "hsn_code": "995452",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Pile driving and foundation services",
+        "hsn_code": "995451",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services involving Repair, alterations, additions, replacements, maintenance of the constructions covered above.",
+        "hsn_code": "995449",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other assembly and erection services n.e.c.",
+        "hsn_code": "995444",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Installation services of all types of street furniture (e.g., bus shelters, benches, telephone booths, public toilets, etc.)",
+        "hsn_code": "995443",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Installation, assembly and erection services of other prefabricated structures and constructions",
+        "hsn_code": "995442",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Installation, assembly and erection services of prefabricated buildings",
+        "hsn_code": "995441",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services involving Repair, alterations, additions, replacements, maintenance of the constructions covered above.",
+        "hsn_code": "995439",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Other site preparation services n.e.c",
+        "hsn_code": "995435",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Water well drilling services and septic system installation services",
+        "hsn_code": "995434",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Excavating and earthmoving services",
+        "hsn_code": "995433",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Site formation and clearance services including preparation services to make sites ready for subsequent construction work, test drilling & boring & core extraction, digging of trenches.",
+        "hsn_code": "995432",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Demolition services",
+        "hsn_code": "995431",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services involving Repair, alterations, additions, replacements, renovation, maintenance or remodelling of the constructions covered above.",
+        "hsn_code": "995429",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "General construction services of other civil engineering works n.e.c.",
+        "hsn_code": "995428",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "General construction services of outdoor sport and recreation facilities",
+        "hsn_code": "995427",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "General Construction services of Power Plants and its related infrastructure",
+        "hsn_code": "995426",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "General construction services of mines and industrial plants",
+        "hsn_code": "995425",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "General construction services of local water & sewage pipelines, electricity and communication cables & related works",
+        "hsn_code": "995424",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "General construction services of long-distance underground/overland/submarine pipelines, communication and electric power lines (cables); pumping stations and related works; transformer stations and related works.",
+        "hsn_code": "995423",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "General construction services of harbours, waterways, dams, water mains and lines, irrigation and other waterworks",
+        "hsn_code": "995422",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "General construction services of highways, streets, roads, railways and airfield runways, bridges and tunnels",
+        "hsn_code": "995421",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Services involving Repair, alterations, additions, replacements, renovation, maintenance or remodelling of the buildings covered above.",
+        "hsn_code": "995419",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Construction Services of other buildings n.e.c",
+        "hsn_code": "995416",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Construction services of other non-residential buildings such as educational institutions, hospitals, clinics including vertinary clinics, religious establishments, courts, prisons, museums and other similar buildings",
+        "hsn_code": "995415",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Construction services of commercial buildings such as office buildings, exhibition & marriage halls, malls, hotels, restaurants, airports, rail or road terminals, parking garages, petrol and service stations, theatres and other similar buildings.",
+        "hsn_code": "995414",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Construction services of industrial buildings such as buildings used for production activities (used for assembly line activities), workshops, storage buildings and other similar industrial buildings",
+        "hsn_code": "995413",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Construction services of other residential buildings such as old age homes, homeless shelters, hostels etc",
+        "hsn_code": "995412",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Construction services of single dwelling or multi dewlling or multi-storied residential buildings",
+        "hsn_code": "995411",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Construction services",
+        "hsn_code": "9954",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "THE FOLLOWING ARTICLES OF STORES ON BOARD OF A VESSEL OR AIRCRAFT ON WHICH DUTY IS LEVIABLE UNDER THE CUSTOMS ACT, 1962 (52 OF 1962) NAMELY:",
+        "hsn_code": "9805",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BALL POINT PENS; FELT TIPPED AND OTHER POROUS-TIPPED PENS AND MARKERS; FOUNTAIN PENS; STYLOGRAPH PENS AND OTHER PENS; DUPLICATING STYLOS; PROPELLING OR SLIDING PENCILS; PEN HOLDERS, PENCIL HOLDERS AND SIMILAR HOLDERS; PARTS (INCLUDING CAPS AND CLIPS) OF THE FOREGOING ARTICLES, OTHER THAN THOSE OF HEADING 9609",
+        "hsn_code": "9619",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PENCILS (OTHER THAN PENCILS OF HEADING 9608), CRAYONS, PENCIL LEADS, PASTELS, DRAWING CHARCOALS, WRITING OR DRAWING CHALKS AND TAILORS’ CHALKS",
+        "hsn_code": "9610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUTTONS, PRESS-FASTENERS, SNAP-FASTENERS AND PRESS-STUDS, BUTTON MOULDS AND OTHER PARTS OF THESE ARTICLES; BUTTON BLANKS",
+        "hsn_code": "9606",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Hand sieves and hand riddles",
+        "hsn_code": "9604",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Percussion musical instruments (for example, drums, xylophones, cymbols, castanets, maracas)",
+        "hsn_code": "9206",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Vessels and other floating structures; for breaking up",
+        "hsn_code": "8908",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Transportation",
+        "hsn_code": "86-89",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Scissors, tailors’ shears and similar shears, and blades therefor",
+        "hsn_code": "8213",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Hand-operated mechanical appliances, weighing 10 kg or less, used in the preparation, conditioning or serving of food or drink",
+        "hsn_code": "8210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GLAZED CERAMIC FLAGS AND PAVING, HEARTH OR WALL TILES; GLAZED CERAMIC MOSAIC CUBES AND THE LIKE, WHETHER OR NOT ON A BACKING",
+        "hsn_code": "6908",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES",
+        "hsn_code": "5516",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Woven fabrics of synthetic staple fibres, n.e.c. in chapter 55",
+        "hsn_code": "5515",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF SUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2",
+        "hsn_code": "5514",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF SUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170G/M2",
+        "hsn_code": "5513",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING 85% OR MORE BY WEIGHT OF SYNTHETIC STAPLE FIBRES",
+        "hsn_code": "5512",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN",
+        "hsn_code": "5311",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF JUTE OR OF OTHER TEXTILE BASE FIBRES OF HEADING 5303",
+        "hsn_code": "5310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF FLAX",
+        "hsn_code": "5309",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF OTHER VEGETABLE TEXTILE FIBRES; PAPER YARN",
+        "hsn_code": "5308",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF JUTE OR OF OTHER TEXTILE BAST FIBRES OF HEADING 5303",
+        "hsn_code": "5307",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAX YARN",
+        "hsn_code": "5306",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF COTTON",
+        "hsn_code": "5212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED MAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2",
+        "hsn_code": "5211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Woven fabrics of cotton, containing less than 85% by weight of cotton, mixed mainly or solely with man-made fibres, weighing not more than 200 g/m2",
+        "hsn_code": "5210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING MORE THAN 200 G/M2",
+        "hsn_code": "5209",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD) PUT UP FOR RETAIL SALE",
+        "hsn_code": "5207",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, NOT PUT UP FOR RETAIL SALE",
+        "hsn_code": "5206",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF COTTON, NOT PUT UP FOR RETAIL SALE",
+        "hsn_code": "5205",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Cotton, carded or combed",
+        "hsn_code": "5203",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON WASTE (INCLUDING YARN WASTE AND GARNETTED STOCK)",
+        "hsn_code": "5202",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON, NOT CARDED OR COMBED",
+        "hsn_code": "5201",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COARSE ANIMAL HAIR OR OF HORSE HAIR",
+        "hsn_code": "5113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR",
+        "hsn_code": "5112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR",
+        "hsn_code": "5111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF COARSE ANIMAL HAIR OR OF HORSE HAIR (INCLUDING GIMPED HORSEHAIR YARN), WHETHER OR NOT PUT UP FOR RETAIL SALE",
+        "hsn_code": "5110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF WOOL OR FINE ANIMAL HAIR, PUT UP FOR RETAIL SALE",
+        "hsn_code": "5109",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF FINE ANIMAL HAIR (CARDED OR COMBED), NOT PUT UP FOR RETAIL SALE",
+        "hsn_code": "5108",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF COMBED WOOL, NOT PUT UP FOR RETAIL SALE",
+        "hsn_code": "5107",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF CARDED WOOL, NOT PUT UP FOR RETAIL SALE",
+        "hsn_code": "5106",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK YARN AND YARN SPUN FROM SILK WASTE, PUT UP FOR RETAIL SALE; SILK-WORM GUT",
+        "hsn_code": "5006",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN SPUN FROM SILK WASTE, NOT PUT UP FOR RETAIL SALE",
+        "hsn_code": "5005",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK YARN (OTHER THAN YARN SPUN FROM SILK WASTE) NOT PUT UP FOR RETAIL SALE",
+        "hsn_code": "5004",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Textiles",
+        "hsn_code": "50-63",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Plans and drawings for architectural, engineering, industrial, commercial, topographical or similar purposes, being originals drawn by hand; hand-written texts; photographic reproductions on sensitised paper and carbon copies of the foregoing",
+        "hsn_code": "4906",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "NATURAL CORK, RAW OR SIMPLY SDF KSDF JKLSD PREPARED; WASTE CORK; CRUSHED, GRANULATED OR GROUND CORK",
+        "hsn_code": "4502",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Raw Hides, Skins, Leather, & Furs",
+        "hsn_code": "41-43",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "NATURAL POLYMERS (FOR EXAMPLE, Alginic ACID) AND MODIFIED NATURAL POLYMERS (FOR EXAMPLE, HARDENED PROTEINS, CHEMICAL DERIVATIVES OF NATURAL RUBBER), NOT ELSEWHERE SPECIFIED OR INCLUDED, IN PRIMARY FORMS",
+        "hsn_code": "3913",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN PRIMARY FORMS",
+        "hsn_code": "3912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM RESINS, COUMARONE-INDENE RESINS, POLYTERPENES, POLYSULPHIDES, POLYSULPHONES AND OTHER PRODUCTS SPECIFIED IN NOTE 3 TO THIS CHAPTER, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN PRIMARY FORMS",
+        "hsn_code": "3911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILICONES IN PRIMARY FORMS",
+        "hsn_code": "3910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMINO-RESINS,PHENOLIC RESINS AND POLYURETHANES, IN PRIMARY FORMS",
+        "hsn_code": "3909",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYAMIDES IN PRIMARY FORMS",
+        "hsn_code": "3908",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS; POLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY FORMS",
+        "hsn_code": "3907",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IRON ORES AND CONCENTRATES, INCLUDING ROASTED IRON PYRITES",
+        "hsn_code": "3906",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL ACETATE OR OF OTHER VINYL ESTERS, IN PRIMARY FORMS; OTHER VINYL POLYMERS IN PRIMARY FORMS",
+        "hsn_code": "3905",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL CHLORIDE OR OF OTHER HALOGENATED OLEFINS, IN PRIMARY FORMS",
+        "hsn_code": "3904",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF STYRENE, IN PRIMARY FORMS",
+        "hsn_code": "3903",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF PROPYLENE OR OF OTHER OLEFINS, IN PRIMARY FORMS",
+        "hsn_code": "3902",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF ETHYLENE, IN PRIMARY FORMS",
+        "hsn_code": "3901",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Plastics / Rubbers",
+        "hsn_code": "39-40",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MATCHES; OTHER THAN PYROTECHNIC ARTICLES OF HEADING 3604",
+        "hsn_code": "3605",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Electrical Energy",
+        "hsn_code": "2716",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PITCH AND PITCH COKE, OBTAINED FROM COAL TAR OR FROM OTHER MINERAL TARS",
+        "hsn_code": "2709",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Mineral Products",
+        "hsn_code": "25-27",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNDENATURED ETHYL ALCOHOL OF AN ALCOHOLIC STRENGTH BY VOLUME OF LESS THAN 80% VOL.; SPIRIT, LIQUEURS AND OTHER SPIRITNOUS BEVERAGES",
+        "hsn_code": "2208",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "VERMOUTH AND OTHER WINE OF FRESH GRAPES FLAVOURED WITH PLANTS OR AROMATIC SUBSTANCES",
+        "hsn_code": "2205",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WINE OF FRESH GRAPES, INCLUDING FORTIFIED WINES; GRAPE MUST OTHER THAN THAT OF HEADING 2009",
+        "hsn_code": "2204",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WATERS, INCLUDING MINERAL WATERS AND AERATED WATERS, CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER OR FLAVOURED, AND OTHER NON-ALCOHOLIC BEVERAGES, NOT INCLUDING FRUIT OR VEGETABLE JUICES OF HEADING 2009",
+        "hsn_code": "2203",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "EXTRACTS, ESSENCES AND CONCENTRATES OF COFFEE, TEA OR MATE AND PREPARATIONS WITH A BASIS OF THESE PRODUCTS OR WITH A BASIS OF COFFEE, TEA OR MATE; ROASTED CHICORY & OTHER ROASTED COFFEE SUBSTITUTES, AND EXTRACTS, ESSENCES AND CONCENTRATES THEREOF",
+        "hsn_code": "2101",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Foodstuffs",
+        "hsn_code": "16-24",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Glycerol, crude; glycerol waters and glycerol lyes",
+        "hsn_code": "1520",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Wheat gluten, whether or not dried",
+        "hsn_code": "1109",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FOLIAGE, BRANCHES AND OTHER PARTS OF PLANTS, WITHOUT FLOWERS OR FLOWER BUDS, AND GRASSES, MOSSES AND LICHENS, BEING GOODS OF A KIND SUITABLE FOR BOUQUETS OR FOR ORNAMENTAL PURPOSES, FRESH, DRIED, DYED, BLEACHED, IMPREGNATED OR OTHERWISE PREPARED",
+        "hsn_code": "0604",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CUT FLOWERS AND FLOWER BUDS OF A KIND SUITABLE FOR BOUQUETS OR FOR ORNAMENTAL PURPOSES, FRESH, DRIED, DYED, BLEACHED, IMPREGNATED OR OTHERWISE PREPARED",
+        "hsn_code": "0603",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE PLANTS (INCLUDING THEIR ROOTS), CUTTINGS AND SLIPS; MUSHROOM SPAWN",
+        "hsn_code": "0602",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BULBS, TUBERS, TUBEROUS ROOTS, CORMS, CROWNS AND RHIZOMES, DORMANT, IN GROWTH OR IN FLOWER; CHICORY PLANTS AND ROOTS OTHER THAN ROOTS OF HEADING 1212",
+        "hsn_code": "0601",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Vegetable Products",
+        "hsn_code": "06-15",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE HORSES, ASSES, MULES AND HINNIES",
+        "hsn_code": "0101",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL ORIGINATED PRODUCTS; NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "05",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DAIRY PRODUCE; BIRDS’ EGGS; NATURAL HONEY; EDIBLE PRODUCTS OF ANIMAL ORIGIN, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "04",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT AND EDIBLE MEAT OFFAL",
+        "hsn_code": "02",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE ANIMALS",
+        "hsn_code": "01",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": null,
+        "hsn_code": "4565",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ljdosodgojsog",
+        "hsn_code": "96190020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": null,
+        "hsn_code": "c9a755ca7e",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUTTONS, PRESS-FASTENERS, SNAP-FASTENERS AND PRESSSTUDS, BUTTON MOULDS AND\nOTHER PARTS OF THESE ARTICLES; BUTTON BLANKS - Press-fasteners, snap-fasteners and press\nstuds and parts thereof : Parts",
+        "hsn_code": "96061020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUTTONS, PRESS-FASTENERS, SNAP-FASTENERS AND PRESSSTUDS, BUTTON MOULDS AND\nOTHER PARTS OF THESE ARTICLES; BUTTON BLANKS - Press-fasteners, snap-fasteners and press\nstuds and parts thereof : Press-fasteners, snap-fasteners and press-studs",
+        "hsn_code": "96061010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DOLLS REPRESENTING ONLY HUMAN BEINGS - DOLLS, WHETHER OR NOT DRESSED: - PARTS AND\nACCESSORIES: OTHER",
+        "hsn_code": "95029900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DOLLS REPRESENTING ONLY HUMAN BEINGS - DOLLS, WHETHER OR NOT DRESSED: - PARTS AND\nACCESSORIES: GARMENTS AND ACCESSORIES THEREFOR, FOOT-WEAR AND HEADGEAR",
+        "hsn_code": "95029100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DOLLS REPRESENTING ONLY HUMAN BEINGS - DOLLS, WHETHER OR NOT DRESSED: OTHER",
+        "hsn_code": "95021090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DOLLS REPRESENTING ONLY HUMAN BEINGS - DOLLS, WHETHER OR NOT DRESSED: OF PLASTICS",
+        "hsn_code": "95021030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DOLLS REPRESENTING ONLY HUMAN BEINGS - DOLLS, WHETHER OR NOT DRESSED: OF METAL",
+        "hsn_code": "95021020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DOLLS REPRESENTING ONLY HUMAN BEINGS - DOLLS, WHETHER OR NOT DRESSED: OF WOOD",
+        "hsn_code": "95021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WHEELED TOYS DESIGNED TO BE RIDDEN BY CHILDREN (FOR EXAMPLE, TRICYCLES, SCOOTERS,\nPEDAL CARS); DOLLR CARRIAGES - WHEELED TOYS DESIGNED TO BE RIDDEN BY CHILDREN (FOR\nEXAMPLE, TRICYCLES, SCOOTERS, PEDAL CARS); DOLLR CARRIAGES: PARTS",
+        "hsn_code": "95010090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WHEELED TOYS DESIGNED TO BE RIDDEN BY CHILDREN (FOR EXAMPLE, TRICYCLES, SCOOTERS,\nPEDAL CARS); DOLLR CARRIAGES - WHEELED TOYS DESIGNED TO BE RIDDEN BY CHILDREN (FOR\nEXAMPLE, TRICYCLES, SCOOTERS, PEDAL CARS); DOLLR CARRIAGES: WHEEL TOYS DESIGNED TO\nBE RIDDEN BY CHILDREN; DOLLS CARRIAGES",
+        "hsn_code": "95010010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ACCORDIONS AND SIMILAR INSTRUMENTS; MOUTH ORGANS MOUTH ORGANS",
+        "hsn_code": "92042000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ACCORDIONS AND SIMILAR INSTRUMENTS; MOUTH ORGANS ACCORDIONS AND SIMILAR\nINSTRUMENTS",
+        "hsn_code": "92041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "KEYBOARD PIPE ORGANS; HARMONIUMS AND SIMILAR KEYBOARD INSTRUMENTS WITH FREE\nMETAL REEDS 9203 00? KEYBOARD PIPE ORGANS; HARMONIUMS AND SIMILAR KEYBOARD\nINSTRUMENTS WITH FREE METAL REEDS KEYBOARD PIPE ORGANS; HARMONIUMS AND SIMILAR\nKEYBOARD INSTRUMENTS WITH FREE METAL REEDS: OTHER",
+        "hsn_code": "92030090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "KEYBOARD PIPE ORGANS; HARMONIUMS AND SIMILAR KEYBOARD INSTRUMENTS WITH FREE\nMETAL REEDS 9203 00? KEYBOARD PIPE ORGANS; HARMONIUMS AND SIMILAR KEYBOARD\nINSTRUMENTS WITH FREE METAL REEDS KEYBOARD PIPE ORGANS; HARMONIUMS AND SIMILAR\nKEYBOARD INSTRUMENTS WITH FREE METAL REEDS: HARMONIUMS AND SIMILAR KEY BOARD\nINSTRUMENTS WITH FREE METAL REEDS",
+        "hsn_code": "92030010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YACHTS AND OTHER VESSELS FOR PLEASURE OR SPORTS; ROWING BOATS AND CANOES - OTHER\n: - OTHER : OTHER",
+        "hsn_code": "89039990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YACHTS AND OTHER VESSELS FOR PLEASURE OR SPORTS; ROWING BOATS AND CANOES - OTHER\n: - OTHER : CANOES",
+        "hsn_code": "89039910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YACHTS AND OTHER VESSELS FOR PLEASURE OR SPORTS; ROWING BOATS AND CANOES - OTHER\n: MOTORBOATS, OTHER THAN OUTBOARD MOTORBOATS",
+        "hsn_code": "89039200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YACHTS AND OTHER VESSELS FOR PLEASURE OR SPORTS; ROWING BOATS AND CANOES - OTHER\n: SAIL BOATS, WITH OR WITHOUT AUXILIARY MOTOR",
+        "hsn_code": "89039100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YACHTS AND OTHER VESSELS FOR PLEASURE OR SPORTS; ROWING BOATS AND CANOES\nINFLATABLE",
+        "hsn_code": "89031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; OTHER : OTHER : OTHER",
+        "hsn_code": "87119099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; OTHER : OTHER : ELECTRICALLY OPERATED",
+        "hsn_code": "87119091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; OTHER : SIDE-CARS",
+        "hsn_code": "87119010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; WITH RECIPROCATING INTERNAL COMBUSTION PISTON ENGINE OF A\nCYLINDER CAPACITY EXCEEDING 800 CC",
+        "hsn_code": "87115000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; WITH RECIPROCATING INTERNAL COMBUSTION PISTON ENGINE OF A\nCYLINDER CAPACITY EXCEEDING 500 CC BUT NOT EXCEEDING 800 CC : OTHER",
+        "hsn_code": "87114090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; WITH RECIPROCATING INTERNAL COMBUSTION PISTON ENGINE OF A\nCYLINDER CAPACITY EXCEEDING 500 CC BUT NOT EXCEEDING 800 CC : MOTOR-CYCLES",
+        "hsn_code": "87114010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; WITH RECIPROCATING INTERNAL COMBUSTION PISTON ENGINE OF A\nCYLINDER CAPACITY EXCEEDING 250 CC BUT NOT EXCEEDING 500 CC : OTHER",
+        "hsn_code": "87113090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; WITH RECIPROCATING INTERNAL COMBUSTION PISTON ENGINE OF A\nCYLINDER CAPACITY EXCEEDING 250 CC BUT NOT EXCEEDING 500 CC : MOTOR-CYCLES",
+        "hsn_code": "87113020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; WITH RECIPROCATING INTERNAL COMBUSTION PISTON ENGINE OF A\nCYLINDER CAPACITY EXCEEDING 250 CC BUT NOT EXCEEDING 500 CC : SCOOTERS",
+        "hsn_code": "87113010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; WITH RECIPROCATING INTERNAL COMBUSTION PISTON ENGINE OF A\nCYLINDER CAPACITY EXCEEDING 50 CC BUT NOT EXCEEDING 250 CC : OTHER : OTHER",
+        "hsn_code": "87112099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; WITH RECIPROCATING INTERNAL COMBUSTION PISTON ENGINE OF A\nCYLINDER CAPACITY EXCEEDING 50 CC BUT NOT EXCEEDING 250 CC : OTHER : OF CYLINDER\nCAPACITY NOT EXCEEDING 75 CC",
+        "hsn_code": "87112091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; WITH RECIPROCATING INTERNAL COMBUSTION PISTON ENGINE OF A\nCYLINDER CAPACITY EXCEEDING 50 CC BUT NOT EXCEEDING 250 CC : MOPEDS : OTHER",
+        "hsn_code": "87112039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; WITH RECIPROCATING INTERNAL COMBUSTION PISTON ENGINE OF A\nCYLINDER CAPACITY EXCEEDING 50 CC BUT NOT EXCEEDING 250 CC : MOPEDS : OF CYLINDER\nCAPACITY NOT EXCEEDING 75 CC",
+        "hsn_code": "87112031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; WITH RECIPROCATING INTERNAL COMBUSTION PISTON ENGINE OF A\nCYLINDER CAPACITY EXCEEDING 50 CC BUT NOT EXCEEDING 250 CC : MOTOR CYCLES OTHER",
+        "hsn_code": "87112029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; WITH RECIPROCATING INTERNAL COMBUSTION PISTON ENGINE OF A\nCYLINDER CAPACITY EXCEEDING 50 CC BUT NOT EXCEEDING 250 CC : MOTOR CYCLES OF\nCYLINDER CAPACITY NOT EXCEEDING 75 CC",
+        "hsn_code": "87112021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; WITH RECIPROCATING INTERNAL COMBUSTION PISTON ENGINE OF A\nCYLINDER CAPACITY EXCEEDING 50 CC BUT NOT EXCEEDING 250 CC : SCOOTERS : OTHER",
+        "hsn_code": "87112019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; WITH RECIPROCATING INTERNAL COMBUSTION PISTON ENGINE OF A\nCYLINDER CAPACITY EXCEEDING 50 CC BUT NOT EXCEEDING 250 CC : SCOOTERS : OF CYLINDER\nCAPACITY NOT EXCEEDING 75 CC",
+        "hsn_code": "87112011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; WITH RECIPROCATING INTERNAL COMBUSTION PISTON ENGINE OF A\nCYLINDER CAPACITY NOT EXCEEDING 50 CC OTHER",
+        "hsn_code": "87111090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; WITH RECIPROCATING INTERNAL COMBUSTION PISTON ENGINE OF A\nCYLINDER CAPACITY NOT EXCEEDING 50 CC MOTORISED CYCLES",
+        "hsn_code": "87111020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTORCYCLES (INCLUDING MOPEDS) AND CYCLES FITTED WITH AN AUXILIARY MOTOR, WITH\nOR WITHOUT SIDE-CARS; WITH RECIPROCATING INTERNAL COMBUSTION PISTON ENGINE OF A\nCYLINDER CAPACITY NOT EXCEEDING 50 CC: MOPEDS",
+        "hsn_code": "87111010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHERS: OTHER",
+        "hsn_code": "87039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHERS: ELECTRICALLY OPERATED",
+        "hsn_code": "87039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH COMPRESSION IGNITION INTERNAL COMBUSTION PISTON ENGINE\n(DIESEL OR SEMI-DIESEL) : OF A CYLINDER CAPACITY EXCEEDING 2,500 CC OTHERS:: OTHER",
+        "hsn_code": "87033399",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH COMPRESSION IGNITION INTERNAL COMBUSTION PISTON ENGINE\n(DIESEL OR SEMI-DIESEL) : OF A CYLINDER CAPACITY EXCEEDING 2,500 CC OTHERS:: SPECIALISED\nTRANSPORT VEHICLES SUCH AS AMBULANCES, PRISON VANS AND THE LIKE",
+        "hsn_code": "87033392",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH COMPRESSION IGNITION INTERNAL COMBUSTION PISTON ENGINE\n(DIESEL OR SEMI-DIESEL) : OF A CYLINDER CAPACITY EXCEEDING 2,500 CC OTHERS:: MOTOR\nCARS",
+        "hsn_code": "87033391",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH COMPRESSION IGNITION INTERNAL COMBUSTION PISTON ENGINE\n(DIESEL OR SEMI-DIESEL) : OF A CYLINDER CAPACITY EXCEEDING 2,500 CC : THREE-WHEELED\nVEHICLES",
+        "hsn_code": "87033320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH COMPRESSION IGNITION INTERNAL COMBUSTION PISTON ENGINE\n(DIESEL OR SEMI-DIESEL) : OF A CYLINDER CAPACITY EXCEEDING 2,500 CC : VEHICLES\nPRINCIPALLY DESIGNED FOR THE TRANSPORT OF MORE THAN SEVEN PERSONS, INCLUDING THE\nDRIVER",
+        "hsn_code": "87033310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH COMPRESSION IGNITION INTERNAL COMBUSTION PISTON ENGINE\n(DIESEL OR SEMI-DIESEL) : OF A CYLINDER CAPACITY EXCEEDING 1,500 CC BUT NOT EXCEEDING\n2,500 CC OTHERS: OTHER",
+        "hsn_code": "87033299",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH COMPRESSION IGNITION INTERNAL COMBUSTION PISTON ENGINE\n(DIESEL OR SEMI-DIESEL) : OF A CYLINDER CAPACITY EXCEEDING 1,500 CC BUT NOT EXCEEDING\n2,500 CC OTHERS: SPECIALIZED TRANSPORT VEHICLES SUCH AS AMBULANCES, PRISON VANS\nAND THE LIKE",
+        "hsn_code": "87033292",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH COMPRESSION IGNITION INTERNAL COMBUSTION PISTON ENGINE\n(DIESEL OR SEMI-DIESEL) : OF A CYLINDER CAPACITY EXCEEDING 1,500 CC BUT NOT EXCEEDING\n2,500 CC OTHERS: MOTOR CARS",
+        "hsn_code": "87033291",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH COMPRESSION IGNITION INTERNAL COMBUSTION PISTON ENGINE\n(DIESEL OR SEMI-DIESEL) : OF A CYLINDER CAPACITY EXCEEDING 1,500 CC BUT NOT EXCEEDING\n2,500 CC THREE-WHEELED VEHICLES",
+        "hsn_code": "87033220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH COMPRESSION IGNITION INTERNAL COMBUSTION PISTON ENGINE\n(DIESEL OR SEMI-DIESEL) : ? OF A CYLINDER CAPACITY EXCEEDING 1,500 CC BUT NOT EXCEEDING\n2,500 CC : VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF MORE THAN SEVEN\nPERSONS, INCLUDING THE DRIVER",
+        "hsn_code": "87033210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY EXCEEDING 3,000 CC : OTHERS OTHER",
+        "hsn_code": "87032499",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY EXCEEDING 3,000 CC : OTHERS SPECIALISED TRANSPORT\nVEHICLES SUCH AS AMBULANCES, PRISON VANS AND THE LIKE",
+        "hsn_code": "87032492",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY EXCEEDING 3,000 CC : OTHERS MOTOR CARS",
+        "hsn_code": "87032491",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY EXCEEDING 3,000 CC : THREE-WHEELED VEHICLES",
+        "hsn_code": "87032420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY EXCEEDING 3,000 CC : VEHICLES PRINCIPALLY DESIGNED FOR\nTHE TRANSPORT OF MORE THAN SEVEN PERSONS, INCLUDING THE DRIVER",
+        "hsn_code": "87032410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY EXCEEDING 1,500 CC BUT NOT EXCEEDING 3,000 CC OTHER",
+        "hsn_code": "87032399",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY EXCEEDING 1,500 CC BUT NOT EXCEEDING 3,000 CC\nSPECIALISED TRANSPORT VEHICLES SUCH AS AMBULANCES, PRISON VANS AND THE LIKE",
+        "hsn_code": "87032392",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY EXCEEDING 1,500 CC BUT NOT EXCEEDING 3,000 CC OTHERS\nMOTOR CARS",
+        "hsn_code": "87032391",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY EXCEEDING 1,500 CC BUT NOT EXCEEDING 3,000 CC THREE\nWHEELED VEHICLES",
+        "hsn_code": "87032320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY EXCEEDING 1,500 CC BUT NOT EXCEEDING 3,000 CC :\nVEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF MORE THAN SEVEN PERSONS\nINCLUDING THE DRIVER",
+        "hsn_code": "87032310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY EXCEEDING 1,000 CC BUT NOT EXCEEDING 1,500 CC OTHER\nOTHER",
+        "hsn_code": "87032299",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY EXCEEDING 1,000 CC BUT NOT EXCEEDING 1,500 CC OTHER\nMOTOR CARS",
+        "hsn_code": "87032291",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY EXCEEDING 1,000 CC BUT NOT EXCEEDING 1,500 CC THREE\nWHEELED VEHICLES",
+        "hsn_code": "87032230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY EXCEEDING 1,000 CC BUT NOT EXCEEDING 1,500 CC\nSPECIALISED TRANSPORT VEHICLES SUCH AS AMBULANCES, PRISON VANS AND THE LIKE",
+        "hsn_code": "87032220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY EXCEEDING 1,000 CC BUT NOT EXCEEDING 1,500 CC\nVEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF MORE THAN SEVEN PERSONS,\nINCLUDING THE DRIVER",
+        "hsn_code": "87032210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY NOT EXCEEDING 1,000 CC : OTHERS OTHER",
+        "hsn_code": "87032199",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY NOT EXCEEDING 1,000 CC : OTHERS SPECIALISED TRANSPORT\nVEHICLES SUCH AS AMBULANCES, PRISON VANS AND THE LIKE",
+        "hsn_code": "87032192",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY NOT EXCEEDING 1,000 CC : OTHERS MOTOR CARS",
+        "hsn_code": "87032191",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY NOT EXCEEDING 1,000 CC : THREE-WHEELED VEHICLES",
+        "hsn_code": "87032120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONS AND RACING\nCARS OTHER VEHICLES, WITH SPARK-IGNITION INTERNAL COMBUSTION RECIPROCATING PISTON\nENGINE : OF A CYLINDER CAPACITY NOT EXCEEDING 1,000 CC : VEHICLES PRINCIPALLY DESIGNED\nFOR THE TRANSPORT OF MORE THAN SEVEN PERSONS, INCLUDING THE DRIVER",
+        "hsn_code": "87032110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MOTOR CARS AND OTHER MOTOR VEHICLES PRINCIPALLY DESIGNED FOR THE TRANSPORT OF\nPERSONS (OTHER THAN THOSE OF HEADING 8702), INCLUDING STATION WAGONSAND RACING\nCARSVEHICLES SPECIALLY DESIGNED FOR TRAVELLING ONSNOW; GOLF CARS AND SIMILAR\nVEHICLESOTHER",
+        "hsn_code": "87031090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS, THE FOLLOWING : SPADES, SHOVELS, MATTOCKS, PICKS, HOES, FORKS AND RAKES;\nAXES, BILL HOOKS AND SIMILAR HEWING TOOLS; SECATEURS AND PRUNERS OF ANY KIND;\nSCYTHES, SICKLES, HAY KNIVES, HEDGE SHEARS, TIMBER WEDGES AND OTHER TOOLS OF A KIND\nUSED IN AGRICULTURE, HORTICULTURE OR FORESTRY : OTHER HAND TOOLS OF A KIND USED IN\nAGRICULTURE, HORTICULTURE OR FORESTRY",
+        "hsn_code": "82019000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS, THE FOLLOWING : SPADES, SHOVELS, MATTOCKS, PICKS, HOES, FORKS AND RAKES;\nAXES, BILL HOOKS AND SIMILAR HEWING TOOLS; SECATEURS AND PRUNERS OF ANY KIND;\nSCYTHES, SICKLES, HAY KNIVES, HEDGE SHEARS, TIMBER WEDGES AND OTHER TOOLS OF A KIND\nUSED IN AGRICULTURE, HORTICULTURE OR FORESTRY : HEDGE SHEARS, TWO-HANDED PRUNING\nSHEARS AND SIMILAR TWO-HANDED SHEARS",
+        "hsn_code": "82016000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS, THE FOLLOWING : SPADES, SHOVELS, MATTOCKS, PICKS, HOES, FORKS AND RAKES;\nAXES, BILL HOOKS AND SIMILAR HEWING TOOLS; SECATEURS AND PRUNERS OF ANY KIND;\nSCYTHES, SICKLES, HAY KNIVES, HEDGE SHEARS, TIMBER WEDGES AND OTHER TOOLS OF A KIND\nUSED IN AGRICULTURE, HORTICULTURE OR FORESTRY : SECATEURS AND SIMILAR ONE-HANDED\nPRUNERS AND SHEARS (INCLUDING POULTRY SHEARS)",
+        "hsn_code": "82015000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS, THE FOLLOWING : SPADES, SHOVELS, MATTOCKS, PICKS, HOES, FORKS AND RAKES;\nAXES, BILL HOOKS AND SIMILAR HEWING TOOLS; SECATEURS AND PRUNERS OF ANY KIND;\nSCYTHES, SICKLES, HAY KNIVES, HEDGE SHEARS, TIMBER WEDGES AND OTHER TOOLS OF A KIND\nUSED IN AGRICULTURE, HORTICULTURE OR FORESTRY : AXES, BILL HOOKS AND SIMILAR HEWING\nTOOLS",
+        "hsn_code": "82014000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS, THE FOLLOWING : SPADES, SHOVELS, MATTOCKS, PICKS, HOES, FORKS AND RAKES;\nAXES, BILL HOOKS AND SIMILAR HEWING TOOLS; SECATEURS AND PRUNERS OF ANY KIND;\nSCYTHES, SICKLES, HAY KNIVES, HEDGE SHEARS, TIMBER WEDGES AND OTHER TOOLS OF A KIND\nUSED IN AGRICULTURE, HORTICULTURE OR FORESTRY : MATTOCKS, PICKS, HOES AND RAKES",
+        "hsn_code": "82013000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HAND TOOLS, THE FOLLOWING : SPADES, SHOVELS, MATTOCKS, PICKS, HOES, FORKS AND RAKES;\nAXES, BILL HOOKS AND SIMILAR HEWING TOOLS; SECATEURS AND PRUNERS OF ANY KIND;\nSCYTHES, SICKLES, HAY KNIVES, HEDGE SHEARS, TIMBER WEDGES AND OTHER TOOLS OF A KIND\nUSED IN AGRICULTURE, HORTICULTURE OR FORESTRY : SPADES AND SHOVELS",
+        "hsn_code": "82011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "TIN TUBES, PIPES AND TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES)-\nTIN TUBES, PIPES AND TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES) :\nTUBE OR PIPE FITTINGS",
+        "hsn_code": "80060020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "TIN TUBES, PIPES AND TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES)-\nTIN TUBES, PIPES AND TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES) :\nTUBES AND PIPES",
+        "hsn_code": "80060010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "TIN FOIL (WHETHER OR NOT PRINTED OR BACKED WITH PAPER, PAPERBOARD, PLASTICS OR\nSIMILAR BACKING MATERIALS), OF A THICKNESS (EXCLUDING ANY BACKING) NOT EXCEEDING 0.2\nMM; TIN POWDERS AND FLAKES",
+        "hsn_code": "80050000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "TIN PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.2 MM - TIN PLATES, SHEETS AND\nSTRIP, OF A THICKNESS EXCEEDING 0.2 MM : OTHER",
+        "hsn_code": "80040090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "TIN PLATES, SHEETS AND STRIP, OF A THICKNESS EXCEEDING 0.2 MM - TIN PLATES, SHEETS AND\nSTRIP, OF A THICKNESS EXCEEDING 0.2 MM : ELECTROLYTIC PLATES AND SHEETS",
+        "hsn_code": "80040010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GLAZED CERAMIC FLAGS AND PAVING, HEARTH OR WALL TILES; GLAZED CERAMIC MOSAIC\nCUBES AND THE LIKE, WHETHER OR NOT ON A BACKING - OTHER: OTHER",
+        "hsn_code": "69089090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GLAZED CERAMIC FLAGS AND PAVING, HEARTH OR WALL TILES; GLAZED CERAMIC MOSAIC\nCUBES AND THE LIKE, WHETHER OR NOT ON A BACKING - OTHER: CERAMIC MOSAIC TILES",
+        "hsn_code": "69089020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GLAZED CERAMIC FLAGS AND PAVING, HEARTH OR WALL TILES; GLAZED CERAMIC MOSAIC\nCUBES AND THE LIKE, WHETHER OR NOT ON A BACKING - OTHER: CERAMIC MOSAIC CUBES",
+        "hsn_code": "69089010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GLAZED CERAMIC FLAGS AND PAVING, HEARTH OR WALL TILES; GLAZED CERAMIC MOSAIC\nCUBES AND THE LIKE, WHETHER OR NOT ON A BACKING - TILES, CUBES AND SIMILAR ARTICLES,\nWHETHER OR NOT RECTANGULAR, THE LARGEST SURFACE AREA OF WHICH IS CAPABLE OF\nBEING ENCLOSED IN A SQUARE THE SIDE OF WHICH IS LESS THAN 7 CM: OTHER",
+        "hsn_code": "69081090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GLAZED CERAMIC FLAGS AND PAVING, HEARTH OR WALL TILES; GLAZED CERAMIC MOSAIC\nCUBES AND THE LIKE, WHETHER OR NOT ON A BACKING - TILES, CUBES AND SIMILAR ARTICLES,\nWHETHER OR NOT RECTANGULAR, THE LARGEST SURFACE AREA OF WHICH IS CAPABLE OF\nBEING ENCLOSED IN A SQUARE THE SIDE OF WHICH IS LESS THAN 7 CM: CERAMIC MOSAIC TILES",
+        "hsn_code": "69081020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GLAZED CERAMIC FLAGS AND PAVING, HEARTH OR WALL TILES; GLAZED CERAMIC MOSAIC\nCUBES AND THE LIKE, WHETHER OR NOT ON A BACKING - TILES, CUBES AND SIMILAR ARTICLES,\nWHETHER OR NOT RECTANGULAR, THE LARGEST SURFACE AREA OF WHICH IS CAPABLE OF\nBEING ENCLOSED IN A SQUARE THE SIDE OF WHICH IS LESS THAN 7 CM: CERAMIC MOSAIC\nCUBES",
+        "hsn_code": "69081010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HUMAN HAIR, DRESSED, THINNED, BLEACHED OR OTHERWISE WORKED; WOOL OR OTHER\nANIMAL HAIR OR OTHER TEXTILE ATERIALS, PREPARED FOR USE IN MAKING WIGS OR THE LIKE\nHUMAN HAIR, DRESSED, THINNED, BLEACHED OR OTHERWISE WORKED; WOOL OR\nOTHERANIMAL HAIROR OTHER TEXTILE MATERIALS, PREPARED FOR USE INMAKING WIGS OR\nTHE LIKE: WOOL OR OTHER ANIMAL HAIR OR OTHER TEXTILE MATERIALS, PREPARED FOR USE IN\nMAKING WIGS OR THE LIKE",
+        "hsn_code": "67030020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HUMAN HAIR, DRESSED, THINNED, BLEACHED OR OTHERWISE WORKED; WOOL OR OTHER\nANIMAL HAIR OR OTHER TEXTILE ATERIALS, PREPARED FOR USE IN MAKING WIGS OR THE LIKE\nHUMAN HAIR, DRESSED, THINNED, BLEACHED OR OTHERWISE WORKED; WOOL OR\nOTHERANIMAL HAIROR OTHER TEXTILE MATERIALS, PREPARED FOR USE INMAKING WIGS OR\nTHE LIKE: HUMAN HAIR, DRESSED, THINNED, BLEACHED OTHERWISE WORKED",
+        "hsn_code": "67030010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FELT HATS AND OTHER FELT HEADGEAR, MADE FROM THE HAT BODIES, HOODS OR PLATEAUX\nOF HEADING6501, WHETHER OR NOT LINED OR TRIMMED",
+        "hsn_code": "65030000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806-\nOF OTHER TEXTILE MATERIALS:PILE FABRICS AND CHENILLE FABRICS NOT ELSEWHERE SPECIFIED\nOR INCLUDED",
+        "hsn_code": "58019090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806-\nOF OTHER TEXTILE MATERIALS:PILE FABRICS AND CHENILLE FABRICS OF SILK CONTAINING MORE\nTHAN 50% BY WEIGHT OF SILK, BUT NOT CONTAINING WOOL OR HAIR",
+        "hsn_code": "58019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806\n- OF COTTON--WARP PILE FABRICS---OTHER",
+        "hsn_code": "58013790",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806\n- OF COTTON--WARP PILE FABRICS---WARP PILE FABRICS,CUT",
+        "hsn_code": "58013720",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806-\nWARP PILE FABRICS--WARP PILE FABRICS, UNCUT",
+        "hsn_code": "58013710",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806\n- OF MAN-MADE FIBRES: - CHENILLE FABRICS:OTHER",
+        "hsn_code": "58013690",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806\n- OF MAN-MADE FIBRES: - CHENILLE FABRICS:CARDUROYS",
+        "hsn_code": "58013610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806\n- OF MAN-MADE FIBRES: OTHER WEFT PILE FABRICS",
+        "hsn_code": "58013300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806\n- OF MAN-MADE FIBRES: CUT CORDUROY",
+        "hsn_code": "58013200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806\n- OF MAN-MADE FIBRES: UNCUT WEFT PILE FABRICS",
+        "hsn_code": "58013100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806\n- OF COTTON--WARP PILE FABRICS---OTHER",
+        "hsn_code": "58012790",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806\n- OF COTTON--WARP PILE FABRICS---WARP PILE FABRICS,CUT",
+        "hsn_code": "58012720",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806\n- OF COTTON--WARP PILE FABRICS---WARP PILE FABRICS,\"EPINGLE\"(UNCUT)",
+        "hsn_code": "58012710",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806\n- OF COTTON:CHENILLE FABRICS",
+        "hsn_code": "58012600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806\n- OF COTTON:OTHER WEFT PILE FABRICS",
+        "hsn_code": "58012300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806\n- OF COTTON:- CUT CORDUROY:OTHER",
+        "hsn_code": "58012290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806\n- OF COTTON:- CUT CORDUROY: SOLELY OF COTTON",
+        "hsn_code": "58012210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806\n- OF COTTON:UNCUT WEFT PILE FABRICS",
+        "hsn_code": "58012100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN PILE FABRICS AND CHENILLE FABRICS, OTHER THAN FABRICS OF HEADING 5802 OR 5806\n- OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "58011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - OTHER : PRINTED",
+        "hsn_code": "55169400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - OTHER : OF YARNS OF DIFFERENT COLOURS",
+        "hsn_code": "55169300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - OTHER : DYED",
+        "hsn_code": "55169200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - OTHER : UNBLEACHED OR BLEACHED :\nBLEACHED",
+        "hsn_code": "55169120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - OTHER : UNBLEACHED OR BLEACHED :\nUNBLEACHED",
+        "hsn_code": "55169110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING LESS THAN 85% BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES, MIXED MAINLY OR SOLELY WITH COTTON : PRINTED",
+        "hsn_code": "55164400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING LESS THAN 85% BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES, MIXED MAINLY OR SOLELY WITH COTTON : OF YARNS OF DIFFERENT\nCOLOURS",
+        "hsn_code": "55164300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING LESS THAN 85% BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES, MIXED MAINLY OR SOLELY WITH COTTON : DYED",
+        "hsn_code": "55164200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING LESS THAN 85% BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES, MIXED MAINLY OR SOLELY WITH COTTON : UNBLEACHED OR\nBLEACHED : BLEACHED",
+        "hsn_code": "55164120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING LESS THAN 85% BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES, MIXED MAINLY OR SOLELY WITH COTTON : UNBLEACHED OR\nBLEACHED : UNBLEACHED",
+        "hsn_code": "55164110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING LESS THAN 85% BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES, MIXED MAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR :\nPRINTED",
+        "hsn_code": "55163400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING LESS THAN 85% BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES, MIXED MAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : OF\nYARNS OF DIFFERENT COLOURS",
+        "hsn_code": "55163300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING LESS THAN 85% BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES, MIXED MAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : DYED",
+        "hsn_code": "55163200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING LESS THAN 85% BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES, MIXED MAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR :\nUNBLEACHED OR BLEACHED : BLEACHED",
+        "hsn_code": "55163120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING LESS THAN 85% BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES, MIXED MAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR :\nUNBLEACHED OR BLEACHED : UNBLEACHED",
+        "hsn_code": "55163110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING LESS THAN 85% BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES, MIXED MAINLY OR SOLELY WITH MANMADE FILAMENTS : PRINTED",
+        "hsn_code": "55162400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING LESS THAN 85% BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES, MIXED MAINLY OR SOLELY WITH MANMADE FILAMENTS : OF YARNS\nOF DIFFERENT COLOURS",
+        "hsn_code": "55162300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING LESS THAN 85% BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES, MIXED MAINLY OR SOLELY WITH MANMADE FILAMENTS : DYED",
+        "hsn_code": "55162200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING LESS THAN 85% BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES, MIXED MAINLY OR SOLELY WITH MANMADE FILAMENTS :\nUNBLEACHED OR BLEACHED : BLEACHED",
+        "hsn_code": "55162120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING LESS THAN 85% BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES, MIXED MAINLY OR SOLELY WITH MANMADE FILAMENTS :\nUNBLEACHED OR BLEACHED : UNBLEACHED",
+        "hsn_code": "55162110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING 85% OR MORE BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES : PRINTED : OTHER",
+        "hsn_code": "55161490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING 85% OR MORE BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES : PRINTED : SPUN RAYON PRINTED LINEN",
+        "hsn_code": "55161420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING 85% OR MORE BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES : PRINTED : SPUN RAYON PRINTED SHANTUNG",
+        "hsn_code": "55161410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING 85% OR MORE BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES : OF YARNS OF DIFFERENT COLOURS",
+        "hsn_code": "55161300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING 85% OR MORE BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES : DYED",
+        "hsn_code": "55161200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING 85% OR MORE BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES : UNBLEACHED OR BLEACHED : BLEACHED",
+        "hsn_code": "55161120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF ARTIFICIAL STAPLE FIBRES - CONTAINING 85% OR MORE BY WEIGHT OF\nARTIFICIAL STAPLE FIBRES : UNBLEACHED OR BLEACHED : UNBLEACHED",
+        "hsn_code": "55161110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OTHER WOVEN FABRICS : OTHER :\nOTHER",
+        "hsn_code": "55159990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OTHER WOVEN FABRICS : OTHER :\nPRINTED",
+        "hsn_code": "55159940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OTHER WOVEN FABRICS : OTHER : DYED",
+        "hsn_code": "55159930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OTHER WOVEN FABRICS : OTHER :\nBLEACHED",
+        "hsn_code": "55159920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OTHER WOVEN FABRICS : OTHER :\nUNBLEACHED",
+        "hsn_code": "55159910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OTHER WOVEN FABRICS : MIXED\nMAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : OTHER",
+        "hsn_code": "55159290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OTHER WOVEN FABRICS : MIXED\nMAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : PRINTED",
+        "hsn_code": "55159240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OTHER WOVEN FABRICS : MIXED\nMAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : DYED",
+        "hsn_code": "55159230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OTHER WOVEN FABRICS : MIXED\nMAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : BLEACHED",
+        "hsn_code": "55159220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OTHER WOVEN FABRICS : MIXED\nMAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : UNBLEACHED",
+        "hsn_code": "55159210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OTHER WOVEN FABRICS : MIXED\nMAINLY OR SOLELY WITH MAN-MADE FILAMENTS : OTHER",
+        "hsn_code": "55159190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OTHER WOVEN FABRICS : MIXED\nMAINLY OR SOLELY WITH MAN-MADE FILAMENTS : PRINTED",
+        "hsn_code": "55159140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OTHER WOVEN FABRICS : MIXED\nMAINLY OR SOLELY WITH MAN-MADE FILAMENTS : DYED",
+        "hsn_code": "55159130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OTHER WOVEN FABRICS : MIXED\nMAINLY OR SOLELY WITH MAN-MADE FILAMENTS : BLEACHED",
+        "hsn_code": "55159120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OTHER WOVEN FABRICS : MIXED\nMAINLY OR SOLELY WITH MAN-MADE FILAMENTS : UNBLEACHED",
+        "hsn_code": "55159110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF ACRYLIC OR MODACRYLIC STAPLE\nFIBRES : OTHER : OTHER",
+        "hsn_code": "55152990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF ACRYLIC OR MODACRYLIC STAPLE\nFIBRES : OTHER : PRINTED",
+        "hsn_code": "55152940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF ACRYLIC OR MODACRYLIC STAPLE\nFIBRES : OTHER : DYED",
+        "hsn_code": "55152930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF ACRYLIC OR MODACRYLIC STAPLE\nFIBRES : OTHER : BLEACHED",
+        "hsn_code": "55152920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF ACRYLIC OR MODACRYLIC STAPLE\nFIBRES : OTHER : UNBLEACHED",
+        "hsn_code": "55152910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF ACRYLIC OR MODACRYLIC STAPLE\nFIBRES : MIXED MAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : OTHER",
+        "hsn_code": "55152290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF ACRYLIC OR MODACRYLIC STAPLE\nFIBRES : MIXED MAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : PRINTED",
+        "hsn_code": "55152240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF ACRYLIC OR MODACRYLIC STAPLE\nFIBRES : MIXED MAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : DYED",
+        "hsn_code": "55152230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF ACRYLIC OR MODACRYLIC STAPLE\nFIBRES : MIXED MAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : BLEACHED",
+        "hsn_code": "55152220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF ACRYLIC OR MODACRYLIC STAPLE\nFIBRES : MIXED MAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : UNBLEACHED",
+        "hsn_code": "55152210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF ACRYLIC OR MODACRYLIC STAPLE\nFIBRES : MIXED MAINLY OR SOLELY WITH MAN-MADE FILAMENTS : OTHER",
+        "hsn_code": "55152190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF ACRYLIC OR MODACRYLIC STAPLE\nFIBRES : MIXED MAINLY OR SOLELY WITH MAN-MADE FILAMENTS : PRINTED",
+        "hsn_code": "55152140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF ACRYLIC OR MODACRYLIC STAPLE\nFIBRES : MIXED MAINLY OR SOLELY WITH MAN-MADE FILAMENTS : DYED",
+        "hsn_code": "55152130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF ACRYLIC OR MODACRYLIC STAPLE\nFIBRES : MIXED MAINLY OR SOLELY WITH MAN-MADE FILAMENTS : BLEACHED",
+        "hsn_code": "55152120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF ACRYLIC OR MODACRYLIC STAPLE\nFIBRES : MIXED MAINLY OR SOLELY WITH MAN-MADE FILAMENTS : UNBLEACHED",
+        "hsn_code": "55152110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : OTHER :\nOTHER",
+        "hsn_code": "55151990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : OTHER :\nPRINTED",
+        "hsn_code": "55151940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : OTHER :\nDYED",
+        "hsn_code": "55151930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : OTHER :\nBLEACHED",
+        "hsn_code": "55151920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : OTHER :\nUNBLEACHED",
+        "hsn_code": "55151910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : MIXED\nMAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : OTHER",
+        "hsn_code": "55151390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : MIXED\nMAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : PRINTED",
+        "hsn_code": "55151340",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : MIXED\nMAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : DYED",
+        "hsn_code": "55151330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : MIXED\nMAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : BLEACHED",
+        "hsn_code": "55151320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : MIXED\nMAINLY OR SOLELY WITH WOOL OR FINE ANIMAL HAIR : UNBLEACHED",
+        "hsn_code": "55151310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : MIXED\nMAINLY OR SOLELY WITH MAN-MADE FILAMENTS : OTHER",
+        "hsn_code": "55151290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : MIXED\nMAINLY OR SOLELY WITH MAN-MADE FILAMENTS : PRINTED",
+        "hsn_code": "55151240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : MIXED\nMAINLY OR SOLELY WITH MAN-MADE FILAMENTS : DYED",
+        "hsn_code": "55151230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : MIXED\nMAINLY OR SOLELY WITH MAN-MADE FILAMENTS : BLEACHED",
+        "hsn_code": "55151220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : MIXED\nMAINLY OR SOLELY WITH MAN-MADE FILAMENTS : UNBLEACHED",
+        "hsn_code": "55151210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : MIXED\nMAINLY OR SOLELY WITH VISCOSE RAYON STAPLE FIBRES : OTHER",
+        "hsn_code": "55151190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : MIXED\nMAINLY OR SOLELY WITH VISCOSE RAYON STAPLE FIBRES : PRINTED",
+        "hsn_code": "55151140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : MIXED\nMAINLY OR SOLELY WITH VISCOSE RAYON STAPLE FIBRES : DYED",
+        "hsn_code": "55151130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : MIXED\nMAINLY OR SOLELY WITH VISCOSE RAYON STAPLE FIBRES : BLEACHED",
+        "hsn_code": "55151120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES - OF POLYESTER STAPLE FIBRES : MIXED\nMAINLY OR SOLELY WITH VISCOSE RAYON STAPLE FIBRES : UNBLEACHED",
+        "hsn_code": "55151110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nPRINTED OTHER WOVEN FABRICS",
+        "hsn_code": "55144900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nPRINTED OTHER WOVEN FABRICS OF POLYESTER STAPLE FIBRES",
+        "hsn_code": "55144300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nPRINTED 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL, OF POLYESTER STAPLE\nFIBRES",
+        "hsn_code": "55144200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nPRINTED OF POLYESTER STAPLE FIBRES, PLAIN WEAVE",
+        "hsn_code": "55144100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nOF YARNS OF DIFFERENT COLOURS : OTHER WOVEN FABRICS",
+        "hsn_code": "55143900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nOF YARNS OF DIFFERENT COLOURS : OTHER WOVEN FABRICS OF POLYESTER STAPLE FIBRES",
+        "hsn_code": "55143300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nOF YARNS OF DIFFERENT COLOURS : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL,\nOF POLYESTER STAPLE FIBRES",
+        "hsn_code": "55143200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nOF YARNS OF DIFFERENT COLOURS : OF POLYESTER STAPLE FIBRES, PLAIN WEAVE",
+        "hsn_code": "55143100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nOF YARNS OF DIFFERENT COLOURS:---- OTHER WOVEN FABRICS",
+        "hsn_code": "55143019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nOF YARNS OF DIFFERENT COLOURS: ---- OTHER WOVEN FBBRICS OF POLYESTER STAPLE FIBRES",
+        "hsn_code": "55143013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2-\nOF YARNS OF DIFFERENT COLOURS: ---- 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL\nOF POLYESTER STAPLE FIBRES",
+        "hsn_code": "55143012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2-OF\nYARNS OF DIFFERENT COLOURS: ---- OF POLYESTER STAPLE FIBRES, PLAIN WEAVE",
+        "hsn_code": "55143011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS",
+        "hsn_code": "55142900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nDYED : OTHER WOVEN FABRICS OF POLYESTER STAPLE FIBRES",
+        "hsn_code": "55142300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nDYED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL, OF POLYESTER STAPLE FIBRES",
+        "hsn_code": "55142200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nDYED : OF POLYESTER STAPLE FIBRES, PLAIN WEAVE",
+        "hsn_code": "55142100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nUNBLEACHED OR BLEACHED : OTHER : BLEACHED",
+        "hsn_code": "55141920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nUNBLEACHED OR BLEACHED : OTHER : UNBLEACHED",
+        "hsn_code": "55141910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nUNBLEACHED OR BLEACHED : OTHER WOVEN FABRICS OF POLYESTER STAPLE FIBRES : BLEACHED",
+        "hsn_code": "55141320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nUNBLEACHED OR BLEACHED : OTHER WOVEN FABRICS OF POLYESTER STAPLE FIBRES :\nUNBLEACHED",
+        "hsn_code": "55141310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nUNBLEACHED OR BLEACHED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL, OF\nPOLYESTER STAPLE FIBRES : BLEACHED",
+        "hsn_code": "55141220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nUNBLEACHED OR BLEACHED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL, OF\nPOLYESTER STAPLE FIBRES : UNBLEACHED",
+        "hsn_code": "55141210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nUNBLEACHED OR BLEACHED : OF POLYESTER STAPLE FIBRES, PLAIN WEAVE : BLEACHED",
+        "hsn_code": "55141120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT EXCEEDING 170 G/M2 -\nUNBLEACHED OR BLEACHED : OF POLYESTER STAPLE FIBRES, PLAIN WEAVE : UNBLEACHED",
+        "hsn_code": "55141110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - PRINTED : OTHER WOVEN FABRICS",
+        "hsn_code": "55134900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - PRINTED : OTHER WOVEN FABRICS OF POLYESTER STAPLE FIBRES",
+        "hsn_code": "55134300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - PRINTED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL, OF POLYESTER\nSTAPLE FIBRES",
+        "hsn_code": "55134200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - PRINTED : OF POLYESTER STAPLE FIBRES, PLAIN WEAVE",
+        "hsn_code": "55134100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - OF YARNS OF DIFFERENT COLOURS : OTHER WOVEN FABRICS",
+        "hsn_code": "55133900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - OF YARNS OF DIFFERENT COLOURS : OTHER WOVEN FABRICS OF POLYESTER STAPLE\nFIBRES",
+        "hsn_code": "55133300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - OF YARNS OF DIFFERENT COLOURS : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL, OF POLYESTER STAPLE FIBRES",
+        "hsn_code": "55133200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - OF YARNS OF DIFFERENT COLOURS : OF POLYESTER STAPLE FIBRES, PLAIN WEAVE",
+        "hsn_code": "55133100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - DYED : OTHER WOVEN FABRICS",
+        "hsn_code": "55132900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - DYED : OTHER WOVEN FABRICS OF POLYESTER STAPLE FIBRES",
+        "hsn_code": "55132300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - DYED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL, OF POLYESTER STAPLE\nFIBRES",
+        "hsn_code": "55132200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - DYED : OF POLYESTER STAPLE FIBRES, PLAIN WEAVE",
+        "hsn_code": "55132100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - UNBLEACHED OR BLEACHED : OTHER WOVEN FABRICS : BLEACHED",
+        "hsn_code": "55131920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - UNBLEACHED OR BLEACHED : OTHER WOVEN FABRICS : UNBLEACHED",
+        "hsn_code": "55131910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - UNBLEACHED OR BLEACHED : OTHER WOVEN FABRICS OF POLYESTER STAPLE FIBRES :\nBLEACHED",
+        "hsn_code": "55131320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - UNBLEACHED OR BLEACHED : OTHER WOVEN FABRICS OF POLYESTER STAPLE FIBRES :\nUNBLEACHED",
+        "hsn_code": "55131310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - UNBLEACHED OR BLEACHED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL,\nOF POLYESTER STAPLE FIBRES : BLEACHED",
+        "hsn_code": "55131220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - UNBLEACHED OR BLEACHED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL,\nOF POLYESTER STAPLE FIBRES : UNBLEACHED",
+        "hsn_code": "55131210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - UNBLEACHED OR BLEACHED : OF POLYESTER STAPLE FIBRES, PLAIN WEAVE : BLEACHED",
+        "hsn_code": "55131120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING LESS THAN 85% BY WEIGHT OF\nSUCH FIBRES, MIXED MAINLY OR SOLELY WITH COTTON, OF A WEIGHT NOT EXCEEDING 170\nG/M2 - UNBLEACHED OR BLEACHED : OF POLYESTER STAPLE FIBRES, PLAIN WEAVE :\nUNBLEACHED",
+        "hsn_code": "55131110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING 85% OR MORE BY WEIGHT OF\nSYNTHETIC STAPLE FIBRES - OTHER : OTHER : OTHER",
+        "hsn_code": "55129990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING 85% OR MORE BY WEIGHT OF\nSYNTHETIC STAPLE FIBRES - OTHER : OTHER : PRINTED",
+        "hsn_code": "55129920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING 85% OR MORE BY WEIGHT OF\nSYNTHETIC STAPLE FIBRES - OTHER : OTHER : DYED",
+        "hsn_code": "55129910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING 85% OR MORE BY WEIGHT OF\nSYNTHETIC STAPLE FIBRES - OTHER : UNBLEACHED OR BLEACHED : BLEACHED",
+        "hsn_code": "55129120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING 85% OR MORE BY WEIGHT OF\nSYNTHETIC STAPLE FIBRES - OTHER : UNBLEACHED OR BLEACHED : UNBLEACHED",
+        "hsn_code": "55129110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING 85% OR MORE BY WEIGHT OF\nSYNTHETIC STAPLE FIBRES - CONTAINING 85% OR MORE BY WEIGHT OF ACRYLIC OR\nMODACRYLIC STAPLE FIBRES : OTHER : OTHER",
+        "hsn_code": "55122990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING 85% OR MORE BY WEIGHT OF\nSYNTHETIC STAPLE FIBRES - CONTAINING 85% OR MORE BY WEIGHT OF ACRYLIC OR\nMODACRYLIC STAPLE FIBRES : OTHER : PRINTED",
+        "hsn_code": "55122920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING 85% OR MORE BY WEIGHT OF\nSYNTHETIC STAPLE FIBRES - CONTAINING 85% OR MORE BY WEIGHT OF ACRYLIC OR\nMODACRYLIC STAPLE FIBRES : OTHER : DYED",
+        "hsn_code": "55122910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING 85% OR MORE BY WEIGHT OF\nSYNTHETIC STAPLE FIBRES - CONTAINING 85% OR MORE BY WEIGHT OF ACRYLIC OR\nMODACRYLIC STAPLE FIBRES : UNBLEACHED OR BLEACHED : BLEACHED",
+        "hsn_code": "55122120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING 85% OR MORE BY WEIGHT OF\nSYNTHETIC STAPLE FIBRES - CONTAINING 85% OR MORE BY WEIGHT OF ACRYLIC OR\nMODACRYLIC STAPLE FIBRES : UNBLEACHED OR BLEACHED : UNBLEACHED",
+        "hsn_code": "55122110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING 85% OR MORE BY WEIGHT OF\nSYNTHETIC STAPLE FIBRES - CONTAINING 85% OR MORE BY WEIGHT OF POLYESTER STAPLE\nFIBRES : OTHER : OTHER",
+        "hsn_code": "55121990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING 85% OR MORE BY WEIGHT OF\nSYNTHETIC STAPLE FIBRES - CONTAINING 85% OR MORE BY WEIGHT OF POLYESTER STAPLE\nFIBRES : OTHER : PRINTED",
+        "hsn_code": "55121920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING 85% OR MORE BY WEIGHT OF\nSYNTHETIC STAPLE FIBRES - CONTAINING 85% OR MORE BY WEIGHT OF POLYESTER STAPLE\nFIBRES : OTHER : DYED",
+        "hsn_code": "55121910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING 85% OR MORE BY WEIGHT OF\nSYNTHETIC STAPLE FIBRES - CONTAINING 85% OR MORE BY WEIGHT OF POLYESTER STAPLE\nFIBRES : UNBLEACHED OR BLEACHED : BLEACHED",
+        "hsn_code": "55121120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF SYNTHETIC STAPLE FIBRES, CONTAINING 85% OR MORE BY WEIGHT OF\nSYNTHETIC STAPLE FIBRES - CONTAINING 85% OR MORE BY WEIGHT OF POLYESTER STAPLE\nFIBRES : UNBLEACHED OR BLEACHED : UNBLEACHED",
+        "hsn_code": "55121110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN\nWOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN : OF\nPAPER YARN : OTHER",
+        "hsn_code": "53110029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN\nWOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN : OF\nPAPER YARN : PRINTED",
+        "hsn_code": "53110024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN\nWOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN : OF\nPAPER YARN : DYED",
+        "hsn_code": "53110023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN\nWOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN : OF\nPAPER YARN : BLEACHED",
+        "hsn_code": "53110022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN\nWOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN : OF\nPAPER YARN : UNBLEACHED",
+        "hsn_code": "53110021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN\nWOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN : OF\nOTHER VEGETABLE TEXTILE FIBRES : OTHER",
+        "hsn_code": "53110019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES - OF COIR INCLUDING LOG FORM AND\nGEOTEXTILES",
+        "hsn_code": "53110015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN\nWOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN : OF\nOTHER VEGETABLE TEXTILE FIBRES : PRINTED",
+        "hsn_code": "53110014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN\nWOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN : OF\nOTHER VEGETABLE TEXTILE FIBRES : DYED",
+        "hsn_code": "53110013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN\nWOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN : OF\nOTHER VEGETABLE TEXTILE FIBRES : BLEACHED",
+        "hsn_code": "53110012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN\nWOVEN FABRICS OF OTHER VEGETABLE TEXTILE FIBRES; WOVEN FABRICS OF PAPER YARN : OF\nOTHER VEGETABLE TEXTILE FIBRES : UNBLEACHED",
+        "hsn_code": "53110011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF JUTE OR OF OTHER TEXTILE BASE FIBRES OF HEADING 5303 OTHER :OTHER\n:OTHER",
+        "hsn_code": "53109099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF JUTE OR OF OTHER TEXTILE BASE FIBRES OF HEADING 5303 OTHER :OTHER\n:PRINTED",
+        "hsn_code": "53109093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF JUTE OR OF OTHER TEXTILE BASE FIBRES OF HEADING 5303 OTHER :OTHER\n:DYED",
+        "hsn_code": "53109092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF JUTE OR OF OTHER TEXTILE BASE FIBRES OF HEADING 5303 OTHER :OTHER :\nBLEACHED",
+        "hsn_code": "53109091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF JUTE OR OF OTHER TEXTILE BASE FIBRES OF HEADING 5303 OTHER\n:DECORATIVE FABRICS",
+        "hsn_code": "53109020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF JUTE OR OF OTHER TEXTILE BASE FIBRES OF HEADING 5303 OTHER :\nBLEACHED",
+        "hsn_code": "53109010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF JUTE OR OF OTHER TEXTILE BASE FIBRES OF HEADING 5303 UNBLEACHED :\nOTHER :OTHER",
+        "hsn_code": "53101099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF JUTE OR OF OTHER TEXTILE BASE FIBRES OF HEADING 5303 UNBLEACHED :\nOTHER :JUTE SWIM FABRICS",
+        "hsn_code": "53101093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF JUTE OR OF OTHER TEXTILE BASE FIBRES OF HEADING 5303 UNBLEACHED :\nOTHER :STRANDED WOVEN FABRICS OF JUTE CONTAINING 50% OR MORE BY WEIGHT OF JUTE",
+        "hsn_code": "53101092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF JUTE OR OF OTHER TEXTILE BASE FIBRES OF HEADING 5303 UNBLEACHED :\nOTHER : WOVEN BLENDED FABRICS CONTAINING MORE THAN 50% BY WEIGHT OF JUTE",
+        "hsn_code": "53101091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF JUTE OR OF OTHER TEXTILE BASE FIBRES OF HEADING 5303 UNBLEACHED :\nCONTAINING 100% BY WEIGHT OF JUTE : OTHER",
+        "hsn_code": "53101019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF JUTE OR OF OTHER TEXTILE BASE FIBRES OF HEADING 5303 UNBLEACHED :\nCONTAINING 100% BY WEIGHT OF JUTE : JUTE CANVAS",
+        "hsn_code": "53101014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF JUTE OR OF OTHER TEXTILE BASE FIBRES OF HEADING 5303 UNBLEACHED :\nCONTAINING 100% BY WEIGHT OF JUTE : HESSIAN FABRICS",
+        "hsn_code": "53101013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF JUTE OR OF OTHER TEXTILE BASE FIBRES OF HEADING 5303 UNBLEACHED :\nCONTAINING 100% BY WEIGHT OF JUTE : SACKING FABRICS",
+        "hsn_code": "53101012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF JUTE OR OF OTHER TEXTILE BASE FIBRES OF HEADING 5303 UNBLEACHED :\nCONTAINING 100% BY WEIGHT OF JUTE : CARPET BACKING FABRICS",
+        "hsn_code": "53101011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF FLAX CONTAINING LESS THAN 85% BY WEIGHT OF FLAX : OTHER :OTHER",
+        "hsn_code": "53092990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF FLAX CONTAINING LESS THAN 85% BY WEIGHT OF FLAX : OTHER :PRINTED",
+        "hsn_code": "53092920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF FLAX CONTAINING LESS THAN 85% BY WEIGHT OF FLAX : OTHER :DYED",
+        "hsn_code": "53092910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF FLAX CONTAINING LESS THAN 85% BY WEIGHT OF FLAX : UNBLEACHED OR\nBLEACHED : BLEACHED",
+        "hsn_code": "53092120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF FLAX CONTAINING LESS THAN 85% BY WEIGHT OF FLAX : UNBLEACHED OR\nBLEACHED : UNBLEACHED",
+        "hsn_code": "53092110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF FLAX CONTAINING 85% OR MORE BY WEIGHT OF FLAX : OTHER : OTHER",
+        "hsn_code": "53091990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF FLAX CONTAINING 85% OR MORE BY WEIGHT OF FLAX : OTHER : PRINTED",
+        "hsn_code": "53091920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF FLAX CONTAINING 85% OR MORE BY WEIGHT OF FLAX : OTHER : DYED",
+        "hsn_code": "53091910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF FLAX CONTAINING 85% OR MORE BY WEIGHT OF FLAX : UNBLEACHED OR\nBLEACHED : BLEACHED",
+        "hsn_code": "53091120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF FLAX CONTAINING 85% OR MORE BY WEIGHT OF FLAX : UNBLEACHED OR\nBLEACHED : UNBLEACHED",
+        "hsn_code": "53091110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF OTHER VEGETABLE TEXTILE FIBRES; PAPER YARN OTHER :OTHER",
+        "hsn_code": "53089090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF OTHER VEGETABLE TEXTILE FIBRES; PAPER YARN OTHER : RAMIE YARN",
+        "hsn_code": "53089010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF OTHER VEGETABLE TEXTILE FIBRES; PAPER YARN TRUE HEMP YARN",
+        "hsn_code": "53082000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF OTHER VEGETABLE TEXTILE FIBRES; PAPER YARN COIR YARN :OTHER",
+        "hsn_code": "53081090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SPOOLED HANKS",
+        "hsn_code": "53081020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF OTHER VEGETABLE TEXTILE FIBRES; PAPER YARN COIR YARN :BALED",
+        "hsn_code": "53081010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF JUTE OR OF OTHER TEXTILE BAST FIBRES OF HEADING 5303 MULTIPLE (FOLDED) OR\nCABLED",
+        "hsn_code": "53072000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF JUTE OR OF OTHER TEXTILE BAST FIBRES OF HEADING 5303 SINGLE : OTHER",
+        "hsn_code": "53071090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF JUTE OR OF OTHER TEXTILE BAST FIBRES OF HEADING 5303 SINGLE : OF JUTE",
+        "hsn_code": "53071010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAX YARN MULTIPLE (FOLDED) OR CABLED :OTHER",
+        "hsn_code": "53062090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAX YARN MULTIPLE (FOLDED) OR CABLED :PUT UP FOR RETAIL SALE",
+        "hsn_code": "53062010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAX YARN SINGLE :OTHER",
+        "hsn_code": "53061090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAX YARN SINGLE : PUT UP FOR RETAIL SALE",
+        "hsn_code": "53061010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUT, ABACA (MANILA HEMP OR MUSA TEXTILIS NEE), RAMIE AND OTHER VEGETABLE\nTEXTILE FIBRES, NOT ELSEWHERE SPECIFIED OR INCLUDED, RAW OR PROCESSED BUT NOT SPUN;\nTOW, NOILS AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND GARNETTED STOCK)\nOTHER :OTHER",
+        "hsn_code": "53059090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUT, ABACA (MANILA HEMP OR MUSA TEXTILIS NEE), RAMIE AND OTHER VEGETABLE\nTEXTILE FIBRES, NOT ELSEWHERE SPECIFIED OR INCLUDED, RAW OR PROCESSED BUT NOT SPUN;\nTOW, NOILS AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND GARNETTED STOCK)\nOTHER : RAMIE OR CHINA GRASS",
+        "hsn_code": "53059010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUT, ABACA (MANILA HEMP OR MUSA TEXTILIS NEE), RAMIE AND OTHER VEGETABLE\nTEXTILE FIBRES, NOT ELSEWHERE SPECIFIED OR INCLUDED, RAW OR PROCESSED BUT NOT SPUN;\nTOW, NOILS AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND GARNETTED STOCK)\nOF ABACA :OTHER",
+        "hsn_code": "53052900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUT, ABACA (MANILA HEMP OR MUSA TEXTILIS NEE), RAMIE AND OTHER VEGETABLE\nTEXTILE FIBRES, NOT ELSEWHERE SPECIFIED OR INCLUDED, RAW OR PROCESSED BUT NOT SPUN;\nTOW, NOILS AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND GARNETTED STOCK)\nOF ABACA : RAW",
+        "hsn_code": "53052100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUT, ABACA (MANILA HEMP OR MUSA TEXTILIS NEE), RAMIE AND OTHER VEGETABLE\nTEXTILE FIBRES, NOT ELSEWHERE SPECIFIED OR INCLUDED, RAW OR PROCESSED BUT NOT SPUN;\nTOW, NOILS AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND GARNETTED STOCK)\nOF COCONUT (COIR) :OTHER:",
+        "hsn_code": "53051900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUT, ABACA (MANILA HEMP OR MUSA TEXTILIS NEE), RAMIE AND OTHER VEGETABLE\nTEXTILE FIBRES, NOT ELSEWHERE SPECIFIED OR INCLUDED, RAW OR PROCESSED BUT NOT SPUN;\nTOW, NOILS AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND GARNETTED STOCK)\nOF COCONUT (COIR) :RAW : OTHER",
+        "hsn_code": "53051190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUT, ABACA (MANILA HEMP OR MUSA TEXTILIS NEE), RAMIE AND OTHER VEGETABLE\nTEXTILE FIBRES, NOT ELSEWHERE SPECIFIED OR INCLUDED, RAW OR PROCESSED BUT NOT SPUN;\nTOW, NOILS AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND GARNETTED STOCK)\nOF COCONUT (COIR) : RAW : COIR PITH",
+        "hsn_code": "53051140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUT, ABACA (MANILA HEMP OR MUSA TEXTILIS NEE), RAMIE AND OTHER VEGETABLE\nTEXTILE FIBRES, NOT ELSEWHERE SPECIFIED OR INCLUDED, RAW OR PROCESSED BUT NOT SPUN;\nTOW, NOILS AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND GARNETTED STOCK)\nOF COCONUT (COIR) : RAW : CURLED OR MACHINE TWISTED COIR FIBRE",
+        "hsn_code": "53051130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUT, ABACA (MANILA HEMP OR MUSA TEXTILIS NEE), RAMIE AND OTHER VEGETABLE\nTEXTILE FIBRES, NOT ELSEWHERE SPECIFIED OR INCLUDED, RAW OR PROCESSED BUT NOT SPUN;\nTOW, NOILS AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND GARNETTED STOCK)\nOF COCONUT (COIR) : RAW : COIR MATTRESS FIBRE",
+        "hsn_code": "53051120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUT, ABACA (MANILA HEMP OR MUSA TEXTILIS NEE), RAMIE AND OTHER VEGETABLE\nTEXTILE FIBRES, NOT ELSEWHERE SPECIFIED OR INCLUDED, RAW OR PROCESSED BUT NOT SPUN;\nTOW, NOILS AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND GARNETTED STOCK)\nOF COCONUT (COIR) : RAW : COIR BRISTLE FIBRE",
+        "hsn_code": "53051110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE TEXTILE FIBRES - OF OTHERS",
+        "hsn_code": "53050090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE TEXTILE FIRES - OF ABACA",
+        "hsn_code": "53050050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COIR PITH",
+        "hsn_code": "53050040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CURLED OR MACHINE TWISTED COIR FIBRE",
+        "hsn_code": "53050030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUT ,ABACA,RAMIE AND OTHER VEGETABLE TEXTILE FIBRES,NOT",
+        "hsn_code": "53050010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SISAL AND OTHER TEXTILE FIBRES OF THE GENUS AGAVE, RAW OR PROCESSED BUT NOT SPUN;\nTOW AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND GARNETTED STOCK) OTHER",
+        "hsn_code": "53049000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SISAL AND OTHER TEXTILE FIBRES OF THE GENUS AGAVE, RAW OR PROCESSED BUT NOT SPUN;\nTOW AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND GARNETTED STOCK) SISAL\nAND OTHER TEXTILE FIBRES OF THE GENUS AGAVE, RAW : OTHER",
+        "hsn_code": "53041090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SISAL AND OTHER TEXTILE FIBRES OF THE GENUS AGAVE, RAW OR PROCESSED BUT NOT SPUN;\nTOW AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND GARNETTED STOCK) SISAL\nAND OTHER TEXTILE FIBRES OF THE GENUS AGAVE, RAW : ALOE FIBRE",
+        "hsn_code": "53041020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SISAL AND OTHER TEXTILE FIBRES OF THE GENUS AGAVE, RAW OR PROCESSED BUT NOT SPUN;\nTOW AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND GARNETTED STOCK) SISAL\nAND OTHER TEXTILE FIBRES OF THE GENUS AGAVE, RAW : SISAL FIBRE",
+        "hsn_code": "53041010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "JUTE AND OTHER TEXTILE BAST FIBRES (EXCLUDING FLAX, TRUE HEMP AND RAMIE), RAW OR\nPROCESSED BUT NOT SPUN; TOW AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND\nGARNETTED STOCK) OTHER :OTHER",
+        "hsn_code": "53039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "JUTE AND OTHER TEXTILE BAST FIBRES (EXCLUDING FLAX, TRUE HEMP AND RAMIE), RAW OR\nPROCESSED BUT NOT SPUN; TOW AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND\nGARNETTED STOCK) OTHER : JUTE CUTTING",
+        "hsn_code": "53039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "JUTE AND OTHER TEXTILE BAST FIBRES (EXCLUDING FLAX, TRUE HEMP AND RAMIE), RAW OR\nPROCESSED BUT NOT SPUN; TOW AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND\nGARNETTED STOCK) JUTE AND OTHER TEXTILE BAST FIBRES, RAW OR RETTED :OTHER",
+        "hsn_code": "53031090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "JUTE AND OTHER TEXTILE BAST FIBRES (EXCLUDING FLAX, TRUE HEMP AND RAMIE), RAW OR\nPROCESSED BUT NOT SPUN; TOW AND WASTE OF THESE FIBRES (INCLUDING YARN WASTE AND\nGARNETTED STOCK) JUTE AND OTHER TEXTILE BAST FIBRES, RAW OR RETTED :JUTE, RAW OR\nRETTED",
+        "hsn_code": "53031010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF COTTON - WEIGHING MORE THAN 200 G/M2 : PRINTED",
+        "hsn_code": "52122500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF COTTON - WEIGHING MORE THAN 200 G/M2 : OF YARNS OF\nDIFFERENT COLOURS",
+        "hsn_code": "52122400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF COTTON - WEIGHING MORE THAN 200 G/M2 : DYED",
+        "hsn_code": "52122300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF COTTON - WEIGHING MORE THAN 200 G/M2 : BLEACHED",
+        "hsn_code": "52122200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF COTTON - WEIGHING MORE THAN 200 G/M2 : UNBLEACHED",
+        "hsn_code": "52122100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF COTTON - WEIGHING NOT MORE THAN 200 G/M2 : PRINTED",
+        "hsn_code": "52121500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF COTTON - WEIGHING NOT MORE THAN 200 G/M2 : OF YARNS OF\nDIFFERENT COLOURS",
+        "hsn_code": "52121400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF COTTON - WEIGHING NOT MORE THAN 200 G/M2 : DYED",
+        "hsn_code": "52121300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF COTTON - WEIGHING NOT MORE THAN 200 G/M2 : BLEACHED",
+        "hsn_code": "52121200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER WOVEN FABRICS OF COTTON - WEIGHING NOT MORE THAN 200 G/M2 : UNBLEACHED",
+        "hsn_code": "52121100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - PRINTED :\nOTHER FABRICS :OTHER",
+        "hsn_code": "52115990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - PRINTED :\nOTHER FABRICS :ZARI BORDERED SAREE",
+        "hsn_code": "52115910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - PRINTED : 3-\nTHREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :OTHER",
+        "hsn_code": "52115290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - PRINTED : 3-\nTHREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :TWILL, NOT ELSEWHERE SPECIFIED\n(INCLUDING GABERDINE)",
+        "hsn_code": "52115230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - PRINTED : 3-\nTHREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :SHIRTING FABRICS",
+        "hsn_code": "52115220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - PRINTED : 3-\nTHREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :CREPE FABRICS INCLUDING CREPE\nCHECKS",
+        "hsn_code": "52115210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - PRINTED :\nPLAIN WEAVE :OTHER",
+        "hsn_code": "52115190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - PRINTED :\nPLAIN WEAVE :SAREE",
+        "hsn_code": "52115150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - PRINTED :\nPLAIN WEAVE :LONG CLOTH (CHINTZ)",
+        "hsn_code": "52115140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - PRINTED :\nPLAIN WEAVE :FLANNELETTE",
+        "hsn_code": "52115130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - PRINTED :\nPLAIN WEAVE :FURNISHING FABRICS (EXCLUDING PILE AND CHENILLE FABRICS)",
+        "hsn_code": "52115120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - PRINTED :\nPLAIN WEAVE :SHIRTING FABRICS",
+        "hsn_code": "52115110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - OF YARNS OF\nDIFFERENT COLOURS : OTHER FABRICS :OTHER",
+        "hsn_code": "52114990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - OF YARNS OF\nDIFFERENT COLOURS : OTHER FABRICS :ZARI BORDERED SAREES",
+        "hsn_code": "52114910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - OF YARNS OF\nDIFFERENT COLOURS : OTHER FABRICS OF 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL : OTHER",
+        "hsn_code": "52114390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - OF YARNS OF\nDIFFERENT COLOURS : OTHER FABRICS OF 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL : SUITINGS",
+        "hsn_code": "52114340",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - OF YARNS OF\nDIFFERENT COLOURS : OTHER FABRICS OF 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL : SHIRTING FABRICS",
+        "hsn_code": "52114330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - OF YARNS OF\nDIFFERENT COLOURS : OTHER FABRICS OF 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL : CREPE FABRICS",
+        "hsn_code": "52114320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - OF YARNS OF\nDIFFERENT COLOURS : OTHER FABRICS OF 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL : BLEEDING MADRAS",
+        "hsn_code": "52114310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - OF YARNS OF\nDIFFERENT COLOURS : DENIM",
+        "hsn_code": "52114200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - OF YARNS OF\nDIFFERENT COLOURS : PLAIN WEAVE :OTHER",
+        "hsn_code": "52114190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - OF YARNS OF\nDIFFERENT COLOURS : PLAIN WEAVE :PARACHUTE FABRICS",
+        "hsn_code": "52114170",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - OF YARNS OF\nDIFFERENT COLOURS : PLAIN WEAVE :SAREE",
+        "hsn_code": "52114160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - OF YARNS OF\nDIFFERENT COLOURS : PLAIN WEAVE :FLANNELETTE",
+        "hsn_code": "52114150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - OF YARNS OF\nDIFFERENT COLOURS : PLAIN WEAVE :SUITINGS",
+        "hsn_code": "52114140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - OF YARNS OF\nDIFFERENT COLOURS : PLAIN WEAVE :SHIRTING",
+        "hsn_code": "52114130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - OF YARNS OF\nDIFFERENT COLOURS : PLAIN WEAVE :CHECK SHIRTING (EXCLUDING CREPE CHECKS)",
+        "hsn_code": "52114120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - OF YARNS OF\nDIFFERENT COLOURS : PLAIN WEAVE :BLEEDING MADRAS",
+        "hsn_code": "52114110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - DYED : OTHER\nFABRICS :OTHER",
+        "hsn_code": "52113990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - DYED : OTHER\nFABRICS :ZARI BORDERED SAREES",
+        "hsn_code": "52113910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - DYED : 3-\nTHREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :OTHER",
+        "hsn_code": "52113290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - DYED : 3-\nTHREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :TROUSERS OR PANT FABRICS\n(EXCLUDING JEANS AND CREPE)",
+        "hsn_code": "52113240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - DYED : 3-\nTHREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :TWILL, NOT ELSEWHERE SPECIFIED\n(INCLUDING GABERDINE)",
+        "hsn_code": "52113230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - DYED : 3-\nTHREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :SHIRTING FABRICS",
+        "hsn_code": "52113220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - DYED : 3-\nTHREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :CREPE FABRICS INCLUDING CREPE\nCHECKS",
+        "hsn_code": "52113210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - DYED : PLAIN\nWEAVE :OTHER",
+        "hsn_code": "52113190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - DYED : PLAIN\nWEAVE :SAREE",
+        "hsn_code": "52113150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - DYED : PLAIN\nWEAVE :FLANNELLETTE",
+        "hsn_code": "52113140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - DYED : PLAIN\nWEAVE :COATING (INCLUDING SUITINGS)",
+        "hsn_code": "52113130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - DYED : PLAIN\nWEAVE :CANVAS (INCLUDING DUCK) OF CARDED OR COMBED YARN",
+        "hsn_code": "52113120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - DYED : PLAIN\nWEAVE :SHIRTING FABRICS",
+        "hsn_code": "52113110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - BLEACHED\n:OTHER FABRICS :OTHER",
+        "hsn_code": "52112990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - BLEACHED\n:OTHER FABRICS :DEDSUTI, DOSUTI, CERETONNES AND OSAMBURGE",
+        "hsn_code": "52112920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - BLEACHED\n:OTHER FABRICS :ZARI BORDERED SARI",
+        "hsn_code": "52112910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - BLEACHED :3-\nTHREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :OTHER",
+        "hsn_code": "52112290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - BLEACHED :3-\nTHREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :TWILL FABRICS",
+        "hsn_code": "52112230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - BLEACHED :3-\nTHREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :SHIRTING FABRICS",
+        "hsn_code": "52112220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - BLEACHED :3-\nTHREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :CREPE FABRICS INCLUDING CREPE\nCHECKS",
+        "hsn_code": "52112210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - BLEACHED\n:PLAIN WEAVE :OTHER",
+        "hsn_code": "52112190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - BLEACHED\n:PLAIN WEAVE :SHIRTING FABRICS",
+        "hsn_code": "52112150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - BLEACHED\n:PLAIN WEAVE :SAREE",
+        "hsn_code": "52112140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - BLEACHED\n:PLAIN WEAVE :FLANNELETTE",
+        "hsn_code": "52112130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - BLEACHED\n:PLAIN WEAVE :CANVAS (INCLUDING DUCK) OF CARDED OR COMBED YARN",
+        "hsn_code": "52112120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - BLEACHED\n:PLAIN WEAVE :SHIRTING FABRICS",
+        "hsn_code": "52112110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER BLEACHED-OTHER",
+        "hsn_code": "52112099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER BLEACHED - DEDSUTTI, DOSUTI, CERETONNES AND OSAMBURGE",
+        "hsn_code": "52112092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER BLEACHED - ZARI BORDED SARI",
+        "hsn_code": "52112091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BLEACHED TWILL FABRICS",
+        "hsn_code": "52112060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BLEACHED CREPE FABRICS INCLUDING CREPE CHECKS",
+        "hsn_code": "52112050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BLEACHED SAREE",
+        "hsn_code": "52112040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BLEACHED FLANNELETTE",
+        "hsn_code": "52112030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BLEACHED CANVAS (INCLUDING DUCK) OF CARDED OR COMBED YARN",
+        "hsn_code": "52112020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BLEACHED SHIRTING FABRICS",
+        "hsn_code": "52112010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER FABRICS",
+        "hsn_code": "52111900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - UNBLEACHED\n:3-THREAD OR 4-THREAD TWILL,` INCLUDING CROSS TWILL :OTHER",
+        "hsn_code": "52111290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - UNBLEACHED\n:3-THREAD OR 4-THREAD TWILL,` INCLUDING CROSS TWILL :DAMASK",
+        "hsn_code": "52111230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - UNBLEACHED\n:3-THREAD OR 4-THREAD TWILL,` INCLUDING CROSS TWILL :TWILL, NOT ELSEWHERE SPECIFIED\n(INCLUDING GABERDINE)",
+        "hsn_code": "52111220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - UNBLEACHED\n:3-THREAD OR 4-THREAD TWILL,` INCLUDING CROSS TWILL :SHIRTING FABRICS",
+        "hsn_code": "52111210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - UNBLEACHED\n:PLAIN WEAVE :OTHER",
+        "hsn_code": "52111190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - UNBLEACHED\n:PLAIN WEAVE :SAREE",
+        "hsn_code": "52111120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING MORE THAN 200 G/M2 - UNBLEACHED\n:PLAIN WEAVE :SHIRTING FABRICS",
+        "hsn_code": "52111110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- PRINTED\n: OTHER FABRICS :OTHER",
+        "hsn_code": "52105990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- PRINTED\n: OTHER FABRICS :ZARI BORDERED SAREE",
+        "hsn_code": "52105910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- PRINTED\n: 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :OTHER",
+        "hsn_code": "52105290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- PRINTED\n: 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :SHIRTING FABRICS",
+        "hsn_code": "52105220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- PRINTED\n: 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :CREPE FABRICS INCLUDING CREPE\nCHECKS",
+        "hsn_code": "52105210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- PRINTED\n: PLAIN WEAVE :OTHER",
+        "hsn_code": "52105190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- PRINTED\n: PLAIN WEAVE :VOILS",
+        "hsn_code": "52105150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- PRINTED\n: PLAIN WEAVE :POPLIN AND BROAD FABRICS",
+        "hsn_code": "52105140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- PRINTED\n: PLAIN WEAVE :SAREE",
+        "hsn_code": "52105130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- PRINTED\n: PLAIN WEAVE :CASEMENT",
+        "hsn_code": "52105120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- PRINTED\n: PLAIN WEAVE :SHIRTING FABRICS",
+        "hsn_code": "52105110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- OF\nYARNS OF DIFFERENT COLOURS : OTHER FABRICS :OTHER",
+        "hsn_code": "52104990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- OF\nYARNS OF DIFFERENT COLOURS : OTHER FABRICS :ZARI BORDERED SAREE",
+        "hsn_code": "52104910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- OF\nYARNS OF DIFFERENT COLOURS : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL\n:OTHER",
+        "hsn_code": "52104290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- OF\nYARNS OF DIFFERENT COLOURS : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL\n:SHIRTINGS (INCLUDING MAZRI)",
+        "hsn_code": "52104260",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- OF\nYARNS OF DIFFERENT COLOURS : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL\n:BEDTICKING, DAMASK",
+        "hsn_code": "52104250",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- OF\nYARNS OF DIFFERENT COLOURS : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL\n:SUITINGS",
+        "hsn_code": "52104240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- OF\nYARNS OF DIFFERENT COLOURS : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL\n:SHIRTING FABRICS",
+        "hsn_code": "52104230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- OF\nYARNS OF DIFFERENT COLOURS : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL\n:CREPE FABRICS INCLUDING CREPE CHECKS",
+        "hsn_code": "52104220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- OF\nYARNS OF DIFFERENT COLOURS : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL\n:BLEEDING MADRAS",
+        "hsn_code": "52104210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- OF\nYARNS OF DIFFERENT COLOURS : PLAIN WEAVE :OTHER",
+        "hsn_code": "52104190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- OF\nYARNS OF DIFFERENT COLOURS : PLAIN WEAVE :VOILS",
+        "hsn_code": "52104170",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- OF\nYARNS OF DIFFERENT COLOURS : PLAIN WEAVE :SAREE",
+        "hsn_code": "52104160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- OF\nYARNS OF DIFFERENT COLOURS : PLAIN WEAVE :POPLIN AND BROAD FABRICS",
+        "hsn_code": "52104150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- OF\nYARNS OF DIFFERENT COLOURS : PLAIN WEAVE :SUITINGS",
+        "hsn_code": "52104140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- OF\nYARNS OF DIFFERENT COLOURS : PLAIN WEAVE :SHIRTING FABRICS",
+        "hsn_code": "52104130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- OF\nYARNS OF DIFFERENT COLOURS : PLAIN WEAVE :CREPE FABRICS (EXCLUDING CREPE CHECKS)",
+        "hsn_code": "52104120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- OF\nYARNS OF DIFFERENT COLOURS : PLAIN WEAVE :BLEEDING MADRAS",
+        "hsn_code": "52104110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- DYED :\nOTHER FABRICS :OTHER",
+        "hsn_code": "52103990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- DYED :\nOTHER FABRICS :ZARI BORDERED SAREE",
+        "hsn_code": "52103910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- DYED : 3-\nTHREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :OTHER",
+        "hsn_code": "52103239",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- DYED : 3-\nTHREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :BEDTICKING, DAMASK",
+        "hsn_code": "52103230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- DYED : 3-\nTHREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :SHIRTING FABRICS",
+        "hsn_code": "52103220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- DYED : 3-\nTHREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :CREPE FABRICS INCLUDING CREPE\nCHECKS",
+        "hsn_code": "52103210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- DYED :\nPLAIN WEAVE :OTHER",
+        "hsn_code": "52103190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- DYED :\nPLAIN WEAVE :VOILS",
+        "hsn_code": "52103160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- DYED :\nPLAIN WEAVE :SAREE",
+        "hsn_code": "52103150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- DYED :\nPLAIN WEAVE :POPLIN AND BROAD FABRICS",
+        "hsn_code": "52103140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- DYED :\nPLAIN WEAVE :FURNISHING FABRICS (EXCLUDING PILE AND CHENILLE FABRICS)",
+        "hsn_code": "52103130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- DYED :\nPLAIN WEAVE :COATING (INCLUDING SUITINGS)",
+        "hsn_code": "52103120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2- DYED :\nPLAIN WEAVE :SHIRTING FABRICS",
+        "hsn_code": "52103110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nBLEACHED :OTHER FABRICS :OTHER",
+        "hsn_code": "52102990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nBLEACHED :OTHER FABRICS :DEDSUTI, DOSUTI, CERETONNES AND OSAMBURGE",
+        "hsn_code": "52102920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nBLEACHED :OTHER FABRICS :DHOTI AND SAREE, ZARI BORDERED",
+        "hsn_code": "52102910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nBLEACHED :3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL : OTHER : OTHER",
+        "hsn_code": "52102229",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nBLEACHED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL : OTHER : :SHIRTING\n(INCLUDING MAZRI)",
+        "hsn_code": "52102221",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nBLEACHED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL : - HANDLOOM :OTHER\nFABRICS",
+        "hsn_code": "52102219",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nBLEACHED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL : - HANDLOOM :SHIRTING\nFABRICS",
+        "hsn_code": "52102212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nBLEACHED :3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL : - HANDLOOM :CREPE\nFABRICS INCLUDING CREPE CHECKS",
+        "hsn_code": "52102211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nBLEACHED :PLAIN WEAVE :OTHER",
+        "hsn_code": "52102190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nBLEACHED :PLAIN WEAVE :VOILE",
+        "hsn_code": "52102150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nBLEACHED :PLAIN WEAVE :SHIRTING (INCLUDING MAZRI)",
+        "hsn_code": "52102140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nBLEACHED :PLAIN WEAVE :SAREE",
+        "hsn_code": "52102130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nBLEACHED :PLAIN WEAVE :POPLIN AND BROAD FABRICS",
+        "hsn_code": "52102120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nBLEACHED :PLAIN WEAVE :SHIRTING FABRICS",
+        "hsn_code": "52102110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nUNBLEACHED : OTHER FABRICS",
+        "hsn_code": "52101900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nUNBLEACHED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :OTHER",
+        "hsn_code": "52101290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nUNBLEACHED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :SHIRTING FABRICS",
+        "hsn_code": "52101210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nUNBLEACHED : PLAIN WEAVE :OTHER",
+        "hsn_code": "52101190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nUNBLEACHED : PLAIN WEAVE :SAREE",
+        "hsn_code": "52101120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING LESS THAN 85% BY WEIGHT OF COTTON, MIXED\nMAINLY OR SOLELY WITH MAN-MADE FIBRES, WEIGHING NOT MORE THAN 200 G/M2-\nUNBLEACHED : PLAIN WEAVE :SHIRTING FABRICS",
+        "hsn_code": "52101110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- PRINTED : OTHER FABRICS :OTHER",
+        "hsn_code": "52095990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- PRINTED : OTHER FABRICS :ZARI BORDERED SAREE",
+        "hsn_code": "52095910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- PRINTED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL\n:OTHER",
+        "hsn_code": "52095290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- PRINTED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL\n:FURNISHING FABRICS (EXCLUDING PILE AND CHENILLE FABRICS)",
+        "hsn_code": "52095220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- PRINTED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL\n:SHIRTING FABRICS",
+        "hsn_code": "52095210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- PRINTED : PLAIN WEAVE :OTHER",
+        "hsn_code": "52095190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- PRINTED : PLAIN WEAVE :FLANNELETTE",
+        "hsn_code": "52095170",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- PRINTED : PLAIN WEAVE :BEDTICKING, DOMESTIC",
+        "hsn_code": "52095160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- PRINTED : PLAIN WEAVE :SEERSUCKER",
+        "hsn_code": "52095150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- PRINTED : PLAIN WEAVE :FURNISHING FABRICS (EXCLUDING PILE AND\nCHENILLE FABRICS)",
+        "hsn_code": "52095140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- PRINTED : PLAIN WEAVE :SHIRTING FABRICS",
+        "hsn_code": "52095130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- PRINTED : PLAIN WEAVE :SAREE",
+        "hsn_code": "52095120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LUNGIS - OTHER",
+        "hsn_code": "52095119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LUNGIS OF HANDLOOM",
+        "hsn_code": "52095111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- - OF YARNS OF DIFFERENT COLOURS : OTHER FABRICS :OTHER",
+        "hsn_code": "52094990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- - OF YARNS OF DIFFERENT COLOURS : OTHER FABRICS :ZARI BORDERED\nSARI",
+        "hsn_code": "52094910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- - OF YARNS OF DIFFERENT COLOURS : OTHER FABRICS OF 3-THREAD OR\n4-THREAD TWILL, INCLUDING CROSS TWILL : OTHER",
+        "hsn_code": "52094390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- - OF YARNS OF DIFFERENT COLOURS : OTHER FABRICS OF 3-THREAD OR\n4-THREAD TWILL, INCLUDING CROSS TWILL : COATING (INCLUDING SUITING)",
+        "hsn_code": "52094340",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- - OF YARNS OF DIFFERENT COLOURS : OTHER FABRICS OF 3-THREAD OR\n4-THREAD TWILL, INCLUDING CROSS TWILL : FURNISHING FABRICS (EXCLUDING PILE AND\nCHENILLE FABRICS)",
+        "hsn_code": "52094330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- - OF YARNS OF DIFFERENT COLOURS : OTHER FABRICS OF 3-THREAD OR\n4-THREAD TWILL, INCLUDING CROSS TWILL : SHIRTING FABRICS",
+        "hsn_code": "52094320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- - OF YARNS OF DIFFERENT COLOURS : OTHER FABRICS OF 3-THREAD OR\n4-THREAD TWILL, INCLUDING CROSS TWILL : BLEEDING MADRAS",
+        "hsn_code": "52094310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- - OF YARNS OF DIFFERENT COLOURS : DENIM",
+        "hsn_code": "52094200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- - OF YARNS OF DIFFERENT COLOURS : PLAIN WEAVE :OTHER",
+        "hsn_code": "52094190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- - OF YARNS OF DIFFERENT COLOURS : PLAIN WEAVE :FLANNELETTE",
+        "hsn_code": "52094170",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- - OF YARNS OF DIFFERENT COLOURS : PLAIN WEAVE :BEDTICKING,\nDOMESTIC (OTHER THAN HAND DYED)",
+        "hsn_code": "52094160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- - OF YARNS OF DIFFERENT COLOURS : PLAIN WEAVE :SEERSUCKER",
+        "hsn_code": "52094150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- - OF YARNS OF DIFFERENT COLOURS : PLAIN WEAVE :FURNISHING\nFABRICS (EXCLUDING PILE AND CHENILLE FABRICS)",
+        "hsn_code": "52094140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- - OF YARNS OF DIFFERENT COLOURS : PLAIN WEAVE :SHIRTING FABRICS",
+        "hsn_code": "52094130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- - OF YARNS OF DIFFERENT COLOURS : PLAIN WEAVE :SAREE",
+        "hsn_code": "52094120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- - OF YARNS OF DIFFERENT COLOURS : PLAIN WEAVE :BLEEDING\nMADRAS",
+        "hsn_code": "52094110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- DYED : OTHER FABRICS :OTHER",
+        "hsn_code": "52093990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- DYED : OTHER FABRICS :ZARI BORDERED SAREES",
+        "hsn_code": "52093910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- DYED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL\n:OTHER",
+        "hsn_code": "52093290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- DYED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL :DRILL",
+        "hsn_code": "52093230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- DYED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL\n:FURNISHING FABRICS(EXCLUDING PILE AND CHENILLE FABRICS)",
+        "hsn_code": "52093220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- DYED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL\n:SHIRTING FABRICS",
+        "hsn_code": "52093210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- DYED : PLAIN WEAVE :OTHER",
+        "hsn_code": "52093190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- DYED : PLAIN WEAVE :FLANNELLETE",
+        "hsn_code": "52093180",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- DYED : PLAIN WEAVE :CANVAS (INCLUDING DUCK), OF CARDED OR\nCOMBED YARN",
+        "hsn_code": "52093170",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- DYED : PLAIN WEAVE :BEDTICKING, DOMESTIC(OTHER THAN HAND\nDYED)",
+        "hsn_code": "52093160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- DYED : PLAIN WEAVE :SEERSUCKER",
+        "hsn_code": "52093150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- DYED : PLAIN WEAVE :FURNISHING FABRICS(EXCLUDING PILE AND\nCHENILLE FABRICS)",
+        "hsn_code": "52093140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- DYED : PLAIN WEAVE :SHIRTING FABRICS",
+        "hsn_code": "52093130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- DYED : PLAIN WEAVE :SAREE",
+        "hsn_code": "52093120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2- DYED : PLAIN WEAVE :LUNGI",
+        "hsn_code": "52093110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - BLEACHED : OTHER FABRICS :OTHER",
+        "hsn_code": "52092990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - BLEACHED : OTHER FABRICS :DEDSUTI, DOSUTI FABRICS, CERETONNES\nAND OSAMBURGE",
+        "hsn_code": "52092920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - BLEACHED : OTHER FABRICS :DHOTI AND SAREE, ZARI BORDERED",
+        "hsn_code": "52092910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - BLEACHED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL\n:OTHER",
+        "hsn_code": "52092290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - BLEACHED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL\n:DRILL",
+        "hsn_code": "52092230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - BLEACHED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL\n:FURNISHING FABRICS (EXCLUDING PILE AND CHENILLE FABRICS)",
+        "hsn_code": "52092220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - BLEACHED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS TWILL\n:SHIRTING FABRICS",
+        "hsn_code": "52092210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - BLEACHED : PLAIN WEAVE :OTHER",
+        "hsn_code": "52092190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - BLEACHED : PLAIN WEAVE :SHEETING( TAKIA, LEOPARDCLOTH)",
+        "hsn_code": "52092180",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - BLEACHED : PLAIN WEAVE :FLANNELLETE",
+        "hsn_code": "52092170",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - BLEACHED : PLAIN WEAVE :DHOTI",
+        "hsn_code": "52092160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - BLEACHED : PLAIN WEAVE : CANVAS (INCLUDING DUCK) OF CARDED OR\nCOMBED YARN",
+        "hsn_code": "52092150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - BLEACHED : PLAIN WEAVE : SEERSUCKER",
+        "hsn_code": "52092140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - BLEACHED : PLAIN WEAVE : FURNISHING FABRICS (EXCLUDING PILE\nAND CHENILLE FABRICS)",
+        "hsn_code": "52092130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - BLEACHED : PLAIN WEAVE : SHIRTING FABRICS",
+        "hsn_code": "52092120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - BLEACHED : PLAIN WEAVE :SAREE",
+        "hsn_code": "52092110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - UNBLEACHED :OTHER FABRICS",
+        "hsn_code": "52091900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - UNBLEACHED :3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL:OTHER",
+        "hsn_code": "52091290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - UNBLEACHED :3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL:SHETTING (TAKIA, LEOPARD CLOTH)",
+        "hsn_code": "52091270",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - UNBLEACHED :3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL:FLANNELETTE",
+        "hsn_code": "52091260",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - UNBLEACHED :3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL:CANVAS, INCLUDING DUCK CARDED OR COMBED YARN",
+        "hsn_code": "52091250",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - UNBLEACHED :3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL:SEERSUCKER",
+        "hsn_code": "52091240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - UNBLEACHED :3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL:FURNISHING FABRICS (EXCLUDING PILE AND CHENILLE FABRICS)",
+        "hsn_code": "52091230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - UNBLEACHED :3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL:SHIRTING FABRICS",
+        "hsn_code": "52091220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - UNBLEACHED :3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL:SAREE",
+        "hsn_code": "52091210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - UNBLEACHED :PLAIN WEAVE : OTHER",
+        "hsn_code": "52091190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - UNBLEACHED :PLAIN WEAVE : HANDLOOM : OTHER",
+        "hsn_code": "52091119",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - UNBLEACHED :PLAIN WEAVE : HANDLOOM : SHEETING (TAKIA,\nLEOPARD CLOTH AND OTHER THAN FURNISHING)",
+        "hsn_code": "52091114",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - UNBLEACHED :PLAIN WEAVE : HANDLOOM : CASEMENT",
+        "hsn_code": "52091113",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - UNBLEACHED :PLAIN WEAVE : HANDLOOM : SAREE",
+        "hsn_code": "52091112",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nMORE THAN 200 G/M2 - UNBLEACHED :PLAIN WEAVE : HANDLOOM : DHOTI",
+        "hsn_code": "52091111",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : OTHER FABRICS :OTHER",
+        "hsn_code": "52085990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER FABRICS, SAREES,HANDLOOM",
+        "hsn_code": "52085920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL :OTHER",
+        "hsn_code": "52085390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL :BEDTICKING",
+        "hsn_code": "52085320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL :SHIRTING FABRICS",
+        "hsn_code": "52085310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:OTHER",
+        "hsn_code": "52085290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2 :VOILS\n(EXCLUDING LENO FABRICS)",
+        "hsn_code": "52085280",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:MUSLIN (INCLUDING LAWN MULMUL AND ORGANDI) OF CARDED OR COMBED YARN",
+        "hsn_code": "52085270",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2 :MULL\n(INCLUDING LIMBRIC AND WILLAYA)",
+        "hsn_code": "52085260",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:CAMBRICS (INCLUDING MADAPOLLAM AND JACONET)",
+        "hsn_code": "52085250",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:CASEMENT",
+        "hsn_code": "52085240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:SHIRTING FABRICS",
+        "hsn_code": "52085230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:SAREE",
+        "hsn_code": "52085220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:LUNGI",
+        "hsn_code": "52085210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100 G/M2\n:OTHER",
+        "hsn_code": "52085190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100 G/M2\n:VOILS (EXCLUDING LENO FABRICS)",
+        "hsn_code": "52085180",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100 G/M2\n:MUSLIN (INCLUDING LAWN MULMUL AND ORGANDI) OF CARDED OR COMBED YARN",
+        "hsn_code": "52085170",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100 G/M2\n:MULL (INCLUDING LIMBRIC AND WILLAYA)",
+        "hsn_code": "52085160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100 G/M2\n:CAMBRICS (INCLUDING MADAPOLLAM AND JACONET)",
+        "hsn_code": "52085150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100 G/M2\n:CASEMENT",
+        "hsn_code": "52085140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100 G/M2\n:SHIRTING FABRICS",
+        "hsn_code": "52085130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100 G/M2\n:SAREE",
+        "hsn_code": "52085120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- PRINTED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100 G/M2\n:LUNGI",
+        "hsn_code": "52085110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : OTHER FABRICS :OTHER",
+        "hsn_code": "52084990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "REAL MADRAS HANDKERCHIEFS - OTHER THAN HANDLOOM",
+        "hsn_code": "52084929",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "REAL MADRAS HANDKERCHIEFS OF HANDLOOM",
+        "hsn_code": "52084921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : OTHER FABRICS :ZARI\nBORDERED SAREES",
+        "hsn_code": "52084910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : 3-THREAD OR 4- THREAD\nTWILL, INCLUDING CROSS TWILL :OTHER",
+        "hsn_code": "52084390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : 3-THREAD OR 4- THREAD\nTWILL, INCLUDING CROSS TWILL :FLANNELETTE",
+        "hsn_code": "52084340",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : 3-THREAD OR 4- THREAD\nTWILL, INCLUDING CROSS TWILL :BEDTICKING, DAMASK",
+        "hsn_code": "52084330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : 3-THREAD OR 4- THREAD\nTWILL, INCLUDING CROSS TWILL :SHIRTING FABRICS",
+        "hsn_code": "52084320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : 3-THREAD OR 4- THREAD\nTWILL, INCLUDING CROSS TWILL :BLEADING MADRAS",
+        "hsn_code": "52084310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : PLAIN WEAVE, WEIGHING\nMORE THAN 100 G/M2 : OTHER",
+        "hsn_code": "52084290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : PLAIN WEAVE, WEIGHING\nMORE THAN 100 G/M2 : FURNISHING FABRICS, OTHER THAN PILE AND CHENILLE FABRIC",
+        "hsn_code": "52084260",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : PLAIN WEAVE, WEIGHING\nMORE THAN 100 G/M2 :BED TICKING, DOMESTIC",
+        "hsn_code": "52084250",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : PLAIN WEAVE, WEIGHING\nMORE THAN 100 G/M2 :CASEMENT",
+        "hsn_code": "52084240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : PLAIN WEAVE, WEIGHING\nMORE THAN 100 G/M2 :SHIRTING FABRICS",
+        "hsn_code": "52084230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : PLAIN WEAVE, WEIGHING\nMORE THAN 100 G/M2 :SAREE",
+        "hsn_code": "52084220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : PLAIN WEAVE, WEIGHING\nMORE THAN 100 G/M2 :BLEEDING MADRAS",
+        "hsn_code": "52084210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : PLAIN WEAVE, WEIGHING NOT\nMORE THAN 100 G/M2 :OTHER",
+        "hsn_code": "52084190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : PLAIN WEAVE, WEIGHING NOT\nMORE THAN 100 G/M2 :FURNISHING FABRICS (EXCLUDING PILE AND CHENILLE FABRICS)",
+        "hsn_code": "52084150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : PLAIN WEAVE, WEIGHING NOT\nMORE THAN 100 G/M2 :BED TICKING, DOMESTIC",
+        "hsn_code": "52084140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : PLAIN WEAVE, WEIGHING NOT\nMORE THAN 100 G/M2 :SHIRTING FABRICS",
+        "hsn_code": "52084130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OF YARN OF DIFFERENT COLOURS - PALIN WEAVE,WEIGHING NOT MORE THAN 100 G PER M2 -\nSAREES OTHER THAN HANDLOOM",
+        "hsn_code": "52084129",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OF YARN OF DIFFERENT COLOURS - PALIN WEAVE,WEIGHING NOT MORE THAN 100 G PER M2 -\nSAREES OF HANDLOOM",
+        "hsn_code": "52084121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : PLAIN WEAVE, WEIGHING NOT\nMORE THAN 100 G/M2 :SAREE",
+        "hsn_code": "52084120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- OF YARN OF DIFFERENT COLOURS : PLAIN WEAVE, WEIGHING NOT\nMORE THAN 100 G/M2 :BLEEDING MADRAS",
+        "hsn_code": "52084110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : OTHER FABRICS :OTHER",
+        "hsn_code": "52083990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : OTHER FABRICS :ZARI BORDERED SAREES",
+        "hsn_code": "52083910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : 3-THREAD OR 4- THREAD TWILL, INCLUDING CROSS TWILL\n:OTHER",
+        "hsn_code": "52083390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : 3-THREAD OR 4- THREAD TWILL, INCLUDING CROSS TWILL\n:SHIRTING (INCLUDING MAZRI)",
+        "hsn_code": "52083330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : 3-THREAD OR 4- THREAD TWILL, INCLUDING CROSS TWILL\n:COATING (INCLUDING SUITING)",
+        "hsn_code": "52083320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : 3-THREAD OR 4- THREAD TWILL, INCLUDING CROSS TWILL\n:SHIRTING FABRICS",
+        "hsn_code": "52083310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/ M2:OTHER",
+        "hsn_code": "52083290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/\nM2:FURNISHING FABRICS( EXCLUDING PILE AND CHENILLE FABRICS)",
+        "hsn_code": "52083280",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/ M2:COATING\n(INCLUDING SUITING)",
+        "hsn_code": "52083270",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/\nM2:CAMBRICS (INCLUDING MADAPOLLAM AND JACONET), LONG CLOTH(INCLUDING CALICO)\nAND VOILS (EXCLUDING LENO FABRICS)",
+        "hsn_code": "52083260",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/\nM2:BEDTICKING, DOMESTIC",
+        "hsn_code": "52083250",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/\nM2:CASEMENT",
+        "hsn_code": "52083240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/\nM2:SHIRTING FABRICS",
+        "hsn_code": "52083230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/ M2:SAREE",
+        "hsn_code": "52083220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/ M2:LUNGI",
+        "hsn_code": "52083210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2:OTHER",
+        "hsn_code": "52083190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2:VOILS (EXCLUDING LENO FABRICS)",
+        "hsn_code": "52083180",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2:MUSLIN (INCLUDING LAWN MULMUL AND ORGANDI) OF CARDED OR COMBED YARN",
+        "hsn_code": "52083170",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2:MULL (INCLUDING LIMBRIC AND WILLAYA)",
+        "hsn_code": "52083160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2:CAMBRICS (INCLUDING MADAPOLLAM AND JACONET)",
+        "hsn_code": "52083150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2:CASEMENT",
+        "hsn_code": "52083140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2:SHIRTING FABRICS",
+        "hsn_code": "52083130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DYED - PLAIN WEAVE, WEIGHING NOT MORE THAN 100 G PER M2 -SAREES OTHER THAN\nHANDLOOM",
+        "hsn_code": "52083129",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DYED-PLAIN WEAVE, WEIGHING NOT MORE THAN 100 G PER M2 - SAREES OF HANDLOOM",
+        "hsn_code": "52083121",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- DYED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2:LUNGI",
+        "hsn_code": "52083110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : OTHER FABRICS :OTHER",
+        "hsn_code": "52082990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : OTHER FABRICS :DEDSUTI, DOSUTI FABRICS,\nCERETONNES AND OSAMBURGE",
+        "hsn_code": "52082920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : OTHER FABRICS :DHOTI AND SAREE, ZARI BORDERED",
+        "hsn_code": "52082910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL :OTHER",
+        "hsn_code": "52082390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL :SHIRTING FABRICS",
+        "hsn_code": "52082330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL :PARMATTA FABRICS (INCLUDING ILESIA, POCKETING, ITALIAN TWILL)",
+        "hsn_code": "52082320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL :SHIRTING FABRICS",
+        "hsn_code": "52082310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:OTHER",
+        "hsn_code": "52082290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:VOILS (EXCLUDING LENO FABRICS)",
+        "hsn_code": "52082280",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:SHEETING (TAKIA AND THE LIKE)",
+        "hsn_code": "52082270",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:LONG CLOTH (INCLUDING CALICO)",
+        "hsn_code": "52082260",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:CAMBRICS (INCLUDING MADAPOLLAM AND JACONET)",
+        "hsn_code": "52082250",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:CASEMENT",
+        "hsn_code": "52082240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:SHIRTING FABRICS",
+        "hsn_code": "52082230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:SAREE",
+        "hsn_code": "52082220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:DHOTI",
+        "hsn_code": "52082210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2 : OTHER",
+        "hsn_code": "52082190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2 : VOILS (EXCLUDING LENO FABRICS)",
+        "hsn_code": "52082180",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2 : MUSLIN (INCLUDING LAWN, MULMUL AND ORGANDI)",
+        "hsn_code": "52082170",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2 : MULLS (INCLUDING LIMBRIC AND WILLAYA)",
+        "hsn_code": "52082160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2 : CAMBRICS (INCLUDING MADAPOLLAM AND JACONET)",
+        "hsn_code": "52082150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2 : SHIRTING FABRICS",
+        "hsn_code": "52082140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2 : CASEMENT",
+        "hsn_code": "52082130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2 : SAREE",
+        "hsn_code": "52082120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - BLEACHED : PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2 : DHOTI",
+        "hsn_code": "52082110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - UNBLEACHED: OTHER FABRICS : OTHER",
+        "hsn_code": "52081990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - UNBLEACHED: OTHER FABRICS : DEDSUTI, DOSUTI FABRICS",
+        "hsn_code": "52081910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - UNBLEACHED: 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL : OTHER",
+        "hsn_code": "52081390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - UNBLEACHED: 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL : DOBBY FABRICS",
+        "hsn_code": "52081320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - UNBLEACHED: 3-THREAD OR 4-THREAD TWILL, INCLUDING CROSS\nTWILL : SHIRTING FABRICS",
+        "hsn_code": "52081310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - UNBLEACHED: PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:OTHER",
+        "hsn_code": "52081290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - UNBLEACHED: PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:VOILS",
+        "hsn_code": "52081260",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - UNBLEACHED: PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:SHEETING(TAKIA, LEOPARD FABRICS, OTHER THAN FURNISHING FABRICS)",
+        "hsn_code": "52081250",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - UNBLEACHED: PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:CASEMENT",
+        "hsn_code": "52081240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - UNBLEACHED: PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:SHIRTING FABRICS",
+        "hsn_code": "52081230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - UNBLEACHED: PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:SAREE",
+        "hsn_code": "52081220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - UNBLEACHED: PLAIN WEAVE, WEIGHING MORE THAN 100 G/M2\n:DHOTI",
+        "hsn_code": "52081210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - UNBLEACHED: PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2 : OTHER",
+        "hsn_code": "52081190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - UNBLEACHED: PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2 : CASEMENT",
+        "hsn_code": "52081140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - UNBLEACHED: PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2 : SHIRTING FABRICS",
+        "hsn_code": "52081130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - UNBLEACHED: PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2 : SAREE",
+        "hsn_code": "52081120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COTTON, CONTAINING 85% OR MORE BY WEIGHT OF COTTON, WEIGHING\nNOT MORE THAN 200 G/M2- - UNBLEACHED: PLAIN WEAVE, WEIGHING NOT MORE THAN 100\nG/M2 : DHOTI",
+        "hsn_code": "52081110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD) PUT UP FOR RETAIL SALE OTHER",
+        "hsn_code": "52079000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD) PUT UP FOR RETAIL SALE CONTAINING 85% OR\nMORE BY WEIGHT OF COTTON",
+        "hsn_code": "52071000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 125 DECITEX (EXCEEDING 80 METRIC\nNUMBER PER SINGLE YARN)",
+        "hsn_code": "52064500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 192.31 DECITEX BUT NOT LESS THAN 125\nDECITEX(EXCEEDING 52 METRIC NUMBER BUT NOT EXCEEDING 80 METRIC NUMBER PER SINGLE\nYARN)",
+        "hsn_code": "52064400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 232.56 DECITEX BUT NOT LESS THAN 192.31\nDECITEX (EXCEEDING 43 METRIC NUMBER BUT NOT EXCEEDING 52 METRIC NUMBER PER SINGLE\nYARN)",
+        "hsn_code": "52064300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 714.29 DECITEX BUT NOT LESS THAN 232.56\nDECITEX(EXCEEDING 14 METRIC NUMBER BUT NOT EXCEEDING43 METRIC NUMBER PER SINGLE\nYARN)",
+        "hsn_code": "52064200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN 714.29 DECITEX OR MORE (NOT EXCEEDING 14 METRIC\nNUMBERPER SINGLE YARN)",
+        "hsn_code": "52064100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 125 DECITEX (EXCEEDING 80 METRIC\nNUMBERPER SINGLE YARN)",
+        "hsn_code": "52063500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 192.31 DECITEX BUT NOT LESS THAN 125\nDECITEX(EXCEEDING 52 METRIC NUMBER BUT NOTEXCEEDING 80 METRIC NUMBER PER SINGLE\nYARN)",
+        "hsn_code": "52063400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 232.56 DECITEX BUT NOT LESS THAN 192.31\nDECITEX(EXCEEDING 43 METRIC NUMBER BUT NOT EXCEEDING 52 METRIC NUMBER PER SINGLE\nYARN)",
+        "hsn_code": "52063300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 714.29 DECITEX BUT NOT LESS THAN 232.56\nDECITEX(EXCEEDING 14 METRIC NUMBER BUT NOT EXCEEDING 43 METRIC NUMBER PER SINGLE\nYARN)",
+        "hsn_code": "52063200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN 714.29 DECITEX OR MORE (NOT EXCEEDING 14 METRIC\nNUMBER PER SINGLE YARN)",
+        "hsn_code": "52063100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 125 DECITEX (EXCEEDING 80 METRIC NUMBER)",
+        "hsn_code": "52062500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 192.31 DECITEX BUT NOT LESS THAN 125 DECITEX (EXCEEDING 52 METRICNUMBER BUT\nNOT EXCEEDING 80 METRIC NUMBER)",
+        "hsn_code": "52062400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 232.56 DECITEX BUT NOT LESS THAN 192.31 DECITEX(EXCEEDING 43 METRICNUMBER BUT\nNOT EXCEEDING 52 METRIC NUMBER)",
+        "hsn_code": "52062300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 714.29 DECITEX BUT NOT LESS THAN 232.56 DECITEX(EXCEEDING 14 METRICNUMBER BUT\nNOT EXCEEDING 43 METRIC NUMBER)",
+        "hsn_code": "52062200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING 714.29\nDECITEX OR MORE (NOT EXCEEDING 14 METRIC NUMBER)",
+        "hsn_code": "52062100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 125 DECITEX (EXCEEDING 80 METRIC NUMBER)",
+        "hsn_code": "52061500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 192.31 DECITEX BUT NOT LESS THAN 125 DECITEX (EXCEEDING 52 METRIC\nNUMBERBUT NOT EXCEEDING 80 METRIC NUMBER)",
+        "hsn_code": "52061400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 232.56 DECITEX BUT NOT LESS THAN 192.31 DECITEX (EXCEEDING 43\nMETRICNUMBER BUT NOT EXCEEDING 52 METRIC NUMBER)",
+        "hsn_code": "52061300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 714.29 DECITEX BUT NOT LESS THAN 232.56 DECITEX (EXCEEDING 14\nMETRICNUMBER BUT NOT EXCEEDING 43 METRIC NUMBER)",
+        "hsn_code": "52061200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING LESS THAN 85% BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\n714.29 DECITEX OR MORE (NOT EXCEEDING 14 METRIC NUMBER)",
+        "hsn_code": "52061100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 83.33 DECITEX (EXCEEDING 120 METRIC\nNUMBER PER SINGLE YARN) : OTHER",
+        "hsn_code": "52054890",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 83.33 DECITEX (EXCEEDING 120 METRIC\nNUMBER PER SINGLE YARN) : DYED",
+        "hsn_code": "52054830",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 83.33 DECITEX (EXCEEDING 120 METRIC\nNUMBER PER SINGLE YARN) : BLEACHED",
+        "hsn_code": "52054820",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 83.33 DECITEX (EXCEEDING 120 METRIC\nNUMBER PER SINGLE YARN) : GREY",
+        "hsn_code": "52054810",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 106.38 DECITEX BUT NOT LESS THAN 83.33\nDECITEX (EXCEEDING 94 METRIC NUMBER BUT NOT EXCEEDING 120 METRIC 272 NUMBER PER\nSINGLE YARN) : OTHER",
+        "hsn_code": "52054790",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 106.38 DECITEX BUT NOT LESS THAN 83.33\nDECITEX (EXCEEDING 94 METRIC NUMBER BUT NOT EXCEEDING 120 METRIC 272 NUMBER PER\nSINGLE YARN) : DYED",
+        "hsn_code": "52054730",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 106.38 DECITEX BUT NOT LESS THAN 83.33\nDECITEX (EXCEEDING 94 METRIC NUMBER BUT NOT EXCEEDING 120 METRIC 272 NUMBER PER\nSINGLE YARN) : BLEACHED",
+        "hsn_code": "52054720",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 106.38 DECITEX BUT NOT LESS THAN 83.33\nDECITEX (EXCEEDING 94 METRIC NUMBER BUT NOT EXCEEDING 120 METRIC 272 NUMBER PER\nSINGLE YARN) : GREY",
+        "hsn_code": "52054710",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 125 DECITEX BUT NOT LESS THAN 106.38\nDECITEX (EXCEEDING 80 METRIC NUMBER BUT NOT EXCEEDING 94 METRIC NUMBER PER SINGLE\nYARN) :OTHER",
+        "hsn_code": "52054690",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 125 DECITEX BUT NOT LESS THAN 106.38\nDECITEX (EXCEEDING 80 METRIC NUMBER BUT NOT EXCEEDING 94 METRIC NUMBER PER SINGLE\nYARN) :DYED",
+        "hsn_code": "52054630",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 125 DECITEX BUT NOT LESS THAN 106.38\nDECITEX (EXCEEDING 80 METRIC NUMBER BUT NOT EXCEEDING 94 METRIC NUMBER PER SINGLE\nYARN) :BLEACHED",
+        "hsn_code": "52054620",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 125 DECITEX BUT NOT LESS THAN 106.38\nDECITEX (EXCEEDING 80 METRIC NUMBER BUT NOT EXCEEDING 94 METRIC NUMBER PER SINGLE\nYARN) :GREY",
+        "hsn_code": "52054610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 192.31 DECITEX BUT NOT LESS THAN 125\nDECITEX (EXCEEDING 52 METRIC NUMBER BUT NOT EXCEEDING 80 METRIC NUMBER PER SINGLE\nYARN) :OTHER",
+        "hsn_code": "52054490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 192.31 DECITEX BUT NOT LESS THAN 125\nDECITEX (EXCEEDING 52 METRIC NUMBER BUT NOT EXCEEDING 80 METRIC NUMBER PER SINGLE\nYARN) :BLEACHED",
+        "hsn_code": "52054420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 192.31 DECITEX BUT NOT LESS THAN 125\nDECITEX (EXCEEDING 52 METRIC NUMBER BUT NOT EXCEEDING 80 METRIC NUMBER PER SINGLE\nYARN) :GREY",
+        "hsn_code": "52054410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 232.56 DECITEX BUT NOT LESS THAN 192.31\nDECITEX (EXCEEDING 43 METRIC NUMBER BUT NOT EXCEEDING 52 METRIC NUMBER PER SINGLE\nYARN) : OTHER",
+        "hsn_code": "52054390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 232.56 DECITEX BUT NOT LESS THAN 192.31\nDECITEX (EXCEEDING 43 METRIC NUMBER BUT NOT EXCEEDING 52 METRIC NUMBER PER SINGLE\nYARN) : BLEACHED",
+        "hsn_code": "52054320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 232.56 DECITEX BUT NOT LESS THAN 192.31\nDECITEX (EXCEEDING 43 METRIC NUMBER BUT NOT EXCEEDING 52 METRIC NUMBER PER SINGLE\nYARN) : GREY",
+        "hsn_code": "52054310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 714.29 DECITEX BUT NOT LESS THAN 232.56\nDECITEX (EXCEEDING 14 METRIC NUMBER BUT NOT EXCEEDING 43 METRIC NUMBER PER SINGLE\nYARN) : OTHER",
+        "hsn_code": "52054290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 714.29 DECITEX BUT NOT LESS THAN 232.56\nDECITEX (EXCEEDING 14 METRIC NUMBER BUT NOT EXCEEDING 43 METRIC NUMBER PER SINGLE\nYARN) : GREY",
+        "hsn_code": "52054210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN 714.29 DECITEX OR MORE (NOT EXCEEDING 14 METRIC\nNUMBER PER SINGLE YARN) : OTHER",
+        "hsn_code": "52054190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN 714.29 DECITEX OR MORE (NOT EXCEEDING 14 METRIC\nNUMBER PER SINGLE YARN) : DYED",
+        "hsn_code": "52054130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN 714.29 DECITEX OR MORE (NOT EXCEEDING 14 METRIC\nNUMBER PER SINGLE YARN) : BLEACHED",
+        "hsn_code": "52054120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF COMBED\nFIBRES : MEASURING PER SINGLE YARN 714.29 DECITEX OR MORE (NOT EXCEEDING 14 METRIC\nNUMBER PER SINGLE YARN) : GREY",
+        "hsn_code": "52054110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 125 DECITEX (EXCEEDING 80 METRIC\nNUMBER PER SINGLE YARN) : OTHER",
+        "hsn_code": "52053590",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 125 DECITEX (EXCEEDING 80 METRIC\nNUMBER PER SINGLE YARN) : GREY",
+        "hsn_code": "52053510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 192.31 DECITEX BUT NOT LESS THAN 125\nDECITEX (EXCEEDING 52 METRIC NUMBER BUT NOT EXCEEDING 80 METRIC NUMBER PER SINGLE\nYARN) : OTHER",
+        "hsn_code": "52053490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 192.31 DECITEX BUT NOT LESS THAN 125\nDECITEX (EXCEEDING 52 METRIC NUMBER BUT NOT EXCEEDING 80 METRIC NUMBER PER SINGLE\nYARN) : DYED",
+        "hsn_code": "52053430",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 192.31 DECITEX BUT NOT LESS THAN 125\nDECITEX (EXCEEDING 52 METRIC NUMBER BUT NOT EXCEEDING 80 METRIC NUMBER PER SINGLE\nYARN) : BLEACHED",
+        "hsn_code": "52053420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 192.31 DECITEX BUT NOT LESS THAN 125\nDECITEX (EXCEEDING 52 METRIC NUMBER BUT NOT EXCEEDING 80 METRIC NUMBER PER SINGLE\nYARN) : GREY",
+        "hsn_code": "52053410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 232.56 DECITEX BUT NOT LESS THAN 192.31\nDECITEX (EXCEEDING 43 METRIC NUMBER BUT NOT EXCEEDING 52 METRIC NUMBER PER SINGLE\nYARN) : OTHER",
+        "hsn_code": "52053390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 232.56 DECITEX BUT NOT LESS THAN 192.31\nDECITEX (EXCEEDING 43 METRIC NUMBER BUT NOT EXCEEDING 52 METRIC NUMBER PER SINGLE\nYARN) : DYED",
+        "hsn_code": "52053330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 232.56 DECITEX BUT NOT LESS THAN 192.31\nDECITEX (EXCEEDING 43 METRIC NUMBER BUT NOT EXCEEDING 52 METRIC NUMBER PER SINGLE\nYARN) : BLEACHED",
+        "hsn_code": "52053320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 232.56 DECITEX BUT NOT LESS THAN 192.31\nDECITEX (EXCEEDING 43 METRIC NUMBER BUT NOT EXCEEDING 52 METRIC NUMBER PER SINGLE\nYARN) : GREY",
+        "hsn_code": "52053310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 714.29 DECITEX BUT NOT LESS THAN 232.56\nDECITEX (EXCEEDING 14 METRIC NUMBER BUT NOT EXCEEDING 43 METRIC NUMBER PER SINGLE\nYARN) : OTHER",
+        "hsn_code": "52053290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 714.29 DECITEX BUT NOT LESS THAN 232.56\nDECITEX (EXCEEDING 14 METRIC NUMBER BUT NOT EXCEEDING 43 METRIC NUMBER PER SINGLE\nYARN) : BLEACHED",
+        "hsn_code": "52053220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN LESS THAN 714.29 DECITEX BUT NOT LESS THAN 232.56\nDECITEX (EXCEEDING 14 METRIC NUMBER BUT NOT EXCEEDING 43 METRIC NUMBER PER SINGLE\nYARN) : GREY",
+        "hsn_code": "52053210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN 714.29 DECITEX OR MORE (NOT EXCEEDING 14 METRIC\nNUMBER PER SINGLE YARN) : OTHER",
+        "hsn_code": "52053190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN 714.29 DECITEX OR MORE (NOT EXCEEDING 14 METRIC\nNUMBER PER SINGLE YARN) : DYED",
+        "hsn_code": "52053130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN 714.29 DECITEX OR MORE (NOT EXCEEDING 14 METRIC\nNUMBER PER SINGLE YARN) : BLEACHED",
+        "hsn_code": "52053120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - MULTIPLE (FOLDED) OR CABLED YARN, OF UNCOMBED\nFIBRES : MEASURING PER SINGLE YARN 714.29 DECITEX OR MORE (NOT EXCEEDING 14 METRIC\nNUMBER PER SINGLE YARN) : GREY",
+        "hsn_code": "52053110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 83.33 DECITEX (EXCEEDING 120 METRIC NUMBER) : OTHER",
+        "hsn_code": "52052890",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 83.33 DECITEX (EXCEEDING 120 METRIC NUMBER) : BLEACHED",
+        "hsn_code": "52052820",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 83.33 DECITEX (EXCEEDING 120 METRIC NUMBER) : DYED",
+        "hsn_code": "52052810",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 106.38 DECITEX BUT NOT LESS THAN 83.33 DECITEX (EXCEEDING 94 METRIC NUMBER BUT\nNOT EXCEEDING 120 METRIC NUMBER) : OTHER",
+        "hsn_code": "52052790",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 106.38 DECITEX BUT NOT LESS THAN 83.33 DECITEX (EXCEEDING 94 METRIC NUMBER BUT\nNOT EXCEEDING 120 METRIC NUMBER) : BLEACHED",
+        "hsn_code": "52052720",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 106.38 DECITEX BUT NOT LESS THAN 83.33 DECITEX (EXCEEDING 94 METRIC NUMBER BUT\nNOT EXCEEDING 120 METRIC NUMBER) : DYED",
+        "hsn_code": "52052710",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 125 DECITEX BUT NOT LESS THAN 106.38 DECITEX (EXCEEDING 80 METRIC NUMBER BUT\nNOT EXCEEDING 94 METRIC NUMBER) : OTHER",
+        "hsn_code": "52052690",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 125 DECITEX BUT NOT LESS THAN 106.38 DECITEX (EXCEEDING 80 METRIC NUMBER BUT\nNOT EXCEEDING 94 METRIC NUMBER) : BLEACHED",
+        "hsn_code": "52052620",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 125 DECITEX BUT NOT LESS THAN 106.38 DECITEX (EXCEEDING 80 METRIC NUMBER BUT\nNOT EXCEEDING 94 METRIC NUMBER) : DYED",
+        "hsn_code": "52052610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 192.31 DECITEX BUT NOT LESS THAN 125 DECITEX (EXCEEDING 52 METRIC NUMBER BUT\nNOT EXCEEDING 80 METRIC NUMBER) : OTHER",
+        "hsn_code": "52052490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 192.31 DECITEX BUT NOT LESS THAN 125 DECITEX (EXCEEDING 52 METRIC NUMBER BUT\nNOT EXCEEDING 80 METRIC NUMBER) : BLEACHED",
+        "hsn_code": "52052420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 192.31 DECITEX BUT NOT LESS THAN 125 DECITEX (EXCEEDING 52 METRIC NUMBER BUT\nNOT EXCEEDING 80 METRIC NUMBER) : GREY",
+        "hsn_code": "52052410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 232.56 DECITEX BUT NOT LESS THAN 192.31 DECITEX (EXCEEDING 43 METRIC NUMBER\nBUT NOT EXCEEDING 52 METRIC NUMBER) : OTHER",
+        "hsn_code": "52052390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 232.56 DECITEX BUT NOT LESS THAN 192.31 DECITEX (EXCEEDING 43 METRIC NUMBER\nBUT NOT EXCEEDING 52 METRIC NUMBER) : BLEACHED",
+        "hsn_code": "52052320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 232.56 DECITEX BUT NOT LESS THAN 192.31 DECITEX (EXCEEDING 43 METRIC NUMBER\nBUT NOT EXCEEDING 52 METRIC NUMBER) : GREY",
+        "hsn_code": "52052310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 714.29 DECITEX BUT NOT LESS THAN 232.56 DECITEX (EXCEEDING 14 METRIC NUMBER\nBUT NOT EXCEEDING 43 METRIC NUMBER) : OTHER",
+        "hsn_code": "52052290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 714.29 DECITEX BUT NOT LESS THAN 232.56 DECITEX (EXCEEDING 14 METRIC NUMBER\nBUT NOT EXCEEDING 43 METRIC NUMBER) : BLEACHED",
+        "hsn_code": "52052220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING LESS\nTHAN 714.29 DECITEX BUT NOT LESS THAN 232.56 DECITEX (EXCEEDING 14 METRIC NUMBER\nBUT NOT EXCEEDING 43 METRIC NUMBER) : GREY",
+        "hsn_code": "52052210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING 714.29\nDECITEX OR MORE (NOT EXCEEDING 14 METRIC NUMBER) : OTHER",
+        "hsn_code": "52052190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING 714.29\nDECITEX OR MORE (NOT EXCEEDING 14 METRIC NUMBER) : DYED",
+        "hsn_code": "52052130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING 714.29\nDECITEX OR MORE (NOT EXCEEDING 14 METRIC NUMBER) : BLEACHED",
+        "hsn_code": "52052120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN OF COMBED FIBRES : MEASURING 714.29\nDECITEX OR MORE (NOT EXCEEDING 14 METRIC NUMBER) : GREY",
+        "hsn_code": "52052110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 125 DECITEX (EXCEEDING 80 METRIC NUMBER) : OTHER",
+        "hsn_code": "52051590",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 125 DECITEX (EXCEEDING 80 METRIC NUMBER) : DYED",
+        "hsn_code": "52051530",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 125 DECITEX (EXCEEDING 80 METRIC NUMBER) : BLEACHED",
+        "hsn_code": "52051520",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 125 DECITEX (EXCEEDING 80 METRIC NUMBER) : GREY",
+        "hsn_code": "52051510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD),CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON,NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 192.31 DECITEX BUT NOT LESS THAN 125 DECITEX (EXCEEDING 52 METRIC NUMBER\nBUT NOT EXCEEDING 80 METRIC NUMBER) : OTHER",
+        "hsn_code": "52051490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD),CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON,NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 192.31 DECITEX BUT NOT LESS THAN 125 DECITEX (EXCEEDING 52 METRIC NUMBER\nBUT NOT EXCEEDING 80 METRIC NUMBER) : DYED",
+        "hsn_code": "52051430",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 192.31 DECITEX BUT NOT LESS THAN 125 DECITEX (EXCEEDING 52 METRIC NUMBER\nBUT NOT EXCEEDING 80 METRIC NUMBER) : BLEACHED",
+        "hsn_code": "52051420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 192.31 DECITEX BUT NOT LESS THAN 125 DECITEX (EXCEEDING 52 METRIC NUMBER\nBUT NOT EXCEEDING 80 METRIC NUMBER) : GREY",
+        "hsn_code": "52051410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD),CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON,NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 232.56 DECITEX BUT NOT LESS THAN 192 .31 DECITEX (EXCEEDING 43 METRIC\nNUMBER BUT NOT EXCEEDING 52 METRIC NUMBER) : OTHER",
+        "hsn_code": "52051390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD),CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON,NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 232.56 DECITEX BUT NOT LESS THAN 192 .31 DECITEX (EXCEEDING 43 METRIC\nNUMBER BUT NOT EXCEEDING 52 METRIC NUMBER) : DYED",
+        "hsn_code": "52051330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD),CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON,NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 232.56 DECITEX BUT NOT LESS THAN 192 .31 DECITEX (EXCEEDING 43 METRIC\nNUMBER BUT NOT EXCEEDING 52 METRIC NUMBER) : BLEACHED",
+        "hsn_code": "52051320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD),CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON,NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 232.56 DECITEX BUT NOT LESS THAN 192 .31 DECITEX (EXCEEDING 43 METRIC\nNUMBER BUT NOT EXCEEDING 52 METRIC NUMBER) : GREY",
+        "hsn_code": "52051310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD),CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON,NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 714.29 DECITEX BUT NOT LESS THAN 232.56 DECITEX (EXCEEDING 14 METRIC\nNUMBER BUT NOT EXCEEDING 43 METRIC NUMBER) OTHER",
+        "hsn_code": "52051290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD),CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON,NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 714.29 DECITEX BUT NOT LESS THAN 232.56 DECITEX (EXCEEDING 14 METRIC\nNUMBER BUT NOT EXCEEDING 43 METRIC NUMBER) DYED",
+        "hsn_code": "52051230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD),CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON,NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 714.29 DECITEX BUT NOT LESS THAN 232.56 DECITEX (EXCEEDING 14 METRIC\nNUMBER BUT NOT EXCEEDING 43 METRIC NUMBER) BLEACHED",
+        "hsn_code": "52051220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD),CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON,NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\nLESS THAN 714.29 DECITEX BUT NOT LESS THAN 232.56 DECITEX (EXCEEDING 14 METRIC\nNUMBER BUT NOT EXCEEDING 43 METRIC NUMBER) GREY",
+        "hsn_code": "52051210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD),CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\n714.29 DECITEX OR MORE (NOT EXCEEDING 14 METRIC NUMBER -OTHER",
+        "hsn_code": "52051190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\n714.29 DECITEX OR MORE (NOT EXCEEDING 14 METRIC NUMBER) : DYED",
+        "hsn_code": "52051130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD),CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON,NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\n714.29 DECITEX OR MORE (NOT EXCEEDING 14 METRIC NUMBER) : BLEACHED",
+        "hsn_code": "52051120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON YARN (OTHER THAN SEWING THREAD), CONTAINING 85% OR MORE BY WEIGHT OF\nCOTTON, NOT PUT UP FOR RETAIL SALE - SINGLE YARN, OF UNCOMBED FIBRES : MEASURING\n714.29 DECITEX OR MORE (NOT EXCEEDING 14 METRIC NUMBER) : GREY",
+        "hsn_code": "52051110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON, CARDED OR COMBED",
+        "hsn_code": "52030000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON WASTE (INCLUDING YARN WASTE AND GARNETTED STOCK) OTHER :OTHER",
+        "hsn_code": "52029900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON WASTE (INCLUDING YARN WASTE AND GARNETTED STOCK) OTHER :GARNETTED STOCK",
+        "hsn_code": "52029100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON WASTE (INCLUDING YARN WASTE AND GARNETTED STOCK) YARN WASTE (INCLUDING\nTHREAD WASTE)",
+        "hsn_code": "52021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON, NOT CARDED OR COMBED COTTON, NOT CARDED OR COMBED : COTTON, OTHER\nTHAN INDIAN, OF ALL STAPLE LENGTHS",
+        "hsn_code": "52010020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON, NOT CARDED OR COMBED COTTON, NOT CARDED OR COMBED : INDIAN COTTON :\nINDIAN COTTON OF ALL STAPLE LENGTH 34.5 AND ABOVE (112/32) MM",
+        "hsn_code": "52010019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON, NOT CARDED OR COMBED COTTON, NOT CARDED OR COMBED : INDIAN COTTON :\nINDIAN COTTON OF STAPLE LENGTH 28.5 (14/32) AND ABOVE BUT BELOW 34.5 MM MM",
+        "hsn_code": "52010015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON, NOT CARDED OR COMBED COTTON, NOT CARDED OR COMBED : INDIAN COTTON :\nINDIAN COTTON OF STAPLE LENGTH OVER 24.5 MM(31/32) TO 28 MM",
+        "hsn_code": "52010014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON, NOT CARDED OR COMBED COTTON, NOT CARDED OR COMBED : INDIAN COTTON :\nINDIAN COTTON OF STAPLE LENGTH EXCEEDING 20.5MM (26/32\") BUT NOT EXCEEDING 24.5MM\n(30/32\")",
+        "hsn_code": "52010013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON, NOT CARDED OR COMBED COTTON, NOT CARDED OR COMBED : INDIAN COTTON :\nINDIAN COTTON OF STAPLE LENGTHS 20.5 MM (25/32\") AND BELOW (E. G. OOMRAS, YELLOW\nPICKING, ASSAM COMILLAS)",
+        "hsn_code": "52010012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COTTON, NOT CARDED OR COMBED COTTON, NOT CARDED OR COMBED : INDIAN COTTON :\nBENGAL DESHI",
+        "hsn_code": "52010011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COARSE ANIMAL HAIR OR OF HORSE HAIR WOVEN FABRICS OF COARSE\nANIMAL HAIR OR OF HORSE HAIR : OTHER",
+        "hsn_code": "51130090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COARSE ANIMAL HAIR OR OF HORSE HAIR WOVEN FABRICS OF COARSE\nANIMAL HAIR OR OF HORSE HAIR : PRINTED",
+        "hsn_code": "51130040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COARSE ANIMAL HAIR OR OF HORSE HAIR WOVEN FABRICS OF COARSE\nANIMAL HAIR OR OF HORSE HAIR : DYED",
+        "hsn_code": "51130030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COARSE ANIMAL HAIR OR OF HORSE HAIR WOVEN FABRICS OF COARSE\nANIMAL HAIR OR OF HORSE HAIR : BLEACHED",
+        "hsn_code": "51130020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COARSE ANIMAL HAIR OR OF HORSE HAIR WOVEN FABRICS OF COARSE\nANIMAL HAIR OR OF HORSE HAIR : UNBLEACHED",
+        "hsn_code": "51130010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR OTHER : OTHER",
+        "hsn_code": "51129090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL, OR OF COMBED FINE ANIMAL HAIR -OF HANDLOOM",
+        "hsn_code": "51129050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR OTHER : PRINTED",
+        "hsn_code": "51129040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR OTHER : DYED",
+        "hsn_code": "51129030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR OTHER : BLEACHED",
+        "hsn_code": "51129020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR OTHER : UNBLEACHED",
+        "hsn_code": "51129010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE STAPLE FIBRES : OTHER",
+        "hsn_code": "51123090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE STAPLE FIBRES : PRINTED",
+        "hsn_code": "51123040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE STAPLE FIBRES : DYED",
+        "hsn_code": "51123030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE STAPLE FIBRES : BLEACHED",
+        "hsn_code": "51123020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE STAPLE FIBRES : UNBLEACHED",
+        "hsn_code": "51123010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE FILAMENTS: OTHER",
+        "hsn_code": "51122090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE FILAMENTS: PRINTED",
+        "hsn_code": "51122040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE FILAMENTS: DYED",
+        "hsn_code": "51122030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE FILAMENTS: BLEACHED",
+        "hsn_code": "51122020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE FILAMENTS: UNBLEACHED",
+        "hsn_code": "51122010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR - CONTAINING 85% OR\nMORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OTHER : OTHER",
+        "hsn_code": "51121990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR - CONTAINING 85% OR\nMORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OTHER : PRINTED",
+        "hsn_code": "51121940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR - CONTAINING 85% OR\nMORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OTHER : DYED",
+        "hsn_code": "51121930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR - CONTAINING 85% OR\nMORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OTHER : BLEACHED",
+        "hsn_code": "51121920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR - CONTAINING 85% OR\nMORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OTHER : UNBLEACHED",
+        "hsn_code": "51121910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR - CONTAINING 85% OR\nMORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OF A WEIGHT NOT EXCEEDING 200\nG/M2: OTHER",
+        "hsn_code": "51121190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR - CONTAINING 85% OR\nMORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OF A WEIGHT NOT EXCEEDING 200\nG/M2: PRINTED",
+        "hsn_code": "51121140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR - CONTAINING 85% OR\nMORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OF A WEIGHT NOT EXCEEDING 200\nG/M2: DYED",
+        "hsn_code": "51121130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR - CONTAINING 85% OR\nMORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OF A WEIGHT NOT EXCEEDING 200\nG/M2: BLEACHED",
+        "hsn_code": "51121120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF COMBED WOOL OR OF COMBED FINE ANIMAL HAIR - CONTAINING 85% OR\nMORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OF A WEIGHT NOT EXCEEDING 200\nG/M2: UNBLEACHED",
+        "hsn_code": "51121110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR OTHER : OTHER",
+        "hsn_code": "51119090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR OTHER : PRINTED",
+        "hsn_code": "51119040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR OTHER : DYED",
+        "hsn_code": "51119030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR OTHER : BLEACHED",
+        "hsn_code": "51119020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR OTHER : UNBLEACHED",
+        "hsn_code": "51119010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE STAPLE FIBRES : OTHER",
+        "hsn_code": "51113090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE STAPLE FIBRES : PRINTED",
+        "hsn_code": "51113040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE STAPLE FIBRES : DYED",
+        "hsn_code": "51113030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE STAPLE FIBRES : BLEACHED",
+        "hsn_code": "51113020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE STAPLE FIBRES : UNBLEACHED",
+        "hsn_code": "51113010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE FILAMENTS : OTHER",
+        "hsn_code": "51112090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE FILAMENTS : PRINTED",
+        "hsn_code": "51112040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE FILAMENTS : DYED",
+        "hsn_code": "51112030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE FILAMENTS : BLEACHED",
+        "hsn_code": "51112020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR OTHER, MIXED MAINLY\nOR SOLELY WITH MAN-MADE FILAMENTS : UNBLEACHED",
+        "hsn_code": "51112010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR - CONTAINING 85\nPERCENT OR MORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OTHER : OTHER",
+        "hsn_code": "51111990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR - CONTAINING 85\nPERCENT OR MORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OTHER : PRINTED",
+        "hsn_code": "51111940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR - CONTAINING 85\nPERCENT OR MORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OTHER : DYED",
+        "hsn_code": "51111930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR - CONTAINING 85\nPERCENT OR MORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OTHER : BLEACHED",
+        "hsn_code": "51111920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR - CONTAINING 85\nPERCENT OR MORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OTHER : UNBLEACHED",
+        "hsn_code": "51111910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR - CONTAINING 85\nPERCENT OR MORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OF A WEIGHT NOT\nEXCEEDING 300 G/ M2: OTHER",
+        "hsn_code": "51111190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR - CONTAINING 85\nPERCENT OR MORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OF A WEIGHT NOT\nEXCEEDING 300 G/ M2: PRINTED",
+        "hsn_code": "51111140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR - CONTAINING 85\nPERCENT OR MORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OF A WEIGHT NOT\nEXCEEDING 300 G/ M2: DYED",
+        "hsn_code": "51111130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR - CONTAINING 85\nPERCENT OR MORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OF A WEIGHT NOT\nEXCEEDING 300 G/ M2: BLEACHED",
+        "hsn_code": "51111120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOVEN FABRICS OF CARDED WOOL OR OF CARDED FINE ANIMAL HAIR - CONTAINING 85\nPERCENT OR MORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OF A WEIGHT NOT\nEXCEEDING 300 G/ M2: UNBLEACHED",
+        "hsn_code": "51111110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF COARSE ANIMAL HAIR OR OF HORSE HAIR (INCLUDING GIMPED HORSEHAIR YARN),\nWHETHER OR NOT PUT UP FOR RETAIL SALE YARN OF COARSE ANIMAL HAIR OR OF HORSE-HAIR\n(INCLUDING GIMPED HORSEHAIR YARN), WHETHER OR NOT PUT UP FOR RETAIL SALE : OTHER",
+        "hsn_code": "51100020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF COARSE ANIMAL HAIR OR OF HORSE HAIR (INCLUDING GIMPED HORSEHAIR YARN),\nWHETHER OR NOT PUT UP FOR RETAIL SALE YARN OF COARSE ANIMAL HAIR OR OF HORSE-HAIR\n(INCLUDING GIMPED HORSEHAIR YARN), WHETHER OR NOT PUT UP FOR RETAIL SALE : PUT UP\nFOR RETAIL SALE",
+        "hsn_code": "51100010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF WOOL OR FINE ANIMAL HAIR, PUT UP FOR RETAIL SALE OTHER",
+        "hsn_code": "51099000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF WOOL OR FINE ANIMAL HAIR, PUT UP FOR RETAIL SALE CONTAINING 85 PERCENT OR\nMORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : OTHER",
+        "hsn_code": "51091090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF WOOL OR FINE ANIMAL HAIR, PUT UP FOR RETAIL SALE CONTAINING 85 PERCENT OR\nMORE BY WEIGHT OF WOOL OR OF FINE ANIMAL HAIR : HOISERY WOOL",
+        "hsn_code": "51091010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF FINE ANIMAL HAIR (CARDED OR COMBED), NOT PUT UP FOR RETAIL SALE COMBED",
+        "hsn_code": "51082000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF FINE ANIMAL HAIR (CARDED OR COMBED), NOT PUT UP FOR RETAIL SALE CARDED",
+        "hsn_code": "51081000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF COMBED WOOL, NOT PUT UP FOR RETAIL SALE CONTAINING LESS THAN 85 PERCENT\nBY WEIGHT OF WOOL : OTHER",
+        "hsn_code": "51072090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF COMBED WOOL, NOT PUT UP FOR RETAIL SALE CONTAINING LESS THAN 85 PERCENT\nBY WEIGHT OF WOOL : WOOLLEN CARPET YARN",
+        "hsn_code": "51072040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF COMBED WOOL, NOT PUT UP FOR RETAIL SALE CONTAINING LESS THAN 85 PERCENT\nBY WEIGHT OF WOOL : WORSTED WEAVING YARN",
+        "hsn_code": "51072030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF COMBED WOOL, NOT PUT UP FOR RETAIL SALE CONTAINING LESS THAN 85 PERCENT\nBY WEIGHT OF WOOL : WORSTED KNITTED YARN",
+        "hsn_code": "51072020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF COMBED WOOL, NOT PUT UP FOR RETAIL SALE CONTAINING LESS THAN 85 PERCENT\nBY WEIGHT OF WOOL : WORSTED HOSIERY YARN",
+        "hsn_code": "51072010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF COMBED WOOL, NOT PUT UP FOR RETAIL SALE CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF WOOL : OTHER",
+        "hsn_code": "51071090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF COMBED WOOL, NOT PUT UP FOR RETAIL SALE CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF WOOL : WOOLLEN CARPET YARN",
+        "hsn_code": "51071040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF COMBED WOOL, NOT PUT UP FOR RETAIL SALE CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF WOOL : WORSTED WEAVING YARN",
+        "hsn_code": "51071030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF COMBED WOOL, NOT PUT UP FOR RETAIL SALE CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF WOOL : WORSTED KNITTED YARN",
+        "hsn_code": "51071020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF COMBED WOOL, NOT PUT UP FOR RETAIL SALE CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF WOOL : WORSTED HOSIERY YARN",
+        "hsn_code": "51071010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF CARDED WOOL, NOT PUT UP FOR RETAIL SALE CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF WOOL : OTHER",
+        "hsn_code": "51062090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF CARDED WOOL, NOT PUT UP FOR RETAIL SALE CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF WOOL : MUNGA WOOLLEN YARN",
+        "hsn_code": "51062020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF CARDED WOOL, NOT PUT UP FOR RETAIL SALE CONTAINING LESS THAN 85 PERCENT BY\nWEIGHT OF WOOL : SHODDY WOOLLEN YARN",
+        "hsn_code": "51062010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF CARDED WOOL, NOT PUT UP FOR RETAIL SALE CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF WOOL : OTHER",
+        "hsn_code": "51061090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF CARDED WOOL, NOT PUT UP FOR RETAIL SALE CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF WOOL : MUNGA WOOLLEN YARN",
+        "hsn_code": "51061020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN OF CARDED WOOL, NOT PUT UP FOR RETAIL SALE CONTAINING 85 PERCENT OR MORE BY\nWEIGHT OF WOOL : SHODDY WOOLLEN YARN",
+        "hsn_code": "51061010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WASTE OF WOOL OR OF FINE OR COARSE ANIMAL HAIR, INCLUDING YARN WASTE BUT\nEXCLUDING GARNETTED STOCK WASTE OF COARSE ANIMAL HAIR",
+        "hsn_code": "51033000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WASTE OF WOOL OR OF FINE OR COARSE ANIMAL HAIR, INCLUDING YARN WASTE BUT\nEXCLUDING GARNETTED STOCK OTHER WASTE OF WOOL OR OF FINE ANIMAL HAIR : OTHER",
+        "hsn_code": "51032090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WASTE OF WOOL OR OF FINE OR COARSE ANIMAL HAIR, INCLUDING YARN WASTE BUT\nEXCLUDING GARNETTED STOCK OTHER WASTE OF WOOL OR OF FINE ANIMAL HAIR : YARN\nWASTE",
+        "hsn_code": "51032020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WASTE OF WOOL OR OF FINE OR COARSE ANIMAL HAIR, INCLUDING YARN WASTE BUT\nEXCLUDING GARNETTED STOCK OTHER WASTE OF WOOL OR OF FINE ANIMAL HAIR : WASTE OF\nSHEEP'S AND LAMB'S WOOL",
+        "hsn_code": "51032010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WASTE OF WOOL OR OF FINE OR COARSE ANIMAL HAIR, INCLUDING YARN WASTE BUT\nEXCLUDING GARNETTED STOCK NOILS OF WOOL OR OF FINE ANIMAL HAIR : OTHER",
+        "hsn_code": "51031090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WASTE OF WOOL OR OF FINE OR COARSE ANIMAL HAIR, INCLUDING YARN WASTE BUT\nEXCLUDING GARNETTED STOCK NOILS OF WOOL OR OF FINE ANIMAL HAIR : NOILS OF WOOL",
+        "hsn_code": "51031010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FINE OR COARSE ANIMAL HAIR, NOT CARDED OR COMBED COARSE ANIMAL HAIR : OTHER\n(EXCLUDING PIG AND BOAR BRISTLES)",
+        "hsn_code": "51022090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FINE OR COARSE ANIMAL HAIR, NOT CARDED OR COMBED COARSE ANIMAL HAIR : GOAT HAIR\n(OTHER THAN ANGORA)",
+        "hsn_code": "51022010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FINE OR COARSE ANIMAL HAIR, NOT CARDED OR COMBED FINE ANIMAL HAIR : OTHER : OTHER",
+        "hsn_code": "51021990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FINE OR COARSE ANIMAL HAIR, NOT CARDED OR COMBED FINE ANIMAL HAIR :: OTHER :\nMARINE ANGORA",
+        "hsn_code": "51021910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FINE OR COARSE ANIMAL HAIR, NOT CARDED OR COMBED FINE ANIMAL HAIR : OF KASHMIR\n(CASHMERE) GOATS : OTHER",
+        "hsn_code": "51021190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FINE OR COARSE ANIMAL HAIR, NOT CARDED OR COMBED FINE ANIMAL HAIR : OF KASHMIR\n(CASHMERE) GOATS : MARINE ANGORA",
+        "hsn_code": "51021110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOOL, NOT CARDED OR COMBED : CARBONISED",
+        "hsn_code": "51013000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOOL, NOT CARDED OR COMBED : DEGREASED, NOT CARBONIZED OTHER",
+        "hsn_code": "51012900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOOL, NOT CARDED OR COMBED : DEGREASED, NOT CARBONIZED SHORN WOOL",
+        "hsn_code": "51012100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOOL, NOT CARDED OR COMBED : GREASY, INCLUDING FLEECE-WASHED WOOL : OTHER",
+        "hsn_code": "51011900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOOL, NOT CARDED OR COMBED : GREASY, INCLUDING FLEECE-WASHED WOOL : SHORN WOOL",
+        "hsn_code": "51011100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK YARN AND YARN SPUN FROM SILK WASTE, PUT UP FOR RETAIL SALE; SILK-WORM GUT SILK\nYARN AND YARN SPUN FROM SILK WASTE, PUT UP FOR RETAIL SALE; SILK WORM GUT: OTHER",
+        "hsn_code": "50060090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK YARN AND YARN SPUN FROM SILK WASTE, PUT UP FOR RETAIL SALE; SILK-WORM GUT SILK\nYARN AND YARN SPUN FROM SILK WASTE, PUT UP FOR RETAIL SALE; SILK WORM GUT: - YARN\nSPUN FROM SILK WASTE CONTAINING 85 PERCENT OR LESS BY WEIGHT OF SILK : OTHER",
+        "hsn_code": "50060039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK YARN AND YARN SPUN FROM SILK WASTE, PUT UP FOR RETAIL SALE; SILK-WORM GUT SILK\nYARN AND YARN SPUN FROM SILK WASTE, PUT UP FOR RETAIL SALE; SILK WORM GUT: - YARN\nSPUN FROM SILK WASTE CONTAINING 85 PERCENT OR LESS BY WEIGHT OF SILK : SILK\nEMBROIDERY THREAD",
+        "hsn_code": "50060033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK YARN AND YARN SPUN FROM SILK WASTE, PUT UP FOR RETAIL SALE; SILK-WORM GUT SILK\nYARN AND YARN SPUN FROM SILK WASTE, PUT UP FOR RETAIL SALE; SILK WORM GUT: - YARN\nSPUN FROM SILK WASTE CONTAINING 85 PERCENT OR LESS BY WEIGHT OF SILK : YARN SPUN\nFROM NOIL SILK",
+        "hsn_code": "50060032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK YARN AND YARN SPUN FROM SILK WASTE, PUT UP FOR RETAIL SALE; SILK-WORM GUT SILK\nYARN AND YARN SPUN FROM SILK WASTE, PUT UP FOR RETAIL SALE; SILK WORM GUT: - YARN\nSPUN FROM SILK WASTE CONTAINING 85 PERCENT OR LESS BY WEIGHT OF SILK : SPUN FROM\nSILK WASTE OTHER THAN NOIL SILK",
+        "hsn_code": "50060031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK YARN AND YARN SPUN FROM SILK WASTE, PUT UP FOR RETAIL SALE; SILK-WORM GUT SILK\nYARN AND YARN SPUN FROM SILK WASTE, PUT UP FOR RETAIL SALE; SILK WORM GUT: YARN\nSPUN FROM SILK WASTE CONTAINING 85 PERCENT OR MORE BY WEIGHT OF SILK : OTHER",
+        "hsn_code": "50060029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK YARN AND YARN SPUN FROM SILK WASTE, PUT UP FOR RETAIL SALE; SILK-WORM GUT SILK\nYARN AND YARN SPUN FROM SILK WASTE, PUT UP FOR RETAIL SALE; SILK WORM GUT: YARN\nSPUN FROM SILK WASTE CONTAINING 85 PERCENT OR MORE BY WEIGHT OF SILK : SILK\nEMBROIDERY THREAD",
+        "hsn_code": "50060021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK YARN AND YARN SPUN FROM SILK WASTE, PUT UP FOR RETAIL SALE; SILK-WORM GUT SILK\nYARN AND YARN SPUN FROM SILK WASTE, PUT UP FOR RETAIL SALE; SILK WORM GUT: - SILK\nYARN : OTHER",
+        "hsn_code": "50060019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK YARN AND YARN SPUN FROM SILK WASTE, PUT UP FOR RETAIL SALE; SILK-WORM GUT SILK\nYARN AND YARN SPUN FROM SILK WASTE, PUT UP FOR RETAIL SALE; SILK WORM GUT: - SILK\nYARN : SILK EMBROIDERY THREAD",
+        "hsn_code": "50060011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN SPUN FROM SILK WASTE, NOT PUT UP FOR RETAIL SALE YARN SPUN FROM SILK WASTE,\nNOT PUT UP FOR RETAIL SALE: - CONTAINING LESS THAN 85 PERCENT BY WEIGHT OF SILK :\nFROM NOIL SILK",
+        "hsn_code": "50050022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN SPUN FROM SILK WASTE, NOT PUT UP FOR RETAIL SALE YARN SPUN FROM SILK WASTE,\nNOT PUT UP FOR RETAIL SALE: - CONTAINING LESS THAN 85 PERCENT BY WEIGHT OF SILK :\nOTHER THAN NOIL SILK",
+        "hsn_code": "50050021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN SPUN FROM SILK WASTE, NOT PUT UP FOR RETAIL SALE YARN SPUN FROM SILK WASTE,\nNOT PUT UP FOR RETAIL SALE: - CONTAINING 85 PERCENT OR MORE BY WEIGHT OF SILK WASTE:\nFROM NOIL SILK",
+        "hsn_code": "50050012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "YARN SPUN FROM SILK WASTE, NOT PUT UP FOR RETAIL SALE YARN SPUN FROM SILK WASTE,\nNOT PUT UP FOR RETAIL SALE: - CONTAINING 85 PERCENT OR MORE BY WEIGHT OF SILK WASTE:\nOTHER THAN NOIL SILK",
+        "hsn_code": "50050011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK YARN (OTHER THAN YARN SPUN FROM SILK WASTE) NOT PUT UP FOR RETAIL SALE SILK\nYARN (OTHER THAN YARN SPUN FROM SILK WASTE) NOT PUT UP FOR RETAIL SALE : OTHER",
+        "hsn_code": "50040090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "(OTHER THAN YARN SPUN FROM SILK WASTE) NOT PUT UP FOR RETAIL SALE : 100 PERCENT\nMULBERRY DUPION SILK YARN",
+        "hsn_code": "50040010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK WASTE (INCLUDING COCOONS UNSUITABLE FOR REELING, YARN WASTE AND GARNETTED\nSTOCK) - OTHER : OTHER",
+        "hsn_code": "50039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK WASTE (INCLUDING COCOONS UNSUITABLE FOR REELING, YARN WASTE AND GARNETTED\nSTOCK) - OTHER : MUNGA WASTE",
+        "hsn_code": "50039040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK WASTE (INCLUDING COCOONS UNSUITABLE FOR REELING, YARN WASTE AND GARNETTED\nSTOCK) - OTHER : ERI WASTE",
+        "hsn_code": "50039030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK WASTE (INCLUDING COCOONS UNSUITABLE FOR REELING, YARN WASTE AND GARNETTED\nSTOCK) - OTHER : TUSSAR WASTE",
+        "hsn_code": "50039020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK WASTE (INCLUDING COCOONS UNSUITABLE FOR REELING, YARN WASTE AND GARNETTED\nSTOCK) - OTHER : MULBERRY SILK WASTE",
+        "hsn_code": "50039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK WASTE (INCLUDING COCOONS UNSUITABLE FOR REELING, YARN WASTE AND GARNETTED\nSTOCK) NOT CARDED OR COMBED OTHER",
+        "hsn_code": "50031090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK WASTE (INCLUDING COCOONS UNSUITABLE FOR REELING, YARN WASTE AND GARNETTED\nSTOCK) NOT CARDED OR COMBED TUSSAR WASTE",
+        "hsn_code": "50031020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK WASTE (INCLUDING COCOONS UNSUITABLE FOR REELING, YARN WASTE AND GARNETTED\nSTOCK) NOT CARDED OR COMBED MULBERRY SILK WASTE",
+        "hsn_code": "50031010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK WASTE - OTHER",
+        "hsn_code": "50030090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MUNGA WASTE",
+        "hsn_code": "50030040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRI WASTE",
+        "hsn_code": "50030030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "TUSSAR SILK WASTE",
+        "hsn_code": "50030020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MULBERRY SILK WASTE",
+        "hsn_code": "50030010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "RAW SILK (NOT THROWN) RAW SILK (NOT THROWN) NON-MULBERRY SILK",
+        "hsn_code": "50020030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "RAW SILK (NOT THROWN) RAW SILK (NOT THROWN) MULBERRY DUPION SILK",
+        "hsn_code": "50020020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "RAW SILK (NOT THROWN) RAW SILK (NOT THROWN) MULBERRY RAW SILK",
+        "hsn_code": "50020010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILK-WORM COCOONS SUITABLE FOR REELING",
+        "hsn_code": "50010000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNUSED POSTAGE, REVENUE OR SIMILAR STAMPS OF CURRENT OR NEW ISSUE IN THE COUNTRY\nIN WHICH THEY HAVE, OR WILL HAVE, A RECOGNIZED FACE VALUE; STAMP-IMPRESSED PAPER;\nBANK NOTES; CHEQUE FORMS; STOCK, SHARE OR BOND CERTIFICATES AND SIMILAR\nDOCUMENTS OF TITLE UNUSED POSTAGE, REVENUE OR SIMILAR STAMPS OFCURRENT OR NEW\nISSUE IN THE COUNTRY IN WHICH THEYHAVE, OR WILL HAVE, A RECOGNIZED FACE\nVALUE;STAMP-IMPRESSED PAPER; BANK NOTES; CHEQUEFORMS; STOCK, SHARE OR BOND\nCERTIFICATES ANDSIMILAR DOCUMENTS OF TITLE : OTHER",
+        "hsn_code": "49070090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNUSED POSTAGE, REVENUE OR SIMILAR STAMPS OF CURRENT OR NEW ISSUE IN THE COUNTRY\nIN WHICH THEY HAVE, OR WILL HAVE, A RECOGNIZED FACE VALUE; STAMP-IMPRESSED PAPER;\nBANK NOTES; CHEQUE FORMS; STOCK, SHARE OR BOND CERTIFICATES AND SIMILAR\nDOCUMENTS OF TITLE UNUSED POSTAGE, REVENUE OR SIMILAR STAMPS OFCURRENT OR NEW\nISSUE IN THE COUNTRY IN WHICH THEYHAVE, OR WILL HAVE, A RECOGNIZED FACE\nVALUE;STAMP-IMPRESSED PAPER; BANK NOTES; CHEQUEFORMS; STOCK, SHARE OR BOND\nCERTIFICATES ANDSIMILAR DOCUMENTS OF TITLE : DOCUMENTS OF TITLE CONVEYING THE\nRIGHT TO USE INFORMATION TECHNOLOGY SOFTWARE",
+        "hsn_code": "49070030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNUSED POSTAGE, REVENUE OR SIMILAR STAMPS OF CURRENT OR NEW ISSUE IN THE COUNTRY\nIN WHICH THEY HAVE, OR WILL HAVE, A RECOGNIZED FACE VALUE; STAMP-IMPRESSED PAPER;\nBANK NOTES; CHEQUE FORMS; STOCK, SHARE OR BOND CERTIFICATES AND SIMILAR\nDOCUMENTS OF TITLE UNUSED POSTAGE, REVENUE OR SIMILAR STAMPS OFCURRENT OR NEW\nISSUE IN THE COUNTRY IN WHICH THEYHAVE, OR WILL HAVE, A RECOGNIZED FACE\nVALUE;STAMP-IMPRESSED PAPER; BANK NOTES; CHEQUEFORMS; STOCK, SHARE OR BOND\nCERTIFICATES ANDSIMILAR DOCUMENTS OF TITLE : BANK NOTES",
+        "hsn_code": "49070020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNUSED POSTAGE, REVENUE OR SIMILAR STAMPS OF CURRENT OR NEW ISSUE IN THE COUNTRY\nIN WHICH THEY HAVE, OR WILL HAVE, A RECOGNIZED FACE VALUE; STAMP-IMPRESSED PAPER;\nBANK NOTES; CHEQUE FORMS; STOCK, SHARE OR BOND CERTIFICATES AND SIMILAR\nDOCUMENTS OF TITLE UNUSED POSTAGE, REVENUE OR SIMILAR STAMPS OFCURRENT OR NEW\nISSUE IN THE COUNTRY IN WHICH THEYHAVE, OR WILL HAVE, A RECOGNIZED FACE\nVALUE;STAMP-IMPRESSED PAPER; BANK NOTES; CHEQUEFORMS; STOCK, SHARE OR BOND\nCERTIFICATES ANDSIMILAR DOCUMENTS OF TITLE : UNUSED POSTAGE, REVENUE OR SIMILAR\nSTAMPS OF CURRENT OR NEW ISSUE IN THE COUNTRY IN WHICH THEY HAVE, OR WILL HAVE, A\nRECOGNIZED FACE VALUE",
+        "hsn_code": "49070010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MAPS AND HYDROGRAPHIC OR SIMILAR CHARTS OF ALL KINDS, INCLUDING ATLASES, WALL\nMAPS, TOPOGRAPHICAL PLANS AND GLOBES, PRINTED - OTHER : - OTHER: OTHER",
+        "hsn_code": "49059990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MAPS AND HYDROGRAPHIC OR SIMILAR CHARTS OF ALL KINDS, INCLUDING ATLASES, WALL\nMAPS, TOPOGRAPHICAL PLANS AND GLOBES, PRINTED - OTHER : - OTHER: GEOGRAPHICAL,\nHYDROLOGICAL, ASTRONOMICAL MAPS OR CHARTS",
+        "hsn_code": "49059910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MAPS AND HYDROGRAPHIC OR SIMILAR CHARTS OF ALL KINDS, INCLUDING ATLASES, WALL\nMAPS, TOPOGRAPHICAL PLANS AND GLOBES, PRINTED - OTHER : IN BOOK FORM",
+        "hsn_code": "49059100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MAPS AND HYDROGRAPHIC OR SIMILAR CHARTS OF ALL KINDS, INCLUDING ATLASES, WALL\nMAPS, TOPOGRAPHICAL PLANS AND GLOBES, PRINTED GLOBES",
+        "hsn_code": "49051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MUSIC, PRINTED OR IN MANUSCRIPT, WHETHER OR NOT FOUND OR ILLUSTRATED",
+        "hsn_code": "49040000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CHILDREN'S PICTURE, DRAWING OR COLOURING BOOKS CHILDREN'S PICTURE, DRAWING OR\nCOLOURING BOOKS : DRAWING OR COLOURING BOOKS",
+        "hsn_code": "49030020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CHILDREN'S PICTURE, DRAWING OR COLOURING BOOKS CHILDREN'S PICTURE, DRAWING OR\nCOLOURING BOOKS : PICTURE BOOKS",
+        "hsn_code": "49030010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "NEWSPAPERS, JOURNALS AND PERIODICALS, WHETHER OR NOT ILLUSTRATED OR CONTAINING\nADVERTISING MATERIAL OTHER : JOURNALS AND PERIODICALS",
+        "hsn_code": "49029020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "NEWSPAPERS, JOURNALS AND PERIODICALS, WHETHER OR NOT ILLUSTRATED OR CONTAINING\nADVERTISING MATERIAL OTHER : NEWSPAPERS",
+        "hsn_code": "49029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "NEWSPAPERS, JOURNALS AND PERIODICALS, WHETHER OR NOT ILLUSTRATED OR CONTAINING\nADVERTISING MATERIAL APPEARING AT LEAST FOUR TIMES A WEEK : JOURNALS AND\nPERIODICALS",
+        "hsn_code": "49021020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "NEWSPAPERS, JOURNALS AND PERIODICALS, WHETHER OR NOT ILLUSTRATED OR CONTAINING\nADVERTISING MATERIAL APPEARING AT LEAST FOUR TIMES A WEEK : NEWSPAPERS",
+        "hsn_code": "49021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PRINTED BOOKS, BROCHURES, LEAFLETS AND SIMILAR PRINTED MATTER, WHETHER OR NOT IN\nSINGLE SHEETS - OTHER : OTHER",
+        "hsn_code": "49019900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PRINTED BOOKS, BROCHURES, LEAFLETS AND SIMILAR PRINTED MATTER, WHETHER OR NOT IN\nSINGLE SHEETS - OTHER : DICTIONARIES AND ENCYCLOPAEDIAS, AND SERIAL INSTALMENTS\nTHEREOF",
+        "hsn_code": "49019100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PRINTED BOOKS, BROCHURES, LEAFLETS AND SIMILAR PRINTED MATTER, WHETHER OR NOT IN\nSINGLE SHEETS IN SINGLE SHEETS, WHETHER OR NOT FOLDED : PAMPHLETS, BOOKLETS,\nBROCHURES, LEAFLETS AND SIMILAR PRINTED MATTER",
+        "hsn_code": "49011020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PRINTED BOOKS, BROCHURES, LEAFLETS AND SIMILAR PRINTED MATTER, WHETHER OR NOT IN\nSINGLE SHEETS IN SINGLE SHEETS, WHETHER OR NOT FOLDED : PRINTED BOOKS",
+        "hsn_code": "49011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF LEATHER OR OF COMPOSITION LEATHER, OF A KIND USED IN MACHINERY OR\nMECHANICAL APPLIANCES OR FOR OTHER TECHNICAL USES - ARTICLES OF LEATHER OR OF\nCOMPOSITION LEATHER, OF A KIND USED IN MACHINERY OR MECHANICAL APPLIANCES OR FOR\nOTHER TECHNICAL USES : OTHER : OTHER",
+        "hsn_code": "42040099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF LEATHER OR OF COMPOSITION LEATHER, OF A KIND USED IN MACHINERY OR\nMECHANICAL APPLIANCES OR FOR OTHER TECHNICAL USES - ARTICLES OF LEATHER OR OF\nCOMPOSITION LEATHER, OF A KIND USED IN MACHINERY OR MECHANICAL APPLIANCES OR FOR\nOTHER TECHNICAL USES : OTHER : LEATHER BOARD MADE FROM LEATHER SCRAP FOR THE\nMANUFACTURE OF COUNTERS AND STIFFNERS",
+        "hsn_code": "42040091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF LEATHER OR OF COMPOSITION LEATHER, OF A KIND USED IN MACHINERY OR\nMECHANICAL APPLIANCES OR FOR OTHER TECHNICAL USES - ARTICLES OF LEATHER OR OF\nCOMPOSITION LEATHER, OF A KIND USED IN MACHINERY OR MECHANICAL APPLIANCES OR FOR\nOTHER TECHNICAL USES : LEATHER STRING",
+        "hsn_code": "42040060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF LEATHER OR OF COMPOSITION LEATHER, OF A KIND USED IN MACHINERY OR\nMECHANICAL APPLIANCES OR FOR OTHER TECHNICAL USES - ARTICLES OF LEATHER OR OF\nCOMPOSITION LEATHER, OF A KIND USED IN MACHINERY OR MECHANICAL APPLIANCES OR FOR\nOTHER TECHNICAL USES : DIAPHRAGM LEATHER",
+        "hsn_code": "42040050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF LEATHER OR OF COMPOSITION LEATHER, OF A KIND USED IN MACHINERY OR\nMECHANICAL APPLIANCES OR FOR OTHER TECHNICAL USES - ARTICLES OF LEATHER OR OF\nCOMPOSITION LEATHER, OF A KIND USED IN MACHINERY OR MECHANICAL APPLIANCES OR FOR\nOTHER TECHNICAL USES : LEATHER BELTING FOR MACHINERY",
+        "hsn_code": "42040040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF LEATHER OR OF COMPOSITION LEATHER, OF A KIND USED IN MACHINERY OR\nMECHANICAL APPLIANCES OR FOR OTHER TECHNICAL USES - ARTICLES OF LEATHER OR OF\nCOMPOSITION LEATHER, OF A KIND USED IN MACHINERY OR MECHANICAL APPLIANCES OR FOR\nOTHER TECHNICAL USES : PICKING BANDS AND STRAPS FOR TEXTILE MACHINERY",
+        "hsn_code": "42040030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF LEATHER OR OF COMPOSITION LEATHER, OF A KIND USED IN MACHINERY OR\nMECHANICAL APPLIANCES OR FOR OTHER TECHNICAL USES - ARTICLES OF LEATHER OR OF\nCOMPOSITION LEATHER, OF A KIND USED IN MACHINERY OR MECHANICAL APPLIANCES OR FOR\nOTHER TECHNICAL USES : PICKERS FOR JUTE MACHINERY",
+        "hsn_code": "42040020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ARTICLES OF LEATHER OR OF COMPOSITION LEATHER, OF A KIND USED IN MACHINERY OR\nMECHANICAL APPLIANCES OR FOR OTHER TECHNICAL USES - ARTICLES OF LEATHER OR OF\nCOMPOSITION LEATHER, OF A KIND USED IN MACHINERY OR MECHANICAL APPLIANCES OR FOR\nOTHER TECHNICAL USES : PICKERS FOR COTTON MACHINERY",
+        "hsn_code": "42040010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HYGIENIC OR PHARMACEUTICAL ARTICLES (INCLUDING TEATS), OF VULCANISED RUBBER OTHER\nTHAN HARD RUBBER, WITH OR WITHOUT FITTINGS OF HARD RUBBER - OTHER : OTHER",
+        "hsn_code": "40149090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HYGIENIC OR PHARMACEUTICAL ARTICLES (INCLUDING TEATS), OF VULCANISED RUBBER OTHER\nTHAN HARD RUBBER, WITH OR WITHOUT FITTINGS OF HARD RUBBER - OTHER : FEEDING BOTTLE\nNIPPLES",
+        "hsn_code": "40149030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HYGIENIC OR PHARMACEUTICAL ARTICLES (INCLUDING TEATS), OF VULCANISED RUBBER OTHER\nTHAN HARD RUBBER, WITH OR WITHOUT FITTINGS OF HARD RUBBER - OTHER : ICE BAGS",
+        "hsn_code": "40149020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HYGIENIC OR PHARMACEUTICAL ARTICLES (INCLUDING TEATS), OF VULCANISED RUBBER OTHER\nTHAN HARD RUBBER, WITH OR WITHOUT FITTINGS OF HARD RUBBER - OTHER : HOT WATER\nBOTTLES",
+        "hsn_code": "40149010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HYGIENIC OR PHARMACEUTICAL ARTICLES (INCLUDING TEATS), OF VULCANISED RUBBER OTHER\nTHAN HARD RUBBER, WITH OR WITHOUT FITTINGS OF HARD RUBBER - SHEATH\nCONTRACEPTIVES : RUBBER CONTRACEPTIVES, FEMALE (DIAPHRAGMS), SUCH AS CERVICAL CAPS",
+        "hsn_code": "40141020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HYGIENIC OR PHARMACEUTICAL ARTICLES (INCLUDING TEATS), OF VULCANISED RUBBER OTHER\nTHAN HARD RUBBER, WITH OR WITHOUT FITTINGS OF HARD RUBBER - SHEATH\nCONTRACEPTIVES : RUBBER CONTRACEPTIVES, MALE (CONDOMS)",
+        "hsn_code": "40141010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nOTHER : OTHER: OTHER",
+        "hsn_code": "39269099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nOTHER : OTHER: OF POLYURETHANE FOAM",
+        "hsn_code": "39269091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nOTHER : POLYPROPYLENE ARTICLES, NOT ELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "39269080",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nOTHER : PLASTIC OR NYLON TIPPED HAMMERS; INSULATING LINER OF NYLON , HDPE : OTHER",
+        "hsn_code": "39269079",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nOTHER : PLASTIC OR NYLON TIPPED HAMMERS; INSULATING LINER OF NYLON , HDPE : OF\nPOLYURETHANE FOAM",
+        "hsn_code": "39269071",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nOTHER : HANGERS : OTHER",
+        "hsn_code": "39269069",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nOTHER : HANGERS : OF POLYURETHANE FOAM",
+        "hsn_code": "39269061",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nOTHER : RETROREFLECTIVE SHEETING OF OTHER THAN OF HEADING 3920 : OTHER",
+        "hsn_code": "39269059",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nOTHER : RETROREFLECTIVE SHEETING OF OTHER THAN OF HEADING 3920 : OF POLYURETHANE\nFOAM",
+        "hsn_code": "39269051",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nOTHER : RINGS, BUCKLES, TACKS, WASHERS AND OTHER DECORATIVE FITTINGS MADE OF\nPLASTIC USED AS TRIMMINGS AND EMBELLISHMENTS FOR LEATHER PRODUCTS; PATTERNS FOR\nLEATHER FOOT WEAR, LEATHER GARMENTS AND LEATHER GOODS: OTHER",
+        "hsn_code": "39269049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nOTHER : RINGS, BUCKLES, TACKS, WASHERS AND OTHER DECORATIVE FITTINGS MADE OF\nPLASTIC USED AS TRIMMINGS AND EMBELLISHMENTS FOR LEATHER PRODUCTS; PATTERNS FOR\nLEATHER FOOT WEAR, LEATHER GARMENTS AND LEATHER GOODS: OF POLYURETHANE FOAM",
+        "hsn_code": "39269041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nOTHER : LASTS, WITH OR WITHOUT STEEL HINGES ; EVA AND GRAPE SHEETS FOR SOLES AND\nHEELS; WELTS: OTHER",
+        "hsn_code": "39269039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nOTHER : LASTS, WITH OR WITHOUT STEEL HINGES ; EVA AND GRAPE SHEETS FOR SOLES AND\nHEELS; WELTS: OF POLYURETHANE FOAM",
+        "hsn_code": "39269031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nOTHER : COUPLERS, PACKING RINGS, O RINGS AND THE LIKE: OTHER",
+        "hsn_code": "39269029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nOTHER : COUPLERS, PACKING RINGS, O RINGS AND THE LIKE: OF POLYURETHANE FOAM",
+        "hsn_code": "39269021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nOTHER : PVC BELT CONVEYOR",
+        "hsn_code": "39269010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nSTATUETTES AND OTHER ORNAMENTAL ARTICLES : OTHER : OTHER",
+        "hsn_code": "39264099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nSTATUETTES AND OTHER ORNAMENTAL ARTICLES : OTHER : OF POLYURETHANE FOAM",
+        "hsn_code": "39264091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nSTATUETTES AND OTHER ORNAMENTAL ARTICLES :SEQUINE",
+        "hsn_code": "39264060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nSTATUETTES AND OTHER ORNAMENTAL ARTICLES : DECORATIVE SHEETS : OTHER",
+        "hsn_code": "39264059",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nSTATUETTES AND OTHER ORNAMENTAL ARTICLES : DECORATIVE SHEETS : OF POLYURETHANE\nFOAM",
+        "hsn_code": "39264051",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nSTATUETTES AND OTHER ORNAMENTAL ARTICLES : TABLE AND OTHER HOUSEHOLD ARTICLES\n(INCLUDING HOTEL AND RESTAURANT) FOR DECORATION : OTHER",
+        "hsn_code": "39264049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nSTATUETTES AND OTHER ORNAMENTAL ARTICLES : TABLE AND OTHER HOUSEHOLD ARTICLES\n(INCLUDING HOTEL AND RESTAURANT) FOR DECORATION : OF POLYURETHANE FOAM",
+        "hsn_code": "39264041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nSTATUETTES AND OTHER ORNAMENTAL ARTICLES : STATUETTES : OTHER",
+        "hsn_code": "39264039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nSTATUETTES AND OTHER ORNAMENTAL ARTICLES : STATUETTES : OF POLYURETHANE FOAM",
+        "hsn_code": "39264031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nSTATUETTES AND OTHER ORNAMENTAL ARTICLES : BEADS : OTHER",
+        "hsn_code": "39264029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nSTATUETTES AND OTHER ORNAMENTAL ARTICLES : BEADS : OF POLYURETHANE FOAM",
+        "hsn_code": "39264021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nSTATUETTES AND OTHER ORNAMENTAL ARTICLES : BANGLES : OTHER",
+        "hsn_code": "39264019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nSTATUETTES AND OTHER ORNAMENTAL ARTICLES : BANGLES : OF POLYURETHANE FOAM",
+        "hsn_code": "39264011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nFITTINGS FOR FURNITURE, COACH WORK OR THE LIKE : OTHER",
+        "hsn_code": "39263090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nFITTINGS FOR FURNITURE, COACH WORK OR THE LIKE : OF POLYURETHANE FOAM",
+        "hsn_code": "39263010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nARTICLES OF APPAREL AND CLOTHING ACCESSORIES (INCLUDING GLOVES, MITTENS AND MITTS)\n: OTHER : OTHER",
+        "hsn_code": "39262099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nARTICLES OF APPAREL AND CLOTHING ACCESSORIES (INCLUDING GLOVES, MITTENS AND MITTS)\n: OTHER : OF POLYURETHANE FOAM",
+        "hsn_code": "39262091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nARTICLES OF APPAREL AND CLOTHING ACCESSORIES (INCLUDING GLOVES, MITTENS AND MITTS)\n: COLLAR STAYS, PATTIES, BUTTERFLY, SHOULDER - PADS AND OTHER STAYS : OTHER",
+        "hsn_code": "39262049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nARTICLES OF APPAREL AND CLOTHING ACCESSORIES (INCLUDING GLOVES, MITTENS AND MITTS)\n: COLLAR STAYS, PATTIES, BUTTERFLY, SHOULDER - PADS AND OTHER STAYS : OF POLYURETHANE\nFOAM",
+        "hsn_code": "39262041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nARTICLES OF APPAREL AND CLOTHING ACCESSORIES (INCLUDING GLOVES, MITTENS AND MITTS)\n: PLASTIC STICKERS FOR GARMENTS : OTHER",
+        "hsn_code": "39262039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nARTICLES OF APPAREL AND CLOTHING ACCESSORIES (INCLUDING GLOVES, MITTENS AND MITTS)\n: PLASTIC STICKERS FOR GARMENTS : OF POLYURETHANE FOAM",
+        "hsn_code": "39262031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nARTICLES OF APPAREL AND CLOTHING ACCESSORIES (INCLUDING GLOVES, MITTENS AND MITTS)\n: APRONS : OTHER",
+        "hsn_code": "39262029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nARTICLES OF APPAREL AND CLOTHING ACCESSORIES (INCLUDING GLOVES, MITTENS AND MITTS)\n: APRONS : OF POLYURETHANE FOAM",
+        "hsn_code": "39262021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nARTICLES OF APPAREL AND CLOTHING ACCESSORIES (INCLUDING GLOVES, MITTENS AND MITTS)\n: I - GLOVES: NON - DISPOSABLE",
+        "hsn_code": "39262019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914-\nARTICLES OF APPAREL AND CLOTHING ACCESSORIES (INCLUDING GLOVES, MITTENS AND MITTS)\n: I - GLOVES: DISPOSABLE",
+        "hsn_code": "39262011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914 -\nOFFICE OR SCHOOL SUPPLIES: OTHER : OTHER",
+        "hsn_code": "39261099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914 -\nOFFICE OR SCHOOL SUPPLIES: OTHER : OF POLYURETHANE FOAM",
+        "hsn_code": "39261091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914 -\nOFFICE OR SCHOOL SUPPLIES: OFFICE SUPPLIES OF A KIND CLASSIFIED AS STATIONERY OTHER\nTHAN PINS, CLIPS, AND WRITING INSTRUMENTS : OTHER",
+        "hsn_code": "39261019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER ARTICLES OF PLASTICS AND ARTICLES OF OTHER MATERIALS OF HEADINGS 3901 TO 3914 -\nOFFICE OR SCHOOL SUPPLIES: OFFICE SUPPLIES OF A KIND CLASSIFIED AS STATIONERY OTHER\nTHAN PINS, CLIPS, AND WRITING INSTRUMENTS : OF POLYURETHANE FOAM",
+        "hsn_code": "39261011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "NATURAL POLYMERS (FOR EXAMPLE, ALGINIC ACID) AND MODIFIED NATURAL POLYMERS (FOR\nEXAMPLE, HARDENED PROTEINS, CHEMICAL DERIVATIVES OF NATURAL RUBBER), NOT\nELSEWHERE SPECIFIED OR INCLUDED, IN PRIMARY FORMS - OTHER :OTHER",
+        "hsn_code": "39139090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "NATURAL POLYMERS (FOR EXAMPLE, ALGINIC ACID) AND MODIFIED NATURAL POLYMERS (FOR\nEXAMPLE, HARDENED PROTEINS, CHEMICAL DERIVATIVES OF NATURAL RUBBER), NOT\nELSEWHERE SPECIFIED OR INCLUDED, IN PRIMARY FORMS - OTHER :DEXTRAN",
+        "hsn_code": "39139030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "NATURAL POLYMERS (FOR EXAMPLE, ALGINIC ACID) AND MODIFIED NATURAL POLYMERS (FOR\nEXAMPLE, HARDENED PROTEINS, CHEMICAL DERIVATIVES OF NATURAL RUBBER), NOT\nELSEWHERE SPECIFIED OR INCLUDED, IN PRIMARY FORMS - OTHER :HARDENED PROTEINS (SUCH\nAS HARDENED CASEIN, GELATIN)",
+        "hsn_code": "39139020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "NATURAL POLYMERS (FOR EXAMPLE, ALGINIC ACID) AND MODIFIED NATURAL POLYMERS (FOR\nEXAMPLE, HARDENED PROTEINS, CHEMICAL DERIVATIVES OF NATURAL RUBBER), NOT\nELSEWHERE SPECIFIED OR INCLUDED, IN PRIMARY FORMS - OTHER : CHEMICAL DERIVATIVES OF\nNATURAL RUBBER:OTHER",
+        "hsn_code": "39139019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "NATURAL POLYMERS (FOR EXAMPLE, ALGINIC ACID) AND MODIFIED NATURAL POLYMERS (FOR\nEXAMPLE, HARDENED PROTEINS, CHEMICAL DERIVATIVES OF NATURAL RUBBER), NOT\nELSEWHERE SPECIFIED OR INCLUDED, IN PRIMARY FORMS - OTHER : CHEMICAL DERIVATIVES OF\nNATURAL RUBBER:CHLORINATED RUBBER",
+        "hsn_code": "39139011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "NATURAL POLYMERS (FOR EXAMPLE, ALGINIC ACID) AND MODIFIED NATURAL POLYMERS (FOR\nEXAMPLE, HARDENED PROTEINS, CHEMICAL DERIVATIVES OF NATURAL RUBBER), NOT\nELSEWHERE SPECIFIED OR INCLUDED, IN PRIMARY FORMS - ALGINIC ACID, ITS SALTS AND ESTERS\n:OTHER",
+        "hsn_code": "39131090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "NATURAL POLYMERS (FOR EXAMPLE, ALGINIC ACID) AND MODIFIED NATURAL POLYMERS (FOR\nEXAMPLE, HARDENED PROTEINS, CHEMICAL DERIVATIVES OF NATURAL RUBBER), NOT\nELSEWHERE SPECIFIED OR INCLUDED, IN PRIMARY FORMS - ALGINIC ACID, ITS SALTS AND ESTERS\n:SODIUM ALGINATE",
+        "hsn_code": "39131010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - OTHER:OTHER",
+        "hsn_code": "39129090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - OTHER:VISCOSE SPONGE",
+        "hsn_code": "39129020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - OTHER: CELLULOSE PROPIONATE AND ACETO PROPIONATE, NON -\nPLASTICISED",
+        "hsn_code": "39129010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE ETHERS: OTHER : PLASTICISED: OTHER CELLULOSE ETHER",
+        "hsn_code": "39123929",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE ETHERS: OTHER : PLASTICISED: METHYL CELLULOSE",
+        "hsn_code": "39123922",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE ETHERS: OTHER : PLASTICISED: ETHYL CELLULOSE",
+        "hsn_code": "39123921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE ETHERS: OTHER : NON - PLASCISED:OTHER CELLULOSE ETHERS",
+        "hsn_code": "39123919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE ETHERS: OTHER : NON - PLASCISED:METHYLCELLULOSE",
+        "hsn_code": "39123912",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE ETHERS: OTHER : NON - PLASCISED:ETHYLCELLULOSE",
+        "hsn_code": "39123911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE ETHERS: CARBOXYMETHYL CELLULOSE AND ITS SALTS",
+        "hsn_code": "39123100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE NITRATES (INCLUDING COLLODIONS): PLASTICISED : OTHER",
+        "hsn_code": "39122029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE NITRATES (INCLUDING COLLODIONS): PLASTICISED : MOULDING\nPOWDERS",
+        "hsn_code": "39122021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE NITRATES (INCLUDING COLLODIONS): NON - PLASTICISED:OTHER",
+        "hsn_code": "39122019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE NITRATES (INCLUDING COLLODIONS): NON -\nPLASTICISED:MOULDING POWDERS",
+        "hsn_code": "39122011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE ACETATES : PLASTICISED :OTHER",
+        "hsn_code": "39121290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE ACETATES : PLASTICISED :CELLULOSE ACETOBUTYRATE\nMOULDING POWDER",
+        "hsn_code": "39121230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE ACETATES : PLASTICISED :CELLULOSE ACETATE MOULDING\nPOWDER",
+        "hsn_code": "39121220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE ACETATES : PLASTICISED :CELLULOSE ACETATE FLAKES",
+        "hsn_code": "39121210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE ACETATES : NON - PLASTICISED:OTHER",
+        "hsn_code": "39121190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE ACETATES : NON - PLASTICISED: CELLULOSE NITRATE, DYNAMIC\nGRADE",
+        "hsn_code": "39121140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE ACETATES : NON - PLASTICISED:CELLULOSE ACETOBUTYRATE\nMOULDING POWDER",
+        "hsn_code": "39121130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE ACETATES : NON - PLASTICISED:CELLULOSE ACETATE MOULDING\nPOWDER",
+        "hsn_code": "39121120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CELLULOSE AND ITS CHEMICAL DERIVATIVES, NOT ELSEWHERE SPECIFIED OR INCLUDED, IN\nPRIMARY FORMS - CELLULOSE ACETATES : NON - PLASTICISED:CELLULOSE ACETATE FLAKES",
+        "hsn_code": "39121110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM RESINS, COUMARONE - INDENE RESINS, POLYTERPENES, POLYSULPHIDES,\nPOLYSULPHONES AND OTHER PRODUCTS SPECIFIED IN NOTE 3 TO THIS CHAPTER, NOT\nELSEWHERE SPECIFIED OR INCLUDED, IN PRIMARY FORMS - OTHER :OTHER",
+        "hsn_code": "39119090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM RESINS, COUMARONE - INDENE RESINS, POLYTERPENES, POLYSULPHIDES,\nPOLYSULPHONES AND OTHER PRODUCTS SPECIFIED IN NOTE 3 TO THIS CHAPTER, NOT\nELSEWHERE SPECIFIED OR INCLUDED, IN PRIMARY FORMS - OTHER :POLYSULPHONES",
+        "hsn_code": "39119010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM RESINS, COUMARONE - INDENE RESINS, POLYTERPENES, POLYSULPHIDES,\nPOLYSULPHONES AND OTHER PRODUCTS SPECIFIED IN NOTE 3 TO THIS CHAPTER, NOT\nELSEWHERE SPECIFIED OR INCLUDED, IN PRIMARY FORMS - PETROLEUM RESINS, COUMARONE -\nINDENE OR COUMARONE - INDENE RESINS AND POLYTERPENES:OTHER",
+        "hsn_code": "39111090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM RESINS, COUMARONE - INDENE RESINS, POLYTERPENES, POLYSULPHIDES,\nPOLYSULPHONES AND OTHER PRODUCTS SPECIFIED IN NOTE 3 TO THIS CHAPTER, NOT\nELSEWHERE SPECIFIED OR INCLUDED, IN PRIMARY FORMS - PETROLEUM RESINS, COUMARONE -\nINDENE OR COUMARONE - INDENE RESINS AND POLYTERPENES:COUMARONE - INDENE RESINS",
+        "hsn_code": "39111010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILICONES IN PRIMARY FORMS - SILICONES IN PRIMARY FORMS - OTHER",
+        "hsn_code": "39100090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILICONES IN PRIMARY FORMS - SILICONES IN PRIMARY FORMS - SILICONE OIL",
+        "hsn_code": "39100020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SILICONES IN PRIMARY FORMS - SILICONES IN PRIMARY FORMS - SILICONE RESINS",
+        "hsn_code": "39100010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMINO - RESINS, PHENOLIC RESINS AND POLYURETHANES, IN PRIMARY FORMS -\nPOLYURETHANES",
+        "hsn_code": "39095000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMINO - RESINS, PHENOLIC RESINS AND POLYURETHANES, IN PRIMARY FORMS - PHENOLIC\nRESINS :OTHER",
+        "hsn_code": "39094090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMINO - RESINS, PHENOLIC RESINS AND POLYURETHANES, IN PRIMARY FORMS - PHENOLIC\nRESINS : TERPENE PHENOLIC RESINS",
+        "hsn_code": "39094060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMINO - RESINS, PHENOLIC RESINS AND POLYURETHANES, IN PRIMARY FORMS - PHENOLIC\nRESINS : PHENOXI RESINS",
+        "hsn_code": "39094050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMINO - RESINS, PHENOLIC RESINS AND POLYURETHANES, IN PRIMARY FORMS - PHENOLIC\nRESINS : KETONIC RESINS",
+        "hsn_code": "39094040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMINO - RESINS, PHENOLIC RESINS AND POLYURETHANES, IN PRIMARY FORMS - PHENOLIC\nRESINS :ALKYL PHENOL - FORMALDEHYDE RESINS",
+        "hsn_code": "39094030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMINO - RESINS, PHENOLIC RESINS AND POLYURETHANES, IN PRIMARY FORMS - PHENOLIC\nRESINS : PHENOL FORMALDEHYDE RESINS",
+        "hsn_code": "39094020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMINO - RESINS, PHENOLIC RESINS AND POLYURETHANES, IN PRIMARY FORMS - PHENOLIC\nRESINS :CRESOL FORMALDEHYDE OXIDE",
+        "hsn_code": "39094010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMINO - RESINS, PHENOLIC RESINS AND POLYURETHANES, IN PRIMARY FORMS - OTHER AMINO -\nRESINS:OTHER",
+        "hsn_code": "39093090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMINO - RESINS, PHENOLIC RESINS AND POLYURETHANES, IN PRIMARY FORMS - OTHER AMINO -\nRESINS: POLY (PHENYLENE OXIDE)",
+        "hsn_code": "39093010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMINO - RESINS, PHENOLIC RESINS AND POLYURETHANES, IN PRIMARY FORMS - MELAMINE\nRESINS:OTHER",
+        "hsn_code": "39092090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMINO - RESINS, PHENOLIC RESINS AND POLYURETHANES, IN PRIMARY FORMS - MELAMINE\nRESINS:MELAMINE FORMALDEHYDE RESINS",
+        "hsn_code": "39092010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMINO - RESINS, PHENOLIC RESINS AND POLYURETHANES, IN PRIMARY FORMS - UREA RESINS;\nTHIOUREA RESINS :OTHER",
+        "hsn_code": "39091090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMINO - RESINS, PHENOLIC RESINS AND POLYURETHANES, IN PRIMARY FORMS - UREA RESINS;\nTHIOUREA RESINS :UREA FORMALDEHYDE RESINS",
+        "hsn_code": "39091010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYAMIDES IN PRIMARY FORMS - OTHER : OTHER",
+        "hsn_code": "39089090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYAMIDES IN PRIMARY FORMS - OTHER : NYLON IN OTHER PRIMARY FORMS",
+        "hsn_code": "39089020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYAMIDES IN PRIMARY FORMS - OTHER : NYLON MOULDING POWDER",
+        "hsn_code": "39089010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYAMIDES IN PRIMARY FORMS - POLYAMIDE - 6, - 11, - 12, - 6, 6, - 6, 9, - 6, 10 OR -\n6,12:OTHER",
+        "hsn_code": "39081090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYAMIDES IN PRIMARY FORMS - POLYAMIDE - 6, - 11, - 12, - 6, 6, - 6, 9, - 6, 10 OR -\n6,12:NYLON MOULDING POWDER",
+        "hsn_code": "39081010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS;\nPOLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY\nFORMS - OTHER POLYESTERS : OTHER :OTHER",
+        "hsn_code": "39079990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS;\nPOLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY\nFORMS - OTHER POLYESTERS : OTHER : POLY(BUTYLENE TEREPTHALATE)",
+        "hsn_code": "39079920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS;\nPOLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY\nFORMS - OTHER POLYESTERS : OTHER :DIALLYL PHTHALATE RESINS",
+        "hsn_code": "39079910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS;\nPOLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY\nFORMS - OTHER POLYESTERS : UNSATURATED :OTHER",
+        "hsn_code": "39079190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLY (BUTYLENE TEREPTHALATE)",
+        "hsn_code": "39079150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS;\nPOLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY\nFORMS - OTHER POLYESTERS : UNSATURATED :DIALLYLPHTHALATE RESINS",
+        "hsn_code": "39079140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS;\nPOLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY\nFORMS - OTHER POLYESTERS : UNSATURATED : FUMERIC RESINS",
+        "hsn_code": "39079130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS;\nPOLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY\nFORMS - OTHER POLYESTERS : UNSATURATED :POLYESTER OR CONTRACT RESINS",
+        "hsn_code": "39079120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS;\nPOLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY\nFORMS - OTHER POLYESTERS : UNSATURATED :MALEIC RESINS",
+        "hsn_code": "39079110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS;\nPOLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY\nFORMS - POLY(LACTIC ACID)",
+        "hsn_code": "39077000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS;\nPOLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY\nFORMS - POLY (ETHYLENE TEREPTHALATE): OTHER (INCLUDING CLEAN, CLOURLESS GRADES)",
+        "hsn_code": "39076090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS;\nPOLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY\nFORMS - POLY (ETHYLENE TEREPTHALATE): HAVING AN INTRINSIC VISCOSITY OF NOT LESS THAN\n0.64 DL/G AND NOT GREATER THAN 0.72 DL/G",
+        "hsn_code": "39076020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS;\nPOLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY\nFORMS - POLY (ETHYLENE TEREPTHALATE):HAVING AN INTRINSIC VISCOSITY OF LESS THAN 0.64\nDL/G",
+        "hsn_code": "39076010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS;\nPOLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY\nFORMS - ALKYD RESINS",
+        "hsn_code": "39075000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS;\nPOLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY\nFORMS - POLYCARBONATES",
+        "hsn_code": "39074000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS;\nPOLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY\nFORMS - EPOXIDE RESINS :OTHER",
+        "hsn_code": "39073090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS;\nPOLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY\nFORMS - EPOXIDE RESINS :EPOXY RESINS",
+        "hsn_code": "39073010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS;\nPOLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY\nFORMS - OTHER POLYETHERS:OTHER",
+        "hsn_code": "39072090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS;\nPOLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY\nFORMS - OTHER POLYETHERS:POLY (ETHER ALCOHOLS)",
+        "hsn_code": "39072010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYACETALS, OTHER POLYETHERS AND EPOXIDE RESINS, IN PRIMARY FORMS;\nPOLYCARBONATES, ALKYD RESINS, POLYALLYLESTERS AND OTHER POLYESTERS, IN PRIMARY\nFORMS - POLYACETALS",
+        "hsn_code": "39071000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ACRYLIC POLYMERS IN PRIMARY FORMS - OTHER :OTHER",
+        "hsn_code": "39069090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ACRYLIC POLYMERS IN PRIMARY FORMS - OTHER :COPOLYMERS OF ACRYLONITRILE",
+        "hsn_code": "39069030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ACRYLIC POLYMERS IN PRIMARY FORMS - OTHER : POLYACRYLATE MOULDING POWDER",
+        "hsn_code": "39069020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ACRYLIC POLYMERS IN PRIMARY FORMS - OTHER :ACRYLIC RESINS",
+        "hsn_code": "39069010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ACRYLIC POLYMERS IN PRIMARY FORMS - POLY (METHYL METHACRYLATE): OTHER",
+        "hsn_code": "39061090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ACRYLIC POLYMERS IN PRIMARY FORMS - POLY (METHYL METHACRYLATE): BINDERS FOR\nPIGMENTS OR INKS",
+        "hsn_code": "39061010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL ACETATE OR OF OTHER VINYL ESTERS, IN PRIMARY FORMS; OTHER VINYL\nPOLYMERS IN PRIMARY FORMS - OTHER : OTHER :OTHER",
+        "hsn_code": "39059990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL ACETATE OR OF OTHER VINYL ESTERS, IN PRIMARY FORMS; OTHER VINYL\nPOLYMERS IN PRIMARY FORMS - OTHER : OTHER :POLY (VINYL PIROLIDONE) (P ALCOHOL)",
+        "hsn_code": "39059910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL ACETATE OR OF OTHER VINYL ESTERS, IN PRIMARY FORMS; OTHER VINYL\nPOLYMERS IN PRIMARY FORMS - OTHER : COPOLYMERS",
+        "hsn_code": "39059100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL ACETATE OR OF OTHER VINYL ESTERS, IN PRIMARY FORMS; OTHER VINYL\nPOLYMERS IN PRIMARY FORMS - POLY (VINYL ALCOHOL), WHETHER OR NOT CONTAINING\nUNHYDROLYSED ACETATE GROUPS",
+        "hsn_code": "39053000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL ACETATE OR OF OTHER VINYL ESTERS, IN PRIMARY FORMS; OTHER VINYL\nPOLYMERS IN PRIMARY FORMS - VINYL ACETATE COPOLYMERS : OTHER",
+        "hsn_code": "39052900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL ACETATE OR OF OTHER VINYL ESTERS, IN PRIMARY FORMS; OTHER VINYL\nPOLYMERS IN PRIMARY FORMS - VINYL ACETATE COPOLYMERS : IN AQUEOUS DISPERSION",
+        "hsn_code": "39052100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL ACETATE OR OF OTHER VINYL ESTERS, IN PRIMARY FORMS; OTHER VINYL\nPOLYMERS IN PRIMARY FORMS POLYMERS OF VINYL ACETATE OR OF OTHER VINYL ESTERS, IN\nPRIMARY FORMS; OTHER VINYL POLYMERS IN PRIMARY FORMS - POLY (VINYL ACETATE) : OTHER\n:OTHER",
+        "hsn_code": "39051990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL ACETATE OR OF OTHER VINYL ESTERS, IN PRIMARY FORMS; OTHER VINYL\nPOLYMERS IN PRIMARY FORMS POLYMERS OF VINYL ACETATE OR OF OTHER VINYL ESTERS, IN\nPRIMARY FORMS; OTHER VINYL POLYMERS IN PRIMARY FORMS - POLY (VINYL ACETATE) : OTHER\n:POLY (VINYL ACETATE) AND RESINS",
+        "hsn_code": "39051920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL ACETATE OR OF OTHER VINYL ESTERS, IN PRIMARY FORMS; OTHER VINYL\nPOLYMERS IN PRIMARY FORMS POLYMERS OF VINYL ACETATE OR OF OTHER VINYL ESTERS, IN\nPRIMARY FORMS; OTHER VINYL POLYMERS IN PRIMARY FORMS - POLY (VINYL ACETATE) : OTHER\n:POLY (VINYL ACETATE) (PVA) MOULDING MATERIAL",
+        "hsn_code": "39051910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL ACETATE OR OF OTHER VINYL ESTERS, IN PRIMARY FORMS; OTHER VINYL\nPOLYMERS IN PRIMARY FORMS POLYMERS OF VINYL ACETATE OR OF OTHER VINYL ESTERS, IN\nPRIMARY FORMS; OTHER VINYL POLYMERS IN PRIMARY FORMS - POLY (VINYL ACETATE) : IN\nAQUEOUS DISPERSION: OTHER",
+        "hsn_code": "39051290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL ACETATE OR OF OTHER VINYL ESTERS, IN PRIMARY FORMS; OTHER VINYL\nPOLYMERS IN PRIMARY FORMS POLYMERS OF VINYL ACETATE OR OF OTHER VINYL ESTERS, IN\nPRIMARY FORMS; OTHER VINYL POLYMERS IN PRIMARY FORMS - POLY (VINYL ACETATE) : IN\nAQUEOUS DISPERSION:POLY (VINYL ACETATE) RESINS",
+        "hsn_code": "39051220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL ACETATE OR OF OTHER VINYL ESTERS, IN PRIMARY FORMS; OTHER VINYL\nPOLYMERS IN PRIMARY FORMS POLYMERS OF VINYL ACETATE OR OF OTHER VINYL ESTERS, IN\nPRIMARY FORMS; OTHER VINYL POLYMERS IN PRIMARY FORMS - POLY (VINYL ACETATE) : IN\nAQUEOUS DISPERSION:POLY (VINYL ACETATE) (PVA), MOULDING MATERIAL",
+        "hsn_code": "39051210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL CHLORIDE OR OF OTHER HALOGENATED OLEFINS, IN PRIMARY FORMS -\nOTHER",
+        "hsn_code": "39049000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL CHLORIDE OR OF OTHER HALOGENATED OLEFINS, IN PRIMARY FORMS -\nFLURO - POLYMERS : OTHER : OTHER",
+        "hsn_code": "39046990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL CHLORIDE OR OF OTHER HALOGENATED OLEFINS, IN PRIMARY FORMS -\nFLURO - POLYMERS : OTHER : POLY (VINYL FLUORIDE), IN ONE OF THE FORMS MENTIONED IN\nNOTE 6(B) TO THIS CHAPTER",
+        "hsn_code": "39046910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL CHLORIDE OR OF OTHER HALOGENATED OLEFINS, IN PRIMARY FORMS -\nFLURO - POLYMERS : POLYTETRAFLUROETHYLENE",
+        "hsn_code": "39046100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL CHLORIDE OR OF OTHER HALOGENATED OLEFINS, IN PRIMARY FORMS -\nVINYLIDENE CHLORIDE POLYMERS OTHER",
+        "hsn_code": "39045090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL CHLORIDE OR OF OTHER HALOGENATED OLEFINS, IN PRIMARY FORMS -\nVINYLIDENE CHLORIDE POLYMERS COPOLYMER OF VINYLIDENE CHLORIDE WITH ACRYLONITRITE,\nIN THE FORM OF EXPANSIBLE BEADS OF A DIAMETER OF 4 MICROMETERS OR MORE BUT NOT\nMORE THAN 20 MICROMETERS",
+        "hsn_code": "39045010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL CHLORIDE OR OF OTHER HALOGENATED OLEFINS, IN PRIMARY FORMS -\nOTHER VINYL CHLORIDE COPOLYMERS",
+        "hsn_code": "39044000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL CHLORIDE OR OF OTHER HALOGENATED OLEFINS, IN PRIMARY FORMS -\nVINYL CHLORIDE - VINYL ACETATE COPOLYMERS:OTHER",
+        "hsn_code": "39043090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL CHLORIDE OR OF OTHER HALOGENATED OLEFINS, IN PRIMARY FORMS -\nVINYL CHLORIDE - VINYL ACETATE COPOLYMERS:POLY (VINYL DERIVATIVES)",
+        "hsn_code": "39043010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL CHLORIDE OR OF OTHER HALOGENATED OLEFINS, IN PRIMARY FORMS -\nOTHER POLY (VINYL CHLORIDE): PLASTICISED:OTHER",
+        "hsn_code": "39042290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL CHLORIDE OR OF OTHER HALOGENATED OLEFINS, IN PRIMARY FORMS -\nOTHER POLY (VINYL CHLORIDE): PLASTICISED: POLY (VINYL CHLORIDE) (PVC) RESINS (EMULSION\nGRADE)",
+        "hsn_code": "39042210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL CHLORIDE OR OF OTHER HALOGENATED OLEFINS, IN PRIMARY FORMS -\nOTHER POLY (VINYL CHLORIDE): NON - PLASTICISED:OTHER",
+        "hsn_code": "39042190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL CHLORIDE OR OF OTHER HALOGENATED OLEFINS, IN PRIMARY FORMS -\nOTHER POLY (VINYL CHLORIDE): NON - PLASTICISED:POLY (VINYL CHLORIDE) RESINS",
+        "hsn_code": "39042110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL CHLORIDE OR OF OTHER HALOGENATED OLEFINS, IN PRIMARY FORMS -\nPOLY (VINYL CHLORIDE), NOT MIXED WITH ANY OTHER SUBSTANCES:OTHER",
+        "hsn_code": "39041090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF VINYL CHLORIDE OR OF OTHER HALOGENATED OLEFINS, IN PRIMARY FORMS -\nPOLY (VINYL CHLORIDE), NOT MIXED WITH ANY OTHER SUBSTANCES:BINDERS FOR PIGMENTS",
+        "hsn_code": "39041010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF STYRENE, IN PRIMARY FORMS - OTHER: OTHER",
+        "hsn_code": "39039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF STYRENE, IN PRIMARY FORMS - OTHER: BROMINATED POLYSTYRENE,\nCONTAINING BY WEIGHT 58% OR MORE BUT NOT MORE THAN 71% OF BROMINE, IN ONE OF\nTHE FORMS MENTIONED IN NOTE 6(B) TO THIS CHAPTER",
+        "hsn_code": "39039020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF STYRENE, IN PRIMARY FORMS - OTHER: COPOLYMERS, SOLELY OF STYRENE WITH\nALLYL ALCOHOL, OF ANY ACETYL VALUE OF 175 OR MORE",
+        "hsn_code": "39039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF STYRENE, IN PRIMARY FORMS - ACRYLONITRILE - BUTADINE - STYRENE (ABS)\nCOPOLYMERS",
+        "hsn_code": "39033000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF STYRENE, IN PRIMARY FORMS - STYRENE - ACRYLONITRILE (SAN) COPOLYMERS",
+        "hsn_code": "39032000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF STYRENE, IN PRIMARY FORMS - POLYSTYRENE: OTHER: OTHER",
+        "hsn_code": "39031990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF STYRENE, IN PRIMARY FORMS - POLYSTYRENE: OTHER: MOULDING POWDER",
+        "hsn_code": "39031910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF STYRENE, IN PRIMARY FORMS - POLYSTYRENE: EXPANSIBLE",
+        "hsn_code": "39031100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF PROPYLENE OR OF OTHER OLEFINS, IN PRIMARY FORMS OTHER",
+        "hsn_code": "39029000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF PROPYLENE OR OF OTHER OLEFINS, IN PRIMARY FORMS PROPYLENE\nCOPOLYMERS",
+        "hsn_code": "39023000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF PROPYLENE OR OF OTHER OLEFINS, IN PRIMARY FORMS POLY ISO BUTYLENE",
+        "hsn_code": "39022000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF PROPYLENE OR OF OTHER OLEFINS, IN PRIMARY FORMS POLYPROPYLENE",
+        "hsn_code": "39021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF ETHYLENE, IN PRIMARY FORMS - OTHER: OTHER",
+        "hsn_code": "39019090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF ETHYLENE, IN PRIMARY FORMS - OTHER: LINEAR MEDIUM DENSITY\nPOLYETHYLENE (LMDPE)",
+        "hsn_code": "39019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF ETHYLENE, IN PRIMARY FORMS ETHYLENE - VINYL ACETATE COPOLYMERS",
+        "hsn_code": "39013000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF ETHYLENE, IN PRIMARY FORMS POLYETHYLENE HAVING A SPECIFIC GRAVITY OF\n0.94 OR MORE",
+        "hsn_code": "39012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF ETHYLENE, IN PRIMARY FORMS - POLYETHYLENE HAVING A SPECIFIC GRAVITY OF\nLESS THAN 0.94 : OTHER",
+        "hsn_code": "39011090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "POLYMERS OF ETHYLENE, IN PRIMARY FORMS - POLYETHYLENE HAVING A SPECIFIC GRAVITY OF\nLESS THAN 0.94 : LINEAR LOW DENSITY POLYETHYLENE (LLDPE)",
+        "hsn_code": "39011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MATCHES, OTHER THAN PYROTECHNIC ARTICLES OF HEADING 3604 MATCHES, OTHER THAN\nPYROTECHNIC ARTICLES OF HEADING 3604: OTHER",
+        "hsn_code": "36050090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BEAUTY OR MAKE-UP PREPARATIONS AND PREPARATIONS FOR THE CARE OF THE SKIN (OTHER\nTHAN MEDICAMENTS), INCLUDING SUNSCREEN OR SUNTAN PREPARATIONS; MANICURE OR\nPEDICURE PREPARATIONS OTHER: OTHER OTHER",
+        "hsn_code": "33049990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BEAUTY OR MAKE-UP PREPARATIONS AND PREPARATIONS FOR THE CARE OF THE SKIN (OTHER\nTHAN MEDICAMENTS), INCLUDING SUNSCREEN OR SUNTAN PREPARATIONS; MANICURE OR\nPEDICURE PREPARATIONS OTHER: OTHER TURMERIC PREPARATIONS",
+        "hsn_code": "33049950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BEAUTY OR MAKE-UP PREPARATIONS AND PREPARATIONS FOR THE CARE OF THE SKIN (OTHER\nTHAN MEDICAMENTS), INCLUDING SUNSCREEN OR SUNTAN PREPARATIONS; MANICURE OR\nPEDICURE PREPARATIONS OTHER: OTHER SINDUR, BINDI, KUMKUM",
+        "hsn_code": "33049940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BEAUTY OR MAKE-UP PREPARATIONS AND PREPARATIONS FOR THE CARE OF THE SKIN (OTHER\nTHAN MEDICAMENTS), INCLUDING SUNSCREEN OR SUNTAN PREPARATIONS; MANICURE OR\nPEDICURE PREPARATIONS OTHER: OTHER MOISTURISING LOTION",
+        "hsn_code": "33049930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BEAUTY OR MAKE-UP PREPARATIONS AND PREPARATIONS FOR THE CARE OF THE SKIN (OTHER\nTHAN MEDICAMENTS), INCLUDING SUNSCREEN OR SUNTAN PREPARATIONS; MANICURE OR\nPEDICURE PREPARATIONS OTHER: OTHER NAIL POLISH OR LACQUERS",
+        "hsn_code": "33049920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BEAUTY OR MAKE-UP PREPARATIONS AND PREPARATIONS FOR THE CARE OF THE SKIN (OTHER\nTHAN MEDICAMENTS), INCLUDING SUNSCREEN OR SUNTAN PREPARATIONS; MANICURE OR\nPEDICURE PREPARATIONS OTHER: OTHER: FACE CREAMS",
+        "hsn_code": "33049910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BEAUTY OR MAKE-UP PREPARATIONS AND PREPARATIONS FOR THE CARE OF THE SKIN (OTHER\nTHAN MEDICAMENTS), INCLUDING SUNSCREEN OR SUNTAN PREPARATIONS; MANICURE OR\nPEDICURE PREPARATIONS OTHER: POWDERS, WHETHER OR NOT COMPRESSED: OTHER",
+        "hsn_code": "33049190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BEAUTY OR MAKE-UP PREPARATIONS AND PREPARATIONS FOR THE CARE OF THE SKIN (OTHER\nTHAN MEDICAMENTS), INCLUDING SUNSCREEN OR SUNTAN PREPARATIONS; MANICURE OR\nPEDICURE PREPARATIONS OTHER: POWDERS, WHETHER OR NOT COMPRESSED: TALCUM\nPOWDERS",
+        "hsn_code": "33049120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BEAUTY OR MAKE-UP PREPARATIONS AND PREPARATIONS FOR THE CARE OF THE SKIN (OTHER\nTHAN MEDICAMENTS), INCLUDING SUNSCREEN OR SUNTAN PREPARATIONS; MANICURE OR\nPEDICURE PREPARATIONS OTHER: POWDERS, WHETHER OR NOT COMPRESSED: FACE POWDERS",
+        "hsn_code": "33049110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BEAUTY OR MAKE-UP PREPARATIONS AND PREPARATIONS FOR THE CARE OF THE SKIN (OTHER\nTHAN MEDICAMENTS), INCLUDING SUNSCREEN OR SUNTAN PREPARATIONS; MANICURE OR\nPEDICURE PREPARATIONS MANICURE OR PEDICURE PREPARATIONS",
+        "hsn_code": "33043000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BEAUTY OR MAKE-UP PREPARATIONS AND PREPARATIONS FOR THE CARE OF THE SKIN (OTHER\nTHAN MEDICAMENTS), INCLUDING SUNSCREEN OR SUNTAN PREPARATIONS; MANICURE OR\nPEDICURE PREPARATIONS EYE MAKE-UP PREPARATIONS",
+        "hsn_code": "33042000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BEAUTY OR MAKE-UP PREPARATIONS AND PREPARATIONS FOR THE CARE OF THE SKIN (OTHER\nTHAN MEDICAMENTS), INCLUDING SUNSCREEN OR SUNTAN PREPARATIONS; MANICURE OR\nPEDICURE PREPARATIONS LIP MAKE-UP PREPARATIONS",
+        "hsn_code": "33041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FERTILISERS, WHETHER OR NOT MIXED TOGETHER OR CHEMICALLY\nTREATED; FERTILISERS PRODUCED BY THE MIXING OR CHEMICAL TREATMENT OF ANIMAL OR\nVEGETABLE PRODUCTS ANIMAL OR VEGETABLE FERTILISERS, WHETHER OR NOT MIXED\nTOGETHER OR CHEMICALLY TREATED; FERTILIZERS PRODUCED BY THE MIXING OR CHEMICAL\nTREATMENT OF ANIMAL OR VEGETABLE PRODUCTS : OTHER : OTHER",
+        "hsn_code": "31010099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FERTILISERS, WHETHER OR NOT MIXED TOGETHER OR CHEMICALLY\nTREATED; FERTILISERS PRODUCED BY THE MIXING OR CHEMICAL TREATMENT OF ANIMAL OR\nVEGETABLE PRODUCTS ANIMAL OR VEGETABLE FERTILISERS, WHETHER OR NOT MIXED\nTOGETHER OR CHEMICALLY TREATED; FERTILIZERS PRODUCED BY THE MIXING OR CHEMICAL\nTREATMENT OF ANIMAL OR VEGETABLE PRODUCTS : OTHER : ANIMAL EXCRETA",
+        "hsn_code": "31010092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FERTILISERS, WHETHER OR NOT MIXED TOGETHER OR CHEMICALLY\nTREATED; FERTILISERS PRODUCED BY THE MIXING OR CHEMICAL TREATMENT OF ANIMAL OR\nVEGETABLE PRODUCTS ANIMAL OR VEGETABLE FERTILISERS, WHETHER OR NOT MIXED\nTOGETHER OR CHEMICALLY TREATED; FERTILIZERS PRODUCED BY THE MIXING OR CHEMICAL\nTREATMENT OF ANIMAL OR VEGETABLE PRODUCTS : OTHER : ANIMAL DUNG",
+        "hsn_code": "31010091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL OR VEGETABLE FERTILISERS, WHETHER OR NOT MIXED TOGETHER OR CHEMICALLY\nTREATED; FERTILISERS PRODUCED BY THE MIXING OR CHEMICAL TREATMENT OF ANIMAL OR\nVEGETABLE PRODUCTS ANIMAL OR VEGETABLE FERTILISERS, WHETHER OR NOT MIXED\nTOGETHER OR CHEMICALLY TREATED; FERTILIZERS PRODUCED BY THE MIXING OR CHEMICAL\nTREATMENT OF ANIMAL OR VEGETABLE PRODUCTS : GUANO",
+        "hsn_code": "31010010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PETROLEUM OILS AND OILS OBTAINED FROM BITUMINOUS MINERALS, CRUDE",
+        "hsn_code": "27090000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PEAT (INCLUDING PEAT LITTER), WHETHER OR NOT AGGLOMERATED - PEAT (INCLUDING PEAT\nLITTER), WHETHER OR NOT AGGLOMERATED: OTHER",
+        "hsn_code": "27030090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PEAT (INCLUDING PEAT LITTER), WHETHER OR NOT AGGLOMERATED - PEAT (INCLUDING PEAT\nLITTER), WHETHER OR NOT AGGLOMERATED: PEAT WHETHER OR NOT COMPRESSED INTO\nBALES, BUT NOT AGGLOMERATED",
+        "hsn_code": "27030010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIGNITE, WHETHER OR NOT AGGLOMERATED, EXCLUDING JET - AGGLOMERATED LIGNITE",
+        "hsn_code": "27022000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIGNITE, WHETHER OR NOT AGGLOMERATED, EXCLUDING JET - LIGNITE, WHETHER OR NOT\nPULVERISED, BUT NOT AGGLOMERATED",
+        "hsn_code": "27021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COAL; BRIQUETTES, OVOIDS AND SIMILAR SOLID FUELS MANUFACTURED FROM COAL -\nBRIQUETTES, OVOIDS AND SIMILAR SOLID FUELS MANUFACTURED FROM COAL: OTHER",
+        "hsn_code": "27012090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COAL; BRIQUETTES, OVOIDS AND SIMILAR SOLID FUELS MANUFACTURED FROM COAL -\nBRIQUETTES, OVOIDS AND SIMILAR SOLID FUELS MANUFACTURED FROM COAL: ANTHRACITE\nAGGLOMERATED",
+        "hsn_code": "27012010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COAL; BRIQUETTES, OVOIDS AND SIMILAR SOLID FUELS MANUFACTURED FROM COAL - COAL,\nWHETHER OR NOT PULVERISED, BUT NOT AGGLOMERATED: - OTHER COAL: OTHER",
+        "hsn_code": "27011990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COAL; BRIQUETTES, OVOIDS AND SIMILAR SOLID FUELS MANUFACTURED FROM COAL - COAL,\nWHETHER OR NOT PULVERISED, BUT NOT AGGLOMERATED: - OTHER COAL: STEAM COAL",
+        "hsn_code": "27011920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COAL; BRIQUETTES, OVOIDS AND SIMILAR SOLID FUELS MANUFACTURED FROM COAL - COAL,\nWHETHER OR NOT PULVERISED, BUT NOT AGGLOMERATED: - OTHER COAL: COKING COAL",
+        "hsn_code": "27011910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COAL; BRIQUETTES, OVOIDS AND SIMILAR SOLID FUELS MANUFACTURED FROM COAL - COAL,\nWHETHER OR NOT PULVERISED, BUT NOT AGGLOMERATED: BITUMINOUS COAL",
+        "hsn_code": "27011200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COAL; BRIQUETTES, OVOIDS AND SIMILAR SOLID FUELS MANUFACTURED FROM COAL - COAL,\nWHETHER OR NOT PULVERISED, BUT NOT AGGLOMERATED: ANTHRACITE",
+        "hsn_code": "27011100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SALT (INCLUDING TABLE SALT AND DENATURED SALT) AND PURE SODIUM CHLORIDE, WHETHER\nOR NOT IN AQUEOUS SOLUTION OR CONTAINING ADDED ANTI-CAKING OR FREE FLOWING\nAGENTS; SEA WATER - SALT (INCLUDING TABLE SALT AND DENATURED SALT) AND PURE SODIUM\nCHLORIDE, WHETHER OR NOT IN AQUEOUS SOLUTION OR CONTAINING ADDED ANTI-CAKING OR\nFREE FLOWING AGENTS; SEA WATER: OTHER",
+        "hsn_code": "25010090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SALT (INCLUDING TABLE SALT AND DENATURED SALT) AND PURE SODIUM CHLORIDE, WHETHER\nOR NOT IN AQUEOUS SOLUTION OR CONTAINING ADDED ANTI-CAKING OR FREE FLOWING\nAGENTS; SEA WATER - SALT (INCLUDING TABLE SALT AND DENATURED SALT) AND PURE SODIUM\nCHLORIDE, WHETHER OR NOT IN AQUEOUS SOLUTION OR CONTAINING ADDED ANTI-CAKING OR\nFREE FLOWING AGENTS; SEA WATER: ROCK SALT",
+        "hsn_code": "25010020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SALT (INCLUDING TABLE SALT AND DENATURED SALT) AND PURE SODIUM CHLORIDE, WHETHER\nOR NOT IN AQUEOUS SOLUTION OR CONTAINING ADDED ANTI-CAKING OR FREE FLOWING\nAGENTS; SEA WATER - SALT (INCLUDING TABLE SALT AND DENATURED SALT) AND PURE SODIUM\nCHLORIDE, WHETHER OR NOT IN AQUEOUS SOLUTION OR CONTAINING ADDED ANTI-CAKING OR\nFREE FLOWING AGENTS; SEA WATER: COMMON SALT (INCLUDING IODISED SALT)",
+        "hsn_code": "25010010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE - TOBACCO, PARTLY OR WHOLLY STEMMED\nOR STRIPPED : OTHER",
+        "hsn_code": "24012090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE - TOBACCO, PARTLY OR WHOLLY STEMMED\nOR STRIPPED : TOBACCO FOR MANUFACTURE OF HOOKAH TOBACCO",
+        "hsn_code": "24012080",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE - TOBACCO, PARTLY OR WHOLLY STEMMED\nOR STRIPPED : TOBACCO FOR MANUFACTURE OF CIGAR AND CHEROOT",
+        "hsn_code": "24012070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE - TOBACCO, PARTLY OR WHOLLY STEMMED\nOR STRIPPED : TOBACCO FOR MANUFACTURE OF CHEWING TOBACCO",
+        "hsn_code": "24012060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE - TOBACCO, PARTLY OR WHOLLY STEMMED\nOR STRIPPED : TOBACCO FOR MANUFACTURE OF BIRIS",
+        "hsn_code": "24012050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE - TOBACCO, PARTLY OR WHOLLY STEMMED\nOR STRIPPED : BURLEY TOBACCO",
+        "hsn_code": "24012040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE - TOBACCO, PARTLY OR WHOLLY STEMMED\nOR STRIPPED : SUN CURED VIRGINIA TOBACCO",
+        "hsn_code": "24012030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE - TOBACCO, PARTLY OR WHOLLY STEMMED\nOR STRIPPED : SUN CURED COUNTRY (NATU) TOBACCO",
+        "hsn_code": "24012020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE - TOBACCO, PARTLY OR WHOLLY STEMMED\nOR STRIPPED : FLUE CURED VIRGINIA TOBACCO",
+        "hsn_code": "24012010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE - TOBACCO, NOT STEMMED OR STRIPPED :\nOTHER",
+        "hsn_code": "24011090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE - TOBACCO, NOT STEMMED OR STRIPPED :\nTOBACCO FOR MANUFACTURE OF HOOKAH TOBACCO",
+        "hsn_code": "24011080",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE - TOBACCO, NOT STEMMED OR STRIPPED :\nTOBACCO FOR MANUFACTURE OF CIGAR AND CHEROOT",
+        "hsn_code": "24011070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE - TOBACCO, NOT STEMMED OR STRIPPED :\nTOBACCO FOR MANUFACTURE OF CHEWING TOBACCO",
+        "hsn_code": "24011060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE - TOBACCO, NOT STEMMED OR STRIPPED :\nTOBACCO FOR MANUFACTURE OF BIRIS, NOT STEMMED",
+        "hsn_code": "24011050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE - TOBACCO, NOT STEMMED OR STRIPPED :\nBURLEY TOBACCO",
+        "hsn_code": "24011040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE - TOBACCO, NOT STEMMED OR STRIPPED :\nSUN CURED VIRGINIA TOBACCO",
+        "hsn_code": "24011030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE - TOBACCO, NOT STEMMED OR STRIPPED :\nSUN CURED COUNTRY (NATU) TOBACCO",
+        "hsn_code": "24011020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "UNMANUFACTURED TOBACCO; TOBACCO REFUSE - TOBACCO, NOT STEMMED OR STRIPPED :\nFLUE CURED VIRGINIA TOBACCO",
+        "hsn_code": "24011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS OF A KIND USED IN ANIMAL FEEDING - OTHER : OTHER",
+        "hsn_code": "23099090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS OF A KIND USED IN ANIMAL FEEDING - OTHER : FEEDS FOR FISH (PRAWN, ETC.) :\nOTHER",
+        "hsn_code": "23099039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS OF A KIND USED IN ANIMAL FEEDING - OTHER : FEEDS FOR FISH (PRAWN, ETC.) :\nFISH MEAL IN POWDERED FORM",
+        "hsn_code": "23099032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS OF A KIND USED IN ANIMAL FEEDING - OTHER : FEEDS FOR FISH (PRAWN, ETC.) :\nPRAWN AND SHRIMPS FEED",
+        "hsn_code": "23099031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS OF A KIND USED IN ANIMAL FEEDING - OTHER : CONCENTRATES FOR\nCOMPOUND ANIMAL FEED",
+        "hsn_code": "23099020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS OF A KIND USED IN ANIMAL FEEDING - OTHER : COMPOUNDED ANIMAL FEED",
+        "hsn_code": "23099010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PREPARATIONS OF A KIND USED IN ANIMAL FEEDING DOG OR CAT FOOD, PUT UP FOR RETAIL\nSALE",
+        "hsn_code": "23091000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE MATERIALS AND VEGETABLE WASTE, VEGETABLE RESIDUES AND BY-PRODUCTS,\nWHETHER OR NOT IN THE FORM OF PELLETS, OFA KIND USED IN ANIMAL FEEDING,\nNOTELSEWHERE SPECIFIED OR INCLUDED",
+        "hsn_code": "23080000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : OTHER",
+        "hsn_code": "23069090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : RESIDUES BABOOL SEED EXTRACTION",
+        "hsn_code": "23069030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : OIL-CAKE AND OIL-CAKE MEAL, SOLVENT\nEXTRACTED (DEFATTED) VARIETY : OF OTHER SEEDS",
+        "hsn_code": "23069029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : OIL-CAKE AND OIL-CAKE MEAL, SOLVENT\nEXTRACTED (DEFATTED) VARIETY : OF NEEM SEEDS",
+        "hsn_code": "23069028",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : OIL-CAKE AND OIL-CAKE MEAL, SOLVENT\nEXTRACTED (DEFATTED) VARIETY : OF CASTOR SEEDS",
+        "hsn_code": "23069027",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : OIL-CAKE AND OIL-CAKE MEAL, SOLVENT\nEXTRACTED (DEFATTED) VARIETY : OF SAL (DE-OILED)",
+        "hsn_code": "23069026",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : OIL-CAKE AND OIL-CAKE MEAL, SOLVENT\nEXTRACTED (DEFATTED) VARIETY : OF MANGO KERNEL",
+        "hsn_code": "23069025",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : OIL-CAKE AND OIL-CAKE MEAL, SOLVENT\nEXTRACTED (DEFATTED) VARIETY : OF SEASAMUM SEEDS",
+        "hsn_code": "23069024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : OIL-CAKE AND OIL-CAKE MEAL, SOLVENT\nEXTRACTED (DEFATTED) VARIETY : OF CARDI SEEDS",
+        "hsn_code": "23069023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : OIL-CAKE AND OIL-CAKE MEAL, SOLVENT\nEXTRACTED (DEFATTED) VARIETY : OF NIGER SEEDS",
+        "hsn_code": "23069022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : OIL-CAKE AND OIL-CAKE MEAL, SOLVENT\nEXTRACTED (DEFATTED) VARIETY : OF MUSTARD SEEDS",
+        "hsn_code": "23069021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : OIL-CAKE AND OIL-CAKE MEAL, EXPELLER VARIETY :\nOF OTHER SEEDS",
+        "hsn_code": "23069019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : OIL-CAKE AND OIL-CAKE MEAL, EXPELLER VARIETY :\nOF NEEM SEEDS",
+        "hsn_code": "23069018",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : OIL-CAKE AND OIL-CAKE MEAL, EXPELLER VARIETY :\nOF CASTOR SEEDS",
+        "hsn_code": "23069017",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : OIL-CAKE AND OIL-CAKE MEAL, EXPELLER VARIETY :\nOF SAL (DE-OILED)",
+        "hsn_code": "23069016",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : OIL-CAKE AND OIL-CAKE MEAL, EXPELLER VARIETY :\nOF MANGO KERNEL",
+        "hsn_code": "23069015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : OIL-CAKE AND OIL-CAKE MEAL, EXPELLER VARIETY :\nOF SEASAMUM SEEDS",
+        "hsn_code": "23069014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : OIL-CAKE AND OIL-CAKE MEAL, EXPELLER VARIETY :\nOF NIGER SEEDS",
+        "hsn_code": "23069013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : OIL-CAKE AND OIL-CAKE MEAL, EXPELLER VARIETY :\nOF MUSTARD SEEDS",
+        "hsn_code": "23069012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OTHER : OIL-CAKE AND OIL-CAKE MEAL, EXPELLER VARIETY :\nOF MOWRA SEEDS",
+        "hsn_code": "23069011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 OF MAIZE (CORN) GERM",
+        "hsn_code": "23067000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 OF PALM NUTS OR KERNELS",
+        "hsn_code": "23066000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OF COCONUT OR COPRA : OTHER",
+        "hsn_code": "23065090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OF COCONUT OR COPRA : OIL-CAKE AND OIL-CAKE MEAL,\nSOLVENT EXTRACTED (DEFATTED) VARIETY",
+        "hsn_code": "23065020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OF COCONUT OR COPRA : OIL-CAKE AND OIL-CAKE MEAL,\nEXPELLER VARIETY",
+        "hsn_code": "23065010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OF RAPE OR COLZA SEEDS : OTHER",
+        "hsn_code": "23064900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OF RAPE OR COLZA SEEDS : OF LOW ERUCIC ACID RAPE OR\nCOLZA SEEDS",
+        "hsn_code": "23064100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OF SUNFLOWER SEEDS : OTHER",
+        "hsn_code": "23063090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OF SUNFLOWER SEEDS : OIL-CAKE AND OIL-CAKE MEAL,\nSOLVENT EXTRACTED (DEFATTED) VARIETY",
+        "hsn_code": "23063020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OF SUNFLOWER SEEDS : OIL-CAKE AND OIL-CAKE MEAL,\nEXPELLER VARIETY",
+        "hsn_code": "23063010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OF LINSEED : OTHER",
+        "hsn_code": "23062090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OF LINSEED : OIL-CAKE AND OIL-CAKE MEAL, SOLVENT\nEXTRACTED (DEFATTED) VARIETY",
+        "hsn_code": "23062020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OF LINSEED : OIL-CAKE AND OIL-CAKE MEAL, EXPELLER\nVARIETY",
+        "hsn_code": "23062010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OF COTTON SEEDS : OTHER",
+        "hsn_code": "23061090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OF COTTON SEEDS : OIL-CAKE AND OIL-CAKE MEAL,\nUNDECORTICATED, SOLVENT EXTRACTED (DEFATTED) VARIETY",
+        "hsn_code": "23061040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OF COTTON SEEDS : OIL-CAKE AND OIL-CAKE MEAL,\nUNDECORTICATED, EXPELLER VARIETY",
+        "hsn_code": "23061030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OF COTTON SEEDS : OIL-CAKE AND OIL-CAKE MEAL,\nDECORTICATED, SOLVENT EXTRACTED (DEFATTED) VARIETY",
+        "hsn_code": "23061020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OIL-CAKE AND OTHER SOLID RESIDUES, WHETHER OR NOT GROUND OR IN THE FORM\nOFPELLETS, RESULTING FROM THE EXTRACTION OFVEGETABLE FATS OR OILS, OTHER THAN\nTHOSEOF HEADING 2304 OR 2305 - OF COTTON SEEDS : OIL-CAKE AND OIL-CAKE MEAL,\nDECORTICATED EXPELLER VARIETY",
+        "hsn_code": "23061010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22089099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22089092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22089091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22089090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22089020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22089019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22089012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22089011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22089010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22087092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22087091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22087020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22087012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22087011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22087010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22086000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22085093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22085092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22085091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22085020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22085013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22085012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22085011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22085010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22084092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22084091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22084020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22084012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22084011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22084010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22083099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22083093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22083092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22083091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22083090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22083030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22083020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22083019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22083013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22083012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22083011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22083010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22082099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22082092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22082091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22082090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22082020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22082019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22082012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22082011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22082010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22060000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22059000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22043000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22042990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22042920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22042910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22042190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22042120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22042110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "-",
+        "hsn_code": "22030000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WATERS, INCLUDING NATURAL OR ARTIFICIALMINERAL WATERS AND AERATED WATERS,\nNOTCONTAINING ADDED SUGAR OR OTHERSWEETENING MATTER NOR FLAVOURED; ICEAND\nSNOW - OTHER : OTHER",
+        "hsn_code": "22019090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WATERS, INCLUDING NATURAL OR ARTIFICIALMINERAL WATERS AND AERATED WATERS,\nNOTCONTAINING ADDED SUGAR OR OTHERSWEETENING MATTER NOR FLAVOURED; ICEAND\nSNOW - MINERAL WATERS AND AERATED WATERS : AERATED WATERS",
+        "hsn_code": "22011020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WATERS, INCLUDING NATURAL OR ARTIFICIALMINERAL WATERS AND AERATED WATERS,\nNOTCONTAINING ADDED SUGAR OR OTHERSWEETENING MATTER NOR FLAVOURED; ICEAND\nSNOW - MINERAL WATERS AND AERATED WATERS : MINERAL WATERS",
+        "hsn_code": "22011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE MATERIAL OF A KIND USED PRIMARILY IN BROOMS OR IN BRUSHES (FOR EXAMPLE,\nBROOM-CORN, PIASSAVA, COUCH-GRASS AND ISTLE), WHETHER OR NOT IN HANKS OR BUNDLES\nVEGETABLE MATERIAL OF A KIND USED PRIMARILY IN BROOMS OR IN BRUSHES (FOR EXAMPLE,\nBROOM-CORN,PIASSAVA, COUCH-GRASS AND ISTLE), WHETHER OR NOT IN HANKS OR BUNDLES\n: OTHER",
+        "hsn_code": "14030090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE MATERIAL OF A KIND USED PRIMARILY IN BROOMS OR IN BRUSHES (FOR EXAMPLE,\nBROOM-CORN, PIASSAVA, COUCH-GRASS AND ISTLE), WHETHER OR NOT IN HANKS OR BUNDLES\nVEGETABLE MATERIAL OF A KIND USED PRIMARILY IN BROOMS OR IN BRUSHES (FOR EXAMPLE,\nBROOM-CORN,PIASSAVA, COUCH-GRASS AND ISTLE), WHETHER OR NOT IN HANKS OR BUNDLES\n: PALM FIBRES FOR BRUSHES",
+        "hsn_code": "14030010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE MATERIALS OF A KIND USED PRIMARILY AS STUFFING OR AS PADDING (FOR\nEXAMPLE, KAPOK, VEGETABLE HAIR AND EEL?GRASS), WHETHER OR NOT PUT UP AS A LAYER\nWITH OR WITHOUT SUPPORTING MATERIAL",
+        "hsn_code": "14020000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE MATERIALS OF A KIND USED PRIMARILY FOR PLAITING (FOR EXAMPLE, BAMBOOS,\nRATTANS, REEDS, RUSHES, OSIER, RAFFIA, CLEANED, BLEACHED OR DYED CEREAL STRAW, AND\nLIME BARK)OTHER : OTHER",
+        "hsn_code": "14019090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE MATERIALS OF A KIND USED PRIMARILY FOR PLAITING (FOR EXAMPLE, BAMBOOS,\nRATTANS, REEDS, RUSHES, OSIER, RAFFIA, CLEANED, BLEACHED OR DYED CEREAL STRAW, AND\nLIME BARK)OTHER : CANES",
+        "hsn_code": "14019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE MATERIALS OF A KIND USED PRIMARILY FOR PLAITING (FOR EXAMPLE, BAMBOOS,\nRATTANS, REEDS, RUSHES, OSIER, RAFFIA, CLEANED, BLEACHED OR DYED CEREAL STRAW, AND\nLIME BARK)RATTANS",
+        "hsn_code": "14012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLE MATERIALS OF A KIND USED PRIMARILY FOR PLAITING (FOR EXAMPLE, BAMBOOS,\nRATTANS, REEDS, RUSHES, OSIER, RAFFIA, CLEANED, BLEACHED OR DYED CEREAL STRAW, AND\nLIME BARK)BAMBOOS",
+        "hsn_code": "14011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SWEDES, MANGOLDS, FODDER ROOTS, HAY, LUCERNE (ALFALFA), CLOVER, SAINFOIN, FORAGE\nKALE, LUPINES, VETCHES AND SIMILAR FORAGE PRODUCTS, WHETHER OR NOT IN THE FORM OF\nPELLETS OTHER",
+        "hsn_code": "12149000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SWEDES, MANGOLDS, FODDER ROOTS, HAY, LUCERNE (ALFALFA), CLOVER, SAINFOIN, FORAGE\nKALE, LUPINES, VETCHES AND SIMILAR FORAGE PRODUCTS, WHETHER OR NOT IN THE FORM OF\nPELLETS LUCERNE (ALFALFA) MEAL AND PELLETS",
+        "hsn_code": "12141000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CEREAL STRAW AND HUSKS, UNPREPARED, WHETHER OR NOT CHOPPED, GROUND, PRESSEDOR\nIN THE FORM OF PELLETS",
+        "hsn_code": "12130000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LOCUST BEANS, SEAWEEDS AND OTHER ALGAE, SUGAR BEET AND SUGARCANE, FRESH, CHILLED,\nFROZEN OR DRIED, WHETHER OR NOT GROUND; FRUIT STONES AND KERNELS AND OTHER\nVEGETABLE PRODUCTS (INCLUDING UNROASTED CHICORY ROOTS OF THE VARIETY CI-CHORIUM\nINTYBUS SATIVUM) OF A KIND USED PRIMARILY FOR HUMAN CONSUMPTION, NOT ELSEWHERE\nSPECIFIED OR INCLUDED OTHER : OTHER : OTHER",
+        "hsn_code": "12129990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LOCUST BEANS, SEAWEEDS AND OTHER ALGAE, SUGAR BEET AND SUGARCANE, FRESH, CHILLED,\nFROZEN OR DRIED, WHETHER OR NOT GROUND; FRUIT STONES AND KERNELS AND OTHER\nVEGETABLE PRODUCTS (INCLUDING UNROASTED CHICORY ROOTS OF THE VARIETY CI-CHORIUM\nINTYBUS SATIVUM) OF A KIND USED PRIMARILY FOR HUMAN CONSUMPTION, NOT ELSEWHERE\nSPECIFIED OR INCLUDED OTHER : OTHER : MOHUA FLOWERS",
+        "hsn_code": "12129920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LOCUST BEANS, SEAWEEDS AND OTHER ALGAE, SUGAR BEET AND SUGARCANE, FRESH, CHILLED,\nFROZEN OR DRIED, WHETHER OR NOT GROUND; FRUIT STONES AND KERNELS AND OTHER\nVEGETABLE PRODUCTS (INCLUDING UNROASTED CHICORY ROOTS OF THE VARIETY CI-CHORIUM\nINTYBUS SATIVUM) OF A KIND USED PRIMARILY FOR HUMAN CONSUMPTION, NOT ELSEWHERE\nSPECIFIED OR INCLUDED OTHER : OTHER : KOKAM (COCUM) FLOWERS",
+        "hsn_code": "12129910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LOCUST BEANS, SEAWEEDS AND OTHER ALGAE, SUGAR BEET AND SUGARCANE, FRESH, CHILLED,\nFROZEN OR DRIED, WHETHER OR NOT GROUND; FRUIT STONES AND KERNELS AND OTHER\nVEGETABLE PRODUCTS (INCLUDING UNROASTED CHICORY ROOTS OF THE VARIETY CI-CHORIUM\nINTYBUS SATIVUM) OF A KIND USED PRIMARILY FOR HUMAN CONSUMPTION, NOT ELSEWHERE\nSPECIFIED OR INCLUDED OTHER : SUGAR BEET",
+        "hsn_code": "12129100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LOCUST BEANS, SEAWEEDS AND OTHER ALGAE, SUGAR BEET AND SUGARCANE, FRESH, CHILLED,\nFROZEN OR DRIED, WHETHER OR NOT GROUND; FRUIT STONES AND KERNELS AND OTHER\nVEGETABLE PRODUCTS (INCLUDING UNROASTED CHICORY ROOTS OF THE VARIETY CI-CHORIUM\nINTYBUS SATIVUM) OF A KIND USED PRIMARILY FOR HUMAN CONSUMPTION, NOT ELSEWHERE\nSPECIFIED OR INCLUDED APRICOT, PEACH (INCLUDING NECTARINE) OR PLUM STONES AND\nKERNELS : OTHER",
+        "hsn_code": "12123090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LOCUST BEANS, SEAWEEDS AND OTHER ALGAE, SUGAR BEET AND SUGARCANE, FRESH, CHILLED,\nFROZEN OR DRIED, WHETHER OR NOT GROUND; FRUIT STONES AND KERNELS AND OTHER\nVEGETABLE PRODUCTS (INCLUDING UNROASTED CHICORY ROOTS OF THE VARIETY CI-CHORIUM\nINTYBUS SATIVUM) OF A KIND USED PRIMARILY FOR HUMAN CONSUMPTION, NOT ELSEWHERE\nSPECIFIED OR INCLUDED APRICOT, PEACH (INCLUDING NECTARINE) OR PLUM STONES AND\nKERNELS : APRICOT KERNELS",
+        "hsn_code": "12123010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LOCUST BEANS, SEAWEEDS AND OTHER ALGAE, SUGAR BEET AND SUGARCANE, FRESH, CHILLED,\nFROZEN OR DRIED, WHETHER OR NOT GROUND; FRUIT STONES AND KERNELS AND OTHER\nVEGETABLE PRODUCTS (INCLUDING UNROASTED CHICORY ROOTS OF THE VARIETY CI-CHORIUM\nINTYBUS SATIVUM) OF A KIND USED PRIMARILY FOR HUMAN CONSUMPTION, NOT ELSEWHERE\nSPECIFIED OR INCLUDED SEAWEEDS AND OTHER ALGAE : OTHER ALGAE",
+        "hsn_code": "12122090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LOCUST BEANS, SEAWEEDS AND OTHER ALGAE, SUGAR BEET AND SUGARCANE, FRESH, CHILLED,\nFROZEN OR DRIED, WHETHER OR NOT GROUND; FRUIT STONES AND KERNELS AND OTHER\nVEGETABLE PRODUCTS (INCLUDING UNROASTED CHICORY ROOTS OF THE VARIETY CI-CHORIUM\nINTYBUS SATIVUM) OF A KIND USED PRIMARILY FOR HUMAN CONSUMPTION, NOT ELSEWHERE\nSPECIFIED OR INCLUDED SEAWEEDS AND OTHER ALGAE : SEAWEEDS",
+        "hsn_code": "12122010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LOCUST BEANS, SEAWEEDS AND OTHER ALGAE, SUGAR BEET AND SUGARCANE, FRESH, CHILLED,\nFROZEN OR DRIED, WHETHER OR NOT GROUND; FRUIT STONES AND KERNELS AND OTHER\nVEGETABLE PRODUCTS (INCLUDING UNROASTED CHICORY ROOTS OF THE VARIETY CI-CHORIUM\nINTYBUS SATIVUM) OF A KIND USED PRIMARILY FOR HUMAN CONSUMPTION, NOT ELSEWHERE\nSPECIFIED OR INCLUDED LOCUST BEANS, INCLUDING LOCUST BEAN SEEDS : OTHER",
+        "hsn_code": "12121090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LOCUST BEANS, SEAWEEDS AND OTHER ALGAE, SUGAR BEET AND SUGARCANE, FRESH, CHILLED,\nFROZEN OR DRIED, WHETHER OR NOT GROUND; FRUIT STONES AND KERNELS AND OTHER\nVEGETABLE PRODUCTS (INCLUDING UNROASTED CHICORY ROOTS OF THE VARIETY CI-CHORIUM\nINTYBUS SATIVUM) OF A KIND USED PRIMARILY FOR HUMAN CONSUMPTION, NOT ELSEWHERE\nSPECIFIED OR INCLUDED LOCUST BEANS, INCLUDING LOCUST BEAN SEEDS : OF SEED QUALITY",
+        "hsn_code": "12121010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : OTHER : OTHER",
+        "hsn_code": "12119099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : OTHER : GARCENIA",
+        "hsn_code": "12119096",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : OTHER : LOVAGE",
+        "hsn_code": "12119095",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : OTHER : BASIL, HYSSOP,\nROSEMARY, SAGE AND SAVORY",
+        "hsn_code": "12119094",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : OTHER : UNAB (INDIAN\nJUJUBA OR CHINESE DATES)",
+        "hsn_code": "12119093",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : OTHER : TUKMARIA",
+        "hsn_code": "12119092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : OTHER : CHIRATA",
+        "hsn_code": "12119091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : AGARWOOD",
+        "hsn_code": "12119080",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : MINT INCLUDING LEAVES (ALL\nSPECIES)",
+        "hsn_code": "12119070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : VINCA ROSEA HERBS",
+        "hsn_code": "12119060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : SANDALWOOD CHIPS AND\nDUST",
+        "hsn_code": "12119050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : ROOTS AND RHIZOMES :\nOTHER",
+        "hsn_code": "12119049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : ROOTS AND RHIZOMES :\nSWEET FLAG RHIZOMES",
+        "hsn_code": "12119048",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : ROOTS AND RHIZOMES :\nSARASAPARILLA ROOTS",
+        "hsn_code": "12119047",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : ROOTS AND RHIZOMES :\nKUTH ROOT",
+        "hsn_code": "12119046",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : ROOTS AND RHIZOMES :\nZEDOVARY ROOTS",
+        "hsn_code": "12119045",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : ROOTS AND RHIZOMES :\nSERPENTINA ROOTS (ROWWALFIA SERPENTINA AND OTHER SPECIES OF ROWWALFIAS)",
+        "hsn_code": "12119044",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : ROOTS AND RHIZOMES :\nIPECAC DRIED RHIZOME AND ROOTS",
+        "hsn_code": "12119043",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : ROOTS AND RHIZOMES :\nGALANGAL RHIZOMES AND ROOTS",
+        "hsn_code": "12119042",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : ROOTS AND RHIZOMES :\nBELLADONA ROOTS",
+        "hsn_code": "12119041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : BARK, HUSK AND RIND :\nOTHER",
+        "hsn_code": "12119039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : BARK, HUSK AND RIND :\nGAMBOGE FRUIT RIND",
+        "hsn_code": "12119033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : BARK, HUSK AND RIND :\nPSYLLIUM HUSK (ISOBGUL HUSK)",
+        "hsn_code": "12119032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : BARK, HUSK AND RIND :\nCASCARA SAGRADA BARK",
+        "hsn_code": "12119031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : LEAVES, POWDER, FLOWERS\nAND PODS : OTHER",
+        "hsn_code": "12119029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : LEAVES, POWDER, FLOWERS\nAND PODS : PYRETHRUM",
+        "hsn_code": "12119026",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : LEAVES, POWDER, FLOWERS\nAND PODS : CUBEB POWDER",
+        "hsn_code": "12119025",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : LEAVES, POWDER, FLOWERS\nAND PODS : GYMNEMA POWDER",
+        "hsn_code": "12119024",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : LEAVES, POWDER, FLOWERS\nAND PODS : NEEM LEAVES, POWDER",
+        "hsn_code": "12119023",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : LEAVES, POWDER, FLOWERS\nAND PODS : SENNA LEAVES AND PODS",
+        "hsn_code": "12119022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : LEAVES, POWDER, FLOWERS\nAND PODS : BELLADONA LEAVES",
+        "hsn_code": "12119021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : SEEDS : OTHER",
+        "hsn_code": "12119019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : SEEDS : JOJOBA SEED",
+        "hsn_code": "12119015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : SEEDS : NEEM SEED",
+        "hsn_code": "12119014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : SEEDS : PSYLLIUM SEED\n(ISOBGUL)",
+        "hsn_code": "12119013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : SEEDS : NUXVOMICA, DRIED\nRIPE SEEDS",
+        "hsn_code": "12119012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED OTHER : SEEDS : AMBRETTE SEEDS",
+        "hsn_code": "12119011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED POPPY STRAW",
+        "hsn_code": "12114000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED COCA LEAF",
+        "hsn_code": "12113000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED GINSENG ROOTS",
+        "hsn_code": "12112000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PLANTS AND PARTS OF PLANTS (INCLUDING SEEDS AND FRUITS), OF A KIND USED PRIMARILY IN\nPERFUMERY, IN PHARMACY OR FOR INSECTICIDAL, FUNGICIDAL OR SIMILAR PURPOSE, FRESH OR\nDRIED, WHETHER OR NOT CUT, CRUSHED OR POWDERED LIQUORICE ROOTS",
+        "hsn_code": "12111000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING OTHER : OTHER : OTHER",
+        "hsn_code": "12099990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING OTHER : OTHER : FRUIT SEEDS FOR\nPLANTING OR SOWING",
+        "hsn_code": "12099910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING OTHER : VEGETABLE SEEDS : OTHER",
+        "hsn_code": "12099190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING OTHER : VEGETABLE SEEDS : OF\nTOMATO",
+        "hsn_code": "12099160",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING OTHER : VEGETABLE SEEDS : OF\nRADISH",
+        "hsn_code": "12099150",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING OTHER : VEGETABLE SEEDS : OF PEA",
+        "hsn_code": "12099140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING OTHER : VEGETABLE SEEDS : OF\nONION",
+        "hsn_code": "12099130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING OTHER : VEGETABLE SEEDS OF\nCAULIFLOWER",
+        "hsn_code": "12099120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING OTHER : VEGETABLE SEEDS : OF\nCABBAGE",
+        "hsn_code": "12099110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING SEEDS OF HERBACEOUS PLANTS\nCULTIVATED PRINCIPALLY FOR THEIR FLOWERS",
+        "hsn_code": "12093000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING SEEDS OF FORAGE PLANTS : OTHER :\nOTHER",
+        "hsn_code": "12092990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING SEEDS OF FORAGE PLANTS : OTHER :\nAUSTRALIAN LUPIN SEEDS",
+        "hsn_code": "12092910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING SEEDS OF FORAGE PLANTS : TIMOTHY\nGRASS SEED",
+        "hsn_code": "12092600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING SEEDS OF FORAGE PLANTS : RYE\nGRASS (LOLIUM MULTIFLORUM LAM., LOLIUM PERENNE L.) SEED",
+        "hsn_code": "12092500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING SEEDS OF FORAGE PLANTS :\nKENTUCKY BLUE GRASS (POA PRATENSIS L.) SEED",
+        "hsn_code": "12092400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING SEEDS OF FORAGE PLANTS : FESCUE\nSEED",
+        "hsn_code": "12092300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING SEEDS OF FORAGE PLANTS : CLOVER\n(TRIFOLIUM SPP.) SEED",
+        "hsn_code": "12092200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING SEEDS OF FORAGE PLANTS : LUCERNE\n(ALFALFA) SEED",
+        "hsn_code": "12092100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SEEDS, FRUIT AND SPORES, OF A KIND USED FOR SOWING SUGAR BEET SEED",
+        "hsn_code": "12091000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER OIL SEEDS AND OLEAGINOUS FRUITS, WHETHER OR NOT BROKEN OTHER : OTHER :\nOTHER",
+        "hsn_code": "12079990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER OIL SEEDS AND OLEAGINOUS FRUITS, WHETHER OR NOT BROKEN OTHER : OTHER :\nKOKAM",
+        "hsn_code": "12079940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER OIL SEEDS AND OLEAGINO FRUITS, WHETHER OR NOT BROKEN OTHER : OTHER : NIGER\nSEED",
+        "hsn_code": "12079930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER OIL SEEDS AND OLEAGINOUS FRUITS, WHETHER OR NOT BROKEN OTHER : OTHER :\nMANGO KERNEL",
+        "hsn_code": "12079920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER OIL SEEDS AND OLEAGINOUS FRUITS, WHETHER OR NOT BROKEN OTHER : OTHER :\nAJAMS",
+        "hsn_code": "12079910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER OIL SEEDS AND OLEAGINOUS FRUITS, WHETHER OR NOT BROKEN OTHER : POPPY SEEDS",
+        "hsn_code": "12079100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER OIL SEEDS AND OLEAGINOUS FRUITS, WHETHER OR NOT BROKEN SAFFLOWER SEEDS :\nOTHER",
+        "hsn_code": "12076090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER OIL SEEDS AND OLEAGINOUS FRUITS, WHETHER OR NOT BROKEN SAFFLOWER SEEDS : OF\nSEED QUALITY",
+        "hsn_code": "12076010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER OIL SEEDS AND OLEAGINOUS FRUITS, WHETHER OR NOT BROKEN MUSTARD SEEDS :\nOTHER",
+        "hsn_code": "12075090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER OIL SEEDS AND OLEAGINOUS FRUITS, WHETHER OR NOT BROKEN MUSTARD SEEDS : OF\nSEED QUALITY",
+        "hsn_code": "12075010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER OIL SEEDS AND OLEAGINOUS FRUITS, WHETHER OR NOT BROKEN SESAMUM SEEDS :\nOTHER",
+        "hsn_code": "12074090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER OIL SEEDS AND OLEAGINOUS FRUITS, WHETHER OR NOT BROKEN SESAMUM SEEDS : OF\nSEED QUALITY",
+        "hsn_code": "12074010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER OIL SEEDS AND OLEAGINOUS FRUITS, WHETHER OR NOT BROKEN CASTOR OIL SEEDS :\nOTHER",
+        "hsn_code": "12073090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER OIL SEEDS AND OLEAGINOUS FRUITS, WHETHER OR NOT BROKEN CASTOR OIL SEEDS : OF\nSEED QUALITY",
+        "hsn_code": "12073010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER OIL SEEDS AND OLEAGINOUS FRUITS, WHETHER OR NOT BROKEN COTTON SEEDS :\nOTHER",
+        "hsn_code": "12072090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER OIL SEEDS AND OLEAGINOUS FRUITS, WHETHER OR NOT BROKEN COTTON SEEDS : OF\nSEED QUALITY",
+        "hsn_code": "12072010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER OIL SEEDS AND OLEAGINOUS FRUITS, WHETHER OR NOT BROKEN PALM NUTS AND\nKERNELS : OTHER",
+        "hsn_code": "12071090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER OIL SEEDS AND OLEAGINOUS FRUITS, WHETHER OR NOT BROKEN PALM NUTS AND\nKERNELS : OF SEED QUALITY",
+        "hsn_code": "12071010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SUNFLOWER SEEDS, WHETHER OR NOT BROKEN SUNFLOWER SEEDS, WHETHER OR NOT\nBROKEN : OTHER",
+        "hsn_code": "12060090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SUNFLOWER SEEDS, WHETHER OR NOT BROKEN SUNFLOWER SEEDS, WHETHER OR NOT\nBROKEN : OF SEED QUALITY",
+        "hsn_code": "12060010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "RAPE OR COLZA SEEDS, WHETHER OR NOT BROKEN OTHER",
+        "hsn_code": "12059000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "RAPE OR COLZA SEEDS, WHETHER OR NOT BROKEN LOW ERUCIC ACID RAPE OR COLZA SEEDS",
+        "hsn_code": "12051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LINSEED, WHETHER OR NOT BROKEN LINSEED, WHETHER OR NOT BROKEN : OTHER",
+        "hsn_code": "12040090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LINSEED, WHETHER OR NOT BROKEN LINSEED, WHETHER OR NOT BROKEN : OF SEED QUALITY",
+        "hsn_code": "12040010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GROUND?NUTS, NOT ROASTED OR OTHERWISE COOKED, WHETHER OR NOT SHELLED OR\nBROKEN SHELLED, WHETHER OR NOT BROKEN : OTHER",
+        "hsn_code": "12022090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GROUND?NUTS, NOT ROASTED OR OTHERWISE COOKED, WHETHER OR NOT SHELLED OR\nBROKEN SHELLED, WHETHER OR NOT BROKEN : KERNELS, H.P.S.",
+        "hsn_code": "12022010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GROUND?NUTS, NOT ROASTED OR OTHERWISE COOKED, WHETHER OR NOT SHELLED OR\nBROKEN IN SHELL : OTHER : OTHER",
+        "hsn_code": "12021099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GROUND?NUTS, NOT ROASTED OR OTHERWISE COOKED, WHETHER OR NOT SHELLED OR\nBROKEN IN SHELL : OTHER : OF SEED QUALITY",
+        "hsn_code": "12021091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GROUND?NUTS, NOT ROASTED OR OTHERWISE COOKED, WHETHER OR NOT SHELLED OR\nBROKEN IN SHELL : H.P.S : OTHER",
+        "hsn_code": "12021019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GROUND?NUTS, NOT ROASTED OR OTHERWISE COOKED, WHETHER OR NOT SHELLED OR\nBROKEN IN SHELL : H.P.S : OF SEED QUALITY",
+        "hsn_code": "12021011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SOYA BEANS, WHETHER OR NOT BROKEN SOYA BEANS, WHETHER OR NOT BROKEN : OTHER",
+        "hsn_code": "12010090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SOYA BEANS, WHETHER OR NOT BROKEN SOYA BEANS, WHETHER OR NOT BROKEN : OF SEED\nQUALITY",
+        "hsn_code": "12010010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLOUR, MEAL AND POWDER OF THE DRIED LEGUMINOUS VEGETABLES OF HEADING 0713, OF\nSAGO OR OF ROOTS OR TUBERS OF HEADING 0714 OR OF THE PRODUCTS OF CHAPTER 8 OF THE\nPRODUCTS OF CHAPTER 8 : OTHER",
+        "hsn_code": "11063090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLOUR, MEAL AND POWDER OF THE DRIED LEGUMINOUS VEGETABLES OF HEADING 0713, OF\nSAGO OR OF ROOTS OR TUBERS OF HEADING 0714 OR OF THE PRODUCTS OF CHAPTER 8 OF THE\nPRODUCTS OF CHAPTER 8 : MANGO FLOUR",
+        "hsn_code": "11063030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLOUR, MEAL AND POWDER OF THE DRIED LEGUMINOUS VEGETABLES OF HEADING 0713, OF\nSAGO OR OF ROOTS OR TUBERS OF HEADING 0714 OR OF THE PRODUCTS OF CHAPTER 8 OF THE\nPRODUCTS OF CHAPTER 8 : OF SINGODA",
+        "hsn_code": "11063020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLOUR, MEAL AND POWDER OF THE DRIED LEGUMINOUS VEGETABLES OF HEADING 0713, OF\nSAGO OR OF ROOTS OR TUBERS OF HEADING 0714 OR OF THE PRODUCTS OF CHAPTER 8 OF THE\nPRODUCTS OF CHAPTER 8 : OF TAMARIND",
+        "hsn_code": "11063010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLOUR, MEAL AND POWDER OF THE DRIED LEGUMINOUS VEGETABLES OF HEADING 0713, OF\nSAGO OR OF ROOTS OR TUBERS OF HEADING 0714 OR OF THE PRODUCTS OF CHAPTER 8 OF\nSAGO OR OF ROOTS OR TUBERS OF HEADING 0714 : OF OTHER ROOTS AND TUBERS",
+        "hsn_code": "11062090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLOUR, MEAL AND POWDER OF THE DRIED LEGUMINOUS VEGETABLES OF HEADING 0713, OF\nSAGO OR OF ROOTS OR TUBERS OF HEADING 0714 OR OF THE PRODUCTS OF CHAPTER 8 OF\nSAGO OR OF ROOTS OR TUBERS OF HEADING 0714 : OF MANIOC (CASSAVA)",
+        "hsn_code": "11062020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLOUR, MEAL AND POWDER OF THE DRIED LEGUMINOUS VEGETABLES OF HEADING 0713, OF\nSAGO OR OF ROOTS OR TUBERS OF HEADING 0714 OR OF THE PRODUCTS OF CHAPTER 8 OF\nSAGO OR OF ROOTS OR TUBERS OF HEADING 0714 : OF SAGO",
+        "hsn_code": "11062010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLOUR, MEAL AND POWDER OF THE DRIED LEGUMINOUS VEGETABLES OF HEADING 0713, OF\nSAGO OR OF ROOTS OR TUBERS OF HEADING 0714 OR OF THE PRODUCTS OF CHAPTER 8 OF THE\nDRIED LEGUMINOUS VEGETABLES OF HEADING 0713",
+        "hsn_code": "11061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLOUR, MEAL, POWDER, FLAKES, GRANULES AND PELLETS OF POTATOES FLAKES, GRANULES\nAND PELLETS",
+        "hsn_code": "11052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLOUR, MEAL, POWDER, FLAKES, GRANULES AND PELLETS OF POTATOES FLOUR, MEAL AND\nPOWDER",
+        "hsn_code": "11051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CEREAL GRAINS OTHERWISE WORKED (FOR EXAMPLE, HULLED, ROLLED, FLAKED, PEARLED,\nSLICED, OR KIBBLED), EXCEPT RICE OF HEADING 1006; GERM OF CEREALS, WHOLE, ROLLED,\nFLAKED OR GROUND GERM OF CEREALS, WHOLE, ROLLED, FLAKED OR GROUND",
+        "hsn_code": "11043000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CEREAL GRAINS OTHERWISE WORKED (FOR EXAMPLE, HULLED, ROLLED, FLAKED, PEARLED,\nSLICED, OR KIBBLED), EXCEPT RICE OF HEADING 1006; GERM OF CEREALS, WHOLE, ROLLED,\nFLAKED OR GROUND OTHER WORKED GRAINS (FOR EXAMPLE, HULLED, PEARLED, SLICED OR\nKIBBLED) : OF OTHER CEREALS",
+        "hsn_code": "11042900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CEREAL GRAINS OTHERWISE WORKED (FOR EXAMPLE, HULLED, ROLLED, FLAKED, PEARLED,\nSLICED, OR KIBBLED), EXCEPT RICE OF HEADING 1006; GERM OF CEREALS, WHOLE, ROLLED,\nFLAKED OR GROUND OTHER WORKED GRAINS (FOR EXAMPLE, HULLED, PEARLED, SLICED OR\nKIBBLED) : OF MAIZE (CORN)",
+        "hsn_code": "11042300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CEREAL GRAINS OTHERWISE WORKED (FOR EXAMPLE, HULLED, ROLLED, FLAKED, PEARLED,\nSLICED, OR KIBBLED), EXCEPT RICE OF HEADING 1006; GERM OF CEREALS, WHOLE, ROLLED,\nFLAKED OR GROUND OTHER WORKED GRAINS (FOR EXAMPLE, HULLED, PEARLED, SLICED OR\nKIBBLED) : OF OATS",
+        "hsn_code": "11042200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CEREAL GRAINS OTHERWISE WORKED (FOR EXAMPLE, HULLED, ROLLED, FLAKED, PEARLED,\nSLICED, OR KIBBLED), EXCEPT RICE OF HEADING 1006; GERM OF CEREALS, WHOLE, ROLLED,\nFLAKED OR GROUND ROLLED OR FLAKED GRAINS : OF OTHER CEREALS",
+        "hsn_code": "11041900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CEREAL GRAINS OTHERWISE WORKED (FOR EXAMPLE, HULLED, ROLLED, FLAKED, PEARLED,\nSLICED, OR KIBBLED), EXCEPT RICE OF HEADING 1006; GERM OF CEREALS, WHOLE, ROLLED,\nFLAKED OR GROUND ROLLED OR FLAKED GRAINS : OF OATS",
+        "hsn_code": "11041200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CEREAL GROATS, MEAL AND PELLETS PELLETS",
+        "hsn_code": "11032000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CEREAL GROATS, MEAL AND PELLETS GROATS AND MEAL : OF OTHER CEREALS",
+        "hsn_code": "11031900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CEREAL GROATS, MEAL AND PELLETS GROATS AND MEAL : OF MAIZE (CORN)",
+        "hsn_code": "11031300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CEREAL GROATS, MEAL AND PELLETS GROATS AND MEAL : OF WHEAT : MEAL",
+        "hsn_code": "11031120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CEREAL GROATS, MEAL AND PELLETS GROATS AND MEAL : OF WHEAT : GROAT",
+        "hsn_code": "11031110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CEREAL FLOURS OTHER THAN THAT OF WHEAT OR MESLIN OTHER",
+        "hsn_code": "11029000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CEREAL FLOURS OTHER THAN THAT OF WHEAT OR MESLIN RICE FLOUR",
+        "hsn_code": "11023000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CEREAL FLOURS OTHER THAN THAT OF WHEAT OR MESLIN MAIZE (CORN) FLOUR",
+        "hsn_code": "11022000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CEREAL FLOURS OTHER THAN THAT OF WHEAT OR MESLIN RYE FLOUR",
+        "hsn_code": "11021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WHEAT OR MESLIN FLOUR",
+        "hsn_code": "11010000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PEEL OF CITRUS FRUIT OR MELONS (INCLUDING WATERMELONS), FRESH, FROZEN, DRIED\nORPROVISIONALLY PRESERVED IN BRINE, INSULPHUR WATER OR IN OTHER\nPRESERVATIVESOLUTIONS",
+        "hsn_code": "8140000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, DRIED, OTHER THAN THAT OF HEADINGS0801 TO 0806; MIXTURES OF NUTS OR\nDRIEDFRUITS OF THIS CHAPTER MIXTURES OF DRIED FRUITS",
+        "hsn_code": "8135020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, DRIED, OTHER THAN THAT OF HEADINGS0801 TO 0806; MIXTURES OF NUTS OR\nDRIEDFRUITS OF THIS CHAPTER MIXTURES OF NUTS",
+        "hsn_code": "8135010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, DRIED, OTHER THAN THAT OF HEADINGS0801 TO 0806; MIXTURES OF NUTS OR\nDRIEDFRUITS OF THIS CHAPTEROTHER FRUIT : OTHER",
+        "hsn_code": "8134090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, DRIED, OTHER THAN THAT OF HEADINGS0801 TO 0806; MIXTURES OF NUTS OR\nDRIEDFRUITS OF THIS CHAPTEROTHER FRUIT : SINGODA WHOLE (WATER NUT)",
+        "hsn_code": "8134020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, DRIED, OTHER THAN THAT OF HEADINGS0801 TO 0806; MIXTURES OF NUTS OR\nDRIEDFRUITS OF THIS CHAPTEROTHER FRUIT : TAMARIND, DRIED",
+        "hsn_code": "8134010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, DRIED, OTHER THAN THAT OF HEADINGS0801 TO 0806; MIXTURES OF NUTS OR\nDRIEDFRUITS OF THIS CHAPTER APPLES",
+        "hsn_code": "8133000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, DRIED, OTHER THAN THAT OF HEADINGS0801 TO 0806; MIXTURES OF NUTS OR\nDRIEDFRUITS OF THIS CHAPTER PRUNES",
+        "hsn_code": "8132000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FRUIT, DRIED, OTHER THAN THAT OF HEADINGS0801 TO 0806; MIXTURES OF NUTS OR\nDRIEDFRUITS OF THIS CHAPTER APRICOTS",
+        "hsn_code": "8131000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FRUIT AND NUTS PROVISIONALLY PRESERVED(FOR EXAMPLE, BY SULPHUR DIOXIDE GAS,IN\nBRINE, IN SULPHUR WATER OR IN OTHERPRESERVATIVE SOLUTIONS), BUT UNSUITABLEIN THAT\nSTATE FOR IMMEDIATE CONSUMPTIONOTHER : OTHER",
+        "hsn_code": "8129090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FRUIT AND NUTS PROVISIONALLY PRESERVED(FOR EXAMPLE, BY SULPHUR DIOXIDE GAS,IN\nBRINE, IN SULPHUR WATER OR IN OTHERPRESERVATIVE SOLUTIONS), BUT UNSUITABLEIN THAT\nSTATE FOR IMMEDIATE CONSUMPTION OTHER : MANGO SLICES IN BRINE",
+        "hsn_code": "8129010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FRUIT AND NUTS PROVISIONALLY PRESERVED(FOR EXAMPLE, BY SULPHUR DIOXIDE GAS,IN\nBRINE, IN SULPHUR WATER OR IN OTHERPRESERVATIVE SOLUTIONS), BUT UNSUITABLEIN THAT\nSTATE FOR IMMEDIATE CONSUMPTIONCHERRIES",
+        "hsn_code": "8121000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FRUIT AND NUTS, UNCOOKED OR COOKED BYSTEAMING OR BOILING IN WATER,\nFROZEN,WHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER\nOTHER OTHER",
+        "hsn_code": "8119090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FRUIT AND NUTS, UNCOOKED OR COOKED BYSTEAMING OR BOILING IN WATER,\nFROZEN,WHETHER OR NOT CONTAINING ADDED SUGAR OROTHER SWEETENING MATTER\nOTHER : CONTAINING ADDED SUGAR",
+        "hsn_code": "8119010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "APRICOTS, CHERRIES, PEACHES (INCLUDINGNECTARINES), PLUMS AND SOLES, FRESHPLUMS AND\nSLOES",
+        "hsn_code": "8094000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "APRICOTS, CHERRIES, PEACHES (INCLUDINGNECTARINES), PLUMS AND SOLES, FRESH PEACHES,\nINCLUDING NECTARINE",
+        "hsn_code": "8093000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "APRICOTS, CHERRIES, PEACHES (INCLUDING NECTARINES), PLUMS AND SOLES, FRESH - CHERRIES\n-- OTHER",
+        "hsn_code": "8092900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "APRICOTS, CHERRIES, PEACHES (INCLUDING NECTARINES), PLUMS AND SOLES, FRESH - CHERRIES\n-- SOUR CHERRIES (PRUNUS CERASUS)",
+        "hsn_code": "8092100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "APRICOTS, CHERRIES, PEACHES (INCLUDINGNECTARINES), PLUMS AND SOLES, FRESH APRICOTS",
+        "hsn_code": "8091000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "APPLES, PEARS AND QUINCES, FRESH - QUINCES",
+        "hsn_code": "8084000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "APPLES, PEARS AND QUINCES, FRESH - PEARS",
+        "hsn_code": "8083000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "APPLES, PEARS AND QUINCES, FRESHAPPLES",
+        "hsn_code": "8081000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MELONS (INCLUDING WATERMELONS) ANDPAPAWS (PAPAYAS), FRESHPAPAWS (PAPAYAS)",
+        "hsn_code": "8072000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MELONS (INCLUDING WATERMELONS) ANDPAPAWS (PAPAYAS), FRESH MELONS (INCLUDING\nWATERMELONS) : OTHER",
+        "hsn_code": "8071900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MELONS (INCLUDING WATERMELONS) ANDPAPAWS (PAPAYAS), FRESH MELONS (INCLUDING\nWATERMELONS) : WATER MELONS",
+        "hsn_code": "8071100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GRAPES, FRESH OR DRIED DRIED : OTHER",
+        "hsn_code": "8062090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GRAPES, FRESH OR DRIED DRIED : RAISINS",
+        "hsn_code": "8062010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GRAPES, FRESH OR DRIED FRESH",
+        "hsn_code": "8061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CITRUS FRUIT, FRESH OR DRIED OTHER",
+        "hsn_code": "8059000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CITRUS FRUIT, FRESH OR DRIED LEMON (CITRUS LIMON, CITRUS LIMONUM) AND LIMES (CITRUS\nAURANTIFOLIA, CITRUS LATIFOLIA)",
+        "hsn_code": "8055000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CITRUS FRUIT, FRESH OR DRIED - GRAPE FRUIT, INCLUDING POMELOS",
+        "hsn_code": "8054000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CITRUS FRUIT, FRESH OR DRIED MANDARINS (INCLUDING TANGERINES AND SATSUMAS);\nCLEMENTINES, WILKINGS AND SIMILAR CITRUS HYBRIDS",
+        "hsn_code": "8052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CITRUS FRUIT, FRESH OR DRIED ORANGES",
+        "hsn_code": "8051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DATES, FIGS, PINEAPPLES, AVOCADOS, GUAVAS,MANGOES, AND MANGOSTEENS, FRESH OR\nDRIED GUAVAS, MANGOES AND MANGOSTEENS : OTHER",
+        "hsn_code": "8045090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DATES, FIGS, PINEAPPLES, AVOCADOS, GUAVAS,MANGOES, AND MANGOSTEENS, FRESH OR\nDRIEDGUAVAS, MANGOES AND MANGOSTEENS : MANGO PULP",
+        "hsn_code": "8045040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DATES, FIGS, PINEAPPLES, AVOCADOS, GUAVAS,MANGOES, AND MANGOSTEENS, FRESH OR\nDRIEDGUAVAS, MANGOES AND MANGOSTEENS : MANGOES, SLICED DRIED",
+        "hsn_code": "8045030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DATES, FIGS, PINEAPPLES, AVOCADOS, GUAVAS,MANGOES, AND MANGOSTEENS, FRESH OR\nDRIEDGUAVAS, MANGOES AND MANGOSTEENS : MANGOES, FRESH",
+        "hsn_code": "8045020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DATES, FIGS, PINEAPPLES, AVOCADOS, GUAVAS,MANGOES, AND MANGOSTEENS, FRESH OR\nDRIEDGUAVAS, MANGOES AND MANGOSTEENS : GUAVAS, FRESH OR DRIED",
+        "hsn_code": "8045010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DATES, FIGS, PINEAPPLES, AVOCADOS, GUAVAS,MANGOES, AND MANGOSTEENS, FRESH OR\nDRIEDAVOCADOS",
+        "hsn_code": "8044000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DATES, FIGS, PINEAPPLES, AVOCADOS, GUAVAS,MANGOES, AND MANGOSTEENS, FRESH OR\nDRIED PINEAPPLES",
+        "hsn_code": "8043000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DATES, FIGS, PINEAPPLES, AVOCADOS, GUAVAS,MANGOES, AND MANGOSTEENS, FRESH OR\nDRIEDFIGS : OTHER",
+        "hsn_code": "8042090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DATES, FIGS, PINEAPPLES, AVOCADOS, GUAVAS,MANGOES, AND MANGOSTEENS, FRESH OR\nDRIEDFIGS : FRESH",
+        "hsn_code": "8042010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DATES, FIGS, PINEAPPLES, AVOCADOS, GUAVAS,MANGOES, AND MANGOSTEENS, FRESH OR\nDRIEDDATES : OTHER",
+        "hsn_code": "8041090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DATES, FIGS, PINEAPPLES, AVOCADOS, GUAVAS,MANGOES, AND MANGOSTEENS, FRESH OR\nDRIEDDATES : HARD (CHHOHARA OR KHAREK)",
+        "hsn_code": "8041030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DATES, FIGS, PINEAPPLES, AVOCADOS, GUAVAS,MANGOES, AND MANGOSTEENS, FRESH OR\nDRIEDDATES : SOFT (KHAYZUR OR WET DATES)",
+        "hsn_code": "8041020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DATES, FIGS, PINEAPPLES, AVOCADOS, GUAVAS,MANGOES, AND MANGOSTEENS, FRESH OR\nDRIEDDATES : FRESH (EXCLUDING WET DATES)",
+        "hsn_code": "8041010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BANANAS, INCLUDING PLANTAINS, FRESH OR DRIED - OTHER --- OTHER",
+        "hsn_code": "8039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BANANAS, INCLUDING PLANTAINS, FRESH OR DRIED - OTHER --- BANANAS, FRESH",
+        "hsn_code": "8039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BANANAS, INCLUDING PLANTAINS, FRESH OR DRIED - PLANTAINS --- OTHER",
+        "hsn_code": "8031090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BANANAS, INCLUDING PLANTAINS, FRESH OR DRIED - PLANTAINS --- CURRY PLANTAIN",
+        "hsn_code": "8031010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER NUTS, FRESH OR DRIED, WHETHER OR NOT SHELLED OR PEELED - OTHER",
+        "hsn_code": "8029000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER NUTS, FRESH OR DRIED, WHETHER OR NOT SHELLED OR PEELED - ARECA NUTS ---OTHER",
+        "hsn_code": "8028090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER NUTS, FRESH OR DRIED, WHETHER OR NOT SHELLED OR PEELED - ARECA NUTS ---\nGROUND",
+        "hsn_code": "8028030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER NUTS, FRESH OR DRIED, WHETHER OR NOT SHELLED OR PEELED - ARECA NUTS ---SPLIT",
+        "hsn_code": "8028020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER NUTS, FRESH OR DRIED, WHETHER OR NOT SHELLED OR PEELED - ARECA NUTS ---WHOLE",
+        "hsn_code": "8028010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER NUTS, FRESH OR DRIED, WHETHER OR NOT SHELLED OR PEELED - KOLA NUTS (COLA SPP.)",
+        "hsn_code": "8027000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER NUTS, FRESH OR DRIED, WHETHER OR NOT SHELLED OR PEELED - MACADEMIA NUTS --\nSHELLED",
+        "hsn_code": "8026200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER NUTS, FRESH OR DRIED, WHETHER OR NOT SHELLED OR PEELED - MACADEMIA NUTS --\nIN SHELL",
+        "hsn_code": "8026100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER NUTS, FRESH OR DRIED, WHETHER OR NOT SHELLED OR PEELED - PISTACHIOS --SHELLED",
+        "hsn_code": "8025200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER NUTS, FRESH OR DRIED, WHETHER OR NOT SHELLED OR PEELED - PISTACHIOS -- IN SHELL",
+        "hsn_code": "8025100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER NUTS, FRESH OR DRIED, WHETHER OR NOT SHELLED OR PEELED - CHESTNUTS (CASTANEA\nSPP.) : -- SHELLED",
+        "hsn_code": "8024200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER NUTS, FRESH OR DRIED, WHETHER OR NOT SHELLED OR PEELED - CHESTNUTS (CASTANEA\nSPP.) : -- IN SHELL",
+        "hsn_code": "8024100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER NUTS, FRESH OR DRIED, WHETHEROR NOT SHELLED OR PEELED WALNUTS : SHELLED",
+        "hsn_code": "8023200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER NUTS, FRESH OR DRIED, WHETHEROR NOT SHELLED OR PEELED WALNUTS : IN SHELL",
+        "hsn_code": "8023100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER NUTS, FRESH OR DRIED, WHETHEROR NOT SHELLED OR PEE HAZELNUTS OR FILBERTS\n(CORYLUS SPP.) : SHELLED",
+        "hsn_code": "8022200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER NUTS, FRESH OR DRIED, WHETHEROR NOT SHELLED OR PEE HAZELNUTS OR FILBERTS\n(CORYLUS SPP.) : IN SHELL",
+        "hsn_code": "8022100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER NUTS, FRESH OR DRIED, WHETHEROR NOT SHELLED OR PEELEDALMONDS : SHELLED",
+        "hsn_code": "8021200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER NUTS, FRESH OR DRIED, WHETHEROR NOT SHELLED OR PEELEDALMONDS : IN SHELL",
+        "hsn_code": "8021100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUTS, BRAZIL NUTS AND CASHEW NUTS,FRESH OR DRIED, WHETHER OR NOT SHELLEDOR\nPEELEDCASHEW NUTS SHELLED OTHER",
+        "hsn_code": "8013290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUTS, BRAZIL NUTS AND CASHEW NUTS,FRESH OR DRIED, WHETHER OR NOT SHELLEDOR\nPEELEDCASHEW NUTS SHELLED SHELLED : CASHEW KERNEL, WHOLE",
+        "hsn_code": "8013220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUTS, BRAZIL NUTS AND CASHEW NUTS,FRESH OR DRIED, WHETHER OR NOT SHELLEDOR\nPEELEDCASHEW NUTS SHELLED CASHEW KERNEL, BROKEN",
+        "hsn_code": "8013210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUTS, BRAZIL NUTS AND CASHEW NUTS,FRESH OR DRIED, WHETHER OR NOT SHELLEDOR\nPEELEDBRAZIL NUTS : SHELLED",
+        "hsn_code": "8012200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUTS, BRAZIL NUTS AND CASHEW NUTS,FRESH OR DRIED, WHETHER OR NOT SHELLEDOR\nPEELEDBRAZIL NUTS : IN SHELL",
+        "hsn_code": "8012100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUTS, BRAZIL NUTS AND CASHEW NUTS,FRESH OR DRIED, WHETHER OR NOT SHELLEDOR\nPEELED OTHER : OTHER",
+        "hsn_code": "8011990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUTS, BRAZIL NUTS AND CASHEW NUTS,FRESH OR DRIED, WHETHER OR NOT SHELLEDOR\nPEELED OTHER : DRIED",
+        "hsn_code": "8011920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUTS, BRAZIL NUTS AND CASHEW NUTS,FRESH OR DRIED, WHETHER OR NOT SHELLEDOR\nPEELED OTHER : FRESH",
+        "hsn_code": "8011910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUTS, BRAZIL NUTS AND CASHEW NUTS,FRESH OR DRIED, WHETHER OR NOT SHELLEDOR\nPEELED COCONUTS --IN THE INNER SHELL (ENDOCARP)---OTHER",
+        "hsn_code": "8011290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUTS, BRAZIL NUTS AND CASHEW NUTS,FRESH OR DRIED, WHETHER OR NOT SHELLEDOR\nPEELED COCONUTS --IN THE INNER SHELL ---DRIED",
+        "hsn_code": "8011220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUTS, BRAZIL NUTS AND CASHEW NUTS,FRESH OR DRIED, WHETHER OR NOT SHELLEDOR\nPEELED COCONUTS --IN THE INNER SHELL (ENDOCARP): ---FRESH",
+        "hsn_code": "8011210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COCONUTS, BRAZIL NUTS AND CASHEW NUTS,FRESH OR DRIED, WHETHER OR NOT SHELLEDOR\nPEELED COCONUTS : DESICCATED",
+        "hsn_code": "8011100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL PRODUCTS NOT ELSEWHERE SPECIFIED - OR INCLUDED; DEAD ANIMALS OF CHAPTER 1\nOR 3, - UNFIT FOR HUMAN CONSUMPTION - OTHER : OTHER : - OTHER : - OTHER",
+        "hsn_code": "5119999",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL PRODUCTS NOT ELSEWHERE SPECIFIED - OR INCLUDED; DEAD ANIMALS OF CHAPTER 1\nOR 3, - UNFIT FOR HUMAN CONSUMPTION - OTHER : OTHER : - OTHER : - OF WILD LIFE",
+        "hsn_code": "5119992",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL PRODUCTS NOT ELSEWHERE SPECIFIED - OR INCLUDED; DEAD ANIMALS OF CHAPTER 1\nOR 3, - UNFIT FOR HUMAN CONSUMPTION - OTHER : OTHER : - OTHER : - FROZEN SEMEN,\nOTHER THAN BOVINE; BOVINE EMBRYO",
+        "hsn_code": "5119991",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL PRODUCTS NOT ELSEWHERE SPECIFIED - OR INCLUDED; DEAD ANIMALS OF CHAPTER 1\nOR 3, - UNFIT FOR HUMAN CONSUMPTION - OTHER : OTHER : - SINEWS AND TENDONS : - OTHER",
+        "hsn_code": "5119929",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL PRODUCTS NOT ELSEWHERE SPECIFIED - OR INCLUDED; DEAD ANIMALS OF CHAPTER 1\nOR 3, - UNFIT FOR HUMAN CONSUMPTION - OTHER : OTHER : - SINEWS AND TENDONS : - OF\nWILD LIFE",
+        "hsn_code": "5119921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL PRODUCTS NOT ELSEWHERE SPECIFIED - OR INCLUDED; DEAD ANIMALS OF CHAPTER 1\nOR 3, - UNFIT FOR HUMAN CONSUMPTION - OTHER : OTHER : - SILKWORM PUPAE : - OTHER",
+        "hsn_code": "5119919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL PRODUCTS NOT ELSEWHERE SPECIFIED - OR INCLUDED; DEAD ANIMALS OF CHAPTER 1\nOR 3, - UNFIT FOR HUMAN CONSUMPTION - OTHER : OTHER : - SILKWORM PUPAE : - ARTEMIA",
+        "hsn_code": "5119911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL PRODUCTS NOT ELSEWHERE SPECIFIED - OR INCLUDED; DEAD ANIMALS OF CHAPTER 1\nOR 3, - UNFIT FOR HUMAN CONSUMPTION - OTHER : - PRODUCTS OF FISH OR CRUSTACEANS,\nMOLLUSCS - OR OTHER AQUATIC INVERTEBRATES; DEAD ANIMALS - OF CHAPTER 3 : - OTHER",
+        "hsn_code": "5119190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL PRODUCTS NOT ELSEWHERE SPECIFIED - OR INCLUDED; DEAD ANIMALS OF CHAPTER 1\nOR 3, - UNFIT FOR HUMAN CONSUMPTION - OTHER : - PRODUCTS OF FISH OR CRUSTACEANS,\nMOLLUSCS - OR OTHER AQUATIC INVERTEBRATES; DEAD ANIMALS - OF CHAPTER 3 : - OTHER\nFISH WASTE",
+        "hsn_code": "5119130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL PRODUCTS NOT ELSEWHERE SPECIFIED - OR INCLUDED; DEAD ANIMALS OF CHAPTER 1\nOR 3, - UNFIT FOR HUMAN CONSUMPTION - OTHER : - PRODUCTS OF FISH OR CRUSTACEANS,\nMOLLUSCS - OR OTHER AQUATIC INVERTEBRATES; DEAD ANIMALS - OF CHAPTER 3 : - FISH TAILS",
+        "hsn_code": "5119120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL PRODUCTS NOT ELSEWHERE SPECIFIED - OR INCLUDED; DEAD ANIMALS OF CHAPTER 1\nOR 3, - UNFIT FOR HUMAN CONSUMPTION - OTHER : - PRODUCTS OF FISH OR CRUSTACEANS,\nMOLLUSCS - OR OTHER AQUATIC INVERTEBRATES; DEAD ANIMALS - OF CHAPTER 3 : - FISH NAILS",
+        "hsn_code": "5119110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ANIMAL PRODUCTS NOT ELSEWHERE SPECIFIED - OR INCLUDED; DEAD ANIMALS OF CHAPTER 1\nOR 3, - UNFIT FOR HUMAN CONSUMPTION - BOVINE SEMEN",
+        "hsn_code": "5111000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMBERGRIS, CASTOREUM, CIVET AND MUSK; - CANTHARIDES; BILE, WHETHER OR NOT DRIED; -\nGLANDS AND OTHER ANIMAL PRODUCTS USED - IN THE PREPARATION OF PHARMACEUTICAL -\nPRODUCTS, FRESH, CHILLED, FROZEN OR - OTHERWISE PROVISIONALLY PRESERVED -\nAMBERGRIS, CASTOREUM, CIVET AND MUSK; - CANTHARIDES; BILE, WHETHER OR NOT DRIED; -\nGLANDS AND OTHER ANIMAL PRODUCTS USED - IN THE PREPARATION OF PHARMACEUTICAL -\nPRODUCTS, FRESH, CHILLED, FROZEN OR - OTHERWISE PROVISIONALLY PRESERVED : - OTHER :\nOTHER",
+        "hsn_code": "5100099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMBERGRIS, CASTOREUM, CIVET AND MUSK; - CANTHARIDES; BILE, WHETHER OR NOT DRIED; -\nGLANDS AND OTHER ANIMAL PRODUCTS USED - IN THE PREPARATION OF PHARMACEUTICAL -\nPRODUCTS, FRESH, CHILLED, FROZEN OR - OTHERWISE PROVISIONALLY PRESERVED -\nAMBERGRIS, CASTOREUM, CIVET AND MUSK; - CANTHARIDES; BILE, WHETHER OR NOT DRIED; -\nGLANDS AND OTHER ANIMAL PRODUCTS USED - IN THE PREPARATION OF PHARMACEUTICAL -\nPRODUCTS, FRESH, CHILLED, FROZEN OR - OTHERWISE PROVISIONALLY PRESERVED : - OTHER :\nOF WILD ANIMALS",
+        "hsn_code": "5100091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMBERGRIS, CASTOREUM, CIVET AND MUSK; - CANTHARIDES; BILE, WHETHER OR NOT DRIED; -\nGLANDS AND OTHER ANIMAL PRODUCTS USED - IN THE PREPARATION OF PHARMACEUTICAL -\nPRODUCTS, FRESH, CHILLED, FROZEN OR - OTHERWISE PROVISIONALLY PRESERVED -\nAMBERGRIS, CASTOREUM, CIVET AND MUSK; - CANTHARIDES; BILE, WHETHER OR NOT DRIED; -\nGLANDS AND OTHER ANIMAL PRODUCTS USED - IN THE PREPARATION OF PHARMACEUTICAL -\nPRODUCTS, FRESH, CHILLED, FROZEN OR - OTHERWISE PROVISIONALLY PRESERVED : -\nPLACENTA, FROZEN",
+        "hsn_code": "5100030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMBERGRIS, CASTOREUM, CIVET AND MUSK; - CANTHARIDES; BILE, WHETHER OR NOT DRIED; -\nGLANDS AND OTHER ANIMAL PRODUCTS USED - IN THE PREPARATION OF PHARMACEUTICAL -\nPRODUCTS, FRESH, CHILLED, FROZEN OR - OTHERWISE PROVISIONALLY PRESERVED -\nAMBERGRIS, CASTOREUM, CIVET AND MUSK; - CANTHARIDES; BILE, WHETHER OR NOT DRIED; -\nGLANDS AND OTHER ANIMAL PRODUCTS USED - IN THE PREPARATION OF PHARMACEUTICAL -\nPRODUCTS, FRESH, CHILLED, FROZEN OR - OTHERWISE PROVISIONALLY PRESERVED : - OX\nGALLSTONE",
+        "hsn_code": "5100020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "AMBERGRIS, CASTOREUM, CIVET AND MUSK; - CANTHARIDES; BILE, WHETHER OR NOT DRIED; -\nGLANDS AND OTHER ANIMAL PRODUCTS USED - IN THE PREPARATION OF PHARMACEUTICAL -\nPRODUCTS, FRESH, CHILLED, FROZEN OR - OTHERWISE PROVISIONALLY PRESERVED -\nAMBERGRIS, CASTOREUM, CIVET AND MUSK; - CANTHARIDES; BILE, WHETHER OR NOT DRIED; -\nGLANDS AND OTHER ANIMAL PRODUCTS USED - IN THE PREPARATION OF PHARMACEUTICAL -\nPRODUCTS, FRESH, CHILLED, FROZEN OR - OTHERWISE PROVISIONALLY PRESERVED : - BEZOAR,\nCOW (GOOLOCHAN)",
+        "hsn_code": "5100010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "NATURAL SPONGES OF ANIMAL ORIGIN - NATURAL SPONGES OF ANIMAL ORIGIN : - OTHER",
+        "hsn_code": "5090090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "NATURAL SPONGES OF ANIMAL ORIGIN - NATURAL SPONGES OF ANIMAL ORIGIN : - OF WILD\nLIFE",
+        "hsn_code": "5090010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CORAL AND SIMILAR MATERIALS, UNWORKED - OR SIMPLY PREPARED BUT NOT OTHERWISE -\nWORKED; SHELLS OF MOLLUSCS, CRUSTACEANS - OR ECHINODERMS AND CUTTLE-BONE, -\nUNWORKED OR SIMPLY PREPARED BUT NOT - CUT TO SHAPE, POWDER AND WASTE THEREOF -\nCORAL AND SIMILAR MATERIALS, UNWORKED OR - SIMPLY PREPARED BUT NOT OTHERWISE\nWORKED; - SHELLS OF MOLLUSCS, CRUSTACEANS OR - ECHINODERMS AND CUTTLE-BONE,\nUNWORKED - OR SIMPLY PREPARED BUT NOT CUT TO SHAPE, - POWDER AND WASTE THEREOF : -\nOTHER",
+        "hsn_code": "5080090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CORAL AND SIMILAR MATERIALS, UNWORKED - OR SIMPLY PREPARED BUT NOT OTHERWISE -\nWORKED; SHELLS OF MOLLUSCS, CRUSTACEANS - OR ECHINODERMS AND CUTTLE-BONE, -\nUNWORKED OR SIMPLY PREPARED BUT NOT - CUT TO SHAPE, POWDER AND WASTE THEREOF -\nCORAL AND SIMILAR MATERIALS, UNWORKED OR - SIMPLY PREPARED BUT NOT OTHERWISE\nWORKED; - SHELLS OF MOLLUSCS, CRUSTACEANS OR - ECHINODERMS AND CUTTLE-BONE,\nUNWORKED - OR SIMPLY PREPARED BUT NOT CUT TO SHAPE, - POWDER AND WASTE THEREOF : -\nSHELLS",
+        "hsn_code": "5080050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CORAL AND SIMILAR MATERIALS, UNWORKED - OR SIMPLY PREPARED BUT NOT OTHERWISE -\nWORKED; SHELLS OF MOLLUSCS, CRUSTACEANS - OR ECHINODERMS AND CUTTLE-BONE, -\nUNWORKED OR SIMPLY PREPARED BUT NOT - CUT TO SHAPE, POWDER AND WASTE THEREOF -\nCORAL AND SIMILAR MATERIALS, UNWORKED OR - SIMPLY PREPARED BUT NOT OTHERWISE\nWORKED; - SHELLS OF MOLLUSCS, CRUSTACEANS OR - ECHINODERMS AND CUTTLE-BONE,\nUNWORKED - OR SIMPLY PREPARED BUT NOT CUT TO SHAPE, - POWDER AND WASTE THEREOF : -\nCUTTLEFISH BONES",
+        "hsn_code": "5080040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CORAL AND SIMILAR MATERIALS, UNWORKED - OR SIMPLY PREPARED BUT NOT OTHERWISE -\nWORKED; SHELLS OF MOLLUSCS, CRUSTACEANS - OR ECHINODERMS AND CUTTLE-BONE, -\nUNWORKED OR SIMPLY PREPARED BUT NOT - CUT TO SHAPE, POWDER AND WASTE THEREOF -\nCORAL AND SIMILAR MATERIALS, UNWORKED OR - SIMPLY PREPARED BUT NOT OTHERWISE\nWORKED; - SHELLS OF MOLLUSCS, CRUSTACEANS OR - ECHINODERMS AND CUTTLE-BONE,\nUNWORKED - OR SIMPLY PREPARED BUT NOT CUT TO SHAPE, - POWDER AND WASTE THEREOF : -\nCOWRIES",
+        "hsn_code": "5080030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CORAL AND SIMILAR MATERIALS, UNWORKED - OR SIMPLY PREPARED BUT NOT OTHERWISE -\nWORKED; SHELLS OF MOLLUSCS, CRUSTACEANS - OR ECHINODERMS AND CUTTLE-BONE, -\nUNWORKED OR SIMPLY PREPARED BUT NOT - CUT TO SHAPE, POWDER AND WASTE THEREOF -\nCORAL AND SIMILAR MATERIALS, UNWORKED OR - SIMPLY PREPARED BUT NOT OTHERWISE\nWORKED; - SHELLS OF MOLLUSCS, CRUSTACEANS OR - ECHINODERMS AND CUTTLE-BONE,\nUNWORKED - OR SIMPLY PREPARED BUT NOT CUT TO SHAPE, - POWDER AND WASTE THEREOF : -\nCHANKS",
+        "hsn_code": "5080020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CORAL AND SIMILAR MATERIALS, UNWORKED - OR SIMPLY PREPARED BUT NOT OTHERWISE -\nWORKED; SHELLS OF MOLLUSCS, CRUSTACEANS - OR ECHINODERMS AND CUTTLE-BONE, -\nUNWORKED OR SIMPLY PREPARED BUT NOT - CUT TO SHAPE, POWDER AND WASTE THEREOF -\nCORAL AND SIMILAR MATERIALS, UNWORKED OR - SIMPLY PREPARED BUT NOT OTHERWISE\nWORKED; - SHELLS OF MOLLUSCS, CRUSTACEANS OR - ECHINODERMS AND CUTTLE-BONE,\nUNWORKED - OR SIMPLY PREPARED BUT NOT CUT TO SHAPE, - POWDER AND WASTE THEREOF : -\nCORAL",
+        "hsn_code": "5080010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IVORY, TORTOISE-SHELL, WHALEBONE AND - WHALEBONE HAIR, HORNS, ANTLERS, HOOVES, -\nNAILS, CLAWS AND BEAKS, UNWORKED OR SIMPLY - PREPARED BUT NOT CUT TO SHAPE;\nPOWDER AND - WASTE OF THESE PRODUCTS - OTHER : OTHER",
+        "hsn_code": "5079090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IVORY, TORTOISE-SHELL, WHALEBONE AND - WHALEBONE HAIR, HORNS, ANTLERS, HOOVES, -\nNAILS, CLAWS AND BEAKS, UNWORKED OR SIMPLY - PREPARED BUT NOT CUT TO SHAPE;\nPOWDER AND - WASTE OF THESE PRODUCTS - OTHER : CLAWS AND WASTE OF TORTOISE SHELL",
+        "hsn_code": "5079070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IVORY, TORTOISE-SHELL, WHALEBONE AND - WHALEBONE HAIR, HORNS, ANTLERS, HOOVES, -\nNAILS, CLAWS AND BEAKS, UNWORKED OR SIMPLY - PREPARED BUT NOT CUT TO SHAPE;\nPOWDER AND - WASTE OF THESE PRODUCTS - OTHER : TORTOISE- SHELL",
+        "hsn_code": "5079060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IVORY, TORTOISE-SHELL, WHALEBONE AND - WHALEBONE HAIR, HORNS, ANTLERS, HOOVES, -\nNAILS, CLAWS AND BEAKS, UNWORKED OR SIMPLY - PREPARED BUT NOT CUT TO SHAPE;\nPOWDER AND - WASTE OF THESE PRODUCTS - OTHER : BUFFALO HORNS",
+        "hsn_code": "5079050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IVORY, TORTOISE-SHELL, WHALEBONE AND - WHALEBONE HAIR, HORNS, ANTLERS, HOOVES, -\nNAILS, CLAWS AND BEAKS, UNWORKED OR SIMPLY - PREPARED BUT NOT CUT TO SHAPE;\nPOWDER AND - WASTE OF THESE PRODUCTS - OTHER : ANTLERS",
+        "hsn_code": "5079040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IVORY, TORTOISE-SHELL, WHALEBONE AND - WHALEBONE HAIR, HORNS, ANTLERS, HOOVES, -\nNAILS, CLAWS AND BEAKS, UNWORKED OR SIMPLY - PREPARED BUT NOT CUT TO SHAPE;\nPOWDER AND - WASTE OF THESE PRODUCTS - OTHER : HOOVES, CLAWS, NAILS AND BEAKS",
+        "hsn_code": "5079030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IVORY, TORTOISE-SHELL, WHALEBONE AND - WHALEBONE HAIR, HORNS, ANTLERS, HOOVES, -\nNAILS, CLAWS AND BEAKS, UNWORKED OR SIMPLY - PREPARED BUT NOT CUT TO SHAPE;\nPOWDER AND - WASTE OF THESE PRODUCTS - OTHER : HORN MEAL",
+        "hsn_code": "5079020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IVORY, TORTOISE-SHELL, WHALEBONE AND - WHALEBONE HAIR, HORNS, ANTLERS, HOOVES, -\nNAILS, CLAWS AND BEAKS, UNWORKED OR SIMPLY - PREPARED BUT NOT CUT TO SHAPE;\nPOWDER AND - WASTE OF THESE PRODUCTS - OTHER : HOOF MEAL",
+        "hsn_code": "5079010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IVORY, TORTOISE-SHELL, WHALEBONE AND - WHALEBONE HAIR, HORNS, ANTLERS, HOOVES, -\nNAILS, CLAWS AND BEAKS, UNWORKED OR SIMPLY - PREPARED BUT NOT CUT TO SHAPE;\nPOWDER AND - WASTE OF THESE PRODUCTS - IVORY; IVORY POWDER AND WASTE : - IVORY\nPOWDER AND WASTE",
+        "hsn_code": "5071020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IVORY, TORTOISE-SHELL, WHALEBONE AND - WHALEBONE HAIR, HORNS, ANTLERS, HOOVES, -\nNAILS, CLAWS AND BEAKS, UNWORKED OR SIMPLY - PREPARED BUT NOT CUT TO SHAPE;\nPOWDER AND - WASTE OF THESE PRODUCTS - IVORY; IVORY POWDER AND WASTE : - IVORY",
+        "hsn_code": "5071010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BONES AND HORN-CORES, UNWORKED, - DEFATTED, SIMPLY PREPARED (BUT NOT CUT TO -\nSHAPE), TREATED WITH ACID OR DEGELATINISED - POWDER AND WASTE OF THESE PRODUCTS -\nOTHER : OTHER : OTHER",
+        "hsn_code": "5069099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BONES AND HORN-CORES, UNWORKED, - DEFATTED, SIMPLY PREPARED (BUT NOT CUT TO -\nSHAPE), TREATED WITH ACID OR DEGELATINISED - POWDER AND WASTE OF THESE PRODUCTS -\nOTHER : OTHER : OF WILD ANIMALS",
+        "hsn_code": "5069091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BONES AND HORN-CORES, UNWORKED, - DEFATTED, SIMPLY PREPARED (BUT NOT CUT TO -\nSHAPE), TREATED WITH ACID OR DEGELATINISED - POWDER AND WASTE OF THESE PRODUCTS -\nOTHER - BONE MEAL : - : OTHER",
+        "hsn_code": "5069019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BONES AND HORN-CORES, UNWORKED, - DEFATTED, SIMPLY PREPARED (BUT NOT CUT TO -\nSHAPE), TREATED WITH ACID OR DEGELATINISED - POWDER AND WASTE OF THESE PRODUCTS -\nOTHER : BONE MEAL : - OF WILD ANIMALS",
+        "hsn_code": "5069011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BONES AND HORN-CORES, UNWORKED, - DEFATTED, SIMPLY PREPARED (BUT NOT CUT TO -\nSHAPE), TREATED WITH ACID OR DEGELATINISED - POWDER AND WASTE OF THESE PRODUCTS -\nOSSEIN AND BONES TREATED WITH ACID : - BONES, HORN-CONES AND PARTS THEREOF, NOT -\nCRUSHED : - OTHER",
+        "hsn_code": "5061049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BONES AND HORN-CORES, UNWORKED, - DEFATTED, SIMPLY PREPARED (BUT NOT CUT TO -\nSHAPE), TREATED WITH ACID OR DEGELATINISED - POWDER AND WASTE OF THESE PRODUCTS -\nOSSEIN AND BONES TREATED WITH ACID : - BONES, HORN-CONES AND PARTS THEREOF, NOT -\nCRUSHED : - OF WILD ANIMALS",
+        "hsn_code": "5061041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BONES AND HORN-CORES, UNWORKED, - DEFATTED, SIMPLY PREPARED (BUT NOT CUT TO -\nSHAPE), TREATED WITH ACID OR DEGELATINISED - POWDER AND WASTE OF THESE PRODUCTS -\nOSSEIN AND BONES TREATED WITH ACID : - OSSEIN - OTHER",
+        "hsn_code": "5061039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BONES AND HORN-CORES, UNWORKED, - DEFATTED, SIMPLY PREPARED (BUT NOT CUT TO -\nSHAPE), TREATED WITH ACID OR DEGELATINISED - POWDER AND WASTE OF THESE PRODUCTS -\nOSSEIN AND BONES TREATED WITH ACID : - OSSEIN - OF WILD ANIMALS",
+        "hsn_code": "5061031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BONES AND HORN-CORES, UNWORKED, - DEFATTED, SIMPLY PREPARED (BUT NOT CUT TO -\nSHAPE), TREATED WITH ACID OR DEGELATINISED - POWDER AND WASTE OF THESE PRODUCTS -\nOSSEIN AND BONES TREATED WITH ACID : - BONE GRIST : OTHER",
+        "hsn_code": "5061029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BONES AND HORN-CORES, UNWORKED, - DEFATTED, SIMPLY PREPARED (BUT NOT CUT TO -\nSHAPE), TREATED WITH ACID OR DEGELATINISED - POWDER AND WASTE OF THESE PRODUCTS -\nOSSEIN AND BONES TREATED WITH ACID : - BONE GRIST : OF WILD ANIMALS",
+        "hsn_code": "5061021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BONES AND HORN-CORES, UNWORKED, - DEFATTED, SIMPLY PREPARED (BUT NOT CUT TO -\nSHAPE), TREATED WITH ACID OR DEGELATINISED - POWDER AND WASTE OF THESE PRODUCTS -\nOSSEIN AND BONES TREATED WITH ACID : - - BONES, INCLUDING HORN-CORES, CRUSHED : -\nOTHER",
+        "hsn_code": "5061019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BONES AND HORN-CORES, UNWORKED, - DEFATTED, SIMPLY PREPARED (BUT NOT CUT TO -\nSHAPE), TREATED WITH ACID OR DEGELATINISED - POWDER AND WASTE OF THESE PRODUCTS -\nOSSEIN AND BONES TREATED WITH ACID : BONES, INCLUDING HORN-CORES, CRUSHED : - - OF\nWILD ANIMALS",
+        "hsn_code": "5061011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SKINS AND OTHER PARTS OF BIRDS, WITH THEIR - FEATHERS OR DOWN, FEATHERS AND PARTS\nOF - FEATHERS (WHETHER OR NOT WITH TRIMMED - EDGES) AND DOWN, NOT FURTHER\nWORKED THAN - CLEANED, DISINFECTED OR TREATED FOR - PRESERVATION; POWDER AND\nWASTE OF - FEATHERS OR PARTS OF FEATHERS - OTHER : - SKINS AND OTHER PARTS : OTHER",
+        "hsn_code": "5059099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SKINS AND OTHER PARTS OF BIRDS, WITH THEIR - FEATHERS OR DOWN, FEATHERS AND PARTS\nOF - FEATHERS (WHETHER OR NOT WITH TRIMMED - EDGES) AND DOWN, NOT FURTHER\nWORKED THAN - CLEANED, DISINFECTED OR TREATED FOR - PRESERVATION; POWDER AND\nWASTE OF - FEATHERS OR PARTS OF FEATHERS - OTHER : - SKINS AND OTHER PARTS : OF WILD\nBIRDS",
+        "hsn_code": "5059091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SKINS AND OTHER PARTS OF BIRDS, WITH THEIR - FEATHERS OR DOWN, FEATHERS AND PARTS\nOF - FEATHERS (WHETHER OR NOT WITH TRIMMED - EDGES) AND DOWN, NOT FURTHER\nWORKED THAN - CLEANED, DISINFECTED OR TREATED FOR - PRESERVATION; POWDER AND\nWASTE OF - FEATHERS OR PARTS OF FEATHERS - OTHER : - POWDER AND WASTE OF FEATHERS\nOR PARTS OF - FEATHERS : - OTHER",
+        "hsn_code": "5059039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SKINS AND OTHER PARTS OF BIRDS, WITH THEIR - FEATHERS OR DOWN, FEATHERS AND PARTS\nOF - FEATHERS (WHETHER OR NOT WITH TRIMMED - EDGES) AND DOWN, NOT FURTHER\nWORKED THAN - CLEANED, DISINFECTED OR TREATED FOR - PRESERVATION; POWDER AND\nWASTE OF - FEATHERS OR PARTS OF FEATHERS - OTHER : - POWDER AND WASTE OF FEATHERS\nOR PARTS OF FEATHERS : - OF WILD BIRDS",
+        "hsn_code": "5059031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SKINS AND OTHER PARTS OF BIRDS, WITH THEIR - FEATHERS OR DOWN, FEATHERS AND PARTS\nOF - FEATHERS (WHETHER OR NOT WITH TRIMMED - EDGES) AND DOWN, NOT FURTHER\nWORKED THAN - CLEANED, DISINFECTED OR TREATED FOR - PRESERVATION; POWDER AND\nWASTE OF - FEATHERS OR PARTS OF FEATHERS - OTHER : - OTHER FEATHER (EXCLUDING FOR\nSTUFFING - PURPOSE) : - OTHER",
+        "hsn_code": "5059029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SKINS AND OTHER PARTS OF BIRDS, WITH THEIR - FEATHERS OR DOWN, FEATHERS AND PARTS\nOF - FEATHERS (WHETHER OR NOT WITH TRIMMED - EDGES) AND DOWN, NOT FURTHER\nWORKED THAN - CLEANED, DISINFECTED OR TREATED FOR - PRESERVATION; POWDER AND\nWASTE OF - FEATHERS OR PARTS OF FEATHERS - OTHER : - OTHER FEATHER (EXCLUDING FOR\nSTUFFING - PURPOSE) : - OF WILD BIRDS",
+        "hsn_code": "5059021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SKINS AND OTHER PARTS OF BIRDS, WITH THEIR - FEATHERS OR DOWN, FEATHERS AND PARTS\nOF - FEATHERS (WHETHER OR NOT WITH TRIMMED - EDGES) AND DOWN, NOT FURTHER\nWORKED THAN - CLEANED, DISINFECTED OR TREATED FOR - PRESERVATION; POWDER AND\nWASTE OF - FEATHERS OR PARTS OF FEATHERS - OTHER : - PEACOCK TAIL AND WING FEATHER\n(TRIMMED OR NOT)",
+        "hsn_code": "5059010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SKINS AND OTHER PARTS OF BIRDS, WITH THEIR - FEATHERS OR DOWN, FEATHERS AND PARTS\nOF - FEATHERS (WHETHER OR NOT WITH TRIMMED - EDGES) AND DOWN, NOT FURTHER\nWORKED THAN - CLEANED, DISINFECTED OR TREATED FOR - PRESERVATION; POWDER AND\nWASTE OF - FEATHERS OR PARTS OF FEATHERS - FEATHERS OF A KIND USED FOR STUFFING;\nDOWN : - OTHER",
+        "hsn_code": "5051090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SKINS AND OTHER PARTS OF BIRDS, WITH THEIR - FEATHERS OR DOWN, FEATHERS AND PARTS\nOF - FEATHERS (WHETHER OR NOT WITH TRIMMED - EDGES) AND DOWN, NOT FURTHER\nWORKED THAN - CLEANED, DISINFECTED OR TREATED FOR - PRESERVATION; POWDER AND\nWASTE OF - FEATHERS OR PARTS OF FEATHERS - FEATHERS OF A KIND USED FOR STUFFING;\nDOWN : - OF WILD BIRDS",
+        "hsn_code": "5051010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GUTS, BLADDERS AND STOMACHS OF ANIMALS - (OTHER THAN FISH), WHOLE AND PIECES -\nTHEREOF, FRESH, CHILLED, FROZEN, SALTED, - IN BRINE, DRIED OR SMOKED - GUTS, BLADDERS\nAND STOMACHS OF ANIMALS - (OTHER THAN FISH), WHOLE AND PIECES - THEREOF, FRESH,\nCHILLED, FROZEN, SALTED, - IN BRINE, DRIED OR SMOKED : BLADDERS AND STOMACHS : - OTHER",
+        "hsn_code": "5040059",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GUTS, BLADDERS AND STOMACHS OF ANIMALS - (OTHER THAN FISH), WHOLE AND PIECES -\nTHEREOF, FRESH, CHILLED, FROZEN, SALTED, - IN BRINE, DRIED OR SMOKED - GUTS, BLADDERS\nAND STOMACHS OF ANIMALS - (OTHER THAN FISH), WHOLE AND PIECES - THEREOF, FRESH,\nCHILLED, FROZEN, SALTED, - IN BRINE, DRIED OR SMOKED : BLADDERS AND STOMACHS : - OF\nWILD ANIMALS",
+        "hsn_code": "5040051",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GUTS, BLADDERS AND STOMACHS OF ANIMALS - (OTHER THAN FISH), WHOLE AND PIECES -\nTHEREOF, FRESH, CHILLED, FROZEN, SALTED, - IN BRINE, DRIED OR SMOKED - GUTS, BLADDERS\nAND STOMACHS OF ANIMALS - (OTHER THAN FISH), WHOLE AND PIECES - THEREOF, FRESH,\nCHILLED, FROZEN, SALTED, - IN BRINE, DRIED OR SMOKED : GUTS OTHER THAN FOR NATURAL\nFOOD CASTINGS : - OTHER",
+        "hsn_code": "5040049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GUTS, BLADDERS AND STOMACHS OF ANIMALS - (OTHER THAN FISH), WHOLE AND PIECES -\nTHEREOF, FRESH, CHILLED, FROZEN, SALTED, - IN BRINE, DRIED OR SMOKED - GUTS, BLADDERS\nAND STOMACHS OF ANIMALS - (OTHER THAN FISH), WHOLE AND PIECES - THEREOF, FRESH,\nCHILLED, FROZEN, SALTED, - IN BRINE, DRIED OR SMOKED : - GUTS OTHER THAN FOR NATURAL\nFOOD CASTINGS : - OF WILD ANIMALS",
+        "hsn_code": "5040041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GUTS, BLADDERS AND STOMACHS OF ANIMALS - (OTHER THAN FISH), WHOLE AND PIECES -\nTHEREOF, FRESH, CHILLED, FROZEN, SALTED, - IN BRINE, DRIED OR SMOKED - GUTS, BLADDERS\nAND STOMACHS OF ANIMALS - (OTHER THAN FISH), WHOLE AND PIECES - THEREOF, FRESH,\nCHILLED, FROZEN, SALTED, - IN BRINE, DRIED OR SMOKED : GUTS OF OTHER ANIMALS FOR\nNATURAL - FOOD CASINGS : - OTHER",
+        "hsn_code": "5040039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GUTS, BLADDERS AND STOMACHS OF ANIMALS - (OTHER THAN FISH), WHOLE AND PIECES -\nTHEREOF, FRESH, CHILLED, FROZEN, SALTED, - IN BRINE, DRIED OR SMOKED - GUTS, BLADDERS\nAND STOMACHS OF ANIMALS - (OTHER THAN FISH), WHOLE AND PIECES - THEREOF, FRESH,\nCHILLED, FROZEN, SALTED, - IN BRINE, DRIED OR SMOKED : GUTS OF OTHER ANIMALS FOR\nNATURAL - FOOD CASINGS : - OF WILD ANIMALS",
+        "hsn_code": "5040031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GUTS, BLADDERS AND STOMACHS OF ANIMALS - (OTHER THAN FISH), WHOLE AND PIECES -\nTHEREOF, FRESH, CHILLED, FROZEN, SALTED, - IN BRINE, DRIED OR SMOKED - GUTS, BLADDERS\nAND STOMACHS OF ANIMALS - (OTHER THAN FISH), WHOLE AND PIECES - THEREOF, FRESH,\nCHILLED, FROZEN, SALTED, - IN BRINE, DRIED OR SMOKED : - GUTS OF SHEEP AND GOATS FOR\nNATURAL FOOD CASINGS",
+        "hsn_code": "5040020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GUTS, BLADDERS AND STOMACHS OF ANIMALS - (OTHER THAN FISH), WHOLE AND PIECES -\nTHEREOF, FRESH, CHILLED, FROZEN, SALTED, - IN BRINE, DRIED OR SMOKED - GUTS, BLADDERS\nAND STOMACHS OF ANIMALS - (OTHER THAN FISH), WHOLE AND PIECES - THEREOF, FRESH,\nCHILLED, FROZEN, SALTED, - IN BRINE, DRIED OR SMOKED : - GUTS OF CATTLE FOR NATURAL\nFOOD CASINGS",
+        "hsn_code": "5040010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HORSEHAIR AND HORSEHAIR WASTE, WHETHER OR NOT PUT UP AS A LAYER WITH OR WITHOUT\nSUPPORTING MATERIAL",
+        "hsn_code": "5030000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PIGS, HOGS OR BOARS BRISTLES AND HAIR; - BADGER HAIR AND OTHER BRUSH MAKING - HAIR;\nWASTE OF SUCH BRISTLES OR HAIR - OTHER : - OTHER",
+        "hsn_code": "5029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PIGS, HOGS OR BOARS BRISTLES AND HAIR; - BADGER HAIR AND OTHER BRUSH MAKING - HAIR;\nWASTE OF SUCH BRISTLES OR HAIR - OTHER : - YAK TAIL HAIR",
+        "hsn_code": "5029020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PIGS, HOGS OR BOARS BRISTLES AND HAIR; - BADGER HAIR AND OTHER BRUSH MAKING - HAIR;\nWASTE OF SUCH BRISTLES OR HAIR - OTHER : - BADGER HAIR AND OTHER BRUSH MAKING HAIR",
+        "hsn_code": "5029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PIGS, HOGS OR BOARS BRISTLES AND HAIR;BADGER HAIR ANDOTHER BRUSH MAKING - HAIR;\nWASTE OF SUCH BRISTLES OR HAIR - PIGS, HOGS OR BOARS BRISTLES AND HAIR AND WASTE\nTHEREOF : - WASTE OF PIGS, HOGS OR BOARS BRISTLES AND HAIR",
+        "hsn_code": "5021020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PIGS, HOGSOR BOARSBRISTLES AND HAIR;BADGER HAIR AND OTHER BRUSH MAKINGHAIR;\nWASTE OF SUCH BRISTLES OR HAIR - PIGS, HOGS OR BOARS BRISTLES AND HAIR AND - WASTE\nTHEREOF : - PIGS, HOGS OR BOARS BRISTLES AND HAIR",
+        "hsn_code": "5021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HUMAN HAIR, UNWORKED, WHETHER OR - NOT WASHED OR SCOURED; WASTE OF - HUMAN\nHAIR - HUMAN HAIR, UNWORKED, WHETHER OR NOT WASHED OR SCOURED; WASTE OF\nHUMAN HAIR : - WASTE OF HUMAN HAIR",
+        "hsn_code": "5010020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HUMAN HAIR, UNWORKED, WHETHER OR - NOT WASHED OR SCOURED; WASTE OF - HUMAN\nHAIR - HUMAN HAIR, UNWORKED, WHETHER OR NOT WASHED OR SCOURED; WASTE OF\nHUMAN HAIR : - HUMAN HAIR, UNWORKED,WHETHER OR NOT WASHED OR SCOURED",
+        "hsn_code": "5010010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "EDIBLE PRODUCTS OF ANIMAL ORIGIN, NOT ELSEWHERE SPECIFIED OR INCLUDED EDIBLE\nPRODUCTS OF ANIMAL ORIGIN, NOT ELSEWHERE SPECIFIED OR INCLUDED : OTHER",
+        "hsn_code": "4100090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "EDIBLE PRODUCTS OF ANIMAL ORIGIN, NOT ELSEWHERE SPECIFIED OR INCLUDED EDIBLE\nPRODUCTS OF ANIMAL ORIGIN, NOT ELSEWHERE SPECIFIED OR INCLUDED : OF WILD ANIMALS\nTURTLE EGGS AND SALANGANES NESTS (BIRDS NESTS)",
+        "hsn_code": "4100020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "EDIBLE PRODUCTS OF ANIMAL ORIGIN, NOT ELSEWHERE SPECIFIED OR INCLUDED EDIBLE\nPRODUCTS OF ANIMAL ORIGIN, NOT ELSEWHERE SPECIFIED OR INCLUDED : OF WILD ANIMALS",
+        "hsn_code": "4100010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "NATURAL HONEY",
+        "hsn_code": "4090000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BIRDS EGGS, NOT IN SHELL, AND EGG YOLKS, FRESH, DRIED, COOKED BY STEAMING OR BY\nBOILING IN WATER, MOULDED, FROZEN OR OTHERWISE PRESERVED, WHETHER OR NOT\nCONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER OTHER : OTHER",
+        "hsn_code": "4089900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BIRDS EGGS, NOT IN SHELL, AND EGG YOLKS, FRESH, DRIED, COOKED BY STEAMING OR BY\nBOILING IN WATER, MOULDED, FROZEN OR OTHERWISE PRESERVED, WHETHER OR NOT\nCONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER OTHER : DRIED",
+        "hsn_code": "4089100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BIRDS EGGS, NOT IN SHELL, AND EGG YOLKS, FRESH, DRIED, COOKED BY STEAMING OR BY\nBOILING IN WATER, MOULDED, FROZEN OR OTHERWISE PRESERVED, WHETHER OR NOT\nCONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER EGG YOLKS : OTHER",
+        "hsn_code": "4081900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BIRDS EGGS, NOT IN SHELL, AND EGG YOLKS, FRESH, DRIED, COOKED BY STEAMING OR BY\nBOILING IN WATER, MOULDED, FROZEN OR OTHERWISE PRESERVED, WHETHER OR NOT\nCONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER EGG YOLKS : DRIED",
+        "hsn_code": "4081100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CHEESE AND CURD OTHER CHEESE",
+        "hsn_code": "4069000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CHEESE AND CURD BLUE-VEINED CHEESE AND OTHER CHEESE CONTAINING VEINS PRODUCED BY\nPENICILLIUM ROQUEFORTI",
+        "hsn_code": "4064000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CHEESE AND CURD PROCESSED CHEESE NOT GRATED OR POWDERED",
+        "hsn_code": "4063000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CHEESE AND CURD GRATED OR POWDERED CHEESE, OF ALL KINDS",
+        "hsn_code": "4062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CHEESE AND CURD FRESH (UNRIPENED OR UNCURED) CHEESE, INCLUDING WHEY CHEESE AND\nCURD",
+        "hsn_code": "4061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUTTER AND OTHER FATS AND OILS DERIVED FROM MILK; DAIRY SPREADS OTHER OTHER",
+        "hsn_code": "4059090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUTTER AND OTHER FATS AND OILS DERIVED FROM MILK; DAIRY SPREADS OTHER GHEE",
+        "hsn_code": "4059020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUTTER AND OTHER FATS AND OILS DERIVED FROM MILK; DAIRY SPREADS OTHER : BUTTER OIL",
+        "hsn_code": "4059010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUTTER AND OTHER FATS AND OILS DERIVED FROM MILK; DAIRY SPREADS DAIRY SPREADS",
+        "hsn_code": "4052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUTTER AND OTHER FATS AND OILS DERIVED FROM MILK; DAIRY SPREADS BUTTER",
+        "hsn_code": "4051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WHEY, WHETHER OR NOT CONCENTRATED OR CONTAINING ADDED SUGAR OR OTHER\nSWEETENING MATTER; PRODUCTS CONSISTING OF NATURAL MILK CONSTITUENTS, WHETHER\nOR NOT CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER, NOT ELSEWHERE\nSPECIFIED OR INCLUDED WHEY AND MODIFIED WHEY, WHETHER OR NOT CONCENTRATED OR\nCONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER : OTHER",
+        "hsn_code": "4049000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WHEY, WHETHER OR NOT CONCENTRATED OR CONTAINING ADDED SUGAR OR OTHER\nSWEETENING MATTER; PRODUCTS CONSISTING OF NATURAL MILK CONSTITUENTS, WHETHER\nOR NOT CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER, NOT ELSEWHERE\nSPECIFIED OR INCLUDED WHEY AND MODIFIED WHEY, WHETHER OR NOT CONCENTRATED OR\nCONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER : OTHER",
+        "hsn_code": "4041090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WHEY, WHETHER OR NOT CONCENTRATED OR CONTAINING ADDED SUGAR OR OTHER\nSWEETENING MATTER; PRODUCTS CONSISTING OF NATURAL MILK CONSTITUENTS, WHETHER\nOR NOT CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER, NOT ELSEWHERE\nSPECIFIED OR INCLUDED WHEY AND MODIFIED WHEY, WHETHER OR NOT CONCENTRATED OR\nCONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER : WHEY, DRY, BLOCKS AND\nPOWDERED",
+        "hsn_code": "4041020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WHEY, WHETHER OR NOT CONCENTRATED OR CONTAINING ADDED SUGAR OR OTHER\nSWEETENING MATTER; PRODUCTS CONSISTING OF NATURAL MILK CONSTITUENTS, WHETHER\nOR NOT CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER, NOT ELSEWHERE\nSPECIFIED OR INCLUDED WHEY AND MODIFIED WHEY, WHETHER OR NOT CONCENTRATED OR\nCONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER : WHEY, CONCENTRATED,\nEVAPORATED OR CONDENSED, LIQUID OR SEMI?SOLID",
+        "hsn_code": "4041010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUTTERMILK, CURDLED MILK AND CREAM, YOGURT, KEPHIR AND OTHER FERMENTED OR\nACIDIFIED MILK AND CREAM, WHETHER OR NOT CONCENTRATED OR CONTAINING ADDED\nSUGAR OR OTHER SWEETENING MATTER OR FLAVOURED OR CONTAINING ADDED FRUIT, NUTS\nOR COCOA OTHER OTHER",
+        "hsn_code": "4039090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUTTERMILK, CURDLED MILK AND CREAM, YOGURT, KEPHIR AND OTHER FERMENTED OR\nACIDIFIED MILK AND CREAM, WHETHER OR NOT CONCENTRATED OR CONTAINING ADDED\nSUGAR OR OTHER SWEETENING MATTER OR FLAVOURED OR CONTAINING ADDED FRUIT, NUTS\nOR COCOA OTHER BUTTER MILK",
+        "hsn_code": "4039010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUTTERMILK, CURDLED MILK AND CREAM, YOGURT, KEPHIR AND OTHER FERMENTED OR\nACIDIFIED MILK AND CREAM, WHETHER OR NOT CONCENTRATED OR CONTAINING ADDED\nSUGAR OR OTHER SWEETENING MATTER OR FLAVOURED OR CONTAINING ADDED FRUIT, NUTS\nOR COCOA YOGURT",
+        "hsn_code": "4031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MILK AND CREAM, CONCENTRATED OR CONTAINING ADDED SUGAR OR OTHER SWEETENING\nMATTER OTHER : OTHER : OTHER",
+        "hsn_code": "4029990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MILK AND CREAM, CONCENTRATED OR CONTAINING ADDED SUGAR OR OTHER SWEETENING\nMATTER OTHER : OTHER : CONDENSED MILK",
+        "hsn_code": "4029920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MILK AND CREAM, CONCENTRATED OR CONTAINING ADDED SUGAR OR OTHER SWEETENING\nMATTER OTHER : OTHER : WHOLE MILK",
+        "hsn_code": "4029910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MILK AND CREAM, CONCENTRATED OR CONTAINING ADDED SUGAR OR OTHER SWEETENING\nMATTER OTHER : NOT CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER : OTHER",
+        "hsn_code": "4029190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MILK AND CREAM, CONCENTRATED OR CONTAINING ADDED SUGAR OR OTHER SWEETENING\nMATTER OTHER : NOT CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER :\nCONDENSED MILK",
+        "hsn_code": "4029110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MILK AND CREAM, CONCENTRATED OR CONTAINING ADDED SUGAR OR OTHER SWEETENING\nMATTER OTHER",
+        "hsn_code": "4022990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MILK AND CREAM, CONCENTRATED OR CONTAINING ADDED SUGAR OR OTHER SWEETENING\nMATTER MILK FOR BABIES",
+        "hsn_code": "4022920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MILK AND CREAM, CONCENTRATED OR CONTAINING ADDED SUGAR OR OTHER SWEETENING\nMATTER WHOLE MILK",
+        "hsn_code": "4022910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MILK AND CREAM, CONCENTRATED OR CONTAINING ADDED SUGAR OR OTHER SWEETENING\nMATTER IN POWDER, GRANULES OR OTHER SOLID FORMS, OF A FAT CONTENT, BY WEIGHT\nEXCEEDING 1.5% : NOT CONTAINING ADDED SUGAR OR OTHER SWEETENING MATTER.",
+        "hsn_code": "4022100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MILK AND CREAM, CONCENTRATED OR CONTAINING ADDED SUGAR OR OTHER SWEETENING\nMATTER IN POWDER, GRANULES OR OTHER SOLID FORMS, OF A FAT CONTENT, BY WEIGHT NOT\nEXCEEDING 1.5% : OTHER",
+        "hsn_code": "4021090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MILK AND CREAM, CONCENTRATED OR CONTAINING ADDED SUGAR OR OTHER SWEETENING\nMATTER IN POWDER, GRANULES OR OTHER SOLID FORMS, OF A FAT CONTENT, BY WEIGHT NOT\nEXCEEDING 1.5% : MILK FOOD FOR BABIES",
+        "hsn_code": "4021020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MILK AND CREAM, CONCENTRATED OR CONTAINING ADDED SUGAR OR OTHER SWEETENING\nMATTER SKIMMED IN POWDER, GRANULES OR OTHER SOLID FORMS, OF A FAT CONTENT, BY\nWEIGHT NOT EXCEEDING 1.5% : SKIMMED MILK",
+        "hsn_code": "4021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Milk and cream, not concentrated nor containing added sugar or other sweetening matter - Of a\nfat content, by weight, exceeding 6% but not exceeding 10%",
+        "hsn_code": "4014000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT AND EDIBLE MEAT OFFAL, SALTED, INBRINE, DRIED OR SMOKED;EDIBLE FLOURS AND\nMEALS OF MEAT OR MEAT OFFAL-OTHER, INCLUDING EDIBLE FLOURS AND MEALS OF MEAT OR\nMEAT OFFAL -- OTHER",
+        "hsn_code": "2109900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT AND EDIBLE MEAT OFFAL, SALTED, INBRINE, DRIED OR SMOKED;EDIBLE FLOURS AND\nMEALS OF MEAT OR MEAT OFFAL-OTHER, INCLUDING EDIBLE FLOURS AND MEALS OF MEAT OR\nMEAT OFFAL : OF REPTILES(INCLUDING SNAKES AND TURTLES)",
+        "hsn_code": "2109300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT AND EDIBLEMEAT OFFAL, SALTED, INBRINE, DRIED OR SMOKED;EDIBLE FLOURS AND\nMEALS OF MEAT OR MEAT OFFAL-OTHER, INCLUDING EDIBLE FLOURS AND MEALS OF MEAT OR\nMEAT OFFAL : OF WHALES, DOLPHINS AND PORPOISES(MAMMALS OF THE ORDER CATECEA); OF\nMANATEES AND DUGONGS(MAMMALS OF THE ORDER SIRENIA)",
+        "hsn_code": "2109200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT AND EDIBLE MEAT OFFAL, SALTED, INBRINE, DRIED OR SMOKED;EDIBLE FLOURS AND\nMEALS OF MEAT OR MEAT OFFAL-OTHER, INCLUDING EDIBLE FLOURS AND MEALS OF MEAT OR\nMEAT OFFAL : OF PRIMATES",
+        "hsn_code": "2109100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT AND EDIBLEMEAT OFFAL, SALTED, INBRINE, DRIED OR SMOKED;EDIBLEFLOURS AND\nMEALS OF MEAT OR MEAT OFFALMEAT OF SWINE : OTHER",
+        "hsn_code": "2101900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT AND EDIBLEMEAT OFFAL, SALTED, INBRINE, DRIED OR SMOKED;EDIBLEFLOURS AND\nMEALS OF MEAT OR MEAT OFFALMEAT OF SWINE : HAMS, SHOULDERS AND CUTS THERE OF,\nWITH BONE IN",
+        "hsn_code": "2101100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PIG FAT, FREE OF LEAN MEAT, AND POULTRY FAT, NOT RENDERED OR OTHERWSE EXTRACTED,\nFRESH, CHILLED, FROZEN, SALTED, IN BRINE, DRIED OR SMOKED - OTHER",
+        "hsn_code": "2099000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PIG FAT, FREE OF LEAN MEAT, AND POULTRY FAT, NOT RENDERED OR OTHERWSE EXTRACTED,\nFRESH, CHILLED, FROZEN, SALTED, IN BRINE, DRIED OR SMOKED - OF PIGS",
+        "hsn_code": "2091000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MEAT AND EDIBLE MEAT OFFAL, FRESH, CHILLED OR FROZEN OTHER : OTHER",
+        "hsn_code": "2089090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MEAT AND EDIBLE MEAT OFFAL, FRESH, CHILLED OR FROZEN OTHER : OF WILD ANIMALS",
+        "hsn_code": "2089010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Of whales, dolphins and porpoises (mammalsof the order cetacea); of manatees and dugongs\n(mammals of the order sirenia)- of camels and other camelids (Camelidae)",
+        "hsn_code": "2086000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MEAT AND EDIBLE MEAT OFFAL, FRESH, CHILLED OR FROZEN OF REPTILES(INCLUDING\nSNAKES AND TURTLES)",
+        "hsn_code": "2085000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MEAT AND EDIBLE MEAT OFFAL, FRESH, CHILLED OR FROZEN OF WHALES, DOLPHINS\nAND P OR POISES(MAMMALS OF THE OR DER CATACEA); OF MANATEES AND\nDUGONGS(MAMMALS OF THE OR DER SIRENIA)",
+        "hsn_code": "2084000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MEAT AND EDIBLE MEAT OFFAL, FRESH, CHILLED OR FROZEN OF PRIMATES",
+        "hsn_code": "2083000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MEAT AND EDIBLE MEAT OFFAL, FRESH, CHILLED OR FROZEN FROGR LEGS",
+        "hsn_code": "2082000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MEAT AND EDIBLE MEAT OFFAL, FRESH, CHILLED OR FROZEN OF RABBITS OR HARES",
+        "hsn_code": "2081000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT, AND EDIBLE OFFAL, OF THE POULTRY OF HEADING 0105, FRESH, CHILLED OR FROZEN- OF\nGEESE - OF GUINEA FOWLS",
+        "hsn_code": "2076000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT, AND EDIBLE OFFAL, OF THE POULTRY OF HEADING 0105, FRESH, CHILLED OR FROZEN-OF\nGEESE -- OTHER, FROZEN",
+        "hsn_code": "2075500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT, AND EDIBLE OFFAL, OF THE POULTRY OF HEADING 0105, FRESH, CHILLED OR FROZEN-OF\nGEESE --OTHER, FRESH OR CHILLED",
+        "hsn_code": "2075400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT, AND EDIBLE OFFAL, OF THE POULTRY OF HEADING 0105, FRESH, CHILLED OR FROZEN- OF\nGEESE -- FATTY LIVERS, FRESH OR CHILLED",
+        "hsn_code": "2075300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT, AND EDIBLE OFFAL, OF THE POULTRY OF HEADING 0105, FRESH, CHILLED OR FROZEN- OF\nGEESE -- NOT CUT IN PIECES, FROZEN",
+        "hsn_code": "2075200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT, AND EDIBLE OFFAL, OF THE POULTRY OF HEADING 0105, FRESH, CHILLED OR FROZEN- OF\nGEESE -- NOT CUT IN PIECES, FRESH OR CHILLED",
+        "hsn_code": "2075100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT, AND EDIBLE OFFAL, OF THE POULTRY OF HEADING 0105, FRESH, CHILLED OR FROZEN- OF\nDUCKS --OTHER, FROZEN",
+        "hsn_code": "2074500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT, AND EDIBLE OFFAL, OF THE POULTRY OF HEADING 0105, FRESH, CHILLED OR FROZEN-OF\nDUCKS -- OTHER, FRESH OR CHILLED",
+        "hsn_code": "2074400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT, AND EDIBLE OFFAL, OF THE POULTRY OF HEADING 0105, FRESH, CHILLED OR FROZEN- OF\nDUCKS -- FATTY LIVERS, FRESH OR CHILLED",
+        "hsn_code": "2074300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT, AND EDIBLE OFFAL, OF THE POULTRY OF HEADING 0105, FRESH, CHILLED OR FROZEN- OF\nDUCKES -- NOT CUT IN PIECES, FROZEN",
+        "hsn_code": "2074200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT, AND EDIBLE OFFAL, OF THE POULTRY OF HEADING 0105, FRESH, CHILLED OR FROZEN- OF\nDUCKS --NOT CUT IN PIECES, FRESH OR CHILLED",
+        "hsn_code": "2074100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT, AND EDIBLE OFFAL, OF THEPOULTRY OF HEADING0105, FRESH, CHILLED OR FROZEN CUTS\nAND OFFAL, FRESH OR CHILLED",
+        "hsn_code": "2072600",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT, AND EDIBLE OFFAL, OF THEPOULTRY OF HEADING0105, FRESH, CHILLED OR FROZEN OF\nTURKEYS NOT CUT IN PIECES, FROZEN",
+        "hsn_code": "2072500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT, AND EDIBLE OFFAL, OF THEPOULTRY OF HEADING0105, FRESH, CHILLED OR FROZEN OF\nTURKEYS NOT CUT IN PIECES, FRESH OR CHILLED",
+        "hsn_code": "2072400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT, AND EDIBLE OFFAL, OF THEPOULTRY OF HEADING0105, FRESH, CHILLED OR FROZEN OF\nFOWLS OF THE SPECIES GALLUSDOMESTICUS : CUTS AND OFFAL, FROZEN",
+        "hsn_code": "2071400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT, AND EDIBLE OFFAL, OF THEPOULTRY OF HEADING0105, FRESH, CHILLED OR FROZEN OF\nFOWLS OF THE SPECIES GALLUSDOMESTICUS : CUTS AND OFFAL, FRESH OR CHILLED",
+        "hsn_code": "2071300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT, AND EDIBLE OFFAL, OF THEPOULTRY OF HEADING0105, FRESH, CHILLED OR FROZEN OF\nFOWLS OF THE SPECIES GALLUSDOMESTICUS : NOT CUT IN PIECES, FROZEN",
+        "hsn_code": "2071200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT, AND EDIBLE OFFAL, OF THEPOULTRY OF HEADING0105, FRESH, CHILLED OR FROZEN OF\nFOWLS OF THE SPECIES GALLUSDOMESTICUS : NOT CUT IN PIECES, FRESH OR CHILLED",
+        "hsn_code": "2071100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "EDIBLE OFFAL OF BOVINEANIMALS, SWINE, SHEEP, GOATS, HORSES, ASSES, MULES OR HINNIES,\nFRESH, CHILLED OR FROZEN OTHER, FROZEN : OTHER",
+        "hsn_code": "2069090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "EDIBLE OFFAL OF BOVINEANIMALS, SWINE, SHEEP, GOATS, HORSES, ASSES, MULES OR HINNIES,\nFRESH, CHILLED OR FROZEN OTHER, FROZEN : OF SHEEP OR GOATS",
+        "hsn_code": "2069010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "EDIBLE OFFAL OF BOVINEANIMALS, SWINE, SHEEP, GOATS, HORSES, ASSES, MULES OR HINNIES,\nFRESH, CHILLED OR FROZEN OTHER, FRESH OR CHILLED OTHER",
+        "hsn_code": "2068090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "EDIBLE OFFAL OF BOVINEANIMALS, SWINE, SHEEP, GOATS, HORSES, ASSES, MULES OR HINNIES,\nFRESH, CHILLED OR FROZEN OTHER, FRESH OR CHILLED OF SHEEP OR GOATS",
+        "hsn_code": "2068010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "EDIBLE OFFAL OF BOVINEANIMALS, SWINE, SHEEP, GOATS, HORSES, ASSES, MULES OR HINNIES,\nFRESH, CHILLED OR FROZEN OF SWINE, FROZEN : OTHER",
+        "hsn_code": "2064900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "EDIBLE OFFAL OF BOVINEANIMALS, SWINE, SHEEP, GOATS, HORSES, ASSES, MULES OR HINNIES,\nFRESH, CHILLED OR FROZEN OF SWINE, FROZEN : LIVERS",
+        "hsn_code": "2064100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "EDIBLE OFFAL OF BOVINEANIMALS, SWINE, SHEEP, GOATS, HORSES, ASSES, MULES OR HINNIES,\nFRESH, CHILLED OR FROZEN OF SWINE, FRESH OR CHILLED",
+        "hsn_code": "2063000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "EDIBLE OFFAL OF BOVINEANIMALS, SWINE, SHEEP, GOATS, HORSES, ASSES, MULES OR HINNIES,\nFRESH, CHILLED OR FROZEN OF BOVINE ANIMALS, FROZEN : OTHER",
+        "hsn_code": "2062900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "EDIBLE OFFAL OF BOVINE ANIMALS, SWINE, SHEEP, GOATS, HORSES, ASSES, MULES OR HINNIES,\nFRESH, CHI LLED OR FROZEN OF BOVINE ANIMALS, FROZEN : LIVERS",
+        "hsn_code": "2062200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "EDIBLE OFFAL OF BOVINE ANIMALS, SWINE, SHEEP, GOATS, HORSES, ASSES, MULES OR HINNIES,\nFRESH, CHILLED OR FROZEN OF BOVINE ANIMALS, FROZEN : TONGUES",
+        "hsn_code": "2062100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "EDIBLE OFFAL OF BOVINEANIMALS, SWINE, SHEEP, GOATS, HORSES, ASSES, MULES OR HINNIES,\nFRESH, CHILLED OR FROZEN OF BOVINE ANIMALS, FRESH OR CHILLED",
+        "hsn_code": "2061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF HORSES, ASSES, MULES OR HINNIES, FRESH, CHILLED OR FROZEN",
+        "hsn_code": "2050000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF SHEEP OR GOATS, FRESH, CHILLED OR FROZEN MEAT OF GOATS",
+        "hsn_code": "2045000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF SHEEP OR GOATS, FRESH, CHILLED OR FROZEN OTHER MEAT OF SHEEP, FROZEN :\nBONELESS",
+        "hsn_code": "2044300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF SHEEP OR GOATS, FRESH, CHILLED OR FROZEN OTHER MEAT OF SHEEP, FROZEN :\nOTHER CUTS WITH BONE IN",
+        "hsn_code": "2044200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF SHEEP OR GOATS, FRESH, CHILLED OR FROZEN OTHERMEAT OF SHEEP, FROZEN :\nCARCASSES AND HALF-CARCASSES",
+        "hsn_code": "2044100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF SHEEP OR GOATS, FRESH, CHILLED OR FROZEN CARCASSES AND HALFCARCASSES OF\nLAMB, FROZEN",
+        "hsn_code": "2043000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF SHEEP OR GOATS, FRESH, CHILLED OR FROZEN OTHER MEAT OF SHEEP, FRESH OR\nCHILLED : BONELESS",
+        "hsn_code": "2042300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF SHEEP OR GOATS, FRESH, CHILLED OR FROZEN OTHERMEAT OF SHEEP, FRESH OR\nCHILLED : OTHER CUTS WITH BONE IN",
+        "hsn_code": "2042200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF SHEEP OR GOATS, FRESH, CHILLED OR FROZEN OTHER MEAT OF SHEEP, FRESH OR\nCHILLED : CARCASSES AND HALF CARCASSES",
+        "hsn_code": "2042100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF SHEEP OR GOATS, FRESH, CHILLED OR FROZEN CARCASSES AND HALF CARCASSES OF\nLAMB, FRESH OR CHILLED",
+        "hsn_code": "2041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF SWINE, FRESH, CHILLED OR FROZEN FROZEN OTHER",
+        "hsn_code": "2032900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF SWINE, FRESH, CHILLED OR FROZEN FROZEN : HAMS, SHOULDERS AND CUTS THERE\nOF, WITH BONE IN",
+        "hsn_code": "2032200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF SWINE, FRESH, CHILLED OR FROZEN FROZEN : CARCASSES AND HALF-CARCASSES",
+        "hsn_code": "2032100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF SWINE, FRESH, CHILLED OR FROZEN HAMS, SHOULDERS AND CUTS THERE OF, WITH\nBONE INOTHER",
+        "hsn_code": "2031900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF SWINE, FRESH, CHILLED OR FROZEN HAMS, SHOULDERS AND CUTS THERE OF, WITH\nBONE IN",
+        "hsn_code": "2031200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF SWINE, FRESH, CHILLED OR FROZEN FRESH OR CHILLED : CARCASSES AND HALF\nCARCASSES",
+        "hsn_code": "2031100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF BOVINE ANIMALS, FROZEN BONELESS",
+        "hsn_code": "2023000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF BOVINE ANIMALS, FROZEN OTHER CUTS WITH BONE IN",
+        "hsn_code": "2022000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF BOVINE ANIMALS, FROZEN CARCASSES AND HALF-CARCASSES",
+        "hsn_code": "2021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF BOVINE ANIMALS, FRESH AND CHILLED BONELESS",
+        "hsn_code": "2013000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF BOVINE ANIMALS, FRESH AND CHILLED OTHER CUTS WITH BONE IN",
+        "hsn_code": "2012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEAT OF BOVINE ANIMALS, FRESH AND CHILLED CARCASSES AND HALF CARCASSES",
+        "hsn_code": "2011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ONIONS, SHALLOTS, GARLIC, LEEKS AND OTHERALLIACEOUS VEGETABLES, FRESH OR CHILLED\nONIONS AND SHALLOTS ONIONS",
+        "hsn_code": "7031010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ONIONS, SHALLOTS, GARLIC, LEEKS AND OTHERALLIACEOUS VEGETABLES, FRESH OR CHILLED\nONIONS AND SHALLOTS SHALLOTS",
+        "hsn_code": "7031020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ONIONS, SHALLOTS, GARLIC, LEEKS AND OTHERALLIACEOUS VEGETABLES, FRESH OR CHILLED\nGARLIC",
+        "hsn_code": "7032000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ONIONS, SHALLOTS, GARLIC, LEEKS AND OTHERALLIACEOUS VEGETABLES, FRESH OR CHILLED\nLEEKS AND OTHER ALLIACEOUS VEGETABLES",
+        "hsn_code": "7039000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CABBAGES, CAULIFLOWERS, KOHLRABI, KALEAND SIMILAR EDIBLE BRASSICAS, FRESH ORCHILLED\nCAULIFLOWERS AND HEADED BROCCOLI",
+        "hsn_code": "7041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CABBAGES, CAULIFLOWERS, KOHLRABI, KALEAND SIMILAR EDIBLE BRASSICAS, FRESH ORCHILLED\nBRUSSELS SPROUTS",
+        "hsn_code": "7042000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CABBAGES, CAULIFLOWERS, KOHLRABI, KALEAND SIMILAR EDIBLE BRASSICAS, FRESH ORCHILLED\nOTHER",
+        "hsn_code": "7049000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LETTUCE (LACTUCASATIVA) AND CHICORY(CICHORIUM SPP. ), FRESH OR CHILLED LETTUCE :\nCABBAGE LETTUCE (HEAD LETTUCE)",
+        "hsn_code": "7051100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LETTUCE (LACTUCASATIVA) AND CHICORY(CICHORIUM SPP. ), FRESH OR CHILLED LETTUCE :\nOTHER",
+        "hsn_code": "7051900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LETTUCE (LACTUCASATIVA) AND CHICORY(CICHORIUM SPP. ), FRESH OR CHILLED CHICORY\nWITLOOF CHICORY (CICHORIUM INTYBUS VAR. FOLIOSUM)",
+        "hsn_code": "7052100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LETTUCE (LACTUCASATIVA) AND CHICORY(CICHORIUM SPP. ), FRESH OR CHILLEDCHICORY OTHER",
+        "hsn_code": "7052900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CARROTS, TURNIPS, SALAD BEETROOT, SALSIFY,CELERIAC, RADISHES AND SIMILAR EDIBLEROOTS,\nFRESH OR CHILLEDCARROTS AND TURNIPS",
+        "hsn_code": "7061000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CARROTS, TURNIPS, SALAD BEETROOT, SALSIFY,CELERIAC, RADISHES AND SIMILAR EDIBLEROOTS,\nFRESH OR CHILLED OTHER : HORSE RADISH",
+        "hsn_code": "7069010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CARROTS, TURNIPS, SALAD BEETROOT, SALSIFY,CELERIAC, RADISHES AND SIMILAR EDIBLEROOTS,\nFRESH OR CHILLED OTHER OTHER RADISH",
+        "hsn_code": "7069020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CARROTS, TURNIPS, SALAD BEETROOT, SALSIFY,CELERIAC, RADISHES AND SIMILAR EDIBLEROOTS,\nFRESH OR CHILLED OTHER SALAD BEETROOT",
+        "hsn_code": "7069030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CARROTS, TURNIPS, SALAD BEETROOT, SALSIFY,CELERIAC, RADISHES AND SIMILAR EDIBLEROOTS,\nFRESH OR CHILLED OTHER OTHER",
+        "hsn_code": "7069090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CUCUMBERS OR GHERKINS, FRESH OR CHILLED",
+        "hsn_code": "7070000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LEGUMINOUS VEGETABLES, SHELLED ORUNSHELLED, FRESH OR CHILLED PEAS (PISUM SATIVUM)",
+        "hsn_code": "7081000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LEGUMINOUS VEGETABLES, SHELLED ORUNSHELLED, FRESH OR CHILLED BEANS (VIGNA SPP.,\nPHASEOLUS SPP.)",
+        "hsn_code": "7082000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LEGUMINOUS VEGETABLES, SHELLED ORUNSHELLED, FRESH OR CHILLED OTHER LEGUMINOUS\nVEGETABLES",
+        "hsn_code": "7089000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES, FRESH OR CHILLED GLOBE ARTICHOKES",
+        "hsn_code": "7091000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES, FRESH OR CHILLED ASPARAGUS",
+        "hsn_code": "7092000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES, FRESH OR CHILLED AUBERGINES (EGG PLANTS)",
+        "hsn_code": "7093000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES, FRESH OR CHILLED CELERY OTHER THAN CELERIAC",
+        "hsn_code": "7094000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES, FRESH OR CHILLED MUSHROOMS AND TRUFFLES MUSHROOMS OF THE\nGENUS AGARICUS",
+        "hsn_code": "7095100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES, FRESH OR CHILLED MUSHROOMS AND TRUFFLES TRUFFLES",
+        "hsn_code": "7095200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES, FRESH OR CHILLED MUSHROOMS AND TRUFFLES OTHER",
+        "hsn_code": "7095900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES, FRESH OR CHILLED FRUITS OF THE GENUS CAPSICUM OR OF THEGENUS\nPIMENTA : GREEN CHILLY",
+        "hsn_code": "7096010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES, FRESH OR CHILLED FRUITS OF THE GENUS CAPSICUM OR OF THEGENUS\nPIMENTA : OTHER",
+        "hsn_code": "7096090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES, FRESH OR CHILLED SPINACH, NEW ZEALAND SPINACH AND ORACHE\nSPINACH (GARDEN SPINACH)",
+        "hsn_code": "7097000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES, FRESH OR CHILLED OTHER : OTHER",
+        "hsn_code": "7099090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES,FRESH OR CHILLED - OTHER --GLOBE ARTICHOKES",
+        "hsn_code": "7099100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES,FRESH OR CHILLED - OTHER --OLIVES",
+        "hsn_code": "7099200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES,FRESH OR CHILLED - OTHER --PUMPKINS,SQUASH AND GOURDS\n(CUCURBITA SPP.)",
+        "hsn_code": "7099300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES,FRESH OR CHILLED - OTHER --OTHER---GREEN PEPPER",
+        "hsn_code": "7099910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES,FRESH OR CHILLED - OTHER --OTHER---MIXED VEGETABLES",
+        "hsn_code": "7099920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER VEGETABLES,FRESH OR CHILLED - OTHER --OTHER---OTHER",
+        "hsn_code": "7099990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES PROVISIONALLY PRESERVED(FOR EXAMPLE, BY SULPHUR DIOXIDE GAS,IN BRINE, IN\nSULPHUR WATER OR IN OTHERPRESERVATIVE SOLUTIONS), BUT UNSUITABLEIN THAT STATE FOR\nIMMEDIATE CONSUMPTION OTHER VEGETABLES; MIXTURES OF VEGETABLES : GREEN PEPPER IN\nBRINE",
+        "hsn_code": "7119010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES PROVISIONALLY PRESERVED(FOR EXAMPLE, BY SULPHUR DIOXIDE GAS,IN BRINE, IN\nSULPHUR WATER OR IN OTHERPRESERVATIVE SOLUTIONS), BUT UNSUITABLEIN THAT STATE FOR\nIMMEDIATE CONSUMPTION OTHER VEGETABLES; MIXTURES OF VEGETABLES : ASSORTED\nCANNED VEGETABLES",
+        "hsn_code": "7119020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "VEGETABLES PROVISIONALLY PRESERVED(FOR EXAMPLE, BY SULPHUR DIOXIDE GAS,IN BRINE, IN\nSULPHUR WATER OR IN OTHERPRESERVATIVE SOLUTIONS), BUT UNSUITABLEIN THAT STATE FOR\nIMMEDIATE CONSUMPTION OTHER VEGETABLES; MIXTURES OF VEGETABLES : OTHER",
+        "hsn_code": "7119090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED VEGETABLES, WHOLE, CUT, SLICED,BROKEN OR IN POWDER, BUT NOT FURTHER\nPREPARED ONIONS",
+        "hsn_code": "7122000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED VEGETABLES, WHOLE, CUT, SLICED,BROKEN OR IN POWDER, BUT NOT FURTHER\nPREPARED MUSHROOMS, WOOD EARS (AURICULARIA SPP.),JELLY FUNGI (TREMELLA SPP.) AND\nTRUFFLES : MUSHROOMS OF THE GENUS LGARICUS",
+        "hsn_code": "7123100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED VEGETABLES, WHOLE, CUT, SLICED,BROKEN OR IN POWDER, BUT NOT FURTHER\nPREPARED MUSHROOMS, WOOD EARS (AURICULARIA SPP.),JELLY FUNGI (TREMELLA SPP.) AND\nTRUFFLES : WOOD EARS (AURICULARIA SPP.)",
+        "hsn_code": "7123200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED VEGETABLES, WHOLE, CUT, SLICED,BROKEN OR IN POWDER, BUT NOT FURTHER\nPREPARED ?MUSHROOMS, WOOD EARS (AURICULARIA SPP.),JELLY FUNGI (TREMELLA SPP.) AND\nTRUFFLES : JELLY FUNGI (TREMELLA SPP.)",
+        "hsn_code": "7123300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED VEGETABLES, WHOLE, CUT, SLICED,BROKEN OR IN POWDER, BUT NOT FURTHER\nPREPARED ? MUSHROOMS, WOOD EARS (AURICULARIA SPP.),JELLY FUNGI (TREMELLA SPP.) AND\nTRUFFLES : OTHER",
+        "hsn_code": "7123900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED VEGETABLES, WHOLE, CUT, SLICED,BROKEN OR IN POWDER, BUT NOT FURTHER\nPREPARED OTHER VEGETABLES; MIXTURES OF VEGETABLES : ASPARAGUS",
+        "hsn_code": "7129010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED VEGETABLES, WHOLE, CUT, SLICED,BROKEN OR IN POWDER, BUT NOT FURTHER\nPREPARED OTHER VEGETABLES; MIXTURES OF VEGETABLES : DEHYDRATED GARLIC POWDER",
+        "hsn_code": "7129020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED VEGETABLES, WHOLE, CUT, SLICED,BROKEN OR IN POWDER, BUT NOT FURTHER\nPREPARED OTHER VEGETABLES; MIXTURES OF VEGETABLES : DEHYDRATED GARLIC FLAKES",
+        "hsn_code": "7129030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED VEGETABLES, WHOLE, CUT, SLICED,BROKEN OR IN POWDER, BUT NOT FURTHER\nPREPARED OTHER VEGETABLES; MIXTURES OF VEGETABLES : DRIED GARLIC",
+        "hsn_code": "7129040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED VEGETABLES, WHOLE, CUT, SLICED,BROKEN OR IN POWDER, BUT NOT FURTHER\nPREPARED OTHER VEGETABLES; MIXTURES OF VEGETABLES : MARJORAM, OREGANO",
+        "hsn_code": "7129050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED VEGETABLES, WHOLE, CUT, SLICED,BROKEN OR IN POWDER, BUT NOT FURTHER\nPREPARED OTHER VEGETABLES; MIXTURES OF VEGETABLES : POTATOES",
+        "hsn_code": "7129060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED VEGETABLES, WHOLE, CUT, SLICED,BROKEN OR IN POWDER, BUT NOT FURTHER\nPREPARED OTHER VEGETABLES; MIXTURES OF VEGETABLES : OTHER",
+        "hsn_code": "7129090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED LEGUMINOUS VEGETABLES, SHELLED,WHETHER OR NOT SKINNED OR SPLIT PEAS (PISUM\nSATIVUM)",
+        "hsn_code": "7131000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED LEGUMINOUS VEGETABLES, SHELLED,WHETHER OR NOT SKINNED OR SPLIT CHICKPEAS\n(GARBANZOS) BEANS (VIGNA SPP., PHASEOLUS SPP.) :",
+        "hsn_code": "7132000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED LEGUMINOUS VEGETABLES, SHELLED,WHETHER OR NOT SKINNED OR SPLIT BEANS (VIGNA\nSPP., PHASEOLUS SPP.) : BEANS OF THE SPECIES VIGNA MUNGO (L.) HEPPER OR VIGNA RADIATA\n(L.) WILCZEK",
+        "hsn_code": "7133100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED LEGUMINOUS VEGETABLES, SHELLED,WHETHER OR NOT SKINNED OR SPLIT BEANS (VIGNA\nSPP., PHASEOLUS SPP.) : SMALL RED (ADZUKI) BEANS (PHASEOLUS OR VIGNA ANGULARIS)",
+        "hsn_code": "7133200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED LEGUMINOUS VEGETABLES, SHELLED,WHETHER OR NOT SKINNED OR SPLIT BEANS (VIGNA\nSPP., PHASEOLUS SPP.) : KIDNEY BEANS, INCLUDING WHITE PEA BEANS",
+        "hsn_code": "7133300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED LEGUMINOUS VEGETABLES, SHELLED,WHETHER OR NOT SKINNED OR SPLIT - BEANS --\nBAMBARA BEANS (VIGNA SUBTERRANEA OR VOANDZEIA SUBTERRANEA)",
+        "hsn_code": "7133400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED LEGUMINOUS VEGETABLES, SHELLED,WHETHER OR NOT SKINNED OR SPLIT - BEANS --\nCOW PEAS (VIGNA UNGUICULATA)",
+        "hsn_code": "7133500",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED LEGUMINOUS VEGETABLES, SHELLED,WHETHER OR NOT SKINNED OR SPLIT BEANS (VIGNA\nSPP., PHASEOLUS SPP.) : OTHER : GUAR SEEDS",
+        "hsn_code": "7133910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED LEGUMINOUS VEGETABLES, SHELLED,WHETHER OR NOT SKINNED OR SPLIT BEANS (VIGNA\nSPP., PHASEOLUS SPP.) : OTHER : OTHER",
+        "hsn_code": "7133990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED LEGUMINOUS VEGETABLES, SHELLED,WHETHER OR NOT SKINNED OR SPLIT LENTILS",
+        "hsn_code": "7134000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED LEGUMINOUS VEGETABLES, SHELLED,WHETHER OR NOT SKINNED OR SPLIT BROAD BEANS\n(VICIA FABA VAR MAJOR) AND HORSE BEANS (VICIA FABA VAR EQUINA, VICIA FABAVAR MINOR)",
+        "hsn_code": "7135000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED LEGUMINOUS VEGETABLES, SHELLED,WHETHER OR NOT SKINNED OR SPLIT - PIGEON\nPEAS (CAJANUS CAJAN)",
+        "hsn_code": "7136000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED LEGUMINOUS VEGETABLES, SHELLED,WHETHER OR NOT SKINNED OR SPLIT -OTHER ---\nSPLIT",
+        "hsn_code": "7139010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "DRIED LEGUMINOUS VEGETABLES, SHELLED,WHETHER OR NOT SKINNED OR SPLIT - OTHER---\nOTHER",
+        "hsn_code": "7139090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MANIOC, ARROWROOT, SALEP, JERUSALEMARTICHOKES, SWEET POTATOES AND SIMILARROOTS\nAND TUBERS WITH HIGH STARCH ORINULIN CONTENT, FRESH, CHILLED, FROZEN ORDRIED,\nWHETHER OR NOT SLICED OR IN THEFORM OF PELLETS; SAGO PITH MANIOC (CASSAVA)",
+        "hsn_code": "7141000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MANIOC, ARROWROOT, SALEP, JERUSALEMARTICHOKES, SWEET POTATOES AND SIMILARROOTS\nAND TUBERS WITH HIGH STARCH ORINULIN CONTENT, FRESH, CHILLED, FROZEN ORDRIED,\nWHETHER OR NOT SLICED OR IN THEFORM OF PELLETS; SAGO PITH SWEET POTATOES",
+        "hsn_code": "7142000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MANIOC, ARROWROOT, SALEP, JERUSALEMARTICHOKES, SWEET POTATOES AND SIMILARROOTS\nAND TUBERS WITH HIGH STARCH ORINULIN CONTENT, FRESH, CHILLED, FROZEN ORDRIED,\nWHETHER OR NOT SLICED OR IN THEFORM OF PELLETS; SAGO PITH-YAMS (DIOSCOREA SPP.)",
+        "hsn_code": "7143000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MANIOC, ARROWROOT, SALEP, JERUSALEMARTICHOKES, SWEET POTATOES AND SIMILARROOTS\nAND TUBERS WITH HIGH STARCH ORINULIN CONTENT, FRESH, CHILLED, FROZEN ORDRIED,\nWHETHER OR NOT SLICED OR IN THEFORM OF PELLETS; SAGO PITH- TARO (COLOCASIA SPP.)",
+        "hsn_code": "7144000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MANIOC, ARROWROOT, SALEP, JERUSALEMARTICHOKES, SWEET POTATOES AND SIMILARROOTS\nAND TUBERS WITH HIGH STARCH ORINULIN CONTENT, FRESH, CHILLED, FROZEN ORDRIED,\nWHETHER OR NOT SLICED OR IN THEFORM OF PELLETS; SAGO PITH-YAUTIA (XANTHOSOMA SPP.)",
+        "hsn_code": "7145000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MANIOC, ARROWROOT, SALEP, JERUSALEMARTICHOKES, SWEET POTATOES AND SIMILARROOTS\nAND TUBERS WITH HIGH STARCH ORINULIN CONTENT, FRESH, CHILLED, FROZEN ORDRIED,\nWHETHER OR NOT SLICED OR IN THEFORM OF PELLETS; SAGO PITH OTHER : SAGO PITH",
+        "hsn_code": "7149010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MANIOC, ARROWROOT, SALEP, JERUSALEMARTICHOKES, SWEET POTATOES AND SIMILARROOTS\nAND TUBERS WITH HIGH STARCH ORINULIN CONTENT, FRESH, CHILLED, FROZEN ORDRIED,\nWHETHER OR NOT SLICED OR IN THEFORM OF PELLETS; SAGO PITH OTHER : OTHER",
+        "hsn_code": "7149090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GLASS INNERS FOR VACUUM FLASKS OR FOR OTHER VACUUM VESSELS",
+        "hsn_code": "70120000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GLASS BEADS, IMITATION PEARLS, IMITATION PRECIOUS OR SEMI-PRECIOUS STONES AND\nSIMILAR GLASS SMALLWARES, AND ARTICLES THEREOF OTHER THAN IMITATION JEWELLERY,\nGLASS EYES OTHER THAN PROSTHETIC ARTICLES; STATUETTES AND OTHER ORNAMENTS OF\nLAMP-WORKED GLASS, OTHER THAN IMITATION JEWELLERY; GLASS MICROSPHERES NOT\nEXCEEDING 1 MM IN DIAMETER - GLASS MICROSPHERES NOT EXCEEDING 1 MM IN DIAMETER",
+        "hsn_code": "70182000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GLASS BEADS, IMITATION PEARLS, IMITATION PRECIOUS OR SEMI-PRECIOUS STONES AND\nSIMILAR GLASS SMALLWARES, AND ARTICLES THEREOF OTHER THAN IMITATION JEWELLERY,\nGLASS EYES OTHER THAN PROSTHETIC ARTICLES; STATUETTES AND OTHER ORNAMENTS OF\nLAMP-WORKED GLASS, OTHER THAN IMITATION JEWELLERY; GLASS MICROSPHERES NOT\nEXCEEDING 1 MM IN DIAMETER - OTHER: OTHER",
+        "hsn_code": "70189090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IMITATION JEWELLERY - OF BASE METAL, WHETHER OR NOT PLATED WITH PRECIOUS METAL :\nCUFF-LINKS AND STUDS",
+        "hsn_code": "71171100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IMITATION JEWELLERY - OF BASE METAL, WHETHER OR NOT PLATED WITH PRECIOUS METAL :\nOTHER: BANGLES",
+        "hsn_code": "71171910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IMITATION JEWELLERY - OF BASE METAL, WHETHER OR NOT PLATED WITH PRECIOUS METAL :\nOTHER: GERMAN SILVER JEWELLERY",
+        "hsn_code": "71171920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IMITATION JEWELLERY - OF BASE METAL, WHETHER OR NOT PLATED WITH PRECIOUS METAL :\nOTHER: OTHER",
+        "hsn_code": "71171990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IMITATION JEWELLERY - OTHER: JEWELLERY STUDDED WITH IMITATION PEARLS OR IMITATION\nOR SYNTHETIC STONES",
+        "hsn_code": "71179010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IMITATION JEWELLERY - OTHER: OTHER",
+        "hsn_code": "71179090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IN COILS, NOT FURTHER WORKED THAN HOT-ROLLED, WITH PATTERNS IN RELIEF",
+        "hsn_code": "72081000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOTROLLED, PICKLED : OF A THICKNESS OF 4.75 MM OR MORE : PLATES",
+        "hsn_code": "72082510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOTROLLED, PICKLED : OF A THICKNESS OF 4.75 MM OR MORE : UNIVERSAL PLATES",
+        "hsn_code": "72082520",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOTROLLED, PICKLED : OF A THICKNESS OF 4.75 MM OR MORE : SHEETS",
+        "hsn_code": "72082530",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOTROLLED, PICKLED : OF A THICKNESS OF 4.75 MM OR MORE : STRIP",
+        "hsn_code": "72082540",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOTROLLED, PICKLED : OF A THICKNESS OF 4.75 MM OR MORE : OTHER",
+        "hsn_code": "72082590",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOTROLLED, PICKLED : OF A THICKNESS OF 3 MM OR MORE BUT LESS THAN 4.75 MM : PLATES",
+        "hsn_code": "72082610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOTROLLED, PICKLED : OF A THICKNESS OF 3 MM OR MORE BUT LESS THAN 4.75 MM :\nUNIVERSAL PLATES",
+        "hsn_code": "72082620",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOTROLLED, PICKLED : OF A THICKNESS OF 3 MM OR MORE BUT LESS THAN 4.75 MM : SHEETS",
+        "hsn_code": "72082630",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOTROLLED, PICKLED : OF A THICKNESS OF 3 MM OR MORE BUT LESS THAN 4.75 MM : STRIP",
+        "hsn_code": "72082640",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOTROLLED, PICKLED : OF A THICKNESS OF 3 MM OR MORE BUT LESS THAN 4.75 MM : OTHER",
+        "hsn_code": "72082690",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOTROLLED, PICKLED : OF A THICKNESS OF LESS THAN 3 MM : PLATES",
+        "hsn_code": "72082710",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOTROLLED, PICKLED : OF A THICKNESS OF LESS THAN 3 MM : UNIVERSAL PLATES",
+        "hsn_code": "72082720",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOTROLLED, PICKLED : OF A THICKNESS OF LESS THAN 3 MM : SHEETS",
+        "hsn_code": "72082730",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOTROLLED, PICKLED : OF A THICKNESS OF LESS THAN 3 MM : STRIP",
+        "hsn_code": "72082740",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOTROLLED, PICKLED : OF A THICKNESS OF LESS THAN 3 MM : OTHER",
+        "hsn_code": "72082790",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "36 FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS EXCEEDING 10 MM : PLATES",
+        "hsn_code": "72083610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "36 FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS EXCEEDING 10 MM : UNIVERSAL PLATES",
+        "hsn_code": "72083620",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "36 FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS EXCEEDING 10 MM : SHEETS",
+        "hsn_code": "72083630",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "36 FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS EXCEEDING 10 MM : STRIP",
+        "hsn_code": "72083640",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "36 FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS EXCEEDING 10 MM : OTHER",
+        "hsn_code": "72083690",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS OF 4.75 MM OR MORE BUT NOT EXCEEDING 10 MM : PLATES",
+        "hsn_code": "72083710",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS OF 4.75 MM OR MORE BUT NOT EXCEEDING 10 MM : UNIVERSAL\nPLATES",
+        "hsn_code": "72083720",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS OF 4.75 MM OR MORE BUT NOT EXCEEDING 10 MM : SHEETS",
+        "hsn_code": "72083730",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS OF 4.75 MM OR MORE BUT NOT EXCEEDING 10 MM : STRIP",
+        "hsn_code": "72083740",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS OF 4.75 MM OR MORE BUT NOT EXCEEDING 10 MM : OTHER",
+        "hsn_code": "72083790",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS OF 3 MM OR MORE BUT LESS THAN 4.75 MM : PLATES",
+        "hsn_code": "72083810",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS OF 3 MM OR MORE BUT LESS THAN 4.75 MM : UNIVERSAL PLATES",
+        "hsn_code": "72083820",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS OF 3 MM OR MORE BUT LESS THAN 4.75 MM : SHEETS",
+        "hsn_code": "72083830",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS OF 3 MM OR MORE BUT LESS THAN 4.75 MM : STRIP",
+        "hsn_code": "72083840",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS OF 3 MM OR MORE BUT LESS THAN 4.75 MM : OTHER",
+        "hsn_code": "72083890",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS OF LESS THAN 3 MM : PLATES",
+        "hsn_code": "72083910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS OF LESS THAN 3 MM : UNIVERSAL PLATES",
+        "hsn_code": "72083920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS OF LESS THAN 3 MM : SHEETS",
+        "hsn_code": "72083930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS OF LESS THAN 3 MM : STRIP",
+        "hsn_code": "72083940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, IN COILS, NOT FURTHER WORKED THAN\nHOT-ROLLED: OF A THICKNESS OF LESS THAN 3 MM : OTHER",
+        "hsn_code": "72083990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED NOT IN COILS, NOT FURTHER WORKED THAN HOT\nROLLED, WITH PATTERNS IN RELIEF : PLATES",
+        "hsn_code": "72084010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED NOT IN COILS, NOT FURTHER WORKED THAN HOT\nROLLED, WITH PATTERNS IN RELIEF : UNIVERSAL PLATES",
+        "hsn_code": "72084020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED NOT IN COILS, NOT FURTHER WORKED THAN HOT\nROLLED, WITH PATTERNS IN RELIEF : SHEETS",
+        "hsn_code": "72084030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED NOT IN COILS, NOT FURTHER WORKED THAN HOT\nROLLED, WITH PATTERNS IN RELIEF : STRIP",
+        "hsn_code": "72084040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED NOT IN COILS, NOT FURTHER WORKED THAN HOT\nROLLED, WITH PATTERNS IN RELIEF : OTHER",
+        "hsn_code": "72084090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS EXCEEDING 10 MM : PLATES",
+        "hsn_code": "72085110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS EXCEEDING 10 MM : UNIVERSAL PLATES",
+        "hsn_code": "72085120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS EXCEEDING 10 MM : SHEETS",
+        "hsn_code": "72085130",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS EXCEEDING 10 MM : STRIP",
+        "hsn_code": "72085140",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS EXCEEDING 10 MM : OTHER",
+        "hsn_code": "72085190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS OF 4.75 MM OR MORE BUT NOT EXCEEDING 10 MM :\nPLATES",
+        "hsn_code": "72085210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS OF 4.75 MM OR MORE BUT NOT EXCEEDING 10 MM :\nUNIVERSAL PLATES",
+        "hsn_code": "72085220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS OF 4.75 MM OR MORE BUT NOT EXCEEDING 10 MM :\nSHEETS",
+        "hsn_code": "72085230",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS OF 4.75 MM OR MORE BUT NOT EXCEEDING 10 MM : STRIP",
+        "hsn_code": "72085240",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS OF 4.75 MM OR MORE BUT NOT EXCEEDING 10 MM :\nOTHER",
+        "hsn_code": "72085290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS OF 3 MM OR MORE BUT LESS THAN 4.75 MM : PLATES",
+        "hsn_code": "72085310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS OF 3 MM OR MORE BUT LESS THAN 4.75 MM : UNIVERSAL\nPLATES",
+        "hsn_code": "72085320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS OF 3 MM OR MORE BUT LESS THAN 4.75 MM : SHEETS",
+        "hsn_code": "72085330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS OF 3 MM OR MORE BUT LESS THAN 4.75 MM : STRIP",
+        "hsn_code": "72085340",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS OF 3 MM OR MORE BUT LESS THAN 4.75 MM : OTHER",
+        "hsn_code": "72085390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS OF LESS THAN 3 MM : PLATES",
+        "hsn_code": "72085410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS OF LESS THAN 3 MM : UNIVERSAL PLATES",
+        "hsn_code": "72085420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS OF LESS THAN 3 MM : SHEETS",
+        "hsn_code": "72085430",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS OF LESS THAN 3 MM : STRIP",
+        "hsn_code": "72085440",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER, NOT IN COILS, NOT FURTHER WORKED\nTHAN HOTROLLED : OF A THICKNESS OF LESS THAN 3 MM : OTHER",
+        "hsn_code": "72085490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nHOT-ROLLED, NOT CLAD, PLATED OR COATED - OTHER",
+        "hsn_code": "72089000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF 3 MM OR MORE : PLATES",
+        "hsn_code": "72091510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF 3 MM OR MORE : SHEETS",
+        "hsn_code": "72091520",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF 3 MM OR MORE : STRIP",
+        "hsn_code": "72091530",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF 3 MM OR MORE : OTHER",
+        "hsn_code": "72091590",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS EXCEEDING 1 MM BUT LESS\nTHAN 3 MM : PLATES",
+        "hsn_code": "72091610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS EXCEEDING 1 MM BUT LESS\nTHAN 3 MM : SHEETS",
+        "hsn_code": "72091620",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS EXCEEDING 1 MM BUT LESS\nTHAN 3 MM : STRIP",
+        "hsn_code": "72091630",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS EXCEEDING 1 MM BUT LESS\nTHAN 3 MM : OTHER",
+        "hsn_code": "72091690",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF 0.5 MM OR MORE BUT\nNOT EXCEEDING 1 MM : PLATES",
+        "hsn_code": "72091710",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF 0.5 MM OR MORE BUT\nNOT EXCEEDING 1 MM : SHEETS",
+        "hsn_code": "72091720",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF 0.5 MM OR MORE BUT\nNOT EXCEEDING 1 MM : STRIP",
+        "hsn_code": "72091730",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF 0.5 MM OR MORE BUT\nNOT EXCEEDING 1 MM : OTHER",
+        "hsn_code": "72091790",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF LESS THAN 0.5 MM :\nPLATES",
+        "hsn_code": "72091810",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF LESS THAN 0.5 MM :\nSHEETS",
+        "hsn_code": "72091820",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF LESS THAN 0.5 MM : STRIP",
+        "hsn_code": "72091830",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF LESS THAN 0.5 MM :\nOTHER",
+        "hsn_code": "72091890",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - NOT IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF 3 MM OR MORE : PLATES",
+        "hsn_code": "72092510",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - NOT IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF 3 MM OR MORE : SHEETS",
+        "hsn_code": "72092520",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - NOT IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF 3 MM OR MORE : STRIP",
+        "hsn_code": "72092530",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - NOT IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF 3 MM OR MORE : OTHER",
+        "hsn_code": "72092590",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - NOT IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS EXCEEDING 1 MM BUT LESS\nTHAN 3 MM : PLATES",
+        "hsn_code": "72092610",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - NOT IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS EXCEEDING 1 MM BUT LESS\nTHAN 3 MM : SHEETS",
+        "hsn_code": "72092620",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - NOT IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS EXCEEDING 1 MM BUT LESS\nTHAN 3 MM : STRIP",
+        "hsn_code": "72092630",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - NOT IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS EXCEEDING 1 MM BUT LESS\nTHAN 3 MM : OTHER",
+        "hsn_code": "72092690",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - NOT IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF 0.5 MM OR MORE BUT\nNOT EXCEEDING 1 MM : PLATES",
+        "hsn_code": "72092710",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - NOT IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF 0.5 MM OR MORE BUT\nNOT EXCEEDING 1 MM : SHEETS",
+        "hsn_code": "72092720",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - NOT IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF 0.5 MM OR MORE BUT\nNOT EXCEEDING 1 MM : STRIP",
+        "hsn_code": "72092730",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - NOT IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF 0.5 MM OR MORE BUT\nNOT EXCEEDING 1 MM : OTHER",
+        "hsn_code": "72092790",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - NOT IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF LESS THAN 0.5 MM :\nPLATES",
+        "hsn_code": "72092810",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - NOT IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF LESS THAN 0.5 MM :\nSHEETS",
+        "hsn_code": "72092820",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - NOT IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF LESS THAN 0.5 MM : STRIP",
+        "hsn_code": "72092830",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - NOT IN COILS, NOT FURTHER\nWORKED THAN COLD-ROLLED (COLD-REDUCED) : OF A THICKNESS OF LESS THAN 0.5 MM :\nOTHER",
+        "hsn_code": "72092890",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCOLD-ROLLED (COLDREDUCED), NOT CLAD, PLATED OR COATED - OTHER",
+        "hsn_code": "72099000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCLAD, PLATED OR COATED - PLATED OR COATED WITH TIN : OF A THICKNESS OF 0.5 MM OR\nMORE : OTS/MR TYPE",
+        "hsn_code": "72101110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCLAD, PLATED OR COATED - PLATED OR COATED WITH TIN : OF A THICKNESS OF 0.5 MM OR\nMORE : OTHER",
+        "hsn_code": "72101190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCLAD, PLATED OR COATED - PLATED OR COATED WITH TIN : OF A THICKNESS OF LESS THAN 0.5\nMM: OTS/MR TYPE",
+        "hsn_code": "72101210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCLAD, PLATED OR COATED - PLATED OR COATED WITH TIN : OF A THICKNESS OF LESS THAN 0.5\nMM: OTHER",
+        "hsn_code": "72101290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCLAD, PLATED OR COATED - PLATED OR COATED WITH LEAD, INCLUDING TERNE-PLATE",
+        "hsn_code": "72102000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCLAD, PLATED OR COATED - ELECTROLYTICALLY PLATED OR COATED WITH ZINC :CORRUGATED",
+        "hsn_code": "72103010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCLAD, PLATED OR COATED - ELECTROLYTICALLY PLATED OR COATED WITH ZINC :OTHER",
+        "hsn_code": "72103090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCLAD, PLATED OR COATED - OTHERWISE PLATED OR COATED WITH ZINC : CORRUGATED",
+        "hsn_code": "72104100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCLAD, PLATED OR COATED - OTHERWISE PLATED OR COATED WITH ZINC : OTHER",
+        "hsn_code": "72104900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCLAD, PLATED OR COATED - PLATED OR COATED WITH CHROMIUM OXIDES OR WITH\nCHROMIUM AND CHROMIUM OXIDES",
+        "hsn_code": "72105000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCLAD, PLATED OR COATED - PLATED OR COATED WITH ALUMINIUM : PLATED OR COATED WITH\nALUMINIUM-ZINC ALLOYS",
+        "hsn_code": "72106100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCLAD, PLATED OR COATED - PLATED OR COATED WITH ALUMINIUM : OTHER",
+        "hsn_code": "72106900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCLAD, PLATED OR COATED - PAINTED, VARNISHED OR COATED WITH PLASTICS",
+        "hsn_code": "72107000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCLAD, PLATED OR COATED - OTHER : LACQUERED",
+        "hsn_code": "72109010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE,\nCLAD, PLATED OR COATED - OTHER : OTHER",
+        "hsn_code": "72109090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN HOT-ROLLED : ROLLED ON FOUR\nFACES OR IN A CLOSED BOX PASS, OF A WIDTH EXCEEDING 150 MM AND A THICKNESS OF NOT\nLESS THAN 4 MM, NOT IN COILS AND WITHOUT PATTERNS IN RELIEF",
+        "hsn_code": "72111300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN HOT-ROLLED : OTHER, OF A\nTHICKNESS OF 4.75 MM OR MORE : FLATS",
+        "hsn_code": "72111410",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN HOT-ROLLED : OTHER, OF A\nTHICKNESS OF 4.75 MM OR MORE : UNIVERSAL PLATES",
+        "hsn_code": "72111420",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN HOT-ROLLED : OTHER, OF A\nTHICKNESS OF 4.75 MM OR MORE : HOOPS",
+        "hsn_code": "72111430",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN HOT-ROLLED : OTHER, OF A\nTHICKNESS OF 4.75 MM OR MORE : SHEETS",
+        "hsn_code": "72111440",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN HOT-ROLLED : OTHER, OF A\nTHICKNESS OF 4.75 MM OR MORE : STRIP",
+        "hsn_code": "72111450",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN HOT-ROLLED : OTHER, OF A\nTHICKNESS OF 4.75 MM OR MORE : SKELP",
+        "hsn_code": "72111460",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN HOT-ROLLED : OTHER, OF A\nTHICKNESS OF 4.75 MM OR MORE : OTHER",
+        "hsn_code": "72111490",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN HOT-ROLLED : OTHER : FLATS",
+        "hsn_code": "72111910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN HOT-ROLLED : OTHER :\nUNIVERSAL PLATES",
+        "hsn_code": "72111920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN HOT-ROLLED : OTHER : HOOPS",
+        "hsn_code": "72111930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN HOT-ROLLED : OTHER : SHEETS",
+        "hsn_code": "72111940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN HOT-ROLLED : OTHER : STRIP",
+        "hsn_code": "72111950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN HOT-ROLLED : OTHER : SKELP",
+        "hsn_code": "72111960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN HOT-ROLLED : OTHER : OTHER",
+        "hsn_code": "72111990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN COLD-ROLLED (COLD-REDUCED) :\nCONTAINING BY WEIGHT LESS THAN 0.25% OF CARBON: FLATS",
+        "hsn_code": "72112310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN COLD-ROLLED (COLD-REDUCED) :\nCONTAINING BY WEIGHT LESS THAN 0.25% OF CARBON: UNIVERSAL PLATES",
+        "hsn_code": "72112320",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN COLD-ROLLED (COLD-REDUCED) :\nCONTAINING BY WEIGHT LESS THAN 0.25% OF CARBON: HOOPS",
+        "hsn_code": "72112330",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN COLD-ROLLED (COLD-REDUCED) :\nCONTAINING BY WEIGHT LESS THAN 0.25% OF CARBON: SHEETS",
+        "hsn_code": "72112340",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN COLD-ROLLED (COLD-REDUCED) :\nCONTAINING BY WEIGHT LESS THAN 0.25% OF CARBON: STRIP",
+        "hsn_code": "72112350",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN COLD-ROLLED (COLD-REDUCED) :\nCONTAINING BY WEIGHT LESS THAN 0.25% OF CARBON: OTHER",
+        "hsn_code": "72112390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN COLD-ROLLED (COLD-REDUCED) :\nOTHER : FLATS",
+        "hsn_code": "72112910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN COLD-ROLLED (COLD-REDUCED) :\nOTHER : UNIVERSAL PLATES",
+        "hsn_code": "72112920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN COLD-ROLLED (COLD-REDUCED) :\nOTHER : HOOPS",
+        "hsn_code": "72112930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN COLD-ROLLED (COLD-REDUCED) :\nOTHER : SHEETS",
+        "hsn_code": "72112940",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN COLD-ROLLED (COLD-REDUCED) :\nOTHER : STRIP",
+        "hsn_code": "72112950",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN COLD-ROLLED (COLD-REDUCED) :\nOTHER : SKELP",
+        "hsn_code": "72112960",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - NOT FURTHER WORKED THAN COLD-ROLLED (COLD-REDUCED) :\nOTHER : OTHER",
+        "hsn_code": "72112990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - OTHER : UNIVERSAL PLATES : OF BOILER QUALITY",
+        "hsn_code": "72119011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - OTHER : UNIVERSAL PLATES : OF HIGH TENSILE QUALITY",
+        "hsn_code": "72119012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - OTHER : UNIVERSAL PLATES : OF SHIP BUILDING QUALITY",
+        "hsn_code": "72119013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nNOT CLAD, PLATED OR COATED - OTHER : OTHER",
+        "hsn_code": "72119090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nCLAD, PLATED OR COATED - PLATED OR COATED WITH TIN : OTS OR MR TYPE",
+        "hsn_code": "72121010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nCLAD, PLATED OR COATED - PLATED OR COATED WITH TIN : OTHER",
+        "hsn_code": "72121090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nCLAD, PLATED OR COATED - ELECTROLYTICALLY PLATED OR COATED WITH ZINC : CORRUGATED",
+        "hsn_code": "72122010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nCLAD, PLATED OR COATED - ELECTROLYTICALLY PLATED OR COATED WITH ZINC : OTHER",
+        "hsn_code": "72122090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nCLAD, PLATED OR COATED - OTHERWISE PLATED OR COATED WITH ZINC : CORRUGATED",
+        "hsn_code": "72123010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nCLAD, PLATED OR COATED - OTHERWISE PLATED OR COATED WITH ZINC : OTHER",
+        "hsn_code": "72123090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nCLAD, PLATED OR COATED - PAINTED, VARNISHED OR COATED WITH PLASTICS",
+        "hsn_code": "72124000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nCLAD, PLATED OR COATED - OTHERWISE PLATED OR COATED : PLATED OR COATED WITH LEAD",
+        "hsn_code": "72125010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nCLAD, PLATED OR COATED - OTHERWISE PLATED OR COATED : LACQUERED",
+        "hsn_code": "72125020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nCLAD, PLATED OR COATED - OTHERWISE PLATED OR COATED : OTHER",
+        "hsn_code": "72125090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM,\nCLAD, PLATED OR COATED - CLAD",
+        "hsn_code": "72126000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL AND NETTING, OF COPPER WIRE; EXPANDED METAL\nOF COPPER - CLOTH : ENDLESS BANDS, FOR MACHINERY",
+        "hsn_code": "74142010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL AND NETTING, OF COPPER WIRE; EXPANDED METAL\nOF COPPER - CLOTH : OTHER",
+        "hsn_code": "74142090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL AND NETTING, OF COPPER WIRE; EXPANDED METAL\nOF COPPER - OTHER :WIRE GAUZE AND NETTING",
+        "hsn_code": "74149010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL AND NETTING, OF COPPER WIRE; EXPANDED METAL\nOF COPPER - OTHER :EXPANDED METAL OF COPPER AND COPPER ALLOYS",
+        "hsn_code": "74149020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CLOTH (INCLUDING ENDLESS BANDS), GRILL AND NETTING, OF COPPER WIRE; EXPANDED METAL\nOF COPPER - OTHER :OTHER",
+        "hsn_code": "74149090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COPPER SPRINGS",
+        "hsn_code": "74160000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COOKING OR HEATING APPARATUS OF A KIND USED FOR DOMESTIC PURPOSES, NON-ELECTRIC,\nAND PARTS THEREOF, OF COPPER - COOKING OR HEATING APPARATUS OF A KIND USED FOR\nDOMESTIC PURPOSES, NON-ELECTRIC, AND PARTS THEREOF, OF COPPER : STOVE : OIL PRESSURE\nSTOVE",
+        "hsn_code": "74170011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COOKING OR HEATING APPARATUS OF A KIND USED FOR DOMESTIC PURPOSES, NON-ELECTRIC,\nAND PARTS THEREOF, OF COPPER - COOKING OR HEATING APPARATUS OF A KIND USED FOR\nDOMESTIC PURPOSES, NON-ELECTRIC, AND PARTS THEREOF, OF COPPER : STOVE : OTHER",
+        "hsn_code": "74170019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COOKING OR HEATING APPARATUS OF A KIND USED FOR DOMESTIC PURPOSES, NON-ELECTRIC,\nAND PARTS THEREOF, OF COPPER - COOKING OR HEATING APPARATUS OF A KIND USED FOR\nDOMESTIC PURPOSES, NON-ELECTRIC, AND PARTS THEREOF, OF COPPER : STOVE : OTHER\nCOOKING AND HEATING APPARATUS",
+        "hsn_code": "74170020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COOKING OR HEATING APPARATUS OF A KIND USED FOR DOMESTIC PURPOSES, NON-ELECTRIC,\nAND PARTS THEREOF, OF COPPER - COOKING OR HEATING APPARATUS OF A KIND USED FOR\nDOMESTIC PURPOSES, NON-ELECTRIC, AND PARTS THEREOF, OF COPPER : PARTS : BURNERS",
+        "hsn_code": "74170091",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COOKING OR HEATING APPARATUS OF A KIND USED FOR DOMESTIC PURPOSES, NON-ELECTRIC,\nAND PARTS THEREOF, OF COPPER - COOKING OR HEATING APPARATUS OF A KIND USED FOR\nDOMESTIC PURPOSES, NON-ELECTRIC, AND PARTS THEREOF, OF COPPER : PARTS : OTHER PARTS\nOF STOVE",
+        "hsn_code": "74170092",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "COOKING OR HEATING APPARATUS OF A KIND USED FOR DOMESTIC PURPOSES, NON-ELECTRIC,\nAND PARTS THEREOF, OF COPPER - COOKING OR HEATING APPARATUS OF A KIND USED FOR\nDOMESTIC PURPOSES, NON-ELECTRIC, AND PARTS THEREOF, OF COPPER : PARTS : OTHER",
+        "hsn_code": "74170099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LEAD BARS, RODS, PROFILES AND WIRE - LEAD BARS, RODS, PROFILES AND WIRE: BARS AND\nRODS: HOLLOW BARS",
+        "hsn_code": "78030011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LEAD BARS, RODS, PROFILES AND WIRE - LEAD BARS, RODS, PROFILES AND WIRE: BARS AND\nRODS: OTHER",
+        "hsn_code": "78030019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LEAD BARS, RODS, PROFILES AND WIRE - LEAD BARS, RODS, PROFILES AND WIRE: PROFILES:\nHOLLOW",
+        "hsn_code": "78030021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LEAD BARS, RODS, PROFILES AND WIRE - LEAD BARS, RODS, PROFILES AND WIRE: PROFILES:\nOTHER",
+        "hsn_code": "78030029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LEAD BARS, RODS, PROFILES AND WIRE - LEAD BARS, RODS, PROFILES AND WIRE: WIRE",
+        "hsn_code": "78030030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LEAD TUBES, PIPES AND TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLING, ELBOWS, SLEEVES) -\nLEAD TUBES, PIPES AND TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLING, ELBOWS, SLEEVES):\nTUBES AND PIPES",
+        "hsn_code": "78050010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LEAD TUBES, PIPES AND TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLING, ELBOWS, SLEEVES) -\nLEAD TUBES, PIPES AND TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLING, ELBOWS, SLEEVES):\nTUBE OR PIPE FITTINGS",
+        "hsn_code": "78050020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ZINC TUBES, PIPES AND TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES)-\nZINC TUBES, PIPES AND TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES)\n:TUBES AND PIPES",
+        "hsn_code": "79060010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "ZINC TUBES, PIPES AND TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES)-\nZINC TUBES, PIPES AND TUBE OR PIPE FITTINGS (FOR EXAMPLE, COUPLINGS, ELBOWS, SLEEVES) :\nTUBE OR PIPE FITTINGS",
+        "hsn_code": "79060020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Metals",
+        "hsn_code": "72-83",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE, HOT-ROLLED, NOT CLAD, PLATED OR COATED",
+        "hsn_code": "7208",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE, COLD-ROLLED (COLD-REDUCED), NOT CLAD, PLATED OR COATED",
+        "hsn_code": "7209",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF 600 MM OR MORE, CLAD, PLATED OR COATED",
+        "hsn_code": "7210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM, NOT CLAD, PLATED OR COATED",
+        "hsn_code": "7211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLAT-ROLLED PRODUCTS OF IRON OR NON-ALLOY STEEL, OF A WIDTH OF LESS THAN 600 MM, CLAD, PLATED OR COATED",
+        "hsn_code": "7212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GLASS BEADS, IMITATION PEARLS, IMITATION PRECIOUS OR SEMI-PRECIOUS STONES AND SIMILAR GLASS SMALLWARES, AND ARTICLES THEREOF OTHER THAN IMITATION JEWELLERY; GLASS EYES OTHER THAN PROSTHETIC ARTICLES; STATUETTES AND OTHER ORNAMENTS OF LAMP-WORKED GLASS, OTHER THAN IMITATION JEWELLERY; GLASS MICROSPHERES NOT EXCEEDING 1 MM IN DIAMETER",
+        "hsn_code": "7018",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "IMITATION JEWELLERY",
+        "hsn_code": "7117",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE HORSES, ASSES, MULES AND HINNIES PURE-BRED BREEDING ANIMALS HORSES",
+        "hsn_code": "1011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE HORSES, ASSES, MULESANDHINNIES PURE-BRED BREEDING ANIMALS ASSES",
+        "hsn_code": "1011020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE HORSES, ASSES, MULES AND HINNIES PURE-BRED BREEDING ANIMAL S OTHER",
+        "hsn_code": "1011090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE HORSES, ASSES, MULES AND HINNIES - OTHER - HORSES FOR POLO",
+        "hsn_code": "1019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE HORSES, ASSES, MULES AND HINNIES - OTHER - ASSES, MULES AND HINNIES AS LIVE STOCK",
+        "hsn_code": "1019020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE HORSES, ASSES, MULES AND HINNIES - OTHER - OTHER",
+        "hsn_code": "1019090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE BOVINE ANIMALS - BULLS - PURE-BRED BREEDING ANIMALS : ADULT",
+        "hsn_code": "1021010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE BOVINE ANIMALS - BULLS - PURE-BRED BREEDING ANIMALS COWS, ADULT",
+        "hsn_code": "1021020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE BOVINE ANIMALS- BULLS - PURE-BRED BREEDING ANIMALS BUFFALOES, ADULT AND\nCALVES",
+        "hsn_code": "1021030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE BOVINE ANIMALS - BULLS - PURE-BRED BREEDING ANIMALS OTHER",
+        "hsn_code": "1021090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE BOVINE ANIMALS - OTHER - BULLS - ADULT",
+        "hsn_code": "1029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE BOVINE ANIMALS - OTHER - BUFFALOES, ADULT AND CALVES",
+        "hsn_code": "1029020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE BOVINE ANIMALS - OTHER - OTHER",
+        "hsn_code": "1029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE SWINE PURE-BRED BREEDING ANIMALS",
+        "hsn_code": "1031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE SWINE - OTHER - WEIGHING LESS THAN 50 KG.",
+        "hsn_code": "1039100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE SWINE- OTHER WEIGHING 50KG. OR MORE",
+        "hsn_code": "1039200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE SHEEP AND GOATS - SHEEP : SHEEP INCLUDING LAMB FOR BREEDING PURPOSE",
+        "hsn_code": "1041010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE SHEEP A ND GOATS SHEEP OTHER",
+        "hsn_code": "1041090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE SHEEP AND GOATS GOATS",
+        "hsn_code": "1042000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SPECIES GALLU SDOMESTICUS, DUCKS, GEESE, TURKEYS AND GUINEA FOWLS WEIGHING NOT\nMORE THAN 185G : FOWLSOFTHESPECIESGALLUSDOMESTICUS",
+        "hsn_code": "1051100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SPECIES GALLU SDOMESTICUS, DUCKS, GEESE, TURKEYS AND GUINEA FOWLS WEIGHING NOT\nMORE THAN 185G : TURKEYS",
+        "hsn_code": "1051200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SPECIES GALLU SDOMESTICUS, DUCKS, GEESE, TURKEYS AND GUINEA FOWLS WEIGHING NOT\nMORE THAN 185G : OTHER",
+        "hsn_code": "1051900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE POULTRY, THAT IS TO SAY, FOWLS OF THE SPECIES GALLUS DOMESTICUS, DUCKS, GEESE,\nTURKEYS AND GUINEAFOWLS - OTHER - FOWLS OF THE SPECIES GALLUS DOMESTICUS\nWEIGHING NOT MORE THAN 2000G",
+        "hsn_code": "1059200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE POULTRY, THAT IS TO SAY, FOWLS OF THE SPECIES GALLUS DOMESTICUS, DUCKS, GEESE,\nTURKEYS AND GUINEAFOWLS - OTHER - FOWLS OF THE SPECIES GALLUSDOMESTICUS WEIGHING\nMORE THAN 2000G,",
+        "hsn_code": "1059300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LIVE POULTRY, THAT IS TO SAY, FOWLS OF THE SPECIES GALLUS DOMESTICUS, DUCKS, GEESE,\nTURKEYS AND GUINEAFOWLS - OTHER - FOWLS OF THE SPECIES GALLUS DOMESTICUS",
+        "hsn_code": "1059400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "SPECIES GALLUSDOMESTICUS, DUCKS, GEESE,TURKEYS AND GUINEA FOWLS - OTHER - OTHER",
+        "hsn_code": "1059900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE ANIMALS MAMMALS : PRIMATES",
+        "hsn_code": "1061100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE ANIMALS MAMMALS WHALES,DOLPHINS AND PORPOISES (MAMMALS OF THE\nORDER CETACEA); MANATEES AND DUGONGS(MAMMALS OF THE ORDER SIRENIA)",
+        "hsn_code": "1061200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE ANIMALS MAMMALS OTHER",
+        "hsn_code": "1061900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE ANIMALS REPTILES (INCLUDING SNAKES AND TURTLES)",
+        "hsn_code": "1062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE ANIMALS BIRDS : BIRDS OF PREY",
+        "hsn_code": "1063100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE ANIMALS BIRDS PSITTACIFORMES(INCLUDING PARROTS, PARAKEETS,MACAWS AND\nCOCKATOOS)",
+        "hsn_code": "1063200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE ANIMALS BIRDS OTHER",
+        "hsn_code": "1063900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHERLIVE ANIMALS- OTHER - BEESANDOTHERINSECTS,NOTELSEWHEREINCLUDEDORSPECIFIED",
+        "hsn_code": "1069010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE ANIMALS - OTHER - PURE LINE STOCK",
+        "hsn_code": "1069020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE ANIMALS - OTHER - OTHER",
+        "hsn_code": "1069090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WHEAT AND MESLIN DURUM WHEAT : OF SEED QUALITY",
+        "hsn_code": "10011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WHEAT AND MESLIN DURUM WHEAT : OTHER",
+        "hsn_code": "10011090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WHEAT AND MESLIN OTHER : WHEAT OF SEED QUALITY",
+        "hsn_code": "10019010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WHEAT AND MESLIN OTHER : OTHER WHEAT",
+        "hsn_code": "10019020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WHEAT AND MESLIN OTHER : MESLIN : OF SEED QUALITY",
+        "hsn_code": "10019031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WHEAT AND MESLIN OTHER : MESLIN : OTHER",
+        "hsn_code": "10019039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "RYE RYE : OF SEED QUALITY",
+        "hsn_code": "10020010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "RYE RYE : OTHER",
+        "hsn_code": "10020090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BARLEY BARLEY : OF SEED QUALITY",
+        "hsn_code": "10030010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BARLEY BARLEY : OTHER",
+        "hsn_code": "10030090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OATS OATS : OF SEED QUALITY",
+        "hsn_code": "10040010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OATS OATS : OTHER",
+        "hsn_code": "10040090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MAIZE (CORN) SEED",
+        "hsn_code": "10051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MAIZE (CORN)OTHER",
+        "hsn_code": "10059000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "RICE RICE IN THE HUSK (PADDY OR ROUGH) : OF SEED QUALITY",
+        "hsn_code": "10061010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "RICE RICE IN THE HUSK (PADDY OR ROUGH) : OTHER",
+        "hsn_code": "10061090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "RICE HUSKED (BROWN) RICE",
+        "hsn_code": "10062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "RICE SEM-MILLED OR WHOLLY-MILLED RICE, WHETHER OR NOT POLISHED OR GLAZED : RICE,\nPARBOILED",
+        "hsn_code": "10063010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "RICE SEMI-MILLED OR WHOLLY-MILLED RICE, WHETHER OR NOT POLISHED OR GLAZED :\nBASMATI RICE",
+        "hsn_code": "10063020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "RICE SEMI-MILLED OR WHOLLY-MILLED RICE, WHETHER OR NOT POLISHED OR GLAZED : OTHER",
+        "hsn_code": "10063090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "RICE BROKEN RICE",
+        "hsn_code": "10064000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GRAIN SORGHUM GRAIN SORGHUM : OF SEED QUALITY",
+        "hsn_code": "10070010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GRAIN SORGHUM GRAIN SORGHUM : OTHER",
+        "hsn_code": "10070090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUCKWHEAT, MILLET AND CANARY SEED; OTHER CEREALS BUCKWHEAT : OF SEED QUALITY",
+        "hsn_code": "10081010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUCKWHEAT, MILLET AND CANARY SEED; OTHER CEREALS BUCKWHEAT : OTHER",
+        "hsn_code": "10081090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUCKWHEAT, MILLET AND CANARY SEED; OTHER CEREALS MILLET : JAWAR : OF SEED QUALITY",
+        "hsn_code": "10082011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUCKWHEAT, MILLET AND CANARY SEED; OTHER CEREALS MILLET : JAWAR : OTHER",
+        "hsn_code": "10082019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUCKWHEAT, MILLET AND CANARY SEED; OTHER CEREALS MILLET : BAJRA : OF SEED QUALITY",
+        "hsn_code": "10082021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUCKWHEAT, MILLET AND CANARY SEED; OTHER CEREALS MILLET : BAJRA : OTHER",
+        "hsn_code": "10082029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUCKWHEAT, MILLET AND CANARY SEED; OTHER CEREALS MILLET : RAGI (FINGER MILLET) : OF\nSEED QUALITY",
+        "hsn_code": "10082031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUCKWHEAT, MILLET AND CANARY SEED; OTHER CEREALS MILLET : RAGI (FINGER MILLET) :\nOTHER",
+        "hsn_code": "10082039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUCKWHEAT, MILLET AND CANARY SEED; OTHER CEREALS CANARY SEED : OF SEED QUALITY",
+        "hsn_code": "10083010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUCKWHEAT, MILLET AND CANARY SEED; OTHER CEREALS CANARY SEED : OTHER",
+        "hsn_code": "10083090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUCKWHEAT, MILLET AND CANARY SEED; OTHER CEREALS OTHER CEREALS : OF SEED QUALITY",
+        "hsn_code": "10089010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUCKWHEAT, MILLET AND CANARY SEED; OTHER CEREALS OTHER CEREALS : OTHER",
+        "hsn_code": "10089090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "other",
+        "hsn_code": "10091090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WHEAT AND MESLIN",
+        "hsn_code": "1001",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "RYE",
+        "hsn_code": "1002",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BARLEY",
+        "hsn_code": "1003",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OATS",
+        "hsn_code": "1004",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MAIZE (CORN)",
+        "hsn_code": "1005",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "RICE",
+        "hsn_code": "1006",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GRAIN SORGHUM",
+        "hsn_code": "1007",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BUCKWHEAT, MILLET AND CANARY SEEDS; OTHER CEREALS",
+        "hsn_code": "1008",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS) LAC :\nSHELLAC",
+        "hsn_code": "13011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS) LAC :\nSEED LAC",
+        "hsn_code": "13011020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS) LAC :\nSTICK LAC",
+        "hsn_code": "13011030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS) LAC :\nDEWAXED AND DECOLOURISED LAC",
+        "hsn_code": "13011040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS) LAC :\nBLEACHED LAC",
+        "hsn_code": "13011050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS) LAC :\nGASKET LAC",
+        "hsn_code": "13011060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS) LAC :\nBUTTON LAC",
+        "hsn_code": "13011070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS) LAC :\nGARNET LAC",
+        "hsn_code": "13011080",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS) LAC :\nOTHER",
+        "hsn_code": "13011090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS) GUM\nARABIC",
+        "hsn_code": "13012000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER :\nNATURAL GUMS : ASIAN GUM",
+        "hsn_code": "13019011",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER :\nNATURAL GUMS : AFRICAN GUM",
+        "hsn_code": "13019012",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER\n: NATURAL GUMS : ASAFOETIDA",
+        "hsn_code": "13019013",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER :\nNATURAL GUMS : BENJAMIN RAS",
+        "hsn_code": "13019014",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER\n: NATURAL GUMS : BENJAMIN COWRIE",
+        "hsn_code": "13019015",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER :\nNATURAL GUMS : KARAYA GUM (INDIAN TRAGACANTH) HASTAB",
+        "hsn_code": "13019016",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER\n: NATURAL GUMS : TRAGACANTH (ADRAGANTH)",
+        "hsn_code": "13019017",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER :\nNATURAL GUMS : STORAX",
+        "hsn_code": "13019018",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER :\nNATURAL GUMS : OTHER",
+        "hsn_code": "13019019",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER :\nRESINS : COPAL",
+        "hsn_code": "13019021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER\n: RESINS : DAMMAR BATU",
+        "hsn_code": "13019022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER\n: RESINS : OTHER",
+        "hsn_code": "13019029",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER\n: GUM RESINS : MYRRH",
+        "hsn_code": "13019031",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER\n: GUM RESINS : OILBANUM OR FRANKINCENSE",
+        "hsn_code": "13019032",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER\n: GUM RESINS : MASTIC GUM",
+        "hsn_code": "13019033",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER\n: GUM RESINS : XANTHIUM GUM",
+        "hsn_code": "13019034",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER\n: GUM RESINS : OTHER",
+        "hsn_code": "13019039",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER\n: OLEORESINS : OF SEEDS",
+        "hsn_code": "13019041",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER :\nOLEORESINS : OF FRUITS",
+        "hsn_code": "13019042",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER :\nOLEORESINS : OF LEAVES",
+        "hsn_code": "13019043",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER :\nOLEORESINS : OF SPICES",
+        "hsn_code": "13019044",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER :\nOLEORESINS : OF FLOWERS",
+        "hsn_code": "13019045",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER :\nOLEORESINS : OF ROOTS",
+        "hsn_code": "13019046",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS)OTHER :\nOLEORESINS : OTHER",
+        "hsn_code": "13019049",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC; NATURAL GUMS, RESINS, GUM-RESINS AND OLEORESINS (FOR EXAMPLE, BALSAMS) -OTHER\n: --- OTHER: ---- OTHER",
+        "hsn_code": "13019099",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "LAC;NATURAL GUMS, RESINS,GUM-RESINS AND OLEORESINS (FOR EXAMPLE,BALSAMS)",
+        "hsn_code": "1301",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER PAINTS AND VARNISHES (INCLUDING ENAMELS, LACQUERS AND DISTEMPERS); PREPARED WATER PIGMENTS OF A KIND USED FOR FINISHING LEATHER",
+        "hsn_code": "3211",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FLOOR COVERINGS ON A BASE OF PAPER OR OF PAPERBOARD, WHETHER OR NOT CUT TO SIZE",
+        "hsn_code": "48150000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BULBS, TUBERS, TUBEROUS ROOTS, CORMS, CROWNS AND RHIZOMES, DORMANT, IN GROWTH\nOR IN FLOWER; CHICORY PLANTS AND ROOTS OTHER THAN ROOTS OF HEADING 1212 BULBS,\nTUBERS, TUBEROUS ROOTS, CORMS, CROWNS AND RHIZOMES, DORMANT",
+        "hsn_code": "6011000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BULBS, TUBERS, TUBEROUS ROOTS, CORMS, CROWNS AND RHIZOMES, DORMANT, IN GROWTH\nOR IN FLOWER; CHICORY PLANTS AND ROOTS OTHER THAN ROOTS OF HEADING 1212 BULBS,\nTUBERS, TUBEROUS ROOTS, CORMS, CROWNS AND RHIZOMES, IN GROWTH OR IN FLOWER;\nCHICORY PLANTS AND ROOTS : BULBS, HORTICULTURAL",
+        "hsn_code": "6012010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BULBS, TUBERS, TUBEROUS ROOTS, CORMS, CROWNS AND RHIZOMES, DORMANT, IN GROWTH\nOR IN FLOWER; CHICORY PLANTS AND ROOTS OTHER THAN ROOTS OF HEADING 1212 BULBS,\nTUBERS, TUBEROUS ROOTS, CORMS, CROWNS AND RHIZOMES, IN GROWTH OR IN FLOWER;\nCHICORY PLANTS AND ROOTS : CHICORY PLANTS AND ROOTS : PLANTS",
+        "hsn_code": "6012021",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BULBS, TUBERS, TUBEROUS ROOTS, CORMS, CROWNS AND RHIZOMES, DORMANT, IN GROWTH\nOR IN FLOWER; CHICORY PLANTS AND ROOTS OTHER THAN ROOTS OF HEADING 1212 BULBS,\nTUBERS, TUBEROUS ROOTS, CORMS, CROWNS AND RHIZOMES, IN GROWTH OR IN FLOWER;\nCHICORY PLANTS AND ROOTS : CHICORY PLANTS AND ROOTS : ROOTS",
+        "hsn_code": "6012022",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BULBS, TUBERS, TUBEROUS ROOTS, CORMS, CROWNS AND RHIZOMES, DORMANT, IN GROWTH\nOR IN FLOWER; CHICORY PLANTS AND ROOTS OTHER THAN ROOTS OF HEADING 1212 BULBS,\nTUBERS, TUBEROUS ROOTS, CORMS, CROWNS AND RHIZOMES, IN GROWTH OR IN FLOWER;\nCHICORY PLANTS AND ROOTS : OTHER",
+        "hsn_code": "6012090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE PLANTS (INCLUDING THEIR ROOTS), CUTTINGS AND SLIPS; MUSHROOM SPAWN\nUNROOTED CUTTINGS AND SLIPS",
+        "hsn_code": "6021000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE PLANTS (INCLUDING THEIR ROOTS), CUTTINGS AND SLIPS; MUSHROOM SPAWN\nTREES, SHRUBS AND BUSHES, GRAFTED OR NOT, OF KINDS, WHICH BEAR EDIBLE FRUITS OR\nNUTS : EDIBLE FRUIT OR NUT TREES, GRAFTED OR NOT",
+        "hsn_code": "6022010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE PLANTS (INCLUDING THEIR ROOTS), CUTTINGS AND SLIPS; MUSHROOM SPAWN\nTREES, SHRUBS AND BUSHES, GRAFTED OR NOT, OF KINDS, WHICH BEAR EDIBLE FRUITS OR\nNUTS : CACTUS",
+        "hsn_code": "6022020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE PLANTS (INCLUDING THEIR ROOTS), CUTTINGS AND SLIPS; MUSHROOM SPAWN\nTREES, SHRUBS AND BUSHES, GRAFTED OR NOT, OF KINDS, WHICH BEAR EDIBLE FRUITS OR\nNUTS : OTHER",
+        "hsn_code": "6022090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE PLANTS (INCLUDING THEIR ROOTS), CUTTINGS AND SLIPS; MUSHROOM SPAWN\nRHODODENDRONS AND AZALEAS, GRAFTED OR NOT",
+        "hsn_code": "6023000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE PLANTS (INCLUDING THEIR ROOTS), CUTTINGS AND SLIPS; MUSHROOM SPAWN\nROSES, GRAFTED OR NOT",
+        "hsn_code": "6024000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE PLANTS (INCLUDING THEIR ROOTS), CUTTINGS AND SLIPS; MUSHROOM SPAWN\nOTHER : MUSHROOM SPAWN",
+        "hsn_code": "6029010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE PLANTS (INCLUDING THEIR ROOTS), CUTTINGS AND SLIPS; MUSHROOM SPAWN\nOTHER : FLOWERING PLANTS (EXCLUDING ROSES AND RHODODENDRONS)",
+        "hsn_code": "6029020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE PLANTS (INCLUDING THEIR ROOTS), CUTTINGS AND SLIPS; MUSHROOM SPAWN\nOTHER : TISSUE CULTURE PLANT",
+        "hsn_code": "6029030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER LIVE PLANTS (INCLUDING THEIR ROOTS), CUTTINGS AND SLIPS; MUSHROOM SPAWN\nOTHER : OTHER",
+        "hsn_code": "6029090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CUT FLOWERS AND FLOWER BUDS OF A KIND SUITABLE FOR BOUQUETS OR FOR ORNAMENTAL\nPURPOSES, FRESH, DRIED, DYED, BLEACHED, IMPREGNATED OR OTHERWISE PREPARED FRESH",
+        "hsn_code": "6031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CUT FLOWERS AND FLOWER BUDS OF A KIND SUITABLE FOR BOUQUETS OR FOR ORNAMENTAL\nPURPOSES, FRESH, DRIED, DYED, BLEACHED, IMPREGNATED OR OTHERWISE PREPARED -FRESH: --\nROSES",
+        "hsn_code": "6031100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CUT FLOWERS AND FLOWER BUDS OF A KIND SUITABLE FOR BOUQUETS OR FOR ORNAMENTAL\nPURPOSES, FRESH, DRIED, DYED, BLEACHED, IMPREGNATED OR OTHERWISE PREPARED -FRESH: --\nCARNATIONS",
+        "hsn_code": "6031200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CUT FLOWERS AND FLOWER BUDS OF A KIND SUITABLE FOR BOUQUETS OR FOR ORNAMENTAL\nPURPOSES, FRESH, DRIED, DYED, BLEACHED, IMPREGNATED OR OTHERWISE PREPARED -FRESH: --\nCHRYSANTHEMUMS",
+        "hsn_code": "6031300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CUT FLOWERS AND FLOWER BUDS OF A KIND SUITABLE FOR BOUQUETS OR FOR ORNAMENTAL\nPURPOSES, FRESH, DRIED, DYED, BLEACHED, IMPREGNATED OR OTHERWISE PREPARED -FRESH: --\nCHRYSANTHEMUMS",
+        "hsn_code": "6031400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CUT FLOWERS AND FLOWER BUDS OF A KIND SUITABLE FOR BOUQUETS OR FOR ORNAMENTAL\nPURPOSES, FRESH, DRIED, DYED, BLEACHED, IMPREGNATED OR OTHERWISE PREPARED -FRESH: --\nOTHER",
+        "hsn_code": "6031900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "CUT FLOWERS AND FLOWER BUDS OF A KIND SUITABLE FOR BOUQUETS OR FOR ORNAMENTAL\nPURPOSES, FRESH, DRIED, DYED, BLEACHED, IMPREGNATED OR OTHERWISE PREPARED OTHER",
+        "hsn_code": "6039000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FOLIAGE, BRANCHES AND OTHER PARTS OF PLANTS, WITHOUT FLOWERS OR FLOWER BUDS,\nAND GRASSES, MOSSES AND LICHENS, BEING GOODS OF A KIND SUITABLE FOR BOUQUETS OR\nFOR ORNAMENTAL PURPOSES, FRESH, DRIED, DYED, BLEACHED, IMPREGNATED OR OTHERWISE\nPREPARED MOSSES AND LICHENS",
+        "hsn_code": "6041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FOLIAGE, BRANCHES AND OTHER PARTS OF PLANTS, WITHOUT FLOWERS OR FLOWER BUDS,\nAND GRASSES, MOSSES AND LICHENS, BEING GOODS OF A KIND SUITABLE FOR BOUQUETS OR\nFOR ORNAMENTAL PURPOSES, FRESH, DRIED, DYED, BLEACHED, IMPREGNATED OR OTHERWISE\nPREPARED OTHER : FRESH",
+        "hsn_code": "6049100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "FOLIAGE, BRANCHES AND OTHER PARTS OF PLANTS, WITHOUT FLOWERS OR FLOWER BUDS,\nAND GRASSES, MOSSES AND LICHENS, BEING GOODS OF A KIND SUITABLE FOR BOUQUETS OR\nFOR ORNAMENTAL PURPOSES, FRESH, DRIED, DYED, BLEACHED, IMPREGNATED OR OTHERWISE\nPREPARED OTHER : OTHER",
+        "hsn_code": "6049900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PILE FABRICS, INCLUDING LONG PILE FABRICS AND TERRY FABRICS, KNITTED OR CROCHETED -\nLONG PILE FABRICS: OF COTTON",
+        "hsn_code": "60011010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PILE FABRICS, INCLUDING LONG PILE FABRICS AND TERRY FABRICS, KNITTED OR CROCHETED -\nLONG PILE FABRICS: OF MAN-MADE FIBRES",
+        "hsn_code": "60011020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PILE FABRICS, INCLUDING LONG PILE FABRICS AND TERRY FABRICS, KNITTED OR CROCHETED -\nLONG PILE FABRICS: OF OTHER TEXTILE MATERIALS",
+        "hsn_code": "60011090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PILE FABRICS, INCLUDING LONG PILE FABRICS AND TERRY FABRICS, KNITTED OR CROCHETED -\nLOOPED PILE FABRICS:OF COTTON",
+        "hsn_code": "60012100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PILE FABRICS, INCLUDING LONG PILE FABRICS AND TERRY FABRICS, KNITTED OR CROCHETED -\nLOOPED PILE FABRICS:OF MAN-MADE FIBRES",
+        "hsn_code": "60012200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PILE FABRICS, INCLUDING LONG PILE FABRICS AND TERRY FABRICS, KNITTED OR CROCHETED -\nLOOPED PILE FABRICS:OF OTHER TEXTILE MATERIALS",
+        "hsn_code": "60012900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PILE FABRICS, INCLUDING LONG PILE FABRICS AND TERRY FABRICS, KNITTED OR CROCHETED -\nOTHER:OF COTTON",
+        "hsn_code": "60019100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PILE FABRICS, INCLUDING LONG PILE FABRICS AND TERRY FABRICS, KNITTED OR CROCHETED -\nOTHER: OF MAN-MADE FIBRES",
+        "hsn_code": "60019200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PILE FABRICS, INCLUDING LONG PILE FABRICS AND TERRY FABRICS, KNITTED OR CROCHETED -\nOTHER:- OF OTHER TEXTILE MATERIALS:OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "60019910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PILE FABRICS, INCLUDING LONG PILE FABRICS AND TERRY FABRICS, KNITTED OR CROCHETED -\nOTHER:- OF OTHER TEXTILE MATERIALS:OTHER",
+        "hsn_code": "60019990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "KNITTED OR CROCHETED FABRICS OF A WIDTH NOT EXCEEDING 30 CM, CONTAINING BY WEIGHT\n5% OR MORE OF ELASTOMERIC YARN OR RUBBER THREAD, OTHER THAN THOSE OF HEADING\n6001 - CONTAINING BY WEIGHT 5% OR MORE OF ELASTOMERIC YARN BUT NOT CONTAINING\nRUBBER THREAD",
+        "hsn_code": "60024000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "KNITTED OR CROCHETED FABRICS OF A WIDTH NOT EXCEEDING 30 CM, CONTAINING BY WEIGHT\n5% OR MORE OF ELASTOMERIC YARN OR RUBBER THREAD, OTHER THAN THOSE OF HEADING\n6001- OTHER",
+        "hsn_code": "60029000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "KNITTED OR CROCHETED FABRICS OF A WIDTH NOT EXCEEDING 30 CM, OTHER THAN THOSE OF\nHEADING 6001 OR 6002 - OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "60031000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "KNITTED OR CROCHETED FABRICS OF A WIDTH NOT EXCEEDING 30 CM, OTHER THAN THOSE OF\nHEADING 6001 OR 6002 - OF COTTON",
+        "hsn_code": "60032000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "KNITTED OR CROCHETED FABRICS OF A WIDTH NOT EXCEEDING 30 CM, OTHER THAN THOSE OF\nHEADING 6001 OR 6002 - OF SYNTHETIC FIBRES",
+        "hsn_code": "60033000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "KNITTED OR CROCHETED FABRICS OF A WIDTH NOT EXCEEDING 30 CM, OTHER THAN THOSE OF\nHEADING 6001 OR 6002 - OF ARTIFICIAL FIBRES",
+        "hsn_code": "60034000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "KNITTED OR CROCHETED FABRICS OF A WIDTH NOT EXCEEDING 30 CM, OTHER THAN THOSE OF\nHEADING 6001 OR 6002 - OTHER",
+        "hsn_code": "60039000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "KNITTED OR CROCHETED FABRICS OF A WIDTH EXCEEDING 30 CM, CONTAINING BY WEIGHT 5%\nOR MORE OF ELASTOMERIC YARN OR RUBBER THREAD, OTHER THAN THOSE OF HEADING 6001 -\nCONTAINING BY WEIGHT 5% OR MORE OF ELASTOMERIC YARN BUT NOT CONTAINING RUBBER\nTHREAD",
+        "hsn_code": "60041000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "KNITTED OR CROCHETED FABRICS OF A WIDTH EXCEEDING 30 CM, CONTAINING BY WEIGHT 5%\nOR MORE OF ELASTOMERIC YARN OR RUBBER THREAD, OTHER THAN THOSE OF HEADING 6001 -\nOTHER",
+        "hsn_code": "60049000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WARP KNIT FABRICS (INCLUDING THOSE MADE ON GALLOON KNITTING MACHINES), OTHER\nTHAN THOSE OF HEADINGS 6001 TO 6004 - OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "60051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WARP KNIT FABRICS (INCLUDING THOSE MADE ON GALLOON KNITTING MACHINES), OTHER\nTHAN THOSE OF HEADINGS 6001 TO 6004 - OF COTTON:UNBLEACHED OR BLEACHED",
+        "hsn_code": "60052100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WARP KNIT FABRICS (INCLUDING THOSE MADE ON GALLOON KNITTING MACHINES), OTHER\nTHAN THOSE OF HEADINGS 6001 TO 6004 - OF COTTON:DYED",
+        "hsn_code": "60052200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WARP KNIT FABRICS (INCLUDING THOSE MADE ON GALLOON KNITTING MACHINES), OTHER\nTHAN THOSE OF HEADINGS 6001 TO 6004 - OF COTTON:OF YARNS OF DIFFERENT COLOURS",
+        "hsn_code": "60052300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WARP KNIT FABRICS (INCLUDING THOSE MADE ON GALLOON KNITTING MACHINES), OTHER\nTHAN THOSE OF HEADINGS 6001 TO 6004 - OF COTTON:PRINTED",
+        "hsn_code": "60052400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WARP KNIT FABRICS (INCLUDING THOSE MADE ON GALLOON KNITTING MACHINES), OTHER\nTHAN THOSE OF HEADINGS 6001 TO 6004 - OF SYNTHETIC FIBRES :UNBLEACHED OR BLEACHED",
+        "hsn_code": "60053100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WARP KNIT FABRICS (INCLUDING THOSE MADE ON GALLOON KNITTING MACHINES), OTHER\nTHAN THOSE OF HEADINGS 6001 TO 6004 - OF SYNTHETIC FIBRES :DYED",
+        "hsn_code": "60053200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WARP KNIT FABRICS (INCLUDING THOSE MADE ON GALLOON KNITTING MACHINES), OTHER\nTHAN THOSE OF HEADINGS 6001 TO 6004 - OF SYNTHETIC FIBRES : OF YARNS OF DIFFERENT\nCOLOURS",
+        "hsn_code": "60053300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WARP KNIT FABRICS (INCLUDING THOSE MADE ON GALLOON KNITTING MACHINES), OTHER\nTHAN THOSE OF HEADINGS 6001 TO 6004 - OF SYNTHETIC FIBRES :PRINTED",
+        "hsn_code": "60053400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WARP KNIT FABRICS (INCLUDING THOSE MADE ON GALLOON KNITTING MACHINES), OTHER\nTHAN THOSE OF HEADINGS 6001 TO 6004 - OF ARTIFICIAL FIBRES : UNBLEACHED OR BLEACHED",
+        "hsn_code": "60054100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WARP KNIT FABRICS (INCLUDING THOSE MADE ON GALLOON KNITTING MACHINES), OTHER\nTHAN THOSE OF HEADINGS 6001 TO 6004 - OF ARTIFICIAL FIBRES : DYED",
+        "hsn_code": "60054200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WARP KNIT FABRICS (INCLUDING THOSE MADE ON GALLOON KNITTING MACHINES), OTHER\nTHAN THOSE OF HEADINGS 6001 TO 6004 - OF ARTIFICIAL FIBRES : OF YARNS OF DIFFERENT\nCOLOURS",
+        "hsn_code": "60054300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WARP KNIT FABRICS (INCLUDING THOSE MADE ON GALLOON KNITTING MACHINES), OTHER\nTHAN THOSE OF HEADINGS 6001 TO 6004 - OF ARTIFICIAL FIBRES : PRINTED",
+        "hsn_code": "60054400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WARP KNIT FABRICS (INCLUDING THOSE MADE ON GALLOON KNITTING MACHINES), OTHER\nTHAN THOSE OF HEADINGS 6001 TO 6004 - OTHER",
+        "hsn_code": "60059000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "PILE FABRICS, INCLUDING LONG PILE FABRICS AND TERRY FABRICS, KNITTED OR CROCHETED",
+        "hsn_code": "6001",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "KNITTED OR CROCHETED FABRICS OF A WIDTH NOT EXCEEDING 30 CM, CONTAINING BY WEIGHT 5% OR MORE OF ELASTOMERIC YARN OR RUBBER THREAD, OTHER THAN THOSE OF HEADING 6001",
+        "hsn_code": "6002",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "KNITTED OR CROCHETED FABRICS OF A WIDTH NOT EXCEEDING 30 CM, OTHER THAN THOSE OF HEADING 6001 OR 6002",
+        "hsn_code": "6003",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "KNITTED OR CROCHETED FABRICS OF A WIDTH EXCEEDING 30 CM, CONTAINING BY WEIGHT 5% OR MORE OF ELASTOMERIC YARN OR RUBBER THREAD, OTHER THAN THOSE OF HEADING 6001",
+        "hsn_code": "6004",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WARP KNIT FABRICS (INCLUDING THOSE MADE ON GALLOON KNITTING MACHINES), OTHER THAN THOSE OF HEADINGS 6001 TO 6004",
+        "hsn_code": "6005",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CAR-COATS, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWINDCHEATERS, WIND-JACKETS AND SIMILAR ARTICLES OTHER THAN THOSE OF HEADING 6203 -\nOVERCOATS, RAINCOATS, CAR-COATS, CAPES, CLOAKS AND SIMILAR ARTICLES : OF WOOL AND\nFINE ANIMAL HAIR",
+        "hsn_code": "62011100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CAR-COATS, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWINDCHEATERS, WIND-JACKETS AND SIMILAR ARTICLES OTHER THAN THOSE OF HEADING 6203 -\nOVERCOATS, RAINCOATS, CAR-COATS, CAPES, CLOAKS AND SIMILAR ARTICLES : OF COTTON :\nRAINCOATS",
+        "hsn_code": "62011210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CAR-COATS, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWINDCHEATERS, WIND-JACKETS AND SIMILAR ARTICLES OTHER THAN THOSE OF HEADING 6203 -\nOVERCOATS, RAINCOATS, CAR-COATS, CAPES, CLOAKS AND SIMILAR ARTICLES : OF COTTON :\nOTHER",
+        "hsn_code": "62011290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CAR-COATS, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWINDCHEATERS, WIND-JACKETS AND SIMILAR ARTICLES OTHER THAN THOSE OF HEADING 6203 -\nOVERCOATS, RAINCOATS, CAR-COATS, CAPES, CLOAKS AND SIMILAR ARTICLES : OF MAN-MADE\nFIBRES : RAINCOATS",
+        "hsn_code": "62011310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CAR-COATS, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWINDCHEATERS, WIND-JACKETS AND SIMILAR ARTICLES OTHER THAN THOSE OF HEADING 6203 -\nOVERCOATS, RAINCOATS, CAR-COATS, CAPES, CLOAKS AND SIMILAR ARTICLES : OF MAN-MADE\nFIBRES : OTHER",
+        "hsn_code": "62011390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CAR-COATS, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWINDCHEATERS, WIND-JACKETS AND SIMILAR ARTICLES OTHER THAN THOSE OF HEADING 6203 -\nOVERCOATS, RAINCOATS, CAR-COATS, CAPES, CLOAKS AND SIMILAR ARTICLES : OF OTHER\nTEXTILE MATERIALS : OF SILK",
+        "hsn_code": "62011910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CAR-COATS, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWINDCHEATERS, WIND-JACKETS AND SIMILAR ARTICLES OTHER THAN THOSE OF HEADING 6203 -\nOVERCOATS, RAINCOATS, CAR-COATS, CAPES, CLOAKS AND SIMILAR ARTICLES : OF OTHER\nTEXTILE MATERIALS : OF OTHER TEXTILE FIBRES",
+        "hsn_code": "62011990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CAR-COATS, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWINDCHEATERS, WIND-JACKETS AND SIMILAR ARTICLES OTHER THAN THOSE OF HEADING 6203 -\nOTHER : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "62019100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CAR-COATS, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWINDCHEATERS, WIND-JACKETS AND SIMILAR ARTICLES OTHER THAN THOSE OF HEADING 6203 -\nOTHER : OF COTTON",
+        "hsn_code": "62019200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CAR-COATS, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWINDCHEATERS, WIND-JACKETS AND SIMILAR ARTICLES OTHER THAN THOSE OF HEADING 6203 -\nOTHER : OF MAN-MADE FIBRES",
+        "hsn_code": "62019300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CAR-COATS, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWINDCHEATERS, WIND-JACKETS AND SIMILAR ARTICLES OTHER THAN THOSE OF HEADING 6203 -\nOTHER : OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "62019910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS OVERCOATS, CAR-COATS, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS),\nWINDCHEATERS, WIND-JACKETS AND SIMILAR ARTICLES OTHER THAN THOSE OF HEADING 6203 -\nOTHER : OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "62019990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI\nJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, OTHER THAN THOSE OF\nHEADING 6204 - OVERCOATS, RAINCOATS, CAR-COATS, CAPES, CLOAKS AND SIMILAR ARTICLES :\nOF WOOL OR FINE ANIMAL HAIR : COATS",
+        "hsn_code": "62021110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI\nJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, OTHER THAN THOSE OF\nHEADING 6204 - OVERCOATS, RAINCOATS, CAR-COATS, CAPES, CLOAKS AND SIMILAR ARTICLES :\nOF WOOL OR FINE ANIMAL HAIR : OTHER",
+        "hsn_code": "62021190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI\nJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, OTHER THAN THOSE OF\nHEADING 6204 - OVERCOATS, RAINCOATS, CAR-COATS, CAPES, CLOAKS AND SIMILAR ARTICLES :\nOF COTTON",
+        "hsn_code": "62021200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI\nJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, OTHER THAN THOSE OF\nHEADING 6204 - OVERCOATS, RAINCOATS, CAR-COATS, CAPES, CLOAKS AND SIMILAR ARTICLES :\nOF MAN-MADE FIBRES",
+        "hsn_code": "62021300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI\nJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, OTHER THAN THOSE OF\nHEADING 6204 - OVERCOATS, RAINCOATS, CAR-COATS, CAPES, CLOAKS AND SIMILAR ARTICLES :\nOF OTHER TEXTILE MATERIALS : COATS OF SILK",
+        "hsn_code": "62021910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI\nJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, OTHER THAN THOSE OF\nHEADING 6204 - OVERCOATS, RAINCOATS, CAR-COATS, CAPES, CLOAKS AND SIMILAR ARTICLES :\nOF OTHER TEXTILE MATERIALS : COATS OF ALL OTHER FIBRES",
+        "hsn_code": "62021920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI\nJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, OTHER THAN THOSE OF\nHEADING 6204 - OVERCOATS, RAINCOATS, CAR-COATS, CAPES, CLOAKS AND SIMILAR ARTICLES :\nOF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "62021990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI\nJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, OTHER THAN THOSE OF\nHEADING 6204 - OTHER : OF WOOL OR FINE ANIMAL HAIR : WIND AND SKI-JACKETS, WIND\nCHEATERS",
+        "hsn_code": "62029110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI\nJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, OTHER THAN THOSE OF\nHEADING 6204 - OTHER : OF WOOL OR FINE ANIMAL HAIR : OTHER",
+        "hsn_code": "62029190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI\nJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, OTHER THAN THOSE OF\nHEADING 6204 - OTHER : OF COTTON : WIND AND SKI-JACKETS, WIND-CHEATERS",
+        "hsn_code": "62029210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI\nJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, OTHER THAN THOSE OF\nHEADING 6204 - OTHER : OF COTTON : OTHER",
+        "hsn_code": "62029290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI\nJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, OTHER THAN THOSE OF\nHEADING 6204 - OTHER : OF MAN-MADE FIBRES : WIND AND SKI-JACKETS, WIND-CHEATERS",
+        "hsn_code": "62029310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI\nJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, OTHER THAN THOSE OF\nHEADING 6204 - OTHER : OF MAN-MADE FIBRES : OTHER",
+        "hsn_code": "62029390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI\nJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, OTHER THAN THOSE OF\nHEADING 6204 - OTHER : OF OTHER TEXTILE MATERIALS : OF SILK : WIND AND SKI-JACKETS",
+        "hsn_code": "62029911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI\nJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, OTHER THAN THOSE OF\nHEADING 6204 - OTHER : OF OTHER TEXTILE MATERIALS : OF SILK : OTHER",
+        "hsn_code": "62029919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI\nJACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, OTHER THAN THOSE OF\nHEADING 6204 - OTHER : OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "62029990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIMWEAR) - SUITS : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "62031100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIMWEAR) - SUITS : OF SYNTHETIC FIBRES",
+        "hsn_code": "62031200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIMWEAR) - SUITS : OF OTHER TEXTILE MATERIALS : OF\nCOTTON",
+        "hsn_code": "62031910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIMWEAR) - SUITS : OF OTHER TEXTILE MATERIALS :\nOTHER",
+        "hsn_code": "62031990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIMWEAR) - ENSEMBLES : OF WOOL OR FINE ANIMAL\nHAIR",
+        "hsn_code": "62032100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIMWEAR) - ENSEMBLES : OF COTTON",
+        "hsn_code": "62032200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIMWEAR) - ENSEMBLES : OF SYNTHETIC FIBRES",
+        "hsn_code": "62032300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIMWEAR) - ENSEMBLES : OF OTHER TEXTILE\nMATERIALS",
+        "hsn_code": "62032900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIMWEAR) - JACKETS AND BLAZERS : OF WOOL OR FINE\nANIMAL HAIR",
+        "hsn_code": "62033100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIMWEAR) - JACKETS AND BLAZERS : OF COTTON",
+        "hsn_code": "62033200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIMWEAR) - JACKETS AND BLAZERS : OF SYNTHETIC\nFIBRES",
+        "hsn_code": "62033300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIMWEAR) - JACKETS AND BLAZERS : OF OTHER TEXTILE\nMATERIALS : OF SILK",
+        "hsn_code": "62033910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIMWEAR) - JACKETS AND BLAZERS : OF OTHER TEXTILE\nMATERIALS : OTHER",
+        "hsn_code": "62033990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIMWEAR) - TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "62034100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIMWEAR) - TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS : OF COTTON",
+        "hsn_code": "62034200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIMWEAR) - TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS : OF SYNTHETIC FIBRES",
+        "hsn_code": "62034300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIMWEAR) - TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS : OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "62034910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS (OTHER THAN SWIMWEAR) - TROUSERS, BIB AND BRACE OVERALLS,\nBREECHES AND SHORTS : OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "62034990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nSUITS : OF WOOL OF FINE ANIMAL HAIR",
+        "hsn_code": "62041100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nSUITS : OF COTTON",
+        "hsn_code": "62041200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nSUITS : OF SYNTHETIC FIBRES",
+        "hsn_code": "62041300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nSUITS : OF OTHER TEXTILE MATERIALS : OF SILK : SEQUINNED OR BEADED WITH CHATTONS OR\nEMBROIDERED",
+        "hsn_code": "62041911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nSUITS : OF OTHER TEXTILE MATERIALS : OF SILK : OTHER",
+        "hsn_code": "62041919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nSUITS : OF OTHER TEXTILE MATERIALS : OF ALL OTHER FIBRES",
+        "hsn_code": "62041990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nENSEMBLES : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "62042100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nENSEMBLES : OF COTTON : BLOUSES COMBINED WITH SKIRTS, TROUSERS OR SHORTS",
+        "hsn_code": "62042210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nENSEMBLES : OF COTTON : OTHER",
+        "hsn_code": "62042290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nENSEMBLES : OF SYNTHETIC FIBRES",
+        "hsn_code": "62042300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nENSEMBLES : OF OTHER TEXTILE MATERIALS : OF SILK : SEQUINNED OR BEADED",
+        "hsn_code": "62042911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nENSEMBLES : OF OTHER TEXTILE MATERIALS : OF SILK : OTHER",
+        "hsn_code": "62042919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nENSEMBLES : OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "62042990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nJACKETS AND BLAZERS : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "62043100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nJACKETS AND BLAZERS : OF COTTON",
+        "hsn_code": "62043200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nJACKETS AND BLAZERS : OF SYNTHETIC FIBRES",
+        "hsn_code": "62043300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nJACKETS AND BLAZERS : OF OTHER TEXTILE MATERIALS : OF SILK : SEQUINNED OR BEADED WITH\nCHATTONS OR EMBROIDERED",
+        "hsn_code": "62043911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nJACKETS AND BLAZERS : OF OTHER TEXTILE MATERIALS : OF SILK : OTHER",
+        "hsn_code": "62043919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nJACKETS AND BLAZERS : OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "62043990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nDRESSES : OF WOOL OR FINE ANIMAL HAIR : HOUSE COATS AND LIKE DRESSES",
+        "hsn_code": "62044110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nDRESSES : OF WOOL OR FINE ANIMAL HAIR : BLAZERS",
+        "hsn_code": "62044120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nDRESSES : OF WOOL OR FINE ANIMAL HAIR : OTHER",
+        "hsn_code": "62044190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nDRESSES : OF COTTON : HOUSE COATS AND THE LIKE DRESSES",
+        "hsn_code": "62044210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nDRESSES : OF COTTON : DRESSES",
+        "hsn_code": "62044220",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nDRESSES : OF COTTON : OTHER",
+        "hsn_code": "62044290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nDRESSES : OF SYNTHETIC FIBRES : HOUSE COATS AND THE LIKE",
+        "hsn_code": "62044310",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nDRESSES : OF SYNTHETIC FIBRES : OTHER",
+        "hsn_code": "62044390",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nDRESSES : OF SYNTHETIC FIBRES : OF ARTIFICIAL FIBRES",
+        "hsn_code": "62044400",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nDRESSES : OF OTHER TEXTILE MATERIALS : OF SILK : HOUSE COATS AND THE LIKE DRESSES",
+        "hsn_code": "62044911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nDRESSES : OF OTHER TEXTILE MATERIALS : OF SILK : OTHER",
+        "hsn_code": "62044919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nDRESSES : OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "62044990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nSKIRTS AND DIVIDED SKIRTS : OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "62045100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nSKIRTS AND DIVIDED SKIRTS : OF COTTON",
+        "hsn_code": "62045200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nSKIRTS AND DIVIDED SKIRTS : OF SYNTHETIC FIBRES",
+        "hsn_code": "62045300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nSKIRTS AND DIVIDED SKIRTS : OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "62045910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nSKIRTS AND DIVIDED SKIRTS : OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "62045990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS : OF WOOL OR FINE ANIMAL\nHAIR : TROUSERS AND SHORTS",
+        "hsn_code": "62046110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS : OF WOOL OR FINE ANIMAL\nHAIR : OTHER",
+        "hsn_code": "62046190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS : OF COTTON",
+        "hsn_code": "62046200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS : OF SYNTHETIC FIBRES",
+        "hsn_code": "62046300",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS : OF OTHER TEXTILE MATERIALS :\nOF SILK : SEQUINNED OR BEADED OR EMBROIDERED",
+        "hsn_code": "62046911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS : OF OTHER TEXTILE MATERIALS :\nOF SILK : OTHER",
+        "hsn_code": "62046919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SUITS, ENSEMBLES, JACKETS, BLAZERS, DRESSES, SKIRTS, DIVIDED SKIRTS,\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR) -\nTROUSERS, BIB AND BRACE OVERALLS, BREECHES AND SHORTS : 69 - OF OTHER TEXTILE\nMATERIALS : OTHER",
+        "hsn_code": "62046990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SHIRTS - OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "62051000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SHIRTS - OF COTTON",
+        "hsn_code": "62052000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SHIRTS - OF MAN-MADE FIBRES",
+        "hsn_code": "62053000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SHIRTS - OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "62059010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SHIRTS - OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "62059090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS BLOUSES, SHIRTS AND SHIRT-BLOUSES - OF SILK OR SILK WASTE : OF SILK",
+        "hsn_code": "62061010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS BLOUSES, SHIRTS AND SHIRT-BLOUSES - OF SILK OR SILK WASTE : OTHER",
+        "hsn_code": "62061090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS BLOUSES, SHIRTS AND SHIRT-BLOUSES - OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "62062000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS BLOUSES, SHIRTS AND SHIRT-BLOUSES - OF COTTON",
+        "hsn_code": "62063000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS BLOUSES, SHIRTS AND SHIRT-BLOUSES - OF MAN-MADE FIBRES",
+        "hsn_code": "62064000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS BLOUSES, SHIRTS AND SHIRT-BLOUSES - OF OTHER TEXTILE MATERIALS",
+        "hsn_code": "62069000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SINGLETS AND OTHER VESTS, UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES - UNDERPANTS AND BRIEFS : OF\nCOTTON",
+        "hsn_code": "62071100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SINGLETS AND OTHER VESTS, UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES - UNDERPANTS AND BRIEFS : OF OTHER\nTEXTILE MATERIALS : OF SYNTHETIC FIBRES",
+        "hsn_code": "62071910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SINGLETS AND OTHER VESTS, UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES - UNDERPANTS AND BRIEFS : OF OTHER\nTEXTILE MATERIALS : OF WOOL",
+        "hsn_code": "62071920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SINGLETS AND OTHER VESTS, UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES - UNDERPANTS AND BRIEFS : OF OTHER\nTEXTILE MATERIALS : OF SILK",
+        "hsn_code": "62071930",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SINGLETS AND OTHER VESTS, UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES - UNDERPANTS AND BRIEFS : OF OTHER\nTEXTILE MATERIALS : OTHER",
+        "hsn_code": "62071990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SINGLETS AND OTHER VESTS, UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES - NIGHT SHIRTS AND PYJAMAS : OF\nCOTTON",
+        "hsn_code": "62072110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SINGLETS AND OTHER VESTS, UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES - NIGHT SHIRTS AND PYJAMAS : OTHER",
+        "hsn_code": "62072190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SINGLETS AND OTHER VESTS, UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES - NIGHT SHIRTS AND PYJAMAS : OF MAN\nMADE FIBRES",
+        "hsn_code": "62072200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SINGLETS AND OTHER VESTS, UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES - NIGHT SHIRTS AND PYJAMAS : OF\nOTHER TEXTILE MATERIALS",
+        "hsn_code": "62072900",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SINGLETS AND OTHER VESTS, UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES - OTHER : OF COTTON : DRESSING\nGOWNS AND BATHROBES",
+        "hsn_code": "62079110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SINGLETS AND OTHER VESTS, UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES - OTHER : OF COTTON : UNDER SHIRTS\nOTHER THAN HAND PRINTED",
+        "hsn_code": "62079120",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SINGLETS AND OTHER VESTS, UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES - OTHER : OF COTTON : OTHER",
+        "hsn_code": "62079190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SINGLETS AND OTHER VESTS, UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES - OTHER : OF MAN-MADE FIBRES",
+        "hsn_code": "62079200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SINGLETS AND OTHER VESTS, UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES - OTHER : OF OTHER TEXTILE MATERIALS\n: OF SILK : DRESSING GOWNS AND BATHROBES",
+        "hsn_code": "62079911",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SINGLETS AND OTHER VESTS, UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES - OTHER : OF OTHER TEXTILE MATERIALS\n: OF SILK : OTHER",
+        "hsn_code": "62079919",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SINGLETS AND OTHER VESTS, UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES - OTHER : OF OTHER TEXTILE MATERIALS\n: OF WOOL : DRESSING GOWNS AND BATHROBES",
+        "hsn_code": "62079921",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SINGLETS AND OTHER VESTS, UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES - OTHER : OF OTHER TEXTILE MATERIALS\n: OF WOOL : OTHER",
+        "hsn_code": "62079929",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MENS OR BOYS SINGLETS AND OTHER VESTS, UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS,\nBATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES - OTHER : OF OTHER TEXTILE MATERIALS\n: OTHER",
+        "hsn_code": "62079990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SINGLETS AND OTHER VESTS, SLIPS, PETTICOATS, BRIEFS, PANTIES,\nNIGHTDRESSES, PYJAMAS, NEGLIGES, BATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES -\nSLIPS AND PETTICOATS : OF MAN-MADE FIBRES",
+        "hsn_code": "62081100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SINGLETS AND OTHER VESTS, SLIPS, PETTICOATS, BRIEFS, PANTIES,\nNIGHTDRESSES, PYJAMAS, NEGLIGES, BATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES -\nSLIPS AND PETTICOATS : OF OTHER TEXTILE MATERIALS : OF COTTON OTHER THAN HAND\nPRINTED",
+        "hsn_code": "62081910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SINGLETS AND OTHER VESTS, SLIPS, PETTICOATS, BRIEFS, PANTIES,\nNIGHTDRESSES, PYJAMAS, NEGLIGES, BATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES -\nSLIPS AND PETTICOATS : OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "62081990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SINGLETS AND OTHER VESTS, SLIPS, PETTICOATS, BRIEFS, PANTIES,\nNIGHTDRESSES, PYJAMAS, NEGLIGES, BATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES -\nNIGHTDRESSES AND PYJAMAS : OF COTTON",
+        "hsn_code": "62082100",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SINGLETS AND OTHER VESTS, SLIPS, PETTICOATS, BRIEFS, PANTIES,\nNIGHTDRESSES, PYJAMAS, NEGLIGES, BATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES -\nNIGHTDRESSES AND PYJAMAS : OF MAN-MADE FIBRES",
+        "hsn_code": "62082200",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SINGLETS AND OTHER VESTS, SLIPS, PETTICOATS, BRIEFS, PANTIES,\nNIGHTDRESSES, PYJAMAS, NEGLIGES, BATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES -\nNIGHTDRESSES AND PYJAMAS : OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "62082910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SINGLETS AND OTHER VESTS, SLIPS, PETTICOATS, BRIEFS, PANTIES,\nNIGHTDRESSES, PYJAMAS, NEGLIGES, BATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES -\nNIGHTDRESSES AND PYJAMAS : OF OTHER TEXTILE MATERIALS : OF WOOL",
+        "hsn_code": "62082920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SINGLETS AND OTHER VESTS, SLIPS, PETTICOATS, BRIEFS, PANTIES,\nNIGHTDRESSES, PYJAMAS, NEGLIGES, BATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES -\nNIGHTDRESSES AND PYJAMAS : OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "62082990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SINGLETS AND OTHER VESTS, SLIPS, PETTICOATS, BRIEFS, PANTIES,\nNIGHTDRESSES, PYJAMAS, NEGLIGES, BATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES -\nOTHER : OF COTTON : DRESSING GOWNS AND BATHROBES",
+        "hsn_code": "62089110",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SINGLETS AND OTHER VESTS, SLIPS, PETTICOATS, BRIEFS, PANTIES,\nNIGHTDRESSES, PYJAMAS, NEGLIGES, BATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES -\nOTHER : OF COTTON : OTHER",
+        "hsn_code": "62089190",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SINGLETS AND OTHER VESTS, SLIPS, PETTICOATS, BRIEFS, PANTIES,\nNIGHTDRESSES, PYJAMAS, NEGLIGES, BATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES -\nOTHER : OF MAN-MADE FIBRES : DRESSING GOWNS AND BATHROBES",
+        "hsn_code": "62089210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SINGLETS AND OTHER VESTS, SLIPS, PETTICOATS, BRIEFS, PANTIES,\nNIGHTDRESSES, PYJAMAS, NEGLIGES, BATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES -\nOTHER : OF MAN-MADE FIBRES : OTHER",
+        "hsn_code": "62089290",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SINGLETS AND OTHER VESTS, SLIPS, PETTICOATS, BRIEFS, PANTIES,\nNIGHTDRESSES, PYJAMAS, NEGLIGES, BATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES -\nOTHER : OF OTHER TEXTILE MATERIALS : DRESSING GOWNS AND BATHROBES OF WOOL",
+        "hsn_code": "62089910",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SINGLETS AND OTHER VESTS, SLIPS, PETTICOATS, BRIEFS, PANTIES,\nNIGHTDRESSES, PYJAMAS, NEGLIGES, BATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES -\nOTHER : OF OTHER TEXTILE MATERIALS : DRESSING GOWNS AND BATHROBES OF SILK",
+        "hsn_code": "62089920",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMENS OR GIRLS SINGLETS AND OTHER VESTS, SLIPS, PETTICOATS, BRIEFS, PANTIES,\nNIGHTDRESSES, PYJAMAS, NEGLIGES, BATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES -\nOTHER : OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "62089990",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BABIES GARMENTS AND CLOTHING ACCESSORIES - OF WOOL OR FINE ANIMAL HAIR",
+        "hsn_code": "62091000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BABIES GARMENTS AND CLOTHING ACCESSORIES - OF COTTON",
+        "hsn_code": "62092000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BABIES GARMENTS AND CLOTHING ACCESSORIES - OF SYNTHETIC FIBRES",
+        "hsn_code": "62093000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BABIES GARMENTS AND CLOTHING ACCESSORIES - OF OTHER TEXTILE MATERIALS : OF SILK",
+        "hsn_code": "62099010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BABIES GARMENTS AND CLOTHING ACCESSORIES - OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "62099090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GARMENTS, MADE UP OF FABRICS OF HEADING 5602, 5603, 5903, 5906 OR 5907 - OF FABRICS\nOF HEADING 5602 OR 5603",
+        "hsn_code": "62101000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GARMENTS, MADE UP OF FABRICS OF HEADING 5602, 5603, 5903, 5906 OR 5907 - OTHER\nGARMENTS, OF THE TYPE DESCRIBED IN SUBHEADINGS 6201 11 TO 6201 19 : OUTER GARMENTS,\nOF RUBBERISED TEXTILE FABRICS",
+        "hsn_code": "62102010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GARMENTS, MADE UP OF FABRICS OF HEADING 5602, 5603, 5903, 5906 OR 5907 - OTHER\nGARMENTS, OF THE TYPE DESCRIBED IN SUBHEADINGS 6201 11 TO 6201 19 : OUTER GARMENTS,\nOF FABRICS IMPREGNATED, COATED, COVERED OR LAMINATED WITH PREPARATIONS OF\nCELLULOSE DERIVATIVES AND OTHER ARTIFICIAL PLASTIC MATERIALS",
+        "hsn_code": "62102020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GARMENTS, MADE UP OF FABRICS OF HEADING 5602, 5603, 5903, 5906 OR 5907 - OTHER\nGARMENTS, OF THE TYPE DESCRIBED IN SUBHEADINGS 6201 11 TO 6201 19 : OUTER GARMENTS,\nOF FABRICS OTHERWISE IMPREGNATED OR COATED",
+        "hsn_code": "62102030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GARMENTS, MADE UP OF FABRICS OF HEADING 5602, 5603, 5903, 5906 OR 5907 - OTHER\nGARMENTS, OF THE TYPE DESCRIBED IN SUBHEADINGS 6201 11 TO 6201 19 : OTHER",
+        "hsn_code": "62102090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GARMENTS, MADE UP OF FABRICS OF HEADING 5602, 5603, 5903, 5906 OR 5907 - OTHER\nGARMENTS, OF THE TYPE DESCRIBED IN SUBHEADINGS 6202 11 TO 6202 19 : OUTER GARMENTS,\nOF FABRICS IMPREGNATED, COATED, COVERED OR LAMINATED WITH PREPARATIONS OF\nCELLULOSE DERIVATIVES AND OTHER ARTIFICIAL PLASTIC MATERIALS",
+        "hsn_code": "62103010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GARMENTS, MADE UP OF FABRICS OF HEADING 5602, 5603, 5903, 5906 OR 5907 - OTHER\nGARMENTS, OF THE TYPE DESCRIBED IN SUBHEADINGS 6202 11 TO 6202 19 : OUTER GARMENTS,\nOF RUBBERISED TEXTILE FABRICS",
+        "hsn_code": "62103020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GARMENTS, MADE UP OF FABRICS OF HEADING 5602, 5603, 5903, 5906 OR 5907 - OTHER\nGARMENTS, OF THE TYPE DESCRIBED IN SUBHEADINGS 6202 11 TO 6202 19 : OUTER GARMENTS,\nOF FABRICS OTHERWISE IMPREGNATED",
+        "hsn_code": "62103030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GARMENTS, MADE UP OF FABRICS OF HEADING 5602, 5603, 5903, 5906 OR 5907 - OTHER\nGARMENTS, OF THE TYPE DESCRIBED IN SUBHEADINGS 6202 11 TO 6202 19 : OTHER",
+        "hsn_code": "62103090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GARMENTS, MADE UP OF FABRICS OF HEADING 5602, 5603, 5903, 5906 OR 5907 - OTHER MENS\nOR BOYS GARMENTS : BULLET PROOF JACKET, BOMB DISPOSAL JACKET AND THE LIKE",
+        "hsn_code": "62104010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GARMENTS, MADE UP OF FABRICS OF HEADING 5602, 5603, 5903, 5906 OR 5907 - OTHER MENS\nOR BOYS GARMENTS : OTHER",
+        "hsn_code": "62104090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GARMENTS, MADE UP OF FABRICS OF HEADING 5602, 5603, 5903, 5906 OR 5907 OTHER\nWOMENS OR GIRLS GARMENTS",
+        "hsn_code": "62105000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BRASSIERES, GIRDLES, CORSETS, BRACES, SUSPENDERS, GARTERS AND SIMILAR ARTICLES AND\nPARTS THEREOF, WHETHER OR NOT KNITTED OR CROCHETED - BRASSIERES",
+        "hsn_code": "62121000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BRASSIERES, GIRDLES, CORSETS, BRACES, SUSPENDERS, GARTERS AND SIMILAR ARTICLES AND\nPARTS THEREOF, WHETHER OR NOT KNITTED OR CROCHETED - GIRDLES AND PANTY-GIRDLES",
+        "hsn_code": "62122000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BRASSIERES, GIRDLES, CORSETS, BRACES, SUSPENDERS, GARTERS AND SIMILAR ARTICLES AND\nPARTS THEREOF, WHETHER OR NOT KNITTED OR CROCHETED - CORSELETTES",
+        "hsn_code": "62123000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BRASSIERES, GIRDLES, CORSETS, BRACES, SUSPENDERS, GARTERS AND SIMILAR ARTICLES AND\nPARTS THEREOF, WHETHER OR NOT KNITTED OR CROCHETED - OTHER : SUSPENDER BELTS,\nBRACES, SUSPENDER GARTERS AND THE LIKE",
+        "hsn_code": "62129010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BRASSIERES, GIRDLES, CORSETS, BRACES, SUSPENDERS, GARTERS AND SIMILAR ARTICLES AND\nPARTS THEREOF, WHETHER OR NOT KNITTED OR CROCHETED - OTHER : OTHER",
+        "hsn_code": "62129090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HANDKERCHIEFS - OF SILK OR SILK WASTE",
+        "hsn_code": "62131000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HANDKERCHIEFS - OF COTTON",
+        "hsn_code": "62132000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HANDKERCHIEFS - OTHER : OF MAN-MADE FIBRES",
+        "hsn_code": "62139010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HANDKERCHIEFS - OTHER : OF OTHER TEXTILE MATERIALS",
+        "hsn_code": "62139090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "TIES, BOW TIES AND CRAVATS - OF SILK OR SILK WASTE",
+        "hsn_code": "62151000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "TIES, BOW TIES AND CRAVATS - OF MAN-MADE FIBRES",
+        "hsn_code": "62152000",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "TIES, BOW TIES AND CRAVATS - OF OTHER TEXTILE MATERIALS : OF COTTON",
+        "hsn_code": "62159010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "TIES, BOW TIES AND CRAVATS - OF OTHER TEXTILE MATERIALS : OTHER",
+        "hsn_code": "62159090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GLOVES, MITTENS AND MITTS - GLOVES, MITTENS AND MITTS : OF COTTON",
+        "hsn_code": "62160010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GLOVES, MITTENS AND MITTS - OF HANDLOOM",
+        "hsn_code": "62160020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GLOVES, MITTENS AND MITTS - GLOVES, MITTENS AND MITTS : OTHER",
+        "hsn_code": "62160090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES; PARTS OF GARMENTS OR OF CLOTHING\nACCESSORIES, OTHER THAN THOSE OF HEADING 6212 - ACCESSORIES : FOR ARTICLES OF\nAPPAREL OF COTTON",
+        "hsn_code": "62171010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES; PARTS OF GARMENTS OR OF CLOTHING\nACCESSORIES, OTHER THAN THOSE OF HEADING 6212 - ACCESSORIES : FOR ARTICLES OF\nAPPAREL OF SYNTHETIC FIBRES",
+        "hsn_code": "62171020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES; PARTS OF GARMENTS OR OF CLOTHING\nACCESSORIES, OTHER THAN THOSE OF HEADING 6212 - ACCESSORIES : FOR ARTICLES OF\nAPPAREL OF WOOL",
+        "hsn_code": "62171030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES; PARTS OF GARMENTS OR OF CLOTHING\nACCESSORIES, OTHER THAN THOSE OF HEADING 6212 - ACCESSORIES : FOR ARTICLES OF\nAPPAREL OF SILK",
+        "hsn_code": "62171040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES; PARTS OF GARMENTS OR OF CLOTHING\nACCESSORIES, OTHER THAN THOSE OF HEADING 6212 - ACCESSORIES : FOR ARTICLES OF\nAPPAREL OF REGENERATED FIBRE",
+        "hsn_code": "62171050",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES; PARTS OF GARMENTS OR OF CLOTHING\nACCESSORIES, OTHER THAN THOSE OF HEADING 6212 - ACCESSORIES : FOR ARTICLES OF\nAPPAREL OF OTHER FIBRES",
+        "hsn_code": "62171060",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES; PARTS OF GARMENTS OR OF CLOTHING\nACCESSORIES, OTHER THAN THOSE OF HEADING 6212 - ACCESSORIES : STOCKINGS, SOCKS\nSOCKETTES AND THE LIKE OF COTTON",
+        "hsn_code": "62171070",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES; PARTS OF GARMENTS OR OF CLOTHING\nACCESSORIES, OTHER THAN THOSE OF HEADING 6212 - ACCESSORIES : OTHER",
+        "hsn_code": "62171090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES; PARTS OF GARMENTS OR OF CLOTHING\nACCESSORIES, OTHER THAN THOSE OF HEADING 6212 - PARTS : COLLAR CUFFS AND THE LIKE OF\nCOTTON",
+        "hsn_code": "62179010",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES; PARTS OF GARMENTS OR OF CLOTHING\nACCESSORIES, OTHER THAN THOSE OF HEADING 6212 - PARTS : OF SILK",
+        "hsn_code": "62179020",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES; PARTS OF GARMENTS OR OF CLOTHING\nACCESSORIES, OTHER THAN THOSE OF HEADING 6212 - PARTS : OF WOOL",
+        "hsn_code": "62179030",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES; PARTS OF GARMENTS OR OF CLOTHING\nACCESSORIES, OTHER THAN THOSE OF HEADING 6212 - PARTS : SEPARATELY PRESENTED\nREMOVABLE LININGS FOR RAINCOATS AND OTHER",
+        "hsn_code": "62179040",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES; PARTS OF GARMENTS OR OF CLOTHING\nACCESSORIES, OTHER THAN THOSE OF HEADING 6212 - PARTS : OTHER",
+        "hsn_code": "62179090",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEN’S OR BOYS’ OVERCOATS, CAR-COATS, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS), WIND-CHEATERS, WIND JACKETS AND SIMILAR ARTICLES OTHER THAN THOSE OF HEADING 6203",
+        "hsn_code": "6201",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMEN’S OR GIRLS’ OVERCOATS, CAR-COATS, CAPES, CLOAKS, ANORAKS (INCLUDING SKI-JACKETS), WIND-CHEATERS, WIND-JACKETS AND SIMILAR ARTICLES, OTHER THAN THOSE OF HEADING 6204",
+        "hsn_code": "6202",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEN’S OR BOYS’ SUITS, ENSEMBLES, JACKETS, BLAZERS, TROUSERS BIB AND BRACE OVERALLS, BREECHES AND SHORTS (OTHER THAN SWIMWEAR)",
+        "hsn_code": "6203",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Suits, ensembles, jackets, dresses, skirts, divided skirts, trousers, bib and brace overalls, breeches and shorts (other than swimwear); women’s or girls’ (not knitted or crocheted)",
+        "hsn_code": "6204",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEN’S OR BOYS’ SHIRTS",
+        "hsn_code": "6205",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMEN’S OR GIRLS’ BLOUSES, SHIRTS AND SHIRT-BLOUSES",
+        "hsn_code": "6206",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "MEN’S OR BOYS’ SINGLETS AND OTHER VESTS, UNDERPANTS, BRIEFS, NIGHTSHIRTS, PYJAMAS, BATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES",
+        "hsn_code": "6207",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "WOMEN’S OR GIRLS’ SINGLETS AND OTHER VESTS, SLIPS, PETTICOATS, BRIEFS, PANTIES, NIGHTDRESSES, PYJAMAS, NEGLIGES, BATHROBES, DRESSING GOWNS AND SIMILAR ARTICLES",
+        "hsn_code": "6208",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BABIES’ GARMENTS AND CLOTHING ACCESSORIES",
+        "hsn_code": "6209",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GARMENTS, MADE UP OF FABRICS OF HEADING 5602, 5603, 5903, 5906 OR 5907",
+        "hsn_code": "6210",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "BRASSIERES, GIRDLES, CORSETS, BRACES, SUSPENDERS, GARTERS AND SIMILAR ARTICLES AND PARTS THEREOF, WHETHER OR NOT KNITTED OR CROCHETED",
+        "hsn_code": "6212",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "HANDKERCHIEFS",
+        "hsn_code": "6213",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "TIES, BOW TIES AND CRAVATS",
+        "hsn_code": "6215",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "GLOVES, MITTENS AND MITTS",
+        "hsn_code": "6216",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "OTHER MADE UP CLOTHING ACCESSORIES; PARTS OF GARMENTS OR OF CLOTHING ACCESSORIES, OTHER THAN THOSE OF HEADING 6212",
+        "hsn_code": "6217",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    },
+    {
+        "description": "Footwear / Headgear",
+        "hsn_code": "64-67",
+        "gst_rates": [
+            {
+                "minimum_taxable_value": 0,
+                "tax_rate": 0
+            }
+        ]
+    }
+]
\ No newline at end of file
diff --git a/erpnext/regional/doctype/datev_settings/__init__.py b/erpnext/regional/doctype/hsn_tax_rate/__init__.py
similarity index 100%
rename from erpnext/regional/doctype/datev_settings/__init__.py
rename to erpnext/regional/doctype/hsn_tax_rate/__init__.py
diff --git a/erpnext/regional/doctype/hsn_tax_rate/hsn_tax_rate.json b/erpnext/regional/doctype/hsn_tax_rate/hsn_tax_rate.json
new file mode 100644
index 0000000..68885cd
--- /dev/null
+++ b/erpnext/regional/doctype/hsn_tax_rate/hsn_tax_rate.json
@@ -0,0 +1,39 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2022-05-11 13:32:42.534779",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "minimum_taxable_value",
+  "tax_rate"
+ ],
+ "fields": [
+  {
+   "columns": 2,
+   "fieldname": "minimum_taxable_value",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Minimum Taxable Value"
+  },
+  {
+   "columns": 2,
+   "fieldname": "tax_rate",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Tax Rate"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2022-05-15 15:37:56.152470",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "HSN Tax Rate",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/hsn_tax_rate/hsn_tax_rate.py b/erpnext/regional/doctype/hsn_tax_rate/hsn_tax_rate.py
new file mode 100644
index 0000000..d8dc7fe
--- /dev/null
+++ b/erpnext/regional/doctype/hsn_tax_rate/hsn_tax_rate.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class HSNTaxRate(Document):
+	pass
diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py
index 97b8488..34701c1 100644
--- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py
+++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py
@@ -27,11 +27,10 @@
 			self.name = "Import Invoice on " + format_datetime(self.creation)
 
 	def import_xml_data(self):
-		zip_file = frappe.get_doc("File", {
-			"file_url": self.zip_file,
-			"attached_to_doctype": self.doctype,
-			"attached_to_name": self.name
-		})
+		zip_file = frappe.get_doc(
+			"File",
+			{"file_url": self.zip_file, "attached_to_doctype": self.doctype, "attached_to_name": self.name},
+		)
 
 		self.publish("File Import", _("Processing XML Files"), 1, 3)
 
@@ -65,10 +64,10 @@
 				"bill_no": line.Numero.text,
 				"total_discount": 0,
 				"items": [],
-				"buying_price_list": self.default_buying_price_list
+				"buying_price_list": self.default_buying_price_list,
 			}
 
-			if not invoices_args.get("bill_no", ''):
+			if not invoices_args.get("bill_no", ""):
 				frappe.throw(_("Numero has not set in the XML file"))
 
 			supp_dict = get_supplier_details(file_content)
@@ -84,15 +83,23 @@
 			self.file_count += 1
 			if pi_name:
 				self.purchase_invoices_count += 1
-				file_save = save_file(file_name, encoded_content, "Purchase Invoice",
-					pi_name, folder=None, decode=False, is_private=0, df=None)
+				file_save = save_file(
+					file_name,
+					encoded_content,
+					"Purchase Invoice",
+					pi_name,
+					folder=None,
+					decode=False,
+					is_private=0,
+					df=None,
+				)
 
 	def prepare_items_for_invoice(self, file_content, invoices_args):
 		qty = 1
-		rate, tax_rate = [0 ,0]
+		rate, tax_rate = [0, 0]
 		uom = self.default_uom
 
-		#read file for item information
+		# read file for item information
 		for line in file_content.find_all("DettaglioLinee"):
 			if line.find("PrezzoUnitario") and line.find("PrezzoTotale"):
 				rate = flt(line.PrezzoUnitario.text) or 0
@@ -103,30 +110,34 @@
 					if line.find("UnitaMisura"):
 						uom = create_uom(line.UnitaMisura.text)
 
-				if (rate < 0 and line_total < 0):
+				if rate < 0 and line_total < 0:
 					qty *= -1
 					invoices_args["return_invoice"] = 1
 
 				if line.find("AliquotaIVA"):
 					tax_rate = flt(line.AliquotaIVA.text)
 
-				line_str = re.sub('[^A-Za-z0-9]+', '-', line.Descrizione.text)
+				line_str = re.sub("[^A-Za-z0-9]+", "-", line.Descrizione.text)
 				item_name = line_str[0:140]
 
-				invoices_args['items'].append({
-					"item_code": self.item_code,
-					"item_name": item_name,
-					"description": line_str,
-					"qty": qty,
-					"uom": uom,
-					"rate": abs(rate),
-					"conversion_factor": 1.0,
-					"tax_rate": tax_rate
-				})
+				invoices_args["items"].append(
+					{
+						"item_code": self.item_code,
+						"item_name": item_name,
+						"description": line_str,
+						"qty": qty,
+						"uom": uom,
+						"rate": abs(rate),
+						"conversion_factor": 1.0,
+						"tax_rate": tax_rate,
+					}
+				)
 
 				for disc_line in line.find_all("ScontoMaggiorazione"):
 					if disc_line.find("Percentuale"):
-						invoices_args["total_discount"] += flt((flt(disc_line.Percentuale.text) / 100) * (rate * qty))
+						invoices_args["total_discount"] += flt(
+							(flt(disc_line.Percentuale.text) / 100) * (rate * qty)
+						)
 
 	@frappe.whitelist()
 	def process_file_data(self):
@@ -134,10 +145,13 @@
 		frappe.enqueue_doc(self.doctype, self.name, "import_xml_data", queue="long", timeout=3600)
 
 	def publish(self, title, message, count, total):
-		frappe.publish_realtime("import_invoice_update", {"title": title, "message": message, "count": count, "total": total})
+		frappe.publish_realtime(
+			"import_invoice_update", {"title": title, "message": message, "count": count, "total": total}
+		)
+
 
 def get_file_content(file_name, zip_file_object):
-	content = ''
+	content = ""
 	encoded_content = zip_file_object.read(file_name)
 
 	try:
@@ -146,117 +160,126 @@
 		try:
 			content = encoded_content.decode("utf-16")
 		except UnicodeDecodeError as e:
-			frappe.log_error(message=e, title="UTF-16 encoding error for File Name: " + file_name)
+			frappe.log_error("UTF-16 encoding error for File Name: " + file_name)
 
 	return content
 
+
 def get_supplier_details(file_content):
 	supplier_info = {}
 	for line in file_content.find_all("CedentePrestatore"):
-		supplier_info['tax_id'] = line.DatiAnagrafici.IdPaese.text + line.DatiAnagrafici.IdCodice.text
+		supplier_info["tax_id"] = line.DatiAnagrafici.IdPaese.text + line.DatiAnagrafici.IdCodice.text
 		if line.find("CodiceFiscale"):
-			supplier_info['fiscal_code'] = line.DatiAnagrafici.CodiceFiscale.text
+			supplier_info["fiscal_code"] = line.DatiAnagrafici.CodiceFiscale.text
 
 		if line.find("RegimeFiscale"):
-			supplier_info['fiscal_regime'] = line.DatiAnagrafici.RegimeFiscale.text
+			supplier_info["fiscal_regime"] = line.DatiAnagrafici.RegimeFiscale.text
 
 		if line.find("Denominazione"):
-			supplier_info['supplier'] = line.DatiAnagrafici.Anagrafica.Denominazione.text
+			supplier_info["supplier"] = line.DatiAnagrafici.Anagrafica.Denominazione.text
 
 		if line.find("Nome"):
-			supplier_info['supplier'] = (line.DatiAnagrafici.Anagrafica.Nome.text
-				+ " " + line.DatiAnagrafici.Anagrafica.Cognome.text)
+			supplier_info["supplier"] = (
+				line.DatiAnagrafici.Anagrafica.Nome.text + " " + line.DatiAnagrafici.Anagrafica.Cognome.text
+			)
 
-		supplier_info['address_line1'] = line.Sede.Indirizzo.text
-		supplier_info['city'] = line.Sede.Comune.text
+		supplier_info["address_line1"] = line.Sede.Indirizzo.text
+		supplier_info["city"] = line.Sede.Comune.text
 		if line.find("Provincia"):
-			supplier_info['province'] = line.Sede.Provincia.text
+			supplier_info["province"] = line.Sede.Provincia.text
 
-		supplier_info['pin_code'] = line.Sede.CAP.text
-		supplier_info['country'] = get_country(line.Sede.Nazione.text)
+		supplier_info["pin_code"] = line.Sede.CAP.text
+		supplier_info["country"] = get_country(line.Sede.Nazione.text)
 
 		return supplier_info
 
+
 def get_taxes_from_file(file_content, tax_account):
 	taxes = []
-	#read file for taxes information
+	# read file for taxes information
 	for line in file_content.find_all("DatiRiepilogo"):
 		if line.find("AliquotaIVA"):
 			if line.find("EsigibilitaIVA"):
 				descr = line.EsigibilitaIVA.text
 			else:
 				descr = "None"
-			taxes.append({
-				"charge_type": "Actual",
-				"account_head": tax_account,
-				"tax_rate": flt(line.AliquotaIVA.text) or 0,
-				"description": descr,
-				"tax_amount": flt(line.Imposta.text) if len(line.find("Imposta"))!=0 else 0
-			})
+			taxes.append(
+				{
+					"charge_type": "Actual",
+					"account_head": tax_account,
+					"tax_rate": flt(line.AliquotaIVA.text) or 0,
+					"description": descr,
+					"tax_amount": flt(line.Imposta.text) if len(line.find("Imposta")) != 0 else 0,
+				}
+			)
 
 	return taxes
 
+
 def get_payment_terms_from_file(file_content):
 	terms = []
-	#Get mode of payment dict from setup
-	mop_options = frappe.get_meta('Mode of Payment').fields[4].options
-	mop_str = re.sub('\n', ',', mop_options)
+	# Get mode of payment dict from setup
+	mop_options = frappe.get_meta("Mode of Payment").fields[4].options
+	mop_str = re.sub("\n", ",", mop_options)
 	mop_dict = dict(item.split("-") for item in mop_str.split(","))
-	#read file for payment information
+	# read file for payment information
 	for line in file_content.find_all("DettaglioPagamento"):
-		mop_code = line.ModalitaPagamento.text + '-' + mop_dict.get(line.ModalitaPagamento.text)
+		mop_code = line.ModalitaPagamento.text + "-" + mop_dict.get(line.ModalitaPagamento.text)
 		if line.find("DataScadenzaPagamento"):
 			due_date = dateutil.parser.parse(line.DataScadenzaPagamento.text).strftime("%Y-%m-%d")
 		else:
 			due_date = today()
-		terms.append({
-			"mode_of_payment_code": mop_code,
-			"bank_account_iban": line.IBAN.text if line.find("IBAN") else "",
-			"due_date": due_date,
-			"payment_amount": line.ImportoPagamento.text
-		})
+		terms.append(
+			{
+				"mode_of_payment_code": mop_code,
+				"bank_account_iban": line.IBAN.text if line.find("IBAN") else "",
+				"due_date": due_date,
+				"payment_amount": line.ImportoPagamento.text,
+			}
+		)
 
 	return terms
 
+
 def get_destination_code_from_file(file_content):
-	destination_code = ''
+	destination_code = ""
 	for line in file_content.find_all("DatiTrasmissione"):
 		destination_code = line.CodiceDestinatario.text
 
 	return destination_code
 
+
 def create_supplier(supplier_group, args):
 	args = frappe._dict(args)
 
-	existing_supplier_name = frappe.db.get_value("Supplier",
-				filters={"tax_id": args.tax_id}, fieldname="name")
+	existing_supplier_name = frappe.db.get_value(
+		"Supplier", filters={"tax_id": args.tax_id}, fieldname="name"
+	)
 	if existing_supplier_name:
 		pass
 	else:
-		existing_supplier_name = frappe.db.get_value("Supplier",
-				filters={"name": args.supplier}, fieldname="name")
+		existing_supplier_name = frappe.db.get_value(
+			"Supplier", filters={"name": args.supplier}, fieldname="name"
+		)
 
 	if existing_supplier_name:
 		filters = [
-				["Dynamic Link", "link_doctype", "=", "Supplier"],
-				["Dynamic Link", "link_name", "=", args.existing_supplier_name],
-				["Dynamic Link", "parenttype", "=", "Contact"]
-			]
+			["Dynamic Link", "link_doctype", "=", "Supplier"],
+			["Dynamic Link", "link_name", "=", args.existing_supplier_name],
+			["Dynamic Link", "parenttype", "=", "Contact"],
+		]
 
 		if not frappe.get_list("Contact", filters):
 			new_contact = frappe.new_doc("Contact")
 			new_contact.first_name = args.supplier[:30]
-			new_contact.append('links', {
-				"link_doctype": "Supplier",
-				"link_name": existing_supplier_name
-			})
+			new_contact.append("links", {"link_doctype": "Supplier", "link_name": existing_supplier_name})
 			new_contact.insert(ignore_mandatory=True)
 
 		return existing_supplier_name
 	else:
 
 		new_supplier = frappe.new_doc("Supplier")
-		new_supplier.supplier_name = re.sub('&amp', '&', args.supplier)
+		new_supplier.supplier_name = re.sub("&amp", "&", args.supplier)
 		new_supplier.supplier_group = supplier_group
 		new_supplier.tax_id = args.tax_id
 		new_supplier.fiscal_code = args.fiscal_code
@@ -265,23 +288,21 @@
 
 		new_contact = frappe.new_doc("Contact")
 		new_contact.first_name = args.supplier[:30]
-		new_contact.append('links', {
-			"link_doctype": "Supplier",
-			"link_name": new_supplier.name
-		})
+		new_contact.append("links", {"link_doctype": "Supplier", "link_name": new_supplier.name})
 
 		new_contact.insert(ignore_mandatory=True)
 
 		return new_supplier.name
 
+
 def create_address(supplier_name, args):
 	args = frappe._dict(args)
 
 	filters = [
-			["Dynamic Link", "link_doctype", "=", "Supplier"],
-			["Dynamic Link", "link_name", "=", supplier_name],
-			["Dynamic Link", "parenttype", "=", "Address"]
-		]
+		["Dynamic Link", "link_doctype", "=", "Supplier"],
+		["Dynamic Link", "link_name", "=", supplier_name],
+		["Dynamic Link", "parenttype", "=", "Address"],
+	]
 
 	existing_address = frappe.get_list("Address", filters)
 
@@ -300,50 +321,52 @@
 
 		for address in existing_address:
 			address_doc = frappe.get_doc("Address", address["name"])
-			if (address_doc.address_line1 == new_address_doc.address_line1 and
-				address_doc.pincode == new_address_doc.pincode):
+			if (
+				address_doc.address_line1 == new_address_doc.address_line1
+				and address_doc.pincode == new_address_doc.pincode
+			):
 				return address
 
-		new_address_doc.append("links", {
-			"link_doctype": "Supplier",
-			"link_name": supplier_name
-		})
+		new_address_doc.append("links", {"link_doctype": "Supplier", "link_name": supplier_name})
 		new_address_doc.address_type = "Billing"
 		new_address_doc.insert(ignore_mandatory=True)
 		return new_address_doc.name
 	else:
 		return None
 
+
 def create_purchase_invoice(supplier_name, file_name, args, name):
 	args = frappe._dict(args)
-	pi = frappe.get_doc({
-		"doctype": "Purchase Invoice",
-		"company": args.company,
-		"currency": erpnext.get_company_currency(args.company),
-		"naming_series": args.naming_series,
-		"supplier": supplier_name,
-		"is_return": args.is_return,
-		"posting_date": today(),
-		"bill_no": args.bill_no,
-		"buying_price_list": args.buying_price_list,
-		"bill_date": args.bill_date,
-		"destination_code": args.destination_code,
-		"document_type": args.document_type,
-		"disable_rounded_total": 1,
-		"items": args["items"],
-		"taxes": args["taxes"]
-	})
+	pi = frappe.get_doc(
+		{
+			"doctype": "Purchase Invoice",
+			"company": args.company,
+			"currency": erpnext.get_company_currency(args.company),
+			"naming_series": args.naming_series,
+			"supplier": supplier_name,
+			"is_return": args.is_return,
+			"posting_date": today(),
+			"bill_no": args.bill_no,
+			"buying_price_list": args.buying_price_list,
+			"bill_date": args.bill_date,
+			"destination_code": args.destination_code,
+			"document_type": args.document_type,
+			"disable_rounded_total": 1,
+			"items": args["items"],
+			"taxes": args["taxes"],
+		}
+	)
 
 	try:
 		pi.set_missing_values()
 		pi.insert(ignore_mandatory=True)
 
-		#if discount exists in file, apply any discount on grand total
+		# if discount exists in file, apply any discount on grand total
 		if args.total_discount > 0:
 			pi.apply_discount_on = "Grand Total"
 			pi.discount_amount = args.total_discount
 			pi.save()
-		#adjust payment amount to match with grand total calculated
+		# adjust payment amount to match with grand total calculated
 		calc_total = 0
 		adj = 0
 		for term in args.terms:
@@ -352,31 +375,35 @@
 			adj = calc_total - flt(pi.grand_total)
 		pi.payment_schedule = []
 		for term in args.terms:
-			pi.append('payment_schedule',{"mode_of_payment_code": term["mode_of_payment_code"],
-			"bank_account_iban": term["bank_account_iban"],
-			"due_date": term["due_date"],
-			"payment_amount": flt(term["payment_amount"]) - adj })
+			pi.append(
+				"payment_schedule",
+				{
+					"mode_of_payment_code": term["mode_of_payment_code"],
+					"bank_account_iban": term["bank_account_iban"],
+					"due_date": term["due_date"],
+					"payment_amount": flt(term["payment_amount"]) - adj,
+				},
+			)
 			adj = 0
 		pi.imported_grand_total = calc_total
 		pi.save()
 		return pi.name
 	except Exception as e:
 		frappe.db.set_value("Import Supplier Invoice", name, "status", "Error")
-		frappe.log_error(message=e,
-			title="Create Purchase Invoice: " + args.get("bill_no") + "File Name: " + file_name)
+		pi.log_error("Unable to create Puchase Invoice")
 		return None
 
+
 def get_country(code):
-	existing_country_name = frappe.db.get_value("Country",
-			filters={"code": code}, fieldname="name")
+	existing_country_name = frappe.db.get_value("Country", filters={"code": code}, fieldname="name")
 	if existing_country_name:
 		return existing_country_name
 	else:
 		frappe.throw(_("Country Code in File does not match with country code set up in the system"))
 
+
 def create_uom(uom):
-	existing_uom = frappe.db.get_value("UOM",
-			filters={"uom_name": uom}, fieldname="uom_name")
+	existing_uom = frappe.db.get_value("UOM", filters={"uom_name": uom}, fieldname="uom_name")
 	if existing_uom:
 		return existing_uom
 	else:
diff --git a/erpnext/regional/france/setup.py b/erpnext/regional/france/setup.py
index 5d48203..da772d6 100644
--- a/erpnext/regional/france/setup.py
+++ b/erpnext/regional/france/setup.py
@@ -5,28 +5,26 @@
 import frappe
 from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 
+
 def setup(company=None, patch=True):
 	make_custom_fields()
 	add_custom_roles_for_reports()
 
+
 def make_custom_fields():
 	custom_fields = {
-		'Company': [
-			dict(fieldname='siren_number', label='SIREN Number',
-			fieldtype='Data', insert_after='website')
+		"Company": [
+			dict(fieldname="siren_number", label="SIREN Number", fieldtype="Data", insert_after="website")
 		]
 	}
 
 	create_custom_fields(custom_fields)
 
-def add_custom_roles_for_reports():
-	report_name = 'Fichier des Ecritures Comptables [FEC]'
 
-	if not frappe.db.get_value('Custom Role', dict(report=report_name)):
-		frappe.get_doc(dict(
-			doctype='Custom Role',
-			report=report_name,
-			roles= [
-				dict(role='Accounts Manager')
-			]
-		)).insert()
+def add_custom_roles_for_reports():
+	report_name = "Fichier des Ecritures Comptables [FEC]"
+
+	if not frappe.db.get_value("Custom Role", dict(report=report_name)):
+		frappe.get_doc(
+			dict(doctype="Custom Role", report=report_name, roles=[dict(role="Accounts Manager")])
+		).insert()
diff --git a/erpnext/regional/france/utils.py b/erpnext/regional/france/utils.py
index 8413165..65dfd2d 100644
--- a/erpnext/regional/france/utils.py
+++ b/erpnext/regional/france/utils.py
@@ -2,8 +2,7 @@
 # For license information, please see license.txt
 
 
-
 # don't remove this function it is used in tests
 def test_method():
-	'''test function'''
-	return 'overridden'
+	"""test function"""
+	return "overridden"
diff --git a/erpnext/regional/germany/__init__.py b/erpnext/regional/germany/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/germany/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/germany/setup.py b/erpnext/regional/germany/setup.py
deleted file mode 100644
index a68cecc..0000000
--- a/erpnext/regional/germany/setup.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import frappe
-from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
-
-
-def setup(company=None, patch=True):
-	make_custom_fields()
-	add_custom_roles_for_reports()
-
-
-def make_custom_fields():
-	custom_fields = {
-		'Party Account': [
-			dict(fieldname='debtor_creditor_number', label='Debtor/Creditor Number',
-			fieldtype='Data', insert_after='account', translatable=0)
-		]
-	}
-
-	create_custom_fields(custom_fields)
-
-
-def add_custom_roles_for_reports():
-	"""Add Access Control to UAE VAT 201."""
-	if not frappe.db.get_value('Custom Role', dict(report='DATEV')):
-		frappe.get_doc(dict(
-			doctype='Custom Role',
-			report='DATEV',
-			roles= [
-				dict(role='Accounts User'),
-				dict(role='Accounts Manager')
-			]
-		)).insert()
diff --git a/erpnext/regional/germany/utils/__init__.py b/erpnext/regional/germany/utils/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/germany/utils/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/germany/utils/datev/__init__.py b/erpnext/regional/germany/utils/datev/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/germany/utils/datev/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/germany/utils/datev/datev_constants.py b/erpnext/regional/germany/utils/datev/datev_constants.py
deleted file mode 100644
index 8f2dc2d..0000000
--- a/erpnext/regional/germany/utils/datev/datev_constants.py
+++ /dev/null
@@ -1,496 +0,0 @@
-"""Constants used in datev.py."""
-
-TRANSACTION_COLUMNS = [
-	# All possible columns must tbe listed here, because DATEV requires them to
-	# be present in the CSV.
-	# ---
-	# Umsatz
-	"Umsatz (ohne Soll/Haben-Kz)",
-	"Soll/Haben-Kennzeichen",
-	"WKZ Umsatz",
-	"Kurs",
-	"Basis-Umsatz",
-	"WKZ Basis-Umsatz",
-	# Konto/Gegenkonto
-	"Konto",
-	"Gegenkonto (ohne BU-Schlüssel)",
-	"BU-Schlüssel",
-	# Datum
-	"Belegdatum",
-	# Rechnungs- / Belegnummer
-	"Belegfeld 1",
-	# z.B. Fälligkeitsdatum Format: TTMMJJ
-	"Belegfeld 2",
-	# Skonto-Betrag / -Abzug (Der Wert 0 ist unzulässig)
-	"Skonto",
-	# Beschreibung des Buchungssatzes
-	"Buchungstext",
-	# Mahn- / Zahl-Sperre (1 = Postensperre)
-	"Postensperre",
-	"Diverse Adressnummer",
-	"Geschäftspartnerbank",
-	"Sachverhalt",
-	# Keine Mahnzinsen
-	"Zinssperre",
-	# Link auf den Buchungsbeleg (Programmkürzel + GUID)
-	"Beleglink",
-	# Beleginfo
-	"Beleginfo - Art 1",
-	"Beleginfo - Inhalt 1",
-	"Beleginfo - Art 2",
-	"Beleginfo - Inhalt 2",
-	"Beleginfo - Art 3",
-	"Beleginfo - Inhalt 3",
-	"Beleginfo - Art 4",
-	"Beleginfo - Inhalt 4",
-	"Beleginfo - Art 5",
-	"Beleginfo - Inhalt 5",
-	"Beleginfo - Art 6",
-	"Beleginfo - Inhalt 6",
-	"Beleginfo - Art 7",
-	"Beleginfo - Inhalt 7",
-	"Beleginfo - Art 8",
-	"Beleginfo - Inhalt 8",
-	# Zuordnung des Geschäftsvorfalls für die Kostenrechnung
-	"KOST1 - Kostenstelle",
-	"KOST2 - Kostenstelle",
-	"KOST-Menge",
-	# USt-ID-Nummer (Beispiel: DE133546770)
-	"EU-Mitgliedstaat u. USt-IdNr.",
-	# Der im EU-Bestimmungsland gültige Steuersatz
-	"EU-Steuersatz",
-	# I = Ist-Versteuerung,
-	# K = keine Umsatzsteuerrechnung
-	# P = Pauschalierung (z. B. für Land- und Forstwirtschaft),
-	# S = Soll-Versteuerung
-	"Abw. Versteuerungsart",
-	# Sachverhalte gem. § 13b Abs. 1 Satz 1 Nrn. 1.-5. UStG
-	"Sachverhalt L+L",
-	# Steuersatz / Funktion zum L+L-Sachverhalt (Beispiel: Wert 190 für 19%)
-	"Funktionsergänzung L+L",
-	# Bei Verwendung des BU-Schlüssels 49 für „andere Steuersätze“ muss der
-	# steuerliche Sachverhalt mitgegeben werden
-	"BU 49 Hauptfunktionstyp",
-	"BU 49 Hauptfunktionsnummer",
-	"BU 49 Funktionsergänzung",
-	# Zusatzinformationen, besitzen den Charakter eines Notizzettels und können
-	# frei erfasst werden.
-	"Zusatzinformation - Art 1",
-	"Zusatzinformation - Inhalt 1",
-	"Zusatzinformation - Art 2",
-	"Zusatzinformation - Inhalt 2",
-	"Zusatzinformation - Art 3",
-	"Zusatzinformation - Inhalt 3",
-	"Zusatzinformation - Art 4",
-	"Zusatzinformation - Inhalt 4",
-	"Zusatzinformation - Art 5",
-	"Zusatzinformation - Inhalt 5",
-	"Zusatzinformation - Art 6",
-	"Zusatzinformation - Inhalt 6",
-	"Zusatzinformation - Art 7",
-	"Zusatzinformation - Inhalt 7",
-	"Zusatzinformation - Art 8",
-	"Zusatzinformation - Inhalt 8",
-	"Zusatzinformation - Art 9",
-	"Zusatzinformation - Inhalt 9",
-	"Zusatzinformation - Art 10",
-	"Zusatzinformation - Inhalt 10",
-	"Zusatzinformation - Art 11",
-	"Zusatzinformation - Inhalt 11",
-	"Zusatzinformation - Art 12",
-	"Zusatzinformation - Inhalt 12",
-	"Zusatzinformation - Art 13",
-	"Zusatzinformation - Inhalt 13",
-	"Zusatzinformation - Art 14",
-	"Zusatzinformation - Inhalt 14",
-	"Zusatzinformation - Art 15",
-	"Zusatzinformation - Inhalt 15",
-	"Zusatzinformation - Art 16",
-	"Zusatzinformation - Inhalt 16",
-	"Zusatzinformation - Art 17",
-	"Zusatzinformation - Inhalt 17",
-	"Zusatzinformation - Art 18",
-	"Zusatzinformation - Inhalt 18",
-	"Zusatzinformation - Art 19",
-	"Zusatzinformation - Inhalt 19",
-	"Zusatzinformation - Art 20",
-	"Zusatzinformation - Inhalt 20",
-	# Wirkt sich nur bei Sachverhalt mit SKR 14 Land- und Forstwirtschaft aus,
-	# für andere SKR werden die Felder beim Import / Export überlesen bzw.
-	# leer exportiert.
-	"Stück",
-	"Gewicht",
-	# 1 = Lastschrift
-	# 2 = Mahnung
-	# 3 = Zahlung
-	"Zahlweise",
-	"Forderungsart",
-	# JJJJ
-	"Veranlagungsjahr",
-	# TTMMJJJJ
-	"Zugeordnete Fälligkeit",
-	# 1 = Einkauf von Waren
-	# 2 = Erwerb von Roh-Hilfs- und Betriebsstoffen
-	"Skontotyp",
-	# Allgemeine Bezeichnung, des Auftrags / Projekts.
-	"Auftragsnummer",
-	# AA = Angeforderte Anzahlung / Abschlagsrechnung
-	# AG = Erhaltene Anzahlung (Geldeingang)
-	# AV = Erhaltene Anzahlung (Verbindlichkeit)
-	# SR = Schlussrechnung
-	# SU = Schlussrechnung (Umbuchung)
-	# SG = Schlussrechnung (Geldeingang)
-	# SO = Sonstige
-	"Buchungstyp",
-	"USt-Schlüssel (Anzahlungen)",
-	"EU-Mitgliedstaat (Anzahlungen)",
-	"Sachverhalt L+L (Anzahlungen)",
-	"EU-Steuersatz (Anzahlungen)",
-	"Erlöskonto (Anzahlungen)",
-	# Wird beim Import durch SV (Stapelverarbeitung) ersetzt.
-	"Herkunft-Kz",
-	# Wird von DATEV verwendet.
-	"Leerfeld",
-	# Format TTMMJJJJ
-	"KOST-Datum",
-	# Vom Zahlungsempfänger individuell vergebenes Kennzeichen eines Mandats
-	# (z.B. Rechnungs- oder Kundennummer).
-	"SEPA-Mandatsreferenz",
-	# 1 = Skontosperre
-	# 0 = Keine Skontosperre
-	"Skontosperre",
-	# Gesellschafter und Sonderbilanzsachverhalt
-	"Gesellschaftername",
-	# Amtliche Nummer aus der Feststellungserklärung
-	"Beteiligtennummer",
-	"Identifikationsnummer",
-	"Zeichnernummer",
-	# Format TTMMJJJJ
-	"Postensperre bis",
-	# Gesellschafter und Sonderbilanzsachverhalt
-	"Bezeichnung SoBil-Sachverhalt",
-	"Kennzeichen SoBil-Buchung",
-	# 0 = keine Festschreibung
-	# 1 = Festschreibung
-	"Festschreibung",
-	# Format TTMMJJJJ
-	"Leistungsdatum",
-	# Format TTMMJJJJ
-	"Datum Zuord. Steuerperiode",
-	# OPOS-Informationen, Format TTMMJJJJ
-	"Fälligkeit",
-	# G oder 1 = Generalumkehr
-	# 0 = keine Generalumkehr
-	"Generalumkehr (GU)",
-	# Steuersatz für Steuerschlüssel
-	"Steuersatz",
-	# Beispiel: DE für Deutschland
-	"Land"
-]
-
-DEBTOR_CREDITOR_COLUMNS = [
-	# All possible columns must tbe listed here, because DATEV requires them to
-	# be present in the CSV.
-	# Columns "Leerfeld" have been replaced with "Leerfeld #" to not confuse pandas
-	# ---
-	"Konto",
-	"Name (Adressatentyp Unternehmen)",
-	"Unternehmensgegenstand",
-	"Name (Adressatentyp natürl. Person)",
-	"Vorname (Adressatentyp natürl. Person)",
-	"Name (Adressatentyp keine Angabe)",
-	"Adressatentyp",
-	"Kurzbezeichnung",
-	"EU-Land",
-	"EU-USt-IdNr.",
-	"Anrede",
-	"Titel/Akad. Grad",
-	"Adelstitel",
-	"Namensvorsatz",
-	"Adressart",
-	"Straße",
-	"Postfach",
-	"Postleitzahl",
-	"Ort",
-	"Land",
-	"Versandzusatz",
-	"Adresszusatz",
-	"Abweichende Anrede",
-	"Abw. Zustellbezeichnung 1",
-	"Abw. Zustellbezeichnung 2",
-	"Kennz. Korrespondenzadresse",
-	"Adresse gültig von",
-	"Adresse gültig bis",
-	"Telefon",
-	"Bemerkung (Telefon)",
-	"Telefon Geschäftsleitung",
-	"Bemerkung (Telefon GL)",
-	"E-Mail",
-	"Bemerkung (E-Mail)",
-	"Internet",
-	"Bemerkung (Internet)",
-	"Fax",
-	"Bemerkung (Fax)",
-	"Sonstige",
-	"Bemerkung (Sonstige)",
-	"Bankleitzahl 1",
-	"Bankbezeichnung 1",
-	"Bankkonto-Nummer 1",
-	"Länderkennzeichen 1",
-	"IBAN 1",
-	"Leerfeld 1",
-	"SWIFT-Code 1",
-	"Abw. Kontoinhaber 1",
-	"Kennz. Haupt-Bankverb. 1",
-	"Bankverb. 1 Gültig von",
-	"Bankverb. 1 Gültig bis",
-	"Bankleitzahl 2",
-	"Bankbezeichnung 2",
-	"Bankkonto-Nummer 2",
-	"Länderkennzeichen 2",
-	"IBAN 2",
-	"Leerfeld 2",
-	"SWIFT-Code 2",
-	"Abw. Kontoinhaber 2",
-	"Kennz. Haupt-Bankverb. 2",
-	"Bankverb. 2 gültig von",
-	"Bankverb. 2 gültig bis",
-	"Bankleitzahl 3",
-	"Bankbezeichnung 3",
-	"Bankkonto-Nummer 3",
-	"Länderkennzeichen 3",
-	"IBAN 3",
-	"Leerfeld 3",
-	"SWIFT-Code 3",
-	"Abw. Kontoinhaber 3",
-	"Kennz. Haupt-Bankverb. 3",
-	"Bankverb. 3 gültig von",
-	"Bankverb. 3 gültig bis",
-	"Bankleitzahl 4",
-	"Bankbezeichnung 4",
-	"Bankkonto-Nummer 4",
-	"Länderkennzeichen 4",
-	"IBAN 4",
-	"Leerfeld 4",
-	"SWIFT-Code 4",
-	"Abw. Kontoinhaber 4",
-	"Kennz. Haupt-Bankverb. 4",
-	"Bankverb. 4 Gültig von",
-	"Bankverb. 4 Gültig bis",
-	"Bankleitzahl 5",
-	"Bankbezeichnung 5",
-	"Bankkonto-Nummer 5",
-	"Länderkennzeichen 5",
-	"IBAN 5",
-	"Leerfeld 5",
-	"SWIFT-Code 5",
-	"Abw. Kontoinhaber 5",
-	"Kennz. Haupt-Bankverb. 5",
-	"Bankverb. 5 gültig von",
-	"Bankverb. 5 gültig bis",
-	"Leerfeld 6",
-	"Briefanrede",
-	"Grußformel",
-	"Kundennummer",
-	"Steuernummer",
-	"Sprache",
-	"Ansprechpartner",
-	"Vertreter",
-	"Sachbearbeiter",
-	"Diverse-Konto",
-	"Ausgabeziel",
-	"Währungssteuerung",
-	"Kreditlimit (Debitor)",
-	"Zahlungsbedingung",
-	"Fälligkeit in Tagen (Debitor)",
-	"Skonto in Prozent (Debitor)",
-	"Kreditoren-Ziel 1 (Tage)",
-	"Kreditoren-Skonto 1 (%)",
-	"Kreditoren-Ziel 2 (Tage)",
-	"Kreditoren-Skonto 2 (%)",
-	"Kreditoren-Ziel 3 Brutto (Tage)",
-	"Kreditoren-Ziel 4 (Tage)",
-	"Kreditoren-Skonto 4 (%)",
-	"Kreditoren-Ziel 5 (Tage)",
-	"Kreditoren-Skonto 5 (%)",
-	"Mahnung",
-	"Kontoauszug",
-	"Mahntext 1",
-	"Mahntext 2",
-	"Mahntext 3",
-	"Kontoauszugstext",
-	"Mahnlimit Betrag",
-	"Mahnlimit %",
-	"Zinsberechnung",
-	"Mahnzinssatz 1",
-	"Mahnzinssatz 2",
-	"Mahnzinssatz 3",
-	"Lastschrift",
-	"Verfahren",
-	"Mandantenbank",
-	"Zahlungsträger",
-	"Indiv. Feld 1",
-	"Indiv. Feld 2",
-	"Indiv. Feld 3",
-	"Indiv. Feld 4",
-	"Indiv. Feld 5",
-	"Indiv. Feld 6",
-	"Indiv. Feld 7",
-	"Indiv. Feld 8",
-	"Indiv. Feld 9",
-	"Indiv. Feld 10",
-	"Indiv. Feld 11",
-	"Indiv. Feld 12",
-	"Indiv. Feld 13",
-	"Indiv. Feld 14",
-	"Indiv. Feld 15",
-	"Abweichende Anrede (Rechnungsadresse)",
-	"Adressart (Rechnungsadresse)",
-	"Straße (Rechnungsadresse)",
-	"Postfach (Rechnungsadresse)",
-	"Postleitzahl (Rechnungsadresse)",
-	"Ort (Rechnungsadresse)",
-	"Land (Rechnungsadresse)",
-	"Versandzusatz (Rechnungsadresse)",
-	"Adresszusatz (Rechnungsadresse)",
-	"Abw. Zustellbezeichnung 1 (Rechnungsadresse)",
-	"Abw. Zustellbezeichnung 2 (Rechnungsadresse)",
-	"Adresse Gültig von (Rechnungsadresse)",
-	"Adresse Gültig bis (Rechnungsadresse)",
-	"Bankleitzahl 6",
-	"Bankbezeichnung 6",
-	"Bankkonto-Nummer 6",
-	"Länderkennzeichen 6",
-	"IBAN 6",
-	"Leerfeld 7",
-	"SWIFT-Code 6",
-	"Abw. Kontoinhaber 6",
-	"Kennz. Haupt-Bankverb. 6",
-	"Bankverb 6 gültig von",
-	"Bankverb 6 gültig bis",
-	"Bankleitzahl 7",
-	"Bankbezeichnung 7",
-	"Bankkonto-Nummer 7",
-	"Länderkennzeichen 7",
-	"IBAN 7",
-	"Leerfeld 8",
-	"SWIFT-Code 7",
-	"Abw. Kontoinhaber 7",
-	"Kennz. Haupt-Bankverb. 7",
-	"Bankverb 7 gültig von",
-	"Bankverb 7 gültig bis",
-	"Bankleitzahl 8",
-	"Bankbezeichnung 8",
-	"Bankkonto-Nummer 8",
-	"Länderkennzeichen 8",
-	"IBAN 8",
-	"Leerfeld 9",
-	"SWIFT-Code 8",
-	"Abw. Kontoinhaber 8",
-	"Kennz. Haupt-Bankverb. 8",
-	"Bankverb 8 gültig von",
-	"Bankverb 8 gültig bis",
-	"Bankleitzahl 9",
-	"Bankbezeichnung 9",
-	"Bankkonto-Nummer 9",
-	"Länderkennzeichen 9",
-	"IBAN 9",
-	"Leerfeld 10",
-	"SWIFT-Code 9",
-	"Abw. Kontoinhaber 9",
-	"Kennz. Haupt-Bankverb. 9",
-	"Bankverb 9 gültig von",
-	"Bankverb 9 gültig bis",
-	"Bankleitzahl 10",
-	"Bankbezeichnung 10",
-	"Bankkonto-Nummer 10",
-	"Länderkennzeichen 10",
-	"IBAN 10",
-	"Leerfeld 11",
-	"SWIFT-Code 10",
-	"Abw. Kontoinhaber 10",
-	"Kennz. Haupt-Bankverb. 10",
-	"Bankverb 10 gültig von",
-	"Bankverb 10 gültig bis",
-	"Nummer Fremdsystem",
-	"Insolvent",
-	"SEPA-Mandatsreferenz 1",
-	"SEPA-Mandatsreferenz 2",
-	"SEPA-Mandatsreferenz 3",
-	"SEPA-Mandatsreferenz 4",
-	"SEPA-Mandatsreferenz 5",
-	"SEPA-Mandatsreferenz 6",
-	"SEPA-Mandatsreferenz 7",
-	"SEPA-Mandatsreferenz 8",
-	"SEPA-Mandatsreferenz 9",
-	"SEPA-Mandatsreferenz 10",
-	"Verknüpftes OPOS-Konto",
-	"Mahnsperre bis",
-	"Lastschriftsperre bis",
-	"Zahlungssperre bis",
-	"Gebührenberechnung",
-	"Mahngebühr 1",
-	"Mahngebühr 2",
-	"Mahngebühr 3",
-	"Pauschalberechnung",
-	"Verzugspauschale 1",
-	"Verzugspauschale 2",
-	"Verzugspauschale 3",
-	"Alternativer Suchname",
-	"Status",
-	"Anschrift manuell geändert (Korrespondenzadresse)",
-	"Anschrift individuell (Korrespondenzadresse)",
-	"Anschrift manuell geändert (Rechnungsadresse)",
-	"Anschrift individuell (Rechnungsadresse)",
-	"Fristberechnung bei Debitor",
-	"Mahnfrist 1",
-	"Mahnfrist 2",
-	"Mahnfrist 3",
-	"Letzte Frist"
-]
-
-ACCOUNT_NAME_COLUMNS = [
-	# Account number
-	"Konto",
-	# Account name
-	"Kontenbeschriftung",
-	# Language of the account name
-	# "de-DE" or "en-GB"
-	"Sprach-ID"
-]
-
-class DataCategory():
-
-	"""Field of the CSV Header."""
-
-	DEBTORS_CREDITORS = "16"
-	ACCOUNT_NAMES = "20"
-	TRANSACTIONS = "21"
-	POSTING_TEXT_CONSTANTS = "67"
-
-class FormatName():
-
-	"""Field of the CSV Header, corresponds to DataCategory."""
-
-	DEBTORS_CREDITORS = "Debitoren/Kreditoren"
-	ACCOUNT_NAMES = "Kontenbeschriftungen"
-	TRANSACTIONS = "Buchungsstapel"
-	POSTING_TEXT_CONSTANTS = "Buchungstextkonstanten"
-
-class Transactions():
-	DATA_CATEGORY = DataCategory.TRANSACTIONS
-	FORMAT_NAME = FormatName.TRANSACTIONS
-	FORMAT_VERSION = "9"
-	COLUMNS = TRANSACTION_COLUMNS
-
-class DebtorsCreditors():
-	DATA_CATEGORY = DataCategory.DEBTORS_CREDITORS
-	FORMAT_NAME = FormatName.DEBTORS_CREDITORS
-	FORMAT_VERSION = "5"
-	COLUMNS = DEBTOR_CREDITOR_COLUMNS
-
-class AccountNames():
-	DATA_CATEGORY = DataCategory.ACCOUNT_NAMES
-	FORMAT_NAME = FormatName.ACCOUNT_NAMES
-	FORMAT_VERSION = "2"
-	COLUMNS = ACCOUNT_NAME_COLUMNS
diff --git a/erpnext/regional/germany/utils/datev/datev_csv.py b/erpnext/regional/germany/utils/datev/datev_csv.py
deleted file mode 100644
index ec271a1..0000000
--- a/erpnext/regional/germany/utils/datev/datev_csv.py
+++ /dev/null
@@ -1,180 +0,0 @@
-import datetime
-import zipfile
-from csv import QUOTE_NONNUMERIC
-from io import BytesIO
-
-import frappe
-import pandas as pd
-from frappe import _
-
-from .datev_constants import DataCategory
-
-
-def get_datev_csv(data, filters, csv_class):
-	"""
-	Fill in missing columns and return a CSV in DATEV Format.
-
-	For automatic processing, DATEV requires the first line of the CSV file to
-	hold meta data such as the length of account numbers oder the category of
-	the data.
-
-	Arguments:
-	data -- array of dictionaries
-	filters -- dict
-	csv_class -- defines DATA_CATEGORY, FORMAT_NAME and COLUMNS
-	"""
-	empty_df = pd.DataFrame(columns=csv_class.COLUMNS)
-	data_df = pd.DataFrame.from_records(data)
-	result = empty_df.append(data_df, sort=True)
-
-	if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS:
-		result['Belegdatum'] = pd.to_datetime(result['Belegdatum'])
-
-		result['Beleginfo - Inhalt 6'] = pd.to_datetime(result['Beleginfo - Inhalt 6'])
-		result['Beleginfo - Inhalt 6'] = result['Beleginfo - Inhalt 6'].dt.strftime('%d%m%Y')
-
-		result['Fälligkeit'] = pd.to_datetime(result['Fälligkeit'])
-		result['Fälligkeit'] = result['Fälligkeit'].dt.strftime('%d%m%y')
-
-		result.sort_values(by='Belegdatum', inplace=True, kind='stable', ignore_index=True)
-
-	if csv_class.DATA_CATEGORY == DataCategory.ACCOUNT_NAMES:
-		result['Sprach-ID'] = 'de-DE'
-
-	data = result.to_csv(
-		# Reason for str(';'): https://github.com/pandas-dev/pandas/issues/6035
-		sep=';',
-		# European decimal seperator
-		decimal=',',
-		# Windows "ANSI" encoding
-		encoding='latin_1',
-		# format date as DDMM
-		date_format='%d%m',
-		# Windows line terminator
-		line_terminator='\r\n',
-		# Do not number rows
-		index=False,
-		# Use all columns defined above
-		columns=csv_class.COLUMNS,
-		# Quote most fields, even currency values with "," separator
-		quoting=QUOTE_NONNUMERIC
-	)
-
-	data = data.encode('latin_1', errors='replace')
-
-	header = get_header(filters, csv_class)
-	header = ';'.join(header).encode('latin_1', errors='replace')
-
-	# 1st Row: Header with meta data
-	# 2nd Row: Data heading (Überschrift der Nutzdaten), included in `data` here.
-	# 3rd - nth Row: Data (Nutzdaten)
-	return header + b'\r\n' + data
-
-
-def get_header(filters, csv_class):
-	description = filters.get('voucher_type', csv_class.FORMAT_NAME)
-	company = filters.get('company')
-	datev_settings = frappe.get_doc('DATEV Settings', {'client': company})
-	default_currency = frappe.get_value('Company', company, 'default_currency')
-	coa = frappe.get_value('Company', company, 'chart_of_accounts')
-	coa_short_code = '04' if 'SKR04' in coa else ('03' if 'SKR03' in coa else '')
-
-	header = [
-		# DATEV format
-		#	"DTVF" = created by DATEV software,
-		#	"EXTF" = created by other software
-		'"EXTF"',
-		# version of the DATEV format
-		#	141 = 1.41, 
-		#	510 = 5.10,
-		#	720 = 7.20
-		'700',
-		csv_class.DATA_CATEGORY,
-		'"%s"' % csv_class.FORMAT_NAME,
-		# Format version (regarding format name)
-		csv_class.FORMAT_VERSION,
-		# Generated on
-		datetime.datetime.now().strftime('%Y%m%d%H%M%S') + '000',
-		# Imported on -- stays empty
-		'',
-		# Origin. Any two symbols, will be replaced by "SV" on import.
-		'"EN"',
-		# I = Exported by
-		'"%s"' % frappe.session.user,
-		# J = Imported by -- stays empty
-		'',
-		# K = Tax consultant number (Beraternummer)
-		datev_settings.get('consultant_number', '0000000'),
-		# L = Tax client number (Mandantennummer)
-		datev_settings.get('client_number', '00000'),
-		# M = Start of the fiscal year (Wirtschaftsjahresbeginn)
-		frappe.utils.formatdate(filters.get('fiscal_year_start'), 'yyyyMMdd'),
-		# N = Length of account numbers (Sachkontenlänge)
-		str(filters.get('account_number_length', 4)),
-		# O = Transaction batch start date (YYYYMMDD)
-		frappe.utils.formatdate(filters.get('from_date'), 'yyyyMMdd') if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
-		# P = Transaction batch end date (YYYYMMDD)
-		frappe.utils.formatdate(filters.get('to_date'), 'yyyyMMdd') if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
-		# Q = Description (for example, "Sales Invoice") Max. 30 chars
-		'"{}"'.format(_(description)) if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
-		# R = Diktatkürzel
-		'',
-		# S = Buchungstyp
-		#	1 = Transaction batch (Finanzbuchführung),
-		#	2 = Annual financial statement (Jahresabschluss)
-		'1' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
-		# T = Rechnungslegungszweck
-		#	0 oder leer = vom Rechnungslegungszweck unabhängig
-		#	50 = Handelsrecht
-		#	30 = Steuerrecht
-		#	64 = IFRS
-		#	40 = Kalkulatorik
-		#	11 = Reserviert
-		#	12 = Reserviert
-		'0' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
-		# U = Festschreibung
-		# TODO: Filter by Accounting Period. In export for closed Accounting Period, this will be "1"
-		'0',
-		# V = Default currency, for example, "EUR"
-		'"%s"' % default_currency if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
-		# reserviert
-		'',
-		# Derivatskennzeichen
-		'',
-		# reserviert
-		'',
-		# reserviert
-		'',
-		# SKR
-		'"%s"' % coa_short_code,
-		# Branchen-Lösungs-ID
-		'',
-		# reserviert
-		'',
-		# reserviert
-		'',
-		# Anwendungsinformation (Verarbeitungskennzeichen der abgebenden Anwendung)
-		''
-	]
-	return header
-
-
-def zip_and_download(zip_filename, csv_files):
-	"""
-	Put CSV files in a zip archive and send that to the client.
-
-	Params:
-	zip_filename	Name of the zip file
-	csv_files		list of dicts [{'file_name': 'my_file.csv', 'csv_data': 'comma,separated,values'}]
-	"""
-	zip_buffer = BytesIO()
-
-	zip_file = zipfile.ZipFile(zip_buffer, mode='w', compression=zipfile.ZIP_DEFLATED)
-	for csv_file in csv_files:
-		zip_file.writestr(csv_file.get('file_name'), csv_file.get('csv_data'))
-
-	zip_file.close()
-
-	frappe.response['filecontent'] = zip_buffer.getvalue()
-	frappe.response['filename'] = zip_filename
-	frappe.response['type'] = 'binary'
diff --git a/erpnext/regional/italy/__init__.py b/erpnext/regional/italy/__init__.py
index eb20f65..643e955 100644
--- a/erpnext/regional/italy/__init__.py
+++ b/erpnext/regional/italy/__init__.py
@@ -1,77 +1,172 @@
 fiscal_regimes = [
-    "RF01-Ordinario",
-    "RF02-Contribuenti minimi (art.1, c.96-117, L. 244/07)",
-    "RF04-Agricoltura e attività connesse e pesca (artt.34 e 34-bis, DPR 633/72)",
-    "RF05-Vendita sali e tabacchi (art.74, c.1, DPR. 633/72)",
-    "RF06-Commercio fiammiferi (art.74, c.1, DPR  633/72)",
-    "RF07-Editoria (art.74, c.1, DPR  633/72)",
-    "RF08-Gestione servizi telefonia pubblica (art.74, c.1, DPR 633/72)",
-    "RF09-Rivendita documenti di trasporto pubblico e di sosta (art.74, c.1, DPR  633/72)",
-    "RF10-Intrattenimenti, giochi e altre attività di cui alla tariffa allegata al DPR 640/72 (art.74, c.6, DPR 633/72)",
-    "RF11-Agenzie viaggi e turismo (art.74-ter, DPR 633/72)",
-    "RF12-Agriturismo (art.5, c.2, L. 413/91)",
-    "RF13-Vendite a domicilio (art.25-bis, c.6, DPR  600/73)",
-    "RF14-Rivendita beni usati, oggetti d’arte, d’antiquariato o da collezione (art.36, DL 41/95)",
-    "RF15-Agenzie di vendite all’asta di oggetti d’arte, antiquariato o da collezione (art.40-bis, DL 41/95)",
-    "RF16-IVA per cassa P.A. (art.6, c.5, DPR 633/72)",
-    "RF17-IVA per cassa (art. 32-bis, DL 83/2012)",
-    "RF18-Altro",
-    "RF19-Regime forfettario (art.1, c.54-89, L. 190/2014)"
+	"RF01-Ordinario",
+	"RF02-Contribuenti minimi (art.1, c.96-117, L. 244/07)",
+	"RF04-Agricoltura e attività connesse e pesca (artt.34 e 34-bis, DPR 633/72)",
+	"RF05-Vendita sali e tabacchi (art.74, c.1, DPR. 633/72)",
+	"RF06-Commercio fiammiferi (art.74, c.1, DPR  633/72)",
+	"RF07-Editoria (art.74, c.1, DPR  633/72)",
+	"RF08-Gestione servizi telefonia pubblica (art.74, c.1, DPR 633/72)",
+	"RF09-Rivendita documenti di trasporto pubblico e di sosta (art.74, c.1, DPR  633/72)",
+	"RF10-Intrattenimenti, giochi e altre attività di cui alla tariffa allegata al DPR 640/72 (art.74, c.6, DPR 633/72)",
+	"RF11-Agenzie viaggi e turismo (art.74-ter, DPR 633/72)",
+	"RF12-Agriturismo (art.5, c.2, L. 413/91)",
+	"RF13-Vendite a domicilio (art.25-bis, c.6, DPR  600/73)",
+	"RF14-Rivendita beni usati, oggetti d’arte, d’antiquariato o da collezione (art.36, DL 41/95)",
+	"RF15-Agenzie di vendite all’asta di oggetti d’arte, antiquariato o da collezione (art.40-bis, DL 41/95)",
+	"RF16-IVA per cassa P.A. (art.6, c.5, DPR 633/72)",
+	"RF17-IVA per cassa (art. 32-bis, DL 83/2012)",
+	"RF18-Altro",
+	"RF19-Regime forfettario (art.1, c.54-89, L. 190/2014)",
 ]
 
 tax_exemption_reasons = [
-    "N1-Escluse ex art. 15",
-    "N2-Non Soggette",
-    "N3-Non Imponibili",
-    "N4-Esenti",
-    "N5-Regime del margine / IVA non esposta in fattura",
-    "N6-Inversione Contabile",
-    "N7-IVA assolta in altro stato UE"
+	"N1-Escluse ex art. 15",
+	"N2-Non Soggette",
+	"N3-Non Imponibili",
+	"N4-Esenti",
+	"N5-Regime del margine / IVA non esposta in fattura",
+	"N6-Inversione Contabile",
+	"N7-IVA assolta in altro stato UE",
 ]
 
 mode_of_payment_codes = [
-    "MP01-Contanti",
-    "MP02-Assegno",
-    "MP03-Assegno circolare",
-    "MP04-Contanti presso Tesoreria",
-    "MP05-Bonifico",
-    "MP06-Vaglia cambiario",
-    "MP07-Bollettino bancario",
-    "MP08-Carta di pagamento",
-    "MP09-RID",
-    "MP10-RID utenze",
-    "MP11-RID veloce",
-    "MP12-RIBA",
-    "MP13-MAV",
-    "MP14-Quietanza erario",
-    "MP15-Giroconto su conti di contabilità speciale",
-    "MP16-Domiciliazione bancaria",
-    "MP17-Domiciliazione postale",
-    "MP18-Bollettino di c/c postale",
-    "MP19-SEPA Direct Debit",
-    "MP20-SEPA Direct Debit CORE",
-    "MP21-SEPA Direct Debit B2B",
-    "MP22-Trattenuta su somme già riscosse"
+	"MP01-Contanti",
+	"MP02-Assegno",
+	"MP03-Assegno circolare",
+	"MP04-Contanti presso Tesoreria",
+	"MP05-Bonifico",
+	"MP06-Vaglia cambiario",
+	"MP07-Bollettino bancario",
+	"MP08-Carta di pagamento",
+	"MP09-RID",
+	"MP10-RID utenze",
+	"MP11-RID veloce",
+	"MP12-RIBA",
+	"MP13-MAV",
+	"MP14-Quietanza erario",
+	"MP15-Giroconto su conti di contabilità speciale",
+	"MP16-Domiciliazione bancaria",
+	"MP17-Domiciliazione postale",
+	"MP18-Bollettino di c/c postale",
+	"MP19-SEPA Direct Debit",
+	"MP20-SEPA Direct Debit CORE",
+	"MP21-SEPA Direct Debit B2B",
+	"MP22-Trattenuta su somme già riscosse",
 ]
 
-vat_collectability_options = [
-    "I-Immediata",
-    "D-Differita",
-    "S-Scissione dei Pagamenti"
-]
+vat_collectability_options = ["I-Immediata", "D-Differita", "S-Scissione dei Pagamenti"]
 
-state_codes = {'Siracusa': 'SR', 'Bologna': 'BO', 'Grosseto': 'GR', 'Caserta': 'CE', 'Alessandria': 'AL', 'Ancona': 'AN', 'Pavia': 'PV',
- 'Benevento or Beneventum': 'BN', 'Modena': 'MO', 'Lodi': 'LO', 'Novara': 'NO', 'Avellino': 'AV', 'Verona': 'VR', 'Forli-Cesena': 'FC',
- 'Caltanissetta': 'CL', 'Brescia': 'BS', 'Rieti': 'RI', 'Treviso': 'TV', 'Ogliastra': 'OG', 'Olbia-Tempio': 'OT', 'Bergamo': 'BG',
- 'Napoli': 'NA', 'Campobasso': 'CB', 'Fermo': 'FM', 'Roma': 'RM', 'Lucca': 'LU', 'Rovigo': 'RO', 'Piacenza': 'PC', 'Monza and Brianza': 'MB',
- 'La Spezia': 'SP', 'Pescara': 'PE', 'Vercelli': 'VC', 'Enna': 'EN', 'Nuoro': 'NU', 'Medio Campidano': 'MD', 'Trieste': 'TS', 'Aosta': 'AO',
- 'Firenze': 'FI', 'Trapani': 'TP', 'Messina': 'ME', 'Teramo': 'TE', 'Udine': 'UD', 'Verbano-Cusio-Ossola': 'VB', 'Padua': 'PD',
- 'Reggio Emilia': 'RE', 'Frosinone': 'FR', 'Taranto': 'TA', 'Catanzaro': 'CZ', 'Belluno': 'BL', 'Pordenone': 'PN', 'Viterbo': 'VT',
- 'Gorizia': 'GO', 'Vatican City': 'SCV', 'Ferrara': 'FE', 'Chieti': 'CH', 'Crotone': 'KR', 'Foggia': 'FG', 'Perugia': 'PG', 'Bari': 'BA',
- 'Massa-Carrara': 'MS', 'Pisa': 'PI', 'Latina': 'LT', 'Salerno': 'SA', 'Turin': 'TO', 'Lecco': 'LC', 'Lecce': 'LE', 'Pistoia': 'PT', 'Como': 'CO',
- 'Barletta-Andria-Trani': 'BT', 'Mantua': 'MN', 'Ragusa': 'RG', 'Macerata': 'MC', 'Imperia': 'IM', 'Palermo': 'PA', 'Matera': 'MT', "L'Aquila": 'AQ',
- 'Milano': 'MI', 'Catania': 'CT', 'Pesaro e Urbino': 'PU', 'Potenza': 'PZ', 'Republic of San Marino': 'RSM', 'Genoa': 'GE', 'Brindisi': 'BR',
- 'Cagliari': 'CA', 'Siena': 'SI', 'Vibo Valentia': 'VV', 'Reggio Calabria': 'RC', 'Ascoli Piceno': 'AP', 'Carbonia-Iglesias': 'CI', 'Oristano': 'OR',
- 'Asti': 'AT', 'Ravenna': 'RA', 'Vicenza': 'VI', 'Savona': 'SV', 'Biella': 'BI', 'Rimini': 'RN', 'Agrigento': 'AG', 'Prato': 'PO', 'Cuneo': 'CN',
- 'Cosenza': 'CS', 'Livorno or Leghorn': 'LI', 'Sondrio': 'SO', 'Cremona': 'CR', 'Isernia': 'IS', 'Trento': 'TN', 'Terni': 'TR', 'Bolzano/Bozen': 'BZ',
- 'Parma': 'PR', 'Varese': 'VA', 'Venezia': 'VE', 'Sassari': 'SS', 'Arezzo': 'AR'}
+state_codes = {
+	"Siracusa": "SR",
+	"Bologna": "BO",
+	"Grosseto": "GR",
+	"Caserta": "CE",
+	"Alessandria": "AL",
+	"Ancona": "AN",
+	"Pavia": "PV",
+	"Benevento or Beneventum": "BN",
+	"Modena": "MO",
+	"Lodi": "LO",
+	"Novara": "NO",
+	"Avellino": "AV",
+	"Verona": "VR",
+	"Forli-Cesena": "FC",
+	"Caltanissetta": "CL",
+	"Brescia": "BS",
+	"Rieti": "RI",
+	"Treviso": "TV",
+	"Ogliastra": "OG",
+	"Olbia-Tempio": "OT",
+	"Bergamo": "BG",
+	"Napoli": "NA",
+	"Campobasso": "CB",
+	"Fermo": "FM",
+	"Roma": "RM",
+	"Lucca": "LU",
+	"Rovigo": "RO",
+	"Piacenza": "PC",
+	"Monza and Brianza": "MB",
+	"La Spezia": "SP",
+	"Pescara": "PE",
+	"Vercelli": "VC",
+	"Enna": "EN",
+	"Nuoro": "NU",
+	"Medio Campidano": "MD",
+	"Trieste": "TS",
+	"Aosta": "AO",
+	"Firenze": "FI",
+	"Trapani": "TP",
+	"Messina": "ME",
+	"Teramo": "TE",
+	"Udine": "UD",
+	"Verbano-Cusio-Ossola": "VB",
+	"Padua": "PD",
+	"Reggio Emilia": "RE",
+	"Frosinone": "FR",
+	"Taranto": "TA",
+	"Catanzaro": "CZ",
+	"Belluno": "BL",
+	"Pordenone": "PN",
+	"Viterbo": "VT",
+	"Gorizia": "GO",
+	"Vatican City": "SCV",
+	"Ferrara": "FE",
+	"Chieti": "CH",
+	"Crotone": "KR",
+	"Foggia": "FG",
+	"Perugia": "PG",
+	"Bari": "BA",
+	"Massa-Carrara": "MS",
+	"Pisa": "PI",
+	"Latina": "LT",
+	"Salerno": "SA",
+	"Turin": "TO",
+	"Lecco": "LC",
+	"Lecce": "LE",
+	"Pistoia": "PT",
+	"Como": "CO",
+	"Barletta-Andria-Trani": "BT",
+	"Mantua": "MN",
+	"Ragusa": "RG",
+	"Macerata": "MC",
+	"Imperia": "IM",
+	"Palermo": "PA",
+	"Matera": "MT",
+	"L'Aquila": "AQ",
+	"Milano": "MI",
+	"Catania": "CT",
+	"Pesaro e Urbino": "PU",
+	"Potenza": "PZ",
+	"Republic of San Marino": "RSM",
+	"Genoa": "GE",
+	"Brindisi": "BR",
+	"Cagliari": "CA",
+	"Siena": "SI",
+	"Vibo Valentia": "VV",
+	"Reggio Calabria": "RC",
+	"Ascoli Piceno": "AP",
+	"Carbonia-Iglesias": "CI",
+	"Oristano": "OR",
+	"Asti": "AT",
+	"Ravenna": "RA",
+	"Vicenza": "VI",
+	"Savona": "SV",
+	"Biella": "BI",
+	"Rimini": "RN",
+	"Agrigento": "AG",
+	"Prato": "PO",
+	"Cuneo": "CN",
+	"Cosenza": "CS",
+	"Livorno or Leghorn": "LI",
+	"Sondrio": "SO",
+	"Cremona": "CR",
+	"Isernia": "IS",
+	"Trento": "TN",
+	"Terni": "TR",
+	"Bolzano/Bozen": "BZ",
+	"Parma": "PR",
+	"Varese": "VA",
+	"Venezia": "VE",
+	"Sassari": "SS",
+	"Arezzo": "AR",
+}
diff --git a/erpnext/regional/italy/setup.py b/erpnext/regional/italy/setup.py
index 531f10d..1f66b36 100644
--- a/erpnext/regional/italy/setup.py
+++ b/erpnext/regional/italy/setup.py
@@ -7,213 +7,489 @@
 from frappe import _
 from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 from frappe.permissions import add_permission, update_permission_property
-from erpnext.regional.italy import fiscal_regimes, tax_exemption_reasons, mode_of_payment_codes, vat_collectability_options
+from erpnext.regional.italy import (
+	fiscal_regimes,
+	tax_exemption_reasons,
+	mode_of_payment_codes,
+	vat_collectability_options,
+)
+
 
 def setup(company=None, patch=True):
 	make_custom_fields()
 	setup_report()
 	add_permissions()
 
+
 def make_custom_fields(update=True):
 	invoice_item_fields = [
-		dict(fieldname='tax_rate', label='Tax Rate',
-			fieldtype='Float', insert_after='description',
-			print_hide=1, hidden=1, read_only=1),
-		dict(fieldname='tax_amount', label='Tax Amount',
-			fieldtype='Currency', insert_after='tax_rate',
-			print_hide=1, hidden=1, read_only=1, options="currency"),
-		dict(fieldname='total_amount', label='Total Amount',
-			fieldtype='Currency', insert_after='tax_amount',
-			print_hide=1, hidden=1, read_only=1, options="currency")
+		dict(
+			fieldname="tax_rate",
+			label="Tax Rate",
+			fieldtype="Float",
+			insert_after="description",
+			print_hide=1,
+			hidden=1,
+			read_only=1,
+		),
+		dict(
+			fieldname="tax_amount",
+			label="Tax Amount",
+			fieldtype="Currency",
+			insert_after="tax_rate",
+			print_hide=1,
+			hidden=1,
+			read_only=1,
+			options="currency",
+		),
+		dict(
+			fieldname="total_amount",
+			label="Total Amount",
+			fieldtype="Currency",
+			insert_after="tax_amount",
+			print_hide=1,
+			hidden=1,
+			read_only=1,
+			options="currency",
+		),
 	]
 
 	customer_po_fields = [
-		dict(fieldname='customer_po_details', label='Customer PO',
-			fieldtype='Section Break', insert_after='image'),
-		dict(fieldname='customer_po_no', label='Customer PO No',
-			fieldtype='Data', insert_after='customer_po_details',
-			fetch_from = 'sales_order.po_no',
-			print_hide=1, allow_on_submit=1, fetch_if_empty= 1, read_only=1, no_copy=1),
-		dict(fieldname='customer_po_clm_brk', label='',
-			fieldtype='Column Break', insert_after='customer_po_no',
-			print_hide=1, read_only=1),
-		dict(fieldname='customer_po_date', label='Customer PO Date',
-			fieldtype='Date', insert_after='customer_po_clm_brk',
-			fetch_from = 'sales_order.po_date',
-			print_hide=1, allow_on_submit=1, fetch_if_empty= 1, read_only=1, no_copy=1)
+		dict(
+			fieldname="customer_po_details",
+			label="Customer PO",
+			fieldtype="Section Break",
+			insert_after="image",
+		),
+		dict(
+			fieldname="customer_po_no",
+			label="Customer PO No",
+			fieldtype="Data",
+			insert_after="customer_po_details",
+			fetch_from="sales_order.po_no",
+			print_hide=1,
+			allow_on_submit=1,
+			fetch_if_empty=1,
+			read_only=1,
+			no_copy=1,
+		),
+		dict(
+			fieldname="customer_po_clm_brk",
+			label="",
+			fieldtype="Column Break",
+			insert_after="customer_po_no",
+			print_hide=1,
+			read_only=1,
+		),
+		dict(
+			fieldname="customer_po_date",
+			label="Customer PO Date",
+			fieldtype="Date",
+			insert_after="customer_po_clm_brk",
+			fetch_from="sales_order.po_date",
+			print_hide=1,
+			allow_on_submit=1,
+			fetch_if_empty=1,
+			read_only=1,
+			no_copy=1,
+		),
 	]
 
 	custom_fields = {
-		'Company': [
-			dict(fieldname='sb_e_invoicing', label='E-Invoicing',
-				fieldtype='Section Break', insert_after='date_of_establishment', print_hide=1),
-			dict(fieldname='fiscal_regime', label='Fiscal Regime',
-				fieldtype='Select', insert_after='sb_e_invoicing', print_hide=1,
-				options="\n".join(map(lambda x: frappe.safe_decode(x, encoding='utf-8'), fiscal_regimes))),
-			dict(fieldname='fiscal_code', label='Fiscal Code', fieldtype='Data', insert_after='fiscal_regime', print_hide=1,
-				description=_("Applicable if the company is an Individual or a Proprietorship")),
-			dict(fieldname='vat_collectability', label='VAT Collectability',
-				fieldtype='Select', insert_after='fiscal_code', print_hide=1,
-				options="\n".join(map(lambda x: frappe.safe_decode(x, encoding='utf-8'), vat_collectability_options))),
-			dict(fieldname='cb_e_invoicing1', fieldtype='Column Break', insert_after='vat_collectability', print_hide=1),
-			dict(fieldname='registrar_office_province', label='Province of the Registrar Office',
-				fieldtype='Data', insert_after='cb_e_invoicing1', print_hide=1, length=2),
-			dict(fieldname='registration_number', label='Registration Number',
-				fieldtype='Data', insert_after='registrar_office_province', print_hide=1, length=20),
-			dict(fieldname='share_capital_amount', label='Share Capital',
-				fieldtype='Currency', insert_after='registration_number', print_hide=1,
-				description=_('Applicable if the company is SpA, SApA or SRL')),
-			dict(fieldname='no_of_members', label='No of Members',
-				fieldtype='Select', insert_after='share_capital_amount', print_hide=1,
-				options="\nSU-Socio Unico\nSM-Piu Soci", description=_("Applicable if the company is a limited liability company")),
-			dict(fieldname='liquidation_state', label='Liquidation State',
-				fieldtype='Select', insert_after='no_of_members', print_hide=1,
-				options="\nLS-In Liquidazione\nLN-Non in Liquidazione")
+		"Company": [
+			dict(
+				fieldname="sb_e_invoicing",
+				label="E-Invoicing",
+				fieldtype="Section Break",
+				insert_after="date_of_establishment",
+				print_hide=1,
+			),
+			dict(
+				fieldname="fiscal_regime",
+				label="Fiscal Regime",
+				fieldtype="Select",
+				insert_after="sb_e_invoicing",
+				print_hide=1,
+				options="\n".join(map(lambda x: frappe.safe_decode(x, encoding="utf-8"), fiscal_regimes)),
+			),
+			dict(
+				fieldname="fiscal_code",
+				label="Fiscal Code",
+				fieldtype="Data",
+				insert_after="fiscal_regime",
+				print_hide=1,
+				description=_("Applicable if the company is an Individual or a Proprietorship"),
+			),
+			dict(
+				fieldname="vat_collectability",
+				label="VAT Collectability",
+				fieldtype="Select",
+				insert_after="fiscal_code",
+				print_hide=1,
+				options="\n".join(
+					map(lambda x: frappe.safe_decode(x, encoding="utf-8"), vat_collectability_options)
+				),
+			),
+			dict(
+				fieldname="cb_e_invoicing1",
+				fieldtype="Column Break",
+				insert_after="vat_collectability",
+				print_hide=1,
+			),
+			dict(
+				fieldname="registrar_office_province",
+				label="Province of the Registrar Office",
+				fieldtype="Data",
+				insert_after="cb_e_invoicing1",
+				print_hide=1,
+				length=2,
+			),
+			dict(
+				fieldname="registration_number",
+				label="Registration Number",
+				fieldtype="Data",
+				insert_after="registrar_office_province",
+				print_hide=1,
+				length=20,
+			),
+			dict(
+				fieldname="share_capital_amount",
+				label="Share Capital",
+				fieldtype="Currency",
+				insert_after="registration_number",
+				print_hide=1,
+				description=_("Applicable if the company is SpA, SApA or SRL"),
+			),
+			dict(
+				fieldname="no_of_members",
+				label="No of Members",
+				fieldtype="Select",
+				insert_after="share_capital_amount",
+				print_hide=1,
+				options="\nSU-Socio Unico\nSM-Piu Soci",
+				description=_("Applicable if the company is a limited liability company"),
+			),
+			dict(
+				fieldname="liquidation_state",
+				label="Liquidation State",
+				fieldtype="Select",
+				insert_after="no_of_members",
+				print_hide=1,
+				options="\nLS-In Liquidazione\nLN-Non in Liquidazione",
+			),
 		],
-		'Sales Taxes and Charges': [
-			dict(fieldname='tax_exemption_reason', label='Tax Exemption Reason',
-				fieldtype='Select', insert_after='included_in_print_rate', print_hide=1,
+		"Sales Taxes and Charges": [
+			dict(
+				fieldname="tax_exemption_reason",
+				label="Tax Exemption Reason",
+				fieldtype="Select",
+				insert_after="included_in_print_rate",
+				print_hide=1,
 				depends_on='eval:doc.charge_type!="Actual" && doc.rate==0.0',
-				options="\n" + "\n".join(map(lambda x: frappe.safe_decode(x, encoding='utf-8'), tax_exemption_reasons))),
-			dict(fieldname='tax_exemption_law', label='Tax Exempt Under',
-				fieldtype='Text', insert_after='tax_exemption_reason', print_hide=1,
-				depends_on='eval:doc.charge_type!="Actual" && doc.rate==0.0')
+				options="\n"
+				+ "\n".join(map(lambda x: frappe.safe_decode(x, encoding="utf-8"), tax_exemption_reasons)),
+			),
+			dict(
+				fieldname="tax_exemption_law",
+				label="Tax Exempt Under",
+				fieldtype="Text",
+				insert_after="tax_exemption_reason",
+				print_hide=1,
+				depends_on='eval:doc.charge_type!="Actual" && doc.rate==0.0',
+			),
 		],
-		'Customer': [
-			dict(fieldname='fiscal_code', label='Fiscal Code', fieldtype='Data', insert_after='tax_id', print_hide=1),
-			dict(fieldname='recipient_code', label='Recipient Code',
-				fieldtype='Data', insert_after='fiscal_code', print_hide=1, default="0000000"),
-			dict(fieldname='pec', label='Recipient PEC',
-				fieldtype='Data', insert_after='fiscal_code', print_hide=1),
-			dict(fieldname='is_public_administration', label='Is Public Administration',
-				fieldtype='Check', insert_after='is_internal_customer', print_hide=1,
+		"Customer": [
+			dict(
+				fieldname="fiscal_code",
+				label="Fiscal Code",
+				fieldtype="Data",
+				insert_after="tax_id",
+				print_hide=1,
+			),
+			dict(
+				fieldname="recipient_code",
+				label="Recipient Code",
+				fieldtype="Data",
+				insert_after="fiscal_code",
+				print_hide=1,
+				default="0000000",
+			),
+			dict(
+				fieldname="pec",
+				label="Recipient PEC",
+				fieldtype="Data",
+				insert_after="fiscal_code",
+				print_hide=1,
+			),
+			dict(
+				fieldname="is_public_administration",
+				label="Is Public Administration",
+				fieldtype="Check",
+				insert_after="is_internal_customer",
+				print_hide=1,
 				description=_("Set this if the customer is a Public Administration company."),
-				depends_on='eval:doc.customer_type=="Company"'),
-			dict(fieldname='first_name', label='First Name', fieldtype='Data',
-				insert_after='salutation', print_hide=1, depends_on='eval:doc.customer_type!="Company"'),
-			dict(fieldname='last_name', label='Last Name', fieldtype='Data',
-				insert_after='first_name', print_hide=1, depends_on='eval:doc.customer_type!="Company"')
+				depends_on='eval:doc.customer_type=="Company"',
+			),
+			dict(
+				fieldname="first_name",
+				label="First Name",
+				fieldtype="Data",
+				insert_after="salutation",
+				print_hide=1,
+				depends_on='eval:doc.customer_type!="Company"',
+			),
+			dict(
+				fieldname="last_name",
+				label="Last Name",
+				fieldtype="Data",
+				insert_after="first_name",
+				print_hide=1,
+				depends_on='eval:doc.customer_type!="Company"',
+			),
 		],
-		'Mode of Payment': [
-			dict(fieldname='mode_of_payment_code', label='Code',
-			fieldtype='Select', insert_after='included_in_print_rate', print_hide=1,
-			options="\n".join(map(lambda x: frappe.safe_decode(x, encoding='utf-8'), mode_of_payment_codes)))
+		"Mode of Payment": [
+			dict(
+				fieldname="mode_of_payment_code",
+				label="Code",
+				fieldtype="Select",
+				insert_after="included_in_print_rate",
+				print_hide=1,
+				options="\n".join(
+					map(lambda x: frappe.safe_decode(x, encoding="utf-8"), mode_of_payment_codes)
+				),
+			)
 		],
-		'Payment Schedule': [
-			dict(fieldname='mode_of_payment_code', label='Code',
-				fieldtype='Select', insert_after='mode_of_payment', print_hide=1,
-				options="\n".join(map(lambda x: frappe.safe_decode(x, encoding='utf-8'), mode_of_payment_codes)),
-				fetch_from="mode_of_payment.mode_of_payment_code", read_only=1),
-			dict(fieldname='bank_account', label='Bank Account',
-				fieldtype='Link', insert_after='mode_of_payment_code', print_hide=1,
-				options="Bank Account"),
-			dict(fieldname='bank_account_name', label='Bank Name',
-				fieldtype='Data', insert_after='bank_account', print_hide=1,
-				fetch_from="bank_account.bank", read_only=1),
-			dict(fieldname='bank_account_no', label='Bank Account No',
-				fieldtype='Data', insert_after='bank_account_name', print_hide=1,
-				fetch_from="bank_account.bank_account_no", read_only=1),
-			dict(fieldname='bank_account_iban', label='IBAN',
-				fieldtype='Data', insert_after='bank_account_name', print_hide=1,
-				fetch_from="bank_account.iban", read_only=1),
-			dict(fieldname='bank_account_swift_number', label='Swift Code (BIC)',
-				fieldtype='Data', insert_after='bank_account_iban', print_hide=1,
-				fetch_from="bank_account.swift_number", read_only=1),
+		"Payment Schedule": [
+			dict(
+				fieldname="mode_of_payment_code",
+				label="Code",
+				fieldtype="Select",
+				insert_after="mode_of_payment",
+				print_hide=1,
+				options="\n".join(
+					map(lambda x: frappe.safe_decode(x, encoding="utf-8"), mode_of_payment_codes)
+				),
+				fetch_from="mode_of_payment.mode_of_payment_code",
+				read_only=1,
+			),
+			dict(
+				fieldname="bank_account",
+				label="Bank Account",
+				fieldtype="Link",
+				insert_after="mode_of_payment_code",
+				print_hide=1,
+				options="Bank Account",
+			),
+			dict(
+				fieldname="bank_account_name",
+				label="Bank Name",
+				fieldtype="Data",
+				insert_after="bank_account",
+				print_hide=1,
+				fetch_from="bank_account.bank",
+				read_only=1,
+			),
+			dict(
+				fieldname="bank_account_no",
+				label="Bank Account No",
+				fieldtype="Data",
+				insert_after="bank_account_name",
+				print_hide=1,
+				fetch_from="bank_account.bank_account_no",
+				read_only=1,
+			),
+			dict(
+				fieldname="bank_account_iban",
+				label="IBAN",
+				fieldtype="Data",
+				insert_after="bank_account_name",
+				print_hide=1,
+				fetch_from="bank_account.iban",
+				read_only=1,
+			),
+			dict(
+				fieldname="bank_account_swift_number",
+				label="Swift Code (BIC)",
+				fieldtype="Data",
+				insert_after="bank_account_iban",
+				print_hide=1,
+				fetch_from="bank_account.swift_number",
+				read_only=1,
+			),
 		],
 		"Sales Invoice": [
-			dict(fieldname='vat_collectability', label='VAT Collectability',
-				fieldtype='Select', insert_after='taxes_and_charges', print_hide=1,
-				options="\n".join(map(lambda x: frappe.safe_decode(x, encoding='utf-8'), vat_collectability_options)),
-				fetch_from="company.vat_collectability"),
-			dict(fieldname='sb_e_invoicing_reference', label='E-Invoicing',
-				fieldtype='Section Break', insert_after='against_income_account', print_hide=1),
-			dict(fieldname='company_fiscal_code', label='Company Fiscal Code',
-				fieldtype='Data', insert_after='sb_e_invoicing_reference', print_hide=1, read_only=1,
-				fetch_from="company.fiscal_code"),
-			dict(fieldname='company_fiscal_regime', label='Company Fiscal Regime',
-				fieldtype='Data', insert_after='company_fiscal_code', print_hide=1, read_only=1,
-				fetch_from="company.fiscal_regime"),
-			dict(fieldname='cb_e_invoicing_reference', fieldtype='Column Break',
-				insert_after='company_fiscal_regime', print_hide=1),
-			dict(fieldname='customer_fiscal_code', label='Customer Fiscal Code',
-				fieldtype='Data', insert_after='cb_e_invoicing_reference', read_only=1,
-				fetch_from="customer.fiscal_code"),
-			dict(fieldname='type_of_document', label='Type of Document',
-				fieldtype='Select', insert_after='customer_fiscal_code',
-				options='\nTD01\nTD02\nTD03\nTD04\nTD05\nTD06\nTD16\nTD17\nTD18\nTD19\nTD20\nTD21\nTD22\nTD23\nTD24\nTD25\nTD26\nTD27'),
-		],
-		'Purchase Invoice Item': invoice_item_fields,
-		'Sales Order Item': invoice_item_fields,
-		'Delivery Note Item': invoice_item_fields,
-		'Sales Invoice Item': invoice_item_fields + customer_po_fields,
-		'Quotation Item': invoice_item_fields,
-		'Purchase Order Item': invoice_item_fields,
-		'Purchase Receipt Item': invoice_item_fields,
-		'Supplier Quotation Item': invoice_item_fields,
-		'Address': [
-			dict(fieldname='country_code', label='Country Code',
-				fieldtype='Data', insert_after='country', print_hide=1, read_only=0,
-				fetch_from="country.code"),
-			dict(fieldname='state_code', label='State Code',
-				fieldtype='Data', insert_after='state', print_hide=1)
-		],
-		'Purchase Invoice': [
-			dict(fieldname='document_type', label='Document Type',
-				fieldtype='Data', insert_after='company', print_hide=1, read_only=1
+			dict(
+				fieldname="vat_collectability",
+				label="VAT Collectability",
+				fieldtype="Select",
+				insert_after="taxes_and_charges",
+				print_hide=1,
+				options="\n".join(
+					map(lambda x: frappe.safe_decode(x, encoding="utf-8"), vat_collectability_options)
 				),
-			dict(fieldname='destination_code', label='Destination Code',
-				fieldtype='Data', insert_after='company', print_hide=1, read_only=1
-				),
-			dict(fieldname='imported_grand_total', label='Imported Grand Total',
-				fieldtype='Data', insert_after='update_auto_repeat_reference', print_hide=1, read_only=1
-				)
+				fetch_from="company.vat_collectability",
+			),
+			dict(
+				fieldname="sb_e_invoicing_reference",
+				label="E-Invoicing",
+				fieldtype="Section Break",
+				insert_after="against_income_account",
+				print_hide=1,
+			),
+			dict(
+				fieldname="company_fiscal_code",
+				label="Company Fiscal Code",
+				fieldtype="Data",
+				insert_after="sb_e_invoicing_reference",
+				print_hide=1,
+				read_only=1,
+				fetch_from="company.fiscal_code",
+			),
+			dict(
+				fieldname="company_fiscal_regime",
+				label="Company Fiscal Regime",
+				fieldtype="Data",
+				insert_after="company_fiscal_code",
+				print_hide=1,
+				read_only=1,
+				fetch_from="company.fiscal_regime",
+			),
+			dict(
+				fieldname="cb_e_invoicing_reference",
+				fieldtype="Column Break",
+				insert_after="company_fiscal_regime",
+				print_hide=1,
+			),
+			dict(
+				fieldname="customer_fiscal_code",
+				label="Customer Fiscal Code",
+				fieldtype="Data",
+				insert_after="cb_e_invoicing_reference",
+				read_only=1,
+				fetch_from="customer.fiscal_code",
+			),
+			dict(
+				fieldname="type_of_document",
+				label="Type of Document",
+				fieldtype="Select",
+				insert_after="customer_fiscal_code",
+				options="\nTD01\nTD02\nTD03\nTD04\nTD05\nTD06\nTD16\nTD17\nTD18\nTD19\nTD20\nTD21\nTD22\nTD23\nTD24\nTD25\nTD26\nTD27",
+			),
 		],
-		'Purchase Taxes and Charges': [
-			dict(fieldname='tax_rate', label='Tax Rate',
-				fieldtype='Data', insert_after='parenttype', print_hide=1, read_only=0
-				)
+		"Purchase Invoice Item": invoice_item_fields,
+		"Sales Order Item": invoice_item_fields,
+		"Delivery Note Item": invoice_item_fields,
+		"Sales Invoice Item": invoice_item_fields + customer_po_fields,
+		"Quotation Item": invoice_item_fields,
+		"Purchase Order Item": invoice_item_fields,
+		"Purchase Receipt Item": invoice_item_fields,
+		"Supplier Quotation Item": invoice_item_fields,
+		"Address": [
+			dict(
+				fieldname="country_code",
+				label="Country Code",
+				fieldtype="Data",
+				insert_after="country",
+				print_hide=1,
+				read_only=0,
+				fetch_from="country.code",
+			),
+			dict(
+				fieldname="state_code",
+				label="State Code",
+				fieldtype="Data",
+				insert_after="state",
+				print_hide=1,
+			),
 		],
-		'Supplier': [
-			dict(fieldname='fiscal_code', label='Fiscal Code',
-				fieldtype='Data', insert_after='tax_id', print_hide=1, read_only=1
-				),
-			dict(fieldname='fiscal_regime', label='Fiscal Regime',
-				fieldtype='Select', insert_after='fiscal_code', print_hide=1, read_only=1,
-				options= "\nRF01\nRF02\nRF04\nRF05\nRF06\nRF07\nRF08\nRF09\nRF10\nRF11\nRF12\nRF13\nRF14\nRF15\nRF16\nRF17\nRF18\nRF19"
-				)
-		]
+		"Purchase Invoice": [
+			dict(
+				fieldname="document_type",
+				label="Document Type",
+				fieldtype="Data",
+				insert_after="company",
+				print_hide=1,
+				read_only=1,
+			),
+			dict(
+				fieldname="destination_code",
+				label="Destination Code",
+				fieldtype="Data",
+				insert_after="company",
+				print_hide=1,
+				read_only=1,
+			),
+			dict(
+				fieldname="imported_grand_total",
+				label="Imported Grand Total",
+				fieldtype="Data",
+				insert_after="update_auto_repeat_reference",
+				print_hide=1,
+				read_only=1,
+			),
+		],
+		"Purchase Taxes and Charges": [
+			dict(
+				fieldname="tax_rate",
+				label="Tax Rate",
+				fieldtype="Data",
+				insert_after="parenttype",
+				print_hide=1,
+				read_only=0,
+			)
+		],
+		"Supplier": [
+			dict(
+				fieldname="fiscal_code",
+				label="Fiscal Code",
+				fieldtype="Data",
+				insert_after="tax_id",
+				print_hide=1,
+				read_only=1,
+			),
+			dict(
+				fieldname="fiscal_regime",
+				label="Fiscal Regime",
+				fieldtype="Select",
+				insert_after="fiscal_code",
+				print_hide=1,
+				read_only=1,
+				options="\nRF01\nRF02\nRF04\nRF05\nRF06\nRF07\nRF08\nRF09\nRF10\nRF11\nRF12\nRF13\nRF14\nRF15\nRF16\nRF17\nRF18\nRF19",
+			),
+		],
 	}
 
-	create_custom_fields(custom_fields, ignore_validate = frappe.flags.in_patch, update=update)
+	create_custom_fields(custom_fields, ignore_validate=frappe.flags.in_patch, update=update)
+
 
 def setup_report():
-	report_name = 'Electronic Invoice Register'
+	report_name = "Electronic Invoice Register"
 	frappe.db.set_value("Report", report_name, "disabled", 0)
 
-	if not frappe.db.get_value('Custom Role', dict(report=report_name)):
-		frappe.get_doc(dict(
-			doctype='Custom Role',
-			report=report_name,
-			roles= [
-				dict(role='Accounts User'),
-				dict(role='Accounts Manager')
-			]
-		)).insert()
+	if not frappe.db.get_value("Custom Role", dict(report=report_name)):
+		frappe.get_doc(
+			dict(
+				doctype="Custom Role",
+				report=report_name,
+				roles=[dict(role="Accounts User"), dict(role="Accounts Manager")],
+			)
+		).insert()
+
 
 def add_permissions():
-	doctype = 'Import Supplier Invoice'
-	add_permission(doctype, 'All', 0)
+	doctype = "Import Supplier Invoice"
+	add_permission(doctype, "All", 0)
 
-	for role in ('Accounts Manager', 'Accounts User','Purchase User', 'Auditor'):
+	for role in ("Accounts Manager", "Accounts User", "Purchase User", "Auditor"):
 		add_permission(doctype, role, 0)
-		update_permission_property(doctype, role, 0, 'print', 1)
-		update_permission_property(doctype, role, 0, 'report', 1)
+		update_permission_property(doctype, role, 0, "print", 1)
+		update_permission_property(doctype, role, 0, "report", 1)
 
-		if role in ('Accounts Manager', 'Accounts User'):
-			update_permission_property(doctype, role, 0, 'write', 1)
-			update_permission_property(doctype, role, 0, 'create', 1)
+		if role in ("Accounts Manager", "Accounts User"):
+			update_permission_property(doctype, role, 0, "write", 1)
+			update_permission_property(doctype, role, 0, "create", 1)
 
-	update_permission_property(doctype, 'Accounts Manager', 0, 'delete', 1)
-	add_permission(doctype, 'Accounts Manager', 1)
-	update_permission_property(doctype, 'Accounts Manager', 1, 'write', 1)
-	update_permission_property(doctype, 'Accounts Manager', 1, 'create', 1)
+	update_permission_property(doctype, "Accounts Manager", 0, "delete", 1)
+	add_permission(doctype, "Accounts Manager", 1)
+	update_permission_property(doctype, "Accounts Manager", 1, "write", 1)
+	update_permission_property(doctype, "Accounts Manager", 1, "create", 1)
diff --git a/erpnext/regional/italy/utils.py b/erpnext/regional/italy/utils.py
index c82557b..f5b2e2d 100644
--- a/erpnext/regional/italy/utils.py
+++ b/erpnext/regional/italy/utils.py
@@ -11,41 +11,41 @@
 
 
 def update_itemised_tax_data(doc):
-	if not doc.taxes: return
+	if not doc.taxes:
+		return
 
-	if doc.doctype == "Purchase Invoice": return
+	if doc.doctype == "Purchase Invoice":
+		return
 
 	itemised_tax = get_itemised_tax(doc.taxes)
 
 	for row in doc.items:
 		tax_rate = 0.0
 		if itemised_tax.get(row.item_code):
-			tax_rate = sum([tax.get('tax_rate', 0) for d, tax in itemised_tax.get(row.item_code).items()])
+			tax_rate = sum([tax.get("tax_rate", 0) for d, tax in itemised_tax.get(row.item_code).items()])
 
 		row.tax_rate = flt(tax_rate, row.precision("tax_rate"))
 		row.tax_amount = flt((row.net_amount * tax_rate) / 100, row.precision("net_amount"))
 		row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount"))
 
+
 @frappe.whitelist()
 def export_invoices(filters=None):
-	frappe.has_permission('Sales Invoice', throw=True)
+	frappe.has_permission("Sales Invoice", throw=True)
 
 	invoices = frappe.get_all(
-		"Sales Invoice",
-		filters=get_conditions(filters),
-		fields=["name", "company_tax_id"]
+		"Sales Invoice", filters=get_conditions(filters), fields=["name", "company_tax_id"]
 	)
 
 	attachments = get_e_invoice_attachments(invoices)
 
-	zip_filename = "{0}-einvoices.zip".format(
-		frappe.utils.get_datetime().strftime("%Y%m%d_%H%M%S"))
+	zip_filename = "{0}-einvoices.zip".format(frappe.utils.get_datetime().strftime("%Y%m%d_%H%M%S"))
 
 	download_zip(attachments, zip_filename)
 
 
 def prepare_invoice(invoice, progressive_number):
-	#set company information
+	# set company information
 	company = frappe.get_doc("Company", invoice.company)
 
 	invoice.progressive_number = progressive_number
@@ -54,15 +54,17 @@
 	company_address = frappe.get_doc("Address", invoice.company_address)
 	invoice.company_address_data = company_address
 
-	#Set invoice type
+	# Set invoice type
 	if not invoice.type_of_document:
 		if invoice.is_return and invoice.return_against:
-			invoice.type_of_document = "TD04" #Credit Note (Nota di Credito)
-			invoice.return_against_unamended =  get_unamended_name(frappe.get_doc("Sales Invoice", invoice.return_against))
+			invoice.type_of_document = "TD04"  # Credit Note (Nota di Credito)
+			invoice.return_against_unamended = get_unamended_name(
+				frappe.get_doc("Sales Invoice", invoice.return_against)
+			)
 		else:
-			invoice.type_of_document = "TD01" #Sales Invoice (Fattura)
+			invoice.type_of_document = "TD01"  # Sales Invoice (Fattura)
 
-	#set customer information
+	# set customer information
 	invoice.customer_data = frappe.get_doc("Customer", invoice.customer)
 	customer_address = frappe.get_doc("Address", invoice.customer_address)
 	invoice.customer_address_data = customer_address
@@ -79,8 +81,10 @@
 	tax_data = get_invoice_summary(invoice.e_invoice_items, invoice.taxes)
 	invoice.tax_data = tax_data
 
-	#Check if stamp duty (Bollo) of 2 EUR exists.
-	stamp_duty_charge_row = next((tax for tax in invoice.taxes if tax.charge_type == "Actual" and tax.tax_amount == 2.0 ), None)
+	# Check if stamp duty (Bollo) of 2 EUR exists.
+	stamp_duty_charge_row = next(
+		(tax for tax in invoice.taxes if tax.charge_type == "Actual" and tax.tax_amount == 2.0), None
+	)
 	if stamp_duty_charge_row:
 		invoice.stamp_duty = stamp_duty_charge_row.tax_amount
 
@@ -90,24 +94,28 @@
 
 	customer_po_data = {}
 	for d in invoice.e_invoice_items:
-		if (d.customer_po_no and d.customer_po_date
-			and d.customer_po_no not in customer_po_data):
+		if d.customer_po_no and d.customer_po_date and d.customer_po_no not in customer_po_data:
 			customer_po_data[d.customer_po_no] = d.customer_po_date
 
 	invoice.customer_po_data = customer_po_data
 
 	return invoice
 
+
 def get_conditions(filters):
 	filters = json.loads(filters)
 
 	conditions = {"docstatus": 1, "company_tax_id": ("!=", "")}
 
-	if filters.get("company"): conditions["company"] = filters["company"]
-	if filters.get("customer"): conditions["customer"] =  filters["customer"]
+	if filters.get("company"):
+		conditions["company"] = filters["company"]
+	if filters.get("customer"):
+		conditions["customer"] = filters["customer"]
 
-	if filters.get("from_date"): conditions["posting_date"] = (">=", filters["from_date"])
-	if filters.get("to_date"): conditions["posting_date"] = ("<=", filters["to_date"])
+	if filters.get("from_date"):
+		conditions["posting_date"] = (">=", filters["from_date"])
+	if filters.get("to_date"):
+		conditions["posting_date"] = ("<=", filters["to_date"])
 
 	if filters.get("from_date") and filters.get("to_date"):
 		conditions["posting_date"] = ("between", [filters.get("from_date"), filters.get("to_date")])
@@ -119,10 +127,9 @@
 	import zipfile
 
 	zip_stream = io.BytesIO()
-	with zipfile.ZipFile(zip_stream, 'w', zipfile.ZIP_DEFLATED) as zip_file:
+	with zipfile.ZipFile(zip_stream, "w", zipfile.ZIP_DEFLATED) as zip_file:
 		for file in files:
-			file_path = frappe.utils.get_files_path(
-				file.file_name, is_private=file.is_private)
+			file_path = frappe.utils.get_files_path(file.file_name, is_private=file.is_private)
 
 			zip_file.write(file_path, arcname=file.file_name)
 
@@ -131,20 +138,21 @@
 	frappe.local.response.type = "download"
 	zip_stream.close()
 
+
 def get_invoice_summary(items, taxes):
 	summary_data = frappe._dict()
 	for tax in taxes:
-		#Include only VAT charges.
+		# Include only VAT charges.
 		if tax.charge_type == "Actual":
 			continue
 
-		#Charges to appear as items in the e-invoice.
+		# Charges to appear as items in the e-invoice.
 		if tax.charge_type in ["On Previous Row Total", "On Previous Row Amount"]:
 			reference_row = next((row for row in taxes if row.idx == int(tax.row_id or 0)), None)
 			if reference_row:
 				items.append(
 					frappe._dict(
-						idx=len(items)+1,
+						idx=len(items) + 1,
 						item_code=reference_row.description,
 						item_name=reference_row.description,
 						description=reference_row.description,
@@ -157,11 +165,11 @@
 						net_amount=reference_row.tax_amount,
 						taxable_amount=reference_row.tax_amount,
 						item_tax_rate={tax.account_head: tax.rate},
-						charges=True
+						charges=True,
 					)
 				)
 
-		#Check item tax rates if tax rate is zero.
+		# Check item tax rates if tax rate is zero.
 		if tax.rate == 0:
 			for item in items:
 				item_tax_rate = item.item_tax_rate
@@ -171,8 +179,15 @@
 				if item_tax_rate and tax.account_head in item_tax_rate:
 					key = cstr(item_tax_rate[tax.account_head])
 					if key not in summary_data:
-						summary_data.setdefault(key, {"tax_amount": 0.0, "taxable_amount": 0.0,
-							"tax_exemption_reason": "", "tax_exemption_law": ""})
+						summary_data.setdefault(
+							key,
+							{
+								"tax_amount": 0.0,
+								"taxable_amount": 0.0,
+								"tax_exemption_reason": "",
+								"tax_exemption_law": "",
+							},
+						)
 
 					summary_data[key]["tax_amount"] += item.tax_amount
 					summary_data[key]["taxable_amount"] += item.net_amount
@@ -180,93 +195,138 @@
 						summary_data[key]["tax_exemption_reason"] = tax.tax_exemption_reason
 						summary_data[key]["tax_exemption_law"] = tax.tax_exemption_law
 
-			if summary_data.get("0.0") and tax.charge_type in ["On Previous Row Total",
-				"On Previous Row Amount"]:
+			if summary_data.get("0.0") and tax.charge_type in [
+				"On Previous Row Total",
+				"On Previous Row Amount",
+			]:
 				summary_data[key]["taxable_amount"] = tax.total
 
-			if summary_data == {}: #Implies that Zero VAT has not been set on any item.
-				summary_data.setdefault("0.0", {"tax_amount": 0.0, "taxable_amount": tax.total,
-					"tax_exemption_reason": tax.tax_exemption_reason, "tax_exemption_law": tax.tax_exemption_law})
+			if summary_data == {}:  # Implies that Zero VAT has not been set on any item.
+				summary_data.setdefault(
+					"0.0",
+					{
+						"tax_amount": 0.0,
+						"taxable_amount": tax.total,
+						"tax_exemption_reason": tax.tax_exemption_reason,
+						"tax_exemption_law": tax.tax_exemption_law,
+					},
+				)
 
 		else:
 			item_wise_tax_detail = json.loads(tax.item_wise_tax_detail)
-			for rate_item in [tax_item for tax_item in item_wise_tax_detail.items() if tax_item[1][0] == tax.rate]:
+			for rate_item in [
+				tax_item for tax_item in item_wise_tax_detail.items() if tax_item[1][0] == tax.rate
+			]:
 				key = cstr(tax.rate)
-				if not summary_data.get(key): summary_data.setdefault(key, {"tax_amount": 0.0, "taxable_amount": 0.0})
+				if not summary_data.get(key):
+					summary_data.setdefault(key, {"tax_amount": 0.0, "taxable_amount": 0.0})
 				summary_data[key]["tax_amount"] += rate_item[1][1]
-				summary_data[key]["taxable_amount"] += sum([item.net_amount for item in items if item.item_code == rate_item[0]])
+				summary_data[key]["taxable_amount"] += sum(
+					[item.net_amount for item in items if item.item_code == rate_item[0]]
+				)
 
 			for item in items:
 				key = cstr(tax.rate)
 				if item.get("charges"):
-					if not summary_data.get(key): summary_data.setdefault(key, {"taxable_amount": 0.0})
+					if not summary_data.get(key):
+						summary_data.setdefault(key, {"taxable_amount": 0.0})
 					summary_data[key]["taxable_amount"] += item.taxable_amount
 
 	return summary_data
 
-#Preflight for successful e-invoice export.
+
+# Preflight for successful e-invoice export.
 def sales_invoice_validate(doc):
-	#Validate company
-	if doc.doctype != 'Sales Invoice':
+	# Validate company
+	if doc.doctype != "Sales Invoice":
 		return
 
 	if not doc.company_address:
-		frappe.throw(_("Please set an Address on the Company '%s'" % doc.company), title=_("E-Invoicing Information Missing"))
+		frappe.throw(
+			_("Please set an Address on the Company '%s'" % doc.company),
+			title=_("E-Invoicing Information Missing"),
+		)
 	else:
 		validate_address(doc.company_address)
 
-	company_fiscal_regime = frappe.get_cached_value("Company", doc.company, 'fiscal_regime')
+	company_fiscal_regime = frappe.get_cached_value("Company", doc.company, "fiscal_regime")
 	if not company_fiscal_regime:
-		frappe.throw(_("Fiscal Regime is mandatory, kindly set the fiscal regime in the company {0}")
-			.format(doc.company))
+		frappe.throw(
+			_("Fiscal Regime is mandatory, kindly set the fiscal regime in the company {0}").format(
+				doc.company
+			)
+		)
 	else:
 		doc.company_fiscal_regime = company_fiscal_regime
 
-	doc.company_tax_id = frappe.get_cached_value("Company", doc.company, 'tax_id')
-	doc.company_fiscal_code = frappe.get_cached_value("Company", doc.company, 'fiscal_code')
+	doc.company_tax_id = frappe.get_cached_value("Company", doc.company, "tax_id")
+	doc.company_fiscal_code = frappe.get_cached_value("Company", doc.company, "fiscal_code")
 	if not doc.company_tax_id and not doc.company_fiscal_code:
-		frappe.throw(_("Please set either the Tax ID or Fiscal Code on Company '%s'" % doc.company), title=_("E-Invoicing Information Missing"))
+		frappe.throw(
+			_("Please set either the Tax ID or Fiscal Code on Company '%s'" % doc.company),
+			title=_("E-Invoicing Information Missing"),
+		)
 
-	#Validate customer details
+	# Validate customer details
 	customer = frappe.get_doc("Customer", doc.customer)
 
 	if customer.customer_type == "Individual":
 		doc.customer_fiscal_code = customer.fiscal_code
 		if not doc.customer_fiscal_code:
-			frappe.throw(_("Please set Fiscal Code for the customer '%s'" % doc.customer), title=_("E-Invoicing Information Missing"))
+			frappe.throw(
+				_("Please set Fiscal Code for the customer '%s'" % doc.customer),
+				title=_("E-Invoicing Information Missing"),
+			)
 	else:
 		if customer.is_public_administration:
 			doc.customer_fiscal_code = customer.fiscal_code
 			if not doc.customer_fiscal_code:
-				frappe.throw(_("Please set Fiscal Code for the public administration '%s'" % doc.customer), title=_("E-Invoicing Information Missing"))
+				frappe.throw(
+					_("Please set Fiscal Code for the public administration '%s'" % doc.customer),
+					title=_("E-Invoicing Information Missing"),
+				)
 		else:
 			doc.tax_id = customer.tax_id
 			if not doc.tax_id:
-				frappe.throw(_("Please set Tax ID for the customer '%s'" % doc.customer), title=_("E-Invoicing Information Missing"))
+				frappe.throw(
+					_("Please set Tax ID for the customer '%s'" % doc.customer),
+					title=_("E-Invoicing Information Missing"),
+				)
 
 	if not doc.customer_address:
-	 	frappe.throw(_("Please set the Customer Address"), title=_("E-Invoicing Information Missing"))
+		frappe.throw(_("Please set the Customer Address"), title=_("E-Invoicing Information Missing"))
 	else:
 		validate_address(doc.customer_address)
 
 	if not len(doc.taxes):
-		frappe.throw(_("Please set at least one row in the Taxes and Charges Table"), title=_("E-Invoicing Information Missing"))
+		frappe.throw(
+			_("Please set at least one row in the Taxes and Charges Table"),
+			title=_("E-Invoicing Information Missing"),
+		)
 	else:
 		for row in doc.taxes:
 			if row.rate == 0 and row.tax_amount == 0 and not row.tax_exemption_reason:
-				frappe.throw(_("Row {0}: Please set at Tax Exemption Reason in Sales Taxes and Charges").format(row.idx),
-					title=_("E-Invoicing Information Missing"))
+				frappe.throw(
+					_("Row {0}: Please set at Tax Exemption Reason in Sales Taxes and Charges").format(row.idx),
+					title=_("E-Invoicing Information Missing"),
+				)
 
 	for schedule in doc.payment_schedule:
 		if schedule.mode_of_payment and not schedule.mode_of_payment_code:
-			schedule.mode_of_payment_code = frappe.get_cached_value('Mode of Payment',
-				schedule.mode_of_payment, 'mode_of_payment_code')
+			schedule.mode_of_payment_code = frappe.get_cached_value(
+				"Mode of Payment", schedule.mode_of_payment, "mode_of_payment_code"
+			)
 
-#Ensure payment details are valid for e-invoice.
+
+# Ensure payment details are valid for e-invoice.
 def sales_invoice_on_submit(doc, method):
-	#Validate payment details
-	if get_company_country(doc.company) not in ['Italy',
-		'Italia', 'Italian Republic', 'Repubblica Italiana']:
+	# Validate payment details
+	if get_company_country(doc.company) not in [
+		"Italy",
+		"Italia",
+		"Italian Republic",
+		"Repubblica Italiana",
+	]:
 		return
 
 	if not len(doc.payment_schedule):
@@ -274,38 +334,53 @@
 	else:
 		for schedule in doc.payment_schedule:
 			if not schedule.mode_of_payment:
-				frappe.throw(_("Row {0}: Please set the Mode of Payment in Payment Schedule").format(schedule.idx),
-					title=_("E-Invoicing Information Missing"))
-			elif not frappe.db.get_value("Mode of Payment", schedule.mode_of_payment, "mode_of_payment_code"):
-				frappe.throw(_("Row {0}: Please set the correct code on Mode of Payment {1}").format(schedule.idx, schedule.mode_of_payment),
-					title=_("E-Invoicing Information Missing"))
+				frappe.throw(
+					_("Row {0}: Please set the Mode of Payment in Payment Schedule").format(schedule.idx),
+					title=_("E-Invoicing Information Missing"),
+				)
+			elif not frappe.db.get_value(
+				"Mode of Payment", schedule.mode_of_payment, "mode_of_payment_code"
+			):
+				frappe.throw(
+					_("Row {0}: Please set the correct code on Mode of Payment {1}").format(
+						schedule.idx, schedule.mode_of_payment
+					),
+					title=_("E-Invoicing Information Missing"),
+				)
 
 	prepare_and_attach_invoice(doc)
 
+
 def prepare_and_attach_invoice(doc, replace=False):
 	progressive_name, progressive_number = get_progressive_name_and_number(doc, replace)
 
 	invoice = prepare_invoice(doc, progressive_number)
 	item_meta = frappe.get_meta("Sales Invoice Item")
 
-	invoice_xml = frappe.render_template('erpnext/regional/italy/e-invoice.xml',
-		context={"doc": invoice, "item_meta": item_meta}, is_path=True)
+	invoice_xml = frappe.render_template(
+		"erpnext/regional/italy/e-invoice.xml",
+		context={"doc": invoice, "item_meta": item_meta},
+		is_path=True,
+	)
 
 	invoice_xml = invoice_xml.replace("&", "&amp;")
 
 	xml_filename = progressive_name + ".xml"
 
-	_file = frappe.get_doc({
-		"doctype": "File",
-		"file_name": xml_filename,
-		"attached_to_doctype": doc.doctype,
-		"attached_to_name": doc.name,
-		"is_private": True,
-		"content": invoice_xml
-	})
+	_file = frappe.get_doc(
+		{
+			"doctype": "File",
+			"file_name": xml_filename,
+			"attached_to_doctype": doc.doctype,
+			"attached_to_name": doc.name,
+			"is_private": True,
+			"content": invoice_xml,
+		}
+	)
 	_file.save()
 	return _file
 
+
 @frappe.whitelist()
 def generate_single_invoice(docname):
 	doc = frappe.get_doc("Sales Invoice", docname)
@@ -314,17 +389,24 @@
 	e_invoice = prepare_and_attach_invoice(doc, True)
 	return e_invoice.file_url
 
+
 # Delete e-invoice attachment on cancel.
 def sales_invoice_on_cancel(doc, method):
-	if get_company_country(doc.company) not in ['Italy',
-		'Italia', 'Italian Republic', 'Repubblica Italiana']:
+	if get_company_country(doc.company) not in [
+		"Italy",
+		"Italia",
+		"Italian Republic",
+		"Repubblica Italiana",
+	]:
 		return
 
 	for attachment in get_e_invoice_attachments(doc):
 		remove_file(attachment.name, attached_to_doctype=doc.doctype, attached_to_name=doc.name)
 
+
 def get_company_country(company):
-	return frappe.get_cached_value('Company', company, 'country')
+	return frappe.get_cached_value("Company", company, "country")
+
 
 def get_e_invoice_attachments(invoices):
 	if not isinstance(invoices, list):
@@ -338,16 +420,14 @@
 			invoice.company_tax_id
 			if invoice.company_tax_id.startswith("IT")
 			else "IT" + invoice.company_tax_id
-		) for invoice in invoices
+		)
+		for invoice in invoices
 	}
 
 	attachments = frappe.get_all(
 		"File",
 		fields=("name", "file_name", "attached_to_name", "is_private"),
-		filters= {
-			"attached_to_name": ('in', tax_id_map),
-			"attached_to_doctype": 'Sales Invoice'
-		}
+		filters={"attached_to_name": ("in", tax_id_map), "attached_to_doctype": "Sales Invoice"},
 	)
 
 	out = []
@@ -355,21 +435,24 @@
 		if (
 			attachment.file_name
 			and attachment.file_name.endswith(".xml")
-			and attachment.file_name.startswith(
-				tax_id_map.get(attachment.attached_to_name))
+			and attachment.file_name.startswith(tax_id_map.get(attachment.attached_to_name))
 		):
 			out.append(attachment)
 
 	return out
 
+
 def validate_address(address_name):
 	fields = ["pincode", "city", "country_code"]
 	data = frappe.get_cached_value("Address", address_name, fields, as_dict=1) or {}
 
 	for field in fields:
 		if not data.get(field):
-			frappe.throw(_("Please set {0} for address {1}").format(field.replace('-',''), address_name),
-				title=_("E-Invoicing Information Missing"))
+			frappe.throw(
+				_("Please set {0} for address {1}").format(field.replace("-", ""), address_name),
+				title=_("E-Invoicing Information Missing"),
+			)
+
 
 def get_unamended_name(doc):
 	attributes = ["naming_series", "amended_from"]
@@ -382,6 +465,7 @@
 	else:
 		return doc.name
 
+
 def get_progressive_name_and_number(doc, replace=False):
 	if replace:
 		for attachment in get_e_invoice_attachments(doc):
@@ -389,24 +473,30 @@
 			filename = attachment.file_name.split(".xml")[0]
 			return filename, filename.split("_")[1]
 
-	company_tax_id = doc.company_tax_id if doc.company_tax_id.startswith("IT") else "IT" + doc.company_tax_id
+	company_tax_id = (
+		doc.company_tax_id if doc.company_tax_id.startswith("IT") else "IT" + doc.company_tax_id
+	)
 	progressive_name = frappe.model.naming.make_autoname(company_tax_id + "_.#####")
 	progressive_number = progressive_name.split("_")[1]
 
 	return progressive_name, progressive_number
 
+
 def set_state_code(doc, method):
-	if doc.get('country_code'):
+	if doc.get("country_code"):
 		doc.country_code = doc.country_code.upper()
 
-	if not doc.get('state'):
+	if not doc.get("state"):
 		return
 
-	if not (hasattr(doc, "state_code") and doc.country in ["Italy", "Italia", "Italian Republic", "Repubblica Italiana"]):
+	if not (
+		hasattr(doc, "state_code")
+		and doc.country in ["Italy", "Italia", "Italian Republic", "Repubblica Italiana"]
+	):
 		return
 
-	state_codes_lower = {key.lower():value for key,value in state_codes.items()}
+	state_codes_lower = {key.lower(): value for key, value in state_codes.items()}
 
-	state = doc.get('state','').lower()
+	state = doc.get("state", "").lower()
 	if state_codes_lower.get(state):
 		doc.state_code = state_codes_lower.get(state)
diff --git a/erpnext/regional/report/datev/__init__.py b/erpnext/regional/report/datev/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/report/datev/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/report/datev/datev.js b/erpnext/regional/report/datev/datev.js
deleted file mode 100644
index 03c729e..0000000
--- a/erpnext/regional/report/datev/datev.js
+++ /dev/null
@@ -1,56 +0,0 @@
-frappe.query_reports["DATEV"] = {
-	"filters": [
-		{
-			"fieldname": "company",
-			"label": __("Company"),
-			"fieldtype": "Link",
-			"options": "Company",
-			"default": frappe.defaults.get_user_default("Company") || frappe.defaults.get_global_default("Company"),
-			"reqd": 1
-		},
-		{
-			"fieldname": "from_date",
-			"label": __("From Date"),
-			"default": moment().subtract(1, 'month').startOf('month').format(),
-			"fieldtype": "Date",
-			"reqd": 1
-		},
-		{
-			"fieldname": "to_date",
-			"label": __("To Date"),
-			"default": moment().subtract(1, 'month').endOf('month').format(),
-			"fieldtype": "Date",
-			"reqd": 1
-		},
-		{
-			"fieldname": "voucher_type",
-			"label": __("Voucher Type"),
-			"fieldtype": "Select",
-			"options": "\nSales Invoice\nPurchase Invoice\nPayment Entry\nExpense Claim\nPayroll Entry\nBank Reconciliation\nAsset\nStock Entry"
-		}
-	],
-	onload: function(query_report) {
-		let company = frappe.query_report.get_filter_value('company');
-		frappe.db.exists('DATEV Settings', company).then((settings_exist) => {
-			if (!settings_exist) {
-				frappe.confirm(__('DATEV Settings for your Company are missing. Would you like to create them now?'),
-					() => frappe.new_doc('DATEV Settings', {'company': company})
-				);
-			}
-		});
-
-		query_report.page.add_menu_item(__("Download DATEV File"), () => {
-			const filters = encodeURIComponent(
-				JSON.stringify(
-					query_report.get_values()
-				)
-			);
-			window.open(`/api/method/erpnext.regional.report.datev.datev.download_datev_csv?filters=${filters}`);
-		});
-
-		query_report.page.add_menu_item(__("Change DATEV Settings"), () => {
-			let company = frappe.query_report.get_filter_value('company'); // read company from filters again – it might have changed by now.
-			frappe.set_route('Form', 'DATEV Settings', company);
-		});
-	}
-};
diff --git a/erpnext/regional/report/datev/datev.json b/erpnext/regional/report/datev/datev.json
deleted file mode 100644
index 94e3960..0000000
--- a/erpnext/regional/report/datev/datev.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "add_total_row": 0,
- "columns": [],
- "creation": "2019-04-24 08:45:16.650129",
- "disable_prepared_report": 0,
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "filters": [],
- "idx": 0,
- "is_standard": "Yes",
- "modified": "2021-04-06 12:23:00.379517",
- "modified_by": "Administrator",
- "module": "Regional",
- "name": "DATEV",
- "owner": "Administrator",
- "prepared_report": 0,
- "ref_doctype": "GL Entry",
- "report_name": "DATEV",
- "report_type": "Script Report",
- "roles": []
-}
\ No newline at end of file
diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py
deleted file mode 100644
index beac7ed..0000000
--- a/erpnext/regional/report/datev/datev.py
+++ /dev/null
@@ -1,586 +0,0 @@
-"""
-Provide a report and downloadable CSV according to the German DATEV format.
-
-- Query report showing only the columns that contain data, formatted nicely for
-	dispay to the user.
-- CSV download functionality `download_datev_csv` that provides a CSV file with
-	all required columns. Used to import the data into the DATEV Software.
-"""
-
-import json
-
-import frappe
-from frappe import _
-
-from erpnext.accounts.utils import get_fiscal_year
-from erpnext.regional.germany.utils.datev.datev_constants import (
-	AccountNames,
-	DebtorsCreditors,
-	Transactions,
-)
-from erpnext.regional.germany.utils.datev.datev_csv import get_datev_csv, zip_and_download
-
-COLUMNS = [
-	{
-		"label": "Umsatz (ohne Soll/Haben-Kz)",
-		"fieldname": "Umsatz (ohne Soll/Haben-Kz)",
-		"fieldtype": "Currency",
-		"width": 100
-	},
-	{
-		"label": "Soll/Haben-Kennzeichen",
-		"fieldname": "Soll/Haben-Kennzeichen",
-		"fieldtype": "Data",
-		"width": 100
-	},
-	{
-		"label": "Konto",
-		"fieldname": "Konto",
-		"fieldtype": "Data",
-		"width": 100
-	},
-	{
-		"label": "Gegenkonto (ohne BU-Schlüssel)",
-		"fieldname": "Gegenkonto (ohne BU-Schlüssel)",
-		"fieldtype": "Data",
-		"width": 100
-	},
-	{
-		"label": "BU-Schlüssel",
-		"fieldname": "BU-Schlüssel",
-		"fieldtype": "Data",
-		"width": 100
-	},
-	{
-		"label": "Belegdatum",
-		"fieldname": "Belegdatum",
-		"fieldtype": "Date",
-		"width": 100
-	},
-	{
-		"label": "Belegfeld 1",
-		"fieldname": "Belegfeld 1",
-		"fieldtype": "Data",
-		"width": 150
-	},
-	{
-		"label": "Buchungstext",
-		"fieldname": "Buchungstext",
-		"fieldtype": "Text",
-		"width": 300
-	},
-	{
-		"label": "Beleginfo - Art 1",
-		"fieldname": "Beleginfo - Art 1",
-		"fieldtype": "Link",
-		"options": "DocType",
-		"width": 100
-	},
-	{
-		"label": "Beleginfo - Inhalt 1",
-		"fieldname": "Beleginfo - Inhalt 1",
-		"fieldtype": "Dynamic Link",
-		"options": "Beleginfo - Art 1",
-		"width": 150
-	},
-	{
-		"label": "Beleginfo - Art 2",
-		"fieldname": "Beleginfo - Art 2",
-		"fieldtype": "Link",
-		"options": "DocType",
-		"width": 100
-	},
-	{
-		"label": "Beleginfo - Inhalt 2",
-		"fieldname": "Beleginfo - Inhalt 2",
-		"fieldtype": "Dynamic Link",
-		"options": "Beleginfo - Art 2",
-		"width": 150
-	},
-	{
-		"label": "Beleginfo - Art 3",
-		"fieldname": "Beleginfo - Art 3",
-		"fieldtype": "Link",
-		"options": "DocType",
-		"width": 100
-	},
-	{
-		"label": "Beleginfo - Inhalt 3",
-		"fieldname": "Beleginfo - Inhalt 3",
-		"fieldtype": "Dynamic Link",
-		"options": "Beleginfo - Art 3",
-		"width": 150
-	},
-	{
-		"label": "Beleginfo - Art 4",
-		"fieldname": "Beleginfo - Art 4",
-		"fieldtype": "Data",
-		"width": 100
-	},
-	{
-		"label": "Beleginfo - Inhalt 4",
-		"fieldname": "Beleginfo - Inhalt 4",
-		"fieldtype": "Data",
-		"width": 150
-	},
-	{
-		"label": "Beleginfo - Art 5",
-		"fieldname": "Beleginfo - Art 5",
-		"fieldtype": "Data",
-		"width": 150
-	},
-	{
-		"label": "Beleginfo - Inhalt 5",
-		"fieldname": "Beleginfo - Inhalt 5",
-		"fieldtype": "Data",
-		"width": 100
-	},
-	{
-		"label": "Beleginfo - Art 6",
-		"fieldname": "Beleginfo - Art 6",
-		"fieldtype": "Data",
-		"width": 150
-	},
-	{
-		"label": "Beleginfo - Inhalt 6",
-		"fieldname": "Beleginfo - Inhalt 6",
-		"fieldtype": "Date",
-		"width": 100
-	},
-	{
-		"label": "Fälligkeit",
-		"fieldname": "Fälligkeit",
-		"fieldtype": "Date",
-		"width": 100
-	}
-]
-
-
-def execute(filters=None):
-	"""Entry point for frappe."""
-	data = []
-	if filters and validate(filters):
-		fn = 'temporary_against_account_number'
-		filters[fn] = frappe.get_value('DATEV Settings', filters.get('company'), fn)
-		data = get_transactions(filters, as_dict=0)
-
-	return COLUMNS, data
-
-
-def validate(filters):
-	"""Make sure all mandatory filters and settings are present."""
-	company = filters.get('company')
-	if not company:
-		frappe.throw(_('<b>Company</b> is a mandatory filter.'))
-
-	from_date = filters.get('from_date')
-	if not from_date:
-		frappe.throw(_('<b>From Date</b> is a mandatory filter.'))
-
-	to_date = filters.get('to_date')
-	if not to_date:
-		frappe.throw(_('<b>To Date</b> is a mandatory filter.'))
-
-	validate_fiscal_year(from_date, to_date, company)
-
-	if not frappe.db.exists('DATEV Settings', filters.get('company')):
-		msg = 'Please create DATEV Settings for Company {}'.format(filters.get('company'))
-		frappe.log_error(msg, title='DATEV Settings missing')
-		return False
-
-	return True
-
-
-def validate_fiscal_year(from_date, to_date, company):
-	from_fiscal_year = get_fiscal_year(date=from_date, company=company)
-	to_fiscal_year = get_fiscal_year(date=to_date, company=company)
-	if from_fiscal_year != to_fiscal_year:
-		frappe.throw(_('Dates {} and {} are not in the same fiscal year.').format(from_date, to_date))
-
-
-def get_transactions(filters, as_dict=1):
-	def run(params_method, filters):
-		extra_fields, extra_joins, extra_filters = params_method(filters)
-		return run_query(filters, extra_fields, extra_joins, extra_filters, as_dict=as_dict)
-
-	def sort_by(row):
-		# "Belegdatum" is in the fifth column when list format is used
-		return row["Belegdatum" if as_dict else 5]
-
-	type_map = {
-		# specific query methods for some voucher types
-		"Payment Entry": get_payment_entry_params,
-		"Sales Invoice": get_sales_invoice_params,
-		"Purchase Invoice": get_purchase_invoice_params
-	}
-
-	only_voucher_type = filters.get("voucher_type")
-	transactions = []
-
-	for voucher_type, get_voucher_params in type_map.items():
-		if only_voucher_type and only_voucher_type != voucher_type:
-			continue
-
-		transactions.extend(run(params_method=get_voucher_params, filters=filters))
-
-	if not only_voucher_type or only_voucher_type not in type_map:
-		# generic query method for all other voucher types
-		filters["exclude_voucher_types"] = type_map.keys()
-		transactions.extend(run(params_method=get_generic_params, filters=filters))
-
-	return sorted(transactions, key=sort_by)
-
-
-def get_payment_entry_params(filters):
-	extra_fields = """
-		, 'Zahlungsreferenz' as 'Beleginfo - Art 5'
-		, pe.reference_no as 'Beleginfo - Inhalt 5'
-		, 'Buchungstag' as 'Beleginfo - Art 6'
-		, pe.reference_date as 'Beleginfo - Inhalt 6'
-		, '' as 'Fälligkeit'
-	"""
-
-	extra_joins = """
-		LEFT JOIN `tabPayment Entry` pe
-		ON gl.voucher_no = pe.name
-	"""
-
-	extra_filters = """
-		AND gl.voucher_type = 'Payment Entry'
-	"""
-
-	return extra_fields, extra_joins, extra_filters
-
-
-def get_sales_invoice_params(filters):
-	extra_fields = """
-		, '' as 'Beleginfo - Art 5'
-		, '' as 'Beleginfo - Inhalt 5'
-		, '' as 'Beleginfo - Art 6'
-		, '' as 'Beleginfo - Inhalt 6'
-		, si.due_date as 'Fälligkeit'
-	"""
-
-	extra_joins = """
-		LEFT JOIN `tabSales Invoice` si
-		ON gl.voucher_no = si.name
-	"""
-
-	extra_filters = """
-		AND gl.voucher_type = 'Sales Invoice'
-	"""
-
-	return extra_fields, extra_joins, extra_filters
-
-
-def get_purchase_invoice_params(filters):
-	extra_fields = """
-		, 'Lieferanten-Rechnungsnummer' as 'Beleginfo - Art 5'
-		, pi.bill_no as 'Beleginfo - Inhalt 5'
-		, 'Lieferanten-Rechnungsdatum' as 'Beleginfo - Art 6'
-		, pi.bill_date as 'Beleginfo - Inhalt 6'
-		, pi.due_date as 'Fälligkeit'
-	"""
-
-	extra_joins = """
-		LEFT JOIN `tabPurchase Invoice` pi
-		ON gl.voucher_no = pi.name
-	"""
-
-	extra_filters = """
-		AND gl.voucher_type = 'Purchase Invoice'
-	"""
-
-	return extra_fields, extra_joins, extra_filters
-
-
-def get_generic_params(filters):
-	# produce empty fields so all rows will have the same length
-	extra_fields = """
-		, '' as 'Beleginfo - Art 5'
-		, '' as 'Beleginfo - Inhalt 5'
-		, '' as 'Beleginfo - Art 6'
-		, '' as 'Beleginfo - Inhalt 6'
-		, '' as 'Fälligkeit'
-	"""
-	extra_joins = ""
-
-	if filters.get("exclude_voucher_types"):
-		# exclude voucher types that are queried by a dedicated method
-		exclude = "({})".format(', '.join("'{}'".format(key) for key in filters.get("exclude_voucher_types")))
-		extra_filters = "AND gl.voucher_type NOT IN {}".format(exclude)
-
-	# if voucher type filter is set, allow only this type
-	if filters.get("voucher_type"):
-		extra_filters += " AND gl.voucher_type = %(voucher_type)s"
-
-	return extra_fields, extra_joins, extra_filters
-
-
-def run_query(filters, extra_fields, extra_joins, extra_filters, as_dict=1):
-	"""
-	Get a list of accounting entries.
-
-	Select GL Entries joined with Account and Party Account in order to get the
-	account numbers. Returns a list of accounting entries.
-
-	Arguments:
-	filters -- dict of filters to be passed to the sql query
-	as_dict -- return as list of dicts [0,1]
-	"""
-	query = """
-		SELECT
-
-			/* either debit or credit amount; always positive */
-			case gl.debit when 0 then gl.credit else gl.debit end as 'Umsatz (ohne Soll/Haben-Kz)',
-
-			/* 'H' when credit, 'S' when debit */
-			case gl.debit when 0 then 'H' else 'S' end as 'Soll/Haben-Kennzeichen',
-
-			/* account number or, if empty, party account number */
-			acc.account_number as 'Konto',
-
-			/* against number or, if empty, party against number */
-			%(temporary_against_account_number)s as 'Gegenkonto (ohne BU-Schlüssel)',
-
-			/* disable automatic VAT deduction */
-			'40' as 'BU-Schlüssel',
-
-			gl.posting_date as 'Belegdatum',
-			gl.voucher_no as 'Belegfeld 1',
-			REPLACE(LEFT(gl.remarks, 60), '\n', ' ') as 'Buchungstext',
-			gl.voucher_type as 'Beleginfo - Art 1',
-			gl.voucher_no as 'Beleginfo - Inhalt 1',
-			gl.against_voucher_type as 'Beleginfo - Art 2',
-			gl.against_voucher as 'Beleginfo - Inhalt 2',
-			gl.party_type as 'Beleginfo - Art 3',
-			gl.party as 'Beleginfo - Inhalt 3',
-			case gl.party_type when 'Customer' then 'Debitorennummer' when 'Supplier' then 'Kreditorennummer' else NULL end as 'Beleginfo - Art 4',
-			par.debtor_creditor_number as 'Beleginfo - Inhalt 4'
-
-			{extra_fields}
-
-		FROM `tabGL Entry` gl
-
-			/* Kontonummer */
-			LEFT JOIN `tabAccount` acc
-			ON gl.account = acc.name
-
-			LEFT JOIN `tabParty Account` par
-			ON par.parent = gl.party
-			AND par.parenttype = gl.party_type
-			AND par.company = %(company)s
-
-			{extra_joins}
-
-		WHERE gl.company = %(company)s
-		AND DATE(gl.posting_date) >= %(from_date)s
-		AND DATE(gl.posting_date) <= %(to_date)s
-
-		{extra_filters}
-
-		ORDER BY 'Belegdatum', gl.voucher_no""".format(
-			extra_fields=extra_fields,
-			extra_joins=extra_joins,
-			extra_filters=extra_filters
-		)
-
-	gl_entries = frappe.db.sql(query, filters, as_dict=as_dict)
-
-	return gl_entries
-
-
-def get_customers(filters):
-	"""
-	Get a list of Customers.
-
-	Arguments:
-	filters -- dict of filters to be passed to the sql query
-	"""
-	return frappe.db.sql("""
-		SELECT
-
-			par.debtor_creditor_number as 'Konto',
-			CASE cus.customer_type
-				WHEN 'Company' THEN cus.customer_name
-				ELSE null
-				END as 'Name (Adressatentyp Unternehmen)',
-			CASE cus.customer_type
-				WHEN 'Individual' THEN TRIM(SUBSTR(cus.customer_name, LOCATE(' ', cus.customer_name)))
-				ELSE null
-				END as 'Name (Adressatentyp natürl. Person)',
-			CASE cus.customer_type
-				WHEN 'Individual' THEN SUBSTRING_INDEX(SUBSTRING_INDEX(cus.customer_name, ' ', 1), ' ', -1)
-				ELSE null
-				END as 'Vorname (Adressatentyp natürl. Person)',
-			CASE cus.customer_type
-				WHEN 'Individual' THEN '1'
-				WHEN 'Company' THEN '2'
-				ELSE '0'
-				END as 'Adressatentyp',
-			adr.address_line1 as 'Straße',
-			adr.pincode as 'Postleitzahl',
-			adr.city as 'Ort',
-			UPPER(country.code) as 'Land',
-			adr.address_line2 as 'Adresszusatz',
-			adr.email_id as 'E-Mail',
-			adr.phone as 'Telefon',
-			adr.fax as 'Fax',
-			cus.website as 'Internet',
-			cus.tax_id as 'Steuernummer'
-
-		FROM `tabCustomer` cus
-
-			left join `tabParty Account` par
-			on par.parent = cus.name
-			and par.parenttype = 'Customer'
-			and par.company = %(company)s
-
-			left join `tabDynamic Link` dyn_adr
-			on dyn_adr.link_name = cus.name
-			and dyn_adr.link_doctype = 'Customer'
-			and dyn_adr.parenttype = 'Address'
-
-			left join `tabAddress` adr
-			on adr.name = dyn_adr.parent
-			and adr.is_primary_address = '1'
-
-			left join `tabCountry` country
-			on country.name = adr.country
-
-		WHERE adr.is_primary_address = '1'
-		""", filters, as_dict=1)
-
-
-def get_suppliers(filters):
-	"""
-	Get a list of Suppliers.
-
-	Arguments:
-	filters -- dict of filters to be passed to the sql query
-	"""
-	return frappe.db.sql("""
-		SELECT
-
-			par.debtor_creditor_number as 'Konto',
-			CASE sup.supplier_type
-				WHEN 'Company' THEN sup.supplier_name
-				ELSE null
-				END as 'Name (Adressatentyp Unternehmen)',
-			CASE sup.supplier_type
-				WHEN 'Individual' THEN TRIM(SUBSTR(sup.supplier_name, LOCATE(' ', sup.supplier_name)))
-				ELSE null
-				END as 'Name (Adressatentyp natürl. Person)',
-			CASE sup.supplier_type
-				WHEN 'Individual' THEN SUBSTRING_INDEX(SUBSTRING_INDEX(sup.supplier_name, ' ', 1), ' ', -1)
-				ELSE null
-				END as 'Vorname (Adressatentyp natürl. Person)',
-			CASE sup.supplier_type
-				WHEN 'Individual' THEN '1'
-				WHEN 'Company' THEN '2'
-				ELSE '0'
-				END as 'Adressatentyp',
-			adr.address_line1 as 'Straße',
-			adr.pincode as 'Postleitzahl',
-			adr.city as 'Ort',
-			UPPER(country.code) as 'Land',
-			adr.address_line2 as 'Adresszusatz',
-			adr.email_id as 'E-Mail',
-			adr.phone as 'Telefon',
-			adr.fax as 'Fax',
-			sup.website as 'Internet',
-			sup.tax_id as 'Steuernummer',
-			case sup.on_hold when 1 then sup.release_date else null end as 'Zahlungssperre bis'
-
-		FROM `tabSupplier` sup
-
-			left join `tabParty Account` par
-			on par.parent = sup.name
-			and par.parenttype = 'Supplier'
-			and par.company = %(company)s
-
-			left join `tabDynamic Link` dyn_adr
-			on dyn_adr.link_name = sup.name
-			and dyn_adr.link_doctype = 'Supplier'
-			and dyn_adr.parenttype = 'Address'
-
-			left join `tabAddress` adr
-			on adr.name = dyn_adr.parent
-			and adr.is_primary_address = '1'
-
-			left join `tabCountry` country
-			on country.name = adr.country
-
-		WHERE adr.is_primary_address = '1'
-		""", filters, as_dict=1)
-
-
-def get_account_names(filters):
-	return frappe.db.sql("""
-		SELECT
-
-			account_number as 'Konto',
-			LEFT(account_name, 40) as 'Kontenbeschriftung',
-			'de-DE' as 'Sprach-ID'
-
-		FROM `tabAccount`
-		WHERE company = %(company)s
-		AND is_group = 0
-		AND account_number != ''
-	""", filters, as_dict=1)
-
-
-@frappe.whitelist()
-def download_datev_csv(filters):
-	"""
-	Provide accounting entries for download in DATEV format.
-
-	Validate the filters, get the data, produce the CSV file and provide it for
-	download. Can be called like this:
-
-	GET /api/method/erpnext.regional.report.datev.datev.download_datev_csv
-
-	Arguments / Params:
-	filters -- dict of filters to be passed to the sql query
-	"""
-	if isinstance(filters, str):
-		filters = json.loads(filters)
-
-	validate(filters)
-	company = filters.get('company')
-
-	fiscal_year = get_fiscal_year(date=filters.get('from_date'), company=company)
-	filters['fiscal_year_start'] = fiscal_year[1]
-
-	# set chart of accounts used
-	coa = frappe.get_value('Company', company, 'chart_of_accounts')
-	filters['skr'] = '04' if 'SKR04' in coa else ('03' if 'SKR03' in coa else '')
-
-	datev_settings = frappe.get_doc('DATEV Settings', company)
-	filters['account_number_length'] = datev_settings.account_number_length
-	filters['temporary_against_account_number'] = datev_settings.temporary_against_account_number
-
-	transactions = get_transactions(filters)
-	account_names = get_account_names(filters)
-	customers = get_customers(filters)
-	suppliers = get_suppliers(filters)
-
-	zip_name = '{} DATEV.zip'.format(frappe.utils.datetime.date.today())
-	zip_and_download(zip_name, [
-		{
-			'file_name': 'EXTF_Buchungsstapel.csv',
-			'csv_data': get_datev_csv(transactions, filters, csv_class=Transactions)
-		},
-		{
-			'file_name': 'EXTF_Kontenbeschriftungen.csv',
-			'csv_data': get_datev_csv(account_names, filters, csv_class=AccountNames)
-		},
-		{
-			'file_name': 'EXTF_Kunden.csv',
-			'csv_data': get_datev_csv(customers, filters, csv_class=DebtorsCreditors)
-		},
-		{
-			'file_name': 'EXTF_Lieferanten.csv',
-			'csv_data': get_datev_csv(suppliers, filters, csv_class=DebtorsCreditors)
-		},
-	])
diff --git a/erpnext/regional/report/datev/test_datev.py b/erpnext/regional/report/datev/test_datev.py
deleted file mode 100644
index 052fb2a..0000000
--- a/erpnext/regional/report/datev/test_datev.py
+++ /dev/null
@@ -1,242 +0,0 @@
-import zipfile
-from io import BytesIO
-from unittest import TestCase
-
-import frappe
-from frappe.utils import cstr, now_datetime, today
-
-from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
-from erpnext.regional.germany.utils.datev.datev_constants import (
-	AccountNames,
-	DebtorsCreditors,
-	Transactions,
-)
-from erpnext.regional.germany.utils.datev.datev_csv import get_datev_csv, get_header
-from erpnext.regional.report.datev.datev import (
-	download_datev_csv,
-	get_account_names,
-	get_customers,
-	get_suppliers,
-	get_transactions,
-)
-
-
-def make_company(company_name, abbr):
-	if not frappe.db.exists("Company", company_name):
-		company = frappe.get_doc({
-			"doctype": "Company",
-			"company_name": company_name,
-			"abbr": abbr,
-			"default_currency": "EUR",
-			"country": "Germany",
-			"create_chart_of_accounts_based_on": "Standard Template",
-			"chart_of_accounts": "SKR04 mit Kontonummern"
-		})
-		company.insert()
-	else:
-		company = frappe.get_doc("Company", company_name)
-
-	# indempotent
-	company.create_default_warehouses()
-
-	if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": company.name}):
-		company.create_default_cost_center()
-
-	company.save()
-	return company
-
-def setup_fiscal_year():
-	fiscal_year = None
-	year = cstr(now_datetime().year)
-	if not frappe.db.get_value("Fiscal Year", {"year": year}, "name"):
-		try:
-			fiscal_year = frappe.get_doc({
-				"doctype": "Fiscal Year",
-				"year": year,
-				"year_start_date": "{0}-01-01".format(year),
-				"year_end_date": "{0}-12-31".format(year)
-			})
-			fiscal_year.insert()
-		except frappe.NameError:
-			pass
-
-	if fiscal_year:
-		fiscal_year.set_as_default()
-
-def make_customer_with_account(customer_name, company):
-	acc_name = frappe.db.get_value("Account", {
-			"account_name": customer_name,
-			"company": company.name
-		}, "name")
-
-	if not acc_name:
-		acc = frappe.get_doc({
-			"doctype": "Account",
-			"parent_account": "1 - Forderungen aus Lieferungen und Leistungen - _TG",
-			"account_name": customer_name,
-			"company": company.name,
-			"account_type": "Receivable",
-			"account_number": "10001"
-		})
-		acc.insert()
-		acc_name = acc.name
-
-	if not frappe.db.exists("Customer", customer_name):
-		customer = frappe.get_doc({
-			"doctype": "Customer",
-			"customer_name": customer_name,
-			"customer_type": "Company",
-			"accounts": [{
-				"company": company.name,
-				"account": acc_name
-			}]
-		})
-		customer.insert()
-	else:
-		customer = frappe.get_doc("Customer", customer_name)
-
-	return customer
-
-def make_item(item_code, company):
-	warehouse_name = frappe.db.get_value("Warehouse", {
-			"warehouse_name": "Stores",
-			"company": company.name
-		}, "name")
-
-	if not frappe.db.exists("Item", item_code):
-		item = frappe.get_doc({
-			"doctype": "Item",
-			"item_code": item_code,
-			"item_name": item_code,
-			"description": item_code,
-			"item_group": "All Item Groups",
-			"is_stock_item": 0,
-			"is_purchase_item": 0,
-			"is_customer_provided_item": 0,
-			"item_defaults": [{
-				"default_warehouse": warehouse_name,
-				"company": company.name
-			}]
-		})
-		item.insert()
-	else:
-		item = frappe.get_doc("Item", item_code)
-	return item
-
-def make_datev_settings(company):
-	if not frappe.db.exists("DATEV Settings", company.name):
-		frappe.get_doc({
-			"doctype": "DATEV Settings",
-			"client": company.name,
-			"client_number": "12345",
-			"consultant_number": "67890",
-			"temporary_against_account_number": "9999"
-		}).insert()
-
-
-class TestDatev(TestCase):
-	def setUp(self):
-		self.company = make_company("_Test GmbH", "_TG")
-		self.customer = make_customer_with_account("_Test Kunde GmbH", self.company)
-		self.filters = {
-			"company": self.company.name,
-			"from_date": today(),
-			"to_date": today(),
-			"temporary_against_account_number": "9999"
-		}
-
-		make_datev_settings(self.company)
-		item = make_item("_Test Item", self.company)
-		setup_fiscal_year()
-
-		warehouse = frappe.db.get_value("Item Default", {
-				"parent": item.name,
-				"company": self.company.name
-			}, "default_warehouse")
-
-		income_account = frappe.db.get_value("Account", {
-				"account_number": "4200",
-				"company": self.company.name
-			}, "name")
-
-		tax_account = frappe.db.get_value("Account", {
-				"account_number": "3806",
-				"company": self.company.name
-			}, "name")
-
-		si = create_sales_invoice(
-			company=self.company.name,
-			customer=self.customer.name,
-			currency=self.company.default_currency,
-			debit_to=self.customer.accounts[0].account,
-			income_account="4200 - Erlöse - _TG",
-			expense_account="6990 - Herstellungskosten - _TG",
-			cost_center=self.company.cost_center,
-			warehouse=warehouse,
-			item=item.name,
-			do_not_save=1
-		)
-
-		si.append("taxes", {
-			"charge_type": "On Net Total",
-			"account_head": tax_account,
-			"description": "Umsatzsteuer 19 %",
-			"rate": 19,
-			"cost_center": self.company.cost_center
-		})
-
-		si.cost_center = self.company.cost_center
-
-		si.save()
-		si.submit()
-
-	def test_columns(self):
-		def is_subset(get_data, allowed_keys):
-			"""
-			Validate that the dict contains only allowed keys.
-
-			Params:
-			get_data -- Function that returns a list of dicts.
-			allowed_keys -- List of allowed keys
-			"""
-			data = get_data(self.filters)
-			if data == []:
-				# No data and, therefore, no columns is okay
-				return True
-			actual_set = set(data[0].keys())
-			# allowed set must be interpreted as unicode to match the actual set
-			allowed_set = set({frappe.as_unicode(key) for key in allowed_keys})
-			return actual_set.issubset(allowed_set)
-
-		self.assertTrue(is_subset(get_transactions, Transactions.COLUMNS))
-		self.assertTrue(is_subset(get_customers, DebtorsCreditors.COLUMNS))
-		self.assertTrue(is_subset(get_suppliers, DebtorsCreditors.COLUMNS))
-		self.assertTrue(is_subset(get_account_names, AccountNames.COLUMNS))
-
-	def test_header(self):
-		self.assertTrue(Transactions.DATA_CATEGORY in get_header(self.filters, Transactions))
-		self.assertTrue(AccountNames.DATA_CATEGORY in get_header(self.filters, AccountNames))
-		self.assertTrue(DebtorsCreditors.DATA_CATEGORY in get_header(self.filters, DebtorsCreditors))
-
-	def test_csv(self):
-		test_data = [{
-			"Umsatz (ohne Soll/Haben-Kz)": 100,
-			"Soll/Haben-Kennzeichen": "H",
-			"Kontonummer": "4200",
-			"Gegenkonto (ohne BU-Schlüssel)": "10000",
-			"Belegdatum": today(),
-			"Buchungstext": "No remark",
-			"Beleginfo - Art 1": "Sales Invoice",
-			"Beleginfo - Inhalt 1": "SINV-0001"
-		}]
-		get_datev_csv(data=test_data, filters=self.filters, csv_class=Transactions)
-
-	def test_download(self):
-		"""Assert that the returned file is a ZIP file."""
-		download_datev_csv(self.filters)
-
-		# zipfile.is_zipfile() expects a file-like object
-		zip_buffer = BytesIO()
-		zip_buffer.write(frappe.response['filecontent'])
-
-		self.assertTrue(zipfile.is_zipfile(zip_buffer))
diff --git "a/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.py" "b/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.py"
index 59888ff..c75179e 100644
--- "a/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.py"
+++ "b/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.py"
@@ -26,31 +26,42 @@
 
 
 def validate_filters(filters, account_details):
-	if not filters.get('company'):
-		frappe.throw(_('{0} is mandatory').format(_('Company')))
+	if not filters.get("company"):
+		frappe.throw(_("{0} is mandatory").format(_("Company")))
 
-	if not filters.get('fiscal_year'):
-		frappe.throw(_('{0} is mandatory').format(_('Fiscal Year')))
+	if not filters.get("fiscal_year"):
+		frappe.throw(_("{0} is mandatory").format(_("Fiscal Year")))
 
 
 def set_account_currency(filters):
 
-	filters["company_currency"] = frappe.get_cached_value('Company',  filters.company,  "default_currency")
+	filters["company_currency"] = frappe.get_cached_value(
+		"Company", filters.company, "default_currency"
+	)
 
 	return filters
 
 
 def get_columns(filters):
 	columns = [
-		"JournalCode" + "::90", "JournalLib" + "::90",
-		"EcritureNum" + ":Dynamic Link:90", "EcritureDate" + "::90",
-		"CompteNum" + ":Link/Account:100", "CompteLib" + ":Link/Account:200",
-		"CompAuxNum" + "::90", "CompAuxLib" + "::90",
-		"PieceRef" + "::90", "PieceDate" + "::90",
-		"EcritureLib" + "::90", "Debit" + "::90", "Credit" + "::90",
-		"EcritureLet" + "::90", "DateLet" +
-		"::90", "ValidDate" + "::90",
-		"Montantdevise" + "::90", "Idevise" + "::90"
+		"JournalCode" + "::90",
+		"JournalLib" + "::90",
+		"EcritureNum" + ":Dynamic Link:90",
+		"EcritureDate" + "::90",
+		"CompteNum" + ":Link/Account:100",
+		"CompteLib" + ":Link/Account:200",
+		"CompAuxNum" + "::90",
+		"CompAuxLib" + "::90",
+		"PieceRef" + "::90",
+		"PieceDate" + "::90",
+		"EcritureLib" + "::90",
+		"Debit" + "::90",
+		"Credit" + "::90",
+		"EcritureLet" + "::90",
+		"DateLet" + "::90",
+		"ValidDate" + "::90",
+		"Montantdevise" + "::90",
+		"Idevise" + "::90",
 	]
 
 	return columns
@@ -66,10 +77,14 @@
 
 def get_gl_entries(filters):
 
-	group_by_condition = "group by voucher_type, voucher_no, account" \
-		if filters.get("group_by_voucher") else "group by gl.name"
+	group_by_condition = (
+		"group by voucher_type, voucher_no, account"
+		if filters.get("group_by_voucher")
+		else "group by gl.name"
+	)
 
-	gl_entries = frappe.db.sql("""
+	gl_entries = frappe.db.sql(
+		"""
 		select
 			gl.posting_date as GlPostDate, gl.name as GlName, gl.account, gl.transaction_date,
 			sum(gl.debit) as debit, sum(gl.credit) as credit,
@@ -99,8 +114,12 @@
 			left join `tabMember` mem on gl.party = mem.name
 		where gl.company=%(company)s and gl.fiscal_year=%(fiscal_year)s
 		{group_by_condition}
-		order by GlPostDate, voucher_no"""\
-		.format(group_by_condition=group_by_condition), filters, as_dict=1)
+		order by GlPostDate, voucher_no""".format(
+			group_by_condition=group_by_condition
+		),
+		filters,
+		as_dict=1,
+	)
 
 	return gl_entries
 
@@ -108,25 +127,37 @@
 def get_result_as_list(data, filters):
 	result = []
 
-	company_currency = frappe.get_cached_value('Company',  filters.company,  "default_currency")
-	accounts = frappe.get_all("Account", filters={"Company": filters.company}, fields=["name", "account_number"])
+	company_currency = frappe.get_cached_value("Company", filters.company, "default_currency")
+	accounts = frappe.get_all(
+		"Account", filters={"Company": filters.company}, fields=["name", "account_number"]
+	)
 
 	for d in data:
 
 		JournalCode = re.split("-|/|[0-9]", d.get("voucher_no"))[0]
 
-		if d.get("voucher_no").startswith("{0}-".format(JournalCode)) or d.get("voucher_no").startswith("{0}/".format(JournalCode)):
+		if d.get("voucher_no").startswith("{0}-".format(JournalCode)) or d.get("voucher_no").startswith(
+			"{0}/".format(JournalCode)
+		):
 			EcritureNum = re.split("-|/", d.get("voucher_no"))[1]
 		else:
-			EcritureNum = re.search(r"{0}(\d+)".format(JournalCode), d.get("voucher_no"), re.IGNORECASE).group(1)
+			EcritureNum = re.search(
+				r"{0}(\d+)".format(JournalCode), d.get("voucher_no"), re.IGNORECASE
+			).group(1)
 
 		EcritureDate = format_datetime(d.get("GlPostDate"), "yyyyMMdd")
 
-		account_number = [account.account_number for account in accounts if account.name == d.get("account")]
+		account_number = [
+			account.account_number for account in accounts if account.name == d.get("account")
+		]
 		if account_number[0] is not None:
-			CompteNum =  account_number[0]
+			CompteNum = account_number[0]
 		else:
-			frappe.throw(_("Account number for account {0} is not available.<br> Please setup your Chart of Accounts correctly.").format(d.get("account")))
+			frappe.throw(
+				_(
+					"Account number for account {0} is not available.<br> Please setup your Chart of Accounts correctly."
+				).format(d.get("account"))
+			)
 
 		if d.get("party_type") == "Customer":
 			CompAuxNum = d.get("cusName")
@@ -172,19 +203,45 @@
 
 		PieceDate = format_datetime(d.get("GlPostDate"), "yyyyMMdd")
 
-		debit = '{:.2f}'.format(d.get("debit")).replace(".", ",")
+		debit = "{:.2f}".format(d.get("debit")).replace(".", ",")
 
-		credit = '{:.2f}'.format(d.get("credit")).replace(".", ",")
+		credit = "{:.2f}".format(d.get("credit")).replace(".", ",")
 
 		Idevise = d.get("account_currency")
 
 		if Idevise != company_currency:
-			Montantdevise = '{:.2f}'.format(d.get("debitCurr")).replace(".", ",") if d.get("debitCurr") != 0 else '{:.2f}'.format(d.get("creditCurr")).replace(".", ",")
+			Montantdevise = (
+				"{:.2f}".format(d.get("debitCurr")).replace(".", ",")
+				if d.get("debitCurr") != 0
+				else "{:.2f}".format(d.get("creditCurr")).replace(".", ",")
+			)
 		else:
-			Montantdevise = '{:.2f}'.format(d.get("debit")).replace(".", ",") if d.get("debit") != 0 else '{:.2f}'.format(d.get("credit")).replace(".", ",")
+			Montantdevise = (
+				"{:.2f}".format(d.get("debit")).replace(".", ",")
+				if d.get("debit") != 0
+				else "{:.2f}".format(d.get("credit")).replace(".", ",")
+			)
 
-		row = [JournalCode, d.get("voucher_type"), EcritureNum, EcritureDate, CompteNum, d.get("account"), CompAuxNum, CompAuxLib,
-			   PieceRef, PieceDate, EcritureLib, debit, credit, "", "", ValidDate, Montantdevise, Idevise]
+		row = [
+			JournalCode,
+			d.get("voucher_type"),
+			EcritureNum,
+			EcritureDate,
+			CompteNum,
+			d.get("account"),
+			CompAuxNum,
+			CompAuxLib,
+			PieceRef,
+			PieceDate,
+			EcritureLib,
+			debit,
+			credit,
+			"",
+			"",
+			ValidDate,
+			Montantdevise,
+			Idevise,
+		]
 
 		result.append(row)
 
diff --git a/erpnext/regional/report/irs_1099/irs_1099.py b/erpnext/regional/report/irs_1099/irs_1099.py
index b1a5d10..66ade1f 100644
--- a/erpnext/regional/report/irs_1099/irs_1099.py
+++ b/erpnext/regional/report/irs_1099/irs_1099.py
@@ -10,7 +10,7 @@
 from frappe.utils.jinja import render_template
 from frappe.utils.pdf import get_pdf
 from frappe.utils.print_format import read_multi_pdf
-from PyPDF2 import PdfFileWriter
+from PyPDF2 import PdfWriter
 
 from erpnext.accounts.utils import get_fiscal_year
 
@@ -20,23 +20,21 @@
 def execute(filters=None):
 	filters = filters if isinstance(filters, frappe._dict) else frappe._dict(filters)
 	if not filters:
-		filters.setdefault('fiscal_year', get_fiscal_year(nowdate())[0])
-		filters.setdefault('company', frappe.db.get_default("company"))
+		filters.setdefault("fiscal_year", get_fiscal_year(nowdate())[0])
+		filters.setdefault("company", frappe.db.get_default("company"))
 
-	region = frappe.db.get_value("Company",
-		filters={"name": filters.company},
-		fieldname=["country"])
+	region = frappe.db.get_value("Company", filters={"name": filters.company}, fieldname=["country"])
 
-	if region != 'United States':
+	if region != "United States":
 		return [], []
 
-	data = []
 	columns = get_columns()
 	conditions = ""
 	if filters.supplier_group:
-		conditions += "AND s.supplier_group = %s" %frappe.db.escape(filters.get("supplier_group"))
+		conditions += "AND s.supplier_group = %s" % frappe.db.escape(filters.get("supplier_group"))
 
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 		SELECT
 			s.supplier_group as "supplier_group",
 			gl.party AS "supplier",
@@ -49,7 +47,7 @@
 			s.name = gl.party
 				AND s.irs_1099 = 1
 				AND gl.fiscal_year = %(fiscal_year)s
-				AND gl.party_type = "Supplier"
+				AND gl.party_type = 'Supplier'
 				AND gl.company = %(company)s
 				{conditions}
 
@@ -57,10 +55,12 @@
 			gl.party
 
 		ORDER BY
-			gl.party DESC""".format(conditions=conditions), {
-				"fiscal_year": filters.fiscal_year,
-				"company": filters.company
-			}, as_dict=True)
+			gl.party DESC""".format(
+			conditions=conditions
+		),
+		{"fiscal_year": filters.fiscal_year, "company": filters.company},
+		as_dict=True,
+	)
 
 	return columns, data
 
@@ -72,37 +72,29 @@
 			"label": _("Supplier Group"),
 			"fieldtype": "Link",
 			"options": "Supplier Group",
-			"width": 200
+			"width": 200,
 		},
 		{
 			"fieldname": "supplier",
 			"label": _("Supplier"),
 			"fieldtype": "Link",
 			"options": "Supplier",
-			"width": 200
+			"width": 200,
 		},
-		{
-			"fieldname": "tax_id",
-			"label": _("Tax ID"),
-			"fieldtype": "Data",
-			"width": 200
-		},
-		{
-			"fieldname": "payments",
-			"label": _("Total Payments"),
-			"fieldtype": "Currency",
-			"width": 200
-		}
+		{"fieldname": "tax_id", "label": _("Tax ID"), "fieldtype": "Data", "width": 200},
+		{"fieldname": "payments", "label": _("Total Payments"), "fieldtype": "Currency", "width": 200},
 	]
 
 
 @frappe.whitelist()
 def irs_1099_print(filters):
 	if not filters:
-		frappe._dict({
-			"company": frappe.db.get_default("Company"),
-			"fiscal_year": frappe.db.get_default("Fiscal Year")
-		})
+		frappe._dict(
+			{
+				"company": frappe.db.get_default("Company"),
+				"fiscal_year": frappe.db.get_default("Fiscal Year"),
+			}
+		)
 	else:
 		filters = frappe._dict(json.loads(filters))
 
@@ -114,7 +106,7 @@
 
 	columns, data = execute(filters)
 	template = frappe.get_doc("Print Format", "IRS 1099 Form").html
-	output = PdfFileWriter()
+	output = PdfWriter()
 
 	for row in data:
 		row["fiscal_year"] = fiscal_year
@@ -122,17 +114,21 @@
 		row["company_tin"] = company_tin
 		row["payer_street_address"] = company_address
 		row["recipient_street_address"], row["recipient_city_state"] = get_street_address_html(
-			"Supplier", row.supplier)
+			"Supplier", row.supplier
+		)
 		row["payments"] = fmt_money(row["payments"], precision=0, currency="USD")
 		pdf = get_pdf(render_template(template, row), output=output if output else None)
 
-	frappe.local.response.filename = f"{filters.fiscal_year} {filters.company} IRS 1099 Forms{IRS_1099_FORMS_FILE_EXTENSION}"
+	frappe.local.response.filename = (
+		f"{filters.fiscal_year} {filters.company} IRS 1099 Forms{IRS_1099_FORMS_FILE_EXTENSION}"
+	)
 	frappe.local.response.filecontent = read_multi_pdf(output)
 	frappe.local.response.type = "download"
 
 
 def get_payer_address_html(company):
-	address_list = frappe.db.sql("""
+	address_list = frappe.db.sql(
+		"""
 		SELECT
 			name
 		FROM
@@ -142,7 +138,10 @@
 		ORDER BY
 			address_type="Postal" DESC, address_type="Billing" DESC
 		LIMIT 1
-	""", {"company": company}, as_dict=True)
+	""",
+		{"company": company},
+		as_dict=True,
+	)
 
 	address_display = ""
 	if address_list:
@@ -153,7 +152,8 @@
 
 
 def get_street_address_html(party_type, party):
-	address_list = frappe.db.sql("""
+	address_list = frappe.db.sql(
+		"""
 		SELECT
 			link.parent
 		FROM
@@ -166,7 +166,10 @@
 			address.address_type="Postal" DESC,
 			address.address_type="Billing" DESC
 		LIMIT 1
-	""", {"party": party}, as_dict=True)
+	""",
+		{"party": party},
+		as_dict=True,
+	)
 
 	street_address = city_state = ""
 	if address_list:
diff --git a/erpnext/regional/report/ksa_vat/ksa_vat.py b/erpnext/regional/report/ksa_vat/ksa_vat.py
index cc26bd7..15996d2 100644
--- a/erpnext/regional/report/ksa_vat/ksa_vat.py
+++ b/erpnext/regional/report/ksa_vat/ksa_vat.py
@@ -14,6 +14,7 @@
 	data = get_data(filters)
 	return columns, data
 
+
 def get_columns():
 	return [
 		{
@@ -48,101 +49,136 @@
 			"label": _("Currency"),
 			"fieldtype": "Currency",
 			"width": 150,
-			"hidden": 1
-		}
+			"hidden": 1,
+		},
 	]
 
+
 def get_data(filters):
 	data = []
 
 	# Validate if vat settings exist
-	company = filters.get('company')
-	company_currency = frappe.get_cached_value('Company',  company, "default_currency")
+	company = filters.get("company")
+	company_currency = frappe.get_cached_value("Company", company, "default_currency")
 
-	if frappe.db.exists('KSA VAT Setting', company) is None:
-		url = get_url_to_list('KSA VAT Setting')
+	if frappe.db.exists("KSA VAT Setting", company) is None:
+		url = get_url_to_list("KSA VAT Setting")
 		frappe.msgprint(_('Create <a href="{}">KSA VAT Setting</a> for this company').format(url))
 		return data
 
-	ksa_vat_setting = frappe.get_doc('KSA VAT Setting', company)
+	ksa_vat_setting = frappe.get_doc("KSA VAT Setting", company)
 
 	# Sales Heading
-	append_data(data, 'VAT on Sales', '', '', '', company_currency)
+	append_data(data, "VAT on Sales", "", "", "", company_currency)
 
 	grand_total_taxable_amount = 0
 	grand_total_taxable_adjustment_amount = 0
 	grand_total_tax = 0
 
 	for vat_setting in ksa_vat_setting.ksa_vat_sales_accounts:
-		total_taxable_amount, total_taxable_adjustment_amount, \
-			total_tax = get_tax_data_for_each_vat_setting(vat_setting, filters, 'Sales Invoice')
+		(
+			total_taxable_amount,
+			total_taxable_adjustment_amount,
+			total_tax,
+		) = get_tax_data_for_each_vat_setting(vat_setting, filters, "Sales Invoice")
 
 		# Adding results to data
-		append_data(data, vat_setting.title, total_taxable_amount,
-			total_taxable_adjustment_amount, total_tax, company_currency)
+		append_data(
+			data,
+			vat_setting.title,
+			total_taxable_amount,
+			total_taxable_adjustment_amount,
+			total_tax,
+			company_currency,
+		)
 
 		grand_total_taxable_amount += total_taxable_amount
 		grand_total_taxable_adjustment_amount += total_taxable_adjustment_amount
 		grand_total_tax += total_tax
 
 	# Sales Grand Total
-	append_data(data, 'Grand Total', grand_total_taxable_amount,
-		grand_total_taxable_adjustment_amount, grand_total_tax, company_currency)
+	append_data(
+		data,
+		"Grand Total",
+		grand_total_taxable_amount,
+		grand_total_taxable_adjustment_amount,
+		grand_total_tax,
+		company_currency,
+	)
 
 	# Blank Line
-	append_data(data, '', '', '', '', company_currency)
+	append_data(data, "", "", "", "", company_currency)
 
 	# Purchase Heading
-	append_data(data, 'VAT on Purchases', '', '', '', company_currency)
+	append_data(data, "VAT on Purchases", "", "", "", company_currency)
 
 	grand_total_taxable_amount = 0
 	grand_total_taxable_adjustment_amount = 0
 	grand_total_tax = 0
 
 	for vat_setting in ksa_vat_setting.ksa_vat_purchase_accounts:
-		total_taxable_amount, total_taxable_adjustment_amount, \
-			total_tax = get_tax_data_for_each_vat_setting(vat_setting, filters, 'Purchase Invoice')
+		(
+			total_taxable_amount,
+			total_taxable_adjustment_amount,
+			total_tax,
+		) = get_tax_data_for_each_vat_setting(vat_setting, filters, "Purchase Invoice")
 
 		# Adding results to data
-		append_data(data, vat_setting.title, total_taxable_amount,
-			total_taxable_adjustment_amount, total_tax, company_currency)
+		append_data(
+			data,
+			vat_setting.title,
+			total_taxable_amount,
+			total_taxable_adjustment_amount,
+			total_tax,
+			company_currency,
+		)
 
 		grand_total_taxable_amount += total_taxable_amount
 		grand_total_taxable_adjustment_amount += total_taxable_adjustment_amount
 		grand_total_tax += total_tax
 
 	# Purchase Grand Total
-	append_data(data, 'Grand Total', grand_total_taxable_amount,
-		grand_total_taxable_adjustment_amount, grand_total_tax, company_currency)
+	append_data(
+		data,
+		"Grand Total",
+		grand_total_taxable_amount,
+		grand_total_taxable_adjustment_amount,
+		grand_total_tax,
+		company_currency,
+	)
 
 	return data
 
+
 def get_tax_data_for_each_vat_setting(vat_setting, filters, doctype):
-	'''
+	"""
 	(KSA, {filters}, 'Sales Invoice') => 500, 153, 10 \n
 	calculates and returns \n
-	total_taxable_amount, total_taxable_adjustment_amount, total_tax'''
-	from_date = filters.get('from_date')
-	to_date = filters.get('to_date')
+	total_taxable_amount, total_taxable_adjustment_amount, total_tax"""
+	from_date = filters.get("from_date")
+	to_date = filters.get("to_date")
 
 	# Initiate variables
 	total_taxable_amount = 0
 	total_taxable_adjustment_amount = 0
 	total_tax = 0
 	# Fetch All Invoices
-	invoices = frappe.get_all(doctype,
-	filters ={
-		'docstatus': 1,
-		'posting_date': ['between', [from_date, to_date]]
-	}, fields =['name', 'is_return'])
+	invoices = frappe.get_all(
+		doctype,
+		filters={"docstatus": 1, "posting_date": ["between", [from_date, to_date]]},
+		fields=["name", "is_return"],
+	)
 
 	for invoice in invoices:
-		invoice_items = frappe.get_all(f'{doctype} Item',
-		filters ={
-			'docstatus': 1,
-			'parent': invoice.name,
-			'item_tax_template': vat_setting.item_tax_template
-		}, fields =['item_code', 'net_amount'])
+		invoice_items = frappe.get_all(
+			f"{doctype} Item",
+			filters={
+				"docstatus": 1,
+				"parent": invoice.name,
+				"item_tax_template": vat_setting.item_tax_template,
+			},
+			fields=["item_code", "net_amount"],
+		)
 
 		for item in invoice_items:
 			# Summing up total taxable amount
@@ -158,24 +194,31 @@
 	return total_taxable_amount, total_taxable_adjustment_amount, total_tax
 
 
-
 def append_data(data, title, amount, adjustment_amount, vat_amount, company_currency):
 	"""Returns data with appended value."""
-	data.append({"title": _(title), "amount": amount, "adjustment_amount": adjustment_amount, "vat_amount": vat_amount,
-		"currency": company_currency})
+	data.append(
+		{
+			"title": _(title),
+			"amount": amount,
+			"adjustment_amount": adjustment_amount,
+			"vat_amount": vat_amount,
+			"currency": company_currency,
+		}
+	)
+
 
 def get_tax_amount(item_code, account_head, doctype, parent):
-	if doctype == 'Sales Invoice':
-		tax_doctype = 'Sales Taxes and Charges'
+	if doctype == "Sales Invoice":
+		tax_doctype = "Sales Taxes and Charges"
 
-	elif doctype == 'Purchase Invoice':
-		tax_doctype = 'Purchase Taxes and Charges'
+	elif doctype == "Purchase Invoice":
+		tax_doctype = "Purchase Taxes and Charges"
 
-	item_wise_tax_detail = frappe.get_value(tax_doctype, {
-		'docstatus': 1,
-		'parent': parent,
-		'account_head': account_head
-	}, 'item_wise_tax_detail')
+	item_wise_tax_detail = frappe.get_value(
+		tax_doctype,
+		{"docstatus": 1, "parent": parent, "account_head": account_head},
+		"item_wise_tax_detail",
+	)
 
 	tax_amount = 0
 	if item_wise_tax_detail and len(item_wise_tax_detail) > 0:
diff --git a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py
index 4133687..2966b06 100644
--- a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py
+++ b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py
@@ -18,6 +18,7 @@
 
 test_dependencies = ["Territory", "Customer Group", "Supplier Group", "Item"]
 
+
 class TestUaeVat201(TestCase):
 	def setUp(self):
 		frappe.set_user("Administrator")
@@ -34,9 +35,9 @@
 
 		create_warehouse("_Test UAE VAT Supplier Warehouse", company="_Test Company UAE VAT")
 
-		make_item("_Test UAE VAT Item", properties = {"is_zero_rated": 0, "is_exempt": 0})
-		make_item("_Test UAE VAT Zero Rated Item", properties = {"is_zero_rated": 1, "is_exempt": 0})
-		make_item("_Test UAE VAT Exempt Item", properties = {"is_zero_rated": 0, "is_exempt": 1})
+		make_item("_Test UAE VAT Item", properties={"is_zero_rated": 0, "is_exempt": 0})
+		make_item("_Test UAE VAT Zero Rated Item", properties={"is_zero_rated": 1, "is_exempt": 0})
+		make_item("_Test UAE VAT Exempt Item", properties={"is_zero_rated": 0, "is_exempt": 1})
 
 		make_sales_invoices()
 
@@ -52,27 +53,30 @@
 				"raw_amount": amount,
 				"raw_vat_amount": vat,
 			}
-		self.assertEqual(amounts_by_emirate["Sharjah"]["raw_amount"],100)
-		self.assertEqual(amounts_by_emirate["Sharjah"]["raw_vat_amount"],5)
-		self.assertEqual(amounts_by_emirate["Dubai"]["raw_amount"],200)
-		self.assertEqual(amounts_by_emirate["Dubai"]["raw_vat_amount"],10)
-		self.assertEqual(get_tourist_tax_return_total(filters),100)
-		self.assertEqual(get_tourist_tax_return_tax(filters),2)
-		self.assertEqual(get_zero_rated_total(filters),100)
-		self.assertEqual(get_exempt_total(filters),100)
-		self.assertEqual(get_standard_rated_expenses_total(filters),250)
-		self.assertEqual(get_standard_rated_expenses_tax(filters),1)
+		self.assertEqual(amounts_by_emirate["Sharjah"]["raw_amount"], 100)
+		self.assertEqual(amounts_by_emirate["Sharjah"]["raw_vat_amount"], 5)
+		self.assertEqual(amounts_by_emirate["Dubai"]["raw_amount"], 200)
+		self.assertEqual(amounts_by_emirate["Dubai"]["raw_vat_amount"], 10)
+		self.assertEqual(get_tourist_tax_return_total(filters), 100)
+		self.assertEqual(get_tourist_tax_return_tax(filters), 2)
+		self.assertEqual(get_zero_rated_total(filters), 100)
+		self.assertEqual(get_exempt_total(filters), 100)
+		self.assertEqual(get_standard_rated_expenses_total(filters), 250)
+		self.assertEqual(get_standard_rated_expenses_tax(filters), 1)
+
 
 def make_company(company_name, abbr):
 	if not frappe.db.exists("Company", company_name):
-		company = frappe.get_doc({
-			"doctype": "Company",
-			"company_name": company_name,
-			"abbr": abbr,
-			"default_currency": "AED",
-			"country": "United Arab Emirates",
-			"create_chart_of_accounts_based_on": "Standard Template",
-		})
+		company = frappe.get_doc(
+			{
+				"doctype": "Company",
+				"company_name": company_name,
+				"abbr": abbr,
+				"default_currency": "AED",
+				"country": "United Arab Emirates",
+				"create_chart_of_accounts_based_on": "Standard Template",
+			}
+		)
 		company.insert()
 	else:
 		company = frappe.get_doc("Company", company_name)
@@ -85,50 +89,51 @@
 	company.save()
 	return company
 
+
 def set_vat_accounts():
 	if not frappe.db.exists("UAE VAT Settings", "_Test Company UAE VAT"):
 		vat_accounts = frappe.get_all(
 			"Account",
 			fields=["name"],
-			filters = {
-				"company": "_Test Company UAE VAT",
-				"is_group": 0,
-				"account_type": "Tax"
-			}
+			filters={"company": "_Test Company UAE VAT", "is_group": 0, "account_type": "Tax"},
 		)
 
 		uae_vat_accounts = []
 		for account in vat_accounts:
-			uae_vat_accounts.append({
-				"doctype": "UAE VAT Account",
-				"account": account.name
-			})
+			uae_vat_accounts.append({"doctype": "UAE VAT Account", "account": account.name})
 
-		frappe.get_doc({
-			"company": "_Test Company UAE VAT",
-			"uae_vat_accounts": uae_vat_accounts,
-			"doctype": "UAE VAT Settings",
-		}).insert()
+		frappe.get_doc(
+			{
+				"company": "_Test Company UAE VAT",
+				"uae_vat_accounts": uae_vat_accounts,
+				"doctype": "UAE VAT Settings",
+			}
+		).insert()
+
 
 def make_customer():
 	if not frappe.db.exists("Customer", "_Test UAE Customer"):
-		customer = frappe.get_doc({
-			"doctype": "Customer",
-			"customer_name": "_Test UAE Customer",
-			"customer_type": "Company",
-		})
+		customer = frappe.get_doc(
+			{
+				"doctype": "Customer",
+				"customer_name": "_Test UAE Customer",
+				"customer_type": "Company",
+			}
+		)
 		customer.insert()
-	else:
-		customer = frappe.get_doc("Customer", "_Test UAE Customer")
+
 
 def make_supplier():
 	if not frappe.db.exists("Supplier", "_Test UAE Supplier"):
-		frappe.get_doc({
-			"supplier_group": "Local",
-			"supplier_name": "_Test UAE Supplier",
-			"supplier_type": "Individual",
-			"doctype": "Supplier",
-		}).insert()
+		frappe.get_doc(
+			{
+				"supplier_group": "Local",
+				"supplier_name": "_Test UAE Supplier",
+				"supplier_type": "Individual",
+				"doctype": "Supplier",
+			}
+		).insert()
+
 
 def create_warehouse(warehouse_name, properties=None, company=None):
 	if not company:
@@ -148,17 +153,20 @@
 	else:
 		return warehouse_id
 
+
 def make_item(item_code, properties=None):
 	if frappe.db.exists("Item", item_code):
 		return frappe.get_doc("Item", item_code)
 
-	item = frappe.get_doc({
-		"doctype": "Item",
-		"item_code": item_code,
-		"item_name": item_code,
-		"description": item_code,
-		"item_group": "Products"
-	})
+	item = frappe.get_doc(
+		{
+			"doctype": "Item",
+			"item_code": item_code,
+			"item_name": item_code,
+			"description": item_code,
+			"item_group": "Products",
+		}
+	)
 
 	if properties:
 		item.update(properties)
@@ -167,71 +175,77 @@
 
 	return item
 
+
 def make_sales_invoices():
-	def make_sales_invoices_wrapper(emirate, item, tax = True, tourist_tax= False):
+	def make_sales_invoices_wrapper(emirate, item, tax=True, tourist_tax=False):
 		si = create_sales_invoice(
 			company="_Test Company UAE VAT",
-			customer = '_Test UAE Customer',
-			currency = 'AED',
-			warehouse = 'Finished Goods - _TCUV',
-			debit_to = 'Debtors - _TCUV',
-			income_account = 'Sales - _TCUV',
-			expense_account = 'Cost of Goods Sold - _TCUV',
-			cost_center = 'Main - _TCUV',
-			item = item,
-			do_not_save=1
+			customer="_Test UAE Customer",
+			currency="AED",
+			warehouse="Finished Goods - _TCUV",
+			debit_to="Debtors - _TCUV",
+			income_account="Sales - _TCUV",
+			expense_account="Cost of Goods Sold - _TCUV",
+			cost_center="Main - _TCUV",
+			item=item,
+			do_not_save=1,
 		)
 		si.vat_emirate = emirate
 		if tax:
 			si.append(
-				"taxes", {
+				"taxes",
+				{
 					"charge_type": "On Net Total",
 					"account_head": "VAT 5% - _TCUV",
 					"cost_center": "Main - _TCUV",
 					"description": "VAT 5% @ 5.0",
-					"rate": 5.0
-				}
+					"rate": 5.0,
+				},
 			)
 		if tourist_tax:
 			si.tourist_tax_return = 2
 		si.submit()
 
-	#Define Item Names
+	# Define Item Names
 	uae_item = "_Test UAE VAT Item"
 	uae_exempt_item = "_Test UAE VAT Exempt Item"
 	uae_zero_rated_item = "_Test UAE VAT Zero Rated Item"
 
-	#Sales Invoice with standard rated expense in Dubai
-	make_sales_invoices_wrapper('Dubai', uae_item)
-	#Sales Invoice with standard rated expense in Sharjah
-	make_sales_invoices_wrapper('Sharjah', uae_item)
-	#Sales Invoice with Tourist Tax Return
-	make_sales_invoices_wrapper('Dubai', uae_item, True, True)
-	#Sales Invoice with Exempt Item
-	make_sales_invoices_wrapper('Sharjah', uae_exempt_item, False)
-	#Sales Invoice with Zero Rated Item
-	make_sales_invoices_wrapper('Sharjah', uae_zero_rated_item, False)
+	# Sales Invoice with standard rated expense in Dubai
+	make_sales_invoices_wrapper("Dubai", uae_item)
+	# Sales Invoice with standard rated expense in Sharjah
+	make_sales_invoices_wrapper("Sharjah", uae_item)
+	# Sales Invoice with Tourist Tax Return
+	make_sales_invoices_wrapper("Dubai", uae_item, True, True)
+	# Sales Invoice with Exempt Item
+	make_sales_invoices_wrapper("Sharjah", uae_exempt_item, False)
+	# Sales Invoice with Zero Rated Item
+	make_sales_invoices_wrapper("Sharjah", uae_zero_rated_item, False)
+
 
 def create_purchase_invoices():
 	pi = make_purchase_invoice(
 		company="_Test Company UAE VAT",
-		supplier = '_Test UAE Supplier',
-		supplier_warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV',
-		warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV',
-		currency = 'AED',
-		cost_center = 'Main - _TCUV',
-		expense_account = 'Cost of Goods Sold - _TCUV',
-		item = "_Test UAE VAT Item",
+		supplier="_Test UAE Supplier",
+		supplier_warehouse="_Test UAE VAT Supplier Warehouse - _TCUV",
+		warehouse="_Test UAE VAT Supplier Warehouse - _TCUV",
+		currency="AED",
+		cost_center="Main - _TCUV",
+		expense_account="Cost of Goods Sold - _TCUV",
+		item="_Test UAE VAT Item",
 		do_not_save=1,
-		uom = "Nos"
+		uom="Nos",
 	)
-	pi.append("taxes", {
-		"charge_type": "On Net Total",
-		"account_head": "VAT 5% - _TCUV",
-		"cost_center": "Main - _TCUV",
-		"description": "VAT 5% @ 5.0",
-		"rate": 5.0
-	})
+	pi.append(
+		"taxes",
+		{
+			"charge_type": "On Net Total",
+			"account_head": "VAT 5% - _TCUV",
+			"cost_center": "Main - _TCUV",
+			"description": "VAT 5% @ 5.0",
+			"rate": 5.0,
+		},
+	)
 
 	pi.recoverable_standard_rated_expenses = 1
 
diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py
index f8379aa..59ef58b 100644
--- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py
+++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py
@@ -11,21 +11,12 @@
 	data, emirates, amounts_by_emirate = get_data(filters)
 	return columns, data
 
+
 def get_columns():
 	"""Creates a list of dictionaries that are used to generate column headers of the data table."""
 	return [
-		{
-			"fieldname": "no",
-			"label": _("No"),
-			"fieldtype": "Data",
-			"width": 50
-		},
-		{
-			"fieldname": "legend",
-			"label": _("Legend"),
-			"fieldtype": "Data",
-			"width": 300
-		},
+		{"fieldname": "no", "label": _("No"), "fieldtype": "Data", "width": 50},
+		{"fieldname": "legend", "label": _("Legend"), "fieldtype": "Data", "width": 300},
 		{
 			"fieldname": "amount",
 			"label": _("Amount (AED)"),
@@ -37,41 +28,53 @@
 			"label": _("VAT Amount (AED)"),
 			"fieldtype": "Currency",
 			"width": 150,
-		}
+		},
 	]
 
-def get_data(filters = None):
+
+def get_data(filters=None):
 	"""Returns the list of dictionaries. Each dictionary is a row in the datatable and chart data."""
 	data = []
 	emirates, amounts_by_emirate = append_vat_on_sales(data, filters)
 	append_vat_on_expenses(data, filters)
 	return data, emirates, amounts_by_emirate
 
+
 def append_vat_on_sales(data, filters):
 	"""Appends Sales and All Other Outputs."""
-	append_data(data, '', _('VAT on Sales and All Other Outputs'), '', '')
+	append_data(data, "", _("VAT on Sales and All Other Outputs"), "", "")
 
 	emirates, amounts_by_emirate = standard_rated_expenses_emiratewise(data, filters)
 
-	append_data(data, '2',
-		 _('Tax Refunds provided to Tourists under the Tax Refunds for Tourists Scheme'),
-		frappe.format((-1) * get_tourist_tax_return_total(filters), 'Currency'),
-		frappe.format((-1) * get_tourist_tax_return_tax(filters), 'Currency'))
+	append_data(
+		data,
+		"2",
+		_("Tax Refunds provided to Tourists under the Tax Refunds for Tourists Scheme"),
+		frappe.format((-1) * get_tourist_tax_return_total(filters), "Currency"),
+		frappe.format((-1) * get_tourist_tax_return_tax(filters), "Currency"),
+	)
 
-	append_data(data, '3', _('Supplies subject to the reverse charge provision'),
-		frappe.format(get_reverse_charge_total(filters), 'Currency'),
-		frappe.format(get_reverse_charge_tax(filters), 'Currency'))
+	append_data(
+		data,
+		"3",
+		_("Supplies subject to the reverse charge provision"),
+		frappe.format(get_reverse_charge_total(filters), "Currency"),
+		frappe.format(get_reverse_charge_tax(filters), "Currency"),
+	)
 
-	append_data(data, '4', _('Zero Rated'),
-		frappe.format(get_zero_rated_total(filters), 'Currency'), "-")
+	append_data(
+		data, "4", _("Zero Rated"), frappe.format(get_zero_rated_total(filters), "Currency"), "-"
+	)
 
-	append_data(data, '5', _('Exempt Supplies'),
-		frappe.format(get_exempt_total(filters), 'Currency'),"-")
+	append_data(
+		data, "5", _("Exempt Supplies"), frappe.format(get_exempt_total(filters), "Currency"), "-"
+	)
 
-	append_data(data, '', '', '', '')
+	append_data(data, "", "", "", "")
 
 	return emirates, amounts_by_emirate
 
+
 def standard_rated_expenses_emiratewise(data, filters):
 	"""Append emiratewise standard rated expenses and vat."""
 	total_emiratewise = get_total_emiratewise(filters)
@@ -82,44 +85,61 @@
 			"legend": emirate,
 			"raw_amount": amount,
 			"raw_vat_amount": vat,
-			"amount": frappe.format(amount, 'Currency'),
-			"vat_amount": frappe.format(vat, 'Currency'),
+			"amount": frappe.format(amount, "Currency"),
+			"vat_amount": frappe.format(vat, "Currency"),
 		}
 	amounts_by_emirate = append_emiratewise_expenses(data, emirates, amounts_by_emirate)
 	return emirates, amounts_by_emirate
 
+
 def append_emiratewise_expenses(data, emirates, amounts_by_emirate):
 	"""Append emiratewise standard rated expenses and vat."""
 	for no, emirate in enumerate(emirates, 97):
 		if emirate in amounts_by_emirate:
-			amounts_by_emirate[emirate]["no"] = _('1{0}').format(chr(no))
-			amounts_by_emirate[emirate]["legend"] = _('Standard rated supplies in {0}').format(emirate)
+			amounts_by_emirate[emirate]["no"] = _("1{0}").format(chr(no))
+			amounts_by_emirate[emirate]["legend"] = _("Standard rated supplies in {0}").format(emirate)
 			data.append(amounts_by_emirate[emirate])
 		else:
-			append_data(data, _('1{0}').format(chr(no)),
-				_('Standard rated supplies in {0}').format(emirate),
-				frappe.format(0, 'Currency'), frappe.format(0, 'Currency'))
+			append_data(
+				data,
+				_("1{0}").format(chr(no)),
+				_("Standard rated supplies in {0}").format(emirate),
+				frappe.format(0, "Currency"),
+				frappe.format(0, "Currency"),
+			)
 	return amounts_by_emirate
 
+
 def append_vat_on_expenses(data, filters):
 	"""Appends Expenses and All Other Inputs."""
-	append_data(data, '', _('VAT on Expenses and All Other Inputs'), '', '')
-	append_data(data, '9', _('Standard Rated Expenses'),
-		frappe.format(get_standard_rated_expenses_total(filters), 'Currency'),
-		frappe.format(get_standard_rated_expenses_tax(filters), 'Currency'))
-	append_data(data, '10', _('Supplies subject to the reverse charge provision'),
-		frappe.format(get_reverse_charge_recoverable_total(filters), 'Currency'),
-		frappe.format(get_reverse_charge_recoverable_tax(filters), 'Currency'))
+	append_data(data, "", _("VAT on Expenses and All Other Inputs"), "", "")
+	append_data(
+		data,
+		"9",
+		_("Standard Rated Expenses"),
+		frappe.format(get_standard_rated_expenses_total(filters), "Currency"),
+		frappe.format(get_standard_rated_expenses_tax(filters), "Currency"),
+	)
+	append_data(
+		data,
+		"10",
+		_("Supplies subject to the reverse charge provision"),
+		frappe.format(get_reverse_charge_recoverable_total(filters), "Currency"),
+		frappe.format(get_reverse_charge_recoverable_tax(filters), "Currency"),
+	)
+
 
 def append_data(data, no, legend, amount, vat_amount):
 	"""Returns data with appended value."""
-	data.append({"no": no, "legend":legend, "amount": amount, "vat_amount": vat_amount})
+	data.append({"no": no, "legend": legend, "amount": amount, "vat_amount": vat_amount})
+
 
 def get_total_emiratewise(filters):
 	"""Returns Emiratewise Amount and Taxes."""
 	conditions = get_conditions(filters)
 	try:
-		return frappe.db.sql("""
+		return frappe.db.sql(
+			"""
 			select
 				s.vat_emirate as emirate, sum(i.base_amount) as total, sum(i.tax_amount)
 			from
@@ -131,52 +151,54 @@
 				{where_conditions}
 			group by
 				s.vat_emirate;
-			""".format(where_conditions=conditions), filters)
+			""".format(
+				where_conditions=conditions
+			),
+			filters,
+		)
 	except (IndexError, TypeError):
 		return 0
 
+
 def get_emirates():
 	"""Returns a List of emirates in the order that they are to be displayed."""
-	return [
-		'Abu Dhabi',
-		'Dubai',
-		'Sharjah',
-		'Ajman',
-		'Umm Al Quwain',
-		'Ras Al Khaimah',
-		'Fujairah'
-	]
+	return ["Abu Dhabi", "Dubai", "Sharjah", "Ajman", "Umm Al Quwain", "Ras Al Khaimah", "Fujairah"]
+
 
 def get_filters(filters):
 	"""The conditions to be used to filter data to calculate the total sale."""
 	query_filters = []
 	if filters.get("company"):
-		query_filters.append(["company", '=', filters['company']])
+		query_filters.append(["company", "=", filters["company"]])
 	if filters.get("from_date"):
-		query_filters.append(["posting_date", '>=', filters['from_date']])
+		query_filters.append(["posting_date", ">=", filters["from_date"]])
 	if filters.get("from_date"):
-		query_filters.append(["posting_date", '<=', filters['to_date']])
+		query_filters.append(["posting_date", "<=", filters["to_date"]])
 	return query_filters
 
+
 def get_reverse_charge_total(filters):
 	"""Returns the sum of the total of each Purchase invoice made."""
 	query_filters = get_filters(filters)
-	query_filters.append(['reverse_charge', '=', 'Y'])
-	query_filters.append(['docstatus', '=', 1])
+	query_filters.append(["reverse_charge", "=", "Y"])
+	query_filters.append(["docstatus", "=", 1])
 	try:
-		return frappe.db.get_all('Purchase Invoice',
-			filters = query_filters,
-			fields = ['sum(total)'],
-			as_list=True,
-			limit = 1
-		)[0][0] or 0
+		return (
+			frappe.db.get_all(
+				"Purchase Invoice", filters=query_filters, fields=["sum(total)"], as_list=True, limit=1
+			)[0][0]
+			or 0
+		)
 	except (IndexError, TypeError):
 		return 0
 
+
 def get_reverse_charge_tax(filters):
 	"""Returns the sum of the tax of each Purchase invoice made."""
 	conditions = get_conditions_join(filters)
-	return frappe.db.sql("""
+	return (
+		frappe.db.sql(
+			"""
 		select sum(debit)  from
 			`tabPurchase Invoice` p inner join `tabGL Entry` gl
 		on
@@ -187,28 +209,38 @@
 			and gl.docstatus = 1
 			and account in (select account from `tabUAE VAT Account` where  parent=%(company)s)
 			{where_conditions} ;
-		""".format(where_conditions=conditions), filters)[0][0] or 0
+		""".format(
+				where_conditions=conditions
+			),
+			filters,
+		)[0][0]
+		or 0
+	)
+
 
 def get_reverse_charge_recoverable_total(filters):
 	"""Returns the sum of the total of each Purchase invoice made with recoverable reverse charge."""
 	query_filters = get_filters(filters)
-	query_filters.append(['reverse_charge', '=', 'Y'])
-	query_filters.append(['recoverable_reverse_charge', '>', '0'])
-	query_filters.append(['docstatus', '=', 1])
+	query_filters.append(["reverse_charge", "=", "Y"])
+	query_filters.append(["recoverable_reverse_charge", ">", "0"])
+	query_filters.append(["docstatus", "=", 1])
 	try:
-		return frappe.db.get_all('Purchase Invoice',
-			filters = query_filters,
-			fields = ['sum(total)'],
-			as_list=True,
-			limit = 1
-		)[0][0] or 0
+		return (
+			frappe.db.get_all(
+				"Purchase Invoice", filters=query_filters, fields=["sum(total)"], as_list=True, limit=1
+			)[0][0]
+			or 0
+		)
 	except (IndexError, TypeError):
 		return 0
 
+
 def get_reverse_charge_recoverable_tax(filters):
 	"""Returns the sum of the tax of each Purchase invoice made."""
 	conditions = get_conditions_join(filters)
-	return frappe.db.sql("""
+	return (
+		frappe.db.sql(
+			"""
 		select
 			sum(debit * p.recoverable_reverse_charge / 100)
 		from
@@ -222,83 +254,107 @@
 			and gl.docstatus = 1
 			and account in (select account from `tabUAE VAT Account` where  parent=%(company)s)
 			{where_conditions} ;
-		""".format(where_conditions=conditions), filters)[0][0] or 0
+		""".format(
+				where_conditions=conditions
+			),
+			filters,
+		)[0][0]
+		or 0
+	)
+
 
 def get_conditions_join(filters):
 	"""The conditions to be used to filter data to calculate the total vat."""
 	conditions = ""
-	for opts in (("company", " and p.company=%(company)s"),
+	for opts in (
+		("company", " and p.company=%(company)s"),
 		("from_date", " and p.posting_date>=%(from_date)s"),
-		("to_date", " and p.posting_date<=%(to_date)s")):
+		("to_date", " and p.posting_date<=%(to_date)s"),
+	):
 		if filters.get(opts[0]):
 			conditions += opts[1]
 	return conditions
 
+
 def get_standard_rated_expenses_total(filters):
 	"""Returns the sum of the total of each Purchase invoice made with recoverable reverse charge."""
 	query_filters = get_filters(filters)
-	query_filters.append(['recoverable_standard_rated_expenses', '>', 0])
-	query_filters.append(['docstatus', '=', 1])
+	query_filters.append(["recoverable_standard_rated_expenses", ">", 0])
+	query_filters.append(["docstatus", "=", 1])
 	try:
-		return frappe.db.get_all('Purchase Invoice',
-			filters = query_filters,
-			fields = ['sum(total)'],
-			as_list=True,
-			limit = 1
-		)[0][0]  or 0
+		return (
+			frappe.db.get_all(
+				"Purchase Invoice", filters=query_filters, fields=["sum(total)"], as_list=True, limit=1
+			)[0][0]
+			or 0
+		)
 	except (IndexError, TypeError):
 		return 0
 
+
 def get_standard_rated_expenses_tax(filters):
 	"""Returns the sum of the tax of each Purchase invoice made."""
 	query_filters = get_filters(filters)
-	query_filters.append(['recoverable_standard_rated_expenses', '>', 0])
-	query_filters.append(['docstatus', '=', 1])
+	query_filters.append(["recoverable_standard_rated_expenses", ">", 0])
+	query_filters.append(["docstatus", "=", 1])
 	try:
-		return frappe.db.get_all('Purchase Invoice',
-			filters = query_filters,
-			fields = ['sum(recoverable_standard_rated_expenses)'],
-			as_list=True,
-			limit = 1
-		)[0][0]  or 0
+		return (
+			frappe.db.get_all(
+				"Purchase Invoice",
+				filters=query_filters,
+				fields=["sum(recoverable_standard_rated_expenses)"],
+				as_list=True,
+				limit=1,
+			)[0][0]
+			or 0
+		)
 	except (IndexError, TypeError):
 		return 0
 
+
 def get_tourist_tax_return_total(filters):
 	"""Returns the sum of the total of each Sales invoice with non zero tourist_tax_return."""
 	query_filters = get_filters(filters)
-	query_filters.append(['tourist_tax_return', '>', 0])
-	query_filters.append(['docstatus', '=', 1])
+	query_filters.append(["tourist_tax_return", ">", 0])
+	query_filters.append(["docstatus", "=", 1])
 	try:
-		return frappe.db.get_all('Sales Invoice',
-			filters = query_filters,
-			fields = ['sum(total)'],
-			as_list=True,
-			limit = 1
-		)[0][0]  or 0
+		return (
+			frappe.db.get_all(
+				"Sales Invoice", filters=query_filters, fields=["sum(total)"], as_list=True, limit=1
+			)[0][0]
+			or 0
+		)
 	except (IndexError, TypeError):
 		return 0
 
+
 def get_tourist_tax_return_tax(filters):
 	"""Returns the sum of the tax of each Sales invoice with non zero tourist_tax_return."""
 	query_filters = get_filters(filters)
-	query_filters.append(['tourist_tax_return', '>', 0])
-	query_filters.append(['docstatus', '=', 1])
+	query_filters.append(["tourist_tax_return", ">", 0])
+	query_filters.append(["docstatus", "=", 1])
 	try:
-		return frappe.db.get_all('Sales Invoice',
-			filters = query_filters,
-			fields = ['sum(tourist_tax_return)'],
-			as_list=True,
-			limit = 1
-		)[0][0]  or 0
+		return (
+			frappe.db.get_all(
+				"Sales Invoice",
+				filters=query_filters,
+				fields=["sum(tourist_tax_return)"],
+				as_list=True,
+				limit=1,
+			)[0][0]
+			or 0
+		)
 	except (IndexError, TypeError):
 		return 0
 
+
 def get_zero_rated_total(filters):
 	"""Returns the sum of each Sales Invoice Item Amount which is zero rated."""
 	conditions = get_conditions(filters)
 	try:
-		return frappe.db.sql("""
+		return (
+			frappe.db.sql(
+				"""
 			select
 				sum(i.base_amount) as total
 			from
@@ -308,15 +364,24 @@
 			where
 				s.docstatus = 1 and  i.is_zero_rated = 1
 				{where_conditions} ;
-			""".format(where_conditions=conditions), filters)[0][0] or 0
+			""".format(
+					where_conditions=conditions
+				),
+				filters,
+			)[0][0]
+			or 0
+		)
 	except (IndexError, TypeError):
 		return 0
 
+
 def get_exempt_total(filters):
 	"""Returns the sum of each Sales Invoice Item Amount which is Vat Exempt."""
 	conditions = get_conditions(filters)
 	try:
-		return frappe.db.sql("""
+		return (
+			frappe.db.sql(
+				"""
 			select
 				sum(i.base_amount) as total
 			from
@@ -326,15 +391,25 @@
 			where
 				s.docstatus = 1 and  i.is_exempt = 1
 				{where_conditions} ;
-			""".format(where_conditions=conditions), filters)[0][0] or 0
+			""".format(
+					where_conditions=conditions
+				),
+				filters,
+			)[0][0]
+			or 0
+		)
 	except (IndexError, TypeError):
 		return 0
+
+
 def get_conditions(filters):
 	"""The conditions to be used to filter data to calculate the total sale."""
 	conditions = ""
-	for opts in (("company", " and company=%(company)s"),
+	for opts in (
+		("company", " and company=%(company)s"),
 		("from_date", " and posting_date>=%(from_date)s"),
-		("to_date", " and posting_date<=%(to_date)s")):
+		("to_date", " and posting_date<=%(to_date)s"),
+	):
 		if filters.get(opts[0]):
 			conditions += opts[1]
 	return conditions
diff --git a/erpnext/regional/report/vat_audit_report/test_vat_audit_report.py b/erpnext/regional/report/vat_audit_report/test_vat_audit_report.py
index f22abae..a898a25 100644
--- a/erpnext/regional/report/vat_audit_report/test_vat_audit_report.py
+++ b/erpnext/regional/report/vat_audit_report/test_vat_audit_report.py
@@ -18,14 +18,22 @@
 		frappe.set_user("Administrator")
 		make_company("_Test Company SA VAT", "_TCSV")
 
-		create_account(account_name="VAT - 0%", account_type="Tax",
-			parent_account="Duties and Taxes - _TCSV", company="_Test Company SA VAT")
-		create_account(account_name="VAT - 15%", account_type="Tax",
-			parent_account="Duties and Taxes - _TCSV", company="_Test Company SA VAT")
+		create_account(
+			account_name="VAT - 0%",
+			account_type="Tax",
+			parent_account="Duties and Taxes - _TCSV",
+			company="_Test Company SA VAT",
+		)
+		create_account(
+			account_name="VAT - 15%",
+			account_type="Tax",
+			parent_account="Duties and Taxes - _TCSV",
+			company="_Test Company SA VAT",
+		)
 		set_sa_vat_accounts()
 
 		make_item("_Test SA VAT Item")
-		make_item("_Test SA VAT Zero Rated Item", properties = {"is_zero_rated": 1})
+		make_item("_Test SA VAT Zero Rated Item", properties={"is_zero_rated": 1})
 
 		make_customer()
 		make_supplier()
@@ -38,34 +46,33 @@
 		frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company SA VAT'")
 
 	def test_vat_audit_report(self):
-		filters = {
-			"company": "_Test Company SA VAT",
-			"from_date": today(),
-			"to_date": today()
-		}
+		filters = {"company": "_Test Company SA VAT", "from_date": today(), "to_date": today()}
 		columns, data = execute(filters)
 		total_tax_amount = 0
 		total_row_tax = 0
 		for row in data:
 			keys = row.keys()
 			# skips total row tax_amount in if.. and skips section header in elif..
-			if 'voucher_no' in keys:
-				total_tax_amount = total_tax_amount + row['tax_amount']
-			elif 'tax_amount' in keys:
-				total_row_tax = total_row_tax + row['tax_amount']
+			if "voucher_no" in keys:
+				total_tax_amount = total_tax_amount + row["tax_amount"]
+			elif "tax_amount" in keys:
+				total_row_tax = total_row_tax + row["tax_amount"]
 
 		self.assertEqual(total_tax_amount, total_row_tax)
 
+
 def make_company(company_name, abbr):
 	if not frappe.db.exists("Company", company_name):
-		company = frappe.get_doc({
-			"doctype": "Company",
-			"company_name": company_name,
-			"abbr": abbr,
-			"default_currency": "ZAR",
-			"country": "South Africa",
-			"create_chart_of_accounts_based_on": "Standard Template"
-		})
+		company = frappe.get_doc(
+			{
+				"doctype": "Company",
+				"company_name": company_name,
+				"abbr": abbr,
+				"default_currency": "ZAR",
+				"country": "South Africa",
+				"create_chart_of_accounts_based_on": "Standard Template",
+			}
+		)
 		company.insert()
 	else:
 		company = frappe.get_doc("Company", company_name)
@@ -79,86 +86,95 @@
 
 	return company
 
+
 def set_sa_vat_accounts():
 	if not frappe.db.exists("South Africa VAT Settings", "_Test Company SA VAT"):
 		vat_accounts = frappe.get_all(
 			"Account",
 			fields=["name"],
-			filters = {
-				"company": "_Test Company SA VAT",
-				"is_group": 0,
-				"account_type": "Tax"
-			}
+			filters={"company": "_Test Company SA VAT", "is_group": 0, "account_type": "Tax"},
 		)
 
 		sa_vat_accounts = []
 		for account in vat_accounts:
-			sa_vat_accounts.append({
-				"doctype": "South Africa VAT Account",
-				"account": account.name
-			})
+			sa_vat_accounts.append({"doctype": "South Africa VAT Account", "account": account.name})
 
-		frappe.get_doc({
-			"company": "_Test Company SA VAT",
-			"vat_accounts": sa_vat_accounts,
-			"doctype": "South Africa VAT Settings",
-		}).insert()
+		frappe.get_doc(
+			{
+				"company": "_Test Company SA VAT",
+				"vat_accounts": sa_vat_accounts,
+				"doctype": "South Africa VAT Settings",
+			}
+		).insert()
+
 
 def make_customer():
 	if not frappe.db.exists("Customer", "_Test SA Customer"):
-		frappe.get_doc({
-			"doctype": "Customer",
-			"customer_name": "_Test SA Customer",
-			"customer_type": "Company",
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Customer",
+				"customer_name": "_Test SA Customer",
+				"customer_type": "Company",
+			}
+		).insert()
+
 
 def make_supplier():
 	if not frappe.db.exists("Supplier", "_Test SA Supplier"):
-		frappe.get_doc({
-			"doctype": "Supplier",
-			"supplier_name": "_Test SA Supplier",
-			"supplier_type": "Company",
-			"supplier_group":"All Supplier Groups"
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Supplier",
+				"supplier_name": "_Test SA Supplier",
+				"supplier_type": "Company",
+				"supplier_group": "All Supplier Groups",
+			}
+		).insert()
+
 
 def make_item(item_code, properties=None):
 	if not frappe.db.exists("Item", item_code):
-		item = frappe.get_doc({
-			"doctype": "Item",
-			"item_code": item_code,
-			"item_name": item_code,
-			"description": item_code,
-			"item_group": "Products"
-		})
+		item = frappe.get_doc(
+			{
+				"doctype": "Item",
+				"item_code": item_code,
+				"item_name": item_code,
+				"description": item_code,
+				"item_group": "Products",
+			}
+		)
 
 		if properties:
 			item.update(properties)
 
 		item.insert()
 
+
 def make_sales_invoices():
 	def make_sales_invoices_wrapper(item, rate, tax_account, tax_rate, tax=True):
 		si = create_sales_invoice(
 			company="_Test Company SA VAT",
-			customer = "_Test SA Customer",
-			currency = "ZAR",
+			customer="_Test SA Customer",
+			currency="ZAR",
 			item=item,
 			rate=rate,
-			warehouse = "Finished Goods - _TCSV",
-			debit_to = "Debtors - _TCSV",
-			income_account = "Sales - _TCSV",
-			expense_account = "Cost of Goods Sold - _TCSV",
-			cost_center = "Main - _TCSV",
-			do_not_save=1
+			warehouse="Finished Goods - _TCSV",
+			debit_to="Debtors - _TCSV",
+			income_account="Sales - _TCSV",
+			expense_account="Cost of Goods Sold - _TCSV",
+			cost_center="Main - _TCSV",
+			do_not_save=1,
 		)
 		if tax:
-			si.append("taxes", {
+			si.append(
+				"taxes",
+				{
 					"charge_type": "On Net Total",
 					"account_head": tax_account,
 					"cost_center": "Main - _TCSV",
 					"description": "VAT 15% @ 15.0",
-					"rate": tax_rate
-				})
+					"rate": tax_rate,
+				},
+			)
 
 		si.submit()
 
@@ -168,27 +184,31 @@
 	make_sales_invoices_wrapper(test_item, 100.0, "VAT - 15% - _TCSV", 15.0)
 	make_sales_invoices_wrapper(test_zero_rated_item, 100.0, "VAT - 0% - _TCSV", 0.0)
 
+
 def create_purchase_invoices():
 	pi = make_purchase_invoice(
-		company = "_Test Company SA VAT",
-		supplier = "_Test SA Supplier",
-		supplier_warehouse = "Finished Goods - _TCSV",
-		warehouse = "Finished Goods - _TCSV",
-		currency = "ZAR",
-		cost_center = "Main - _TCSV",
-		expense_account = "Cost of Goods Sold - _TCSV",
-		item = "_Test SA VAT Item",
-		qty = 1,
-		rate = 100,
-		uom = "Nos",
-		do_not_save = 1
+		company="_Test Company SA VAT",
+		supplier="_Test SA Supplier",
+		supplier_warehouse="Finished Goods - _TCSV",
+		warehouse="Finished Goods - _TCSV",
+		currency="ZAR",
+		cost_center="Main - _TCSV",
+		expense_account="Cost of Goods Sold - _TCSV",
+		item="_Test SA VAT Item",
+		qty=1,
+		rate=100,
+		uom="Nos",
+		do_not_save=1,
 	)
-	pi.append("taxes", {
-		"charge_type": "On Net Total",
-		"account_head": "VAT - 15% - _TCSV",
-		"cost_center": "Main - _TCSV",
-		"description": "VAT 15% @ 15.0",
-		"rate": 15.0
-	})
+	pi.append(
+		"taxes",
+		{
+			"charge_type": "On Net Total",
+			"account_head": "VAT - 15% - _TCSV",
+			"cost_center": "Main - _TCSV",
+			"description": "VAT 15% @ 15.0",
+			"rate": 15.0,
+		},
+	)
 
 	pi.submit()
diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.py b/erpnext/regional/report/vat_audit_report/vat_audit_report.py
index 17e5064..3d486ce 100644
--- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py
+++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py
@@ -12,8 +12,8 @@
 def execute(filters=None):
 	return VATAuditReport(filters).run()
 
-class VATAuditReport(object):
 
+class VATAuditReport(object):
 	def __init__(self, filters=None):
 		self.filters = frappe._dict(filters or {})
 		self.columns = []
@@ -27,8 +27,11 @@
 			self.select_columns = """
 			name as voucher_no,
 			posting_date, remarks"""
-			columns = ", supplier as party, credit_to as account" if doctype=="Purchase Invoice" \
+			columns = (
+				", supplier as party, credit_to as account"
+				if doctype == "Purchase Invoice"
 				else ", customer as party, debit_to as account"
+			)
 			self.select_columns += columns
 
 			self.get_invoice_data(doctype)
@@ -41,28 +44,36 @@
 		return self.columns, self.data
 
 	def get_sa_vat_accounts(self):
-		self.sa_vat_accounts = frappe.get_all("South Africa VAT Account",
-			filters = {"parent": self.filters.company}, pluck="account")
+		self.sa_vat_accounts = frappe.get_all(
+			"South Africa VAT Account", filters={"parent": self.filters.company}, pluck="account"
+		)
 		if not self.sa_vat_accounts and not frappe.flags.in_test and not frappe.flags.in_migrate:
-			link_to_settings = get_link_to_form("South Africa VAT Settings", "", label="South Africa VAT Settings")
+			link_to_settings = get_link_to_form(
+				"South Africa VAT Settings", "", label="South Africa VAT Settings"
+			)
 			frappe.throw(_("Please set VAT Accounts in {0}").format(link_to_settings))
 
 	def get_invoice_data(self, doctype):
 		conditions = self.get_conditions()
 		self.invoices = frappe._dict()
 
-		invoice_data = frappe.db.sql("""
+		invoice_data = frappe.db.sql(
+			"""
 			SELECT
 				{select_columns}
 			FROM
 				`tab{doctype}`
 			WHERE
 				docstatus = 1 {where_conditions}
-				and is_opening = "No"
+				and is_opening = 'No'
 			ORDER BY
 				posting_date DESC
-			""".format(select_columns=self.select_columns, doctype=doctype,
-				where_conditions=conditions), self.filters, as_dict=1)
+			""".format(
+				select_columns=self.select_columns, doctype=doctype, where_conditions=conditions
+			),
+			self.filters,
+			as_dict=1,
+		)
 
 		for d in invoice_data:
 			self.invoices.setdefault(d.voucher_no, d)
@@ -70,30 +81,35 @@
 	def get_invoice_items(self, doctype):
 		self.invoice_items = frappe._dict()
 
-		items = frappe.db.sql("""
+		items = frappe.db.sql(
+			"""
 			SELECT
 				item_code, parent, base_net_amount, is_zero_rated
 			FROM
 				`tab%s Item`
 			WHERE
 				parent in (%s)
-			""" % (doctype, ", ".join(["%s"]*len(self.invoices))), tuple(self.invoices), as_dict=1)
+			"""
+			% (doctype, ", ".join(["%s"] * len(self.invoices))),
+			tuple(self.invoices),
+			as_dict=1,
+		)
 		for d in items:
-			if d.item_code not in self.invoice_items.get(d.parent, {}):
-				self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, {
-					'net_amount': 0.0})
-				self.invoice_items[d.parent][d.item_code]['net_amount'] += d.get('base_net_amount', 0)
-				self.invoice_items[d.parent][d.item_code]['is_zero_rated'] = d.is_zero_rated
+			self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, {"net_amount": 0.0})
+			self.invoice_items[d.parent][d.item_code]["net_amount"] += d.get("base_net_amount", 0)
+			self.invoice_items[d.parent][d.item_code]["is_zero_rated"] = d.is_zero_rated
 
 	def get_items_based_on_tax_rate(self, doctype):
 		self.items_based_on_tax_rate = frappe._dict()
 		self.item_tax_rate = frappe._dict()
-		self.tax_doctype = "Purchase Taxes and Charges" if doctype=="Purchase Invoice" \
-			else "Sales Taxes and Charges"
+		self.tax_doctype = (
+			"Purchase Taxes and Charges" if doctype == "Purchase Invoice" else "Sales Taxes and Charges"
+		)
 
-		self.tax_details = frappe.db.sql("""
+		self.tax_details = frappe.db.sql(
+			"""
 			SELECT
-				parent, account_head, item_wise_tax_detail, base_tax_amount_after_discount_amount
+				parent, account_head, item_wise_tax_detail
 			FROM
 				`tab%s`
 			WHERE
@@ -101,10 +117,12 @@
 				and parent in (%s)
 			ORDER BY
 				account_head
-			""" % (self.tax_doctype, "%s", ", ".join(["%s"]*len(self.invoices.keys()))),
-			tuple([doctype] + list(self.invoices.keys())))
+			"""
+			% (self.tax_doctype, "%s", ", ".join(["%s"] * len(self.invoices.keys()))),
+			tuple([doctype] + list(self.invoices.keys())),
+		)
 
-		for parent, account, item_wise_tax_detail, tax_amount in self.tax_details:
+		for parent, account, item_wise_tax_detail in self.tax_details:
 			if item_wise_tax_detail:
 				try:
 					if account in self.sa_vat_accounts:
@@ -113,14 +131,15 @@
 						continue
 					for item_code, taxes in item_wise_tax_detail.items():
 						is_zero_rated = self.invoice_items.get(parent).get(item_code).get("is_zero_rated")
-						#to skip items with non-zero tax rate in multiple rows
+						# to skip items with non-zero tax rate in multiple rows
 						if taxes[0] == 0 and not is_zero_rated:
 							continue
-						tax_rate, item_amount_map = self.get_item_amount_map(parent, item_code, taxes)
+						tax_rate = self.get_item_amount_map(parent, item_code, taxes)
 
 						if tax_rate is not None:
-							rate_based_dict = self.items_based_on_tax_rate.setdefault(parent, {}) \
-								.setdefault(tax_rate, [])
+							rate_based_dict = self.items_based_on_tax_rate.setdefault(parent, {}).setdefault(
+								tax_rate, []
+							)
 							if item_code not in rate_based_dict:
 								rate_based_dict.append(item_code)
 				except ValueError:
@@ -131,23 +150,30 @@
 		tax_rate = taxes[0]
 		tax_amount = taxes[1]
 		gross_amount = net_amount + tax_amount
-		item_amount_map = self.item_tax_rate.setdefault(parent, {}) \
-			.setdefault(item_code, [])
-		amount_dict = {
-			"tax_rate": tax_rate,
-			"gross_amount": gross_amount,
-			"tax_amount": tax_amount,
-			"net_amount": net_amount
-		}
-		item_amount_map.append(amount_dict)
 
-		return tax_rate, item_amount_map
+		self.item_tax_rate.setdefault(parent, {}).setdefault(
+			item_code,
+			{
+				"tax_rate": tax_rate,
+				"gross_amount": 0.0,
+				"tax_amount": 0.0,
+				"net_amount": 0.0,
+			},
+		)
+
+		self.item_tax_rate[parent][item_code]["net_amount"] += net_amount
+		self.item_tax_rate[parent][item_code]["tax_amount"] += tax_amount
+		self.item_tax_rate[parent][item_code]["gross_amount"] += gross_amount
+
+		return tax_rate
 
 	def get_conditions(self):
 		conditions = ""
-		for opts in (("company", " and company=%(company)s"),
+		for opts in (
+			("company", " and company=%(company)s"),
 			("from_date", " and posting_date>=%(from_date)s"),
-			("to_date", " and posting_date<=%(to_date)s")):
+			("to_date", " and posting_date<=%(to_date)s"),
+		):
 			if self.filters.get(opts[0]):
 				conditions += opts[1]
 
@@ -174,19 +200,20 @@
 				"gross_amount": total_gross,
 				"tax_amount": total_tax,
 				"net_amount": total_net,
-				"bold":1
+				"bold": 1,
 			}
 			self.data.append(total)
 			self.data.append({})
 
 	def get_consolidated_data(self, doctype):
-		consolidated_data_map={}
+		consolidated_data_map = {}
 		for inv, inv_data in self.invoices.items():
 			if self.items_based_on_tax_rate.get(inv):
 				for rate, items in self.items_based_on_tax_rate.get(inv).items():
+					row = {"tax_amount": 0.0, "gross_amount": 0.0, "net_amount": 0.0}
+
 					consolidated_data_map.setdefault(rate, {"data": []})
 					for item in items:
-						row = {}
 						item_details = self.item_tax_rate.get(inv).get(item)
 						row["account"] = inv_data.get("account")
 						row["posting_date"] = formatdate(inv_data.get("posting_date"), "dd-mm-yyyy")
@@ -195,78 +222,54 @@
 						row["party_type"] = "Customer" if doctype == "Sales Invoice" else "Supplier"
 						row["party"] = inv_data.get("party")
 						row["remarks"] = inv_data.get("remarks")
-						row["gross_amount"]= item_details[0].get("gross_amount")
-						row["tax_amount"]= item_details[0].get("tax_amount")
-						row["net_amount"]= item_details[0].get("net_amount")
-						consolidated_data_map[rate]["data"].append(row)
+						row["gross_amount"] += item_details.get("gross_amount")
+						row["tax_amount"] += item_details.get("tax_amount")
+						row["net_amount"] += item_details.get("net_amount")
+
+					consolidated_data_map[rate]["data"].append(row)
 
 		return consolidated_data_map
 
 	def get_columns(self):
 		self.columns = [
-			{
-				"fieldname": "posting_date",
-				"label": "Posting Date",
-				"fieldtype": "Data",
-				"width": 200
-			},
+			{"fieldname": "posting_date", "label": "Posting Date", "fieldtype": "Data", "width": 200},
 			{
 				"fieldname": "account",
 				"label": "Account",
 				"fieldtype": "Link",
 				"options": "Account",
-				"width": 150
+				"width": 150,
 			},
 			{
 				"fieldname": "voucher_type",
 				"label": "Voucher Type",
 				"fieldtype": "Data",
 				"width": 140,
-				"hidden": 1
+				"hidden": 1,
 			},
 			{
 				"fieldname": "voucher_no",
 				"label": "Reference",
 				"fieldtype": "Dynamic Link",
 				"options": "voucher_type",
-				"width": 150
+				"width": 150,
 			},
 			{
 				"fieldname": "party_type",
 				"label": "Party Type",
 				"fieldtype": "Data",
 				"width": 140,
-				"hidden": 1
+				"hidden": 1,
 			},
 			{
 				"fieldname": "party",
 				"label": "Party",
 				"fieldtype": "Dynamic Link",
 				"options": "party_type",
-				"width": 150
+				"width": 150,
 			},
-			{
-				"fieldname": "remarks",
-				"label": "Details",
-				"fieldtype": "Data",
-				"width": 150
-			},
-			{
-				"fieldname": "net_amount",
-				"label": "Net Amount",
-				"fieldtype": "Currency",
-				"width": 130
-			},
-			{
-				"fieldname": "tax_amount",
-				"label": "Tax Amount",
-				"fieldtype": "Currency",
-				"width": 130
-			},
-			{
-				"fieldname": "gross_amount",
-				"label": "Gross Amount",
-				"fieldtype": "Currency",
-				"width": 130
-			},
+			{"fieldname": "remarks", "label": "Details", "fieldtype": "Data", "width": 150},
+			{"fieldname": "net_amount", "label": "Net Amount", "fieldtype": "Currency", "width": 130},
+			{"fieldname": "tax_amount", "label": "Tax Amount", "fieldtype": "Currency", "width": 130},
+			{"fieldname": "gross_amount", "label": "Gross Amount", "fieldtype": "Currency", "width": 130},
 		]
diff --git a/erpnext/regional/saudi_arabia/setup.py b/erpnext/regional/saudi_arabia/setup.py
index d2ef6f3..7f41c46 100644
--- a/erpnext/regional/saudi_arabia/setup.py
+++ b/erpnext/regional/saudi_arabia/setup.py
@@ -3,14 +3,18 @@
 
 import frappe
 from frappe.permissions import add_permission, update_permission_property
-from erpnext.regional.saudi_arabia.wizard.operations.setup_ksa_vat_setting import create_ksa_vat_setting
+from erpnext.regional.saudi_arabia.wizard.operations.setup_ksa_vat_setting import (
+	create_ksa_vat_setting,
+)
 from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 
+
 def setup(company=None, patch=True):
 	add_print_formats()
 	add_permissions()
 	make_custom_fields()
 
+
 def add_print_formats():
 	frappe.reload_doc("regional", "print_format", "detailed_tax_invoice", force=True)
 	frappe.reload_doc("regional", "print_format", "simplified_tax_invoice", force=True)
@@ -18,19 +22,27 @@
 	frappe.reload_doc("regional", "print_format", "ksa_vat_invoice", force=True)
 	frappe.reload_doc("regional", "print_format", "ksa_pos_invoice", force=True)
 
-	for d in ('Simplified Tax Invoice', 'Detailed Tax Invoice', 'Tax Invoice', 'KSA VAT Invoice', 'KSA POS Invoice'):
+	for d in (
+		"Simplified Tax Invoice",
+		"Detailed Tax Invoice",
+		"Tax Invoice",
+		"KSA VAT Invoice",
+		"KSA POS Invoice",
+	):
 		frappe.db.set_value("Print Format", d, "disabled", 0)
 
+
 def add_permissions():
 	"""Add Permissions for KSA VAT Setting."""
-	add_permission('KSA VAT Setting', 'All', 0)
-	for role in ('Accounts Manager', 'Accounts User', 'System Manager'):
-		add_permission('KSA VAT Setting', role, 0)
-		update_permission_property('KSA VAT Setting', role, 0, 'write', 1)
-		update_permission_property('KSA VAT Setting', role, 0, 'create', 1)
+	add_permission("KSA VAT Setting", "All", 0)
+	for role in ("Accounts Manager", "Accounts User", "System Manager"):
+		add_permission("KSA VAT Setting", role, 0)
+		update_permission_property("KSA VAT Setting", role, 0, "write", 1)
+		update_permission_property("KSA VAT Setting", role, 0, "create", 1)
 
 	"""Enable KSA VAT Report"""
-	frappe.db.set_value('Report', 'KSA VAT', 'disabled', 0)
+	frappe.db.set_value("Report", "KSA VAT", "disabled", 0)
+
 
 def make_custom_fields():
 	"""Create Custom fields
@@ -38,71 +50,124 @@
 	- Company Name in Arabic
 	- Address in Arabic
 	"""
-	is_zero_rated = dict(fieldname='is_zero_rated', label='Is Zero Rated',
-		fieldtype='Check', fetch_from='item_code.is_zero_rated', insert_after='description',
-		print_hide=1)
+	is_zero_rated = dict(
+		fieldname="is_zero_rated",
+		label="Is Zero Rated",
+		fieldtype="Check",
+		fetch_from="item_code.is_zero_rated",
+		insert_after="description",
+		print_hide=1,
+	)
 
-	is_exempt = dict(fieldname='is_exempt', label='Is Exempt',
-		fieldtype='Check', fetch_from='item_code.is_exempt', insert_after='is_zero_rated',
-		print_hide=1)
+	is_exempt = dict(
+		fieldname="is_exempt",
+		label="Is Exempt",
+		fieldtype="Check",
+		fetch_from="item_code.is_exempt",
+		insert_after="is_zero_rated",
+		print_hide=1,
+	)
 
 	purchase_invoice_fields = [
-			dict(fieldname='company_trn', label='Company TRN',
-				fieldtype='Read Only', insert_after='shipping_address',
-				fetch_from='company.tax_id', print_hide=1),
-			dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic',
-				fieldtype='Read Only', insert_after='supplier_name',
-				fetch_from='supplier.supplier_name_in_arabic', print_hide=1)
-		]
+		dict(
+			fieldname="company_trn",
+			label="Company TRN",
+			fieldtype="Read Only",
+			insert_after="shipping_address",
+			fetch_from="company.tax_id",
+			print_hide=1,
+		),
+		dict(
+			fieldname="supplier_name_in_arabic",
+			label="Supplier Name in Arabic",
+			fieldtype="Read Only",
+			insert_after="supplier_name",
+			fetch_from="supplier.supplier_name_in_arabic",
+			print_hide=1,
+		),
+	]
 
 	sales_invoice_fields = [
-			dict(fieldname='company_trn', label='Company TRN',
-				fieldtype='Read Only', insert_after='company_address',
-				fetch_from='company.tax_id', print_hide=1),
-			dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic',
-				fieldtype='Read Only', insert_after='customer_name',
-				fetch_from='customer.customer_name_in_arabic', print_hide=1),
-			dict(fieldname='ksa_einv_qr', label='KSA E-Invoicing QR',
-				fieldtype='Attach Image', read_only=1, no_copy=1, hidden=1)
-		]
+		dict(
+			fieldname="company_trn",
+			label="Company TRN",
+			fieldtype="Read Only",
+			insert_after="company_address",
+			fetch_from="company.tax_id",
+			print_hide=1,
+		),
+		dict(
+			fieldname="customer_name_in_arabic",
+			label="Customer Name in Arabic",
+			fieldtype="Read Only",
+			insert_after="customer_name",
+			fetch_from="customer.customer_name_in_arabic",
+			print_hide=1,
+		),
+		dict(
+			fieldname="ksa_einv_qr",
+			label="KSA E-Invoicing QR",
+			fieldtype="Attach Image",
+			read_only=1,
+			no_copy=1,
+			hidden=1,
+		),
+	]
 
 	custom_fields = {
-		'Item': [is_zero_rated, is_exempt],
-		'Customer': [
-			dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic',
-				fieldtype='Data', insert_after='customer_name'),
+		"Item": [is_zero_rated, is_exempt],
+		"Customer": [
+			dict(
+				fieldname="customer_name_in_arabic",
+				label="Customer Name in Arabic",
+				fieldtype="Data",
+				insert_after="customer_name",
+			),
 		],
-		'Supplier': [
-			dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic',
-				fieldtype='Data', insert_after='supplier_name'),
+		"Supplier": [
+			dict(
+				fieldname="supplier_name_in_arabic",
+				label="Supplier Name in Arabic",
+				fieldtype="Data",
+				insert_after="supplier_name",
+			),
 		],
-		'Purchase Invoice': purchase_invoice_fields,
-		'Purchase Order': purchase_invoice_fields,
-		'Purchase Receipt': purchase_invoice_fields,
-		'Sales Invoice': sales_invoice_fields,
-		'POS Invoice': sales_invoice_fields,
-		'Sales Order': sales_invoice_fields,
-		'Delivery Note': sales_invoice_fields,
-		'Sales Invoice Item': [is_zero_rated, is_exempt],
-		'POS Invoice Item': [is_zero_rated, is_exempt],
-		'Purchase Invoice Item': [is_zero_rated, is_exempt],
-		'Sales Order Item': [is_zero_rated, is_exempt],
-		'Delivery Note Item': [is_zero_rated, is_exempt],
-		'Quotation Item': [is_zero_rated, is_exempt],
-		'Purchase Order Item': [is_zero_rated, is_exempt],
-		'Purchase Receipt Item': [is_zero_rated, is_exempt],
-		'Supplier Quotation Item': [is_zero_rated, is_exempt],
-		'Address': [
-			dict(fieldname='address_in_arabic', label='Address in Arabic',
-				fieldtype='Data',insert_after='address_line2')
+		"Purchase Invoice": purchase_invoice_fields,
+		"Purchase Order": purchase_invoice_fields,
+		"Purchase Receipt": purchase_invoice_fields,
+		"Sales Invoice": sales_invoice_fields,
+		"POS Invoice": sales_invoice_fields,
+		"Sales Order": sales_invoice_fields,
+		"Delivery Note": sales_invoice_fields,
+		"Sales Invoice Item": [is_zero_rated, is_exempt],
+		"POS Invoice Item": [is_zero_rated, is_exempt],
+		"Purchase Invoice Item": [is_zero_rated, is_exempt],
+		"Sales Order Item": [is_zero_rated, is_exempt],
+		"Delivery Note Item": [is_zero_rated, is_exempt],
+		"Quotation Item": [is_zero_rated, is_exempt],
+		"Purchase Order Item": [is_zero_rated, is_exempt],
+		"Purchase Receipt Item": [is_zero_rated, is_exempt],
+		"Supplier Quotation Item": [is_zero_rated, is_exempt],
+		"Address": [
+			dict(
+				fieldname="address_in_arabic",
+				label="Address in Arabic",
+				fieldtype="Data",
+				insert_after="address_line2",
+			)
 		],
-		'Company': [
-			dict(fieldname='company_name_in_arabic', label='Company Name In Arabic',
-				fieldtype='Data', insert_after='company_name')
-		]
+		"Company": [
+			dict(
+				fieldname="company_name_in_arabic",
+				label="Company Name In Arabic",
+				fieldtype="Data",
+				insert_after="company_name",
+			)
+		],
 	}
 
 	create_custom_fields(custom_fields, ignore_validate=True, update=True)
 
+
 def update_regional_tax_settings(country, company):
 	create_ksa_vat_setting(company)
diff --git a/erpnext/regional/saudi_arabia/utils.py b/erpnext/regional/saudi_arabia/utils.py
index a03c3f0..b47adc9 100644
--- a/erpnext/regional/saudi_arabia/utils.py
+++ b/erpnext/regional/saudi_arabia/utils.py
@@ -13,21 +13,25 @@
 
 def create_qr_code(doc, method=None):
 	region = get_region(doc.company)
-	if region not in ['Saudi Arabia']:
+	if region not in ["Saudi Arabia"]:
 		return
 
 	# if QR Code field not present, create it. Invoices without QR are invalid as per law.
-	if not hasattr(doc, 'ksa_einv_qr'):
-		create_custom_fields({
-			doc.doctype: [
-				dict(
-					fieldname='ksa_einv_qr',
-					label='KSA E-Invoicing QR',
-					fieldtype='Attach Image',
-					read_only=1, no_copy=1, hidden=1
-				)
-			]
-		})
+	if not hasattr(doc, "ksa_einv_qr"):
+		create_custom_fields(
+			{
+				doc.doctype: [
+					dict(
+						fieldname="ksa_einv_qr",
+						label="KSA E-Invoicing QR",
+						fieldtype="Attach Image",
+						read_only=1,
+						no_copy=1,
+						hidden=1,
+					)
+				]
+			}
+		)
 
 	# Don't create QR Code if it already exists
 	qr_code = doc.get("ksa_einv_qr")
@@ -37,113 +41,129 @@
 	meta = frappe.get_meta(doc.doctype)
 
 	if "ksa_einv_qr" in [d.fieldname for d in meta.get_image_fields()]:
-		''' TLV conversion for
+		"""TLV conversion for
 		1. Seller's Name
 		2. VAT Number
 		3. Time Stamp
 		4. Invoice Amount
 		5. VAT Amount
-		'''
+		"""
 		tlv_array = []
 		# Sellers Name
 
-		seller_name = frappe.db.get_value(
-			'Company',
-			doc.company,
-			'company_name_in_arabic')
+		seller_name = frappe.db.get_value("Company", doc.company, "company_name_in_arabic")
 
 		if not seller_name:
-			frappe.throw(_('Arabic name missing for {} in the company document').format(doc.company))
+			frappe.throw(_("Arabic name missing for {} in the company document").format(doc.company))
 
 		tag = bytes([1]).hex()
-		length = bytes([len(seller_name.encode('utf-8'))]).hex()
-		value = seller_name.encode('utf-8').hex()
-		tlv_array.append(''.join([tag, length, value]))
+		length = bytes([len(seller_name.encode("utf-8"))]).hex()
+		value = seller_name.encode("utf-8").hex()
+		tlv_array.append("".join([tag, length, value]))
 
 		# VAT Number
-		tax_id = frappe.db.get_value('Company', doc.company, 'tax_id')
+		tax_id = frappe.db.get_value("Company", doc.company, "tax_id")
 		if not tax_id:
-			frappe.throw(_('Tax ID missing for {} in the company document').format(doc.company))
+			frappe.throw(_("Tax ID missing for {} in the company document").format(doc.company))
 
 		tag = bytes([2]).hex()
 		length = bytes([len(tax_id)]).hex()
-		value = tax_id.encode('utf-8').hex()
-		tlv_array.append(''.join([tag, length, value]))
+		value = tax_id.encode("utf-8").hex()
+		tlv_array.append("".join([tag, length, value]))
 
 		# Time Stamp
 		posting_date = getdate(doc.posting_date)
 		time = get_time(doc.posting_time)
 		seconds = time.hour * 60 * 60 + time.minute * 60 + time.second
 		time_stamp = add_to_date(posting_date, seconds=seconds)
-		time_stamp = time_stamp.strftime('%Y-%m-%dT%H:%M:%SZ')
+		time_stamp = time_stamp.strftime("%Y-%m-%dT%H:%M:%SZ")
 
 		tag = bytes([3]).hex()
 		length = bytes([len(time_stamp)]).hex()
-		value = time_stamp.encode('utf-8').hex()
-		tlv_array.append(''.join([tag, length, value]))
+		value = time_stamp.encode("utf-8").hex()
+		tlv_array.append("".join([tag, length, value]))
 
 		# Invoice Amount
 		invoice_amount = str(doc.grand_total)
 		tag = bytes([4]).hex()
 		length = bytes([len(invoice_amount)]).hex()
-		value = invoice_amount.encode('utf-8').hex()
-		tlv_array.append(''.join([tag, length, value]))
+		value = invoice_amount.encode("utf-8").hex()
+		tlv_array.append("".join([tag, length, value]))
 
 		# VAT Amount
-		vat_amount = str(doc.total_taxes_and_charges)
+		vat_amount = str(get_vat_amount(doc))
 
 		tag = bytes([5]).hex()
 		length = bytes([len(vat_amount)]).hex()
-		value = vat_amount.encode('utf-8').hex()
-		tlv_array.append(''.join([tag, length, value]))
+		value = vat_amount.encode("utf-8").hex()
+		tlv_array.append("".join([tag, length, value]))
 
 		# Joining bytes into one
-		tlv_buff = ''.join(tlv_array)
+		tlv_buff = "".join(tlv_array)
 
 		# base64 conversion for QR Code
 		base64_string = b64encode(bytes.fromhex(tlv_buff)).decode()
 
 		qr_image = io.BytesIO()
-		url = qr_create(base64_string, error='L')
+		url = qr_create(base64_string, error="L")
 		url.png(qr_image, scale=2, quiet_zone=1)
 
 		name = frappe.generate_hash(doc.name, 5)
 
 		# making file
 		filename = f"QRCode-{name}.png".replace(os.path.sep, "__")
-		_file = frappe.get_doc({
-			"doctype": "File",
-			"file_name": filename,
-			"is_private": 0,
-			"content": qr_image.getvalue(),
-			"attached_to_doctype": doc.get("doctype"),
-			"attached_to_name": doc.get("name"),
-			"attached_to_field": "ksa_einv_qr"
-		})
+		_file = frappe.get_doc(
+			{
+				"doctype": "File",
+				"file_name": filename,
+				"is_private": 0,
+				"content": qr_image.getvalue(),
+				"attached_to_doctype": doc.get("doctype"),
+				"attached_to_name": doc.get("name"),
+				"attached_to_field": "ksa_einv_qr",
+			}
+		)
 
 		_file.save()
 
 		# assigning to document
-		doc.db_set('ksa_einv_qr', _file.file_url)
+		doc.db_set("ksa_einv_qr", _file.file_url)
 		doc.notify_update()
 
 
+def get_vat_amount(doc):
+	vat_settings = frappe.db.get_value("KSA VAT Setting", {"company": doc.company})
+	vat_accounts = []
+	vat_amount = 0
+
+	if vat_settings:
+		vat_settings_doc = frappe.get_cached_doc("KSA VAT Setting", vat_settings)
+
+		for row in vat_settings_doc.get("ksa_vat_sales_accounts"):
+			vat_accounts.append(row.account)
+
+	for tax in doc.get("taxes"):
+		if tax.account_head in vat_accounts:
+			vat_amount += tax.tax_amount
+
+	return vat_amount
+
+
 def delete_qr_code_file(doc, method=None):
 	region = get_region(doc.company)
-	if region not in ['Saudi Arabia']:
+	if region not in ["Saudi Arabia"]:
 		return
 
-	if hasattr(doc, 'ksa_einv_qr'):
-		if doc.get('ksa_einv_qr'):
-			file_doc = frappe.get_list('File', {
-				'file_url': doc.get('ksa_einv_qr')
-			})
+	if hasattr(doc, "ksa_einv_qr"):
+		if doc.get("ksa_einv_qr"):
+			file_doc = frappe.get_list("File", {"file_url": doc.get("ksa_einv_qr")})
 			if len(file_doc):
-				frappe.delete_doc('File', file_doc[0].name)
+				frappe.delete_doc("File", file_doc[0].name)
+
 
 def delete_vat_settings_for_company(doc, method=None):
-	if doc.country != 'Saudi Arabia':
+	if doc.country != "Saudi Arabia":
 		return
 
-	if frappe.db.exists('KSA VAT Setting', doc.name):
-		frappe.delete_doc('KSA VAT Setting', doc.name)
+	if frappe.db.exists("KSA VAT Setting", doc.name):
+		frappe.delete_doc("KSA VAT Setting", doc.name)
diff --git a/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py b/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py
index 97300dc..66d9df2 100644
--- a/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py
+++ b/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py
@@ -5,39 +5,42 @@
 
 
 def create_ksa_vat_setting(company):
-    """On creation of first company. Creates KSA VAT Setting"""
+	"""On creation of first company. Creates KSA VAT Setting"""
 
-    company = frappe.get_doc('Company', company)
+	company = frappe.get_doc("Company", company)
 
-    file_path = os.path.join(os.path.dirname(__file__), '..', 'data', 'ksa_vat_settings.json')
-    with open(file_path, 'r') as json_file:
-        account_data = json.load(json_file)
+	file_path = os.path.join(os.path.dirname(__file__), "..", "data", "ksa_vat_settings.json")
+	with open(file_path, "r") as json_file:
+		account_data = json.load(json_file)
 
-    # Creating KSA VAT Setting
-    ksa_vat_setting = frappe.get_doc({
-        'doctype': 'KSA VAT Setting',
-        'company': company.name
-    })
+	# Creating KSA VAT Setting
+	ksa_vat_setting = frappe.get_doc({"doctype": "KSA VAT Setting", "company": company.name})
 
-    for data in account_data:
-        if data['type'] == 'Sales Account':
-            for row in data['accounts']:
-                item_tax_template = row['item_tax_template']
-                account = row['account']
-                ksa_vat_setting.append('ksa_vat_sales_accounts', {
-                    'title': row['title'],
-                    'item_tax_template': f'{item_tax_template} - {company.abbr}',
-                    'account': f'{account} - {company.abbr}'
-                })
+	for data in account_data:
+		if data["type"] == "Sales Account":
+			for row in data["accounts"]:
+				item_tax_template = row["item_tax_template"]
+				account = row["account"]
+				ksa_vat_setting.append(
+					"ksa_vat_sales_accounts",
+					{
+						"title": row["title"],
+						"item_tax_template": f"{item_tax_template} - {company.abbr}",
+						"account": f"{account} - {company.abbr}",
+					},
+				)
 
-        elif data['type'] == 'Purchase Account':
-            for row in data['accounts']:
-                item_tax_template = row['item_tax_template']
-                account = row['account']
-                ksa_vat_setting.append('ksa_vat_purchase_accounts', {
-                    'title': row['title'],
-                    'item_tax_template': f'{item_tax_template} - {company.abbr}',
-                    'account': f'{account} - {company.abbr}'
-                })
+		elif data["type"] == "Purchase Account":
+			for row in data["accounts"]:
+				item_tax_template = row["item_tax_template"]
+				account = row["account"]
+				ksa_vat_setting.append(
+					"ksa_vat_purchase_accounts",
+					{
+						"title": row["title"],
+						"item_tax_template": f"{item_tax_template} - {company.abbr}",
+						"account": f"{account} - {company.abbr}",
+					},
+				)
 
-    ksa_vat_setting.save()
+	ksa_vat_setting.save()
diff --git a/erpnext/regional/south_africa/setup.py b/erpnext/regional/south_africa/setup.py
index 6af135b..289f272 100644
--- a/erpnext/regional/south_africa/setup.py
+++ b/erpnext/regional/south_africa/setup.py
@@ -6,44 +6,53 @@
 from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 from frappe.permissions import add_permission, update_permission_property
 
+
 def setup(company=None, patch=True):
 	make_custom_fields()
 	add_permissions()
 
+
 def make_custom_fields(update=True):
-	is_zero_rated = dict(fieldname='is_zero_rated', label='Is Zero Rated',
-		fieldtype='Check', fetch_from='item_code.is_zero_rated',
-		insert_after='description', print_hide=1)
+	is_zero_rated = dict(
+		fieldname="is_zero_rated",
+		label="Is Zero Rated",
+		fieldtype="Check",
+		fetch_from="item_code.is_zero_rated",
+		insert_after="description",
+		print_hide=1,
+	)
 	custom_fields = {
-		'Item': [
-			dict(fieldname='is_zero_rated', label='Is Zero Rated',
-				fieldtype='Check', insert_after='item_group',
-				print_hide=1)
+		"Item": [
+			dict(
+				fieldname="is_zero_rated",
+				label="Is Zero Rated",
+				fieldtype="Check",
+				insert_after="item_group",
+				print_hide=1,
+			)
 		],
-		'Sales Invoice Item': is_zero_rated,
-		'Purchase Invoice Item': is_zero_rated
+		"Sales Invoice Item": is_zero_rated,
+		"Purchase Invoice Item": is_zero_rated,
 	}
 
 	create_custom_fields(custom_fields, update=update)
 
+
 def add_permissions():
 	"""Add Permissions for South Africa VAT Settings and South Africa VAT Account
-		and VAT Audit Report"""
-	for doctype in ('South Africa VAT Settings', 'South Africa VAT Account'):
-		add_permission(doctype, 'All', 0)
-		for role in ('Accounts Manager', 'Accounts User', 'System Manager'):
+	and VAT Audit Report"""
+	for doctype in ("South Africa VAT Settings", "South Africa VAT Account"):
+		add_permission(doctype, "All", 0)
+		for role in ("Accounts Manager", "Accounts User", "System Manager"):
 			add_permission(doctype, role, 0)
-			update_permission_property(doctype, role, 0, 'write', 1)
-			update_permission_property(doctype, role, 0, 'create', 1)
+			update_permission_property(doctype, role, 0, "write", 1)
+			update_permission_property(doctype, role, 0, "create", 1)
 
-
-	if not frappe.db.get_value('Custom Role', dict(report="VAT Audit Report")):
-		frappe.get_doc(dict(
-			doctype='Custom Role',
-			report="VAT Audit Report",
-			roles= [
-				dict(role='Accounts User'),
-				dict(role='Accounts Manager'),
-				dict(role='Auditor')
-			]
-		)).insert()
+	if not frappe.db.get_value("Custom Role", dict(report="VAT Audit Report")):
+		frappe.get_doc(
+			dict(
+				doctype="Custom Role",
+				report="VAT Audit Report",
+				roles=[dict(role="Accounts User"), dict(role="Accounts Manager"), dict(role="Auditor")],
+			)
+		).insert()
diff --git a/erpnext/regional/turkey/setup.py b/erpnext/regional/turkey/setup.py
index 1d3770a..c915189 100644
--- a/erpnext/regional/turkey/setup.py
+++ b/erpnext/regional/turkey/setup.py
@@ -1,2 +1,2 @@
 def setup(company=None, patch=True):
-    pass
+	pass
diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py
index 922443b..c77fdcc 100644
--- a/erpnext/regional/united_arab_emirates/setup.py
+++ b/erpnext/regional/united_arab_emirates/setup.py
@@ -7,6 +7,7 @@
 from frappe.permissions import add_permission, update_permission_property
 from erpnext.payroll.doctype.gratuity_rule.gratuity_rule import get_gratuity_rule
 
+
 def setup(company=None, patch=True):
 	make_custom_fields()
 	add_print_formats()
@@ -14,145 +15,270 @@
 	add_permissions()
 	create_gratuity_rule()
 
+
 def make_custom_fields():
-	is_zero_rated = dict(fieldname='is_zero_rated', label='Is Zero Rated',
-		fieldtype='Check', fetch_from='item_code.is_zero_rated', insert_after='description',
-		print_hide=1)
-	is_exempt = dict(fieldname='is_exempt', label='Is Exempt',
-		fieldtype='Check', fetch_from='item_code.is_exempt', insert_after='is_zero_rated',
-		print_hide=1)
+	is_zero_rated = dict(
+		fieldname="is_zero_rated",
+		label="Is Zero Rated",
+		fieldtype="Check",
+		fetch_from="item_code.is_zero_rated",
+		insert_after="description",
+		print_hide=1,
+	)
+	is_exempt = dict(
+		fieldname="is_exempt",
+		label="Is Exempt",
+		fieldtype="Check",
+		fetch_from="item_code.is_exempt",
+		insert_after="is_zero_rated",
+		print_hide=1,
+	)
 
 	invoice_fields = [
-		dict(fieldname='vat_section', label='VAT Details', fieldtype='Section Break',
-			insert_after='group_same_items', print_hide=1, collapsible=1),
-		dict(fieldname='permit_no', label='Permit Number',
-			fieldtype='Data', insert_after='vat_section', print_hide=1),
+		dict(
+			fieldname="vat_section",
+			label="VAT Details",
+			fieldtype="Section Break",
+			insert_after="group_same_items",
+			print_hide=1,
+			collapsible=1,
+		),
+		dict(
+			fieldname="permit_no",
+			label="Permit Number",
+			fieldtype="Data",
+			insert_after="vat_section",
+			print_hide=1,
+		),
 	]
 
 	purchase_invoice_fields = [
-			dict(fieldname='company_trn', label='Company TRN',
-				fieldtype='Read Only', insert_after='shipping_address',
-				fetch_from='company.tax_id', print_hide=1),
-			dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic',
-				fieldtype='Read Only', insert_after='supplier_name',
-				fetch_from='supplier.supplier_name_in_arabic', print_hide=1),
-			dict(fieldname='recoverable_standard_rated_expenses', print_hide=1, default='0',
-				label='Recoverable Standard Rated Expenses (AED)', insert_after='permit_no',
-				fieldtype='Currency', ),
-			dict(fieldname='reverse_charge', label='Reverse Charge Applicable',
-				fieldtype='Select', insert_after='recoverable_standard_rated_expenses', print_hide=1,
-				options='Y\nN', default='N'),
-			dict(fieldname='recoverable_reverse_charge', label='Recoverable Reverse Charge (Percentage)',
-				insert_after='reverse_charge', fieldtype='Percent', print_hide=1,
-				depends_on="eval:doc.reverse_charge=='Y'", default='100.000'),
-		]
+		dict(
+			fieldname="company_trn",
+			label="Company TRN",
+			fieldtype="Read Only",
+			insert_after="shipping_address",
+			fetch_from="company.tax_id",
+			print_hide=1,
+		),
+		dict(
+			fieldname="supplier_name_in_arabic",
+			label="Supplier Name in Arabic",
+			fieldtype="Read Only",
+			insert_after="supplier_name",
+			fetch_from="supplier.supplier_name_in_arabic",
+			print_hide=1,
+		),
+		dict(
+			fieldname="recoverable_standard_rated_expenses",
+			print_hide=1,
+			default="0",
+			label="Recoverable Standard Rated Expenses (AED)",
+			insert_after="permit_no",
+			fieldtype="Currency",
+		),
+		dict(
+			fieldname="reverse_charge",
+			label="Reverse Charge Applicable",
+			fieldtype="Select",
+			insert_after="recoverable_standard_rated_expenses",
+			print_hide=1,
+			options="Y\nN",
+			default="N",
+		),
+		dict(
+			fieldname="recoverable_reverse_charge",
+			label="Recoverable Reverse Charge (Percentage)",
+			insert_after="reverse_charge",
+			fieldtype="Percent",
+			print_hide=1,
+			depends_on="eval:doc.reverse_charge=='Y'",
+			default="100.000",
+		),
+	]
 
 	sales_invoice_fields = [
-			dict(fieldname='company_trn', label='Company TRN',
-				fieldtype='Read Only', insert_after='company_address',
-				fetch_from='company.tax_id', print_hide=1),
-			dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic',
-				fieldtype='Read Only', insert_after='customer_name',
-				fetch_from='customer.customer_name_in_arabic', print_hide=1),
-			dict(fieldname='vat_emirate', label='VAT Emirate', insert_after='permit_no', fieldtype='Select',
-				options='\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain',
-				fetch_from='company_address.emirate'),
-			dict(fieldname='tourist_tax_return', label='Tax Refund provided to Tourists (AED)',
-				insert_after='vat_emirate', fieldtype='Currency', print_hide=1, default='0'),
-		]
+		dict(
+			fieldname="company_trn",
+			label="Company TRN",
+			fieldtype="Read Only",
+			insert_after="company_address",
+			fetch_from="company.tax_id",
+			print_hide=1,
+		),
+		dict(
+			fieldname="customer_name_in_arabic",
+			label="Customer Name in Arabic",
+			fieldtype="Read Only",
+			insert_after="customer_name",
+			fetch_from="customer.customer_name_in_arabic",
+			print_hide=1,
+		),
+		dict(
+			fieldname="vat_emirate",
+			label="VAT Emirate",
+			insert_after="permit_no",
+			fieldtype="Select",
+			options="\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain",
+			fetch_from="company_address.emirate",
+		),
+		dict(
+			fieldname="tourist_tax_return",
+			label="Tax Refund provided to Tourists (AED)",
+			insert_after="vat_emirate",
+			fieldtype="Currency",
+			print_hide=1,
+			default="0",
+		),
+	]
 
 	invoice_item_fields = [
-		dict(fieldname='tax_code', label='Tax Code',
-			fieldtype='Read Only', fetch_from='item_code.tax_code', insert_after='description',
-			allow_on_submit=1, print_hide=1),
-		dict(fieldname='tax_rate', label='Tax Rate',
-			fieldtype='Float', insert_after='tax_code',
-			print_hide=1, hidden=1, read_only=1),
-		dict(fieldname='tax_amount', label='Tax Amount',
-			fieldtype='Currency', insert_after='tax_rate',
-			print_hide=1, hidden=1, read_only=1, options="currency"),
-		dict(fieldname='total_amount', label='Total Amount',
-			fieldtype='Currency', insert_after='tax_amount',
-			print_hide=1, hidden=1, read_only=1, options="currency"),
+		dict(
+			fieldname="tax_code",
+			label="Tax Code",
+			fieldtype="Read Only",
+			fetch_from="item_code.tax_code",
+			insert_after="description",
+			allow_on_submit=1,
+			print_hide=1,
+		),
+		dict(
+			fieldname="tax_rate",
+			label="Tax Rate",
+			fieldtype="Float",
+			insert_after="tax_code",
+			print_hide=1,
+			hidden=1,
+			read_only=1,
+		),
+		dict(
+			fieldname="tax_amount",
+			label="Tax Amount",
+			fieldtype="Currency",
+			insert_after="tax_rate",
+			print_hide=1,
+			hidden=1,
+			read_only=1,
+			options="currency",
+		),
+		dict(
+			fieldname="total_amount",
+			label="Total Amount",
+			fieldtype="Currency",
+			insert_after="tax_amount",
+			print_hide=1,
+			hidden=1,
+			read_only=1,
+			options="currency",
+		),
 	]
 
 	delivery_date_field = [
-		dict(fieldname='delivery_date', label='Delivery Date',
-			fieldtype='Date', insert_after='item_name', print_hide=1)
+		dict(
+			fieldname="delivery_date",
+			label="Delivery Date",
+			fieldtype="Date",
+			insert_after="item_name",
+			print_hide=1,
+		)
 	]
 
 	custom_fields = {
-		'Item': [
-			dict(fieldname='tax_code', label='Tax Code',
-				fieldtype='Data', insert_after='item_group'),
-			dict(fieldname='is_zero_rated', label='Is Zero Rated',
-				fieldtype='Check', insert_after='tax_code',
-				print_hide=1),
-			dict(fieldname='is_exempt', label='Is Exempt',
-				fieldtype='Check', insert_after='is_zero_rated',
-				print_hide=1)
+		"Item": [
+			dict(fieldname="tax_code", label="Tax Code", fieldtype="Data", insert_after="item_group"),
+			dict(
+				fieldname="is_zero_rated",
+				label="Is Zero Rated",
+				fieldtype="Check",
+				insert_after="tax_code",
+				print_hide=1,
+			),
+			dict(
+				fieldname="is_exempt",
+				label="Is Exempt",
+				fieldtype="Check",
+				insert_after="is_zero_rated",
+				print_hide=1,
+			),
 		],
-		'Customer': [
-			dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic',
-				fieldtype='Data', insert_after='customer_name'),
+		"Customer": [
+			dict(
+				fieldname="customer_name_in_arabic",
+				label="Customer Name in Arabic",
+				fieldtype="Data",
+				insert_after="customer_name",
+			),
 		],
-		'Supplier': [
-			dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic',
-				fieldtype='Data', insert_after='supplier_name'),
+		"Supplier": [
+			dict(
+				fieldname="supplier_name_in_arabic",
+				label="Supplier Name in Arabic",
+				fieldtype="Data",
+				insert_after="supplier_name",
+			),
 		],
-		'Address': [
-			dict(fieldname='emirate', label='Emirate', fieldtype='Select', insert_after='state',
-			options='\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain')
+		"Address": [
+			dict(
+				fieldname="emirate",
+				label="Emirate",
+				fieldtype="Select",
+				insert_after="state",
+				options="\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain",
+			)
 		],
-		'Purchase Invoice': purchase_invoice_fields + invoice_fields,
-		'Purchase Order': purchase_invoice_fields + invoice_fields,
-		'Purchase Receipt': purchase_invoice_fields + invoice_fields,
-		'Sales Invoice': sales_invoice_fields + invoice_fields,
-		'POS Invoice': sales_invoice_fields + invoice_fields,
-		'Sales Order': sales_invoice_fields + invoice_fields,
-		'Delivery Note': sales_invoice_fields + invoice_fields,
-		'Sales Invoice Item': invoice_item_fields + delivery_date_field + [is_zero_rated, is_exempt],
-		'POS Invoice Item': invoice_item_fields + delivery_date_field + [is_zero_rated, is_exempt],
-		'Purchase Invoice Item': invoice_item_fields,
-		'Sales Order Item': invoice_item_fields,
-		'Delivery Note Item': invoice_item_fields,
-		'Quotation Item': invoice_item_fields,
-		'Purchase Order Item': invoice_item_fields,
-		'Purchase Receipt Item': invoice_item_fields,
-		'Supplier Quotation Item': invoice_item_fields,
+		"Purchase Invoice": purchase_invoice_fields + invoice_fields,
+		"Purchase Order": purchase_invoice_fields + invoice_fields,
+		"Purchase Receipt": purchase_invoice_fields + invoice_fields,
+		"Sales Invoice": sales_invoice_fields + invoice_fields,
+		"POS Invoice": sales_invoice_fields + invoice_fields,
+		"Sales Order": sales_invoice_fields + invoice_fields,
+		"Delivery Note": sales_invoice_fields + invoice_fields,
+		"Sales Invoice Item": invoice_item_fields + delivery_date_field + [is_zero_rated, is_exempt],
+		"POS Invoice Item": invoice_item_fields + delivery_date_field + [is_zero_rated, is_exempt],
+		"Purchase Invoice Item": invoice_item_fields,
+		"Sales Order Item": invoice_item_fields,
+		"Delivery Note Item": invoice_item_fields,
+		"Quotation Item": invoice_item_fields,
+		"Purchase Order Item": invoice_item_fields,
+		"Purchase Receipt Item": invoice_item_fields,
+		"Supplier Quotation Item": invoice_item_fields,
 	}
 
-	create_custom_fields(custom_fields)
+	create_custom_fields(custom_fields, ignore_validate=True)
+
 
 def add_print_formats():
 	frappe.reload_doc("regional", "print_format", "detailed_tax_invoice")
 	frappe.reload_doc("regional", "print_format", "simplified_tax_invoice")
 	frappe.reload_doc("regional", "print_format", "tax_invoice")
 
-	frappe.db.sql(""" update `tabPrint Format` set disabled = 0 where
-		name in('Simplified Tax Invoice', 'Detailed Tax Invoice', 'Tax Invoice') """)
+	frappe.db.sql(
+		""" update `tabPrint Format` set disabled = 0 where
+		name in('Simplified Tax Invoice', 'Detailed Tax Invoice', 'Tax Invoice') """
+	)
+
 
 def add_custom_roles_for_reports():
 	"""Add Access Control to UAE VAT 201."""
-	if not frappe.db.get_value('Custom Role', dict(report='UAE VAT 201')):
-		frappe.get_doc(dict(
-			doctype='Custom Role',
-			report='UAE VAT 201',
-			roles= [
-				dict(role='Accounts User'),
-				dict(role='Accounts Manager'),
-				dict(role='Auditor')
-			]
-		)).insert()
+	if not frappe.db.get_value("Custom Role", dict(report="UAE VAT 201")):
+		frappe.get_doc(
+			dict(
+				doctype="Custom Role",
+				report="UAE VAT 201",
+				roles=[dict(role="Accounts User"), dict(role="Accounts Manager"), dict(role="Auditor")],
+			)
+		).insert()
+
 
 def add_permissions():
 	"""Add Permissions for UAE VAT Settings and UAE VAT Account."""
-	for doctype in ('UAE VAT Settings', 'UAE VAT Account'):
-		add_permission(doctype, 'All', 0)
-		for role in ('Accounts Manager', 'Accounts User', 'System Manager'):
+	for doctype in ("UAE VAT Settings", "UAE VAT Account"):
+		add_permission(doctype, "All", 0)
+		for role in ("Accounts Manager", "Accounts User", "System Manager"):
 			add_permission(doctype, role, 0)
-			update_permission_property(doctype, role, 0, 'write', 1)
-			update_permission_property(doctype, role, 0, 'create', 1)
+			update_permission_property(doctype, role, 0, "write", 1)
+			update_permission_property(doctype, role, 0, "create", 1)
+
 
 def create_gratuity_rule():
 	rule_1 = rule_2 = rule_3 = None
@@ -160,7 +286,11 @@
 	# Rule Under Limited Contract
 	slabs = get_slab_for_limited_contract()
 	if not frappe.db.exists("Gratuity Rule", "Rule Under Limited Contract (UAE)"):
-		rule_1 = get_gratuity_rule("Rule Under Limited Contract (UAE)", slabs, calculate_gratuity_amount_based_on="Sum of all previous slabs")
+		rule_1 = get_gratuity_rule(
+			"Rule Under Limited Contract (UAE)",
+			slabs,
+			calculate_gratuity_amount_based_on="Sum of all previous slabs",
+		)
 
 	# Rule Under Unlimited Contract on termination
 	slabs = get_slab_for_unlimited_contract_on_termination()
@@ -172,7 +302,7 @@
 	if not frappe.db.exists("Gratuity Rule", "Rule Under Unlimited Contract on resignation (UAE)"):
 		rule_3 = get_gratuity_rule("Rule Under Unlimited Contract on resignation (UAE)", slabs)
 
-	#for applicable salary component user need to set this by its own
+	# for applicable salary component user need to set this by its own
 	if rule_1:
 		rule_1.flags.ignore_mandatory = True
 		rule_1.save()
@@ -185,61 +315,29 @@
 
 
 def get_slab_for_limited_contract():
-	return [{
-		"from_year": 0,
-		"to_year":1,
-		"fraction_of_applicable_earnings": 0
-	},
-	{
-		"from_year": 1,
-		"to_year":5,
-		"fraction_of_applicable_earnings": 21/30
-	},
-	{
-		"from_year": 5,
-		"to_year":0,
-		"fraction_of_applicable_earnings": 1
-	}]
+	return [
+		{"from_year": 0, "to_year": 1, "fraction_of_applicable_earnings": 0},
+		{"from_year": 1, "to_year": 5, "fraction_of_applicable_earnings": 21 / 30},
+		{"from_year": 5, "to_year": 0, "fraction_of_applicable_earnings": 1},
+	]
+
 
 def get_slab_for_unlimited_contract_on_termination():
-	return [{
-		"from_year": 0,
-		"to_year":1,
-		"fraction_of_applicable_earnings": 0
-	},
-	{
-		"from_year": 1,
-		"to_year":5,
-		"fraction_of_applicable_earnings": 21/30
-	},
-	{
-		"from_year": 5,
-		"to_year":0,
-		"fraction_of_applicable_earnings": 1
-	}]
+	return [
+		{"from_year": 0, "to_year": 1, "fraction_of_applicable_earnings": 0},
+		{"from_year": 1, "to_year": 5, "fraction_of_applicable_earnings": 21 / 30},
+		{"from_year": 5, "to_year": 0, "fraction_of_applicable_earnings": 1},
+	]
+
 
 def get_slab_for_unlimited_contract_on_resignation():
-	fraction_1 = 1/3 * 21/30
-	fraction_2 = 2/3 * 21/30
-	fraction_3 = 21/30
+	fraction_1 = 1 / 3 * 21 / 30
+	fraction_2 = 2 / 3 * 21 / 30
+	fraction_3 = 21 / 30
 
-	return [{
-		"from_year": 0,
-		"to_year":1,
-		"fraction_of_applicable_earnings": 0
-	},
-	{
-		"from_year": 1,
-		"to_year":3,
-		"fraction_of_applicable_earnings": fraction_1
-	},
-	{
-		"from_year": 3,
-		"to_year":5,
-		"fraction_of_applicable_earnings": fraction_2
-	},
-	{
-		"from_year": 5,
-		"to_year":0,
-		"fraction_of_applicable_earnings": fraction_3
-	}]
+	return [
+		{"from_year": 0, "to_year": 1, "fraction_of_applicable_earnings": 0},
+		{"from_year": 1, "to_year": 3, "fraction_of_applicable_earnings": fraction_1},
+		{"from_year": 3, "to_year": 5, "fraction_of_applicable_earnings": fraction_2},
+		{"from_year": 5, "to_year": 0, "fraction_of_applicable_earnings": fraction_3},
+	]
diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py
index f350ec4..a910af6 100644
--- a/erpnext/regional/united_arab_emirates/utils.py
+++ b/erpnext/regional/united_arab_emirates/utils.py
@@ -7,7 +7,8 @@
 
 
 def update_itemised_tax_data(doc):
-	if not doc.taxes: return
+	if not doc.taxes:
+		return
 
 	itemised_tax = get_itemised_tax(doc.taxes)
 
@@ -24,37 +25,39 @@
 			for account, rate in item_tax_rate.items():
 				tax_rate += rate
 		elif row.item_code and itemised_tax.get(row.item_code):
-			tax_rate = sum([tax.get('tax_rate', 0) for d, tax in itemised_tax.get(row.item_code).items()])
+			tax_rate = sum([tax.get("tax_rate", 0) for d, tax in itemised_tax.get(row.item_code).items()])
 
-		row.tax_rate = flt(tax_rate, row.precision("tax_rate"))
-		row.tax_amount = flt((row.net_amount * tax_rate) / 100, row.precision("net_amount"))
-		row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount"))
+		meta = frappe.get_meta(row.doctype)
+
+		if meta.has_field("tax_rate"):
+			row.tax_rate = flt(tax_rate, row.precision("tax_rate"))
+			row.tax_amount = flt((row.net_amount * tax_rate) / 100, row.precision("net_amount"))
+			row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount"))
+
 
 def get_account_currency(account):
 	"""Helper function to get account currency."""
 	if not account:
 		return
+
 	def generator():
 		account_currency, company = frappe.get_cached_value(
-			"Account",
-			account,
-			["account_currency",
-			"company"]
+			"Account", account, ["account_currency", "company"]
 		)
 		if not account_currency:
-			account_currency = frappe.get_cached_value('Company',  company,  "default_currency")
+			account_currency = frappe.get_cached_value("Company", company, "default_currency")
 
 		return account_currency
 
 	return frappe.local_cache("account_currency", account, generator)
 
+
 def get_tax_accounts(company):
 	"""Get the list of tax accounts for a specific company."""
 	tax_accounts_dict = frappe._dict()
-	tax_accounts_list = frappe.get_all("UAE VAT Account",
-		filters={"parent": company},
-		fields=["Account"]
-		)
+	tax_accounts_list = frappe.get_all(
+		"UAE VAT Account", filters={"parent": company}, fields=["Account"]
+	)
 
 	if not tax_accounts_list and not frappe.flags.in_test:
 		frappe.throw(_('Please set Vat Accounts for Company: "{0}" in UAE VAT Settings').format(company))
@@ -64,23 +67,24 @@
 
 	return tax_accounts_dict
 
+
 def update_grand_total_for_rcm(doc, method):
 	"""If the Reverse Charge is Applicable subtract the tax amount from the grand total and update in the form."""
-	country = frappe.get_cached_value('Company', doc.company, 'country')
+	country = frappe.get_cached_value("Company", doc.company, "country")
 
-	if country != 'United Arab Emirates':
+	if country != "United Arab Emirates":
 		return
 
 	if not doc.total_taxes_and_charges:
 		return
 
-	if doc.reverse_charge == 'Y':
+	if doc.reverse_charge == "Y":
 		tax_accounts = get_tax_accounts(doc.company)
 
 		base_vat_tax = 0
 		vat_tax = 0
 
-		for tax in doc.get('taxes'):
+		for tax in doc.get("taxes"):
 			if tax.category not in ("Total", "Valuation and Total"):
 				continue
 
@@ -95,6 +99,7 @@
 
 		update_totals(vat_tax, base_vat_tax, doc)
 
+
 def update_totals(vat_tax, base_vat_tax, doc):
 	"""Update the grand total values in the form."""
 	doc.base_grand_total -= base_vat_tax
@@ -106,56 +111,67 @@
 			doc.outstanding_amount = doc.grand_total
 
 		else:
-			doc.rounded_total = round_based_on_smallest_currency_fraction(doc.grand_total,
-				doc.currency, doc.precision("rounded_total"))
-			doc.rounding_adjustment = flt(doc.rounded_total - doc.grand_total,
-				doc.precision("rounding_adjustment"))
+			doc.rounded_total = round_based_on_smallest_currency_fraction(
+				doc.grand_total, doc.currency, doc.precision("rounded_total")
+			)
+			doc.rounding_adjustment = flt(
+				doc.rounded_total - doc.grand_total, doc.precision("rounding_adjustment")
+			)
 			doc.outstanding_amount = doc.rounded_total or doc.grand_total
 
 	doc.in_words = money_in_words(doc.grand_total, doc.currency)
-	doc.base_in_words = money_in_words(doc.base_grand_total, erpnext.get_company_currency(doc.company))
+	doc.base_in_words = money_in_words(
+		doc.base_grand_total, erpnext.get_company_currency(doc.company)
+	)
 	doc.set_payment_schedule()
 
+
 def make_regional_gl_entries(gl_entries, doc):
 	"""Hooked to make_regional_gl_entries in Purchase Invoice.It appends the region specific general ledger entries to the list of GL Entries."""
-	country = frappe.get_cached_value('Company', doc.company, 'country')
+	country = frappe.get_cached_value("Company", doc.company, "country")
 
-	if country != 'United Arab Emirates':
+	if country != "United Arab Emirates":
 		return gl_entries
 
-	if doc.reverse_charge == 'Y':
+	if doc.reverse_charge == "Y":
 		tax_accounts = get_tax_accounts(doc.company)
-		for tax in doc.get('taxes'):
+		for tax in doc.get("taxes"):
 			if tax.category not in ("Total", "Valuation and Total"):
 				continue
 			gl_entries = make_gl_entry(tax, gl_entries, doc, tax_accounts)
 	return gl_entries
 
+
 def make_gl_entry(tax, gl_entries, doc, tax_accounts):
 	dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit"
-	if flt(tax.base_tax_amount_after_discount_amount)  and tax.account_head in tax_accounts:
+	if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in tax_accounts:
 		account_currency = get_account_currency(tax.account_head)
 
-		gl_entries.append(doc.get_gl_dict({
-				"account": tax.account_head,
-				"cost_center": tax.cost_center,
-				"posting_date": doc.posting_date,
-				"against": doc.supplier,
-				dr_or_cr: tax.base_tax_amount_after_discount_amount,
-				dr_or_cr + "_in_account_currency": tax.base_tax_amount_after_discount_amount \
-					if account_currency==doc.company_currency \
-					else tax.tax_amount_after_discount_amount
-			}, account_currency, item=tax
-		))
+		gl_entries.append(
+			doc.get_gl_dict(
+				{
+					"account": tax.account_head,
+					"cost_center": tax.cost_center,
+					"posting_date": doc.posting_date,
+					"against": doc.supplier,
+					dr_or_cr: tax.base_tax_amount_after_discount_amount,
+					dr_or_cr + "_in_account_currency": tax.base_tax_amount_after_discount_amount
+					if account_currency == doc.company_currency
+					else tax.tax_amount_after_discount_amount,
+				},
+				account_currency,
+				item=tax,
+			)
+		)
 	return gl_entries
 
 
 def validate_returns(doc, method):
 	"""Standard Rated expenses should not be set when Reverse Charge Applicable is set."""
-	country = frappe.get_cached_value('Company', doc.company, 'country')
-	if country != 'United Arab Emirates':
+	country = frappe.get_cached_value("Company", doc.company, "country")
+	if country != "United Arab Emirates":
 		return
-	if doc.reverse_charge == 'Y' and  flt(doc.recoverable_standard_rated_expenses) != 0:
-		frappe.throw(_(
-			"Recoverable Standard Rated expenses should not be set when Reverse Charge Applicable is Y"
-		))
+	if doc.reverse_charge == "Y" and flt(doc.recoverable_standard_rated_expenses) != 0:
+		frappe.throw(
+			_("Recoverable Standard Rated expenses should not be set when Reverse Charge Applicable is Y")
+		)
diff --git a/erpnext/regional/united_states/setup.py b/erpnext/regional/united_states/setup.py
index db6a9c3..b8f8572 100644
--- a/erpnext/regional/united_states/setup.py
+++ b/erpnext/regional/united_states/setup.py
@@ -7,40 +7,64 @@
 from frappe.permissions import add_permission, update_permission_property
 from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 
+
 def setup(company=None, patch=True):
 	# Company independent fixtures should be called only once at the first company setup
-	if frappe.db.count('Company', {'country': 'United States'}) <=1:
+	if frappe.db.count("Company", {"country": "United States"}) <= 1:
 		setup_company_independent_fixtures(patch=patch)
 
+
 def setup_company_independent_fixtures(company=None, patch=True):
 	make_custom_fields()
 	add_print_formats()
 
+
 def make_custom_fields(update=True):
 	custom_fields = {
-		'Supplier': [
-			dict(fieldname='irs_1099', fieldtype='Check', insert_after='tax_id',
-				label='Is IRS 1099 reporting required for supplier?')
+		"Supplier": [
+			dict(
+				fieldname="irs_1099",
+				fieldtype="Check",
+				insert_after="tax_id",
+				label="Is IRS 1099 reporting required for supplier?",
+			)
 		],
-		'Sales Order': [
-			dict(fieldname='exempt_from_sales_tax', fieldtype='Check', insert_after='taxes_and_charges',
-				label='Is customer exempted from sales tax?')
+		"Sales Order": [
+			dict(
+				fieldname="exempt_from_sales_tax",
+				fieldtype="Check",
+				insert_after="taxes_and_charges",
+				label="Is customer exempted from sales tax?",
+			)
 		],
-		'Sales Invoice': [
-			dict(fieldname='exempt_from_sales_tax', fieldtype='Check', insert_after='taxes_section',
-				label='Is customer exempted from sales tax?')
+		"Sales Invoice": [
+			dict(
+				fieldname="exempt_from_sales_tax",
+				fieldtype="Check",
+				insert_after="taxes_section",
+				label="Is customer exempted from sales tax?",
+			)
 		],
-		'Customer': [
-			dict(fieldname='exempt_from_sales_tax', fieldtype='Check', insert_after='represents_company',
-				label='Is customer exempted from sales tax?')
+		"Customer": [
+			dict(
+				fieldname="exempt_from_sales_tax",
+				fieldtype="Check",
+				insert_after="represents_company",
+				label="Is customer exempted from sales tax?",
+			)
 		],
-		'Quotation': [
-			dict(fieldname='exempt_from_sales_tax', fieldtype='Check', insert_after='taxes_and_charges',
-				label='Is customer exempted from sales tax?')
-		]
+		"Quotation": [
+			dict(
+				fieldname="exempt_from_sales_tax",
+				fieldtype="Check",
+				insert_after="taxes_and_charges",
+				label="Is customer exempted from sales tax?",
+			)
+		],
 	}
 	create_custom_fields(custom_fields, update=update)
 
+
 def add_print_formats():
 	frappe.reload_doc("regional", "print_format", "irs_1099_form")
 	frappe.db.set_value("Print Format", "IRS 1099 Form", "disabled", 0)
diff --git a/erpnext/regional/united_states/test_united_states.py b/erpnext/regional/united_states/test_united_states.py
index 652b483..83ba6ed 100644
--- a/erpnext/regional/united_states/test_united_states.py
+++ b/erpnext/regional/united_states/test_united_states.py
@@ -9,49 +9,51 @@
 
 
 class TestUnitedStates(unittest.TestCase):
-    def test_irs_1099_custom_field(self):
+	def test_irs_1099_custom_field(self):
 
-        if not frappe.db.exists("Supplier", "_US 1099 Test Supplier"):
-            doc = frappe.new_doc("Supplier")
-            doc.supplier_name = "_US 1099 Test Supplier"
-            doc.supplier_group = "Services"
-            doc.supplier_type = "Company"
-            doc.country = "United States"
-            doc.tax_id = "04-1234567"
-            doc.irs_1099 = 1
-            doc.save()
-            frappe.db.commit()
-            supplier = frappe.get_doc('Supplier', "_US 1099 Test Supplier")
-            self.assertEqual(supplier.irs_1099, 1)
+		if not frappe.db.exists("Supplier", "_US 1099 Test Supplier"):
+			doc = frappe.new_doc("Supplier")
+			doc.supplier_name = "_US 1099 Test Supplier"
+			doc.supplier_group = "Services"
+			doc.supplier_type = "Company"
+			doc.country = "United States"
+			doc.tax_id = "04-1234567"
+			doc.irs_1099 = 1
+			doc.save()
+			frappe.db.commit()
+			supplier = frappe.get_doc("Supplier", "_US 1099 Test Supplier")
+			self.assertEqual(supplier.irs_1099, 1)
 
-    def test_irs_1099_report(self):
-        make_payment_entry_to_irs_1099_supplier()
-        filters = frappe._dict({"fiscal_year": "_Test Fiscal Year 2016", "company": "_Test Company 1"})
-        columns, data = execute_1099_report(filters)
-        expected_row = {'supplier': '_US 1099 Test Supplier',
-                        'supplier_group': 'Services',
-                        'payments': 100.0,
-                        'tax_id': '04-1234567'}
-        self.assertEqual(data[0], expected_row)
+	def test_irs_1099_report(self):
+		make_payment_entry_to_irs_1099_supplier()
+		filters = frappe._dict({"fiscal_year": "_Test Fiscal Year 2016", "company": "_Test Company 1"})
+		columns, data = execute_1099_report(filters)
+		expected_row = {
+			"supplier": "_US 1099 Test Supplier",
+			"supplier_group": "Services",
+			"payments": 100.0,
+			"tax_id": "04-1234567",
+		}
+		self.assertEqual(data[0], expected_row)
 
 
 def make_payment_entry_to_irs_1099_supplier():
 
-    frappe.db.sql("delete from `tabGL Entry` where party='_US 1099 Test Supplier'")
-    frappe.db.sql("delete from `tabGL Entry` where against='_US 1099 Test Supplier'")
-    frappe.db.sql("delete from `tabPayment Entry` where party='_US 1099 Test Supplier'")
+	frappe.db.sql("delete from `tabGL Entry` where party='_US 1099 Test Supplier'")
+	frappe.db.sql("delete from `tabGL Entry` where against='_US 1099 Test Supplier'")
+	frappe.db.sql("delete from `tabPayment Entry` where party='_US 1099 Test Supplier'")
 
-    pe = frappe.new_doc("Payment Entry")
-    pe.payment_type = "Pay"
-    pe.company = "_Test Company 1"
-    pe.posting_date = "2016-01-10"
-    pe.paid_from = "_Test Bank USD - _TC1"
-    pe.paid_to = "_Test Payable USD - _TC1"
-    pe.paid_amount = 100
-    pe.received_amount = 100
-    pe.reference_no = "For IRS 1099 testing"
-    pe.reference_date = "2016-01-10"
-    pe.party_type = "Supplier"
-    pe.party = "_US 1099 Test Supplier"
-    pe.insert()
-    pe.submit()
+	pe = frappe.new_doc("Payment Entry")
+	pe.payment_type = "Pay"
+	pe.company = "_Test Company 1"
+	pe.posting_date = "2016-01-10"
+	pe.paid_from = "_Test Bank USD - _TC1"
+	pe.paid_to = "_Test Payable USD - _TC1"
+	pe.paid_amount = 100
+	pe.received_amount = 100
+	pe.reference_no = "For IRS 1099 testing"
+	pe.reference_date = "2016-01-10"
+	pe.party_type = "Supplier"
+	pe.party = "_US 1099 Test Supplier"
+	pe.insert()
+	pe.submit()
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index ae40630..7cba141 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -15,23 +15,23 @@
   "salutation",
   "customer_name",
   "gender",
-  "customer_type",
-  "tax_withholding_category",
   "default_bank_account",
+  "tax_id",
+  "tax_category",
+  "tax_withholding_category",
   "lead_name",
   "opportunity_name",
   "image",
   "column_break0",
-  "account_manager",
   "customer_group",
+  "customer_type",
   "territory",
-  "tax_id",
-  "tax_category",
+  "account_manager",
   "so_required",
   "dn_required",
-  "disabled",
   "is_internal_customer",
   "represents_company",
+  "disabled",
   "allowed_to_transact_section",
   "companies",
   "currency_and_price_list",
@@ -40,7 +40,6 @@
   "default_price_list",
   "address_contacts",
   "address_html",
-  "website",
   "column_break1",
   "contact_html",
   "primary_address_and_contact_detail",
@@ -60,6 +59,7 @@
   "column_break_45",
   "market_segment",
   "industry",
+  "website",
   "language",
   "is_frozen",
   "column_break_38",
@@ -100,7 +100,7 @@
    "fieldname": "customer_name",
    "fieldtype": "Data",
    "in_global_search": 1,
-   "label": "Full Name",
+   "label": "Customer Name",
    "no_copy": 1,
    "oldfieldname": "customer_name",
    "oldfieldtype": "Data",
@@ -118,7 +118,7 @@
    "default": "Company",
    "fieldname": "customer_type",
    "fieldtype": "Select",
-   "label": "Type",
+   "label": "Customer Type",
    "oldfieldname": "customer_type",
    "oldfieldtype": "Select",
    "options": "Company\nIndividual",
@@ -337,7 +337,7 @@
    "collapsible": 1,
    "fieldname": "default_receivable_accounts",
    "fieldtype": "Section Break",
-   "label": "Accounting"
+   "label": "Default Receivable Accounts"
   },
   {
    "description": "Mention if non-standard receivable account",
@@ -511,7 +511,7 @@
    "link_fieldname": "party"
   }
  ],
- "modified": "2021-10-20 22:07:52.485809",
+ "modified": "2022-04-16 20:32:34.000304",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Customer",
@@ -595,6 +595,7 @@
  "show_name_in_global_search": 1,
  "sort_field": "modified",
  "sort_order": "ASC",
+ "states": [],
  "title_field": "customer_name",
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 7742f26..35e0b0d 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -37,13 +37,13 @@
 
 	def load_dashboard_info(self):
 		info = get_dashboard_info(self.doctype, self.name, self.loyalty_program)
-		self.set_onload('dashboard_info', info)
+		self.set_onload("dashboard_info", info)
 
 	def autoname(self):
-		cust_master_name = frappe.defaults.get_global_default('cust_master_name')
-		if cust_master_name == 'Customer Name':
+		cust_master_name = frappe.defaults.get_global_default("cust_master_name")
+		if cust_master_name == "Customer Name":
 			self.name = self.get_customer_name()
-		elif cust_master_name == 'Naming Series':
+		elif cust_master_name == "Naming Series":
 			set_name_by_naming_series(self)
 		else:
 			self.name = set_name_from_naming_options(frappe.get_meta(self.doctype).autoname, self)
@@ -51,22 +51,30 @@
 	def get_customer_name(self):
 
 		if frappe.db.get_value("Customer", self.customer_name) and not frappe.flags.in_import:
-			count = frappe.db.sql("""select ifnull(MAX(CAST(SUBSTRING_INDEX(name, ' ', -1) AS UNSIGNED)), 0) from tabCustomer
-				 where name like %s""", "%{0} - %".format(self.customer_name), as_list=1)[0][0]
+			count = frappe.db.sql(
+				"""select ifnull(MAX(CAST(SUBSTRING_INDEX(name, ' ', -1) AS UNSIGNED)), 0) from tabCustomer
+				 where name like %s""",
+				"%{0} - %".format(self.customer_name),
+				as_list=1,
+			)[0][0]
 			count = cint(count) + 1
 
 			new_customer_name = "{0} - {1}".format(self.customer_name, cstr(count))
 
-			msgprint(_("Changed customer name to '{}' as '{}' already exists.")
-					.format(new_customer_name, self.customer_name),
-					title=_("Note"), indicator="yellow")
+			msgprint(
+				_("Changed customer name to '{}' as '{}' already exists.").format(
+					new_customer_name, self.customer_name
+				),
+				title=_("Note"),
+				indicator="yellow",
+			)
 
 			return new_customer_name
 
 		return self.customer_name
 
 	def after_insert(self):
-		'''If customer created from Lead, update customer id in quotations, opportunities'''
+		"""If customer created from Lead, update customer id in quotations, opportunities"""
 		self.update_lead_status()
 
 	def validate(self):
@@ -80,8 +88,8 @@
 		self.validate_internal_customer()
 
 		# set loyalty program tier
-		if frappe.db.exists('Customer', self.name):
-			customer = frappe.get_doc('Customer', self.name)
+		if frappe.db.exists("Customer", self.name):
+			customer = frappe.get_doc("Customer", self.name)
 			if self.loyalty_program == customer.loyalty_program and not self.loyalty_program_tier:
 				self.loyalty_program_tier = customer.loyalty_program_tier
 
@@ -91,8 +99,9 @@
 
 	@frappe.whitelist()
 	def get_customer_group_details(self):
-		doc = frappe.get_doc('Customer Group', self.customer_group)
-		self.accounts = self.credit_limits = []
+		doc = frappe.get_doc("Customer Group", self.customer_group)
+		self.accounts = []
+		self.credit_limits = []
 		self.payment_terms = self.default_price_list = ""
 
 		tables = [["accounts", "account"], ["credit_limits", "credit_limit"]]
@@ -100,14 +109,16 @@
 
 		for row in tables:
 			table, field = row[0], row[1]
-			if not doc.get(table): continue
+			if not doc.get(table):
+				continue
 
 			for entry in doc.get(table):
 				child = self.append(table)
 				child.update({"company": entry.company, field: entry.get(field)})
 
 		for field in fields:
-			if not doc.get(field): continue
+			if not doc.get(field):
+				continue
 			self.update({field: doc.get(field)})
 
 		self.save()
@@ -115,23 +126,40 @@
 	def check_customer_group_change(self):
 		frappe.flags.customer_group_changed = False
 
-		if not self.get('__islocal'):
-			if self.customer_group != frappe.db.get_value('Customer', self.name, 'customer_group'):
+		if not self.get("__islocal"):
+			if self.customer_group != frappe.db.get_value("Customer", self.name, "customer_group"):
 				frappe.flags.customer_group_changed = True
 
 	def validate_default_bank_account(self):
 		if self.default_bank_account:
-			is_company_account = frappe.db.get_value('Bank Account', self.default_bank_account, 'is_company_account')
+			is_company_account = frappe.db.get_value(
+				"Bank Account", self.default_bank_account, "is_company_account"
+			)
 			if not is_company_account:
-				frappe.throw(_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account)))
+				frappe.throw(
+					_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account))
+				)
 
 	def validate_internal_customer(self):
-		internal_customer = frappe.db.get_value("Customer",
-			{"is_internal_customer": 1, "represents_company": self.represents_company, "name": ("!=", self.name)}, "name")
+		if not self.is_internal_customer:
+			self.represents_company = ""
+
+		internal_customer = frappe.db.get_value(
+			"Customer",
+			{
+				"is_internal_customer": 1,
+				"represents_company": self.represents_company,
+				"name": ("!=", self.name),
+			},
+			"name",
+		)
 
 		if internal_customer:
-			frappe.throw(_("Internal Customer for company {0} already exists").format(
-				frappe.bold(self.represents_company)))
+			frappe.throw(
+				_("Internal Customer for company {0} already exists").format(
+					frappe.bold(self.represents_company)
+				)
+			)
 
 	def on_update(self):
 		self.validate_name_with_customer_group()
@@ -149,21 +177,22 @@
 	def update_customer_groups(self):
 		ignore_doctypes = ["Lead", "Opportunity", "POS Profile", "Tax Rule", "Pricing Rule"]
 		if frappe.flags.customer_group_changed:
-			update_linked_doctypes('Customer', self.name, 'Customer Group',
-				self.customer_group, ignore_doctypes)
+			update_linked_doctypes(
+				"Customer", self.name, "Customer Group", self.customer_group, ignore_doctypes
+			)
 
 	def create_primary_contact(self):
 		if not self.customer_primary_contact and not self.lead_name:
 			if self.mobile_no or self.email_id:
 				contact = make_contact(self)
-				self.db_set('customer_primary_contact', contact.name)
-				self.db_set('mobile_no', self.mobile_no)
-				self.db_set('email_id', self.email_id)
+				self.db_set("customer_primary_contact", contact.name)
+				self.db_set("mobile_no", self.mobile_no)
+				self.db_set("email_id", self.email_id)
 
 	def create_primary_address(self):
 		from frappe.contacts.doctype.address.address import get_address_display
 
-		if self.flags.is_new_doc and self.get('address_line1'):
+		if self.flags.is_new_doc and self.get("address_line1"):
 			address = make_address(self)
 			address_display = get_address_display(address.name)
 
@@ -171,8 +200,8 @@
 			self.db_set("primary_address", address_display)
 
 	def update_lead_status(self):
-		'''If Customer created from Lead, update lead status to "Converted"
-		update Customer link in Quotation, Opportunity'''
+		"""If Customer created from Lead, update lead status to "Converted"
+		update Customer link in Quotation, Opportunity"""
 		if self.lead_name:
 			frappe.db.set_value("Lead", self.lead_name, "status", "Converted")
 
@@ -191,22 +220,36 @@
 
 			for row in linked_contacts_and_addresses:
 				linked_doc = frappe.get_doc(row.doctype, row.name)
-				if not linked_doc.has_link('Customer', self.name):
-					linked_doc.append('links', dict(link_doctype='Customer', link_name=self.name))
+				if not linked_doc.has_link("Customer", self.name):
+					linked_doc.append("links", dict(link_doctype="Customer", link_name=self.name))
 					linked_doc.save(ignore_permissions=self.flags.ignore_permissions)
 
 	def validate_name_with_customer_group(self):
 		if frappe.db.exists("Customer Group", self.name):
-			frappe.throw(_("A Customer Group exists with same name please change the Customer name or rename the Customer Group"), frappe.NameError)
+			frappe.throw(
+				_(
+					"A Customer Group exists with same name please change the Customer name or rename the Customer Group"
+				),
+				frappe.NameError,
+			)
 
 	def validate_credit_limit_on_change(self):
 		if self.get("__islocal") or not self.credit_limits:
 			return
 
-		past_credit_limits = [d.credit_limit
-			for d in frappe.db.get_all("Customer Credit Limit", filters={'parent': self.name}, fields=["credit_limit"], order_by="company")]
+		past_credit_limits = [
+			d.credit_limit
+			for d in frappe.db.get_all(
+				"Customer Credit Limit",
+				filters={"parent": self.name},
+				fields=["credit_limit"],
+				order_by="company",
+			)
+		]
 
-		current_credit_limits = [d.credit_limit for d in sorted(self.credit_limits, key=lambda k: k.company)]
+		current_credit_limits = [
+			d.credit_limit for d in sorted(self.credit_limits, key=lambda k: k.company)
+		]
 
 		if past_credit_limits == current_credit_limits:
 			return
@@ -214,17 +257,26 @@
 		company_record = []
 		for limit in self.credit_limits:
 			if limit.company in company_record:
-				frappe.throw(_("Credit limit is already defined for the Company {0}").format(limit.company, self.name))
+				frappe.throw(
+					_("Credit limit is already defined for the Company {0}").format(limit.company, self.name)
+				)
 			else:
 				company_record.append(limit.company)
 
-			outstanding_amt = get_customer_outstanding(self.name, limit.company)
+			outstanding_amt = get_customer_outstanding(
+				self.name, limit.company, ignore_outstanding_sales_order=limit.bypass_credit_limit_check
+			)
 			if flt(limit.credit_limit) < outstanding_amt:
-				frappe.throw(_("""New credit limit is less than current outstanding amount for the customer. Credit limit has to be atleast {0}""").format(outstanding_amt))
+				frappe.throw(
+					_(
+						"""New credit limit is less than current outstanding amount for the customer. Credit limit has to be atleast {0}"""
+					).format(outstanding_amt)
+				)
 
 	def on_trash(self):
 		if self.customer_primary_contact:
-			frappe.db.sql("""
+			frappe.db.sql(
+				"""
 				UPDATE `tabCustomer`
 				SET
 					customer_primary_contact=null,
@@ -232,14 +284,16 @@
 					mobile_no=null,
 					email_id=null,
 					primary_address=null
-				WHERE name=%(name)s""", {"name": self.name})
+				WHERE name=%(name)s""",
+				{"name": self.name},
+			)
 
-		delete_contact_and_address('Customer', self.name)
+		delete_contact_and_address("Customer", self.name)
 		if self.lead_name:
 			frappe.db.sql("update `tabLead` set status='Interested' where name=%s", self.lead_name)
 
 	def after_rename(self, olddn, newdn, merge=False):
-		if frappe.defaults.get_global_default('cust_master_name') == 'Customer Name':
+		if frappe.defaults.get_global_default("cust_master_name") == "Customer Name":
 			frappe.db.set(self, "customer_name", newdn)
 
 	def set_loyalty_program(self):
@@ -254,43 +308,49 @@
 			self.loyalty_program = loyalty_program[0]
 		else:
 			frappe.msgprint(
-				_("Multiple Loyalty Programs found for Customer {}. Please select manually.")
-				.format(frappe.bold(self.customer_name))
+				_("Multiple Loyalty Programs found for Customer {}. Please select manually.").format(
+					frappe.bold(self.customer_name)
+				)
 			)
 
+
 def create_contact(contact, party_type, party, email):
 	"""Create contact based on given contact name"""
-	contact = contact.split(' ')
+	contact = contact.split(" ")
 
-	contact = frappe.get_doc({
-		'doctype': 'Contact',
-		'first_name': contact[0],
-		'last_name': len(contact) > 1 and contact[1] or ""
-	})
-	contact.append('email_ids', dict(email_id=email, is_primary=1))
-	contact.append('links', dict(link_doctype=party_type, link_name=party))
+	contact = frappe.get_doc(
+		{
+			"doctype": "Contact",
+			"first_name": contact[0],
+			"last_name": len(contact) > 1 and contact[1] or "",
+		}
+	)
+	contact.append("email_ids", dict(email_id=email, is_primary=1))
+	contact.append("links", dict(link_doctype=party_type, link_name=party))
 	contact.insert()
 
+
 @frappe.whitelist()
 def make_quotation(source_name, target_doc=None):
-
 	def set_missing_values(source, target):
 		_set_missing_values(source, target)
 
-	target_doc = get_mapped_doc("Customer", source_name,
-		{"Customer": {
-			"doctype": "Quotation",
-			"field_map": {
-				"name":"party_name"
-			}
-		}}, target_doc, set_missing_values)
+	target_doc = get_mapped_doc(
+		"Customer",
+		source_name,
+		{"Customer": {"doctype": "Quotation", "field_map": {"name": "party_name"}}},
+		target_doc,
+		set_missing_values,
+	)
 
 	target_doc.quotation_to = "Customer"
 	target_doc.run_method("set_missing_values")
 	target_doc.run_method("set_other_charges")
 	target_doc.run_method("calculate_taxes_and_totals")
 
-	price_list, currency = frappe.db.get_value("Customer", {'name': source_name}, ['default_price_list', 'default_currency'])
+	price_list, currency = frappe.db.get_value(
+		"Customer", {"name": source_name}, ["default_price_list", "default_currency"]
+	)
 	if price_list:
 		target_doc.selling_price_list = price_list
 	if currency:
@@ -298,34 +358,53 @@
 
 	return target_doc
 
+
 @frappe.whitelist()
 def make_opportunity(source_name, target_doc=None):
 	def set_missing_values(source, target):
 		_set_missing_values(source, target)
 
-	target_doc = get_mapped_doc("Customer", source_name,
-		{"Customer": {
-			"doctype": "Opportunity",
-			"field_map": {
-				"name": "party_name",
-				"doctype": "opportunity_from",
+	target_doc = get_mapped_doc(
+		"Customer",
+		source_name,
+		{
+			"Customer": {
+				"doctype": "Opportunity",
+				"field_map": {
+					"name": "party_name",
+					"doctype": "opportunity_from",
+				},
 			}
-		}}, target_doc, set_missing_values)
+		},
+		target_doc,
+		set_missing_values,
+	)
 
 	return target_doc
 
-def _set_missing_values(source, target):
-	address = frappe.get_all('Dynamic Link', {
-			'link_doctype': source.doctype,
-			'link_name': source.name,
-			'parenttype': 'Address',
-		}, ['parent'], limit=1)
 
-	contact = frappe.get_all('Dynamic Link', {
-			'link_doctype': source.doctype,
-			'link_name': source.name,
-			'parenttype': 'Contact',
-		}, ['parent'], limit=1)
+def _set_missing_values(source, target):
+	address = frappe.get_all(
+		"Dynamic Link",
+		{
+			"link_doctype": source.doctype,
+			"link_name": source.name,
+			"parenttype": "Address",
+		},
+		["parent"],
+		limit=1,
+	)
+
+	contact = frappe.get_all(
+		"Dynamic Link",
+		{
+			"link_doctype": source.doctype,
+			"link_name": source.name,
+			"parenttype": "Contact",
+		},
+		["parent"],
+		limit=1,
+	)
 
 	if address:
 		target.customer_address = address[0].parent
@@ -333,35 +412,41 @@
 	if contact:
 		target.contact_person = contact[0].parent
 
+
 @frappe.whitelist()
 def get_loyalty_programs(doc):
-	''' returns applicable loyalty programs for a customer '''
+	"""returns applicable loyalty programs for a customer"""
 
 	lp_details = []
-	loyalty_programs = frappe.get_all("Loyalty Program",
+	loyalty_programs = frappe.get_all(
+		"Loyalty Program",
 		fields=["name", "customer_group", "customer_territory"],
-		filters={"auto_opt_in": 1, "from_date": ["<=", today()],
-			"ifnull(to_date, '2500-01-01')": [">=", today()]})
+		filters={
+			"auto_opt_in": 1,
+			"from_date": ["<=", today()],
+			"ifnull(to_date, '2500-01-01')": [">=", today()],
+		},
+	)
 
 	for loyalty_program in loyalty_programs:
 		if (
-			(not loyalty_program.customer_group
-			or doc.customer_group in get_nested_links(
-				"Customer Group",
-				loyalty_program.customer_group,
-				doc.flags.ignore_permissions
-			))
-			and (not loyalty_program.customer_territory
-			or doc.territory in get_nested_links(
-				"Territory",
-				loyalty_program.customer_territory,
-				doc.flags.ignore_permissions
-			))
+			not loyalty_program.customer_group
+			or doc.customer_group
+			in get_nested_links(
+				"Customer Group", loyalty_program.customer_group, doc.flags.ignore_permissions
+			)
+		) and (
+			not loyalty_program.customer_territory
+			or doc.territory
+			in get_nested_links(
+				"Territory", loyalty_program.customer_territory, doc.flags.ignore_permissions
+			)
 		):
 			lp_details.append(loyalty_program.name)
 
 	return lp_details
 
+
 def get_nested_links(link_doctype, link_name, ignore_permissions=False):
 	from frappe.desk.treeview import _get_children
 
@@ -371,10 +456,12 @@
 
 	return links
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_customer_list(doctype, txt, searchfield, start, page_len, filters=None):
 	from erpnext.controllers.queries import get_fields
+
 	fields = ["name", "customer_name", "customer_group", "territory"]
 
 	if frappe.db.get_default("cust_master_name") == "Customer Name":
@@ -389,7 +476,8 @@
 		filter_conditions = get_filters_cond(doctype, filters, [])
 		match_conditions += "{}".format(filter_conditions)
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select %s
 		from `tabCustomer`
 		where docstatus < 2
@@ -399,8 +487,12 @@
 			case when name like %s then 0 else 1 end,
 			case when customer_name like %s then 0 else 1 end,
 			name, customer_name limit %s, %s
-		""".format(match_conditions=match_conditions) % (", ".join(fields), searchfield, "%s", "%s", "%s", "%s", "%s", "%s"),
-		("%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, start, page_len))
+		""".format(
+			match_conditions=match_conditions
+		)
+		% (", ".join(fields), searchfield, "%s", "%s", "%s", "%s", "%s", "%s"),
+		("%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, start, page_len),
+	)
 
 
 def check_credit_limit(customer, company, ignore_outstanding_sales_order=False, extra_amount=0):
@@ -413,63 +505,87 @@
 		customer_outstanding += flt(extra_amount)
 
 	if credit_limit > 0 and flt(customer_outstanding) > credit_limit:
-		msgprint(_("Credit limit has been crossed for customer {0} ({1}/{2})")
-			.format(customer, customer_outstanding, credit_limit))
+		msgprint(
+			_("Credit limit has been crossed for customer {0} ({1}/{2})").format(
+				customer, customer_outstanding, credit_limit
+			)
+		)
 
 		# If not authorized person raise exception
-		credit_controller_role = frappe.db.get_single_value('Accounts Settings', 'credit_controller')
+		credit_controller_role = frappe.db.get_single_value("Accounts Settings", "credit_controller")
 		if not credit_controller_role or credit_controller_role not in frappe.get_roles():
 			# form a list of emails for the credit controller users
 			credit_controller_users = get_users_with_role(credit_controller_role or "Sales Master Manager")
 
 			# form a list of emails and names to show to the user
-			credit_controller_users_formatted = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users]
+			credit_controller_users_formatted = [
+				get_formatted_email(user).replace("<", "(").replace(">", ")")
+				for user in credit_controller_users
+			]
 			if not credit_controller_users_formatted:
-				frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.").format(customer))
+				frappe.throw(
+					_("Please contact your administrator to extend the credit limits for {0}.").format(customer)
+				)
 
 			message = """Please contact any of the following users to extend the credit limits for {0}:
-				<br><br><ul><li>{1}</li></ul>""".format(customer, '<li>'.join(credit_controller_users_formatted))
+				<br><br><ul><li>{1}</li></ul>""".format(
+				customer, "<li>".join(credit_controller_users_formatted)
+			)
 
 			# if the current user does not have permissions to override credit limit,
 			# prompt them to send out an email to the controller users
-			frappe.msgprint(message,
+			frappe.msgprint(
+				message,
 				title="Notify",
 				raise_exception=1,
 				primary_action={
-					'label': 'Send Email',
-					'server_action': 'erpnext.selling.doctype.customer.customer.send_emails',
-					'args': {
-						'customer': customer,
-						'customer_outstanding': customer_outstanding,
-						'credit_limit': credit_limit,
-						'credit_controller_users_list': credit_controller_users
-					}
-				}
+					"label": "Send Email",
+					"server_action": "erpnext.selling.doctype.customer.customer.send_emails",
+					"args": {
+						"customer": customer,
+						"customer_outstanding": customer_outstanding,
+						"credit_limit": credit_limit,
+						"credit_controller_users_list": credit_controller_users,
+					},
+				},
 			)
 
+
 @frappe.whitelist()
 def send_emails(args):
 	args = json.loads(args)
-	subject = (_("Credit limit reached for customer {0}").format(args.get('customer')))
-	message = (_("Credit limit has been crossed for customer {0} ({1}/{2})")
-			.format(args.get('customer'), args.get('customer_outstanding'), args.get('credit_limit')))
-	frappe.sendmail(recipients=args.get('credit_controller_users_list'), subject=subject, message=message)
+	subject = _("Credit limit reached for customer {0}").format(args.get("customer"))
+	message = _("Credit limit has been crossed for customer {0} ({1}/{2})").format(
+		args.get("customer"), args.get("customer_outstanding"), args.get("credit_limit")
+	)
+	frappe.sendmail(
+		recipients=args.get("credit_controller_users_list"), subject=subject, message=message
+	)
 
-def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=False, cost_center=None):
+
+def get_customer_outstanding(
+	customer, company, ignore_outstanding_sales_order=False, cost_center=None
+):
 	# Outstanding based on GL Entries
 
 	cond = ""
 	if cost_center:
-		lft, rgt = frappe.get_cached_value("Cost Center",
-			cost_center, ['lft', 'rgt'])
+		lft, rgt = frappe.get_cached_value("Cost Center", cost_center, ["lft", "rgt"])
 
 		cond = """ and cost_center in (select name from `tabCost Center` where
-			lft >= {0} and rgt <= {1})""".format(lft, rgt)
+			lft >= {0} and rgt <= {1})""".format(
+			lft, rgt
+		)
 
-	outstanding_based_on_gle = frappe.db.sql("""
+	outstanding_based_on_gle = frappe.db.sql(
+		"""
 		select sum(debit) - sum(credit)
 		from `tabGL Entry` where party_type = 'Customer'
-		and party = %s and company=%s {0}""".format(cond), (customer, company))
+		and party = %s and company=%s {0}""".format(
+			cond
+		),
+		(customer, company),
+	)
 
 	outstanding_based_on_gle = flt(outstanding_based_on_gle[0][0]) if outstanding_based_on_gle else 0
 
@@ -479,18 +595,22 @@
 	# if credit limit check is bypassed at sales order level,
 	# we should not consider outstanding Sales Orders, when customer credit balance report is run
 	if not ignore_outstanding_sales_order:
-		outstanding_based_on_so = frappe.db.sql("""
+		outstanding_based_on_so = frappe.db.sql(
+			"""
 			select sum(base_grand_total*(100 - per_billed)/100)
 			from `tabSales Order`
 			where customer=%s and docstatus = 1 and company=%s
-			and per_billed < 100 and status != 'Closed'""", (customer, company))
+			and per_billed < 100 and status != 'Closed'""",
+			(customer, company),
+		)
 
 		outstanding_based_on_so = flt(outstanding_based_on_so[0][0]) if outstanding_based_on_so else 0
 
 	# Outstanding based on Delivery Note, which are not created against Sales Order
 	outstanding_based_on_dn = 0
 
-	unmarked_delivery_note_items = frappe.db.sql("""select
+	unmarked_delivery_note_items = frappe.db.sql(
+		"""select
 			dn_item.name, dn_item.amount, dn.base_net_total, dn.base_grand_total
 		from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item
 		where
@@ -499,21 +619,24 @@
 			and dn.docstatus = 1 and dn.status not in ('Closed', 'Stopped')
 			and ifnull(dn_item.against_sales_order, '') = ''
 			and ifnull(dn_item.against_sales_invoice, '') = ''
-		""", (customer, company), as_dict=True)
+		""",
+		(customer, company),
+		as_dict=True,
+	)
 
 	if not unmarked_delivery_note_items:
 		return outstanding_based_on_gle + outstanding_based_on_so
 
-	si_amounts = frappe.db.sql("""
+	si_amounts = frappe.db.sql(
+		"""
 		SELECT
 			dn_detail, sum(amount) from `tabSales Invoice Item`
 		WHERE
 			docstatus = 1
 			and dn_detail in ({})
-		GROUP BY dn_detail""".format(", ".join(
-			frappe.db.escape(dn_item.name)
-			for dn_item in unmarked_delivery_note_items
-		))
+		GROUP BY dn_detail""".format(
+			", ".join(frappe.db.escape(dn_item.name) for dn_item in unmarked_delivery_note_items)
+		)
 	)
 
 	si_amounts = {si_item[0]: si_item[1] for si_item in si_amounts}
@@ -523,8 +646,9 @@
 		si_amount = flt(si_amounts.get(dn_item.name))
 
 		if dn_amount > si_amount and dn_item.base_net_total:
-			outstanding_based_on_dn += ((dn_amount - si_amount)
-				/ dn_item.base_net_total) * dn_item.base_grand_total
+			outstanding_based_on_dn += (
+				(dn_amount - si_amount) / dn_item.base_net_total
+			) * dn_item.base_grand_total
 
 	return outstanding_based_on_gle + outstanding_based_on_so + outstanding_based_on_dn
 
@@ -533,75 +657,84 @@
 	credit_limit = None
 
 	if customer:
-		credit_limit = frappe.db.get_value("Customer Credit Limit",
-			{'parent': customer, 'parenttype': 'Customer', 'company': company}, 'credit_limit')
+		credit_limit = frappe.db.get_value(
+			"Customer Credit Limit",
+			{"parent": customer, "parenttype": "Customer", "company": company},
+			"credit_limit",
+		)
 
 		if not credit_limit:
-			customer_group = frappe.get_cached_value("Customer", customer, 'customer_group')
-			credit_limit = frappe.db.get_value("Customer Credit Limit",
-				{'parent': customer_group, 'parenttype': 'Customer Group', 'company': company}, 'credit_limit')
+			customer_group = frappe.get_cached_value("Customer", customer, "customer_group")
+			credit_limit = frappe.db.get_value(
+				"Customer Credit Limit",
+				{"parent": customer_group, "parenttype": "Customer Group", "company": company},
+				"credit_limit",
+			)
 
 	if not credit_limit:
-		credit_limit = frappe.get_cached_value('Company',  company,  "credit_limit")
+		credit_limit = frappe.get_cached_value("Company", company, "credit_limit")
 
 	return flt(credit_limit)
 
+
 def make_contact(args, is_primary_contact=1):
-	contact = frappe.get_doc({
-		'doctype': 'Contact',
-		'first_name': args.get('name'),
-		'is_primary_contact': is_primary_contact,
-		'links': [{
-			'link_doctype': args.get('doctype'),
-			'link_name': args.get('name')
-		}]
-	})
-	if args.get('email_id'):
-		contact.add_email(args.get('email_id'), is_primary=True)
-	if args.get('mobile_no'):
-		contact.add_phone(args.get('mobile_no'), is_primary_mobile_no=True)
+	contact = frappe.get_doc(
+		{
+			"doctype": "Contact",
+			"first_name": args.get("name"),
+			"is_primary_contact": is_primary_contact,
+			"links": [{"link_doctype": args.get("doctype"), "link_name": args.get("name")}],
+		}
+	)
+	if args.get("email_id"):
+		contact.add_email(args.get("email_id"), is_primary=True)
+	if args.get("mobile_no"):
+		contact.add_phone(args.get("mobile_no"), is_primary_mobile_no=True)
 	contact.insert()
 
 	return contact
 
+
 def make_address(args, is_primary_address=1):
 	reqd_fields = []
-	for field in ['city', 'country']:
+	for field in ["city", "country"]:
 		if not args.get(field):
-			reqd_fields.append( '<li>' + field.title() + '</li>')
+			reqd_fields.append("<li>" + field.title() + "</li>")
 
 	if reqd_fields:
 		msg = _("Following fields are mandatory to create address:")
-		frappe.throw("{0} <br><br> <ul>{1}</ul>".format(msg, '\n'.join(reqd_fields)),
-			title = _("Missing Values Required"))
+		frappe.throw(
+			"{0} <br><br> <ul>{1}</ul>".format(msg, "\n".join(reqd_fields)),
+			title=_("Missing Values Required"),
+		)
 
-	address = frappe.get_doc({
-		'doctype': 'Address',
-		'address_title': args.get('name'),
-		'address_line1': args.get('address_line1'),
-		'address_line2': args.get('address_line2'),
-		'city': args.get('city'),
-		'state': args.get('state'),
-		'pincode': args.get('pincode'),
-		'country': args.get('country'),
-		'links': [{
-			'link_doctype': args.get('doctype'),
-			'link_name': args.get('name')
-		}]
-	}).insert()
+	address = frappe.get_doc(
+		{
+			"doctype": "Address",
+			"address_title": args.get("name"),
+			"address_line1": args.get("address_line1"),
+			"address_line2": args.get("address_line2"),
+			"city": args.get("city"),
+			"state": args.get("state"),
+			"pincode": args.get("pincode"),
+			"country": args.get("country"),
+			"links": [{"link_doctype": args.get("doctype"), "link_name": args.get("name")}],
+		}
+	).insert()
 
 	return address
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_customer_primary_contact(doctype, txt, searchfield, start, page_len, filters):
-	customer = filters.get('customer')
-	return frappe.db.sql("""
+	customer = filters.get("customer")
+	return frappe.db.sql(
+		"""
 		select `tabContact`.name from `tabContact`, `tabDynamic Link`
 			where `tabContact`.name = `tabDynamic Link`.parent and `tabDynamic Link`.link_name = %(customer)s
 			and `tabDynamic Link`.link_doctype = 'Customer'
 			and `tabContact`.name like %(txt)s
-		""", {
-			'customer': customer,
-			'txt': '%%%s%%' % txt
-		})
+		""",
+		{"customer": customer, "txt": "%%%s%%" % txt},
+	)
diff --git a/erpnext/selling/doctype/customer/customer_dashboard.py b/erpnext/selling/doctype/customer/customer_dashboard.py
index 58394d0..1b22963 100644
--- a/erpnext/selling/doctype/customer/customer_dashboard.py
+++ b/erpnext/selling/doctype/customer/customer_dashboard.py
@@ -3,47 +3,29 @@
 
 def get_data():
 	return {
-		'heatmap': True,
-		'heatmap_message': _('This is based on transactions against this Customer. See timeline below for details'),
-		'fieldname': 'customer',
-		'non_standard_fieldnames': {
-			'Payment Entry': 'party',
-			'Quotation': 'party_name',
-			'Opportunity': 'party_name',
-			'Bank Account': 'party',
-			'Subscription': 'party'
+		"heatmap": True,
+		"heatmap_message": _(
+			"This is based on transactions against this Customer. See timeline below for details"
+		),
+		"fieldname": "customer",
+		"non_standard_fieldnames": {
+			"Payment Entry": "party",
+			"Quotation": "party_name",
+			"Opportunity": "party_name",
+			"Bank Account": "party",
+			"Subscription": "party",
 		},
-		'dynamic_links': {
-			'party_name': ['Customer', 'quotation_to']
-		},
-		'transactions': [
+		"dynamic_links": {"party_name": ["Customer", "quotation_to"]},
+		"transactions": [
+			{"label": _("Pre Sales"), "items": ["Opportunity", "Quotation"]},
+			{"label": _("Orders"), "items": ["Sales Order", "Delivery Note", "Sales Invoice"]},
+			{"label": _("Payments"), "items": ["Payment Entry", "Bank Account"]},
 			{
-				'label': _('Pre Sales'),
-				'items': ['Opportunity', 'Quotation']
+				"label": _("Support"),
+				"items": ["Issue", "Maintenance Visit", "Installation Note", "Warranty Claim"],
 			},
-			{
-				'label': _('Orders'),
-				'items': ['Sales Order', 'Delivery Note', 'Sales Invoice']
-			},
-			{
-				'label': _('Payments'),
-				'items': ['Payment Entry', 'Bank Account']
-			},
-			{
-				'label': _('Support'),
-				'items': ['Issue', 'Maintenance Visit', 'Installation Note', 'Warranty Claim']
-			},
-			{
-				'label': _('Projects'),
-				'items': ['Project']
-			},
-			{
-				'label': _('Pricing'),
-				'items': ['Pricing Rule']
-			},
-			{
-				'label': _('Subscriptions'),
-				'items': ['Subscription']
-			}
-		]
+			{"label": _("Projects"), "items": ["Project"]},
+			{"label": _("Pricing"), "items": ["Pricing Rule"]},
+			{"label": _("Subscriptions"), "items": ["Subscription"]},
+		],
 	}
diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py
index 165ee81..2458756 100644
--- a/erpnext/selling/doctype/customer/test_customer.py
+++ b/erpnext/selling/doctype/customer/test_customer.py
@@ -13,18 +13,17 @@
 from erpnext.tests.utils import create_test_contact_and_address
 
 test_ignore = ["Price List"]
-test_dependencies = ['Payment Term', 'Payment Terms Template']
-test_records = frappe.get_test_records('Customer')
-
+test_dependencies = ["Payment Term", "Payment Terms Template"]
+test_records = frappe.get_test_records("Customer")
 
 
 class TestCustomer(FrappeTestCase):
 	def setUp(self):
-		if not frappe.get_value('Item', '_Test Item'):
-			make_test_records('Item')
+		if not frappe.get_value("Item", "_Test Item"):
+			make_test_records("Item")
 
 	def tearDown(self):
-		set_credit_limit('_Test Customer', '_Test Company', 0)
+		set_credit_limit("_Test Customer", "_Test Company", 0)
 
 	def test_get_customer_group_details(self):
 		doc = frappe.new_doc("Customer Group")
@@ -37,10 +36,7 @@
 			"company": "_Test Company",
 			"account": "Creditors - _TC",
 		}
-		test_credit_limits = {
-			"company": "_Test Company",
-			"credit_limit": 350000
-		}
+		test_credit_limits = {"company": "_Test Company", "credit_limit": 350000}
 		doc.append("accounts", test_account_details)
 		doc.append("credit_limits", test_credit_limits)
 		doc.insert()
@@ -49,7 +45,8 @@
 		c_doc.customer_name = "Testing Customer"
 		c_doc.customer_group = "_Testing Customer Group"
 		c_doc.payment_terms = c_doc.default_price_list = ""
-		c_doc.accounts = c_doc.credit_limits= []
+		c_doc.accounts = []
+		c_doc.credit_limits = []
 		c_doc.insert()
 		c_doc.get_customer_group_details()
 		self.assertEqual(c_doc.payment_terms, "_Test Payment Term Template 3")
@@ -66,25 +63,26 @@
 		from erpnext.accounts.party import get_party_details
 
 		to_check = {
-			'selling_price_list': None,
-			'customer_group': '_Test Customer Group',
-			'contact_designation': None,
-			'customer_address': '_Test Address for Customer-Office',
-			'contact_department': None,
-			'contact_email': 'test_contact_customer@example.com',
-			'contact_mobile': None,
-			'sales_team': [],
-			'contact_display': '_Test Contact for _Test Customer',
-			'contact_person': '_Test Contact for _Test Customer-_Test Customer',
-			'territory': u'_Test Territory',
-			'contact_phone': '+91 0000000000',
-			'customer_name': '_Test Customer'
+			"selling_price_list": None,
+			"customer_group": "_Test Customer Group",
+			"contact_designation": None,
+			"customer_address": "_Test Address for Customer-Office",
+			"contact_department": None,
+			"contact_email": "test_contact_customer@example.com",
+			"contact_mobile": None,
+			"sales_team": [],
+			"contact_display": "_Test Contact for _Test Customer",
+			"contact_person": "_Test Contact for _Test Customer-_Test Customer",
+			"territory": "_Test Territory",
+			"contact_phone": "+91 0000000000",
+			"customer_name": "_Test Customer",
 		}
 
 		create_test_contact_and_address()
 
-		frappe.db.set_value("Contact", "_Test Contact for _Test Customer-_Test Customer",
-			"is_primary_contact", 1)
+		frappe.db.set_value(
+			"Contact", "_Test Contact for _Test Customer-_Test Customer", "is_primary_contact", 1
+		)
 
 		details = get_party_details("_Test Customer")
 
@@ -105,32 +103,30 @@
 		details = get_party_details("_Test Customer With Tax Category")
 		self.assertEqual(details.tax_category, "_Test Tax Category 1")
 
-		billing_address = frappe.get_doc(dict(
-			doctype='Address',
-			address_title='_Test Address With Tax Category',
-			tax_category='_Test Tax Category 2',
-			address_type='Billing',
-			address_line1='Station Road',
-			city='_Test City',
-			country='India',
-			links=[dict(
-				link_doctype='Customer',
-				link_name='_Test Customer With Tax Category'
-			)]
-		)).insert()
-		shipping_address = frappe.get_doc(dict(
-			doctype='Address',
-			address_title='_Test Address With Tax Category',
-			tax_category='_Test Tax Category 3',
-			address_type='Shipping',
-			address_line1='Station Road',
-			city='_Test City',
-			country='India',
-			links=[dict(
-				link_doctype='Customer',
-				link_name='_Test Customer With Tax Category'
-			)]
-		)).insert()
+		billing_address = frappe.get_doc(
+			dict(
+				doctype="Address",
+				address_title="_Test Address With Tax Category",
+				tax_category="_Test Tax Category 2",
+				address_type="Billing",
+				address_line1="Station Road",
+				city="_Test City",
+				country="India",
+				links=[dict(link_doctype="Customer", link_name="_Test Customer With Tax Category")],
+			)
+		).insert()
+		shipping_address = frappe.get_doc(
+			dict(
+				doctype="Address",
+				address_title="_Test Address With Tax Category",
+				tax_category="_Test Tax Category 3",
+				address_type="Shipping",
+				address_line1="Station Road",
+				city="_Test City",
+				country="India",
+				links=[dict(link_doctype="Customer", link_name="_Test Customer With Tax Category")],
+			)
+		).insert()
 
 		settings = frappe.get_single("Accounts Settings")
 		rollback_setting = settings.determine_address_tax_category_from
@@ -158,12 +154,16 @@
 
 		new_name = "_Test Customer 1 Renamed"
 		for name in ("_Test Customer 1", new_name):
-			frappe.db.sql("""delete from `tabComment`
+			frappe.db.sql(
+				"""delete from `tabComment`
 				where reference_doctype=%s and reference_name=%s""",
-				("Customer", name))
+				("Customer", name),
+			)
 
 		# add comments
-		comment = frappe.get_doc("Customer", "_Test Customer 1").add_comment("Comment", "Test Comment for Rename")
+		comment = frappe.get_doc("Customer", "_Test Customer 1").add_comment(
+			"Comment", "Test Comment for Rename"
+		)
 
 		# rename
 		frappe.rename_doc("Customer", "_Test Customer 1", new_name)
@@ -173,11 +173,17 @@
 		self.assertFalse(frappe.db.exists("Customer", "_Test Customer 1"))
 
 		# test that comment gets linked to renamed doc
-		self.assertEqual(frappe.db.get_value("Comment", {
-			"reference_doctype": "Customer",
-			"reference_name": new_name,
-			"content": "Test Comment for Rename"
-		}), comment.name)
+		self.assertEqual(
+			frappe.db.get_value(
+				"Comment",
+				{
+					"reference_doctype": "Customer",
+					"reference_name": new_name,
+					"content": "Test Comment for Rename",
+				},
+			),
+			comment.name,
+		)
 
 		# rename back to original
 		frappe.rename_doc("Customer", new_name, "_Test Customer 1")
@@ -191,7 +197,7 @@
 
 		from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
 
-		so = make_sales_order(do_not_save= True)
+		so = make_sales_order(do_not_save=True)
 
 		self.assertRaises(PartyFrozen, so.save)
 
@@ -200,13 +206,14 @@
 		so.save()
 
 	def test_delete_customer_contact(self):
-		customer = frappe.get_doc(
-			get_customer_dict('_Test Customer for delete')).insert(ignore_permissions=True)
+		customer = frappe.get_doc(get_customer_dict("_Test Customer for delete")).insert(
+			ignore_permissions=True
+		)
 
 		customer.mobile_no = "8989889890"
 		customer.save()
 		self.assertTrue(customer.customer_primary_contact)
-		frappe.delete_doc('Customer', customer.name)
+		frappe.delete_doc("Customer", customer.name)
 
 	def test_disabled_customer(self):
 		make_test_records("Item")
@@ -227,13 +234,15 @@
 		frappe.db.sql("delete from `tabCustomer` where customer_name='_Test Customer 1'")
 
 		if not frappe.db.get_value("Customer", "_Test Customer 1"):
-			test_customer_1 = frappe.get_doc(
-				get_customer_dict('_Test Customer 1')).insert(ignore_permissions=True)
+			test_customer_1 = frappe.get_doc(get_customer_dict("_Test Customer 1")).insert(
+				ignore_permissions=True
+			)
 		else:
 			test_customer_1 = frappe.get_doc("Customer", "_Test Customer 1")
 
-		duplicate_customer = frappe.get_doc(
-			get_customer_dict('_Test Customer 1')).insert(ignore_permissions=True)
+		duplicate_customer = frappe.get_doc(get_customer_dict("_Test Customer 1")).insert(
+			ignore_permissions=True
+		)
 
 		self.assertEqual("_Test Customer 1", test_customer_1.name)
 		self.assertEqual("_Test Customer 1 - 1", duplicate_customer.name)
@@ -241,15 +250,16 @@
 
 	def get_customer_outstanding_amount(self):
 		from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
-		outstanding_amt = get_customer_outstanding('_Test Customer', '_Test Company')
+
+		outstanding_amt = get_customer_outstanding("_Test Customer", "_Test Company")
 
 		# If outstanding is negative make a transaction to get positive outstanding amount
 		if outstanding_amt > 0.0:
 			return outstanding_amt
 
-		item_qty = int((abs(outstanding_amt) + 200)/100)
+		item_qty = int((abs(outstanding_amt) + 200) / 100)
 		make_sales_order(qty=item_qty)
-		return get_customer_outstanding('_Test Customer', '_Test Company')
+		return get_customer_outstanding("_Test Customer", "_Test Company")
 
 	def test_customer_credit_limit(self):
 		from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
@@ -258,14 +268,14 @@
 		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
 
 		outstanding_amt = self.get_customer_outstanding_amount()
-		credit_limit = get_credit_limit('_Test Customer', '_Test Company')
+		credit_limit = get_credit_limit("_Test Customer", "_Test Company")
 
 		if outstanding_amt <= 0.0:
-			item_qty = int((abs(outstanding_amt) + 200)/100)
+			item_qty = int((abs(outstanding_amt) + 200) / 100)
 			make_sales_order(qty=item_qty)
 
 		if not credit_limit:
-			set_credit_limit('_Test Customer', '_Test Company', outstanding_amt - 50)
+			set_credit_limit("_Test Customer", "_Test Company", outstanding_amt - 50)
 
 		# Sales Order
 		so = make_sales_order(do_not_submit=True)
@@ -280,7 +290,7 @@
 		self.assertRaises(frappe.ValidationError, si.submit)
 
 		if credit_limit > outstanding_amt:
-			set_credit_limit('_Test Customer', '_Test Company', credit_limit)
+			set_credit_limit("_Test Customer", "_Test Company", credit_limit)
 
 		# Makes Sales invoice from Sales Order
 		so.save(ignore_permissions=True)
@@ -290,16 +300,21 @@
 
 	def test_customer_credit_limit_on_change(self):
 		outstanding_amt = self.get_customer_outstanding_amount()
-		customer = frappe.get_doc("Customer", '_Test Customer')
-		customer.append('credit_limits', {'credit_limit': flt(outstanding_amt - 100), 'company': '_Test Company'})
+		customer = frappe.get_doc("Customer", "_Test Customer")
+		customer.append(
+			"credit_limits", {"credit_limit": flt(outstanding_amt - 100), "company": "_Test Company"}
+		)
 
-		''' define new credit limit for same company '''
-		customer.append('credit_limits', {'credit_limit': flt(outstanding_amt - 100), 'company': '_Test Company'})
+		""" define new credit limit for same company """
+		customer.append(
+			"credit_limits", {"credit_limit": flt(outstanding_amt - 100), "company": "_Test Company"}
+		)
 		self.assertRaises(frappe.ValidationError, customer.save)
 
 	def test_customer_payment_terms(self):
 		frappe.db.set_value(
-			"Customer", "_Test Customer With Template", "payment_terms", "_Test Payment Term Template 3")
+			"Customer", "_Test Customer With Template", "payment_terms", "_Test Payment Term Template 3"
+		)
 
 		due_date = get_due_date("2016-01-22", "Customer", "_Test Customer With Template")
 		self.assertEqual(due_date, "2016-02-21")
@@ -308,7 +323,8 @@
 		self.assertEqual(due_date, "2017-02-21")
 
 		frappe.db.set_value(
-			"Customer", "_Test Customer With Template", "payment_terms", "_Test Payment Term Template 1")
+			"Customer", "_Test Customer With Template", "payment_terms", "_Test Payment Term Template 1"
+		)
 
 		due_date = get_due_date("2016-01-22", "Customer", "_Test Customer With Template")
 		self.assertEqual(due_date, "2016-02-29")
@@ -328,13 +344,14 @@
 
 def get_customer_dict(customer_name):
 	return {
-		 "customer_group": "_Test Customer Group",
-		 "customer_name": customer_name,
-		 "customer_type": "Individual",
-		 "doctype": "Customer",
-		 "territory": "_Test Territory"
+		"customer_group": "_Test Customer Group",
+		"customer_name": customer_name,
+		"customer_type": "Individual",
+		"doctype": "Customer",
+		"territory": "_Test Territory",
 	}
 
+
 def set_credit_limit(customer, company, credit_limit):
 	customer = frappe.get_doc("Customer", customer)
 	existing_row = None
@@ -346,27 +363,32 @@
 			break
 
 	if not existing_row:
-		customer.append('credit_limits', {
-			'company': company,
-			'credit_limit': credit_limit
-		})
+		customer.append("credit_limits", {"company": company, "credit_limit": credit_limit})
 		customer.credit_limits[-1].db_insert()
 
-def create_internal_customer(customer_name, represents_company, allowed_to_interact_with):
-	if not frappe.db.exists("Customer", customer_name):
-		customer = frappe.get_doc({
-			"doctype": "Customer",
-			"customer_group": "_Test Customer Group",
-			"customer_name": customer_name,
-			"customer_type": "Individual",
-			"territory": "_Test Territory",
-			"is_internal_customer": 1,
-			"represents_company": represents_company
-		})
 
-		customer.append("companies", {
-			"company": allowed_to_interact_with
-		})
+def create_internal_customer(
+	customer_name=None, represents_company=None, allowed_to_interact_with=None
+):
+	if not customer_name:
+		customer_name = represents_company
+	if not allowed_to_interact_with:
+		allowed_to_interact_with = represents_company
+
+	if not frappe.db.exists("Customer", customer_name):
+		customer = frappe.get_doc(
+			{
+				"doctype": "Customer",
+				"customer_group": "_Test Customer Group",
+				"customer_name": customer_name,
+				"customer_type": "Individual",
+				"territory": "_Test Territory",
+				"is_internal_customer": 1,
+				"represents_company": represents_company,
+			}
+		)
+
+		customer.append("companies", {"company": allowed_to_interact_with})
 
 		customer.insert()
 		customer_name = customer.name
diff --git a/erpnext/selling/doctype/industry_type/test_industry_type.py b/erpnext/selling/doctype/industry_type/test_industry_type.py
index 250c2be..eb5f905 100644
--- a/erpnext/selling/doctype/industry_type/test_industry_type.py
+++ b/erpnext/selling/doctype/industry_type/test_industry_type.py
@@ -3,4 +3,4 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Industry Type')
+test_records = frappe.get_test_records("Industry Type")
diff --git a/erpnext/selling/doctype/installation_note/installation_note.py b/erpnext/selling/doctype/installation_note/installation_note.py
index 36acdbe..dd0b1e8 100644
--- a/erpnext/selling/doctype/installation_note/installation_note.py
+++ b/erpnext/selling/doctype/installation_note/installation_note.py
@@ -13,26 +13,29 @@
 class InstallationNote(TransactionBase):
 	def __init__(self, *args, **kwargs):
 		super(InstallationNote, self).__init__(*args, **kwargs)
-		self.status_updater = [{
-			'source_dt': 'Installation Note Item',
-			'target_dt': 'Delivery Note Item',
-			'target_field': 'installed_qty',
-			'target_ref_field': 'qty',
-			'join_field': 'prevdoc_detail_docname',
-			'target_parent_dt': 'Delivery Note',
-			'target_parent_field': 'per_installed',
-			'source_field': 'qty',
-			'percent_join_field': 'prevdoc_docname',
-			'status_field': 'installation_status',
-			'keyword': 'Installed',
-			'overflow_type': 'installation'
-		}]
+		self.status_updater = [
+			{
+				"source_dt": "Installation Note Item",
+				"target_dt": "Delivery Note Item",
+				"target_field": "installed_qty",
+				"target_ref_field": "qty",
+				"join_field": "prevdoc_detail_docname",
+				"target_parent_dt": "Delivery Note",
+				"target_parent_field": "per_installed",
+				"source_field": "qty",
+				"percent_join_field": "prevdoc_docname",
+				"status_field": "installation_status",
+				"keyword": "Installed",
+				"overflow_type": "installation",
+			}
+		]
 
 	def validate(self):
 		self.validate_installation_date()
 		self.check_item_table()
 
 		from erpnext.controllers.selling_controller import set_default_income_account_for_item
+
 		set_default_income_account_for_item(self)
 
 	def is_serial_no_added(self, item_code, serial_no):
@@ -48,18 +51,19 @@
 				frappe.throw(_("Serial No {0} does not exist").format(x))
 
 	def get_prevdoc_serial_no(self, prevdoc_detail_docname):
-		serial_nos = frappe.db.get_value("Delivery Note Item",
-			prevdoc_detail_docname, "serial_no")
+		serial_nos = frappe.db.get_value("Delivery Note Item", prevdoc_detail_docname, "serial_no")
 		return get_valid_serial_nos(serial_nos)
 
 	def is_serial_no_match(self, cur_s_no, prevdoc_s_no, prevdoc_docname):
 		for sr in cur_s_no:
 			if sr not in prevdoc_s_no:
-				frappe.throw(_("Serial No {0} does not belong to Delivery Note {1}").format(sr, prevdoc_docname))
+				frappe.throw(
+					_("Serial No {0} does not belong to Delivery Note {1}").format(sr, prevdoc_docname)
+				)
 
 	def validate_serial_no(self):
 		prevdoc_s_no, sr_list = [], []
-		for d in self.get('items'):
+		for d in self.get("items"):
 			self.is_serial_no_added(d.item_code, d.serial_no)
 			if d.serial_no:
 				sr_list = get_valid_serial_nos(d.serial_no, d.qty, d.item_code)
@@ -69,26 +73,27 @@
 				if prevdoc_s_no:
 					self.is_serial_no_match(sr_list, prevdoc_s_no, d.prevdoc_docname)
 
-
 	def validate_installation_date(self):
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if d.prevdoc_docname:
 				d_date = frappe.db.get_value("Delivery Note", d.prevdoc_docname, "posting_date")
 				if d_date > getdate(self.inst_date):
-					frappe.throw(_("Installation date cannot be before delivery date for Item {0}").format(d.item_code))
+					frappe.throw(
+						_("Installation date cannot be before delivery date for Item {0}").format(d.item_code)
+					)
 
 	def check_item_table(self):
-		if not(self.get('items')):
+		if not (self.get("items")):
 			frappe.throw(_("Please pull items from Delivery Note"))
 
 	def on_update(self):
-		frappe.db.set(self, 'status', 'Draft')
+		frappe.db.set(self, "status", "Draft")
 
 	def on_submit(self):
 		self.validate_serial_no()
 		self.update_prevdoc_status()
-		frappe.db.set(self, 'status', 'Submitted')
+		frappe.db.set(self, "status", "Submitted")
 
 	def on_cancel(self):
 		self.update_prevdoc_status()
-		frappe.db.set(self, 'status', 'Cancelled')
+		frappe.db.set(self, "status", "Cancelled")
diff --git a/erpnext/selling/doctype/installation_note/test_installation_note.py b/erpnext/selling/doctype/installation_note/test_installation_note.py
index d3c8be5..56e0fe1 100644
--- a/erpnext/selling/doctype/installation_note/test_installation_note.py
+++ b/erpnext/selling/doctype/installation_note/test_installation_note.py
@@ -5,5 +5,6 @@
 
 # test_records = frappe.get_test_records('Installation Note')
 
+
 class TestInstallationNote(unittest.TestCase):
 	pass
diff --git a/erpnext/selling/doctype/party_specific_item/party_specific_item.py b/erpnext/selling/doctype/party_specific_item/party_specific_item.py
index a408af5..0aef7d3 100644
--- a/erpnext/selling/doctype/party_specific_item/party_specific_item.py
+++ b/erpnext/selling/doctype/party_specific_item/party_specific_item.py
@@ -8,12 +8,14 @@
 
 class PartySpecificItem(Document):
 	def validate(self):
-		exists = frappe.db.exists({
-			'doctype': 'Party Specific Item',
-			'party_type': self.party_type,
-			'party': self.party,
-			'restrict_based_on': self.restrict_based_on,
-			'based_on': self.based_on_value,
-		})
+		exists = frappe.db.exists(
+			{
+				"doctype": "Party Specific Item",
+				"party_type": self.party_type,
+				"party": self.party,
+				"restrict_based_on": self.restrict_based_on,
+				"based_on": self.based_on_value,
+			}
+		)
 		if exists:
 			frappe.throw(_("This item filter has already been applied for the {0}").format(self.party_type))
diff --git a/erpnext/selling/doctype/party_specific_item/test_party_specific_item.py b/erpnext/selling/doctype/party_specific_item/test_party_specific_item.py
index 9b672b4..f98cbd7 100644
--- a/erpnext/selling/doctype/party_specific_item/test_party_specific_item.py
+++ b/erpnext/selling/doctype/party_specific_item/test_party_specific_item.py
@@ -6,16 +6,18 @@
 
 from erpnext.controllers.queries import item_query
 
-test_dependencies = ['Item', 'Customer', 'Supplier']
+test_dependencies = ["Item", "Customer", "Supplier"]
+
 
 def create_party_specific_item(**args):
 	psi = frappe.new_doc("Party Specific Item")
-	psi.party_type = args.get('party_type')
-	psi.party = args.get('party')
-	psi.restrict_based_on = args.get('restrict_based_on')
-	psi.based_on_value = args.get('based_on_value')
+	psi.party_type = args.get("party_type")
+	psi.party = args.get("party")
+	psi.restrict_based_on = args.get("restrict_based_on")
+	psi.based_on_value = args.get("based_on_value")
 	psi.insert()
 
+
 class TestPartySpecificItem(FrappeTestCase):
 	def setUp(self):
 		self.customer = frappe.get_last_doc("Customer")
@@ -23,15 +25,29 @@
 		self.item = frappe.get_last_doc("Item")
 
 	def test_item_query_for_customer(self):
-		create_party_specific_item(party_type='Customer', party=self.customer.name, restrict_based_on='Item', based_on_value=self.item.name)
-		filters = {'is_sales_item': 1, 'customer': self.customer.name}
-		items = item_query(doctype= 'Item', txt= '', searchfield= 'name', start= 0, page_len= 20,filters=filters, as_dict= False)
+		create_party_specific_item(
+			party_type="Customer",
+			party=self.customer.name,
+			restrict_based_on="Item",
+			based_on_value=self.item.name,
+		)
+		filters = {"is_sales_item": 1, "customer": self.customer.name}
+		items = item_query(
+			doctype="Item", txt="", searchfield="name", start=0, page_len=20, filters=filters, as_dict=False
+		)
 		for item in items:
 			self.assertEqual(item[0], self.item.name)
 
 	def test_item_query_for_supplier(self):
-		create_party_specific_item(party_type='Supplier', party=self.supplier.name, restrict_based_on='Item Group', based_on_value=self.item.item_group)
-		filters = {'supplier': self.supplier.name, 'is_purchase_item': 1}
-		items = item_query(doctype= 'Item', txt= '', searchfield= 'name', start= 0, page_len= 20,filters=filters, as_dict= False)
+		create_party_specific_item(
+			party_type="Supplier",
+			party=self.supplier.name,
+			restrict_based_on="Item Group",
+			based_on_value=self.item.item_group,
+		)
+		filters = {"supplier": self.supplier.name, "is_purchase_item": 1}
+		items = item_query(
+			doctype="Item", txt="", searchfield="name", start=0, page_len=20, filters=filters, as_dict=False
+		)
 		for item in items:
 			self.assertEqual(item[2], self.item.item_group)
diff --git a/erpnext/selling/doctype/product_bundle/product_bundle.py b/erpnext/selling/doctype/product_bundle/product_bundle.py
index 2bb876e..ac83c0f 100644
--- a/erpnext/selling/doctype/product_bundle/product_bundle.py
+++ b/erpnext/selling/doctype/product_bundle/product_bundle.py
@@ -16,11 +16,22 @@
 		self.validate_main_item()
 		self.validate_child_items()
 		from erpnext.utilities.transaction_base import validate_uom_is_integer
+
 		validate_uom_is_integer(self, "uom", "qty")
 
 	def on_trash(self):
-		linked_doctypes = ["Delivery Note", "Sales Invoice", "POS Invoice", "Purchase Receipt", "Purchase Invoice",
-			"Stock Entry", "Stock Reconciliation", "Sales Order", "Purchase Order", "Material Request"]
+		linked_doctypes = [
+			"Delivery Note",
+			"Sales Invoice",
+			"POS Invoice",
+			"Purchase Receipt",
+			"Purchase Invoice",
+			"Stock Entry",
+			"Stock Reconciliation",
+			"Sales Order",
+			"Purchase Order",
+			"Material Request",
+		]
 
 		invoice_links = []
 		for doctype in linked_doctypes:
@@ -29,15 +40,20 @@
 			if doctype == "Stock Entry":
 				item_doctype = doctype + " Detail"
 
-			invoices = frappe.db.get_all(item_doctype, {"item_code": self.new_item_code, "docstatus": 1}, ["parent"])
+			invoices = frappe.db.get_all(
+				item_doctype, {"item_code": self.new_item_code, "docstatus": 1}, ["parent"]
+			)
 
 			for invoice in invoices:
-				invoice_links.append(get_link_to_form(doctype, invoice['parent']))
+				invoice_links.append(get_link_to_form(doctype, invoice["parent"]))
 
 		if len(invoice_links):
 			frappe.throw(
-				"This Product Bundle is linked with {0}. You will have to cancel these documents in order to delete this Product Bundle"
-				.format(", ".join(invoice_links)), title=_("Not Allowed"))
+				"This Product Bundle is linked with {0}. You will have to cancel these documents in order to delete this Product Bundle".format(
+					", ".join(invoice_links)
+				),
+				title=_("Not Allowed"),
+			)
 
 	def validate_main_item(self):
 		"""Validates, main Item is not a stock item"""
@@ -47,15 +63,22 @@
 	def validate_child_items(self):
 		for item in self.items:
 			if frappe.db.exists("Product Bundle", item.item_code):
-				frappe.throw(_("Row #{0}: Child Item should not be a Product Bundle. Please remove Item {1} and Save").format(item.idx, frappe.bold(item.item_code)))
+				frappe.throw(
+					_(
+						"Row #{0}: Child Item should not be a Product Bundle. Please remove Item {1} and Save"
+					).format(item.idx, frappe.bold(item.item_code))
+				)
+
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_new_item_code(doctype, txt, searchfield, start, page_len, filters):
 	from erpnext.controllers.queries import get_match_cond
 
-	return frappe.db.sql("""select name, item_name, description from tabItem
+	return frappe.db.sql(
+		"""select name, item_name, description from tabItem
 		where is_stock_item=0 and name not in (select name from `tabProduct Bundle`)
-		and %s like %s %s limit %s, %s""" % (searchfield, "%s",
-		get_match_cond(doctype),"%s", "%s"),
-		("%%%s%%" % txt, start, page_len))
+		and %s like %s %s limit %s offset %s"""
+		% (searchfield, "%s", get_match_cond(doctype), "%s", "%s"),
+		("%%%s%%" % txt, page_len, start),
+	)
diff --git a/erpnext/selling/doctype/product_bundle/test_product_bundle.py b/erpnext/selling/doctype/product_bundle/test_product_bundle.py
index c1e2fde..82fe892 100644
--- a/erpnext/selling/doctype/product_bundle/test_product_bundle.py
+++ b/erpnext/selling/doctype/product_bundle/test_product_bundle.py
@@ -3,16 +3,14 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Product Bundle')
+test_records = frappe.get_test_records("Product Bundle")
+
 
 def make_product_bundle(parent, items, qty=None):
 	if frappe.db.exists("Product Bundle", parent):
 		return frappe.get_doc("Product Bundle", parent)
 
-	product_bundle = frappe.get_doc({
-		"doctype": "Product Bundle",
-		"new_item_code": parent
-	})
+	product_bundle = frappe.get_doc({"doctype": "Product Bundle", "new_item_code": parent})
 
 	for item in items:
 		product_bundle.append("items", {"item_code": item, "qty": qty or 1})
diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js
index 0e1a915..70ae085 100644
--- a/erpnext/selling/doctype/quotation/quotation.js
+++ b/erpnext/selling/doctype/quotation/quotation.js
@@ -20,6 +20,20 @@
 
 		frm.set_df_property('packed_items', 'cannot_add_rows', true);
 		frm.set_df_property('packed_items', 'cannot_delete_rows', true);
+
+		frm.set_query('company_address', function(doc) {
+			if(!doc.company) {
+				frappe.throw(__('Please set Company'));
+			}
+
+			return {
+				query: 'frappe.contacts.doctype.address.address.address_query',
+				filters: {
+					link_doctype: 'Company',
+					link_name: doc.company
+				}
+			};
+		});
 	},
 
 	refresh: function(frm) {
@@ -40,7 +54,6 @@
 
 erpnext.selling.QuotationController = class QuotationController extends erpnext.selling.SellingController {
 	onload(doc, dt, dn) {
-		var me = this;
 		super.onload(doc, dt, dn);
 	}
 	party_name() {
@@ -71,7 +84,7 @@
 			}
 		}
 
-		if(doc.docstatus == 1 && doc.status!=='Lost') {
+		if(doc.docstatus == 1 && !(['Lost', 'Ordered']).includes(doc.status)) {
 			if(!doc.valid_till || frappe.datetime.get_diff(doc.valid_till, frappe.datetime.get_today()) >= 0) {
 				cur_frm.add_custom_button(__('Sales Order'),
 					cur_frm.cscript['Make Sales Order'], __('Create'));
diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json
index ee5b0ea..bb2f95d 100644
--- a/erpnext/selling/doctype/quotation/quotation.json
+++ b/erpnext/selling/doctype/quotation/quotation.json
@@ -31,6 +31,8 @@
   "col_break98",
   "shipping_address_name",
   "shipping_address",
+  "company_address",
+  "company_address_display",
   "customer_group",
   "territory",
   "currency_and_price_list",
@@ -41,6 +43,8 @@
   "price_list_currency",
   "plc_conversion_rate",
   "ignore_pricing_rule",
+  "section_break_33",
+  "scan_barcode",
   "items_section",
   "items",
   "bundle_items_section",
@@ -292,7 +296,7 @@
    "read_only": 1
   },
   {
-   "depends_on": "eval:doc.quotaion_to=='Customer' && doc.party_name",
+   "depends_on": "eval:doc.quotation_to=='Customer' && doc.party_name",
    "fieldname": "col_break98",
    "fieldtype": "Column Break",
    "width": "50%"
@@ -312,7 +316,7 @@
    "read_only": 1
   },
   {
-   "depends_on": "eval:doc.quotaion_to=='Customer' && doc.party_name",
+   "depends_on": "eval:doc.quotation_to=='Customer' && doc.party_name",
    "fieldname": "customer_group",
    "fieldtype": "Link",
    "hidden": 1,
@@ -893,7 +897,7 @@
    "no_copy": 1,
    "oldfieldname": "status",
    "oldfieldtype": "Select",
-   "options": "Draft\nOpen\nReplied\nOrdered\nLost\nCancelled\nExpired",
+   "options": "Draft\nOpen\nReplied\nPartially Ordered\nOrdered\nLost\nCancelled\nExpired",
    "print_hide": 1,
    "read_only": 1,
    "reqd": 1
@@ -953,15 +957,36 @@
    "fieldname": "competitors",
    "fieldtype": "Table MultiSelect",
    "label": "Competitors",
-   "options": "Competitor Detail",
+   "options": "Competitor Detail"
+  },
+  {
+   "fieldname": "company_address",
+   "fieldtype": "Link",
+   "label": "Company Address Name",
+   "options": "Address"
+  },
+  {
+   "fieldname": "company_address_display",
+   "fieldtype": "Small Text",
+   "label": "Company Address",
    "read_only": 1
+  },
+  {
+   "fieldname": "section_break_33",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "scan_barcode",
+   "fieldtype": "Data",
+   "label": "Scan Barcode",
+   "options": "Barcode"
   }
  ],
  "icon": "fa fa-shopping-cart",
  "idx": 82,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-11-30 01:33:21.106073",
+ "modified": "2022-06-11 20:35:32.635804",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Quotation",
@@ -1056,6 +1081,7 @@
  "show_name_in_global_search": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "timeline_field": "party_name",
  "title_field": "title"
-}
\ No newline at end of file
+}
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index eebde76..4fa4515 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -10,29 +10,30 @@
 from erpnext.controllers.selling_controller import SellingController
 from erpnext.crm.utils import add_link_in_communication, copy_comments
 
-form_grid_templates = {
-	"items": "templates/form_grid/item_grid.html"
-}
+form_grid_templates = {"items": "templates/form_grid/item_grid.html"}
+
 
 class Quotation(SellingController):
 	def set_indicator(self):
-		if self.docstatus==1:
-			self.indicator_color = 'blue'
-			self.indicator_title = 'Submitted'
+		if self.docstatus == 1:
+			self.indicator_color = "blue"
+			self.indicator_title = "Submitted"
 		if self.valid_till and getdate(self.valid_till) < getdate(nowdate()):
-			self.indicator_color = 'gray'
-			self.indicator_title = 'Expired'
+			self.indicator_color = "gray"
+			self.indicator_title = "Expired"
 
 	def validate(self):
 		super(Quotation, self).validate()
 		self.set_status()
 		self.validate_uom_is_integer("stock_uom", "qty")
 		self.validate_valid_till()
+		self.validate_shopping_cart_items()
 		self.set_customer_name()
 		if self.items:
 			self.with_items = 1
 
 		from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
+
 		make_packing_list(self)
 
 	def after_insert(self):
@@ -49,18 +50,64 @@
 		if self.valid_till and getdate(self.valid_till) < getdate(self.transaction_date):
 			frappe.throw(_("Valid till date cannot be before transaction date"))
 
-	def has_sales_order(self):
-		return frappe.db.get_value("Sales Order Item", {"prevdoc_docname": self.name, "docstatus": 1})
+	def validate_shopping_cart_items(self):
+		if self.order_type != "Shopping Cart":
+			return
+
+		for item in self.items:
+			has_web_item = frappe.db.exists("Website Item", {"item_code": item.item_code})
+
+			# If variant is unpublished but template is published: valid
+			template = frappe.get_cached_value("Item", item.item_code, "variant_of")
+			if template and not has_web_item:
+				has_web_item = frappe.db.exists("Website Item", {"item_code": template})
+
+			if not has_web_item:
+				frappe.throw(
+					_("Row #{0}: Item {1} must have a Website Item for Shopping Cart Quotations").format(
+						item.idx, frappe.bold(item.item_code)
+					),
+					title=_("Unpublished Item"),
+				)
+
+	def get_ordered_status(self):
+		ordered_items = frappe._dict(
+			frappe.db.get_all(
+				"Sales Order Item",
+				{"prevdoc_docname": self.name, "docstatus": 1},
+				["item_code", "sum(qty)"],
+				group_by="item_code",
+				as_list=1,
+			)
+		)
+
+		status = "Open"
+		if ordered_items:
+			status = "Ordered"
+
+			for item in self.get("items"):
+				if item.qty > ordered_items.get(item.item_code, 0.0):
+					status = "Partially Ordered"
+
+		return status
+
+	def is_fully_ordered(self):
+		return self.get_ordered_status() == "Ordered"
+
+	def is_partially_ordered(self):
+		return self.get_ordered_status() == "Partially Ordered"
 
 	def update_lead(self):
 		if self.quotation_to == "Lead" and self.party_name:
 			frappe.get_doc("Lead", self.party_name).set_status(update=True)
 
 	def set_customer_name(self):
-		if self.party_name and self.quotation_to == 'Customer':
+		if self.party_name and self.quotation_to == "Customer":
 			self.customer_name = frappe.db.get_value("Customer", self.party_name, "customer_name")
-		elif self.party_name and self.quotation_to == 'Lead':
-			lead_name, company_name = frappe.db.get_value("Lead", self.party_name, ["lead_name", "company_name"])
+		elif self.party_name and self.quotation_to == "Lead":
+			lead_name, company_name = frappe.db.get_value(
+				"Lead", self.party_name, ["lead_name", "company_name"]
+			)
 			self.customer_name = company_name or lead_name
 
 	def update_opportunity(self, status):
@@ -80,25 +127,28 @@
 
 	@frappe.whitelist()
 	def declare_enquiry_lost(self, lost_reasons_list, competitors, detailed_reason=None):
-		if not self.has_sales_order():
-			get_lost_reasons = frappe.get_list('Quotation Lost Reason',
-			fields = ["name"])
-			lost_reasons_lst = [reason.get('name') for reason in get_lost_reasons]
-			frappe.db.set(self, 'status', 'Lost')
+		if not (self.is_fully_ordered() or self.is_partially_ordered()):
+			get_lost_reasons = frappe.get_list("Quotation Lost Reason", fields=["name"])
+			lost_reasons_lst = [reason.get("name") for reason in get_lost_reasons]
+			frappe.db.set(self, "status", "Lost")
 
 			if detailed_reason:
-				frappe.db.set(self, 'order_lost_reason', detailed_reason)
+				frappe.db.set(self, "order_lost_reason", detailed_reason)
 
 			for reason in lost_reasons_list:
-				if reason.get('lost_reason') in lost_reasons_lst:
-					self.append('lost_reasons', reason)
+				if reason.get("lost_reason") in lost_reasons_lst:
+					self.append("lost_reasons", reason)
 				else:
-					frappe.throw(_("Invalid lost reason {0}, please create a new lost reason").format(frappe.bold(reason.get('lost_reason'))))
+					frappe.throw(
+						_("Invalid lost reason {0}, please create a new lost reason").format(
+							frappe.bold(reason.get("lost_reason"))
+						)
+					)
 
 			for competitor in competitors:
-				self.append('competitors', competitor)
+				self.append("competitors", competitor)
 
-			self.update_opportunity('Lost')
+			self.update_opportunity("Lost")
 			self.update_lead()
 			self.save()
 
@@ -107,11 +157,12 @@
 
 	def on_submit(self):
 		# Check for Approving Authority
-		frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
-			self.company, self.base_grand_total, self)
+		frappe.get_doc("Authorization Control").validate_approving_authority(
+			self.doctype, self.company, self.base_grand_total, self
+		)
 
-		#update enquiry status
-		self.update_opportunity('Quotation')
+		# update enquiry status
+		self.update_opportunity("Quotation")
 		self.update_lead()
 
 	def on_cancel(self):
@@ -119,14 +170,14 @@
 			self.lost_reasons = []
 		super(Quotation, self).on_cancel()
 
-		#update enquiry status
+		# update enquiry status
 		self.set_status(update=True)
-		self.update_opportunity('Open')
+		self.update_opportunity("Open")
 		self.update_lead()
 
-	def print_other_charges(self,docname):
+	def print_other_charges(self, docname):
 		print_lst = []
-		for d in self.get('taxes'):
+		for d in self.get("taxes"):
 			lst1 = []
 			lst1.append(d.description)
 			lst1.append(d.total)
@@ -136,25 +187,35 @@
 	def on_recurring(self, reference_doc, auto_repeat_doc):
 		self.valid_till = None
 
+
 def get_list_context(context=None):
 	from erpnext.controllers.website_list_for_contact import get_list_context
+
 	list_context = get_list_context(context)
-	list_context.update({
-		'show_sidebar': True,
-		'show_search': True,
-		'no_breadcrumbs': True,
-		'title': _('Quotations'),
-	})
+	list_context.update(
+		{
+			"show_sidebar": True,
+			"show_search": True,
+			"no_breadcrumbs": True,
+			"title": _("Quotations"),
+		}
+	)
 
 	return list_context
 
+
 @frappe.whitelist()
 def make_sales_order(source_name, target_doc=None):
-	quotation = frappe.db.get_value("Quotation", source_name, ["transaction_date", "valid_till"], as_dict = 1)
-	if quotation.valid_till and (quotation.valid_till < quotation.transaction_date or quotation.valid_till < getdate(nowdate())):
+	quotation = frappe.db.get_value(
+		"Quotation", source_name, ["transaction_date", "valid_till"], as_dict=1
+	)
+	if quotation.valid_till and (
+		quotation.valid_till < quotation.transaction_date or quotation.valid_till < getdate(nowdate())
+	):
 		frappe.throw(_("Validity period of this quotation has ended."))
 	return _make_sales_order(source_name, target_doc)
 
+
 def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
 	customer = _make_customer(source_name, ignore_permissions)
 
@@ -163,9 +224,10 @@
 			target.customer = customer.name
 			target.customer_name = customer.customer_name
 		if source.referral_sales_partner:
-			target.sales_partner=source.referral_sales_partner
-			target.commission_rate=frappe.get_value('Sales Partner', source.referral_sales_partner, 'commission_rate')
-		target.ignore_pricing_rule = 1
+			target.sales_partner = source.referral_sales_partner
+			target.commission_rate = frappe.get_value(
+				"Sales Partner", source.referral_sales_partner, "commission_rate"
+			)
 		target.flags.ignore_permissions = ignore_permissions
 		target.run_method("set_missing_values")
 		target.run_method("calculate_taxes_and_totals")
@@ -178,41 +240,34 @@
 			target.blanket_order = obj.blanket_order
 			target.blanket_order_rate = obj.blanket_order_rate
 
-	doclist = get_mapped_doc("Quotation", source_name, {
-			"Quotation": {
-				"doctype": "Sales Order",
-				"validation": {
-					"docstatus": ["=", 1]
-				}
-			},
+	doclist = get_mapped_doc(
+		"Quotation",
+		source_name,
+		{
+			"Quotation": {"doctype": "Sales Order", "validation": {"docstatus": ["=", 1]}},
 			"Quotation Item": {
 				"doctype": "Sales Order Item",
-				"field_map": {
-					"parent": "prevdoc_docname"
-				},
-				"postprocess": update_item
+				"field_map": {"parent": "prevdoc_docname"},
+				"postprocess": update_item,
 			},
-			"Sales Taxes and Charges": {
-				"doctype": "Sales Taxes and Charges",
-				"add_if_empty": True
-			},
-			"Sales Team": {
-				"doctype": "Sales Team",
-				"add_if_empty": True
-			},
-			"Payment Schedule": {
-				"doctype": "Payment Schedule",
-				"add_if_empty": True
-			}
-		}, target_doc, set_missing_values, ignore_permissions=ignore_permissions)
+			"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True},
+			"Sales Team": {"doctype": "Sales Team", "add_if_empty": True},
+			"Payment Schedule": {"doctype": "Payment Schedule", "add_if_empty": True},
+		},
+		target_doc,
+		set_missing_values,
+		ignore_permissions=ignore_permissions,
+	)
 
 	# postprocess: fetch shipping address, set missing values
+	doclist.set_onload("ignore_price_list", True)
 
 	return doclist
 
+
 def set_expired_status():
 	# filter out submitted non expired quotations whose validity has been ended
-	cond = "qo.docstatus = 1 and qo.status != 'Expired' and qo.valid_till < %s"
+	cond = "`tabQuotation`.docstatus = 1 and `tabQuotation`.status != 'Expired' and `tabQuotation`.valid_till < %s"
 	# check if those QUO have SO against it
 	so_against_quo = """
 		SELECT
@@ -220,19 +275,27 @@
 		WHERE
 			so_item.docstatus = 1 and so.docstatus = 1
 			and so_item.parent = so.name
-			and so_item.prevdoc_docname = qo.name"""
+			and so_item.prevdoc_docname = `tabQuotation`.name"""
 
 	# if not exists any SO, set status as Expired
-	frappe.db.sql(
-		"""UPDATE `tabQuotation` qo SET qo.status = 'Expired' WHERE {cond} and not exists({so_against_quo})"""
-			.format(cond=cond, so_against_quo=so_against_quo),
-			(nowdate())
-		)
+	frappe.db.multisql(
+		{
+			"mariadb": """UPDATE `tabQuotation`  SET `tabQuotation`.status = 'Expired' WHERE {cond} and not exists({so_against_quo})""".format(
+				cond=cond, so_against_quo=so_against_quo
+			),
+			"postgres": """UPDATE `tabQuotation` SET status = 'Expired' FROM `tabSales Order`, `tabSales Order Item` WHERE {cond} and not exists({so_against_quo})""".format(
+				cond=cond, so_against_quo=so_against_quo
+			),
+		},
+		(nowdate()),
+	)
+
 
 @frappe.whitelist()
 def make_sales_invoice(source_name, target_doc=None):
 	return _make_sales_invoice(source_name, target_doc)
 
+
 def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
 	customer = _make_customer(source_name, ignore_permissions)
 
@@ -240,7 +303,7 @@
 		if customer:
 			target.customer = customer.name
 			target.customer_name = customer.customer_name
-		target.ignore_pricing_rule = 1
+
 		target.flags.ignore_permissions = ignore_permissions
 		target.run_method("set_missing_values")
 		target.run_method("calculate_taxes_and_totals")
@@ -249,52 +312,52 @@
 		target.cost_center = None
 		target.stock_qty = flt(obj.qty) * flt(obj.conversion_factor)
 
-	doclist = get_mapped_doc("Quotation", source_name, {
-			"Quotation": {
-				"doctype": "Sales Invoice",
-				"validation": {
-					"docstatus": ["=", 1]
-				}
-			},
-			"Quotation Item": {
-				"doctype": "Sales Invoice Item",
-				"postprocess": update_item
-			},
-			"Sales Taxes and Charges": {
-				"doctype": "Sales Taxes and Charges",
-				"add_if_empty": True
-			},
-			"Sales Team": {
-				"doctype": "Sales Team",
-				"add_if_empty": True
-			}
-		}, target_doc, set_missing_values, ignore_permissions=ignore_permissions)
+	doclist = get_mapped_doc(
+		"Quotation",
+		source_name,
+		{
+			"Quotation": {"doctype": "Sales Invoice", "validation": {"docstatus": ["=", 1]}},
+			"Quotation Item": {"doctype": "Sales Invoice Item", "postprocess": update_item},
+			"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True},
+			"Sales Team": {"doctype": "Sales Team", "add_if_empty": True},
+		},
+		target_doc,
+		set_missing_values,
+		ignore_permissions=ignore_permissions,
+	)
+
+	doclist.set_onload("ignore_price_list", True)
 
 	return doclist
 
-def _make_customer(source_name, ignore_permissions=False):
-	quotation = frappe.db.get_value("Quotation",
-		source_name, ["order_type", "party_name", "customer_name"], as_dict=1)
 
-	if quotation and quotation.get('party_name'):
+def _make_customer(source_name, ignore_permissions=False):
+	quotation = frappe.db.get_value(
+		"Quotation", source_name, ["order_type", "party_name", "customer_name"], as_dict=1
+	)
+
+	if quotation and quotation.get("party_name"):
 		if not frappe.db.exists("Customer", quotation.get("party_name")):
 			lead_name = quotation.get("party_name")
-			customer_name = frappe.db.get_value("Customer", {"lead_name": lead_name},
-				["name", "customer_name"], as_dict=True)
+			customer_name = frappe.db.get_value(
+				"Customer", {"lead_name": lead_name}, ["name", "customer_name"], as_dict=True
+			)
 			if not customer_name:
 				from erpnext.crm.doctype.lead.lead import _make_customer
+
 				customer_doclist = _make_customer(lead_name, ignore_permissions=ignore_permissions)
 				customer = frappe.get_doc(customer_doclist)
 				customer.flags.ignore_permissions = ignore_permissions
 				if quotation.get("party_name") == "Shopping Cart":
-					customer.customer_group = frappe.db.get_value("E Commerce Settings", None,
-						"default_customer_group")
+					customer.customer_group = frappe.db.get_value(
+						"E Commerce Settings", None, "default_customer_group"
+					)
 
 				try:
 					customer.insert()
 					return customer
 				except frappe.NameError:
-					if frappe.defaults.get_global_default('cust_master_name') == "Customer Name":
+					if frappe.defaults.get_global_default("cust_master_name") == "Customer Name":
 						customer.run_method("autoname")
 						customer.name += "-" + lead_name
 						customer.insert()
@@ -302,12 +365,14 @@
 					else:
 						raise
 				except frappe.MandatoryError as e:
-					mandatory_fields = e.args[0].split(':')[1].split(',')
+					mandatory_fields = e.args[0].split(":")[1].split(",")
 					mandatory_fields = [customer.meta.get_label(field.strip()) for field in mandatory_fields]
 
 					frappe.local.message_log = []
 					lead_link = frappe.utils.get_link_to_form("Lead", lead_name)
-					message = _("Could not auto create Customer due to the following missing mandatory field(s):") + "<br>"
+					message = (
+						_("Could not auto create Customer due to the following missing mandatory field(s):") + "<br>"
+					)
 					message += "<br><ul><li>" + "</li><li>".join(mandatory_fields) + "</li></ul>"
 					message += _("Please create Customer from Lead {0}.").format(lead_link)
 
diff --git a/erpnext/selling/doctype/quotation/quotation_dashboard.py b/erpnext/selling/doctype/quotation/quotation_dashboard.py
index 0a1aad7..7bfa034 100644
--- a/erpnext/selling/doctype/quotation/quotation_dashboard.py
+++ b/erpnext/selling/doctype/quotation/quotation_dashboard.py
@@ -3,18 +3,12 @@
 
 def get_data():
 	return {
-		'fieldname': 'prevdoc_docname',
-		'non_standard_fieldnames': {
-			'Auto Repeat': 'reference_document',
+		"fieldname": "prevdoc_docname",
+		"non_standard_fieldnames": {
+			"Auto Repeat": "reference_document",
 		},
-		'transactions': [
-			{
-				'label': _('Sales Order'),
-				'items': ['Sales Order']
-			},
-			{
-				'label': _('Subscription'),
-				'items': ['Auto Repeat']
-			},
-		]
+		"transactions": [
+			{"label": _("Sales Order"), "items": ["Sales Order"]},
+			{"label": _("Subscription"), "items": ["Auto Repeat"]},
+		],
 	}
diff --git a/erpnext/selling/doctype/quotation/quotation_list.js b/erpnext/selling/doctype/quotation/quotation_list.js
index 4c8f9c4..32fce1f 100644
--- a/erpnext/selling/doctype/quotation/quotation_list.js
+++ b/erpnext/selling/doctype/quotation/quotation_list.js
@@ -25,6 +25,8 @@
 	get_indicator: function(doc) {
 		if(doc.status==="Open") {
 			return [__("Open"), "orange", "status,=,Open"];
+		} else if (doc.status==="Partially Ordered") {
+			return [__("Partially Ordered"), "yellow", "status,=,Partially Ordered"];
 		} else if(doc.status==="Ordered") {
 			return [__("Ordered"), "green", "status,=,Ordered"];
 		} else if(doc.status==="Lost") {
diff --git a/erpnext/selling/doctype/quotation/regional/india.js b/erpnext/selling/doctype/quotation/regional/india.js
new file mode 100644
index 0000000..9550835
--- /dev/null
+++ b/erpnext/selling/doctype/quotation/regional/india.js
@@ -0,0 +1,3 @@
+{% include "erpnext/regional/india/taxes.js" %}
+
+erpnext.setup_auto_gst_taxation('Quotation');
diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py
index a749d9e..6f0b381 100644
--- a/erpnext/selling/doctype/quotation/test_quotation.py
+++ b/erpnext/selling/doctype/quotation/test_quotation.py
@@ -11,7 +11,7 @@
 class TestQuotation(FrappeTestCase):
 	def test_make_quotation_without_terms(self):
 		quotation = make_quotation(do_not_save=1)
-		self.assertFalse(quotation.get('payment_schedule'))
+		self.assertFalse(quotation.get("payment_schedule"))
 
 		quotation.insert()
 
@@ -28,7 +28,7 @@
 
 		sales_order = make_sales_order(quotation.name)
 
-		self.assertTrue(sales_order.get('payment_schedule'))
+		self.assertTrue(sales_order.get("payment_schedule"))
 
 	def test_make_sales_order_with_different_currency(self):
 		from erpnext.selling.doctype.quotation.quotation import make_sales_order
@@ -80,9 +80,7 @@
 		quotation = frappe.copy_doc(test_records[0])
 		quotation.transaction_date = nowdate()
 		quotation.valid_till = add_months(quotation.transaction_date, 1)
-		quotation.update(
-			{"payment_terms_template": "_Test Payment Term Template"}
-		)
+		quotation.update({"payment_terms_template": "_Test Payment Term Template"})
 		quotation.insert()
 
 		self.assertRaises(frappe.ValidationError, make_sales_order, quotation.name)
@@ -92,7 +90,9 @@
 		self.assertEqual(quotation.payment_schedule[0].payment_amount, 8906.00)
 		self.assertEqual(quotation.payment_schedule[0].due_date, quotation.transaction_date)
 		self.assertEqual(quotation.payment_schedule[1].payment_amount, 8906.00)
-		self.assertEqual(quotation.payment_schedule[1].due_date, add_days(quotation.transaction_date, 30))
+		self.assertEqual(
+			quotation.payment_schedule[1].due_date, add_days(quotation.transaction_date, 30)
+		)
 
 		sales_order = make_sales_order(quotation.name)
 
@@ -108,7 +108,7 @@
 		sales_order.insert()
 
 		# Remove any unknown taxes if applied
-		sales_order.set('taxes', [])
+		sales_order.set("taxes", [])
 		sales_order.save()
 
 		self.assertEqual(sales_order.payment_schedule[0].payment_amount, 8906.00)
@@ -130,6 +130,15 @@
 		quotation.submit()
 		self.assertRaises(frappe.ValidationError, make_sales_order, quotation.name)
 
+	def test_shopping_cart_without_website_item(self):
+		if frappe.db.exists("Website Item", {"item_code": "_Test Item Home Desktop 100"}):
+			frappe.get_last_doc("Website Item", {"item_code": "_Test Item Home Desktop 100"}).delete()
+
+		quotation = frappe.copy_doc(test_records[0])
+		quotation.order_type = "Shopping Cart"
+		quotation.valid_till = getdate()
+		self.assertRaises(frappe.ValidationError, quotation.validate)
+
 	def test_create_quotation_with_margin(self):
 		from erpnext.selling.doctype.quotation.quotation import make_sales_order
 		from erpnext.selling.doctype.sales_order.sales_order import (
@@ -137,11 +146,11 @@
 			make_sales_invoice,
 		)
 
-		rate_with_margin = flt((1500*18.75)/100 + 1500)
+		rate_with_margin = flt((1500 * 18.75) / 100 + 1500)
 
-		test_records[0]['items'][0]['price_list_rate'] = 1500
-		test_records[0]['items'][0]['margin_type'] = 'Percentage'
-		test_records[0]['items'][0]['margin_rate_or_amount'] = 18.75
+		test_records[0]["items"][0]["price_list_rate"] = 1500
+		test_records[0]["items"][0]["margin_type"] = "Percentage"
+		test_records[0]["items"][0]["margin_rate_or_amount"] = 18.75
 
 		quotation = frappe.copy_doc(test_records[0])
 		quotation.transaction_date = nowdate()
@@ -174,11 +183,9 @@
 	def test_create_two_quotations(self):
 		from erpnext.stock.doctype.item.test_item import make_item
 
-		first_item = make_item("_Test Laptop",
-							{"is_stock_item": 1})
+		first_item = make_item("_Test Laptop", {"is_stock_item": 1})
 
-		second_item = make_item("_Test CPU",
-							{"is_stock_item": 1})
+		second_item = make_item("_Test CPU", {"is_stock_item": 1})
 
 		qo_item1 = [
 			{
@@ -187,7 +194,7 @@
 				"qty": 2,
 				"rate": 400,
 				"delivered_by_supplier": 1,
-				"supplier": '_Test Supplier'
+				"supplier": "_Test Supplier",
 			}
 		]
 
@@ -197,7 +204,7 @@
 				"warehouse": "_Test Warehouse - _TC",
 				"qty": 2,
 				"rate": 300,
-				"conversion_factor": 1.0
+				"conversion_factor": 1.0,
 			}
 		]
 
@@ -209,17 +216,12 @@
 	def test_quotation_expiry(self):
 		from erpnext.selling.doctype.quotation.quotation import set_expired_status
 
-		quotation_item = [
-			{
-				"item_code": "_Test Item",
-				"warehouse":"",
-				"qty": 1,
-				"rate": 500
-			}
-		]
+		quotation_item = [{"item_code": "_Test Item", "warehouse": "", "qty": 1, "rate": 500}]
 
 		yesterday = add_days(nowdate(), -1)
-		expired_quotation = make_quotation(item_list=quotation_item, transaction_date=yesterday, do_not_submit=True)
+		expired_quotation = make_quotation(
+			item_list=quotation_item, transaction_date=yesterday, do_not_submit=True
+		)
 		expired_quotation.valid_till = yesterday
 		expired_quotation.save()
 		expired_quotation.submit()
@@ -236,24 +238,49 @@
 		make_item("_Test Bundle Item 1", {"is_stock_item": 1})
 		make_item("_Test Bundle Item 2", {"is_stock_item": 1})
 
-		make_product_bundle("_Test Product Bundle",
-			["_Test Bundle Item 1", "_Test Bundle Item 2"])
+		make_product_bundle("_Test Product Bundle", ["_Test Bundle Item 1", "_Test Bundle Item 2"])
 
 		quotation = make_quotation(item_code="_Test Product Bundle", qty=1, rate=100)
 		sales_order = make_sales_order(quotation.name)
 
-		quotation_item = [quotation.items[0].item_code, quotation.items[0].rate, quotation.items[0].qty, quotation.items[0].amount]
-		so_item = [sales_order.items[0].item_code, sales_order.items[0].rate, sales_order.items[0].qty, sales_order.items[0].amount]
+		quotation_item = [
+			quotation.items[0].item_code,
+			quotation.items[0].rate,
+			quotation.items[0].qty,
+			quotation.items[0].amount,
+		]
+		so_item = [
+			sales_order.items[0].item_code,
+			sales_order.items[0].rate,
+			sales_order.items[0].qty,
+			sales_order.items[0].amount,
+		]
 
 		self.assertEqual(quotation_item, so_item)
 
 		quotation_packed_items = [
-			[quotation.packed_items[0].parent_item, quotation.packed_items[0].item_code, quotation.packed_items[0].qty],
-			[quotation.packed_items[1].parent_item, quotation.packed_items[1].item_code, quotation.packed_items[1].qty]
+			[
+				quotation.packed_items[0].parent_item,
+				quotation.packed_items[0].item_code,
+				quotation.packed_items[0].qty,
+			],
+			[
+				quotation.packed_items[1].parent_item,
+				quotation.packed_items[1].item_code,
+				quotation.packed_items[1].qty,
+			],
 		]
 		so_packed_items = [
-			[sales_order.packed_items[0].parent_item, sales_order.packed_items[0].item_code, sales_order.packed_items[0].qty],
-			[sales_order.packed_items[1].parent_item, sales_order.packed_items[1].item_code, sales_order.packed_items[1].qty]
+			[
+				sales_order.packed_items[0].parent_item,
+				sales_order.packed_items[0].item_code,
+				sales_order.packed_items[0].qty,
+			],
+			[
+				sales_order.packed_items[1].parent_item,
+				sales_order.packed_items[1].item_code,
+				sales_order.packed_items[1].qty,
+			],
 		]
 
 		self.assertEqual(quotation_packed_items, so_packed_items)
@@ -266,8 +293,7 @@
 		bundle_item1 = make_item("_Test Bundle Item 1", {"is_stock_item": 1})
 		bundle_item2 = make_item("_Test Bundle Item 2", {"is_stock_item": 1})
 
-		make_product_bundle("_Test Product Bundle",
-			["_Test Bundle Item 1", "_Test Bundle Item 2"])
+		make_product_bundle("_Test Product Bundle", ["_Test Bundle Item 1", "_Test Bundle Item 2"])
 
 		bundle_item1.valuation_rate = 100
 		bundle_item1.save()
@@ -286,8 +312,7 @@
 		make_item("_Test Bundle Item 1", {"is_stock_item": 1})
 		make_item("_Test Bundle Item 2", {"is_stock_item": 1})
 
-		make_product_bundle("_Test Product Bundle",
-			["_Test Bundle Item 1", "_Test Bundle Item 2"])
+		make_product_bundle("_Test Product Bundle", ["_Test Bundle Item 1", "_Test Bundle Item 2"])
 
 		enable_calculate_bundle_price()
 
@@ -301,7 +326,9 @@
 
 		enable_calculate_bundle_price(enable=0)
 
-	def test_product_bundle_price_calculation_for_multiple_product_bundles_when_calculate_bundle_price_is_checked(self):
+	def test_product_bundle_price_calculation_for_multiple_product_bundles_when_calculate_bundle_price_is_checked(
+		self,
+	):
 		from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
 		from erpnext.stock.doctype.item.test_item import make_item
 
@@ -311,10 +338,8 @@
 		make_item("_Test Bundle Item 2", {"is_stock_item": 1})
 		make_item("_Test Bundle Item 3", {"is_stock_item": 1})
 
-		make_product_bundle("_Test Product Bundle 1",
-			["_Test Bundle Item 1", "_Test Bundle Item 2"])
-		make_product_bundle("_Test Product Bundle 2",
-			["_Test Bundle Item 2", "_Test Bundle Item 3"])
+		make_product_bundle("_Test Product Bundle 1", ["_Test Bundle Item 1", "_Test Bundle Item 2"])
+		make_product_bundle("_Test Product Bundle 2", ["_Test Bundle Item 2", "_Test Bundle Item 3"])
 
 		enable_calculate_bundle_price()
 
@@ -325,7 +350,7 @@
 				"qty": 1,
 				"rate": 400,
 				"delivered_by_supplier": 1,
-				"supplier": '_Test Supplier'
+				"supplier": "_Test Supplier",
 			},
 			{
 				"item_code": "_Test Product Bundle 2",
@@ -333,8 +358,8 @@
 				"qty": 1,
 				"rate": 400,
 				"delivered_by_supplier": 1,
-				"supplier": '_Test Supplier'
-			}
+				"supplier": "_Test Supplier",
+			},
 		]
 
 		quotation = make_quotation(item_list=item_list, do_not_submit=1)
@@ -347,7 +372,7 @@
 		expected_values = [300, 500]
 
 		for item in quotation.items:
-			self.assertEqual(item.amount, expected_values[item.idx-1])
+			self.assertEqual(item.amount, expected_values[item.idx - 1])
 
 		enable_calculate_bundle_price(enable=0)
 
@@ -362,12 +387,9 @@
 		make_item("_Test Bundle Item 2", {"is_stock_item": 1})
 		make_item("_Test Bundle Item 3", {"is_stock_item": 1})
 
-		make_product_bundle("_Test Product Bundle 1",
-			["_Test Bundle Item 1", "_Test Bundle Item 2"])
-		make_product_bundle("_Test Product Bundle 2",
-			["_Test Bundle Item 2", "_Test Bundle Item 3"])
-		make_product_bundle("_Test Product Bundle 3",
-			["_Test Bundle Item 3", "_Test Bundle Item 1"])
+		make_product_bundle("_Test Product Bundle 1", ["_Test Bundle Item 1", "_Test Bundle Item 2"])
+		make_product_bundle("_Test Product Bundle 2", ["_Test Bundle Item 2", "_Test Bundle Item 3"])
+		make_product_bundle("_Test Product Bundle 3", ["_Test Bundle Item 3", "_Test Bundle Item 1"])
 
 		item_list = [
 			{
@@ -376,7 +398,7 @@
 				"qty": 1,
 				"rate": 400,
 				"delivered_by_supplier": 1,
-				"supplier": '_Test Supplier'
+				"supplier": "_Test Supplier",
 			},
 			{
 				"item_code": "_Test Product Bundle 2",
@@ -384,7 +406,7 @@
 				"qty": 1,
 				"rate": 400,
 				"delivered_by_supplier": 1,
-				"supplier": '_Test Supplier'
+				"supplier": "_Test Supplier",
 			},
 			{
 				"item_code": "_Test Product Bundle 3",
@@ -392,8 +414,8 @@
 				"qty": 1,
 				"rate": 400,
 				"delivered_by_supplier": 1,
-				"supplier": '_Test Supplier'
-			}
+				"supplier": "_Test Supplier",
+			},
 		]
 
 		quotation = make_quotation(item_list=item_list, do_not_submit=1)
@@ -404,29 +426,26 @@
 			expected_index = id + 1
 			self.assertEqual(item.idx, expected_index)
 
-test_records = frappe.get_test_records('Quotation')
+
+test_records = frappe.get_test_records("Quotation")
+
 
 def enable_calculate_bundle_price(enable=1):
 	selling_settings = frappe.get_doc("Selling Settings")
 	selling_settings.editable_bundle_item_rates = enable
 	selling_settings.save()
 
+
 def get_quotation_dict(party_name=None, item_code=None):
 	if not party_name:
-		party_name = '_Test Customer'
+		party_name = "_Test Customer"
 	if not item_code:
-		item_code = '_Test Item'
+		item_code = "_Test Item"
 
 	return {
-		'doctype': 'Quotation',
-		'party_name': party_name,
-		'items': [
-			{
-				'item_code': item_code,
-				'qty': 1,
-				'rate': 100
-			}
-		]
+		"doctype": "Quotation",
+		"party_name": party_name,
+		"items": [{"item_code": item_code, "qty": 1, "rate": 100}],
 	}
 
 
@@ -450,13 +469,16 @@
 			qo.append("items", item)
 
 	else:
-		qo.append("items", {
-			"item_code": args.item or args.item_code or "_Test Item",
-			"warehouse": args.warehouse,
-			"qty": args.qty or 10,
-			"uom": args.uom or None,
-			"rate": args.rate or 100
-		})
+		qo.append(
+			"items",
+			{
+				"item_code": args.item or args.item_code or "_Test Item",
+				"warehouse": args.warehouse,
+				"qty": args.qty or 10,
+				"uom": args.uom or None,
+				"rate": args.rate or 100,
+			},
+		)
 
 	qo.delivery_date = add_days(qo.transaction_date, 10)
 
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index f80eaf2..6b6ea89 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -65,7 +65,11 @@
 			frm.set_value('transaction_date', frappe.datetime.get_today())
 		}
 		erpnext.queries.setup_queries(frm, "Warehouse", function() {
-			return erpnext.queries.warehouse(frm.doc);
+			return {
+				filters: [
+					["Warehouse", "company", "in", ["", cstr(frm.doc.company)]],
+				]
+			};
 		});
 
 		frm.set_query('project', function(doc, cdt, cdn) {
@@ -77,7 +81,19 @@
 			}
 		});
 
-		erpnext.queries.setup_warehouse_query(frm);
+		frm.set_query('warehouse', 'items', function(doc, cdt, cdn) {
+			let row  = locals[cdt][cdn];
+			let query = {
+				filters: [
+					["Warehouse", "company", "in", ["", cstr(frm.doc.company)]],
+				]
+			};
+			if (row.item_code) {
+				query.query = "erpnext.controllers.queries.warehouse_query";
+				query.filters.push(["Bin", "item_code", "=", row.item_code]);
+			}
+			return query;
+		});
 
 		frm.ignore_doctypes_on_cancel_all = ['Purchase Order'];
 	},
@@ -152,7 +168,9 @@
 						}
 					}
 
-					this.frm.add_custom_button(__('Pick List'), () => this.create_pick_list(), __('Create'));
+					if (flt(doc.per_picked, 6) < 100 && flt(doc.per_delivered, 6) < 100) {
+						this.frm.add_custom_button(__('Pick List'), () => this.create_pick_list(), __('Create'));
+					}
 
 					const order_is_a_sale = ["Sales", "Shopping Cart"].indexOf(doc.order_type) !== -1;
 					const order_is_maintenance = ["Maintenance"].indexOf(doc.order_type) !== -1;
@@ -693,12 +711,12 @@
 
 	get_ordered_qty(item, so) {
 		let ordered_qty = item.ordered_qty;
-		if (so.packed_items) {
+		if (so.packed_items && so.packed_items.length) {
 			// calculate ordered qty based on packed items in case of product bundle
 			let packed_items = so.packed_items.filter(
 				(pi) => pi.parent_detail_docname == item.name
 			);
-			if (packed_items) {
+			if (packed_items && packed_items.length) {
 				ordered_qty = packed_items.reduce(
 					(sum, pi) => sum + flt(pi.ordered_qty),
 					0
@@ -727,7 +745,7 @@
 					args: {
 						reference_doctype: me.frm.doctype,
 						reference_name: me.frm.docname,
-						content: __('Reason for hold: ')+data.reason_for_hold,
+						content: __('Reason for hold:') + ' ' + data.reason_for_hold,
 						comment_email: frappe.session.user,
 						comment_by: frappe.session.user_fullname
 					},
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index 7e99a06..74c5c07 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -25,6 +25,10 @@
   "po_no",
   "po_date",
   "tax_id",
+  "accounting_dimensions_section",
+  "cost_center",
+  "dimension_col_break",
+  "project",
   "contact_info",
   "customer_address",
   "address_display",
@@ -113,7 +117,6 @@
   "is_internal_customer",
   "represents_company",
   "inter_company_order_reference",
-  "project",
   "party_account_currency",
   "column_break_77",
   "source",
@@ -130,6 +133,7 @@
   "per_delivered",
   "column_break_81",
   "per_billed",
+  "per_picked",
   "billing_status",
   "sales_team_section_break",
   "sales_partner",
@@ -1355,6 +1359,8 @@
    "width": "50%"
   },
   {
+   "fetch_from": "sales_partner.commission_rate",
+   "fetch_if_empty": 1,
    "fieldname": "commission_rate",
    "fieldtype": "Float",
    "hide_days": 1,
@@ -1514,13 +1520,36 @@
    "fieldtype": "Currency",
    "label": "Amount Eligible for Commission",
    "read_only": 1
+  },
+  {
+   "fieldname": "per_picked",
+   "fieldtype": "Percent",
+   "label": "% Picked",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "accounting_dimensions_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Dimensions"
+  },
+  {
+   "fieldname": "cost_center",
+   "fieldtype": "Link",
+   "label": "Cost Center",
+   "options": "Cost Center"
+  },
+  {
+   "fieldname": "dimension_col_break",
+   "fieldtype": "Column Break"
   }
  ],
  "icon": "fa fa-file-text",
  "idx": 105,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-10-05 12:16:40.775704",
+ "modified": "2022-06-10 03:52:22.212953",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order",
@@ -1594,6 +1623,7 @@
  "show_name_in_global_search": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "timeline_field": "customer",
  "title_field": "customer_name",
  "track_changes": 1,
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index abbb3c9..8c03cb5 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -25,13 +25,15 @@
 from erpnext.selling.doctype.customer.customer import check_credit_limit
 from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
 from erpnext.stock.doctype.item.item import get_item_defaults
+from erpnext.stock.get_item_details import get_default_bom
 from erpnext.stock.stock_balance import get_reserved_qty, update_bin_qty
 
-form_grid_templates = {
-	"items": "templates/form_grid/item_grid.html"
-}
+form_grid_templates = {"items": "templates/form_grid/item_grid.html"}
 
-class WarehouseRequired(frappe.ValidationError): pass
+
+class WarehouseRequired(frappe.ValidationError):
+	pass
+
 
 class SalesOrder(SellingController):
 	def __init__(self, *args, **kwargs):
@@ -48,20 +50,26 @@
 		self.validate_warehouse()
 		self.validate_drop_ship()
 		self.validate_serial_no_based_delivery()
-		validate_inter_company_party(self.doctype, self.customer, self.company, self.inter_company_order_reference)
+		validate_inter_company_party(
+			self.doctype, self.customer, self.company, self.inter_company_order_reference
+		)
 
 		if self.coupon_code:
 			from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code
+
 			validate_coupon_code(self.coupon_code)
 
 		from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
+
 		make_packing_list(self)
 
 		self.validate_with_previous_doc()
 		self.set_status()
 
-		if not self.billing_status: self.billing_status = 'Not Billed'
-		if not self.delivery_status: self.delivery_status = 'Not Delivered'
+		if not self.billing_status:
+			self.billing_status = "Not Billed"
+		if not self.delivery_status:
+			self.delivery_status = "Not Delivered"
 
 		self.reset_default_field_value("set_warehouse", "items", "warehouse")
 
@@ -70,55 +78,82 @@
 		if self.po_date and not self.skip_delivery_note:
 			for d in self.get("items"):
 				if d.delivery_date and getdate(self.po_date) > getdate(d.delivery_date):
-					frappe.throw(_("Row #{0}: Expected Delivery Date cannot be before Purchase Order Date")
-						.format(d.idx))
+					frappe.throw(
+						_("Row #{0}: Expected Delivery Date cannot be before Purchase Order Date").format(d.idx)
+					)
 
 		if self.po_no and self.customer and not self.skip_delivery_note:
-			so = frappe.db.sql("select name from `tabSales Order` \
+			so = frappe.db.sql(
+				"select name from `tabSales Order` \
 				where ifnull(po_no, '') = %s and name != %s and docstatus < 2\
-				and customer = %s", (self.po_no, self.name, self.customer))
-			if so and so[0][0] and not cint(frappe.db.get_single_value("Selling Settings",
-				"allow_against_multiple_purchase_orders")):
-				frappe.msgprint(_("Warning: Sales Order {0} already exists against Customer's Purchase Order {1}").format(so[0][0], self.po_no))
+				and customer = %s",
+				(self.po_no, self.name, self.customer),
+			)
+			if (
+				so
+				and so[0][0]
+				and not cint(
+					frappe.db.get_single_value("Selling Settings", "allow_against_multiple_purchase_orders")
+				)
+			):
+				frappe.msgprint(
+					_("Warning: Sales Order {0} already exists against Customer's Purchase Order {1}").format(
+						so[0][0], self.po_no
+					)
+				)
 
 	def validate_for_items(self):
-		for d in self.get('items'):
+		for d in self.get("items"):
 
 			# used for production plan
 			d.transaction_date = self.transaction_date
 
-			tot_avail_qty = frappe.db.sql("select projected_qty from `tabBin` \
-				where item_code = %s and warehouse = %s", (d.item_code, d.warehouse))
+			tot_avail_qty = frappe.db.sql(
+				"select projected_qty from `tabBin` \
+				where item_code = %s and warehouse = %s",
+				(d.item_code, d.warehouse),
+			)
 			d.projected_qty = tot_avail_qty and flt(tot_avail_qty[0][0]) or 0
 
 	def product_bundle_has_stock_item(self, product_bundle):
 		"""Returns true if product bundle has stock item"""
-		ret = len(frappe.db.sql("""select i.name from tabItem i, `tabProduct Bundle Item` pbi
-			where pbi.parent = %s and pbi.item_code = i.name and i.is_stock_item = 1""", product_bundle))
+		ret = len(
+			frappe.db.sql(
+				"""select i.name from tabItem i, `tabProduct Bundle Item` pbi
+			where pbi.parent = %s and pbi.item_code = i.name and i.is_stock_item = 1""",
+				product_bundle,
+			)
+		)
 		return ret
 
 	def validate_sales_mntc_quotation(self):
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if d.prevdoc_docname:
-				res = frappe.db.sql("select name from `tabQuotation` where name=%s and order_type = %s",
-					(d.prevdoc_docname, self.order_type))
+				res = frappe.db.sql(
+					"select name from `tabQuotation` where name=%s and order_type = %s",
+					(d.prevdoc_docname, self.order_type),
+				)
 				if not res:
-					frappe.msgprint(_("Quotation {0} not of type {1}")
-						.format(d.prevdoc_docname, self.order_type))
+					frappe.msgprint(_("Quotation {0} not of type {1}").format(d.prevdoc_docname, self.order_type))
 
 	def validate_delivery_date(self):
-		if self.order_type == 'Sales' and not self.skip_delivery_note:
+		if self.order_type == "Sales" and not self.skip_delivery_note:
 			delivery_date_list = [d.delivery_date for d in self.get("items") if d.delivery_date]
 			max_delivery_date = max(delivery_date_list) if delivery_date_list else None
-			if (max_delivery_date and not self.delivery_date) or (max_delivery_date and getdate(self.delivery_date) != getdate(max_delivery_date)):
+			if (max_delivery_date and not self.delivery_date) or (
+				max_delivery_date and getdate(self.delivery_date) != getdate(max_delivery_date)
+			):
 				self.delivery_date = max_delivery_date
 			if self.delivery_date:
 				for d in self.get("items"):
 					if not d.delivery_date:
 						d.delivery_date = self.delivery_date
 					if getdate(self.transaction_date) > getdate(d.delivery_date):
-						frappe.msgprint(_("Expected Delivery Date should be after Sales Order Date"),
-							indicator='orange', title=_('Warning'))
+						frappe.msgprint(
+							_("Expected Delivery Date should be after Sales Order Date"),
+							indicator="orange",
+							title=_("Warning"),
+						)
 			else:
 				frappe.throw(_("Please enter Delivery Date"))
 
@@ -126,47 +161,56 @@
 
 	def validate_proj_cust(self):
 		if self.project and self.customer_name:
-			res = frappe.db.sql("""select name from `tabProject` where name = %s
+			res = frappe.db.sql(
+				"""select name from `tabProject` where name = %s
 				and (customer = %s or ifnull(customer,'')='')""",
-					(self.project, self.customer))
+				(self.project, self.customer),
+			)
 			if not res:
-				frappe.throw(_("Customer {0} does not belong to project {1}").format(self.customer, self.project))
+				frappe.throw(
+					_("Customer {0} does not belong to project {1}").format(self.customer, self.project)
+				)
 
 	def validate_warehouse(self):
 		super(SalesOrder, self).validate_warehouse()
 
 		for d in self.get("items"):
-			if (frappe.get_cached_value("Item", d.item_code, "is_stock_item") == 1 or
-				(self.has_product_bundle(d.item_code) and self.product_bundle_has_stock_item(d.item_code))) \
-				and not d.warehouse and not cint(d.delivered_by_supplier):
-				frappe.throw(_("Delivery warehouse required for stock item {0}").format(d.item_code),
-					WarehouseRequired)
+			if (
+				(
+					frappe.get_cached_value("Item", d.item_code, "is_stock_item") == 1
+					or (self.has_product_bundle(d.item_code) and self.product_bundle_has_stock_item(d.item_code))
+				)
+				and not d.warehouse
+				and not cint(d.delivered_by_supplier)
+			):
+				frappe.throw(
+					_("Delivery warehouse required for stock item {0}").format(d.item_code), WarehouseRequired
+				)
 
 	def validate_with_previous_doc(self):
-		super(SalesOrder, self).validate_with_previous_doc({
-			"Quotation": {
-				"ref_dn_field": "prevdoc_docname",
-				"compare_fields": [["company", "="]]
-			}
-		})
-
+		super(SalesOrder, self).validate_with_previous_doc(
+			{"Quotation": {"ref_dn_field": "prevdoc_docname", "compare_fields": [["company", "="]]}}
+		)
 
 	def update_enquiry_status(self, prevdoc, flag):
-		enq = frappe.db.sql("select t2.prevdoc_docname from `tabQuotation` t1, `tabQuotation Item` t2 where t2.parent = t1.name and t1.name=%s", prevdoc)
+		enq = frappe.db.sql(
+			"select t2.prevdoc_docname from `tabQuotation` t1, `tabQuotation Item` t2 where t2.parent = t1.name and t1.name=%s",
+			prevdoc,
+		)
 		if enq:
-			frappe.db.sql("update `tabOpportunity` set status = %s where name=%s",(flag,enq[0][0]))
+			frappe.db.sql("update `tabOpportunity` set status = %s where name=%s", (flag, enq[0][0]))
 
 	def update_prevdoc_status(self, flag=None):
 		for quotation in set(d.prevdoc_docname for d in self.get("items")):
 			if quotation:
 				doc = frappe.get_doc("Quotation", quotation)
-				if doc.docstatus==2:
+				if doc.docstatus == 2:
 					frappe.throw(_("Quotation {0} is cancelled").format(quotation))
 
 				doc.set_status(update=True)
 
 	def validate_drop_ship(self):
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if d.delivered_by_supplier and not d.supplier:
 				frappe.throw(_("Row #{0}: Set Supplier for item {1}").format(d.idx, d.item_code))
 
@@ -174,41 +218,47 @@
 		self.check_credit_limit()
 		self.update_reserved_qty()
 
-		frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, self.company, self.base_grand_total, self)
+		frappe.get_doc("Authorization Control").validate_approving_authority(
+			self.doctype, self.company, self.base_grand_total, self
+		)
 		self.update_project()
-		self.update_prevdoc_status('submit')
+		self.update_prevdoc_status("submit")
 
 		self.update_blanket_order()
 
 		update_linked_doc(self.doctype, self.name, self.inter_company_order_reference)
 		if self.coupon_code:
 			from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count
-			update_coupon_code_count(self.coupon_code,'used')
+
+			update_coupon_code_count(self.coupon_code, "used")
 
 	def on_cancel(self):
-		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+		self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
 		super(SalesOrder, self).on_cancel()
 
 		# Cannot cancel closed SO
-		if self.status == 'Closed':
+		if self.status == "Closed":
 			frappe.throw(_("Closed order cannot be cancelled. Unclose to cancel."))
 
 		self.check_nextdoc_docstatus()
 		self.update_reserved_qty()
 		self.update_project()
-		self.update_prevdoc_status('cancel')
+		self.update_prevdoc_status("cancel")
 
-		frappe.db.set(self, 'status', 'Cancelled')
+		frappe.db.set(self, "status", "Cancelled")
 
 		self.update_blanket_order()
 
 		unlink_inter_company_doc(self.doctype, self.name, self.inter_company_order_reference)
 		if self.coupon_code:
 			from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count
-			update_coupon_code_count(self.coupon_code,'cancelled')
+
+			update_coupon_code_count(self.coupon_code, "cancelled")
 
 	def update_project(self):
-		if frappe.db.get_single_value('Selling Settings', 'sales_update_frequency') != "Each Transaction":
+		if (
+			frappe.db.get_single_value("Selling Settings", "sales_update_frequency") != "Each Transaction"
+		):
 			return
 
 		if self.project:
@@ -219,26 +269,34 @@
 	def check_credit_limit(self):
 		# if bypass credit limit check is set to true (1) at sales order level,
 		# then we need not to check credit limit and vise versa
-		if not cint(frappe.db.get_value("Customer Credit Limit",
-			{'parent': self.customer, 'parenttype': 'Customer', 'company': self.company},
-			"bypass_credit_limit_check")):
+		if not cint(
+			frappe.db.get_value(
+				"Customer Credit Limit",
+				{"parent": self.customer, "parenttype": "Customer", "company": self.company},
+				"bypass_credit_limit_check",
+			)
+		):
 			check_credit_limit(self.customer, self.company)
 
 	def check_nextdoc_docstatus(self):
-		linked_invoices = frappe.db.sql_list("""select distinct t1.name
+		linked_invoices = frappe.db.sql_list(
+			"""select distinct t1.name
 			from `tabSales Invoice` t1,`tabSales Invoice Item` t2
 			where t1.name = t2.parent and t2.sales_order = %s and t1.docstatus = 0""",
-			self.name)
+			self.name,
+		)
 
 		if linked_invoices:
 			linked_invoices = [get_link_to_form("Sales Invoice", si) for si in linked_invoices]
-			frappe.throw(_("Sales Invoice {0} must be deleted before cancelling this Sales Order")
-				.format(", ".join(linked_invoices)))
+			frappe.throw(
+				_("Sales Invoice {0} must be deleted before cancelling this Sales Order").format(
+					", ".join(linked_invoices)
+				)
+			)
 
 	def check_modified_date(self):
 		mod_db = frappe.db.get_value("Sales Order", self.name, "modified")
-		date_diff = frappe.db.sql("select TIMEDIFF('%s', '%s')" %
-			( mod_db, cstr(self.modified)))
+		date_diff = frappe.db.sql("select TIMEDIFF('%s', '%s')" % (mod_db, cstr(self.modified)))
 		if date_diff and date_diff[0][0]:
 			frappe.throw(_("{0} {1} has been modified. Please refresh.").format(self.doctype, self.name))
 
@@ -252,10 +310,15 @@
 	def update_reserved_qty(self, so_item_rows=None):
 		"""update requested qty (before ordered_qty is updated)"""
 		item_wh_list = []
+
 		def _valid_for_reserve(item_code, warehouse):
-			if item_code and warehouse and [item_code, warehouse] not in item_wh_list \
-				and frappe.get_cached_value("Item", item_code, "is_stock_item"):
-					item_wh_list.append([item_code, warehouse])
+			if (
+				item_code
+				and warehouse
+				and [item_code, warehouse] not in item_wh_list
+				and frappe.get_cached_value("Item", item_code, "is_stock_item")
+			):
+				item_wh_list.append([item_code, warehouse])
 
 		for d in self.get("items"):
 			if (not so_item_rows or d.name in so_item_rows) and not d.delivered_by_supplier:
@@ -267,9 +330,7 @@
 					_valid_for_reserve(d.item_code, d.warehouse)
 
 		for item_code, warehouse in item_wh_list:
-			update_bin_qty(item_code, warehouse, {
-				"reserved_qty": get_reserved_qty(item_code, warehouse)
-			})
+			update_bin_qty(item_code, warehouse, {"reserved_qty": get_reserved_qty(item_code, warehouse)})
 
 	def on_update(self):
 		pass
@@ -286,13 +347,18 @@
 
 		for item in self.items:
 			if item.supplier:
-				supplier = frappe.db.get_value("Sales Order Item", {"parent": self.name, "item_code": item.item_code},
-					"supplier")
+				supplier = frappe.db.get_value(
+					"Sales Order Item", {"parent": self.name, "item_code": item.item_code}, "supplier"
+				)
 				if item.ordered_qty > 0.0 and item.supplier != supplier:
-					exc_list.append(_("Row #{0}: Not allowed to change Supplier as Purchase Order already exists").format(item.idx))
+					exc_list.append(
+						_("Row #{0}: Not allowed to change Supplier as Purchase Order already exists").format(
+							item.idx
+						)
+					)
 
 		if exc_list:
-			frappe.throw('\n'.join(exc_list))
+			frappe.throw("\n".join(exc_list))
 
 	def update_delivery_status(self):
 		"""Update delivery status from Purchase Order for drop shipping"""
@@ -300,13 +366,16 @@
 
 		for item in self.items:
 			if item.delivered_by_supplier:
-				item_delivered_qty  = frappe.db.sql("""select sum(qty)
+				item_delivered_qty = frappe.db.sql(
+					"""select sum(qty)
 					from `tabPurchase Order Item` poi, `tabPurchase Order` po
 					where poi.sales_order_item = %s
 						and poi.item_code = %s
 						and poi.parent = po.name
 						and po.docstatus = 1
-						and po.status = 'Delivered'""", (item.name, item.item_code))
+						and po.status = 'Delivered'""",
+					(item.name, item.item_code),
+				)
 
 				item_delivered_qty = item_delivered_qty[0][0] if item_delivered_qty else 0
 				item.db_set("delivered_qty", flt(item_delivered_qty), update_modified=False)
@@ -315,9 +384,17 @@
 			tot_qty += item.qty
 
 		if tot_qty != 0:
-			self.db_set("per_delivered", flt(delivered_qty/tot_qty) * 100,
-				update_modified=False)
+			self.db_set("per_delivered", flt(delivered_qty / tot_qty) * 100, update_modified=False)
 
+	def update_picking_status(self):
+		total_picked_qty = 0.0
+		total_qty = 0.0
+		for so_item in self.items:
+			total_picked_qty += flt(so_item.picked_qty)
+			total_qty += flt(so_item.stock_qty)
+		per_picked = total_picked_qty / total_qty * 100
+
+		self.db_set("per_picked", flt(per_picked), update_modified=False)
 
 	def set_indicator(self):
 		"""Set indicator for portal"""
@@ -335,49 +412,50 @@
 
 	@frappe.whitelist()
 	def get_work_order_items(self, for_raw_material_request=0):
-		'''Returns items with BOM that already do not have a linked work order'''
+		"""Returns items with BOM that already do not have a linked work order"""
 		items = []
 		item_codes = [i.item_code for i in self.items]
-		product_bundle_parents = [pb.new_item_code for pb in frappe.get_all("Product Bundle", {"new_item_code": ["in", item_codes]}, ["new_item_code"])]
+		product_bundle_parents = [
+			pb.new_item_code
+			for pb in frappe.get_all(
+				"Product Bundle", {"new_item_code": ["in", item_codes]}, ["new_item_code"]
+			)
+		]
 
 		for table in [self.items, self.packed_items]:
 			for i in table:
-				bom = get_default_bom_item(i.item_code)
-				stock_qty = i.qty if i.doctype == 'Packed Item' else i.stock_qty
+				bom = get_default_bom(i.item_code)
+				stock_qty = i.qty if i.doctype == "Packed Item" else i.stock_qty
+
 				if not for_raw_material_request:
-					total_work_order_qty = flt(frappe.db.sql('''select sum(qty) from `tabWork Order`
-						where production_item=%s and sales_order=%s and sales_order_item = %s and docstatus<2''', (i.item_code, self.name, i.name))[0][0])
+					total_work_order_qty = flt(
+						frappe.db.sql(
+							"""select sum(qty) from `tabWork Order`
+						where production_item=%s and sales_order=%s and sales_order_item = %s and docstatus<2""",
+							(i.item_code, self.name, i.name),
+						)[0][0]
+					)
 					pending_qty = stock_qty - total_work_order_qty
 				else:
 					pending_qty = stock_qty
 
 				if pending_qty and i.item_code not in product_bundle_parents:
-					if bom:
-						items.append(dict(
-							name= i.name,
-							item_code= i.item_code,
-							description= i.description,
-							bom = bom,
-							warehouse = i.warehouse,
-							pending_qty = pending_qty,
-							required_qty = pending_qty if for_raw_material_request else 0,
-							sales_order_item = i.name
-						))
-					else:
-						items.append(dict(
-							name= i.name,
-							item_code= i.item_code,
-							description= i.description,
-							bom = '',
-							warehouse = i.warehouse,
-							pending_qty = pending_qty,
-							required_qty = pending_qty if for_raw_material_request else 0,
-							sales_order_item = i.name
-						))
+					items.append(
+						dict(
+							name=i.name,
+							item_code=i.item_code,
+							description=i.description,
+							bom=bom or "",
+							warehouse=i.warehouse,
+							pending_qty=pending_qty,
+							required_qty=pending_qty if for_raw_material_request else 0,
+							sales_order_item=i.name,
+						)
+					)
+
 		return items
 
 	def on_recurring(self, reference_doc, auto_repeat_doc):
-
 		def _get_delivery_date(ref_doc_delivery_date, red_doc_transaction_date, transaction_date):
 			delivery_date = auto_repeat_doc.get_next_schedule_date(schedule_date=ref_doc_delivery_date)
 
@@ -387,15 +465,26 @@
 
 			return delivery_date
 
-		self.set("delivery_date", _get_delivery_date(reference_doc.delivery_date,
-			reference_doc.transaction_date, self.transaction_date ))
+		self.set(
+			"delivery_date",
+			_get_delivery_date(
+				reference_doc.delivery_date, reference_doc.transaction_date, self.transaction_date
+			),
+		)
 
 		for d in self.get("items"):
-			reference_delivery_date = frappe.db.get_value("Sales Order Item",
-				{"parent": reference_doc.name, "item_code": d.item_code, "idx": d.idx}, "delivery_date")
+			reference_delivery_date = frappe.db.get_value(
+				"Sales Order Item",
+				{"parent": reference_doc.name, "item_code": d.item_code, "idx": d.idx},
+				"delivery_date",
+			)
 
-			d.set("delivery_date", _get_delivery_date(reference_delivery_date,
-				reference_doc.transaction_date, self.transaction_date))
+			d.set(
+				"delivery_date",
+				_get_delivery_date(
+					reference_delivery_date, reference_doc.transaction_date, self.transaction_date
+				),
+			)
 
 	def validate_serial_no_based_delivery(self):
 		reserved_items = []
@@ -403,32 +492,52 @@
 		for item in self.items:
 			if item.ensure_delivery_based_on_produced_serial_no:
 				if item.item_code in normal_items:
-					frappe.throw(_("Cannot ensure delivery by Serial No as Item {0} is added with and without Ensure Delivery by Serial No.").format(item.item_code))
+					frappe.throw(
+						_(
+							"Cannot ensure delivery by Serial No as Item {0} is added with and without Ensure Delivery by Serial No."
+						).format(item.item_code)
+					)
 				if item.item_code not in reserved_items:
 					if not frappe.get_cached_value("Item", item.item_code, "has_serial_no"):
-						frappe.throw(_("Item {0} has no Serial No. Only serilialized items can have delivery based on Serial No").format(item.item_code))
+						frappe.throw(
+							_(
+								"Item {0} has no Serial No. Only serilialized items can have delivery based on Serial No"
+							).format(item.item_code)
+						)
 					if not frappe.db.exists("BOM", {"item": item.item_code, "is_active": 1}):
-						frappe.throw(_("No active BOM found for item {0}. Delivery by Serial No cannot be ensured").format(item.item_code))
+						frappe.throw(
+							_("No active BOM found for item {0}. Delivery by Serial No cannot be ensured").format(
+								item.item_code
+							)
+						)
 				reserved_items.append(item.item_code)
 			else:
 				normal_items.append(item.item_code)
 
-			if not item.ensure_delivery_based_on_produced_serial_no and \
-				item.item_code in reserved_items:
-				frappe.throw(_("Cannot ensure delivery by Serial No as Item {0} is added with and without Ensure Delivery by Serial No.").format(item.item_code))
+			if not item.ensure_delivery_based_on_produced_serial_no and item.item_code in reserved_items:
+				frappe.throw(
+					_(
+						"Cannot ensure delivery by Serial No as Item {0} is added with and without Ensure Delivery by Serial No."
+					).format(item.item_code)
+				)
+
 
 def get_list_context(context=None):
 	from erpnext.controllers.website_list_for_contact import get_list_context
+
 	list_context = get_list_context(context)
-	list_context.update({
-		'show_sidebar': True,
-		'show_search': True,
-		'no_breadcrumbs': True,
-		'title': _('Orders'),
-	})
+	list_context.update(
+		{
+			"show_sidebar": True,
+			"show_search": True,
+			"no_breadcrumbs": True,
+			"title": _("Orders"),
+		}
+	)
 
 	return list_context
 
+
 @frappe.whitelist()
 def close_or_unclose_sales_orders(names, status):
 	if not frappe.has_permission("Sales Order", "write"):
@@ -439,23 +548,32 @@
 		so = frappe.get_doc("Sales Order", name)
 		if so.docstatus == 1:
 			if status == "Closed":
-				if so.status not in ("Cancelled", "Closed") and (so.per_delivered < 100 or so.per_billed < 100):
+				if so.status not in ("Cancelled", "Closed") and (
+					so.per_delivered < 100 or so.per_billed < 100
+				):
 					so.update_status(status)
 			else:
 				if so.status == "Closed":
-					so.update_status('Draft')
+					so.update_status("Draft")
 			so.update_blanket_order()
 
 	frappe.local.message_log = []
 
+
 def get_requested_item_qty(sales_order):
-	return frappe._dict(frappe.db.sql("""
+	return frappe._dict(
+		frappe.db.sql(
+			"""
 		select sales_order_item, sum(qty)
 		from `tabMaterial Request Item`
 		where docstatus = 1
 			and sales_order = %s
 		group by sales_order_item
-	""", sales_order))
+	""",
+			sales_order,
+		)
+	)
+
 
 @frappe.whitelist()
 def make_material_request(source_name, target_doc=None):
@@ -468,71 +586,71 @@
 		target.qty = qty - requested_item_qty.get(source.name, 0)
 		target.stock_qty = flt(target.qty) * flt(target.conversion_factor)
 
-	doc = get_mapped_doc("Sales Order", source_name, {
-		"Sales Order": {
-			"doctype": "Material Request",
-			"validation": {
-				"docstatus": ["=", 1]
-			}
-		},
-		"Packed Item": {
-			"doctype": "Material Request Item",
-			"field_map": {
-				"parent": "sales_order",
-				"uom": "stock_uom"
+	doc = get_mapped_doc(
+		"Sales Order",
+		source_name,
+		{
+			"Sales Order": {"doctype": "Material Request", "validation": {"docstatus": ["=", 1]}},
+			"Packed Item": {
+				"doctype": "Material Request Item",
+				"field_map": {"parent": "sales_order", "uom": "stock_uom"},
+				"postprocess": update_item,
 			},
-			"postprocess": update_item
-		},
-		"Sales Order Item": {
-			"doctype": "Material Request Item",
-			"field_map": {
-				"name": "sales_order_item",
-				"parent": "sales_order"
+			"Sales Order Item": {
+				"doctype": "Material Request Item",
+				"field_map": {"name": "sales_order_item", "parent": "sales_order"},
+				"condition": lambda doc: not frappe.db.exists("Product Bundle", doc.item_code)
+				and doc.stock_qty > requested_item_qty.get(doc.name, 0),
+				"postprocess": update_item,
 			},
-			"condition": lambda doc: not frappe.db.exists('Product Bundle', doc.item_code) and doc.stock_qty > requested_item_qty.get(doc.name, 0),
-			"postprocess": update_item
-		}
-	}, target_doc)
+		},
+		target_doc,
+	)
 
 	return doc
 
+
 @frappe.whitelist()
 def make_project(source_name, target_doc=None):
 	def postprocess(source, doc):
 		doc.project_type = "External"
 		doc.project_name = source.name
 
-	doc = get_mapped_doc("Sales Order", source_name, {
-		"Sales Order": {
-			"doctype": "Project",
-			"validation": {
-				"docstatus": ["=", 1]
+	doc = get_mapped_doc(
+		"Sales Order",
+		source_name,
+		{
+			"Sales Order": {
+				"doctype": "Project",
+				"validation": {"docstatus": ["=", 1]},
+				"field_map": {
+					"name": "sales_order",
+					"base_grand_total": "estimated_costing",
+				},
 			},
-			"field_map":{
-				"name" : "sales_order",
-				"base_grand_total" : "estimated_costing",
-			}
 		},
-	}, target_doc, postprocess)
+		target_doc,
+		postprocess,
+	)
 
 	return doc
 
+
 @frappe.whitelist()
 def make_delivery_note(source_name, target_doc=None, skip_item_mapping=False):
 	def set_missing_values(source, target):
-		target.ignore_pricing_rule = 1
 		target.run_method("set_missing_values")
 		target.run_method("set_po_nos")
 		target.run_method("calculate_taxes_and_totals")
 
 		if source.company_address:
-			target.update({'company_address': source.company_address})
+			target.update({"company_address": source.company_address})
 		else:
 			# set company address
 			target.update(get_company_address(target.company))
 
 		if target.company_address:
-			target.update(get_fetch_values("Delivery Note", 'company_address', target.company_address))
+			target.update(get_fetch_values("Delivery Note", "company_address", target.company_address))
 
 	def update_item(source, target, source_parent):
 		target.base_amount = (flt(source.qty) - flt(source.delivered_qty)) * flt(source.base_rate)
@@ -543,34 +661,26 @@
 		item_group = get_item_group_defaults(target.item_code, source_parent.company)
 
 		if item:
-			target.cost_center = frappe.db.get_value("Project", source_parent.project, "cost_center") \
-				or item.get("buying_cost_center") \
+			target.cost_center = (
+				frappe.db.get_value("Project", source_parent.project, "cost_center")
+				or item.get("buying_cost_center")
 				or item_group.get("buying_cost_center")
+			)
 
 	mapper = {
-		"Sales Order": {
-			"doctype": "Delivery Note",
-			"validation": {
-				"docstatus": ["=", 1]
-			}
-		},
-		"Sales Taxes and Charges": {
-			"doctype": "Sales Taxes and Charges",
-			"add_if_empty": True
-		},
-		"Sales Team": {
-			"doctype": "Sales Team",
-			"add_if_empty": True
-		}
+		"Sales Order": {"doctype": "Delivery Note", "validation": {"docstatus": ["=", 1]}},
+		"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True},
+		"Sales Team": {"doctype": "Sales Team", "add_if_empty": True},
 	}
 
 	if not skip_item_mapping:
+
 		def condition(doc):
 			# make_mapped_doc sets js `args` into `frappe.flags.args`
 			if frappe.flags.args and frappe.flags.args.delivery_dates:
 				if cstr(doc.delivery_date) not in frappe.flags.args.delivery_dates:
 					return False
-			return abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1
+			return abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier != 1
 
 		mapper["Sales Order Item"] = {
 			"doctype": "Delivery Note Item",
@@ -580,36 +690,38 @@
 				"parent": "against_sales_order",
 			},
 			"postprocess": update_item,
-			"condition": condition
+			"condition": condition,
 		}
 
 	target_doc = get_mapped_doc("Sales Order", source_name, mapper, target_doc, set_missing_values)
 
+	target_doc.set_onload("ignore_price_list", True)
+
 	return target_doc
 
+
 @frappe.whitelist()
 def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
 	def postprocess(source, target):
 		set_missing_values(source, target)
-		#Get the advance paid Journal Entries in Sales Invoice Advance
+		# Get the advance paid Journal Entries in Sales Invoice Advance
 		if target.get("allocate_advances_automatically"):
 			target.set_advances()
 
 	def set_missing_values(source, target):
-		target.ignore_pricing_rule = 1
 		target.flags.ignore_permissions = True
 		target.run_method("set_missing_values")
 		target.run_method("set_po_nos")
 		target.run_method("calculate_taxes_and_totals")
 
 		if source.company_address:
-			target.update({'company_address': source.company_address})
+			target.update({"company_address": source.company_address})
 		else:
 			# set company address
 			target.update(get_company_address(target.company))
 
 		if target.company_address:
-			target.update(get_fetch_values("Sales Invoice", 'company_address', target.company_address))
+			target.update(get_fetch_values("Sales Invoice", "company_address", target.company_address))
 
 		# set the redeem loyalty points if provided via shopping cart
 		if source.loyalty_points and source.order_type == "Shopping Cart":
@@ -618,106 +730,117 @@
 	def update_item(source, target, source_parent):
 		target.amount = flt(source.amount) - flt(source.billed_amt)
 		target.base_amount = target.amount * flt(source_parent.conversion_rate)
-		target.qty = target.amount / flt(source.rate) if (source.rate and source.billed_amt) else source.qty - source.returned_qty
+		target.qty = (
+			target.amount / flt(source.rate)
+			if (source.rate and source.billed_amt)
+			else source.qty - source.returned_qty
+		)
 
 		if source_parent.project:
 			target.cost_center = frappe.db.get_value("Project", source_parent.project, "cost_center")
 		if target.item_code:
 			item = get_item_defaults(target.item_code, source_parent.company)
 			item_group = get_item_group_defaults(target.item_code, source_parent.company)
-			cost_center = item.get("selling_cost_center") \
-				or item_group.get("selling_cost_center")
+			cost_center = item.get("selling_cost_center") or item_group.get("selling_cost_center")
 
 			if cost_center:
 				target.cost_center = cost_center
 
-	doclist = get_mapped_doc("Sales Order", source_name, {
-		"Sales Order": {
-			"doctype": "Sales Invoice",
-			"field_map": {
-				"party_account_currency": "party_account_currency",
-				"payment_terms_template": "payment_terms_template"
+	doclist = get_mapped_doc(
+		"Sales Order",
+		source_name,
+		{
+			"Sales Order": {
+				"doctype": "Sales Invoice",
+				"field_map": {
+					"party_account_currency": "party_account_currency",
+					"payment_terms_template": "payment_terms_template",
+				},
+				"field_no_map": ["payment_terms_template"],
+				"validation": {"docstatus": ["=", 1]},
 			},
-			"field_no_map": ["payment_terms_template"],
-			"validation": {
-				"docstatus": ["=", 1]
-			}
-		},
-		"Sales Order Item": {
-			"doctype": "Sales Invoice Item",
-			"field_map": {
-				"name": "so_detail",
-				"parent": "sales_order",
+			"Sales Order Item": {
+				"doctype": "Sales Invoice Item",
+				"field_map": {
+					"name": "so_detail",
+					"parent": "sales_order",
+				},
+				"postprocess": update_item,
+				"condition": lambda doc: doc.qty
+				and (doc.base_amount == 0 or abs(doc.billed_amt) < abs(doc.amount)),
 			},
-			"postprocess": update_item,
-			"condition": lambda doc: doc.qty and (doc.base_amount==0 or abs(doc.billed_amt) < abs(doc.amount))
+			"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True},
+			"Sales Team": {"doctype": "Sales Team", "add_if_empty": True},
 		},
-		"Sales Taxes and Charges": {
-			"doctype": "Sales Taxes and Charges",
-			"add_if_empty": True
-		},
-		"Sales Team": {
-			"doctype": "Sales Team",
-			"add_if_empty": True
-		}
-	}, target_doc, postprocess, ignore_permissions=ignore_permissions)
+		target_doc,
+		postprocess,
+		ignore_permissions=ignore_permissions,
+	)
 
-	automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms'))
+	automatically_fetch_payment_terms = cint(
+		frappe.db.get_single_value("Accounts Settings", "automatically_fetch_payment_terms")
+	)
 	if automatically_fetch_payment_terms:
 		doclist.set_payment_schedule()
 
+	doclist.set_onload("ignore_price_list", True)
+
 	return doclist
 
+
 @frappe.whitelist()
 def make_maintenance_schedule(source_name, target_doc=None):
-	maint_schedule = frappe.db.sql("""select t1.name
+	maint_schedule = frappe.db.sql(
+		"""select t1.name
 		from `tabMaintenance Schedule` t1, `tabMaintenance Schedule Item` t2
-		where t2.parent=t1.name and t2.sales_order=%s and t1.docstatus=1""", source_name)
+		where t2.parent=t1.name and t2.sales_order=%s and t1.docstatus=1""",
+		source_name,
+	)
 
 	if not maint_schedule:
-		doclist = get_mapped_doc("Sales Order", source_name, {
-			"Sales Order": {
-				"doctype": "Maintenance Schedule",
-				"validation": {
-					"docstatus": ["=", 1]
-				}
+		doclist = get_mapped_doc(
+			"Sales Order",
+			source_name,
+			{
+				"Sales Order": {"doctype": "Maintenance Schedule", "validation": {"docstatus": ["=", 1]}},
+				"Sales Order Item": {
+					"doctype": "Maintenance Schedule Item",
+					"field_map": {"parent": "sales_order"},
+				},
 			},
-			"Sales Order Item": {
-				"doctype": "Maintenance Schedule Item",
-				"field_map": {
-					"parent": "sales_order"
-				}
-			}
-		}, target_doc)
+			target_doc,
+		)
 
 		return doclist
 
+
 @frappe.whitelist()
 def make_maintenance_visit(source_name, target_doc=None):
-	visit = frappe.db.sql("""select t1.name
+	visit = frappe.db.sql(
+		"""select t1.name
 		from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2
 		where t2.parent=t1.name and t2.prevdoc_docname=%s
-		and t1.docstatus=1 and t1.completion_status='Fully Completed'""", source_name)
+		and t1.docstatus=1 and t1.completion_status='Fully Completed'""",
+		source_name,
+	)
 
 	if not visit:
-		doclist = get_mapped_doc("Sales Order", source_name, {
-			"Sales Order": {
-				"doctype": "Maintenance Visit",
-				"validation": {
-					"docstatus": ["=", 1]
-				}
+		doclist = get_mapped_doc(
+			"Sales Order",
+			source_name,
+			{
+				"Sales Order": {"doctype": "Maintenance Visit", "validation": {"docstatus": ["=", 1]}},
+				"Sales Order Item": {
+					"doctype": "Maintenance Visit Purpose",
+					"field_map": {"parent": "prevdoc_docname", "parenttype": "prevdoc_doctype"},
+				},
 			},
-			"Sales Order Item": {
-				"doctype": "Maintenance Visit Purpose",
-				"field_map": {
-					"parent": "prevdoc_docname",
-					"parenttype": "prevdoc_doctype"
-				}
-			}
-		}, target_doc)
+			target_doc,
+		)
 
 		return doclist
 
+
 @frappe.whitelist()
 def get_events(start, end, filters=None):
 	"""Returns events for Gantt / Calendar view rendering.
@@ -727,9 +850,11 @@
 	:param filters: Filters (JSON).
 	"""
 	from frappe.desk.calendar import get_event_conditions
+
 	conditions = get_event_conditions("Sales Order", filters)
 
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 		select
 			distinct `tabSales Order`.name, `tabSales Order`.customer_name, `tabSales Order`.status,
 			`tabSales Order`.delivery_status, `tabSales Order`.billing_status,
@@ -742,16 +867,21 @@
 			and (`tabSales Order Item`.delivery_date between %(start)s and %(end)s)
 			and `tabSales Order`.docstatus < 2
 			{conditions}
-		""".format(conditions=conditions), {
-			"start": start,
-			"end": end
-		}, as_dict=True, update={"allDay": 0})
+		""".format(
+			conditions=conditions
+		),
+		{"start": start, "end": end},
+		as_dict=True,
+		update={"allDay": 0},
+	)
 	return data
 
+
 @frappe.whitelist()
 def make_purchase_order_for_default_supplier(source_name, selected_items=None, target_doc=None):
 	"""Creates Purchase Order for each Supplier. Returns a list of doc objects."""
-	if not selected_items: return
+	if not selected_items:
+		return
 
 	if isinstance(selected_items, str):
 		selected_items = json.loads(selected_items)
@@ -767,7 +897,7 @@
 		if default_price_list:
 			target.buying_price_list = default_price_list
 
-		if any( item.delivered_by_supplier==1 for item in source.items):
+		if any(item.delivered_by_supplier == 1 for item in source.items):
 			if source.shipping_address_name:
 				target.shipping_address = source.shipping_address_name
 				target.shipping_address_display = source.shipping_address
@@ -790,59 +920,67 @@
 	def update_item(source, target, source_parent):
 		target.schedule_date = source.delivery_date
 		target.qty = flt(source.qty) - (flt(source.ordered_qty) / flt(source.conversion_factor))
-		target.stock_qty = (flt(source.stock_qty) - flt(source.ordered_qty))
+		target.stock_qty = flt(source.stock_qty) - flt(source.ordered_qty)
 		target.project = source_parent.project
 
-	suppliers = [item.get('supplier') for item in selected_items if item.get('supplier')]
-	suppliers = list(dict.fromkeys(suppliers)) # remove duplicates while preserving order
+	suppliers = [item.get("supplier") for item in selected_items if item.get("supplier")]
+	suppliers = list(dict.fromkeys(suppliers))  # remove duplicates while preserving order
 
-	items_to_map = [item.get('item_code') for item in selected_items if item.get('item_code')]
+	items_to_map = [item.get("item_code") for item in selected_items if item.get("item_code")]
 	items_to_map = list(set(items_to_map))
 
 	if not suppliers:
-		frappe.throw(_("Please set a Supplier against the Items to be considered in the Purchase Order."))
+		frappe.throw(
+			_("Please set a Supplier against the Items to be considered in the Purchase Order.")
+		)
 
 	purchase_orders = []
 	for supplier in suppliers:
-		doc = get_mapped_doc("Sales Order", source_name, {
-			"Sales Order": {
-				"doctype": "Purchase Order",
-				"field_no_map": [
-					"address_display",
-					"contact_display",
-					"contact_mobile",
-					"contact_email",
-					"contact_person",
-					"taxes_and_charges",
-					"shipping_address",
-					"terms"
-				],
-				"validation": {
-					"docstatus": ["=", 1]
-				}
+		doc = get_mapped_doc(
+			"Sales Order",
+			source_name,
+			{
+				"Sales Order": {
+					"doctype": "Purchase Order",
+					"field_no_map": [
+						"address_display",
+						"contact_display",
+						"contact_mobile",
+						"contact_email",
+						"contact_person",
+						"taxes_and_charges",
+						"shipping_address",
+						"terms",
+					],
+					"validation": {"docstatus": ["=", 1]},
+				},
+				"Sales Order Item": {
+					"doctype": "Purchase Order Item",
+					"field_map": [
+						["name", "sales_order_item"],
+						["parent", "sales_order"],
+						["stock_uom", "stock_uom"],
+						["uom", "uom"],
+						["conversion_factor", "conversion_factor"],
+						["delivery_date", "schedule_date"],
+					],
+					"field_no_map": [
+						"rate",
+						"price_list_rate",
+						"item_tax_template",
+						"discount_percentage",
+						"discount_amount",
+						"pricing_rules",
+					],
+					"postprocess": update_item,
+					"condition": lambda doc: doc.ordered_qty < doc.stock_qty
+					and doc.supplier == supplier
+					and doc.item_code in items_to_map,
+				},
 			},
-			"Sales Order Item": {
-				"doctype": "Purchase Order Item",
-				"field_map":  [
-					["name", "sales_order_item"],
-					["parent", "sales_order"],
-					["stock_uom", "stock_uom"],
-					["uom", "uom"],
-					["conversion_factor", "conversion_factor"],
-					["delivery_date", "schedule_date"]
-				],
-				"field_no_map": [
-					"rate",
-					"price_list_rate",
-					"item_tax_template",
-					"discount_percentage",
-					"discount_amount",
-					"pricing_rules"
-				],
-				"postprocess": update_item,
-				"condition": lambda doc: doc.ordered_qty < doc.stock_qty and doc.supplier == supplier and doc.item_code in items_to_map
-			}
-		}, target_doc, set_missing_values)
+			target_doc,
+			set_missing_values,
+		)
 
 		doc.insert()
 		frappe.db.commit()
@@ -850,14 +988,20 @@
 
 	return purchase_orders
 
+
 @frappe.whitelist()
 def make_purchase_order(source_name, selected_items=None, target_doc=None):
-	if not selected_items: return
+	if not selected_items:
+		return
 
 	if isinstance(selected_items, str):
 		selected_items = json.loads(selected_items)
 
-	items_to_map = [item.get('item_code') for item in selected_items if item.get('item_code') and item.get('item_code')]
+	items_to_map = [
+		item.get("item_code")
+		for item in selected_items
+		if item.get("item_code") and item.get("item_code")
+	]
 	items_to_map = list(set(items_to_map))
 
 	def set_missing_values(source, target):
@@ -874,86 +1018,89 @@
 	def update_item(source, target, source_parent):
 		target.schedule_date = source.delivery_date
 		target.qty = flt(source.qty) - (flt(source.ordered_qty) / flt(source.conversion_factor))
-		target.stock_qty = (flt(source.stock_qty) - flt(source.ordered_qty))
+		target.stock_qty = flt(source.stock_qty) - flt(source.ordered_qty)
 		target.project = source_parent.project
 
 	def update_item_for_packed_item(source, target, source_parent):
 		target.qty = flt(source.qty) - flt(source.ordered_qty)
 
 	# po = frappe.get_list("Purchase Order", filters={"sales_order":source_name, "supplier":supplier, "docstatus": ("<", "2")})
-	doc = get_mapped_doc("Sales Order", source_name, {
-		"Sales Order": {
-			"doctype": "Purchase Order",
-			"field_no_map": [
-				"address_display",
-				"contact_display",
-				"contact_mobile",
-				"contact_email",
-				"contact_person",
-				"taxes_and_charges",
-				"shipping_address",
-				"terms"
-			],
-			"validation": {
-				"docstatus": ["=", 1]
-			}
+	doc = get_mapped_doc(
+		"Sales Order",
+		source_name,
+		{
+			"Sales Order": {
+				"doctype": "Purchase Order",
+				"field_no_map": [
+					"address_display",
+					"contact_display",
+					"contact_mobile",
+					"contact_email",
+					"contact_person",
+					"taxes_and_charges",
+					"shipping_address",
+					"terms",
+				],
+				"validation": {"docstatus": ["=", 1]},
+			},
+			"Sales Order Item": {
+				"doctype": "Purchase Order Item",
+				"field_map": [
+					["name", "sales_order_item"],
+					["parent", "sales_order"],
+					["stock_uom", "stock_uom"],
+					["uom", "uom"],
+					["conversion_factor", "conversion_factor"],
+					["delivery_date", "schedule_date"],
+				],
+				"field_no_map": [
+					"rate",
+					"price_list_rate",
+					"item_tax_template",
+					"discount_percentage",
+					"discount_amount",
+					"supplier",
+					"pricing_rules",
+				],
+				"postprocess": update_item,
+				"condition": lambda doc: doc.ordered_qty < doc.stock_qty
+				and doc.item_code in items_to_map
+				and not is_product_bundle(doc.item_code),
+			},
+			"Packed Item": {
+				"doctype": "Purchase Order Item",
+				"field_map": [
+					["name", "sales_order_packed_item"],
+					["parent", "sales_order"],
+					["uom", "uom"],
+					["conversion_factor", "conversion_factor"],
+					["parent_item", "product_bundle"],
+					["rate", "rate"],
+				],
+				"field_no_map": [
+					"price_list_rate",
+					"item_tax_template",
+					"discount_percentage",
+					"discount_amount",
+					"supplier",
+					"pricing_rules",
+				],
+				"postprocess": update_item_for_packed_item,
+				"condition": lambda doc: doc.parent_item in items_to_map,
+			},
 		},
-		"Sales Order Item": {
-			"doctype": "Purchase Order Item",
-			"field_map":  [
-				["name", "sales_order_item"],
-				["parent", "sales_order"],
-				["stock_uom", "stock_uom"],
-				["uom", "uom"],
-				["conversion_factor", "conversion_factor"],
-				["delivery_date", "schedule_date"]
-			],
-			"field_no_map": [
-				"rate",
-				"price_list_rate",
-				"item_tax_template",
-				"discount_percentage",
-				"discount_amount",
-				"supplier",
-				"pricing_rules"
-			],
-			"postprocess": update_item,
-			"condition": lambda doc: doc.ordered_qty < doc.stock_qty and doc.item_code in items_to_map and not is_product_bundle(doc.item_code)
-		},
-		"Packed Item": {
-			"doctype": "Purchase Order Item",
-			"field_map":  [
-				["name", "sales_order_packed_item"],
-				["parent", "sales_order"],
-				["uom", "uom"],
-				["conversion_factor", "conversion_factor"],
-				["parent_item", "product_bundle"],
-				["rate", "rate"]
-			],
-			"field_no_map": [
-				"price_list_rate",
-				"item_tax_template",
-				"discount_percentage",
-				"discount_amount",
-				"supplier",
-				"pricing_rules"
-			],
-			"postprocess": update_item_for_packed_item,
-			"condition": lambda doc: doc.parent_item in items_to_map
-		}
-	}, target_doc, set_missing_values)
+		target_doc,
+		set_missing_values,
+	)
 
 	set_delivery_date(doc.items, source_name)
 
 	return doc
 
+
 def set_delivery_date(items, sales_order):
 	delivery_dates = frappe.get_all(
-		'Sales Order Item',
-		filters = {
-			'parent': sales_order
-		},
-		fields = ['delivery_date', 'item_code']
+		"Sales Order Item", filters={"parent": sales_order}, fields=["delivery_date", "item_code"]
 	)
 
 	delivery_by_item = frappe._dict()
@@ -964,13 +1111,15 @@
 		if item.product_bundle:
 			item.schedule_date = delivery_by_item[item.product_bundle]
 
+
 def is_product_bundle(item_code):
-	return frappe.db.exists('Product Bundle', item_code)
+	return frappe.db.exists("Product Bundle", item_code)
+
 
 @frappe.whitelist()
 def make_work_orders(items, sales_order, company, project=None):
-	'''Make Work Orders against the given Sales Order for the given `items`'''
-	items = json.loads(items).get('items')
+	"""Make Work Orders against the given Sales Order for the given `items`"""
+	items = json.loads(items).get("items")
 	out = []
 
 	for i in items:
@@ -979,18 +1128,20 @@
 		if not i.get("pending_qty"):
 			frappe.throw(_("Please select Qty against item {0}").format(i.get("item_code")))
 
-		work_order = frappe.get_doc(dict(
-			doctype='Work Order',
-			production_item=i['item_code'],
-			bom_no=i.get('bom'),
-			qty=i['pending_qty'],
-			company=company,
-			sales_order=sales_order,
-			sales_order_item=i['sales_order_item'],
-			project=project,
-			fg_warehouse=i['warehouse'],
-			description=i['description']
-		)).insert()
+		work_order = frappe.get_doc(
+			dict(
+				doctype="Work Order",
+				production_item=i["item_code"],
+				bom_no=i.get("bom"),
+				qty=i["pending_qty"],
+				company=company,
+				sales_order=sales_order,
+				sales_order_item=i["sales_order_item"],
+				project=project,
+				fg_warehouse=i["warehouse"],
+				description=i["description"],
+			)
+		).insert()
 		work_order.set_work_order_operations()
 		work_order.flags.ignore_mandatory = True
 		work_order.save()
@@ -998,17 +1149,12 @@
 
 	return [p.name for p in out]
 
+
 @frappe.whitelist()
 def update_status(status, name):
 	so = frappe.get_doc("Sales Order", name)
 	so.update_status(status)
 
-def get_default_bom_item(item_code):
-	bom = frappe.get_all('BOM', dict(item=item_code, is_active=True),
-			order_by='is_default desc')
-	bom = bom[0].name if bom else None
-
-	return bom
 
 @frappe.whitelist()
 def make_raw_material_request(items, company, sales_order, project=None):
@@ -1018,43 +1164,49 @@
 	if isinstance(items, str):
 		items = frappe._dict(json.loads(items))
 
-	for item in items.get('items'):
-		item["include_exploded_items"] = items.get('include_exploded_items')
-		item["ignore_existing_ordered_qty"] = items.get('ignore_existing_ordered_qty')
-		item["include_raw_materials_from_sales_order"] = items.get('include_raw_materials_from_sales_order')
+	for item in items.get("items"):
+		item["include_exploded_items"] = items.get("include_exploded_items")
+		item["ignore_existing_ordered_qty"] = items.get("ignore_existing_ordered_qty")
+		item["include_raw_materials_from_sales_order"] = items.get(
+			"include_raw_materials_from_sales_order"
+		)
 
-	items.update({
-		'company': company,
-		'sales_order': sales_order
-	})
+	items.update({"company": company, "sales_order": sales_order})
 
 	raw_materials = get_items_for_material_requests(items)
 	if not raw_materials:
-		frappe.msgprint(_("Material Request not created, as quantity for Raw Materials already available."))
+		frappe.msgprint(
+			_("Material Request not created, as quantity for Raw Materials already available.")
+		)
 		return
 
-	material_request = frappe.new_doc('Material Request')
-	material_request.update(dict(
-		doctype = 'Material Request',
-		transaction_date = nowdate(),
-		company = company,
-		material_request_type = 'Purchase'
-	))
+	material_request = frappe.new_doc("Material Request")
+	material_request.update(
+		dict(
+			doctype="Material Request",
+			transaction_date=nowdate(),
+			company=company,
+			material_request_type="Purchase",
+		)
+	)
 	for item in raw_materials:
-		item_doc = frappe.get_cached_doc('Item', item.get('item_code'))
+		item_doc = frappe.get_cached_doc("Item", item.get("item_code"))
 
 		schedule_date = add_days(nowdate(), cint(item_doc.lead_time_days))
-		row = material_request.append('items', {
-			'item_code': item.get('item_code'),
-			'qty': item.get('quantity'),
-			'schedule_date': schedule_date,
-			'warehouse': item.get('warehouse'),
-			'sales_order': sales_order,
-			'project': project
-		})
+		row = material_request.append(
+			"items",
+			{
+				"item_code": item.get("item_code"),
+				"qty": item.get("quantity"),
+				"schedule_date": schedule_date,
+				"warehouse": item.get("warehouse"),
+				"sales_order": sales_order,
+				"project": project,
+			},
+		)
 
 		if not (strip_html(item.get("description")) and strip_html(item_doc.description)):
-			row.description = item_doc.item_name or item.get('item_code')
+			row.description = item_doc.item_name or item.get("item_code")
 
 	material_request.insert()
 	material_request.flags.ignore_permissions = 1
@@ -1062,53 +1214,86 @@
 	material_request.submit()
 	return material_request
 
+
 @frappe.whitelist()
 def make_inter_company_purchase_order(source_name, target_doc=None):
 	from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction
+
 	return make_inter_company_transaction("Sales Order", source_name, target_doc)
 
+
 @frappe.whitelist()
 def create_pick_list(source_name, target_doc=None):
-	def update_item_quantity(source, target, source_parent):
-		target.qty = flt(source.qty) - flt(source.delivered_qty)
-		target.stock_qty = (flt(source.qty) - flt(source.delivered_qty)) * flt(source.conversion_factor)
+	from erpnext.stock.doctype.packed_item.packed_item import is_product_bundle
 
-	doc = get_mapped_doc('Sales Order', source_name, {
-		'Sales Order': {
-			'doctype': 'Pick List',
-			'validation': {
-				'docstatus': ['=', 1]
-			}
-		},
-		'Sales Order Item': {
-			'doctype': 'Pick List Item',
-			'field_map': {
-				'parent': 'sales_order',
-				'name': 'sales_order_item'
+	def update_item_quantity(source, target, source_parent) -> None:
+		picked_qty = flt(source.picked_qty) / (flt(source.conversion_factor) or 1)
+		qty_to_be_picked = flt(source.qty) - max(picked_qty, flt(source.delivered_qty))
+
+		target.qty = qty_to_be_picked
+		target.stock_qty = qty_to_be_picked * flt(source.conversion_factor)
+
+	def update_packed_item_qty(source, target, source_parent) -> None:
+		qty = flt(source.qty)
+		for item in source_parent.items:
+			if source.parent_detail_docname == item.name:
+				picked_qty = flt(item.picked_qty) / (flt(item.conversion_factor) or 1)
+				pending_percent = (item.qty - max(picked_qty, item.delivered_qty)) / item.qty
+				target.qty = target.stock_qty = qty * pending_percent
+				return
+
+	def should_pick_order_item(item) -> bool:
+		return (
+			abs(item.delivered_qty) < abs(item.qty)
+			and item.delivered_by_supplier != 1
+			and not is_product_bundle(item.item_code)
+		)
+
+	doc = get_mapped_doc(
+		"Sales Order",
+		source_name,
+		{
+			"Sales Order": {"doctype": "Pick List", "validation": {"docstatus": ["=", 1]}},
+			"Sales Order Item": {
+				"doctype": "Pick List Item",
+				"field_map": {"parent": "sales_order", "name": "sales_order_item"},
+				"postprocess": update_item_quantity,
+				"condition": should_pick_order_item,
 			},
-			'postprocess': update_item_quantity,
-			'condition': lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1
+			"Packed Item": {
+				"doctype": "Pick List Item",
+				"field_map": {
+					"parent": "sales_order",
+					"name": "sales_order_item",
+					"parent_detail_docname": "product_bundle_item",
+				},
+				"field_no_map": ["picked_qty"],
+				"postprocess": update_packed_item_qty,
+			},
 		},
-	}, target_doc)
+		target_doc,
+	)
 
-	doc.purpose = 'Delivery'
+	doc.purpose = "Delivery"
 
 	doc.set_item_locations()
 
 	return doc
 
+
 def update_produced_qty_in_so_item(sales_order, sales_order_item):
-	#for multiple work orders against same sales order item
-	linked_wo_with_so_item = frappe.db.get_all('Work Order', ['produced_qty'], {
-		'sales_order_item': sales_order_item,
-		'sales_order': sales_order,
-		'docstatus': 1
-	})
+	# for multiple work orders against same sales order item
+	linked_wo_with_so_item = frappe.db.get_all(
+		"Work Order",
+		["produced_qty"],
+		{"sales_order_item": sales_order_item, "sales_order": sales_order, "docstatus": 1},
+	)
 
 	total_produced_qty = 0
 	for wo in linked_wo_with_so_item:
-		total_produced_qty += flt(wo.get('produced_qty'))
+		total_produced_qty += flt(wo.get("produced_qty"))
 
-	if not total_produced_qty and frappe.flags.in_patch: return
+	if not total_produced_qty and frappe.flags.in_patch:
+		return
 
-	frappe.db.set_value('Sales Order Item', sales_order_item, 'produced_qty', total_produced_qty)
+	frappe.db.set_value("Sales Order Item", sales_order_item, "produced_qty", total_produced_qty)
diff --git a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py
index 1e616b8..ace2e29 100644
--- a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py
+++ b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py
@@ -3,42 +3,25 @@
 
 def get_data():
 	return {
-		'fieldname': 'sales_order',
-		'non_standard_fieldnames': {
-			'Delivery Note': 'against_sales_order',
-			'Journal Entry': 'reference_name',
-			'Payment Entry': 'reference_name',
-			'Payment Request': 'reference_name',
-			'Auto Repeat': 'reference_document',
-			'Maintenance Visit': 'prevdoc_docname'
+		"fieldname": "sales_order",
+		"non_standard_fieldnames": {
+			"Delivery Note": "against_sales_order",
+			"Journal Entry": "reference_name",
+			"Payment Entry": "reference_name",
+			"Payment Request": "reference_name",
+			"Auto Repeat": "reference_document",
+			"Maintenance Visit": "prevdoc_docname",
 		},
-		'internal_links': {
-			'Quotation': ['items', 'prevdoc_docname']
-		},
-		'transactions': [
+		"internal_links": {"Quotation": ["items", "prevdoc_docname"]},
+		"transactions": [
 			{
-				'label': _('Fulfillment'),
-				'items': ['Sales Invoice', 'Pick List', 'Delivery Note', 'Maintenance Visit']
+				"label": _("Fulfillment"),
+				"items": ["Sales Invoice", "Pick List", "Delivery Note", "Maintenance Visit"],
 			},
-			{
-				'label': _('Purchasing'),
-				'items': ['Material Request', 'Purchase Order']
-			},
-			{
-				'label': _('Projects'),
-				'items': ['Project']
-			},
-			{
-				'label': _('Manufacturing'),
-				'items': ['Work Order']
-			},
-			{
-				'label': _('Reference'),
-				'items': ['Quotation', 'Auto Repeat']
-			},
-			{
-				'label': _('Payment'),
-				'items': ['Payment Entry', 'Payment Request', 'Journal Entry']
-			},
-		]
+			{"label": _("Purchasing"), "items": ["Material Request", "Purchase Order"]},
+			{"label": _("Projects"), "items": ["Project"]},
+			{"label": _("Manufacturing"), "items": ["Work Order"]},
+			{"label": _("Reference"), "items": ["Quotation", "Auto Repeat"]},
+			{"label": _("Payment"), "items": ["Payment Entry", "Payment Request", "Journal Entry"]},
+		],
 	}
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index b528479..e5e317c 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -31,18 +31,24 @@
 
 
 class TestSalesOrder(FrappeTestCase):
-
 	@classmethod
 	def setUpClass(cls):
 		super().setUpClass()
-		cls.unlink_setting = int(frappe.db.get_value("Accounts Settings", "Accounts Settings",
-			"unlink_advance_payment_on_cancelation_of_order"))
+		cls.unlink_setting = int(
+			frappe.db.get_value(
+				"Accounts Settings", "Accounts Settings", "unlink_advance_payment_on_cancelation_of_order"
+			)
+		)
 
 	@classmethod
 	def tearDownClass(cls) -> None:
 		# reset config to previous state
-		frappe.db.set_value("Accounts Settings", "Accounts Settings",
-			"unlink_advance_payment_on_cancelation_of_order", cls.unlink_setting)
+		frappe.db.set_value(
+			"Accounts Settings",
+			"Accounts Settings",
+			"unlink_advance_payment_on_cancelation_of_order",
+			cls.unlink_setting,
+		)
 		super().tearDownClass()
 
 	def tearDown(self):
@@ -89,6 +95,7 @@
 
 	def test_so_billed_amount_against_return_entry(self):
 		from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return
+
 		so = make_sales_order(do_not_submit=True)
 		so.submit()
 
@@ -117,7 +124,7 @@
 		self.assertEqual(len(si.get("items")), 1)
 
 		si.insert()
-		si.set('taxes', [])
+		si.set("taxes", [])
 		si.save()
 
 		self.assertEqual(si.payment_schedule[0].payment_amount, 500.0)
@@ -185,16 +192,16 @@
 		dn1.items[0].so_detail = so.items[0].name
 		dn1.submit()
 
-		si1 = create_sales_invoice(is_return=1, return_against=si2.name, qty=-1, update_stock=1, do_not_submit=True)
+		si1 = create_sales_invoice(
+			is_return=1, return_against=si2.name, qty=-1, update_stock=1, do_not_submit=True
+		)
 		si1.items[0].sales_order = so.name
 		si1.items[0].so_detail = so.items[0].name
 		si1.submit()
 
-
 		so.load_from_db()
 		self.assertEqual(so.get("items")[0].delivered_qty, 5)
 
-
 	def test_reserved_qty_for_partial_delivery(self):
 		make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100)
 		existing_reserved_qty = get_reserved_qty()
@@ -212,7 +219,7 @@
 
 		# unclose so
 		so.load_from_db()
-		so.update_status('Draft')
+		so.update_status("Draft")
 		self.assertEqual(get_reserved_qty(), existing_reserved_qty + 5)
 
 		dn.cancel()
@@ -226,7 +233,7 @@
 	def test_reserved_qty_for_over_delivery(self):
 		make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100)
 		# set over-delivery allowance
-		frappe.db.set_value('Item', "_Test Item", 'over_delivery_receipt_allowance', 50)
+		frappe.db.set_value("Item", "_Test Item", "over_delivery_receipt_allowance", 50)
 
 		existing_reserved_qty = get_reserved_qty()
 
@@ -243,8 +250,8 @@
 		make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100)
 
 		# set over-delivery allowance
-		frappe.db.set_value('Item', "_Test Item", 'over_delivery_receipt_allowance', 50)
-		frappe.db.set_value('Item', "_Test Item", 'over_billing_allowance', 20)
+		frappe.db.set_value("Item", "_Test Item", "over_delivery_receipt_allowance", 50)
+		frappe.db.set_value("Item", "_Test Item", "over_billing_allowance", 20)
 
 		existing_reserved_qty = get_reserved_qty()
 
@@ -272,7 +279,9 @@
 
 	def test_reserved_qty_for_partial_delivery_with_packing_list(self):
 		make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100)
-		make_stock_entry(item="_Test Item Home Desktop 100", target="_Test Warehouse - _TC", qty=10, rate=100)
+		make_stock_entry(
+			item="_Test Item Home Desktop 100", target="_Test Warehouse - _TC", qty=10, rate=100
+		)
 
 		existing_reserved_qty_item1 = get_reserved_qty("_Test Item")
 		existing_reserved_qty_item2 = get_reserved_qty("_Test Item Home Desktop 100")
@@ -280,14 +289,16 @@
 		so = make_sales_order(item_code="_Test Product Bundle Item")
 
 		self.assertEqual(get_reserved_qty("_Test Item"), existing_reserved_qty_item1 + 50)
-		self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"),
-			existing_reserved_qty_item2 + 20)
+		self.assertEqual(
+			get_reserved_qty("_Test Item Home Desktop 100"), existing_reserved_qty_item2 + 20
+		)
 
 		dn = create_dn_against_so(so.name)
 
 		self.assertEqual(get_reserved_qty("_Test Item"), existing_reserved_qty_item1 + 25)
-		self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"),
-			existing_reserved_qty_item2 + 10)
+		self.assertEqual(
+			get_reserved_qty("_Test Item Home Desktop 100"), existing_reserved_qty_item2 + 10
+		)
 
 		# close so
 		so.load_from_db()
@@ -298,16 +309,18 @@
 
 		# unclose so
 		so.load_from_db()
-		so.update_status('Draft')
+		so.update_status("Draft")
 
 		self.assertEqual(get_reserved_qty("_Test Item"), existing_reserved_qty_item1 + 25)
-		self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"),
-			existing_reserved_qty_item2 + 10)
+		self.assertEqual(
+			get_reserved_qty("_Test Item Home Desktop 100"), existing_reserved_qty_item2 + 10
+		)
 
 		dn.cancel()
 		self.assertEqual(get_reserved_qty("_Test Item"), existing_reserved_qty_item1 + 50)
-		self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"),
-			existing_reserved_qty_item2 + 20)
+		self.assertEqual(
+			get_reserved_qty("_Test Item Home Desktop 100"), existing_reserved_qty_item2 + 20
+		)
 
 		so.load_from_db()
 		so.cancel()
@@ -316,17 +329,19 @@
 
 	def test_sales_order_on_hold(self):
 		so = make_sales_order(item_code="_Test Product Bundle Item")
-		so.db_set('Status', "On Hold")
+		so.db_set("status", "On Hold")
 		si = make_sales_invoice(so.name)
 		self.assertRaises(frappe.ValidationError, create_dn_against_so, so.name)
 		self.assertRaises(frappe.ValidationError, si.submit)
 
 	def test_reserved_qty_for_over_delivery_with_packing_list(self):
 		make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100)
-		make_stock_entry(item="_Test Item Home Desktop 100", target="_Test Warehouse - _TC", qty=10, rate=100)
+		make_stock_entry(
+			item="_Test Item Home Desktop 100", target="_Test Warehouse - _TC", qty=10, rate=100
+		)
 
 		# set over-delivery allowance
-		frappe.db.set_value('Item', "_Test Product Bundle Item", 'over_delivery_receipt_allowance', 50)
+		frappe.db.set_value("Item", "_Test Product Bundle Item", "over_delivery_receipt_allowance", 50)
 
 		existing_reserved_qty_item1 = get_reserved_qty("_Test Item")
 		existing_reserved_qty_item2 = get_reserved_qty("_Test Item Home Desktop 100")
@@ -334,22 +349,23 @@
 		so = make_sales_order(item_code="_Test Product Bundle Item")
 
 		self.assertEqual(get_reserved_qty("_Test Item"), existing_reserved_qty_item1 + 50)
-		self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"),
-			existing_reserved_qty_item2 + 20)
+		self.assertEqual(
+			get_reserved_qty("_Test Item Home Desktop 100"), existing_reserved_qty_item2 + 20
+		)
 
 		dn = create_dn_against_so(so.name, 15)
 
 		self.assertEqual(get_reserved_qty("_Test Item"), existing_reserved_qty_item1)
-		self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"),
-			existing_reserved_qty_item2)
+		self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"), existing_reserved_qty_item2)
 
 		dn.cancel()
 		self.assertEqual(get_reserved_qty("_Test Item"), existing_reserved_qty_item1 + 50)
-		self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"),
-			existing_reserved_qty_item2 + 20)
+		self.assertEqual(
+			get_reserved_qty("_Test Item Home Desktop 100"), existing_reserved_qty_item2 + 20
+		)
 
 	def test_update_child_adding_new_item(self):
-		so = make_sales_order(item_code= "_Test Item", qty=4)
+		so = make_sales_order(item_code="_Test Item", qty=4)
 		create_dn_against_so(so.name, 4)
 		make_sales_invoice(so.name)
 
@@ -360,38 +376,38 @@
 		reserved_qty_for_second_item = get_reserved_qty("_Test Item 2")
 
 		first_item_of_so = so.get("items")[0]
-		trans_item = json.dumps([
-			{'item_code' : first_item_of_so.item_code, 'rate' : first_item_of_so.rate, \
-				'qty' : first_item_of_so.qty, 'docname': first_item_of_so.name},
-			{'item_code' : '_Test Item 2', 'rate' : 200, 'qty' : 7}
-		])
-		update_child_qty_rate('Sales Order', trans_item, so.name)
+		trans_item = json.dumps(
+			[
+				{
+					"item_code": first_item_of_so.item_code,
+					"rate": first_item_of_so.rate,
+					"qty": first_item_of_so.qty,
+					"docname": first_item_of_so.name,
+				},
+				{"item_code": "_Test Item 2", "rate": 200, "qty": 7},
+			]
+		)
+		update_child_qty_rate("Sales Order", trans_item, so.name)
 
 		so.reload()
-		self.assertEqual(so.get("items")[-1].item_code, '_Test Item 2')
+		self.assertEqual(so.get("items")[-1].item_code, "_Test Item 2")
 		self.assertEqual(so.get("items")[-1].rate, 200)
 		self.assertEqual(so.get("items")[-1].qty, 7)
 		self.assertEqual(so.get("items")[-1].amount, 1400)
 
 		# reserved qty should increase after adding row
-		self.assertEqual(get_reserved_qty('_Test Item 2'), reserved_qty_for_second_item + 7)
+		self.assertEqual(get_reserved_qty("_Test Item 2"), reserved_qty_for_second_item + 7)
 
-		self.assertEqual(so.status, 'To Deliver and Bill')
+		self.assertEqual(so.status, "To Deliver and Bill")
 
 		updated_total = so.get("base_total")
 		updated_total_in_words = so.get("base_in_words")
 
-		self.assertEqual(updated_total, prev_total+1400)
+		self.assertEqual(updated_total, prev_total + 1400)
 		self.assertNotEqual(updated_total_in_words, prev_total_in_words)
 
 	def test_update_child_removing_item(self):
-		so = make_sales_order(**{
-			"item_list": [{
-				"item_code": '_Test Item',
-				"qty": 5,
-				"rate":1000
-			}]
-		})
+		so = make_sales_order(**{"item_list": [{"item_code": "_Test Item", "qty": 5, "rate": 1000}]})
 		create_dn_against_so(so.name, 2)
 		make_sales_invoice(so.name)
 
@@ -399,64 +415,67 @@
 		reserved_qty_for_second_item = get_reserved_qty("_Test Item 2")
 
 		# add an item so as to try removing items
-		trans_item = json.dumps([
-			{"item_code": '_Test Item', "qty": 5, "rate":1000, "docname": so.get("items")[0].name},
-			{"item_code": '_Test Item 2', "qty": 2, "rate":500}
-		])
-		update_child_qty_rate('Sales Order', trans_item, so.name)
+		trans_item = json.dumps(
+			[
+				{"item_code": "_Test Item", "qty": 5, "rate": 1000, "docname": so.get("items")[0].name},
+				{"item_code": "_Test Item 2", "qty": 2, "rate": 500},
+			]
+		)
+		update_child_qty_rate("Sales Order", trans_item, so.name)
 		so.reload()
 		self.assertEqual(len(so.get("items")), 2)
 
 		# reserved qty should increase after adding row
-		self.assertEqual(get_reserved_qty('_Test Item 2'), reserved_qty_for_second_item + 2)
+		self.assertEqual(get_reserved_qty("_Test Item 2"), reserved_qty_for_second_item + 2)
 
 		# check if delivered items can be removed
-		trans_item = json.dumps([{
-			"item_code": '_Test Item 2',
-			"qty": 2,
-			"rate":500,
-			"docname": so.get("items")[1].name
-		}])
-		self.assertRaises(frappe.ValidationError, update_child_qty_rate, 'Sales Order', trans_item, so.name)
+		trans_item = json.dumps(
+			[{"item_code": "_Test Item 2", "qty": 2, "rate": 500, "docname": so.get("items")[1].name}]
+		)
+		self.assertRaises(
+			frappe.ValidationError, update_child_qty_rate, "Sales Order", trans_item, so.name
+		)
 
-		#remove last added item
-		trans_item = json.dumps([{
-			"item_code": '_Test Item',
-			"qty": 5,
-			"rate":1000,
-			"docname": so.get("items")[0].name
-		}])
-		update_child_qty_rate('Sales Order', trans_item, so.name)
+		# remove last added item
+		trans_item = json.dumps(
+			[{"item_code": "_Test Item", "qty": 5, "rate": 1000, "docname": so.get("items")[0].name}]
+		)
+		update_child_qty_rate("Sales Order", trans_item, so.name)
 
 		so.reload()
 		self.assertEqual(len(so.get("items")), 1)
 
 		# reserved qty should decrease (back to initial) after deleting row
-		self.assertEqual(get_reserved_qty('_Test Item 2'), reserved_qty_for_second_item)
+		self.assertEqual(get_reserved_qty("_Test Item 2"), reserved_qty_for_second_item)
 
-		self.assertEqual(so.status, 'To Deliver and Bill')
-
+		self.assertEqual(so.status, "To Deliver and Bill")
 
 	def test_update_child(self):
-		so = make_sales_order(item_code= "_Test Item", qty=4)
+		so = make_sales_order(item_code="_Test Item", qty=4)
 		create_dn_against_so(so.name, 4)
 		make_sales_invoice(so.name)
 
 		existing_reserved_qty = get_reserved_qty()
 
-		trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': so.items[0].name}])
-		update_child_qty_rate('Sales Order', trans_item, so.name)
+		trans_item = json.dumps(
+			[{"item_code": "_Test Item", "rate": 200, "qty": 7, "docname": so.items[0].name}]
+		)
+		update_child_qty_rate("Sales Order", trans_item, so.name)
 
 		so.reload()
 		self.assertEqual(so.get("items")[0].rate, 200)
 		self.assertEqual(so.get("items")[0].qty, 7)
 		self.assertEqual(so.get("items")[0].amount, 1400)
-		self.assertEqual(so.status, 'To Deliver and Bill')
+		self.assertEqual(so.status, "To Deliver and Bill")
 
 		self.assertEqual(get_reserved_qty(), existing_reserved_qty + 3)
 
-		trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 2, 'docname': so.items[0].name}])
-		self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
+		trans_item = json.dumps(
+			[{"item_code": "_Test Item", "rate": 200, "qty": 2, "docname": so.items[0].name}]
+		)
+		self.assertRaises(
+			frappe.ValidationError, update_child_qty_rate, "Sales Order", trans_item, so.name
+		)
 
 	def test_update_child_with_precision(self):
 		from frappe.custom.doctype.property_setter.property_setter import make_property_setter
@@ -465,48 +484,60 @@
 		precision = get_field_precision(frappe.get_meta("Sales Order Item").get_field("rate"))
 
 		make_property_setter("Sales Order Item", "rate", "precision", 7, "Currency")
-		so = make_sales_order(item_code= "_Test Item", qty=4, rate=200.34664)
+		so = make_sales_order(item_code="_Test Item", qty=4, rate=200.34664)
 
-		trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200.34669, 'qty' : 4, 'docname': so.items[0].name}])
-		update_child_qty_rate('Sales Order', trans_item, so.name)
+		trans_item = json.dumps(
+			[{"item_code": "_Test Item", "rate": 200.34669, "qty": 4, "docname": so.items[0].name}]
+		)
+		update_child_qty_rate("Sales Order", trans_item, so.name)
 
 		so.reload()
 		self.assertEqual(so.items[0].rate, 200.34669)
 		make_property_setter("Sales Order Item", "rate", "precision", precision, "Currency")
 
 	def test_update_child_perm(self):
-		so = make_sales_order(item_code= "_Test Item", qty=4)
+		so = make_sales_order(item_code="_Test Item", qty=4)
 
 		test_user = create_user("test_so_child_perms@example.com", "Accounts User")
 		frappe.set_user(test_user.name)
 
 		# update qty
-		trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': so.items[0].name}])
-		self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
+		trans_item = json.dumps(
+			[{"item_code": "_Test Item", "rate": 200, "qty": 7, "docname": so.items[0].name}]
+		)
+		self.assertRaises(
+			frappe.ValidationError, update_child_qty_rate, "Sales Order", trans_item, so.name
+		)
 
 		# add new item
-		trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}])
-		self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
+		trans_item = json.dumps([{"item_code": "_Test Item", "rate": 100, "qty": 2}])
+		self.assertRaises(
+			frappe.ValidationError, update_child_qty_rate, "Sales Order", trans_item, so.name
+		)
 
 	def test_update_child_qty_rate_with_workflow(self):
 		from frappe.model.workflow import apply_workflow
 
 		workflow = make_sales_order_workflow()
-		so = make_sales_order(item_code= "_Test Item", qty=1, rate=150, do_not_submit=1)
-		apply_workflow(so, 'Approve')
+		so = make_sales_order(item_code="_Test Item", qty=1, rate=150, do_not_submit=1)
+		apply_workflow(so, "Approve")
 
-		user = 'test@example.com'
-		test_user = frappe.get_doc('User', user)
+		user = "test@example.com"
+		test_user = frappe.get_doc("User", user)
 		test_user.add_roles("Sales User", "Test Junior Approver")
 		frappe.set_user(user)
 
 		# user shouldn't be able to edit since grand_total will become > 200 if qty is doubled
-		trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 150, 'qty' : 2, 'docname': so.items[0].name}])
-		self.assertRaises(frappe.ValidationError, update_child_qty_rate, 'Sales Order', trans_item, so.name)
+		trans_item = json.dumps(
+			[{"item_code": "_Test Item", "rate": 150, "qty": 2, "docname": so.items[0].name}]
+		)
+		self.assertRaises(
+			frappe.ValidationError, update_child_qty_rate, "Sales Order", trans_item, so.name
+		)
 
 		frappe.set_user("Administrator")
-		user2 = 'test2@example.com'
-		test_user2 = frappe.get_doc('User', user2)
+		user2 = "test2@example.com"
+		test_user2 = frappe.get_doc("User", user2)
 		test_user2.add_roles("Sales User", "Test Approver")
 		frappe.set_user(user2)
 
@@ -525,21 +556,21 @@
 		# test Update Items with product bundle
 		if not frappe.db.exists("Item", "_Product Bundle Item"):
 			bundle_item = make_item("_Product Bundle Item", {"is_stock_item": 0})
-			bundle_item.append("item_defaults", {
-					"company": "_Test Company",
-					"default_warehouse": "_Test Warehouse - _TC"})
+			bundle_item.append(
+				"item_defaults", {"company": "_Test Company", "default_warehouse": "_Test Warehouse - _TC"}
+			)
 			bundle_item.save(ignore_permissions=True)
 
 		make_item("_Packed Item", {"is_stock_item": 1})
 		make_product_bundle("_Product Bundle Item", ["_Packed Item"], 2)
 
-		so = make_sales_order(item_code = "_Test Item", warehouse=None)
+		so = make_sales_order(item_code="_Test Item", warehouse=None)
 
 		# get reserved qty of packed item
 		existing_reserved_qty = get_reserved_qty("_Packed Item")
 
-		added_item = json.dumps([{"item_code" : "_Product Bundle Item", "rate" : 200, 'qty' : 2}])
-		update_child_qty_rate('Sales Order', added_item, so.name)
+		added_item = json.dumps([{"item_code": "_Product Bundle Item", "rate": 200, "qty": 2}])
+		update_child_qty_rate("Sales Order", added_item, so.name)
 
 		so.reload()
 		self.assertEqual(so.packed_items[0].qty, 4)
@@ -548,15 +579,19 @@
 		self.assertEqual(get_reserved_qty("_Packed Item"), existing_reserved_qty + 4)
 
 		# test uom and conversion factor change
-		update_uom_conv_factor = json.dumps([{
-			'item_code': so.get("items")[0].item_code,
-			'rate': so.get("items")[0].rate,
-			'qty': so.get("items")[0].qty,
-			'uom': "_Test UOM 1",
-			'conversion_factor': 2,
-			'docname': so.get("items")[0].name
-		}])
-		update_child_qty_rate('Sales Order', update_uom_conv_factor, so.name)
+		update_uom_conv_factor = json.dumps(
+			[
+				{
+					"item_code": so.get("items")[0].item_code,
+					"rate": so.get("items")[0].rate,
+					"qty": so.get("items")[0].qty,
+					"uom": "_Test UOM 1",
+					"conversion_factor": 2,
+					"docname": so.get("items")[0].name,
+				}
+			]
+		)
+		update_child_qty_rate("Sales Order", update_uom_conv_factor, so.name)
 
 		so.reload()
 		self.assertEqual(so.packed_items[0].qty, 8)
@@ -566,61 +601,67 @@
 
 	def test_update_child_with_tax_template(self):
 		"""
-			Test Action: Create a SO with one item having its tax account head already in the SO.
-			Add the same item + new item with tax template via Update Items.
-			Expected result: First Item's tax row is updated. New tax row is added for second Item.
+		Test Action: Create a SO with one item having its tax account head already in the SO.
+		Add the same item + new item with tax template via Update Items.
+		Expected result: First Item's tax row is updated. New tax row is added for second Item.
 		"""
 		if not frappe.db.exists("Item", "Test Item with Tax"):
-			make_item("Test Item with Tax", {
-				'is_stock_item': 1,
-			})
+			make_item(
+				"Test Item with Tax",
+				{
+					"is_stock_item": 1,
+				},
+			)
 
-		if not frappe.db.exists("Item Tax Template", {"title": 'Test Update Items Template'}):
-			frappe.get_doc({
-				'doctype': 'Item Tax Template',
-				'title': 'Test Update Items Template',
-				'company': '_Test Company',
-				'taxes': [
-					{
-						'tax_type': "_Test Account Service Tax - _TC",
-						'tax_rate': 10,
-					}
-				]
-			}).insert()
+		if not frappe.db.exists("Item Tax Template", {"title": "Test Update Items Template"}):
+			frappe.get_doc(
+				{
+					"doctype": "Item Tax Template",
+					"title": "Test Update Items Template",
+					"company": "_Test Company",
+					"taxes": [
+						{
+							"tax_type": "_Test Account Service Tax - _TC",
+							"tax_rate": 10,
+						}
+					],
+				}
+			).insert()
 
 		new_item_with_tax = frappe.get_doc("Item", "Test Item with Tax")
 
-		new_item_with_tax.append("taxes", {
-			"item_tax_template": "Test Update Items Template - _TC",
-			"valid_from": nowdate()
-		})
+		new_item_with_tax.append(
+			"taxes", {"item_tax_template": "Test Update Items Template - _TC", "valid_from": nowdate()}
+		)
 		new_item_with_tax.save()
 
 		tax_template = "_Test Account Excise Duty @ 10 - _TC"
-		item =  "_Test Item Home Desktop 100"
-		if not frappe.db.exists("Item Tax", {"parent":item, "item_tax_template":tax_template}):
+		item = "_Test Item Home Desktop 100"
+		if not frappe.db.exists("Item Tax", {"parent": item, "item_tax_template": tax_template}):
 			item_doc = frappe.get_doc("Item", item)
-			item_doc.append("taxes", {
-				"item_tax_template": tax_template,
-				"valid_from": nowdate()
-			})
+			item_doc.append("taxes", {"item_tax_template": tax_template, "valid_from": nowdate()})
 			item_doc.save()
 		else:
 			# update valid from
-			frappe.db.sql("""UPDATE `tabItem Tax` set valid_from = CURDATE()
+			frappe.db.sql(
+				"""UPDATE `tabItem Tax` set valid_from = CURRENT_DATE
 				where parent = %(item)s and item_tax_template = %(tax)s""",
-					{"item": item, "tax": tax_template})
+				{"item": item, "tax": tax_template},
+			)
 
 		so = make_sales_order(item_code=item, qty=1, do_not_save=1)
 
-		so.append("taxes", {
-			"account_head": "_Test Account Excise Duty - _TC",
-			"charge_type": "On Net Total",
-			"cost_center": "_Test Cost Center - _TC",
-			"description": "Excise Duty",
-			"doctype": "Sales Taxes and Charges",
-			"rate": 10
-		})
+		so.append(
+			"taxes",
+			{
+				"account_head": "_Test Account Excise Duty - _TC",
+				"charge_type": "On Net Total",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "Excise Duty",
+				"doctype": "Sales Taxes and Charges",
+				"rate": 10,
+			},
+		)
 		so.insert()
 		so.submit()
 
@@ -630,12 +671,22 @@
 		old_stock_settings_value = frappe.db.get_single_value("Stock Settings", "default_warehouse")
 		frappe.db.set_value("Stock Settings", None, "default_warehouse", "_Test Warehouse - _TC")
 
-		items = json.dumps([
-			{'item_code' : item, 'rate' : 100, 'qty' : 1, 'docname': so.items[0].name},
-			{'item_code' : item, 'rate' : 200, 'qty' : 1}, # added item whose tax account head already exists in PO
-			{'item_code' : new_item_with_tax.name, 'rate' : 100, 'qty' : 1} # added item whose tax account head  is missing in PO
-		])
-		update_child_qty_rate('Sales Order', items, so.name)
+		items = json.dumps(
+			[
+				{"item_code": item, "rate": 100, "qty": 1, "docname": so.items[0].name},
+				{
+					"item_code": item,
+					"rate": 200,
+					"qty": 1,
+				},  # added item whose tax account head already exists in PO
+				{
+					"item_code": new_item_with_tax.name,
+					"rate": 100,
+					"qty": 1,
+				},  # added item whose tax account head  is missing in PO
+			]
+		)
+		update_child_qty_rate("Sales Order", items, so.name)
 
 		so.reload()
 		self.assertEqual(so.taxes[0].tax_amount, 40)
@@ -645,8 +696,11 @@
 		self.assertEqual(so.taxes[1].total, 480)
 
 		# teardown
-		frappe.db.sql("""UPDATE `tabItem Tax` set valid_from = NULL
-			where parent = %(item)s and item_tax_template = %(tax)s""", {"item": item, "tax": tax_template})
+		frappe.db.sql(
+			"""UPDATE `tabItem Tax` set valid_from = NULL
+			where parent = %(item)s and item_tax_template = %(tax)s""",
+			{"item": item, "tax": tax_template},
+		)
 		so.cancel()
 		so.delete()
 		new_item_with_tax.delete()
@@ -666,8 +720,12 @@
 
 		frappe.set_user(test_user.name)
 
-		so = make_sales_order(company="_Test Company 1", customer="_Test Customer 1",
-			warehouse="_Test Warehouse 2 - _TC1", do_not_save=True)
+		so = make_sales_order(
+			company="_Test Company 1",
+			customer="_Test Customer 1",
+			warehouse="_Test Warehouse 2 - _TC1",
+			do_not_save=True,
+		)
 		so.conversion_rate = 0.02
 		so.plc_conversion_rate = 0.02
 		self.assertRaises(frappe.PermissionError, so.insert)
@@ -677,7 +735,9 @@
 
 		frappe.set_user("Administrator")
 		frappe.permissions.remove_user_permission("Warehouse", "_Test Warehouse 1 - _TC", test_user.name)
-		frappe.permissions.remove_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", test_user_2.name)
+		frappe.permissions.remove_user_permission(
+			"Warehouse", "_Test Warehouse 2 - _TC1", test_user_2.name
+		)
 		frappe.permissions.remove_user_permission("Company", "_Test Company 1", test_user_2.name)
 
 	def test_block_delivery_note_against_cancelled_sales_order(self):
@@ -697,10 +757,12 @@
 		make_item("_Test Service Product Bundle Item 1", {"is_stock_item": 0})
 		make_item("_Test Service Product Bundle Item 2", {"is_stock_item": 0})
 
-		make_product_bundle("_Test Service Product Bundle",
-			["_Test Service Product Bundle Item 1", "_Test Service Product Bundle Item 2"])
+		make_product_bundle(
+			"_Test Service Product Bundle",
+			["_Test Service Product Bundle Item 1", "_Test Service Product Bundle Item 2"],
+		)
 
-		so = make_sales_order(item_code = "_Test Service Product Bundle", warehouse=None)
+		so = make_sales_order(item_code="_Test Service Product Bundle", warehouse=None)
 
 		self.assertTrue("_Test Service Product Bundle Item 1" in [d.item_code for d in so.packed_items])
 		self.assertTrue("_Test Service Product Bundle Item 2" in [d.item_code for d in so.packed_items])
@@ -710,38 +772,79 @@
 		make_item("_Test Mix Product Bundle Item 1", {"is_stock_item": 1})
 		make_item("_Test Mix Product Bundle Item 2", {"is_stock_item": 0})
 
-		make_product_bundle("_Test Mix Product Bundle",
-			["_Test Mix Product Bundle Item 1", "_Test Mix Product Bundle Item 2"])
+		make_product_bundle(
+			"_Test Mix Product Bundle",
+			["_Test Mix Product Bundle Item 1", "_Test Mix Product Bundle Item 2"],
+		)
 
-		self.assertRaises(WarehouseRequired, make_sales_order, item_code = "_Test Mix Product Bundle", warehouse="")
+		self.assertRaises(
+			WarehouseRequired, make_sales_order, item_code="_Test Mix Product Bundle", warehouse=""
+		)
 
 	def test_auto_insert_price(self):
 		make_item("_Test Item for Auto Price List", {"is_stock_item": 0})
+		make_item("_Test Item for Auto Price List with Discount Percentage", {"is_stock_item": 0})
 		frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 1)
 
-		item_price = frappe.db.get_value("Item Price", {"price_list": "_Test Price List",
-			"item_code": "_Test Item for Auto Price List"})
+		item_price = frappe.db.get_value(
+			"Item Price", {"price_list": "_Test Price List", "item_code": "_Test Item for Auto Price List"}
+		)
 		if item_price:
 			frappe.delete_doc("Item Price", item_price)
 
-		make_sales_order(item_code = "_Test Item for Auto Price List", selling_price_list="_Test Price List", rate=100)
+		make_sales_order(
+			item_code="_Test Item for Auto Price List", selling_price_list="_Test Price List", rate=100
+		)
 
-		self.assertEqual(frappe.db.get_value("Item Price",
-			{"price_list": "_Test Price List", "item_code": "_Test Item for Auto Price List"}, "price_list_rate"), 100)
+		self.assertEqual(
+			frappe.db.get_value(
+				"Item Price",
+				{"price_list": "_Test Price List", "item_code": "_Test Item for Auto Price List"},
+				"price_list_rate",
+			),
+			100,
+		)
 
+		make_sales_order(
+			item_code="_Test Item for Auto Price List with Discount Percentage",
+			selling_price_list="_Test Price List",
+			price_list_rate=200,
+			discount_percentage=20,
+		)
+
+		self.assertEqual(
+			frappe.db.get_value(
+				"Item Price",
+				{
+					"price_list": "_Test Price List",
+					"item_code": "_Test Item for Auto Price List with Discount Percentage",
+				},
+				"price_list_rate",
+			),
+			200,
+		)
 
 		# do not update price list
 		frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 0)
 
-		item_price = frappe.db.get_value("Item Price", {"price_list": "_Test Price List",
-			"item_code": "_Test Item for Auto Price List"})
+		item_price = frappe.db.get_value(
+			"Item Price", {"price_list": "_Test Price List", "item_code": "_Test Item for Auto Price List"}
+		)
 		if item_price:
 			frappe.delete_doc("Item Price", item_price)
 
-		make_sales_order(item_code = "_Test Item for Auto Price List", selling_price_list="_Test Price List", rate=100)
+		make_sales_order(
+			item_code="_Test Item for Auto Price List", selling_price_list="_Test Price List", rate=100
+		)
 
-		self.assertEqual(frappe.db.get_value("Item Price",
-			{"price_list": "_Test Price List", "item_code": "_Test Item for Auto Price List"}, "price_list_rate"), None)
+		self.assertEqual(
+			frappe.db.get_value(
+				"Item Price",
+				{"price_list": "_Test Price List", "item_code": "_Test Item for Auto Price List"},
+				"price_list_rate",
+			),
+			None,
+		)
 
 		frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 1)
 
@@ -753,7 +856,9 @@
 		from erpnext.selling.doctype.sales_order.sales_order import update_status as so_update_status
 
 		# make items
-		po_item = make_item("_Test Item for Drop Shipping", {"is_stock_item": 1, "delivered_by_supplier": 1})
+		po_item = make_item(
+			"_Test Item for Drop Shipping", {"is_stock_item": 1, "delivered_by_supplier": 1}
+		)
 		dn_item = make_item("_Test Regular Item", {"is_stock_item": 1})
 
 		so_items = [
@@ -763,21 +868,21 @@
 				"qty": 2,
 				"rate": 400,
 				"delivered_by_supplier": 1,
-				"supplier": '_Test Supplier'
+				"supplier": "_Test Supplier",
 			},
 			{
 				"item_code": dn_item.item_code,
 				"warehouse": "_Test Warehouse - _TC",
 				"qty": 2,
 				"rate": 300,
-				"conversion_factor": 1.0
-			}
+				"conversion_factor": 1.0,
+			},
 		]
 
-		if frappe.db.get_value("Item", "_Test Regular Item", "is_stock_item")==1:
+		if frappe.db.get_value("Item", "_Test Regular Item", "is_stock_item") == 1:
 			make_stock_entry(item="_Test Regular Item", target="_Test Warehouse - _TC", qty=2, rate=100)
 
-		#create so, po and dn
+		# create so, po and dn
 		so = make_sales_order(item_list=so_items, do_not_submit=True)
 		so.submit()
 
@@ -790,12 +895,15 @@
 		self.assertEqual(po.items[0].sales_order, so.name)
 		self.assertEqual(po.items[0].item_code, po_item.item_code)
 		self.assertEqual(dn.items[0].item_code, dn_item.item_code)
-		#test po_item length
+		# test po_item length
 		self.assertEqual(len(po.items), 1)
 
 		# test ordered_qty and reserved_qty for drop ship item
-		bin_po_item = frappe.get_all("Bin", filters={"item_code": po_item.item_code, "warehouse": "_Test Warehouse - _TC"},
-			fields=["ordered_qty", "reserved_qty"])
+		bin_po_item = frappe.get_all(
+			"Bin",
+			filters={"item_code": po_item.item_code, "warehouse": "_Test Warehouse - _TC"},
+			fields=["ordered_qty", "reserved_qty"],
+		)
 
 		ordered_qty = bin_po_item[0].ordered_qty if bin_po_item else 0.0
 		reserved_qty = bin_po_item[0].reserved_qty if bin_po_item else 0.0
@@ -810,12 +918,15 @@
 		po.load_from_db()
 
 		# test after closing so
-		so.db_set('status', "Closed")
+		so.db_set("status", "Closed")
 		so.update_reserved_qty()
 
 		# test ordered_qty and reserved_qty for drop ship item after closing so
-		bin_po_item = frappe.get_all("Bin", filters={"item_code": po_item.item_code, "warehouse": "_Test Warehouse - _TC"},
-			fields=["ordered_qty", "reserved_qty"])
+		bin_po_item = frappe.get_all(
+			"Bin",
+			filters={"item_code": po_item.item_code, "warehouse": "_Test Warehouse - _TC"},
+			fields=["ordered_qty", "reserved_qty"],
+		)
 
 		ordered_qty = bin_po_item[0].ordered_qty if bin_po_item else 0.0
 		reserved_qty = bin_po_item[0].reserved_qty if bin_po_item else 0.0
@@ -838,8 +949,12 @@
 		from erpnext.selling.doctype.sales_order.sales_order import update_status as so_update_status
 
 		# make items
-		po_item1 = make_item("_Test Item for Drop Shipping 1", {"is_stock_item": 1, "delivered_by_supplier": 1})
-		po_item2 = make_item("_Test Item for Drop Shipping 2", {"is_stock_item": 1, "delivered_by_supplier": 1})
+		po_item1 = make_item(
+			"_Test Item for Drop Shipping 1", {"is_stock_item": 1, "delivered_by_supplier": 1}
+		)
+		po_item2 = make_item(
+			"_Test Item for Drop Shipping 2", {"is_stock_item": 1, "delivered_by_supplier": 1}
+		)
 
 		so_items = [
 			{
@@ -848,7 +963,7 @@
 				"qty": 2,
 				"rate": 400,
 				"delivered_by_supplier": 1,
-				"supplier": '_Test Supplier'
+				"supplier": "_Test Supplier",
 			},
 			{
 				"item_code": po_item2.item_code,
@@ -856,8 +971,8 @@
 				"qty": 2,
 				"rate": 400,
 				"delivered_by_supplier": 1,
-				"supplier": '_Test Supplier'
-			}
+				"supplier": "_Test Supplier",
+			},
 		]
 
 		# create so and po
@@ -871,7 +986,7 @@
 		self.assertEqual(so.customer, po1.customer)
 		self.assertEqual(po1.items[0].sales_order, so.name)
 		self.assertEqual(po1.items[0].item_code, po_item1.item_code)
-		#test po item length
+		# test po item length
 		self.assertEqual(len(po1.items), 1)
 
 		# create po for remaining item
@@ -905,7 +1020,7 @@
 				"qty": 2,
 				"rate": 400,
 				"delivered_by_supplier": 1,
-				"supplier": '_Test Supplier'
+				"supplier": "_Test Supplier",
 			},
 			{
 				"item_code": "_Test Item for Drop Shipping 2",
@@ -913,8 +1028,8 @@
 				"qty": 2,
 				"rate": 400,
 				"delivered_by_supplier": 1,
-				"supplier": '_Test Supplier 1'
-			}
+				"supplier": "_Test Supplier 1",
+			},
 		]
 
 		# create so and po
@@ -924,13 +1039,13 @@
 		purchase_orders = make_purchase_order_for_default_supplier(so.name, selected_items=so_items)
 
 		self.assertEqual(len(purchase_orders), 2)
-		self.assertEqual(purchase_orders[0].supplier, '_Test Supplier')
-		self.assertEqual(purchase_orders[1].supplier, '_Test Supplier 1')
+		self.assertEqual(purchase_orders[0].supplier, "_Test Supplier")
+		self.assertEqual(purchase_orders[1].supplier, "_Test Supplier 1")
 
 	def test_product_bundles_in_so_are_replaced_with_bundle_items_in_po(self):
 		"""
-			Tests if the the Product Bundles in the Items table of Sales Orders are replaced with
-			their child items(from the Packed Items table) on creating a Purchase Order from it.
+		Tests if the the Product Bundles in the Items table of Sales Orders are replaced with
+		their child items(from the Packed Items table) on creating a Purchase Order from it.
 		"""
 		from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order
 
@@ -938,8 +1053,7 @@
 		make_item("_Test Bundle Item 1", {"is_stock_item": 1})
 		make_item("_Test Bundle Item 2", {"is_stock_item": 1})
 
-		make_product_bundle("_Test Product Bundle",
-			["_Test Bundle Item 1", "_Test Bundle Item 2"])
+		make_product_bundle("_Test Product Bundle", ["_Test Bundle Item 1", "_Test Bundle Item 2"])
 
 		so_items = [
 			{
@@ -948,7 +1062,7 @@
 				"qty": 2,
 				"rate": 400,
 				"delivered_by_supplier": 1,
-				"supplier": '_Test Supplier'
+				"supplier": "_Test Supplier",
 			}
 		]
 
@@ -961,7 +1075,7 @@
 
 	def test_purchase_order_updates_packed_item_ordered_qty(self):
 		"""
-			Tests if the packed item's `ordered_qty` is updated with the quantity of the Purchase Order
+		Tests if the packed item's `ordered_qty` is updated with the quantity of the Purchase Order
 		"""
 		from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order
 
@@ -969,8 +1083,7 @@
 		make_item("_Test Bundle Item 1", {"is_stock_item": 1})
 		make_item("_Test Bundle Item 2", {"is_stock_item": 1})
 
-		make_product_bundle("_Test Product Bundle",
-			["_Test Bundle Item 1", "_Test Bundle Item 2"])
+		make_product_bundle("_Test Product Bundle", ["_Test Bundle Item 1", "_Test Bundle Item 2"])
 
 		so_items = [
 			{
@@ -979,7 +1092,7 @@
 				"qty": 2,
 				"rate": 400,
 				"delivered_by_supplier": 1,
-				"supplier": '_Test Supplier'
+				"supplier": "_Test Supplier",
 			}
 		]
 
@@ -996,145 +1109,163 @@
 		self.assertEqual(so.packed_items[1].ordered_qty, 2)
 
 	def test_reserved_qty_for_closing_so(self):
-		bin = frappe.get_all("Bin", filters={"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"},
-			fields=["reserved_qty"])
+		bin = frappe.get_all(
+			"Bin",
+			filters={"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"},
+			fields=["reserved_qty"],
+		)
 
 		existing_reserved_qty = bin[0].reserved_qty if bin else 0.0
 
 		so = make_sales_order(item_code="_Test Item", qty=1)
 
-		self.assertEqual(get_reserved_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"), existing_reserved_qty+1)
+		self.assertEqual(
+			get_reserved_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"),
+			existing_reserved_qty + 1,
+		)
 
 		so.update_status("Closed")
 
-		self.assertEqual(get_reserved_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"), existing_reserved_qty)
+		self.assertEqual(
+			get_reserved_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"),
+			existing_reserved_qty,
+		)
 
 	def test_create_so_with_margin(self):
 		so = make_sales_order(item_code="_Test Item", qty=1, do_not_submit=True)
 		so.items[0].price_list_rate = price_list_rate = 100
-		so.items[0].margin_type = 'Percentage'
+		so.items[0].margin_type = "Percentage"
 		so.items[0].margin_rate_or_amount = 25
 		so.save()
 
 		new_so = frappe.copy_doc(so)
 		new_so.save(ignore_permissions=True)
 
-		self.assertEqual(new_so.get("items")[0].rate, flt((price_list_rate*25)/100 + price_list_rate))
+		self.assertEqual(
+			new_so.get("items")[0].rate, flt((price_list_rate * 25) / 100 + price_list_rate)
+		)
 		new_so.items[0].margin_rate_or_amount = 25
 		new_so.payment_schedule = []
 		new_so.save()
 		new_so.submit()
 
-		self.assertEqual(new_so.get("items")[0].rate, flt((price_list_rate*25)/100 + price_list_rate))
+		self.assertEqual(
+			new_so.get("items")[0].rate, flt((price_list_rate * 25) / 100 + price_list_rate)
+		)
 
 	def test_terms_auto_added(self):
 		so = make_sales_order(do_not_save=1)
 
-		self.assertFalse(so.get('payment_schedule'))
+		self.assertFalse(so.get("payment_schedule"))
 
 		so.insert()
 
-		self.assertTrue(so.get('payment_schedule'))
+		self.assertTrue(so.get("payment_schedule"))
 
 	def test_terms_not_copied(self):
 		so = make_sales_order()
-		self.assertTrue(so.get('payment_schedule'))
+		self.assertTrue(so.get("payment_schedule"))
 
 		si = make_sales_invoice(so.name)
-		self.assertFalse(si.get('payment_schedule'))
+		self.assertFalse(si.get("payment_schedule"))
 
 	def test_terms_copied(self):
 		so = make_sales_order(do_not_copy=1, do_not_save=1)
-		so.payment_terms_template = '_Test Payment Term Template'
+		so.payment_terms_template = "_Test Payment Term Template"
 		so.insert()
 		so.submit()
-		self.assertTrue(so.get('payment_schedule'))
+		self.assertTrue(so.get("payment_schedule"))
 
 		si = make_sales_invoice(so.name)
 		si.insert()
-		self.assertTrue(si.get('payment_schedule'))
+		self.assertTrue(si.get("payment_schedule"))
 
 	def test_make_work_order(self):
 		# Make a new Sales Order
-		so = make_sales_order(**{
-			"item_list": [{
-				"item_code": "_Test FG Item",
-				"qty": 10,
-				"rate":100
-			},
-			{
-				"item_code": "_Test FG Item",
-				"qty": 20,
-				"rate":200
-			}]
-		})
+		so = make_sales_order(
+			**{
+				"item_list": [
+					{"item_code": "_Test FG Item", "qty": 10, "rate": 100},
+					{"item_code": "_Test FG Item", "qty": 20, "rate": 200},
+				]
+			}
+		)
 
 		# Raise Work Orders
-		po_items= []
-		so_item_name= {}
+		po_items = []
+		so_item_name = {}
 		for item in so.get_work_order_items():
-			po_items.append({
-				"warehouse": item.get("warehouse"),
-				"item_code": item.get("item_code"),
-				"pending_qty": item.get("pending_qty"),
-				"sales_order_item": item.get("sales_order_item"),
-				"bom": item.get("bom"),
-				"description": item.get("description")
-			})
-			so_item_name[item.get("sales_order_item")]= item.get("pending_qty")
-		make_work_orders(json.dumps({"items":po_items}), so.name, so.company)
+			po_items.append(
+				{
+					"warehouse": item.get("warehouse"),
+					"item_code": item.get("item_code"),
+					"pending_qty": item.get("pending_qty"),
+					"sales_order_item": item.get("sales_order_item"),
+					"bom": item.get("bom"),
+					"description": item.get("description"),
+				}
+			)
+			so_item_name[item.get("sales_order_item")] = item.get("pending_qty")
+		make_work_orders(json.dumps({"items": po_items}), so.name, so.company)
 
 		# Check if Work Orders were raised
 		for item in so_item_name:
-			wo_qty = frappe.db.sql("select sum(qty) from `tabWork Order` where sales_order=%s and sales_order_item=%s", (so.name, item))
+			wo_qty = frappe.db.sql(
+				"select sum(qty) from `tabWork Order` where sales_order=%s and sales_order_item=%s",
+				(so.name, item),
+			)
 			self.assertEqual(wo_qty[0][0], so_item_name.get(item))
 
 	def test_serial_no_based_delivery(self):
 		frappe.set_value("Stock Settings", None, "automatically_set_serial_nos_based_on_fifo", 1)
-		item = make_item("_Reserved_Serialized_Item", {"is_stock_item": 1,
-					"maintain_stock": 1,
-					"has_serial_no": 1,
-					"serial_no_series": "SI.####",
-					"valuation_rate": 500,
-					"item_defaults": [
-						{
-							"default_warehouse": "_Test Warehouse - _TC",
-							"company": "_Test Company"
-						}]
-					})
+		item = make_item(
+			"_Reserved_Serialized_Item",
+			{
+				"is_stock_item": 1,
+				"maintain_stock": 1,
+				"has_serial_no": 1,
+				"serial_no_series": "SI.####",
+				"valuation_rate": 500,
+				"item_defaults": [{"default_warehouse": "_Test Warehouse - _TC", "company": "_Test Company"}],
+			},
+		)
 		frappe.db.sql("""delete from `tabSerial No` where item_code=%s""", (item.item_code))
-		make_item("_Test Item A", {"maintain_stock": 1,
-					"valuation_rate": 100,
-					"item_defaults": [
-						{
-							"default_warehouse": "_Test Warehouse - _TC",
-							"company": "_Test Company"
-						}]
-					})
-		make_item("_Test Item B", {"maintain_stock": 1,
-					"valuation_rate": 200,
-					"item_defaults": [
-						{
-							"default_warehouse": "_Test Warehouse - _TC",
-							"company": "_Test Company"
-						}]
-					})
+		make_item(
+			"_Test Item A",
+			{
+				"maintain_stock": 1,
+				"valuation_rate": 100,
+				"item_defaults": [{"default_warehouse": "_Test Warehouse - _TC", "company": "_Test Company"}],
+			},
+		)
+		make_item(
+			"_Test Item B",
+			{
+				"maintain_stock": 1,
+				"valuation_rate": 200,
+				"item_defaults": [{"default_warehouse": "_Test Warehouse - _TC", "company": "_Test Company"}],
+			},
+		)
 		from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
-		make_bom(item=item.item_code, rate=1000,
-			raw_materials = ['_Test Item A', '_Test Item B'])
 
-		so = make_sales_order(**{
-			"item_list": [{
-				"item_code": item.item_code,
-				"ensure_delivery_based_on_produced_serial_no": 1,
-				"qty": 1,
-				"rate":1000
-			}]
-		})
+		make_bom(item=item.item_code, rate=1000, raw_materials=["_Test Item A", "_Test Item B"])
+
+		so = make_sales_order(
+			**{
+				"item_list": [
+					{
+						"item_code": item.item_code,
+						"ensure_delivery_based_on_produced_serial_no": 1,
+						"qty": 1,
+						"rate": 1000,
+					}
+				]
+			}
+		)
 		so.submit()
 		from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
-		work_order = make_wo_order_test_record(item=item.item_code,
-			qty=1, do_not_save=True)
+
+		work_order = make_wo_order_test_record(item=item.item_code, qty=1, do_not_save=True)
 		work_order.fg_warehouse = "_Test Warehouse - _TC"
 		work_order.sales_order = so.name
 		work_order.submit()
@@ -1143,6 +1274,7 @@
 		from erpnext.manufacturing.doctype.work_order.work_order import (
 			make_stock_entry as make_production_stock_entry,
 		)
+
 		se = frappe.get_doc(make_production_stock_entry(work_order.name, "Manufacture", 1))
 		se.submit()
 		reserved_serial_no = se.get("items")[2].serial_no
@@ -1154,7 +1286,7 @@
 		item_line = dn.get("items")[0]
 		item_line.serial_no = item_serial_no.name
 		item_line = dn.get("items")[0]
-		item_line.serial_no =  reserved_serial_no
+		item_line.serial_no = reserved_serial_no
 		dn.submit()
 		dn.load_from_db()
 		dn.cancel()
@@ -1177,6 +1309,7 @@
 		from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
 			make_delivery_note as make_delivery_note_from_invoice,
 		)
+
 		dn = make_delivery_note_from_invoice(si.name)
 		dn.save()
 		dn.submit()
@@ -1191,8 +1324,10 @@
 
 	def test_advance_payment_entry_unlink_against_sales_order(self):
 		from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
-		frappe.db.set_value("Accounts Settings", "Accounts Settings",
-			"unlink_advance_payment_on_cancelation_of_order", 0)
+
+		frappe.db.set_value(
+			"Accounts Settings", "Accounts Settings", "unlink_advance_payment_on_cancelation_of_order", 0
+		)
 
 		so = make_sales_order()
 
@@ -1207,7 +1342,7 @@
 		pe.save(ignore_permissions=True)
 		pe.submit()
 
-		so_doc = frappe.get_doc('Sales Order', so.name)
+		so_doc = frappe.get_doc("Sales Order", so.name)
 
 		self.assertRaises(frappe.LinkExistsError, so_doc.cancel)
 
@@ -1218,8 +1353,9 @@
 		so = make_sales_order()
 
 		# disable unlinking of payment entry
-		frappe.db.set_value("Accounts Settings", "Accounts Settings",
-			"unlink_advance_payment_on_cancelation_of_order", 0)
+		frappe.db.set_value(
+			"Accounts Settings", "Accounts Settings", "unlink_advance_payment_on_cancelation_of_order", 0
+		)
 
 		# create a payment entry against sales order
 		pe = get_payment_entry("Sales Order", so.name, bank_account="_Test Bank - _TC")
@@ -1239,81 +1375,130 @@
 
 		# Cancel sales order
 		try:
-			so_doc = frappe.get_doc('Sales Order', so.name)
+			so_doc = frappe.get_doc("Sales Order", so.name)
 			so_doc.cancel()
 		except Exception:
 			self.fail("Can not cancel sales order with linked cancelled payment entry")
 
-	def test_request_for_raw_materials(self):
-		item = make_item("_Test Finished Item", {"is_stock_item": 1,
-			"maintain_stock": 1,
-			"valuation_rate": 500,
-			"item_defaults": [
-				{
-					"default_warehouse": "_Test Warehouse - _TC",
-					"company": "_Test Company"
-				}]
-			})
-		make_item("_Test Raw Item A", {"maintain_stock": 1,
-					"valuation_rate": 100,
-					"item_defaults": [
-						{
-							"default_warehouse": "_Test Warehouse - _TC",
-							"company": "_Test Company"
-						}]
-					})
-		make_item("_Test Raw Item B", {"maintain_stock": 1,
-					"valuation_rate": 200,
-					"item_defaults": [
-						{
-							"default_warehouse": "_Test Warehouse - _TC",
-							"company": "_Test Company"
-						}]
-					})
-		from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
-		make_bom(item=item.item_code, rate=1000,
-			raw_materials = ['_Test Raw Item A', '_Test Raw Item B'])
+	def test_work_order_pop_up_from_sales_order(self):
+		"Test `get_work_order_items` in Sales Order picks the right BOM for items to manufacture."
 
-		so = make_sales_order(**{
-			"item_list": [{
-				"item_code": item.item_code,
-				"qty": 1,
-				"rate":1000
-			}]
-		})
+		from erpnext.controllers.item_variant import create_variant
+		from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
+
+		make_item(  # template item
+			"Test-WO-Tshirt",
+			{
+				"has_variant": 1,
+				"variant_based_on": "Item Attribute",
+				"attributes": [{"attribute": "Test Colour"}],
+			},
+		)
+		make_item("Test-RM-Cotton")  # RM for BOM
+
+		for colour in (
+			"Red",
+			"Green",
+		):
+			variant = create_variant("Test-WO-Tshirt", {"Test Colour": colour})
+			variant.save()
+
+		template_bom = make_bom(item="Test-WO-Tshirt", rate=100, raw_materials=["Test-RM-Cotton"])
+		red_var_bom = make_bom(item="Test-WO-Tshirt-R", rate=100, raw_materials=["Test-RM-Cotton"])
+
+		so = make_sales_order(
+			**{
+				"item_list": [
+					{
+						"item_code": "Test-WO-Tshirt-R",
+						"qty": 1,
+						"rate": 1000,
+						"warehouse": "_Test Warehouse - _TC",
+					},
+					{
+						"item_code": "Test-WO-Tshirt-G",
+						"qty": 1,
+						"rate": 1000,
+						"warehouse": "_Test Warehouse - _TC",
+					},
+				]
+			}
+		)
+		wo_items = so.get_work_order_items()
+
+		self.assertEqual(wo_items[0].get("item_code"), "Test-WO-Tshirt-R")
+		self.assertEqual(wo_items[0].get("bom"), red_var_bom.name)
+
+		# Must pick Template Item BOM for Test-WO-Tshirt-G as it has no BOM
+		self.assertEqual(wo_items[1].get("item_code"), "Test-WO-Tshirt-G")
+		self.assertEqual(wo_items[1].get("bom"), template_bom.name)
+
+	def test_request_for_raw_materials(self):
+		item = make_item(
+			"_Test Finished Item",
+			{
+				"is_stock_item": 1,
+				"maintain_stock": 1,
+				"valuation_rate": 500,
+				"item_defaults": [{"default_warehouse": "_Test Warehouse - _TC", "company": "_Test Company"}],
+			},
+		)
+		make_item(
+			"_Test Raw Item A",
+			{
+				"maintain_stock": 1,
+				"valuation_rate": 100,
+				"item_defaults": [{"default_warehouse": "_Test Warehouse - _TC", "company": "_Test Company"}],
+			},
+		)
+		make_item(
+			"_Test Raw Item B",
+			{
+				"maintain_stock": 1,
+				"valuation_rate": 200,
+				"item_defaults": [{"default_warehouse": "_Test Warehouse - _TC", "company": "_Test Company"}],
+			},
+		)
+		from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
+
+		make_bom(item=item.item_code, rate=1000, raw_materials=["_Test Raw Item A", "_Test Raw Item B"])
+
+		so = make_sales_order(**{"item_list": [{"item_code": item.item_code, "qty": 1, "rate": 1000}]})
 		so.submit()
 		mr_dict = frappe._dict()
 		items = so.get_work_order_items(1)
-		mr_dict['items'] = items
-		mr_dict['include_exploded_items'] = 0
-		mr_dict['ignore_existing_ordered_qty'] = 1
+		mr_dict["items"] = items
+		mr_dict["include_exploded_items"] = 0
+		mr_dict["ignore_existing_ordered_qty"] = 1
 		make_raw_material_request(mr_dict, so.company, so.name)
-		mr = frappe.db.sql("""select name from `tabMaterial Request` ORDER BY creation DESC LIMIT 1""", as_dict=1)[0]
-		mr_doc = frappe.get_doc('Material Request',mr.get('name'))
+		mr = frappe.db.sql(
+			"""select name from `tabMaterial Request` ORDER BY creation DESC LIMIT 1""", as_dict=1
+		)[0]
+		mr_doc = frappe.get_doc("Material Request", mr.get("name"))
 		self.assertEqual(mr_doc.items[0].sales_order, so.name)
 
 	def test_so_optional_blanket_order(self):
 		"""
-			Expected result: Blanket order Ordered Quantity should only be affected on Sales Order with against_blanket_order = 1.
-			Second Sales Order should not add on to Blanket Orders Ordered Quantity.
+		Expected result: Blanket order Ordered Quantity should only be affected on Sales Order with against_blanket_order = 1.
+		Second Sales Order should not add on to Blanket Orders Ordered Quantity.
 		"""
 
-		bo = make_blanket_order(blanket_order_type = "Selling", quantity = 10, rate = 10)
+		bo = make_blanket_order(blanket_order_type="Selling", quantity=10, rate=10)
 
-		so = make_sales_order(item_code= "_Test Item", qty = 5, against_blanket_order = 1)
-		so_doc = frappe.get_doc('Sales Order', so.get('name'))
+		so = make_sales_order(item_code="_Test Item", qty=5, against_blanket_order=1)
+		so_doc = frappe.get_doc("Sales Order", so.get("name"))
 		# To test if the SO has a Blanket Order
 		self.assertTrue(so_doc.items[0].blanket_order)
 
-		so = make_sales_order(item_code= "_Test Item", qty = 5, against_blanket_order = 0)
-		so_doc = frappe.get_doc('Sales Order', so.get('name'))
+		so = make_sales_order(item_code="_Test Item", qty=5, against_blanket_order=0)
+		so_doc = frappe.get_doc("Sales Order", so.get("name"))
 		# To test if the SO does NOT have a Blanket Order
 		self.assertEqual(so_doc.items[0].blanket_order, None)
 
 	def test_so_cancellation_when_si_drafted(self):
 		"""
-			Test to check if Sales Order gets cancelled if Sales Invoice is in Draft state
-			Expected result: sales order should not get cancelled
+		Test to check if Sales Order gets cancelled if Sales Invoice is in Draft state
+		Expected result: sales order should not get cancelled
 		"""
 		so = make_sales_order()
 		so.submit()
@@ -1324,8 +1509,8 @@
 
 	def test_so_cancellation_after_si_submission(self):
 		"""
-			Test to check if Sales Order gets cancelled when linked Sales Invoice has been Submitted
-			Expected result: Sales Order should not get cancelled
+		Test to check if Sales Order gets cancelled when linked Sales Invoice has been Submitted
+		Expected result: Sales Order should not get cancelled
 		"""
 		so = make_sales_order()
 		so.submit()
@@ -1337,8 +1522,8 @@
 
 	def test_so_cancellation_after_dn_submission(self):
 		"""
-			Test to check if Sales Order gets cancelled when linked Delivery Note has been Submitted
-			Expected result: Sales Order should not get cancelled
+		Test to check if Sales Order gets cancelled when linked Delivery Note has been Submitted
+		Expected result: Sales Order should not get cancelled
 		"""
 		so = make_sales_order()
 		so.submit()
@@ -1350,7 +1535,7 @@
 
 	def test_so_cancellation_after_maintenance_schedule_submission(self):
 		"""
-			Expected result: Sales Order should not get cancelled
+		Expected result: Sales Order should not get cancelled
 		"""
 		so = make_sales_order()
 		so.submit()
@@ -1363,7 +1548,7 @@
 
 	def test_so_cancellation_after_maintenance_visit_submission(self):
 		"""
-			Expected result: Sales Order should not get cancelled
+		Expected result: Sales Order should not get cancelled
 		"""
 		so = make_sales_order()
 		so.submit()
@@ -1377,7 +1562,7 @@
 
 	def test_so_cancellation_after_work_order_submission(self):
 		"""
-			Expected result: Sales Order should not get cancelled
+		Expected result: Sales Order should not get cancelled
 		"""
 		from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
 
@@ -1398,7 +1583,7 @@
 
 		so = make_sales_order(uom="Nos", do_not_save=1)
 		create_payment_terms_template()
-		so.payment_terms_template = 'Test Receivable Template'
+		so.payment_terms_template = "Test Receivable Template"
 		so.submit()
 
 		si = create_sales_invoice(qty=10, do_not_save=1)
@@ -1420,10 +1605,10 @@
 		so.submit()
 
 		self.assertEqual(so.net_total, 0)
-		self.assertEqual(so.billing_status, 'Not Billed')
+		self.assertEqual(so.billing_status, "Not Billed")
 
 		si = create_sales_invoice(qty=10, do_not_save=1)
-		si.price_list = '_Test Price List'
+		si.price_list = "_Test Price List"
 		si.items[0].rate = 0
 		si.items[0].price_list_rate = 0
 		si.items[0].sales_order = so.name
@@ -1433,7 +1618,7 @@
 
 		self.assertEqual(si.net_total, 0)
 		so.load_from_db()
-		self.assertEqual(so.billing_status, 'Fully Billed')
+		self.assertEqual(so.billing_status, "Fully Billed")
 
 	def test_so_back_updated_from_wo_via_mr(self):
 		"SO -> MR (Manufacture) -> WO. Test if WO Qty is updated in SO."
@@ -1442,7 +1627,7 @@
 		)
 		from erpnext.stock.doctype.material_request.material_request import raise_work_orders
 
-		so = make_sales_order(item_list=[{"item_code": "_Test FG Item","qty": 2, "rate":100}])
+		so = make_sales_order(item_list=[{"item_code": "_Test FG Item", "qty": 2, "rate": 100}])
 
 		mr = make_material_request(so.name)
 		mr.material_request_type = "Manufacture"
@@ -1459,17 +1644,18 @@
 		self.assertEqual(wo.sales_order_item, so.items[0].name)
 
 		wo.submit()
-		make_stock_entry(item_code="_Test Item", # Stock RM
-			target="Work In Progress - _TC",
-			qty=4, basic_rate=100
+		make_stock_entry(
+			item_code="_Test Item", target="Work In Progress - _TC", qty=4, basic_rate=100  # Stock RM
 		)
-		make_stock_entry(item_code="_Test Item Home Desktop 100", # Stock RM
+		make_stock_entry(
+			item_code="_Test Item Home Desktop 100",  # Stock RM
 			target="Work In Progress - _TC",
-			qty=4, basic_rate=100
+			qty=4,
+			basic_rate=100,
 		)
 
 		se = frappe.get_doc(make_se_from_wo(wo.name, "Manufacture", 2))
-		se.submit() # Finish WO
+		se.submit()  # Finish WO
 
 		mr.reload()
 		wo.reload()
@@ -1477,29 +1663,57 @@
 		self.assertEqual(so.items[0].work_order_qty, wo.produced_qty)
 		self.assertEqual(mr.status, "Manufactured")
 
+	def test_sales_order_with_shipping_rule(self):
+		from erpnext.accounts.doctype.shipping_rule.test_shipping_rule import create_shipping_rule
+
+		shipping_rule = create_shipping_rule(
+			shipping_rule_type="Selling", shipping_rule_name="Shipping Rule - Sales Invoice Test"
+		)
+		sales_order = make_sales_order(do_not_save=True)
+		sales_order.shipping_rule = shipping_rule.name
+
+		sales_order.items[0].qty = 1
+		sales_order.save()
+		self.assertEqual(sales_order.taxes[0].tax_amount, 50)
+
+		sales_order.items[0].qty = 2
+		sales_order.save()
+		self.assertEqual(sales_order.taxes[0].tax_amount, 100)
+
+		sales_order.items[0].qty = 3
+		sales_order.save()
+		self.assertEqual(sales_order.taxes[0].tax_amount, 200)
+
+		sales_order.items[0].qty = 21
+		sales_order.save()
+		self.assertEqual(sales_order.taxes[0].tax_amount, 0)
+
+
 def automatically_fetch_payment_terms(enable=1):
 	accounts_settings = frappe.get_doc("Accounts Settings")
 	accounts_settings.automatically_fetch_payment_terms = enable
 	accounts_settings.save()
 
+
 def compare_payment_schedules(doc, doc1, doc2):
-	for index, schedule in enumerate(doc1.get('payment_schedule')):
+	for index, schedule in enumerate(doc1.get("payment_schedule")):
 		doc.assertEqual(schedule.payment_term, doc2.payment_schedule[index].payment_term)
 		doc.assertEqual(getdate(schedule.due_date), doc2.payment_schedule[index].due_date)
 		doc.assertEqual(schedule.invoice_portion, doc2.payment_schedule[index].invoice_portion)
 		doc.assertEqual(schedule.payment_amount, doc2.payment_schedule[index].payment_amount)
 
+
 def make_sales_order(**args):
 	so = frappe.new_doc("Sales Order")
 	args = frappe._dict(args)
 	if args.transaction_date:
 		so.transaction_date = args.transaction_date
 
-	so.set_warehouse = "" # no need to test set_warehouse permission since it only affects the client
+	so.set_warehouse = ""  # no need to test set_warehouse permission since it only affects the client
 	so.company = args.company or "_Test Company"
 	so.customer = args.customer or "_Test Customer"
 	so.currency = args.currency or "INR"
-	so.po_no = args.po_no or '12345'
+	so.po_no = args.po_no or "12345"
 	if args.selling_price_list:
 		so.selling_price_list = args.selling_price_list
 
@@ -1511,14 +1725,19 @@
 			so.append("items", item)
 
 	else:
-		so.append("items", {
-			"item_code": args.item or args.item_code or "_Test Item",
-			"warehouse": args.warehouse,
-			"qty": args.qty or 10,
-			"uom": args.uom or None,
-			"rate": args.rate or 100,
-			"against_blanket_order": args.against_blanket_order
-		})
+		so.append(
+			"items",
+			{
+				"item_code": args.item or args.item_code or "_Test Item",
+				"warehouse": args.warehouse,
+				"qty": args.qty or 10,
+				"uom": args.uom or None,
+				"price_list_rate": args.price_list_rate or None,
+				"discount_percentage": args.discount_percentage or None,
+				"rate": args.rate or (None if args.price_list_rate else 100),
+				"against_blanket_order": args.against_blanket_order,
+			},
+		)
 
 	so.delivery_date = add_days(so.transaction_date, 10)
 
@@ -1533,6 +1752,7 @@
 
 	return so
 
+
 def create_dn_against_so(so, delivered_qty=0):
 	frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
 
@@ -1542,41 +1762,63 @@
 	dn.submit()
 	return dn
 
+
 def get_reserved_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"):
-	return flt(frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse},
-		"reserved_qty"))
+	return flt(
+		frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "reserved_qty")
+	)
+
 
 test_dependencies = ["Currency Exchange"]
 
+
 def make_sales_order_workflow():
-	if frappe.db.exists('Workflow', 'SO Test Workflow'):
+	if frappe.db.exists("Workflow", "SO Test Workflow"):
 		doc = frappe.get_doc("Workflow", "SO Test Workflow")
 		doc.set("is_active", 1)
 		doc.save()
 		return doc
 
-	frappe.get_doc(dict(doctype='Role', role_name='Test Junior Approver')).insert(ignore_if_duplicate=True)
-	frappe.get_doc(dict(doctype='Role', role_name='Test Approver')).insert(ignore_if_duplicate=True)
-	frappe.cache().hdel('roles', frappe.session.user)
+	frappe.get_doc(dict(doctype="Role", role_name="Test Junior Approver")).insert(
+		ignore_if_duplicate=True
+	)
+	frappe.get_doc(dict(doctype="Role", role_name="Test Approver")).insert(ignore_if_duplicate=True)
+	frappe.cache().hdel("roles", frappe.session.user)
 
-	workflow = frappe.get_doc({
-		"doctype": "Workflow",
-		"workflow_name": "SO Test Workflow",
-		"document_type": "Sales Order",
-		"workflow_state_field": "workflow_state",
-		"is_active": 1,
-		"send_email_alert": 0,
-	})
-	workflow.append('states', dict( state = 'Pending', allow_edit = 'All' ))
-	workflow.append('states', dict( state = 'Approved', allow_edit = 'Test Approver', doc_status = 1 ))
-	workflow.append('transitions', dict(
-		state = 'Pending', action = 'Approve', next_state = 'Approved', allowed = 'Test Junior Approver', allow_self_approval = 1,
-		condition = 'doc.grand_total < 200'
-	))
-	workflow.append('transitions', dict(
-		state = 'Pending', action = 'Approve', next_state = 'Approved', allowed = 'Test Approver', allow_self_approval = 1,
-		 condition = 'doc.grand_total > 200'
-	))
+	workflow = frappe.get_doc(
+		{
+			"doctype": "Workflow",
+			"workflow_name": "SO Test Workflow",
+			"document_type": "Sales Order",
+			"workflow_state_field": "workflow_state",
+			"is_active": 1,
+			"send_email_alert": 0,
+		}
+	)
+	workflow.append("states", dict(state="Pending", allow_edit="All"))
+	workflow.append("states", dict(state="Approved", allow_edit="Test Approver", doc_status=1))
+	workflow.append(
+		"transitions",
+		dict(
+			state="Pending",
+			action="Approve",
+			next_state="Approved",
+			allowed="Test Junior Approver",
+			allow_self_approval=1,
+			condition="doc.grand_total < 200",
+		),
+	)
+	workflow.append(
+		"transitions",
+		dict(
+			state="Pending",
+			action="Approve",
+			next_state="Approved",
+			allowed="Test Approver",
+			allow_self_approval=1,
+			condition="doc.grand_total > 200",
+		),
+	)
 	workflow.insert(ignore_permissions=True)
 
 	return workflow
diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
index 7e55499..3187999 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -86,6 +86,7 @@
   "delivered_qty",
   "produced_qty",
   "returned_qty",
+  "picked_qty",
   "shopping_cart_section",
   "additional_notes",
   "section_break_63",
@@ -197,6 +198,7 @@
    "width": "100px"
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "stock_uom",
    "fieldtype": "Link",
    "label": "Stock UOM",
@@ -219,6 +221,7 @@
    "reqd": 1
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "conversion_factor",
    "fieldtype": "Float",
    "label": "UOM Conversion Factor",
@@ -227,6 +230,7 @@
    "reqd": 1
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "stock_qty",
    "fieldtype": "Float",
    "label": "Qty as per Stock UOM",
@@ -798,12 +802,19 @@
    "fieldtype": "Check",
    "label": "Grant Commission",
    "read_only": 1
+  },
+  {
+   "fieldname": "picked_qty",
+   "fieldtype": "Float",
+   "label": "Picked Qty (in Stock UOM)",
+   "no_copy": 1,
+   "read_only": 1
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2022-02-24 14:41:57.325799",
+ "modified": "2022-06-17 05:27:41.603006",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order Item",
diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.py b/erpnext/selling/doctype/sales_order_item/sales_order_item.py
index 441a6ac..83d3f3b 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.py
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.py
@@ -9,5 +9,6 @@
 class SalesOrderItem(Document):
 	pass
 
+
 def on_doctype_update():
 	frappe.db.add_index("Sales Order Item", ["item_code", "warehouse"])
diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.json b/erpnext/selling/doctype/selling_settings/selling_settings.json
index 7c4a3f6..2abb169 100644
--- a/erpnext/selling/doctype/selling_settings/selling_settings.json
+++ b/erpnext/selling/doctype/selling_settings/selling_settings.json
@@ -27,7 +27,8 @@
   "column_break_5",
   "allow_multiple_items",
   "allow_against_multiple_purchase_orders",
-  "hide_tax_id"
+  "hide_tax_id",
+  "enable_discount_accounting"
  ],
  "fields": [
   {
@@ -164,6 +165,13 @@
    "fieldname": "editable_bundle_item_rates",
    "fieldtype": "Check",
    "label": "Calculate Product Bundle Price based on Child Items' Rates"
+  },
+  {
+   "default": "0",
+   "description": "If enabled, additional ledger entries will be made for discounts in a separate Discount Account",
+   "fieldname": "enable_discount_accounting",
+   "fieldtype": "Check",
+   "label": "Enable Discount Accounting for Selling"
   }
  ],
  "icon": "fa fa-cog",
@@ -171,7 +179,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2022-02-04 15:41:59.939261",
+ "modified": "2022-05-31 19:39:48.398738",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Selling Settings",
@@ -185,6 +193,15 @@
    "role": "System Manager",
    "share": 1,
    "write": 1
+  },
+  {
+   "create": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "role": "Sales Manager",
+   "share": 1,
+   "write": 1
   }
  ],
  "sort_field": "modified",
diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.py b/erpnext/selling/doctype/selling_settings/selling_settings.py
index e1ef635..d977807 100644
--- a/erpnext/selling/doctype/selling_settings/selling_settings.py
+++ b/erpnext/selling/doctype/selling_settings/selling_settings.py
@@ -14,25 +14,105 @@
 	def on_update(self):
 		self.toggle_hide_tax_id()
 		self.toggle_editable_rate_for_bundle_items()
+		self.toggle_discount_accounting_fields()
 
 	def validate(self):
-		for key in ["cust_master_name", "customer_group", "territory",
-			"maintain_same_sales_rate", "editable_price_list_rate", "selling_price_list"]:
-				frappe.db.set_default(key, self.get(key, ""))
+		for key in [
+			"cust_master_name",
+			"customer_group",
+			"territory",
+			"maintain_same_sales_rate",
+			"editable_price_list_rate",
+			"selling_price_list",
+		]:
+			frappe.db.set_default(key, self.get(key, ""))
 
-		from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
-		set_by_naming_series("Customer", "customer_name",
-			self.get("cust_master_name")=="Naming Series", hide_name_field=False)
+		from erpnext.utilities.naming import set_by_naming_series
+
+		set_by_naming_series(
+			"Customer",
+			"customer_name",
+			self.get("cust_master_name") == "Naming Series",
+			hide_name_field=False,
+		)
 
 	def toggle_hide_tax_id(self):
 		self.hide_tax_id = cint(self.hide_tax_id)
 
 		# Make property setters to hide tax_id fields
 		for doctype in ("Sales Order", "Sales Invoice", "Delivery Note"):
-			make_property_setter(doctype, "tax_id", "hidden", self.hide_tax_id, "Check", validate_fields_for_doctype=False)
-			make_property_setter(doctype, "tax_id", "print_hide", self.hide_tax_id, "Check", validate_fields_for_doctype=False)
+			make_property_setter(
+				doctype, "tax_id", "hidden", self.hide_tax_id, "Check", validate_fields_for_doctype=False
+			)
+			make_property_setter(
+				doctype, "tax_id", "print_hide", self.hide_tax_id, "Check", validate_fields_for_doctype=False
+			)
 
 	def toggle_editable_rate_for_bundle_items(self):
 		editable_bundle_item_rates = cint(self.editable_bundle_item_rates)
 
-		make_property_setter("Packed Item", "rate", "read_only", not(editable_bundle_item_rates), "Check", validate_fields_for_doctype=False)
+		make_property_setter(
+			"Packed Item",
+			"rate",
+			"read_only",
+			not (editable_bundle_item_rates),
+			"Check",
+			validate_fields_for_doctype=False,
+		)
+
+	def toggle_discount_accounting_fields(self):
+		enable_discount_accounting = cint(self.enable_discount_accounting)
+
+		make_property_setter(
+			"Sales Invoice Item",
+			"discount_account",
+			"hidden",
+			not (enable_discount_accounting),
+			"Check",
+			validate_fields_for_doctype=False,
+		)
+		if enable_discount_accounting:
+			make_property_setter(
+				"Sales Invoice Item",
+				"discount_account",
+				"mandatory_depends_on",
+				"eval: doc.discount_amount",
+				"Code",
+				validate_fields_for_doctype=False,
+			)
+		else:
+			make_property_setter(
+				"Sales Invoice Item",
+				"discount_account",
+				"mandatory_depends_on",
+				"",
+				"Code",
+				validate_fields_for_doctype=False,
+			)
+
+		make_property_setter(
+			"Sales Invoice",
+			"additional_discount_account",
+			"hidden",
+			not (enable_discount_accounting),
+			"Check",
+			validate_fields_for_doctype=False,
+		)
+		if enable_discount_accounting:
+			make_property_setter(
+				"Sales Invoice",
+				"additional_discount_account",
+				"mandatory_depends_on",
+				"eval: doc.discount_amount",
+				"Code",
+				validate_fields_for_doctype=False,
+			)
+		else:
+			make_property_setter(
+				"Sales Invoice",
+				"additional_discount_account",
+				"mandatory_depends_on",
+				"",
+				"Code",
+				validate_fields_for_doctype=False,
+			)
diff --git a/erpnext/selling/doctype/selling_settings/test_selling_settings.py b/erpnext/selling/doctype/selling_settings/test_selling_settings.py
index fc6754a..7290e68 100644
--- a/erpnext/selling/doctype/selling_settings/test_selling_settings.py
+++ b/erpnext/selling/doctype/selling_settings/test_selling_settings.py
@@ -1,9 +1,14 @@
 # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 
-# import frappe
 import unittest
 
+import frappe
+
 
 class TestSellingSettings(unittest.TestCase):
-	pass
+	def test_defaults_populated(self):
+		# Setup default values are not populated on migrate, this test checks
+		# if setup was completed correctly
+		default = frappe.db.get_single_value("Selling Settings", "maintain_same_rate_action")
+		self.assertEqual("Stop", default)
diff --git a/erpnext/selling/doctype/sms_center/sms_center.py b/erpnext/selling/doctype/sms_center/sms_center.py
index d192457..cdc7397 100644
--- a/erpnext/selling/doctype/sms_center/sms_center.py
+++ b/erpnext/selling/doctype/sms_center/sms_center.py
@@ -12,59 +12,80 @@
 class SMSCenter(Document):
 	@frappe.whitelist()
 	def create_receiver_list(self):
-		rec, where_clause = '', ''
-		if self.send_to == 'All Customer Contact':
+		rec, where_clause = "", ""
+		if self.send_to == "All Customer Contact":
 			where_clause = " and dl.link_doctype = 'Customer'"
 			if self.customer:
-				where_clause += " and dl.link_name = '%s'" % \
-					self.customer.replace("'", "\'") or " and ifnull(dl.link_name, '') != ''"
-		if self.send_to == 'All Supplier Contact':
+				where_clause += (
+					" and dl.link_name = '%s'" % self.customer.replace("'", "'")
+					or " and ifnull(dl.link_name, '') != ''"
+				)
+		if self.send_to == "All Supplier Contact":
 			where_clause = " and dl.link_doctype = 'Supplier'"
 			if self.supplier:
-				where_clause += " and dl.link_name = '%s'" % \
-					self.supplier.replace("'", "\'") or " and ifnull(dl.link_name, '') != ''"
-		if self.send_to == 'All Sales Partner Contact':
+				where_clause += (
+					" and dl.link_name = '%s'" % self.supplier.replace("'", "'")
+					or " and ifnull(dl.link_name, '') != ''"
+				)
+		if self.send_to == "All Sales Partner Contact":
 			where_clause = " and dl.link_doctype = 'Sales Partner'"
 			if self.sales_partner:
-				where_clause += "and dl.link_name = '%s'" % \
-					self.sales_partner.replace("'", "\'") or " and ifnull(dl.link_name, '') != ''"
-		if self.send_to in ['All Contact', 'All Customer Contact', 'All Supplier Contact', 'All Sales Partner Contact']:
-			rec = frappe.db.sql("""select CONCAT(ifnull(c.first_name,''), ' ', ifnull(c.last_name,'')),
+				where_clause += (
+					"and dl.link_name = '%s'" % self.sales_partner.replace("'", "'")
+					or " and ifnull(dl.link_name, '') != ''"
+				)
+		if self.send_to in [
+			"All Contact",
+			"All Customer Contact",
+			"All Supplier Contact",
+			"All Sales Partner Contact",
+		]:
+			rec = frappe.db.sql(
+				"""select CONCAT(ifnull(c.first_name,''), ' ', ifnull(c.last_name,'')),
 				c.mobile_no from `tabContact` c, `tabDynamic Link` dl  where ifnull(c.mobile_no,'')!='' and
-				c.docstatus != 2 and dl.parent = c.name%s""" % where_clause)
+				c.docstatus != 2 and dl.parent = c.name%s"""
+				% where_clause
+			)
 
-		elif self.send_to == 'All Lead (Open)':
-			rec = frappe.db.sql("""select lead_name, mobile_no from `tabLead` where
-				ifnull(mobile_no,'')!='' and docstatus != 2 and status='Open'""")
+		elif self.send_to == "All Lead (Open)":
+			rec = frappe.db.sql(
+				"""select lead_name, mobile_no from `tabLead` where
+				ifnull(mobile_no,'')!='' and docstatus != 2 and status='Open'"""
+			)
 
-		elif self.send_to == 'All Employee (Active)':
-			where_clause = self.department and " and department = '%s'" % \
-				self.department.replace("'", "\'") or ""
-			where_clause += self.branch and " and branch = '%s'" % \
-				self.branch.replace("'", "\'") or ""
+		elif self.send_to == "All Employee (Active)":
+			where_clause = (
+				self.department and " and department = '%s'" % self.department.replace("'", "'") or ""
+			)
+			where_clause += self.branch and " and branch = '%s'" % self.branch.replace("'", "'") or ""
 
-			rec = frappe.db.sql("""select employee_name, cell_number from
+			rec = frappe.db.sql(
+				"""select employee_name, cell_number from
 				`tabEmployee` where status = 'Active' and docstatus < 2 and
-				ifnull(cell_number,'')!='' %s""" % where_clause)
+				ifnull(cell_number,'')!='' %s"""
+				% where_clause
+			)
 
-		elif self.send_to == 'All Sales Person':
-			rec = frappe.db.sql("""select sales_person_name,
+		elif self.send_to == "All Sales Person":
+			rec = frappe.db.sql(
+				"""select sales_person_name,
 				tabEmployee.cell_number from `tabSales Person` left join tabEmployee
 				on `tabSales Person`.employee = tabEmployee.name
-				where ifnull(tabEmployee.cell_number,'')!=''""")
+				where ifnull(tabEmployee.cell_number,'')!=''"""
+			)
 
-		rec_list = ''
+		rec_list = ""
 		for d in rec:
-			rec_list += d[0] + ' - ' + d[1] + '\n'
+			rec_list += d[0] + " - " + d[1] + "\n"
 		self.receiver_list = rec_list
 
 	def get_receiver_nos(self):
 		receiver_nos = []
 		if self.receiver_list:
-			for d in self.receiver_list.split('\n'):
+			for d in self.receiver_list.split("\n"):
 				receiver_no = d
-				if '-' in d:
-					receiver_no = receiver_no.split('-')[1]
+				if "-" in d:
+					receiver_no = receiver_no.split("-")[1]
 				if receiver_no.strip():
 					receiver_nos.append(cstr(receiver_no).strip())
 		else:
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py
index 993c61d..13d5069 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.py
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.py
@@ -3,12 +3,14 @@
 
 
 import json
+from typing import Dict, Optional
 
 import frappe
 from frappe.utils.nestedset import get_root_of
 
 from erpnext.accounts.doctype.pos_invoice.pos_invoice import get_stock_availability
-from erpnext.accounts.doctype.pos_profile.pos_profile import get_item_groups
+from erpnext.accounts.doctype.pos_profile.pos_profile import get_child_nodes, get_item_groups
+from erpnext.stock.utils import scan_barcode
 
 
 def search_by_term(search_term, warehouse, price_list):
@@ -20,31 +22,46 @@
 	barcode = result.get("barcode") or ""
 
 	if result:
-		item_info = frappe.db.get_value("Item", item_code,
-			["name as item_code", "item_name", "description", "stock_uom", "image as item_image", "is_stock_item"],
-			as_dict=1)
+		item_info = frappe.db.get_value(
+			"Item",
+			item_code,
+			[
+				"name as item_code",
+				"item_name",
+				"description",
+				"stock_uom",
+				"image as item_image",
+				"is_stock_item",
+			],
+			as_dict=1,
+		)
 
 		item_stock_qty, is_stock_item = get_stock_availability(item_code, warehouse)
-		price_list_rate, currency = frappe.db.get_value('Item Price', {
-			'price_list': price_list,
-			'item_code': item_code
-		}, ["price_list_rate", "currency"]) or [None, None]
+		price_list_rate, currency = frappe.db.get_value(
+			"Item Price",
+			{"price_list": price_list, "item_code": item_code},
+			["price_list_rate", "currency"],
+		) or [None, None]
 
-		item_info.update({
-			'serial_no': serial_no,
-			'batch_no': batch_no,
-			'barcode': barcode,
-			'price_list_rate': price_list_rate,
-			'currency': currency,
-			'actual_qty': item_stock_qty
-		})
+		item_info.update(
+			{
+				"serial_no": serial_no,
+				"batch_no": batch_no,
+				"barcode": barcode,
+				"price_list_rate": price_list_rate,
+				"currency": currency,
+				"actual_qty": item_stock_qty,
+			}
+		)
 
-		return {'items': [item_info]}
+		return {"items": [item_info]}
+
 
 @frappe.whitelist()
 def get_items(start, page_length, price_list, item_group, pos_profile, search_term=""):
 	warehouse, hide_unavailable_items = frappe.db.get_value(
-		'POS Profile', pos_profile, ['warehouse', 'hide_unavailable_items'])
+		"POS Profile", pos_profile, ["warehouse", "hide_unavailable_items"]
+	)
 
 	result = []
 
@@ -53,20 +70,23 @@
 		if result:
 			return result
 
-	if not frappe.db.exists('Item Group', item_group):
-		item_group = get_root_of('Item Group')
+	if not frappe.db.exists("Item Group", item_group):
+		item_group = get_root_of("Item Group")
 
 	condition = get_conditions(search_term)
 	condition += get_item_group_condition(pos_profile)
 
-	lft, rgt = frappe.db.get_value('Item Group', item_group, ['lft', 'rgt'])
+	lft, rgt = frappe.db.get_value("Item Group", item_group, ["lft", "rgt"])
 
 	bin_join_selection, bin_join_condition = "", ""
 	if hide_unavailable_items:
 		bin_join_selection = ", `tabBin` bin"
-		bin_join_condition = "AND bin.warehouse = %(warehouse)s AND bin.item_code = item.name AND bin.actual_qty > 0"
+		bin_join_condition = (
+			"AND bin.warehouse = %(warehouse)s AND bin.item_code = item.name AND bin.actual_qty > 0"
+		)
 
-	items_data = frappe.db.sql("""
+	items_data = frappe.db.sql(
+		"""
 		SELECT
 			item.name AS item_code,
 			item.item_name,
@@ -87,22 +107,26 @@
 		ORDER BY
 			item.name asc
 		LIMIT
-			{start}, {page_length}"""
-		.format(
+			{page_length} offset {start}""".format(
 			start=start,
 			page_length=page_length,
 			lft=lft,
 			rgt=rgt,
 			condition=condition,
 			bin_join_selection=bin_join_selection,
-			bin_join_condition=bin_join_condition
-		), {'warehouse': warehouse}, as_dict=1)
+			bin_join_condition=bin_join_condition,
+		),
+		{"warehouse": warehouse},
+		as_dict=1,
+	)
 
 	if items_data:
 		items = [d.item_code for d in items_data]
-		item_prices_data = frappe.get_all("Item Price",
-			fields = ["item_code", "price_list_rate", "currency"],
-			filters = {'price_list': price_list, 'item_code': ['in', items]})
+		item_prices_data = frappe.get_all(
+			"Item Price",
+			fields=["item_code", "price_list_rate", "currency"],
+			filters={"price_list": price_list, "item_code": ["in", items]},
+		)
 
 		item_prices = {}
 		for d in item_prices_data:
@@ -115,163 +139,183 @@
 
 			row = {}
 			row.update(item)
-			row.update({
-				'price_list_rate': item_price.get('price_list_rate'),
-				'currency': item_price.get('currency'),
-				'actual_qty': item_stock_qty,
-			})
+			row.update(
+				{
+					"price_list_rate": item_price.get("price_list_rate"),
+					"currency": item_price.get("currency"),
+					"actual_qty": item_stock_qty,
+				}
+			)
 			result.append(row)
 
-	return {'items': result}
+	return {"items": result}
+
 
 @frappe.whitelist()
-def search_for_serial_or_batch_or_barcode_number(search_value):
-	# search barcode no
-	barcode_data = frappe.db.get_value('Item Barcode', {'barcode': search_value}, ['barcode', 'parent as item_code'], as_dict=True)
-	if barcode_data:
-		return barcode_data
+def search_for_serial_or_batch_or_barcode_number(search_value: str) -> Dict[str, Optional[str]]:
+	return scan_barcode(search_value)
 
-	# search serial no
-	serial_no_data = frappe.db.get_value('Serial No', search_value, ['name as serial_no', 'item_code'], as_dict=True)
-	if serial_no_data:
-		return serial_no_data
-
-	# search batch no
-	batch_no_data = frappe.db.get_value('Batch', search_value, ['name as batch_no', 'item as item_code'], as_dict=True)
-	if batch_no_data:
-		return batch_no_data
-
-	return {}
 
 def get_conditions(search_term):
 	condition = "("
 	condition += """item.name like {search_term}
-		or item.item_name like {search_term}""".format(search_term=frappe.db.escape('%' + search_term + '%'))
+		or item.item_name like {search_term}""".format(
+		search_term=frappe.db.escape("%" + search_term + "%")
+	)
 	condition += add_search_fields_condition(search_term)
 	condition += ")"
 
 	return condition
 
+
 def add_search_fields_condition(search_term):
-	condition = ''
-	search_fields = frappe.get_all('POS Search Fields', fields = ['fieldname'])
+	condition = ""
+	search_fields = frappe.get_all("POS Search Fields", fields=["fieldname"])
 	if search_fields:
 		for field in search_fields:
-			condition += " or item.`{0}` like {1}".format(field['fieldname'], frappe.db.escape('%' + search_term + '%'))
+			condition += " or item.`{0}` like {1}".format(
+				field["fieldname"], frappe.db.escape("%" + search_term + "%")
+			)
 	return condition
 
+
 def get_item_group_condition(pos_profile):
 	cond = "and 1=1"
 	item_groups = get_item_groups(pos_profile)
 	if item_groups:
-		cond = "and item.item_group in (%s)"%(', '.join(['%s']*len(item_groups)))
+		cond = "and item.item_group in (%s)" % (", ".join(["%s"] * len(item_groups)))
 
 	return cond % tuple(item_groups)
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def item_group_query(doctype, txt, searchfield, start, page_len, filters):
 	item_groups = []
 	cond = "1=1"
-	pos_profile= filters.get('pos_profile')
+	pos_profile = filters.get("pos_profile")
 
 	if pos_profile:
 		item_groups = get_item_groups(pos_profile)
 
 		if item_groups:
-			cond = "name in (%s)"%(', '.join(['%s']*len(item_groups)))
+			cond = "name in (%s)" % (", ".join(["%s"] * len(item_groups)))
 			cond = cond % tuple(item_groups)
 
-	return frappe.db.sql(""" select distinct name from `tabItem Group`
-			where {condition} and (name like %(txt)s) limit {start}, {page_len}"""
-		.format(condition = cond, start=start, page_len= page_len),
-			{'txt': '%%%s%%' % txt})
+	return frappe.db.sql(
+		""" select distinct name from `tabItem Group`
+			where {condition} and (name like %(txt)s) limit {page_len} offset {start}""".format(
+			condition=cond, start=start, page_len=page_len
+		),
+		{"txt": "%%%s%%" % txt},
+	)
+
 
 @frappe.whitelist()
 def check_opening_entry(user):
-	open_vouchers = frappe.db.get_all("POS Opening Entry",
-		filters = {
-			"user": user,
-			"pos_closing_entry": ["in", ["", None]],
-			"docstatus": 1
-		},
-		fields = ["name", "company", "pos_profile", "period_start_date"],
-		order_by = "period_start_date desc"
+	open_vouchers = frappe.db.get_all(
+		"POS Opening Entry",
+		filters={"user": user, "pos_closing_entry": ["in", ["", None]], "docstatus": 1},
+		fields=["name", "company", "pos_profile", "period_start_date"],
+		order_by="period_start_date desc",
 	)
 
 	return open_vouchers
 
+
 @frappe.whitelist()
 def create_opening_voucher(pos_profile, company, balance_details):
 	balance_details = json.loads(balance_details)
 
-	new_pos_opening = frappe.get_doc({
-		'doctype': 'POS Opening Entry',
-		"period_start_date": frappe.utils.get_datetime(),
-		"posting_date": frappe.utils.getdate(),
-		"user": frappe.session.user,
-		"pos_profile": pos_profile,
-		"company": company,
-	})
+	new_pos_opening = frappe.get_doc(
+		{
+			"doctype": "POS Opening Entry",
+			"period_start_date": frappe.utils.get_datetime(),
+			"posting_date": frappe.utils.getdate(),
+			"user": frappe.session.user,
+			"pos_profile": pos_profile,
+			"company": company,
+		}
+	)
 	new_pos_opening.set("balance_details", balance_details)
 	new_pos_opening.submit()
 
 	return new_pos_opening.as_dict()
 
+
 @frappe.whitelist()
 def get_past_order_list(search_term, status, limit=20):
-	fields = ['name', 'grand_total', 'currency', 'customer', 'posting_time', 'posting_date']
+	fields = ["name", "grand_total", "currency", "customer", "posting_time", "posting_date"]
 	invoice_list = []
 
 	if search_term and status:
-		invoices_by_customer = frappe.db.get_all('POS Invoice', filters={
-			'customer': ['like', '%{}%'.format(search_term)],
-			'status': status
-		}, fields=fields)
-		invoices_by_name = frappe.db.get_all('POS Invoice', filters={
-			'name': ['like', '%{}%'.format(search_term)],
-			'status': status
-		}, fields=fields)
+		invoices_by_customer = frappe.db.get_all(
+			"POS Invoice",
+			filters={"customer": ["like", "%{}%".format(search_term)], "status": status},
+			fields=fields,
+		)
+		invoices_by_name = frappe.db.get_all(
+			"POS Invoice",
+			filters={"name": ["like", "%{}%".format(search_term)], "status": status},
+			fields=fields,
+		)
 
 		invoice_list = invoices_by_customer + invoices_by_name
 	elif status:
-		invoice_list = frappe.db.get_all('POS Invoice', filters={
-			'status': status
-		}, fields=fields)
+		invoice_list = frappe.db.get_all("POS Invoice", filters={"status": status}, fields=fields)
 
 	return invoice_list
 
+
 @frappe.whitelist()
 def set_customer_info(fieldname, customer, value=""):
-	if fieldname == 'loyalty_program':
-		frappe.db.set_value('Customer', customer, 'loyalty_program', value)
+	if fieldname == "loyalty_program":
+		frappe.db.set_value("Customer", customer, "loyalty_program", value)
 
-	contact = frappe.get_cached_value('Customer', customer, 'customer_primary_contact')
+	contact = frappe.get_cached_value("Customer", customer, "customer_primary_contact")
 	if not contact:
-		contact = frappe.db.sql("""
+		contact = frappe.db.sql(
+			"""
 			SELECT parent FROM `tabDynamic Link`
 			WHERE
 				parenttype = 'Contact' AND
 				parentfield = 'links' AND
 				link_doctype = 'Customer' AND
 				link_name = %s
-			""", (customer), as_dict=1)
-		contact = contact[0].get('parent') if contact else None
+			""",
+			(customer),
+			as_dict=1,
+		)
+		contact = contact[0].get("parent") if contact else None
 
 	if not contact:
-		new_contact = frappe.new_doc('Contact')
+		new_contact = frappe.new_doc("Contact")
 		new_contact.is_primary_contact = 1
 		new_contact.first_name = customer
-		new_contact.set('links', [{'link_doctype': 'Customer', 'link_name': customer}])
+		new_contact.set("links", [{"link_doctype": "Customer", "link_name": customer}])
 		new_contact.save()
 		contact = new_contact.name
-		frappe.db.set_value('Customer', customer, 'customer_primary_contact', contact)
+		frappe.db.set_value("Customer", customer, "customer_primary_contact", contact)
 
-	contact_doc = frappe.get_doc('Contact', contact)
-	if fieldname == 'email_id':
-		contact_doc.set('email_ids', [{ 'email_id': value, 'is_primary': 1}])
-		frappe.db.set_value('Customer', customer, 'email_id', value)
-	elif fieldname == 'mobile_no':
-		contact_doc.set('phone_nos', [{ 'phone': value, 'is_primary_mobile_no': 1}])
-		frappe.db.set_value('Customer', customer, 'mobile_no', value)
+	contact_doc = frappe.get_doc("Contact", contact)
+	if fieldname == "email_id":
+		contact_doc.set("email_ids", [{"email_id": value, "is_primary": 1}])
+		frappe.db.set_value("Customer", customer, "email_id", value)
+	elif fieldname == "mobile_no":
+		contact_doc.set("phone_nos", [{"phone": value, "is_primary_mobile_no": 1}])
+		frappe.db.set_value("Customer", customer, "mobile_no", value)
 	contact_doc.save()
+
+
+@frappe.whitelist()
+def get_pos_profile_data(pos_profile):
+	pos_profile = frappe.get_doc("POS Profile", pos_profile)
+	pos_profile = pos_profile.as_dict()
+
+	_customer_groups_with_children = []
+	for row in pos_profile.customer_groups:
+		children = get_child_nodes("Customer Group", row.customer_group)
+		_customer_groups_with_children.extend(children)
+
+	pos_profile.customer_groups = _customer_groups_with_children
+	return pos_profile
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index ea8459f..cb4bd51 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -119,10 +119,15 @@
 			this.allow_negative_stock = flt(message.allow_negative_stock) || false;
 		});
 
-		frappe.db.get_doc("POS Profile", this.pos_profile).then((profile) => {
-			Object.assign(this.settings, profile);
-			this.settings.customer_groups = profile.customer_groups.map(group => group.customer_group);
-			this.make_app();
+		frappe.call({
+			method: "erpnext.selling.page.point_of_sale.point_of_sale.get_pos_profile_data",
+			args: { "pos_profile": this.pos_profile },
+			callback: (res) => {
+				const profile = res.message;
+				Object.assign(this.settings, profile);
+				this.settings.customer_groups = profile.customer_groups.map(group => group.name);
+				this.make_app();
+			}
 		});
 	}
 
@@ -338,9 +343,9 @@
 				toggle_other_sections: (show) => {
 					if (show) {
 						this.item_details.$component.is(':visible') ? this.item_details.$component.css('display', 'none') : '';
-						this.item_selector.$component.css('display', 'none');
+						this.item_selector.toggle_component(false);
 					} else {
-						this.item_selector.$component.css('display', 'flex');
+						this.item_selector.toggle_component(true);
 					}
 				},
 
@@ -474,16 +479,20 @@
 		frappe.dom.freeze();
 		this.frm = this.get_new_frm(this.frm);
 		this.frm.doc.items = [];
-		const res = await frappe.call({
+		return frappe.call({
 			method: "erpnext.accounts.doctype.pos_invoice.pos_invoice.make_sales_return",
 			args: {
 				'source_name': doc.name,
 				'target_doc': this.frm.doc
+			},
+			callback: (r) => {
+				frappe.model.sync(r.message);
+				frappe.get_doc(r.message.doctype, r.message.name).__run_link_triggers = false;
+				this.set_pos_profile_data().then(() => {
+					frappe.dom.unfreeze();
+				});
 			}
 		});
-		frappe.model.sync(res.message);
-		await this.set_pos_profile_data();
-		frappe.dom.unfreeze();
 	}
 
 	set_pos_profile_data() {
@@ -555,7 +564,7 @@
 				if (this.item_details.$component.is(':visible'))
 					this.edit_item_details_of(item_row);
 
-				if (this.check_serial_batch_selection_needed(item_row))
+				if (this.check_serial_batch_selection_needed(item_row) && !this.item_details.$component.is(':visible'))
 					this.edit_item_details_of(item_row);
 			}
 
@@ -704,7 +713,7 @@
 		frappe.dom.freeze();
 		const { doctype, name, current_item } = this.item_details;
 
-		frappe.model.set_value(doctype, name, 'qty', 0)
+		return frappe.model.set_value(doctype, name, 'qty', 0)
 			.then(() => {
 				frappe.model.clear_doc(doctype, name);
 				this.update_cart_html(current_item, true);
@@ -715,7 +724,17 @@
 	}
 
 	async save_and_checkout() {
-		this.frm.is_dirty() && await this.frm.save();
-		this.payment.checkout();
+		if (this.frm.is_dirty()) {
+			let save_error = false;
+			await this.frm.save(null, null, null, () => save_error = true);
+			// only move to payment section if save is successful
+			!save_error && this.payment.checkout();
+			// show checkout button on error
+			save_error && setTimeout(() => {
+				this.cart.toggle_checkout_btn(true);
+			}, 300); // wait for save to finish
+		} else {
+			this.payment.checkout();
+		}
 	}
 };
diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js
index 4a99f06..eacf480 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -130,10 +130,10 @@
 			},
 			cols: 5,
 			keys: [
-				[ 1, 2, 3, __('Quantity') ],
-				[ 4, 5, 6, __('Discount') ],
-				[ 7, 8, 9, __('Rate') ],
-				[ '.', 0, __('Delete'), __('Remove') ]
+				[ 1, 2, 3, 'Quantity' ],
+				[ 4, 5, 6, 'Discount' ],
+				[ 7, 8, 9, 'Rate' ],
+				[ '.', 0, 'Delete', 'Remove' ]
 			],
 			css_classes: [
 				[ '', '', '', 'col-span-2' ],
diff --git a/erpnext/selling/page/point_of_sale/pos_item_details.js b/erpnext/selling/page/point_of_sale/pos_item_details.js
index fb69b63..b75ffb2 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_details.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_details.js
@@ -60,12 +60,18 @@
 		return item && item.name == this.current_item.name;
 	}
 
-	toggle_item_details_section(item) {
+	async toggle_item_details_section(item) {
 		const current_item_changed = !this.compare_with_current_item(item);
 
 		// if item is null or highlighted cart item is clicked twice
 		const hide_item_details = !Boolean(item) || !current_item_changed;
 
+		if ((!hide_item_details && current_item_changed) || hide_item_details) {
+			// if item details is being closed OR if item details is opened but item is changed
+			// in both cases, if the current item is a serialized item, then validate and remove the item
+			await this.validate_serial_batch_item();
+		}
+
 		this.events.toggle_item_selector(!hide_item_details);
 		this.toggle_component(!hide_item_details);
 
@@ -83,7 +89,6 @@
 			this.render_form(item);
 			this.events.highlight_cart_item(item);
 		} else {
-			this.validate_serial_batch_item();
 			this.current_item = {};
 		}
 	}
@@ -103,11 +108,11 @@
 			(serialized && batched && (no_batch_selected || no_serial_selected))) {
 
 			frappe.show_alert({
-				message: __("Item will be removed since no serial / batch no selected."),
+				message: __("Item is removed since no serial / batch no selected."),
 				indicator: 'orange'
 			});
 			frappe.utils.play_sound("cancel");
-			this.events.remove_item_from_cart();
+			return this.events.remove_item_from_cart();
 		}
 	}
 
diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js
index 1177615..7a90fb0 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_selector.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js
@@ -179,6 +179,25 @@
 		});
 		this.search_field.toggle_label(false);
 		this.item_group_field.toggle_label(false);
+
+		this.attach_clear_btn();
+	}
+
+	attach_clear_btn() {
+		this.search_field.$wrapper.find('.control-input').append(
+			`<span class="link-btn" style="top: 2px;">
+				<a class="btn-open no-decoration" title="${__("Clear")}">
+					${frappe.utils.icon('close', 'sm')}
+				</a>
+			</span>`
+		);
+
+		this.$clear_search_btn = this.search_field.$wrapper.find('.link-btn');
+
+		this.$clear_search_btn.on('click', 'a', () => {
+			this.set_search_value('');
+			this.search_field.set_focus();
+		});
 	}
 
 	set_search_value(value) {
@@ -243,7 +262,7 @@
 				value: "+1",
 				item: { item_code, batch_no, serial_no, uom, rate }
 			});
-			me.set_search_value('');
+			me.search_field.set_focus();
 		});
 
 		this.search_field.$input.on('input', (e) => {
@@ -252,6 +271,16 @@
 				const search_term = e.target.value;
 				this.filter_items({ search_term });
 			}, 300);
+
+			this.$clear_search_btn.toggle(
+				Boolean(this.search_field.$input.val())
+			);
+		});
+
+		this.search_field.$input.on('focus', () => {
+			this.$clear_search_btn.toggle(
+				Boolean(this.search_field.$input.val())
+			);
 		});
 	}
 
@@ -284,7 +313,7 @@
 			if (this.items.length == 1) {
 				this.$items_container.find(".item-wrapper").click();
 				frappe.utils.play_sound("submit");
-				$(this.search_field.$input[0]).val("").trigger("input");
+				this.set_search_value('');
 			} else if (this.items.length == 0 && this.barcode_scanned) {
 				// only show alert of barcode is scanned and enter is pressed
 				frappe.show_alert({
@@ -293,7 +322,7 @@
 				});
 				frappe.utils.play_sound("error");
 				this.barcode_scanned = false;
-				$(this.search_field.$input[0]).val("").trigger("input");
+				this.set_search_value('');
 			}
 		});
 	}
@@ -328,6 +357,7 @@
 
 	add_filtered_item_to_cart() {
 		this.$items_container.find(".item-wrapper").click();
+		this.set_search_value('');
 	}
 
 	resize_selector(minimize) {
@@ -349,6 +379,7 @@
 	}
 
 	toggle_component(show) {
-		show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none');
+		this.set_search_value('');
+		this.$component.css('display', show ? 'flex': 'none');
 	}
 };
diff --git a/erpnext/selling/page/point_of_sale/pos_number_pad.js b/erpnext/selling/page/point_of_sale/pos_number_pad.js
index 95293d1..f27b0d5 100644
--- a/erpnext/selling/page/point_of_sale/pos_number_pad.js
+++ b/erpnext/selling/page/point_of_sale/pos_number_pad.js
@@ -25,7 +25,7 @@
 					const fieldname = fieldnames && fieldnames[number] ?
 						fieldnames[number] : typeof number === 'string' ? frappe.scrub(number) : number;
 
-					return a2 + `<div class="numpad-btn ${class_to_append}" data-button-value="${fieldname}">${number}</div>`;
+					return a2 + `<div class="numpad-btn ${class_to_append}" data-button-value="${fieldname}">${__(number)}</div>`;
 				}, '');
 			}, '');
 		}
diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js
index 1e9f6d7..b4ece46 100644
--- a/erpnext/selling/page/point_of_sale/pos_payment.js
+++ b/erpnext/selling/page/point_of_sale/pos_payment.js
@@ -170,20 +170,24 @@
     });
 
 		frappe.ui.form.on('POS Invoice', 'coupon_code', (frm) => {
-			if (!frm.doc.ignore_pricing_rule && frm.doc.coupon_code) {
-				frappe.run_serially([
-					() => frm.doc.ignore_pricing_rule=1,
-					() => frm.trigger('ignore_pricing_rule'),
-					() => frm.doc.ignore_pricing_rule=0,
-					() => frm.trigger('apply_pricing_rule'),
-					() => frm.save(),
-					() => this.update_totals_section(frm.doc)
-				]);
-			} else if (frm.doc.ignore_pricing_rule && frm.doc.coupon_code) {
-				frappe.show_alert({
-					message: __("Ignore Pricing Rule is enabled. Cannot apply coupon code."),
-					indicator: "orange"
-				});
+			if (frm.doc.coupon_code && !frm.applying_pos_coupon_code) {
+				if (!frm.doc.ignore_pricing_rule) {
+					frm.applying_pos_coupon_code = true;
+					frappe.run_serially([
+						() => frm.doc.ignore_pricing_rule=1,
+						() => frm.trigger('ignore_pricing_rule'),
+						() => frm.doc.ignore_pricing_rule=0,
+						() => frm.trigger('apply_pricing_rule'),
+						() => frm.save(),
+						() => this.update_totals_section(frm.doc),
+						() => (frm.applying_pos_coupon_code = false)
+					]);
+				} else if (frm.doc.ignore_pricing_rule) {
+					frappe.show_alert({
+						message: __("Ignore Pricing Rule is enabled. Cannot apply coupon code."),
+						indicator: "orange"
+					});
+				}
 			}
 		});
 
diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.py b/erpnext/selling/page/sales_funnel/sales_funnel.py
index a75108e..6b33a71 100644
--- a/erpnext/selling/page/sales_funnel/sales_funnel.py
+++ b/erpnext/selling/page/sales_funnel/sales_funnel.py
@@ -1,10 +1,11 @@
 # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 
+from itertools import groupby
 
 import frappe
-import pandas as pd
 from frappe import _
+from frappe.utils import flt
 
 from erpnext.accounts.report.utils import convert
 
@@ -16,86 +17,140 @@
 	if not company:
 		frappe.throw(_("Please Select a Company"))
 
+
 @frappe.whitelist()
 def get_funnel_data(from_date, to_date, company):
 	validate_filters(from_date, to_date, company)
 
-	active_leads = frappe.db.sql("""select count(*) from `tabLead`
+	active_leads = frappe.db.sql(
+		"""select count(*) from `tabLead`
 		where (date(`creation`) between %s and %s)
-		and company=%s""", (from_date, to_date, company))[0][0]
+		and company=%s""",
+		(from_date, to_date, company),
+	)[0][0]
 
-	opportunities = frappe.db.sql("""select count(*) from `tabOpportunity`
+	opportunities = frappe.db.sql(
+		"""select count(*) from `tabOpportunity`
 		where (date(`creation`) between %s and %s)
-		and opportunity_from='Lead' and company=%s""", (from_date, to_date, company))[0][0]
+		and opportunity_from='Lead' and company=%s""",
+		(from_date, to_date, company),
+	)[0][0]
 
-	quotations = frappe.db.sql("""select count(*) from `tabQuotation`
+	quotations = frappe.db.sql(
+		"""select count(*) from `tabQuotation`
 		where docstatus = 1 and (date(`creation`) between %s and %s)
-		and (opportunity!="" or quotation_to="Lead") and company=%s""", (from_date, to_date, company))[0][0]
+		and (opportunity!="" or quotation_to="Lead") and company=%s""",
+		(from_date, to_date, company),
+	)[0][0]
 
-	converted = frappe.db.sql("""select count(*) from `tabCustomer`
+	converted = frappe.db.sql(
+		"""select count(*) from `tabCustomer`
 		JOIN `tabLead` ON `tabLead`.name = `tabCustomer`.lead_name
 		WHERE (date(`tabCustomer`.creation) between %s and %s)
-		and `tabLead`.company=%s""", (from_date, to_date, company))[0][0]
-
+		and `tabLead`.company=%s""",
+		(from_date, to_date, company),
+	)[0][0]
 
 	return [
-		{ "title": _("Active Leads"), "value": active_leads, "color": "#B03B46" },
-		{ "title": _("Opportunities"), "value": opportunities, "color": "#F09C00" },
-		{ "title": _("Quotations"), "value": quotations, "color": "#006685" },
-		{ "title": _("Converted"), "value": converted, "color": "#00AD65" }
+		{"title": _("Active Leads"), "value": active_leads, "color": "#B03B46"},
+		{"title": _("Opportunities"), "value": opportunities, "color": "#F09C00"},
+		{"title": _("Quotations"), "value": quotations, "color": "#006685"},
+		{"title": _("Converted"), "value": converted, "color": "#00AD65"},
 	]
 
+
 @frappe.whitelist()
 def get_opp_by_lead_source(from_date, to_date, company):
 	validate_filters(from_date, to_date, company)
 
-	opportunities = frappe.get_all("Opportunity", filters=[['status', 'in', ['Open', 'Quotation', 'Replied']], ['company', '=', company], ['transaction_date', 'Between', [from_date, to_date]]], fields=['currency', 'sales_stage', 'opportunity_amount', 'probability', 'source'])
+	opportunities = frappe.get_all(
+		"Opportunity",
+		filters=[
+			["status", "in", ["Open", "Quotation", "Replied"]],
+			["company", "=", company],
+			["transaction_date", "Between", [from_date, to_date]],
+		],
+		fields=["currency", "sales_stage", "opportunity_amount", "probability", "source"],
+	)
 
 	if opportunities:
-		default_currency = frappe.get_cached_value('Global Defaults', 'None',  'default_currency')
+		default_currency = frappe.get_cached_value("Global Defaults", "None", "default_currency")
 
-		cp_opportunities = [dict(x, **{'compound_amount': (convert(x['opportunity_amount'], x['currency'], default_currency, to_date) * x['probability']/100)}) for x in opportunities]
+		cp_opportunities = [
+			dict(
+				x,
+				**{
+					"compound_amount": (
+						convert(x["opportunity_amount"], x["currency"], default_currency, to_date)
+						* x["probability"]
+						/ 100
+					)
+				}
+			)
+			for x in opportunities
+		]
 
-		df = pd.DataFrame(cp_opportunities).groupby(['source', 'sales_stage'], as_index=False).agg({'compound_amount': 'sum'})
+		summary = {}
+		sales_stages = set()
+		group_key = lambda o: (o["source"], o["sales_stage"])  # noqa
+		for (source, sales_stage), rows in groupby(cp_opportunities, group_key):
+			summary.setdefault(source, {})[sales_stage] = sum(r["compound_amount"] for r in rows)
+			sales_stages.add(sales_stage)
 
-		result = {}
-		result['labels'] = list(set(df.source.values))
-		result['datasets'] = []
+		pivot_table = []
+		for sales_stage in sales_stages:
+			row = []
+			for source, sales_stage_values in summary.items():
+				row.append(flt(sales_stage_values.get(sales_stage)))
+			pivot_table.append({"chartType": "bar", "name": sales_stage, "values": row})
 
-		for s in set(df.sales_stage.values):
-			result['datasets'].append({'name': s, 'values': [0]*len(result['labels']), 'chartType': 'bar'})
-
-		for row in df.itertuples():
-			source_index = result['labels'].index(row.source)
-
-			for dataset in result['datasets']:
-				if dataset['name'] == row.sales_stage:
-					dataset['values'][source_index] = row.compound_amount
-
+		result = {"datasets": pivot_table, "labels": list(summary.keys())}
 		return result
 
 	else:
-		return 'empty'
+		return "empty"
+
 
 @frappe.whitelist()
 def get_pipeline_data(from_date, to_date, company):
 	validate_filters(from_date, to_date, company)
 
-	opportunities = frappe.get_all("Opportunity", filters=[['status', 'in', ['Open', 'Quotation', 'Replied']], ['company', '=', company], ['transaction_date', 'Between', [from_date, to_date]]], fields=['currency', 'sales_stage', 'opportunity_amount', 'probability'])
+	opportunities = frappe.get_all(
+		"Opportunity",
+		filters=[
+			["status", "in", ["Open", "Quotation", "Replied"]],
+			["company", "=", company],
+			["transaction_date", "Between", [from_date, to_date]],
+		],
+		fields=["currency", "sales_stage", "opportunity_amount", "probability"],
+	)
 
 	if opportunities:
-		default_currency = frappe.get_cached_value('Global Defaults', 'None',  'default_currency')
+		default_currency = frappe.get_cached_value("Global Defaults", "None", "default_currency")
 
-		cp_opportunities = [dict(x, **{'compound_amount': (convert(x['opportunity_amount'], x['currency'], default_currency, to_date) * x['probability']/100)}) for x in opportunities]
+		cp_opportunities = [
+			dict(
+				x,
+				**{
+					"compound_amount": (
+						convert(x["opportunity_amount"], x["currency"], default_currency, to_date)
+						* x["probability"]
+						/ 100
+					)
+				}
+			)
+			for x in opportunities
+		]
 
-		df = pd.DataFrame(cp_opportunities).groupby(['sales_stage'], as_index=True).agg({'compound_amount': 'sum'}).to_dict()
+		summary = {}
+		for sales_stage, rows in groupby(cp_opportunities, lambda o: o["sales_stage"]):
+			summary[sales_stage] = sum(flt(r["compound_amount"]) for r in rows)
 
-		result = {}
-		result['labels'] = df['compound_amount'].keys()
-		result['datasets'] = []
-		result['datasets'].append({'name': _("Total Amount"), 'values': df['compound_amount'].values(), 'chartType': 'bar'})
-
+		result = {
+			"labels": list(summary.keys()),
+			"datasets": [{"name": _("Total Amount"), "values": list(summary.values()), "chartType": "bar"}],
+		}
 		return result
 
 	else:
-		return 'empty'
+		return "empty"
diff --git a/erpnext/selling/report/address_and_contacts/address_and_contacts.py b/erpnext/selling/report/address_and_contacts/address_and_contacts.py
index 915f8dc..9a1cfda 100644
--- a/erpnext/selling/report/address_and_contacts/address_and_contacts.py
+++ b/erpnext/selling/report/address_and_contacts/address_and_contacts.py
@@ -5,20 +5,30 @@
 import frappe
 
 field_map = {
-	"Contact": [ "first_name", "last_name", "phone", "mobile_no", "email_id", "is_primary_contact" ],
-	"Address": [ "address_line1", "address_line2", "city", "state", "pincode", "country", "is_primary_address" ]
+	"Contact": ["first_name", "last_name", "phone", "mobile_no", "email_id", "is_primary_contact"],
+	"Address": [
+		"address_line1",
+		"address_line2",
+		"city",
+		"state",
+		"pincode",
+		"country",
+		"is_primary_address",
+	],
 }
 
+
 def execute(filters=None):
 	columns, data = get_columns(filters), get_data(filters)
 	return columns, data
 
+
 def get_columns(filters):
 	party_type = filters.get("party_type")
 	party_type_value = get_party_group(party_type)
 	return [
 		"{party_type}:Link/{party_type}".format(party_type=party_type),
-		"{party_value_type}::150".format(party_value_type = frappe.unscrub(str(party_type_value))),
+		"{party_value_type}::150".format(party_value_type=frappe.unscrub(str(party_type_value))),
 		"Address Line 1",
 		"Address Line 2",
 		"City",
@@ -31,9 +41,10 @@
 		"Phone",
 		"Mobile No",
 		"Email Id",
-		"Is Primary Contact:Check"
+		"Is Primary Contact:Check",
 	]
 
+
 def get_data(filters):
 	party_type = filters.get("party_type")
 	party = filters.get("party_name")
@@ -41,6 +52,7 @@
 
 	return get_party_addresses_and_contact(party_type, party, party_group)
 
+
 def get_party_addresses_and_contact(party_type, party, party_group):
 	data = []
 	filters = None
@@ -50,9 +62,11 @@
 		return []
 
 	if party:
-		filters = { "name": party }
+		filters = {"name": party}
 
-	fetch_party_list = frappe.get_list(party_type, filters=filters, fields=["name", party_group], as_list=True)
+	fetch_party_list = frappe.get_list(
+		party_type, filters=filters, fields=["name", party_group], as_list=True
+	)
 	party_list = [d[0] for d in fetch_party_list]
 	party_groups = {}
 	for d in fetch_party_list:
@@ -66,7 +80,7 @@
 
 	for party, details in party_details.items():
 		addresses = details.get("address", [])
-		contacts  = details.get("contact", [])
+		contacts = details.get("contact", [])
 		if not any([addresses, contacts]):
 			result = [party]
 			result.append(party_groups[party])
@@ -89,10 +103,11 @@
 				data.append(result)
 	return data
 
+
 def get_party_details(party_type, party_list, doctype, party_details):
-	filters =  [
+	filters = [
 		["Dynamic Link", "link_doctype", "=", party_type],
-		["Dynamic Link", "link_name", "in", party_list]
+		["Dynamic Link", "link_name", "in", party_list],
 	]
 	fields = ["`tabDynamic Link`.link_name"] + field_map.get(doctype, [])
 
@@ -103,15 +118,18 @@
 
 	return party_details
 
+
 def add_blank_columns_for(doctype):
 	return ["" for field in field_map.get(doctype, [])]
 
+
 def get_party_group(party_type):
-	if not party_type: return
+	if not party_type:
+		return
 	group = {
 		"Customer": "customer_group",
 		"Supplier": "supplier_group",
-		"Sales Partner": "partner_type"
+		"Sales Partner": "partner_type",
 	}
 
 	return group[party_type]
diff --git a/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py b/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py
index e702a51..5e763bb 100644
--- a/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py
+++ b/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py
@@ -7,7 +7,8 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	columns = get_columns()
 	iwq_map = get_item_warehouse_quantity_map()
@@ -20,32 +21,49 @@
 		for wh, item_qty in warehouse.items():
 			total += 1
 			if item_map.get(sbom):
-				row = [sbom, item_map.get(sbom).item_name, item_map.get(sbom).description,
-						item_map.get(sbom).stock_uom, wh]
+				row = [
+					sbom,
+					item_map.get(sbom).item_name,
+					item_map.get(sbom).description,
+					item_map.get(sbom).stock_uom,
+					wh,
+				]
 				available_qty = item_qty
 				total_qty += flt(available_qty)
 				row += [available_qty]
 
 				if available_qty:
 					data.append(row)
-					if (total == len(warehouse)):
+					if total == len(warehouse):
 						row = ["", "", "Total", "", "", total_qty]
 						data.append(row)
 	return columns, data
 
+
 def get_columns():
-	columns = ["Item Code:Link/Item:100", "Item Name::100", "Description::120", \
-				"UOM:Link/UOM:80", "Warehouse:Link/Warehouse:100", "Quantity::100"]
+	columns = [
+		"Item Code:Link/Item:100",
+		"Item Name::100",
+		"Description::120",
+		"UOM:Link/UOM:80",
+		"Warehouse:Link/Warehouse:100",
+		"Quantity::100",
+	]
 
 	return columns
 
+
 def get_item_details():
 	item_map = {}
-	for item in frappe.db.sql("""SELECT name, item_name, description, stock_uom
-								from `tabItem`""", as_dict=1):
+	for item in frappe.db.sql(
+		"""SELECT name, item_name, description, stock_uom
+								from `tabItem`""",
+		as_dict=1,
+	):
 		item_map.setdefault(item.name, item)
 	return item_map
 
+
 def get_item_warehouse_quantity_map():
 	query = """SELECT parent, warehouse, MIN(qty) AS qty
 			   FROM (SELECT b.parent, bi.item_code, bi.warehouse,
diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py
index 2426cbb..3e4bfb2 100644
--- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py
+++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py
@@ -12,178 +12,177 @@
 def execute(filters=None):
 	common_columns = [
 		{
-			'label': _('New Customers'),
-			'fieldname': 'new_customers',
-			'fieldtype': 'Int',
-			'default': 0,
-			'width': 125
+			"label": _("New Customers"),
+			"fieldname": "new_customers",
+			"fieldtype": "Int",
+			"default": 0,
+			"width": 125,
 		},
 		{
-			'label': _('Repeat Customers'),
-			'fieldname': 'repeat_customers',
-			'fieldtype': 'Int',
-			'default': 0,
-			'width': 125
+			"label": _("Repeat Customers"),
+			"fieldname": "repeat_customers",
+			"fieldtype": "Int",
+			"default": 0,
+			"width": 125,
+		},
+		{"label": _("Total"), "fieldname": "total", "fieldtype": "Int", "default": 0, "width": 100},
+		{
+			"label": _("New Customer Revenue"),
+			"fieldname": "new_customer_revenue",
+			"fieldtype": "Currency",
+			"default": 0.0,
+			"width": 175,
 		},
 		{
-			'label': _('Total'),
-			'fieldname': 'total',
-			'fieldtype': 'Int',
-			'default': 0,
-			'width': 100
+			"label": _("Repeat Customer Revenue"),
+			"fieldname": "repeat_customer_revenue",
+			"fieldtype": "Currency",
+			"default": 0.0,
+			"width": 175,
 		},
 		{
-			'label': _('New Customer Revenue'),
-			'fieldname': 'new_customer_revenue',
-			'fieldtype': 'Currency',
-			'default': 0.0,
-			'width': 175
+			"label": _("Total Revenue"),
+			"fieldname": "total_revenue",
+			"fieldtype": "Currency",
+			"default": 0.0,
+			"width": 175,
 		},
-		{
-			'label': _('Repeat Customer Revenue'),
-			'fieldname': 'repeat_customer_revenue',
-			'fieldtype': 'Currency',
-			'default': 0.0,
-			'width': 175
-		},
-		{
-			'label': _('Total Revenue'),
-			'fieldname': 'total_revenue',
-			'fieldtype': 'Currency',
-			'default': 0.0,
-			'width': 175
-		}
 	]
-	if filters.get('view_type') == 'Monthly':
+	if filters.get("view_type") == "Monthly":
 		return get_data_by_time(filters, common_columns)
 	else:
 		return get_data_by_territory(filters, common_columns)
 
+
 def get_data_by_time(filters, common_columns):
 	# key yyyy-mm
 	columns = [
-		{
-			'label': _('Year'),
-			'fieldname': 'year',
-			'fieldtype': 'Data',
-			'width': 100
-		},
-		{
-			'label': _('Month'),
-			'fieldname': 'month',
-			'fieldtype': 'Data',
-			'width': 100
-		},
+		{"label": _("Year"), "fieldname": "year", "fieldtype": "Data", "width": 100},
+		{"label": _("Month"), "fieldname": "month", "fieldtype": "Data", "width": 100},
 	]
 	columns += common_columns
 
 	customers_in = get_customer_stats(filters)
 
 	# time series
-	from_year, from_month, temp = filters.get('from_date').split('-')
-	to_year, to_month, temp = filters.get('to_date').split('-')
+	from_year, from_month, temp = filters.get("from_date").split("-")
+	to_year, to_month, temp = filters.get("to_date").split("-")
 
-	from_year, from_month, to_year, to_month = \
-		cint(from_year), cint(from_month), cint(to_year), cint(to_month)
+	from_year, from_month, to_year, to_month = (
+		cint(from_year),
+		cint(from_month),
+		cint(to_year),
+		cint(to_month),
+	)
 
 	out = []
-	for year in range(from_year, to_year+1):
-		for month in range(from_month if year==from_year else 1, (to_month+1) if year==to_year else 13):
-			key = '{year}-{month:02d}'.format(year=year, month=month)
+	for year in range(from_year, to_year + 1):
+		for month in range(
+			from_month if year == from_year else 1, (to_month + 1) if year == to_year else 13
+		):
+			key = "{year}-{month:02d}".format(year=year, month=month)
 			data = customers_in.get(key)
-			new = data['new'] if data else [0, 0.0]
-			repeat = data['repeat'] if data else [0, 0.0]
-			out.append({
-				'year': cstr(year),
-				'month': calendar.month_name[month],
-				'new_customers': new[0],
-				'repeat_customers': repeat[0],
-				'total': new[0] + repeat[0],
-				'new_customer_revenue': new[1],
-				'repeat_customer_revenue': repeat[1],
-				'total_revenue': new[1] + repeat[1]
-			})
+			new = data["new"] if data else [0, 0.0]
+			repeat = data["repeat"] if data else [0, 0.0]
+			out.append(
+				{
+					"year": cstr(year),
+					"month": calendar.month_name[month],
+					"new_customers": new[0],
+					"repeat_customers": repeat[0],
+					"total": new[0] + repeat[0],
+					"new_customer_revenue": new[1],
+					"repeat_customer_revenue": repeat[1],
+					"total_revenue": new[1] + repeat[1],
+				}
+			)
 	return columns, out
 
+
 def get_data_by_territory(filters, common_columns):
-	columns = [{
-		'label': 'Territory',
-		'fieldname': 'territory',
-		'fieldtype': 'Link',
-		'options': 'Territory',
-		'width': 150
-	}]
+	columns = [
+		{
+			"label": _("Territory"),
+			"fieldname": "territory",
+			"fieldtype": "Link",
+			"options": "Territory",
+			"width": 150,
+		}
+	]
 	columns += common_columns
 
 	customers_in = get_customer_stats(filters, tree_view=True)
 
 	territory_dict = {}
-	for t in frappe.db.sql('''SELECT name, lft, parent_territory, is_group FROM `tabTerritory` ORDER BY lft''', as_dict=1):
-		territory_dict.update({
-			t.name: {
-				'parent': t.parent_territory,
-				'is_group': t.is_group
-			}
-		})
+	for t in frappe.db.sql(
+		"""SELECT name, lft, parent_territory, is_group FROM `tabTerritory` ORDER BY lft""", as_dict=1
+	):
+		territory_dict.update({t.name: {"parent": t.parent_territory, "is_group": t.is_group}})
 
 	depth_map = frappe._dict()
 	for name, info in territory_dict.items():
-		default = depth_map.get(info['parent']) + 1 if info['parent'] else 0
+		default = depth_map.get(info["parent"]) + 1 if info["parent"] else 0
 		depth_map.setdefault(name, default)
 
 	data = []
 	for name, indent in depth_map.items():
 		condition = customers_in.get(name)
-		new = customers_in[name]['new'] if condition else [0, 0.0]
-		repeat = customers_in[name]['repeat'] if condition else [0, 0.0]
+		new = customers_in[name]["new"] if condition else [0, 0.0]
+		repeat = customers_in[name]["repeat"] if condition else [0, 0.0]
 		temp = {
-			'territory': name,
-			'parent_territory': territory_dict[name]['parent'],
-			'indent': indent,
-			'new_customers': new[0],
-			'repeat_customers': repeat[0],
-			'total': new[0] + repeat[0],
-			'new_customer_revenue': new[1],
-			'repeat_customer_revenue': repeat[1],
-			'total_revenue': new[1] + repeat[1],
-			'bold': 0 if indent else 1
+			"territory": name,
+			"parent_territory": territory_dict[name]["parent"],
+			"indent": indent,
+			"new_customers": new[0],
+			"repeat_customers": repeat[0],
+			"total": new[0] + repeat[0],
+			"new_customer_revenue": new[1],
+			"repeat_customer_revenue": repeat[1],
+			"total_revenue": new[1] + repeat[1],
+			"bold": 0 if indent else 1,
 		}
 		data.append(temp)
 
-	loop_data = sorted(data, key=lambda k: k['indent'], reverse=True)
+	loop_data = sorted(data, key=lambda k: k["indent"], reverse=True)
 
 	for ld in loop_data:
-		if ld['parent_territory']:
-			parent_data = [x for x in data if x['territory'] == ld['parent_territory']][0]
+		if ld["parent_territory"]:
+			parent_data = [x for x in data if x["territory"] == ld["parent_territory"]][0]
 			for key in parent_data.keys():
-				if key not in  ['indent', 'territory', 'parent_territory', 'bold']:
+				if key not in ["indent", "territory", "parent_territory", "bold"]:
 					parent_data[key] += ld[key]
 
 	return columns, data, None, None, None, 1
 
+
 def get_customer_stats(filters, tree_view=False):
-	""" Calculates number of new and repeated customers and revenue. """
-	company_condition = ''
-	if filters.get('company'):
-		company_condition = ' and company=%(company)s'
+	"""Calculates number of new and repeated customers and revenue."""
+	company_condition = ""
+	if filters.get("company"):
+		company_condition = " and company=%(company)s"
 
 	customers = []
 	customers_in = {}
 
-	for si in frappe.db.sql('''select territory, posting_date, customer, base_grand_total from `tabSales Invoice`
+	for si in frappe.db.sql(
+		"""select territory, posting_date, customer, base_grand_total from `tabSales Invoice`
 		where docstatus=1 and posting_date <= %(to_date)s
-		{company_condition} order by posting_date'''.format(company_condition=company_condition),
-		filters, as_dict=1):
+		{company_condition} order by posting_date""".format(
+			company_condition=company_condition
+		),
+		filters,
+		as_dict=1,
+	):
 
-		key = si.territory if tree_view else si.posting_date.strftime('%Y-%m')
-		new_or_repeat = 'new' if si.customer not in customers else 'repeat'
-		customers_in.setdefault(key, {'new': [0, 0.0], 'repeat': [0, 0.0]})
+		key = si.territory if tree_view else si.posting_date.strftime("%Y-%m")
+		new_or_repeat = "new" if si.customer not in customers else "repeat"
+		customers_in.setdefault(key, {"new": [0, 0.0], "repeat": [0, 0.0]})
 
 		# if filters.from_date <= si.posting_date.strftime('%Y-%m-%d'):
 		if getdate(filters.from_date) <= getdate(si.posting_date):
-				customers_in[key][new_or_repeat][0] += 1
-				customers_in[key][new_or_repeat][1] += si.base_grand_total
-		if new_or_repeat == 'new':
+			customers_in[key][new_or_repeat][0] += 1
+			customers_in[key][new_or_repeat][1] += si.base_grand_total
+		if new_or_repeat == "new":
 			customers.append(si.customer)
 
 	return customers_in
diff --git a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py
index dd49f13..98633cb 100644
--- a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py
+++ b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py
@@ -10,8 +10,9 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
-	#Check if customer id is according to naming series or customer name
+	if not filters:
+		filters = {}
+	# Check if customer id is according to naming series or customer name
 	customer_naming_type = frappe.db.get_value("Selling Settings", None, "cust_master_name")
 	columns = get_columns(customer_naming_type)
 
@@ -22,8 +23,9 @@
 	for d in customer_list:
 		row = []
 
-		outstanding_amt = get_customer_outstanding(d.name, filters.get("company"),
-			ignore_outstanding_sales_order=d.bypass_credit_limit_check)
+		outstanding_amt = get_customer_outstanding(
+			d.name, filters.get("company"), ignore_outstanding_sales_order=d.bypass_credit_limit_check
+		)
 
 		credit_limit = get_credit_limit(d.name, filters.get("company"))
 
@@ -31,15 +33,24 @@
 
 		if customer_naming_type == "Naming Series":
 			row = [
-				d.name, d.customer_name, credit_limit,
-				outstanding_amt, bal, d.bypass_credit_limit_check,
-				d.is_frozen, d.disabled
+				d.name,
+				d.customer_name,
+				credit_limit,
+				outstanding_amt,
+				bal,
+				d.bypass_credit_limit_check,
+				d.is_frozen,
+				d.disabled,
 			]
 		else:
 			row = [
-				d.name, credit_limit, outstanding_amt, bal,
-				d.bypass_credit_limit_check, d.is_frozen,
-				d.disabled
+				d.name,
+				credit_limit,
+				outstanding_amt,
+				bal,
+				d.bypass_credit_limit_check,
+				d.is_frozen,
+				d.disabled,
 			]
 
 		if credit_limit:
@@ -47,13 +58,14 @@
 
 	return columns, data
 
+
 def get_columns(customer_naming_type):
 	columns = [
 		_("Customer") + ":Link/Customer:120",
 		_("Credit Limit") + ":Currency:120",
 		_("Outstanding Amt") + ":Currency:100",
 		_("Credit Balance") + ":Currency:120",
-		_("Bypass credit check at Sales Order ") + ":Check:80",
+		_("Bypass credit check at Sales Order") + ":Check:80",
 		_("Is Frozen") + ":Check:80",
 		_("Disabled") + ":Check:80",
 	]
@@ -63,6 +75,7 @@
 
 	return columns
 
+
 def get_details(filters):
 
 	sql_query = """SELECT
diff --git a/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py b/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py
index e5f9354..a58f403 100644
--- a/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py
+++ b/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py
@@ -30,32 +30,23 @@
 			"fieldname": "item_code",
 			"fieldtype": "Link",
 			"options": "Item",
-			"width": 150
+			"width": 150,
 		},
-		{
-			"label": _("Item Name"),
-			"fieldname": "item_name",
-			"fieldtype": "Data",
-			"width": 200
-		},
-		{
-			"label": _("Selling Rate"),
-			"fieldname": "selling_rate",
-			"fieldtype": "Currency"
-		},
+		{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 200},
+		{"label": _("Selling Rate"), "fieldname": "selling_rate", "fieldtype": "Currency"},
 		{
 			"label": _("Available Stock"),
 			"fieldname": "available_stock",
 			"fieldtype": "Float",
-			"width": 150
+			"width": 150,
 		},
 		{
 			"label": _("Price List"),
 			"fieldname": "price_list",
 			"fieldtype": "Link",
 			"options": "Price List",
-			"width": 120
-		}
+			"width": 120,
+		},
 	]
 
 
@@ -64,30 +55,33 @@
 	customer_details = get_customer_details(filters)
 
 	items = get_selling_items(filters)
-	item_stock_map = frappe.get_all("Bin", fields=["item_code", "sum(actual_qty) AS available"], group_by="item_code")
+	item_stock_map = frappe.get_all(
+		"Bin", fields=["item_code", "sum(actual_qty) AS available"], group_by="item_code"
+	)
 	item_stock_map = {item.item_code: item.available for item in item_stock_map}
 
 	for item in items:
 		price_list_rate = get_price_list_rate_for(customer_details, item.item_code) or 0.0
 		available_stock = item_stock_map.get(item.item_code)
 
-		data.append({
-			"item_code": item.item_code,
-			"item_name": item.item_name,
-			"selling_rate": price_list_rate,
-			"price_list": customer_details.get("price_list"),
-			"available_stock": available_stock,
-		})
+		data.append(
+			{
+				"item_code": item.item_code,
+				"item_name": item.item_name,
+				"selling_rate": price_list_rate,
+				"price_list": customer_details.get("price_list"),
+				"available_stock": available_stock,
+			}
+		)
 
 	return data
 
 
 def get_customer_details(filters):
 	customer_details = get_party_details(party=filters.get("customer"), party_type="Customer")
-	customer_details.update({
-		"company": get_default_company(),
-		"price_list": customer_details.get("selling_price_list")
-	})
+	customer_details.update(
+		{"company": get_default_company(), "price_list": customer_details.get("selling_price_list")}
+	)
 
 	return customer_details
 
@@ -98,6 +92,8 @@
 	else:
 		item_filters = {"is_sales_item": 1, "disabled": 0}
 
-	items = frappe.get_all("Item", filters=item_filters, fields=["item_code", "item_name"], order_by="item_name")
+	items = frappe.get_all(
+		"Item", filters=item_filters, fields=["item_code", "item_name"], order_by="item_name"
+	)
 
 	return items
diff --git a/erpnext/selling/report/inactive_customers/inactive_customers.py b/erpnext/selling/report/inactive_customers/inactive_customers.py
index d97e1c6..a166085 100644
--- a/erpnext/selling/report/inactive_customers/inactive_customers.py
+++ b/erpnext/selling/report/inactive_customers/inactive_customers.py
@@ -8,7 +8,8 @@
 
 
 def execute(filters=None):
-	if not filters: filters ={}
+	if not filters:
+		filters = {}
 
 	days_since_last_order = filters.get("days_since_last_order")
 	doctype = filters.get("doctype")
@@ -22,22 +23,24 @@
 	data = []
 	for cust in customers:
 		if cint(cust[8]) >= cint(days_since_last_order):
-			cust.insert(7,get_last_sales_amt(cust[0], doctype))
+			cust.insert(7, get_last_sales_amt(cust[0], doctype))
 			data.append(cust)
 	return columns, data
 
+
 def get_sales_details(doctype):
 	cond = """sum(so.base_net_total) as 'total_order_considered',
 			max(so.posting_date) as 'last_order_date',
-			DATEDIFF(CURDATE(), max(so.posting_date)) as 'days_since_last_order' """
+			DATEDIFF(CURRENT_DATE, max(so.posting_date)) as 'days_since_last_order' """
 	if doctype == "Sales Order":
 		cond = """sum(if(so.status = "Stopped",
 				so.base_net_total * so.per_delivered/100,
 				so.base_net_total)) as 'total_order_considered',
 			max(so.transaction_date) as 'last_order_date',
-			DATEDIFF(CURDATE(), max(so.transaction_date)) as 'days_since_last_order'"""
+			DATEDIFF(CURRENT_DATE, max(so.transaction_date)) as 'days_since_last_order'"""
 
-	return frappe.db.sql("""select
+	return frappe.db.sql(
+		"""select
 			cust.name,
 			cust.customer_name,
 			cust.territory,
@@ -47,18 +50,29 @@
 		from `tabCustomer` cust, `tab{1}` so
 		where cust.name = so.customer and so.docstatus = 1
 		group by cust.name
-		order by 'days_since_last_order' desc """.format(cond, doctype), as_list=1)
+		order by 'days_since_last_order' desc """.format(
+			cond, doctype
+		),
+		as_list=1,
+	)
+
 
 def get_last_sales_amt(customer, doctype):
 	cond = "posting_date"
-	if doctype =="Sales Order":
+	if doctype == "Sales Order":
 		cond = "transaction_date"
-	res =  frappe.db.sql("""select base_net_total from `tab{0}`
+	res = frappe.db.sql(
+		"""select base_net_total from `tab{0}`
 		where customer = %s and docstatus = 1 order by {1} desc
-		limit 1""".format(doctype, cond), customer)
+		limit 1""".format(
+			doctype, cond
+		),
+		customer,
+	)
 
 	return res and res[0][0] or 0
 
+
 def get_columns():
 	return [
 		_("Customer") + ":Link/Customer:120",
@@ -70,5 +84,5 @@
 		_("Total Order Considered") + ":Currency:160",
 		_("Last Order Amount") + ":Currency:160",
 		_("Last Order Date") + ":Date:160",
-		_("Days Since Last Order") + "::160"
+		_("Days Since Last Order") + "::160",
 	]
diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py
index 4a245e1..e10df2a 100644
--- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py
+++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py
@@ -20,6 +20,7 @@
 
 	return columns, data, None, chart_data
 
+
 def get_columns(filters):
 	return [
 		{
@@ -27,120 +28,85 @@
 			"fieldtype": "Link",
 			"fieldname": "item_code",
 			"options": "Item",
-			"width": 120
+			"width": 120,
 		},
-		{
-			"label": _("Item Name"),
-			"fieldtype": "Data",
-			"fieldname": "item_name",
-			"width": 140
-		},
+		{"label": _("Item Name"), "fieldtype": "Data", "fieldname": "item_name", "width": 140},
 		{
 			"label": _("Item Group"),
 			"fieldtype": "Link",
 			"fieldname": "item_group",
 			"options": "Item Group",
-			"width": 120
+			"width": 120,
 		},
-		{
-			"label": _("Description"),
-			"fieldtype": "Data",
-			"fieldname": "description",
-			"width": 150
-		},
-		{
-			"label": _("Quantity"),
-			"fieldtype": "Float",
-			"fieldname": "quantity",
-			"width": 150
-		},
-		{
-			"label": _("UOM"),
-			"fieldtype": "Link",
-			"fieldname": "uom",
-			"options": "UOM",
-			"width": 100
-		},
-		{
-			"label": _("Rate"),
-			"fieldname": "rate",
-			"options": "Currency",
-			"width": 120
-		},
-		{
-			"label": _("Amount"),
-			"fieldname": "amount",
-			"options": "Currency",
-			"width": 120
-		},
+		{"label": _("Description"), "fieldtype": "Data", "fieldname": "description", "width": 150},
+		{"label": _("Quantity"), "fieldtype": "Float", "fieldname": "quantity", "width": 150},
+		{"label": _("UOM"), "fieldtype": "Link", "fieldname": "uom", "options": "UOM", "width": 100},
+		{"label": _("Rate"), "fieldname": "rate", "options": "Currency", "width": 120},
+		{"label": _("Amount"), "fieldname": "amount", "options": "Currency", "width": 120},
 		{
 			"label": _("Sales Order"),
 			"fieldtype": "Link",
 			"fieldname": "sales_order",
 			"options": "Sales Order",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Transaction Date"),
 			"fieldtype": "Date",
 			"fieldname": "transaction_date",
-			"width": 90
+			"width": 90,
 		},
 		{
 			"label": _("Customer"),
 			"fieldtype": "Link",
 			"fieldname": "customer",
 			"options": "Customer",
-			"width": 100
+			"width": 100,
 		},
-		{
-			"label": _("Customer Name"),
-			"fieldtype": "Data",
-			"fieldname": "customer_name",
-			"width": 140
-		},
+		{"label": _("Customer Name"), "fieldtype": "Data", "fieldname": "customer_name", "width": 140},
 		{
 			"label": _("Customer Group"),
 			"fieldtype": "Link",
 			"fieldname": "customer_group",
 			"options": "Customer Group",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("Territory"),
 			"fieldtype": "Link",
 			"fieldname": "territory",
 			"options": "Territory",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Project"),
 			"fieldtype": "Link",
 			"fieldname": "project",
 			"options": "Project",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Delivered Quantity"),
 			"fieldtype": "Float",
 			"fieldname": "delivered_quantity",
-			"width": 150
+			"width": 150,
 		},
 		{
 			"label": _("Billed Amount"),
 			"fieldtype": "currency",
 			"fieldname": "billed_amount",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("Company"),
 			"fieldtype": "Link",
 			"fieldname": "company",
 			"options": "Company",
-			"width": 100
-		}
+			"width": 100,
+		},
 	]
 
+
 def get_data(filters):
 
 	data = []
@@ -156,74 +122,75 @@
 		customer_record = customer_details.get(record.customer)
 		item_record = item_details.get(record.item_code)
 		row = {
-			"item_code": record.item_code,
-			"item_name": item_record.item_name,
-			"item_group": item_record.item_group,
-			"description": record.description,
-			"quantity": record.qty,
-			"uom": record.uom,
-			"rate": record.base_rate,
-			"amount": record.base_amount,
-			"sales_order": record.name,
-			"transaction_date": record.transaction_date,
-			"customer": record.customer,
-			"customer_name": customer_record.customer_name,
-			"customer_group": customer_record.customer_group,
-			"territory": record.territory,
-			"project": record.project,
-			"delivered_quantity": flt(record.delivered_qty),
-			"billed_amount": flt(record.billed_amt),
-			"company": record.company
+			"item_code": record.get("item_code"),
+			"item_name": item_record.get("item_name"),
+			"item_group": item_record.get("item_group"),
+			"description": record.get("description"),
+			"quantity": record.get("qty"),
+			"uom": record.get("uom"),
+			"rate": record.get("base_rate"),
+			"amount": record.get("base_amount"),
+			"sales_order": record.get("name"),
+			"transaction_date": record.get("transaction_date"),
+			"customer": record.get("customer"),
+			"customer_name": customer_record.get("customer_name"),
+			"customer_group": customer_record.get("customer_group"),
+			"territory": record.get("territory"),
+			"project": record.get("project"),
+			"delivered_quantity": flt(record.get("delivered_qty")),
+			"billed_amount": flt(record.get("billed_amt")),
+			"company": record.get("company"),
 		}
 		data.append(row)
 
 	return data
 
+
 def get_conditions(filters):
-	conditions = ''
-	if filters.get('item_group'):
-		conditions += "AND so_item.item_group = %s" %frappe.db.escape(filters.item_group)
+	conditions = ""
+	if filters.get("item_group"):
+		conditions += "AND so_item.item_group = %s" % frappe.db.escape(filters.item_group)
 
-	if filters.get('from_date'):
-		conditions += "AND so.transaction_date >= '%s'" %filters.from_date
+	if filters.get("from_date"):
+		conditions += "AND so.transaction_date >= '%s'" % filters.from_date
 
-	if filters.get('to_date'):
-		conditions += "AND so.transaction_date <= '%s'" %filters.to_date
+	if filters.get("to_date"):
+		conditions += "AND so.transaction_date <= '%s'" % filters.to_date
 
 	if filters.get("item_code"):
-		conditions += "AND so_item.item_code = %s" %frappe.db.escape(filters.item_code)
+		conditions += "AND so_item.item_code = %s" % frappe.db.escape(filters.item_code)
 
 	if filters.get("customer"):
-		conditions += "AND so.customer = %s" %frappe.db.escape(filters.customer)
+		conditions += "AND so.customer = %s" % frappe.db.escape(filters.customer)
 
 	return conditions
 
+
 def get_customer_details():
-	details = frappe.get_all("Customer",
-		fields=["name", "customer_name", "customer_group"])
+	details = frappe.get_all("Customer", fields=["name", "customer_name", "customer_group"])
 	customer_details = {}
 	for d in details:
-		customer_details.setdefault(d.name, frappe._dict({
-			"customer_name": d.customer_name,
-			"customer_group": d.customer_group
-		}))
+		customer_details.setdefault(
+			d.name, frappe._dict({"customer_name": d.customer_name, "customer_group": d.customer_group})
+		)
 	return customer_details
 
+
 def get_item_details():
-	details = frappe.db.get_all("Item",
-		fields=["item_code", "item_name", "item_group"])
+	details = frappe.db.get_all("Item", fields=["name", "item_name", "item_group"])
 	item_details = {}
 	for d in details:
-		item_details.setdefault(d.item_code, frappe._dict({
-			"item_name": d.item_name,
-			"item_group": d.item_group
-		}))
+		item_details.setdefault(
+			d.name, frappe._dict({"item_name": d.item_name, "item_group": d.item_group})
+		)
 	return item_details
 
+
 def get_sales_order_details(company_list, filters):
 	conditions = get_conditions(filters)
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		SELECT
 			so_item.item_code, so_item.description, so_item.qty,
 			so_item.uom, so_item.base_rate, so_item.base_amount,
@@ -236,7 +203,13 @@
 			so.name = so_item.parent
 			AND so.company in ({0})
 			AND so.docstatus = 1 {1}
-	""".format(','.join(["%s"] * len(company_list)), conditions), tuple(company_list), as_dict=1)
+	""".format(
+			",".join(["%s"] * len(company_list)), conditions
+		),
+		tuple(company_list),
+		as_dict=1,
+	)
+
 
 def get_chart_data(data):
 	item_wise_sales_map = {}
@@ -250,21 +223,20 @@
 
 		item_wise_sales_map[item_key] = flt(item_wise_sales_map[item_key]) + flt(row.get("amount"))
 
-	item_wise_sales_map = { item: value for item, value in (sorted(item_wise_sales_map.items(), key = lambda i: i[1], reverse=True))}
+	item_wise_sales_map = {
+		item: value
+		for item, value in (sorted(item_wise_sales_map.items(), key=lambda i: i[1], reverse=True))
+	}
 
 	for key in item_wise_sales_map:
 		labels.append(key)
 		datapoints.append(item_wise_sales_map[key])
 
 	return {
-		"data" : {
-			"labels" : labels[:30], # show max of 30 items in chart
-			"datasets" : [
-				{
-					"name" : _(" Total Sales Amount"),
-					"values" : datapoints[:30]
-				}
-			]
+		"data": {
+			"labels": labels[:30],  # show max of 30 items in chart
+			"datasets": [{"name": _("Total Sales Amount"), "values": datapoints[:30]}],
 		},
-		"type" : "bar"
+		"type": "bar",
+		"fieldtype": "Currency",
 	}
diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js
index 0e36b3f..c068ae3 100644
--- a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js
+++ b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js
@@ -27,28 +27,55 @@
 			"default": frappe.datetime.get_today()
 		},
 		{
-			"fieldname":"sales_order",
-			"label": __("Sales Order"),
-			"fieldtype": "MultiSelectList",
+			"fieldname":"customer_group",
+			"label": __("Customer Group"),
+			"fieldtype": "Link",
 			"width": 100,
-			"options": "Sales Order",
-			"get_data": function(txt) {
-				return frappe.db.get_link_options("Sales Order", txt, this.filters());
-			},
-			"filters": () => {
-				return {
-					docstatus: 1,
-					payment_terms_template: ['not in', ['']],
-					company: frappe.query_report.get_filter_value("company"),
-					transaction_date: ['between', [frappe.query_report.get_filter_value("period_start_date"), frappe.query_report.get_filter_value("period_end_date")]]
+			"options": "Customer Group",
+		},
+		{
+			"fieldname":"customer",
+			"label": __("Customer"),
+			"fieldtype": "Link",
+			"width": 100,
+			"options": "Customer",
+			"get_query": () => {
+				var customer_group = frappe.query_report.get_filter_value('customer_group');
+				return{
+					"query": "erpnext.selling.report.payment_terms_status_for_sales_order.payment_terms_status_for_sales_order.get_customers_or_items",
+					"filters": [
+						['Customer', 'disabled', '=', '0'],
+						['Customer Group','name', '=', customer_group]
+					]
 				}
-			},
-			on_change: function(){
-				frappe.query_report.refresh();
+			}
+		},
+		{
+			"fieldname":"item_group",
+			"label": __("Item Group"),
+			"fieldtype": "Link",
+			"width": 100,
+			"options": "Item Group",
+
+		},
+		{
+			"fieldname":"item",
+			"label": __("Item"),
+			"fieldtype": "Link",
+			"width": 100,
+			"options": "Item",
+			"get_query": () => {
+				var item_group = frappe.query_report.get_filter_value('item_group');
+				return{
+					"query": "erpnext.selling.report.payment_terms_status_for_sales_order.payment_terms_status_for_sales_order.get_customers_or_items",
+					"filters": [
+						['Item', 'disabled', '=', '0'],
+						['Item Group','name', '=', item_group]
+					]
+				}
 			}
 		}
 	]
-
 	return filters;
 }
 
diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py
index e6a56ee..91f4a5e 100644
--- a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py
+++ b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py
@@ -3,7 +3,7 @@
 
 import frappe
 from frappe import _, qb, query_builder
-from frappe.query_builder import functions
+from frappe.query_builder import Criterion, functions
 
 
 def get_columns():
@@ -15,6 +15,12 @@
 			"options": "Sales Order",
 		},
 		{
+			"label": _("Customer"),
+			"fieldname": "customer",
+			"fieldtype": "Link",
+			"options": "Customer",
+		},
+		{
 			"label": _("Posting Date"),
 			"fieldname": "submitted",
 			"fieldtype": "Date",
@@ -62,16 +68,60 @@
 			"fieldname": "status",
 			"fieldtype": "Data",
 		},
-		{
-			"label": _("Currency"),
-			"fieldname": "currency",
-			"fieldtype": "Currency",
-			"hidden": 1
-		}
+		{"label": _("Currency"), "fieldname": "currency", "fieldtype": "Currency", "hidden": 1},
 	]
 	return columns
 
 
+def get_descendants_of(doctype, group_name):
+	group_doc = qb.DocType(doctype)
+	# get lft and rgt of group node
+	lft, rgt = (
+		qb.from_(group_doc).select(group_doc.lft, group_doc.rgt).where(group_doc.name == group_name)
+	).run()[0]
+
+	# get all children of group node
+	query = (
+		qb.from_(group_doc).select(group_doc.name).where((group_doc.lft >= lft) & (group_doc.rgt <= rgt))
+	)
+
+	child_nodes = []
+	for x in query.run():
+		child_nodes.append(x[0])
+
+	return child_nodes
+
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
+def get_customers_or_items(doctype, txt, searchfield, start, page_len, filters):
+	filter_list = []
+	if isinstance(filters, list):
+		for item in filters:
+			if item[0] == doctype:
+				filter_list.append(item)
+			elif item[0] == "Customer Group":
+				if item[3] != "":
+					filter_list.append(
+						[doctype, "customer_group", "in", get_descendants_of("Customer Group", item[3])]
+					)
+			elif item[0] == "Item Group":
+				if item[3] != "":
+					filter_list.append([doctype, "item_group", "in", get_descendants_of("Item Group", item[3])])
+
+	if searchfield and txt:
+		filter_list.append([doctype, searchfield, "like", "%%%s%%" % txt])
+
+	return frappe.desk.reportview.execute(
+		doctype,
+		filters=filter_list,
+		fields=["name", "customer_group"] if doctype == "Customer" else ["name", "item_group"],
+		limit_start=start,
+		limit_page_length=page_len,
+		as_list=True,
+	)
+
+
 def get_conditions(filters):
 	"""
 	Convert filter options to conditions used in query
@@ -84,11 +134,37 @@
 	conditions.start_date = filters.period_start_date or frappe.utils.add_months(
 		conditions.end_date, -1
 	)
-	conditions.sales_order = filters.sales_order or []
 
 	return conditions
 
 
+def build_filter_criterions(filters):
+	filters = frappe._dict(filters) if filters else frappe._dict({})
+	qb_criterions = []
+
+	if filters.customer_group:
+		qb_criterions.append(
+			qb.DocType("Sales Order").customer_group.isin(
+				get_descendants_of("Customer Group", filters.customer_group)
+			)
+		)
+
+	if filters.customer:
+		qb_criterions.append(qb.DocType("Sales Order").customer == filters.customer)
+
+	if filters.item_group:
+		qb_criterions.append(
+			qb.DocType("Sales Order Item").item_group.isin(
+				get_descendants_of("Item Group", filters.item_group)
+			)
+		)
+
+	if filters.item:
+		qb_criterions.append(qb.DocType("Sales Order Item").item_code == filters.item)
+
+	return qb_criterions
+
+
 def get_so_with_invoices(filters):
 	"""
 	Get Sales Order with payment terms template with their associated Invoices
@@ -97,16 +173,24 @@
 
 	so = qb.DocType("Sales Order")
 	ps = qb.DocType("Payment Schedule")
+	soi = qb.DocType("Sales Order Item")
+
+	conditions = get_conditions(filters)
+	filter_criterions = build_filter_criterions(filters)
+
 	datediff = query_builder.CustomFunction("DATEDIFF", ["cur_date", "due_date"])
 	ifelse = query_builder.CustomFunction("IF", ["condition", "then", "else"])
 
-	conditions = get_conditions(filters)
 	query_so = (
 		qb.from_(so)
+		.join(soi)
+		.on(soi.parent == so.name)
 		.join(ps)
 		.on(ps.parent == so.name)
+		.select(so.name)
+		.distinct()
 		.select(
-			so.name,
+			so.customer,
 			so.transaction_date.as_("submitted"),
 			ifelse(datediff(ps.due_date, functions.CurDate()) < 0, "Overdue", "Unpaid").as_("status"),
 			ps.payment_term,
@@ -122,12 +206,10 @@
 			& (so.company == conditions.company)
 			& (so.transaction_date[conditions.start_date : conditions.end_date])
 		)
+		.where(Criterion.all(filter_criterions))
 		.orderby(so.name, so.transaction_date, ps.due_date)
 	)
 
-	if conditions.sales_order != []:
-		query_so = query_so.where(so.name.isin(conditions.sales_order))
-
 	sorders = query_so.run(as_dict=True)
 
 	invoices = []
@@ -156,7 +238,7 @@
 	"""
 
 	for so in sales_orders:
-		so.currency = frappe.get_cached_value('Company', filters.get('company'), 'default_currency')
+		so.currency = frappe.get_cached_value("Company", filters.get("company"), "default_currency")
 		so.invoices = ""
 		for inv in [x for x in invoices if x.sales_order == so.name and x.invoice_amount > 0]:
 			if so.base_payment_amount - so.paid_amount > 0:
@@ -182,8 +264,14 @@
 			"data": {
 				"labels": [term.payment_term for term in s_orders],
 				"datasets": [
-					{"name": "Payment Amount", "values": [x.base_payment_amount for x in s_orders],},
-					{"name": "Paid Amount", "values": [x.paid_amount for x in s_orders],},
+					{
+						"name": "Payment Amount",
+						"values": [x.base_payment_amount for x in s_orders],
+					},
+					{
+						"name": "Paid Amount",
+						"values": [x.paid_amount for x in s_orders],
+					},
 				],
 			},
 			"type": "bar",
diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py b/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py
index f7f8a5d..9d542f5 100644
--- a/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py
+++ b/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py
@@ -11,10 +11,13 @@
 )
 from erpnext.stock.doctype.item.test_item import create_item
 
-test_dependencies = ["Sales Order", "Item", "Sales Invoice", "Payment Terms Template"]
+test_dependencies = ["Sales Order", "Item", "Sales Invoice", "Payment Terms Template", "Customer"]
 
 
 class TestPaymentTermsStatusForSalesOrder(FrappeTestCase):
+	def tearDown(self):
+		frappe.db.rollback()
+
 	def create_payment_terms_template(self):
 		# create template for 50-50 payments
 		template = None
@@ -48,9 +51,9 @@
 			template.insert()
 		self.template = template
 
-	def test_payment_terms_status(self):
+	def test_01_payment_terms_status(self):
 		self.create_payment_terms_template()
-		item = create_item(item_code="_Test Excavator", is_stock_item=0)
+		item = create_item(item_code="_Test Excavator 1", is_stock_item=0)
 		so = make_sales_order(
 			transaction_date="2021-06-15",
 			delivery_date=add_days("2021-06-15", -30),
@@ -78,13 +81,14 @@
 				"company": "_Test Company",
 				"period_start_date": "2021-06-01",
 				"period_end_date": "2021-06-30",
-				"sales_order": [so.name],
+				"item": item.item_code,
 			}
 		)
 
 		expected_value = [
 			{
 				"name": so.name,
+				"customer": so.customer,
 				"submitted": datetime.date(2021, 6, 15),
 				"status": "Completed",
 				"payment_term": None,
@@ -94,10 +98,11 @@
 				"currency": "INR",
 				"base_payment_amount": 500000.0,
 				"paid_amount": 500000.0,
-				"invoices": ","+sinv.name,
+				"invoices": "," + sinv.name,
 			},
 			{
 				"name": so.name,
+				"customer": so.customer,
 				"submitted": datetime.date(2021, 6, 15),
 				"status": "Partly Paid",
 				"payment_term": None,
@@ -107,32 +112,36 @@
 				"currency": "INR",
 				"base_payment_amount": 500000.0,
 				"paid_amount": 100000.0,
-				"invoices": ","+sinv.name,
+				"invoices": "," + sinv.name,
 			},
 		]
 		self.assertEqual(data, expected_value)
 
 	def create_exchange_rate(self, date):
 		# make an entry in Currency Exchange list. serves as a static exchange rate
-		if frappe.db.exists({'doctype': "Currency Exchange",'date': date,'from_currency': 'USD', 'to_currency':'INR'}):
+		if frappe.db.exists(
+			{"doctype": "Currency Exchange", "date": date, "from_currency": "USD", "to_currency": "INR"}
+		):
 			return
 		else:
-			doc = frappe.get_doc({
-				'doctype': "Currency Exchange",
-				'date': date,
-				'from_currency': 'USD',
-				'to_currency': frappe.get_cached_value("Company", '_Test Company','default_currency'),
-				'exchange_rate': 70,
-				'for_buying': True,
-				'for_selling': True
-			})
+			doc = frappe.get_doc(
+				{
+					"doctype": "Currency Exchange",
+					"date": date,
+					"from_currency": "USD",
+					"to_currency": frappe.get_cached_value("Company", "_Test Company", "default_currency"),
+					"exchange_rate": 70,
+					"for_buying": True,
+					"for_selling": True,
+				}
+			)
 			doc.insert()
 
-	def test_alternate_currency(self):
+	def test_02_alternate_currency(self):
 		transaction_date = "2021-06-15"
 		self.create_payment_terms_template()
 		self.create_exchange_rate(transaction_date)
-		item = create_item(item_code="_Test Excavator", is_stock_item=0)
+		item = create_item(item_code="_Test Excavator 2", is_stock_item=0)
 		so = make_sales_order(
 			transaction_date=transaction_date,
 			currency="USD",
@@ -162,7 +171,7 @@
 				"company": "_Test Company",
 				"period_start_date": "2021-06-01",
 				"period_end_date": "2021-06-30",
-				"sales_order": [so.name],
+				"item": item.item_code,
 			}
 		)
 
@@ -170,29 +179,162 @@
 		expected_value = [
 			{
 				"name": so.name,
+				"customer": so.customer,
 				"submitted": datetime.date(2021, 6, 15),
 				"status": "Completed",
 				"payment_term": None,
 				"description": "_Test 50-50",
 				"due_date": datetime.date(2021, 6, 30),
 				"invoice_portion": 50.0,
-				"currency": frappe.get_cached_value("Company", '_Test Company','default_currency'),
+				"currency": frappe.get_cached_value("Company", "_Test Company", "default_currency"),
 				"base_payment_amount": 3500000.0,
 				"paid_amount": 3500000.0,
-				"invoices": ","+sinv.name,
+				"invoices": "," + sinv.name,
 			},
 			{
 				"name": so.name,
+				"customer": so.customer,
 				"submitted": datetime.date(2021, 6, 15),
 				"status": "Partly Paid",
 				"payment_term": None,
 				"description": "_Test 50-50",
 				"due_date": datetime.date(2021, 7, 15),
 				"invoice_portion": 50.0,
-				"currency": frappe.get_cached_value("Company", '_Test Company','default_currency'),
+				"currency": frappe.get_cached_value("Company", "_Test Company", "default_currency"),
 				"base_payment_amount": 3500000.0,
 				"paid_amount": 700000.0,
-				"invoices": ","+sinv.name,
+				"invoices": "," + sinv.name,
 			},
 		]
 		self.assertEqual(data, expected_value)
+
+	def test_03_group_filters(self):
+		transaction_date = "2021-06-15"
+		self.create_payment_terms_template()
+		item1 = create_item(item_code="_Test Excavator 1", is_stock_item=0)
+		item1.item_group = "Products"
+		item1.save()
+
+		so1 = make_sales_order(
+			transaction_date=transaction_date,
+			delivery_date=add_days(transaction_date, -30),
+			item=item1.item_code,
+			qty=1,
+			rate=1000000,
+			do_not_save=True,
+		)
+		so1.po_no = ""
+		so1.taxes_and_charges = ""
+		so1.taxes = ""
+		so1.payment_terms_template = self.template.name
+		so1.save()
+		so1.submit()
+
+		item2 = create_item(item_code="_Test Steel", is_stock_item=0)
+		item2.item_group = "Raw Material"
+		item2.save()
+
+		so2 = make_sales_order(
+			customer="_Test Customer 1",
+			transaction_date=transaction_date,
+			delivery_date=add_days(transaction_date, -30),
+			item=item2.item_code,
+			qty=100,
+			rate=1000,
+			do_not_save=True,
+		)
+		so2.po_no = ""
+		so2.taxes_and_charges = ""
+		so2.taxes = ""
+		so2.payment_terms_template = self.template.name
+		so2.save()
+		so2.submit()
+
+		base_filters = {
+			"company": "_Test Company",
+			"period_start_date": "2021-06-01",
+			"period_end_date": "2021-06-30",
+		}
+
+		expected_value_so1 = [
+			{
+				"name": so1.name,
+				"customer": so1.customer,
+				"submitted": datetime.date(2021, 6, 15),
+				"status": "Overdue",
+				"payment_term": None,
+				"description": "_Test 50-50",
+				"due_date": datetime.date(2021, 6, 30),
+				"invoice_portion": 50.0,
+				"currency": "INR",
+				"base_payment_amount": 500000.0,
+				"paid_amount": 0.0,
+				"invoices": "",
+			},
+			{
+				"name": so1.name,
+				"customer": so1.customer,
+				"submitted": datetime.date(2021, 6, 15),
+				"status": "Overdue",
+				"payment_term": None,
+				"description": "_Test 50-50",
+				"due_date": datetime.date(2021, 7, 15),
+				"invoice_portion": 50.0,
+				"currency": "INR",
+				"base_payment_amount": 500000.0,
+				"paid_amount": 0.0,
+				"invoices": "",
+			},
+		]
+
+		expected_value_so2 = [
+			{
+				"name": so2.name,
+				"customer": so2.customer,
+				"submitted": datetime.date(2021, 6, 15),
+				"status": "Overdue",
+				"payment_term": None,
+				"description": "_Test 50-50",
+				"due_date": datetime.date(2021, 6, 30),
+				"invoice_portion": 50.0,
+				"currency": "INR",
+				"base_payment_amount": 50000.0,
+				"paid_amount": 0.0,
+				"invoices": "",
+			},
+			{
+				"name": so2.name,
+				"customer": so2.customer,
+				"submitted": datetime.date(2021, 6, 15),
+				"status": "Overdue",
+				"payment_term": None,
+				"description": "_Test 50-50",
+				"due_date": datetime.date(2021, 7, 15),
+				"invoice_portion": 50.0,
+				"currency": "INR",
+				"base_payment_amount": 50000.0,
+				"paid_amount": 0.0,
+				"invoices": "",
+			},
+		]
+
+		group_filters = [
+			{"customer_group": "All Customer Groups"},
+			{"item_group": "All Item Groups"},
+			{"item_group": "Products"},
+			{"item_group": "Raw Material"},
+		]
+
+		expected_values_for_group_filters = [
+			expected_value_so1 + expected_value_so2,
+			expected_value_so1 + expected_value_so2,
+			expected_value_so1,
+			expected_value_so2,
+		]
+
+		for idx, g in enumerate(group_filters, 0):
+			# build filter
+			filters = frappe._dict({}).update(base_filters).update(g)
+			with self.subTest(filters=filters):
+				columns, data, message, chart = execute(filters)
+				self.assertEqual(data, expected_values_for_group_filters[idx])
diff --git a/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py b/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py
index 01421e8..928ed80 100644
--- a/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py
+++ b/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py
@@ -12,6 +12,7 @@
 	data = get_data()
 	return columns, data
 
+
 def get_columns():
 	columns = [
 		{
@@ -19,80 +20,37 @@
 			"options": "Item",
 			"fieldname": "item_code",
 			"fieldtype": "Link",
-			"width": 200
+			"width": 200,
 		},
-		{
-			"label": _("Item Name"),
-			"fieldname": "item_name",
-			"fieldtype": "Data",
-			"width": 200
-		},
-		{
-			"label": _("Description"),
-			"fieldname": "description",
-			"fieldtype": "Data",
-			"width": 140
-		},
+		{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 200},
+		{"label": _("Description"), "fieldname": "description", "fieldtype": "Data", "width": 140},
 		{
 			"label": _("S.O. No."),
 			"options": "Sales Order",
 			"fieldname": "sales_order_no",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
-		{
-			"label": _("Date"),
-			"fieldname": "date",
-			"fieldtype": "Date",
-			"width": 140
-		},
+		{"label": _("Date"), "fieldname": "date", "fieldtype": "Date", "width": 140},
 		{
 			"label": _("Material Request"),
 			"fieldname": "material_request",
 			"fieldtype": "Data",
-			"width": 140
+			"width": 140,
 		},
-		{
-			"label": _("Customer"),
-			"fieldname": "customer",
-			"fieldtype": "Data",
-			"width": 140
-		},
-		{
-			"label": _("Territory"),
-			"fieldname": "territory",
-			"fieldtype": "Data",
-			"width": 140
-		},
-		{
-			"label": _("SO Qty"),
-			"fieldname": "so_qty",
-			"fieldtype": "Float",
-			"width": 140
-		},
-		{
-			"label": _("Requested Qty"),
-			"fieldname": "requested_qty",
-			"fieldtype": "Float",
-			"width": 140
-		},
-		{
-			"label": _("Pending Qty"),
-			"fieldname": "pending_qty",
-			"fieldtype": "Float",
-			"width": 140
-		},
-		{
-			"label": _("Company"),
-			"fieldname": "company",
-			"fieldtype": "Data",
-			"width": 140
-		}
+		{"label": _("Customer"), "fieldname": "customer", "fieldtype": "Data", "width": 140},
+		{"label": _("Territory"), "fieldname": "territory", "fieldtype": "Data", "width": 140},
+		{"label": _("SO Qty"), "fieldname": "so_qty", "fieldtype": "Float", "width": 140},
+		{"label": _("Requested Qty"), "fieldname": "requested_qty", "fieldtype": "Float", "width": 140},
+		{"label": _("Pending Qty"), "fieldname": "pending_qty", "fieldtype": "Float", "width": 140},
+		{"label": _("Company"), "fieldname": "company", "fieldtype": "Data", "width": 140},
 	]
 	return columns
 
+
 def get_data():
-	sales_order_entry = frappe.db.sql("""
+	sales_order_entry = frappe.db.sql(
+		"""
 		SELECT
 			so_item.item_code,
 			so_item.item_name,
@@ -107,91 +65,97 @@
 		WHERE
 			so.docstatus = 1
 			and so.name = so_item.parent
-			and so.status not in  ("Closed","Completed","Cancelled")
+			and so.status not in  ('Closed','Completed','Cancelled')
 		GROUP BY
 			so.name,so_item.item_code
-		""", as_dict = 1)
+		""",
+		as_dict=1,
+	)
 
 	sales_orders = [row.name for row in sales_order_entry]
-	mr_records = frappe.get_all("Material Request Item",
+	mr_records = frappe.get_all(
+		"Material Request Item",
 		{"sales_order": ("in", sales_orders), "docstatus": 1},
-		["parent", "qty", "sales_order", "item_code"])
+		["parent", "qty", "sales_order", "item_code"],
+	)
 
 	bundled_item_map = get_packed_items(sales_orders)
 
-	item_with_product_bundle = get_items_with_product_bundle([row.item_code for row in sales_order_entry])
+	item_with_product_bundle = get_items_with_product_bundle(
+		[row.item_code for row in sales_order_entry]
+	)
 
 	materials_request_dict = {}
 
 	for record in mr_records:
 		key = (record.sales_order, record.item_code)
 		if key not in materials_request_dict:
-			materials_request_dict.setdefault(key, {
-				'qty': 0,
-				'material_requests': [record.parent]
-			})
+			materials_request_dict.setdefault(key, {"qty": 0, "material_requests": [record.parent]})
 
 		details = materials_request_dict.get(key)
-		details['qty'] += record.qty
+		details["qty"] += record.qty
 
-		if record.parent not in details.get('material_requests'):
-			details['material_requests'].append(record.parent)
+		if record.parent not in details.get("material_requests"):
+			details["material_requests"].append(record.parent)
 
 	pending_so = []
 	for so in sales_order_entry:
 		if so.item_code not in item_with_product_bundle:
 			material_requests_against_so = materials_request_dict.get((so.name, so.item_code)) or {}
 			# check for pending sales order
-			if flt(so.total_qty) > flt(material_requests_against_so.get('qty')):
+			if flt(so.total_qty) > flt(material_requests_against_so.get("qty")):
 				so_record = {
 					"item_code": so.item_code,
 					"item_name": so.item_name,
 					"description": so.description,
 					"sales_order_no": so.name,
 					"date": so.transaction_date,
-					"material_request": ','.join(material_requests_against_so.get('material_requests', [])),
+					"material_request": ",".join(material_requests_against_so.get("material_requests", [])),
 					"customer": so.customer,
 					"territory": so.territory,
 					"so_qty": so.total_qty,
-					"requested_qty": material_requests_against_so.get('qty'),
-					"pending_qty": so.total_qty - flt(material_requests_against_so.get('qty')),
-					"company": so.company
+					"requested_qty": material_requests_against_so.get("qty"),
+					"pending_qty": so.total_qty - flt(material_requests_against_so.get("qty")),
+					"company": so.company,
 				}
 				pending_so.append(so_record)
 		else:
 			for item in bundled_item_map.get((so.name, so.item_code), []):
 				material_requests_against_so = materials_request_dict.get((so.name, item.item_code)) or {}
-				if flt(item.qty) > flt(material_requests_against_so.get('qty')):
+				if flt(item.qty) > flt(material_requests_against_so.get("qty")):
 					so_record = {
 						"item_code": item.item_code,
 						"item_name": item.item_name,
 						"description": item.description,
 						"sales_order_no": so.name,
 						"date": so.transaction_date,
-						"material_request": ','.join(material_requests_against_so.get('material_requests', [])),
+						"material_request": ",".join(material_requests_against_so.get("material_requests", [])),
 						"customer": so.customer,
 						"territory": so.territory,
 						"so_qty": item.qty,
-						"requested_qty": material_requests_against_so.get('qty', 0),
-						"pending_qty": item.qty - flt(material_requests_against_so.get('qty', 0)),
-						"company": so.company
+						"requested_qty": material_requests_against_so.get("qty", 0),
+						"pending_qty": item.qty - flt(material_requests_against_so.get("qty", 0)),
+						"company": so.company,
 					}
 					pending_so.append(so_record)
 
-
 	return pending_so
 
+
 def get_items_with_product_bundle(item_list):
-	bundled_items = frappe.get_all("Product Bundle", filters = [
-		("new_item_code", "IN", item_list)
-	], fields = ["new_item_code"])
+	bundled_items = frappe.get_all(
+		"Product Bundle", filters=[("new_item_code", "IN", item_list)], fields=["new_item_code"]
+	)
 
 	return [d.new_item_code for d in bundled_items]
 
+
 def get_packed_items(sales_order_list):
-	packed_items = frappe.get_all("Packed Item", filters = [
-		("parent", "IN", sales_order_list)
-	], fields = ["parent_item", "item_code", "qty", "item_name", "description", "parent"])
+	packed_items = frappe.get_all(
+		"Packed Item",
+		filters=[("parent", "IN", sales_order_list)],
+		fields=["parent_item", "item_code", "qty", "item_name", "description", "parent"],
+	)
 
 	bundled_item_map = frappe._dict()
 	for d in packed_items:
diff --git a/erpnext/selling/report/pending_so_items_for_purchase_request/test_pending_so_items_for_purchase_request.py b/erpnext/selling/report/pending_so_items_for_purchase_request/test_pending_so_items_for_purchase_request.py
index 16162ac..e4ad5c6 100644
--- a/erpnext/selling/report/pending_so_items_for_purchase_request/test_pending_so_items_for_purchase_request.py
+++ b/erpnext/selling/report/pending_so_items_for_purchase_request/test_pending_so_items_for_purchase_request.py
@@ -13,18 +13,18 @@
 
 
 class TestPendingSOItemsForPurchaseRequest(FrappeTestCase):
-    def test_result_for_partial_material_request(self):
-        so = make_sales_order()
-        mr=make_material_request(so.name)
-        mr.items[0].qty = 4
-        mr.schedule_date = add_months(nowdate(),1)
-        mr.submit()
-        report = execute()
-        l = len(report[1])
-        self.assertEqual((so.items[0].qty - mr.items[0].qty), report[1][l-1]['pending_qty'])
+	def test_result_for_partial_material_request(self):
+		so = make_sales_order()
+		mr = make_material_request(so.name)
+		mr.items[0].qty = 4
+		mr.schedule_date = add_months(nowdate(), 1)
+		mr.submit()
+		report = execute()
+		l = len(report[1])
+		self.assertEqual((so.items[0].qty - mr.items[0].qty), report[1][l - 1]["pending_qty"])
 
-    def test_result_for_so_item(self):
-        so = make_sales_order()
-        report = execute()
-        l = len(report[1])
-        self.assertEqual(so.items[0].qty, report[1][l-1]['pending_qty'])
+	def test_result_for_so_item(self):
+		so = make_sales_order()
+		report = execute()
+		l = len(report[1])
+		self.assertEqual(so.items[0].qty, report[1][l - 1]["pending_qty"])
diff --git a/erpnext/selling/report/quotation_trends/quotation_trends.py b/erpnext/selling/report/quotation_trends/quotation_trends.py
index 047b090..4d71ce7 100644
--- a/erpnext/selling/report/quotation_trends/quotation_trends.py
+++ b/erpnext/selling/report/quotation_trends/quotation_trends.py
@@ -8,7 +8,8 @@
 
 
 def execute(filters=None):
-	if not filters: filters ={}
+	if not filters:
+		filters = {}
 	data = []
 	conditions = get_columns(filters, "Quotation")
 	data = get_data(filters, conditions)
@@ -17,6 +18,7 @@
 
 	return conditions["columns"], data, None, chart_data
 
+
 def get_chart_data(data, conditions, filters):
 	if not (data and conditions):
 		return []
@@ -29,32 +31,28 @@
 
 	# fetch only periodic columns as labels
 	columns = conditions.get("columns")[start:-2][1::2]
-	labels = [column.split(':')[0] for column in columns]
+	labels = [column.split(":")[0] for column in columns]
 	datapoints = [0] * len(labels)
 
 	for row in data:
 		# If group by filter, don't add first row of group (it's already summed)
-		if not row[start-1]:
+		if not row[start - 1]:
 			continue
 		# Remove None values and compute only periodic data
 		row = [x if x else 0 for x in row[start:-2]]
-		row  = row[1::2]
+		row = row[1::2]
 
 		for i in range(len(row)):
 			datapoints[i] += row[i]
 
 	return {
-		"data" : {
-			"labels" : labels,
-			"datasets" : [
-				{
-					"name" : _("{0}").format(filters.get("period")) + _(" Quoted Amount"),
-					"values" : datapoints
-				}
-			]
+		"data": {
+			"labels": labels,
+			"datasets": [
+				{"name": _(filters.get("period")) + " " + _("Quoted Amount"), "values": datapoints}
+			],
 		},
-		"type" : "line",
-		"lineOptions": {
-			"regionFill": 1
-		}
+		"type": "line",
+		"lineOptions": {"regionFill": 1},
+		"fieldtype": "Currency",
 	}
diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.js b/erpnext/selling/report/sales_analytics/sales_analytics.js
index 6b03c7d..d527e42 100644
--- a/erpnext/selling/report/sales_analytics/sales_analytics.js
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.js
@@ -82,62 +82,42 @@
 					const tree_type = frappe.query_report.filters[0].value;
 					if (data_doctype != tree_type) return;
 
-					row_name = data[2].content;
-					length = data.length;
-
-					if (tree_type == "Customer") {
-						row_values = data
-							.slice(4, length - 1)
-							.map(function (column) {
-								return column.content;
-							});
-					} else if (tree_type == "Item") {
-						row_values = data
-							.slice(5, length - 1)
-							.map(function (column) {
-								return column.content;
-							});
-					} else {
-						row_values = data
-							.slice(3, length - 1)
-							.map(function (column) {
-								return column.content;
-							});
-					}
-
-					entry = {
-						name: row_name,
-						values: row_values,
-					};
-
-					let raw_data = frappe.query_report.chart.data;
-					let new_datasets = raw_data.datasets;
-
-					let element_found = new_datasets.some((element, index, array)=>{
-						if(element.name == row_name){
-							array.splice(index, 1)
-							return true
+					const row_name = data[2].content;
+					const raw_data = frappe.query_report.chart.data;
+					const new_datasets = raw_data.datasets;
+					const element_found = new_datasets.some(
+						(element, index, array) => {
+							if (element.name == row_name) {
+								array.splice(index, 1);
+								return true;
+							}
+							return false;
 						}
-						return false
-					})
+					);
+					const slice_at = { Customer: 4, Item: 5 }[tree_type] || 3;
 
 					if (!element_found) {
-						new_datasets.push(entry);
+						new_datasets.push({
+							name: row_name,
+							values: data
+								.slice(slice_at, data.length - 1)
+								.map(column => column.content),
+						});
 					}
 
-					let new_data = {
+					const new_data = {
 						labels: raw_data.labels,
 						datasets: new_datasets,
 					};
-					chart_options = {
+
+					frappe.query_report.render_chart({
 						data: new_data,
 						type: "line",
-					};
-					frappe.query_report.render_chart(chart_options);
+					});
 
 					frappe.query_report.raw_chart_data = new_data;
 				},
 			},
 		});
 	},
-}
+};
diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py
index 83588c3..9d7d806 100644
--- a/erpnext/selling/report/sales_analytics/sales_analytics.py
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.py
@@ -12,12 +12,29 @@
 def execute(filters=None):
 	return Analytics(filters).run()
 
+
 class Analytics(object):
 	def __init__(self, filters=None):
 		self.filters = frappe._dict(filters or {})
-		self.date_field = 'transaction_date' \
-			if self.filters.doc_type in ['Sales Order', 'Purchase Order'] else 'posting_date'
-		self.months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+		self.date_field = (
+			"transaction_date"
+			if self.filters.doc_type in ["Sales Order", "Purchase Order"]
+			else "posting_date"
+		)
+		self.months = [
+			"Jan",
+			"Feb",
+			"Mar",
+			"Apr",
+			"May",
+			"Jun",
+			"Jul",
+			"Aug",
+			"Sep",
+			"Oct",
+			"Nov",
+			"Dec",
+		]
 		self.get_period_date_ranges()
 
 	def run(self):
@@ -34,52 +51,52 @@
 		return self.columns, self.data, None, self.chart, None, skip_total_row
 
 	def get_columns(self):
-		self.columns = [{
+		self.columns = [
+			{
 				"label": _(self.filters.tree_type),
 				"options": self.filters.tree_type if self.filters.tree_type != "Order Type" else "",
 				"fieldname": "entity",
 				"fieldtype": "Link" if self.filters.tree_type != "Order Type" else "Data",
-				"width": 140 if self.filters.tree_type != "Order Type" else 200
-			}]
+				"width": 140 if self.filters.tree_type != "Order Type" else 200,
+			}
+		]
 		if self.filters.tree_type in ["Customer", "Supplier", "Item"]:
-			self.columns.append({
-				"label": _(self.filters.tree_type + " Name"),
-				"fieldname": "entity_name",
-				"fieldtype": "Data",
-				"width": 140
-			})
+			self.columns.append(
+				{
+					"label": _(self.filters.tree_type + " Name"),
+					"fieldname": "entity_name",
+					"fieldtype": "Data",
+					"width": 140,
+				}
+			)
 
 		if self.filters.tree_type == "Item":
-			self.columns.append({
-				"label": _("UOM"),
-				"fieldname": 'stock_uom',
-				"fieldtype": "Link",
-				"options": "UOM",
-				"width": 100
-			})
+			self.columns.append(
+				{
+					"label": _("UOM"),
+					"fieldname": "stock_uom",
+					"fieldtype": "Link",
+					"options": "UOM",
+					"width": 100,
+				}
+			)
 
 		for end_date in self.periodic_daterange:
 			period = self.get_period(end_date)
-			self.columns.append({
-				"label": _(period),
-				"fieldname": scrub(period),
-				"fieldtype": "Float",
-				"width": 120
-			})
+			self.columns.append(
+				{"label": _(period), "fieldname": scrub(period), "fieldtype": "Float", "width": 120}
+			)
 
-		self.columns.append({
-			"label": _("Total"),
-			"fieldname": "total",
-			"fieldtype": "Float",
-			"width": 120
-		})
+		self.columns.append(
+			{"label": _("Total"), "fieldname": "total", "fieldtype": "Float", "width": 120}
+		)
 
 	def get_data(self):
 		if self.filters.tree_type in ["Customer", "Supplier"]:
 			self.get_sales_transactions_based_on_customers_or_suppliers()
 			self.get_rows()
 
-		elif self.filters.tree_type == 'Item':
+		elif self.filters.tree_type == "Item":
 			self.get_sales_transactions_based_on_items()
 			self.get_rows()
 
@@ -87,7 +104,7 @@
 			self.get_sales_transactions_based_on_customer_or_territory_group()
 			self.get_rows_by_group()
 
-		elif self.filters.tree_type == 'Item Group':
+		elif self.filters.tree_type == "Item Group":
 			self.get_sales_transactions_based_on_item_group()
 			self.get_rows_by_group()
 
@@ -103,40 +120,45 @@
 			self.get_rows()
 
 	def get_sales_transactions_based_on_order_type(self):
-		if self.filters["value_quantity"] == 'Value':
+		if self.filters["value_quantity"] == "Value":
 			value_field = "base_net_total"
 		else:
 			value_field = "total_qty"
 
-		self.entries = frappe.db.sql(""" select s.order_type as entity, s.{value_field} as value_field, s.{date_field}
+		self.entries = frappe.db.sql(
+			""" select s.order_type as entity, s.{value_field} as value_field, s.{date_field}
 			from `tab{doctype}` s where s.docstatus = 1 and s.company = %s and s.{date_field} between %s and %s
 			and ifnull(s.order_type, '') != '' order by s.order_type
-		"""
-		.format(date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type),
-		(self.filters.company, self.filters.from_date, self.filters.to_date), as_dict=1)
+		""".format(
+				date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type
+			),
+			(self.filters.company, self.filters.from_date, self.filters.to_date),
+			as_dict=1,
+		)
 
 		self.get_teams()
 
 	def get_sales_transactions_based_on_customers_or_suppliers(self):
-		if self.filters["value_quantity"] == 'Value':
+		if self.filters["value_quantity"] == "Value":
 			value_field = "base_net_total as value_field"
 		else:
 			value_field = "total_qty as value_field"
 
-		if self.filters.tree_type == 'Customer':
+		if self.filters.tree_type == "Customer":
 			entity = "customer as entity"
 			entity_name = "customer_name as entity_name"
 		else:
 			entity = "supplier as entity"
 			entity_name = "supplier_name as entity_name"
 
-		self.entries = frappe.get_all(self.filters.doc_type,
+		self.entries = frappe.get_all(
+			self.filters.doc_type,
 			fields=[entity, entity_name, value_field, self.date_field],
 			filters={
 				"docstatus": 1,
 				"company": self.filters.company,
-				self.date_field: ('between', [self.filters.from_date, self.filters.to_date])
-			}
+				self.date_field: ("between", [self.filters.from_date, self.filters.to_date]),
+			},
 		)
 
 		self.entity_names = {}
@@ -145,80 +167,91 @@
 
 	def get_sales_transactions_based_on_items(self):
 
-		if self.filters["value_quantity"] == 'Value':
-			value_field = 'base_amount'
+		if self.filters["value_quantity"] == "Value":
+			value_field = "base_amount"
 		else:
-			value_field = 'stock_qty'
+			value_field = "stock_qty"
 
-		self.entries = frappe.db.sql("""
+		self.entries = frappe.db.sql(
+			"""
 			select i.item_code as entity, i.item_name as entity_name, i.stock_uom, i.{value_field} as value_field, s.{date_field}
 			from `tab{doctype} Item` i , `tab{doctype}` s
 			where s.name = i.parent and i.docstatus = 1 and s.company = %s
 			and s.{date_field} between %s and %s
-		"""
-		.format(date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type),
-		(self.filters.company, self.filters.from_date, self.filters.to_date), as_dict=1)
+		""".format(
+				date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type
+			),
+			(self.filters.company, self.filters.from_date, self.filters.to_date),
+			as_dict=1,
+		)
 
 		self.entity_names = {}
 		for d in self.entries:
 			self.entity_names.setdefault(d.entity, d.entity_name)
 
 	def get_sales_transactions_based_on_customer_or_territory_group(self):
-		if self.filters["value_quantity"] == 'Value':
+		if self.filters["value_quantity"] == "Value":
 			value_field = "base_net_total as value_field"
 		else:
 			value_field = "total_qty as value_field"
 
-		if self.filters.tree_type == 'Customer Group':
-			entity_field = 'customer_group as entity'
-		elif self.filters.tree_type == 'Supplier Group':
+		if self.filters.tree_type == "Customer Group":
+			entity_field = "customer_group as entity"
+		elif self.filters.tree_type == "Supplier Group":
 			entity_field = "supplier as entity"
 			self.get_supplier_parent_child_map()
 		else:
 			entity_field = "territory as entity"
 
-		self.entries = frappe.get_all(self.filters.doc_type,
+		self.entries = frappe.get_all(
+			self.filters.doc_type,
 			fields=[entity_field, value_field, self.date_field],
 			filters={
 				"docstatus": 1,
 				"company": self.filters.company,
-				self.date_field: ('between', [self.filters.from_date, self.filters.to_date])
-			}
+				self.date_field: ("between", [self.filters.from_date, self.filters.to_date]),
+			},
 		)
 		self.get_groups()
 
 	def get_sales_transactions_based_on_item_group(self):
-		if self.filters["value_quantity"] == 'Value':
+		if self.filters["value_quantity"] == "Value":
 			value_field = "base_amount"
 		else:
 			value_field = "qty"
 
-		self.entries = frappe.db.sql("""
+		self.entries = frappe.db.sql(
+			"""
 			select i.item_group as entity, i.{value_field} as value_field, s.{date_field}
 			from `tab{doctype} Item` i , `tab{doctype}` s
 			where s.name = i.parent and i.docstatus = 1 and s.company = %s
 			and s.{date_field} between %s and %s
-		""".format(date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type),
-		(self.filters.company, self.filters.from_date, self.filters.to_date), as_dict=1)
+		""".format(
+				date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type
+			),
+			(self.filters.company, self.filters.from_date, self.filters.to_date),
+			as_dict=1,
+		)
 
 		self.get_groups()
 
 	def get_sales_transactions_based_on_project(self):
-		if self.filters["value_quantity"] == 'Value':
+		if self.filters["value_quantity"] == "Value":
 			value_field = "base_net_total as value_field"
 		else:
 			value_field = "total_qty as value_field"
 
 		entity = "project as entity"
 
-		self.entries = frappe.get_all(self.filters.doc_type,
+		self.entries = frappe.get_all(
+			self.filters.doc_type,
 			fields=[entity, value_field, self.date_field],
 			filters={
 				"docstatus": 1,
 				"company": self.filters.company,
 				"project": ["!=", ""],
-				self.date_field: ('between', [self.filters.from_date, self.filters.to_date])
-			}
+				self.date_field: ("between", [self.filters.from_date, self.filters.to_date]),
+			},
 		)
 
 	def get_rows(self):
@@ -228,7 +261,7 @@
 		for entity, period_data in self.entity_periodic_data.items():
 			row = {
 				"entity": entity,
-				"entity_name": self.entity_names.get(entity) if hasattr(self, 'entity_names') else None
+				"entity_name": self.entity_names.get(entity) if hasattr(self, "entity_names") else None,
 			}
 			total = 0
 			for end_date in self.periodic_daterange:
@@ -249,10 +282,7 @@
 		out = []
 
 		for d in reversed(self.group_entries):
-			row = {
-				"entity": d.name,
-				"indent": self.depth_map.get(d.name)
-			}
+			row = {"entity": d.name, "indent": self.depth_map.get(d.name)}
 			total = 0
 			for end_date in self.periodic_daterange:
 				period = self.get_period(end_date)
@@ -279,14 +309,14 @@
 			self.entity_periodic_data[d.entity][period] += flt(d.value_field)
 
 			if self.filters.tree_type == "Item":
-				self.entity_periodic_data[d.entity]['stock_uom'] = d.stock_uom
+				self.entity_periodic_data[d.entity]["stock_uom"] = d.stock_uom
 
 	def get_period(self, posting_date):
-		if self.filters.range == 'Weekly':
+		if self.filters.range == "Weekly":
 			period = "Week " + str(posting_date.isocalendar()[1]) + " " + str(posting_date.year)
-		elif self.filters.range == 'Monthly':
+		elif self.filters.range == "Monthly":
 			period = str(self.months[posting_date.month - 1]) + " " + str(posting_date.year)
-		elif self.filters.range == 'Quarterly':
+		elif self.filters.range == "Quarterly":
 			period = "Quarter " + str(((posting_date.month - 1) // 3) + 1) + " " + str(posting_date.year)
 		else:
 			year = get_fiscal_year(posting_date, company=self.filters.company)
@@ -295,16 +325,14 @@
 
 	def get_period_date_ranges(self):
 		from dateutil.relativedelta import MO, relativedelta
+
 		from_date, to_date = getdate(self.filters.from_date), getdate(self.filters.to_date)
 
-		increment = {
-			"Monthly": 1,
-			"Quarterly": 3,
-			"Half-Yearly": 6,
-			"Yearly": 12
-		}.get(self.filters.range, 1)
+		increment = {"Monthly": 1, "Quarterly": 3, "Half-Yearly": 6, "Yearly": 12}.get(
+			self.filters.range, 1
+		)
 
-		if self.filters.range in ['Monthly', 'Quarterly']:
+		if self.filters.range in ["Monthly", "Quarterly"]:
 			from_date = from_date.replace(day=1)
 		elif self.filters.range == "Yearly":
 			from_date = get_fiscal_year(from_date)[1]
@@ -329,19 +357,23 @@
 
 	def get_groups(self):
 		if self.filters.tree_type == "Territory":
-			parent = 'parent_territory'
+			parent = "parent_territory"
 		if self.filters.tree_type == "Customer Group":
-			parent = 'parent_customer_group'
+			parent = "parent_customer_group"
 		if self.filters.tree_type == "Item Group":
-			parent = 'parent_item_group'
+			parent = "parent_item_group"
 		if self.filters.tree_type == "Supplier Group":
-			parent = 'parent_supplier_group'
+			parent = "parent_supplier_group"
 
 		self.depth_map = frappe._dict()
 
-		self.group_entries = frappe.db.sql("""select name, lft, rgt , {parent} as parent
-			from `tab{tree}` order by lft"""
-		.format(tree=self.filters.tree_type, parent=parent), as_dict=1)
+		self.group_entries = frappe.db.sql(
+			"""select name, lft, rgt , {parent} as parent
+			from `tab{tree}` order by lft""".format(
+				tree=self.filters.tree_type, parent=parent
+			),
+			as_dict=1,
+		)
 
 		for d in self.group_entries:
 			if d.parent:
@@ -352,11 +384,15 @@
 	def get_teams(self):
 		self.depth_map = frappe._dict()
 
-		self.group_entries = frappe.db.sql(""" select * from (select "Order Types" as name, 0 as lft,
+		self.group_entries = frappe.db.sql(
+			""" select * from (select "Order Types" as name, 0 as lft,
 			2 as rgt, '' as parent union select distinct order_type as name, 1 as lft, 1 as rgt, "Order Types" as parent
 			from `tab{doctype}` where ifnull(order_type, '') != '') as b order by lft, name
-		"""
-		.format(doctype=self.filters.doc_type), as_dict=1)
+		""".format(
+				doctype=self.filters.doc_type
+			),
+			as_dict=1,
+		)
 
 		for d in self.group_entries:
 			if d.parent:
@@ -365,21 +401,22 @@
 				self.depth_map.setdefault(d.name, 0)
 
 	def get_supplier_parent_child_map(self):
-		self.parent_child_map = frappe._dict(frappe.db.sql(""" select name, supplier_group from `tabSupplier`"""))
+		self.parent_child_map = frappe._dict(
+			frappe.db.sql(""" select name, supplier_group from `tabSupplier`""")
+		)
 
 	def get_chart_data(self):
 		length = len(self.columns)
 
 		if self.filters.tree_type in ["Customer", "Supplier"]:
-			labels = [d.get("label") for d in self.columns[2:length - 1]]
+			labels = [d.get("label") for d in self.columns[2 : length - 1]]
 		elif self.filters.tree_type == "Item":
-			labels = [d.get("label") for d in self.columns[3:length - 1]]
+			labels = [d.get("label") for d in self.columns[3 : length - 1]]
 		else:
-			labels = [d.get("label") for d in self.columns[1:length - 1]]
-		self.chart = {
-			"data": {
-				'labels': labels,
-				'datasets': []
-			},
-			"type": "line"
-		}
+			labels = [d.get("label") for d in self.columns[1 : length - 1]]
+		self.chart = {"data": {"labels": labels, "datasets": []}, "type": "line"}
+
+		if self.filters["value_quantity"] == "Value":
+			self.chart["fieldtype"] = "Currency"
+		else:
+			self.chart["fieldtype"] = "Float"
diff --git a/erpnext/selling/report/sales_analytics/test_analytics.py b/erpnext/selling/report/sales_analytics/test_analytics.py
index 564f48f..15f06d9 100644
--- a/erpnext/selling/report/sales_analytics/test_analytics.py
+++ b/erpnext/selling/report/sales_analytics/test_analytics.py
@@ -19,16 +19,15 @@
 		self.compare_result_for_customer_group()
 		self.compare_result_for_customer_based_on_quantity()
 
-
 	def compare_result_for_customer(self):
 		filters = {
-			'doc_type': 'Sales Order',
-			'range': 'Monthly',
-			'to_date': '2018-03-31',
-			'tree_type': 'Customer',
-			'company': '_Test Company 2',
-			'from_date': '2017-04-01',
-			'value_quantity': 'Value'
+			"doc_type": "Sales Order",
+			"range": "Monthly",
+			"to_date": "2018-03-31",
+			"tree_type": "Customer",
+			"company": "_Test Company 2",
+			"from_date": "2017-04-01",
+			"value_quantity": "Value",
 		}
 
 		report = execute(filters)
@@ -49,7 +48,7 @@
 				"jan_2018": 0.0,
 				"feb_2018": 2000.0,
 				"mar_2018": 0.0,
-				"total":2000.0
+				"total": 2000.0,
 			},
 			{
 				"entity": "_Test Customer 2",
@@ -66,7 +65,7 @@
 				"jan_2018": 0.0,
 				"feb_2018": 0.0,
 				"mar_2018": 0.0,
-				"total":2500.0
+				"total": 2500.0,
 			},
 			{
 				"entity": "_Test Customer 3",
@@ -83,21 +82,21 @@
 				"jan_2018": 0.0,
 				"feb_2018": 0.0,
 				"mar_2018": 0.0,
-				"total": 3000.0
-			}
+				"total": 3000.0,
+			},
 		]
-		result = sorted(report[1], key=lambda k: k['entity'])
+		result = sorted(report[1], key=lambda k: k["entity"])
 		self.assertEqual(expected_data, result)
 
 	def compare_result_for_customer_group(self):
 		filters = {
-			'doc_type': 'Sales Order',
-			'range': 'Monthly',
-			'to_date': '2018-03-31',
-			'tree_type': 'Customer Group',
-			'company': '_Test Company 2',
-			'from_date': '2017-04-01',
-			'value_quantity': 'Value'
+			"doc_type": "Sales Order",
+			"range": "Monthly",
+			"to_date": "2018-03-31",
+			"tree_type": "Customer Group",
+			"company": "_Test Company 2",
+			"from_date": "2017-04-01",
+			"value_quantity": "Value",
 		}
 
 		report = execute(filters)
@@ -117,19 +116,19 @@
 			"jan_2018": 0.0,
 			"feb_2018": 2000.0,
 			"mar_2018": 0.0,
-			"total":7500.0
+			"total": 7500.0,
 		}
 		self.assertEqual(expected_first_row, report[1][0])
 
 	def compare_result_for_customer_based_on_quantity(self):
 		filters = {
-			'doc_type': 'Sales Order',
-			'range': 'Monthly',
-			'to_date': '2018-03-31',
-			'tree_type': 'Customer',
-			'company': '_Test Company 2',
-			'from_date': '2017-04-01',
-			'value_quantity': 'Quantity'
+			"doc_type": "Sales Order",
+			"range": "Monthly",
+			"to_date": "2018-03-31",
+			"tree_type": "Customer",
+			"company": "_Test Company 2",
+			"from_date": "2017-04-01",
+			"value_quantity": "Quantity",
 		}
 
 		report = execute(filters)
@@ -150,7 +149,7 @@
 				"jan_2018": 0.0,
 				"feb_2018": 20.0,
 				"mar_2018": 0.0,
-				"total":20.0
+				"total": 20.0,
 			},
 			{
 				"entity": "_Test Customer 2",
@@ -167,7 +166,7 @@
 				"jan_2018": 0.0,
 				"feb_2018": 0.0,
 				"mar_2018": 0.0,
-				"total":25.0
+				"total": 25.0,
 			},
 			{
 				"entity": "_Test Customer 3",
@@ -184,47 +183,66 @@
 				"jan_2018": 0.0,
 				"feb_2018": 0.0,
 				"mar_2018": 0.0,
-				"total": 30.0
-			}
+				"total": 30.0,
+			},
 		]
-		result = sorted(report[1], key=lambda k: k['entity'])
+		result = sorted(report[1], key=lambda k: k["entity"])
 		self.assertEqual(expected_data, result)
 
+
 def create_sales_orders():
 	frappe.set_user("Administrator")
 
-	make_sales_order(company="_Test Company 2", qty=10,
-		customer = "_Test Customer 1",
-		transaction_date = '2018-02-10',
-		warehouse = 'Finished Goods - _TC2',
-		currency = 'EUR')
+	make_sales_order(
+		company="_Test Company 2",
+		qty=10,
+		customer="_Test Customer 1",
+		transaction_date="2018-02-10",
+		warehouse="Finished Goods - _TC2",
+		currency="EUR",
+	)
 
-	make_sales_order(company="_Test Company 2",
-		qty=10, customer = "_Test Customer 1",
-		transaction_date = '2018-02-15',
-		warehouse = 'Finished Goods - _TC2',
-		currency = 'EUR')
+	make_sales_order(
+		company="_Test Company 2",
+		qty=10,
+		customer="_Test Customer 1",
+		transaction_date="2018-02-15",
+		warehouse="Finished Goods - _TC2",
+		currency="EUR",
+	)
 
-	make_sales_order(company = "_Test Company 2",
-		qty=10, customer = "_Test Customer 2",
-		transaction_date = '2017-10-10',
-		warehouse='Finished Goods - _TC2',
-		currency = 'EUR')
+	make_sales_order(
+		company="_Test Company 2",
+		qty=10,
+		customer="_Test Customer 2",
+		transaction_date="2017-10-10",
+		warehouse="Finished Goods - _TC2",
+		currency="EUR",
+	)
 
-	make_sales_order(company="_Test Company 2",
-		qty=15, customer = "_Test Customer 2",
-		transaction_date='2017-09-23',
-		warehouse='Finished Goods - _TC2',
-		currency = 'EUR')
+	make_sales_order(
+		company="_Test Company 2",
+		qty=15,
+		customer="_Test Customer 2",
+		transaction_date="2017-09-23",
+		warehouse="Finished Goods - _TC2",
+		currency="EUR",
+	)
 
-	make_sales_order(company="_Test Company 2",
-		qty=20, customer = "_Test Customer 3",
-		transaction_date='2017-06-15',
-		warehouse='Finished Goods - _TC2',
-		currency = 'EUR')
+	make_sales_order(
+		company="_Test Company 2",
+		qty=20,
+		customer="_Test Customer 3",
+		transaction_date="2017-06-15",
+		warehouse="Finished Goods - _TC2",
+		currency="EUR",
+	)
 
-	make_sales_order(company="_Test Company 2",
-		qty=10, customer = "_Test Customer 3",
-		transaction_date='2017-07-10',
-		warehouse='Finished Goods - _TC2',
-		currency = 'EUR')
+	make_sales_order(
+		company="_Test Company 2",
+		qty=10,
+		customer="_Test Customer 3",
+		transaction_date="2017-07-10",
+		warehouse="Finished Goods - _TC2",
+		currency="EUR",
+	)
diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
index 3e22d0f..720aa41 100644
--- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
+++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
@@ -1,11 +1,13 @@
 # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
 
-
 import copy
+from collections import OrderedDict
 
 import frappe
-from frappe import _
+from frappe import _, qb
+from frappe.query_builder import CustomFunction
+from frappe.query_builder.functions import Max
 from frappe.utils import date_diff, flt, getdate
 
 
@@ -18,14 +20,16 @@
 	columns = get_columns(filters)
 	conditions = get_conditions(filters)
 	data = get_data(conditions, filters)
+	so_elapsed_time = get_so_elapsed_time(data)
 
 	if not data:
 		return [], [], None, []
 
-	data, chart_data = prepare_data(data, filters)
+	data, chart_data = prepare_data(data, so_elapsed_time, filters)
 
 	return columns, data, None, chart_data
 
+
 def validate_filters(filters):
 	from_date, to_date = filters.get("from_date"), filters.get("to_date")
 
@@ -34,6 +38,7 @@
 	elif date_diff(to_date, from_date) < 0:
 		frappe.throw(_("To Date cannot be before From Date."))
 
+
 def get_conditions(filters):
 	conditions = ""
 	if filters.get("from_date") and filters.get("to_date"):
@@ -50,18 +55,19 @@
 
 	return conditions
 
+
 def get_data(conditions, filters):
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 		SELECT
 			so.transaction_date as date,
 			soi.delivery_date as delivery_date,
 			so.name as sales_order,
 			so.status, so.customer, soi.item_code,
-			DATEDIFF(CURDATE(), soi.delivery_date) as delay_days,
+			DATEDIFF(CURRENT_DATE, soi.delivery_date) as delay_days,
 			IF(so.status in ('Completed','To Bill'), 0, (SELECT delay_days)) as delay,
 			soi.qty, soi.delivered_qty,
 			(soi.qty - soi.delivered_qty) AS pending_qty,
-			IF((SELECT pending_qty) = 0, (TO_SECONDS(Max(dn.posting_date))-TO_SECONDS(so.transaction_date)), 0) as time_taken_to_deliver,
 			IFNULL(SUM(sii.qty), 0) as billed_qty,
 			soi.base_amount as amount,
 			(soi.delivered_qty * soi.base_rate) as delivered_qty_amount,
@@ -72,13 +78,9 @@
 			soi.description as description
 		FROM
 			`tabSales Order` so,
-			(`tabSales Order Item` soi
+			`tabSales Order Item` soi
 		LEFT JOIN `tabSales Invoice Item` sii
-			ON sii.so_detail = soi.name and sii.docstatus = 1)
-		LEFT JOIN `tabDelivery Note Item` dni
-			on dni.so_detail = soi.name
-		RIGHT JOIN `tabDelivery Note` dn
-			on dni.parent = dn.name and dn.docstatus = 1
+			ON sii.so_detail = soi.name and sii.docstatus = 1
 		WHERE
 			soi.parent = so.name
 			and so.status not in ('Stopped', 'Closed', 'On Hold')
@@ -86,11 +88,58 @@
 			{conditions}
 		GROUP BY soi.name
 		ORDER BY so.transaction_date ASC, soi.item_code ASC
-	""".format(conditions=conditions), filters, as_dict=1)
+	""".format(
+			conditions=conditions
+		),
+		filters,
+		as_dict=1,
+	)
 
 	return data
 
-def prepare_data(data, filters):
+
+def get_so_elapsed_time(data):
+	"""
+	query SO's elapsed time till latest delivery note
+	"""
+	so_elapsed_time = OrderedDict()
+	if data:
+		sales_orders = [x.sales_order for x in data]
+
+		so = qb.DocType("Sales Order")
+		soi = qb.DocType("Sales Order Item")
+		dn = qb.DocType("Delivery Note")
+		dni = qb.DocType("Delivery Note Item")
+
+		to_seconds = CustomFunction("TO_SECONDS", ["date"])
+
+		query = (
+			qb.from_(so)
+			.inner_join(soi)
+			.on(soi.parent == so.name)
+			.left_join(dni)
+			.on(dni.so_detail == soi.name)
+			.left_join(dn)
+			.on(dni.parent == dn.name)
+			.select(
+				so.name.as_("sales_order"),
+				soi.item_code.as_("so_item_code"),
+				(to_seconds(Max(dn.posting_date)) - to_seconds(so.transaction_date)).as_("elapsed_seconds"),
+			)
+			.where((so.name.isin(sales_orders)) & (dn.docstatus == 1))
+			.orderby(so.name, soi.name)
+			.groupby(soi.name)
+		)
+		dn_elapsed_time = query.run(as_dict=True)
+
+		for e in dn_elapsed_time:
+			key = (e.sales_order, e.so_item_code)
+			so_elapsed_time[key] = e.elapsed_seconds
+
+	return so_elapsed_time
+
+
+def prepare_data(data, so_elapsed_time, filters):
 	completed, pending = 0, 0
 
 	if filters.get("group_by_so"):
@@ -105,6 +154,13 @@
 		row["qty_to_bill"] = flt(row["qty"]) - flt(row["billed_qty"])
 
 		row["delay"] = 0 if row["delay"] and row["delay"] < 0 else row["delay"]
+
+		row["time_taken_to_deliver"] = (
+			so_elapsed_time.get((row.sales_order, row.item_code))
+			if row["status"] in ("To Bill", "Completed")
+			else 0
+		)
+
 		if filters.get("group_by_so"):
 			so_name = row["sales_order"]
 
@@ -119,8 +175,17 @@
 				so_row["delay"] = min(so_row["delay"], row["delay"])
 
 				# sum numeric columns
-				fields = ["qty", "delivered_qty", "pending_qty", "billed_qty", "qty_to_bill", "amount",
-					"delivered_qty_amount", "billed_amount", "pending_amount"]
+				fields = [
+					"qty",
+					"delivered_qty",
+					"pending_qty",
+					"billed_qty",
+					"qty_to_bill",
+					"amount",
+					"delivered_qty_amount",
+					"billed_amount",
+					"pending_amount",
+				]
 				for field in fields:
 					so_row[field] = flt(row[field]) + flt(so_row[field])
 
@@ -134,166 +199,148 @@
 
 	return data, chart_data
 
+
 def prepare_chart_data(pending, completed):
 	labels = ["Amount to Bill", "Billed Amount"]
 
 	return {
-		"data" : {
-			"labels": labels,
-			"datasets": [
-				{"values": [pending, completed]}
-				]
-		},
-		"type": 'donut',
-		"height": 300
+		"data": {"labels": labels, "datasets": [{"values": [pending, completed]}]},
+		"type": "donut",
+		"height": 300,
 	}
 
+
 def get_columns(filters):
 	columns = [
-		{
-			"label":_("Date"),
-			"fieldname": "date",
-			"fieldtype": "Date",
-			"width": 90
-		},
+		{"label": _("Date"), "fieldname": "date", "fieldtype": "Date", "width": 90},
 		{
 			"label": _("Sales Order"),
 			"fieldname": "sales_order",
 			"fieldtype": "Link",
 			"options": "Sales Order",
-			"width": 160
+			"width": 160,
 		},
-		{
-			"label":_("Status"),
-			"fieldname": "status",
-			"fieldtype": "Data",
-			"width": 130
-		},
+		{"label": _("Status"), "fieldname": "status", "fieldtype": "Data", "width": 130},
 		{
 			"label": _("Customer"),
 			"fieldname": "customer",
 			"fieldtype": "Link",
 			"options": "Customer",
-			"width": 130
-		}]
-
-	if not filters.get("group_by_so"):
-		columns.append({
-			"label":_("Item Code"),
-			"fieldname": "item_code",
-			"fieldtype": "Link",
-			"options": "Item",
-			"width": 100
-		})
-		columns.append({
-			"label":_("Description"),
-			"fieldname": "description",
-			"fieldtype": "Small Text",
-			"width": 100
-		})
-
-	columns.extend([
-		{
-			"label": _("Qty"),
-			"fieldname": "qty",
-			"fieldtype": "Float",
-			"width": 120,
-			"convertible": "qty"
-		},
-		{
-			"label": _("Delivered Qty"),
-			"fieldname": "delivered_qty",
-			"fieldtype": "Float",
-			"width": 120,
-			"convertible": "qty"
-		},
-		{
-			"label": _("Qty to Deliver"),
-			"fieldname": "pending_qty",
-			"fieldtype": "Float",
-			"width": 120,
-			"convertible": "qty"
-		},
-		{
-			"label": _("Billed Qty"),
-			"fieldname": "billed_qty",
-			"fieldtype": "Float",
-			"width": 80,
-			"convertible": "qty"
-		},
-		{
-			"label": _("Qty to Bill"),
-			"fieldname": "qty_to_bill",
-			"fieldtype": "Float",
-			"width": 80,
-			"convertible": "qty"
-		},
-		{
-			"label": _("Amount"),
-			"fieldname": "amount",
-			"fieldtype": "Currency",
-			"width": 110,
-			"options": "Company:company:default_currency",
-			"convertible": "rate"
-		},
-		{
-			"label": _("Billed Amount"),
-			"fieldname": "billed_amount",
-			"fieldtype": "Currency",
-			"width": 110,
-			"options": "Company:company:default_currency",
-			"convertible": "rate"
-		},
-		{
-			"label": _("Pending Amount"),
-			"fieldname": "pending_amount",
-			"fieldtype": "Currency",
 			"width": 130,
-			"options": "Company:company:default_currency",
-			"convertible": "rate"
 		},
-		{
-			"label": _("Amount Delivered"),
-			"fieldname": "delivered_qty_amount",
-			"fieldtype": "Currency",
-			"width": 100,
-			"options": "Company:company:default_currency",
-			"convertible": "rate"
-		},
-		{
-			"label":_("Delivery Date"),
-			"fieldname": "delivery_date",
-			"fieldtype": "Date",
-			"width": 120
-		},
-		{
-			"label": _("Delay (in Days)"),
-			"fieldname": "delay",
-			"fieldtype": "Data",
-			"width": 100
-		},
-		{
-			"label": _("Time Taken to Deliver"),
-			"fieldname": "time_taken_to_deliver",
-			"fieldtype": "Duration",
-			"width": 100
-		}
-	])
+	]
+
 	if not filters.get("group_by_so"):
-		columns.append({
-			"label": _("Warehouse"),
-			"fieldname": "warehouse",
-			"fieldtype": "Link",
-			"options": "Warehouse",
-			"width": 100
-		})
-	columns.append({
+		columns.append(
+			{
+				"label": _("Item Code"),
+				"fieldname": "item_code",
+				"fieldtype": "Link",
+				"options": "Item",
+				"width": 100,
+			}
+		)
+		columns.append(
+			{"label": _("Description"), "fieldname": "description", "fieldtype": "Small Text", "width": 100}
+		)
+
+	columns.extend(
+		[
+			{
+				"label": _("Qty"),
+				"fieldname": "qty",
+				"fieldtype": "Float",
+				"width": 120,
+				"convertible": "qty",
+			},
+			{
+				"label": _("Delivered Qty"),
+				"fieldname": "delivered_qty",
+				"fieldtype": "Float",
+				"width": 120,
+				"convertible": "qty",
+			},
+			{
+				"label": _("Qty to Deliver"),
+				"fieldname": "pending_qty",
+				"fieldtype": "Float",
+				"width": 120,
+				"convertible": "qty",
+			},
+			{
+				"label": _("Billed Qty"),
+				"fieldname": "billed_qty",
+				"fieldtype": "Float",
+				"width": 80,
+				"convertible": "qty",
+			},
+			{
+				"label": _("Qty to Bill"),
+				"fieldname": "qty_to_bill",
+				"fieldtype": "Float",
+				"width": 80,
+				"convertible": "qty",
+			},
+			{
+				"label": _("Amount"),
+				"fieldname": "amount",
+				"fieldtype": "Currency",
+				"width": 110,
+				"options": "Company:company:default_currency",
+				"convertible": "rate",
+			},
+			{
+				"label": _("Billed Amount"),
+				"fieldname": "billed_amount",
+				"fieldtype": "Currency",
+				"width": 110,
+				"options": "Company:company:default_currency",
+				"convertible": "rate",
+			},
+			{
+				"label": _("Pending Amount"),
+				"fieldname": "pending_amount",
+				"fieldtype": "Currency",
+				"width": 130,
+				"options": "Company:company:default_currency",
+				"convertible": "rate",
+			},
+			{
+				"label": _("Amount Delivered"),
+				"fieldname": "delivered_qty_amount",
+				"fieldtype": "Currency",
+				"width": 100,
+				"options": "Company:company:default_currency",
+				"convertible": "rate",
+			},
+			{"label": _("Delivery Date"), "fieldname": "delivery_date", "fieldtype": "Date", "width": 120},
+			{"label": _("Delay (in Days)"), "fieldname": "delay", "fieldtype": "Data", "width": 100},
+			{
+				"label": _("Time Taken to Deliver"),
+				"fieldname": "time_taken_to_deliver",
+				"fieldtype": "Duration",
+				"width": 100,
+			},
+		]
+	)
+	if not filters.get("group_by_so"):
+		columns.append(
+			{
+				"label": _("Warehouse"),
+				"fieldname": "warehouse",
+				"fieldtype": "Link",
+				"options": "Warehouse",
+				"width": 100,
+			}
+		)
+	columns.append(
+		{
 			"label": _("Company"),
 			"fieldname": "company",
 			"fieldtype": "Link",
 			"options": "Company",
-			"width": 100
-		})
-
+			"width": 100,
+		}
+	)
 
 	return columns
diff --git a/erpnext/selling/report/sales_order_analysis/test_sales_order_analysis.py b/erpnext/selling/report/sales_order_analysis/test_sales_order_analysis.py
new file mode 100644
index 0000000..241f435
--- /dev/null
+++ b/erpnext/selling/report/sales_order_analysis/test_sales_order_analysis.py
@@ -0,0 +1,254 @@
+import frappe
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import add_days
+
+from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note, make_sales_invoice
+from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
+from erpnext.selling.report.sales_order_analysis.sales_order_analysis import execute
+from erpnext.stock.doctype.item.test_item import create_item
+
+test_dependencies = ["Sales Order", "Item", "Sales Invoice", "Delivery Note"]
+
+
+class TestSalesOrderAnalysis(FrappeTestCase):
+	def create_sales_order(self, transaction_date, do_not_save=False, do_not_submit=False):
+		item = create_item(item_code="_Test Excavator", is_stock_item=0)
+		so = make_sales_order(
+			transaction_date=transaction_date,
+			item=item.item_code,
+			qty=10,
+			rate=100000,
+			do_not_save=True,
+		)
+		so.po_no = ""
+		so.taxes_and_charges = ""
+		so.taxes = ""
+		so.items[0].delivery_date = add_days(transaction_date, 15)
+		if not do_not_save:
+			so.save()
+			if not do_not_submit:
+				so.submit()
+		return item, so
+
+	def create_sales_invoice(self, so, do_not_save=False, do_not_submit=False):
+		sinv = make_sales_invoice(so.name)
+		sinv.posting_date = so.transaction_date
+		sinv.taxes_and_charges = ""
+		sinv.taxes = ""
+		if not do_not_save:
+			sinv.save()
+			if not do_not_submit:
+				sinv.submit()
+		return sinv
+
+	def create_delivery_note(self, so, do_not_save=False, do_not_submit=False):
+		dn = make_delivery_note(so.name)
+		dn.set_posting_time = True
+		dn.posting_date = add_days(so.transaction_date, 1)
+		if not do_not_save:
+			dn.save()
+			if not do_not_submit:
+				dn.submit()
+		return dn
+
+	def test_01_so_to_deliver_and_bill(self):
+		transaction_date = "2021-06-01"
+		item, so = self.create_sales_order(transaction_date)
+		columns, data, message, chart = execute(
+			{
+				"company": "_Test Company",
+				"from_date": "2021-06-01",
+				"to_date": "2021-06-30",
+				"status": ["To Deliver and Bill"],
+			}
+		)
+		expected_value = {
+			"status": "To Deliver and Bill",
+			"sales_order": so.name,
+			"delay_days": frappe.utils.date_diff(frappe.utils.datetime.date.today(), so.delivery_date),
+			"qty": 10,
+			"delivered_qty": 0,
+			"pending_qty": 10,
+			"qty_to_bill": 10,
+			"time_taken_to_deliver": 0,
+		}
+		self.assertEqual(len(data), 1)
+		for key, val in expected_value.items():
+			with self.subTest(key=key, val=val):
+				self.assertEqual(data[0][key], val)
+
+	def test_02_so_to_deliver(self):
+		transaction_date = "2021-06-01"
+		item, so = self.create_sales_order(transaction_date)
+		self.create_sales_invoice(so)
+		columns, data, message, chart = execute(
+			{
+				"company": "_Test Company",
+				"from_date": "2021-06-01",
+				"to_date": "2021-06-30",
+				"status": ["To Deliver"],
+			}
+		)
+		expected_value = {
+			"status": "To Deliver",
+			"sales_order": so.name,
+			"delay_days": frappe.utils.date_diff(frappe.utils.datetime.date.today(), so.delivery_date),
+			"qty": 10,
+			"delivered_qty": 0,
+			"pending_qty": 10,
+			"qty_to_bill": 0,
+			"time_taken_to_deliver": 0,
+		}
+		self.assertEqual(len(data), 1)
+		for key, val in expected_value.items():
+			with self.subTest(key=key, val=val):
+				self.assertEqual(data[0][key], val)
+
+	def test_03_so_to_bill(self):
+		transaction_date = "2021-06-01"
+		item, so = self.create_sales_order(transaction_date)
+		self.create_delivery_note(so)
+		columns, data, message, chart = execute(
+			{
+				"company": "_Test Company",
+				"from_date": "2021-06-01",
+				"to_date": "2021-06-30",
+				"status": ["To Bill"],
+			}
+		)
+		expected_value = {
+			"status": "To Bill",
+			"sales_order": so.name,
+			"delay_days": frappe.utils.date_diff(frappe.utils.datetime.date.today(), so.delivery_date),
+			"qty": 10,
+			"delivered_qty": 10,
+			"pending_qty": 0,
+			"qty_to_bill": 10,
+			"time_taken_to_deliver": 86400,
+		}
+		self.assertEqual(len(data), 1)
+		for key, val in expected_value.items():
+			with self.subTest(key=key, val=val):
+				self.assertEqual(data[0][key], val)
+
+	def test_04_so_completed(self):
+		transaction_date = "2021-06-01"
+		item, so = self.create_sales_order(transaction_date)
+		self.create_sales_invoice(so)
+		self.create_delivery_note(so)
+		columns, data, message, chart = execute(
+			{
+				"company": "_Test Company",
+				"from_date": "2021-06-01",
+				"to_date": "2021-06-30",
+				"status": ["Completed"],
+			}
+		)
+		expected_value = {
+			"status": "Completed",
+			"sales_order": so.name,
+			"delay_days": frappe.utils.date_diff(frappe.utils.datetime.date.today(), so.delivery_date),
+			"qty": 10,
+			"delivered_qty": 10,
+			"pending_qty": 0,
+			"qty_to_bill": 0,
+			"billed_qty": 10,
+			"time_taken_to_deliver": 86400,
+		}
+		self.assertEqual(len(data), 1)
+		for key, val in expected_value.items():
+			with self.subTest(key=key, val=val):
+				self.assertEqual(data[0][key], val)
+
+	def test_05_all_so_status(self):
+		columns, data, message, chart = execute(
+			{
+				"company": "_Test Company",
+				"from_date": "2021-06-01",
+				"to_date": "2021-06-30",
+			}
+		)
+		# SO's from first 4 test cases should be in output
+		self.assertEqual(len(data), 4)
+
+	def test_06_so_pending_delivery_with_multiple_delivery_notes(self):
+		transaction_date = "2021-06-01"
+		item, so = self.create_sales_order(transaction_date)
+
+		# bill 2 items
+		sinv1 = self.create_sales_invoice(so, do_not_save=True)
+		sinv1.items[0].qty = 2
+		sinv1 = sinv1.save().submit()
+		# deliver 2 items
+		dn1 = self.create_delivery_note(so, do_not_save=True)
+		dn1.items[0].qty = 2
+		dn1 = dn1.save().submit()
+
+		# bill 2 items
+		sinv2 = self.create_sales_invoice(so, do_not_save=True)
+		sinv2.items[0].qty = 2
+		sinv2 = sinv2.save().submit()
+		# deliver 1 item
+		dn2 = self.create_delivery_note(so, do_not_save=True)
+		dn2.items[0].qty = 1
+		dn2 = dn2.save().submit()
+
+		columns, data, message, chart = execute(
+			{
+				"company": "_Test Company",
+				"from_date": "2021-06-01",
+				"to_date": "2021-06-30",
+				"sales_order": [so.name],
+			}
+		)
+		expected_value = {
+			"status": "To Deliver and Bill",
+			"sales_order": so.name,
+			"delay_days": frappe.utils.date_diff(frappe.utils.datetime.date.today(), so.delivery_date),
+			"qty": 10,
+			"delivered_qty": 3,
+			"pending_qty": 7,
+			"qty_to_bill": 6,
+			"billed_qty": 4,
+			"time_taken_to_deliver": 0,
+		}
+		self.assertEqual(len(data), 1)
+		for key, val in expected_value.items():
+			with self.subTest(key=key, val=val):
+				self.assertEqual(data[0][key], val)
+
+	def test_07_so_delivered_with_multiple_delivery_notes(self):
+		transaction_date = "2021-06-01"
+		item, so = self.create_sales_order(transaction_date)
+
+		dn1 = self.create_delivery_note(so, do_not_save=True)
+		dn1.items[0].qty = 5
+		dn1 = dn1.save().submit()
+
+		dn2 = self.create_delivery_note(so, do_not_save=True)
+		dn2.items[0].qty = 5
+		dn2 = dn2.save().submit()
+
+		columns, data, message, chart = execute(
+			{
+				"company": "_Test Company",
+				"from_date": "2021-06-01",
+				"to_date": "2021-06-30",
+				"sales_order": [so.name],
+			}
+		)
+		expected_value = {
+			"status": "To Bill",
+			"sales_order": so.name,
+			"delay_days": frappe.utils.date_diff(frappe.utils.datetime.date.today(), so.delivery_date),
+			"qty": 10,
+			"delivered_qty": 10,
+			"pending_qty": 0,
+			"qty_to_bill": 10,
+			"billed_qty": 0,
+			"time_taken_to_deliver": 86400,
+		}
+		self.assertEqual(len(data), 1)
+		for key, val in expected_value.items():
+			with self.subTest(key=key, val=val):
+				self.assertEqual(data[0][key], val)
diff --git a/erpnext/selling/report/sales_order_trends/sales_order_trends.py b/erpnext/selling/report/sales_order_trends/sales_order_trends.py
index 5a71171..18f448c 100644
--- a/erpnext/selling/report/sales_order_trends/sales_order_trends.py
+++ b/erpnext/selling/report/sales_order_trends/sales_order_trends.py
@@ -8,7 +8,8 @@
 
 
 def execute(filters=None):
-	if not filters: filters ={}
+	if not filters:
+		filters = {}
 	data = []
 	conditions = get_columns(filters, "Sales Order")
 	data = get_data(filters, conditions)
@@ -16,6 +17,7 @@
 
 	return conditions["columns"], data, None, chart_data
 
+
 def get_chart_data(data, conditions, filters):
 	if not (data and conditions):
 		return []
@@ -28,32 +30,26 @@
 
 	# fetch only periodic columns as labels
 	columns = conditions.get("columns")[start:-2][1::2]
-	labels = [column.split(':')[0] for column in columns]
+	labels = [column.split(":")[0] for column in columns]
 	datapoints = [0] * len(labels)
 
 	for row in data:
 		# If group by filter, don't add first row of group (it's already summed)
-		if not row[start-1]:
+		if not row[start - 1]:
 			continue
 		# Remove None values and compute only periodic data
 		row = [x if x else 0 for x in row[start:-2]]
-		row  = row[1::2]
+		row = row[1::2]
 
 		for i in range(len(row)):
 			datapoints[i] += row[i]
 
 	return {
-		"data" : {
-			"labels" : labels,
-			"datasets" : [
-				{
-					"name" : _("{0}").format(filters.get("period")) + _(" Sales Value"),
-					"values" : datapoints
-				}
-			]
+		"data": {
+			"labels": labels,
+			"datasets": [{"name": _(filters.get("period")) + " " + _("Sales Value"), "values": datapoints}],
 		},
-		"type" : "line",
-		"lineOptions": {
-			"regionFill": 1
-		}
+		"type": "line",
+		"lineOptions": {"regionFill": 1},
+		"fieldtype": "Currency",
 	}
diff --git a/erpnext/selling/report/sales_partner_commission_summary/sales_partner_commission_summary.py b/erpnext/selling/report/sales_partner_commission_summary/sales_partner_commission_summary.py
index b775907..cf9ea21 100644
--- a/erpnext/selling/report/sales_partner_commission_summary/sales_partner_commission_summary.py
+++ b/erpnext/selling/report/sales_partner_commission_summary/sales_partner_commission_summary.py
@@ -7,80 +7,73 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	columns = get_columns(filters)
 	data = get_entries(filters)
 
 	return columns, data
 
+
 def get_columns(filters):
 	if not filters.get("doctype"):
 		msgprint(_("Please select the document type first"), raise_exception=1)
 
-	columns =[
+	columns = [
 		{
 			"label": _(filters["doctype"]),
 			"options": filters["doctype"],
 			"fieldname": "name",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Customer"),
 			"options": "Customer",
 			"fieldname": "customer",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Territory"),
 			"options": "Territory",
 			"fieldname": "territory",
 			"fieldtype": "Link",
-			"width": 100
+			"width": 100,
 		},
-		{
-			"label": _("Posting Date"),
-			"fieldname": "posting_date",
-			"fieldtype": "Date",
-			"width": 100
-		},
-		{
-			"label": _("Amount"),
-			"fieldname": "amount",
-			"fieldtype": "Currency",
-			"width": 120
-		},
+		{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 100},
+		{"label": _("Amount"), "fieldname": "amount", "fieldtype": "Currency", "width": 120},
 		{
 			"label": _("Sales Partner"),
 			"options": "Sales Partner",
 			"fieldname": "sales_partner",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Commission Rate %"),
 			"fieldname": "commission_rate",
 			"fieldtype": "Data",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Total Commission"),
 			"fieldname": "total_commission",
 			"fieldtype": "Currency",
-			"width": 120
-		}
+			"width": 120,
+		},
 	]
 
 	return columns
 
+
 def get_entries(filters):
-	date_field = ("transaction_date" if filters.get('doctype') == "Sales Order"
-		else "posting_date")
+	date_field = "transaction_date" if filters.get("doctype") == "Sales Order" else "posting_date"
 
 	conditions = get_conditions(filters, date_field)
-	entries = frappe.db.sql("""
+	entries = frappe.db.sql(
+		"""
 		SELECT
 			name, customer, territory, {0} as posting_date, base_net_total as amount,
 			sales_partner, commission_rate, total_commission
@@ -89,10 +82,16 @@
 		WHERE
 			{2} and docstatus = 1 and sales_partner is not null
 			and sales_partner != '' order by name desc, sales_partner
-		""".format(date_field, filters.get('doctype'), conditions), filters, as_dict=1)
+		""".format(
+			date_field, filters.get("doctype"), conditions
+		),
+		filters,
+		as_dict=1,
+	)
 
 	return entries
 
+
 def get_conditions(filters, date_field):
 	conditions = "1=1"
 
diff --git a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py
index a647eb4..f34f3e3 100644
--- a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py
+++ b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py
@@ -14,8 +14,15 @@
 
 def get_data_column(filters, partner_doctype):
 	data = []
-	period_list = get_period_list(filters.fiscal_year, filters.fiscal_year, '', '',
-		'Fiscal Year', filters.period, company=filters.company)
+	period_list = get_period_list(
+		filters.fiscal_year,
+		filters.fiscal_year,
+		"",
+		"",
+		"Fiscal Year",
+		filters.period,
+		company=filters.company,
+	)
 
 	rows = get_data(filters, period_list, partner_doctype)
 	columns = get_columns(filters, period_list, partner_doctype)
@@ -24,20 +31,19 @@
 		return columns, data
 
 	for key, value in rows.items():
-		value.update({
-			frappe.scrub(partner_doctype): key[0],
-			'item_group': key[1]
-		})
+		value.update({frappe.scrub(partner_doctype): key[0], "item_group": key[1]})
 
 		data.append(value)
 
 	return columns, data
 
+
 def get_data(filters, period_list, partner_doctype):
 	sales_field = frappe.scrub(partner_doctype)
 	sales_users_data = get_parents_data(filters, partner_doctype)
 
-	if not sales_users_data: return
+	if not sales_users_data:
+		return
 	sales_users, item_groups = [], []
 
 	for d in sales_users_data:
@@ -47,99 +53,110 @@
 		if d.item_group not in item_groups:
 			item_groups.append(d.item_group)
 
-	date_field = ("transaction_date"
-		if filters.get('doctype') == "Sales Order" else "posting_date")
+	date_field = "transaction_date" if filters.get("doctype") == "Sales Order" else "posting_date"
 
 	actual_data = get_actual_data(filters, item_groups, sales_users, date_field, sales_field)
 
-	return prepare_data(filters, sales_users_data,
-		actual_data, date_field, period_list, sales_field)
+	return prepare_data(filters, sales_users_data, actual_data, date_field, period_list, sales_field)
+
 
 def get_columns(filters, period_list, partner_doctype):
 	fieldtype, options = "Currency", "currency"
 
-	if filters.get("target_on") == 'Quantity':
+	if filters.get("target_on") == "Quantity":
 		fieldtype, options = "Float", ""
 
-	columns = [{
-		"fieldname": frappe.scrub(partner_doctype),
-		"label": _(partner_doctype),
-		"fieldtype": "Link",
-		"options": partner_doctype,
-		"width": 150
-	}, {
-		"fieldname": "item_group",
-		"label": _("Item Group"),
-		"fieldtype": "Link",
-		"options": "Item Group",
-		"width": 150
-	}]
+	columns = [
+		{
+			"fieldname": frappe.scrub(partner_doctype),
+			"label": _(partner_doctype),
+			"fieldtype": "Link",
+			"options": partner_doctype,
+			"width": 150,
+		},
+		{
+			"fieldname": "item_group",
+			"label": _("Item Group"),
+			"fieldtype": "Link",
+			"options": "Item Group",
+			"width": 150,
+		},
+	]
 
 	for period in period_list:
-		target_key = 'target_{}'.format(period.key)
-		variance_key = 'variance_{}'.format(period.key)
+		target_key = "target_{}".format(period.key)
+		variance_key = "variance_{}".format(period.key)
 
-		columns.extend([{
-			"fieldname": target_key,
-			"label": _("Target ({})").format(period.label),
-			"fieldtype": fieldtype,
-			"options": options,
-			"width": 150
-		}, {
-			"fieldname": period.key,
-			"label": _("Achieved ({})").format(period.label),
-			"fieldtype": fieldtype,
-			"options": options,
-			"width": 150
-		}, {
-			"fieldname": variance_key,
-			"label": _("Variance ({})").format(period.label),
-			"fieldtype": fieldtype,
-			"options": options,
-			"width": 150
-		}])
+		columns.extend(
+			[
+				{
+					"fieldname": target_key,
+					"label": _("Target ({})").format(period.label),
+					"fieldtype": fieldtype,
+					"options": options,
+					"width": 150,
+				},
+				{
+					"fieldname": period.key,
+					"label": _("Achieved ({})").format(period.label),
+					"fieldtype": fieldtype,
+					"options": options,
+					"width": 150,
+				},
+				{
+					"fieldname": variance_key,
+					"label": _("Variance ({})").format(period.label),
+					"fieldtype": fieldtype,
+					"options": options,
+					"width": 150,
+				},
+			]
+		)
 
-	columns.extend([{
-		"fieldname": "total_target",
-		"label": _("Total Target"),
-		"fieldtype": fieldtype,
-		"options": options,
-		"width": 150
-	}, {
-		"fieldname": "total_achieved",
-		"label": _("Total Achieved"),
-		"fieldtype": fieldtype,
-		"options": options,
-		"width": 150
-	}, {
-		"fieldname": "total_variance",
-		"label": _("Total Variance"),
-		"fieldtype": fieldtype,
-		"options": options,
-		"width": 150
-	}])
+	columns.extend(
+		[
+			{
+				"fieldname": "total_target",
+				"label": _("Total Target"),
+				"fieldtype": fieldtype,
+				"options": options,
+				"width": 150,
+			},
+			{
+				"fieldname": "total_achieved",
+				"label": _("Total Achieved"),
+				"fieldtype": fieldtype,
+				"options": options,
+				"width": 150,
+			},
+			{
+				"fieldname": "total_variance",
+				"label": _("Total Variance"),
+				"fieldtype": fieldtype,
+				"options": options,
+				"width": 150,
+			},
+		]
+	)
 
 	return columns
 
+
 def prepare_data(filters, sales_users_data, actual_data, date_field, period_list, sales_field):
 	rows = {}
 
-	target_qty_amt_field = ("target_qty"
-		if filters.get("target_on") == 'Quantity' else "target_amount")
+	target_qty_amt_field = "target_qty" if filters.get("target_on") == "Quantity" else "target_amount"
 
-	qty_or_amount_field = ("stock_qty"
-		if filters.get("target_on") == 'Quantity' else "base_net_amount")
+	qty_or_amount_field = "stock_qty" if filters.get("target_on") == "Quantity" else "base_net_amount"
 
 	for d in sales_users_data:
 		key = (d.parent, d.item_group)
-		dist_data = get_periodwise_distribution_data(d.distribution_id, period_list, filters.get("period"))
+		dist_data = get_periodwise_distribution_data(
+			d.distribution_id, period_list, filters.get("period")
+		)
 
 		if key not in rows:
-			rows.setdefault(key,{
-				'total_target': 0,
-				'total_achieved': 0,
-				'total_variance': 0
-			})
+			rows.setdefault(key, {"total_target": 0, "total_achieved": 0, "total_variance": 0})
 
 		details = rows[key]
 		for period in period_list:
@@ -147,15 +164,19 @@
 			if p_key not in details:
 				details[p_key] = 0
 
-			target_key = 'target_{}'.format(p_key)
-			variance_key = 'variance_{}'.format(p_key)
+			target_key = "target_{}".format(p_key)
+			variance_key = "variance_{}".format(p_key)
 			details[target_key] = (d.get(target_qty_amt_field) * dist_data.get(p_key)) / 100
 			details[variance_key] = 0
 			details["total_target"] += details[target_key]
 
 			for r in actual_data:
-				if (r.get(sales_field) == d.parent and r.item_group == d.item_group and
-					period.from_date <= r.get(date_field) and r.get(date_field) <= period.to_date):
+				if (
+					r.get(sales_field) == d.parent
+					and r.item_group == d.item_group
+					and period.from_date <= r.get(date_field)
+					and r.get(date_field) <= period.to_date
+				):
 					details[p_key] += r.get(qty_or_amount_field, 0)
 					details[variance_key] = details.get(p_key) - details.get(target_key)
 
@@ -164,24 +185,28 @@
 
 	return rows
 
+
 def get_actual_data(filters, item_groups, sales_users_or_territory_data, date_field, sales_field):
 	fiscal_year = get_fiscal_year(fiscal_year=filters.get("fiscal_year"), as_dict=1)
 	dates = [fiscal_year.year_start_date, fiscal_year.year_end_date]
 
 	select_field = "`tab{0}`.{1}".format(filters.get("doctype"), sales_field)
-	child_table = "`tab{0}`".format(filters.get("doctype") + ' Item')
+	child_table = "`tab{0}`".format(filters.get("doctype") + " Item")
 
-	if sales_field == 'sales_person':
+	if sales_field == "sales_person":
 		select_field = "`tabSales Team`.sales_person"
-		child_table = "`tab{0}`, `tabSales Team`".format(filters.get("doctype") + ' Item')
+		child_table = "`tab{0}`, `tabSales Team`".format(filters.get("doctype") + " Item")
 		cond = """`tabSales Team`.parent = `tab{0}`.name and
-			`tabSales Team`.sales_person in ({1}) """.format(filters.get("doctype"),
-			','.join(['%s'] * len(sales_users_or_territory_data)))
+			`tabSales Team`.sales_person in ({1}) """.format(
+			filters.get("doctype"), ",".join(["%s"] * len(sales_users_or_territory_data))
+		)
 	else:
-		cond = "`tab{0}`.{1} in ({2})".format(filters.get("doctype"), sales_field,
-			','.join(['%s'] * len(sales_users_or_territory_data)))
+		cond = "`tab{0}`.{1} in ({2})".format(
+			filters.get("doctype"), sales_field, ",".join(["%s"] * len(sales_users_or_territory_data))
+		)
 
-	return frappe.db.sql(""" SELECT `tab{child_doc}`.item_group,
+	return frappe.db.sql(
+		""" SELECT `tab{child_doc}`.item_group,
 			`tab{child_doc}`.stock_qty, `tab{child_doc}`.base_net_amount,
 			{select_field}, `tab{parent_doc}`.{date_field}
 		FROM `tab{parent_doc}`, {child_table}
@@ -189,26 +214,30 @@
 			`tab{child_doc}`.parent = `tab{parent_doc}`.name
 			and `tab{parent_doc}`.docstatus = 1 and {cond}
 			and `tab{child_doc}`.item_group in ({item_groups})
-			and `tab{parent_doc}`.{date_field} between %s and %s"""
-			.format(
-				cond = cond,
-				date_field = date_field,
-				select_field = select_field,
-				child_table = child_table,
-				parent_doc = filters.get("doctype"),
-				child_doc = filters.get("doctype") + ' Item',
-				item_groups = ','.join(['%s'] * len(item_groups))
-			), tuple(sales_users_or_territory_data + item_groups + dates), as_dict=1)
+			and `tab{parent_doc}`.{date_field} between %s and %s""".format(
+			cond=cond,
+			date_field=date_field,
+			select_field=select_field,
+			child_table=child_table,
+			parent_doc=filters.get("doctype"),
+			child_doc=filters.get("doctype") + " Item",
+			item_groups=",".join(["%s"] * len(item_groups)),
+		),
+		tuple(sales_users_or_territory_data + item_groups + dates),
+		as_dict=1,
+	)
+
 
 def get_parents_data(filters, partner_doctype):
-	filters_dict = {'parenttype': partner_doctype}
+	filters_dict = {"parenttype": partner_doctype}
 
-	target_qty_amt_field = ("target_qty"
-		if filters.get("target_on") == 'Quantity' else "target_amount")
+	target_qty_amt_field = "target_qty" if filters.get("target_on") == "Quantity" else "target_amount"
 
 	if filters.get("fiscal_year"):
 		filters_dict["fiscal_year"] = filters.get("fiscal_year")
 
-	return frappe.get_all('Target Detail',
-		filters = filters_dict,
-		fields = ["parent", "item_group", target_qty_amt_field, "fiscal_year", "distribution_id"])
+	return frappe.get_all(
+		"Target Detail",
+		filters=filters_dict,
+		fields=["parent", "item_group", target_qty_amt_field, "fiscal_year", "distribution_id"],
+	)
diff --git a/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.py b/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.py
index c64555b..2049520 100644
--- a/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.py
+++ b/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.py
@@ -7,120 +7,98 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	columns = get_columns(filters)
 	data = get_entries(filters)
 
 	return columns, data
 
+
 def get_columns(filters):
 	if not filters.get("doctype"):
 		msgprint(_("Please select the document type first"), raise_exception=1)
 
-	columns =[
+	columns = [
 		{
 			"label": _(filters["doctype"]),
 			"options": filters["doctype"],
 			"fieldname": "name",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Customer"),
 			"options": "Customer",
 			"fieldname": "customer",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Territory"),
 			"options": "Territory",
 			"fieldname": "territory",
 			"fieldtype": "Link",
-			"width": 100
+			"width": 100,
 		},
-		{
-			"label": _("Posting Date"),
-			"fieldname": "posting_date",
-			"fieldtype": "Date",
-			"width": 100
-		},
+		{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 100},
 		{
 			"label": _("Item Code"),
 			"fieldname": "item_code",
 			"fieldtype": "Link",
 			"options": "Item",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Item Group"),
 			"fieldname": "item_group",
 			"fieldtype": "Link",
 			"options": "Item Group",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Brand"),
 			"fieldname": "brand",
 			"fieldtype": "Link",
 			"options": "Brand",
-			"width": 100
+			"width": 100,
 		},
-		{
-			"label": _("Quantity"),
-			"fieldname": "qty",
-			"fieldtype": "Float",
-			"width": 120
-		},
-		{
-			"label": _("Rate"),
-			"fieldname": "rate",
-			"fieldtype": "Currency",
-			"width": 120
-		},
-		{
-			"label": _("Amount"),
-			"fieldname": "amount",
-			"fieldtype": "Currency",
-			"width": 120
-		},
+		{"label": _("Quantity"), "fieldname": "qty", "fieldtype": "Float", "width": 120},
+		{"label": _("Rate"), "fieldname": "rate", "fieldtype": "Currency", "width": 120},
+		{"label": _("Amount"), "fieldname": "amount", "fieldtype": "Currency", "width": 120},
 		{
 			"label": _("Sales Partner"),
 			"options": "Sales Partner",
 			"fieldname": "sales_partner",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Commission Rate %"),
 			"fieldname": "commission_rate",
 			"fieldtype": "Data",
-			"width": 100
+			"width": 100,
 		},
-		{
-			"label": _("Commission"),
-			"fieldname": "commission",
-			"fieldtype": "Currency",
-			"width": 120
-		},
+		{"label": _("Commission"), "fieldname": "commission", "fieldtype": "Currency", "width": 120},
 		{
 			"label": _("Currency"),
 			"fieldname": "currency",
 			"fieldtype": "Link",
 			"options": "Currency",
-			"width": 120
-		}
+			"width": 120,
+		},
 	]
 
 	return columns
 
+
 def get_entries(filters):
-	date_field = ("transaction_date" if filters.get('doctype') == "Sales Order"
-		else "posting_date")
+	date_field = "transaction_date" if filters.get("doctype") == "Sales Order" else "posting_date"
 
 	conditions = get_conditions(filters, date_field)
-	entries = frappe.db.sql("""
+	entries = frappe.db.sql(
+		"""
 		SELECT
 			dt.name, dt.customer, dt.territory, dt.{date_field} as posting_date, dt.currency,
 			dt_item.base_net_rate as rate, dt_item.qty, dt_item.base_net_amount as amount,
@@ -132,11 +110,16 @@
 			{cond} and dt.name = dt_item.parent and dt.docstatus = 1
 			and dt.sales_partner is not null and dt.sales_partner != ''
 			order by dt.name desc, dt.sales_partner
-		""".format(date_field=date_field, doctype=filters.get('doctype'),
-			cond=conditions), filters, as_dict=1)
+		""".format(
+			date_field=date_field, doctype=filters.get("doctype"), cond=conditions
+		),
+		filters,
+		as_dict=1,
+	)
 
 	return entries
 
+
 def get_conditions(filters, date_field):
 	conditions = "1=1"
 
@@ -150,18 +133,19 @@
 	if filters.get("to_date"):
 		conditions += " and dt.{0} <= %(to_date)s".format(date_field)
 
-	if not filters.get('show_return_entries'):
+	if not filters.get("show_return_entries"):
 		conditions += " and dt_item.qty > 0.0"
 
-	if filters.get('brand'):
+	if filters.get("brand"):
 		conditions += " and dt_item.brand = %(brand)s"
 
-	if filters.get('item_group'):
-		lft, rgt = frappe.get_cached_value('Item Group',
-			filters.get('item_group'), ['lft', 'rgt'])
+	if filters.get("item_group"):
+		lft, rgt = frappe.get_cached_value("Item Group", filters.get("item_group"), ["lft", "rgt"])
 
 		conditions += """ and dt_item.item_group in (select name from
-			`tabItem Group` where lft >= %s and rgt <= %s)""" % (lft, rgt)
-
+			`tabItem Group` where lft >= %s and rgt <= %s)""" % (
+			lft,
+			rgt,
+		)
 
 	return conditions
diff --git a/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.py b/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.py
index 1542e31..a8df530 100644
--- a/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.py
+++ b/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.py
@@ -7,102 +7,101 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	columns = get_columns(filters)
 	entries = get_entries(filters)
 	data = []
 
 	for d in entries:
-		data.append([
-			d.name, d.customer, d.territory, d.posting_date,
-			d.base_net_amount, d.sales_person, d.allocated_percentage, d.commission_rate, d.allocated_amount,d.incentives
-		])
+		data.append(
+			[
+				d.name,
+				d.customer,
+				d.territory,
+				d.posting_date,
+				d.base_net_amount,
+				d.sales_person,
+				d.allocated_percentage,
+				d.commission_rate,
+				d.allocated_amount,
+				d.incentives,
+			]
+		)
 
 	if data:
-		total_row = [""]*len(data[0])
+		total_row = [""] * len(data[0])
 		data.append(total_row)
 
 	return columns, data
 
+
 def get_columns(filters):
 	if not filters.get("doc_type"):
 		msgprint(_("Please select the document type first"), raise_exception=1)
 
-	columns =[
+	columns = [
 		{
 			"label": _(filters["doc_type"]),
 			"options": filters["doc_type"],
-			"fieldname": filters['doc_type'],
+			"fieldname": filters["doc_type"],
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Customer"),
 			"options": "Customer",
 			"fieldname": "customer",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Territory"),
 			"options": "Territory",
 			"fieldname": "territory",
 			"fieldtype": "Link",
-			"width": 100
+			"width": 100,
 		},
-		{
-			"label": _("Posting Date"),
-			"fieldname": "posting_date",
-			"fieldtype": "Date",
-			"width": 100
-		},
-		{
-			"label": _("Amount"),
-			"fieldname": "amount",
-			"fieldtype": "Currency",
-			"width": 120
-		},
+		{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 100},
+		{"label": _("Amount"), "fieldname": "amount", "fieldtype": "Currency", "width": 120},
 		{
 			"label": _("Sales Person"),
 			"options": "Sales Person",
 			"fieldname": "sales_person",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Contribution %"),
 			"fieldname": "contribution_percentage",
 			"fieldtype": "Data",
-			"width": 110
+			"width": 110,
 		},
 		{
 			"label": _("Commission Rate %"),
 			"fieldname": "commission_rate",
 			"fieldtype": "Data",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Contribution Amount"),
 			"fieldname": "contribution_amount",
 			"fieldtype": "Currency",
-			"width": 120
+			"width": 120,
 		},
-		{
-			"label": _("Incentives"),
-			"fieldname": "incentives",
-			"fieldtype": "Currency",
-			"width": 120
-		}
+		{"label": _("Incentives"), "fieldname": "incentives", "fieldtype": "Currency", "width": 120},
 	]
 
 	return columns
 
+
 def get_entries(filters):
 	date_field = filters["doc_type"] == "Sales Order" and "transaction_date" or "posting_date"
 
 	conditions, values = get_conditions(filters, date_field)
-	entries = frappe.db.sql("""
+	entries = frappe.db.sql(
+		"""
 		select
 			dt.name, dt.customer, dt.territory, dt.%s as posting_date,dt.base_net_total as base_net_amount,
 			st.commission_rate,st.sales_person, st.allocated_percentage, st.allocated_amount, st.incentives
@@ -111,11 +110,15 @@
 		where
 			st.parent = dt.name and st.parenttype = %s
 			and dt.docstatus = 1 %s order by dt.name desc,st.sales_person
-		""" %(date_field, filters["doc_type"], '%s', conditions),
-			tuple([filters["doc_type"]] + values), as_dict=1)
+		"""
+		% (date_field, filters["doc_type"], "%s", conditions),
+		tuple([filters["doc_type"]] + values),
+		as_dict=1,
+	)
 
 	return entries
 
+
 def get_conditions(filters, date_field):
 	conditions = [""]
 	values = []
diff --git a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py
index c621be8..cb6e8a1 100644
--- a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py
+++ b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py
@@ -9,7 +9,8 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	columns = get_columns(filters)
 	entries = get_entries(filters)
@@ -19,19 +20,33 @@
 	company_currency = get_company_currency(filters.get("company"))
 
 	for d in entries:
-		if d.stock_qty > 0 or filters.get('show_return_entries', 0):
-			data.append([
-				d.name, d.customer, d.territory, d.warehouse, d.posting_date, d.item_code,
-				item_details.get(d.item_code, {}).get("item_group"), item_details.get(d.item_code, {}).get("brand"),
-				d.stock_qty, d.base_net_amount, d.sales_person, d.allocated_percentage, d.contribution_amt, company_currency
-			])
+		if d.stock_qty > 0 or filters.get("show_return_entries", 0):
+			data.append(
+				[
+					d.name,
+					d.customer,
+					d.territory,
+					d.warehouse,
+					d.posting_date,
+					d.item_code,
+					item_details.get(d.item_code, {}).get("item_group"),
+					item_details.get(d.item_code, {}).get("brand"),
+					d.stock_qty,
+					d.base_net_amount,
+					d.sales_person,
+					d.allocated_percentage,
+					d.contribution_amt,
+					company_currency,
+				]
+			)
 
 	if data:
-		total_row = [""]*len(data[0])
+		total_row = [""] * len(data[0])
 		data.append(total_row)
 
 	return columns, data
 
+
 def get_columns(filters):
 	if not filters.get("doc_type"):
 		msgprint(_("Please select the document type first"), raise_exception=1)
@@ -40,102 +55,88 @@
 		{
 			"label": _(filters["doc_type"]),
 			"options": filters["doc_type"],
-			"fieldname": frappe.scrub(filters['doc_type']),
+			"fieldname": frappe.scrub(filters["doc_type"]),
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Customer"),
 			"options": "Customer",
 			"fieldname": "customer",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Territory"),
 			"options": "Territory",
 			"fieldname": "territory",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Warehouse"),
 			"options": "Warehouse",
 			"fieldname": "warehouse",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
-		{
-			"label": _("Posting Date"),
-			"fieldname": "posting_date",
-			"fieldtype": "Date",
-			"width": 140
-		},
+		{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 140},
 		{
 			"label": _("Item Code"),
 			"options": "Item",
 			"fieldname": "item_code",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Item Group"),
 			"options": "Item Group",
 			"fieldname": "item_group",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Brand"),
 			"options": "Brand",
 			"fieldname": "brand",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
-		{
-			"label": _("Qty"),
-			"fieldname": "qty",
-			"fieldtype": "Float",
-			"width": 140
-		},
+		{"label": _("Qty"), "fieldname": "qty", "fieldtype": "Float", "width": 140},
 		{
 			"label": _("Amount"),
 			"options": "currency",
 			"fieldname": "amount",
 			"fieldtype": "Currency",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Sales Person"),
 			"options": "Sales Person",
 			"fieldname": "sales_person",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
-		{
-			"label": _("Contribution %"),
-			"fieldname": "contribution",
-			"fieldtype": "Float",
-			"width": 140
-		},
+		{"label": _("Contribution %"), "fieldname": "contribution", "fieldtype": "Float", "width": 140},
 		{
 			"label": _("Contribution Amount"),
 			"options": "currency",
 			"fieldname": "contribution_amt",
 			"fieldtype": "Currency",
-			"width": 140
+			"width": 140,
 		},
 		{
-			"label":_("Currency"),
+			"label": _("Currency"),
 			"options": "Currency",
-			"fieldname":"currency",
-			"fieldtype":"Link",
-			"hidden" : 1
-		}
+			"fieldname": "currency",
+			"fieldtype": "Link",
+			"hidden": 1,
+		},
 	]
 
 	return columns
 
+
 def get_entries(filters):
 	date_field = filters["doc_type"] == "Sales Order" and "transaction_date" or "posting_date"
 	if filters["doc_type"] == "Sales Order":
@@ -144,7 +145,8 @@
 		qty_field = "qty"
 	conditions, values = get_conditions(filters, date_field)
 
-	entries = frappe.db.sql("""
+	entries = frappe.db.sql(
+		"""
 		SELECT
 			dt.name, dt.customer, dt.territory, dt.%s as posting_date, dt_item.item_code,
 			st.sales_person, st.allocated_percentage, dt_item.warehouse,
@@ -165,11 +167,24 @@
 		WHERE
 			st.parent = dt.name and dt.name = dt_item.parent and st.parenttype = %s
 			and dt.docstatus = 1 %s order by st.sales_person, dt.name desc
-		""" %(date_field, qty_field, qty_field, qty_field, filters["doc_type"], filters["doc_type"], '%s', conditions),
-			tuple([filters["doc_type"]] + values), as_dict=1)
+		"""
+		% (
+			date_field,
+			qty_field,
+			qty_field,
+			qty_field,
+			filters["doc_type"],
+			filters["doc_type"],
+			"%s",
+			conditions,
+		),
+		tuple([filters["doc_type"]] + values),
+		as_dict=1,
+	)
 
 	return entries
 
+
 def get_conditions(filters, date_field):
 	conditions = [""]
 	values = []
@@ -181,7 +196,11 @@
 
 	if filters.get("sales_person"):
 		lft, rgt = frappe.get_value("Sales Person", filters.get("sales_person"), ["lft", "rgt"])
-		conditions.append("exists(select name from `tabSales Person` where lft >= {0} and rgt <= {1} and name=st.sales_person)".format(lft, rgt))
+		conditions.append(
+			"exists(select name from `tabSales Person` where lft >= {0} and rgt <= {1} and name=st.sales_person)".format(
+				lft, rgt
+			)
+		)
 
 	if filters.get("from_date"):
 		conditions.append("dt.{0}>=%s".format(date_field))
@@ -193,23 +212,29 @@
 
 	items = get_items(filters)
 	if items:
-		conditions.append("dt_item.item_code in (%s)" % ', '.join(['%s']*len(items)))
+		conditions.append("dt_item.item_code in (%s)" % ", ".join(["%s"] * len(items)))
 		values += items
 
 	return " and ".join(conditions), values
 
+
 def get_items(filters):
-	if filters.get("item_group"): key = "item_group"
-	elif filters.get("brand"): key = "brand"
-	else: key = ""
+	if filters.get("item_group"):
+		key = "item_group"
+	elif filters.get("brand"):
+		key = "brand"
+	else:
+		key = ""
 
 	items = []
 	if key:
-		items = frappe.db.sql_list("""select name from tabItem where %s = %s""" %
-			(key, '%s'), (filters[key]))
+		items = frappe.db.sql_list(
+			"""select name from tabItem where %s = %s""" % (key, "%s"), (filters[key])
+		)
 
 	return items
 
+
 def get_item_details():
 	item_details = {}
 	for d in frappe.db.sql("""SELECT `name`, `item_group`, `brand` FROM `tabItem`""", as_dict=1):
diff --git a/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py b/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py
index b7b4d3a..5dfc1db 100644
--- a/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py
+++ b/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py
@@ -23,38 +23,39 @@
 			"fieldname": "territory",
 			"fieldtype": "Link",
 			"options": "Territory",
-			"width": 150
+			"width": 150,
 		},
 		{
 			"label": _("Opportunity Amount"),
 			"fieldname": "opportunity_amount",
 			"fieldtype": "Currency",
 			"options": currency,
-			"width": 150
+			"width": 150,
 		},
 		{
 			"label": _("Quotation Amount"),
 			"fieldname": "quotation_amount",
 			"fieldtype": "Currency",
 			"options": currency,
-			"width": 150
+			"width": 150,
 		},
 		{
 			"label": _("Order Amount"),
 			"fieldname": "order_amount",
 			"fieldtype": "Currency",
 			"options": currency,
-			"width": 150
+			"width": 150,
 		},
 		{
 			"label": _("Billing Amount"),
 			"fieldname": "billing_amount",
 			"fieldtype": "Currency",
 			"options": currency,
-			"width": 150
-		}
+			"width": 150,
+		},
 	]
 
+
 def get_data(filters=None):
 	data = []
 
@@ -84,26 +85,32 @@
 		if territory_orders:
 			t_order_names = [t.name for t in territory_orders]
 
-		territory_invoices = list(filter(lambda x: x.sales_order in t_order_names, sales_invoices)) if t_order_names and sales_invoices else []
+		territory_invoices = (
+			list(filter(lambda x: x.sales_order in t_order_names, sales_invoices))
+			if t_order_names and sales_invoices
+			else []
+		)
 
 		territory_data = {
 			"territory": territory.name,
 			"opportunity_amount": _get_total(territory_opportunities, "opportunity_amount"),
 			"quotation_amount": _get_total(territory_quotations),
 			"order_amount": _get_total(territory_orders),
-			"billing_amount": _get_total(territory_invoices)
+			"billing_amount": _get_total(territory_invoices),
 		}
 		data.append(territory_data)
 
 	return data
 
+
 def get_opportunities(filters):
 	conditions = ""
 
-	if filters.get('transaction_date'):
+	if filters.get("transaction_date"):
 		conditions = " WHERE transaction_date between {0} and {1}".format(
-			frappe.db.escape(filters['transaction_date'][0]),
-			frappe.db.escape(filters['transaction_date'][1]))
+			frappe.db.escape(filters["transaction_date"][0]),
+			frappe.db.escape(filters["transaction_date"][1]),
+		)
 
 	if filters.company:
 		if conditions:
@@ -112,11 +119,17 @@
 			conditions += " WHERE"
 		conditions += " company = %(company)s"
 
-
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		SELECT name, territory, opportunity_amount
 		FROM `tabOpportunity` {0}
-	""".format(conditions), filters, as_dict=1) #nosec
+	""".format(
+			conditions
+		),
+		filters,
+		as_dict=1,
+	)  # nosec
+
 
 def get_quotations(opportunities):
 	if not opportunities:
@@ -124,11 +137,18 @@
 
 	opportunity_names = [o.name for o in opportunities]
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		SELECT `name`,`base_grand_total`, `opportunity`
 		FROM `tabQuotation`
 		WHERE docstatus=1 AND opportunity in ({0})
-	""".format(', '.join(["%s"]*len(opportunity_names))), tuple(opportunity_names), as_dict=1) #nosec
+	""".format(
+			", ".join(["%s"] * len(opportunity_names))
+		),
+		tuple(opportunity_names),
+		as_dict=1,
+	)  # nosec
+
 
 def get_sales_orders(quotations):
 	if not quotations:
@@ -136,11 +156,18 @@
 
 	quotation_names = [q.name for q in quotations]
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 	SELECT so.`name`, so.`base_grand_total`, soi.prevdoc_docname as quotation
 	FROM `tabSales Order` so, `tabSales Order Item` soi
 	WHERE so.docstatus=1 AND so.name = soi.parent AND soi.prevdoc_docname in ({0})
-	""".format(', '.join(["%s"]*len(quotation_names))), tuple(quotation_names), as_dict=1) #nosec
+	""".format(
+			", ".join(["%s"] * len(quotation_names))
+		),
+		tuple(quotation_names),
+		as_dict=1,
+	)  # nosec
+
 
 def get_sales_invoice(sales_orders):
 	if not sales_orders:
@@ -148,11 +175,18 @@
 
 	so_names = [so.name for so in sales_orders]
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 	SELECT si.name, si.base_grand_total, sii.sales_order
 	FROM `tabSales Invoice` si, `tabSales Invoice Item` sii
 	WHERE si.docstatus=1 AND si.name = sii.parent AND sii.sales_order in ({0})
-	""".format(', '.join(["%s"]*len(so_names))), tuple(so_names), as_dict=1) #nosec
+	""".format(
+			", ".join(["%s"] * len(so_names))
+		),
+		tuple(so_names),
+		as_dict=1,
+	)  # nosec
+
 
 def _get_total(doclist, amount_field="base_grand_total"):
 	if not doclist:
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
index 98131f9..8ff01f5 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -12,8 +12,6 @@
 erpnext.selling.SellingController = class SellingController extends erpnext.TransactionController {
 	setup() {
 		super.setup();
-		this.frm.add_fetch("sales_partner", "commission_rate", "commission_rate");
-		this.frm.add_fetch("sales_person", "commission_rate", "commission_rate");
 	}
 
 	onload() {
@@ -43,6 +41,7 @@
 		me.frm.set_query('shipping_address_name', erpnext.queries.address_query);
 		me.frm.set_query('dispatch_address_name', erpnext.queries.dispatch_address_query);
 
+		erpnext.accounts.dimensions.setup_dimension_filters(me.frm, me.frm.doctype);
 
 		if(this.frm.fields_dict.selling_price_list) {
 			this.frm.set_query("selling_price_list", function() {
@@ -64,7 +63,7 @@
 			this.frm.set_query("item_code", "items", function() {
 				return {
 					query: "erpnext.controllers.queries.item_query",
-					filters: {'is_sales_item': 1, 'customer': cur_frm.doc.customer}
+					filters: {'is_sales_item': 1, 'customer': cur_frm.doc.customer, 'has_variants': 0}
 				}
 			});
 		}
@@ -513,4 +512,4 @@
 
 		dialog.show();
 	}
-})
\ No newline at end of file
+})
diff --git a/erpnext/selling/workspace/selling/selling.json b/erpnext/selling/workspace/selling/selling.json
index a700ad8..45e160d 100644
--- a/erpnext/selling/workspace/selling/selling.json
+++ b/erpnext/selling/workspace/selling/selling.json
@@ -5,7 +5,7 @@
    "label": "Sales Order Trends"
   }
  ],
- "content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Selling\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Sales Order Trends\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Quick Access</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Order\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Analytics\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Order Analysis\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Selling\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Items and Pricing\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]",
+ "content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Selling\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Sales Order Trends\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Quick Access</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Order\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Analytics\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Order Analysis\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Selling\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Items and Pricing\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]",
  "creation": "2020-01-28 11:49:12.092882",
  "docstatus": 0,
  "doctype": "Workspace",
@@ -317,115 +317,8 @@
   {
    "hidden": 0,
    "is_query_report": 0,
-   "label": "Key Reports",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 1,
-   "label": "Sales Analytics",
-   "link_count": 0,
-   "link_to": "Sales Analytics",
-   "link_type": "Report",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "Sales Order",
-   "hidden": 0,
-   "is_query_report": 1,
-   "label": "Sales Order Analysis",
-   "link_count": 0,
-   "link_to": "Sales Order Analysis",
-   "link_type": "Report",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Sales Funnel",
-   "link_count": 0,
-   "link_to": "sales-funnel",
-   "link_type": "Page",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "Sales Order",
-   "hidden": 0,
-   "is_query_report": 1,
-   "label": "Sales Order Trends",
-   "link_count": 0,
-   "link_to": "Sales Order Trends",
-   "link_type": "Report",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "Quotation",
-   "hidden": 0,
-   "is_query_report": 1,
-   "label": "Quotation Trends",
-   "link_count": 0,
-   "link_to": "Quotation Trends",
-   "link_type": "Report",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "Customer",
-   "hidden": 0,
-   "is_query_report": 1,
-   "label": "Customer Acquisition and Loyalty",
-   "link_count": 0,
-   "link_to": "Customer Acquisition and Loyalty",
-   "link_type": "Report",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "Sales Order",
-   "hidden": 0,
-   "is_query_report": 1,
-   "label": "Inactive Customers",
-   "link_count": 0,
-   "link_to": "Inactive Customers",
-   "link_type": "Report",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "Sales Order",
-   "hidden": 0,
-   "is_query_report": 1,
-   "label": "Sales Person-wise Transaction Summary",
-   "link_count": 0,
-   "link_to": "Sales Person-wise Transaction Summary",
-   "link_type": "Report",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "Item",
-   "hidden": 0,
-   "is_query_report": 1,
-   "label": "Item-wise Sales History",
-   "link_count": 0,
-   "link_to": "Item-wise Sales History",
-   "link_type": "Report",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
    "label": "Other Reports",
-   "link_count": 0,
+   "link_count": 12,
    "onboard": 0,
    "type": "Card Break"
   },
@@ -560,9 +453,258 @@
    "link_type": "Report",
    "onboard": 0,
    "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Key Reports",
+   "link_count": 22,
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Sales Analytics",
+   "link_count": 0,
+   "link_to": "Sales Analytics",
+   "link_type": "Report",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Sales Order",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Sales Order Analysis",
+   "link_count": 0,
+   "link_to": "Sales Order Analysis",
+   "link_type": "Report",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Sales Funnel",
+   "link_count": 0,
+   "link_to": "sales-funnel",
+   "link_type": "Page",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Sales Order",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Sales Order Trends",
+   "link_count": 0,
+   "link_to": "Sales Order Trends",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Quotation",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Quotation Trends",
+   "link_count": 0,
+   "link_to": "Quotation Trends",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Customer",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Customer Acquisition and Loyalty",
+   "link_count": 0,
+   "link_to": "Customer Acquisition and Loyalty",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Sales Order",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Inactive Customers",
+   "link_count": 0,
+   "link_to": "Inactive Customers",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Sales Order",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Sales Person-wise Transaction Summary",
+   "link_count": 0,
+   "link_to": "Sales Person-wise Transaction Summary",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Item",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Item-wise Sales History",
+   "link_count": 0,
+   "link_to": "Item-wise Sales History",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Lead",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Lead Details",
+   "link_count": 0,
+   "link_to": "Lead Details",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Address",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Customer Addresses And Contacts",
+   "link_count": 0,
+   "link_to": "Address And Contacts",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Item",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Available Stock for Packing Items",
+   "link_count": 0,
+   "link_to": "Available Stock for Packing Items",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Sales Order",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Pending SO Items For Purchase Request",
+   "link_count": 0,
+   "link_to": "Pending SO Items For Purchase Request",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Delivery Note",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Delivery Note Trends",
+   "link_count": 0,
+   "link_to": "Delivery Note Trends",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Sales Invoice",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Sales Invoice Trends",
+   "link_count": 0,
+   "link_to": "Sales Invoice Trends",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Customer",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Customer Credit Balance",
+   "link_count": 0,
+   "link_to": "Customer Credit Balance",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Customer",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Customers Without Any Sales Transactions",
+   "link_count": 0,
+   "link_to": "Customers Without Any Sales Transactions",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Customer",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Sales Partners Commission",
+   "link_count": 0,
+   "link_to": "Sales Partners Commission",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Sales Order",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Territory Target Variance Based On Item Group",
+   "link_count": 0,
+   "link_to": "Territory Target Variance Based On Item Group",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Sales Order",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Sales Person Target Variance Based On Item Group",
+   "link_count": 0,
+   "link_to": "Sales Person Target Variance Based On Item Group",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Sales Order",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Sales Partner Target Variance Based On Item Group",
+   "link_count": 0,
+   "link_to": "Sales Partner Target Variance based on Item Group",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Payment Terms Status for Sales Order",
+   "link_count": 0,
+   "link_to": "Payment Terms Status for Sales Order",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
   }
  ],
- "modified": "2022-01-13 17:43:02.778627",
+ "modified": "2022-04-26 13:29:55.087240",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Selling",
diff --git a/erpnext/setup/default_energy_point_rules.py b/erpnext/setup/default_energy_point_rules.py
index 1ce06b8..b7fe197 100644
--- a/erpnext/setup/default_energy_point_rules.py
+++ b/erpnext/setup/default_energy_point_rules.py
@@ -1,56 +1,48 @@
 from frappe import _
 
 doctype_rule_map = {
-	'Item': {
-		'points': 5,
-		'for_doc_event': 'New'
+	"Item": {"points": 5, "for_doc_event": "New"},
+	"Customer": {"points": 5, "for_doc_event": "New"},
+	"Supplier": {"points": 5, "for_doc_event": "New"},
+	"Lead": {"points": 2, "for_doc_event": "New"},
+	"Opportunity": {
+		"points": 10,
+		"for_doc_event": "Custom",
+		"condition": 'doc.status=="Converted"',
+		"rule_name": _("On Converting Opportunity"),
+		"user_field": "converted_by",
 	},
-	'Customer': {
-		'points': 5,
-		'for_doc_event': 'New'
+	"Sales Order": {
+		"points": 10,
+		"for_doc_event": "Submit",
+		"rule_name": _("On Sales Order Submission"),
+		"user_field": "modified_by",
 	},
-	'Supplier': {
-		'points': 5,
-		'for_doc_event': 'New'
+	"Purchase Order": {
+		"points": 10,
+		"for_doc_event": "Submit",
+		"rule_name": _("On Purchase Order Submission"),
+		"user_field": "modified_by",
 	},
-	'Lead': {
-		'points': 2,
-		'for_doc_event': 'New'
+	"Task": {
+		"points": 5,
+		"condition": 'doc.status == "Completed"',
+		"rule_name": _("On Task Completion"),
+		"user_field": "completed_by",
 	},
-	'Opportunity': {
-		'points': 10,
-		'for_doc_event': 'Custom',
-		'condition': 'doc.status=="Converted"',
-		'rule_name': _('On Converting Opportunity'),
-		'user_field': 'converted_by'
-	},
-	'Sales Order': {
-		'points': 10,
-		'for_doc_event': 'Submit',
-		'rule_name': _('On Sales Order Submission'),
-		'user_field': 'modified_by'
-	},
-	'Purchase Order': {
-		'points': 10,
-		'for_doc_event': 'Submit',
-		'rule_name': _('On Purchase Order Submission'),
-		'user_field': 'modified_by'
-	},
-	'Task': {
-		'points': 5,
-		'condition': 'doc.status == "Completed"',
-		'rule_name': _('On Task Completion'),
-		'user_field': 'completed_by'
-	}
 }
 
+
 def get_default_energy_point_rules():
-	return [{
-		'doctype': 'Energy Point Rule',
-		'reference_doctype': doctype,
-		'for_doc_event': rule.get('for_doc_event') or 'Custom',
-		'condition': rule.get('condition'),
-		'rule_name': rule.get('rule_name') or _('On {0} Creation').format(doctype),
-		'points': rule.get('points'),
-		'user_field': rule.get('user_field') or 'owner'
-	} for doctype, rule in doctype_rule_map.items()]
+	return [
+		{
+			"doctype": "Energy Point Rule",
+			"reference_doctype": doctype,
+			"for_doc_event": rule.get("for_doc_event") or "Custom",
+			"condition": rule.get("condition"),
+			"rule_name": rule.get("rule_name") or _("On {0} Creation").format(doctype),
+			"points": rule.get("points"),
+			"user_field": rule.get("user_field") or "owner",
+		}
+		for doctype, rule in doctype_rule_map.items()
+	]
diff --git a/erpnext/setup/default_success_action.py b/erpnext/setup/default_success_action.py
index a1f48df..2b9e75c 100644
--- a/erpnext/setup/default_success_action.py
+++ b/erpnext/setup/default_success_action.py
@@ -1,25 +1,31 @@
 from frappe import _
 
 doctype_list = [
-	'Purchase Receipt',
-	'Purchase Invoice',
-	'Quotation',
-	'Sales Order',
-	'Delivery Note',
-	'Sales Invoice'
+	"Purchase Receipt",
+	"Purchase Invoice",
+	"Quotation",
+	"Sales Order",
+	"Delivery Note",
+	"Sales Invoice",
 ]
 
+
 def get_message(doctype):
 	return _("{0} has been submitted successfully").format(_(doctype))
 
+
 def get_first_success_message(doctype):
 	return get_message(doctype)
 
+
 def get_default_success_action():
-	return [{
-		'doctype': 'Success Action',
-		'ref_doctype': doctype,
-		'message': get_message(doctype),
-		'first_success_message': get_first_success_message(doctype),
-		'next_actions': 'new\nprint\nemail'
-	} for doctype in doctype_list]
+	return [
+		{
+			"doctype": "Success Action",
+			"ref_doctype": doctype,
+			"message": get_message(doctype),
+			"first_success_message": get_first_success_message(doctype),
+			"next_actions": "new\nprint\nemail",
+		}
+		for doctype in doctype_list
+	]
diff --git a/erpnext/setup/doctype/authorization_control/authorization_control.py b/erpnext/setup/doctype/authorization_control/authorization_control.py
index 2a0d785..cfe3d62 100644
--- a/erpnext/setup/doctype/authorization_control/authorization_control.py
+++ b/erpnext/setup/doctype/authorization_control/authorization_control.py
@@ -12,90 +12,121 @@
 class AuthorizationControl(TransactionBase):
 	def get_appr_user_role(self, det, doctype_name, total, based_on, condition, item, company):
 		amt_list, appr_users, appr_roles = [], [], []
-		users, roles = '',''
+		users, roles = "", ""
 		if det:
 			for x in det:
 				amt_list.append(flt(x[0]))
 			max_amount = max(amt_list)
 
-			app_dtl = frappe.db.sql("""select approving_user, approving_role from `tabAuthorization Rule`
+			app_dtl = frappe.db.sql(
+				"""select approving_user, approving_role from `tabAuthorization Rule`
 				where transaction = %s and (value = %s or value > %s)
-				and docstatus != 2 and based_on = %s and company = %s %s""" %
-				('%s', '%s', '%s', '%s', '%s', condition),
-				(doctype_name, flt(max_amount), total, based_on, company))
+				and docstatus != 2 and based_on = %s and company = %s %s"""
+				% ("%s", "%s", "%s", "%s", "%s", condition),
+				(doctype_name, flt(max_amount), total, based_on, company),
+			)
 
 			if not app_dtl:
-				app_dtl = frappe.db.sql("""select approving_user, approving_role from `tabAuthorization Rule`
+				app_dtl = frappe.db.sql(
+					"""select approving_user, approving_role from `tabAuthorization Rule`
 					where transaction = %s and (value = %s or value > %s) and docstatus != 2
-					and based_on = %s and ifnull(company,'') = '' %s""" %
-					('%s', '%s', '%s', '%s', condition), (doctype_name, flt(max_amount), total, based_on))
+					and based_on = %s and ifnull(company,'') = '' %s"""
+					% ("%s", "%s", "%s", "%s", condition),
+					(doctype_name, flt(max_amount), total, based_on),
+				)
 
 			for d in app_dtl:
-				if(d[0]): appr_users.append(d[0])
-				if(d[1]): appr_roles.append(d[1])
+				if d[0]:
+					appr_users.append(d[0])
+				if d[1]:
+					appr_roles.append(d[1])
 
-			if not has_common(appr_roles, frappe.get_roles()) and not has_common(appr_users, [session['user']]):
+			if not has_common(appr_roles, frappe.get_roles()) and not has_common(
+				appr_users, [session["user"]]
+			):
 				frappe.msgprint(_("Not authroized since {0} exceeds limits").format(_(based_on)))
 				frappe.throw(_("Can be approved by {0}").format(comma_or(appr_roles + appr_users)))
 
-	def validate_auth_rule(self, doctype_name, total, based_on, cond, company, item = ''):
+	def validate_auth_rule(self, doctype_name, total, based_on, cond, company, item=""):
 		chk = 1
-		add_cond1,add_cond2	= '',''
-		if based_on == 'Itemwise Discount':
+		add_cond1, add_cond2 = "", ""
+		if based_on == "Itemwise Discount":
 			add_cond1 += " and master_name = " + frappe.db.escape(cstr(item))
-			itemwise_exists = frappe.db.sql("""select value from `tabAuthorization Rule`
+			itemwise_exists = frappe.db.sql(
+				"""select value from `tabAuthorization Rule`
 				where transaction = %s and value <= %s
-				and based_on = %s and company = %s and docstatus != 2 %s %s""" %
-				('%s', '%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on, company))
+				and based_on = %s and company = %s and docstatus != 2 %s %s"""
+				% ("%s", "%s", "%s", "%s", cond, add_cond1),
+				(doctype_name, total, based_on, company),
+			)
 
 			if not itemwise_exists:
-				itemwise_exists = frappe.db.sql("""select value from `tabAuthorization Rule`
+				itemwise_exists = frappe.db.sql(
+					"""select value from `tabAuthorization Rule`
 					where transaction = %s and value <= %s and based_on = %s
-					and ifnull(company,'') = ''	and docstatus != 2 %s %s""" %
-					('%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on))
+					and ifnull(company,'') = ''	and docstatus != 2 %s %s"""
+					% ("%s", "%s", "%s", cond, add_cond1),
+					(doctype_name, total, based_on),
+				)
 
 			if itemwise_exists:
-				self.get_appr_user_role(itemwise_exists, doctype_name, total, based_on, cond+add_cond1, item,company)
+				self.get_appr_user_role(
+					itemwise_exists, doctype_name, total, based_on, cond + add_cond1, item, company
+				)
 				chk = 0
 		if chk == 1:
-			if based_on == 'Itemwise Discount':
+			if based_on == "Itemwise Discount":
 				add_cond2 += " and ifnull(master_name,'') = ''"
 
-			appr = frappe.db.sql("""select value from `tabAuthorization Rule`
+			appr = frappe.db.sql(
+				"""select value from `tabAuthorization Rule`
 				where transaction = %s and value <= %s and based_on = %s
-				and company = %s and docstatus != 2 %s %s""" %
-				('%s', '%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on, company))
+				and company = %s and docstatus != 2 %s %s"""
+				% ("%s", "%s", "%s", "%s", cond, add_cond2),
+				(doctype_name, total, based_on, company),
+			)
 
 			if not appr:
-				appr = frappe.db.sql("""select value from `tabAuthorization Rule`
+				appr = frappe.db.sql(
+					"""select value from `tabAuthorization Rule`
 					where transaction = %s and value <= %s and based_on = %s
-					and ifnull(company,'') = '' and docstatus != 2 %s %s""" %
-					('%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on))
+					and ifnull(company,'') = '' and docstatus != 2 %s %s"""
+					% ("%s", "%s", "%s", cond, add_cond2),
+					(doctype_name, total, based_on),
+				)
 
-			self.get_appr_user_role(appr, doctype_name, total, based_on, cond+add_cond2, item, company)
+			self.get_appr_user_role(appr, doctype_name, total, based_on, cond + add_cond2, item, company)
 
 	def bifurcate_based_on_type(self, doctype_name, total, av_dis, based_on, doc_obj, val, company):
-		add_cond = ''
+		add_cond = ""
 		auth_value = av_dis
 
-		if val == 1: add_cond += " and system_user = {}".format(frappe.db.escape(session['user']))
-		elif val == 2: add_cond += " and system_role IN %s" % ("('"+"','".join(frappe.get_roles())+"')")
-		else: add_cond += " and ifnull(system_user,'') = '' and ifnull(system_role,'') = ''"
+		if val == 1:
+			add_cond += " and system_user = {}".format(frappe.db.escape(session["user"]))
+		elif val == 2:
+			add_cond += " and system_role IN %s" % ("('" + "','".join(frappe.get_roles()) + "')")
+		else:
+			add_cond += " and ifnull(system_user,'') = '' and ifnull(system_role,'') = ''"
 
-		if based_on == 'Grand Total': auth_value = total
-		elif based_on == 'Customerwise Discount':
+		if based_on == "Grand Total":
+			auth_value = total
+		elif based_on == "Customerwise Discount":
 			if doc_obj:
-				if doc_obj.doctype == 'Sales Invoice': customer = doc_obj.customer
-				else: customer = doc_obj.customer_name
+				if doc_obj.doctype == "Sales Invoice":
+					customer = doc_obj.customer
+				else:
+					customer = doc_obj.customer_name
 				add_cond = " and master_name = {}".format(frappe.db.escape(customer))
-		if based_on == 'Itemwise Discount':
+		if based_on == "Itemwise Discount":
 			if doc_obj:
 				for t in doc_obj.get("items"):
-					self.validate_auth_rule(doctype_name, t.discount_percentage, based_on, add_cond, company,t.item_code )
+					self.validate_auth_rule(
+						doctype_name, t.discount_percentage, based_on, add_cond, company, t.item_code
+					)
 		else:
 			self.validate_auth_rule(doctype_name, auth_value, based_on, add_cond, company)
 
-	def validate_approving_authority(self, doctype_name,company, total, doc_obj = ''):
+	def validate_approving_authority(self, doctype_name, company, total, doc_obj=""):
 		if not frappe.db.count("Authorization Rule"):
 			return
 
@@ -104,61 +135,90 @@
 			price_list_rate, base_rate = 0, 0
 			for d in doc_obj.get("items"):
 				if d.base_rate:
-					price_list_rate += flt(d.base_price_list_rate) or flt(d.base_rate)
-					base_rate += flt(d.base_rate)
+					price_list_rate += (flt(d.base_price_list_rate) or flt(d.base_rate)) * flt(d.qty)
+					base_rate += flt(d.base_rate) * flt(d.qty)
 			if doc_obj.get("discount_amount"):
 				base_rate -= flt(doc_obj.discount_amount)
 
-			if price_list_rate: av_dis = 100 - flt(base_rate * 100 / price_list_rate)
+			if price_list_rate:
+				av_dis = 100 - flt(base_rate * 100 / price_list_rate)
 
-		final_based_on = ['Grand Total','Average Discount','Customerwise Discount','Itemwise Discount']
+		final_based_on = [
+			"Grand Total",
+			"Average Discount",
+			"Customerwise Discount",
+			"Itemwise Discount",
+		]
 
 		# Check for authorization set for individual user
-		based_on = [x[0] for x in frappe.db.sql("""select distinct based_on from `tabAuthorization Rule`
+		based_on = [
+			x[0]
+			for x in frappe.db.sql(
+				"""select distinct based_on from `tabAuthorization Rule`
 			where transaction = %s and system_user = %s
 			and (company = %s or ifnull(company,'')='') and docstatus != 2""",
-			(doctype_name, session['user'], company))]
+				(doctype_name, session["user"], company),
+			)
+		]
 
 		for d in based_on:
 			self.bifurcate_based_on_type(doctype_name, total, av_dis, d, doc_obj, 1, company)
 
 		# Remove user specific rules from global authorization rules
 		for r in based_on:
-			if r in final_based_on and r != 'Itemwise Discount': final_based_on.remove(r)
+			if r in final_based_on and r != "Itemwise Discount":
+				final_based_on.remove(r)
 
 		# Check for authorization set on particular roles
-		based_on = [x[0] for x in frappe.db.sql("""select based_on
+		based_on = [
+			x[0]
+			for x in frappe.db.sql(
+				"""select based_on
 			from `tabAuthorization Rule`
 			where transaction = %s and system_role IN (%s) and based_on IN (%s)
 			and (company = %s or ifnull(company,'')='')
 			and docstatus != 2
-		""" % ('%s', "'"+"','".join(frappe.get_roles())+"'", "'"+"','".join(final_based_on)+"'", '%s'), (doctype_name, company))]
+		"""
+				% (
+					"%s",
+					"'" + "','".join(frappe.get_roles()) + "'",
+					"'" + "','".join(final_based_on) + "'",
+					"%s",
+				),
+				(doctype_name, company),
+			)
+		]
 
 		for d in based_on:
 			self.bifurcate_based_on_type(doctype_name, total, av_dis, d, doc_obj, 2, company)
 
 		# Remove role specific rules from global authorization rules
 		for r in based_on:
-			if r in final_based_on and r != 'Itemwise Discount': final_based_on.remove(r)
+			if r in final_based_on and r != "Itemwise Discount":
+				final_based_on.remove(r)
 
 		# Check for global authorization
 		for g in final_based_on:
 			self.bifurcate_based_on_type(doctype_name, total, av_dis, g, doc_obj, 0, company)
 
-	def get_value_based_rule(self,doctype_name,employee,total_claimed_amount,company):
-		val_lst =[]
-		val = frappe.db.sql("""select value from `tabAuthorization Rule`
+	def get_value_based_rule(self, doctype_name, employee, total_claimed_amount, company):
+		val_lst = []
+		val = frappe.db.sql(
+			"""select value from `tabAuthorization Rule`
 			where transaction=%s and (to_emp=%s or
 				to_designation IN (select designation from `tabEmployee` where name=%s))
 			and ifnull(value,0)< %s and company = %s and docstatus!=2""",
-			(doctype_name,employee,employee,total_claimed_amount,company))
+			(doctype_name, employee, employee, total_claimed_amount, company),
+		)
 
 		if not val:
-			val = frappe.db.sql("""select value from `tabAuthorization Rule`
+			val = frappe.db.sql(
+				"""select value from `tabAuthorization Rule`
 				where transaction=%s and (to_emp=%s or
 					to_designation IN (select designation from `tabEmployee` where name=%s))
 				and ifnull(value,0)< %s and ifnull(company,'') = '' and docstatus!=2""",
-				(doctype_name, employee, employee, total_claimed_amount))
+				(doctype_name, employee, employee, total_claimed_amount),
+			)
 
 		if val:
 			val_lst = [y[0] for y in val]
@@ -166,64 +226,83 @@
 			val_lst.append(0)
 
 		max_val = max(val_lst)
-		rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user
+		rule = frappe.db.sql(
+			"""select name, to_emp, to_designation, approving_role, approving_user
 			from `tabAuthorization Rule`
 			where transaction=%s and company = %s
 			and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s))
 			and ifnull(value,0)= %s and docstatus!=2""",
-			(doctype_name,company,employee,employee,flt(max_val)), as_dict=1)
+			(doctype_name, company, employee, employee, flt(max_val)),
+			as_dict=1,
+		)
 
 		if not rule:
-			rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user
+			rule = frappe.db.sql(
+				"""select name, to_emp, to_designation, approving_role, approving_user
 				from `tabAuthorization Rule`
 				where transaction=%s and ifnull(company,'') = ''
 				and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s))
 				and ifnull(value,0)= %s and docstatus!=2""",
-				(doctype_name,employee,employee,flt(max_val)), as_dict=1)
+				(doctype_name, employee, employee, flt(max_val)),
+				as_dict=1,
+			)
 
 		return rule
 
 	# related to payroll module only
-	def get_approver_name(self, doctype_name, total, doc_obj=''):
-		app_user=[]
-		app_specific_user =[]
-		rule ={}
+	def get_approver_name(self, doctype_name, total, doc_obj=""):
+		app_user = []
+		app_specific_user = []
+		rule = {}
 
 		if doc_obj:
-			if doctype_name == 'Expense Claim':
-				rule = self.get_value_based_rule(doctype_name, doc_obj.employee,
-					doc_obj.total_claimed_amount, doc_obj.company)
-			elif doctype_name == 'Appraisal':
-				rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user
+			if doctype_name == "Expense Claim":
+				rule = self.get_value_based_rule(
+					doctype_name, doc_obj.employee, doc_obj.total_claimed_amount, doc_obj.company
+				)
+			elif doctype_name == "Appraisal":
+				rule = frappe.db.sql(
+					"""select name, to_emp, to_designation, approving_role, approving_user
 					from `tabAuthorization Rule` where transaction=%s
 					and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s))
 					and company = %s and docstatus!=2""",
-					(doctype_name,doc_obj.employee, doc_obj.employee, doc_obj.company),as_dict=1)
+					(doctype_name, doc_obj.employee, doc_obj.employee, doc_obj.company),
+					as_dict=1,
+				)
 
 				if not rule:
-					rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user
+					rule = frappe.db.sql(
+						"""select name, to_emp, to_designation, approving_role, approving_user
 						from `tabAuthorization Rule`
 						where transaction=%s and (to_emp=%s or
 							to_designation IN (select designation from `tabEmployee` where name=%s))
 							and ifnull(company,'') = '' and docstatus!=2""",
-							(doctype_name,doc_obj.employee, doc_obj.employee), as_dict=1)
+						(doctype_name, doc_obj.employee, doc_obj.employee),
+						as_dict=1,
+					)
 
 			if rule:
 				for m in rule:
-					if m['to_emp'] or m['to_designation']:
-						if m['approving_user']:
-							app_specific_user.append(m['approving_user'])
-						elif m['approving_role']:
-							user_lst = [z[0] for z in frappe.db.sql("""select distinct t1.name
+					if m["to_emp"] or m["to_designation"]:
+						if m["approving_user"]:
+							app_specific_user.append(m["approving_user"])
+						elif m["approving_role"]:
+							user_lst = [
+								z[0]
+								for z in frappe.db.sql(
+									"""select distinct t1.name
 								from `tabUser` t1, `tabHas Role` t2 where t2.role=%s
 								and t2.parent=t1.name and t1.name !='Administrator'
-								and t1.name != 'Guest' and t1.docstatus !=2""", m['approving_role'])]
+								and t1.name != 'Guest' and t1.docstatus !=2""",
+									m["approving_role"],
+								)
+							]
 
 							for x in user_lst:
 								if not x in app_user:
 									app_user.append(x)
 
-			if len(app_specific_user) >0:
+			if len(app_specific_user) > 0:
 				return app_specific_user
 			else:
 				return app_user
diff --git a/erpnext/setup/doctype/authorization_rule/authorization_rule.py b/erpnext/setup/doctype/authorization_rule/authorization_rule.py
index e07de3b..faecd5a 100644
--- a/erpnext/setup/doctype/authorization_rule/authorization_rule.py
+++ b/erpnext/setup/doctype/authorization_rule/authorization_rule.py
@@ -10,40 +10,58 @@
 
 class AuthorizationRule(Document):
 	def check_duplicate_entry(self):
-		exists = frappe.db.sql("""select name, docstatus from `tabAuthorization Rule`
+		exists = frappe.db.sql(
+			"""select name, docstatus from `tabAuthorization Rule`
 			where transaction = %s and based_on = %s and system_user = %s
 			and system_role = %s and approving_user = %s and approving_role = %s
 			and to_emp =%s and to_designation=%s and name != %s""",
-			(self.transaction, self.based_on, cstr(self.system_user),
-				cstr(self.system_role), cstr(self.approving_user),
-				cstr(self.approving_role), cstr(self.to_emp),
-				cstr(self.to_designation), self.name))
-		auth_exists = exists and exists[0][0] or ''
+			(
+				self.transaction,
+				self.based_on,
+				cstr(self.system_user),
+				cstr(self.system_role),
+				cstr(self.approving_user),
+				cstr(self.approving_role),
+				cstr(self.to_emp),
+				cstr(self.to_designation),
+				self.name,
+			),
+		)
+		auth_exists = exists and exists[0][0] or ""
 		if auth_exists:
 			frappe.throw(_("Duplicate Entry. Please check Authorization Rule {0}").format(auth_exists))
 
-
 	def validate_rule(self):
-		if self.transaction != 'Appraisal':
+		if self.transaction != "Appraisal":
 			if not self.approving_role and not self.approving_user:
 				frappe.throw(_("Please enter Approving Role or Approving User"))
 			elif self.system_user and self.system_user == self.approving_user:
 				frappe.throw(_("Approving User cannot be same as user the rule is Applicable To"))
 			elif self.system_role and self.system_role == self.approving_role:
 				frappe.throw(_("Approving Role cannot be same as role the rule is Applicable To"))
-			elif self.transaction in ['Purchase Order', 'Purchase Receipt', \
-					'Purchase Invoice', 'Stock Entry'] and self.based_on \
-					in ['Average Discount', 'Customerwise Discount', 'Itemwise Discount']:
-				frappe.throw(_("Cannot set authorization on basis of Discount for {0}").format(self.transaction))
-			elif self.based_on == 'Average Discount' and flt(self.value) > 100.00:
+			elif self.transaction in [
+				"Purchase Order",
+				"Purchase Receipt",
+				"Purchase Invoice",
+				"Stock Entry",
+			] and self.based_on in [
+				"Average Discount",
+				"Customerwise Discount",
+				"Itemwise Discount",
+			]:
+				frappe.throw(
+					_("Cannot set authorization on basis of Discount for {0}").format(self.transaction)
+				)
+			elif self.based_on == "Average Discount" and flt(self.value) > 100.00:
 				frappe.throw(_("Discount must be less than 100"))
-			elif self.based_on == 'Customerwise Discount' and not self.master_name:
+			elif self.based_on == "Customerwise Discount" and not self.master_name:
 				frappe.throw(_("Customer required for 'Customerwise Discount'"))
 		else:
-			if self.transaction == 'Appraisal':
+			if self.transaction == "Appraisal":
 				self.based_on = "Not Applicable"
 
 	def validate(self):
 		self.check_duplicate_entry()
 		self.validate_rule()
-		if not self.value: self.value = 0.0
+		if not self.value:
+			self.value = 0.0
diff --git a/erpnext/setup/doctype/authorization_rule/test_authorization_rule.py b/erpnext/setup/doctype/authorization_rule/test_authorization_rule.py
index 7d3d5d4..55c1bbb 100644
--- a/erpnext/setup/doctype/authorization_rule/test_authorization_rule.py
+++ b/erpnext/setup/doctype/authorization_rule/test_authorization_rule.py
@@ -5,5 +5,6 @@
 
 # test_records = frappe.get_test_records('Authorization Rule')
 
+
 class TestAuthorizationRule(unittest.TestCase):
 	pass
diff --git a/erpnext/setup/doctype/brand/brand.py b/erpnext/setup/doctype/brand/brand.py
index 9b91b45..1bb6fc9 100644
--- a/erpnext/setup/doctype/brand/brand.py
+++ b/erpnext/setup/doctype/brand/brand.py
@@ -11,6 +11,7 @@
 class Brand(Document):
 	pass
 
+
 def get_brand_defaults(item, company):
 	item = frappe.get_cached_doc("Item", item)
 	if item.brand:
diff --git a/erpnext/setup/doctype/brand/test_brand.py b/erpnext/setup/doctype/brand/test_brand.py
index 1c71448..2e030b0 100644
--- a/erpnext/setup/doctype/brand/test_brand.py
+++ b/erpnext/setup/doctype/brand/test_brand.py
@@ -3,4 +3,4 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Brand')
+test_records = frappe.get_test_records("Brand")
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index dd185fc..0de5b2d 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -233,7 +233,8 @@
 		["expenses_included_in_asset_valuation", {"account_type": "Expenses Included In Asset Valuation"}],
 		["capital_work_in_progress_account", {"account_type": "Capital Work in Progress"}],
 		["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}],
-		["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}]
+		["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}],
+		["default_provisional_account", {"root_type": ["in", ["Liability", "Asset"]]}]
 	], function(i, v) {
 		erpnext.company.set_custom_query(frm, v);
 	});
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index 370a327..cc62a2a 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -147,9 +147,8 @@
   },
   {
    "fieldname": "domain",
-   "fieldtype": "Link",
-   "label": "Domain",
-   "options": "Domain"
+   "fieldtype": "Data",
+   "label": "Domain"
   },
   {
    "fieldname": "parent_company",
@@ -748,7 +747,7 @@
  "image_field": "company_logo",
  "is_tree": 1,
  "links": [],
- "modified": "2022-01-25 10:33:16.826067",
+ "modified": "2022-04-27 10:33:16.826067",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Company",
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 36ad8fe..9ffd6df 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -18,7 +18,7 @@
 
 
 class Company(NestedSet):
-	nsm_parent_field = 'parent_company'
+	nsm_parent_field = "parent_company"
 
 	def onload(self):
 		load_address_and_contact(self, "company")
@@ -26,12 +26,24 @@
 	@frappe.whitelist()
 	def check_if_transactions_exist(self):
 		exists = False
-		for doctype in ["Sales Invoice", "Delivery Note", "Sales Order", "Quotation",
-			"Purchase Invoice", "Purchase Receipt", "Purchase Order", "Supplier Quotation"]:
-				if frappe.db.sql("""select name from `tab%s` where company=%s and docstatus=1
-					limit 1""" % (doctype, "%s"), self.name):
-						exists = True
-						break
+		for doctype in [
+			"Sales Invoice",
+			"Delivery Note",
+			"Sales Order",
+			"Quotation",
+			"Purchase Invoice",
+			"Purchase Receipt",
+			"Purchase Order",
+			"Supplier Quotation",
+		]:
+			if frappe.db.sql(
+				"""select name from `tab%s` where company=%s and docstatus=1
+					limit 1"""
+				% (doctype, "%s"),
+				self.name,
+			):
+				exists = True
+				break
 
 		return exists
 
@@ -53,7 +65,7 @@
 
 	def validate_abbr(self):
 		if not self.abbr:
-			self.abbr = ''.join(c[0] for c in self.company_name.split()).upper()
+			self.abbr = "".join(c[0] for c in self.company_name.split()).upper()
 
 		self.abbr = self.abbr.strip()
 
@@ -63,7 +75,9 @@
 		if not self.abbr.strip():
 			frappe.throw(_("Abbreviation is mandatory"))
 
-		if frappe.db.sql("select abbr from tabCompany where name!=%s and abbr=%s", (self.name, self.abbr)):
+		if frappe.db.sql(
+			"select abbr from tabCompany where name!=%s and abbr=%s", (self.name, self.abbr)
+		):
 			frappe.throw(_("Abbreviation already used for another company"))
 
 	@frappe.whitelist()
@@ -72,37 +86,57 @@
 
 	def validate_default_accounts(self):
 		accounts = [
-			["Default Bank Account", "default_bank_account"], ["Default Cash Account", "default_cash_account"],
-			["Default Receivable Account", "default_receivable_account"], ["Default Payable Account", "default_payable_account"],
-			["Default Expense Account", "default_expense_account"], ["Default Income Account", "default_income_account"],
-			["Stock Received But Not Billed Account", "stock_received_but_not_billed"], ["Stock Adjustment Account", "stock_adjustment_account"],
-			["Expense Included In Valuation Account", "expenses_included_in_valuation"], ["Default Payroll Payable Account", "default_payroll_payable_account"]
+			["Default Bank Account", "default_bank_account"],
+			["Default Cash Account", "default_cash_account"],
+			["Default Receivable Account", "default_receivable_account"],
+			["Default Payable Account", "default_payable_account"],
+			["Default Expense Account", "default_expense_account"],
+			["Default Income Account", "default_income_account"],
+			["Stock Received But Not Billed Account", "stock_received_but_not_billed"],
+			["Stock Adjustment Account", "stock_adjustment_account"],
+			["Expense Included In Valuation Account", "expenses_included_in_valuation"],
+			["Default Payroll Payable Account", "default_payroll_payable_account"],
 		]
 
 		for account in accounts:
 			if self.get(account[1]):
 				for_company = frappe.db.get_value("Account", self.get(account[1]), "company")
 				if for_company != self.name:
-					frappe.throw(_("Account {0} does not belong to company: {1}").format(self.get(account[1]), self.name))
+					frappe.throw(
+						_("Account {0} does not belong to company: {1}").format(self.get(account[1]), self.name)
+					)
 
 				if get_account_currency(self.get(account[1])) != self.default_currency:
-					error_message = _("{0} currency must be same as company's default currency. Please select another account.") \
-						.format(frappe.bold(account[0]))
+					error_message = _(
+						"{0} currency must be same as company's default currency. Please select another account."
+					).format(frappe.bold(account[0]))
 					frappe.throw(error_message)
 
 	def validate_currency(self):
 		if self.is_new():
 			return
-		self.previous_default_currency = frappe.get_cached_value('Company',  self.name,  "default_currency")
-		if self.default_currency and self.previous_default_currency and \
-			self.default_currency != self.previous_default_currency and \
-			self.check_if_transactions_exist():
-				frappe.throw(_("Cannot change company's default currency, because there are existing transactions. Transactions must be cancelled to change the default currency."))
+		self.previous_default_currency = frappe.get_cached_value(
+			"Company", self.name, "default_currency"
+		)
+		if (
+			self.default_currency
+			and self.previous_default_currency
+			and self.default_currency != self.previous_default_currency
+			and self.check_if_transactions_exist()
+		):
+			frappe.throw(
+				_(
+					"Cannot change company's default currency, because there are existing transactions. Transactions must be cancelled to change the default currency."
+				)
+			)
 
 	def on_update(self):
 		NestedSet.on_update(self)
-		if not frappe.db.sql("""select name from tabAccount
-				where company=%s and docstatus<2 limit 1""", self.name):
+		if not frappe.db.sql(
+			"""select name from tabAccount
+				where company=%s and docstatus<2 limit 1""",
+			self.name,
+		):
 			if not frappe.local.flags.ignore_chart_of_accounts:
 				frappe.flags.country_change = True
 				self.create_default_accounts()
@@ -117,7 +151,8 @@
 
 		if not frappe.db.get_value("Department", {"company": self.name}):
 			from erpnext.setup.setup_wizard.operations.install_fixtures import install_post_company_fixtures
-			install_post_company_fixtures(frappe._dict({'company_name': self.name}))
+
+			install_post_company_fixtures(frappe._dict({"company_name": self.name}))
 
 		if not frappe.local.flags.ignore_chart_of_accounts:
 			self.set_default_accounts()
@@ -127,12 +162,15 @@
 		if self.default_currency:
 			frappe.db.set_value("Currency", self.default_currency, "enabled", 1)
 
-		if hasattr(frappe.local, 'enable_perpetual_inventory') and \
-			self.name in frappe.local.enable_perpetual_inventory:
+		if (
+			hasattr(frappe.local, "enable_perpetual_inventory")
+			and self.name in frappe.local.enable_perpetual_inventory
+		):
 			frappe.local.enable_perpetual_inventory[self.name] = self.enable_perpetual_inventory
 
 		if frappe.flags.parent_company_changed:
 			from frappe.utils.nestedset import rebuild_tree
+
 			rebuild_tree("Company", "parent_company")
 
 		frappe.clear_cache()
@@ -143,31 +181,48 @@
 			{"warehouse_name": _("Stores"), "is_group": 0},
 			{"warehouse_name": _("Work In Progress"), "is_group": 0},
 			{"warehouse_name": _("Finished Goods"), "is_group": 0},
-			{"warehouse_name": _("Goods In Transit"), "is_group": 0, "warehouse_type": "Transit"}]:
+			{"warehouse_name": _("Goods In Transit"), "is_group": 0, "warehouse_type": "Transit"},
+		]:
 
-			if not frappe.db.exists("Warehouse", "{0} - {1}".format(wh_detail["warehouse_name"], self.abbr)):
-				warehouse = frappe.get_doc({
-					"doctype":"Warehouse",
-					"warehouse_name": wh_detail["warehouse_name"],
-					"is_group": wh_detail["is_group"],
-					"company": self.name,
-					"parent_warehouse": "{0} - {1}".format(_("All Warehouses"), self.abbr) \
-						if not wh_detail["is_group"] else "",
-					"warehouse_type" : wh_detail["warehouse_type"] if "warehouse_type" in wh_detail else None
-				})
+			if not frappe.db.exists(
+				"Warehouse", "{0} - {1}".format(wh_detail["warehouse_name"], self.abbr)
+			):
+				warehouse = frappe.get_doc(
+					{
+						"doctype": "Warehouse",
+						"warehouse_name": wh_detail["warehouse_name"],
+						"is_group": wh_detail["is_group"],
+						"company": self.name,
+						"parent_warehouse": "{0} - {1}".format(_("All Warehouses"), self.abbr)
+						if not wh_detail["is_group"]
+						else "",
+						"warehouse_type": wh_detail["warehouse_type"] if "warehouse_type" in wh_detail else None,
+					}
+				)
 				warehouse.flags.ignore_permissions = True
 				warehouse.flags.ignore_mandatory = True
 				warehouse.insert()
 
 	def create_default_accounts(self):
 		from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts
+
 		frappe.local.flags.ignore_root_company_validation = True
 		create_charts(self.name, self.chart_of_accounts, self.existing_company)
 
-		frappe.db.set(self, "default_receivable_account", frappe.db.get_value("Account",
-			{"company": self.name, "account_type": "Receivable", "is_group": 0}))
-		frappe.db.set(self, "default_payable_account", frappe.db.get_value("Account",
-			{"company": self.name, "account_type": "Payable", "is_group": 0}))
+		frappe.db.set(
+			self,
+			"default_receivable_account",
+			frappe.db.get_value(
+				"Account", {"company": self.name, "account_type": "Receivable", "is_group": 0}
+			),
+		)
+		frappe.db.set(
+			self,
+			"default_payable_account",
+			frappe.db.get_value(
+				"Account", {"company": self.name, "account_type": "Payable", "is_group": 0}
+			),
+		)
 
 	def validate_coa_input(self):
 		if self.create_chart_of_accounts_based_on == "Existing Company":
@@ -184,34 +239,46 @@
 	def validate_perpetual_inventory(self):
 		if not self.get("__islocal"):
 			if cint(self.enable_perpetual_inventory) == 1 and not self.default_inventory_account:
-				frappe.msgprint(_("Set default inventory account for perpetual inventory"),
-					alert=True, indicator='orange')
+				frappe.msgprint(
+					_("Set default inventory account for perpetual inventory"), alert=True, indicator="orange"
+				)
 
 	def validate_provisional_account_for_non_stock_items(self):
 		if not self.get("__islocal"):
-			if cint(self.enable_provisional_accounting_for_non_stock_items) == 1 and not self.default_provisional_account:
-				frappe.throw(_("Set default {0} account for non stock items").format(
-					frappe.bold('Provisional Account')))
+			if (
+				cint(self.enable_provisional_accounting_for_non_stock_items) == 1
+				and not self.default_provisional_account
+			):
+				frappe.throw(
+					_("Set default {0} account for non stock items").format(frappe.bold("Provisional Account"))
+				)
 
-			make_property_setter("Purchase Receipt", "provisional_expense_account", "hidden",
-				not self.enable_provisional_accounting_for_non_stock_items, "Check", validate_fields_for_doctype=False)
+			make_property_setter(
+				"Purchase Receipt",
+				"provisional_expense_account",
+				"hidden",
+				not self.enable_provisional_accounting_for_non_stock_items,
+				"Check",
+				validate_fields_for_doctype=False,
+			)
 
 	def check_country_change(self):
 		frappe.flags.country_change = False
 
-		if not self.is_new() and \
-			self.country != frappe.get_cached_value('Company',  self.name,  'country'):
+		if not self.is_new() and self.country != frappe.get_cached_value(
+			"Company", self.name, "country"
+		):
 			frappe.flags.country_change = True
 
 	def set_chart_of_accounts(self):
-		''' If parent company is set, chart of accounts will be based on that company '''
+		"""If parent company is set, chart of accounts will be based on that company"""
 		if self.parent_company:
 			self.create_chart_of_accounts_based_on = "Existing Company"
 			self.existing_company = self.parent_company
 
 	def validate_parent_company(self):
 		if self.parent_company:
-			is_group = frappe.get_value('Company', self.parent_company, 'is_group')
+			is_group = frappe.get_value("Company", self.parent_company, "is_group")
 
 			if not is_group:
 				frappe.throw(_("Parent Company must be a group company"))
@@ -225,29 +292,33 @@
 			"depreciation_expense_account": "Depreciation",
 			"capital_work_in_progress_account": "Capital Work in Progress",
 			"asset_received_but_not_billed": "Asset Received But Not Billed",
-			"expenses_included_in_asset_valuation": "Expenses Included In Asset Valuation"
+			"expenses_included_in_asset_valuation": "Expenses Included In Asset Valuation",
 		}
 
 		if self.enable_perpetual_inventory:
-			default_accounts.update({
-				"stock_received_but_not_billed": "Stock Received But Not Billed",
-				"default_inventory_account": "Stock",
-				"stock_adjustment_account": "Stock Adjustment",
-				"expenses_included_in_valuation": "Expenses Included In Valuation",
-				"default_expense_account": "Cost of Goods Sold"
-			})
+			default_accounts.update(
+				{
+					"stock_received_but_not_billed": "Stock Received But Not Billed",
+					"default_inventory_account": "Stock",
+					"stock_adjustment_account": "Stock Adjustment",
+					"expenses_included_in_valuation": "Expenses Included In Valuation",
+					"default_expense_account": "Cost of Goods Sold",
+				}
+			)
 
 		if self.update_default_account:
 			for default_account in default_accounts:
 				self._set_default_account(default_account, default_accounts.get(default_account))
 
 		if not self.default_income_account:
-			income_account = frappe.db.get_value("Account",
-				{"account_name": _("Sales"), "company": self.name, "is_group": 0})
+			income_account = frappe.db.get_value(
+				"Account", {"account_name": _("Sales"), "company": self.name, "is_group": 0}
+			)
 
 			if not income_account:
-				income_account = frappe.db.get_value("Account",
-					{"account_name": _("Sales Account"), "company": self.name})
+				income_account = frappe.db.get_value(
+					"Account", {"account_name": _("Sales Account"), "company": self.name}
+				)
 
 			self.db_set("default_income_account", income_account)
 
@@ -255,32 +326,38 @@
 			self.db_set("default_payable_account", self.default_payable_account)
 
 		if not self.default_payroll_payable_account:
-			payroll_payable_account = frappe.db.get_value("Account",
-				{"account_name": _("Payroll Payable"), "company": self.name, "is_group": 0})
+			payroll_payable_account = frappe.db.get_value(
+				"Account", {"account_name": _("Payroll Payable"), "company": self.name, "is_group": 0}
+			)
 
 			self.db_set("default_payroll_payable_account", payroll_payable_account)
 
 		if not self.default_employee_advance_account:
-			employe_advance_account = frappe.db.get_value("Account",
-				{"account_name": _("Employee Advances"), "company": self.name, "is_group": 0})
+			employe_advance_account = frappe.db.get_value(
+				"Account", {"account_name": _("Employee Advances"), "company": self.name, "is_group": 0}
+			)
 
 			self.db_set("default_employee_advance_account", employe_advance_account)
 
 		if not self.write_off_account:
-			write_off_acct = frappe.db.get_value("Account",
-				{"account_name": _("Write Off"), "company": self.name, "is_group": 0})
+			write_off_acct = frappe.db.get_value(
+				"Account", {"account_name": _("Write Off"), "company": self.name, "is_group": 0}
+			)
 
 			self.db_set("write_off_account", write_off_acct)
 
 		if not self.exchange_gain_loss_account:
-			exchange_gain_loss_acct = frappe.db.get_value("Account",
-				{"account_name": _("Exchange Gain/Loss"), "company": self.name, "is_group": 0})
+			exchange_gain_loss_acct = frappe.db.get_value(
+				"Account", {"account_name": _("Exchange Gain/Loss"), "company": self.name, "is_group": 0}
+			)
 
 			self.db_set("exchange_gain_loss_account", exchange_gain_loss_acct)
 
 		if not self.disposal_account:
-			disposal_acct = frappe.db.get_value("Account",
-				{"account_name": _("Gain/Loss on Asset Disposal"), "company": self.name, "is_group": 0})
+			disposal_acct = frappe.db.get_value(
+				"Account",
+				{"account_name": _("Gain/Loss on Asset Disposal"), "company": self.name, "is_group": 0},
+			)
 
 			self.db_set("disposal_account", disposal_acct)
 
@@ -288,35 +365,39 @@
 		if self.get(fieldname):
 			return
 
-		account = frappe.db.get_value("Account", {"account_type": account_type, "is_group": 0, "company": self.name})
+		account = frappe.db.get_value(
+			"Account", {"account_type": account_type, "is_group": 0, "company": self.name}
+		)
 
 		if account:
 			self.db_set(fieldname, account)
 
 	def set_mode_of_payment_account(self):
-		cash = frappe.db.get_value('Mode of Payment', {'type': 'Cash'}, 'name')
-		if cash and self.default_cash_account \
-			and not frappe.db.get_value('Mode of Payment Account', {'company': self.name, 'parent': cash}):
-			mode_of_payment = frappe.get_doc('Mode of Payment', cash, for_update=True)
-			mode_of_payment.append('accounts', {
-				'company': self.name,
-				'default_account': self.default_cash_account
-			})
+		cash = frappe.db.get_value("Mode of Payment", {"type": "Cash"}, "name")
+		if (
+			cash
+			and self.default_cash_account
+			and not frappe.db.get_value("Mode of Payment Account", {"company": self.name, "parent": cash})
+		):
+			mode_of_payment = frappe.get_doc("Mode of Payment", cash, for_update=True)
+			mode_of_payment.append(
+				"accounts", {"company": self.name, "default_account": self.default_cash_account}
+			)
 			mode_of_payment.save(ignore_permissions=True)
 
 	def create_default_cost_center(self):
 		cc_list = [
 			{
-				'cost_center_name': self.name,
-				'company':self.name,
-				'is_group': 1,
-				'parent_cost_center':None
+				"cost_center_name": self.name,
+				"company": self.name,
+				"is_group": 1,
+				"parent_cost_center": None,
 			},
 			{
-				'cost_center_name':_('Main'),
-				'company':self.name,
-				'is_group':0,
-				'parent_cost_center':self.name + ' - ' + self.abbr
+				"cost_center_name": _("Main"),
+				"company": self.name,
+				"is_group": 0,
+				"parent_cost_center": self.name + " - " + self.abbr,
 			},
 		]
 		for cc in cc_list:
@@ -335,26 +416,32 @@
 	def after_rename(self, olddn, newdn, merge=False):
 		frappe.db.set(self, "company_name", newdn)
 
-		frappe.db.sql("""update `tabDefaultValue` set defvalue=%s
-			where defkey='Company' and defvalue=%s""", (newdn, olddn))
+		frappe.db.sql(
+			"""update `tabDefaultValue` set defvalue=%s
+			where defkey='Company' and defvalue=%s""",
+			(newdn, olddn),
+		)
 
 		clear_defaults_cache()
 
 	def abbreviate(self):
-		self.abbr = ''.join(c[0].upper() for c in self.company_name.split())
+		self.abbr = "".join(c[0].upper() for c in self.company_name.split())
 
 	def on_trash(self):
 		"""
-			Trash accounts and cost centers for this company if no gl entry exists
+		Trash accounts and cost centers for this company if no gl entry exists
 		"""
 		NestedSet.validate_if_child_exists(self)
 		frappe.utils.nestedset.update_nsm(self)
 
 		rec = frappe.db.sql("SELECT name from `tabGL Entry` where company = %s", self.name)
 		if not rec:
-			frappe.db.sql("""delete from `tabBudget Account`
+			frappe.db.sql(
+				"""delete from `tabBudget Account`
 				where exists(select name from tabBudget
-					where name=`tabBudget Account`.parent and company = %s)""", self.name)
+					where name=`tabBudget Account`.parent and company = %s)""",
+				self.name,
+			)
 
 			for doctype in ["Account", "Cost Center", "Budget", "Party Account"]:
 				frappe.db.sql("delete from `tab{0}` where company = %s".format(doctype), self.name)
@@ -369,26 +456,37 @@
 		# clear default accounts, warehouses from item
 		warehouses = frappe.db.sql_list("select name from tabWarehouse where company=%s", self.name)
 		if warehouses:
-			frappe.db.sql("""delete from `tabItem Reorder` where warehouse in (%s)"""
-				% ', '.join(['%s']*len(warehouses)), tuple(warehouses))
+			frappe.db.sql(
+				"""delete from `tabItem Reorder` where warehouse in (%s)"""
+				% ", ".join(["%s"] * len(warehouses)),
+				tuple(warehouses),
+			)
 
 		# reset default company
-		frappe.db.sql("""update `tabSingles` set value=""
+		frappe.db.sql(
+			"""update `tabSingles` set value=''
 			where doctype='Global Defaults' and field='default_company'
-			and value=%s""", self.name)
+			and value=%s""",
+			self.name,
+		)
 
 		# reset default company
-		frappe.db.sql("""update `tabSingles` set value=""
+		frappe.db.sql(
+			"""update `tabSingles` set value=''
 			where doctype='Chart of Accounts Importer' and field='company'
-			and value=%s""", self.name)
+			and value=%s""",
+			self.name,
+		)
 
 		# delete BOMs
 		boms = frappe.db.sql_list("select name from tabBOM where company=%s", self.name)
 		if boms:
 			frappe.db.sql("delete from tabBOM where company=%s", self.name)
 			for dt in ("BOM Operation", "BOM Item", "BOM Scrap Item", "BOM Explosion Item"):
-				frappe.db.sql("delete from `tab%s` where parent in (%s)"""
-					% (dt, ', '.join(['%s']*len(boms))), tuple(boms))
+				frappe.db.sql(
+					"delete from `tab%s` where parent in (%s)" "" % (dt, ", ".join(["%s"] * len(boms))),
+					tuple(boms),
+				)
 
 		frappe.db.sql("delete from tabEmployee where company=%s", self.name)
 		frappe.db.sql("delete from tabDepartment where company=%s", self.name)
@@ -401,18 +499,20 @@
 		frappe.db.sql("delete from `tabItem Tax Template` where company=%s", self.name)
 
 		# delete Process Deferred Accounts if no GL Entry found
-		if not frappe.db.get_value('GL Entry', {'company': self.name}):
+		if not frappe.db.get_value("GL Entry", {"company": self.name}):
 			frappe.db.sql("delete from `tabProcess Deferred Accounting` where company=%s", self.name)
 
 	def check_parent_changed(self):
 		frappe.flags.parent_company_changed = False
 
-		if not self.is_new() and \
-			self.parent_company != frappe.db.get_value("Company",  self.name,  "parent_company"):
+		if not self.is_new() and self.parent_company != frappe.db.get_value(
+			"Company", self.name, "parent_company"
+		):
 			frappe.flags.parent_company_changed = True
 
+
 def get_name_with_abbr(name, company):
-	company_abbr = frappe.get_cached_value('Company',  company,  "abbr")
+	company_abbr = frappe.get_cached_value("Company", company, "abbr")
 	parts = name.split(" - ")
 
 	if parts[-1].lower() != company_abbr.lower():
@@ -420,6 +520,7 @@
 
 	return " - ".join(parts)
 
+
 def install_country_fixtures(company, country):
 	try:
 		module_name = f"erpnext.regional.{frappe.scrub(country)}.setup.setup"
@@ -427,14 +528,19 @@
 	except ImportError:
 		pass
 	except Exception:
-		frappe.log_error()
-		frappe.throw(_("Failed to setup defaults for country {0}. Please contact support.").format(frappe.bold(country)))
+		frappe.log_error("Unable to set country fixtures")
+		frappe.throw(
+			_("Failed to setup defaults for country {0}. Please contact support.").format(
+				frappe.bold(country)
+			)
+		)
 
 
 def update_company_current_month_sales(company):
 	current_month_year = formatdate(today(), "MM-yyyy")
 
-	results = frappe.db.sql('''
+	results = frappe.db.sql(
+		"""
 		SELECT
 			SUM(base_grand_total) AS total,
 			DATE_FORMAT(`posting_date`, '%m-%Y') AS month_year
@@ -446,44 +552,58 @@
 			AND company = {company}
 		GROUP BY
 			month_year
-	'''.format(current_month_year=current_month_year, company=frappe.db.escape(company)),
-		as_dict = True)
+	""".format(
+			current_month_year=current_month_year, company=frappe.db.escape(company)
+		),
+		as_dict=True,
+	)
 
-	monthly_total = results[0]['total'] if len(results) > 0 else 0
+	monthly_total = results[0]["total"] if len(results) > 0 else 0
 
 	frappe.db.set_value("Company", company, "total_monthly_sales", monthly_total)
 
+
 def update_company_monthly_sales(company):
-	'''Cache past year monthly sales of every company based on sales invoices'''
+	"""Cache past year monthly sales of every company based on sales invoices"""
 	import json
 
 	from frappe.utils.goal import get_monthly_results
-	filter_str = "company = {0} and status != 'Draft' and docstatus=1".format(frappe.db.escape(company))
-	month_to_value_dict = get_monthly_results("Sales Invoice", "base_grand_total",
-		"posting_date", filter_str, "sum")
+
+	filter_str = "company = {0} and status != 'Draft' and docstatus=1".format(
+		frappe.db.escape(company)
+	)
+	month_to_value_dict = get_monthly_results(
+		"Sales Invoice", "base_grand_total", "posting_date", filter_str, "sum"
+	)
 
 	frappe.db.set_value("Company", company, "sales_monthly_history", json.dumps(month_to_value_dict))
 
+
 def update_transactions_annual_history(company, commit=False):
 	transactions_history = get_all_transactions_annual_history(company)
-	frappe.db.set_value("Company", company, "transactions_annual_history", json.dumps(transactions_history))
+	frappe.db.set_value(
+		"Company", company, "transactions_annual_history", json.dumps(transactions_history)
+	)
 
 	if commit:
 		frappe.db.commit()
 
+
 def cache_companies_monthly_sales_history():
-	companies = [d['name'] for d in frappe.get_list("Company")]
+	companies = [d["name"] for d in frappe.get_list("Company")]
 	for company in companies:
 		update_company_monthly_sales(company)
 		update_transactions_annual_history(company)
 	frappe.db.commit()
 
+
 @frappe.whitelist()
 def get_children(doctype, parent=None, company=None, is_root=False):
 	if parent == None or parent == "All Companies":
 		parent = ""
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select
 			name as value,
 			is_group as expandable
@@ -492,25 +612,30 @@
 		where
 			ifnull(parent_company, "")={parent}
 		""".format(
-			doctype = doctype,
-			parent=frappe.db.escape(parent)
-		), as_dict=1)
+			doctype=doctype, parent=frappe.db.escape(parent)
+		),
+		as_dict=1,
+	)
+
 
 @frappe.whitelist()
 def add_node():
 	from frappe.desk.treeview import make_tree_args
+
 	args = frappe.form_dict
 	args = make_tree_args(**args)
 
-	if args.parent_company == 'All Companies':
+	if args.parent_company == "All Companies":
 		args.parent_company = None
 
 	frappe.get_doc(args).insert()
 
+
 def get_all_transactions_annual_history(company):
 	out = {}
 
-	items = frappe.db.sql('''
+	items = frappe.db.sql(
+		"""
 		select transaction_date, count(*) as count
 
 		from (
@@ -550,46 +675,55 @@
 
 		group by
 			transaction_date
-			''', (company), as_dict=True)
+			""",
+		(company),
+		as_dict=True,
+	)
 
 	for d in items:
 		timestamp = get_timestamp(d["transaction_date"])
-		out.update({ timestamp: d["count"] })
+		out.update({timestamp: d["count"]})
 
 	return out
 
+
 def get_timeline_data(doctype, name):
-	'''returns timeline data based on linked records in dashboard'''
+	"""returns timeline data based on linked records in dashboard"""
 	out = {}
 	date_to_value_dict = {}
 
-	history = frappe.get_cached_value('Company',  name,  "transactions_annual_history")
+	history = frappe.get_cached_value("Company", name, "transactions_annual_history")
 
 	try:
-		date_to_value_dict = json.loads(history) if history and '{' in history else None
+		date_to_value_dict = json.loads(history) if history and "{" in history else None
 	except ValueError:
 		date_to_value_dict = None
 
 	if date_to_value_dict is None:
 		update_transactions_annual_history(name, True)
-		history = frappe.get_cached_value('Company',  name,  "transactions_annual_history")
-		return json.loads(history) if history and '{' in history else {}
+		history = frappe.get_cached_value("Company", name, "transactions_annual_history")
+		return json.loads(history) if history and "{" in history else {}
 
 	return date_to_value_dict
 
+
 @frappe.whitelist()
-def get_default_company_address(name, sort_key='is_primary_address', existing_address=None):
-	if sort_key not in ['is_shipping_address', 'is_primary_address']:
+def get_default_company_address(name, sort_key="is_primary_address", existing_address=None):
+	if sort_key not in ["is_shipping_address", "is_primary_address"]:
 		return None
 
-	out = frappe.db.sql(""" SELECT
+	out = frappe.db.sql(
+		""" SELECT
 			addr.name, addr.%s
 		FROM
 			`tabAddress` addr, `tabDynamic Link` dl
 		WHERE
 			dl.parent = addr.name and dl.link_doctype = 'Company' and
 			dl.link_name = %s and ifnull(addr.disabled, 0) = 0
-		""" %(sort_key, '%s'), (name)) #nosec
+		"""
+		% (sort_key, "%s"),
+		(name),
+	)  # nosec
 
 	if existing_address:
 		if existing_address in [d[0] for d in out]:
@@ -600,11 +734,9 @@
 	else:
 		return None
 
+
 @frappe.whitelist()
 def create_transaction_deletion_request(company):
-	tdr = frappe.get_doc({
-		'doctype': 'Transaction Deletion Record',
-		'company': company
-	})
+	tdr = frappe.get_doc({"doctype": "Transaction Deletion Record", "company": company})
 	tdr.insert()
 	tdr.submit()
diff --git a/erpnext/setup/doctype/company/company_dashboard.py b/erpnext/setup/doctype/company/company_dashboard.py
index 7cb0b12..2d073c1 100644
--- a/erpnext/setup/doctype/company/company_dashboard.py
+++ b/erpnext/setup/doctype/company/company_dashboard.py
@@ -3,38 +3,25 @@
 
 def get_data():
 	return {
-		'graph': True,
-		'graph_method': "frappe.utils.goal.get_monthly_goal_graph_data",
-		'graph_method_args': {
-			'title': _('Sales'),
-			'goal_value_field': 'monthly_sales_target',
-			'goal_total_field': 'total_monthly_sales',
-			'goal_history_field': 'sales_monthly_history',
-			'goal_doctype': 'Sales Invoice',
-			'goal_doctype_link': 'company',
-			'goal_field': 'base_grand_total',
-			'date_field': 'posting_date',
-			'filter_str': "docstatus = 1 and is_opening != 'Yes'",
-			'aggregation': 'sum'
+		"graph": True,
+		"graph_method": "frappe.utils.goal.get_monthly_goal_graph_data",
+		"graph_method_args": {
+			"title": _("Sales"),
+			"goal_value_field": "monthly_sales_target",
+			"goal_total_field": "total_monthly_sales",
+			"goal_history_field": "sales_monthly_history",
+			"goal_doctype": "Sales Invoice",
+			"goal_doctype_link": "company",
+			"goal_field": "base_grand_total",
+			"date_field": "posting_date",
+			"filters": {"docstatus": 1, "is_opening": ("!=", "Yes")},
+			"aggregation": "sum",
 		},
-
-		'fieldname': 'company',
-		'transactions': [
-			{
-				'label': _('Pre Sales'),
-				'items': ['Quotation']
-			},
-			{
-				'label': _('Orders'),
-				'items': ['Sales Order', 'Delivery Note', 'Sales Invoice']
-			},
-			{
-				'label': _('Support'),
-				'items': ['Issue']
-			},
-			{
-				'label': _('Projects'),
-				'items': ['Project']
-			}
-		]
+		"fieldname": "company",
+		"transactions": [
+			{"label": _("Pre Sales"), "items": ["Quotation"]},
+			{"label": _("Orders"), "items": ["Sales Order", "Delivery Note", "Sales Invoice"]},
+			{"label": _("Support"), "items": ["Issue"]},
+			{"label": _("Projects"), "items": ["Project"]},
+		],
 	}
diff --git a/erpnext/setup/doctype/company/test_company.py b/erpnext/setup/doctype/company/test_company.py
index e175c54..29e056e 100644
--- a/erpnext/setup/doctype/company/test_company.py
+++ b/erpnext/setup/doctype/company/test_company.py
@@ -14,7 +14,8 @@
 
 test_ignore = ["Account", "Cost Center", "Payment Terms Template", "Salary Component", "Warehouse"]
 test_dependencies = ["Fiscal Year"]
-test_records = frappe.get_test_records('Company')
+test_records = frappe.get_test_records("Company")
+
 
 class TestCompany(unittest.TestCase):
 	def test_coa_based_on_existing_company(self):
@@ -37,8 +38,8 @@
 				"account_type": "Cash",
 				"is_group": 0,
 				"root_type": "Asset",
-				"parent_account": "Cash In Hand - CFEC"
-			}
+				"parent_account": "Cash In Hand - CFEC",
+			},
 		}
 
 		for account, acc_property in expected_results.items():
@@ -69,15 +70,22 @@
 					company.chart_of_accounts = template
 					company.save()
 
-					account_types = ["Cost of Goods Sold", "Depreciation",
-						"Expenses Included In Valuation", "Fixed Asset", "Payable", "Receivable",
-						"Stock Adjustment", "Stock Received But Not Billed", "Bank", "Cash", "Stock"]
+					account_types = [
+						"Cost of Goods Sold",
+						"Depreciation",
+						"Expenses Included In Valuation",
+						"Fixed Asset",
+						"Payable",
+						"Receivable",
+						"Stock Adjustment",
+						"Stock Received But Not Billed",
+						"Bank",
+						"Cash",
+						"Stock",
+					]
 
 					for account_type in account_types:
-						filters = {
-							"company": template,
-							"account_type": account_type
-						}
+						filters = {"company": template, "account_type": account_type}
 						if account_type in ["Bank", "Cash"]:
 							filters["is_group"] = 1
 
@@ -90,8 +98,11 @@
 					frappe.delete_doc("Company", template)
 
 	def delete_mode_of_payment(self, company):
-		frappe.db.sql(""" delete from `tabMode of Payment Account`
-			where company =%s """, (company))
+		frappe.db.sql(
+			""" delete from `tabMode of Payment Account`
+			where company =%s """,
+			(company),
+		)
 
 	def test_basic_tree(self, records=None):
 		min_lft = 1
@@ -101,12 +112,12 @@
 			records = test_records[2:]
 
 		for company in records:
-			lft, rgt, parent_company = frappe.db.get_value("Company", company["company_name"],
-				["lft", "rgt", "parent_company"])
+			lft, rgt, parent_company = frappe.db.get_value(
+				"Company", company["company_name"], ["lft", "rgt", "parent_company"]
+			)
 
 			if parent_company:
-				parent_lft, parent_rgt = frappe.db.get_value("Company", parent_company,
-					["lft", "rgt"])
+				parent_lft, parent_rgt = frappe.db.get_value("Company", parent_company, ["lft", "rgt"])
 			else:
 				# root
 				parent_lft = min_lft - 1
@@ -125,8 +136,11 @@
 		def get_no_of_children(companies, no_of_children):
 			children = []
 			for company in companies:
-				children += frappe.db.sql_list("""select name from `tabCompany`
-				where ifnull(parent_company, '')=%s""", company or '')
+				children += frappe.db.sql_list(
+					"""select name from `tabCompany`
+				where ifnull(parent_company, '')=%s""",
+					company or "",
+				)
 
 			if len(children):
 				return get_no_of_children(children, no_of_children + len(children))
@@ -148,40 +162,45 @@
 		child_company.save()
 		self.test_basic_tree()
 
+
 def create_company_communication(doctype, docname):
-	comm = frappe.get_doc({
+	comm = frappe.get_doc(
+		{
 			"doctype": "Communication",
 			"communication_type": "Communication",
 			"content": "Deduplication of Links",
 			"communication_medium": "Email",
-			"reference_doctype":doctype,
-			"reference_name":docname
-		})
+			"reference_doctype": doctype,
+			"reference_name": docname,
+		}
+	)
 	comm.insert()
 
+
 def create_child_company():
 	child_company = frappe.db.exists("Company", "Test Company")
 	if not child_company:
-		child_company = frappe.get_doc({
-			"doctype":"Company",
-			"company_name":"Test Company",
-			"abbr":"test_company",
-			"default_currency":"INR"
-		})
+		child_company = frappe.get_doc(
+			{
+				"doctype": "Company",
+				"company_name": "Test Company",
+				"abbr": "test_company",
+				"default_currency": "INR",
+			}
+		)
 		child_company.insert()
 	else:
 		child_company = frappe.get_doc("Company", child_company)
 
 	return child_company.name
 
+
 def create_test_lead_in_company(company):
 	lead = frappe.db.exists("Lead", "Test Lead in new company")
 	if not lead:
-		lead = frappe.get_doc({
-			"doctype": "Lead",
-			"lead_name": "Test Lead in new company",
-			"scompany": company
-		})
+		lead = frappe.get_doc(
+			{"doctype": "Lead", "lead_name": "Test Lead in new company", "scompany": company}
+		)
 		lead.insert()
 	else:
 		lead = frappe.get_doc("Lead", lead)
diff --git a/erpnext/setup/doctype/company/test_records.json b/erpnext/setup/doctype/company/test_records.json
index 89be607..19b6ef2 100644
--- a/erpnext/setup/doctype/company/test_records.json
+++ b/erpnext/setup/doctype/company/test_records.json
@@ -8,7 +8,8 @@
 		"domain": "Manufacturing",
 		"chart_of_accounts": "Standard",
 		"default_holiday_list": "_Test Holiday List",
-		"enable_perpetual_inventory": 0
+		"enable_perpetual_inventory": 0,
+		"allow_account_creation_against_child_company": 1
 	},
 	{
 		"abbr": "_TC1",
diff --git a/erpnext/setup/doctype/currency_exchange/currency_exchange.py b/erpnext/setup/doctype/currency_exchange/currency_exchange.py
index 4191935..f9f3b3a 100644
--- a/erpnext/setup/doctype/currency_exchange/currency_exchange.py
+++ b/erpnext/setup/doctype/currency_exchange/currency_exchange.py
@@ -17,13 +17,17 @@
 
 		# If both selling and buying enabled
 		purpose = "Selling-Buying"
-		if cint(self.for_buying)==0 and cint(self.for_selling)==1:
+		if cint(self.for_buying) == 0 and cint(self.for_selling) == 1:
 			purpose = "Selling"
-		if cint(self.for_buying)==1 and cint(self.for_selling)==0:
+		if cint(self.for_buying) == 1 and cint(self.for_selling) == 0:
 			purpose = "Buying"
 
-		self.name = '{0}-{1}-{2}{3}'.format(formatdate(get_datetime_str(self.date), "yyyy-MM-dd"),
-			self.from_currency, self.to_currency, ("-" + purpose) if purpose else "")
+		self.name = "{0}-{1}-{2}{3}".format(
+			formatdate(get_datetime_str(self.date), "yyyy-MM-dd"),
+			self.from_currency,
+			self.to_currency,
+			("-" + purpose) if purpose else "",
+		)
 
 	def validate(self):
 		self.validate_value("exchange_rate", ">", 0)
diff --git a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py
index 06a79b4..e3d281a 100644
--- a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py
+++ b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py
@@ -9,20 +9,27 @@
 
 from erpnext.setup.utils import get_exchange_rate
 
-test_records = frappe.get_test_records('Currency Exchange')
+test_records = frappe.get_test_records("Currency Exchange")
+
 
 def save_new_records(test_records):
 	for record in test_records:
 		# If both selling and buying enabled
 		purpose = "Selling-Buying"
 
-		if cint(record.get("for_buying"))==0 and cint(record.get("for_selling"))==1:
+		if cint(record.get("for_buying")) == 0 and cint(record.get("for_selling")) == 1:
 			purpose = "Selling"
-		if cint(record.get("for_buying"))==1 and cint(record.get("for_selling"))==0:
+		if cint(record.get("for_buying")) == 1 and cint(record.get("for_selling")) == 0:
 			purpose = "Buying"
 		kwargs = dict(
 			doctype=record.get("doctype"),
-			docname=record.get("date") + '-' + record.get("from_currency") + '-' + record.get("to_currency") + '-' + purpose,
+			docname=record.get("date")
+			+ "-"
+			+ record.get("from_currency")
+			+ "-"
+			+ record.get("to_currency")
+			+ "-"
+			+ purpose,
 			fieldname="exchange_rate",
 			value=record.get("exchange_rate"),
 		)
@@ -39,10 +46,8 @@
 			curr_exchange.for_selling = record["for_selling"]
 			curr_exchange.insert()
 
-test_exchange_values = {
-	'2015-12-15': '66.999',
-	'2016-01-15': '65.1'
-}
+
+test_exchange_values = {"2015-12-15": "66.999", "2016-01-15": "65.1"}
 
 # Removing API call from get_exchange_rate
 def patched_requests_get(*args, **kwargs):
@@ -58,19 +63,22 @@
 		def json(self):
 			return self.json_data
 
-	if args[0] == "https://api.exchangerate.host/convert" and kwargs.get('params'):
-		if kwargs['params'].get('date') and kwargs['params'].get('from') and kwargs['params'].get('to'):
-			if test_exchange_values.get(kwargs['params']['date']):
-				return PatchResponse({'result': test_exchange_values[kwargs['params']['date']]}, 200)
-	elif args[0].startswith("https://frankfurter.app") and kwargs.get('params'):
-		if kwargs['params'].get('base') and kwargs['params'].get('symbols'):
+	if args[0] == "https://api.exchangerate.host/convert" and kwargs.get("params"):
+		if kwargs["params"].get("date") and kwargs["params"].get("from") and kwargs["params"].get("to"):
+			if test_exchange_values.get(kwargs["params"]["date"]):
+				return PatchResponse({"result": test_exchange_values[kwargs["params"]["date"]]}, 200)
+	elif args[0].startswith("https://frankfurter.app") and kwargs.get("params"):
+		if kwargs["params"].get("base") and kwargs["params"].get("symbols"):
 			date = args[0].replace("https://frankfurter.app/", "")
 			if test_exchange_values.get(date):
-				return PatchResponse({'rates': {kwargs['params'].get('symbols'): test_exchange_values.get(date)}}, 200)
+				return PatchResponse(
+					{"rates": {kwargs["params"].get("symbols"): test_exchange_values.get(date)}}, 200
+				)
 
-	return PatchResponse({'rates': None}, 404)
+	return PatchResponse({"rates": None}, 404)
 
-@mock.patch('requests.get', side_effect=patched_requests_get)
+
+@mock.patch("requests.get", side_effect=patched_requests_get)
 class TestCurrencyExchange(unittest.TestCase):
 	def clear_cache(self):
 		cache = frappe.cache()
@@ -112,7 +120,7 @@
 
 		# Update Currency Exchange Rate
 		settings = frappe.get_single("Currency Exchange Settings")
-		settings.service_provider = 'exchangerate.host'
+		settings.service_provider = "exchangerate.host"
 		settings.save()
 
 		# Update exchange
@@ -139,7 +147,7 @@
 		self.assertEqual(flt(exchange_rate, 3), 65.1)
 
 		settings = frappe.get_single("Currency Exchange Settings")
-		settings.service_provider = 'frankfurter.app'
+		settings.service_provider = "frankfurter.app"
 		settings.save()
 
 	def test_exchange_rate_strict(self, mock_get):
diff --git a/erpnext/setup/doctype/customer_group/customer_group.py b/erpnext/setup/doctype/customer_group/customer_group.py
index 5b91726..246cc19 100644
--- a/erpnext/setup/doctype/customer_group/customer_group.py
+++ b/erpnext/setup/doctype/customer_group/customer_group.py
@@ -8,7 +8,8 @@
 
 
 class CustomerGroup(NestedSet):
-	nsm_parent_field = 'parent_customer_group'
+	nsm_parent_field = "parent_customer_group"
+
 	def validate(self):
 		if not self.parent_customer_group:
 			self.parent_customer_group = get_root_of("Customer Group")
@@ -22,12 +23,18 @@
 		if frappe.db.exists("Customer", self.name):
 			frappe.msgprint(_("A customer with the same name already exists"), raise_exception=1)
 
-def get_parent_customer_groups(customer_group):
-	lft, rgt = frappe.db.get_value("Customer Group", customer_group, ['lft', 'rgt'])
 
-	return frappe.db.sql("""select name from `tabCustomer Group`
+def get_parent_customer_groups(customer_group):
+	lft, rgt = frappe.db.get_value("Customer Group", customer_group, ["lft", "rgt"])
+
+	return frappe.db.sql(
+		"""select name from `tabCustomer Group`
 		where lft <= %s and rgt >= %s
-		order by lft asc""", (lft, rgt), as_dict=True)
+		order by lft asc""",
+		(lft, rgt),
+		as_dict=True,
+	)
+
 
 def on_doctype_update():
 	frappe.db.add_index("Customer Group", ["lft", "rgt"])
diff --git a/erpnext/setup/doctype/customer_group/test_customer_group.py b/erpnext/setup/doctype/customer_group/test_customer_group.py
index f02ae09..8876270 100644
--- a/erpnext/setup/doctype/customer_group/test_customer_group.py
+++ b/erpnext/setup/doctype/customer_group/test_customer_group.py
@@ -4,7 +4,6 @@
 test_ignore = ["Price List"]
 
 
-
 import frappe
 
-test_records = frappe.get_test_records('Customer Group')
+test_records = frappe.get_test_records("Customer Group")
diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py
index 02f9156..4fc20e6 100644
--- a/erpnext/setup/doctype/email_digest/email_digest.py
+++ b/erpnext/setup/doctype/email_digest/email_digest.py
@@ -36,16 +36,22 @@
 		self.from_date, self.to_date = self.get_from_to_date()
 		self.set_dates()
 		self._accounts = {}
-		self.currency = frappe.db.get_value('Company',  self.company,  "default_currency")
+		self.currency = frappe.db.get_value("Company", self.company, "default_currency")
 
 	@frappe.whitelist()
 	def get_users(self):
 		"""get list of users"""
-		user_list = frappe.db.sql("""
+		user_list = frappe.db.sql(
+			"""
 			select name, enabled from tabUser
 			where name not in ({})
 			and user_type != "Website User"
-			order by enabled desc, name asc""".format(", ".join(["%s"]*len(STANDARD_USERS))), STANDARD_USERS, as_dict=1)
+			order by enabled desc, name asc""".format(
+				", ".join(["%s"] * len(STANDARD_USERS))
+			),
+			STANDARD_USERS,
+			as_dict=1,
+		)
 
 		if self.recipient_list:
 			recipient_list = self.recipient_list.split("\n")
@@ -54,13 +60,18 @@
 		for p in user_list:
 			p["checked"] = p["name"] in recipient_list and 1 or 0
 
-		frappe.response['user_list'] = user_list
+		frappe.response["user_list"] = user_list
 
 	@frappe.whitelist()
 	def send(self):
 		# send email only to enabled users
-		valid_users = [p[0] for p in frappe.db.sql("""select name from `tabUser`
-			where enabled=1""")]
+		valid_users = [
+			p[0]
+			for p in frappe.db.sql(
+				"""select name from `tabUser`
+			where enabled=1"""
+			)
+		]
 
 		if self.recipients:
 			for row in self.recipients:
@@ -70,9 +81,10 @@
 						recipients=row.recipient,
 						subject=_("{0} Digest").format(self.frequency),
 						message=msg_for_this_recipient,
-						reference_doctype = self.doctype,
-						reference_name = self.name,
-						unsubscribe_message = _("Unsubscribe from this Email Digest"))
+						reference_doctype=self.doctype,
+						reference_name=self.name,
+						unsubscribe_message=_("Unsubscribe from this Email Digest"),
+					)
 
 	def get_msg_html(self):
 		"""Build email digest content"""
@@ -104,7 +116,10 @@
 		context.quote = {"text": quote[0], "author": quote[1]}
 
 		if self.get("purchase_orders_items_overdue"):
-			context.purchase_order_list, context.purchase_orders_items_overdue_list = self.get_purchase_orders_items_overdue_list()
+			(
+				context.purchase_order_list,
+				context.purchase_orders_items_overdue_list,
+			) = self.get_purchase_orders_items_overdue_list()
 			if not context.purchase_order_list:
 				frappe.throw(_("No items to be received are overdue"))
 
@@ -114,49 +129,54 @@
 		frappe.flags.ignore_account_permission = False
 
 		# style
-		return frappe.render_template("erpnext/setup/doctype/email_digest/templates/default.html",
-			context, is_path=True)
+		return frappe.render_template(
+			"erpnext/setup/doctype/email_digest/templates/default.html", context, is_path=True
+		)
 
 	def set_title(self, context):
 		"""Set digest title"""
-		if self.frequency=="Daily":
+		if self.frequency == "Daily":
 			context.title = _("Daily Reminders")
 			context.subtitle = _("Pending activities for today")
-		elif self.frequency=="Weekly":
+		elif self.frequency == "Weekly":
 			context.title = _("This Week's Summary")
 			context.subtitle = _("Summary for this week and pending activities")
-		elif self.frequency=="Monthly":
+		elif self.frequency == "Monthly":
 			context.title = _("This Month's Summary")
 			context.subtitle = _("Summary for this month and pending activities")
 
 	def set_style(self, context):
 		"""Set standard digest style"""
-		context.text_muted = '#8D99A6'
-		context.text_color = '#36414C'
-		context.h1 = 'margin-bottom: 30px; margin-top: 40px; font-weight: 400; font-size: 30px;'
-		context.h2 = 'margin-bottom: 30px; margin-top: -20px; font-weight: 400; font-size: 20px;'
-		context.label_css = '''display: inline-block; color: {text_muted};
-			padding: 3px 7px; margin-right: 7px;'''.format(text_muted = context.text_muted)
-		context.section_head = 'margin-top: 60px; font-size: 16px;'
-		context.line_item  = 'padding: 5px 0px; margin: 0; border-bottom: 1px solid #d1d8dd;'
-		context.link_css = 'color: {text_color}; text-decoration: none;'.format(text_color = context.text_color)
-
+		context.text_muted = "#8D99A6"
+		context.text_color = "#36414C"
+		context.h1 = "margin-bottom: 30px; margin-top: 40px; font-weight: 400; font-size: 30px;"
+		context.h2 = "margin-bottom: 30px; margin-top: -20px; font-weight: 400; font-size: 20px;"
+		context.label_css = """display: inline-block; color: {text_muted};
+			padding: 3px 7px; margin-right: 7px;""".format(
+			text_muted=context.text_muted
+		)
+		context.section_head = "margin-top: 60px; font-size: 16px;"
+		context.line_item = "padding: 5px 0px; margin: 0; border-bottom: 1px solid #d1d8dd;"
+		context.link_css = "color: {text_color}; text-decoration: none;".format(
+			text_color=context.text_color
+		)
 
 	def get_notifications(self):
 		"""Get notifications for user"""
 		notifications = frappe.desk.notifications.get_notifications()
 
-		notifications = sorted(notifications.get("open_count_doctype", {}).items(),
-			key=lambda a: a[1])
+		notifications = sorted(notifications.get("open_count_doctype", {}).items(), key=lambda a: a[1])
 
-		notifications = [{"key": n[0], "value": n[1],
-			"link": get_url_to_list(n[0])} for n in notifications if n[1]]
+		notifications = [
+			{"key": n[0], "value": n[1], "link": get_url_to_list(n[0])} for n in notifications if n[1]
+		]
 
 		return notifications
 
 	def get_calendar_events(self):
 		"""Get calendar events for given user"""
 		from frappe.desk.doctype.event.event import get_events
+
 		from_date, to_date = get_future_date_for_calendaer_event(self.frequency)
 
 		events = get_events(from_date, to_date)
@@ -176,10 +196,13 @@
 		if not user_id:
 			user_id = frappe.session.user
 
-		todo_list = frappe.db.sql("""select *
-			from `tabToDo` where (owner=%s or assigned_by=%s) and status="Open"
+		todo_list = frappe.db.sql(
+			"""select *
+			from `tabToDo` where (owner=%s or assigned_by=%s) and status='Open'
 			order by field(priority, 'High', 'Medium', 'Low') asc, date asc limit 20""",
-			(user_id, user_id), as_dict=True)
+			(user_id, user_id),
+			as_dict=True,
+		)
 
 		for t in todo_list:
 			t.link = get_url_to_form("ToDo", t.name)
@@ -191,9 +214,11 @@
 		if not user_id:
 			user_id = frappe.session.user
 
-		return frappe.db.sql("""select count(*) from `tabToDo`
+		return frappe.db.sql(
+			"""select count(*) from `tabToDo`
 			where status='Open' and (owner=%s or assigned_by=%s)""",
-			(user_id, user_id))[0][0]
+			(user_id, user_id),
+		)[0][0]
 
 	def get_issue_list(self, user_id=None):
 		"""Get issue list"""
@@ -205,9 +230,12 @@
 		if not role_permissions.get("read"):
 			return None
 
-		issue_list = frappe.db.sql("""select *
+		issue_list = frappe.db.sql(
+			"""select *
 			from `tabIssue` where status in ("Replied","Open")
-			order by modified asc limit 10""", as_dict=True)
+			order by modified asc limit 10""",
+			as_dict=True,
+		)
 
 		for t in issue_list:
 			t.link = get_url_to_form("Issue", t.name)
@@ -216,17 +244,22 @@
 
 	def get_issue_count(self):
 		"""Get count of Issue"""
-		return frappe.db.sql("""select count(*) from `tabIssue`
-			where status in ('Open','Replied') """)[0][0]
+		return frappe.db.sql(
+			"""select count(*) from `tabIssue`
+			where status in ('Open','Replied') """
+		)[0][0]
 
 	def get_project_list(self, user_id=None):
 		"""Get project list"""
 		if not user_id:
 			user_id = frappe.session.user
 
-		project_list = frappe.db.sql("""select *
+		project_list = frappe.db.sql(
+			"""select *
 			from `tabProject` where status='Open' and project_type='External'
-			order by modified asc limit 10""", as_dict=True)
+			order by modified asc limit 10""",
+			as_dict=True,
+		)
 
 		for t in project_list:
 			t.link = get_url_to_form("Issue", t.name)
@@ -235,22 +268,41 @@
 
 	def get_project_count(self):
 		"""Get count of Project"""
-		return frappe.db.sql("""select count(*) from `tabProject`
-			where status='Open' and project_type='External'""")[0][0]
+		return frappe.db.sql(
+			"""select count(*) from `tabProject`
+			where status='Open' and project_type='External'"""
+		)[0][0]
 
 	def set_accounting_cards(self, context):
 		"""Create accounting cards if checked"""
 
 		cache = frappe.cache()
 		context.cards = []
-		for key in ("income", "expenses_booked", "income_year_to_date", "expense_year_to_date",
-					"bank_balance", "credit_balance", "invoiced_amount", "payables",
-					"sales_orders_to_bill", "purchase_orders_to_bill", "sales_order", "purchase_order",
-					"sales_orders_to_deliver", "purchase_orders_to_receive", "sales_invoice", "purchase_invoice",
-					"new_quotations", "pending_quotations"):
+		for key in (
+			"income",
+			"expenses_booked",
+			"income_year_to_date",
+			"expense_year_to_date",
+			"bank_balance",
+			"credit_balance",
+			"invoiced_amount",
+			"payables",
+			"sales_orders_to_bill",
+			"purchase_orders_to_bill",
+			"sales_order",
+			"purchase_order",
+			"sales_orders_to_deliver",
+			"purchase_orders_to_receive",
+			"sales_invoice",
+			"purchase_invoice",
+			"new_quotations",
+			"pending_quotations",
+		):
 
 			if self.get(key):
-				cache_key = "email_digest:card:{0}:{1}:{2}:{3}".format(self.company, self.frequency, key, self.from_date)
+				cache_key = "email_digest:card:{0}:{1}:{2}:{3}".format(
+					self.company, self.frequency, key, self.from_date
+				)
 				card = cache.get(cache_key)
 
 				if card:
@@ -271,8 +323,9 @@
 
 						if key == "credit_balance":
 							card.last_value = card.last_value * -1
-						card.last_value = self.fmt_money(card.last_value,False if key in ("bank_balance", "credit_balance") else True)
-
+						card.last_value = self.fmt_money(
+							card.last_value, False if key in ("bank_balance", "credit_balance") else True
+						)
 
 					if card.billed_value:
 						card.billed = int(flt(card.billed_value) / card.value * 100)
@@ -285,9 +338,11 @@
 						else:
 							card.delivered = "% Received " + str(card.delivered)
 
-					if key =="credit_balance":
-						card.value = card.value *-1
-					card.value = self.fmt_money(card.value,False if key in ("bank_balance", "credit_balance") else True)
+					if key == "credit_balance":
+						card.value = card.value * -1
+					card.value = self.fmt_money(
+						card.value, False if key in ("bank_balance", "credit_balance") else True
+					)
 
 					cache.set_value(cache_key, card, expires_in_sec=24 * 60 * 60)
 
@@ -295,30 +350,25 @@
 
 	def get_income(self):
 		"""Get income for given period"""
-		income, past_income, count = self.get_period_amounts(self.get_roots("income"),'income')
+		income, past_income, count = self.get_period_amounts(self.get_roots("income"), "income")
 
-		income_account = frappe.db.get_all('Account',
+		income_account = frappe.db.get_all(
+			"Account",
 			fields=["name"],
-			filters={
-				"root_type":"Income",
-				"parent_account":'',
-				"company": self.company
-			})
+			filters={"root_type": "Income", "parent_account": "", "company": self.company},
+		)
 
-		label = get_link_to_report("General Ledger",self.meta.get_label("income"),
+		label = get_link_to_report(
+			"General Ledger",
+			self.meta.get_label("income"),
 			filters={
 				"from_date": self.future_from_date,
 				"to_date": self.future_to_date,
 				"account": income_account[0].name,
-				"company": self.company
-			}
+				"company": self.company,
+			},
 		)
-		return {
-			"label": label,
-			"value": income,
-			"last_value": past_income,
-			"count": count
-		}
+		return {"label": label, "value": income, "last_value": past_income, "count": count}
 
 	def get_income_year_to_date(self):
 		"""Get income to date"""
@@ -326,7 +376,7 @@
 
 	def get_expense_year_to_date(self):
 		"""Get income to date"""
-		return self.get_year_to_date_balance("expense","expenses_booked")
+		return self.get_year_to_date_balance("expense", "expenses_booked")
 
 	def get_year_to_date_balance(self, root_type, fieldname):
 		"""Get income to date"""
@@ -334,67 +384,63 @@
 		count = 0
 
 		for account in self.get_root_type_accounts(root_type):
-			balance += get_balance_on(account, date = self.future_to_date)
-			count += get_count_on(account, fieldname, date = self.future_to_date)
+			balance += get_balance_on(account, date=self.future_to_date)
+			count += get_count_on(account, fieldname, date=self.future_to_date)
 
-		if fieldname == 'income':
-			filters = {
-				"currency": self.currency
-			}
-			label = get_link_to_report('Profit and Loss Statement', label=self.meta.get_label(root_type + "_year_to_date"), filters=filters)
+		if fieldname == "income":
+			filters = {"currency": self.currency}
+			label = get_link_to_report(
+				"Profit and Loss Statement",
+				label=self.meta.get_label(root_type + "_year_to_date"),
+				filters=filters,
+			)
 
-		elif fieldname == 'expenses_booked':
-			filters = {
-				"currency": self.currency
-			}
-			label = get_link_to_report('Profit and Loss Statement', label=self.meta.get_label(root_type + "_year_to_date"), filters=filters)
+		elif fieldname == "expenses_booked":
+			filters = {"currency": self.currency}
+			label = get_link_to_report(
+				"Profit and Loss Statement",
+				label=self.meta.get_label(root_type + "_year_to_date"),
+				filters=filters,
+			)
 
-		return {
-			"label": label,
-			"value": balance,
-			"count": count
-		}
+		return {"label": label, "value": balance, "count": count}
 
 	def get_bank_balance(self):
 		# account is of type "Bank" and root_type is Asset
-		return self.get_type_balance('bank_balance', 'Bank', root_type='Asset')
+		return self.get_type_balance("bank_balance", "Bank", root_type="Asset")
 
 	def get_credit_balance(self):
 		# account is of type "Bank" and root_type is Liability
-		return self.get_type_balance('credit_balance', 'Bank', root_type='Liability')
+		return self.get_type_balance("credit_balance", "Bank", root_type="Liability")
 
 	def get_payables(self):
-		return self.get_type_balance('payables', 'Payable')
+		return self.get_type_balance("payables", "Payable")
 
 	def get_invoiced_amount(self):
-		return self.get_type_balance('invoiced_amount', 'Receivable')
+		return self.get_type_balance("invoiced_amount", "Receivable")
 
 	def get_expenses_booked(self):
-		expenses, past_expenses, count = self.get_period_amounts(self.get_roots("expense"), 'expenses_booked')
-
-		expense_account = frappe.db.get_all('Account',
-			fields=["name"],
-			filters={
-				"root_type": "Expense",
-				"parent_account": '',
-				"company": self.company
-				}
-			)
-
-		label = get_link_to_report("General Ledger",self.meta.get_label("expenses_booked"),
-			filters={
-				"company":self.company,
-				"from_date":self.future_from_date,
-				"to_date":self.future_to_date,
-				"account": expense_account[0].name
-			}
+		expenses, past_expenses, count = self.get_period_amounts(
+			self.get_roots("expense"), "expenses_booked"
 		)
-		return {
-			"label": label,
-			"value": expenses,
-			"last_value": past_expenses,
-			"count": count
-		}
+
+		expense_account = frappe.db.get_all(
+			"Account",
+			fields=["name"],
+			filters={"root_type": "Expense", "parent_account": "", "company": self.company},
+		)
+
+		label = get_link_to_report(
+			"General Ledger",
+			self.meta.get_label("expenses_booked"),
+			filters={
+				"company": self.company,
+				"from_date": self.future_from_date,
+				"to_date": self.future_to_date,
+				"account": expense_account[0].name,
+			},
+		)
+		return {"label": label, "value": expenses, "last_value": past_expenses, "count": count}
 
 	def get_period_amounts(self, accounts, fieldname):
 		"""Get amounts for current and past periods"""
@@ -410,113 +456,129 @@
 	def get_sales_orders_to_bill(self):
 		"""Get value not billed"""
 
-		value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_billed/100)),0),
+		value, count = frappe.db.sql(
+			"""select ifnull((sum(grand_total)) - (sum(grand_total*per_billed/100)),0),
                     count(*) from `tabSales Order`
 					where (transaction_date <= %(to_date)s) and billing_status != "Fully Billed" and company = %(company)s
-					and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0]
+					and status not in ('Closed','Cancelled', 'Completed') """,
+			{"to_date": self.future_to_date, "company": self.company},
+		)[0]
 
-		label = get_link_to_report('Sales Order', label=self.meta.get_label("sales_orders_to_bill"),
+		label = get_link_to_report(
+			"Sales Order",
+			label=self.meta.get_label("sales_orders_to_bill"),
 			report_type="Report Builder",
 			doctype="Sales Order",
-			filters = {
-				"status": [['!=', "Closed"], ['!=', "Cancelled"]],
-				"per_billed": [['<', 100]],
-				"transaction_date": [['<=', self.future_to_date]],
-				"company": self.company
-			}
+			filters={
+				"status": [["!=", "Closed"], ["!=", "Cancelled"]],
+				"per_billed": [["<", 100]],
+				"transaction_date": [["<=", self.future_to_date]],
+				"company": self.company,
+			},
 		)
 
-		return {
-			"label": label,
-			"value": value,
-			"count": count
-		}
+		return {"label": label, "value": value, "count": count}
 
 	def get_sales_orders_to_deliver(self):
 		"""Get value not delivered"""
 
-		value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_delivered/100)),0),
+		value, count = frappe.db.sql(
+			"""select ifnull((sum(grand_total)) - (sum(grand_total*per_delivered/100)),0),
 					count(*) from `tabSales Order`
 					where (transaction_date <= %(to_date)s) and delivery_status != "Fully Delivered" and company = %(company)s
-					and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0]
+					and status not in ('Closed','Cancelled', 'Completed') """,
+			{"to_date": self.future_to_date, "company": self.company},
+		)[0]
 
-		label = get_link_to_report('Sales Order', label=self.meta.get_label("sales_orders_to_deliver"),
+		label = get_link_to_report(
+			"Sales Order",
+			label=self.meta.get_label("sales_orders_to_deliver"),
 			report_type="Report Builder",
 			doctype="Sales Order",
-			filters = {
-				"status": [['!=', "Closed"], ['!=', "Cancelled"], ['!=', "Completed"]],
-				"delivery_status": [['!=', "Fully Delivered"]],
-				"transaction_date": [['<=', self.future_to_date]],
-				"company": self.company
-			}
+			filters={
+				"status": [["!=", "Closed"], ["!=", "Cancelled"], ["!=", "Completed"]],
+				"delivery_status": [["!=", "Fully Delivered"]],
+				"transaction_date": [["<=", self.future_to_date]],
+				"company": self.company,
+			},
 		)
 
-		return {
-			"label": label,
-			"value": value,
-			"count": count
-		}
+		return {"label": label, "value": value, "count": count}
 
 	def get_purchase_orders_to_receive(self):
 		"""Get value not received"""
 
-		value, count = frappe.db.sql("""select ifnull((sum(grand_total))-(sum(grand_total*per_received/100)),0),
+		value, count = frappe.db.sql(
+			"""select ifnull((sum(grand_total))-(sum(grand_total*per_received/100)),0),
                     count(*) from `tabPurchase Order`
 					where (transaction_date <= %(to_date)s) and per_received < 100 and company = %(company)s
-					and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0]
+					and status not in ('Closed','Cancelled', 'Completed') """,
+			{"to_date": self.future_to_date, "company": self.company},
+		)[0]
 
-		label = get_link_to_report('Purchase Order', label=self.meta.get_label("purchase_orders_to_receive"),
+		label = get_link_to_report(
+			"Purchase Order",
+			label=self.meta.get_label("purchase_orders_to_receive"),
 			report_type="Report Builder",
 			doctype="Purchase Order",
-			filters = {
-				"status": [['!=', "Closed"], ['!=', "Cancelled"], ['!=', "Completed"]],
-				"per_received": [['<', 100]],
-				"transaction_date": [['<=', self.future_to_date]],
-				"company": self.company
-			}
+			filters={
+				"status": [["!=", "Closed"], ["!=", "Cancelled"], ["!=", "Completed"]],
+				"per_received": [["<", 100]],
+				"transaction_date": [["<=", self.future_to_date]],
+				"company": self.company,
+			},
 		)
 
-		return {
-			"label": label,
-			"value": value,
-			"count": count
-		}
+		return {"label": label, "value": value, "count": count}
 
 	def get_purchase_orders_to_bill(self):
 		"""Get purchase not billed"""
 
-		value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_billed/100)),0),
+		value, count = frappe.db.sql(
+			"""select ifnull((sum(grand_total)) - (sum(grand_total*per_billed/100)),0),
                     count(*) from `tabPurchase Order`
 					where (transaction_date <= %(to_date)s) and per_billed < 100 and company = %(company)s
-					and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0]
+					and status not in ('Closed','Cancelled', 'Completed') """,
+			{"to_date": self.future_to_date, "company": self.company},
+		)[0]
 
-		label = get_link_to_report('Purchase Order', label=self.meta.get_label("purchase_orders_to_bill"),
+		label = get_link_to_report(
+			"Purchase Order",
+			label=self.meta.get_label("purchase_orders_to_bill"),
 			report_type="Report Builder",
 			doctype="Purchase Order",
-			filters = {
-				"status": [['!=', "Closed"], ['!=', "Cancelled"], ['!=', "Completed"]],
-				"per_received": [['<', 100]],
-				"transaction_date": [['<=', self.future_to_date]],
-				"company": self.company
-			}
+			filters={
+				"status": [["!=", "Closed"], ["!=", "Cancelled"], ["!=", "Completed"]],
+				"per_received": [["<", 100]],
+				"transaction_date": [["<=", self.future_to_date]],
+				"company": self.company,
+			},
 		)
 
-		return {
-			"label": label,
-			"value": value,
-			"count": count
-		}
+		return {"label": label, "value": value, "count": count}
 
 	def get_type_balance(self, fieldname, account_type, root_type=None):
 
 		if root_type:
-			accounts = [d.name for d in \
-				frappe.db.get_all("Account", filters={"account_type": account_type,
-				"company": self.company, "is_group": 0, "root_type": root_type})]
+			accounts = [
+				d.name
+				for d in frappe.db.get_all(
+					"Account",
+					filters={
+						"account_type": account_type,
+						"company": self.company,
+						"is_group": 0,
+						"root_type": root_type,
+					},
+				)
+			]
 		else:
-			accounts = [d.name for d in \
-				frappe.db.get_all("Account", filters={"account_type": account_type,
-				"company": self.company, "is_group": 0})]
+			accounts = [
+				d.name
+				for d in frappe.db.get_all(
+					"Account", filters={"account_type": account_type, "company": self.company, "is_group": 0}
+				)
+			]
 
 		balance = prev_balance = 0.0
 		count = 0
@@ -525,92 +587,99 @@
 			count += get_count_on(account, fieldname, date=self.future_to_date)
 			prev_balance += get_balance_on(account, date=self.past_to_date, in_account_currency=False)
 
-		if fieldname in ("bank_balance","credit_balance"):
+		if fieldname in ("bank_balance", "credit_balance"):
 			label = ""
 			if fieldname == "bank_balance":
 				filters = {
 					"root_type": "Asset",
 					"account_type": "Bank",
 					"report_date": self.future_to_date,
-					"company": self.company
+					"company": self.company,
 				}
-				label = get_link_to_report('Account Balance', label=self.meta.get_label(fieldname), filters=filters)
+				label = get_link_to_report(
+					"Account Balance", label=self.meta.get_label(fieldname), filters=filters
+				)
 			else:
 				filters = {
 					"root_type": "Liability",
 					"account_type": "Bank",
 					"report_date": self.future_to_date,
-					"company": self.company
+					"company": self.company,
 				}
-				label = get_link_to_report('Account Balance', label=self.meta.get_label(fieldname), filters=filters)
+				label = get_link_to_report(
+					"Account Balance", label=self.meta.get_label(fieldname), filters=filters
+				)
 
-			return {
-				'label': label,
-				'value': balance,
-				'last_value': prev_balance
-			}
+			return {"label": label, "value": balance, "last_value": prev_balance}
 		else:
-			if account_type == 'Payable':
-				label = get_link_to_report('Accounts Payable', label=self.meta.get_label(fieldname),
-					filters={
-						"report_date": self.future_to_date,
-						"company": self.company
-					} )
-			elif account_type == 'Receivable':
-				label = get_link_to_report('Accounts Receivable', label=self.meta.get_label(fieldname),
-					filters={
-						"report_date": self.future_to_date,
-						"company": self.company
-					})
+			if account_type == "Payable":
+				label = get_link_to_report(
+					"Accounts Payable",
+					label=self.meta.get_label(fieldname),
+					filters={"report_date": self.future_to_date, "company": self.company},
+				)
+			elif account_type == "Receivable":
+				label = get_link_to_report(
+					"Accounts Receivable",
+					label=self.meta.get_label(fieldname),
+					filters={"report_date": self.future_to_date, "company": self.company},
+				)
 			else:
 				label = self.meta.get_label(fieldname)
 
-			return {
-				'label': label,
-				'value': balance,
-				'last_value': prev_balance,
-				'count': count
-			}
+			return {"label": label, "value": balance, "last_value": prev_balance, "count": count}
 
 	def get_roots(self, root_type):
-		return [d.name for d in frappe.db.get_all("Account",
-			filters={"root_type": root_type.title(), "company": self.company,
-				"is_group": 1, "parent_account": ["in", ("", None)]})]
+		return [
+			d.name
+			for d in frappe.db.get_all(
+				"Account",
+				filters={
+					"root_type": root_type.title(),
+					"company": self.company,
+					"is_group": 1,
+					"parent_account": ["in", ("", None)],
+				},
+			)
+		]
 
 	def get_root_type_accounts(self, root_type):
 		if not root_type in self._accounts:
-			self._accounts[root_type] = [d.name for d in \
-				frappe.db.get_all("Account", filters={"root_type": root_type.title(),
-					"company": self.company, "is_group": 0})]
+			self._accounts[root_type] = [
+				d.name
+				for d in frappe.db.get_all(
+					"Account", filters={"root_type": root_type.title(), "company": self.company, "is_group": 0}
+				)
+			]
 		return self._accounts[root_type]
 
 	def get_purchase_order(self):
 
-		return self.get_summary_of_doc("Purchase Order","purchase_order")
+		return self.get_summary_of_doc("Purchase Order", "purchase_order")
 
 	def get_sales_order(self):
 
-		return self.get_summary_of_doc("Sales Order","sales_order")
+		return self.get_summary_of_doc("Sales Order", "sales_order")
 
 	def get_pending_purchase_orders(self):
 
-		return self.get_summary_of_pending("Purchase Order","pending_purchase_orders","per_received")
+		return self.get_summary_of_pending("Purchase Order", "pending_purchase_orders", "per_received")
 
 	def get_pending_sales_orders(self):
 
-		return self.get_summary_of_pending("Sales Order","pending_sales_orders","per_delivered")
+		return self.get_summary_of_pending("Sales Order", "pending_sales_orders", "per_delivered")
 
 	def get_sales_invoice(self):
 
-		return self.get_summary_of_doc("Sales Invoice","sales_invoice")
+		return self.get_summary_of_doc("Sales Invoice", "sales_invoice")
 
 	def get_purchase_invoice(self):
 
-		return self.get_summary_of_doc("Purchase Invoice","purchase_invoice")
+		return self.get_summary_of_doc("Purchase Invoice", "purchase_invoice")
 
 	def get_new_quotations(self):
 
-		return self.get_summary_of_doc("Quotation","new_quotations")
+		return self.get_summary_of_doc("Quotation", "new_quotations")
 
 	def get_pending_quotations(self):
 
@@ -618,89 +687,104 @@
 
 	def get_summary_of_pending(self, doc_type, fieldname, getfield):
 
-		value, count, billed_value, delivered_value = frappe.db.sql("""select ifnull(sum(grand_total),0), count(*),
+		value, count, billed_value, delivered_value = frappe.db.sql(
+			"""select ifnull(sum(grand_total),0), count(*),
 			ifnull(sum(grand_total*per_billed/100),0), ifnull(sum(grand_total*{0}/100),0)  from `tab{1}`
 			where (transaction_date <= %(to_date)s)
 			and status not in ('Closed','Cancelled', 'Completed')
-			and company = %(company)s """.format(getfield, doc_type),
-			{"to_date": self.future_to_date, "company": self.company})[0]
+			and company = %(company)s """.format(
+				getfield, doc_type
+			),
+			{"to_date": self.future_to_date, "company": self.company},
+		)[0]
 
 		return {
 			"label": self.meta.get_label(fieldname),
 			"value": value,
 			"billed_value": billed_value,
 			"delivered_value": delivered_value,
-			"count": count
+			"count": count,
 		}
 
 	def get_summary_of_pending_quotations(self, fieldname):
 
-		value, count = frappe.db.sql("""select ifnull(sum(grand_total),0), count(*) from `tabQuotation`
+		value, count = frappe.db.sql(
+			"""select ifnull(sum(grand_total),0), count(*) from `tabQuotation`
 			where (transaction_date <= %(to_date)s)
 			and company = %(company)s
-			and status not in ('Ordered','Cancelled', 'Lost') """,{"to_date": self.future_to_date, "company": self.company})[0]
+			and status not in ('Ordered','Cancelled', 'Lost') """,
+			{"to_date": self.future_to_date, "company": self.company},
+		)[0]
 
-		last_value = frappe.db.sql("""select ifnull(sum(grand_total),0) from `tabQuotation`
+		last_value = frappe.db.sql(
+			"""select ifnull(sum(grand_total),0) from `tabQuotation`
 			where (transaction_date <= %(to_date)s)
 			and company = %(company)s
-			and status not in ('Ordered','Cancelled', 'Lost') """,{"to_date": self.past_to_date, "company": self.company})[0][0]
+			and status not in ('Ordered','Cancelled', 'Lost') """,
+			{"to_date": self.past_to_date, "company": self.company},
+		)[0][0]
 
-		label = get_link_to_report('Quotation', label=self.meta.get_label(fieldname),
+		label = get_link_to_report(
+			"Quotation",
+			label=self.meta.get_label(fieldname),
 			report_type="Report Builder",
 			doctype="Quotation",
-			filters = {
-				"status": [['!=', "Ordered"], ['!=', "Cancelled"], ['!=', "Lost"]],
-				"per_received": [['<', 100]],
-				"transaction_date": [['<=', self.future_to_date]],
-				"company": self.company
-			}
+			filters={
+				"status": [["!=", "Ordered"], ["!=", "Cancelled"], ["!=", "Lost"]],
+				"per_received": [["<", 100]],
+				"transaction_date": [["<=", self.future_to_date]],
+				"company": self.company,
+			},
 		)
 
-		return {
-			"label": label,
-			"value": value,
-			"last_value": last_value,
-			"count": count
-		}
+		return {"label": label, "value": value, "last_value": last_value, "count": count}
 
 	def get_summary_of_doc(self, doc_type, fieldname):
 
-		date_field = 'posting_date' if doc_type in ['Sales Invoice', 'Purchase Invoice'] \
-			else 'transaction_date'
+		date_field = (
+			"posting_date" if doc_type in ["Sales Invoice", "Purchase Invoice"] else "transaction_date"
+		)
 
-		value = flt(self.get_total_on(doc_type, self.future_from_date, self.future_to_date)[0].grand_total)
+		value = flt(
+			self.get_total_on(doc_type, self.future_from_date, self.future_to_date)[0].grand_total
+		)
 		count = self.get_total_on(doc_type, self.future_from_date, self.future_to_date)[0].count
 
-		last_value = flt(self.get_total_on(doc_type, self.past_from_date, self.past_to_date)[0].grand_total)
+		last_value = flt(
+			self.get_total_on(doc_type, self.past_from_date, self.past_to_date)[0].grand_total
+		)
 
 		filters = {
-			date_field: [['>=', self.future_from_date], ['<=', self.future_to_date]],
-			"status": [['!=','Cancelled']],
-			"company": self.company
+			date_field: [[">=", self.future_from_date], ["<=", self.future_to_date]],
+			"status": [["!=", "Cancelled"]],
+			"company": self.company,
 		}
 
-		label = get_link_to_report(doc_type,label=self.meta.get_label(fieldname),
-			report_type="Report Builder", filters=filters, doctype=doc_type)
+		label = get_link_to_report(
+			doc_type,
+			label=self.meta.get_label(fieldname),
+			report_type="Report Builder",
+			filters=filters,
+			doctype=doc_type,
+		)
 
-		return {
-			"label": label,
-			"value": value,
-			"last_value": last_value,
-			"count": count
-		}
+		return {"label": label, "value": value, "last_value": last_value, "count": count}
 
 	def get_total_on(self, doc_type, from_date, to_date):
 
-		date_field = 'posting_date' if doc_type in ['Sales Invoice', 'Purchase Invoice'] \
-			else 'transaction_date'
+		date_field = (
+			"posting_date" if doc_type in ["Sales Invoice", "Purchase Invoice"] else "transaction_date"
+		)
 
-		return frappe.get_all(doc_type,
+		return frappe.get_all(
+			doc_type,
 			filters={
-				date_field: ['between', (from_date, to_date)],
-				'status': ['not in', ('Cancelled')],
-				'company': self.company
+				date_field: ["between", (from_date, to_date)],
+				"status": ["not in", ("Cancelled")],
+				"company": self.company,
 			},
-			fields=['count(*) as count', 'sum(grand_total) as grand_total'])
+			fields=["count(*) as count", "sum(grand_total) as grand_total"],
+		)
 
 	def get_from_to_date(self):
 		today = now_datetime().date()
@@ -717,7 +801,7 @@
 			to_date = from_date + timedelta(days=6)
 		else:
 			# from date is the 1st day of the previous month
-			from_date = today - relativedelta(days=today.day-1, months=1)
+			from_date = today - relativedelta(days=today.day - 1, months=1)
 			# to date is the last day of the previous month
 			to_date = today - relativedelta(days=today.day)
 
@@ -728,7 +812,7 @@
 
 		# decide from date based on email digest frequency
 		if self.frequency == "Daily":
-			self.past_from_date = self.past_to_date = self.future_from_date - relativedelta(days = 1)
+			self.past_from_date = self.past_to_date = self.future_from_date - relativedelta(days=1)
 
 		elif self.frequency == "Weekly":
 			self.past_from_date = self.future_from_date - relativedelta(weeks=1)
@@ -755,27 +839,33 @@
 	def onload(self):
 		self.get_next_sending()
 
-	def fmt_money(self, value,absol=True):
+	def fmt_money(self, value, absol=True):
 		if absol:
-			return fmt_money(abs(value), currency = self.currency)
+			return fmt_money(abs(value), currency=self.currency)
 		else:
 			return fmt_money(value, currency=self.currency)
 
 	def get_purchase_orders_items_overdue_list(self):
 		fields_po = "distinct `tabPurchase Order Item`.parent as po"
-		fields_poi = "`tabPurchase Order Item`.parent, `tabPurchase Order Item`.schedule_date, item_code," \
-		             "received_qty, qty - received_qty as missing_qty, rate, amount"
+		fields_poi = (
+			"`tabPurchase Order Item`.parent, `tabPurchase Order Item`.schedule_date, item_code,"
+			"received_qty, qty - received_qty as missing_qty, rate, amount"
+		)
 
 		sql_po = """select {fields} from `tabPurchase Order Item`
 			left join `tabPurchase Order` on `tabPurchase Order`.name = `tabPurchase Order Item`.parent
-			where status<>'Closed' and `tabPurchase Order Item`.docstatus=1 and curdate() > `tabPurchase Order Item`.schedule_date
+			where status<>'Closed' and `tabPurchase Order Item`.docstatus=1 and CURRENT_DATE > `tabPurchase Order Item`.schedule_date
 			and received_qty < qty order by `tabPurchase Order Item`.parent DESC,
-			`tabPurchase Order Item`.schedule_date DESC""".format(fields=fields_po)
+			`tabPurchase Order Item`.schedule_date DESC""".format(
+			fields=fields_po
+		)
 
 		sql_poi = """select {fields} from `tabPurchase Order Item`
 			left join `tabPurchase Order` on `tabPurchase Order`.name = `tabPurchase Order Item`.parent
-			where status<>'Closed' and `tabPurchase Order Item`.docstatus=1 and curdate() > `tabPurchase Order Item`.schedule_date
-			and received_qty < qty order by `tabPurchase Order Item`.idx""".format(fields=fields_poi)
+			where status<>'Closed' and `tabPurchase Order Item`.docstatus=1 and CURRENT_DATE > `tabPurchase Order Item`.schedule_date
+			and received_qty < qty order by `tabPurchase Order Item`.idx""".format(
+			fields=fields_poi
+		)
 		purchase_order_list = frappe.db.sql(sql_po, as_dict=True)
 		purchase_order_items_overdue_list = frappe.db.sql(sql_poi, as_dict=True)
 
@@ -785,37 +875,44 @@
 			t.amount = fmt_money(t.amount, 2, t.currency)
 		return purchase_order_list, purchase_order_items_overdue_list
 
+
 def send():
 	now_date = now_datetime().date()
 
-	for ed in frappe.db.sql("""select name from `tabEmail Digest`
-			where enabled=1 and docstatus<2""", as_list=1):
-		ed_obj = frappe.get_doc('Email Digest', ed[0])
-		if (now_date == ed_obj.get_next_sending()):
+	for ed in frappe.db.sql(
+		"""select name from `tabEmail Digest`
+			where enabled=1 and docstatus<2""",
+		as_list=1,
+	):
+		ed_obj = frappe.get_doc("Email Digest", ed[0])
+		if now_date == ed_obj.get_next_sending():
 			ed_obj.send()
 
+
 @frappe.whitelist()
 def get_digest_msg(name):
 	return frappe.get_doc("Email Digest", name).get_msg_html()
 
+
 def get_incomes_expenses_for_period(account, from_date, to_date):
-		"""Get amounts for current and past periods"""
+	"""Get amounts for current and past periods"""
 
-		val = 0.0
-		balance_on_to_date = get_balance_on(account, date = to_date)
-		balance_before_from_date = get_balance_on(account, date = from_date - timedelta(days=1))
+	val = 0.0
+	balance_on_to_date = get_balance_on(account, date=to_date)
+	balance_before_from_date = get_balance_on(account, date=from_date - timedelta(days=1))
 
-		fy_start_date = get_fiscal_year(to_date)[1]
+	fy_start_date = get_fiscal_year(to_date)[1]
 
-		if from_date == fy_start_date:
-			val = balance_on_to_date
-		elif from_date > fy_start_date:
-			val = balance_on_to_date - balance_before_from_date
-		else:
-			last_year_closing_balance = get_balance_on(account, date=fy_start_date - timedelta(days=1))
-			val = balance_on_to_date + (last_year_closing_balance - balance_before_from_date)
+	if from_date == fy_start_date:
+		val = balance_on_to_date
+	elif from_date > fy_start_date:
+		val = balance_on_to_date - balance_before_from_date
+	else:
+		last_year_closing_balance = get_balance_on(account, date=fy_start_date - timedelta(days=1))
+		val = balance_on_to_date + (last_year_closing_balance - balance_before_from_date)
 
-		return val
+	return val
+
 
 def get_count_for_period(account, fieldname, from_date, to_date):
 	count = 0.0
@@ -833,6 +930,7 @@
 
 	return count
 
+
 def get_future_date_for_calendaer_event(frequency):
 	from_date = to_date = today()
 
diff --git a/erpnext/setup/doctype/email_digest/quotes.py b/erpnext/setup/doctype/email_digest/quotes.py
index fbd2d94..8c077a5 100644
--- a/erpnext/setup/doctype/email_digest/quotes.py
+++ b/erpnext/setup/doctype/email_digest/quotes.py
@@ -3,31 +3,63 @@
 
 def get_random_quote():
 	quotes = [
-		("Start by doing what's necessary; then do what's possible; and suddenly you are doing the impossible.", "Francis of Assisi"),
-		("The best and most beautiful things in the world cannot be seen or even touched - they must be felt with the heart.", "Hellen Keller"),
-		("I can't change the direction of the wind, but I can adjust my sails to always reach my destination.", "Jimmy Dean"),
+		(
+			"Start by doing what's necessary; then do what's possible; and suddenly you are doing the impossible.",
+			"Francis of Assisi",
+		),
+		(
+			"The best and most beautiful things in the world cannot be seen or even touched - they must be felt with the heart.",
+			"Hellen Keller",
+		),
+		(
+			"I can't change the direction of the wind, but I can adjust my sails to always reach my destination.",
+			"Jimmy Dean",
+		),
 		("We know what we are, but know not what we may be.", "William Shakespeare"),
-		("There are only two mistakes one can make along the road to truth; not going all the way, and not starting.", "Buddha"),
+		(
+			"There are only two mistakes one can make along the road to truth; not going all the way, and not starting.",
+			"Buddha",
+		),
 		("Always remember that you are absolutely unique. Just like everyone else.", "Margaret Mead"),
-		("You have to learn the rules of the game. And then you have to play better than anyone else.", "Albert Einstein"),
+		(
+			"You have to learn the rules of the game. And then you have to play better than anyone else.",
+			"Albert Einstein",
+		),
 		("Once we accept our limits, we go beyond them.", "Albert Einstein"),
 		("Quality is not an act, it is a habit.", "Aristotle"),
-		("The more that you read, the more things you will know. The more that you learn, the more places you'll go.", "Dr. Seuss"),
+		(
+			"The more that you read, the more things you will know. The more that you learn, the more places you'll go.",
+			"Dr. Seuss",
+		),
 		("From there to here, and here to there, funny things are everywhere.", "Dr. Seuss"),
 		("The secret of getting ahead is getting started.", "Mark Twain"),
 		("All generalizations are false, including this one.", "Mark Twain"),
 		("Don't let schooling interfere with your education.", "Mark Twain"),
 		("Cauliflower is nothing but cabbage with a college education.", "Mark Twain"),
-		("It's not the size of the dog in the fight, it's the size of the fight in the dog.", "Mark Twain"),
+		(
+			"It's not the size of the dog in the fight, it's the size of the fight in the dog.",
+			"Mark Twain",
+		),
 		("Climate is what we expect, weather is what we get.", "Mark Twain"),
 		("There are lies, damned lies and statistics.", "Mark Twain"),
-		("Happiness is when what you think, what you say, and what you do are in harmony.", "Mahatma Gandhi"),
-		("First they ignore you, then they laugh at you, then they fight you, then you win.", "Mahatma Gandhi"),
+		(
+			"Happiness is when what you think, what you say, and what you do are in harmony.",
+			"Mahatma Gandhi",
+		),
+		(
+			"First they ignore you, then they laugh at you, then they fight you, then you win.",
+			"Mahatma Gandhi",
+		),
 		("There is more to life than increasing its speed.", "Mahatma Gandhi"),
-		("A small body of determined spirits fired by an unquenchable faith in their mission can alter the course of history.", "Mahatma Gandhi"),
+		(
+			"A small body of determined spirits fired by an unquenchable faith in their mission can alter the course of history.",
+			"Mahatma Gandhi",
+		),
 		("If two wrongs don't make a right, try three.", "Laurence J. Peter"),
 		("Inspiration exists, but it has to find you working.", "Pablo Picasso"),
-		("The world’s first speeding ticket was given to a man going 4 times the speed limit! Walter Arnold was traveling at a breakneck 8 miles an hour in a 2mph zone, and was caught by a policeman on bicycle and fined one shilling!"),
+		(
+			"The world’s first speeding ticket was given to a man going 4 times the speed limit! Walter Arnold was traveling at a breakneck 8 miles an hour in a 2mph zone, and was caught by a policeman on bicycle and fined one shilling!"
+		),
 	]
 
 	return random.choice(quotes)
diff --git a/erpnext/setup/doctype/email_digest/test_email_digest.py b/erpnext/setup/doctype/email_digest/test_email_digest.py
index 3fdf168..dae28b8 100644
--- a/erpnext/setup/doctype/email_digest/test_email_digest.py
+++ b/erpnext/setup/doctype/email_digest/test_email_digest.py
@@ -5,5 +5,6 @@
 
 # test_records = frappe.get_test_records('Email Digest')
 
+
 class TestEmailDigest(unittest.TestCase):
 	pass
diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.py b/erpnext/setup/doctype/global_defaults/global_defaults.py
index f0b720a..984bab4 100644
--- a/erpnext/setup/doctype/global_defaults/global_defaults.py
+++ b/erpnext/setup/doctype/global_defaults/global_defaults.py
@@ -11,35 +11,37 @@
 keydict = {
 	# "key in defaults": "key in Global Defaults"
 	"fiscal_year": "current_fiscal_year",
-	'company': 'default_company',
-	'currency': 'default_currency',
+	"company": "default_company",
+	"currency": "default_currency",
 	"country": "country",
-	'hide_currency_symbol':'hide_currency_symbol',
-	'account_url':'account_url',
-	'disable_rounded_total': 'disable_rounded_total',
-	'disable_in_words': 'disable_in_words',
+	"hide_currency_symbol": "hide_currency_symbol",
+	"account_url": "account_url",
+	"disable_rounded_total": "disable_rounded_total",
+	"disable_in_words": "disable_in_words",
 }
 
 from frappe.model.document import Document
 
 
 class GlobalDefaults(Document):
-
 	def on_update(self):
 		"""update defaults"""
 		for key in keydict:
-			frappe.db.set_default(key, self.get(keydict[key], ''))
+			frappe.db.set_default(key, self.get(keydict[key], ""))
 
 		# update year start date and year end date from fiscal_year
-		year_start_end_date = frappe.db.sql("""select year_start_date, year_end_date
-			from `tabFiscal Year` where name=%s""", self.current_fiscal_year)
+		year_start_end_date = frappe.db.sql(
+			"""select year_start_date, year_end_date
+			from `tabFiscal Year` where name=%s""",
+			self.current_fiscal_year,
+		)
 		if year_start_end_date:
-			ysd = year_start_end_date[0][0] or ''
-			yed = year_start_end_date[0][1] or ''
+			ysd = year_start_end_date[0][0] or ""
+			yed = year_start_end_date[0][1] or ""
 
 			if ysd and yed:
-				frappe.db.set_default('year_start_date', ysd.strftime('%Y-%m-%d'))
-				frappe.db.set_default('year_end_date', yed.strftime('%Y-%m-%d'))
+				frappe.db.set_default("year_start_date", ysd.strftime("%Y-%m-%d"))
+				frappe.db.set_default("year_end_date", yed.strftime("%Y-%m-%d"))
 
 		# enable default currency
 		if self.default_currency:
@@ -59,21 +61,81 @@
 		self.disable_rounded_total = cint(self.disable_rounded_total)
 
 		# Make property setters to hide rounded total fields
-		for doctype in ("Quotation", "Sales Order", "Sales Invoice", "Delivery Note",
-			"Supplier Quotation", "Purchase Order", "Purchase Invoice", "Purchase Receipt"):
-			make_property_setter(doctype, "base_rounded_total", "hidden", self.disable_rounded_total, "Check", validate_fields_for_doctype=False)
-			make_property_setter(doctype, "base_rounded_total", "print_hide", 1, "Check", validate_fields_for_doctype=False)
+		for doctype in (
+			"Quotation",
+			"Sales Order",
+			"Sales Invoice",
+			"Delivery Note",
+			"Supplier Quotation",
+			"Purchase Order",
+			"Purchase Invoice",
+			"Purchase Receipt",
+		):
+			make_property_setter(
+				doctype,
+				"base_rounded_total",
+				"hidden",
+				self.disable_rounded_total,
+				"Check",
+				validate_fields_for_doctype=False,
+			)
+			make_property_setter(
+				doctype, "base_rounded_total", "print_hide", 1, "Check", validate_fields_for_doctype=False
+			)
 
-			make_property_setter(doctype, "rounded_total", "hidden", self.disable_rounded_total, "Check", validate_fields_for_doctype=False)
-			make_property_setter(doctype, "rounded_total", "print_hide", self.disable_rounded_total, "Check", validate_fields_for_doctype=False)
+			make_property_setter(
+				doctype,
+				"rounded_total",
+				"hidden",
+				self.disable_rounded_total,
+				"Check",
+				validate_fields_for_doctype=False,
+			)
+			make_property_setter(
+				doctype,
+				"rounded_total",
+				"print_hide",
+				self.disable_rounded_total,
+				"Check",
+				validate_fields_for_doctype=False,
+			)
 
-			make_property_setter(doctype, "disable_rounded_total", "default", cint(self.disable_rounded_total), "Text", validate_fields_for_doctype=False)
+			make_property_setter(
+				doctype,
+				"disable_rounded_total",
+				"default",
+				cint(self.disable_rounded_total),
+				"Text",
+				validate_fields_for_doctype=False,
+			)
 
 	def toggle_in_words(self):
 		self.disable_in_words = cint(self.disable_in_words)
 
 		# Make property setters to hide in words fields
-		for doctype in ("Quotation", "Sales Order", "Sales Invoice", "Delivery Note",
-				"Supplier Quotation", "Purchase Order", "Purchase Invoice", "Purchase Receipt"):
-			make_property_setter(doctype, "in_words", "hidden", self.disable_in_words, "Check", validate_fields_for_doctype=False)
-			make_property_setter(doctype, "in_words", "print_hide", self.disable_in_words, "Check", validate_fields_for_doctype=False)
+		for doctype in (
+			"Quotation",
+			"Sales Order",
+			"Sales Invoice",
+			"Delivery Note",
+			"Supplier Quotation",
+			"Purchase Order",
+			"Purchase Invoice",
+			"Purchase Receipt",
+		):
+			make_property_setter(
+				doctype,
+				"in_words",
+				"hidden",
+				self.disable_in_words,
+				"Check",
+				validate_fields_for_doctype=False,
+			)
+			make_property_setter(
+				doctype,
+				"in_words",
+				"print_hide",
+				self.disable_in_words,
+				"Check",
+				validate_fields_for_doctype=False,
+			)
diff --git a/erpnext/setup/doctype/item_group/item_group.js b/erpnext/setup/doctype/item_group/item_group.js
index 885d874..4b04ac1 100644
--- a/erpnext/setup/doctype/item_group/item_group.js
+++ b/erpnext/setup/doctype/item_group/item_group.js
@@ -14,6 +14,16 @@
 				]
 			}
 		}
+		frm.fields_dict['item_group_defaults'].grid.get_field("default_discount_account").get_query = function(doc, cdt, cdn) {
+			const row = locals[cdt][cdn];
+			return {
+				filters: {
+					'report_type': 'Profit and Loss',
+					'company': row.company,
+					"is_group": 0
+				}
+			};
+		}
 		frm.fields_dict["item_group_defaults"].grid.get_field("expense_account").get_query = function(doc, cdt, cdn) {
 			const row = locals[cdt][cdn];
 			return {
@@ -62,17 +72,16 @@
 			});
 		}
 
-		frappe.model.with_doctype('Item', () => {
-			const item_meta = frappe.get_meta('Item');
+		frappe.model.with_doctype('Website Item', () => {
+			const web_item_meta = frappe.get_meta('Website Item');
 
-			const valid_fields = item_meta.fields.filter(
-				df => ['Link', 'Table MultiSelect'].includes(df.fieldtype) && !df.hidden
-			).map(df => ({ label: df.label, value: df.fieldname }));
-
-			frm.fields_dict.filter_fields.grid.update_docfield_property(
-				'fieldname', 'fieldtype', 'Select'
+			const valid_fields = web_item_meta.fields.filter(df =>
+				['Link', 'Table MultiSelect'].includes(df.fieldtype) && !df.hidden
+			).map(df =>
+				({ label: df.label, value: df.fieldname })
 			);
-			frm.fields_dict.filter_fields.grid.update_docfield_property(
+
+			frm.get_field("filter_fields").grid.update_docfield_property(
 				'fieldname', 'options', valid_fields
 			);
 		});
diff --git a/erpnext/setup/doctype/item_group/item_group.json b/erpnext/setup/doctype/item_group/item_group.json
index 3e0680f..50f923d 100644
--- a/erpnext/setup/doctype/item_group/item_group.json
+++ b/erpnext/setup/doctype/item_group/item_group.json
@@ -20,12 +20,14 @@
   "sec_break_taxes",
   "taxes",
   "sb9",
-  "show_in_website",
   "route",
-  "weightage",
-  "slideshow",
   "website_title",
   "description",
+  "show_in_website",
+  "include_descendants",
+  "column_break_16",
+  "weightage",
+  "slideshow",
   "website_specifications",
   "website_filters_section",
   "filter_fields",
@@ -111,7 +113,7 @@
   },
   {
    "default": "0",
-   "description": "Check this if you want to show in website",
+   "description": "Make Item Group visible in website",
    "fieldname": "show_in_website",
    "fieldtype": "Check",
    "label": "Show in Website"
@@ -124,6 +126,7 @@
    "unique": 1
   },
   {
+   "depends_on": "show_in_website",
    "fieldname": "weightage",
    "fieldtype": "Int",
    "label": "Weightage"
@@ -186,6 +189,8 @@
    "report_hide": 1
   },
   {
+   "collapsible": 1,
+   "depends_on": "show_in_website",
    "fieldname": "website_filters_section",
    "fieldtype": "Section Break",
    "label": "Website Filters"
@@ -203,9 +208,22 @@
    "options": "Website Attribute"
   },
   {
+   "depends_on": "show_in_website",
    "fieldname": "website_title",
    "fieldtype": "Data",
    "label": "Title"
+  },
+  {
+   "fieldname": "column_break_16",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "depends_on": "show_in_website",
+   "description": "Include Website Items belonging to child Item Groups",
+   "fieldname": "include_descendants",
+   "fieldtype": "Check",
+   "label": "Include Descendants"
   }
  ],
  "icon": "fa fa-sitemap",
@@ -214,11 +232,12 @@
  "is_tree": 1,
  "links": [],
  "max_attachments": 3,
- "modified": "2021-02-18 13:40:30.049650",
+ "modified": "2022-03-09 12:27:11.055782",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Item Group",
  "name_case": "Title Case",
+ "naming_rule": "By fieldname",
  "nsm_parent_field": "parent_item_group",
  "owner": "Administrator",
  "permissions": [
@@ -285,5 +304,6 @@
  "search_fields": "parent_item_group",
  "show_name_in_global_search": 1,
  "sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "states": []
 }
\ No newline at end of file
diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index 4f92240..6e940d0 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -11,16 +11,17 @@
 from frappe.website.utils import clear_cache
 from frappe.website.website_generator import WebsiteGenerator
 
+from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import ECommerceSettings
 from erpnext.e_commerce.product_data_engine.filters import ProductFiltersBuilder
 
 
 class ItemGroup(NestedSet, WebsiteGenerator):
-	nsm_parent_field = 'parent_item_group'
+	nsm_parent_field = "parent_item_group"
 	website = frappe._dict(
-		condition_field = "show_in_website",
-		template = "templates/generators/item_group.html",
-		no_cache = 1,
-		no_breadcrumbs = 1
+		condition_field="show_in_website",
+		template="templates/generators/item_group.html",
+		no_cache=1,
+		no_breadcrumbs=1,
 	)
 
 	def autoname(self):
@@ -30,11 +31,12 @@
 		super(ItemGroup, self).validate()
 
 		if not self.parent_item_group and not frappe.flags.in_test:
-			if frappe.db.exists("Item Group", _('All Item Groups')):
-				self.parent_item_group = _('All Item Groups')
+			if frappe.db.exists("Item Group", _("All Item Groups")):
+				self.parent_item_group = _("All Item Groups")
 
 		self.make_route()
 		self.validate_item_group_defaults()
+		ECommerceSettings.validate_field_filters(self.filter_fields, enable_field_filters=True)
 
 	def on_update(self):
 		NestedSet.on_update(self)
@@ -43,15 +45,15 @@
 		self.delete_child_item_groups_key()
 
 	def make_route(self):
-		'''Make website route'''
+		"""Make website route"""
 		if not self.route:
-			self.route = ''
+			self.route = ""
 			if self.parent_item_group:
-				parent_item_group = frappe.get_doc('Item Group', self.parent_item_group)
+				parent_item_group = frappe.get_doc("Item Group", self.parent_item_group)
 
 				# make parent route only if not root
 				if parent_item_group.parent_item_group and parent_item_group.route:
-					self.route = parent_item_group.route + '/'
+					self.route = parent_item_group.route + "/"
 
 			self.route += self.scrub(self.item_group_name)
 
@@ -65,28 +67,22 @@
 	def get_context(self, context):
 		context.show_search = True
 		context.body_class = "product-page"
-		context.page_length = cint(frappe.db.get_single_value('E Commerce Settings', 'products_per_page')) or 6
-		context.search_link = '/product_search'
+		context.page_length = (
+			cint(frappe.db.get_single_value("E Commerce Settings", "products_per_page")) or 6
+		)
+		context.search_link = "/product_search"
 
 		filter_engine = ProductFiltersBuilder(self.name)
 
 		context.field_filters = filter_engine.get_field_filters()
 		context.attribute_filters = filter_engine.get_attribute_filters()
 
-		context.update({
-			"parents": get_parent_item_groups(self.parent_item_group),
-			"title": self.name
-		})
+		context.update({"parents": get_parent_item_groups(self.parent_item_group), "title": self.name})
 
 		if self.slideshow:
-			values = {
-				'show_indicators': 1,
-				'show_controls': 0,
-				'rounded': 1,
-				'slider_name': self.slideshow
-			}
+			values = {"show_indicators": 1, "show_controls": 0, "rounded": 1, "slider_name": self.slideshow}
 			slideshow = frappe.get_doc("Website Slideshow", self.slideshow)
-			slides = slideshow.get({"doctype":"Website Slideshow Item"})
+			slides = slideshow.get({"doctype": "Website Slideshow Item"})
 			for index, slide in enumerate(slides):
 				values[f"slide_{index + 1}_image"] = slide.image
 				values[f"slide_{index + 1}_title"] = slide.heading
@@ -109,61 +105,64 @@
 
 	def validate_item_group_defaults(self):
 		from erpnext.stock.doctype.item.item import validate_item_default_company_links
+
 		validate_item_default_company_links(self.item_group_defaults)
 
-def get_child_groups_for_website(item_group_name, immediate=False):
+
+def get_child_groups_for_website(item_group_name, immediate=False, include_self=False):
 	"""Returns child item groups *excluding* passed group."""
 	item_group = frappe.get_cached_value("Item Group", item_group_name, ["lft", "rgt"], as_dict=1)
-	filters = {
-		"lft": [">", item_group.lft],
-		"rgt": ["<", item_group.rgt],
-		"show_in_website": 1
-	}
+	filters = {"lft": [">", item_group.lft], "rgt": ["<", item_group.rgt], "show_in_website": 1}
 
 	if immediate:
 		filters["parent_item_group"] = item_group_name
 
-	return frappe.get_all(
-		"Item Group",
-		filters=filters,
-		fields=["name", "route"]
-	)
+	if include_self:
+		filters.update({"lft": [">=", item_group.lft], "rgt": ["<=", item_group.rgt]})
+
+	return frappe.get_all("Item Group", filters=filters, fields=["name", "route"], order_by="name")
+
 
 def get_child_item_groups(item_group_name):
-	item_group = frappe.get_cached_value("Item Group",
-		item_group_name, ["lft", "rgt"], as_dict=1)
+	item_group = frappe.get_cached_value("Item Group", item_group_name, ["lft", "rgt"], as_dict=1)
 
-	child_item_groups = [d.name for d in frappe.get_all('Item Group',
-		filters= {'lft': ('>=', item_group.lft),'rgt': ('<=', item_group.rgt)})]
+	child_item_groups = [
+		d.name
+		for d in frappe.get_all(
+			"Item Group", filters={"lft": (">=", item_group.lft), "rgt": ("<=", item_group.rgt)}
+		)
+	]
 
 	return child_item_groups or {}
 
+
 def get_item_for_list_in_html(context):
 	# add missing absolute link in files
 	# user may forget it during upload
 	if (context.get("website_image") or "").startswith("files/"):
 		context["website_image"] = "/" + quote(context["website_image"])
 
-	context["show_availability_status"] = cint(frappe.db.get_single_value('E Commerce Settings',
-		'show_availability_status'))
+	context["show_availability_status"] = cint(
+		frappe.db.get_single_value("E Commerce Settings", "show_availability_status")
+	)
 
-	products_template = 'templates/includes/products_as_list.html'
+	products_template = "templates/includes/products_as_list.html"
 
 	return frappe.get_template(products_template).render(context)
 
 
 def get_parent_item_groups(item_group_name, from_item=False):
-	base_nav_page = {"name": _("Shop by Category"), "route":"/shop-by-category"}
+	base_nav_page = {"name": _("Shop by Category"), "route": "/shop-by-category"}
 
 	if from_item and frappe.request.environ.get("HTTP_REFERER"):
 		# base page after 'Home' will vary on Item page
-		last_page = frappe.request.environ["HTTP_REFERER"].split('/')[-1]
+		last_page = frappe.request.environ["HTTP_REFERER"].split("/")[-1]
 		if last_page and last_page in ("shop-by-category", "all-products"):
 			base_nav_page_title = " ".join(last_page.split("-")).title()
-			base_nav_page = {"name": _(base_nav_page_title), "route":"/"+last_page}
+			base_nav_page = {"name": _(base_nav_page_title), "route": "/" + last_page}
 
 	base_parents = [
-		{"name": _("Home"), "route":"/"},
+		{"name": _("Home"), "route": "/"},
 		base_nav_page,
 	]
 
@@ -171,21 +170,27 @@
 		return base_parents
 
 	item_group = frappe.db.get_value("Item Group", item_group_name, ["lft", "rgt"], as_dict=1)
-	parent_groups = frappe.db.sql("""select name, route from `tabItem Group`
+	parent_groups = frappe.db.sql(
+		"""select name, route from `tabItem Group`
 		where lft <= %s and rgt >= %s
 		and show_in_website=1
-		order by lft asc""", (item_group.lft, item_group.rgt), as_dict=True)
+		order by lft asc""",
+		(item_group.lft, item_group.rgt),
+		as_dict=True,
+	)
 
 	return base_parents + parent_groups
 
+
 def invalidate_cache_for(doc, item_group=None):
 	if not item_group:
 		item_group = doc.name
 
 	for d in get_parent_item_groups(item_group):
-		item_group_name = frappe.db.get_value("Item Group", d.get('name'))
+		item_group_name = frappe.db.get_value("Item Group", d.get("name"))
 		if item_group_name:
-			clear_cache(frappe.db.get_value('Item Group', item_group_name, 'route'))
+			clear_cache(frappe.db.get_value("Item Group", item_group_name, "route"))
+
 
 def get_item_group_defaults(item, company):
 	item = frappe.get_cached_doc("Item", item)
diff --git a/erpnext/setup/doctype/item_group/test_item_group.py b/erpnext/setup/doctype/item_group/test_item_group.py
index f6e9ed4..11bc9b9 100644
--- a/erpnext/setup/doctype/item_group/test_item_group.py
+++ b/erpnext/setup/doctype/item_group/test_item_group.py
@@ -14,7 +14,8 @@
 	rebuild_tree,
 )
 
-test_records = frappe.get_test_records('Item Group')
+test_records = frappe.get_test_records("Item Group")
+
 
 class TestItem(unittest.TestCase):
 	def test_basic_tree(self, records=None):
@@ -25,12 +26,12 @@
 			records = test_records[2:]
 
 		for item_group in records:
-			lft, rgt, parent_item_group = frappe.db.get_value("Item Group", item_group["item_group_name"],
-				["lft", "rgt", "parent_item_group"])
+			lft, rgt, parent_item_group = frappe.db.get_value(
+				"Item Group", item_group["item_group_name"], ["lft", "rgt", "parent_item_group"]
+			)
 
 			if parent_item_group:
-				parent_lft, parent_rgt = frappe.db.get_value("Item Group", parent_item_group,
-					["lft", "rgt"])
+				parent_lft, parent_rgt = frappe.db.get_value("Item Group", parent_item_group, ["lft", "rgt"])
 			else:
 				# root
 				parent_lft = min_lft - 1
@@ -55,8 +56,11 @@
 		def get_no_of_children(item_groups, no_of_children):
 			children = []
 			for ig in item_groups:
-				children += frappe.db.sql_list("""select name from `tabItem Group`
-				where ifnull(parent_item_group, '')=%s""", ig or '')
+				children += frappe.db.sql_list(
+					"""select name from `tabItem Group`
+				where ifnull(parent_item_group, '')=%s""",
+					ig or "",
+				)
 
 			if len(children):
 				return get_no_of_children(children, no_of_children + len(children))
@@ -119,7 +123,10 @@
 
 	def print_tree(self):
 		import json
-		print(json.dumps(frappe.db.sql("select name, lft, rgt from `tabItem Group` order by lft"), indent=1))
+
+		print(
+			json.dumps(frappe.db.sql("select name, lft, rgt from `tabItem Group` order by lft"), indent=1)
+		)
 
 	def test_move_leaf_into_another_group(self):
 		# before move
@@ -149,12 +156,20 @@
 
 	def test_delete_leaf(self):
 		# for checking later
-		parent_item_group = frappe.db.get_value("Item Group", "_Test Item Group B - 3", "parent_item_group")
+		parent_item_group = frappe.db.get_value(
+			"Item Group", "_Test Item Group B - 3", "parent_item_group"
+		)
 		rgt = frappe.db.get_value("Item Group", parent_item_group, "rgt")
 
 		ancestors = get_ancestors_of("Item Group", "_Test Item Group B - 3")
-		ancestors = frappe.db.sql("""select name, rgt from `tabItem Group`
-			where name in ({})""".format(", ".join(["%s"]*len(ancestors))), tuple(ancestors), as_dict=True)
+		ancestors = frappe.db.sql(
+			"""select name, rgt from `tabItem Group`
+			where name in ({})""".format(
+				", ".join(["%s"] * len(ancestors))
+			),
+			tuple(ancestors),
+			as_dict=True,
+		)
 
 		frappe.delete_doc("Item Group", "_Test Item Group B - 3")
 		records_to_test = test_records[2:]
@@ -173,7 +188,9 @@
 
 	def test_delete_group(self):
 		# cannot delete group with child, but can delete leaf
-		self.assertRaises(NestedSetChildExistsError, frappe.delete_doc, "Item Group", "_Test Item Group B")
+		self.assertRaises(
+			NestedSetChildExistsError, frappe.delete_doc, "Item Group", "_Test Item Group B"
+		)
 
 	def test_merge_groups(self):
 		frappe.rename_doc("Item Group", "_Test Item Group B", "_Test Item Group C", merge=True)
@@ -186,8 +203,10 @@
 		self.test_basic_tree()
 
 		# move its children back
-		for name in frappe.db.sql_list("""select name from `tabItem Group`
-			where parent_item_group='_Test Item Group C'"""):
+		for name in frappe.db.sql_list(
+			"""select name from `tabItem Group`
+			where parent_item_group='_Test Item Group C'"""
+		):
 
 			doc = frappe.get_doc("Item Group", name)
 			doc.parent_item_group = "_Test Item Group B"
@@ -206,9 +225,21 @@
 		self.test_basic_tree()
 
 	def test_merge_leaf_into_group(self):
-		self.assertRaises(NestedSetInvalidMergeError, frappe.rename_doc, "Item Group", "_Test Item Group B - 3",
-			"_Test Item Group B", merge=True)
+		self.assertRaises(
+			NestedSetInvalidMergeError,
+			frappe.rename_doc,
+			"Item Group",
+			"_Test Item Group B - 3",
+			"_Test Item Group B",
+			merge=True,
+		)
 
 	def test_merge_group_into_leaf(self):
-		self.assertRaises(NestedSetInvalidMergeError, frappe.rename_doc, "Item Group", "_Test Item Group B",
-			"_Test Item Group B - 3", merge=True)
+		self.assertRaises(
+			NestedSetInvalidMergeError,
+			frappe.rename_doc,
+			"Item Group",
+			"_Test Item Group B",
+			"_Test Item Group B - 3",
+			merge=True,
+		)
diff --git a/erpnext/setup/doctype/naming_series/README.md b/erpnext/setup/doctype/naming_series/README.md
deleted file mode 100644
index 5a9b8ca..0000000
--- a/erpnext/setup/doctype/naming_series/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Tool to set numbering (naming) series for various DocTypes.
\ No newline at end of file
diff --git a/erpnext/setup/doctype/naming_series/__init__.py b/erpnext/setup/doctype/naming_series/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/setup/doctype/naming_series/__init__.py
+++ /dev/null
diff --git a/erpnext/setup/doctype/naming_series/naming_series.js b/erpnext/setup/doctype/naming_series/naming_series.js
deleted file mode 100644
index 7c76d9c..0000000
--- a/erpnext/setup/doctype/naming_series/naming_series.js
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-
-frappe.ui.form.on("Naming Series", {
-	onload: function(frm) {
-		frm.disable_save();
-		frm.events.get_doc_and_prefix(frm);
-	},
-
-	get_doc_and_prefix: function(frm) {
-		frappe.call({
-			method: "get_transactions",
-			doc: frm.doc,
-			callback: function(r) {
-				frm.set_df_property("select_doc_for_series", "options", r.message.transactions);
-				frm.set_df_property("prefix", "options", r.message.prefixes);
-			}
-		});
-	},
-
-	select_doc_for_series: function(frm) {
-		frm.set_value("user_must_always_select", 0);
-		frappe.call({
-			method: "get_options",
-			doc: frm.doc,
-			callback: function(r) {
-				frm.set_value("set_options", r.message);
-				if(r.message && r.message.split('\n')[0]=='')
-					frm.set_value('user_must_always_select', 1);
-				frm.refresh();
-			}
-		});
-	},
-
-	prefix: function(frm) {
-		frappe.call({
-			method: "get_current",
-			doc: frm.doc,
-			callback: function(r) {
-				frm.refresh_field("current_value");
-			}
-		});
-	},
-
-	update: function(frm) {
-		frappe.call({
-			method: "update_series",
-			doc: frm.doc,
-			callback: function(r) {
-				frm.events.get_doc_and_prefix(frm);
-			}
-		});
-	}
-});
diff --git a/erpnext/setup/doctype/naming_series/naming_series.json b/erpnext/setup/doctype/naming_series/naming_series.json
deleted file mode 100644
index f936dcf..0000000
--- a/erpnext/setup/doctype/naming_series/naming_series.json
+++ /dev/null
@@ -1,360 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2013-01-25 11:35:08", 
- "custom": 0, 
- "description": "Set prefix for numbering series on your transactions", 
- "docstatus": 0, 
- "doctype": "DocType", 
- "editable_grid": 0, 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "Set prefix for numbering series on your transactions", 
-   "fieldname": "setup_series", 
-   "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": "Setup Series", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "select_doc_for_series", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Select Transaction", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "select_doc_for_series", 
-   "fieldname": "help_html", 
-   "fieldtype": "HTML", 
-   "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": "Help HTML", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "<div class=\"well\">\nEdit list of Series in the box below. Rules:\n<ul>\n<li>Each Series Prefix on a new line.</li>\n<li>Allowed special characters are \"/\" and \"-\"</li>\n<li>Optionally, set the number of digits in the series using dot (.) followed by hashes (#). For example, \".####\" means that the series will have four digits. Default is five digits.</li>\n</ul>\nExamples:<br>\nINV-<br>\nINV-10-<br>\nINVK-<br>\nINV-.####<br>\n</div>", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "select_doc_for_series", 
-   "fieldname": "set_options", 
-   "fieldtype": "Text", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Series List for this Transaction", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "select_doc_for_series", 
-   "description": "Check this if you want to force the user to select a series before saving. There will be no default if you check this.", 
-   "fieldname": "user_must_always_select", 
-   "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": "User must always select", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "select_doc_for_series", 
-   "fieldname": "update", 
-   "fieldtype": "Button", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Update", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "Change the starting / current sequence number of an existing series.", 
-   "fieldname": "update_series", 
-   "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": "Update Series", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "prefix", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Prefix", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "This is the number of the last created transaction with this prefix", 
-   "fieldname": "current_value", 
-   "fieldtype": "Int", 
-   "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": "Current Value", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "update_series_start", 
-   "fieldtype": "Button", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Update Series Number", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "update_series_start", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 1, 
- "icon": "fa fa-sort-by-order", 
- "idx": 1, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 1, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2017-08-17 03:41:37.685910", 
- "modified_by": "Administrator", 
- "module": "Setup", 
- "name": "Naming Series", 
- "owner": "Administrator", 
- "permissions": [
-  {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 0, 
-   "email": 1, 
-   "export": 0, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 0, 
-   "role": "System Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 0, 
- "read_only": 1, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "track_changes": 0, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/setup/doctype/naming_series/naming_series.py b/erpnext/setup/doctype/naming_series/naming_series.py
deleted file mode 100644
index 986b4e8..0000000
--- a/erpnext/setup/doctype/naming_series/naming_series.py
+++ /dev/null
@@ -1,221 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-
-import frappe
-from frappe import _, msgprint, throw
-from frappe.core.doctype.doctype.doctype import validate_series
-from frappe.model.document import Document
-from frappe.model.naming import parse_naming_series
-from frappe.permissions import get_doctypes_with_read
-from frappe.utils import cint, cstr
-
-
-class NamingSeriesNotSetError(frappe.ValidationError): pass
-
-class NamingSeries(Document):
-	@frappe.whitelist()
-	def get_transactions(self, arg=None):
-		doctypes = list(set(frappe.db.sql_list("""select parent
-				from `tabDocField` df where fieldname='naming_series'""")
-			+ frappe.db.sql_list("""select dt from `tabCustom Field`
-				where fieldname='naming_series'""")))
-
-		doctypes = list(set(get_doctypes_with_read()).intersection(set(doctypes)))
-		prefixes = ""
-		for d in doctypes:
-			options = ""
-			try:
-				options = self.get_options(d)
-			except frappe.DoesNotExistError:
-				frappe.msgprint(_('Unable to find DocType {0}').format(d))
-				#frappe.pass_does_not_exist_error()
-				continue
-
-			if options:
-				prefixes = prefixes + "\n" + options
-		prefixes.replace("\n\n", "\n")
-		prefixes = prefixes.split("\n")
-
-		custom_prefixes = frappe.get_all('DocType', fields=["autoname"],
-			filters={"name": ('not in', doctypes), "autoname":('like', '%.#%'), 'module': ('not in', ['Core'])})
-		if custom_prefixes:
-			prefixes = prefixes + [d.autoname.rsplit('.', 1)[0] for d in custom_prefixes]
-
-		prefixes = "\n".join(sorted(prefixes))
-
-		return {
-			"transactions": "\n".join([''] + sorted(doctypes)),
-			"prefixes": prefixes
-		}
-
-	def scrub_options_list(self, ol):
-		options = list(filter(lambda x: x, [cstr(n).strip() for n in ol]))
-		return options
-
-	@frappe.whitelist()
-	def update_series(self, arg=None):
-		"""update series list"""
-		self.validate_series_set()
-		self.check_duplicate()
-		series_list = self.set_options.split("\n")
-
-		# set in doctype
-		self.set_series_for(self.select_doc_for_series, series_list)
-
-		# create series
-		map(self.insert_series, [d.split('.')[0] for d in series_list if d.strip()])
-
-		msgprint(_("Series Updated"))
-
-		return self.get_transactions()
-
-	def validate_series_set(self):
-		if self.select_doc_for_series and not self.set_options:
-			frappe.throw(_("Please set the series to be used."))
-
-	def set_series_for(self, doctype, ol):
-		options = self.scrub_options_list(ol)
-
-		# validate names
-		for i in options:
-			self.validate_series_name(i)
-
-		if options and self.user_must_always_select:
-			options = [''] + options
-
-		default = options[0] if options else ''
-
-		# update in property setter
-		prop_dict = {'options': "\n".join(options), 'default': default}
-
-		for prop in prop_dict:
-			ps_exists = frappe.db.get_value("Property Setter",
-				{"field_name": 'naming_series', 'doc_type': doctype, 'property': prop})
-
-			if ps_exists:
-				ps = frappe.get_doc('Property Setter', ps_exists)
-				ps.value = prop_dict[prop]
-				ps.save()
-			else:
-				ps = frappe.get_doc({
-					'doctype': 'Property Setter',
-					'doctype_or_field': 'DocField',
-					'doc_type': doctype,
-					'field_name': 'naming_series',
-					'property': prop,
-					'value': prop_dict[prop],
-					'property_type': 'Text',
-					'__islocal': 1
-				})
-				ps.save()
-
-		self.set_options = "\n".join(options)
-
-		frappe.clear_cache(doctype=doctype)
-
-	def check_duplicate(self):
-		parent = list(set(
-			frappe.db.sql_list("""select dt.name
-				from `tabDocField` df, `tabDocType` dt
-				where dt.name = df.parent and df.fieldname='naming_series' and dt.name != %s""",
-				self.select_doc_for_series)
-			+ frappe.db.sql_list("""select dt.name
-				from `tabCustom Field` df, `tabDocType` dt
-				where dt.name = df.dt and df.fieldname='naming_series' and dt.name != %s""",
-				self.select_doc_for_series)
-			))
-		sr = [[frappe.get_meta(p).get_field("naming_series").options, p] for p in parent]
-		dt = frappe.get_doc("DocType", self.select_doc_for_series)
-		options = self.scrub_options_list(self.set_options.split("\n"))
-		for series in options:
-			validate_series(dt, series)
-			for i in sr:
-				if i[0]:
-					existing_series = [d.split('.')[0] for d in i[0].split("\n")]
-					if series.split(".")[0] in existing_series:
-						frappe.throw(_("Series {0} already used in {1}").format(series,i[1]))
-
-	def validate_series_name(self, n):
-		import re
-		if not re.match(r"^[\w\- \/.#{}]+$", n, re.UNICODE):
-			throw(_('Special Characters except "-", "#", ".", "/", "{" and "}" not allowed in naming series'))
-
-	@frappe.whitelist()
-	def get_options(self, arg=None):
-		if frappe.get_meta(arg or self.select_doc_for_series).get_field("naming_series"):
-			return frappe.get_meta(arg or self.select_doc_for_series).get_field("naming_series").options
-
-	@frappe.whitelist()
-	def get_current(self, arg=None):
-		"""get series current"""
-		if self.prefix:
-			prefix = self.parse_naming_series()
-			self.current_value = frappe.db.get_value("Series",
-				prefix, "current", order_by = "name")
-
-	def insert_series(self, series):
-		"""insert series if missing"""
-		if frappe.db.get_value('Series', series, 'name', order_by="name") == None:
-			frappe.db.sql("insert into tabSeries (name, current) values (%s, 0)", (series))
-
-	@frappe.whitelist()
-	def update_series_start(self):
-		if self.prefix:
-			prefix = self.parse_naming_series()
-			self.insert_series(prefix)
-			frappe.db.sql("update `tabSeries` set current = %s where name = %s",
-				(cint(self.current_value), prefix))
-			msgprint(_("Series Updated Successfully"))
-		else:
-			msgprint(_("Please select prefix first"))
-
-	def parse_naming_series(self):
-		parts = self.prefix.split('.')
-
-		# Remove ### from the end of series
-		if parts[-1] == "#" * len(parts[-1]):
-			del parts[-1]
-
-		prefix = parse_naming_series(parts)
-		return prefix
-
-def set_by_naming_series(doctype, fieldname, naming_series, hide_name_field=True, make_mandatory=1):
-	from frappe.custom.doctype.property_setter.property_setter import make_property_setter
-	if naming_series:
-		make_property_setter(doctype, "naming_series", "hidden", 0, "Check", validate_fields_for_doctype=False)
-		make_property_setter(doctype, "naming_series", "reqd", make_mandatory, "Check", validate_fields_for_doctype=False)
-
-		# set values for mandatory
-		try:
-			frappe.db.sql("""update `tab{doctype}` set naming_series={s} where
-				ifnull(naming_series, '')=''""".format(doctype=doctype, s="%s"),
-				get_default_naming_series(doctype))
-		except NamingSeriesNotSetError:
-			pass
-
-		if hide_name_field:
-			make_property_setter(doctype, fieldname, "reqd", 0, "Check", validate_fields_for_doctype=False)
-			make_property_setter(doctype, fieldname, "hidden", 1, "Check", validate_fields_for_doctype=False)
-	else:
-		make_property_setter(doctype, "naming_series", "reqd", 0, "Check", validate_fields_for_doctype=False)
-		make_property_setter(doctype, "naming_series", "hidden", 1, "Check", validate_fields_for_doctype=False)
-
-		if hide_name_field:
-			make_property_setter(doctype, fieldname, "hidden", 0, "Check", validate_fields_for_doctype=False)
-			make_property_setter(doctype, fieldname, "reqd", 1, "Check", validate_fields_for_doctype=False)
-
-			# set values for mandatory
-			frappe.db.sql("""update `tab{doctype}` set `{fieldname}`=`name` where
-				ifnull({fieldname}, '')=''""".format(doctype=doctype, fieldname=fieldname))
-
-def get_default_naming_series(doctype):
-	naming_series = frappe.get_meta(doctype).get_field("naming_series").options or ""
-	naming_series = naming_series.split("\n")
-	out = naming_series[0] or (naming_series[1] if len(naming_series) > 1 else None)
-
-	if not out:
-		frappe.throw(_("Please set Naming Series for {0} via Setup > Settings > Naming Series").format(doctype),
-			NamingSeriesNotSetError)
-	else:
-		return out
diff --git a/erpnext/setup/doctype/party_type/party_type.py b/erpnext/setup/doctype/party_type/party_type.py
index d0d2946..cf7cba8 100644
--- a/erpnext/setup/doctype/party_type/party_type.py
+++ b/erpnext/setup/doctype/party_type/party_type.py
@@ -9,18 +9,20 @@
 class PartyType(Document):
 	pass
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_party_type(doctype, txt, searchfield, start, page_len, filters):
-	cond = ''
-	if filters and filters.get('account'):
-		account_type = frappe.db.get_value('Account', filters.get('account'), 'account_type')
+	cond = ""
+	if filters and filters.get("account"):
+		account_type = frappe.db.get_value("Account", filters.get("account"), "account_type")
 		cond = "and account_type = '%s'" % account_type
 
-	return frappe.db.sql("""select name from `tabParty Type`
+	return frappe.db.sql(
+		"""select name from `tabParty Type`
 			where `{key}` LIKE %(txt)s {cond}
-			order by name limit %(start)s, %(page_len)s"""
-			.format(key=searchfield, cond=cond), {
-				'txt': '%' + txt + '%',
-				'start': start, 'page_len': page_len
-			})
+			order by name limit %(page_len)s offset %(start)s""".format(
+			key=searchfield, cond=cond
+		),
+		{"txt": "%" + txt + "%", "start": start, "page_len": page_len},
+	)
diff --git a/erpnext/setup/doctype/party_type/test_party_type.py b/erpnext/setup/doctype/party_type/test_party_type.py
index a9a3db8..ab92ee1 100644
--- a/erpnext/setup/doctype/party_type/test_party_type.py
+++ b/erpnext/setup/doctype/party_type/test_party_type.py
@@ -5,5 +5,6 @@
 
 # test_records = frappe.get_test_records('Party Type')
 
+
 class TestPartyType(unittest.TestCase):
 	pass
diff --git a/erpnext/setup/doctype/print_heading/test_print_heading.py b/erpnext/setup/doctype/print_heading/test_print_heading.py
index 04de08d..f0e4c76 100644
--- a/erpnext/setup/doctype/print_heading/test_print_heading.py
+++ b/erpnext/setup/doctype/print_heading/test_print_heading.py
@@ -3,4 +3,4 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Print Heading')
+test_records = frappe.get_test_records("Print Heading")
diff --git a/erpnext/setup/doctype/quotation_lost_reason/test_quotation_lost_reason.py b/erpnext/setup/doctype/quotation_lost_reason/test_quotation_lost_reason.py
index 9330ba8..891864a 100644
--- a/erpnext/setup/doctype/quotation_lost_reason/test_quotation_lost_reason.py
+++ b/erpnext/setup/doctype/quotation_lost_reason/test_quotation_lost_reason.py
@@ -3,4 +3,4 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Quotation Lost Reason')
+test_records = frappe.get_test_records("Quotation Lost Reason")
diff --git a/erpnext/setup/doctype/sales_partner/sales_partner.py b/erpnext/setup/doctype/sales_partner/sales_partner.py
index d2ec49d..c313671 100644
--- a/erpnext/setup/doctype/sales_partner/sales_partner.py
+++ b/erpnext/setup/doctype/sales_partner/sales_partner.py
@@ -10,9 +10,9 @@
 
 class SalesPartner(WebsiteGenerator):
 	website = frappe._dict(
-		page_title_field = "partner_name",
-		condition_field = "show_in_website",
-		template = "templates/generators/sales_partner.html"
+		page_title_field="partner_name",
+		condition_field="show_in_website",
+		template="templates/generators/sales_partner.html",
 	)
 
 	def onload(self):
@@ -30,18 +30,25 @@
 			self.partner_website = "http://" + self.partner_website
 
 	def get_context(self, context):
-		address = frappe.db.get_value("Address",
-			{"sales_partner": self.name, "is_primary_address": 1},
-			"*", as_dict=True)
+		address = frappe.db.get_value(
+			"Address", {"sales_partner": self.name, "is_primary_address": 1}, "*", as_dict=True
+		)
 		if address:
 			city_state = ", ".join(filter(None, [address.city, address.state]))
-			address_rows = [address.address_line1, address.address_line2,
-				city_state, address.pincode, address.country]
+			address_rows = [
+				address.address_line1,
+				address.address_line2,
+				city_state,
+				address.pincode,
+				address.country,
+			]
 
-			context.update({
-				"email": address.email_id,
-				"partner_address": filter_strip_join(address_rows, "\n<br>"),
-				"phone": filter_strip_join(cstr(address.phone).split(","), "\n<br>")
-			})
+			context.update(
+				{
+					"email": address.email_id,
+					"partner_address": filter_strip_join(address_rows, "\n<br>"),
+					"phone": filter_strip_join(cstr(address.phone).split(","), "\n<br>"),
+				}
+			)
 
 		return context
diff --git a/erpnext/setup/doctype/sales_partner/test_sales_partner.py b/erpnext/setup/doctype/sales_partner/test_sales_partner.py
index 80ef368..933f68d 100644
--- a/erpnext/setup/doctype/sales_partner/test_sales_partner.py
+++ b/erpnext/setup/doctype/sales_partner/test_sales_partner.py
@@ -3,6 +3,6 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Sales Partner')
+test_records = frappe.get_test_records("Sales Partner")
 
 test_ignore = ["Item Group"]
diff --git a/erpnext/setup/doctype/sales_person/sales_person.js b/erpnext/setup/doctype/sales_person/sales_person.js
index b71a92f..d86a8f3 100644
--- a/erpnext/setup/doctype/sales_person/sales_person.js
+++ b/erpnext/setup/doctype/sales_person/sales_person.js
@@ -4,8 +4,12 @@
 frappe.ui.form.on('Sales Person', {
 	refresh: function(frm) {
 		if(frm.doc.__onload && frm.doc.__onload.dashboard_info) {
-			var info = frm.doc.__onload.dashboard_info;
-			frm.dashboard.add_indicator(__('Total Contribution Amount: {0}', [format_currency(info.allocated_amount, info.currency)]), 'blue');
+			let info = frm.doc.__onload.dashboard_info;
+			frm.dashboard.add_indicator(__('Total Contribution Amount Against Orders: {0}',
+				[format_currency(info.allocated_amount_against_order, info.currency)]), 'blue');
+
+			frm.dashboard.add_indicator(__('Total Contribution Amount Against Invoices: {0}',
+				[format_currency(info.allocated_amount_against_invoice, info.currency)]), 'blue');
 		}
 	},
 
diff --git a/erpnext/setup/doctype/sales_person/sales_person.py b/erpnext/setup/doctype/sales_person/sales_person.py
index b79a566..0082c70 100644
--- a/erpnext/setup/doctype/sales_person/sales_person.py
+++ b/erpnext/setup/doctype/sales_person/sales_person.py
@@ -11,13 +11,13 @@
 
 
 class SalesPerson(NestedSet):
-	nsm_parent_field = 'parent_sales_person'
+	nsm_parent_field = "parent_sales_person"
 
 	def validate(self):
 		if not self.parent_sales_person:
 			self.parent_sales_person = get_root_of("Sales Person")
 
-		for d in self.get('targets') or []:
+		for d in self.get("targets") or []:
 			if not flt(d.target_qty) and not flt(d.target_amount):
 				frappe.throw(_("Either target qty or target amount is mandatory."))
 		self.validate_employee_id()
@@ -28,17 +28,28 @@
 	def load_dashboard_info(self):
 		company_default_currency = get_default_currency()
 
-		allocated_amount = frappe.db.sql("""
-			select sum(allocated_amount)
-			from `tabSales Team`
-			where sales_person = %s and docstatus=1 and parenttype = 'Sales Order'
-		""",(self.sales_person_name))
+		allocated_amount_against_order = flt(
+			frappe.db.get_value(
+				"Sales Team",
+				{"docstatus": 1, "parenttype": "Sales Order", "sales_person": self.sales_person_name},
+				"sum(allocated_amount)",
+			)
+		)
+
+		allocated_amount_against_invoice = flt(
+			frappe.db.get_value(
+				"Sales Team",
+				{"docstatus": 1, "parenttype": "Sales Invoice", "sales_person": self.sales_person_name},
+				"sum(allocated_amount)",
+			)
+		)
 
 		info = {}
-		info["allocated_amount"] = flt(allocated_amount[0][0]) if allocated_amount else 0
+		info["allocated_amount_against_order"] = allocated_amount_against_order
+		info["allocated_amount_against_invoice"] = allocated_amount_against_invoice
 		info["currency"] = company_default_currency
 
-		self.set_onload('dashboard_info', info)
+		self.set_onload("dashboard_info", info)
 
 	def on_update(self):
 		super(SalesPerson, self).on_update()
@@ -57,30 +68,46 @@
 			sales_person = frappe.db.get_value("Sales Person", {"employee": self.employee})
 
 			if sales_person and sales_person != self.name:
-				frappe.throw(_("Another Sales Person {0} exists with the same Employee id").format(sales_person))
+				frappe.throw(
+					_("Another Sales Person {0} exists with the same Employee id").format(sales_person)
+				)
+
 
 def on_doctype_update():
 	frappe.db.add_index("Sales Person", ["lft", "rgt"])
 
+
 def get_timeline_data(doctype, name):
 
 	out = {}
 
-	out.update(dict(frappe.db.sql('''select
+	out.update(
+		dict(
+			frappe.db.sql(
+				"""select
 			unix_timestamp(dt.transaction_date), count(st.parenttype)
 		from
 			`tabSales Order` dt, `tabSales Team` st
 		where
 			st.sales_person = %s and st.parent = dt.name and dt.transaction_date > date_sub(curdate(), interval 1 year)
-			group by dt.transaction_date ''', name)))
+			group by dt.transaction_date """,
+				name,
+			)
+		)
+	)
 
-	sales_invoice = dict(frappe.db.sql('''select
+	sales_invoice = dict(
+		frappe.db.sql(
+			"""select
 			unix_timestamp(dt.posting_date), count(st.parenttype)
 		from
 			`tabSales Invoice` dt, `tabSales Team` st
 		where
 			st.sales_person = %s and st.parent = dt.name and dt.posting_date > date_sub(curdate(), interval 1 year)
-			group by dt.posting_date ''', name))
+			group by dt.posting_date """,
+			name,
+		)
+	)
 
 	for key in sales_invoice:
 		if out.get(key):
@@ -88,13 +115,18 @@
 		else:
 			out[key] = sales_invoice[key]
 
-	delivery_note = dict(frappe.db.sql('''select
+	delivery_note = dict(
+		frappe.db.sql(
+			"""select
 			unix_timestamp(dt.posting_date), count(st.parenttype)
 		from
 			`tabDelivery Note` dt, `tabSales Team` st
 		where
 			st.sales_person = %s and st.parent = dt.name and dt.posting_date > date_sub(curdate(), interval 1 year)
-			group by dt.posting_date ''', name))
+			group by dt.posting_date """,
+			name,
+		)
+	)
 
 	for key in delivery_note:
 		if out.get(key):
diff --git a/erpnext/setup/doctype/sales_person/sales_person_dashboard.py b/erpnext/setup/doctype/sales_person/sales_person_dashboard.py
index e946406..2ec2002 100644
--- a/erpnext/setup/doctype/sales_person/sales_person_dashboard.py
+++ b/erpnext/setup/doctype/sales_person/sales_person_dashboard.py
@@ -3,13 +3,12 @@
 
 def get_data():
 	return {
-		'heatmap': True,
-		'heatmap_message': _('This is based on transactions against this Sales Person. See timeline below for details'),
-		'fieldname': 'sales_person',
-		'transactions': [
-			{
-				'label': _('Sales'),
-				'items': ['Sales Order', 'Delivery Note', 'Sales Invoice']
-			},
-		]
+		"heatmap": True,
+		"heatmap_message": _(
+			"This is based on transactions against this Sales Person. See timeline below for details"
+		),
+		"fieldname": "sales_person",
+		"transactions": [
+			{"label": _("Sales"), "items": ["Sales Order", "Delivery Note", "Sales Invoice"]},
+		],
 	}
diff --git a/erpnext/setup/doctype/sales_person/test_sales_person.py b/erpnext/setup/doctype/sales_person/test_sales_person.py
index 786d2ca..6ff1888 100644
--- a/erpnext/setup/doctype/sales_person/test_sales_person.py
+++ b/erpnext/setup/doctype/sales_person/test_sales_person.py
@@ -5,6 +5,6 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Sales Person')
+test_records = frappe.get_test_records("Sales Person")
 
 test_ignore = ["Item Group"]
diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.py b/erpnext/setup/doctype/supplier_group/supplier_group.py
index 381e125..9d2b733 100644
--- a/erpnext/setup/doctype/supplier_group/supplier_group.py
+++ b/erpnext/setup/doctype/supplier_group/supplier_group.py
@@ -7,7 +7,7 @@
 
 
 class SupplierGroup(NestedSet):
-	nsm_parent_field = 'parent_supplier_group'
+	nsm_parent_field = "parent_supplier_group"
 
 	def validate(self):
 		if not self.parent_supplier_group:
diff --git a/erpnext/setup/doctype/supplier_group/test_supplier_group.py b/erpnext/setup/doctype/supplier_group/test_supplier_group.py
index 283b3bf..97ba705 100644
--- a/erpnext/setup/doctype/supplier_group/test_supplier_group.py
+++ b/erpnext/setup/doctype/supplier_group/test_supplier_group.py
@@ -3,4 +3,4 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Supplier Group')
+test_records = frappe.get_test_records("Supplier Group")
diff --git a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py
index 658f286..344f6c6 100644
--- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py
+++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py
@@ -15,9 +15,15 @@
 	def validate(self):
 		if self.terms:
 			validate_template(self.terms)
-		if not cint(self.buying) and not cint(self.selling) and not cint(self.hr) and not cint(self.disabled):
+		if (
+			not cint(self.buying)
+			and not cint(self.selling)
+			and not cint(self.hr)
+			and not cint(self.disabled)
+		):
 			throw(_("At least one of the Applicable Modules should be selected"))
 
+
 @frappe.whitelist()
 def get_terms_and_conditions(template_name, doc):
 	if isinstance(doc, str):
diff --git a/erpnext/setup/doctype/terms_and_conditions/test_terms_and_conditions.py b/erpnext/setup/doctype/terms_and_conditions/test_terms_and_conditions.py
index ca9e6c1..171840a 100644
--- a/erpnext/setup/doctype/terms_and_conditions/test_terms_and_conditions.py
+++ b/erpnext/setup/doctype/terms_and_conditions/test_terms_and_conditions.py
@@ -3,4 +3,4 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Terms and Conditions')
+test_records = frappe.get_test_records("Terms and Conditions")
diff --git a/erpnext/setup/doctype/territory/territory.py b/erpnext/setup/doctype/territory/territory.py
index 4c47d82..9bb5569 100644
--- a/erpnext/setup/doctype/territory/territory.py
+++ b/erpnext/setup/doctype/territory/territory.py
@@ -9,13 +9,13 @@
 
 
 class Territory(NestedSet):
-	nsm_parent_field = 'parent_territory'
+	nsm_parent_field = "parent_territory"
 
 	def validate(self):
 		if not self.parent_territory:
 			self.parent_territory = get_root_of("Territory")
 
-		for d in self.get('targets') or []:
+		for d in self.get("targets") or []:
 			if not flt(d.target_qty) and not flt(d.target_amount):
 				frappe.throw(_("Either target qty or target amount is mandatory"))
 
@@ -23,5 +23,6 @@
 		super(Territory, self).on_update()
 		self.validate_one_root()
 
+
 def on_doctype_update():
 	frappe.db.add_index("Territory", ["lft", "rgt"])
diff --git a/erpnext/setup/doctype/territory/test_territory.py b/erpnext/setup/doctype/territory/test_territory.py
index a18b7bf..4ec695d 100644
--- a/erpnext/setup/doctype/territory/test_territory.py
+++ b/erpnext/setup/doctype/territory/test_territory.py
@@ -3,6 +3,6 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Territory')
+test_records = frappe.get_test_records("Territory")
 
 test_ignore = ["Item Group"]
diff --git a/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py
index 095c3d0..319d435 100644
--- a/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py
+++ b/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py
@@ -8,61 +8,51 @@
 
 class TestTransactionDeletionRecord(unittest.TestCase):
 	def setUp(self):
-		create_company('Dunder Mifflin Paper Co')
+		create_company("Dunder Mifflin Paper Co")
 
 	def tearDown(self):
 		frappe.db.rollback()
 
 	def test_doctypes_contain_company_field(self):
-		tdr = create_transaction_deletion_request('Dunder Mifflin Paper Co')
+		tdr = create_transaction_deletion_request("Dunder Mifflin Paper Co")
 		for doctype in tdr.doctypes:
 			contains_company = False
-			doctype_fields = frappe.get_meta(doctype.doctype_name).as_dict()['fields']
+			doctype_fields = frappe.get_meta(doctype.doctype_name).as_dict()["fields"]
 			for doctype_field in doctype_fields:
-				if doctype_field['fieldtype'] == 'Link' and doctype_field['options'] == 'Company':
+				if doctype_field["fieldtype"] == "Link" and doctype_field["options"] == "Company":
 					contains_company = True
 					break
 			self.assertTrue(contains_company)
 
 	def test_no_of_docs_is_correct(self):
 		for i in range(5):
-			create_task('Dunder Mifflin Paper Co')
-		tdr = create_transaction_deletion_request('Dunder Mifflin Paper Co')
+			create_task("Dunder Mifflin Paper Co")
+		tdr = create_transaction_deletion_request("Dunder Mifflin Paper Co")
 		for doctype in tdr.doctypes:
-			if doctype.doctype_name == 'Task':
+			if doctype.doctype_name == "Task":
 				self.assertEqual(doctype.no_of_docs, 5)
 
 	def test_deletion_is_successful(self):
-		create_task('Dunder Mifflin Paper Co')
-		create_transaction_deletion_request('Dunder Mifflin Paper Co')
-		tasks_containing_company = frappe.get_all('Task',
-		filters = {
-			'company' : 'Dunder Mifflin Paper Co'
-		})
+		create_task("Dunder Mifflin Paper Co")
+		create_transaction_deletion_request("Dunder Mifflin Paper Co")
+		tasks_containing_company = frappe.get_all("Task", filters={"company": "Dunder Mifflin Paper Co"})
 		self.assertEqual(tasks_containing_company, [])
 
+
 def create_company(company_name):
-	company = frappe.get_doc({
-		'doctype': 'Company',
-		'company_name': company_name,
-		'default_currency': 'INR'
-	})
-	company.insert(ignore_if_duplicate = True)
+	company = frappe.get_doc(
+		{"doctype": "Company", "company_name": company_name, "default_currency": "INR"}
+	)
+	company.insert(ignore_if_duplicate=True)
+
 
 def create_transaction_deletion_request(company):
-	tdr = frappe.get_doc({
-		'doctype': 'Transaction Deletion Record',
-		'company': company
-	})
+	tdr = frappe.get_doc({"doctype": "Transaction Deletion Record", "company": company})
 	tdr.insert()
 	tdr.submit()
 	return tdr
 
 
 def create_task(company):
-	task = frappe.get_doc({
-		'doctype': 'Task',
-		'company': company,
-		'subject': 'Delete'
-	})
+	task = frappe.get_doc({"doctype": "Task", "company": company, "subject": "Delete"})
 	task.insert()
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
index a4719f1..c18a4b2 100644
--- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
@@ -11,15 +11,19 @@
 
 class TransactionDeletionRecord(Document):
 	def validate(self):
-		frappe.only_for('System Manager')
+		frappe.only_for("System Manager")
 		self.validate_doctypes_to_be_ignored()
 
 	def validate_doctypes_to_be_ignored(self):
 		doctypes_to_be_ignored_list = get_doctypes_to_be_ignored()
 		for doctype in self.doctypes_to_be_ignored:
 			if doctype.doctype_name not in doctypes_to_be_ignored_list:
-				frappe.throw(_("DocTypes should not be added manually to the 'Excluded DocTypes' table. You are only allowed to remove entries from it."),
-					title=_("Not Allowed"))
+				frappe.throw(
+					_(
+						"DocTypes should not be added manually to the 'Excluded DocTypes' table. You are only allowed to remove entries from it."
+					),
+					title=_("Not Allowed"),
+				)
 
 	def before_submit(self):
 		if not self.doctypes_to_be_ignored:
@@ -34,38 +38,55 @@
 	def populate_doctypes_to_be_ignored_table(self):
 		doctypes_to_be_ignored_list = get_doctypes_to_be_ignored()
 		for doctype in doctypes_to_be_ignored_list:
-			self.append('doctypes_to_be_ignored', {
-						'doctype_name' : doctype
-					})
+			self.append("doctypes_to_be_ignored", {"doctype_name": doctype})
 
 	def delete_bins(self):
-		frappe.db.sql("""delete from tabBin where warehouse in
-				(select name from tabWarehouse where company=%s)""", self.company)
+		frappe.db.sql(
+			"""delete from `tabBin` where warehouse in
+				(select name from tabWarehouse where company=%s)""",
+			self.company,
+		)
 
 	def delete_lead_addresses(self):
 		"""Delete addresses to which leads are linked"""
-		leads = frappe.get_all('Lead', filters={'company': self.company})
+		leads = frappe.get_all("Lead", filters={"company": self.company})
 		leads = ["'%s'" % row.get("name") for row in leads]
 		addresses = []
 		if leads:
-			addresses = frappe.db.sql_list("""select parent from `tabDynamic Link` where link_name
-				in ({leads})""".format(leads=",".join(leads)))
+			addresses = frappe.db.sql_list(
+				"""select parent from `tabDynamic Link` where link_name
+				in ({leads})""".format(
+					leads=",".join(leads)
+				)
+			)
 
 			if addresses:
 				addresses = ["%s" % frappe.db.escape(addr) for addr in addresses]
 
-				frappe.db.sql("""delete from tabAddress where name in ({addresses}) and
+				frappe.db.sql(
+					"""delete from `tabAddress` where name in ({addresses}) and
 					name not in (select distinct dl1.parent from `tabDynamic Link` dl1
 					inner join `tabDynamic Link` dl2 on dl1.parent=dl2.parent
-					and dl1.link_doctype<>dl2.link_doctype)""".format(addresses=",".join(addresses)))
+					and dl1.link_doctype<>dl2.link_doctype)""".format(
+						addresses=",".join(addresses)
+					)
+				)
 
-				frappe.db.sql("""delete from `tabDynamic Link` where link_doctype='Lead'
-					and parenttype='Address' and link_name in ({leads})""".format(leads=",".join(leads)))
+				frappe.db.sql(
+					"""delete from `tabDynamic Link` where link_doctype='Lead'
+					and parenttype='Address' and link_name in ({leads})""".format(
+						leads=",".join(leads)
+					)
+				)
 
-			frappe.db.sql("""update tabCustomer set lead_name=NULL where lead_name in ({leads})""".format(leads=",".join(leads)))
+			frappe.db.sql(
+				"""update `tabCustomer` set lead_name=NULL where lead_name in ({leads})""".format(
+					leads=",".join(leads)
+				)
+			)
 
 	def reset_company_values(self):
-		company_obj = frappe.get_doc('Company', self.company)
+		company_obj = frappe.get_doc("Company", self.company)
 		company_obj.total_monthly_sales = 0
 		company_obj.sales_monthly_history = None
 		company_obj.save()
@@ -76,24 +97,26 @@
 
 		tables = self.get_all_child_doctypes()
 		for docfield in docfields:
-			if docfield['parent'] != self.doctype:
-				no_of_docs = self.get_number_of_docs_linked_with_specified_company(docfield['parent'], docfield['fieldname'])
+			if docfield["parent"] != self.doctype:
+				no_of_docs = self.get_number_of_docs_linked_with_specified_company(
+					docfield["parent"], docfield["fieldname"]
+				)
 
 				if no_of_docs > 0:
-					self.delete_version_log(docfield['parent'], docfield['fieldname'])
-					self.delete_communications(docfield['parent'], docfield['fieldname'])
-					self.populate_doctypes_table(tables, docfield['parent'], no_of_docs)
+					self.delete_version_log(docfield["parent"], docfield["fieldname"])
+					self.delete_communications(docfield["parent"], docfield["fieldname"])
+					self.populate_doctypes_table(tables, docfield["parent"], no_of_docs)
 
-					self.delete_child_tables(docfield['parent'], docfield['fieldname'])
-					self.delete_docs_linked_with_specified_company(docfield['parent'], docfield['fieldname'])
+					self.delete_child_tables(docfield["parent"], docfield["fieldname"])
+					self.delete_docs_linked_with_specified_company(docfield["parent"], docfield["fieldname"])
 
-					naming_series = frappe.db.get_value('DocType', docfield['parent'], 'autoname')
+					naming_series = frappe.db.get_value("DocType", docfield["parent"], "autoname")
 					if naming_series:
-						if '#' in naming_series:
-							self.update_naming_series(naming_series, docfield['parent'])
+						if "#" in naming_series:
+							self.update_naming_series(naming_series, docfield["parent"])
 
 	def get_doctypes_to_be_ignored_list(self):
-		singles = frappe.get_all('DocType', filters = {'issingle': 1}, pluck = 'name')
+		singles = frappe.get_all("DocType", filters={"issingle": 1}, pluck="name")
 		doctypes_to_be_ignored_list = singles
 		for doctype in self.doctypes_to_be_ignored:
 			doctypes_to_be_ignored_list.append(doctype.doctype_name)
@@ -101,81 +124,103 @@
 		return doctypes_to_be_ignored_list
 
 	def get_doctypes_with_company_field(self, doctypes_to_be_ignored_list):
-		docfields = frappe.get_all('DocField',
-			filters = {
-				'fieldtype': 'Link',
-				'options': 'Company',
-				'parent': ['not in', doctypes_to_be_ignored_list]},
-			fields=['parent', 'fieldname'])
+		docfields = frappe.get_all(
+			"DocField",
+			filters={
+				"fieldtype": "Link",
+				"options": "Company",
+				"parent": ["not in", doctypes_to_be_ignored_list],
+			},
+			fields=["parent", "fieldname"],
+		)
 
 		return docfields
 
 	def get_all_child_doctypes(self):
-		return frappe.get_all('DocType', filters = {'istable': 1}, pluck = 'name')
+		return frappe.get_all("DocType", filters={"istable": 1}, pluck="name")
 
 	def get_number_of_docs_linked_with_specified_company(self, doctype, company_fieldname):
-		return frappe.db.count(doctype, {company_fieldname : self.company})
+		return frappe.db.count(doctype, {company_fieldname: self.company})
 
 	def populate_doctypes_table(self, tables, doctype, no_of_docs):
 		if doctype not in tables:
-			self.append('doctypes', {
-				'doctype_name' : doctype,
-				'no_of_docs' : no_of_docs
-			})
+			self.append("doctypes", {"doctype_name": doctype, "no_of_docs": no_of_docs})
 
 	def delete_child_tables(self, doctype, company_fieldname):
-		parent_docs_to_be_deleted = frappe.get_all(doctype, {
-			company_fieldname : self.company
-		}, pluck = 'name')
+		parent_docs_to_be_deleted = frappe.get_all(
+			doctype, {company_fieldname: self.company}, pluck="name"
+		)
 
-		child_tables = frappe.get_all('DocField', filters = {
-			'fieldtype': 'Table',
-			'parent': doctype
-		}, pluck = 'options')
+		child_tables = frappe.get_all(
+			"DocField", filters={"fieldtype": "Table", "parent": doctype}, pluck="options"
+		)
 
 		for table in child_tables:
-			frappe.db.delete(table, {
-				'parent': ['in', parent_docs_to_be_deleted]
-			})
+			frappe.db.delete(table, {"parent": ["in", parent_docs_to_be_deleted]})
 
 	def delete_docs_linked_with_specified_company(self, doctype, company_fieldname):
-		frappe.db.delete(doctype, {
-			company_fieldname : self.company
-		})
+		frappe.db.delete(doctype, {company_fieldname: self.company})
 
 	def update_naming_series(self, naming_series, doctype_name):
-		if '.' in naming_series:
-			prefix, hashes = naming_series.rsplit('.', 1)
+		if "." in naming_series:
+			prefix, hashes = naming_series.rsplit(".", 1)
 		else:
-			prefix, hashes = naming_series.rsplit('{', 1)
-		last = frappe.db.sql("""select max(name) from `tab{0}`
-						where name like %s""".format(doctype_name), prefix + '%')
+			prefix, hashes = naming_series.rsplit("{", 1)
+		last = frappe.db.sql(
+			"""select max(name) from `tab{0}`
+						where name like %s""".format(
+				doctype_name
+			),
+			prefix + "%",
+		)
 		if last and last[0][0]:
-			last = cint(last[0][0].replace(prefix, ''))
+			last = cint(last[0][0].replace(prefix, ""))
 		else:
 			last = 0
 
-		frappe.db.sql("""update tabSeries set current = %s where name=%s""", (last, prefix))
+		frappe.db.sql("""update `tabSeries` set current = %s where name=%s""", (last, prefix))
 
 	def delete_version_log(self, doctype, company_fieldname):
-		frappe.db.sql("""delete from `tabVersion` where ref_doctype=%s and docname in
-			(select name from `tab{0}` where `{1}`=%s)""".format(doctype,
-				company_fieldname), (doctype, self.company))
+		frappe.db.sql(
+			"""delete from `tabVersion` where ref_doctype=%s and docname in
+			(select name from `tab{0}` where `{1}`=%s)""".format(
+				doctype, company_fieldname
+			),
+			(doctype, self.company),
+		)
 
 	def delete_communications(self, doctype, company_fieldname):
-		reference_docs = frappe.get_all(doctype, filters={company_fieldname:self.company})
+		reference_docs = frappe.get_all(doctype, filters={company_fieldname: self.company})
 		reference_doc_names = [r.name for r in reference_docs]
 
-		communications = frappe.get_all('Communication', filters={'reference_doctype':doctype,'reference_name':['in', reference_doc_names]})
+		communications = frappe.get_all(
+			"Communication",
+			filters={"reference_doctype": doctype, "reference_name": ["in", reference_doc_names]},
+		)
 		communication_names = [c.name for c in communications]
 
-		frappe.delete_doc('Communication', communication_names, ignore_permissions=True)
+		frappe.delete_doc("Communication", communication_names, ignore_permissions=True)
+
 
 @frappe.whitelist()
 def get_doctypes_to_be_ignored():
-	doctypes_to_be_ignored_list = ['Account', 'Cost Center', 'Warehouse', 'Budget',
-		'Party Account', 'Employee', 'Sales Taxes and Charges Template',
-		'Purchase Taxes and Charges Template', 'POS Profile', 'BOM',
-		'Company', 'Bank Account', 'Item Tax Template', 'Mode of Payment',
-		'Item Default', 'Customer', 'Supplier']
+	doctypes_to_be_ignored_list = [
+		"Account",
+		"Cost Center",
+		"Warehouse",
+		"Budget",
+		"Party Account",
+		"Employee",
+		"Sales Taxes and Charges Template",
+		"Purchase Taxes and Charges Template",
+		"POS Profile",
+		"BOM",
+		"Company",
+		"Bank Account",
+		"Item Tax Template",
+		"Mode of Payment",
+		"Item Default",
+		"Customer",
+		"Supplier",
+	]
 	return doctypes_to_be_ignored_list
diff --git a/erpnext/setup/doctype/uom/test_uom.py b/erpnext/setup/doctype/uom/test_uom.py
index feb4329..3278d4e 100644
--- a/erpnext/setup/doctype/uom/test_uom.py
+++ b/erpnext/setup/doctype/uom/test_uom.py
@@ -3,4 +3,4 @@
 
 import frappe
 
-test_records = frappe.get_test_records('UOM')
+test_records = frappe.get_test_records("UOM")
diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py
index 1d7bad2..8dae23d 100644
--- a/erpnext/setup/install.py
+++ b/erpnext/setup/install.py
@@ -19,11 +19,9 @@
 
 
 def after_install():
-	frappe.get_doc({'doctype': "Role", "role_name": "Analytics"}).insert()
+	frappe.get_doc({"doctype": "Role", "role_name": "Analytics"}).insert()
 	set_single_defaults()
-	create_compact_item_print_custom_field()
-	create_print_uom_after_qty_custom_field()
-	create_print_zero_amount_taxes_custom_field()
+	create_print_setting_custom_fields()
 	add_all_roles_to("Administrator")
 	create_default_cash_flow_mapper_templates()
 	create_default_success_action()
@@ -36,25 +34,33 @@
 
 
 def check_setup_wizard_not_completed():
-	if cint(frappe.db.get_single_value('System Settings', 'setup_complete') or 0):
+	if cint(frappe.db.get_single_value("System Settings", "setup_complete") or 0):
 		message = """ERPNext can only be installed on a fresh site where the setup wizard is not completed.
 You can reinstall this site (after saving your data) using: bench --site [sitename] reinstall"""
-		frappe.throw(message)   # nosemgrep
+		frappe.throw(message)  # nosemgrep
 
 
 def set_single_defaults():
-	for dt in ('Accounts Settings', 'Print Settings', 'HR Settings', 'Buying Settings',
-		'Selling Settings', 'Stock Settings'):
-		default_values = frappe.db.sql("""select fieldname, `default` from `tabDocField`
-			where parent=%s""", dt)
+	for dt in (
+		"Accounts Settings",
+		"Print Settings",
+		"HR Settings",
+		"Buying Settings",
+		"Selling Settings",
+		"Stock Settings",
+	):
+		default_values = frappe.db.sql(
+			"""select fieldname, `default` from `tabDocField`
+			where parent=%s""",
+			dt,
+		)
 		if default_values:
 			try:
-				b = frappe.get_doc(dt, dt)
+				doc = frappe.get_doc(dt, dt)
 				for fieldname, value in default_values:
-					b.set(fieldname, value)
-				b.save()
-			except frappe.MandatoryError:
-				pass
+					doc.set(fieldname, value)
+				doc.flags.ignore_mandatory = True
+				doc.save()
 			except frappe.ValidationError:
 				pass
 
@@ -62,127 +68,140 @@
 
 	setup_currency_exchange()
 
+
 def setup_currency_exchange():
-	ces = frappe.get_single('Currency Exchange Settings')
+	ces = frappe.get_single("Currency Exchange Settings")
 	try:
-		ces.set('result_key', [])
-		ces.set('req_params', [])
+		ces.set("result_key", [])
+		ces.set("req_params", [])
 
 		ces.api_endpoint = "https://frankfurter.app/{transaction_date}"
-		ces.append('result_key', {'key': 'rates'})
-		ces.append('result_key', {'key': '{to_currency}'})
-		ces.append('req_params', {'key': 'base', 'value': '{from_currency}'})
-		ces.append('req_params', {'key': 'symbols', 'value': '{to_currency}'})
+		ces.append("result_key", {"key": "rates"})
+		ces.append("result_key", {"key": "{to_currency}"})
+		ces.append("req_params", {"key": "base", "value": "{from_currency}"})
+		ces.append("req_params", {"key": "symbols", "value": "{to_currency}"})
 		ces.save()
 	except frappe.ValidationError:
 		pass
 
-def create_compact_item_print_custom_field():
-	create_custom_field('Print Settings', {
-		'label': _('Compact Item Print'),
-		'fieldname': 'compact_item_print',
-		'fieldtype': 'Check',
-		'default': 1,
-		'insert_after': 'with_letterhead'
-	})
 
-
-def create_print_uom_after_qty_custom_field():
-	create_custom_field('Print Settings', {
-		'label': _('Print UOM after Quantity'),
-		'fieldname': 'print_uom_after_quantity',
-		'fieldtype': 'Check',
-		'default': 0,
-		'insert_after': 'compact_item_print'
-	})
-
-
-def create_print_zero_amount_taxes_custom_field():
-	create_custom_field('Print Settings', {
-		'label': _('Print taxes with zero amount'),
-		'fieldname': 'print_taxes_with_zero_amount',
-		'fieldtype': 'Check',
-		'default': 0,
-		'insert_after': 'allow_print_for_cancelled'
-	})
+def create_print_setting_custom_fields():
+	create_custom_field(
+		"Print Settings",
+		{
+			"label": _("Compact Item Print"),
+			"fieldname": "compact_item_print",
+			"fieldtype": "Check",
+			"default": 1,
+			"insert_after": "with_letterhead",
+		},
+	)
+	create_custom_field(
+		"Print Settings",
+		{
+			"label": _("Print UOM after Quantity"),
+			"fieldname": "print_uom_after_quantity",
+			"fieldtype": "Check",
+			"default": 0,
+			"insert_after": "compact_item_print",
+		},
+	)
+	create_custom_field(
+		"Print Settings",
+		{
+			"label": _("Print taxes with zero amount"),
+			"fieldname": "print_taxes_with_zero_amount",
+			"fieldtype": "Check",
+			"default": 0,
+			"insert_after": "allow_print_for_cancelled",
+		},
+	)
 
 
 def create_default_cash_flow_mapper_templates():
 	for mapper in DEFAULT_MAPPERS:
-		if not frappe.db.exists('Cash Flow Mapper', mapper['section_name']):
+		if not frappe.db.exists("Cash Flow Mapper", mapper["section_name"]):
 			doc = frappe.get_doc(mapper)
 			doc.insert(ignore_permissions=True)
 
+
 def create_default_success_action():
 	for success_action in get_default_success_action():
-		if not frappe.db.exists('Success Action', success_action.get("ref_doctype")):
+		if not frappe.db.exists("Success Action", success_action.get("ref_doctype")):
 			doc = frappe.get_doc(success_action)
 			doc.insert(ignore_permissions=True)
 
+
 def create_default_energy_point_rules():
 
 	for rule in get_default_energy_point_rules():
 		# check if any rule for ref. doctype exists
-		rule_exists = frappe.db.exists('Energy Point Rule', {
-			'reference_doctype': rule.get('reference_doctype')
-		})
-		if rule_exists: continue
+		rule_exists = frappe.db.exists(
+			"Energy Point Rule", {"reference_doctype": rule.get("reference_doctype")}
+		)
+		if rule_exists:
+			continue
 		doc = frappe.get_doc(rule)
 		doc.insert(ignore_permissions=True)
 
+
 def add_company_to_session_defaults():
 	settings = frappe.get_single("Session Default Settings")
-	settings.append("session_defaults", {
-		"ref_doctype": "Company"
-	})
+	settings.append("session_defaults", {"ref_doctype": "Company"})
 	settings.save()
 
+
 def add_standard_navbar_items():
 	navbar_settings = frappe.get_single("Navbar Settings")
 
 	erpnext_navbar_items = [
 		{
-			'item_label': 'Documentation',
-			'item_type': 'Route',
-			'route': 'https://erpnext.com/docs/user/manual',
-			'is_standard': 1
+			"item_label": "Documentation",
+			"item_type": "Route",
+			"route": "https://erpnext.com/docs/user/manual",
+			"is_standard": 1,
 		},
 		{
-			'item_label': 'User Forum',
-			'item_type': 'Route',
-			'route': 'https://discuss.erpnext.com',
-			'is_standard': 1
+			"item_label": "User Forum",
+			"item_type": "Route",
+			"route": "https://discuss.erpnext.com",
+			"is_standard": 1,
 		},
 		{
-			'item_label': 'Report an Issue',
-			'item_type': 'Route',
-			'route': 'https://github.com/frappe/erpnext/issues',
-			'is_standard': 1
-		}
+			"item_label": "Report an Issue",
+			"item_type": "Route",
+			"route": "https://github.com/frappe/erpnext/issues",
+			"is_standard": 1,
+		},
 	]
 
 	current_navbar_items = navbar_settings.help_dropdown
-	navbar_settings.set('help_dropdown', [])
+	navbar_settings.set("help_dropdown", [])
 
 	for item in erpnext_navbar_items:
-		current_labels = [item.get('item_label') for item in current_navbar_items]
-		if not item.get('item_label') in current_labels:
-			navbar_settings.append('help_dropdown', item)
+		current_labels = [item.get("item_label") for item in current_navbar_items]
+		if not item.get("item_label") in current_labels:
+			navbar_settings.append("help_dropdown", item)
 
 	for item in current_navbar_items:
-		navbar_settings.append('help_dropdown', {
-			'item_label': item.item_label,
-			'item_type': item.item_type,
-			'route': item.route,
-			'action': item.action,
-			'is_standard': item.is_standard,
-			'hidden': item.hidden
-		})
+		navbar_settings.append(
+			"help_dropdown",
+			{
+				"item_label": item.item_label,
+				"item_type": item.item_type,
+				"route": item.route,
+				"action": item.action,
+				"is_standard": item.is_standard,
+				"hidden": item.hidden,
+			},
+		)
 
 	navbar_settings.save()
 
+
 def add_app_name():
-	frappe.db.set_value('System Settings', None, 'app_name', 'ERPNext')
+	frappe.db.set_value("System Settings", None, "app_name", "ERPNext")
+
 
 def add_non_standard_user_types():
 	user_types = get_user_types_data()
@@ -191,91 +210,95 @@
 	for user_type, data in user_types.items():
 		user_type_limit.setdefault(frappe.scrub(user_type), 20)
 
-	update_site_config('user_type_doctype_limit', user_type_limit)
+	update_site_config("user_type_doctype_limit", user_type_limit)
 
 	for user_type, data in user_types.items():
 		create_custom_role(data)
 		create_user_type(user_type, data)
 
+
 def get_user_types_data():
 	return {
-		'Employee Self Service': {
-			'role': 'Employee Self Service',
-			'apply_user_permission_on': 'Employee',
-			'user_id_field': 'user_id',
-			'doctypes': {
+		"Employee Self Service": {
+			"role": "Employee Self Service",
+			"apply_user_permission_on": "Employee",
+			"user_id_field": "user_id",
+			"doctypes": {
 				# masters
-				'Holiday List': ['read'],
-				'Employee': ['read', 'write'],
+				"Holiday List": ["read"],
+				"Employee": ["read", "write"],
 				# payroll
-				'Salary Slip': ['read'],
-				'Employee Benefit Application': ['read', 'write', 'create', 'delete'],
+				"Salary Slip": ["read"],
+				"Employee Benefit Application": ["read", "write", "create", "delete"],
 				# expenses
-				'Expense Claim': ['read', 'write', 'create', 'delete'],
-				'Employee Advance': ['read', 'write', 'create', 'delete'],
+				"Expense Claim": ["read", "write", "create", "delete"],
+				"Employee Advance": ["read", "write", "create", "delete"],
 				# leave and attendance
-				'Leave Application': ['read', 'write', 'create', 'delete'],
-				'Attendance Request': ['read', 'write', 'create', 'delete'],
-				'Compensatory Leave Request': ['read', 'write', 'create', 'delete'],
+				"Leave Application": ["read", "write", "create", "delete"],
+				"Attendance Request": ["read", "write", "create", "delete"],
+				"Compensatory Leave Request": ["read", "write", "create", "delete"],
 				# tax
-				'Employee Tax Exemption Declaration': ['read', 'write', 'create', 'delete'],
-				'Employee Tax Exemption Proof Submission': ['read', 'write', 'create', 'delete'],
+				"Employee Tax Exemption Declaration": ["read", "write", "create", "delete"],
+				"Employee Tax Exemption Proof Submission": ["read", "write", "create", "delete"],
 				# projects
-				'Timesheet': ['read', 'write', 'create', 'delete', 'submit', 'cancel', 'amend'],
+				"Timesheet": ["read", "write", "create", "delete", "submit", "cancel", "amend"],
 				# trainings
-				'Training Program': ['read'],
-				'Training Feedback': ['read', 'write', 'create', 'delete', 'submit', 'cancel', 'amend'],
+				"Training Program": ["read"],
+				"Training Feedback": ["read", "write", "create", "delete", "submit", "cancel", "amend"],
 				# shifts
-				'Shift Request': ['read', 'write', 'create', 'delete', 'submit', 'cancel', 'amend'],
+				"Shift Request": ["read", "write", "create", "delete", "submit", "cancel", "amend"],
 				# misc
-				'Employee Grievance': ['read', 'write', 'create', 'delete'],
-				'Employee Referral': ['read', 'write', 'create', 'delete'],
-				'Travel Request': ['read', 'write', 'create', 'delete']
-			}
+				"Employee Grievance": ["read", "write", "create", "delete"],
+				"Employee Referral": ["read", "write", "create", "delete"],
+				"Travel Request": ["read", "write", "create", "delete"],
+			},
 		}
 	}
 
+
 def create_custom_role(data):
-	if data.get('role') and not frappe.db.exists('Role', data.get('role')):
-		frappe.get_doc({
-			'doctype': 'Role',
-			'role_name': data.get('role'),
-			'desk_access': 1,
-			'is_custom': 1
-		}).insert(ignore_permissions=True)
+	if data.get("role") and not frappe.db.exists("Role", data.get("role")):
+		frappe.get_doc(
+			{"doctype": "Role", "role_name": data.get("role"), "desk_access": 1, "is_custom": 1}
+		).insert(ignore_permissions=True)
+
 
 def create_user_type(user_type, data):
-	if frappe.db.exists('User Type', user_type):
-		doc = frappe.get_cached_doc('User Type', user_type)
+	if frappe.db.exists("User Type", user_type):
+		doc = frappe.get_cached_doc("User Type", user_type)
 		doc.user_doctypes = []
 	else:
-		doc = frappe.new_doc('User Type')
-		doc.update({
-			'name': user_type,
-			'role': data.get('role'),
-			'user_id_field': data.get('user_id_field'),
-			'apply_user_permission_on': data.get('apply_user_permission_on')
-		})
+		doc = frappe.new_doc("User Type")
+		doc.update(
+			{
+				"name": user_type,
+				"role": data.get("role"),
+				"user_id_field": data.get("user_id_field"),
+				"apply_user_permission_on": data.get("apply_user_permission_on"),
+			}
+		)
 
 	create_role_permissions_for_doctype(doc, data)
 	doc.save(ignore_permissions=True)
 
+
 def create_role_permissions_for_doctype(doc, data):
-	for doctype, perms in data.get('doctypes').items():
-		args = {'document_type': doctype}
+	for doctype, perms in data.get("doctypes").items():
+		args = {"document_type": doctype}
 		for perm in perms:
 			args[perm] = 1
 
-		doc.append('user_doctypes', args)
+		doc.append("user_doctypes", args)
+
 
 def update_select_perm_after_install():
 	if not frappe.flags.update_select_perm_after_migrate:
 		return
 
 	frappe.flags.ignore_select_perm = False
-	for row in frappe.get_all('User Type', filters= {'is_standard': 0}):
-		print('Updating user type :- ', row.name)
-		doc = frappe.get_doc('User Type', row.name)
+	for row in frappe.get_all("User Type", filters={"is_standard": 0}):
+		print("Updating user type :- ", row.name)
+		doc = frappe.get_doc("User Type", row.name)
 		doc.save()
 
 	frappe.flags.update_select_perm_after_migrate = False
diff --git a/erpnext/setup/module_onboarding/home/home.json b/erpnext/setup/module_onboarding/home/home.json
index 1b2dbc6..f02fc45 100644
--- a/erpnext/setup/module_onboarding/home/home.json
+++ b/erpnext/setup/module_onboarding/home/home.json
@@ -25,7 +25,7 @@
  "documentation_url": "https://docs.erpnext.com/docs/v13/user/manual/en/setting-up/company-setup",
  "idx": 0,
  "is_complete": 0,
- "modified": "2021-12-15 14:23:52.460913",
+ "modified": "2022-06-07 14:31:00.575193",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Home",
diff --git a/erpnext/setup/onboarding_step/data_import/data_import.json b/erpnext/setup/onboarding_step/data_import/data_import.json
index 48741dc..4999a36 100644
--- a/erpnext/setup/onboarding_step/data_import/data_import.json
+++ b/erpnext/setup/onboarding_step/data_import/data_import.json
@@ -2,14 +2,14 @@
  "action": "Watch Video",
  "action_label": "Learn more about data migration",
  "creation": "2021-05-19 05:29:16.809610",
- "description": "# Import Data from Spreadsheet\n\nIn ERPNext, you can easily migrate your historical data using spreadsheets. You can use it for migrating not just masters (like Customer, Supplier, Items), but also for transactions like (outstanding invoices, opening stock and accounting entries, etc). If you are migrating from [Tally](https://tallysolutions.com/) or [Quickbooks](https://quickbooks.intuit.com/in/), we got special migration tools for you.",
+ "description": "# Import Data from Spreadsheet\n\nIn ERPNext, you can easily migrate your historical data using spreadsheets. You can use it for migrating not just masters (like Customer, Supplier, Items), but also for transactions like (outstanding invoices, opening stock and accounting entries, etc).",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2021-12-15 13:10:57.346422",
+ "modified": "2022-06-07 14:28:51.390813",
  "modified_by": "Administrator",
  "name": "Data import",
  "owner": "Administrator",
diff --git a/erpnext/setup/onboarding_step/navigation_help/navigation_help.json b/erpnext/setup/onboarding_step/navigation_help/navigation_help.json
index 388853d..cf07968 100644
--- a/erpnext/setup/onboarding_step/navigation_help/navigation_help.json
+++ b/erpnext/setup/onboarding_step/navigation_help/navigation_help.json
@@ -9,7 +9,7 @@
  "is_complete": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2021-12-15 14:20:55.441678",
+ "modified": "2022-06-07 14:28:00.901082",
  "modified_by": "Administrator",
  "name": "Navigation Help",
  "owner": "Administrator",
diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py
index 6cb15b2..83b5038 100644
--- a/erpnext/setup/setup_wizard/data/dashboard_charts.py
+++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py
@@ -13,6 +13,7 @@
 			return company_list[0].name
 	return None
 
+
 def get_default_dashboards():
 	company = frappe.get_doc("Company", get_company_for_dashboards())
 	income_account = company.default_income_account or get_account("Income Account", company.name)
@@ -25,20 +26,18 @@
 				"doctype": "Dashboard",
 				"dashboard_name": "Accounts",
 				"charts": [
-					{ "chart": "Outgoing Bills (Sales Invoice)" },
-					{ "chart": "Incoming Bills (Purchase Invoice)" },
-					{ "chart": "Bank Balance" },
-					{ "chart": "Income" },
-					{ "chart": "Expenses" },
-					{ "chart": "Patient Appointments" }
-				]
+					{"chart": "Outgoing Bills (Sales Invoice)"},
+					{"chart": "Incoming Bills (Purchase Invoice)"},
+					{"chart": "Bank Balance"},
+					{"chart": "Income"},
+					{"chart": "Expenses"},
+					{"chart": "Patient Appointments"},
+				],
 			},
 			{
 				"doctype": "Dashboard",
 				"dashboard_name": "Project",
-				"charts": [
-					{ "chart": "Project Summary", "width": "Full" }
-				]
+				"charts": [{"chart": "Project Summary", "width": "Full"}],
 			},
 		],
 		"Charts": [
@@ -54,7 +53,7 @@
 				"timeseries": 1,
 				"owner": "Administrator",
 				"type": "Line",
-				"width": "Half"
+				"width": "Half",
 			},
 			{
 				"doctype": "Dashboard Chart",
@@ -68,7 +67,7 @@
 				"timeseries": 1,
 				"owner": "Administrator",
 				"type": "Line",
-				"width": "Half"
+				"width": "Half",
 			},
 			{
 				"doctype": "Dashboard Chart",
@@ -82,7 +81,7 @@
 				"timeseries": 1,
 				"owner": "Administrator",
 				"type": "Line",
-				"width": "Half"
+				"width": "Half",
 			},
 			{
 				"doctype": "Dashboard Chart",
@@ -98,7 +97,7 @@
 				"owner": "Administrator",
 				"document_type": "Purchase Invoice",
 				"type": "Bar",
-				"width": "Half"
+				"width": "Half",
 			},
 			{
 				"doctype": "Dashboard Chart",
@@ -114,18 +113,18 @@
 				"owner": "Administrator",
 				"document_type": "Sales Invoice",
 				"type": "Bar",
-				"width": "Half"
+				"width": "Half",
 			},
 			{
-				'doctype': 'Dashboard Chart',
-				'name': 'Project Summary',
-				'chart_name': 'Project Summary',
-				'chart_type': 'Report',
-				'report_name': 'Project Summary',
-				'is_public': 1,
-				'filters_json': json.dumps({"company": company.name, "status": "Open"}),
-				'type': 'Bar',
-				'custom_options': '{"type": "bar", "colors": ["#fc4f51", "#78d6ff", "#7575ff"], "axisOptions": { "shortenYAxisNumbers": 1}, "barOptions": { "stacked": 1 }}',
+				"doctype": "Dashboard Chart",
+				"name": "Project Summary",
+				"chart_name": "Project Summary",
+				"chart_type": "Report",
+				"report_name": "Project Summary",
+				"is_public": 1,
+				"filters_json": json.dumps({"company": company.name, "status": "Open"}),
+				"type": "Bar",
+				"custom_options": '{"type": "bar", "colors": ["#fc4f51", "#78d6ff", "#7575ff"], "axisOptions": { "shortenYAxisNumbers": 1}, "barOptions": { "stacked": 1 }}',
 			},
 			{
 				"doctype": "Dashboard Chart",
@@ -140,11 +139,12 @@
 				"owner": "Administrator",
 				"document_type": "Patient Appointment",
 				"type": "Line",
-				"width": "Half"
-			}
-		]
+				"width": "Half",
+			},
+		],
 	}
 
+
 def get_account(account_type, company):
 	accounts = frappe.get_list("Account", filters={"account_type": account_type, "company": company})
 	if accounts:
diff --git a/erpnext/setup/setup_wizard/data/industry_type.py b/erpnext/setup/setup_wizard/data/industry_type.py
index ecd8b00..0bc3f32 100644
--- a/erpnext/setup/setup_wizard/data/industry_type.py
+++ b/erpnext/setup/setup_wizard/data/industry_type.py
@@ -3,55 +3,55 @@
 
 def get_industry_types():
 	return [
-		_('Accounting'),
-		_('Advertising'),
-		_('Aerospace'),
-		_('Agriculture'),
-		_('Airline'),
-		_('Apparel & Accessories'),
-		_('Automotive'),
-		_('Banking'),
-		_('Biotechnology'),
-		_('Broadcasting'),
-		_('Brokerage'),
-		_('Chemical'),
-		_('Computer'),
-		_('Consulting'),
-		_('Consumer Products'),
-		_('Cosmetics'),
-		_('Defense'),
-		_('Department Stores'),
-		_('Education'),
-		_('Electronics'),
-		_('Energy'),
-		_('Entertainment & Leisure'),
-		_('Executive Search'),
-		_('Financial Services'),
-		_('Food, Beverage & Tobacco'),
-		_('Grocery'),
-		_('Health Care'),
-		_('Internet Publishing'),
-		_('Investment Banking'),
-		_('Legal'),
-		_('Manufacturing'),
-		_('Motion Picture & Video'),
-		_('Music'),
-		_('Newspaper Publishers'),
-		_('Online Auctions'),
-		_('Pension Funds'),
-		_('Pharmaceuticals'),
-		_('Private Equity'),
-		_('Publishing'),
-		_('Real Estate'),
-		_('Retail & Wholesale'),
-		_('Securities & Commodity Exchanges'),
-		_('Service'),
-		_('Soap & Detergent'),
-		_('Software'),
-		_('Sports'),
-		_('Technology'),
-		_('Telecommunications'),
-		_('Television'),
-		_('Transportation'),
-		_('Venture Capital')
+		_("Accounting"),
+		_("Advertising"),
+		_("Aerospace"),
+		_("Agriculture"),
+		_("Airline"),
+		_("Apparel & Accessories"),
+		_("Automotive"),
+		_("Banking"),
+		_("Biotechnology"),
+		_("Broadcasting"),
+		_("Brokerage"),
+		_("Chemical"),
+		_("Computer"),
+		_("Consulting"),
+		_("Consumer Products"),
+		_("Cosmetics"),
+		_("Defense"),
+		_("Department Stores"),
+		_("Education"),
+		_("Electronics"),
+		_("Energy"),
+		_("Entertainment & Leisure"),
+		_("Executive Search"),
+		_("Financial Services"),
+		_("Food, Beverage & Tobacco"),
+		_("Grocery"),
+		_("Health Care"),
+		_("Internet Publishing"),
+		_("Investment Banking"),
+		_("Legal"),
+		_("Manufacturing"),
+		_("Motion Picture & Video"),
+		_("Music"),
+		_("Newspaper Publishers"),
+		_("Online Auctions"),
+		_("Pension Funds"),
+		_("Pharmaceuticals"),
+		_("Private Equity"),
+		_("Publishing"),
+		_("Real Estate"),
+		_("Retail & Wholesale"),
+		_("Securities & Commodity Exchanges"),
+		_("Service"),
+		_("Soap & Detergent"),
+		_("Software"),
+		_("Sports"),
+		_("Technology"),
+		_("Telecommunications"),
+		_("Television"),
+		_("Transportation"),
+		_("Venture Capital"),
 	]
diff --git a/erpnext/setup/setup_wizard/operations/company_setup.py b/erpnext/setup/setup_wizard/operations/company_setup.py
index 74c1bd8..aadc989 100644
--- a/erpnext/setup/setup_wizard/operations/company_setup.py
+++ b/erpnext/setup/setup_wizard/operations/company_setup.py
@@ -6,42 +6,51 @@
 from frappe.utils import cstr, getdate
 from .default_website import website_maker
 
+
 def create_fiscal_year_and_company(args):
-	if (args.get('fy_start_date')):
-		curr_fiscal_year = get_fy_details(args.get('fy_start_date'), args.get('fy_end_date'))
-		frappe.get_doc({
-			"doctype":"Fiscal Year",
-			'year': curr_fiscal_year,
-			'year_start_date': args.get('fy_start_date'),
-			'year_end_date': args.get('fy_end_date'),
-		}).insert()
+	if args.get("fy_start_date"):
+		curr_fiscal_year = get_fy_details(args.get("fy_start_date"), args.get("fy_end_date"))
+		frappe.get_doc(
+			{
+				"doctype": "Fiscal Year",
+				"year": curr_fiscal_year,
+				"year_start_date": args.get("fy_start_date"),
+				"year_end_date": args.get("fy_end_date"),
+			}
+		).insert()
 
-	if (args.get('company_name')):
-		frappe.get_doc({
-			"doctype":"Company",
-			'company_name':args.get('company_name'),
-			'enable_perpetual_inventory': 1,
-			'abbr':args.get('company_abbr'),
-			'default_currency':args.get('currency'),
-			'country': args.get('country'),
-			'create_chart_of_accounts_based_on': 'Standard Template',
-			'chart_of_accounts': args.get('chart_of_accounts'),
-			'domain': args.get('domains')[0]
-		}).insert()
+	if args.get("company_name"):
+		frappe.get_doc(
+			{
+				"doctype": "Company",
+				"company_name": args.get("company_name"),
+				"enable_perpetual_inventory": 1,
+				"abbr": args.get("company_abbr"),
+				"default_currency": args.get("currency"),
+				"country": args.get("country"),
+				"create_chart_of_accounts_based_on": "Standard Template",
+				"chart_of_accounts": args.get("chart_of_accounts"),
+			}
+		).insert()
 
-def enable_shopping_cart(args): # nosemgrep
+
+def enable_shopping_cart(args):  # nosemgrep
 	# Needs price_lists
-	frappe.get_doc({
-		"doctype": "E Commerce Settings",
-		"enabled": 1,
-		'company': args.get('company_name')	,
-		'price_list': frappe.db.get_value("Price List", {"selling": 1}),
-		'default_customer_group': _("Individual"),
-		'quotation_series': "QTN-",
-	}).insert()
+	frappe.get_doc(
+		{
+			"doctype": "E Commerce Settings",
+			"enabled": 1,
+			"company": args.get("company_name"),
+			"price_list": frappe.db.get_value("Price List", {"selling": 1}),
+			"default_customer_group": _("Individual"),
+			"quotation_series": "QTN-",
+		}
+	).insert()
+
 
 def create_email_digest():
 	from frappe.utils.user import get_system_managers
+
 	system_managers = get_system_managers(only_name=True)
 
 	if not system_managers:
@@ -49,20 +58,20 @@
 
 	recipients = []
 	for d in system_managers:
-		recipients.append({
-			'recipient': d
-		})
+		recipients.append({"recipient": d})
 
 	companies = frappe.db.sql_list("select name FROM `tabCompany`")
 	for company in companies:
 		if not frappe.db.exists("Email Digest", "Default Weekly Digest - " + company):
-			edigest = frappe.get_doc({
-				"doctype": "Email Digest",
-				"name": "Default Weekly Digest - " + company,
-				"company": company,
-				"frequency": "Weekly",
-				"recipients": recipients
-			})
+			edigest = frappe.get_doc(
+				{
+					"doctype": "Email Digest",
+					"name": "Default Weekly Digest - " + company,
+					"company": company,
+					"frequency": "Weekly",
+					"recipients": recipients,
+				}
+			)
 
 			for df in edigest.meta.get("fields", {"fieldtype": "Check"}):
 				if df.fieldname != "scheduler_errors":
@@ -73,39 +82,53 @@
 	# scheduler errors digest
 	if companies:
 		edigest = frappe.new_doc("Email Digest")
-		edigest.update({
-			"name": "Scheduler Errors",
-			"company": companies[0],
-			"frequency": "Daily",
-			"recipients": recipients,
-			"scheduler_errors": 1,
-			"enabled": 1
-		})
+		edigest.update(
+			{
+				"name": "Scheduler Errors",
+				"company": companies[0],
+				"frequency": "Daily",
+				"recipients": recipients,
+				"scheduler_errors": 1,
+				"enabled": 1,
+			}
+		)
 		edigest.insert()
 
+
 def create_logo(args):
 	if args.get("attach_logo"):
 		attach_logo = args.get("attach_logo").split(",")
-		if len(attach_logo)==3:
+		if len(attach_logo) == 3:
 			filename, filetype, content = attach_logo
-			_file = frappe.get_doc({
-				"doctype": "File",
-				"file_name": filename,
-				"attached_to_doctype": "Website Settings",
-				"attached_to_name": "Website Settings",
-				"decode": True})
+			_file = frappe.get_doc(
+				{
+					"doctype": "File",
+					"file_name": filename,
+					"attached_to_doctype": "Website Settings",
+					"attached_to_name": "Website Settings",
+					"decode": True,
+				}
+			)
 			_file.save()
 			fileurl = _file.file_url
-			frappe.db.set_value("Website Settings", "Website Settings", "brand_html",
-				"<img src='{0}' style='max-width: 40px; max-height: 25px;'> {1}".format(fileurl, args.get("company_name")))
+			frappe.db.set_value(
+				"Website Settings",
+				"Website Settings",
+				"brand_html",
+				"<img src='{0}' style='max-width: 40px; max-height: 25px;'> {1}".format(
+					fileurl, args.get("company_name")
+				),
+			)
+
 
 def create_website(args):
 	website_maker(args)
 
+
 def get_fy_details(fy_start_date, fy_end_date):
 	start_year = getdate(fy_start_date).year
 	if start_year == getdate(fy_end_date).year:
 		fy = cstr(start_year)
 	else:
-		fy = cstr(start_year) + '-' + cstr(start_year + 1)
+		fy = cstr(start_year) + "-" + cstr(start_year + 1)
 	return fy
diff --git a/erpnext/setup/setup_wizard/operations/default_website.py b/erpnext/setup/setup_wizard/operations/default_website.py
index c11910b..40b02b3 100644
--- a/erpnext/setup/setup_wizard/operations/default_website.py
+++ b/erpnext/setup/setup_wizard/operations/default_website.py
@@ -12,14 +12,14 @@
 		self.args = args
 		self.company = args.company_name
 		self.tagline = args.company_tagline
-		self.user = args.get('email')
+		self.user = args.get("email")
 		self.make_web_page()
 		self.make_website_settings()
 		self.make_blog()
 
 	def make_web_page(self):
 		# home page
-		homepage = frappe.get_doc('Homepage', 'Homepage')
+		homepage = frappe.get_doc("Homepage", "Homepage")
 		homepage.company = self.company
 		homepage.tag_line = self.tagline
 		homepage.setup_items()
@@ -28,34 +28,25 @@
 	def make_website_settings(self):
 		# update in home page in settings
 		website_settings = frappe.get_doc("Website Settings", "Website Settings")
-		website_settings.home_page = 'home'
+		website_settings.home_page = "home"
 		website_settings.brand_html = self.company
 		website_settings.copyright = self.company
 		website_settings.top_bar_items = []
-		website_settings.append("top_bar_items", {
-			"doctype": "Top Bar Item",
-			"label":"Contact",
-			"url": "/contact"
-		})
-		website_settings.append("top_bar_items", {
-			"doctype": "Top Bar Item",
-			"label":"Blog",
-			"url": "/blog"
-		})
-		website_settings.append("top_bar_items", {
-			"doctype": "Top Bar Item",
-			"label": _("Products"),
-			"url": "/all-products"
-		})
+		website_settings.append(
+			"top_bar_items", {"doctype": "Top Bar Item", "label": "Contact", "url": "/contact"}
+		)
+		website_settings.append(
+			"top_bar_items", {"doctype": "Top Bar Item", "label": "Blog", "url": "/blog"}
+		)
+		website_settings.append(
+			"top_bar_items", {"doctype": "Top Bar Item", "label": _("Products"), "url": "/all-products"}
+		)
 		website_settings.save()
 
 	def make_blog(self):
-		blog_category = frappe.get_doc({
-			"doctype": "Blog Category",
-			"category_name": "general",
-			"published": 1,
-			"title": _("General")
-		}).insert()
+		blog_category = frappe.get_doc(
+			{"doctype": "Blog Category", "category_name": "general", "published": 1, "title": _("General")}
+		).insert()
 
 		if not self.user:
 			# Admin setup
@@ -69,21 +60,30 @@
 		blogger.avatar = user.user_image
 		blogger.insert()
 
-		frappe.get_doc({
-			"doctype": "Blog Post",
-			"title": "Welcome",
-			"published": 1,
-			"published_on": nowdate(),
-			"blogger": blogger.name,
-			"blog_category": blog_category.name,
-			"blog_intro": "My First Blog",
-			"content": frappe.get_template("setup/setup_wizard/data/sample_blog_post.html").render(),
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Blog Post",
+				"title": "Welcome",
+				"published": 1,
+				"published_on": nowdate(),
+				"blogger": blogger.name,
+				"blog_category": blog_category.name,
+				"blog_intro": "My First Blog",
+				"content": frappe.get_template("setup/setup_wizard/data/sample_blog_post.html").render(),
+			}
+		).insert()
+
 
 def test():
 	frappe.delete_doc("Web Page", "test-company")
 	frappe.delete_doc("Blog Post", "welcome")
 	frappe.delete_doc("Blogger", "administrator")
 	frappe.delete_doc("Blog Category", "general")
-	website_maker({'company':"Test Company", 'company_tagline': "Better Tools for Everyone", 'name': "Administrator"})
+	website_maker(
+		{
+			"company": "Test Company",
+			"company_tagline": "Better Tools for Everyone",
+			"name": "Administrator",
+		}
+	)
 	frappe.db.commit()
diff --git a/erpnext/setup/setup_wizard/operations/defaults_setup.py b/erpnext/setup/setup_wizard/operations/defaults_setup.py
index ca1f57e..ea077986 100644
--- a/erpnext/setup/setup_wizard/operations/defaults_setup.py
+++ b/erpnext/setup/setup_wizard/operations/defaults_setup.py
@@ -5,17 +5,20 @@
 from frappe import _
 from frappe.utils import cstr, getdate
 
+
 def set_default_settings(args):
 	# enable default currency
 	frappe.db.set_value("Currency", args.get("currency"), "enabled", 1)
 
 	global_defaults = frappe.get_doc("Global Defaults", "Global Defaults")
-	global_defaults.update({
-		'current_fiscal_year': get_fy_details(args.get('fy_start_date'), args.get('fy_end_date')),
-		'default_currency': args.get('currency'),
-		'default_company':args.get('company_name')	,
-		"country": args.get("country"),
-	})
+	global_defaults.update(
+		{
+			"current_fiscal_year": get_fy_details(args.get("fy_start_date"), args.get("fy_end_date")),
+			"default_currency": args.get("currency"),
+			"default_company": args.get("company_name"),
+			"country": args.get("country"),
+		}
+	)
 
 	global_defaults.save()
 
@@ -23,13 +26,12 @@
 	system_settings.email_footer_address = args.get("company_name")
 	system_settings.save()
 
-	domain_settings = frappe.get_single('Domain Settings')
-	domain_settings.set_active_domains(args.get('domains'))
-
 	stock_settings = frappe.get_doc("Stock Settings")
 	stock_settings.item_naming_by = "Item Code"
 	stock_settings.valuation_method = "FIFO"
-	stock_settings.default_warehouse = frappe.db.get_value('Warehouse', {'warehouse_name': _('Stores')})
+	stock_settings.default_warehouse = frappe.db.get_value(
+		"Warehouse", {"warehouse_name": _("Stores")}
+	)
 	stock_settings.stock_uom = _("Nos")
 	stock_settings.auto_indent = 1
 	stock_settings.auto_insert_price_list_rate_if_missing = 1
@@ -72,61 +74,74 @@
 	hr_settings.exit_questionnaire_notification_template = _("Exit Questionnaire Notification")
 	hr_settings.save()
 
+
 def set_no_copy_fields_in_variant_settings():
 	# set no copy fields of an item doctype to item variant settings
-	doc = frappe.get_doc('Item Variant Settings')
+	doc = frappe.get_doc("Item Variant Settings")
 	doc.set_default_fields()
 	doc.save()
 
+
 def create_price_lists(args):
 	for pl_type, pl_name in (("Selling", _("Standard Selling")), ("Buying", _("Standard Buying"))):
-		frappe.get_doc({
-			"doctype": "Price List",
-			"price_list_name": pl_name,
-			"enabled": 1,
-			"buying": 1 if pl_type == "Buying" else 0,
-			"selling": 1 if pl_type == "Selling" else 0,
-			"currency": args["currency"]
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Price List",
+				"price_list_name": pl_name,
+				"enabled": 1,
+				"buying": 1 if pl_type == "Buying" else 0,
+				"selling": 1 if pl_type == "Selling" else 0,
+				"currency": args["currency"],
+			}
+		).insert()
+
 
 def create_employee_for_self(args):
-	if frappe.session.user == 'Administrator':
+	if frappe.session.user == "Administrator":
 		return
 
 	# create employee for self
-	emp = frappe.get_doc({
-		"doctype": "Employee",
-		"employee_name": " ".join(filter(None, [args.get("first_name"), args.get("last_name")])),
-		"user_id": frappe.session.user,
-		"status": "Active",
-		"company": args.get("company_name")
-	})
+	emp = frappe.get_doc(
+		{
+			"doctype": "Employee",
+			"employee_name": " ".join(filter(None, [args.get("first_name"), args.get("last_name")])),
+			"user_id": frappe.session.user,
+			"status": "Active",
+			"company": args.get("company_name"),
+		}
+	)
 	emp.flags.ignore_mandatory = True
-	emp.insert(ignore_permissions = True)
+	emp.insert(ignore_permissions=True)
+
 
 def create_territories():
 	"""create two default territories, one for home country and one named Rest of the World"""
 	from frappe.utils.nestedset import get_root_of
+
 	country = frappe.db.get_default("country")
 	root_territory = get_root_of("Territory")
 
 	for name in (country, _("Rest Of The World")):
 		if name and not frappe.db.exists("Territory", name):
-			frappe.get_doc({
-				"doctype": "Territory",
-				"territory_name": name.replace("'", ""),
-				"parent_territory": root_territory,
-				"is_group": "No"
-			}).insert()
+			frappe.get_doc(
+				{
+					"doctype": "Territory",
+					"territory_name": name.replace("'", ""),
+					"parent_territory": root_territory,
+					"is_group": "No",
+				}
+			).insert()
+
 
 def create_feed_and_todo():
 	"""update Activity feed and create todo for creation of item, customer, vendor"""
 	return
 
+
 def get_fy_details(fy_start_date, fy_end_date):
 	start_year = getdate(fy_start_date).year
 	if start_year == getdate(fy_end_date).year:
 		fy = cstr(start_year)
 	else:
-		fy = cstr(start_year) + '-' + cstr(start_year + 1)
+		fy = cstr(start_year) + "-" + cstr(start_year + 1)
 	return fy
diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py
index cefa0f3..4235e1f 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -17,195 +17,363 @@
 from erpnext.accounts.doctype.account.account import RootNotEditable
 from erpnext.regional.address_template.setup import set_up_address_templates
 
-default_lead_sources = ["Existing Customer", "Reference", "Advertisement",
-	"Cold Calling", "Exhibition", "Supplier Reference", "Mass Mailing",
-	"Customer's Vendor", "Campaign", "Walk In"]
+default_lead_sources = [
+	"Existing Customer",
+	"Reference",
+	"Advertisement",
+	"Cold Calling",
+	"Exhibition",
+	"Supplier Reference",
+	"Mass Mailing",
+	"Customer's Vendor",
+	"Campaign",
+	"Walk In",
+]
 
-default_sales_partner_type = ["Channel Partner", "Distributor", "Dealer", "Agent",
-	"Retailer", "Implementation Partner", "Reseller"]
+default_sales_partner_type = [
+	"Channel Partner",
+	"Distributor",
+	"Dealer",
+	"Agent",
+	"Retailer",
+	"Implementation Partner",
+	"Reseller",
+]
+
 
 def install(country=None):
 	records = [
-		# domains
-		{ 'doctype': 'Domain', 'domain': 'Distribution'},
-		{ 'doctype': 'Domain', 'domain': 'Manufacturing'},
-		{ 'doctype': 'Domain', 'domain': 'Retail'},
-		{ 'doctype': 'Domain', 'domain': 'Services'},
-		{ 'doctype': 'Domain', 'domain': 'Education'},
-		{ 'doctype': 'Domain', 'domain': 'Healthcare'},
-		{ 'doctype': 'Domain', 'domain': 'Non Profit'},
-
 		# ensure at least an empty Address Template exists for this Country
-		{'doctype':"Address Template", "country": country},
-
+		{"doctype": "Address Template", "country": country},
 		# item group
-		{'doctype': 'Item Group', 'item_group_name': _('All Item Groups'),
-			'is_group': 1, 'parent_item_group': ''},
-		{'doctype': 'Item Group', 'item_group_name': _('Products'),
-			'is_group': 0, 'parent_item_group': _('All Item Groups'), "show_in_website": 1 },
-		{'doctype': 'Item Group', 'item_group_name': _('Raw Material'),
-			'is_group': 0, 'parent_item_group': _('All Item Groups') },
-		{'doctype': 'Item Group', 'item_group_name': _('Services'),
-			'is_group': 0, 'parent_item_group': _('All Item Groups') },
-		{'doctype': 'Item Group', 'item_group_name': _('Sub Assemblies'),
-			'is_group': 0, 'parent_item_group': _('All Item Groups') },
-		{'doctype': 'Item Group', 'item_group_name': _('Consumable'),
-			'is_group': 0, 'parent_item_group': _('All Item Groups') },
-
+		{
+			"doctype": "Item Group",
+			"item_group_name": _("All Item Groups"),
+			"is_group": 1,
+			"parent_item_group": "",
+		},
+		{
+			"doctype": "Item Group",
+			"item_group_name": _("Products"),
+			"is_group": 0,
+			"parent_item_group": _("All Item Groups"),
+			"show_in_website": 1,
+		},
+		{
+			"doctype": "Item Group",
+			"item_group_name": _("Raw Material"),
+			"is_group": 0,
+			"parent_item_group": _("All Item Groups"),
+		},
+		{
+			"doctype": "Item Group",
+			"item_group_name": _("Services"),
+			"is_group": 0,
+			"parent_item_group": _("All Item Groups"),
+		},
+		{
+			"doctype": "Item Group",
+			"item_group_name": _("Sub Assemblies"),
+			"is_group": 0,
+			"parent_item_group": _("All Item Groups"),
+		},
+		{
+			"doctype": "Item Group",
+			"item_group_name": _("Consumable"),
+			"is_group": 0,
+			"parent_item_group": _("All Item Groups"),
+		},
 		# salary component
-		{'doctype': 'Salary Component', 'salary_component': _('Income Tax'), 'description': _('Income Tax'), 'type': 'Deduction', 'is_income_tax_component': 1},
-		{'doctype': 'Salary Component', 'salary_component': _('Basic'), 'description': _('Basic'), 'type': 'Earning'},
-		{'doctype': 'Salary Component', 'salary_component': _('Arrear'), 'description': _('Arrear'), 'type': 'Earning'},
-		{'doctype': 'Salary Component', 'salary_component': _('Leave Encashment'), 'description': _('Leave Encashment'), 'type': 'Earning'},
-
-
+		{
+			"doctype": "Salary Component",
+			"salary_component": _("Income Tax"),
+			"description": _("Income Tax"),
+			"type": "Deduction",
+			"is_income_tax_component": 1,
+		},
+		{
+			"doctype": "Salary Component",
+			"salary_component": _("Basic"),
+			"description": _("Basic"),
+			"type": "Earning",
+		},
+		{
+			"doctype": "Salary Component",
+			"salary_component": _("Arrear"),
+			"description": _("Arrear"),
+			"type": "Earning",
+		},
+		{
+			"doctype": "Salary Component",
+			"salary_component": _("Leave Encashment"),
+			"description": _("Leave Encashment"),
+			"type": "Earning",
+		},
 		# expense claim type
-		{'doctype': 'Expense Claim Type', 'name': _('Calls'), 'expense_type': _('Calls')},
-		{'doctype': 'Expense Claim Type', 'name': _('Food'), 'expense_type': _('Food')},
-		{'doctype': 'Expense Claim Type', 'name': _('Medical'), 'expense_type': _('Medical')},
-		{'doctype': 'Expense Claim Type', 'name': _('Others'), 'expense_type': _('Others')},
-		{'doctype': 'Expense Claim Type', 'name': _('Travel'), 'expense_type': _('Travel')},
-
+		{"doctype": "Expense Claim Type", "name": _("Calls"), "expense_type": _("Calls")},
+		{"doctype": "Expense Claim Type", "name": _("Food"), "expense_type": _("Food")},
+		{"doctype": "Expense Claim Type", "name": _("Medical"), "expense_type": _("Medical")},
+		{"doctype": "Expense Claim Type", "name": _("Others"), "expense_type": _("Others")},
+		{"doctype": "Expense Claim Type", "name": _("Travel"), "expense_type": _("Travel")},
 		# leave type
-		{'doctype': 'Leave Type', 'leave_type_name': _('Casual Leave'), 'name': _('Casual Leave'),
-			'allow_encashment': 1, 'is_carry_forward': 1, 'max_continuous_days_allowed': '3', 'include_holiday': 1},
-		{'doctype': 'Leave Type', 'leave_type_name': _('Compensatory Off'), 'name': _('Compensatory Off'),
-			'allow_encashment': 0, 'is_carry_forward': 0, 'include_holiday': 1, 'is_compensatory':1 },
-		{'doctype': 'Leave Type', 'leave_type_name': _('Sick Leave'), 'name': _('Sick Leave'),
-			'allow_encashment': 0, 'is_carry_forward': 0, 'include_holiday': 1},
-		{'doctype': 'Leave Type', 'leave_type_name': _('Privilege Leave'), 'name': _('Privilege Leave'),
-			'allow_encashment': 0, 'is_carry_forward': 0, 'include_holiday': 1},
-		{'doctype': 'Leave Type', 'leave_type_name': _('Leave Without Pay'), 'name': _('Leave Without Pay'),
-			'allow_encashment': 0, 'is_carry_forward': 0, 'is_lwp':1, 'include_holiday': 1},
-
+		{
+			"doctype": "Leave Type",
+			"leave_type_name": _("Casual Leave"),
+			"name": _("Casual Leave"),
+			"allow_encashment": 1,
+			"is_carry_forward": 1,
+			"max_continuous_days_allowed": "3",
+			"include_holiday": 1,
+		},
+		{
+			"doctype": "Leave Type",
+			"leave_type_name": _("Compensatory Off"),
+			"name": _("Compensatory Off"),
+			"allow_encashment": 0,
+			"is_carry_forward": 0,
+			"include_holiday": 1,
+			"is_compensatory": 1,
+		},
+		{
+			"doctype": "Leave Type",
+			"leave_type_name": _("Sick Leave"),
+			"name": _("Sick Leave"),
+			"allow_encashment": 0,
+			"is_carry_forward": 0,
+			"include_holiday": 1,
+		},
+		{
+			"doctype": "Leave Type",
+			"leave_type_name": _("Privilege Leave"),
+			"name": _("Privilege Leave"),
+			"allow_encashment": 0,
+			"is_carry_forward": 0,
+			"include_holiday": 1,
+		},
+		{
+			"doctype": "Leave Type",
+			"leave_type_name": _("Leave Without Pay"),
+			"name": _("Leave Without Pay"),
+			"allow_encashment": 0,
+			"is_carry_forward": 0,
+			"is_lwp": 1,
+			"include_holiday": 1,
+		},
 		# Employment Type
-		{'doctype': 'Employment Type', 'employee_type_name': _('Full-time')},
-		{'doctype': 'Employment Type', 'employee_type_name': _('Part-time')},
-		{'doctype': 'Employment Type', 'employee_type_name': _('Probation')},
-		{'doctype': 'Employment Type', 'employee_type_name': _('Contract')},
-		{'doctype': 'Employment Type', 'employee_type_name': _('Commission')},
-		{'doctype': 'Employment Type', 'employee_type_name': _('Piecework')},
-		{'doctype': 'Employment Type', 'employee_type_name': _('Intern')},
-		{'doctype': 'Employment Type', 'employee_type_name': _('Apprentice')},
-
-
+		{"doctype": "Employment Type", "employee_type_name": _("Full-time")},
+		{"doctype": "Employment Type", "employee_type_name": _("Part-time")},
+		{"doctype": "Employment Type", "employee_type_name": _("Probation")},
+		{"doctype": "Employment Type", "employee_type_name": _("Contract")},
+		{"doctype": "Employment Type", "employee_type_name": _("Commission")},
+		{"doctype": "Employment Type", "employee_type_name": _("Piecework")},
+		{"doctype": "Employment Type", "employee_type_name": _("Intern")},
+		{"doctype": "Employment Type", "employee_type_name": _("Apprentice")},
 		# Stock Entry Type
-		{'doctype': 'Stock Entry Type', 'name': 'Material Issue', 'purpose': 'Material Issue'},
-		{'doctype': 'Stock Entry Type', 'name': 'Material Receipt', 'purpose': 'Material Receipt'},
-		{'doctype': 'Stock Entry Type', 'name': 'Material Transfer', 'purpose': 'Material Transfer'},
-		{'doctype': 'Stock Entry Type', 'name': 'Manufacture', 'purpose': 'Manufacture'},
-		{'doctype': 'Stock Entry Type', 'name': 'Repack', 'purpose': 'Repack'},
-		{'doctype': 'Stock Entry Type', 'name': 'Send to Subcontractor', 'purpose': 'Send to Subcontractor'},
-		{'doctype': 'Stock Entry Type', 'name': 'Material Transfer for Manufacture', 'purpose': 'Material Transfer for Manufacture'},
-		{'doctype': 'Stock Entry Type', 'name': 'Material Consumption for Manufacture', 'purpose': 'Material Consumption for Manufacture'},
-
+		{"doctype": "Stock Entry Type", "name": "Material Issue", "purpose": "Material Issue"},
+		{"doctype": "Stock Entry Type", "name": "Material Receipt", "purpose": "Material Receipt"},
+		{"doctype": "Stock Entry Type", "name": "Material Transfer", "purpose": "Material Transfer"},
+		{"doctype": "Stock Entry Type", "name": "Manufacture", "purpose": "Manufacture"},
+		{"doctype": "Stock Entry Type", "name": "Repack", "purpose": "Repack"},
+		{
+			"doctype": "Stock Entry Type",
+			"name": "Send to Subcontractor",
+			"purpose": "Send to Subcontractor",
+		},
+		{
+			"doctype": "Stock Entry Type",
+			"name": "Material Transfer for Manufacture",
+			"purpose": "Material Transfer for Manufacture",
+		},
+		{
+			"doctype": "Stock Entry Type",
+			"name": "Material Consumption for Manufacture",
+			"purpose": "Material Consumption for Manufacture",
+		},
 		# Designation
-		{'doctype': 'Designation', 'designation_name': _('CEO')},
-		{'doctype': 'Designation', 'designation_name': _('Manager')},
-		{'doctype': 'Designation', 'designation_name': _('Analyst')},
-		{'doctype': 'Designation', 'designation_name': _('Engineer')},
-		{'doctype': 'Designation', 'designation_name': _('Accountant')},
-		{'doctype': 'Designation', 'designation_name': _('Secretary')},
-		{'doctype': 'Designation', 'designation_name': _('Associate')},
-		{'doctype': 'Designation', 'designation_name': _('Administrative Officer')},
-		{'doctype': 'Designation', 'designation_name': _('Business Development Manager')},
-		{'doctype': 'Designation', 'designation_name': _('HR Manager')},
-		{'doctype': 'Designation', 'designation_name': _('Project Manager')},
-		{'doctype': 'Designation', 'designation_name': _('Head of Marketing and Sales')},
-		{'doctype': 'Designation', 'designation_name': _('Software Developer')},
-		{'doctype': 'Designation', 'designation_name': _('Designer')},
-		{'doctype': 'Designation', 'designation_name': _('Researcher')},
-
+		{"doctype": "Designation", "designation_name": _("CEO")},
+		{"doctype": "Designation", "designation_name": _("Manager")},
+		{"doctype": "Designation", "designation_name": _("Analyst")},
+		{"doctype": "Designation", "designation_name": _("Engineer")},
+		{"doctype": "Designation", "designation_name": _("Accountant")},
+		{"doctype": "Designation", "designation_name": _("Secretary")},
+		{"doctype": "Designation", "designation_name": _("Associate")},
+		{"doctype": "Designation", "designation_name": _("Administrative Officer")},
+		{"doctype": "Designation", "designation_name": _("Business Development Manager")},
+		{"doctype": "Designation", "designation_name": _("HR Manager")},
+		{"doctype": "Designation", "designation_name": _("Project Manager")},
+		{"doctype": "Designation", "designation_name": _("Head of Marketing and Sales")},
+		{"doctype": "Designation", "designation_name": _("Software Developer")},
+		{"doctype": "Designation", "designation_name": _("Designer")},
+		{"doctype": "Designation", "designation_name": _("Researcher")},
 		# territory: with two default territories, one for home country and one named Rest of the World
-		{'doctype': 'Territory', 'territory_name': _('All Territories'), 'is_group': 1, 'name': _('All Territories'), 'parent_territory': ''},
-		{'doctype': 'Territory', 'territory_name': country.replace("'", ""), 'is_group': 0, 'parent_territory': _('All Territories')},
-		{'doctype': 'Territory', 'territory_name': _("Rest Of The World"), 'is_group': 0, 'parent_territory': _('All Territories')},
-
+		{
+			"doctype": "Territory",
+			"territory_name": _("All Territories"),
+			"is_group": 1,
+			"name": _("All Territories"),
+			"parent_territory": "",
+		},
+		{
+			"doctype": "Territory",
+			"territory_name": country.replace("'", ""),
+			"is_group": 0,
+			"parent_territory": _("All Territories"),
+		},
+		{
+			"doctype": "Territory",
+			"territory_name": _("Rest Of The World"),
+			"is_group": 0,
+			"parent_territory": _("All Territories"),
+		},
 		# customer group
-		{'doctype': 'Customer Group', 'customer_group_name': _('All Customer Groups'), 'is_group': 1, 	'name': _('All Customer Groups'), 'parent_customer_group': ''},
-		{'doctype': 'Customer Group', 'customer_group_name': _('Individual'), 'is_group': 0, 'parent_customer_group': _('All Customer Groups')},
-		{'doctype': 'Customer Group', 'customer_group_name': _('Commercial'), 'is_group': 0, 'parent_customer_group': _('All Customer Groups')},
-		{'doctype': 'Customer Group', 'customer_group_name': _('Non Profit'), 'is_group': 0, 'parent_customer_group': _('All Customer Groups')},
-		{'doctype': 'Customer Group', 'customer_group_name': _('Government'), 'is_group': 0, 'parent_customer_group': _('All Customer Groups')},
-
+		{
+			"doctype": "Customer Group",
+			"customer_group_name": _("All Customer Groups"),
+			"is_group": 1,
+			"name": _("All Customer Groups"),
+			"parent_customer_group": "",
+		},
+		{
+			"doctype": "Customer Group",
+			"customer_group_name": _("Individual"),
+			"is_group": 0,
+			"parent_customer_group": _("All Customer Groups"),
+		},
+		{
+			"doctype": "Customer Group",
+			"customer_group_name": _("Commercial"),
+			"is_group": 0,
+			"parent_customer_group": _("All Customer Groups"),
+		},
+		{
+			"doctype": "Customer Group",
+			"customer_group_name": _("Non Profit"),
+			"is_group": 0,
+			"parent_customer_group": _("All Customer Groups"),
+		},
+		{
+			"doctype": "Customer Group",
+			"customer_group_name": _("Government"),
+			"is_group": 0,
+			"parent_customer_group": _("All Customer Groups"),
+		},
 		# supplier group
-		{'doctype': 'Supplier Group', 'supplier_group_name': _('All Supplier Groups'), 'is_group': 1, 'name': _('All Supplier Groups'), 'parent_supplier_group': ''},
-		{'doctype': 'Supplier Group', 'supplier_group_name': _('Services'), 'is_group': 0, 'parent_supplier_group': _('All Supplier Groups')},
-		{'doctype': 'Supplier Group', 'supplier_group_name': _('Local'), 'is_group': 0, 'parent_supplier_group': _('All Supplier Groups')},
-		{'doctype': 'Supplier Group', 'supplier_group_name': _('Raw Material'), 'is_group': 0, 'parent_supplier_group': _('All Supplier Groups')},
-		{'doctype': 'Supplier Group', 'supplier_group_name': _('Electrical'), 'is_group': 0, 'parent_supplier_group': _('All Supplier Groups')},
-		{'doctype': 'Supplier Group', 'supplier_group_name': _('Hardware'), 'is_group': 0, 'parent_supplier_group': _('All Supplier Groups')},
-		{'doctype': 'Supplier Group', 'supplier_group_name': _('Pharmaceutical'), 'is_group': 0, 'parent_supplier_group': _('All Supplier Groups')},
-		{'doctype': 'Supplier Group', 'supplier_group_name': _('Distributor'), 'is_group': 0, 'parent_supplier_group': _('All Supplier Groups')},
-
+		{
+			"doctype": "Supplier Group",
+			"supplier_group_name": _("All Supplier Groups"),
+			"is_group": 1,
+			"name": _("All Supplier Groups"),
+			"parent_supplier_group": "",
+		},
+		{
+			"doctype": "Supplier Group",
+			"supplier_group_name": _("Services"),
+			"is_group": 0,
+			"parent_supplier_group": _("All Supplier Groups"),
+		},
+		{
+			"doctype": "Supplier Group",
+			"supplier_group_name": _("Local"),
+			"is_group": 0,
+			"parent_supplier_group": _("All Supplier Groups"),
+		},
+		{
+			"doctype": "Supplier Group",
+			"supplier_group_name": _("Raw Material"),
+			"is_group": 0,
+			"parent_supplier_group": _("All Supplier Groups"),
+		},
+		{
+			"doctype": "Supplier Group",
+			"supplier_group_name": _("Electrical"),
+			"is_group": 0,
+			"parent_supplier_group": _("All Supplier Groups"),
+		},
+		{
+			"doctype": "Supplier Group",
+			"supplier_group_name": _("Hardware"),
+			"is_group": 0,
+			"parent_supplier_group": _("All Supplier Groups"),
+		},
+		{
+			"doctype": "Supplier Group",
+			"supplier_group_name": _("Pharmaceutical"),
+			"is_group": 0,
+			"parent_supplier_group": _("All Supplier Groups"),
+		},
+		{
+			"doctype": "Supplier Group",
+			"supplier_group_name": _("Distributor"),
+			"is_group": 0,
+			"parent_supplier_group": _("All Supplier Groups"),
+		},
 		# Sales Person
-		{'doctype': 'Sales Person', 'sales_person_name': _('Sales Team'), 'is_group': 1, "parent_sales_person": ""},
-
+		{
+			"doctype": "Sales Person",
+			"sales_person_name": _("Sales Team"),
+			"is_group": 1,
+			"parent_sales_person": "",
+		},
 		# Mode of Payment
-		{'doctype': 'Mode of Payment',
-			'mode_of_payment': 'Check' if country=="United States" else _('Cheque'),
-			'type': 'Bank'},
-		{'doctype': 'Mode of Payment', 'mode_of_payment': _('Cash'),
-			'type': 'Cash'},
-		{'doctype': 'Mode of Payment', 'mode_of_payment': _('Credit Card'),
-			'type': 'Bank'},
-		{'doctype': 'Mode of Payment', 'mode_of_payment': _('Wire Transfer'),
-			'type': 'Bank'},
-		{'doctype': 'Mode of Payment', 'mode_of_payment': _('Bank Draft'),
-			'type': 'Bank'},
-
+		{
+			"doctype": "Mode of Payment",
+			"mode_of_payment": "Check" if country == "United States" else _("Cheque"),
+			"type": "Bank",
+		},
+		{"doctype": "Mode of Payment", "mode_of_payment": _("Cash"), "type": "Cash"},
+		{"doctype": "Mode of Payment", "mode_of_payment": _("Credit Card"), "type": "Bank"},
+		{"doctype": "Mode of Payment", "mode_of_payment": _("Wire Transfer"), "type": "Bank"},
+		{"doctype": "Mode of Payment", "mode_of_payment": _("Bank Draft"), "type": "Bank"},
 		# Activity Type
-		{'doctype': 'Activity Type', 'activity_type': _('Planning')},
-		{'doctype': 'Activity Type', 'activity_type': _('Research')},
-		{'doctype': 'Activity Type', 'activity_type': _('Proposal Writing')},
-		{'doctype': 'Activity Type', 'activity_type': _('Execution')},
-		{'doctype': 'Activity Type', 'activity_type': _('Communication')},
-
-		{'doctype': "Item Attribute", "attribute_name": _("Size"), "item_attribute_values": [
-			{"attribute_value": _("Extra Small"), "abbr": "XS"},
-			{"attribute_value": _("Small"), "abbr": "S"},
-			{"attribute_value": _("Medium"), "abbr": "M"},
-			{"attribute_value": _("Large"), "abbr": "L"},
-			{"attribute_value": _("Extra Large"), "abbr": "XL"}
-		]},
-
-		{'doctype': "Item Attribute", "attribute_name": _("Colour"), "item_attribute_values": [
-			{"attribute_value": _("Red"), "abbr": "RED"},
-			{"attribute_value": _("Green"), "abbr": "GRE"},
-			{"attribute_value": _("Blue"), "abbr": "BLU"},
-			{"attribute_value": _("Black"), "abbr": "BLA"},
-			{"attribute_value": _("White"), "abbr": "WHI"}
-		]},
-
+		{"doctype": "Activity Type", "activity_type": _("Planning")},
+		{"doctype": "Activity Type", "activity_type": _("Research")},
+		{"doctype": "Activity Type", "activity_type": _("Proposal Writing")},
+		{"doctype": "Activity Type", "activity_type": _("Execution")},
+		{"doctype": "Activity Type", "activity_type": _("Communication")},
+		{
+			"doctype": "Item Attribute",
+			"attribute_name": _("Size"),
+			"item_attribute_values": [
+				{"attribute_value": _("Extra Small"), "abbr": "XS"},
+				{"attribute_value": _("Small"), "abbr": "S"},
+				{"attribute_value": _("Medium"), "abbr": "M"},
+				{"attribute_value": _("Large"), "abbr": "L"},
+				{"attribute_value": _("Extra Large"), "abbr": "XL"},
+			],
+		},
+		{
+			"doctype": "Item Attribute",
+			"attribute_name": _("Colour"),
+			"item_attribute_values": [
+				{"attribute_value": _("Red"), "abbr": "RED"},
+				{"attribute_value": _("Green"), "abbr": "GRE"},
+				{"attribute_value": _("Blue"), "abbr": "BLU"},
+				{"attribute_value": _("Black"), "abbr": "BLA"},
+				{"attribute_value": _("White"), "abbr": "WHI"},
+			],
+		},
 		# Issue Priority
-		{'doctype': 'Issue Priority', 'name': _('Low')},
-		{'doctype': 'Issue Priority', 'name': _('Medium')},
-		{'doctype': 'Issue Priority', 'name': _('High')},
-
-		#Job Applicant Source
-		{'doctype': 'Job Applicant Source', 'source_name': _('Website Listing')},
-		{'doctype': 'Job Applicant Source', 'source_name': _('Walk In')},
-		{'doctype': 'Job Applicant Source', 'source_name': _('Employee Referral')},
-		{'doctype': 'Job Applicant Source', 'source_name': _('Campaign')},
-
-		{'doctype': "Email Account", "email_id": "sales@example.com", "append_to": "Opportunity"},
-		{'doctype': "Email Account", "email_id": "support@example.com", "append_to": "Issue"},
-		{'doctype': "Email Account", "email_id": "jobs@example.com", "append_to": "Job Applicant"},
-
-		{'doctype': "Party Type", "party_type": "Customer", "account_type": "Receivable"},
-		{'doctype': "Party Type", "party_type": "Supplier", "account_type": "Payable"},
-		{'doctype': "Party Type", "party_type": "Employee", "account_type": "Payable"},
-		{'doctype': "Party Type", "party_type": "Shareholder", "account_type": "Payable"},
-		{'doctype': "Party Type", "party_type": "Student", "account_type": "Receivable"},
-
-		{'doctype': "Opportunity Type", "name": _("Sales")},
-		{'doctype': "Opportunity Type", "name": _("Support")},
-		{'doctype': "Opportunity Type", "name": _("Maintenance")},
-
-		{'doctype': "Project Type", "project_type": "Internal"},
-		{'doctype': "Project Type", "project_type": "External"},
-		{'doctype': "Project Type", "project_type": "Other"},
-
+		{"doctype": "Issue Priority", "name": _("Low")},
+		{"doctype": "Issue Priority", "name": _("Medium")},
+		{"doctype": "Issue Priority", "name": _("High")},
+		# Job Applicant Source
+		{"doctype": "Job Applicant Source", "source_name": _("Website Listing")},
+		{"doctype": "Job Applicant Source", "source_name": _("Walk In")},
+		{"doctype": "Job Applicant Source", "source_name": _("Employee Referral")},
+		{"doctype": "Job Applicant Source", "source_name": _("Campaign")},
+		{"doctype": "Email Account", "email_id": "sales@example.com", "append_to": "Opportunity"},
+		{"doctype": "Email Account", "email_id": "support@example.com", "append_to": "Issue"},
+		{"doctype": "Email Account", "email_id": "jobs@example.com", "append_to": "Job Applicant"},
+		{"doctype": "Party Type", "party_type": "Customer", "account_type": "Receivable"},
+		{"doctype": "Party Type", "party_type": "Supplier", "account_type": "Payable"},
+		{"doctype": "Party Type", "party_type": "Employee", "account_type": "Payable"},
+		{"doctype": "Party Type", "party_type": "Shareholder", "account_type": "Payable"},
+		{"doctype": "Opportunity Type", "name": _("Sales")},
+		{"doctype": "Opportunity Type", "name": _("Support")},
+		{"doctype": "Opportunity Type", "name": _("Maintenance")},
+		{"doctype": "Project Type", "project_type": "Internal"},
+		{"doctype": "Project Type", "project_type": "External"},
+		{"doctype": "Project Type", "project_type": "Other"},
 		{"doctype": "Offer Term", "offer_term": _("Date of Joining")},
 		{"doctype": "Offer Term", "offer_term": _("Annual Salary")},
 		{"doctype": "Offer Term", "offer_term": _("Probationary Period")},
@@ -218,23 +386,15 @@
 		{"doctype": "Offer Term", "offer_term": _("Leaves per Year")},
 		{"doctype": "Offer Term", "offer_term": _("Notice Period")},
 		{"doctype": "Offer Term", "offer_term": _("Incentives")},
-
-		{'doctype': "Print Heading", 'print_heading': _("Credit Note")},
-		{'doctype': "Print Heading", 'print_heading': _("Debit Note")},
-
-		# Assessment Group
-		{'doctype': 'Assessment Group', 'assessment_group_name': _('All Assessment Groups'),
-			'is_group': 1, 'parent_assessment_group': ''},
-
+		{"doctype": "Print Heading", "print_heading": _("Credit Note")},
+		{"doctype": "Print Heading", "print_heading": _("Debit Note")},
 		# Share Management
 		{"doctype": "Share Type", "title": _("Equity")},
 		{"doctype": "Share Type", "title": _("Preference")},
-
 		# Market Segments
 		{"doctype": "Market Segment", "market_segment": _("Lower Income")},
 		{"doctype": "Market Segment", "market_segment": _("Middle Income")},
 		{"doctype": "Market Segment", "market_segment": _("Upper Income")},
-
 		# Sales Stages
 		{"doctype": "Sales Stage", "stage_name": _("Prospecting")},
 		{"doctype": "Sales Stage", "stage_name": _("Qualification")},
@@ -244,47 +404,101 @@
 		{"doctype": "Sales Stage", "stage_name": _("Perception Analysis")},
 		{"doctype": "Sales Stage", "stage_name": _("Proposal/Price Quote")},
 		{"doctype": "Sales Stage", "stage_name": _("Negotiation/Review")},
-
 		# Warehouse Type
-		{'doctype': 'Warehouse Type', 'name': 'Transit'},
+		{"doctype": "Warehouse Type", "name": "Transit"},
 	]
 
 	from erpnext.setup.setup_wizard.data.industry_type import get_industry_types
-	records += [{"doctype":"Industry Type", "industry": d} for d in get_industry_types()]
-	# records += [{"doctype":"Operation", "operation": d} for d in get_operations()]
-	records += [{'doctype': 'Lead Source', 'source_name': _(d)} for d in default_lead_sources]
 
-	records += [{'doctype': 'Sales Partner Type', 'sales_partner_type': _(d)} for d in default_sales_partner_type]
+	records += [{"doctype": "Industry Type", "industry": d} for d in get_industry_types()]
+	# records += [{"doctype":"Operation", "operation": d} for d in get_operations()]
+	records += [{"doctype": "Lead Source", "source_name": _(d)} for d in default_lead_sources]
+
+	records += [
+		{"doctype": "Sales Partner Type", "sales_partner_type": _(d)} for d in default_sales_partner_type
+	]
 
 	base_path = frappe.get_app_path("erpnext", "hr", "doctype")
-	response = frappe.read_file(os.path.join(base_path, "leave_application/leave_application_email_template.html"))
+	response = frappe.read_file(
+		os.path.join(base_path, "leave_application/leave_application_email_template.html")
+	)
 
-	records += [{'doctype': 'Email Template', 'name': _("Leave Approval Notification"), 'response': response,
-		'subject': _("Leave Approval Notification"), 'owner': frappe.session.user}]
+	records += [
+		{
+			"doctype": "Email Template",
+			"name": _("Leave Approval Notification"),
+			"response": response,
+			"subject": _("Leave Approval Notification"),
+			"owner": frappe.session.user,
+		}
+	]
 
-	records += [{'doctype': 'Email Template', 'name': _("Leave Status Notification"), 'response': response,
-		'subject': _("Leave Status Notification"), 'owner': frappe.session.user}]
+	records += [
+		{
+			"doctype": "Email Template",
+			"name": _("Leave Status Notification"),
+			"response": response,
+			"subject": _("Leave Status Notification"),
+			"owner": frappe.session.user,
+		}
+	]
 
-	response = frappe.read_file(os.path.join(base_path, "interview/interview_reminder_notification_template.html"))
+	response = frappe.read_file(
+		os.path.join(base_path, "interview/interview_reminder_notification_template.html")
+	)
 
-	records += [{'doctype': 'Email Template', 'name': _('Interview Reminder'), 'response': response,
-		'subject': _('Interview Reminder'), 'owner': frappe.session.user}]
+	records += [
+		{
+			"doctype": "Email Template",
+			"name": _("Interview Reminder"),
+			"response": response,
+			"subject": _("Interview Reminder"),
+			"owner": frappe.session.user,
+		}
+	]
 
-	response = frappe.read_file(os.path.join(base_path, "interview/interview_feedback_reminder_template.html"))
+	response = frappe.read_file(
+		os.path.join(base_path, "interview/interview_feedback_reminder_template.html")
+	)
 
-	records += [{'doctype': 'Email Template', 'name': _('Interview Feedback Reminder'), 'response': response,
-		'subject': _('Interview Feedback Reminder'), 'owner': frappe.session.user}]
+	records += [
+		{
+			"doctype": "Email Template",
+			"name": _("Interview Feedback Reminder"),
+			"response": response,
+			"subject": _("Interview Feedback Reminder"),
+			"owner": frappe.session.user,
+		}
+	]
 
-	response = frappe.read_file(os.path.join(base_path, 'exit_interview/exit_questionnaire_notification_template.html'))
+	response = frappe.read_file(
+		os.path.join(base_path, "exit_interview/exit_questionnaire_notification_template.html")
+	)
 
-	records += [{'doctype': 'Email Template', 'name': _('Exit Questionnaire Notification'), 'response': response,
-		'subject': _('Exit Questionnaire Notification'), 'owner': frappe.session.user}]
+	records += [
+		{
+			"doctype": "Email Template",
+			"name": _("Exit Questionnaire Notification"),
+			"response": response,
+			"subject": _("Exit Questionnaire Notification"),
+			"owner": frappe.session.user,
+		}
+	]
 
 	base_path = frappe.get_app_path("erpnext", "stock", "doctype")
-	response = frappe.read_file(os.path.join(base_path, "delivery_trip/dispatch_notification_template.html"))
+	response = frappe.read_file(
+		os.path.join(base_path, "delivery_trip/dispatch_notification_template.html")
+	)
 
-	records += [{'doctype': 'Email Template', 'name': _("Dispatch Notification"), 'response': response,
-		'subject': _("Your order is out for delivery!"), 'owner': frappe.session.user}]
+	records += [
+		{
+			"doctype": "Email Template",
+			"name": _("Dispatch Notification"),
+			"response": response,
+			"subject": _("Your order is out for delivery!"),
+			"owner": frappe.session.user,
+		}
+	]
 
 	# Records for the Supplier Scorecard
 	from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import make_default_records
@@ -295,6 +509,7 @@
 	set_more_defaults()
 	update_global_search_doctypes()
 
+
 def set_more_defaults():
 	# Do more setup stuff that can be done here with no dependencies
 	update_selling_defaults()
@@ -303,6 +518,7 @@
 	add_uom_data()
 	update_item_variant_settings()
 
+
 def update_selling_defaults():
 	selling_settings = frappe.get_doc("Selling Settings")
 	selling_settings.cust_master_name = "Customer Name"
@@ -312,6 +528,7 @@
 	selling_settings.sales_update_frequency = "Each Transaction"
 	selling_settings.save()
 
+
 def update_buying_defaults():
 	buying_settings = frappe.get_doc("Buying Settings")
 	buying_settings.supp_master_name = "Supplier Name"
@@ -321,6 +538,7 @@
 	buying_settings.allow_multiple_items = 1
 	buying_settings.save()
 
+
 def update_hr_defaults():
 	hr_settings = frappe.get_doc("HR Settings")
 	hr_settings.emp_created_by = "Naming Series"
@@ -336,53 +554,66 @@
 
 	hr_settings.save()
 
+
 def update_item_variant_settings():
 	# set no copy fields of an item doctype to item variant settings
-	doc = frappe.get_doc('Item Variant Settings')
+	doc = frappe.get_doc("Item Variant Settings")
 	doc.set_default_fields()
 	doc.save()
 
+
 def add_uom_data():
 	# add UOMs
-	uoms = json.loads(open(frappe.get_app_path("erpnext", "setup", "setup_wizard", "data", "uom_data.json")).read())
+	uoms = json.loads(
+		open(frappe.get_app_path("erpnext", "setup", "setup_wizard", "data", "uom_data.json")).read()
+	)
 	for d in uoms:
-		if not frappe.db.exists('UOM', _(d.get("uom_name"))):
-			uom_doc = frappe.get_doc({
-				"doctype": "UOM",
-				"uom_name": _(d.get("uom_name")),
-				"name": _(d.get("uom_name")),
-				"must_be_whole_number": d.get("must_be_whole_number"),
-				"enabled": 1,
-			}).db_insert()
+		if not frappe.db.exists("UOM", _(d.get("uom_name"))):
+			uom_doc = frappe.get_doc(
+				{
+					"doctype": "UOM",
+					"uom_name": _(d.get("uom_name")),
+					"name": _(d.get("uom_name")),
+					"must_be_whole_number": d.get("must_be_whole_number"),
+					"enabled": 1,
+				}
+			).db_insert()
 
 	# bootstrap uom conversion factors
-	uom_conversions = json.loads(open(frappe.get_app_path("erpnext", "setup", "setup_wizard", "data", "uom_conversion_data.json")).read())
+	uom_conversions = json.loads(
+		open(
+			frappe.get_app_path("erpnext", "setup", "setup_wizard", "data", "uom_conversion_data.json")
+		).read()
+	)
 	for d in uom_conversions:
 		if not frappe.db.exists("UOM Category", _(d.get("category"))):
-			frappe.get_doc({
-				"doctype": "UOM Category",
-				"category_name": _(d.get("category"))
-			}).db_insert()
+			frappe.get_doc({"doctype": "UOM Category", "category_name": _(d.get("category"))}).db_insert()
 
-		if not frappe.db.exists("UOM Conversion Factor", {"from_uom": _(d.get("from_uom")), "to_uom": _(d.get("to_uom"))}):
-			uom_conversion = frappe.get_doc({
-				"doctype": "UOM Conversion Factor",
-				"category": _(d.get("category")),
-				"from_uom": _(d.get("from_uom")),
-				"to_uom": _(d.get("to_uom")),
-				"value": d.get("value")
-			}).insert(ignore_permissions=True)
+		if not frappe.db.exists(
+			"UOM Conversion Factor", {"from_uom": _(d.get("from_uom")), "to_uom": _(d.get("to_uom"))}
+		):
+			uom_conversion = frappe.get_doc(
+				{
+					"doctype": "UOM Conversion Factor",
+					"category": _(d.get("category")),
+					"from_uom": _(d.get("from_uom")),
+					"to_uom": _(d.get("to_uom")),
+					"value": d.get("value"),
+				}
+			).insert(ignore_permissions=True)
+
 
 def add_market_segments():
 	records = [
 		# Market Segments
 		{"doctype": "Market Segment", "market_segment": _("Lower Income")},
 		{"doctype": "Market Segment", "market_segment": _("Middle Income")},
-		{"doctype": "Market Segment", "market_segment": _("Upper Income")}
+		{"doctype": "Market Segment", "market_segment": _("Upper Income")},
 	]
 
 	make_records(records)
 
+
 def add_sale_stages():
 	# Sale Stages
 	records = [
@@ -393,33 +624,33 @@
 		{"doctype": "Sales Stage", "stage_name": _("Identifying Decision Makers")},
 		{"doctype": "Sales Stage", "stage_name": _("Perception Analysis")},
 		{"doctype": "Sales Stage", "stage_name": _("Proposal/Price Quote")},
-		{"doctype": "Sales Stage", "stage_name": _("Negotiation/Review")}
+		{"doctype": "Sales Stage", "stage_name": _("Negotiation/Review")},
 	]
 	for sales_stage in records:
 		frappe.get_doc(sales_stage).db_insert()
 
+
 def install_company(args):
 	records = [
 		# Fiscal Year
 		{
-			'doctype': "Fiscal Year",
-			'year': get_fy_details(args.fy_start_date, args.fy_end_date),
-			'year_start_date': args.fy_start_date,
-			'year_end_date': args.fy_end_date
+			"doctype": "Fiscal Year",
+			"year": get_fy_details(args.fy_start_date, args.fy_end_date),
+			"year_start_date": args.fy_start_date,
+			"year_end_date": args.fy_end_date,
 		},
-
 		# Company
 		{
-			"doctype":"Company",
-			'company_name': args.company_name,
-			'enable_perpetual_inventory': 1,
-			'abbr': args.company_abbr,
-			'default_currency': args.currency,
-			'country': args.country,
-			'create_chart_of_accounts_based_on': 'Standard Template',
-			'chart_of_accounts': args.chart_of_accounts,
-			'domain': args.domain
-		}
+			"doctype": "Company",
+			"company_name": args.company_name,
+			"enable_perpetual_inventory": 1,
+			"abbr": args.company_abbr,
+			"default_currency": args.currency,
+			"country": args.country,
+			"create_chart_of_accounts_based_on": "Standard Template",
+			"chart_of_accounts": args.chart_of_accounts,
+			"domain": args.domain,
+		},
 	]
 
 	make_records(records)
@@ -428,20 +659,90 @@
 def install_post_company_fixtures(args=None):
 	records = [
 		# Department
-		{'doctype': 'Department', 'department_name': _('All Departments'), 'is_group': 1, 'parent_department': ''},
-		{'doctype': 'Department', 'department_name': _('Accounts'), 'parent_department': _('All Departments'), 'company': args.company_name},
-		{'doctype': 'Department', 'department_name': _('Marketing'), 'parent_department': _('All Departments'), 'company': args.company_name},
-		{'doctype': 'Department', 'department_name': _('Sales'), 'parent_department': _('All Departments'), 'company': args.company_name},
-		{'doctype': 'Department', 'department_name': _('Purchase'), 'parent_department': _('All Departments'), 'company': args.company_name},
-		{'doctype': 'Department', 'department_name': _('Operations'), 'parent_department': _('All Departments'), 'company': args.company_name},
-		{'doctype': 'Department', 'department_name': _('Production'), 'parent_department': _('All Departments'), 'company': args.company_name},
-		{'doctype': 'Department', 'department_name': _('Dispatch'), 'parent_department': _('All Departments'), 'company': args.company_name},
-		{'doctype': 'Department', 'department_name': _('Customer Service'), 'parent_department': _('All Departments'), 'company': args.company_name},
-		{'doctype': 'Department', 'department_name': _('Human Resources'), 'parent_department': _('All Departments'), 'company': args.company_name},
-		{'doctype': 'Department', 'department_name': _('Management'), 'parent_department': _('All Departments'), 'company': args.company_name},
-		{'doctype': 'Department', 'department_name': _('Quality Management'), 'parent_department': _('All Departments'), 'company': args.company_name},
-		{'doctype': 'Department', 'department_name': _('Research & Development'), 'parent_department': _('All Departments'), 'company': args.company_name},
-		{'doctype': 'Department', 'department_name': _('Legal'), 'parent_department': _('All Departments'), 'company': args.company_name},
+		{
+			"doctype": "Department",
+			"department_name": _("All Departments"),
+			"is_group": 1,
+			"parent_department": "",
+		},
+		{
+			"doctype": "Department",
+			"department_name": _("Accounts"),
+			"parent_department": _("All Departments"),
+			"company": args.company_name,
+		},
+		{
+			"doctype": "Department",
+			"department_name": _("Marketing"),
+			"parent_department": _("All Departments"),
+			"company": args.company_name,
+		},
+		{
+			"doctype": "Department",
+			"department_name": _("Sales"),
+			"parent_department": _("All Departments"),
+			"company": args.company_name,
+		},
+		{
+			"doctype": "Department",
+			"department_name": _("Purchase"),
+			"parent_department": _("All Departments"),
+			"company": args.company_name,
+		},
+		{
+			"doctype": "Department",
+			"department_name": _("Operations"),
+			"parent_department": _("All Departments"),
+			"company": args.company_name,
+		},
+		{
+			"doctype": "Department",
+			"department_name": _("Production"),
+			"parent_department": _("All Departments"),
+			"company": args.company_name,
+		},
+		{
+			"doctype": "Department",
+			"department_name": _("Dispatch"),
+			"parent_department": _("All Departments"),
+			"company": args.company_name,
+		},
+		{
+			"doctype": "Department",
+			"department_name": _("Customer Service"),
+			"parent_department": _("All Departments"),
+			"company": args.company_name,
+		},
+		{
+			"doctype": "Department",
+			"department_name": _("Human Resources"),
+			"parent_department": _("All Departments"),
+			"company": args.company_name,
+		},
+		{
+			"doctype": "Department",
+			"department_name": _("Management"),
+			"parent_department": _("All Departments"),
+			"company": args.company_name,
+		},
+		{
+			"doctype": "Department",
+			"department_name": _("Quality Management"),
+			"parent_department": _("All Departments"),
+			"company": args.company_name,
+		},
+		{
+			"doctype": "Department",
+			"department_name": _("Research & Development"),
+			"parent_department": _("All Departments"),
+			"company": args.company_name,
+		},
+		{
+			"doctype": "Department",
+			"department_name": _("Legal"),
+			"parent_department": _("All Departments"),
+			"company": args.company_name,
+		},
 	]
 
 	# Make root department with NSM updation
@@ -456,8 +757,22 @@
 def install_defaults(args=None):
 	records = [
 		# Price Lists
-		{ "doctype": "Price List", "price_list_name": _("Standard Buying"), "enabled": 1, "buying": 1, "selling": 0, "currency": args.currency },
-		{ "doctype": "Price List", "price_list_name": _("Standard Selling"), "enabled": 1, "buying": 0, "selling": 1, "currency": args.currency },
+		{
+			"doctype": "Price List",
+			"price_list_name": _("Standard Buying"),
+			"enabled": 1,
+			"buying": 1,
+			"selling": 0,
+			"currency": args.currency,
+		},
+		{
+			"doctype": "Price List",
+			"price_list_name": _("Standard Selling"),
+			"enabled": 1,
+			"buying": 0,
+			"selling": 1,
+			"currency": args.currency,
+		},
 	]
 
 	make_records(records)
@@ -467,34 +782,36 @@
 	frappe.db.set_value("Stock Settings", None, "email_footer_address", args.get("company_name"))
 
 	set_global_defaults(args)
-	set_active_domains(args)
 	update_stock_settings()
 	update_shopping_cart_settings(args)
 
 	args.update({"set_default": 1})
 	create_bank_account(args)
 
+
 def set_global_defaults(args):
 	global_defaults = frappe.get_doc("Global Defaults", "Global Defaults")
 	current_fiscal_year = frappe.get_all("Fiscal Year")[0]
 
-	global_defaults.update({
-		'current_fiscal_year': current_fiscal_year.name,
-		'default_currency': args.get('currency'),
-		'default_company':args.get('company_name')	,
-		"country": args.get("country"),
-	})
+	global_defaults.update(
+		{
+			"current_fiscal_year": current_fiscal_year.name,
+			"default_currency": args.get("currency"),
+			"default_company": args.get("company_name"),
+			"country": args.get("country"),
+		}
+	)
 
 	global_defaults.save()
 
-def set_active_domains(args):
-	frappe.get_single('Domain Settings').set_active_domains(args.get('domains'))
 
 def update_stock_settings():
 	stock_settings = frappe.get_doc("Stock Settings")
 	stock_settings.item_naming_by = "Item Code"
 	stock_settings.valuation_method = "FIFO"
-	stock_settings.default_warehouse = frappe.db.get_value('Warehouse', {'warehouse_name': _('Stores')})
+	stock_settings.default_warehouse = frappe.db.get_value(
+		"Warehouse", {"warehouse_name": _("Stores")}
+	)
 	stock_settings.stock_uom = _("Nos")
 	stock_settings.auto_indent = 1
 	stock_settings.auto_insert_price_list_rate_if_missing = 1
@@ -502,52 +819,65 @@
 	stock_settings.set_qty_in_transactions_based_on_serial_no_input = 1
 	stock_settings.save()
 
+
 def create_bank_account(args):
-	if not args.get('bank_account'):
+	if not args.get("bank_account"):
 		return
 
-	company_name = args.get('company_name')
-	bank_account_group =  frappe.db.get_value("Account",
-		{"account_type": "Bank", "is_group": 1, "root_type": "Asset",
-			"company": company_name})
+	company_name = args.get("company_name")
+	bank_account_group = frappe.db.get_value(
+		"Account", {"account_type": "Bank", "is_group": 1, "root_type": "Asset", "company": company_name}
+	)
 	if bank_account_group:
-		bank_account = frappe.get_doc({
-			"doctype": "Account",
-			'account_name': args.get('bank_account'),
-			'parent_account': bank_account_group,
-			'is_group':0,
-			'company': company_name,
-			"account_type": "Bank",
-		})
+		bank_account = frappe.get_doc(
+			{
+				"doctype": "Account",
+				"account_name": args.get("bank_account"),
+				"parent_account": bank_account_group,
+				"is_group": 0,
+				"company": company_name,
+				"account_type": "Bank",
+			}
+		)
 		try:
 			doc = bank_account.insert()
 
-			if args.get('set_default'):
-				frappe.db.set_value("Company", args.get('company_name'), "default_bank_account", bank_account.name, update_modified=False)
+			if args.get("set_default"):
+				frappe.db.set_value(
+					"Company",
+					args.get("company_name"),
+					"default_bank_account",
+					bank_account.name,
+					update_modified=False,
+				)
 
 			return doc
 
 		except RootNotEditable:
-			frappe.throw(_("Bank account cannot be named as {0}").format(args.get('bank_account')))
+			frappe.throw(_("Bank account cannot be named as {0}").format(args.get("bank_account")))
 		except frappe.DuplicateEntryError:
 			# bank account same as a CoA entry
 			pass
 
-def update_shopping_cart_settings(args): # nosemgrep
+
+def update_shopping_cart_settings(args):  # nosemgrep
 	shopping_cart = frappe.get_doc("E Commerce Settings")
-	shopping_cart.update({
-		"enabled": 1,
-		'company': args.company_name,
-		'price_list': frappe.db.get_value("Price List", {"selling": 1}),
-		'default_customer_group': _("Individual"),
-		'quotation_series': "QTN-",
-	})
+	shopping_cart.update(
+		{
+			"enabled": 1,
+			"company": args.company_name,
+			"price_list": frappe.db.get_value("Price List", {"selling": 1}),
+			"default_customer_group": _("Individual"),
+			"quotation_series": "QTN-",
+		}
+	)
 	shopping_cart.update_single(shopping_cart.get_valid_dict())
 
+
 def get_fy_details(fy_start_date, fy_end_date):
 	start_year = getdate(fy_start_date).year
 	if start_year == getdate(fy_end_date).year:
 		fy = cstr(start_year)
 	else:
-		fy = cstr(start_year) + '-' + cstr(start_year + 1)
+		fy = cstr(start_year) + "-" + cstr(start_year + 1)
 	return fy
diff --git a/erpnext/setup/setup_wizard/operations/sample_data.py b/erpnext/setup/setup_wizard/operations/sample_data.py
deleted file mode 100644
index 1685994..0000000
--- a/erpnext/setup/setup_wizard/operations/sample_data.py
+++ /dev/null
@@ -1,179 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-
-import json
-import os
-import random
-
-import frappe
-import frappe.utils
-from frappe import _
-from frappe.utils.make_random import add_random_children
-
-
-def make_sample_data(domains, make_dependent = False):
-	"""Create a few opportunities, quotes, material requests, issues, todos, projects
-	to help the user get started"""
-
-	if make_dependent:
-		items = frappe.get_all("Item", {'is_sales_item': 1})
-		customers = frappe.get_all("Customer")
-		warehouses = frappe.get_all("Warehouse")
-
-		if items and customers:
-			for i in range(3):
-				customer = random.choice(customers).name
-				make_opportunity(items, customer)
-				make_quote(items, customer)
-
-		if items and warehouses:
-			make_material_request(frappe.get_all("Item"))
-
-	make_projects(domains)
-	import_notification()
-
-def make_opportunity(items, customer):
-	b = frappe.get_doc({
-		"doctype": "Opportunity",
-		"opportunity_from": "Customer",
-		"customer": customer,
-		"opportunity_type": _("Sales"),
-		"with_items": 1
-	})
-
-	add_random_children(b, "items", rows=len(items), randomize = {
-		"qty": (1, 5),
-		"item_code": ["Item"]
-	}, unique="item_code")
-
-	b.insert(ignore_permissions=True)
-
-	b.add_comment('Comment', text="This is a dummy record")
-
-def make_quote(items, customer):
-	qtn = frappe.get_doc({
-		"doctype": "Quotation",
-		"quotation_to": "Customer",
-		"party_name": customer,
-		"order_type": "Sales"
-	})
-
-	add_random_children(qtn, "items", rows=len(items), randomize = {
-		"qty": (1, 5),
-		"item_code": ["Item"]
-	}, unique="item_code")
-
-	qtn.insert(ignore_permissions=True)
-
-	qtn.add_comment('Comment', text="This is a dummy record")
-
-def make_material_request(items):
-	for i in items:
-		mr = frappe.get_doc({
-			"doctype": "Material Request",
-			"material_request_type": "Purchase",
-			"schedule_date": frappe.utils.add_days(frappe.utils.nowdate(), 7),
-			"items": [{
-				"schedule_date": frappe.utils.add_days(frappe.utils.nowdate(), 7),
-				"item_code": i.name,
-				"qty": 10
-			}]
-		})
-		mr.insert()
-		mr.submit()
-
-		mr.add_comment('Comment', text="This is a dummy record")
-
-
-def make_issue():
-	pass
-
-def make_projects(domains):
-	current_date = frappe.utils.nowdate()
-	project = frappe.get_doc({
-		"doctype": "Project",
-		"project_name": "ERPNext Implementation",
-	})
-
-	tasks = [
-		{
-			"title": "Explore ERPNext",
-			"start_date": current_date,
-			"end_date": current_date,
-			"file": "explore.md"
-		}]
-
-	if 'Education' in domains:
-		tasks += [
-			{
-				"title": _("Setup your Institute in ERPNext"),
-				"start_date": current_date,
-				"end_date": frappe.utils.add_days(current_date, 1),
-				"file": "education_masters.md"
-			},
-			{
-				"title": "Setup Master Data",
-				"start_date": current_date,
-				"end_date": frappe.utils.add_days(current_date, 1),
-				"file": "education_masters.md"
-			}]
-
-	else:
-		tasks += [
-			{
-				"title": "Setup Your Company",
-				"start_date": current_date,
-				"end_date": frappe.utils.add_days(current_date, 1),
-				"file": "masters.md"
-			},
-			{
-				"title": "Start Tracking your Sales",
-				"start_date": current_date,
-				"end_date": frappe.utils.add_days(current_date, 2),
-				"file": "sales.md"
-			},
-			{
-				"title": "Start Managing Purchases",
-				"start_date": current_date,
-				"end_date": frappe.utils.add_days(current_date, 3),
-				"file": "purchase.md"
-			},
-			{
-				"title": "Import Data",
-				"start_date": current_date,
-				"end_date": frappe.utils.add_days(current_date, 4),
-				"file": "import_data.md"
-			},
-			{
-				"title": "Go Live!",
-				"start_date": current_date,
-				"end_date": frappe.utils.add_days(current_date, 5),
-				"file": "go_live.md"
-			}]
-
-	for t in tasks:
-		with open (os.path.join(os.path.dirname(__file__), "tasks", t['file'])) as f:
-			t['description'] = frappe.utils.md_to_html(f.read())
-			del t['file']
-
-		project.append('tasks', t)
-
-	project.insert(ignore_permissions=True)
-
-def import_notification():
-	'''Import notification for task start'''
-	with open (os.path.join(os.path.dirname(__file__), "tasks/task_alert.json")) as f:
-		notification = frappe.get_doc(json.loads(f.read())[0])
-		notification.insert()
-
-	# trigger the first message!
-	from frappe.email.doctype.notification.notification import trigger_daily_alerts
-	trigger_daily_alerts()
-
-def test_sample():
-	frappe.db.sql('delete from `tabNotification`')
-	frappe.db.sql('delete from tabProject')
-	frappe.db.sql('delete from tabTask')
-	make_projects('Education')
-	import_notification()
diff --git a/erpnext/setup/setup_wizard/operations/taxes_setup.py b/erpnext/setup/setup_wizard/operations/taxes_setup.py
index 289ffa5..2f77dd6 100644
--- a/erpnext/setup/setup_wizard/operations/taxes_setup.py
+++ b/erpnext/setup/setup_wizard/operations/taxes_setup.py
@@ -10,11 +10,11 @@
 
 
 def setup_taxes_and_charges(company_name: str, country: str):
-	if not frappe.db.exists('Company', company_name):
-		frappe.throw(_('Company {} does not exist yet. Taxes setup aborted.').format(company_name))
+	if not frappe.db.exists("Company", company_name):
+		frappe.throw(_("Company {} does not exist yet. Taxes setup aborted.").format(company_name))
 
-	file_path = os.path.join(os.path.dirname(__file__), '..', 'data', 'country_wise_tax.json')
-	with open(file_path, 'r') as json_file:
+	file_path = os.path.join(os.path.dirname(__file__), "..", "data", "country_wise_tax.json")
+	with open(file_path, "r") as json_file:
 		tax_data = json.load(json_file)
 
 	country_wise_tax = tax_data.get(country)
@@ -22,7 +22,7 @@
 	if not country_wise_tax:
 		return
 
-	if 'chart_of_accounts' not in country_wise_tax:
+	if "chart_of_accounts" not in country_wise_tax:
 		country_wise_tax = simple_to_detailed(country_wise_tax)
 
 	from_detailed_data(company_name, country_wise_tax)
@@ -36,39 +36,44 @@
 	Example input:
 
 	{
-		"France VAT 20%": {
-			"account_name": "VAT 20%",
-			"tax_rate": 20,
-			"default": 1
-		},
-		"France VAT 10%": {
-			"account_name": "VAT 10%",
-			"tax_rate": 10
-		}
+	        "France VAT 20%": {
+	                "account_name": "VAT 20%",
+	                "tax_rate": 20,
+	                "default": 1
+	        },
+	        "France VAT 10%": {
+	                "account_name": "VAT 10%",
+	                "tax_rate": 10
+	        }
 	}
 	"""
 	return {
-		'chart_of_accounts': {
-			'*': {
-				'item_tax_templates': [{
-					'title': title,
-					'taxes': [{
-						'tax_type': {
-							'account_name': data.get('account_name'),
-							'tax_rate': data.get('tax_rate')
-						}
-					}]
-				} for title, data in templates.items()],
-				'*': [{
-					'title': title,
-					'is_default': data.get('default', 0),
-					'taxes': [{
-						'account_head': {
-							'account_name': data.get('account_name'),
-							'tax_rate': data.get('tax_rate')
-						}
-					}]
-				} for title, data in templates.items()]
+		"chart_of_accounts": {
+			"*": {
+				"item_tax_templates": [
+					{
+						"title": title,
+						"taxes": [
+							{"tax_type": {"account_name": data.get("account_name"), "tax_rate": data.get("tax_rate")}}
+						],
+					}
+					for title, data in templates.items()
+				],
+				"*": [
+					{
+						"title": title,
+						"is_default": data.get("default", 0),
+						"taxes": [
+							{
+								"account_head": {
+									"account_name": data.get("account_name"),
+									"tax_rate": data.get("tax_rate"),
+								}
+							}
+						],
+					}
+					for title, data in templates.items()
+				],
 			}
 		}
 	}
@@ -76,13 +81,13 @@
 
 def from_detailed_data(company_name, data):
 	"""Create Taxes and Charges Templates from detailed data."""
-	coa_name = frappe.db.get_value('Company', company_name, 'chart_of_accounts')
-	coa_data = data.get('chart_of_accounts', {})
-	tax_templates = coa_data.get(coa_name) or coa_data.get('*', {})
-	tax_categories = data.get('tax_categories')
-	sales_tax_templates = tax_templates.get('sales_tax_templates') or tax_templates.get('*', {})
-	purchase_tax_templates = tax_templates.get('purchase_tax_templates') or tax_templates.get('*', {})
-	item_tax_templates = tax_templates.get('item_tax_templates') or tax_templates.get('*', {})
+	coa_name = frappe.db.get_value("Company", company_name, "chart_of_accounts")
+	coa_data = data.get("chart_of_accounts", {})
+	tax_templates = coa_data.get(coa_name) or coa_data.get("*", {})
+	tax_categories = data.get("tax_categories")
+	sales_tax_templates = tax_templates.get("sales_tax_templates") or tax_templates.get("*", {})
+	purchase_tax_templates = tax_templates.get("purchase_tax_templates") or tax_templates.get("*", {})
+	item_tax_templates = tax_templates.get("item_tax_templates") or tax_templates.get("*", {})
 
 	if tax_categories:
 		for tax_category in tax_categories:
@@ -90,11 +95,11 @@
 
 	if sales_tax_templates:
 		for template in sales_tax_templates:
-			make_taxes_and_charges_template(company_name, 'Sales Taxes and Charges Template', template)
+			make_taxes_and_charges_template(company_name, "Sales Taxes and Charges Template", template)
 
 	if purchase_tax_templates:
 		for template in purchase_tax_templates:
-			make_taxes_and_charges_template(company_name, 'Purchase Taxes and Charges Template', template)
+			make_taxes_and_charges_template(company_name, "Purchase Taxes and Charges Template", template)
 
 	if item_tax_templates:
 		for template in item_tax_templates:
@@ -102,40 +107,45 @@
 
 
 def update_regional_tax_settings(country, company):
-	path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(country))
+	path = frappe.get_app_path("erpnext", "regional", frappe.scrub(country))
 	if os.path.exists(path.encode("utf-8")):
 		try:
-			module_name = "erpnext.regional.{0}.setup.update_regional_tax_settings".format(frappe.scrub(country))
+			module_name = "erpnext.regional.{0}.setup.update_regional_tax_settings".format(
+				frappe.scrub(country)
+			)
 			frappe.get_attr(module_name)(country, company)
 		except Exception as e:
 			# Log error and ignore if failed to setup regional tax settings
-			frappe.log_error()
+			frappe.log_error("Unable to setup regional tax settings")
 			pass
 
-def make_taxes_and_charges_template(company_name, doctype, template):
-	template['company'] = company_name
-	template['doctype'] = doctype
 
-	if frappe.db.exists(doctype, {'title': template.get('title'), 'company': company_name}):
+def make_taxes_and_charges_template(company_name, doctype, template):
+	template["company"] = company_name
+	template["doctype"] = doctype
+
+	if frappe.db.exists(doctype, {"title": template.get("title"), "company": company_name}):
 		return
 
-	for tax_row in template.get('taxes'):
-		account_data = tax_row.get('account_head')
+	for tax_row in template.get("taxes"):
+		account_data = tax_row.get("account_head")
 		tax_row_defaults = {
-			'category': 'Total',
-			'charge_type': 'On Net Total',
-			'cost_center': frappe.db.get_value('Company', company_name, 'cost_center')
+			"category": "Total",
+			"charge_type": "On Net Total",
+			"cost_center": frappe.db.get_value("Company", company_name, "cost_center"),
 		}
 
-		if doctype == 'Purchase Taxes and Charges Template':
-			tax_row_defaults['add_deduct_tax'] = 'Add'
+		if doctype == "Purchase Taxes and Charges Template":
+			tax_row_defaults["add_deduct_tax"] = "Add"
 
 		# if account_head is a dict, search or create the account and get it's name
 		if isinstance(account_data, dict):
-			tax_row_defaults['description'] = '{0} @ {1}'.format(account_data.get('account_name'), account_data.get('tax_rate'))
-			tax_row_defaults['rate'] = account_data.get('tax_rate')
+			tax_row_defaults["description"] = "{0} @ {1}".format(
+				account_data.get("account_name"), account_data.get("tax_rate")
+			)
+			tax_row_defaults["rate"] = account_data.get("tax_rate")
 			account = get_or_create_account(company_name, account_data)
-			tax_row['account_head'] = account.name
+			tax_row["account_head"] = account.name
 
 		# use the default value if nothing other is specified
 		for fieldname, default_value in tax_row_defaults.items():
@@ -151,28 +161,29 @@
 	doc.insert(ignore_permissions=True)
 	return doc
 
+
 def make_item_tax_template(company_name, template):
 	"""Create an Item Tax Template.
 
 	This requires a separate method because Item Tax Template is structured
 	differently from Sales and Purchase Tax Templates.
 	"""
-	doctype = 'Item Tax Template'
-	template['company'] = company_name
-	template['doctype'] = doctype
+	doctype = "Item Tax Template"
+	template["company"] = company_name
+	template["doctype"] = doctype
 
-	if frappe.db.exists(doctype, {'title': template.get('title'), 'company': company_name}):
+	if frappe.db.exists(doctype, {"title": template.get("title"), "company": company_name}):
 		return
 
-	for tax_row in template.get('taxes'):
-		account_data = tax_row.get('tax_type')
+	for tax_row in template.get("taxes"):
+		account_data = tax_row.get("tax_type")
 
 		# if tax_type is a dict, search or create the account and get it's name
 		if isinstance(account_data, dict):
 			account = get_or_create_account(company_name, account_data)
-			tax_row['tax_type'] = account.name
-			if 'tax_rate' not in tax_row:
-				tax_row['tax_rate'] = account_data.get('tax_rate')
+			tax_row["tax_type"] = account.name
+			if "tax_rate" not in tax_row:
+				tax_row["tax_rate"] = account_data.get("tax_rate")
 
 	doc = frappe.get_doc(template)
 
@@ -183,36 +194,36 @@
 	doc.insert(ignore_permissions=True)
 	return doc
 
+
 def get_or_create_account(company_name, account):
 	"""
 	Check if account already exists. If not, create it.
 	Return a tax account or None.
 	"""
-	default_root_type = 'Liability'
-	root_type = account.get('root_type', default_root_type)
+	default_root_type = "Liability"
+	root_type = account.get("root_type", default_root_type)
 
-	existing_accounts = frappe.get_all('Account',
-		filters={
-			'company': company_name,
-			'root_type': root_type
-		},
+	existing_accounts = frappe.get_all(
+		"Account",
+		filters={"company": company_name, "root_type": root_type},
 		or_filters={
-			'account_name': account.get('account_name'),
-			'account_number': account.get('account_number')
-		})
+			"account_name": account.get("account_name"),
+			"account_number": account.get("account_number"),
+		},
+	)
 
 	if existing_accounts:
-		return frappe.get_doc('Account', existing_accounts[0].name)
+		return frappe.get_doc("Account", existing_accounts[0].name)
 
 	tax_group = get_or_create_tax_group(company_name, root_type)
 
-	account['doctype'] = 'Account'
-	account['company'] = company_name
-	account['parent_account'] = tax_group
-	account['report_type'] = 'Balance Sheet'
-	account['account_type'] = 'Tax'
-	account['root_type'] = root_type
-	account['is_group'] = 0
+	account["doctype"] = "Account"
+	account["company"] = company_name
+	account["parent_account"] = tax_group
+	account["report_type"] = "Balance Sheet"
+	account["account_type"] = "Tax"
+	account["root_type"] = root_type
+	account["is_group"] = 0
 
 	doc = frappe.get_doc(account)
 	doc.flags.ignore_links = True
@@ -220,50 +231,53 @@
 	doc.insert(ignore_permissions=True, ignore_mandatory=True)
 	return doc
 
+
 def get_or_create_tax_group(company_name, root_type):
 	# Look for a group account of type 'Tax'
-	tax_group_name = frappe.db.get_value('Account', {
-		'is_group': 1,
-		'root_type': root_type,
-		'account_type': 'Tax',
-		'company': company_name
-	})
+	tax_group_name = frappe.db.get_value(
+		"Account",
+		{"is_group": 1, "root_type": root_type, "account_type": "Tax", "company": company_name},
+	)
 
 	if tax_group_name:
 		return tax_group_name
 
 	# Look for a group account named 'Duties and Taxes' or 'Tax Assets'
-	account_name = _('Duties and Taxes') if root_type == 'Liability' else _('Tax Assets')
-	tax_group_name = frappe.db.get_value('Account', {
-		'is_group': 1,
-		'root_type': root_type,
-		'account_name': account_name,
-		'company': company_name
-	})
+	account_name = _("Duties and Taxes") if root_type == "Liability" else _("Tax Assets")
+	tax_group_name = frappe.db.get_value(
+		"Account",
+		{"is_group": 1, "root_type": root_type, "account_name": account_name, "company": company_name},
+	)
 
 	if tax_group_name:
 		return tax_group_name
 
 	# Create a new group account named 'Duties and Taxes' or 'Tax Assets' just
 	# below the root account
-	root_account = frappe.get_all('Account', {
-		'is_group': 1,
-		'root_type': root_type,
-		'company': company_name,
-		'report_type': 'Balance Sheet',
-		'parent_account': ('is', 'not set')
-	}, limit=1)[0]
+	root_account = frappe.get_all(
+		"Account",
+		{
+			"is_group": 1,
+			"root_type": root_type,
+			"company": company_name,
+			"report_type": "Balance Sheet",
+			"parent_account": ("is", "not set"),
+		},
+		limit=1,
+	)[0]
 
-	tax_group_account = frappe.get_doc({
-		'doctype': 'Account',
-		'company': company_name,
-		'is_group': 1,
-		'report_type': 'Balance Sheet',
-		'root_type': root_type,
-		'account_type': 'Tax',
-		'account_name': account_name,
-		'parent_account': root_account.name
-	})
+	tax_group_account = frappe.get_doc(
+		{
+			"doctype": "Account",
+			"company": company_name,
+			"is_group": 1,
+			"report_type": "Balance Sheet",
+			"root_type": root_type,
+			"account_type": "Tax",
+			"account_name": account_name,
+			"parent_account": root_account.name,
+		}
+	)
 
 	tax_group_account.flags.ignore_links = True
 	tax_group_account.flags.ignore_validate = True
@@ -275,11 +289,11 @@
 
 
 def make_tax_category(tax_category):
-	doctype = 'Tax Category'
+	doctype = "Tax Category"
 	if isinstance(tax_category, str):
-		tax_category = {'title': tax_category}
+		tax_category = {"title": tax_category}
 
-	tax_category['doctype'] = doctype
-	if not frappe.db.exists(doctype, tax_category['title']):
+	tax_category["doctype"] = doctype
+	if not frappe.db.exists(doctype, tax_category["title"]):
 		doc = frappe.get_doc(tax_category)
 		doc.insert(ignore_permissions=True)
diff --git a/erpnext/setup/setup_wizard/setup_wizard.py b/erpnext/setup/setup_wizard/setup_wizard.py
index c9ed184..bd86a5b 100644
--- a/erpnext/setup/setup_wizard/setup_wizard.py
+++ b/erpnext/setup/setup_wizard/setup_wizard.py
@@ -7,112 +7,69 @@
 
 from .operations import company_setup
 from .operations import install_fixtures as fixtures
-from .operations import sample_data
 
 
 def get_setup_stages(args=None):
 	if frappe.db.sql("select name from tabCompany"):
 		stages = [
 			{
-				'status': _('Wrapping up'),
-				'fail_msg': _('Failed to login'),
-				'tasks': [
-					{
-						'fn': fin,
-						'args': args,
-						'fail_msg': _("Failed to login")
-					}
-				]
+				"status": _("Wrapping up"),
+				"fail_msg": _("Failed to login"),
+				"tasks": [{"fn": fin, "args": args, "fail_msg": _("Failed to login")}],
 			}
 		]
 	else:
 		stages = [
 			{
-				'status': _('Installing presets'),
-				'fail_msg': _('Failed to install presets'),
-				'tasks': [
-					{
-						'fn': stage_fixtures,
-						'args': args,
-						'fail_msg': _("Failed to install presets")
-					}
-				]
+				"status": _("Installing presets"),
+				"fail_msg": _("Failed to install presets"),
+				"tasks": [{"fn": stage_fixtures, "args": args, "fail_msg": _("Failed to install presets")}],
 			},
 			{
-				'status': _('Setting up company'),
-				'fail_msg': _('Failed to setup company'),
-				'tasks': [
-					{
-						'fn': setup_company,
-						'args': args,
-						'fail_msg': _("Failed to setup company")
-					}
-				]
+				"status": _("Setting up company"),
+				"fail_msg": _("Failed to setup company"),
+				"tasks": [{"fn": setup_company, "args": args, "fail_msg": _("Failed to setup company")}],
 			},
 			{
-				'status': _('Setting defaults'),
-				'fail_msg': 'Failed to set defaults',
-				'tasks': [
-					{
-						'fn': setup_defaults,
-						'args': args,
-						'fail_msg': _("Failed to setup defaults")
-					},
-					{
-						'fn': stage_four,
-						'args': args,
-						'fail_msg': _("Failed to create website")
-					},
-					{
-						'fn': set_active_domains,
-						'args': args,
-						'fail_msg': _("Failed to add Domain")
-					},
-				]
+				"status": _("Setting defaults"),
+				"fail_msg": "Failed to set defaults",
+				"tasks": [
+					{"fn": setup_defaults, "args": args, "fail_msg": _("Failed to setup defaults")},
+					{"fn": stage_four, "args": args, "fail_msg": _("Failed to create website")},
+				],
 			},
 			{
-				'status': _('Wrapping up'),
-				'fail_msg': _('Failed to login'),
-				'tasks': [
-					{
-						'fn': fin,
-						'args': args,
-						'fail_msg': _("Failed to login")
-					}
-				]
-			}
+				"status": _("Wrapping up"),
+				"fail_msg": _("Failed to login"),
+				"tasks": [{"fn": fin, "args": args, "fail_msg": _("Failed to login")}],
+			},
 		]
 
 	return stages
 
+
 def stage_fixtures(args):
-	fixtures.install(args.get('country'))
+	fixtures.install(args.get("country"))
+
 
 def setup_company(args):
 	fixtures.install_company(args)
 
+
 def setup_defaults(args):
 	fixtures.install_defaults(frappe._dict(args))
 
+
 def stage_four(args):
 	company_setup.create_website(args)
 	company_setup.create_email_digest()
 	company_setup.create_logo(args)
 
+
 def fin(args):
 	frappe.local.message_log = []
 	login_as_first_user(args)
 
-	make_sample_data(args.get('domains'))
-
-def make_sample_data(domains):
-	try:
-		sample_data.make_sample_data(domains)
-	except Exception:
-		# clear message
-		if frappe.message_log:
-			frappe.message_log.pop()
-		pass
 
 def login_as_first_user(args):
 	if args.get("email") and hasattr(frappe.local, "login_manager"):
@@ -126,7 +83,3 @@
 	setup_defaults(args)
 	stage_four(args)
 	fin(args)
-
-def set_active_domains(args):
-	domain_settings = frappe.get_single('Domain Settings')
-	domain_settings.set_active_domains(args.get('domains'))
diff --git a/erpnext/setup/setup_wizard/tasks/education_masters.md b/erpnext/setup/setup_wizard/tasks/education_masters.md
deleted file mode 100644
index d0887d2..0000000
--- a/erpnext/setup/setup_wizard/tasks/education_masters.md
+++ /dev/null
@@ -1,9 +0,0 @@
-Lets start making things in ERPNext that are representative of your institution.
-
-1. Make a list of **Programs** that you offer
-1. Add a few **Courses** that your programs cover
-1. Create **Academic Terms** and **Academic Years**
-1. Start adding **Students**
-1. Group your students into **Batches**
-
-Watch this video to learn more about ERPNext Education: https://www.youtube.com/watch?v=f6foQOyGzdA
diff --git a/erpnext/setup/setup_wizard/tasks/explore.md b/erpnext/setup/setup_wizard/tasks/explore.md
deleted file mode 100644
index ce6cb60..0000000
--- a/erpnext/setup/setup_wizard/tasks/explore.md
+++ /dev/null
@@ -1,7 +0,0 @@
-Thanks for checking this out! ❤️
-
-If you are evaluating an ERP system for the first time, this is going to be quite a task! But don't worry, ERPNext is awesome.
-
-First, get familiar with the surroundings. ERPNext covers a *lot of features*, go to the home page and click on the "Explore" icon.
-
-All the best!
\ No newline at end of file
diff --git a/erpnext/setup/setup_wizard/tasks/go_live.md b/erpnext/setup/setup_wizard/tasks/go_live.md
deleted file mode 100644
index 0a934a4..0000000
--- a/erpnext/setup/setup_wizard/tasks/go_live.md
+++ /dev/null
@@ -1,18 +0,0 @@
-Ready to go live with ERPNext? 🏁🏁🏁
-
-Here are the steps:
-
-1. Sync up your **Chart of Accounts**
-3. Add your opening stock using **Stock Reconciliation**
-4. Add your open invoices (both sales and purchase)
-3. Add your opening account balances by making a **Journal Entry**
-
-If you need help for going live, sign up for an account at erpnext.com or find a partner to help you with this.
-
-Or you can watch these videos 📺:
-
-Setup your chart of accounts: https://www.youtube.com/watch?v=AcfMCT7wLLo
-
-Add Open Stock: https://www.youtube.com/watch?v=nlHX0ZZ84Lw
-
-Add Opening Balances: https://www.youtube.com/watch?v=nlHX0ZZ84Lw
diff --git a/erpnext/setup/setup_wizard/tasks/import_data.md b/erpnext/setup/setup_wizard/tasks/import_data.md
deleted file mode 100644
index c5b85c9..0000000
--- a/erpnext/setup/setup_wizard/tasks/import_data.md
+++ /dev/null
@@ -1,5 +0,0 @@
-Lets import some data! 💪💪
-
-If you are already running a business, you most likely have your Items, Customers or Suppliers in some spreadsheet file somewhere, import it into ERPNext with the Data Import Tool.
-
-Watch this video to get started: https://www.youtube.com/watch?v=Ta2Xx3QoK3E
diff --git a/erpnext/setup/setup_wizard/tasks/masters.md b/erpnext/setup/setup_wizard/tasks/masters.md
deleted file mode 100644
index 6ade159..0000000
--- a/erpnext/setup/setup_wizard/tasks/masters.md
+++ /dev/null
@@ -1,7 +0,0 @@
-Start building a model of your business in ERPNext by adding your Items and Customers.
-
-These videos 📺 will help you get started:
-
-Adding Customers and Suppliers: https://www.youtube.com/watch?v=zsrrVDk6VBs
-
-Adding Items and Prices: https://www.youtube.com/watch?v=FcOsV-e8ymE
\ No newline at end of file
diff --git a/erpnext/setup/setup_wizard/tasks/purchase.md b/erpnext/setup/setup_wizard/tasks/purchase.md
deleted file mode 100644
index 3f3bc3b..0000000
--- a/erpnext/setup/setup_wizard/tasks/purchase.md
+++ /dev/null
@@ -1,10 +0,0 @@
-How to manage your purchasing in ERPNext 🛒🛒🛒:
-
-1. Add a few **Suppliers**
-2. Find out what you need by making **Material Requests**.
-3. Now start placing orders via **Purchase Order**.
-4. When your suppliers deliver, make **Purchase Receipts**
-
-Now never run out of stock again! 😎
-
-Watch this video 📺 to get an overview: https://www.youtube.com/watch?v=4TN9kPyfIqM
\ No newline at end of file
diff --git a/erpnext/setup/setup_wizard/tasks/sales.md b/erpnext/setup/setup_wizard/tasks/sales.md
deleted file mode 100644
index 15268fa..0000000
--- a/erpnext/setup/setup_wizard/tasks/sales.md
+++ /dev/null
@@ -1,8 +0,0 @@
-Start managing your sales with ERPNext 🔔🔔🔔:
-
-1. Add potential business contacts as **Leads**
-2. Udpate your deals in pipeline in **Opportunities**
-3. Send proposals to your leads or customers with **Quotations**
-4. Track confirmed orders with **Sales Orders**
-
-Watch this video 📺 to get an overview: https://www.youtube.com/watch?v=o9XCSZHJfpA
diff --git a/erpnext/setup/setup_wizard/tasks/school_import_data.md b/erpnext/setup/setup_wizard/tasks/school_import_data.md
deleted file mode 100644
index 1fbe049..0000000
--- a/erpnext/setup/setup_wizard/tasks/school_import_data.md
+++ /dev/null
@@ -1,5 +0,0 @@
-Lets import some data! 💪💪
-
-If you are already running a Institute, you most likely have your Students in some spreadsheet file somewhere. Import it into ERPNext with the Data Import Tool.
-
-Watch this video to get started: https://www.youtube.com/watch?v=Ta2Xx3QoK3E
\ No newline at end of file
diff --git a/erpnext/setup/setup_wizard/tasks/task_alert.json b/erpnext/setup/setup_wizard/tasks/task_alert.json
deleted file mode 100644
index cac868a..0000000
--- a/erpnext/setup/setup_wizard/tasks/task_alert.json
+++ /dev/null
@@ -1,28 +0,0 @@
-[
- {
-  "attach_print": 0,
-  "condition": "doc.status in ('Open', 'Overdue')",
-  "date_changed": "exp_end_date",
-  "days_in_advance": 0,
-  "docstatus": 0,
-  "doctype": "Notification",
-  "document_type": "Task",
-  "enabled": 1,
-  "event": "Days After",
-  "is_standard": 0,
-  "message": "<p>Task due today:</p>\n\n<div>\n{{ doc.description }}\n</div>\n\n<hr>\n<p style=\"font-size: 85%\">\nThis is a notification for a task that is due today, and a sample <b>Notification</b>. In ERPNext you can setup notifications on anything, Invoices, Orders, Leads, Opportunities, so you never miss a thing.\n<br>To edit this, and setup other alerts, just type <b>Notification</b> in the search bar.</p>",
-  "method": null,
-  "modified": "2017-03-09 07:34:58.168370",
-  "module": null,
-  "name": "Task Due Alert",
-  "recipients": [
-   {
-    "cc": null,
-    "condition": null,
-    "email_by_document_field": "owner"
-   }
-  ],
-  "subject": "{{ doc.subject }}",
-  "value_changed": null
- }
-]
\ No newline at end of file
diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py
index a4f2207..b3ff439 100644
--- a/erpnext/setup/utils.py
+++ b/erpnext/setup/utils.py
@@ -1,62 +1,53 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 
-
 import frappe
 from frappe import _
 from frappe.utils import add_days, flt, get_datetime_str, nowdate
+from frappe.utils.data import now_datetime
+from frappe.utils.nestedset import get_ancestors_of, get_root_of  # noqa
 
 from erpnext import get_default_company
 
 
-def get_root_of(doctype):
-	"""Get root element of a DocType with a tree structure"""
-	result = frappe.db.sql_list("""select name from `tab%s`
-		where lft=1 and rgt=(select max(rgt) from `tab%s` where docstatus < 2)""" %
-		(doctype, doctype))
-	return result[0] if result else None
-
-def get_ancestors_of(doctype, name):
-	"""Get ancestor elements of a DocType with a tree structure"""
-	lft, rgt = frappe.db.get_value(doctype, name, ["lft", "rgt"])
-	result = frappe.db.sql_list("""select name from `tab%s`
-		where lft<%s and rgt>%s order by lft desc""" % (doctype, "%s", "%s"), (lft, rgt))
-	return result or []
-
 def before_tests():
 	frappe.clear_cache()
 	# complete setup if missing
 	from frappe.desk.page.setup_wizard.setup_wizard import setup_complete
-	if not frappe.get_list("Company"):
-		setup_complete({
-			"currency"          :"USD",
-			"full_name"         :"Test User",
-			"company_name"      :"Wind Power LLC",
-			"timezone"          :"America/New_York",
-			"company_abbr"      :"WP",
-			"industry"          :"Manufacturing",
-			"country"           :"United States",
-			"fy_start_date"     :"2021-01-01",
-			"fy_end_date"       :"2021-12-31",
-			"language"          :"english",
-			"company_tagline"   :"Testing",
-			"email"             :"test@erpnext.com",
-			"password"          :"test",
-			"chart_of_accounts" : "Standard",
-			"domains"           : ["Manufacturing"],
-		})
+
+	if not frappe.db.a_row_exists("Company"):
+		current_year = now_datetime().year
+		setup_complete(
+			{
+				"currency": "USD",
+				"full_name": "Test User",
+				"company_name": "Wind Power LLC",
+				"timezone": "America/New_York",
+				"company_abbr": "WP",
+				"industry": "Manufacturing",
+				"country": "United States",
+				"fy_start_date": f"{current_year}-01-01",
+				"fy_end_date": f"{current_year}-12-31",
+				"language": "english",
+				"company_tagline": "Testing",
+				"email": "test@erpnext.com",
+				"password": "test",
+				"chart_of_accounts": "Standard",
+			}
+		)
 
 	frappe.db.sql("delete from `tabLeave Allocation`")
 	frappe.db.sql("delete from `tabLeave Application`")
 	frappe.db.sql("delete from `tabSalary Slip`")
 	frappe.db.sql("delete from `tabItem Price`")
 
-	frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 0)
-	enable_all_roles_and_domains()
+	_enable_all_roles_for_admin()
+
 	set_defaults_for_tests()
 
 	frappe.db.commit()
 
+
 @frappe.whitelist()
 def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=None):
 	if not (from_currency and to_currency):
@@ -73,7 +64,7 @@
 	filters = [
 		["date", "<=", get_datetime_str(transaction_date)],
 		["from_currency", "=", from_currency],
-		["to_currency", "=", to_currency]
+		["to_currency", "=", to_currency],
 	]
 
 	if args == "for_buying":
@@ -88,8 +79,8 @@
 
 	# cksgb 19/09/2016: get last entry in Currency Exchange with from_currency and to_currency.
 	entries = frappe.get_all(
-		"Currency Exchange", fields=["exchange_rate"], filters=filters, order_by="date desc",
-		limit=1)
+		"Currency Exchange", fields=["exchange_rate"], filters=filters, order_by="date desc", limit=1
+	)
 	if entries:
 		return flt(entries[0].exchange_rate)
 
@@ -100,11 +91,12 @@
 
 		if not value:
 			import requests
-			settings = frappe.get_cached_doc('Currency Exchange Settings')
+
+			settings = frappe.get_cached_doc("Currency Exchange Settings")
 			req_params = {
 				"transaction_date": transaction_date,
 				"from_currency": from_currency,
-				"to_currency": to_currency
+				"to_currency": to_currency,
 			}
 			params = {}
 			for row in settings.req_params:
@@ -118,51 +110,56 @@
 			cache.setex(name=key, time=21600, value=flt(value))
 		return flt(value)
 	except Exception:
-		frappe.log_error(title="Get Exchange Rate")
-		frappe.msgprint(_("Unable to find exchange rate for {0} to {1} for key date {2}. Please create a Currency Exchange record manually").format(from_currency, to_currency, transaction_date))
+		frappe.log_error("Unable to fetch exchange rate")
+		frappe.msgprint(
+			_(
+				"Unable to find exchange rate for {0} to {1} for key date {2}. Please create a Currency Exchange record manually"
+			).format(from_currency, to_currency, transaction_date)
+		)
 		return 0.0
 
+
 def format_ces_api(data, param):
 	return data.format(
 		transaction_date=param.get("transaction_date"),
 		to_currency=param.get("to_currency"),
-		from_currency=param.get("from_currency")
+		from_currency=param.get("from_currency"),
 	)
 
-def enable_all_roles_and_domains():
-	""" enable all roles and domain for testing """
-	# add all roles to users
-	domains = frappe.get_all("Domain")
-	if not domains:
-		return
 
+def enable_all_roles_and_domains():
+	"""enable all roles and domain for testing"""
+	_enable_all_roles_for_admin()
+
+
+def _enable_all_roles_for_admin():
 	from frappe.desk.page.setup_wizard.setup_wizard import add_all_roles_to
-	frappe.get_single('Domain Settings').set_active_domains(\
-		[d.name for d in domains])
-	add_all_roles_to('Administrator')
+
+	all_roles = set(frappe.db.get_values("Role", pluck="name"))
+	admin_roles = set(
+		frappe.db.get_values("Has Role", {"parent": "Administrator"}, fieldname="role", pluck="role")
+	)
+
+	if all_roles.difference(admin_roles):
+		add_all_roles_to("Administrator")
+
 
 def set_defaults_for_tests():
-	from frappe.utils.nestedset import get_root_of
-
-	selling_settings = frappe.get_single("Selling Settings")
-	selling_settings.customer_group = get_root_of("Customer Group")
-	selling_settings.territory = get_root_of("Territory")
-	selling_settings.save()
+	defaults = {
+		"customer_group": get_root_of("Customer Group"),
+		"territory": get_root_of("Territory"),
+	}
+	frappe.db.set_single_value("Selling Settings", defaults)
+	for key, value in defaults.items():
+		frappe.db.set_default(key, value)
+	frappe.db.set_single_value("Stock Settings", "auto_insert_price_list_rate_if_missing", 0)
 
 
 def insert_record(records):
-	for r in records:
-		doc = frappe.new_doc(r.get("doctype"))
-		doc.update(r)
-		try:
-			doc.insert(ignore_permissions=True, ignore_if_duplicate=True)
-		except frappe.DuplicateEntryError as e:
-			# pass DuplicateEntryError and continue
-			if e.args and e.args[0]==doc.doctype and e.args[1]==doc.name:
-				# make sure DuplicateEntryError is for the exact same doc and not a related doc
-				pass
-			else:
-				raise
+	from frappe.desk.page.setup_wizard.setup_wizard import make_records
+
+	make_records(records)
+
 
 def welcome_email():
 	site_name = get_default_company() or "ERPNext"
diff --git a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
index c5640bc..eeb71c7 100644
--- a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
+++ b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
@@ -1,7 +1,7 @@
 {
  "charts": [],
  "content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Projects Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stock Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"HR Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Selling Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Buying Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Support Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Shopping Cart Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Portal Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Domain Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Products Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Naming Series\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Manufacturing Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Education Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Hotel Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"CRM Settings\",\"col\":3}}]",
- "creation": "2020-03-12 14:47:51.166455",
+ "creation": "2022-01-27 13:14:47.349433",
  "docstatus": 0,
  "doctype": "Workspace",
  "for_user": "",
@@ -10,7 +10,7 @@
  "idx": 0,
  "label": "ERPNext Settings",
  "links": [],
- "modified": "2022-01-13 19:18:59.362820",
+ "modified": "2022-04-28 11:42:01.043569",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "ERPNext Settings",
@@ -91,20 +91,6 @@
    "type": "DocType"
   },
   {
-   "icon": "education",
-   "label": "Education Settings",
-   "link_to": "Education Settings",
-   "restrict_to_domain": "Education",
-   "type": "DocType"
-  },
-  {
-   "icon": "organization",
-   "label": "Hotel Settings",
-   "link_to": "Hotel Settings",
-   "restrict_to_domain": "Hospitality",
-   "type": "DocType"
-  },
-  {
    "icon": "setting",
    "label": "Domain Settings",
    "link_to": "Domain Settings",
diff --git a/erpnext/startup/__init__.py b/erpnext/startup/__init__.py
index dd1b401..489e244 100644
--- a/erpnext/startup/__init__.py
+++ b/erpnext/startup/__init__.py
@@ -17,7 +17,4 @@
 # default settings that can be made for a user.
 
 product_name = "ERPNext"
-user_defaults = {
-	"Company": "company",
-	"Territory": "territory"
-}
+user_defaults = {"Company": "company", "Territory": "territory"}
diff --git a/erpnext/startup/boot.py b/erpnext/startup/boot.py
index 0da45a5..c3445ab 100644
--- a/erpnext/startup/boot.py
+++ b/erpnext/startup/boot.py
@@ -2,7 +2,6 @@
 # License: GNU General Public License v3. See license.txt"
 
 
-
 import frappe
 from frappe.utils import cint
 
@@ -10,69 +9,72 @@
 def boot_session(bootinfo):
 	"""boot session - send website info if guest"""
 
-	bootinfo.custom_css = frappe.db.get_value('Style Settings', None, 'custom_css') or ''
+	bootinfo.custom_css = frappe.db.get_value("Style Settings", None, "custom_css") or ""
 
-	if frappe.session['user']!='Guest':
+	if frappe.session["user"] != "Guest":
 		update_page_info(bootinfo)
 
 		load_country_and_currency(bootinfo)
-		bootinfo.sysdefaults.territory = frappe.db.get_single_value('Selling Settings',
-			'territory')
-		bootinfo.sysdefaults.customer_group = frappe.db.get_single_value('Selling Settings',
-			'customer_group')
-		bootinfo.sysdefaults.allow_stale = cint(frappe.db.get_single_value('Accounts Settings',
-			'allow_stale'))
-		bootinfo.sysdefaults.quotation_valid_till = cint(frappe.db.get_single_value('CRM Settings',
-			'default_valid_till'))
+		bootinfo.sysdefaults.territory = frappe.db.get_single_value("Selling Settings", "territory")
+		bootinfo.sysdefaults.customer_group = frappe.db.get_single_value(
+			"Selling Settings", "customer_group"
+		)
+		bootinfo.sysdefaults.allow_stale = cint(
+			frappe.db.get_single_value("Accounts Settings", "allow_stale")
+		)
+		bootinfo.sysdefaults.quotation_valid_till = cint(
+			frappe.db.get_single_value("CRM Settings", "default_valid_till")
+		)
 
 		# if no company, show a dialog box to create a new company
 		bootinfo.customer_count = frappe.db.sql("""SELECT count(*) FROM `tabCustomer`""")[0][0]
 
 		if not bootinfo.customer_count:
-			bootinfo.setup_complete = frappe.db.sql("""SELECT `name`
+			bootinfo.setup_complete = (
+				frappe.db.sql(
+					"""SELECT `name`
 				FROM `tabCompany`
-				LIMIT 1""") and 'Yes' or 'No'
+				LIMIT 1"""
+				)
+				and "Yes"
+				or "No"
+			)
 
-		bootinfo.docs += frappe.db.sql("""select name, default_currency, cost_center, default_selling_terms, default_buying_terms,
+		bootinfo.docs += frappe.db.sql(
+			"""select name, default_currency, cost_center, default_selling_terms, default_buying_terms,
 			default_letter_head, default_bank_account, enable_perpetual_inventory, country from `tabCompany`""",
-			as_dict=1, update={"doctype":":Company"})
+			as_dict=1,
+			update={"doctype": ":Company"},
+		)
 
-		party_account_types = frappe.db.sql(""" select name, ifnull(account_type, '') from `tabParty Type`""")
+		party_account_types = frappe.db.sql(
+			""" select name, ifnull(account_type, '') from `tabParty Type`"""
+		)
 		bootinfo.party_account_types = frappe._dict(party_account_types)
 
+
 def load_country_and_currency(bootinfo):
 	country = frappe.db.get_default("country")
 	if country and frappe.db.exists("Country", country):
 		bootinfo.docs += [frappe.get_doc("Country", country)]
 
-	bootinfo.docs += frappe.db.sql("""select name, fraction, fraction_units,
+	bootinfo.docs += frappe.db.sql(
+		"""select name, fraction, fraction_units,
 		number_format, smallest_currency_fraction_value, symbol from tabCurrency
-		where enabled=1""", as_dict=1, update={"doctype":":Currency"})
+		where enabled=1""",
+		as_dict=1,
+		update={"doctype": ":Currency"},
+	)
+
 
 def update_page_info(bootinfo):
-	bootinfo.page_info.update({
-		"Chart of Accounts": {
-			"title": "Chart of Accounts",
-			"route": "Tree/Account"
-		},
-		"Chart of Cost Centers": {
-			"title": "Chart of Cost Centers",
-			"route": "Tree/Cost Center"
-		},
-		"Item Group Tree": {
-			"title": "Item Group Tree",
-			"route": "Tree/Item Group"
-		},
-		"Customer Group Tree": {
-			"title": "Customer Group Tree",
-			"route": "Tree/Customer Group"
-		},
-		"Territory Tree": {
-			"title": "Territory Tree",
-			"route": "Tree/Territory"
-		},
-		"Sales Person Tree": {
-			"title": "Sales Person Tree",
-			"route": "Tree/Sales Person"
+	bootinfo.page_info.update(
+		{
+			"Chart of Accounts": {"title": "Chart of Accounts", "route": "Tree/Account"},
+			"Chart of Cost Centers": {"title": "Chart of Cost Centers", "route": "Tree/Cost Center"},
+			"Item Group Tree": {"title": "Item Group Tree", "route": "Tree/Item Group"},
+			"Customer Group Tree": {"title": "Customer Group Tree", "route": "Tree/Customer Group"},
+			"Territory Tree": {"title": "Territory Tree", "route": "Tree/Territory"},
+			"Sales Person Tree": {"title": "Sales Person Tree", "route": "Tree/Sales Person"},
 		}
-	})
+	)
diff --git a/erpnext/startup/leaderboard.py b/erpnext/startup/leaderboard.py
index 8ae70d2..da7edbf 100644
--- a/erpnext/startup/leaderboard.py
+++ b/erpnext/startup/leaderboard.py
@@ -6,66 +6,66 @@
 	leaderboards = {
 		"Customer": {
 			"fields": [
-				{'fieldname': 'total_sales_amount', 'fieldtype': 'Currency'},
-				'total_qty_sold',
-				{'fieldname': 'outstanding_amount', 'fieldtype': 'Currency'}
+				{"fieldname": "total_sales_amount", "fieldtype": "Currency"},
+				"total_qty_sold",
+				{"fieldname": "outstanding_amount", "fieldtype": "Currency"},
 			],
 			"method": "erpnext.startup.leaderboard.get_all_customers",
-			"icon": "customer"
+			"icon": "customer",
 		},
 		"Item": {
 			"fields": [
-				{'fieldname': 'total_sales_amount', 'fieldtype': 'Currency'},
-				'total_qty_sold',
-				{'fieldname': 'total_purchase_amount', 'fieldtype': 'Currency'},
-				'total_qty_purchased',
-				'available_stock_qty',
-				{'fieldname': 'available_stock_value', 'fieldtype': 'Currency'}
+				{"fieldname": "total_sales_amount", "fieldtype": "Currency"},
+				"total_qty_sold",
+				{"fieldname": "total_purchase_amount", "fieldtype": "Currency"},
+				"total_qty_purchased",
+				"available_stock_qty",
+				{"fieldname": "available_stock_value", "fieldtype": "Currency"},
 			],
 			"method": "erpnext.startup.leaderboard.get_all_items",
-			"icon": "stock"
+			"icon": "stock",
 		},
 		"Supplier": {
 			"fields": [
-				{'fieldname': 'total_purchase_amount', 'fieldtype': 'Currency'},
-				'total_qty_purchased',
-				{'fieldname': 'outstanding_amount', 'fieldtype': 'Currency'}
+				{"fieldname": "total_purchase_amount", "fieldtype": "Currency"},
+				"total_qty_purchased",
+				{"fieldname": "outstanding_amount", "fieldtype": "Currency"},
 			],
 			"method": "erpnext.startup.leaderboard.get_all_suppliers",
-			"icon": "buying"
+			"icon": "buying",
 		},
 		"Sales Partner": {
 			"fields": [
-				{'fieldname': 'total_sales_amount', 'fieldtype': 'Currency'},
-				{'fieldname': 'total_commission', 'fieldtype': 'Currency'}
+				{"fieldname": "total_sales_amount", "fieldtype": "Currency"},
+				{"fieldname": "total_commission", "fieldtype": "Currency"},
 			],
 			"method": "erpnext.startup.leaderboard.get_all_sales_partner",
-			"icon": "hr"
+			"icon": "hr",
 		},
 		"Sales Person": {
-			"fields": [
-				{'fieldname': 'total_sales_amount', 'fieldtype': 'Currency'}
-			],
+			"fields": [{"fieldname": "total_sales_amount", "fieldtype": "Currency"}],
 			"method": "erpnext.startup.leaderboard.get_all_sales_person",
-			"icon": "customer"
-		}
+			"icon": "customer",
+		},
 	}
 
 	return leaderboards
 
+
 @frappe.whitelist()
-def get_all_customers(date_range, company, field, limit = None):
+def get_all_customers(date_range, company, field, limit=None):
 	if field == "outstanding_amount":
-		filters = [['docstatus', '=', '1'], ['company', '=', company]]
+		filters = [["docstatus", "=", "1"], ["company", "=", company]]
 		if date_range:
 			date_range = frappe.parse_json(date_range)
-			filters.append(['posting_date', '>=', 'between', [date_range[0], date_range[1]]])
-		return frappe.db.get_all('Sales Invoice',
-			fields = ['customer as name', 'sum(outstanding_amount) as value'],
-			filters = filters,
-			group_by = 'customer',
-			order_by = 'value desc',
-			limit = limit
+			filters.append(["posting_date", ">=", "between", [date_range[0], date_range[1]]])
+		return frappe.db.get_all(
+			"Sales Invoice",
+			fields=["customer as name", "sum(outstanding_amount) as value"],
+			filters=filters,
+			group_by="customer",
+			order_by="value desc",
+			limit=limit,
 		)
 	else:
 		if field == "total_sales_amount":
@@ -73,9 +73,10 @@
 		elif field == "total_qty_sold":
 			select_field = "sum(so_item.stock_qty)"
 
-		date_condition = get_date_condition(date_range, 'so.transaction_date')
+		date_condition = get_date_condition(date_range, "so.transaction_date")
 
-		return frappe.db.sql("""
+		return frappe.db.sql(
+			"""
 			select so.customer as name, {0} as value
 			FROM `tabSales Order` as so JOIN `tabSales Order Item` as so_item
 				ON so.name = so_item.parent
@@ -83,17 +84,24 @@
 			group by so.customer
 			order by value DESC
 			limit %s
-		""".format(select_field, date_condition), (company, cint(limit)), as_dict=1)
+		""".format(
+				select_field, date_condition
+			),
+			(company, cint(limit)),
+			as_dict=1,
+		)
+
 
 @frappe.whitelist()
-def get_all_items(date_range, company, field, limit = None):
+def get_all_items(date_range, company, field, limit=None):
 	if field in ("available_stock_qty", "available_stock_value"):
-		select_field = "sum(actual_qty)" if field=="available_stock_qty" else "sum(stock_value)"
-		return frappe.db.get_all('Bin',
-			fields = ['item_code as name', '{0} as value'.format(select_field)],
-			group_by = 'item_code',
-			order_by = 'value desc',
-			limit = limit
+		select_field = "sum(actual_qty)" if field == "available_stock_qty" else "sum(stock_value)"
+		return frappe.db.get_all(
+			"Bin",
+			fields=["item_code as name", "{0} as value".format(select_field)],
+			group_by="item_code",
+			order_by="value desc",
+			limit=limit,
 		)
 	else:
 		if field == "total_sales_amount":
@@ -109,9 +117,10 @@
 			select_field = "sum(order_item.stock_qty)"
 			select_doctype = "Purchase Order"
 
-		date_condition = get_date_condition(date_range, 'sales_order.transaction_date')
+		date_condition = get_date_condition(date_range, "sales_order.transaction_date")
 
-		return frappe.db.sql("""
+		return frappe.db.sql(
+			"""
 			select order_item.item_code as name, {0} as value
 			from `tab{1}` sales_order join `tab{1} Item` as order_item
 				on sales_order.name = order_item.parent
@@ -120,21 +129,28 @@
 			group by order_item.item_code
 			order by value desc
 			limit %s
-		""".format(select_field, select_doctype, date_condition), (company, cint(limit)), as_dict=1) #nosec
+		""".format(
+				select_field, select_doctype, date_condition
+			),
+			(company, cint(limit)),
+			as_dict=1,
+		)  # nosec
+
 
 @frappe.whitelist()
-def get_all_suppliers(date_range, company, field, limit = None):
+def get_all_suppliers(date_range, company, field, limit=None):
 	if field == "outstanding_amount":
-		filters = [['docstatus', '=', '1'], ['company', '=', company]]
+		filters = [["docstatus", "=", "1"], ["company", "=", company]]
 		if date_range:
 			date_range = frappe.parse_json(date_range)
-			filters.append(['posting_date', 'between', [date_range[0], date_range[1]]])
-		return frappe.db.get_all('Purchase Invoice',
-			fields = ['supplier as name', 'sum(outstanding_amount) as value'],
-			filters = filters,
-			group_by = 'supplier',
-			order_by = 'value desc',
-			limit = limit
+			filters.append(["posting_date", "between", [date_range[0], date_range[1]]])
+		return frappe.db.get_all(
+			"Purchase Invoice",
+			fields=["supplier as name", "sum(outstanding_amount) as value"],
+			filters=filters,
+			group_by="supplier",
+			order_by="value desc",
+			limit=limit,
 		)
 	else:
 		if field == "total_purchase_amount":
@@ -142,9 +158,10 @@
 		elif field == "total_qty_purchased":
 			select_field = "sum(purchase_order_item.stock_qty)"
 
-		date_condition = get_date_condition(date_range, 'purchase_order.modified')
+		date_condition = get_date_condition(date_range, "purchase_order.modified")
 
-		return frappe.db.sql("""
+		return frappe.db.sql(
+			"""
 			select purchase_order.supplier as name, {0} as value
 			FROM `tabPurchase Order` as purchase_order LEFT JOIN `tabPurchase Order Item`
 				as purchase_order_item ON purchase_order.name = purchase_order_item.parent
@@ -154,34 +171,45 @@
 				and  purchase_order.company = %s
 			group by purchase_order.supplier
 			order by value DESC
-			limit %s""".format(select_field, date_condition), (company, cint(limit)), as_dict=1) #nosec
+			limit %s""".format(
+				select_field, date_condition
+			),
+			(company, cint(limit)),
+			as_dict=1,
+		)  # nosec
+
 
 @frappe.whitelist()
-def get_all_sales_partner(date_range, company, field, limit = None):
+def get_all_sales_partner(date_range, company, field, limit=None):
 	if field == "total_sales_amount":
 		select_field = "sum(`base_net_total`)"
 	elif field == "total_commission":
 		select_field = "sum(`total_commission`)"
 
-	filters = {
-		'sales_partner': ['!=', ''],
-		'docstatus': 1,
-		'company': company
-	}
+	filters = {"sales_partner": ["!=", ""], "docstatus": 1, "company": company}
 	if date_range:
 		date_range = frappe.parse_json(date_range)
-		filters['transaction_date'] = ['between', [date_range[0], date_range[1]]]
+		filters["transaction_date"] = ["between", [date_range[0], date_range[1]]]
 
-	return frappe.get_list('Sales Order', fields=[
-		'`sales_partner` as name',
-		'{} as value'.format(select_field),
-	], filters=filters, group_by='sales_partner', order_by='value DESC', limit=limit)
+	return frappe.get_list(
+		"Sales Order",
+		fields=[
+			"`sales_partner` as name",
+			"{} as value".format(select_field),
+		],
+		filters=filters,
+		group_by="sales_partner",
+		order_by="value DESC",
+		limit=limit,
+	)
+
 
 @frappe.whitelist()
-def get_all_sales_person(date_range, company, field = None, limit = 0):
-	date_condition = get_date_condition(date_range, 'sales_order.transaction_date')
+def get_all_sales_person(date_range, company, field=None, limit=0):
+	date_condition = get_date_condition(date_range, "sales_order.transaction_date")
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select sales_team.sales_person as name, sum(sales_order.base_net_total) as value
 		from `tabSales Order` as sales_order join `tabSales Team` as sales_team
 			on sales_order.name = sales_team.parent and sales_team.parenttype = 'Sales Order'
@@ -191,10 +219,16 @@
 		group by sales_team.sales_person
 		order by value DESC
 		limit %s
-	""".format(date_condition=date_condition), (company, cint(limit)), as_dict=1)
+	""".format(
+			date_condition=date_condition
+		),
+		(company, cint(limit)),
+		as_dict=1,
+	)
+
 
 def get_date_condition(date_range, field):
-	date_condition = ''
+	date_condition = ""
 	if date_range:
 		date_range = frappe.parse_json(date_range)
 		from_date, to_date = date_range
diff --git a/erpnext/startup/notifications.py b/erpnext/startup/notifications.py
index 0965ead..76cb91a 100644
--- a/erpnext/startup/notifications.py
+++ b/erpnext/startup/notifications.py
@@ -6,8 +6,8 @@
 
 
 def get_notification_config():
-	notifications =  { "for_doctype":
-		{
+	notifications = {
+		"for_doctype": {
 			"Issue": {"status": "Open"},
 			"Warranty Claim": {"status": "Open"},
 			"Task": {"status": ("in", ("Open", "Overdue"))},
@@ -16,66 +16,46 @@
 			"Contact": {"status": "Open"},
 			"Opportunity": {"status": "Open"},
 			"Quotation": {"docstatus": 0},
-			"Sales Order": {
-				"status": ("not in", ("Completed", "Closed")),
-				"docstatus": ("<", 2)
-			},
+			"Sales Order": {"status": ("not in", ("Completed", "Closed")), "docstatus": ("<", 2)},
 			"Journal Entry": {"docstatus": 0},
-			"Sales Invoice": {
-				"outstanding_amount": (">", 0),
-				"docstatus": ("<", 2)
-			},
-			"Purchase Invoice": {
-				"outstanding_amount": (">", 0),
-				"docstatus": ("<", 2)
-			},
+			"Sales Invoice": {"outstanding_amount": (">", 0), "docstatus": ("<", 2)},
+			"Purchase Invoice": {"outstanding_amount": (">", 0), "docstatus": ("<", 2)},
 			"Payment Entry": {"docstatus": 0},
 			"Leave Application": {"docstatus": 0},
 			"Expense Claim": {"docstatus": 0},
 			"Job Applicant": {"status": "Open"},
-			"Delivery Note": {
-				"status": ("not in", ("Completed", "Closed")),
-				"docstatus": ("<", 2)
-			},
+			"Delivery Note": {"status": ("not in", ("Completed", "Closed")), "docstatus": ("<", 2)},
 			"Stock Entry": {"docstatus": 0},
 			"Material Request": {
 				"docstatus": ("<", 2),
 				"status": ("not in", ("Stopped",)),
-				"per_ordered": ("<", 100)
+				"per_ordered": ("<", 100),
 			},
-			"Request for Quotation": { "docstatus": 0 },
+			"Request for Quotation": {"docstatus": 0},
 			"Supplier Quotation": {"docstatus": 0},
-			"Purchase Order": {
-				"status": ("not in", ("Completed", "Closed")),
-				"docstatus": ("<", 2)
-			},
-			"Purchase Receipt": {
-				"status": ("not in", ("Completed", "Closed")),
-				"docstatus": ("<", 2)
-			},
-			"Work Order": { "status": ("in", ("Draft", "Not Started", "In Process")) },
+			"Purchase Order": {"status": ("not in", ("Completed", "Closed")), "docstatus": ("<", 2)},
+			"Purchase Receipt": {"status": ("not in", ("Completed", "Closed")), "docstatus": ("<", 2)},
+			"Work Order": {"status": ("in", ("Draft", "Not Started", "In Process"))},
 			"BOM": {"docstatus": 0},
-
 			"Timesheet": {"status": "Draft"},
-
 			"Lab Test": {"docstatus": 0},
 			"Sample Collection": {"docstatus": 0},
 			"Patient Appointment": {"status": "Open"},
-			"Patient Encounter": {"docstatus": 0}
+			"Patient Encounter": {"docstatus": 0},
 		},
-
 		"targets": {
 			"Company": {
-				"filters" : { "monthly_sales_target": ( ">", 0 ) },
-				"target_field" : "monthly_sales_target",
-				"value_field" : "total_monthly_sales"
+				"filters": {"monthly_sales_target": (">", 0)},
+				"target_field": "monthly_sales_target",
+				"value_field": "total_monthly_sales",
 			}
-		}
+		},
 	}
 
-	doctype = [d for d in notifications.get('for_doctype')]
-	for doc in frappe.get_all('DocType',
-		fields= ["name"], filters = {"name": ("not in", doctype), 'is_submittable': 1}):
+	doctype = [d for d in notifications.get("for_doctype")]
+	for doc in frappe.get_all(
+		"DocType", fields=["name"], filters={"name": ("not in", doctype), "is_submittable": 1}
+	):
 		notifications["for_doctype"][doc.name] = {"docstatus": 0}
 
 	return notifications
diff --git a/erpnext/startup/report_data_map.py b/erpnext/startup/report_data_map.py
index 65b48bf..f8c1b6c 100644
--- a/erpnext/startup/report_data_map.py
+++ b/erpnext/startup/report_data_map.py
@@ -6,90 +6,98 @@
 # "remember to add indexes!"
 
 data_map = {
-	"Company": {
-		"columns": ["name"],
-		"conditions": ["docstatus < 2"]
-	},
+	"Company": {"columns": ["name"], "conditions": ["docstatus < 2"]},
 	"Fiscal Year": {
 		"columns": ["name", "year_start_date", "year_end_date"],
 		"conditions": ["docstatus < 2"],
 	},
-
 	# Accounts
 	"Account": {
-		"columns": ["name", "parent_account", "lft", "rgt", "report_type",
-			"company", "is_group"],
+		"columns": ["name", "parent_account", "lft", "rgt", "report_type", "company", "is_group"],
 		"conditions": ["docstatus < 2"],
 		"order_by": "lft",
 		"links": {
 			"company": ["Company", "name"],
-		}
-
+		},
 	},
 	"Cost Center": {
 		"columns": ["name", "lft", "rgt"],
 		"conditions": ["docstatus < 2"],
-		"order_by": "lft"
+		"order_by": "lft",
 	},
 	"GL Entry": {
-		"columns": ["name", "account", "posting_date", "cost_center", "debit", "credit",
-			"is_opening", "company", "voucher_type", "voucher_no", "remarks"],
+		"columns": [
+			"name",
+			"account",
+			"posting_date",
+			"cost_center",
+			"debit",
+			"credit",
+			"is_opening",
+			"company",
+			"voucher_type",
+			"voucher_no",
+			"remarks",
+		],
 		"order_by": "posting_date, account",
 		"links": {
 			"account": ["Account", "name"],
 			"company": ["Company", "name"],
-			"cost_center": ["Cost Center", "name"]
-		}
+			"cost_center": ["Cost Center", "name"],
+		},
 	},
-
 	# Stock
 	"Item": {
-		"columns": ["name", "if(item_name=name, '', item_name) as item_name", "description",
-			"item_group as parent_item_group", "stock_uom", "brand", "valuation_method"],
+		"columns": [
+			"name",
+			"if(item_name=name, '', item_name) as item_name",
+			"description",
+			"item_group as parent_item_group",
+			"stock_uom",
+			"brand",
+			"valuation_method",
+		],
 		# "conditions": ["docstatus < 2"],
 		"order_by": "name",
-		"links": {
-			"parent_item_group": ["Item Group", "name"],
-			"brand": ["Brand", "name"]
-		}
+		"links": {"parent_item_group": ["Item Group", "name"], "brand": ["Brand", "name"]},
 	},
 	"Item Group": {
 		"columns": ["name", "parent_item_group"],
 		# "conditions": ["docstatus < 2"],
-		"order_by": "lft"
+		"order_by": "lft",
 	},
-	"Brand": {
-		"columns": ["name"],
-		"conditions": ["docstatus < 2"],
-		"order_by": "name"
-	},
-	"Project": {
-		"columns": ["name"],
-		"conditions": ["docstatus < 2"],
-		"order_by": "name"
-	},
-	"Warehouse": {
-		"columns": ["name"],
-		"conditions": ["docstatus < 2"],
-		"order_by": "name"
-	},
+	"Brand": {"columns": ["name"], "conditions": ["docstatus < 2"], "order_by": "name"},
+	"Project": {"columns": ["name"], "conditions": ["docstatus < 2"], "order_by": "name"},
+	"Warehouse": {"columns": ["name"], "conditions": ["docstatus < 2"], "order_by": "name"},
 	"Stock Ledger Entry": {
-		"columns": ["name", "posting_date", "posting_time", "item_code", "warehouse",
-			"actual_qty as qty", "voucher_type", "voucher_no", "project",
-			"incoming_rate as incoming_rate", "stock_uom", "serial_no",
-			"qty_after_transaction", "valuation_rate"],
+		"columns": [
+			"name",
+			"posting_date",
+			"posting_time",
+			"item_code",
+			"warehouse",
+			"actual_qty as qty",
+			"voucher_type",
+			"voucher_no",
+			"project",
+			"incoming_rate as incoming_rate",
+			"stock_uom",
+			"serial_no",
+			"qty_after_transaction",
+			"valuation_rate",
+		],
 		"order_by": "posting_date, posting_time, creation",
 		"links": {
 			"item_code": ["Item", "name"],
 			"warehouse": ["Warehouse", "name"],
-			"project": ["Project", "name"]
+			"project": ["Project", "name"],
 		},
-		"force_index": "posting_sort_index"
+		"force_index": "posting_sort_index",
 	},
 	"Serial No": {
 		"columns": ["name", "purchase_rate as incoming_rate"],
 		"conditions": ["docstatus < 2"],
-		"order_by": "name"
+		"order_by": "name",
 	},
 	"Stock Entry": {
 		"columns": ["name", "purpose"],
@@ -97,227 +105,223 @@
 		"order_by": "posting_date, posting_time, name",
 	},
 	"Material Request Item": {
-		"columns": ["item.name as name", "item_code", "warehouse",
-			"(qty - ordered_qty) as qty"],
+		"columns": ["item.name as name", "item_code", "warehouse", "(qty - ordered_qty) as qty"],
 		"from": "`tabMaterial Request Item` item, `tabMaterial Request` main",
-		"conditions": ["item.parent = main.name", "main.docstatus=1", "main.status != 'Stopped'",
-			"ifnull(warehouse, '')!=''", "qty > ordered_qty"],
-		"links": {
-			"item_code": ["Item", "name"],
-			"warehouse": ["Warehouse", "name"]
-		},
+		"conditions": [
+			"item.parent = main.name",
+			"main.docstatus=1",
+			"main.status != 'Stopped'",
+			"ifnull(warehouse, '')!=''",
+			"qty > ordered_qty",
+		],
+		"links": {"item_code": ["Item", "name"], "warehouse": ["Warehouse", "name"]},
 	},
 	"Purchase Order Item": {
-		"columns": ["item.name as name", "item_code", "warehouse",
-			"(qty - received_qty)*conversion_factor as qty"],
+		"columns": [
+			"item.name as name",
+			"item_code",
+			"warehouse",
+			"(qty - received_qty)*conversion_factor as qty",
+		],
 		"from": "`tabPurchase Order Item` item, `tabPurchase Order` main",
-		"conditions": ["item.parent = main.name", "main.docstatus=1", "main.status != 'Stopped'",
-			"ifnull(warehouse, '')!=''", "qty > received_qty"],
-		"links": {
-			"item_code": ["Item", "name"],
-			"warehouse": ["Warehouse", "name"]
-		},
+		"conditions": [
+			"item.parent = main.name",
+			"main.docstatus=1",
+			"main.status != 'Stopped'",
+			"ifnull(warehouse, '')!=''",
+			"qty > received_qty",
+		],
+		"links": {"item_code": ["Item", "name"], "warehouse": ["Warehouse", "name"]},
 	},
-
 	"Sales Order Item": {
-		"columns": ["item.name as name", "item_code", "(qty - delivered_qty)*conversion_factor as qty", "warehouse"],
+		"columns": [
+			"item.name as name",
+			"item_code",
+			"(qty - delivered_qty)*conversion_factor as qty",
+			"warehouse",
+		],
 		"from": "`tabSales Order Item` item, `tabSales Order` main",
-		"conditions": ["item.parent = main.name", "main.docstatus=1", "main.status != 'Stopped'",
-			"ifnull(warehouse, '')!=''", "qty > delivered_qty"],
-		"links": {
-			"item_code": ["Item", "name"],
-			"warehouse": ["Warehouse", "name"]
-		},
+		"conditions": [
+			"item.parent = main.name",
+			"main.docstatus=1",
+			"main.status != 'Stopped'",
+			"ifnull(warehouse, '')!=''",
+			"qty > delivered_qty",
+		],
+		"links": {"item_code": ["Item", "name"], "warehouse": ["Warehouse", "name"]},
 	},
-
 	# Sales
 	"Customer": {
-		"columns": ["name", "if(customer_name=name, '', customer_name) as customer_name",
-			"customer_group as parent_customer_group", "territory as parent_territory"],
+		"columns": [
+			"name",
+			"if(customer_name=name, '', customer_name) as customer_name",
+			"customer_group as parent_customer_group",
+			"territory as parent_territory",
+		],
 		"conditions": ["docstatus < 2"],
 		"order_by": "name",
 		"links": {
 			"parent_customer_group": ["Customer Group", "name"],
 			"parent_territory": ["Territory", "name"],
-		}
+		},
 	},
 	"Customer Group": {
 		"columns": ["name", "parent_customer_group"],
 		"conditions": ["docstatus < 2"],
-		"order_by": "lft"
+		"order_by": "lft",
 	},
 	"Territory": {
 		"columns": ["name", "parent_territory"],
 		"conditions": ["docstatus < 2"],
-		"order_by": "lft"
+		"order_by": "lft",
 	},
 	"Sales Invoice": {
 		"columns": ["name", "customer", "posting_date", "company"],
 		"conditions": ["docstatus=1"],
 		"order_by": "posting_date",
-		"links": {
-			"customer": ["Customer", "name"],
-			"company":["Company", "name"]
-		}
+		"links": {"customer": ["Customer", "name"], "company": ["Company", "name"]},
 	},
 	"Sales Invoice Item": {
 		"columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"],
 		"conditions": ["docstatus=1", "ifnull(parent, '')!=''"],
 		"order_by": "parent",
-		"links": {
-			"parent": ["Sales Invoice", "name"],
-			"item_code": ["Item", "name"]
-		}
+		"links": {"parent": ["Sales Invoice", "name"], "item_code": ["Item", "name"]},
 	},
 	"Sales Order": {
 		"columns": ["name", "customer", "transaction_date as posting_date", "company"],
 		"conditions": ["docstatus=1"],
 		"order_by": "transaction_date",
-		"links": {
-			"customer": ["Customer", "name"],
-			"company":["Company", "name"]
-		}
+		"links": {"customer": ["Customer", "name"], "company": ["Company", "name"]},
 	},
 	"Sales Order Item[Sales Analytics]": {
 		"columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"],
 		"conditions": ["docstatus=1", "ifnull(parent, '')!=''"],
 		"order_by": "parent",
-		"links": {
-			"parent": ["Sales Order", "name"],
-			"item_code": ["Item", "name"]
-		}
+		"links": {"parent": ["Sales Order", "name"], "item_code": ["Item", "name"]},
 	},
 	"Delivery Note": {
 		"columns": ["name", "customer", "posting_date", "company"],
 		"conditions": ["docstatus=1"],
 		"order_by": "posting_date",
-		"links": {
-			"customer": ["Customer", "name"],
-			"company":["Company", "name"]
-		}
+		"links": {"customer": ["Customer", "name"], "company": ["Company", "name"]},
 	},
 	"Delivery Note Item[Sales Analytics]": {
 		"columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"],
 		"conditions": ["docstatus=1", "ifnull(parent, '')!=''"],
 		"order_by": "parent",
-		"links": {
-			"parent": ["Delivery Note", "name"],
-			"item_code": ["Item", "name"]
-		}
+		"links": {"parent": ["Delivery Note", "name"], "item_code": ["Item", "name"]},
 	},
 	"Supplier": {
-		"columns": ["name", "if(supplier_name=name, '', supplier_name) as supplier_name",
-			"supplier_group as parent_supplier_group"],
+		"columns": [
+			"name",
+			"if(supplier_name=name, '', supplier_name) as supplier_name",
+			"supplier_group as parent_supplier_group",
+		],
 		"conditions": ["docstatus < 2"],
 		"order_by": "name",
 		"links": {
 			"parent_supplier_group": ["Supplier Group", "name"],
-		}
+		},
 	},
 	"Supplier Group": {
 		"columns": ["name", "parent_supplier_group"],
 		"conditions": ["docstatus < 2"],
-		"order_by": "name"
+		"order_by": "name",
 	},
 	"Purchase Invoice": {
 		"columns": ["name", "supplier", "posting_date", "company"],
 		"conditions": ["docstatus=1"],
 		"order_by": "posting_date",
-		"links": {
-			"supplier": ["Supplier", "name"],
-			"company":["Company", "name"]
-		}
+		"links": {"supplier": ["Supplier", "name"], "company": ["Company", "name"]},
 	},
 	"Purchase Invoice Item": {
 		"columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"],
 		"conditions": ["docstatus=1", "ifnull(parent, '')!=''"],
 		"order_by": "parent",
-		"links": {
-			"parent": ["Purchase Invoice", "name"],
-			"item_code": ["Item", "name"]
-		}
+		"links": {"parent": ["Purchase Invoice", "name"], "item_code": ["Item", "name"]},
 	},
 	"Purchase Order": {
 		"columns": ["name", "supplier", "transaction_date as posting_date", "company"],
 		"conditions": ["docstatus=1"],
 		"order_by": "posting_date",
-		"links": {
-			"supplier": ["Supplier", "name"],
-			"company":["Company", "name"]
-		}
+		"links": {"supplier": ["Supplier", "name"], "company": ["Company", "name"]},
 	},
 	"Purchase Order Item[Purchase Analytics]": {
 		"columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"],
 		"conditions": ["docstatus=1", "ifnull(parent, '')!=''"],
 		"order_by": "parent",
-		"links": {
-			"parent": ["Purchase Order", "name"],
-			"item_code": ["Item", "name"]
-		}
+		"links": {"parent": ["Purchase Order", "name"], "item_code": ["Item", "name"]},
 	},
 	"Purchase Receipt": {
 		"columns": ["name", "supplier", "posting_date", "company"],
 		"conditions": ["docstatus=1"],
 		"order_by": "posting_date",
-		"links": {
-			"supplier": ["Supplier", "name"],
-			"company":["Company", "name"]
-		}
+		"links": {"supplier": ["Supplier", "name"], "company": ["Company", "name"]},
 	},
 	"Purchase Receipt Item[Purchase Analytics]": {
 		"columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"],
 		"conditions": ["docstatus=1", "ifnull(parent, '')!=''"],
 		"order_by": "parent",
-		"links": {
-			"parent": ["Purchase Receipt", "name"],
-			"item_code": ["Item", "name"]
-		}
+		"links": {"parent": ["Purchase Receipt", "name"], "item_code": ["Item", "name"]},
 	},
 	# Support
 	"Issue": {
-		"columns": ["name","status","creation","resolution_date","first_responded_on"],
+		"columns": ["name", "status", "creation", "resolution_date", "first_responded_on"],
 		"conditions": ["docstatus < 2"],
-		"order_by": "creation"
+		"order_by": "creation",
 	},
-
 	# Manufacturing
 	"Work Order": {
-		"columns": ["name","status","creation","planned_start_date","planned_end_date","status","actual_start_date","actual_end_date", "modified"],
+		"columns": [
+			"name",
+			"status",
+			"creation",
+			"planned_start_date",
+			"planned_end_date",
+			"status",
+			"actual_start_date",
+			"actual_end_date",
+			"modified",
+		],
 		"conditions": ["docstatus = 1"],
-		"order_by": "creation"
+		"order_by": "creation",
 	},
-
-	#Medical
+	# Medical
 	"Patient": {
-		"columns": ["name", "creation", "owner", "if(patient_name=name, '', patient_name) as patient_name"],
+		"columns": [
+			"name",
+			"creation",
+			"owner",
+			"if(patient_name=name, '', patient_name) as patient_name",
+		],
 		"conditions": ["docstatus < 2"],
 		"order_by": "name",
-		"links": {
-			"owner" : ["User", "name"]
-		}
+		"links": {"owner": ["User", "name"]},
 	},
 	"Patient Appointment": {
-		"columns": ["name", "appointment_type", "patient", "practitioner", "appointment_date", "department", "status", "company"],
+		"columns": [
+			"name",
+			"appointment_type",
+			"patient",
+			"practitioner",
+			"appointment_date",
+			"department",
+			"status",
+			"company",
+		],
 		"order_by": "name",
 		"links": {
 			"practitioner": ["Healthcare Practitioner", "name"],
-			"appointment_type": ["Appointment Type", "name"]
-		}
+			"appointment_type": ["Appointment Type", "name"],
+		},
 	},
 	"Healthcare Practitioner": {
 		"columns": ["name", "department"],
 		"order_by": "name",
 		"links": {
 			"department": ["Department", "name"],
-		}
-
+		},
 	},
-	"Appointment Type": {
-		"columns": ["name"],
-		"order_by": "name"
-	},
-	"Medical Department": {
-		"columns": ["name"],
-		"order_by": "name"
-	}
+	"Appointment Type": {"columns": ["name"], "order_by": "name"},
+	"Medical Department": {"columns": ["name"], "order_by": "name"},
 }
diff --git a/erpnext/stock/__init__.py b/erpnext/stock/__init__.py
index e8b2804..45bf012 100644
--- a/erpnext/stock/__init__.py
+++ b/erpnext/stock/__init__.py
@@ -2,17 +2,24 @@
 from frappe import _
 
 install_docs = [
-	{"doctype":"Role", "role_name":"Stock Manager", "name":"Stock Manager"},
-	{"doctype":"Role", "role_name":"Item Manager", "name":"Item Manager"},
-	{"doctype":"Role", "role_name":"Stock User", "name":"Stock User"},
-	{"doctype":"Role", "role_name":"Quality Manager", "name":"Quality Manager"},
-	{"doctype":"Item Group", "item_group_name":"All Item Groups", "is_group": 1},
-	{"doctype":"Item Group", "item_group_name":"Default",
-		"parent_item_group":"All Item Groups", "is_group": 0},
+	{"doctype": "Role", "role_name": "Stock Manager", "name": "Stock Manager"},
+	{"doctype": "Role", "role_name": "Item Manager", "name": "Item Manager"},
+	{"doctype": "Role", "role_name": "Stock User", "name": "Stock User"},
+	{"doctype": "Role", "role_name": "Quality Manager", "name": "Quality Manager"},
+	{"doctype": "Item Group", "item_group_name": "All Item Groups", "is_group": 1},
+	{
+		"doctype": "Item Group",
+		"item_group_name": "Default",
+		"parent_item_group": "All Item Groups",
+		"is_group": 0,
+	},
 ]
 
+
 def get_warehouse_account_map(company=None):
-	company_warehouse_account_map = company and frappe.flags.setdefault('warehouse_account_map', {}).get(company)
+	company_warehouse_account_map = company and frappe.flags.setdefault(
+		"warehouse_account_map", {}
+	).get(company)
 	warehouse_account_map = frappe.flags.warehouse_account_map
 
 	if not warehouse_account_map or not company_warehouse_account_map or frappe.flags.in_test:
@@ -20,18 +27,20 @@
 
 		filters = {}
 		if company:
-			filters['company'] = company
-			frappe.flags.setdefault('warehouse_account_map', {}).setdefault(company, {})
+			filters["company"] = company
+			frappe.flags.setdefault("warehouse_account_map", {}).setdefault(company, {})
 
-		for d in frappe.get_all('Warehouse',
-			fields = ["name", "account", "parent_warehouse", "company", "is_group"],
-			filters = filters,
-			order_by="lft, rgt"):
+		for d in frappe.get_all(
+			"Warehouse",
+			fields=["name", "account", "parent_warehouse", "company", "is_group"],
+			filters=filters,
+			order_by="lft, rgt",
+		):
 			if not d.account:
 				d.account = get_warehouse_account(d, warehouse_account)
 
 			if d.account:
-				d.account_currency = frappe.db.get_value('Account', d.account, 'account_currency', cache=True)
+				d.account_currency = frappe.db.get_value("Account", d.account, "account_currency", cache=True)
 				warehouse_account.setdefault(d.name, d)
 		if company:
 			frappe.flags.warehouse_account_map[company] = warehouse_account
@@ -40,6 +49,7 @@
 
 	return frappe.flags.warehouse_account_map.get(company) or frappe.flags.warehouse_account_map
 
+
 def get_warehouse_account(warehouse, warehouse_account=None):
 	account = warehouse.account
 	if not account and warehouse.parent_warehouse:
@@ -48,15 +58,20 @@
 				account = warehouse_account.get(warehouse.parent_warehouse).account
 			else:
 				from frappe.utils.nestedset import rebuild_tree
+
 				rebuild_tree("Warehouse", "parent_warehouse")
 		else:
-			account = frappe.db.sql("""
+			account = frappe.db.sql(
+				"""
 				select
 					account from `tabWarehouse`
 				where
 					lft <= %s and rgt >= %s and company = %s
 					and account is not null and ifnull(account, '') !=''
-				order by lft desc limit 1""", (warehouse.lft, warehouse.rgt, warehouse.company), as_list=1)
+				order by lft desc limit 1""",
+				(warehouse.lft, warehouse.rgt, warehouse.company),
+				as_list=1,
+			)
 
 			account = account[0][0] if account else None
 
@@ -64,13 +79,18 @@
 		account = get_company_default_inventory_account(warehouse.company)
 
 	if not account and warehouse.company:
-		account = frappe.db.get_value('Account',
-			{'account_type': 'Stock', 'is_group': 0, 'company': warehouse.company}, 'name')
+		account = frappe.db.get_value(
+			"Account", {"account_type": "Stock", "is_group": 0, "company": warehouse.company}, "name"
+		)
 
 	if not account and warehouse.company and not warehouse.is_group:
-		frappe.throw(_("Please set Account in Warehouse {0} or Default Inventory Account in Company {1}")
-			.format(warehouse.name, warehouse.company))
+		frappe.throw(
+			_("Please set Account in Warehouse {0} or Default Inventory Account in Company {1}").format(
+				warehouse.name, warehouse.company
+			)
+		)
 	return account
 
+
 def get_company_default_inventory_account(company):
-	return frappe.get_cached_value('Company',  company, 'default_inventory_account')
+	return frappe.get_cached_value("Company", company, "default_inventory_account")
diff --git a/erpnext/stock/dashboard/item_dashboard.html b/erpnext/stock/dashboard/item_dashboard.html
index 99698ba..b7a786e 100644
--- a/erpnext/stock/dashboard/item_dashboard.html
+++ b/erpnext/stock/dashboard/item_dashboard.html
@@ -1,4 +1,4 @@
-<div>
+<div class="stock-levels">
 	<div class="result">
 	</div>
 	<div class="more hidden" style="padding: 15px;">
diff --git a/erpnext/stock/dashboard/item_dashboard.py b/erpnext/stock/dashboard/item_dashboard.py
index 57d78a2..8fbb56c 100644
--- a/erpnext/stock/dashboard/item_dashboard.py
+++ b/erpnext/stock/dashboard/item_dashboard.py
@@ -4,58 +4,72 @@
 
 
 @frappe.whitelist()
-def get_data(item_code=None, warehouse=None, item_group=None,
-	start=0, sort_by='actual_qty', sort_order='desc'):
-	'''Return data to render the item dashboard'''
+def get_data(
+	item_code=None, warehouse=None, item_group=None, start=0, sort_by="actual_qty", sort_order="desc"
+):
+	"""Return data to render the item dashboard"""
 	filters = []
 	if item_code:
-		filters.append(['item_code', '=', item_code])
+		filters.append(["item_code", "=", item_code])
 	if warehouse:
-		filters.append(['warehouse', '=', warehouse])
+		filters.append(["warehouse", "=", warehouse])
 	if item_group:
 		lft, rgt = frappe.db.get_value("Item Group", item_group, ["lft", "rgt"])
-		items = frappe.db.sql_list("""
+		items = frappe.db.sql_list(
+			"""
 			select i.name from `tabItem` i
 			where exists(select name from `tabItem Group`
 				where name=i.item_group and lft >=%s and rgt<=%s)
-		""", (lft, rgt))
-		filters.append(['item_code', 'in', items])
+		""",
+			(lft, rgt),
+		)
+		filters.append(["item_code", "in", items])
 	try:
 		# check if user has any restrictions based on user permissions on warehouse
-		if DatabaseQuery('Warehouse', user=frappe.session.user).build_match_conditions():
-			filters.append(['warehouse', 'in', [w.name for w in frappe.get_list('Warehouse')]])
+		if DatabaseQuery("Warehouse", user=frappe.session.user).build_match_conditions():
+			filters.append(["warehouse", "in", [w.name for w in frappe.get_list("Warehouse")]])
 	except frappe.PermissionError:
 		# user does not have access on warehouse
 		return []
 
-	items = frappe.db.get_all('Bin', fields=['item_code', 'warehouse', 'projected_qty',
-			'reserved_qty', 'reserved_qty_for_production', 'reserved_qty_for_sub_contract', 'actual_qty', 'valuation_rate'],
+	items = frappe.db.get_all(
+		"Bin",
+		fields=[
+			"item_code",
+			"warehouse",
+			"projected_qty",
+			"reserved_qty",
+			"reserved_qty_for_production",
+			"reserved_qty_for_sub_contract",
+			"actual_qty",
+			"valuation_rate",
+		],
 		or_filters={
-			'projected_qty': ['!=', 0],
-			'reserved_qty': ['!=', 0],
-			'reserved_qty_for_production': ['!=', 0],
-			'reserved_qty_for_sub_contract': ['!=', 0],
-			'actual_qty': ['!=', 0],
+			"projected_qty": ["!=", 0],
+			"reserved_qty": ["!=", 0],
+			"reserved_qty_for_production": ["!=", 0],
+			"reserved_qty_for_sub_contract": ["!=", 0],
+			"actual_qty": ["!=", 0],
 		},
 		filters=filters,
-		order_by=sort_by + ' ' + sort_order,
+		order_by=sort_by + " " + sort_order,
 		limit_start=start,
-		limit_page_length='21')
+		limit_page_length=21,
+	)
 
 	precision = cint(frappe.db.get_single_value("System Settings", "float_precision"))
 
 	for item in items:
-		item.update({
-			'item_name': frappe.get_cached_value(
-				"Item", item.item_code, 'item_name'),
-			'disable_quick_entry': frappe.get_cached_value(
-				"Item", item.item_code, 'has_batch_no')
-			or frappe.get_cached_value(
-				"Item", item.item_code, 'has_serial_no'),
-			'projected_qty': flt(item.projected_qty, precision),
-			'reserved_qty': flt(item.reserved_qty, precision),
-			'reserved_qty_for_production': flt(item.reserved_qty_for_production, precision),
-			'reserved_qty_for_sub_contract': flt(item.reserved_qty_for_sub_contract, precision),
-			'actual_qty': flt(item.actual_qty, precision),
-		})
+		item.update(
+			{
+				"item_name": frappe.get_cached_value("Item", item.item_code, "item_name"),
+				"disable_quick_entry": frappe.get_cached_value("Item", item.item_code, "has_batch_no")
+				or frappe.get_cached_value("Item", item.item_code, "has_serial_no"),
+				"projected_qty": flt(item.projected_qty, precision),
+				"reserved_qty": flt(item.reserved_qty, precision),
+				"reserved_qty_for_production": flt(item.reserved_qty_for_production, precision),
+				"reserved_qty_for_sub_contract": flt(item.reserved_qty_for_sub_contract, precision),
+				"actual_qty": flt(item.actual_qty, precision),
+			}
+		)
 	return items
diff --git a/erpnext/stock/dashboard/warehouse_capacity_dashboard.py b/erpnext/stock/dashboard/warehouse_capacity_dashboard.py
index c0666cf..24e0ef1 100644
--- a/erpnext/stock/dashboard/warehouse_capacity_dashboard.py
+++ b/erpnext/stock/dashboard/warehouse_capacity_dashboard.py
@@ -6,8 +6,15 @@
 
 
 @frappe.whitelist()
-def get_data(item_code=None, warehouse=None, parent_warehouse=None,
-	company=None, start=0, sort_by="stock_capacity", sort_order="desc"):
+def get_data(
+	item_code=None,
+	warehouse=None,
+	parent_warehouse=None,
+	company=None,
+	start=0,
+	sort_by="stock_capacity",
+	sort_order="desc",
+):
 	"""Return data to render the warehouse capacity dashboard."""
 	filters = get_filters(item_code, warehouse, parent_warehouse, company)
 
@@ -18,51 +25,59 @@
 	capacity_data = get_warehouse_capacity_data(filters, start)
 
 	asc_desc = -1 if sort_order == "desc" else 1
-	capacity_data = sorted(capacity_data, key = lambda i: (i[sort_by] * asc_desc))
+	capacity_data = sorted(capacity_data, key=lambda i: (i[sort_by] * asc_desc))
 
 	return capacity_data
 
-def get_filters(item_code=None, warehouse=None, parent_warehouse=None,
-	company=None):
-	filters = [['disable', '=', 0]]
+
+def get_filters(item_code=None, warehouse=None, parent_warehouse=None, company=None):
+	filters = [["disable", "=", 0]]
 	if item_code:
-		filters.append(['item_code', '=', item_code])
+		filters.append(["item_code", "=", item_code])
 	if warehouse:
-		filters.append(['warehouse', '=', warehouse])
+		filters.append(["warehouse", "=", warehouse])
 	if company:
-		filters.append(['company', '=', company])
+		filters.append(["company", "=", company])
 	if parent_warehouse:
 		lft, rgt = frappe.db.get_value("Warehouse", parent_warehouse, ["lft", "rgt"])
-		warehouses = frappe.db.sql_list("""
+		warehouses = frappe.db.sql_list(
+			"""
 			select name from `tabWarehouse`
 			where lft >=%s and rgt<=%s
-		""", (lft, rgt))
-		filters.append(['warehouse', 'in', warehouses])
+		""",
+			(lft, rgt),
+		)
+		filters.append(["warehouse", "in", warehouses])
 	return filters
 
+
 def get_warehouse_filter_based_on_permissions(filters):
 	try:
 		# check if user has any restrictions based on user permissions on warehouse
-		if DatabaseQuery('Warehouse', user=frappe.session.user).build_match_conditions():
-			filters.append(['warehouse', 'in', [w.name for w in frappe.get_list('Warehouse')]])
+		if DatabaseQuery("Warehouse", user=frappe.session.user).build_match_conditions():
+			filters.append(["warehouse", "in", [w.name for w in frappe.get_list("Warehouse")]])
 		return False, filters
 	except frappe.PermissionError:
 		# user does not have access on warehouse
 		return True, []
 
+
 def get_warehouse_capacity_data(filters, start):
-	capacity_data = frappe.db.get_all('Putaway Rule',
-		fields=['item_code', 'warehouse','stock_capacity', 'company'],
+	capacity_data = frappe.db.get_all(
+		"Putaway Rule",
+		fields=["item_code", "warehouse", "stock_capacity", "company"],
 		filters=filters,
 		limit_start=start,
-		limit_page_length='11'
+		limit_page_length="11",
 	)
 
 	for entry in capacity_data:
 		balance_qty = get_stock_balance(entry.item_code, entry.warehouse, nowdate()) or 0
-		entry.update({
-			'actual_qty': balance_qty,
-			'percent_occupied': flt((flt(balance_qty) / flt(entry.stock_capacity)) * 100, 0)
-		})
+		entry.update(
+			{
+				"actual_qty": balance_qty,
+				"percent_occupied": flt((flt(balance_qty) / flt(entry.stock_capacity)) * 100, 0),
+			}
+		)
 
 	return capacity_data
diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py
index d835420..dbf6cf0 100644
--- a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py
+++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py
@@ -11,27 +11,38 @@
 
 @frappe.whitelist()
 @cache_source
-def get(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None,
-	to_date = None, timespan = None, time_interval = None, heatmap_year = None):
+def get(
+	chart_name=None,
+	chart=None,
+	no_cache=None,
+	filters=None,
+	from_date=None,
+	to_date=None,
+	timespan=None,
+	time_interval=None,
+	heatmap_year=None,
+):
 	labels, datapoints = [], []
 	filters = frappe.parse_json(filters)
 
-	warehouse_filters = [['is_group', '=', 0]]
+	warehouse_filters = [["is_group", "=", 0]]
 	if filters and filters.get("company"):
-		warehouse_filters.append(['company', '=', filters.get("company")])
+		warehouse_filters.append(["company", "=", filters.get("company")])
 
-	warehouses = frappe.get_list("Warehouse", fields=['name'], filters=warehouse_filters, order_by='name')
+	warehouses = frappe.get_list(
+		"Warehouse", fields=["name"], filters=warehouse_filters, order_by="name"
+	)
 
 	for wh in warehouses:
 		balance = get_stock_value_from_bin(warehouse=wh.name)
 		wh["balance"] = balance[0][0]
 
-	warehouses  = [x for x in warehouses if not (x.get('balance') == None)]
+	warehouses = [x for x in warehouses if not (x.get("balance") == None)]
 
 	if not warehouses:
 		return []
 
-	sorted_warehouse_map = sorted(warehouses, key = lambda i: i['balance'], reverse=True)
+	sorted_warehouse_map = sorted(warehouses, key=lambda i: i["balance"], reverse=True)
 
 	if len(sorted_warehouse_map) > 10:
 		sorted_warehouse_map = sorted_warehouse_map[:10]
@@ -40,11 +51,8 @@
 		labels.append(_(warehouse.get("name")))
 		datapoints.append(warehouse.get("balance"))
 
-	return{
+	return {
 		"labels": labels,
-		"datasets": [{
-			"name": _("Stock Value"),
-			"values": datapoints
-		}],
-		"type": "bar"
+		"datasets": [{"name": _("Stock Value"), "values": datapoints}],
+		"type": "bar",
 	}
diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py
index c9b4c14..52854a0 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -23,7 +23,7 @@
 	temp = None
 	while not temp:
 		temp = frappe.generate_hash()[:7].upper()
-		if frappe.db.exists('Batch', temp):
+		if frappe.db.exists("Batch", temp):
 			temp = None
 
 	return temp
@@ -34,7 +34,7 @@
 	Verify if the Batch is to be named using a naming series
 	:return: bool
 	"""
-	use_naming_series = cint(frappe.db.get_single_value('Stock Settings', 'use_naming_series'))
+	use_naming_series = cint(frappe.db.get_single_value("Stock Settings", "use_naming_series"))
 	return bool(use_naming_series)
 
 
@@ -46,9 +46,9 @@
 	is set to use naming series.
 	:return: The naming series.
 	"""
-	naming_series_prefix = frappe.db.get_single_value('Stock Settings', 'naming_series_prefix')
+	naming_series_prefix = frappe.db.get_single_value("Stock Settings", "naming_series_prefix")
 	if not naming_series_prefix:
-		naming_series_prefix = 'BATCH-'
+		naming_series_prefix = "BATCH-"
 
 	return naming_series_prefix
 
@@ -62,9 +62,9 @@
 	:return: The derived key. If no prefix is given, an empty string is returned
 	"""
 	if not str(prefix):
-		return ''
+		return ""
 	else:
-		return prefix.upper() + '.#####'
+		return prefix.upper() + ".#####"
 
 
 def get_batch_naming_series():
@@ -74,7 +74,7 @@
 	Naming series key is in the format [prefix].[#####]
 	:return: The naming series or empty string if not available
 	"""
-	series = ''
+	series = ""
 	if batch_uses_naming_series():
 		prefix = _get_batch_prefix()
 		key = _make_naming_series_key(prefix)
@@ -86,24 +86,34 @@
 class Batch(Document):
 	def autoname(self):
 		"""Generate random ID for batch if not specified"""
-		if not self.batch_id:
-			create_new_batch, batch_number_series = frappe.db.get_value('Item', self.item,
-				['create_new_batch', 'batch_number_series'])
 
-			if create_new_batch:
-				if batch_number_series:
-					self.batch_id = make_autoname(batch_number_series, doc=self)
-				elif batch_uses_naming_series():
-					self.batch_id = self.get_name_from_naming_series()
-				else:
-					self.batch_id = get_name_from_hash()
+		if self.batch_id:
+			self.name = self.batch_id
+			return
+
+		create_new_batch, batch_number_series = frappe.db.get_value(
+			"Item", self.item, ["create_new_batch", "batch_number_series"]
+		)
+
+		if not create_new_batch:
+			frappe.throw(_("Batch ID is mandatory"), frappe.MandatoryError)
+
+		while not self.batch_id:
+			if batch_number_series:
+				self.batch_id = make_autoname(batch_number_series, doc=self)
+			elif batch_uses_naming_series():
+				self.batch_id = self.get_name_from_naming_series()
 			else:
-				frappe.throw(_('Batch ID is mandatory'), frappe.MandatoryError)
+				self.batch_id = get_name_from_hash()
+
+			# User might have manually created a batch with next number
+			if frappe.db.exists("Batch", self.batch_id):
+				self.batch_id = None
 
 		self.name = self.batch_id
 
 	def onload(self):
-		self.image = frappe.db.get_value('Item', self.item, 'image')
+		self.image = frappe.db.get_value("Item", self.item, "image")
 
 	def after_delete(self):
 		revert_series_if_last(get_batch_naming_series(), self.name)
@@ -123,16 +133,21 @@
 			self.use_batchwise_valuation = 1
 
 	def before_save(self):
-		has_expiry_date, shelf_life_in_days = frappe.db.get_value('Item', self.item, ['has_expiry_date', 'shelf_life_in_days'])
+		has_expiry_date, shelf_life_in_days = frappe.db.get_value(
+			"Item", self.item, ["has_expiry_date", "shelf_life_in_days"]
+		)
 		if not self.expiry_date and has_expiry_date and shelf_life_in_days:
 			self.expiry_date = add_days(self.manufacturing_date, shelf_life_in_days)
 
 		if has_expiry_date and not self.expiry_date:
-			frappe.throw(msg=_("Please set {0} for Batched Item {1}, which is used to set {2} on Submit.") \
-				.format(frappe.bold("Shelf Life in Days"),
+			frappe.throw(
+				msg=_("Please set {0} for Batched Item {1}, which is used to set {2} on Submit.").format(
+					frappe.bold("Shelf Life in Days"),
 					get_link_to_form("Item", self.item),
-					frappe.bold("Batch Expiry Date")),
-				title=_("Expiry Date Mandatory"))
+					frappe.bold("Batch Expiry Date"),
+				),
+				title=_("Expiry Date Mandatory"),
+			)
 
 	def get_name_from_naming_series(self):
 		"""
@@ -149,9 +164,11 @@
 
 
 @frappe.whitelist()
-def get_batch_qty(batch_no=None, warehouse=None, item_code=None, posting_date=None, posting_time=None):
+def get_batch_qty(
+	batch_no=None, warehouse=None, item_code=None, posting_date=None, posting_time=None
+):
 	"""Returns batch actual qty if warehouse is passed,
-		or returns dict of qty by warehouse if warehouse is None
+	        or returns dict of qty by warehouse if warehouse is None
 
 	The user must pass either batch_no or batch_no + warehouse or item_code + warehouse
 
@@ -163,25 +180,41 @@
 	if batch_no and warehouse:
 		cond = ""
 		if posting_date and posting_time:
-			cond = " and timestamp(posting_date, posting_time) <= timestamp('{0}', '{1}')".format(posting_date,
-				posting_time)
+			cond = " and timestamp(posting_date, posting_time) <= timestamp('{0}', '{1}')".format(
+				posting_date, posting_time
+			)
 
-		out = float(frappe.db.sql("""select sum(actual_qty)
+		out = float(
+			frappe.db.sql(
+				"""select sum(actual_qty)
 			from `tabStock Ledger Entry`
-			where is_cancelled = 0 and warehouse=%s and batch_no=%s {0}""".format(cond),
-			(warehouse, batch_no))[0][0] or 0)
+			where is_cancelled = 0 and warehouse=%s and batch_no=%s {0}""".format(
+					cond
+				),
+				(warehouse, batch_no),
+			)[0][0]
+			or 0
+		)
 
 	if batch_no and not warehouse:
-		out = frappe.db.sql('''select warehouse, sum(actual_qty) as qty
+		out = frappe.db.sql(
+			"""select warehouse, sum(actual_qty) as qty
 			from `tabStock Ledger Entry`
 			where is_cancelled = 0 and batch_no=%s
-			group by warehouse''', batch_no, as_dict=1)
+			group by warehouse""",
+			batch_no,
+			as_dict=1,
+		)
 
 	if not batch_no and item_code and warehouse:
-		out = frappe.db.sql('''select batch_no, sum(actual_qty) as qty
+		out = frappe.db.sql(
+			"""select batch_no, sum(actual_qty) as qty
 			from `tabStock Ledger Entry`
 			where is_cancelled = 0 and item_code = %s and warehouse=%s
-			group by batch_no''', (item_code, warehouse), as_dict=1)
+			group by batch_no""",
+			(item_code, warehouse),
+			as_dict=1,
+		)
 
 	return out
 
@@ -190,7 +223,9 @@
 def get_batches_by_oldest(item_code, warehouse):
 	"""Returns the oldest batch and qty for the given item_code and warehouse"""
 	batches = get_batch_qty(item_code=item_code, warehouse=warehouse)
-	batches_dates = [[batch, frappe.get_value('Batch', batch.batch_no, 'expiry_date')] for batch in batches]
+	batches_dates = [
+		[batch, frappe.get_value("Batch", batch.batch_no, "expiry_date")] for batch in batches
+	]
 	batches_dates.sort(key=lambda tup: tup[1])
 	return batches_dates
 
@@ -198,33 +233,25 @@
 @frappe.whitelist()
 def split_batch(batch_no, item_code, warehouse, qty, new_batch_id=None):
 	"""Split the batch into a new batch"""
-	batch = frappe.get_doc(dict(doctype='Batch', item=item_code, batch_id=new_batch_id)).insert()
+	batch = frappe.get_doc(dict(doctype="Batch", item=item_code, batch_id=new_batch_id)).insert()
 
-	company = frappe.db.get_value('Stock Ledger Entry', dict(
-			item_code=item_code,
-			batch_no=batch_no,
-			warehouse=warehouse
-		), ['company'])
+	company = frappe.db.get_value(
+		"Stock Ledger Entry",
+		dict(item_code=item_code, batch_no=batch_no, warehouse=warehouse),
+		["company"],
+	)
 
-	stock_entry = frappe.get_doc(dict(
-		doctype='Stock Entry',
-		purpose='Repack',
-		company=company,
-		items=[
-			dict(
-				item_code=item_code,
-				qty=float(qty or 0),
-				s_warehouse=warehouse,
-				batch_no=batch_no
-			),
-			dict(
-				item_code=item_code,
-				qty=float(qty or 0),
-				t_warehouse=warehouse,
-				batch_no=batch.name
-			),
-		]
-	))
+	stock_entry = frappe.get_doc(
+		dict(
+			doctype="Stock Entry",
+			purpose="Repack",
+			company=company,
+			items=[
+				dict(item_code=item_code, qty=float(qty or 0), s_warehouse=warehouse, batch_no=batch_no),
+				dict(item_code=item_code, qty=float(qty or 0), t_warehouse=warehouse, batch_no=batch.name),
+			],
+		)
+	)
 	stock_entry.set_stock_entry_type()
 	stock_entry.insert()
 	stock_entry.submit()
@@ -235,15 +262,20 @@
 def set_batch_nos(doc, warehouse_field, throw=False, child_table="items"):
 	"""Automatically select `batch_no` for outgoing items in item table"""
 	for d in doc.get(child_table):
-		qty = d.get('stock_qty') or d.get('transfer_qty') or d.get('qty') or 0
+		qty = d.get("stock_qty") or d.get("transfer_qty") or d.get("qty") or 0
 		warehouse = d.get(warehouse_field, None)
-		if warehouse and qty > 0 and frappe.db.get_value('Item', d.item_code, 'has_batch_no'):
+		if warehouse and qty > 0 and frappe.db.get_value("Item", d.item_code, "has_batch_no"):
 			if not d.batch_no:
 				d.batch_no = get_batch_no(d.item_code, warehouse, qty, throw, d.serial_no)
 			else:
 				batch_qty = get_batch_qty(batch_no=d.batch_no, warehouse=warehouse)
 				if flt(batch_qty, d.precision("qty")) < flt(qty, d.precision("qty")):
-					frappe.throw(_("Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches").format(d.idx, d.batch_no, batch_qty, qty))
+					frappe.throw(
+						_(
+							"Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches"
+						).format(d.idx, d.batch_no, batch_qty, qty)
+					)
+
 
 @frappe.whitelist()
 def get_batch_no(item_code, warehouse, qty=1, throw=False, serial_no=None):
@@ -264,7 +296,11 @@
 			break
 
 	if not batch_no:
-		frappe.msgprint(_('Please select a Batch for Item {0}. Unable to find a single batch that fulfills this requirement').format(frappe.bold(item_code)))
+		frappe.msgprint(
+			_(
+				"Please select a Batch for Item {0}. Unable to find a single batch that fulfills this requirement"
+			).format(frappe.bold(item_code))
+		)
 		if throw:
 			raise UnableToSelectBatchError
 
@@ -273,16 +309,14 @@
 
 def get_batches(item_code, warehouse, qty=1, throw=False, serial_no=None):
 	from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
-	cond = ''
-	if serial_no and frappe.get_cached_value('Item', item_code, 'has_batch_no'):
+
+	cond = ""
+	if serial_no and frappe.get_cached_value("Item", item_code, "has_batch_no"):
 		serial_nos = get_serial_nos(serial_no)
-		batch = frappe.get_all("Serial No",
-			fields = ["distinct batch_no"],
-			filters= {
-				"item_code": item_code,
-				"warehouse": warehouse,
-				"name": ("in", serial_nos)
-			}
+		batch = frappe.get_all(
+			"Serial No",
+			fields=["distinct batch_no"],
+			filters={"item_code": item_code, "warehouse": warehouse, "name": ("in", serial_nos)},
 		)
 
 		if not batch:
@@ -291,36 +325,47 @@
 		if batch and len(batch) > 1:
 			return []
 
-		cond = " and `tabBatch`.name = %s" %(frappe.db.escape(batch[0].batch_no))
+		cond = " and `tabBatch`.name = %s" % (frappe.db.escape(batch[0].batch_no))
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select batch_id, sum(`tabStock Ledger Entry`.actual_qty) as qty
 		from `tabBatch`
 			join `tabStock Ledger Entry` ignore index (item_code, warehouse)
 				on (`tabBatch`.batch_id = `tabStock Ledger Entry`.batch_no )
 		where `tabStock Ledger Entry`.item_code = %s and `tabStock Ledger Entry`.warehouse = %s
 			and `tabStock Ledger Entry`.is_cancelled = 0
-			and (`tabBatch`.expiry_date >= CURDATE() or `tabBatch`.expiry_date IS NULL) {0}
+			and (`tabBatch`.expiry_date >= CURRENT_DATE or `tabBatch`.expiry_date IS NULL) {0}
 		group by batch_id
 		order by `tabBatch`.expiry_date ASC, `tabBatch`.creation ASC
-	""".format(cond), (item_code, warehouse), as_dict=True)
+	""".format(
+			cond
+		),
+		(item_code, warehouse),
+		as_dict=True,
+	)
+
 
 def validate_serial_no_with_batch(serial_nos, item_code):
 	if frappe.get_cached_value("Serial No", serial_nos[0], "item_code") != item_code:
-		frappe.throw(_("The serial no {0} does not belong to item {1}")
-			.format(get_link_to_form("Serial No", serial_nos[0]), get_link_to_form("Item", item_code)))
+		frappe.throw(
+			_("The serial no {0} does not belong to item {1}").format(
+				get_link_to_form("Serial No", serial_nos[0]), get_link_to_form("Item", item_code)
+			)
+		)
 
-	serial_no_link = ','.join(get_link_to_form("Serial No", sn) for sn in serial_nos)
+	serial_no_link = ",".join(get_link_to_form("Serial No", sn) for sn in serial_nos)
 
 	message = "Serial Nos" if len(serial_nos) > 1 else "Serial No"
-	frappe.throw(_("There is no batch found against the {0}: {1}")
-		.format(message, serial_no_link))
+	frappe.throw(_("There is no batch found against the {0}: {1}").format(message, serial_no_link))
+
 
 def make_batch(args):
 	if frappe.db.get_value("Item", args.item, "has_batch_no"):
 		args.doctype = "Batch"
 		frappe.get_doc(args).insert().name
 
+
 @frappe.whitelist()
 def get_pos_reserved_batch_qty(filters):
 	import json
@@ -332,16 +377,22 @@
 	item = frappe.qb.DocType("POS Invoice Item").as_("item")
 	sum_qty = frappe.query_builder.functions.Sum(item.qty).as_("qty")
 
-	reserved_batch_qty = frappe.qb.from_(p).from_(item).select(sum_qty).where(
-		(p.name == item.parent) &
-		(p.consolidated_invoice.isnull()) &
-		(p.status != "Consolidated") &
-		(p.docstatus == 1) &
-		(item.docstatus == 1) &
-		(item.item_code == filters.get('item_code')) &
-		(item.warehouse == filters.get('warehouse')) &
-		(item.batch_no == filters.get('batch_no'))
-	).run()
+	reserved_batch_qty = (
+		frappe.qb.from_(p)
+		.from_(item)
+		.select(sum_qty)
+		.where(
+			(p.name == item.parent)
+			& (p.consolidated_invoice.isnull())
+			& (p.status != "Consolidated")
+			& (p.docstatus == 1)
+			& (item.docstatus == 1)
+			& (item.item_code == filters.get("item_code"))
+			& (item.warehouse == filters.get("warehouse"))
+			& (item.batch_no == filters.get("batch_no"))
+		)
+		.run()
+	)
 
 	flt_reserved_batch_qty = flt(reserved_batch_qty[0][0])
 	return flt_reserved_batch_qty
diff --git a/erpnext/stock/doctype/batch/batch_dashboard.py b/erpnext/stock/doctype/batch/batch_dashboard.py
index 725365b..84b64f3 100644
--- a/erpnext/stock/doctype/batch/batch_dashboard.py
+++ b/erpnext/stock/doctype/batch/batch_dashboard.py
@@ -3,23 +3,11 @@
 
 def get_data():
 	return {
-		'fieldname': 'batch_no',
-		'transactions': [
-			{
-				'label': _('Buy'),
-				'items': ['Purchase Invoice', 'Purchase Receipt']
-			},
-			{
-				'label': _('Sell'),
-				'items': ['Sales Invoice', 'Delivery Note']
-			},
-			{
-				'label': _('Move'),
-				'items': ['Stock Entry']
-			},
-			{
-				'label': _('Quality'),
-				'items': ['Quality Inspection']
-			}
-		]
+		"fieldname": "batch_no",
+		"transactions": [
+			{"label": _("Buy"), "items": ["Purchase Invoice", "Purchase Receipt"]},
+			{"label": _("Sell"), "items": ["Sales Invoice", "Delivery Note"]},
+			{"label": _("Move"), "items": ["Stock Entry"]},
+			{"label": _("Quality"), "items": ["Quality Inspection"]},
+		],
 	}
diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py
index 5763753..3e470d4 100644
--- a/erpnext/stock/doctype/batch/test_batch.py
+++ b/erpnext/stock/doctype/batch/test_batch.py
@@ -11,6 +11,8 @@
 
 from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
 from erpnext.stock.doctype.batch.batch import UnableToSelectBatchError, get_batch_no, get_batch_qty
+from erpnext.stock.doctype.item.test_item import make_item
+from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
 	create_stock_reconciliation,
@@ -21,134 +23,127 @@
 
 class TestBatch(FrappeTestCase):
 	def test_item_has_batch_enabled(self):
-		self.assertRaises(ValidationError, frappe.get_doc({
-			"doctype": "Batch",
-			"name": "_test Batch",
-			"item": "_Test Item"
-		}).save)
+		self.assertRaises(
+			ValidationError,
+			frappe.get_doc({"doctype": "Batch", "name": "_test Batch", "item": "_Test Item"}).save,
+		)
 
 	@classmethod
-	def make_batch_item(cls, item_name):
+	def make_batch_item(cls, item_name=None):
 		from erpnext.stock.doctype.item.test_item import make_item
+
 		if not frappe.db.exists(item_name):
-			return make_item(item_name, dict(has_batch_no = 1, create_new_batch = 1, is_stock_item=1))
+			return make_item(item_name, dict(has_batch_no=1, create_new_batch=1, is_stock_item=1))
 
-	def test_purchase_receipt(self, batch_qty = 100):
-		'''Test automated batch creation from Purchase Receipt'''
-		self.make_batch_item('ITEM-BATCH-1')
+	def test_purchase_receipt(self, batch_qty=100):
+		"""Test automated batch creation from Purchase Receipt"""
+		self.make_batch_item("ITEM-BATCH-1")
 
-		receipt = frappe.get_doc(dict(
-			doctype='Purchase Receipt',
-			supplier='_Test Supplier',
-			company='_Test Company',
-			items=[
-				dict(
-					item_code='ITEM-BATCH-1',
-					qty=batch_qty,
-					rate=10,
-					warehouse= 'Stores - _TC'
-				)
-			]
-		)).insert()
+		receipt = frappe.get_doc(
+			dict(
+				doctype="Purchase Receipt",
+				supplier="_Test Supplier",
+				company="_Test Company",
+				items=[dict(item_code="ITEM-BATCH-1", qty=batch_qty, rate=10, warehouse="Stores - _TC")],
+			)
+		).insert()
 		receipt.submit()
 
 		self.assertTrue(receipt.items[0].batch_no)
-		self.assertEqual(get_batch_qty(receipt.items[0].batch_no,
-			receipt.items[0].warehouse), batch_qty)
+		self.assertEqual(get_batch_qty(receipt.items[0].batch_no, receipt.items[0].warehouse), batch_qty)
 
 		return receipt
 
 	def test_stock_entry_incoming(self):
-		'''Test batch creation via Stock Entry (Work Order)'''
+		"""Test batch creation via Stock Entry (Work Order)"""
 
-		self.make_batch_item('ITEM-BATCH-1')
+		self.make_batch_item("ITEM-BATCH-1")
 
-		stock_entry = frappe.get_doc(dict(
-			doctype = 'Stock Entry',
-			purpose = 'Material Receipt',
-			company = '_Test Company',
-			items = [
-				dict(
-					item_code = 'ITEM-BATCH-1',
-					qty = 90,
-					t_warehouse = '_Test Warehouse - _TC',
-					cost_center = 'Main - _TC',
-					rate = 10
-				)
-			]
-		))
+		stock_entry = frappe.get_doc(
+			dict(
+				doctype="Stock Entry",
+				purpose="Material Receipt",
+				company="_Test Company",
+				items=[
+					dict(
+						item_code="ITEM-BATCH-1",
+						qty=90,
+						t_warehouse="_Test Warehouse - _TC",
+						cost_center="Main - _TC",
+						rate=10,
+					)
+				],
+			)
+		)
 
 		stock_entry.set_stock_entry_type()
 		stock_entry.insert()
 		stock_entry.submit()
 
 		self.assertTrue(stock_entry.items[0].batch_no)
-		self.assertEqual(get_batch_qty(stock_entry.items[0].batch_no, stock_entry.items[0].t_warehouse), 90)
+		self.assertEqual(
+			get_batch_qty(stock_entry.items[0].batch_no, stock_entry.items[0].t_warehouse), 90
+		)
 
 	def test_delivery_note(self):
-		'''Test automatic batch selection for outgoing items'''
+		"""Test automatic batch selection for outgoing items"""
 		batch_qty = 15
 		receipt = self.test_purchase_receipt(batch_qty)
-		item_code = 'ITEM-BATCH-1'
+		item_code = "ITEM-BATCH-1"
 
-		delivery_note = frappe.get_doc(dict(
-			doctype='Delivery Note',
-			customer='_Test Customer',
-			company=receipt.company,
-			items=[
-				dict(
-					item_code=item_code,
-					qty=batch_qty,
-					rate=10,
-					warehouse=receipt.items[0].warehouse
-				)
-			]
-		)).insert()
+		delivery_note = frappe.get_doc(
+			dict(
+				doctype="Delivery Note",
+				customer="_Test Customer",
+				company=receipt.company,
+				items=[
+					dict(item_code=item_code, qty=batch_qty, rate=10, warehouse=receipt.items[0].warehouse)
+				],
+			)
+		).insert()
 		delivery_note.submit()
 
 		# shipped from FEFO batch
 		self.assertEqual(
-			delivery_note.items[0].batch_no,
-			get_batch_no(item_code, receipt.items[0].warehouse, batch_qty)
+			delivery_note.items[0].batch_no, get_batch_no(item_code, receipt.items[0].warehouse, batch_qty)
 		)
 
 	def test_delivery_note_fail(self):
-		'''Test automatic batch selection for outgoing items'''
+		"""Test automatic batch selection for outgoing items"""
 		receipt = self.test_purchase_receipt(100)
-		delivery_note = frappe.get_doc(dict(
-			doctype = 'Delivery Note',
-			customer = '_Test Customer',
-			company = receipt.company,
-			items = [
-				dict(
-					item_code = 'ITEM-BATCH-1',
-					qty = 5000,
-					rate = 10,
-					warehouse = receipt.items[0].warehouse
-				)
-			]
-		))
+		delivery_note = frappe.get_doc(
+			dict(
+				doctype="Delivery Note",
+				customer="_Test Customer",
+				company=receipt.company,
+				items=[
+					dict(item_code="ITEM-BATCH-1", qty=5000, rate=10, warehouse=receipt.items[0].warehouse)
+				],
+			)
+		)
 		self.assertRaises(UnableToSelectBatchError, delivery_note.insert)
 
 	def test_stock_entry_outgoing(self):
-		'''Test automatic batch selection for outgoing stock entry'''
+		"""Test automatic batch selection for outgoing stock entry"""
 
 		batch_qty = 16
 		receipt = self.test_purchase_receipt(batch_qty)
-		item_code = 'ITEM-BATCH-1'
+		item_code = "ITEM-BATCH-1"
 
-		stock_entry = frappe.get_doc(dict(
-			doctype='Stock Entry',
-			purpose='Material Issue',
-			company=receipt.company,
-			items=[
-				dict(
-					item_code=item_code,
-					qty=batch_qty,
-					s_warehouse=receipt.items[0].warehouse,
-				)
-			]
-		))
+		stock_entry = frappe.get_doc(
+			dict(
+				doctype="Stock Entry",
+				purpose="Material Issue",
+				company=receipt.company,
+				items=[
+					dict(
+						item_code=item_code,
+						qty=batch_qty,
+						s_warehouse=receipt.items[0].warehouse,
+					)
+				],
+			)
+		)
 
 		stock_entry.set_stock_entry_type()
 		stock_entry.insert()
@@ -156,35 +151,38 @@
 
 		# assert same batch is selected
 		self.assertEqual(
-			stock_entry.items[0].batch_no,
-			get_batch_no(item_code, receipt.items[0].warehouse, batch_qty)
+			stock_entry.items[0].batch_no, get_batch_no(item_code, receipt.items[0].warehouse, batch_qty)
 		)
 
 	def test_batch_split(self):
-		'''Test batch splitting'''
+		"""Test batch splitting"""
 		receipt = self.test_purchase_receipt()
 		from erpnext.stock.doctype.batch.batch import split_batch
 
-		new_batch = split_batch(receipt.items[0].batch_no, 'ITEM-BATCH-1', receipt.items[0].warehouse, 22)
+		new_batch = split_batch(
+			receipt.items[0].batch_no, "ITEM-BATCH-1", receipt.items[0].warehouse, 22
+		)
 
 		self.assertEqual(get_batch_qty(receipt.items[0].batch_no, receipt.items[0].warehouse), 78)
 		self.assertEqual(get_batch_qty(new_batch, receipt.items[0].warehouse), 22)
 
 	def test_get_batch_qty(self):
-		'''Test getting batch quantities by batch_numbers, item_code or warehouse'''
-		self.make_batch_item('ITEM-BATCH-2')
-		self.make_new_batch_and_entry('ITEM-BATCH-2', 'batch a', '_Test Warehouse - _TC')
-		self.make_new_batch_and_entry('ITEM-BATCH-2', 'batch b', '_Test Warehouse - _TC')
+		"""Test getting batch quantities by batch_numbers, item_code or warehouse"""
+		self.make_batch_item("ITEM-BATCH-2")
+		self.make_new_batch_and_entry("ITEM-BATCH-2", "batch a", "_Test Warehouse - _TC")
+		self.make_new_batch_and_entry("ITEM-BATCH-2", "batch b", "_Test Warehouse - _TC")
 
-		self.assertEqual(get_batch_qty(item_code = 'ITEM-BATCH-2', warehouse = '_Test Warehouse - _TC'),
-			[{'batch_no': u'batch a', 'qty': 90.0}, {'batch_no': u'batch b', 'qty': 90.0}])
+		self.assertEqual(
+			get_batch_qty(item_code="ITEM-BATCH-2", warehouse="_Test Warehouse - _TC"),
+			[{"batch_no": "batch a", "qty": 90.0}, {"batch_no": "batch b", "qty": 90.0}],
+		)
 
-		self.assertEqual(get_batch_qty('batch a', '_Test Warehouse - _TC'), 90)
+		self.assertEqual(get_batch_qty("batch a", "_Test Warehouse - _TC"), 90)
 
 	def test_total_batch_qty(self):
-		self.make_batch_item('ITEM-BATCH-3')
+		self.make_batch_item("ITEM-BATCH-3")
 		existing_batch_qty = flt(frappe.db.get_value("Batch", "B100", "batch_qty"))
-		stock_entry = self.make_new_batch_and_entry('ITEM-BATCH-3', 'B100', '_Test Warehouse - _TC')
+		stock_entry = self.make_new_batch_and_entry("ITEM-BATCH-3", "B100", "_Test Warehouse - _TC")
 
 		current_batch_qty = flt(frappe.db.get_value("Batch", "B100", "batch_qty"))
 		self.assertEqual(current_batch_qty, existing_batch_qty + 90)
@@ -195,32 +193,32 @@
 
 	@classmethod
 	def make_new_batch_and_entry(cls, item_name, batch_name, warehouse):
-		'''Make a new stock entry for given target warehouse and batch name of item'''
+		"""Make a new stock entry for given target warehouse and batch name of item"""
 
 		if not frappe.db.exists("Batch", batch_name):
-			batch = frappe.get_doc(dict(
-				doctype = 'Batch',
-				item = item_name,
-				batch_id = batch_name
-			)).insert(ignore_permissions=True)
+			batch = frappe.get_doc(dict(doctype="Batch", item=item_name, batch_id=batch_name)).insert(
+				ignore_permissions=True
+			)
 			batch.save()
 
-		stock_entry = frappe.get_doc(dict(
-			doctype = 'Stock Entry',
-			purpose = 'Material Receipt',
-			company = '_Test Company',
-			items = [
-				dict(
-					item_code = item_name,
-					qty = 90,
-					t_warehouse = warehouse,
-					cost_center = 'Main - _TC',
-					rate = 10,
-					batch_no = batch_name,
-					allow_zero_valuation_rate = 1
-				)
-			]
-		))
+		stock_entry = frappe.get_doc(
+			dict(
+				doctype="Stock Entry",
+				purpose="Material Receipt",
+				company="_Test Company",
+				items=[
+					dict(
+						item_code=item_name,
+						qty=90,
+						t_warehouse=warehouse,
+						cost_center="Main - _TC",
+						rate=10,
+						batch_no=batch_name,
+						allow_zero_valuation_rate=1,
+					)
+				],
+			)
+		)
 
 		stock_entry.set_stock_entry_type()
 		stock_entry.insert()
@@ -229,28 +227,28 @@
 		return stock_entry
 
 	def test_batch_name_with_naming_series(self):
-		stock_settings = frappe.get_single('Stock Settings')
+		stock_settings = frappe.get_single("Stock Settings")
 		use_naming_series = cint(stock_settings.use_naming_series)
 
 		if not use_naming_series:
-			frappe.set_value('Stock Settings', 'Stock Settings', 'use_naming_series', 1)
+			frappe.set_value("Stock Settings", "Stock Settings", "use_naming_series", 1)
 
-		batch = self.make_new_batch('_Test Stock Item For Batch Test1')
+		batch = self.make_new_batch("_Test Stock Item For Batch Test1")
 		batch_name = batch.name
 
-		self.assertTrue(batch_name.startswith('BATCH-'))
+		self.assertTrue(batch_name.startswith("BATCH-"))
 
 		batch.delete()
-		batch = self.make_new_batch('_Test Stock Item For Batch Test2')
+		batch = self.make_new_batch("_Test Stock Item For Batch Test2")
 
 		self.assertEqual(batch_name, batch.name)
 
 		# reset Stock Settings
 		if not use_naming_series:
-			frappe.set_value('Stock Settings', 'Stock Settings', 'use_naming_series', 0)
+			frappe.set_value("Stock Settings", "Stock Settings", "use_naming_series", 0)
 
-	def make_new_batch(self, item_name, batch_id=None, do_not_insert=0):
-		batch = frappe.new_doc('Batch')
+	def make_new_batch(self, item_name=None, batch_id=None, do_not_insert=0):
+		batch = frappe.new_doc("Batch")
 		item = self.make_batch_item(item_name)
 		batch.item = item.name
 
@@ -263,53 +261,56 @@
 		return batch
 
 	def test_batch_wise_item_price(self):
-		if not frappe.db.get_value('Item', '_Test Batch Price Item'):
-			frappe.get_doc({
-				'doctype': 'Item',
-				'is_stock_item': 1,
-				'item_code': '_Test Batch Price Item',
-				'item_group': 'Products',
-				'has_batch_no': 1,
-				'create_new_batch': 1
-			}).insert(ignore_permissions=True)
+		if not frappe.db.get_value("Item", "_Test Batch Price Item"):
+			frappe.get_doc(
+				{
+					"doctype": "Item",
+					"is_stock_item": 1,
+					"item_code": "_Test Batch Price Item",
+					"item_group": "Products",
+					"has_batch_no": 1,
+					"create_new_batch": 1,
+				}
+			).insert(ignore_permissions=True)
 
-		batch1 = create_batch('_Test Batch Price Item', 200, 1)
-		batch2 = create_batch('_Test Batch Price Item', 300, 1)
-		batch3 = create_batch('_Test Batch Price Item', 400, 0)
+		batch1 = create_batch("_Test Batch Price Item", 200, 1)
+		batch2 = create_batch("_Test Batch Price Item", 300, 1)
+		batch3 = create_batch("_Test Batch Price Item", 400, 0)
 
 		company = "_Test Company with perpetual inventory"
-		currency = frappe.get_cached_value("Company",  company,  "default_currency")
+		currency = frappe.get_cached_value("Company", company, "default_currency")
 
-		args = frappe._dict({
-			"item_code": "_Test Batch Price Item",
-			"company": company,
-			"price_list": "_Test Price List",
-			"currency": currency,
-			"doctype": "Sales Invoice",
-			"conversion_rate": 1,
-			"price_list_currency": "_Test Currency",
-			"plc_conversion_rate": 1,
-			"customer": "_Test Customer",
-			"name": None
-		})
+		args = frappe._dict(
+			{
+				"item_code": "_Test Batch Price Item",
+				"company": company,
+				"price_list": "_Test Price List",
+				"currency": currency,
+				"doctype": "Sales Invoice",
+				"conversion_rate": 1,
+				"price_list_currency": "_Test Currency",
+				"plc_conversion_rate": 1,
+				"customer": "_Test Customer",
+				"name": None,
+			}
+		)
 
-		#test price for batch1
-		args.update({'batch_no': batch1})
+		# test price for batch1
+		args.update({"batch_no": batch1})
 		details = get_item_details(args)
-		self.assertEqual(details.get('price_list_rate'), 200)
+		self.assertEqual(details.get("price_list_rate"), 200)
 
-		#test price for batch2
-		args.update({'batch_no': batch2})
+		# test price for batch2
+		args.update({"batch_no": batch2})
 		details = get_item_details(args)
-		self.assertEqual(details.get('price_list_rate'), 300)
+		self.assertEqual(details.get("price_list_rate"), 300)
 
-		#test price for batch3
-		args.update({'batch_no': batch3})
+		# test price for batch3
+		args.update({"batch_no": batch3})
 		details = get_item_details(args)
-		self.assertEqual(details.get('price_list_rate'), 400)
+		self.assertEqual(details.get("price_list_rate"), 400)
 
-
-	def test_basic_batch_wise_valuation(self, batch_qty = 100):
+	def test_basic_batch_wise_valuation(self, batch_qty=100):
 		item_code = "_TestBatchWiseVal"
 		warehouse = "_Test Warehouse - _TC"
 		self.make_batch_item(item_code)
@@ -358,7 +359,9 @@
 		self.make_batch_item(item_code)
 
 		def assertValuation(expected):
-			actual = get_valuation_rate(item_code, warehouse, "voucher_type", "voucher_no", batch_no=batch_no)
+			actual = get_valuation_rate(
+				item_code, warehouse, "voucher_type", "voucher_no", batch_no=batch_no
+			)
 			self.assertAlmostEqual(actual, expected)
 
 		se = make_stock_entry(item_code=item_code, qty=100, rate=10, target=warehouse)
@@ -381,13 +384,14 @@
 		assertValuation(15)
 
 		# reset rate with stock reconiliation
-		create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=10, rate=25, batch_no=batch_no)
+		create_stock_reconciliation(
+			item_code=item_code, warehouse=warehouse, qty=10, rate=25, batch_no=batch_no
+		)
 		assertValuation(25)
 
 		make_stock_entry(item_code=item_code, qty=20, rate=20, target=warehouse, batch_no=batch_no)
 		assertValuation((20 * 20 + 10 * 25) / (10 + 20))
 
-
 	def test_update_batch_properties(self):
 		item_code = "_TestBatchWiseVal"
 		self.make_batch_item(item_code)
@@ -405,14 +409,38 @@
 
 		self.assertEqual(getdate(batch.expiry_date), getdate(expiry_date))
 
+	def test_autocreation_of_batches(self):
+		"""
+		Test if auto created Serial No excludes existing serial numbers
+		"""
+		item_code = make_item(
+			properties={
+				"has_batch_no": 1,
+				"batch_number_series": "BATCHEXISTING.###",
+				"create_new_batch": 1,
+			}
+		).name
+
+		manually_created_batch = self.make_new_batch(item_code, batch_id="BATCHEXISTING001").name
+
+		pr_1 = make_purchase_receipt(item_code=item_code, qty=1, batch_no=manually_created_batch)
+		pr_2 = make_purchase_receipt(item_code=item_code, qty=1)
+
+		self.assertNotEqual(pr_1.items[0].batch_no, pr_2.items[0].batch_no)
+		self.assertEqual("BATCHEXISTING002", pr_2.items[0].batch_no)
 
 
 def create_batch(item_code, rate, create_item_price_for_batch):
-	pi = make_purchase_invoice(company="_Test Company",
-		warehouse= "Stores - _TC", cost_center = "Main - _TC", update_stock=1,
-		expense_account ="_Test Account Cost for Goods Sold - _TC", item_code=item_code)
+	pi = make_purchase_invoice(
+		company="_Test Company",
+		warehouse="Stores - _TC",
+		cost_center="Main - _TC",
+		update_stock=1,
+		expense_account="_Test Account Cost for Goods Sold - _TC",
+		item_code=item_code,
+	)
 
-	batch = frappe.db.get_value('Batch', {'item': item_code, 'reference_name': pi.name})
+	batch = frappe.db.get_value("Batch", {"item": item_code, "reference_name": pi.name})
 
 	if not create_item_price_for_batch:
 		create_price_list_for_batch(item_code, None, rate)
@@ -421,14 +449,18 @@
 
 	return batch
 
+
 def create_price_list_for_batch(item_code, batch, rate):
-	frappe.get_doc({
-		'doctype': 'Item Price',
-		'item_code': '_Test Batch Price Item',
-		'price_list': '_Test Price List',
-		'batch_no': batch,
-		'price_list_rate': rate
-	}).insert()
+	frappe.get_doc(
+		{
+			"doctype": "Item Price",
+			"item_code": "_Test Batch Price Item",
+			"price_list": "_Test Price List",
+			"batch_no": batch,
+			"price_list_rate": rate,
+		}
+	).insert()
+
 
 def make_new_batch(**args):
 	args = frappe._dict(args)
@@ -436,10 +468,12 @@
 	if frappe.db.exists("Batch", args.batch_id):
 		batch = frappe.get_doc("Batch", args.batch_id)
 	else:
-		batch = frappe.get_doc({
-			"doctype": "Batch",
-			"batch_id": args.batch_id,
-			"item": args.item_code,
-		}).insert()
+		batch = frappe.get_doc(
+			{
+				"doctype": "Batch",
+				"batch_id": args.batch_id,
+				"item": args.item_code,
+			}
+		).insert()
 
 	return batch
diff --git a/erpnext/stock/doctype/bin/bin.json b/erpnext/stock/doctype/bin/bin.json
index 56dc71c..d822f4a 100644
--- a/erpnext/stock/doctype/bin/bin.json
+++ b/erpnext/stock/doctype/bin/bin.json
@@ -1,6 +1,6 @@
 {
  "actions": [],
- "autoname": "MAT-BIN-.YYYY.-.#####",
+ "autoname": "hash",
  "creation": "2013-01-10 16:34:25",
  "doctype": "DocType",
  "engine": "InnoDB",
@@ -171,11 +171,11 @@
  "idx": 1,
  "in_create": 1,
  "links": [],
- "modified": "2022-01-30 17:04:54.715288",
+ "modified": "2022-03-30 07:22:23.868602",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Bin",
- "naming_rule": "Expression (old style)",
+ "naming_rule": "Random",
  "owner": "Administrator",
  "permissions": [
   {
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index 3bc15a8..bc235d9 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -4,137 +4,170 @@
 
 import frappe
 from frappe.model.document import Document
-from frappe.query_builder import Case
-from frappe.query_builder.functions import Coalesce, Sum
+from frappe.query_builder import Case, Order
+from frappe.query_builder.functions import Coalesce, CombineDatetime, Sum
 from frappe.utils import flt
 
 
 class Bin(Document):
 	def before_save(self):
 		if self.get("__islocal") or not self.stock_uom:
-			self.stock_uom = frappe.get_cached_value('Item', self.item_code, 'stock_uom')
+			self.stock_uom = frappe.get_cached_value("Item", self.item_code, "stock_uom")
 		self.set_projected_qty()
 
 	def set_projected_qty(self):
-		self.projected_qty = (flt(self.actual_qty) + flt(self.ordered_qty)
-			+ flt(self.indented_qty) + flt(self.planned_qty) - flt(self.reserved_qty)
-			- flt(self.reserved_qty_for_production) - flt(self.reserved_qty_for_sub_contract))
+		self.projected_qty = (
+			flt(self.actual_qty)
+			+ flt(self.ordered_qty)
+			+ flt(self.indented_qty)
+			+ flt(self.planned_qty)
+			- flt(self.reserved_qty)
+			- flt(self.reserved_qty_for_production)
+			- flt(self.reserved_qty_for_sub_contract)
+		)
 
 	def update_reserved_qty_for_production(self):
-		'''Update qty reserved for production from Production Item tables
-			in open work orders'''
+		"""Update qty reserved for production from Production Item tables
+		in open work orders"""
 		from erpnext.manufacturing.doctype.work_order.work_order import get_reserved_qty_for_production
 
-		self.reserved_qty_for_production = get_reserved_qty_for_production(self.item_code, self.warehouse)
+		self.reserved_qty_for_production = get_reserved_qty_for_production(
+			self.item_code, self.warehouse
+		)
 
 		self.set_projected_qty()
 
-		self.db_set('reserved_qty_for_production', flt(self.reserved_qty_for_production))
-		self.db_set('projected_qty', self.projected_qty)
+		self.db_set("reserved_qty_for_production", flt(self.reserved_qty_for_production))
+		self.db_set("projected_qty", self.projected_qty)
 
 	def update_reserved_qty_for_sub_contracting(self):
-		#reserved qty
+		# reserved qty
 
 		po = frappe.qb.DocType("Purchase Order")
 		supplied_item = frappe.qb.DocType("Purchase Order Item Supplied")
 
 		reserved_qty_for_sub_contract = (
-				frappe.qb
-					.from_(po)
-					.from_(supplied_item)
-					.select(Sum(Coalesce(supplied_item.required_qty, 0)))
-					.where(
-						(supplied_item.rm_item_code == self.item_code)
-						& (po.name == supplied_item.parent)
-						& (po.docstatus == 1)
-						& (po.is_subcontracted == "Yes")
-						& (po.status != "Closed")
-						& (po.per_received < 100)
-						& (supplied_item.reserve_warehouse == self.warehouse)
-					)
-				).run()[0][0] or 0.0
+			frappe.qb.from_(po)
+			.from_(supplied_item)
+			.select(Sum(Coalesce(supplied_item.required_qty, 0)))
+			.where(
+				(supplied_item.rm_item_code == self.item_code)
+				& (po.name == supplied_item.parent)
+				& (po.docstatus == 1)
+				& (po.is_subcontracted)
+				& (po.status != "Closed")
+				& (po.per_received < 100)
+				& (supplied_item.reserve_warehouse == self.warehouse)
+			)
+		).run()[0][0] or 0.0
 
 		se = frappe.qb.DocType("Stock Entry")
 		se_item = frappe.qb.DocType("Stock Entry Detail")
 
+		if frappe.db.field_exists("Stock Entry", "is_return"):
+			qty_field = (
+				Case().when(se.is_return == 1, se_item.transfer_qty * -1).else_(se_item.transfer_qty)
+			)
+		else:
+			qty_field = se_item.transfer_qty
+
 		materials_transferred = (
-				frappe.qb
-					.from_(se)
-					.from_(se_item)
-					.from_(po)
-					.select(Sum(
-						Case()
-							.when(se.is_return == 1, se_item.transfer_qty * -1)
-							.else_(se_item.transfer_qty)
-						))
-					.where(
-						(se.docstatus == 1)
-						& (se.purpose == "Send to Subcontractor")
-						& (Coalesce(se.purchase_order, "") != "")
-						& ((se_item.item_code == self.item_code)
-							| (se_item.original_item == self.item_code))
-						& (se.name == se_item.parent)
-						& (po.name == se.purchase_order)
-						& (po.docstatus == 1)
-						& (po.is_subcontracted == "Yes")
-						& (po.status != "Closed")
-						& (po.per_received < 100)
-					)
-				).run()[0][0] or 0.0
+			frappe.qb.from_(se)
+			.from_(se_item)
+			.from_(po)
+			.select(Sum(qty_field))
+			.where(
+				(se.docstatus == 1)
+				& (se.purpose == "Send to Subcontractor")
+				& (Coalesce(se.purchase_order, "") != "")
+				& ((se_item.item_code == self.item_code) | (se_item.original_item == self.item_code))
+				& (se.name == se_item.parent)
+				& (po.name == se.purchase_order)
+				& (po.docstatus == 1)
+				& (po.is_subcontracted == 1)
+				& (po.status != "Closed")
+				& (po.per_received < 100)
+			)
+		).run()[0][0] or 0.0
 
 		if reserved_qty_for_sub_contract > materials_transferred:
 			reserved_qty_for_sub_contract = reserved_qty_for_sub_contract - materials_transferred
 		else:
 			reserved_qty_for_sub_contract = 0
 
-		self.db_set('reserved_qty_for_sub_contract', reserved_qty_for_sub_contract)
+		self.db_set("reserved_qty_for_sub_contract", reserved_qty_for_sub_contract)
 		self.set_projected_qty()
-		self.db_set('projected_qty', self.projected_qty)
+		self.db_set("projected_qty", self.projected_qty)
+
 
 def on_doctype_update():
 	frappe.db.add_unique("Bin", ["item_code", "warehouse"], constraint_name="unique_item_warehouse")
 
 
 def get_bin_details(bin_name):
-	return frappe.db.get_value('Bin', bin_name, ['actual_qty', 'ordered_qty',
-	'reserved_qty', 'indented_qty', 'planned_qty', 'reserved_qty_for_production',
-	'reserved_qty_for_sub_contract'], as_dict=1)
+	return frappe.db.get_value(
+		"Bin",
+		bin_name,
+		[
+			"actual_qty",
+			"ordered_qty",
+			"reserved_qty",
+			"indented_qty",
+			"planned_qty",
+			"reserved_qty_for_production",
+			"reserved_qty_for_sub_contract",
+		],
+		as_dict=1,
+	)
+
 
 def update_qty(bin_name, args):
 	from erpnext.controllers.stock_controller import future_sle_exists
 
 	bin_details = get_bin_details(bin_name)
 	# actual qty is already updated by processing current voucher
-	actual_qty = bin_details.actual_qty
+	actual_qty = bin_details.actual_qty or 0.0
+	sle = frappe.qb.DocType("Stock Ledger Entry")
 
 	# actual qty is not up to date in case of backdated transaction
 	if future_sle_exists(args):
-		actual_qty = frappe.db.get_value("Stock Ledger Entry",
-				filters={
-					"item_code": args.get("item_code"),
-					"warehouse": args.get("warehouse"),
-					"is_cancelled": 0
-				},
-				fieldname="qty_after_transaction",
-				order_by="posting_date desc, posting_time desc, creation desc",
-			) or 0.0
+		last_sle_qty = (
+			frappe.qb.from_(sle)
+			.select(sle.qty_after_transaction)
+			.where((sle.item_code == args.get("item_code")) & (sle.warehouse == args.get("warehouse")))
+			.orderby(CombineDatetime(sle.posting_date, sle.posting_time), order=Order.desc)
+			.orderby(sle.creation, order=Order.desc)
+			.run()
+		)
+
+		if last_sle_qty:
+			actual_qty = last_sle_qty[0][0]
 
 	ordered_qty = flt(bin_details.ordered_qty) + flt(args.get("ordered_qty"))
 	reserved_qty = flt(bin_details.reserved_qty) + flt(args.get("reserved_qty"))
 	indented_qty = flt(bin_details.indented_qty) + flt(args.get("indented_qty"))
 	planned_qty = flt(bin_details.planned_qty) + flt(args.get("planned_qty"))
 
-
 	# compute projected qty
-	projected_qty = (flt(actual_qty) + flt(ordered_qty)
-		+ flt(indented_qty) + flt(planned_qty) - flt(reserved_qty)
-		- flt(bin_details.reserved_qty_for_production) - flt(bin_details.reserved_qty_for_sub_contract))
+	projected_qty = (
+		flt(actual_qty)
+		+ flt(ordered_qty)
+		+ flt(indented_qty)
+		+ flt(planned_qty)
+		- flt(reserved_qty)
+		- flt(bin_details.reserved_qty_for_production)
+		- flt(bin_details.reserved_qty_for_sub_contract)
+	)
 
-	frappe.db.set_value('Bin', bin_name, {
-		'actual_qty': actual_qty,
-		'ordered_qty': ordered_qty,
-		'reserved_qty': reserved_qty,
-		'indented_qty': indented_qty,
-		'planned_qty': planned_qty,
-		'projected_qty': projected_qty
-	})
+	frappe.db.set_value(
+		"Bin",
+		bin_name,
+		{
+			"actual_qty": actual_qty,
+			"ordered_qty": ordered_qty,
+			"reserved_qty": reserved_qty,
+			"indented_qty": indented_qty,
+			"planned_qty": planned_qty,
+			"projected_qty": projected_qty,
+		},
+	)
diff --git a/erpnext/stock/doctype/bin/test_bin.py b/erpnext/stock/doctype/bin/test_bin.py
index ec0d8a8..b79dee8 100644
--- a/erpnext/stock/doctype/bin/test_bin.py
+++ b/erpnext/stock/doctype/bin/test_bin.py
@@ -9,10 +9,8 @@
 
 
 class TestBin(FrappeTestCase):
-
-
 	def test_concurrent_inserts(self):
-		""" Ensure no duplicates are possible in case of concurrent inserts"""
+		"""Ensure no duplicates are possible in case of concurrent inserts"""
 		item_code = "_TestConcurrentBin"
 		make_item(item_code)
 		warehouse = "_Test Warehouse - _TC"
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index 706ca36..ea3cf19 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -77,8 +77,6 @@
 			}
 		});
 
-		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
-
 		frm.set_df_property('packed_items', 'cannot_add_rows', true);
 		frm.set_df_property('packed_items', 'cannot_delete_rows', true);
 	},
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index 55a4c95..f9e9349 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -23,6 +23,10 @@
   "is_return",
   "issue_credit_note",
   "return_against",
+  "accounting_dimensions_section",
+  "cost_center",
+  "dimension_col_break",
+  "project",
   "customer_po_details",
   "po_no",
   "column_break_17",
@@ -115,7 +119,6 @@
   "driver_name",
   "lr_date",
   "more_info",
-  "project",
   "campaign",
   "source",
   "column_break5",
@@ -1189,6 +1192,8 @@
    "width": "50%"
   },
   {
+   "fetch_from": "sales_partner.commission_rate",
+   "fetch_if_empty": 1,
    "fieldname": "commission_rate",
    "fieldtype": "Float",
    "label": "Commission Rate (%)",
@@ -1309,13 +1314,29 @@
    "fieldtype": "Currency",
    "label": "Amount Eligible for Commission",
    "read_only": 1
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "accounting_dimensions_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Dimensions"
+  },
+  {
+   "fieldname": "cost_center",
+   "fieldtype": "Link",
+   "label": "Cost Center",
+   "options": "Cost Center"
+  },
+  {
+   "fieldname": "dimension_col_break",
+   "fieldtype": "Column Break"
   }
  ],
  "icon": "fa fa-truck",
  "idx": 146,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-10-09 14:29:13.428984",
+ "modified": "2022-06-10 03:52:04.197415",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Note",
@@ -1380,6 +1401,7 @@
  "show_name_in_global_search": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "timeline_field": "customer",
  "title_field": "title",
  "track_changes": 1,
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 2a4d639..0e68e85 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -14,74 +14,77 @@
 from erpnext.controllers.selling_controller import SellingController
 from erpnext.stock.doctype.batch.batch import set_batch_nos
 from erpnext.stock.doctype.serial_no.serial_no import get_delivery_note_serial_no
-from erpnext.stock.utils import calculate_mapped_packed_items_return
 
-form_grid_templates = {
-	"items": "templates/form_grid/item_grid.html"
-}
+form_grid_templates = {"items": "templates/form_grid/item_grid.html"}
+
 
 class DeliveryNote(SellingController):
 	def __init__(self, *args, **kwargs):
 		super(DeliveryNote, self).__init__(*args, **kwargs)
-		self.status_updater = [{
-			'source_dt': 'Delivery Note Item',
-			'target_dt': 'Sales Order Item',
-			'join_field': 'so_detail',
-			'target_field': 'delivered_qty',
-			'target_parent_dt': 'Sales Order',
-			'target_parent_field': 'per_delivered',
-			'target_ref_field': 'qty',
-			'source_field': 'qty',
-			'percent_join_field': 'against_sales_order',
-			'status_field': 'delivery_status',
-			'keyword': 'Delivered',
-			'second_source_dt': 'Sales Invoice Item',
-			'second_source_field': 'qty',
-			'second_join_field': 'so_detail',
-			'overflow_type': 'delivery',
-			'second_source_extra_cond': """ and exists(select name from `tabSales Invoice`
-				where name=`tabSales Invoice Item`.parent and update_stock = 1)"""
-		},
-		{
-			'source_dt': 'Delivery Note Item',
-			'target_dt': 'Sales Invoice Item',
-			'join_field': 'si_detail',
-			'target_field': 'delivered_qty',
-			'target_parent_dt': 'Sales Invoice',
-			'target_ref_field': 'qty',
-			'source_field': 'qty',
-			'percent_join_field': 'against_sales_invoice',
-			'overflow_type': 'delivery',
-			'no_allowance': 1
-		}]
-		if cint(self.is_return):
-			self.status_updater.extend([{
-				'source_dt': 'Delivery Note Item',
-				'target_dt': 'Sales Order Item',
-				'join_field': 'so_detail',
-				'target_field': 'returned_qty',
-				'target_parent_dt': 'Sales Order',
-				'source_field': '-1 * qty',
-				'second_source_dt': 'Sales Invoice Item',
-				'second_source_field': '-1 * qty',
-				'second_join_field': 'so_detail',
-				'extra_cond': """ and exists (select name from `tabDelivery Note`
-					where name=`tabDelivery Note Item`.parent and is_return=1)""",
-				'second_source_extra_cond': """ and exists (select name from `tabSales Invoice`
-					where name=`tabSales Invoice Item`.parent and is_return=1 and update_stock=1)"""
+		self.status_updater = [
+			{
+				"source_dt": "Delivery Note Item",
+				"target_dt": "Sales Order Item",
+				"join_field": "so_detail",
+				"target_field": "delivered_qty",
+				"target_parent_dt": "Sales Order",
+				"target_parent_field": "per_delivered",
+				"target_ref_field": "qty",
+				"source_field": "qty",
+				"percent_join_field": "against_sales_order",
+				"status_field": "delivery_status",
+				"keyword": "Delivered",
+				"second_source_dt": "Sales Invoice Item",
+				"second_source_field": "qty",
+				"second_join_field": "so_detail",
+				"overflow_type": "delivery",
+				"second_source_extra_cond": """ and exists(select name from `tabSales Invoice`
+				where name=`tabSales Invoice Item`.parent and update_stock = 1)""",
 			},
 			{
-				'source_dt': 'Delivery Note Item',
-				'target_dt': 'Delivery Note Item',
-				'join_field': 'dn_detail',
-				'target_field': 'returned_qty',
-				'target_parent_dt': 'Delivery Note',
-				'target_parent_field': 'per_returned',
-				'target_ref_field': 'stock_qty',
-				'source_field': '-1 * stock_qty',
-				'percent_join_field_parent': 'return_against'
-			}
-		])
+				"source_dt": "Delivery Note Item",
+				"target_dt": "Sales Invoice Item",
+				"join_field": "si_detail",
+				"target_field": "delivered_qty",
+				"target_parent_dt": "Sales Invoice",
+				"target_ref_field": "qty",
+				"source_field": "qty",
+				"percent_join_field": "against_sales_invoice",
+				"overflow_type": "delivery",
+				"no_allowance": 1,
+			},
+		]
+		if cint(self.is_return):
+			self.status_updater.extend(
+				[
+					{
+						"source_dt": "Delivery Note Item",
+						"target_dt": "Sales Order Item",
+						"join_field": "so_detail",
+						"target_field": "returned_qty",
+						"target_parent_dt": "Sales Order",
+						"source_field": "-1 * qty",
+						"second_source_dt": "Sales Invoice Item",
+						"second_source_field": "-1 * qty",
+						"second_join_field": "so_detail",
+						"extra_cond": """ and exists (select name from `tabDelivery Note`
+					where name=`tabDelivery Note Item`.parent and is_return=1)""",
+						"second_source_extra_cond": """ and exists (select name from `tabSales Invoice`
+					where name=`tabSales Invoice Item`.parent and is_return=1 and update_stock=1)""",
+					},
+					{
+						"source_dt": "Delivery Note Item",
+						"target_dt": "Delivery Note Item",
+						"join_field": "dn_detail",
+						"target_field": "returned_qty",
+						"target_parent_dt": "Delivery Note",
+						"target_parent_field": "per_returned",
+						"target_ref_field": "stock_qty",
+						"source_field": "-1 * stock_qty",
+						"percent_join_field_parent": "return_against",
+					},
+				]
+			)
 
 	def before_print(self, settings=None):
 		def toggle_print_hide(meta, fieldname):
@@ -94,7 +97,7 @@
 		item_meta = frappe.get_meta("Delivery Note Item")
 		print_hide_fields = {
 			"parent": ["grand_total", "rounded_total", "in_words", "currency", "total", "taxes"],
-			"items": ["rate", "amount", "discount_amount", "price_list_rate", "discount_percentage"]
+			"items": ["rate", "amount", "discount_amount", "price_list_rate", "discount_percentage"],
 		}
 
 		for key, fieldname in print_hide_fields.items():
@@ -104,16 +107,19 @@
 		super(DeliveryNote, self).before_print(settings)
 
 	def set_actual_qty(self):
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if d.item_code and d.warehouse:
-				actual_qty = frappe.db.sql("""select actual_qty from `tabBin`
-					where item_code = %s and warehouse = %s""", (d.item_code, d.warehouse))
+				actual_qty = frappe.db.sql(
+					"""select actual_qty from `tabBin`
+					where item_code = %s and warehouse = %s""",
+					(d.item_code, d.warehouse),
+				)
 				d.actual_qty = actual_qty and flt(actual_qty[0][0]) or 0
 
 	def so_required(self):
 		"""check in manage account if sales order required or not"""
-		if frappe.db.get_value("Selling Settings", None, 'so_required') == 'Yes':
-			for d in self.get('items'):
+		if frappe.db.get_value("Selling Settings", None, "so_required") == "Yes":
+			for d in self.get("items"):
 				if not d.against_sales_order:
 					frappe.throw(_("Sales Order required for Item {0}").format(d.item_code))
 
@@ -129,76 +135,92 @@
 		self.validate_uom_is_integer("uom", "qty")
 		self.validate_with_previous_doc()
 
-		# Keeps mapped packed_items in case product bundle is updated.
-		if self.is_return and self.return_against:
-			calculate_mapped_packed_items_return(self)
-		else:
-			from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
-			make_packing_list(self)
+		from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
 
-		if self._action != 'submit' and not self.is_return:
-			set_batch_nos(self, 'warehouse', throw=True)
-			set_batch_nos(self, 'warehouse', throw=True, child_table="packed_items")
+		make_packing_list(self)
+
+		if self._action != "submit" and not self.is_return:
+			set_batch_nos(self, "warehouse", throw=True)
+			set_batch_nos(self, "warehouse", throw=True, child_table="packed_items")
 
 		self.update_current_stock()
 
-		if not self.installation_status: self.installation_status = 'Not Installed'
+		if not self.installation_status:
+			self.installation_status = "Not Installed"
 		self.reset_default_field_value("set_warehouse", "items", "warehouse")
 
 	def validate_with_previous_doc(self):
-		super(DeliveryNote, self).validate_with_previous_doc({
-			"Sales Order": {
-				"ref_dn_field": "against_sales_order",
-				"compare_fields": [["customer", "="], ["company", "="], ["project", "="], ["currency", "="]]
-			},
-			"Sales Order Item": {
-				"ref_dn_field": "so_detail",
-				"compare_fields": [["item_code", "="], ["uom", "="], ["conversion_factor", "="]],
-				"is_child_table": True,
-				"allow_duplicate_prev_row_id": True
-			},
-			"Sales Invoice": {
-				"ref_dn_field": "against_sales_invoice",
-				"compare_fields": [["customer", "="], ["company", "="], ["project", "="], ["currency", "="]]
-			},
-			"Sales Invoice Item": {
-				"ref_dn_field": "si_detail",
-				"compare_fields": [["item_code", "="], ["uom", "="], ["conversion_factor", "="]],
-				"is_child_table": True,
-				"allow_duplicate_prev_row_id": True
-			},
-		})
+		super(DeliveryNote, self).validate_with_previous_doc(
+			{
+				"Sales Order": {
+					"ref_dn_field": "against_sales_order",
+					"compare_fields": [["customer", "="], ["company", "="], ["project", "="], ["currency", "="]],
+				},
+				"Sales Order Item": {
+					"ref_dn_field": "so_detail",
+					"compare_fields": [["item_code", "="], ["uom", "="], ["conversion_factor", "="]],
+					"is_child_table": True,
+					"allow_duplicate_prev_row_id": True,
+				},
+				"Sales Invoice": {
+					"ref_dn_field": "against_sales_invoice",
+					"compare_fields": [["customer", "="], ["company", "="], ["project", "="], ["currency", "="]],
+				},
+				"Sales Invoice Item": {
+					"ref_dn_field": "si_detail",
+					"compare_fields": [["item_code", "="], ["uom", "="], ["conversion_factor", "="]],
+					"is_child_table": True,
+					"allow_duplicate_prev_row_id": True,
+				},
+			}
+		)
 
-		if cint(frappe.db.get_single_value('Selling Settings', 'maintain_same_sales_rate')) \
-				and not self.is_return:
-			self.validate_rate_with_reference_doc([["Sales Order", "against_sales_order", "so_detail"],
-				["Sales Invoice", "against_sales_invoice", "si_detail"]])
+		if (
+			cint(frappe.db.get_single_value("Selling Settings", "maintain_same_sales_rate"))
+			and not self.is_return
+		):
+			self.validate_rate_with_reference_doc(
+				[
+					["Sales Order", "against_sales_order", "so_detail"],
+					["Sales Invoice", "against_sales_invoice", "si_detail"],
+				]
+			)
 
 	def validate_proj_cust(self):
 		"""check for does customer belong to same project as entered.."""
 		if self.project and self.customer:
-			res = frappe.db.sql("""select name from `tabProject`
+			res = frappe.db.sql(
+				"""select name from `tabProject`
 				where name = %s and (customer = %s or
-					ifnull(customer,'')='')""", (self.project, self.customer))
+					ifnull(customer,'')='')""",
+				(self.project, self.customer),
+			)
 			if not res:
-				frappe.throw(_("Customer {0} does not belong to project {1}").format(self.customer, self.project))
+				frappe.throw(
+					_("Customer {0} does not belong to project {1}").format(self.customer, self.project)
+				)
 
 	def validate_warehouse(self):
 		super(DeliveryNote, self).validate_warehouse()
 
 		for d in self.get_item_list():
-			if not d['warehouse'] and frappe.db.get_value("Item", d['item_code'], "is_stock_item") == 1:
+			if not d["warehouse"] and frappe.db.get_value("Item", d["item_code"], "is_stock_item") == 1:
 				frappe.throw(_("Warehouse required for stock Item {0}").format(d["item_code"]))
 
 	def update_current_stock(self):
 		if self.get("_action") and self._action != "update_after_submit":
-			for d in self.get('items'):
-				d.actual_qty = frappe.db.get_value("Bin", {"item_code": d.item_code,
-					"warehouse": d.warehouse}, "actual_qty")
+			for d in self.get("items"):
+				d.actual_qty = frappe.db.get_value(
+					"Bin", {"item_code": d.item_code, "warehouse": d.warehouse}, "actual_qty"
+				)
 
-			for d in self.get('packed_items'):
-				bin_qty = frappe.db.get_value("Bin", {"item_code": d.item_code,
-					"warehouse": d.warehouse}, ["actual_qty", "projected_qty"], as_dict=True)
+			for d in self.get("packed_items"):
+				bin_qty = frappe.db.get_value(
+					"Bin",
+					{"item_code": d.item_code, "warehouse": d.warehouse},
+					["actual_qty", "projected_qty"],
+					as_dict=True,
+				)
 				if bin_qty:
 					d.actual_qty = flt(bin_qty.actual_qty)
 					d.projected_qty = flt(bin_qty.projected_qty)
@@ -207,7 +229,9 @@
 		self.validate_packed_qty()
 
 		# Check for Approving Authority
-		frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, self.company, self.base_grand_total, self)
+		frappe.get_doc("Authorization Control").validate_approving_authority(
+			self.doctype, self.company, self.base_grand_total, self
+		)
 
 		# update delivered qty in sales order
 		self.update_prevdoc_status()
@@ -240,20 +264,27 @@
 
 		self.make_gl_entries_on_cancel()
 		self.repost_future_sle_and_gle()
-		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
+		self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Repost Item Valuation")
 
 	def check_credit_limit(self):
 		from erpnext.selling.doctype.customer.customer import check_credit_limit
 
 		extra_amount = 0
 		validate_against_credit_limit = False
-		bypass_credit_limit_check_at_sales_order = cint(frappe.db.get_value("Customer Credit Limit",
-			filters={'parent': self.customer, 'parenttype': 'Customer', 'company': self.company},
-			fieldname="bypass_credit_limit_check"))
+		bypass_credit_limit_check_at_sales_order = cint(
+			frappe.db.get_value(
+				"Customer Credit Limit",
+				filters={"parent": self.customer, "parenttype": "Customer", "company": self.company},
+				fieldname="bypass_credit_limit_check",
+			)
+		)
 
 		if bypass_credit_limit_check_at_sales_order:
-			validate_against_credit_limit = True
-			extra_amount = self.base_grand_total
+			for d in self.get("items"):
+				if not d.against_sales_invoice:
+					validate_against_credit_limit = True
+					extra_amount = self.base_grand_total
+					break
 		else:
 			for d in self.get("items"):
 				if not (d.against_sales_order or d.against_sales_invoice):
@@ -261,48 +292,58 @@
 					break
 
 		if validate_against_credit_limit:
-			check_credit_limit(self.customer, self.company,
-				bypass_credit_limit_check_at_sales_order, extra_amount)
+			check_credit_limit(
+				self.customer, self.company, bypass_credit_limit_check_at_sales_order, extra_amount
+			)
 
 	def validate_packed_qty(self):
 		"""
-			Validate that if packed qty exists, it should be equal to qty
+		Validate that if packed qty exists, it should be equal to qty
 		"""
-		if not any(flt(d.get('packed_qty')) for d in self.get("items")):
+		if not any(flt(d.get("packed_qty")) for d in self.get("items")):
 			return
 		has_error = False
 		for d in self.get("items"):
-			if flt(d.get('qty')) != flt(d.get('packed_qty')):
-				frappe.msgprint(_("Packed quantity must equal quantity for Item {0} in row {1}").format(d.item_code, d.idx))
+			if flt(d.get("qty")) != flt(d.get("packed_qty")):
+				frappe.msgprint(
+					_("Packed quantity must equal quantity for Item {0} in row {1}").format(d.item_code, d.idx)
+				)
 				has_error = True
 		if has_error:
 			raise frappe.ValidationError
 
 	def check_next_docstatus(self):
-		submit_rv = frappe.db.sql("""select t1.name
+		submit_rv = frappe.db.sql(
+			"""select t1.name
 			from `tabSales Invoice` t1,`tabSales Invoice Item` t2
 			where t1.name = t2.parent and t2.delivery_note = %s and t1.docstatus = 1""",
-			(self.name))
+			(self.name),
+		)
 		if submit_rv:
 			frappe.throw(_("Sales Invoice {0} has already been submitted").format(submit_rv[0][0]))
 
-		submit_in = frappe.db.sql("""select t1.name
+		submit_in = frappe.db.sql(
+			"""select t1.name
 			from `tabInstallation Note` t1, `tabInstallation Note Item` t2
 			where t1.name = t2.parent and t2.prevdoc_docname = %s and t1.docstatus = 1""",
-			(self.name))
+			(self.name),
+		)
 		if submit_in:
 			frappe.throw(_("Installation Note {0} has already been submitted").format(submit_in[0][0]))
 
 	def cancel_packing_slips(self):
 		"""
-			Cancel submitted packing slips related to this delivery note
+		Cancel submitted packing slips related to this delivery note
 		"""
-		res = frappe.db.sql("""SELECT name FROM `tabPacking Slip` WHERE delivery_note = %s
-			AND docstatus = 1""", self.name)
+		res = frappe.db.sql(
+			"""SELECT name FROM `tabPacking Slip` WHERE delivery_note = %s
+			AND docstatus = 1""",
+			self.name,
+		)
 
 		if res:
 			for r in res:
-				ps = frappe.get_doc('Packing Slip', r[0])
+				ps = frappe.get_doc("Packing Slip", r[0])
 				ps.cancel()
 			frappe.msgprint(_("Packing Slip(s) cancelled"))
 
@@ -315,7 +356,7 @@
 		updated_delivery_notes = [self.name]
 		for d in self.get("items"):
 			if d.si_detail and not d.so_detail:
-				d.db_set('billed_amt', d.amount, update_modified=update_modified)
+				d.db_set("billed_amt", d.amount, update_modified=update_modified)
 			elif d.so_detail:
 				updated_delivery_notes += update_billed_amount_based_on_so(d.so_detail, update_modified)
 
@@ -332,42 +373,53 @@
 			return_invoice.save()
 			return_invoice.submit()
 
-			credit_note_link = frappe.utils.get_link_to_form('Sales Invoice', return_invoice.name)
+			credit_note_link = frappe.utils.get_link_to_form("Sales Invoice", return_invoice.name)
 
 			frappe.msgprint(_("Credit Note {0} has been created automatically").format(credit_note_link))
 		except Exception:
-			frappe.throw(_("Could not create Credit Note automatically, please uncheck 'Issue Credit Note' and submit again"))
+			frappe.throw(
+				_(
+					"Could not create Credit Note automatically, please uncheck 'Issue Credit Note' and submit again"
+				)
+			)
+
 
 def update_billed_amount_based_on_so(so_detail, update_modified=True):
 	from frappe.query_builder.functions import Sum
 
 	# Billed against Sales Order directly
-	si = frappe.qb.DocType("Sales Invoice").as_("si")
 	si_item = frappe.qb.DocType("Sales Invoice Item").as_("si_item")
 	sum_amount = Sum(si_item.amount).as_("amount")
 
-	billed_against_so = frappe.qb.from_(si).from_(si_item).select(sum_amount).where(
-		(si_item.parent == si.name) &
-		(si_item.so_detail == so_detail) &
-		((si_item.dn_detail.isnull()) | (si_item.dn_detail == '')) &
-		(si_item.docstatus == 1) &
-		(si.update_stock == 0)
-	).run()
+	billed_against_so = (
+		frappe.qb.from_(si_item)
+		.select(sum_amount)
+		.where(
+			(si_item.so_detail == so_detail)
+			& ((si_item.dn_detail.isnull()) | (si_item.dn_detail == ""))
+			& (si_item.docstatus == 1)
+		)
+		.run()
+	)
 	billed_against_so = billed_against_so and billed_against_so[0][0] or 0
 
 	# Get all Delivery Note Item rows against the Sales Order Item row
-
 	dn = frappe.qb.DocType("Delivery Note").as_("dn")
 	dn_item = frappe.qb.DocType("Delivery Note Item").as_("dn_item")
 
-	dn_details = frappe.qb.from_(dn).from_(dn_item).select(dn_item.name, dn_item.amount, dn_item.si_detail, dn_item.parent, dn_item.stock_qty, dn_item.returned_qty).where(
-		(dn.name == dn_item.parent) &
-		(dn_item.so_detail == so_detail) &
-		(dn.docstatus == 1) &
-		(dn.is_return == 0)
-	).orderby(
-		dn.posting_date, dn.posting_time, dn.name
-	).run(as_dict=True)
+	dn_details = (
+		frappe.qb.from_(dn)
+		.from_(dn_item)
+		.select(dn_item.name, dn_item.amount, dn_item.si_detail, dn_item.parent)
+		.where(
+			(dn.name == dn_item.parent)
+			& (dn_item.so_detail == so_detail)
+			& (dn.docstatus == 1)
+			& (dn.is_return == 0)
+		)
+		.orderby(dn.posting_date, dn.posting_time, dn.name)
+		.run(as_dict=True)
+	)
 
 	updated_dn = []
 	for dnd in dn_details:
@@ -379,17 +431,16 @@
 			billed_against_so -= billed_amt_agianst_dn
 		else:
 			# Get billed amount directly against Delivery Note
-			billed_amt_agianst_dn = frappe.db.sql("""select sum(amount) from `tabSales Invoice Item`
-				where dn_detail=%s and docstatus=1""", dnd.name)
+			billed_amt_agianst_dn = frappe.db.sql(
+				"""select sum(amount) from `tabSales Invoice Item`
+				where dn_detail=%s and docstatus=1""",
+				dnd.name,
+			)
 			billed_amt_agianst_dn = billed_amt_agianst_dn and billed_amt_agianst_dn[0][0] or 0
 
 		# Distribute billed amount directly against SO between DNs based on FIFO
 		if billed_against_so and billed_amt_agianst_dn < dnd.amount:
-			if dnd.returned_qty:
-				pending_to_bill = flt(dnd.amount) * (dnd.stock_qty - dnd.returned_qty) / dnd.stock_qty
-			else:
-				pending_to_bill = flt(dnd.amount)
-			pending_to_bill -= billed_amt_agianst_dn
+			pending_to_bill = flt(dnd.amount) - billed_amt_agianst_dn
 			if pending_to_bill <= billed_against_so:
 				billed_amt_agianst_dn += pending_to_bill
 				billed_against_so -= pending_to_bill
@@ -397,57 +448,77 @@
 				billed_amt_agianst_dn += billed_against_so
 				billed_against_so = 0
 
-		frappe.db.set_value("Delivery Note Item", dnd.name, "billed_amt", billed_amt_agianst_dn, update_modified=update_modified)
+		frappe.db.set_value(
+			"Delivery Note Item",
+			dnd.name,
+			"billed_amt",
+			billed_amt_agianst_dn,
+			update_modified=update_modified,
+		)
 
 		updated_dn.append(dnd.parent)
 
 	return updated_dn
 
+
 def get_list_context(context=None):
 	from erpnext.controllers.website_list_for_contact import get_list_context
+
 	list_context = get_list_context(context)
-	list_context.update({
-		'show_sidebar': True,
-		'show_search': True,
-		'no_breadcrumbs': True,
-		'title': _('Shipments'),
-	})
+	list_context.update(
+		{
+			"show_sidebar": True,
+			"show_search": True,
+			"no_breadcrumbs": True,
+			"title": _("Shipments"),
+		}
+	)
 	return list_context
 
+
 def get_invoiced_qty_map(delivery_note):
 	"""returns a map: {dn_detail: invoiced_qty}"""
 	invoiced_qty_map = {}
 
-	for dn_detail, qty in frappe.db.sql("""select dn_detail, qty from `tabSales Invoice Item`
-		where delivery_note=%s and docstatus=1""", delivery_note):
-			if not invoiced_qty_map.get(dn_detail):
-				invoiced_qty_map[dn_detail] = 0
-			invoiced_qty_map[dn_detail] += qty
+	for dn_detail, qty in frappe.db.sql(
+		"""select dn_detail, qty from `tabSales Invoice Item`
+		where delivery_note=%s and docstatus=1""",
+		delivery_note,
+	):
+		if not invoiced_qty_map.get(dn_detail):
+			invoiced_qty_map[dn_detail] = 0
+		invoiced_qty_map[dn_detail] += qty
 
 	return invoiced_qty_map
 
+
 def get_returned_qty_map(delivery_note):
 	"""returns a map: {so_detail: returned_qty}"""
-	returned_qty_map = frappe._dict(frappe.db.sql("""select dn_item.dn_detail, abs(dn_item.qty) as qty
+	returned_qty_map = frappe._dict(
+		frappe.db.sql(
+			"""select dn_item.dn_detail, abs(dn_item.qty) as qty
 		from `tabDelivery Note Item` dn_item, `tabDelivery Note` dn
 		where dn.name = dn_item.parent
 			and dn.docstatus = 1
 			and dn.is_return = 1
 			and dn.return_against = %s
-	""", delivery_note))
+	""",
+			delivery_note,
+		)
+	)
 
 	return returned_qty_map
 
+
 @frappe.whitelist()
 def make_sales_invoice(source_name, target_doc=None):
-	doc = frappe.get_doc('Delivery Note', source_name)
+	doc = frappe.get_doc("Delivery Note", source_name)
 
 	to_make_invoice_qty_map = {}
 	returned_qty_map = get_returned_qty_map(source_name)
 	invoiced_qty_map = get_invoiced_qty_map(source_name)
 
 	def set_missing_values(source, target):
-		target.ignore_pricing_rule = 1
 		target.run_method("set_missing_values")
 		target.run_method("set_po_nos")
 
@@ -458,20 +529,21 @@
 
 		# set company address
 		if source.company_address:
-			target.update({'company_address': source.company_address})
+			target.update({"company_address": source.company_address})
 		else:
 			# set company address
 			target.update(get_company_address(target.company))
 
 		if target.company_address:
-			target.update(get_fetch_values("Sales Invoice", 'company_address', target.company_address))
+			target.update(get_fetch_values("Sales Invoice", "company_address", target.company_address))
 
 	def update_item(source_doc, target_doc, source_parent):
 		target_doc.qty = to_make_invoice_qty_map[source_doc.name]
 
 		if source_doc.serial_no and source_parent.per_billed > 0 and not source_parent.is_return:
-			target_doc.serial_no = get_delivery_note_serial_no(source_doc.item_code,
-				target_doc.qty, source_parent.name)
+			target_doc.serial_no = get_delivery_note_serial_no(
+				source_doc.item_code, target_doc.qty, source_parent.name
+			)
 
 	def get_pending_qty(item_row):
 		pending_qty = item_row.qty - invoiced_qty_map.get(item_row.name, 0)
@@ -493,48 +565,52 @@
 
 		return pending_qty
 
-	doc = get_mapped_doc("Delivery Note", source_name, {
-		"Delivery Note": {
-			"doctype": "Sales Invoice",
-			"field_map": {
-				"is_return": "is_return"
+	doc = get_mapped_doc(
+		"Delivery Note",
+		source_name,
+		{
+			"Delivery Note": {
+				"doctype": "Sales Invoice",
+				"field_map": {"is_return": "is_return"},
+				"validation": {"docstatus": ["=", 1]},
 			},
-			"validation": {
-				"docstatus": ["=", 1]
-			}
-		},
-		"Delivery Note Item": {
-			"doctype": "Sales Invoice Item",
-			"field_map": {
-				"name": "dn_detail",
-				"parent": "delivery_note",
-				"so_detail": "so_detail",
-				"against_sales_order": "sales_order",
-				"serial_no": "serial_no",
-				"cost_center": "cost_center"
+			"Delivery Note Item": {
+				"doctype": "Sales Invoice Item",
+				"field_map": {
+					"name": "dn_detail",
+					"parent": "delivery_note",
+					"so_detail": "so_detail",
+					"against_sales_order": "sales_order",
+					"serial_no": "serial_no",
+					"cost_center": "cost_center",
+				},
+				"postprocess": update_item,
+				"filter": lambda d: get_pending_qty(d) <= 0
+				if not doc.get("is_return")
+				else get_pending_qty(d) > 0,
 			},
-			"postprocess": update_item,
-			"filter": lambda d: get_pending_qty(d) <= 0 if not doc.get("is_return") else get_pending_qty(d) > 0
-		},
-		"Sales Taxes and Charges": {
-			"doctype": "Sales Taxes and Charges",
-			"add_if_empty": True
-		},
-		"Sales Team": {
-			"doctype": "Sales Team",
-			"field_map": {
-				"incentives": "incentives"
+			"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True},
+			"Sales Team": {
+				"doctype": "Sales Team",
+				"field_map": {"incentives": "incentives"},
+				"add_if_empty": True,
 			},
-			"add_if_empty": True
-		}
-	}, target_doc, set_missing_values)
+		},
+		target_doc,
+		set_missing_values,
+	)
 
-	automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms'))
+	automatically_fetch_payment_terms = cint(
+		frappe.db.get_single_value("Accounts Settings", "automatically_fetch_payment_terms")
+	)
 	if automatically_fetch_payment_terms:
 		doc.set_payment_schedule()
 
+	doc.set_onload("ignore_price_list", True)
+
 	return doc
 
+
 @frappe.whitelist()
 def make_delivery_trip(source_name, target_doc=None):
 	def update_stop_details(source_doc, target_doc, source_parent):
@@ -550,107 +626,110 @@
 
 	delivery_notes = []
 
-	doclist = get_mapped_doc("Delivery Note", source_name, {
-		"Delivery Note": {
-			"doctype": "Delivery Trip",
-			"validation": {
-				"docstatus": ["=", 1]
-			}
-		},
-		"Delivery Note Item": {
-			"doctype": "Delivery Stop",
-			"field_map": {
-				"parent": "delivery_note"
+	doclist = get_mapped_doc(
+		"Delivery Note",
+		source_name,
+		{
+			"Delivery Note": {"doctype": "Delivery Trip", "validation": {"docstatus": ["=", 1]}},
+			"Delivery Note Item": {
+				"doctype": "Delivery Stop",
+				"field_map": {"parent": "delivery_note"},
+				"condition": lambda item: item.parent not in delivery_notes,
+				"postprocess": update_stop_details,
 			},
-			"condition": lambda item: item.parent not in delivery_notes,
-			"postprocess": update_stop_details
-		}
-	}, target_doc)
+		},
+		target_doc,
+	)
 
 	return doclist
 
+
 @frappe.whitelist()
 def make_installation_note(source_name, target_doc=None):
 	def update_item(obj, target, source_parent):
 		target.qty = flt(obj.qty) - flt(obj.installed_qty)
 		target.serial_no = obj.serial_no
 
-	doclist = get_mapped_doc("Delivery Note", source_name, 	{
-		"Delivery Note": {
-			"doctype": "Installation Note",
-			"validation": {
-				"docstatus": ["=", 1]
-			}
-		},
-		"Delivery Note Item": {
-			"doctype": "Installation Note Item",
-			"field_map": {
-				"name": "prevdoc_detail_docname",
-				"parent": "prevdoc_docname",
-				"parenttype": "prevdoc_doctype",
+	doclist = get_mapped_doc(
+		"Delivery Note",
+		source_name,
+		{
+			"Delivery Note": {"doctype": "Installation Note", "validation": {"docstatus": ["=", 1]}},
+			"Delivery Note Item": {
+				"doctype": "Installation Note Item",
+				"field_map": {
+					"name": "prevdoc_detail_docname",
+					"parent": "prevdoc_docname",
+					"parenttype": "prevdoc_doctype",
+				},
+				"postprocess": update_item,
+				"condition": lambda doc: doc.installed_qty < doc.qty,
 			},
-			"postprocess": update_item,
-			"condition": lambda doc: doc.installed_qty < doc.qty
-		}
-	}, target_doc)
+		},
+		target_doc,
+	)
 
 	return doclist
 
+
 @frappe.whitelist()
 def make_packing_slip(source_name, target_doc=None):
-	doclist = get_mapped_doc("Delivery Note", source_name, 	{
-		"Delivery Note": {
-			"doctype": "Packing Slip",
-			"field_map": {
-				"name": "delivery_note",
-				"letter_head": "letter_head"
+	doclist = get_mapped_doc(
+		"Delivery Note",
+		source_name,
+		{
+			"Delivery Note": {
+				"doctype": "Packing Slip",
+				"field_map": {"name": "delivery_note", "letter_head": "letter_head"},
+				"validation": {"docstatus": ["=", 0]},
 			},
-			"validation": {
-				"docstatus": ["=", 0]
-			}
+			"Delivery Note Item": {
+				"doctype": "Packing Slip Item",
+				"field_map": {
+					"item_code": "item_code",
+					"item_name": "item_name",
+					"description": "description",
+					"qty": "qty",
+				},
+			},
 		},
-
-		"Delivery Note Item": {
-			"doctype": "Packing Slip Item",
-			"field_map": {
-				"item_code": "item_code",
-				"item_name": "item_name",
-				"description": "description",
-				"qty": "qty",
-			}
-		}
-
-	}, target_doc)
+		target_doc,
+	)
 
 	return doclist
 
+
 @frappe.whitelist()
 def make_shipment(source_name, target_doc=None):
 	def postprocess(source, target):
-		user = frappe.db.get_value("User", frappe.session.user, ['email', 'full_name', 'phone', 'mobile_no'], as_dict=1)
+		user = frappe.db.get_value(
+			"User", frappe.session.user, ["email", "full_name", "phone", "mobile_no"], as_dict=1
+		)
 		target.pickup_contact_email = user.email
-		pickup_contact_display = '{}'.format(user.full_name)
+		pickup_contact_display = "{}".format(user.full_name)
 		if user:
 			if user.email:
-				pickup_contact_display += '<br>' + user.email
+				pickup_contact_display += "<br>" + user.email
 			if user.phone:
-				pickup_contact_display += '<br>' + user.phone
+				pickup_contact_display += "<br>" + user.phone
 			if user.mobile_no and not user.phone:
-				pickup_contact_display += '<br>' + user.mobile_no
+				pickup_contact_display += "<br>" + user.mobile_no
 		target.pickup_contact = pickup_contact_display
 
 		# As we are using session user details in the pickup_contact then pickup_contact_person will be session user
 		target.pickup_contact_person = frappe.session.user
 
-		contact = frappe.db.get_value("Contact", source.contact_person, ['email_id', 'phone', 'mobile_no'], as_dict=1)
-		delivery_contact_display = '{}'.format(source.contact_display)
+		contact = frappe.db.get_value(
+			"Contact", source.contact_person, ["email_id", "phone", "mobile_no"], as_dict=1
+		)
+		delivery_contact_display = "{}".format(source.contact_display)
 		if contact:
 			if contact.email_id:
-				delivery_contact_display += '<br>' + contact.email_id
+				delivery_contact_display += "<br>" + contact.email_id
 			if contact.phone:
-				delivery_contact_display += '<br>' + contact.phone
+				delivery_contact_display += "<br>" + contact.phone
 			if contact.mobile_no and not contact.phone:
-				delivery_contact_display += '<br>' + contact.mobile_no
+				delivery_contact_display += "<br>" + contact.mobile_no
 		target.delivery_contact = delivery_contact_display
 
 		if source.shipping_address_name:
@@ -660,38 +739,44 @@
 			target.delivery_address_name = source.customer_address
 			target.delivery_address = source.address_display
 
-	doclist = get_mapped_doc("Delivery Note", source_name, 	{
-		"Delivery Note": {
-			"doctype": "Shipment",
-			"field_map": {
-				"grand_total": "value_of_goods",
-				"company": "pickup_company",
-				"company_address": "pickup_address_name",
-				"company_address_display": "pickup_address",
-				"customer": "delivery_customer",
-				"contact_person": "delivery_contact_name",
-				"contact_email": "delivery_contact_email"
+	doclist = get_mapped_doc(
+		"Delivery Note",
+		source_name,
+		{
+			"Delivery Note": {
+				"doctype": "Shipment",
+				"field_map": {
+					"grand_total": "value_of_goods",
+					"company": "pickup_company",
+					"company_address": "pickup_address_name",
+					"company_address_display": "pickup_address",
+					"customer": "delivery_customer",
+					"contact_person": "delivery_contact_name",
+					"contact_email": "delivery_contact_email",
+				},
+				"validation": {"docstatus": ["=", 1]},
 			},
-			"validation": {
-				"docstatus": ["=", 1]
-			}
+			"Delivery Note Item": {
+				"doctype": "Shipment Delivery Note",
+				"field_map": {
+					"name": "prevdoc_detail_docname",
+					"parent": "prevdoc_docname",
+					"parenttype": "prevdoc_doctype",
+					"base_amount": "grand_total",
+				},
+			},
 		},
-		"Delivery Note Item": {
-			"doctype": "Shipment Delivery Note",
-			"field_map": {
-				"name": "prevdoc_detail_docname",
-				"parent": "prevdoc_docname",
-				"parenttype": "prevdoc_doctype",
-				"base_amount": "grand_total"
-			}
-		}
-	}, target_doc, postprocess)
+		target_doc,
+		postprocess,
+	)
 
 	return doclist
 
+
 @frappe.whitelist()
 def make_sales_return(source_name, target_doc=None):
 	from erpnext.controllers.sales_and_purchase_return import make_return_doc
+
 	return make_return_doc("Delivery Note", source_name, target_doc)
 
 
@@ -700,10 +785,12 @@
 	dn = frappe.get_doc("Delivery Note", docname)
 	dn.update_status(status)
 
+
 @frappe.whitelist()
 def make_inter_company_purchase_receipt(source_name, target_doc=None):
 	return make_inter_company_transaction("Delivery Note", source_name, target_doc)
 
+
 def make_inter_company_transaction(doctype, source_name, target_doc=None):
 	from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
 		get_inter_company_details,
@@ -713,16 +800,16 @@
 		validate_inter_company_transaction,
 	)
 
-	if doctype == 'Delivery Note':
+	if doctype == "Delivery Note":
 		source_doc = frappe.get_doc(doctype, source_name)
 		target_doctype = "Purchase Receipt"
-		source_document_warehouse_field = 'target_warehouse'
-		target_document_warehouse_field = 'from_warehouse'
+		source_document_warehouse_field = "target_warehouse"
+		target_document_warehouse_field = "from_warehouse"
 	else:
 		source_doc = frappe.get_doc(doctype, source_name)
-		target_doctype = 'Delivery Note'
-		source_document_warehouse_field = 'from_warehouse'
-		target_document_warehouse_field = 'target_warehouse'
+		target_doctype = "Delivery Note"
+		source_document_warehouse_field = "from_warehouse"
+		target_document_warehouse_field = "target_warehouse"
 
 	validate_inter_company_transaction(source_doc, doctype)
 	details = get_inter_company_details(source_doc, doctype)
@@ -731,18 +818,18 @@
 		target.run_method("set_missing_values")
 		set_purchase_references(target)
 
-		if target.doctype == 'Purchase Receipt':
-			master_doctype = 'Purchase Taxes and Charges Template'
+		if target.doctype == "Purchase Receipt":
+			master_doctype = "Purchase Taxes and Charges Template"
 		else:
-			master_doctype = 'Sales Taxes and Charges Template'
+			master_doctype = "Sales Taxes and Charges Template"
 
-		if not target.get('taxes') and target.get('taxes_and_charges'):
-			for tax in get_taxes_and_charges(master_doctype, target.get('taxes_and_charges')):
-				target.append('taxes', tax)
+		if not target.get("taxes") and target.get("taxes_and_charges"):
+			for tax in get_taxes_and_charges(master_doctype, target.get("taxes_and_charges")):
+				target.append("taxes", tax)
 
 	def update_details(source_doc, target_doc, source_parent):
 		target_doc.inter_company_invoice_reference = source_doc.name
-		if target_doc.doctype == 'Purchase Receipt':
+		if target_doc.doctype == "Purchase Receipt":
 			target_doc.company = details.get("company")
 			target_doc.supplier = details.get("party")
 			target_doc.buying_price_list = source_doc.selling_price_list
@@ -750,12 +837,20 @@
 			target_doc.inter_company_reference = source_doc.name
 
 			# Invert the address on target doc creation
-			update_address(target_doc, 'supplier_address', 'address_display', source_doc.company_address)
-			update_address(target_doc, 'shipping_address', 'shipping_address_display', source_doc.customer_address)
+			update_address(target_doc, "supplier_address", "address_display", source_doc.company_address)
+			update_address(
+				target_doc, "shipping_address", "shipping_address_display", source_doc.customer_address
+			)
 
-			update_taxes(target_doc, party=target_doc.supplier, party_type='Supplier', company=target_doc.company,
-				doctype=target_doc.doctype, party_address=target_doc.supplier_address,
-				company_address=target_doc.shipping_address)
+			update_taxes(
+				target_doc,
+				party=target_doc.supplier,
+				party_type="Supplier",
+				company=target_doc.company,
+				doctype=target_doc.doctype,
+				party_address=target_doc.supplier_address,
+				company_address=target_doc.shipping_address,
+			)
 		else:
 			target_doc.company = details.get("company")
 			target_doc.customer = details.get("party")
@@ -765,36 +860,52 @@
 			target_doc.inter_company_reference = source_doc.name
 
 			# Invert the address on target doc creation
-			update_address(target_doc, 'company_address', 'company_address_display', source_doc.supplier_address)
-			update_address(target_doc, 'shipping_address_name', 'shipping_address', source_doc.shipping_address)
-			update_address(target_doc, 'customer_address', 'address_display', source_doc.shipping_address)
+			update_address(
+				target_doc, "company_address", "company_address_display", source_doc.supplier_address
+			)
+			update_address(
+				target_doc, "shipping_address_name", "shipping_address", source_doc.shipping_address
+			)
+			update_address(target_doc, "customer_address", "address_display", source_doc.shipping_address)
 
-			update_taxes(target_doc, party=target_doc.customer, party_type='Customer', company=target_doc.company,
-				doctype=target_doc.doctype, party_address=target_doc.customer_address,
-				company_address=target_doc.company_address, shipping_address_name=target_doc.shipping_address_name)
+			update_taxes(
+				target_doc,
+				party=target_doc.customer,
+				party_type="Customer",
+				company=target_doc.company,
+				doctype=target_doc.doctype,
+				party_address=target_doc.customer_address,
+				company_address=target_doc.company_address,
+				shipping_address_name=target_doc.shipping_address_name,
+			)
 
-	doclist = get_mapped_doc(doctype, source_name, {
-		doctype: {
-			"doctype": target_doctype,
-			"postprocess": update_details,
-			"field_no_map": [
-				"taxes_and_charges",
-				"set_warehouse"
-			]
-		},
-		doctype +" Item": {
-			"doctype": target_doctype + " Item",
-			"field_map": {
-				source_document_warehouse_field: target_document_warehouse_field,
-				'name': 'delivery_note_item',
-				'batch_no': 'batch_no',
-				'serial_no': 'serial_no'
+	doclist = get_mapped_doc(
+		doctype,
+		source_name,
+		{
+			doctype: {
+				"doctype": target_doctype,
+				"postprocess": update_details,
+				"field_no_map": ["taxes_and_charges", "set_warehouse"],
 			},
-			"field_no_map": [
-				"warehouse"
-			]
-		}
-
-	}, target_doc, set_missing_values)
+			doctype
+			+ " Item": {
+				"doctype": target_doctype + " Item",
+				"field_map": {
+					source_document_warehouse_field: target_document_warehouse_field,
+					"name": "delivery_note_item",
+					"batch_no": "batch_no",
+					"serial_no": "serial_no",
+				},
+				"field_no_map": ["warehouse"],
+			},
+		},
+		target_doc,
+		set_missing_values,
+	)
 
 	return doclist
+
+
+def on_doctype_update():
+	frappe.db.add_index("Delivery Note", ["customer", "is_return", "return_against"])
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py b/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py
index ca61a36..fd44e9c 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py
@@ -3,31 +3,19 @@
 
 def get_data():
 	return {
-		'fieldname': 'delivery_note',
-		'non_standard_fieldnames': {
-			'Stock Entry': 'delivery_note_no',
-			'Quality Inspection': 'reference_name',
-			'Auto Repeat': 'reference_document',
+		"fieldname": "delivery_note",
+		"non_standard_fieldnames": {
+			"Stock Entry": "delivery_note_no",
+			"Quality Inspection": "reference_name",
+			"Auto Repeat": "reference_document",
 		},
-		'internal_links': {
-			'Sales Order': ['items', 'against_sales_order'],
+		"internal_links": {
+			"Sales Order": ["items", "against_sales_order"],
 		},
-		'transactions': [
-			{
-				'label': _('Related'),
-				'items': ['Sales Invoice', 'Packing Slip', 'Delivery Trip']
-			},
-			{
-				'label': _('Reference'),
-				'items': ['Sales Order', 'Shipment', 'Quality Inspection']
-			},
-			{
-				'label': _('Returns'),
-				'items': ['Stock Entry']
-			},
-			{
-				'label': _('Subscription'),
-				'items': ['Auto Repeat']
-			},
-		]
+		"transactions": [
+			{"label": _("Related"), "items": ["Sales Invoice", "Packing Slip", "Delivery Trip"]},
+			{"label": _("Reference"), "items": ["Sales Order", "Shipment", "Quality Inspection"]},
+			{"label": _("Returns"), "items": ["Stock Entry"]},
+			{"label": _("Subscription"), "items": ["Auto Repeat"]},
+		],
 	}
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 16c8921..fffcdca 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -2,7 +2,6 @@
 # License: GNU General Public License v3. See license.txt
 
 
-
 import json
 
 import frappe
@@ -54,93 +53,69 @@
 		self.assertRaises(frappe.ValidationError, frappe.get_doc(si).insert)
 
 	def test_delivery_note_no_gl_entry(self):
-		company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company')
+		company = frappe.db.get_value("Warehouse", "_Test Warehouse - _TC", "company")
 		make_stock_entry(target="_Test Warehouse - _TC", qty=5, basic_rate=100)
 
-		stock_queue = json.loads(get_previous_sle({
-			"item_code": "_Test Item",
-			"warehouse": "_Test Warehouse - _TC",
-			"posting_date": nowdate(),
-			"posting_time": nowtime()
-		}).stock_queue or "[]")
+		stock_queue = json.loads(
+			get_previous_sle(
+				{
+					"item_code": "_Test Item",
+					"warehouse": "_Test Warehouse - _TC",
+					"posting_date": nowdate(),
+					"posting_time": nowtime(),
+				}
+			).stock_queue
+			or "[]"
+		)
 
 		dn = create_delivery_note()
 
-		sle = frappe.get_doc("Stock Ledger Entry", {"voucher_type": "Delivery Note", "voucher_no": dn.name})
+		sle = frappe.get_doc(
+			"Stock Ledger Entry", {"voucher_type": "Delivery Note", "voucher_no": dn.name}
+		)
 
-		self.assertEqual(sle.stock_value_difference, flt(-1*stock_queue[0][1], 2))
+		self.assertEqual(sle.stock_value_difference, flt(-1 * stock_queue[0][1], 2))
 
 		self.assertFalse(get_gl_entries("Delivery Note", dn.name))
 
-	# def test_delivery_note_gl_entry(self):
-	# 	company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
-
-	# 	set_valuation_method("_Test Item", "FIFO")
-
-	# 	make_stock_entry(target="Stores - TCP1", qty=5, basic_rate=100)
-
-	# 	stock_in_hand_account = get_inventory_account('_Test Company with perpetual inventory')
-	# 	prev_bal = get_balance_on(stock_in_hand_account)
-
-	# 	dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1")
-
-	# 	gl_entries = get_gl_entries("Delivery Note", dn.name)
-	# 	self.assertTrue(gl_entries)
-
-	# 	stock_value_difference = abs(frappe.db.get_value("Stock Ledger Entry",
-	# 		{"voucher_type": "Delivery Note", "voucher_no": dn.name}, "stock_value_difference"))
-
-	# 	expected_values = {
-	# 		stock_in_hand_account: [0.0, stock_value_difference],
-	# 		"Cost of Goods Sold - TCP1": [stock_value_difference, 0.0]
-	# 	}
-	# 	for i, gle in enumerate(gl_entries):
-	# 		self.assertEqual([gle.debit, gle.credit], expected_values.get(gle.account))
-
-	# 	# check stock in hand balance
-	# 	bal = get_balance_on(stock_in_hand_account)
-	# 	self.assertEqual(bal, prev_bal - stock_value_difference)
-
-	# 	# back dated incoming entry
-	# 	make_stock_entry(posting_date=add_days(nowdate(), -2), target="Stores - TCP1",
-	# 		qty=5, basic_rate=100)
-
-	# 	gl_entries = get_gl_entries("Delivery Note", dn.name)
-	# 	self.assertTrue(gl_entries)
-
-	# 	stock_value_difference = abs(frappe.db.get_value("Stock Ledger Entry",
-	# 		{"voucher_type": "Delivery Note", "voucher_no": dn.name}, "stock_value_difference"))
-
-	# 	expected_values = {
-	# 		stock_in_hand_account: [0.0, stock_value_difference],
-	# 		"Cost of Goods Sold - TCP1": [stock_value_difference, 0.0]
-	# 	}
-	# 	for i, gle in enumerate(gl_entries):
-	# 		self.assertEqual([gle.debit, gle.credit], expected_values.get(gle.account))
-
-	# 	dn.cancel()
-	# 	self.assertTrue(get_gl_entries("Delivery Note", dn.name))
-	# 	set_perpetual_inventory(0, company)
-
 	def test_delivery_note_gl_entry_packing_item(self):
-		company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
+		company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")
 
 		make_stock_entry(item_code="_Test Item", target="Stores - TCP1", qty=10, basic_rate=100)
-		make_stock_entry(item_code="_Test Item Home Desktop 100",
-			target="Stores - TCP1", qty=10, basic_rate=100)
+		make_stock_entry(
+			item_code="_Test Item Home Desktop 100", target="Stores - TCP1", qty=10, basic_rate=100
+		)
 
-		stock_in_hand_account = get_inventory_account('_Test Company with perpetual inventory')
+		stock_in_hand_account = get_inventory_account("_Test Company with perpetual inventory")
 		prev_bal = get_balance_on(stock_in_hand_account)
 
-		dn = create_delivery_note(item_code="_Test Product Bundle Item", company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1")
+		dn = create_delivery_note(
+			item_code="_Test Product Bundle Item",
+			company="_Test Company with perpetual inventory",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+		)
 
-		stock_value_diff_rm1 = abs(frappe.db.get_value("Stock Ledger Entry",
-			{"voucher_type": "Delivery Note", "voucher_no": dn.name, "item_code": "_Test Item"},
-			"stock_value_difference"))
+		stock_value_diff_rm1 = abs(
+			frappe.db.get_value(
+				"Stock Ledger Entry",
+				{"voucher_type": "Delivery Note", "voucher_no": dn.name, "item_code": "_Test Item"},
+				"stock_value_difference",
+			)
+		)
 
-		stock_value_diff_rm2 = abs(frappe.db.get_value("Stock Ledger Entry",
-			{"voucher_type": "Delivery Note", "voucher_no": dn.name,
-				"item_code": "_Test Item Home Desktop 100"}, "stock_value_difference"))
+		stock_value_diff_rm2 = abs(
+			frappe.db.get_value(
+				"Stock Ledger Entry",
+				{
+					"voucher_type": "Delivery Note",
+					"voucher_no": dn.name,
+					"item_code": "_Test Item Home Desktop 100",
+				},
+				"stock_value_difference",
+			)
+		)
 
 		stock_value_diff = stock_value_diff_rm1 + stock_value_diff_rm2
 
@@ -149,7 +124,7 @@
 
 		expected_values = {
 			stock_in_hand_account: [0.0, stock_value_diff],
-			"Cost of Goods Sold - TCP1": [stock_value_diff, 0.0]
+			"Cost of Goods Sold - TCP1": [stock_value_diff, 0.0],
 		}
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual([gle.debit, gle.credit], expected_values.get(gle.account))
@@ -166,10 +141,7 @@
 
 		dn = create_delivery_note(item_code="_Test Serialized Item With Series", serial_no=serial_no)
 
-		self.check_serial_no_values(serial_no, {
-			"warehouse": "",
-			"delivery_document_no": dn.name
-		})
+		self.check_serial_no_values(serial_no, {"warehouse": "", "delivery_document_no": dn.name})
 
 		si = make_sales_invoice(dn.name)
 		si.insert(ignore_permissions=True)
@@ -177,17 +149,18 @@
 
 		dn.cancel()
 
-		self.check_serial_no_values(serial_no, {
-			"warehouse": "_Test Warehouse - _TC",
-			"delivery_document_no": ""
-		})
+		self.check_serial_no_values(
+			serial_no, {"warehouse": "_Test Warehouse - _TC", "delivery_document_no": ""}
+		)
 
 	def test_serialized_partial_sales_invoice(self):
 		se = make_serialized_item()
 		serial_no = get_serial_nos(se.get("items")[0].serial_no)
-		serial_no = '\n'.join(serial_no)
+		serial_no = "\n".join(serial_no)
 
-		dn = create_delivery_note(item_code="_Test Serialized Item With Series", qty=2, serial_no=serial_no)
+		dn = create_delivery_note(
+			item_code="_Test Serialized Item With Series", qty=2, serial_no=serial_no
+		)
 
 		si = make_sales_invoice(dn.name)
 		si.items[0].qty = 1
@@ -200,15 +173,19 @@
 
 	def test_serialize_status(self):
 		from frappe.model.naming import make_autoname
-		serial_no = frappe.get_doc({
-			"doctype": "Serial No",
-			"item_code": "_Test Serialized Item With Series",
-			"serial_no": make_autoname("SR", "Serial No")
-		})
+
+		serial_no = frappe.get_doc(
+			{
+				"doctype": "Serial No",
+				"item_code": "_Test Serialized Item With Series",
+				"serial_no": make_autoname("SR", "Serial No"),
+			}
+		)
 		serial_no.save()
 
-		dn = create_delivery_note(item_code="_Test Serialized Item With Series",
-			serial_no=serial_no.name, do_not_submit=True)
+		dn = create_delivery_note(
+			item_code="_Test Serialized Item With Series", serial_no=serial_no.name, do_not_submit=True
+		)
 
 		self.assertRaises(SerialNoWarehouseError, dn.submit)
 
@@ -218,26 +195,46 @@
 			self.assertEqual(cstr(serial_no.get(field)), value)
 
 	def test_sales_return_for_non_bundled_items_partial(self):
-		company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
+		company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")
 
 		make_stock_entry(item_code="_Test Item", target="Stores - TCP1", qty=50, basic_rate=100)
 
 		actual_qty_0 = get_qty_after_transaction(warehouse="Stores - TCP1")
 
-		dn = create_delivery_note(qty=5, rate=500, warehouse="Stores - TCP1", company=company,
-			expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1")
+		dn = create_delivery_note(
+			qty=5,
+			rate=500,
+			warehouse="Stores - TCP1",
+			company=company,
+			expense_account="Cost of Goods Sold - TCP1",
+			cost_center="Main - TCP1",
+		)
 
 		actual_qty_1 = get_qty_after_transaction(warehouse="Stores - TCP1")
 		self.assertEqual(actual_qty_0 - 5, actual_qty_1)
 
 		# outgoing_rate
-		outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note",
-			"voucher_no": dn.name}, "stock_value_difference") / 5
+		outgoing_rate = (
+			frappe.db.get_value(
+				"Stock Ledger Entry",
+				{"voucher_type": "Delivery Note", "voucher_no": dn.name},
+				"stock_value_difference",
+			)
+			/ 5
+		)
 
 		# return entry
-		dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-2, rate=500,
-			company=company, warehouse="Stores - TCP1", expense_account="Cost of Goods Sold - TCP1",
-			cost_center="Main - TCP1", do_not_submit=1)
+		dn1 = create_delivery_note(
+			is_return=1,
+			return_against=dn.name,
+			qty=-2,
+			rate=500,
+			company=company,
+			warehouse="Stores - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			cost_center="Main - TCP1",
+			do_not_submit=1,
+		)
 		dn1.items[0].dn_detail = dn.items[0].name
 		dn1.submit()
 
@@ -245,15 +242,20 @@
 
 		self.assertEqual(actual_qty_1 + 2, actual_qty_2)
 
-		incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
+		incoming_rate, stock_value_difference = frappe.db.get_value(
+			"Stock Ledger Entry",
 			{"voucher_type": "Delivery Note", "voucher_no": dn1.name},
-			["incoming_rate", "stock_value_difference"])
+			["incoming_rate", "stock_value_difference"],
+		)
 
 		self.assertEqual(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3)))
 		stock_in_hand_account = get_inventory_account(company, dn1.items[0].warehouse)
 
-		gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Delivery Note",
-			"voucher_no": dn1.name, "account": stock_in_hand_account}, "debit")
+		gle_warehouse_amount = frappe.db.get_value(
+			"GL Entry",
+			{"voucher_type": "Delivery Note", "voucher_no": dn1.name, "account": stock_in_hand_account},
+			"debit",
+		)
 
 		self.assertEqual(gle_warehouse_amount, stock_value_difference)
 
@@ -267,6 +269,7 @@
 		self.assertEqual(dn.per_returned, 40)
 
 		from erpnext.controllers.sales_and_purchase_return import make_return_doc
+
 		return_dn_2 = make_return_doc("Delivery Note", dn.name)
 
 		# Check if unreturned amount is mapped in 2nd return
@@ -281,7 +284,7 @@
 		# DN should be completed on billing all unreturned amount
 		self.assertEqual(dn.items[0].billed_amt, 1500)
 		self.assertEqual(dn.per_billed, 100)
-		self.assertEqual(dn.status, 'Completed')
+		self.assertEqual(dn.status, "Completed")
 
 		si.load_from_db()
 		si.cancel()
@@ -293,19 +296,35 @@
 		dn.cancel()
 
 	def test_sales_return_for_non_bundled_items_full(self):
-		company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
+		company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")
 
-		make_item("Box", {'is_stock_item': 1})
+		make_item("Box", {"is_stock_item": 1})
 
 		make_stock_entry(item_code="Box", target="Stores - TCP1", qty=10, basic_rate=100)
 
-		dn = create_delivery_note(item_code="Box", qty=5, rate=500, warehouse="Stores - TCP1", company=company,
-			expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1")
+		dn = create_delivery_note(
+			item_code="Box",
+			qty=5,
+			rate=500,
+			warehouse="Stores - TCP1",
+			company=company,
+			expense_account="Cost of Goods Sold - TCP1",
+			cost_center="Main - TCP1",
+		)
 
-		#return entry
-		dn1 = create_delivery_note(item_code="Box", is_return=1, return_against=dn.name, qty=-5, rate=500,
-			company=company, warehouse="Stores - TCP1", expense_account="Cost of Goods Sold - TCP1",
-			cost_center="Main - TCP1", do_not_submit=1)
+		# return entry
+		dn1 = create_delivery_note(
+			item_code="Box",
+			is_return=1,
+			return_against=dn.name,
+			qty=-5,
+			rate=500,
+			company=company,
+			warehouse="Stores - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			cost_center="Main - TCP1",
+			do_not_submit=1,
+		)
 		dn1.items[0].dn_detail = dn.items[0].name
 		dn1.submit()
 
@@ -317,92 +336,157 @@
 		# Check if Original DN updated
 		self.assertEqual(dn.items[0].returned_qty, 5)
 		self.assertEqual(dn.per_returned, 100)
-		self.assertEqual(dn.status, 'Return Issued')
+		self.assertEqual(dn.status, "Return Issued")
 
 	def test_return_single_item_from_bundled_items(self):
-		company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
+		company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")
 
-		create_stock_reconciliation(item_code="_Test Item",
-			warehouse="Stores - TCP1", qty=50, rate=100,
-			company=company, expense_account = "Stock Adjustment - TCP1")
-		create_stock_reconciliation(item_code="_Test Item Home Desktop 100",
-			warehouse="Stores - TCP1", qty=50, rate=100,
-			company=company, expense_account = "Stock Adjustment - TCP1")
+		create_stock_reconciliation(
+			item_code="_Test Item",
+			warehouse="Stores - TCP1",
+			qty=50,
+			rate=100,
+			company=company,
+			expense_account="Stock Adjustment - TCP1",
+		)
+		create_stock_reconciliation(
+			item_code="_Test Item Home Desktop 100",
+			warehouse="Stores - TCP1",
+			qty=50,
+			rate=100,
+			company=company,
+			expense_account="Stock Adjustment - TCP1",
+		)
 
-		dn = create_delivery_note(item_code="_Test Product Bundle Item", qty=5, rate=500,
-			company=company, warehouse="Stores - TCP1",
-			expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1")
+		dn = create_delivery_note(
+			item_code="_Test Product Bundle Item",
+			qty=5,
+			rate=500,
+			company=company,
+			warehouse="Stores - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			cost_center="Main - TCP1",
+		)
 
 		# Qty after delivery
 		actual_qty_1 = get_qty_after_transaction(warehouse="Stores - TCP1")
-		self.assertEqual(actual_qty_1,  25)
+		self.assertEqual(actual_qty_1, 25)
 
 		# outgoing_rate
-		outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note",
-			"voucher_no": dn.name, "item_code": "_Test Item"}, "stock_value_difference") / 25
+		outgoing_rate = (
+			frappe.db.get_value(
+				"Stock Ledger Entry",
+				{"voucher_type": "Delivery Note", "voucher_no": dn.name, "item_code": "_Test Item"},
+				"stock_value_difference",
+			)
+			/ 25
+		)
 
 		# return 'test item' from packed items
-		dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-10, rate=500,
-			company=company, warehouse="Stores - TCP1",
-			expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1")
+		dn1 = create_delivery_note(
+			is_return=1,
+			return_against=dn.name,
+			qty=-10,
+			rate=500,
+			company=company,
+			warehouse="Stores - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			cost_center="Main - TCP1",
+		)
 
 		# qty after return
 		actual_qty_2 = get_qty_after_transaction(warehouse="Stores - TCP1")
 		self.assertEqual(actual_qty_2, 35)
 
 		# Check incoming rate for return entry
-		incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
+		incoming_rate, stock_value_difference = frappe.db.get_value(
+			"Stock Ledger Entry",
 			{"voucher_type": "Delivery Note", "voucher_no": dn1.name},
-			["incoming_rate", "stock_value_difference"])
+			["incoming_rate", "stock_value_difference"],
+		)
 
 		self.assertEqual(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3)))
 		stock_in_hand_account = get_inventory_account(company, dn1.items[0].warehouse)
 
 		# Check gl entry for warehouse
-		gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Delivery Note",
-			"voucher_no": dn1.name, "account": stock_in_hand_account}, "debit")
+		gle_warehouse_amount = frappe.db.get_value(
+			"GL Entry",
+			{"voucher_type": "Delivery Note", "voucher_no": dn1.name, "account": stock_in_hand_account},
+			"debit",
+		)
 
 		self.assertEqual(gle_warehouse_amount, stock_value_difference)
 
-
 	def test_return_entire_bundled_items(self):
-		company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
+		company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")
 
-		create_stock_reconciliation(item_code="_Test Item",
-			warehouse="Stores - TCP1", qty=50, rate=100,
-			company=company, expense_account = "Stock Adjustment - TCP1")
-		create_stock_reconciliation(item_code="_Test Item Home Desktop 100",
-			warehouse="Stores - TCP1", qty=50, rate=100,
-			company=company, expense_account = "Stock Adjustment - TCP1")
+		create_stock_reconciliation(
+			item_code="_Test Item",
+			warehouse="Stores - TCP1",
+			qty=50,
+			rate=100,
+			company=company,
+			expense_account="Stock Adjustment - TCP1",
+		)
+		create_stock_reconciliation(
+			item_code="_Test Item Home Desktop 100",
+			warehouse="Stores - TCP1",
+			qty=50,
+			rate=100,
+			company=company,
+			expense_account="Stock Adjustment - TCP1",
+		)
 
 		actual_qty = get_qty_after_transaction(warehouse="Stores - TCP1")
 		self.assertEqual(actual_qty, 50)
 
-		dn = create_delivery_note(item_code="_Test Product Bundle Item",
-			qty=5, rate=500, company=company, warehouse="Stores - TCP1", expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1")
+		dn = create_delivery_note(
+			item_code="_Test Product Bundle Item",
+			qty=5,
+			rate=500,
+			company=company,
+			warehouse="Stores - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			cost_center="Main - TCP1",
+		)
 
 		# qty after return
 		actual_qty = get_qty_after_transaction(warehouse="Stores - TCP1")
 		self.assertEqual(actual_qty, 25)
 
 		#  return bundled item
-		dn1 = create_return_delivery_note(source_name=dn.name, rate=500, qty=-2)
+		dn1 = create_delivery_note(
+			item_code="_Test Product Bundle Item",
+			is_return=1,
+			return_against=dn.name,
+			qty=-2,
+			rate=500,
+			company=company,
+			warehouse="Stores - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			cost_center="Main - TCP1",
+		)
 
 		# qty after return
 		actual_qty = get_qty_after_transaction(warehouse="Stores - TCP1")
 		self.assertEqual(actual_qty, 35)
 
 		# Check incoming rate for return entry
-		incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
+		incoming_rate, stock_value_difference = frappe.db.get_value(
+			"Stock Ledger Entry",
 			{"voucher_type": "Delivery Note", "voucher_no": dn1.name},
-			["incoming_rate", "stock_value_difference"])
+			["incoming_rate", "stock_value_difference"],
+		)
 
 		self.assertEqual(incoming_rate, 100)
-		stock_in_hand_account = get_inventory_account('_Test Company', dn1.items[0].warehouse)
+		stock_in_hand_account = get_inventory_account("_Test Company", dn1.items[0].warehouse)
 
 		# Check gl entry for warehouse
-		gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Delivery Note",
-			"voucher_no": dn1.name, "account": stock_in_hand_account}, "debit")
+		gle_warehouse_amount = frappe.db.get_value(
+			"GL Entry",
+			{"voucher_type": "Delivery Note", "voucher_no": dn1.name, "account": stock_in_hand_account},
+			"debit",
+		)
 
 		self.assertEqual(gle_warehouse_amount, 1400)
 
@@ -410,71 +494,87 @@
 		se = make_serialized_item()
 		serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]
 
-		dn = create_delivery_note(item_code="_Test Serialized Item With Series", rate=500, serial_no=serial_no)
+		dn = create_delivery_note(
+			item_code="_Test Serialized Item With Series", rate=500, serial_no=serial_no
+		)
 
-		self.check_serial_no_values(serial_no, {
-			"warehouse": "",
-			"delivery_document_no": dn.name
-		})
+		self.check_serial_no_values(serial_no, {"warehouse": "", "delivery_document_no": dn.name})
 
 		# return entry
-		dn1 = create_delivery_note(item_code="_Test Serialized Item With Series",
-			is_return=1, return_against=dn.name, qty=-1, rate=500, serial_no=serial_no)
+		dn1 = create_delivery_note(
+			item_code="_Test Serialized Item With Series",
+			is_return=1,
+			return_against=dn.name,
+			qty=-1,
+			rate=500,
+			serial_no=serial_no,
+		)
 
-		self.check_serial_no_values(serial_no, {
-			"warehouse": "_Test Warehouse - _TC",
-			"delivery_document_no": ""
-		})
+		self.check_serial_no_values(
+			serial_no, {"warehouse": "_Test Warehouse - _TC", "delivery_document_no": ""}
+		)
 
 		dn1.cancel()
 
-		self.check_serial_no_values(serial_no, {
-			"warehouse": "",
-			"delivery_document_no": dn.name
-		})
+		self.check_serial_no_values(serial_no, {"warehouse": "", "delivery_document_no": dn.name})
 
 		dn.cancel()
 
-		self.check_serial_no_values(serial_no, {
-			"warehouse": "_Test Warehouse - _TC",
-			"delivery_document_no": "",
-			"purchase_document_no": se.name
-		})
+		self.check_serial_no_values(
+			serial_no,
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"delivery_document_no": "",
+				"purchase_document_no": se.name,
+			},
+		)
 
 	def test_delivery_of_bundled_items_to_target_warehouse(self):
 		from erpnext.selling.doctype.customer.test_customer import create_internal_customer
 
-		company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
+		company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")
 		customer_name = create_internal_customer(
 			customer_name="_Test Internal Customer 2",
 			represents_company="_Test Company with perpetual inventory",
-			allowed_to_interact_with="_Test Company with perpetual inventory"
+			allowed_to_interact_with="_Test Company with perpetual inventory",
 		)
 
 		set_valuation_method("_Test Item", "FIFO")
 		set_valuation_method("_Test Item Home Desktop 100", "FIFO")
 
-		target_warehouse = get_warehouse(company=company, abbr="TCP1",
-			warehouse_name="_Test Customer Warehouse").name
+		target_warehouse = get_warehouse(
+			company=company, abbr="TCP1", warehouse_name="_Test Customer Warehouse"
+		).name
 
 		for warehouse in ("Stores - TCP1", target_warehouse):
-			create_stock_reconciliation(item_code="_Test Item", warehouse=warehouse, company = company,
-				expense_account = "Stock Adjustment - TCP1", qty=500, rate=100)
-			create_stock_reconciliation(item_code="_Test Item Home Desktop 100", company = company,
-				expense_account = "Stock Adjustment - TCP1", warehouse=warehouse, qty=500, rate=100)
+			create_stock_reconciliation(
+				item_code="_Test Item",
+				warehouse=warehouse,
+				company=company,
+				expense_account="Stock Adjustment - TCP1",
+				qty=500,
+				rate=100,
+			)
+			create_stock_reconciliation(
+				item_code="_Test Item Home Desktop 100",
+				company=company,
+				expense_account="Stock Adjustment - TCP1",
+				warehouse=warehouse,
+				qty=500,
+				rate=100,
+			)
 
 		dn = create_delivery_note(
 			item_code="_Test Product Bundle Item",
 			company="_Test Company with perpetual inventory",
 			customer=customer_name,
-			cost_center = 'Main - TCP1',
-			expense_account = "Cost of Goods Sold - TCP1",
-			do_not_submit=True,
-			qty=5, rate=500,
+			cost_center="Main - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			qty=5,
+			rate=500,
 			warehouse="Stores - TCP1",
-			target_warehouse=target_warehouse)
-
-		dn.submit()
+			target_warehouse=target_warehouse,
+		)
 
 		# qty after delivery
 		actual_qty_at_source = get_qty_after_transaction(warehouse="Stores - TCP1")
@@ -484,16 +584,28 @@
 		self.assertEqual(actual_qty_at_target, 525)
 
 		# stock value diff for source warehouse for "_Test Item"
-		stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
-			{"voucher_type": "Delivery Note", "voucher_no": dn.name,
-				"item_code": "_Test Item", "warehouse": "Stores - TCP1"},
-			"stock_value_difference")
+		stock_value_difference = frappe.db.get_value(
+			"Stock Ledger Entry",
+			{
+				"voucher_type": "Delivery Note",
+				"voucher_no": dn.name,
+				"item_code": "_Test Item",
+				"warehouse": "Stores - TCP1",
+			},
+			"stock_value_difference",
+		)
 
 		# stock value diff for target warehouse
-		stock_value_difference1 = frappe.db.get_value("Stock Ledger Entry",
-			{"voucher_type": "Delivery Note", "voucher_no": dn.name,
-				"item_code": "_Test Item", "warehouse": target_warehouse},
-			"stock_value_difference")
+		stock_value_difference1 = frappe.db.get_value(
+			"Stock Ledger Entry",
+			{
+				"voucher_type": "Delivery Note",
+				"voucher_no": dn.name,
+				"item_code": "_Test Item",
+				"warehouse": target_warehouse,
+			},
+			"stock_value_difference",
+		)
 
 		self.assertEqual(abs(stock_value_difference), stock_value_difference1)
 
@@ -501,13 +613,18 @@
 		gl_entries = get_gl_entries("Delivery Note", dn.name)
 		self.assertTrue(gl_entries)
 
-		stock_value_difference = abs(frappe.db.sql("""select sum(stock_value_difference)
+		stock_value_difference = abs(
+			frappe.db.sql(
+				"""select sum(stock_value_difference)
 			from `tabStock Ledger Entry` where voucher_type='Delivery Note' and voucher_no=%s
-			and warehouse='Stores - TCP1'""", dn.name)[0][0])
+			and warehouse='Stores - TCP1'""",
+				dn.name,
+			)[0][0]
+		)
 
 		expected_values = {
 			"Stock In Hand - TCP1": [0.0, stock_value_difference],
-			target_warehouse: [stock_value_difference, 0.0]
+			target_warehouse: [stock_value_difference, 0.0],
 		}
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual([gle.debit, gle.credit], expected_values.get(gle.account))
@@ -520,8 +637,13 @@
 
 		make_stock_entry(target="Stores - TCP1", qty=5, basic_rate=100)
 
-		dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1',
-			cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1", do_not_submit=True)
+		dn = create_delivery_note(
+			company="_Test Company with perpetual inventory",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			do_not_submit=True,
+		)
 
 		dn.submit()
 
@@ -598,6 +720,7 @@
 		from erpnext.selling.doctype.sales_order.sales_order import (
 			make_sales_invoice as make_sales_invoice_from_so,
 		)
+
 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
 
 		so = make_sales_order()
@@ -671,28 +794,31 @@
 
 	def test_delivery_note_with_cost_center(self):
 		from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
-		cost_center = "_Test Cost Center for BS Account - TCP1"
-		create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company with perpetual inventory")
 
-		company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
+		cost_center = "_Test Cost Center for BS Account - TCP1"
+		create_cost_center(
+			cost_center_name="_Test Cost Center for BS Account",
+			company="_Test Company with perpetual inventory",
+		)
 
 		set_valuation_method("_Test Item", "FIFO")
 
 		make_stock_entry(target="Stores - TCP1", qty=5, basic_rate=100)
 
-		stock_in_hand_account = get_inventory_account('_Test Company with perpetual inventory')
-		dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1',  expense_account = "Cost of Goods Sold - TCP1", cost_center=cost_center)
+		stock_in_hand_account = get_inventory_account("_Test Company with perpetual inventory")
+		dn = create_delivery_note(
+			company="_Test Company with perpetual inventory",
+			warehouse="Stores - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			cost_center=cost_center,
+		)
 
 		gl_entries = get_gl_entries("Delivery Note", dn.name)
 		self.assertTrue(gl_entries)
 
 		expected_values = {
-			"Cost of Goods Sold - TCP1": {
-				"cost_center": cost_center
-			},
-			stock_in_hand_account: {
-				"cost_center": cost_center
-			}
+			"Cost of Goods Sold - TCP1": {"cost_center": cost_center},
+			stock_in_hand_account: {"cost_center": cost_center},
 		}
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
@@ -700,29 +826,28 @@
 	def test_delivery_note_cost_center_with_balance_sheet_account(self):
 		cost_center = "Main - TCP1"
 
-		company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
-
 		set_valuation_method("_Test Item", "FIFO")
 
 		make_stock_entry(target="Stores - TCP1", qty=5, basic_rate=100)
 
-		stock_in_hand_account = get_inventory_account('_Test Company with perpetual inventory')
-		dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1",
-			do_not_submit=1)
+		stock_in_hand_account = get_inventory_account("_Test Company with perpetual inventory")
+		dn = create_delivery_note(
+			company="_Test Company with perpetual inventory",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			do_not_submit=1,
+		)
 
-		dn.get('items')[0].cost_center = None
+		dn.get("items")[0].cost_center = None
 		dn.submit()
 
 		gl_entries = get_gl_entries("Delivery Note", dn.name)
 
 		self.assertTrue(gl_entries)
 		expected_values = {
-			"Cost of Goods Sold - TCP1": {
-				"cost_center": cost_center
-			},
-			stock_in_hand_account: {
-				"cost_center": cost_center
-			}
+			"Cost of Goods Sold - TCP1": {"cost_center": cost_center},
+			stock_in_hand_account: {"cost_center": cost_center},
 		}
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
@@ -750,15 +875,18 @@
 		from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
 
 		dn = create_delivery_note(qty=8, do_not_submit=True)
-		dn.append("items", {
-			"item_code": "_Test Item",
-			"warehouse": "_Test Warehouse - _TC",
-			"qty": 1,
-			"rate": 100,
-			"conversion_factor": 1.0,
-			"expense_account": "Cost of Goods Sold - _TC",
-			"cost_center": "_Test Cost Center - _TC"
-		})
+		dn.append(
+			"items",
+			{
+				"item_code": "_Test Item",
+				"warehouse": "_Test Warehouse - _TC",
+				"qty": 1,
+				"rate": 100,
+				"conversion_factor": 1.0,
+				"expense_account": "Cost of Goods Sold - _TC",
+				"cost_center": "_Test Cost Center - _TC",
+			},
+		)
 		dn.submit()
 
 		si1 = make_sales_invoice(dn.name)
@@ -775,14 +903,21 @@
 		self.assertEqual(si2.items[0].qty, 2)
 		self.assertEqual(si2.items[1].qty, 1)
 
-
 	def test_delivery_note_bundle_with_batched_item(self):
 		batched_bundle = make_item("_Test Batched bundle", {"is_stock_item": 0})
-		batched_item = make_item("_Test Batched Item",
-				{"is_stock_item": 1, "has_batch_no": 1, "create_new_batch": 1, "batch_number_series": "TESTBATCH.#####"}
-				)
+		batched_item = make_item(
+			"_Test Batched Item",
+			{
+				"is_stock_item": 1,
+				"has_batch_no": 1,
+				"create_new_batch": 1,
+				"batch_number_series": "TESTBATCH.#####",
+			},
+		)
 		make_product_bundle(parent=batched_bundle.name, items=[batched_item.name])
-		make_stock_entry(item_code=batched_item.name, target="_Test Warehouse - _TC", qty=10, basic_rate=42)
+		make_stock_entry(
+			item_code=batched_item.name, target="_Test Warehouse - _TC", qty=10, basic_rate=42
+		)
 
 		try:
 			dn = create_delivery_note(item_code=batched_bundle.name, qty=1)
@@ -791,7 +926,9 @@
 				self.fail("Batch numbers not getting added to bundled items in DN.")
 			raise e
 
-		self.assertTrue("TESTBATCH" in dn.packed_items[0].batch_no, "Batch number not added in packed item")
+		self.assertTrue(
+			"TESTBATCH" in dn.packed_items[0].batch_no, "Batch number not added in packed item"
+		)
 
 	def test_payment_terms_are_fetched_when_creating_sales_invoice(self):
 		from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
@@ -803,13 +940,13 @@
 
 		so = make_sales_order(uom="Nos", do_not_save=1)
 		create_payment_terms_template()
-		so.payment_terms_template = 'Test Receivable Template'
+		so.payment_terms_template = "Test Receivable Template"
 		so.submit()
 
 		dn = create_dn_against_so(so.name, delivered_qty=10)
 
 		si = create_sales_invoice(qty=10, do_not_save=1)
-		si.items[0].delivery_note= dn.name
+		si.items[0].delivery_note = dn.name
 		si.items[0].dn_detail = dn.items[0].name
 		si.items[0].sales_order = so.name
 		si.items[0].so_detail = so.items[0].name
@@ -822,14 +959,111 @@
 
 		automatically_fetch_payment_terms(enable=0)
 
-def create_return_delivery_note(**args):
-	args = frappe._dict(args)
-	from erpnext.controllers.sales_and_purchase_return import make_return_doc
-	doc = make_return_doc("Delivery Note", args.source_name, None)
-	doc.items[0].rate = args.rate
-	doc.items[0].qty = args.qty
-	doc.submit()
-	return doc
+	def test_returned_qty_in_return_dn(self):
+		# SO ---> SI ---> DN
+		#                 |
+		#                 |---> DN(Partial Sales Return) ---> SI(Credit Note)
+		#                 |
+		#                 |---> DN(Partial Sales Return) ---> SI(Credit Note)
+
+		from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_delivery_note
+		from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
+
+		so = make_sales_order(qty=10)
+		si = make_sales_invoice(so.name)
+		si.insert()
+		si.submit()
+		dn = make_delivery_note(si.name)
+		dn.insert()
+		dn.submit()
+		self.assertEqual(dn.items[0].returned_qty, 0)
+		self.assertEqual(dn.per_billed, 100)
+
+		from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
+
+		dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-3)
+		si1 = make_sales_invoice(dn1.name)
+		si1.insert()
+		si1.submit()
+		dn1.reload()
+		self.assertEqual(dn1.items[0].returned_qty, 0)
+		self.assertEqual(dn1.per_billed, 100)
+
+		dn2 = create_delivery_note(is_return=1, return_against=dn.name, qty=-4)
+		si2 = make_sales_invoice(dn2.name)
+		si2.insert()
+		si2.submit()
+		dn2.reload()
+		self.assertEqual(dn2.items[0].returned_qty, 0)
+		self.assertEqual(dn2.per_billed, 100)
+
+	def test_internal_transfer_with_valuation_only(self):
+		from erpnext.selling.doctype.customer.test_customer import create_internal_customer
+
+		item = make_item().name
+		warehouse = "_Test Warehouse - _TC"
+		target = "Stores - _TC"
+		company = "_Test Company"
+		customer = create_internal_customer(represents_company=company)
+		rate = 42
+
+		# Create item price and pricing rule
+		frappe.get_doc(
+			{
+				"item_code": item,
+				"price_list": "Standard Selling",
+				"price_list_rate": 1000,
+				"doctype": "Item Price",
+			}
+		).insert()
+
+		frappe.get_doc(
+			{
+				"doctype": "Pricing Rule",
+				"title": frappe.generate_hash(),
+				"apply_on": "Item Code",
+				"price_or_product_discount": "Price",
+				"selling": 1,
+				"company": company,
+				"margin_type": "Percentage",
+				"margin_rate_or_amount": 10,
+				"apply_discount_on": "Grand Total",
+				"items": [
+					{
+						"item_code": item,
+					}
+				],
+			}
+		).insert()
+
+		make_stock_entry(target=warehouse, qty=5, basic_rate=rate, item_code=item)
+		dn = create_delivery_note(
+			item_code=item,
+			company=company,
+			customer=customer,
+			qty=5,
+			rate=500,
+			warehouse=warehouse,
+			target_warehouse=target,
+			ignore_pricing_rule=0,
+			do_not_save=True,
+			do_not_submit=True,
+		)
+
+		self.assertEqual(dn.items[0].rate, 500)  # haven't saved yet
+		dn.save()
+		self.assertEqual(dn.ignore_pricing_rule, 1)
+
+		# rate should reset to incoming rate
+		self.assertEqual(dn.items[0].rate, rate)
+
+		# rate should reset again if discounts are fiddled with
+		dn.items[0].margin_type = "Amount"
+		dn.items[0].margin_rate_or_amount = 50
+		dn.save()
+
+		self.assertEqual(dn.items[0].rate, rate)
+
 
 def create_delivery_note(**args):
 	dn = frappe.new_doc("Delivery Note")
@@ -844,18 +1078,21 @@
 	dn.is_return = args.is_return
 	dn.return_against = args.return_against
 
-	dn.append("items", {
-		"item_code": args.item or args.item_code or "_Test Item",
-		"warehouse": args.warehouse or "_Test Warehouse - _TC",
-		"qty": args.qty or 1,
-		"rate": args.rate if args.get("rate") is not None else 100,
-		"conversion_factor": 1.0,
-		"allow_zero_valuation_rate": args.allow_zero_valuation_rate or 1,
-		"expense_account": args.expense_account or "Cost of Goods Sold - _TC",
-		"cost_center": args.cost_center or "_Test Cost Center - _TC",
-		"serial_no": args.serial_no,
-		"target_warehouse": args.target_warehouse
-	})
+	dn.append(
+		"items",
+		{
+			"item_code": args.item or args.item_code or "_Test Item",
+			"warehouse": args.warehouse or "_Test Warehouse - _TC",
+			"qty": args.qty or 1,
+			"rate": args.rate if args.get("rate") is not None else 100,
+			"conversion_factor": 1.0,
+			"allow_zero_valuation_rate": args.allow_zero_valuation_rate or 1,
+			"expense_account": args.expense_account or "Cost of Goods Sold - _TC",
+			"cost_center": args.cost_center or "_Test Cost Center - _TC",
+			"serial_no": args.serial_no,
+			"target_warehouse": args.target_warehouse,
+		},
+	)
 
 	if not args.do_not_save:
 		dn.insert()
@@ -863,4 +1100,5 @@
 			dn.submit()
 	return dn
 
+
 test_dependencies = ["Product Bundle"]
diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
index f1f5d96..2de4842 100644
--- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
+++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
@@ -74,6 +74,7 @@
   "against_sales_invoice",
   "si_detail",
   "dn_detail",
+  "pick_list_item",
   "section_break_40",
   "batch_no",
   "serial_no",
@@ -183,6 +184,7 @@
    "width": "100px"
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "stock_uom",
    "fieldtype": "Link",
    "label": "Stock UOM",
@@ -208,6 +210,7 @@
    "reqd": 1
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "conversion_factor",
    "fieldtype": "Float",
    "label": "UOM Conversion Factor",
@@ -216,6 +219,7 @@
    "reqd": 1
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "stock_qty",
    "fieldtype": "Float",
    "label": "Qty in Stock UOM",
@@ -736,7 +740,9 @@
    "depends_on": "returned_qty",
    "fieldname": "returned_qty",
    "fieldtype": "Float",
-   "label": "Returned Qty in Stock UOM"
+   "label": "Returned Qty in Stock UOM",
+   "no_copy": 1,
+   "read_only": 1
   },
   {
    "fieldname": "incoming_rate",
@@ -762,13 +768,22 @@
    "fieldtype": "Check",
    "label": "Grant Commission",
    "read_only": 1
+  },
+  {
+   "fieldname": "pick_list_item",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Pick List Item",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
   }
  ],
  "idx": 1,
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2022-02-24 14:42:20.211085",
+ "modified": "2022-06-17 05:25:47.711177",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Note Item",
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.py b/erpnext/stock/doctype/delivery_trip/delivery_trip.py
index c749b2e..ff95c50 100644
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.py
@@ -17,9 +17,12 @@
 		super(DeliveryTrip, self).__init__(*args, **kwargs)
 
 		# Google Maps returns distances in meters by default
-		self.default_distance_uom = frappe.db.get_single_value("Global Defaults", "default_distance_unit") or "Meter"
-		self.uom_conversion_factor = frappe.db.get_value("UOM Conversion Factor",
-			{"from_uom": "Meter", "to_uom": self.default_distance_uom}, "value")
+		self.default_distance_uom = (
+			frappe.db.get_single_value("Global Defaults", "default_distance_unit") or "Meter"
+		)
+		self.uom_conversion_factor = frappe.db.get_value(
+			"UOM Conversion Factor", {"from_uom": "Meter", "to_uom": self.default_distance_uom}, "value"
+		)
 
 	def validate(self):
 		self.validate_stop_addresses()
@@ -41,11 +44,7 @@
 				stop.customer_address = get_address_display(frappe.get_doc("Address", stop.address).as_dict())
 
 	def update_status(self):
-		status = {
-			0: "Draft",
-			1: "Scheduled",
-			2: "Cancelled"
-		}[self.docstatus]
+		status = {0: "Draft", 1: "Scheduled", 2: "Cancelled"}[self.docstatus]
 
 		if self.docstatus == 1:
 			visited_stops = [stop.visited for stop in self.delivery_stops]
@@ -63,17 +62,19 @@
 		are removed.
 
 		Args:
-			delete (bool, optional): Defaults to `False`. `True` if driver details need to be emptied, else `False`.
+		        delete (bool, optional): Defaults to `False`. `True` if driver details need to be emptied, else `False`.
 		"""
 
-		delivery_notes = list(set(stop.delivery_note for stop in self.delivery_stops if stop.delivery_note))
+		delivery_notes = list(
+			set(stop.delivery_note for stop in self.delivery_stops if stop.delivery_note)
+		)
 
 		update_fields = {
 			"driver": self.driver,
 			"driver_name": self.driver_name,
 			"vehicle_no": self.vehicle,
 			"lr_no": self.name,
-			"lr_date": self.departure_time
+			"lr_date": self.departure_time,
 		}
 
 		for delivery_note in delivery_notes:
@@ -97,7 +98,7 @@
 		on the optimized order, before estimating the arrival times.
 
 		Args:
-			optimize (bool): True if route needs to be optimized, else False
+		        optimize (bool): True if route needs to be optimized, else False
 		"""
 
 		departure_datetime = get_datetime(self.departure_time)
@@ -134,8 +135,9 @@
 
 				# Include last leg in the final distance calculation
 				self.uom = self.default_distance_uom
-				total_distance = sum(leg.get("distance", {}).get("value", 0.0)
-					for leg in directions.get("legs"))  # in meters
+				total_distance = sum(
+					leg.get("distance", {}).get("value", 0.0) for leg in directions.get("legs")
+				)  # in meters
 				self.total_distance = total_distance * self.uom_conversion_factor
 			else:
 				idx += len(route) - 1
@@ -149,10 +151,10 @@
 		split into sublists at the specified lock position(s).
 
 		Args:
-			optimize (bool): `True` if route needs to be optimized, else `False`
+		        optimize (bool): `True` if route needs to be optimized, else `False`
 
 		Returns:
-			(list of list of str): List of address routes split at locks, if optimize is `True`
+		        (list of list of str): List of address routes split at locks, if optimize is `True`
 		"""
 		if not self.driver_address:
 			frappe.throw(_("Cannot Calculate Arrival Time as Driver Address is Missing."))
@@ -186,8 +188,8 @@
 		for vehicle routing problems.
 
 		Args:
-			optimized_order (list of int): The index-based optimized order of the route
-			start (int): The index at which to start the rearrangement
+		        optimized_order (list of int): The index-based optimized order of the route
+		        start (int): The index at which to start the rearrangement
 		"""
 
 		stops_order = []
@@ -200,7 +202,7 @@
 			self.delivery_stops[old_idx].idx = new_idx
 			stops_order.append(self.delivery_stops[old_idx])
 
-		self.delivery_stops[start:start + len(stops_order)] = stops_order
+		self.delivery_stops[start : start + len(stops_order)] = stops_order
 
 	def get_directions(self, route, optimize):
 		"""
@@ -212,11 +214,11 @@
 		but it only works for routes without any waypoints.
 
 		Args:
-			route (list of str): Route addresses (origin -> waypoint(s), if any -> destination)
-			optimize (bool): `True` if route needs to be optimized, else `False`
+		        route (list of str): Route addresses (origin -> waypoint(s), if any -> destination)
+		        optimize (bool): `True` if route needs to be optimized, else `False`
 
 		Returns:
-			(dict): Route legs and, if `optimize` is `True`, optimized waypoint order
+		        (dict): Route legs and, if `optimize` is `True`, optimized waypoint order
 		"""
 		if not frappe.db.get_single_value("Google Settings", "api_key"):
 			frappe.throw(_("Enter API key in Google Settings."))
@@ -231,8 +233,8 @@
 		directions_data = {
 			"origin": route[0],
 			"destination": route[-1],
-			"waypoints": route[1: -1],
-			"optimize_waypoints": optimize
+			"waypoints": route[1:-1],
+			"optimize_waypoints": optimize,
 		}
 
 		try:
@@ -243,7 +245,6 @@
 		return directions[0] if directions else False
 
 
-
 @frappe.whitelist()
 def get_contact_and_address(name):
 	out = frappe._dict()
@@ -262,10 +263,13 @@
 			FROM
 				`tabDynamic Link` dl
 			WHERE
-				dl.link_doctype="Customer"
+				dl.link_doctype='Customer'
 				AND dl.link_name=%s
-				AND dl.parenttype = "Contact"
-		""", (name), as_dict=1)
+				AND dl.parenttype = 'Contact'
+		""",
+		(name),
+		as_dict=1,
+	)
 
 	if contact_persons:
 		for out.contact_person in contact_persons:
@@ -285,10 +289,13 @@
 			FROM
 				`tabDynamic Link` dl
 			WHERE
-				dl.link_doctype="Customer"
+				dl.link_doctype='Customer'
 				AND dl.link_name=%s
-				AND dl.parenttype = "Address"
-		""", (name), as_dict=1)
+				AND dl.parenttype = 'Address'
+		""",
+		(name),
+		as_dict=1,
+	)
 
 	if shipping_addresses:
 		for out.shipping_address in shipping_addresses:
@@ -303,16 +310,18 @@
 @frappe.whitelist()
 def get_contact_display(contact):
 	contact_info = frappe.db.get_value(
-		"Contact", contact,
-		["first_name", "last_name", "phone", "mobile_no"],
-		as_dict=1)
+		"Contact", contact, ["first_name", "last_name", "phone", "mobile_no"], as_dict=1
+	)
 
-	contact_info.html = """ <b>%(first_name)s %(last_name)s</b> <br> %(phone)s <br> %(mobile_no)s""" % {
-		"first_name": contact_info.first_name,
-		"last_name": contact_info.last_name or "",
-		"phone": contact_info.phone or "",
-		"mobile_no": contact_info.mobile_no or ""
-	}
+	contact_info.html = (
+		""" <b>%(first_name)s %(last_name)s</b> <br> %(phone)s <br> %(mobile_no)s"""
+		% {
+			"first_name": contact_info.first_name,
+			"last_name": contact_info.last_name or "",
+			"phone": contact_info.phone or "",
+			"mobile_no": contact_info.mobile_no or "",
+		}
+	)
 
 	return contact_info.html
 
@@ -322,19 +331,19 @@
 	Remove HTML breaks in a given address
 
 	Args:
-		address (str): Address to be sanitized
+	        address (str): Address to be sanitized
 
 	Returns:
-		(str): Sanitized address
+	        (str): Sanitized address
 	"""
 
 	if not address:
 		return
 
-	address = address.split('<br>')
+	address = address.split("<br>")
 
 	# Only get the first 3 blocks of the address
-	return ', '.join(address[:3])
+	return ", ".join(address[:3])
 
 
 @frappe.whitelist()
@@ -349,11 +358,15 @@
 	email_recipients = []
 
 	for stop in delivery_trip.delivery_stops:
-		contact_info = frappe.db.get_value("Contact", stop.contact, ["first_name", "last_name", "email_id"], as_dict=1)
+		contact_info = frappe.db.get_value(
+			"Contact", stop.contact, ["first_name", "last_name", "email_id"], as_dict=1
+		)
 
 		context.update({"items": []})
 		if stop.delivery_note:
-			items = frappe.get_all("Delivery Note Item", filters={"parent": stop.delivery_note, "docstatus": 1}, fields=["*"])
+			items = frappe.get_all(
+				"Delivery Note Item", filters={"parent": stop.delivery_note, "docstatus": 1}, fields=["*"]
+			)
 			context.update({"items": items})
 
 		if contact_info and contact_info.email_id:
@@ -363,45 +376,55 @@
 			dispatch_template_name = frappe.db.get_single_value("Delivery Settings", "dispatch_template")
 			dispatch_template = frappe.get_doc("Email Template", dispatch_template_name)
 
-			frappe.sendmail(recipients=contact_info.email_id,
+			frappe.sendmail(
+				recipients=contact_info.email_id,
 				subject=dispatch_template.subject,
 				message=frappe.render_template(dispatch_template.response, context),
-				attachments=get_attachments(stop))
+				attachments=get_attachments(stop),
+			)
 
 			stop.db_set("email_sent_to", contact_info.email_id)
 			email_recipients.append(contact_info.email_id)
 
 	if email_recipients:
 		frappe.msgprint(_("Email sent to {0}").format(", ".join(email_recipients)))
-		delivery_trip.db_set("email_notification_sent", True)
+		delivery_trip.db_set("email_notification_sent", 1)
 	else:
 		frappe.msgprint(_("No contacts with email IDs found."))
 
 
 def get_attachments(delivery_stop):
-	if not (frappe.db.get_single_value("Delivery Settings", "send_with_attachment") and delivery_stop.delivery_note):
+	if not (
+		frappe.db.get_single_value("Delivery Settings", "send_with_attachment")
+		and delivery_stop.delivery_note
+	):
 		return []
 
 	dispatch_attachment = frappe.db.get_single_value("Delivery Settings", "dispatch_attachment")
-	attachments = frappe.attach_print("Delivery Note", delivery_stop.delivery_note,
-		file_name="Delivery Note", print_format=dispatch_attachment)
+	attachments = frappe.attach_print(
+		"Delivery Note",
+		delivery_stop.delivery_note,
+		file_name="Delivery Note",
+		print_format=dispatch_attachment,
+	)
 
 	return [attachments]
 
+
 @frappe.whitelist()
 def get_driver_email(driver):
 	employee = frappe.db.get_value("Driver", driver, "employee")
 	email = frappe.db.get_value("Employee", employee, "prefered_email")
 	return {"email": email}
 
+
 @frappe.whitelist()
 def make_expense_claim(source_name, target_doc=None):
-	doc = get_mapped_doc("Delivery Trip", source_name,
-		{"Delivery Trip": {
-			"doctype": "Expense Claim",
-			"field_map": {
-				"name" : "delivery_trip"
-			}
-		}}, target_doc)
+	doc = get_mapped_doc(
+		"Delivery Trip",
+		source_name,
+		{"Delivery Trip": {"doctype": "Expense Claim", "field_map": {"name": "delivery_trip"}}},
+		target_doc,
+	)
 
 	return doc
diff --git a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
index dcdff4a..555361a 100644
--- a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
@@ -106,23 +106,21 @@
 		self.delivery_trip.save()
 		self.assertEqual(self.delivery_trip.status, "Completed")
 
+
 def create_address(driver):
 	if not frappe.db.exists("Address", {"address_title": "_Test Address for Driver"}):
-		address = frappe.get_doc({
-			"doctype": "Address",
-			"address_title": "_Test Address for Driver",
-			"address_type": "Office",
-			"address_line1": "Station Road",
-			"city": "_Test City",
-			"state": "Test State",
-			"country": "India",
-			"links":[
-				{
-					"link_doctype": "Driver",
-					"link_name": driver.name
-				}
-			]
-		}).insert(ignore_permissions=True)
+		address = frappe.get_doc(
+			{
+				"doctype": "Address",
+				"address_title": "_Test Address for Driver",
+				"address_type": "Office",
+				"address_line1": "Station Road",
+				"city": "_Test City",
+				"state": "Test State",
+				"country": "India",
+				"links": [{"link_doctype": "Driver", "link_name": driver.name}],
+			}
+		).insert(ignore_permissions=True)
 
 		frappe.db.set_value("Driver", driver.name, "address", address.name)
 
@@ -130,49 +128,57 @@
 
 	return frappe.get_doc("Address", {"address_title": "_Test Address for Driver"})
 
+
 def create_driver():
 	if not frappe.db.exists("Driver", {"full_name": "Newton Scmander"}):
-		driver = frappe.get_doc({
-			"doctype": "Driver",
-			"full_name": "Newton Scmander",
-			"cell_number": "98343424242",
-			"license_number": "B809",
-		}).insert(ignore_permissions=True)
+		driver = frappe.get_doc(
+			{
+				"doctype": "Driver",
+				"full_name": "Newton Scmander",
+				"cell_number": "98343424242",
+				"license_number": "B809",
+			}
+		).insert(ignore_permissions=True)
 
 		return driver
 
 	return frappe.get_doc("Driver", {"full_name": "Newton Scmander"})
 
+
 def create_delivery_notification():
 	if not frappe.db.exists("Email Template", "Delivery Notification"):
-		dispatch_template = frappe.get_doc({
-			'doctype': 'Email Template',
-			'name': 'Delivery Notification',
-			'response': 'Test Delivery Trip',
-			'subject': 'Test Subject',
-			'owner': frappe.session.user
-		})
+		dispatch_template = frappe.get_doc(
+			{
+				"doctype": "Email Template",
+				"name": "Delivery Notification",
+				"response": "Test Delivery Trip",
+				"subject": "Test Subject",
+				"owner": frappe.session.user,
+			}
+		)
 		dispatch_template.insert()
 
 	delivery_settings = frappe.get_single("Delivery Settings")
-	delivery_settings.dispatch_template = 'Delivery Notification'
+	delivery_settings.dispatch_template = "Delivery Notification"
 	delivery_settings.save()
 
 
 def create_vehicle():
 	if not frappe.db.exists("Vehicle", "JB 007"):
-		vehicle = frappe.get_doc({
-			"doctype": "Vehicle",
-			"license_plate": "JB 007",
-			"make": "Maruti",
-			"model": "PCM",
-			"last_odometer": 5000,
-			"acquisition_date": nowdate(),
-			"location": "Mumbai",
-			"chassis_no": "1234ABCD",
-			"uom": "Litre",
-			"vehicle_value": flt(500000)
-		})
+		vehicle = frappe.get_doc(
+			{
+				"doctype": "Vehicle",
+				"license_plate": "JB 007",
+				"make": "Maruti",
+				"model": "PCM",
+				"last_odometer": 5000,
+				"acquisition_date": nowdate(),
+				"location": "Mumbai",
+				"chassis_no": "1234ABCD",
+				"uom": "Litre",
+				"vehicle_value": flt(500000),
+			}
+		)
 		vehicle.insert()
 
 
@@ -180,23 +186,27 @@
 	if not contact:
 		contact = get_contact_and_address("_Test Customer")
 
-	delivery_trip = frappe.get_doc({
-		"doctype": "Delivery Trip",
-		"company": erpnext.get_default_company(),
-		"departure_time": add_days(now_datetime(), 5),
-		"driver": driver.name,
-		"driver_address": address.name,
-		"vehicle": "JB 007",
-		"delivery_stops": [{
-			"customer": "_Test Customer",
-			"address": contact.shipping_address.parent,
-			"contact": contact.contact_person.parent
-		},
+	delivery_trip = frappe.get_doc(
 		{
-			"customer": "_Test Customer",
-			"address": contact.shipping_address.parent,
-			"contact": contact.contact_person.parent
-		}]
-	}).insert(ignore_permissions=True)
+			"doctype": "Delivery Trip",
+			"company": erpnext.get_default_company(),
+			"departure_time": add_days(now_datetime(), 5),
+			"driver": driver.name,
+			"driver_address": address.name,
+			"vehicle": "JB 007",
+			"delivery_stops": [
+				{
+					"customer": "_Test Customer",
+					"address": contact.shipping_address.parent,
+					"contact": contact.contact_person.parent,
+				},
+				{
+					"customer": "_Test Customer",
+					"address": contact.shipping_address.parent,
+					"contact": contact.contact_person.parent,
+				},
+			],
+		}
+	).insert(ignore_permissions=True)
 
 	return delivery_trip
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index ffea9c2..29b001f 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -55,10 +55,15 @@
 
 		if (frm.doc.has_variants) {
 			frm.set_intro(__("This Item is a Template and cannot be used in transactions. Item attributes will be copied over into the variants unless 'No Copy' is set"), true);
+
 			frm.add_custom_button(__("Show Variants"), function() {
 				frappe.set_route("List", "Item", {"variant_of": frm.doc.name});
 			}, __("View"));
 
+			frm.add_custom_button(__("Item Variant Settings"), function() {
+				frappe.set_route("Form", "Item Variant Settings");
+			}, __("View"));
+
 			frm.add_custom_button(__("Variant Details Report"), function() {
 				frappe.set_route("query-report", "Item Variant Details", {"item": frm.doc.name});
 			}, __("View"));
@@ -110,6 +115,13 @@
 					}
 				});
 			}, __('Actions'));
+		} else {
+			frm.add_custom_button(__("Website Item"), function() {
+				frappe.db.get_value("Website Item", {item_code: frm.doc.name}, "name", (d) => {
+					if (!d.name) frappe.throw(__("Website Item not found"));
+					frappe.set_route("Form", "Website Item", d.name);
+				});
+			}, __("View"));
 		}
 
 		erpnext.item.edit_prices_button(frm);
@@ -131,12 +143,6 @@
 			frappe.set_route('Form', 'Item', new_item.name);
 		});
 
-		if(frm.doc.has_variants) {
-			frm.add_custom_button(__("Item Variant Settings"), function() {
-				frappe.set_route("Form", "Item Variant Settings");
-			}, __("View"));
-		}
-
 		const stock_exists = (frm.doc.__onload
 			&& frm.doc.__onload.stock_exists) ? 1 : 0;
 
@@ -165,21 +171,21 @@
 		frm.set_value('has_batch_no', 0);
 		frm.toggle_enable(['has_serial_no', 'serial_no_series'], !frm.doc.is_fixed_asset);
 
-		frm.call({
-			method: "set_asset_naming_series",
-			doc: frm.doc,
-			callback: function() {
+		frappe.call({
+			method: "erpnext.stock.doctype.item.item.get_asset_naming_series",
+			callback: function(r) {
 				frm.set_value("is_stock_item", frm.doc.is_fixed_asset ? 0 : 1);
-				frm.trigger("set_asset_naming_series");
+				frm.events.set_asset_naming_series(frm, r.message);
 			}
 		});
 
 		frm.trigger('auto_create_assets');
 	},
 
-	set_asset_naming_series: function(frm) {
-		if (frm.doc.__onload && frm.doc.__onload.asset_naming_series) {
-			frm.set_df_property("asset_naming_series", "options", frm.doc.__onload.asset_naming_series);
+	set_asset_naming_series: function(frm, asset_naming_series) {
+		if ((frm.doc.__onload && frm.doc.__onload.asset_naming_series) || asset_naming_series) {
+			let naming_series = (frm.doc.__onload && frm.doc.__onload.asset_naming_series) || asset_naming_series;
+			frm.set_df_property("asset_naming_series", "options", naming_series);
 		}
 	},
 
@@ -371,6 +377,17 @@
 			}
 		}
 
+		frm.set_query('default_provisional_account', 'item_defaults', (doc, cdt, cdn) => {
+			let row = locals[cdt][cdn];
+			return {
+				filters: {
+					"company": row.company,
+					"root_type": ["in", ["Liability", "Asset"]],
+					"is_group": 0
+				}
+			};
+		});
+
 	},
 
 	make_dashboard: function(frm) {
@@ -569,8 +586,7 @@
 								["parent","=", d.attribute]
 							],
 							fields: ["attribute_value"],
-							limit_start: 0,
-							limit_page_length: 500,
+							limit_page_length: 0,
 							parent: "Item Attribute",
 							order_by: "idx"
 						}
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index c797187..76cb31d 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -11,10 +11,9 @@
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
-  "name_and_description_section",
+  "details",
   "naming_series",
   "item_code",
-  "variant_of",
   "item_name",
   "item_group",
   "stock_uom",
@@ -22,6 +21,7 @@
   "disabled",
   "allow_alternative_item",
   "is_stock_item",
+  "has_variants",
   "include_item_in_manufacturing",
   "opening_stock",
   "valuation_rate",
@@ -35,11 +35,11 @@
   "over_billing_allowance",
   "image",
   "section_break_11",
-  "brand",
   "description",
-  "sb_barcodes",
-  "barcodes",
+  "brand",
+  "dashboard_tab",
   "inventory_section",
+  "inventory_settings_section",
   "shelf_life_in_days",
   "end_of_life",
   "default_material_request_type",
@@ -49,6 +49,8 @@
   "weight_per_unit",
   "weight_uom",
   "allow_negative_stock",
+  "sb_barcodes",
+  "barcodes",
   "reorder_section",
   "reorder_levels",
   "unit_of_measure_conversion",
@@ -64,16 +66,16 @@
   "has_serial_no",
   "serial_no_series",
   "variants_section",
-  "has_variants",
+  "variant_of",
   "variant_based_on",
   "attributes",
-  "defaults",
+  "accounting",
   "item_defaults",
-  "purchase_details",
-  "is_purchase_item",
+  "purchasing_tab",
   "purchase_uom",
   "min_order_qty",
   "safety_stock",
+  "is_purchase_item",
   "purchase_details_cb",
   "lead_time_days",
   "last_purchase_rate",
@@ -83,53 +85,44 @@
   "delivered_by_supplier",
   "column_break2",
   "supplier_items",
+  "deferred_expense_section",
+  "enable_deferred_expense",
+  "deferred_expense_account",
+  "no_of_months_exp",
   "foreign_trade_details",
   "country_of_origin",
   "column_break_59",
   "customs_tariff_number",
   "sales_details",
   "sales_uom",
-  "is_sales_item",
   "grant_commission",
+  "is_sales_item",
   "column_break3",
   "max_discount",
   "deferred_revenue",
-  "deferred_revenue_account",
   "enable_deferred_revenue",
-  "column_break_85",
+  "deferred_revenue_account",
   "no_of_months",
-  "deferred_expense_section",
-  "deferred_expense_account",
-  "enable_deferred_expense",
-  "column_break_88",
-  "no_of_months_exp",
   "customer_details",
   "customer_items",
   "item_tax_section_break",
   "taxes",
-  "inspection_criteria",
-  "quality_inspection_template",
+  "quality_tab",
   "inspection_required_before_purchase",
+  "quality_inspection_template",
   "inspection_required_before_delivery",
   "manufacturing",
-  "default_bom",
   "is_sub_contracted_item",
+  "default_bom",
   "column_break_74",
   "customer_code",
   "default_item_manufacturer",
   "default_manufacturer_part_no",
-  "more_information_section",
   "published_in_website",
   "total_projected_qty"
  ],
  "fields": [
   {
-   "fieldname": "name_and_description_section",
-   "fieldtype": "Section Break",
-   "oldfieldtype": "Section Break",
-   "options": "fa fa-flag"
-  },
-  {
    "fieldname": "naming_series",
    "fieldtype": "Select",
    "label": "Series",
@@ -315,7 +308,7 @@
    "collapsible_depends_on": "is_stock_item",
    "depends_on": "is_stock_item",
    "fieldname": "inventory_section",
-   "fieldtype": "Section Break",
+   "fieldtype": "Tab Break",
    "label": "Inventory",
    "oldfieldtype": "Section Break",
    "options": "fa fa-truck"
@@ -422,7 +415,6 @@
    "fieldname": "has_batch_no",
    "fieldtype": "Check",
    "label": "Has Batch No",
-   "no_copy": 1,
    "oldfieldname": "has_batch_no",
    "oldfieldtype": "Select"
   },
@@ -472,7 +464,6 @@
    "fieldname": "has_serial_no",
    "fieldtype": "Check",
    "label": "Has Serial No",
-   "no_copy": 1,
    "oldfieldname": "has_serial_no",
    "oldfieldtype": "Select"
   },
@@ -488,7 +479,7 @@
    "collapsible_depends_on": "attributes",
    "depends_on": "eval:!doc.is_fixed_asset",
    "fieldname": "variants_section",
-   "fieldtype": "Section Break",
+   "fieldtype": "Tab Break",
    "label": "Variants"
   },
   {
@@ -513,34 +504,21 @@
    "fieldname": "attributes",
    "fieldtype": "Table",
    "hidden": 1,
-   "label": "Attributes",
+   "label": "Variant Attributes",
+   "mandatory_depends_on": "has_variants",
    "options": "Item Variant Attribute"
   },
   {
-   "depends_on": "eval:!doc.is_fixed_asset",
-   "fieldname": "defaults",
-   "fieldtype": "Section Break",
-   "label": "Sales, Purchase, Accounting Defaults"
-  },
-  {
    "fieldname": "item_defaults",
    "fieldtype": "Table",
    "label": "Item Defaults",
    "options": "Item Default"
   },
   {
-   "collapsible": 1,
-   "fieldname": "purchase_details",
-   "fieldtype": "Section Break",
-   "label": "Purchase, Replenishment Details",
-   "oldfieldtype": "Section Break",
-   "options": "fa fa-shopping-cart"
-  },
-  {
    "default": "1",
    "fieldname": "is_purchase_item",
    "fieldtype": "Check",
-   "label": "Is Purchase Item"
+   "label": "Allow Purchase"
   },
   {
    "fieldname": "purchase_uom",
@@ -647,10 +625,9 @@
   },
   {
    "collapsible": 1,
-   "default": "eval:!doc.is_fixed_asset",
    "fieldname": "sales_details",
-   "fieldtype": "Section Break",
-   "label": "Sales Details",
+   "fieldtype": "Tab Break",
+   "label": "Sales",
    "oldfieldtype": "Section Break",
    "options": "fa fa-tag"
   },
@@ -664,7 +641,7 @@
    "default": "1",
    "fieldname": "is_sales_item",
    "fieldtype": "Check",
-   "label": "Is Sales Item"
+   "label": "Allow Sales"
   },
   {
    "fieldname": "column_break3",
@@ -700,10 +677,6 @@
    "label": "Enable Deferred Revenue"
   },
   {
-   "fieldname": "column_break_85",
-   "fieldtype": "Column Break"
-  },
-  {
    "depends_on": "enable_deferred_revenue",
    "fieldname": "no_of_months",
    "fieldtype": "Int",
@@ -730,10 +703,6 @@
    "label": "Enable Deferred Expense"
   },
   {
-   "fieldname": "column_break_88",
-   "fieldtype": "Column Break"
-  },
-  {
    "depends_on": "enable_deferred_expense",
    "fieldname": "no_of_months_exp",
    "fieldtype": "Int",
@@ -756,8 +725,8 @@
    "collapsible": 1,
    "collapsible_depends_on": "taxes",
    "fieldname": "item_tax_section_break",
-   "fieldtype": "Section Break",
-   "label": "Item Tax",
+   "fieldtype": "Tab Break",
+   "label": "Tax",
    "oldfieldtype": "Section Break",
    "options": "fa fa-money"
   },
@@ -771,15 +740,6 @@
    "options": "Item Tax"
   },
   {
-   "collapsible": 1,
-   "depends_on": "eval:!doc.is_fixed_asset",
-   "fieldname": "inspection_criteria",
-   "fieldtype": "Section Break",
-   "label": "Inspection Criteria",
-   "oldfieldtype": "Section Break",
-   "options": "fa fa-search"
-  },
-  {
    "default": "0",
    "fieldname": "inspection_required_before_purchase",
    "fieldtype": "Check",
@@ -804,7 +764,7 @@
    "collapsible": 1,
    "depends_on": "is_stock_item",
    "fieldname": "manufacturing",
-   "fieldtype": "Section Break",
+   "fieldtype": "Tab Break",
    "label": "Manufacturing",
    "oldfieldtype": "Section Break",
    "options": "fa fa-cogs"
@@ -872,8 +832,9 @@
   },
   {
    "fieldname": "default_item_manufacturer",
-   "fieldtype": "Data",
+   "fieldtype": "Link",
    "label": "Default Item Manufacturer",
+   "options": "Manufacturer",
    "read_only": 1
   },
   {
@@ -883,12 +844,6 @@
    "read_only": 1
   },
   {
-   "collapsible": 1,
-   "fieldname": "more_information_section",
-   "fieldtype": "Section Break",
-   "label": "More Information"
-  },
-  {
    "default": "0",
    "depends_on": "published_in_website",
    "fieldname": "published_in_website",
@@ -914,6 +869,40 @@
    "fieldname": "allow_negative_stock",
    "fieldtype": "Check",
    "label": "Allow Negative Stock"
+  },
+  {
+   "fieldname": "inventory_settings_section",
+   "fieldtype": "Section Break",
+   "label": "Inventory Settings"
+  },
+  {
+   "fieldname": "purchasing_tab",
+   "fieldtype": "Tab Break",
+   "label": "Purchasing"
+  },
+  {
+   "fieldname": "quality_tab",
+   "fieldtype": "Tab Break",
+   "label": "Quality"
+  },
+  {
+   "fieldname": "details",
+   "fieldtype": "Tab Break",
+   "label": "Details",
+   "oldfieldtype": "Section Break",
+   "options": "fa fa-flag"
+  },
+  {
+   "fieldname": "dashboard_tab",
+   "fieldtype": "Tab Break",
+   "label": "Dashboard",
+   "show_dashboard": 1
+  },
+  {
+   "depends_on": "eval:!doc.is_fixed_asset",
+   "fieldname": "accounting",
+   "fieldtype": "Tab Break",
+   "label": "Accounting"
   }
  ],
  "icon": "fa fa-tag",
@@ -921,7 +910,7 @@
  "image_field": "image",
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2022-02-11 08:07:46.663220",
+ "modified": "2022-06-15 09:02:06.177691",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Item",
@@ -994,4 +983,4 @@
  "states": [],
  "title_field": "item_name",
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 494fb3b..87fa72d 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -3,7 +3,7 @@
 
 import copy
 import json
-from typing import List
+from typing import Dict, List, Optional
 
 import frappe
 from frappe import _
@@ -18,6 +18,7 @@
 	now_datetime,
 	nowtime,
 	strip,
+	strip_html,
 )
 from frappe.utils.html_utils import clean_html
 
@@ -44,21 +45,15 @@
 class InvalidBarcode(frappe.ValidationError):
 	pass
 
+
 class DataValidationError(frappe.ValidationError):
 	pass
 
+
 class Item(Document):
 	def onload(self):
-		self.set_onload('stock_exists', self.stock_ledger_created())
-		self.set_asset_naming_series()
-
-	@frappe.whitelist()
-	def set_asset_naming_series(self):
-		if not hasattr(self, '_asset_naming_series'):
-			from erpnext.assets.doctype.asset.asset import get_asset_naming_series
-			self._asset_naming_series = get_asset_naming_series()
-
-		self.set_onload('asset_naming_series', self._asset_naming_series)
+		self.set_onload("stock_exists", self.stock_ledger_created())
+		self.set_onload("asset_naming_series", get_asset_naming_series())
 
 	def autoname(self):
 		if frappe.db.get_default("item_naming_by") == "Naming Series":
@@ -68,19 +63,15 @@
 					make_variant_item_code(self.variant_of, template_item_name, self)
 			else:
 				from frappe.model.naming import set_name_by_naming_series
+
 				set_name_by_naming_series(self)
 				self.item_code = self.name
 
 		self.item_code = strip(self.item_code)
 		self.name = self.item_code
 
-	def before_insert(self):
-		if not self.description:
-			self.description = self.item_name
-
-
 	def after_insert(self):
-		'''set opening stock and item price'''
+		"""set opening stock and item price"""
 		if self.standard_rate:
 			for default in self.item_defaults or [frappe._dict()]:
 				self.add_price(default.default_price_list)
@@ -92,7 +83,7 @@
 		if not self.item_name:
 			self.item_name = self.item_code
 
-		if not self.description:
+		if not strip_html(cstr(self.description)).strip():
 			self.description = self.item_name
 
 		self.validate_uom()
@@ -115,6 +106,7 @@
 		self.validate_variant_attributes()
 		self.validate_variant_based_on_change()
 		self.validate_fixed_asset()
+		self.clear_retain_sample()
 		self.validate_retain_sample()
 		self.validate_uom_conversion_factor()
 		self.validate_customer_provided_part()
@@ -135,8 +127,8 @@
 		self.update_website_item()
 
 	def validate_description(self):
-		'''Clean HTML description if set'''
-		if cint(frappe.db.get_single_value('Stock Settings', 'clean_description_html')):
+		"""Clean HTML description if set"""
+		if cint(frappe.db.get_single_value("Stock Settings", "clean_description_html")):
 			self.description = clean_html(self.description)
 
 	def validate_customer_provided_part(self):
@@ -148,24 +140,27 @@
 			self.default_material_request_type = "Customer Provided"
 
 	def add_price(self, price_list=None):
-		'''Add a new price'''
+		"""Add a new price"""
 		if not price_list:
-			price_list = (frappe.db.get_single_value('Selling Settings', 'selling_price_list')
-						or frappe.db.get_value('Price List', _('Standard Selling')))
+			price_list = frappe.db.get_single_value(
+				"Selling Settings", "selling_price_list"
+			) or frappe.db.get_value("Price List", _("Standard Selling"))
 		if price_list:
-			item_price = frappe.get_doc({
-				"doctype": "Item Price",
-				"price_list": price_list,
-				"item_code": self.name,
-				"uom": self.stock_uom,
-				"brand": self.brand,
-				"currency": erpnext.get_default_currency(),
-				"price_list_rate": self.standard_rate
-			})
+			item_price = frappe.get_doc(
+				{
+					"doctype": "Item Price",
+					"price_list": price_list,
+					"item_code": self.name,
+					"uom": self.stock_uom,
+					"brand": self.brand,
+					"currency": erpnext.get_default_currency(),
+					"price_list_rate": self.standard_rate,
+				}
+			)
 			item_price.insert()
 
 	def set_opening_stock(self):
-		'''set opening stock'''
+		"""set opening stock"""
 		if not self.is_stock_item or self.has_serial_no or self.has_batch_no:
 			return
 
@@ -178,19 +173,30 @@
 		from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 
 		# default warehouse, or Stores
-		for default in self.item_defaults or [frappe._dict({'company': frappe.defaults.get_defaults().company})]:
-			default_warehouse = (default.default_warehouse
-					or frappe.db.get_single_value('Stock Settings', 'default_warehouse'))
+		for default in self.item_defaults or [
+			frappe._dict({"company": frappe.defaults.get_defaults().company})
+		]:
+			default_warehouse = default.default_warehouse or frappe.db.get_single_value(
+				"Stock Settings", "default_warehouse"
+			)
 			if default_warehouse:
 				warehouse_company = frappe.db.get_value("Warehouse", default_warehouse, "company")
 
 			if not default_warehouse or warehouse_company != default.company:
-				default_warehouse = frappe.db.get_value('Warehouse',
-					{'warehouse_name': _('Stores'), 'company': default.company})
+				default_warehouse = frappe.db.get_value(
+					"Warehouse", {"warehouse_name": _("Stores"), "company": default.company}
+				)
 
 			if default_warehouse:
-				stock_entry = make_stock_entry(item_code=self.name, target=default_warehouse, qty=self.opening_stock,
-					rate=self.valuation_rate, company=default.company, posting_date=getdate(), posting_time=nowtime())
+				stock_entry = make_stock_entry(
+					item_code=self.name,
+					target=default_warehouse,
+					qty=self.opening_stock,
+					rate=self.valuation_rate,
+					company=default.company,
+					posting_date=getdate(),
+					posting_time=nowtime(),
+				)
 
 				stock_entry.add_comment("Comment", _("Opening Stock"))
 
@@ -208,14 +214,28 @@
 		if not self.is_fixed_asset:
 			asset = frappe.db.get_all("Asset", filters={"item_code": self.name, "docstatus": 1}, limit=1)
 			if asset:
-				frappe.throw(_('"Is Fixed Asset" cannot be unchecked, as Asset record exists against the item'))
+				frappe.throw(
+					_('"Is Fixed Asset" cannot be unchecked, as Asset record exists against the item')
+				)
 
 	def validate_retain_sample(self):
-		if self.retain_sample and not frappe.db.get_single_value('Stock Settings', 'sample_retention_warehouse'):
+		if self.retain_sample and not frappe.db.get_single_value(
+			"Stock Settings", "sample_retention_warehouse"
+		):
 			frappe.throw(_("Please select Sample Retention Warehouse in Stock Settings first"))
 		if self.retain_sample and not self.has_batch_no:
-			frappe.throw(_("{0} Retain Sample is based on batch, please check Has Batch No to retain sample of item").format(
-				self.item_code))
+			frappe.throw(
+				_(
+					"{0} Retain Sample is based on batch, please check Has Batch No to retain sample of item"
+				).format(self.item_code)
+			)
+
+	def clear_retain_sample(self):
+		if not self.has_batch_no:
+			self.retain_sample = None
+
+		if not self.retain_sample:
+			self.sample_quantity = None
 
 	def add_default_uom_in_conversion_factor_table(self):
 		if not self.is_new() and self.has_value_changed("stock_uom"):
@@ -228,10 +248,7 @@
 		uoms_list = [d.uom for d in self.get("uoms")]
 
 		if self.stock_uom not in uoms_list:
-			self.append("uoms", {
-				"uom": self.stock_uom,
-				"conversion_factor": 1
-			})
+			self.append("uoms", {"uom": self.stock_uom, "conversion_factor": 1})
 
 	def update_website_item(self):
 		"""Update Website Item if change in Item impacts it."""
@@ -239,8 +256,7 @@
 
 		if web_item:
 			changed = {}
-			editable_fields = ["item_name", "item_group", "stock_uom", "brand", "description",
-				"disabled"]
+			editable_fields = ["item_name", "item_group", "stock_uom", "brand", "description", "disabled"]
 			doc_before_save = self.get_doc_before_save()
 
 			for field in editable_fields:
@@ -258,7 +274,7 @@
 			web_item_doc.save()
 
 	def validate_item_tax_net_rate_range(self):
-		for tax in self.get('taxes'):
+		for tax in self.get("taxes"):
 			if flt(tax.maximum_net_rate) < flt(tax.minimum_net_rate):
 				frappe.throw(_("Row #{0}: Maximum Net Rate cannot be greater than Minimum Net Rate"))
 
@@ -273,23 +289,31 @@
 		if not self.get("reorder_levels"):
 			for d in template.get("reorder_levels"):
 				n = {}
-				for k in ("warehouse", "warehouse_reorder_level",
-					"warehouse_reorder_qty", "material_request_type"):
+				for k in (
+					"warehouse",
+					"warehouse_reorder_level",
+					"warehouse_reorder_qty",
+					"material_request_type",
+				):
 					n[k] = d.get(k)
 				self.append("reorder_levels", n)
 
 	def validate_conversion_factor(self):
 		check_list = []
-		for d in self.get('uoms'):
+		for d in self.get("uoms"):
 			if cstr(d.uom) in check_list:
 				frappe.throw(
-					_("Unit of Measure {0} has been entered more than once in Conversion Factor Table").format(d.uom))
+					_("Unit of Measure {0} has been entered more than once in Conversion Factor Table").format(
+						d.uom
+					)
+				)
 			else:
 				check_list.append(cstr(d.uom))
 
 			if d.uom and cstr(d.uom) == cstr(self.stock_uom) and flt(d.conversion_factor) != 1:
 				frappe.throw(
-					_("Conversion factor for default Unit of Measure must be 1 in row {0}").format(d.idx))
+					_("Conversion factor for default Unit of Measure must be 1 in row {0}").format(d.idx)
+				)
 
 	def validate_item_type(self):
 		if self.has_serial_no == 1 and self.is_stock_item == 0 and not self.is_fixed_asset:
@@ -302,28 +326,32 @@
 		for field in ["serial_no_series", "batch_number_series"]:
 			series = self.get(field)
 			if series and "#" in series and "." not in series:
-				frappe.throw(_("Invalid naming series (. missing) for {0}")
-					.format(frappe.bold(self.meta.get_field(field).label)))
+				frappe.throw(
+					_("Invalid naming series (. missing) for {0}").format(
+						frappe.bold(self.meta.get_field(field).label)
+					)
+				)
 
 	def check_for_active_boms(self):
 		if self.default_bom:
 			bom_item = frappe.db.get_value("BOM", self.default_bom, "item")
 			if bom_item not in (self.name, self.variant_of):
 				frappe.throw(
-					_("Default BOM ({0}) must be active for this item or its template").format(bom_item))
+					_("Default BOM ({0}) must be active for this item or its template").format(bom_item)
+				)
 
 	def fill_customer_code(self):
 		"""
-			Append all the customer codes and insert into "customer_code" field of item table.
-			Used to search Item by customer code.
+		Append all the customer codes and insert into "customer_code" field of item table.
+		Used to search Item by customer code.
 		"""
 		customer_codes = set(d.ref_code for d in self.get("customer_items", []))
-		self.customer_code = ','.join(customer_codes)
+		self.customer_code = ",".join(customer_codes)
 
 	def check_item_tax(self):
 		"""Check whether Tax Rate is not entered twice for same Tax Type"""
 		check_list = []
-		for d in self.get('taxes'):
+		for d in self.get("taxes"):
 			if d.item_tax_template:
 				if d.item_tax_template in check_list:
 					frappe.throw(_("{0} entered twice in Item Tax").format(d.item_tax_template))
@@ -332,24 +360,39 @@
 
 	def validate_barcode(self):
 		from stdnum import ean
+
 		if len(self.barcodes) > 0:
 			for item_barcode in self.barcodes:
-				options = frappe.get_meta("Item Barcode").get_options("barcode_type").split('\n')
+				options = frappe.get_meta("Item Barcode").get_options("barcode_type").split("\n")
 				if item_barcode.barcode:
 					duplicate = frappe.db.sql(
-						"""select parent from `tabItem Barcode` where barcode = %s and parent != %s""", (item_barcode.barcode, self.name))
+						"""select parent from `tabItem Barcode` where barcode = %s and parent != %s""",
+						(item_barcode.barcode, self.name),
+					)
 					if duplicate:
-						frappe.throw(_("Barcode {0} already used in Item {1}").format(
-							item_barcode.barcode, duplicate[0][0]))
+						frappe.throw(
+							_("Barcode {0} already used in Item {1}").format(item_barcode.barcode, duplicate[0][0])
+						)
 
-					item_barcode.barcode_type = "" if item_barcode.barcode_type not in options else item_barcode.barcode_type
-					if item_barcode.barcode_type and item_barcode.barcode_type.upper() in ('EAN', 'UPC-A', 'EAN-13', 'EAN-8'):
+					item_barcode.barcode_type = (
+						"" if item_barcode.barcode_type not in options else item_barcode.barcode_type
+					)
+					if item_barcode.barcode_type and item_barcode.barcode_type.upper() in (
+						"EAN",
+						"UPC-A",
+						"EAN-13",
+						"EAN-8",
+					):
 						if not ean.is_valid(item_barcode.barcode):
-							frappe.throw(_("Barcode {0} is not a valid {1} code").format(
-								item_barcode.barcode, item_barcode.barcode_type), InvalidBarcode)
+							frappe.throw(
+								_("Barcode {0} is not a valid {1} code").format(
+									item_barcode.barcode, item_barcode.barcode_type
+								),
+								InvalidBarcode,
+							)
 
 	def validate_warehouse_for_reorder(self):
-		'''Validate Reorder level table for duplicate and conditional mandatory'''
+		"""Validate Reorder level table for duplicate and conditional mandatory"""
 		warehouse = []
 		for d in self.get("reorder_levels"):
 			if not d.warehouse_group:
@@ -357,20 +400,30 @@
 			if d.get("warehouse") and d.get("warehouse") not in warehouse:
 				warehouse += [d.get("warehouse")]
 			else:
-				frappe.throw(_("Row {0}: An Reorder entry already exists for this warehouse {1}")
-									.format(d.idx, d.warehouse), DuplicateReorderRows)
+				frappe.throw(
+					_("Row {0}: An Reorder entry already exists for this warehouse {1}").format(
+						d.idx, d.warehouse
+					),
+					DuplicateReorderRows,
+				)
 
 			if d.warehouse_reorder_level and not d.warehouse_reorder_qty:
 				frappe.throw(_("Row #{0}: Please set reorder quantity").format(d.idx))
 
 	def stock_ledger_created(self):
-		if not hasattr(self, '_stock_ledger_created'):
-			self._stock_ledger_created = len(frappe.db.sql("""select name from `tabStock Ledger Entry`
-				where item_code = %s and is_cancelled = 0 limit 1""", self.name))
+		if not hasattr(self, "_stock_ledger_created"):
+			self._stock_ledger_created = len(
+				frappe.db.sql(
+					"""select name from `tabStock Ledger Entry`
+				where item_code = %s and is_cancelled = 0 limit 1""",
+					self.name,
+				)
+			)
 		return self._stock_ledger_created
 
 	def update_item_price(self):
-		frappe.db.sql("""
+		frappe.db.sql(
+			"""
 				UPDATE `tabItem Price`
 				SET
 					item_name=%(item_name)s,
@@ -382,8 +435,8 @@
 				item_name=self.item_name,
 				item_description=self.description,
 				brand=self.brand,
-				item_code=self.name
-			)
+				item_code=self.name,
+			),
 		)
 
 	def on_trash(self):
@@ -400,12 +453,16 @@
 			self.validate_properties_before_merge(new_name)
 			self.validate_duplicate_product_bundles_before_merge(old_name, new_name)
 			self.validate_duplicate_website_item_before_merge(old_name, new_name)
+			self.delete_old_bins(old_name)
 
 	def after_rename(self, old_name, new_name, merge):
 		if merge:
 			self.validate_duplicate_item_in_stock_reconciliation(old_name, new_name)
-			frappe.msgprint(_("It can take upto few hours for accurate stock values to be visible after merging items."),
-					indicator="orange", title="Note")
+			frappe.msgprint(
+				_("It can take upto few hours for accurate stock values to be visible after merging items."),
+				indicator="orange",
+				title=_("Note"),
+			)
 
 		if self.published_in_website:
 			invalidate_cache_for_item(self)
@@ -417,36 +474,54 @@
 			self.recalculate_bin_qty(new_name)
 
 		for dt in ("Sales Taxes and Charges", "Purchase Taxes and Charges"):
-			for d in frappe.db.sql("""select name, item_wise_tax_detail from `tab{0}`
-					where ifnull(item_wise_tax_detail, '') != ''""".format(dt), as_dict=1):
+			for d in frappe.db.sql(
+				"""select name, item_wise_tax_detail from `tab{0}`
+					where ifnull(item_wise_tax_detail, '') != ''""".format(
+					dt
+				),
+				as_dict=1,
+			):
 
 				item_wise_tax_detail = json.loads(d.item_wise_tax_detail)
 				if isinstance(item_wise_tax_detail, dict) and old_name in item_wise_tax_detail:
 					item_wise_tax_detail[new_name] = item_wise_tax_detail[old_name]
 					item_wise_tax_detail.pop(old_name)
 
-					frappe.db.set_value(dt, d.name, "item_wise_tax_detail",
-											json.dumps(item_wise_tax_detail), update_modified=False)
+					frappe.db.set_value(
+						dt, d.name, "item_wise_tax_detail", json.dumps(item_wise_tax_detail), update_modified=False
+					)
+
+	def delete_old_bins(self, old_name):
+		frappe.db.delete("Bin", {"item_code": old_name})
 
 	def validate_duplicate_item_in_stock_reconciliation(self, old_name, new_name):
-		records = frappe.db.sql(""" SELECT parent, COUNT(*) as records
+		records = frappe.db.sql(
+			""" SELECT parent, COUNT(*) as records
 			FROM `tabStock Reconciliation Item`
 			WHERE item_code = %s and docstatus = 1
 			GROUP By item_code, warehouse, parent
 			HAVING records > 1
-		""", new_name, as_dict=1)
+		""",
+			new_name,
+			as_dict=1,
+		)
 
-		if not records: return
+		if not records:
+			return
 		document = _("Stock Reconciliation") if len(records) == 1 else _("Stock Reconciliations")
 
 		msg = _("The items {0} and {1} are present in the following {2} :").format(
-			frappe.bold(old_name), frappe.bold(new_name), document)
+			frappe.bold(old_name), frappe.bold(new_name), document
+		)
 
-		msg += ' <br>'
-		msg += ', '.join([get_link_to_form("Stock Reconciliation", d.parent) for d in records]) + "<br><br>"
+		msg += " <br>"
+		msg += (
+			", ".join([get_link_to_form("Stock Reconciliation", d.parent) for d in records]) + "<br><br>"
+		)
 
-		msg += _("Note: To merge the items, create a separate Stock Reconciliation for the old item {0}").format(
-			frappe.bold(old_name))
+		msg += _(
+			"Note: To merge the items, create a separate Stock Reconciliation for the old item {0}"
+		).format(frappe.bold(old_name))
 
 		frappe.throw(_(msg), title=_("Cannot Merge"), exc=DataValidationError)
 
@@ -465,8 +540,8 @@
 
 	def validate_duplicate_product_bundles_before_merge(self, old_name, new_name):
 		"Block merge if both old and new items have product bundles."
-		old_bundle = frappe.get_value("Product Bundle",filters={"new_item_code": old_name})
-		new_bundle = frappe.get_value("Product Bundle",filters={"new_item_code": new_name})
+		old_bundle = frappe.get_value("Product Bundle", filters={"new_item_code": old_name})
+		new_bundle = frappe.get_value("Product Bundle", filters={"new_item_code": new_name})
 
 		if old_bundle and new_bundle:
 			bundle_link = get_link_to_form("Product Bundle", old_bundle)
@@ -479,15 +554,14 @@
 
 	def validate_duplicate_website_item_before_merge(self, old_name, new_name):
 		"""
-			Block merge if both old and new items have website items against them.
-			This is to avoid duplicate website items after merging.
+		Block merge if both old and new items have website items against them.
+		This is to avoid duplicate website items after merging.
 		"""
 		web_items = frappe.get_all(
 			"Website Item",
-			filters={
-				"item_code": ["in", [old_name, new_name]]
-			},
-			fields=["item_code", "name"])
+			filters={"item_code": ["in", [old_name, new_name]]},
+			fields=["item_code", "name"],
+		)
 
 		if len(web_items) <= 1:
 			return
@@ -505,42 +579,61 @@
 
 	def recalculate_bin_qty(self, new_name):
 		from erpnext.stock.stock_balance import repost_stock
-		existing_allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
+
+		existing_allow_negative_stock = frappe.db.get_value(
+			"Stock Settings", None, "allow_negative_stock"
+		)
 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
 
-		repost_stock_for_warehouses = frappe.db.sql_list("""select distinct warehouse
-			from tabBin where item_code=%s""", new_name)
+		repost_stock_for_warehouses = frappe.get_all(
+			"Stock Ledger Entry",
+			"warehouse",
+			filters={"item_code": new_name},
+			pluck="warehouse",
+			distinct=True,
+		)
 
 		# Delete all existing bins to avoid duplicate bins for the same item and warehouse
-		frappe.db.sql("delete from `tabBin` where item_code=%s", new_name)
+		frappe.db.delete("Bin", {"item_code": new_name})
 
 		for warehouse in repost_stock_for_warehouses:
 			repost_stock(new_name, warehouse)
 
-		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock)
+		frappe.db.set_value(
+			"Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock
+		)
 
 	def update_bom_item_desc(self):
 		if self.is_new():
 			return
 
-		if self.db_get('description') != self.description:
-			frappe.db.sql("""
+		if self.db_get("description") != self.description:
+			frappe.db.sql(
+				"""
 				update `tabBOM`
 				set description = %s
 				where item = %s and docstatus < 2
-			""", (self.description, self.name))
+			""",
+				(self.description, self.name),
+			)
 
-			frappe.db.sql("""
+			frappe.db.sql(
+				"""
 				update `tabBOM Item`
 				set description = %s
 				where item_code = %s and docstatus < 2
-			""", (self.description, self.name))
+			""",
+				(self.description, self.name),
+			)
 
-			frappe.db.sql("""
+			frappe.db.sql(
+				"""
 				update `tabBOM Explosion Item`
 				set description = %s
 				where item_code = %s and docstatus < 2
-			""", (self.description, self.name))
+			""",
+				(self.description, self.name),
+			)
 
 	def validate_item_defaults(self):
 		companies = {row.company for row in self.item_defaults}
@@ -550,41 +643,61 @@
 
 		validate_item_default_company_links(self.item_defaults)
 
-
 	def update_defaults_from_item_group(self):
 		"""Get defaults from Item Group"""
 		if self.item_defaults or not self.item_group:
 			return
 
-		item_defaults = frappe.db.get_values("Item Default", {"parent": self.item_group},
-			['company', 'default_warehouse','default_price_list','buying_cost_center','default_supplier',
-			'expense_account','selling_cost_center','income_account'], as_dict = 1)
+		item_defaults = frappe.db.get_values(
+			"Item Default",
+			{"parent": self.item_group},
+			[
+				"company",
+				"default_warehouse",
+				"default_price_list",
+				"buying_cost_center",
+				"default_supplier",
+				"expense_account",
+				"selling_cost_center",
+				"income_account",
+			],
+			as_dict=1,
+		)
 		if item_defaults:
 			for item in item_defaults:
-				self.append('item_defaults', {
-					'company': item.company,
-					'default_warehouse': item.default_warehouse,
-					'default_price_list': item.default_price_list,
-					'buying_cost_center': item.buying_cost_center,
-					'default_supplier': item.default_supplier,
-					'expense_account': item.expense_account,
-					'selling_cost_center': item.selling_cost_center,
-					'income_account': item.income_account
-				})
+				self.append(
+					"item_defaults",
+					{
+						"company": item.company,
+						"default_warehouse": item.default_warehouse,
+						"default_price_list": item.default_price_list,
+						"buying_cost_center": item.buying_cost_center,
+						"default_supplier": item.default_supplier,
+						"expense_account": item.expense_account,
+						"selling_cost_center": item.selling_cost_center,
+						"income_account": item.income_account,
+					},
+				)
 		else:
 			defaults = frappe.defaults.get_defaults() or {}
 
 			# To check default warehouse is belong to the default company
-			if defaults.get("default_warehouse") and defaults.company and frappe.db.exists("Warehouse",
-				{'name': defaults.default_warehouse, 'company': defaults.company}):
-					self.append("item_defaults", {
-						"company": defaults.get("company"),
-						"default_warehouse": defaults.default_warehouse
-					})
+			if (
+				defaults.get("default_warehouse")
+				and defaults.company
+				and frappe.db.exists(
+					"Warehouse", {"name": defaults.default_warehouse, "company": defaults.company}
+				)
+			):
+				self.append(
+					"item_defaults",
+					{"company": defaults.get("company"), "default_warehouse": defaults.default_warehouse},
+				)
 
 	def update_variants(self):
-		if self.flags.dont_update_variants or \
-						frappe.db.get_single_value('Item Variant Settings', 'do_not_update_variants'):
+		if self.flags.dont_update_variants or frappe.db.get_single_value(
+			"Item Variant Settings", "do_not_update_variants"
+		):
 			return
 		if self.has_variants:
 			variants = frappe.db.get_all("Item", fields=["item_code"], filters={"variant_of": self.name})
@@ -593,8 +706,13 @@
 					update_variants(variants, self, publish_progress=False)
 					frappe.msgprint(_("Item Variants updated"))
 				else:
-					frappe.enqueue("erpnext.stock.doctype.item.item.update_variants",
-						variants=variants, template=self, now=frappe.flags.in_test, timeout=600)
+					frappe.enqueue(
+						"erpnext.stock.doctype.item.item.update_variants",
+						variants=variants,
+						template=self,
+						now=frappe.flags.in_test,
+						timeout=600,
+					)
 
 	def validate_has_variants(self):
 		if not self.has_variants and frappe.db.get_value("Item", self.name, "has_variants"):
@@ -626,11 +744,8 @@
 		# fetch all attributes of these items
 		item_attributes = frappe.get_all(
 			"Item Variant Attribute",
-			filters={
-				"parent": ["in", items],
-				"attribute": ["in", deleted_attribute]
-			},
-			fields=["attribute", "parent"]
+			filters={"parent": ["in", items], "attribute": ["in", deleted_attribute]},
+			fields=["attribute", "parent"],
 		)
 		not_included = defaultdict(list)
 
@@ -649,14 +764,18 @@
 			return """<tr>
 				<td>{0}</td>
 				<td>{1}</td>
-			</tr>""".format(title, body)
+			</tr>""".format(
+				title, body
+			)
 
-		rows = ''
+		rows = ""
 		for docname, attr_list in not_included.items():
 			link = "<a href='/app/Form/Item/{0}'>{0}</a>".format(frappe.bold(_(docname)))
 			rows += table_row(link, body(attr_list))
 
-		error_description = _('The following deleted attributes exist in Variants but not in the Template. You can either delete the Variants or keep the attribute(s) in template.')
+		error_description = _(
+			"The following deleted attributes exist in Variants but not in the Template. You can either delete the Variants or keep the attribute(s) in template."
+		)
 
 		message = """
 			<div>{0}</div><br>
@@ -667,25 +786,37 @@
 				</thead>
 				{3}
 			</table>
-		""".format(error_description, _('Variant Items'), _('Attributes'), rows)
+		""".format(
+			error_description, _("Variant Items"), _("Attributes"), rows
+		)
 
 		frappe.throw(message, title=_("Variant Attribute Error"), is_minimizable=True, wide=True)
 
-
 	def validate_stock_exists_for_template_item(self):
 		if self.stock_ledger_created() and self._doc_before_save:
-			if (cint(self._doc_before_save.has_variants) != cint(self.has_variants)
-				or self._doc_before_save.variant_of != self.variant_of):
-				frappe.throw(_("Cannot change Variant properties after stock transaction. You will have to make a new Item to do this.").format(self.name),
-					StockExistsForTemplate)
+			if (
+				cint(self._doc_before_save.has_variants) != cint(self.has_variants)
+				or self._doc_before_save.variant_of != self.variant_of
+			):
+				frappe.throw(
+					_(
+						"Cannot change Variant properties after stock transaction. You will have to make a new Item to do this."
+					).format(self.name),
+					StockExistsForTemplate,
+				)
 
 			if self.has_variants or self.variant_of:
-				if not self.is_child_table_same('attributes'):
+				if not self.is_child_table_same("attributes"):
 					frappe.throw(
-						_('Cannot change Attributes after stock transaction. Make a new Item and transfer stock to the new Item'))
+						_(
+							"Cannot change Attributes after stock transaction. Make a new Item and transfer stock to the new Item"
+						)
+					)
 
 	def validate_variant_based_on_change(self):
-		if not self.is_new() and (self.variant_of or (self.has_variants and frappe.get_all("Item", {"variant_of": self.name}))):
+		if not self.is_new() and (
+			self.variant_of or (self.has_variants and frappe.get_all("Item", {"variant_of": self.name}))
+		):
 			if self.variant_based_on != frappe.db.get_value("Item", self.name, "variant_based_on"):
 				frappe.throw(_("Variant Based On cannot be changed"))
 
@@ -698,8 +829,11 @@
 		if self.variant_of:
 			template_uom = frappe.db.get_value("Item", self.variant_of, "stock_uom")
 			if template_uom != self.stock_uom:
-				frappe.throw(_("Default Unit of Measure for Variant '{0}' must be same as in Template '{1}'")
-									.format(self.stock_uom, template_uom))
+				frappe.throw(
+					_("Default Unit of Measure for Variant '{0}' must be same as in Template '{1}'").format(
+						self.stock_uom, template_uom
+					)
+				)
 
 	def validate_uom_conversion_factor(self):
 		if self.uoms:
@@ -713,21 +847,22 @@
 			return
 
 		if not self.variant_based_on:
-			self.variant_based_on = 'Item Attribute'
+			self.variant_based_on = "Item Attribute"
 
-		if self.variant_based_on == 'Item Attribute':
+		if self.variant_based_on == "Item Attribute":
 			attributes = []
 			if not self.attributes:
 				frappe.throw(_("Attribute table is mandatory"))
 			for d in self.attributes:
 				if d.attribute in attributes:
 					frappe.throw(
-						_("Attribute {0} selected multiple times in Attributes Table").format(d.attribute))
+						_("Attribute {0} selected multiple times in Attributes Table").format(d.attribute)
+					)
 				else:
 					attributes.append(d.attribute)
 
 	def validate_variant_attributes(self):
-		if self.is_new() and self.variant_of and self.variant_based_on == 'Item Attribute':
+		if self.is_new() and self.variant_of and self.variant_based_on == "Item Attribute":
 			# remove attributes with no attribute_value set
 			self.attributes = [d for d in self.attributes if cstr(d.attribute_value).strip()]
 
@@ -738,8 +873,9 @@
 
 			variant = get_variant(self.variant_of, args, self.name)
 			if variant:
-				frappe.throw(_("Item variant {0} exists with same attributes")
-					.format(variant), ItemVariantExistsError)
+				frappe.throw(
+					_("Item variant {0} exists with same attributes").format(variant), ItemVariantExistsError
+				)
 
 			validate_item_variant_attributes(self, args)
 
@@ -751,74 +887,129 @@
 		if self.is_new():
 			return
 
-		fields = ("has_serial_no", "is_stock_item", "valuation_method", "has_batch_no")
+		restricted_fields = ("has_serial_no", "is_stock_item", "valuation_method", "has_batch_no")
 
-		values = frappe.db.get_value("Item", self.name, fields, as_dict=True)
-		if not values.get('valuation_method') and self.get('valuation_method'):
-			values['valuation_method'] = frappe.db.get_single_value("Stock Settings", "valuation_method") or "FIFO"
+		values = frappe.db.get_value("Item", self.name, restricted_fields, as_dict=True)
+		if not values:
+			return
 
-		if values:
-			for field in fields:
-				if cstr(self.get(field)) != cstr(values.get(field)):
-					if self.check_if_linked_document_exists(field):
-						frappe.throw(_("As there are existing transactions against item {0}, you can not change the value of {1}").format(self.name, frappe.bold(self.meta.get_label(field))))
+		if not values.get("valuation_method") and self.get("valuation_method"):
+			values["valuation_method"] = (
+				frappe.db.get_single_value("Stock Settings", "valuation_method") or "FIFO"
+			)
 
-	def check_if_linked_document_exists(self, field):
-		linked_doctypes = ["Delivery Note Item", "Sales Invoice Item", "POS Invoice Item", "Purchase Receipt Item",
-			"Purchase Invoice Item", "Stock Entry Detail", "Stock Reconciliation Item"]
+		changed_fields = [
+			field for field in restricted_fields if cstr(self.get(field)) != cstr(values.get(field))
+		]
+		if not changed_fields:
+			return
+
+		if linked_doc := self._get_linked_submitted_documents(changed_fields):
+			changed_field_labels = [frappe.bold(self.meta.get_label(f)) for f in changed_fields]
+			msg = _(
+				"As there are existing submitted transactions against item {0}, you can not change the value of {1}."
+			).format(self.name, ", ".join(changed_field_labels))
+
+			if linked_doc and isinstance(linked_doc, dict):
+				msg += "<br>"
+				msg += _("Example of a linked document: {0}").format(
+					frappe.get_desk_link(linked_doc.doctype, linked_doc.docname)
+				)
+
+			frappe.throw(msg, title=_("Linked with submitted documents"))
+
+	def _get_linked_submitted_documents(self, changed_fields: List[str]) -> Optional[Dict[str, str]]:
+		linked_doctypes = [
+			"Delivery Note Item",
+			"Sales Invoice Item",
+			"POS Invoice Item",
+			"Purchase Receipt Item",
+			"Purchase Invoice Item",
+			"Stock Entry Detail",
+			"Stock Reconciliation Item",
+		]
 
 		# For "Is Stock Item", following doctypes is important
 		# because reserved_qty, ordered_qty and requested_qty updated from these doctypes
-		if field == "is_stock_item":
-			linked_doctypes += ["Sales Order Item", "Purchase Order Item", "Material Request Item", "Product Bundle"]
+		if "is_stock_item" in changed_fields:
+			linked_doctypes += [
+				"Sales Order Item",
+				"Purchase Order Item",
+				"Material Request Item",
+				"Product Bundle",
+			]
 
 		for doctype in linked_doctypes:
-			filters={"item_code": self.name, "docstatus": 1}
+			filters = {"item_code": self.name, "docstatus": 1}
 
 			if doctype == "Product Bundle":
-				filters={"new_item_code": self.name}
+				filters = {"new_item_code": self.name}
 
-			if doctype in ("Purchase Invoice Item", "Sales Invoice Item",):
+			if doctype in (
+				"Purchase Invoice Item",
+				"Sales Invoice Item",
+			):
 				# If Invoice has Stock impact, only then consider it.
-				if self.stock_ledger_created():
-					return True
+				if linked_doc := frappe.db.get_value(
+					"Stock Ledger Entry",
+					{"item_code": self.name, "is_cancelled": 0},
+					["voucher_no as docname", "voucher_type as doctype"],
+					as_dict=True,
+				):
+					return linked_doc
 
-			elif frappe.db.get_value(doctype, filters):
-				return True
+			elif linked_doc := frappe.db.get_value(
+				doctype,
+				filters,
+				["parent as docname", "parenttype as doctype"],
+				as_dict=True,
+			):
+				return linked_doc
 
 	def validate_auto_reorder_enabled_in_stock_settings(self):
 		if self.reorder_levels:
-			enabled = frappe.db.get_single_value('Stock Settings', 'auto_indent')
+			enabled = frappe.db.get_single_value("Stock Settings", "auto_indent")
 			if not enabled:
-				frappe.msgprint(msg=_("You have to enable auto re-order in Stock Settings to maintain re-order levels."), title=_("Enable Auto Re-Order"), indicator="orange")
+				frappe.msgprint(
+					msg=_("You have to enable auto re-order in Stock Settings to maintain re-order levels."),
+					title=_("Enable Auto Re-Order"),
+					indicator="orange",
+				)
 
 
 def make_item_price(item, price_list_name, item_price):
-	frappe.get_doc({
-		'doctype': 'Item Price',
-		'price_list': price_list_name,
-		'item_code': item,
-		'price_list_rate': item_price
-	}).insert()
+	frappe.get_doc(
+		{
+			"doctype": "Item Price",
+			"price_list": price_list_name,
+			"item_code": item,
+			"price_list_rate": item_price,
+		}
+	).insert()
+
 
 def get_timeline_data(doctype, name):
 	"""get timeline data based on Stock Ledger Entry. This is displayed as heatmap on the item page."""
 
-	items = frappe.db.sql("""select unix_timestamp(posting_date), count(*)
+	items = frappe.db.sql(
+		"""select unix_timestamp(posting_date), count(*)
 							from `tabStock Ledger Entry`
 							where item_code=%s and posting_date > date_sub(curdate(), interval 1 year)
-							group by posting_date""", name)
+							group by posting_date""",
+		name,
+	)
 
 	return dict(items)
 
 
-
 def validate_end_of_life(item_code, end_of_life=None, disabled=None):
 	if (not end_of_life) or (disabled is None):
 		end_of_life, disabled = frappe.db.get_value("Item", item_code, ["end_of_life", "disabled"])
 
 	if end_of_life and end_of_life != "0000-00-00" and getdate(end_of_life) <= now_datetime().date():
-		frappe.throw(_("Item {0} has reached its end of life on {1}").format(item_code, formatdate(end_of_life)))
+		frappe.throw(
+			_("Item {0} has reached its end of life on {1}").format(item_code, formatdate(end_of_life))
+		)
 
 	if disabled:
 		frappe.throw(_("Item {0} is disabled").format(item_code))
@@ -839,11 +1030,13 @@
 	if docstatus == 2:
 		frappe.throw(_("Item {0} is cancelled").format(item_code))
 
+
 def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0):
 	"""returns last purchase details in stock uom"""
 	# get last purchase order item details
 
-	last_purchase_order = frappe.db.sql("""\
+	last_purchase_order = frappe.db.sql(
+		"""\
 		select po.name, po.transaction_date, po.conversion_rate,
 			po_item.conversion_factor, po_item.base_price_list_rate,
 			po_item.discount_percentage, po_item.base_rate, po_item.base_net_rate
@@ -851,11 +1044,14 @@
 		where po.docstatus = 1 and po_item.item_code = %s and po.name != %s and
 			po.name = po_item.parent
 		order by po.transaction_date desc, po.name desc
-		limit 1""", (item_code, cstr(doc_name)), as_dict=1)
-
+		limit 1""",
+		(item_code, cstr(doc_name)),
+		as_dict=1,
+	)
 
 	# get last purchase receipt item details
-	last_purchase_receipt = frappe.db.sql("""\
+	last_purchase_receipt = frappe.db.sql(
+		"""\
 		select pr.name, pr.posting_date, pr.posting_time, pr.conversion_rate,
 			pr_item.conversion_factor, pr_item.base_price_list_rate, pr_item.discount_percentage,
 			pr_item.base_rate, pr_item.base_net_rate
@@ -863,20 +1059,29 @@
 		where pr.docstatus = 1 and pr_item.item_code = %s and pr.name != %s and
 			pr.name = pr_item.parent
 		order by pr.posting_date desc, pr.posting_time desc, pr.name desc
-		limit 1""", (item_code, cstr(doc_name)), as_dict=1)
+		limit 1""",
+		(item_code, cstr(doc_name)),
+		as_dict=1,
+	)
 
-	purchase_order_date = getdate(last_purchase_order and last_purchase_order[0].transaction_date
-							   or "1900-01-01")
-	purchase_receipt_date = getdate(last_purchase_receipt and
-								 last_purchase_receipt[0].posting_date or "1900-01-01")
+	purchase_order_date = getdate(
+		last_purchase_order and last_purchase_order[0].transaction_date or "1900-01-01"
+	)
+	purchase_receipt_date = getdate(
+		last_purchase_receipt and last_purchase_receipt[0].posting_date or "1900-01-01"
+	)
 
-	if last_purchase_order and (purchase_order_date >= purchase_receipt_date or not last_purchase_receipt):
+	if last_purchase_order and (
+		purchase_order_date >= purchase_receipt_date or not last_purchase_receipt
+	):
 		# use purchase order
 
 		last_purchase = last_purchase_order[0]
 		purchase_date = purchase_order_date
 
-	elif last_purchase_receipt and (purchase_receipt_date > purchase_order_date or not last_purchase_order):
+	elif last_purchase_receipt and (
+		purchase_receipt_date > purchase_order_date or not last_purchase_order
+	):
 		# use purchase receipt
 		last_purchase = last_purchase_receipt[0]
 		purchase_date = purchase_receipt_date
@@ -885,22 +1090,25 @@
 		return frappe._dict()
 
 	conversion_factor = flt(last_purchase.conversion_factor)
-	out = frappe._dict({
-		"base_price_list_rate": flt(last_purchase.base_price_list_rate) / conversion_factor,
-		"base_rate": flt(last_purchase.base_rate) / conversion_factor,
-		"base_net_rate": flt(last_purchase.base_net_rate) / conversion_factor,
-		"discount_percentage": flt(last_purchase.discount_percentage),
-		"purchase_date": purchase_date
-	})
-
+	out = frappe._dict(
+		{
+			"base_price_list_rate": flt(last_purchase.base_price_list_rate) / conversion_factor,
+			"base_rate": flt(last_purchase.base_rate) / conversion_factor,
+			"base_net_rate": flt(last_purchase.base_net_rate) / conversion_factor,
+			"discount_percentage": flt(last_purchase.discount_percentage),
+			"purchase_date": purchase_date,
+		}
+	)
 
 	conversion_rate = flt(conversion_rate) or 1.0
-	out.update({
-		"price_list_rate": out.base_price_list_rate / conversion_rate,
-		"rate": out.base_rate / conversion_rate,
-		"base_rate": out.base_rate,
-		"base_net_rate": out.base_net_rate
-	})
+	out.update(
+		{
+			"price_list_rate": out.base_price_list_rate / conversion_rate,
+			"rate": out.base_rate / conversion_rate,
+			"base_rate": out.base_rate,
+			"base_net_rate": out.base_net_rate,
+		}
+	)
 
 	return out
 
@@ -923,39 +1131,51 @@
 	is_web_item = doc.get("published_in_website") or doc.get("published")
 	if doc.has_variants and is_web_item:
 		item_code = doc.item_code
-	elif doc.variant_of and frappe.db.get_value('Item', doc.variant_of, 'published_in_website'):
+	elif doc.variant_of and frappe.db.get_value("Item", doc.variant_of, "published_in_website"):
 		item_code = doc.variant_of
 
 	if item_code:
 		item_cache = ItemVariantsCacheManager(item_code)
 		item_cache.rebuild_cache()
 
+
 def check_stock_uom_with_bin(item, stock_uom):
 	if stock_uom == frappe.db.get_value("Item", item, "stock_uom"):
 		return
 
-	ref_uom = frappe.db.get_value("Stock Ledger Entry",
-							   {"item_code": item}, "stock_uom")
+	ref_uom = frappe.db.get_value("Stock Ledger Entry", {"item_code": item}, "stock_uom")
 
 	if ref_uom:
 		if cstr(ref_uom) != cstr(stock_uom):
-			frappe.throw(_("Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM.").format(item))
+			frappe.throw(
+				_(
+					"Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM."
+				).format(item)
+			)
 
-	bin_list = frappe.db.sql("""
-			select * from tabBin where item_code = %s
+	bin_list = frappe.db.sql(
+		"""
+			select * from `tabBin` where item_code = %s
 				and (reserved_qty > 0 or ordered_qty > 0 or indented_qty > 0 or planned_qty > 0)
 				and stock_uom != %s
-			""", (item, stock_uom), as_dict=1)
+			""",
+		(item, stock_uom),
+		as_dict=1,
+	)
 
 	if bin_list:
-		frappe.throw(_("Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You need to either cancel the linked documents or create a new Item.").format(item))
+		frappe.throw(
+			_(
+				"Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You need to either cancel the linked documents or create a new Item."
+			).format(item)
+		)
 
 	# No SLE or documents against item. Bin UOM can be changed safely.
-	frappe.db.sql("""update tabBin set stock_uom=%s where item_code=%s""", (stock_uom, item))
+	frappe.db.sql("""update `tabBin` set stock_uom=%s where item_code=%s""", (stock_uom, item))
 
 
 def get_item_defaults(item_code, company):
-	item = frappe.get_cached_doc('Item', item_code)
+	item = frappe.get_cached_doc("Item", item_code)
 
 	out = item.as_dict()
 
@@ -966,8 +1186,9 @@
 			out.update(row)
 	return out
 
+
 def set_item_default(item_code, company, fieldname, value):
-	item = frappe.get_cached_doc('Item', item_code)
+	item = frappe.get_cached_doc("Item", item_code)
 
 	for d in item.item_defaults:
 		if d.company == company:
@@ -976,10 +1197,11 @@
 			return
 
 	# no row found, add a new row for the company
-	d = item.append('item_defaults', {fieldname: value, "company": company})
+	d = item.append("item_defaults", {fieldname: value, "company": company})
 	d.db_insert()
 	item.clear_cache()
 
+
 @frappe.whitelist()
 def get_item_details(item_code, company=None):
 	out = frappe._dict()
@@ -991,30 +1213,36 @@
 
 	return out
 
+
 @frappe.whitelist()
 def get_uom_conv_factor(uom, stock_uom):
-	""" Get UOM conversion factor from uom to stock_uom
-		e.g. uom = "Kg", stock_uom = "Gram" then returns 1000.0
+	"""Get UOM conversion factor from uom to stock_uom
+	e.g. uom = "Kg", stock_uom = "Gram" then returns 1000.0
 	"""
 	if uom == stock_uom:
 		return 1.0
 
-	from_uom, to_uom = uom, stock_uom   # renaming for readability
+	from_uom, to_uom = uom, stock_uom  # renaming for readability
 
-	exact_match = frappe.db.get_value("UOM Conversion Factor", {"to_uom": to_uom, "from_uom": from_uom}, ["value"], as_dict=1)
+	exact_match = frappe.db.get_value(
+		"UOM Conversion Factor", {"to_uom": to_uom, "from_uom": from_uom}, ["value"], as_dict=1
+	)
 	if exact_match:
 		return exact_match.value
 
-	inverse_match = frappe.db.get_value("UOM Conversion Factor", {"to_uom": from_uom, "from_uom": to_uom}, ["value"], as_dict=1)
+	inverse_match = frappe.db.get_value(
+		"UOM Conversion Factor", {"to_uom": from_uom, "from_uom": to_uom}, ["value"], as_dict=1
+	)
 	if inverse_match:
 		return 1 / inverse_match.value
 
 	# This attempts to try and get conversion from intermediate UOM.
 	# case:
-	#            g -> mg = 1000
-	#            g -> kg = 0.001
-	# therefore  kg -> mg = 1000  / 0.001 = 1,000,000
-	intermediate_match = frappe.db.sql("""
+	# 			 g -> mg = 1000
+	# 			 g -> kg = 0.001
+	# therefore	 kg -> mg = 1000  / 0.001 = 1,000,000
+	intermediate_match = frappe.db.sql(
+		"""
 			select (first.value / second.value) as value
 			from `tabUOM Conversion Factor` first
 			join `tabUOM Conversion Factor` second
@@ -1023,7 +1251,10 @@
 				first.to_uom = %(to_uom)s
 				and second.to_uom = %(from_uom)s
 			limit 1
-			""", {"to_uom": to_uom, "from_uom": from_uom}, as_dict=1)
+			""",
+		{"to_uom": to_uom, "from_uom": from_uom},
+		as_dict=1,
+	)
 
 	if intermediate_match:
 		return intermediate_match[0].value
@@ -1035,8 +1266,12 @@
 	if not frappe.has_permission("Item"):
 		frappe.throw(_("No Permission"))
 
-	return frappe.get_all("Item Attribute Value", fields = ["attribute_value"],
-		filters = {'parent': parent, 'attribute_value': ("like", f"%{attribute_value}%")})
+	return frappe.get_all(
+		"Item Attribute Value",
+		fields=["attribute_value"],
+		filters={"parent": parent, "attribute_value": ("like", f"%{attribute_value}%")},
+	)
+
 
 def update_variants(variants, template, publish_progress=True):
 	total = len(variants)
@@ -1047,6 +1282,7 @@
 		if publish_progress:
 			frappe.publish_progress(count / total * 100, title=_("Updating Variants..."))
 
+
 @erpnext.allow_regional
 def set_item_tax_from_hsn_code(item):
 	pass
@@ -1055,20 +1291,29 @@
 def validate_item_default_company_links(item_defaults: List[ItemDefault]) -> None:
 	for item_default in item_defaults:
 		for doctype, field in [
-			['Warehouse', 'default_warehouse'],
-			['Cost Center', 'buying_cost_center'],
-			['Cost Center', 'selling_cost_center'],
-			['Account', 'expense_account'],
-			['Account', 'income_account']
+			["Warehouse", "default_warehouse"],
+			["Cost Center", "buying_cost_center"],
+			["Cost Center", "selling_cost_center"],
+			["Account", "expense_account"],
+			["Account", "income_account"],
 		]:
 			if item_default.get(field):
-				company = frappe.db.get_value(doctype, item_default.get(field), 'company', cache=True)
+				company = frappe.db.get_value(doctype, item_default.get(field), "company", cache=True)
 				if company and company != item_default.company:
-					frappe.throw(_("Row #{}: {} {} doesn't belong to Company {}. Please select valid {}.")
-						.format(
+					frappe.throw(
+						_("Row #{}: {} {} doesn't belong to Company {}. Please select valid {}.").format(
 							item_default.idx,
 							doctype,
 							frappe.bold(item_default.get(field)),
 							frappe.bold(item_default.company),
-							frappe.bold(frappe.unscrub(field))
-						), title=_("Invalid Item Defaults"))
+							frappe.bold(frappe.unscrub(field)),
+						),
+						title=_("Invalid Item Defaults"),
+					)
+
+
+@frappe.whitelist()
+def get_asset_naming_series():
+	from erpnext.assets.doctype.asset.asset import get_asset_naming_series
+
+	return get_asset_naming_series()
diff --git a/erpnext/stock/doctype/item/item_dashboard.py b/erpnext/stock/doctype/item/item_dashboard.py
index e16f5bb..897acb7 100644
--- a/erpnext/stock/doctype/item/item_dashboard.py
+++ b/erpnext/stock/doctype/item/item_dashboard.py
@@ -3,45 +3,35 @@
 
 def get_data():
 	return {
-		'heatmap': True,
-		'heatmap_message': _('This is based on stock movement. See {0} for details')\
-			.format('<a href="#query-report/Stock Ledger">' + _('Stock Ledger') + '</a>'),
-		'fieldname': 'item_code',
-		'non_standard_fieldnames': {
-			'Work Order': 'production_item',
-			'Product Bundle': 'new_item_code',
-			'BOM': 'item',
-			'Batch': 'item'
+		"heatmap": True,
+		"heatmap_message": _("This is based on stock movement. See {0} for details").format(
+			'<a href="#query-report/Stock Ledger">' + _("Stock Ledger") + "</a>"
+		),
+		"fieldname": "item_code",
+		"non_standard_fieldnames": {
+			"Work Order": "production_item",
+			"Product Bundle": "new_item_code",
+			"BOM": "item",
+			"Batch": "item",
 		},
-		'transactions': [
+		"transactions": [
+			{"label": _("Groups"), "items": ["BOM", "Product Bundle", "Item Alternative"]},
+			{"label": _("Pricing"), "items": ["Item Price", "Pricing Rule"]},
+			{"label": _("Sell"), "items": ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]},
 			{
-				'label': _('Groups'),
-				'items': ['BOM', 'Product Bundle', 'Item Alternative']
+				"label": _("Buy"),
+				"items": [
+					"Material Request",
+					"Supplier Quotation",
+					"Request for Quotation",
+					"Purchase Order",
+					"Purchase Receipt",
+					"Purchase Invoice",
+				],
 			},
-			{
-				'label': _('Pricing'),
-				'items': ['Item Price', 'Pricing Rule']
-			},
-			{
-				'label': _('Sell'),
-				'items': ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']
-			},
-			{
-				'label': _('Buy'),
-				'items': ['Material Request', 'Supplier Quotation', 'Request for Quotation',
-					'Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
-			},
-			{
-				'label': _('Manufacture'),
-				'items': ['Production Plan', 'Work Order', 'Item Manufacturer']
-			},
-			{
-				'label': _('Traceability'),
-				'items': ['Serial No', 'Batch']
-			},
-			{
-				'label': _('Move'),
-				'items': ['Stock Entry']
-			}
-		]
+			{"label": _("Manufacture"), "items": ["Production Plan", "Work Order", "Item Manufacturer"]},
+			{"label": _("Traceability"), "items": ["Serial No", "Batch"]},
+			{"label": _("Stock Movement"), "items": ["Stock Entry", "Stock Reconciliation"]},
+			{"label": _("E-commerce"), "items": ["Website Item"]},
+		],
 	}
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index d7671b1..3366c73 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -30,17 +30,23 @@
 test_ignore = ["BOM"]
 test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand", "Item Attribute"]
 
-def make_item(item_code, properties=None):
+
+def make_item(item_code=None, properties=None):
+	if not item_code:
+		item_code = frappe.generate_hash(length=16)
+
 	if frappe.db.exists("Item", item_code):
 		return frappe.get_doc("Item", item_code)
 
-	item = frappe.get_doc({
-		"doctype": "Item",
-		"item_code": item_code,
-		"item_name": item_code,
-		"description": item_code,
-		"item_group": "Products"
-	})
+	item = frappe.get_doc(
+		{
+			"doctype": "Item",
+			"item_code": item_code,
+			"item_name": item_code,
+			"description": item_code,
+			"item_group": "Products",
+		}
+	)
 
 	if properties:
 		item.update(properties)
@@ -53,6 +59,7 @@
 
 	return item
 
+
 class TestItem(FrappeTestCase):
 	def setUp(self):
 		super().setUp()
@@ -95,56 +102,91 @@
 		make_test_objects("Item Price")
 
 		company = "_Test Company"
-		currency = frappe.get_cached_value("Company",  company,  "default_currency")
+		currency = frappe.get_cached_value("Company", company, "default_currency")
 
-		details = get_item_details({
-			"item_code": "_Test Item",
-			"company": company,
-			"price_list": "_Test Price List",
-			"currency": currency,
-			"doctype": "Sales Order",
-			"conversion_rate": 1,
-			"price_list_currency": currency,
-			"plc_conversion_rate": 1,
-			"order_type": "Sales",
-			"customer": "_Test Customer",
-			"conversion_factor": 1,
-			"price_list_uom_dependant": 1,
-			"ignore_pricing_rule": 1
-		})
+		details = get_item_details(
+			{
+				"item_code": "_Test Item",
+				"company": company,
+				"price_list": "_Test Price List",
+				"currency": currency,
+				"doctype": "Sales Order",
+				"conversion_rate": 1,
+				"price_list_currency": currency,
+				"plc_conversion_rate": 1,
+				"order_type": "Sales",
+				"customer": "_Test Customer",
+				"conversion_factor": 1,
+				"price_list_uom_dependant": 1,
+				"ignore_pricing_rule": 1,
+			}
+		)
 
 		for key, value in to_check.items():
 			self.assertEqual(value, details.get(key))
 
 	def test_item_tax_template(self):
 		expected_item_tax_template = [
-			{"item_code": "_Test Item With Item Tax Template", "tax_category": "",
-				"item_tax_template": "_Test Account Excise Duty @ 10 - _TC"},
-			{"item_code": "_Test Item With Item Tax Template", "tax_category": "_Test Tax Category 1",
-				"item_tax_template": "_Test Account Excise Duty @ 12 - _TC"},
-			{"item_code": "_Test Item With Item Tax Template", "tax_category": "_Test Tax Category 2",
-				"item_tax_template": None},
-
-			{"item_code": "_Test Item Inherit Group Item Tax Template 1", "tax_category": "",
-				"item_tax_template": "_Test Account Excise Duty @ 10 - _TC"},
-			{"item_code": "_Test Item Inherit Group Item Tax Template 1", "tax_category": "_Test Tax Category 1",
-				"item_tax_template": "_Test Account Excise Duty @ 12 - _TC"},
-			{"item_code": "_Test Item Inherit Group Item Tax Template 1", "tax_category": "_Test Tax Category 2",
-				"item_tax_template": None},
-
-			{"item_code": "_Test Item Inherit Group Item Tax Template 2", "tax_category": "",
-				"item_tax_template": "_Test Account Excise Duty @ 15 - _TC"},
-			{"item_code": "_Test Item Inherit Group Item Tax Template 2", "tax_category": "_Test Tax Category 1",
-				"item_tax_template": "_Test Account Excise Duty @ 12 - _TC"},
-			{"item_code": "_Test Item Inherit Group Item Tax Template 2", "tax_category": "_Test Tax Category 2",
-				"item_tax_template": None},
-
-			{"item_code": "_Test Item Override Group Item Tax Template", "tax_category": "",
-				"item_tax_template": "_Test Account Excise Duty @ 20 - _TC"},
-			{"item_code": "_Test Item Override Group Item Tax Template", "tax_category": "_Test Tax Category 1",
-				"item_tax_template": "_Test Item Tax Template 1 - _TC"},
-			{"item_code": "_Test Item Override Group Item Tax Template", "tax_category": "_Test Tax Category 2",
-				"item_tax_template": None},
+			{
+				"item_code": "_Test Item With Item Tax Template",
+				"tax_category": "",
+				"item_tax_template": "_Test Account Excise Duty @ 10 - _TC",
+			},
+			{
+				"item_code": "_Test Item With Item Tax Template",
+				"tax_category": "_Test Tax Category 1",
+				"item_tax_template": "_Test Account Excise Duty @ 12 - _TC",
+			},
+			{
+				"item_code": "_Test Item With Item Tax Template",
+				"tax_category": "_Test Tax Category 2",
+				"item_tax_template": None,
+			},
+			{
+				"item_code": "_Test Item Inherit Group Item Tax Template 1",
+				"tax_category": "",
+				"item_tax_template": "_Test Account Excise Duty @ 10 - _TC",
+			},
+			{
+				"item_code": "_Test Item Inherit Group Item Tax Template 1",
+				"tax_category": "_Test Tax Category 1",
+				"item_tax_template": "_Test Account Excise Duty @ 12 - _TC",
+			},
+			{
+				"item_code": "_Test Item Inherit Group Item Tax Template 1",
+				"tax_category": "_Test Tax Category 2",
+				"item_tax_template": None,
+			},
+			{
+				"item_code": "_Test Item Inherit Group Item Tax Template 2",
+				"tax_category": "",
+				"item_tax_template": "_Test Account Excise Duty @ 15 - _TC",
+			},
+			{
+				"item_code": "_Test Item Inherit Group Item Tax Template 2",
+				"tax_category": "_Test Tax Category 1",
+				"item_tax_template": "_Test Account Excise Duty @ 12 - _TC",
+			},
+			{
+				"item_code": "_Test Item Inherit Group Item Tax Template 2",
+				"tax_category": "_Test Tax Category 2",
+				"item_tax_template": None,
+			},
+			{
+				"item_code": "_Test Item Override Group Item Tax Template",
+				"tax_category": "",
+				"item_tax_template": "_Test Account Excise Duty @ 20 - _TC",
+			},
+			{
+				"item_code": "_Test Item Override Group Item Tax Template",
+				"tax_category": "_Test Tax Category 1",
+				"item_tax_template": "_Test Item Tax Template 1 - _TC",
+			},
+			{
+				"item_code": "_Test Item Override Group Item Tax Template",
+				"tax_category": "_Test Tax Category 2",
+				"item_tax_template": None,
+			},
 		]
 
 		expected_item_tax_map = {
@@ -153,43 +195,55 @@
 			"_Test Account Excise Duty @ 12 - _TC": {"_Test Account Excise Duty - _TC": 12},
 			"_Test Account Excise Duty @ 15 - _TC": {"_Test Account Excise Duty - _TC": 15},
 			"_Test Account Excise Duty @ 20 - _TC": {"_Test Account Excise Duty - _TC": 20},
-			"_Test Item Tax Template 1 - _TC": {"_Test Account Excise Duty - _TC": 5, "_Test Account Education Cess - _TC": 10,
-				"_Test Account S&H Education Cess - _TC": 15}
+			"_Test Item Tax Template 1 - _TC": {
+				"_Test Account Excise Duty - _TC": 5,
+				"_Test Account Education Cess - _TC": 10,
+				"_Test Account S&H Education Cess - _TC": 15,
+			},
 		}
 
 		for data in expected_item_tax_template:
-			details = get_item_details({
-				"item_code": data['item_code'],
-				"tax_category": data['tax_category'],
-				"company": "_Test Company",
-				"price_list": "_Test Price List",
-				"currency": "_Test Currency",
-				"doctype": "Sales Order",
-				"conversion_rate": 1,
-				"price_list_currency": "_Test Currency",
-				"plc_conversion_rate": 1,
-				"order_type": "Sales",
-				"customer": "_Test Customer",
-				"conversion_factor": 1,
-				"price_list_uom_dependant": 1,
-				"ignore_pricing_rule": 1
-			})
+			details = get_item_details(
+				{
+					"item_code": data["item_code"],
+					"tax_category": data["tax_category"],
+					"company": "_Test Company",
+					"price_list": "_Test Price List",
+					"currency": "_Test Currency",
+					"doctype": "Sales Order",
+					"conversion_rate": 1,
+					"price_list_currency": "_Test Currency",
+					"plc_conversion_rate": 1,
+					"order_type": "Sales",
+					"customer": "_Test Customer",
+					"conversion_factor": 1,
+					"price_list_uom_dependant": 1,
+					"ignore_pricing_rule": 1,
+				}
+			)
 
-			self.assertEqual(details.item_tax_template, data['item_tax_template'])
-			self.assertEqual(json.loads(details.item_tax_rate), expected_item_tax_map[details.item_tax_template])
+			self.assertEqual(details.item_tax_template, data["item_tax_template"])
+			self.assertEqual(
+				json.loads(details.item_tax_rate), expected_item_tax_map[details.item_tax_template]
+			)
 
 	def test_item_defaults(self):
 		frappe.delete_doc_if_exists("Item", "Test Item With Defaults", force=1)
-		make_item("Test Item With Defaults", {
-			"item_group": "_Test Item Group",
-			"brand": "_Test Brand With Item Defaults",
-			"item_defaults": [{
-				"company": "_Test Company",
-				"default_warehouse": "_Test Warehouse 2 - _TC",  # no override
-				"expense_account": "_Test Account Stock Expenses - _TC",  # override brand default
-				"buying_cost_center": "_Test Write Off Cost Center - _TC",  # override item group default
-			}]
-		})
+		make_item(
+			"Test Item With Defaults",
+			{
+				"item_group": "_Test Item Group",
+				"brand": "_Test Brand With Item Defaults",
+				"item_defaults": [
+					{
+						"company": "_Test Company",
+						"default_warehouse": "_Test Warehouse 2 - _TC",  # no override
+						"expense_account": "_Test Account Stock Expenses - _TC",  # override brand default
+						"buying_cost_center": "_Test Write Off Cost Center - _TC",  # override item group default
+					}
+				],
+			},
+		)
 
 		sales_item_check = {
 			"item_code": "Test Item With Defaults",
@@ -198,17 +252,19 @@
 			"expense_account": "_Test Account Stock Expenses - _TC",  # from item
 			"cost_center": "_Test Cost Center 2 - _TC",  # from item group
 		}
-		sales_item_details = get_item_details({
-			"item_code": "Test Item With Defaults",
-			"company": "_Test Company",
-			"price_list": "_Test Price List",
-			"currency": "_Test Currency",
-			"doctype": "Sales Invoice",
-			"conversion_rate": 1,
-			"price_list_currency": "_Test Currency",
-			"plc_conversion_rate": 1,
-			"customer": "_Test Customer",
-		})
+		sales_item_details = get_item_details(
+			{
+				"item_code": "Test Item With Defaults",
+				"company": "_Test Company",
+				"price_list": "_Test Price List",
+				"currency": "_Test Currency",
+				"doctype": "Sales Invoice",
+				"conversion_rate": 1,
+				"price_list_currency": "_Test Currency",
+				"plc_conversion_rate": 1,
+				"customer": "_Test Customer",
+			}
+		)
 		for key, value in sales_item_check.items():
 			self.assertEqual(value, sales_item_details.get(key))
 
@@ -217,38 +273,47 @@
 			"warehouse": "_Test Warehouse 2 - _TC",  # from item
 			"expense_account": "_Test Account Stock Expenses - _TC",  # from item
 			"income_account": "_Test Account Sales - _TC",  # from brand
-			"cost_center": "_Test Write Off Cost Center - _TC"  # from item
+			"cost_center": "_Test Write Off Cost Center - _TC",  # from item
 		}
-		purchase_item_details = get_item_details({
-			"item_code": "Test Item With Defaults",
-			"company": "_Test Company",
-			"price_list": "_Test Price List",
-			"currency": "_Test Currency",
-			"doctype": "Purchase Invoice",
-			"conversion_rate": 1,
-			"price_list_currency": "_Test Currency",
-			"plc_conversion_rate": 1,
-			"supplier": "_Test Supplier",
-		})
+		purchase_item_details = get_item_details(
+			{
+				"item_code": "Test Item With Defaults",
+				"company": "_Test Company",
+				"price_list": "_Test Price List",
+				"currency": "_Test Currency",
+				"doctype": "Purchase Invoice",
+				"conversion_rate": 1,
+				"price_list_currency": "_Test Currency",
+				"plc_conversion_rate": 1,
+				"supplier": "_Test Supplier",
+			}
+		)
 		for key, value in purchase_item_check.items():
 			self.assertEqual(value, purchase_item_details.get(key))
 
 	def test_item_default_validations(self):
 
 		with self.assertRaises(frappe.ValidationError) as ve:
-			make_item("Bad Item defaults", {
-				"item_group": "_Test Item Group",
-				"item_defaults": [{
-					"company": "_Test Company 1",
-					"default_warehouse": "_Test Warehouse - _TC",
-					"expense_account": "Stock In Hand - _TC",
-					"buying_cost_center": "_Test Cost Center - _TC",
-					"selling_cost_center": "_Test Cost Center - _TC",
-				}]
-			})
+			make_item(
+				"Bad Item defaults",
+				{
+					"item_group": "_Test Item Group",
+					"item_defaults": [
+						{
+							"company": "_Test Company 1",
+							"default_warehouse": "_Test Warehouse - _TC",
+							"expense_account": "Stock In Hand - _TC",
+							"buying_cost_center": "_Test Cost Center - _TC",
+							"selling_cost_center": "_Test Cost Center - _TC",
+						}
+					],
+				},
+			)
 
-		self.assertTrue("belong to company" in str(ve.exception).lower(),
-				msg="Mismatching company entities in item defaults should not be allowed.")
+		self.assertTrue(
+			"belong to company" in str(ve.exception).lower(),
+			msg="Mismatching company entities in item defaults should not be allowed.",
+		)
 
 	def test_item_attribute_change_after_variant(self):
 		frappe.delete_doc_if_exists("Item", "_Test Variant Item-L", force=1)
@@ -256,7 +321,7 @@
 		variant = create_variant("_Test Variant Item", {"Test Size": "Large"})
 		variant.save()
 
-		attribute = frappe.get_doc('Item Attribute', 'Test Size')
+		attribute = frappe.get_doc("Item Attribute", "Test Size")
 		attribute.item_attribute_values = []
 
 		# reset flags
@@ -279,20 +344,18 @@
 	def test_copy_fields_from_template_to_variants(self):
 		frappe.delete_doc_if_exists("Item", "_Test Variant Item-XL", force=1)
 
-		fields = [{'field_name': 'item_group'}, {'field_name': 'is_stock_item'}]
-		allow_fields = [d.get('field_name') for d in fields]
+		fields = [{"field_name": "item_group"}, {"field_name": "is_stock_item"}]
+		allow_fields = [d.get("field_name") for d in fields]
 		set_item_variant_settings(fields)
 
-		if not frappe.db.get_value('Item Attribute Value',
-			{'parent': 'Test Size', 'attribute_value': 'Extra Large'}, 'name'):
-			item_attribute = frappe.get_doc('Item Attribute', 'Test Size')
-			item_attribute.append('item_attribute_values', {
-				'attribute_value' : 'Extra Large',
-				'abbr': 'XL'
-			})
+		if not frappe.db.get_value(
+			"Item Attribute Value", {"parent": "Test Size", "attribute_value": "Extra Large"}, "name"
+		):
+			item_attribute = frappe.get_doc("Item Attribute", "Test Size")
+			item_attribute.append("item_attribute_values", {"attribute_value": "Extra Large", "abbr": "XL"})
 			item_attribute.save()
 
-		template = frappe.get_doc('Item', '_Test Variant Item')
+		template = frappe.get_doc("Item", "_Test Variant Item")
 		template.item_group = "_Test Item Group D"
 		template.save()
 
@@ -301,93 +364,94 @@
 		variant.item_name = "_Test Variant Item-XL"
 		variant.save()
 
-		variant = frappe.get_doc('Item', '_Test Variant Item-XL')
+		variant = frappe.get_doc("Item", "_Test Variant Item-XL")
 		for fieldname in allow_fields:
 			self.assertEqual(template.get(fieldname), variant.get(fieldname))
 
-		template = frappe.get_doc('Item', '_Test Variant Item')
+		template = frappe.get_doc("Item", "_Test Variant Item")
 		template.item_group = "_Test Item Group Desktops"
 		template.save()
 
 	def test_make_item_variant_with_numeric_values(self):
 		# cleanup
-		for d in frappe.db.get_all('Item', filters={'variant_of':
-				'_Test Numeric Template Item'}):
+		for d in frappe.db.get_all("Item", filters={"variant_of": "_Test Numeric Template Item"}):
 			frappe.delete_doc_if_exists("Item", d.name)
 
 		frappe.delete_doc_if_exists("Item", "_Test Numeric Template Item")
 		frappe.delete_doc_if_exists("Item Attribute", "Test Item Length")
 
-		frappe.db.sql('''delete from `tabItem Variant Attribute`
-			where attribute="Test Item Length"''')
+		frappe.db.sql(
+			"""delete from `tabItem Variant Attribute`
+			where attribute='Test Item Length' """
+		)
 
 		frappe.flags.attribute_values = None
 
 		# make item attribute
-		frappe.get_doc({
-			"doctype": "Item Attribute",
-			"attribute_name": "Test Item Length",
-			"numeric_values": 1,
-			"from_range": 0.0,
-			"to_range": 100.0,
-			"increment": 0.5
-		}).insert()
+		frappe.get_doc(
+			{
+				"doctype": "Item Attribute",
+				"attribute_name": "Test Item Length",
+				"numeric_values": 1,
+				"from_range": 0.0,
+				"to_range": 100.0,
+				"increment": 0.5,
+			}
+		).insert()
 
 		# make template item
-		make_item("_Test Numeric Template Item", {
-			"attributes": [
-				{
-					"attribute": "Test Size"
-				},
-				{
-					"attribute": "Test Item Length",
-					"numeric_values": 1,
-					"from_range": 0.0,
-					"to_range": 100.0,
-					"increment": 0.5
-				}
-			],
-			"item_defaults": [
-				{
-					"default_warehouse": "_Test Warehouse - _TC",
-					"company": "_Test Company"
-				}
-			],
-			"has_variants": 1
-		})
+		make_item(
+			"_Test Numeric Template Item",
+			{
+				"attributes": [
+					{"attribute": "Test Size"},
+					{
+						"attribute": "Test Item Length",
+						"numeric_values": 1,
+						"from_range": 0.0,
+						"to_range": 100.0,
+						"increment": 0.5,
+					},
+				],
+				"item_defaults": [{"default_warehouse": "_Test Warehouse - _TC", "company": "_Test Company"}],
+				"has_variants": 1,
+			},
+		)
 
-		variant = create_variant("_Test Numeric Template Item",
-			{"Test Size": "Large", "Test Item Length": 1.1})
+		variant = create_variant(
+			"_Test Numeric Template Item", {"Test Size": "Large", "Test Item Length": 1.1}
+		)
 		self.assertEqual(variant.item_code, "_Test Numeric Template Item-L-1.1")
 		variant.item_code = "_Test Numeric Variant-L-1.1"
 		variant.item_name = "_Test Numeric Variant Large 1.1m"
 		self.assertRaises(InvalidItemAttributeValueError, variant.save)
 
-		variant = create_variant("_Test Numeric Template Item",
-			{"Test Size": "Large", "Test Item Length": 1.5})
+		variant = create_variant(
+			"_Test Numeric Template Item", {"Test Size": "Large", "Test Item Length": 1.5}
+		)
 		self.assertEqual(variant.item_code, "_Test Numeric Template Item-L-1.5")
 		variant.item_code = "_Test Numeric Variant-L-1.5"
 		variant.item_name = "_Test Numeric Variant Large 1.5m"
 		variant.save()
 
 	def test_item_merging(self):
-		create_item("Test Item for Merging 1")
-		create_item("Test Item for Merging 2")
+		old = create_item(frappe.generate_hash(length=20)).name
+		new = create_item(frappe.generate_hash(length=20)).name
 
-		make_stock_entry(item_code="Test Item for Merging 1", target="_Test Warehouse - _TC",
-			qty=1, rate=100)
-		make_stock_entry(item_code="Test Item for Merging 2", target="_Test Warehouse 1 - _TC",
-			qty=1, rate=100)
+		make_stock_entry(item_code=old, target="_Test Warehouse - _TC", qty=1, rate=100)
+		make_stock_entry(item_code=old, target="_Test Warehouse 1 - _TC", qty=1, rate=100)
+		make_stock_entry(item_code=new, target="_Test Warehouse 1 - _TC", qty=1, rate=100)
 
-		frappe.rename_doc("Item", "Test Item for Merging 1", "Test Item for Merging 2", merge=True)
+		frappe.rename_doc("Item", old, new, merge=True)
 
-		self.assertFalse(frappe.db.exists("Item", "Test Item for Merging 1"))
+		self.assertFalse(frappe.db.exists("Item", old))
 
-		self.assertTrue(frappe.db.get_value("Bin",
-			{"item_code": "Test Item for Merging 2", "warehouse": "_Test Warehouse - _TC"}))
-
-		self.assertTrue(frappe.db.get_value("Bin",
-			{"item_code": "Test Item for Merging 2", "warehouse": "_Test Warehouse 1 - _TC"}))
+		self.assertTrue(
+			frappe.db.get_value("Bin", {"item_code": new, "warehouse": "_Test Warehouse - _TC"})
+		)
+		self.assertTrue(
+			frappe.db.get_value("Bin", {"item_code": new, "warehouse": "_Test Warehouse 1 - _TC"})
+		)
 
 	def test_item_merging_with_product_bundle(self):
 		from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
@@ -410,13 +474,12 @@
 		self.assertFalse(frappe.db.exists("Item", "Test Item Bundle Item 1"))
 
 	def test_uom_conversion_factor(self):
-		if frappe.db.exists('Item', 'Test Item UOM'):
-			frappe.delete_doc('Item', 'Test Item UOM')
+		if frappe.db.exists("Item", "Test Item UOM"):
+			frappe.delete_doc("Item", "Test Item UOM")
 
-		item_doc = make_item("Test Item UOM", {
-			"stock_uom": "Gram",
-			"uoms": [dict(uom='Carat'), dict(uom='Kg')]
-		})
+		item_doc = make_item(
+			"Test Item UOM", {"stock_uom": "Gram", "uoms": [dict(uom="Carat"), dict(uom="Kg")]}
+		)
 
 		for d in item_doc.uoms:
 			value = get_uom_conv_factor(d.uom, item_doc.stock_uom)
@@ -436,48 +499,46 @@
 		self.assertEqual(factor, 1.0)
 
 	def test_item_variant_by_manufacturer(self):
-		fields = [{'field_name': 'description'}, {'field_name': 'variant_based_on'}]
+		fields = [{"field_name": "description"}, {"field_name": "variant_based_on"}]
 		set_item_variant_settings(fields)
 
-		if frappe.db.exists('Item', '_Test Variant Mfg'):
-			frappe.delete_doc('Item', '_Test Variant Mfg')
-		if frappe.db.exists('Item', '_Test Variant Mfg-1'):
-			frappe.delete_doc('Item', '_Test Variant Mfg-1')
-		if frappe.db.exists('Manufacturer', 'MSG1'):
-			frappe.delete_doc('Manufacturer', 'MSG1')
+		if frappe.db.exists("Item", "_Test Variant Mfg"):
+			frappe.delete_doc("Item", "_Test Variant Mfg")
+		if frappe.db.exists("Item", "_Test Variant Mfg-1"):
+			frappe.delete_doc("Item", "_Test Variant Mfg-1")
+		if frappe.db.exists("Manufacturer", "MSG1"):
+			frappe.delete_doc("Manufacturer", "MSG1")
 
-		template = frappe.get_doc(dict(
-			doctype='Item',
-			item_code='_Test Variant Mfg',
-			has_variant=1,
-			item_group='Products',
-			variant_based_on='Manufacturer'
-		)).insert()
+		template = frappe.get_doc(
+			dict(
+				doctype="Item",
+				item_code="_Test Variant Mfg",
+				has_variant=1,
+				item_group="Products",
+				variant_based_on="Manufacturer",
+			)
+		).insert()
 
-		manufacturer = frappe.get_doc(dict(
-			doctype='Manufacturer',
-			short_name='MSG1'
-		)).insert()
+		manufacturer = frappe.get_doc(dict(doctype="Manufacturer", short_name="MSG1")).insert()
 
 		variant = get_variant(template.name, manufacturer=manufacturer.name)
-		self.assertEqual(variant.item_code, '_Test Variant Mfg-1')
-		self.assertEqual(variant.description, '_Test Variant Mfg')
-		self.assertEqual(variant.manufacturer, 'MSG1')
+		self.assertEqual(variant.item_code, "_Test Variant Mfg-1")
+		self.assertEqual(variant.description, "_Test Variant Mfg")
+		self.assertEqual(variant.manufacturer, "MSG1")
 		variant.insert()
 
-		variant = get_variant(template.name, manufacturer=manufacturer.name,
-			manufacturer_part_no='007')
-		self.assertEqual(variant.item_code, '_Test Variant Mfg-2')
-		self.assertEqual(variant.description, '_Test Variant Mfg')
-		self.assertEqual(variant.manufacturer, 'MSG1')
-		self.assertEqual(variant.manufacturer_part_no, '007')
+		variant = get_variant(template.name, manufacturer=manufacturer.name, manufacturer_part_no="007")
+		self.assertEqual(variant.item_code, "_Test Variant Mfg-2")
+		self.assertEqual(variant.description, "_Test Variant Mfg")
+		self.assertEqual(variant.manufacturer, "MSG1")
+		self.assertEqual(variant.manufacturer_part_no, "007")
 
 	def test_stock_exists_against_template_item(self):
-		stock_item = frappe.get_all('Stock Ledger Entry', fields = ["item_code"], limit=1)
+		stock_item = frappe.get_all("Stock Ledger Entry", fields=["item_code"], limit=1)
 		if stock_item:
 			item_code = stock_item[0].item_code
 
-			item_doc = frappe.get_doc('Item', item_code)
+			item_doc = frappe.get_doc("Item", item_code)
 			item_doc.has_variants = 1
 			self.assertRaises(StockExistsForTemplate, item_doc.save)
 
@@ -490,37 +551,27 @@
 
 		# Create new item and add barcodes
 		barcode_properties_list = [
-			{
-				"barcode": "0012345678905",
-				"barcode_type": "EAN"
-			},
-			{
-				"barcode": "012345678905",
-				"barcode_type": "UAN"
-			},
+			{"barcode": "0012345678905", "barcode_type": "EAN"},
+			{"barcode": "012345678905", "barcode_type": "UAN"},
 			{
 				"barcode": "ARBITRARY_TEXT",
-			}
+			},
 		]
 		create_item(item_code)
 		for barcode_properties in barcode_properties_list:
-			item_doc = frappe.get_doc('Item', item_code)
-			new_barcode = item_doc.append('barcodes')
+			item_doc = frappe.get_doc("Item", item_code)
+			new_barcode = item_doc.append("barcodes")
 			new_barcode.update(barcode_properties)
 			item_doc.save()
 
 		# Check values saved correctly
 		barcodes = frappe.get_all(
-			'Item Barcode',
-			fields=['barcode', 'barcode_type'],
-			filters={'parent': item_code})
+			"Item Barcode", fields=["barcode", "barcode_type"], filters={"parent": item_code}
+		)
 
 		for barcode_properties in barcode_properties_list:
-			barcode_to_find = barcode_properties['barcode']
-			matching_barcodes = [
-				x for x in barcodes
-				if x['barcode'] == barcode_to_find
-			]
+			barcode_to_find = barcode_properties["barcode"]
+			matching_barcodes = [x for x in barcodes if x["barcode"] == barcode_to_find]
 		self.assertEqual(len(matching_barcodes), 1)
 		details = matching_barcodes[0]
 
@@ -528,20 +579,21 @@
 			self.assertEqual(value, details.get(key))
 
 		# Add barcode again - should cause DuplicateEntryError
-		item_doc = frappe.get_doc('Item', item_code)
-		new_barcode = item_doc.append('barcodes')
+		item_doc = frappe.get_doc("Item", item_code)
+		new_barcode = item_doc.append("barcodes")
 		new_barcode.update(barcode_properties_list[0])
 		self.assertRaises(frappe.UniqueValidationError, item_doc.save)
 
 		# Add invalid barcode - should cause InvalidBarcode
-		item_doc = frappe.get_doc('Item', item_code)
-		new_barcode = item_doc.append('barcodes')
-		new_barcode.barcode = '9999999999999'
-		new_barcode.barcode_type = 'EAN'
+		item_doc = frappe.get_doc("Item", item_code)
+		new_barcode = item_doc.append("barcodes")
+		new_barcode.barcode = "9999999999999"
+		new_barcode.barcode_type = "EAN"
 		self.assertRaises(InvalidBarcode, item_doc.save)
 
 	def test_heatmap_data(self):
 		import time
+
 		data = get_timeline_data("Item", "_Test Item")
 		self.assertTrue(isinstance(data, dict))
 
@@ -584,20 +636,17 @@
 
 	def test_check_stock_uom_with_bin_no_sle(self):
 		from erpnext.stock.stock_balance import update_bin_qty
+
 		item = create_item("_Item with bin qty")
 		item.stock_uom = "Gram"
 		item.save()
 
-		update_bin_qty(item.item_code, "_Test Warehouse - _TC", {
-			"reserved_qty": 10
-		})
+		update_bin_qty(item.item_code, "_Test Warehouse - _TC", {"reserved_qty": 10})
 
 		item.stock_uom = "Kilometer"
 		self.assertRaises(frappe.ValidationError, item.save)
 
-		update_bin_qty(item.item_code, "_Test Warehouse - _TC", {
-			"reserved_qty": 0
-		})
+		update_bin_qty(item.item_code, "_Test Warehouse - _TC", {"reserved_qty": 0})
 
 		item.load_from_db()
 		item.stock_uom = "Kilometer"
@@ -632,7 +681,7 @@
 
 	@change_settings("Stock Settings", {"allow_negative_stock": 0})
 	def test_item_wise_negative_stock(self):
-		""" When global settings are disabled check that item that allows
+		"""When global settings are disabled check that item that allows
 		negative stock can still consume material in all known stock
 		transactions that consume inventory."""
 		from erpnext.stock.stock_ledger import is_negative_stock_allowed
@@ -644,16 +693,36 @@
 
 	@change_settings("Stock Settings", {"allow_negative_stock": 0})
 	def test_backdated_negative_stock(self):
-		""" same as test above but backdated entries """
+		"""same as test above but backdated entries"""
 		from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+
 		item = make_item("_TestNegativeItemSetting", {"allow_negative_stock": 1, "valuation_rate": 100})
 
 		# create a future entry so all new entries are backdated
-		make_stock_entry(qty=1, item_code=item.name, target="_Test Warehouse - _TC", posting_date = add_days(today(), 5))
+		make_stock_entry(
+			qty=1, item_code=item.name, target="_Test Warehouse - _TC", posting_date=add_days(today(), 5)
+		)
 		self.consume_item_code_with_differet_stock_transactions(item_code=item.name)
 
+	@change_settings("Stock Settings", {"sample_retention_warehouse": "_Test Warehouse - _TC"})
+	def test_retain_sample(self):
+		item = make_item(
+			"_TestRetainSample", {"has_batch_no": 1, "retain_sample": 1, "sample_quantity": 1}
+		)
 
-	def consume_item_code_with_differet_stock_transactions(self, item_code, warehouse="_Test Warehouse - _TC"):
+		self.assertEqual(item.has_batch_no, 1)
+		self.assertEqual(item.retain_sample, 1)
+		self.assertEqual(item.sample_quantity, 1)
+
+		item.has_batch_no = None
+		item.save()
+		self.assertEqual(item.retain_sample, None)
+		self.assertEqual(item.sample_quantity, None)
+		item.delete()
+
+	def consume_item_code_with_differet_stock_transactions(
+		self, item_code, warehouse="_Test Warehouse - _TC"
+	):
 		from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
 		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
 		from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
@@ -668,13 +737,54 @@
 		# standalone return
 		make_purchase_receipt(is_return=True, qty=-1, **typical_args)
 
+	def test_item_dashboard(self):
+		from erpnext.stock.dashboard.item_dashboard import get_data
+
+		self.assertTrue(get_data(item_code="_Test Item"))
+		self.assertTrue(get_data(warehouse="_Test Warehouse - _TC"))
+		self.assertTrue(get_data(item_group="All Item Groups"))
+
+	def test_empty_description(self):
+		item = make_item(properties={"description": "<p></p>"})
+		self.assertEqual(item.description, item.item_name)
+		item.description = ""
+		item.save()
+		self.assertEqual(item.description, item.item_name)
+
+	def test_item_type_field_change(self):
+		"""Check if critical fields like `is_stock_item`, `has_batch_no` are not changed if transactions exist."""
+		from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+		from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+		from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+
+		transaction_creators = [
+			lambda i: make_purchase_receipt(item_code=i),
+			lambda i: make_purchase_invoice(item_code=i, update_stock=1),
+			lambda i: make_stock_entry(item_code=i, qty=1, target="_Test Warehouse - _TC"),
+			lambda i: create_delivery_note(item_code=i),
+		]
+
+		properties = {"has_batch_no": 0, "allow_negative_stock": 1, "valuation_rate": 10}
+		for transaction_creator in transaction_creators:
+			item = make_item(properties=properties)
+			transaction = transaction_creator(item.name)
+			item.has_batch_no = 1
+			self.assertRaises(frappe.ValidationError, item.save)
+
+			transaction.cancel()
+			# should be allowed now
+			item.reload()
+			item.has_batch_no = 1
+			item.save()
 
 
 def set_item_variant_settings(fields):
-	doc = frappe.get_doc('Item Variant Settings')
-	doc.set('fields', fields)
+	doc = frappe.get_doc("Item Variant Settings")
+	doc.set("fields", fields)
 	doc.save()
 
+
 def make_item_variant():
 	if not frappe.db.exists("Item", "_Test Variant Item-S"):
 		variant = create_variant("_Test Variant Item", """{"Test Size": "Small"}""")
@@ -682,17 +792,31 @@
 		variant.item_name = "_Test Variant Item-S"
 		variant.save()
 
-test_records = frappe.get_test_records('Item')
 
-def create_item(item_code, is_stock_item=1, valuation_rate=0, warehouse="_Test Warehouse - _TC",
-		is_customer_provided_item=None, customer=None, is_purchase_item=None, opening_stock=0, is_fixed_asset=0,
-		asset_category=None, company="_Test Company"):
+test_records = frappe.get_test_records("Item")
+
+
+def create_item(
+	item_code,
+	is_stock_item=1,
+	valuation_rate=0,
+	stock_uom="Nos",
+	warehouse="_Test Warehouse - _TC",
+	is_customer_provided_item=None,
+	customer=None,
+	is_purchase_item=None,
+	opening_stock=0,
+	is_fixed_asset=0,
+	asset_category=None,
+	company="_Test Company",
+):
 	if not frappe.db.exists("Item", item_code):
 		item = frappe.new_doc("Item")
 		item.item_code = item_code
 		item.item_name = item_code
 		item.description = item_code
 		item.item_group = "All Item Groups"
+		item.stock_uom = stock_uom
 		item.is_stock_item = is_stock_item
 		item.is_fixed_asset = is_fixed_asset
 		item.asset_category = asset_category
@@ -700,11 +824,8 @@
 		item.valuation_rate = valuation_rate
 		item.is_purchase_item = is_purchase_item
 		item.is_customer_provided_item = is_customer_provided_item
-		item.customer = customer or ''
-		item.append("item_defaults", {
-			"default_warehouse": warehouse,
-			"company": company
-		})
+		item.customer = customer or ""
+		item.append("item_defaults", {"default_warehouse": warehouse, "company": company})
 		item.save()
 	else:
 		item = frappe.get_doc("Item", item_code)
diff --git a/erpnext/stock/doctype/item_alternative/item_alternative.py b/erpnext/stock/doctype/item_alternative/item_alternative.py
index 766647b..fb1a28d 100644
--- a/erpnext/stock/doctype/item_alternative/item_alternative.py
+++ b/erpnext/stock/doctype/item_alternative/item_alternative.py
@@ -14,8 +14,7 @@
 		self.validate_duplicate()
 
 	def has_alternative_item(self):
-		if (self.item_code and
-			not frappe.db.get_value('Item', self.item_code, 'allow_alternative_item')):
+		if self.item_code and not frappe.db.get_value("Item", self.item_code, "allow_alternative_item"):
 			frappe.throw(_("Not allow to set alternative item for the item {0}").format(self.item_code))
 
 	def validate_alternative_item(self):
@@ -23,19 +22,32 @@
 			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", "allow_alternative_item"]
+		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)
+		alternative_item_data = frappe.db.get_value(
+			"Item", self.alternative_item_code, fields, as_dict=1
+		)
 
 		for field in fields:
-			if  item_data.get(field) != alternative_item_data.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, indicator="Orange")
+				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,
+					indicator="Orange",
+				)
 
 		alternate_item_check_msg = _("Allow Alternative Item must be checked on Item {}")
 
@@ -44,24 +56,30 @@
 		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,
-			'alternative_item_code': self.alternative_item_code, 'name': ('!=', self.name)}):
+		if frappe.db.get_value(
+			"Item Alternative",
+			{
+				"item_code": self.item_code,
+				"alternative_item_code": self.alternative_item_code,
+				"name": ("!=", self.name),
+			},
+		):
 			frappe.throw(_("Already record exists for the item {0}").format(self.item_code))
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_alternative_items(doctype, txt, searchfield, start, page_len, filters):
-	return frappe.db.sql(""" (select alternative_item_code from `tabItem Alternative`
+	return frappe.db.sql(
+		""" (select alternative_item_code from `tabItem Alternative`
 			where item_code = %(item_code)s and alternative_item_code like %(txt)s)
 		union
 			(select item_code from `tabItem Alternative`
 			where alternative_item_code = %(item_code)s and item_code like %(txt)s
-			and two_way = 1) limit {0}, {1}
-		""".format(start, page_len), {
-			"item_code": filters.get('item_code'),
-			"txt": '%' + txt + '%'
-		})
+			and two_way = 1) limit {1} offset {0}
+		""".format(
+			start, page_len
+		),
+		{"item_code": filters.get("item_code"), "txt": "%" + txt + "%"},
+	)
diff --git a/erpnext/stock/doctype/item_alternative/test_item_alternative.py b/erpnext/stock/doctype/item_alternative/test_item_alternative.py
index 501c1c1..b8f4803 100644
--- a/erpnext/stock/doctype/item_alternative/test_item_alternative.py
+++ b/erpnext/stock/doctype/item_alternative/test_item_alternative.py
@@ -16,6 +16,9 @@
 from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
 from erpnext.manufacturing.doctype.work_order.work_order import make_stock_entry
 from erpnext.stock.doctype.item.test_item import create_item
+from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
+	EmptyStockReconciliationItemsError,
+)
 from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
 	create_stock_reconciliation,
 )
@@ -27,121 +30,184 @@
 		make_items()
 
 	def test_alternative_item_for_subcontract_rm(self):
-		frappe.db.set_value('Buying Settings', None,
-			'backflush_raw_materials_of_subcontract_based_on', 'BOM')
+		frappe.db.set_value(
+			"Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", "BOM"
+		)
 
-		create_stock_reconciliation(item_code='Alternate Item For A RW 1', warehouse='_Test Warehouse - _TC',
-			qty=5, rate=2000)
-		create_stock_reconciliation(item_code='Test FG A RW 2', warehouse='_Test Warehouse - _TC',
-			qty=5, rate=2000)
+		create_stock_reconciliation(
+			item_code="Alternate Item For A RW 1", warehouse="_Test Warehouse - _TC", qty=5, rate=2000
+		)
+		create_stock_reconciliation(
+			item_code="Test FG A RW 2", warehouse="_Test Warehouse - _TC", qty=5, rate=2000
+		)
 
 		supplier_warehouse = "Test Supplier Warehouse - _TC"
-		po = create_purchase_order(item = "Test Finished Goods - A",
-			is_subcontracted='Yes', qty=5, rate=3000, supplier_warehouse=supplier_warehouse)
+		po = create_purchase_order(
+			item="Test Finished Goods - A",
+			is_subcontracted=1,
+			qty=5,
+			rate=3000,
+			supplier_warehouse=supplier_warehouse,
+		)
 
-		rm_item = [{"item_code": "Test Finished Goods - A", "rm_item_code": "Test FG A RW 1", "item_name":"Test FG A RW 1",
-					"qty":5, "warehouse":"_Test Warehouse - _TC", "rate":2000, "amount":10000, "stock_uom":"Nos"},
-					{"item_code": "Test Finished Goods - A", "rm_item_code": "Test FG A RW 2", "item_name":"Test FG A RW 2",
-					"qty":5, "warehouse":"_Test Warehouse - _TC", "rate":2000, "amount":10000, "stock_uom":"Nos"}]
+		rm_item = [
+			{
+				"item_code": "Test Finished Goods - A",
+				"rm_item_code": "Test FG A RW 1",
+				"item_name": "Test FG A RW 1",
+				"qty": 5,
+				"warehouse": "_Test Warehouse - _TC",
+				"rate": 2000,
+				"amount": 10000,
+				"stock_uom": "Nos",
+			},
+			{
+				"item_code": "Test Finished Goods - A",
+				"rm_item_code": "Test FG A RW 2",
+				"item_name": "Test FG A RW 2",
+				"qty": 5,
+				"warehouse": "_Test Warehouse - _TC",
+				"rate": 2000,
+				"amount": 10000,
+				"stock_uom": "Nos",
+			},
+		]
 
 		rm_item_string = json.dumps(rm_item)
-		reserved_qty_for_sub_contract = frappe.db.get_value('Bin',
-			{'item_code': 'Test FG A RW 1', 'warehouse': '_Test Warehouse - _TC'}, 'reserved_qty_for_sub_contract')
+		reserved_qty_for_sub_contract = frappe.db.get_value(
+			"Bin",
+			{"item_code": "Test FG A RW 1", "warehouse": "_Test Warehouse - _TC"},
+			"reserved_qty_for_sub_contract",
+		)
 
 		se = frappe.get_doc(make_rm_stock_entry(po.name, rm_item_string))
 		se.to_warehouse = supplier_warehouse
 		se.insert()
 
-		doc = frappe.get_doc('Stock Entry', se.name)
+		doc = frappe.get_doc("Stock Entry", se.name)
 		for item in doc.items:
-			if item.item_code == 'Test FG A RW 1':
-				item.item_code = 'Alternate Item For A RW 1'
-				item.item_name = 'Alternate Item For A RW 1'
-				item.description = 'Alternate Item For A RW 1'
-				item.original_item = 'Test FG A RW 1'
+			if item.item_code == "Test FG A RW 1":
+				item.item_code = "Alternate Item For A RW 1"
+				item.item_name = "Alternate Item For A RW 1"
+				item.description = "Alternate Item For A RW 1"
+				item.original_item = "Test FG A RW 1"
 
 		doc.save()
 		doc.submit()
-		after_transfer_reserved_qty_for_sub_contract = frappe.db.get_value('Bin',
-			{'item_code': 'Test FG A RW 1', 'warehouse': '_Test Warehouse - _TC'}, 'reserved_qty_for_sub_contract')
+		after_transfer_reserved_qty_for_sub_contract = frappe.db.get_value(
+			"Bin",
+			{"item_code": "Test FG A RW 1", "warehouse": "_Test Warehouse - _TC"},
+			"reserved_qty_for_sub_contract",
+		)
 
-		self.assertEqual(after_transfer_reserved_qty_for_sub_contract, flt(reserved_qty_for_sub_contract - 5))
+		self.assertEqual(
+			after_transfer_reserved_qty_for_sub_contract, flt(reserved_qty_for_sub_contract - 5)
+		)
 
 		pr = make_purchase_receipt(po.name)
 		pr.save()
 
-		pr = frappe.get_doc('Purchase Receipt', pr.name)
+		pr = frappe.get_doc("Purchase Receipt", pr.name)
 		status = False
 		for d in pr.supplied_items:
-			if d.rm_item_code == 'Alternate Item For A RW 1':
+			if d.rm_item_code == "Alternate Item For A RW 1":
 				status = True
 
 		self.assertEqual(status, True)
-		frappe.db.set_value('Buying Settings', None,
-			'backflush_raw_materials_of_subcontract_based_on', 'Material Transferred for Subcontract')
+		frappe.db.set_value(
+			"Buying Settings",
+			None,
+			"backflush_raw_materials_of_subcontract_based_on",
+			"Material Transferred for Subcontract",
+		)
 
 	def test_alternative_item_for_production_rm(self):
-		create_stock_reconciliation(item_code='Alternate Item For A RW 1',
-			warehouse='_Test Warehouse - _TC',qty=5, rate=2000)
-		create_stock_reconciliation(item_code='Test FG A RW 2', warehouse='_Test Warehouse - _TC',
-			qty=5, rate=2000)
-		pro_order = make_wo_order_test_record(production_item='Test Finished Goods - A',
-			qty=5, source_warehouse='_Test Warehouse - _TC', wip_warehouse='Test Supplier Warehouse - _TC')
+		create_stock_reconciliation(
+			item_code="Alternate Item For A RW 1", warehouse="_Test Warehouse - _TC", qty=5, rate=2000
+		)
+		create_stock_reconciliation(
+			item_code="Test FG A RW 2", warehouse="_Test Warehouse - _TC", qty=5, rate=2000
+		)
+		pro_order = make_wo_order_test_record(
+			production_item="Test Finished Goods - A",
+			qty=5,
+			source_warehouse="_Test Warehouse - _TC",
+			wip_warehouse="Test Supplier Warehouse - _TC",
+		)
 
-		reserved_qty_for_production = frappe.db.get_value('Bin',
-			{'item_code': 'Test FG A RW 1', 'warehouse': '_Test Warehouse - _TC'}, 'reserved_qty_for_production')
+		reserved_qty_for_production = frappe.db.get_value(
+			"Bin",
+			{"item_code": "Test FG A RW 1", "warehouse": "_Test Warehouse - _TC"},
+			"reserved_qty_for_production",
+		)
 
 		ste = frappe.get_doc(make_stock_entry(pro_order.name, "Material Transfer for Manufacture", 5))
 		ste.insert()
 
 		for item in ste.items:
-			if item.item_code == 'Test FG A RW 1':
-				item.item_code = 'Alternate Item For A RW 1'
-				item.item_name = 'Alternate Item For A RW 1'
-				item.description = 'Alternate Item For A RW 1'
-				item.original_item = 'Test FG A RW 1'
+			if item.item_code == "Test FG A RW 1":
+				item.item_code = "Alternate Item For A RW 1"
+				item.item_name = "Alternate Item For A RW 1"
+				item.description = "Alternate Item For A RW 1"
+				item.original_item = "Test FG A RW 1"
 
 		ste.submit()
-		reserved_qty_for_production_after_transfer = frappe.db.get_value('Bin',
-			{'item_code': 'Test FG A RW 1', 'warehouse': '_Test Warehouse - _TC'}, 'reserved_qty_for_production')
+		reserved_qty_for_production_after_transfer = frappe.db.get_value(
+			"Bin",
+			{"item_code": "Test FG A RW 1", "warehouse": "_Test Warehouse - _TC"},
+			"reserved_qty_for_production",
+		)
 
-		self.assertEqual(reserved_qty_for_production_after_transfer, flt(reserved_qty_for_production - 5))
+		self.assertEqual(
+			reserved_qty_for_production_after_transfer, flt(reserved_qty_for_production - 5)
+		)
 		ste1 = frappe.get_doc(make_stock_entry(pro_order.name, "Manufacture", 5))
 
 		status = False
 		for d in ste1.items:
-			if d.item_code == 'Alternate Item For A RW 1':
+			if d.item_code == "Alternate Item For A RW 1":
 				status = True
 
 		self.assertEqual(status, True)
 		ste1.submit()
 
+
 def make_items():
-	items = ['Test Finished Goods - A', 'Test FG A RW 1', 'Test FG A RW 2', 'Alternate Item For A RW 1']
+	items = [
+		"Test Finished Goods - A",
+		"Test FG A RW 1",
+		"Test FG A RW 2",
+		"Alternate Item For A RW 1",
+	]
 	for item_code in items:
-		if not frappe.db.exists('Item', item_code):
+		if not frappe.db.exists("Item", item_code):
 			create_item(item_code)
 
-	create_stock_reconciliation(item_code="Test FG A RW 1",
-		warehouse='_Test Warehouse - _TC', qty=10, rate=2000)
+	try:
+		create_stock_reconciliation(
+			item_code="Test FG A RW 1", warehouse="_Test Warehouse - _TC", qty=10, rate=2000
+		)
+	except EmptyStockReconciliationItemsError:
+		pass
 
-	if frappe.db.exists('Item', 'Test FG A RW 1'):
-		doc = frappe.get_doc('Item', 'Test FG A RW 1')
+	if frappe.db.exists("Item", "Test FG A RW 1"):
+		doc = frappe.get_doc("Item", "Test FG A RW 1")
 		doc.allow_alternative_item = 1
 		doc.save()
 
-	if frappe.db.exists('Item', 'Test Finished Goods - A'):
-		doc = frappe.get_doc('Item', 'Test Finished Goods - A')
+	if frappe.db.exists("Item", "Test Finished Goods - A"):
+		doc = frappe.get_doc("Item", "Test Finished Goods - A")
 		doc.is_sub_contracted_item = 1
 		doc.save()
 
-	if not frappe.db.get_value('BOM',
-		{'item': 'Test Finished Goods - A', 'docstatus': 1}):
-		make_bom(item = 'Test Finished Goods - A', raw_materials = ['Test FG A RW 1', 'Test FG A RW 2'])
+	if not frappe.db.get_value("BOM", {"item": "Test Finished Goods - A", "docstatus": 1}):
+		make_bom(item="Test Finished Goods - A", raw_materials=["Test FG A RW 1", "Test FG A RW 2"])
 
-	if not frappe.db.get_value('Warehouse', {'warehouse_name': 'Test Supplier Warehouse'}):
-		frappe.get_doc({
-			'doctype': 'Warehouse',
-			'warehouse_name': 'Test Supplier Warehouse',
-			'company': '_Test Company'
-		}).insert(ignore_permissions=True)
+	if not frappe.db.get_value("Warehouse", {"warehouse_name": "Test Supplier Warehouse"}):
+		frappe.get_doc(
+			{
+				"doctype": "Warehouse",
+				"warehouse_name": "Test Supplier Warehouse",
+				"company": "_Test Company",
+			}
+		).insert(ignore_permissions=True)
diff --git a/erpnext/stock/doctype/item_attribute/item_attribute.py b/erpnext/stock/doctype/item_attribute/item_attribute.py
index 5a28a9e..391ff06 100644
--- a/erpnext/stock/doctype/item_attribute/item_attribute.py
+++ b/erpnext/stock/doctype/item_attribute/item_attribute.py
@@ -14,7 +14,9 @@
 )
 
 
-class ItemAttributeIncrementError(frappe.ValidationError): pass
+class ItemAttributeIncrementError(frappe.ValidationError):
+	pass
+
 
 class ItemAttribute(Document):
 	def __setup__(self):
@@ -29,11 +31,12 @@
 		self.validate_exising_items()
 
 	def validate_exising_items(self):
-		'''Validate that if there are existing items with attributes, they are valid'''
+		"""Validate that if there are existing items with attributes, they are valid"""
 		attributes_list = [d.attribute_value for d in self.item_attribute_values]
 
 		# Get Item Variant Attribute details of variant items
-		items = frappe.db.sql("""
+		items = frappe.db.sql(
+			"""
 			select
 				i.name, iva.attribute_value as value
 			from
@@ -41,13 +44,18 @@
 			where
 				iva.attribute = %(attribute)s
 				and iva.parent = i.name and
-				i.variant_of is not null and i.variant_of != ''""", {"attribute" : self.name}, as_dict=1)
+				i.variant_of is not null and i.variant_of != ''""",
+			{"attribute": self.name},
+			as_dict=1,
+		)
 
 		for item in items:
 			if self.numeric_values:
 				validate_is_incremental(self, self.name, item.value, item.name)
 			else:
-				validate_item_attribute_value(attributes_list, self.name, item.value, item.name, from_variant=False)
+				validate_item_attribute_value(
+					attributes_list, self.name, item.value, item.name, from_variant=False
+				)
 
 	def validate_numeric(self):
 		if self.numeric_values:
diff --git a/erpnext/stock/doctype/item_attribute/test_item_attribute.py b/erpnext/stock/doctype/item_attribute/test_item_attribute.py
index 055c22e..a30f0e9 100644
--- a/erpnext/stock/doctype/item_attribute/test_item_attribute.py
+++ b/erpnext/stock/doctype/item_attribute/test_item_attribute.py
@@ -4,7 +4,7 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Item Attribute')
+test_records = frappe.get_test_records("Item Attribute")
 
 from frappe.tests.utils import FrappeTestCase
 
@@ -18,14 +18,16 @@
 			frappe.delete_doc("Item Attribute", "_Test_Length")
 
 	def test_numeric_item_attribute(self):
-		item_attribute = frappe.get_doc({
-			"doctype": "Item Attribute",
-			"attribute_name": "_Test_Length",
-			"numeric_values": 1,
-			"from_range": 0.0,
-			"to_range": 100.0,
-			"increment": 0
-		})
+		item_attribute = frappe.get_doc(
+			{
+				"doctype": "Item Attribute",
+				"attribute_name": "_Test_Length",
+				"numeric_values": 1,
+				"from_range": 0.0,
+				"to_range": 100.0,
+				"increment": 0,
+			}
+		)
 
 		self.assertRaises(ItemAttributeIncrementError, item_attribute.save)
 
diff --git a/erpnext/stock/doctype/item_barcode/item_barcode.json b/erpnext/stock/doctype/item_barcode/item_barcode.json
index d89ca55..56832f3 100644
--- a/erpnext/stock/doctype/item_barcode/item_barcode.json
+++ b/erpnext/stock/doctype/item_barcode/item_barcode.json
@@ -1,109 +1,50 @@
 {
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "autoname": "field:barcode", 
- "beta": 0, 
- "creation": "2017-12-09 18:54:50.562438", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "actions": [],
+ "autoname": "hash",
+ "creation": "2022-02-11 11:26:22.155183",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+  "barcode",
+  "barcode_type",
+  "uom"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "barcode", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Barcode", 
-   "length": 0, 
-   "no_copy": 1, 
-   "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, 
+   "fieldname": "barcode",
+   "fieldtype": "Data",
+   "in_global_search": 1,
+   "in_list_view": 1,
+   "label": "Barcode",
+   "no_copy": 1,
    "unique": 1
-  }, 
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "barcode_type", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Barcode Type", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "\nEAN\nUPC-A", 
-   "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
+   "fieldname": "barcode_type",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Barcode Type",
+   "options": "\nEAN\nUPC-A"
+  },
+  {
+   "fieldname": "uom",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "UOM",
+   "options": "UOM"
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2018-11-13 06:03:09.814357", 
- "modified_by": "Administrator", 
- "module": "Stock", 
- "name": "Item Barcode", 
- "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
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2022-06-01 06:24:33.969534",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Item Barcode",
+ "naming_rule": "Random",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": [],
+ "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/stock/doctype/item_barcode/item_barcode.py b/erpnext/stock/doctype/item_barcode/item_barcode.py
index 64c39da..c2c0421 100644
--- a/erpnext/stock/doctype/item_barcode/item_barcode.py
+++ b/erpnext/stock/doctype/item_barcode/item_barcode.py
@@ -6,4 +6,4 @@
 
 
 class ItemBarcode(Document):
-    pass
+	pass
diff --git a/erpnext/stock/doctype/item_default/item_default.json b/erpnext/stock/doctype/item_default/item_default.json
index bc17160..042d398 100644
--- a/erpnext/stock/doctype/item_default/item_default.json
+++ b/erpnext/stock/doctype/item_default/item_default.json
@@ -15,6 +15,7 @@
   "default_supplier",
   "column_break_8",
   "expense_account",
+  "default_provisional_account",
   "selling_defaults",
   "selling_cost_center",
   "column_break_12",
@@ -101,11 +102,17 @@
    "fieldtype": "Link",
    "label": "Default Discount Account",
    "options": "Account"
+  },
+  {
+   "fieldname": "default_provisional_account",
+   "fieldtype": "Link",
+   "label": "Default Provisional Account",
+   "options": "Account"
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2021-07-13 01:26:03.860065",
+ "modified": "2022-04-10 20:18:54.148195",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Item Default",
@@ -114,5 +121,6 @@
  "quick_entry": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py
index 469ccd8..b65ba98 100644
--- a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py
+++ b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py
@@ -18,14 +18,17 @@
 	def validate_duplicate_entry(self):
 		if self.is_new():
 			filters = {
-				'item_code': self.item_code,
-				'manufacturer': self.manufacturer,
-				'manufacturer_part_no': self.manufacturer_part_no
+				"item_code": self.item_code,
+				"manufacturer": self.manufacturer,
+				"manufacturer_part_no": self.manufacturer_part_no,
 			}
 
 			if frappe.db.exists("Item Manufacturer", filters):
-				frappe.throw(_("Duplicate entry against the item code {0} and manufacturer {1}")
-					.format(self.item_code, self.manufacturer))
+				frappe.throw(
+					_("Duplicate entry against the item code {0} and manufacturer {1}").format(
+						self.item_code, self.manufacturer
+					)
+				)
 
 	def manage_default_item_manufacturer(self, delete=False):
 		from frappe.model.utils import set_default
@@ -37,11 +40,9 @@
 		if not self.is_default:
 			# if unchecked and default in Item master, clear it.
 			if default_manufacturer == self.manufacturer and default_part_no == self.manufacturer_part_no:
-				frappe.db.set_value("Item", item.name,
-					{
-						"default_item_manufacturer": None,
-						"default_manufacturer_part_no": None
-					})
+				frappe.db.set_value(
+					"Item", item.name, {"default_item_manufacturer": None, "default_manufacturer_part_no": None}
+				)
 
 		elif self.is_default:
 			set_default(self, "item_code")
@@ -50,18 +51,26 @@
 			if delete:
 				manufacturer, manufacturer_part_no = None, None
 
-			elif (default_manufacturer != self.manufacturer) or \
-				(default_manufacturer == self.manufacturer and default_part_no != self.manufacturer_part_no):
+			elif (default_manufacturer != self.manufacturer) or (
+				default_manufacturer == self.manufacturer and default_part_no != self.manufacturer_part_no
+			):
 				manufacturer = self.manufacturer
 				manufacturer_part_no = self.manufacturer_part_no
 
-			frappe.db.set_value("Item", item.name,
-					{
-						"default_item_manufacturer": manufacturer,
-						"default_manufacturer_part_no": manufacturer_part_no
-					})
+			frappe.db.set_value(
+				"Item",
+				item.name,
+				{
+					"default_item_manufacturer": manufacturer,
+					"default_manufacturer_part_no": manufacturer_part_no,
+				},
+			)
+
 
 @frappe.whitelist()
 def get_item_manufacturer_part_no(item_code, manufacturer):
-	return frappe.db.get_value("Item Manufacturer",
-		{'item_code': item_code, 'manufacturer': manufacturer}, 'manufacturer_part_no')
+	return frappe.db.get_value(
+		"Item Manufacturer",
+		{"item_code": item_code, "manufacturer": manufacturer},
+		"manufacturer_part_no",
+	)
diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py
index 010e01a..bcd31ad 100644
--- a/erpnext/stock/doctype/item_price/item_price.py
+++ b/erpnext/stock/doctype/item_price/item_price.py
@@ -5,6 +5,8 @@
 import frappe
 from frappe import _
 from frappe.model.document import Document
+from frappe.query_builder import Criterion
+from frappe.query_builder.functions import Cast_
 from frappe.utils import getdate
 
 
@@ -13,7 +15,6 @@
 
 
 class ItemPrice(Document):
-
 	def validate(self):
 		self.validate_item()
 		self.validate_dates()
@@ -32,45 +33,81 @@
 
 	def update_price_list_details(self):
 		if self.price_list:
-			price_list_details = frappe.db.get_value("Price List",
-				{"name": self.price_list, "enabled": 1},
-				["buying", "selling", "currency"])
+			price_list_details = frappe.db.get_value(
+				"Price List", {"name": self.price_list, "enabled": 1}, ["buying", "selling", "currency"]
+			)
 
 			if not price_list_details:
-				link = frappe.utils.get_link_to_form('Price List', self.price_list)
+				link = frappe.utils.get_link_to_form("Price List", self.price_list)
 				frappe.throw("The price list {0} does not exist or is disabled".format(link))
 
 			self.buying, self.selling, self.currency = price_list_details
 
 	def update_item_details(self):
 		if self.item_code:
-			self.item_name, self.item_description = frappe.db.get_value("Item", self.item_code,["item_name", "description"])
+			self.item_name, self.item_description = frappe.db.get_value(
+				"Item", self.item_code, ["item_name", "description"]
+			)
 
 	def check_duplicates(self):
-		conditions = """where item_code = %(item_code)s and price_list = %(price_list)s and name != %(name)s"""
 
-		for field in [
+		item_price = frappe.qb.DocType("Item Price")
+
+		query = (
+			frappe.qb.from_(item_price)
+			.select(item_price.price_list_rate)
+			.where(
+				(item_price.item_code == self.item_code)
+				& (item_price.price_list == self.price_list)
+				& (item_price.name != self.name)
+			)
+		)
+		data_fields = (
 			"uom",
 			"valid_from",
 			"valid_upto",
-			"packing_unit",
 			"customer",
 			"supplier",
-			"batch_no"]:
-			if self.get(field):
-				conditions += " and {0} = %({0})s ".format(field)
-			else:
-				conditions += "and (isnull({0}) or {0} = '')".format(field)
+			"batch_no",
+		)
 
-		price_list_rate = frappe.db.sql("""
-				select price_list_rate
-				from `tabItem Price`
-				{conditions}
-			""".format(conditions=conditions),
-			self.as_dict(),)
+		number_fields = ["packing_unit"]
+
+		for field in data_fields:
+			if self.get(field):
+				query = query.where(item_price[field] == self.get(field))
+			else:
+				query = query.where(
+					Criterion.any(
+						[
+							item_price[field].isnull(),
+							Cast_(item_price[field], "varchar") == "",
+						]
+					)
+				)
+
+		for field in number_fields:
+			if self.get(field):
+				query = query.where(item_price[field] == self.get(field))
+			else:
+				query = query.where(
+					Criterion.any(
+						[
+							item_price[field].isnull(),
+							item_price[field] == 0,
+						]
+					)
+				)
+
+		price_list_rate = query.run(as_dict=True)
 
 		if price_list_rate:
-			frappe.throw(_("Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, Batch, UOM, Qty, and Dates."), ItemPriceDuplicateItem,)
+			frappe.throw(
+				_(
+					"Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, Batch, UOM, Qty, and Dates."
+				),
+				ItemPriceDuplicateItem,
+			)
 
 	def before_save(self):
 		if self.selling:
diff --git a/erpnext/stock/doctype/item_price/test_item_price.py b/erpnext/stock/doctype/item_price/test_item_price.py
index 6ceba3f..30d933e 100644
--- a/erpnext/stock/doctype/item_price/test_item_price.py
+++ b/erpnext/stock/doctype/item_price/test_item_price.py
@@ -23,8 +23,14 @@
 	def test_addition_of_new_fields(self):
 		# Based on https://github.com/frappe/erpnext/issues/8456
 		test_fields_existance = [
-			'supplier', 'customer', 'uom', 'lead_time_days',
-			'packing_unit', 'valid_from', 'valid_upto', 'note'
+			"supplier",
+			"customer",
+			"uom",
+			"lead_time_days",
+			"packing_unit",
+			"valid_from",
+			"valid_upto",
+			"note",
 		]
 		doc_fields = frappe.copy_doc(test_records[1]).__dict__.keys()
 
@@ -45,10 +51,10 @@
 
 		args = {
 			"price_list": doc.price_list,
-            "customer": doc.customer,
-            "uom": "_Test UOM",
-            "transaction_date": '2017-04-18',
-            "qty": 10
+			"customer": doc.customer,
+			"uom": "_Test UOM",
+			"transaction_date": "2017-04-18",
+			"qty": 10,
 		}
 
 		price = get_price_list_rate_for(process_args(args), doc.item_code)
@@ -61,13 +67,12 @@
 			"price_list": doc.price_list,
 			"customer": doc.customer,
 			"uom": "_Test UOM",
-            "transaction_date": '2017-04-18',
+			"transaction_date": "2017-04-18",
 		}
 
 		price = get_price_list_rate_for(args, doc.item_code)
 		self.assertEqual(price, None)
 
-
 	def test_prices_at_date(self):
 		# Check correct price at first date
 		doc = frappe.copy_doc(test_records[2])
@@ -76,35 +81,35 @@
 			"price_list": doc.price_list,
 			"customer": "_Test Customer",
 			"uom": "_Test UOM",
-			"transaction_date": '2017-04-18',
-			"qty": 7
+			"transaction_date": "2017-04-18",
+			"qty": 7,
 		}
 
 		price = get_price_list_rate_for(args, doc.item_code)
 		self.assertEqual(price, 20)
 
 	def test_prices_at_invalid_date(self):
-		#Check correct price at invalid date
+		# Check correct price at invalid date
 		doc = frappe.copy_doc(test_records[3])
 
 		args = {
 			"price_list": doc.price_list,
 			"qty": 7,
 			"uom": "_Test UOM",
-			"transaction_date": "01-15-2019"
+			"transaction_date": "01-15-2019",
 		}
 
 		price = get_price_list_rate_for(args, doc.item_code)
 		self.assertEqual(price, None)
 
 	def test_prices_outside_of_date(self):
-		#Check correct price when outside of the date
+		# Check correct price when outside of the date
 		doc = frappe.copy_doc(test_records[4])
 
 		args = {
 			"price_list": doc.price_list,
-            "customer": "_Test Customer",
-            "uom": "_Test UOM",
+			"customer": "_Test Customer",
+			"uom": "_Test UOM",
 			"transaction_date": "2017-04-25",
 			"qty": 7,
 		}
@@ -113,7 +118,7 @@
 		self.assertEqual(price, None)
 
 	def test_lowest_price_when_no_date_provided(self):
-		#Check lowest price when no date provided
+		# Check lowest price when no date provided
 		doc = frappe.copy_doc(test_records[1])
 
 		args = {
@@ -125,7 +130,6 @@
 		price = get_price_list_rate_for(args, doc.item_code)
 		self.assertEqual(price, 10)
 
-
 	def test_invalid_item(self):
 		doc = frappe.copy_doc(test_records[1])
 		# Enter invalid item code
@@ -150,8 +154,8 @@
 		args = {
 			"price_list": doc.price_list,
 			"uom": "_Test UOM",
-			"transaction_date": '2017-04-18',
-			"qty": 7
+			"transaction_date": "2017-04-18",
+			"qty": 7,
 		}
 
 		price = get_price_list_rate_for(args, doc.item_code)
@@ -159,4 +163,5 @@
 
 		self.assertEqual(price, 21)
 
-test_records = frappe.get_test_records('Item Price')
+
+test_records = frappe.get_test_records("Item Price")
diff --git a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js
index 5e1f7d5..058783c 100644
--- a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js
+++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js
@@ -13,10 +13,16 @@
 		const exclude_field_types = ['HTML', 'Section Break', 'Column Break', 'Button', 'Read Only'];
 
 		frappe.model.with_doctype('Item', () => {
+			const field_label_map = {};
 			frappe.get_meta('Item').fields.forEach(d => {
+				field_label_map[d.fieldname] = __(d.label) + ` (${d.fieldname})`;
+
 				if (!in_list(exclude_field_types, d.fieldtype)
 					&& !d.no_copy && !in_list(exclude_fields, d.fieldname)) {
-					allow_fields.push(d.fieldname);
+					allow_fields.push({
+						label: field_label_map[d.fieldname],
+						value: d.fieldname,
+					});
 				}
 			});
 
diff --git a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py
index be1517e..cec5e21 100644
--- a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py
+++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py
@@ -8,29 +8,46 @@
 
 
 class ItemVariantSettings(Document):
-	invalid_fields_for_copy_fields_in_variants = ['barcodes']
+	invalid_fields_for_copy_fields_in_variants = ["barcodes"]
 
 	def set_default_fields(self):
 		self.fields = []
-		fields = frappe.get_meta('Item').fields
-		exclude_fields = {"naming_series", "item_code", "item_name", "published_in_website",
-			"standard_rate", "opening_stock", "image", "description",
-			"variant_of", "valuation_rate", "description", "barcodes",
-			"has_variants", "attributes"}
+		fields = frappe.get_meta("Item").fields
+		exclude_fields = {
+			"naming_series",
+			"item_code",
+			"item_name",
+			"published_in_website",
+			"standard_rate",
+			"opening_stock",
+			"image",
+			"description",
+			"variant_of",
+			"valuation_rate",
+			"description",
+			"barcodes",
+			"has_variants",
+			"attributes",
+		}
 
 		for d in fields:
-			if not d.no_copy and d.fieldname not in exclude_fields and \
-				d.fieldtype not in ['HTML', 'Section Break', 'Column Break', 'Button', 'Read Only']:
-				self.append('fields', {
-					'field_name': d.fieldname
-				})
+			if (
+				not d.no_copy
+				and d.fieldname not in exclude_fields
+				and d.fieldtype not in ["HTML", "Section Break", "Column Break", "Button", "Read Only"]
+			):
+				self.append("fields", {"field_name": d.fieldname})
 
 	def remove_invalid_fields_for_copy_fields_in_variants(self):
-		fields = [row for row in self.fields if row.field_name not in self.invalid_fields_for_copy_fields_in_variants]
+		fields = [
+			row
+			for row in self.fields
+			if row.field_name not in self.invalid_fields_for_copy_fields_in_variants
+		]
 		self.fields = fields
 		self.save()
 
 	def validate(self):
 		for d in self.fields:
 			if d.field_name in self.invalid_fields_for_copy_fields_in_variants:
-				frappe.throw(_('Cannot set the field <b>{0}</b> for copying in variants').format(d.field_name))
+				frappe.throw(_("Cannot set the field <b>{0}</b> for copying in variants").format(d.field_name))
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
index 7aff95d..b3af309 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
@@ -19,13 +19,19 @@
 		self.set("items", [])
 		for pr in self.get("purchase_receipts"):
 			if pr.receipt_document_type and pr.receipt_document:
-				pr_items = frappe.db.sql("""select pr_item.item_code, pr_item.description,
+				pr_items = frappe.db.sql(
+					"""select pr_item.item_code, pr_item.description,
 					pr_item.qty, pr_item.base_rate, pr_item.base_amount, pr_item.name,
 					pr_item.cost_center, pr_item.is_fixed_asset
 					from `tab{doctype} Item` pr_item where parent = %s
 					and exists(select name from tabItem
 						where name = pr_item.item_code and (is_stock_item = 1 or is_fixed_asset=1))
-					""".format(doctype=pr.receipt_document_type), pr.receipt_document, as_dict=True)
+					""".format(
+						doctype=pr.receipt_document_type
+					),
+					pr.receipt_document,
+					as_dict=True,
+				)
 
 				for d in pr_items:
 					item = self.append("items")
@@ -33,8 +39,7 @@
 					item.description = d.description
 					item.qty = d.qty
 					item.rate = d.base_rate
-					item.cost_center = d.cost_center or \
-						erpnext.get_default_cost_center(self.company)
+					item.cost_center = d.cost_center or erpnext.get_default_cost_center(self.company)
 					item.amount = d.base_amount
 					item.receipt_document_type = pr.receipt_document_type
 					item.receipt_document = pr.receipt_document
@@ -52,26 +57,30 @@
 		self.set_applicable_charges_on_item()
 		self.validate_applicable_charges_for_item()
 
-
 	def check_mandatory(self):
 		if not self.get("purchase_receipts"):
 			frappe.throw(_("Please enter Receipt Document"))
 
-
 	def validate_receipt_documents(self):
 		receipt_documents = []
 
 		for d in self.get("purchase_receipts"):
 			docstatus = frappe.db.get_value(d.receipt_document_type, d.receipt_document, "docstatus")
 			if docstatus != 1:
-				msg = f"Row {d.idx}: {d.receipt_document_type} {frappe.bold(d.receipt_document)} must be submitted"
+				msg = (
+					f"Row {d.idx}: {d.receipt_document_type} {frappe.bold(d.receipt_document)} must be submitted"
+				)
 				frappe.throw(_(msg), title=_("Invalid Document"))
 
 			if d.receipt_document_type == "Purchase Invoice":
 				update_stock = frappe.db.get_value(d.receipt_document_type, d.receipt_document, "update_stock")
 				if not update_stock:
-					msg = _("Row {0}: Purchase Invoice {1} has no stock impact.").format(d.idx, frappe.bold(d.receipt_document))
-					msg += "<br>" + _("Please create Landed Cost Vouchers against Invoices that have 'Update Stock' enabled.")
+					msg = _("Row {0}: Purchase Invoice {1} has no stock impact.").format(
+						d.idx, frappe.bold(d.receipt_document)
+					)
+					msg += "<br>" + _(
+						"Please create Landed Cost Vouchers against Invoices that have 'Update Stock' enabled."
+					)
 					frappe.throw(msg, title=_("Incorrect Invoice"))
 
 			receipt_documents.append(d.receipt_document)
@@ -81,52 +90,64 @@
 				frappe.throw(_("Item must be added using 'Get Items from Purchase Receipts' button"))
 
 			elif item.receipt_document not in receipt_documents:
-				frappe.throw(_("Item Row {0}: {1} {2} does not exist in above '{1}' table")
-					.format(item.idx, item.receipt_document_type, item.receipt_document))
+				frappe.throw(
+					_("Item Row {0}: {1} {2} does not exist in above '{1}' table").format(
+						item.idx, item.receipt_document_type, item.receipt_document
+					)
+				)
 
 			if not item.cost_center:
-				frappe.throw(_("Row {0}: Cost center is required for an item {1}")
-					.format(item.idx, item.item_code))
+				frappe.throw(
+					_("Row {0}: Cost center is required for an item {1}").format(item.idx, item.item_code)
+				)
 
 	def set_total_taxes_and_charges(self):
 		self.total_taxes_and_charges = sum(flt(d.base_amount) for d in self.get("taxes"))
 
 	def set_applicable_charges_on_item(self):
-		if self.get('taxes') and self.distribute_charges_based_on != 'Distribute Manually':
+		if self.get("taxes") and self.distribute_charges_based_on != "Distribute Manually":
 			total_item_cost = 0.0
 			total_charges = 0.0
 			item_count = 0
 			based_on_field = frappe.scrub(self.distribute_charges_based_on)
 
-			for item in self.get('items'):
+			for item in self.get("items"):
 				total_item_cost += item.get(based_on_field)
 
-			for item in self.get('items'):
-				item.applicable_charges = flt(flt(item.get(based_on_field)) * (flt(self.total_taxes_and_charges) / flt(total_item_cost)),
-					item.precision('applicable_charges'))
+			for item in self.get("items"):
+				item.applicable_charges = flt(
+					flt(item.get(based_on_field)) * (flt(self.total_taxes_and_charges) / flt(total_item_cost)),
+					item.precision("applicable_charges"),
+				)
 				total_charges += item.applicable_charges
 				item_count += 1
 
 			if total_charges != self.total_taxes_and_charges:
 				diff = self.total_taxes_and_charges - total_charges
-				self.get('items')[item_count - 1].applicable_charges += diff
+				self.get("items")[item_count - 1].applicable_charges += diff
 
 	def validate_applicable_charges_for_item(self):
 		based_on = self.distribute_charges_based_on.lower()
 
-		if based_on != 'distribute manually':
+		if based_on != "distribute manually":
 			total = sum(flt(d.get(based_on)) for d in self.get("items"))
 		else:
 			# consider for proportion while distributing manually
-			total = sum(flt(d.get('applicable_charges')) for d in self.get("items"))
+			total = sum(flt(d.get("applicable_charges")) for d in self.get("items"))
 
 		if not total:
-			frappe.throw(_("Total {0} for all items is zero, may be you should change 'Distribute Charges Based On'").format(based_on))
+			frappe.throw(
+				_(
+					"Total {0} for all items is zero, may be you should change 'Distribute Charges Based On'"
+				).format(based_on)
+			)
 
 		total_applicable_charges = sum(flt(d.applicable_charges) for d in self.get("items"))
 
-		precision = get_field_precision(frappe.get_meta("Landed Cost Item").get_field("applicable_charges"),
-		currency=frappe.get_cached_value('Company',  self.company,  "default_currency"))
+		precision = get_field_precision(
+			frappe.get_meta("Landed Cost Item").get_field("applicable_charges"),
+			currency=frappe.get_cached_value("Company", self.company, "default_currency"),
+		)
 
 		diff = flt(self.total_taxes_and_charges) - flt(total_applicable_charges)
 		diff = flt(diff, precision)
@@ -134,7 +155,11 @@
 		if abs(diff) < (2.0 / (10**precision)):
 			self.items[-1].applicable_charges += diff
 		else:
-			frappe.throw(_("Total Applicable Charges in Purchase Receipt Items table must be same as Total Taxes and Charges"))
+			frappe.throw(
+				_(
+					"Total Applicable Charges in Purchase Receipt Items table must be same as Total Taxes and Charges"
+				)
+			)
 
 	def on_submit(self):
 		self.update_landed_cost()
@@ -177,25 +202,41 @@
 			doc.repost_future_sle_and_gle()
 
 	def validate_asset_qty_and_status(self, receipt_document_type, receipt_document):
-		for item in self.get('items'):
+		for item in self.get("items"):
 			if item.is_fixed_asset:
-				receipt_document_type = 'purchase_invoice' if item.receipt_document_type == 'Purchase Invoice' \
-						else 'purchase_receipt'
-				docs = frappe.db.get_all('Asset', filters={ receipt_document_type: item.receipt_document,
-					'item_code': item.item_code }, fields=['name', 'docstatus'])
+				receipt_document_type = (
+					"purchase_invoice" if item.receipt_document_type == "Purchase Invoice" else "purchase_receipt"
+				)
+				docs = frappe.db.get_all(
+					"Asset",
+					filters={receipt_document_type: item.receipt_document, "item_code": item.item_code},
+					fields=["name", "docstatus"],
+				)
 				if not docs or len(docs) != item.qty:
-					frappe.throw(_('There are not enough asset created or linked to {0}. Please create or link {1} Assets with respective document.').format(
-						item.receipt_document, item.qty))
+					frappe.throw(
+						_(
+							"There are not enough asset created or linked to {0}. Please create or link {1} Assets with respective document."
+						).format(item.receipt_document, item.qty)
+					)
 				if docs:
 					for d in docs:
 						if d.docstatus == 1:
-							frappe.throw(_('{2} <b>{0}</b> has submitted Assets. Remove Item <b>{1}</b> from table to continue.').format(
-									item.receipt_document, item.item_code, item.receipt_document_type))
+							frappe.throw(
+								_(
+									"{2} <b>{0}</b> has submitted Assets. Remove Item <b>{1}</b> from table to continue."
+								).format(
+									item.receipt_document, item.item_code, item.receipt_document_type
+								)
+							)
 
 	def update_rate_in_serial_no_for_non_asset_items(self, receipt_document):
 		for item in receipt_document.get("items"):
 			if not item.is_fixed_asset and item.serial_no:
 				serial_nos = get_serial_nos(item.serial_no)
 				if serial_nos:
-					frappe.db.sql("update `tabSerial No` set purchase_rate=%s where name in ({0})"
-						.format(", ".join(["%s"]*len(serial_nos))), tuple([item.valuation_rate] + serial_nos))
+					frappe.db.sql(
+						"update `tabSerial No` set purchase_rate=%s where name in ({0})".format(
+							", ".join(["%s"] * len(serial_nos))
+						),
+						tuple([item.valuation_rate] + serial_nos),
+					)
diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
index 6dc4fee..1ba8011 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
@@ -2,7 +2,6 @@
 # License: GNU General Public License v3. See license.txt
 
 
-
 import frappe
 from frappe.tests.utils import FrappeTestCase
 from frappe.utils import add_to_date, flt, now
@@ -22,34 +21,50 @@
 	def test_landed_cost_voucher(self):
 		frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
 
-		pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
-			warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1",
-			get_multiple_items = True, get_taxes_and_charges = True)
+		pr = make_purchase_receipt(
+			company="_Test Company with perpetual inventory",
+			warehouse="Stores - TCP1",
+			supplier_warehouse="Work In Progress - TCP1",
+			get_multiple_items=True,
+			get_taxes_and_charges=True,
+		)
 
-		last_sle = frappe.db.get_value("Stock Ledger Entry", {
+		last_sle = frappe.db.get_value(
+			"Stock Ledger Entry",
+			{
 				"voucher_type": pr.doctype,
 				"voucher_no": pr.name,
 				"item_code": "_Test Item",
 				"warehouse": "Stores - TCP1",
 				"is_cancelled": 0,
 			},
-			fieldname=["qty_after_transaction", "stock_value"], as_dict=1)
+			fieldname=["qty_after_transaction", "stock_value"],
+			as_dict=1,
+		)
 
 		create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company)
 
-		pr_lc_value = frappe.db.get_value("Purchase Receipt Item", {"parent": pr.name}, "landed_cost_voucher_amount")
+		pr_lc_value = frappe.db.get_value(
+			"Purchase Receipt Item", {"parent": pr.name}, "landed_cost_voucher_amount"
+		)
 		self.assertEqual(pr_lc_value, 25.0)
 
-		last_sle_after_landed_cost = frappe.db.get_value("Stock Ledger Entry", {
+		last_sle_after_landed_cost = frappe.db.get_value(
+			"Stock Ledger Entry",
+			{
 				"voucher_type": pr.doctype,
 				"voucher_no": pr.name,
 				"item_code": "_Test Item",
 				"warehouse": "Stores - TCP1",
 				"is_cancelled": 0,
 			},
-			fieldname=["qty_after_transaction", "stock_value"], as_dict=1)
+			fieldname=["qty_after_transaction", "stock_value"],
+			as_dict=1,
+		)
 
-		self.assertEqual(last_sle.qty_after_transaction, last_sle_after_landed_cost.qty_after_transaction)
+		self.assertEqual(
+			last_sle.qty_after_transaction, last_sle_after_landed_cost.qty_after_transaction
+		)
 		self.assertEqual(last_sle_after_landed_cost.stock_value - last_sle.stock_value, 25.0)
 
 		# assert after submit
@@ -57,24 +72,20 @@
 
 		# Mess up cancelled SLE modified timestamp to check
 		# if they aren't effective in any business logic.
-		frappe.db.set_value("Stock Ledger Entry",
-			{
-				"is_cancelled": 1,
-				"voucher_type": pr.doctype,
-				"voucher_no": pr.name
-			},
-			"is_cancelled", 1,
-			modified=add_to_date(now(), hours=1, as_datetime=True, as_string=True)
+		frappe.db.set_value(
+			"Stock Ledger Entry",
+			{"is_cancelled": 1, "voucher_type": pr.doctype, "voucher_no": pr.name},
+			"is_cancelled",
+			1,
+			modified=add_to_date(now(), hours=1, as_datetime=True, as_string=True),
 		)
 
 		items, warehouses = pr.get_items_and_warehouses()
-		update_gl_entries_after(pr.posting_date, pr.posting_time,
-			warehouses, items, company=pr.company)
+		update_gl_entries_after(pr.posting_date, pr.posting_time, warehouses, items, company=pr.company)
 
 		# reassert after reposting
 		self.assertPurchaseReceiptLCVGLEntries(pr)
 
-
 	def assertPurchaseReceiptLCVGLEntries(self, pr):
 
 		gl_entries = get_gl_entries("Purchase Receipt", pr.name)
@@ -90,54 +101,74 @@
 				"Stock Received But Not Billed - TCP1": [0.0, 500.0],
 				"Expenses Included In Valuation - TCP1": [0.0, 50.0],
 				"_Test Account Customs Duty - TCP1": [0.0, 150],
-				"_Test Account Shipping Charges - TCP1": [0.0, 100.00]
+				"_Test Account Shipping Charges - TCP1": [0.0, 100.00],
 			}
 		else:
 			expected_values = {
 				stock_in_hand_account: [400.0, 0.0],
 				fixed_asset_account: [400.0, 0.0],
 				"Stock Received But Not Billed - TCP1": [0.0, 500.0],
-				"Expenses Included In Valuation - TCP1": [0.0, 300.0]
+				"Expenses Included In Valuation - TCP1": [0.0, 300.0],
 			}
 
 		for gle in gl_entries:
-			if not gle.get('is_cancelled'):
-				self.assertEqual(expected_values[gle.account][0], gle.debit, msg=f"incorrect debit for {gle.account}")
-				self.assertEqual(expected_values[gle.account][1], gle.credit, msg=f"incorrect credit for {gle.account}")
-
+			if not gle.get("is_cancelled"):
+				self.assertEqual(
+					expected_values[gle.account][0], gle.debit, msg=f"incorrect debit for {gle.account}"
+				)
+				self.assertEqual(
+					expected_values[gle.account][1], gle.credit, msg=f"incorrect credit for {gle.account}"
+				)
 
 	def test_landed_cost_voucher_against_purchase_invoice(self):
 
-		pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(),
-			posting_time=frappe.utils.nowtime(), cash_bank_account="Cash - TCP1",
-			company="_Test Company with perpetual inventory", supplier_warehouse="Work In Progress - TCP1",
-			warehouse= "Stores - TCP1", cost_center = "Main - TCP1",
-			expense_account ="_Test Account Cost for Goods Sold - TCP1")
+		pi = make_purchase_invoice(
+			update_stock=1,
+			posting_date=frappe.utils.nowdate(),
+			posting_time=frappe.utils.nowtime(),
+			cash_bank_account="Cash - TCP1",
+			company="_Test Company with perpetual inventory",
+			supplier_warehouse="Work In Progress - TCP1",
+			warehouse="Stores - TCP1",
+			cost_center="Main - TCP1",
+			expense_account="_Test Account Cost for Goods Sold - TCP1",
+		)
 
-		last_sle = frappe.db.get_value("Stock Ledger Entry", {
+		last_sle = frappe.db.get_value(
+			"Stock Ledger Entry",
+			{
 				"voucher_type": pi.doctype,
 				"voucher_no": pi.name,
 				"item_code": "_Test Item",
-				"warehouse": "Stores - TCP1"
+				"warehouse": "Stores - TCP1",
 			},
-			fieldname=["qty_after_transaction", "stock_value"], as_dict=1)
+			fieldname=["qty_after_transaction", "stock_value"],
+			as_dict=1,
+		)
 
 		create_landed_cost_voucher("Purchase Invoice", pi.name, pi.company)
 
-		pi_lc_value = frappe.db.get_value("Purchase Invoice Item", {"parent": pi.name},
-			"landed_cost_voucher_amount")
+		pi_lc_value = frappe.db.get_value(
+			"Purchase Invoice Item", {"parent": pi.name}, "landed_cost_voucher_amount"
+		)
 
 		self.assertEqual(pi_lc_value, 50.0)
 
-		last_sle_after_landed_cost = frappe.db.get_value("Stock Ledger Entry", {
+		last_sle_after_landed_cost = frappe.db.get_value(
+			"Stock Ledger Entry",
+			{
 				"voucher_type": pi.doctype,
 				"voucher_no": pi.name,
 				"item_code": "_Test Item",
-				"warehouse": "Stores - TCP1"
+				"warehouse": "Stores - TCP1",
 			},
-			fieldname=["qty_after_transaction", "stock_value"], as_dict=1)
+			fieldname=["qty_after_transaction", "stock_value"],
+			as_dict=1,
+		)
 
-		self.assertEqual(last_sle.qty_after_transaction, last_sle_after_landed_cost.qty_after_transaction)
+		self.assertEqual(
+			last_sle.qty_after_transaction, last_sle_after_landed_cost.qty_after_transaction
+		)
 
 		self.assertEqual(last_sle_after_landed_cost.stock_value - last_sle.stock_value, 50.0)
 
@@ -149,20 +180,26 @@
 		expected_values = {
 			stock_in_hand_account: [300.0, 0.0],
 			"Creditors - TCP1": [0.0, 250.0],
-			"Expenses Included In Valuation - TCP1": [0.0, 50.0]
+			"Expenses Included In Valuation - TCP1": [0.0, 50.0],
 		}
 
 		for gle in gl_entries:
-			if not gle.get('is_cancelled'):
+			if not gle.get("is_cancelled"):
 				self.assertEqual(expected_values[gle.account][0], gle.debit)
 				self.assertEqual(expected_values[gle.account][1], gle.credit)
 
-
 	def test_landed_cost_voucher_for_serialized_item(self):
-		frappe.db.sql("delete from `tabSerial No` where name in ('SN001', 'SN002', 'SN003', 'SN004', 'SN005')")
-		pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1",
-		supplier_warehouse = "Work in Progress - TCP1", get_multiple_items = True,
-		get_taxes_and_charges = True, do_not_submit = True)
+		frappe.db.sql(
+			"delete from `tabSerial No` where name in ('SN001', 'SN002', 'SN003', 'SN004', 'SN005')"
+		)
+		pr = make_purchase_receipt(
+			company="_Test Company with perpetual inventory",
+			warehouse="Stores - TCP1",
+			supplier_warehouse="Work In Progress - TCP1",
+			get_multiple_items=True,
+			get_taxes_and_charges=True,
+			do_not_submit=True,
+		)
 
 		pr.items[0].item_code = "_Test Serialized Item"
 		pr.items[0].serial_no = "SN001\nSN002\nSN003\nSN004\nSN005"
@@ -172,8 +209,7 @@
 
 		create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company)
 
-		serial_no = frappe.db.get_value("Serial No", "SN001",
-			["warehouse", "purchase_rate"], as_dict=1)
+		serial_no = frappe.db.get_value("Serial No", "SN001", ["warehouse", "purchase_rate"], as_dict=1)
 
 		self.assertEqual(serial_no.purchase_rate - serial_no_rate, 5.0)
 		self.assertEqual(serial_no.warehouse, "Stores - TCP1")
@@ -183,60 +219,82 @@
 		landed costs, this should be allowed for serial nos too.
 
 		Case:
-			- receipt a serial no @ X rate
-			- delivery the serial no @ X rate
-			- add LCV to receipt X + Y
-			- LCV should be successful
-			- delivery should reflect X+Y valuation.
+		        - receipt a serial no @ X rate
+		        - delivery the serial no @ X rate
+		        - add LCV to receipt X + Y
+		        - LCV should be successful
+		        - delivery should reflect X+Y valuation.
 		"""
 		serial_no = "LCV_TEST_SR_NO"
 		item_code = "_Test Serialized Item"
 		warehouse = "Stores - TCP1"
 
-		pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
-				warehouse=warehouse, qty=1, rate=200,
-				item_code=item_code, serial_no=serial_no)
+		pr = make_purchase_receipt(
+			company="_Test Company with perpetual inventory",
+			warehouse=warehouse,
+			qty=1,
+			rate=200,
+			item_code=item_code,
+			serial_no=serial_no,
+		)
 
 		serial_no_rate = frappe.db.get_value("Serial No", serial_no, "purchase_rate")
 
 		# deliver it before creating LCV
-		dn = create_delivery_note(item_code=item_code,
-				company='_Test Company with perpetual inventory', warehouse='Stores - TCP1',
-				serial_no=serial_no, qty=1, rate=500,
-				cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1")
+		dn = create_delivery_note(
+			item_code=item_code,
+			company="_Test Company with perpetual inventory",
+			warehouse="Stores - TCP1",
+			serial_no=serial_no,
+			qty=1,
+			rate=500,
+			cost_center="Main - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+		)
 
 		charges = 10
 		create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, charges=charges)
 
 		new_purchase_rate = serial_no_rate + charges
 
-		serial_no = frappe.db.get_value("Serial No", serial_no,
-			["warehouse", "purchase_rate"], as_dict=1)
+		serial_no = frappe.db.get_value(
+			"Serial No", serial_no, ["warehouse", "purchase_rate"], as_dict=1
+		)
 
 		self.assertEqual(serial_no.purchase_rate, new_purchase_rate)
 
-		stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
-				filters={
-					"voucher_no": dn.name,
-					"voucher_type": dn.doctype,
-					"is_cancelled": 0  # LCV cancels with same name.
-				},
-				fieldname="stock_value_difference")
+		stock_value_difference = frappe.db.get_value(
+			"Stock Ledger Entry",
+			filters={
+				"voucher_no": dn.name,
+				"voucher_type": dn.doctype,
+				"is_cancelled": 0,  # LCV cancels with same name.
+			},
+			fieldname="stock_value_difference",
+		)
 
 		# reposting should update the purchase rate in future delivery
 		self.assertEqual(stock_value_difference, -new_purchase_rate)
 
-	def test_landed_cost_voucher_for_odd_numbers (self):
-		pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", do_not_save=True)
+	def test_landed_cost_voucher_for_odd_numbers(self):
+		pr = make_purchase_receipt(
+			company="_Test Company with perpetual inventory",
+			warehouse="Stores - TCP1",
+			supplier_warehouse="Work In Progress - TCP1",
+			do_not_save=True,
+		)
 		pr.items[0].cost_center = "Main - TCP1"
 		for x in range(2):
-			pr.append("items", {
-				"item_code": "_Test Item",
-				"warehouse": "Stores - TCP1",
-				"cost_center": "Main - TCP1",
-				"qty": 5,
-				"rate": 50
-			})
+			pr.append(
+				"items",
+				{
+					"item_code": "_Test Item",
+					"warehouse": "Stores - TCP1",
+					"cost_center": "Main - TCP1",
+					"qty": 5,
+					"rate": 50,
+				},
+			)
 		pr.submit()
 
 		lcv = create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, 123.22)
@@ -245,37 +303,50 @@
 		self.assertEqual(flt(lcv.items[2].applicable_charges, 2), 41.08)
 
 	def test_multiple_landed_cost_voucher_against_pr(self):
-		pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1",
-			supplier_warehouse = "Stores - TCP1", do_not_save=True)
+		pr = make_purchase_receipt(
+			company="_Test Company with perpetual inventory",
+			warehouse="Stores - TCP1",
+			supplier_warehouse="Stores - TCP1",
+			do_not_save=True,
+		)
 
-		pr.append("items", {
-			"item_code": "_Test Item",
-			"warehouse": "Stores - TCP1",
-			"cost_center": "Main - TCP1",
-			"qty": 5,
-			"rate": 100
-		})
+		pr.append(
+			"items",
+			{
+				"item_code": "_Test Item",
+				"warehouse": "Stores - TCP1",
+				"cost_center": "Main - TCP1",
+				"qty": 5,
+				"rate": 100,
+			},
+		)
 
 		pr.submit()
 
-		lcv1 = make_landed_cost_voucher(company = pr.company, receipt_document_type = 'Purchase Receipt',
-			receipt_document=pr.name, charges=100, do_not_save=True)
+		lcv1 = make_landed_cost_voucher(
+			company=pr.company,
+			receipt_document_type="Purchase Receipt",
+			receipt_document=pr.name,
+			charges=100,
+			do_not_save=True,
+		)
 
 		lcv1.insert()
-		lcv1.set('items', [
-			lcv1.get('items')[0]
-		])
+		lcv1.set("items", [lcv1.get("items")[0]])
 		distribute_landed_cost_on_items(lcv1)
 
 		lcv1.submit()
 
-		lcv2 = make_landed_cost_voucher(company = pr.company, receipt_document_type = 'Purchase Receipt',
-			receipt_document=pr.name, charges=100, do_not_save=True)
+		lcv2 = make_landed_cost_voucher(
+			company=pr.company,
+			receipt_document_type="Purchase Receipt",
+			receipt_document=pr.name,
+			charges=100,
+			do_not_save=True,
+		)
 
 		lcv2.insert()
-		lcv2.set('items', [
-			lcv2.get('items')[1]
-		])
+		lcv2.set("items", [lcv2.get("items")[1]])
 		distribute_landed_cost_on_items(lcv2)
 
 		lcv2.submit()
@@ -294,22 +365,31 @@
 		save_new_records(test_records)
 
 		## Create USD Shipping charges_account
-		usd_shipping = create_account(account_name="Shipping Charges USD",
-			parent_account="Duties and Taxes - TCP1", company="_Test Company with perpetual inventory",
-			account_currency="USD")
+		usd_shipping = create_account(
+			account_name="Shipping Charges USD",
+			parent_account="Duties and Taxes - TCP1",
+			company="_Test Company with perpetual inventory",
+			account_currency="USD",
+		)
 
-		pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1",
-			supplier_warehouse = "Stores - TCP1")
+		pr = make_purchase_receipt(
+			company="_Test Company with perpetual inventory",
+			warehouse="Stores - TCP1",
+			supplier_warehouse="Stores - TCP1",
+		)
 		pr.submit()
 
-		lcv = make_landed_cost_voucher(company = pr.company, receipt_document_type = "Purchase Receipt",
-			receipt_document=pr.name, charges=100, do_not_save=True)
+		lcv = make_landed_cost_voucher(
+			company=pr.company,
+			receipt_document_type="Purchase Receipt",
+			receipt_document=pr.name,
+			charges=100,
+			do_not_save=True,
+		)
 
-		lcv.append("taxes", {
-			"description": "Shipping Charges",
-			"expense_account": usd_shipping,
-			"amount": 10
-		})
+		lcv.append(
+			"taxes", {"description": "Shipping Charges", "expense_account": usd_shipping, "amount": 10}
+		)
 
 		lcv.save()
 		lcv.submit()
@@ -319,12 +399,18 @@
 		self.assertEqual(lcv.total_taxes_and_charges, 729)
 		self.assertEqual(pr.items[0].landed_cost_voucher_amount, 729)
 
-		gl_entries = frappe.get_all("GL Entry", fields=["account", "credit", "credit_in_account_currency"],
-			filters={"voucher_no": pr.name, "account": ("in", ["Shipping Charges USD - TCP1", "Expenses Included In Valuation - TCP1"])})
+		gl_entries = frappe.get_all(
+			"GL Entry",
+			fields=["account", "credit", "credit_in_account_currency"],
+			filters={
+				"voucher_no": pr.name,
+				"account": ("in", ["Shipping Charges USD - TCP1", "Expenses Included In Valuation - TCP1"]),
+			},
+		)
 
 		expected_gl_entries = {
 			"Shipping Charges USD - TCP1": [629, 10],
-			"Expenses Included In Valuation - TCP1": [100, 100]
+			"Expenses Included In Valuation - TCP1": [100, 100],
 		}
 
 		for entry in gl_entries:
@@ -334,7 +420,9 @@
 
 	def test_asset_lcv(self):
 		"Check if LCV for an Asset updates the Assets Gross Purchase Amount correctly."
-		frappe.db.set_value("Company", "_Test Company", "capital_work_in_progress_account", "CWIP Account - _TC")
+		frappe.db.set_value(
+			"Company", "_Test Company", "capital_work_in_progress_account", "CWIP Account - _TC"
+		)
 
 		if not frappe.db.exists("Asset Category", "Computers"):
 			create_asset_category()
@@ -345,15 +433,16 @@
 		pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=50000)
 
 		# check if draft asset was created
-		assets = frappe.db.get_all('Asset', filters={'purchase_receipt': pr.name})
+		assets = frappe.db.get_all("Asset", filters={"purchase_receipt": pr.name})
 		self.assertEqual(len(assets), 1)
 
 		lcv = make_landed_cost_voucher(
-			company = pr.company,
-			receipt_document_type = "Purchase Receipt",
+			company=pr.company,
+			receipt_document_type="Purchase Receipt",
 			receipt_document=pr.name,
 			charges=80,
-			expense_account="Expenses Included In Valuation - _TC")
+			expense_account="Expenses Included In Valuation - _TC",
+		)
 
 		lcv.save()
 		lcv.submit()
@@ -365,27 +454,38 @@
 		lcv.cancel()
 		pr.cancel()
 
-def make_landed_cost_voucher(** args):
+
+def make_landed_cost_voucher(**args):
 	args = frappe._dict(args)
 	ref_doc = frappe.get_doc(args.receipt_document_type, args.receipt_document)
 
-	lcv = frappe.new_doc('Landed Cost Voucher')
-	lcv.company = args.company or '_Test Company'
-	lcv.distribute_charges_based_on = 'Amount'
+	lcv = frappe.new_doc("Landed Cost Voucher")
+	lcv.company = args.company or "_Test Company"
+	lcv.distribute_charges_based_on = "Amount"
 
-	lcv.set('purchase_receipts', [{
-		"receipt_document_type": args.receipt_document_type,
-		"receipt_document": args.receipt_document,
-		"supplier": ref_doc.supplier,
-		"posting_date": ref_doc.posting_date,
-		"grand_total": ref_doc.grand_total
-	}])
+	lcv.set(
+		"purchase_receipts",
+		[
+			{
+				"receipt_document_type": args.receipt_document_type,
+				"receipt_document": args.receipt_document,
+				"supplier": ref_doc.supplier,
+				"posting_date": ref_doc.posting_date,
+				"grand_total": ref_doc.grand_total,
+			}
+		],
+	)
 
-	lcv.set("taxes", [{
-		"description": "Shipping Charges",
-		"expense_account": args.expense_account or "Expenses Included In Valuation - TCP1",
-		"amount": args.charges
-	}])
+	lcv.set(
+		"taxes",
+		[
+			{
+				"description": "Shipping Charges",
+				"expense_account": args.expense_account or "Expenses Included In Valuation - TCP1",
+				"amount": args.charges,
+			}
+		],
+	)
 
 	if not args.do_not_save:
 		lcv.insert()
@@ -400,21 +500,31 @@
 
 	lcv = frappe.new_doc("Landed Cost Voucher")
 	lcv.company = company
-	lcv.distribute_charges_based_on = 'Amount'
+	lcv.distribute_charges_based_on = "Amount"
 
-	lcv.set("purchase_receipts", [{
-		"receipt_document_type": receipt_document_type,
-		"receipt_document": receipt_document,
-		"supplier": ref_doc.supplier,
-		"posting_date": ref_doc.posting_date,
-		"grand_total": ref_doc.base_grand_total
-	}])
+	lcv.set(
+		"purchase_receipts",
+		[
+			{
+				"receipt_document_type": receipt_document_type,
+				"receipt_document": receipt_document,
+				"supplier": ref_doc.supplier,
+				"posting_date": ref_doc.posting_date,
+				"grand_total": ref_doc.base_grand_total,
+			}
+		],
+	)
 
-	lcv.set("taxes", [{
-		"description": "Insurance Charges",
-		"expense_account": "Expenses Included In Valuation - TCP1",
-		"amount": charges
-	}])
+	lcv.set(
+		"taxes",
+		[
+			{
+				"description": "Insurance Charges",
+				"expense_account": "Expenses Included In Valuation - TCP1",
+				"amount": charges,
+			}
+		],
+	)
 
 	lcv.insert()
 
@@ -424,6 +534,7 @@
 
 	return lcv
 
+
 def distribute_landed_cost_on_items(lcv):
 	based_on = lcv.distribute_charges_based_on.lower()
 	total = sum(flt(d.get(based_on)) for d in lcv.get("items"))
@@ -432,4 +543,5 @@
 		item.applicable_charges = flt(item.get(based_on)) * flt(lcv.total_taxes_and_charges) / flt(total)
 		item.applicable_charges = flt(item.applicable_charges, lcv.precision("applicable_charges", item))
 
-test_records = frappe.get_test_records('Landed Cost Voucher')
+
+test_records = frappe.get_test_records("Landed Cost Voucher")
diff --git a/erpnext/stock/doctype/manufacturer/test_manufacturer.py b/erpnext/stock/doctype/manufacturer/test_manufacturer.py
index 6632347..e176b28 100644
--- a/erpnext/stock/doctype/manufacturer/test_manufacturer.py
+++ b/erpnext/stock/doctype/manufacturer/test_manufacturer.py
@@ -5,5 +5,6 @@
 
 # test_records = frappe.get_test_records('Manufacturer')
 
+
 class TestManufacturer(unittest.TestCase):
 	pass
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index 5f53be0..e68b0ab 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -214,6 +214,7 @@
 					material_request_type: frm.doc.material_request_type,
 					plc_conversion_rate: 1,
 					rate: item.rate,
+					uom: item.uom,
 					conversion_factor: item.conversion_factor
 				},
 				overwrite_warehouse: overwrite_warehouse
@@ -392,6 +393,7 @@
 	item_code: function(frm, doctype, name) {
 		const item = locals[doctype][name];
 		item.rate = 0;
+		item.uom = '';
 		set_schedule_date(frm);
 		frm.events.get_item_data(frm, item, true);
 	},
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index 51209ac..2614a7f 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -18,20 +18,19 @@
 from erpnext.stock.doctype.item.item import get_item_defaults
 from erpnext.stock.stock_balance import get_indented_qty, update_bin_qty
 
-form_grid_templates = {
-	"items": "templates/form_grid/material_request_grid.html"
-}
+form_grid_templates = {"items": "templates/form_grid/material_request_grid.html"}
+
 
 class MaterialRequest(BuyingController):
 	def get_feed(self):
-		return _("{0}: {1}").format(self.status, self.material_request_type)
+		return
 
 	def check_if_already_pulled(self):
 		pass
 
 	def validate_qty_against_so(self):
-		so_items = {} # Format --> {'SO/00001': {'Item/001': 120, 'Item/002': 24}}
-		for d in self.get('items'):
+		so_items = {}  # Format --> {'SO/00001': {'Item/001': 120, 'Item/002': 24}}
+		for d in self.get("items"):
 			if d.sales_order:
 				if not d.sales_order in so_items:
 					so_items[d.sales_order] = {d.item_code: flt(d.qty)}
@@ -43,24 +42,34 @@
 
 		for so_no in so_items.keys():
 			for item in so_items[so_no].keys():
-				already_indented = frappe.db.sql("""select sum(qty)
+				already_indented = frappe.db.sql(
+					"""select sum(qty)
 					from `tabMaterial Request Item`
 					where item_code = %s and sales_order = %s and
-					docstatus = 1 and parent != %s""", (item, so_no, self.name))
+					docstatus = 1 and parent != %s""",
+					(item, so_no, self.name),
+				)
 				already_indented = already_indented and flt(already_indented[0][0]) or 0
 
-				actual_so_qty = frappe.db.sql("""select sum(stock_qty) from `tabSales Order Item`
-					where parent = %s and item_code = %s and docstatus = 1""", (so_no, item))
+				actual_so_qty = frappe.db.sql(
+					"""select sum(stock_qty) from `tabSales Order Item`
+					where parent = %s and item_code = %s and docstatus = 1""",
+					(so_no, item),
+				)
 				actual_so_qty = actual_so_qty and flt(actual_so_qty[0][0]) or 0
 
 				if actual_so_qty and (flt(so_items[so_no][item]) + already_indented > actual_so_qty):
-					frappe.throw(_("Material Request of maximum {0} can be made for Item {1} against Sales Order {2}").format(actual_so_qty - already_indented, item, so_no))
+					frappe.throw(
+						_("Material Request of maximum {0} can be made for Item {1} against Sales Order {2}").format(
+							actual_so_qty - already_indented, item, so_no
+						)
+					)
 
 	def validate(self):
 		super(MaterialRequest, self).validate()
 
 		self.validate_schedule_date()
-		self.check_for_on_hold_or_closed_status('Sales Order', 'sales_order')
+		self.check_for_on_hold_or_closed_status("Sales Order", "sales_order")
 		self.validate_uom_is_integer("uom", "qty")
 		self.validate_material_request_type()
 
@@ -68,9 +77,22 @@
 			self.status = "Draft"
 
 		from erpnext.controllers.status_updater import validate_status
-		validate_status(self.status,
-			["Draft", "Submitted", "Stopped", "Cancelled", "Pending",
-			"Partially Ordered", "Ordered", "Issued", "Transferred", "Received"])
+
+		validate_status(
+			self.status,
+			[
+				"Draft",
+				"Submitted",
+				"Stopped",
+				"Cancelled",
+				"Pending",
+				"Partially Ordered",
+				"Ordered",
+				"Issued",
+				"Transferred",
+				"Received",
+			],
+		)
 
 		validate_for_items(self)
 
@@ -82,23 +104,26 @@
 		self.reset_default_field_value("set_warehouse", "items", "warehouse")
 		self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
 
+	def before_update_after_submit(self):
+		self.validate_schedule_date()
+
 	def validate_material_request_type(self):
-		""" Validate fields in accordance with selected type """
+		"""Validate fields in accordance with selected type"""
 
 		if self.material_request_type != "Customer Provided":
 			self.customer = None
 
 	def set_title(self):
-		'''Set title as comma separated list of items'''
+		"""Set title as comma separated list of items"""
 		if not self.title:
-			items = ', '.join([d.item_name for d in self.items][:3])
-			self.title = _('{0} Request for {1}').format(self.material_request_type, items)[:100]
+			items = ", ".join([d.item_name for d in self.items][:3])
+			self.title = _("{0} Request for {1}").format(self.material_request_type, items)[:100]
 
 	def on_submit(self):
 		# frappe.db.set(self, 'status', 'Submitted')
 		self.update_requested_qty()
 		self.update_requested_qty_in_production_plan()
-		if self.material_request_type == 'Purchase':
+		if self.material_request_type == "Purchase":
 			self.validate_budget()
 
 	def before_save(self):
@@ -111,13 +136,15 @@
 		# if MRQ is already closed, no point saving the document
 		check_on_hold_or_closed_status(self.doctype, self.name)
 
-		self.set_status(update=True, status='Cancelled')
+		self.set_status(update=True, status="Cancelled")
 
 	def check_modified_date(self):
-		mod_db = frappe.db.sql("""select modified from `tabMaterial Request` where name = %s""",
-			self.name)
-		date_diff = frappe.db.sql("""select TIMEDIFF('%s', '%s')"""
-			% (mod_db[0][0], cstr(self.modified)))
+		mod_db = frappe.db.sql(
+			"""select modified from `tabMaterial Request` where name = %s""", self.name
+		)
+		date_diff = frappe.db.sql(
+			"""select TIMEDIFF('%s', '%s')""" % (mod_db[0][0], cstr(self.modified))
+		)
 
 		if date_diff and date_diff[0][0]:
 			frappe.throw(_("{0} {1} has been modified. Please refresh.").format(_(self.doctype), self.name))
@@ -133,22 +160,24 @@
 		validates that `status` is acceptable for the present controller status
 		and throws an Exception if otherwise.
 		"""
-		if self.status and self.status == 'Cancelled':
+		if self.status and self.status == "Cancelled":
 			# cancelled documents cannot change
 			if status != self.status:
 				frappe.throw(
-					_("{0} {1} is cancelled so the action cannot be completed").
-						format(_(self.doctype), self.name),
-					frappe.InvalidStatusError
+					_("{0} {1} is cancelled so the action cannot be completed").format(
+						_(self.doctype), self.name
+					),
+					frappe.InvalidStatusError,
 				)
 
-		elif self.status and self.status == 'Draft':
+		elif self.status and self.status == "Draft":
 			# draft document to pending only
-			if status != 'Pending':
+			if status != "Pending":
 				frappe.throw(
-					_("{0} {1} has not been submitted so the action cannot be completed").
-						format(_(self.doctype), self.name),
-					frappe.InvalidStatusError
+					_("{0} {1} has not been submitted so the action cannot be completed").format(
+						_(self.doctype), self.name
+					),
+					frappe.InvalidStatusError,
 				)
 
 	def on_cancel(self):
@@ -165,67 +194,88 @@
 		for d in self.get("items"):
 			if d.name in mr_items:
 				if self.material_request_type in ("Material Issue", "Material Transfer", "Customer Provided"):
-					d.ordered_qty =  flt(frappe.db.sql("""select sum(transfer_qty)
+					d.ordered_qty = flt(
+						frappe.db.sql(
+							"""select sum(transfer_qty)
 						from `tabStock Entry Detail` where material_request = %s
 						and material_request_item = %s and docstatus = 1""",
-						(self.name, d.name))[0][0])
-					mr_qty_allowance = frappe.db.get_single_value('Stock Settings', 'mr_qty_allowance')
+							(self.name, d.name),
+						)[0][0]
+					)
+					mr_qty_allowance = frappe.db.get_single_value("Stock Settings", "mr_qty_allowance")
 
 					if mr_qty_allowance:
-						allowed_qty = d.qty + (d.qty * (mr_qty_allowance/100))
+						allowed_qty = d.qty + (d.qty * (mr_qty_allowance / 100))
 						if d.ordered_qty and d.ordered_qty > allowed_qty:
-							frappe.throw(_("The total Issue / Transfer quantity {0} in Material Request {1}  \
-								cannot be greater than allowed requested quantity {2} for Item {3}").format(d.ordered_qty, d.parent, allowed_qty, d.item_code))
+							frappe.throw(
+								_(
+									"The total Issue / Transfer quantity {0} in Material Request {1}  cannot be greater than allowed requested quantity {2} for Item {3}"
+								).format(d.ordered_qty, d.parent, allowed_qty, d.item_code)
+							)
 
 					elif d.ordered_qty and d.ordered_qty > d.stock_qty:
-						frappe.throw(_("The total Issue / Transfer quantity {0} in Material Request {1}  \
-							cannot be greater than requested quantity {2} for Item {3}").format(d.ordered_qty, d.parent, d.qty, d.item_code))
+						frappe.throw(
+							_(
+								"The total Issue / Transfer quantity {0} in Material Request {1} cannot be greater than requested quantity {2} for Item {3}"
+							).format(d.ordered_qty, d.parent, d.qty, d.item_code)
+						)
 
 				elif self.material_request_type == "Manufacture":
-					d.ordered_qty = flt(frappe.db.sql("""select sum(qty)
+					d.ordered_qty = flt(
+						frappe.db.sql(
+							"""select sum(qty)
 						from `tabWork Order` where material_request = %s
 						and material_request_item = %s and docstatus = 1""",
-						(self.name, d.name))[0][0])
+							(self.name, d.name),
+						)[0][0]
+					)
 
 				frappe.db.set_value(d.doctype, d.name, "ordered_qty", d.ordered_qty)
 
-		self._update_percent_field({
-			"target_dt": "Material Request Item",
-			"target_parent_dt": self.doctype,
-			"target_parent_field": "per_ordered",
-			"target_ref_field": "stock_qty",
-			"target_field": "ordered_qty",
-			"name": self.name,
-		}, update_modified)
+		self._update_percent_field(
+			{
+				"target_dt": "Material Request Item",
+				"target_parent_dt": self.doctype,
+				"target_parent_field": "per_ordered",
+				"target_ref_field": "stock_qty",
+				"target_field": "ordered_qty",
+				"name": self.name,
+			},
+			update_modified,
+		)
 
 	def update_requested_qty(self, mr_item_rows=None):
 		"""update requested qty (before ordered_qty is updated)"""
 		item_wh_list = []
 		for d in self.get("items"):
-			if (not mr_item_rows or d.name in mr_item_rows) and [d.item_code, d.warehouse] not in item_wh_list \
-					and d.warehouse and frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 :
+			if (
+				(not mr_item_rows or d.name in mr_item_rows)
+				and [d.item_code, d.warehouse] not in item_wh_list
+				and d.warehouse
+				and frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1
+			):
 				item_wh_list.append([d.item_code, d.warehouse])
 
 		for item_code, warehouse in item_wh_list:
-			update_bin_qty(item_code, warehouse, {
-				"indented_qty": get_indented_qty(item_code, warehouse)
-			})
+			update_bin_qty(item_code, warehouse, {"indented_qty": get_indented_qty(item_code, warehouse)})
 
 	def update_requested_qty_in_production_plan(self):
 		production_plans = []
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if d.production_plan and d.material_request_plan_item:
 				qty = d.qty if self.docstatus == 1 else 0
-				frappe.db.set_value('Material Request Plan Item',
-					d.material_request_plan_item, 'requested_qty', qty)
+				frappe.db.set_value(
+					"Material Request Plan Item", d.material_request_plan_item, "requested_qty", qty
+				)
 
 				if d.production_plan not in production_plans:
 					production_plans.append(d.production_plan)
 
 		for production_plan in production_plans:
-			doc = frappe.get_doc('Production Plan', production_plan)
+			doc = frappe.get_doc("Production Plan", production_plan)
 			doc.set_status()
-			doc.db_set('status', doc.status)
+			doc.db_set("status", doc.status)
+
 
 def update_completed_and_requested_qty(stock_entry, method):
 	if stock_entry.doctype == "Stock Entry":
@@ -240,43 +290,55 @@
 				mr_obj = frappe.get_doc("Material Request", mr)
 
 				if mr_obj.status in ["Stopped", "Cancelled"]:
-					frappe.throw(_("{0} {1} is cancelled or stopped").format(_("Material Request"), mr),
-						frappe.InvalidStatusError)
+					frappe.throw(
+						_("{0} {1} is cancelled or stopped").format(_("Material Request"), mr),
+						frappe.InvalidStatusError,
+					)
 
 				mr_obj.update_completed_qty(mr_item_rows)
 				mr_obj.update_requested_qty(mr_item_rows)
 
+
 def set_missing_values(source, target_doc):
-	if target_doc.doctype == "Purchase Order" and getdate(target_doc.schedule_date) <  getdate(nowdate()):
+	if target_doc.doctype == "Purchase Order" and getdate(target_doc.schedule_date) < getdate(
+		nowdate()
+	):
 		target_doc.schedule_date = None
 	target_doc.run_method("set_missing_values")
 	target_doc.run_method("calculate_taxes_and_totals")
 
+
 def update_item(obj, target, source_parent):
 	target.conversion_factor = obj.conversion_factor
-	target.qty = flt(flt(obj.stock_qty) - flt(obj.ordered_qty))/ target.conversion_factor
-	target.stock_qty = (target.qty * target.conversion_factor)
+	target.qty = flt(flt(obj.stock_qty) - flt(obj.ordered_qty)) / target.conversion_factor
+	target.stock_qty = target.qty * target.conversion_factor
 	if getdate(target.schedule_date) < getdate(nowdate()):
 		target.schedule_date = None
 
+
 def get_list_context(context=None):
 	from erpnext.controllers.website_list_for_contact import get_list_context
+
 	list_context = get_list_context(context)
-	list_context.update({
-		'show_sidebar': True,
-		'show_search': True,
-		'no_breadcrumbs': True,
-		'title': _('Material Request'),
-	})
+	list_context.update(
+		{
+			"show_sidebar": True,
+			"show_search": True,
+			"no_breadcrumbs": True,
+			"title": _("Material Request"),
+		}
+	)
 
 	return list_context
 
+
 @frappe.whitelist()
 def update_status(name, status):
-	material_request = frappe.get_doc('Material Request', name)
-	material_request.check_permission('write')
+	material_request = frappe.get_doc("Material Request", name)
+	material_request.check_permission("write")
 	material_request.update_status(status)
 
+
 @frappe.whitelist()
 def make_purchase_order(source_name, target_doc=None, args=None):
 	if args is None:
@@ -289,7 +351,7 @@
 			# items only for given default supplier
 			supplier_items = []
 			for d in target_doc.items:
-				default_supplier = get_item_defaults(d.item_code, target_doc.company).get('default_supplier')
+				default_supplier = get_item_defaults(d.item_code, target_doc.company).get("default_supplier")
 				if frappe.flags.args.default_supplier == default_supplier:
 					supplier_items.append(d)
 			target_doc.items = supplier_items
@@ -297,58 +359,65 @@
 		set_missing_values(source, target_doc)
 
 	def select_item(d):
-		filtered_items = args.get('filtered_children', [])
+		filtered_items = args.get("filtered_children", [])
 		child_filter = d.name in filtered_items if filtered_items else True
 
 		return d.ordered_qty < d.stock_qty and child_filter
 
-	doclist = get_mapped_doc("Material Request", source_name, {
-		"Material Request": {
-			"doctype": "Purchase Order",
-			"validation": {
-				"docstatus": ["=", 1],
-				"material_request_type": ["=", "Purchase"]
-			}
+	doclist = get_mapped_doc(
+		"Material Request",
+		source_name,
+		{
+			"Material Request": {
+				"doctype": "Purchase Order",
+				"validation": {"docstatus": ["=", 1], "material_request_type": ["=", "Purchase"]},
+			},
+			"Material Request Item": {
+				"doctype": "Purchase Order Item",
+				"field_map": [
+					["name", "material_request_item"],
+					["parent", "material_request"],
+					["uom", "stock_uom"],
+					["uom", "uom"],
+					["sales_order", "sales_order"],
+					["sales_order_item", "sales_order_item"],
+				],
+				"postprocess": update_item,
+				"condition": select_item,
+			},
 		},
-		"Material Request Item": {
-			"doctype": "Purchase Order Item",
-			"field_map": [
-				["name", "material_request_item"],
-				["parent", "material_request"],
-				["uom", "stock_uom"],
-				["uom", "uom"],
-				["sales_order", "sales_order"],
-				["sales_order_item", "sales_order_item"]
-			],
-			"postprocess": update_item,
-			"condition": select_item
-		}
-	}, target_doc, postprocess)
+		target_doc,
+		postprocess,
+	)
 
 	return doclist
 
+
 @frappe.whitelist()
 def make_request_for_quotation(source_name, target_doc=None):
-	doclist = get_mapped_doc("Material Request", source_name, {
-		"Material Request": {
-			"doctype": "Request for Quotation",
-			"validation": {
-				"docstatus": ["=", 1],
-				"material_request_type": ["=", "Purchase"]
-			}
+	doclist = get_mapped_doc(
+		"Material Request",
+		source_name,
+		{
+			"Material Request": {
+				"doctype": "Request for Quotation",
+				"validation": {"docstatus": ["=", 1], "material_request_type": ["=", "Purchase"]},
+			},
+			"Material Request Item": {
+				"doctype": "Request for Quotation Item",
+				"field_map": [
+					["name", "material_request_item"],
+					["parent", "material_request"],
+					["uom", "uom"],
+				],
+			},
 		},
-		"Material Request Item": {
-			"doctype": "Request for Quotation Item",
-			"field_map": [
-				["name", "material_request_item"],
-				["parent", "material_request"],
-				["uom", "uom"]
-			]
-		}
-	}, target_doc)
+		target_doc,
+	)
 
 	return doclist
 
+
 @frappe.whitelist()
 def make_purchase_order_based_on_supplier(source_name, target_doc=None, args=None):
 	mr = source_name
@@ -359,43 +428,59 @@
 		target_doc.supplier = args.get("supplier")
 		if getdate(target_doc.schedule_date) < getdate(nowdate()):
 			target_doc.schedule_date = None
-		target_doc.set("items", [d for d in target_doc.get("items")
-			if d.get("item_code") in supplier_items and d.get("qty") > 0])
+		target_doc.set(
+			"items",
+			[
+				d for d in target_doc.get("items") if d.get("item_code") in supplier_items and d.get("qty") > 0
+			],
+		)
 
 		set_missing_values(source, target_doc)
 
-	target_doc = get_mapped_doc("Material Request", mr, {
-		"Material Request": {
-			"doctype": "Purchase Order",
+	target_doc = get_mapped_doc(
+		"Material Request",
+		mr,
+		{
+			"Material Request": {
+				"doctype": "Purchase Order",
+			},
+			"Material Request Item": {
+				"doctype": "Purchase Order Item",
+				"field_map": [
+					["name", "material_request_item"],
+					["parent", "material_request"],
+					["uom", "stock_uom"],
+					["uom", "uom"],
+				],
+				"postprocess": update_item,
+				"condition": lambda doc: doc.ordered_qty < doc.qty,
+			},
 		},
-		"Material Request Item": {
-			"doctype": "Purchase Order Item",
-			"field_map": [
-				["name", "material_request_item"],
-				["parent", "material_request"],
-				["uom", "stock_uom"],
-				["uom", "uom"]
-			],
-			"postprocess": update_item,
-			"condition": lambda doc: doc.ordered_qty < doc.qty
-		}
-	}, target_doc, postprocess)
+		target_doc,
+		postprocess,
+	)
 
 	return target_doc
 
+
 @frappe.whitelist()
 def get_items_based_on_default_supplier(supplier):
-	supplier_items = [d.parent for d in frappe.db.get_all("Item Default",
-		{"default_supplier": supplier, "parenttype": "Item"}, 'parent')]
+	supplier_items = [
+		d.parent
+		for d in frappe.db.get_all(
+			"Item Default", {"default_supplier": supplier, "parenttype": "Item"}, "parent"
+		)
+	]
 
 	return supplier_items
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_material_requests_based_on_supplier(doctype, txt, searchfield, start, page_len, filters):
 	conditions = ""
 	if txt:
-		conditions += "and mr.name like '%%"+txt+"%%' "
+		conditions += "and mr.name like '%%" + txt + "%%' "
 
 	if filters.get("transaction_date"):
 		date = filters.get("transaction_date")[1]
@@ -407,7 +492,8 @@
 	if not supplier_items:
 		frappe.throw(_("{0} is not the default supplier for any items.").format(supplier))
 
-	material_requests = frappe.db.sql("""select distinct mr.name, transaction_date,company
+	material_requests = frappe.db.sql(
+		"""select distinct mr.name, transaction_date,company
 		from `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
 		where mr.name = mr_item.parent
 			and mr_item.item_code in ({0})
@@ -418,12 +504,16 @@
 			and mr.company = '{1}'
 			{2}
 		order by mr_item.item_code ASC
-		limit {3} offset {4} """ \
-		.format(', '.join(['%s']*len(supplier_items)), filters.get("company"), conditions, page_len, start),
-		tuple(supplier_items), as_dict=1)
+		limit {3} offset {4} """.format(
+			", ".join(["%s"] * len(supplier_items)), filters.get("company"), conditions, page_len, start
+		),
+		tuple(supplier_items),
+		as_dict=1,
+	)
 
 	return material_requests
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_default_supplier_query(doctype, txt, searchfield, start, page_len, filters):
@@ -432,47 +522,63 @@
 	for d in doc.items:
 		item_list.append(d.item_code)
 
-	return frappe.db.sql("""select default_supplier
+	return frappe.db.sql(
+		"""select default_supplier
 		from `tabItem Default`
 		where parent in ({0}) and
 		default_supplier IS NOT NULL
-		""".format(', '.join(['%s']*len(item_list))),tuple(item_list))
+		""".format(
+			", ".join(["%s"] * len(item_list))
+		),
+		tuple(item_list),
+	)
+
 
 @frappe.whitelist()
 def make_supplier_quotation(source_name, target_doc=None):
 	def postprocess(source, target_doc):
 		set_missing_values(source, target_doc)
 
-	doclist = get_mapped_doc("Material Request", source_name, {
-		"Material Request": {
-			"doctype": "Supplier Quotation",
-			"validation": {
-				"docstatus": ["=", 1],
-				"material_request_type": ["=", "Purchase"]
-			}
+	doclist = get_mapped_doc(
+		"Material Request",
+		source_name,
+		{
+			"Material Request": {
+				"doctype": "Supplier Quotation",
+				"validation": {"docstatus": ["=", 1], "material_request_type": ["=", "Purchase"]},
+			},
+			"Material Request Item": {
+				"doctype": "Supplier Quotation Item",
+				"field_map": {
+					"name": "material_request_item",
+					"parent": "material_request",
+					"sales_order": "sales_order",
+				},
+			},
 		},
-		"Material Request Item": {
-			"doctype": "Supplier Quotation Item",
-			"field_map": {
-				"name": "material_request_item",
-				"parent": "material_request",
-				"sales_order": "sales_order"
-			}
-		}
-	}, target_doc, postprocess)
+		target_doc,
+		postprocess,
+	)
 
 	return doclist
 
+
 @frappe.whitelist()
 def make_stock_entry(source_name, target_doc=None):
 	def update_item(obj, target, source_parent):
-		qty = flt(flt(obj.stock_qty) - flt(obj.ordered_qty))/ target.conversion_factor \
-			if flt(obj.stock_qty) > flt(obj.ordered_qty) else 0
+		qty = (
+			flt(flt(obj.stock_qty) - flt(obj.ordered_qty)) / target.conversion_factor
+			if flt(obj.stock_qty) > flt(obj.ordered_qty)
+			else 0
+		)
 		target.qty = qty
 		target.transfer_qty = qty * obj.conversion_factor
 		target.conversion_factor = obj.conversion_factor
 
-		if source_parent.material_request_type == "Material Transfer" or source_parent.material_request_type == "Customer Provided":
+		if (
+			source_parent.material_request_type == "Material Transfer"
+			or source_parent.material_request_type == "Customer Provided"
+		):
 			target.t_warehouse = obj.warehouse
 		else:
 			target.s_warehouse = obj.warehouse
@@ -486,110 +592,128 @@
 	def set_missing_values(source, target):
 		target.purpose = source.material_request_type
 		if source.job_card:
-			target.purpose = 'Material Transfer for Manufacture'
+			target.purpose = "Material Transfer for Manufacture"
 
 		if source.material_request_type == "Customer Provided":
 			target.purpose = "Material Receipt"
 
-		target.run_method("calculate_rate_and_amount")
+		target.set_missing_values()
 		target.set_stock_entry_type()
 		target.set_job_card_data()
 
-	doclist = get_mapped_doc("Material Request", source_name, {
-		"Material Request": {
-			"doctype": "Stock Entry",
-			"validation": {
-				"docstatus": ["=", 1],
-				"material_request_type": ["in", ["Material Transfer", "Material Issue", "Customer Provided"]]
-			}
-		},
-		"Material Request Item": {
-			"doctype": "Stock Entry Detail",
-			"field_map": {
-				"name": "material_request_item",
-				"parent": "material_request",
-				"uom": "stock_uom",
-				"job_card_item": "job_card_item"
+	doclist = get_mapped_doc(
+		"Material Request",
+		source_name,
+		{
+			"Material Request": {
+				"doctype": "Stock Entry",
+				"validation": {
+					"docstatus": ["=", 1],
+					"material_request_type": ["in", ["Material Transfer", "Material Issue", "Customer Provided"]],
+				},
 			},
-			"postprocess": update_item,
-			"condition": lambda doc: doc.ordered_qty < doc.stock_qty
-		}
-	}, target_doc, set_missing_values)
+			"Material Request Item": {
+				"doctype": "Stock Entry Detail",
+				"field_map": {
+					"name": "material_request_item",
+					"parent": "material_request",
+					"uom": "stock_uom",
+					"job_card_item": "job_card_item",
+				},
+				"postprocess": update_item,
+				"condition": lambda doc: doc.ordered_qty < doc.stock_qty,
+			},
+		},
+		target_doc,
+		set_missing_values,
+	)
 
 	return doclist
 
+
 @frappe.whitelist()
 def raise_work_orders(material_request):
-	mr= frappe.get_doc("Material Request", material_request)
-	errors =[]
+	mr = frappe.get_doc("Material Request", material_request)
+	errors = []
 	work_orders = []
-	default_wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_wip_warehouse")
+	default_wip_warehouse = frappe.db.get_single_value(
+		"Manufacturing Settings", "default_wip_warehouse"
+	)
 
 	for d in mr.items:
 		if (d.stock_qty - d.ordered_qty) > 0:
 			if frappe.db.exists("BOM", {"item": d.item_code, "is_default": 1}):
 				wo_order = frappe.new_doc("Work Order")
-				wo_order.update({
-					"production_item": d.item_code,
-					"qty": d.stock_qty - d.ordered_qty,
-					"fg_warehouse": d.warehouse,
-					"wip_warehouse": default_wip_warehouse,
-					"description": d.description,
-					"stock_uom": d.stock_uom,
-					"expected_delivery_date": d.schedule_date,
-					"sales_order": d.sales_order,
-					"sales_order_item": d.get("sales_order_item"),
-					"bom_no": get_item_details(d.item_code).bom_no,
-					"material_request": mr.name,
-					"material_request_item": d.name,
-					"planned_start_date": mr.transaction_date,
-					"company": mr.company
-				})
+				wo_order.update(
+					{
+						"production_item": d.item_code,
+						"qty": d.stock_qty - d.ordered_qty,
+						"fg_warehouse": d.warehouse,
+						"wip_warehouse": default_wip_warehouse,
+						"description": d.description,
+						"stock_uom": d.stock_uom,
+						"expected_delivery_date": d.schedule_date,
+						"sales_order": d.sales_order,
+						"sales_order_item": d.get("sales_order_item"),
+						"bom_no": get_item_details(d.item_code).bom_no,
+						"material_request": mr.name,
+						"material_request_item": d.name,
+						"planned_start_date": mr.transaction_date,
+						"company": mr.company,
+					}
+				)
 
 				wo_order.set_work_order_operations()
 				wo_order.save()
 
 				work_orders.append(wo_order.name)
 			else:
-				errors.append(_("Row {0}: Bill of Materials not found for the Item {1}")
-					.format(d.idx, get_link_to_form("Item", d.item_code)))
+				errors.append(
+					_("Row {0}: Bill of Materials not found for the Item {1}").format(
+						d.idx, get_link_to_form("Item", d.item_code)
+					)
+				)
 
 	if work_orders:
 		work_orders_list = [get_link_to_form("Work Order", d) for d in work_orders]
 
 		if len(work_orders) > 1:
-			msgprint(_("The following {0} were created: {1}")
-				.format(frappe.bold(_("Work Orders")), '<br>' + ', '.join(work_orders_list)))
+			msgprint(
+				_("The following {0} were created: {1}").format(
+					frappe.bold(_("Work Orders")), "<br>" + ", ".join(work_orders_list)
+				)
+			)
 		else:
-			msgprint(_("The {0} {1} created sucessfully")
-				.format(frappe.bold(_("Work Order")), work_orders_list[0]))
+			msgprint(
+				_("The {0} {1} created sucessfully").format(frappe.bold(_("Work Order")), work_orders_list[0])
+			)
 
 	if errors:
-		frappe.throw(_("Work Order cannot be created for following reason: <br> {0}")
-			.format(new_line_sep(errors)))
+		frappe.throw(
+			_("Work Order cannot be created for following reason: <br> {0}").format(new_line_sep(errors))
+		)
 
 	return work_orders
 
+
 @frappe.whitelist()
 def create_pick_list(source_name, target_doc=None):
-	doc = get_mapped_doc('Material Request', source_name, {
-		'Material Request': {
-			'doctype': 'Pick List',
-			'field_map': {
-				'material_request_type': 'purpose'
+	doc = get_mapped_doc(
+		"Material Request",
+		source_name,
+		{
+			"Material Request": {
+				"doctype": "Pick List",
+				"field_map": {"material_request_type": "purpose"},
+				"validation": {"docstatus": ["=", 1]},
 			},
-			'validation': {
-				'docstatus': ['=', 1]
-			}
-		},
-		'Material Request Item': {
-			'doctype': 'Pick List Item',
-			'field_map': {
-				'name': 'material_request_item',
-				'qty': 'stock_qty'
+			"Material Request Item": {
+				"doctype": "Pick List Item",
+				"field_map": {"name": "material_request_item", "qty": "stock_qty"},
 			},
 		},
-	}, target_doc)
+		target_doc,
+	)
 
 	doc.set_item_locations()
 
diff --git a/erpnext/stock/doctype/material_request/material_request_dashboard.py b/erpnext/stock/doctype/material_request/material_request_dashboard.py
index c1ce0a9..b073e6a 100644
--- a/erpnext/stock/doctype/material_request/material_request_dashboard.py
+++ b/erpnext/stock/doctype/material_request/material_request_dashboard.py
@@ -3,20 +3,13 @@
 
 def get_data():
 	return {
-		'fieldname': 'material_request',
-		'transactions': [
+		"fieldname": "material_request",
+		"transactions": [
 			{
-				'label': _('Reference'),
-				'items': ['Request for Quotation', 'Supplier Quotation', 'Purchase Order']
+				"label": _("Reference"),
+				"items": ["Request for Quotation", "Supplier Quotation", "Purchase Order"],
 			},
-			{
-				'label': _('Stock'),
-				'items': ['Stock Entry', 'Purchase Receipt', 'Pick List']
-
-			},
-			{
-				'label': _('Manufacturing'),
-				'items': ['Work Order']
-			}
-		]
+			{"label": _("Stock"), "items": ["Stock Entry", "Purchase Receipt", "Pick List"]},
+			{"label": _("Manufacturing"), "items": ["Work Order"]},
+		],
 	}
diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py
index 1cda781..78af153 100644
--- a/erpnext/stock/doctype/material_request/test_material_request.py
+++ b/erpnext/stock/doctype/material_request/test_material_request.py
@@ -22,8 +22,7 @@
 	def test_make_purchase_order(self):
 		mr = frappe.copy_doc(test_records[0]).insert()
 
-		self.assertRaises(frappe.ValidationError, make_purchase_order,
-			mr.name)
+		self.assertRaises(frappe.ValidationError, make_purchase_order, mr.name)
 
 		mr = frappe.get_doc("Material Request", mr.name)
 		mr.submit()
@@ -44,7 +43,6 @@
 		self.assertEqual(sq.doctype, "Supplier Quotation")
 		self.assertEqual(len(sq.get("items")), len(mr.get("items")))
 
-
 	def test_make_stock_entry(self):
 		mr = frappe.copy_doc(test_records[0]).insert()
 
@@ -58,42 +56,44 @@
 		self.assertEqual(se.doctype, "Stock Entry")
 		self.assertEqual(len(se.get("items")), len(mr.get("items")))
 
-	def _insert_stock_entry(self, qty1, qty2, warehouse = None ):
-		se = frappe.get_doc({
-			"company": "_Test Company",
-			"doctype": "Stock Entry",
-			"posting_date": "2013-03-01",
-			"posting_time": "00:00:00",
-			"purpose": "Material Receipt",
-			"items": [
-				{
-					"conversion_factor": 1.0,
-					"doctype": "Stock Entry Detail",
-					"item_code": "_Test Item Home Desktop 100",
-					"parentfield": "items",
-					"basic_rate": 100,
-					"qty": qty1,
-					"stock_uom": "_Test UOM 1",
-					"transfer_qty": qty1,
-					"uom": "_Test UOM 1",
-					"t_warehouse": warehouse or "_Test Warehouse 1 - _TC",
-					"cost_center": "_Test Cost Center - _TC"
-				},
-				{
-					"conversion_factor": 1.0,
-					"doctype": "Stock Entry Detail",
-					"item_code": "_Test Item Home Desktop 200",
-					"parentfield": "items",
-					"basic_rate": 100,
-					"qty": qty2,
-					"stock_uom": "_Test UOM 1",
-					"transfer_qty": qty2,
-					"uom": "_Test UOM 1",
-					"t_warehouse": warehouse or "_Test Warehouse 1 - _TC",
-					"cost_center": "_Test Cost Center - _TC"
-				}
-			]
-		})
+	def _insert_stock_entry(self, qty1, qty2, warehouse=None):
+		se = frappe.get_doc(
+			{
+				"company": "_Test Company",
+				"doctype": "Stock Entry",
+				"posting_date": "2013-03-01",
+				"posting_time": "00:00:00",
+				"purpose": "Material Receipt",
+				"items": [
+					{
+						"conversion_factor": 1.0,
+						"doctype": "Stock Entry Detail",
+						"item_code": "_Test Item Home Desktop 100",
+						"parentfield": "items",
+						"basic_rate": 100,
+						"qty": qty1,
+						"stock_uom": "_Test UOM 1",
+						"transfer_qty": qty1,
+						"uom": "_Test UOM 1",
+						"t_warehouse": warehouse or "_Test Warehouse 1 - _TC",
+						"cost_center": "_Test Cost Center - _TC",
+					},
+					{
+						"conversion_factor": 1.0,
+						"doctype": "Stock Entry Detail",
+						"item_code": "_Test Item Home Desktop 200",
+						"parentfield": "items",
+						"basic_rate": 100,
+						"qty": qty2,
+						"stock_uom": "_Test UOM 1",
+						"transfer_qty": qty2,
+						"uom": "_Test UOM 1",
+						"t_warehouse": warehouse or "_Test Warehouse 1 - _TC",
+						"cost_center": "_Test Cost Center - _TC",
+					},
+				],
+			}
+		)
 
 		se.set_stock_entry_type()
 		se.insert()
@@ -106,19 +106,19 @@
 
 		mr.load_from_db()
 		mr.cancel()
-		self.assertRaises(frappe.ValidationError, mr.update_status, 'Stopped')
+		self.assertRaises(frappe.ValidationError, mr.update_status, "Stopped")
 
 	def test_mr_changes_from_stopped_to_pending_after_reopen(self):
 		mr = frappe.copy_doc(test_records[0])
 		mr.insert()
 		mr.submit()
-		self.assertEqual('Pending', mr.status)
+		self.assertEqual("Pending", mr.status)
 
-		mr.update_status('Stopped')
-		self.assertEqual('Stopped', mr.status)
+		mr.update_status("Stopped")
+		self.assertEqual("Stopped", mr.status)
 
-		mr.update_status('Submitted')
-		self.assertEqual('Pending', mr.status)
+		mr.update_status("Submitted")
+		self.assertEqual("Pending", mr.status)
 
 	def test_cannot_submit_cancelled_mr(self):
 		mr = frappe.copy_doc(test_records[0])
@@ -133,7 +133,7 @@
 		mr.insert()
 		mr.submit()
 		mr.cancel()
-		self.assertEqual('Cancelled', mr.status)
+		self.assertEqual("Cancelled", mr.status)
 
 	def test_cannot_change_cancelled_mr(self):
 		mr = frappe.copy_doc(test_records[0])
@@ -142,12 +142,12 @@
 		mr.load_from_db()
 		mr.cancel()
 
-		self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Draft')
-		self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Stopped')
-		self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Ordered')
-		self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Issued')
-		self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Transferred')
-		self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Pending')
+		self.assertRaises(frappe.InvalidStatusError, mr.update_status, "Draft")
+		self.assertRaises(frappe.InvalidStatusError, mr.update_status, "Stopped")
+		self.assertRaises(frappe.InvalidStatusError, mr.update_status, "Ordered")
+		self.assertRaises(frappe.InvalidStatusError, mr.update_status, "Issued")
+		self.assertRaises(frappe.InvalidStatusError, mr.update_status, "Transferred")
+		self.assertRaises(frappe.InvalidStatusError, mr.update_status, "Pending")
 
 	def test_cannot_submit_deleted_material_request(self):
 		mr = frappe.copy_doc(test_records[0])
@@ -169,9 +169,9 @@
 		mr.submit()
 		mr.load_from_db()
 
-		mr.update_status('Stopped')
-		mr.update_status('Submitted')
-		self.assertEqual(mr.status, 'Pending')
+		mr.update_status("Stopped")
+		mr.update_status("Submitted")
+		self.assertEqual(mr.status, "Pending")
 
 	def test_pending_mr_changes_to_stopped_after_stop(self):
 		mr = frappe.copy_doc(test_records[0])
@@ -179,17 +179,21 @@
 		mr.submit()
 		mr.load_from_db()
 
-		mr.update_status('Stopped')
-		self.assertEqual(mr.status, 'Stopped')
+		mr.update_status("Stopped")
+		self.assertEqual(mr.status, "Stopped")
 
 	def test_cannot_stop_unsubmitted_mr(self):
 		mr = frappe.copy_doc(test_records[0])
 		mr.insert()
-		self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Stopped')
+		self.assertRaises(frappe.InvalidStatusError, mr.update_status, "Stopped")
 
 	def test_completed_qty_for_purchase(self):
-		existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
-		existing_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
+		existing_requested_qty_item1 = self._get_requested_qty(
+			"_Test Item Home Desktop 100", "_Test Warehouse - _TC"
+		)
+		existing_requested_qty_item2 = self._get_requested_qty(
+			"_Test Item Home Desktop 200", "_Test Warehouse - _TC"
+		)
 
 		# submit material request of type Purchase
 		mr = frappe.copy_doc(test_records[0])
@@ -206,19 +210,18 @@
 		po_doc.get("items")[0].schedule_date = "2013-07-09"
 		po_doc.get("items")[1].schedule_date = "2013-07-09"
 
-
 		# check for stopped status of Material Request
 		po = frappe.copy_doc(po_doc)
 		po.insert()
 		po.load_from_db()
-		mr.update_status('Stopped')
+		mr.update_status("Stopped")
 		self.assertRaises(frappe.InvalidStatusError, po.submit)
 		frappe.db.set(po, "docstatus", 1)
 		self.assertRaises(frappe.InvalidStatusError, po.cancel)
 
 		# resubmit and check for per complete
 		mr.load_from_db()
-		mr.update_status('Submitted')
+		mr.update_status("Submitted")
 		po = frappe.copy_doc(po_doc)
 		po.insert()
 		po.submit()
@@ -229,8 +232,12 @@
 		self.assertEqual(mr.get("items")[0].ordered_qty, 27.0)
 		self.assertEqual(mr.get("items")[1].ordered_qty, 1.5)
 
-		current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
-		current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
+		current_requested_qty_item1 = self._get_requested_qty(
+			"_Test Item Home Desktop 100", "_Test Warehouse - _TC"
+		)
+		current_requested_qty_item2 = self._get_requested_qty(
+			"_Test Item Home Desktop 200", "_Test Warehouse - _TC"
+		)
 
 		self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 27.0)
 		self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 1.5)
@@ -242,15 +249,23 @@
 		self.assertEqual(mr.get("items")[0].ordered_qty, 0)
 		self.assertEqual(mr.get("items")[1].ordered_qty, 0)
 
-		current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
-		current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
+		current_requested_qty_item1 = self._get_requested_qty(
+			"_Test Item Home Desktop 100", "_Test Warehouse - _TC"
+		)
+		current_requested_qty_item2 = self._get_requested_qty(
+			"_Test Item Home Desktop 200", "_Test Warehouse - _TC"
+		)
 
 		self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
 		self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
 
 	def test_completed_qty_for_transfer(self):
-		existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
-		existing_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
+		existing_requested_qty_item1 = self._get_requested_qty(
+			"_Test Item Home Desktop 100", "_Test Warehouse - _TC"
+		)
+		existing_requested_qty_item2 = self._get_requested_qty(
+			"_Test Item Home Desktop 200", "_Test Warehouse - _TC"
+		)
 
 		# submit material request of type Purchase
 		mr = frappe.copy_doc(test_records[0])
@@ -264,31 +279,31 @@
 		self.assertEqual(mr.get("items")[0].ordered_qty, 0)
 		self.assertEqual(mr.get("items")[1].ordered_qty, 0)
 
-		current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
-		current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
+		current_requested_qty_item1 = self._get_requested_qty(
+			"_Test Item Home Desktop 100", "_Test Warehouse - _TC"
+		)
+		current_requested_qty_item2 = self._get_requested_qty(
+			"_Test Item Home Desktop 200", "_Test Warehouse - _TC"
+		)
 
 		self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
 		self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
 
 		# map a stock entry
 		se_doc = make_stock_entry(mr.name)
-		se_doc.update({
-			"posting_date": "2013-03-01",
-			"posting_time": "01:00",
-			"fiscal_year": "_Test Fiscal Year 2013",
-		})
-		se_doc.get("items")[0].update({
-			"qty": 27.0,
-			"transfer_qty": 27.0,
-			"s_warehouse": "_Test Warehouse 1 - _TC",
-			"basic_rate": 1.0
-		})
-		se_doc.get("items")[1].update({
-			"qty": 1.5,
-			"transfer_qty": 1.5,
-			"s_warehouse": "_Test Warehouse 1 - _TC",
-			"basic_rate": 1.0
-		})
+		se_doc.update(
+			{
+				"posting_date": "2013-03-01",
+				"posting_time": "01:00",
+				"fiscal_year": "_Test Fiscal Year 2013",
+			}
+		)
+		se_doc.get("items")[0].update(
+			{"qty": 27.0, "transfer_qty": 27.0, "s_warehouse": "_Test Warehouse 1 - _TC", "basic_rate": 1.0}
+		)
+		se_doc.get("items")[1].update(
+			{"qty": 1.5, "transfer_qty": 1.5, "s_warehouse": "_Test Warehouse 1 - _TC", "basic_rate": 1.0}
+		)
 
 		# make available the qty in _Test Warehouse 1 before transfer
 		self._insert_stock_entry(27.0, 1.5)
@@ -296,17 +311,17 @@
 		# check for stopped status of Material Request
 		se = frappe.copy_doc(se_doc)
 		se.insert()
-		mr.update_status('Stopped')
+		mr.update_status("Stopped")
 		self.assertRaises(frappe.InvalidStatusError, se.submit)
 
-		mr.update_status('Submitted')
+		mr.update_status("Submitted")
 
 		se.flags.ignore_validate_update_after_submit = True
 		se.submit()
-		mr.update_status('Stopped')
+		mr.update_status("Stopped")
 		self.assertRaises(frappe.InvalidStatusError, se.cancel)
 
-		mr.update_status('Submitted')
+		mr.update_status("Submitted")
 		se = frappe.copy_doc(se_doc)
 		se.insert()
 		se.submit()
@@ -317,8 +332,12 @@
 		self.assertEqual(mr.get("items")[0].ordered_qty, 27.0)
 		self.assertEqual(mr.get("items")[1].ordered_qty, 1.5)
 
-		current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
-		current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
+		current_requested_qty_item1 = self._get_requested_qty(
+			"_Test Item Home Desktop 100", "_Test Warehouse - _TC"
+		)
+		current_requested_qty_item2 = self._get_requested_qty(
+			"_Test Item Home Desktop 200", "_Test Warehouse - _TC"
+		)
 
 		self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 27.0)
 		self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 1.5)
@@ -330,56 +349,70 @@
 		self.assertEqual(mr.get("items")[0].ordered_qty, 0)
 		self.assertEqual(mr.get("items")[1].ordered_qty, 0)
 
-		current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
-		current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
+		current_requested_qty_item1 = self._get_requested_qty(
+			"_Test Item Home Desktop 100", "_Test Warehouse - _TC"
+		)
+		current_requested_qty_item2 = self._get_requested_qty(
+			"_Test Item Home Desktop 200", "_Test Warehouse - _TC"
+		)
 
 		self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
 		self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
 
 	def test_over_transfer_qty_allowance(self):
-		mr = frappe.new_doc('Material Request')
+		mr = frappe.new_doc("Material Request")
 		mr.company = "_Test Company"
 		mr.scheduled_date = today()
-		mr.append('items',{
-			"item_code": "_Test FG Item",
-			"item_name": "_Test FG Item",
-			"qty": 10,
-			"schedule_date": today(),
-			"uom": "_Test UOM 1",
-			"warehouse": "_Test Warehouse - _TC"
-		})
+		mr.append(
+			"items",
+			{
+				"item_code": "_Test FG Item",
+				"item_name": "_Test FG Item",
+				"qty": 10,
+				"schedule_date": today(),
+				"uom": "_Test UOM 1",
+				"warehouse": "_Test Warehouse - _TC",
+			},
+		)
 
 		mr.material_request_type = "Material Transfer"
 		mr.insert()
 		mr.submit()
 
-		frappe.db.set_value('Stock Settings', None, 'mr_qty_allowance', 20)
+		frappe.db.set_value("Stock Settings", None, "mr_qty_allowance", 20)
 
 		# map a stock entry
 
 		se_doc = make_stock_entry(mr.name)
-		se_doc.update({
-			"posting_date": today(),
-			"posting_time": "00:00",
-		})
-		se_doc.get("items")[0].update({
-			"qty": 13,
-			"transfer_qty": 12.0,
-			"s_warehouse": "_Test Warehouse - _TC",
-			"t_warehouse": "_Test Warehouse 1 - _TC",
-			"basic_rate": 1.0
-		})
+		se_doc.update(
+			{
+				"posting_date": today(),
+				"posting_time": "00:00",
+			}
+		)
+		se_doc.get("items")[0].update(
+			{
+				"qty": 13,
+				"transfer_qty": 12.0,
+				"s_warehouse": "_Test Warehouse - _TC",
+				"t_warehouse": "_Test Warehouse 1 - _TC",
+				"basic_rate": 1.0,
+			}
+		)
 
 		# make available the qty in _Test Warehouse 1 before transfer
 		sr = frappe.new_doc("Stock Reconciliation")
 		sr.company = "_Test Company"
 		sr.purpose = "Opening Stock"
-		sr.append('items', {
-			"item_code": "_Test FG Item",
-			"warehouse": "_Test Warehouse - _TC",
-			"qty": 20,
-			"valuation_rate": 0.01
-		})
+		sr.append(
+			"items",
+			{
+				"item_code": "_Test FG Item",
+				"warehouse": "_Test Warehouse - _TC",
+				"qty": 20,
+				"valuation_rate": 0.01,
+			},
+		)
 		sr.insert()
 		sr.submit()
 		se = frappe.copy_doc(se_doc)
@@ -389,8 +422,12 @@
 		se.submit()
 
 	def test_completed_qty_for_over_transfer(self):
-		existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
-		existing_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
+		existing_requested_qty_item1 = self._get_requested_qty(
+			"_Test Item Home Desktop 100", "_Test Warehouse - _TC"
+		)
+		existing_requested_qty_item2 = self._get_requested_qty(
+			"_Test Item Home Desktop 200", "_Test Warehouse - _TC"
+		)
 
 		# submit material request of type Purchase
 		mr = frappe.copy_doc(test_records[0])
@@ -401,23 +438,19 @@
 		# map a stock entry
 
 		se_doc = make_stock_entry(mr.name)
-		se_doc.update({
-			"posting_date": "2013-03-01",
-			"posting_time": "00:00",
-			"fiscal_year": "_Test Fiscal Year 2013",
-		})
-		se_doc.get("items")[0].update({
-			"qty": 54.0,
-			"transfer_qty": 54.0,
-			"s_warehouse": "_Test Warehouse 1 - _TC",
-			"basic_rate": 1.0
-		})
-		se_doc.get("items")[1].update({
-			"qty": 3.0,
-			"transfer_qty": 3.0,
-			"s_warehouse": "_Test Warehouse 1 - _TC",
-			"basic_rate": 1.0
-		})
+		se_doc.update(
+			{
+				"posting_date": "2013-03-01",
+				"posting_time": "00:00",
+				"fiscal_year": "_Test Fiscal Year 2013",
+			}
+		)
+		se_doc.get("items")[0].update(
+			{"qty": 54.0, "transfer_qty": 54.0, "s_warehouse": "_Test Warehouse 1 - _TC", "basic_rate": 1.0}
+		)
+		se_doc.get("items")[1].update(
+			{"qty": 3.0, "transfer_qty": 3.0, "s_warehouse": "_Test Warehouse 1 - _TC", "basic_rate": 1.0}
+		)
 
 		# make available the qty in _Test Warehouse 1 before transfer
 		self._insert_stock_entry(60.0, 3.0)
@@ -426,11 +459,11 @@
 		se = frappe.copy_doc(se_doc)
 		se.set_stock_entry_type()
 		se.insert()
-		mr.update_status('Stopped')
+		mr.update_status("Stopped")
 		self.assertRaises(frappe.InvalidStatusError, se.submit)
 		self.assertRaises(frappe.InvalidStatusError, se.cancel)
 
-		mr.update_status('Submitted')
+		mr.update_status("Submitted")
 		se = frappe.copy_doc(se_doc)
 		se.set_stock_entry_type()
 		se.insert()
@@ -443,8 +476,12 @@
 		self.assertEqual(mr.get("items")[0].ordered_qty, 54.0)
 		self.assertEqual(mr.get("items")[1].ordered_qty, 3.0)
 
-		current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
-		current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
+		current_requested_qty_item1 = self._get_requested_qty(
+			"_Test Item Home Desktop 100", "_Test Warehouse - _TC"
+		)
+		current_requested_qty_item2 = self._get_requested_qty(
+			"_Test Item Home Desktop 200", "_Test Warehouse - _TC"
+		)
 
 		self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1)
 		self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2)
@@ -456,8 +493,12 @@
 		self.assertEqual(mr.get("items")[0].ordered_qty, 0)
 		self.assertEqual(mr.get("items")[1].ordered_qty, 0)
 
-		current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
-		current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
+		current_requested_qty_item1 = self._get_requested_qty(
+			"_Test Item Home Desktop 100", "_Test Warehouse - _TC"
+		)
+		current_requested_qty_item2 = self._get_requested_qty(
+			"_Test Item Home Desktop 200", "_Test Warehouse - _TC"
+		)
 
 		self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
 		self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
@@ -470,25 +511,31 @@
 		mr.submit()
 
 		se_doc = make_stock_entry(mr.name)
-		se_doc.update({
-			"posting_date": "2013-03-01",
-			"posting_time": "00:00",
-			"fiscal_year": "_Test Fiscal Year 2013",
-		})
-		se_doc.get("items")[0].update({
-			"qty": 60.0,
-			"transfer_qty": 60.0,
-			"s_warehouse": "_Test Warehouse - _TC",
-			"t_warehouse": "_Test Warehouse 1 - _TC",
-			"basic_rate": 1.0
-		})
-		se_doc.get("items")[1].update({
-			"item_code": "_Test Item Home Desktop 100",
-			"qty": 3.0,
-			"transfer_qty": 3.0,
-			"s_warehouse": "_Test Warehouse 1 - _TC",
-			"basic_rate": 1.0
-		})
+		se_doc.update(
+			{
+				"posting_date": "2013-03-01",
+				"posting_time": "00:00",
+				"fiscal_year": "_Test Fiscal Year 2013",
+			}
+		)
+		se_doc.get("items")[0].update(
+			{
+				"qty": 60.0,
+				"transfer_qty": 60.0,
+				"s_warehouse": "_Test Warehouse - _TC",
+				"t_warehouse": "_Test Warehouse 1 - _TC",
+				"basic_rate": 1.0,
+			}
+		)
+		se_doc.get("items")[1].update(
+			{
+				"item_code": "_Test Item Home Desktop 100",
+				"qty": 3.0,
+				"transfer_qty": 3.0,
+				"s_warehouse": "_Test Warehouse 1 - _TC",
+				"basic_rate": 1.0,
+			}
+		)
 
 		# check for stopped status of Material Request
 		se = frappe.copy_doc(se_doc)
@@ -505,18 +552,20 @@
 
 	def test_warehouse_company_validation(self):
 		from erpnext.stock.utils import InvalidWarehouseCompany
+
 		mr = frappe.copy_doc(test_records[0])
 		mr.company = "_Test Company 1"
 		self.assertRaises(InvalidWarehouseCompany, mr.insert)
 
 	def _get_requested_qty(self, item_code, warehouse):
-		return flt(frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "indented_qty"))
+		return flt(
+			frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "indented_qty")
+		)
 
 	def test_make_stock_entry_for_material_issue(self):
 		mr = frappe.copy_doc(test_records[0]).insert()
 
-		self.assertRaises(frappe.ValidationError, make_stock_entry,
-			mr.name)
+		self.assertRaises(frappe.ValidationError, make_stock_entry, mr.name)
 
 		mr = frappe.get_doc("Material Request", mr.name)
 		mr.material_request_type = "Material Issue"
@@ -528,8 +577,13 @@
 
 	def test_completed_qty_for_issue(self):
 		def _get_requested_qty():
-			return flt(frappe.db.get_value("Bin", {"item_code": "_Test Item Home Desktop 100",
-				"warehouse": "_Test Warehouse - _TC"}, "indented_qty"))
+			return flt(
+				frappe.db.get_value(
+					"Bin",
+					{"item_code": "_Test Item Home Desktop 100", "warehouse": "_Test Warehouse - _TC"},
+					"indented_qty",
+				)
+			)
 
 		existing_requested_qty = _get_requested_qty()
 
@@ -537,7 +591,7 @@
 		mr.material_request_type = "Material Issue"
 		mr.submit()
 
-		#testing bin value after material request is submitted
+		# testing bin value after material request is submitted
 		self.assertEqual(_get_requested_qty(), existing_requested_qty - 54.0)
 
 		# receive items to allow issue
@@ -556,7 +610,7 @@
 		self.assertEqual(mr.get("items")[0].ordered_qty, 54.0)
 		self.assertEqual(mr.get("items")[1].ordered_qty, 3.0)
 
-		#testing bin requested qty after issuing stock against material request
+		# testing bin requested qty after issuing stock against material request
 		self.assertEqual(_get_requested_qty(), existing_requested_qty)
 
 	def test_material_request_type_manufacture(self):
@@ -564,8 +618,11 @@
 		mr = frappe.get_doc("Material Request", mr.name)
 		mr.submit()
 		completed_qty = mr.items[0].ordered_qty
-		requested_qty = frappe.db.sql("""select indented_qty from `tabBin` where \
-			item_code= %s and warehouse= %s """, (mr.items[0].item_code, mr.items[0].warehouse))[0][0]
+		requested_qty = frappe.db.sql(
+			"""select indented_qty from `tabBin` where \
+			item_code= %s and warehouse= %s """,
+			(mr.items[0].item_code, mr.items[0].warehouse),
+		)[0][0]
 
 		prod_order = raise_work_orders(mr.name)
 		po = frappe.get_doc("Work Order", prod_order[0])
@@ -575,8 +632,11 @@
 		mr = frappe.get_doc("Material Request", mr.name)
 		self.assertEqual(completed_qty + po.qty, mr.items[0].ordered_qty)
 
-		new_requested_qty = frappe.db.sql("""select indented_qty from `tabBin` where \
-			item_code= %s and warehouse= %s """, (mr.items[0].item_code, mr.items[0].warehouse))[0][0]
+		new_requested_qty = frappe.db.sql(
+			"""select indented_qty from `tabBin` where \
+			item_code= %s and warehouse= %s """,
+			(mr.items[0].item_code, mr.items[0].warehouse),
+		)[0][0]
 
 		self.assertEqual(requested_qty - po.qty, new_requested_qty)
 
@@ -585,17 +645,24 @@
 		mr = frappe.get_doc("Material Request", mr.name)
 		self.assertEqual(completed_qty, mr.items[0].ordered_qty)
 
-		new_requested_qty = frappe.db.sql("""select indented_qty from `tabBin` where \
-			item_code= %s and warehouse= %s """, (mr.items[0].item_code, mr.items[0].warehouse))[0][0]
+		new_requested_qty = frappe.db.sql(
+			"""select indented_qty from `tabBin` where \
+			item_code= %s and warehouse= %s """,
+			(mr.items[0].item_code, mr.items[0].warehouse),
+		)[0][0]
 		self.assertEqual(requested_qty, new_requested_qty)
 
 	def test_requested_qty_multi_uom(self):
-		existing_requested_qty = self._get_requested_qty('_Test FG Item', '_Test Warehouse - _TC')
+		existing_requested_qty = self._get_requested_qty("_Test FG Item", "_Test Warehouse - _TC")
 
-		mr = make_material_request(item_code='_Test FG Item', material_request_type='Manufacture',
-			uom="_Test UOM 1", conversion_factor=12)
+		mr = make_material_request(
+			item_code="_Test FG Item",
+			material_request_type="Manufacture",
+			uom="_Test UOM 1",
+			conversion_factor=12,
+		)
 
-		requested_qty = self._get_requested_qty('_Test FG Item', '_Test Warehouse - _TC')
+		requested_qty = self._get_requested_qty("_Test FG Item", "_Test Warehouse - _TC")
 
 		self.assertEqual(requested_qty, existing_requested_qty + 120)
 
@@ -605,42 +672,36 @@
 		wo.wip_warehouse = "_Test Warehouse 1 - _TC"
 		wo.submit()
 
-		requested_qty = self._get_requested_qty('_Test FG Item', '_Test Warehouse - _TC')
+		requested_qty = self._get_requested_qty("_Test FG Item", "_Test Warehouse - _TC")
 		self.assertEqual(requested_qty, existing_requested_qty + 70)
 
 		wo.cancel()
 
-		requested_qty = self._get_requested_qty('_Test FG Item', '_Test Warehouse - _TC')
+		requested_qty = self._get_requested_qty("_Test FG Item", "_Test Warehouse - _TC")
 		self.assertEqual(requested_qty, existing_requested_qty + 120)
 
 		mr.reload()
 		mr.cancel()
-		requested_qty = self._get_requested_qty('_Test FG Item', '_Test Warehouse - _TC')
+		requested_qty = self._get_requested_qty("_Test FG Item", "_Test Warehouse - _TC")
 		self.assertEqual(requested_qty, existing_requested_qty)
 
-
 	def test_multi_uom_for_purchase(self):
 		mr = frappe.copy_doc(test_records[0])
-		mr.material_request_type = 'Purchase'
+		mr.material_request_type = "Purchase"
 		item = mr.items[0]
 		mr.schedule_date = today()
 
-		if not frappe.db.get_value('UOM Conversion Detail',
-			 {'parent': item.item_code, 'uom': 'Kg'}):
-			 item_doc = frappe.get_doc('Item', item.item_code)
-			 item_doc.append('uoms', {
-				 'uom': 'Kg',
-				 'conversion_factor': 5
-			 })
-			 item_doc.save(ignore_permissions=True)
+		if not frappe.db.get_value("UOM Conversion Detail", {"parent": item.item_code, "uom": "Kg"}):
+			item_doc = frappe.get_doc("Item", item.item_code)
+			item_doc.append("uoms", {"uom": "Kg", "conversion_factor": 5})
+			item_doc.save(ignore_permissions=True)
 
-		item.uom = 'Kg'
+		item.uom = "Kg"
 		for item in mr.items:
 			item.schedule_date = mr.schedule_date
 
 		mr.insert()
-		self.assertRaises(frappe.ValidationError, make_purchase_order,
-			mr.name)
+		self.assertRaises(frappe.ValidationError, make_purchase_order, mr.name)
 
 		mr = frappe.get_doc("Material Request", mr.name)
 		mr.submit()
@@ -654,17 +715,19 @@
 		self.assertEqual(po.doctype, "Purchase Order")
 		self.assertEqual(len(po.get("items")), len(mr.get("items")))
 
-		po.supplier = '_Test Supplier'
+		po.supplier = "_Test Supplier"
 		po.insert()
 		po.submit()
 		mr = frappe.get_doc("Material Request", mr.name)
 		self.assertEqual(mr.per_ordered, 100)
 
 	def test_customer_provided_parts_mr(self):
-		create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
+		create_item(
+			"CUST-0987", is_customer_provided_item=1, customer="_Test Customer", is_purchase_item=0
+		)
 		existing_requested_qty = self._get_requested_qty("_Test Customer", "_Test Warehouse - _TC")
 
-		mr = make_material_request(item_code='CUST-0987', material_request_type='Customer Provided')
+		mr = make_material_request(item_code="CUST-0987", material_request_type="Customer Provided")
 		se = make_stock_entry(mr.name)
 		se.insert()
 		se.submit()
@@ -677,25 +740,30 @@
 		self.assertEqual(mr.per_ordered, 100)
 		self.assertEqual(existing_requested_qty, current_requested_qty)
 
+
 def make_material_request(**args):
 	args = frappe._dict(args)
 	mr = frappe.new_doc("Material Request")
 	mr.material_request_type = args.material_request_type or "Purchase"
 	mr.company = args.company or "_Test Company"
-	mr.customer = args.customer or '_Test Customer'
-	mr.append("items", {
-		"item_code": args.item_code or "_Test Item",
-		"qty": args.qty or 10,
-		"uom": args.uom or "_Test UOM",
-		"conversion_factor": args.conversion_factor or 1,
-		"schedule_date": args.schedule_date or today(),
-		"warehouse": args.warehouse or "_Test Warehouse - _TC",
-		"cost_center": args.cost_center or "_Test Cost Center - _TC"
-	})
+	mr.customer = args.customer or "_Test Customer"
+	mr.append(
+		"items",
+		{
+			"item_code": args.item_code or "_Test Item",
+			"qty": args.qty or 10,
+			"uom": args.uom or "_Test UOM",
+			"conversion_factor": args.conversion_factor or 1,
+			"schedule_date": args.schedule_date or today(),
+			"warehouse": args.warehouse or "_Test Warehouse - _TC",
+			"cost_center": args.cost_center or "_Test Cost Center - _TC",
+		},
+	)
 	mr.insert()
 	if not args.do_not_submit:
 		mr.submit()
 	return mr
 
+
 test_dependencies = ["Currency Exchange", "BOM"]
-test_records = frappe.get_test_records('Material Request')
+test_records = frappe.get_test_records("Material Request")
diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json
index 2bad42a..dd66cff 100644
--- a/erpnext/stock/doctype/material_request_item/material_request_item.json
+++ b/erpnext/stock/doctype/material_request_item/material_request_item.json
@@ -177,6 +177,7 @@
    "fieldtype": "Column Break"
   },
   {
+   "allow_on_submit": 1,
    "bold": 1,
    "columns": 2,
    "fieldname": "schedule_date",
@@ -459,7 +460,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-11-03 14:40:24.409826",
+ "modified": "2022-03-10 18:42:42.705190",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Material Request Item",
diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.py b/erpnext/stock/doctype/material_request_item/material_request_item.py
index 32407d0..08c9ed2 100644
--- a/erpnext/stock/doctype/material_request_item/material_request_item.py
+++ b/erpnext/stock/doctype/material_request_item/material_request_item.py
@@ -11,5 +11,6 @@
 class MaterialRequestItem(Document):
 	pass
 
+
 def on_doctype_update():
 	frappe.db.add_index("Material Request Item", ["item_code", "warehouse"])
diff --git a/erpnext/stock/doctype/packed_item/packed_item.json b/erpnext/stock/doctype/packed_item/packed_item.json
index d6e2e9c..cb8eb30 100644
--- a/erpnext/stock/doctype/packed_item/packed_item.json
+++ b/erpnext/stock/doctype/packed_item/packed_item.json
@@ -29,6 +29,7 @@
   "ordered_qty",
   "column_break_16",
   "incoming_rate",
+  "picked_qty",
   "page_break",
   "prevdoc_doctype",
   "parent_detail_docname"
@@ -223,6 +224,7 @@
    "fieldtype": "Currency",
    "in_list_view": 1,
    "label": "Rate",
+   "options": "currency",
    "print_hide": 1,
    "read_only": 1
   },
@@ -233,13 +235,20 @@
    "label": "Ordered Qty",
    "no_copy": 1,
    "read_only": 1
+  },
+  {
+   "fieldname": "picked_qty",
+   "fieldtype": "Float",
+   "label": "Picked Qty",
+   "no_copy": 1,
+   "read_only": 1
   }
  ],
  "idx": 1,
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2022-02-22 12:57:45.325488",
+ "modified": "2022-04-27 05:23:08.683245",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Packed Item",
diff --git a/erpnext/stock/doctype/packed_item/packed_item.py b/erpnext/stock/doctype/packed_item/packed_item.py
index 07c2f1f..4d05d7a 100644
--- a/erpnext/stock/doctype/packed_item/packed_item.py
+++ b/erpnext/stock/doctype/packed_item/packed_item.py
@@ -23,19 +23,23 @@
 		return
 
 	parent_items_price, reset = {}, False
-	set_price_from_children = frappe.db.get_single_value("Selling Settings", "editable_bundle_item_rates")
+	set_price_from_children = frappe.db.get_single_value(
+		"Selling Settings", "editable_bundle_item_rates"
+	)
 
 	stale_packed_items_table = get_indexed_packed_items_table(doc)
 
 	reset = reset_packing_list(doc)
 
 	for item_row in doc.get("items"):
-		if frappe.db.exists("Product Bundle", {"new_item_code": item_row.item_code}):
+		if is_product_bundle(item_row.item_code):
 			for bundle_item in get_product_bundle_items(item_row.item_code):
 				pi_row = add_packed_item_row(
-					doc=doc, packing_item=bundle_item,
-					main_item_row=item_row, packed_items_table=stale_packed_items_table,
-					reset=reset
+					doc=doc,
+					packing_item=bundle_item,
+					main_item_row=item_row,
+					packed_items_table=stale_packed_items_table,
+					reset=reset,
 				)
 				item_data = get_packed_item_details(bundle_item.item_code, doc.company)
 				update_packed_item_basic_data(item_row, pi_row, bundle_item, item_data)
@@ -43,18 +47,23 @@
 				update_packed_item_price_data(pi_row, item_data, doc)
 				update_packed_item_from_cancelled_doc(item_row, bundle_item, pi_row, doc)
 
-				if set_price_from_children: # create/update bundle item wise price dict
+				if set_price_from_children:  # create/update bundle item wise price dict
 					update_product_bundle_rate(parent_items_price, pi_row)
 
 	if parent_items_price:
-		set_product_bundle_rate_amount(doc, parent_items_price) # set price in bundle item
+		set_product_bundle_rate_amount(doc, parent_items_price)  # set price in bundle item
+
+
+def is_product_bundle(item_code: str) -> bool:
+	return bool(frappe.db.exists("Product Bundle", {"new_item_code": item_code}))
+
 
 def get_indexed_packed_items_table(doc):
 	"""
-		Create dict from stale packed items table like:
-		{(Parent Item 1, Bundle Item 1, ae4b5678): {...}, (key): {value}}
+	Create dict from stale packed items table like:
+	{(Parent Item 1, Bundle Item 1, ae4b5678): {...}, (key): {value}}
 
-		Use: to quickly retrieve/check if row existed in table instead of looping n times
+	Use: to quickly retrieve/check if row existed in table instead of looping n times
 	"""
 	indexed_table = {}
 	for packed_item in doc.get("packed_items"):
@@ -63,6 +72,7 @@
 
 	return indexed_table
 
+
 def reset_packing_list(doc):
 	"Conditionally reset the table and return if it was reset or not."
 	reset_table = False
@@ -86,33 +96,34 @@
 		doc.set("packed_items", [])
 	return reset_table
 
+
 def get_product_bundle_items(item_code):
 	product_bundle = frappe.qb.DocType("Product Bundle")
 	product_bundle_item = frappe.qb.DocType("Product Bundle Item")
 
 	query = (
 		frappe.qb.from_(product_bundle_item)
-		.join(product_bundle).on(product_bundle_item.parent == product_bundle.name)
+		.join(product_bundle)
+		.on(product_bundle_item.parent == product_bundle.name)
 		.select(
 			product_bundle_item.item_code,
 			product_bundle_item.qty,
 			product_bundle_item.uom,
-			product_bundle_item.description
-		).where(
-			product_bundle.new_item_code == item_code
-		).orderby(
-			product_bundle_item.idx
+			product_bundle_item.description,
 		)
+		.where(product_bundle.new_item_code == item_code)
+		.orderby(product_bundle_item.idx)
 	)
 	return query.run(as_dict=True)
 
+
 def add_packed_item_row(doc, packing_item, main_item_row, packed_items_table, reset):
 	"""Add and return packed item row.
-		doc: Transaction document
-		packing_item (dict): Packed Item details
-		main_item_row (dict): Items table row corresponding to packed item
-		packed_items_table (dict): Packed Items table before save (indexed)
-		reset (bool): State if table is reset or preserved as is
+	doc: Transaction document
+	packing_item (dict): Packed Item details
+	main_item_row (dict): Items table row corresponding to packed item
+	packed_items_table (dict): Packed Items table before save (indexed)
+	reset (bool): State if table is reset or preserved as is
 	"""
 	exists, pi_row = False, {}
 
@@ -122,33 +133,34 @@
 		pi_row, exists = packed_items_table.get(key), True
 
 	if not exists:
-		pi_row = doc.append('packed_items', {})
-	elif reset: # add row if row exists but table is reset
+		pi_row = doc.append("packed_items", {})
+	elif reset:  # add row if row exists but table is reset
 		pi_row.idx, pi_row.name = None, None
-		pi_row = doc.append('packed_items', pi_row)
+		pi_row = doc.append("packed_items", pi_row)
 
 	return pi_row
 
+
 def get_packed_item_details(item_code, company):
 	item = frappe.qb.DocType("Item")
 	item_default = frappe.qb.DocType("Item Default")
 	query = (
 		frappe.qb.from_(item)
 		.left_join(item_default)
-		.on(
-			(item_default.parent == item.name)
-			& (item_default.company == company)
-		).select(
-			item.item_name, item.is_stock_item,
-			item.description, item.stock_uom,
+		.on((item_default.parent == item.name) & (item_default.company == company))
+		.select(
+			item.item_name,
+			item.is_stock_item,
+			item.description,
+			item.stock_uom,
 			item.valuation_rate,
-			item_default.default_warehouse
-		).where(
-			item.name == item_code
+			item_default.default_warehouse,
 		)
+		.where(item.name == item_code)
 	)
 	return query.run(as_dict=True)[0]
 
+
 def update_packed_item_basic_data(main_item_row, pi_row, packing_item, item_data):
 	pi_row.parent_item = main_item_row.item_code
 	pi_row.parent_detail_docname = main_item_row.name
@@ -161,12 +173,16 @@
 	if not pi_row.description:
 		pi_row.description = packing_item.get("description")
 
+
 def update_packed_item_stock_data(main_item_row, pi_row, packing_item, item_data, doc):
 	# TODO batch_no, actual_batch_qty, incoming_rate
 	if not pi_row.warehouse and not doc.amended_from:
-		fetch_warehouse = (doc.get('is_pos') or item_data.is_stock_item or not item_data.default_warehouse)
-		pi_row.warehouse = (main_item_row.warehouse if (fetch_warehouse and main_item_row.warehouse)
-			else item_data.default_warehouse)
+		fetch_warehouse = doc.get("is_pos") or item_data.is_stock_item or not item_data.default_warehouse
+		pi_row.warehouse = (
+			main_item_row.warehouse
+			if (fetch_warehouse and main_item_row.warehouse)
+			else item_data.default_warehouse
+		)
 
 	if not pi_row.target_warehouse:
 		pi_row.target_warehouse = main_item_row.get("target_warehouse")
@@ -175,6 +191,7 @@
 	pi_row.actual_qty = flt(bin.get("actual_qty"))
 	pi_row.projected_qty = flt(bin.get("projected_qty"))
 
+
 def update_packed_item_price_data(pi_row, item_data, doc):
 	"Set price as per price list or from the Item master."
 	if pi_row.rate:
@@ -182,49 +199,60 @@
 
 	item_doc = frappe.get_cached_doc("Item", pi_row.item_code)
 	row_data = pi_row.as_dict().copy()
-	row_data.update({
-		"company": doc.get("company"),
-		"price_list": doc.get("selling_price_list"),
-		"currency": doc.get("currency")
-	})
+	row_data.update(
+		{
+			"company": doc.get("company"),
+			"price_list": doc.get("selling_price_list"),
+			"currency": doc.get("currency"),
+			"conversion_rate": doc.get("conversion_rate"),
+		}
+	)
 	rate = get_price_list_rate(row_data, item_doc).get("price_list_rate")
 
 	pi_row.rate = rate or item_data.get("valuation_rate") or 0.0
 
+
 def update_packed_item_from_cancelled_doc(main_item_row, packing_item, pi_row, doc):
 	"Update packed item row details from cancelled doc into amended doc."
 	prev_doc_packed_items_map = None
 	if doc.amended_from:
 		prev_doc_packed_items_map = get_cancelled_doc_packed_item_details(doc.packed_items)
 
-	if prev_doc_packed_items_map and prev_doc_packed_items_map.get((packing_item.item_code, main_item_row.item_code)):
+	if prev_doc_packed_items_map and prev_doc_packed_items_map.get(
+		(packing_item.item_code, main_item_row.item_code)
+	):
 		prev_doc_row = prev_doc_packed_items_map.get((packing_item.item_code, main_item_row.item_code))
 		pi_row.batch_no = prev_doc_row[0].batch_no
 		pi_row.serial_no = prev_doc_row[0].serial_no
 		pi_row.warehouse = prev_doc_row[0].warehouse
 
+
 def get_packed_item_bin_qty(item, warehouse):
 	bin_data = frappe.db.get_values(
 		"Bin",
 		fieldname=["actual_qty", "projected_qty"],
 		filters={"item_code": item, "warehouse": warehouse},
-		as_dict=True
+		as_dict=True,
 	)
 
 	return bin_data[0] if bin_data else {}
 
+
 def get_cancelled_doc_packed_item_details(old_packed_items):
 	prev_doc_packed_items_map = {}
 	for items in old_packed_items:
-		prev_doc_packed_items_map.setdefault((items.item_code ,items.parent_item), []).append(items.as_dict())
+		prev_doc_packed_items_map.setdefault((items.item_code, items.parent_item), []).append(
+			items.as_dict()
+		)
 	return prev_doc_packed_items_map
 
+
 def update_product_bundle_rate(parent_items_price, pi_row):
 	"""
-		Update the price dict of Product Bundles based on the rates of the Items in the bundle.
+	Update the price dict of Product Bundles based on the rates of the Items in the bundle.
 
-		Stucture:
-		{(Bundle Item 1, ae56fgji): 150.0, (Bundle Item 2, bc78fkjo): 200.0}
+	Stucture:
+	{(Bundle Item 1, ae56fgji): 150.0, (Bundle Item 2, bc78fkjo): 200.0}
 	"""
 	key = (pi_row.parent_item, pi_row.parent_detail_docname)
 	rate = parent_items_price.get(key)
@@ -233,6 +261,7 @@
 
 	parent_items_price[key] += flt(pi_row.rate)
 
+
 def set_product_bundle_rate_amount(doc, parent_items_price):
 	"Set cumulative rate and amount in bundle item."
 	for item in doc.get("items"):
@@ -241,6 +270,7 @@
 			item.rate = bundle_rate
 			item.amount = flt(bundle_rate * item.qty)
 
+
 def on_doctype_update():
 	frappe.db.add_index("Packed Item", ["item_code", "warehouse"])
 
@@ -251,10 +281,7 @@
 
 	bundled_items = get_product_bundle_items(row["item_code"])
 	for item in bundled_items:
-		row.update({
-			"item_code": item.item_code,
-			"qty": flt(row["quantity"]) * flt(item.qty)
-		})
+		row.update({"item_code": item.item_code, "qty": flt(row["quantity"]) * flt(item.qty)})
 		items.append(get_item_details(row))
 
 	return items
diff --git a/erpnext/stock/doctype/packed_item/test_packed_item.py b/erpnext/stock/doctype/packed_item/test_packed_item.py
index 94268a8..ad7fd9a 100644
--- a/erpnext/stock/doctype/packed_item/test_packed_item.py
+++ b/erpnext/stock/doctype/packed_item/test_packed_item.py
@@ -1,10 +1,12 @@
 # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 
+from typing import List, Optional, Tuple
+
+import frappe
 from frappe.tests.utils import FrappeTestCase, change_settings
 from frappe.utils import add_to_date, nowdate
 
-from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
 from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note
 from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
 from erpnext.stock.doctype.item.test_item import make_item
@@ -12,25 +14,49 @@
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 
 
+def create_product_bundle(
+	quantities: Optional[List[int]] = None, warehouse: Optional[str] = None
+) -> Tuple[str, List[str]]:
+	"""Get a new product_bundle for use in tests.
+
+	Create 10x required stock if warehouse is specified.
+	"""
+	if not quantities:
+		quantities = [2, 2]
+
+	bundle = make_item(properties={"is_stock_item": 0}).name
+
+	bundle_doc = frappe.get_doc({"doctype": "Product Bundle", "new_item_code": bundle})
+
+	components = []
+	for qty in quantities:
+		compoenent = make_item().name
+		components.append(compoenent)
+		bundle_doc.append("items", {"item_code": compoenent, "qty": qty})
+		if warehouse:
+			make_stock_entry(item=compoenent, to_warehouse=warehouse, qty=10 * qty, rate=100)
+
+	bundle_doc.insert()
+
+	return bundle, components
+
+
 class TestPackedItem(FrappeTestCase):
 	"Test impact on Packed Items table in various scenarios."
+
 	@classmethod
 	def setUpClass(cls) -> None:
 		super().setUpClass()
-		cls.bundle = "_Test Product Bundle X"
-		cls.bundle_items = ["_Test Bundle Item 1", "_Test Bundle Item 2"]
-		make_item(cls.bundle, {"is_stock_item": 0})
-		for item in cls.bundle_items:
-			make_item(item, {"is_stock_item": 1})
+		cls.warehouse = "_Test Warehouse - _TC"
 
-		make_item("_Test Normal Stock Item", {"is_stock_item": 1})
+		cls.bundle, cls.bundle_items = create_product_bundle(warehouse=cls.warehouse)
+		cls.bundle2, cls.bundle2_items = create_product_bundle(warehouse=cls.warehouse)
 
-		make_product_bundle(cls.bundle, cls.bundle_items, qty=2)
+		cls.normal_item = make_item().name
 
 	def test_adding_bundle_item(self):
 		"Test impact on packed items if bundle item row is added."
-		so = make_sales_order(item_code = self.bundle, qty=1,
-			do_not_submit=True)
+		so = make_sales_order(item_code=self.bundle, qty=1, do_not_submit=True)
 
 		self.assertEqual(so.items[0].qty, 1)
 		self.assertEqual(len(so.packed_items), 2)
@@ -41,14 +67,14 @@
 		"Test impact on packed items if bundle item row is updated."
 		so = make_sales_order(item_code=self.bundle, qty=1, do_not_submit=True)
 
-		so.items[0].qty = 2 # change qty
+		so.items[0].qty = 2  # change qty
 		so.save()
 
 		self.assertEqual(so.packed_items[0].qty, 4)
 		self.assertEqual(so.packed_items[1].qty, 4)
 
 		# change item code to non bundle item
-		so.items[0].item_code = "_Test Normal Stock Item"
+		so.items[0].item_code = self.normal_item
 		so.save()
 
 		self.assertEqual(len(so.packed_items), 0)
@@ -57,12 +83,9 @@
 		"Test impact on packed items if same bundle item is added and removed."
 		so_items = []
 		for qty in [2, 4, 6, 8]:
-			so_items.append({
-				"item_code": self.bundle,
-				"qty": qty,
-				"rate": 400,
-				"warehouse": "_Test Warehouse - _TC"
-			})
+			so_items.append(
+				{"item_code": self.bundle, "qty": qty, "rate": 400, "warehouse": "_Test Warehouse - _TC"}
+			)
 
 		# create SO with recurring bundle item
 		so = make_sales_order(item_list=so_items, do_not_submit=True)
@@ -110,18 +133,15 @@
 		"Test impact on packed items in newly mapped DN from SO."
 		so_items = []
 		for qty in [2, 4]:
-			so_items.append({
-				"item_code": self.bundle,
-				"qty": qty,
-				"rate": 400,
-				"warehouse": "_Test Warehouse - _TC"
-			})
+			so_items.append(
+				{"item_code": self.bundle, "qty": qty, "rate": 400, "warehouse": "_Test Warehouse - _TC"}
+			)
 
 		# create SO with recurring bundle item
 		so = make_sales_order(item_list=so_items)
 
 		dn = make_delivery_note(so.name)
-		dn.items[1].qty = 3 # change second row qty for inserting doc
+		dn.items[1].qty = 3  # change second row qty for inserting doc
 		dn.save()
 
 		self.assertEqual(len(dn.packed_items), 4)
@@ -138,7 +158,7 @@
 		for item in self.bundle_items:
 			make_stock_entry(item_code=item, to_warehouse=warehouse, qty=10, rate=100, posting_date=today)
 
-		so = make_sales_order(item_code = self.bundle, qty=1, company=company, warehouse=warehouse)
+		so = make_sales_order(item_code=self.bundle, qty=1, company=company, warehouse=warehouse)
 
 		dn = make_delivery_note(so.name)
 		dn.save()
@@ -149,10 +169,111 @@
 
 		# backdated stock entry
 		for item in self.bundle_items:
-			make_stock_entry(item_code=item, to_warehouse=warehouse, qty=10, rate=200, posting_date=yesterday)
+			make_stock_entry(
+				item_code=item, to_warehouse=warehouse, qty=10, rate=200, posting_date=yesterday
+			)
 
 		# assert correct reposting
 		gles = get_gl_entries(dn.doctype, dn.name)
 		credit_after_reposting = sum(gle.credit for gle in gles)
 		self.assertNotEqual(credit_before_repost, credit_after_reposting)
 		self.assertAlmostEqual(credit_after_reposting, 2 * credit_before_repost)
+
+	def assertReturns(self, original, returned):
+		self.assertEqual(len(original), len(returned))
+
+		sort_function = lambda p: (p.parent_item, p.item_code, p.qty)
+
+		for sent, returned in zip(
+			sorted(original, key=sort_function), sorted(returned, key=sort_function)
+		):
+			self.assertEqual(sent.item_code, returned.item_code)
+			self.assertEqual(sent.parent_item, returned.parent_item)
+			self.assertEqual(sent.qty, -1 * returned.qty)
+
+	def test_returning_full_bundles(self):
+		from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return
+
+		item_list = [
+			{
+				"item_code": self.bundle,
+				"warehouse": self.warehouse,
+				"qty": 1,
+				"rate": 100,
+			},
+			{
+				"item_code": self.bundle2,
+				"warehouse": self.warehouse,
+				"qty": 1,
+				"rate": 100,
+			},
+		]
+		so = make_sales_order(item_list=item_list, warehouse=self.warehouse)
+
+		dn = make_delivery_note(so.name)
+		dn.save()
+		dn.submit()
+
+		# create return
+		dn_ret = make_sales_return(dn.name)
+		dn_ret.save()
+		dn_ret.submit()
+		self.assertReturns(dn.packed_items, dn_ret.packed_items)
+
+	def test_returning_partial_bundles(self):
+		from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return
+
+		item_list = [
+			{
+				"item_code": self.bundle,
+				"warehouse": self.warehouse,
+				"qty": 1,
+				"rate": 100,
+			},
+			{
+				"item_code": self.bundle2,
+				"warehouse": self.warehouse,
+				"qty": 1,
+				"rate": 100,
+			},
+		]
+		so = make_sales_order(item_list=item_list, warehouse=self.warehouse)
+
+		dn = make_delivery_note(so.name)
+		dn.save()
+		dn.submit()
+
+		# create return
+		dn_ret = make_sales_return(dn.name)
+		# remove bundle 2
+		dn_ret.items.pop()
+
+		dn_ret.save()
+		dn_ret.submit()
+		dn_ret.reload()
+
+		self.assertTrue(all(d.parent_item == self.bundle for d in dn_ret.packed_items))
+
+		expected_returns = [d for d in dn.packed_items if d.parent_item == self.bundle]
+		self.assertReturns(expected_returns, dn_ret.packed_items)
+
+	def test_returning_partial_bundle_qty(self):
+		from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return
+
+		so = make_sales_order(item_code=self.bundle, warehouse=self.warehouse, qty=2)
+
+		dn = make_delivery_note(so.name)
+		dn.save()
+		dn.submit()
+
+		# create return
+		dn_ret = make_sales_return(dn.name)
+		# halve the qty
+		dn_ret.items[0].qty = -1
+		dn_ret.save()
+		dn_ret.submit()
+
+		expected_returns = dn.packed_items
+		for d in expected_returns:
+			d.qty /= 2
+		self.assertReturns(expected_returns, dn_ret.packed_items)
diff --git a/erpnext/stock/doctype/packing_slip/packing_slip.py b/erpnext/stock/doctype/packing_slip/packing_slip.py
index b092862..e5b9de8 100644
--- a/erpnext/stock/doctype/packing_slip/packing_slip.py
+++ b/erpnext/stock/doctype/packing_slip/packing_slip.py
@@ -10,14 +10,13 @@
 
 
 class PackingSlip(Document):
-
 	def validate(self):
 		"""
-			* Validate existence of submitted Delivery Note
-			* Case nos do not overlap
-			* Check if packed qty doesn't exceed actual qty of delivery note
+		* Validate existence of submitted Delivery Note
+		* Case nos do not overlap
+		* Check if packed qty doesn't exceed actual qty of delivery note
 
-			It is necessary to validate case nos before checking quantity
+		It is necessary to validate case nos before checking quantity
 		"""
 		self.validate_delivery_note()
 		self.validate_items_mandatory()
@@ -25,12 +24,13 @@
 		self.validate_qty()
 
 		from erpnext.utilities.transaction_base import validate_uom_is_integer
+
 		validate_uom_is_integer(self, "stock_uom", "qty")
 		validate_uom_is_integer(self, "weight_uom", "net_weight")
 
 	def validate_delivery_note(self):
 		"""
-			Validates if delivery note has status as draft
+		Validates if delivery note has status as draft
 		"""
 		if cint(frappe.db.get_value("Delivery Note", self.delivery_note, "docstatus")) != 0:
 			frappe.throw(_("Delivery Note {0} must not be submitted").format(self.delivery_note))
@@ -42,27 +42,33 @@
 
 	def validate_case_nos(self):
 		"""
-			Validate if case nos overlap. If they do, recommend next case no.
+		Validate if case nos overlap. If they do, recommend next case no.
 		"""
 		if not cint(self.from_case_no):
 			frappe.msgprint(_("Please specify a valid 'From Case No.'"), raise_exception=1)
 		elif not self.to_case_no:
 			self.to_case_no = self.from_case_no
 		elif cint(self.from_case_no) > cint(self.to_case_no):
-			frappe.msgprint(_("'To Case No.' cannot be less than 'From Case No.'"),
-				raise_exception=1)
+			frappe.msgprint(_("'To Case No.' cannot be less than 'From Case No.'"), raise_exception=1)
 
-		res = frappe.db.sql("""SELECT name FROM `tabPacking Slip`
+		res = frappe.db.sql(
+			"""SELECT name FROM `tabPacking Slip`
 			WHERE delivery_note = %(delivery_note)s AND docstatus = 1 AND
 			((from_case_no BETWEEN %(from_case_no)s AND %(to_case_no)s)
 			OR (to_case_no BETWEEN %(from_case_no)s AND %(to_case_no)s)
 			OR (%(from_case_no)s BETWEEN from_case_no AND to_case_no))
-			""", {"delivery_note":self.delivery_note,
-				"from_case_no":self.from_case_no,
-				"to_case_no":self.to_case_no})
+			""",
+			{
+				"delivery_note": self.delivery_note,
+				"from_case_no": self.from_case_no,
+				"to_case_no": self.to_case_no,
+			},
+		)
 
 		if res:
-			frappe.throw(_("""Case No(s) already in use. Try from Case No {0}""").format(self.get_recommended_case_no()))
+			frappe.throw(
+				_("""Case No(s) already in use. Try from Case No {0}""").format(self.get_recommended_case_no())
+			)
 
 	def validate_qty(self):
 		"""Check packed qty across packing slips and delivery note"""
@@ -70,36 +76,37 @@
 		dn_details, ps_item_qty, no_of_cases = self.get_details_for_packing()
 
 		for item in dn_details:
-			new_packed_qty = (flt(ps_item_qty[item['item_code']]) * no_of_cases) + \
-			 	flt(item['packed_qty'])
-			if new_packed_qty > flt(item['qty']) and no_of_cases:
+			new_packed_qty = (flt(ps_item_qty[item["item_code"]]) * no_of_cases) + flt(item["packed_qty"])
+			if new_packed_qty > flt(item["qty"]) and no_of_cases:
 				self.recommend_new_qty(item, ps_item_qty, no_of_cases)
 
-
 	def get_details_for_packing(self):
 		"""
-			Returns
-			* 'Delivery Note Items' query result as a list of dict
-			* Item Quantity dict of current packing slip doc
-			* No. of Cases of this packing slip
+		Returns
+		* 'Delivery Note Items' query result as a list of dict
+		* Item Quantity dict of current packing slip doc
+		* No. of Cases of this packing slip
 		"""
 
 		rows = [d.item_code for d in self.get("items")]
 
 		# also pick custom fields from delivery note
-		custom_fields = ', '.join('dni.`{0}`'.format(d.fieldname)
+		custom_fields = ", ".join(
+			"dni.`{0}`".format(d.fieldname)
 			for d in frappe.get_meta("Delivery Note Item").get_custom_fields()
-			if d.fieldtype not in no_value_fields)
+			if d.fieldtype not in no_value_fields
+		)
 
 		if custom_fields:
-			custom_fields = ', ' + custom_fields
+			custom_fields = ", " + custom_fields
 
 		condition = ""
 		if rows:
-			condition = " and item_code in (%s)" % (", ".join(["%s"]*len(rows)))
+			condition = " and item_code in (%s)" % (", ".join(["%s"] * len(rows)))
 
 		# gets item code, qty per item code, latest packed qty per item code and stock uom
-		res = frappe.db.sql("""select item_code, sum(qty) as qty,
+		res = frappe.db.sql(
+			"""select item_code, sum(qty) as qty,
 			(select sum(psi.qty * (abs(ps.to_case_no - ps.from_case_no) + 1))
 				from `tabPacking Slip` ps, `tabPacking Slip Item` psi
 				where ps.name = psi.parent and ps.docstatus = 1
@@ -107,47 +114,57 @@
 			stock_uom, item_name, description, dni.batch_no {custom_fields}
 			from `tabDelivery Note Item` dni
 			where parent=%s {condition}
-			group by item_code""".format(condition=condition, custom_fields=custom_fields),
-			tuple([self.delivery_note] + rows), as_dict=1)
+			group by item_code""".format(
+				condition=condition, custom_fields=custom_fields
+			),
+			tuple([self.delivery_note] + rows),
+			as_dict=1,
+		)
 
 		ps_item_qty = dict([[d.item_code, d.qty] for d in self.get("items")])
 		no_of_cases = cint(self.to_case_no) - cint(self.from_case_no) + 1
 
 		return res, ps_item_qty, no_of_cases
 
-
 	def recommend_new_qty(self, item, ps_item_qty, no_of_cases):
 		"""
-			Recommend a new quantity and raise a validation exception
+		Recommend a new quantity and raise a validation exception
 		"""
-		item['recommended_qty'] = (flt(item['qty']) - flt(item['packed_qty'])) / no_of_cases
-		item['specified_qty'] = flt(ps_item_qty[item['item_code']])
-		if not item['packed_qty']: item['packed_qty'] = 0
+		item["recommended_qty"] = (flt(item["qty"]) - flt(item["packed_qty"])) / no_of_cases
+		item["specified_qty"] = flt(ps_item_qty[item["item_code"]])
+		if not item["packed_qty"]:
+			item["packed_qty"] = 0
 
-		frappe.throw(_("Quantity for Item {0} must be less than {1}").format(item.get("item_code"), item.get("recommended_qty")))
+		frappe.throw(
+			_("Quantity for Item {0} must be less than {1}").format(
+				item.get("item_code"), item.get("recommended_qty")
+			)
+		)
 
 	def update_item_details(self):
 		"""
-			Fill empty columns in Packing Slip Item
+		Fill empty columns in Packing Slip Item
 		"""
 		if not self.from_case_no:
 			self.from_case_no = self.get_recommended_case_no()
 
 		for d in self.get("items"):
-			res = frappe.db.get_value("Item", d.item_code,
-				["weight_per_unit", "weight_uom"], as_dict=True)
+			res = frappe.db.get_value("Item", d.item_code, ["weight_per_unit", "weight_uom"], as_dict=True)
 
-			if res and len(res)>0:
+			if res and len(res) > 0:
 				d.net_weight = res["weight_per_unit"]
 				d.weight_uom = res["weight_uom"]
 
 	def get_recommended_case_no(self):
 		"""
-			Returns the next case no. for a new packing slip for a delivery
-			note
+		Returns the next case no. for a new packing slip for a delivery
+		note
 		"""
-		recommended_case_no = frappe.db.sql("""SELECT MAX(to_case_no) FROM `tabPacking Slip`
-			WHERE delivery_note = %s AND docstatus=1""", self.delivery_note)
+		recommended_case_no = frappe.db.sql(
+			"""SELECT MAX(to_case_no) FROM `tabPacking Slip`
+			WHERE delivery_note = %s AND docstatus=1""",
+			self.delivery_note,
+		)
 
 		return cint(recommended_case_no[0][0]) + 1
 
@@ -160,7 +177,7 @@
 		dn_details = self.get_details_for_packing()[0]
 		for item in dn_details:
 			if flt(item.qty) > flt(item.packed_qty):
-				ch = self.append('items', {})
+				ch = self.append("items", {})
 				ch.item_code = item.item_code
 				ch.item_name = item.item_name
 				ch.stock_uom = item.stock_uom
@@ -175,14 +192,18 @@
 
 		self.update_item_details()
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def item_details(doctype, txt, searchfield, start, page_len, filters):
 	from erpnext.controllers.queries import get_match_cond
-	return frappe.db.sql("""select name, item_name, description from `tabItem`
+
+	return frappe.db.sql(
+		"""select name, item_name, description from `tabItem`
 				where name in ( select item_code FROM `tabDelivery Note Item`
 	 						where parent= %s)
 	 			and %s like "%s" %s
-	 			limit  %s, %s """ % ("%s", searchfield, "%s",
-	 			get_match_cond(doctype), "%s", "%s"),
-	 			((filters or {}).get("delivery_note"), "%%%s%%" % txt, start, page_len))
+	 			limit  %s offset %s """
+		% ("%s", searchfield, "%s", get_match_cond(doctype), "%s", "%s"),
+		((filters or {}).get("delivery_note"), "%%%s%%" % txt, page_len, start),
+	)
diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js
index 730fd7a..799406c 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.js
+++ b/erpnext/stock/doctype/pick_list/pick_list.js
@@ -146,10 +146,6 @@
 			customer: frm.doc.customer
 		};
 		frm.get_items_btn = frm.add_custom_button(__('Get Items'), () => {
-			if (!frm.doc.customer) {
-				frappe.msgprint(__('Please select Customer first'));
-				return;
-			}
 			erpnext.utils.map_current_doc({
 				method: 'erpnext.selling.doctype.sales_order.sales_order.create_pick_list',
 				source_doctype: 'Sales Order',
@@ -162,6 +158,19 @@
 				get_query_filters: get_query_filters
 			});
 		});
+	},
+	scan_barcode: (frm) => {
+		const opts = {
+			frm,
+			items_table_name: 'locations',
+			qty_field: 'picked_qty',
+			max_qty_field: 'qty',
+			dont_allow_new_row: true,
+			prompt_qty: frm.doc.prompt_qty,
+			serial_no_field: "not_supported",  // doesn't make sense for picklist without a separate field.
+		};
+		const barcode_scanner = new erpnext.utils.BarcodeScanner(opts);
+		barcode_scanner.process_scan();
 	}
 });
 
diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json
index c604c71..ff20909 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.json
+++ b/erpnext/stock/doctype/pick_list/pick_list.json
@@ -17,6 +17,11 @@
   "parent_warehouse",
   "get_item_locations",
   "section_break_6",
+  "scan_barcode",
+  "column_break_13",
+  "scan_mode",
+  "prompt_qty",
+  "section_break_15",
   "locations",
   "amended_from",
   "print_settings_section",
@@ -36,6 +41,7 @@
    "fieldtype": "Column Break"
   },
   {
+   "depends_on": "eval:!doc.docstatus",
    "fieldname": "section_break_6",
    "fieldtype": "Section Break"
   },
@@ -114,6 +120,7 @@
    "set_only_once": 1
   },
   {
+   "collapsible": 1,
    "fieldname": "print_settings_section",
    "fieldtype": "Section Break",
    "label": "Print Settings"
@@ -125,11 +132,38 @@
    "fieldtype": "Check",
    "label": "Group Same Items",
    "print_hide": 1
+  },
+  {
+   "fieldname": "section_break_15",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "scan_barcode",
+   "fieldtype": "Data",
+   "label": "Scan Barcode",
+   "options": "Barcode"
+  },
+  {
+   "fieldname": "column_break_13",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "description": "If checked, picked qty won't automatically be fulfilled on submit of pick list.",
+   "fieldname": "scan_mode",
+   "fieldtype": "Check",
+   "label": "Scan Mode"
+  },
+  {
+   "default": "0",
+   "fieldname": "prompt_qty",
+   "fieldtype": "Check",
+   "label": "Prompt Qty"
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2021-10-05 15:08:40.369957",
+ "modified": "2022-05-11 09:09:53.029312",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Pick List",
@@ -199,5 +233,6 @@
  ],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "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 5484a11..d31d695 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -3,12 +3,15 @@
 
 import json
 from collections import OrderedDict, defaultdict
+from itertools import groupby
+from typing import Dict, List, Set
 
 import frappe
 from frappe import _
 from frappe.model.document import Document
 from frappe.model.mapper import map_child_doc
 from frappe.utils import cint, floor, flt, today
+from frappe.utils.nestedset import get_descendants_of
 
 from erpnext.selling.doctype.sales_order.sales_order import (
 	make_delivery_note as create_delivery_note_from_sales_order,
@@ -17,6 +20,7 @@
 
 # TODO: Prioritize SO or WO group warehouse
 
+
 class PickList(Document):
 	def validate(self):
 		self.validate_for_qty()
@@ -24,18 +28,95 @@
 	def before_save(self):
 		self.set_item_locations()
 
+		# set percentage picked in SO
+		for location in self.get("locations"):
+			if (
+				location.sales_order
+				and frappe.db.get_value("Sales Order", location.sales_order, "per_picked") == 100
+			):
+				frappe.throw(
+					_("Row #{}: item {} has been picked already.").format(location.idx, location.item_code)
+				)
+
 	def before_submit(self):
+		update_sales_orders = set()
 		for item in self.locations:
-			if not frappe.get_cached_value('Item', item.item_code, 'has_serial_no'):
+			if self.scan_mode and item.picked_qty < item.stock_qty:
+				frappe.throw(
+					_(
+						"Row {0} picked quantity is less than the required quantity, additional {1} {2} required."
+					).format(item.idx, item.stock_qty - item.picked_qty, item.stock_uom),
+					title=_("Pick List Incomplete"),
+				)
+			elif not self.scan_mode and item.picked_qty == 0:
+				# if the user has not entered any picked qty, set it to stock_qty, before submit
+				item.picked_qty = item.stock_qty
+
+			if item.sales_order_item:
+				# update the picked_qty in SO Item
+				self.update_sales_order_item(item, item.picked_qty, item.item_code)
+				update_sales_orders.add(item.sales_order)
+
+			if not frappe.get_cached_value("Item", item.item_code, "has_serial_no"):
 				continue
 			if not item.serial_no:
-				frappe.throw(_("Row #{0}: {1} does not have any available serial numbers in {2}").format(
-					frappe.bold(item.idx), frappe.bold(item.item_code), frappe.bold(item.warehouse)),
-					title=_("Serial Nos Required"))
-			if len(item.serial_no.split('\n')) == item.picked_qty:
+				frappe.throw(
+					_("Row #{0}: {1} does not have any available serial numbers in {2}").format(
+						frappe.bold(item.idx), frappe.bold(item.item_code), frappe.bold(item.warehouse)
+					),
+					title=_("Serial Nos Required"),
+				)
+			if len(item.serial_no.split("\n")) == item.picked_qty:
 				continue
-			frappe.throw(_('For item {0} at row {1}, count of serial numbers does not match with the picked quantity')
-				.format(frappe.bold(item.item_code), frappe.bold(item.idx)), title=_("Quantity Mismatch"))
+			frappe.throw(
+				_(
+					"For item {0} at row {1}, count of serial numbers does not match with the picked quantity"
+				).format(frappe.bold(item.item_code), frappe.bold(item.idx)),
+				title=_("Quantity Mismatch"),
+			)
+
+		self.update_bundle_picked_qty()
+		self.update_sales_order_picking_status(update_sales_orders)
+
+	def before_cancel(self):
+		"""Deduct picked qty on cancelling pick list"""
+		updated_sales_orders = set()
+
+		for item in self.get("locations"):
+			if item.sales_order_item:
+				self.update_sales_order_item(item, -1 * item.picked_qty, item.item_code)
+				updated_sales_orders.add(item.sales_order)
+
+		self.update_bundle_picked_qty()
+		self.update_sales_order_picking_status(updated_sales_orders)
+
+	def update_sales_order_item(self, item, picked_qty, item_code):
+		item_table = "Sales Order Item" if not item.product_bundle_item else "Packed Item"
+		stock_qty_field = "stock_qty" if not item.product_bundle_item else "qty"
+
+		already_picked, actual_qty = frappe.db.get_value(
+			item_table,
+			item.sales_order_item,
+			["picked_qty", stock_qty_field],
+		)
+
+		if self.docstatus == 1:
+			if (((already_picked + picked_qty) / actual_qty) * 100) > (
+				100 + flt(frappe.db.get_single_value("Stock Settings", "over_delivery_receipt_allowance"))
+			):
+				frappe.throw(
+					_(
+						"You are picking more than required quantity for {}. Check if there is any other pick list created for {}"
+					).format(item_code, item.sales_order)
+				)
+
+		frappe.db.set_value(item_table, item.sales_order_item, "picked_qty", already_picked + picked_qty)
+
+	@staticmethod
+	def update_sales_order_picking_status(sales_orders: Set[str]) -> None:
+		for sales_order in sales_orders:
+			if sales_order:
+				frappe.get_doc("Sales Order", sales_order).update_picking_status()
 
 	@frappe.whitelist()
 	def set_item_locations(self, save=False):
@@ -45,48 +126,55 @@
 
 		from_warehouses = None
 		if self.parent_warehouse:
-			from_warehouses = frappe.db.get_descendants('Warehouse', self.parent_warehouse)
+			from_warehouses = get_descendants_of("Warehouse", self.parent_warehouse)
 
 		# Create replica before resetting, to handle empty table on update after submit.
-		locations_replica  = self.get('locations')
+		locations_replica = self.get("locations")
 
 		# reset
-		self.delete_key('locations')
+		self.delete_key("locations")
 		for item_doc in items:
 			item_code = item_doc.item_code
 
-			self.item_location_map.setdefault(item_code,
-				get_available_item_locations(item_code, from_warehouses, self.item_count_map.get(item_code), self.company))
+			self.item_location_map.setdefault(
+				item_code,
+				get_available_item_locations(
+					item_code, from_warehouses, self.item_count_map.get(item_code), self.company
+				),
+			)
 
-			locations = get_items_with_location_and_quantity(item_doc, self.item_location_map, self.docstatus)
+			locations = get_items_with_location_and_quantity(
+				item_doc, self.item_location_map, self.docstatus
+			)
 
 			item_doc.idx = None
 			item_doc.name = None
 
 			for row in locations:
-				row.update({
-					'picked_qty': row.stock_qty
-				})
-
 				location = item_doc.as_dict()
 				location.update(row)
-				self.append('locations', location)
+				self.append("locations", location)
 
 		# If table is empty on update after submit, set stock_qty, picked_qty to 0 so that indicator is red
 		# and give feedback to the user. This is to avoid empty Pick Lists.
-		if not self.get('locations') and self.docstatus == 1:
+		if not self.get("locations") and self.docstatus == 1:
 			for location in locations_replica:
 				location.stock_qty = 0
 				location.picked_qty = 0
-				self.append('locations', location)
-			frappe.msgprint(_("Please Restock Items and Update the Pick List to continue. To discontinue, cancel the Pick List."),
-				 title=_("Out of Stock"), indicator="red")
+				self.append("locations", location)
+			frappe.msgprint(
+				_(
+					"Please Restock Items and Update the Pick List to continue. To discontinue, cancel the Pick List."
+				),
+				title=_("Out of Stock"),
+				indicator="red",
+			)
 
 		if save:
 			self.save()
 
 	def aggregate_item_qty(self):
-		locations = self.get('locations')
+		locations = self.get("locations")
 		self.item_count_map = {}
 		# aggregate qty for same item
 		item_map = OrderedDict()
@@ -113,20 +201,20 @@
 		return item_map.values()
 
 	def validate_for_qty(self):
-		if self.purpose == "Material Transfer for Manufacture" \
-				and (self.for_qty is None or self.for_qty == 0):
+		if self.purpose == "Material Transfer for Manufacture" 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()
+		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_item_qty[(item.item_code, item.warehouse)] += item.qty
 			group_picked_qty[(item.item_code, item.warehouse)] += item.picked_qty
 
 		duplicate_list = []
@@ -145,42 +233,103 @@
 		for idx, item in enumerate(self.locations, start=1):
 			item.idx = idx
 
+	def update_bundle_picked_qty(self):
+		product_bundles = self._get_product_bundles()
+		product_bundle_qty_map = self._get_product_bundle_qty_map(product_bundles.values())
+
+		for so_row, item_code in product_bundles.items():
+			picked_qty = self._compute_picked_qty_for_bundle(so_row, product_bundle_qty_map[item_code])
+			item_table = "Sales Order Item"
+			already_picked = frappe.db.get_value(item_table, so_row, "picked_qty")
+			frappe.db.set_value(
+				item_table,
+				so_row,
+				"picked_qty",
+				already_picked + (picked_qty * (1 if self.docstatus == 1 else -1)),
+			)
+
+	def _get_product_bundles(self) -> Dict[str, str]:
+		# Dict[so_item_row: item_code]
+		product_bundles = {}
+		for item in self.locations:
+			if not item.product_bundle_item:
+				continue
+			product_bundles[item.product_bundle_item] = frappe.db.get_value(
+				"Sales Order Item",
+				item.product_bundle_item,
+				"item_code",
+			)
+		return product_bundles
+
+	def _get_product_bundle_qty_map(self, bundles: List[str]) -> Dict[str, Dict[str, float]]:
+		# bundle_item_code: Dict[component, qty]
+		product_bundle_qty_map = {}
+		for bundle_item_code in bundles:
+			bundle = frappe.get_last_doc("Product Bundle", {"new_item_code": bundle_item_code})
+			product_bundle_qty_map[bundle_item_code] = {item.item_code: item.qty for item in bundle.items}
+		return product_bundle_qty_map
+
+	def _compute_picked_qty_for_bundle(self, bundle_row, bundle_items) -> int:
+		"""Compute how many full bundles can be created from picked items."""
+		precision = frappe.get_precision("Stock Ledger Entry", "qty_after_transaction")
+
+		possible_bundles = []
+		for item in self.locations:
+			if item.product_bundle_item != bundle_row:
+				continue
+
+			if qty_in_bundle := bundle_items.get(item.item_code):
+				possible_bundles.append(item.picked_qty / qty_in_bundle)
+			else:
+				possible_bundles.append(0)
+		return int(flt(min(possible_bundles), precision or 6))
+
 
 def validate_item_locations(pick_list):
 	if not pick_list.locations:
 		frappe.throw(_("Add items in the Item Locations table"))
 
+
 def get_items_with_location_and_quantity(item_doc, item_location_map, docstatus):
 	available_locations = item_location_map.get(item_doc.item_code)
 	locations = []
 
 	# if stock qty is zero on submitted entry, show positive remaining qty to recalculate in case of restock.
-	remaining_stock_qty = item_doc.qty if (docstatus == 1 and item_doc.stock_qty == 0) else item_doc.stock_qty
+	remaining_stock_qty = (
+		item_doc.qty if (docstatus == 1 and item_doc.stock_qty == 0) else item_doc.stock_qty
+	)
 
 	while remaining_stock_qty > 0 and available_locations:
 		item_location = available_locations.pop(0)
 		item_location = frappe._dict(item_location)
 
-		stock_qty = remaining_stock_qty if item_location.qty >= remaining_stock_qty else item_location.qty
+		stock_qty = (
+			remaining_stock_qty if item_location.qty >= remaining_stock_qty else item_location.qty
+		)
 		qty = stock_qty / (item_doc.conversion_factor or 1)
 
-		uom_must_be_whole_number = frappe.db.get_value('UOM', item_doc.uom, 'must_be_whole_number')
+		uom_must_be_whole_number = frappe.db.get_value("UOM", item_doc.uom, "must_be_whole_number")
 		if uom_must_be_whole_number:
 			qty = floor(qty)
 			stock_qty = qty * item_doc.conversion_factor
-			if not stock_qty: break
+			if not stock_qty:
+				break
 
 		serial_nos = None
 		if item_location.serial_no:
-			serial_nos = '\n'.join(item_location.serial_no[0: cint(stock_qty)])
+			serial_nos = "\n".join(item_location.serial_no[0 : cint(stock_qty)])
 
-		locations.append(frappe._dict({
-			'qty': qty,
-			'stock_qty': stock_qty,
-			'warehouse': item_location.warehouse,
-			'serial_no': serial_nos,
-			'batch_no': item_location.batch_no
-		}))
+		locations.append(
+			frappe._dict(
+				{
+					"qty": qty,
+					"stock_qty": stock_qty,
+					"warehouse": item_location.warehouse,
+					"serial_no": serial_nos,
+					"batch_no": item_location.batch_no,
+				}
+			)
+		)
 
 		remaining_stock_qty -= stock_qty
 
@@ -190,55 +339,69 @@
 			item_location.qty = qty_diff
 			if item_location.serial_no:
 				# set remaining serial numbers
-				item_location.serial_no = item_location.serial_no[-int(qty_diff):]
+				item_location.serial_no = item_location.serial_no[-int(qty_diff) :]
 			available_locations = [item_location] + available_locations
 
 	# update available locations for the item
 	item_location_map[item_doc.item_code] = available_locations
 	return locations
 
-def get_available_item_locations(item_code, from_warehouses, required_qty, company, ignore_validation=False):
+
+def get_available_item_locations(
+	item_code, from_warehouses, required_qty, company, ignore_validation=False
+):
 	locations = []
-	has_serial_no  = frappe.get_cached_value('Item', item_code, 'has_serial_no')
-	has_batch_no = frappe.get_cached_value('Item', item_code, 'has_batch_no')
+	has_serial_no = frappe.get_cached_value("Item", item_code, "has_serial_no")
+	has_batch_no = frappe.get_cached_value("Item", item_code, "has_batch_no")
 
 	if has_batch_no and has_serial_no:
-		locations = get_available_item_locations_for_serial_and_batched_item(item_code, from_warehouses, required_qty, company)
+		locations = get_available_item_locations_for_serial_and_batched_item(
+			item_code, from_warehouses, required_qty, company
+		)
 	elif has_serial_no:
-		locations = get_available_item_locations_for_serialized_item(item_code, from_warehouses, required_qty, company)
+		locations = get_available_item_locations_for_serialized_item(
+			item_code, from_warehouses, required_qty, company
+		)
 	elif has_batch_no:
-		locations = get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty, company)
+		locations = get_available_item_locations_for_batched_item(
+			item_code, from_warehouses, required_qty, company
+		)
 	else:
-		locations = get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty, company)
+		locations = get_available_item_locations_for_other_item(
+			item_code, from_warehouses, required_qty, company
+		)
 
-	total_qty_available = sum(location.get('qty') for location in locations)
+	total_qty_available = sum(location.get("qty") for location in locations)
 
 	remaining_qty = required_qty - total_qty_available
 
 	if remaining_qty > 0 and not ignore_validation:
-		frappe.msgprint(_('{0} units of Item {1} is not available.')
-			.format(remaining_qty, frappe.get_desk_link('Item', item_code)),
-			title=_("Insufficient Stock"))
+		frappe.msgprint(
+			_("{0} units of Item {1} is not available.").format(
+				remaining_qty, frappe.get_desk_link("Item", item_code)
+			),
+			title=_("Insufficient Stock"),
+		)
 
 	return locations
 
 
-def get_available_item_locations_for_serialized_item(item_code, from_warehouses, required_qty, company):
-	filters = frappe._dict({
-		'item_code': item_code,
-		'company': company,
-		'warehouse': ['!=', '']
-	})
+def get_available_item_locations_for_serialized_item(
+	item_code, from_warehouses, required_qty, company
+):
+	filters = frappe._dict({"item_code": item_code, "company": company, "warehouse": ["!=", ""]})
 
 	if from_warehouses:
-		filters.warehouse = ['in', from_warehouses]
+		filters.warehouse = ["in", from_warehouses]
 
-	serial_nos = frappe.get_all('Serial No',
-		fields=['name', 'warehouse'],
+	serial_nos = frappe.get_all(
+		"Serial No",
+		fields=["name", "warehouse"],
 		filters=filters,
 		limit=required_qty,
-		order_by='purchase_date',
-		as_list=1)
+		order_by="purchase_date",
+		as_list=1,
+	)
 
 	warehouse_serial_nos_map = frappe._dict()
 	for serial_no, warehouse in serial_nos:
@@ -246,17 +409,17 @@
 
 	locations = []
 	for warehouse, serial_nos in warehouse_serial_nos_map.items():
-		locations.append({
-			'qty': len(serial_nos),
-			'warehouse': warehouse,
-			'serial_no': serial_nos
-		})
+		locations.append({"qty": len(serial_nos), "warehouse": warehouse, "serial_no": serial_nos})
 
 	return locations
 
-def get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty, company):
-	warehouse_condition = 'and warehouse in %(warehouses)s' if from_warehouses else ''
-	batch_locations = frappe.db.sql("""
+
+def get_available_item_locations_for_batched_item(
+	item_code, from_warehouses, required_qty, company
+):
+	warehouse_condition = "and warehouse in %(warehouses)s" if from_warehouses else ""
+	batch_locations = frappe.db.sql(
+		"""
 		SELECT
 			sle.`warehouse`,
 			sle.`batch_no`,
@@ -272,118 +435,171 @@
 			and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s
 			{warehouse_condition}
 		GROUP BY
-			`warehouse`,
-			`batch_no`,
-			`item_code`
+			sle.`warehouse`,
+			sle.`batch_no`,
+			sle.`item_code`
 		HAVING `qty` > 0
 		ORDER BY IFNULL(batch.`expiry_date`, '2200-01-01'), batch.`creation`
-	""".format(warehouse_condition=warehouse_condition), { #nosec
-		'item_code': item_code,
-		'company': company,
-		'today': today(),
-		'warehouses': from_warehouses
-	}, as_dict=1)
+	""".format(
+			warehouse_condition=warehouse_condition
+		),
+		{  # nosec
+			"item_code": item_code,
+			"company": company,
+			"today": today(),
+			"warehouses": from_warehouses,
+		},
+		as_dict=1,
+	)
 
 	return batch_locations
 
-def get_available_item_locations_for_serial_and_batched_item(item_code, from_warehouses, required_qty, company):
-	# Get batch nos by FIFO
-	locations = get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty, company)
 
-	filters = frappe._dict({
-		'item_code': item_code,
-		'company': company,
-		'warehouse': ['!=', ''],
-		'batch_no': ''
-	})
+def get_available_item_locations_for_serial_and_batched_item(
+	item_code, from_warehouses, required_qty, company
+):
+	# Get batch nos by FIFO
+	locations = get_available_item_locations_for_batched_item(
+		item_code, from_warehouses, required_qty, company
+	)
+
+	filters = frappe._dict(
+		{"item_code": item_code, "company": company, "warehouse": ["!=", ""], "batch_no": ""}
+	)
 
 	# Get Serial Nos by FIFO for Batch No
 	for location in locations:
 		filters.batch_no = location.batch_no
 		filters.warehouse = location.warehouse
-		location.qty = required_qty if location.qty > required_qty else location.qty # if extra qty in batch
+		location.qty = (
+			required_qty if location.qty > required_qty else location.qty
+		)  # if extra qty in batch
 
-		serial_nos = frappe.get_list('Serial No',
-			fields=['name'],
-			filters=filters,
-			limit=location.qty,
-			order_by='purchase_date')
+		serial_nos = frappe.get_list(
+			"Serial No", fields=["name"], filters=filters, limit=location.qty, order_by="purchase_date"
+		)
 
 		serial_nos = [sn.name for sn in serial_nos]
 		location.serial_no = serial_nos
 
 	return locations
 
+
 def get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty, company):
 	# gets all items available in different warehouses
-	warehouses = [x.get('name') for x in frappe.get_list("Warehouse", {'company': company}, "name")]
+	warehouses = [x.get("name") for x in frappe.get_list("Warehouse", {"company": company}, "name")]
 
-	filters = frappe._dict({
-		'item_code': item_code,
-		'warehouse': ['in', warehouses],
-		'actual_qty': ['>', 0]
-	})
+	filters = frappe._dict(
+		{"item_code": item_code, "warehouse": ["in", warehouses], "actual_qty": [">", 0]}
+	)
 
 	if from_warehouses:
-		filters.warehouse = ['in', from_warehouses]
+		filters.warehouse = ["in", from_warehouses]
 
-	item_locations = frappe.get_all('Bin',
-		fields=['warehouse', 'actual_qty as qty'],
+	item_locations = frappe.get_all(
+		"Bin",
+		fields=["warehouse", "actual_qty as qty"],
 		filters=filters,
 		limit=required_qty,
-		order_by='creation')
+		order_by="creation",
+	)
 
 	return item_locations
 
 
 @frappe.whitelist()
 def create_delivery_note(source_name, target_doc=None):
-	pick_list = frappe.get_doc('Pick List', source_name)
+	pick_list = frappe.get_doc("Pick List", source_name)
 	validate_item_locations(pick_list)
-
-	sales_orders = [d.sales_order for d in pick_list.locations if d.sales_order]
-	sales_orders = set(sales_orders)
-
+	sales_dict = dict()
+	sales_orders = []
 	delivery_note = None
-	for sales_order in sales_orders:
-		delivery_note = create_delivery_note_from_sales_order(sales_order,
-			delivery_note, skip_item_mapping=True)
+	for location in pick_list.locations:
+		if location.sales_order:
+			sales_orders.append(
+				frappe.db.get_value(
+					"Sales Order", location.sales_order, ["customer", "name as sales_order"], as_dict=True
+				)
+			)
 
-	# map rows without sales orders as well
-	if not delivery_note:
-		delivery_note = frappe.new_doc("Delivery Note")
+	for customer, rows in groupby(sales_orders, key=lambda so: so["customer"]):
+		sales_dict[customer] = {row.sales_order for row in rows}
 
-	item_table_mapper = {
-		'doctype': 'Delivery Note Item',
-		'field_map': {
-			'rate': 'rate',
-			'name': 'so_detail',
-			'parent': 'against_sales_order',
-		},
-		'condition': lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1
-	}
+	if sales_dict:
+		delivery_note = create_dn_with_so(sales_dict, pick_list)
+
+	if not all(item.sales_order for item in pick_list.locations):
+		delivery_note = create_dn_wo_so(pick_list)
+
+	frappe.msgprint(_("Delivery Note(s) created for the Pick List"))
+	return delivery_note
+
+
+def create_dn_wo_so(pick_list):
+	delivery_note = frappe.new_doc("Delivery Note")
 
 	item_table_mapper_without_so = {
-		'doctype': 'Delivery Note Item',
-		'field_map': {
-			'rate': 'rate',
-			'name': 'name',
-			'parent': '',
-		}
+		"doctype": "Delivery Note Item",
+		"field_map": {
+			"rate": "rate",
+			"name": "name",
+			"parent": "",
+		},
+	}
+	map_pl_locations(pick_list, item_table_mapper_without_so, delivery_note)
+	delivery_note.insert(ignore_mandatory=True)
+
+	return delivery_note
+
+
+def create_dn_with_so(sales_dict, pick_list):
+	delivery_note = None
+
+	item_table_mapper = {
+		"doctype": "Delivery Note Item",
+		"field_map": {
+			"rate": "rate",
+			"name": "so_detail",
+			"parent": "against_sales_order",
+		},
+		"condition": lambda doc: abs(doc.delivered_qty) < abs(doc.qty)
+		and doc.delivered_by_supplier != 1,
 	}
 
+	for customer in sales_dict:
+		for so in sales_dict[customer]:
+			delivery_note = None
+			delivery_note = create_delivery_note_from_sales_order(so, delivery_note, skip_item_mapping=True)
+			break
+		if delivery_note:
+			# map all items of all sales orders of that customer
+			for so in sales_dict[customer]:
+				map_pl_locations(pick_list, item_table_mapper, delivery_note, so)
+			delivery_note.flags.ignore_mandatory = True
+			delivery_note.insert()
+			update_packed_item_details(pick_list, delivery_note)
+			delivery_note.save()
+
+	return delivery_note
+
+
+def map_pl_locations(pick_list, item_mapper, delivery_note, sales_order=None):
+
 	for location in pick_list.locations:
+		if location.sales_order != sales_order or location.product_bundle_item:
+			continue
+
 		if location.sales_order_item:
-			sales_order_item = frappe.get_cached_doc('Sales Order Item', {'name':location.sales_order_item})
+			sales_order_item = frappe.get_doc("Sales Order Item", location.sales_order_item)
 		else:
 			sales_order_item = None
 
-		source_doc, table_mapper = [sales_order_item, item_table_mapper] if sales_order_item \
-			else [location, item_table_mapper_without_so]
+		source_doc = sales_order_item or location
 
-		dn_item = map_child_doc(source_doc, delivery_note, table_mapper)
+		dn_item = map_child_doc(source_doc, delivery_note, item_mapper)
 
 		if dn_item:
+			dn_item.pick_list_item = location.name
 			dn_item.warehouse = location.warehouse
 			dn_item.qty = flt(location.picked_qty) / (flt(location.conversion_factor) or 1)
 			dn_item.batch_no = location.batch_no
@@ -391,41 +607,87 @@
 
 			update_delivery_note_item(source_doc, dn_item, delivery_note)
 
+	add_product_bundles_to_delivery_note(pick_list, delivery_note, item_mapper)
 	set_delivery_note_missing_values(delivery_note)
 
 	delivery_note.pick_list = pick_list.name
-	delivery_note.customer = pick_list.customer if pick_list.customer else None
+	delivery_note.company = pick_list.company
+	delivery_note.customer = frappe.get_value("Sales Order", sales_order, "customer")
 
-	return delivery_note
+
+def add_product_bundles_to_delivery_note(
+	pick_list: "PickList", delivery_note, item_mapper
+) -> None:
+	"""Add product bundles found in pick list to delivery note.
+
+	When mapping pick list items, the bundle item itself isn't part of the
+	locations. Dynamically fetch and add parent bundle item into DN."""
+	product_bundles = pick_list._get_product_bundles()
+	product_bundle_qty_map = pick_list._get_product_bundle_qty_map(product_bundles.values())
+
+	for so_row, item_code in product_bundles.items():
+		sales_order_item = frappe.get_doc("Sales Order Item", so_row)
+		dn_bundle_item = map_child_doc(sales_order_item, delivery_note, item_mapper)
+		dn_bundle_item.qty = pick_list._compute_picked_qty_for_bundle(
+			so_row, product_bundle_qty_map[item_code]
+		)
+		update_delivery_note_item(sales_order_item, dn_bundle_item, delivery_note)
+
+
+def update_packed_item_details(pick_list: "PickList", delivery_note) -> None:
+	"""Update stock details on packed items table of delivery note."""
+
+	def _find_so_row(packed_item):
+		for item in delivery_note.items:
+			if packed_item.parent_detail_docname == item.name:
+				return item.so_detail
+
+	def _find_pick_list_location(bundle_row, packed_item):
+		if not bundle_row:
+			return
+		for loc in pick_list.locations:
+			if loc.product_bundle_item == bundle_row and loc.item_code == packed_item.item_code:
+				return loc
+
+	for packed_item in delivery_note.packed_items:
+		so_row = _find_so_row(packed_item)
+		location = _find_pick_list_location(so_row, packed_item)
+		if not location:
+			continue
+		packed_item.warehouse = location.warehouse
+		packed_item.batch_no = location.batch_no
+		packed_item.serial_no = location.serial_no
+
 
 @frappe.whitelist()
 def create_stock_entry(pick_list):
 	pick_list = frappe.get_doc(json.loads(pick_list))
 	validate_item_locations(pick_list)
 
-	if stock_entry_exists(pick_list.get('name')):
-		return frappe.msgprint(_('Stock Entry has been already created against this Pick List'))
+	if stock_entry_exists(pick_list.get("name")):
+		return frappe.msgprint(_("Stock Entry has been already created against this Pick List"))
 
-	stock_entry = frappe.new_doc('Stock Entry')
-	stock_entry.pick_list = pick_list.get('name')
-	stock_entry.purpose = pick_list.get('purpose')
+	stock_entry = frappe.new_doc("Stock Entry")
+	stock_entry.pick_list = pick_list.get("name")
+	stock_entry.purpose = pick_list.get("purpose")
 	stock_entry.set_stock_entry_type()
 
-	if pick_list.get('work_order'):
+	if pick_list.get("work_order"):
 		stock_entry = update_stock_entry_based_on_work_order(pick_list, stock_entry)
-	elif pick_list.get('material_request'):
+	elif pick_list.get("material_request"):
 		stock_entry = update_stock_entry_based_on_material_request(pick_list, stock_entry)
 	else:
 		stock_entry = update_stock_entry_items_with_no_reference(pick_list, stock_entry)
 
-	stock_entry.set_actual_qty()
-	stock_entry.calculate_rate_and_amount()
+	stock_entry.set_missing_values()
 
 	return stock_entry.as_dict()
 
+
 @frappe.whitelist()
 def get_pending_work_orders(doctype, txt, searchfield, start, page_length, filters, as_dict):
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		SELECT
 			`name`, `company`, `planned_start_date`
 		FROM
@@ -437,29 +699,31 @@
 			AND `company` = %(company)s
 			AND `name` like %(txt)s
 		ORDER BY
-			if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), name
+			(case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end) name
 		LIMIT
 			%(start)s, %(page_length)s""",
 		{
-			'txt': "%%%s%%" % txt,
-			'_txt': txt.replace('%', ''),
-			'start': start,
-			'page_length': frappe.utils.cint(page_length),
-			'company': filters.get('company')
-		}, as_dict=as_dict)
+			"txt": "%%%s%%" % txt,
+			"_txt": txt.replace("%", ""),
+			"start": start,
+			"page_length": frappe.utils.cint(page_length),
+			"company": filters.get("company"),
+		},
+		as_dict=as_dict,
+	)
+
 
 @frappe.whitelist()
 def target_document_exists(pick_list_name, purpose):
-	if purpose == 'Delivery':
-		return frappe.db.exists('Delivery Note', {
-			'pick_list': pick_list_name
-		})
+	if purpose == "Delivery":
+		return frappe.db.exists("Delivery Note", {"pick_list": pick_list_name})
 
 	return stock_entry_exists(pick_list_name)
 
+
 @frappe.whitelist()
 def get_item_details(item_code, uom=None):
-	details = frappe.db.get_value('Item', item_code, ['stock_uom', 'name'], as_dict=1)
+	details = frappe.db.get_value("Item", item_code, ["stock_uom", "name"], as_dict=1)
 	details.uom = uom or details.stock_uom
 	if uom:
 		details.update(get_conversion_factor(item_code, uom))
@@ -468,37 +732,37 @@
 
 
 def update_delivery_note_item(source, target, delivery_note):
-	cost_center = frappe.db.get_value('Project', delivery_note.project, 'cost_center')
+	cost_center = frappe.db.get_value("Project", delivery_note.project, "cost_center")
 	if not cost_center:
-		cost_center = get_cost_center(source.item_code, 'Item', delivery_note.company)
+		cost_center = get_cost_center(source.item_code, "Item", delivery_note.company)
 
 	if not cost_center:
-		cost_center = get_cost_center(source.item_group, 'Item Group', delivery_note.company)
+		cost_center = get_cost_center(source.item_group, "Item Group", delivery_note.company)
 
 	target.cost_center = cost_center
 
+
 def get_cost_center(for_item, from_doctype, company):
-	'''Returns Cost Center for Item or Item Group'''
-	return frappe.db.get_value('Item Default',
-		fieldname=['buying_cost_center'],
-		filters={
-			'parent': for_item,
-			'parenttype': from_doctype,
-			'company': company
-		})
+	"""Returns Cost Center for Item or Item Group"""
+	return frappe.db.get_value(
+		"Item Default",
+		fieldname=["buying_cost_center"],
+		filters={"parent": for_item, "parenttype": from_doctype, "company": company},
+	)
+
 
 def set_delivery_note_missing_values(target):
-	target.run_method('set_missing_values')
-	target.run_method('set_po_nos')
-	target.run_method('calculate_taxes_and_totals')
+	target.run_method("set_missing_values")
+	target.run_method("set_po_nos")
+	target.run_method("calculate_taxes_and_totals")
+
 
 def stock_entry_exists(pick_list_name):
-	return frappe.db.exists('Stock Entry', {
-		'pick_list': pick_list_name
-	})
+	return frappe.db.exists("Stock Entry", {"pick_list": pick_list_name})
+
 
 def update_stock_entry_based_on_work_order(pick_list, stock_entry):
-	work_order = frappe.get_doc("Work Order", pick_list.get('work_order'))
+	work_order = frappe.get_doc("Work Order", pick_list.get("work_order"))
 
 	stock_entry.work_order = work_order.name
 	stock_entry.company = work_order.company
@@ -507,10 +771,11 @@
 	stock_entry.use_multi_level_bom = work_order.use_multi_level_bom
 	stock_entry.fg_completed_qty = pick_list.for_qty
 	if work_order.bom_no:
-		stock_entry.inspection_required = frappe.db.get_value('BOM',
-			work_order.bom_no, 'inspection_required')
+		stock_entry.inspection_required = frappe.db.get_value(
+			"BOM", work_order.bom_no, "inspection_required"
+		)
 
-	is_wip_warehouse_group = frappe.db.get_value('Warehouse', work_order.wip_warehouse, 'is_group')
+	is_wip_warehouse_group = frappe.db.get_value("Warehouse", work_order.wip_warehouse, "is_group")
 	if not (is_wip_warehouse_group and work_order.skip_transfer):
 		wip_warehouse = work_order.wip_warehouse
 	else:
@@ -524,32 +789,36 @@
 		update_common_item_properties(item, location)
 		item.t_warehouse = wip_warehouse
 
-		stock_entry.append('items', item)
+		stock_entry.append("items", item)
 
 	return stock_entry
 
+
 def update_stock_entry_based_on_material_request(pick_list, stock_entry):
 	for location in pick_list.locations:
 		target_warehouse = None
 		if location.material_request_item:
-			target_warehouse = frappe.get_value('Material Request Item',
-				location.material_request_item, 'warehouse')
+			target_warehouse = frappe.get_value(
+				"Material Request Item", location.material_request_item, "warehouse"
+			)
 		item = frappe._dict()
 		update_common_item_properties(item, location)
 		item.t_warehouse = target_warehouse
-		stock_entry.append('items', item)
+		stock_entry.append("items", item)
 
 	return stock_entry
 
+
 def update_stock_entry_items_with_no_reference(pick_list, stock_entry):
 	for location in pick_list.locations:
 		item = frappe._dict()
 		update_common_item_properties(item, location)
 
-		stock_entry.append('items', item)
+		stock_entry.append("items", item)
 
 	return stock_entry
 
+
 def update_common_item_properties(item, location):
 	item.item_code = location.item_code
 	item.s_warehouse = location.warehouse
diff --git a/erpnext/stock/doctype/pick_list/pick_list_dashboard.py b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py
index ec3047e..92e57be 100644
--- a/erpnext/stock/doctype/pick_list/pick_list_dashboard.py
+++ b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py
@@ -1,9 +1,7 @@
 def get_data():
 	return {
-		'fieldname': 'pick_list',
-		'transactions': [
-			{
-				'items': ['Stock Entry', 'Delivery Note']
-			},
-		]
+		"fieldname": "pick_list",
+		"transactions": [
+			{"items": ["Stock Entry", "Delivery Note"]},
+		],
 	}
diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py
index f3b6b89..f552299 100644
--- a/erpnext/stock/doctype/pick_list/test_pick_list.py
+++ b/erpnext/stock/doctype/pick_list/test_pick_list.py
@@ -3,163 +3,194 @@
 
 import frappe
 from frappe import _dict
-
-test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch']
-
 from frappe.tests.utils import FrappeTestCase
 
-from erpnext.stock.doctype.item.test_item import create_item
+from erpnext.selling.doctype.sales_order.sales_order import create_pick_list
+from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
+from erpnext.stock.doctype.item.test_item import create_item, make_item
+from erpnext.stock.doctype.packed_item.test_packed_item import create_product_bundle
 from erpnext.stock.doctype.pick_list.pick_list import create_delivery_note
 from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
 	EmptyStockReconciliationItemsError,
 )
 
+test_dependencies = ["Item", "Sales Invoice", "Stock Entry", "Batch"]
+
 
 class TestPickList(FrappeTestCase):
-
 	def test_pick_list_picks_warehouse_for_each_item(self):
+		item_code = make_item().name
 		try:
-			frappe.get_doc({
-				'doctype': 'Stock Reconciliation',
-				'company': '_Test Company',
-				'purpose': 'Opening Stock',
-				'expense_account': 'Temporary Opening - _TC',
-				'items': [{
-					'item_code': '_Test Item',
-					'warehouse': '_Test Warehouse - _TC',
-					'valuation_rate': 100,
-					'qty': 5
-				}]
-			}).submit()
+			frappe.get_doc(
+				{
+					"doctype": "Stock Reconciliation",
+					"company": "_Test Company",
+					"purpose": "Opening Stock",
+					"expense_account": "Temporary Opening - _TC",
+					"items": [
+						{
+							"item_code": item_code,
+							"warehouse": "_Test Warehouse - _TC",
+							"valuation_rate": 100,
+							"qty": 5,
+						}
+					],
+				}
+			).submit()
 		except EmptyStockReconciliationItemsError:
 			pass
 
-		pick_list = frappe.get_doc({
-			'doctype': 'Pick List',
-			'company': '_Test Company',
-			'customer': '_Test Customer',
-			'items_based_on': 'Sales Order',
-			'purpose': 'Delivery',
-			'locations': [{
-				'item_code': '_Test Item',
-				'qty': 5,
-				'stock_qty': 5,
-				'conversion_factor': 1,
-				'sales_order': '_T-Sales Order-1',
-				'sales_order_item': '_T-Sales Order-1_item',
-			}]
-		})
+		pick_list = frappe.get_doc(
+			{
+				"doctype": "Pick List",
+				"company": "_Test Company",
+				"customer": "_Test Customer",
+				"items_based_on": "Sales Order",
+				"purpose": "Delivery",
+				"locations": [
+					{
+						"item_code": item_code,
+						"qty": 5,
+						"stock_qty": 5,
+						"conversion_factor": 1,
+						"sales_order": "_T-Sales Order-1",
+						"sales_order_item": "_T-Sales Order-1_item",
+					}
+				],
+			}
+		)
 		pick_list.set_item_locations()
 
-		self.assertEqual(pick_list.locations[0].item_code, '_Test Item')
-		self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC')
+		self.assertEqual(pick_list.locations[0].item_code, item_code)
+		self.assertEqual(pick_list.locations[0].warehouse, "_Test Warehouse - _TC")
 		self.assertEqual(pick_list.locations[0].qty, 5)
 
 	def test_pick_list_splits_row_according_to_warehouse_availability(self):
 		try:
-			frappe.get_doc({
-				'doctype': 'Stock Reconciliation',
-				'company': '_Test Company',
-				'purpose': 'Opening Stock',
-				'expense_account': 'Temporary Opening - _TC',
-				'items': [{
-					'item_code': '_Test Item Warehouse Group Wise Reorder',
-					'warehouse': '_Test Warehouse Group-C1 - _TC',
-					'valuation_rate': 100,
-					'qty': 5
-				}]
-			}).submit()
+			frappe.get_doc(
+				{
+					"doctype": "Stock Reconciliation",
+					"company": "_Test Company",
+					"purpose": "Opening Stock",
+					"expense_account": "Temporary Opening - _TC",
+					"items": [
+						{
+							"item_code": "_Test Item Warehouse Group Wise Reorder",
+							"warehouse": "_Test Warehouse Group-C1 - _TC",
+							"valuation_rate": 100,
+							"qty": 5,
+						}
+					],
+				}
+			).submit()
 		except EmptyStockReconciliationItemsError:
 			pass
 
 		try:
-			frappe.get_doc({
-				'doctype': 'Stock Reconciliation',
-				'company': '_Test Company',
-				'purpose': 'Opening Stock',
-				'expense_account': 'Temporary Opening - _TC',
-				'items': [{
-					'item_code': '_Test Item Warehouse Group Wise Reorder',
-					'warehouse': '_Test Warehouse 2 - _TC',
-					'valuation_rate': 400,
-					'qty': 10
-				}]
-			}).submit()
+			frappe.get_doc(
+				{
+					"doctype": "Stock Reconciliation",
+					"company": "_Test Company",
+					"purpose": "Opening Stock",
+					"expense_account": "Temporary Opening - _TC",
+					"items": [
+						{
+							"item_code": "_Test Item Warehouse Group Wise Reorder",
+							"warehouse": "_Test Warehouse 2 - _TC",
+							"valuation_rate": 400,
+							"qty": 10,
+						}
+					],
+				}
+			).submit()
 		except EmptyStockReconciliationItemsError:
 			pass
 
-		pick_list = frappe.get_doc({
-			'doctype': 'Pick List',
-			'company': '_Test Company',
-			'customer': '_Test Customer',
-			'items_based_on': 'Sales Order',
-			'purpose': 'Delivery',
-			'locations': [{
-				'item_code': '_Test Item Warehouse Group Wise Reorder',
-				'qty': 1000,
-				'stock_qty': 1000,
-				'conversion_factor': 1,
-				'sales_order': '_T-Sales Order-1',
-				'sales_order_item': '_T-Sales Order-1_item',
-			}]
-		})
+		pick_list = frappe.get_doc(
+			{
+				"doctype": "Pick List",
+				"company": "_Test Company",
+				"customer": "_Test Customer",
+				"items_based_on": "Sales Order",
+				"purpose": "Delivery",
+				"locations": [
+					{
+						"item_code": "_Test Item Warehouse Group Wise Reorder",
+						"qty": 1000,
+						"stock_qty": 1000,
+						"conversion_factor": 1,
+						"sales_order": "_T-Sales Order-1",
+						"sales_order_item": "_T-Sales Order-1_item",
+					}
+				],
+			}
+		)
 
 		pick_list.set_item_locations()
 
-		self.assertEqual(pick_list.locations[0].item_code, '_Test Item Warehouse Group Wise Reorder')
-		self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse Group-C1 - _TC')
+		self.assertEqual(pick_list.locations[0].item_code, "_Test Item Warehouse Group Wise Reorder")
+		self.assertEqual(pick_list.locations[0].warehouse, "_Test Warehouse Group-C1 - _TC")
 		self.assertEqual(pick_list.locations[0].qty, 5)
 
-		self.assertEqual(pick_list.locations[1].item_code, '_Test Item Warehouse Group Wise Reorder')
-		self.assertEqual(pick_list.locations[1].warehouse, '_Test Warehouse 2 - _TC')
+		self.assertEqual(pick_list.locations[1].item_code, "_Test Item Warehouse Group Wise Reorder")
+		self.assertEqual(pick_list.locations[1].warehouse, "_Test Warehouse 2 - _TC")
 		self.assertEqual(pick_list.locations[1].qty, 10)
 
 	def test_pick_list_shows_serial_no_for_serialized_item(self):
 
-		stock_reconciliation = frappe.get_doc({
-			'doctype': 'Stock Reconciliation',
-			'purpose': 'Stock Reconciliation',
-			'company': '_Test Company',
-			'items': [{
-				'item_code': '_Test Serialized Item',
-				'warehouse': '_Test Warehouse - _TC',
-				'valuation_rate': 100,
-				'qty': 5,
-				'serial_no': '123450\n123451\n123452\n123453\n123454'
-			}]
-		})
+		stock_reconciliation = frappe.get_doc(
+			{
+				"doctype": "Stock Reconciliation",
+				"purpose": "Stock Reconciliation",
+				"company": "_Test Company",
+				"items": [
+					{
+						"item_code": "_Test Serialized Item",
+						"warehouse": "_Test Warehouse - _TC",
+						"valuation_rate": 100,
+						"qty": 5,
+						"serial_no": "123450\n123451\n123452\n123453\n123454",
+					}
+				],
+			}
+		)
 
 		try:
 			stock_reconciliation.submit()
 		except EmptyStockReconciliationItemsError:
 			pass
 
-		pick_list = frappe.get_doc({
-			'doctype': 'Pick List',
-			'company': '_Test Company',
-			'customer': '_Test Customer',
-			'items_based_on': 'Sales Order',
-			'purpose': 'Delivery',
-			'locations': [{
-				'item_code': '_Test Serialized Item',
-				'qty': 1000,
-				'stock_qty': 1000,
-				'conversion_factor': 1,
-				'sales_order': '_T-Sales Order-1',
-				'sales_order_item': '_T-Sales Order-1_item',
-			}]
-		})
+		pick_list = frappe.get_doc(
+			{
+				"doctype": "Pick List",
+				"company": "_Test Company",
+				"customer": "_Test Customer",
+				"items_based_on": "Sales Order",
+				"purpose": "Delivery",
+				"locations": [
+					{
+						"item_code": "_Test Serialized Item",
+						"qty": 1000,
+						"stock_qty": 1000,
+						"conversion_factor": 1,
+						"sales_order": "_T-Sales Order-1",
+						"sales_order_item": "_T-Sales Order-1_item",
+					}
+				],
+			}
+		)
 
 		pick_list.set_item_locations()
-		self.assertEqual(pick_list.locations[0].item_code, '_Test Serialized Item')
-		self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC')
+		self.assertEqual(pick_list.locations[0].item_code, "_Test Serialized Item")
+		self.assertEqual(pick_list.locations[0].warehouse, "_Test Warehouse - _TC")
 		self.assertEqual(pick_list.locations[0].qty, 5)
-		self.assertEqual(pick_list.locations[0].serial_no, '123450\n123451\n123452\n123453\n123454')
+		self.assertEqual(pick_list.locations[0].serial_no, "123450\n123451\n123452\n123453\n123454")
 
 	def test_pick_list_shows_batch_no_for_batched_item(self):
 		# check if oldest batch no is picked
-		item = frappe.db.exists("Item", {'item_name': 'Batched Item'})
+		item = frappe.db.exists("Item", {"item_name": "Batched Item"})
 		if not item:
 			item = create_item("Batched Item")
 			item.has_batch_no = 1
@@ -167,7 +198,7 @@
 			item.batch_number_series = "B-BATCH-.##"
 			item.save()
 		else:
-			item = frappe.get_doc("Item", {'item_name': 'Batched Item'})
+			item = frappe.get_doc("Item", {"item_name": "Batched Item"})
 
 		pr1 = make_purchase_receipt(item_code="Batched Item", qty=1, rate=100.0)
 
@@ -176,28 +207,30 @@
 
 		pr2 = make_purchase_receipt(item_code="Batched Item", qty=2, rate=100.0)
 
-		pick_list = frappe.get_doc({
-			'doctype': 'Pick List',
-			'company': '_Test Company',
-			'purpose': 'Material Transfer',
-			'locations': [{
-				'item_code': 'Batched Item',
-				'qty': 1,
-				'stock_qty': 1,
-				'conversion_factor': 1,
-			}]
-		})
+		pick_list = frappe.get_doc(
+			{
+				"doctype": "Pick List",
+				"company": "_Test Company",
+				"purpose": "Material Transfer",
+				"locations": [
+					{
+						"item_code": "Batched Item",
+						"qty": 1,
+						"stock_qty": 1,
+						"conversion_factor": 1,
+					}
+				],
+			}
+		)
 		pick_list.set_item_locations()
-
 		self.assertEqual(pick_list.locations[0].batch_no, oldest_batch_no)
 
 		pr1.cancel()
 		pr2.cancel()
 
-
 	def test_pick_list_for_batched_and_serialised_item(self):
 		# check if oldest batch no and serial nos are picked
-		item = frappe.db.exists("Item", {'item_name': 'Batched and Serialised Item'})
+		item = frappe.db.exists("Item", {"item_name": "Batched and Serialised Item"})
 		if not item:
 			item = create_item("Batched and Serialised Item")
 			item.has_batch_no = 1
@@ -207,7 +240,7 @@
 			item.serial_no_series = "S-.####"
 			item.save()
 		else:
-			item = frappe.get_doc("Item", {'item_name': 'Batched and Serialised Item'})
+			item = frappe.get_doc("Item", {"item_name": "Batched and Serialised Item"})
 
 		pr1 = make_purchase_receipt(item_code="Batched and Serialised Item", qty=2, rate=100.0)
 
@@ -217,17 +250,21 @@
 
 		pr2 = make_purchase_receipt(item_code="Batched and Serialised Item", qty=2, rate=100.0)
 
-		pick_list = frappe.get_doc({
-			'doctype': 'Pick List',
-			'company': '_Test Company',
-			'purpose': 'Material Transfer',
-			'locations': [{
-				'item_code': 'Batched and Serialised Item',
-				'qty': 2,
-				'stock_qty': 2,
-				'conversion_factor': 1,
-			}]
-		})
+		pick_list = frappe.get_doc(
+			{
+				"doctype": "Pick List",
+				"company": "_Test Company",
+				"purpose": "Material Transfer",
+				"locations": [
+					{
+						"item_code": "Batched and Serialised Item",
+						"qty": 2,
+						"stock_qty": 2,
+						"conversion_factor": 1,
+					}
+				],
+			}
+		)
 		pick_list.set_item_locations()
 
 		self.assertEqual(pick_list.locations[0].batch_no, oldest_batch_no)
@@ -237,112 +274,142 @@
 		pr2.cancel()
 
 	def test_pick_list_for_items_from_multiple_sales_orders(self):
+
+		item_code = make_item().name
 		try:
-			frappe.get_doc({
-				'doctype': 'Stock Reconciliation',
-				'company': '_Test Company',
-				'purpose': 'Opening Stock',
-				'expense_account': 'Temporary Opening - _TC',
-				'items': [{
-					'item_code': '_Test Item',
-					'warehouse': '_Test Warehouse - _TC',
-					'valuation_rate': 100,
-					'qty': 10
-				}]
-			}).submit()
+			frappe.get_doc(
+				{
+					"doctype": "Stock Reconciliation",
+					"company": "_Test Company",
+					"purpose": "Opening Stock",
+					"expense_account": "Temporary Opening - _TC",
+					"items": [
+						{
+							"item_code": item_code,
+							"warehouse": "_Test Warehouse - _TC",
+							"valuation_rate": 100,
+							"qty": 10,
+						}
+					],
+				}
+			).submit()
 		except EmptyStockReconciliationItemsError:
 			pass
 
-		sales_order = frappe.get_doc({
-				'doctype': "Sales Order",
-				'customer': '_Test Customer',
-				'company': '_Test Company',
-				'items': [{
-					'item_code': '_Test Item',
-					'qty': 10,
-					'delivery_date': frappe.utils.today()
-				}],
-			})
+		sales_order = frappe.get_doc(
+			{
+				"doctype": "Sales Order",
+				"customer": "_Test Customer",
+				"company": "_Test Company",
+				"items": [
+					{
+						"item_code": item_code,
+						"qty": 10,
+						"delivery_date": frappe.utils.today(),
+						"warehouse": "_Test Warehouse - _TC",
+					}
+				],
+			}
+		)
 		sales_order.submit()
 
-		pick_list = frappe.get_doc({
-			'doctype': 'Pick List',
-			'company': '_Test Company',
-			'customer': '_Test Customer',
-			'items_based_on': 'Sales Order',
-			'purpose': 'Delivery',
-			'locations': [{
-				'item_code': '_Test Item',
-				'qty': 5,
-				'stock_qty': 5,
-				'conversion_factor': 1,
-				'sales_order': '_T-Sales Order-1',
-				'sales_order_item': '_T-Sales Order-1_item',
-			}, {
-				'item_code': '_Test Item',
-				'qty': 5,
-				'stock_qty': 5,
-				'conversion_factor': 1,
-				'sales_order': sales_order.name,
-				'sales_order_item': sales_order.items[0].name,
-			}]
-		})
+		pick_list = frappe.get_doc(
+			{
+				"doctype": "Pick List",
+				"company": "_Test Company",
+				"customer": "_Test Customer",
+				"items_based_on": "Sales Order",
+				"purpose": "Delivery",
+				"locations": [
+					{
+						"item_code": item_code,
+						"qty": 5,
+						"stock_qty": 5,
+						"conversion_factor": 1,
+						"sales_order": "_T-Sales Order-1",
+						"sales_order_item": "_T-Sales Order-1_item",
+					},
+					{
+						"item_code": item_code,
+						"qty": 5,
+						"stock_qty": 5,
+						"conversion_factor": 1,
+						"sales_order": sales_order.name,
+						"sales_order_item": sales_order.items[0].name,
+					},
+				],
+			}
+		)
 		pick_list.set_item_locations()
 
-		self.assertEqual(pick_list.locations[0].item_code, '_Test Item')
-		self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC')
+		self.assertEqual(pick_list.locations[0].item_code, item_code)
+		self.assertEqual(pick_list.locations[0].warehouse, "_Test Warehouse - _TC")
 		self.assertEqual(pick_list.locations[0].qty, 5)
-		self.assertEqual(pick_list.locations[0].sales_order_item, '_T-Sales Order-1_item')
+		self.assertEqual(pick_list.locations[0].sales_order_item, "_T-Sales Order-1_item")
 
-		self.assertEqual(pick_list.locations[1].item_code, '_Test Item')
-		self.assertEqual(pick_list.locations[1].warehouse, '_Test Warehouse - _TC')
+		self.assertEqual(pick_list.locations[1].item_code, item_code)
+		self.assertEqual(pick_list.locations[1].warehouse, "_Test Warehouse - _TC")
 		self.assertEqual(pick_list.locations[1].qty, 5)
 		self.assertEqual(pick_list.locations[1].sales_order_item, sales_order.items[0].name)
 
 	def test_pick_list_for_items_with_multiple_UOM(self):
-		purchase_receipt = make_purchase_receipt(item_code="_Test Item", qty=10)
+		item_code = make_item().name
+		purchase_receipt = make_purchase_receipt(item_code=item_code, qty=10)
 		purchase_receipt.submit()
 
-		sales_order = frappe.get_doc({
-				'doctype': 'Sales Order',
-				'customer': '_Test Customer',
-				'company': '_Test Company',
-				'items': [{
-					'item_code': '_Test Item',
-					'qty': 1,
-					'conversion_factor': 5,
-					'delivery_date': frappe.utils.today()
-				}, {
-					'item_code': '_Test Item',
-					'qty': 1,
-					'conversion_factor': 1,
-					'delivery_date': frappe.utils.today()
-				}],
-			}).insert()
+		sales_order = frappe.get_doc(
+			{
+				"doctype": "Sales Order",
+				"customer": "_Test Customer",
+				"company": "_Test Company",
+				"items": [
+					{
+						"item_code": item_code,
+						"qty": 1,
+						"conversion_factor": 5,
+						"stock_qty": 5,
+						"delivery_date": frappe.utils.today(),
+						"warehouse": "_Test Warehouse - _TC",
+					},
+					{
+						"item_code": item_code,
+						"qty": 1,
+						"conversion_factor": 1,
+						"delivery_date": frappe.utils.today(),
+						"warehouse": "_Test Warehouse - _TC",
+					},
+				],
+			}
+		).insert()
 		sales_order.submit()
 
-		pick_list = frappe.get_doc({
-			'doctype': 'Pick List',
-			'company': '_Test Company',
-			'customer': '_Test Customer',
-			'items_based_on': 'Sales Order',
-			'purpose': 'Delivery',
-			'locations': [{
-				'item_code': '_Test Item',
-				'qty': 1,
-				'stock_qty': 5,
-				'conversion_factor': 5,
-				'sales_order': sales_order.name,
-				'sales_order_item': sales_order.items[0].name ,
-			}, {
-				'item_code': '_Test Item',
-				'qty': 1,
-				'stock_qty': 1,
-				'conversion_factor': 1,
-				'sales_order': sales_order.name,
-				'sales_order_item': sales_order.items[1].name ,
-			}]
-		})
+		pick_list = frappe.get_doc(
+			{
+				"doctype": "Pick List",
+				"company": "_Test Company",
+				"customer": "_Test Customer",
+				"items_based_on": "Sales Order",
+				"purpose": "Delivery",
+				"locations": [
+					{
+						"item_code": item_code,
+						"qty": 2,
+						"stock_qty": 1,
+						"conversion_factor": 0.5,
+						"sales_order": sales_order.name,
+						"sales_order_item": sales_order.items[0].name,
+					},
+					{
+						"item_code": item_code,
+						"qty": 1,
+						"stock_qty": 1,
+						"conversion_factor": 1,
+						"sales_order": sales_order.name,
+						"sales_order_item": sales_order.items[1].name,
+					},
+				],
+			}
+		)
 		pick_list.set_item_locations()
 		pick_list.submit()
 
@@ -350,7 +417,9 @@
 
 		self.assertEqual(pick_list.locations[0].qty, delivery_note.items[0].qty)
 		self.assertEqual(pick_list.locations[1].qty, delivery_note.items[1].qty)
-		self.assertEqual(sales_order.items[0].conversion_factor, delivery_note.items[0].conversion_factor)
+		self.assertEqual(
+			sales_order.items[0].conversion_factor, delivery_note.items[0].conversion_factor
+		)
 
 		pick_list.cancel()
 		sales_order.cancel()
@@ -363,22 +432,30 @@
 				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 = 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 = 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)
 
@@ -389,14 +466,195 @@
 		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
+	def test_multiple_dn_creation(self):
+		sales_order_1 = frappe.get_doc(
+			{
+				"doctype": "Sales Order",
+				"customer": "_Test Customer",
+				"company": "_Test Company",
+				"items": [
+					{
+						"item_code": "_Test Item",
+						"qty": 1,
+						"conversion_factor": 1,
+						"delivery_date": frappe.utils.today(),
+					}
+				],
+			}
+		).insert()
+		sales_order_1.submit()
+		sales_order_2 = frappe.get_doc(
+			{
+				"doctype": "Sales Order",
+				"customer": "_Test Customer 1",
+				"company": "_Test Company",
+				"items": [
+					{
+						"item_code": "_Test Item 2",
+						"qty": 1,
+						"conversion_factor": 1,
+						"delivery_date": frappe.utils.today(),
+					},
+				],
+			}
+		).insert()
+		sales_order_2.submit()
+		pick_list = frappe.get_doc(
+			{
+				"doctype": "Pick List",
+				"company": "_Test Company",
+				"items_based_on": "Sales Order",
+				"purpose": "Delivery",
+				"picker": "P001",
+				"locations": [
+					{
+						"item_code": "_Test Item ",
+						"qty": 1,
+						"stock_qty": 1,
+						"conversion_factor": 1,
+						"sales_order": sales_order_1.name,
+						"sales_order_item": sales_order_1.items[0].name,
+					},
+					{
+						"item_code": "_Test Item 2",
+						"qty": 1,
+						"stock_qty": 1,
+						"conversion_factor": 1,
+						"sales_order": sales_order_2.name,
+						"sales_order_item": sales_order_2.items[0].name,
+					},
+				],
+			}
+		)
+		pick_list.set_item_locations()
+		pick_list.submit()
+		create_delivery_note(pick_list.name)
+		for dn in frappe.get_all(
+			"Delivery Note",
+			filters={"pick_list": pick_list.name, "customer": "_Test Customer"},
+			fields={"name"},
+		):
+			for dn_item in frappe.get_doc("Delivery Note", dn.name).get("items"):
+				self.assertEqual(dn_item.item_code, "_Test Item")
+				self.assertEqual(dn_item.against_sales_order, sales_order_1.name)
+				self.assertEqual(dn_item.pick_list_item, pick_list.locations[dn_item.idx - 1].name)
 
-	# def test_pick_list_from_sales_order(self):
-	# 	pass
+		for dn in frappe.get_all(
+			"Delivery Note",
+			filters={"pick_list": pick_list.name, "customer": "_Test Customer 1"},
+			fields={"name"},
+		):
+			for dn_item in frappe.get_doc("Delivery Note", dn.name).get("items"):
+				self.assertEqual(dn_item.item_code, "_Test Item 2")
+				self.assertEqual(dn_item.against_sales_order, sales_order_2.name)
+		# test DN creation without so
+		pick_list_1 = frappe.get_doc(
+			{
+				"doctype": "Pick List",
+				"company": "_Test Company",
+				"purpose": "Delivery",
+				"picker": "P001",
+				"locations": [
+					{
+						"item_code": "_Test Item ",
+						"qty": 1,
+						"stock_qty": 1,
+						"conversion_factor": 1,
+					},
+					{
+						"item_code": "_Test Item 2",
+						"qty": 2,
+						"stock_qty": 2,
+						"conversion_factor": 1,
+					},
+				],
+			}
+		)
+		pick_list_1.set_item_locations()
+		pick_list_1.submit()
+		create_delivery_note(pick_list_1.name)
+		for dn in frappe.get_all(
+			"Delivery Note", filters={"pick_list": pick_list_1.name}, fields={"name"}
+		):
+			for dn_item in frappe.get_doc("Delivery Note", dn.name).get("items"):
+				if dn_item.item_code == "_Test Item":
+					self.assertEqual(dn_item.qty, 1)
+				if dn_item.item_code == "_Test Item 2":
+					self.assertEqual(dn_item.qty, 2)
 
-	# def test_pick_list_from_work_order(self):
-	# 	pass
+	def test_picklist_with_multi_uom(self):
+		warehouse = "_Test Warehouse - _TC"
+		item = make_item(properties={"uoms": [dict(uom="Box", conversion_factor=24)]}).name
+		make_stock_entry(item=item, to_warehouse=warehouse, qty=1000)
 
-	# def test_pick_list_from_material_request(self):
-	# 	pass
+		so = make_sales_order(item_code=item, qty=10, rate=42, uom="Box")
+		pl = create_pick_list(so.name)
+		# pick half the qty
+		for loc in pl.locations:
+			loc.picked_qty = loc.stock_qty / 2
+		pl.save()
+		pl.submit()
+
+		so.reload()
+		self.assertEqual(so.per_picked, 50)
+
+	def test_picklist_with_bundles(self):
+		warehouse = "_Test Warehouse - _TC"
+
+		quantities = [5, 2]
+		bundle, components = create_product_bundle(quantities, warehouse=warehouse)
+		bundle_items = dict(zip(components, quantities))
+
+		so = make_sales_order(item_code=bundle, qty=3, rate=42)
+
+		pl = create_pick_list(so.name)
+		pl.save()
+		self.assertEqual(len(pl.locations), 2)
+		for item in pl.locations:
+			self.assertEqual(item.stock_qty, bundle_items[item.item_code] * 3)
+
+		# check picking status on sales order
+		pl.submit()
+		so.reload()
+		self.assertEqual(so.per_picked, 100)
+
+		# deliver
+		dn = create_delivery_note(pl.name).submit()
+		self.assertEqual(dn.items[0].rate, 42)
+		self.assertEqual(dn.packed_items[0].warehouse, warehouse)
+		so.reload()
+		self.assertEqual(so.per_delivered, 100)
+
+	def test_picklist_with_partial_bundles(self):
+		# from test_records.json
+		warehouse = "_Test Warehouse - _TC"
+
+		quantities = [5, 2]
+		bundle, components = create_product_bundle(quantities, warehouse=warehouse)
+
+		so = make_sales_order(item_code=bundle, qty=4, rate=42)
+
+		pl = create_pick_list(so.name)
+		for loc in pl.locations:
+			loc.picked_qty = loc.qty / 2
+
+		pl.save().submit()
+		so.reload()
+		self.assertEqual(so.per_picked, 50)
+
+		# deliver half qty
+		dn = create_delivery_note(pl.name).submit()
+		self.assertEqual(dn.items[0].rate, 42)
+		so.reload()
+		self.assertEqual(so.per_delivered, 50)
+
+		pl = create_pick_list(so.name)
+		pl.save().submit()
+		so.reload()
+		self.assertEqual(so.per_picked, 100)
+
+		# deliver remaining
+		dn = create_delivery_note(pl.name).submit()
+		self.assertEqual(dn.items[0].rate, 42)
+		so.reload()
+		self.assertEqual(so.per_delivered, 100)
diff --git a/erpnext/stock/doctype/pick_list_item/pick_list_item.json b/erpnext/stock/doctype/pick_list_item/pick_list_item.json
index 805286d..a6f8c0d 100644
--- a/erpnext/stock/doctype/pick_list_item/pick_list_item.json
+++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json
@@ -27,6 +27,7 @@
   "column_break_15",
   "sales_order",
   "sales_order_item",
+  "product_bundle_item",
   "material_request",
   "material_request_item"
  ],
@@ -146,6 +147,7 @@
   {
    "fieldname": "sales_order_item",
    "fieldtype": "Data",
+   "hidden": 1,
    "label": "Sales Order Item",
    "read_only": 1
   },
@@ -177,11 +179,19 @@
    "fieldtype": "Data",
    "label": "Item Group",
    "read_only": 1
+  },
+  {
+   "description": "product bundle item row's name in sales order. Also indicates that picked item is to be used for a product bundle",
+   "fieldname": "product_bundle_item",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Product Bundle Item",
+   "read_only": 1
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2021-09-28 12:02:16.923056",
+ "modified": "2022-04-22 05:27:38.497997",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Pick List Item",
@@ -190,5 +200,6 @@
  "quick_entry": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/price_list/price_list.py b/erpnext/stock/doctype/price_list/price_list.py
index 8a3172e..554055f 100644
--- a/erpnext/stock/doctype/price_list/price_list.py
+++ b/erpnext/stock/doctype/price_list/price_list.py
@@ -31,9 +31,11 @@
 				frappe.set_value("Buying Settings", "Buying Settings", "buying_price_list", self.name)
 
 	def update_item_price(self):
-		frappe.db.sql("""update `tabItem Price` set currency=%s,
+		frappe.db.sql(
+			"""update `tabItem Price` set currency=%s,
 			buying=%s, selling=%s, modified=NOW() where price_list=%s""",
-			(self.currency, cint(self.buying), cint(self.selling), self.name))
+			(self.currency, cint(self.buying), cint(self.selling), self.name),
+		)
 
 	def check_impact_on_shopping_cart(self):
 		"Check if Price List currency change impacts E Commerce Cart."
@@ -66,12 +68,14 @@
 	def delete_price_list_details_key(self):
 		frappe.cache().hdel("price_list_details", self.name)
 
+
 def get_price_list_details(price_list):
 	price_list_details = frappe.cache().hget("price_list_details", price_list)
 
 	if not price_list_details:
-		price_list_details = frappe.get_cached_value("Price List", price_list,
-			["currency", "price_not_uom_dependent", "enabled"], as_dict=1)
+		price_list_details = frappe.get_cached_value(
+			"Price List", price_list, ["currency", "price_not_uom_dependent", "enabled"], as_dict=1
+		)
 
 		if not price_list_details or not price_list_details.get("enabled"):
 			throw(_("Price List {0} is disabled or does not exist").format(price_list))
diff --git a/erpnext/stock/doctype/price_list/test_price_list.py b/erpnext/stock/doctype/price_list/test_price_list.py
index b8218b9..9366093 100644
--- a/erpnext/stock/doctype/price_list/test_price_list.py
+++ b/erpnext/stock/doctype/price_list/test_price_list.py
@@ -6,4 +6,4 @@
 
 # test_ignore = ["Item"]
 
-test_records = frappe.get_test_records('Price List')
+test_records = frappe.get_test_records("Price List")
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index 0182ed5..754404b 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -46,8 +46,6 @@
 		erpnext.queries.setup_queries(frm, "Warehouse", function() {
 			return erpnext.queries.warehouse(frm.doc);
 		});
-
-		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
 	},
 
 	refresh: function(frm) {
@@ -200,7 +198,7 @@
 			cur_frm.add_custom_button(__('Reopen'), this.reopen_purchase_receipt, __("Status"))
 		}
 
-		this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted==="Yes");
+		this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted);
 	}
 
 	make_purchase_invoice() {
@@ -298,10 +296,10 @@
 frappe.provide("erpnext.buying");
 
 frappe.ui.form.on("Purchase Receipt", "is_subcontracted", function(frm) {
-	if (frm.doc.is_subcontracted === "Yes") {
+	if (frm.doc.is_subcontracted) {
 		erpnext.buying.get_default_bom(frm);
 	}
-	frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted==="Yes");
+	frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted);
 });
 
 frappe.ui.form.on('Purchase Receipt Item', {
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
index b54a90e..923ceb3 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -24,6 +24,10 @@
   "apply_putaway_rule",
   "is_return",
   "return_against",
+  "accounting_dimensions_section",
+  "cost_center",
+  "dimension_col_break",
+  "project",
   "section_addresses",
   "supplier_address",
   "contact_person",
@@ -104,12 +108,7 @@
   "terms_section_break",
   "tc_name",
   "terms",
-  "bill_no",
-  "bill_date",
-  "accounting_details_section",
-  "provisional_expense_account",
   "more_info",
-  "project",
   "status",
   "amended_from",
   "range",
@@ -437,17 +436,16 @@
    "fieldtype": "Column Break"
   },
   {
-   "default": "No",
+   "default": "0",
    "fieldname": "is_subcontracted",
-   "fieldtype": "Select",
-   "label": "Raw Materials Consumed",
+   "fieldtype": "Check",
+   "label": "Is Subcontracted",
    "oldfieldname": "is_subcontracted",
    "oldfieldtype": "Select",
-   "options": "No\nYes",
    "print_hide": 1
   },
   {
-   "depends_on": "eval:doc.is_subcontracted==\"Yes\"",
+   "depends_on": "eval:doc.is_subcontracted",
    "fieldname": "supplier_warehouse",
    "fieldtype": "Link",
    "label": "Supplier Warehouse",
@@ -868,24 +866,6 @@
    "oldfieldtype": "Text Editor"
   },
   {
-   "fieldname": "bill_no",
-   "fieldtype": "Data",
-   "hidden": 1,
-   "label": "Bill No",
-   "oldfieldname": "bill_no",
-   "oldfieldtype": "Data",
-   "print_hide": 1
-  },
-  {
-   "fieldname": "bill_date",
-   "fieldtype": "Date",
-   "hidden": 1,
-   "label": "Bill Date",
-   "oldfieldname": "bill_date",
-   "oldfieldtype": "Date",
-   "print_hide": 1
-  },
-  {
    "collapsible": 1,
    "fieldname": "more_info",
    "fieldtype": "Section Break",
@@ -1149,23 +1129,26 @@
   },
   {
    "collapsible": 1,
-   "fieldname": "accounting_details_section",
+   "fieldname": "accounting_dimensions_section",
    "fieldtype": "Section Break",
-   "label": "Accounting Details"
+   "label": "Accounting Dimensions"
   },
   {
-   "fieldname": "provisional_expense_account",
+   "fieldname": "cost_center",
    "fieldtype": "Link",
-   "hidden": 1,
-   "label": "Provisional Expense Account",
-   "options": "Account"
+   "label": "Cost Center",
+   "options": "Cost Center"
+  },
+  {
+   "fieldname": "dimension_col_break",
+   "fieldtype": "Column Break"
   }
  ],
  "icon": "fa fa-truck",
  "idx": 261,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-02-01 11:40:52.690984",
+ "modified": "2022-05-27 15:59:18.550583",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 33e40c8..fcf0cd1 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -16,82 +16,85 @@
 from erpnext.controllers.buying_controller import BuyingController
 from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_transaction
 
-form_grid_templates = {
-	"items": "templates/form_grid/item_grid.html"
-}
+form_grid_templates = {"items": "templates/form_grid/item_grid.html"}
+
 
 class PurchaseReceipt(BuyingController):
 	def __init__(self, *args, **kwargs):
 		super(PurchaseReceipt, self).__init__(*args, **kwargs)
-		self.status_updater = [{
-			'target_dt': 'Purchase Order Item',
-			'join_field': 'purchase_order_item',
-			'target_field': 'received_qty',
-			'target_parent_dt': 'Purchase Order',
-			'target_parent_field': 'per_received',
-			'target_ref_field': 'qty',
-			'source_dt': 'Purchase Receipt Item',
-			'source_field': 'received_qty',
-			'second_source_dt': 'Purchase Invoice Item',
-			'second_source_field': 'received_qty',
-			'second_join_field': 'po_detail',
-			'percent_join_field': 'purchase_order',
-			'overflow_type': 'receipt',
-			'second_source_extra_cond': """ and exists(select name from `tabPurchase Invoice`
-				where name=`tabPurchase Invoice Item`.parent and update_stock = 1)"""
-		},
-		{
-			'source_dt': 'Purchase Receipt Item',
-			'target_dt': 'Material Request Item',
-			'join_field': 'material_request_item',
-			'target_field': 'received_qty',
-			'target_parent_dt': 'Material Request',
-			'target_parent_field': 'per_received',
-			'target_ref_field': 'stock_qty',
-			'source_field': 'stock_qty',
-			'percent_join_field': 'material_request'
-		},
-		{
-			'source_dt': 'Purchase Receipt Item',
-			'target_dt': 'Purchase Invoice Item',
-			'join_field': 'purchase_invoice_item',
-			'target_field': 'received_qty',
-			'target_parent_dt': 'Purchase Invoice',
-			'target_parent_field': 'per_received',
-			'target_ref_field': 'qty',
-			'source_field': 'received_qty',
-			'percent_join_field': 'purchase_invoice',
-			'overflow_type': 'receipt'
-		}]
+		self.status_updater = [
+			{
+				"target_dt": "Purchase Order Item",
+				"join_field": "purchase_order_item",
+				"target_field": "received_qty",
+				"target_parent_dt": "Purchase Order",
+				"target_parent_field": "per_received",
+				"target_ref_field": "qty",
+				"source_dt": "Purchase Receipt Item",
+				"source_field": "received_qty",
+				"second_source_dt": "Purchase Invoice Item",
+				"second_source_field": "received_qty",
+				"second_join_field": "po_detail",
+				"percent_join_field": "purchase_order",
+				"overflow_type": "receipt",
+				"second_source_extra_cond": """ and exists(select name from `tabPurchase Invoice`
+				where name=`tabPurchase Invoice Item`.parent and update_stock = 1)""",
+			},
+			{
+				"source_dt": "Purchase Receipt Item",
+				"target_dt": "Material Request Item",
+				"join_field": "material_request_item",
+				"target_field": "received_qty",
+				"target_parent_dt": "Material Request",
+				"target_parent_field": "per_received",
+				"target_ref_field": "stock_qty",
+				"source_field": "stock_qty",
+				"percent_join_field": "material_request",
+			},
+			{
+				"source_dt": "Purchase Receipt Item",
+				"target_dt": "Purchase Invoice Item",
+				"join_field": "purchase_invoice_item",
+				"target_field": "received_qty",
+				"target_parent_dt": "Purchase Invoice",
+				"target_parent_field": "per_received",
+				"target_ref_field": "qty",
+				"source_field": "received_qty",
+				"percent_join_field": "purchase_invoice",
+				"overflow_type": "receipt",
+			},
+		]
 
 		if cint(self.is_return):
-			self.status_updater.extend([
-				{
-					'source_dt': 'Purchase Receipt Item',
-					'target_dt': 'Purchase Order Item',
-					'join_field': 'purchase_order_item',
-					'target_field': 'returned_qty',
-					'source_field': '-1 * qty',
-					'second_source_dt': 'Purchase Invoice Item',
-					'second_source_field': '-1 * qty',
-					'second_join_field': 'po_detail',
-					'extra_cond': """ and exists (select name from `tabPurchase Receipt`
+			self.status_updater.extend(
+				[
+					{
+						"source_dt": "Purchase Receipt Item",
+						"target_dt": "Purchase Order Item",
+						"join_field": "purchase_order_item",
+						"target_field": "returned_qty",
+						"source_field": "-1 * qty",
+						"second_source_dt": "Purchase Invoice Item",
+						"second_source_field": "-1 * qty",
+						"second_join_field": "po_detail",
+						"extra_cond": """ and exists (select name from `tabPurchase Receipt`
 						where name=`tabPurchase Receipt Item`.parent and is_return=1)""",
-					'second_source_extra_cond': """ and exists (select name from `tabPurchase Invoice`
-						where name=`tabPurchase Invoice Item`.parent and is_return=1 and update_stock=1)"""
-				},
-				{
-					'source_dt': 'Purchase Receipt Item',
-					'target_dt': 'Purchase Receipt Item',
-					'join_field': 'purchase_receipt_item',
-					'target_field': 'returned_qty',
-					'target_parent_dt': 'Purchase Receipt',
-					'target_parent_field': 'per_returned',
-					'target_ref_field': 'received_stock_qty',
-					'source_field': '-1 * received_stock_qty',
-					'percent_join_field_parent': 'return_against'
-				}
-			])
+						"second_source_extra_cond": """ and exists (select name from `tabPurchase Invoice`
+						where name=`tabPurchase Invoice Item`.parent and is_return=1 and update_stock=1)""",
+					},
+					{
+						"source_dt": "Purchase Receipt Item",
+						"target_dt": "Purchase Receipt Item",
+						"join_field": "purchase_receipt_item",
+						"target_field": "returned_qty",
+						"target_parent_dt": "Purchase Receipt",
+						"target_parent_field": "per_returned",
+						"target_ref_field": "received_stock_qty",
+						"source_field": "-1 * received_stock_qty",
+						"percent_join_field_parent": "return_against",
+					},
+				]
+			)
 
 	def before_validate(self):
 		from erpnext.stock.doctype.putaway_rule.putaway_rule import apply_putaway_rule
@@ -103,8 +106,8 @@
 		self.validate_posting_time()
 		super(PurchaseReceipt, self).validate()
 
-		if self._action=="submit":
-			self.make_batches('warehouse')
+		if self._action == "submit":
+			self.make_batches("warehouse")
 		else:
 			self.set_status()
 
@@ -124,77 +127,95 @@
 		self.reset_default_field_value("rejected_warehouse", "items", "rejected_warehouse")
 		self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
 
-
 	def validate_cwip_accounts(self):
-		for item in self.get('items'):
+		for item in self.get("items"):
 			if item.is_fixed_asset and is_cwip_accounting_enabled(item.asset_category):
 				# check cwip accounts before making auto assets
 				# Improves UX by not giving messages of "Assets Created" before throwing error of not finding arbnb account
 				arbnb_account = self.get_company_default("asset_received_but_not_billed")
-				cwip_account = get_asset_account("capital_work_in_progress_account", asset_category = item.asset_category, \
-					company = self.company)
+				cwip_account = get_asset_account(
+					"capital_work_in_progress_account", asset_category=item.asset_category, company=self.company
+				)
 				break
 
 	def validate_provisional_expense_account(self):
-		provisional_accounting_for_non_stock_items = \
-			cint(frappe.db.get_value('Company', self.company, 'enable_provisional_accounting_for_non_stock_items'))
+		provisional_accounting_for_non_stock_items = cint(
+			frappe.db.get_value(
+				"Company", self.company, "enable_provisional_accounting_for_non_stock_items"
+			)
+		)
 
-		if provisional_accounting_for_non_stock_items:
-			default_provisional_account = self.get_company_default("default_provisional_account")
-			if not self.provisional_expense_account:
-				self.provisional_expense_account = default_provisional_account
+		if not provisional_accounting_for_non_stock_items:
+			return
+
+		default_provisional_account = self.get_company_default("default_provisional_account")
+		for item in self.get("items"):
+			if not item.get("provisional_expense_account"):
+				item.provisional_expense_account = default_provisional_account
 
 	def validate_with_previous_doc(self):
-		super(PurchaseReceipt, self).validate_with_previous_doc({
-			"Purchase Order": {
-				"ref_dn_field": "purchase_order",
-				"compare_fields": [["supplier", "="], ["company", "="],	["currency", "="]],
-			},
-			"Purchase Order Item": {
-				"ref_dn_field": "purchase_order_item",
-				"compare_fields": [["project", "="], ["uom", "="], ["item_code", "="]],
-				"is_child_table": True,
-				"allow_duplicate_prev_row_id": True
+		super(PurchaseReceipt, self).validate_with_previous_doc(
+			{
+				"Purchase Order": {
+					"ref_dn_field": "purchase_order",
+					"compare_fields": [["supplier", "="], ["company", "="], ["currency", "="]],
+				},
+				"Purchase Order Item": {
+					"ref_dn_field": "purchase_order_item",
+					"compare_fields": [["project", "="], ["uom", "="], ["item_code", "="]],
+					"is_child_table": True,
+					"allow_duplicate_prev_row_id": True,
+				},
 			}
-		})
+		)
 
-		if cint(frappe.db.get_single_value('Buying Settings', 'maintain_same_rate')) and not self.is_return:
-			self.validate_rate_with_reference_doc([["Purchase Order", "purchase_order", "purchase_order_item"]])
+		if (
+			cint(frappe.db.get_single_value("Buying Settings", "maintain_same_rate")) and not self.is_return
+		):
+			self.validate_rate_with_reference_doc(
+				[["Purchase Order", "purchase_order", "purchase_order_item"]]
+			)
 
 	def po_required(self):
-		if frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes':
-			for d in self.get('items'):
+		if frappe.db.get_value("Buying Settings", None, "po_required") == "Yes":
+			for d in self.get("items"):
 				if not d.purchase_order:
 					frappe.throw(_("Purchase Order number required for Item {0}").format(d.item_code))
 
 	def get_already_received_qty(self, po, po_detail):
-		qty = frappe.db.sql("""select sum(qty) from `tabPurchase Receipt Item`
+		qty = frappe.db.sql(
+			"""select sum(qty) from `tabPurchase Receipt Item`
 			where purchase_order_item = %s and docstatus = 1
 			and purchase_order=%s
-			and parent != %s""", (po_detail, po, self.name))
+			and parent != %s""",
+			(po_detail, po, self.name),
+		)
 		return qty and flt(qty[0][0]) or 0.0
 
 	def get_po_qty_and_warehouse(self, po_detail):
-		po_qty, po_warehouse = frappe.db.get_value("Purchase Order Item", po_detail,
-			["qty", "warehouse"])
+		po_qty, po_warehouse = frappe.db.get_value(
+			"Purchase Order Item", po_detail, ["qty", "warehouse"]
+		)
 		return po_qty, po_warehouse
 
 	# Check for Closed status
 	def check_on_hold_or_closed_status(self):
-		check_list =[]
-		for d in self.get('items'):
-			if (d.meta.get_field('purchase_order') and d.purchase_order
-				and d.purchase_order not in check_list):
+		check_list = []
+		for d in self.get("items"):
+			if (
+				d.meta.get_field("purchase_order") and d.purchase_order and d.purchase_order not in check_list
+			):
 				check_list.append(d.purchase_order)
-				check_on_hold_or_closed_status('Purchase Order', d.purchase_order)
+				check_on_hold_or_closed_status("Purchase Order", d.purchase_order)
 
 	# on submit
 	def on_submit(self):
 		super(PurchaseReceipt, self).on_submit()
 
 		# Check for Approving Authority
-		frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
-			self.company, self.base_grand_total)
+		frappe.get_doc("Authorization Control").validate_approving_authority(
+			self.doctype, self.company, self.base_grand_total
+		)
 
 		self.update_prevdoc_status()
 		if flt(self.per_billed) < 100:
@@ -202,13 +223,13 @@
 		else:
 			self.db_set("status", "Completed")
 
-
 		# Updating stock ledger should always be called after updating prevdoc status,
 		# because updating ordered qty, reserved_qty_for_subcontract in bin
 		# depends upon updated ordered qty in PO
 		self.update_stock_ledger()
 
 		from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
+
 		update_serial_nos_after_submit(self, "items")
 
 		self.make_gl_entries()
@@ -216,10 +237,12 @@
 		self.set_consumed_qty_in_po()
 
 	def check_next_docstatus(self):
-		submit_rv = frappe.db.sql("""select t1.name
+		submit_rv = frappe.db.sql(
+			"""select t1.name
 			from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2
 			where t1.name = t2.parent and t2.purchase_receipt = %s and t1.docstatus = 1""",
-			(self.name))
+			(self.name),
+		)
 		if submit_rv:
 			frappe.throw(_("Purchase Invoice {0} is already submitted").format(self.submit_rv[0][0]))
 
@@ -228,10 +251,12 @@
 
 		self.check_on_hold_or_closed_status()
 		# Check if Purchase Invoice has been submitted against current Purchase Order
-		submitted = frappe.db.sql("""select t1.name
+		submitted = frappe.db.sql(
+			"""select t1.name
 			from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2
 			where t1.name = t2.parent and t2.purchase_receipt = %s and t1.docstatus = 1""",
-			self.name)
+			self.name,
+		)
 		if submitted:
 			frappe.throw(_("Purchase Invoice {0} is already submitted").format(submitted[0][0]))
 
@@ -243,19 +268,24 @@
 		self.update_stock_ledger()
 		self.make_gl_entries_on_cancel()
 		self.repost_future_sle_and_gle()
-		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
+		self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Repost Item Valuation")
 		self.delete_auto_created_batches()
 		self.set_consumed_qty_in_po()
 
 	@frappe.whitelist()
 	def get_current_stock(self):
-		for d in self.get('supplied_items'):
+		for d in self.get("supplied_items"):
 			if self.supplier_warehouse:
-				bin = frappe.db.sql("select actual_qty from `tabBin` where item_code = %s and warehouse = %s", (d.rm_item_code, self.supplier_warehouse), as_dict = 1)
-				d.current_stock = bin and flt(bin[0]['actual_qty']) or 0
+				bin = frappe.db.sql(
+					"select actual_qty from `tabBin` where item_code = %s and warehouse = %s",
+					(d.rm_item_code, self.supplier_warehouse),
+					as_dict=1,
+				)
+				d.current_stock = bin and flt(bin[0]["actual_qty"]) or 0
 
 	def get_gl_entries(self, warehouse_account=None):
 		from erpnext.accounts.general_ledger import process_gl_map
+
 		gl_entries = []
 
 		self.make_item_gl_entries(gl_entries, warehouse_account=warehouse_account)
@@ -276,31 +306,46 @@
 
 		warehouse_with_no_account = []
 		stock_items = self.get_stock_items()
-		provisional_accounting_for_non_stock_items = \
-				cint(frappe.db.get_value('Company', self.company, 'enable_provisional_accounting_for_non_stock_items'))
+		provisional_accounting_for_non_stock_items = cint(
+			frappe.db.get_value(
+				"Company", self.company, "enable_provisional_accounting_for_non_stock_items"
+			)
+		)
 
 		exchange_rate_map, net_rate_map = get_purchase_document_details(self)
 
 		for d in self.get("items"):
 			if d.item_code in stock_items and flt(d.valuation_rate) and flt(d.qty):
 				if warehouse_account.get(d.warehouse):
-					stock_value_diff = frappe.db.get_value("Stock Ledger Entry",
-						{"voucher_type": "Purchase Receipt", "voucher_no": self.name,
-						"voucher_detail_no": d.name, "warehouse": d.warehouse, "is_cancelled": 0}, "stock_value_difference")
+					stock_value_diff = frappe.db.get_value(
+						"Stock Ledger Entry",
+						{
+							"voucher_type": "Purchase Receipt",
+							"voucher_no": self.name,
+							"voucher_detail_no": d.name,
+							"warehouse": d.warehouse,
+							"is_cancelled": 0,
+						},
+						"stock_value_difference",
+					)
 
 					warehouse_account_name = warehouse_account[d.warehouse]["account"]
 					warehouse_account_currency = warehouse_account[d.warehouse]["account_currency"]
 					supplier_warehouse_account = warehouse_account.get(self.supplier_warehouse, {}).get("account")
-					supplier_warehouse_account_currency = warehouse_account.get(self.supplier_warehouse, {}).get("account_currency")
+					supplier_warehouse_account_currency = warehouse_account.get(self.supplier_warehouse, {}).get(
+						"account_currency"
+					)
 					remarks = self.get("remarks") or _("Accounting Entry for Stock")
 
 					# If PR is sub-contracted and fg item rate is zero
 					# in that case if account for source and target warehouse are same,
 					# then GL entries should not be posted
-					if flt(stock_value_diff) == flt(d.rm_supp_cost) \
-						and warehouse_account.get(self.supplier_warehouse) \
-						and warehouse_account_name == supplier_warehouse_account:
-							continue
+					if (
+						flt(stock_value_diff) == flt(d.rm_supp_cost)
+						and warehouse_account.get(self.supplier_warehouse)
+						and warehouse_account_name == supplier_warehouse_account
+					):
+						continue
 
 					self.add_gl_entry(
 						gl_entries=gl_entries,
@@ -311,18 +356,24 @@
 						remarks=remarks,
 						against_account=stock_rbnb,
 						account_currency=warehouse_account_currency,
-						item=d)
+						item=d,
+					)
 
 					# GL Entry for from warehouse or Stock Received but not billed
 					# Intentionally passed negative debit amount to avoid incorrect GL Entry validation
-					credit_currency = get_account_currency(warehouse_account[d.from_warehouse]['account']) \
-						if d.from_warehouse else get_account_currency(stock_rbnb)
+					credit_currency = (
+						get_account_currency(warehouse_account[d.from_warehouse]["account"])
+						if d.from_warehouse
+						else get_account_currency(stock_rbnb)
+					)
 
-					credit_amount = flt(d.base_net_amount, d.precision("base_net_amount")) \
-						if credit_currency == self.company_currency else flt(d.net_amount, d.precision("net_amount"))
+					credit_amount = (
+						flt(d.base_net_amount, d.precision("base_net_amount"))
+						if credit_currency == self.company_currency
+						else flt(d.net_amount, d.precision("net_amount"))
+					)
 					if credit_amount:
-						account = warehouse_account[d.from_warehouse]['account'] \
-								if d.from_warehouse else stock_rbnb
+						account = warehouse_account[d.from_warehouse]["account"] if d.from_warehouse else stock_rbnb
 
 						self.add_gl_entry(
 							gl_entries=gl_entries,
@@ -334,16 +385,20 @@
 							against_account=warehouse_account_name,
 							debit_in_account_currency=-1 * credit_amount,
 							account_currency=credit_currency,
-							item=d)
+							item=d,
+						)
 
 						# check if the exchange rate has changed
-						if d.get('purchase_invoice'):
-							if exchange_rate_map[d.purchase_invoice] and \
-								self.conversion_rate != exchange_rate_map[d.purchase_invoice] and \
-								d.net_rate == net_rate_map[d.purchase_invoice_item]:
+						if d.get("purchase_invoice"):
+							if (
+								exchange_rate_map[d.purchase_invoice]
+								and self.conversion_rate != exchange_rate_map[d.purchase_invoice]
+								and d.net_rate == net_rate_map[d.purchase_invoice_item]
+							):
 
-								discrepancy_caused_by_exchange_rate_difference = (d.qty * d.net_rate) * \
-									(exchange_rate_map[d.purchase_invoice] - self.conversion_rate)
+								discrepancy_caused_by_exchange_rate_difference = (d.qty * d.net_rate) * (
+									exchange_rate_map[d.purchase_invoice] - self.conversion_rate
+								)
 
 								self.add_gl_entry(
 									gl_entries=gl_entries,
@@ -355,7 +410,8 @@
 									against_account=self.supplier,
 									debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
 									account_currency=credit_currency,
-									item=d)
+									item=d,
+								)
 
 								self.add_gl_entry(
 									gl_entries=gl_entries,
@@ -367,14 +423,18 @@
 									against_account=self.supplier,
 									debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
 									account_currency=credit_currency,
-									item=d)
+									item=d,
+								)
 
 					# Amount added through landed-cos-voucher
 					if d.landed_cost_voucher_amount and landed_cost_entries:
 						for account, amount in landed_cost_entries[(d.item_code, d.name)].items():
 							account_currency = get_account_currency(account)
-							credit_amount = (flt(amount["base_amount"]) if (amount["base_amount"] or
-								account_currency!=self.company_currency) else flt(amount["amount"]))
+							credit_amount = (
+								flt(amount["base_amount"])
+								if (amount["base_amount"] or account_currency != self.company_currency)
+								else flt(amount["amount"])
+							)
 
 							self.add_gl_entry(
 								gl_entries=gl_entries,
@@ -387,7 +447,8 @@
 								credit_in_account_currency=flt(amount["amount"]),
 								account_currency=account_currency,
 								project=d.project,
-								item=d)
+								item=d,
+							)
 
 					# sub-contracting warehouse
 					if flt(d.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse):
@@ -400,22 +461,32 @@
 							remarks=remarks,
 							against_account=warehouse_account_name,
 							account_currency=supplier_warehouse_account_currency,
-							item=d)
+							item=d,
+						)
 
 					# divisional loss adjustment
-					valuation_amount_as_per_doc = flt(d.base_net_amount, d.precision("base_net_amount")) + \
-						flt(d.landed_cost_voucher_amount) + flt(d.rm_supp_cost) + flt(d.item_tax_amount)
+					valuation_amount_as_per_doc = (
+						flt(d.base_net_amount, d.precision("base_net_amount"))
+						+ flt(d.landed_cost_voucher_amount)
+						+ flt(d.rm_supp_cost)
+						+ flt(d.item_tax_amount)
+					)
 
-					divisional_loss = flt(valuation_amount_as_per_doc - stock_value_diff,
-						d.precision("base_net_amount"))
+					divisional_loss = flt(
+						valuation_amount_as_per_doc - stock_value_diff, d.precision("base_net_amount")
+					)
 
 					if divisional_loss:
 						if self.is_return or flt(d.item_tax_amount):
 							loss_account = expenses_included_in_valuation
 						else:
-							loss_account = self.get_company_default("default_expense_account", ignore_validation=True) or stock_rbnb
+							loss_account = (
+								self.get_company_default("default_expense_account", ignore_validation=True) or stock_rbnb
+							)
 
-						cost_center = d.cost_center or frappe.get_cached_value("Company", self.company, "cost_center")
+						cost_center = d.cost_center or frappe.get_cached_value(
+							"Company", self.company, "cost_center"
+						)
 
 						self.add_gl_entry(
 							gl_entries=gl_entries,
@@ -427,21 +498,35 @@
 							against_account=warehouse_account_name,
 							account_currency=credit_currency,
 							project=d.project,
-							item=d)
+							item=d,
+						)
 
-				elif d.warehouse not in warehouse_with_no_account or \
-					d.rejected_warehouse not in warehouse_with_no_account:
-						warehouse_with_no_account.append(d.warehouse)
-			elif d.item_code not in stock_items and not d.is_fixed_asset and flt(d.qty) and provisional_accounting_for_non_stock_items:
-				self.add_provisional_gl_entry(d, gl_entries, self.posting_date)
+				elif (
+					d.warehouse not in warehouse_with_no_account
+					or d.rejected_warehouse not in warehouse_with_no_account
+				):
+					warehouse_with_no_account.append(d.warehouse)
+			elif (
+				d.item_code not in stock_items
+				and not d.is_fixed_asset
+				and flt(d.qty)
+				and provisional_accounting_for_non_stock_items
+			):
+				self.add_provisional_gl_entry(
+					d, gl_entries, self.posting_date, d.get("provisional_expense_account")
+				)
 
 		if warehouse_with_no_account:
-			frappe.msgprint(_("No accounting entries for the following warehouses") + ": \n" +
-				"\n".join(warehouse_with_no_account))
+			frappe.msgprint(
+				_("No accounting entries for the following warehouses")
+				+ ": \n"
+				+ "\n".join(warehouse_with_no_account)
+			)
 
-	def add_provisional_gl_entry(self, item, gl_entries, posting_date, reverse=0):
-		provisional_expense_account = self.get('provisional_expense_account')
-		credit_currency = get_account_currency(provisional_expense_account)
+	def add_provisional_gl_entry(
+		self, item, gl_entries, posting_date, provisional_account, reverse=0
+	):
+		credit_currency = get_account_currency(provisional_account)
 		debit_currency = get_account_currency(item.expense_account)
 		expense_account = item.expense_account
 		remarks = self.get("remarks") or _("Accounting Entry for Service")
@@ -449,11 +534,13 @@
 
 		if reverse:
 			multiplication_factor = -1
-			expense_account = frappe.db.get_value('Purchase Receipt Item', {'name': item.get('pr_detail')}, ['expense_account'])
+			expense_account = frappe.db.get_value(
+				"Purchase Receipt Item", {"name": item.get("pr_detail")}, ["expense_account"]
+			)
 
 		self.add_gl_entry(
 			gl_entries=gl_entries,
-			account=provisional_expense_account,
+			account=provisional_account,
 			cost_center=item.cost_center,
 			debit=0.0,
 			credit=multiplication_factor * item.amount,
@@ -463,7 +550,8 @@
 			project=item.project,
 			voucher_detail_no=item.name,
 			item=item,
-			posting_date=posting_date)
+			posting_date=posting_date,
+		)
 
 		self.add_gl_entry(
 			gl_entries=gl_entries,
@@ -472,28 +560,36 @@
 			debit=multiplication_factor * item.amount,
 			credit=0.0,
 			remarks=remarks,
-			against_account=provisional_expense_account,
-			account_currency = debit_currency,
+			against_account=provisional_account,
+			account_currency=debit_currency,
 			project=item.project,
 			voucher_detail_no=item.name,
 			item=item,
-			posting_date=posting_date)
+			posting_date=posting_date,
+		)
 
 	def make_tax_gl_entries(self, gl_entries):
 
 		if erpnext.is_perpetual_inventory_enabled(self.company):
 			expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
 
-		negative_expense_to_be_booked = sum([flt(d.item_tax_amount) for d in self.get('items')])
+		negative_expense_to_be_booked = sum([flt(d.item_tax_amount) for d in self.get("items")])
 		# Cost center-wise amount breakup for other charges included for valuation
 		valuation_tax = {}
 		for tax in self.get("taxes"):
-			if tax.category in ("Valuation", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount):
+			if tax.category in ("Valuation", "Valuation and Total") and flt(
+				tax.base_tax_amount_after_discount_amount
+			):
 				if not tax.cost_center:
-					frappe.throw(_("Cost Center is required in row {0} in Taxes table for type {1}").format(tax.idx, _(tax.category)))
+					frappe.throw(
+						_("Cost Center is required in row {0} in Taxes table for type {1}").format(
+							tax.idx, _(tax.category)
+						)
+					)
 				valuation_tax.setdefault(tax.name, 0)
-				valuation_tax[tax.name] += \
-					(tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.base_tax_amount_after_discount_amount)
+				valuation_tax[tax.name] += (tax.add_deduct_tax == "Add" and 1 or -1) * flt(
+					tax.base_tax_amount_after_discount_amount
+				)
 
 		if negative_expense_to_be_booked and valuation_tax:
 			# Backward compatibility:
@@ -502,10 +598,13 @@
 			# post valuation related charges on "Stock Received But Not Billed"
 			# introduced in 2014 for backward compatibility of expenses already booked in expenses_included_in_valuation account
 
-			negative_expense_booked_in_pi = frappe.db.sql("""select name from `tabPurchase Invoice Item` pi
+			negative_expense_booked_in_pi = frappe.db.sql(
+				"""select name from `tabPurchase Invoice Item` pi
 				where docstatus = 1 and purchase_receipt=%s
 				and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice'
-					and voucher_no=pi.parent and account=%s)""", (self.name, expenses_included_in_valuation))
+					and voucher_no=pi.parent and account=%s)""",
+				(self.name, expenses_included_in_valuation),
+			)
 
 			against_account = ", ".join([d.account for d in gl_entries if flt(d.debit) > 0])
 			total_valuation_amount = sum(valuation_tax.values())
@@ -523,7 +622,9 @@
 					if i == len(valuation_tax):
 						applicable_amount = amount_including_divisional_loss
 					else:
-						applicable_amount = negative_expense_to_be_booked * (valuation_tax[tax.name] / total_valuation_amount)
+						applicable_amount = negative_expense_to_be_booked * (
+							valuation_tax[tax.name] / total_valuation_amount
+						)
 						amount_including_divisional_loss -= applicable_amount
 
 					self.add_gl_entry(
@@ -534,13 +635,28 @@
 						credit=applicable_amount,
 						remarks=self.remarks or _("Accounting Entry for Stock"),
 						against_account=against_account,
-						item=tax)
+						item=tax,
+					)
 
 					i += 1
 
-	def add_gl_entry(self, gl_entries, account, cost_center, debit, credit, remarks, against_account,
-		debit_in_account_currency=None, credit_in_account_currency=None, account_currency=None,
-		project=None, voucher_detail_no=None, item=None, posting_date=None):
+	def add_gl_entry(
+		self,
+		gl_entries,
+		account,
+		cost_center,
+		debit,
+		credit,
+		remarks,
+		against_account,
+		debit_in_account_currency=None,
+		credit_in_account_currency=None,
+		account_currency=None,
+		project=None,
+		voucher_detail_no=None,
+		item=None,
+		posting_date=None,
+	):
 
 		gl_entry = {
 			"account": account,
@@ -580,17 +696,19 @@
 	def add_asset_gl_entries(self, item, gl_entries):
 		arbnb_account = self.get_company_default("asset_received_but_not_billed")
 		# This returns category's cwip account if not then fallback to company's default cwip account
-		cwip_account = get_asset_account("capital_work_in_progress_account", asset_category = item.asset_category, \
-			company = self.company)
+		cwip_account = get_asset_account(
+			"capital_work_in_progress_account", asset_category=item.asset_category, company=self.company
+		)
 
-		asset_amount = flt(item.net_amount) + flt(item.item_tax_amount/self.conversion_rate)
+		asset_amount = flt(item.net_amount) + flt(item.item_tax_amount / self.conversion_rate)
 		base_asset_amount = flt(item.base_net_amount + item.item_tax_amount)
 		remarks = self.get("remarks") or _("Accounting Entry for Asset")
 
 		cwip_account_currency = get_account_currency(cwip_account)
 		# debit cwip account
-		debit_in_account_currency = (base_asset_amount
-			if cwip_account_currency == self.company_currency else asset_amount)
+		debit_in_account_currency = (
+			base_asset_amount if cwip_account_currency == self.company_currency else asset_amount
+		)
 
 		self.add_gl_entry(
 			gl_entries=gl_entries,
@@ -601,12 +719,14 @@
 			remarks=remarks,
 			against_account=arbnb_account,
 			debit_in_account_currency=debit_in_account_currency,
-			item=item)
+			item=item,
+		)
 
 		asset_rbnb_currency = get_account_currency(arbnb_account)
 		# credit arbnb account
-		credit_in_account_currency = (base_asset_amount
-			if asset_rbnb_currency == self.company_currency else asset_amount)
+		credit_in_account_currency = (
+			base_asset_amount if asset_rbnb_currency == self.company_currency else asset_amount
+		)
 
 		self.add_gl_entry(
 			gl_entries=gl_entries,
@@ -617,13 +737,17 @@
 			remarks=remarks,
 			against_account=cwip_account,
 			credit_in_account_currency=credit_in_account_currency,
-			item=item)
+			item=item,
+		)
 
 	def add_lcv_gl_entries(self, item, gl_entries):
-		expenses_included_in_asset_valuation = self.get_company_default("expenses_included_in_asset_valuation")
+		expenses_included_in_asset_valuation = self.get_company_default(
+			"expenses_included_in_asset_valuation"
+		)
 		if not is_cwip_accounting_enabled(item.asset_category):
-			asset_account = get_asset_category_account(asset_category=item.asset_category, \
-					fieldname='fixed_asset_account', company=self.company)
+			asset_account = get_asset_category_account(
+				asset_category=item.asset_category, fieldname="fixed_asset_account", company=self.company
+			)
 		else:
 			# This returns company's default cwip account
 			asset_account = get_asset_account("capital_work_in_progress_account", company=self.company)
@@ -639,7 +763,8 @@
 			remarks=remarks,
 			against_account=asset_account,
 			project=item.project,
-			item=item)
+			item=item,
+		)
 
 		self.add_gl_entry(
 			gl_entries=gl_entries,
@@ -650,11 +775,12 @@
 			remarks=remarks,
 			against_account=expenses_included_in_asset_valuation,
 			project=item.project,
-			item=item)
+			item=item,
+		)
 
 	def update_assets(self, item, valuation_rate):
-		assets = frappe.db.get_all('Asset',
-			filters={ 'purchase_receipt': self.name, 'item_code': item.item_code }
+		assets = frappe.db.get_all(
+			"Asset", filters={"purchase_receipt": self.name, "item_code": item.item_code}
 		)
 
 		for asset in assets:
@@ -670,7 +796,7 @@
 		updated_pr = [self.name]
 		for d in self.get("items"):
 			if d.get("purchase_invoice") and d.get("purchase_invoice_item"):
-				d.db_set('billed_amt', d.amount, update_modified=update_modified)
+				d.db_set("billed_amt", d.amount, update_modified=update_modified)
 			elif d.purchase_order_item:
 				updated_pr += update_billed_amount_based_on_po(d.purchase_order_item, update_modified)
 
@@ -680,24 +806,35 @@
 
 		self.load_from_db()
 
+
 def update_billed_amount_based_on_po(po_detail, update_modified=True):
 	# Billed against Sales Order directly
-	billed_against_po = frappe.db.sql("""select sum(amount) from `tabPurchase Invoice Item`
-		where po_detail=%s and (pr_detail is null or pr_detail = '') and docstatus=1""", po_detail)
+	billed_against_po = frappe.db.sql(
+		"""select sum(amount) from `tabPurchase Invoice Item`
+		where po_detail=%s and (pr_detail is null or pr_detail = '') and docstatus=1""",
+		po_detail,
+	)
 	billed_against_po = billed_against_po and billed_against_po[0][0] or 0
 
 	# Get all Purchase Receipt Item rows against the Purchase Order Item row
-	pr_details = frappe.db.sql("""select pr_item.name, pr_item.amount, pr_item.parent
+	pr_details = frappe.db.sql(
+		"""select pr_item.name, pr_item.amount, pr_item.parent
 		from `tabPurchase Receipt Item` pr_item, `tabPurchase Receipt` pr
 		where pr.name=pr_item.parent and pr_item.purchase_order_item=%s
 			and pr.docstatus=1 and pr.is_return = 0
-		order by pr.posting_date asc, pr.posting_time asc, pr.name asc""", po_detail, as_dict=1)
+		order by pr.posting_date asc, pr.posting_time asc, pr.name asc""",
+		po_detail,
+		as_dict=1,
+	)
 
 	updated_pr = []
 	for pr_item in pr_details:
 		# Get billed amount directly against Purchase Receipt
-		billed_amt_agianst_pr = frappe.db.sql("""select sum(amount) from `tabPurchase Invoice Item`
-			where pr_detail=%s and docstatus=1""", pr_item.name)
+		billed_amt_agianst_pr = frappe.db.sql(
+			"""select sum(amount) from `tabPurchase Invoice Item`
+			where pr_detail=%s and docstatus=1""",
+			pr_item.name,
+		)
 		billed_amt_agianst_pr = billed_amt_agianst_pr and billed_amt_agianst_pr[0][0] or 0
 
 		# Distribute billed amount directly against PO between PRs based on FIFO
@@ -710,12 +847,19 @@
 				billed_amt_agianst_pr += billed_against_po
 				billed_against_po = 0
 
-		frappe.db.set_value("Purchase Receipt Item", pr_item.name, "billed_amt", billed_amt_agianst_pr, update_modified=update_modified)
+		frappe.db.set_value(
+			"Purchase Receipt Item",
+			pr_item.name,
+			"billed_amt",
+			billed_amt_agianst_pr,
+			update_modified=update_modified,
+		)
 
 		updated_pr.append(pr_item.parent)
 
 	return updated_pr
 
+
 def update_billing_percentage(pr_doc, update_modified=True):
 	# Reload as billed amount was set in db directly
 	pr_doc.load_from_db()
@@ -723,15 +867,15 @@
 	# Update Billing % based on pending accepted qty
 	total_amount, total_billed_amount = 0, 0
 	for item in pr_doc.items:
-		return_data = frappe.db.get_list("Purchase Receipt",
-			fields = [
-				"sum(abs(`tabPurchase Receipt Item`.qty)) as qty"
-			],
-			filters = [
+		return_data = frappe.db.get_list(
+			"Purchase Receipt",
+			fields=["sum(abs(`tabPurchase Receipt Item`.qty)) as qty"],
+			filters=[
 				["Purchase Receipt", "docstatus", "=", 1],
 				["Purchase Receipt", "is_return", "=", 1],
-				["Purchase Receipt Item", "purchase_receipt_item", "=", item.name]
-		])
+				["Purchase Receipt Item", "purchase_receipt_item", "=", item.name],
+			],
+		)
 
 		returned_qty = return_data[0].qty if return_data else 0
 		returned_amount = flt(returned_qty) * flt(item.rate)
@@ -749,11 +893,12 @@
 		pr_doc.set_status(update=True)
 		pr_doc.notify_update()
 
+
 @frappe.whitelist()
 def make_purchase_invoice(source_name, target_doc=None):
 	from erpnext.accounts.party import get_payment_terms_template
 
-	doc = frappe.get_doc('Purchase Receipt', source_name)
+	doc = frappe.get_doc("Purchase Receipt", source_name)
 	returned_qty_map = get_returned_qty_map(source_name)
 	invoiced_qty_map = get_invoiced_qty_map(source_name)
 
@@ -762,8 +907,9 @@
 			frappe.throw(_("All items have already been Invoiced/Returned"))
 
 		doc = frappe.get_doc(target)
-		doc.ignore_pricing_rule = 1
-		doc.payment_terms_template = get_payment_terms_template(source.supplier, "Supplier", source.company)
+		doc.payment_terms_template = get_payment_terms_template(
+			source.supplier, "Supplier", source.company
+		)
 		doc.run_method("onload")
 		doc.run_method("set_missing_values")
 		doc.run_method("calculate_taxes_and_totals")
@@ -771,14 +917,20 @@
 
 	def update_item(source_doc, target_doc, source_parent):
 		target_doc.qty, returned_qty = get_pending_qty(source_doc)
-		if frappe.db.get_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice"):
+		if frappe.db.get_single_value(
+			"Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice"
+		):
 			target_doc.rejected_qty = 0
-		target_doc.stock_qty = flt(target_doc.qty) * flt(target_doc.conversion_factor, target_doc.precision("conversion_factor"))
+		target_doc.stock_qty = flt(target_doc.qty) * flt(
+			target_doc.conversion_factor, target_doc.precision("conversion_factor")
+		)
 		returned_qty_map[source_doc.name] = returned_qty
 
 	def get_pending_qty(item_row):
 		qty = item_row.qty
-		if frappe.db.get_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice"):
+		if frappe.db.get_single_value(
+			"Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice"
+		):
 			qty = item_row.received_qty
 		pending_qty = qty - invoiced_qty_map.get(item_row.name, 0)
 		returned_qty = flt(returned_qty_map.get(item_row.name, 0))
@@ -791,68 +943,85 @@
 				returned_qty = 0
 		return pending_qty, returned_qty
 
-
-	doclist = get_mapped_doc("Purchase Receipt", source_name,	{
-		"Purchase Receipt": {
-			"doctype": "Purchase Invoice",
-			"field_map": {
-				"supplier_warehouse":"supplier_warehouse",
-				"is_return": "is_return",
-				"bill_date": "bill_date"
+	doclist = get_mapped_doc(
+		"Purchase Receipt",
+		source_name,
+		{
+			"Purchase Receipt": {
+				"doctype": "Purchase Invoice",
+				"field_map": {
+					"supplier_warehouse": "supplier_warehouse",
+					"is_return": "is_return",
+					"bill_date": "bill_date",
+				},
+				"validation": {
+					"docstatus": ["=", 1],
+				},
 			},
-			"validation": {
-				"docstatus": ["=", 1],
+			"Purchase Receipt Item": {
+				"doctype": "Purchase Invoice Item",
+				"field_map": {
+					"name": "pr_detail",
+					"parent": "purchase_receipt",
+					"purchase_order_item": "po_detail",
+					"purchase_order": "purchase_order",
+					"is_fixed_asset": "is_fixed_asset",
+					"asset_location": "asset_location",
+					"asset_category": "asset_category",
+				},
+				"postprocess": update_item,
+				"filter": lambda d: get_pending_qty(d)[0] <= 0
+				if not doc.get("is_return")
+				else get_pending_qty(d)[0] > 0,
 			},
+			"Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges", "add_if_empty": True},
 		},
-		"Purchase Receipt Item": {
-			"doctype": "Purchase Invoice Item",
-			"field_map": {
-				"name": "pr_detail",
-				"parent": "purchase_receipt",
-				"purchase_order_item": "po_detail",
-				"purchase_order": "purchase_order",
-				"is_fixed_asset": "is_fixed_asset",
-				"asset_location": "asset_location",
-				"asset_category": 'asset_category'
-			},
-			"postprocess": update_item,
-			"filter": lambda d: get_pending_qty(d)[0] <= 0 if not doc.get("is_return") else get_pending_qty(d)[0] > 0
-		},
-		"Purchase Taxes and Charges": {
-			"doctype": "Purchase Taxes and Charges",
-			"add_if_empty": True
-		}
-	}, target_doc, set_missing_values)
+		target_doc,
+		set_missing_values,
+	)
 
+	doclist.set_onload("ignore_price_list", True)
 	return doclist
 
+
 def get_invoiced_qty_map(purchase_receipt):
 	"""returns a map: {pr_detail: invoiced_qty}"""
 	invoiced_qty_map = {}
 
-	for pr_detail, qty in frappe.db.sql("""select pr_detail, qty from `tabPurchase Invoice Item`
-		where purchase_receipt=%s and docstatus=1""", purchase_receipt):
-			if not invoiced_qty_map.get(pr_detail):
-				invoiced_qty_map[pr_detail] = 0
-			invoiced_qty_map[pr_detail] += qty
+	for pr_detail, qty in frappe.db.sql(
+		"""select pr_detail, qty from `tabPurchase Invoice Item`
+		where purchase_receipt=%s and docstatus=1""",
+		purchase_receipt,
+	):
+		if not invoiced_qty_map.get(pr_detail):
+			invoiced_qty_map[pr_detail] = 0
+		invoiced_qty_map[pr_detail] += qty
 
 	return invoiced_qty_map
 
+
 def get_returned_qty_map(purchase_receipt):
 	"""returns a map: {so_detail: returned_qty}"""
-	returned_qty_map = frappe._dict(frappe.db.sql("""select pr_item.purchase_receipt_item, abs(pr_item.qty) as qty
+	returned_qty_map = frappe._dict(
+		frappe.db.sql(
+			"""select pr_item.purchase_receipt_item, abs(pr_item.qty) as qty
 		from `tabPurchase Receipt Item` pr_item, `tabPurchase Receipt` pr
 		where pr.name = pr_item.parent
 			and pr.docstatus = 1
 			and pr.is_return = 1
 			and pr.return_against = %s
-	""", purchase_receipt))
+	""",
+			purchase_receipt,
+		)
+	)
 
 	return returned_qty_map
 
+
 @frappe.whitelist()
 def make_purchase_return(source_name, target_doc=None):
 	from erpnext.controllers.sales_and_purchase_return import make_return_doc
+
 	return make_return_doc("Purchase Receipt", source_name, target_doc)
 
 
@@ -861,35 +1030,48 @@
 	pr = frappe.get_doc("Purchase Receipt", docname)
 	pr.update_status(status)
 
+
 @frappe.whitelist()
-def make_stock_entry(source_name,target_doc=None):
+def make_stock_entry(source_name, target_doc=None):
 	def set_missing_values(source, target):
 		target.stock_entry_type = "Material Transfer"
-		target.purpose =  "Material Transfer"
+		target.purpose = "Material Transfer"
+		target.set_missing_values()
 
-	doclist = get_mapped_doc("Purchase Receipt", source_name,{
-		"Purchase Receipt": {
-			"doctype": "Stock Entry",
-		},
-		"Purchase Receipt Item": {
-			"doctype": "Stock Entry Detail",
-			"field_map": {
-				"warehouse": "s_warehouse",
-				"parent": "reference_purchase_receipt",
-				"batch_no": "batch_no"
+	doclist = get_mapped_doc(
+		"Purchase Receipt",
+		source_name,
+		{
+			"Purchase Receipt": {
+				"doctype": "Stock Entry",
+			},
+			"Purchase Receipt Item": {
+				"doctype": "Stock Entry Detail",
+				"field_map": {
+					"warehouse": "s_warehouse",
+					"parent": "reference_purchase_receipt",
+					"batch_no": "batch_no",
+				},
 			},
 		},
-	}, target_doc, set_missing_values)
+		target_doc,
+		set_missing_values,
+	)
 
 	return doclist
 
+
 @frappe.whitelist()
 def make_inter_company_delivery_note(source_name, target_doc=None):
 	return make_inter_company_transaction("Purchase Receipt", source_name, target_doc)
 
+
 def get_item_account_wise_additional_cost(purchase_document):
-	landed_cost_vouchers = frappe.get_all("Landed Cost Purchase Receipt", fields=["parent"],
-		filters = {"receipt_document": purchase_document, "docstatus": 1})
+	landed_cost_vouchers = frappe.get_all(
+		"Landed Cost Purchase Receipt",
+		fields=["parent"],
+		filters={"receipt_document": purchase_document, "docstatus": 1},
+	)
 
 	if not landed_cost_vouchers:
 		return
@@ -899,9 +1081,9 @@
 	for lcv in landed_cost_vouchers:
 		landed_cost_voucher_doc = frappe.get_doc("Landed Cost Voucher", lcv.parent)
 
-		#Use amount field for total item cost for manually cost distributed LCVs
-		if landed_cost_voucher_doc.distribute_charges_based_on == 'Distribute Manually':
-			based_on_field = 'amount'
+		# Use amount field for total item cost for manually cost distributed LCVs
+		if landed_cost_voucher_doc.distribute_charges_based_on == "Distribute Manually":
+			based_on_field = "amount"
 		else:
 			based_on_field = frappe.scrub(landed_cost_voucher_doc.distribute_charges_based_on)
 
@@ -914,15 +1096,20 @@
 			if item.receipt_document == purchase_document:
 				for account in landed_cost_voucher_doc.taxes:
 					item_account_wise_cost.setdefault((item.item_code, item.purchase_receipt_item), {})
-					item_account_wise_cost[(item.item_code, item.purchase_receipt_item)].setdefault(account.expense_account, {
-						"amount": 0.0,
-						"base_amount": 0.0
-					})
+					item_account_wise_cost[(item.item_code, item.purchase_receipt_item)].setdefault(
+						account.expense_account, {"amount": 0.0, "base_amount": 0.0}
+					)
 
-					item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account]["amount"] += \
-						account.amount * item.get(based_on_field) / total_item_cost
+					item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account][
+						"amount"
+					] += (account.amount * item.get(based_on_field) / total_item_cost)
 
-					item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account]["base_amount"] += \
-						account.base_amount * item.get(based_on_field) / total_item_cost
+					item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account][
+						"base_amount"
+					] += (account.base_amount * item.get(based_on_field) / total_item_cost)
 
 	return item_account_wise_cost
+
+
+def on_doctype_update():
+	frappe.db.add_index("Purchase Receipt", ["supplier", "is_return", "return_against"])
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py
index bdc5435..06ba936 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py
@@ -3,35 +3,23 @@
 
 def get_data():
 	return {
-		'fieldname': 'purchase_receipt_no',
-		'non_standard_fieldnames': {
-			'Purchase Invoice': 'purchase_receipt',
-			'Asset': 'purchase_receipt',
-			'Landed Cost Voucher': 'receipt_document',
-			'Auto Repeat': 'reference_document',
-			'Purchase Receipt': 'return_against'
+		"fieldname": "purchase_receipt_no",
+		"non_standard_fieldnames": {
+			"Purchase Invoice": "purchase_receipt",
+			"Asset": "purchase_receipt",
+			"Landed Cost Voucher": "receipt_document",
+			"Auto Repeat": "reference_document",
+			"Purchase Receipt": "return_against",
 		},
-		'internal_links': {
-			'Purchase Order': ['items', 'purchase_order'],
-			'Project': ['items', 'project'],
-			'Quality Inspection': ['items', 'quality_inspection'],
+		"internal_links": {
+			"Purchase Order": ["items", "purchase_order"],
+			"Project": ["items", "project"],
+			"Quality Inspection": ["items", "quality_inspection"],
 		},
-		'transactions': [
-			{
-				'label': _('Related'),
-				'items': ['Purchase Invoice', 'Landed Cost Voucher', 'Asset']
-			},
-			{
-				'label': _('Reference'),
-				'items': ['Purchase Order', 'Quality Inspection', 'Project']
-			},
-			{
-				'label': _('Returns'),
-				'items': ['Purchase Receipt']
-			},
-			{
-				'label': _('Subscription'),
-				'items': ['Auto Repeat']
-			},
-		]
+		"transactions": [
+			{"label": _("Related"), "items": ["Purchase Invoice", "Landed Cost Voucher", "Asset"]},
+			{"label": _("Reference"), "items": ["Purchase Order", "Quality Inspection", "Project"]},
+			{"label": _("Returns"), "items": ["Purchase Receipt"]},
+			{"label": _("Subscription"), "items": ["Auto Repeat"]},
+		],
 	}
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index fa28f22..be4f274 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -26,15 +26,11 @@
 
 	def test_purchase_receipt_received_qty(self):
 		"""
-			1. Test if received qty is validated against accepted + rejected
-			2. Test if received qty is auto set on save
+		1. Test if received qty is validated against accepted + rejected
+		2. Test if received qty is auto set on save
 		"""
 		pr = make_purchase_receipt(
-			qty=1,
-			rejected_qty=1,
-			received_qty=3,
-			item_code="_Test Item Home Desktop 200",
-			do_not_save=True
+			qty=1, rejected_qty=1, received_qty=3, item_code="_Test Item Home Desktop 200", do_not_save=True
 		)
 		self.assertRaises(QtyMismatchError, pr.save)
 
@@ -51,11 +47,8 @@
 
 		sl_entry = frappe.db.get_all(
 			"Stock Ledger Entry",
-			{
-				"voucher_type": "Purchase Receipt",
-				"voucher_no": pr.name
-			},
-			['actual_qty']
+			{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
+			["actual_qty"],
 		)
 
 		self.assertEqual(len(sl_entry), 1)
@@ -65,47 +58,44 @@
 
 		sl_entry_cancelled = frappe.db.get_all(
 			"Stock Ledger Entry",
-			{
-				"voucher_type": "Purchase Receipt",
-				"voucher_no": pr.name
-			},
-			['actual_qty'],
-			order_by='creation'
+			{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
+			["actual_qty"],
+			order_by="creation",
 		)
 
 		self.assertEqual(len(sl_entry_cancelled), 2)
 		self.assertEqual(sl_entry_cancelled[1].actual_qty, -0.5)
 
 	def test_make_purchase_invoice(self):
-		if not frappe.db.exists('Payment Terms Template', '_Test Payment Terms Template For Purchase Invoice'):
-			frappe.get_doc({
-				'doctype': 'Payment Terms Template',
-				'template_name': '_Test Payment Terms Template For Purchase Invoice',
-				'allocate_payment_based_on_payment_terms': 1,
-				'terms': [
-					{
-						'doctype': 'Payment Terms Template Detail',
-						'invoice_portion': 50.00,
-						'credit_days_based_on': 'Day(s) after invoice date',
-						'credit_days': 00
-					},
-					{
-						'doctype': 'Payment Terms Template Detail',
-						'invoice_portion': 50.00,
-						'credit_days_based_on': 'Day(s) after invoice date',
-						'credit_days': 30
-					}]
-			}).insert()
+		if not frappe.db.exists(
+			"Payment Terms Template", "_Test Payment Terms Template For Purchase Invoice"
+		):
+			frappe.get_doc(
+				{
+					"doctype": "Payment Terms Template",
+					"template_name": "_Test Payment Terms Template For Purchase Invoice",
+					"allocate_payment_based_on_payment_terms": 1,
+					"terms": [
+						{
+							"doctype": "Payment Terms Template Detail",
+							"invoice_portion": 50.00,
+							"credit_days_based_on": "Day(s) after invoice date",
+							"credit_days": 00,
+						},
+						{
+							"doctype": "Payment Terms Template Detail",
+							"invoice_portion": 50.00,
+							"credit_days_based_on": "Day(s) after invoice date",
+							"credit_days": 30,
+						},
+					],
+				}
+			).insert()
 
 		template = frappe.db.get_value(
-			"Payment Terms Template",
-			"_Test Payment Terms Template For Purchase Invoice"
+			"Payment Terms Template", "_Test Payment Terms Template For Purchase Invoice"
 		)
-		old_template_in_supplier = frappe.db.get_value(
-			"Supplier",
-			"_Test Supplier",
-			"payment_terms"
-		)
+		old_template_in_supplier = frappe.db.get_value("Supplier", "_Test Supplier", "payment_terms")
 		frappe.db.set_value("Supplier", "_Test Supplier", "payment_terms", template)
 
 		pr = make_purchase_receipt(do_not_save=True)
@@ -123,23 +113,17 @@
 
 		# test if payment terms are fetched and set in PI
 		self.assertEqual(pi.payment_terms_template, template)
-		self.assertEqual(pi.payment_schedule[0].payment_amount, flt(pi.grand_total)/2)
+		self.assertEqual(pi.payment_schedule[0].payment_amount, flt(pi.grand_total) / 2)
 		self.assertEqual(pi.payment_schedule[0].invoice_portion, 50)
-		self.assertEqual(pi.payment_schedule[1].payment_amount, flt(pi.grand_total)/2)
+		self.assertEqual(pi.payment_schedule[1].payment_amount, flt(pi.grand_total) / 2)
 		self.assertEqual(pi.payment_schedule[1].invoice_portion, 50)
 
 		# teardown
-		pi.delete() # draft PI
+		pi.delete()  # draft PI
 		pr.cancel()
-		frappe.db.set_value(
-			"Supplier",
-			"_Test Supplier",
-			"payment_terms",
-			old_template_in_supplier
-		)
+		frappe.db.set_value("Supplier", "_Test Supplier", "payment_terms", old_template_in_supplier)
 		frappe.get_doc(
-			"Payment Terms Template",
-			"_Test Payment Terms Template For Purchase Invoice"
+			"Payment Terms Template", "_Test Payment Terms Template For Purchase Invoice"
 		).delete()
 
 	def test_purchase_receipt_no_gl_entry(self):
@@ -147,27 +131,19 @@
 
 		existing_bin_qty, existing_bin_stock_value = frappe.db.get_value(
 			"Bin",
-			{
-				"item_code": "_Test Item",
-				"warehouse": "_Test Warehouse - _TC"
-			},
-			["actual_qty", "stock_value"]
+			{"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"},
+			["actual_qty", "stock_value"],
 		)
 
 		if existing_bin_qty < 0:
 			make_stock_entry(
-				item_code="_Test Item",
-				target="_Test Warehouse - _TC",
-				qty=abs(existing_bin_qty)
+				item_code="_Test Item", target="_Test Warehouse - _TC", qty=abs(existing_bin_qty)
 			)
 
 		existing_bin_qty, existing_bin_stock_value = frappe.db.get_value(
 			"Bin",
-			{
-				"item_code": "_Test Item",
-				"warehouse": "_Test Warehouse - _TC"
-			},
-			["actual_qty", "stock_value"]
+			{"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"},
+			["actual_qty", "stock_value"],
 		)
 
 		pr = make_purchase_receipt()
@@ -178,20 +154,15 @@
 				"voucher_type": "Purchase Receipt",
 				"voucher_no": pr.name,
 				"item_code": "_Test Item",
-				"warehouse": "_Test Warehouse - _TC"
+				"warehouse": "_Test Warehouse - _TC",
 			},
-			"stock_value_difference"
+			"stock_value_difference",
 		)
 
 		self.assertEqual(stock_value_difference, 250)
 
 		current_bin_stock_value = frappe.db.get_value(
-			"Bin",
-			{
-				"item_code": "_Test Item",
-				"warehouse": "_Test Warehouse - _TC"
-			},
-			"stock_value"
+			"Bin", {"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"}, "stock_value"
 		)
 		self.assertEqual(current_bin_stock_value, existing_bin_stock_value + 250)
 
@@ -200,7 +171,7 @@
 		pr.cancel()
 
 	def test_batched_serial_no_purchase(self):
-		item = frappe.db.exists("Item", {'item_name': 'Batched Serialized Item'})
+		item = frappe.db.exists("Item", {"item_name": "Batched Serialized Item"})
 		if not item:
 			item = create_item("Batched Serialized Item")
 			item.has_batch_no = 1
@@ -210,34 +181,30 @@
 			item.serial_no_series = "BS-.####"
 			item.save()
 		else:
-			item = frappe.get_doc("Item", {'item_name': 'Batched Serialized Item'})
+			item = frappe.get_doc("Item", {"item_name": "Batched Serialized Item"})
 
 		pr = make_purchase_receipt(item_code=item.name, qty=5, rate=500)
 
-		self.assertTrue(
-			frappe.db.get_value('Batch', {'item': item.name, 'reference_name': pr.name})
-		)
+		self.assertTrue(frappe.db.get_value("Batch", {"item": item.name, "reference_name": pr.name}))
 
 		pr.load_from_db()
 		batch_no = pr.items[0].batch_no
 		pr.cancel()
 
-		self.assertFalse(
-			frappe.db.get_value('Batch', {'item': item.name, 'reference_name': pr.name})
-		)
-		self.assertFalse(frappe.db.get_all('Serial No', {'batch_no': batch_no}))
+		self.assertFalse(frappe.db.get_value("Batch", {"item": item.name, "reference_name": pr.name}))
+		self.assertFalse(frappe.db.get_all("Serial No", {"batch_no": batch_no}))
 
 	def test_duplicate_serial_nos(self):
 		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
 
-		item = frappe.db.exists("Item", {'item_name': 'Test Serialized Item 123'})
+		item = frappe.db.exists("Item", {"item_name": "Test Serialized Item 123"})
 		if not item:
 			item = create_item("Test Serialized Item 123")
 			item.has_serial_no = 1
 			item.serial_no_series = "TSI123-.####"
 			item.save()
 		else:
-			item = frappe.get_doc("Item", {'item_name': 'Test Serialized Item 123'})
+			item = frappe.get_doc("Item", {"item_name": "Test Serialized Item 123"})
 
 		# First make purchase receipt
 		pr = make_purchase_receipt(item_code=item.name, qty=2, rate=500)
@@ -245,12 +212,8 @@
 
 		serial_nos = frappe.db.get_value(
 			"Stock Ledger Entry",
-			{
-				"voucher_type": "Purchase Receipt",
-				"voucher_no": pr.name,
-				"item_code": item.name
-			},
-			"serial_no"
+			{"voucher_type": "Purchase Receipt", "voucher_no": pr.name, "item_code": item.name},
+			"serial_no",
 		)
 
 		serial_nos = get_serial_nos(serial_nos)
@@ -262,21 +225,16 @@
 			item_code=item.name,
 			qty=2,
 			rate=500,
-			serial_no='\n'.join(serial_nos),
-			company='_Test Company 1',
+			serial_no="\n".join(serial_nos),
+			company="_Test Company 1",
 			do_not_submit=True,
-			warehouse = 'Stores - _TC1'
+			warehouse="Stores - _TC1",
 		)
 
 		self.assertRaises(SerialNoDuplicateError, pr_different_company.submit)
 
 		# Then made delivery note to remove the serial nos from stock
-		dn = create_delivery_note(
-			item_code=item.name,
-			qty=2,
-			rate=1500,
-			serial_no='\n'.join(serial_nos)
-		)
+		dn = create_delivery_note(item_code=item.name, qty=2, rate=1500, serial_no="\n".join(serial_nos))
 		dn.load_from_db()
 		self.assertEquals(get_serial_nos(dn.items[0].serial_no), serial_nos)
 
@@ -288,8 +246,8 @@
 			qty=2,
 			rate=500,
 			posting_date=posting_date,
-			serial_no='\n'.join(serial_nos),
-			do_not_submit=True
+			serial_no="\n".join(serial_nos),
+			do_not_submit=True,
 		)
 
 		self.assertRaises(SerialNoExistsInFutureTransaction, pr1.submit)
@@ -300,29 +258,28 @@
 			qty=2,
 			rate=500,
 			posting_date=posting_date,
-			serial_no='\n'.join(serial_nos),
+			serial_no="\n".join(serial_nos),
 			company="_Test Company 1",
 			do_not_submit=True,
-			warehouse="Stores - _TC1"
+			warehouse="Stores - _TC1",
 		)
 
 		self.assertRaises(SerialNoExistsInFutureTransaction, pr2.submit)
 
 		# Receive the same serial nos after the delivery note posting date and time
-		make_purchase_receipt(
-			item_code=item.name,
-			qty=2,
-			rate=500,
-			serial_no='\n'.join(serial_nos)
-		)
+		make_purchase_receipt(item_code=item.name, qty=2, rate=500, serial_no="\n".join(serial_nos))
 
 		# Raise the error for backdated deliver note entry cancel
 		self.assertRaises(SerialNoExistsInFutureTransaction, dn.cancel)
 
 	def test_purchase_receipt_gl_entry(self):
-		pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
-			warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1",
-			get_multiple_items = True, get_taxes_and_charges = True)
+		pr = make_purchase_receipt(
+			company="_Test Company with perpetual inventory",
+			warehouse="Stores - TCP1",
+			supplier_warehouse="Work In Progress - TCP1",
+			get_multiple_items=True,
+			get_taxes_and_charges=True,
+		)
 
 		self.assertEqual(cint(erpnext.is_perpetual_inventory_enabled(pr.company)), 1)
 
@@ -338,14 +295,14 @@
 				stock_in_hand_account: [750.0, 0.0],
 				"Stock Received But Not Billed - TCP1": [0.0, 500.0],
 				"_Test Account Shipping Charges - TCP1": [0.0, 100.0],
-				"_Test Account Customs Duty - TCP1": [0.0, 150.0]
+				"_Test Account Customs Duty - TCP1": [0.0, 150.0],
 			}
 		else:
 			expected_values = {
 				stock_in_hand_account: [375.0, 0.0],
 				fixed_asset_account: [375.0, 0.0],
 				"Stock Received But Not Billed - TCP1": [0.0, 500.0],
-				"_Test Account Shipping Charges - TCP1": [0.0, 250.0]
+				"_Test Account Shipping Charges - TCP1": [0.0, 250.0],
 			}
 		for gle in gl_entries:
 			self.assertEqual(expected_values[gle.account][0], gle.debit)
@@ -358,22 +315,19 @@
 		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 
 		frappe.db.set_value(
-			"Buying Settings", None,
-			"backflush_raw_materials_of_subcontract_based_on", "BOM"
+			"Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", "BOM"
 		)
 
 		make_stock_entry(
-			item_code="_Test Item", qty=100,
-			target="_Test Warehouse 1 - _TC", basic_rate=100
+			item_code="_Test Item", qty=100, target="_Test Warehouse 1 - _TC", basic_rate=100
 		)
 		make_stock_entry(
-			item_code="_Test Item Home Desktop 100", qty=100,
-			target="_Test Warehouse 1 - _TC", basic_rate=100
+			item_code="_Test Item Home Desktop 100",
+			qty=100,
+			target="_Test Warehouse 1 - _TC",
+			basic_rate=100,
 		)
-		pr = make_purchase_receipt(
-			item_code="_Test FG Item", qty=10,
-			rate=500, is_subcontracted="Yes"
-		)
+		pr = make_purchase_receipt(item_code="_Test FG Item", qty=10, rate=500, is_subcontracted=1)
 		self.assertEqual(len(pr.get("supplied_items")), 2)
 
 		rm_supp_cost = sum(d.amount for d in pr.get("supplied_items"))
@@ -383,32 +337,35 @@
 
 	def test_subcontracting_gle_fg_item_rate_zero(self):
 		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+
 		frappe.db.set_value(
-			"Buying Settings", None,
-			"backflush_raw_materials_of_subcontract_based_on", "BOM"
+			"Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", "BOM"
 		)
 
 		se1 = make_stock_entry(
 			item_code="_Test Item",
 			target="Work In Progress - TCP1",
-			qty=100, basic_rate=100,
-			company="_Test Company with perpetual inventory"
+			qty=100,
+			basic_rate=100,
+			company="_Test Company with perpetual inventory",
 		)
 
 		se2 = make_stock_entry(
 			item_code="_Test Item Home Desktop 100",
 			target="Work In Progress - TCP1",
-			qty=100, basic_rate=100,
-			company="_Test Company with perpetual inventory"
+			qty=100,
+			basic_rate=100,
+			company="_Test Company with perpetual inventory",
 		)
 
 		pr = make_purchase_receipt(
 			item_code="_Test FG Item",
-			qty=10, rate=0,
-			is_subcontracted="Yes",
+			qty=10,
+			rate=0,
+			is_subcontracted=1,
 			company="_Test Company with perpetual inventory",
 			warehouse="Stores - TCP1",
-			supplier_warehouse="Work In Progress - TCP1"
+			supplier_warehouse="Work In Progress - TCP1",
 		)
 
 		gl_entries = get_gl_entries("Purchase Receipt", pr.name)
@@ -421,9 +378,9 @@
 
 	def test_subcontracting_over_receipt(self):
 		"""
-			Behaviour: Raise multiple PRs against one PO that in total
-				receive more than the required qty in the PO.
-			Expected Result: Error Raised for Over Receipt against PO.
+		Behaviour: Raise multiple PRs against one PO that in total
+		        receive more than the required qty in the PO.
+		Expected Result: Error Raised for Over Receipt against PO.
 		"""
 		from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
 		from erpnext.buying.doctype.purchase_order.purchase_order import (
@@ -440,24 +397,23 @@
 		item_code = "_Test Subcontracted FG Item 1"
 		make_subcontracted_item(item_code=item_code)
 
-		po = create_purchase_order(item_code=item_code, qty=1, include_exploded_items=0,
-			is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC")
+		po = create_purchase_order(
+			item_code=item_code,
+			qty=1,
+			include_exploded_items=0,
+			is_subcontracted=1,
+			supplier_warehouse="_Test Warehouse 1 - _TC",
+		)
 
 		# stock raw materials in a warehouse before transfer
 		make_stock_entry(
-			target="_Test Warehouse - _TC",
-			item_code = "Test Extra Item 1",
-			qty=10, basic_rate=100
+			target="_Test Warehouse - _TC", item_code="Test Extra Item 1", qty=10, basic_rate=100
 		)
 		make_stock_entry(
-			target="_Test Warehouse - _TC",
-			item_code = "_Test FG Item",
-			qty=1, basic_rate=100
+			target="_Test Warehouse - _TC", item_code="_Test FG Item", qty=1, basic_rate=100
 		)
 		make_stock_entry(
-			target="_Test Warehouse - _TC",
-			item_code = "Test Extra Item 2",
-			qty=1, basic_rate=100
+			target="_Test Warehouse - _TC", item_code="Test Extra Item 2", qty=1, basic_rate=100
 		)
 
 		rm_items = [
@@ -467,7 +423,7 @@
 				"item_name": "_Test FG Item",
 				"qty": po.supplied_items[0].required_qty,
 				"warehouse": "_Test Warehouse - _TC",
-				"stock_uom": "Nos"
+				"stock_uom": "Nos",
 			},
 			{
 				"item_code": item_code,
@@ -475,8 +431,8 @@
 				"item_name": "Test Extra Item 1",
 				"qty": po.supplied_items[1].required_qty,
 				"warehouse": "_Test Warehouse - _TC",
-				"stock_uom": "Nos"
-			}
+				"stock_uom": "Nos",
+			},
 		]
 		rm_item_string = json.dumps(rm_items)
 		se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string))
@@ -495,15 +451,10 @@
 		pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1)
 		pr_row_1_serial_no = pr.get("items")[0].serial_no
 
-		self.assertEqual(
-			frappe.db.get_value("Serial No", pr_row_1_serial_no, "supplier"),
-			pr.supplier
-		)
+		self.assertEqual(frappe.db.get_value("Serial No", pr_row_1_serial_no, "supplier"), pr.supplier)
 
 		pr.cancel()
-		self.assertFalse(
-			frappe.db.get_value("Serial No", pr_row_1_serial_no, "warehouse")
-		)
+		self.assertFalse(frappe.db.get_value("Serial No", pr_row_1_serial_no, "warehouse"))
 
 	def test_rejected_serial_no(self):
 		pr = frappe.copy_doc(test_records[0])
@@ -518,32 +469,34 @@
 		accepted_serial_nos = pr.get("items")[0].serial_no.split("\n")
 		self.assertEqual(len(accepted_serial_nos), 3)
 		for serial_no in accepted_serial_nos:
-			self.assertEqual(frappe.db.get_value("Serial No", serial_no, "warehouse"),
-				pr.get("items")[0].warehouse)
+			self.assertEqual(
+				frappe.db.get_value("Serial No", serial_no, "warehouse"), pr.get("items")[0].warehouse
+			)
 
 		rejected_serial_nos = pr.get("items")[0].rejected_serial_no.split("\n")
 		self.assertEqual(len(rejected_serial_nos), 2)
 		for serial_no in rejected_serial_nos:
-			self.assertEqual(frappe.db.get_value("Serial No", serial_no, "warehouse"),
-				pr.get("items")[0].rejected_warehouse)
+			self.assertEqual(
+				frappe.db.get_value("Serial No", serial_no, "warehouse"), pr.get("items")[0].rejected_warehouse
+			)
 
 		pr.cancel()
 
 	def test_purchase_return_partial(self):
 		pr = make_purchase_receipt(
 			company="_Test Company with perpetual inventory",
-			warehouse = "Stores - TCP1",
-			supplier_warehouse = "Work in Progress - TCP1"
+			warehouse="Stores - TCP1",
+			supplier_warehouse="Work In Progress - TCP1",
 		)
 
 		return_pr = make_purchase_receipt(
 			company="_Test Company with perpetual inventory",
-			warehouse = "Stores - TCP1",
-			supplier_warehouse = "Work in Progress - TCP1",
+			warehouse="Stores - TCP1",
+			supplier_warehouse="Work In Progress - TCP1",
 			is_return=1,
 			return_against=pr.name,
 			qty=-2,
-			do_not_submit=1
+			do_not_submit=1,
 		)
 		return_pr.items[0].purchase_receipt_item = pr.items[0].name
 		return_pr.submit()
@@ -551,16 +504,12 @@
 		# check sle
 		outgoing_rate = frappe.db.get_value(
 			"Stock Ledger Entry",
-			{
-				"voucher_type": "Purchase Receipt",
-				"voucher_no": return_pr.name
-			},
-			"outgoing_rate"
+			{"voucher_type": "Purchase Receipt", "voucher_no": return_pr.name},
+			"outgoing_rate",
 		)
 
 		self.assertEqual(outgoing_rate, 50)
 
-
 		# check gl entries for return
 		gl_entries = get_gl_entries("Purchase Receipt", return_pr.name)
 
@@ -586,6 +535,7 @@
 		self.assertEqual(pr.per_returned, 40)
 
 		from erpnext.controllers.sales_and_purchase_return import make_return_doc
+
 		return_pr_2 = make_return_doc("Purchase Receipt", pr.name)
 
 		# Check if unreturned amount is mapped in 2nd return
@@ -608,7 +558,7 @@
 		# PR should be completed on billing all unreturned amount
 		self.assertEqual(pr.items[0].billed_amt, 150)
 		self.assertEqual(pr.per_billed, 100)
-		self.assertEqual(pr.status, 'Completed')
+		self.assertEqual(pr.status, "Completed")
 
 		pi.load_from_db()
 		pi.cancel()
@@ -622,18 +572,18 @@
 	def test_purchase_return_full(self):
 		pr = make_purchase_receipt(
 			company="_Test Company with perpetual inventory",
-			warehouse = "Stores - TCP1",
-			supplier_warehouse = "Work in Progress - TCP1"
+			warehouse="Stores - TCP1",
+			supplier_warehouse="Work In Progress - TCP1",
 		)
 
 		return_pr = make_purchase_receipt(
 			company="_Test Company with perpetual inventory",
-			warehouse = "Stores - TCP1",
-			supplier_warehouse = "Work in Progress - TCP1",
+			warehouse="Stores - TCP1",
+			supplier_warehouse="Work In Progress - TCP1",
 			is_return=1,
 			return_against=pr.name,
 			qty=-5,
-			do_not_submit=1
+			do_not_submit=1,
 		)
 		return_pr.items[0].purchase_receipt_item = pr.items[0].name
 		return_pr.submit()
@@ -646,7 +596,7 @@
 		# Check if Original PR updated
 		self.assertEqual(pr.items[0].returned_qty, 5)
 		self.assertEqual(pr.per_returned, 100)
-		self.assertEqual(pr.status, 'Return Issued')
+		self.assertEqual(pr.status, "Return Issued")
 
 		return_pr.cancel()
 		pr.cancel()
@@ -654,32 +604,32 @@
 	def test_purchase_return_for_rejected_qty(self):
 		from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
 
-		rejected_warehouse="_Test Rejected Warehouse - TCP1"
+		rejected_warehouse = "_Test Rejected Warehouse - TCP1"
 		if not frappe.db.exists("Warehouse", rejected_warehouse):
 			get_warehouse(
-				company = "_Test Company with perpetual inventory",
-				abbr = " - TCP1",
-				warehouse_name = "_Test Rejected Warehouse"
+				company="_Test Company with perpetual inventory",
+				abbr=" - TCP1",
+				warehouse_name="_Test Rejected Warehouse",
 			).name
 
 		pr = make_purchase_receipt(
 			company="_Test Company with perpetual inventory",
-			warehouse = "Stores - TCP1",
-			supplier_warehouse = "Work in Progress - TCP1",
+			warehouse="Stores - TCP1",
+			supplier_warehouse="Work In Progress - TCP1",
 			qty=2,
 			rejected_qty=2,
-			rejected_warehouse=rejected_warehouse
+			rejected_warehouse=rejected_warehouse,
 		)
 
 		return_pr = make_purchase_receipt(
 			company="_Test Company with perpetual inventory",
-			warehouse = "Stores - TCP1",
-			supplier_warehouse = "Work in Progress - TCP1",
+			warehouse="Stores - TCP1",
+			supplier_warehouse="Work In Progress - TCP1",
 			is_return=1,
 			return_against=pr.name,
 			qty=-2,
-			rejected_qty = -2,
-			rejected_warehouse=rejected_warehouse
+			rejected_qty=-2,
+			rejected_warehouse=rejected_warehouse,
 		)
 
 		actual_qty = frappe.db.get_value(
@@ -687,9 +637,9 @@
 			{
 				"voucher_type": "Purchase Receipt",
 				"voucher_no": return_pr.name,
-				"warehouse": return_pr.items[0].rejected_warehouse
+				"warehouse": return_pr.items[0].rejected_warehouse,
 			},
-			"actual_qty"
+			"actual_qty",
 		)
 
 		self.assertEqual(actual_qty, -2)
@@ -697,6 +647,44 @@
 		return_pr.cancel()
 		pr.cancel()
 
+	def test_purchase_receipt_for_rejected_gle_without_accepted_warehouse(self):
+		from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
+
+		rejected_warehouse = "_Test Rejected Warehouse - TCP1"
+		if not frappe.db.exists("Warehouse", rejected_warehouse):
+			get_warehouse(
+				company="_Test Company with perpetual inventory",
+				abbr=" - TCP1",
+				warehouse_name="_Test Rejected Warehouse",
+			).name
+
+		pr = make_purchase_receipt(
+			company="_Test Company with perpetual inventory",
+			warehouse="Stores - TCP1",
+			received_qty=2,
+			rejected_qty=2,
+			rejected_warehouse=rejected_warehouse,
+			do_not_save=True,
+		)
+
+		pr.items[0].qty = 0.0
+		pr.items[0].warehouse = ""
+		pr.submit()
+
+		actual_qty = frappe.db.get_value(
+			"Stock Ledger Entry",
+			{
+				"voucher_type": "Purchase Receipt",
+				"voucher_no": pr.name,
+				"warehouse": pr.items[0].rejected_warehouse,
+				"is_cancelled": 0,
+			},
+			"actual_qty",
+		)
+
+		self.assertEqual(actual_qty, 2)
+		self.assertFalse(pr.items[0].warehouse)
+		pr.cancel()
 
 	def test_purchase_return_for_serialized_items(self):
 		def _check_serial_no_values(serial_no, field_values):
@@ -710,24 +698,22 @@
 
 		serial_no = get_serial_nos(pr.get("items")[0].serial_no)[0]
 
-		_check_serial_no_values(serial_no, {
-			"warehouse": "_Test Warehouse - _TC",
-			"purchase_document_no": pr.name
-		})
+		_check_serial_no_values(
+			serial_no, {"warehouse": "_Test Warehouse - _TC", "purchase_document_no": pr.name}
+		)
 
 		return_pr = make_purchase_receipt(
 			item_code="_Test Serialized Item With Series",
 			qty=-1,
 			is_return=1,
 			return_against=pr.name,
-			serial_no=serial_no
+			serial_no=serial_no,
 		)
 
-		_check_serial_no_values(serial_no, {
-			"warehouse": "",
-			"purchase_document_no": pr.name,
-			"delivery_document_no": return_pr.name
-		})
+		_check_serial_no_values(
+			serial_no,
+			{"warehouse": "", "purchase_document_no": pr.name, "delivery_document_no": return_pr.name},
+		)
 
 		return_pr.cancel()
 		pr.reload()
@@ -735,20 +721,12 @@
 
 	def test_purchase_return_for_multi_uom(self):
 		item_code = "_Test Purchase Return For Multi-UOM"
-		if not frappe.db.exists('Item', item_code):
-			item = make_item(item_code, {'stock_uom': 'Box'})
-			row = item.append('uoms', {
-				'uom': 'Unit',
-				'conversion_factor': 0.1
-			})
+		if not frappe.db.exists("Item", item_code):
+			item = make_item(item_code, {"stock_uom": "Box"})
+			row = item.append("uoms", {"uom": "Unit", "conversion_factor": 0.1})
 			row.db_update()
 
-		pr = make_purchase_receipt(
-			item_code=item_code,
-			qty=1,
-			uom="Box",
-			conversion_factor=1.0
-		)
+		pr = make_purchase_receipt(item_code=item_code, qty=1, uom="Box", conversion_factor=1.0)
 		return_pr = make_purchase_receipt(
 			item_code=item_code,
 			qty=-10,
@@ -756,7 +734,7 @@
 			stock_uom="Box",
 			conversion_factor=0.1,
 			is_return=1,
-			return_against=pr.name
+			return_against=pr.name,
 		)
 
 		self.assertEqual(abs(return_pr.items[0].stock_qty), 1.0)
@@ -769,22 +747,18 @@
 			update_purchase_receipt_status,
 		)
 
-		pr = make_purchase_receipt(do_not_submit=True)
-		pr.submit()
+		item = make_item()
+
+		pr = make_purchase_receipt(item_code=item.name)
 
 		update_purchase_receipt_status(pr.name, "Closed")
-		self.assertEqual(
-			frappe.db.get_value("Purchase Receipt", pr.name, "status"), "Closed"
-		)
-
-		pr.reload()
-		pr.cancel()
+		self.assertEqual(frappe.db.get_value("Purchase Receipt", pr.name, "status"), "Closed")
 
 	def test_pr_billing_status(self):
 		"""Flow:
-			1. PO -> PR1 -> PI
-			2. PO -> PI
-			3. PO -> PR2.
+		1. PO -> PR1 -> PI
+		2. PO -> PI
+		3. PO -> PR2.
 		"""
 		from erpnext.buying.doctype.purchase_order.purchase_order import (
 			make_purchase_invoice as make_purchase_invoice_from_po,
@@ -846,19 +820,15 @@
 			item = make_item(item_code, dict(has_serial_no=1))
 
 		serial_no = "12903812901"
-		pr_doc = make_purchase_receipt(item_code=item_code,
-			qty=1, serial_no = serial_no)
+		pr_doc = make_purchase_receipt(item_code=item_code, qty=1, serial_no=serial_no)
 
 		self.assertEqual(
 			serial_no,
 			frappe.db.get_value(
 				"Serial No",
-				{
-					"purchase_document_type": "Purchase Receipt",
-					"purchase_document_no": pr_doc.name
-				},
-				"name"
-			)
+				{"purchase_document_type": "Purchase Receipt", "purchase_document_no": pr_doc.name},
+				"name",
+			),
 		)
 
 		pr_doc.cancel()
@@ -875,12 +845,9 @@
 			serial_no,
 			frappe.db.get_value(
 				"Serial No",
-				{
-					"purchase_document_type": "Purchase Receipt",
-					"purchase_document_no": new_pr_doc.name
-				},
-				"name"
-			)
+				{"purchase_document_type": "Purchase Receipt", "purchase_document_no": new_pr_doc.name},
+				"name",
+			),
 		)
 
 		new_pr_doc.cancel()
@@ -888,40 +855,52 @@
 	def test_auto_asset_creation(self):
 		asset_item = "Test Asset Item"
 
-		if not frappe.db.exists('Item', asset_item):
-			asset_category = frappe.get_all('Asset Category')
+		if not frappe.db.exists("Item", asset_item):
+			asset_category = frappe.get_all("Asset Category")
 
 			if asset_category:
 				asset_category = asset_category[0].name
 
 			if not asset_category:
-				doc = frappe.get_doc({
-					'doctype': 'Asset Category',
-					'asset_category_name': 'Test Asset Category',
-					'depreciation_method': 'Straight Line',
-					'total_number_of_depreciations': 12,
-					'frequency_of_depreciation': 1,
-					'accounts': [{
-						'company_name': '_Test Company',
-						'fixed_asset_account': '_Test Fixed Asset - _TC',
-						'accumulated_depreciation_account': '_Test Accumulated Depreciations - _TC',
-						'depreciation_expense_account': '_Test Depreciations - _TC'
-					}]
-				}).insert()
+				doc = frappe.get_doc(
+					{
+						"doctype": "Asset Category",
+						"asset_category_name": "Test Asset Category",
+						"depreciation_method": "Straight Line",
+						"total_number_of_depreciations": 12,
+						"frequency_of_depreciation": 1,
+						"accounts": [
+							{
+								"company_name": "_Test Company",
+								"fixed_asset_account": "_Test Fixed Asset - _TC",
+								"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
+								"depreciation_expense_account": "_Test Depreciations - _TC",
+							}
+						],
+					}
+				).insert()
 
 				asset_category = doc.name
 
-			item_data = make_item(asset_item, {'is_stock_item':0,
-				'stock_uom': 'Box', 'is_fixed_asset': 1, 'auto_create_assets': 1,
-				'asset_category': asset_category, 'asset_naming_series': 'ABC.###'})
+			item_data = make_item(
+				asset_item,
+				{
+					"is_stock_item": 0,
+					"stock_uom": "Box",
+					"is_fixed_asset": 1,
+					"auto_create_assets": 1,
+					"asset_category": asset_category,
+					"asset_naming_series": "ABC.###",
+				},
+			)
 			asset_item = item_data.item_code
 
 		pr = make_purchase_receipt(item_code=asset_item, qty=3)
-		assets = frappe.db.get_all('Asset', filters={'purchase_receipt': pr.name})
+		assets = frappe.db.get_all("Asset", filters={"purchase_receipt": pr.name})
 
 		self.assertEqual(len(assets), 3)
 
-		location = frappe.db.get_value('Asset', assets[0].name, 'location')
+		location = frappe.db.get_value("Asset", assets[0].name, "location")
 		self.assertEqual(location, "Test Location")
 
 		pr.cancel()
@@ -931,17 +910,18 @@
 
 		pr = make_purchase_receipt(item_code="Test Asset Item", qty=1)
 
-		asset = frappe.get_doc("Asset", {
-			'purchase_receipt': pr.name
-		})
+		asset = frappe.get_doc("Asset", {"purchase_receipt": pr.name})
 		asset.available_for_use_date = frappe.utils.nowdate()
 		asset.gross_purchase_amount = 50.0
-		asset.append("finance_books", {
-			"expected_value_after_useful_life": 10,
-			"depreciation_method": "Straight Line",
-			"total_number_of_depreciations": 3,
-			"frequency_of_depreciation": 1
-		})
+		asset.append(
+			"finance_books",
+			{
+				"expected_value_after_useful_life": 10,
+				"depreciation_method": "Straight Line",
+				"total_number_of_depreciations": 3,
+				"frequency_of_depreciation": 1,
+			},
+		)
 		asset.submit()
 
 		pr_return = make_purchase_return(pr.name)
@@ -961,36 +941,27 @@
 		cost_center = "_Test Cost Center for BS Account - TCP1"
 		create_cost_center(
 			cost_center_name="_Test Cost Center for BS Account",
-			company="_Test Company with perpetual inventory"
+			company="_Test Company with perpetual inventory",
 		)
 
-		if not frappe.db.exists('Location', 'Test Location'):
-			frappe.get_doc({
-				'doctype': 'Location',
-				'location_name': 'Test Location'
-			}).insert()
+		if not frappe.db.exists("Location", "Test Location"):
+			frappe.get_doc({"doctype": "Location", "location_name": "Test Location"}).insert()
 
 		pr = make_purchase_receipt(
 			cost_center=cost_center,
 			company="_Test Company with perpetual inventory",
-			warehouse = "Stores - TCP1",
-			supplier_warehouse = "Work in Progress - TCP1"
+			warehouse="Stores - TCP1",
+			supplier_warehouse="Work In Progress - TCP1",
 		)
 
-		stock_in_hand_account = get_inventory_account(
-			pr.company, pr.get("items")[0].warehouse
-		)
+		stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse)
 		gl_entries = get_gl_entries("Purchase Receipt", pr.name)
 
 		self.assertTrue(gl_entries)
 
 		expected_values = {
-			"Stock Received But Not Billed - TCP1": {
-				"cost_center": cost_center
-			},
-			stock_in_hand_account: {
-				"cost_center": cost_center
-			}
+			"Stock Received But Not Billed - TCP1": {"cost_center": cost_center},
+			stock_in_hand_account: {"cost_center": cost_center},
 		}
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
@@ -998,33 +969,24 @@
 		pr.cancel()
 
 	def test_purchase_receipt_cost_center_with_balance_sheet_account(self):
-		if not frappe.db.exists('Location', 'Test Location'):
-			frappe.get_doc({
-				'doctype': 'Location',
-				'location_name': 'Test Location'
-			}).insert()
+		if not frappe.db.exists("Location", "Test Location"):
+			frappe.get_doc({"doctype": "Location", "location_name": "Test Location"}).insert()
 
 		pr = make_purchase_receipt(
 			company="_Test Company with perpetual inventory",
-			warehouse = "Stores - TCP1",
-			supplier_warehouse = "Work in Progress - TCP1"
+			warehouse="Stores - TCP1",
+			supplier_warehouse="Work In Progress - TCP1",
 		)
 
-		stock_in_hand_account = get_inventory_account(
-			pr.company, pr.get("items")[0].warehouse
-		)
+		stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse)
 		gl_entries = get_gl_entries("Purchase Receipt", pr.name)
 
 		self.assertTrue(gl_entries)
-		cost_center = pr.get('items')[0].cost_center
+		cost_center = pr.get("items")[0].cost_center
 
 		expected_values = {
-			"Stock Received But Not Billed - TCP1": {
-				"cost_center": cost_center
-			},
-			stock_in_hand_account: {
-				"cost_center": cost_center
-			}
+			"Stock Received But Not Billed - TCP1": {"cost_center": cost_center},
+			stock_in_hand_account: {"cost_center": cost_center},
 		}
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
@@ -1040,11 +1002,7 @@
 		po = create_purchase_order()
 		pr = create_pr_against_po(po.name)
 
-		pr1 = make_purchase_receipt(
-			qty=-1,
-			is_return=1, return_against=pr.name,
-			do_not_submit=True
-		)
+		pr1 = make_purchase_receipt(qty=-1, is_return=1, return_against=pr.name, do_not_submit=True)
 		pr1.items[0].purchase_order = po.name
 		pr1.items[0].purchase_order_item = po.items[0].name
 		pr1.items[0].purchase_receipt_item = pr.items[0].name
@@ -1061,14 +1019,17 @@
 
 	def test_make_purchase_invoice_from_pr_with_returned_qty_duplicate_items(self):
 		pr1 = make_purchase_receipt(qty=8, do_not_submit=True)
-		pr1.append("items", {
-			"item_code": "_Test Item",
-			"warehouse": "_Test Warehouse - _TC",
-			"qty": 1,
-			"received_qty": 1,
-			"rate": 100,
-			"conversion_factor": 1.0,
-		})
+		pr1.append(
+			"items",
+			{
+				"item_code": "_Test Item",
+				"warehouse": "_Test Warehouse - _TC",
+				"qty": 1,
+				"received_qty": 1,
+				"rate": 100,
+				"conversion_factor": 1.0,
+			},
+		)
 		pr1.submit()
 
 		pi1 = make_purchase_invoice(pr1.name)
@@ -1077,11 +1038,7 @@
 		pi1.save()
 		pi1.submit()
 
-		pr2 = make_purchase_receipt(
-			qty=-2,
-			is_return=1, return_against=pr1.name,
-			do_not_submit=True
-		)
+		pr2 = make_purchase_receipt(qty=-2, is_return=1, return_against=pr1.name, do_not_submit=True)
 		pr2.items[0].purchase_receipt_item = pr1.items[0].name
 		pr2.submit()
 
@@ -1095,26 +1052,25 @@
 		pr1.cancel()
 
 	def test_stock_transfer_from_purchase_receipt(self):
-		pr1 = make_purchase_receipt(warehouse = 'Work In Progress - TCP1',
-			company="_Test Company with perpetual inventory")
+		pr1 = make_purchase_receipt(
+			warehouse="Work In Progress - TCP1", company="_Test Company with perpetual inventory"
+		)
 
-		pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
-			warehouse = "Stores - TCP1", do_not_save=1)
+		pr = make_purchase_receipt(
+			company="_Test Company with perpetual inventory", warehouse="Stores - TCP1", do_not_save=1
+		)
 
-		pr.supplier_warehouse = ''
-		pr.items[0].from_warehouse = 'Work In Progress - TCP1'
+		pr.supplier_warehouse = ""
+		pr.items[0].from_warehouse = "Work In Progress - TCP1"
 
 		pr.submit()
 
-		gl_entries = get_gl_entries('Purchase Receipt', pr.name)
-		sl_entries = get_sl_entries('Purchase Receipt', pr.name)
+		gl_entries = get_gl_entries("Purchase Receipt", pr.name)
+		sl_entries = get_sl_entries("Purchase Receipt", pr.name)
 
 		self.assertFalse(gl_entries)
 
-		expected_sle = {
-			'Work In Progress - TCP1': -5,
-			'Stores - TCP1': 5
-		}
+		expected_sle = {"Work In Progress - TCP1": -5, "Stores - TCP1": 5}
 
 		for sle in sl_entries:
 			self.assertEqual(expected_sle[sle.warehouse], sle.actual_qty)
@@ -1126,48 +1082,45 @@
 		create_warehouse(
 			"_Test Warehouse for Valuation",
 			company="_Test Company with perpetual inventory",
-			properties={"account": '_Test Account Stock In Hand - TCP1'}
+			properties={"account": "_Test Account Stock In Hand - TCP1"},
 		)
 
 		pr1 = make_purchase_receipt(
-			warehouse = '_Test Warehouse for Valuation - TCP1',
-			company="_Test Company with perpetual inventory"
+			warehouse="_Test Warehouse for Valuation - TCP1",
+			company="_Test Company with perpetual inventory",
 		)
 
 		pr = make_purchase_receipt(
-			company="_Test Company with perpetual inventory",
-			warehouse = "Stores - TCP1",
-			do_not_save=1
+			company="_Test Company with perpetual inventory", warehouse="Stores - TCP1", do_not_save=1
 		)
 
-		pr.items[0].from_warehouse = '_Test Warehouse for Valuation - TCP1'
-		pr.supplier_warehouse = ''
+		pr.items[0].from_warehouse = "_Test Warehouse for Valuation - TCP1"
+		pr.supplier_warehouse = ""
 
-
-		pr.append('taxes', {
-			'charge_type': 'On Net Total',
-			'account_head': '_Test Account Shipping Charges - TCP1',
-			'category': 'Valuation and Total',
-			'cost_center': 'Main - TCP1',
-			'description': 'Test',
-			'rate': 9
-		})
+		pr.append(
+			"taxes",
+			{
+				"charge_type": "On Net Total",
+				"account_head": "_Test Account Shipping Charges - TCP1",
+				"category": "Valuation and Total",
+				"cost_center": "Main - TCP1",
+				"description": "Test",
+				"rate": 9,
+			},
+		)
 
 		pr.submit()
 
-		gl_entries = get_gl_entries('Purchase Receipt', pr.name)
-		sl_entries = get_sl_entries('Purchase Receipt', pr.name)
+		gl_entries = get_gl_entries("Purchase Receipt", pr.name)
+		sl_entries = get_sl_entries("Purchase Receipt", pr.name)
 
 		expected_gle = [
-			['Stock In Hand - TCP1', 272.5, 0.0],
-			['_Test Account Stock In Hand - TCP1', 0.0, 250.0],
-			['_Test Account Shipping Charges - TCP1', 0.0, 22.5]
+			["Stock In Hand - TCP1", 272.5, 0.0],
+			["_Test Account Stock In Hand - TCP1", 0.0, 250.0],
+			["_Test Account Shipping Charges - TCP1", 0.0, 22.5],
 		]
 
-		expected_sle = {
-			'_Test Warehouse for Valuation - TCP1': -5,
-			'Stores - TCP1': 5
-		}
+		expected_sle = {"_Test Warehouse for Valuation - TCP1": -5, "Stores - TCP1": 5}
 
 		for sle in sl_entries:
 			self.assertEqual(expected_sle[sle.warehouse], sle.actual_qty)
@@ -1180,7 +1133,6 @@
 		pr.cancel()
 		pr1.cancel()
 
-
 	def test_subcontracted_pr_for_multi_transfer_batches(self):
 		from erpnext.buying.doctype.purchase_order.purchase_order import (
 			make_purchase_receipt,
@@ -1195,49 +1147,57 @@
 		update_backflush_based_on("Material Transferred for Subcontract")
 		item_code = "_Test Subcontracted FG Item 3"
 
-		make_item('Sub Contracted Raw Material 3', {
-			'is_stock_item': 1,
-			'is_sub_contracted_item': 1,
-			'has_batch_no': 1,
-			'create_new_batch': 1
-		})
+		make_item(
+			"Sub Contracted Raw Material 3",
+			{"is_stock_item": 1, "is_sub_contracted_item": 1, "has_batch_no": 1, "create_new_batch": 1},
+		)
 
-		create_subcontracted_item(item_code=item_code, has_batch_no=1,
-			raw_materials=["Sub Contracted Raw Material 3"])
+		create_subcontracted_item(
+			item_code=item_code, has_batch_no=1, raw_materials=["Sub Contracted Raw Material 3"]
+		)
 
 		order_qty = 500
-		po = create_purchase_order(item_code=item_code, qty=order_qty,
-			is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC")
+		po = create_purchase_order(
+			item_code=item_code,
+			qty=order_qty,
+			is_subcontracted=1,
+			supplier_warehouse="_Test Warehouse 1 - _TC",
+		)
 
-		ste1=make_stock_entry(target="_Test Warehouse - _TC",
-			item_code = "Sub Contracted Raw Material 3", qty=300, basic_rate=100)
-		ste2=make_stock_entry(target="_Test Warehouse - _TC",
-			item_code = "Sub Contracted Raw Material 3", qty=200, basic_rate=100)
+		ste1 = make_stock_entry(
+			target="_Test Warehouse - _TC",
+			item_code="Sub Contracted Raw Material 3",
+			qty=300,
+			basic_rate=100,
+		)
+		ste2 = make_stock_entry(
+			target="_Test Warehouse - _TC",
+			item_code="Sub Contracted Raw Material 3",
+			qty=200,
+			basic_rate=100,
+		)
 
-		transferred_batch = {
-			ste1.items[0].batch_no : 300,
-			ste2.items[0].batch_no : 200
-		}
+		transferred_batch = {ste1.items[0].batch_no: 300, ste2.items[0].batch_no: 200}
 
 		rm_items = [
 			{
-				"item_code":item_code,
-				"rm_item_code":"Sub Contracted Raw Material 3",
-				"item_name":"_Test Item",
-				"qty":300,
-				"warehouse":"_Test Warehouse - _TC",
-				"stock_uom":"Nos",
-				"name": po.supplied_items[0].name
+				"item_code": item_code,
+				"rm_item_code": "Sub Contracted Raw Material 3",
+				"item_name": "_Test Item",
+				"qty": 300,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "Nos",
+				"name": po.supplied_items[0].name,
 			},
 			{
-				"item_code":item_code,
-				"rm_item_code":"Sub Contracted Raw Material 3",
-				"item_name":"_Test Item",
-				"qty":200,
-				"warehouse":"_Test Warehouse - _TC",
-				"stock_uom":"Nos",
-				"name": po.supplied_items[0].name
-			}
+				"item_code": item_code,
+				"rm_item_code": "Sub Contracted Raw Material 3",
+				"item_name": "_Test Item",
+				"qty": 200,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "Nos",
+				"name": po.supplied_items[0].name,
+			},
 		]
 
 		rm_item_string = json.dumps(rm_items)
@@ -1249,11 +1209,8 @@
 
 		supplied_qty = frappe.db.get_value(
 			"Purchase Order Item Supplied",
-			{
-				"parent": po.name,
-				"rm_item_code": "Sub Contracted Raw Material 3"
-			},
-			"supplied_qty"
+			{"parent": po.name, "rm_item_code": "Sub Contracted Raw Material 3"},
+			"supplied_qty",
 		)
 
 		self.assertEqual(supplied_qty, 500.00)
@@ -1273,12 +1230,11 @@
 		ste1.cancel()
 		po.cancel()
 
-
 	def test_po_to_pi_and_po_to_pr_worflow_full(self):
 		"""Test following behaviour:
-			- Create PO
-			- Create PI from PO and submit
-			- Create PR from PO and submit
+		- Create PO
+		- Create PI from PO and submit
+		- Create PR from PO and submit
 		"""
 		from erpnext.buying.doctype.purchase_order import purchase_order, test_purchase_order
 
@@ -1297,16 +1253,16 @@
 
 	def test_po_to_pi_and_po_to_pr_worflow_partial(self):
 		"""Test following behaviour:
-			- Create PO
-			- Create partial PI from PO and submit
-			- Create PR from PO and submit
+		- Create PO
+		- Create partial PI from PO and submit
+		- Create PR from PO and submit
 		"""
 		from erpnext.buying.doctype.purchase_order import purchase_order, test_purchase_order
 
 		po = test_purchase_order.create_purchase_order()
 
 		pi = purchase_order.make_purchase_invoice(po.name)
-		pi.items[0].qty /= 2   # roughly 50%, ^ this function only creates PI with 1 item.
+		pi.items[0].qty /= 2  # roughly 50%, ^ this function only creates PI with 1 item.
 		pi.submit()
 
 		pr = purchase_order.make_purchase_receipt(po.name)
@@ -1329,12 +1285,24 @@
 		from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import (
 			make_purchase_invoice as create_purchase_invoice,
 		)
+		from erpnext.accounts.party import add_party_account
 
-		pi = create_purchase_invoice(company="_Test Company with perpetual inventory",
-			cost_center = "Main - TCP1",
-			warehouse = "Stores - TCP1",
-			expense_account ="_Test Account Cost for Goods Sold - TCP1",
-			currency = "USD", conversion_rate = 70)
+		add_party_account(
+			"Supplier",
+			"_Test Supplier USD",
+			"_Test Company with perpetual inventory",
+			"_Test Payable USD - TCP1",
+		)
+
+		pi = create_purchase_invoice(
+			company="_Test Company with perpetual inventory",
+			cost_center="Main - TCP1",
+			warehouse="Stores - TCP1",
+			expense_account="_Test Account Cost for Goods Sold - TCP1",
+			currency="USD",
+			conversion_rate=70,
+			supplier="_Test Supplier USD",
+		)
 
 		pr = create_purchase_receipt(pi.name)
 		pr.conversion_rate = 80
@@ -1346,19 +1314,16 @@
 
 		# Get exchnage gain and loss account
 		exchange_gain_loss_account = frappe.db.get_value(
-			'Company', pr.company, 'exchange_gain_loss_account'
+			"Company", pr.company, "exchange_gain_loss_account"
 		)
 
 		# fetching the latest GL Entry with exchange gain and loss account account
 		amount = frappe.db.get_value(
-			'GL Entry',
-			{
-				'account': exchange_gain_loss_account,
-				'voucher_no': pr.name
-			},
-			'credit'
+			"GL Entry", {"account": exchange_gain_loss_account, "voucher_no": pr.name}, "credit"
 		)
-		discrepancy_caused_by_exchange_rate_diff = abs(pi.items[0].base_net_amount - pr.items[0].base_net_amount)
+		discrepancy_caused_by_exchange_rate_diff = abs(
+			pi.items[0].base_net_amount - pr.items[0].base_net_amount
+		)
 
 		self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount)
 
@@ -1380,7 +1345,7 @@
 
 		po = create_purchase_order(qty=10, rate=100, do_not_save=1)
 		create_payment_terms_template()
-		po.payment_terms_template = 'Test Receivable Template'
+		po.payment_terms_template = "Test Receivable Template"
 		po.submit()
 
 		pr = make_pr_against_po(po.name, received_qty=10)
@@ -1407,7 +1372,9 @@
 		account = "Stock Received But Not Billed - TCP1"
 
 		make_item(item_code)
-		se = make_stock_entry(item_code=item_code, from_warehouse=warehouse, qty=50, do_not_save=True, rate=0)
+		se = make_stock_entry(
+			item_code=item_code, from_warehouse=warehouse, qty=50, do_not_save=True, rate=0
+		)
 		se.items[0].allow_zero_valuation_rate = 1
 		se.save()
 		se.submit()
@@ -1428,93 +1395,112 @@
 
 
 def get_sl_entries(voucher_type, voucher_no):
-	return frappe.db.sql(""" select actual_qty, warehouse, stock_value_difference
+	return frappe.db.sql(
+		""" select actual_qty, warehouse, stock_value_difference
 		from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s
-		order by posting_time desc""", (voucher_type, voucher_no), as_dict=1)
+		order by posting_time desc""",
+		(voucher_type, voucher_no),
+		as_dict=1,
+	)
+
 
 def get_gl_entries(voucher_type, voucher_no):
-	return frappe.db.sql("""select account, debit, credit, cost_center, is_cancelled
+	return frappe.db.sql(
+		"""select account, debit, credit, cost_center, is_cancelled
 		from `tabGL Entry` where voucher_type=%s and voucher_no=%s
-		order by account desc""", (voucher_type, voucher_no), as_dict=1)
+		order by account desc""",
+		(voucher_type, voucher_no),
+		as_dict=1,
+	)
+
 
 def get_taxes(**args):
 
 	args = frappe._dict(args)
 
-	return [{'account_head': '_Test Account Shipping Charges - TCP1',
-			'add_deduct_tax': 'Add',
-			'category': 'Valuation and Total',
-			'charge_type': 'Actual',
-			'cost_center': args.cost_center or 'Main - TCP1',
-			'description': 'Shipping Charges',
-			'doctype': 'Purchase Taxes and Charges',
-			'parentfield': 'taxes',
-			'rate': 100.0,
-			'tax_amount': 100.0},
-		{'account_head': '_Test Account VAT - TCP1',
-			'add_deduct_tax': 'Add',
-			'category': 'Total',
-			'charge_type': 'Actual',
-			'cost_center': args.cost_center or 'Main - TCP1',
-			'description': 'VAT',
-			'doctype': 'Purchase Taxes and Charges',
-			'parentfield': 'taxes',
-			'rate': 120.0,
-			'tax_amount': 120.0},
-		{'account_head': '_Test Account Customs Duty - TCP1',
-			'add_deduct_tax': 'Add',
-			'category': 'Valuation',
-			'charge_type': 'Actual',
-			'cost_center': args.cost_center or 'Main - TCP1',
-			'description': 'Customs Duty',
-			'doctype': 'Purchase Taxes and Charges',
-			'parentfield': 'taxes',
-			'rate': 150.0,
-			'tax_amount': 150.0}]
+	return [
+		{
+			"account_head": "_Test Account Shipping Charges - TCP1",
+			"add_deduct_tax": "Add",
+			"category": "Valuation and Total",
+			"charge_type": "Actual",
+			"cost_center": args.cost_center or "Main - TCP1",
+			"description": "Shipping Charges",
+			"doctype": "Purchase Taxes and Charges",
+			"parentfield": "taxes",
+			"rate": 100.0,
+			"tax_amount": 100.0,
+		},
+		{
+			"account_head": "_Test Account VAT - TCP1",
+			"add_deduct_tax": "Add",
+			"category": "Total",
+			"charge_type": "Actual",
+			"cost_center": args.cost_center or "Main - TCP1",
+			"description": "VAT",
+			"doctype": "Purchase Taxes and Charges",
+			"parentfield": "taxes",
+			"rate": 120.0,
+			"tax_amount": 120.0,
+		},
+		{
+			"account_head": "_Test Account Customs Duty - TCP1",
+			"add_deduct_tax": "Add",
+			"category": "Valuation",
+			"charge_type": "Actual",
+			"cost_center": args.cost_center or "Main - TCP1",
+			"description": "Customs Duty",
+			"doctype": "Purchase Taxes and Charges",
+			"parentfield": "taxes",
+			"rate": 150.0,
+			"tax_amount": 150.0,
+		},
+	]
+
 
 def get_items(**args):
 	args = frappe._dict(args)
-	return [{
-	"base_amount": 250.0,
-	"conversion_factor": 1.0,
-	"description": "_Test Item",
-	"doctype": "Purchase Receipt Item",
-	"item_code": "_Test Item",
-	"item_name": "_Test Item",
-	"parentfield": "items",
-	"qty": 5.0,
-	"rate": 50.0,
-	"received_qty": 5.0,
-	"rejected_qty": 0.0,
-	"stock_uom": "_Test UOM",
-	"uom": "_Test UOM",
-	"warehouse": args.warehouse or "_Test Warehouse - _TC",
-	"cost_center": args.cost_center or "Main - _TC"
-	},
-	{
-	"base_amount": 250.0,
-	"conversion_factor": 1.0,
-	"description": "_Test Item Home Desktop 100",
-	"doctype": "Purchase Receipt Item",
-	"item_code": "_Test Item Home Desktop 100",
-	"item_name": "_Test Item Home Desktop 100",
-	"parentfield": "items",
-	"qty": 5.0,
-	"rate": 50.0,
-	"received_qty": 5.0,
-	"rejected_qty": 0.0,
-	"stock_uom": "_Test UOM",
-	"uom": "_Test UOM",
-	"warehouse": args.warehouse or "_Test Warehouse 1 - _TC",
-	"cost_center": args.cost_center or "Main - _TC"
-	}]
+	return [
+		{
+			"base_amount": 250.0,
+			"conversion_factor": 1.0,
+			"description": "_Test Item",
+			"doctype": "Purchase Receipt Item",
+			"item_code": "_Test Item",
+			"item_name": "_Test Item",
+			"parentfield": "items",
+			"qty": 5.0,
+			"rate": 50.0,
+			"received_qty": 5.0,
+			"rejected_qty": 0.0,
+			"stock_uom": "_Test UOM",
+			"uom": "_Test UOM",
+			"warehouse": args.warehouse or "_Test Warehouse - _TC",
+			"cost_center": args.cost_center or "Main - _TC",
+		},
+		{
+			"base_amount": 250.0,
+			"conversion_factor": 1.0,
+			"description": "_Test Item Home Desktop 100",
+			"doctype": "Purchase Receipt Item",
+			"item_code": "_Test Item Home Desktop 100",
+			"item_name": "_Test Item Home Desktop 100",
+			"parentfield": "items",
+			"qty": 5.0,
+			"rate": 50.0,
+			"received_qty": 5.0,
+			"rejected_qty": 0.0,
+			"stock_uom": "_Test UOM",
+			"uom": "_Test UOM",
+			"warehouse": args.warehouse or "_Test Warehouse 1 - _TC",
+			"cost_center": args.cost_center or "Main - _TC",
+		},
+	]
+
 
 def make_purchase_receipt(**args):
-	if not frappe.db.exists('Location', 'Test Location'):
-		frappe.get_doc({
-			'doctype': 'Location',
-			'location_name': 'Test Location'
-		}).insert()
+	if not frappe.db.exists("Location", "Test Location"):
+		frappe.get_doc({"doctype": "Location", "location_name": "Test Location"}).insert()
 
 	frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
 	pr = frappe.new_doc("Purchase Receipt")
@@ -1526,7 +1512,7 @@
 		pr.set_posting_time = 1
 	pr.company = args.company or "_Test Company"
 	pr.supplier = args.supplier or "_Test Supplier"
-	pr.is_subcontracted = args.is_subcontracted or "No"
+	pr.is_subcontracted = args.is_subcontracted or 0
 	pr.supplier_warehouse = args.supplier_warehouse or "_Test Warehouse 1 - _TC"
 	pr.currency = args.currency or "INR"
 	pr.is_return = args.is_return
@@ -1538,28 +1524,34 @@
 
 	item_code = args.item or args.item_code or "_Test Item"
 	uom = args.uom or frappe.db.get_value("Item", item_code, "stock_uom") or "_Test UOM"
-	pr.append("items", {
-		"item_code": item_code,
-		"warehouse": args.warehouse or "_Test Warehouse - _TC",
-		"qty": qty,
-		"received_qty": received_qty,
-		"rejected_qty": rejected_qty,
-		"rejected_warehouse": args.rejected_warehouse or "_Test Rejected Warehouse - _TC" if rejected_qty != 0 else "",
-		"rate": args.rate if args.rate != None else 50,
-		"conversion_factor": args.conversion_factor or 1.0,
-		"stock_qty": flt(qty) * (flt(args.conversion_factor) or 1.0),
-		"serial_no": args.serial_no,
-		"batch_no": args.batch_no,
-		"stock_uom": args.stock_uom or "_Test UOM",
-		"uom": uom,
-		"cost_center": args.cost_center or frappe.get_cached_value('Company',  pr.company,  'cost_center'),
-		"asset_location": args.location or "Test Location"
-	})
+	pr.append(
+		"items",
+		{
+			"item_code": item_code,
+			"warehouse": args.warehouse or "_Test Warehouse - _TC",
+			"qty": qty,
+			"received_qty": received_qty,
+			"rejected_qty": rejected_qty,
+			"rejected_warehouse": args.rejected_warehouse or "_Test Rejected Warehouse - _TC"
+			if rejected_qty != 0
+			else "",
+			"rate": args.rate if args.rate != None else 50,
+			"conversion_factor": args.conversion_factor or 1.0,
+			"stock_qty": flt(qty) * (flt(args.conversion_factor) or 1.0),
+			"serial_no": args.serial_no,
+			"batch_no": args.batch_no,
+			"stock_uom": args.stock_uom or "_Test UOM",
+			"uom": uom,
+			"cost_center": args.cost_center
+			or frappe.get_cached_value("Company", pr.company, "cost_center"),
+			"asset_location": args.location or "Test Location",
+		},
+	)
 
 	if args.get_multiple_items:
 		pr.items = []
 
-		company_cost_center = frappe.get_cached_value('Company', pr.company, 'cost_center')
+		company_cost_center = frappe.get_cached_value("Company", pr.company, "cost_center")
 		cost_center = args.cost_center or company_cost_center
 
 		for item in get_items(warehouse=args.warehouse, cost_center=cost_center):
@@ -1575,33 +1567,44 @@
 			pr.submit()
 	return pr
 
+
 def create_subcontracted_item(**args):
 	from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
 
 	args = frappe._dict(args)
 
-	if not frappe.db.exists('Item', args.item_code):
-		make_item(args.item_code, {
-			'is_stock_item': 1,
-			'is_sub_contracted_item': 1,
-			'has_batch_no': args.get("has_batch_no") or 0
-		})
+	if not frappe.db.exists("Item", args.item_code):
+		make_item(
+			args.item_code,
+			{
+				"is_stock_item": 1,
+				"is_sub_contracted_item": 1,
+				"has_batch_no": args.get("has_batch_no") or 0,
+			},
+		)
 
 	if not args.raw_materials:
-		if not frappe.db.exists('Item', "Test Extra Item 1"):
-			make_item("Test Extra Item 1", {
-				'is_stock_item': 1,
-			})
+		if not frappe.db.exists("Item", "Test Extra Item 1"):
+			make_item(
+				"Test Extra Item 1",
+				{
+					"is_stock_item": 1,
+				},
+			)
 
-		if not frappe.db.exists('Item', "Test Extra Item 2"):
-			make_item("Test Extra Item 2", {
-				'is_stock_item': 1,
-			})
+		if not frappe.db.exists("Item", "Test Extra Item 2"):
+			make_item(
+				"Test Extra Item 2",
+				{
+					"is_stock_item": 1,
+				},
+			)
 
-		args.raw_materials = ['_Test FG Item', 'Test Extra Item 1']
+		args.raw_materials = ["_Test FG Item", "Test Extra Item 1"]
 
-	if not frappe.db.get_value('BOM', {'item': args.item_code}, 'name'):
-		make_bom(item = args.item_code, raw_materials = args.get("raw_materials"))
+	if not frappe.db.get_value("BOM", {"item": args.item_code}, "name"):
+		make_bom(item=args.item_code, raw_materials=args.get("raw_materials"))
+
 
 test_dependencies = ["BOM", "Item Price", "Location"]
-test_records = frappe.get_test_records('Purchase Receipt')
+test_records = frappe.get_test_records("Purchase Receipt")
diff --git a/erpnext/stock/doctype/purchase_receipt/test_records.json b/erpnext/stock/doctype/purchase_receipt/test_records.json
index 724e3d7..990ad12 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_records.json
+++ b/erpnext/stock/doctype/purchase_receipt/test_records.json
@@ -92,7 +92,7 @@
   "currency": "INR",
   "doctype": "Purchase Receipt",
   "base_grand_total": 5000.0,
-  "is_subcontracted": "Yes",
+  "is_subcontracted": 1,
   "base_net_total": 5000.0,
   "items": [
    {
diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
index e5994b2..b45d663 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -96,7 +96,6 @@
   "include_exploded_items",
   "batch_no",
   "rejected_serial_no",
-  "expense_account",
   "item_tax_rate",
   "item_weight_details",
   "weight_per_unit",
@@ -107,6 +106,10 @@
   "manufacturer",
   "column_break_16",
   "manufacturer_part_no",
+  "accounting_details_section",
+  "expense_account",
+  "column_break_102",
+  "provisional_expense_account",
   "accounting_dimensions_section",
   "project",
   "dimension_col_break",
@@ -249,6 +252,7 @@
    "width": "100px"
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "stock_uom",
    "fieldtype": "Link",
    "label": "Stock UOM",
@@ -262,6 +266,7 @@
    "width": "100px"
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "conversion_factor",
    "fieldtype": "Float",
    "label": "Conversion Factor",
@@ -544,6 +549,7 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "stock_qty",
    "fieldtype": "Float",
    "label": "Accepted Qty in Stock UOM",
@@ -648,7 +654,7 @@
   },
   {
    "default": "0",
-   "depends_on": "eval:parent.is_subcontracted == 'Yes'",
+   "depends_on": "eval:parent.is_subcontracted",
    "fieldname": "include_exploded_items",
    "fieldtype": "Check",
    "label": "Include Exploded Items",
@@ -875,7 +881,7 @@
    "fieldtype": "Column Break"
   },
   {
-   "depends_on": "returned_qty",
+   "depends_on": "doc.returned_qty",
    "fieldname": "returned_qty",
    "fieldtype": "Float",
    "label": "Returned Qty in Stock UOM",
@@ -884,6 +890,7 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "received_stock_qty",
    "fieldtype": "Float",
    "label": "Received Qty in Stock UOM",
@@ -971,12 +978,27 @@
    "label": "Product Bundle",
    "options": "Product Bundle",
    "read_only": 1
+  },
+  {
+   "fieldname": "provisional_expense_account",
+   "fieldtype": "Link",
+   "label": "Provisional Expense Account",
+   "options": "Account"
+  },
+  {
+   "fieldname": "accounting_details_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Details"
+  },
+  {
+   "fieldname": "column_break_102",
+   "fieldtype": "Column Break"
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2022-02-01 11:32:27.980524",
+ "modified": "2022-06-17 05:32:16.483178",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt Item",
diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.py b/erpnext/stock/doctype/putaway_rule/putaway_rule.py
index 4e472a9..623fbde 100644
--- a/erpnext/stock/doctype/putaway_rule/putaway_rule.py
+++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.py
@@ -24,11 +24,16 @@
 		self.set_stock_capacity()
 
 	def validate_duplicate_rule(self):
-		existing_rule = frappe.db.exists("Putaway Rule", {"item_code": self.item_code, "warehouse": self.warehouse})
+		existing_rule = frappe.db.exists(
+			"Putaway Rule", {"item_code": self.item_code, "warehouse": self.warehouse}
+		)
 		if existing_rule and existing_rule != self.name:
-			frappe.throw(_("Putaway Rule already exists for Item {0} in Warehouse {1}.")
-				.format(frappe.bold(self.item_code), frappe.bold(self.warehouse)),
-				title=_("Duplicate"))
+			frappe.throw(
+				_("Putaway Rule already exists for Item {0} in Warehouse {1}.").format(
+					frappe.bold(self.item_code), frappe.bold(self.warehouse)
+				),
+				title=_("Duplicate"),
+			)
 
 	def validate_priority(self):
 		if self.priority < 1:
@@ -37,18 +42,24 @@
 	def validate_warehouse_and_company(self):
 		company = frappe.db.get_value("Warehouse", self.warehouse, "company")
 		if company != self.company:
-			frappe.throw(_("Warehouse {0} does not belong to Company {1}.")
-				.format(frappe.bold(self.warehouse), frappe.bold(self.company)),
-				title=_("Invalid Warehouse"))
+			frappe.throw(
+				_("Warehouse {0} does not belong to Company {1}.").format(
+					frappe.bold(self.warehouse), frappe.bold(self.company)
+				),
+				title=_("Invalid Warehouse"),
+			)
 
 	def validate_capacity(self):
 		stock_uom = frappe.db.get_value("Item", self.item_code, "stock_uom")
 		balance_qty = get_stock_balance(self.item_code, self.warehouse, nowdate())
 
 		if flt(self.stock_capacity) < flt(balance_qty):
-			frappe.throw(_("Warehouse Capacity for Item '{0}' must be greater than the existing stock level of {1} {2}.")
-				.format(self.item_code, frappe.bold(balance_qty), stock_uom),
-				title=_("Insufficient Capacity"))
+			frappe.throw(
+				_(
+					"Warehouse Capacity for Item '{0}' must be greater than the existing stock level of {1} {2}."
+				).format(self.item_code, frappe.bold(balance_qty), stock_uom),
+				title=_("Insufficient Capacity"),
+			)
 
 		if not self.capacity:
 			frappe.throw(_("Capacity must be greater than 0"), title=_("Invalid"))
@@ -56,23 +67,26 @@
 	def set_stock_capacity(self):
 		self.stock_capacity = (flt(self.conversion_factor) or 1) * flt(self.capacity)
 
+
 @frappe.whitelist()
 def get_available_putaway_capacity(rule):
-	stock_capacity, item_code, warehouse = frappe.db.get_value("Putaway Rule", rule,
-		["stock_capacity", "item_code", "warehouse"])
+	stock_capacity, item_code, warehouse = frappe.db.get_value(
+		"Putaway Rule", rule, ["stock_capacity", "item_code", "warehouse"]
+	)
 	balance_qty = get_stock_balance(item_code, warehouse, nowdate())
 	free_space = flt(stock_capacity) - flt(balance_qty)
 	return free_space if free_space > 0 else 0
 
+
 @frappe.whitelist()
 def apply_putaway_rule(doctype, items, company, sync=None, purpose=None):
-	""" Applies Putaway Rule on line items.
+	"""Applies Putaway Rule on line items.
 
-		items: List of Purchase Receipt/Stock Entry Items
-		company: Company in the Purchase Receipt/Stock Entry
-		doctype: Doctype to apply rule on
-		purpose: Purpose of Stock Entry
-		sync (optional): Sync with client side only for client side calls
+	items: List of Purchase Receipt/Stock Entry Items
+	company: Company in the Purchase Receipt/Stock Entry
+	doctype: Doctype to apply rule on
+	purpose: Purpose of Stock Entry
+	sync (optional): Sync with client side only for client side calls
 	"""
 	if isinstance(items, str):
 		items = json.loads(items)
@@ -89,16 +103,18 @@
 		item.conversion_factor = flt(item.conversion_factor) or 1.0
 		pending_qty, item_code = flt(item.qty), item.item_code
 		pending_stock_qty = flt(item.transfer_qty) if doctype == "Stock Entry" else flt(item.stock_qty)
-		uom_must_be_whole_number = frappe.db.get_value('UOM', item.uom, 'must_be_whole_number')
+		uom_must_be_whole_number = frappe.db.get_value("UOM", item.uom, "must_be_whole_number")
 
 		if not pending_qty or not item_code:
 			updated_table = add_row(item, pending_qty, source_warehouse or item.warehouse, updated_table)
 			continue
 
-		at_capacity, rules = get_ordered_putaway_rules(item_code, company, source_warehouse=source_warehouse)
+		at_capacity, rules = get_ordered_putaway_rules(
+			item_code, company, source_warehouse=source_warehouse
+		)
 
 		if not rules:
-			warehouse = source_warehouse or item.get('warehouse')
+			warehouse = source_warehouse or item.get("warehouse")
 			if at_capacity:
 				# rules available, but no free space
 				items_not_accomodated.append([item_code, pending_qty])
@@ -117,23 +133,28 @@
 
 		for rule in item_wise_rules[key]:
 			if pending_stock_qty > 0 and rule.free_space:
-				stock_qty_to_allocate = flt(rule.free_space) if pending_stock_qty >= flt(rule.free_space) else pending_stock_qty
+				stock_qty_to_allocate = (
+					flt(rule.free_space) if pending_stock_qty >= flt(rule.free_space) else pending_stock_qty
+				)
 				qty_to_allocate = stock_qty_to_allocate / item.conversion_factor
 
 				if uom_must_be_whole_number:
 					qty_to_allocate = floor(qty_to_allocate)
 					stock_qty_to_allocate = qty_to_allocate * item.conversion_factor
 
-				if not qty_to_allocate: break
+				if not qty_to_allocate:
+					break
 
-				updated_table = add_row(item, qty_to_allocate, rule.warehouse, updated_table,
-					rule.name, serial_nos=serial_nos)
+				updated_table = add_row(
+					item, qty_to_allocate, rule.warehouse, updated_table, rule.name, serial_nos=serial_nos
+				)
 
 				pending_stock_qty -= stock_qty_to_allocate
 				pending_qty -= qty_to_allocate
 				rule["free_space"] -= stock_qty_to_allocate
 
-				if not pending_stock_qty > 0: break
+				if not pending_stock_qty > 0:
+					break
 
 		# if pending qty after applying all rules, add row without warehouse
 		if pending_stock_qty > 0:
@@ -146,13 +167,14 @@
 		items[:] = updated_table
 		frappe.msgprint(_("Applied putaway rules."), alert=True)
 
-	if sync and json.loads(sync): # sync with client side
+	if sync and json.loads(sync):  # sync with client side
 		return items
 
-def _items_changed(old, new, doctype: str) -> bool:
-	""" Check if any items changed by application of putaway rules.
 
-		If not, changing item table can have side effects since `name` items also changes.
+def _items_changed(old, new, doctype: str) -> bool:
+	"""Check if any items changed by application of putaway rules.
+
+	If not, changing item table can have side effects since `name` items also changes.
 	"""
 	if len(old) != len(new):
 		return True
@@ -161,13 +183,22 @@
 
 	if doctype == "Stock Entry":
 		compare_keys = ("item_code", "t_warehouse", "transfer_qty", "serial_no")
-		sort_key = lambda item: (item.item_code, cstr(item.t_warehouse),  # noqa
-				flt(item.transfer_qty), cstr(item.serial_no))
+		sort_key = lambda item: (  # noqa
+			item.item_code,
+			cstr(item.t_warehouse),
+			flt(item.transfer_qty),
+			cstr(item.serial_no),
+		)
 	else:
 		# purchase receipt / invoice
 		compare_keys = ("item_code", "warehouse", "stock_qty", "received_qty", "serial_no")
-		sort_key = lambda item: (item.item_code, cstr(item.warehouse),  # noqa
-				flt(item.stock_qty), flt(item.received_qty), cstr(item.serial_no))
+		sort_key = lambda item: (  # noqa
+			item.item_code,
+			cstr(item.warehouse),
+			flt(item.stock_qty),
+			flt(item.received_qty),
+			cstr(item.serial_no),
+		)
 
 	old_sorted = sorted(old, key=sort_key)
 	new_sorted = sorted(new, key=sort_key)
@@ -182,18 +213,16 @@
 
 def get_ordered_putaway_rules(item_code, company, source_warehouse=None):
 	"""Returns an ordered list of putaway rules to apply on an item."""
-	filters = {
-		"item_code": item_code,
-		"company": company,
-		"disable": 0
-	}
+	filters = {"item_code": item_code, "company": company, "disable": 0}
 	if source_warehouse:
 		filters.update({"warehouse": ["!=", source_warehouse]})
 
-	rules = frappe.get_all("Putaway Rule",
+	rules = frappe.get_all(
+		"Putaway Rule",
 		fields=["name", "item_code", "stock_capacity", "priority", "warehouse"],
 		filters=filters,
-		order_by="priority asc, capacity desc")
+		order_by="priority asc, capacity desc",
+	)
 
 	if not rules:
 		return False, None
@@ -211,10 +240,11 @@
 		# then there is not enough space left in any rule
 		return True, None
 
-	vacant_rules = sorted(vacant_rules, key = lambda i: (i['priority'], -i['free_space']))
+	vacant_rules = sorted(vacant_rules, key=lambda i: (i["priority"], -i["free_space"]))
 
 	return False, vacant_rules
 
+
 def add_row(item, to_allocate, warehouse, updated_table, rule=None, serial_nos=None):
 	new_updated_table_row = copy.deepcopy(item)
 	new_updated_table_row.idx = 1 if not updated_table else cint(updated_table[-1].idx) + 1
@@ -223,7 +253,9 @@
 
 	if item.doctype == "Stock Entry Detail":
 		new_updated_table_row.t_warehouse = warehouse
-		new_updated_table_row.transfer_qty = flt(to_allocate) * flt(new_updated_table_row.conversion_factor)
+		new_updated_table_row.transfer_qty = flt(to_allocate) * flt(
+			new_updated_table_row.conversion_factor
+		)
 	else:
 		new_updated_table_row.stock_qty = flt(to_allocate) * flt(new_updated_table_row.conversion_factor)
 		new_updated_table_row.warehouse = warehouse
@@ -238,6 +270,7 @@
 	updated_table.append(new_updated_table_row)
 	return updated_table
 
+
 def show_unassigned_items_message(items_not_accomodated):
 	msg = _("The following Items, having Putaway Rules, could not be accomodated:") + "<br><br>"
 	formatted_item_rows = ""
@@ -247,7 +280,9 @@
 		formatted_item_rows += """
 			<td>{0}</td>
 			<td>{1}</td>
-		</tr>""".format(item_link, frappe.bold(entry[1]))
+		</tr>""".format(
+			item_link, frappe.bold(entry[1])
+		)
 
 	msg += """
 		<table class="table">
@@ -257,13 +292,17 @@
 			</thead>
 			{2}
 		</table>
-	""".format(_("Item"), _("Unassigned Qty"), formatted_item_rows)
+	""".format(
+		_("Item"), _("Unassigned Qty"), formatted_item_rows
+	)
 
 	frappe.msgprint(msg, title=_("Insufficient Capacity"), is_minimizable=True, wide=True)
 
+
 def get_serial_nos_to_allocate(serial_nos, to_allocate):
 	if serial_nos:
-		allocated_serial_nos = serial_nos[0: cint(to_allocate)]
-		serial_nos[:] = serial_nos[cint(to_allocate):] # pop out allocated serial nos and modify list
+		allocated_serial_nos = serial_nos[0 : cint(to_allocate)]
+		serial_nos[:] = serial_nos[cint(to_allocate) :]  # pop out allocated serial nos and modify list
 		return "\n".join(allocated_serial_nos) if allocated_serial_nos else ""
-	else: return ""
+	else:
+		return ""
diff --git a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
index 4e8d71f..ab0ca10 100644
--- a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
+++ b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
@@ -15,12 +15,9 @@
 class TestPutawayRule(FrappeTestCase):
 	def setUp(self):
 		if not frappe.db.exists("Item", "_Rice"):
-			make_item("_Rice", {
-				'is_stock_item': 1,
-				'has_batch_no' : 1,
-				'create_new_batch': 1,
-				'stock_uom': 'Kg'
-			})
+			make_item(
+				"_Rice", {"is_stock_item": 1, "has_batch_no": 1, "create_new_batch": 1, "stock_uom": "Kg"}
+			)
 
 		if not frappe.db.exists("Warehouse", {"warehouse_name": "Rack 1"}):
 			create_warehouse("Rack 1")
@@ -36,10 +33,10 @@
 			new_uom.save()
 
 	def assertUnchangedItemsOnResave(self, doc):
-		""" Check if same items remain even after reapplication of rules.
+		"""Check if same items remain even after reapplication of rules.
 
-			This is required since some business logic like subcontracting
-			depends on `name` of items to be same if item isn't changed.
+		This is required since some business logic like subcontracting
+		depends on `name` of items to be same if item isn't changed.
 		"""
 		doc.reload()
 		old_items = {d.name for d in doc.items}
@@ -49,13 +46,14 @@
 
 	def test_putaway_rules_priority(self):
 		"""Test if rule is applied by priority, irrespective of free space."""
-		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
-			uom="Kg")
-		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=300,
-			uom="Kg", priority=2)
+		rule_1 = create_putaway_rule(
+			item_code="_Rice", warehouse=self.warehouse_1, capacity=200, uom="Kg"
+		)
+		rule_2 = create_putaway_rule(
+			item_code="_Rice", warehouse=self.warehouse_2, capacity=300, uom="Kg", priority=2
+		)
 
-		pr = make_purchase_receipt(item_code="_Rice", qty=300, apply_putaway_rule=1,
-			do_not_submit=1)
+		pr = make_purchase_receipt(item_code="_Rice", qty=300, apply_putaway_rule=1, do_not_submit=1)
 		self.assertEqual(len(pr.items), 2)
 		self.assertEqual(pr.items[0].qty, 200)
 		self.assertEqual(pr.items[0].warehouse, self.warehouse_1)
@@ -71,16 +69,19 @@
 	def test_putaway_rules_with_same_priority(self):
 		"""Test if rule with more free space is applied,
 		among two rules with same priority and capacity."""
-		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=500,
-			uom="Kg")
-		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=500,
-			uom="Kg")
+		rule_1 = create_putaway_rule(
+			item_code="_Rice", warehouse=self.warehouse_1, capacity=500, uom="Kg"
+		)
+		rule_2 = create_putaway_rule(
+			item_code="_Rice", warehouse=self.warehouse_2, capacity=500, uom="Kg"
+		)
 
 		# out of 500 kg capacity, occupy 100 kg in warehouse_1
-		stock_receipt = make_stock_entry(item_code="_Rice", target=self.warehouse_1, qty=100, basic_rate=50)
+		stock_receipt = make_stock_entry(
+			item_code="_Rice", target=self.warehouse_1, qty=100, basic_rate=50
+		)
 
-		pr = make_purchase_receipt(item_code="_Rice", qty=700, apply_putaway_rule=1,
-			do_not_submit=1)
+		pr = make_purchase_receipt(item_code="_Rice", qty=700, apply_putaway_rule=1, do_not_submit=1)
 		self.assertEqual(len(pr.items), 2)
 		self.assertEqual(pr.items[0].qty, 500)
 		# warehouse_2 has 500 kg free space, it is given priority
@@ -96,13 +97,14 @@
 
 	def test_putaway_rules_with_insufficient_capacity(self):
 		"""Test if qty exceeding capacity, is handled."""
-		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=100,
-			uom="Kg")
-		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=200,
-			uom="Kg")
+		rule_1 = create_putaway_rule(
+			item_code="_Rice", warehouse=self.warehouse_1, capacity=100, uom="Kg"
+		)
+		rule_2 = create_putaway_rule(
+			item_code="_Rice", warehouse=self.warehouse_2, capacity=200, uom="Kg"
+		)
 
-		pr = make_purchase_receipt(item_code="_Rice", qty=350, apply_putaway_rule=1,
-			do_not_submit=1)
+		pr = make_purchase_receipt(item_code="_Rice", qty=350, apply_putaway_rule=1, do_not_submit=1)
 		self.assertEqual(len(pr.items), 2)
 		self.assertEqual(pr.items[0].qty, 200)
 		self.assertEqual(pr.items[0].warehouse, self.warehouse_2)
@@ -118,24 +120,32 @@
 		"""Test rules applied on uom other than stock uom."""
 		item = frappe.get_doc("Item", "_Rice")
 		if not frappe.db.get_value("UOM Conversion Detail", {"parent": "_Rice", "uom": "Bag"}):
-			item.append("uoms", {
-				"uom": "Bag",
-				"conversion_factor": 1000
-			})
+			item.append("uoms", {"uom": "Bag", "conversion_factor": 1000})
 			item.save()
 
-		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=3,
-			uom="Bag")
+		rule_1 = create_putaway_rule(
+			item_code="_Rice", warehouse=self.warehouse_1, capacity=3, uom="Bag"
+		)
 		self.assertEqual(rule_1.stock_capacity, 3000)
-		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=4,
-			uom="Bag")
+		rule_2 = create_putaway_rule(
+			item_code="_Rice", warehouse=self.warehouse_2, capacity=4, uom="Bag"
+		)
 		self.assertEqual(rule_2.stock_capacity, 4000)
 
 		# populate 'Rack 1' with 1 Bag, making the free space 2 Bags
-		stock_receipt = make_stock_entry(item_code="_Rice", target=self.warehouse_1, qty=1000, basic_rate=50)
+		stock_receipt = make_stock_entry(
+			item_code="_Rice", target=self.warehouse_1, qty=1000, basic_rate=50
+		)
 
-		pr = make_purchase_receipt(item_code="_Rice", qty=6, uom="Bag", stock_uom="Kg",
-			conversion_factor=1000, apply_putaway_rule=1, do_not_submit=1)
+		pr = make_purchase_receipt(
+			item_code="_Rice",
+			qty=6,
+			uom="Bag",
+			stock_uom="Kg",
+			conversion_factor=1000,
+			apply_putaway_rule=1,
+			do_not_submit=1,
+		)
 		self.assertEqual(len(pr.items), 2)
 		self.assertEqual(pr.items[0].qty, 4)
 		self.assertEqual(pr.items[0].warehouse, self.warehouse_2)
@@ -151,25 +161,30 @@
 		"""Test if whole UOMs are handled."""
 		item = frappe.get_doc("Item", "_Rice")
 		if not frappe.db.get_value("UOM Conversion Detail", {"parent": "_Rice", "uom": "Bag"}):
-			item.append("uoms", {
-				"uom": "Bag",
-				"conversion_factor": 1000
-			})
+			item.append("uoms", {"uom": "Bag", "conversion_factor": 1000})
 			item.save()
 
 		frappe.db.set_value("UOM", "Bag", "must_be_whole_number", 1)
 
 		# Putaway Rule in different UOM
-		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=1,
-			uom="Bag")
+		rule_1 = create_putaway_rule(
+			item_code="_Rice", warehouse=self.warehouse_1, capacity=1, uom="Bag"
+		)
 		self.assertEqual(rule_1.stock_capacity, 1000)
 		# Putaway Rule in Stock UOM
 		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=500)
 		self.assertEqual(rule_2.stock_capacity, 500)
 		# total capacity is 1500 Kg
 
-		pr = make_purchase_receipt(item_code="_Rice", qty=2, uom="Bag", stock_uom="Kg",
-			conversion_factor=1000, apply_putaway_rule=1, do_not_submit=1)
+		pr = make_purchase_receipt(
+			item_code="_Rice",
+			qty=2,
+			uom="Bag",
+			stock_uom="Kg",
+			conversion_factor=1000,
+			apply_putaway_rule=1,
+			do_not_submit=1,
+		)
 		self.assertEqual(len(pr.items), 1)
 		self.assertEqual(pr.items[0].qty, 1)
 		self.assertEqual(pr.items[0].warehouse, self.warehouse_1)
@@ -184,23 +199,26 @@
 
 	def test_putaway_rules_with_reoccurring_item(self):
 		"""Test rules on same item entered multiple times with different rate."""
-		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
-			uom="Kg")
+		rule_1 = create_putaway_rule(
+			item_code="_Rice", warehouse=self.warehouse_1, capacity=200, uom="Kg"
+		)
 		# total capacity is 200 Kg
 
-		pr = make_purchase_receipt(item_code="_Rice", qty=100, apply_putaway_rule=1,
-			do_not_submit=1)
-		pr.append("items", {
-			"item_code": "_Rice",
-			"warehouse": "_Test Warehouse - _TC",
-			"qty": 200,
-			"uom": "Kg",
-			"stock_uom": "Kg",
-			"stock_qty": 200,
-			"received_qty": 200,
-			"rate": 100,
-			"conversion_factor": 1.0,
-		}) # same item entered again in PR but with different rate
+		pr = make_purchase_receipt(item_code="_Rice", qty=100, apply_putaway_rule=1, do_not_submit=1)
+		pr.append(
+			"items",
+			{
+				"item_code": "_Rice",
+				"warehouse": "_Test Warehouse - _TC",
+				"qty": 200,
+				"uom": "Kg",
+				"stock_uom": "Kg",
+				"stock_qty": 200,
+				"received_qty": 200,
+				"rate": 100,
+				"conversion_factor": 1.0,
+			},
+		)  # same item entered again in PR but with different rate
 		pr.save()
 		self.assertEqual(len(pr.items), 2)
 		self.assertEqual(pr.items[0].qty, 100)
@@ -208,7 +226,7 @@
 		self.assertEqual(pr.items[0].putaway_rule, rule_1.name)
 		# same rule applied to second item row
 		# with previous assignment considered
-		self.assertEqual(pr.items[1].qty, 100) # 100 unassigned in second row from 200
+		self.assertEqual(pr.items[1].qty, 100)  # 100 unassigned in second row from 200
 		self.assertEqual(pr.items[1].warehouse, self.warehouse_1)
 		self.assertEqual(pr.items[1].putaway_rule, rule_1.name)
 
@@ -219,13 +237,13 @@
 
 	def test_validate_over_receipt_in_warehouse(self):
 		"""Test if overreceipt is blocked in the presence of putaway rules."""
-		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
-			uom="Kg")
+		rule_1 = create_putaway_rule(
+			item_code="_Rice", warehouse=self.warehouse_1, capacity=200, uom="Kg"
+		)
 
-		pr = make_purchase_receipt(item_code="_Rice", qty=300, apply_putaway_rule=1,
-			do_not_submit=1)
+		pr = make_purchase_receipt(item_code="_Rice", qty=300, apply_putaway_rule=1, do_not_submit=1)
 		self.assertEqual(len(pr.items), 1)
-		self.assertEqual(pr.items[0].qty, 200) # 100 is unassigned fro 300 Kg
+		self.assertEqual(pr.items[0].qty, 200)  # 100 is unassigned fro 300 Kg
 		self.assertEqual(pr.items[0].warehouse, self.warehouse_1)
 		self.assertEqual(pr.items[0].putaway_rule, rule_1.name)
 
@@ -240,21 +258,29 @@
 
 	def test_putaway_rule_on_stock_entry_material_transfer(self):
 		"""Test if source warehouse is considered while applying rules."""
-		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
-			uom="Kg") # higher priority
-		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=100,
-			uom="Kg", priority=2)
+		rule_1 = create_putaway_rule(
+			item_code="_Rice", warehouse=self.warehouse_1, capacity=200, uom="Kg"
+		)  # higher priority
+		rule_2 = create_putaway_rule(
+			item_code="_Rice", warehouse=self.warehouse_2, capacity=100, uom="Kg", priority=2
+		)
 
-		stock_entry = make_stock_entry(item_code="_Rice", source=self.warehouse_1, qty=200,
-			target="_Test Warehouse - _TC", purpose="Material Transfer",
-			apply_putaway_rule=1, do_not_submit=1)
+		stock_entry = make_stock_entry(
+			item_code="_Rice",
+			source=self.warehouse_1,
+			qty=200,
+			target="_Test Warehouse - _TC",
+			purpose="Material Transfer",
+			apply_putaway_rule=1,
+			do_not_submit=1,
+		)
 
 		stock_entry_item = stock_entry.get("items")[0]
 
 		# since source warehouse is Rack 1, rule 1 (for Rack 1) will be avoided
 		# even though it has more free space and higher priority
 		self.assertEqual(stock_entry_item.t_warehouse, self.warehouse_2)
-		self.assertEqual(stock_entry_item.qty, 100) # unassigned 100 out of 200 Kg
+		self.assertEqual(stock_entry_item.qty, 100)  # unassigned 100 out of 200 Kg
 		self.assertEqual(stock_entry_item.putaway_rule, rule_2.name)
 
 		self.assertUnchangedItemsOnResave(stock_entry)
@@ -265,37 +291,48 @@
 
 	def test_putaway_rule_on_stock_entry_material_transfer_reoccuring_item(self):
 		"""Test if reoccuring item is correctly considered."""
-		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=300,
-			uom="Kg")
-		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=600,
-			uom="Kg", priority=2)
+		rule_1 = create_putaway_rule(
+			item_code="_Rice", warehouse=self.warehouse_1, capacity=300, uom="Kg"
+		)
+		rule_2 = create_putaway_rule(
+			item_code="_Rice", warehouse=self.warehouse_2, capacity=600, uom="Kg", priority=2
+		)
 
 		# create SE with first row having source warehouse as Rack 2
-		stock_entry = make_stock_entry(item_code="_Rice", source=self.warehouse_2, qty=200,
-			target="_Test Warehouse - _TC", purpose="Material Transfer",
-			apply_putaway_rule=1, do_not_submit=1)
+		stock_entry = make_stock_entry(
+			item_code="_Rice",
+			source=self.warehouse_2,
+			qty=200,
+			target="_Test Warehouse - _TC",
+			purpose="Material Transfer",
+			apply_putaway_rule=1,
+			do_not_submit=1,
+		)
 
 		# Add rows with source warehouse as Rack 1
-		stock_entry.extend("items", [
-			{
-				"item_code": "_Rice",
-				"s_warehouse": self.warehouse_1,
-				"t_warehouse": "_Test Warehouse - _TC",
-				"qty": 100,
-				"basic_rate": 50,
-				"conversion_factor": 1.0,
-				"transfer_qty": 100
-			},
-			{
-				"item_code": "_Rice",
-				"s_warehouse": self.warehouse_1,
-				"t_warehouse": "_Test Warehouse - _TC",
-				"qty": 200,
-				"basic_rate": 60,
-				"conversion_factor": 1.0,
-				"transfer_qty": 200
-			}
-		])
+		stock_entry.extend(
+			"items",
+			[
+				{
+					"item_code": "_Rice",
+					"s_warehouse": self.warehouse_1,
+					"t_warehouse": "_Test Warehouse - _TC",
+					"qty": 100,
+					"basic_rate": 50,
+					"conversion_factor": 1.0,
+					"transfer_qty": 100,
+				},
+				{
+					"item_code": "_Rice",
+					"s_warehouse": self.warehouse_1,
+					"t_warehouse": "_Test Warehouse - _TC",
+					"qty": 200,
+					"basic_rate": 60,
+					"conversion_factor": 1.0,
+					"transfer_qty": 200,
+				},
+			],
+		)
 
 		stock_entry.save()
 
@@ -323,19 +360,24 @@
 	def test_putaway_rule_on_stock_entry_material_transfer_batch_serial_item(self):
 		"""Test if batch and serial items are split correctly."""
 		if not frappe.db.exists("Item", "Water Bottle"):
-			make_item("Water Bottle", {
-				"is_stock_item": 1,
-				"has_batch_no" : 1,
-				"create_new_batch": 1,
-				"has_serial_no": 1,
-				"serial_no_series": "BOTTL-.####",
-				"stock_uom": "Nos"
-			})
+			make_item(
+				"Water Bottle",
+				{
+					"is_stock_item": 1,
+					"has_batch_no": 1,
+					"create_new_batch": 1,
+					"has_serial_no": 1,
+					"serial_no_series": "BOTTL-.####",
+					"stock_uom": "Nos",
+				},
+			)
 
-		rule_1 = create_putaway_rule(item_code="Water Bottle", warehouse=self.warehouse_1, capacity=3,
-			uom="Nos")
-		rule_2 = create_putaway_rule(item_code="Water Bottle", warehouse=self.warehouse_2, capacity=2,
-		uom="Nos")
+		rule_1 = create_putaway_rule(
+			item_code="Water Bottle", warehouse=self.warehouse_1, capacity=3, uom="Nos"
+		)
+		rule_2 = create_putaway_rule(
+			item_code="Water Bottle", warehouse=self.warehouse_2, capacity=2, uom="Nos"
+		)
 
 		make_new_batch(batch_id="BOTTL-BATCH-1", item_code="Water Bottle")
 
@@ -344,12 +386,20 @@
 		pr.save()
 		pr.submit()
 
-		serial_nos = frappe.get_list("Serial No", filters={"purchase_document_no": pr.name, "status": "Active"})
+		serial_nos = frappe.get_list(
+			"Serial No", filters={"purchase_document_no": pr.name, "status": "Active"}
+		)
 		serial_nos = [d.name for d in serial_nos]
 
-		stock_entry = make_stock_entry(item_code="Water Bottle", source="_Test Warehouse - _TC", qty=5,
-			target="Finished Goods - _TC", purpose="Material Transfer",
-			apply_putaway_rule=1, do_not_save=1)
+		stock_entry = make_stock_entry(
+			item_code="Water Bottle",
+			source="_Test Warehouse - _TC",
+			qty=5,
+			target="Finished Goods - _TC",
+			purpose="Material Transfer",
+			apply_putaway_rule=1,
+			do_not_save=1,
+		)
 		stock_entry.items[0].batch_no = "BOTTL-BATCH-1"
 		stock_entry.items[0].serial_no = "\n".join(serial_nos)
 		stock_entry.save()
@@ -375,14 +425,21 @@
 
 	def test_putaway_rule_on_stock_entry_material_receipt(self):
 		"""Test if rules are applied in Stock Entry of type Receipt."""
-		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
-			uom="Kg") # more capacity
-		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=100,
-			uom="Kg")
+		rule_1 = create_putaway_rule(
+			item_code="_Rice", warehouse=self.warehouse_1, capacity=200, uom="Kg"
+		)  # more capacity
+		rule_2 = create_putaway_rule(
+			item_code="_Rice", warehouse=self.warehouse_2, capacity=100, uom="Kg"
+		)
 
-		stock_entry = make_stock_entry(item_code="_Rice", qty=100,
-			target="_Test Warehouse - _TC", purpose="Material Receipt",
-			apply_putaway_rule=1, do_not_submit=1)
+		stock_entry = make_stock_entry(
+			item_code="_Rice",
+			qty=100,
+			target="_Test Warehouse - _TC",
+			purpose="Material Receipt",
+			apply_putaway_rule=1,
+			do_not_submit=1,
+		)
 
 		stock_entry_item = stock_entry.get("items")[0]
 
@@ -396,6 +453,21 @@
 		rule_1.delete()
 		rule_2.delete()
 
+	def test_warehouse_capacity_dashbord(self):
+		from erpnext.stock.dashboard.warehouse_capacity_dashboard import get_data
+
+		item = "_Rice"
+		rule = create_putaway_rule(item_code=item, warehouse=self.warehouse_1, capacity=500, uom="Kg")
+
+		capacities = get_data(warehouse=self.warehouse_1)
+		for capacity in capacities:
+			if capacity.item_code == item and capacity.warehouse == self.warehouse_1:
+				self.assertEqual(capacity.stock_capacity, 500)
+
+		get_data(warehouse=self.warehouse_1)
+		rule.delete()
+
+
 def create_putaway_rule(**args):
 	args = frappe._dict(args)
 	putaway = frappe.new_doc("Putaway Rule")
@@ -408,7 +480,9 @@
 	putaway.capacity = args.capacity or 1
 	putaway.stock_uom = frappe.db.get_value("Item", putaway.item_code, "stock_uom")
 	putaway.uom = args.uom or putaway.stock_uom
-	putaway.conversion_factor = get_conversion_factor(putaway.item_code, putaway.uom)['conversion_factor']
+	putaway.conversion_factor = get_conversion_factor(putaway.item_code, putaway.uom)[
+		"conversion_factor"
+	]
 
 	if not args.do_not_save:
 		putaway.save()
diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py
index 4e3b80a..13abfad 100644
--- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py
@@ -18,8 +18,8 @@
 		if not self.readings and self.item_code:
 			self.get_item_specification_details()
 
-		if self.inspection_type=="In Process" and self.reference_type=="Job Card":
-			item_qi_template = frappe.db.get_value("Item", self.item_code, 'quality_inspection_template')
+		if self.inspection_type == "In Process" and self.reference_type == "Job Card":
+			item_qi_template = frappe.db.get_value("Item", self.item_code, "quality_inspection_template")
 			parameters = get_template_details(item_qi_template)
 			for reading in self.readings:
 				for d in parameters:
@@ -33,26 +33,28 @@
 	@frappe.whitelist()
 	def get_item_specification_details(self):
 		if not self.quality_inspection_template:
-			self.quality_inspection_template = frappe.db.get_value('Item',
-				self.item_code, 'quality_inspection_template')
+			self.quality_inspection_template = frappe.db.get_value(
+				"Item", self.item_code, "quality_inspection_template"
+			)
 
-		if not self.quality_inspection_template: return
+		if not self.quality_inspection_template:
+			return
 
-		self.set('readings', [])
+		self.set("readings", [])
 		parameters = get_template_details(self.quality_inspection_template)
 		for d in parameters:
-			child = self.append('readings', {})
+			child = self.append("readings", {})
 			child.update(d)
 			child.status = "Accepted"
 
 	@frappe.whitelist()
 	def get_quality_inspection_template(self):
-		template = ''
+		template = ""
 		if self.bom_no:
-			template = frappe.db.get_value('BOM', self.bom_no, 'quality_inspection_template')
+			template = frappe.db.get_value("BOM", self.bom_no, "quality_inspection_template")
 
 		if not template:
-			template = frappe.db.get_value('BOM', self.item_code, 'quality_inspection_template')
+			template = frappe.db.get_value("BOM", self.item_code, "quality_inspection_template")
 
 		self.quality_inspection_template = template
 		self.get_item_specification_details()
@@ -66,21 +68,25 @@
 	def update_qc_reference(self):
 		quality_inspection = self.name if self.docstatus == 1 else ""
 
-		if self.reference_type == 'Job Card':
+		if self.reference_type == "Job Card":
 			if self.reference_name:
-				frappe.db.sql("""
+				frappe.db.sql(
+					"""
 					UPDATE `tab{doctype}`
 					SET quality_inspection = %s, modified = %s
 					WHERE name = %s and production_item = %s
-				""".format(doctype=self.reference_type),
-					(quality_inspection, self.modified, self.reference_name, self.item_code))
+				""".format(
+						doctype=self.reference_type
+					),
+					(quality_inspection, self.modified, self.reference_name, self.item_code),
+				)
 
 		else:
 			args = [quality_inspection, self.modified, self.reference_name, self.item_code]
-			doctype = self.reference_type + ' Item'
+			doctype = self.reference_type + " Item"
 
-			if self.reference_type == 'Stock Entry':
-				doctype = 'Stock Entry Detail'
+			if self.reference_type == "Stock Entry":
+				doctype = "Stock Entry Detail"
 
 			if self.reference_type and self.reference_name:
 				conditions = ""
@@ -88,11 +94,12 @@
 					conditions += " and t1.batch_no = %s"
 					args.append(self.batch_no)
 
-				if self.docstatus == 2: # if cancel, then remove qi link wherever same name
+				if self.docstatus == 2:  # if cancel, then remove qi link wherever same name
 					conditions += " and t1.quality_inspection = %s"
 					args.append(self.name)
 
-				frappe.db.sql("""
+				frappe.db.sql(
+					"""
 					UPDATE
 						`tab{child_doc}` t1, `tab{parent_doc}` t2
 					SET
@@ -102,12 +109,15 @@
 						and t1.item_code = %s
 						and t1.parent = t2.name
 						{conditions}
-				""".format(parent_doc=self.reference_type, child_doc=doctype, conditions=conditions),
-					args)
+				""".format(
+						parent_doc=self.reference_type, child_doc=doctype, conditions=conditions
+					),
+					args,
+				)
 
 	def inspect_and_set_status(self):
 		for reading in self.readings:
-			if not reading.manual_inspection: # dont auto set status if manual
+			if not reading.manual_inspection:  # dont auto set status if manual
 				if reading.formula_based_criteria:
 					self.set_status_based_on_acceptance_formula(reading)
 				else:
@@ -129,13 +139,16 @@
 			reading_value = reading.get("reading_" + str(i))
 			if reading_value is not None and reading_value.strip():
 				result = flt(reading.get("min_value")) <= flt(reading_value) <= flt(reading.get("max_value"))
-				if not result: return False
+				if not result:
+					return False
 		return True
 
 	def set_status_based_on_acceptance_formula(self, reading):
 		if not reading.acceptance_formula:
-			frappe.throw(_("Row #{0}: Acceptance Criteria Formula is required.").format(reading.idx),
-				title=_("Missing Formula"))
+			frappe.throw(
+				_("Row #{0}: Acceptance Criteria Formula is required.").format(reading.idx),
+				title=_("Missing Formula"),
+			)
 
 		condition = reading.acceptance_formula
 		data = self.get_formula_evaluation_data(reading)
@@ -145,12 +158,17 @@
 			reading.status = "Accepted" if result else "Rejected"
 		except NameError as e:
 			field = frappe.bold(e.args[0].split()[1])
-			frappe.throw(_("Row #{0}: {1} is not a valid reading field. Please refer to the field description.")
-				.format(reading.idx, field),
-				title=_("Invalid Formula"))
+			frappe.throw(
+				_("Row #{0}: {1} is not a valid reading field. Please refer to the field description.").format(
+					reading.idx, field
+				),
+				title=_("Invalid Formula"),
+			)
 		except Exception:
-			frappe.throw(_("Row #{0}: Acceptance Criteria Formula is incorrect.").format(reading.idx),
-				title=_("Invalid Formula"))
+			frappe.throw(
+				_("Row #{0}: Acceptance Criteria Formula is incorrect.").format(reading.idx),
+				title=_("Invalid Formula"),
+			)
 
 	def get_formula_evaluation_data(self, reading):
 		data = {}
@@ -168,6 +186,7 @@
 	def calculate_mean(self, reading):
 		"""Calculate mean of all non-empty readings."""
 		from statistics import mean
+
 		readings_list = []
 
 		for i in range(1, 11):
@@ -178,65 +197,90 @@
 		actual_mean = mean(readings_list) if readings_list else 0
 		return actual_mean
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def item_query(doctype, txt, searchfield, start, page_len, filters):
 	if filters.get("from"):
 		from frappe.desk.reportview import get_match_cond
+
 		mcond = get_match_cond(filters["from"])
 		cond, qi_condition = "", "and (quality_inspection is null or quality_inspection = '')"
 
 		if filters.get("parent"):
-			if filters.get('from') in ['Purchase Invoice Item', 'Purchase Receipt Item']\
-					and filters.get("inspection_type") != "In Process":
+			if (
+				filters.get("from") in ["Purchase Invoice Item", "Purchase Receipt Item"]
+				and filters.get("inspection_type") != "In Process"
+			):
 				cond = """and item_code in (select name from `tabItem` where
 					inspection_required_before_purchase = 1)"""
-			elif filters.get('from') in ['Sales Invoice Item', 'Delivery Note Item']\
-					and filters.get("inspection_type") != "In Process":
+			elif (
+				filters.get("from") in ["Sales Invoice Item", "Delivery Note Item"]
+				and filters.get("inspection_type") != "In Process"
+			):
 				cond = """and item_code in (select name from `tabItem` where
 					inspection_required_before_delivery = 1)"""
-			elif filters.get('from') == 'Stock Entry Detail':
+			elif filters.get("from") == "Stock Entry Detail":
 				cond = """and s_warehouse is null"""
 
-			if filters.get('from') in ['Supplier Quotation Item']:
+			if filters.get("from") in ["Supplier Quotation Item"]:
 				qi_condition = ""
 
-			return frappe.db.sql("""
+			return frappe.db.sql(
+				"""
 					SELECT item_code
 					FROM `tab{doc}`
 					WHERE parent=%(parent)s and docstatus < 2 and item_code like %(txt)s
 					{qi_condition} {cond} {mcond}
-					ORDER BY item_code limit {start}, {page_len}
-				""".format(doc=filters.get('from'),
-					cond = cond, mcond = mcond, start = start,
-					page_len = page_len, qi_condition = qi_condition),
-					{'parent': filters.get('parent'), 'txt': "%%%s%%" % txt})
+					ORDER BY item_code limit {page_len} offset {start}
+				""".format(
+					doc=filters.get("from"),
+					cond=cond,
+					mcond=mcond,
+					start=start,
+					page_len=page_len,
+					qi_condition=qi_condition,
+				),
+				{"parent": filters.get("parent"), "txt": "%%%s%%" % txt},
+			)
 
 		elif filters.get("reference_name"):
-			return frappe.db.sql("""
+			return frappe.db.sql(
+				"""
 					SELECT production_item
 					FROM `tab{doc}`
 					WHERE name = %(reference_name)s and docstatus < 2 and production_item like %(txt)s
 					{qi_condition} {cond} {mcond}
 					ORDER BY production_item
-					LIMIT {start}, {page_len}
-				""".format(doc=filters.get("from"),
-					cond = cond, mcond = mcond, start = start,
-					page_len = page_len, qi_condition = qi_condition),
-					{'reference_name': filters.get('reference_name'), 'txt': "%%%s%%" % txt})
+					limit {page_len} offset {start}
+				""".format(
+					doc=filters.get("from"),
+					cond=cond,
+					mcond=mcond,
+					start=start,
+					page_len=page_len,
+					qi_condition=qi_condition,
+				),
+				{"reference_name": filters.get("reference_name"), "txt": "%%%s%%" % txt},
+			)
+
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def quality_inspection_query(doctype, txt, searchfield, start, page_len, filters):
-	return frappe.get_all('Quality Inspection',
+	return frappe.get_all(
+		"Quality Inspection",
 		limit_start=start,
 		limit_page_length=page_len,
-		filters = {
-			'docstatus': 1,
-			'name': ('like', '%%%s%%' % txt),
-			'item_code': filters.get("item_code"),
-			'reference_name': ('in', [filters.get("reference_name", ''), ''])
-		}, as_list=1)
+		filters={
+			"docstatus": 1,
+			"name": ("like", "%%%s%%" % txt),
+			"item_code": filters.get("item_code"),
+			"reference_name": ("in", [filters.get("reference_name", ""), ""]),
+		},
+		as_list=1,
+	)
+
 
 @frappe.whitelist()
 def make_quality_inspection(source_name, target_doc=None):
@@ -244,19 +288,18 @@
 		doc.inspected_by = frappe.session.user
 		doc.get_quality_inspection_template()
 
-	doc = get_mapped_doc("BOM", source_name, {
-		'BOM': {
-			"doctype": "Quality Inspection",
-			"validation": {
-				"docstatus": ["=", 1]
-			},
-			"field_map": {
-				"name": "bom_no",
-				"item": "item_code",
-				"stock_uom": "uom",
-				"stock_qty": "qty"
-			},
-		}
-	}, target_doc, postprocess)
+	doc = get_mapped_doc(
+		"BOM",
+		source_name,
+		{
+			"BOM": {
+				"doctype": "Quality Inspection",
+				"validation": {"docstatus": ["=", 1]},
+				"field_map": {"name": "bom_no", "item": "item_code", "stock_uom": "uom", "stock_qty": "qty"},
+			}
+		},
+		target_doc,
+		postprocess,
+	)
 
 	return doc
diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
index 601ca05..144f138 100644
--- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
@@ -22,16 +22,11 @@
 	def setUp(self):
 		super().setUp()
 		create_item("_Test Item with QA")
-		frappe.db.set_value(
-			"Item", "_Test Item with QA", "inspection_required_before_delivery", 1
-		)
+		frappe.db.set_value("Item", "_Test Item with QA", "inspection_required_before_delivery", 1)
 
 	def test_qa_for_delivery(self):
 		make_stock_entry(
-			item_code="_Test Item with QA",
-			target="_Test Warehouse - _TC",
-			qty=1,
-			basic_rate=100
+			item_code="_Test Item with QA", target="_Test Warehouse - _TC", qty=1, basic_rate=100
 		)
 		dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
 
@@ -71,21 +66,18 @@
 				"specification": "Iron Content",  # numeric reading
 				"min_value": 0.1,
 				"max_value": 0.9,
-				"reading_1": "0.4"
+				"reading_1": "0.4",
 			},
 			{
 				"specification": "Particle Inspection Needed",  # non-numeric reading
 				"numeric": 0,
 				"value": "Yes",
-				"reading_value": "Yes"
-			}
+				"reading_value": "Yes",
+			},
 		]
 
 		qa = create_quality_inspection(
-			reference_type="Delivery Note",
-			reference_name=dn.name,
-			readings=readings,
-			do_not_save=True
+			reference_type="Delivery Note", reference_name=dn.name, readings=readings, do_not_save=True
 		)
 
 		qa.save()
@@ -104,13 +96,13 @@
 				"specification": "Iron Content",  # numeric reading
 				"formula_based_criteria": 1,
 				"acceptance_formula": "reading_1 > 0.35 and reading_1 < 0.50",
-				"reading_1": "0.4"
+				"reading_1": "0.4",
 			},
 			{
 				"specification": "Calcium Content",  # numeric reading
 				"formula_based_criteria": 1,
 				"acceptance_formula": "reading_1 > 0.20 and reading_1 < 0.50",
-				"reading_1": "0.7"
+				"reading_1": "0.7",
 			},
 			{
 				"specification": "Mg Content",  # numeric reading
@@ -118,22 +110,19 @@
 				"acceptance_formula": "mean < 0.9",
 				"reading_1": "0.5",
 				"reading_2": "0.7",
-				"reading_3": "random text"  # check if random string input causes issues
+				"reading_3": "random text",  # check if random string input causes issues
 			},
 			{
 				"specification": "Calcium Content",  # non-numeric reading
 				"formula_based_criteria": 1,
 				"numeric": 0,
 				"acceptance_formula": "reading_value in ('Grade A', 'Grade B', 'Grade C')",
-				"reading_value": "Grade B"
-			}
+				"reading_value": "Grade B",
+			},
 		]
 
 		qa = create_quality_inspection(
-			reference_type="Delivery Note",
-			reference_name=dn.name,
-			readings=readings,
-			do_not_save=True
+			reference_type="Delivery Note", reference_name=dn.name, readings=readings, do_not_save=True
 		)
 
 		qa.save()
@@ -167,32 +156,26 @@
 			qty=1,
 			basic_rate=100,
 			inspection_required=True,
-			do_not_submit=True
+			do_not_submit=True,
 		)
 
 		readings = [
-			{
-				"specification": "Iron Content",
-				"min_value": 0.1,
-				"max_value": 0.9,
-				"reading_1": "0.4"
-			}
+			{"specification": "Iron Content", "min_value": 0.1, "max_value": 0.9, "reading_1": "0.4"}
 		]
 
 		qa = create_quality_inspection(
-			reference_type="Stock Entry",
-			reference_name=se.name,
-			readings=readings,
-			status="Rejected"
+			reference_type="Stock Entry", reference_name=se.name, readings=readings, status="Rejected"
 		)
 
 		frappe.db.set_value("Stock Settings", None, "action_if_quality_inspection_is_rejected", "Stop")
 		se.reload()
-		self.assertRaises(QualityInspectionRejectedError, se.submit) # when blocked in Stock settings, block rejected QI
+		self.assertRaises(
+			QualityInspectionRejectedError, se.submit
+		)  # when blocked in Stock settings, block rejected QI
 
 		frappe.db.set_value("Stock Settings", None, "action_if_quality_inspection_is_rejected", "Warn")
 		se.reload()
-		se.submit() # when allowed in Stock settings, allow rejected QI
+		se.submit()  # when allowed in Stock settings, allow rejected QI
 
 		# teardown
 		qa.reload()
@@ -201,6 +184,7 @@
 		se.cancel()
 		frappe.db.set_value("Stock Settings", None, "action_if_quality_inspection_is_rejected", "Stop")
 
+
 def create_quality_inspection(**args):
 	args = frappe._dict(args)
 	qa = frappe.new_doc("Quality Inspection")
@@ -238,8 +222,6 @@
 
 def create_quality_inspection_parameter(parameter):
 	if not frappe.db.exists("Quality Inspection Parameter", parameter):
-		frappe.get_doc({
-			"doctype": "Quality Inspection Parameter",
-			"parameter": parameter,
-			"description": parameter
-		}).insert()
+		frappe.get_doc(
+			{"doctype": "Quality Inspection Parameter", "parameter": parameter, "description": parameter}
+		).insert()
diff --git a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py
index 7f8c871..9b8f5d6 100644
--- a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py
+++ b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py
@@ -9,11 +9,22 @@
 class QualityInspectionTemplate(Document):
 	pass
 
-def get_template_details(template):
-	if not template: return []
 
-	return frappe.get_all('Item Quality Inspection Parameter',
-		fields=["specification", "value", "acceptance_formula",
-			"numeric", "formula_based_criteria", "min_value", "max_value"],
-		filters={'parenttype': 'Quality Inspection Template', 'parent': template},
-		order_by="idx")
+def get_template_details(template):
+	if not template:
+		return []
+
+	return frappe.get_all(
+		"Item Quality Inspection Parameter",
+		fields=[
+			"specification",
+			"value",
+			"acceptance_formula",
+			"numeric",
+			"formula_based_criteria",
+			"min_value",
+			"max_value",
+		],
+		filters={"parenttype": "Quality Inspection Template", "parent": template},
+		order_by="idx",
+	)
diff --git a/erpnext/stock/doctype/quality_inspection_template/test_records.json b/erpnext/stock/doctype/quality_inspection_template/test_records.json
new file mode 100644
index 0000000..980f49a
--- /dev/null
+++ b/erpnext/stock/doctype/quality_inspection_template/test_records.json
@@ -0,0 +1,13 @@
+[
+ {
+     "quality_inspection_template_name" : "_Test Quality Inspection Template",
+     "doctype": "Quality Inspection Template",
+     "item_quality_inspection_parameter" : [
+           {
+            "specification": "_Test Param",
+            "doctype": "Item Quality Inspection Parameter",
+            "parentfield": "item_quality_inspection_parameter"
+           }
+     ]
+ }
+]
diff --git a/erpnext/stock/doctype/quick_stock_balance/quick_stock_balance.py b/erpnext/stock/doctype/quick_stock_balance/quick_stock_balance.py
index 7a0f5d0..846be0b 100644
--- a/erpnext/stock/doctype/quick_stock_balance/quick_stock_balance.py
+++ b/erpnext/stock/doctype/quick_stock_balance/quick_stock_balance.py
@@ -12,24 +12,25 @@
 class QuickStockBalance(Document):
 	pass
 
+
 @frappe.whitelist()
 def get_stock_item_details(warehouse, date, item=None, barcode=None):
 	out = {}
 	if barcode:
 		out["item"] = frappe.db.get_value(
-			"Item Barcode", filters={"barcode": barcode}, fieldname=["parent"])
+			"Item Barcode", filters={"barcode": barcode}, fieldname=["parent"]
+		)
 		if not out["item"]:
-			frappe.throw(
-				_("Invalid Barcode. There is no Item attached to this barcode."))
+			frappe.throw(_("Invalid Barcode. There is no Item attached to this barcode."))
 	else:
 		out["item"] = item
 
-	barcodes = frappe.db.get_values("Item Barcode", filters={"parent": out["item"]},
-		fieldname=["barcode"])
+	barcodes = frappe.db.get_values(
+		"Item Barcode", filters={"parent": out["item"]}, fieldname=["barcode"]
+	)
 
 	out["barcodes"] = [x[0] for x in barcodes]
 	out["qty"] = get_stock_balance(out["item"], warehouse, date)
 	out["value"] = get_stock_value_on(warehouse, date, out["item"])
-	out["image"] = frappe.db.get_value("Item",
-		filters={"name": out["item"]}, fieldname=["image"])
+	out["image"] = frappe.db.get_value("Item", filters={"name": out["item"]}, fieldname=["image"])
 	return out
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json
index 0ba97d5..e093933 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json
@@ -1,6 +1,6 @@
 {
  "actions": [],
- "autoname": "REPOST-ITEM-VAL-.######",
+ "autoname": "hash",
  "creation": "2022-01-11 15:03:38.273179",
  "doctype": "DocType",
  "editable_grid": 1,
@@ -23,8 +23,10 @@
   "error_section",
   "error_log",
   "items_to_be_repost",
+  "affected_transactions",
   "distinct_item_and_warehouse",
-  "current_index"
+  "current_index",
+  "gl_reposting_index"
  ],
  "fields": [
   {
@@ -172,16 +174,32 @@
    "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "fieldname": "affected_transactions",
+   "fieldtype": "Code",
+   "hidden": 1,
+   "label": "Affected Transactions",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "gl_reposting_index",
+   "fieldtype": "Int",
+   "hidden": 1,
+   "label": "GL reposting index",
+   "read_only": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-01-18 10:57:33.450907",
+ "modified": "2022-06-13 12:20:22.182322",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Repost Item Valuation",
- "naming_rule": "Expression (old style)",
+ "naming_rule": "Random",
  "owner": "Administrator",
  "permissions": [
   {
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
index 977d470..b1017d2 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -3,17 +3,21 @@
 
 import frappe
 from frappe import _
+from frappe.exceptions import QueryDeadlockError, QueryTimeoutError
 from frappe.model.document import Document
-from frappe.utils import cint, get_link_to_form, get_weekday, now, nowtime, today
+from frappe.utils import cint, get_link_to_form, get_weekday, now, nowtime
 from frappe.utils.user import get_users_with_role
 from rq.timeouts import JobTimeoutException
 
 import erpnext
-from erpnext.accounts.utils import (
-	check_if_stock_and_account_balance_synced,
-	update_gl_entries_after,
+from erpnext.accounts.utils import get_future_stock_vouchers, repost_gle_for_stock_vouchers
+from erpnext.stock.stock_ledger import (
+	get_affected_transactions,
+	get_items_to_be_repost,
+	repost_future_sle,
 )
-from erpnext.stock.stock_ledger import get_items_to_be_repost, repost_future_sle
+
+RecoverableErrors = (JobTimeoutException, QueryDeadlockError, QueryTimeoutError)
 
 
 class RepostItemValuation(Document):
@@ -23,7 +27,7 @@
 		self.set_company()
 
 	def reset_field_values(self):
-		if self.based_on == 'Transaction':
+		if self.based_on == "Transaction":
 			self.item_code = None
 			self.warehouse = None
 
@@ -38,29 +42,56 @@
 	def set_status(self, status=None, write=True):
 		status = status or self.status
 		if not status:
-			self.status = 'Queued'
+			self.status = "Queued"
 		else:
 			self.status = status
 		if write:
-			self.db_set('status', self.status)
+			self.db_set("status", self.status)
 
 	def on_submit(self):
-		if not frappe.flags.in_test or self.flags.dont_run_in_test or frappe.flags.dont_execute_stock_reposts:
+		"""During tests reposts are executed immediately.
+
+		Exceptions:
+		        1. "Repost Item Valuation" document has self.flags.dont_run_in_test
+		        2. global flag frappe.flags.dont_execute_stock_reposts is set
+
+		        These flags are useful for asserting real time behaviour like quantity updates.
+		"""
+
+		if not frappe.flags.in_test:
+			return
+		if self.flags.dont_run_in_test or frappe.flags.dont_execute_stock_reposts:
 			return
 
-		frappe.enqueue(repost, timeout=1800, queue='long',
-			job_name='repost_sle', now=frappe.flags.in_test, doc=self)
+		repost(self)
+
+	def before_cancel(self):
+		self.check_pending_repost_against_cancelled_transaction()
+
+	def check_pending_repost_against_cancelled_transaction(self):
+		if self.status not in ("Queued", "In Progress"):
+			return
+
+		if not (self.voucher_no and self.voucher_no):
+			return
+
+		transaction_status = frappe.db.get_value(self.voucher_type, self.voucher_no, "docstatus")
+		if transaction_status == 2:
+			msg = _("Cannot cancel as processing of cancelled documents is  pending.")
+			msg += "<br>" + _("Please try again in an hour.")
+			frappe.throw(msg, title=_("Pending processing"))
 
 	@frappe.whitelist()
 	def restart_reposting(self):
-		self.set_status('Queued', write=False)
+		self.set_status("Queued", write=False)
 		self.current_index = 0
 		self.distinct_item_and_warehouse = None
 		self.items_to_be_repost = None
+		self.gl_reposting_index = 0
 		self.db_update()
 
 	def deduplicate_similar_repost(self):
-		""" Deduplicate similar reposts based on item-warehouse-posting combination."""
+		"""Deduplicate similar reposts based on item-warehouse-posting combination."""
 		if self.based_on != "Item and Warehouse":
 			return
 
@@ -72,7 +103,8 @@
 			"posting_time": self.posting_time,
 		}
 
-		frappe.db.sql("""
+		frappe.db.sql(
+			"""
 			update `tabRepost Item Valuation`
 			set status = 'Skipped'
 			WHERE item_code = %(item_code)s
@@ -83,9 +115,10 @@
 				and status = 'Queued'
 				and based_on = 'Item and Warehouse'
 				""",
-			filters
+			filters,
 		)
 
+
 def on_doctype_update():
 	frappe.db.add_index("Repost Item Valuation", ["warehouse", "item_code"], "item_warehouse")
 
@@ -95,63 +128,106 @@
 		if not frappe.db.exists("Repost Item Valuation", doc.name):
 			return
 
-		doc.set_status('In Progress')
+		doc.set_status("In Progress")
 		if not frappe.flags.in_test:
 			frappe.db.commit()
 
 		repost_sl_entries(doc)
 		repost_gl_entries(doc)
 
-		doc.set_status('Completed')
+		doc.set_status("Completed")
 
-	except (Exception, JobTimeoutException):
+	except Exception as e:
 		frappe.db.rollback()
 		traceback = frappe.get_traceback()
-		frappe.log_error(traceback)
+		doc.log_error("Unable to repost item valuation")
 
-		message = frappe.message_log.pop()
+		message = frappe.message_log.pop() if frappe.message_log else ""
 		if traceback:
 			message += "<br>" + "Traceback: <br>" + traceback
-		frappe.db.set_value(doc.doctype, doc.name, 'error_log', message)
+		frappe.db.set_value(doc.doctype, doc.name, "error_log", message)
 
-		notify_error_to_stock_managers(doc, message)
-		doc.set_status('Failed')
-		raise
+		if not isinstance(e, RecoverableErrors):
+			notify_error_to_stock_managers(doc, message)
+			doc.set_status("Failed")
 	finally:
-		frappe.db.commit()
+		if not frappe.flags.in_test:
+			frappe.db.commit()
+
 
 def repost_sl_entries(doc):
-	if doc.based_on == 'Transaction':
-		repost_future_sle(voucher_type=doc.voucher_type, voucher_no=doc.voucher_no,
-			allow_negative_stock=doc.allow_negative_stock, via_landed_cost_voucher=doc.via_landed_cost_voucher, doc=doc)
+	if doc.based_on == "Transaction":
+		repost_future_sle(
+			voucher_type=doc.voucher_type,
+			voucher_no=doc.voucher_no,
+			allow_negative_stock=doc.allow_negative_stock,
+			via_landed_cost_voucher=doc.via_landed_cost_voucher,
+			doc=doc,
+		)
 	else:
-		repost_future_sle(args=[frappe._dict({
-			"item_code": doc.item_code,
-			"warehouse": doc.warehouse,
-			"posting_date": doc.posting_date,
-			"posting_time": doc.posting_time
-		})], allow_negative_stock=doc.allow_negative_stock, via_landed_cost_voucher=doc.via_landed_cost_voucher)
+		repost_future_sle(
+			args=[
+				frappe._dict(
+					{
+						"item_code": doc.item_code,
+						"warehouse": doc.warehouse,
+						"posting_date": doc.posting_date,
+						"posting_time": doc.posting_time,
+					}
+				)
+			],
+			allow_negative_stock=doc.allow_negative_stock,
+			via_landed_cost_voucher=doc.via_landed_cost_voucher,
+			doc=doc,
+		)
+
 
 def repost_gl_entries(doc):
 	if not cint(erpnext.is_perpetual_inventory_enabled(doc.company)):
 		return
 
-	if doc.based_on == 'Transaction':
+	# directly modified transactions
+	directly_dependent_transactions = _get_directly_dependent_vouchers(doc)
+	repost_affected_transaction = get_affected_transactions(doc)
+	repost_gle_for_stock_vouchers(
+		directly_dependent_transactions + list(repost_affected_transaction),
+		doc.posting_date,
+		doc.company,
+		repost_doc=doc,
+	)
+
+
+def _get_directly_dependent_vouchers(doc):
+	"""Get stock vouchers that are directly affected by reposting
+	i.e. any one item-warehouse is present in the stock transaction"""
+
+	items = set()
+	warehouses = set()
+
+	if doc.based_on == "Transaction":
 		ref_doc = frappe.get_doc(doc.voucher_type, doc.voucher_no)
 		doc_items, doc_warehouses = ref_doc.get_items_and_warehouses()
+		items.update(doc_items)
+		warehouses.update(doc_warehouses)
 
 		sles = get_items_to_be_repost(doc.voucher_type, doc.voucher_no)
-		sle_items = [sle.item_code for sle in sles]
-		sle_warehouse = [sle.warehouse for sle in sles]
-
-		items = list(set(doc_items).union(set(sle_items)))
-		warehouses = list(set(doc_warehouses).union(set(sle_warehouse)))
+		sle_items = {sle.item_code for sle in sles}
+		sle_warehouses = {sle.warehouse for sle in sles}
+		items.update(sle_items)
+		warehouses.update(sle_warehouses)
 	else:
-		items = [doc.item_code]
-		warehouses = [doc.warehouse]
+		items.add(doc.item_code)
+		warehouses.add(doc.warehouse)
 
-	update_gl_entries_after(doc.posting_date, doc.posting_time,
-		for_warehouses=warehouses, for_items=items, company=doc.company)
+	affected_vouchers = get_future_stock_vouchers(
+		posting_date=doc.posting_date,
+		posting_time=doc.posting_time,
+		for_warehouses=list(warehouses),
+		for_items=list(items),
+		company=doc.company,
+	)
+	return affected_vouchers
+
 
 def notify_error_to_stock_managers(doc, traceback):
 	recipients = get_users_with_role("Stock Manager")
@@ -159,22 +235,33 @@
 		get_users_with_role("System Manager")
 
 	subject = _("Error while reposting item valuation")
-	message = (_("Hi,") + "<br>"
-		+ _("An error has been appeared while reposting item valuation via {0}")
-			.format(get_link_to_form(doc.doctype, doc.name)) + "<br>"
-		+ _("Please check the error message and take necessary actions to fix the error and then restart the reposting again.")
+	message = (
+		_("Hi,")
+		+ "<br>"
+		+ _("An error has been appeared while reposting item valuation via {0}").format(
+			get_link_to_form(doc.doctype, doc.name)
+		)
+		+ "<br>"
+		+ _(
+			"Please check the error message and take necessary actions to fix the error and then restart the reposting again."
+		)
 	)
 	frappe.sendmail(recipients=recipients, subject=subject, message=message)
 
+
 def repost_entries():
+	"""
+	Reposts 'Repost Item Valuation' entries in queue.
+	Called hourly via hooks.py.
+	"""
 	if not in_configured_timeslot():
 		return
 
 	riv_entries = get_repost_item_valuation_entries()
 
 	for row in riv_entries:
-		doc = frappe.get_doc('Repost Item Valuation', row.name)
-		if doc.status in ('Queued', 'In Progress'):
+		doc = frappe.get_doc("Repost Item Valuation", row.name)
+		if doc.status in ("Queued", "In Progress"):
 			repost(doc)
 			doc.deduplicate_similar_repost()
 
@@ -182,14 +269,16 @@
 	if riv_entries:
 		return
 
-	for d in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
-		check_if_stock_and_account_balance_synced(today(), d.name)
 
 def get_repost_item_valuation_entries():
-	return frappe.db.sql(""" SELECT name from `tabRepost Item Valuation`
+	return frappe.db.sql(
+		""" SELECT name from `tabRepost Item Valuation`
 		WHERE status in ('Queued', 'In Progress') and creation <= %s and docstatus = 1
 		ORDER BY timestamp(posting_date, posting_time) asc, creation asc
-	""", now(), as_dict=1)
+	""",
+		now(),
+		as_dict=1,
+	)
 
 
 def in_configured_timeslot(repost_settings=None, current_time=None):
diff --git a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
index 78b432d..edd2553 100644
--- a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
@@ -1,20 +1,30 @@
 # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 
-import unittest
+
+from unittest.mock import MagicMock, call
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils import nowdate
+from frappe.utils.data import add_to_date, today
 
+from erpnext.accounts.utils import repost_gle_for_stock_vouchers
 from erpnext.controllers.stock_controller import create_item_wise_repost_entries
+from erpnext.stock.doctype.item.test_item import make_item
 from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
 from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import (
 	in_configured_timeslot,
 )
+from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+from erpnext.stock.tests.test_utils import StockTestMixin
 from erpnext.stock.utils import PendingRepostingError
 
 
-class TestRepostItemValuation(unittest.TestCase):
+class TestRepostItemValuation(FrappeTestCase, StockTestMixin):
+	def tearDown(self):
+		frappe.flags.dont_execute_stock_reposts = False
+
 	def test_repost_time_slot(self):
 		repost_settings = frappe.get_doc("Stock Reposting Settings")
 
@@ -153,7 +163,7 @@
 			posting_date=today,
 			posting_time="00:01:00",
 		)
-		riv.flags.dont_run_in_test = True # keep it queued
+		riv.flags.dont_run_in_test = True  # keep it queued
 		riv.submit()
 
 		stock_settings = frappe.get_doc("Stock Settings")
@@ -162,3 +172,103 @@
 		self.assertRaises(PendingRepostingError, stock_settings.save)
 
 		riv.set_status("Skipped")
+
+	def test_prevention_of_cancelled_transaction_riv(self):
+		frappe.flags.dont_execute_stock_reposts = True
+
+		item = make_item()
+		warehouse = "_Test Warehouse - _TC"
+		old = make_stock_entry(item_code=item.name, to_warehouse=warehouse, qty=2, rate=5)
+		_new = make_stock_entry(item_code=item.name, to_warehouse=warehouse, qty=5, rate=10)
+
+		old.cancel()
+
+		riv = frappe.get_last_doc(
+			"Repost Item Valuation", {"voucher_type": old.doctype, "voucher_no": old.name}
+		)
+		self.assertRaises(frappe.ValidationError, riv.cancel)
+
+		riv.db_set("status", "Skipped")
+		riv.reload()
+		riv.cancel()  # it should cancel now
+
+	def test_queue_progress_serialization(self):
+		# Make sure set/tuple -> list behaviour is retained.
+		self.assertEqual(
+			[["a", "b"], ["c", "d"]],
+			sorted(frappe.parse_json(frappe.as_json(set([("a", "b"), ("c", "d")])))),
+		)
+
+	def test_gl_repost_progress(self):
+		from erpnext.accounts import utils
+
+		# lower numbers to simplify test
+		orig_chunk_size = utils.GL_REPOSTING_CHUNK
+		utils.GL_REPOSTING_CHUNK = 1
+		self.addCleanup(setattr, utils, "GL_REPOSTING_CHUNK", orig_chunk_size)
+
+		doc = frappe.new_doc("Repost Item Valuation")
+		doc.db_set = MagicMock()
+
+		vouchers = []
+		company = "_Test Company with perpetual inventory"
+		posting_date = today()
+
+		for _ in range(3):
+			se = make_stock_entry(company=company, qty=1, rate=2, target="Stores - TCP1")
+			vouchers.append((se.doctype, se.name))
+
+		repost_gle_for_stock_vouchers(stock_vouchers=vouchers, posting_date=posting_date, repost_doc=doc)
+		self.assertIn(call("gl_reposting_index", 1), doc.db_set.mock_calls)
+		doc.db_set.reset_mock()
+
+		doc.gl_reposting_index = 1
+		repost_gle_for_stock_vouchers(stock_vouchers=vouchers, posting_date=posting_date, repost_doc=doc)
+
+		self.assertNotIn(call("gl_reposting_index", 1), doc.db_set.mock_calls)
+
+	def test_gl_complete_gl_reposting(self):
+		from erpnext.accounts import utils
+
+		# lower numbers to simplify test
+		orig_chunk_size = utils.GL_REPOSTING_CHUNK
+		utils.GL_REPOSTING_CHUNK = 2
+		self.addCleanup(setattr, utils, "GL_REPOSTING_CHUNK", orig_chunk_size)
+
+		item = self.make_item().name
+
+		company = "_Test Company with perpetual inventory"
+
+		for _ in range(10):
+			make_stock_entry(item=item, company=company, qty=1, rate=10, target="Stores - TCP1")
+
+		# consume
+		consumption = make_stock_entry(item=item, company=company, qty=1, source="Stores - TCP1")
+
+		self.assertGLEs(
+			consumption,
+			[{"credit": 10, "debit": 0}],
+			gle_filters={"account": "Stock In Hand - TCP1"},
+		)
+
+		# backdated receipt
+		backdated_receipt = make_stock_entry(
+			item=item,
+			company=company,
+			qty=1,
+			rate=50,
+			target="Stores - TCP1",
+			posting_date=add_to_date(today(), days=-1),
+		)
+		self.assertGLEs(
+			backdated_receipt,
+			[{"credit": 0, "debit": 50}],
+			gle_filters={"account": "Stock In Hand - TCP1"},
+		)
+
+		# check that original consumption GLe is updated
+		self.assertGLEs(
+			consumption,
+			[{"credit": 50, "debit": 0}],
+			gle_filters={"account": "Stock In Hand - TCP1"},
+		)
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index ee08e38..7101190 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -3,26 +3,66 @@
 
 
 import json
+from typing import List, Optional, Union
 
 import frappe
 from frappe import ValidationError, _
 from frappe.model.naming import make_autoname
-from frappe.utils import add_days, cint, cstr, flt, get_link_to_form, getdate, nowdate
+from frappe.query_builder.functions import Coalesce
+from frappe.utils import (
+	add_days,
+	cint,
+	cstr,
+	flt,
+	get_link_to_form,
+	getdate,
+	nowdate,
+	safe_json_loads,
+)
 
 from erpnext.controllers.stock_controller import StockController
 from erpnext.stock.get_item_details import get_reserved_qty_for_so
 
 
-class SerialNoCannotCreateDirectError(ValidationError): pass
-class SerialNoCannotCannotChangeError(ValidationError): pass
-class SerialNoNotRequiredError(ValidationError): pass
-class SerialNoRequiredError(ValidationError): pass
-class SerialNoQtyError(ValidationError): pass
-class SerialNoItemError(ValidationError): pass
-class SerialNoWarehouseError(ValidationError): pass
-class SerialNoBatchError(ValidationError): pass
-class SerialNoNotExistsError(ValidationError): pass
-class SerialNoDuplicateError(ValidationError): pass
+class SerialNoCannotCreateDirectError(ValidationError):
+	pass
+
+
+class SerialNoCannotCannotChangeError(ValidationError):
+	pass
+
+
+class SerialNoNotRequiredError(ValidationError):
+	pass
+
+
+class SerialNoRequiredError(ValidationError):
+	pass
+
+
+class SerialNoQtyError(ValidationError):
+	pass
+
+
+class SerialNoItemError(ValidationError):
+	pass
+
+
+class SerialNoWarehouseError(ValidationError):
+	pass
+
+
+class SerialNoBatchError(ValidationError):
+	pass
+
+
+class SerialNoNotExistsError(ValidationError):
+	pass
+
+
+class SerialNoDuplicateError(ValidationError):
+	pass
+
 
 class SerialNo(StockController):
 	def __init__(self, *args, **kwargs):
@@ -31,7 +71,12 @@
 
 	def validate(self):
 		if self.get("__islocal") and self.warehouse and not self.via_stock_ledger:
-			frappe.throw(_("New Serial No cannot have Warehouse. Warehouse must be set by Stock Entry or Purchase Receipt"), SerialNoCannotCreateDirectError)
+			frappe.throw(
+				_(
+					"New Serial No cannot have Warehouse. Warehouse must be set by Stock Entry or Purchase Receipt"
+				),
+				SerialNoCannotCreateDirectError,
+			)
 
 		self.set_maintenance_status()
 		self.validate_warehouse()
@@ -66,22 +111,21 @@
 
 	def validate_warehouse(self):
 		if not self.get("__islocal"):
-			item_code, warehouse = frappe.db.get_value("Serial No",
-				self.name, ["item_code", "warehouse"])
+			item_code, warehouse = frappe.db.get_value("Serial No", self.name, ["item_code", "warehouse"])
 			if not self.via_stock_ledger and item_code != self.item_code:
-				frappe.throw(_("Item Code cannot be changed for Serial No."),
-					SerialNoCannotCannotChangeError)
+				frappe.throw(_("Item Code cannot be changed for Serial No."), SerialNoCannotCannotChangeError)
 			if not self.via_stock_ledger and warehouse != self.warehouse:
-				frappe.throw(_("Warehouse cannot be changed for Serial No."),
-					SerialNoCannotCannotChangeError)
+				frappe.throw(_("Warehouse cannot be changed for Serial No."), SerialNoCannotCannotChangeError)
 
 	def validate_item(self):
 		"""
-			Validate whether serial no is required for this item
+		Validate whether serial no is required for this item
 		"""
 		item = frappe.get_cached_doc("Item", self.item_code)
-		if item.has_serial_no!=1:
-			frappe.throw(_("Item {0} is not setup for Serial Nos. Check Item master").format(self.item_code))
+		if item.has_serial_no != 1:
+			frappe.throw(
+				_("Item {0} is not setup for Serial Nos. Check Item master").format(self.item_code)
+			)
 
 		self.item_group = item.item_group
 		self.description = item.description
@@ -97,17 +141,24 @@
 			self.purchase_time = purchase_sle.posting_time
 			self.purchase_rate = purchase_sle.incoming_rate
 			if purchase_sle.voucher_type in ("Purchase Receipt", "Purchase Invoice"):
-				self.supplier, self.supplier_name = \
-					frappe.db.get_value(purchase_sle.voucher_type, purchase_sle.voucher_no,
-						["supplier", "supplier_name"])
+				self.supplier, self.supplier_name = frappe.db.get_value(
+					purchase_sle.voucher_type, purchase_sle.voucher_no, ["supplier", "supplier_name"]
+				)
 
 			# If sales return entry
-			if self.purchase_document_type == 'Delivery Note':
+			if self.purchase_document_type == "Delivery Note":
 				self.sales_invoice = None
 		else:
-			for fieldname in ("purchase_document_type", "purchase_document_no",
-				"purchase_date", "purchase_time", "purchase_rate", "supplier", "supplier_name"):
-					self.set(fieldname, None)
+			for fieldname in (
+				"purchase_document_type",
+				"purchase_document_no",
+				"purchase_date",
+				"purchase_time",
+				"purchase_rate",
+				"supplier",
+				"supplier_name",
+			):
+				self.set(fieldname, None)
 
 	def set_sales_details(self, delivery_sle):
 		if delivery_sle:
@@ -115,18 +166,25 @@
 			self.delivery_document_no = delivery_sle.voucher_no
 			self.delivery_date = delivery_sle.posting_date
 			self.delivery_time = delivery_sle.posting_time
-			if delivery_sle.voucher_type  in ("Delivery Note", "Sales Invoice"):
-				self.customer, self.customer_name = \
-					frappe.db.get_value(delivery_sle.voucher_type, delivery_sle.voucher_no,
-						["customer", "customer_name"])
+			if delivery_sle.voucher_type in ("Delivery Note", "Sales Invoice"):
+				self.customer, self.customer_name = frappe.db.get_value(
+					delivery_sle.voucher_type, delivery_sle.voucher_no, ["customer", "customer_name"]
+				)
 			if self.warranty_period:
-				self.warranty_expiry_date	= add_days(cstr(delivery_sle.posting_date),
-					cint(self.warranty_period))
+				self.warranty_expiry_date = add_days(
+					cstr(delivery_sle.posting_date), cint(self.warranty_period)
+				)
 		else:
-			for fieldname in ("delivery_document_type", "delivery_document_no",
-				"delivery_date", "delivery_time", "customer", "customer_name",
-				"warranty_expiry_date"):
-					self.set(fieldname, None)
+			for fieldname in (
+				"delivery_document_type",
+				"delivery_document_no",
+				"delivery_date",
+				"delivery_time",
+				"customer",
+				"customer_name",
+				"warranty_expiry_date",
+			):
+				self.set(fieldname, None)
 
 	def get_last_sle(self, serial_no=None):
 		entries = {}
@@ -148,7 +206,8 @@
 		if not serial_no:
 			serial_no = self.name
 
-		for sle in frappe.db.sql("""
+		for sle in frappe.db.sql(
+			"""
 			SELECT voucher_type, voucher_no,
 				posting_date, posting_time, incoming_rate, actual_qty, serial_no
 			FROM
@@ -164,25 +223,30 @@
 			ORDER BY
 				posting_date desc, posting_time desc, creation desc""",
 			(
-				self.item_code, self.company,
+				self.item_code,
+				self.company,
 				serial_no,
-				serial_no+'\n%',
-				'%\n'+serial_no,
-				'%\n'+serial_no+'\n%'
+				serial_no + "\n%",
+				"%\n" + serial_no,
+				"%\n" + serial_no + "\n%",
 			),
-			as_dict=1):
-				if serial_no.upper() in get_serial_nos(sle.serial_no):
-					if cint(sle.actual_qty) > 0:
-						sle_dict.setdefault("incoming", []).append(sle)
-					else:
-						sle_dict.setdefault("outgoing", []).append(sle)
+			as_dict=1,
+		):
+			if serial_no.upper() in get_serial_nos(sle.serial_no):
+				if cint(sle.actual_qty) > 0:
+					sle_dict.setdefault("incoming", []).append(sle)
+				else:
+					sle_dict.setdefault("outgoing", []).append(sle)
 
 		return sle_dict
 
 	def on_trash(self):
-		sl_entries = frappe.db.sql("""select serial_no from `tabStock Ledger Entry`
+		sl_entries = frappe.db.sql(
+			"""select serial_no from `tabStock Ledger Entry`
 			where serial_no like %s and item_code=%s and is_cancelled=0""",
-			("%%%s%%" % self.name, self.item_code), as_dict=True)
+			("%%%s%%" % self.name, self.item_code),
+			as_dict=True,
+		)
 
 		# Find the exact match
 		sle_exists = False
@@ -192,7 +256,9 @@
 				break
 
 		if sle_exists:
-			frappe.throw(_("Cannot delete Serial No {0}, as it is used in stock transactions").format(self.name))
+			frappe.throw(
+				_("Cannot delete Serial No {0}, as it is used in stock transactions").format(self.name)
+			)
 
 	def update_serial_no_reference(self, serial_no=None):
 		last_sle = self.get_last_sle(serial_no)
@@ -201,57 +267,95 @@
 		self.set_maintenance_status()
 		self.set_status()
 
+
 def process_serial_no(sle):
 	item_det = get_item_details(sle.item_code)
 	validate_serial_no(sle, item_det)
 	update_serial_nos(sle, item_det)
 
+
 def validate_serial_no(sle, item_det):
 	serial_nos = get_serial_nos(sle.serial_no) if sle.serial_no else []
 	validate_material_transfer_entry(sle)
 
-	if item_det.has_serial_no==0:
+	if item_det.has_serial_no == 0:
 		if serial_nos:
-			frappe.throw(_("Item {0} is not setup for Serial Nos. Column must be blank").format(sle.item_code),
-				SerialNoNotRequiredError)
+			frappe.throw(
+				_("Item {0} is not setup for Serial Nos. Column must be blank").format(sle.item_code),
+				SerialNoNotRequiredError,
+			)
 	elif not sle.is_cancelled:
 		if serial_nos:
 			if cint(sle.actual_qty) != flt(sle.actual_qty):
-				frappe.throw(_("Serial No {0} quantity {1} cannot be a fraction").format(sle.item_code, sle.actual_qty))
+				frappe.throw(
+					_("Serial No {0} quantity {1} cannot be a fraction").format(sle.item_code, sle.actual_qty)
+				)
 
 			if len(serial_nos) and len(serial_nos) != abs(cint(sle.actual_qty)):
-				frappe.throw(_("{0} Serial Numbers required for Item {1}. You have provided {2}.").format(abs(sle.actual_qty), sle.item_code, len(serial_nos)),
-					SerialNoQtyError)
+				frappe.throw(
+					_("{0} Serial Numbers required for Item {1}. You have provided {2}.").format(
+						abs(sle.actual_qty), sle.item_code, len(serial_nos)
+					),
+					SerialNoQtyError,
+				)
 
 			if len(serial_nos) != len(set(serial_nos)):
-				frappe.throw(_("Duplicate Serial No entered for Item {0}").format(sle.item_code), SerialNoDuplicateError)
+				frappe.throw(
+					_("Duplicate Serial No entered for Item {0}").format(sle.item_code), SerialNoDuplicateError
+				)
 
 			for serial_no in serial_nos:
 				if frappe.db.exists("Serial No", serial_no):
-					sr = frappe.db.get_value("Serial No", serial_no, ["name", "item_code", "batch_no", "sales_order",
-						"delivery_document_no", "delivery_document_type", "warehouse", "purchase_document_type",
-						"purchase_document_no", "company", "status"], as_dict=1)
+					sr = frappe.db.get_value(
+						"Serial No",
+						serial_no,
+						[
+							"name",
+							"item_code",
+							"batch_no",
+							"sales_order",
+							"delivery_document_no",
+							"delivery_document_type",
+							"warehouse",
+							"purchase_document_type",
+							"purchase_document_no",
+							"company",
+							"status",
+						],
+						as_dict=1,
+					)
 
-					if sr.item_code!=sle.item_code:
+					if sr.item_code != sle.item_code:
 						if not allow_serial_nos_with_different_item(serial_no, sle):
-							frappe.throw(_("Serial No {0} does not belong to Item {1}").format(serial_no,
-								sle.item_code), SerialNoItemError)
+							frappe.throw(
+								_("Serial No {0} does not belong to Item {1}").format(serial_no, sle.item_code),
+								SerialNoItemError,
+							)
 
 					if cint(sle.actual_qty) > 0 and has_serial_no_exists(sr, sle):
 						doc_name = frappe.bold(get_link_to_form(sr.purchase_document_type, sr.purchase_document_no))
-						frappe.throw(_("Serial No {0} has already been received in the {1} #{2}")
-							.format(frappe.bold(serial_no), sr.purchase_document_type, doc_name), SerialNoDuplicateError)
+						frappe.throw(
+							_("Serial No {0} has already been received in the {1} #{2}").format(
+								frappe.bold(serial_no), sr.purchase_document_type, doc_name
+							),
+							SerialNoDuplicateError,
+						)
 
-					if (sr.delivery_document_no and sle.voucher_type not in ['Stock Entry', 'Stock Reconciliation']
-						and sle.voucher_type == sr.delivery_document_type):
-						return_against = frappe.db.get_value(sle.voucher_type, sle.voucher_no, 'return_against')
+					if (
+						sr.delivery_document_no
+						and sle.voucher_type not in ["Stock Entry", "Stock Reconciliation"]
+						and sle.voucher_type == sr.delivery_document_type
+					):
+						return_against = frappe.db.get_value(sle.voucher_type, sle.voucher_no, "return_against")
 						if return_against and return_against != sr.delivery_document_no:
 							frappe.throw(_("Serial no {0} has been already returned").format(sr.name))
 
 					if cint(sle.actual_qty) < 0:
-						if sr.warehouse!=sle.warehouse:
-							frappe.throw(_("Serial No {0} does not belong to Warehouse {1}").format(serial_no,
-								sle.warehouse), SerialNoWarehouseError)
+						if sr.warehouse != sle.warehouse:
+							frappe.throw(
+								_("Serial No {0} does not belong to Warehouse {1}").format(serial_no, sle.warehouse),
+								SerialNoWarehouseError,
+							)
 
 						if not sr.purchase_document_no:
 							frappe.throw(_("Serial No {0} not in stock").format(serial_no), SerialNoNotExistsError)
@@ -259,66 +363,100 @@
 						if sle.voucher_type in ("Delivery Note", "Sales Invoice"):
 
 							if sr.batch_no and sr.batch_no != sle.batch_no:
-								frappe.throw(_("Serial No {0} does not belong to Batch {1}").format(serial_no,
-									sle.batch_no), SerialNoBatchError)
+								frappe.throw(
+									_("Serial No {0} does not belong to Batch {1}").format(serial_no, sle.batch_no),
+									SerialNoBatchError,
+								)
 
 							if not sle.is_cancelled and not sr.warehouse:
-								frappe.throw(_("Serial No {0} does not belong to any Warehouse")
-									.format(serial_no), SerialNoWarehouseError)
+								frappe.throw(
+									_("Serial No {0} does not belong to any Warehouse").format(serial_no),
+									SerialNoWarehouseError,
+								)
 
 							# if Sales Order reference in Serial No validate the Delivery Note or Invoice is against the same
 							if sr.sales_order:
 								if sle.voucher_type == "Sales Invoice":
-									if not frappe.db.exists("Sales Invoice Item", {"parent": sle.voucher_no,
-										"item_code": sle.item_code, "sales_order": sr.sales_order}):
+									if not frappe.db.exists(
+										"Sales Invoice Item",
+										{"parent": sle.voucher_no, "item_code": sle.item_code, "sales_order": sr.sales_order},
+									):
 										frappe.throw(
-											_("Cannot deliver Serial No {0} of item {1} as it is reserved to fullfill Sales Order {2}")
-											.format(sr.name, sle.item_code, sr.sales_order)
+											_(
+												"Cannot deliver Serial No {0} of item {1} as it is reserved to fullfill Sales Order {2}"
+											).format(sr.name, sle.item_code, sr.sales_order)
 										)
 								elif sle.voucher_type == "Delivery Note":
-									if not frappe.db.exists("Delivery Note Item", {"parent": sle.voucher_no,
-										"item_code": sle.item_code, "against_sales_order": sr.sales_order}):
-										invoice = frappe.db.get_value("Delivery Note Item", {"parent": sle.voucher_no,
-											"item_code": sle.item_code}, "against_sales_invoice")
-										if not invoice or frappe.db.exists("Sales Invoice Item",
-											{"parent": invoice, "item_code": sle.item_code,
-											"sales_order": sr.sales_order}):
+									if not frappe.db.exists(
+										"Delivery Note Item",
+										{
+											"parent": sle.voucher_no,
+											"item_code": sle.item_code,
+											"against_sales_order": sr.sales_order,
+										},
+									):
+										invoice = frappe.db.get_value(
+											"Delivery Note Item",
+											{"parent": sle.voucher_no, "item_code": sle.item_code},
+											"against_sales_invoice",
+										)
+										if not invoice or frappe.db.exists(
+											"Sales Invoice Item",
+											{"parent": invoice, "item_code": sle.item_code, "sales_order": sr.sales_order},
+										):
 											frappe.throw(
-												_("Cannot deliver Serial No {0} of item {1} as it is reserved to fullfill Sales Order {2}")
-												.format(sr.name, sle.item_code, sr.sales_order)
+												_(
+													"Cannot deliver Serial No {0} of item {1} as it is reserved to fullfill Sales Order {2}"
+												).format(sr.name, sle.item_code, sr.sales_order)
 											)
 							# if Sales Order reference in Delivery Note or Invoice validate SO reservations for item
 							if sle.voucher_type == "Sales Invoice":
-								sales_order = frappe.db.get_value("Sales Invoice Item", {"parent": sle.voucher_no,
-									"item_code": sle.item_code}, "sales_order")
+								sales_order = frappe.db.get_value(
+									"Sales Invoice Item",
+									{"parent": sle.voucher_no, "item_code": sle.item_code},
+									"sales_order",
+								)
 								if sales_order and get_reserved_qty_for_so(sales_order, sle.item_code):
 									validate_so_serial_no(sr, sales_order)
 							elif sle.voucher_type == "Delivery Note":
-								sales_order = frappe.get_value("Delivery Note Item", {"parent": sle.voucher_no,
-									"item_code": sle.item_code}, "against_sales_order")
+								sales_order = frappe.get_value(
+									"Delivery Note Item",
+									{"parent": sle.voucher_no, "item_code": sle.item_code},
+									"against_sales_order",
+								)
 								if sales_order and get_reserved_qty_for_so(sales_order, sle.item_code):
 									validate_so_serial_no(sr, sales_order)
 								else:
-									sales_invoice = frappe.get_value("Delivery Note Item", {"parent": sle.voucher_no,
-										"item_code": sle.item_code}, "against_sales_invoice")
+									sales_invoice = frappe.get_value(
+										"Delivery Note Item",
+										{"parent": sle.voucher_no, "item_code": sle.item_code},
+										"against_sales_invoice",
+									)
 									if sales_invoice:
-										sales_order = frappe.db.get_value("Sales Invoice Item", {
-											"parent": sales_invoice, "item_code": sle.item_code}, "sales_order")
+										sales_order = frappe.db.get_value(
+											"Sales Invoice Item",
+											{"parent": sales_invoice, "item_code": sle.item_code},
+											"sales_order",
+										)
 										if sales_order and get_reserved_qty_for_so(sales_order, sle.item_code):
 											validate_so_serial_no(sr, sales_order)
 				elif cint(sle.actual_qty) < 0:
 					# transfer out
 					frappe.throw(_("Serial No {0} not in stock").format(serial_no), SerialNoNotExistsError)
 		elif cint(sle.actual_qty) < 0 or not item_det.serial_no_series:
-			frappe.throw(_("Serial Nos Required for Serialized Item {0}").format(sle.item_code),
-				SerialNoRequiredError)
+			frappe.throw(
+				_("Serial Nos Required for Serialized Item {0}").format(sle.item_code), SerialNoRequiredError
+			)
 	elif serial_nos:
 		# SLE is being cancelled and has serial nos
 		for serial_no in serial_nos:
 			check_serial_no_validity_on_cancel(serial_no, sle)
 
+
 def check_serial_no_validity_on_cancel(serial_no, sle):
-	sr = frappe.db.get_value("Serial No", serial_no, ["name", "warehouse", "company", "status"], as_dict=1)
+	sr = frappe.db.get_value(
+		"Serial No", serial_no, ["name", "warehouse", "company", "status"], as_dict=1
+	)
 	sr_link = frappe.utils.get_link_to_form("Serial No", serial_no)
 	doc_link = frappe.utils.get_link_to_form(sle.voucher_type, sle.voucher_no)
 	actual_qty = cint(sle.actual_qty)
@@ -328,57 +466,65 @@
 	if sr and (actual_qty < 0 or is_stock_reco) and (sr.warehouse and sr.warehouse != sle.warehouse):
 		# receipt(inward) is being cancelled
 		msg = _("Cannot cancel {0} {1} as Serial No {2} does not belong to the warehouse {3}").format(
-			sle.voucher_type, doc_link, sr_link, frappe.bold(sle.warehouse))
+			sle.voucher_type, doc_link, sr_link, frappe.bold(sle.warehouse)
+		)
 	elif sr and actual_qty > 0 and not is_stock_reco:
 		# delivery is being cancelled, check for warehouse.
 		if sr.warehouse:
 			# serial no is active in another warehouse/company.
 			msg = _("Cannot cancel {0} {1} as Serial No {2} is active in warehouse {3}").format(
-				sle.voucher_type, doc_link, sr_link, frappe.bold(sr.warehouse))
+				sle.voucher_type, doc_link, sr_link, frappe.bold(sr.warehouse)
+			)
 		elif sr.company != sle.company and sr.status == "Delivered":
 			# serial no is inactive (allowed) or delivered from another company (block).
 			msg = _("Cannot cancel {0} {1} as Serial No {2} does not belong to the company {3}").format(
-				sle.voucher_type, doc_link, sr_link, frappe.bold(sle.company))
+				sle.voucher_type, doc_link, sr_link, frappe.bold(sle.company)
+			)
 
 	if msg:
 		frappe.throw(msg, title=_("Cannot cancel"))
 
-def validate_material_transfer_entry(sle_doc):
-	sle_doc.update({
-		"skip_update_serial_no": False,
-		"skip_serial_no_validaiton": False
-	})
 
-	if (sle_doc.voucher_type == "Stock Entry" and not sle_doc.is_cancelled and
-		frappe.get_cached_value("Stock Entry", sle_doc.voucher_no, "purpose") == "Material Transfer"):
+def validate_material_transfer_entry(sle_doc):
+	sle_doc.update({"skip_update_serial_no": False, "skip_serial_no_validaiton": False})
+
+	if (
+		sle_doc.voucher_type == "Stock Entry"
+		and not sle_doc.is_cancelled
+		and frappe.get_cached_value("Stock Entry", sle_doc.voucher_no, "purpose") == "Material Transfer"
+	):
 		if sle_doc.actual_qty < 0:
 			sle_doc.skip_update_serial_no = True
 		else:
 			sle_doc.skip_serial_no_validaiton = True
 
-def validate_so_serial_no(sr, sales_order):
-	if not sr.sales_order or sr.sales_order!= sales_order:
-		msg = (_("Sales Order {0} has reservation for the item {1}, you can only deliver reserved {1} against {0}.")
-			.format(sales_order, sr.item_code))
 
-		frappe.throw(_("""{0} Serial No {1} cannot be delivered""")
-			.format(msg, sr.name))
+def validate_so_serial_no(sr, sales_order):
+	if not sr.sales_order or sr.sales_order != sales_order:
+		msg = _(
+			"Sales Order {0} has reservation for the item {1}, you can only deliver reserved {1} against {0}."
+		).format(sales_order, sr.item_code)
+
+		frappe.throw(_("""{0} Serial No {1} cannot be delivered""").format(msg, sr.name))
+
 
 def has_serial_no_exists(sn, sle):
-	if (sn.warehouse and not sle.skip_serial_no_validaiton
-		and sle.voucher_type != 'Stock Reconciliation'):
+	if (
+		sn.warehouse and not sle.skip_serial_no_validaiton and sle.voucher_type != "Stock Reconciliation"
+	):
 		return True
 
 	if sn.company != sle.company:
 		return False
 
+
 def allow_serial_nos_with_different_item(sle_serial_no, sle):
 	"""
-		Allows same serial nos for raw materials and finished goods
-		in Manufacture / Repack type Stock Entry
+	Allows same serial nos for raw materials and finished goods
+	in Manufacture / Repack type Stock Entry
 	"""
 	allow_serial_nos = False
-	if sle.voucher_type=="Stock Entry" and cint(sle.actual_qty) > 0:
+	if sle.voucher_type == "Stock Entry" and cint(sle.actual_qty) > 0:
 		stock_entry = frappe.get_cached_doc("Stock Entry", sle.voucher_no)
 		if stock_entry.purpose in ("Repack", "Manufacture"):
 			for d in stock_entry.get("items"):
@@ -389,16 +535,24 @@
 
 	return allow_serial_nos
 
+
 def update_serial_nos(sle, item_det):
-	if sle.skip_update_serial_no: return
-	if not sle.is_cancelled and not sle.serial_no and cint(sle.actual_qty) > 0 \
-			and item_det.has_serial_no == 1 and item_det.serial_no_series:
+	if sle.skip_update_serial_no:
+		return
+	if (
+		not sle.is_cancelled
+		and not sle.serial_no
+		and cint(sle.actual_qty) > 0
+		and item_det.has_serial_no == 1
+		and item_det.serial_no_series
+	):
 		serial_nos = get_auto_serial_nos(item_det.serial_no_series, sle.actual_qty)
-		frappe.db.set(sle, "serial_no", serial_nos)
+		sle.db_set("serial_no", serial_nos)
 		validate_serial_no(sle, item_det)
 	if sle.serial_no:
 		auto_make_serial_nos(sle)
 
+
 def get_auto_serial_nos(serial_no_series, qty):
 	serial_nos = []
 	for i in range(cint(qty)):
@@ -406,22 +560,24 @@
 
 	return "\n".join(serial_nos)
 
+
 def get_new_serial_number(series):
 	sr_no = make_autoname(series, "Serial No")
 	if frappe.db.exists("Serial No", sr_no):
 		sr_no = get_new_serial_number(series)
 	return sr_no
 
+
 def auto_make_serial_nos(args):
-	serial_nos = get_serial_nos(args.get('serial_no'))
+	serial_nos = get_serial_nos(args.get("serial_no"))
 	created_numbers = []
-	voucher_type = args.get('voucher_type')
-	item_code = args.get('item_code')
+	voucher_type = args.get("voucher_type")
+	item_code = args.get("item_code")
 	for serial_no in serial_nos:
 		is_new = False
 		if frappe.db.exists("Serial No", serial_no):
 			sr = frappe.get_cached_doc("Serial No", serial_no)
-		elif args.get('actual_qty', 0) > 0:
+		elif args.get("actual_qty", 0) > 0:
 			sr = frappe.new_doc("Serial No")
 			is_new = True
 
@@ -429,7 +585,7 @@
 		if is_new:
 			created_numbers.append(sr.name)
 
-	form_links = list(map(lambda d: get_link_to_form('Serial No', d), created_numbers))
+	form_links = list(map(lambda d: get_link_to_form("Serial No", d), created_numbers))
 
 	# Setting up tranlated title field for all cases
 	singular_title = _("Serial Number Created")
@@ -441,29 +597,41 @@
 	if len(form_links) == 1:
 		frappe.msgprint(_("Serial No {0} Created").format(form_links[0]), singular_title)
 	elif len(form_links) > 0:
-		message = _("The following serial numbers were created: <br><br> {0}").format(get_items_html(form_links, item_code))
+		message = _("The following serial numbers were created: <br><br> {0}").format(
+			get_items_html(form_links, item_code)
+		)
 		frappe.msgprint(message, multiple_title)
 
+
 def get_items_html(serial_nos, item_code):
-	body = ', '.join(serial_nos)
-	return '''<details><summary>
+	body = ", ".join(serial_nos)
+	return """<details><summary>
 		<b>{0}:</b> {1} Serial Numbers <span class="caret"></span>
 	</summary>
 	<div class="small">{2}</div></details>
-	'''.format(item_code, len(serial_nos), body)
+	""".format(
+		item_code, len(serial_nos), body
+	)
 
 
 def get_item_details(item_code):
-	return frappe.db.sql("""select name, has_batch_no, docstatus,
+	return frappe.db.sql(
+		"""select name, has_batch_no, docstatus,
 		is_stock_item, has_serial_no, serial_no_series
-		from tabItem where name=%s""", item_code, as_dict=True)[0]
+		from tabItem where name=%s""",
+		item_code,
+		as_dict=True,
+	)[0]
+
 
 def get_serial_nos(serial_no):
 	if isinstance(serial_no, list):
 		return serial_no
 
-	return [s.strip() for s in cstr(serial_no).strip().upper().replace(',', '\n').split('\n')
-		if s.strip()]
+	return [
+		s.strip() for s in cstr(serial_no).strip().upper().replace(",", "\n").split("\n") if s.strip()
+	]
+
 
 def clean_serial_no_string(serial_no: str) -> str:
 	if not serial_no:
@@ -472,20 +640,23 @@
 	serial_no_list = get_serial_nos(serial_no)
 	return "\n".join(serial_no_list)
 
+
 def update_args_for_serial_no(serial_no_doc, serial_no, args, is_new=False):
 	for field in ["item_code", "work_order", "company", "batch_no", "supplier", "location"]:
 		if args.get(field):
 			serial_no_doc.set(field, args.get(field))
 
 	serial_no_doc.via_stock_ledger = args.get("via_stock_ledger") or True
-	serial_no_doc.warehouse = (args.get("warehouse")
-		if args.get("actual_qty", 0) > 0 else None)
+	serial_no_doc.warehouse = args.get("warehouse") if args.get("actual_qty", 0) > 0 else None
 
 	if is_new:
 		serial_no_doc.serial_no = serial_no
 
-	if (serial_no_doc.sales_order and args.get("voucher_type") == "Stock Entry"
-		and not args.get("actual_qty", 0) > 0):
+	if (
+		serial_no_doc.sales_order
+		and args.get("voucher_type") == "Stock Entry"
+		and not args.get("actual_qty", 0) > 0
+	):
 		serial_no_doc.sales_order = None
 
 	serial_no_doc.validate_item()
@@ -498,146 +669,224 @@
 
 	return serial_no_doc
 
-def update_serial_nos_after_submit(controller, parentfield):
-	stock_ledger_entries = frappe.db.sql("""select voucher_detail_no, serial_no, actual_qty, warehouse
-		from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""",
-		(controller.doctype, controller.name), as_dict=True)
 
-	if not stock_ledger_entries: return
+def update_serial_nos_after_submit(controller, parentfield):
+	stock_ledger_entries = frappe.db.sql(
+		"""select voucher_detail_no, serial_no, actual_qty, warehouse
+		from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""",
+		(controller.doctype, controller.name),
+		as_dict=True,
+	)
+
+	if not stock_ledger_entries:
+		return
 
 	for d in controller.get(parentfield):
 		if d.serial_no:
 			continue
 
-		update_rejected_serial_nos = True if (controller.doctype in ("Purchase Receipt", "Purchase Invoice")
-			and d.rejected_qty) else False
+		update_rejected_serial_nos = (
+			True
+			if (controller.doctype in ("Purchase Receipt", "Purchase Invoice") and d.rejected_qty)
+			else False
+		)
 		accepted_serial_nos_updated = False
 
 		if controller.doctype == "Stock Entry":
 			warehouse = d.t_warehouse
 			qty = d.transfer_qty
+		elif controller.doctype in ("Sales Invoice", "Delivery Note"):
+			warehouse = d.warehouse
+			qty = d.stock_qty
 		else:
 			warehouse = d.warehouse
-			qty = (d.qty if controller.doctype == "Stock Reconciliation"
-				else d.stock_qty)
+			qty = d.qty if controller.doctype == "Stock Reconciliation" else d.stock_qty
 		for sle in stock_ledger_entries:
-			if sle.voucher_detail_no==d.name:
-				if not accepted_serial_nos_updated and qty and abs(sle.actual_qty)==qty \
-					and sle.warehouse == warehouse and sle.serial_no != d.serial_no:
-						d.serial_no = sle.serial_no
-						frappe.db.set_value(d.doctype, d.name, "serial_no", sle.serial_no)
-						accepted_serial_nos_updated = True
-						if not update_rejected_serial_nos:
-							break
-				elif update_rejected_serial_nos and abs(sle.actual_qty)==d.rejected_qty \
-					and sle.warehouse == d.rejected_warehouse and sle.serial_no != d.rejected_serial_no:
-						d.rejected_serial_no = sle.serial_no
-						frappe.db.set_value(d.doctype, d.name, "rejected_serial_no", sle.serial_no)
-						update_rejected_serial_nos = False
-						if accepted_serial_nos_updated:
-							break
+			if sle.voucher_detail_no == d.name:
+				if (
+					not accepted_serial_nos_updated
+					and qty
+					and abs(sle.actual_qty) == abs(qty)
+					and sle.warehouse == warehouse
+					and sle.serial_no != d.serial_no
+				):
+					d.serial_no = sle.serial_no
+					frappe.db.set_value(d.doctype, d.name, "serial_no", sle.serial_no)
+					accepted_serial_nos_updated = True
+					if not update_rejected_serial_nos:
+						break
+				elif (
+					update_rejected_serial_nos
+					and abs(sle.actual_qty) == d.rejected_qty
+					and sle.warehouse == d.rejected_warehouse
+					and sle.serial_no != d.rejected_serial_no
+				):
+					d.rejected_serial_no = sle.serial_no
+					frappe.db.set_value(d.doctype, d.name, "rejected_serial_no", sle.serial_no)
+					update_rejected_serial_nos = False
+					if accepted_serial_nos_updated:
+						break
+
 
 def update_maintenance_status():
-	serial_nos = frappe.db.sql('''select name from `tabSerial No` where (amc_expiry_date<%s or
-		warranty_expiry_date<%s) and maintenance_status not in ('Out of Warranty', 'Out of AMC')''',
-		(nowdate(), nowdate()))
+	serial_nos = frappe.db.sql(
+		"""select name from `tabSerial No` where (amc_expiry_date<%s or
+		warranty_expiry_date<%s) and maintenance_status not in ('Out of Warranty', 'Out of AMC')""",
+		(nowdate(), nowdate()),
+	)
 	for serial_no in serial_nos:
 		doc = frappe.get_doc("Serial No", serial_no[0])
 		doc.set_maintenance_status()
-		frappe.db.set_value('Serial No', doc.name, 'maintenance_status', doc.maintenance_status)
+		frappe.db.set_value("Serial No", doc.name, "maintenance_status", doc.maintenance_status)
+
 
 def get_delivery_note_serial_no(item_code, qty, delivery_note):
-	serial_nos = ''
-	dn_serial_nos = frappe.db.sql_list(""" select name from `tabSerial No`
+	serial_nos = ""
+	dn_serial_nos = frappe.db.sql_list(
+		""" select name from `tabSerial No`
 		where item_code = %(item_code)s and delivery_document_no = %(delivery_note)s
-		and sales_invoice is null limit {0}""".format(cint(qty)), {
-		'item_code': item_code,
-		'delivery_note': delivery_note
-	})
+		and sales_invoice is null limit {0}""".format(
+			cint(qty)
+		),
+		{"item_code": item_code, "delivery_note": delivery_note},
+	)
 
-	if dn_serial_nos and len(dn_serial_nos)>0:
-		serial_nos = '\n'.join(dn_serial_nos)
+	if dn_serial_nos and len(dn_serial_nos) > 0:
+		serial_nos = "\n".join(dn_serial_nos)
 
 	return serial_nos
 
+
 @frappe.whitelist()
-def auto_fetch_serial_number(qty, item_code, warehouse, posting_date=None, batch_nos=None, for_doctype=None):
-	filters = { "item_code": item_code, "warehouse": warehouse }
+def auto_fetch_serial_number(
+	qty: float,
+	item_code: str,
+	warehouse: str,
+	posting_date: Optional[str] = None,
+	batch_nos: Optional[Union[str, List[str]]] = None,
+	for_doctype: Optional[str] = None,
+	exclude_sr_nos: Optional[List[str]] = None,
+) -> List[str]:
+
+	filters = frappe._dict({"item_code": item_code, "warehouse": warehouse})
+
+	if exclude_sr_nos is None:
+		exclude_sr_nos = []
+	else:
+		exclude_sr_nos = safe_json_loads(exclude_sr_nos)
+		exclude_sr_nos = get_serial_nos(clean_serial_no_string("\n".join(exclude_sr_nos)))
 
 	if batch_nos:
-		try:
-			filters["batch_no"] = json.loads(batch_nos) if (type(json.loads(batch_nos)) == list) else [json.loads(batch_nos)]
-		except Exception:
-			filters["batch_no"] = [batch_nos]
+		batch_nos_list = safe_json_loads(batch_nos)
+		if isinstance(batch_nos_list, list):
+			filters.batch_no = batch_nos_list
+		else:
+			filters.batch_no = [batch_nos]
 
 	if posting_date:
-		filters["expiry_date"] = posting_date
+		filters.expiry_date = posting_date
 
 	serial_numbers = []
-	if for_doctype == 'POS Invoice':
-		reserved_sr_nos = get_pos_reserved_serial_nos(filters)
-		serial_numbers = fetch_serial_numbers(filters, qty, do_not_include=reserved_sr_nos)
-	else:
-		serial_numbers = fetch_serial_numbers(filters, qty)
+	if for_doctype == "POS Invoice":
+		exclude_sr_nos.extend(get_pos_reserved_serial_nos(filters))
 
-	return [d.get('name') for d in serial_numbers]
+	serial_numbers = fetch_serial_numbers(filters, qty, do_not_include=exclude_sr_nos)
+
+	return sorted([d.get("name") for d in serial_numbers])
+
+
+def get_delivered_serial_nos(serial_nos):
+	"""
+	Returns serial numbers that delivered from the list of serial numbers
+	"""
+	from frappe.query_builder.functions import Coalesce
+
+	SerialNo = frappe.qb.DocType("Serial No")
+	serial_nos = get_serial_nos(serial_nos)
+	query = (
+		frappe.qb.select(SerialNo.name)
+		.from_(SerialNo)
+		.where((SerialNo.name.isin(serial_nos)) & (Coalesce(SerialNo.delivery_document_type, "") != ""))
+	)
+
+	result = query.run()
+	if result and len(result) > 0:
+		delivered_serial_nos = [row[0] for row in result]
+		return delivered_serial_nos
+
 
 @frappe.whitelist()
 def get_pos_reserved_serial_nos(filters):
 	if isinstance(filters, str):
 		filters = json.loads(filters)
 
-	pos_transacted_sr_nos = frappe.db.sql("""select item.serial_no as serial_no
-		from `tabPOS Invoice` p, `tabPOS Invoice Item` item
-		where p.name = item.parent
-		and p.consolidated_invoice is NULL
-		and p.docstatus = 1
-		and item.docstatus = 1
-		and item.item_code = %(item_code)s
-		and item.warehouse = %(warehouse)s
-		and item.serial_no is NOT NULL and item.serial_no != ''
-		""", filters, as_dict=1)
+	POSInvoice = frappe.qb.DocType("POS Invoice")
+	POSInvoiceItem = frappe.qb.DocType("POS Invoice Item")
+	query = (
+		frappe.qb.from_(POSInvoice)
+		.from_(POSInvoiceItem)
+		.select(POSInvoice.is_return, POSInvoiceItem.serial_no)
+		.where(
+			(POSInvoice.name == POSInvoiceItem.parent)
+			& (POSInvoice.docstatus == 1)
+			& (POSInvoiceItem.docstatus == 1)
+			& (POSInvoiceItem.item_code == filters.get("item_code"))
+			& (POSInvoiceItem.warehouse == filters.get("warehouse"))
+			& (POSInvoiceItem.serial_no.isnotnull())
+			& (POSInvoiceItem.serial_no != "")
+		)
+	)
+
+	pos_transacted_sr_nos = query.run(as_dict=True)
 
 	reserved_sr_nos = []
+	returned_sr_nos = []
 	for d in pos_transacted_sr_nos:
-		reserved_sr_nos += get_serial_nos(d.serial_no)
+		if d.is_return == 0:
+			reserved_sr_nos += get_serial_nos(d.serial_no)
+		elif d.is_return == 1:
+			returned_sr_nos += get_serial_nos(d.serial_no)
+
+	for sr_no in returned_sr_nos:
+		reserved_sr_nos.remove(sr_no)
 
 	return reserved_sr_nos
 
+
 def fetch_serial_numbers(filters, qty, do_not_include=None):
 	if do_not_include is None:
 		do_not_include = []
-	batch_join_selection = ""
-	batch_no_condition = ""
+
 	batch_nos = filters.get("batch_no")
 	expiry_date = filters.get("expiry_date")
+	serial_no = frappe.qb.DocType("Serial No")
+
+	query = (
+		frappe.qb.from_(serial_no)
+		.select(serial_no.name)
+		.where(
+			(serial_no.item_code == filters["item_code"])
+			& (serial_no.warehouse == filters["warehouse"])
+			& (Coalesce(serial_no.sales_invoice, "") == "")
+			& (Coalesce(serial_no.delivery_document_no, "") == "")
+		)
+		.orderby(serial_no.creation)
+		.limit(qty or 1)
+	)
+
+	if do_not_include:
+		query = query.where(serial_no.name.notin(do_not_include))
+
 	if batch_nos:
-		batch_no_condition = """and sr.batch_no in ({}) """.format(', '.join("'%s'" % d for d in batch_nos))
+		query = query.where(serial_no.batch_no.isin(batch_nos))
 
 	if expiry_date:
-		batch_join_selection = "LEFT JOIN `tabBatch` batch on sr.batch_no = batch.name "
-		expiry_date_cond = "AND ifnull(batch.expiry_date, '2500-12-31') >= %(expiry_date)s "
-		batch_no_condition += expiry_date_cond
+		batch = frappe.qb.DocType("Batch")
+		query = (
+			query.left_join(batch)
+			.on(serial_no.batch_no == batch.name)
+			.where(Coalesce(batch.expiry_date, "4000-12-31") >= expiry_date)
+		)
 
-	excluded_sr_nos = ", ".join(["" + frappe.db.escape(sr) + "" for sr in do_not_include]) or "''"
-	serial_numbers = frappe.db.sql("""
-		SELECT sr.name FROM `tabSerial No` sr {batch_join_selection}
-		WHERE
-			sr.name not in ({excluded_sr_nos}) AND
-			sr.item_code = %(item_code)s AND
-			sr.warehouse = %(warehouse)s AND
-			ifnull(sr.sales_invoice,'') = '' AND
-			ifnull(sr.delivery_document_no, '') = ''
-			{batch_no_condition}
-		ORDER BY
-			sr.creation
-		LIMIT
-			{qty}
-		""".format(
-				excluded_sr_nos=excluded_sr_nos,
-				qty=qty or 1,
-				batch_join_selection=batch_join_selection,
-				batch_no_condition=batch_no_condition
-			), filters, as_dict=1)
-
+	serial_numbers = query.run(as_dict=True)
 	return serial_numbers
diff --git a/erpnext/stock/doctype/serial_no/test_serial_no.py b/erpnext/stock/doctype/serial_no/test_serial_no.py
index 057a7d4..68623fb 100644
--- a/erpnext/stock/doctype/serial_no/test_serial_no.py
+++ b/erpnext/stock/doctype/serial_no/test_serial_no.py
@@ -6,25 +6,22 @@
 
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 
 from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
 from erpnext.stock.doctype.item.test_item import make_item
 from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+from erpnext.stock.doctype.serial_no.serial_no import *
 from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
 
 test_dependencies = ["Item"]
-test_records = frappe.get_test_records('Serial No')
-
-from frappe.tests.utils import FrappeTestCase
-
-from erpnext.stock.doctype.serial_no.serial_no import *
+test_records = frappe.get_test_records("Serial No")
 
 
 class TestSerialNo(FrappeTestCase):
-
 	def tearDown(self):
 		frappe.db.rollback()
 
@@ -49,7 +46,9 @@
 		se = make_serialized_item(target_warehouse="_Test Warehouse - _TC")
 		serial_nos = get_serial_nos(se.get("items")[0].serial_no)
 
-		dn = create_delivery_note(item_code="_Test Serialized Item With Series", qty=1, serial_no=serial_nos[0])
+		dn = create_delivery_note(
+			item_code="_Test Serialized Item With Series", qty=1, serial_no=serial_nos[0]
+		)
 
 		serial_no = frappe.get_doc("Serial No", serial_nos[0])
 
@@ -61,8 +60,13 @@
 		self.assertEqual(serial_no.delivery_document_no, dn.name)
 
 		wh = create_warehouse("_Test Warehouse", company="_Test Company 1")
-		pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1, serial_no=serial_nos[0],
-			company="_Test Company 1", warehouse=wh)
+		pr = make_purchase_receipt(
+			item_code="_Test Serialized Item With Series",
+			qty=1,
+			serial_no=serial_nos[0],
+			company="_Test Company 1",
+			warehouse=wh,
+		)
 
 		serial_no.reload()
 
@@ -75,9 +79,9 @@
 
 	def test_inter_company_transfer_intermediate_cancellation(self):
 		"""
-			Receive into and Deliver Serial No from one company.
-			Then Receive into and Deliver from second company.
-			Try to cancel intermediate receipts/deliveries to test if it is blocked.
+		Receive into and Deliver Serial No from one company.
+		Then Receive into and Deliver from second company.
+		Try to cancel intermediate receipts/deliveries to test if it is blocked.
 		"""
 		se = make_serialized_item(target_warehouse="_Test Warehouse - _TC")
 		serial_nos = get_serial_nos(se.get("items")[0].serial_no)
@@ -90,8 +94,9 @@
 		self.assertEqual(sn_doc.warehouse, "_Test Warehouse - _TC")
 		self.assertEqual(sn_doc.purchase_document_no, se.name)
 
-		dn = create_delivery_note(item_code="_Test Serialized Item With Series",
-			qty=1, serial_no=serial_nos[0])
+		dn = create_delivery_note(
+			item_code="_Test Serialized Item With Series", qty=1, serial_no=serial_nos[0]
+		)
 		sn_doc.reload()
 		# check Serial No details after delivery from **first** company
 		self.assertEqual(sn_doc.status, "Delivered")
@@ -105,8 +110,13 @@
 
 		# receive serial no in second company
 		wh = create_warehouse("_Test Warehouse", company="_Test Company 1")
-		pr = make_purchase_receipt(item_code="_Test Serialized Item With Series",
-			qty=1, serial_no=serial_nos[0], company="_Test Company 1", warehouse=wh)
+		pr = make_purchase_receipt(
+			item_code="_Test Serialized Item With Series",
+			qty=1,
+			serial_no=serial_nos[0],
+			company="_Test Company 1",
+			warehouse=wh,
+		)
 		sn_doc.reload()
 
 		self.assertEqual(sn_doc.warehouse, wh)
@@ -115,8 +125,13 @@
 		self.assertRaises(frappe.ValidationError, dn.cancel)
 
 		# deliver from second company
-		dn_2 = create_delivery_note(item_code="_Test Serialized Item With Series",
-			qty=1, serial_no=serial_nos[0], company="_Test Company 1", warehouse=wh)
+		dn_2 = create_delivery_note(
+			item_code="_Test Serialized Item With Series",
+			qty=1,
+			serial_no=serial_nos[0],
+			company="_Test Company 1",
+			warehouse=wh,
+		)
 		sn_doc.reload()
 
 		# check Serial No details after delivery from **second** company
@@ -132,9 +147,9 @@
 
 	def test_inter_company_transfer_fallback_on_cancel(self):
 		"""
-			Test Serial No state changes on cancellation.
-			If Delivery cancelled, it should fall back on last Receipt in the same company.
-			If Receipt is cancelled, it should be Inactive in the same company.
+		Test Serial No state changes on cancellation.
+		If Delivery cancelled, it should fall back on last Receipt in the same company.
+		If Receipt is cancelled, it should be Inactive in the same company.
 		"""
 		# Receipt in **first** company
 		se = make_serialized_item(target_warehouse="_Test Warehouse - _TC")
@@ -142,17 +157,28 @@
 		sn_doc = frappe.get_doc("Serial No", serial_nos[0])
 
 		# Delivery from first company
-		dn = create_delivery_note(item_code="_Test Serialized Item With Series",
-			qty=1, serial_no=serial_nos[0])
+		dn = create_delivery_note(
+			item_code="_Test Serialized Item With Series", qty=1, serial_no=serial_nos[0]
+		)
 
 		# Receipt in **second** company
 		wh = create_warehouse("_Test Warehouse", company="_Test Company 1")
-		pr = make_purchase_receipt(item_code="_Test Serialized Item With Series",
-			qty=1, serial_no=serial_nos[0], company="_Test Company 1", warehouse=wh)
+		pr = make_purchase_receipt(
+			item_code="_Test Serialized Item With Series",
+			qty=1,
+			serial_no=serial_nos[0],
+			company="_Test Company 1",
+			warehouse=wh,
+		)
 
 		# Delivery from second company
-		dn_2 = create_delivery_note(item_code="_Test Serialized Item With Series",
-			qty=1, serial_no=serial_nos[0], company="_Test Company 1", warehouse=wh)
+		dn_2 = create_delivery_note(
+			item_code="_Test Serialized Item With Series",
+			qty=1,
+			serial_no=serial_nos[0],
+			company="_Test Company 1",
+			warehouse=wh,
+		)
 		sn_doc.reload()
 
 		self.assertEqual(sn_doc.status, "Delivered")
@@ -185,12 +211,11 @@
 
 	def test_auto_creation_of_serial_no(self):
 		"""
-			Test if auto created Serial No excludes existing serial numbers
+		Test if auto created Serial No excludes existing serial numbers
 		"""
-		item_code = make_item("_Test Auto Serial Item ", {
-			"has_serial_no": 1,
-			"serial_no_series": "XYZ.###"
-		}).item_code
+		item_code = make_item(
+			"_Test Auto Serial Item ", {"has_serial_no": 1, "serial_no_series": "XYZ.###"}
+		).item_code
 
 		# Reserve XYZ005
 		pr_1 = make_purchase_receipt(item_code=item_code, qty=1, serial_no="XYZ005")
@@ -204,7 +229,7 @@
 	def test_serial_no_sanitation(self):
 		"Test if Serial No input is sanitised before entering the DB."
 		item_code = "_Test Serialized Item"
-		test_records = frappe.get_test_records('Stock Entry')
+		test_records = frappe.get_test_records("Stock Entry")
 
 		se = frappe.copy_doc(test_records[0])
 		se.get("items")[0].item_code = item_code
@@ -218,27 +243,94 @@
 		self.assertEqual(se.get("items")[0].serial_no, "_TS1\n_TS2\n_TS3\n_TS4 - 2021")
 
 	def test_correct_serial_no_incoming_rate(self):
-		""" Check correct consumption rate based on serial no record.
-		"""
+		"""Check correct consumption rate based on serial no record."""
 		item_code = "_Test Serialized Item"
 		warehouse = "_Test Warehouse - _TC"
 		serial_nos = ["LOWVALUATION", "HIGHVALUATION"]
 
-		in1 = make_stock_entry(item_code=item_code, to_warehouse=warehouse, qty=1, rate=42,
-				serial_no=serial_nos[0])
-		in2 = make_stock_entry(item_code=item_code, to_warehouse=warehouse, qty=1, rate=113,
-				serial_no=serial_nos[1])
+		in1 = make_stock_entry(
+			item_code=item_code, to_warehouse=warehouse, qty=1, rate=42, serial_no=serial_nos[0]
+		)
+		in2 = make_stock_entry(
+			item_code=item_code, to_warehouse=warehouse, qty=1, rate=113, serial_no=serial_nos[1]
+		)
 
-		out = create_delivery_note(item_code=item_code, qty=1, serial_no=serial_nos[0], do_not_submit=True)
+		out = create_delivery_note(
+			item_code=item_code, qty=1, serial_no=serial_nos[0], do_not_submit=True
+		)
 
 		# change serial no
 		out.items[0].serial_no = serial_nos[1]
 		out.save()
 		out.submit()
 
-		value_diff = frappe.db.get_value("Stock Ledger Entry",
-				{"voucher_no": out.name, "voucher_type": "Delivery Note"},
-				"stock_value_difference"
-			)
+		value_diff = frappe.db.get_value(
+			"Stock Ledger Entry",
+			{"voucher_no": out.name, "voucher_type": "Delivery Note"},
+			"stock_value_difference",
+		)
 		self.assertEqual(value_diff, -113)
 
+	def test_auto_fetch(self):
+		item_code = make_item(
+			properties={
+				"has_serial_no": 1,
+				"has_batch_no": 1,
+				"create_new_batch": 1,
+				"serial_no_series": "TEST.#######",
+			}
+		).name
+		warehouse = "_Test Warehouse - _TC"
+
+		in1 = make_stock_entry(item_code=item_code, to_warehouse=warehouse, qty=5)
+		in2 = make_stock_entry(item_code=item_code, to_warehouse=warehouse, qty=5)
+
+		in1.reload()
+		in2.reload()
+
+		batch1 = in1.items[0].batch_no
+		batch2 = in2.items[0].batch_no
+
+		batch_wise_serials = {
+			batch1: get_serial_nos(in1.items[0].serial_no),
+			batch2: get_serial_nos(in2.items[0].serial_no),
+		}
+
+		# Test FIFO
+		first_fetch = auto_fetch_serial_number(5, item_code, warehouse)
+		self.assertEqual(first_fetch, batch_wise_serials[batch1])
+
+		# partial FIFO
+		partial_fetch = auto_fetch_serial_number(2, item_code, warehouse)
+		self.assertTrue(
+			set(partial_fetch).issubset(set(first_fetch)),
+			msg=f"{partial_fetch} should be subset of {first_fetch}",
+		)
+
+		# exclusion
+		remaining = auto_fetch_serial_number(
+			3, item_code, warehouse, exclude_sr_nos=json.dumps(partial_fetch)
+		)
+		self.assertEqual(sorted(remaining + partial_fetch), first_fetch)
+
+		# batchwise
+		for batch, expected_serials in batch_wise_serials.items():
+			fetched_sr = auto_fetch_serial_number(5, item_code, warehouse, batch_nos=batch)
+			self.assertEqual(fetched_sr, sorted(expected_serials))
+
+		# non existing warehouse
+		self.assertEqual(auto_fetch_serial_number(10, item_code, warehouse="Nonexisting"), [])
+
+		# multi batch
+		all_serials = [sr for sr_list in batch_wise_serials.values() for sr in sr_list]
+		fetched_serials = auto_fetch_serial_number(
+			10, item_code, warehouse, batch_nos=list(batch_wise_serials.keys())
+		)
+		self.assertEqual(sorted(all_serials), fetched_serials)
+
+		# expiry date
+		frappe.db.set_value("Batch", batch1, "expiry_date", "1980-01-01")
+		non_expired_serials = auto_fetch_serial_number(
+			5, item_code, warehouse, posting_date="2021-01-01", batch_nos=batch1
+		)
+		self.assertEqual(non_expired_serials, [])
diff --git a/erpnext/stock/doctype/shipment/shipment.py b/erpnext/stock/doctype/shipment/shipment.py
index 666de57..42a67f4 100644
--- a/erpnext/stock/doctype/shipment/shipment.py
+++ b/erpnext/stock/doctype/shipment/shipment.py
@@ -17,22 +17,22 @@
 		self.validate_pickup_time()
 		self.set_value_of_goods()
 		if self.docstatus == 0:
-			self.status = 'Draft'
+			self.status = "Draft"
 
 	def on_submit(self):
 		if not self.shipment_parcel:
-			frappe.throw(_('Please enter Shipment Parcel information'))
+			frappe.throw(_("Please enter Shipment Parcel information"))
 		if self.value_of_goods == 0:
-			frappe.throw(_('Value of goods cannot be 0'))
-		self.db_set('status', 'Submitted')
+			frappe.throw(_("Value of goods cannot be 0"))
+		self.db_set("status", "Submitted")
 
 	def on_cancel(self):
-		self.db_set('status', 'Cancelled')
+		self.db_set("status", "Cancelled")
 
 	def validate_weight(self):
 		for parcel in self.shipment_parcel:
 			if flt(parcel.weight) <= 0:
-				frappe.throw(_('Parcel weight cannot be 0'))
+				frappe.throw(_("Parcel weight cannot be 0"))
 
 	def validate_pickup_time(self):
 		if self.pickup_from and self.pickup_to and get_time(self.pickup_to) < get_time(self.pickup_from):
@@ -44,26 +44,34 @@
 			value_of_goods += flt(entry.get("grand_total"))
 		self.value_of_goods = value_of_goods if value_of_goods else self.value_of_goods
 
+
 @frappe.whitelist()
 def get_address_name(ref_doctype, docname):
 	# Return address name
 	return get_party_shipping_address(ref_doctype, docname)
 
+
 @frappe.whitelist()
 def get_contact_name(ref_doctype, docname):
 	# Return address name
 	return get_default_contact(ref_doctype, docname)
 
+
 @frappe.whitelist()
 def get_company_contact(user):
-	contact = frappe.db.get_value('User', user, [
-		'first_name',
-		'last_name',
-		'email',
-		'phone',
-		'mobile_no',
-		'gender',
-	], as_dict=1)
+	contact = frappe.db.get_value(
+		"User",
+		user,
+		[
+			"first_name",
+			"last_name",
+			"email",
+			"phone",
+			"mobile_no",
+			"gender",
+		],
+		as_dict=1,
+	)
 	if not contact.phone:
 		contact.phone = contact.mobile_no
 	return contact
diff --git a/erpnext/stock/doctype/shipment/test_shipment.py b/erpnext/stock/doctype/shipment/test_shipment.py
index 7f418ca..726dff4 100644
--- a/erpnext/stock/doctype/shipment/test_shipment.py
+++ b/erpnext/stock/doctype/shipment/test_shipment.py
@@ -13,13 +13,14 @@
 	def test_shipment_from_delivery_note(self):
 		delivery_note = create_test_delivery_note()
 		delivery_note.submit()
-		shipment = create_test_shipment([ delivery_note ])
+		shipment = create_test_shipment([delivery_note])
 		shipment.submit()
 		second_shipment = make_shipment(delivery_note.name)
 		self.assertEqual(second_shipment.value_of_goods, delivery_note.grand_total)
 		self.assertEqual(len(second_shipment.shipment_delivery_note), 1)
 		self.assertEqual(second_shipment.shipment_delivery_note[0].delivery_note, delivery_note.name)
 
+
 def create_test_delivery_note():
 	company = get_shipment_company()
 	customer = get_shipment_customer()
@@ -30,25 +31,26 @@
 	delivery_note = frappe.new_doc("Delivery Note")
 	delivery_note.company = company.name
 	delivery_note.posting_date = posting_date.strftime("%Y-%m-%d")
-	delivery_note.posting_time = '10:00'
+	delivery_note.posting_time = "10:00"
 	delivery_note.customer = customer.name
-	delivery_note.append('items',
+	delivery_note.append(
+		"items",
 		{
 			"item_code": item.name,
 			"item_name": item.item_name,
-			"description": 'Test delivery note for shipment',
+			"description": "Test delivery note for shipment",
 			"qty": 5,
-			"uom": 'Nos',
-			"warehouse": 'Stores - _TC',
+			"uom": "Nos",
+			"warehouse": "Stores - _TC",
 			"rate": item.standard_rate,
-			"cost_center": 'Main - _TC'
-		}
+			"cost_center": "Main - _TC",
+		},
 	)
 	delivery_note.insert()
 	return delivery_note
 
 
-def create_test_shipment(delivery_notes = None):
+def create_test_shipment(delivery_notes=None):
 	company = get_shipment_company()
 	company_address = get_shipment_company_address(company.name)
 	customer = get_shipment_customer()
@@ -57,45 +59,35 @@
 	posting_date = date.today() + timedelta(days=5)
 
 	shipment = frappe.new_doc("Shipment")
-	shipment.pickup_from_type = 'Company'
+	shipment.pickup_from_type = "Company"
 	shipment.pickup_company = company.name
 	shipment.pickup_address_name = company_address.name
-	shipment.delivery_to_type = 'Customer'
+	shipment.delivery_to_type = "Customer"
 	shipment.delivery_customer = customer.name
 	shipment.delivery_address_name = customer_address.name
 	shipment.delivery_contact_name = customer_contact.name
-	shipment.pallets = 'No'
-	shipment.shipment_type = 'Goods'
+	shipment.pallets = "No"
+	shipment.shipment_type = "Goods"
 	shipment.value_of_goods = 1000
-	shipment.pickup_type = 'Pickup'
+	shipment.pickup_type = "Pickup"
 	shipment.pickup_date = posting_date.strftime("%Y-%m-%d")
-	shipment.pickup_from = '09:00'
-	shipment.pickup_to = '17:00'
-	shipment.description_of_content = 'unit test entry'
+	shipment.pickup_from = "09:00"
+	shipment.pickup_to = "17:00"
+	shipment.description_of_content = "unit test entry"
 	for delivery_note in delivery_notes:
-		shipment.append('shipment_delivery_note',
-			{
-				"delivery_note": delivery_note.name
-			}
-		)
-	shipment.append('shipment_parcel',
-		{
-			"length": 5,
-			"width": 5,
-			"height": 5,
-			"weight": 5,
-			"count": 5
-		}
+		shipment.append("shipment_delivery_note", {"delivery_note": delivery_note.name})
+	shipment.append(
+		"shipment_parcel", {"length": 5, "width": 5, "height": 5, "weight": 5, "count": 5}
 	)
 	shipment.insert()
 	return shipment
 
 
 def get_shipment_customer_contact(customer_name):
-	contact_fname = 'Customer Shipment'
-	contact_lname = 'Testing'
-	customer_name = contact_fname + ' ' + contact_lname
-	contacts = frappe.get_all("Contact", fields=["name"], filters = {"name": customer_name})
+	contact_fname = "Customer Shipment"
+	contact_lname = "Testing"
+	customer_name = contact_fname + " " + contact_lname
+	contacts = frappe.get_all("Contact", fields=["name"], filters={"name": customer_name})
 	if len(contacts):
 		return contacts[0]
 	else:
@@ -103,103 +95,105 @@
 
 
 def get_shipment_customer_address(customer_name):
-	address_title = customer_name + ' address 123'
-	customer_address = frappe.get_all("Address", fields=["name"], filters = {"address_title": address_title})
+	address_title = customer_name + " address 123"
+	customer_address = frappe.get_all(
+		"Address", fields=["name"], filters={"address_title": address_title}
+	)
 	if len(customer_address):
 		return customer_address[0]
 	else:
 		return create_shipment_address(address_title, customer_name, 81929)
 
+
 def get_shipment_customer():
-	customer_name = 'Shipment Customer'
-	customer = frappe.get_all("Customer", fields=["name"], filters = {"name": customer_name})
+	customer_name = "Shipment Customer"
+	customer = frappe.get_all("Customer", fields=["name"], filters={"name": customer_name})
 	if len(customer):
 		return customer[0]
 	else:
 		return create_shipment_customer(customer_name)
 
+
 def get_shipment_company_address(company_name):
-	address_title = company_name + ' address 123'
-	addresses = frappe.get_all("Address", fields=["name"], filters = {"address_title": address_title})
+	address_title = company_name + " address 123"
+	addresses = frappe.get_all("Address", fields=["name"], filters={"address_title": address_title})
 	if len(addresses):
 		return addresses[0]
 	else:
 		return create_shipment_address(address_title, company_name, 80331)
 
+
 def get_shipment_company():
 	return frappe.get_doc("Company", "_Test Company")
 
+
 def get_shipment_item(company_name):
-	item_name = 'Testing Shipment item'
-	items = frappe.get_all("Item",
+	item_name = "Testing Shipment item"
+	items = frappe.get_all(
+		"Item",
 		fields=["name", "item_name", "item_code", "standard_rate"],
-		filters = {"item_name": item_name}
+		filters={"item_name": item_name},
 	)
 	if len(items):
 		return items[0]
 	else:
 		return create_shipment_item(item_name, company_name)
 
+
 def create_shipment_address(address_title, company_name, postal_code):
 	address = frappe.new_doc("Address")
 	address.address_title = address_title
-	address.address_type = 'Shipping'
-	address.address_line1 = company_name + ' address line 1'
-	address.city = 'Random City'
+	address.address_type = "Shipping"
+	address.address_line1 = company_name + " address line 1"
+	address.city = "Random City"
 	address.postal_code = postal_code
-	address.country = 'Germany'
+	address.country = "Germany"
 	address.insert()
 	return address
 
 
 def create_customer_contact(fname, lname):
 	customer = frappe.new_doc("Contact")
-	customer.customer_name = fname + ' ' + lname
+	customer.customer_name = fname + " " + lname
 	customer.first_name = fname
 	customer.last_name = lname
 	customer.is_primary_contact = 1
 	customer.is_billing_contact = 1
-	customer.append('email_ids',
-		{
-			'email_id': 'randomme@email.com',
-			'is_primary': 1
-		}
+	customer.append("email_ids", {"email_id": "randomme@email.com", "is_primary": 1})
+	customer.append(
+		"phone_nos", {"phone": "123123123", "is_primary_phone": 1, "is_primary_mobile_no": 1}
 	)
-	customer.append('phone_nos',
-		{
-			'phone': '123123123',
-			'is_primary_phone': 1,
-			'is_primary_mobile_no': 1
-		}
-	)
-	customer.status = 'Passive'
+	customer.status = "Passive"
 	customer.insert()
 	return customer
 
+
 def create_shipment_customer(customer_name):
 	customer = frappe.new_doc("Customer")
 	customer.customer_name = customer_name
-	customer.customer_type = 'Company'
-	customer.customer_group = 'All Customer Groups'
-	customer.territory = 'All Territories'
+	customer.customer_type = "Company"
+	customer.customer_group = "All Customer Groups"
+	customer.territory = "All Territories"
 	customer.insert()
 	return customer
 
+
 def create_material_receipt(item, company):
 	posting_date = date.today()
 	stock = frappe.new_doc("Stock Entry")
 	stock.company = company
-	stock.stock_entry_type = 'Material Receipt'
+	stock.stock_entry_type = "Material Receipt"
 	stock.posting_date = posting_date.strftime("%Y-%m-%d")
-	stock.append('items',
+	stock.append(
+		"items",
 		{
-			"t_warehouse": 'Stores - _TC',
+			"t_warehouse": "Stores - _TC",
 			"item_code": item.name,
 			"qty": 5,
-			"uom": 'Nos',
+			"uom": "Nos",
 			"basic_rate": item.standard_rate,
-			"cost_center": 'Main - _TC'
-		}
+			"cost_center": "Main - _TC",
+		},
 	)
 	stock.insert()
 	stock.submit()
@@ -209,14 +203,9 @@
 	item = frappe.new_doc("Item")
 	item.item_name = item_name
 	item.item_code = item_name
-	item.item_group = 'All Item Groups'
-	item.stock_uom = 'Nos'
+	item.item_group = "All Item Groups"
+	item.stock_uom = "Nos"
 	item.standard_rate = 50
-	item.append('item_defaults',
-		{
-			"company": company_name,
-			"default_warehouse": 'Stores - _TC'
-		}
-	)
+	item.append("item_defaults", {"company": company_name, "default_warehouse": "Stores - _TC"})
 	item.insert()
 	return item
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 324ca7a..540ad18 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -214,7 +214,7 @@
 
 		if (frm.doc.docstatus === 1) {
 			if (frm.doc.add_to_transit && frm.doc.purpose=='Material Transfer' && frm.doc.per_transferred < 100) {
-				frm.add_custom_button('End Transit', function() {
+				frm.add_custom_button(__('End Transit'), function() {
 					frappe.model.open_mapped_doc({
 						method: "erpnext.stock.doctype.stock_entry.stock_entry.make_stock_in_entry",
 						frm: frm
@@ -470,7 +470,9 @@
 				},
 				callback: function(r) {
 					if (!r.exc) {
-						$.extend(child, r.message);
+						["actual_qty", "basic_rate"].forEach((field) => {
+							frappe.model.set_value(cdt, cdn, field, (r.message[field] || 0.0));
+						});
 						frm.events.calculate_basic_amount(frm, child);
 					}
 				}
@@ -633,7 +635,7 @@
 		// set allow_zero_valuation_rate to 0 if s_warehouse is selected.
 		let item = frappe.get_doc(cdt, cdn);
 		if (item.s_warehouse) {
-			item.allow_zero_valuation_rate = 0;
+			frappe.model.set_value(cdt, cdn, "allow_zero_valuation_rate", 0);
 		}
 	},
 
@@ -646,21 +648,6 @@
 		frm.events.calculate_basic_amount(frm, item);
 	},
 
-	barcode: function(doc, cdt, cdn) {
-		var d = locals[cdt][cdn];
-		if (d.barcode) {
-			frappe.call({
-				method: "erpnext.stock.get_item_details.get_item_code",
-				args: {"barcode": d.barcode },
-				callback: function(r) {
-					if (!r.exe){
-						frappe.model.set_value(cdt, cdn, "item_code", r.message);
-					}
-				}
-			});
-		}
-	},
-
 	uom: function(doc, cdt, cdn) {
 		var d = locals[cdt][cdn];
 		if(d.uom && d.item_code){
@@ -793,7 +780,7 @@
 			return {
 				"filters": {
 					"docstatus": 1,
-					"is_subcontracted": "Yes",
+					"is_subcontracted": 1,
 					"company": me.frm.doc.company
 				}
 			};
@@ -845,8 +832,8 @@
 	}
 
 	scan_barcode() {
-		let transaction_controller= new erpnext.TransactionController({frm:this.frm});
-		transaction_controller.scan_barcode();
+		const barcode_scanner = new erpnext.utils.BarcodeScanner({frm:this.frm});
+		barcode_scanner.process_scan();
 	}
 
 	on_submit() {
@@ -1072,8 +1059,8 @@
 
 function check_should_not_attach_bom_items(bom_no) {
   return (
-    bom_no === undefined ||
-    (erpnext.stock.bom && erpnext.stock.bom.name === bom_no)
+	bom_no === undefined ||
+	(erpnext.stock.bom && erpnext.stock.bom.name === bom_no)
   );
 }
 
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json
index c38dfaa..f56e059 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.json
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -46,9 +46,9 @@
   "items",
   "get_stock_and_rate",
   "section_break_19",
-  "total_incoming_value",
-  "column_break_22",
   "total_outgoing_value",
+  "column_break_22",
+  "total_incoming_value",
   "value_difference",
   "additional_costs_section",
   "additional_costs",
@@ -374,7 +374,7 @@
   {
    "fieldname": "total_incoming_value",
    "fieldtype": "Currency",
-   "label": "Total Incoming Value",
+   "label": "Total Incoming Value (Receipt)",
    "options": "Company:company:default_currency",
    "print_hide": 1,
    "read_only": 1
@@ -386,7 +386,7 @@
   {
    "fieldname": "total_outgoing_value",
    "fieldtype": "Currency",
-   "label": "Total Outgoing Value",
+   "label": "Total Outgoing Value (Consumption)",
    "options": "Company:company:default_currency",
    "print_hide": 1,
    "read_only": 1
@@ -394,7 +394,7 @@
   {
    "fieldname": "value_difference",
    "fieldtype": "Currency",
-   "label": "Total Value Difference (Out - In)",
+   "label": "Total Value Difference (Incoming - Outgoing)",
    "options": "Company:company:default_currency",
    "print_hide_if_no_value": 1,
    "read_only": 1
@@ -619,7 +619,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-02-07 12:55:14.614077",
+ "modified": "2022-05-02 05:21:39.060501",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 99cf4de..e902d1e 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -38,20 +38,28 @@
 
 class FinishedGoodError(frappe.ValidationError):
 	pass
+
+
 class IncorrectValuationRateError(frappe.ValidationError):
 	pass
+
+
 class DuplicateEntryForWorkOrderError(frappe.ValidationError):
 	pass
+
+
 class OperationsNotCompleteError(frappe.ValidationError):
 	pass
+
+
 class MaxSampleAlreadyRetainedError(frappe.ValidationError):
 	pass
 
+
 from erpnext.controllers.stock_controller import StockController
 
-form_grid_templates = {
-	"items": "templates/form_grid/stock_entry_grid.html"
-}
+form_grid_templates = {"items": "templates/form_grid/stock_entry_grid.html"}
+
 
 class StockEntry(StockController):
 	def get_feed(self):
@@ -63,16 +71,18 @@
 
 	def before_validate(self):
 		from erpnext.stock.doctype.putaway_rule.putaway_rule import apply_putaway_rule
-		apply_rule = self.apply_putaway_rule and (self.purpose in ["Material Transfer", "Material Receipt"])
+
+		apply_rule = self.apply_putaway_rule and (
+			self.purpose in ["Material Transfer", "Material Receipt"]
+		)
 
 		if self.get("items") and apply_rule:
-			apply_putaway_rule(self.doctype, self.get("items"), self.company,
-				purpose=self.purpose)
+			apply_putaway_rule(self.doctype, self.get("items"), self.company, purpose=self.purpose)
 
 	def validate(self):
 		self.pro_doc = frappe._dict()
 		if self.work_order:
-			self.pro_doc = frappe.get_doc('Work Order', self.work_order)
+			self.pro_doc = frappe.get_doc("Work Order", self.work_order)
 
 		self.validate_posting_time()
 		self.validate_purpose()
@@ -103,10 +113,10 @@
 		if not self.from_bom:
 			self.fg_completed_qty = 0.0
 
-		if self._action == 'submit':
-			self.make_batches('t_warehouse')
+		if self._action == "submit":
+			self.make_batches("t_warehouse")
 		else:
-			set_batch_nos(self, 's_warehouse')
+			set_batch_nos(self, "s_warehouse")
 
 		self.validate_serialized_batch()
 		self.set_actual_qty()
@@ -138,10 +148,10 @@
 		if self.work_order and self.purpose == "Manufacture":
 			self.update_so_in_serial_number()
 
-		if self.purpose == 'Material Transfer' and self.add_to_transit:
-			self.set_material_request_transfer_status('In Transit')
-		if self.purpose == 'Material Transfer' and self.outgoing_stock_entry:
-			self.set_material_request_transfer_status('Completed')
+		if self.purpose == "Material Transfer" and self.add_to_transit:
+			self.set_material_request_transfer_status("In Transit")
+		if self.purpose == "Material Transfer" and self.outgoing_stock_entry:
+			self.set_material_request_transfer_status("Completed")
 
 	def on_cancel(self):
 		self.update_purchase_order_supplied_items()
@@ -152,7 +162,7 @@
 		self.update_work_order()
 		self.update_stock_ledger()
 
-		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
+		self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Repost Item Valuation")
 
 		self.make_gl_entries_on_cancel()
 		self.repost_future_sle_and_gle()
@@ -162,15 +172,16 @@
 		self.delete_auto_created_batches()
 		self.delete_linked_stock_entry()
 
-		if self.purpose == 'Material Transfer' and self.add_to_transit:
-			self.set_material_request_transfer_status('Not Started')
-		if self.purpose == 'Material Transfer' and self.outgoing_stock_entry:
-			self.set_material_request_transfer_status('In Transit')
+		if self.purpose == "Material Transfer" and self.add_to_transit:
+			self.set_material_request_transfer_status("Not Started")
+		if self.purpose == "Material Transfer" and self.outgoing_stock_entry:
+			self.set_material_request_transfer_status("In Transit")
 
 	def set_job_card_data(self):
 		if self.job_card and not self.work_order:
-			data = frappe.db.get_value('Job Card',
-				self.job_card, ['for_quantity', 'work_order', 'bom_no'], as_dict=1)
+			data = frappe.db.get_value(
+				"Job Card", self.job_card, ["for_quantity", "work_order", "bom_no"], as_dict=1
+			)
 			self.fg_completed_qty = data.for_quantity
 			self.work_order = data.work_order
 			self.from_bom = 1
@@ -178,107 +189,158 @@
 
 	def validate_work_order_status(self):
 		pro_doc = frappe.get_doc("Work Order", self.work_order)
-		if pro_doc.status == 'Completed':
+		if pro_doc.status == "Completed":
 			frappe.throw(_("Cannot cancel transaction for Completed Work Order."))
 
 	def validate_purpose(self):
-		valid_purposes = ["Material Issue", "Material Receipt", "Material Transfer",
-			"Material Transfer for Manufacture", "Manufacture", "Repack", "Send to Subcontractor",
-			"Material Consumption for Manufacture"]
+		valid_purposes = [
+			"Material Issue",
+			"Material Receipt",
+			"Material Transfer",
+			"Material Transfer for Manufacture",
+			"Manufacture",
+			"Repack",
+			"Send to Subcontractor",
+			"Material Consumption for Manufacture",
+		]
 
 		if self.purpose not in valid_purposes:
 			frappe.throw(_("Purpose must be one of {0}").format(comma_or(valid_purposes)))
 
-		if self.job_card and self.purpose not in ['Material Transfer for Manufacture', 'Repack']:
-			frappe.throw(_("For job card {0}, you can only make the 'Material Transfer for Manufacture' type stock entry")
-				.format(self.job_card))
+		if self.job_card and self.purpose not in ["Material Transfer for Manufacture", "Repack"]:
+			frappe.throw(
+				_(
+					"For job card {0}, you can only make the 'Material Transfer for Manufacture' type stock entry"
+				).format(self.job_card)
+			)
 
 	def delete_linked_stock_entry(self):
 		if self.purpose == "Send to Warehouse":
-			for d in frappe.get_all("Stock Entry", filters={"docstatus": 0,
-				"outgoing_stock_entry": self.name, "purpose": "Receive at Warehouse"}):
+			for d in frappe.get_all(
+				"Stock Entry",
+				filters={"docstatus": 0, "outgoing_stock_entry": self.name, "purpose": "Receive at Warehouse"},
+			):
 				frappe.delete_doc("Stock Entry", d.name)
 
 	def set_transfer_qty(self):
 		for item in self.get("items"):
 			if not flt(item.qty):
-				frappe.throw(_("Row {0}: Qty is mandatory").format(item.idx))
+				frappe.throw(_("Row {0}: Qty is mandatory").format(item.idx), title=_("Zero quantity"))
 			if not flt(item.conversion_factor):
 				frappe.throw(_("Row {0}: UOM Conversion Factor is mandatory").format(item.idx))
-			item.transfer_qty = flt(flt(item.qty) * flt(item.conversion_factor),
-				self.precision("transfer_qty", item))
+			item.transfer_qty = flt(
+				flt(item.qty) * flt(item.conversion_factor), self.precision("transfer_qty", item)
+			)
+			if not flt(item.transfer_qty):
+				frappe.throw(
+					_("Row {0}: Qty in Stock UOM can not be zero.").format(item.idx), title=_("Zero quantity")
+				)
 
 	def update_cost_in_project(self):
-		if (self.work_order and not frappe.db.get_value("Work Order",
-			self.work_order, "update_consumed_material_cost_in_project")):
+		if self.work_order and not frappe.db.get_value(
+			"Work Order", self.work_order, "update_consumed_material_cost_in_project"
+		):
 			return
 
 		if self.project:
-			amount = frappe.db.sql(""" select ifnull(sum(sed.amount), 0)
+			amount = frappe.db.sql(
+				""" select ifnull(sum(sed.amount), 0)
 				from
 					`tabStock Entry` se, `tabStock Entry Detail` sed
 				where
 					se.docstatus = 1 and se.project = %s and sed.parent = se.name
-					and (sed.t_warehouse is null or sed.t_warehouse = '')""", self.project, as_list=1)
+					and (sed.t_warehouse is null or sed.t_warehouse = '')""",
+				self.project,
+				as_list=1,
+			)
 
 			amount = amount[0][0] if amount else 0
-			additional_costs = frappe.db.sql(""" select ifnull(sum(sed.base_amount), 0)
+			additional_costs = frappe.db.sql(
+				""" select ifnull(sum(sed.base_amount), 0)
 				from
 					`tabStock Entry` se, `tabLanded Cost Taxes and Charges` sed
 				where
 					se.docstatus = 1 and se.project = %s and sed.parent = se.name
-					and se.purpose = 'Manufacture'""", self.project, as_list=1)
+					and se.purpose = 'Manufacture'""",
+				self.project,
+				as_list=1,
+			)
 
 			additional_cost_amt = additional_costs[0][0] if additional_costs else 0
 
 			amount += additional_cost_amt
-			frappe.db.set_value('Project', self.project, 'total_consumed_material_cost', amount)
+			frappe.db.set_value("Project", self.project, "total_consumed_material_cost", amount)
 
 	def validate_item(self):
 		stock_items = self.get_stock_items()
 		serialized_items = self.get_serialized_items()
 		for item in self.get("items"):
 			if flt(item.qty) and flt(item.qty) < 0:
-				frappe.throw(_("Row {0}: The item {1}, quantity must be positive number")
-					.format(item.idx, frappe.bold(item.item_code)))
+				frappe.throw(
+					_("Row {0}: The item {1}, quantity must be positive number").format(
+						item.idx, frappe.bold(item.item_code)
+					)
+				)
 
 			if item.item_code not in stock_items:
 				frappe.throw(_("{0} is not a stock Item").format(item.item_code))
 
-			item_details = self.get_item_details(frappe._dict(
-				{"item_code": item.item_code, "company": self.company,
-				"project": self.project, "uom": item.uom, 's_warehouse': item.s_warehouse}),
-				for_update=True)
+			item_details = self.get_item_details(
+				frappe._dict(
+					{
+						"item_code": item.item_code,
+						"company": self.company,
+						"project": self.project,
+						"uom": item.uom,
+						"s_warehouse": item.s_warehouse,
+					}
+				),
+				for_update=True,
+			)
 
-			for f in ("uom", "stock_uom", "description", "item_name", "expense_account",
-				"cost_center", "conversion_factor"):
-					if f == "stock_uom" or not item.get(f):
-						item.set(f, item_details.get(f))
-					if f == 'conversion_factor' and item.uom == item_details.get('stock_uom'):
-						item.set(f, item_details.get(f))
+			reset_fields = ("stock_uom", "item_name")
+			for field in reset_fields:
+				item.set(field, item_details.get(field))
+
+			update_fields = ("uom", "description", "expense_account", "cost_center", "conversion_factor")
+
+			for field in update_fields:
+				if not item.get(field):
+					item.set(field, item_details.get(field))
+				if field == "conversion_factor" and item.uom == item_details.get("stock_uom"):
+					item.set(field, item_details.get(field))
 
 			if not item.transfer_qty and item.qty:
-				item.transfer_qty = flt(flt(item.qty) * flt(item.conversion_factor),
-				self.precision("transfer_qty", item))
+				item.transfer_qty = flt(
+					flt(item.qty) * flt(item.conversion_factor), self.precision("transfer_qty", item)
+				)
 
-			if (self.purpose in ("Material Transfer", "Material Transfer for Manufacture")
+			if (
+				self.purpose in ("Material Transfer", "Material Transfer for Manufacture")
 				and not item.serial_no
-				and item.item_code in serialized_items):
-				frappe.throw(_("Row #{0}: Please specify Serial No for Item {1}").format(item.idx, item.item_code),
-					frappe.MandatoryError)
+				and item.item_code in serialized_items
+			):
+				frappe.throw(
+					_("Row #{0}: Please specify Serial No for Item {1}").format(item.idx, item.item_code),
+					frappe.MandatoryError,
+				)
 
 	def validate_qty(self):
 		manufacture_purpose = ["Manufacture", "Material Consumption for Manufacture"]
 
 		if self.purpose in manufacture_purpose and self.work_order:
-			if not frappe.get_value('Work Order', self.work_order, 'skip_transfer'):
+			if not frappe.get_value("Work Order", self.work_order, "skip_transfer"):
 				item_code = []
 				for item in self.items:
-					if cstr(item.t_warehouse) == '':
-						req_items = frappe.get_all('Work Order Item',
-										filters={'parent': self.work_order, 'item_code': item.item_code}, fields=["item_code"])
+					if cstr(item.t_warehouse) == "":
+						req_items = frappe.get_all(
+							"Work Order Item",
+							filters={"parent": self.work_order, "item_code": item.item_code},
+							fields=["item_code"],
+						)
 
-						transferred_materials = frappe.db.sql("""
+						transferred_materials = frappe.db.sql(
+							"""
 									select
 										sum(qty) as qty
 									from `tabStock Entry` se,`tabStock Entry Detail` sed
@@ -286,7 +348,10 @@
 										se.name = sed.parent and se.docstatus=1 and
 										(se.purpose='Material Transfer for Manufacture' or se.purpose='Manufacture')
 										and sed.item_code=%s and se.work_order= %s and ifnull(sed.t_warehouse, '') != ''
-								""", (item.item_code, self.work_order), as_dict=1)
+								""",
+							(item.item_code, self.work_order),
+							as_dict=1,
+						)
 
 						stock_qty = flt(item.qty)
 						trans_qty = flt(transferred_materials[0].qty)
@@ -304,8 +369,11 @@
 		for item_code, qty_list in item_wise_qty.items():
 			total = flt(sum(qty_list), frappe.get_precision("Stock Entry Detail", "qty"))
 			if self.fg_completed_qty != total:
-				frappe.throw(_("The finished product {0} quantity {1} and For Quantity {2} cannot be different")
-					.format(frappe.bold(item_code), frappe.bold(total), frappe.bold(self.fg_completed_qty)))
+				frappe.throw(
+					_("The finished product {0} quantity {1} and For Quantity {2} cannot be different").format(
+						frappe.bold(item_code), frappe.bold(total), frappe.bold(self.fg_completed_qty)
+					)
+				)
 
 	def validate_difference_account(self):
 		if not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
@@ -313,33 +381,53 @@
 
 		for d in self.get("items"):
 			if not d.expense_account:
-				frappe.throw(_("Please enter <b>Difference Account</b> or set default <b>Stock Adjustment Account</b> for company {0}")
-					.format(frappe.bold(self.company)))
+				frappe.throw(
+					_(
+						"Please enter <b>Difference Account</b> or set default <b>Stock Adjustment Account</b> for company {0}"
+					).format(frappe.bold(self.company))
+				)
 
-			elif self.is_opening == "Yes" and frappe.db.get_value("Account", d.expense_account, "report_type") == "Profit and Loss":
-				frappe.throw(_("Difference Account must be a Asset/Liability type account, since this Stock Entry is an Opening Entry"), OpeningEntryAccountError)
+			elif (
+				self.is_opening == "Yes"
+				and frappe.db.get_value("Account", d.expense_account, "report_type") == "Profit and Loss"
+			):
+				frappe.throw(
+					_(
+						"Difference Account must be a Asset/Liability type account, since this Stock Entry is an Opening Entry"
+					),
+					OpeningEntryAccountError,
+				)
 
 	def validate_warehouse(self):
 		"""perform various (sometimes conditional) validations on warehouse"""
 
-		source_mandatory = ["Material Issue", "Material Transfer", "Send to Subcontractor", "Material Transfer for Manufacture",
-			"Material Consumption for Manufacture"]
+		source_mandatory = [
+			"Material Issue",
+			"Material Transfer",
+			"Send to Subcontractor",
+			"Material Transfer for Manufacture",
+			"Material Consumption for Manufacture",
+		]
 
-		target_mandatory = ["Material Receipt", "Material Transfer", "Send to Subcontractor",
-			"Material Transfer for Manufacture"]
+		target_mandatory = [
+			"Material Receipt",
+			"Material Transfer",
+			"Send to Subcontractor",
+			"Material Transfer for Manufacture",
+		]
 
 		validate_for_manufacture = any([d.bom_no for d in self.get("items")])
 
 		if self.purpose in source_mandatory and self.purpose not in target_mandatory:
 			self.to_warehouse = None
-			for d in self.get('items'):
+			for d in self.get("items"):
 				d.t_warehouse = None
 		elif self.purpose in target_mandatory and self.purpose not in source_mandatory:
 			self.from_warehouse = None
-			for d in self.get('items'):
+			for d in self.get("items"):
 				d.s_warehouse = None
 
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if not d.s_warehouse and not d.t_warehouse:
 				d.s_warehouse = self.from_warehouse
 				d.t_warehouse = self.to_warehouse
@@ -356,7 +444,6 @@
 				else:
 					frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx))
 
-
 			if self.purpose == "Manufacture":
 				if validate_for_manufacture:
 					if d.is_finished_item or d.is_scrap_item or d.is_process_loss:
@@ -368,18 +455,26 @@
 						if not d.s_warehouse:
 							frappe.throw(_("Source warehouse is mandatory for row {0}").format(d.idx))
 
-			if cstr(d.s_warehouse) == cstr(d.t_warehouse) and not self.purpose == "Material Transfer for Manufacture":
+			if (
+				cstr(d.s_warehouse) == cstr(d.t_warehouse)
+				and not self.purpose == "Material Transfer for Manufacture"
+			):
 				frappe.throw(_("Source and target warehouse cannot be same for row {0}").format(d.idx))
 
 			if not (d.s_warehouse or d.t_warehouse):
 				frappe.throw(_("Atleast one warehouse is mandatory"))
 
 	def validate_work_order(self):
-		if self.purpose in ("Manufacture", "Material Transfer for Manufacture", "Material Consumption for Manufacture"):
+		if self.purpose in (
+			"Manufacture",
+			"Material Transfer for Manufacture",
+			"Material Consumption for Manufacture",
+		):
 			# check if work order is entered
 
-			if (self.purpose=="Manufacture" or self.purpose=="Material Consumption for Manufacture") \
-					and self.work_order:
+			if (
+				self.purpose == "Manufacture" or self.purpose == "Material Consumption for Manufacture"
+			) and self.work_order:
 				if not self.fg_completed_qty:
 					frappe.throw(_("For Quantity (Manufactured Qty) is mandatory"))
 				self.check_if_operations_completed()
@@ -390,40 +485,66 @@
 	def check_if_operations_completed(self):
 		"""Check if Time Sheets are completed against before manufacturing to capture operating costs."""
 		prod_order = frappe.get_doc("Work Order", self.work_order)
-		allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings",
-			"overproduction_percentage_for_work_order"))
+		allowance_percentage = flt(
+			frappe.db.get_single_value("Manufacturing Settings", "overproduction_percentage_for_work_order")
+		)
 
 		for d in prod_order.get("operations"):
 			total_completed_qty = flt(self.fg_completed_qty) + flt(prod_order.produced_qty)
-			completed_qty = d.completed_qty + (allowance_percentage/100 * d.completed_qty)
+			completed_qty = d.completed_qty + (allowance_percentage / 100 * d.completed_qty)
 			if total_completed_qty > flt(completed_qty):
-				job_card = frappe.db.get_value('Job Card', {'operation_id': d.name}, 'name')
+				job_card = frappe.db.get_value("Job Card", {"operation_id": d.name}, "name")
 				if not job_card:
-					frappe.throw(_("Work Order {0}: Job Card not found for the operation {1}")
-						.format(self.work_order, d.operation))
+					frappe.throw(
+						_("Work Order {0}: Job Card not found for the operation {1}").format(
+							self.work_order, d.operation
+						)
+					)
 
-				work_order_link = frappe.utils.get_link_to_form('Work Order', self.work_order)
-				job_card_link = frappe.utils.get_link_to_form('Job Card', job_card)
-				frappe.throw(_("Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}.")
-					.format(d.idx, frappe.bold(d.operation), frappe.bold(total_completed_qty), work_order_link, job_card_link), OperationsNotCompleteError)
+				work_order_link = frappe.utils.get_link_to_form("Work Order", self.work_order)
+				job_card_link = frappe.utils.get_link_to_form("Job Card", job_card)
+				frappe.throw(
+					_(
+						"Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}."
+					).format(
+						d.idx,
+						frappe.bold(d.operation),
+						frappe.bold(total_completed_qty),
+						work_order_link,
+						job_card_link,
+					),
+					OperationsNotCompleteError,
+				)
 
 	def check_duplicate_entry_for_work_order(self):
-		other_ste = [t[0] for t in frappe.db.get_values("Stock Entry",  {
-			"work_order": self.work_order,
-			"purpose": self.purpose,
-			"docstatus": ["!=", 2],
-			"name": ["!=", self.name]
-		}, "name")]
+		other_ste = [
+			t[0]
+			for t in frappe.db.get_values(
+				"Stock Entry",
+				{
+					"work_order": self.work_order,
+					"purpose": self.purpose,
+					"docstatus": ["!=", 2],
+					"name": ["!=", self.name],
+				},
+				"name",
+			)
+		]
 
 		if other_ste:
-			production_item, qty = frappe.db.get_value("Work Order",
-				self.work_order, ["production_item", "qty"])
+			production_item, qty = frappe.db.get_value(
+				"Work Order", self.work_order, ["production_item", "qty"]
+			)
 			args = other_ste + [production_item]
-			fg_qty_already_entered = frappe.db.sql("""select sum(transfer_qty)
+			fg_qty_already_entered = frappe.db.sql(
+				"""select sum(transfer_qty)
 				from `tabStock Entry Detail`
 				where parent in (%s)
 					and item_code = %s
-					and ifnull(s_warehouse,'')='' """ % (", ".join(["%s" * len(other_ste)]), "%s"), args)[0][0]
+					and ifnull(s_warehouse,'')='' """
+				% (", ".join(["%s" * len(other_ste)]), "%s"),
+				args,
+			)[0][0]
 			if fg_qty_already_entered and fg_qty_already_entered >= qty:
 				frappe.throw(
 					_("Stock Entries already created for Work Order {0}: {1}").format(
@@ -435,34 +556,57 @@
 	def set_actual_qty(self):
 		from erpnext.stock.stock_ledger import is_negative_stock_allowed
 
-		for d in self.get('items'):
+		for d in self.get("items"):
 			allow_negative_stock = is_negative_stock_allowed(item_code=d.item_code)
-			previous_sle = get_previous_sle({
-				"item_code": d.item_code,
-				"warehouse": d.s_warehouse or d.t_warehouse,
-				"posting_date": self.posting_date,
-				"posting_time": self.posting_time
-			})
+			previous_sle = get_previous_sle(
+				{
+					"item_code": d.item_code,
+					"warehouse": d.s_warehouse or d.t_warehouse,
+					"posting_date": self.posting_date,
+					"posting_time": self.posting_time,
+				}
+			)
 
 			# get actual stock at source warehouse
 			d.actual_qty = previous_sle.get("qty_after_transaction") or 0
 
 			# validate qty during submit
-			if d.docstatus==1 and d.s_warehouse and not allow_negative_stock and flt(d.actual_qty, d.precision("actual_qty")) < flt(d.transfer_qty, d.precision("actual_qty")):
-				frappe.throw(_("Row {0}: Quantity not available for {4} in warehouse {1} at posting time of the entry ({2} {3})").format(d.idx,
-					frappe.bold(d.s_warehouse), formatdate(self.posting_date),
-					format_time(self.posting_time), frappe.bold(d.item_code))
-					+ '<br><br>' + _("Available quantity is {0}, you need {1}").format(frappe.bold(d.actual_qty),
-						frappe.bold(d.transfer_qty)),
-					NegativeStockError, title=_('Insufficient Stock'))
+			if (
+				d.docstatus == 1
+				and d.s_warehouse
+				and not allow_negative_stock
+				and flt(d.actual_qty, d.precision("actual_qty"))
+				< flt(d.transfer_qty, d.precision("actual_qty"))
+			):
+				frappe.throw(
+					_(
+						"Row {0}: Quantity not available for {4} in warehouse {1} at posting time of the entry ({2} {3})"
+					).format(
+						d.idx,
+						frappe.bold(d.s_warehouse),
+						formatdate(self.posting_date),
+						format_time(self.posting_time),
+						frappe.bold(d.item_code),
+					)
+					+ "<br><br>"
+					+ _("Available quantity is {0}, you need {1}").format(
+						frappe.bold(flt(d.actual_qty, d.precision("actual_qty"))), frappe.bold(d.transfer_qty)
+					),
+					NegativeStockError,
+					title=_("Insufficient Stock"),
+				)
 
 	def set_serial_nos(self, work_order):
-		previous_se = frappe.db.get_value("Stock Entry", {"work_order": work_order,
-				"purpose": "Material Transfer for Manufacture"}, "name")
+		previous_se = frappe.db.get_value(
+			"Stock Entry",
+			{"work_order": work_order, "purpose": "Material Transfer for Manufacture"},
+			"name",
+		)
 
-		for d in self.get('items'):
-			transferred_serial_no = frappe.db.get_value("Stock Entry Detail",{"parent": previous_se,
-				"item_code": d.item_code}, "serial_no")
+		for d in self.get("items"):
+			transferred_serial_no = frappe.db.get_value(
+				"Stock Entry Detail", {"parent": previous_se, "item_code": d.item_code}, "serial_no"
+			)
 
 			if transferred_serial_no:
 				d.serial_no = transferred_serial_no
@@ -470,8 +614,8 @@
 	@frappe.whitelist()
 	def get_stock_and_rate(self):
 		"""
-			Updates rate and availability of all the items.
-			Called from Update Rate and Availability button.
+		Updates rate and availability of all the items.
+		Called from Update Rate and Availability button.
 		"""
 		self.set_work_order_details()
 		self.set_transfer_qty()
@@ -488,38 +632,53 @@
 
 	def set_basic_rate(self, reset_outgoing_rate=True, raise_error_if_no_rate=True):
 		"""
-			Set rate for outgoing, scrapped and finished items
+		Set rate for outgoing, scrapped and finished items
 		"""
 		# Set rate for outgoing items
-		outgoing_items_cost = self.set_rate_for_outgoing_items(reset_outgoing_rate, raise_error_if_no_rate)
-		finished_item_qty = sum(d.transfer_qty for d in self.items if d.is_finished_item or d.is_process_loss)
+		outgoing_items_cost = self.set_rate_for_outgoing_items(
+			reset_outgoing_rate, raise_error_if_no_rate
+		)
+		finished_item_qty = sum(
+			d.transfer_qty for d in self.items if d.is_finished_item or d.is_process_loss
+		)
 
 		# Set basic rate for incoming items
-		for d in self.get('items'):
-			if d.s_warehouse or d.set_basic_rate_manually: continue
+		for d in self.get("items"):
+			if d.s_warehouse or d.set_basic_rate_manually:
+				continue
 
 			if d.allow_zero_valuation_rate:
 				d.basic_rate = 0.0
 			elif d.is_finished_item:
 				if self.purpose == "Manufacture":
-					d.basic_rate = self.get_basic_rate_for_manufactured_item(finished_item_qty, outgoing_items_cost)
+					d.basic_rate = self.get_basic_rate_for_manufactured_item(
+						finished_item_qty, outgoing_items_cost
+					)
 				elif self.purpose == "Repack":
 					d.basic_rate = self.get_basic_rate_for_repacked_items(d.transfer_qty, outgoing_items_cost)
 
 			if not d.basic_rate and not d.allow_zero_valuation_rate:
-				d.basic_rate = get_valuation_rate(d.item_code, d.t_warehouse,
-					self.doctype, self.name, d.allow_zero_valuation_rate,
-					currency=erpnext.get_company_currency(self.company), company=self.company,
-					raise_error_if_no_rate=raise_error_if_no_rate, batch_no=d.batch_no)
+				d.basic_rate = get_valuation_rate(
+					d.item_code,
+					d.t_warehouse,
+					self.doctype,
+					self.name,
+					d.allow_zero_valuation_rate,
+					currency=erpnext.get_company_currency(self.company),
+					company=self.company,
+					raise_error_if_no_rate=raise_error_if_no_rate,
+					batch_no=d.batch_no,
+				)
 
-			d.basic_rate = flt(d.basic_rate, d.precision("basic_rate"))
+			# do not round off basic rate to avoid precision loss
+			d.basic_rate = flt(d.basic_rate)
 			if d.is_process_loss:
-				d.basic_rate = flt(0.)
+				d.basic_rate = flt(0.0)
 			d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount"))
 
 	def set_rate_for_outgoing_items(self, reset_outgoing_rate=True, raise_error_if_no_rate=True):
 		outgoing_items_cost = 0.0
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if d.s_warehouse:
 				if reset_outgoing_rate:
 					args = self.get_args_for_incoming_rate(d)
@@ -534,19 +693,21 @@
 		return outgoing_items_cost
 
 	def get_args_for_incoming_rate(self, item):
-		return frappe._dict({
-			"item_code": item.item_code,
-			"warehouse": item.s_warehouse or item.t_warehouse,
-			"posting_date": self.posting_date,
-			"posting_time": self.posting_time,
-			"qty": item.s_warehouse and -1*flt(item.transfer_qty) or flt(item.transfer_qty),
-			"serial_no": item.serial_no,
-			"batch_no": item.batch_no,
-			"voucher_type": self.doctype,
-			"voucher_no": self.name,
-			"company": self.company,
-			"allow_zero_valuation": item.allow_zero_valuation_rate,
-		})
+		return frappe._dict(
+			{
+				"item_code": item.item_code,
+				"warehouse": item.s_warehouse or item.t_warehouse,
+				"posting_date": self.posting_date,
+				"posting_time": self.posting_time,
+				"qty": item.s_warehouse and -1 * flt(item.transfer_qty) or flt(item.transfer_qty),
+				"serial_no": item.serial_no,
+				"batch_no": item.batch_no,
+				"voucher_type": self.doctype,
+				"voucher_no": self.name,
+				"company": self.company,
+				"allow_zero_valuation": item.allow_zero_valuation_rate,
+			}
+		)
 
 	def get_basic_rate_for_repacked_items(self, finished_item_qty, outgoing_items_cost):
 		finished_items = [d.item_code for d in self.get("items") if d.is_finished_item]
@@ -558,13 +719,15 @@
 				total_fg_qty = sum([flt(d.transfer_qty) for d in self.items if d.is_finished_item])
 				return flt(outgoing_items_cost / total_fg_qty)
 
-	def get_basic_rate_for_manufactured_item(self, finished_item_qty, outgoing_items_cost=0):
+	def get_basic_rate_for_manufactured_item(self, finished_item_qty, outgoing_items_cost=0) -> float:
 		scrap_items_cost = sum([flt(d.basic_amount) for d in self.get("items") if d.is_scrap_item])
 
 		# Get raw materials cost from BOM if multiple material consumption entries
-		if not outgoing_items_cost and frappe.db.get_single_value("Manufacturing Settings", "material_consumption", cache=True):
+		if not outgoing_items_cost and frappe.db.get_single_value(
+			"Manufacturing Settings", "material_consumption", cache=True
+		):
 			bom_items = self.get_bom_raw_materials(finished_item_qty)
-			outgoing_items_cost = sum([flt(row.qty)*flt(row.rate) for row in bom_items.values()])
+			outgoing_items_cost = sum([flt(row.qty) * flt(row.rate) for row in bom_items.values()])
 
 		return flt((outgoing_items_cost - scrap_items_cost) / finished_item_qty)
 
@@ -596,8 +759,8 @@
 		for d in self.get("items"):
 			if d.transfer_qty:
 				d.amount = flt(flt(d.basic_amount) + flt(d.additional_cost), d.precision("amount"))
-				d.valuation_rate = flt(flt(d.basic_rate) + (flt(d.additional_cost) / flt(d.transfer_qty)),
-					d.precision("valuation_rate"))
+				# Do not round off valuation rate to avoid precision loss
+				d.valuation_rate = flt(d.basic_rate) + (flt(d.additional_cost) / flt(d.transfer_qty))
 
 	def set_total_incoming_outgoing_value(self):
 		self.total_incoming_value = self.total_outgoing_value = 0.0
@@ -611,92 +774,120 @@
 
 	def set_total_amount(self):
 		self.total_amount = None
-		if self.purpose not in ['Manufacture', 'Repack']:
+		if self.purpose not in ["Manufacture", "Repack"]:
 			self.total_amount = sum([flt(item.amount) for item in self.get("items")])
 
 	def set_stock_entry_type(self):
 		if self.purpose:
-			self.stock_entry_type = frappe.get_cached_value('Stock Entry Type',
-				{'purpose': self.purpose}, 'name')
+			self.stock_entry_type = frappe.get_cached_value(
+				"Stock Entry Type", {"purpose": self.purpose}, "name"
+			)
 
 	def set_purpose_for_stock_entry(self):
 		if self.stock_entry_type and not self.purpose:
-			self.purpose = frappe.get_cached_value('Stock Entry Type',
-				self.stock_entry_type, 'purpose')
+			self.purpose = frappe.get_cached_value("Stock Entry Type", self.stock_entry_type, "purpose")
 
 	def validate_duplicate_serial_no(self):
 		warehouse_wise_serial_nos = {}
 
 		# In case of repack the source and target serial nos could be same
-		for warehouse in ['s_warehouse', 't_warehouse']:
+		for warehouse in ["s_warehouse", "t_warehouse"]:
 			serial_nos = []
 			for row in self.items:
-				if not (row.serial_no and row.get(warehouse)): continue
+				if not (row.serial_no and row.get(warehouse)):
+					continue
 
 				for sn in get_serial_nos(row.serial_no):
 					if sn in serial_nos:
-						frappe.throw(_('The serial no {0} has added multiple times in the stock entry {1}')
-							.format(frappe.bold(sn), self.name))
+						frappe.throw(
+							_("The serial no {0} has added multiple times in the stock entry {1}").format(
+								frappe.bold(sn), self.name
+							)
+						)
 
 					serial_nos.append(sn)
 
 	def validate_purchase_order(self):
 		"""Throw exception if more raw material is transferred against Purchase Order than in
 		the raw materials supplied table"""
-		backflush_raw_materials_based_on = frappe.db.get_single_value("Buying Settings",
-			"backflush_raw_materials_of_subcontract_based_on")
+		backflush_raw_materials_based_on = frappe.db.get_single_value(
+			"Buying Settings", "backflush_raw_materials_of_subcontract_based_on"
+		)
 
-		qty_allowance = flt(frappe.db.get_single_value("Buying Settings",
-			"over_transfer_allowance"))
+		qty_allowance = flt(frappe.db.get_single_value("Buying Settings", "over_transfer_allowance"))
 
-		if not (self.purpose == "Send to Subcontractor" and self.purchase_order): return
+		if not (self.purpose == "Send to Subcontractor" and self.purchase_order):
+			return
 
-		if (backflush_raw_materials_based_on == 'BOM'):
+		if backflush_raw_materials_based_on == "BOM":
 			purchase_order = frappe.get_doc("Purchase Order", self.purchase_order)
 			for se_item in self.items:
 				item_code = se_item.original_item or se_item.item_code
 				precision = cint(frappe.db.get_default("float_precision")) or 3
-				required_qty = sum([flt(d.required_qty) for d in purchase_order.supplied_items \
-					if d.rm_item_code == item_code])
+				required_qty = sum(
+					[flt(d.required_qty) for d in purchase_order.supplied_items if d.rm_item_code == item_code]
+				)
 
-				total_allowed = required_qty + (required_qty * (qty_allowance/100))
+				total_allowed = required_qty + (required_qty * (qty_allowance / 100))
 
 				if not required_qty:
-					bom_no = frappe.db.get_value("Purchase Order Item",
+					bom_no = frappe.db.get_value(
+						"Purchase Order Item",
 						{"parent": self.purchase_order, "item_code": se_item.subcontracted_item},
-						"bom")
+						"bom",
+					)
 
 					if se_item.allow_alternative_item:
-						original_item_code = frappe.get_value("Item Alternative", {"alternative_item_code": item_code}, "item_code")
+						original_item_code = frappe.get_value(
+							"Item Alternative", {"alternative_item_code": item_code}, "item_code"
+						)
 
-						required_qty = sum([flt(d.required_qty) for d in purchase_order.supplied_items \
-							if d.rm_item_code == original_item_code])
+						required_qty = sum(
+							[
+								flt(d.required_qty)
+								for d in purchase_order.supplied_items
+								if d.rm_item_code == original_item_code
+							]
+						)
 
-						total_allowed = required_qty + (required_qty * (qty_allowance/100))
+						total_allowed = required_qty + (required_qty * (qty_allowance / 100))
 
 				if not required_qty:
-					frappe.throw(_("Item {0} not found in 'Raw Materials Supplied' table in Purchase Order {1}")
-						.format(se_item.item_code, self.purchase_order))
-				total_supplied = frappe.db.sql("""select sum(transfer_qty)
+					frappe.throw(
+						_("Item {0} not found in 'Raw Materials Supplied' table in Purchase Order {1}").format(
+							se_item.item_code, self.purchase_order
+						)
+					)
+				total_supplied = frappe.db.sql(
+					"""select sum(transfer_qty)
 					from `tabStock Entry Detail`, `tabStock Entry`
 					where `tabStock Entry`.purchase_order = %s
 						and `tabStock Entry`.docstatus = 1
 						and `tabStock Entry Detail`.item_code = %s
 						and `tabStock Entry Detail`.parent = `tabStock Entry`.name""",
-							(self.purchase_order, se_item.item_code))[0][0]
+					(self.purchase_order, se_item.item_code),
+				)[0][0]
 
 				if flt(total_supplied, precision) > flt(total_allowed, precision):
-					frappe.throw(_("Row {0}# Item {1} cannot be transferred more than {2} against Purchase Order {3}")
-						.format(se_item.idx, se_item.item_code, total_allowed, self.purchase_order))
+					frappe.throw(
+						_("Row {0}# Item {1} cannot be transferred more than {2} against Purchase Order {3}").format(
+							se_item.idx, se_item.item_code, total_allowed, self.purchase_order
+						)
+					)
 		elif backflush_raw_materials_based_on == "Material Transferred for Subcontract":
 			for row in self.items:
 				if not row.subcontracted_item:
-					frappe.throw(_("Row {0}: Subcontracted Item is mandatory for the raw material {1}")
-						.format(row.idx, frappe.bold(row.item_code)))
+					frappe.throw(
+						_("Row {0}: Subcontracted Item is mandatory for the raw material {1}").format(
+							row.idx, frappe.bold(row.item_code)
+						)
+					)
 				elif not row.po_detail:
 					filters = {
-						"parent": self.purchase_order, "docstatus": 1,
-						"rm_item_code": row.item_code, "main_item_code": row.subcontracted_item
+						"parent": self.purchase_order,
+						"docstatus": 1,
+						"rm_item_code": row.item_code,
+						"main_item_code": row.subcontracted_item,
 					}
 
 					po_detail = frappe.db.get_value("Purchase Order Item Supplied", filters, "name")
@@ -704,7 +895,7 @@
 						row.db_set("po_detail", po_detail)
 
 	def validate_bom(self):
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if d.bom_no and d.is_finished_item:
 				item_code = d.original_item or d.item_code
 				validate_bom_no(item_code, d.bom_no)
@@ -722,7 +913,7 @@
 
 		for d in self.items:
 			if d.t_warehouse and not d.s_warehouse:
-				if self.purpose=="Repack" or d.item_code == finished_item:
+				if self.purpose == "Repack" or d.item_code == finished_item:
 					d.is_finished_item = 1
 				else:
 					d.is_scrap_item = 1
@@ -741,19 +932,17 @@
 
 	def validate_finished_goods(self):
 		"""
-			1. Check if FG exists (mfg, repack)
-			2. Check if Multiple FG Items are present (mfg)
-			3. Check FG Item and Qty against WO if present (mfg)
+		1. Check if FG exists (mfg, repack)
+		2. Check if Multiple FG Items are present (mfg)
+		3. Check FG Item and Qty against WO if present (mfg)
 		"""
 		production_item, wo_qty, finished_items = None, 0, []
 
-		wo_details = frappe.db.get_value(
-			"Work Order", self.work_order, ["production_item", "qty"]
-		)
+		wo_details = frappe.db.get_value("Work Order", self.work_order, ["production_item", "qty"])
 		if wo_details:
 			production_item, wo_qty = wo_details
 
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if d.is_finished_item:
 				if not self.work_order:
 					# Independent MFG Entry/ Repack Entry, no WO to match against
@@ -761,12 +950,16 @@
 					continue
 
 				if d.item_code != production_item:
-					frappe.throw(_("Finished Item {0} does not match with Work Order {1}")
-						.format(d.item_code, self.work_order)
+					frappe.throw(
+						_("Finished Item {0} does not match with Work Order {1}").format(
+							d.item_code, self.work_order
+						)
 					)
 				elif flt(d.transfer_qty) > flt(self.fg_completed_qty):
-					frappe.throw(_("Quantity in row {0} ({1}) must be same as manufactured quantity {2}")
-						.format(d.idx, d.transfer_qty, self.fg_completed_qty)
+					frappe.throw(
+						_("Quantity in row {0} ({1}) must be same as manufactured quantity {2}").format(
+							d.idx, d.transfer_qty, self.fg_completed_qty
+						)
 					)
 
 				finished_items.append(d.item_code)
@@ -774,28 +967,31 @@
 		if not finished_items:
 			frappe.throw(
 				msg=_("There must be atleast 1 Finished Good in this Stock Entry").format(self.name),
-				title=_("Missing Finished Good"), exc=FinishedGoodError
+				title=_("Missing Finished Good"),
+				exc=FinishedGoodError,
 			)
 
 		if self.purpose == "Manufacture":
 			if len(set(finished_items)) > 1:
 				frappe.throw(
 					msg=_("Multiple items cannot be marked as finished item"),
-					title=_("Note"), exc=FinishedGoodError
+					title=_("Note"),
+					exc=FinishedGoodError,
 				)
 
 			allowance_percentage = flt(
 				frappe.db.get_single_value(
-					"Manufacturing Settings","overproduction_percentage_for_work_order"
+					"Manufacturing Settings", "overproduction_percentage_for_work_order"
 				)
 			)
-			allowed_qty = wo_qty + ((allowance_percentage/100) * wo_qty)
+			allowed_qty = wo_qty + ((allowance_percentage / 100) * wo_qty)
 
 			# No work order could mean independent Manufacture entry, if so skip validation
 			if self.work_order and self.fg_completed_qty > allowed_qty:
 				frappe.throw(
-					_("For quantity {0} should not be greater than work order quantity {1}")
-					.format(flt(self.fg_completed_qty), wo_qty)
+					_("For quantity {0} should not be greater than work order quantity {1}").format(
+						flt(self.fg_completed_qty), wo_qty
+					)
 				)
 
 	def update_stock_ledger(self):
@@ -817,35 +1013,38 @@
 	def get_finished_item_row(self):
 		finished_item_row = None
 		if self.purpose in ("Manufacture", "Repack"):
-			for d in self.get('items'):
+			for d in self.get("items"):
 				if d.is_finished_item:
 					finished_item_row = d
 
 		return finished_item_row
 
 	def get_sle_for_source_warehouse(self, sl_entries, finished_item_row):
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if cstr(d.s_warehouse):
-				sle = self.get_sl_entries(d, {
-					"warehouse": cstr(d.s_warehouse),
-					"actual_qty": -flt(d.transfer_qty),
-					"incoming_rate": 0
-				})
+				sle = self.get_sl_entries(
+					d, {"warehouse": cstr(d.s_warehouse), "actual_qty": -flt(d.transfer_qty), "incoming_rate": 0}
+				)
 				if cstr(d.t_warehouse):
 					sle.dependant_sle_voucher_detail_no = d.name
-				elif finished_item_row and (finished_item_row.item_code != d.item_code or finished_item_row.t_warehouse != d.s_warehouse):
+				elif finished_item_row and (
+					finished_item_row.item_code != d.item_code or finished_item_row.t_warehouse != d.s_warehouse
+				):
 					sle.dependant_sle_voucher_detail_no = finished_item_row.name
 
 				sl_entries.append(sle)
 
 	def get_sle_for_target_warehouse(self, sl_entries, finished_item_row):
-		for d in self.get('items'):
+		for d in self.get("items"):
 			if cstr(d.t_warehouse):
-				sle = self.get_sl_entries(d, {
-					"warehouse": cstr(d.t_warehouse),
-					"actual_qty": flt(d.transfer_qty),
-					"incoming_rate": flt(d.valuation_rate)
-				})
+				sle = self.get_sl_entries(
+					d,
+					{
+						"warehouse": cstr(d.t_warehouse),
+						"actual_qty": flt(d.transfer_qty),
+						"incoming_rate": flt(d.valuation_rate),
+					},
+				)
 				if cstr(d.s_warehouse) or (finished_item_row and d.name == finished_item_row.name):
 					sle.recalculate_rate = 1
 
@@ -875,40 +1074,55 @@
 					continue
 
 				item_account_wise_additional_cost.setdefault((d.item_code, d.name), {})
-				item_account_wise_additional_cost[(d.item_code, d.name)].setdefault(t.expense_account, {
-					"amount": 0.0,
-					"base_amount": 0.0
-				})
+				item_account_wise_additional_cost[(d.item_code, d.name)].setdefault(
+					t.expense_account, {"amount": 0.0, "base_amount": 0.0}
+				)
 
 				multiply_based_on = d.basic_amount if total_basic_amount else d.qty
 
-				item_account_wise_additional_cost[(d.item_code, d.name)][t.expense_account]["amount"] += \
+				item_account_wise_additional_cost[(d.item_code, d.name)][t.expense_account]["amount"] += (
 					flt(t.amount * multiply_based_on) / divide_based_on
+				)
 
-				item_account_wise_additional_cost[(d.item_code, d.name)][t.expense_account]["base_amount"] += \
+				item_account_wise_additional_cost[(d.item_code, d.name)][t.expense_account]["base_amount"] += (
 					flt(t.base_amount * multiply_based_on) / divide_based_on
+				)
 
 		if item_account_wise_additional_cost:
 			for d in self.get("items"):
-				for account, amount in item_account_wise_additional_cost.get((d.item_code, d.name), {}).items():
-					if not amount: continue
+				for account, amount in item_account_wise_additional_cost.get(
+					(d.item_code, d.name), {}
+				).items():
+					if not amount:
+						continue
 
-					gl_entries.append(self.get_gl_dict({
-						"account": account,
-						"against": d.expense_account,
-						"cost_center": d.cost_center,
-						"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-						"credit_in_account_currency": flt(amount["amount"]),
-						"credit": flt(amount["base_amount"])
-					}, item=d))
+					gl_entries.append(
+						self.get_gl_dict(
+							{
+								"account": account,
+								"against": d.expense_account,
+								"cost_center": d.cost_center,
+								"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+								"credit_in_account_currency": flt(amount["amount"]),
+								"credit": flt(amount["base_amount"]),
+							},
+							item=d,
+						)
+					)
 
-					gl_entries.append(self.get_gl_dict({
-						"account": d.expense_account,
-						"against": account,
-						"cost_center": d.cost_center,
-						"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-						"credit": -1 * amount['base_amount'] # put it as negative credit instead of debit purposefully
-					}, item=d))
+					gl_entries.append(
+						self.get_gl_dict(
+							{
+								"account": d.expense_account,
+								"against": account,
+								"cost_center": d.cost_center,
+								"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+								"credit": -1
+								* amount["base_amount"],  # put it as negative credit instead of debit purposefully
+							},
+							item=d,
+						)
+					)
 
 		return process_gl_map(gl_entries)
 
@@ -917,13 +1131,15 @@
 			if flt(pro_doc.docstatus) != 1:
 				frappe.throw(_("Work Order {0} must be submitted").format(self.work_order))
 
-			if pro_doc.status == 'Stopped':
-				frappe.throw(_("Transaction not allowed against stopped Work Order {0}").format(self.work_order))
+			if pro_doc.status == "Stopped":
+				frappe.throw(
+					_("Transaction not allowed against stopped Work Order {0}").format(self.work_order)
+				)
 
 		if self.job_card:
-			job_doc = frappe.get_doc('Job Card', self.job_card)
+			job_doc = frappe.get_doc("Job Card", self.job_card)
 			job_doc.set_transferred_qty(update_status=True)
-			job_doc.set_transferred_qty_in_job_card(self)
+			job_doc.set_transferred_qty_in_job_card_item(self)
 
 		if self.work_order:
 			pro_doc = frappe.get_doc("Work Order", self.work_order)
@@ -941,73 +1157,95 @@
 
 	@frappe.whitelist()
 	def get_item_details(self, args=None, for_update=False):
-		item = frappe.db.sql("""select i.name, i.stock_uom, i.description, i.image, i.item_name, i.item_group,
+		item = frappe.db.sql(
+			"""select i.name, i.stock_uom, i.description, i.image, i.item_name, i.item_group,
 				i.has_batch_no, i.sample_quantity, i.has_serial_no, i.allow_alternative_item,
 				id.expense_account, id.buying_cost_center
 			from `tabItem` i LEFT JOIN `tabItem Default` id ON i.name=id.parent and id.company=%s
 			where i.name=%s
 				and i.disabled=0
-				and (i.end_of_life is null or i.end_of_life='0000-00-00' or i.end_of_life > %s)""",
-			(self.company, args.get('item_code'), nowdate()), as_dict = 1)
+				and (i.end_of_life is null or i.end_of_life<'1900-01-01' or i.end_of_life > %s)""",
+			(self.company, args.get("item_code"), nowdate()),
+			as_dict=1,
+		)
 
 		if not item:
-			frappe.throw(_("Item {0} is not active or end of life has been reached").format(args.get("item_code")))
+			frappe.throw(
+				_("Item {0} is not active or end of life has been reached").format(args.get("item_code"))
+			)
 
 		item = item[0]
 		item_group_defaults = get_item_group_defaults(item.name, self.company)
 		brand_defaults = get_brand_defaults(item.name, self.company)
 
-		ret = frappe._dict({
-			'uom'			      	: item.stock_uom,
-			'stock_uom'				: item.stock_uom,
-			'description'		  	: item.description,
-			'image'					: item.image,
-			'item_name' 		  	: item.item_name,
-			'cost_center'			: get_default_cost_center(args, item, item_group_defaults, brand_defaults, self.company),
-			'qty'					: args.get("qty"),
-			'transfer_qty'			: args.get('qty'),
-			'conversion_factor'		: 1,
-			'batch_no'				: '',
-			'actual_qty'			: 0,
-			'basic_rate'			: 0,
-			'serial_no'				: '',
-			'has_serial_no'			: item.has_serial_no,
-			'has_batch_no'			: item.has_batch_no,
-			'sample_quantity'		: item.sample_quantity,
-			'expense_account'		: item.expense_account
-		})
+		ret = frappe._dict(
+			{
+				"uom": item.stock_uom,
+				"stock_uom": item.stock_uom,
+				"description": item.description,
+				"image": item.image,
+				"item_name": item.item_name,
+				"cost_center": get_default_cost_center(
+					args, item, item_group_defaults, brand_defaults, self.company
+				),
+				"qty": args.get("qty"),
+				"transfer_qty": args.get("qty"),
+				"conversion_factor": 1,
+				"batch_no": "",
+				"actual_qty": 0,
+				"basic_rate": 0,
+				"serial_no": "",
+				"has_serial_no": item.has_serial_no,
+				"has_batch_no": item.has_batch_no,
+				"sample_quantity": item.sample_quantity,
+				"expense_account": item.expense_account,
+			}
+		)
 
-		if self.purpose == 'Send to Subcontractor':
+		if self.purpose == "Send to Subcontractor":
 			ret["allow_alternative_item"] = item.allow_alternative_item
 
 		# update uom
 		if args.get("uom") and for_update:
-			ret.update(get_uom_details(args.get('item_code'), args.get('uom'), args.get('qty')))
+			ret.update(get_uom_details(args.get("item_code"), args.get("uom"), args.get("qty")))
 
-		if self.purpose == 'Material Issue':
-			ret["expense_account"] = (item.get("expense_account") or
-				item_group_defaults.get("expense_account") or
-				frappe.get_cached_value('Company',  self.company,  "default_expense_account"))
+		if self.purpose == "Material Issue":
+			ret["expense_account"] = (
+				item.get("expense_account")
+				or item_group_defaults.get("expense_account")
+				or frappe.get_cached_value("Company", self.company, "default_expense_account")
+			)
 
-		for company_field, field in {'stock_adjustment_account': 'expense_account',
-			'cost_center': 'cost_center'}.items():
+		for company_field, field in {
+			"stock_adjustment_account": "expense_account",
+			"cost_center": "cost_center",
+		}.items():
 			if not ret.get(field):
-				ret[field] = frappe.get_cached_value('Company',  self.company,  company_field)
+				ret[field] = frappe.get_cached_value("Company", self.company, company_field)
 
-		args['posting_date'] = self.posting_date
-		args['posting_time'] = self.posting_time
+		args["posting_date"] = self.posting_date
+		args["posting_time"] = self.posting_time
 
-		stock_and_rate = get_warehouse_details(args) if args.get('warehouse') else {}
+		stock_and_rate = get_warehouse_details(args) if args.get("warehouse") else {}
 		ret.update(stock_and_rate)
 
 		# automatically select batch for outgoing item
-		if (args.get('s_warehouse', None) and args.get('qty') and
-			ret.get('has_batch_no') and not args.get('batch_no')):
-			args.batch_no = get_batch_no(args['item_code'], args['s_warehouse'], args['qty'])
+		if (
+			args.get("s_warehouse", None)
+			and args.get("qty")
+			and ret.get("has_batch_no")
+			and not args.get("batch_no")
+		):
+			args.batch_no = get_batch_no(args["item_code"], args["s_warehouse"], args["qty"])
 
-		if self.purpose == "Send to Subcontractor" and self.get("purchase_order") and args.get('item_code'):
-			subcontract_items = frappe.get_all("Purchase Order Item Supplied",
-				{"parent": self.purchase_order, "rm_item_code": args.get('item_code')}, "main_item_code")
+		if (
+			self.purpose == "Send to Subcontractor" and self.get("purchase_order") and args.get("item_code")
+		):
+			subcontract_items = frappe.get_all(
+				"Purchase Order Item Supplied",
+				{"parent": self.purchase_order, "rm_item_code": args.get("item_code")},
+				"main_item_code",
+			)
 
 			if subcontract_items and len(subcontract_items) == 1:
 				ret["subcontracted_item"] = subcontract_items[0].main_item_code
@@ -1018,46 +1256,57 @@
 	def set_items_for_stock_in(self):
 		self.items = []
 
-		if self.outgoing_stock_entry and self.purpose == 'Material Transfer':
-			doc = frappe.get_doc('Stock Entry', self.outgoing_stock_entry)
+		if self.outgoing_stock_entry and self.purpose == "Material Transfer":
+			doc = frappe.get_doc("Stock Entry", self.outgoing_stock_entry)
 
 			if doc.per_transferred == 100:
-				frappe.throw(_("Goods are already received against the outward entry {0}")
-					.format(doc.name))
+				frappe.throw(_("Goods are already received against the outward entry {0}").format(doc.name))
 
 			for d in doc.items:
-				self.append('items', {
-					's_warehouse': d.t_warehouse,
-					'item_code': d.item_code,
-					'qty': d.qty,
-					'uom': d.uom,
-					'against_stock_entry': d.parent,
-					'ste_detail': d.name,
-					'stock_uom': d.stock_uom,
-					'conversion_factor': d.conversion_factor,
-					'serial_no': d.serial_no,
-					'batch_no': d.batch_no
-				})
+				self.append(
+					"items",
+					{
+						"s_warehouse": d.t_warehouse,
+						"item_code": d.item_code,
+						"qty": d.qty,
+						"uom": d.uom,
+						"against_stock_entry": d.parent,
+						"ste_detail": d.name,
+						"stock_uom": d.stock_uom,
+						"conversion_factor": d.conversion_factor,
+						"serial_no": d.serial_no,
+						"batch_no": d.batch_no,
+					},
+				)
 
 	@frappe.whitelist()
 	def get_items(self):
-		self.set('items', [])
+		self.set("items", [])
 		self.validate_work_order()
 
 		if not self.posting_date or not self.posting_time:
 			frappe.throw(_("Posting date and posting time is mandatory"))
 
 		self.set_work_order_details()
-		self.flags.backflush_based_on = frappe.db.get_single_value("Manufacturing Settings",
-			"backflush_raw_materials_based_on")
+		self.flags.backflush_based_on = frappe.db.get_single_value(
+			"Manufacturing Settings", "backflush_raw_materials_based_on"
+		)
 
 		if self.bom_no:
 
-			backflush_based_on = frappe.db.get_single_value("Manufacturing Settings",
-				"backflush_raw_materials_based_on")
+			backflush_based_on = frappe.db.get_single_value(
+				"Manufacturing Settings", "backflush_raw_materials_based_on"
+			)
 
-			if self.purpose in ["Material Issue", "Material Transfer", "Manufacture", "Repack",
-					"Send to Subcontractor", "Material Transfer for Manufacture", "Material Consumption for Manufacture"]:
+			if self.purpose in [
+				"Material Issue",
+				"Material Transfer",
+				"Manufacture",
+				"Repack",
+				"Send to Subcontractor",
+				"Material Transfer for Manufacture",
+				"Material Consumption for Manufacture",
+			]:
 
 				if self.work_order and self.purpose == "Material Transfer for Manufacture":
 					item_dict = self.get_pending_raw_materials(backflush_based_on)
@@ -1066,14 +1315,20 @@
 							item["to_warehouse"] = self.pro_doc.wip_warehouse
 					self.add_to_stock_entry_detail(item_dict)
 
-				elif (self.work_order and (self.purpose == "Manufacture"
-						or self.purpose == "Material Consumption for Manufacture") and not self.pro_doc.skip_transfer
-					and self.flags.backflush_based_on == "Material Transferred for Manufacture"):
+				elif (
+					self.work_order
+					and (self.purpose == "Manufacture" or self.purpose == "Material Consumption for Manufacture")
+					and not self.pro_doc.skip_transfer
+					and self.flags.backflush_based_on == "Material Transferred for Manufacture"
+				):
 					self.get_transfered_raw_materials()
 
-				elif (self.work_order and (self.purpose == "Manufacture" or
-					self.purpose == "Material Consumption for Manufacture") and self.flags.backflush_based_on== "BOM"
-					and frappe.db.get_single_value("Manufacturing Settings", "material_consumption")== 1):
+				elif (
+					self.work_order
+					and (self.purpose == "Manufacture" or self.purpose == "Material Consumption for Manufacture")
+					and self.flags.backflush_based_on == "BOM"
+					and frappe.db.get_single_value("Manufacturing Settings", "material_consumption") == 1
+				):
 					self.get_unconsumed_raw_materials()
 
 				else:
@@ -1082,31 +1337,36 @@
 
 					item_dict = self.get_bom_raw_materials(self.fg_completed_qty)
 
-					#Get PO Supplied Items Details
+					# Get PO Supplied Items Details
 					if self.purchase_order and self.purpose == "Send to Subcontractor":
-						#Get PO Supplied Items Details
-						item_wh = frappe._dict(frappe.db.sql("""
+						# Get PO Supplied Items Details
+						item_wh = frappe._dict(
+							frappe.db.sql(
+								"""
 							SELECT
 								rm_item_code, reserve_warehouse
 							FROM
 								`tabPurchase Order` po, `tabPurchase Order Item Supplied` poitemsup
 							WHERE
-								po.name = poitemsup.parent and po.name = %s """,self.purchase_order))
+								po.name = poitemsup.parent and po.name = %s """,
+								self.purchase_order,
+							)
+						)
 
 					for item in item_dict.values():
 						if self.pro_doc and cint(self.pro_doc.from_wip_warehouse):
 							item["from_warehouse"] = self.pro_doc.wip_warehouse
-						#Get Reserve Warehouse from PO
-						if self.purchase_order and self.purpose=="Send to Subcontractor":
+						# Get Reserve Warehouse from PO
+						if self.purchase_order and self.purpose == "Send to Subcontractor":
 							item["from_warehouse"] = item_wh.get(item.item_code)
-						item["to_warehouse"] = self.to_warehouse if self.purpose=="Send to Subcontractor" else ""
+						item["to_warehouse"] = self.to_warehouse if self.purpose == "Send to Subcontractor" else ""
 
 					self.add_to_stock_entry_detail(item_dict)
 
 			# fetch the serial_no of the first stock entry for the second stock entry
 			if self.work_order and self.purpose == "Manufacture":
 				self.set_serial_nos(self.work_order)
-				work_order = frappe.get_doc('Work Order', self.work_order)
+				work_order = frappe.get_doc("Work Order", self.work_order)
 				add_additional_cost(self, work_order)
 
 			# add finished goods item
@@ -1123,7 +1383,6 @@
 		if self.purpose != "Send to Subcontractor" and self.purpose in ["Manufacture", "Repack"]:
 			scrap_item_dict = self.get_bom_scrap_material(self.fg_completed_qty)
 			for item in scrap_item_dict.values():
-				item.idx = ''
 				if self.pro_doc and self.pro_doc.scrap_warehouse:
 					item["to_warehouse"] = self.pro_doc.scrap_warehouse
 
@@ -1136,7 +1395,7 @@
 		if self.work_order:
 			# common validations
 			if not self.pro_doc:
-				self.pro_doc = frappe.get_doc('Work Order', self.work_order)
+				self.pro_doc = frappe.get_doc("Work Order", self.work_order)
 
 			if self.pro_doc:
 				self.bom_no = self.pro_doc.bom_no
@@ -1167,11 +1426,18 @@
 			"stock_uom": item.stock_uom,
 			"expense_account": item.get("expense_account"),
 			"cost_center": item.get("buying_cost_center"),
-			"is_finished_item": 1
+			"is_finished_item": 1,
 		}
 
-		if self.work_order and self.pro_doc.has_batch_no and cint(frappe.db.get_single_value('Manufacturing Settings',
-			'make_serial_no_batch_from_work_order', cache=True)):
+		if (
+			self.work_order
+			and self.pro_doc.has_batch_no
+			and cint(
+				frappe.db.get_single_value(
+					"Manufacturing Settings", "make_serial_no_batch_from_work_order", cache=True
+				)
+			)
+		):
 			self.set_batchwise_finished_goods(args, item)
 		else:
 			self.add_finished_goods(args, item)
@@ -1180,12 +1446,12 @@
 		filters = {
 			"reference_name": self.pro_doc.name,
 			"reference_doctype": self.pro_doc.doctype,
-			"qty_to_produce": (">", 0)
+			"qty_to_produce": (">", 0),
 		}
 
 		fields = ["qty_to_produce as qty", "produced_qty", "name"]
 
-		data = frappe.get_all("Batch", filters = filters, fields = fields, order_by="creation asc")
+		data = frappe.get_all("Batch", filters=filters, fields=fields, order_by="creation asc")
 
 		if not data:
 			self.add_finished_goods(args, item)
@@ -1200,7 +1466,7 @@
 			if not batch_qty:
 				continue
 
-			if qty <=0:
+			if qty <= 0:
 				break
 
 			fg_qty = batch_qty
@@ -1214,23 +1480,27 @@
 			self.add_finished_goods(args, item)
 
 	def add_finished_goods(self, args, item):
-		self.add_to_stock_entry_detail({
-			item.name: args
-		}, bom_no = self.bom_no)
+		self.add_to_stock_entry_detail({item.name: args}, bom_no=self.bom_no)
 
 	def get_bom_raw_materials(self, qty):
 		from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
 
 		# item dict = { item_code: {qty, description, stock_uom} }
-		item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=qty,
-			fetch_exploded = self.use_multi_level_bom, fetch_qty_in_stock_uom=False)
+		item_dict = get_bom_items_as_dict(
+			self.bom_no,
+			self.company,
+			qty=qty,
+			fetch_exploded=self.use_multi_level_bom,
+			fetch_qty_in_stock_uom=False,
+		)
 
-		used_alternative_items = get_used_alternative_items(work_order = self.work_order)
+		used_alternative_items = get_used_alternative_items(work_order=self.work_order)
 		for item in item_dict.values():
 			# if source warehouse presents in BOM set from_warehouse as bom source_warehouse
 			if item["allow_alternative_item"]:
-				item["allow_alternative_item"] = frappe.db.get_value('Work Order',
-					self.work_order, "allow_alternative_item")
+				item["allow_alternative_item"] = frappe.db.get_value(
+					"Work Order", self.work_order, "allow_alternative_item"
+				)
 
 			item.from_warehouse = self.from_warehouse or item.source_warehouse or item.default_warehouse
 			if item.item_code in used_alternative_items:
@@ -1248,8 +1518,10 @@
 		from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
 
 		# item dict = { item_code: {qty, description, stock_uom} }
-		item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=qty,
-			fetch_exploded = 0, fetch_scrap_items = 1) or {}
+		item_dict = (
+			get_bom_items_as_dict(self.bom_no, self.company, qty=qty, fetch_exploded=0, fetch_scrap_items=1)
+			or {}
+		)
 
 		for item in item_dict.values():
 			item.from_warehouse = ""
@@ -1263,16 +1535,18 @@
 			if not item_row:
 				item_row = frappe._dict({})
 
-			item_row.update({
-				'uom': row.stock_uom,
-				'from_warehouse': '',
-				'qty': row.stock_qty + flt(item_row.stock_qty),
-				'converison_factor': 1,
-				'is_scrap_item': 1,
-				'item_name': row.item_name,
-				'description': row.description,
-				'allow_zero_valuation_rate': 1
-			})
+			item_row.update(
+				{
+					"uom": row.stock_uom,
+					"from_warehouse": "",
+					"qty": row.stock_qty + flt(item_row.stock_qty),
+					"converison_factor": 1,
+					"is_scrap_item": 1,
+					"item_name": row.item_name,
+					"description": row.description,
+					"allow_zero_valuation_rate": 1,
+				}
+			)
 
 			item_dict[row.item_code] = item_row
 
@@ -1285,21 +1559,25 @@
 		if not self.pro_doc.operations:
 			return []
 
-		job_card = frappe.qb.DocType('Job Card')
-		job_card_scrap_item = frappe.qb.DocType('Job Card Scrap Item')
+		job_card = frappe.qb.DocType("Job Card")
+		job_card_scrap_item = frappe.qb.DocType("Job Card Scrap Item")
 
 		scrap_items = (
 			frappe.qb.from_(job_card)
 			.select(
-				Sum(job_card_scrap_item.stock_qty).as_('stock_qty'),
-				job_card_scrap_item.item_code, job_card_scrap_item.item_name,
-				job_card_scrap_item.description, job_card_scrap_item.stock_uom)
+				Sum(job_card_scrap_item.stock_qty).as_("stock_qty"),
+				job_card_scrap_item.item_code,
+				job_card_scrap_item.item_name,
+				job_card_scrap_item.description,
+				job_card_scrap_item.stock_uom,
+			)
 			.join(job_card_scrap_item)
 			.on(job_card_scrap_item.parent == job_card.name)
 			.where(
 				(job_card_scrap_item.item_code.isnotnull())
 				& (job_card.work_order == self.work_order)
-				& (job_card.docstatus == 1))
+				& (job_card.docstatus == 1)
+			)
 			.groupby(job_card_scrap_item.item_code)
 		).run(as_dict=1)
 
@@ -1313,7 +1591,7 @@
 			if used_scrap_items.get(row.item_code):
 				used_scrap_items[row.item_code] -= row.stock_qty
 
-			if cint(frappe.get_cached_value('UOM', row.stock_uom, 'must_be_whole_number')):
+			if cint(frappe.get_cached_value("UOM", row.stock_uom, "must_be_whole_number")):
 				row.stock_qty = frappe.utils.ceil(row.stock_qty)
 
 		return scrap_items
@@ -1324,16 +1602,14 @@
 	def get_used_scrap_items(self):
 		used_scrap_items = defaultdict(float)
 		data = frappe.get_all(
-			'Stock Entry',
-			fields = [
-				'`tabStock Entry Detail`.`item_code`', '`tabStock Entry Detail`.`qty`'
+			"Stock Entry",
+			fields=["`tabStock Entry Detail`.`item_code`", "`tabStock Entry Detail`.`qty`"],
+			filters=[
+				["Stock Entry", "work_order", "=", self.work_order],
+				["Stock Entry Detail", "is_scrap_item", "=", 1],
+				["Stock Entry", "docstatus", "=", 1],
+				["Stock Entry", "purpose", "in", ["Repack", "Manufacture"]],
 			],
-			filters = [
-				['Stock Entry', 'work_order', '=', self.work_order],
-				['Stock Entry Detail', 'is_scrap_item', '=', 1],
-				['Stock Entry', 'docstatus', '=', 1],
-				['Stock Entry', 'purpose', 'in', ['Repack', 'Manufacture']]
-			]
 		)
 
 		for row in data:
@@ -1343,10 +1619,11 @@
 
 	def get_unconsumed_raw_materials(self):
 		wo = frappe.get_doc("Work Order", self.work_order)
-		wo_items = frappe.get_all('Work Order Item',
-			filters={'parent': self.work_order},
-			fields=["item_code", "source_warehouse", "required_qty", "consumed_qty", "transferred_qty"]
-			)
+		wo_items = frappe.get_all(
+			"Work Order Item",
+			filters={"parent": self.work_order},
+			fields=["item_code", "source_warehouse", "required_qty", "consumed_qty", "transferred_qty"],
+		)
 
 		work_order_qty = wo.material_transferred_for_manufacturing or wo.qty
 		for item in wo_items:
@@ -1363,21 +1640,24 @@
 			qty = req_qty_each * flt(self.fg_completed_qty)
 
 			if qty > 0:
-				self.add_to_stock_entry_detail({
-					item.item_code: {
-						"from_warehouse": wo.wip_warehouse or item.source_warehouse,
-						"to_warehouse": "",
-						"qty": qty,
-						"item_name": item.item_name,
-						"description": item.description,
-						"stock_uom": item_account_details.stock_uom,
-						"expense_account": item_account_details.get("expense_account"),
-						"cost_center": item_account_details.get("buying_cost_center"),
+				self.add_to_stock_entry_detail(
+					{
+						item.item_code: {
+							"from_warehouse": wo.wip_warehouse or item.source_warehouse,
+							"to_warehouse": "",
+							"qty": qty,
+							"item_name": item.item_name,
+							"description": item.description,
+							"stock_uom": item_account_details.stock_uom,
+							"expense_account": item_account_details.get("expense_account"),
+							"cost_center": item_account_details.get("buying_cost_center"),
+						}
 					}
-				})
+				)
 
 	def get_transfered_raw_materials(self):
-		transferred_materials = frappe.db.sql("""
+		transferred_materials = frappe.db.sql(
+			"""
 			select
 				item_name, original_item, item_code, sum(qty) as qty, sed.t_warehouse as warehouse,
 				description, stock_uom, expense_account, cost_center
@@ -1386,9 +1666,13 @@
 				se.name = sed.parent and se.docstatus=1 and se.purpose='Material Transfer for Manufacture'
 				and se.work_order= %s and ifnull(sed.t_warehouse, '') != ''
 			group by sed.item_code, sed.t_warehouse
-		""", self.work_order, as_dict=1)
+		""",
+			self.work_order,
+			as_dict=1,
+		)
 
-		materials_already_backflushed = frappe.db.sql("""
+		materials_already_backflushed = frappe.db.sql(
+			"""
 			select
 				item_code, sed.s_warehouse as warehouse, sum(qty) as qty
 			from
@@ -1398,26 +1682,34 @@
 				and (se.purpose='Manufacture' or se.purpose='Material Consumption for Manufacture')
 				and se.work_order= %s and ifnull(sed.s_warehouse, '') != ''
 			group by sed.item_code, sed.s_warehouse
-		""", self.work_order, as_dict=1)
+		""",
+			self.work_order,
+			as_dict=1,
+		)
 
-		backflushed_materials= {}
+		backflushed_materials = {}
 		for d in materials_already_backflushed:
-			backflushed_materials.setdefault(d.item_code,[]).append({d.warehouse: d.qty})
+			backflushed_materials.setdefault(d.item_code, []).append({d.warehouse: d.qty})
 
-		po_qty = frappe.db.sql("""select qty, produced_qty, material_transferred_for_manufacturing from
-			`tabWork Order` where name=%s""", self.work_order, as_dict=1)[0]
+		po_qty = frappe.db.sql(
+			"""select qty, produced_qty, material_transferred_for_manufacturing from
+			`tabWork Order` where name=%s""",
+			self.work_order,
+			as_dict=1,
+		)[0]
 
 		manufacturing_qty = flt(po_qty.qty) or 1
 		produced_qty = flt(po_qty.produced_qty)
 		trans_qty = flt(po_qty.material_transferred_for_manufacturing) or 1
 
 		for item in transferred_materials:
-			qty= item.qty
+			qty = item.qty
 			item_code = item.original_item or item.item_code
-			req_items = frappe.get_all('Work Order Item',
-				filters={'parent': self.work_order, 'item_code': item_code},
-				fields=["required_qty", "consumed_qty"]
-				)
+			req_items = frappe.get_all(
+				"Work Order Item",
+				filters={"parent": self.work_order, "item_code": item_code},
+				fields=["required_qty", "consumed_qty"],
+			)
 
 			req_qty = flt(req_items[0].required_qty) if req_items else flt(4)
 			req_qty_each = flt(req_qty / manufacturing_qty)
@@ -1425,23 +1717,23 @@
 
 			if trans_qty and manufacturing_qty > (produced_qty + flt(self.fg_completed_qty)):
 				if qty >= req_qty:
-					qty = (req_qty/trans_qty) * flt(self.fg_completed_qty)
+					qty = (req_qty / trans_qty) * flt(self.fg_completed_qty)
 				else:
 					qty = qty - consumed_qty
 
-				if self.purpose == 'Manufacture':
+				if self.purpose == "Manufacture":
 					# If Material Consumption is booked, must pull only remaining components to finish product
 					if consumed_qty != 0:
 						remaining_qty = consumed_qty - (produced_qty * req_qty_each)
 						exhaust_qty = req_qty_each * produced_qty
-						if remaining_qty > exhaust_qty :
-							if (remaining_qty/(req_qty_each * flt(self.fg_completed_qty))) >= 1:
-								qty =0
+						if remaining_qty > exhaust_qty:
+							if (remaining_qty / (req_qty_each * flt(self.fg_completed_qty))) >= 1:
+								qty = 0
 							else:
 								qty = (req_qty_each * flt(self.fg_completed_qty)) - remaining_qty
 					else:
 						if self.flags.backflush_based_on == "Material Transferred for Manufacture":
-							qty = (item.qty/trans_qty) * flt(self.fg_completed_qty)
+							qty = (item.qty / trans_qty) * flt(self.fg_completed_qty)
 						else:
 							qty = req_qty_each * flt(self.fg_completed_qty)
 
@@ -1449,45 +1741,51 @@
 				precision = frappe.get_precision("Stock Entry Detail", "qty")
 				for d in backflushed_materials.get(item.item_code):
 					if d.get(item.warehouse) > 0:
-						if (qty > req_qty):
-							qty = ((flt(qty, precision) - flt(d.get(item.warehouse), precision))
+						if qty > req_qty:
+							qty = (
+								(flt(qty, precision) - flt(d.get(item.warehouse), precision))
 								/ (flt(trans_qty, precision) - flt(produced_qty, precision))
 							) * flt(self.fg_completed_qty)
 
 							d[item.warehouse] -= qty
 
-			if cint(frappe.get_cached_value('UOM', item.stock_uom, 'must_be_whole_number')):
+			if cint(frappe.get_cached_value("UOM", item.stock_uom, "must_be_whole_number")):
 				qty = frappe.utils.ceil(qty)
 
 			if qty > 0:
-				self.add_to_stock_entry_detail({
-					item.item_code: {
-						"from_warehouse": item.warehouse,
-						"to_warehouse": "",
-						"qty": qty,
-						"item_name": item.item_name,
-						"description": item.description,
-						"stock_uom": item.stock_uom,
-						"expense_account": item.expense_account,
-						"cost_center": item.buying_cost_center,
-						"original_item": item.original_item
+				self.add_to_stock_entry_detail(
+					{
+						item.item_code: {
+							"from_warehouse": item.warehouse,
+							"to_warehouse": "",
+							"qty": qty,
+							"item_name": item.item_name,
+							"description": item.description,
+							"stock_uom": item.stock_uom,
+							"expense_account": item.expense_account,
+							"cost_center": item.buying_cost_center,
+							"original_item": item.original_item,
+						}
 					}
-				})
+				)
 
 	def get_pending_raw_materials(self, backflush_based_on=None):
 		"""
-			issue (item quantity) that is pending to issue or desire to transfer,
-			whichever is less
+		issue (item quantity) that is pending to issue or desire to transfer,
+		whichever is less
 		"""
 		item_dict = self.get_pro_order_required_items(backflush_based_on)
 
 		max_qty = flt(self.pro_doc.qty)
 
 		allow_overproduction = False
-		overproduction_percentage = flt(frappe.db.get_single_value("Manufacturing Settings",
-			"overproduction_percentage_for_work_order"))
+		overproduction_percentage = flt(
+			frappe.db.get_single_value("Manufacturing Settings", "overproduction_percentage_for_work_order")
+		)
 
-		to_transfer_qty = flt(self.pro_doc.material_transferred_for_manufacturing) + flt(self.fg_completed_qty)
+		to_transfer_qty = flt(self.pro_doc.material_transferred_for_manufacturing) + flt(
+			self.fg_completed_qty
+		)
 		transfer_limit_qty = max_qty + ((max_qty * overproduction_percentage) / 100)
 
 		if transfer_limit_qty >= to_transfer_qty:
@@ -1497,10 +1795,14 @@
 			pending_to_issue = flt(item_details.required_qty) - flt(item_details.transferred_qty)
 			desire_to_transfer = flt(self.fg_completed_qty) * flt(item_details.required_qty) / max_qty
 
-			if (desire_to_transfer <= pending_to_issue
+			if (
+				desire_to_transfer <= pending_to_issue
 				or (desire_to_transfer > 0 and backflush_based_on == "Material Transferred for Manufacture")
-				or allow_overproduction):
-				item_dict[item]["qty"] = desire_to_transfer
+				or allow_overproduction
+			):
+				# "No need for transfer but qty still pending to transfer" case can occur
+				# when transferring multiple RM in different Stock Entries
+				item_dict[item]["qty"] = desire_to_transfer if (desire_to_transfer > 0) else pending_to_issue
 			elif pending_to_issue > 0:
 				item_dict[item]["qty"] = pending_to_issue
 			else:
@@ -1520,7 +1822,7 @@
 
 	def get_pro_order_required_items(self, backflush_based_on=None):
 		"""
-			Gets Work Order Required Items only if Stock Entry purpose is **Material Transferred for Manufacture**.
+		Gets Work Order Required Items only if Stock Entry purpose is **Material Transferred for Manufacture**.
 		"""
 		item_dict, job_card_items = frappe._dict(), []
 		work_order = frappe.get_doc("Work Order", self.work_order)
@@ -1539,7 +1841,9 @@
 				continue
 
 			transfer_pending = flt(d.required_qty) > flt(d.transferred_qty)
-			can_transfer = transfer_pending or (backflush_based_on == "Material Transferred for Manufacture")
+			can_transfer = transfer_pending or (
+				backflush_based_on == "Material Transferred for Manufacture"
+			)
 
 			if not can_transfer:
 				continue
@@ -1550,11 +1854,7 @@
 
 				if consider_job_card:
 					job_card_item = frappe.db.get_value(
-						"Job Card Item",
-						{
-							"item_code": d.item_code,
-							"parent": self.get("job_card")
-						}
+						"Job Card Item", {"item_code": d.item_code, "parent": self.get("job_card")}
 					)
 					item_row["job_card_item"] = job_card_item or None
 
@@ -1574,12 +1874,7 @@
 			return []
 
 		job_card_items = frappe.get_all(
-			"Job Card Item",
-			filters={
-				"parent": job_card
-			},
-			fields=["item_code"],
-			distinct=True
+			"Job Card Item", filters={"parent": job_card}, fields=["item_code"], distinct=True
 		)
 		return [d.item_code for d in job_card_items]
 
@@ -1588,60 +1883,86 @@
 			item_row = item_dict[d]
 			stock_uom = item_row.get("stock_uom") or frappe.db.get_value("Item", d, "stock_uom")
 
-			se_child = self.append('items')
+			se_child = self.append("items")
 			se_child.s_warehouse = item_row.get("from_warehouse")
 			se_child.t_warehouse = item_row.get("to_warehouse")
-			se_child.item_code = item_row.get('item_code') or cstr(d)
+			se_child.item_code = item_row.get("item_code") or cstr(d)
 			se_child.uom = item_row["uom"] if item_row.get("uom") else stock_uom
 			se_child.stock_uom = stock_uom
 			se_child.qty = flt(item_row["qty"], se_child.precision("qty"))
 			se_child.allow_alternative_item = item_row.get("allow_alternative_item", 0)
 			se_child.subcontracted_item = item_row.get("main_item_code")
-			se_child.cost_center = (item_row.get("cost_center") or
-				get_default_cost_center(item_row, company = self.company))
+			se_child.cost_center = item_row.get("cost_center") or get_default_cost_center(
+				item_row, company=self.company
+			)
 			se_child.is_finished_item = item_row.get("is_finished_item", 0)
 			se_child.is_scrap_item = item_row.get("is_scrap_item", 0)
 			se_child.is_process_loss = item_row.get("is_process_loss", 0)
 
-			for field in ["idx", "po_detail", "original_item", "expense_account",
-				"description", "item_name", "serial_no", "batch_no", "allow_zero_valuation_rate"]:
+			for field in [
+				"po_detail",
+				"original_item",
+				"expense_account",
+				"description",
+				"item_name",
+				"serial_no",
+				"batch_no",
+				"allow_zero_valuation_rate",
+			]:
 				if item_row.get(field):
 					se_child.set(field, item_row.get(field))
 
-			if se_child.s_warehouse==None:
+			if se_child.s_warehouse == None:
 				se_child.s_warehouse = self.from_warehouse
-			if se_child.t_warehouse==None:
+			if se_child.t_warehouse == None:
 				se_child.t_warehouse = self.to_warehouse
 
 			# in stock uom
 			se_child.conversion_factor = flt(item_row.get("conversion_factor")) or 1
-			se_child.transfer_qty = flt(item_row["qty"]*se_child.conversion_factor, se_child.precision("qty"))
+			se_child.transfer_qty = flt(
+				item_row["qty"] * se_child.conversion_factor, se_child.precision("qty")
+			)
 
-			se_child.bom_no = bom_no # to be assigned for finished item
+			se_child.bom_no = bom_no  # to be assigned for finished item
 			se_child.job_card_item = item_row.get("job_card_item") if self.get("job_card") else None
 
 	def validate_with_material_request(self):
 		for item in self.get("items"):
 			material_request = item.material_request or None
 			material_request_item = item.material_request_item or None
-			if self.purpose == 'Material Transfer' and self.outgoing_stock_entry:
-				parent_se = frappe.get_value("Stock Entry Detail", item.ste_detail, ['material_request','material_request_item'],as_dict=True)
+			if self.purpose == "Material Transfer" and self.outgoing_stock_entry:
+				parent_se = frappe.get_value(
+					"Stock Entry Detail",
+					item.ste_detail,
+					["material_request", "material_request_item"],
+					as_dict=True,
+				)
 				if parent_se:
 					material_request = parent_se.material_request
 					material_request_item = parent_se.material_request_item
 
 			if material_request:
-				mreq_item = frappe.db.get_value("Material Request Item",
+				mreq_item = frappe.db.get_value(
+					"Material Request Item",
 					{"name": material_request_item, "parent": material_request},
-					["item_code", "warehouse", "idx"], as_dict=True)
+					["item_code", "warehouse", "idx"],
+					as_dict=True,
+				)
 				if mreq_item.item_code != item.item_code:
-					frappe.throw(_("Item for row {0} does not match Material Request").format(item.idx),
-						frappe.MappingMismatchError)
+					frappe.throw(
+						_("Item for row {0} does not match Material Request").format(item.idx),
+						frappe.MappingMismatchError,
+					)
 				elif self.purpose == "Material Transfer" and self.add_to_transit:
 					continue
 
 	def validate_batch(self):
-		if self.purpose in ["Material Transfer for Manufacture", "Manufacture", "Repack", "Send to Subcontractor"]:
+		if self.purpose in [
+			"Material Transfer for Manufacture",
+			"Manufacture",
+			"Repack",
+			"Send to Subcontractor",
+		]:
 			for item in self.get("items"):
 				if item.batch_no:
 					disabled = frappe.db.get_value("Batch", item.batch_no, "disabled")
@@ -1649,30 +1970,41 @@
 						expiry_date = frappe.db.get_value("Batch", item.batch_no, "expiry_date")
 						if expiry_date:
 							if getdate(self.posting_date) > getdate(expiry_date):
-								frappe.throw(_("Batch {0} of Item {1} has expired.")
-									.format(item.batch_no, item.item_code))
+								frappe.throw(_("Batch {0} of Item {1} has expired.").format(item.batch_no, item.item_code))
 					else:
-						frappe.throw(_("Batch {0} of Item {1} is disabled.")
-							.format(item.batch_no, item.item_code))
+						frappe.throw(_("Batch {0} of Item {1} is disabled.").format(item.batch_no, item.item_code))
 
 	def update_purchase_order_supplied_items(self):
-		if (self.purchase_order and
-			(self.purpose in ['Send to Subcontractor', 'Material Transfer'] or self.is_return)):
+		if self.purchase_order and (
+			self.purpose in ["Send to Subcontractor", "Material Transfer"] or self.is_return
+		):
 
-			#Get PO Supplied Items Details
-			item_wh = frappe._dict(frappe.db.sql("""
-				select rm_item_code, reserve_warehouse
-				from `tabPurchase Order` po, `tabPurchase Order Item Supplied` poitemsup
-				where po.name = poitemsup.parent
-				and po.name = %s""", self.purchase_order))
+			# Get PO Supplied Items Details
+			po_supplied_items = frappe.db.get_all(
+				"Purchase Order Item Supplied",
+				filters={"parent": self.purchase_order},
+				fields=["name", "rm_item_code", "reserve_warehouse"],
+			)
 
+			# Get Items Supplied in Stock Entries against PO
 			supplied_items = get_supplied_items(self.purchase_order)
-			for name, item in supplied_items.items():
-				frappe.db.set_value('Purchase Order Item Supplied', name, item)
 
-			#Update reserved sub contracted quantity in bin based on Supplied Item Details and
+			for row in po_supplied_items:
+				key, item = row.name, {}
+				if not supplied_items.get(key):
+					# no stock transferred against PO Supplied Items row
+					item = {"supplied_qty": 0, "returned_qty": 0, "total_supplied_qty": 0}
+				else:
+					item = supplied_items.get(key)
+
+				frappe.db.set_value("Purchase Order Item Supplied", row.name, item)
+
+			# RM Item-Reserve Warehouse Dict
+			item_wh = {x.get("rm_item_code"): x.get("reserve_warehouse") for x in po_supplied_items}
+
 			for d in self.get("items"):
-				item_code = d.get('original_item') or d.get('item_code')
+				# Update reserved sub contracted quantity in bin based on Supplied Item Details and
+				item_code = d.get("original_item") or d.get("item_code")
 				reserve_warehouse = item_wh.get(item_code)
 				if not (reserve_warehouse and item_code):
 					continue
@@ -1680,12 +2012,17 @@
 				stock_bin.update_reserved_qty_for_sub_contracting()
 
 	def update_so_in_serial_number(self):
-		so_name, item_code = frappe.db.get_value("Work Order", self.work_order, ["sales_order", "production_item"])
+		so_name, item_code = frappe.db.get_value(
+			"Work Order", self.work_order, ["sales_order", "production_item"]
+		)
 		if so_name and item_code:
 			qty_to_reserve = get_reserved_qty_for_so(so_name, item_code)
 			if qty_to_reserve:
-				reserved_qty = frappe.db.sql("""select count(name) from `tabSerial No` where item_code=%s and
-					sales_order=%s""", (item_code, so_name))
+				reserved_qty = frappe.db.sql(
+					"""select count(name) from `tabSerial No` where item_code=%s and
+					sales_order=%s""",
+					(item_code, so_name),
+				)
 				if reserved_qty and reserved_qty[0][0]:
 					qty_to_reserve -= reserved_qty[0][0]
 				if qty_to_reserve > 0:
@@ -1696,7 +2033,7 @@
 							for serial_no in serial_nos:
 								if qty_to_reserve > 0:
 									frappe.db.set_value("Serial No", serial_no, "sales_order", so_name)
-									qty_to_reserve -=1
+									qty_to_reserve -= 1
 
 	def validate_reserved_serial_no_consumption(self):
 		for item in self.items:
@@ -1704,13 +2041,14 @@
 				for sr in get_serial_nos(item.serial_no):
 					sales_order = frappe.db.get_value("Serial No", sr, "sales_order")
 					if sales_order:
-						msg = (_("(Serial No: {0}) cannot be consumed as it's reserverd to fullfill Sales Order {1}.")
-							.format(sr, sales_order))
+						msg = _(
+							"(Serial No: {0}) cannot be consumed as it's reserverd to fullfill Sales Order {1}."
+						).format(sr, sales_order)
 
 						frappe.throw(_("Item {0} {1}").format(item.item_code, msg))
 
 	def update_transferred_qty(self):
-		if self.purpose == 'Material Transfer' and self.outgoing_stock_entry:
+		if self.purpose == "Material Transfer" and self.outgoing_stock_entry:
 			stock_entries = {}
 			stock_entries_child_list = []
 			for d in self.items:
@@ -1718,70 +2056,87 @@
 					continue
 
 				stock_entries_child_list.append(d.ste_detail)
-				transferred_qty = frappe.get_all("Stock Entry Detail", fields = ["sum(qty) as qty"],
-					filters = { 'against_stock_entry': d.against_stock_entry,
-						'ste_detail': d.ste_detail,'docstatus': 1})
+				transferred_qty = frappe.get_all(
+					"Stock Entry Detail",
+					fields=["sum(qty) as qty"],
+					filters={
+						"against_stock_entry": d.against_stock_entry,
+						"ste_detail": d.ste_detail,
+						"docstatus": 1,
+					},
+				)
 
-				stock_entries[(d.against_stock_entry, d.ste_detail)] = (transferred_qty[0].qty
-					if transferred_qty and transferred_qty[0] else 0.0) or 0.0
+				stock_entries[(d.against_stock_entry, d.ste_detail)] = (
+					transferred_qty[0].qty if transferred_qty and transferred_qty[0] else 0.0
+				) or 0.0
 
-			if not stock_entries: return None
+			if not stock_entries:
+				return None
 
-			cond = ''
+			cond = ""
 			for data, transferred_qty in stock_entries.items():
 				cond += """ WHEN (parent = %s and name = %s) THEN %s
-					""" %(frappe.db.escape(data[0]), frappe.db.escape(data[1]), transferred_qty)
+					""" % (
+					frappe.db.escape(data[0]),
+					frappe.db.escape(data[1]),
+					transferred_qty,
+				)
 
 			if stock_entries_child_list:
-				frappe.db.sql(""" UPDATE `tabStock Entry Detail`
+				frappe.db.sql(
+					""" UPDATE `tabStock Entry Detail`
 					SET
 						transferred_qty = CASE {cond} END
 					WHERE
-						name in ({ste_details}) """.format(cond=cond,
-					ste_details = ','.join(['%s'] * len(stock_entries_child_list))),
-				tuple(stock_entries_child_list))
+						name in ({ste_details}) """.format(
+						cond=cond, ste_details=",".join(["%s"] * len(stock_entries_child_list))
+					),
+					tuple(stock_entries_child_list),
+				)
 
 			args = {
-				'source_dt': 'Stock Entry Detail',
-				'target_field': 'transferred_qty',
-				'target_ref_field': 'qty',
-				'target_dt': 'Stock Entry Detail',
-				'join_field': 'ste_detail',
-				'target_parent_dt': 'Stock Entry',
-				'target_parent_field': 'per_transferred',
-				'source_field': 'qty',
-				'percent_join_field': 'against_stock_entry'
+				"source_dt": "Stock Entry Detail",
+				"target_field": "transferred_qty",
+				"target_ref_field": "qty",
+				"target_dt": "Stock Entry Detail",
+				"join_field": "ste_detail",
+				"target_parent_dt": "Stock Entry",
+				"target_parent_field": "per_transferred",
+				"source_field": "qty",
+				"percent_join_field": "against_stock_entry",
 			}
 
 			self._update_percent_field_in_targets(args, update_modified=True)
 
 	def update_quality_inspection(self):
 		if self.inspection_required:
-			reference_type = reference_name = ''
+			reference_type = reference_name = ""
 			if self.docstatus == 1:
 				reference_name = self.name
-				reference_type = 'Stock Entry'
+				reference_type = "Stock Entry"
 
 			for d in self.items:
 				if d.quality_inspection:
-					frappe.db.set_value("Quality Inspection", d.quality_inspection, {
-						'reference_type': reference_type,
-						'reference_name': reference_name
-					})
+					frappe.db.set_value(
+						"Quality Inspection",
+						d.quality_inspection,
+						{"reference_type": reference_type, "reference_name": reference_name},
+					)
+
 	def set_material_request_transfer_status(self, status):
 		material_requests = []
 		if self.outgoing_stock_entry:
-			parent_se = frappe.get_value("Stock Entry", self.outgoing_stock_entry, 'add_to_transit')
+			parent_se = frappe.get_value("Stock Entry", self.outgoing_stock_entry, "add_to_transit")
 
 		for item in self.items:
 			material_request = item.material_request or None
 			if self.purpose == "Material Transfer" and material_request not in material_requests:
 				if self.outgoing_stock_entry and parent_se:
-					material_request = frappe.get_value("Stock Entry Detail", item.ste_detail, 'material_request')
+					material_request = frappe.get_value("Stock Entry Detail", item.ste_detail, "material_request")
 
 			if material_request and material_request not in material_requests:
 				material_requests.append(material_request)
-				frappe.db.set_value('Material Request', material_request, 'transfer_status', status)
+				frappe.db.set_value("Material Request", material_request, "transfer_status", status)
 
 	def update_items_for_process_loss(self):
 		process_loss_dict = {}
@@ -1789,7 +2144,9 @@
 			if not d.is_process_loss:
 				continue
 
-			scrap_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_scrap_warehouse")
+			scrap_warehouse = frappe.db.get_single_value(
+				"Manufacturing Settings", "default_scrap_warehouse"
+			)
 			if scrap_warehouse is not None:
 				d.t_warehouse = scrap_warehouse
 			d.is_scrap_item = 0
@@ -1814,14 +2171,22 @@
 		for row in self.items:
 			if row.is_finished_item and row.item_code == self.pro_doc.production_item:
 				if args.get("serial_no"):
-					row.serial_no = '\n'.join(args["serial_no"][0: cint(row.qty)])
+					row.serial_no = "\n".join(args["serial_no"][0 : cint(row.qty)])
 
 	def get_serial_nos_for_fg(self, args):
-		fields = ["`tabStock Entry`.`name`", "`tabStock Entry Detail`.`qty`",
-			"`tabStock Entry Detail`.`serial_no`", "`tabStock Entry Detail`.`batch_no`"]
+		fields = [
+			"`tabStock Entry`.`name`",
+			"`tabStock Entry Detail`.`qty`",
+			"`tabStock Entry Detail`.`serial_no`",
+			"`tabStock Entry Detail`.`batch_no`",
+		]
 
-		filters = [["Stock Entry","work_order","=",self.work_order], ["Stock Entry","purpose","=","Manufacture"],
-			["Stock Entry","docstatus","=",1], ["Stock Entry Detail","item_code","=",self.pro_doc.production_item]]
+		filters = [
+			["Stock Entry", "work_order", "=", self.work_order],
+			["Stock Entry", "purpose", "=", "Manufacture"],
+			["Stock Entry", "docstatus", "=", 1],
+			["Stock Entry Detail", "item_code", "=", self.pro_doc.production_item],
+		]
 
 		stock_entries = frappe.get_all("Stock Entry", fields=fields, filters=filters)
 
@@ -1836,85 +2201,105 @@
 
 		return sorted(list(set(get_serial_nos(self.pro_doc.serial_no)) - set(used_serial_nos)))
 
+	def set_missing_values(self):
+		"Updates rate and availability of all the items of mapped doc."
+		self.set_transfer_qty()
+		self.set_actual_qty()
+		self.calculate_rate_and_amount()
+
+
 @frappe.whitelist()
 def move_sample_to_retention_warehouse(company, items):
 	if isinstance(items, str):
 		items = json.loads(items)
-	retention_warehouse = frappe.db.get_single_value('Stock Settings', 'sample_retention_warehouse')
+	retention_warehouse = frappe.db.get_single_value("Stock Settings", "sample_retention_warehouse")
 	stock_entry = frappe.new_doc("Stock Entry")
 	stock_entry.company = company
 	stock_entry.purpose = "Material Transfer"
 	stock_entry.set_stock_entry_type()
 	for item in items:
-		if item.get('sample_quantity') and item.get('batch_no'):
-			sample_quantity = validate_sample_quantity(item.get('item_code'), item.get('sample_quantity'),
-				item.get('transfer_qty') or item.get('qty'), item.get('batch_no'))
+		if item.get("sample_quantity") and item.get("batch_no"):
+			sample_quantity = validate_sample_quantity(
+				item.get("item_code"),
+				item.get("sample_quantity"),
+				item.get("transfer_qty") or item.get("qty"),
+				item.get("batch_no"),
+			)
 			if sample_quantity:
-				sample_serial_nos = ''
-				if item.get('serial_no'):
-					serial_nos = (item.get('serial_no')).split()
-					if serial_nos and len(serial_nos) > item.get('sample_quantity'):
-						serial_no_list = serial_nos[:-(len(serial_nos)-item.get('sample_quantity'))]
-						sample_serial_nos = '\n'.join(serial_no_list)
+				sample_serial_nos = ""
+				if item.get("serial_no"):
+					serial_nos = (item.get("serial_no")).split()
+					if serial_nos and len(serial_nos) > item.get("sample_quantity"):
+						serial_no_list = serial_nos[: -(len(serial_nos) - item.get("sample_quantity"))]
+						sample_serial_nos = "\n".join(serial_no_list)
 
-				stock_entry.append("items", {
-					"item_code": item.get('item_code'),
-					"s_warehouse": item.get('t_warehouse'),
-					"t_warehouse": retention_warehouse,
-					"qty": item.get('sample_quantity'),
-					"basic_rate": item.get('valuation_rate'),
-					'uom': item.get('uom'),
-					'stock_uom': item.get('stock_uom'),
-					"conversion_factor": 1.0,
-					"serial_no": sample_serial_nos,
-					'batch_no': item.get('batch_no')
-				})
-	if stock_entry.get('items'):
+				stock_entry.append(
+					"items",
+					{
+						"item_code": item.get("item_code"),
+						"s_warehouse": item.get("t_warehouse"),
+						"t_warehouse": retention_warehouse,
+						"qty": item.get("sample_quantity"),
+						"basic_rate": item.get("valuation_rate"),
+						"uom": item.get("uom"),
+						"stock_uom": item.get("stock_uom"),
+						"conversion_factor": 1.0,
+						"serial_no": sample_serial_nos,
+						"batch_no": item.get("batch_no"),
+					},
+				)
+	if stock_entry.get("items"):
 		return stock_entry.as_dict()
 
+
 @frappe.whitelist()
 def make_stock_in_entry(source_name, target_doc=None):
-
 	def set_missing_values(source, target):
 		target.set_stock_entry_type()
+		target.set_missing_values()
 
 	def update_item(source_doc, target_doc, source_parent):
-		target_doc.t_warehouse = ''
+		target_doc.t_warehouse = ""
 
-		if source_doc.material_request_item and source_doc.material_request :
-			add_to_transit = frappe.db.get_value('Stock Entry', source_name, 'add_to_transit')
+		if source_doc.material_request_item and source_doc.material_request:
+			add_to_transit = frappe.db.get_value("Stock Entry", source_name, "add_to_transit")
 			if add_to_transit:
-				warehouse = frappe.get_value('Material Request Item', source_doc.material_request_item, 'warehouse')
+				warehouse = frappe.get_value(
+					"Material Request Item", source_doc.material_request_item, "warehouse"
+				)
 				target_doc.t_warehouse = warehouse
 
 		target_doc.s_warehouse = source_doc.t_warehouse
 		target_doc.qty = source_doc.qty - source_doc.transferred_qty
 
-	doclist = get_mapped_doc("Stock Entry", source_name, 	{
-		"Stock Entry": {
-			"doctype": "Stock Entry",
-			"field_map": {
-				"name": "outgoing_stock_entry"
+	doclist = get_mapped_doc(
+		"Stock Entry",
+		source_name,
+		{
+			"Stock Entry": {
+				"doctype": "Stock Entry",
+				"field_map": {"name": "outgoing_stock_entry"},
+				"validation": {"docstatus": ["=", 1]},
 			},
-			"validation": {
-				"docstatus": ["=", 1]
-			}
-		},
-		"Stock Entry Detail": {
-			"doctype": "Stock Entry Detail",
-			"field_map": {
-				"name": "ste_detail",
-				"parent": "against_stock_entry",
-				"serial_no": "serial_no",
-				"batch_no": "batch_no"
+			"Stock Entry Detail": {
+				"doctype": "Stock Entry Detail",
+				"field_map": {
+					"name": "ste_detail",
+					"parent": "against_stock_entry",
+					"serial_no": "serial_no",
+					"batch_no": "batch_no",
+				},
+				"postprocess": update_item,
+				"condition": lambda doc: flt(doc.qty) - flt(doc.transferred_qty) > 0.01,
 			},
-			"postprocess": update_item,
-			"condition": lambda doc: flt(doc.qty) - flt(doc.transferred_qty) > 0.01
 		},
-	}, target_doc, set_missing_values)
+		target_doc,
+		set_missing_values,
+	)
 
 	return doclist
 
+
 @frappe.whitelist()
 def get_work_order_details(work_order, company):
 	work_order = frappe.get_doc("Work Order", work_order)
@@ -1926,9 +2311,10 @@
 		"use_multi_level_bom": work_order.use_multi_level_bom,
 		"wip_warehouse": work_order.wip_warehouse,
 		"fg_warehouse": work_order.fg_warehouse,
-		"fg_completed_qty": pending_qty_to_produce
+		"fg_completed_qty": pending_qty_to_produce,
 	}
 
+
 def get_operating_cost_per_unit(work_order=None, bom_no=None):
 	operating_cost_per_unit = 0
 	if work_order:
@@ -1947,54 +2333,78 @@
 		if bom.quantity:
 			operating_cost_per_unit = flt(bom.operating_cost) / flt(bom.quantity)
 
-	if work_order and work_order.produced_qty and cint(frappe.db.get_single_value('Manufacturing Settings',
-		'add_corrective_operation_cost_in_finished_good_valuation')):
-		operating_cost_per_unit += flt(work_order.corrective_operation_cost) / flt(work_order.produced_qty)
+	if (
+		work_order
+		and work_order.produced_qty
+		and cint(
+			frappe.db.get_single_value(
+				"Manufacturing Settings", "add_corrective_operation_cost_in_finished_good_valuation"
+			)
+		)
+	):
+		operating_cost_per_unit += flt(work_order.corrective_operation_cost) / flt(
+			work_order.produced_qty
+		)
 
 	return operating_cost_per_unit
 
+
 def get_used_alternative_items(purchase_order=None, work_order=None):
 	cond = ""
 
 	if purchase_order:
-		cond = "and ste.purpose = 'Send to Subcontractor' and ste.purchase_order = '{0}'".format(purchase_order)
+		cond = "and ste.purpose = 'Send to Subcontractor' and ste.purchase_order = '{0}'".format(
+			purchase_order
+		)
 	elif work_order:
-		cond = "and ste.purpose = 'Material Transfer for Manufacture' and ste.work_order = '{0}'".format(work_order)
+		cond = "and ste.purpose = 'Material Transfer for Manufacture' and ste.work_order = '{0}'".format(
+			work_order
+		)
 
-	if not cond: return {}
+	if not cond:
+		return {}
 
 	used_alternative_items = {}
-	data = frappe.db.sql(""" select sted.original_item, sted.uom, sted.conversion_factor,
+	data = frappe.db.sql(
+		""" select sted.original_item, sted.uom, sted.conversion_factor,
 			sted.item_code, sted.item_name, sted.conversion_factor,sted.stock_uom, sted.description
 		from
 			`tabStock Entry` ste, `tabStock Entry Detail` sted
 		where
 			sted.parent = ste.name and ste.docstatus = 1 and sted.original_item !=  sted.item_code
-			{0} """.format(cond), as_dict=1)
+			{0} """.format(
+			cond
+		),
+		as_dict=1,
+	)
 
 	for d in data:
 		used_alternative_items[d.original_item] = d
 
 	return used_alternative_items
 
+
 def get_valuation_rate_for_finished_good_entry(work_order):
-	work_order_qty = flt(frappe.get_cached_value("Work Order",
-		work_order, 'material_transferred_for_manufacturing'))
+	work_order_qty = flt(
+		frappe.get_cached_value("Work Order", work_order, "material_transferred_for_manufacturing")
+	)
 
 	field = "(SUM(total_outgoing_value) / %s) as valuation_rate" % (work_order_qty)
 
-	stock_data = frappe.get_all("Stock Entry",
-		fields = field,
-		filters = {
+	stock_data = frappe.get_all(
+		"Stock Entry",
+		fields=field,
+		filters={
 			"docstatus": 1,
 			"purpose": "Material Transfer for Manufacture",
-			"work_order": work_order
-		}
+			"work_order": work_order,
+		},
 	)
 
 	if stock_data:
 		return stock_data[0].valuation_rate
 
+
 @frappe.whitelist()
 def get_uom_details(item_code, uom, qty):
 	"""Returns dict `{"conversion_factor": [value], "transfer_qty": qty * [value]}`
@@ -2003,24 +2413,31 @@
 	conversion_factor = get_conversion_factor(item_code, uom).get("conversion_factor")
 
 	if not conversion_factor:
-		frappe.msgprint(_("UOM coversion factor required for UOM: {0} in Item: {1}")
-			.format(uom, item_code))
-		ret = {'uom' : ''}
+		frappe.msgprint(
+			_("UOM coversion factor required for UOM: {0} in Item: {1}").format(uom, item_code)
+		)
+		ret = {"uom": ""}
 	else:
 		ret = {
-			'conversion_factor'		: flt(conversion_factor),
-			'transfer_qty'			: flt(qty) * flt(conversion_factor)
+			"conversion_factor": flt(conversion_factor),
+			"transfer_qty": flt(qty) * flt(conversion_factor),
 		}
 	return ret
 
+
 @frappe.whitelist()
 def get_expired_batch_items():
-	return frappe.db.sql("""select b.item, sum(sle.actual_qty) as qty, sle.batch_no, sle.warehouse, sle.stock_uom\
+	return frappe.db.sql(
+		"""select b.item, sum(sle.actual_qty) as qty, sle.batch_no, sle.warehouse, sle.stock_uom\
 	from `tabBatch` b, `tabStock Ledger Entry` sle
 	where b.expiry_date <= %s
 	and b.expiry_date is not NULL
 	and b.batch_id = sle.batch_no and sle.is_cancelled = 0
-	group by sle.warehouse, sle.item_code, sle.batch_no""",(nowdate()), as_dict=1)
+	group by sle.warehouse, sle.item_code, sle.batch_no""",
+		(nowdate()),
+		as_dict=1,
+	)
+
 
 @frappe.whitelist()
 def get_warehouse_details(args):
@@ -2031,51 +2448,73 @@
 
 	ret = {}
 	if args.warehouse and args.item_code:
-		args.update({
-			"posting_date": args.posting_date,
-			"posting_time": args.posting_time,
-		})
+		args.update(
+			{
+				"posting_date": args.posting_date,
+				"posting_time": args.posting_time,
+			}
+		)
 		ret = {
-			"actual_qty" : get_previous_sle(args).get("qty_after_transaction") or 0,
-			"basic_rate" : get_incoming_rate(args)
+			"actual_qty": get_previous_sle(args).get("qty_after_transaction") or 0,
+			"basic_rate": get_incoming_rate(args),
 		}
 	return ret
 
+
 @frappe.whitelist()
-def validate_sample_quantity(item_code, sample_quantity, qty, batch_no = None):
+def validate_sample_quantity(item_code, sample_quantity, qty, batch_no=None):
 	if cint(qty) < cint(sample_quantity):
-		frappe.throw(_("Sample quantity {0} cannot be more than received quantity {1}").format(sample_quantity, qty))
-	retention_warehouse = frappe.db.get_single_value('Stock Settings', 'sample_retention_warehouse')
+		frappe.throw(
+			_("Sample quantity {0} cannot be more than received quantity {1}").format(sample_quantity, qty)
+		)
+	retention_warehouse = frappe.db.get_single_value("Stock Settings", "sample_retention_warehouse")
 	retainted_qty = 0
 	if batch_no:
 		retainted_qty = get_batch_qty(batch_no, retention_warehouse, item_code)
-	max_retain_qty = frappe.get_value('Item', item_code, 'sample_quantity')
+	max_retain_qty = frappe.get_value("Item", item_code, "sample_quantity")
 	if retainted_qty >= max_retain_qty:
-		frappe.msgprint(_("Maximum Samples - {0} have already been retained for Batch {1} and Item {2} in Batch {3}.").
-			format(retainted_qty, batch_no, item_code, batch_no), alert=True)
+		frappe.msgprint(
+			_(
+				"Maximum Samples - {0} have already been retained for Batch {1} and Item {2} in Batch {3}."
+			).format(retainted_qty, batch_no, item_code, batch_no),
+			alert=True,
+		)
 		sample_quantity = 0
-	qty_diff = max_retain_qty-retainted_qty
+	qty_diff = max_retain_qty - retainted_qty
 	if cint(sample_quantity) > cint(qty_diff):
-		frappe.msgprint(_("Maximum Samples - {0} can be retained for Batch {1} and Item {2}.").
-			format(max_retain_qty, batch_no, item_code), alert=True)
+		frappe.msgprint(
+			_("Maximum Samples - {0} can be retained for Batch {1} and Item {2}.").format(
+				max_retain_qty, batch_no, item_code
+			),
+			alert=True,
+		)
 		sample_quantity = qty_diff
 	return sample_quantity
 
-def get_supplied_items(purchase_order):
-	fields = ['`tabStock Entry Detail`.`transfer_qty`', '`tabStock Entry`.`is_return`',
-			'`tabStock Entry Detail`.`po_detail`', '`tabStock Entry Detail`.`item_code`']
 
-	filters = [['Stock Entry', 'docstatus', '=', 1], ['Stock Entry', 'purchase_order', '=', purchase_order]]
+def get_supplied_items(purchase_order):
+	fields = [
+		"`tabStock Entry Detail`.`transfer_qty`",
+		"`tabStock Entry`.`is_return`",
+		"`tabStock Entry Detail`.`po_detail`",
+		"`tabStock Entry Detail`.`item_code`",
+	]
+
+	filters = [
+		["Stock Entry", "docstatus", "=", 1],
+		["Stock Entry", "purchase_order", "=", purchase_order],
+	]
 
 	supplied_item_details = {}
-	for row in frappe.get_all('Stock Entry', fields = fields, filters = filters):
+	for row in frappe.get_all("Stock Entry", fields=fields, filters=filters):
 		if not row.po_detail:
 			continue
 
 		key = row.po_detail
 		if key not in supplied_item_details:
-			supplied_item_details.setdefault(key,
-				frappe._dict({'supplied_qty': 0, 'returned_qty':0, 'total_supplied_qty':0}))
+			supplied_item_details.setdefault(
+				key, frappe._dict({"supplied_qty": 0, "returned_qty": 0, "total_supplied_qty": 0})
+			)
 
 		supplied_item = supplied_item_details[key]
 
@@ -2084,6 +2523,8 @@
 		else:
 			supplied_item.supplied_qty += row.transfer_qty
 
-		supplied_item.total_supplied_qty = flt(supplied_item.supplied_qty) - flt(supplied_item.returned_qty)
+		supplied_item.total_supplied_qty = flt(supplied_item.supplied_qty) - flt(
+			supplied_item.returned_qty
+		)
 
 	return supplied_item_details
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
index 17266ad..41a3b89 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
@@ -2,15 +2,41 @@
 # See license.txt
 
 
+from typing import TYPE_CHECKING, Optional, overload
+
 import frappe
 from frappe.utils import cint, flt
 
 import erpnext
 
+if TYPE_CHECKING:
+	from erpnext.stock.doctype.stock_entry.stock_entry import StockEntry
+
+
+@overload
+def make_stock_entry(
+	*,
+	item_code: str,
+	qty: float,
+	company: Optional[str] = None,
+	from_warehouse: Optional[str] = None,
+	to_warehouse: Optional[str] = None,
+	rate: Optional[float] = None,
+	serial_no: Optional[str] = None,
+	batch_no: Optional[str] = None,
+	posting_date: Optional[str] = None,
+	posting_time: Optional[str] = None,
+	purpose: Optional[str] = None,
+	do_not_save: bool = False,
+	do_not_submit: bool = False,
+	inspection_required: bool = False,
+) -> "StockEntry":
+	...
+
 
 @frappe.whitelist()
 def make_stock_entry(**args):
-	'''Helper function to make a Stock Entry
+	"""Helper function to make a Stock Entry
 
 	:item_code: Item to be moved
 	:qty: Qty to be moved
@@ -25,16 +51,16 @@
 	:purpose: Optional
 	:do_not_save: Optional flag
 	:do_not_submit: Optional flag
-	'''
+	"""
 
 	def process_serial_numbers(serial_nos_list):
 		serial_nos_list = [
-			'\n'.join(serial_num['serial_no'] for serial_num in serial_nos_list if serial_num.serial_no)
+			"\n".join(serial_num["serial_no"] for serial_num in serial_nos_list if serial_num.serial_no)
 		]
 
-		uniques = list(set(serial_nos_list[0].split('\n')))
+		uniques = list(set(serial_nos_list[0].split("\n")))
 
-		return '\n'.join(uniques)
+		return "\n".join(uniques)
 
 	s = frappe.new_doc("Stock Entry")
 	args = frappe._dict(args)
@@ -60,7 +86,7 @@
 		s.apply_putaway_rule = args.apply_putaway_rule
 
 	if isinstance(args.qty, str):
-		if '.' in args.qty:
+		if "." in args.qty:
 			args.qty = flt(args.qty)
 		else:
 			args.qty = cint(args.qty)
@@ -79,16 +105,16 @@
 	# company
 	if not args.company:
 		if args.source:
-			args.company = frappe.db.get_value('Warehouse', args.source, 'company')
+			args.company = frappe.db.get_value("Warehouse", args.source, "company")
 		elif args.target:
-			args.company = frappe.db.get_value('Warehouse', args.target, 'company')
+			args.company = frappe.db.get_value("Warehouse", args.target, "company")
 
 	# set vales from test
 	if frappe.flags.in_test:
 		if not args.company:
-			args.company = '_Test Company'
+			args.company = "_Test Company"
 		if not args.item:
-			args.item = '_Test Item'
+			args.item = "_Test Item"
 
 	s.company = args.company or erpnext.get_default_company()
 	s.purchase_receipt_no = args.purchase_receipt_no
@@ -96,42 +122,43 @@
 	s.sales_invoice_no = args.sales_invoice_no
 	s.is_opening = args.is_opening or "No"
 	if not args.cost_center:
-		args.cost_center = frappe.get_value('Company', s.company, 'cost_center')
+		args.cost_center = frappe.get_value("Company", s.company, "cost_center")
 
 	if not args.expense_account and s.is_opening == "No":
-		args.expense_account = frappe.get_value('Company', s.company, 'stock_adjustment_account')
+		args.expense_account = frappe.get_value("Company", s.company, "stock_adjustment_account")
 
 	# We can find out the serial number using the batch source document
 	serial_number = args.serial_no
 
 	if not args.serial_no and args.qty and args.batch_no:
 		serial_number_list = frappe.get_list(
-			doctype='Stock Ledger Entry',
-			fields=['serial_no'],
-			filters={
-				'batch_no': args.batch_no,
-				'warehouse': args.from_warehouse
-			}
+			doctype="Stock Ledger Entry",
+			fields=["serial_no"],
+			filters={"batch_no": args.batch_no, "warehouse": args.from_warehouse},
 		)
 		serial_number = process_serial_numbers(serial_number_list)
 
 	args.serial_no = serial_number
 
-	s.append("items", {
-		"item_code": args.item,
-		"s_warehouse": args.source,
-		"t_warehouse": args.target,
-		"qty": args.qty,
-		"basic_rate": args.rate or args.basic_rate,
-		"conversion_factor": args.conversion_factor or 1.0,
-		"transfer_qty": flt(args.qty) * (flt(args.conversion_factor) or 1.0),
-		"serial_no": args.serial_no,
-		'batch_no': args.batch_no,
-		'cost_center': args.cost_center,
-		'expense_account': args.expense_account
-	})
+	s.append(
+		"items",
+		{
+			"item_code": args.item,
+			"s_warehouse": args.source,
+			"t_warehouse": args.target,
+			"qty": args.qty,
+			"basic_rate": args.rate or args.basic_rate,
+			"conversion_factor": args.conversion_factor or 1.0,
+			"transfer_qty": flt(args.qty) * (flt(args.conversion_factor) or 1.0),
+			"serial_no": args.serial_no,
+			"batch_no": args.batch_no,
+			"cost_center": args.cost_center,
+			"expense_account": args.expense_account,
+		},
+	)
 
 	s.set_stock_entry_type()
+
 	if not args.do_not_save:
 		s.insert()
 		if not args.do_not_submit:
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 54c0e43..6f4c910 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -2,8 +2,6 @@
 # License: GNU General Public License v3. See license.txt
 
 
-import unittest
-
 import frappe
 from frappe.permissions import add_user_permission, remove_user_permission
 from frappe.tests.utils import FrappeTestCase, change_settings
@@ -12,6 +10,7 @@
 from erpnext.accounts.doctype.account.test_account import get_inventory_account
 from erpnext.stock.doctype.item.test_item import (
 	create_item,
+	make_item,
 	make_item_variant,
 	set_item_variant_settings,
 )
@@ -38,51 +37,56 @@
 		condition += "`{0}`=%s".format(key)
 		values.append(value)
 
-	return frappe.db.sql("""select * from `tabStock Ledger Entry` %s
-		order by timestamp(posting_date, posting_time) desc, creation desc limit 1"""% condition,
-		values, as_dict=1)
+	return frappe.db.sql(
+		"""select * from `tabStock Ledger Entry` %s
+		order by timestamp(posting_date, posting_time) desc, creation desc limit 1"""
+		% condition,
+		values,
+		as_dict=1,
+	)
+
 
 class TestStockEntry(FrappeTestCase):
 	def tearDown(self):
 		frappe.db.rollback()
 		frappe.set_user("Administrator")
-		frappe.db.set_value("Manufacturing Settings", None, "material_consumption", "0")
 
 	def test_fifo(self):
 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
 		item_code = "_Test Item 2"
 		warehouse = "_Test Warehouse - _TC"
 
-		create_stock_reconciliation(item_code="_Test Item 2", warehouse="_Test Warehouse - _TC",
-			qty=0, rate=100)
+		create_stock_reconciliation(
+			item_code="_Test Item 2", warehouse="_Test Warehouse - _TC", qty=0, rate=100
+		)
 
 		make_stock_entry(item_code=item_code, target=warehouse, qty=1, basic_rate=10)
-		sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
+		sle = get_sle(item_code=item_code, warehouse=warehouse)[0]
 
 		self.assertEqual([[1, 10]], frappe.safe_eval(sle.stock_queue))
 
 		# negative qty
 		make_stock_entry(item_code=item_code, source=warehouse, qty=2, basic_rate=10)
-		sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
+		sle = get_sle(item_code=item_code, warehouse=warehouse)[0]
 
 		self.assertEqual([[-1, 10]], frappe.safe_eval(sle.stock_queue))
 
 		# further negative
 		make_stock_entry(item_code=item_code, source=warehouse, qty=1)
-		sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
+		sle = get_sle(item_code=item_code, warehouse=warehouse)[0]
 
 		self.assertEqual([[-2, 10]], frappe.safe_eval(sle.stock_queue))
 
 		# move stock to positive
 		make_stock_entry(item_code=item_code, target=warehouse, qty=3, basic_rate=20)
-		sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
+		sle = get_sle(item_code=item_code, warehouse=warehouse)[0]
 		self.assertEqual([[1, 20]], frappe.safe_eval(sle.stock_queue))
 
 		# incoming entry with diff rate
 		make_stock_entry(item_code=item_code, target=warehouse, qty=1, basic_rate=30)
-		sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
+		sle = get_sle(item_code=item_code, warehouse=warehouse)[0]
 
-		self.assertEqual([[1, 20],[1, 30]], frappe.safe_eval(sle.stock_queue))
+		self.assertEqual([[1, 20], [1, 30]], frappe.safe_eval(sle.stock_queue))
 
 		frappe.db.set_default("allow_negative_stock", 0)
 
@@ -92,37 +96,48 @@
 		self._test_auto_material_request("_Test Item", material_request_type="Transfer")
 
 	def test_auto_material_request_for_variant(self):
-		fields = [{'field_name': 'reorder_levels'}]
+		fields = [{"field_name": "reorder_levels"}]
 		set_item_variant_settings(fields)
 		make_item_variant()
 		template = frappe.get_doc("Item", "_Test Variant Item")
 
 		if not template.reorder_levels:
-			template.append('reorder_levels', {
-				"material_request_type": "Purchase",
-				"warehouse": "_Test Warehouse - _TC",
-				"warehouse_reorder_level": 20,
-				"warehouse_reorder_qty": 20
-			})
+			template.append(
+				"reorder_levels",
+				{
+					"material_request_type": "Purchase",
+					"warehouse": "_Test Warehouse - _TC",
+					"warehouse_reorder_level": 20,
+					"warehouse_reorder_qty": 20,
+				},
+			)
 
 		template.save()
 		self._test_auto_material_request("_Test Variant Item-S")
 
 	def test_auto_material_request_for_warehouse_group(self):
-		self._test_auto_material_request("_Test Item Warehouse Group Wise Reorder", warehouse="_Test Warehouse Group-C1 - _TC")
+		self._test_auto_material_request(
+			"_Test Item Warehouse Group Wise Reorder", warehouse="_Test Warehouse Group-C1 - _TC"
+		)
 
-	def _test_auto_material_request(self, item_code, material_request_type="Purchase", warehouse="_Test Warehouse - _TC"):
+	def _test_auto_material_request(
+		self, item_code, material_request_type="Purchase", warehouse="_Test Warehouse - _TC"
+	):
 		variant = frappe.get_doc("Item", item_code)
 
-		projected_qty, actual_qty = frappe.db.get_value("Bin", {"item_code": item_code,
-			"warehouse": warehouse}, ["projected_qty", "actual_qty"]) or [0, 0]
+		projected_qty, actual_qty = frappe.db.get_value(
+			"Bin", {"item_code": item_code, "warehouse": warehouse}, ["projected_qty", "actual_qty"]
+		) or [0, 0]
 
 		# stock entry reqd for auto-reorder
-		create_stock_reconciliation(item_code=item_code, warehouse=warehouse,
-			qty = actual_qty + abs(projected_qty) + 10, rate=100)
+		create_stock_reconciliation(
+			item_code=item_code, warehouse=warehouse, qty=actual_qty + abs(projected_qty) + 10, rate=100
+		)
 
-		projected_qty = frappe.db.get_value("Bin", {"item_code": item_code,
-			"warehouse": warehouse}, "projected_qty") or 0
+		projected_qty = (
+			frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "projected_qty")
+			or 0
+		)
 
 		frappe.db.set_value("Stock Settings", None, "auto_indent", 1)
 
@@ -133,6 +148,7 @@
 			variant.save()
 
 		from erpnext.stock.reorder_item import reorder_item
+
 		mr_list = reorder_item()
 
 		frappe.db.set_value("Stock Settings", None, "auto_indent", 0)
@@ -145,65 +161,113 @@
 		self.assertTrue(item_code in items)
 
 	def test_material_receipt_gl_entry(self):
-		company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
+		company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")
 
-		mr = make_stock_entry(item_code="_Test Item", target="Stores - TCP1", company= company,
-			qty=50, basic_rate=100, expense_account="Stock Adjustment - TCP1")
+		mr = make_stock_entry(
+			item_code="_Test Item",
+			target="Stores - TCP1",
+			company=company,
+			qty=50,
+			basic_rate=100,
+			expense_account="Stock Adjustment - TCP1",
+		)
 
 		stock_in_hand_account = get_inventory_account(mr.company, mr.get("items")[0].t_warehouse)
-		self.check_stock_ledger_entries("Stock Entry", mr.name,
-			[["_Test Item", "Stores - TCP1", 50.0]])
+		self.check_stock_ledger_entries("Stock Entry", mr.name, [["_Test Item", "Stores - TCP1", 50.0]])
 
-		self.check_gl_entries("Stock Entry", mr.name,
-			sorted([
-				[stock_in_hand_account, 5000.0, 0.0],
-				["Stock Adjustment - TCP1", 0.0, 5000.0]
-			])
+		self.check_gl_entries(
+			"Stock Entry",
+			mr.name,
+			sorted([[stock_in_hand_account, 5000.0, 0.0], ["Stock Adjustment - TCP1", 0.0, 5000.0]]),
 		)
 
 		mr.cancel()
 
-		self.assertTrue(frappe.db.sql("""select * from `tabStock Ledger Entry`
-			where voucher_type='Stock Entry' and voucher_no=%s""", mr.name))
+		self.assertTrue(
+			frappe.db.sql(
+				"""select * from `tabStock Ledger Entry`
+			where voucher_type='Stock Entry' and voucher_no=%s""",
+				mr.name,
+			)
+		)
 
-		self.assertTrue(frappe.db.sql("""select * from `tabGL Entry`
-			where voucher_type='Stock Entry' and voucher_no=%s""", mr.name))
+		self.assertTrue(
+			frappe.db.sql(
+				"""select * from `tabGL Entry`
+			where voucher_type='Stock Entry' and voucher_no=%s""",
+				mr.name,
+			)
+		)
 
 	def test_material_issue_gl_entry(self):
-		company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
-		make_stock_entry(item_code="_Test Item", target="Stores - TCP1", company= company,
-			qty=50, basic_rate=100, expense_account="Stock Adjustment - TCP1")
+		company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")
+		make_stock_entry(
+			item_code="_Test Item",
+			target="Stores - TCP1",
+			company=company,
+			qty=50,
+			basic_rate=100,
+			expense_account="Stock Adjustment - TCP1",
+		)
 
-		mi = make_stock_entry(item_code="_Test Item", source="Stores - TCP1", company=company,
-			qty=40, expense_account="Stock Adjustment - TCP1")
+		mi = make_stock_entry(
+			item_code="_Test Item",
+			source="Stores - TCP1",
+			company=company,
+			qty=40,
+			expense_account="Stock Adjustment - TCP1",
+		)
 
-		self.check_stock_ledger_entries("Stock Entry", mi.name,
-			[["_Test Item", "Stores - TCP1", -40.0]])
+		self.check_stock_ledger_entries("Stock Entry", mi.name, [["_Test Item", "Stores - TCP1", -40.0]])
 
 		stock_in_hand_account = get_inventory_account(mi.company, "Stores - TCP1")
-		stock_value_diff = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Stock Entry",
-			"voucher_no": mi.name}, "stock_value_difference"))
+		stock_value_diff = abs(
+			frappe.db.get_value(
+				"Stock Ledger Entry",
+				{"voucher_type": "Stock Entry", "voucher_no": mi.name},
+				"stock_value_difference",
+			)
+		)
 
-		self.check_gl_entries("Stock Entry", mi.name,
-			sorted([
-				[stock_in_hand_account, 0.0, stock_value_diff],
-				["Stock Adjustment - TCP1", stock_value_diff, 0.0]
-			])
+		self.check_gl_entries(
+			"Stock Entry",
+			mi.name,
+			sorted(
+				[
+					[stock_in_hand_account, 0.0, stock_value_diff],
+					["Stock Adjustment - TCP1", stock_value_diff, 0.0],
+				]
+			),
 		)
 		mi.cancel()
 
 	def test_material_transfer_gl_entry(self):
-		company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
+		company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")
 
-		item_code = 'Hand Sanitizer - 001'
-		create_item(item_code =item_code, is_stock_item = 1,
-			is_purchase_item=1, opening_stock=1000, valuation_rate=10, company=company, warehouse="Stores - TCP1")
+		item_code = "Hand Sanitizer - 001"
+		create_item(
+			item_code=item_code,
+			is_stock_item=1,
+			is_purchase_item=1,
+			opening_stock=1000,
+			valuation_rate=10,
+			company=company,
+			warehouse="Stores - TCP1",
+		)
 
-		mtn = make_stock_entry(item_code=item_code, source="Stores - TCP1",
-			target="Finished Goods - TCP1", qty=45, company=company)
+		mtn = make_stock_entry(
+			item_code=item_code,
+			source="Stores - TCP1",
+			target="Finished Goods - TCP1",
+			qty=45,
+			company=company,
+		)
 
-		self.check_stock_ledger_entries("Stock Entry", mtn.name,
-			[[item_code, "Stores - TCP1", -45.0], [item_code, "Finished Goods - TCP1", 45.0]])
+		self.check_stock_ledger_entries(
+			"Stock Entry",
+			mtn.name,
+			[[item_code, "Stores - TCP1", -45.0], [item_code, "Finished Goods - TCP1", 45.0]],
+		)
 
 		source_warehouse_account = get_inventory_account(mtn.company, mtn.get("items")[0].s_warehouse)
 
@@ -211,18 +275,33 @@
 
 		if source_warehouse_account == target_warehouse_account:
 			# no gl entry as both source and target warehouse has linked to same account.
-			self.assertFalse(frappe.db.sql("""select * from `tabGL Entry`
-				where voucher_type='Stock Entry' and voucher_no=%s""", mtn.name, as_dict=1))
+			self.assertFalse(
+				frappe.db.sql(
+					"""select * from `tabGL Entry`
+				where voucher_type='Stock Entry' and voucher_no=%s""",
+					mtn.name,
+					as_dict=1,
+				)
+			)
 
 		else:
-			stock_value_diff = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Stock Entry",
-				"voucher_no": mtn.name, "warehouse": "Stores - TCP1"}, "stock_value_difference"))
+			stock_value_diff = abs(
+				frappe.db.get_value(
+					"Stock Ledger Entry",
+					{"voucher_type": "Stock Entry", "voucher_no": mtn.name, "warehouse": "Stores - TCP1"},
+					"stock_value_difference",
+				)
+			)
 
-			self.check_gl_entries("Stock Entry", mtn.name,
-				sorted([
-					[source_warehouse_account, 0.0, stock_value_diff],
-					[target_warehouse_account, stock_value_diff, 0.0],
-				])
+			self.check_gl_entries(
+				"Stock Entry",
+				mtn.name,
+				sorted(
+					[
+						[source_warehouse_account, 0.0, stock_value_diff],
+						[target_warehouse_account, stock_value_diff, 0.0],
+					]
+				),
 			)
 
 		mtn.cancel()
@@ -239,20 +318,23 @@
 		repack.items[0].transfer_qty = 100.0
 		repack.items[1].qty = 50.0
 
-		repack.append("items", {
-			"conversion_factor": 1.0,
-			"cost_center": "_Test Cost Center - _TC",
-			"doctype": "Stock Entry Detail",
-			"expense_account": "Stock Adjustment - _TC",
-			"basic_rate": 150,
-			"item_code": "_Test Item 2",
-			"parentfield": "items",
-			"qty": 50.0,
-			"stock_uom": "_Test UOM",
-			"t_warehouse": "_Test Warehouse - _TC",
-			"transfer_qty": 50.0,
-			"uom": "_Test UOM"
-		})
+		repack.append(
+			"items",
+			{
+				"conversion_factor": 1.0,
+				"cost_center": "_Test Cost Center - _TC",
+				"doctype": "Stock Entry Detail",
+				"expense_account": "Stock Adjustment - _TC",
+				"basic_rate": 150,
+				"item_code": "_Test Item 2",
+				"parentfield": "items",
+				"qty": 50.0,
+				"stock_uom": "_Test UOM",
+				"t_warehouse": "_Test Warehouse - _TC",
+				"transfer_qty": 50.0,
+				"uom": "_Test UOM",
+			},
+		)
 		repack.set_stock_entry_type()
 		repack.insert()
 
@@ -265,12 +347,13 @@
 		# must raise error if 0 fg in repack entry
 		self.assertRaises(FinishedGoodError, repack.validate_finished_goods)
 
-		repack.delete() # teardown
+		repack.delete()  # teardown
 
 	def test_repack_no_change_in_valuation(self):
 		make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100)
-		make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse - _TC",
-			qty=50, basic_rate=100)
+		make_stock_entry(
+			item_code="_Test Item Home Desktop 100", target="_Test Warehouse - _TC", qty=50, basic_rate=100
+		)
 
 		repack = frappe.copy_doc(test_records[3])
 		repack.posting_date = nowdate()
@@ -279,76 +362,113 @@
 		repack.insert()
 		repack.submit()
 
-		self.check_stock_ledger_entries("Stock Entry", repack.name,
-			[["_Test Item", "_Test Warehouse - _TC", -50.0],
-				["_Test Item Home Desktop 100", "_Test Warehouse - _TC", 1]])
+		self.check_stock_ledger_entries(
+			"Stock Entry",
+			repack.name,
+			[
+				["_Test Item", "_Test Warehouse - _TC", -50.0],
+				["_Test Item Home Desktop 100", "_Test Warehouse - _TC", 1],
+			],
+		)
 
-		gl_entries = frappe.db.sql("""select account, debit, credit
+		gl_entries = frappe.db.sql(
+			"""select account, debit, credit
 			from `tabGL Entry` where voucher_type='Stock Entry' and voucher_no=%s
-			order by account desc""", repack.name, as_dict=1)
+			order by account desc""",
+			repack.name,
+			as_dict=1,
+		)
 		self.assertFalse(gl_entries)
 
 	def test_repack_with_additional_costs(self):
-		company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
+		company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")
 
-		make_stock_entry(item_code="_Test Item", target="Stores - TCP1", company= company,
-			qty=50, basic_rate=100, expense_account="Stock Adjustment - TCP1")
+		make_stock_entry(
+			item_code="_Test Item",
+			target="Stores - TCP1",
+			company=company,
+			qty=50,
+			basic_rate=100,
+			expense_account="Stock Adjustment - TCP1",
+		)
 
-
-		repack = make_stock_entry(company = company, purpose="Repack", do_not_save=True)
+		repack = make_stock_entry(company=company, purpose="Repack", do_not_save=True)
 		repack.posting_date = nowdate()
 		repack.posting_time = nowtime()
 
-		expenses_included_in_valuation = frappe.get_value("Company", company, "expenses_included_in_valuation")
+		expenses_included_in_valuation = frappe.get_value(
+			"Company", company, "expenses_included_in_valuation"
+		)
 
 		items = get_multiple_items()
 		repack.items = []
 		for item in items:
 			repack.append("items", item)
 
-		repack.set("additional_costs", [
-			{
-				"expense_account": expenses_included_in_valuation,
-				"description": "Actual Operating Cost",
-				"amount": 1000
-			},
-			{
-				"expense_account": expenses_included_in_valuation,
-				"description": "Additional Operating Cost",
-				"amount": 200
-			},
-		])
+		repack.set(
+			"additional_costs",
+			[
+				{
+					"expense_account": expenses_included_in_valuation,
+					"description": "Actual Operating Cost",
+					"amount": 1000,
+				},
+				{
+					"expense_account": expenses_included_in_valuation,
+					"description": "Additional Operating Cost",
+					"amount": 200,
+				},
+			],
+		)
 
 		repack.set_stock_entry_type()
 		repack.insert()
 		repack.submit()
 
 		stock_in_hand_account = get_inventory_account(repack.company, repack.get("items")[1].t_warehouse)
-		rm_stock_value_diff = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Stock Entry",
-			"voucher_no": repack.name, "item_code": "_Test Item"}, "stock_value_difference"))
+		rm_stock_value_diff = abs(
+			frappe.db.get_value(
+				"Stock Ledger Entry",
+				{"voucher_type": "Stock Entry", "voucher_no": repack.name, "item_code": "_Test Item"},
+				"stock_value_difference",
+			)
+		)
 
-		fg_stock_value_diff = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Stock Entry",
-			"voucher_no": repack.name, "item_code": "_Test Item Home Desktop 100"}, "stock_value_difference"))
+		fg_stock_value_diff = abs(
+			frappe.db.get_value(
+				"Stock Ledger Entry",
+				{
+					"voucher_type": "Stock Entry",
+					"voucher_no": repack.name,
+					"item_code": "_Test Item Home Desktop 100",
+				},
+				"stock_value_difference",
+			)
+		)
 
 		stock_value_diff = flt(fg_stock_value_diff - rm_stock_value_diff, 2)
 
 		self.assertEqual(stock_value_diff, 1200)
 
-		self.check_gl_entries("Stock Entry", repack.name,
-			sorted([
-				[stock_in_hand_account, 1200, 0.0],
-				["Expenses Included In Valuation - TCP1", 0.0, 1200.0]
-			])
+		self.check_gl_entries(
+			"Stock Entry",
+			repack.name,
+			sorted(
+				[[stock_in_hand_account, 1200, 0.0], ["Expenses Included In Valuation - TCP1", 0.0, 1200.0]]
+			),
 		)
 
 	def check_stock_ledger_entries(self, voucher_type, voucher_no, expected_sle):
 		expected_sle.sort(key=lambda x: x[1])
 
 		# check stock ledger entries
-		sle = frappe.db.sql("""select item_code, warehouse, actual_qty
+		sle = frappe.db.sql(
+			"""select item_code, warehouse, actual_qty
 			from `tabStock Ledger Entry` where voucher_type = %s
 			and voucher_no = %s order by item_code, warehouse, actual_qty""",
-			(voucher_type, voucher_no), as_list=1)
+			(voucher_type, voucher_no),
+			as_list=1,
+		)
 		self.assertTrue(sle)
 		sle.sort(key=lambda x: x[1])
 
@@ -360,9 +480,13 @@
 	def check_gl_entries(self, voucher_type, voucher_no, expected_gl_entries):
 		expected_gl_entries.sort(key=lambda x: x[0])
 
-		gl_entries = frappe.db.sql("""select account, debit, credit
+		gl_entries = frappe.db.sql(
+			"""select account, debit, credit
 			from `tabGL Entry` where voucher_type=%s and voucher_no=%s
-			order by account asc, debit asc""", (voucher_type, voucher_no), as_list=1)
+			order by account asc, debit asc""",
+			(voucher_type, voucher_no),
+			as_list=1,
+		)
 
 		self.assertTrue(gl_entries)
 		gl_entries.sort(key=lambda x: x[0])
@@ -463,7 +587,7 @@
 
 	def test_serial_item_error(self):
 		se, serial_nos = self.test_serial_by_series()
-		if not frappe.db.exists('Serial No', 'ABCD'):
+		if not frappe.db.exists("Serial No", "ABCD"):
 			make_serialized_item(item_code="_Test Serialized Item", serial_no="ABCD\nEFGH")
 
 		se = frappe.copy_doc(test_records[0])
@@ -493,10 +617,14 @@
 		se.set_stock_entry_type()
 		se.insert()
 		se.submit()
-		self.assertTrue(frappe.db.get_value("Serial No", serial_no, "warehouse"), "_Test Warehouse 1 - _TC")
+		self.assertTrue(
+			frappe.db.get_value("Serial No", serial_no, "warehouse"), "_Test Warehouse 1 - _TC"
+		)
 
 		se.cancel()
-		self.assertTrue(frappe.db.get_value("Serial No", serial_no, "warehouse"), "_Test Warehouse - _TC")
+		self.assertTrue(
+			frappe.db.get_value("Serial No", serial_no, "warehouse"), "_Test Warehouse - _TC"
+		)
 
 	def test_serial_warehouse_error(self):
 		make_serialized_item(target_warehouse="_Test Warehouse 1 - _TC")
@@ -523,15 +651,115 @@
 		serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]
 		self.assertFalse(frappe.db.get_value("Serial No", serial_no, "warehouse"))
 
+	def test_serial_batch_item_stock_entry(self):
+		"""
+		Behaviour: 1) Submit Stock Entry (Receipt) with Serial & Batched Item
+		2) Cancel same Stock Entry
+		Expected Result: 1) Batch is created with Reference in Serial No
+		2) Batch is deleted and Serial No is Inactive
+		"""
+		from erpnext.stock.doctype.batch.batch import get_batch_qty
+
+		item = frappe.db.exists("Item", {"item_name": "Batched and Serialised Item"})
+		if not item:
+			item = create_item("Batched and Serialised Item")
+			item.has_batch_no = 1
+			item.create_new_batch = 1
+			item.has_serial_no = 1
+			item.batch_number_series = "B-BATCH-.##"
+			item.serial_no_series = "S-.####"
+			item.save()
+		else:
+			item = frappe.get_doc("Item", {"item_name": "Batched and Serialised Item"})
+
+		se = make_stock_entry(
+			item_code=item.item_code, target="_Test Warehouse - _TC", qty=1, basic_rate=100
+		)
+		batch_no = se.items[0].batch_no
+		serial_no = get_serial_nos(se.items[0].serial_no)[0]
+		batch_qty = get_batch_qty(batch_no, "_Test Warehouse - _TC", item.item_code)
+
+		batch_in_serial_no = frappe.db.get_value("Serial No", serial_no, "batch_no")
+		self.assertEqual(batch_in_serial_no, batch_no)
+
+		self.assertEqual(batch_qty, 1)
+
+		se.cancel()
+
+		batch_in_serial_no = frappe.db.get_value("Serial No", serial_no, "batch_no")
+		self.assertEqual(batch_in_serial_no, None)
+
+		self.assertEqual(frappe.db.get_value("Serial No", serial_no, "status"), "Inactive")
+		self.assertEqual(frappe.db.exists("Batch", batch_no), None)
+
+	def test_serial_batch_item_qty_deduction(self):
+		"""
+		Behaviour: Create 2 Stock Entries, both adding Serial Nos to same batch
+		Expected: 1) Cancelling first Stock Entry (origin transaction of created batch)
+		should throw a LinkExistsError
+		2) Cancelling second Stock Entry should make Serial Nos that are, linked to mentioned batch
+		and in that transaction only, Inactive.
+		"""
+		from erpnext.stock.doctype.batch.batch import get_batch_qty
+
+		item = frappe.db.exists("Item", {"item_name": "Batched and Serialised Item"})
+		if not item:
+			item = create_item("Batched and Serialised Item")
+			item.has_batch_no = 1
+			item.create_new_batch = 1
+			item.has_serial_no = 1
+			item.batch_number_series = "B-BATCH-.##"
+			item.serial_no_series = "S-.####"
+			item.save()
+		else:
+			item = frappe.get_doc("Item", {"item_name": "Batched and Serialised Item"})
+
+		se1 = make_stock_entry(
+			item_code=item.item_code, target="_Test Warehouse - _TC", qty=1, basic_rate=100
+		)
+		batch_no = se1.items[0].batch_no
+		serial_no1 = get_serial_nos(se1.items[0].serial_no)[0]
+
+		# Check Source (Origin) Document of Batch
+		self.assertEqual(frappe.db.get_value("Batch", batch_no, "reference_name"), se1.name)
+
+		se2 = make_stock_entry(
+			item_code=item.item_code,
+			target="_Test Warehouse - _TC",
+			qty=1,
+			basic_rate=100,
+			batch_no=batch_no,
+		)
+		serial_no2 = get_serial_nos(se2.items[0].serial_no)[0]
+
+		batch_qty = get_batch_qty(batch_no, "_Test Warehouse - _TC", item.item_code)
+		self.assertEqual(batch_qty, 2)
+
+		se2.cancel()
+
+		# Check decrease in Batch Qty
+		batch_qty = get_batch_qty(batch_no, "_Test Warehouse - _TC", item.item_code)
+		self.assertEqual(batch_qty, 1)
+
+		# Check if Serial No from Stock Entry 1 is intact
+		self.assertEqual(frappe.db.get_value("Serial No", serial_no1, "batch_no"), batch_no)
+		self.assertEqual(frappe.db.get_value("Serial No", serial_no1, "status"), "Active")
+
+		# Check if Serial No from Stock Entry 2 is Unlinked and Inactive
+		self.assertEqual(frappe.db.get_value("Serial No", serial_no2, "batch_no"), None)
+		self.assertEqual(frappe.db.get_value("Serial No", serial_no2, "status"), "Inactive")
+
 	def test_warehouse_company_validation(self):
-		company = frappe.db.get_value('Warehouse', '_Test Warehouse 2 - _TC1', 'company')
-		frappe.get_doc("User", "test2@example.com")\
-			.add_roles("Sales User", "Sales Manager", "Stock User", "Stock Manager")
+		company = frappe.db.get_value("Warehouse", "_Test Warehouse 2 - _TC1", "company")
+		frappe.get_doc("User", "test2@example.com").add_roles(
+			"Sales User", "Sales Manager", "Stock User", "Stock Manager"
+		)
 		frappe.set_user("test2@example.com")
 
 		from erpnext.stock.utils import InvalidWarehouseCompany
+
 		st1 = frappe.copy_doc(test_records[0])
-		st1.get("items")[0].t_warehouse="_Test Warehouse 2 - _TC1"
+		st1.get("items")[0].t_warehouse = "_Test Warehouse 2 - _TC1"
 		st1.set_stock_entry_type()
 		st1.insert()
 		self.assertRaises(InvalidWarehouseCompany, st1.submit)
@@ -545,14 +773,15 @@
 		test_user.add_roles("Sales User", "Sales Manager", "Stock User")
 		test_user.remove_roles("Stock Manager", "System Manager")
 
-		frappe.get_doc("User", "test2@example.com")\
-			.add_roles("Sales User", "Sales Manager", "Stock User", "Stock Manager")
+		frappe.get_doc("User", "test2@example.com").add_roles(
+			"Sales User", "Sales Manager", "Stock User", "Stock Manager"
+		)
 
 		st1 = frappe.copy_doc(test_records[0])
 		st1.company = "_Test Company 1"
 
 		frappe.set_user("test@example.com")
-		st1.get("items")[0].t_warehouse="_Test Warehouse 2 - _TC1"
+		st1.get("items")[0].t_warehouse = "_Test Warehouse 2 - _TC1"
 		self.assertRaises(frappe.PermissionError, st1.insert)
 
 		test_user.add_roles("System Manager")
@@ -560,7 +789,7 @@
 		frappe.set_user("test2@example.com")
 		st1 = frappe.copy_doc(test_records[0])
 		st1.company = "_Test Company 1"
-		st1.get("items")[0].t_warehouse="_Test Warehouse 2 - _TC1"
+		st1.get("items")[0].t_warehouse = "_Test Warehouse 2 - _TC1"
 		st1.get("items")[0].expense_account = "Stock Adjustment - _TC1"
 		st1.get("items")[0].cost_center = "Main - _TC1"
 		st1.set_stock_entry_type()
@@ -574,14 +803,14 @@
 		remove_user_permission("Company", "_Test Company 1", "test2@example.com")
 
 	def test_freeze_stocks(self):
-		frappe.db.set_value('Stock Settings', None,'stock_auth_role', '')
+		frappe.db.set_value("Stock Settings", None, "stock_auth_role", "")
 
 		# test freeze_stocks_upto
 		frappe.db.set_value("Stock Settings", None, "stock_frozen_upto", add_days(nowdate(), 5))
 		se = frappe.copy_doc(test_records[0]).insert()
 		self.assertRaises(StockFreezeError, se.submit)
 
-		frappe.db.set_value("Stock Settings", None, "stock_frozen_upto", '')
+		frappe.db.set_value("Stock Settings", None, "stock_frozen_upto", "")
 
 		# test freeze_stocks_upto_days
 		frappe.db.set_value("Stock Settings", None, "stock_frozen_upto_days", -1)
@@ -597,20 +826,24 @@
 		from erpnext.manufacturing.doctype.work_order.work_order import (
 			make_stock_entry as _make_stock_entry,
 		)
-		bom_no, bom_operation_cost = frappe.db.get_value("BOM", {"item": "_Test FG Item 2",
-			"is_default": 1, "docstatus": 1}, ["name", "operating_cost"])
+
+		bom_no, bom_operation_cost = frappe.db.get_value(
+			"BOM", {"item": "_Test FG Item 2", "is_default": 1, "docstatus": 1}, ["name", "operating_cost"]
+		)
 
 		work_order = frappe.new_doc("Work Order")
-		work_order.update({
-			"company": "_Test Company",
-			"fg_warehouse": "_Test Warehouse 1 - _TC",
-			"production_item": "_Test FG Item 2",
-			"bom_no": bom_no,
-			"qty": 1.0,
-			"stock_uom": "_Test UOM",
-			"wip_warehouse": "_Test Warehouse - _TC",
-			"additional_operating_cost": 1000
-		})
+		work_order.update(
+			{
+				"company": "_Test Company",
+				"fg_warehouse": "_Test Warehouse 1 - _TC",
+				"production_item": "_Test FG Item 2",
+				"bom_no": bom_no,
+				"qty": 1.0,
+				"stock_uom": "_Test UOM",
+				"wip_warehouse": "_Test Warehouse - _TC",
+				"additional_operating_cost": 1000,
+			}
+		)
 		work_order.insert()
 		work_order.submit()
 
@@ -623,37 +856,40 @@
 		for d in stock_entry.get("items"):
 			if d.item_code != "_Test FG Item 2":
 				rm_cost += flt(d.amount)
-		fg_cost = list(filter(lambda x: x.item_code=="_Test FG Item 2", stock_entry.get("items")))[0].amount
-		self.assertEqual(fg_cost,
-			flt(rm_cost + bom_operation_cost + work_order.additional_operating_cost, 2))
+		fg_cost = list(filter(lambda x: x.item_code == "_Test FG Item 2", stock_entry.get("items")))[
+			0
+		].amount
+		self.assertEqual(
+			fg_cost, flt(rm_cost + bom_operation_cost + work_order.additional_operating_cost, 2)
+		)
 
+	@change_settings("Manufacturing Settings", {"material_consumption": 1})
 	def test_work_order_manufacture_with_material_consumption(self):
 		from erpnext.manufacturing.doctype.work_order.work_order import (
 			make_stock_entry as _make_stock_entry,
 		)
-		frappe.db.set_value("Manufacturing Settings", None, "material_consumption", "1")
 
-		bom_no = frappe.db.get_value("BOM", {"item": "_Test FG Item",
-			"is_default": 1, "docstatus": 1})
+		bom_no = frappe.db.get_value("BOM", {"item": "_Test FG Item", "is_default": 1, "docstatus": 1})
 
 		work_order = frappe.new_doc("Work Order")
-		work_order.update({
-			"company": "_Test Company",
-			"fg_warehouse": "_Test Warehouse 1 - _TC",
-			"production_item": "_Test FG Item",
-			"bom_no": bom_no,
-			"qty": 1.0,
-			"stock_uom": "_Test UOM",
-			"wip_warehouse": "_Test Warehouse - _TC"
-		})
+		work_order.update(
+			{
+				"company": "_Test Company",
+				"fg_warehouse": "_Test Warehouse 1 - _TC",
+				"production_item": "_Test FG Item",
+				"bom_no": bom_no,
+				"qty": 1.0,
+				"stock_uom": "_Test UOM",
+				"wip_warehouse": "_Test Warehouse - _TC",
+			}
+		)
 		work_order.insert()
 		work_order.submit()
 
-		make_stock_entry(item_code="_Test Item",
-			target="Stores - _TC", qty=10, basic_rate=5000.0)
-		make_stock_entry(item_code="_Test Item Home Desktop 100",
-			target="Stores - _TC", qty=10, basic_rate=1000.0)
-
+		make_stock_entry(item_code="_Test Item", target="Stores - _TC", qty=10, basic_rate=5000.0)
+		make_stock_entry(
+			item_code="_Test Item Home Desktop 100", target="Stores - _TC", qty=10, basic_rate=1000.0
+		)
 
 		s = frappe.get_doc(_make_stock_entry(work_order.name, "Material Transfer for Manufacture", 1))
 		for d in s.get("items"):
@@ -665,13 +901,12 @@
 		s = frappe.get_doc(_make_stock_entry(work_order.name, "Manufacture", 1))
 		s.save()
 		rm_cost = 0
-		for d in s.get('items'):
+		for d in s.get("items"):
 			if d.s_warehouse:
 				rm_cost += d.amount
-		fg_cost = list(filter(lambda x: x.item_code=="_Test FG Item", s.get("items")))[0].amount
+		fg_cost = list(filter(lambda x: x.item_code == "_Test FG Item", s.get("items")))[0].amount
 		scrap_cost = list(filter(lambda x: x.is_scrap_item, s.get("items")))[0].amount
-		self.assertEqual(fg_cost,
-			flt(rm_cost - scrap_cost, 2))
+		self.assertEqual(fg_cost, flt(rm_cost - scrap_cost, 2))
 
 		# When Stock Entry has only FG + Scrap
 		s.items.pop(0)
@@ -679,31 +914,34 @@
 		s.submit()
 
 		rm_cost = 0
-		for d in s.get('items'):
+		for d in s.get("items"):
 			if d.s_warehouse:
 				rm_cost += d.amount
 		self.assertEqual(rm_cost, 0)
 		expected_fg_cost = s.get_basic_rate_for_manufactured_item(1)
-		fg_cost = list(filter(lambda x: x.item_code=="_Test FG Item", s.get("items")))[0].amount
+		fg_cost = list(filter(lambda x: x.item_code == "_Test FG Item", s.get("items")))[0].amount
 		self.assertEqual(flt(fg_cost, 2), flt(expected_fg_cost, 2))
 
 	def test_variant_work_order(self):
-		bom_no = frappe.db.get_value("BOM", {"item": "_Test Variant Item",
-			"is_default": 1, "docstatus": 1})
+		bom_no = frappe.db.get_value(
+			"BOM", {"item": "_Test Variant Item", "is_default": 1, "docstatus": 1}
+		)
 
-		make_item_variant() # make variant of _Test Variant Item if absent
+		make_item_variant()  # make variant of _Test Variant Item if absent
 
 		work_order = frappe.new_doc("Work Order")
-		work_order.update({
-			"company": "_Test Company",
-			"fg_warehouse": "_Test Warehouse 1 - _TC",
-			"production_item": "_Test Variant Item-S",
-			"bom_no": bom_no,
-			"qty": 1.0,
-			"stock_uom": "_Test UOM",
-			"wip_warehouse": "_Test Warehouse - _TC",
-			"skip_transfer": 1
-		})
+		work_order.update(
+			{
+				"company": "_Test Company",
+				"fg_warehouse": "_Test Warehouse 1 - _TC",
+				"production_item": "_Test Variant Item-S",
+				"bom_no": bom_no,
+				"qty": 1.0,
+				"stock_uom": "_Test UOM",
+				"wip_warehouse": "_Test Warehouse - _TC",
+				"skip_transfer": 1,
+			}
+		)
 		work_order.insert()
 		work_order.submit()
 
@@ -717,19 +955,29 @@
 		s1 = make_serialized_item(target_warehouse="_Test Warehouse - _TC")
 		serial_nos = s1.get("items")[0].serial_no
 
-		s2 = make_stock_entry(item_code="_Test Serialized Item With Series", source="_Test Warehouse - _TC",
-			qty=2, basic_rate=100, purpose="Repack", serial_no=serial_nos, do_not_save=True)
+		s2 = make_stock_entry(
+			item_code="_Test Serialized Item With Series",
+			source="_Test Warehouse - _TC",
+			qty=2,
+			basic_rate=100,
+			purpose="Repack",
+			serial_no=serial_nos,
+			do_not_save=True,
+		)
 
-		s2.append("items", {
-			"item_code": "_Test Serialized Item",
-			"t_warehouse": "_Test Warehouse - _TC",
-			"qty": 2,
-			"basic_rate": 120,
-			"expense_account": "Stock Adjustment - _TC",
-			"conversion_factor": 1.0,
-			"cost_center": "_Test Cost Center - _TC",
-			"serial_no": serial_nos
-		})
+		s2.append(
+			"items",
+			{
+				"item_code": "_Test Serialized Item",
+				"t_warehouse": "_Test Warehouse - _TC",
+				"qty": 2,
+				"basic_rate": 120,
+				"expense_account": "Stock Adjustment - _TC",
+				"conversion_factor": 1.0,
+				"cost_center": "_Test Cost Center - _TC",
+				"serial_no": serial_nos,
+			},
+		)
 
 		s2.submit()
 		s2.cancel()
@@ -739,10 +987,15 @@
 		from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
 
 		create_warehouse("Test Warehouse for Sample Retention")
-		frappe.db.set_value("Stock Settings", None, "sample_retention_warehouse", "Test Warehouse for Sample Retention - _TC")
+		frappe.db.set_value(
+			"Stock Settings",
+			None,
+			"sample_retention_warehouse",
+			"Test Warehouse for Sample Retention - _TC",
+		)
 
 		test_item_code = "Retain Sample Item"
-		if not frappe.db.exists('Item', test_item_code):
+		if not frappe.db.exists("Item", test_item_code):
 			item = frappe.new_doc("Item")
 			item.item_code = test_item_code
 			item.item_name = "Retain Sample Item"
@@ -758,44 +1011,58 @@
 		receipt_entry = frappe.new_doc("Stock Entry")
 		receipt_entry.company = "_Test Company"
 		receipt_entry.purpose = "Material Receipt"
-		receipt_entry.append("items", {
-			"item_code": test_item_code,
-			"t_warehouse": "_Test Warehouse - _TC",
-			"qty": 40,
-			"basic_rate": 12,
-			"cost_center": "_Test Cost Center - _TC",
-			"sample_quantity": 4
-		})
+		receipt_entry.append(
+			"items",
+			{
+				"item_code": test_item_code,
+				"t_warehouse": "_Test Warehouse - _TC",
+				"qty": 40,
+				"basic_rate": 12,
+				"cost_center": "_Test Cost Center - _TC",
+				"sample_quantity": 4,
+			},
+		)
 		receipt_entry.set_stock_entry_type()
 		receipt_entry.insert()
 		receipt_entry.submit()
 
-		retention_data = move_sample_to_retention_warehouse(receipt_entry.company, receipt_entry.get("items"))
+		retention_data = move_sample_to_retention_warehouse(
+			receipt_entry.company, receipt_entry.get("items")
+		)
 		retention_entry = frappe.new_doc("Stock Entry")
 		retention_entry.company = retention_data.company
 		retention_entry.purpose = retention_data.purpose
-		retention_entry.append("items", {
-			"item_code": test_item_code,
-			"t_warehouse": "Test Warehouse for Sample Retention - _TC",
-			"s_warehouse": "_Test Warehouse - _TC",
-			"qty": 4,
-			"basic_rate": 12,
-			"cost_center": "_Test Cost Center - _TC",
-			"batch_no": receipt_entry.get("items")[0].batch_no
-		})
+		retention_entry.append(
+			"items",
+			{
+				"item_code": test_item_code,
+				"t_warehouse": "Test Warehouse for Sample Retention - _TC",
+				"s_warehouse": "_Test Warehouse - _TC",
+				"qty": 4,
+				"basic_rate": 12,
+				"cost_center": "_Test Cost Center - _TC",
+				"batch_no": receipt_entry.get("items")[0].batch_no,
+			},
+		)
 		retention_entry.set_stock_entry_type()
 		retention_entry.insert()
 		retention_entry.submit()
 
-		qty_in_usable_warehouse = get_batch_qty(receipt_entry.get("items")[0].batch_no, "_Test Warehouse - _TC", "_Test Item")
-		qty_in_retention_warehouse = get_batch_qty(receipt_entry.get("items")[0].batch_no, "Test Warehouse for Sample Retention - _TC", "_Test Item")
+		qty_in_usable_warehouse = get_batch_qty(
+			receipt_entry.get("items")[0].batch_no, "_Test Warehouse - _TC", "_Test Item"
+		)
+		qty_in_retention_warehouse = get_batch_qty(
+			receipt_entry.get("items")[0].batch_no,
+			"Test Warehouse for Sample Retention - _TC",
+			"_Test Item",
+		)
 
 		self.assertEqual(qty_in_usable_warehouse, 36)
 		self.assertEqual(qty_in_retention_warehouse, 4)
 
 	def test_quality_check(self):
 		item_code = "_Test Item For QC"
-		if not frappe.db.exists('Item', item_code):
+		if not frappe.db.exists("Item", item_code):
 			create_item(item_code)
 
 		repack = frappe.copy_doc(test_records[3])
@@ -811,82 +1078,64 @@
 		repack.insert()
 		self.assertRaises(frappe.ValidationError, repack.submit)
 
-	# def test_material_consumption(self):
-	# 	frappe.db.set_value("Manufacturing Settings", None, "backflush_raw_materials_based_on", "BOM")
-	# 	frappe.db.set_value("Manufacturing Settings", None, "material_consumption", "0")
-
-	# 	from erpnext.manufacturing.doctype.work_order.work_order \
-	# 		import make_stock_entry as _make_stock_entry
-	# 	bom_no = frappe.db.get_value("BOM", {"item": "_Test FG Item 2",
-	# 		"is_default": 1, "docstatus": 1})
-
-	# 	work_order = frappe.new_doc("Work Order")
-	# 	work_order.update({
-	# 		"company": "_Test Company",
-	# 		"fg_warehouse": "_Test Warehouse 1 - _TC",
-	# 		"production_item": "_Test FG Item 2",
-	# 		"bom_no": bom_no,
-	# 		"qty": 4.0,
-	# 		"stock_uom": "_Test UOM",
-	# 		"wip_warehouse": "_Test Warehouse - _TC",
-	# 		"additional_operating_cost": 1000,
-	# 		"use_multi_level_bom": 1
-	# 	})
-	# 	work_order.insert()
-	# 	work_order.submit()
-
-	# 	make_stock_entry(item_code="_Test Serialized Item With Series", target="_Test Warehouse - _TC", qty=50, basic_rate=100)
-	# 	make_stock_entry(item_code="_Test Item 2", target="_Test Warehouse - _TC", qty=50, basic_rate=20)
-
-	# 	item_quantity = {
-	# 		'_Test Item': 2.0,
-	# 		'_Test Item 2': 12.0,
-	# 		'_Test Serialized Item With Series': 6.0
-	# 	}
-
-	# 	stock_entry = frappe.get_doc(_make_stock_entry(work_order.name, "Material Consumption for Manufacture", 2))
-	# 	for d in stock_entry.get('items'):
-	# 		self.assertEqual(item_quantity.get(d.item_code), d.qty)
-
 	def test_customer_provided_parts_se(self):
-		create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
-		se = make_stock_entry(item_code='CUST-0987', purpose = 'Material Receipt',
-			qty=4, to_warehouse = "_Test Warehouse - _TC")
+		create_item(
+			"CUST-0987", is_customer_provided_item=1, customer="_Test Customer", is_purchase_item=0
+		)
+		se = make_stock_entry(
+			item_code="CUST-0987", purpose="Material Receipt", qty=4, to_warehouse="_Test Warehouse - _TC"
+		)
 		self.assertEqual(se.get("items")[0].allow_zero_valuation_rate, 1)
 		self.assertEqual(se.get("items")[0].amount, 0)
 
 	def test_zero_incoming_rate(self):
-		""" Make sure incoming rate of 0 is allowed while consuming.
+		"""Make sure incoming rate of 0 is allowed while consuming.
 
-			qty  | rate | valuation rate
-			 1   | 100  | 100
-			 1   | 0    | 50
-			-1   | 100  | 0
-			-1   | 0  <--- assert this
+		qty  | rate | valuation rate
+		 1   | 100  | 100
+		 1   | 0    | 50
+		-1   | 100  | 0
+		-1   | 0  <--- assert this
 		"""
 		item_code = "_TestZeroVal"
 		warehouse = "_Test Warehouse - _TC"
-		create_item('_TestZeroVal')
+		create_item("_TestZeroVal")
 		_receipt = make_stock_entry(item_code=item_code, qty=1, to_warehouse=warehouse, rate=100)
-		receipt2 = make_stock_entry(item_code=item_code, qty=1, to_warehouse=warehouse, rate=0, do_not_save=True)
+		receipt2 = make_stock_entry(
+			item_code=item_code, qty=1, to_warehouse=warehouse, rate=0, do_not_save=True
+		)
 		receipt2.items[0].allow_zero_valuation_rate = 1
 		receipt2.save()
 		receipt2.submit()
 
 		issue = make_stock_entry(item_code=item_code, qty=1, from_warehouse=warehouse)
 
-		value_diff = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": issue.name, "voucher_type": "Stock Entry"}, "stock_value_difference")
+		value_diff = frappe.db.get_value(
+			"Stock Ledger Entry",
+			{"voucher_no": issue.name, "voucher_type": "Stock Entry"},
+			"stock_value_difference",
+		)
 		self.assertEqual(value_diff, -100)
 
 		issue2 = make_stock_entry(item_code=item_code, qty=1, from_warehouse=warehouse)
-		value_diff = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": issue2.name, "voucher_type": "Stock Entry"}, "stock_value_difference")
+		value_diff = frappe.db.get_value(
+			"Stock Ledger Entry",
+			{"voucher_no": issue2.name, "voucher_type": "Stock Entry"},
+			"stock_value_difference",
+		)
 		self.assertEqual(value_diff, 0)
 
-
 	def test_gle_for_opening_stock_entry(self):
-		mr = make_stock_entry(item_code="_Test Item", target="Stores - TCP1",
-			company="_Test Company with perpetual inventory", qty=50, basic_rate=100,
-			expense_account="Stock Adjustment - TCP1", is_opening="Yes", do_not_save=True)
+		mr = make_stock_entry(
+			item_code="_Test Item",
+			target="Stores - TCP1",
+			company="_Test Company with perpetual inventory",
+			qty=50,
+			basic_rate=100,
+			expense_account="Stock Adjustment - TCP1",
+			is_opening="Yes",
+			do_not_save=True,
+		)
 
 		self.assertRaises(OpeningEntryAccountError, mr.save)
 
@@ -895,52 +1144,61 @@
 		mr.save()
 		mr.submit()
 
-		is_opening = frappe.db.get_value("GL Entry",
-			filters={"voucher_type": "Stock Entry", "voucher_no": mr.name}, fieldname="is_opening")
+		is_opening = frappe.db.get_value(
+			"GL Entry",
+			filters={"voucher_type": "Stock Entry", "voucher_no": mr.name},
+			fieldname="is_opening",
+		)
 		self.assertEqual(is_opening, "Yes")
 
 	def test_total_basic_amount_zero(self):
-		se = frappe.get_doc({"doctype":"Stock Entry",
-			"purpose":"Material Receipt",
-			"stock_entry_type":"Material Receipt",
-			"posting_date": nowdate(),
-			"company":"_Test Company with perpetual inventory",
-			"items":[
-				{
-					"item_code":"_Test Item",
-					"description":"_Test Item",
-					"qty": 1,
-					"basic_rate": 0,
-					"uom":"Nos",
-					"t_warehouse": "Stores - TCP1",
-					"allow_zero_valuation_rate": 1,
-					"cost_center": "Main - TCP1"
-				},
-				{
-					"item_code":"_Test Item",
-					"description":"_Test Item",
-					"qty": 2,
-					"basic_rate": 0,
-					"uom":"Nos",
-					"t_warehouse": "Stores - TCP1",
-					"allow_zero_valuation_rate": 1,
-					"cost_center": "Main - TCP1"
-				},
-			],
-			"additional_costs":[
-				{"expense_account":"Miscellaneous Expenses - TCP1",
-				"amount":100,
-				"description": "miscellanous"
-			}]
-		})
+		se = frappe.get_doc(
+			{
+				"doctype": "Stock Entry",
+				"purpose": "Material Receipt",
+				"stock_entry_type": "Material Receipt",
+				"posting_date": nowdate(),
+				"company": "_Test Company with perpetual inventory",
+				"items": [
+					{
+						"item_code": "_Test Item",
+						"description": "_Test Item",
+						"qty": 1,
+						"basic_rate": 0,
+						"uom": "Nos",
+						"t_warehouse": "Stores - TCP1",
+						"allow_zero_valuation_rate": 1,
+						"cost_center": "Main - TCP1",
+					},
+					{
+						"item_code": "_Test Item",
+						"description": "_Test Item",
+						"qty": 2,
+						"basic_rate": 0,
+						"uom": "Nos",
+						"t_warehouse": "Stores - TCP1",
+						"allow_zero_valuation_rate": 1,
+						"cost_center": "Main - TCP1",
+					},
+				],
+				"additional_costs": [
+					{
+						"expense_account": "Miscellaneous Expenses - TCP1",
+						"amount": 100,
+						"description": "miscellanous",
+					}
+				],
+			}
+		)
 		se.insert()
 		se.submit()
 
-		self.check_gl_entries("Stock Entry", se.name,
-			sorted([
-				["Stock Adjustment - TCP1", 100.0, 0.0],
-				["Miscellaneous Expenses - TCP1", 0.0, 100.0]
-			])
+		self.check_gl_entries(
+			"Stock Entry",
+			se.name,
+			sorted(
+				[["Stock Adjustment - TCP1", 100.0, 0.0], ["Miscellaneous Expenses - TCP1", 0.0, 100.0]]
+			),
 		)
 
 	def test_conversion_factor_change(self):
@@ -971,15 +1229,15 @@
 
 	def test_additional_cost_distribution_manufacture(self):
 		se = frappe.get_doc(
-				doctype="Stock Entry",
-				purpose="Manufacture",
-				additional_costs=[frappe._dict(base_amount=100)],
-				items=[
-					frappe._dict(item_code="RM", basic_amount=10),
-					frappe._dict(item_code="FG", basic_amount=20, t_warehouse="X", is_finished_item=1),
-					frappe._dict(item_code="scrap", basic_amount=30, t_warehouse="X")
-				],
-			)
+			doctype="Stock Entry",
+			purpose="Manufacture",
+			additional_costs=[frappe._dict(base_amount=100)],
+			items=[
+				frappe._dict(item_code="RM", basic_amount=10),
+				frappe._dict(item_code="FG", basic_amount=20, t_warehouse="X", is_finished_item=1),
+				frappe._dict(item_code="scrap", basic_amount=30, t_warehouse="X"),
+			],
+		)
 
 		se.distribute_additional_costs()
 
@@ -988,14 +1246,14 @@
 
 	def test_additional_cost_distribution_non_manufacture(self):
 		se = frappe.get_doc(
-				doctype="Stock Entry",
-				purpose="Material Receipt",
-				additional_costs=[frappe._dict(base_amount=100)],
-				items=[
-					frappe._dict(item_code="RECEIVED_1", basic_amount=20, t_warehouse="X"),
-					frappe._dict(item_code="RECEIVED_2", basic_amount=30, t_warehouse="X")
-				],
-			)
+			doctype="Stock Entry",
+			purpose="Material Receipt",
+			additional_costs=[frappe._dict(base_amount=100)],
+			items=[
+				frappe._dict(item_code="RECEIVED_1", basic_amount=20, t_warehouse="X"),
+				frappe._dict(item_code="RECEIVED_2", basic_amount=30, t_warehouse="X"),
+			],
+		)
 
 		se.distribute_additional_costs()
 
@@ -1010,9 +1268,11 @@
 			stock_entry_type="Manufacture",
 			company="_Test Company",
 			items=[
-				frappe._dict(item_code="_Test Item", qty=1, basic_rate=200, s_warehouse="_Test Warehouse - _TC"),
-				frappe._dict(item_code="_Test FG Item", qty=4, t_warehouse="_Test Warehouse 1 - _TC")
-			]
+				frappe._dict(
+					item_code="_Test Item", qty=1, basic_rate=200, s_warehouse="_Test Warehouse - _TC"
+				),
+				frappe._dict(item_code="_Test FG Item", qty=4, t_warehouse="_Test Warehouse 1 - _TC"),
+			],
 		)
 		# SE must have atleast one FG
 		self.assertRaises(FinishedGoodError, se.save)
@@ -1027,47 +1287,49 @@
 
 		# Check if FG cost is calculated based on RM total cost
 		# RM total cost = 200, FG rate = 200/4(FG qty) =  50
-		self.assertEqual(se.items[1].basic_rate, flt(se.items[0].basic_rate/4))
+		self.assertEqual(se.items[1].basic_rate, flt(se.items[0].basic_rate / 4))
 		self.assertEqual(se.value_difference, 0.0)
 		self.assertEqual(se.total_incoming_value, se.total_outgoing_value)
 
 	@change_settings("Stock Settings", {"allow_negative_stock": 0})
 	def test_future_negative_sle(self):
 		# Initialize item, batch, warehouse, opening qty
-		item_code = '_Test Future Neg Item'
-		batch_no = '_Test Future Neg Batch'
-		warehouses = [
-			'_Test Future Neg Warehouse Source',
-			'_Test Future Neg Warehouse Destination'
-		]
+		item_code = "_Test Future Neg Item"
+		batch_no = "_Test Future Neg Batch"
+		warehouses = ["_Test Future Neg Warehouse Source", "_Test Future Neg Warehouse Destination"]
 		warehouse_names = initialize_records_for_future_negative_sle_test(
-			item_code, batch_no, warehouses,
-			opening_qty=2, posting_date='2021-07-01'
+			item_code, batch_no, warehouses, opening_qty=2, posting_date="2021-07-01"
 		)
 
 		# Executing an illegal sequence should raise an error
 		sequence_of_entries = [
-			dict(item_code=item_code,
+			dict(
+				item_code=item_code,
 				qty=2,
 				from_warehouse=warehouse_names[0],
 				to_warehouse=warehouse_names[1],
 				batch_no=batch_no,
-				posting_date='2021-07-03',
-				purpose='Material Transfer'),
-			dict(item_code=item_code,
+				posting_date="2021-07-03",
+				purpose="Material Transfer",
+			),
+			dict(
+				item_code=item_code,
 				qty=2,
 				from_warehouse=warehouse_names[1],
 				to_warehouse=warehouse_names[0],
 				batch_no=batch_no,
-				posting_date='2021-07-04',
-				purpose='Material Transfer'),
-			dict(item_code=item_code,
+				posting_date="2021-07-04",
+				purpose="Material Transfer",
+			),
+			dict(
+				item_code=item_code,
 				qty=2,
 				from_warehouse=warehouse_names[0],
 				to_warehouse=warehouse_names[1],
 				batch_no=batch_no,
-				posting_date='2021-07-02',          # Illegal SE
-				purpose='Material Transfer')
+				posting_date="2021-07-02",  # Illegal SE
+				purpose="Material Transfer",
+			),
 		]
 
 		self.assertRaises(NegativeStockError, create_stock_entries, sequence_of_entries)
@@ -1077,74 +1339,74 @@
 		from erpnext.stock.doctype.batch.test_batch import TestBatch
 
 		# Initialize item, batch, warehouse, opening qty
-		item_code = '_Test MultiBatch Item'
+		item_code = "_Test MultiBatch Item"
 		TestBatch.make_batch_item(item_code)
 
-		batch_nos = [] # store generate batches
-		warehouse = '_Test Warehouse - _TC'
+		batch_nos = []  # store generate batches
+		warehouse = "_Test Warehouse - _TC"
 
 		se1 = make_stock_entry(
-				item_code=item_code,
-				qty=2,
-				to_warehouse=warehouse,
-				posting_date='2021-09-01',
-				purpose='Material Receipt'
-			)
+			item_code=item_code,
+			qty=2,
+			to_warehouse=warehouse,
+			posting_date="2021-09-01",
+			purpose="Material Receipt",
+		)
 		batch_nos.append(se1.items[0].batch_no)
 		se2 = make_stock_entry(
-				item_code=item_code,
-				qty=2,
-				to_warehouse=warehouse,
-				posting_date='2021-09-03',
-				purpose='Material Receipt'
-			)
+			item_code=item_code,
+			qty=2,
+			to_warehouse=warehouse,
+			posting_date="2021-09-03",
+			purpose="Material Receipt",
+		)
 		batch_nos.append(se2.items[0].batch_no)
 
 		with self.assertRaises(NegativeStockError) as nse:
-			make_stock_entry(item_code=item_code,
+			make_stock_entry(
+				item_code=item_code,
 				qty=1,
 				from_warehouse=warehouse,
 				batch_no=batch_nos[1],
-				posting_date='2021-09-02', # backdated consumption of 2nd batch
-				purpose='Material Issue')
+				posting_date="2021-09-02",  # backdated consumption of 2nd batch
+				purpose="Material Issue",
+			)
 
 	def test_multi_batch_value_diff(self):
-		""" Test value difference on stock entry in case of multi-batch.
-			| Stock entry | batch | qty | rate | value diff on SE             |
-			| ---         | ---   | --- | ---  | ---                          |
-			| receipt     | A     | 1   | 10   | 30                           |
-			| receipt     | B     | 1   | 20   |                              |
-			| issue       | A     | -1  | 10   | -30 (to assert after submit) |
-			| issue       | B     | -1  | 20   |                              |
+		"""Test value difference on stock entry in case of multi-batch.
+		| Stock entry | batch | qty | rate | value diff on SE             |
+		| ---         | ---   | --- | ---  | ---                          |
+		| receipt     | A     | 1   | 10   | 30                           |
+		| receipt     | B     | 1   | 20   |                              |
+		| issue       | A     | -1  | 10   | -30 (to assert after submit) |
+		| issue       | B     | -1  | 20   |                              |
 		"""
 		from erpnext.stock.doctype.batch.test_batch import TestBatch
 
 		batch_nos = []
 
-		item_code = '_TestMultibatchFifo'
+		item_code = "_TestMultibatchFifo"
 		TestBatch.make_batch_item(item_code)
-		warehouse = '_Test Warehouse - _TC'
+		warehouse = "_Test Warehouse - _TC"
 		receipt = make_stock_entry(
-				item_code=item_code,
-				qty=1,
-				rate=10,
-				to_warehouse=warehouse,
-				purpose='Material Receipt',
-				do_not_save=True
-			)
-		receipt.append("items", frappe.copy_doc(receipt.items[0], ignore_no_copy=False).update({"basic_rate": 20}) )
+			item_code=item_code,
+			qty=1,
+			rate=10,
+			to_warehouse=warehouse,
+			purpose="Material Receipt",
+			do_not_save=True,
+		)
+		receipt.append(
+			"items", frappe.copy_doc(receipt.items[0], ignore_no_copy=False).update({"basic_rate": 20})
+		)
 		receipt.save()
 		receipt.submit()
 		batch_nos.extend(row.batch_no for row in receipt.items)
 		self.assertEqual(receipt.value_difference, 30)
 
 		issue = make_stock_entry(
-				item_code=item_code,
-				qty=1,
-				from_warehouse=warehouse,
-				purpose='Material Issue',
-				do_not_save=True
-			)
+			item_code=item_code, qty=1, from_warehouse=warehouse, purpose="Material Issue", do_not_save=True
+		)
 		issue.append("items", frappe.copy_doc(issue.items[0], ignore_no_copy=False))
 		for row, batch_no in zip(issue.items, batch_nos):
 			row.batch_no = batch_no
@@ -1154,6 +1416,48 @@
 		issue.reload()  # reload because reposting current voucher updates rate
 		self.assertEqual(issue.value_difference, -30)
 
+	def test_transfer_qty_validation(self):
+		se = make_stock_entry(item_code="_Test Item", do_not_save=True, qty=0.001, rate=100)
+		se.items[0].uom = "Kg"
+		se.items[0].conversion_factor = 0.002
+
+		self.assertRaises(frappe.ValidationError, se.save)
+
+	def test_mapped_stock_entry(self):
+		"Check if rate and stock details are populated in mapped SE given warehouse."
+		from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_stock_entry
+		from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+
+		item_code = "_TestMappedItem"
+		create_item(item_code, is_stock_item=True)
+
+		pr = make_purchase_receipt(
+			item_code=item_code, qty=2, rate=100, company="_Test Company", warehouse="Stores - _TC"
+		)
+
+		mapped_se = make_stock_entry(pr.name)
+
+		self.assertEqual(mapped_se.items[0].s_warehouse, "Stores - _TC")
+		self.assertEqual(mapped_se.items[0].actual_qty, 2)
+		self.assertEqual(mapped_se.items[0].basic_rate, 100)
+		self.assertEqual(mapped_se.items[0].basic_amount, 200)
+
+	def test_stock_entry_item_details(self):
+		item = make_item()
+
+		se = make_stock_entry(
+			item_code=item.name, qty=1, to_warehouse="_Test Warehouse - _TC", do_not_submit=True
+		)
+
+		self.assertEqual(se.items[0].item_name, item.item_name)
+		se.items[0].item_name = "wat"
+		se.items[0].stock_uom = "Kg"
+		se.save()
+
+		self.assertEqual(se.items[0].item_name, item.item_name)
+		self.assertEqual(se.items[0].stock_uom, item.stock_uom)
+
+
 def make_serialized_item(**args):
 	args = frappe._dict(args)
 	se = frappe.copy_doc(test_records[0])
@@ -1183,50 +1487,57 @@
 	se.submit()
 	return se
 
+
 def get_qty_after_transaction(**args):
 	args = frappe._dict(args)
-	last_sle = get_previous_sle({
-		"item_code": args.item_code or "_Test Item",
-		"warehouse": args.warehouse or "_Test Warehouse - _TC",
-		"posting_date": args.posting_date or nowdate(),
-		"posting_time": args.posting_time or nowtime()
-	})
+	last_sle = get_previous_sle(
+		{
+			"item_code": args.item_code or "_Test Item",
+			"warehouse": args.warehouse or "_Test Warehouse - _TC",
+			"posting_date": args.posting_date or nowdate(),
+			"posting_time": args.posting_time or nowtime(),
+		}
+	)
 	return flt(last_sle.get("qty_after_transaction"))
 
+
 def get_multiple_items():
 	return [
-			{
-				"conversion_factor": 1.0,
-				"cost_center": "Main - TCP1",
-				"doctype": "Stock Entry Detail",
-				"expense_account": "Stock Adjustment - TCP1",
-				"basic_rate": 100,
-				"item_code": "_Test Item",
-				"qty": 50.0,
-				"s_warehouse": "Stores - TCP1",
-				"stock_uom": "_Test UOM",
-				"transfer_qty": 50.0,
-				"uom": "_Test UOM"
-			},
-			{
-				"conversion_factor": 1.0,
-				"cost_center": "Main - TCP1",
-				"doctype": "Stock Entry Detail",
-				"expense_account": "Stock Adjustment - TCP1",
-				"basic_rate": 5000,
-				"item_code": "_Test Item Home Desktop 100",
-				"qty": 1,
-				"stock_uom": "_Test UOM",
-				"t_warehouse": "Stores - TCP1",
-				"transfer_qty": 1,
-				"uom": "_Test UOM"
-			}
-		]
+		{
+			"conversion_factor": 1.0,
+			"cost_center": "Main - TCP1",
+			"doctype": "Stock Entry Detail",
+			"expense_account": "Stock Adjustment - TCP1",
+			"basic_rate": 100,
+			"item_code": "_Test Item",
+			"qty": 50.0,
+			"s_warehouse": "Stores - TCP1",
+			"stock_uom": "_Test UOM",
+			"transfer_qty": 50.0,
+			"uom": "_Test UOM",
+		},
+		{
+			"conversion_factor": 1.0,
+			"cost_center": "Main - TCP1",
+			"doctype": "Stock Entry Detail",
+			"expense_account": "Stock Adjustment - TCP1",
+			"basic_rate": 5000,
+			"item_code": "_Test Item Home Desktop 100",
+			"qty": 1,
+			"stock_uom": "_Test UOM",
+			"t_warehouse": "Stores - TCP1",
+			"transfer_qty": 1,
+			"uom": "_Test UOM",
+		},
+	]
 
-test_records = frappe.get_test_records('Stock Entry')
+
+test_records = frappe.get_test_records("Stock Entry")
+
 
 def initialize_records_for_future_negative_sle_test(
-		item_code, batch_no, warehouses, opening_qty, posting_date):
+	item_code, batch_no, warehouses, opening_qty, posting_date
+):
 	from erpnext.stock.doctype.batch.test_batch import TestBatch, make_new_batch
 	from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
 		create_stock_reconciliation,
@@ -1237,9 +1548,9 @@
 	make_new_batch(item_code=item_code, batch_id=batch_no)
 	warehouse_names = [create_warehouse(w) for w in warehouses]
 	create_stock_reconciliation(
-		purpose='Opening Stock',
+		purpose="Opening Stock",
 		posting_date=posting_date,
-		posting_time='20:00:20',
+		posting_time="20:00:20",
 		item_code=item_code,
 		warehouse=warehouse_names[0],
 		valuation_rate=100,
diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
index 83aed90..d758c8a 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
@@ -233,6 +233,7 @@
    "reqd": 1
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "conversion_factor",
    "fieldtype": "Float",
    "label": "Conversion Factor",
@@ -242,6 +243,7 @@
    "reqd": 1
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "stock_uom",
    "fieldtype": "Link",
    "label": "Stock UOM",
@@ -253,6 +255,7 @@
    "reqd": 1
   },
   {
+   "depends_on": "eval:doc.uom != doc.stock_uom",
    "fieldname": "transfer_qty",
    "fieldtype": "Float",
    "label": "Qty as per Stock UOM",
@@ -556,7 +559,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2022-02-26 00:51:24.963653",
+ "modified": "2022-06-17 05:06:33.621264",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry Detail",
diff --git a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py
index efd97c0..7258cfb 100644
--- a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py
+++ b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py
@@ -8,5 +8,5 @@
 
 class StockEntryType(Document):
 	def validate(self):
-		if self.add_to_transit and self.purpose != 'Material Transfer':
+		if self.add_to_transit and self.purpose != "Material Transfer":
 			self.add_to_transit = 0
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.js b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.js
index 42cc7e6..23018aa 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.js
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.js
@@ -3,6 +3,6 @@
 
 frappe.ui.form.on('Stock Ledger Entry', {
 	refresh: function(frm) {
-
+		frm.page.btn_secondary.hide()
 	}
 });
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index c538307..329cd7d 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -14,11 +14,17 @@
 from erpnext.controllers.item_variant import ItemTemplateCannotHaveStock
 
 
-class StockFreezeError(frappe.ValidationError): pass
-class BackDatedStockTransaction(frappe.ValidationError): pass
+class StockFreezeError(frappe.ValidationError):
+	pass
+
+
+class BackDatedStockTransaction(frappe.ValidationError):
+	pass
+
 
 exclude_from_linked_with = True
 
+
 class StockLedgerEntry(Document):
 	def autoname(self):
 		"""
@@ -26,10 +32,13 @@
 		name will be changed using autoname options (in a scheduled job)
 		"""
 		self.name = frappe.generate_hash(txt="", length=10)
+		if self.meta.autoname == "hash":
+			self.to_rename = 0
 
 	def validate(self):
 		self.flags.ignore_submit_comment = True
 		from erpnext.stock.utils import validate_disabled_warehouse, validate_warehouse_company
+
 		self.validate_mandatory()
 		self.validate_item()
 		self.validate_batch()
@@ -40,24 +49,29 @@
 		self.block_transactions_against_group_warehouse()
 		self.validate_with_last_transaction_posting_time()
 
-
 	def on_submit(self):
 		self.check_stock_frozen_date()
 		self.calculate_batch_qty()
 
 		if not self.get("via_landed_cost_voucher"):
 			from erpnext.stock.doctype.serial_no.serial_no import process_serial_no
+
 			process_serial_no(self)
 
 	def calculate_batch_qty(self):
 		if self.batch_no:
-			batch_qty = frappe.db.get_value("Stock Ledger Entry",
-				{"docstatus": 1, "batch_no": self.batch_no, "is_cancelled": 0},
-				"sum(actual_qty)") or 0
+			batch_qty = (
+				frappe.db.get_value(
+					"Stock Ledger Entry",
+					{"docstatus": 1, "batch_no": self.batch_no, "is_cancelled": 0},
+					"sum(actual_qty)",
+				)
+				or 0
+			)
 			frappe.db.set_value("Batch", self.batch_no, "batch_qty", batch_qty)
 
 	def validate_mandatory(self):
-		mandatory = ['warehouse','posting_date','voucher_type','voucher_no','company']
+		mandatory = ["warehouse", "posting_date", "voucher_type", "voucher_no", "company"]
 		for k in mandatory:
 			if not self.get(k):
 				frappe.throw(_("{0} is required").format(self.meta.get_label(k)))
@@ -66,9 +80,13 @@
 			frappe.throw(_("Actual Qty is mandatory"))
 
 	def validate_item(self):
-		item_det = frappe.db.sql("""select name, item_name, has_batch_no, docstatus,
+		item_det = frappe.db.sql(
+			"""select name, item_name, has_batch_no, docstatus,
 			is_stock_item, has_variants, stock_uom, create_new_batch
-			from tabItem where name=%s""", self.item_code, as_dict=True)
+			from tabItem where name=%s""",
+			self.item_code,
+			as_dict=True,
+		)
 
 		if not item_det:
 			frappe.throw(_("Item {0} not found").format(self.item_code))
@@ -80,39 +98,58 @@
 
 		# check if batch number is valid
 		if item_det.has_batch_no == 1:
-			batch_item = self.item_code if self.item_code == item_det.item_name else self.item_code + ":" + item_det.item_name
+			batch_item = (
+				self.item_code
+				if self.item_code == item_det.item_name
+				else self.item_code + ":" + item_det.item_name
+			)
 			if not self.batch_no:
 				frappe.throw(_("Batch number is mandatory for Item {0}").format(batch_item))
-			elif not frappe.db.get_value("Batch",{"item": self.item_code, "name": self.batch_no}):
-				frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, batch_item))
+			elif not frappe.db.get_value("Batch", {"item": self.item_code, "name": self.batch_no}):
+				frappe.throw(
+					_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, batch_item)
+				)
 
 		elif item_det.has_batch_no == 0 and self.batch_no and self.is_cancelled == 0:
 			frappe.throw(_("The Item {0} cannot have Batch").format(self.item_code))
 
 		if item_det.has_variants:
-			frappe.throw(_("Stock cannot exist for Item {0} since has variants").format(self.item_code),
-				ItemTemplateCannotHaveStock)
+			frappe.throw(
+				_("Stock cannot exist for Item {0} since has variants").format(self.item_code),
+				ItemTemplateCannotHaveStock,
+			)
 
 		self.stock_uom = item_det.stock_uom
 
 	def check_stock_frozen_date(self):
-		stock_settings = frappe.get_cached_doc('Stock Settings')
+		stock_settings = frappe.get_cached_doc("Stock Settings")
 
 		if stock_settings.stock_frozen_upto:
-			if (getdate(self.posting_date) <= getdate(stock_settings.stock_frozen_upto)
-				and stock_settings.stock_auth_role not in frappe.get_roles()):
-				frappe.throw(_("Stock transactions before {0} are frozen")
-					.format(formatdate(stock_settings.stock_frozen_upto)), StockFreezeError)
+			if (
+				getdate(self.posting_date) <= getdate(stock_settings.stock_frozen_upto)
+				and stock_settings.stock_auth_role not in frappe.get_roles()
+			):
+				frappe.throw(
+					_("Stock transactions before {0} are frozen").format(
+						formatdate(stock_settings.stock_frozen_upto)
+					),
+					StockFreezeError,
+				)
 
 		stock_frozen_upto_days = cint(stock_settings.stock_frozen_upto_days)
 		if stock_frozen_upto_days:
-			older_than_x_days_ago = (add_days(getdate(self.posting_date), stock_frozen_upto_days) <= date.today())
+			older_than_x_days_ago = (
+				add_days(getdate(self.posting_date), stock_frozen_upto_days) <= date.today()
+			)
 			if older_than_x_days_ago and stock_settings.stock_auth_role not in frappe.get_roles():
-				frappe.throw(_("Not allowed to update stock transactions older than {0}").format(stock_frozen_upto_days), StockFreezeError)
+				frappe.throw(
+					_("Not allowed to update stock transactions older than {0}").format(stock_frozen_upto_days),
+					StockFreezeError,
+				)
 
 	def scrub_posting_time(self):
-		if not self.posting_time or self.posting_time == '00:0':
-			self.posting_time = '00:00'
+		if not self.posting_time or self.posting_time == "00:0":
+			self.posting_time = "00:00"
 
 	def validate_batch(self):
 		if self.batch_no and self.voucher_type != "Stock Entry":
@@ -126,43 +163,66 @@
 			self.fiscal_year = get_fiscal_year(self.posting_date, company=self.company)[0]
 		else:
 			from erpnext.accounts.utils import validate_fiscal_year
-			validate_fiscal_year(self.posting_date, self.fiscal_year, self.company,
-				self.meta.get_label("posting_date"), self)
+
+			validate_fiscal_year(
+				self.posting_date, self.fiscal_year, self.company, self.meta.get_label("posting_date"), self
+			)
 
 	def block_transactions_against_group_warehouse(self):
 		from erpnext.stock.utils import is_group_warehouse
+
 		is_group_warehouse(self.warehouse)
 
 	def validate_with_last_transaction_posting_time(self):
-		authorized_role = frappe.db.get_single_value("Stock Settings", "role_allowed_to_create_edit_back_dated_transactions")
+		authorized_role = frappe.db.get_single_value(
+			"Stock Settings", "role_allowed_to_create_edit_back_dated_transactions"
+		)
 		if authorized_role:
 			authorized_users = get_users(authorized_role)
 			if authorized_users and frappe.session.user not in authorized_users:
-				last_transaction_time = frappe.db.sql("""
+				last_transaction_time = frappe.db.sql(
+					"""
 					select MAX(timestamp(posting_date, posting_time)) as posting_time
 					from `tabStock Ledger Entry`
 					where docstatus = 1 and is_cancelled = 0 and item_code = %s
-					and warehouse = %s""", (self.item_code, self.warehouse))[0][0]
+					and warehouse = %s""",
+					(self.item_code, self.warehouse),
+				)[0][0]
 
-				cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00")
+				cur_doc_posting_datetime = "%s %s" % (
+					self.posting_date,
+					self.get("posting_time") or "00:00:00",
+				)
 
-				if last_transaction_time and get_datetime(cur_doc_posting_datetime) < get_datetime(last_transaction_time):
-					msg = _("Last Stock Transaction for item {0} under warehouse {1} was on {2}.").format(frappe.bold(self.item_code),
-						frappe.bold(self.warehouse), frappe.bold(last_transaction_time))
+				if last_transaction_time and get_datetime(cur_doc_posting_datetime) < get_datetime(
+					last_transaction_time
+				):
+					msg = _("Last Stock Transaction for item {0} under warehouse {1} was on {2}.").format(
+						frappe.bold(self.item_code), frappe.bold(self.warehouse), frappe.bold(last_transaction_time)
+					)
 
-					msg += "<br><br>" + _("You are not authorized to make/edit Stock Transactions for Item {0} under warehouse {1} before this time.").format(
-						frappe.bold(self.item_code), frappe.bold(self.warehouse))
+					msg += "<br><br>" + _(
+						"You are not authorized to make/edit Stock Transactions for Item {0} under warehouse {1} before this time."
+					).format(frappe.bold(self.item_code), frappe.bold(self.warehouse))
 
 					msg += "<br><br>" + _("Please contact any of the following users to {} this transaction.")
 					msg += "<br>" + "<br>".join(authorized_users)
 					frappe.throw(msg, BackDatedStockTransaction, title=_("Backdated Stock Entry"))
 
+	def on_cancel(self):
+		msg = _("Individual Stock Ledger Entry cannot be cancelled.")
+		msg += "<br>" + _("Please cancel related transaction.")
+		frappe.throw(msg)
+
+
 def on_doctype_update():
-	if not frappe.db.has_index('tabStock Ledger Entry', 'posting_sort_index'):
+	if not frappe.db.has_index("tabStock Ledger Entry", "posting_sort_index"):
 		frappe.db.commit()
-		frappe.db.add_index("Stock Ledger Entry",
+		frappe.db.add_index(
+			"Stock Ledger Entry",
 			fields=["posting_date", "posting_time", "name"],
-			index_name="posting_sort_index")
+			index_name="posting_sort_index",
+		)
 
 	frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"])
 	frappe.db.add_index("Stock Ledger Entry", ["batch_no", "item_code", "warehouse"])
diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
index 684a8d4..f669e90 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
@@ -2,18 +2,17 @@
 # See license.txt
 
 import json
-from operator import itemgetter
 from uuid import uuid4
 
 import frappe
 from frappe.core.page.permission_manager.permission_manager import reset
-from frappe.tests.utils import FrappeTestCase
-from frappe.utils import add_days, today
+from frappe.custom.doctype.property_setter.property_setter import make_property_setter
+from frappe.query_builder.functions import CombineDatetime
+from frappe.tests.utils import FrappeTestCase, change_settings
+from frappe.utils import add_days, add_to_date, flt, today
 
-from erpnext.stock.doctype.delivery_note.test_delivery_note import (
-	create_delivery_note,
-	create_return_delivery_note,
-)
+from erpnext.accounts.doctype.gl_entry.gl_entry import rename_gle_sle_docs
+from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
 from erpnext.stock.doctype.item.test_item import make_item
 from erpnext.stock.doctype.landed_cost_voucher.test_landed_cost_voucher import (
 	create_landed_cost_voucher,
@@ -25,16 +24,26 @@
 	create_stock_reconciliation,
 )
 from erpnext.stock.stock_ledger import get_previous_sle
+from erpnext.stock.tests.test_utils import StockTestMixin
 
 
-class TestStockLedgerEntry(FrappeTestCase):
+class TestStockLedgerEntry(FrappeTestCase, StockTestMixin):
 	def setUp(self):
 		items = create_items()
-		reset('Stock Entry')
+		reset("Stock Entry")
 
 		# delete SLE and BINs for all items
-		frappe.db.sql("delete from `tabStock Ledger Entry` where item_code in (%s)" % (', '.join(['%s']*len(items))), items)
-		frappe.db.sql("delete from `tabBin` where item_code in (%s)" % (', '.join(['%s']*len(items))), items)
+		frappe.db.sql(
+			"delete from `tabStock Ledger Entry` where item_code in (%s)"
+			% (", ".join(["%s"] * len(items))),
+			items,
+		)
+		frappe.db.sql(
+			"delete from `tabBin` where item_code in (%s)" % (", ".join(["%s"] * len(items))), items
+		)
+
+	def tearDown(self):
+		frappe.db.rollback()
 
 	def test_item_cost_reposting(self):
 		company = "_Test Company"
@@ -46,9 +55,11 @@
 			qty=50,
 			rate=100,
 			company=company,
-			expense_account = "Stock Adjustment - _TC" if frappe.get_all("Stock Ledger Entry") else "Temporary Opening - _TC",
-			posting_date='2020-04-10',
-			posting_time='14:00'
+			expense_account="Stock Adjustment - _TC"
+			if frappe.get_all("Stock Ledger Entry")
+			else "Temporary Opening - _TC",
+			posting_date="2020-04-10",
+			posting_time="14:00",
 		)
 
 		# _Test Item for Reposting at FG warehouse on 20-04-2020: Qty = 10, Rate = 200
@@ -58,9 +69,11 @@
 			qty=10,
 			rate=200,
 			company=company,
-			expense_account="Stock Adjustment - _TC" if frappe.get_all("Stock Ledger Entry") else "Temporary Opening - _TC",
-			posting_date='2020-04-20',
-			posting_time='14:00'
+			expense_account="Stock Adjustment - _TC"
+			if frappe.get_all("Stock Ledger Entry")
+			else "Temporary Opening - _TC",
+			posting_date="2020-04-20",
+			posting_time="14:00",
 		)
 
 		# _Test Item for Reposting transferred from Stores to FG warehouse on 30-04-2020
@@ -70,28 +83,40 @@
 			target="Finished Goods - _TC",
 			company=company,
 			qty=10,
-			expense_account="Stock Adjustment - _TC" if frappe.get_all("Stock Ledger Entry") else "Temporary Opening - _TC",
-			posting_date='2020-04-30',
-			posting_time='14:00'
+			expense_account="Stock Adjustment - _TC"
+			if frappe.get_all("Stock Ledger Entry")
+			else "Temporary Opening - _TC",
+			posting_date="2020-04-30",
+			posting_time="14:00",
 		)
-		target_wh_sle = frappe.db.get_value('Stock Ledger Entry', {
-			"item_code": "_Test Item for Reposting",
-			"warehouse": "Finished Goods - _TC",
-			"voucher_type": "Stock Entry",
-			"voucher_no": se.name
-		}, ["valuation_rate"], as_dict=1)
+		target_wh_sle = frappe.db.get_value(
+			"Stock Ledger Entry",
+			{
+				"item_code": "_Test Item for Reposting",
+				"warehouse": "Finished Goods - _TC",
+				"voucher_type": "Stock Entry",
+				"voucher_no": se.name,
+			},
+			["valuation_rate"],
+			as_dict=1,
+		)
 
 		self.assertEqual(target_wh_sle.get("valuation_rate"), 150)
 
 		# Repack entry on 5-5-2020
-		repack = create_repack_entry(company=company, posting_date='2020-05-05', posting_time='14:00')
+		repack = create_repack_entry(company=company, posting_date="2020-05-05", posting_time="14:00")
 
-		finished_item_sle = frappe.db.get_value('Stock Ledger Entry', {
-			"item_code": "_Test Finished Item for Reposting",
-			"warehouse": "Finished Goods - _TC",
-			"voucher_type": "Stock Entry",
-			"voucher_no": repack.name
-		}, ["incoming_rate", "valuation_rate"], as_dict=1)
+		finished_item_sle = frappe.db.get_value(
+			"Stock Ledger Entry",
+			{
+				"item_code": "_Test Finished Item for Reposting",
+				"warehouse": "Finished Goods - _TC",
+				"voucher_type": "Stock Entry",
+				"voucher_no": repack.name,
+			},
+			["incoming_rate", "valuation_rate"],
+			as_dict=1,
+		)
 		self.assertEqual(finished_item_sle.get("incoming_rate"), 540)
 		self.assertEqual(finished_item_sle.get("valuation_rate"), 540)
 
@@ -102,29 +127,37 @@
 			qty=50,
 			rate=150,
 			company=company,
-			expense_account ="Stock Adjustment - _TC" if frappe.get_all("Stock Ledger Entry") else "Temporary Opening - _TC",
-			posting_date='2020-04-12',
-			posting_time='14:00'
+			expense_account="Stock Adjustment - _TC"
+			if frappe.get_all("Stock Ledger Entry")
+			else "Temporary Opening - _TC",
+			posting_date="2020-04-12",
+			posting_time="14:00",
 		)
 
-
 		# Check valuation rate of finished goods warehouse after back-dated entry at Stores
-		target_wh_sle = get_previous_sle({
-			"item_code": "_Test Item for Reposting",
-			"warehouse": "Finished Goods - _TC",
-			"posting_date": '2020-04-30',
-			"posting_time": '14:00'
-		})
+		target_wh_sle = get_previous_sle(
+			{
+				"item_code": "_Test Item for Reposting",
+				"warehouse": "Finished Goods - _TC",
+				"posting_date": "2020-04-30",
+				"posting_time": "14:00",
+			}
+		)
 		self.assertEqual(target_wh_sle.get("incoming_rate"), 150)
 		self.assertEqual(target_wh_sle.get("valuation_rate"), 175)
 
 		# Check valuation rate of repacked item after back-dated entry at Stores
-		finished_item_sle = frappe.db.get_value('Stock Ledger Entry', {
-			"item_code": "_Test Finished Item for Reposting",
-			"warehouse": "Finished Goods - _TC",
-			"voucher_type": "Stock Entry",
-			"voucher_no": repack.name
-		}, ["incoming_rate", "valuation_rate"], as_dict=1)
+		finished_item_sle = frappe.db.get_value(
+			"Stock Ledger Entry",
+			{
+				"item_code": "_Test Finished Item for Reposting",
+				"warehouse": "Finished Goods - _TC",
+				"voucher_type": "Stock Entry",
+				"voucher_no": repack.name,
+			},
+			["incoming_rate", "valuation_rate"],
+			as_dict=1,
+		)
 		self.assertEqual(finished_item_sle.get("incoming_rate"), 790)
 		self.assertEqual(finished_item_sle.get("valuation_rate"), 790)
 
@@ -134,76 +167,133 @@
 		self.assertEqual(repack.items[1].get("basic_rate"), 750)
 
 	def test_purchase_return_valuation_reposting(self):
-		pr = make_purchase_receipt(company="_Test Company", posting_date='2020-04-10',
-			warehouse="Stores - _TC", item_code="_Test Item for Reposting", qty=5, rate=100)
+		pr = make_purchase_receipt(
+			company="_Test Company",
+			posting_date="2020-04-10",
+			warehouse="Stores - _TC",
+			item_code="_Test Item for Reposting",
+			qty=5,
+			rate=100,
+		)
 
-		return_pr = make_purchase_receipt(company="_Test Company", posting_date='2020-04-15',
-			warehouse="Stores - _TC", item_code="_Test Item for Reposting", is_return=1, return_against=pr.name, qty=-2)
+		return_pr = make_purchase_receipt(
+			company="_Test Company",
+			posting_date="2020-04-15",
+			warehouse="Stores - _TC",
+			item_code="_Test Item for Reposting",
+			is_return=1,
+			return_against=pr.name,
+			qty=-2,
+		)
 
 		# check sle
-		outgoing_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
-			"voucher_no": return_pr.name}, ["outgoing_rate", "stock_value_difference"])
+		outgoing_rate, stock_value_difference = frappe.db.get_value(
+			"Stock Ledger Entry",
+			{"voucher_type": "Purchase Receipt", "voucher_no": return_pr.name},
+			["outgoing_rate", "stock_value_difference"],
+		)
 
 		self.assertEqual(outgoing_rate, 100)
 		self.assertEqual(stock_value_difference, -200)
 
 		create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company)
 
-		outgoing_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
-			"voucher_no": return_pr.name}, ["outgoing_rate", "stock_value_difference"])
+		outgoing_rate, stock_value_difference = frappe.db.get_value(
+			"Stock Ledger Entry",
+			{"voucher_type": "Purchase Receipt", "voucher_no": return_pr.name},
+			["outgoing_rate", "stock_value_difference"],
+		)
 
 		self.assertEqual(outgoing_rate, 110)
 		self.assertEqual(stock_value_difference, -220)
 
 	def test_sales_return_valuation_reposting(self):
 		company = "_Test Company"
-		item_code="_Test Item for Reposting"
+		item_code = "_Test Item for Reposting"
 
 		# Purchase Return: Qty = 5, Rate = 100
-		pr = make_purchase_receipt(company=company, posting_date='2020-04-10',
-			warehouse="Stores - _TC", item_code=item_code, qty=5, rate=100)
+		pr = make_purchase_receipt(
+			company=company,
+			posting_date="2020-04-10",
+			warehouse="Stores - _TC",
+			item_code=item_code,
+			qty=5,
+			rate=100,
+		)
 
-		#Delivery Note: Qty = 5, Rate = 150
-		dn = create_delivery_note(item_code=item_code, qty=5, rate=150, warehouse="Stores - _TC",
-			company=company, expense_account="Cost of Goods Sold - _TC", cost_center="Main - _TC")
+		# Delivery Note: Qty = 5, Rate = 150
+		dn = create_delivery_note(
+			item_code=item_code,
+			qty=5,
+			rate=150,
+			warehouse="Stores - _TC",
+			company=company,
+			expense_account="Cost of Goods Sold - _TC",
+			cost_center="Main - _TC",
+		)
 
 		# check outgoing_rate for DN
-		outgoing_rate = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note",
-			"voucher_no": dn.name}, "stock_value_difference") / 5)
+		outgoing_rate = abs(
+			frappe.db.get_value(
+				"Stock Ledger Entry",
+				{"voucher_type": "Delivery Note", "voucher_no": dn.name},
+				"stock_value_difference",
+			)
+			/ 5
+		)
 
 		self.assertEqual(dn.items[0].incoming_rate, 100)
 		self.assertEqual(outgoing_rate, 100)
 
 		# Return Entry: Qty = -2, Rate = 150
-		return_dn = create_delivery_note(is_return=1, return_against=dn.name, item_code=item_code, qty=-2, rate=150,
-			company=company, warehouse="Stores - _TC", expense_account="Cost of Goods Sold - _TC", cost_center="Main - _TC")
+		return_dn = create_delivery_note(
+			is_return=1,
+			return_against=dn.name,
+			item_code=item_code,
+			qty=-2,
+			rate=150,
+			company=company,
+			warehouse="Stores - _TC",
+			expense_account="Cost of Goods Sold - _TC",
+			cost_center="Main - _TC",
+		)
 
 		# check incoming rate for Return entry
-		incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
+		incoming_rate, stock_value_difference = frappe.db.get_value(
+			"Stock Ledger Entry",
 			{"voucher_type": "Delivery Note", "voucher_no": return_dn.name},
-			["incoming_rate", "stock_value_difference"])
+			["incoming_rate", "stock_value_difference"],
+		)
 
 		self.assertEqual(return_dn.items[0].incoming_rate, 100)
 		self.assertEqual(incoming_rate, 100)
 		self.assertEqual(stock_value_difference, 200)
 
-		#-------------------------------
+		# -------------------------------
 
 		# Landed Cost Voucher to update the rate of incoming Purchase Return: Additional cost = 50
 		lcv = create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company)
 
 		# check outgoing_rate for DN after reposting
-		outgoing_rate = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note",
-			"voucher_no": dn.name}, "stock_value_difference") / 5)
+		outgoing_rate = abs(
+			frappe.db.get_value(
+				"Stock Ledger Entry",
+				{"voucher_type": "Delivery Note", "voucher_no": dn.name},
+				"stock_value_difference",
+			)
+			/ 5
+		)
 		self.assertEqual(outgoing_rate, 110)
 
 		dn.reload()
 		self.assertEqual(dn.items[0].incoming_rate, 110)
 
 		# check incoming rate for Return entry after reposting
-		incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
+		incoming_rate, stock_value_difference = frappe.db.get_value(
+			"Stock Ledger Entry",
 			{"voucher_type": "Delivery Note", "voucher_no": return_dn.name},
-			["incoming_rate", "stock_value_difference"])
+			["incoming_rate", "stock_value_difference"],
+		)
 
 		self.assertEqual(incoming_rate, 110)
 		self.assertEqual(stock_value_difference, 220)
@@ -219,54 +309,93 @@
 
 	def test_reposting_of_sales_return_for_packed_item(self):
 		company = "_Test Company"
-		packed_item_code="_Test Item for Reposting"
+		packed_item_code = "_Test Item for Reposting"
 		bundled_item = "_Test Bundled Item for Reposting"
 		create_product_bundle_item(bundled_item, [[packed_item_code, 4]])
 
 		# Purchase Return: Qty = 50, Rate = 100
-		pr = make_purchase_receipt(company=company, posting_date='2020-04-10',
-			warehouse="Stores - _TC", item_code=packed_item_code, qty=50, rate=100)
+		pr = make_purchase_receipt(
+			company=company,
+			posting_date="2020-04-10",
+			warehouse="Stores - _TC",
+			item_code=packed_item_code,
+			qty=50,
+			rate=100,
+		)
 
-		#Delivery Note: Qty = 5, Rate = 150
-		dn = create_delivery_note(item_code=bundled_item, qty=5, rate=150, warehouse="Stores - _TC",
-			company=company, expense_account="Cost of Goods Sold - _TC", cost_center="Main - _TC")
+		# Delivery Note: Qty = 5, Rate = 150
+		dn = create_delivery_note(
+			item_code=bundled_item,
+			qty=5,
+			rate=150,
+			warehouse="Stores - _TC",
+			company=company,
+			expense_account="Cost of Goods Sold - _TC",
+			cost_center="Main - _TC",
+		)
 
 		# check outgoing_rate for DN
-		outgoing_rate = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note",
-			"voucher_no": dn.name}, "stock_value_difference") / 20)
+		outgoing_rate = abs(
+			frappe.db.get_value(
+				"Stock Ledger Entry",
+				{"voucher_type": "Delivery Note", "voucher_no": dn.name},
+				"stock_value_difference",
+			)
+			/ 20
+		)
 
 		self.assertEqual(dn.packed_items[0].incoming_rate, 100)
 		self.assertEqual(outgoing_rate, 100)
 
 		# Return Entry: Qty = -2, Rate = 150
-		return_dn = create_return_delivery_note(source_name=dn.name, rate=150, qty=-2)
+		return_dn = create_delivery_note(
+			is_return=1,
+			return_against=dn.name,
+			item_code=bundled_item,
+			qty=-2,
+			rate=150,
+			company=company,
+			warehouse="Stores - _TC",
+			expense_account="Cost of Goods Sold - _TC",
+			cost_center="Main - _TC",
+		)
 
 		# check incoming rate for Return entry
-		incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
+		incoming_rate, stock_value_difference = frappe.db.get_value(
+			"Stock Ledger Entry",
 			{"voucher_type": "Delivery Note", "voucher_no": return_dn.name},
-			["incoming_rate", "stock_value_difference"])
+			["incoming_rate", "stock_value_difference"],
+		)
 
 		self.assertEqual(return_dn.packed_items[0].incoming_rate, 100)
 		self.assertEqual(incoming_rate, 100)
 		self.assertEqual(stock_value_difference, 800)
 
-		#-------------------------------
+		# -------------------------------
 
 		# Landed Cost Voucher to update the rate of incoming Purchase Return: Additional cost = 50
 		lcv = create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company)
 
 		# check outgoing_rate for DN after reposting
-		outgoing_rate = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note",
-			"voucher_no": dn.name}, "stock_value_difference") / 20)
+		outgoing_rate = abs(
+			frappe.db.get_value(
+				"Stock Ledger Entry",
+				{"voucher_type": "Delivery Note", "voucher_no": dn.name},
+				"stock_value_difference",
+			)
+			/ 20
+		)
 		self.assertEqual(outgoing_rate, 101)
 
 		dn.reload()
 		self.assertEqual(dn.packed_items[0].incoming_rate, 101)
 
 		# check incoming rate for Return entry after reposting
-		incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
+		incoming_rate, stock_value_difference = frappe.db.get_value(
+			"Stock Ledger Entry",
 			{"voucher_type": "Delivery Note", "voucher_no": return_dn.name},
-			["incoming_rate", "stock_value_difference"])
+			["incoming_rate", "stock_value_difference"],
+		)
 
 		self.assertEqual(incoming_rate, 101)
 		self.assertEqual(stock_value_difference, 808)
@@ -284,20 +413,35 @@
 		from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
 
 		company = "_Test Company"
-		rm_item_code="_Test Item for Reposting"
+		rm_item_code = "_Test Item for Reposting"
 		subcontracted_item = "_Test Subcontracted Item for Reposting"
 
-		frappe.db.set_value("Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", "BOM")
-		make_bom(item = subcontracted_item, raw_materials =[rm_item_code], currency="INR")
+		frappe.db.set_value(
+			"Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", "BOM"
+		)
+		make_bom(item=subcontracted_item, raw_materials=[rm_item_code], currency="INR")
 
 		# Purchase raw materials on supplier warehouse: Qty = 50, Rate = 100
-		pr = make_purchase_receipt(company=company, posting_date='2020-04-10',
-			warehouse="Stores - _TC", item_code=rm_item_code, qty=10, rate=100)
+		pr = make_purchase_receipt(
+			company=company,
+			posting_date="2020-04-10",
+			warehouse="Stores - _TC",
+			item_code=rm_item_code,
+			qty=10,
+			rate=100,
+		)
 
 		# Purchase Receipt for subcontracted item
-		pr1 = make_purchase_receipt(company=company, posting_date='2020-04-20',
-			warehouse="Finished Goods - _TC", supplier_warehouse="Stores - _TC",
-			item_code=subcontracted_item, qty=10, rate=20, is_subcontracted="Yes")
+		pr1 = make_purchase_receipt(
+			company=company,
+			posting_date="2020-04-20",
+			warehouse="Finished Goods - _TC",
+			supplier_warehouse="Stores - _TC",
+			item_code=subcontracted_item,
+			qty=10,
+			rate=20,
+			is_subcontracted=1,
+		)
 
 		self.assertEqual(pr1.items[0].valuation_rate, 120)
 
@@ -308,8 +452,11 @@
 		self.assertEqual(pr1.items[0].valuation_rate, 125)
 
 		# check outgoing_rate for DN after reposting
-		incoming_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
-			"voucher_no": pr1.name, "item_code": subcontracted_item}, "incoming_rate")
+		incoming_rate = frappe.db.get_value(
+			"Stock Ledger Entry",
+			{"voucher_type": "Purchase Receipt", "voucher_no": pr1.name, "item_code": subcontracted_item},
+			"incoming_rate",
+		)
 		self.assertEqual(incoming_rate, 125)
 
 		# cleanup data
@@ -319,8 +466,9 @@
 
 	def test_back_dated_entry_not_allowed(self):
 		# Back dated stock transactions are only allowed to stock managers
-		frappe.db.set_value("Stock Settings", None,
-			"role_allowed_to_create_edit_back_dated_transactions", "Stock Manager")
+		frappe.db.set_value(
+			"Stock Settings", None, "role_allowed_to_create_edit_back_dated_transactions", "Stock Manager"
+		)
 
 		# Set User with Stock User role but not Stock Manager
 		try:
@@ -331,8 +479,13 @@
 			frappe.set_user(user.name)
 
 			stock_entry_on_today = make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100)
-			back_dated_se_1 = make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100,
-				posting_date=add_days(today(), -1), do_not_submit=True)
+			back_dated_se_1 = make_stock_entry(
+				target="_Test Warehouse - _TC",
+				qty=10,
+				basic_rate=100,
+				posting_date=add_days(today(), -1),
+				do_not_submit=True,
+			)
 
 			# Block back-dated entry
 			self.assertRaises(BackDatedStockTransaction, back_dated_se_1.submit)
@@ -342,14 +495,17 @@
 			frappe.set_user(user.name)
 
 			# Back dated entry allowed to Stock Manager
-			back_dated_se_2 = make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100,
-				posting_date=add_days(today(), -1))
+			back_dated_se_2 = make_stock_entry(
+				target="_Test Warehouse - _TC", qty=10, basic_rate=100, posting_date=add_days(today(), -1)
+			)
 
 			back_dated_se_2.cancel()
 			stock_entry_on_today.cancel()
 
 		finally:
-			frappe.db.set_value("Stock Settings", None, "role_allowed_to_create_edit_back_dated_transactions", None)
+			frappe.db.set_value(
+				"Stock Settings", None, "role_allowed_to_create_edit_back_dated_transactions", None
+			)
 			frappe.set_user("Administrator")
 			user.remove_roles("Stock Manager")
 
@@ -359,13 +515,13 @@
 		# Incoming Entries for Stock Value check
 		pr_entry_list = [
 			(item, warehouses[0], batches[0], 1, 100),
-			(item, warehouses[0], batches[1], 1,  50),
+			(item, warehouses[0], batches[1], 1, 50),
 			(item, warehouses[0], batches[0], 1, 150),
 			(item, warehouses[0], batches[1], 1, 100),
 		]
 		prs = create_purchase_receipt_entries_for_batchwise_item_valuation_test(pr_entry_list)
-		sle_details = fetch_sle_details_for_doc_list(prs, ['stock_value'])
-		sv_list = [d['stock_value'] for d in sle_details]
+		sle_details = fetch_sle_details_for_doc_list(prs, ["stock_value"])
+		sv_list = [d["stock_value"] for d in sle_details]
 		expected_sv = [100, 150, 300, 400]
 		self.assertEqual(expected_sv, sv_list, "Incorrect 'Stock Value' values")
 
@@ -374,48 +530,25 @@
 			(item, warehouses[0], batches[1], 1, 200),
 			(item, warehouses[0], batches[0], 1, 200),
 			(item, warehouses[0], batches[1], 1, 200),
-			(item, warehouses[0], batches[0], 1, 200)
+			(item, warehouses[0], batches[0], 1, 200),
 		]
 		dns = create_delivery_note_entries_for_batchwise_item_valuation_test(dn_entry_list)
-		sle_details = fetch_sle_details_for_doc_list(dns, ['stock_value_difference'])
-		svd_list = [-1 * d['stock_value_difference'] for d in sle_details]
+		sle_details = fetch_sle_details_for_doc_list(dns, ["stock_value_difference"])
+		svd_list = [-1 * d["stock_value_difference"] for d in sle_details]
 		expected_incoming_rates = expected_abs_svd = [75, 125, 75, 125]
 
 		self.assertEqual(expected_abs_svd, svd_list, "Incorrect 'Stock Value Difference' values")
 		for dn, incoming_rate in zip(dns, expected_incoming_rates):
 			self.assertEqual(
-				dn.items[0].incoming_rate, incoming_rate,
-				"Incorrect 'Incoming Rate' values fetched for DN items"
+				dn.items[0].incoming_rate,
+				incoming_rate,
+				"Incorrect 'Incoming Rate' values fetched for DN items",
 			)
 
-
-	def assertSLEs(self, doc, expected_sles, sle_filters=None):
-		""" Compare sorted SLEs, useful for vouchers that create multiple SLEs for same line"""
-
-		filters = {"voucher_no": doc.name, "voucher_type": doc.doctype, "is_cancelled": 0}
-		if sle_filters:
-			filters.update(sle_filters)
-		sles = frappe.get_all("Stock Ledger Entry", fields=["*"], filters=filters,
-			order_by="timestamp(posting_date, posting_time), creation")
-
-		for exp_sle, act_sle in zip(expected_sles, sles):
-			for k, v in exp_sle.items():
-				act_value = act_sle[k]
-				if k == "stock_queue":
-					act_value = json.loads(act_value)
-					if act_value and act_value[0][0] == 0:
-						# ignore empty fifo bins
-						continue
-
-				self.assertEqual(v, act_value, msg=f"{k} doesn't match \n{exp_sle}\n{act_sle}")
-
-
 	def test_batchwise_item_valuation_stock_reco(self):
 		item, warehouses, batches = setup_item_valuation_test()
-		state = {
-			"stock_value" : 0.0,
-			"qty": 0.0
-		}
+		state = {"stock_value": 0.0, "qty": 0.0}
+
 		def update_invariants(exp_sles):
 			for sle in exp_sles:
 				state["stock_value"] += sle["stock_value_difference"]
@@ -423,33 +556,41 @@
 				sle["stock_value"] = state["stock_value"]
 				sle["qty_after_transaction"] = state["qty"]
 
-		osr1 = create_stock_reconciliation(warehouse=warehouses[0], item_code=item, qty=10, rate=100, batch_no=batches[1])
+		osr1 = create_stock_reconciliation(
+			warehouse=warehouses[0], item_code=item, qty=10, rate=100, batch_no=batches[1]
+		)
 		expected_sles = [
 			{"actual_qty": 10, "stock_value_difference": 1000},
 		]
 		update_invariants(expected_sles)
 		self.assertSLEs(osr1, expected_sles)
 
-		osr2 = create_stock_reconciliation(warehouse=warehouses[0], item_code=item, qty=13, rate=200, batch_no=batches[0])
+		osr2 = create_stock_reconciliation(
+			warehouse=warehouses[0], item_code=item, qty=13, rate=200, batch_no=batches[0]
+		)
 		expected_sles = [
-			{"actual_qty": 13, "stock_value_difference": 200*13},
+			{"actual_qty": 13, "stock_value_difference": 200 * 13},
 		]
 		update_invariants(expected_sles)
 		self.assertSLEs(osr2, expected_sles)
 
-		sr1 = create_stock_reconciliation(warehouse=warehouses[0], item_code=item, qty=5, rate=50, batch_no=batches[1])
+		sr1 = create_stock_reconciliation(
+			warehouse=warehouses[0], item_code=item, qty=5, rate=50, batch_no=batches[1]
+		)
 
 		expected_sles = [
 			{"actual_qty": -10, "stock_value_difference": -10 * 100},
-			{"actual_qty": 5, "stock_value_difference": 250}
+			{"actual_qty": 5, "stock_value_difference": 250},
 		]
 		update_invariants(expected_sles)
 		self.assertSLEs(sr1, expected_sles)
 
-		sr2 = create_stock_reconciliation(warehouse=warehouses[0], item_code=item, qty=20, rate=75, batch_no=batches[0])
+		sr2 = create_stock_reconciliation(
+			warehouse=warehouses[0], item_code=item, qty=20, rate=75, batch_no=batches[0]
+		)
 		expected_sles = [
 			{"actual_qty": -13, "stock_value_difference": -13 * 200},
-			{"actual_qty": 20, "stock_value_difference": 20 * 75}
+			{"actual_qty": 20, "stock_value_difference": 20 * 75},
 		]
 		update_invariants(expected_sles)
 		self.assertSLEs(sr2, expected_sles)
@@ -459,108 +600,190 @@
 		source = warehouses[0]
 		target = warehouses[1]
 
-		unrelated_batch = make_stock_entry(item_code=item_code, target=source, batch_no=batches[1],
-				qty=5, rate=10)
-		self.assertSLEs(unrelated_batch, [
-			{"actual_qty": 5, "stock_value_difference": 10 * 5},
-		])
+		unrelated_batch = make_stock_entry(
+			item_code=item_code, target=source, batch_no=batches[1], qty=5, rate=10
+		)
+		self.assertSLEs(
+			unrelated_batch,
+			[
+				{"actual_qty": 5, "stock_value_difference": 10 * 5},
+			],
+		)
 
-		reciept = make_stock_entry(item_code=item_code, target=source, batch_no=batches[0], qty=5, rate=10)
-		self.assertSLEs(reciept, [
-			{"actual_qty": 5, "stock_value_difference": 10 * 5},
-		])
+		reciept = make_stock_entry(
+			item_code=item_code, target=source, batch_no=batches[0], qty=5, rate=10
+		)
+		self.assertSLEs(
+			reciept,
+			[
+				{"actual_qty": 5, "stock_value_difference": 10 * 5},
+			],
+		)
 
-		transfer = make_stock_entry(item_code=item_code, source=source, target=target, batch_no=batches[0], qty=5)
-		self.assertSLEs(transfer, [
-			{"actual_qty": -5, "stock_value_difference": -10 * 5, "warehouse": source},
-			{"actual_qty": 5, "stock_value_difference": 10 * 5, "warehouse": target}
-		])
+		transfer = make_stock_entry(
+			item_code=item_code, source=source, target=target, batch_no=batches[0], qty=5
+		)
+		self.assertSLEs(
+			transfer,
+			[
+				{"actual_qty": -5, "stock_value_difference": -10 * 5, "warehouse": source},
+				{"actual_qty": 5, "stock_value_difference": 10 * 5, "warehouse": target},
+			],
+		)
 
-		backdated_receipt = make_stock_entry(item_code=item_code, target=source, batch_no=batches[0],
-				qty=5, rate=20, posting_date=add_days(today(), -1))
-		self.assertSLEs(backdated_receipt, [
-			{"actual_qty": 5, "stock_value_difference": 20 * 5},
-		])
+		backdated_receipt = make_stock_entry(
+			item_code=item_code,
+			target=source,
+			batch_no=batches[0],
+			qty=5,
+			rate=20,
+			posting_date=add_days(today(), -1),
+		)
+		self.assertSLEs(
+			backdated_receipt,
+			[
+				{"actual_qty": 5, "stock_value_difference": 20 * 5},
+			],
+		)
 
 		# check reposted average rate in *future* transfer
-		self.assertSLEs(transfer, [
-			{"actual_qty": -5, "stock_value_difference": -15 * 5, "warehouse": source, "stock_value": 15 * 5 + 10 * 5},
-			{"actual_qty": 5, "stock_value_difference": 15 * 5, "warehouse": target, "stock_value": 15 * 5}
-		])
+		self.assertSLEs(
+			transfer,
+			[
+				{
+					"actual_qty": -5,
+					"stock_value_difference": -15 * 5,
+					"warehouse": source,
+					"stock_value": 15 * 5 + 10 * 5,
+				},
+				{
+					"actual_qty": 5,
+					"stock_value_difference": 15 * 5,
+					"warehouse": target,
+					"stock_value": 15 * 5,
+				},
+			],
+		)
 
-		transfer_unrelated = make_stock_entry(item_code=item_code, source=source,
-				target=target, batch_no=batches[1], qty=5)
-		self.assertSLEs(transfer_unrelated, [
-			{"actual_qty": -5, "stock_value_difference": -10 * 5, "warehouse": source, "stock_value": 15 * 5},
-			{"actual_qty": 5, "stock_value_difference": 10 * 5, "warehouse": target, "stock_value": 15 * 5 + 10 * 5}
-		])
+		transfer_unrelated = make_stock_entry(
+			item_code=item_code, source=source, target=target, batch_no=batches[1], qty=5
+		)
+		self.assertSLEs(
+			transfer_unrelated,
+			[
+				{
+					"actual_qty": -5,
+					"stock_value_difference": -10 * 5,
+					"warehouse": source,
+					"stock_value": 15 * 5,
+				},
+				{
+					"actual_qty": 5,
+					"stock_value_difference": 10 * 5,
+					"warehouse": target,
+					"stock_value": 15 * 5 + 10 * 5,
+				},
+			],
+		)
 
 	def test_intermediate_average_batch_wise_valuation(self):
-		""" A batch has moving average up until posting time,
+		"""A batch has moving average up until posting time,
 		check if same is respected when backdated entry is inserted in middle"""
 		item_code, warehouses, batches = setup_item_valuation_test()
 		warehouse = warehouses[0]
 
 		batch = batches[0]
 
-		yesterday = make_stock_entry(item_code=item_code, target=warehouse, batch_no=batch,
-				qty=1, rate=10, posting_date=add_days(today(), -1))
-		self.assertSLEs(yesterday, [
-			{"actual_qty": 1, "stock_value_difference": 10},
-		])
+		yesterday = make_stock_entry(
+			item_code=item_code,
+			target=warehouse,
+			batch_no=batch,
+			qty=1,
+			rate=10,
+			posting_date=add_days(today(), -1),
+		)
+		self.assertSLEs(
+			yesterday,
+			[
+				{"actual_qty": 1, "stock_value_difference": 10},
+			],
+		)
 
-		tomorrow = make_stock_entry(item_code=item_code, target=warehouse, batch_no=batches[0],
-				qty=1, rate=30, posting_date=add_days(today(), 1))
-		self.assertSLEs(tomorrow, [
-			{"actual_qty": 1, "stock_value_difference": 30},
-		])
+		tomorrow = make_stock_entry(
+			item_code=item_code,
+			target=warehouse,
+			batch_no=batches[0],
+			qty=1,
+			rate=30,
+			posting_date=add_days(today(), 1),
+		)
+		self.assertSLEs(
+			tomorrow,
+			[
+				{"actual_qty": 1, "stock_value_difference": 30},
+			],
+		)
 
-		create_today = make_stock_entry(item_code=item_code, target=warehouse, batch_no=batches[0],
-				qty=1, rate=20)
-		self.assertSLEs(create_today, [
-			{"actual_qty": 1, "stock_value_difference": 20},
-		])
+		create_today = make_stock_entry(
+			item_code=item_code, target=warehouse, batch_no=batches[0], qty=1, rate=20
+		)
+		self.assertSLEs(
+			create_today,
+			[
+				{"actual_qty": 1, "stock_value_difference": 20},
+			],
+		)
 
-		consume_today = make_stock_entry(item_code=item_code, source=warehouse, batch_no=batches[0],
-				qty=1)
-		self.assertSLEs(consume_today, [
-			{"actual_qty": -1, "stock_value_difference": -15},
-		])
+		consume_today = make_stock_entry(
+			item_code=item_code, source=warehouse, batch_no=batches[0], qty=1
+		)
+		self.assertSLEs(
+			consume_today,
+			[
+				{"actual_qty": -1, "stock_value_difference": -15},
+			],
+		)
 
-		consume_tomorrow = make_stock_entry(item_code=item_code, source=warehouse, batch_no=batches[0],
-				qty=2, posting_date=add_days(today(), 2))
-		self.assertSLEs(consume_tomorrow, [
-			{"stock_value_difference": -(30 + 15), "stock_value": 0, "qty_after_transaction": 0},
-		])
+		consume_tomorrow = make_stock_entry(
+			item_code=item_code,
+			source=warehouse,
+			batch_no=batches[0],
+			qty=2,
+			posting_date=add_days(today(), 2),
+		)
+		self.assertSLEs(
+			consume_tomorrow,
+			[
+				{"stock_value_difference": -(30 + 15), "stock_value": 0, "qty_after_transaction": 0},
+			],
+		)
 
 	def test_legacy_item_valuation_stock_entry(self):
 		columns = [
-				'stock_value_difference',
-				'stock_value',
-				'actual_qty',
-				'qty_after_transaction',
-				'stock_queue',
+			"stock_value_difference",
+			"stock_value",
+			"actual_qty",
+			"qty_after_transaction",
+			"stock_queue",
 		]
 		item, warehouses, batches = setup_item_valuation_test(use_batchwise_valuation=0)
 
 		def check_sle_details_against_expected(sle_details, expected_sle_details, detail, columns):
 			for i, (sle_vals, ex_sle_vals) in enumerate(zip(sle_details, expected_sle_details)):
 				for col, sle_val, ex_sle_val in zip(columns, sle_vals, ex_sle_vals):
-					if col == 'stock_queue':
+					if col == "stock_queue":
 						sle_val = get_stock_value_from_q(sle_val)
 						ex_sle_val = get_stock_value_from_q(ex_sle_val)
 					self.assertEqual(
-						sle_val, ex_sle_val,
-						f"Incorrect {col} value on transaction #: {i} in {detail}"
+						sle_val, ex_sle_val, f"Incorrect {col} value on transaction #: {i} in {detail}"
 					)
 
 		# List used to defer assertions to prevent commits cause of error skipped rollback
 		details_list = []
 
-
 		# Test Material Receipt Entries
 		se_entry_list_mr = [
-			(item, None, warehouses[0], batches[0], 1,  50, "2021-01-21"),
+			(item, None, warehouses[0], batches[0], 1, 50, "2021-01-21"),
 			(item, None, warehouses[0], batches[1], 1, 100, "2021-01-23"),
 		]
 		ses = create_stock_entry_entries_for_batchwise_item_valuation_test(
@@ -568,14 +791,10 @@
 		)
 		sle_details = fetch_sle_details_for_doc_list(ses, columns=columns, as_dict=0)
 		expected_sle_details = [
-			(50.0, 50.0, 1.0, 1.0, '[[1.0, 50.0]]'),
-			(100.0, 150.0, 1.0, 2.0, '[[1.0, 50.0], [1.0, 100.0]]'),
+			(50.0, 50.0, 1.0, 1.0, "[[1.0, 50.0]]"),
+			(100.0, 150.0, 1.0, 2.0, "[[1.0, 50.0], [1.0, 100.0]]"),
 		]
-		details_list.append((
-			sle_details, expected_sle_details,
-			"Material Receipt Entries", columns
-		))
-
+		details_list.append((sle_details, expected_sle_details, "Material Receipt Entries", columns))
 
 		# Test Material Issue Entries
 		se_entry_list_mi = [
@@ -585,14 +804,8 @@
 			se_entry_list_mi, "Material Issue"
 		)
 		sle_details = fetch_sle_details_for_doc_list(ses, columns=columns, as_dict=0)
-		expected_sle_details = [
-			(-50.0, 100.0, -1.0, 1.0, '[[1, 100.0]]')
-		]
-		details_list.append((
-			sle_details, expected_sle_details,
-			"Material Issue Entries", columns
-		))
-
+		expected_sle_details = [(-50.0, 100.0, -1.0, 1.0, "[[1, 100.0]]")]
+		details_list.append((sle_details, expected_sle_details, "Material Issue Entries", columns))
 
 		# Run assertions
 		for details in details_list:
@@ -602,10 +815,8 @@
 		item_code, warehouses, batches = setup_item_valuation_test(use_batchwise_valuation=0)
 		warehouse = warehouses[0]
 
-		state = {
-			"qty": 0.0,
-			"stock_value": 0.0
-		}
+		state = {"qty": 0.0, "stock_value": 0.0}
+
 		def update_invariants(exp_sles):
 			for sle in exp_sles:
 				state["stock_value"] += sle["stock_value_difference"]
@@ -614,59 +825,131 @@
 				sle["qty_after_transaction"] = state["qty"]
 			return exp_sles
 
-		old1 = make_stock_entry(item_code=item_code, target=warehouse, batch_no=batches[0],
-				qty=10, rate=10)
-		self.assertSLEs(old1, update_invariants([
-			{"actual_qty": 10, "stock_value_difference": 10*10, "stock_queue": [[10, 10]]},
-		]))
-		old2 = make_stock_entry(item_code=item_code, target=warehouse, batch_no=batches[1],
-				qty=10, rate=20)
-		self.assertSLEs(old2, update_invariants([
-			{"actual_qty": 10, "stock_value_difference": 10*20, "stock_queue": [[10, 10], [10, 20]]},
-		]))
-		old3 = make_stock_entry(item_code=item_code, target=warehouse, batch_no=batches[0],
-				qty=5, rate=15)
+		old1 = make_stock_entry(
+			item_code=item_code, target=warehouse, batch_no=batches[0], qty=10, rate=10
+		)
+		self.assertSLEs(
+			old1,
+			update_invariants(
+				[
+					{"actual_qty": 10, "stock_value_difference": 10 * 10, "stock_queue": [[10, 10]]},
+				]
+			),
+		)
+		old2 = make_stock_entry(
+			item_code=item_code, target=warehouse, batch_no=batches[1], qty=10, rate=20
+		)
+		self.assertSLEs(
+			old2,
+			update_invariants(
+				[
+					{"actual_qty": 10, "stock_value_difference": 10 * 20, "stock_queue": [[10, 10], [10, 20]]},
+				]
+			),
+		)
+		old3 = make_stock_entry(
+			item_code=item_code, target=warehouse, batch_no=batches[0], qty=5, rate=15
+		)
 
-		self.assertSLEs(old3, update_invariants([
-			{"actual_qty": 5, "stock_value_difference": 5*15, "stock_queue": [[10, 10], [10, 20], [5, 15]]},
-		]))
+		self.assertSLEs(
+			old3,
+			update_invariants(
+				[
+					{
+						"actual_qty": 5,
+						"stock_value_difference": 5 * 15,
+						"stock_queue": [[10, 10], [10, 20], [5, 15]],
+					},
+				]
+			),
+		)
 
 		new1 = make_stock_entry(item_code=item_code, target=warehouse, qty=10, rate=40)
 		batches.append(new1.items[0].batch_no)
 		# assert old queue remains
-		self.assertSLEs(new1, update_invariants([
-			{"actual_qty": 10, "stock_value_difference": 10*40, "stock_queue": [[10, 10], [10, 20], [5, 15]]},
-		]))
+		self.assertSLEs(
+			new1,
+			update_invariants(
+				[
+					{
+						"actual_qty": 10,
+						"stock_value_difference": 10 * 40,
+						"stock_queue": [[10, 10], [10, 20], [5, 15]],
+					},
+				]
+			),
+		)
 
 		new2 = make_stock_entry(item_code=item_code, target=warehouse, qty=10, rate=42)
 		batches.append(new2.items[0].batch_no)
-		self.assertSLEs(new2, update_invariants([
-			{"actual_qty": 10, "stock_value_difference": 10*42, "stock_queue": [[10, 10], [10, 20], [5, 15]]},
-		]))
+		self.assertSLEs(
+			new2,
+			update_invariants(
+				[
+					{
+						"actual_qty": 10,
+						"stock_value_difference": 10 * 42,
+						"stock_queue": [[10, 10], [10, 20], [5, 15]],
+					},
+				]
+			),
+		)
 
 		# consume old batch as per FIFO
-		consume_old1 = make_stock_entry(item_code=item_code, source=warehouse, qty=15, batch_no=batches[0])
-		self.assertSLEs(consume_old1, update_invariants([
-			{"actual_qty": -15, "stock_value_difference": -10*10 - 5*20, "stock_queue": [[5, 20], [5, 15]]},
-		]))
+		consume_old1 = make_stock_entry(
+			item_code=item_code, source=warehouse, qty=15, batch_no=batches[0]
+		)
+		self.assertSLEs(
+			consume_old1,
+			update_invariants(
+				[
+					{
+						"actual_qty": -15,
+						"stock_value_difference": -10 * 10 - 5 * 20,
+						"stock_queue": [[5, 20], [5, 15]],
+					},
+				]
+			),
+		)
 
 		# consume new batch as per batch
-		consume_new2 = make_stock_entry(item_code=item_code, source=warehouse, qty=10, batch_no=batches[-1])
-		self.assertSLEs(consume_new2, update_invariants([
-			{"actual_qty": -10, "stock_value_difference": -10*42, "stock_queue": [[5, 20], [5, 15]]},
-		]))
+		consume_new2 = make_stock_entry(
+			item_code=item_code, source=warehouse, qty=10, batch_no=batches[-1]
+		)
+		self.assertSLEs(
+			consume_new2,
+			update_invariants(
+				[
+					{"actual_qty": -10, "stock_value_difference": -10 * 42, "stock_queue": [[5, 20], [5, 15]]},
+				]
+			),
+		)
 
 		# finish all old batches
-		consume_old2 = make_stock_entry(item_code=item_code, source=warehouse, qty=10, batch_no=batches[1])
-		self.assertSLEs(consume_old2, update_invariants([
-			{"actual_qty": -10, "stock_value_difference": -5*20 - 5*15, "stock_queue": []},
-		]))
+		consume_old2 = make_stock_entry(
+			item_code=item_code, source=warehouse, qty=10, batch_no=batches[1]
+		)
+		self.assertSLEs(
+			consume_old2,
+			update_invariants(
+				[
+					{"actual_qty": -10, "stock_value_difference": -5 * 20 - 5 * 15, "stock_queue": []},
+				]
+			),
+		)
 
 		# finish all new batches
-		consume_new1 = make_stock_entry(item_code=item_code, source=warehouse, qty=10, batch_no=batches[-2])
-		self.assertSLEs(consume_new1, update_invariants([
-			{"actual_qty": -10, "stock_value_difference": -10*40, "stock_queue": []},
-		]))
+		consume_new1 = make_stock_entry(
+			item_code=item_code, source=warehouse, qty=10, batch_no=batches[-2]
+		)
+		self.assertSLEs(
+			consume_new1,
+			update_invariants(
+				[
+					{"actual_qty": -10, "stock_value_difference": -10 * 40, "stock_queue": []},
+				]
+			),
+		)
 
 	def test_fifo_dependent_consumption(self):
 		item = make_item("_TestFifoTransferRates")
@@ -686,12 +969,12 @@
 
 		expected_queues = []
 		for idx, rate in enumerate(rates, start=1):
-			expected_queues.append(
-				{"stock_queue": [[10, 10 * i] for i in range(1, idx + 1)]}
-			)
+			expected_queues.append({"stock_queue": [[10, 10 * i] for i in range(1, idx + 1)]})
 		self.assertSLEs(receipt, expected_queues)
 
-		transfer = make_stock_entry(item_code=item.name, source=source, target=target, qty=10, do_not_save=True, rate=10)
+		transfer = make_stock_entry(
+			item_code=item.name, source=source, target=target, qty=10, do_not_save=True, rate=10
+		)
 		for rate in rates[1:]:
 			row = frappe.copy_doc(transfer.items[0], ignore_no_copy=False)
 			transfer.append("items", row)
@@ -709,7 +992,9 @@
 
 		rates = [10 * i for i in range(1, 5)]
 
-		receipt = make_stock_entry(item_code=rm.name, target=warehouse, qty=10, do_not_save=True, rate=10)
+		receipt = make_stock_entry(
+			item_code=rm.name, target=warehouse, qty=10, do_not_save=True, rate=10
+		)
 		for rate in rates[1:]:
 			row = frappe.copy_doc(receipt.items[0], ignore_no_copy=False)
 			row.basic_rate = rate
@@ -718,26 +1003,322 @@
 		receipt.save()
 		receipt.submit()
 
-		repack = make_stock_entry(item_code=rm.name, source=warehouse, qty=10,
-				do_not_save=True, rate=10, purpose="Repack")
+		repack = make_stock_entry(
+			item_code=rm.name, source=warehouse, qty=10, do_not_save=True, rate=10, purpose="Repack"
+		)
 		for rate in rates[1:]:
 			row = frappe.copy_doc(repack.items[0], ignore_no_copy=False)
 			repack.append("items", row)
 
-		repack.append("items", {
-			"item_code": packed.name,
-			"t_warehouse": warehouse,
-			"qty": 1,
-			"transfer_qty": 1,
-		})
+		repack.append(
+			"items",
+			{
+				"item_code": packed.name,
+				"t_warehouse": warehouse,
+				"qty": 1,
+				"transfer_qty": 1,
+			},
+		)
 
 		repack.save()
 		repack.submit()
 
 		# same exact queue should be transferred
-		self.assertSLEs(repack, [
-			{"incoming_rate": sum(rates) * 10}
-		], sle_filters={"item_code": packed.name})
+		self.assertSLEs(
+			repack, [{"incoming_rate": sum(rates) * 10}], sle_filters={"item_code": packed.name}
+		)
+
+	def test_negative_fifo_valuation(self):
+		"""
+		When stock goes negative discard FIFO queue.
+		Only pervailing valuation rate should be used for making transactions in such cases.
+		"""
+		item = make_item(properties={"allow_negative_stock": 1}).name
+		warehouse = "_Test Warehouse - _TC"
+
+		receipt = make_stock_entry(item_code=item, target=warehouse, qty=10, rate=10)
+		consume1 = make_stock_entry(item_code=item, source=warehouse, qty=15)
+
+		self.assertSLEs(consume1, [{"stock_value": -5 * 10, "stock_queue": [[-5, 10]]}])
+
+		consume2 = make_stock_entry(item_code=item, source=warehouse, qty=5)
+		self.assertSLEs(consume2, [{"stock_value": -10 * 10, "stock_queue": [[-10, 10]]}])
+
+		receipt2 = make_stock_entry(item_code=item, target=warehouse, qty=15, rate=15)
+		self.assertSLEs(receipt2, [{"stock_queue": [[5, 15]], "stock_value_difference": 175}])
+
+	def test_dependent_gl_entry_reposting(self):
+		def _get_stock_credit(doc):
+			return frappe.db.get_value(
+				"GL Entry",
+				{
+					"voucher_no": doc.name,
+					"voucher_type": doc.doctype,
+					"is_cancelled": 0,
+					"account": "Stock In Hand - TCP1",
+				},
+				"sum(credit)",
+			)
+
+		def _day(days):
+			return add_to_date(date=today(), days=days)
+
+		item = make_item().name
+		A = "Stores - TCP1"
+		B = "Work In Progress - TCP1"
+		C = "Finished Goods - TCP1"
+
+		make_stock_entry(item_code=item, to_warehouse=A, qty=5, rate=10, posting_date=_day(0))
+		make_stock_entry(item_code=item, from_warehouse=A, to_warehouse=B, qty=5, posting_date=_day(1))
+		depdendent_consumption = make_stock_entry(
+			item_code=item, from_warehouse=B, qty=5, posting_date=_day(2)
+		)
+		self.assertEqual(50, _get_stock_credit(depdendent_consumption))
+
+		# backdated receipt - should trigger GL repost of all previous stock entries
+		bd_receipt = make_stock_entry(
+			item_code=item, to_warehouse=A, qty=5, rate=20, posting_date=_day(-1)
+		)
+		self.assertEqual(100, _get_stock_credit(depdendent_consumption))
+
+		# cancelling receipt should reset it back
+		bd_receipt.cancel()
+		self.assertEqual(50, _get_stock_credit(depdendent_consumption))
+
+		bd_receipt2 = make_stock_entry(
+			item_code=item, to_warehouse=A, qty=2, rate=20, posting_date=_day(-2)
+		)
+		# total as per FIFO -> 2 * 20 + 3 * 10 = 70
+		self.assertEqual(70, _get_stock_credit(depdendent_consumption))
+
+		# transfer WIP material to final destination and consume it all
+		depdendent_consumption.cancel()
+		make_stock_entry(item_code=item, from_warehouse=B, to_warehouse=C, qty=5, posting_date=_day(3))
+		final_consumption = make_stock_entry(
+			item_code=item, from_warehouse=C, qty=5, posting_date=_day(4)
+		)
+		# exact amount gets consumed
+		self.assertEqual(70, _get_stock_credit(final_consumption))
+
+		# cancel original backdated receipt - should repost A -> B -> C
+		bd_receipt2.cancel()
+		# original amount
+		self.assertEqual(50, _get_stock_credit(final_consumption))
+
+	def test_tie_breaking(self):
+		frappe.flags.dont_execute_stock_reposts = True
+		self.addCleanup(frappe.flags.pop, "dont_execute_stock_reposts")
+
+		item = make_item().name
+		warehouse = "_Test Warehouse - _TC"
+
+		posting_date = "2022-01-01"
+		posting_time = "00:00:01"
+		sle = frappe.qb.DocType("Stock Ledger Entry")
+
+		def ordered_qty_after_transaction():
+			return (
+				frappe.qb.from_(sle)
+				.select("qty_after_transaction")
+				.where((sle.item_code == item) & (sle.warehouse == warehouse) & (sle.is_cancelled == 0))
+				.orderby(CombineDatetime(sle.posting_date, sle.posting_time))
+				.orderby(sle.creation)
+			).run(pluck=True)
+
+		first = make_stock_entry(
+			item_code=item,
+			to_warehouse=warehouse,
+			qty=10,
+			posting_time=posting_time,
+			posting_date=posting_date,
+			do_not_submit=True,
+		)
+		second = make_stock_entry(
+			item_code=item,
+			to_warehouse=warehouse,
+			qty=1,
+			posting_date=posting_date,
+			posting_time=posting_time,
+			do_not_submit=True,
+		)
+
+		first.submit()
+		second.submit()
+
+		self.assertEqual([10, 11], ordered_qty_after_transaction())
+
+		first.cancel()
+		self.assertEqual([1], ordered_qty_after_transaction())
+
+		backdated = make_stock_entry(
+			item_code=item,
+			to_warehouse=warehouse,
+			qty=1,
+			posting_date="2021-01-01",
+			posting_time=posting_time,
+		)
+		self.assertEqual([1, 2], ordered_qty_after_transaction())
+
+		backdated.cancel()
+		self.assertEqual([1], ordered_qty_after_transaction())
+
+	def test_timestamp_clash(self):
+
+		item = make_item().name
+		warehouse = "_Test Warehouse - _TC"
+
+		reciept = make_stock_entry(
+			item_code=item,
+			to_warehouse=warehouse,
+			qty=100,
+			rate=10,
+			posting_date="2021-01-01",
+			posting_time="01:00:00",
+		)
+
+		consumption = make_stock_entry(
+			item_code=item,
+			from_warehouse=warehouse,
+			qty=50,
+			posting_date="2021-01-01",
+			posting_time="02:00:00.1234",  # ms are possible when submitted without editing posting time
+		)
+
+		backdated_receipt = make_stock_entry(
+			item_code=item,
+			to_warehouse=warehouse,
+			qty=100,
+			posting_date="2021-01-01",
+			rate=10,
+			posting_time="02:00:00",  # same posting time as consumption but ms part stripped
+		)
+
+		try:
+			backdated_receipt.cancel()
+		except Exception as e:
+			self.fail("Double processing of qty for clashing timestamp.")
+
+	@change_settings("System Settings", {"float_precision": 3, "currency_precision": 2})
+	def test_transfer_invariants(self):
+		"""Extact stock value should be transferred."""
+
+		item = make_item(
+			properties={
+				"valuation_method": "Moving Average",
+				"stock_uom": "Kg",
+			}
+		).name
+		source_warehouse = "Stores - TCP1"
+		target_warehouse = "Finished Goods - TCP1"
+
+		make_purchase_receipt(
+			item=item,
+			warehouse=source_warehouse,
+			qty=20,
+			conversion_factor=1000,
+			uom="Tonne",
+			rate=156_526.0,
+			company="_Test Company with perpetual inventory",
+		)
+		transfer = make_stock_entry(
+			item=item, from_warehouse=source_warehouse, to_warehouse=target_warehouse, qty=1_728.0
+		)
+
+		filters = {"voucher_no": transfer.name, "voucher_type": transfer.doctype, "is_cancelled": 0}
+		sles = frappe.get_all(
+			"Stock Ledger Entry",
+			fields=["*"],
+			filters=filters,
+			order_by="timestamp(posting_date, posting_time), creation",
+		)
+		self.assertEqual(abs(sles[0].stock_value_difference), sles[1].stock_value_difference)
+
+	@change_settings("System Settings", {"float_precision": 4})
+	def test_negative_qty_with_precision(self):
+		"Test if system precision is respected while validating negative qty."
+		from erpnext.stock.doctype.item.test_item import create_item
+		from erpnext.stock.utils import get_stock_balance
+
+		item_code = "ItemPrecisionTest"
+		warehouse = "_Test Warehouse - _TC"
+		create_item(item_code, is_stock_item=1, stock_uom="Kg")
+
+		create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=559.8327, rate=100)
+
+		make_stock_entry(item_code=item_code, source=warehouse, qty=470.84, rate=100)
+		self.assertEqual(get_stock_balance(item_code, warehouse), 88.9927)
+
+		settings = frappe.get_doc("System Settings")
+		settings.float_precision = 3
+		settings.save()
+
+		# To deliver 100 qty we fall short of 11.0073 qty (11.007 with precision 3)
+		# Stock up with 11.007 (balance in db becomes 99.9997, on UI it will show as 100)
+		make_stock_entry(item_code=item_code, target=warehouse, qty=11.007, rate=100)
+		self.assertEqual(get_stock_balance(item_code, warehouse), 99.9997)
+
+		# See if delivery note goes through
+		# Negative qty error should not be raised as 99.9997 is 100 with precision 3 (system precision)
+		dn = create_delivery_note(
+			item_code=item_code,
+			qty=100,
+			rate=150,
+			warehouse=warehouse,
+			company="_Test Company",
+			expense_account="Cost of Goods Sold - _TC",
+			cost_center="Main - _TC",
+			do_not_submit=True,
+		)
+		dn.submit()
+
+		self.assertEqual(flt(get_stock_balance(item_code, warehouse), 3), 0.000)
+
+	@change_settings("System Settings", {"float_precision": 4})
+	def test_future_negative_qty_with_precision(self):
+		"""
+		Ledger:
+		| Voucher | Qty		| Balance
+		-------------------
+		| Reco	  | 559.8327| 559.8327
+		| SE	  | -470.84	| [Backdated] (new bal: 88.9927)
+		| SE	  | 11.007	| 570.8397 (new bal: 99.9997)
+		| DN	  | -100	| 470.8397 (new bal: -0.0003)
+
+		Check if future negative qty is asserted as per precision 3.
+		-0.0003 should be considered as 0.000
+		"""
+		from erpnext.stock.doctype.item.test_item import create_item
+
+		item_code = "ItemPrecisionTest"
+		warehouse = "_Test Warehouse - _TC"
+		create_item(item_code, is_stock_item=1, stock_uom="Kg")
+
+		create_stock_reconciliation(
+			item_code=item_code,
+			warehouse=warehouse,
+			qty=559.8327,
+			rate=100,
+			posting_date=add_days(today(), -2),
+		)
+		make_stock_entry(item_code=item_code, target=warehouse, qty=11.007, rate=100)
+		create_delivery_note(
+			item_code=item_code,
+			qty=100,
+			rate=150,
+			warehouse=warehouse,
+			company="_Test Company",
+			expense_account="Cost of Goods Sold - _TC",
+			cost_center="Main - _TC",
+		)
+
+		settings = frappe.get_doc("System Settings")
+		settings.float_precision = 3
+		settings.save()
+
+		# Make backdated SE and make sure SE goes through as per precision (no negative qty error)
+		make_stock_entry(
+			item_code=item_code, source=warehouse, qty=470.84, rate=100, posting_date=add_days(today(), -1)
+		)
 
 
 def create_repack_entry(**args):
@@ -747,51 +1328,63 @@
 	repack.company = args.company or "_Test Company"
 	repack.posting_date = args.posting_date
 	repack.set_posting_time = 1
-	repack.append("items", {
-		"item_code": "_Test Item for Reposting",
-		"s_warehouse": "Stores - _TC",
-		"qty": 5,
-		"conversion_factor": 1,
-		"expense_account": "Stock Adjustment - _TC",
-		"cost_center": "Main - _TC"
-	})
+	repack.append(
+		"items",
+		{
+			"item_code": "_Test Item for Reposting",
+			"s_warehouse": "Stores - _TC",
+			"qty": 5,
+			"conversion_factor": 1,
+			"expense_account": "Stock Adjustment - _TC",
+			"cost_center": "Main - _TC",
+		},
+	)
 
-	repack.append("items", {
-		"item_code": "_Test Finished Item for Reposting",
-		"t_warehouse": "Finished Goods - _TC",
-		"qty": 1,
-		"conversion_factor": 1,
-		"expense_account": "Stock Adjustment - _TC",
-		"cost_center": "Main - _TC"
-	})
+	repack.append(
+		"items",
+		{
+			"item_code": "_Test Finished Item for Reposting",
+			"t_warehouse": "Finished Goods - _TC",
+			"qty": 1,
+			"conversion_factor": 1,
+			"expense_account": "Stock Adjustment - _TC",
+			"cost_center": "Main - _TC",
+		},
+	)
 
-	repack.append("additional_costs", {
-		"expense_account": "Freight and Forwarding Charges - _TC",
-		"description": "transport cost",
-		"amount": 40
-	})
+	repack.append(
+		"additional_costs",
+		{
+			"expense_account": "Freight and Forwarding Charges - _TC",
+			"description": "transport cost",
+			"amount": 40,
+		},
+	)
 
 	repack.save()
 	repack.submit()
 
 	return repack
 
+
 def create_product_bundle_item(new_item_code, packed_items):
 	if not frappe.db.exists("Product Bundle", new_item_code):
 		item = frappe.new_doc("Product Bundle")
 		item.new_item_code = new_item_code
 
 		for d in packed_items:
-			item.append("items", {
-				"item_code": d[0],
-				"qty": d[1]
-			})
+			item.append("items", {"item_code": d[0], "qty": d[1]})
 
 		item.save()
 
+
 def create_items():
-	items = ["_Test Item for Reposting", "_Test Finished Item for Reposting",
-		"_Test Subcontracted Item for Reposting", "_Test Bundled Item for Reposting"]
+	items = [
+		"_Test Item for Reposting",
+		"_Test Finished Item for Reposting",
+		"_Test Subcontracted Item for Reposting",
+		"_Test Bundled Item for Reposting",
+	]
 	for d in items:
 		properties = {"valuation_method": "FIFO"}
 		if d == "_Test Bundled Item for Reposting":
@@ -803,7 +1396,10 @@
 
 	return items
 
-def setup_item_valuation_test(valuation_method="FIFO", suffix=None, use_batchwise_valuation=1, batches_list=['X', 'Y']):
+
+def setup_item_valuation_test(
+	valuation_method="FIFO", suffix=None, use_batchwise_valuation=1, batches_list=["X", "Y"]
+):
 	from erpnext.stock.doctype.batch.batch import make_batch
 	from erpnext.stock.doctype.item.test_item import make_item
 	from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
@@ -813,9 +1409,9 @@
 
 	item = make_item(
 		f"IV - Test Item {valuation_method} {suffix}",
-		dict(valuation_method=valuation_method, has_batch_no=1, create_new_batch=1)
+		dict(valuation_method=valuation_method, has_batch_no=1, create_new_batch=1),
 	)
-	warehouses = [create_warehouse(f"IV - Test Warehouse {i}") for i in ['J', 'K']]
+	warehouses = [create_warehouse(f"IV - Test Warehouse {i}") for i in ["J", "K"]]
 	batches = [f"IV - Test Batch {i} {valuation_method} {suffix}" for i in batches_list]
 
 	for i, batch_id in enumerate(batches):
@@ -823,11 +1419,9 @@
 			ubw = use_batchwise_valuation
 			if isinstance(use_batchwise_valuation, (list, tuple)):
 				ubw = use_batchwise_valuation[i]
-			batch = frappe.get_doc(frappe._dict(
-					doctype="Batch",
-					batch_id=batch_id,
-					item=item.item_code,
-					use_batchwise_valuation=ubw
+			batch = frappe.get_doc(
+				frappe._dict(
+					doctype="Batch", batch_id=batch_id, item=item.item_code, use_batchwise_valuation=ubw
 				)
 			).insert()
 			batch.use_batchwise_valuation = ubw
@@ -835,8 +1429,10 @@
 
 	return item.item_code, warehouses, batches
 
+
 def create_purchase_receipt_entries_for_batchwise_item_valuation_test(pr_entry_list):
 	from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+
 	prs = []
 
 	for item, warehouse, batch_no, qty, rate in pr_entry_list:
@@ -845,17 +1441,15 @@
 
 	return prs
 
+
 def create_delivery_note_entries_for_batchwise_item_valuation_test(dn_entry_list):
 	from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note
 	from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
+
 	dns = []
 	for item, warehouse, batch_no, qty, rate in dn_entry_list:
 		so = make_sales_order(
-			rate=rate,
-			qty=qty,
-			item=item,
-			warehouse=warehouse,
-			against_blanket_order=0
+			rate=rate, qty=qty, item=item, warehouse=warehouse, against_blanket_order=0
 		)
 
 		dn = make_delivery_note(so.name)
@@ -865,20 +1459,25 @@
 		dns.append(dn)
 	return dns
 
+
 def fetch_sle_details_for_doc_list(doc_list, columns, as_dict=1):
-	return frappe.db.sql(f"""
+	return frappe.db.sql(
+		f"""
 		SELECT { ', '.join(columns)}
 		FROM `tabStock Ledger Entry`
 		WHERE
 			voucher_no IN %(voucher_nos)s
 			and docstatus = 1
 		ORDER BY timestamp(posting_date, posting_time) ASC, CREATION ASC
-	""", dict(
-		voucher_nos=[doc.name for doc in doc_list]
-	), as_dict=as_dict)
+	""",
+		dict(voucher_nos=[doc.name for doc in doc_list]),
+		as_dict=as_dict,
+	)
+
 
 def get_stock_value_from_q(q):
-	return sum(r*q for r,q in json.loads(q))
+	return sum(r * q for r, q in json.loads(q))
+
 
 def create_stock_entry_entries_for_batchwise_item_valuation_test(se_entry_list, purpose):
 	ses = []
@@ -889,23 +1488,17 @@
 			company="_Test Company",
 			batch_no=batch,
 			posting_date=posting_date,
-			purpose=purpose
+			purpose=purpose,
 		)
 
 		if purpose == "Material Receipt":
-			args.update(
-				dict(to_warehouse=target, rate=rate)
-			)
+			args.update(dict(to_warehouse=target, rate=rate))
 
 		elif purpose == "Material Issue":
-			args.update(
-				dict(from_warehouse=source)
-			)
+			args.update(dict(from_warehouse=source))
 
 		elif purpose == "Material Transfer":
-			args.update(
-				dict(from_warehouse=source, to_warehouse=target)
-			)
+			args.update(dict(from_warehouse=source, to_warehouse=target))
 
 		else:
 			raise ValueError(f"Invalid purpose: {purpose}")
@@ -913,7 +1506,86 @@
 
 	return ses
 
+
 def get_unique_suffix():
 	# Used to isolate valuation sensitive
 	# tests to prevent future tests from failing.
 	return str(uuid4())[:8].upper()
+
+
+class TestDeferredNaming(FrappeTestCase):
+	@classmethod
+	def setUpClass(cls) -> None:
+		super().setUpClass()
+		cls.gle_autoname = frappe.get_meta("GL Entry").autoname
+		cls.sle_autoname = frappe.get_meta("Stock Ledger Entry").autoname
+
+	def setUp(self) -> None:
+		self.item = make_item().name
+		self.warehouse = "Stores - TCP1"
+		self.company = "_Test Company with perpetual inventory"
+
+	def tearDown(self) -> None:
+		make_property_setter(
+			doctype="GL Entry",
+			for_doctype=True,
+			property="autoname",
+			value=self.gle_autoname,
+			property_type="Data",
+			fieldname=None,
+		)
+		make_property_setter(
+			doctype="Stock Ledger Entry",
+			for_doctype=True,
+			property="autoname",
+			value=self.sle_autoname,
+			property_type="Data",
+			fieldname=None,
+		)
+
+		# since deferred naming autocommits, commit all changes to avoid flake
+		frappe.db.commit()  # nosemgrep
+
+	@staticmethod
+	def get_gle_sles(se):
+		filters = {"voucher_type": se.doctype, "voucher_no": se.name}
+		gle = set(frappe.get_list("GL Entry", filters, pluck="name"))
+		sle = set(frappe.get_list("Stock Ledger Entry", filters, pluck="name"))
+		return gle, sle
+
+	def test_deferred_naming(self):
+		se = make_stock_entry(
+			item_code=self.item, to_warehouse=self.warehouse, qty=10, rate=100, company=self.company
+		)
+
+		gle, sle = self.get_gle_sles(se)
+		rename_gle_sle_docs()
+		renamed_gle, renamed_sle = self.get_gle_sles(se)
+
+		self.assertFalse(gle & renamed_gle, msg="GLEs not renamed")
+		self.assertFalse(sle & renamed_sle, msg="SLEs not renamed")
+		se.cancel()
+
+	def test_hash_naming(self):
+		# disable naming series
+		for doctype in ("GL Entry", "Stock Ledger Entry"):
+			make_property_setter(
+				doctype=doctype,
+				for_doctype=True,
+				property="autoname",
+				value="hash",
+				property_type="Data",
+				fieldname=None,
+			)
+
+		se = make_stock_entry(
+			item_code=self.item, to_warehouse=self.warehouse, qty=10, rate=100, company=self.company
+		)
+
+		gle, sle = self.get_gle_sles(se)
+		rename_gle_sle_docs()
+		renamed_gle, renamed_sle = self.get_gle_sles(se)
+
+		self.assertEqual(gle, renamed_gle, msg="GLEs are renamed while using hash naming")
+		self.assertEqual(sle, renamed_sle, msg="SLEs are renamed while using hash naming")
+		se.cancel()
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
index 84f65a0..05dd105 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
@@ -55,6 +55,25 @@
 		}
 	},
 
+	scan_barcode: function(frm) {
+		const barcode_scanner = new erpnext.utils.BarcodeScanner({frm:frm});
+		barcode_scanner.process_scan();
+	},
+
+	scan_mode: function(frm) {
+		if (frm.doc.scan_mode) {
+			frappe.show_alert({
+				message: __("Scan mode enabled, existing quantity will not be fetched."),
+				indicator: "green"
+			});
+		}
+	},
+
+	set_warehouse: function(frm) {
+		let transaction_controller = new erpnext.TransactionController({frm:frm});
+		transaction_controller.autofill_warehouse(frm.doc.items, "warehouse", frm.doc.set_warehouse);
+	},
+
 	get_items: function(frm) {
 		let fields = [
 			{
@@ -148,35 +167,25 @@
 					batch_no: d.batch_no
 				},
 				callback: function(r) {
-					frappe.model.set_value(cdt, cdn, "qty", r.message.qty);
+					const row = frappe.model.get_doc(cdt, cdn);
+					if (!frm.doc.scan_mode) {
+						frappe.model.set_value(cdt, cdn, "qty", r.message.qty);
+					}
 					frappe.model.set_value(cdt, cdn, "valuation_rate", r.message.rate);
 					frappe.model.set_value(cdt, cdn, "current_qty", r.message.qty);
 					frappe.model.set_value(cdt, cdn, "current_valuation_rate", r.message.rate);
 					frappe.model.set_value(cdt, cdn, "current_amount", r.message.rate * r.message.qty);
-					frappe.model.set_value(cdt, cdn, "amount", r.message.rate * r.message.qty);
+					frappe.model.set_value(cdt, cdn, "amount", row.qty * row.valuation_rate);
 					frappe.model.set_value(cdt, cdn, "current_serial_no", r.message.serial_nos);
 
-					if (frm.doc.purpose == "Stock Reconciliation") {
+					if (frm.doc.purpose == "Stock Reconciliation" && !frm.doc.scan_mode) {
 						frappe.model.set_value(cdt, cdn, "serial_no", r.message.serial_nos);
 					}
 				}
 			});
 		}
 	},
-	set_item_code: function(doc, cdt, cdn) {
-		var d = frappe.model.get_doc(cdt, cdn);
-		if (d.barcode) {
-			frappe.call({
-				method: "erpnext.stock.get_item_details.get_item_code",
-				args: {"barcode": d.barcode },
-				callback: function(r) {
-					if (!r.exe){
-						frappe.model.set_value(cdt, cdn, "item_code", r.message);
-					}
-				}
-			});
-		}
-	},
+
 	set_amount_quantity: function(doc, cdt, cdn) {
 		var d = frappe.model.get_doc(cdt, cdn);
 		if (d.qty & d.valuation_rate) {
@@ -214,13 +223,10 @@
 });
 
 frappe.ui.form.on("Stock Reconciliation Item", {
-	barcode: function(frm, cdt, cdn) {
-		frm.events.set_item_code(frm, cdt, cdn);
-	},
 
 	warehouse: function(frm, cdt, cdn) {
 		var child = locals[cdt][cdn];
-		if (child.batch_no) {
+		if (child.batch_no && !frm.doc.scan_mode) {
 			frappe.model.set_value(child.cdt, child.cdn, "batch_no", "");
 		}
 
@@ -229,7 +235,7 @@
 
 	item_code: function(frm, cdt, cdn) {
 		var child = locals[cdt][cdn];
-		if (child.batch_no) {
+		if (child.batch_no && !frm.doc.scan_mode) {
 			frappe.model.set_value(cdt, cdn, "batch_no", "");
 		}
 
@@ -255,7 +261,14 @@
 			const serial_nos = child.serial_no.trim().split('\n');
 			frappe.model.set_value(cdt, cdn, "qty", serial_nos.length);
 		}
-	}
+	},
+
+	items_add: function(frm, cdt, cdn) {
+		var item = frappe.get_doc(cdt, cdn);
+		if (!item.warehouse && frm.doc.set_warehouse) {
+			frappe.model.set_value(cdt, cdn, "warehouse", frm.doc.set_warehouse);
+		}
+	},
 
 });
 
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json
index a882a61..9a85431 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json
@@ -14,6 +14,12 @@
   "posting_date",
   "posting_time",
   "set_posting_time",
+  "section_break_8",
+  "set_warehouse",
+  "section_break_22",
+  "scan_barcode",
+  "column_break_12",
+  "scan_mode",
   "sb9",
   "items",
   "section_break_9",
@@ -139,13 +145,45 @@
   {
    "fieldname": "dimension_col_break",
    "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "section_break_8",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "scan_barcode",
+   "fieldtype": "Data",
+   "label": "Scan Barcode",
+   "options": "Barcode"
+  },
+  {
+   "default": "0",
+   "description": "Disables auto-fetching of existing quantity",
+   "fieldname": "scan_mode",
+   "fieldtype": "Check",
+   "label": "Scan Mode"
+  },
+  {
+   "fieldname": "set_warehouse",
+   "fieldtype": "Link",
+   "label": "Default Warehouse",
+   "options": "Warehouse"
+  },
+  {
+   "depends_on": "eval:!doc.docstatus",
+   "fieldname": "section_break_22",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "column_break_12",
+   "fieldtype": "Column Break"
   }
  ],
  "icon": "fa fa-upload-alt",
  "idx": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-02-06 14:28:19.043905",
+ "modified": "2022-05-11 09:10:26.327652",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Reconciliation",
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 82a8c37..23e0f1e 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -1,6 +1,7 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 
+from typing import Optional
 
 import frappe
 from frappe import _, msgprint
@@ -14,8 +15,13 @@
 from erpnext.stock.utils import get_stock_balance
 
 
-class OpeningEntryAccountError(frappe.ValidationError): pass
-class EmptyStockReconciliationItemsError(frappe.ValidationError): pass
+class OpeningEntryAccountError(frappe.ValidationError):
+	pass
+
+
+class EmptyStockReconciliationItemsError(frappe.ValidationError):
+	pass
+
 
 class StockReconciliation(StockController):
 	def __init__(self, *args, **kwargs):
@@ -24,9 +30,11 @@
 
 	def validate(self):
 		if not self.expense_account:
-			self.expense_account = frappe.get_cached_value('Company',  self.company,  "stock_adjustment_account")
+			self.expense_account = frappe.get_cached_value(
+				"Company", self.company, "stock_adjustment_account"
+			)
 		if not self.cost_center:
-			self.cost_center = frappe.get_cached_value('Company',  self.company,  "cost_center")
+			self.cost_center = frappe.get_cached_value("Company", self.company, "cost_center")
 		self.validate_posting_time()
 		self.remove_items_with_no_change()
 		self.validate_data()
@@ -37,8 +45,8 @@
 		self.set_total_qty_and_amount()
 		self.validate_putaway_capacity()
 
-		if self._action=="submit":
-			self.make_batches('warehouse')
+		if self._action == "submit":
+			self.make_batches("warehouse")
 
 	def on_submit(self):
 		self.update_stock_ledger()
@@ -46,24 +54,30 @@
 		self.repost_future_sle_and_gle()
 
 		from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
+
 		update_serial_nos_after_submit(self, "items")
 
 	def on_cancel(self):
-		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
+		self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Repost Item Valuation")
 		self.make_sle_on_cancel()
 		self.make_gl_entries_on_cancel()
 		self.repost_future_sle_and_gle()
+		self.delete_auto_created_batches()
 
 	def remove_items_with_no_change(self):
 		"""Remove items if qty or rate is not changed"""
 		self.difference_amount = 0.0
-		def _changed(item):
-			item_dict = get_stock_balance_for(item.item_code, item.warehouse,
-				self.posting_date, self.posting_time, batch_no=item.batch_no)
 
-			if ((item.qty is None or item.qty==item_dict.get("qty")) and
-				(item.valuation_rate is None or item.valuation_rate==item_dict.get("rate")) and
-				(not item.serial_no or (item.serial_no == item_dict.get("serial_nos")) )):
+		def _changed(item):
+			item_dict = get_stock_balance_for(
+				item.item_code, item.warehouse, self.posting_date, self.posting_time, batch_no=item.batch_no
+			)
+
+			if (
+				(item.qty is None or item.qty == item_dict.get("qty"))
+				and (item.valuation_rate is None or item.valuation_rate == item_dict.get("rate"))
+				and (not item.serial_no or (item.serial_no == item_dict.get("serial_nos")))
+			):
 				return False
 			else:
 				# set default as current rates
@@ -80,16 +94,20 @@
 
 				item.current_qty = item_dict.get("qty")
 				item.current_valuation_rate = item_dict.get("rate")
-				self.difference_amount += (flt(item.qty, item.precision("qty")) * \
-					flt(item.valuation_rate or item_dict.get("rate"), item.precision("valuation_rate")) \
-					- flt(item_dict.get("qty"), item.precision("qty")) * flt(item_dict.get("rate"), item.precision("valuation_rate")))
+				self.difference_amount += flt(item.qty, item.precision("qty")) * flt(
+					item.valuation_rate or item_dict.get("rate"), item.precision("valuation_rate")
+				) - flt(item_dict.get("qty"), item.precision("qty")) * flt(
+					item_dict.get("rate"), item.precision("valuation_rate")
+				)
 				return True
 
 		items = list(filter(lambda d: _changed(d), self.items))
 
 		if not items:
-			frappe.throw(_("None of the items have any change in quantity or value."),
-				EmptyStockReconciliationItemsError)
+			frappe.throw(
+				_("None of the items have any change in quantity or value."),
+				EmptyStockReconciliationItemsError,
+			)
 
 		elif len(items) != len(self.items):
 			self.items = items
@@ -99,7 +117,7 @@
 
 	def validate_data(self):
 		def _get_msg(row_num, msg):
-			return _("Row # {0}:").format(row_num+1) + " " + msg
+			return _("Row # {0}:").format(row_num + 1) + " " + msg
 
 		self.validation_messages = []
 		item_warehouse_combinations = []
@@ -109,7 +127,7 @@
 		for row_num, row in enumerate(self.items):
 			# find duplicates
 			key = [row.item_code, row.warehouse]
-			for field in ['serial_no', 'batch_no']:
+			for field in ["serial_no", "batch_no"]:
 				if row.get(field):
 					key.append(row.get(field))
 
@@ -126,32 +144,35 @@
 
 			# if both not specified
 			if row.qty in ["", None] and row.valuation_rate in ["", None]:
-				self.validation_messages.append(_get_msg(row_num,
-					_("Please specify either Quantity or Valuation Rate or both")))
+				self.validation_messages.append(
+					_get_msg(row_num, _("Please specify either Quantity or Valuation Rate or both"))
+				)
 
 			# do not allow negative quantity
 			if flt(row.qty) < 0:
-				self.validation_messages.append(_get_msg(row_num,
-					_("Negative Quantity is not allowed")))
+				self.validation_messages.append(_get_msg(row_num, _("Negative Quantity is not allowed")))
 
 			# do not allow negative valuation
 			if flt(row.valuation_rate) < 0:
-				self.validation_messages.append(_get_msg(row_num,
-					_("Negative Valuation Rate is not allowed")))
+				self.validation_messages.append(_get_msg(row_num, _("Negative Valuation Rate is not allowed")))
 
 			if row.qty and row.valuation_rate in ["", None]:
-				row.valuation_rate = get_stock_balance(row.item_code, row.warehouse,
-							self.posting_date, self.posting_time, with_valuation_rate=True)[1]
+				row.valuation_rate = get_stock_balance(
+					row.item_code, row.warehouse, self.posting_date, self.posting_time, with_valuation_rate=True
+				)[1]
 				if not row.valuation_rate:
 					# try if there is a buying price list in default currency
-					buying_rate = frappe.db.get_value("Item Price", {"item_code": row.item_code,
-						"buying": 1, "currency": default_currency}, "price_list_rate")
+					buying_rate = frappe.db.get_value(
+						"Item Price",
+						{"item_code": row.item_code, "buying": 1, "currency": default_currency},
+						"price_list_rate",
+					)
 					if buying_rate:
 						row.valuation_rate = buying_rate
 
 					else:
 						# get valuation rate from Item
-						row.valuation_rate = frappe.get_value('Item', row.item_code, 'valuation_rate')
+						row.valuation_rate = frappe.get_value("Item", row.item_code, "valuation_rate")
 
 		# throw all validation messages
 		if self.validation_messages:
@@ -178,7 +199,9 @@
 
 			# item should not be serialized
 			if item.has_serial_no and not row.serial_no and not item.serial_no_series:
-				raise frappe.ValidationError(_("Serial no(s) required for serialized item {0}").format(item_code))
+				raise frappe.ValidationError(
+					_("Serial no(s) required for serialized item {0}").format(item_code)
+				)
 
 			# item managed batch-wise not allowed
 			if item.has_batch_no and not row.batch_no and not item.create_new_batch:
@@ -191,8 +214,8 @@
 			self.validation_messages.append(_("Row #") + " " + ("%d: " % (row.idx)) + cstr(e))
 
 	def update_stock_ledger(self):
-		"""	find difference between current and expected entries
-			and create stock ledger entries based on the difference"""
+		"""find difference between current and expected entries
+		and create stock ledger entries based on the difference"""
 		from erpnext.stock.stock_ledger import get_previous_sle
 
 		sl_entries = []
@@ -208,15 +231,20 @@
 				self.get_sle_for_serialized_items(row, sl_entries)
 			else:
 				if row.serial_no or row.batch_no:
-					frappe.throw(_("Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it.") \
-						.format(row.idx, frappe.bold(row.item_code)))
+					frappe.throw(
+						_(
+							"Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it."
+						).format(row.idx, frappe.bold(row.item_code))
+					)
 
-				previous_sle = get_previous_sle({
-					"item_code": row.item_code,
-					"warehouse": row.warehouse,
-					"posting_date": self.posting_date,
-					"posting_time": self.posting_time
-				})
+				previous_sle = get_previous_sle(
+					{
+						"item_code": row.item_code,
+						"warehouse": row.warehouse,
+						"posting_date": self.posting_date,
+						"posting_time": self.posting_time,
+					}
+				)
 
 				if previous_sle:
 					if row.qty in ("", None):
@@ -226,12 +254,16 @@
 						row.valuation_rate = previous_sle.get("valuation_rate", 0)
 
 				if row.qty and not row.valuation_rate and not row.allow_zero_valuation_rate:
-					frappe.throw(_("Valuation Rate required for Item {0} at row {1}").format(row.item_code, row.idx))
+					frappe.throw(
+						_("Valuation Rate required for Item {0} at row {1}").format(row.item_code, row.idx)
+					)
 
-				if ((previous_sle and row.qty == previous_sle.get("qty_after_transaction")
-					and (row.valuation_rate == previous_sle.get("valuation_rate") or row.qty == 0))
-					or (not previous_sle and not row.qty)):
-						continue
+				if (
+					previous_sle
+					and row.qty == previous_sle.get("qty_after_transaction")
+					and (row.valuation_rate == previous_sle.get("valuation_rate") or row.qty == 0)
+				) or (not previous_sle and not row.qty):
+					continue
 
 				sl_entries.append(self.get_sle_for_items(row))
 
@@ -253,21 +285,24 @@
 
 		serial_nos = get_serial_nos(row.serial_no)
 
-
 		# To issue existing serial nos
 		if row.current_qty and (row.current_serial_no or row.batch_no):
 			args = self.get_sle_for_items(row)
-			args.update({
-				'actual_qty': -1 * row.current_qty,
-				'serial_no': row.current_serial_no,
-				'batch_no': row.batch_no,
-				'valuation_rate': row.current_valuation_rate
-			})
+			args.update(
+				{
+					"actual_qty": -1 * row.current_qty,
+					"serial_no": row.current_serial_no,
+					"batch_no": row.batch_no,
+					"valuation_rate": row.current_valuation_rate,
+				}
+			)
 
 			if row.current_serial_no:
-				args.update({
-					'qty_after_transaction': 0,
-				})
+				args.update(
+					{
+						"qty_after_transaction": 0,
+					}
+				)
 
 			sl_entries.append(args)
 
@@ -275,42 +310,49 @@
 		for serial_no in serial_nos:
 			args = self.get_sle_for_items(row, [serial_no])
 
-			previous_sle = get_previous_sle({
-				"item_code": row.item_code,
-				"posting_date": self.posting_date,
-				"posting_time": self.posting_time,
-				"serial_no": serial_no
-			})
+			previous_sle = get_previous_sle(
+				{
+					"item_code": row.item_code,
+					"posting_date": self.posting_date,
+					"posting_time": self.posting_time,
+					"serial_no": serial_no,
+				}
+			)
 
 			if previous_sle and row.warehouse != previous_sle.get("warehouse"):
 				# If serial no exists in different warehouse
 
-				warehouse = previous_sle.get("warehouse", '') or row.warehouse
+				warehouse = previous_sle.get("warehouse", "") or row.warehouse
 
 				if not qty_after_transaction:
-					qty_after_transaction = get_stock_balance(row.item_code,
-						warehouse, self.posting_date, self.posting_time)
+					qty_after_transaction = get_stock_balance(
+						row.item_code, warehouse, self.posting_date, self.posting_time
+					)
 
 				qty_after_transaction -= 1
 
 				new_args = args.copy()
-				new_args.update({
-					'actual_qty': -1,
-					'qty_after_transaction': qty_after_transaction,
-					'warehouse': warehouse,
-					'valuation_rate': previous_sle.get("valuation_rate")
-				})
+				new_args.update(
+					{
+						"actual_qty": -1,
+						"qty_after_transaction": qty_after_transaction,
+						"warehouse": warehouse,
+						"valuation_rate": previous_sle.get("valuation_rate"),
+					}
+				)
 
 				sl_entries.append(new_args)
 
 		if row.qty:
 			args = self.get_sle_for_items(row)
 
-			args.update({
-				'actual_qty': row.qty,
-				'incoming_rate': row.valuation_rate,
-				'valuation_rate': row.valuation_rate
-			})
+			args.update(
+				{
+					"actual_qty": row.qty,
+					"incoming_rate": row.valuation_rate,
+					"valuation_rate": row.valuation_rate,
+				}
+			)
 
 			sl_entries.append(args)
 
@@ -320,7 +362,8 @@
 
 	def update_valuation_rate_for_serial_no(self):
 		for d in self.items:
-			if not d.serial_no: continue
+			if not d.serial_no:
+				continue
 
 			serial_nos = get_serial_nos(d.serial_no)
 			self.update_valuation_rate_for_serial_nos(d, serial_nos)
@@ -331,7 +374,7 @@
 			return
 
 		for d in serial_nos:
-			frappe.db.set_value("Serial No", d, 'purchase_rate', valuation_rate)
+			frappe.db.set_value("Serial No", d, "purchase_rate", valuation_rate)
 
 	def get_sle_for_items(self, row, serial_nos=None):
 		"""Insert Stock Ledger Entries"""
@@ -339,22 +382,24 @@
 		if not serial_nos and row.serial_no:
 			serial_nos = get_serial_nos(row.serial_no)
 
-		data = frappe._dict({
-			"doctype": "Stock Ledger Entry",
-			"item_code": row.item_code,
-			"warehouse": row.warehouse,
-			"posting_date": self.posting_date,
-			"posting_time": self.posting_time,
-			"voucher_type": self.doctype,
-			"voucher_no": self.name,
-			"voucher_detail_no": row.name,
-			"company": self.company,
-			"stock_uom": frappe.db.get_value("Item", row.item_code, "stock_uom"),
-			"is_cancelled": 1 if self.docstatus == 2 else 0,
-			"serial_no": '\n'.join(serial_nos) if serial_nos else '',
-			"batch_no": row.batch_no,
-			"valuation_rate": flt(row.valuation_rate, row.precision("valuation_rate"))
-		})
+		data = frappe._dict(
+			{
+				"doctype": "Stock Ledger Entry",
+				"item_code": row.item_code,
+				"warehouse": row.warehouse,
+				"posting_date": self.posting_date,
+				"posting_time": self.posting_time,
+				"voucher_type": self.doctype,
+				"voucher_no": self.name,
+				"voucher_detail_no": row.name,
+				"company": self.company,
+				"stock_uom": frappe.db.get_value("Item", row.item_code, "stock_uom"),
+				"is_cancelled": 1 if self.docstatus == 2 else 0,
+				"serial_no": "\n".join(serial_nos) if serial_nos else "",
+				"batch_no": row.batch_no,
+				"valuation_rate": flt(row.valuation_rate, row.precision("valuation_rate")),
+			}
+		)
 
 		if not row.batch_no:
 			data.qty_after_transaction = flt(row.qty, row.precision("qty"))
@@ -382,7 +427,7 @@
 		for row in self.items:
 			if row.serial_no or row.batch_no or row.current_serial_no:
 				has_serial_no = True
-				serial_nos = ''
+				serial_nos = ""
 				if row.current_serial_no:
 					serial_nos = get_serial_nos(row.current_serial_no)
 
@@ -395,10 +440,11 @@
 				sl_entries = self.merge_similar_item_serial_nos(sl_entries)
 
 			sl_entries.reverse()
-			allow_negative_stock = cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock"))
+			allow_negative_stock = cint(
+				frappe.db.get_single_value("Stock Settings", "allow_negative_stock")
+			)
 			self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock)
 
-
 	def merge_similar_item_serial_nos(self, sl_entries):
 		# If user has put the same item in multiple row with different serial no
 		new_sl_entries = []
@@ -411,16 +457,16 @@
 
 			key = (d.item_code, d.warehouse)
 			if key not in merge_similar_entries:
-				d.total_amount = (d.actual_qty * d.valuation_rate)
+				d.total_amount = flt(d.actual_qty) * d.valuation_rate
 				merge_similar_entries[key] = d
 			elif d.serial_no:
 				data = merge_similar_entries[key]
 				data.actual_qty += d.actual_qty
 				data.qty_after_transaction += d.qty_after_transaction
 
-				data.total_amount += (d.actual_qty * d.valuation_rate)
+				data.total_amount += d.actual_qty * d.valuation_rate
 				data.valuation_rate = (data.total_amount) / data.actual_qty
-				data.serial_no += '\n' + d.serial_no
+				data.serial_no += "\n" + d.serial_no
 
 				data.incoming_rate = (data.total_amount) / data.actual_qty
 
@@ -433,8 +479,9 @@
 		if not self.cost_center:
 			msgprint(_("Please enter Cost Center"), raise_exception=1)
 
-		return super(StockReconciliation, self).get_gl_entries(warehouse_account,
-			self.expense_account, self.cost_center)
+		return super(StockReconciliation, self).get_gl_entries(
+			warehouse_account, self.expense_account, self.cost_center
+		)
 
 	def validate_expense_account(self):
 		if not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
@@ -442,29 +489,39 @@
 
 		if not self.expense_account:
 			frappe.throw(_("Please enter Expense Account"))
-		elif self.purpose == "Opening Stock" or not frappe.db.sql("""select name from `tabStock Ledger Entry` limit 1"""):
+		elif self.purpose == "Opening Stock" or not frappe.db.sql(
+			"""select name from `tabStock Ledger Entry` limit 1"""
+		):
 			if frappe.db.get_value("Account", self.expense_account, "report_type") == "Profit and Loss":
-				frappe.throw(_("Difference Account must be a Asset/Liability type account, since this Stock Reconciliation is an Opening Entry"), OpeningEntryAccountError)
+				frappe.throw(
+					_(
+						"Difference Account must be a Asset/Liability type account, since this Stock Reconciliation is an Opening Entry"
+					),
+					OpeningEntryAccountError,
+				)
 
 	def set_zero_value_for_customer_provided_items(self):
 		changed_any_values = False
 
-		for d in self.get('items'):
-			is_customer_item = frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item')
+		for d in self.get("items"):
+			is_customer_item = frappe.db.get_value("Item", d.item_code, "is_customer_provided_item")
 			if is_customer_item and d.valuation_rate:
 				d.valuation_rate = 0.0
 				changed_any_values = True
 
 		if changed_any_values:
-			msgprint(_("Valuation rate for customer provided items has been set to zero."),
-				title=_("Note"), indicator="blue")
-
+			msgprint(
+				_("Valuation rate for customer provided items has been set to zero."),
+				title=_("Note"),
+				indicator="blue",
+			)
 
 	def set_total_qty_and_amount(self):
 		for d in self.get("items"):
 			d.amount = flt(d.qty, d.precision("qty")) * flt(d.valuation_rate, d.precision("valuation_rate"))
-			d.current_amount = (flt(d.current_qty,
-				d.precision("current_qty")) * flt(d.current_valuation_rate, d.precision("current_valuation_rate")))
+			d.current_amount = flt(d.current_qty, d.precision("current_qty")) * flt(
+				d.current_valuation_rate, d.precision("current_valuation_rate")
+			)
 
 			d.quantity_difference = flt(d.qty) - flt(d.current_qty)
 			d.amount_difference = flt(d.amount) - flt(d.current_amount)
@@ -476,25 +533,33 @@
 
 	def submit(self):
 		if len(self.items) > 100:
-			msgprint(_("The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Draft stage"))
-			self.queue_action('submit', timeout=4600)
+			msgprint(
+				_(
+					"The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Draft stage"
+				)
+			)
+			self.queue_action("submit", timeout=4600)
 		else:
 			self._submit()
 
 	def cancel(self):
 		if len(self.items) > 100:
-			msgprint(_("The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Submitted stage"))
-			self.queue_action('cancel', timeout=2000)
+			msgprint(
+				_(
+					"The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Submitted stage"
+				)
+			)
+			self.queue_action("cancel", timeout=2000)
 		else:
 			self._cancel()
 
+
 @frappe.whitelist()
-def get_items(warehouse, posting_date, posting_time, company, item_code=None, ignore_empty_stock=False):
+def get_items(
+	warehouse, posting_date, posting_time, company, item_code=None, ignore_empty_stock=False
+):
 	ignore_empty_stock = cint(ignore_empty_stock)
-	items = [frappe._dict({
-		'item_code': item_code,
-		'warehouse': warehouse
-	})]
+	items = [frappe._dict({"item_code": item_code, "warehouse": warehouse})]
 
 	if not item_code:
 		items = get_items_for_stock_reco(warehouse, company)
@@ -504,8 +569,9 @@
 
 	for d in items:
 		if d.item_code in itemwise_batch_data:
-			valuation_rate = get_stock_balance(d.item_code, d.warehouse,
-				posting_date, posting_time, with_valuation_rate=True)[1]
+			valuation_rate = get_stock_balance(
+				d.item_code, d.warehouse, posting_date, posting_time, with_valuation_rate=True
+			)[1]
 
 			for row in itemwise_batch_data.get(d.item_code):
 				if ignore_empty_stock and not row.qty:
@@ -514,12 +580,22 @@
 				args = get_item_data(row, row.qty, valuation_rate)
 				res.append(args)
 		else:
-			stock_bal = get_stock_balance(d.item_code, d.warehouse, posting_date, posting_time,
-				with_valuation_rate=True , with_serial_no=cint(d.has_serial_no))
-			qty, valuation_rate, serial_no = stock_bal[0], stock_bal[1], stock_bal[2] if cint(d.has_serial_no) else ''
+			stock_bal = get_stock_balance(
+				d.item_code,
+				d.warehouse,
+				posting_date,
+				posting_time,
+				with_valuation_rate=True,
+				with_serial_no=cint(d.has_serial_no),
+			)
+			qty, valuation_rate, serial_no = (
+				stock_bal[0],
+				stock_bal[1],
+				stock_bal[2] if cint(d.has_serial_no) else "",
+			)
 
 			if ignore_empty_stock and not stock_bal[0]:
-					continue
+				continue
 
 			args = get_item_data(d, qty, valuation_rate, serial_no)
 
@@ -527,13 +603,15 @@
 
 	return res
 
+
 def get_items_for_stock_reco(warehouse, company):
 	lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"])
-	items = frappe.db.sql(f"""
+	items = frappe.db.sql(
+		f"""
 		select
 			i.name as item_code, i.item_name, bin.warehouse as warehouse, i.has_serial_no, i.has_batch_no
 		from
-			tabBin bin, tabItem i
+			`tabBin` bin, `tabItem` i
 		where
 			i.name = bin.item_code
 			and IFNULL(i.disabled, 0) = 0
@@ -542,13 +620,16 @@
 			and exists(
 				select name from `tabWarehouse` where lft >= {lft} and rgt <= {rgt} and name = bin.warehouse
 			)
-	""", as_dict=1)
+	""",
+		as_dict=1,
+	)
 
-	items += frappe.db.sql("""
+	items += frappe.db.sql(
+		"""
 		select
 			i.name as item_code, i.item_name, id.default_warehouse as warehouse, i.has_serial_no, i.has_batch_no
 		from
-			tabItem i, `tabItem Default` id
+			`tabItem` i, `tabItem Default` id
 		where
 			i.name = id.parent
 			and exists(
@@ -559,40 +640,50 @@
 			and IFNULL(i.disabled, 0) = 0
 			and id.company = %s
 		group by i.name
-	""", (lft, rgt, company), as_dict=1)
+	""",
+		(lft, rgt, company),
+		as_dict=1,
+	)
 
 	# remove duplicates
 	# check if item-warehouse key extracted from each entry exists in set iw_keys
 	# and update iw_keys
 	iw_keys = set()
-	items = [item for item in items if [(item.item_code, item.warehouse) not in iw_keys, iw_keys.add((item.item_code, item.warehouse))][0]]
+	items = [
+		item
+		for item in items
+		if [
+			(item.item_code, item.warehouse) not in iw_keys,
+			iw_keys.add((item.item_code, item.warehouse)),
+		][0]
+	]
 
 	return items
 
+
 def get_item_data(row, qty, valuation_rate, serial_no=None):
 	return {
-		'item_code': row.item_code,
-		'warehouse': row.warehouse,
-		'qty': qty,
-		'item_name': row.item_name,
-		'valuation_rate': valuation_rate,
-		'current_qty': qty,
-		'current_valuation_rate': valuation_rate,
-		'current_serial_no': serial_no,
-		'serial_no': serial_no,
-		'batch_no': row.get('batch_no')
+		"item_code": row.item_code,
+		"warehouse": row.warehouse,
+		"qty": qty,
+		"item_name": row.item_name,
+		"valuation_rate": valuation_rate,
+		"current_qty": qty,
+		"current_valuation_rate": valuation_rate,
+		"current_serial_no": serial_no,
+		"serial_no": serial_no,
+		"batch_no": row.get("batch_no"),
 	}
 
+
 def get_itemwise_batch(warehouse, posting_date, company, item_code=None):
 	from erpnext.stock.report.batch_wise_balance_history.batch_wise_balance_history import execute
+
 	itemwise_batch_data = {}
 
-	filters = frappe._dict({
-		'warehouse': warehouse,
-		'from_date': posting_date,
-		'to_date': posting_date,
-		'company': company
-	})
+	filters = frappe._dict(
+		{"warehouse": warehouse, "from_date": posting_date, "to_date": posting_date, "company": company}
+	)
 
 	if item_code:
 		filters.item_code = item_code
@@ -600,54 +691,79 @@
 	columns, data = execute(filters)
 
 	for row in data:
-		itemwise_batch_data.setdefault(row[0], []).append(frappe._dict({
-			'item_code': row[0],
-			'warehouse': warehouse,
-			'qty': row[8],
-			'item_name': row[1],
-			'batch_no': row[4]
-		}))
+		itemwise_batch_data.setdefault(row[0], []).append(
+			frappe._dict(
+				{
+					"item_code": row[0],
+					"warehouse": warehouse,
+					"qty": row[8],
+					"item_name": row[1],
+					"batch_no": row[4],
+				}
+			)
+		)
 
 	return itemwise_batch_data
 
-@frappe.whitelist()
-def get_stock_balance_for(item_code, warehouse,
-	posting_date, posting_time, batch_no=None, with_valuation_rate= True):
-	frappe.has_permission("Stock Reconciliation", "write", throw = True)
 
-	item_dict = frappe.db.get_value("Item", item_code,
-		["has_serial_no", "has_batch_no"], as_dict=1)
+@frappe.whitelist()
+def get_stock_balance_for(
+	item_code: str,
+	warehouse: str,
+	posting_date: str,
+	posting_time: str,
+	batch_no: Optional[str] = None,
+	with_valuation_rate: bool = True,
+):
+	frappe.has_permission("Stock Reconciliation", "write", throw=True)
+
+	item_dict = frappe.get_cached_value(
+		"Item", item_code, ["has_serial_no", "has_batch_no"], as_dict=1
+	)
 
 	if not item_dict:
 		# In cases of data upload to Items table
 		msg = _("Item {} does not exist.").format(item_code)
 		frappe.throw(msg, title=_("Missing"))
 
-	serial_nos = ""
-	with_serial_no = True if item_dict.get("has_serial_no") else False
-	data = get_stock_balance(item_code, warehouse, posting_date, posting_time,
-		with_valuation_rate=with_valuation_rate, with_serial_no=with_serial_no)
+	serial_nos = None
+	has_serial_no = bool(item_dict.get("has_serial_no"))
+	has_batch_no = bool(item_dict.get("has_batch_no"))
 
-	if with_serial_no:
+	if not batch_no and has_batch_no:
+		# Not enough information to fetch data
+		return {"qty": 0, "rate": 0, "serial_nos": None}
+
+	# TODO: fetch only selected batch's values
+	data = get_stock_balance(
+		item_code,
+		warehouse,
+		posting_date,
+		posting_time,
+		with_valuation_rate=with_valuation_rate,
+		with_serial_no=has_serial_no,
+	)
+
+	if has_serial_no:
 		qty, rate, serial_nos = data
 	else:
 		qty, rate = data
 
 	if item_dict.get("has_batch_no"):
-		qty = get_batch_qty(batch_no, warehouse, posting_date=posting_date, posting_time=posting_time) or 0
+		qty = (
+			get_batch_qty(batch_no, warehouse, posting_date=posting_date, posting_time=posting_time) or 0
+		)
 
-	return {
-		'qty': qty,
-		'rate': rate,
-		'serial_nos': serial_nos
-	}
+	return {"qty": qty, "rate": rate, "serial_nos": serial_nos}
+
 
 @frappe.whitelist()
 def get_difference_account(purpose, company):
-	if purpose == 'Stock Reconciliation':
+	if purpose == "Stock Reconciliation":
 		account = get_company_default(company, "stock_adjustment_account")
 	else:
-		account = frappe.db.get_value('Account', {'is_group': 0,
-			'company': company, 'account_type': 'Temporary'}, 'name')
+		account = frappe.db.get_value(
+			"Account", {"is_group": 0, "company": company, "account_type": "Temporary"}, "name"
+		)
 
 	return account
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index e6b252e..191c03f 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -19,10 +19,11 @@
 )
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
 from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after
+from erpnext.stock.tests.test_utils import StockTestMixin
 from erpnext.stock.utils import get_incoming_rate, get_stock_value_on, get_valuation_method
 
 
-class TestStockReconciliation(FrappeTestCase):
+class TestStockReconciliation(FrappeTestCase, StockTestMixin):
 	@classmethod
 	def setUpClass(cls):
 		create_batch_or_serial_no_items()
@@ -30,8 +31,8 @@
 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
 
 	def tearDown(self):
-		frappe.flags.dont_execute_stock_reposts = None
-
+		frappe.local.future_sle = {}
+		frappe.flags.pop("dont_execute_stock_reposts", None)
 
 	def test_reco_for_fifo(self):
 		self._test_reco_sle_gle("FIFO")
@@ -40,55 +41,73 @@
 		self._test_reco_sle_gle("Moving Average")
 
 	def _test_reco_sle_gle(self, valuation_method):
-		se1, se2, se3 = insert_existing_sle(warehouse='Stores - TCP1')
-		company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
+		item_code = self.make_item(properties={"valuation_method": valuation_method}).name
+
+		se1, se2, se3 = insert_existing_sle(warehouse="Stores - TCP1", item_code=item_code)
+		company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")
 		# [[qty, valuation_rate, posting_date,
-		#		posting_time, expected_stock_value, bin_qty, bin_valuation]]
+		# 		posting_time, expected_stock_value, bin_qty, bin_valuation]]
 
 		input_data = [
 			[50, 1000, "2012-12-26", "12:00"],
 			[25, 900, "2012-12-26", "12:00"],
 			["", 1000, "2012-12-20", "12:05"],
 			[20, "", "2012-12-26", "12:05"],
-			[0, "", "2012-12-31", "12:10"]
+			[0, "", "2012-12-31", "12:10"],
 		]
 
 		for d in input_data:
-			set_valuation_method("_Test Item", valuation_method)
-
-			last_sle = get_previous_sle({
-				"item_code": "_Test Item",
-				"warehouse": "Stores - TCP1",
-				"posting_date": d[2],
-				"posting_time": d[3]
-			})
+			last_sle = get_previous_sle(
+				{
+					"item_code": item_code,
+					"warehouse": "Stores - TCP1",
+					"posting_date": d[2],
+					"posting_time": d[3],
+				}
+			)
 
 			# submit stock reconciliation
-			stock_reco = create_stock_reconciliation(qty=d[0], rate=d[1],
-				posting_date=d[2], posting_time=d[3], warehouse="Stores - TCP1",
-				company=company, expense_account = "Stock Adjustment - TCP1")
+			stock_reco = create_stock_reconciliation(
+				item_code=item_code,
+				qty=d[0],
+				rate=d[1],
+				posting_date=d[2],
+				posting_time=d[3],
+				warehouse="Stores - TCP1",
+				company=company,
+				expense_account="Stock Adjustment - TCP1",
+			)
 
 			# check stock value
-			sle = frappe.db.sql("""select * from `tabStock Ledger Entry`
-				where voucher_type='Stock Reconciliation' and voucher_no=%s""", stock_reco.name, as_dict=1)
+			sle = frappe.db.sql(
+				"""select * from `tabStock Ledger Entry`
+				where voucher_type='Stock Reconciliation' and voucher_no=%s""",
+				stock_reco.name,
+				as_dict=1,
+			)
 
 			qty_after_transaction = flt(d[0]) if d[0] != "" else flt(last_sle.get("qty_after_transaction"))
 
 			valuation_rate = flt(d[1]) if d[1] != "" else flt(last_sle.get("valuation_rate"))
 
-			if qty_after_transaction == last_sle.get("qty_after_transaction") \
-				and valuation_rate == last_sle.get("valuation_rate"):
-					self.assertFalse(sle)
+			if qty_after_transaction == last_sle.get(
+				"qty_after_transaction"
+			) and valuation_rate == last_sle.get("valuation_rate"):
+				self.assertFalse(sle)
 			else:
 				self.assertEqual(flt(sle[0].qty_after_transaction, 1), flt(qty_after_transaction, 1))
 				self.assertEqual(flt(sle[0].stock_value, 1), flt(qty_after_transaction * valuation_rate, 1))
 
 				# no gl entries
-				self.assertTrue(frappe.db.get_value("Stock Ledger Entry",
-					{"voucher_type": "Stock Reconciliation", "voucher_no": stock_reco.name}))
+				self.assertTrue(
+					frappe.db.get_value(
+						"Stock Ledger Entry", {"voucher_type": "Stock Reconciliation", "voucher_no": stock_reco.name}
+					)
+				)
 
-				acc_bal, stock_bal, wh_list = get_stock_and_account_balance("Stock In Hand - TCP1",
-					stock_reco.posting_date, stock_reco.company)
+				acc_bal, stock_bal, wh_list = get_stock_and_account_balance(
+					"Stock In Hand - TCP1", stock_reco.posting_date, stock_reco.company
+				)
 				self.assertEqual(flt(acc_bal, 1), flt(stock_bal, 1))
 
 				stock_reco.cancel()
@@ -98,18 +117,33 @@
 		se1.cancel()
 
 	def test_get_items(self):
-		create_warehouse("_Test Warehouse Group 1",
-			{"is_group": 1, "company": "_Test Company", "parent_warehouse": "All Warehouses - _TC"})
-		create_warehouse("_Test Warehouse Ledger 1",
-			{"is_group": 0, "parent_warehouse": "_Test Warehouse Group 1 - _TC", "company": "_Test Company"})
+		create_warehouse(
+			"_Test Warehouse Group 1",
+			{"is_group": 1, "company": "_Test Company", "parent_warehouse": "All Warehouses - _TC"},
+		)
+		create_warehouse(
+			"_Test Warehouse Ledger 1",
+			{
+				"is_group": 0,
+				"parent_warehouse": "_Test Warehouse Group 1 - _TC",
+				"company": "_Test Company",
+			},
+		)
 
-		create_item("_Test Stock Reco Item", is_stock_item=1, valuation_rate=100,
-			warehouse="_Test Warehouse Ledger 1 - _TC", opening_stock=100)
+		create_item(
+			"_Test Stock Reco Item",
+			is_stock_item=1,
+			valuation_rate=100,
+			warehouse="_Test Warehouse Ledger 1 - _TC",
+			opening_stock=100,
+		)
 
 		items = get_items("_Test Warehouse Group 1 - _TC", nowdate(), nowtime(), "_Test Company")
 
-		self.assertEqual(["_Test Stock Reco Item", "_Test Warehouse Ledger 1 - _TC", 100],
-			[items[0]["item_code"], items[0]["warehouse"], items[0]["qty"]])
+		self.assertEqual(
+			["_Test Stock Reco Item", "_Test Warehouse Ledger 1 - _TC", 100],
+			[items[0]["item_code"], items[0]["warehouse"], items[0]["qty"]],
+		)
 
 	def test_stock_reco_for_serialized_item(self):
 		to_delete_records = []
@@ -119,8 +153,9 @@
 		serial_item_code = "Stock-Reco-Serial-Item-1"
 		serial_warehouse = "_Test Warehouse for Stock Reco1 - _TC"
 
-		sr = create_stock_reconciliation(item_code=serial_item_code,
-			warehouse = serial_warehouse, qty=5, rate=200)
+		sr = create_stock_reconciliation(
+			item_code=serial_item_code, warehouse=serial_warehouse, qty=5, rate=200
+		)
 
 		serial_nos = get_serial_nos(sr.items[0].serial_no)
 		self.assertEqual(len(serial_nos), 5)
@@ -130,7 +165,7 @@
 			"warehouse": serial_warehouse,
 			"posting_date": nowdate(),
 			"posting_time": nowtime(),
-			"serial_no": sr.items[0].serial_no
+			"serial_no": sr.items[0].serial_no,
 		}
 
 		valuation_rate = get_incoming_rate(args)
@@ -138,8 +173,9 @@
 
 		to_delete_records.append(sr.name)
 
-		sr = create_stock_reconciliation(item_code=serial_item_code,
-			warehouse = serial_warehouse, qty=5, rate=300)
+		sr = create_stock_reconciliation(
+			item_code=serial_item_code, warehouse=serial_warehouse, qty=5, rate=300
+		)
 
 		serial_nos1 = get_serial_nos(sr.items[0].serial_no)
 		self.assertEqual(len(serial_nos1), 5)
@@ -149,7 +185,7 @@
 			"warehouse": serial_warehouse,
 			"posting_date": nowdate(),
 			"posting_time": nowtime(),
-			"serial_no": sr.items[0].serial_no
+			"serial_no": sr.items[0].serial_no,
 		}
 
 		valuation_rate = get_incoming_rate(args)
@@ -162,7 +198,6 @@
 			stock_doc = frappe.get_doc("Stock Reconciliation", d)
 			stock_doc.cancel()
 
-
 	def test_stock_reco_for_merge_serialized_item(self):
 		to_delete_records = []
 
@@ -170,23 +205,34 @@
 		serial_item_code = "Stock-Reco-Serial-Item-2"
 		serial_warehouse = "_Test Warehouse for Stock Reco1 - _TC"
 
-		sr = create_stock_reconciliation(item_code=serial_item_code, serial_no=random_string(6),
-			warehouse = serial_warehouse, qty=1, rate=100, do_not_submit=True, purpose='Opening Stock')
+		sr = create_stock_reconciliation(
+			item_code=serial_item_code,
+			serial_no=random_string(6),
+			warehouse=serial_warehouse,
+			qty=1,
+			rate=100,
+			do_not_submit=True,
+			purpose="Opening Stock",
+		)
 
 		for i in range(3):
-			sr.append('items', {
-				'item_code': serial_item_code,
-				'warehouse': serial_warehouse,
-				'qty': 1,
-				'valuation_rate': 100,
-				'serial_no': random_string(6)
-			})
+			sr.append(
+				"items",
+				{
+					"item_code": serial_item_code,
+					"warehouse": serial_warehouse,
+					"qty": 1,
+					"valuation_rate": 100,
+					"serial_no": random_string(6),
+				},
+			)
 
 		sr.save()
 		sr.submit()
 
-		sle_entries = frappe.get_all('Stock Ledger Entry', filters= {'voucher_no': sr.name},
-			fields = ['name', 'incoming_rate'])
+		sle_entries = frappe.get_all(
+			"Stock Ledger Entry", filters={"voucher_no": sr.name}, fields=["name", "incoming_rate"]
+		)
 
 		self.assertEqual(len(sle_entries), 1)
 		self.assertEqual(sle_entries[0].incoming_rate, 100)
@@ -205,8 +251,9 @@
 		item_code = "Stock-Reco-batch-Item-1"
 		warehouse = "_Test Warehouse for Stock Reco2 - _TC"
 
-		sr = create_stock_reconciliation(item_code=item_code,
-			warehouse = warehouse, qty=5, rate=200, do_not_submit=1)
+		sr = create_stock_reconciliation(
+			item_code=item_code, warehouse=warehouse, qty=5, rate=200, do_not_save=1
+		)
 		sr.save()
 		sr.submit()
 
@@ -214,8 +261,9 @@
 		self.assertTrue(batch_no)
 		to_delete_records.append(sr.name)
 
-		sr1 = create_stock_reconciliation(item_code=item_code,
-			warehouse = warehouse, qty=6, rate=300, batch_no=batch_no)
+		sr1 = create_stock_reconciliation(
+			item_code=item_code, warehouse=warehouse, qty=6, rate=300, batch_no=batch_no
+		)
 
 		args = {
 			"item_code": item_code,
@@ -229,9 +277,9 @@
 		self.assertEqual(valuation_rate, 300)
 		to_delete_records.append(sr1.name)
 
-
-		sr2 = create_stock_reconciliation(item_code=item_code,
-			warehouse = warehouse, qty=0, rate=0, batch_no=batch_no)
+		sr2 = create_stock_reconciliation(
+			item_code=item_code, warehouse=warehouse, qty=0, rate=0, batch_no=batch_no
+		)
 
 		stock_value = get_stock_value_on(warehouse, nowdate(), item_code)
 		self.assertEqual(stock_value, 0)
@@ -242,12 +290,91 @@
 			stock_doc = frappe.get_doc("Stock Reconciliation", d)
 			stock_doc.cancel()
 
-	def test_customer_provided_items(self):
-		item_code = 'Stock-Reco-customer-Item-100'
-		create_item(item_code, is_customer_provided_item = 1,
-			  customer = '_Test Customer', is_purchase_item = 0)
+	def test_stock_reco_for_serial_and_batch_item(self):
+		item = create_item("_TestBatchSerialItemReco")
+		item.has_batch_no = 1
+		item.create_new_batch = 1
+		item.has_serial_no = 1
+		item.batch_number_series = "TBS-BATCH-.##"
+		item.serial_no_series = "TBS-.####"
+		item.save()
 
-		sr = create_stock_reconciliation(item_code = item_code, qty = 10, rate = 420)
+		warehouse = "_Test Warehouse for Stock Reco2 - _TC"
+
+		sr = create_stock_reconciliation(item_code=item.item_code, warehouse=warehouse, qty=1, rate=100)
+
+		batch_no = sr.items[0].batch_no
+
+		serial_nos = get_serial_nos(sr.items[0].serial_no)
+		self.assertEqual(len(serial_nos), 1)
+		self.assertEqual(frappe.db.get_value("Serial No", serial_nos[0], "batch_no"), batch_no)
+
+		sr.cancel()
+
+		self.assertEqual(frappe.db.get_value("Serial No", serial_nos[0], "status"), "Inactive")
+		self.assertEqual(frappe.db.exists("Batch", batch_no), None)
+
+	def test_stock_reco_for_serial_and_batch_item_with_future_dependent_entry(self):
+		"""
+		Behaviour: 1) Create Stock Reconciliation, which will be the origin document
+		of a new batch having a serial no
+		2) Create a Stock Entry that adds a serial no to the same batch following this
+		Stock Reconciliation
+		3) Cancel Stock Entry
+		Expected Result: 3) Serial No only in the Stock Entry is Inactive and Batch qty decreases
+		"""
+		from erpnext.stock.doctype.batch.batch import get_batch_qty
+		from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+
+		item = create_item("_TestBatchSerialItemDependentReco")
+		item.has_batch_no = 1
+		item.create_new_batch = 1
+		item.has_serial_no = 1
+		item.batch_number_series = "TBSD-BATCH-.##"
+		item.serial_no_series = "TBSD-.####"
+		item.save()
+
+		warehouse = "_Test Warehouse for Stock Reco2 - _TC"
+
+		stock_reco = create_stock_reconciliation(
+			item_code=item.item_code, warehouse=warehouse, qty=1, rate=100
+		)
+		batch_no = stock_reco.items[0].batch_no
+		reco_serial_no = get_serial_nos(stock_reco.items[0].serial_no)[0]
+
+		stock_entry = make_stock_entry(
+			item_code=item.item_code, target=warehouse, qty=1, basic_rate=100, batch_no=batch_no
+		)
+		serial_no_2 = get_serial_nos(stock_entry.items[0].serial_no)[0]
+
+		# Check Batch qty after 2 transactions
+		batch_qty = get_batch_qty(batch_no, warehouse, item.item_code)
+		self.assertEqual(batch_qty, 2)
+
+		# Cancel latest stock document
+		stock_entry.cancel()
+
+		# Check Batch qty after cancellation
+		batch_qty = get_batch_qty(batch_no, warehouse, item.item_code)
+		self.assertEqual(batch_qty, 1)
+
+		# Check if Serial No from Stock Reconcilation is intact
+		self.assertEqual(frappe.db.get_value("Serial No", reco_serial_no, "batch_no"), batch_no)
+		self.assertEqual(frappe.db.get_value("Serial No", reco_serial_no, "status"), "Active")
+
+		# Check if Serial No from Stock Entry is Unlinked and Inactive
+		self.assertEqual(frappe.db.get_value("Serial No", serial_no_2, "batch_no"), None)
+		self.assertEqual(frappe.db.get_value("Serial No", serial_no_2, "status"), "Inactive")
+
+		stock_reco.cancel()
+
+	def test_customer_provided_items(self):
+		item_code = "Stock-Reco-customer-Item-100"
+		create_item(
+			item_code, is_customer_provided_item=1, customer="_Test Customer", is_purchase_item=0
+		)
+
+		sr = create_stock_reconciliation(item_code=item_code, qty=10, rate=420)
 
 		self.assertEqual(sr.get("items")[0].allow_zero_valuation_rate, 1)
 		self.assertEqual(sr.get("items")[0].valuation_rate, 0)
@@ -255,106 +382,111 @@
 
 	def test_backdated_stock_reco_qty_reposting(self):
 		"""
-			Test if a backdated stock reco recalculates future qty until next reco.
-			-------------------------------------------
-			Var		| Doc	|	Qty	| Balance
-			-------------------------------------------
-			SR5		| Reco	|	0	|	8	(posting date: today-4) [backdated]
-			PR1		| PR	|	10	|	18	(posting date: today-3)
-			PR2		| PR	|	1	|	19	(posting date: today-2)
-			SR4		| Reco	|	0	|	6	(posting date: today-1) [backdated]
-			PR3		| PR	|	1	|	7	(posting date: today) # can't post future PR
+		Test if a backdated stock reco recalculates future qty until next reco.
+		-------------------------------------------
+		Var		| Doc	|	Qty	| Balance
+		-------------------------------------------
+		PR5     | PR    |   10  |  10   (posting date: today-4) [backdated]
+		SR5		| Reco	|	0	|	8	(posting date: today-4) [backdated]
+		PR1		| PR	|	10	|	18	(posting date: today-3)
+		PR2		| PR	|	1	|	19	(posting date: today-2)
+		SR4		| Reco	|	0	|	6	(posting date: today-1) [backdated]
+		PR3		| PR	|	1	|	7	(posting date: today) # can't post future PR
 		"""
-		item_code = "Backdated-Reco-Item"
+		item_code = self.make_item().name
 		warehouse = "_Test Warehouse - _TC"
-		create_item(item_code)
 
-		pr1 = make_purchase_receipt(item_code=item_code, warehouse=warehouse, qty=10, rate=100,
-			posting_date=add_days(nowdate(), -3))
-		pr2 = make_purchase_receipt(item_code=item_code, warehouse=warehouse, qty=1, rate=100,
-			posting_date=add_days(nowdate(), -2))
-		pr3 = make_purchase_receipt(item_code=item_code, warehouse=warehouse, qty=1, rate=100,
-			posting_date=nowdate())
+		frappe.flags.dont_execute_stock_reposts = True
 
-		pr1_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr1.name, "is_cancelled": 0},
-			"qty_after_transaction")
-		pr3_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr3.name, "is_cancelled": 0},
-			"qty_after_transaction")
-		self.assertEqual(pr1_balance, 10)
-		self.assertEqual(pr3_balance, 12)
+		def assertBalance(doc, qty_after_transaction):
+			sle_balance = frappe.db.get_value(
+				"Stock Ledger Entry", {"voucher_no": doc.name, "is_cancelled": 0}, "qty_after_transaction"
+			)
+			self.assertEqual(sle_balance, qty_after_transaction)
+
+		pr1 = make_purchase_receipt(
+			item_code=item_code, warehouse=warehouse, qty=10, rate=100, posting_date=add_days(nowdate(), -3)
+		)
+		pr2 = make_purchase_receipt(
+			item_code=item_code, warehouse=warehouse, qty=1, rate=100, posting_date=add_days(nowdate(), -2)
+		)
+		pr3 = make_purchase_receipt(
+			item_code=item_code, warehouse=warehouse, qty=1, rate=100, posting_date=nowdate()
+		)
+		assertBalance(pr1, 10)
+		assertBalance(pr3, 12)
 
 		# post backdated stock reco in between
-		sr4 = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=6, rate=100,
-			posting_date=add_days(nowdate(), -1))
-		pr3_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr3.name, "is_cancelled": 0},
-			"qty_after_transaction")
-		self.assertEqual(pr3_balance, 7)
+		sr4 = create_stock_reconciliation(
+			item_code=item_code, warehouse=warehouse, qty=6, rate=100, posting_date=add_days(nowdate(), -1)
+		)
+		assertBalance(pr3, 7)
 
 		# post backdated stock reco at the start
-		sr5 = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=8, rate=100,
-			posting_date=add_days(nowdate(), -4))
-		pr1_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr1.name, "is_cancelled": 0},
-			"qty_after_transaction")
-		pr2_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr2.name, "is_cancelled": 0},
-			"qty_after_transaction")
-		sr4_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": sr4.name, "is_cancelled": 0},
-			"qty_after_transaction")
-		self.assertEqual(pr1_balance, 18)
-		self.assertEqual(pr2_balance, 19)
-		self.assertEqual(sr4_balance, 6) # check if future stock reco is unaffected
+		sr5 = create_stock_reconciliation(
+			item_code=item_code, warehouse=warehouse, qty=8, rate=100, posting_date=add_days(nowdate(), -4)
+		)
+		assertBalance(pr1, 18)
+		assertBalance(pr2, 19)
+		assertBalance(sr4, 6)  # check if future stock reco is unaffected
+
+		# Make a backdated receipt and check only entries till first SR are affected
+		pr5 = make_purchase_receipt(
+			item_code=item_code, warehouse=warehouse, qty=10, rate=100, posting_date=add_days(nowdate(), -5)
+		)
+		assertBalance(pr5, 10)
+		# check if future stock reco is unaffected
+		assertBalance(sr4, 6)
+		assertBalance(sr5, 8)
 
 		# cancel backdated stock reco and check future impact
 		sr5.cancel()
-		pr1_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr1.name, "is_cancelled": 0},
-			"qty_after_transaction")
-		pr2_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr2.name, "is_cancelled": 0},
-			"qty_after_transaction")
-		sr4_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": sr4.name, "is_cancelled": 0},
-			"qty_after_transaction")
-		self.assertEqual(pr1_balance, 10)
-		self.assertEqual(pr2_balance, 11)
-		self.assertEqual(sr4_balance, 6) # check if future stock reco is unaffected
-
-		# teardown
-		sr4.cancel()
-		pr3.cancel()
-		pr2.cancel()
-		pr1.cancel()
+		assertBalance(pr1, 10)
+		assertBalance(pr2, 11)
+		assertBalance(sr4, 6)  # check if future stock reco is unaffected
 
 	@change_settings("Stock Settings", {"allow_negative_stock": 0})
 	def test_backdated_stock_reco_future_negative_stock(self):
 		"""
-			Test if a backdated stock reco causes future negative stock and is blocked.
-			-------------------------------------------
-			Var		| Doc	|	Qty	| Balance
-			-------------------------------------------
-			PR1		| PR	|	10	|	10		(posting date: today-2)
-			SR3		| Reco	|	0	|	1		(posting date: today-1) [backdated & blocked]
-			DN2		| DN	|	-2	|	8(-1)	(posting date: today)
+		Test if a backdated stock reco causes future negative stock and is blocked.
+		-------------------------------------------
+		Var		| Doc	|	Qty	| Balance
+		-------------------------------------------
+		PR1		| PR	|	10	|	10		(posting date: today-2)
+		SR3		| Reco	|	0	|	1		(posting date: today-1) [backdated & blocked]
+		DN2		| DN	|	-2	|	8(-1)	(posting date: today)
 		"""
 		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
 		from erpnext.stock.stock_ledger import NegativeStockError
 
-		item_code = "Backdated-Reco-Item"
+		item_code = self.make_item().name
 		warehouse = "_Test Warehouse - _TC"
-		create_item(item_code)
 
+		pr1 = make_purchase_receipt(
+			item_code=item_code, warehouse=warehouse, qty=10, rate=100, posting_date=add_days(nowdate(), -2)
+		)
+		dn2 = create_delivery_note(
+			item_code=item_code, warehouse=warehouse, qty=2, rate=120, posting_date=nowdate()
+		)
 
-		pr1 = make_purchase_receipt(item_code=item_code, warehouse=warehouse, qty=10, rate=100,
-			posting_date=add_days(nowdate(), -2))
-		dn2 = create_delivery_note(item_code=item_code, warehouse=warehouse, qty=2, rate=120,
-			posting_date=nowdate())
-
-		pr1_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr1.name, "is_cancelled": 0},
-			"qty_after_transaction")
-		dn2_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": dn2.name, "is_cancelled": 0},
-			"qty_after_transaction")
+		pr1_balance = frappe.db.get_value(
+			"Stock Ledger Entry", {"voucher_no": pr1.name, "is_cancelled": 0}, "qty_after_transaction"
+		)
+		dn2_balance = frappe.db.get_value(
+			"Stock Ledger Entry", {"voucher_no": dn2.name, "is_cancelled": 0}, "qty_after_transaction"
+		)
 		self.assertEqual(pr1_balance, 10)
 		self.assertEqual(dn2_balance, 8)
 
 		# check if stock reco is blocked
-		sr3 = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=1, rate=100,
-			posting_date=add_days(nowdate(), -1), do_not_submit=True)
+		sr3 = create_stock_reconciliation(
+			item_code=item_code,
+			warehouse=warehouse,
+			qty=1,
+			rate=100,
+			posting_date=add_days(nowdate(), -1),
+			do_not_submit=True,
+		)
 		self.assertRaises(NegativeStockError, sr3.submit)
 
 		# teardown
@@ -362,33 +494,37 @@
 		dn2.cancel()
 		pr1.cancel()
 
-
 	@change_settings("Stock Settings", {"allow_negative_stock": 0})
 	def test_backdated_stock_reco_cancellation_future_negative_stock(self):
 		"""
-			Test if a backdated stock reco cancellation that causes future negative stock is blocked.
-			-------------------------------------------
-			Var | Doc  | Qty | Balance
-			-------------------------------------------
-			SR  | Reco | 100 | 100     (posting date: today-1) (shouldn't be cancelled after DN)
-			DN  | DN   | 100 |   0     (posting date: today)
+		Test if a backdated stock reco cancellation that causes future negative stock is blocked.
+		-------------------------------------------
+		Var | Doc  | Qty | Balance
+		-------------------------------------------
+		SR  | Reco | 100 | 100     (posting date: today-1) (shouldn't be cancelled after DN)
+		DN  | DN   | 100 |   0     (posting date: today)
 		"""
 		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
 		from erpnext.stock.stock_ledger import NegativeStockError
 
-		item_code = "Backdated-Reco-Cancellation-Item"
+		item_code = self.make_item().name
 		warehouse = "_Test Warehouse - _TC"
-		create_item(item_code)
 
+		sr = create_stock_reconciliation(
+			item_code=item_code,
+			warehouse=warehouse,
+			qty=100,
+			rate=100,
+			posting_date=add_days(nowdate(), -1),
+		)
 
-		sr = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=100, rate=100,
-			posting_date=add_days(nowdate(), -1))
+		dn = create_delivery_note(
+			item_code=item_code, warehouse=warehouse, qty=100, rate=120, posting_date=nowdate()
+		)
 
-		dn = create_delivery_note(item_code=item_code, warehouse=warehouse, qty=100, rate=120,
-			posting_date=nowdate())
-
-		dn_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": dn.name, "is_cancelled": 0},
-			"qty_after_transaction")
+		dn_balance = frappe.db.get_value(
+			"Stock Ledger Entry", {"voucher_no": dn.name, "is_cancelled": 0}, "qty_after_transaction"
+		)
 		self.assertEqual(dn_balance, 0)
 
 		# check if cancellation of stock reco is blocked
@@ -400,43 +536,50 @@
 	def test_intermediate_sr_bin_update(self):
 		"""Bin should show correct qty even for backdated entries.
 
-			-------------------------------------------
-			| creation | Var | Doc  | Qty | balance qty
-			-------------------------------------------
-			|  1       | SR  | Reco | 10  | 10     (posting date: today+10)
-			|  3       | SR2 | Reco | 11  | 11     (posting date: today+11)
-			|  2       | DN  | DN   | 5   | 6 <-- assert in BIN  (posting date: today+12)
+		-------------------------------------------
+		| creation | Var | Doc  | Qty | balance qty
+		-------------------------------------------
+		|  1       | SR  | Reco | 10  | 10     (posting date: today+10)
+		|  3       | SR2 | Reco | 11  | 11     (posting date: today+11)
+		|  2       | DN  | DN   | 5   | 6 <-- assert in BIN  (posting date: today+12)
 		"""
 		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
 
-		# repost will make this test useless, qty should update in realtime without reposts
-		frappe.flags.dont_execute_stock_reposts = True
 		frappe.db.rollback()
 
-		item_code = "Backdated-Reco-Cancellation-Item"
+		# repost will make this test useless, qty should update in realtime without reposts
+		frappe.flags.dont_execute_stock_reposts = True
+
+		item_code = self.make_item().name
 		warehouse = "_Test Warehouse - _TC"
-		create_item(item_code)
 
-		sr = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=10, rate=100,
-			posting_date=add_days(nowdate(), 10))
+		sr = create_stock_reconciliation(
+			item_code=item_code, warehouse=warehouse, qty=10, rate=100, posting_date=add_days(nowdate(), 10)
+		)
 
-		dn = create_delivery_note(item_code=item_code, warehouse=warehouse, qty=5, rate=120,
-			posting_date=add_days(nowdate(), 12))
-		old_bin_qty = frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "actual_qty")
+		dn = create_delivery_note(
+			item_code=item_code, warehouse=warehouse, qty=5, rate=120, posting_date=add_days(nowdate(), 12)
+		)
+		old_bin_qty = frappe.db.get_value(
+			"Bin", {"item_code": item_code, "warehouse": warehouse}, "actual_qty"
+		)
 
-		sr2 = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=11, rate=100,
-			posting_date=add_days(nowdate(), 11))
-		new_bin_qty = frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "actual_qty")
+		sr2 = create_stock_reconciliation(
+			item_code=item_code, warehouse=warehouse, qty=11, rate=100, posting_date=add_days(nowdate(), 11)
+		)
+		new_bin_qty = frappe.db.get_value(
+			"Bin", {"item_code": item_code, "warehouse": warehouse}, "actual_qty"
+		)
 
 		self.assertEqual(old_bin_qty + 1, new_bin_qty)
 		frappe.db.rollback()
 
-
 	def test_valid_batch(self):
 		create_batch_item_with_batch("Testing Batch Item 1", "001")
 		create_batch_item_with_batch("Testing Batch Item 2", "002")
-		sr = create_stock_reconciliation(item_code="Testing Batch Item 1", qty=1, rate=100, batch_no="002"
-			, do_not_submit=True)
+		sr = create_stock_reconciliation(
+			item_code="Testing Batch Item 1", qty=1, rate=100, batch_no="002", do_not_submit=True
+		)
 		self.assertRaises(frappe.ValidationError, sr.submit)
 
 	def test_serial_no_cancellation(self):
@@ -458,15 +601,17 @@
 		serial_nos.pop()
 		new_serial_nos = "\n".join(serial_nos)
 
-		sr = create_stock_reconciliation(item_code=item.name, warehouse=warehouse, serial_no=new_serial_nos, qty=9)
+		sr = create_stock_reconciliation(
+			item_code=item.name, warehouse=warehouse, serial_no=new_serial_nos, qty=9
+		)
 		sr.cancel()
 
-		active_sr_no = frappe.get_all("Serial No",
-				filters={"item_code": item_code, "warehouse": warehouse, "status": "Active"})
+		active_sr_no = frappe.get_all(
+			"Serial No", filters={"item_code": item_code, "warehouse": warehouse, "status": "Active"}
+		)
 
 		self.assertEqual(len(active_sr_no), 10)
 
-
 	def test_serial_no_creation_and_inactivation(self):
 		item = create_item("_TestItemCreatedWithStockReco", is_stock_item=1)
 		if not item.has_serial_no:
@@ -476,19 +621,27 @@
 		item_code = item.name
 		warehouse = "_Test Warehouse - _TC"
 
-		sr = create_stock_reconciliation(item_code=item.name, warehouse=warehouse,
-				serial_no="SR-CREATED-SR-NO", qty=1, do_not_submit=True, rate=100)
+		sr = create_stock_reconciliation(
+			item_code=item.name,
+			warehouse=warehouse,
+			serial_no="SR-CREATED-SR-NO",
+			qty=1,
+			do_not_submit=True,
+			rate=100,
+		)
 		sr.save()
 		self.assertEqual(cstr(sr.items[0].current_serial_no), "")
 		sr.submit()
 
-		active_sr_no = frappe.get_all("Serial No",
-				filters={"item_code": item_code, "warehouse": warehouse, "status": "Active"})
+		active_sr_no = frappe.get_all(
+			"Serial No", filters={"item_code": item_code, "warehouse": warehouse, "status": "Active"}
+		)
 		self.assertEqual(len(active_sr_no), 1)
 
 		sr.cancel()
-		active_sr_no = frappe.get_all("Serial No",
-				filters={"item_code": item_code, "warehouse": warehouse, "status": "Active"})
+		active_sr_no = frappe.get_all(
+			"Serial No", filters={"item_code": item_code, "warehouse": warehouse, "status": "Active"}
+		)
 		self.assertEqual(len(active_sr_no), 0)
 
 
@@ -499,32 +652,51 @@
 		batch_item_doc.create_new_batch = 1
 		batch_item_doc.save(ignore_permissions=True)
 
-	if not frappe.db.exists('Batch', batch_id):
-		b = frappe.new_doc('Batch')
+	if not frappe.db.exists("Batch", batch_id):
+		b = frappe.new_doc("Batch")
 		b.item = item_name
 		b.batch_id = batch_id
 		b.save()
 
-def insert_existing_sle(warehouse):
+
+def insert_existing_sle(warehouse, item_code="_Test Item"):
 	from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 
-	se1 = make_stock_entry(posting_date="2012-12-15", posting_time="02:00", item_code="_Test Item",
-		target=warehouse, qty=10, basic_rate=700)
+	se1 = make_stock_entry(
+		posting_date="2012-12-15",
+		posting_time="02:00",
+		item_code=item_code,
+		target=warehouse,
+		qty=10,
+		basic_rate=700,
+	)
 
-	se2 = make_stock_entry(posting_date="2012-12-25", posting_time="03:00", item_code="_Test Item",
-		source=warehouse, qty=15)
+	se2 = make_stock_entry(
+		posting_date="2012-12-25", posting_time="03:00", item_code=item_code, source=warehouse, qty=15
+	)
 
-	se3 = make_stock_entry(posting_date="2013-01-05", posting_time="07:00", item_code="_Test Item",
-		target=warehouse, qty=15, basic_rate=1200)
+	se3 = make_stock_entry(
+		posting_date="2013-01-05",
+		posting_time="07:00",
+		item_code=item_code,
+		target=warehouse,
+		qty=15,
+		basic_rate=1200,
+	)
 
 	return se1, se2, se3
 
-def create_batch_or_serial_no_items():
-	create_warehouse("_Test Warehouse for Stock Reco1",
-		{"is_group": 0, "parent_warehouse": "_Test Warehouse Group - _TC"})
 
-	create_warehouse("_Test Warehouse for Stock Reco2",
-		{"is_group": 0, "parent_warehouse": "_Test Warehouse Group - _TC"})
+def create_batch_or_serial_no_items():
+	create_warehouse(
+		"_Test Warehouse for Stock Reco1",
+		{"is_group": 0, "parent_warehouse": "_Test Warehouse Group - _TC"},
+	)
+
+	create_warehouse(
+		"_Test Warehouse for Stock Reco2",
+		{"is_group": 0, "parent_warehouse": "_Test Warehouse Group - _TC"},
+	)
 
 	serial_item_doc = create_item("Stock-Reco-Serial-Item-1", is_stock_item=1)
 	if not serial_item_doc.has_serial_no:
@@ -545,6 +717,7 @@
 		serial_item_doc.batch_number_series = "BASR.#####"
 		batch_item_doc.save(ignore_permissions=True)
 
+
 def create_stock_reconciliation(**args):
 	args = frappe._dict(args)
 	sr = frappe.new_doc("Stock Reconciliation")
@@ -553,28 +726,37 @@
 	sr.posting_time = args.posting_time or nowtime()
 	sr.set_posting_time = 1
 	sr.company = args.company or "_Test Company"
-	sr.expense_account = args.expense_account or \
-		("Stock Adjustment - _TC" if frappe.get_all("Stock Ledger Entry") else "Temporary Opening - _TC")
-	sr.cost_center = args.cost_center \
-		or frappe.get_cached_value("Company", sr.company, "cost_center") \
+	sr.expense_account = args.expense_account or (
+		"Stock Adjustment - _TC" if frappe.get_all("Stock Ledger Entry") else "Temporary Opening - _TC"
+	)
+	sr.cost_center = (
+		args.cost_center
+		or frappe.get_cached_value("Company", sr.company, "cost_center")
 		or "_Test Cost Center - _TC"
+	)
 
-	sr.append("items", {
-		"item_code": args.item_code or "_Test Item",
-		"warehouse": args.warehouse or "_Test Warehouse - _TC",
-		"qty": args.qty,
-		"valuation_rate": args.rate,
-		"serial_no": args.serial_no,
-		"batch_no": args.batch_no
-	})
+	sr.append(
+		"items",
+		{
+			"item_code": args.item_code or "_Test Item",
+			"warehouse": args.warehouse or "_Test Warehouse - _TC",
+			"qty": args.qty,
+			"valuation_rate": args.rate,
+			"serial_no": args.serial_no,
+			"batch_no": args.batch_no,
+		},
+	)
 
-	try:
-		if not args.do_not_submit:
-			sr.submit()
-	except EmptyStockReconciliationItemsError:
-		pass
+	if not args.do_not_save:
+		sr.insert()
+		try:
+			if not args.do_not_submit:
+				sr.submit()
+		except EmptyStockReconciliationItemsError:
+			pass
 	return sr
 
+
 def set_valuation_method(item_code, valuation_method):
 	existing_valuation_method = get_valuation_method(item_code)
 	if valuation_method == existing_valuation_method:
@@ -582,11 +764,13 @@
 
 	frappe.db.set_value("Item", item_code, "valuation_method", valuation_method)
 
-	for warehouse in frappe.get_all("Warehouse", filters={"company": "_Test Company"}, fields=["name", "is_group"]):
+	for warehouse in frappe.get_all(
+		"Warehouse", filters={"company": "_Test Company"}, fields=["name", "is_group"]
+	):
 		if not warehouse.is_group:
-			update_entries_after({
-				"item_code": item_code,
-				"warehouse": warehouse.name
-			}, allow_negative_stock=1)
+			update_entries_after(
+				{"item_code": item_code, "warehouse": warehouse.name}, allow_negative_stock=1
+			)
+
 
 test_dependencies = ["Item", "Warehouse"]
diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
index 6bbba05..79c2fcc 100644
--- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
+++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
@@ -16,15 +16,15 @@
   "amount",
   "allow_zero_valuation_rate",
   "serial_no_and_batch_section",
-  "serial_no",
-  "column_break_11",
   "batch_no",
+  "column_break_11",
+  "serial_no",
   "section_break_3",
   "current_qty",
-  "current_serial_no",
+  "current_amount",
   "column_break_9",
   "current_valuation_rate",
-  "current_amount",
+  "current_serial_no",
   "section_break_14",
   "quantity_difference",
   "column_break_16",
@@ -181,7 +181,7 @@
  ],
  "istable": 1,
  "links": [],
- "modified": "2021-05-21 12:13:33.041266",
+ "modified": "2022-04-02 04:19:40.380587",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Reconciliation Item",
@@ -190,5 +190,6 @@
  "quick_entry": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py
index bab521d..e0c8ed1 100644
--- a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py
+++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py
@@ -6,8 +6,6 @@
 
 
 class StockRepostingSettings(Document):
-
-
 	def validate(self):
 		self.set_minimum_reposting_time_slot()
 
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.js b/erpnext/stock/doctype/stock_settings/stock_settings.js
index cc0e2cf..89ac4b5 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.js
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.js
@@ -13,5 +13,24 @@
 
 		frm.set_query("default_warehouse", filters);
 		frm.set_query("sample_retention_warehouse", filters);
+	},
+	allow_negative_stock: function(frm) {
+		if (!frm.doc.allow_negative_stock) {
+			return;
+		}
+
+		let msg = __("Using negative stock disables FIFO/Moving average valuation when inventory is negative.");
+		msg += " ";
+		msg += __("This is considered dangerous from accounting point of view.")
+		msg += "<br>";
+		msg += ("Do you still want to enable negative inventory?");
+
+		frappe.confirm(
+			msg,
+			() => {},
+			() => {
+				frm.set_value("allow_negative_stock", 0);
+			}
+		);
 	}
 });
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py
index c1293cb..50807a9 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.py
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.py
@@ -16,24 +16,40 @@
 
 class StockSettings(Document):
 	def validate(self):
-		for key in ["item_naming_by", "item_group", "stock_uom",
-			"allow_negative_stock", "default_warehouse", "set_qty_in_transactions_based_on_serial_no_input"]:
-				frappe.db.set_default(key, self.get(key, ""))
+		for key in [
+			"item_naming_by",
+			"item_group",
+			"stock_uom",
+			"allow_negative_stock",
+			"default_warehouse",
+			"set_qty_in_transactions_based_on_serial_no_input",
+		]:
+			frappe.db.set_default(key, self.get(key, ""))
 
-		from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
-		set_by_naming_series("Item", "item_code",
-			self.get("item_naming_by")=="Naming Series", hide_name_field=True, make_mandatory=0)
+		from erpnext.utilities.naming import set_by_naming_series
+
+		set_by_naming_series(
+			"Item",
+			"item_code",
+			self.get("item_naming_by") == "Naming Series",
+			hide_name_field=True,
+			make_mandatory=0,
+		)
 
 		stock_frozen_limit = 356
 		submitted_stock_frozen = self.stock_frozen_upto_days or 0
 		if submitted_stock_frozen > stock_frozen_limit:
 			self.stock_frozen_upto_days = stock_frozen_limit
-			frappe.msgprint (_("`Freeze Stocks Older Than` should be smaller than %d days.") %stock_frozen_limit)
+			frappe.msgprint(
+				_("`Freeze Stocks Older Than` should be smaller than %d days.") % stock_frozen_limit
+			)
 
 		# show/hide barcode field
 		for name in ["barcode", "barcodes", "scan_barcode"]:
-			frappe.make_property_setter({'fieldname': name, 'property': 'hidden',
-				'value': 0 if self.show_barcode_field else 1}, validate_fields_for_doctype=False)
+			frappe.make_property_setter(
+				{"fieldname": name, "property": "hidden", "value": 0 if self.show_barcode_field else 1},
+				validate_fields_for_doctype=False,
+			)
 
 		self.validate_warehouses()
 		self.cant_change_valuation_method()
@@ -44,8 +60,12 @@
 		warehouse_fields = ["default_warehouse", "sample_retention_warehouse"]
 		for field in warehouse_fields:
 			if frappe.db.get_value("Warehouse", self.get(field), "is_group"):
-				frappe.throw(_("Group Warehouses cannot be used in transactions. Please change the value of {0}") \
-					.format(frappe.bold(self.meta.get_field(field).label)), title =_("Incorrect Warehouse"))
+				frappe.throw(
+					_("Group Warehouses cannot be used in transactions. Please change the value of {0}").format(
+						frappe.bold(self.meta.get_field(field).label)
+					),
+					title=_("Incorrect Warehouse"),
+				)
 
 	def cant_change_valuation_method(self):
 		db_valuation_method = frappe.db.get_single_value("Stock Settings", "valuation_method")
@@ -53,38 +73,73 @@
 		if db_valuation_method and db_valuation_method != self.valuation_method:
 			# check if there are any stock ledger entries against items
 			# which does not have it's own valuation method
-			sle = frappe.db.sql("""select name from `tabStock Ledger Entry` sle
+			sle = frappe.db.sql(
+				"""select name from `tabStock Ledger Entry` sle
 				where exists(select name from tabItem
 					where name=sle.item_code and (valuation_method is null or valuation_method='')) limit 1
-			""")
+			"""
+			)
 
 			if sle:
-				frappe.throw(_("Can't change the valuation method, as there are transactions against some items which do not have its own valuation method"))
+				frappe.throw(
+					_(
+						"Can't change the valuation method, as there are transactions against some items which do not have its own valuation method"
+					)
+				)
 
 	def validate_clean_description_html(self):
-		if int(self.clean_description_html or 0) \
-			and not int(self.db_get('clean_description_html') or 0):
+		if int(self.clean_description_html or 0) and not int(self.db_get("clean_description_html") or 0):
 			# changed to text
-			frappe.enqueue('erpnext.stock.doctype.stock_settings.stock_settings.clean_all_descriptions', now=frappe.flags.in_test)
+			frappe.enqueue(
+				"erpnext.stock.doctype.stock_settings.stock_settings.clean_all_descriptions",
+				now=frappe.flags.in_test,
+			)
 
 	def validate_pending_reposts(self):
 		if self.stock_frozen_upto:
 			check_pending_reposting(self.stock_frozen_upto)
 
-
 	def on_update(self):
 		self.toggle_warehouse_field_for_inter_warehouse_transfer()
 
 	def toggle_warehouse_field_for_inter_warehouse_transfer(self):
-		make_property_setter("Sales Invoice Item", "target_warehouse", "hidden", 1 - cint(self.allow_from_dn), "Check", validate_fields_for_doctype=False)
-		make_property_setter("Delivery Note Item", "target_warehouse", "hidden", 1 - cint(self.allow_from_dn), "Check", validate_fields_for_doctype=False)
-		make_property_setter("Purchase Invoice Item", "from_warehouse", "hidden", 1 - cint(self.allow_from_pr), "Check", validate_fields_for_doctype=False)
-		make_property_setter("Purchase Receipt Item", "from_warehouse", "hidden", 1 - cint(self.allow_from_pr), "Check", validate_fields_for_doctype=False)
+		make_property_setter(
+			"Sales Invoice Item",
+			"target_warehouse",
+			"hidden",
+			1 - cint(self.allow_from_dn),
+			"Check",
+			validate_fields_for_doctype=False,
+		)
+		make_property_setter(
+			"Delivery Note Item",
+			"target_warehouse",
+			"hidden",
+			1 - cint(self.allow_from_dn),
+			"Check",
+			validate_fields_for_doctype=False,
+		)
+		make_property_setter(
+			"Purchase Invoice Item",
+			"from_warehouse",
+			"hidden",
+			1 - cint(self.allow_from_pr),
+			"Check",
+			validate_fields_for_doctype=False,
+		)
+		make_property_setter(
+			"Purchase Receipt Item",
+			"from_warehouse",
+			"hidden",
+			1 - cint(self.allow_from_pr),
+			"Check",
+			validate_fields_for_doctype=False,
+		)
 
 
 def clean_all_descriptions():
-	for item in frappe.get_all('Item', ['name', 'description']):
+	for item in frappe.get_all("Item", ["name", "description"]):
 		if item.description:
 			clean_description = clean_html(item.description)
 		if item.description != clean_description:
-			frappe.db.set_value('Item', item.name, 'description', clean_description)
+			frappe.db.set_value("Item", item.name, "description", clean_description)
diff --git a/erpnext/stock/doctype/stock_settings/test_stock_settings.py b/erpnext/stock/doctype/stock_settings/test_stock_settings.py
index 1349671..974e163 100644
--- a/erpnext/stock/doctype/stock_settings/test_stock_settings.py
+++ b/erpnext/stock/doctype/stock_settings/test_stock_settings.py
@@ -13,35 +13,45 @@
 		frappe.db.set_value("Stock Settings", None, "clean_description_html", 0)
 
 	def test_settings(self):
-		item = frappe.get_doc(dict(
-			doctype = 'Item',
-			item_code = 'Item for description test',
-			item_group = 'Products',
-			description = '<p><span style="font-size: 12px;">Drawing No. 07-xxx-PO132<br></span><span style="font-size: 12px;">1800 x 1685 x 750<br></span><span style="font-size: 12px;">All parts made of Marine Ply<br></span><span style="font-size: 12px;">Top w/ Corian dd<br></span><span style="font-size: 12px;">CO, CS, VIP Day Cabin</span></p>'
-		)).insert()
+		item = frappe.get_doc(
+			dict(
+				doctype="Item",
+				item_code="Item for description test",
+				item_group="Products",
+				description='<p><span style="font-size: 12px;">Drawing No. 07-xxx-PO132<br></span><span style="font-size: 12px;">1800 x 1685 x 750<br></span><span style="font-size: 12px;">All parts made of Marine Ply<br></span><span style="font-size: 12px;">Top w/ Corian dd<br></span><span style="font-size: 12px;">CO, CS, VIP Day Cabin</span></p>',
+			)
+		).insert()
 
-		settings = frappe.get_single('Stock Settings')
+		settings = frappe.get_single("Stock Settings")
 		settings.clean_description_html = 1
 		settings.save()
 
 		item.reload()
 
-		self.assertEqual(item.description, '<p>Drawing No. 07-xxx-PO132<br>1800 x 1685 x 750<br>All parts made of Marine Ply<br>Top w/ Corian dd<br>CO, CS, VIP Day Cabin</p>')
+		self.assertEqual(
+			item.description,
+			"<p>Drawing No. 07-xxx-PO132<br>1800 x 1685 x 750<br>All parts made of Marine Ply<br>Top w/ Corian dd<br>CO, CS, VIP Day Cabin</p>",
+		)
 
 		item.delete()
 
 	def test_clean_html(self):
-		settings = frappe.get_single('Stock Settings')
+		settings = frappe.get_single("Stock Settings")
 		settings.clean_description_html = 1
 		settings.save()
 
-		item = frappe.get_doc(dict(
-			doctype = 'Item',
-			item_code = 'Item for description test',
-			item_group = 'Products',
-			description = '<p><span style="font-size: 12px;">Drawing No. 07-xxx-PO132<br></span><span style="font-size: 12px;">1800 x 1685 x 750<br></span><span style="font-size: 12px;">All parts made of Marine Ply<br></span><span style="font-size: 12px;">Top w/ Corian dd<br></span><span style="font-size: 12px;">CO, CS, VIP Day Cabin</span></p>'
-		)).insert()
+		item = frappe.get_doc(
+			dict(
+				doctype="Item",
+				item_code="Item for description test",
+				item_group="Products",
+				description='<p><span style="font-size: 12px;">Drawing No. 07-xxx-PO132<br></span><span style="font-size: 12px;">1800 x 1685 x 750<br></span><span style="font-size: 12px;">All parts made of Marine Ply<br></span><span style="font-size: 12px;">Top w/ Corian dd<br></span><span style="font-size: 12px;">CO, CS, VIP Day Cabin</span></p>',
+			)
+		).insert()
 
-		self.assertEqual(item.description, '<p>Drawing No. 07-xxx-PO132<br>1800 x 1685 x 750<br>All parts made of Marine Ply<br>Top w/ Corian dd<br>CO, CS, VIP Day Cabin</p>')
+		self.assertEqual(
+			item.description,
+			"<p>Drawing No. 07-xxx-PO132<br>1800 x 1685 x 750<br>All parts made of Marine Ply<br>Top w/ Corian dd<br>CO, CS, VIP Day Cabin</p>",
+		)
 
 		item.delete()
diff --git a/erpnext/stock/doctype/variant_field/variant_field.json b/erpnext/stock/doctype/variant_field/variant_field.json
index ae90884..5a7da15 100644
--- a/erpnext/stock/doctype/variant_field/variant_field.json
+++ b/erpnext/stock/doctype/variant_field/variant_field.json
@@ -1,72 +1,32 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2017-08-29 16:33:33.978574", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "actions": [],
+ "creation": "2022-02-11 11:26:20.611960",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "field_name"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "field_name", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Field Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "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
+   "fieldname": "field_name",
+   "fieldtype": "Autocomplete",
+   "in_list_view": 1,
+   "label": "Field Name",
+   "reqd": 1
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2017-08-29 17:19:20.353197", 
- "modified_by": "Administrator", 
- "module": "Stock", 
- "name": "Variant Field", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 1, 
- "track_seen": 0
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2022-03-25 05:48:30.946201",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Variant Field",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": [],
+ "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/stock/doctype/warehouse/test_warehouse.py b/erpnext/stock/doctype/warehouse/test_warehouse.py
index cdb7719..5a7228a 100644
--- a/erpnext/stock/doctype/warehouse/test_warehouse.py
+++ b/erpnext/stock/doctype/warehouse/test_warehouse.py
@@ -4,20 +4,21 @@
 import frappe
 from frappe.test_runner import make_test_records
 from frappe.tests.utils import FrappeTestCase
-from frappe.utils import cint
 
 import erpnext
-from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
+from erpnext.accounts.doctype.account.test_account import create_account
 from erpnext.stock.doctype.item.test_item import create_item
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+from erpnext.stock.doctype.warehouse.warehouse import convert_to_group_or_ledger, get_children
 
-test_records = frappe.get_test_records('Warehouse')
+test_records = frappe.get_test_records("Warehouse")
+
 
 class TestWarehouse(FrappeTestCase):
 	def setUp(self):
 		super().setUp()
-		if not frappe.get_value('Item', '_Test Item'):
-			make_test_records('Item')
+		if not frappe.get_value("Item", "_Test Item"):
+			make_test_records("Item")
 
 	def test_parent_warehouse(self):
 		parent_warehouse = frappe.get_doc("Warehouse", "_Test Warehouse Group - _TC")
@@ -26,23 +27,37 @@
 	def test_warehouse_hierarchy(self):
 		p_warehouse = frappe.get_doc("Warehouse", "_Test Warehouse Group - _TC")
 
-		child_warehouses =  frappe.db.sql("""select name, is_group, parent_warehouse from `tabWarehouse` wh
-			where wh.lft > %s and wh.rgt < %s""", (p_warehouse.lft, p_warehouse.rgt), as_dict=1)
+		child_warehouses = frappe.db.sql(
+			"""select name, is_group, parent_warehouse from `tabWarehouse` wh
+			where wh.lft > %s and wh.rgt < %s""",
+			(p_warehouse.lft, p_warehouse.rgt),
+			as_dict=1,
+		)
 
 		for child_warehouse in child_warehouses:
 			self.assertEqual(p_warehouse.name, child_warehouse.parent_warehouse)
 			self.assertEqual(child_warehouse.is_group, 0)
 
+	def test_naming(self):
+		company = "Wind Power LLC"
+		warehouse_name = "Named Warehouse - WP"
+		wh = frappe.get_doc(doctype="Warehouse", warehouse_name=warehouse_name, company=company).insert()
+		self.assertEqual(wh.name, warehouse_name)
+
+		warehouse_name = "Unnamed Warehouse"
+		wh = frappe.get_doc(doctype="Warehouse", warehouse_name=warehouse_name, company=company).insert()
+		self.assertIn(warehouse_name, wh.name)
+
 	def test_unlinking_warehouse_from_item_defaults(self):
 		company = "_Test Company"
 
-		warehouse_names = [f'_Test Warehouse {i} for Unlinking' for i in range(2)]
+		warehouse_names = [f"_Test Warehouse {i} for Unlinking" for i in range(2)]
 		warehouse_ids = []
 		for warehouse in warehouse_names:
 			warehouse_id = create_warehouse(warehouse, company=company)
 			warehouse_ids.append(warehouse_id)
 
-		item_names = [f'_Test Item {i} for Unlinking' for i in range(2)]
+		item_names = [f"_Test Item {i} for Unlinking" for i in range(2)]
 		for item, warehouse in zip(item_names, warehouse_ids):
 			create_item(item, warehouse=warehouse, company=company)
 
@@ -52,19 +67,43 @@
 
 		# Check Item existance
 		for item in item_names:
-			self.assertTrue(
-				bool(frappe.db.exists("Item", item)),
-				f"{item} doesn't exist"
-			)
+			self.assertTrue(bool(frappe.db.exists("Item", item)), f"{item} doesn't exist")
 
 			item_doc = frappe.get_doc("Item", item)
 			for item_default in item_doc.item_defaults:
 				self.assertNotIn(
 					item_default.default_warehouse,
 					warehouse_ids,
-					f"{item} linked to {item_default.default_warehouse} in {warehouse_ids}."
+					f"{item} linked to {item_default.default_warehouse} in {warehouse_ids}.",
 				)
 
+	def test_group_non_group_conversion(self):
+
+		warehouse = frappe.get_doc("Warehouse", create_warehouse("TestGroupConversion"))
+
+		convert_to_group_or_ledger(warehouse.name)
+		warehouse.reload()
+		self.assertEqual(warehouse.is_group, 1)
+
+		child = create_warehouse("GroupWHChild", {"parent_warehouse": warehouse.name})
+		# chid exists
+		self.assertRaises(frappe.ValidationError, convert_to_group_or_ledger, warehouse.name)
+		frappe.delete_doc("Warehouse", child)
+
+		convert_to_group_or_ledger(warehouse.name)
+		warehouse.reload()
+		self.assertEqual(warehouse.is_group, 0)
+
+		make_stock_entry(item_code="_Test Item", target=warehouse.name, qty=1)
+		# SLE exists
+		self.assertRaises(frappe.ValidationError, convert_to_group_or_ledger, warehouse.name)
+
+	def test_get_children(self):
+		company = "_Test Company"
+
+		children = get_children("Warehouse", parent=company, company=company, is_root=True)
+		self.assertTrue(any(wh["value"] == "_Test Warehouse - _TC" for wh in children))
+
 
 def create_warehouse(warehouse_name, properties=None, company=None):
 	if not company:
@@ -84,40 +123,46 @@
 	else:
 		return warehouse_id
 
+
 def get_warehouse(**args):
 	args = frappe._dict(args)
-	if(frappe.db.exists("Warehouse", args.warehouse_name + " - " + args.abbr)):
+	if frappe.db.exists("Warehouse", args.warehouse_name + " - " + args.abbr):
 		return frappe.get_doc("Warehouse", args.warehouse_name + " - " + args.abbr)
 	else:
-		w = frappe.get_doc({
-		"company": args.company or "_Test Company",
-		"doctype": "Warehouse",
-		"warehouse_name": args.warehouse_name,
-		"is_group": 0,
-		"account": get_warehouse_account(args.warehouse_name, args.company, args.abbr)
-		})
+		w = frappe.get_doc(
+			{
+				"company": args.company or "_Test Company",
+				"doctype": "Warehouse",
+				"warehouse_name": args.warehouse_name,
+				"is_group": 0,
+				"account": get_warehouse_account(args.warehouse_name, args.company, args.abbr),
+			}
+		)
 		w.insert()
 		return w
 
+
 def get_warehouse_account(warehouse_name, company, company_abbr=None):
 	if not company_abbr:
-		company_abbr = frappe.get_cached_value("Company", company, 'abbr')
+		company_abbr = frappe.get_cached_value("Company", company, "abbr")
 
 	if not frappe.db.exists("Account", warehouse_name + " - " + company_abbr):
 		return create_account(
 			account_name=warehouse_name,
 			parent_account=get_group_stock_account(company, company_abbr),
-			account_type='Stock',
-			company=company)
+			account_type="Stock",
+			company=company,
+		)
 	else:
 		return warehouse_name + " - " + company_abbr
 
 
 def get_group_stock_account(company, company_abbr=None):
-	group_stock_account = frappe.db.get_value("Account",
-		filters={'account_type': 'Stock', 'is_group': 1, 'company': company}, fieldname='name')
+	group_stock_account = frappe.db.get_value(
+		"Account", filters={"account_type": "Stock", "is_group": 1, "company": company}, fieldname="name"
+	)
 	if not group_stock_account:
 		if not company_abbr:
-			company_abbr = frappe.get_cached_value("Company", company, 'abbr')
+			company_abbr = frappe.get_cached_value("Company", company, "abbr")
 		group_stock_account = "Current Assets - " + company_abbr
 	return group_stock_account
diff --git a/erpnext/stock/doctype/warehouse/warehouse.js b/erpnext/stock/doctype/warehouse/warehouse.js
index 9243e1e..d69c624 100644
--- a/erpnext/stock/doctype/warehouse/warehouse.js
+++ b/erpnext/stock/doctype/warehouse/warehouse.js
@@ -1,88 +1,97 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-
 frappe.ui.form.on("Warehouse", {
-	onload: function(frm) {
-		frm.set_query("default_in_transit_warehouse", function() {
+	setup: function (frm) {
+		frm.set_query("default_in_transit_warehouse", function (doc) {
 			return {
-				filters:{
-					'warehouse_type' : 'Transit',
-					'is_group': 0,
-					'company': frm.doc.company
-				}
+				filters: {
+					warehouse_type: "Transit",
+					is_group: 0,
+					company: doc.company,
+				},
+			};
+		});
+
+		frm.set_query("parent_warehouse", function () {
+			return {
+				filters: {
+					is_group: 1,
+				},
+			};
+		});
+
+		frm.set_query("account", function (doc) {
+			return {
+				filters: {
+					is_group: 0,
+					account_type: "Stock",
+					company: doc.company,
+				},
 			};
 		});
 	},
 
-	refresh: function(frm) {
-		frm.toggle_display('warehouse_name', frm.doc.__islocal);
-		frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal);
+	refresh: function (frm) {
+		frm.toggle_display("warehouse_name", frm.doc.__islocal);
+		frm.toggle_display(
+			["address_html", "contact_html"],
+			!frm.doc.__islocal
+		);
 
-
-		if(!frm.doc.__islocal) {
+		if (!frm.doc.__islocal) {
 			frappe.contacts.render_address_and_contact(frm);
-
 		} else {
 			frappe.contacts.clear_address_and_contact(frm);
 		}
 
-		frm.add_custom_button(__("Stock Balance"), function() {
-			frappe.set_route("query-report", "Stock Balance", {"warehouse": frm.doc.name});
+		frm.add_custom_button(__("Stock Balance"), function () {
+			frappe.set_route("query-report", "Stock Balance", {
+				warehouse: frm.doc.name,
+			});
 		});
 
-		if (cint(frm.doc.is_group) == 1) {
-			frm.add_custom_button(__('Group to Non-Group'),
-				function() { convert_to_group_or_ledger(frm); }, 'fa fa-retweet', 'btn-default')
-		} else if (cint(frm.doc.is_group) == 0) {
-			if(frm.doc.__onload && frm.doc.__onload.account) {
-				frm.add_custom_button(__("General Ledger"), function() {
+		frm.add_custom_button(
+			frm.doc.is_group
+				? __("Convert to Ledger", null, "Warehouse")
+				: __("Convert to Group", null, "Warehouse"),
+			function () {
+				convert_to_group_or_ledger(frm);
+			},
+		);
+
+		if (!frm.doc.is_group && frm.doc.__onload && frm.doc.__onload.account) {
+			frm.add_custom_button(
+				__("General Ledger", null, "Warehouse"),
+				function () {
 					frappe.route_options = {
-						"account": frm.doc.__onload.account,
-						"company": frm.doc.company
-					}
+						account: frm.doc.__onload.account,
+						company: frm.doc.company,
+					};
 					frappe.set_route("query-report", "General Ledger");
-				});
-			}
-
-			frm.add_custom_button(__('Non-Group to Group'),
-				function() { convert_to_group_or_ledger(frm); }, 'fa fa-retweet', 'btn-default')
-		}
-
-		frm.toggle_enable(['is_group', 'company'], false);
-
-		frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Warehouse'};
-
-		frm.fields_dict['parent_warehouse'].get_query = function(doc) {
-			return {
-				filters: {
-					"is_group": 1,
 				}
-			}
+			);
 		}
 
-		frm.fields_dict['account'].get_query = function(doc) {
-			return {
-				filters: {
-					"is_group": 0,
-					"account_type": "Stock",
-					"company": frm.doc.company
-				}
-			}
-		}
-	}
+		frm.toggle_enable(["is_group", "company"], false);
+
+		frappe.dynamic_link = {
+			doc: frm.doc,
+			fieldname: "name",
+			doctype: "Warehouse",
+		};
+	},
 });
 
-function convert_to_group_or_ledger(frm){
+function convert_to_group_or_ledger(frm) {
 	frappe.call({
-		method:"erpnext.stock.doctype.warehouse.warehouse.convert_to_group_or_ledger",
+		method: "erpnext.stock.doctype.warehouse.warehouse.convert_to_group_or_ledger",
 		args: {
 			docname: frm.doc.name,
-			is_group: frm.doc.is_group
+			is_group: frm.doc.is_group,
 		},
-		callback: function(){
+		callback: function () {
 			frm.refresh();
-		}
-
-	})
+		},
+	});
 }
diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py
index 9cfad86..ab784ca 100644
--- a/erpnext/stock/doctype/warehouse/warehouse.py
+++ b/erpnext/stock/doctype/warehouse/warehouse.py
@@ -14,25 +14,31 @@
 
 
 class Warehouse(NestedSet):
-	nsm_parent_field = 'parent_warehouse'
+	nsm_parent_field = "parent_warehouse"
 
 	def autoname(self):
 		if self.company:
-			suffix = " - " + frappe.get_cached_value('Company',  self.company,  "abbr")
+			suffix = " - " + frappe.get_cached_value("Company", self.company, "abbr")
 			if not self.warehouse_name.endswith(suffix):
 				self.name = self.warehouse_name + suffix
-		else:
-			self.name = self.warehouse_name
+				return
+
+		self.name = self.warehouse_name
 
 	def onload(self):
-		'''load account name for General Ledger Report'''
-		if self.company and cint(frappe.db.get_value("Company", self.company, "enable_perpetual_inventory")):
+		"""load account name for General Ledger Report"""
+		if self.company and cint(
+			frappe.db.get_value("Company", self.company, "enable_perpetual_inventory")
+		):
 			account = self.account or get_warehouse_account(self)
 
 			if account:
-				self.set_onload('account', account)
+				self.set_onload("account", account)
 		load_address_and_contact(self)
 
+	def validate(self):
+		self.warn_about_multiple_warehouse_account()
+
 	def on_update(self):
 		self.update_nsm_model()
 
@@ -41,14 +47,21 @@
 
 	def on_trash(self):
 		# delete bin
-		bins = frappe.db.sql("select * from `tabBin` where warehouse = %s",
-			self.name, as_dict=1)
+		bins = frappe.get_all("Bin", fields="*", filters={"warehouse": self.name})
 		for d in bins:
-			if d['actual_qty'] or d['reserved_qty'] or d['ordered_qty'] or \
-					d['indented_qty'] or d['projected_qty'] or d['planned_qty']:
-				throw(_("Warehouse {0} can not be deleted as quantity exists for Item {1}").format(self.name, d['item_code']))
-			else:
-				frappe.db.sql("delete from `tabBin` where name = %s", d['name'])
+			if (
+				d["actual_qty"]
+				or d["reserved_qty"]
+				or d["ordered_qty"]
+				or d["indented_qty"]
+				or d["projected_qty"]
+				or d["planned_qty"]
+			):
+				throw(
+					_("Warehouse {0} can not be deleted as quantity exists for Item {1}").format(
+						self.name, d["item_code"]
+					)
+				)
 
 		if self.check_if_sle_exists():
 			throw(_("Warehouse can not be deleted as stock ledger entry exists for this warehouse."))
@@ -56,16 +69,62 @@
 		if self.check_if_child_exists():
 			throw(_("Child warehouse exists for this warehouse. You can not delete this warehouse."))
 
+		frappe.db.delete("Bin", filters={"warehouse": self.name})
 		self.update_nsm_model()
 		self.unlink_from_items()
 
+	def warn_about_multiple_warehouse_account(self):
+		"If Warehouse value is split across multiple accounts, warn."
+
+		def get_accounts_where_value_is_booked(name):
+			sle = frappe.qb.DocType("Stock Ledger Entry")
+			gle = frappe.qb.DocType("GL Entry")
+			ac = frappe.qb.DocType("Account")
+
+			return (
+				frappe.qb.from_(sle)
+				.join(gle)
+				.on(sle.voucher_no == gle.voucher_no)
+				.join(ac)
+				.on(ac.name == gle.account)
+				.select(gle.account)
+				.distinct()
+				.where((sle.warehouse == name) & (ac.account_type == "Stock"))
+				.orderby(sle.creation)
+				.run(as_dict=True)
+			)
+
+		if self.is_new():
+			return
+
+		old_wh_account = frappe.db.get_value("Warehouse", self.name, "account")
+
+		# WH account is being changed or set get all accounts against which wh value is booked
+		if self.account != old_wh_account:
+			accounts = get_accounts_where_value_is_booked(self.name)
+			accounts = [d.account for d in accounts]
+
+			if not accounts or (len(accounts) == 1 and self.account in accounts):
+				# if same singular account has stock value booked ignore
+				return
+
+			warning = _("Warehouse's Stock Value has already been booked in the following accounts:")
+			account_str = "<br>" + ", ".join(frappe.bold(ac) for ac in accounts)
+			reason = "<br><br>" + _(
+				"Booking stock value across multiple accounts will make it harder to track stock and account value."
+			)
+
+			frappe.msgprint(
+				warning + account_str + reason,
+				title=_("Multiple Warehouse Accounts"),
+				indicator="orange",
+			)
+
 	def check_if_sle_exists(self):
-		return frappe.db.sql("""select name from `tabStock Ledger Entry`
-			where warehouse = %s limit 1""", self.name)
+		return frappe.db.exists("Stock Ledger Entry", {"warehouse": self.name})
 
 	def check_if_child_exists(self):
-		return frappe.db.sql("""select name from `tabWarehouse`
-			where parent_warehouse = %s limit 1""", self.name)
+		return frappe.db.exists("Warehouse", {"parent_warehouse": self.name})
 
 	def convert_to_group_or_ledger(self):
 		if self.is_group:
@@ -92,28 +151,25 @@
 			return 1
 
 	def unlink_from_items(self):
-		frappe.db.sql("""
-				update `tabItem Default`
-				set default_warehouse=NULL
-				where default_warehouse=%s""", self.name)
+		frappe.db.set_value("Item Default", {"default_warehouse": self.name}, "default_warehouse", None)
+
 
 @frappe.whitelist()
 def get_children(doctype, parent=None, company=None, is_root=False):
 	if is_root:
 		parent = ""
 
-	fields = ['name as value', 'is_group as expandable']
+	fields = ["name as value", "is_group as expandable"]
 	filters = [
-		['docstatus', '<', '2'],
-		['ifnull(`parent_warehouse`, "")', '=', parent],
-		['company', 'in', (company, None,'')]
+		["ifnull(`parent_warehouse`, '')", "=", parent],
+		["company", "in", (company, None, "")],
 	]
 
-	warehouses = frappe.get_list(doctype, fields=fields, filters=filters, order_by='name')
+	warehouses = frappe.get_list(doctype, fields=fields, filters=filters, order_by="name")
 
-	company_currency = ''
+	company_currency = ""
 	if company:
-		company_currency = frappe.get_cached_value('Company', company, 'default_currency')
+		company_currency = frappe.get_cached_value("Company", company, "default_currency")
 
 	warehouse_wise_value = get_warehouse_wise_stock_value(company)
 
@@ -124,14 +180,20 @@
 			wh["company_currency"] = company_currency
 	return warehouses
 
-def get_warehouse_wise_stock_value(company):
-	warehouses = frappe.get_all('Warehouse',
-		fields = ['name', 'parent_warehouse'], filters = {'company': company})
-	parent_warehouse = {d.name : d.parent_warehouse for d in warehouses}
 
-	filters = {'warehouse': ('in', [data.name for data in warehouses])}
-	bin_data = frappe.get_all('Bin', fields = ['sum(stock_value) as stock_value', 'warehouse'],
-		filters = filters, group_by = 'warehouse')
+def get_warehouse_wise_stock_value(company):
+	warehouses = frappe.get_all(
+		"Warehouse", fields=["name", "parent_warehouse"], filters={"company": company}
+	)
+	parent_warehouse = {d.name: d.parent_warehouse for d in warehouses}
+
+	filters = {"warehouse": ("in", [data.name for data in warehouses])}
+	bin_data = frappe.get_all(
+		"Bin",
+		fields=["sum(stock_value) as stock_value", "warehouse"],
+		filters=filters,
+		group_by="warehouse",
+	)
 
 	warehouse_wise_stock_value = defaultdict(float)
 	for row in bin_data:
@@ -139,23 +201,30 @@
 			continue
 
 		warehouse_wise_stock_value[row.warehouse] = row.stock_value
-		update_value_in_parent_warehouse(warehouse_wise_stock_value,
-			parent_warehouse, row.warehouse, row.stock_value)
+		update_value_in_parent_warehouse(
+			warehouse_wise_stock_value, parent_warehouse, row.warehouse, row.stock_value
+		)
 
 	return warehouse_wise_stock_value
 
-def update_value_in_parent_warehouse(warehouse_wise_stock_value, parent_warehouse_dict, warehouse, stock_value):
+
+def update_value_in_parent_warehouse(
+	warehouse_wise_stock_value, parent_warehouse_dict, warehouse, stock_value
+):
 	parent_warehouse = parent_warehouse_dict.get(warehouse)
 	if not parent_warehouse:
 		return
 
 	warehouse_wise_stock_value[parent_warehouse] += flt(stock_value)
-	update_value_in_parent_warehouse(warehouse_wise_stock_value, parent_warehouse_dict,
-		parent_warehouse, stock_value)
+	update_value_in_parent_warehouse(
+		warehouse_wise_stock_value, parent_warehouse_dict, parent_warehouse, stock_value
+	)
+
 
 @frappe.whitelist()
 def add_node():
 	from frappe.desk.treeview import make_tree_args
+
 	args = make_tree_args(**frappe.form_dict)
 
 	if cint(args.is_root):
@@ -163,32 +232,37 @@
 
 	frappe.get_doc(args).insert()
 
+
 @frappe.whitelist()
-def convert_to_group_or_ledger():
-	args = frappe.form_dict
-	return frappe.get_doc("Warehouse", args.docname).convert_to_group_or_ledger()
+def convert_to_group_or_ledger(docname=None):
+	if not docname:
+		docname = frappe.form_dict.docname
+	return frappe.get_doc("Warehouse", docname).convert_to_group_or_ledger()
+
 
 def get_child_warehouses(warehouse):
-	lft, rgt = frappe.get_cached_value("Warehouse", warehouse, ["lft", "rgt"])
+	from frappe.utils.nestedset import get_descendants_of
 
-	return frappe.db.sql_list("""select name from `tabWarehouse`
-		where lft >= %s and rgt <= %s""", (lft, rgt))
+	children = get_descendants_of("Warehouse", warehouse, ignore_permissions=True, order_by="lft")
+	return children + [warehouse]  # append self for backward compatibility
+
 
 def get_warehouses_based_on_account(account, company=None):
 	warehouses = []
-	for d in frappe.get_all("Warehouse", fields = ["name", "is_group"],
-		filters = {"account": account}):
+	for d in frappe.get_all("Warehouse", fields=["name", "is_group"], filters={"account": account}):
 		if d.is_group:
 			warehouses.extend(get_child_warehouses(d.name))
 		else:
 			warehouses.append(d.name)
 
-	if (not warehouses and company and
-		frappe.get_cached_value("Company", company, "default_inventory_account") == account):
-		warehouses = [d.name for d in frappe.get_all("Warehouse", filters={'is_group': 0})]
+	if (
+		not warehouses
+		and company
+		and frappe.get_cached_value("Company", company, "default_inventory_account") == account
+	):
+		warehouses = [d.name for d in frappe.get_all("Warehouse", filters={"is_group": 0})]
 
 	if not warehouses:
-		frappe.throw(_("Warehouse not found against the account {0}")
-			.format(account))
+		frappe.throw(_("Warehouse not found against the account {0}").format(account))
 
 	return warehouses
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 9bec5f7..38ad662 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -23,31 +23,38 @@
 from erpnext.stock.doctype.item_manufacturer.item_manufacturer import get_item_manufacturer_part_no
 from erpnext.stock.doctype.price_list.price_list import get_price_list_details
 
-sales_doctypes = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice', 'POS Invoice']
-purchase_doctypes = ['Material Request', 'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
+sales_doctypes = ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"]
+purchase_doctypes = [
+	"Material Request",
+	"Supplier Quotation",
+	"Purchase Order",
+	"Purchase Receipt",
+	"Purchase Invoice",
+]
+
 
 @frappe.whitelist()
 def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=True):
 	"""
-		args = {
-			"item_code": "",
-			"warehouse": None,
-			"customer": "",
-			"conversion_rate": 1.0,
-			"selling_price_list": None,
-			"price_list_currency": None,
-			"plc_conversion_rate": 1.0,
-			"doctype": "",
-			"name": "",
-			"supplier": None,
-			"transaction_date": None,
-			"conversion_rate": 1.0,
-			"buying_price_list": None,
-			"is_subcontracted": "Yes" / "No",
-			"ignore_pricing_rule": 0/1
-			"project": ""
-			"set_warehouse": ""
-		}
+	args = {
+	        "item_code": "",
+	        "warehouse": None,
+	        "customer": "",
+	        "conversion_rate": 1.0,
+	        "selling_price_list": None,
+	        "price_list_currency": None,
+	        "plc_conversion_rate": 1.0,
+	        "doctype": "",
+	        "name": "",
+	        "supplier": None,
+	        "transaction_date": None,
+	        "conversion_rate": 1.0,
+	        "buying_price_list": None,
+	        "is_subcontracted": 0/1,
+	        "ignore_pricing_rule": 0/1
+	        "project": ""
+	        "set_warehouse": ""
+	}
 	"""
 
 	args = process_args(args)
@@ -56,21 +63,24 @@
 	item = frappe.get_cached_doc("Item", args.item_code)
 	validate_item_details(args, item)
 
-	out = get_basic_details(args, item, overwrite_warehouse)
-
 	if isinstance(doc, str):
 		doc = json.loads(doc)
 
-	if doc and doc.get('doctype') == 'Purchase Invoice':
-		args['bill_date'] = doc.get('bill_date')
-
 	if doc:
-		args['posting_date'] = doc.get('posting_date')
-		args['transaction_date'] = doc.get('transaction_date')
+		args["transaction_date"] = doc.get("transaction_date") or doc.get("posting_date")
 
+		if doc.get("doctype") == "Purchase Invoice":
+			args["bill_date"] = doc.get("bill_date")
+
+	out = get_basic_details(args, item, overwrite_warehouse)
 	get_item_tax_template(args, item, out)
-	out["item_tax_rate"] = get_item_tax_map(args.company, args.get("item_tax_template") if out.get("item_tax_template") is None \
-		else out.get("item_tax_template"), as_json=True)
+	out["item_tax_rate"] = get_item_tax_map(
+		args.company,
+		args.get("item_tax_template")
+		if out.get("item_tax_template") is None
+		else out.get("item_tax_template"),
+		as_json=True,
+	)
 
 	get_party_item_code(args, item, out)
 
@@ -83,12 +93,14 @@
 	if args.customer and cint(args.is_pos):
 		out.update(get_pos_profile_item_details(args.company, args, update_data=True))
 
-	if (args.get("doctype") == "Material Request" and
-		args.get("material_request_type") == "Material Transfer"):
+	if (
+		args.get("doctype") == "Material Request"
+		and args.get("material_request_type") == "Material Transfer"
+	):
 		out.update(get_bin_details(args.item_code, args.get("from_warehouse")))
 
 	elif out.get("warehouse"):
-		if doc and doc.get('doctype') == 'Purchase Order':
+		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:
@@ -101,28 +113,27 @@
 		if args.get(key) is None:
 			args[key] = value
 
-	data = get_pricing_rule_for_item(args, out.price_list_rate,
-		doc, for_validate=for_validate)
+	data = get_pricing_rule_for_item(args, out.price_list_rate, doc, for_validate=for_validate)
 
 	out.update(data)
 
 	update_stock(args, out)
 
 	if args.transaction_date and item.lead_time_days:
-		out.schedule_date = out.lead_time_date = add_days(args.transaction_date,
-			item.lead_time_days)
+		out.schedule_date = out.lead_time_date = add_days(args.transaction_date, item.lead_time_days)
 
-	if args.get("is_subcontracted") == "Yes":
-		out.bom = args.get('bom') or get_default_bom(args.item_code)
+	if args.get("is_subcontracted"):
+		out.bom = args.get("bom") or get_default_bom(args.item_code)
 
 	get_gross_profit(out)
-	if args.doctype == 'Material Request':
+	if args.doctype == "Material Request":
 		out.rate = args.rate or out.price_list_rate
 		out.amount = flt(args.qty) * flt(out.rate)
 
 	out = remove_standard_fields(out)
 	return out
 
+
 def remove_standard_fields(details):
 	for key in child_table_fields + default_fields:
 		details.pop(key, None)
@@ -130,9 +141,14 @@
 
 
 def update_stock(args, out):
-	if (args.get("doctype") == "Delivery Note" or
-		(args.get("doctype") == "Sales Invoice" and args.get('update_stock'))) \
-		and out.warehouse and out.stock_qty > 0:
+	if (
+		(
+			args.get("doctype") == "Delivery Note"
+			or (args.get("doctype") == "Sales Invoice" and args.get("update_stock"))
+		)
+		and out.warehouse
+		and out.stock_qty > 0
+	):
 
 		if out.has_batch_no and not args.get("batch_no"):
 			out.batch_no = get_batch_no(out.item_code, out.warehouse, out.qty)
@@ -140,15 +156,18 @@
 			if actual_batch_qty:
 				out.update(actual_batch_qty)
 
-		if out.has_serial_no and args.get('batch_no'):
+		if out.has_serial_no and args.get("batch_no"):
 			reserved_so = get_so_reservation_for_item(args)
-			out.batch_no = args.get('batch_no')
+			out.batch_no = args.get("batch_no")
 			out.serial_no = get_serial_no(out, args.serial_no, sales_order=reserved_so)
 
 		elif out.has_serial_no:
 			reserved_so = get_so_reservation_for_item(args)
 			out.serial_no = get_serial_no(out, args.serial_no, sales_order=reserved_so)
 
+	if not out.serial_no:
+		out.pop("serial_no", None)
+
 
 def set_valuation_rate(out, args):
 	if frappe.db.exists("Product Bundle", args.item_code, cache=True):
@@ -156,13 +175,14 @@
 		bundled_items = frappe.get_doc("Product Bundle", args.item_code)
 
 		for bundle_item in bundled_items.items:
-			valuation_rate += \
-				flt(get_valuation_rate(bundle_item.item_code, args.company, out.get("warehouse")).get("valuation_rate") \
-					* bundle_item.qty)
+			valuation_rate += flt(
+				get_valuation_rate(bundle_item.item_code, args.company, out.get("warehouse")).get(
+					"valuation_rate"
+				)
+				* bundle_item.qty
+			)
 
-		out.update({
-			"valuation_rate": valuation_rate
-		})
+		out.update({"valuation_rate": valuation_rate})
 
 	else:
 		out.update(get_valuation_rate(args.item_code, args.company, out.get("warehouse")))
@@ -177,7 +197,7 @@
 	if not args.get("price_list"):
 		args.price_list = args.get("selling_price_list") or args.get("buying_price_list")
 
-	if args.barcode:
+	if not args.item_code and args.barcode:
 		args.item_code = get_item_code(barcode=args.barcode)
 	elif not args.item_code and args.serial_no:
 		args.item_code = get_item_code(serial_no=args.serial_no)
@@ -185,11 +205,13 @@
 	set_transaction_type(args)
 	return args
 
+
 def process_string_args(args):
 	if isinstance(args, str):
 		args = json.loads(args)
 	return args
 
+
 @frappe.whitelist()
 def get_item_code(barcode=None, serial_no=None):
 	if barcode:
@@ -209,50 +231,51 @@
 		throw(_("Please specify Company"))
 
 	from erpnext.stock.doctype.item.item import validate_end_of_life
+
 	validate_end_of_life(item.name, item.end_of_life, item.disabled)
 
 	if args.transaction_type == "selling" and cint(item.has_variants):
 		throw(_("Item {0} is a template, please select one of its variants").format(item.name))
 
 	elif args.transaction_type == "buying" and args.doctype != "Material Request":
-		if args.get("is_subcontracted") == "Yes" and item.is_sub_contracted_item != 1:
+		if args.get("is_subcontracted") and item.is_sub_contracted_item != 1:
 			throw(_("Item {0} must be a Sub-contracted Item").format(item.name))
 
 
 def get_basic_details(args, item, overwrite_warehouse=True):
 	"""
 	:param args: {
-			"item_code": "",
-			"warehouse": None,
-			"customer": "",
-			"conversion_rate": 1.0,
-			"selling_price_list": None,
-			"price_list_currency": None,
-			"price_list_uom_dependant": None,
-			"plc_conversion_rate": 1.0,
-			"doctype": "",
-			"name": "",
-			"supplier": None,
-			"transaction_date": None,
-			"conversion_rate": 1.0,
-			"buying_price_list": None,
-			"is_subcontracted": "Yes" / "No",
-			"ignore_pricing_rule": 0/1
-			"project": "",
-			barcode: "",
-			serial_no: "",
-			currency: "",
-			update_stock: "",
-			price_list: "",
-			company: "",
-			order_type: "",
-			is_pos: "",
-			project: "",
-			qty: "",
-			stock_qty: "",
-			conversion_factor: "",
-			against_blanket_order: 0/1
-		}
+	                "item_code": "",
+	                "warehouse": None,
+	                "customer": "",
+	                "conversion_rate": 1.0,
+	                "selling_price_list": None,
+	                "price_list_currency": None,
+	                "price_list_uom_dependant": None,
+	                "plc_conversion_rate": 1.0,
+	                "doctype": "",
+	                "name": "",
+	                "supplier": None,
+	                "transaction_date": None,
+	                "conversion_rate": 1.0,
+	                "buying_price_list": None,
+	                "is_subcontracted": 0/1,
+	                "ignore_pricing_rule": 0/1
+	                "project": "",
+	                barcode: "",
+	                serial_no: "",
+	                currency: "",
+	                update_stock: "",
+	                price_list: "",
+	                company: "",
+	                order_type: "",
+	                is_pos: "",
+	                project: "",
+	                qty: "",
+	                stock_qty: "",
+	                conversion_factor: "",
+	                against_blanket_order: 0/1
+	        }
 	:param item: `item_code` of Item object
 	:return: frappe._dict
 	"""
@@ -267,77 +290,100 @@
 	item_group_defaults = get_item_group_defaults(item.name, args.company)
 	brand_defaults = get_brand_defaults(item.name, args.company)
 
-	defaults = frappe._dict({
-		'item_defaults': item_defaults,
-		'item_group_defaults': item_group_defaults,
-		'brand_defaults': brand_defaults
-	})
+	defaults = frappe._dict(
+		{
+			"item_defaults": item_defaults,
+			"item_group_defaults": item_group_defaults,
+			"brand_defaults": brand_defaults,
+		}
+	)
 
 	warehouse = get_item_warehouse(item, args, overwrite_warehouse, defaults)
 
-	if args.get('doctype') == "Material Request" and not args.get('material_request_type'):
-		args['material_request_type'] = frappe.db.get_value('Material Request',
-			args.get('name'), 'material_request_type', cache=True)
+	if args.get("doctype") == "Material Request" and not args.get("material_request_type"):
+		args["material_request_type"] = frappe.db.get_value(
+			"Material Request", args.get("name"), "material_request_type", cache=True
+		)
 
 	expense_account = None
 
-	if args.get('doctype') == 'Purchase Invoice' and item.is_fixed_asset:
+	if args.get("doctype") == "Purchase Invoice" and item.is_fixed_asset:
 		from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
-		expense_account = get_asset_category_account(fieldname = "fixed_asset_account", item = args.item_code, company= args.company)
 
-	#Set the UOM to the Default Sales UOM or Default Purchase UOM if configured in the Item Master
-	if not args.get('uom'):
-		if args.get('doctype') in sales_doctypes:
+		expense_account = get_asset_category_account(
+			fieldname="fixed_asset_account", item=args.item_code, company=args.company
+		)
+
+	# Set the UOM to the Default Sales UOM or Default Purchase UOM if configured in the Item Master
+	if not args.get("uom"):
+		if args.get("doctype") in sales_doctypes:
 			args.uom = item.sales_uom if item.sales_uom else item.stock_uom
-		elif (args.get('doctype') in ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']) or \
-			(args.get('doctype') == 'Material Request' and args.get('material_request_type') == 'Purchase'):
+		elif (args.get("doctype") in ["Purchase Order", "Purchase Receipt", "Purchase Invoice"]) or (
+			args.get("doctype") == "Material Request" and args.get("material_request_type") == "Purchase"
+		):
 			args.uom = item.purchase_uom if item.purchase_uom else item.stock_uom
 		else:
 			args.uom = item.stock_uom
 
-	if (args.get("batch_no") and
-		item.name != frappe.get_cached_value('Batch', args.get("batch_no"), 'item')):
-		args['batch_no'] = ''
+	if args.get("batch_no") and item.name != frappe.get_cached_value(
+		"Batch", args.get("batch_no"), "item"
+	):
+		args["batch_no"] = ""
 
-	out = frappe._dict({
-		"item_code": item.name,
-		"item_name": item.item_name,
-		"description": cstr(item.description).strip(),
-		"image": cstr(item.image).strip(),
-		"warehouse": warehouse,
-		"income_account": get_default_income_account(args, item_defaults, item_group_defaults, brand_defaults),
-		"expense_account": expense_account or get_default_expense_account(args, item_defaults, item_group_defaults, brand_defaults) ,
-		"discount_account": get_default_discount_account(args, item_defaults),
-		"cost_center": get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults),
-		'has_serial_no': item.has_serial_no,
-		'has_batch_no': item.has_batch_no,
-		"batch_no": args.get("batch_no"),
-		"uom": args.uom,
-		"min_order_qty": flt(item.min_order_qty) if args.doctype == "Material Request" else "",
-		"qty": flt(args.qty) or 1.0,
-		"stock_qty": flt(args.qty) or 1.0,
-		"price_list_rate": 0.0,
-		"base_price_list_rate": 0.0,
-		"rate": 0.0,
-		"base_rate": 0.0,
-		"amount": 0.0,
-		"base_amount": 0.0,
-		"net_rate": 0.0,
-		"net_amount": 0.0,
-		"discount_percentage": 0.0,
-		"discount_amount": 0.0,
-		"supplier": get_default_supplier(args, item_defaults, item_group_defaults, brand_defaults),
-		"update_stock": args.get("update_stock") if args.get('doctype') in ['Sales Invoice', 'Purchase Invoice'] else 0,
-		"delivered_by_supplier": item.delivered_by_supplier if args.get("doctype") in ["Sales Order", "Sales Invoice"] else 0,
-		"is_fixed_asset": item.is_fixed_asset,
-		"last_purchase_rate": item.last_purchase_rate if args.get("doctype") in ["Purchase Order"] else 0,
-		"transaction_date": args.get("transaction_date"),
-		"against_blanket_order": args.get("against_blanket_order"),
-		"bom_no": item.get("default_bom"),
-		"weight_per_unit": args.get("weight_per_unit") or item.get("weight_per_unit"),
-		"weight_uom": args.get("weight_uom") or item.get("weight_uom"),
-		"grant_commission": item.get("grant_commission")
-	})
+	out = frappe._dict(
+		{
+			"item_code": item.name,
+			"item_name": item.item_name,
+			"description": cstr(item.description).strip(),
+			"image": cstr(item.image).strip(),
+			"warehouse": warehouse,
+			"income_account": get_default_income_account(
+				args, item_defaults, item_group_defaults, brand_defaults
+			),
+			"expense_account": expense_account
+			or get_default_expense_account(args, item_defaults, item_group_defaults, brand_defaults),
+			"discount_account": get_default_discount_account(args, item_defaults),
+			"provisional_expense_account": get_provisional_account(args, item_defaults),
+			"cost_center": get_default_cost_center(
+				args, item_defaults, item_group_defaults, brand_defaults
+			),
+			"has_serial_no": item.has_serial_no,
+			"has_batch_no": item.has_batch_no,
+			"batch_no": args.get("batch_no"),
+			"uom": args.uom,
+			"stock_uom": item.stock_uom,
+			"min_order_qty": flt(item.min_order_qty) if args.doctype == "Material Request" else "",
+			"qty": flt(args.qty) or 1.0,
+			"stock_qty": flt(args.qty) or 1.0,
+			"price_list_rate": 0.0,
+			"base_price_list_rate": 0.0,
+			"rate": 0.0,
+			"base_rate": 0.0,
+			"amount": 0.0,
+			"base_amount": 0.0,
+			"net_rate": 0.0,
+			"net_amount": 0.0,
+			"discount_percentage": 0.0,
+			"discount_amount": flt(args.discount_amount) or 0.0,
+			"supplier": get_default_supplier(args, item_defaults, item_group_defaults, brand_defaults),
+			"update_stock": args.get("update_stock")
+			if args.get("doctype") in ["Sales Invoice", "Purchase Invoice"]
+			else 0,
+			"delivered_by_supplier": item.delivered_by_supplier
+			if args.get("doctype") in ["Sales Order", "Sales Invoice"]
+			else 0,
+			"is_fixed_asset": item.is_fixed_asset,
+			"last_purchase_rate": item.last_purchase_rate
+			if args.get("doctype") in ["Purchase Order"]
+			else 0,
+			"transaction_date": args.get("transaction_date"),
+			"against_blanket_order": args.get("against_blanket_order"),
+			"bom_no": item.get("default_bom"),
+			"weight_per_unit": args.get("weight_per_unit") or item.get("weight_per_unit"),
+			"weight_uom": args.get("weight_uom") or item.get("weight_uom"),
+			"grant_commission": item.get("grant_commission"),
+		}
+	)
 
 	if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"):
 		out.update(calculate_service_end_date(args, item))
@@ -346,28 +392,33 @@
 	if item.stock_uom == args.uom:
 		out.conversion_factor = 1.0
 	else:
-		out.conversion_factor = args.conversion_factor or \
-			get_conversion_factor(item.name, args.uom).get("conversion_factor")
+		out.conversion_factor = args.conversion_factor or get_conversion_factor(item.name, args.uom).get(
+			"conversion_factor"
+		)
 
 	args.conversion_factor = out.conversion_factor
 	out.stock_qty = out.qty * out.conversion_factor
 	args.stock_qty = out.stock_qty
 
 	# calculate last purchase rate
-	if args.get('doctype') in purchase_doctypes:
+	if args.get("doctype") in purchase_doctypes:
 		from erpnext.buying.doctype.purchase_order.purchase_order import item_last_purchase_rate
-		out.last_purchase_rate = item_last_purchase_rate(args.name, args.conversion_rate, item.name, out.conversion_factor)
+
+		out.last_purchase_rate = item_last_purchase_rate(
+			args.name, args.conversion_rate, item.name, out.conversion_factor
+		)
 
 	# if default specified in item is for another company, fetch from company
 	for d in [
 		["Account", "income_account", "default_income_account"],
 		["Account", "expense_account", "default_expense_account"],
 		["Cost Center", "cost_center", "cost_center"],
-		["Warehouse", "warehouse", ""]]:
-			if not out[d[1]]:
-				out[d[1]] = frappe.get_cached_value('Company',  args.company,  d[2]) if d[2] else None
+		["Warehouse", "warehouse", ""],
+	]:
+		if not out[d[1]]:
+			out[d[1]] = frappe.get_cached_value("Company", args.company, d[2]) if d[2] else None
 
-	for fieldname in ("item_name", "item_group", "barcodes", "brand", "stock_uom"):
+	for fieldname in ("item_name", "item_group", "brand", "stock_uom"):
 		out[fieldname] = item.get(fieldname)
 
 	if args.get("manufacturer"):
@@ -378,53 +429,58 @@
 			out["manufacturer_part_no"] = None
 			out["manufacturer"] = None
 	else:
-		data = frappe.get_value("Item", item.name,
-			["default_item_manufacturer", "default_manufacturer_part_no"] , as_dict=1)
+		data = frappe.get_value(
+			"Item", item.name, ["default_item_manufacturer", "default_manufacturer_part_no"], as_dict=1
+		)
 
 		if data:
-			out.update({
-				"manufacturer": data.default_item_manufacturer,
-				"manufacturer_part_no": data.default_manufacturer_part_no
-			})
+			out.update(
+				{
+					"manufacturer": data.default_item_manufacturer,
+					"manufacturer_part_no": data.default_manufacturer_part_no,
+				}
+			)
 
-	child_doctype = args.doctype + ' Item'
+	child_doctype = args.doctype + " Item"
 	meta = frappe.get_meta(child_doctype)
 	if meta.get_field("barcode"):
 		update_barcode_value(out)
 
 	if out.get("weight_per_unit"):
-		out['total_weight'] = out.weight_per_unit * out.stock_qty
+		out["total_weight"] = out.weight_per_unit * out.stock_qty
 
 	return out
 
+
 def get_item_warehouse(item, args, overwrite_warehouse, defaults=None):
 	if not defaults:
-		defaults = frappe._dict({
-			'item_defaults' : get_item_defaults(item.name, args.company),
-			'item_group_defaults' : get_item_group_defaults(item.name, args.company),
-			'brand_defaults' : get_brand_defaults(item.name, args.company)
-		})
+		defaults = frappe._dict(
+			{
+				"item_defaults": get_item_defaults(item.name, args.company),
+				"item_group_defaults": get_item_group_defaults(item.name, args.company),
+				"brand_defaults": get_brand_defaults(item.name, args.company),
+			}
+		)
 
 	if overwrite_warehouse or not args.warehouse:
 		warehouse = (
-			args.get("set_warehouse") or
-			defaults.item_defaults.get("default_warehouse") or
-			defaults.item_group_defaults.get("default_warehouse") or
-			defaults.brand_defaults.get("default_warehouse") or
-			args.get('warehouse')
+			args.get("set_warehouse")
+			or defaults.item_defaults.get("default_warehouse")
+			or defaults.item_group_defaults.get("default_warehouse")
+			or defaults.brand_defaults.get("default_warehouse")
+			or args.get("warehouse")
 		)
 
 		if not warehouse:
 			defaults = frappe.defaults.get_defaults() or {}
-			warehouse_exists = frappe.db.exists("Warehouse", {
-				'name': defaults.default_warehouse,
-				'company': args.company
-			})
+			warehouse_exists = frappe.db.exists(
+				"Warehouse", {"name": defaults.default_warehouse, "company": args.company}
+			)
 			if defaults.get("default_warehouse") and warehouse_exists:
 				warehouse = defaults.default_warehouse
 
 	else:
-		warehouse = args.get('warehouse')
+		warehouse = args.get("warehouse")
 
 	if not warehouse:
 		default_warehouse = frappe.db.get_single_value("Stock Settings", "default_warehouse")
@@ -433,12 +489,14 @@
 
 	return warehouse
 
+
 def update_barcode_value(out):
 	barcode_data = get_barcode_data([out])
 
 	# If item has one barcode then update the value of the barcode field
 	if barcode_data and len(barcode_data.get(out.item_code)) == 1:
-		out['barcode'] = barcode_data.get(out.item_code)[0]
+		out["barcode"] = barcode_data.get(out.item_code)[0]
+
 
 def get_barcode_data(items_list):
 	# get itemwise batch no data
@@ -447,9 +505,13 @@
 
 	itemwise_barcode = {}
 	for item in items_list:
-		barcodes = frappe.db.sql("""
+		barcodes = frappe.db.sql(
+			"""
 			select barcode from `tabItem Barcode` where parent = %s
-		""", item.item_code, as_dict=1)
+		""",
+			item.item_code,
+			as_dict=1,
+		)
 
 		for barcode in barcodes:
 			if item.item_code not in itemwise_barcode:
@@ -458,6 +520,7 @@
 
 	return itemwise_barcode
 
+
 @frappe.whitelist()
 def get_item_tax_info(company, tax_category, item_codes, item_rates=None, item_tax_templates=None):
 	out = {}
@@ -483,22 +546,29 @@
 
 		out[item_code[1]] = {}
 		item = frappe.get_cached_doc("Item", item_code[0])
-		args = {"company": company, "tax_category": tax_category, "net_rate": item_rates.get(item_code[1])}
+		args = {
+			"company": company,
+			"tax_category": tax_category,
+			"net_rate": item_rates.get(item_code[1]),
+		}
 
 		if item_tax_templates:
 			args.update({"item_tax_template": item_tax_templates.get(item_code[1])})
 
 		get_item_tax_template(args, item, out[item_code[1]])
-		out[item_code[1]]["item_tax_rate"] = get_item_tax_map(company, out[item_code[1]].get("item_tax_template"), as_json=True)
+		out[item_code[1]]["item_tax_rate"] = get_item_tax_map(
+			company, out[item_code[1]].get("item_tax_template"), as_json=True
+		)
 
 	return out
 
+
 def get_item_tax_template(args, item, out):
 	"""
-		args = {
-			"tax_category": None
-			"item_tax_template": None
-		}
+	args = {
+	        "tax_category": None
+	        "item_tax_template": None
+	}
 	"""
 	item_tax_template = None
 	if item.taxes:
@@ -511,6 +581,7 @@
 			item_tax_template = _get_item_tax_template(args, item_group_doc.taxes, out)
 			item_group = item_group_doc.parent_item_group
 
+
 def _get_item_tax_template(args, taxes, out=None, for_validate=False):
 	if out is None:
 		out = {}
@@ -518,36 +589,41 @@
 	taxes_with_no_validity = []
 
 	for tax in taxes:
-		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):
+		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')
+				validation_date = args.get("bill_date") or args.get("transaction_date")
 
-				if getdate(tax.valid_from) <= getdate(validation_date) \
-					and is_within_valid_range(args, tax):
+				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:
-		taxes = sorted(taxes_with_validity, key = lambda i: i.valid_from, reverse=True)
+		taxes = sorted(taxes_with_validity, key=lambda i: i.valid_from, reverse=True)
 	else:
 		taxes = taxes_with_no_validity
 
 	if for_validate:
-		return [tax.item_tax_template for tax in taxes if (cstr(tax.tax_category) == cstr(args.get('tax_category')) \
-			and (tax.item_tax_template not in taxes))]
+		return [
+			tax.item_tax_template
+			for tax in taxes
+			if (
+				cstr(tax.tax_category) == cstr(args.get("tax_category"))
+				and (tax.item_tax_template not in taxes)
+			)
+		]
 
 	# all templates have validity and no template is valid
 	if not taxes_with_validity and (not taxes_with_no_validity):
 		return None
 
 	# do not change if already a valid template
-	if args.get('item_tax_template') in {t.item_tax_template for t in taxes}:
-		out["item_tax_template"] = args.get('item_tax_template')
-		return args.get('item_tax_template')
+	if args.get("item_tax_template") in {t.item_tax_template for t in taxes}:
+		out["item_tax_template"] = args.get("item_tax_template")
+		return args.get("item_tax_template")
 
 	for tax in taxes:
 		if cstr(tax.tax_category) == cstr(args.get("tax_category")):
@@ -555,15 +631,17 @@
 			return tax.item_tax_template
 	return None
 
+
 def is_within_valid_range(args, tax):
 	if not flt(tax.maximum_net_rate):
 		# No range specified, just ignore
 		return True
-	elif flt(tax.minimum_net_rate) <= flt(args.get('net_rate')) <= flt(tax.maximum_net_rate):
+	elif flt(tax.minimum_net_rate) <= flt(args.get("net_rate")) <= flt(tax.maximum_net_rate):
 		return True
 
 	return False
 
+
 @frappe.whitelist()
 def get_item_tax_map(company, item_tax_template, as_json=True):
 	item_tax_map = {}
@@ -575,6 +653,7 @@
 
 	return json.dumps(item_tax_map) if as_json else item_tax_map
 
+
 @frappe.whitelist()
 def calculate_service_end_date(args, item=None):
 	args = process_args(args)
@@ -593,53 +672,72 @@
 
 	service_start_date = args.service_start_date if args.service_start_date else args.transaction_date
 	service_end_date = add_months(service_start_date, item.get(no_of_months))
-	deferred_detail = {
-		"service_start_date": service_start_date,
-		"service_end_date": service_end_date
-	}
+	deferred_detail = {"service_start_date": service_start_date, "service_end_date": service_end_date}
 	deferred_detail[enable_deferred] = item.get(enable_deferred)
 	deferred_detail[account] = get_default_deferred_account(args, item, fieldname=account)
 
 	return deferred_detail
 
+
 def get_default_income_account(args, item, item_group, brand):
-	return (item.get("income_account")
+	return (
+		item.get("income_account")
 		or item_group.get("income_account")
 		or brand.get("income_account")
-		or args.income_account)
+		or args.income_account
+	)
+
 
 def get_default_expense_account(args, item, item_group, brand):
-	return (item.get("expense_account")
+	return (
+		item.get("expense_account")
 		or item_group.get("expense_account")
 		or brand.get("expense_account")
-		or args.expense_account)
+		or args.expense_account
+	)
+
+
+def get_provisional_account(args, item):
+	return item.get("default_provisional_account") or args.default_provisional_account
+
 
 def get_default_discount_account(args, item):
-	return (item.get("default_discount_account")
-		or args.discount_account)
+	return item.get("default_discount_account") or args.discount_account
+
 
 def get_default_deferred_account(args, item, fieldname=None):
 	if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"):
-		return (item.get(fieldname)
+		return (
+			item.get(fieldname)
 			or args.get(fieldname)
-			or frappe.get_cached_value('Company',  args.company,  "default_"+fieldname))
+			or frappe.get_cached_value("Company", args.company, "default_" + fieldname)
+		)
 	else:
 		return None
 
+
 def get_default_cost_center(args, item=None, item_group=None, brand=None, company=None):
 	cost_center = None
 
 	if not company and args.get("company"):
 		company = args.get("company")
 
-	if args.get('project'):
+	if args.get("project"):
 		cost_center = frappe.db.get_value("Project", args.get("project"), "cost_center", cache=True)
 
 	if not cost_center and (item and item_group and brand):
-		if args.get('customer'):
-			cost_center = item.get('selling_cost_center') or item_group.get('selling_cost_center') or brand.get('selling_cost_center')
+		if args.get("customer"):
+			cost_center = (
+				item.get("selling_cost_center")
+				or item_group.get("selling_cost_center")
+				or brand.get("selling_cost_center")
+			)
 		else:
-			cost_center = item.get('buying_cost_center') or item_group.get('buying_cost_center') or brand.get('buying_cost_center')
+			cost_center = (
+				item.get("buying_cost_center")
+				or item_group.get("buying_cost_center")
+				or brand.get("buying_cost_center")
+			)
 
 	elif not cost_center and args.get("item_code") and company:
 		for method in ["get_item_defaults", "get_item_group_defaults", "get_brand_defaults"]:
@@ -652,20 +750,26 @@
 	if not cost_center and args.get("cost_center"):
 		cost_center = args.get("cost_center")
 
-	if (company and cost_center
-		and frappe.get_cached_value("Cost Center", cost_center, "company") != company):
+	if (
+		company
+		and cost_center
+		and frappe.get_cached_value("Cost Center", cost_center, "company") != company
+	):
 		return None
 
 	if not cost_center and company:
-		cost_center = frappe.get_cached_value("Company",
-			company, "cost_center")
+		cost_center = frappe.get_cached_value("Company", company, "cost_center")
 
 	return cost_center
 
+
 def get_default_supplier(args, item, item_group, brand):
-	return (item.get("default_supplier")
+	return (
+		item.get("default_supplier")
 		or item_group.get("default_supplier")
-		or brand.get("default_supplier"))
+		or brand.get("default_supplier")
+	)
+
 
 def get_price_list_rate(args, item_doc, out=None):
 	if out is None:
@@ -673,7 +777,7 @@
 
 	meta = frappe.get_meta(args.parenttype or args.doctype)
 
-	if meta.get_field("currency") or args.get('currency'):
+	if meta.get_field("currency") or args.get("currency"):
 		if not args.get("price_list_currency") or not args.get("plc_conversion_rate"):
 			# if currency and plc_conversion_rate exist then
 			# `get_price_list_currency_and_exchange_rate` has already been called
@@ -695,54 +799,75 @@
 				insert_item_price(args)
 			return out
 
-		out.price_list_rate = flt(price_list_rate) * flt(args.plc_conversion_rate) \
-			/ flt(args.conversion_rate)
+		out.price_list_rate = (
+			flt(price_list_rate) * flt(args.plc_conversion_rate) / flt(args.conversion_rate)
+		)
 
-		if not out.price_list_rate and args.transaction_type=="buying":
+		if not out.price_list_rate and args.transaction_type == "buying":
 			from erpnext.stock.doctype.item.item import get_last_purchase_details
-			out.update(get_last_purchase_details(item_doc.name,
-				args.name, args.conversion_rate))
+
+			out.update(get_last_purchase_details(item_doc.name, args.name, args.conversion_rate))
 
 	return out
 
+
 def insert_item_price(args):
 	"""Insert Item Price if Price List and Price List Rate are specified and currency is the same"""
-	if frappe.db.get_value("Price List", args.price_list, "currency", cache=True) == args.currency \
-		and cint(frappe.db.get_single_value("Stock Settings", "auto_insert_price_list_rate_if_missing")):
+	if frappe.db.get_value(
+		"Price List", args.price_list, "currency", cache=True
+	) == args.currency and cint(
+		frappe.db.get_single_value("Stock Settings", "auto_insert_price_list_rate_if_missing")
+	):
 		if frappe.has_permission("Item Price", "write"):
-			price_list_rate = (args.rate / args.get('conversion_factor')
-				if args.get("conversion_factor") else args.rate)
+			price_list_rate = (
+				(args.rate + args.discount_amount) / args.get("conversion_factor")
+				if args.get("conversion_factor")
+				else (args.rate + args.discount_amount)
+			)
 
-			item_price = frappe.db.get_value('Item Price',
-				{'item_code': args.item_code, 'price_list': args.price_list, 'currency': args.currency},
-				['name', 'price_list_rate'], as_dict=1)
+			item_price = frappe.db.get_value(
+				"Item Price",
+				{"item_code": args.item_code, "price_list": args.price_list, "currency": args.currency},
+				["name", "price_list_rate"],
+				as_dict=1,
+			)
 			if item_price and item_price.name:
-				if item_price.price_list_rate != price_list_rate and frappe.db.get_single_value('Stock Settings', 'update_existing_price_list_rate'):
-					frappe.db.set_value('Item Price', item_price.name, "price_list_rate", price_list_rate)
-					frappe.msgprint(_("Item Price updated for {0} in Price List {1}").format(args.item_code,
-						args.price_list), alert=True)
+				if item_price.price_list_rate != price_list_rate and frappe.db.get_single_value(
+					"Stock Settings", "update_existing_price_list_rate"
+				):
+					frappe.db.set_value("Item Price", item_price.name, "price_list_rate", price_list_rate)
+					frappe.msgprint(
+						_("Item Price updated for {0} in Price List {1}").format(args.item_code, args.price_list),
+						alert=True,
+					)
 			else:
-				item_price = frappe.get_doc({
-					"doctype": "Item Price",
-					"price_list": args.price_list,
-					"item_code": args.item_code,
-					"currency": args.currency,
-					"price_list_rate": price_list_rate
-				})
+				item_price = frappe.get_doc(
+					{
+						"doctype": "Item Price",
+						"price_list": args.price_list,
+						"item_code": args.item_code,
+						"currency": args.currency,
+						"price_list_rate": price_list_rate,
+						"uom": args.stock_uom,
+					}
+				)
 				item_price.insert()
-				frappe.msgprint(_("Item Price added for {0} in Price List {1}").format(args.item_code,
-					args.price_list), alert=True)
+				frappe.msgprint(
+					_("Item Price added for {0} in Price List {1}").format(args.item_code, args.price_list),
+					alert=True,
+				)
+
 
 def get_item_price(args, item_code, ignore_party=False):
 	"""
-		Get name, price_list_rate from Item Price based on conditions
-			Check if the desired qty is within the increment of the packing list.
-		:param args: dict (or frappe._dict) with mandatory fields price_list, uom
-			optional fields transaction_date, customer, supplier
-		:param item_code: str, Item Doctype field item_code
+	Get name, price_list_rate from Item Price based on conditions
+	        Check if the desired qty is within the increment of the packing list.
+	:param args: dict (or frappe._dict) with mandatory fields price_list, uom
+	        optional fields transaction_date, customer, supplier
+	:param item_code: str, Item Doctype field item_code
 	"""
 
-	args['item_code'] = item_code
+	args["item_code"] = item_code
 
 	conditions = """where item_code=%(item_code)s
 		and price_list=%(price_list)s
@@ -758,36 +883,37 @@
 		else:
 			conditions += "and (customer is null or customer = '') and (supplier is null or supplier = '')"
 
-	if args.get('transaction_date'):
+	if args.get("transaction_date"):
 		conditions += """ and %(transaction_date)s between
 			ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31')"""
 
-	if args.get('posting_date'):
-		conditions += """ and %(posting_date)s between
-			ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31')"""
-
-	return frappe.db.sql(""" select name, price_list_rate, uom
+	return frappe.db.sql(
+		""" select name, price_list_rate, uom
 		from `tabItem Price` {conditions}
-		order by valid_from desc, batch_no desc, uom desc """.format(conditions=conditions), args)
+		order by valid_from desc, ifnull(batch_no, '') desc, uom desc """.format(
+			conditions=conditions
+		),
+		args,
+	)
+
 
 def get_price_list_rate_for(args, item_code):
 	"""
-		:param customer: link to Customer DocType
-		:param supplier: link to Supplier DocType
-		:param price_list: str (Standard Buying or Standard Selling)
-		:param item_code: str, Item Doctype field item_code
-		:param qty: Desired Qty
-		:param transaction_date: Date of the price
+	:param customer: link to Customer DocType
+	:param supplier: link to Supplier DocType
+	:param price_list: str (Standard Buying or Standard Selling)
+	:param item_code: str, Item Doctype field item_code
+	:param qty: Desired Qty
+	:param transaction_date: Date of the price
 	"""
 	item_price_args = {
-			"item_code": item_code,
-			"price_list": args.get('price_list'),
-			"customer": args.get('customer'),
-			"supplier": args.get('supplier'),
-			"uom": args.get('uom'),
-			"transaction_date": args.get('transaction_date'),
-			"posting_date": args.get('posting_date'),
-			"batch_no": args.get('batch_no')
+		"item_code": item_code,
+		"price_list": args.get("price_list"),
+		"customer": args.get("customer"),
+		"supplier": args.get("supplier"),
+		"uom": args.get("uom"),
+		"transaction_date": args.get("transaction_date"),
+		"batch_no": args.get("batch_no"),
 	}
 
 	item_price_data = 0
@@ -800,12 +926,15 @@
 		for field in ["customer", "supplier"]:
 			del item_price_args[field]
 
-		general_price_list_rate = get_item_price(item_price_args, item_code,
-			ignore_party=args.get("ignore_party"))
+		general_price_list_rate = get_item_price(
+			item_price_args, item_code, ignore_party=args.get("ignore_party")
+		)
 
 		if not general_price_list_rate and args.get("uom") != args.get("stock_uom"):
 			item_price_args["uom"] = args.get("stock_uom")
-			general_price_list_rate = get_item_price(item_price_args, item_code, ignore_party=args.get("ignore_party"))
+			general_price_list_rate = get_item_price(
+				item_price_args, item_code, ignore_party=args.get("ignore_party")
+			)
 
 		if general_price_list_rate:
 			item_price_data = general_price_list_rate
@@ -813,18 +942,19 @@
 	if item_price_data:
 		if item_price_data[0][2] == args.get("uom"):
 			return item_price_data[0][1]
-		elif not args.get('price_list_uom_dependant'):
+		elif not args.get("price_list_uom_dependant"):
 			return flt(item_price_data[0][1] * flt(args.get("conversion_factor", 1)))
 		else:
 			return item_price_data[0][1]
 
+
 def check_packing_list(price_list_rate_name, desired_qty, item_code):
 	"""
-		Check if the desired qty is within the increment of the packing list.
-		:param price_list_rate_name: Name of Item Price
-		:param desired_qty: Desired Qt
-		:param item_code: str, Item Doctype field item_code
-		:param qty: Desired Qt
+	Check if the desired qty is within the increment of the packing list.
+	:param price_list_rate_name: Name of Item Price
+	:param desired_qty: Desired Qt
+	:param item_code: str, Item Doctype field item_code
+	:param qty: Desired Qt
 	"""
 
 	flag = True
@@ -837,47 +967,62 @@
 
 	return flag
 
+
 def validate_conversion_rate(args, meta):
 	from erpnext.controllers.accounts_controller import validate_conversion_rate
 
-	company_currency = frappe.get_cached_value('Company',  args.company,  "default_currency")
-	if (not args.conversion_rate and args.currency==company_currency):
+	company_currency = frappe.get_cached_value("Company", args.company, "default_currency")
+	if not args.conversion_rate and args.currency == company_currency:
 		args.conversion_rate = 1.0
 
-	if (not args.ignore_conversion_rate and args.conversion_rate == 1 and args.currency!=company_currency):
-		args.conversion_rate = get_exchange_rate(args.currency,
-			company_currency, args.transaction_date, "for_buying") or 1.0
+	if (
+		not args.ignore_conversion_rate
+		and args.conversion_rate == 1
+		and args.currency != company_currency
+	):
+		args.conversion_rate = (
+			get_exchange_rate(args.currency, company_currency, args.transaction_date, "for_buying") or 1.0
+		)
 
 	# validate currency conversion rate
-	validate_conversion_rate(args.currency, args.conversion_rate,
-		meta.get_label("conversion_rate"), args.company)
+	validate_conversion_rate(
+		args.currency, args.conversion_rate, meta.get_label("conversion_rate"), args.company
+	)
 
-	args.conversion_rate = flt(args.conversion_rate,
-		get_field_precision(meta.get_field("conversion_rate"),
-			frappe._dict({"fields": args})))
+	args.conversion_rate = flt(
+		args.conversion_rate,
+		get_field_precision(meta.get_field("conversion_rate"), frappe._dict({"fields": args})),
+	)
 
 	if args.price_list:
-		if (not args.plc_conversion_rate
-			and args.price_list_currency==frappe.db.get_value("Price List", args.price_list, "currency", cache=True)):
+		if not args.plc_conversion_rate and args.price_list_currency == frappe.db.get_value(
+			"Price List", args.price_list, "currency", cache=True
+		):
 			args.plc_conversion_rate = 1.0
 
 		# validate price list currency conversion rate
 		if not args.get("price_list_currency"):
 			throw(_("Price List Currency not selected"))
 		else:
-			validate_conversion_rate(args.price_list_currency, args.plc_conversion_rate,
-				meta.get_label("plc_conversion_rate"), args.company)
+			validate_conversion_rate(
+				args.price_list_currency,
+				args.plc_conversion_rate,
+				meta.get_label("plc_conversion_rate"),
+				args.company,
+			)
 
 			if meta.get_field("plc_conversion_rate"):
-				args.plc_conversion_rate = flt(args.plc_conversion_rate,
-					get_field_precision(meta.get_field("plc_conversion_rate"),
-					frappe._dict({"fields": args})))
+				args.plc_conversion_rate = flt(
+					args.plc_conversion_rate,
+					get_field_precision(meta.get_field("plc_conversion_rate"), frappe._dict({"fields": args})),
+				)
+
 
 def get_party_item_code(args, item_doc, out):
-	if args.transaction_type=="selling" and args.customer:
+	if args.transaction_type == "selling" and args.customer:
 		out.customer_item_code = None
 
-		if args.quotation_to and args.quotation_to != 'Customer':
+		if args.quotation_to and args.quotation_to != "Customer":
 			return
 
 		customer_item_code = item_doc.get("customer_items", {"customer_name": args.customer})
@@ -890,15 +1035,16 @@
 			if customer_group_item_code and not customer_group_item_code[0].customer_name:
 				out.customer_item_code = customer_group_item_code[0].ref_code
 
-	if args.transaction_type=="buying" and args.supplier:
+	if args.transaction_type == "buying" and args.supplier:
 		item_supplier = item_doc.get("supplier_items", {"supplier": args.supplier})
 		out.supplier_part_no = item_supplier[0].supplier_part_no if item_supplier else None
 
+
 def get_pos_profile_item_details(company, args, pos_profile=None, update_data=False):
 	res = frappe._dict()
 
 	if not frappe.flags.pos_profile and not pos_profile:
-		pos_profile = frappe.flags.pos_profile = get_pos_profile(company, args.get('pos_profile'))
+		pos_profile = frappe.flags.pos_profile = get_pos_profile(company, args.get("pos_profile"))
 
 	if pos_profile:
 		for fieldname in ("income_account", "cost_center", "warehouse", "expense_account"):
@@ -910,70 +1056,89 @@
 
 	return res
 
+
 @frappe.whitelist()
 def get_pos_profile(company, pos_profile=None, user=None):
-	if pos_profile: return frappe.get_cached_doc('POS Profile', pos_profile)
+	if pos_profile:
+		return frappe.get_cached_doc("POS Profile", pos_profile)
 
 	if not user:
-		user = frappe.session['user']
+		user = frappe.session["user"]
 
 	condition = "pfu.user = %(user)s AND pfu.default=1"
 	if user and company:
 		condition = "pfu.user = %(user)s AND pf.company = %(company)s AND pfu.default=1"
 
-	pos_profile = frappe.db.sql("""SELECT pf.*
+	pos_profile = frappe.db.sql(
+		"""SELECT pf.*
 		FROM
 			`tabPOS Profile` pf LEFT JOIN `tabPOS Profile User` pfu
 		ON
 				pf.name = pfu.parent
 		WHERE
 			{cond} AND pf.disabled = 0
-	""".format(cond = condition), {
-		'user': user,
-		'company': company
-	}, as_dict=1)
+	""".format(
+			cond=condition
+		),
+		{"user": user, "company": company},
+		as_dict=1,
+	)
 
 	if not pos_profile and company:
-		pos_profile = frappe.db.sql("""SELECT pf.*
+		pos_profile = frappe.db.sql(
+			"""SELECT pf.*
 			FROM
 				`tabPOS Profile` pf LEFT JOIN `tabPOS Profile User` pfu
 			ON
 					pf.name = pfu.parent
 			WHERE
 				pf.company = %(company)s AND pf.disabled = 0
-		""", {
-			'company': company
-		}, as_dict=1)
+		""",
+			{"company": company},
+			as_dict=1,
+		)
 
 	return pos_profile and pos_profile[0] or None
 
+
 def get_serial_nos_by_fifo(args, sales_order=None):
 	if frappe.db.get_single_value("Stock Settings", "automatically_set_serial_nos_based_on_fifo"):
-		return "\n".join(frappe.db.sql_list("""select name from `tabSerial No`
+		return "\n".join(
+			frappe.db.sql_list(
+				"""select name from `tabSerial No`
 			where item_code=%(item_code)s and warehouse=%(warehouse)s and
 			sales_order=IF(%(sales_order)s IS NULL, sales_order, %(sales_order)s)
 			order by timestamp(purchase_date, purchase_time)
 			asc limit %(qty)s""",
-			{
-				"item_code": args.item_code,
-				"warehouse": args.warehouse,
-				"qty": abs(cint(args.stock_qty)),
-				"sales_order": sales_order
-			}))
+				{
+					"item_code": args.item_code,
+					"warehouse": args.warehouse,
+					"qty": abs(cint(args.stock_qty)),
+					"sales_order": sales_order,
+				},
+			)
+		)
+
 
 def get_serial_no_batchwise(args, sales_order=None):
 	if frappe.db.get_single_value("Stock Settings", "automatically_set_serial_nos_based_on_fifo"):
-		return "\n".join(frappe.db.sql_list("""select name from `tabSerial No`
+		return "\n".join(
+			frappe.db.sql_list(
+				"""select name from `tabSerial No`
 			where item_code=%(item_code)s and warehouse=%(warehouse)s and
 			sales_order=IF(%(sales_order)s IS NULL, sales_order, %(sales_order)s)
 			and batch_no=IF(%(batch_no)s IS NULL, batch_no, %(batch_no)s) order
-			by timestamp(purchase_date, purchase_time) asc limit %(qty)s""", {
-				"item_code": args.item_code,
-				"warehouse": args.warehouse,
-				"batch_no": args.batch_no,
-				"qty": abs(cint(args.stock_qty)),
-				"sales_order": sales_order
-			}))
+			by timestamp(purchase_date, purchase_time) asc limit %(qty)s""",
+				{
+					"item_code": args.item_code,
+					"warehouse": args.warehouse,
+					"batch_no": args.batch_no,
+					"qty": abs(cint(args.stock_qty)),
+					"sales_order": sales_order,
+				},
+			)
+		)
+
 
 @frappe.whitelist()
 def get_conversion_factor(item_code, uom):
@@ -981,69 +1146,94 @@
 	filters = {"parent": item_code, "uom": uom}
 	if variant_of:
 		filters["parent"] = ("in", (item_code, variant_of))
-	conversion_factor = frappe.db.get_value("UOM Conversion Detail",
-		filters, "conversion_factor")
+	conversion_factor = frappe.db.get_value("UOM Conversion Detail", filters, "conversion_factor")
 	if not conversion_factor:
 		stock_uom = frappe.db.get_value("Item", item_code, "stock_uom")
 		conversion_factor = get_uom_conv_factor(uom, stock_uom)
 	return {"conversion_factor": conversion_factor or 1.0}
 
+
 @frappe.whitelist()
 def get_projected_qty(item_code, warehouse):
-	return {"projected_qty": frappe.db.get_value("Bin",
-		{"item_code": item_code, "warehouse": warehouse}, "projected_qty")}
+	return {
+		"projected_qty": frappe.db.get_value(
+			"Bin", {"item_code": item_code, "warehouse": warehouse}, "projected_qty"
+		)
+	}
+
 
 @frappe.whitelist()
 def get_bin_details(item_code, warehouse, company=None):
-	bin_details = frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse},
-		["projected_qty", "actual_qty", "reserved_qty"], as_dict=True, cache=True) \
-			or {"projected_qty": 0, "actual_qty": 0, "reserved_qty": 0}
+	bin_details = frappe.db.get_value(
+		"Bin",
+		{"item_code": item_code, "warehouse": warehouse},
+		["projected_qty", "actual_qty", "reserved_qty"],
+		as_dict=True,
+		cache=True,
+	) or {"projected_qty": 0, "actual_qty": 0, "reserved_qty": 0}
 	if company:
-		bin_details['company_total_stock'] = get_company_total_stock(item_code, company)
+		bin_details["company_total_stock"] = get_company_total_stock(item_code, company)
 	return bin_details
 
+
 def get_company_total_stock(item_code, company):
-	return frappe.db.sql("""SELECT sum(actual_qty) from
+	return frappe.db.sql(
+		"""SELECT sum(actual_qty) from
 		(`tabBin` INNER JOIN `tabWarehouse` ON `tabBin`.warehouse = `tabWarehouse`.name)
 		WHERE `tabWarehouse`.company = %s and `tabBin`.item_code = %s""",
-		(company, item_code))[0][0]
+		(company, item_code),
+	)[0][0]
+
 
 @frappe.whitelist()
 def get_serial_no_details(item_code, warehouse, stock_qty, serial_no):
-	args = frappe._dict({"item_code":item_code, "warehouse":warehouse, "stock_qty":stock_qty, "serial_no":serial_no})
+	args = frappe._dict(
+		{"item_code": item_code, "warehouse": warehouse, "stock_qty": stock_qty, "serial_no": serial_no}
+	)
 	serial_no = get_serial_no(args)
-	return {'serial_no': serial_no}
+	return {"serial_no": serial_no}
+
 
 @frappe.whitelist()
-def get_bin_details_and_serial_nos(item_code, warehouse, has_batch_no=None, stock_qty=None, serial_no=None):
+def get_bin_details_and_serial_nos(
+	item_code, warehouse, has_batch_no=None, stock_qty=None, serial_no=None
+):
 	bin_details_and_serial_nos = {}
 	bin_details_and_serial_nos.update(get_bin_details(item_code, warehouse))
 	if flt(stock_qty) > 0:
 		if has_batch_no:
-			args = frappe._dict({"item_code":item_code, "warehouse":warehouse, "stock_qty":stock_qty})
+			args = frappe._dict({"item_code": item_code, "warehouse": warehouse, "stock_qty": stock_qty})
 			serial_no = get_serial_no(args)
-			bin_details_and_serial_nos.update({'serial_no': serial_no})
+			bin_details_and_serial_nos.update({"serial_no": serial_no})
 			return bin_details_and_serial_nos
 
-		bin_details_and_serial_nos.update(get_serial_no_details(item_code, warehouse, stock_qty, serial_no))
+		bin_details_and_serial_nos.update(
+			get_serial_no_details(item_code, warehouse, stock_qty, serial_no)
+		)
 	return bin_details_and_serial_nos
 
+
 @frappe.whitelist()
 def get_batch_qty_and_serial_no(batch_no, stock_qty, warehouse, item_code, has_serial_no):
 	batch_qty_and_serial_no = {}
 	batch_qty_and_serial_no.update(get_batch_qty(batch_no, warehouse, item_code))
 
-	if (flt(batch_qty_and_serial_no.get('actual_batch_qty')) >= flt(stock_qty)) and has_serial_no:
-		args = frappe._dict({"item_code":item_code, "warehouse":warehouse, "stock_qty":stock_qty, "batch_no":batch_no})
+	if (flt(batch_qty_and_serial_no.get("actual_batch_qty")) >= flt(stock_qty)) and has_serial_no:
+		args = frappe._dict(
+			{"item_code": item_code, "warehouse": warehouse, "stock_qty": stock_qty, "batch_no": batch_no}
+		)
 		serial_no = get_serial_no(args)
-		batch_qty_and_serial_no.update({'serial_no': serial_no})
+		batch_qty_and_serial_no.update({"serial_no": serial_no})
 	return batch_qty_and_serial_no
 
+
 @frappe.whitelist()
 def get_batch_qty(batch_no, warehouse, item_code):
 	from erpnext.stock.doctype.batch import batch
+
 	if batch_no:
-		return {'actual_batch_qty': batch.get_batch_qty(batch_no, warehouse)}
+		return {"actual_batch_qty": batch.get_batch_qty(batch_no, warehouse)}
+
 
 @frappe.whitelist()
 def apply_price_list(args, as_doc=False):
@@ -1053,23 +1243,23 @@
 	:param args: See below
 	:param as_doc: Updates value in the passed dict
 
-		args = {
-			"doctype": "",
-			"name": "",
-			"items": [{"doctype": "", "name": "", "item_code": "", "brand": "", "item_group": ""}, ...],
-			"conversion_rate": 1.0,
-			"selling_price_list": None,
-			"price_list_currency": None,
-			"price_list_uom_dependant": None,
-			"plc_conversion_rate": 1.0,
-			"doctype": "",
-			"name": "",
-			"supplier": None,
-			"transaction_date": None,
-			"conversion_rate": 1.0,
-			"buying_price_list": None,
-			"ignore_pricing_rule": 0/1
-		}
+	        args = {
+	                "doctype": "",
+	                "name": "",
+	                "items": [{"doctype": "", "name": "", "item_code": "", "brand": "", "item_group": ""}, ...],
+	                "conversion_rate": 1.0,
+	                "selling_price_list": None,
+	                "price_list_currency": None,
+	                "price_list_uom_dependant": None,
+	                "plc_conversion_rate": 1.0,
+	                "doctype": "",
+	                "name": "",
+	                "supplier": None,
+	                "transaction_date": None,
+	                "conversion_rate": 1.0,
+	                "buying_price_list": None,
+	                "ignore_pricing_rule": 0/1
+	        }
 	"""
 	args = process_args(args)
 
@@ -1089,10 +1279,10 @@
 			children.append(item_details)
 
 	if as_doc:
-		args.price_list_currency = parent.price_list_currency,
+		args.price_list_currency = (parent.price_list_currency,)
 		args.plc_conversion_rate = parent.plc_conversion_rate
-		if args.get('items'):
-			for i, item in enumerate(args.get('items')):
+		if args.get("items"):
+			for i, item in enumerate(args.get("items")):
 				for fieldname in children[i]:
 					# if the field exists in the original doc
 					# update the value
@@ -1100,26 +1290,25 @@
 						item[fieldname] = children[i][fieldname]
 		return args
 	else:
-		return {
-			"parent": parent,
-			"children": children
-		}
+		return {"parent": parent, "children": children}
+
 
 def apply_price_list_on_item(args):
-	item_doc = frappe.db.get_value("Item", args.item_code, ['name', 'variant_of'], as_dict=1)
+	item_doc = frappe.db.get_value("Item", args.item_code, ["name", "variant_of"], as_dict=1)
 	item_details = get_price_list_rate(args, item_doc)
 
 	item_details.update(get_pricing_rule_for_item(args, item_details.price_list_rate))
 
 	return item_details
 
+
 def get_price_list_currency_and_exchange_rate(args):
 	if not args.price_list:
 		return {}
 
-	if args.doctype in ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']:
+	if args.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]:
 		args.update({"exchange_rate": "for_selling"})
-	elif args.doctype in ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']:
+	elif args.doctype in ["Purchase Order", "Purchase Receipt", "Purchase Invoice"]:
 		args.update({"exchange_rate": "for_buying"})
 
 	price_list_details = get_price_list_details(args.price_list)
@@ -1130,24 +1319,47 @@
 	plc_conversion_rate = args.plc_conversion_rate
 	company_currency = get_company_currency(args.company)
 
-	if (not plc_conversion_rate) or (price_list_currency and args.price_list_currency \
-		and price_list_currency != args.price_list_currency):
-			# cksgb 19/09/2016: added args.transaction_date as posting_date argument for get_exchange_rate
-			plc_conversion_rate = get_exchange_rate(price_list_currency, company_currency,
-				args.transaction_date, args.exchange_rate) or plc_conversion_rate
+	if (not plc_conversion_rate) or (
+		price_list_currency
+		and args.price_list_currency
+		and price_list_currency != args.price_list_currency
+	):
+		# cksgb 19/09/2016: added args.transaction_date as posting_date argument for get_exchange_rate
+		plc_conversion_rate = (
+			get_exchange_rate(
+				price_list_currency, company_currency, args.transaction_date, args.exchange_rate
+			)
+			or plc_conversion_rate
+		)
 
-	return frappe._dict({
-		"price_list_currency": price_list_currency,
-		"price_list_uom_dependant": price_list_uom_dependant,
-		"plc_conversion_rate": plc_conversion_rate or 1
-	})
+	return frappe._dict(
+		{
+			"price_list_currency": price_list_currency,
+			"price_list_uom_dependant": price_list_uom_dependant,
+			"plc_conversion_rate": plc_conversion_rate or 1,
+		}
+	)
+
 
 @frappe.whitelist()
 def get_default_bom(item_code=None):
-	if item_code:
-		bom = frappe.db.get_value("BOM", {"docstatus": 1, "is_default": 1, "is_active": 1, "item": item_code})
-		if bom:
-			return bom
+	def _get_bom(item):
+		bom = frappe.get_all(
+			"BOM", dict(item=item, is_active=True, is_default=True, docstatus=1), limit=1
+		)
+		return bom[0].name if bom else None
+
+	if not item_code:
+		return
+
+	bom_name = _get_bom(item_code)
+
+	template_item = frappe.db.get_value("Item", item_code, "variant_of")
+	if not bom_name and template_item:
+		bom_name = _get_bom(template_item)
+
+	return bom_name
+
 
 @frappe.whitelist()
 def get_valuation_rate(item_code, company, warehouse=None):
@@ -1157,43 +1369,57 @@
 	# item = frappe.get_doc("Item", item_code)
 	if item.get("is_stock_item"):
 		if not warehouse:
-			warehouse = item.get("default_warehouse") or item_group.get("default_warehouse") or brand.get("default_warehouse")
+			warehouse = (
+				item.get("default_warehouse")
+				or item_group.get("default_warehouse")
+				or brand.get("default_warehouse")
+			)
 
-		return frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse},
-			["valuation_rate"], as_dict=True) or {"valuation_rate": 0}
+		return frappe.db.get_value(
+			"Bin", {"item_code": item_code, "warehouse": warehouse}, ["valuation_rate"], as_dict=True
+		) or {"valuation_rate": 0}
 
 	elif not item.get("is_stock_item"):
-		valuation_rate =frappe.db.sql("""select sum(base_net_amount) / sum(qty*conversion_factor)
+		valuation_rate = frappe.db.sql(
+			"""select sum(base_net_amount) / sum(qty*conversion_factor)
 			from `tabPurchase Invoice Item`
-			where item_code = %s and docstatus=1""", item_code)
+			where item_code = %s and docstatus=1""",
+			item_code,
+		)
 
 		if valuation_rate:
 			return {"valuation_rate": valuation_rate[0][0] or 0.0}
 	else:
 		return {"valuation_rate": 0.0}
 
+
 def get_gross_profit(out):
 	if out.valuation_rate:
-		out.update({
-			"gross_profit": ((out.base_rate - out.valuation_rate) * out.stock_qty)
-		})
+		out.update({"gross_profit": ((out.base_rate - out.valuation_rate) * out.stock_qty)})
 
 	return out
 
+
 @frappe.whitelist()
 def get_serial_no(args, serial_nos=None, sales_order=None):
 	serial_no = None
 	if isinstance(args, str):
 		args = json.loads(args)
 		args = frappe._dict(args)
-	if args.get('doctype') == 'Sales Invoice' and not args.get('update_stock'):
+	if args.get("doctype") == "Sales Invoice" and not args.get("update_stock"):
 		return ""
-	if args.get('warehouse') and args.get('stock_qty') and args.get('item_code'):
-		has_serial_no = frappe.get_value('Item', {'item_code': args.item_code}, "has_serial_no")
-		if args.get('batch_no') and has_serial_no == 1:
+	if args.get("warehouse") and args.get("stock_qty") and args.get("item_code"):
+		has_serial_no = frappe.get_value("Item", {"item_code": args.item_code}, "has_serial_no")
+		if args.get("batch_no") and has_serial_no == 1:
 			return get_serial_no_batchwise(args, sales_order)
 		elif has_serial_no == 1:
-			args = json.dumps({"item_code": args.get('item_code'),"warehouse": args.get('warehouse'),"stock_qty": args.get('stock_qty')})
+			args = json.dumps(
+				{
+					"item_code": args.get("item_code"),
+					"warehouse": args.get("warehouse"),
+					"stock_qty": args.get("stock_qty"),
+				}
+			)
 			args = process_args(args)
 			serial_no = get_serial_nos_by_fifo(args, sales_order)
 
@@ -1210,53 +1436,68 @@
 		if blanket_order_details:
 			out.update(blanket_order_details)
 
+
 @frappe.whitelist()
 def get_blanket_order_details(args):
 	if isinstance(args, str):
 		args = frappe._dict(json.loads(args))
 
 	blanket_order_details = None
-	condition = ''
+	condition = ""
 	if args.item_code:
 		if args.customer and args.doctype == "Sales Order":
-			condition = ' and bo.customer=%(customer)s'
+			condition = " and bo.customer=%(customer)s"
 		elif args.supplier and args.doctype == "Purchase Order":
-			condition = ' and bo.supplier=%(supplier)s'
+			condition = " and bo.supplier=%(supplier)s"
 		if args.blanket_order:
-			condition += ' and bo.name =%(blanket_order)s'
+			condition += " and bo.name =%(blanket_order)s"
 		if args.transaction_date:
-			condition += ' and bo.to_date>=%(transaction_date)s'
+			condition += " and bo.to_date>=%(transaction_date)s"
 
-		blanket_order_details = frappe.db.sql('''
+		blanket_order_details = frappe.db.sql(
+			"""
 				select boi.rate as blanket_order_rate, bo.name as blanket_order
 				from `tabBlanket Order` bo, `tabBlanket Order Item` boi
 				where bo.company=%(company)s and boi.item_code=%(item_code)s
 					and bo.docstatus=1 and bo.name = boi.parent {0}
-			'''.format(condition), args, as_dict=True)
+			""".format(
+				condition
+			),
+			args,
+			as_dict=True,
+		)
 
-		blanket_order_details = blanket_order_details[0] if blanket_order_details else ''
+		blanket_order_details = blanket_order_details[0] if blanket_order_details else ""
 	return blanket_order_details
 
+
 def get_so_reservation_for_item(args):
 	reserved_so = None
-	if args.get('against_sales_order'):
-		if get_reserved_qty_for_so(args.get('against_sales_order'), args.get('item_code')):
-			reserved_so = args.get('against_sales_order')
-	elif args.get('against_sales_invoice'):
-		sales_order = frappe.db.sql("""select sales_order from `tabSales Invoice Item` where
-		parent=%s and item_code=%s""", (args.get('against_sales_invoice'), args.get('item_code')))
+	if args.get("against_sales_order"):
+		if get_reserved_qty_for_so(args.get("against_sales_order"), args.get("item_code")):
+			reserved_so = args.get("against_sales_order")
+	elif args.get("against_sales_invoice"):
+		sales_order = frappe.db.sql(
+			"""select sales_order from `tabSales Invoice Item` where
+		parent=%s and item_code=%s""",
+			(args.get("against_sales_invoice"), args.get("item_code")),
+		)
 		if sales_order and sales_order[0]:
-			if get_reserved_qty_for_so(sales_order[0][0], args.get('item_code')):
+			if get_reserved_qty_for_so(sales_order[0][0], args.get("item_code")):
 				reserved_so = sales_order[0]
 	elif args.get("sales_order"):
-		if get_reserved_qty_for_so(args.get('sales_order'), args.get('item_code')):
-			reserved_so = args.get('sales_order')
+		if get_reserved_qty_for_so(args.get("sales_order"), args.get("item_code")):
+			reserved_so = args.get("sales_order")
 	return reserved_so
 
+
 def get_reserved_qty_for_so(sales_order, item_code):
-	reserved_qty = frappe.db.sql("""select sum(qty) from `tabSales Order Item`
+	reserved_qty = frappe.db.sql(
+		"""select sum(qty) from `tabSales Order Item`
 	where parent=%s and item_code=%s and ensure_delivery_based_on_produced_serial_no=1
-	""", (sales_order, item_code))
+	""",
+		(sales_order, item_code),
+	)
 	if reserved_qty and reserved_qty[0][0]:
 		return reserved_qty[0][0]
 	else:
diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
index ea27dd2..61927f5 100644
--- a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
+++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
@@ -68,7 +68,7 @@
 			options: [
 				{fieldname: 'stock_capacity', label: __('Capacity (Stock UOM)')},
 				{fieldname: 'percent_occupied', label: __('% Occupied')},
-				{fieldname: 'actual_qty', label: __('Balance Qty (Stock ')}
+				{fieldname: 'actual_qty', label: __('Balance Qty (Stock)')}
 			]
 		},
 		change: function(sort_by, sort_order) {
diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py
index 21f2573..136c78f 100644
--- a/erpnext/stock/reorder_item.py
+++ b/erpnext/stock/reorder_item.py
@@ -13,22 +13,29 @@
 
 
 def reorder_item():
-	""" Reorder item if stock reaches reorder level"""
+	"""Reorder item if stock reaches reorder level"""
 	# if initial setup not completed, return
 	if not (frappe.db.a_row_exists("Company") and frappe.db.a_row_exists("Fiscal Year")):
 		return
 
-	if cint(frappe.db.get_value('Stock Settings', None, 'auto_indent')):
+	if cint(frappe.db.get_value("Stock Settings", None, "auto_indent")):
 		return _reorder_item()
 
+
 def _reorder_item():
 	material_requests = {"Purchase": {}, "Transfer": {}, "Material Issue": {}, "Manufacture": {}}
-	warehouse_company = frappe._dict(frappe.db.sql("""select name, company from `tabWarehouse`
-		where disabled=0"""))
-	default_company = (erpnext.get_default_company() or
-		frappe.db.sql("""select name from tabCompany limit 1""")[0][0])
+	warehouse_company = frappe._dict(
+		frappe.db.sql(
+			"""select name, company from `tabWarehouse`
+		where disabled=0"""
+		)
+	)
+	default_company = (
+		erpnext.get_default_company() or frappe.db.sql("""select name from tabCompany limit 1""")[0][0]
+	)
 
-	items_to_consider = frappe.db.sql_list("""select name from `tabItem` item
+	items_to_consider = frappe.db.sql_list(
+		"""select name from `tabItem` item
 		where is_stock_item=1 and has_variants=0
 			and disabled=0
 			and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %(today)s)
@@ -36,14 +43,17 @@
 				or (variant_of is not null and variant_of != ''
 				and exists (select name from `tabItem Reorder` ir where ir.parent=item.variant_of))
 			)""",
-		{"today": nowdate()})
+		{"today": nowdate()},
+	)
 
 	if not items_to_consider:
 		return
 
 	item_warehouse_projected_qty = get_item_warehouse_projected_qty(items_to_consider)
 
-	def add_to_material_request(item_code, warehouse, reorder_level, reorder_qty, material_request_type, warehouse_group=None):
+	def add_to_material_request(
+		item_code, warehouse, reorder_level, reorder_qty, material_request_type, warehouse_group=None
+	):
 		if warehouse not in warehouse_company:
 			# a disabled warehouse
 			return
@@ -64,11 +74,9 @@
 
 			company = warehouse_company.get(warehouse) or default_company
 
-			material_requests[material_request_type].setdefault(company, []).append({
-				"item_code": item_code,
-				"warehouse": warehouse,
-				"reorder_qty": reorder_qty
-			})
+			material_requests[material_request_type].setdefault(company, []).append(
+				{"item_code": item_code, "warehouse": warehouse, "reorder_qty": reorder_qty}
+			)
 
 	for item_code in items_to_consider:
 		item = frappe.get_doc("Item", item_code)
@@ -78,19 +86,30 @@
 
 		if item.get("reorder_levels"):
 			for d in item.get("reorder_levels"):
-				add_to_material_request(item_code, d.warehouse, d.warehouse_reorder_level,
-					d.warehouse_reorder_qty, d.material_request_type, warehouse_group=d.warehouse_group)
+				add_to_material_request(
+					item_code,
+					d.warehouse,
+					d.warehouse_reorder_level,
+					d.warehouse_reorder_qty,
+					d.material_request_type,
+					warehouse_group=d.warehouse_group,
+				)
 
 	if material_requests:
 		return create_material_request(material_requests)
 
+
 def get_item_warehouse_projected_qty(items_to_consider):
 	item_warehouse_projected_qty = {}
 
-	for item_code, warehouse, projected_qty in frappe.db.sql("""select item_code, warehouse, projected_qty
+	for item_code, warehouse, projected_qty in frappe.db.sql(
+		"""select item_code, warehouse, projected_qty
 		from tabBin where item_code in ({0})
-			and (warehouse != "" and warehouse is not null)"""\
-		.format(", ".join(["%s"] * len(items_to_consider))), items_to_consider):
+			and (warehouse != '' and warehouse is not null)""".format(
+			", ".join(["%s"] * len(items_to_consider))
+		),
+		items_to_consider,
+	):
 
 		if item_code not in item_warehouse_projected_qty:
 			item_warehouse_projected_qty.setdefault(item_code, {})
@@ -102,26 +121,29 @@
 
 		while warehouse_doc.parent_warehouse:
 			if not item_warehouse_projected_qty.get(item_code, {}).get(warehouse_doc.parent_warehouse):
-				item_warehouse_projected_qty.setdefault(item_code, {})[warehouse_doc.parent_warehouse] = flt(projected_qty)
+				item_warehouse_projected_qty.setdefault(item_code, {})[warehouse_doc.parent_warehouse] = flt(
+					projected_qty
+				)
 			else:
 				item_warehouse_projected_qty[item_code][warehouse_doc.parent_warehouse] += flt(projected_qty)
 			warehouse_doc = frappe.get_doc("Warehouse", warehouse_doc.parent_warehouse)
 
 	return item_warehouse_projected_qty
 
+
 def create_material_request(material_requests):
-	"""	Create indent on reaching reorder level	"""
+	"""Create indent on reaching reorder level"""
 	mr_list = []
 	exceptions_list = []
 
-	def _log_exception():
+	def _log_exception(mr):
 		if frappe.local.message_log:
 			exceptions_list.extend(frappe.local.message_log)
 			frappe.local.message_log = []
 		else:
 			exceptions_list.append(frappe.get_traceback())
 
-		frappe.log_error(frappe.get_traceback())
+		mr.log_error("Unable to create material request")
 
 	for request_type in material_requests:
 		for company in material_requests[request_type]:
@@ -131,11 +153,13 @@
 					continue
 
 				mr = frappe.new_doc("Material Request")
-				mr.update({
-					"company": company,
-					"transaction_date": nowdate(),
-					"material_request_type": "Material Transfer" if request_type=="Transfer" else request_type
-				})
+				mr.update(
+					{
+						"company": company,
+						"transaction_date": nowdate(),
+						"material_request_type": "Material Transfer" if request_type == "Transfer" else request_type,
+					}
+				)
 
 				for d in items:
 					d = frappe._dict(d)
@@ -143,30 +167,37 @@
 					uom = item.stock_uom
 					conversion_factor = 1.0
 
-					if request_type == 'Purchase':
+					if request_type == "Purchase":
 						uom = item.purchase_uom or item.stock_uom
 						if uom != item.stock_uom:
-							conversion_factor = frappe.db.get_value("UOM Conversion Detail",
-								{'parent': item.name, 'uom': uom}, 'conversion_factor') or 1.0
+							conversion_factor = (
+								frappe.db.get_value(
+									"UOM Conversion Detail", {"parent": item.name, "uom": uom}, "conversion_factor"
+								)
+								or 1.0
+							)
 
 					must_be_whole_number = frappe.db.get_value("UOM", uom, "must_be_whole_number", cache=True)
 					qty = d.reorder_qty / conversion_factor
 					if must_be_whole_number:
 						qty = ceil(qty)
 
-					mr.append("items", {
-						"doctype": "Material Request Item",
-						"item_code": d.item_code,
-						"schedule_date": add_days(nowdate(),cint(item.lead_time_days)),
-						"qty": qty,
-						"uom": uom,
-						"stock_uom": item.stock_uom,
-						"warehouse": d.warehouse,
-						"item_name": item.item_name,
-						"description": item.description,
-						"item_group": item.item_group,
-						"brand": item.brand,
-					})
+					mr.append(
+						"items",
+						{
+							"doctype": "Material Request Item",
+							"item_code": d.item_code,
+							"schedule_date": add_days(nowdate(), cint(item.lead_time_days)),
+							"qty": qty,
+							"uom": uom,
+							"stock_uom": item.stock_uom,
+							"warehouse": d.warehouse,
+							"item_name": item.item_name,
+							"description": item.description,
+							"item_group": item.item_group,
+							"brand": item.brand,
+						},
+					)
 
 				schedule_dates = [d.schedule_date for d in mr.items]
 				mr.schedule_date = max(schedule_dates or [nowdate()])
@@ -176,14 +207,15 @@
 				mr_list.append(mr)
 
 			except Exception:
-				_log_exception()
+				_log_exception(mr)
 
 	if mr_list:
 		if getattr(frappe.local, "reorder_email_notify", None) is None:
-			frappe.local.reorder_email_notify = cint(frappe.db.get_value('Stock Settings', None,
-				'reorder_email_notify'))
+			frappe.local.reorder_email_notify = cint(
+				frappe.db.get_value("Stock Settings", None, "reorder_email_notify")
+			)
 
-		if(frappe.local.reorder_email_notify):
+		if frappe.local.reorder_email_notify:
 			send_email_notification(mr_list)
 
 	if exceptions_list:
@@ -191,33 +223,46 @@
 
 	return mr_list
 
-def send_email_notification(mr_list):
-	""" Notify user about auto creation of indent"""
 
-	email_list = frappe.db.sql_list("""select distinct r.parent
+def send_email_notification(mr_list):
+	"""Notify user about auto creation of indent"""
+
+	email_list = frappe.db.sql_list(
+		"""select distinct r.parent
 		from `tabHas Role` r, tabUser p
 		where p.name = r.parent and p.enabled = 1 and p.docstatus < 2
 		and r.role in ('Purchase Manager','Stock Manager')
-		and p.name not in ('Administrator', 'All', 'Guest')""")
+		and p.name not in ('Administrator', 'All', 'Guest')"""
+	)
 
-	msg = frappe.render_template("templates/emails/reorder_item.html", {
-		"mr_list": mr_list
-	})
+	msg = frappe.render_template("templates/emails/reorder_item.html", {"mr_list": mr_list})
 
-	frappe.sendmail(recipients=email_list,
-		subject=_('Auto Material Requests Generated'), message = msg)
+	frappe.sendmail(recipients=email_list, subject=_("Auto Material Requests Generated"), message=msg)
+
 
 def notify_errors(exceptions_list):
 	subject = _("[Important] [ERPNext] Auto Reorder Errors")
-	content = _("Dear System Manager,") + "<br>" + _("An error occured for certain Items while creating Material Requests based on Re-order level. \
-		Please rectify these issues :") + "<br>"
+	content = (
+		_("Dear System Manager,")
+		+ "<br>"
+		+ _(
+			"An error occured for certain Items while creating Material Requests based on Re-order level. Please rectify these issues :"
+		)
+		+ "<br>"
+	)
 
 	for exception in exceptions_list:
-		exception = json.loads(exception)
-		error_message = """<div class='small text-muted'>{0}</div><br>""".format(_(exception.get("message")))
-		content += error_message
+		try:
+			exception = json.loads(exception)
+			error_message = """<div class='small text-muted'>{0}</div><br>""".format(
+				_(exception.get("message"))
+			)
+			content += error_message
+		except Exception:
+			pass
 
 	content += _("Regards,") + "<br>" + _("Administrator")
 
 	from frappe.email import sendmail_to_system_managers
+
 	sendmail_to_system_managers(subject, content)
diff --git a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py
index 87097c7..3d9b046 100644
--- a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py
+++ b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py
@@ -8,7 +8,8 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	float_precision = cint(frappe.db.get_default("float_precision")) or 3
 
@@ -22,22 +23,37 @@
 			for batch in sorted(iwb_map[item][wh]):
 				qty_dict = iwb_map[item][wh][batch]
 
-				data.append([item, item_map[item]["item_name"], item_map[item]["description"], wh, batch,
-					frappe.db.get_value('Batch', batch, 'expiry_date'), qty_dict.expiry_status
-				])
-
+				data.append(
+					[
+						item,
+						item_map[item]["item_name"],
+						item_map[item]["description"],
+						wh,
+						batch,
+						frappe.db.get_value("Batch", batch, "expiry_date"),
+						qty_dict.expiry_status,
+					]
+				)
 
 	return columns, data
 
+
 def get_columns(filters):
 	"""return columns based on filters"""
 
-	columns = [_("Item") + ":Link/Item:100"] + [_("Item Name") + "::150"] + [_("Description") + "::150"] + \
-	[_("Warehouse") + ":Link/Warehouse:100"] + [_("Batch") + ":Link/Batch:100"] + [_("Expires On") + ":Date:90"] + \
-	[_("Expiry (In Days)") + ":Int:120"]
+	columns = (
+		[_("Item") + ":Link/Item:100"]
+		+ [_("Item Name") + "::150"]
+		+ [_("Description") + "::150"]
+		+ [_("Warehouse") + ":Link/Warehouse:100"]
+		+ [_("Batch") + ":Link/Batch:100"]
+		+ [_("Expires On") + ":Date:90"]
+		+ [_("Expiry (In Days)") + ":Int:120"]
+	)
 
 	return columns
 
+
 def get_conditions(filters):
 	conditions = ""
 	if not filters.get("from_date"):
@@ -50,14 +66,19 @@
 
 	return conditions
 
+
 def get_stock_ledger_entries(filters):
 	conditions = get_conditions(filters)
-	return frappe.db.sql("""select item_code, batch_no, warehouse,
+	return frappe.db.sql(
+		"""select item_code, batch_no, warehouse,
 		posting_date, actual_qty
 		from `tabStock Ledger Entry`
 		where is_cancelled = 0
-		and docstatus < 2 and ifnull(batch_no, '') != '' %s order by item_code, warehouse""" %
-		conditions, as_dict=1)
+		and docstatus < 2 and ifnull(batch_no, '') != '' %s order by item_code, warehouse"""
+		% conditions,
+		as_dict=1,
+	)
+
 
 def get_item_warehouse_batch_map(filters, float_precision):
 	sle = get_stock_ledger_entries(filters)
@@ -67,13 +88,13 @@
 	to_date = getdate(filters["to_date"])
 
 	for d in sle:
-		iwb_map.setdefault(d.item_code, {}).setdefault(d.warehouse, {})\
-			.setdefault(d.batch_no, frappe._dict({
-				"expires_on": None, "expiry_status": None}))
+		iwb_map.setdefault(d.item_code, {}).setdefault(d.warehouse, {}).setdefault(
+			d.batch_no, frappe._dict({"expires_on": None, "expiry_status": None})
+		)
 
 		qty_dict = iwb_map[d.item_code][d.warehouse][d.batch_no]
 
-		expiry_date_unicode = frappe.db.get_value('Batch', d.batch_no, 'expiry_date')
+		expiry_date_unicode = frappe.db.get_value("Batch", d.batch_no, "expiry_date")
 		qty_dict.expires_on = expiry_date_unicode
 
 		exp_date = frappe.utils.data.getdate(expiry_date_unicode)
@@ -88,6 +109,7 @@
 
 	return iwb_map
 
+
 def get_item_details(filters):
 	item_map = {}
 	for d in frappe.db.sql("select name, item_name, description from tabItem", as_dict=1):
diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
index 9b21dea..8a13300 100644
--- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
+++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
@@ -8,7 +8,8 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	if filters.from_date > filters.to_date:
 		frappe.throw(_("From Date must be before To Date"))
@@ -26,11 +27,20 @@
 				for batch in sorted(iwb_map[item][wh]):
 					qty_dict = iwb_map[item][wh][batch]
 					if qty_dict.opening_qty or qty_dict.in_qty or qty_dict.out_qty or qty_dict.bal_qty:
-						data.append([item, item_map[item]["item_name"], item_map[item]["description"], wh, batch,
-							flt(qty_dict.opening_qty, float_precision), flt(qty_dict.in_qty, float_precision),
-							flt(qty_dict.out_qty, float_precision), flt(qty_dict.bal_qty, float_precision),
-							item_map[item]["stock_uom"]
-						])
+						data.append(
+							[
+								item,
+								item_map[item]["item_name"],
+								item_map[item]["description"],
+								wh,
+								batch,
+								flt(qty_dict.opening_qty, float_precision),
+								flt(qty_dict.in_qty, float_precision),
+								flt(qty_dict.out_qty, float_precision),
+								flt(qty_dict.bal_qty, float_precision),
+								item_map[item]["stock_uom"],
+							]
+						)
 
 	return columns, data
 
@@ -38,10 +48,18 @@
 def get_columns(filters):
 	"""return columns based on filters"""
 
-	columns = [_("Item") + ":Link/Item:100"] + [_("Item Name") + "::150"] + [_("Description") + "::150"] + \
-		[_("Warehouse") + ":Link/Warehouse:100"] + [_("Batch") + ":Link/Batch:100"] + [_("Opening Qty") + ":Float:90"] + \
-		[_("In Qty") + ":Float:80"] + [_("Out Qty") + ":Float:80"] + [_("Balance Qty") + ":Float:90"] + \
-		[_("UOM") + "::90"]
+	columns = (
+		[_("Item") + ":Link/Item:100"]
+		+ [_("Item Name") + "::150"]
+		+ [_("Description") + "::150"]
+		+ [_("Warehouse") + ":Link/Warehouse:100"]
+		+ [_("Batch") + ":Link/Batch:100"]
+		+ [_("Opening Qty") + ":Float:90"]
+		+ [_("In Qty") + ":Float:80"]
+		+ [_("Out Qty") + ":Float:80"]
+		+ [_("Balance Qty") + ":Float:90"]
+		+ [_("UOM") + "::90"]
+	)
 
 	return columns
 
@@ -66,13 +84,16 @@
 # get all details
 def get_stock_ledger_entries(filters):
 	conditions = get_conditions(filters)
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select item_code, batch_no, warehouse, posting_date, sum(actual_qty) as actual_qty
 		from `tabStock Ledger Entry`
 		where is_cancelled = 0 and docstatus < 2 and ifnull(batch_no, '') != '' %s
 		group by voucher_no, batch_no, item_code, warehouse
-		order by item_code, warehouse""" %
-		conditions, as_dict=1)
+		order by item_code, warehouse"""
+		% conditions,
+		as_dict=1,
+	)
 
 
 def get_item_warehouse_batch_map(filters, float_precision):
@@ -83,20 +104,21 @@
 	to_date = getdate(filters["to_date"])
 
 	for d in sle:
-		iwb_map.setdefault(d.item_code, {}).setdefault(d.warehouse, {})\
-			.setdefault(d.batch_no, frappe._dict({
-				"opening_qty": 0.0, "in_qty": 0.0, "out_qty": 0.0, "bal_qty": 0.0
-			}))
+		iwb_map.setdefault(d.item_code, {}).setdefault(d.warehouse, {}).setdefault(
+			d.batch_no, frappe._dict({"opening_qty": 0.0, "in_qty": 0.0, "out_qty": 0.0, "bal_qty": 0.0})
+		)
 		qty_dict = iwb_map[d.item_code][d.warehouse][d.batch_no]
 		if d.posting_date < from_date:
-			qty_dict.opening_qty = flt(qty_dict.opening_qty, float_precision) \
-				+ flt(d.actual_qty, float_precision)
+			qty_dict.opening_qty = flt(qty_dict.opening_qty, float_precision) + flt(
+				d.actual_qty, float_precision
+			)
 		elif d.posting_date >= from_date and d.posting_date <= to_date:
 			if flt(d.actual_qty) > 0:
 				qty_dict.in_qty = flt(qty_dict.in_qty, float_precision) + flt(d.actual_qty, float_precision)
 			else:
-				qty_dict.out_qty = flt(qty_dict.out_qty, float_precision) \
-					+ abs(flt(d.actual_qty, float_precision))
+				qty_dict.out_qty = flt(qty_dict.out_qty, float_precision) + abs(
+					flt(d.actual_qty, float_precision)
+				)
 
 		qty_dict.bal_qty = flt(qty_dict.bal_qty, float_precision) + flt(d.actual_qty, float_precision)
 
diff --git a/erpnext/stock/report/bom_search/bom_search.py b/erpnext/stock/report/bom_search/bom_search.py
index a22b224..56a65c3 100644
--- a/erpnext/stock/report/bom_search/bom_search.py
+++ b/erpnext/stock/report/bom_search/bom_search.py
@@ -3,6 +3,7 @@
 
 
 import frappe
+from frappe import _
 
 
 def execute(filters=None):
@@ -10,11 +11,13 @@
 	parents = {
 		"Product Bundle Item": "Product Bundle",
 		"BOM Explosion Item": "BOM",
-		"BOM Item": "BOM"
+		"BOM Item": "BOM",
 	}
 
-	for doctype in ("Product Bundle Item",
-		"BOM Explosion Item" if filters.search_sub_assemblies else "BOM Item"):
+	for doctype in (
+		"Product Bundle Item",
+		"BOM Explosion Item" if filters.search_sub_assemblies else "BOM Item",
+	):
 		all_boms = {}
 		for d in frappe.get_all(doctype, fields=["parent", "item_code"]):
 			all_boms.setdefault(d.parent, []).append(d.item_code)
@@ -29,16 +32,13 @@
 			if valid:
 				data.append((parent, parents[doctype]))
 
-	return [{
-		"fieldname": "parent",
-		"label": "BOM",
-		"width": 200,
-		"fieldtype": "Dynamic Link",
-		"options": "doctype"
-	},
-	{
-		"fieldname": "doctype",
-		"label": "Type",
-		"width": 200,
-		"fieldtype": "Data"
-	}], data
+	return [
+		{
+			"fieldname": "parent",
+			"label": _("BOM"),
+			"width": 200,
+			"fieldtype": "Dynamic Link",
+			"options": "doctype",
+		},
+		{"fieldname": "doctype", "label": _("Type"), "width": 200, "fieldtype": "Data"},
+	], data
diff --git a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py
index 058af77..4642a53 100644
--- a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py
+++ b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py
@@ -41,18 +41,8 @@
 
 def get_columns() -> Columns:
 	return [
-		{
-			'label': _('Item Group'),
-			'fieldname': 'item_group',
-			'fieldtype': 'Data',
-			'width': '200'
-		},
-		{
-			'label': _('COGS Debit'),
-			'fieldname': 'cogs_debit',
-			'fieldtype': 'Currency',
-			'width': '200'
-		}
+		{"label": _("Item Group"), "fieldname": "item_group", "fieldtype": "Data", "width": "200"},
+		{"label": _("COGS Debit"), "fieldname": "cogs_debit", "fieldtype": "Currency", "width": "200"},
 	]
 
 
@@ -67,11 +57,11 @@
 	data = []
 	for item in leveled_dict.items():
 		i = item[1]
-		if i['agg_value'] == 0:
+		if i["agg_value"] == 0:
 			continue
-		data.append(get_row(i['name'], i['agg_value'], i['is_group'], i['level']))
-		if i['self_value'] < i['agg_value'] and i['self_value'] > 0:
-			data.append(get_row(i['name'], i['self_value'], 0, i['level'] + 1))
+		data.append(get_row(i["name"], i["agg_value"], i["is_group"], i["level"]))
+		if i["self_value"] < i["agg_value"] and i["self_value"] > 0:
+			data.append(get_row(i["name"], i["self_value"], 0, i["level"] + 1))
 	return data
 
 
@@ -79,8 +69,8 @@
 	gl_entries = get_gl_entries(filters, [])
 	filtered_entries = []
 	for entry in gl_entries:
-		posting_date = entry.get('posting_date')
-		from_date = filters.get('from_date')
+		posting_date = entry.get("posting_date")
+		from_date = filters.get("from_date")
 		if date_diff(from_date, posting_date) > 0:
 			continue
 		filtered_entries.append(entry)
@@ -88,10 +78,11 @@
 
 
 def get_stock_value_difference_list(filtered_entries: FilteredEntries) -> SVDList:
-	voucher_nos = [fe.get('voucher_no') for fe in filtered_entries]
+	voucher_nos = [fe.get("voucher_no") for fe in filtered_entries]
 	svd_list = frappe.get_list(
-		'Stock Ledger Entry', fields=['item_code','stock_value_difference'],
-		filters=[('voucher_no', 'in', voucher_nos), ("is_cancelled", "=", 0)]
+		"Stock Ledger Entry",
+		fields=["item_code", "stock_value_difference"],
+		filters=[("voucher_no", "in", voucher_nos), ("is_cancelled", "=", 0)],
 	)
 	assign_item_groups_to_svd_list(svd_list)
 	return svd_list
@@ -99,7 +90,7 @@
 
 def get_leveled_dict() -> OrderedDict:
 	item_groups_dict = get_item_groups_dict()
-	lr_list = sorted(item_groups_dict, key=lambda x : int(x[0]))
+	lr_list = sorted(item_groups_dict, key=lambda x: int(x[0]))
 	leveled_dict = OrderedDict()
 	current_level = 0
 	nesting_r = []
@@ -108,10 +99,10 @@
 			nesting_r.pop()
 			current_level -= 1
 
-		leveled_dict[(l,r)] = {
-			'level' : current_level,
-			'name' : item_groups_dict[(l,r)]['name'],
-			'is_group' : item_groups_dict[(l,r)]['is_group']
+		leveled_dict[(l, r)] = {
+			"level": current_level,
+			"name": item_groups_dict[(l, r)]["name"],
+			"is_group": item_groups_dict[(l, r)]["is_group"],
 		}
 
 		if int(r) - int(l) > 1:
@@ -123,38 +114,38 @@
 
 
 def assign_self_values(leveled_dict: OrderedDict, svd_list: SVDList) -> None:
-	key_dict = {v['name']:k for k, v in leveled_dict.items()}
+	key_dict = {v["name"]: k for k, v in leveled_dict.items()}
 	for item in svd_list:
 		key = key_dict[item.get("item_group")]
-		leveled_dict[key]['self_value'] += -item.get("stock_value_difference")
+		leveled_dict[key]["self_value"] += -item.get("stock_value_difference")
 
 
 def assign_agg_values(leveled_dict: OrderedDict) -> None:
 	keys = list(leveled_dict.keys())[::-1]
-	prev_level = leveled_dict[keys[-1]]['level']
+	prev_level = leveled_dict[keys[-1]]["level"]
 	accu = [0]
 	for k in keys[:-1]:
-		curr_level = leveled_dict[k]['level']
+		curr_level = leveled_dict[k]["level"]
 		if curr_level == prev_level:
-			accu[-1] += leveled_dict[k]['self_value']
-			leveled_dict[k]['agg_value'] = leveled_dict[k]['self_value']
+			accu[-1] += leveled_dict[k]["self_value"]
+			leveled_dict[k]["agg_value"] = leveled_dict[k]["self_value"]
 
 		elif curr_level > prev_level:
-			accu.append(leveled_dict[k]['self_value'])
-			leveled_dict[k]['agg_value'] = accu[-1]
+			accu.append(leveled_dict[k]["self_value"])
+			leveled_dict[k]["agg_value"] = accu[-1]
 
 		elif curr_level < prev_level:
-			accu[-1] += leveled_dict[k]['self_value']
-			leveled_dict[k]['agg_value'] = accu[-1]
+			accu[-1] += leveled_dict[k]["self_value"]
+			leveled_dict[k]["agg_value"] = accu[-1]
 
 		prev_level = curr_level
 
 	# root node
 	rk = keys[-1]
-	leveled_dict[rk]['agg_value'] = sum(accu) + leveled_dict[rk]['self_value']
+	leveled_dict[rk]["agg_value"] = sum(accu) + leveled_dict[rk]["self_value"]
 
 
-def get_row(name:str, value:float, is_bold:int, indent:int) -> Row:
+def get_row(name: str, value: float, is_bold: int, indent: int) -> Row:
 	item_group = name
 	if is_bold:
 		item_group = frappe.bold(item_group)
@@ -168,20 +159,20 @@
 
 
 def get_item_groups_map(svd_list: SVDList) -> Dict[str, str]:
-	item_codes = set(i['item_code'] for i in svd_list)
+	item_codes = set(i["item_code"] for i in svd_list)
 	ig_list = frappe.get_list(
-		'Item', fields=['item_code','item_group'],
-		filters=[('item_code', 'in', item_codes)]
+		"Item", fields=["item_code", "item_group"], filters=[("item_code", "in", item_codes)]
 	)
-	return {i['item_code']:i['item_group'] for i in ig_list}
+	return {i["item_code"]: i["item_group"] for i in ig_list}
 
 
 def get_item_groups_dict() -> ItemGroupsDict:
 	item_groups_list = frappe.get_all("Item Group", fields=("name", "is_group", "lft", "rgt"))
-	return {(i['lft'],i['rgt']):{'name':i['name'], 'is_group':i['is_group']}
-		for i in item_groups_list}
+	return {
+		(i["lft"], i["rgt"]): {"name": i["name"], "is_group": i["is_group"]} for i in item_groups_list
+	}
 
 
 def update_leveled_dict(leveled_dict: OrderedDict) -> None:
 	for k in leveled_dict:
-		leveled_dict[k].update({'self_value':0, 'agg_value':0})
+		leveled_dict[k].update({"self_value": 0, "agg_value": 0})
diff --git a/erpnext/stock/report/delayed_item_report/delayed_item_report.py b/erpnext/stock/report/delayed_item_report/delayed_item_report.py
index 4ec36ea..9df24d6 100644
--- a/erpnext/stock/report/delayed_item_report/delayed_item_report.py
+++ b/erpnext/stock/report/delayed_item_report/delayed_item_report.py
@@ -7,11 +7,12 @@
 from frappe.utils import date_diff
 
 
-def execute(filters=None, consolidated = False):
+def execute(filters=None, consolidated=False):
 	data, columns = DelayedItemReport(filters).run()
 
 	return data, columns
 
+
 class DelayedItemReport(object):
 	def __init__(self, filters=None):
 		self.filters = frappe._dict(filters or {})
@@ -23,28 +24,38 @@
 		conditions = ""
 
 		doctype = self.filters.get("based_on")
-		child_doc= "%s Item" % doctype
+		child_doc = "%s Item" % doctype
 
 		if doctype == "Sales Invoice":
 			conditions = " and `tabSales Invoice`.update_stock = 1 and `tabSales Invoice`.is_pos = 0"
 
 		if self.filters.get("item_group"):
-			conditions += " and `tab%s`.item_group = %s" % (child_doc,
-				frappe.db.escape(self.filters.get("item_group")))
+			conditions += " and `tab%s`.item_group = %s" % (
+				child_doc,
+				frappe.db.escape(self.filters.get("item_group")),
+			)
 
 		for field in ["customer", "customer_group", "company"]:
 			if self.filters.get(field):
-				conditions += " and `tab%s`.%s = %s" % (doctype,
-					field, frappe.db.escape(self.filters.get(field)))
+				conditions += " and `tab%s`.%s = %s" % (
+					doctype,
+					field,
+					frappe.db.escape(self.filters.get(field)),
+				)
 
 		sales_order_field = "against_sales_order"
 		if doctype == "Sales Invoice":
 			sales_order_field = "sales_order"
 
 		if self.filters.get("sales_order"):
-			conditions = " and `tab%s`.%s = '%s'" %(child_doc, sales_order_field, self.filters.get("sales_order"))
+			conditions = " and `tab%s`.%s = '%s'" % (
+				child_doc,
+				sales_order_field,
+				self.filters.get("sales_order"),
+			)
 
-		self.transactions = frappe.db.sql(""" SELECT `tab{child_doc}`.item_code, `tab{child_doc}`.item_name,
+		self.transactions = frappe.db.sql(
+			""" SELECT `tab{child_doc}`.item_code, `tab{child_doc}`.item_name,
 				`tab{child_doc}`.item_group, `tab{child_doc}`.qty, `tab{child_doc}`.rate, `tab{child_doc}`.amount,
 				`tab{child_doc}`.so_detail, `tab{child_doc}`.{so_field} as sales_order,
 				`tab{doctype}`.shipping_address_name, `tab{doctype}`.po_no, `tab{doctype}`.customer,
@@ -54,10 +65,12 @@
 				`tab{child_doc}`.parent = `tab{doctype}`.name and `tab{doctype}`.docstatus = 1 and
 				`tab{doctype}`.posting_date between %(from_date)s and %(to_date)s and
 				`tab{child_doc}`.{so_field} is not null and `tab{child_doc}`.{so_field} != '' {cond}
-		""".format(cond=conditions, doctype=doctype, child_doc=child_doc, so_field=sales_order_field), {
-			'from_date': self.filters.get('from_date'),
-			'to_date': self.filters.get('to_date')
-		}, as_dict=1)
+		""".format(
+				cond=conditions, doctype=doctype, child_doc=child_doc, so_field=sales_order_field
+			),
+			{"from_date": self.filters.get("from_date"), "to_date": self.filters.get("to_date")},
+			as_dict=1,
+		)
 
 		if self.transactions:
 			self.filter_transactions_data(consolidated)
@@ -67,112 +80,85 @@
 	def filter_transactions_data(self, consolidated=False):
 		sales_orders = [d.sales_order for d in self.transactions]
 		doctype = "Sales Order"
-		filters = {'name': ('in', sales_orders)}
+		filters = {"name": ("in", sales_orders)}
 
 		if not consolidated:
 			sales_order_items = [d.so_detail for d in self.transactions]
 			doctype = "Sales Order Item"
-			filters = {'parent': ('in', sales_orders), 'name': ('in', sales_order_items)}
+			filters = {"parent": ("in", sales_orders), "name": ("in", sales_order_items)}
 
 		so_data = {}
-		for d in frappe.get_all(doctype, filters = filters,
-			fields = ["delivery_date", "parent", "name"]):
+		for d in frappe.get_all(doctype, filters=filters, fields=["delivery_date", "parent", "name"]):
 			key = d.name if consolidated else (d.parent, d.name)
 			if key not in so_data:
 				so_data.setdefault(key, d.delivery_date)
 
 		for row in self.transactions:
 			key = row.sales_order if consolidated else (row.sales_order, row.so_detail)
-			row.update({
-				'delivery_date': so_data.get(key),
-				'delayed_days': date_diff(row.posting_date, so_data.get(key))
-			})
+			row.update(
+				{
+					"delivery_date": so_data.get(key),
+					"delayed_days": date_diff(row.posting_date, so_data.get(key)),
+				}
+			)
 
 		return self.transactions
 
 	def get_columns(self):
 		based_on = self.filters.get("based_on")
 
-		return [{
-			"label": _(based_on),
-			"fieldname": "name",
-			"fieldtype": "Link",
-			"options": based_on,
-			"width": 100
-		},
-		{
-			"label": _("Customer"),
-			"fieldname": "customer",
-			"fieldtype": "Link",
-			"options": "Customer",
-			"width": 200
-		},
-		{
-			"label": _("Shipping Address"),
-			"fieldname": "shipping_address_name",
-			"fieldtype": "Link",
-			"options": "Address",
-			"width": 120
-		},
-		{
-			"label": _("Expected Delivery Date"),
-			"fieldname": "delivery_date",
-			"fieldtype": "Date",
-			"width": 100
-		},
-		{
-			"label": _("Actual Delivery Date"),
-			"fieldname": "posting_date",
-			"fieldtype": "Date",
-			"width": 100
-		},
-		{
-			"label": _("Item Code"),
-			"fieldname": "item_code",
-			"fieldtype": "Link",
-			"options": "Item",
-			"width": 100
-		},
-		{
-			"label": _("Item Name"),
-			"fieldname": "item_name",
-			"fieldtype": "Data",
-			"width": 100
-		},
-		{
-			"label": _("Quantity"),
-			"fieldname": "qty",
-			"fieldtype": "Float",
-			"width": 100
-		},
-		{
-			"label": _("Rate"),
-			"fieldname": "rate",
-			"fieldtype": "Currency",
-			"width": 100
-		},
-		{
-			"label": _("Amount"),
-			"fieldname": "amount",
-			"fieldtype": "Currency",
-			"width": 100
-		},
-		{
-			"label": _("Delayed Days"),
-			"fieldname": "delayed_days",
-			"fieldtype": "Int",
-			"width": 100
-		},
-		{
-			"label": _("Sales Order"),
-			"fieldname": "sales_order",
-			"fieldtype": "Link",
-			"options": "Sales Order",
-			"width": 100
-		},
-		{
-			"label": _("Customer PO"),
-			"fieldname": "po_no",
-			"fieldtype": "Data",
-			"width": 100
-		}]
+		return [
+			{
+				"label": _(based_on),
+				"fieldname": "name",
+				"fieldtype": "Link",
+				"options": based_on,
+				"width": 100,
+			},
+			{
+				"label": _("Customer"),
+				"fieldname": "customer",
+				"fieldtype": "Link",
+				"options": "Customer",
+				"width": 200,
+			},
+			{
+				"label": _("Shipping Address"),
+				"fieldname": "shipping_address_name",
+				"fieldtype": "Link",
+				"options": "Address",
+				"width": 120,
+			},
+			{
+				"label": _("Expected Delivery Date"),
+				"fieldname": "delivery_date",
+				"fieldtype": "Date",
+				"width": 100,
+			},
+			{
+				"label": _("Actual Delivery Date"),
+				"fieldname": "posting_date",
+				"fieldtype": "Date",
+				"width": 100,
+			},
+			{
+				"label": _("Item Code"),
+				"fieldname": "item_code",
+				"fieldtype": "Link",
+				"options": "Item",
+				"width": 100,
+			},
+			{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 100},
+			{"label": _("Quantity"), "fieldname": "qty", "fieldtype": "Float", "width": 100},
+			{"label": _("Rate"), "fieldname": "rate", "fieldtype": "Currency", "width": 100},
+			{"label": _("Amount"), "fieldname": "amount", "fieldtype": "Currency", "width": 100},
+			{"label": _("Delayed Days"), "fieldname": "delayed_days", "fieldtype": "Int", "width": 100},
+			{
+				"label": _("Sales Order"),
+				"fieldname": "sales_order",
+				"fieldtype": "Link",
+				"options": "Sales Order",
+				"width": 100,
+			},
+			{"label": _("Customer PO"), "fieldname": "po_no", "fieldtype": "Data", "width": 100},
+		]
diff --git a/erpnext/stock/report/delayed_order_report/delayed_order_report.py b/erpnext/stock/report/delayed_order_report/delayed_order_report.py
index 26090ab..197218d 100644
--- a/erpnext/stock/report/delayed_order_report/delayed_order_report.py
+++ b/erpnext/stock/report/delayed_order_report/delayed_order_report.py
@@ -14,6 +14,7 @@
 
 	return columns, data
 
+
 class DelayedOrderReport(DelayedItemReport):
 	def run(self):
 		return self.get_columns(), self.get_data(consolidated=True) or []
@@ -33,60 +34,48 @@
 	def get_columns(self):
 		based_on = self.filters.get("based_on")
 
-		return [{
-			"label": _(based_on),
-			"fieldname": "name",
-			"fieldtype": "Link",
-			"options": based_on,
-			"width": 100
-		},{
-			"label": _("Customer"),
-			"fieldname": "customer",
-			"fieldtype": "Link",
-			"options": "Customer",
-			"width": 200
-		},
-		{
-			"label": _("Shipping Address"),
-			"fieldname": "shipping_address_name",
-			"fieldtype": "Link",
-			"options": "Address",
-			"width": 140
-		},
-		{
-			"label": _("Expected Delivery Date"),
-			"fieldname": "delivery_date",
-			"fieldtype": "Date",
-			"width": 100
-		},
-		{
-			"label": _("Actual Delivery Date"),
-			"fieldname": "posting_date",
-			"fieldtype": "Date",
-			"width": 100
-		},
-		{
-			"label": _("Amount"),
-			"fieldname": "grand_total",
-			"fieldtype": "Currency",
-			"width": 100
-		},
-		{
-			"label": _("Delayed Days"),
-			"fieldname": "delayed_days",
-			"fieldtype": "Int",
-			"width": 100
-		},
-		{
-			"label": _("Sales Order"),
-			"fieldname": "sales_order",
-			"fieldtype": "Link",
-			"options": "Sales Order",
-			"width": 150
-		},
-		{
-			"label": _("Customer PO"),
-			"fieldname": "po_no",
-			"fieldtype": "Data",
-			"width": 110
-		}]
+		return [
+			{
+				"label": _(based_on),
+				"fieldname": "name",
+				"fieldtype": "Link",
+				"options": based_on,
+				"width": 100,
+			},
+			{
+				"label": _("Customer"),
+				"fieldname": "customer",
+				"fieldtype": "Link",
+				"options": "Customer",
+				"width": 200,
+			},
+			{
+				"label": _("Shipping Address"),
+				"fieldname": "shipping_address_name",
+				"fieldtype": "Link",
+				"options": "Address",
+				"width": 140,
+			},
+			{
+				"label": _("Expected Delivery Date"),
+				"fieldname": "delivery_date",
+				"fieldtype": "Date",
+				"width": 100,
+			},
+			{
+				"label": _("Actual Delivery Date"),
+				"fieldname": "posting_date",
+				"fieldtype": "Date",
+				"width": 100,
+			},
+			{"label": _("Amount"), "fieldname": "grand_total", "fieldtype": "Currency", "width": 100},
+			{"label": _("Delayed Days"), "fieldname": "delayed_days", "fieldtype": "Int", "width": 100},
+			{
+				"label": _("Sales Order"),
+				"fieldname": "sales_order",
+				"fieldtype": "Link",
+				"options": "Sales Order",
+				"width": 150,
+			},
+			{"label": _("Customer PO"), "fieldname": "po_no", "fieldtype": "Data", "width": 110},
+		]
diff --git a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py
index b7ac7ff..7a1b8c0 100644
--- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py
+++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py
@@ -8,7 +8,8 @@
 
 
 def execute(filters=None):
-	if not filters: filters ={}
+	if not filters:
+		filters = {}
 	data = []
 	conditions = get_columns(filters, "Delivery Note")
 	data = get_data(filters, conditions)
@@ -17,6 +18,7 @@
 
 	return conditions["columns"], data, None, chart_data
 
+
 def get_chart_data(data, filters):
 	if not data:
 		return []
@@ -27,7 +29,7 @@
 		# consider only consolidated row
 		data = [row for row in data if row[0]]
 
-	data = sorted(data, key = lambda i: i[-1],reverse=True)
+	data = sorted(data, key=lambda i: i[-1], reverse=True)
 
 	if len(data) > 10:
 		# get top 10 if data too long
@@ -39,13 +41,8 @@
 
 	return {
 		"data": {
-			"labels" : labels,
-			"datasets" : [
-				{
-				"name": _("Total Delivered Amount"),
-				"values": datapoints
-				}
-			]
+			"labels": labels,
+			"datasets": [{"name": _("Total Delivered Amount"), "values": datapoints}],
 		},
-		"type" : "bar"
+		"type": "bar",
 	}
diff --git a/erpnext/education/doctype/__init__.py b/erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/__init__.py
similarity index 100%
copy from erpnext/education/doctype/__init__.py
copy to erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/__init__.py
diff --git a/erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.js b/erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.js
new file mode 100644
index 0000000..0b8f496
--- /dev/null
+++ b/erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.js
@@ -0,0 +1,53 @@
+// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+const DIFFERNCE_FIELD_NAMES = [
+	"fifo_qty_diff",
+	"fifo_value_diff",
+];
+
+frappe.query_reports["FIFO Queue vs Qty After Transaction Comparison"] = {
+	"filters": [
+		{
+			"fieldname": "item_code",
+			"fieldtype": "Link",
+			"label": "Item",
+			"options": "Item",
+			get_query: function() {
+				return {
+					filters: {is_stock_item: 1, has_serial_no: 0}
+				}
+			}
+		},
+		{
+			"fieldname": "item_group",
+			"fieldtype": "Link",
+			"label": "Item Group",
+			"options": "Item Group",
+		},
+		{
+			"fieldname": "warehouse",
+			"fieldtype": "Link",
+			"label": "Warehouse",
+			"options": "Warehouse",
+		},
+		{
+			"fieldname": "from_date",
+			"fieldtype": "Date",
+			"label": "From Posting Date",
+		},
+		{
+			"fieldname": "to_date",
+			"fieldtype": "Date",
+			"label": "From Posting Date",
+		}
+	],
+	formatter (value, row, column, data, default_formatter) {
+		value = default_formatter(value, row, column, data);
+		if (DIFFERNCE_FIELD_NAMES.includes(column.fieldname) && Math.abs(data[column.fieldname]) > 0.001) {
+			value = "<span style='color:red'>" + value + "</span>";
+		}
+		return value;
+	},
+};
diff --git a/erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.json b/erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.json
new file mode 100644
index 0000000..5e958aa
--- /dev/null
+++ b/erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.json
@@ -0,0 +1,27 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2022-05-11 04:09:13.460652",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "letter_head": "abc",
+ "modified": "2022-05-11 04:09:20.232177",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "FIFO Queue vs Qty After Transaction Comparison",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Stock Ledger Entry",
+ "report_name": "FIFO Queue vs Qty After Transaction Comparison",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "Administrator"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.py b/erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.py
new file mode 100644
index 0000000..9e14033
--- /dev/null
+++ b/erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.py
@@ -0,0 +1,212 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import json
+
+import frappe
+from frappe import _
+from frappe.utils import flt
+from frappe.utils.nestedset import get_descendants_of
+
+SLE_FIELDS = (
+	"name",
+	"item_code",
+	"warehouse",
+	"posting_date",
+	"posting_time",
+	"creation",
+	"voucher_type",
+	"voucher_no",
+	"actual_qty",
+	"qty_after_transaction",
+	"stock_queue",
+	"batch_no",
+	"stock_value",
+	"valuation_rate",
+)
+
+
+def execute(filters=None):
+	columns = get_columns()
+	data = get_data(filters)
+	return columns, data
+
+
+def get_data(filters):
+	if not any([filters.warehouse, filters.item_code, filters.item_group]):
+		frappe.throw(_("Any one of following filters required: warehouse, Item Code, Item Group"))
+	sles = get_stock_ledger_entries(filters)
+	return find_first_bad_queue(sles)
+
+
+def get_stock_ledger_entries(filters):
+
+	sle_filters = {"is_cancelled": 0}
+
+	if filters.warehouse:
+		children = get_descendants_of("Warehouse", filters.warehouse)
+		sle_filters["warehouse"] = ("in", children + [filters.warehouse])
+
+	if filters.item_code:
+		sle_filters["item_code"] = filters.item_code
+	elif filters.get("item_group"):
+		item_group = filters.get("item_group")
+		children = get_descendants_of("Item Group", item_group)
+		item_group_filter = {"item_group": ("in", children + [item_group])}
+		sle_filters["item_code"] = (
+			"in",
+			frappe.get_all("Item", filters=item_group_filter, pluck="name", order_by=None),
+		)
+
+	if filters.from_date:
+		sle_filters["posting_date"] = (">=", filters.from_date)
+	if filters.to_date:
+		sle_filters["posting_date"] = ("<=", filters.to_date)
+
+	return frappe.get_all(
+		"Stock Ledger Entry",
+		fields=SLE_FIELDS,
+		filters=sle_filters,
+		order_by="timestamp(posting_date, posting_time), creation",
+	)
+
+
+def find_first_bad_queue(sles):
+	item_warehouse_sles = {}
+	for sle in sles:
+		item_warehouse_sles.setdefault((sle.item_code, sle.warehouse), []).append(sle)
+
+	data = []
+
+	for _item_wh, sles in item_warehouse_sles.items():
+		for idx, sle in enumerate(sles):
+			queue = json.loads(sle.stock_queue or "[]")
+
+			sle.fifo_queue_qty = 0.0
+			sle.fifo_stock_value = 0.0
+			for qty, rate in queue:
+				sle.fifo_queue_qty += flt(qty)
+				sle.fifo_stock_value += flt(qty) * flt(rate)
+
+			sle.fifo_qty_diff = sle.qty_after_transaction - sle.fifo_queue_qty
+			sle.fifo_value_diff = sle.stock_value - sle.fifo_stock_value
+
+			if sle.batch_no:
+				sle.use_batchwise_valuation = frappe.db.get_value(
+					"Batch", sle.batch_no, "use_batchwise_valuation", cache=True
+				)
+
+			if abs(sle.fifo_qty_diff) > 0.001 or abs(sle.fifo_value_diff) > 0.1:
+				if idx:
+					data.append(sles[idx - 1])
+				data.append(sle)
+				data.append({})
+				break
+
+	return data
+
+
+def get_columns():
+	return [
+		{
+			"fieldname": "name",
+			"fieldtype": "Link",
+			"label": _("Stock Ledger Entry"),
+			"options": "Stock Ledger Entry",
+		},
+		{
+			"fieldname": "item_code",
+			"fieldtype": "Link",
+			"label": _("Item Code"),
+			"options": "Item",
+		},
+		{
+			"fieldname": "warehouse",
+			"fieldtype": "Link",
+			"label": _("Warehouse"),
+			"options": "Warehouse",
+		},
+		{
+			"fieldname": "posting_date",
+			"fieldtype": "Data",
+			"label": _("Posting Date"),
+		},
+		{
+			"fieldname": "posting_time",
+			"fieldtype": "Data",
+			"label": _("Posting Time"),
+		},
+		{
+			"fieldname": "creation",
+			"fieldtype": "Data",
+			"label": _("Creation"),
+		},
+		{
+			"fieldname": "voucher_type",
+			"fieldtype": "Link",
+			"label": _("Voucher Type"),
+			"options": "DocType",
+		},
+		{
+			"fieldname": "voucher_no",
+			"fieldtype": "Dynamic Link",
+			"label": _("Voucher No"),
+			"options": "voucher_type",
+		},
+		{
+			"fieldname": "batch_no",
+			"fieldtype": "Link",
+			"label": _("Batch"),
+			"options": "Batch",
+		},
+		{
+			"fieldname": "use_batchwise_valuation",
+			"fieldtype": "Check",
+			"label": _("Batchwise Valuation"),
+		},
+		{
+			"fieldname": "actual_qty",
+			"fieldtype": "Float",
+			"label": _("Qty Change"),
+		},
+		{
+			"fieldname": "qty_after_transaction",
+			"fieldtype": "Float",
+			"label": _("(A) Qty After Transaction"),
+		},
+		{
+			"fieldname": "stock_queue",
+			"fieldtype": "Data",
+			"label": _("FIFO/LIFO Queue"),
+		},
+		{
+			"fieldname": "fifo_queue_qty",
+			"fieldtype": "Float",
+			"label": _("(C) Total qty in queue"),
+		},
+		{
+			"fieldname": "fifo_qty_diff",
+			"fieldtype": "Float",
+			"label": _("A - C"),
+		},
+		{
+			"fieldname": "stock_value",
+			"fieldtype": "Float",
+			"label": _("(D) Balance Stock Value"),
+		},
+		{
+			"fieldname": "fifo_stock_value",
+			"fieldtype": "Float",
+			"label": _("(E) Balance Stock Value in Queue"),
+		},
+		{
+			"fieldname": "fifo_value_diff",
+			"fieldtype": "Float",
+			"label": _("D - E"),
+		},
+		{
+			"fieldname": "valuation_rate",
+			"fieldtype": "Float",
+			"label": _("(H) Valuation Rate"),
+		},
+	]
diff --git a/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.py b/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.py
index 6aa12ac..b68db35 100644
--- a/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.py
+++ b/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.py
@@ -12,6 +12,7 @@
 	data = get_data(filters)
 	return columns, data
 
+
 def get_data(filters):
 	data = get_stock_ledger_entries(filters)
 	itewise_balance_qty = {}
@@ -23,6 +24,7 @@
 	res = validate_data(itewise_balance_qty)
 	return res
 
+
 def validate_data(itewise_balance_qty):
 	res = []
 	for key, data in itewise_balance_qty.items():
@@ -33,6 +35,7 @@
 
 	return res
 
+
 def get_incorrect_data(data):
 	balance_qty = 0.0
 	for row in data:
@@ -45,67 +48,84 @@
 			row.differnce = abs(flt(row.expected_balance_qty) - flt(row.qty_after_transaction))
 			return row
 
+
 def get_stock_ledger_entries(report_filters):
 	filters = {"is_cancelled": 0}
-	fields = ['name', 'voucher_type', 'voucher_no', 'item_code', 'actual_qty',
-		'posting_date', 'posting_time', 'company', 'warehouse', 'qty_after_transaction', 'batch_no']
+	fields = [
+		"name",
+		"voucher_type",
+		"voucher_no",
+		"item_code",
+		"actual_qty",
+		"posting_date",
+		"posting_time",
+		"company",
+		"warehouse",
+		"qty_after_transaction",
+		"batch_no",
+	]
 
-	for field in ['warehouse', 'item_code', 'company']:
+	for field in ["warehouse", "item_code", "company"]:
 		if report_filters.get(field):
 			filters[field] = report_filters.get(field)
 
-	return frappe.get_all('Stock Ledger Entry', fields = fields, filters = filters,
-		order_by = 'timestamp(posting_date, posting_time) asc, creation asc')
+	return frappe.get_all(
+		"Stock Ledger Entry",
+		fields=fields,
+		filters=filters,
+		order_by="posting_date asc, posting_time asc, creation asc",
+	)
+
 
 def get_columns():
-	return [{
-		'label': _('Id'),
-		'fieldtype': 'Link',
-		'fieldname': 'name',
-		'options': 'Stock Ledger Entry',
-		'width': 120
-	}, {
-		'label': _('Posting Date'),
-		'fieldtype': 'Date',
-		'fieldname': 'posting_date',
-		'width': 110
-	}, {
-		'label': _('Voucher Type'),
-		'fieldtype': 'Link',
-		'fieldname': 'voucher_type',
-		'options': 'DocType',
-		'width': 120
-	}, {
-		'label': _('Voucher No'),
-		'fieldtype': 'Dynamic Link',
-		'fieldname': 'voucher_no',
-		'options': 'voucher_type',
-		'width': 120
-	}, {
-		'label': _('Item Code'),
-		'fieldtype': 'Link',
-		'fieldname': 'item_code',
-		'options': 'Item',
-		'width': 120
-	}, {
-		'label': _('Warehouse'),
-		'fieldtype': 'Link',
-		'fieldname': 'warehouse',
-		'options': 'Warehouse',
-		'width': 120
-	}, {
-		'label': _('Expected Balance Qty'),
-		'fieldtype': 'Float',
-		'fieldname': 'expected_balance_qty',
-		'width': 170
-	}, {
-		'label': _('Actual Balance Qty'),
-		'fieldtype': 'Float',
-		'fieldname': 'qty_after_transaction',
-		'width': 150
-	}, {
-		'label': _('Difference'),
-		'fieldtype': 'Float',
-		'fieldname': 'differnce',
-		'width': 110
-	}]
+	return [
+		{
+			"label": _("Id"),
+			"fieldtype": "Link",
+			"fieldname": "name",
+			"options": "Stock Ledger Entry",
+			"width": 120,
+		},
+		{"label": _("Posting Date"), "fieldtype": "Date", "fieldname": "posting_date", "width": 110},
+		{
+			"label": _("Voucher Type"),
+			"fieldtype": "Link",
+			"fieldname": "voucher_type",
+			"options": "DocType",
+			"width": 120,
+		},
+		{
+			"label": _("Voucher No"),
+			"fieldtype": "Dynamic Link",
+			"fieldname": "voucher_no",
+			"options": "voucher_type",
+			"width": 120,
+		},
+		{
+			"label": _("Item Code"),
+			"fieldtype": "Link",
+			"fieldname": "item_code",
+			"options": "Item",
+			"width": 120,
+		},
+		{
+			"label": _("Warehouse"),
+			"fieldtype": "Link",
+			"fieldname": "warehouse",
+			"options": "Warehouse",
+			"width": 120,
+		},
+		{
+			"label": _("Expected Balance Qty"),
+			"fieldtype": "Float",
+			"fieldname": "expected_balance_qty",
+			"width": 170,
+		},
+		{
+			"label": _("Actual Balance Qty"),
+			"fieldtype": "Float",
+			"fieldname": "qty_after_transaction",
+			"width": 150,
+		},
+		{"label": _("Difference"), "fieldtype": "Float", "fieldname": "differnce", "width": 110},
+	]
diff --git a/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py
index be8597d..39d84a7 100644
--- a/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py
+++ b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py
@@ -15,6 +15,7 @@
 	data = get_data(filters)
 	return columns, data
 
+
 def get_data(filters):
 	data = get_stock_ledger_entries(filters)
 	serial_nos_data = prepare_serial_nos(data)
@@ -22,6 +23,7 @@
 
 	return data
 
+
 def prepare_serial_nos(data):
 	serial_no_wise_data = {}
 	for row in data:
@@ -37,13 +39,16 @@
 
 	return serial_no_wise_data
 
+
 def get_incorrect_serial_nos(serial_nos_data):
 	result = []
 
-	total_value = frappe._dict({'qty': 0, 'valuation_rate': 0, 'serial_no': frappe.bold(_('Balance'))})
+	total_value = frappe._dict(
+		{"qty": 0, "valuation_rate": 0, "serial_no": frappe.bold(_("Balance"))}
+	)
 
 	for serial_no, data in serial_nos_data.items():
-		total_dict = frappe._dict({'qty': 0, 'valuation_rate': 0, 'serial_no': frappe.bold(_('Total'))})
+		total_dict = frappe._dict({"qty": 0, "valuation_rate": 0, "serial_no": frappe.bold(_("Total"))})
 
 		if check_incorrect_serial_data(data, total_dict):
 			result.extend(data)
@@ -58,93 +63,111 @@
 
 	return result
 
+
 def check_incorrect_serial_data(data, total_dict):
 	incorrect_data = False
 	for row in data:
 		total_dict.qty += row.qty
 		total_dict.valuation_rate += row.valuation_rate
 
-		if ((total_dict.qty == 0 and abs(total_dict.valuation_rate) > 0) or total_dict.qty < 0):
+		if (total_dict.qty == 0 and abs(total_dict.valuation_rate) > 0) or total_dict.qty < 0:
 			incorrect_data = True
 
 	return incorrect_data
 
+
 def get_stock_ledger_entries(report_filters):
-	fields = ['name', 'voucher_type', 'voucher_no', 'item_code', 'serial_no as serial_nos', 'actual_qty',
-		'posting_date', 'posting_time', 'company', 'warehouse', '(stock_value_difference / actual_qty) as valuation_rate']
+	fields = [
+		"name",
+		"voucher_type",
+		"voucher_no",
+		"item_code",
+		"serial_no as serial_nos",
+		"actual_qty",
+		"posting_date",
+		"posting_time",
+		"company",
+		"warehouse",
+		"(stock_value_difference / actual_qty) as valuation_rate",
+	]
 
-	filters = {'serial_no': ("is", "set"), "is_cancelled": 0}
+	filters = {"serial_no": ("is", "set"), "is_cancelled": 0}
 
-	if report_filters.get('item_code'):
-		filters['item_code'] = report_filters.get('item_code')
+	if report_filters.get("item_code"):
+		filters["item_code"] = report_filters.get("item_code")
 
-	if report_filters.get('from_date') and report_filters.get('to_date'):
-		filters['posting_date'] = ('between', [report_filters.get('from_date'), report_filters.get('to_date')])
+	if report_filters.get("from_date") and report_filters.get("to_date"):
+		filters["posting_date"] = (
+			"between",
+			[report_filters.get("from_date"), report_filters.get("to_date")],
+		)
 
-	return frappe.get_all('Stock Ledger Entry', fields = fields, filters = filters,
-		order_by = 'timestamp(posting_date, posting_time) asc, creation asc')
+	return frappe.get_all(
+		"Stock Ledger Entry",
+		fields=fields,
+		filters=filters,
+		order_by="posting_date asc, posting_time asc, creation asc",
+	)
+
 
 def get_columns():
-	return [{
-		'label': _('Company'),
-		'fieldtype': 'Link',
-		'fieldname': 'company',
-		'options': 'Company',
-		'width': 120
-	}, {
-		'label': _('Id'),
-		'fieldtype': 'Link',
-		'fieldname': 'name',
-		'options': 'Stock Ledger Entry',
-		'width': 120
-	}, {
-		'label': _('Posting Date'),
-		'fieldtype': 'Date',
-		'fieldname': 'posting_date',
-		'width': 90
-	}, {
-		'label': _('Posting Time'),
-		'fieldtype': 'Time',
-		'fieldname': 'posting_time',
-		'width': 90
-	}, {
-		'label': _('Voucher Type'),
-		'fieldtype': 'Link',
-		'fieldname': 'voucher_type',
-		'options': 'DocType',
-		'width': 100
-	}, {
-		'label': _('Voucher No'),
-		'fieldtype': 'Dynamic Link',
-		'fieldname': 'voucher_no',
-		'options': 'voucher_type',
-		'width': 110
-	}, {
-		'label': _('Item Code'),
-		'fieldtype': 'Link',
-		'fieldname': 'item_code',
-		'options': 'Item',
-		'width': 120
-	}, {
-		'label': _('Warehouse'),
-		'fieldtype': 'Link',
-		'fieldname': 'warehouse',
-		'options': 'Warehouse',
-		'width': 120
-	}, {
-		'label': _('Serial No'),
-		'fieldtype': 'Link',
-		'fieldname': 'serial_no',
-		'options': 'Serial No',
-		'width': 100
-	}, {
-		'label': _('Qty'),
-		'fieldtype': 'Float',
-		'fieldname': 'qty',
-		'width': 80
-	}, {
-		'label': _('Valuation Rate (In / Out)'),
-		'fieldtype': 'Currency',
-		'fieldname': 'valuation_rate',
-		'width': 110
-	}]
+	return [
+		{
+			"label": _("Company"),
+			"fieldtype": "Link",
+			"fieldname": "company",
+			"options": "Company",
+			"width": 120,
+		},
+		{
+			"label": _("Id"),
+			"fieldtype": "Link",
+			"fieldname": "name",
+			"options": "Stock Ledger Entry",
+			"width": 120,
+		},
+		{"label": _("Posting Date"), "fieldtype": "Date", "fieldname": "posting_date", "width": 90},
+		{"label": _("Posting Time"), "fieldtype": "Time", "fieldname": "posting_time", "width": 90},
+		{
+			"label": _("Voucher Type"),
+			"fieldtype": "Link",
+			"fieldname": "voucher_type",
+			"options": "DocType",
+			"width": 100,
+		},
+		{
+			"label": _("Voucher No"),
+			"fieldtype": "Dynamic Link",
+			"fieldname": "voucher_no",
+			"options": "voucher_type",
+			"width": 110,
+		},
+		{
+			"label": _("Item Code"),
+			"fieldtype": "Link",
+			"fieldname": "item_code",
+			"options": "Item",
+			"width": 120,
+		},
+		{
+			"label": _("Warehouse"),
+			"fieldtype": "Link",
+			"fieldname": "warehouse",
+			"options": "Warehouse",
+			"width": 120,
+		},
+		{
+			"label": _("Serial No"),
+			"fieldtype": "Link",
+			"fieldname": "serial_no",
+			"options": "Serial No",
+			"width": 100,
+		},
+		{"label": _("Qty"), "fieldtype": "Float", "fieldname": "qty", "width": 80},
+		{
+			"label": _("Valuation Rate (In / Out)"),
+			"fieldtype": "Currency",
+			"fieldname": "valuation_rate",
+			"width": 110,
+		},
+	]
diff --git a/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py
index 28e6cb2..23e3c8a 100644
--- a/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py
+++ b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py
@@ -13,14 +13,18 @@
 
 def execute(filters=None):
 	if not erpnext.is_perpetual_inventory_enabled(filters.company):
-		frappe.throw(_("Perpetual inventory required for the company {0} to view this report.")
-			.format(filters.company))
+		frappe.throw(
+			_("Perpetual inventory required for the company {0} to view this report.").format(
+				filters.company
+			)
+		)
 
 	data = get_data(filters)
 	columns = get_columns(filters)
 
 	return columns, data
 
+
 def get_unsync_date(filters):
 	date = filters.from_date
 	if not date:
@@ -31,14 +35,16 @@
 		return
 
 	while getdate(date) < getdate(today()):
-		account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(posting_date=date,
-			company=filters.company, account = filters.account)
+		account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(
+			posting_date=date, company=filters.company, account=filters.account
+		)
 
 		if abs(account_bal - stock_bal) > 0.1:
 			return date
 
 		date = add_days(date, 1)
 
+
 def get_data(report_filters):
 	from_date = get_unsync_date(report_filters)
 
@@ -48,7 +54,8 @@
 	result = []
 
 	voucher_wise_dict = {}
-	data = frappe.db.sql('''
+	data = frappe.db.sql(
+		"""
 			SELECT
 				name, posting_date, posting_time, voucher_type, voucher_no,
 				stock_value_difference, stock_value, warehouse, item_code
@@ -59,14 +66,19 @@
 				= %s and company = %s
 				and is_cancelled = 0
 			ORDER BY timestamp(posting_date, posting_time) asc, creation asc
-		''', (from_date, report_filters.company), as_dict=1)
+		""",
+		(from_date, report_filters.company),
+		as_dict=1,
+	)
 
 	for d in data:
 		voucher_wise_dict.setdefault((d.item_code, d.warehouse), []).append(d)
 
 	closing_date = add_days(from_date, -1)
 	for key, stock_data in voucher_wise_dict.items():
-		prev_stock_value = get_stock_value_on(posting_date = closing_date, item_code=key[0], warehouse =key[1])
+		prev_stock_value = get_stock_value_on(
+			posting_date=closing_date, item_code=key[0], warehouse=key[1]
+		)
 		for data in stock_data:
 			expected_stock_value = prev_stock_value + data.stock_value_difference
 			if abs(data.stock_value - expected_stock_value) > 0.1:
@@ -76,6 +88,7 @@
 
 	return result
 
+
 def get_columns(filters):
 	return [
 		{
@@ -83,60 +96,43 @@
 			"fieldname": "name",
 			"fieldtype": "Link",
 			"options": "Stock Ledger Entry",
-			"width": "80"
+			"width": "80",
 		},
-		{
-			"label": _("Posting Date"),
-			"fieldname": "posting_date",
-			"fieldtype": "Date"
-		},
-		{
-			"label": _("Posting Time"),
-			"fieldname": "posting_time",
-			"fieldtype": "Time"
-		},
-		{
-			"label": _("Voucher Type"),
-			"fieldname": "voucher_type",
-			"width": "110"
-		},
+		{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date"},
+		{"label": _("Posting Time"), "fieldname": "posting_time", "fieldtype": "Time"},
+		{"label": _("Voucher Type"), "fieldname": "voucher_type", "width": "110"},
 		{
 			"label": _("Voucher No"),
 			"fieldname": "voucher_no",
 			"fieldtype": "Dynamic Link",
 			"options": "voucher_type",
-			"width": "110"
+			"width": "110",
 		},
 		{
 			"label": _("Item Code"),
 			"fieldname": "item_code",
 			"fieldtype": "Link",
 			"options": "Item",
-			"width": "110"
+			"width": "110",
 		},
 		{
 			"label": _("Warehouse"),
 			"fieldname": "warehouse",
 			"fieldtype": "Link",
 			"options": "Warehouse",
-			"width": "110"
+			"width": "110",
 		},
 		{
 			"label": _("Expected Stock Value"),
 			"fieldname": "expected_stock_value",
 			"fieldtype": "Currency",
-			"width": "150"
+			"width": "150",
 		},
-		{
-			"label": _("Stock Value"),
-			"fieldname": "stock_value",
-			"fieldtype": "Currency",
-			"width": "120"
-		},
+		{"label": _("Stock Value"), "fieldname": "stock_value", "fieldtype": "Currency", "width": "120"},
 		{
 			"label": _("Difference Value"),
 			"fieldname": "difference_value",
 			"fieldtype": "Currency",
-			"width": "150"
-		}
+			"width": "150",
+		},
 	]
diff --git a/erpnext/stock/report/item_price_stock/item_price_stock.py b/erpnext/stock/report/item_price_stock/item_price_stock.py
index 65af9f5..15218e6 100644
--- a/erpnext/stock/report/item_price_stock/item_price_stock.py
+++ b/erpnext/stock/report/item_price_stock/item_price_stock.py
@@ -7,10 +7,11 @@
 
 def execute(filters=None):
 	columns, data = [], []
-	columns=get_columns()
-	data=get_data(filters,columns)
+	columns = get_columns()
+	data = get_data(filters, columns)
 	return columns, data
 
+
 def get_columns():
 	return [
 		{
@@ -18,77 +19,64 @@
 			"fieldname": "item_code",
 			"fieldtype": "Link",
 			"options": "Item",
-			"width": 120
+			"width": 120,
 		},
-		{
-			"label": _("Item Name"),
-			"fieldname": "item_name",
-			"fieldtype": "Data",
-			"width": 120
-		},
-		{
-			"label": _("Brand"),
-			"fieldname": "brand",
-			"fieldtype": "Data",
-			"width": 100
-		},
+		{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 120},
+		{"label": _("Brand"), "fieldname": "brand", "fieldtype": "Data", "width": 100},
 		{
 			"label": _("Warehouse"),
 			"fieldname": "warehouse",
 			"fieldtype": "Link",
 			"options": "Warehouse",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("Stock Available"),
 			"fieldname": "stock_available",
 			"fieldtype": "Float",
-			"width": 120
+			"width": 120,
 		},
 		{
 			"label": _("Buying Price List"),
 			"fieldname": "buying_price_list",
 			"fieldtype": "Link",
 			"options": "Price List",
-			"width": 120
+			"width": 120,
 		},
-		{
-			"label": _("Buying Rate"),
-			"fieldname": "buying_rate",
-			"fieldtype": "Currency",
-			"width": 120
-		},
+		{"label": _("Buying Rate"), "fieldname": "buying_rate", "fieldtype": "Currency", "width": 120},
 		{
 			"label": _("Selling Price List"),
 			"fieldname": "selling_price_list",
 			"fieldtype": "Link",
 			"options": "Price List",
-			"width": 120
+			"width": 120,
 		},
-		{
-			"label": _("Selling Rate"),
-			"fieldname": "selling_rate",
-			"fieldtype": "Currency",
-			"width": 120
-		}
+		{"label": _("Selling Rate"), "fieldname": "selling_rate", "fieldtype": "Currency", "width": 120},
 	]
 
+
 def get_data(filters, columns):
 	item_price_qty_data = []
 	item_price_qty_data = get_item_price_qty_data(filters)
 	return item_price_qty_data
 
+
 def get_item_price_qty_data(filters):
 	conditions = ""
 	if filters.get("item_code"):
 		conditions += "where a.item_code=%(item_code)s"
 
-	item_results = frappe.db.sql("""select a.item_code, a.item_name, a.name as price_list_name,
+	item_results = frappe.db.sql(
+		"""select a.item_code, a.item_name, a.name as price_list_name,
 		a.brand as brand, b.warehouse as warehouse, b.actual_qty as actual_qty
 		from `tabItem Price` a left join `tabBin` b
 		ON a.item_code = b.item_code
-		{conditions}"""
-		.format(conditions=conditions), filters, as_dict=1)
+		{conditions}""".format(
+			conditions=conditions
+		),
+		filters,
+		as_dict=1,
+	)
 
 	price_list_names = list(set(item.price_list_name for item in item_results))
 
@@ -99,15 +87,15 @@
 	if item_results:
 		for item_dict in item_results:
 			data = {
-				'item_code': item_dict.item_code,
-				'item_name': item_dict.item_name,
-				'brand': item_dict.brand,
-				'warehouse': item_dict.warehouse,
-				'stock_available': item_dict.actual_qty or 0,
-				'buying_price_list': "",
-				'buying_rate': 0.0,
-				'selling_price_list': "",
-				'selling_rate': 0.0
+				"item_code": item_dict.item_code,
+				"item_name": item_dict.item_name,
+				"brand": item_dict.brand,
+				"warehouse": item_dict.warehouse,
+				"stock_available": item_dict.actual_qty or 0,
+				"buying_price_list": "",
+				"buying_rate": 0.0,
+				"selling_price_list": "",
+				"selling_rate": 0.0,
 			}
 
 			price_list = item_dict["price_list_name"]
@@ -122,6 +110,7 @@
 
 	return result
 
+
 def get_price_map(price_list_names, buying=0, selling=0):
 	price_map = {}
 
@@ -137,14 +126,12 @@
 	else:
 		filters["selling"] = 1
 
-	pricing_details = frappe.get_all("Item Price",
-		fields = ["name", "price_list", "price_list_rate"], filters=filters)
+	pricing_details = frappe.get_all(
+		"Item Price", fields=["name", "price_list", "price_list_rate"], filters=filters
+	)
 
 	for d in pricing_details:
 		name = d["name"]
-		price_map[name] = {
-			price_list_key :d["price_list"],
-			rate_key :d["price_list_rate"]
-		}
+		price_map[name] = {price_list_key: d["price_list"], rate_key: d["price_list_rate"]}
 
 	return price_map
diff --git a/erpnext/stock/report/item_prices/item_prices.py b/erpnext/stock/report/item_prices/item_prices.py
index 0d0e8d2..87f1a42 100644
--- a/erpnext/stock/report/item_prices/item_prices.py
+++ b/erpnext/stock/report/item_prices/item_prices.py
@@ -8,7 +8,8 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	columns = get_columns(filters)
 	conditions = get_condition(filters)
@@ -19,64 +20,95 @@
 	val_rate_map = get_valuation_rate()
 
 	from erpnext.accounts.utils import get_currency_precision
+
 	precision = get_currency_precision() or 2
 	data = []
 	for item in sorted(item_map):
-		data.append([item, item_map[item]["item_name"],item_map[item]["item_group"],
-			item_map[item]["brand"], item_map[item]["description"], item_map[item]["stock_uom"],
-			flt(last_purchase_rate.get(item, 0), precision),
-			flt(val_rate_map.get(item, 0), precision),
-			pl.get(item, {}).get("Selling"),
-			pl.get(item, {}).get("Buying"),
-			flt(bom_rate.get(item, 0), precision)
-		])
+		data.append(
+			[
+				item,
+				item_map[item]["item_name"],
+				item_map[item]["item_group"],
+				item_map[item]["brand"],
+				item_map[item]["description"],
+				item_map[item]["stock_uom"],
+				flt(last_purchase_rate.get(item, 0), precision),
+				flt(val_rate_map.get(item, 0), precision),
+				pl.get(item, {}).get("Selling"),
+				pl.get(item, {}).get("Buying"),
+				flt(bom_rate.get(item, 0), precision),
+			]
+		)
 
 	return columns, data
 
+
 def get_columns(filters):
 	"""return columns based on filters"""
 
-	columns = [_("Item") + ":Link/Item:100", _("Item Name") + "::150",_("Item Group") + ":Link/Item Group:125",
-		_("Brand") + "::100", _("Description") + "::150", _("UOM") + ":Link/UOM:80",
-		_("Last Purchase Rate") + ":Currency:90", _("Valuation Rate") + ":Currency:80",	_("Sales Price List") + "::180",
-		_("Purchase Price List") + "::180", _("BOM Rate") + ":Currency:90"]
+	columns = [
+		_("Item") + ":Link/Item:100",
+		_("Item Name") + "::150",
+		_("Item Group") + ":Link/Item Group:125",
+		_("Brand") + "::100",
+		_("Description") + "::150",
+		_("UOM") + ":Link/UOM:80",
+		_("Last Purchase Rate") + ":Currency:90",
+		_("Valuation Rate") + ":Currency:80",
+		_("Sales Price List") + "::180",
+		_("Purchase Price List") + "::180",
+		_("BOM Rate") + ":Currency:90",
+	]
 
 	return columns
 
+
 def get_item_details(conditions):
 	"""returns all items details"""
 
 	item_map = {}
 
-	for i in frappe.db.sql("""select name, item_group, item_name, description,
+	for i in frappe.db.sql(
+		"""select name, item_group, item_name, description,
 		brand, stock_uom from tabItem %s
-		order by item_code, item_group""" % (conditions), as_dict=1):
-			item_map.setdefault(i.name, i)
+		order by item_code, item_group"""
+		% (conditions),
+		as_dict=1,
+	):
+		item_map.setdefault(i.name, i)
 
 	return item_map
 
+
 def get_price_list():
 	"""Get selling & buying price list of every item"""
 
 	rate = {}
 
-	price_list = frappe.db.sql("""select ip.item_code, ip.buying, ip.selling,
+	price_list = frappe.db.sql(
+		"""select ip.item_code, ip.buying, ip.selling,
 		concat(ifnull(cu.symbol,ip.currency), " ", round(ip.price_list_rate,2), " - ", ip.price_list) as price
 		from `tabItem Price` ip, `tabPrice List` pl, `tabCurrency` cu
-		where ip.price_list=pl.name and pl.currency=cu.name and pl.enabled=1""", as_dict=1)
+		where ip.price_list=pl.name and pl.currency=cu.name and pl.enabled=1""",
+		as_dict=1,
+	)
 
 	for j in price_list:
 		if j.price:
-			rate.setdefault(j.item_code, {}).setdefault("Buying" if j.buying else "Selling", []).append(j.price)
+			rate.setdefault(j.item_code, {}).setdefault("Buying" if j.buying else "Selling", []).append(
+				j.price
+			)
 	item_rate_map = {}
 
 	for item in rate:
 		for buying_or_selling in rate[item]:
-			item_rate_map.setdefault(item, {}).setdefault(buying_or_selling,
-				", ".join(rate[item].get(buying_or_selling, [])))
+			item_rate_map.setdefault(item, {}).setdefault(
+				buying_or_selling, ", ".join(rate[item].get(buying_or_selling, []))
+			)
 
 	return item_rate_map
 
+
 def get_last_purchase_rate():
 	item_last_purchase_rate_map = {}
 
@@ -108,29 +140,38 @@
 
 	return item_last_purchase_rate_map
 
+
 def get_item_bom_rate():
 	"""Get BOM rate of an item from BOM"""
 
 	item_bom_map = {}
 
-	for b in frappe.db.sql("""select item, (total_cost/quantity) as bom_rate
-		from `tabBOM` where is_active=1 and is_default=1""", as_dict=1):
-			item_bom_map.setdefault(b.item, flt(b.bom_rate))
+	for b in frappe.db.sql(
+		"""select item, (total_cost/quantity) as bom_rate
+		from `tabBOM` where is_active=1 and is_default=1""",
+		as_dict=1,
+	):
+		item_bom_map.setdefault(b.item, flt(b.bom_rate))
 
 	return item_bom_map
 
+
 def get_valuation_rate():
 	"""Get an average valuation rate of an item from all warehouses"""
 
 	item_val_rate_map = {}
 
-	for d in frappe.db.sql("""select item_code,
+	for d in frappe.db.sql(
+		"""select item_code,
 		sum(actual_qty*valuation_rate)/sum(actual_qty) as val_rate
-		from tabBin where actual_qty > 0 group by item_code""", as_dict=1):
-			item_val_rate_map.setdefault(d.item_code, d.val_rate)
+		from tabBin where actual_qty > 0 group by item_code""",
+		as_dict=1,
+	):
+		item_val_rate_map.setdefault(d.item_code, d.val_rate)
 
 	return item_val_rate_map
 
+
 def get_condition(filters):
 	"""Get Filter Items"""
 
diff --git a/erpnext/stock/report/item_shortage_report/item_shortage_report.py b/erpnext/stock/report/item_shortage_report/item_shortage_report.py
index 30c7614..03a3a6a 100644
--- a/erpnext/stock/report/item_shortage_report/item_shortage_report.py
+++ b/erpnext/stock/report/item_shortage_report/item_shortage_report.py
@@ -18,6 +18,7 @@
 
 	return columns, data, None, chart_data
 
+
 def get_conditions(filters):
 	conditions = ""
 
@@ -28,8 +29,10 @@
 
 	return conditions
 
+
 def get_data(conditions, filters):
-	data = frappe.db.sql("""
+	data = frappe.db.sql(
+		"""
 		SELECT
 			bin.warehouse,
 			bin.item_code,
@@ -51,10 +54,16 @@
 			AND warehouse.name = bin.warehouse
 			AND bin.item_code=item.name
 			{0}
-		ORDER BY bin.projected_qty;""".format(conditions), filters, as_dict=1)
+		ORDER BY bin.projected_qty;""".format(
+			conditions
+		),
+		filters,
+		as_dict=1,
+	)
 
 	return data
 
+
 def get_chart_data(data):
 	labels, datapoints = [], []
 
@@ -67,18 +76,11 @@
 		datapoints = datapoints[:10]
 
 	return {
-		"data": {
-			"labels": labels,
-			"datasets":[
-				{
-					"name": _("Projected Qty"),
-					"values": datapoints
-				}
-			]
-		},
-		"type": "bar"
+		"data": {"labels": labels, "datasets": [{"name": _("Projected Qty"), "values": datapoints}]},
+		"type": "bar",
 	}
 
+
 def get_columns():
 	columns = [
 		{
@@ -86,76 +88,66 @@
 			"fieldname": "warehouse",
 			"fieldtype": "Link",
 			"options": "Warehouse",
-			"width": 150
+			"width": 150,
 		},
 		{
 			"label": _("Item"),
 			"fieldname": "item_code",
 			"fieldtype": "Link",
 			"options": "Item",
-			"width": 150
+			"width": 150,
 		},
 		{
 			"label": _("Actual Quantity"),
 			"fieldname": "actual_qty",
 			"fieldtype": "Float",
 			"width": 120,
-			"convertible": "qty"
+			"convertible": "qty",
 		},
 		{
 			"label": _("Ordered Quantity"),
 			"fieldname": "ordered_qty",
 			"fieldtype": "Float",
 			"width": 120,
-			"convertible": "qty"
+			"convertible": "qty",
 		},
 		{
 			"label": _("Planned Quantity"),
 			"fieldname": "planned_qty",
 			"fieldtype": "Float",
 			"width": 120,
-			"convertible": "qty"
+			"convertible": "qty",
 		},
 		{
 			"label": _("Reserved Quantity"),
 			"fieldname": "reserved_qty",
 			"fieldtype": "Float",
 			"width": 120,
-			"convertible": "qty"
+			"convertible": "qty",
 		},
 		{
 			"label": _("Reserved Quantity for Production"),
 			"fieldname": "reserved_qty_for_production",
 			"fieldtype": "Float",
 			"width": 120,
-			"convertible": "qty"
+			"convertible": "qty",
 		},
 		{
 			"label": _("Projected Quantity"),
 			"fieldname": "projected_qty",
 			"fieldtype": "Float",
 			"width": 120,
-			"convertible": "qty"
+			"convertible": "qty",
 		},
 		{
 			"label": _("Company"),
 			"fieldname": "company",
 			"fieldtype": "Link",
 			"options": "Company",
-			"width": 120
+			"width": 120,
 		},
-		{
-			"label": _("Item Name"),
-			"fieldname": "item_name",
-			"fieldtype": "Data",
-			"width": 100
-		},
-		{
-			"label": _("Description"),
-			"fieldname": "description",
-			"fieldtype": "Data",
-			"width": 120
-		}
+		{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 100},
+		{"label": _("Description"), "fieldname": "description", "fieldtype": "Data", "width": 120},
 	]
 
 	return columns
diff --git a/erpnext/stock/report/item_variant_details/item_variant_details.py b/erpnext/stock/report/item_variant_details/item_variant_details.py
index 10cef70..e3a2a65 100644
--- a/erpnext/stock/report/item_variant_details/item_variant_details.py
+++ b/erpnext/stock/report/item_variant_details/item_variant_details.py
@@ -11,25 +11,21 @@
 	data = get_data(filters.item)
 	return columns, data
 
+
 def get_data(item):
 	if not item:
 		return []
 	item_dicts = []
 
 	variant_results = frappe.db.get_all(
-		"Item",
-		fields=["name"],
-		filters={
-			"variant_of": ["=", item],
-			"disabled": 0
-		}
+		"Item", fields=["name"], filters={"variant_of": ["=", item], "disabled": 0}
 	)
 
 	if not variant_results:
 		frappe.msgprint(_("There aren't any item variants for the selected item"))
 		return []
 	else:
-		variant_list = [variant['name'] for variant in variant_results]
+		variant_list = [variant["name"] for variant in variant_results]
 
 	order_count_map = get_open_sales_orders_count(variant_list)
 	stock_details_map = get_stock_details_map(variant_list)
@@ -40,15 +36,13 @@
 	attributes = frappe.db.get_all(
 		"Item Variant Attribute",
 		fields=["attribute"],
-		filters={
-			"parent": ["in", variant_list]
-		},
-		group_by="attribute"
+		filters={"parent": ["in", variant_list]},
+		group_by="attribute",
 	)
 	attribute_list = [row.get("attribute") for row in attributes]
 
 	# Prepare dicts
-	variant_dicts = [{"variant_name": d['name']} for d in variant_results]
+	variant_dicts = [{"variant_name": d["name"]} for d in variant_results]
 	for item_dict in variant_dicts:
 		name = item_dict.get("variant_name")
 
@@ -72,73 +66,66 @@
 
 	return item_dicts
 
+
 def get_columns(item):
-	columns = [{
-		"fieldname": "variant_name",
-		"label": "Variant",
-		"fieldtype": "Link",
-		"options": "Item",
-		"width": 200
-	}]
+	columns = [
+		{
+			"fieldname": "variant_name",
+			"label": _("Variant"),
+			"fieldtype": "Link",
+			"options": "Item",
+			"width": 200,
+		}
+	]
 
 	item_doc = frappe.get_doc("Item", item)
 
 	for entry in item_doc.attributes:
-		columns.append({
-			"fieldname": frappe.scrub(entry.attribute),
-			"label": entry.attribute,
-			"fieldtype": "Data",
-			"width": 100
-		})
+		columns.append(
+			{
+				"fieldname": frappe.scrub(entry.attribute),
+				"label": entry.attribute,
+				"fieldtype": "Data",
+				"width": 100,
+			}
+		)
 
 	additional_columns = [
 		{
 			"fieldname": "avg_buying_price_list_rate",
 			"label": _("Avg. Buying Price List Rate"),
 			"fieldtype": "Currency",
-			"width": 150
+			"width": 150,
 		},
 		{
 			"fieldname": "avg_selling_price_list_rate",
 			"label": _("Avg. Selling Price List Rate"),
 			"fieldtype": "Currency",
-			"width": 150
+			"width": 150,
 		},
-		{
-			"fieldname": "current_stock",
-			"label": _("Current Stock"),
-			"fieldtype": "Float",
-			"width": 120
-		},
-		{
-			"fieldname": "in_production",
-			"label": _("In Production"),
-			"fieldtype": "Float",
-			"width": 150
-		},
+		{"fieldname": "current_stock", "label": _("Current Stock"), "fieldtype": "Float", "width": 120},
+		{"fieldname": "in_production", "label": _("In Production"), "fieldtype": "Float", "width": 150},
 		{
 			"fieldname": "open_orders",
 			"label": _("Open Sales Orders"),
 			"fieldtype": "Float",
-			"width": 150
-		}
+			"width": 150,
+		},
 	]
 	columns.extend(additional_columns)
 
 	return columns
 
+
 def get_open_sales_orders_count(variants_list):
 	open_sales_orders = frappe.db.get_list(
 		"Sales Order",
-		fields=[
-			"name",
-			"`tabSales Order Item`.item_code"
-		],
+		fields=["name", "`tabSales Order Item`.item_code"],
 		filters=[
 			["Sales Order", "docstatus", "=", 1],
-			["Sales Order Item", "item_code", "in", variants_list]
+			["Sales Order Item", "item_code", "in", variants_list],
 		],
-		distinct=1
+		distinct=1,
 	)
 
 	order_count_map = {}
@@ -151,6 +138,7 @@
 
 	return order_count_map
 
+
 def get_stock_details_map(variant_list):
 	stock_details = frappe.db.get_all(
 		"Bin",
@@ -160,10 +148,8 @@
 			"sum(projected_qty) as projected_qty",
 			"item_code",
 		],
-		filters={
-			"item_code": ["in", variant_list]
-		},
-		group_by="item_code"
+		filters={"item_code": ["in", variant_list]},
+		group_by="item_code",
 	)
 
 	stock_details_map = {}
@@ -171,11 +157,12 @@
 		name = row.get("item_code")
 		stock_details_map[name] = {
 			"Inventory": row.get("actual_qty"),
-			"In Production": row.get("planned_qty")
+			"In Production": row.get("planned_qty"),
 		}
 
 	return stock_details_map
 
+
 def get_buying_price_map(variant_list):
 	buying = frappe.db.get_all(
 		"Item Price",
@@ -183,11 +170,8 @@
 			"avg(price_list_rate) as avg_rate",
 			"item_code",
 		],
-		filters={
-			"item_code": ["in", variant_list],
-			"buying": 1
-		},
-		group_by="item_code"
+		filters={"item_code": ["in", variant_list], "buying": 1},
+		group_by="item_code",
 	)
 
 	buying_price_map = {}
@@ -196,6 +180,7 @@
 
 	return buying_price_map
 
+
 def get_selling_price_map(variant_list):
 	selling = frappe.db.get_all(
 		"Item Price",
@@ -203,11 +188,8 @@
 			"avg(price_list_rate) as avg_rate",
 			"item_code",
 		],
-		filters={
-			"item_code": ["in", variant_list],
-			"selling": 1
-		},
-		group_by="item_code"
+		filters={"item_code": ["in", variant_list], "selling": 1},
+		group_by="item_code",
 	)
 
 	selling_price_map = {}
@@ -216,17 +198,12 @@
 
 	return selling_price_map
 
+
 def get_attribute_values_map(variant_list):
 	attribute_list = frappe.db.get_all(
 		"Item Variant Attribute",
-		fields=[
-			"attribute",
-			"attribute_value",
-			"parent"
-		],
-		filters={
-			"parent": ["in", variant_list]
-		}
+		fields=["attribute", "attribute_value", "parent"],
+		filters={"parent": ["in", variant_list]},
 	)
 
 	attr_val_map = {}
diff --git a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py
index cfa1e47..f308e9e 100644
--- a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py
+++ b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py
@@ -7,13 +7,14 @@
 
 
 def execute(filters=None):
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 	float_precision = frappe.db.get_default("float_precision")
 
 	condition = get_condition(filters)
 
 	avg_daily_outgoing = 0
-	diff = ((getdate(filters.get("to_date")) - getdate(filters.get("from_date"))).days)+1
+	diff = ((getdate(filters.get("to_date")) - getdate(filters.get("from_date"))).days) + 1
 	if diff <= 0:
 		frappe.throw(_("'From Date' must be after 'To Date'"))
 
@@ -24,42 +25,72 @@
 
 	data = []
 	for item in items:
-		total_outgoing = flt(consumed_item_map.get(item.name, 0)) + flt(delivered_item_map.get(item.name,0))
+		total_outgoing = flt(consumed_item_map.get(item.name, 0)) + flt(
+			delivered_item_map.get(item.name, 0)
+		)
 		avg_daily_outgoing = flt(total_outgoing / diff, float_precision)
 		reorder_level = (avg_daily_outgoing * flt(item.lead_time_days)) + flt(item.safety_stock)
 
-		data.append([item.name, item.item_name, item.item_group, item.brand, item.description,
-			item.safety_stock, item.lead_time_days, consumed_item_map.get(item.name, 0),
-			delivered_item_map.get(item.name,0), total_outgoing, avg_daily_outgoing, reorder_level])
+		data.append(
+			[
+				item.name,
+				item.item_name,
+				item.item_group,
+				item.brand,
+				item.description,
+				item.safety_stock,
+				item.lead_time_days,
+				consumed_item_map.get(item.name, 0),
+				delivered_item_map.get(item.name, 0),
+				total_outgoing,
+				avg_daily_outgoing,
+				reorder_level,
+			]
+		)
 
-	return columns , data
+	return columns, data
+
 
 def get_columns():
-	return[
-			_("Item") + ":Link/Item:120", _("Item Name") + ":Data:120", _("Item Group") + ":Link/Item Group:100",
-			_("Brand") + ":Link/Brand:100", _("Description") + "::160",
-			_("Safety Stock") + ":Float:160", _("Lead Time Days") + ":Float:120", _("Consumed") + ":Float:120",
-			_("Delivered") + ":Float:120", _("Total Outgoing") + ":Float:120", _("Avg Daily Outgoing") + ":Float:160",
-			_("Reorder Level") + ":Float:120"
+	return [
+		_("Item") + ":Link/Item:120",
+		_("Item Name") + ":Data:120",
+		_("Item Group") + ":Link/Item Group:100",
+		_("Brand") + ":Link/Brand:100",
+		_("Description") + "::160",
+		_("Safety Stock") + ":Float:160",
+		_("Lead Time Days") + ":Float:120",
+		_("Consumed") + ":Float:120",
+		_("Delivered") + ":Float:120",
+		_("Total Outgoing") + ":Float:120",
+		_("Avg Daily Outgoing") + ":Float:160",
+		_("Reorder Level") + ":Float:120",
 	]
 
+
 def get_item_info(filters):
 	from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition
+
 	conditions = [get_item_group_condition(filters.get("item_group"))]
 	if filters.get("brand"):
 		conditions.append("item.brand=%(brand)s")
 	conditions.append("is_stock_item = 1")
 
-	return frappe.db.sql("""select name, item_name, description, brand, item_group,
-		safety_stock, lead_time_days from `tabItem` item where {}"""
-		.format(" and ".join(conditions)), filters, as_dict=1)
+	return frappe.db.sql(
+		"""select name, item_name, description, brand, item_group,
+		safety_stock, lead_time_days from `tabItem` item where {}""".format(
+			" and ".join(conditions)
+		),
+		filters,
+		as_dict=1,
+	)
 
 
 def get_consumed_items(condition):
 	purpose_to_exclude = [
 		"Material Transfer for Manufacture",
 		"Material Transfer",
-		"Send to Subcontractor"
+		"Send to Subcontractor",
 	]
 
 	condition += """
@@ -67,10 +98,13 @@
 			purpose is NULL
 			or purpose not in ({})
 		)
-	""".format(', '.join(f"'{p}'" for p in purpose_to_exclude))
+	""".format(
+		", ".join(f"'{p}'" for p in purpose_to_exclude)
+	)
 	condition = condition.replace("posting_date", "sle.posting_date")
 
-	consumed_items = frappe.db.sql("""
+	consumed_items = frappe.db.sql(
+		"""
 		select item_code, abs(sum(actual_qty)) as consumed_qty
 		from `tabStock Ledger Entry` as sle left join `tabStock Entry` as se
 			on sle.voucher_no = se.name
@@ -79,22 +113,34 @@
 			and is_cancelled = 0
 			and voucher_type not in ('Delivery Note', 'Sales Invoice')
 			%s
-		group by item_code""" % condition, as_dict=1)
+		group by item_code"""
+		% condition,
+		as_dict=1,
+	)
 
-	consumed_items_map = {item.item_code : item.consumed_qty for item in consumed_items}
+	consumed_items_map = {item.item_code: item.consumed_qty for item in consumed_items}
 	return consumed_items_map
 
+
 def get_delivered_items(condition):
-	dn_items = frappe.db.sql("""select dn_item.item_code, sum(dn_item.stock_qty) as dn_qty
+	dn_items = frappe.db.sql(
+		"""select dn_item.item_code, sum(dn_item.stock_qty) as dn_qty
 		from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item
 		where dn.name = dn_item.parent and dn.docstatus = 1 %s
-		group by dn_item.item_code""" % (condition), as_dict=1)
+		group by dn_item.item_code"""
+		% (condition),
+		as_dict=1,
+	)
 
-	si_items = frappe.db.sql("""select si_item.item_code, sum(si_item.stock_qty) as si_qty
+	si_items = frappe.db.sql(
+		"""select si_item.item_code, sum(si_item.stock_qty) as si_qty
 		from `tabSales Invoice` si, `tabSales Invoice Item` si_item
 		where si.name = si_item.parent and si.docstatus = 1 and
 		si.update_stock = 1 %s
-		group by si_item.item_code""" % (condition), as_dict=1)
+		group by si_item.item_code"""
+		% (condition),
+		as_dict=1,
+	)
 
 	dn_item_map = {}
 	for item in dn_items:
@@ -105,10 +151,14 @@
 
 	return dn_item_map
 
+
 def get_condition(filters):
 	conditions = ""
 	if filters.get("from_date") and filters.get("to_date"):
-		conditions += " and posting_date between '%s' and '%s'" % (filters["from_date"],filters["to_date"])
+		conditions += " and posting_date between '%s' and '%s'" % (
+			filters["from_date"],
+			filters["to_date"],
+		)
 	else:
 		frappe.throw(_("From and To dates required"))
 	return conditions
diff --git a/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py b/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py
index d9adced..854875a 100644
--- a/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py
+++ b/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py
@@ -44,7 +44,9 @@
 
 			child_rows = []
 			for child_item_detail in required_items:
-				child_item_balance = stock_balance.get(child_item_detail.item_code, frappe._dict()).get(warehouse, frappe._dict())
+				child_item_balance = stock_balance.get(child_item_detail.item_code, frappe._dict()).get(
+					warehouse, frappe._dict()
+				)
 				child_row = {
 					"indent": 1,
 					"parent_item": parent_item,
@@ -73,16 +75,46 @@
 
 def get_columns():
 	columns = [
-		{"fieldname": "item_code", "label": _("Item"), "fieldtype": "Link", "options": "Item", "width": 300},
-		{"fieldname": "warehouse", "label": _("Warehouse"), "fieldtype": "Link", "options": "Warehouse", "width": 100},
+		{
+			"fieldname": "item_code",
+			"label": _("Item"),
+			"fieldtype": "Link",
+			"options": "Item",
+			"width": 300,
+		},
+		{
+			"fieldname": "warehouse",
+			"label": _("Warehouse"),
+			"fieldtype": "Link",
+			"options": "Warehouse",
+			"width": 100,
+		},
 		{"fieldname": "uom", "label": _("UOM"), "fieldtype": "Link", "options": "UOM", "width": 70},
 		{"fieldname": "bundle_qty", "label": _("Bundle Qty"), "fieldtype": "Float", "width": 100},
 		{"fieldname": "actual_qty", "label": _("Actual Qty"), "fieldtype": "Float", "width": 100},
 		{"fieldname": "minimum_qty", "label": _("Minimum Qty"), "fieldtype": "Float", "width": 100},
-		{"fieldname": "item_group", "label": _("Item Group"), "fieldtype": "Link", "options": "Item Group", "width": 100},
-		{"fieldname": "brand", "label": _("Brand"), "fieldtype": "Link", "options": "Brand", "width": 100},
+		{
+			"fieldname": "item_group",
+			"label": _("Item Group"),
+			"fieldtype": "Link",
+			"options": "Item Group",
+			"width": 100,
+		},
+		{
+			"fieldname": "brand",
+			"label": _("Brand"),
+			"fieldtype": "Link",
+			"options": "Brand",
+			"width": 100,
+		},
 		{"fieldname": "description", "label": _("Description"), "width": 140},
-		{"fieldname": "company", "label": _("Company"), "fieldtype": "Link", "options": "Company", "width": 100}
+		{
+			"fieldname": "company",
+			"label": _("Company"),
+			"fieldtype": "Link",
+			"options": "Company",
+			"width": 100,
+		},
 	]
 	return columns
 
@@ -92,12 +124,18 @@
 	item_details = frappe._dict()
 
 	conditions = get_parent_item_conditions(filters)
-	parent_item_details = frappe.db.sql("""
+	parent_item_details = frappe.db.sql(
+		"""
 		select item.name as item_code, item.item_name, pb.description, item.item_group, item.brand, item.stock_uom
 		from `tabItem` item
 		inner join `tabProduct Bundle` pb on pb.new_item_code = item.name
 		where ifnull(item.disabled, 0) = 0 {0}
-	""".format(conditions), filters, as_dict=1)  # nosec
+	""".format(
+			conditions
+		),
+		filters,
+		as_dict=1,
+	)  # nosec
 
 	parent_items = []
 	for d in parent_item_details:
@@ -105,7 +143,8 @@
 		item_details[d.item_code] = d
 
 	if parent_items:
-		child_item_details = frappe.db.sql("""
+		child_item_details = frappe.db.sql(
+			"""
 			select
 				pb.new_item_code as parent_item, pbi.item_code, item.item_name, pbi.description, item.item_group, item.brand,
 				item.stock_uom, pbi.uom, pbi.qty
@@ -113,7 +152,12 @@
 			inner join `tabProduct Bundle` pb on pb.name = pbi.parent
 			inner join `tabItem` item on item.name = pbi.item_code
 			where pb.new_item_code in ({0})
-		""".format(", ".join(["%s"] * len(parent_items))), parent_items, as_dict=1)  # nosec
+		""".format(
+				", ".join(["%s"] * len(parent_items))
+			),
+			parent_items,
+			as_dict=1,
+		)  # nosec
 	else:
 		child_item_details = []
 
@@ -140,12 +184,14 @@
 	if not items:
 		return []
 
-	item_conditions_sql = ' and sle.item_code in ({})' \
-		.format(', '.join(frappe.db.escape(i) for i in items))
+	item_conditions_sql = " and sle.item_code in ({})".format(
+		", ".join(frappe.db.escape(i) for i in items)
+	)
 
 	conditions = get_sle_conditions(filters)
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select
 			sle.item_code, sle.warehouse, sle.qty_after_transaction, sle.company
 		from
@@ -153,7 +199,10 @@
 		left join `tabStock Ledger Entry` sle2 on
 			sle.item_code = sle2.item_code and sle.warehouse = sle2.warehouse
 			and (sle.posting_date, sle.posting_time, sle.name) < (sle2.posting_date, sle2.posting_time, sle2.name)
-		where sle2.name is null and sle.docstatus < 2 %s %s""" % (item_conditions_sql, conditions), as_dict=1)  # nosec
+		where sle2.name is null and sle.docstatus < 2 %s %s"""
+		% (item_conditions_sql, conditions),
+		as_dict=1,
+	)  # nosec
 
 
 def get_parent_item_conditions(filters):
@@ -179,9 +228,14 @@
 	conditions += " and sle.posting_date <= %s" % frappe.db.escape(filters.get("date"))
 
 	if filters.get("warehouse"):
-		warehouse_details = frappe.db.get_value("Warehouse", filters.get("warehouse"), ["lft", "rgt"], as_dict=1)
+		warehouse_details = frappe.db.get_value(
+			"Warehouse", filters.get("warehouse"), ["lft", "rgt"], as_dict=1
+		)
 		if warehouse_details:
-			conditions += " and exists (select name from `tabWarehouse` wh \
-				where wh.lft >= %s and wh.rgt <= %s and sle.warehouse = wh.name)" % (warehouse_details.lft, warehouse_details.rgt)  # nosec
+			conditions += (
+				" and exists (select name from `tabWarehouse` wh \
+				where wh.lft >= %s and wh.rgt <= %s and sle.warehouse = wh.name)"
+				% (warehouse_details.lft, warehouse_details.rgt)
+			)  # nosec
 
 	return conditions
diff --git a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py
index 9738442..fe2d55a 100644
--- a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py
+++ b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py
@@ -8,7 +8,8 @@
 
 
 def execute(filters=None):
-	if not filters: filters ={}
+	if not filters:
+		filters = {}
 	data = []
 	conditions = get_columns(filters, "Purchase Receipt")
 	data = get_data(filters, conditions)
@@ -17,6 +18,7 @@
 
 	return conditions["columns"], data, None, chart_data
 
+
 def get_chart_data(data, filters):
 	if not data:
 		return []
@@ -27,7 +29,7 @@
 		# consider only consolidated row
 		data = [row for row in data if row[0]]
 
-	data = sorted(data, key = lambda i: i[-1], reverse=True)
+	data = sorted(data, key=lambda i: i[-1], reverse=True)
 
 	if len(data) > 10:
 		# get top 10 if data too long
@@ -39,14 +41,9 @@
 
 	return {
 		"data": {
-			"labels" : labels,
-			"datasets" : [
-				{
-				"name": _("Total Received Amount"),
-				"values": datapoints
-				}
-			]
+			"labels": labels,
+			"datasets": [{"name": _("Total Received Amount"), "values": datapoints}],
 		},
-		"type" : "bar",
-		"colors":["#5e64ff"]
+		"type": "bar",
+		"colors": ["#5e64ff"],
 	}
diff --git a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
index 80ec848..e439f51 100644
--- a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
+++ b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
@@ -12,42 +12,43 @@
 	data = get_data(filters)
 	return columns, data
 
+
 def get_columns(filters):
-	columns = [{
-		'label': _('Posting Date'),
-		'fieldtype': 'Date',
-		'fieldname': 'posting_date'
-	}, {
-		'label': _('Posting Time'),
-		'fieldtype': 'Time',
-		'fieldname': 'posting_time'
-	}, {
-		'label': _('Voucher Type'),
-		'fieldtype': 'Link',
-		'fieldname': 'voucher_type',
-		'options': 'DocType',
-		'width': 220
-	}, {
-		'label': _('Voucher No'),
-		'fieldtype': 'Dynamic Link',
-		'fieldname': 'voucher_no',
-		'options': 'voucher_type',
-		'width': 220
-	}, {
-		'label': _('Company'),
-		'fieldtype': 'Link',
-		'fieldname': 'company',
-		'options': 'Company',
-		'width': 220
-	}, {
-		'label': _('Warehouse'),
-		'fieldtype': 'Link',
-		'fieldname': 'warehouse',
-		'options': 'Warehouse',
-		'width': 220
-	}]
+	columns = [
+		{"label": _("Posting Date"), "fieldtype": "Date", "fieldname": "posting_date"},
+		{"label": _("Posting Time"), "fieldtype": "Time", "fieldname": "posting_time"},
+		{
+			"label": _("Voucher Type"),
+			"fieldtype": "Link",
+			"fieldname": "voucher_type",
+			"options": "DocType",
+			"width": 220,
+		},
+		{
+			"label": _("Voucher No"),
+			"fieldtype": "Dynamic Link",
+			"fieldname": "voucher_no",
+			"options": "voucher_type",
+			"width": 220,
+		},
+		{
+			"label": _("Company"),
+			"fieldtype": "Link",
+			"fieldname": "company",
+			"options": "Company",
+			"width": 220,
+		},
+		{
+			"label": _("Warehouse"),
+			"fieldtype": "Link",
+			"fieldname": "warehouse",
+			"options": "Warehouse",
+			"width": 220,
+		},
+	]
 
 	return columns
 
+
 def get_data(filters):
-	return get_stock_ledger_entries(filters, '<=', order="asc") or []
+	return get_stock_ledger_entries(filters, "<=", order="asc") or []
diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py
index 97a740e..1956238 100644
--- a/erpnext/stock/report/stock_ageing/stock_ageing.py
+++ b/erpnext/stock/report/stock_ageing/stock_ageing.py
@@ -12,7 +12,7 @@
 from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 
 Filters = frappe._dict
-precision = cint(frappe.db.get_single_value("System Settings", "float_precision"))
+
 
 def execute(filters: Filters = None) -> Tuple:
 	to_date = filters["to_date"]
@@ -25,42 +25,52 @@
 
 	return columns, data, None, chart_data
 
+
 def format_report_data(filters: Filters, item_details: Dict, to_date: str) -> List[Dict]:
 	"Returns ordered, formatted data with ranges."
 	_func = itemgetter(1)
 	data = []
 
+	precision = cint(frappe.db.get_single_value("System Settings", "float_precision", cache=True))
+
 	for item, item_dict in item_details.items():
 		earliest_age, latest_age = 0, 0
 		details = item_dict["details"]
 
 		fifo_queue = sorted(filter(_func, item_dict["fifo_queue"]), key=_func)
 
-		if not fifo_queue: continue
+		if not fifo_queue:
+			continue
 
 		average_age = get_average_age(fifo_queue, to_date)
 		earliest_age = date_diff(to_date, fifo_queue[0][1])
 		latest_age = date_diff(to_date, fifo_queue[-1][1])
 		range1, range2, range3, above_range3 = get_range_age(filters, fifo_queue, to_date, item_dict)
 
-		row = [details.name, details.item_name, details.description,
-			details.item_group, details.brand]
+		row = [details.name, details.item_name, details.description, details.item_group, details.brand]
 
 		if filters.get("show_warehouse_wise_stock"):
 			row.append(details.warehouse)
 
-		row.extend([
-			flt(item_dict.get("total_qty"), precision),
-			average_age,
-			range1, range2, range3, above_range3,
-			earliest_age, latest_age,
-			details.stock_uom
-		])
+		row.extend(
+			[
+				flt(item_dict.get("total_qty"), precision),
+				average_age,
+				range1,
+				range2,
+				range3,
+				above_range3,
+				earliest_age,
+				latest_age,
+				details.stock_uom,
+			]
+		)
 
 		data.append(row)
 
 	return data
 
+
 def get_average_age(fifo_queue: List, to_date: str) -> float:
 	batch_age = age_qty = total_qty = 0.0
 	for batch in fifo_queue:
@@ -75,7 +85,11 @@
 
 	return flt(age_qty / total_qty, 2) if total_qty else 0.0
 
+
 def get_range_age(filters: Filters, fifo_queue: List, to_date: str, item_dict: Dict) -> Tuple:
+
+	precision = cint(frappe.db.get_single_value("System Settings", "float_precision", cache=True))
+
 	range1 = range2 = range3 = above_range3 = 0.0
 
 	for item in fifo_queue:
@@ -93,6 +107,7 @@
 
 	return range1, range2, range3, above_range3
 
+
 def get_columns(filters: Filters) -> List[Dict]:
 	range_columns = []
 	setup_ageing_columns(filters, range_columns)
@@ -102,82 +117,55 @@
 			"fieldname": "item_code",
 			"fieldtype": "Link",
 			"options": "Item",
-			"width": 100
+			"width": 100,
 		},
-		{
-			"label": _("Item Name"),
-			"fieldname": "item_name",
-			"fieldtype": "Data",
-			"width": 100
-		},
-		{
-			"label": _("Description"),
-			"fieldname": "description",
-			"fieldtype": "Data",
-			"width": 200
-		},
+		{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 100},
+		{"label": _("Description"), "fieldname": "description", "fieldtype": "Data", "width": 200},
 		{
 			"label": _("Item Group"),
 			"fieldname": "item_group",
 			"fieldtype": "Link",
 			"options": "Item Group",
-			"width": 100
+			"width": 100,
 		},
 		{
 			"label": _("Brand"),
 			"fieldname": "brand",
 			"fieldtype": "Link",
 			"options": "Brand",
-			"width": 100
-		}]
+			"width": 100,
+		},
+	]
 
 	if filters.get("show_warehouse_wise_stock"):
-		columns +=[{
-			"label": _("Warehouse"),
-			"fieldname": "warehouse",
-			"fieldtype": "Link",
-			"options": "Warehouse",
-			"width": 100
-		}]
+		columns += [
+			{
+				"label": _("Warehouse"),
+				"fieldname": "warehouse",
+				"fieldtype": "Link",
+				"options": "Warehouse",
+				"width": 100,
+			}
+		]
 
-	columns.extend([
-		{
-			"label": _("Available Qty"),
-			"fieldname": "qty",
-			"fieldtype": "Float",
-			"width": 100
-		},
-		{
-			"label": _("Average Age"),
-			"fieldname": "average_age",
-			"fieldtype": "Float",
-			"width": 100
-		}])
+	columns.extend(
+		[
+			{"label": _("Available Qty"), "fieldname": "qty", "fieldtype": "Float", "width": 100},
+			{"label": _("Average Age"), "fieldname": "average_age", "fieldtype": "Float", "width": 100},
+		]
+	)
 	columns.extend(range_columns)
-	columns.extend([
-		{
-			"label": _("Earliest"),
-			"fieldname": "earliest",
-			"fieldtype": "Int",
-			"width": 80
-		},
-		{
-			"label": _("Latest"),
-			"fieldname": "latest",
-			"fieldtype": "Int",
-			"width": 80
-		},
-		{
-			"label": _("UOM"),
-			"fieldname": "uom",
-			"fieldtype": "Link",
-			"options": "UOM",
-			"width": 100
-		}
-	])
+	columns.extend(
+		[
+			{"label": _("Earliest"), "fieldname": "earliest", "fieldtype": "Int", "width": 80},
+			{"label": _("Latest"), "fieldname": "latest", "fieldtype": "Int", "width": 80},
+			{"label": _("UOM"), "fieldname": "uom", "fieldtype": "Link", "options": "UOM", "width": 100},
+		]
+	)
 
 	return columns
 
+
 def get_chart_data(data: List, filters: Filters) -> Dict:
 	if not data:
 		return []
@@ -187,7 +175,7 @@
 	if filters.get("show_warehouse_wise_stock"):
 		return {}
 
-	data.sort(key = lambda row: row[6], reverse=True)
+	data.sort(key=lambda row: row[6], reverse=True)
 
 	if len(data) > 10:
 		data = data[:10]
@@ -197,42 +185,33 @@
 		datapoints.append(row[6])
 
 	return {
-		"data" : {
-			"labels": labels,
-			"datasets": [
-				{
-					"name": _("Average Age"),
-					"values": datapoints
-				}
-			]
-		},
-		"type" : "bar"
+		"data": {"labels": labels, "datasets": [{"name": _("Average Age"), "values": datapoints}]},
+		"type": "bar",
 	}
 
+
 def setup_ageing_columns(filters: Filters, range_columns: List):
 	ranges = [
 		f"0 - {filters['range1']}",
 		f"{cint(filters['range1']) + 1} - {cint(filters['range2'])}",
 		f"{cint(filters['range2']) + 1} - {cint(filters['range3'])}",
-		f"{cint(filters['range3']) + 1} - {_('Above')}"
+		f"{cint(filters['range3']) + 1} - {_('Above')}",
 	]
 	for i, label in enumerate(ranges):
-		fieldname = 'range' + str(i+1)
-		add_column(range_columns, label=f"Age ({label})",fieldname=fieldname)
+		fieldname = "range" + str(i + 1)
+		add_column(range_columns, label=f"Age ({label})", fieldname=fieldname)
 
-def add_column(range_columns: List, label:str, fieldname: str, fieldtype: str = 'Float', width: int = 140):
-	range_columns.append(dict(
-		label=label,
-		fieldname=fieldname,
-		fieldtype=fieldtype,
-		width=width
-	))
+
+def add_column(
+	range_columns: List, label: str, fieldname: str, fieldtype: str = "Float", width: int = 140
+):
+	range_columns.append(dict(label=label, fieldname=fieldname, fieldtype=fieldtype, width=width))
 
 
 class FIFOSlots:
 	"Returns FIFO computed slots of inwarded stock as per date."
 
-	def __init__(self, filters: Dict = None , sle: List = None):
+	def __init__(self, filters: Dict = None, sle: List = None):
 		self.item_details = {}
 		self.transferred_item_details = {}
 		self.serial_no_batch_purchase_details = {}
@@ -241,13 +220,13 @@
 
 	def generate(self) -> Dict:
 		"""
-			Returns dict of the foll.g structure:
-			Key = Item A / (Item A, Warehouse A)
-			Key: {
-				'details' -> Dict: ** item details **,
-				'fifo_queue' -> List: ** list of lists containing entries/slots for existing stock,
-					consumed/updated and maintained via FIFO. **
-			}
+		Returns dict of the foll.g structure:
+		Key = Item A / (Item A, Warehouse A)
+		Key: {
+		        'details' -> Dict: ** item details **,
+		        'fifo_queue' -> List: ** list of lists containing entries/slots for existing stock,
+		                consumed/updated and maintained via FIFO. **
+		}
 		"""
 		if self.sle is None:
 			self.sle = self.__get_stock_ledger_entries()
@@ -287,7 +266,9 @@
 
 		return key, fifo_queue, transferred_item_key
 
-	def __compute_incoming_stock(self, row: Dict, fifo_queue: List, transfer_key: Tuple, serial_nos: List):
+	def __compute_incoming_stock(
+		self, row: Dict, fifo_queue: List, transfer_key: Tuple, serial_nos: List
+	):
 		"Update FIFO Queue on inward stock."
 
 		transfer_data = self.transferred_item_details.get(transfer_key)
@@ -313,7 +294,9 @@
 					self.serial_no_batch_purchase_details.setdefault(serial_no, row.posting_date)
 					fifo_queue.append([serial_no, row.posting_date])
 
-	def __compute_outgoing_stock(self, row: Dict, fifo_queue: List, transfer_key: Tuple, serial_nos: List):
+	def __compute_outgoing_stock(
+		self, row: Dict, fifo_queue: List, transfer_key: Tuple, serial_nos: List
+	):
 		"Update FIFO Queue on outward stock."
 		if serial_nos:
 			fifo_queue[:] = [serial_no for serial_no in fifo_queue if serial_no[0] not in serial_nos]
@@ -379,15 +362,13 @@
 	def __aggregate_details_by_item(self, wh_wise_data: Dict) -> Dict:
 		"Aggregate Item-Wh wise data into single Item entry."
 		item_aggregated_data = {}
-		for key,row in wh_wise_data.items():
+		for key, row in wh_wise_data.items():
 			item = key[0]
 			if not item_aggregated_data.get(item):
-				item_aggregated_data.setdefault(item, {
-					"details": frappe._dict(),
-					"fifo_queue": [],
-					"qty_after_transaction": 0.0,
-					"total_qty": 0.0
-				})
+				item_aggregated_data.setdefault(
+					item,
+					{"details": frappe._dict(), "fifo_queue": [], "qty_after_transaction": 0.0, "total_qty": 0.0},
+				)
 			item_row = item_aggregated_data.get(item)
 			item_row["details"].update(row["details"])
 			item_row["fifo_queue"].extend(row["fifo_queue"])
@@ -399,19 +380,29 @@
 
 	def __get_stock_ledger_entries(self) -> List[Dict]:
 		sle = frappe.qb.DocType("Stock Ledger Entry")
-		item = self.__get_item_query() # used as derived table in sle query
+		item = self.__get_item_query()  # used as derived table in sle query
 
 		sle_query = (
-			frappe.qb.from_(sle).from_(item)
+			frappe.qb.from_(sle)
+			.from_(item)
 			.select(
-				item.name, item.item_name, item.item_group,
-				item.brand, item.description,
-				item.stock_uom, item.has_serial_no,
-				sle.actual_qty, sle.posting_date,
-				sle.voucher_type, sle.voucher_no,
-				sle.serial_no, sle.batch_no,
-				sle.qty_after_transaction, sle.warehouse
-			).where(
+				item.name,
+				item.item_name,
+				item.item_group,
+				item.brand,
+				item.description,
+				item.stock_uom,
+				item.has_serial_no,
+				sle.actual_qty,
+				sle.posting_date,
+				sle.voucher_type,
+				sle.voucher_no,
+				sle.serial_no,
+				sle.batch_no,
+				sle.qty_after_transaction,
+				sle.warehouse,
+			)
+			.where(
 				(sle.item_code == item.name)
 				& (sle.company == self.filters.get("company"))
 				& (sle.posting_date <= self.filters.get("to_date"))
@@ -422,9 +413,7 @@
 		if self.filters.get("warehouse"):
 			sle_query = self.__get_warehouse_conditions(sle, sle_query)
 
-		sle_query = sle_query.orderby(
-			sle.posting_date, sle.posting_time, sle.creation, sle.actual_qty
-		)
+		sle_query = sle_query.orderby(sle.posting_date, sle.posting_time, sle.creation, sle.actual_qty)
 
 		return sle_query.run(as_dict=True)
 
@@ -432,8 +421,7 @@
 		item_table = frappe.qb.DocType("Item")
 
 		item = frappe.qb.from_("Item").select(
-			"name", "item_name", "description", "stock_uom",
-			"brand", "item_group", "has_serial_no"
+			"name", "item_name", "description", "stock_uom", "brand", "item_group", "has_serial_no"
 		)
 
 		if self.filters.get("item_code"):
@@ -446,18 +434,13 @@
 
 	def __get_warehouse_conditions(self, sle, sle_query) -> str:
 		warehouse = frappe.qb.DocType("Warehouse")
-		lft, rgt = frappe.db.get_value(
-			"Warehouse",
-			self.filters.get("warehouse"),
-			['lft', 'rgt']
-		)
+		lft, rgt = frappe.db.get_value("Warehouse", self.filters.get("warehouse"), ["lft", "rgt"])
 
 		warehouse_results = (
 			frappe.qb.from_(warehouse)
-			.select("name").where(
-				(warehouse.lft >= lft)
-				& (warehouse.rgt <= rgt)
-			).run()
+			.select("name")
+			.where((warehouse.lft >= lft) & (warehouse.rgt <= rgt))
+			.run()
 		)
 		warehouse_results = [x[0] for x in warehouse_results]
 
diff --git a/erpnext/stock/report/stock_ageing/test_stock_ageing.py b/erpnext/stock/report/stock_ageing/test_stock_ageing.py
index 2630805..fb36360 100644
--- a/erpnext/stock/report/stock_ageing/test_stock_ageing.py
+++ b/erpnext/stock/report/stock_ageing/test_stock_ageing.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 
 import frappe
@@ -10,9 +10,7 @@
 class TestStockAgeing(FrappeTestCase):
 	def setUp(self) -> None:
 		self.filters = frappe._dict(
-			company="_Test Company",
-			to_date="2021-12-10",
-			range1=30, range2=60, range3=90
+			company="_Test Company", to_date="2021-12-10", range1=30, range2=60, range3=90
 		)
 
 	def test_normal_inward_outward_queue(self):
@@ -20,28 +18,37 @@
 		sle = [
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=30, qty_after_transaction=30,
+				actual_qty=30,
+				qty_after_transaction=30,
 				warehouse="WH 1",
-				posting_date="2021-12-01", voucher_type="Stock Entry",
+				posting_date="2021-12-01",
+				voucher_type="Stock Entry",
 				voucher_no="001",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=20, qty_after_transaction=50,
+				actual_qty=20,
+				qty_after_transaction=50,
 				warehouse="WH 1",
-				posting_date="2021-12-02", voucher_type="Stock Entry",
+				posting_date="2021-12-02",
+				voucher_type="Stock Entry",
 				voucher_no="002",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=(-10), qty_after_transaction=40,
+				actual_qty=(-10),
+				qty_after_transaction=40,
 				warehouse="WH 1",
-				posting_date="2021-12-03", voucher_type="Stock Entry",
+				posting_date="2021-12-03",
+				voucher_type="Stock Entry",
 				voucher_no="003",
-				has_serial_no=False, serial_no=None
-			)
+				has_serial_no=False,
+				serial_no=None,
+			),
 		]
 
 		slots = FIFOSlots(self.filters, sle).generate()
@@ -58,36 +65,48 @@
 		sle = [
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=(-30), qty_after_transaction=(-30),
+				actual_qty=(-30),
+				qty_after_transaction=(-30),
 				warehouse="WH 1",
-				posting_date="2021-12-01", voucher_type="Stock Entry",
+				posting_date="2021-12-01",
+				voucher_type="Stock Entry",
 				voucher_no="001",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=20, qty_after_transaction=(-10),
+				actual_qty=20,
+				qty_after_transaction=(-10),
 				warehouse="WH 1",
-				posting_date="2021-12-02", voucher_type="Stock Entry",
+				posting_date="2021-12-02",
+				voucher_type="Stock Entry",
 				voucher_no="002",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=20, qty_after_transaction=10,
+				actual_qty=20,
+				qty_after_transaction=10,
 				warehouse="WH 1",
-				posting_date="2021-12-03", voucher_type="Stock Entry",
+				posting_date="2021-12-03",
+				voucher_type="Stock Entry",
 				voucher_no="003",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=10, qty_after_transaction=20,
+				actual_qty=10,
+				qty_after_transaction=20,
 				warehouse="WH 1",
-				posting_date="2021-12-03", voucher_type="Stock Entry",
+				posting_date="2021-12-03",
+				voucher_type="Stock Entry",
 				voucher_no="004",
-				has_serial_no=False, serial_no=None
-			)
+				has_serial_no=False,
+				serial_no=None,
+			),
 		]
 
 		slots = FIFOSlots(self.filters, sle).generate()
@@ -107,28 +126,37 @@
 		sle = [
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=30, qty_after_transaction=30,
+				actual_qty=30,
+				qty_after_transaction=30,
 				warehouse="WH 1",
-				posting_date="2021-12-01", voucher_type="Stock Entry",
+				posting_date="2021-12-01",
+				voucher_type="Stock Entry",
 				voucher_no="001",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=0, qty_after_transaction=50,
+				actual_qty=0,
+				qty_after_transaction=50,
 				warehouse="WH 1",
-				posting_date="2021-12-02", voucher_type="Stock Reconciliation",
+				posting_date="2021-12-02",
+				voucher_type="Stock Reconciliation",
 				voucher_no="002",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=(-10), qty_after_transaction=40,
+				actual_qty=(-10),
+				qty_after_transaction=40,
 				warehouse="WH 1",
-				posting_date="2021-12-03", voucher_type="Stock Entry",
+				posting_date="2021-12-03",
+				voucher_type="Stock Entry",
 				voucher_no="003",
-				has_serial_no=False, serial_no=None
-			)
+				has_serial_no=False,
+				serial_no=None,
+			),
 		]
 
 		slots = FIFOSlots(self.filters, sle).generate()
@@ -150,28 +178,37 @@
 		sle = [
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=0, qty_after_transaction=1000,
+				actual_qty=0,
+				qty_after_transaction=1000,
 				warehouse="WH 1",
-				posting_date="2021-12-01", voucher_type="Stock Reconciliation",
+				posting_date="2021-12-01",
+				voucher_type="Stock Reconciliation",
 				voucher_no="002",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=0, qty_after_transaction=400,
+				actual_qty=0,
+				qty_after_transaction=400,
 				warehouse="WH 1",
-				posting_date="2021-12-02", voucher_type="Stock Reconciliation",
+				posting_date="2021-12-02",
+				voucher_type="Stock Reconciliation",
 				voucher_no="003",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=(-10), qty_after_transaction=390,
+				actual_qty=(-10),
+				qty_after_transaction=390,
 				warehouse="WH 1",
-				posting_date="2021-12-03", voucher_type="Stock Entry",
+				posting_date="2021-12-03",
+				voucher_type="Stock Entry",
 				voucher_no="003",
-				has_serial_no=False, serial_no=None
-			)
+				has_serial_no=False,
+				serial_no=None,
+			),
 		]
 		slots = FIFOSlots(self.filters, sle).generate()
 
@@ -196,32 +233,41 @@
 		sle = [
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=0, qty_after_transaction=1000,
+				actual_qty=0,
+				qty_after_transaction=1000,
 				warehouse="WH 1",
-				posting_date="2021-12-01", voucher_type="Stock Reconciliation",
+				posting_date="2021-12-01",
+				voucher_type="Stock Reconciliation",
 				voucher_no="002",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=0, qty_after_transaction=400,
+				actual_qty=0,
+				qty_after_transaction=400,
 				warehouse="WH 2",
-				posting_date="2021-12-02", voucher_type="Stock Reconciliation",
+				posting_date="2021-12-02",
+				voucher_type="Stock Reconciliation",
 				voucher_no="003",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=(-10), qty_after_transaction=990,
+				actual_qty=(-10),
+				qty_after_transaction=990,
 				warehouse="WH 1",
-				posting_date="2021-12-03", voucher_type="Stock Entry",
+				posting_date="2021-12-03",
+				voucher_type="Stock Entry",
 				voucher_no="004",
-				has_serial_no=False, serial_no=None
-			)
+				has_serial_no=False,
+				serial_no=None,
+			),
 		]
 
 		item_wise_slots, item_wh_wise_slots = generate_item_and_item_wh_wise_slots(
-			filters=self.filters,sle=sle
+			filters=self.filters, sle=sle
 		)
 
 		# test without 'show_warehouse_wise_stock'
@@ -234,7 +280,9 @@
 		self.assertEqual(queue[1][0], 400.0)
 
 		# test with 'show_warehouse_wise_stock' checked
-		item_wh_balances = [item_wh_wise_slots.get(i).get("qty_after_transaction") for i in item_wh_wise_slots]
+		item_wh_balances = [
+			item_wh_wise_slots.get(i).get("qty_after_transaction") for i in item_wh_wise_slots
+		]
 		self.assertEqual(sum(item_wh_balances), item_result["qty_after_transaction"])
 
 	def test_repack_entry_same_item_split_rows(self):
@@ -251,37 +299,49 @@
 		Case most likely for batch items. Test time bucket computation.
 		"""
 		sle = [
-			frappe._dict( # stock up item
+			frappe._dict(  # stock up item
 				name="Flask Item",
-				actual_qty=500, qty_after_transaction=500,
+				actual_qty=500,
+				qty_after_transaction=500,
 				warehouse="WH 1",
-				posting_date="2021-12-03", voucher_type="Stock Entry",
+				posting_date="2021-12-03",
+				voucher_type="Stock Entry",
 				voucher_no="001",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=(-50), qty_after_transaction=450,
+				actual_qty=(-50),
+				qty_after_transaction=450,
 				warehouse="WH 1",
-				posting_date="2021-12-04", voucher_type="Stock Entry",
+				posting_date="2021-12-04",
+				voucher_type="Stock Entry",
 				voucher_no="002",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=(-50), qty_after_transaction=400,
+				actual_qty=(-50),
+				qty_after_transaction=400,
 				warehouse="WH 1",
-				posting_date="2021-12-04", voucher_type="Stock Entry",
+				posting_date="2021-12-04",
+				voucher_type="Stock Entry",
 				voucher_no="002",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=100, qty_after_transaction=500,
+				actual_qty=100,
+				qty_after_transaction=500,
 				warehouse="WH 1",
-				posting_date="2021-12-04", voucher_type="Stock Entry",
+				posting_date="2021-12-04",
+				voucher_type="Stock Entry",
 				voucher_no="002",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 		]
 		slots = FIFOSlots(self.filters, sle).generate()
@@ -308,29 +368,38 @@
 		Case most likely for batch items. Test time bucket computation.
 		"""
 		sle = [
-			frappe._dict( # stock up item
+			frappe._dict(  # stock up item
 				name="Flask Item",
-				actual_qty=500, qty_after_transaction=500,
+				actual_qty=500,
+				qty_after_transaction=500,
 				warehouse="WH 1",
-				posting_date="2021-12-03", voucher_type="Stock Entry",
+				posting_date="2021-12-03",
+				voucher_type="Stock Entry",
 				voucher_no="001",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=(-100), qty_after_transaction=400,
+				actual_qty=(-100),
+				qty_after_transaction=400,
 				warehouse="WH 1",
-				posting_date="2021-12-04", voucher_type="Stock Entry",
+				posting_date="2021-12-04",
+				voucher_type="Stock Entry",
 				voucher_no="002",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=50, qty_after_transaction=450,
+				actual_qty=50,
+				qty_after_transaction=450,
 				warehouse="WH 1",
-				posting_date="2021-12-04", voucher_type="Stock Entry",
+				posting_date="2021-12-04",
+				voucher_type="Stock Entry",
 				voucher_no="002",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 		]
 		slots = FIFOSlots(self.filters, sle).generate()
@@ -355,37 +424,49 @@
 		Item 1  | 50   | 002 (repack)
 		"""
 		sle = [
-			frappe._dict( # stock up item
+			frappe._dict(  # stock up item
 				name="Flask Item",
-				actual_qty=20, qty_after_transaction=20,
+				actual_qty=20,
+				qty_after_transaction=20,
 				warehouse="WH 1",
-				posting_date="2021-12-03", voucher_type="Stock Entry",
+				posting_date="2021-12-03",
+				voucher_type="Stock Entry",
 				voucher_no="001",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=(-50), qty_after_transaction=(-30),
+				actual_qty=(-50),
+				qty_after_transaction=(-30),
 				warehouse="WH 1",
-				posting_date="2021-12-04", voucher_type="Stock Entry",
+				posting_date="2021-12-04",
+				voucher_type="Stock Entry",
 				voucher_no="002",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=(-50), qty_after_transaction=(-80),
+				actual_qty=(-50),
+				qty_after_transaction=(-80),
 				warehouse="WH 1",
-				posting_date="2021-12-04", voucher_type="Stock Entry",
+				posting_date="2021-12-04",
+				voucher_type="Stock Entry",
 				voucher_no="002",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=50, qty_after_transaction=(-30),
+				actual_qty=50,
+				qty_after_transaction=(-30),
 				warehouse="WH 1",
-				posting_date="2021-12-04", voucher_type="Stock Entry",
+				posting_date="2021-12-04",
+				voucher_type="Stock Entry",
 				voucher_no="002",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 		]
 		fifo_slots = FIFOSlots(self.filters, sle)
@@ -397,7 +478,7 @@
 		self.assertEqual(queue[0][0], -30.0)
 
 		# check transfer bucket
-		transfer_bucket = fifo_slots.transferred_item_details[('002', 'Flask Item', 'WH 1')]
+		transfer_bucket = fifo_slots.transferred_item_details[("002", "Flask Item", "WH 1")]
 		self.assertEqual(transfer_bucket[0][0], 50)
 
 	def test_repack_entry_same_item_overproduce(self):
@@ -413,29 +494,38 @@
 		Case most likely for batch items. Test time bucket computation.
 		"""
 		sle = [
-			frappe._dict( # stock up item
+			frappe._dict(  # stock up item
 				name="Flask Item",
-				actual_qty=500, qty_after_transaction=500,
+				actual_qty=500,
+				qty_after_transaction=500,
 				warehouse="WH 1",
-				posting_date="2021-12-03", voucher_type="Stock Entry",
+				posting_date="2021-12-03",
+				voucher_type="Stock Entry",
 				voucher_no="001",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=(-50), qty_after_transaction=450,
+				actual_qty=(-50),
+				qty_after_transaction=450,
 				warehouse="WH 1",
-				posting_date="2021-12-04", voucher_type="Stock Entry",
+				posting_date="2021-12-04",
+				voucher_type="Stock Entry",
 				voucher_no="002",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=100, qty_after_transaction=550,
+				actual_qty=100,
+				qty_after_transaction=550,
 				warehouse="WH 1",
-				posting_date="2021-12-04", voucher_type="Stock Entry",
+				posting_date="2021-12-04",
+				voucher_type="Stock Entry",
 				voucher_no="002",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 		]
 		slots = FIFOSlots(self.filters, sle).generate()
@@ -461,37 +551,49 @@
 		Item 1  | 50   | 002 (repack)
 		"""
 		sle = [
-			frappe._dict( # stock up item
+			frappe._dict(  # stock up item
 				name="Flask Item",
-				actual_qty=20, qty_after_transaction=20,
+				actual_qty=20,
+				qty_after_transaction=20,
 				warehouse="WH 1",
-				posting_date="2021-12-03", voucher_type="Stock Entry",
+				posting_date="2021-12-03",
+				voucher_type="Stock Entry",
 				voucher_no="001",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=(-50), qty_after_transaction=(-30),
+				actual_qty=(-50),
+				qty_after_transaction=(-30),
 				warehouse="WH 1",
-				posting_date="2021-12-04", voucher_type="Stock Entry",
+				posting_date="2021-12-04",
+				voucher_type="Stock Entry",
 				voucher_no="002",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=50, qty_after_transaction=20,
+				actual_qty=50,
+				qty_after_transaction=20,
 				warehouse="WH 1",
-				posting_date="2021-12-04", voucher_type="Stock Entry",
+				posting_date="2021-12-04",
+				voucher_type="Stock Entry",
 				voucher_no="002",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 			frappe._dict(
 				name="Flask Item",
-				actual_qty=50, qty_after_transaction=70,
+				actual_qty=50,
+				qty_after_transaction=70,
 				warehouse="WH 1",
-				posting_date="2021-12-04", voucher_type="Stock Entry",
+				posting_date="2021-12-04",
+				voucher_type="Stock Entry",
 				voucher_no="002",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 		]
 		fifo_slots = FIFOSlots(self.filters, sle)
@@ -504,7 +606,7 @@
 		self.assertEqual(queue[1][0], 50.0)
 
 		# check transfer bucket
-		transfer_bucket = fifo_slots.transferred_item_details[('002', 'Flask Item', 'WH 1')]
+		transfer_bucket = fifo_slots.transferred_item_details[("002", "Flask Item", "WH 1")]
 		self.assertFalse(transfer_bucket)
 
 	def test_negative_stock_same_voucher(self):
@@ -519,29 +621,38 @@
 		Item 1  | 80   | 001
 		"""
 		sle = [
-			frappe._dict( # stock up item
+			frappe._dict(  # stock up item
 				name="Flask Item",
-				actual_qty=(-50), qty_after_transaction=(-50),
+				actual_qty=(-50),
+				qty_after_transaction=(-50),
 				warehouse="WH 1",
-				posting_date="2021-12-01", voucher_type="Stock Entry",
+				posting_date="2021-12-01",
+				voucher_type="Stock Entry",
 				voucher_no="001",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
-			frappe._dict( # stock up item
+			frappe._dict(  # stock up item
 				name="Flask Item",
-				actual_qty=(-50), qty_after_transaction=(-100),
+				actual_qty=(-50),
+				qty_after_transaction=(-100),
 				warehouse="WH 1",
-				posting_date="2021-12-01", voucher_type="Stock Entry",
+				posting_date="2021-12-01",
+				voucher_type="Stock Entry",
 				voucher_no="001",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
-			frappe._dict( # stock up item
+			frappe._dict(  # stock up item
 				name="Flask Item",
-				actual_qty=30, qty_after_transaction=(-70),
+				actual_qty=30,
+				qty_after_transaction=(-70),
 				warehouse="WH 1",
-				posting_date="2021-12-01", voucher_type="Stock Entry",
+				posting_date="2021-12-01",
+				voucher_type="Stock Entry",
 				voucher_no="001",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 		]
 		fifo_slots = FIFOSlots(self.filters, sle)
@@ -549,59 +660,71 @@
 		item_result = slots["Flask Item"]
 
 		# check transfer bucket
-		transfer_bucket = fifo_slots.transferred_item_details[('001', 'Flask Item', 'WH 1')]
+		transfer_bucket = fifo_slots.transferred_item_details[("001", "Flask Item", "WH 1")]
 		self.assertEqual(transfer_bucket[0][0], 20)
 		self.assertEqual(transfer_bucket[1][0], 50)
 		self.assertEqual(item_result["fifo_queue"][0][0], -70.0)
 
-		sle.append(frappe._dict(
-			name="Flask Item",
-			actual_qty=80, qty_after_transaction=10,
-			warehouse="WH 1",
-			posting_date="2021-12-01", voucher_type="Stock Entry",
-			voucher_no="001",
-			has_serial_no=False, serial_no=None
-		))
+		sle.append(
+			frappe._dict(
+				name="Flask Item",
+				actual_qty=80,
+				qty_after_transaction=10,
+				warehouse="WH 1",
+				posting_date="2021-12-01",
+				voucher_type="Stock Entry",
+				voucher_no="001",
+				has_serial_no=False,
+				serial_no=None,
+			)
+		)
 
 		fifo_slots = FIFOSlots(self.filters, sle)
 		slots = fifo_slots.generate()
 		item_result = slots["Flask Item"]
 
-		transfer_bucket = fifo_slots.transferred_item_details[('001', 'Flask Item', 'WH 1')]
+		transfer_bucket = fifo_slots.transferred_item_details[("001", "Flask Item", "WH 1")]
 		self.assertFalse(transfer_bucket)
 		self.assertEqual(item_result["fifo_queue"][0][0], 10.0)
 
 	def test_precision(self):
 		"Test if final balance qty is rounded off correctly."
 		sle = [
-			frappe._dict( # stock up item
+			frappe._dict(  # stock up item
 				name="Flask Item",
-				actual_qty=0.3, qty_after_transaction=0.3,
+				actual_qty=0.3,
+				qty_after_transaction=0.3,
 				warehouse="WH 1",
-				posting_date="2021-12-01", voucher_type="Stock Entry",
+				posting_date="2021-12-01",
+				voucher_type="Stock Entry",
 				voucher_no="001",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
-			frappe._dict( # stock up item
+			frappe._dict(  # stock up item
 				name="Flask Item",
-				actual_qty=0.6, qty_after_transaction=0.9,
+				actual_qty=0.6,
+				qty_after_transaction=0.9,
 				warehouse="WH 1",
-				posting_date="2021-12-01", voucher_type="Stock Entry",
+				posting_date="2021-12-01",
+				voucher_type="Stock Entry",
 				voucher_no="001",
-				has_serial_no=False, serial_no=None
+				has_serial_no=False,
+				serial_no=None,
 			),
 		]
 
 		slots = FIFOSlots(self.filters, sle).generate()
 		report_data = format_report_data(self.filters, slots, self.filters["to_date"])
-		row = report_data[0] # first row in report
+		row = report_data[0]  # first row in report
 		bal_qty = row[5]
-		range_qty_sum = sum([i for i in row[7:11]]) # get sum of range balance
+		range_qty_sum = sum([i for i in row[7:11]])  # get sum of range balance
 
 		# check if value of Available Qty column matches with range bucket post format
 		self.assertEqual(bal_qty, 0.9)
 		self.assertEqual(bal_qty, range_qty_sum)
 
+
 def generate_item_and_item_wh_wise_slots(filters, sle):
 	"Return results with and without 'show_warehouse_wise_stock'"
 	item_wise_slots = FIFOSlots(filters, sle).generate()
diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.py b/erpnext/stock/report/stock_analytics/stock_analytics.py
index ddc8310..89ca9d9 100644
--- a/erpnext/stock/report/stock_analytics/stock_analytics.py
+++ b/erpnext/stock/report/stock_analytics/stock_analytics.py
@@ -1,6 +1,7 @@
 # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
 import datetime
+from typing import List
 
 import frappe
 from frappe import _, scrub
@@ -25,84 +26,64 @@
 
 	return columns, data, None, chart
 
+
 def get_columns(filters):
 	columns = [
-		{
-			"label": _("Item"),
-			"options":"Item",
-			"fieldname": "name",
-			"fieldtype": "Link",
-			"width": 140
-		},
+		{"label": _("Item"), "options": "Item", "fieldname": "name", "fieldtype": "Link", "width": 140},
 		{
 			"label": _("Item Name"),
-			"options":"Item",
+			"options": "Item",
 			"fieldname": "item_name",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
 		{
 			"label": _("Item Group"),
-			"options":"Item Group",
+			"options": "Item Group",
 			"fieldname": "item_group",
 			"fieldtype": "Link",
-			"width": 140
+			"width": 140,
 		},
-		{
-			"label": _("Brand"),
-			"fieldname": "brand",
-			"fieldtype": "Data",
-			"width": 120
-		},
-		{
-			"label": _("UOM"),
-			"fieldname": "uom",
-			"fieldtype": "Data",
-			"width": 120
-		}]
+		{"label": _("Brand"), "fieldname": "brand", "fieldtype": "Data", "width": 120},
+		{"label": _("UOM"), "fieldname": "uom", "fieldtype": "Data", "width": 120},
+	]
 
 	ranges = get_period_date_ranges(filters)
 
 	for dummy, end_date in ranges:
 		period = get_period(end_date, filters)
 
-		columns.append({
-			"label": _(period),
-			"fieldname":scrub(period),
-			"fieldtype": "Float",
-			"width": 120
-		})
+		columns.append(
+			{"label": _(period), "fieldname": scrub(period), "fieldtype": "Float", "width": 120}
+		)
 
 	return columns
 
+
 def get_period_date_ranges(filters):
-		from dateutil.relativedelta import relativedelta
-		from_date = round_down_to_nearest_frequency(filters.from_date, filters.range)
-		to_date = getdate(filters.to_date)
+	from dateutil.relativedelta import relativedelta
 
-		increment = {
-			"Monthly": 1,
-			"Quarterly": 3,
-			"Half-Yearly": 6,
-			"Yearly": 12
-		}.get(filters.range,1)
+	from_date = round_down_to_nearest_frequency(filters.from_date, filters.range)
+	to_date = getdate(filters.to_date)
 
-		periodic_daterange = []
-		for dummy in range(1, 53, increment):
-			if filters.range == "Weekly":
-				period_end_date = from_date + relativedelta(days=6)
-			else:
-				period_end_date = from_date + relativedelta(months=increment, days=-1)
+	increment = {"Monthly": 1, "Quarterly": 3, "Half-Yearly": 6, "Yearly": 12}.get(filters.range, 1)
 
-			if period_end_date > to_date:
-				period_end_date = to_date
-			periodic_daterange.append([from_date, period_end_date])
+	periodic_daterange = []
+	for dummy in range(1, 53, increment):
+		if filters.range == "Weekly":
+			period_end_date = from_date + relativedelta(days=6)
+		else:
+			period_end_date = from_date + relativedelta(months=increment, days=-1)
 
-			from_date = period_end_date + relativedelta(days=1)
-			if period_end_date == to_date:
-				break
+		if period_end_date > to_date:
+			period_end_date = to_date
+		periodic_daterange.append([from_date, period_end_date])
 
-		return periodic_daterange
+		from_date = period_end_date + relativedelta(days=1)
+		if period_end_date == to_date:
+			break
+
+	return periodic_daterange
 
 
 def round_down_to_nearest_frequency(date: str, frequency: str) -> datetime.datetime:
@@ -132,12 +113,12 @@
 def get_period(posting_date, filters):
 	months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
 
-	if filters.range == 'Weekly':
+	if filters.range == "Weekly":
 		period = "Week " + str(posting_date.isocalendar()[1]) + " " + str(posting_date.year)
-	elif filters.range == 'Monthly':
+	elif filters.range == "Monthly":
 		period = str(months[posting_date.month - 1]) + " " + str(posting_date.year)
-	elif filters.range == 'Quarterly':
-		period = "Quarter " + str(((posting_date.month-1)//3)+1) +" " + str(posting_date.year)
+	elif filters.range == "Quarterly":
+		period = "Quarter " + str(((posting_date.month - 1) // 3) + 1) + " " + str(posting_date.year)
 	else:
 		year = get_fiscal_year(posting_date, company=filters.company)
 		period = str(year[2])
@@ -147,60 +128,103 @@
 
 def get_periodic_data(entry, filters):
 	"""Structured as:
-		Item 1
-			- Balance (updated and carried forward):
-					- Warehouse A : bal_qty/value
-					- Warehouse B : bal_qty/value
-			- Jun 2021 (sum of warehouse quantities used in report)
-					- Warehouse A : bal_qty/value
-					- Warehouse B : bal_qty/value
-			- Jul 2021 (sum of warehouse quantities used in report)
-					- Warehouse A : bal_qty/value
-					- Warehouse B : bal_qty/value
-		Item 2
-			- Balance (updated and carried forward):
-					- Warehouse A : bal_qty/value
-					- Warehouse B : bal_qty/value
-			- Jun 2021 (sum of warehouse quantities used in report)
-					- Warehouse A : bal_qty/value
-					- Warehouse B : bal_qty/value
-			- Jul 2021 (sum of warehouse quantities used in report)
-					- Warehouse A : bal_qty/value
-					- Warehouse B : bal_qty/value
+	Item 1
+	        - Balance (updated and carried forward):
+	                        - Warehouse A : bal_qty/value
+	                        - Warehouse B : bal_qty/value
+	        - Jun 2021 (sum of warehouse quantities used in report)
+	                        - Warehouse A : bal_qty/value
+	                        - Warehouse B : bal_qty/value
+	        - Jul 2021 (sum of warehouse quantities used in report)
+	                        - Warehouse A : bal_qty/value
+	                        - Warehouse B : bal_qty/value
+	Item 2
+	        - Balance (updated and carried forward):
+	                        - Warehouse A : bal_qty/value
+	                        - Warehouse B : bal_qty/value
+	        - Jun 2021 (sum of warehouse quantities used in report)
+	                        - Warehouse A : bal_qty/value
+	                        - Warehouse B : bal_qty/value
+	        - Jul 2021 (sum of warehouse quantities used in report)
+	                        - Warehouse A : bal_qty/value
+	                        - Warehouse B : bal_qty/value
 	"""
+
+	expected_ranges = get_period_date_ranges(filters)
+	expected_periods = []
+	for _start_date, end_date in expected_ranges:
+		expected_periods.append(get_period(end_date, filters))
+
 	periodic_data = {}
 	for d in entry:
 		period = get_period(d.posting_date, filters)
 		bal_qty = 0
 
+		fill_intermediate_periods(periodic_data, d.item_code, period, expected_periods)
+
 		# if period against item does not exist yet, instantiate it
 		# insert existing balance dict against period, and add/subtract to it
 		if periodic_data.get(d.item_code) and not periodic_data.get(d.item_code).get(period):
-			previous_balance = periodic_data[d.item_code]['balance'].copy()
+			previous_balance = periodic_data[d.item_code]["balance"].copy()
 			periodic_data[d.item_code][period] = previous_balance
 
-		if d.voucher_type == "Stock Reconciliation":
-			if periodic_data.get(d.item_code) and periodic_data.get(d.item_code).get('balance').get(d.warehouse):
-				bal_qty = periodic_data[d.item_code]['balance'][d.warehouse]
+		if d.voucher_type == "Stock Reconciliation" and not d.batch_no:
+			if periodic_data.get(d.item_code) and periodic_data.get(d.item_code).get("balance").get(
+				d.warehouse
+			):
+				bal_qty = periodic_data[d.item_code]["balance"][d.warehouse]
 
 			qty_diff = d.qty_after_transaction - bal_qty
 		else:
 			qty_diff = d.actual_qty
 
-		if filters["value_quantity"] == 'Quantity':
+		if filters["value_quantity"] == "Quantity":
 			value = qty_diff
 		else:
 			value = d.stock_value_difference
 
 		# period-warehouse wise balance
-		periodic_data.setdefault(d.item_code, {}).setdefault('balance', {}).setdefault(d.warehouse, 0.0)
+		periodic_data.setdefault(d.item_code, {}).setdefault("balance", {}).setdefault(d.warehouse, 0.0)
 		periodic_data.setdefault(d.item_code, {}).setdefault(period, {}).setdefault(d.warehouse, 0.0)
 
-		periodic_data[d.item_code]['balance'][d.warehouse] += value
-		periodic_data[d.item_code][period][d.warehouse] = periodic_data[d.item_code]['balance'][d.warehouse]
+		periodic_data[d.item_code]["balance"][d.warehouse] += value
+		periodic_data[d.item_code][period][d.warehouse] = periodic_data[d.item_code]["balance"][
+			d.warehouse
+		]
 
 	return periodic_data
 
+
+def fill_intermediate_periods(
+	periodic_data, item_code: str, current_period: str, all_periods: List[str]
+) -> None:
+	"""There might be intermediate periods where no stock ledger entry exists, copy previous previous data.
+
+	Previous data is ONLY copied if period falls in report range and before period being processed currently.
+
+	args:
+	        current_period: process till this period (exclusive)
+	        all_periods: all periods expected in report via filters
+	        periodic_data: report's periodic data
+	        item_code: item_code being processed
+	"""
+
+	previous_period_data = None
+	for period in all_periods:
+		if period == current_period:
+			return
+
+		if (
+			periodic_data.get(item_code)
+			and not periodic_data.get(item_code).get(period)
+			and previous_period_data
+		):
+			# This period should exist since it's in report range, assign previous period data
+			periodic_data[item_code][period] = previous_period_data.copy()
+
+		previous_period_data = periodic_data.get(item_code, {}).get(period)
+
+
 def get_data(filters):
 	data = []
 	items = get_items(filters)
@@ -209,6 +233,8 @@
 	periodic_data = get_periodic_data(sle, filters)
 	ranges = get_period_date_ranges(filters)
 
+	today = getdate()
+
 	for dummy, item_data in item_details.items():
 		row = {
 			"name": item_data.name,
@@ -217,26 +243,23 @@
 			"uom": item_data.stock_uom,
 			"brand": item_data.brand,
 		}
-		total = 0
-		for dummy, end_date in ranges:
+		previous_period_value = 0.0
+		for start_date, end_date in ranges:
 			period = get_period(end_date, filters)
 			period_data = periodic_data.get(item_data.name, {}).get(period)
-			amount = sum(period_data.values()) if period_data else 0
-			row[scrub(period)] = amount
-			total += amount
-		row["total"] = total
+			if period_data:
+				row[scrub(period)] = previous_period_value = sum(period_data.values())
+			else:
+				row[scrub(period)] = previous_period_value if today >= start_date else None
+
 		data.append(row)
 
 	return data
 
+
 def get_chart_data(columns):
 	labels = [d.get("label") for d in columns[5:]]
-	chart = {
-		"data": {
-			'labels': labels,
-			'datasets':[]
-		}
-	}
+	chart = {"data": {"labels": labels, "datasets": []}}
 	chart["type"] = "line"
 
 	return chart
diff --git a/erpnext/stock/report/stock_analytics/test_stock_analytics.py b/erpnext/stock/report/stock_analytics/test_stock_analytics.py
index f6c98f9..dd8f8d8 100644
--- a/erpnext/stock/report/stock_analytics/test_stock_analytics.py
+++ b/erpnext/stock/report/stock_analytics/test_stock_analytics.py
@@ -1,13 +1,59 @@
 import datetime
 
+import frappe
 from frappe import _dict
 from frappe.tests.utils import FrappeTestCase
+from frappe.utils.data import add_to_date, get_datetime, getdate, nowdate
 
 from erpnext.accounts.utils import get_fiscal_year
-from erpnext.stock.report.stock_analytics.stock_analytics import get_period_date_ranges
+from erpnext.stock.doctype.item.test_item import make_item
+from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+from erpnext.stock.report.stock_analytics.stock_analytics import execute, get_period_date_ranges
+
+
+def stock_analytics(filters):
+	col, data, *_ = execute(filters)
+	return col, data
 
 
 class TestStockAnalyticsReport(FrappeTestCase):
+	def setUp(self) -> None:
+		self.item = make_item().name
+		self.warehouse = "_Test Warehouse - _TC"
+
+	def assert_single_item_report(self, movement, expected_buckets):
+		self.generate_stock(movement)
+		filters = _dict(
+			range="Monthly",
+			from_date=movement[0][1].replace(day=1),
+			to_date=movement[-1][1].replace(day=28),
+			value_quantity="Quantity",
+			company="_Test Company",
+			item_code=self.item,
+		)
+
+		cols, data = stock_analytics(filters)
+
+		self.assertEqual(len(data), 1)
+		row = frappe._dict(data[0])
+		self.assertEqual(row.name, self.item)
+		self.compare_analytics_row(row, cols, expected_buckets)
+
+	def generate_stock(self, movement):
+		for qty, posting_date in movement:
+			args = {"item": self.item, "qty": abs(qty), "posting_date": posting_date}
+			args["to_warehouse" if qty > 0 else "from_warehouse"] = self.warehouse
+			make_stock_entry(**args)
+
+	def compare_analytics_row(self, report_row, columns, expected_buckets):
+		# last (N) cols will be monthly data
+		no_of_buckets = len(expected_buckets)
+		month_cols = [col["fieldname"] for col in columns[-no_of_buckets:]]
+
+		actual_buckets = [report_row.get(col) for col in month_cols]
+
+		self.assertEqual(actual_buckets, expected_buckets)
+
 	def test_get_period_date_ranges(self):
 
 		filters = _dict(range="Monthly", from_date="2020-12-28", to_date="2021-02-06")
@@ -33,3 +79,38 @@
 		]
 
 		self.assertEqual(ranges, expected_ranges)
+
+	def test_basic_report_functionality(self):
+		"""Stock analytics report generates balance "as of" periods based on
+		user defined ranges. Check that this behaviour is correct."""
+
+		# create stock movement in 3 months at 15th of month
+		today = getdate()
+		movement = [
+			(10, add_to_date(today, months=0).replace(day=15)),
+			(-5, add_to_date(today, months=1).replace(day=15)),
+			(10, add_to_date(today, months=2).replace(day=15)),
+		]
+		self.assert_single_item_report(movement, [10, 5, 15])
+
+	def test_empty_month_in_between(self):
+		today = getdate()
+		movement = [
+			(100, add_to_date(today, months=0).replace(day=15)),
+			(-50, add_to_date(today, months=1).replace(day=15)),
+			# Skip a month
+			(20, add_to_date(today, months=3).replace(day=15)),
+		]
+		self.assert_single_item_report(movement, [100, 50, 50, 70])
+
+	def test_multi_month_missings(self):
+		today = getdate()
+		movement = [
+			(100, add_to_date(today, months=0).replace(day=15)),
+			(-50, add_to_date(today, months=1).replace(day=15)),
+			# Skip a month
+			(20, add_to_date(today, months=3).replace(day=15)),
+			# Skip another month
+			(-10, add_to_date(today, months=5).replace(day=15)),
+		]
+		self.assert_single_item_report(movement, [100, 50, 50, 70, 70, 60])
diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py
index 6fd3fe7..99f820e 100644
--- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py
+++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py
@@ -12,21 +12,25 @@
 
 def execute(filters=None):
 	if not erpnext.is_perpetual_inventory_enabled(filters.company):
-		frappe.throw(_("Perpetual inventory required for the company {0} to view this report.")
-			.format(filters.company))
+		frappe.throw(
+			_("Perpetual inventory required for the company {0} to view this report.").format(
+				filters.company
+			)
+		)
 
 	data = get_data(filters)
 	columns = get_columns(filters)
 
 	return columns, data
 
+
 def get_data(report_filters):
 	data = []
 
 	filters = {
 		"is_cancelled": 0,
 		"company": report_filters.company,
-		"posting_date": ("<=", report_filters.as_on_date)
+		"posting_date": ("<=", report_filters.as_on_date),
 	}
 
 	currency_precision = get_currency_precision() or 2
@@ -43,18 +47,28 @@
 
 	return data
 
+
 def get_stock_ledger_data(report_filters, filters):
 	if report_filters.account:
-		warehouses = get_warehouses_based_on_account(report_filters.account,
-			report_filters.company)
+		warehouses = get_warehouses_based_on_account(report_filters.account, report_filters.company)
 
 		filters["warehouse"] = ("in", warehouses)
 
-	return frappe.get_all("Stock Ledger Entry", filters=filters,
-		fields = ["name", "voucher_type", "voucher_no",
-			"sum(stock_value_difference) as stock_value", "posting_date", "posting_time"],
-		group_by = "voucher_type, voucher_no",
-		order_by = "posting_date ASC, posting_time ASC")
+	return frappe.get_all(
+		"Stock Ledger Entry",
+		filters=filters,
+		fields=[
+			"name",
+			"voucher_type",
+			"voucher_no",
+			"sum(stock_value_difference) as stock_value",
+			"posting_date",
+			"posting_time",
+		],
+		group_by="voucher_type, voucher_no",
+		order_by="posting_date ASC, posting_time ASC",
+	)
+
 
 def get_gl_data(report_filters, filters):
 	if report_filters.account:
@@ -62,17 +76,22 @@
 	else:
 		stock_accounts = get_stock_accounts(report_filters.company)
 
-	filters.update({
-		"account": ("in", stock_accounts)
-	})
+	filters.update({"account": ("in", stock_accounts)})
 
 	if filters.get("warehouse"):
 		del filters["warehouse"]
 
-	gl_entries = frappe.get_all("GL Entry", filters=filters,
-		fields = ["name", "voucher_type", "voucher_no",
-			"sum(debit_in_account_currency) - sum(credit_in_account_currency) as account_value"],
-		group_by = "voucher_type, voucher_no")
+	gl_entries = frappe.get_all(
+		"GL Entry",
+		filters=filters,
+		fields=[
+			"name",
+			"voucher_type",
+			"voucher_no",
+			"sum(debit_in_account_currency) - sum(credit_in_account_currency) as account_value",
+		],
+		group_by="voucher_type, voucher_no",
+	)
 
 	voucher_wise_gl_data = {}
 	for d in gl_entries:
@@ -81,6 +100,7 @@
 
 	return voucher_wise_gl_data
 
+
 def get_columns(filters):
 	return [
 		{
@@ -88,46 +108,29 @@
 			"fieldname": "name",
 			"fieldtype": "Link",
 			"options": "Stock Ledger Entry",
-			"width": "80"
+			"width": "80",
 		},
-		{
-			"label": _("Posting Date"),
-			"fieldname": "posting_date",
-			"fieldtype": "Date"
-		},
-		{
-			"label": _("Posting Time"),
-			"fieldname": "posting_time",
-			"fieldtype": "Time"
-		},
-		{
-			"label": _("Voucher Type"),
-			"fieldname": "voucher_type",
-			"width": "110"
-		},
+		{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date"},
+		{"label": _("Posting Time"), "fieldname": "posting_time", "fieldtype": "Time"},
+		{"label": _("Voucher Type"), "fieldname": "voucher_type", "width": "110"},
 		{
 			"label": _("Voucher No"),
 			"fieldname": "voucher_no",
 			"fieldtype": "Dynamic Link",
 			"options": "voucher_type",
-			"width": "110"
+			"width": "110",
 		},
-		{
-			"label": _("Stock Value"),
-			"fieldname": "stock_value",
-			"fieldtype": "Currency",
-			"width": "120"
-		},
+		{"label": _("Stock Value"), "fieldname": "stock_value", "fieldtype": "Currency", "width": "120"},
 		{
 			"label": _("Account Value"),
 			"fieldname": "account_value",
 			"fieldtype": "Currency",
-			"width": "120"
+			"width": "120",
 		},
 		{
 			"label": _("Difference Value"),
 			"fieldname": "difference_value",
 			"fieldtype": "Currency",
-			"width": "120"
-		}
+			"width": "120",
+		},
 	]
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index b4f43a7..6369f91 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -3,23 +3,40 @@
 
 
 from operator import itemgetter
+from typing import Any, Dict, List, Optional, TypedDict
 
 import frappe
 from frappe import _
+from frappe.query_builder.functions import CombineDatetime
 from frappe.utils import cint, date_diff, flt, getdate
+from frappe.utils.nestedset import get_descendants_of
+from pypika.terms import ExistsCriterion
 
 import erpnext
 from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots, get_average_age
-from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition
 from erpnext.stock.utils import add_additional_uom_columns, is_reposting_item_valuation_in_progress
 
 
-def execute(filters=None):
-	is_reposting_item_valuation_in_progress()
-	if not filters: filters = {}
+class StockBalanceFilter(TypedDict):
+	company: Optional[str]
+	from_date: str
+	to_date: str
+	item_group: Optional[str]
+	item: Optional[str]
+	warehouse: Optional[str]
+	warehouse_type: Optional[str]
+	include_uom: Optional[str]  # include extra info in converted UOM
+	show_stock_ageing_data: bool
+	show_variant_attributes: bool
 
-	from_date = filters.get('from_date')
-	to_date = filters.get('to_date')
+
+SLEntry = Dict[str, Any]
+
+
+def execute(filters: Optional[StockBalanceFilter] = None):
+	is_reposting_item_valuation_in_progress()
+	if not filters:
+		filters = {}
 
 	if filters.get("company"):
 		company_currency = erpnext.get_company_currency(filters.get("company"))
@@ -31,8 +48,8 @@
 	items = get_items(filters)
 	sle = get_stock_ledger_entries(filters, items)
 
-	if filters.get('show_stock_ageing_data'):
-		filters['show_warehouse_wise_stock'] = True
+	if filters.get("show_stock_ageing_data"):
+		filters["show_warehouse_wise_stock"] = True
 		item_wise_fifo_queue = FIFOSlots(filters, sle).generate()
 
 	# if no stock ledger entry found return
@@ -48,6 +65,7 @@
 
 	_func = itemgetter(1)
 
+	to_date = filters.get("to_date")
 	for (company, item, warehouse) in sorted(iwb_map):
 		if item_map.get(item):
 			qty_dict = iwb_map[(company, item, warehouse)]
@@ -58,12 +76,12 @@
 				item_reorder_qty = item_reorder_detail_map[item + warehouse]["warehouse_reorder_qty"]
 
 			report_data = {
-				'currency': company_currency,
-				'item_code': item,
-				'warehouse': warehouse,
-				'company': company,
-				'reorder_level': item_reorder_level,
-				'reorder_qty': item_reorder_qty,
+				"currency": company_currency,
+				"item_code": item,
+				"warehouse": warehouse,
+				"company": company,
+				"reorder_level": item_reorder_level,
+				"reorder_qty": item_reorder_qty,
 			}
 			report_data.update(item_map[item])
 			report_data.update(qty_dict)
@@ -71,21 +89,18 @@
 			if include_uom:
 				conversion_factors.setdefault(item, item_map[item].conversion_factor)
 
-			if filters.get('show_stock_ageing_data'):
-				fifo_queue = item_wise_fifo_queue[(item, warehouse)].get('fifo_queue')
+			if filters.get("show_stock_ageing_data"):
+				fifo_queue = item_wise_fifo_queue[(item, warehouse)].get("fifo_queue")
 
-				stock_ageing_data = {
-					'average_age': 0,
-					'earliest_age': 0,
-					'latest_age': 0
-				}
+				stock_ageing_data = {"average_age": 0, "earliest_age": 0, "latest_age": 0}
 				if fifo_queue:
 					fifo_queue = sorted(filter(_func, fifo_queue), key=_func)
-					if not fifo_queue: continue
+					if not fifo_queue:
+						continue
 
-					stock_ageing_data['average_age'] = get_average_age(fifo_queue, to_date)
-					stock_ageing_data['earliest_age'] = date_diff(to_date, fifo_queue[0][1])
-					stock_ageing_data['latest_age'] = date_diff(to_date, fifo_queue[-1][1])
+					stock_ageing_data["average_age"] = get_average_age(fifo_queue, to_date)
+					stock_ageing_data["earliest_age"] = date_diff(to_date, fifo_queue[0][1])
+					stock_ageing_data["latest_age"] = date_diff(to_date, fifo_queue[-1][1])
 
 				report_data.update(stock_ageing_data)
 
@@ -94,86 +109,201 @@
 	add_additional_uom_columns(columns, data, include_uom, conversion_factors)
 	return columns, data
 
-def get_columns(filters):
+
+def get_columns(filters: StockBalanceFilter):
 	"""return columns"""
 	columns = [
-		{"label": _("Item"), "fieldname": "item_code", "fieldtype": "Link", "options": "Item", "width": 100},
+		{
+			"label": _("Item"),
+			"fieldname": "item_code",
+			"fieldtype": "Link",
+			"options": "Item",
+			"width": 100,
+		},
 		{"label": _("Item Name"), "fieldname": "item_name", "width": 150},
-		{"label": _("Item Group"), "fieldname": "item_group", "fieldtype": "Link", "options": "Item Group", "width": 100},
-		{"label": _("Warehouse"), "fieldname": "warehouse", "fieldtype": "Link", "options": "Warehouse", "width": 100},
-		{"label": _("Stock UOM"), "fieldname": "stock_uom", "fieldtype": "Link", "options": "UOM", "width": 90},
-		{"label": _("Balance Qty"), "fieldname": "bal_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"},
-		{"label": _("Balance Value"), "fieldname": "bal_val", "fieldtype": "Currency", "width": 100, "options": "currency"},
-		{"label": _("Opening Qty"), "fieldname": "opening_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"},
-		{"label": _("Opening Value"), "fieldname": "opening_val", "fieldtype": "Currency", "width": 110, "options": "currency"},
-		{"label": _("In Qty"), "fieldname": "in_qty", "fieldtype": "Float", "width": 80, "convertible": "qty"},
+		{
+			"label": _("Item Group"),
+			"fieldname": "item_group",
+			"fieldtype": "Link",
+			"options": "Item Group",
+			"width": 100,
+		},
+		{
+			"label": _("Warehouse"),
+			"fieldname": "warehouse",
+			"fieldtype": "Link",
+			"options": "Warehouse",
+			"width": 100,
+		},
+		{
+			"label": _("Stock UOM"),
+			"fieldname": "stock_uom",
+			"fieldtype": "Link",
+			"options": "UOM",
+			"width": 90,
+		},
+		{
+			"label": _("Balance Qty"),
+			"fieldname": "bal_qty",
+			"fieldtype": "Float",
+			"width": 100,
+			"convertible": "qty",
+		},
+		{
+			"label": _("Balance Value"),
+			"fieldname": "bal_val",
+			"fieldtype": "Currency",
+			"width": 100,
+			"options": "currency",
+		},
+		{
+			"label": _("Opening Qty"),
+			"fieldname": "opening_qty",
+			"fieldtype": "Float",
+			"width": 100,
+			"convertible": "qty",
+		},
+		{
+			"label": _("Opening Value"),
+			"fieldname": "opening_val",
+			"fieldtype": "Currency",
+			"width": 110,
+			"options": "currency",
+		},
+		{
+			"label": _("In Qty"),
+			"fieldname": "in_qty",
+			"fieldtype": "Float",
+			"width": 80,
+			"convertible": "qty",
+		},
 		{"label": _("In Value"), "fieldname": "in_val", "fieldtype": "Float", "width": 80},
-		{"label": _("Out Qty"), "fieldname": "out_qty", "fieldtype": "Float", "width": 80, "convertible": "qty"},
+		{
+			"label": _("Out Qty"),
+			"fieldname": "out_qty",
+			"fieldtype": "Float",
+			"width": 80,
+			"convertible": "qty",
+		},
 		{"label": _("Out Value"), "fieldname": "out_val", "fieldtype": "Float", "width": 80},
-		{"label": _("Valuation Rate"), "fieldname": "val_rate", "fieldtype": "Currency", "width": 90, "convertible": "rate", "options": "currency"},
-		{"label": _("Reorder Level"), "fieldname": "reorder_level", "fieldtype": "Float", "width": 80, "convertible": "qty"},
-		{"label": _("Reorder Qty"), "fieldname": "reorder_qty", "fieldtype": "Float", "width": 80, "convertible": "qty"},
-		{"label": _("Company"), "fieldname": "company", "fieldtype": "Link", "options": "Company", "width": 100}
+		{
+			"label": _("Valuation Rate"),
+			"fieldname": "val_rate",
+			"fieldtype": "Currency",
+			"width": 90,
+			"convertible": "rate",
+			"options": "currency",
+		},
+		{
+			"label": _("Reorder Level"),
+			"fieldname": "reorder_level",
+			"fieldtype": "Float",
+			"width": 80,
+			"convertible": "qty",
+		},
+		{
+			"label": _("Reorder Qty"),
+			"fieldname": "reorder_qty",
+			"fieldtype": "Float",
+			"width": 80,
+			"convertible": "qty",
+		},
+		{
+			"label": _("Company"),
+			"fieldname": "company",
+			"fieldtype": "Link",
+			"options": "Company",
+			"width": 100,
+		},
 	]
 
-	if filters.get('show_stock_ageing_data'):
-		columns += [{'label': _('Average Age'), 'fieldname': 'average_age', 'width': 100},
-		{'label': _('Earliest Age'), 'fieldname': 'earliest_age', 'width': 100},
-		{'label': _('Latest Age'), 'fieldname': 'latest_age', 'width': 100}]
+	if filters.get("show_stock_ageing_data"):
+		columns += [
+			{"label": _("Average Age"), "fieldname": "average_age", "width": 100},
+			{"label": _("Earliest Age"), "fieldname": "earliest_age", "width": 100},
+			{"label": _("Latest Age"), "fieldname": "latest_age", "width": 100},
+		]
 
-	if filters.get('show_variant_attributes'):
-		columns += [{'label': att_name, 'fieldname': att_name, 'width': 100} for att_name in get_variants_attributes()]
+	if filters.get("show_variant_attributes"):
+		columns += [
+			{"label": att_name, "fieldname": att_name, "width": 100}
+			for att_name in get_variants_attributes()
+		]
 
 	return columns
 
-def get_conditions(filters):
-	conditions = ""
+
+def apply_conditions(query, filters):
+	sle = frappe.qb.DocType("Stock Ledger Entry")
+	warehouse_table = frappe.qb.DocType("Warehouse")
+
 	if not filters.get("from_date"):
 		frappe.throw(_("'From Date' is required"))
 
-	if filters.get("to_date"):
-		conditions += " and sle.posting_date <= %s" % frappe.db.escape(filters.get("to_date"))
+	if to_date := filters.get("to_date"):
+		query = query.where(sle.posting_date <= to_date)
 	else:
 		frappe.throw(_("'To Date' is required"))
 
-	if filters.get("company"):
-		conditions += " and sle.company = %s" % frappe.db.escape(filters.get("company"))
+	if company := filters.get("company"):
+		query = query.where(sle.company == company)
 
-	if filters.get("warehouse"):
-		warehouse_details = frappe.db.get_value("Warehouse",
-			filters.get("warehouse"), ["lft", "rgt"], as_dict=1)
-		if warehouse_details:
-			conditions += " and exists (select name from `tabWarehouse` wh \
-				where wh.lft >= %s and wh.rgt <= %s and sle.warehouse = wh.name)"%(warehouse_details.lft,
-				warehouse_details.rgt)
+	if warehouse := filters.get("warehouse"):
+		lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"])
+		chilren_subquery = (
+			frappe.qb.from_(warehouse_table)
+			.select(warehouse_table.name)
+			.where(
+				(warehouse_table.lft >= lft)
+				& (warehouse_table.rgt <= rgt)
+				& (warehouse_table.name == sle.warehouse)
+			)
+		)
+		query = query.where(ExistsCriterion(chilren_subquery))
+	elif warehouse_type := filters.get("warehouse_type"):
+		query = (
+			query.join(warehouse_table)
+			.on(warehouse_table.name == sle.warehouse)
+			.where(warehouse_table.warehouse_type == warehouse_type)
+		)
 
-	if filters.get("warehouse_type") and not filters.get("warehouse"):
-		conditions += " and exists (select name from `tabWarehouse` wh \
-			where wh.warehouse_type = '%s' and sle.warehouse = wh.name)"%(filters.get("warehouse_type"))
+	return query
 
-	return conditions
 
-def get_stock_ledger_entries(filters, items):
-	item_conditions_sql = ''
+def get_stock_ledger_entries(filters: StockBalanceFilter, items: List[str]) -> List[SLEntry]:
+	sle = frappe.qb.DocType("Stock Ledger Entry")
+
+	query = (
+		frappe.qb.from_(sle)
+		.select(
+			sle.item_code,
+			sle.warehouse,
+			sle.posting_date,
+			sle.actual_qty,
+			sle.valuation_rate,
+			sle.company,
+			sle.voucher_type,
+			sle.qty_after_transaction,
+			sle.stock_value_difference,
+			sle.item_code.as_("name"),
+			sle.voucher_no,
+			sle.stock_value,
+			sle.batch_no,
+		)
+		.where((sle.docstatus < 2) & (sle.is_cancelled == 0))
+		.orderby(CombineDatetime(sle.posting_date, sle.posting_time))
+		.orderby(sle.creation)
+		.orderby(sle.actual_qty)
+	)
+
 	if items:
-		item_conditions_sql = ' and sle.item_code in ({})'\
-			.format(', '.join(frappe.db.escape(i, percent=False) for i in items))
+		query = query.where(sle.item_code.isin(items))
 
-	conditions = get_conditions(filters)
+	query = apply_conditions(query, filters)
+	return query.run(as_dict=True)
 
-	return frappe.db.sql("""
-		select
-			sle.item_code, warehouse, sle.posting_date, sle.actual_qty, sle.valuation_rate,
-			sle.company, sle.voucher_type, sle.qty_after_transaction, sle.stock_value_difference,
-			sle.item_code as name, sle.voucher_no, sle.stock_value, sle.batch_no
-		from
-			`tabStock Ledger Entry` sle
-		where sle.docstatus < 2 %s %s
-		and is_cancelled = 0
-		order by sle.posting_date, sle.posting_time, sle.creation, sle.actual_qty""" % #nosec
-		(item_conditions_sql, conditions), as_dict=1)
 
-def get_item_warehouse_map(filters, sle):
+def get_item_warehouse_map(filters: StockBalanceFilter, sle: List[SLEntry]):
 	iwb_map = {}
 	from_date = getdate(filters.get("from_date"))
 	to_date = getdate(filters.get("to_date"))
@@ -183,13 +313,19 @@
 	for d in sle:
 		key = (d.company, d.item_code, d.warehouse)
 		if key not in iwb_map:
-			iwb_map[key] = frappe._dict({
-				"opening_qty": 0.0, "opening_val": 0.0,
-				"in_qty": 0.0, "in_val": 0.0,
-				"out_qty": 0.0, "out_val": 0.0,
-				"bal_qty": 0.0, "bal_val": 0.0,
-				"val_rate": 0.0
-			})
+			iwb_map[key] = frappe._dict(
+				{
+					"opening_qty": 0.0,
+					"opening_val": 0.0,
+					"in_qty": 0.0,
+					"in_val": 0.0,
+					"out_qty": 0.0,
+					"out_val": 0.0,
+					"bal_qty": 0.0,
+					"bal_val": 0.0,
+					"val_rate": 0.0,
+				}
+			)
 
 		qty_dict = iwb_map[(d.company, d.item_code, d.warehouse)]
 
@@ -200,9 +336,11 @@
 
 		value_diff = flt(d.stock_value_difference)
 
-		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"):
+		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
 
@@ -222,7 +360,8 @@
 
 	return iwb_map
 
-def filter_items_with_no_transactions(iwb_map, float_precision):
+
+def filter_items_with_no_transactions(iwb_map, float_precision: float):
 	for (company, item, warehouse) in sorted(iwb_map):
 		qty_dict = iwb_map[(company, item, warehouse)]
 
@@ -238,24 +377,23 @@
 
 	return iwb_map
 
-def get_items(filters):
+
+def get_items(filters: StockBalanceFilter) -> List[str]:
 	"Get items based on item code, item group or brand."
-	conditions = []
-	if filters.get("item_code"):
-		conditions.append("item.name=%(item_code)s")
+	if item_code := filters.get("item_code"):
+		return [item_code]
 	else:
-		if filters.get("item_group"):
-			conditions.append(get_item_group_condition(filters.get("item_group")))
-		if filters.get("brand"): # used in stock analytics report
-			conditions.append("item.brand=%(brand)s")
+		item_filters = {}
+		if item_group := filters.get("item_group"):
+			children = get_descendants_of("Item Group", item_group, ignore_permissions=True)
+			item_filters["item_group"] = ("in", children + [item_group])
+		if brand := filters.get("brand"):
+			item_filters["brand"] = brand
 
-	items = []
-	if conditions:
-		items = frappe.db.sql_list("""select name from `tabItem` item where {}"""
-			.format(" and ".join(conditions)), filters)
-	return items
+		return frappe.get_all("Item", filters=item_filters, pluck="name", order_by=None)
 
-def get_item_details(items, sle, filters):
+
+def get_item_details(items: List[str], sle: List[SLEntry], filters: StockBalanceFilter):
 	item_details = {}
 	if not items:
 		items = list(set(d.item_code for d in sle))
@@ -263,54 +401,73 @@
 	if not items:
 		return item_details
 
-	cf_field = cf_join = ""
-	if filters.get("include_uom"):
-		cf_field = ", ucd.conversion_factor"
-		cf_join = "left join `tabUOM Conversion Detail` ucd on ucd.parent=item.name and ucd.uom=%s" \
-			% frappe.db.escape(filters.get("include_uom"))
+	item_table = frappe.qb.DocType("Item")
 
-	res = frappe.db.sql("""
-		select
-			item.name, item.item_name, item.description, item.item_group, item.brand, item.stock_uom %s
-		from
-			`tabItem` item
-			%s
-		where
-			item.name in (%s)
-	""" % (cf_field, cf_join, ','.join(['%s'] *len(items))), items, as_dict=1)
+	query = (
+		frappe.qb.from_(item_table)
+		.select(
+			item_table.name,
+			item_table.item_name,
+			item_table.description,
+			item_table.item_group,
+			item_table.brand,
+			item_table.stock_uom,
+		)
+		.where(item_table.name.isin(items))
+	)
 
-	for item in res:
-		item_details.setdefault(item.name, item)
+	if uom := filters.get("include_uom"):
+		uom_conv_detail = frappe.qb.DocType("UOM Conversion Detail")
+		query = (
+			query.left_join(uom_conv_detail)
+			.on((uom_conv_detail.parent == item_table.name) & (uom_conv_detail.uom == uom))
+			.select(uom_conv_detail.conversion_factor)
+		)
 
-	if filters.get('show_variant_attributes', 0) == 1:
+	result = query.run(as_dict=1)
+
+	for item_table in result:
+		item_details.setdefault(item_table.name, item_table)
+
+	if filters.get("show_variant_attributes"):
 		variant_values = get_variant_values_for(list(item_details))
 		item_details = {k: v.update(variant_values.get(k, {})) for k, v in item_details.items()}
 
 	return item_details
 
+
 def get_item_reorder_details(items):
 	item_reorder_details = frappe._dict()
 
 	if items:
-		item_reorder_details = frappe.db.sql("""
-			select parent, warehouse, warehouse_reorder_qty, warehouse_reorder_level
-			from `tabItem Reorder`
-			where parent in ({0})
-		""".format(', '.join(frappe.db.escape(i, percent=False) for i in items)), as_dict=1)
+		item_reorder_details = frappe.get_all(
+			"Item Reorder",
+			["parent", "warehouse", "warehouse_reorder_qty", "warehouse_reorder_level"],
+			filters={"parent": ("in", items)},
+		)
 
 	return dict((d.parent + d.warehouse, d) for d in item_reorder_details)
 
-def get_variants_attributes():
-	'''Return all item variant attributes.'''
-	return [i.name for i in frappe.get_all('Item Attribute')]
+
+def get_variants_attributes() -> List[str]:
+	"""Return all item variant attributes."""
+	return frappe.get_all("Item Attribute", pluck="name")
+
 
 def get_variant_values_for(items):
-	'''Returns variant values for items.'''
+	"""Returns variant values for items."""
 	attribute_map = {}
-	for attr in frappe.db.sql('''select parent, attribute, attribute_value
-		from `tabItem Variant Attribute` where parent in (%s)
-		''' % ", ".join(["%s"] * len(items)), tuple(items), as_dict=1):
-			attribute_map.setdefault(attr['parent'], {})
-			attribute_map[attr['parent']].update({attr['attribute']: attr['attribute_value']})
+
+	attribute_info = frappe.get_all(
+		"Item Variant Attribute",
+		["parent", "attribute", "attribute_value"],
+		{
+			"parent": ("in", items),
+		},
+	)
+
+	for attr in attribute_info:
+		attribute_map.setdefault(attr["parent"], {})
+		attribute_map[attr["parent"]].update({attr["attribute"]: attr["attribute_value"]})
 
 	return attribute_map
diff --git a/erpnext/stock/report/stock_balance/test_stock_balance.py b/erpnext/stock/report/stock_balance/test_stock_balance.py
new file mode 100644
index 0000000..e963de2
--- /dev/null
+++ b/erpnext/stock/report/stock_balance/test_stock_balance.py
@@ -0,0 +1,174 @@
+from typing import Any, Dict
+
+import frappe
+from frappe import _dict
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import today
+
+from erpnext.stock.doctype.item.test_item import make_item
+from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+from erpnext.stock.report.stock_balance.stock_balance import execute
+
+
+def stock_balance(filters):
+	"""Get rows from stock balance report"""
+	return [_dict(row) for row in execute(filters)[1]]
+
+
+class TestStockBalance(FrappeTestCase):
+	# ----------- utils
+
+	def setUp(self):
+		self.item = make_item()
+		self.filters = _dict(
+			{
+				"company": "_Test Company",
+				"item_code": self.item.name,
+				"from_date": "2020-01-01",
+				"to_date": str(today()),
+			}
+		)
+
+	def tearDown(self):
+		frappe.db.rollback()
+
+	def assertPartialDictEq(self, expected: Dict[str, Any], actual: Dict[str, Any]):
+		for k, v in expected.items():
+			self.assertEqual(v, actual[k], msg=f"{expected=}\n{actual=}")
+
+	def generate_stock_ledger(self, item_code: str, movements):
+
+		for movement in map(_dict, movements):
+			if "to_warehouse" not in movement:
+				movement.to_warehouse = "_Test Warehouse - _TC"
+			make_stock_entry(item_code=item_code, **movement)
+
+	def assertInvariants(self, rows):
+		last_balance = frappe.db.sql(
+			"""
+			WITH last_balances AS (
+				SELECT item_code, warehouse,
+					stock_value, qty_after_transaction,
+					ROW_NUMBER() OVER (PARTITION BY item_code, warehouse
+						ORDER BY timestamp(posting_date, posting_time) desc, creation desc)
+						AS rn
+					FROM `tabStock Ledger Entry`
+					where is_cancelled=0
+				)
+				SELECT * FROM last_balances WHERE rn = 1""",
+			as_dict=True,
+		)
+
+		item_wh_stock = _dict()
+
+		for line in last_balance:
+			item_wh_stock.setdefault((line.item_code, line.warehouse), line)
+
+		for row in rows:
+			msg = f"Invariants not met for {rows=}"
+			# qty invariant
+			self.assertAlmostEqual(row.bal_qty, row.opening_qty + row.in_qty - row.out_qty, msg)
+
+			# value invariant
+			self.assertAlmostEqual(row.bal_val, row.opening_val + row.in_val - row.out_val, msg)
+
+			# check against SLE
+			last_sle = item_wh_stock[(row.item_code, row.warehouse)]
+			self.assertAlmostEqual(row.bal_qty, last_sle.qty_after_transaction, 3)
+			self.assertAlmostEqual(row.bal_val, last_sle.stock_value, 3)
+
+			# valuation rate
+			if not row.bal_qty:
+				continue
+			self.assertAlmostEqual(row.val_rate, row.bal_val / row.bal_qty, 3, msg)
+
+	# ----------- tests
+
+	def test_basic_stock_balance(self):
+		"""Check very basic functionality and item info"""
+		rows = stock_balance(self.filters)
+		self.assertEqual(rows, [])
+
+		self.generate_stock_ledger(self.item.name, [_dict(qty=5, rate=10)])
+
+		# check item info
+		rows = stock_balance(self.filters)
+		self.assertPartialDictEq(
+			{
+				"item_code": self.item.name,
+				"item_name": self.item.item_name,
+				"item_group": self.item.item_group,
+				"stock_uom": self.item.stock_uom,
+				"in_qty": 5,
+				"in_val": 50,
+				"val_rate": 10,
+			},
+			rows[0],
+		)
+		self.assertInvariants(rows)
+
+	def test_opening_balance(self):
+		self.generate_stock_ledger(
+			self.item.name,
+			[
+				_dict(qty=1, rate=1, posting_date="2021-01-01"),
+				_dict(qty=2, rate=2, posting_date="2021-01-02"),
+				_dict(qty=3, rate=3, posting_date="2021-01-03"),
+			],
+		)
+		rows = stock_balance(self.filters)
+		self.assertInvariants(rows)
+
+		rows = stock_balance(self.filters.update({"from_date": "2021-01-02"}))
+		self.assertInvariants(rows)
+		self.assertPartialDictEq({"opening_qty": 1, "in_qty": 5}, rows[0])
+
+		rows = stock_balance(self.filters.update({"from_date": "2022-01-01"}))
+		self.assertInvariants(rows)
+		self.assertPartialDictEq({"opening_qty": 6, "in_qty": 0}, rows[0])
+
+	def test_uom_converted_info(self):
+
+		self.item.append("uoms", {"conversion_factor": 5, "uom": "Box"})
+		self.item.save()
+
+		self.generate_stock_ledger(self.item.name, [_dict(qty=5, rate=10)])
+
+		rows = stock_balance(self.filters.update({"include_uom": "Box"}))
+		self.assertEqual(rows[0].bal_qty_alt, 1)
+		self.assertInvariants(rows)
+
+	def test_item_group(self):
+		self.filters.pop("item_code", None)
+		rows = stock_balance(self.filters.update({"item_group": self.item.item_group}))
+		self.assertTrue(all(r.item_group == self.item.item_group for r in rows))
+
+	def test_child_warehouse_balances(self):
+		# This is default
+		self.generate_stock_ledger(self.item.name, [_dict(qty=5, rate=10, to_warehouse="Stores - _TC")])
+
+		self.filters.pop("item_code", None)
+		rows = stock_balance(self.filters.update({"warehouse": "All Warehouses - _TC"}))
+
+		self.assertTrue(
+			any(r.item_code == self.item.name and r.warehouse == "Stores - _TC" for r in rows),
+			msg=f"Expected child warehouse balances \n{rows}",
+		)
+
+	def test_show_item_attr(self):
+		from erpnext.controllers.item_variant import create_variant
+
+		self.item.has_variants = True
+		self.item.append("attributes", {"attribute": "Test Size"})
+		self.item.save()
+
+		attributes = {"Test Size": "Large"}
+		variant = create_variant(self.item.name, attributes)
+		variant.save()
+
+		self.generate_stock_ledger(variant.name, [_dict(qty=5, rate=10)])
+		rows = stock_balance(
+			self.filters.update({"show_variant_attributes": 1, "item_code": variant.name})
+		)
+		self.assertPartialDictEq(attributes, rows[0])
+		self.assertInvariants(rows)
diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py
index 81fa045..ef1642e 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.py
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.py
@@ -7,6 +7,7 @@
 from frappe.utils import cint, flt
 
 from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import get_stock_balance_for
 from erpnext.stock.utils import (
 	is_reposting_item_valuation_in_progress,
 	update_included_uom_in_report,
@@ -41,19 +42,13 @@
 			actual_qty += flt(sle.actual_qty, precision)
 			stock_value += sle.stock_value_difference
 
-			if sle.voucher_type == 'Stock Reconciliation' and not sle.actual_qty:
+			if sle.voucher_type == "Stock Reconciliation" and not sle.actual_qty:
 				actual_qty = sle.qty_after_transaction
 				stock_value = sle.stock_value
 
-			sle.update({
-				"qty_after_transaction": actual_qty,
-				"stock_value": stock_value
-			})
+			sle.update({"qty_after_transaction": actual_qty, "stock_value": stock_value})
 
-		sle.update({
-			"in_qty": max(sle.actual_qty, 0),
-			"out_qty": min(sle.actual_qty, 0)
-		})
+		sle.update({"in_qty": max(sle.actual_qty, 0), "out_qty": min(sle.actual_qty, 0)})
 
 		if sle.serial_no:
 			update_available_serial_nos(available_serial_nos, sle)
@@ -66,11 +61,16 @@
 	update_included_uom_in_report(columns, data, include_uom, conversion_factors)
 	return columns, data
 
+
 def update_available_serial_nos(available_serial_nos, sle):
 	serial_nos = get_serial_nos(sle.serial_no)
 	key = (sle.item_code, sle.warehouse)
 	if key not in available_serial_nos:
-		available_serial_nos.setdefault(key, [])
+		stock_balance = get_stock_balance_for(
+			sle.item_code, sle.warehouse, sle.date.split(" ")[0], sle.date.split(" ")[1]
+		)
+		serials = get_serial_nos(stock_balance["serial_nos"]) if stock_balance["serial_nos"] else []
+		available_serial_nos.setdefault(key, serials)
 
 	existing_serial_no = available_serial_nos[key]
 	for sn in serial_nos:
@@ -85,47 +85,160 @@
 			else:
 				existing_serial_no.append(sn)
 
-	sle.balance_serial_no = '\n'.join(existing_serial_no)
+	sle.balance_serial_no = "\n".join(existing_serial_no)
+
 
 def get_columns():
 	columns = [
 		{"label": _("Date"), "fieldname": "date", "fieldtype": "Datetime", "width": 150},
-		{"label": _("Item"), "fieldname": "item_code", "fieldtype": "Link", "options": "Item", "width": 100},
+		{
+			"label": _("Item"),
+			"fieldname": "item_code",
+			"fieldtype": "Link",
+			"options": "Item",
+			"width": 100,
+		},
 		{"label": _("Item Name"), "fieldname": "item_name", "width": 100},
-		{"label": _("Stock UOM"), "fieldname": "stock_uom", "fieldtype": "Link", "options": "UOM", "width": 90},
-		{"label": _("In Qty"), "fieldname": "in_qty", "fieldtype": "Float", "width": 80, "convertible": "qty"},
-		{"label": _("Out Qty"), "fieldname": "out_qty", "fieldtype": "Float", "width": 80, "convertible": "qty"},
-		{"label": _("Balance Qty"), "fieldname": "qty_after_transaction", "fieldtype": "Float", "width": 100, "convertible": "qty"},
-		{"label": _("Voucher #"), "fieldname": "voucher_no", "fieldtype": "Dynamic Link", "options": "voucher_type", "width": 150},
-		{"label": _("Warehouse"), "fieldname": "warehouse", "fieldtype": "Link", "options": "Warehouse", "width": 150},
-		{"label": _("Item Group"), "fieldname": "item_group", "fieldtype": "Link", "options": "Item Group", "width": 100},
-		{"label": _("Brand"), "fieldname": "brand", "fieldtype": "Link", "options": "Brand", "width": 100},
+		{
+			"label": _("Stock UOM"),
+			"fieldname": "stock_uom",
+			"fieldtype": "Link",
+			"options": "UOM",
+			"width": 90,
+		},
+		{
+			"label": _("In Qty"),
+			"fieldname": "in_qty",
+			"fieldtype": "Float",
+			"width": 80,
+			"convertible": "qty",
+		},
+		{
+			"label": _("Out Qty"),
+			"fieldname": "out_qty",
+			"fieldtype": "Float",
+			"width": 80,
+			"convertible": "qty",
+		},
+		{
+			"label": _("Balance Qty"),
+			"fieldname": "qty_after_transaction",
+			"fieldtype": "Float",
+			"width": 100,
+			"convertible": "qty",
+		},
+		{
+			"label": _("Voucher #"),
+			"fieldname": "voucher_no",
+			"fieldtype": "Dynamic Link",
+			"options": "voucher_type",
+			"width": 150,
+		},
+		{
+			"label": _("Warehouse"),
+			"fieldname": "warehouse",
+			"fieldtype": "Link",
+			"options": "Warehouse",
+			"width": 150,
+		},
+		{
+			"label": _("Item Group"),
+			"fieldname": "item_group",
+			"fieldtype": "Link",
+			"options": "Item Group",
+			"width": 100,
+		},
+		{
+			"label": _("Brand"),
+			"fieldname": "brand",
+			"fieldtype": "Link",
+			"options": "Brand",
+			"width": 100,
+		},
 		{"label": _("Description"), "fieldname": "description", "width": 200},
-		{"label": _("Incoming Rate"), "fieldname": "incoming_rate", "fieldtype": "Currency", "width": 110, "options": "Company:company:default_currency", "convertible": "rate"},
-		{"label": _("Valuation Rate"), "fieldname": "valuation_rate", "fieldtype": "Currency", "width": 110, "options": "Company:company:default_currency", "convertible": "rate"},
-		{"label": _("Balance Value"), "fieldname": "stock_value", "fieldtype": "Currency", "width": 110, "options": "Company:company:default_currency"},
-		{"label": _("Value Change"), "fieldname": "stock_value_difference", "fieldtype": "Currency", "width": 110, "options": "Company:company:default_currency"},
+		{
+			"label": _("Incoming Rate"),
+			"fieldname": "incoming_rate",
+			"fieldtype": "Currency",
+			"width": 110,
+			"options": "Company:company:default_currency",
+			"convertible": "rate",
+		},
+		{
+			"label": _("Valuation Rate"),
+			"fieldname": "valuation_rate",
+			"fieldtype": "Currency",
+			"width": 110,
+			"options": "Company:company:default_currency",
+			"convertible": "rate",
+		},
+		{
+			"label": _("Balance Value"),
+			"fieldname": "stock_value",
+			"fieldtype": "Currency",
+			"width": 110,
+			"options": "Company:company:default_currency",
+		},
+		{
+			"label": _("Value Change"),
+			"fieldname": "stock_value_difference",
+			"fieldtype": "Currency",
+			"width": 110,
+			"options": "Company:company:default_currency",
+		},
 		{"label": _("Voucher Type"), "fieldname": "voucher_type", "width": 110},
-		{"label": _("Voucher #"), "fieldname": "voucher_no", "fieldtype": "Dynamic Link", "options": "voucher_type", "width": 100},
-		{"label": _("Batch"), "fieldname": "batch_no", "fieldtype": "Link", "options": "Batch", "width": 100},
-		{"label": _("Serial No"), "fieldname": "serial_no", "fieldtype": "Link", "options": "Serial No", "width": 100},
+		{
+			"label": _("Voucher #"),
+			"fieldname": "voucher_no",
+			"fieldtype": "Dynamic Link",
+			"options": "voucher_type",
+			"width": 100,
+		},
+		{
+			"label": _("Batch"),
+			"fieldname": "batch_no",
+			"fieldtype": "Link",
+			"options": "Batch",
+			"width": 100,
+		},
+		{
+			"label": _("Serial No"),
+			"fieldname": "serial_no",
+			"fieldtype": "Link",
+			"options": "Serial No",
+			"width": 100,
+		},
 		{"label": _("Balance Serial No"), "fieldname": "balance_serial_no", "width": 100},
-		{"label": _("Project"), "fieldname": "project", "fieldtype": "Link", "options": "Project", "width": 100},
-		{"label": _("Company"), "fieldname": "company", "fieldtype": "Link", "options": "Company", "width": 110}
+		{
+			"label": _("Project"),
+			"fieldname": "project",
+			"fieldtype": "Link",
+			"options": "Project",
+			"width": 100,
+		},
+		{
+			"label": _("Company"),
+			"fieldname": "company",
+			"fieldtype": "Link",
+			"options": "Company",
+			"width": 110,
+		},
 	]
 
 	return columns
 
 
 def get_stock_ledger_entries(filters, items):
-	item_conditions_sql = ''
+	item_conditions_sql = ""
 	if items:
-		item_conditions_sql = 'and sle.item_code in ({})'\
-			.format(', '.join(frappe.db.escape(i) for i in items))
+		item_conditions_sql = "and sle.item_code in ({})".format(
+			", ".join(frappe.db.escape(i) for i in items)
+		)
 
-	sl_entries = frappe.db.sql("""
+	sl_entries = frappe.db.sql(
+		"""
 		SELECT
-			concat_ws(" ", posting_date, posting_time) AS date,
+			concat_ws(' ', posting_date, posting_time) AS date,
 			item_code,
 			warehouse,
 			actual_qty,
@@ -149,8 +262,12 @@
 				{item_conditions_sql}
 		ORDER BY
 			posting_date asc, posting_time asc, creation asc
-		""".format(sle_conditions=get_sle_conditions(filters), item_conditions_sql=item_conditions_sql),
-		filters, as_dict=1)
+		""".format(
+			sle_conditions=get_sle_conditions(filters), item_conditions_sql=item_conditions_sql
+		),
+		filters,
+		as_dict=1,
+	)
 
 	return sl_entries
 
@@ -167,8 +284,9 @@
 
 	items = []
 	if conditions:
-		items = frappe.db.sql_list("""select name from `tabItem` item where {}"""
-			.format(" and ".join(conditions)), filters)
+		items = frappe.db.sql_list(
+			"""select name from `tabItem` item where {}""".format(" and ".join(conditions)), filters
+		)
 	return items
 
 
@@ -183,10 +301,13 @@
 	cf_field = cf_join = ""
 	if include_uom:
 		cf_field = ", ucd.conversion_factor"
-		cf_join = "left join `tabUOM Conversion Detail` ucd on ucd.parent=item.name and ucd.uom=%s" \
+		cf_join = (
+			"left join `tabUOM Conversion Detail` ucd on ucd.parent=item.name and ucd.uom=%s"
 			% frappe.db.escape(include_uom)
+		)
 
-	res = frappe.db.sql("""
+	res = frappe.db.sql(
+		"""
 		select
 			item.name, item.item_name, item.description, item.item_group, item.brand, item.stock_uom {cf_field}
 		from
@@ -194,7 +315,12 @@
 			{cf_join}
 		where
 			item.name in ({item_codes})
-	""".format(cf_field=cf_field, cf_join=cf_join, item_codes=','.join(['%s'] *len(items))), items, as_dict=1)
+	""".format(
+			cf_field=cf_field, cf_join=cf_join, item_codes=",".join(["%s"] * len(items))
+		),
+		items,
+		as_dict=1,
+	)
 
 	for item in res:
 		item_details.setdefault(item.name, item)
@@ -223,16 +349,20 @@
 		return
 
 	from erpnext.stock.stock_ledger import get_previous_sle
-	last_entry = get_previous_sle({
-		"item_code": filters.item_code,
-		"warehouse_condition": get_warehouse_condition(filters.warehouse),
-		"posting_date": filters.from_date,
-		"posting_time": "00:00:00"
-	})
+
+	last_entry = get_previous_sle(
+		{
+			"item_code": filters.item_code,
+			"warehouse_condition": get_warehouse_condition(filters.warehouse),
+			"posting_date": filters.from_date,
+			"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"
+		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"
 		):
@@ -243,7 +373,7 @@
 		"item_code": _("'Opening'"),
 		"qty_after_transaction": last_entry.get("qty_after_transaction", 0),
 		"valuation_rate": last_entry.get("valuation_rate", 0),
-		"stock_value": last_entry.get("stock_value", 0)
+		"stock_value": last_entry.get("stock_value", 0),
 	}
 
 	return row
@@ -252,18 +382,22 @@
 def get_warehouse_condition(warehouse):
 	warehouse_details = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"], as_dict=1)
 	if warehouse_details:
-		return " exists (select name from `tabWarehouse` wh \
-			where wh.lft >= %s and wh.rgt <= %s and warehouse = wh.name)"%(warehouse_details.lft,
-			warehouse_details.rgt)
+		return (
+			" exists (select name from `tabWarehouse` wh \
+			where wh.lft >= %s and wh.rgt <= %s and warehouse = wh.name)"
+			% (warehouse_details.lft, warehouse_details.rgt)
+		)
 
-	return ''
+	return ""
 
 
 def get_item_group_condition(item_group):
 	item_group_details = frappe.db.get_value("Item Group", item_group, ["lft", "rgt"], as_dict=1)
 	if item_group_details:
-		return "item.item_group in (select ig.name from `tabItem Group` ig \
-			where ig.lft >= %s and ig.rgt <= %s and item.item_group = ig.name)"%(item_group_details.lft,
-			item_group_details.rgt)
+		return (
+			"item.item_group in (select ig.name from `tabItem Group` ig \
+			where ig.lft >= %s and ig.rgt <= %s and item.item_group = ig.name)"
+			% (item_group_details.lft, item_group_details.rgt)
+		)
 
-	return ''
+	return ""
diff --git a/erpnext/stock/report/stock_ledger/test_stock_ledger_report.py b/erpnext/stock/report/stock_ledger/test_stock_ledger_report.py
new file mode 100644
index 0000000..f93bd66
--- /dev/null
+++ b/erpnext/stock/report/stock_ledger/test_stock_ledger_report.py
@@ -0,0 +1,42 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+import frappe
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import add_days, today
+
+from erpnext.maintenance.doctype.maintenance_schedule.test_maintenance_schedule import (
+	make_serial_item_with_serial,
+)
+from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+from erpnext.stock.report.stock_ledger.stock_ledger import execute
+
+
+class TestStockLedgerReeport(FrappeTestCase):
+	def setUp(self) -> None:
+		make_serial_item_with_serial("_Test Stock Report Serial Item")
+		self.filters = frappe._dict(
+			company="_Test Company",
+			from_date=today(),
+			to_date=add_days(today(), 30),
+			item_code="_Test Stock Report Serial Item",
+		)
+
+	def tearDown(self) -> None:
+		frappe.db.rollback()
+
+	def test_serial_balance(self):
+		item_code = "_Test Stock Report Serial Item"
+		# Checks serials which were added through stock in entry.
+		columns, data = execute(self.filters)
+		self.assertEqual(data[0].in_qty, 2)
+		serials_added = get_serial_nos(data[0].serial_no)
+		self.assertEqual(len(serials_added), 2)
+		# Stock out entry for one of the serials.
+		dn = create_delivery_note(item=item_code, serial_no=serials_added[1])
+		self.filters.voucher_no = dn.name
+		columns, data = execute(self.filters)
+		self.assertEqual(data[0].out_qty, -1)
+		self.assertEqual(data[0].serial_no, serials_added[1])
+		self.assertEqual(data[0].balance_serial_no, serials_added[0])
diff --git a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py
index 1ba2482..ed0e2fc 100644
--- a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py
+++ b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py
@@ -4,6 +4,7 @@
 import json
 
 import frappe
+from frappe import _
 
 SLE_FIELDS = (
 	"name",
@@ -40,11 +41,7 @@
 	return frappe.get_all(
 		"Stock Ledger Entry",
 		fields=SLE_FIELDS,
-		filters={
-			"item_code": filters.item_code,
-			"warehouse": filters.warehouse,
-			"is_cancelled": 0
-		},
+		filters={"item_code": filters.item_code, "warehouse": filters.warehouse, "is_cancelled": 0},
 		order_by="timestamp(posting_date, posting_time), creation",
 	)
 
@@ -62,7 +59,7 @@
 			fifo_value += qty * rate
 
 		if sle.actual_qty < 0:
-			sle.consumption_rate = sle.stock_value_difference  / sle.actual_qty
+			sle.consumption_rate = sle.stock_value_difference / sle.actual_qty
 
 		balance_qty += sle.actual_qty
 		balance_stock_value += sle.stock_value_difference
@@ -90,14 +87,16 @@
 		sle.valuation_diff = (
 			sle.valuation_rate - sle.balance_value_by_qty if sle.balance_value_by_qty else None
 		)
-		sle.diff_value_diff = sle.stock_value_from_diff -  sle.stock_value
+		sle.diff_value_diff = sle.stock_value_from_diff - sle.stock_value
 
 		if idx > 0:
 			sle.fifo_stock_diff = sle.fifo_stock_value - sles[idx - 1].fifo_stock_value
 			sle.fifo_difference_diff = sle.fifo_stock_diff - sle.stock_value_difference
 
 		if sle.batch_no:
-			sle.use_batchwise_valuation = frappe.db.get_value("Batch", sle.batch_no, "use_batchwise_valuation", cache=True)
+			sle.use_batchwise_valuation = frappe.db.get_value(
+				"Batch", sle.batch_no, "use_batchwise_valuation", cache=True
+			)
 
 	return sles
 
@@ -107,157 +106,155 @@
 		{
 			"fieldname": "name",
 			"fieldtype": "Link",
-			"label": "Stock Ledger Entry",
+			"label": _("Stock Ledger Entry"),
 			"options": "Stock Ledger Entry",
 		},
 		{
 			"fieldname": "posting_date",
-			"fieldtype": "Date",
-			"label": "Posting Date",
+			"fieldtype": "Data",
+			"label": _("Posting Date"),
 		},
 		{
 			"fieldname": "posting_time",
-			"fieldtype": "Time",
-			"label": "Posting Time",
+			"fieldtype": "Data",
+			"label": _("Posting Time"),
 		},
 		{
 			"fieldname": "creation",
-			"fieldtype": "Datetime",
-			"label": "Creation",
+			"fieldtype": "Data",
+			"label": _("Creation"),
 		},
 		{
 			"fieldname": "voucher_type",
 			"fieldtype": "Link",
-			"label": "Voucher Type",
+			"label": _("Voucher Type"),
 			"options": "DocType",
 		},
 		{
 			"fieldname": "voucher_no",
 			"fieldtype": "Dynamic Link",
-			"label": "Voucher No",
+			"label": _("Voucher No"),
 			"options": "voucher_type",
 		},
 		{
 			"fieldname": "batch_no",
 			"fieldtype": "Link",
-			"label": "Batch",
+			"label": _("Batch"),
 			"options": "Batch",
 		},
 		{
 			"fieldname": "use_batchwise_valuation",
 			"fieldtype": "Check",
-			"label": "Batchwise Valuation",
+			"label": _("Batchwise Valuation"),
 		},
 		{
 			"fieldname": "actual_qty",
 			"fieldtype": "Float",
-			"label": "Qty Change",
+			"label": _("Qty Change"),
 		},
 		{
 			"fieldname": "incoming_rate",
 			"fieldtype": "Float",
-			"label": "Incoming Rate",
+			"label": _("Incoming Rate"),
 		},
 		{
 			"fieldname": "consumption_rate",
 			"fieldtype": "Float",
-			"label": "Consumption Rate",
+			"label": _("Consumption Rate"),
 		},
 		{
 			"fieldname": "qty_after_transaction",
 			"fieldtype": "Float",
-			"label": "(A) Qty After Transaction",
+			"label": _("(A) Qty After Transaction"),
 		},
 		{
 			"fieldname": "expected_qty_after_transaction",
 			"fieldtype": "Float",
-			"label": "(B) Expected Qty After Transaction",
+			"label": _("(B) Expected Qty After Transaction"),
 		},
 		{
 			"fieldname": "difference_in_qty",
 			"fieldtype": "Float",
-			"label": "A - B",
+			"label": _("A - B"),
 		},
 		{
 			"fieldname": "stock_queue",
 			"fieldtype": "Data",
-			"label": "FIFO/LIFO Queue",
+			"label": _("FIFO/LIFO Queue"),
 		},
-
 		{
 			"fieldname": "fifo_queue_qty",
 			"fieldtype": "Float",
-			"label": "(C) Total qty in queue",
+			"label": _("(C) Total qty in queue"),
 		},
 		{
 			"fieldname": "fifo_qty_diff",
 			"fieldtype": "Float",
-			"label": "A - C",
+			"label": _("A - C"),
 		},
 		{
 			"fieldname": "stock_value",
 			"fieldtype": "Float",
-			"label": "(D) Balance Stock Value",
+			"label": _("(D) Balance Stock Value"),
 		},
 		{
 			"fieldname": "fifo_stock_value",
 			"fieldtype": "Float",
-			"label": "(E) Balance Stock Value in Queue",
+			"label": _("(E) Balance Stock Value in Queue"),
 		},
 		{
 			"fieldname": "fifo_value_diff",
 			"fieldtype": "Float",
-			"label": "D - E",
+			"label": _("D - E"),
 		},
 		{
 			"fieldname": "stock_value_difference",
 			"fieldtype": "Float",
-			"label": "(F) Stock Value Difference",
+			"label": _("(F) Stock Value Difference"),
 		},
 		{
 			"fieldname": "stock_value_from_diff",
 			"fieldtype": "Float",
-			"label": "Balance Stock Value using (F)",
+			"label": _("Balance Stock Value using (F)"),
 		},
 		{
 			"fieldname": "diff_value_diff",
 			"fieldtype": "Float",
-			"label": "K - D",
+			"label": _("K - D"),
 		},
 		{
 			"fieldname": "fifo_stock_diff",
 			"fieldtype": "Float",
-			"label": "(G) Stock Value difference (FIFO queue)",
+			"label": _("(G) Stock Value difference (FIFO queue)"),
 		},
 		{
 			"fieldname": "fifo_difference_diff",
 			"fieldtype": "Float",
-			"label": "F - G",
+			"label": _("F - G"),
 		},
 		{
 			"fieldname": "valuation_rate",
 			"fieldtype": "Float",
-			"label": "(H) Valuation Rate",
+			"label": _("(H) Valuation Rate"),
 		},
 		{
 			"fieldname": "fifo_valuation_rate",
 			"fieldtype": "Float",
-			"label": "(I) Valuation Rate as per FIFO",
+			"label": _("(I) Valuation Rate as per FIFO"),
 		},
-
 		{
 			"fieldname": "fifo_valuation_diff",
 			"fieldtype": "Float",
-			"label": "H - I",
+			"label": _("H - I"),
 		},
 		{
 			"fieldname": "balance_value_by_qty",
 			"fieldtype": "Float",
-			"label": "(J) Valuation = Value (D) ÷ Qty (A)",
+			"label": _("(J) Valuation = Value (D) ÷ Qty (A)"),
 		},
 		{
 			"fieldname": "valuation_diff",
 			"fieldtype": "Float",
-			"label": "H - J",
+			"label": _("H - J"),
 		},
 	]
diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
index a28b752..49e797d 100644
--- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
+++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
@@ -32,8 +32,9 @@
 			continue
 
 		# item = item_map.setdefault(bin.item_code, get_item(bin.item_code))
-		company = warehouse_company.setdefault(bin.warehouse,
-			frappe.db.get_value("Warehouse", bin.warehouse, "company"))
+		company = warehouse_company.setdefault(
+			bin.warehouse, frappe.db.get_value("Warehouse", bin.warehouse, "company")
+		)
 
 		if filters.brand and filters.brand != item.brand:
 			continue
@@ -59,10 +60,29 @@
 		if reserved_qty_for_pos:
 			bin.projected_qty -= reserved_qty_for_pos
 
-		data.append([item.name, item.item_name, item.description, item.item_group, item.brand, bin.warehouse,
-			item.stock_uom, bin.actual_qty, bin.planned_qty, bin.indented_qty, bin.ordered_qty,
-			bin.reserved_qty, bin.reserved_qty_for_production, bin.reserved_qty_for_sub_contract, reserved_qty_for_pos,
-			bin.projected_qty, re_order_level, re_order_qty, shortage_qty])
+		data.append(
+			[
+				item.name,
+				item.item_name,
+				item.description,
+				item.item_group,
+				item.brand,
+				bin.warehouse,
+				item.stock_uom,
+				bin.actual_qty,
+				bin.planned_qty,
+				bin.indented_qty,
+				bin.ordered_qty,
+				bin.reserved_qty,
+				bin.reserved_qty_for_production,
+				bin.reserved_qty_for_sub_contract,
+				reserved_qty_for_pos,
+				bin.projected_qty,
+				re_order_level,
+				re_order_qty,
+				shortage_qty,
+			]
+		)
 
 		if include_uom:
 			conversion_factors.append(item.conversion_factor)
@@ -70,66 +90,180 @@
 	update_included_uom_in_report(columns, data, include_uom, conversion_factors)
 	return columns, data
 
+
 def get_columns():
 	return [
-		{"label": _("Item Code"), "fieldname": "item_code", "fieldtype": "Link", "options": "Item", "width": 140},
+		{
+			"label": _("Item Code"),
+			"fieldname": "item_code",
+			"fieldtype": "Link",
+			"options": "Item",
+			"width": 140,
+		},
 		{"label": _("Item Name"), "fieldname": "item_name", "width": 100},
 		{"label": _("Description"), "fieldname": "description", "width": 200},
-		{"label": _("Item Group"), "fieldname": "item_group", "fieldtype": "Link", "options": "Item Group", "width": 100},
-		{"label": _("Brand"), "fieldname": "brand", "fieldtype": "Link", "options": "Brand", "width": 100},
-		{"label": _("Warehouse"), "fieldname": "warehouse", "fieldtype": "Link", "options": "Warehouse", "width": 120},
-		{"label": _("UOM"), "fieldname": "stock_uom", "fieldtype": "Link", "options": "UOM", "width": 100},
-		{"label": _("Actual Qty"), "fieldname": "actual_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"},
-		{"label": _("Planned Qty"), "fieldname": "planned_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"},
-		{"label": _("Requested Qty"), "fieldname": "indented_qty", "fieldtype": "Float", "width": 110, "convertible": "qty"},
-		{"label": _("Ordered Qty"), "fieldname": "ordered_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"},
-		{"label": _("Reserved Qty"), "fieldname": "reserved_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"},
-		{"label": _("Reserved for Production"), "fieldname": "reserved_qty_for_production", "fieldtype": "Float",
-			"width": 100, "convertible": "qty"},
-		{"label": _("Reserved for Sub Contracting"), "fieldname": "reserved_qty_for_sub_contract", "fieldtype": "Float",
-			"width": 100, "convertible": "qty"},
-		{"label": _("Reserved for POS Transactions"), "fieldname": "reserved_qty_for_pos", "fieldtype": "Float",
-			"width": 100, "convertible": "qty"},
-		{"label": _("Projected Qty"), "fieldname": "projected_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"},
-		{"label": _("Reorder Level"), "fieldname": "re_order_level", "fieldtype": "Float", "width": 100, "convertible": "qty"},
-		{"label": _("Reorder Qty"), "fieldname": "re_order_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"},
-		{"label": _("Shortage Qty"), "fieldname": "shortage_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"}
+		{
+			"label": _("Item Group"),
+			"fieldname": "item_group",
+			"fieldtype": "Link",
+			"options": "Item Group",
+			"width": 100,
+		},
+		{
+			"label": _("Brand"),
+			"fieldname": "brand",
+			"fieldtype": "Link",
+			"options": "Brand",
+			"width": 100,
+		},
+		{
+			"label": _("Warehouse"),
+			"fieldname": "warehouse",
+			"fieldtype": "Link",
+			"options": "Warehouse",
+			"width": 120,
+		},
+		{
+			"label": _("UOM"),
+			"fieldname": "stock_uom",
+			"fieldtype": "Link",
+			"options": "UOM",
+			"width": 100,
+		},
+		{
+			"label": _("Actual Qty"),
+			"fieldname": "actual_qty",
+			"fieldtype": "Float",
+			"width": 100,
+			"convertible": "qty",
+		},
+		{
+			"label": _("Planned Qty"),
+			"fieldname": "planned_qty",
+			"fieldtype": "Float",
+			"width": 100,
+			"convertible": "qty",
+		},
+		{
+			"label": _("Requested Qty"),
+			"fieldname": "indented_qty",
+			"fieldtype": "Float",
+			"width": 110,
+			"convertible": "qty",
+		},
+		{
+			"label": _("Ordered Qty"),
+			"fieldname": "ordered_qty",
+			"fieldtype": "Float",
+			"width": 100,
+			"convertible": "qty",
+		},
+		{
+			"label": _("Reserved Qty"),
+			"fieldname": "reserved_qty",
+			"fieldtype": "Float",
+			"width": 100,
+			"convertible": "qty",
+		},
+		{
+			"label": _("Reserved for Production"),
+			"fieldname": "reserved_qty_for_production",
+			"fieldtype": "Float",
+			"width": 100,
+			"convertible": "qty",
+		},
+		{
+			"label": _("Reserved for Sub Contracting"),
+			"fieldname": "reserved_qty_for_sub_contract",
+			"fieldtype": "Float",
+			"width": 100,
+			"convertible": "qty",
+		},
+		{
+			"label": _("Reserved for POS Transactions"),
+			"fieldname": "reserved_qty_for_pos",
+			"fieldtype": "Float",
+			"width": 100,
+			"convertible": "qty",
+		},
+		{
+			"label": _("Projected Qty"),
+			"fieldname": "projected_qty",
+			"fieldtype": "Float",
+			"width": 100,
+			"convertible": "qty",
+		},
+		{
+			"label": _("Reorder Level"),
+			"fieldname": "re_order_level",
+			"fieldtype": "Float",
+			"width": 100,
+			"convertible": "qty",
+		},
+		{
+			"label": _("Reorder Qty"),
+			"fieldname": "re_order_qty",
+			"fieldtype": "Float",
+			"width": 100,
+			"convertible": "qty",
+		},
+		{
+			"label": _("Shortage Qty"),
+			"fieldname": "shortage_qty",
+			"fieldtype": "Float",
+			"width": 100,
+			"convertible": "qty",
+		},
 	]
 
+
 def get_bin_list(filters):
 	conditions = []
 
 	if filters.item_code:
-		conditions.append("item_code = '%s' "%filters.item_code)
+		conditions.append("item_code = '%s' " % filters.item_code)
 
 	if filters.warehouse:
-		warehouse_details = frappe.db.get_value("Warehouse", filters.warehouse, ["lft", "rgt"], as_dict=1)
+		warehouse_details = frappe.db.get_value(
+			"Warehouse", filters.warehouse, ["lft", "rgt"], as_dict=1
+		)
 
 		if warehouse_details:
-			conditions.append(" exists (select name from `tabWarehouse` wh \
-				where wh.lft >= %s and wh.rgt <= %s and bin.warehouse = wh.name)"%(warehouse_details.lft,
-				warehouse_details.rgt))
+			conditions.append(
+				" exists (select name from `tabWarehouse` wh \
+				where wh.lft >= %s and wh.rgt <= %s and bin.warehouse = wh.name)"
+				% (warehouse_details.lft, warehouse_details.rgt)
+			)
 
-	bin_list = frappe.db.sql("""select item_code, warehouse, actual_qty, planned_qty, indented_qty,
+	bin_list = frappe.db.sql(
+		"""select item_code, warehouse, actual_qty, planned_qty, indented_qty,
 		ordered_qty, reserved_qty, reserved_qty_for_production, reserved_qty_for_sub_contract, projected_qty
 		from tabBin bin {conditions} order by item_code, warehouse
-		""".format(conditions=" where " + " and ".join(conditions) if conditions else ""), as_dict=1)
+		""".format(
+			conditions=" where " + " and ".join(conditions) if conditions else ""
+		),
+		as_dict=1,
+	)
 
 	return bin_list
 
+
 def get_item_map(item_code, include_uom):
 	"""Optimization: get only the item doc and re_order_levels table"""
 
 	condition = ""
 	if item_code:
-		condition = 'and item_code = {0}'.format(frappe.db.escape(item_code, percent=False))
+		condition = "and item_code = {0}".format(frappe.db.escape(item_code, percent=False))
 
 	cf_field = cf_join = ""
 	if include_uom:
 		cf_field = ", ucd.conversion_factor"
-		cf_join = "left join `tabUOM Conversion Detail` ucd on ucd.parent=item.name and ucd.uom=%(include_uom)s"
+		cf_join = (
+			"left join `tabUOM Conversion Detail` ucd on ucd.parent=item.name and ucd.uom=%(include_uom)s"
+		)
 
-	items = frappe.db.sql("""
+	items = frappe.db.sql(
+		"""
 		select item.name, item.item_name, item.description, item.item_group, item.brand, item.stock_uom{cf_field}
 		from `tabItem` item
 		{cf_join}
@@ -137,16 +271,21 @@
 		and item.disabled=0
 		{condition}
 		and (item.end_of_life > %(today)s or item.end_of_life is null or item.end_of_life='0000-00-00')
-		and exists (select name from `tabBin` bin where bin.item_code=item.name)"""\
-		.format(cf_field=cf_field, cf_join=cf_join, condition=condition),
-		{"today": today(), "include_uom": include_uom}, as_dict=True)
+		and exists (select name from `tabBin` bin where bin.item_code=item.name)""".format(
+			cf_field=cf_field, cf_join=cf_join, condition=condition
+		),
+		{"today": today(), "include_uom": include_uom},
+		as_dict=True,
+	)
 
 	condition = ""
 	if item_code:
-		condition = 'where parent={0}'.format(frappe.db.escape(item_code, percent=False))
+		condition = "where parent={0}".format(frappe.db.escape(item_code, percent=False))
 
 	reorder_levels = frappe._dict()
-	for ir in frappe.db.sql("""select * from `tabItem Reorder` {condition}""".format(condition=condition), as_dict=1):
+	for ir in frappe.db.sql(
+		"""select * from `tabItem Reorder` {condition}""".format(condition=condition), as_dict=1
+	):
 		if ir.parent not in reorder_levels:
 			reorder_levels[ir.parent] = []
 
diff --git a/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py b/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py
index a7b4835..70f04da 100644
--- a/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py
+++ b/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py
@@ -12,12 +12,14 @@
 	data = get_data(filters.warehouse)
 	return columns, data
 
+
 def validate_warehouse(filters):
 	company = filters.company
 	warehouse = filters.warehouse
 	if not frappe.db.exists("Warehouse", {"name": warehouse, "company": company}):
 		frappe.throw(_("Warehouse: {0} does not belong to {1}").format(warehouse, company))
 
+
 def get_columns():
 	columns = [
 		{
@@ -25,49 +27,37 @@
 			"fieldname": "item_code",
 			"fieldtype": "Link",
 			"options": "Item",
-			"width": 200
+			"width": 200,
 		},
-		{
-			"label": _("Item Name"),
-			"fieldname": "item_name",
-			"fieldtype": "Data",
-			"width": 200
-		},
-		{
-			"label": _("Serial No Count"),
-			"fieldname": "total",
-			"fieldtype": "Float",
-			"width": 150
-		},
-		{
-			"label": _("Stock Qty"),
-			"fieldname": "stock_qty",
-			"fieldtype": "Float",
-			"width": 150
-		},
-		{
-			"label": _("Difference"),
-			"fieldname": "difference",
-			"fieldtype": "Float",
-			"width": 150
-		},
+		{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 200},
+		{"label": _("Serial No Count"), "fieldname": "total", "fieldtype": "Float", "width": 150},
+		{"label": _("Stock Qty"), "fieldname": "stock_qty", "fieldtype": "Float", "width": 150},
+		{"label": _("Difference"), "fieldname": "difference", "fieldtype": "Float", "width": 150},
 	]
 
 	return columns
 
-def get_data(warehouse):
-	serial_item_list = frappe.get_all("Item", filters={
-		'has_serial_no': True,
-	}, fields=['item_code', 'item_name'])
 
-	status_list = ['Active', 'Expired']
+def get_data(warehouse):
+	serial_item_list = frappe.get_all(
+		"Item",
+		filters={
+			"has_serial_no": True,
+		},
+		fields=["item_code", "item_name"],
+	)
+
+	status_list = ["Active", "Expired"]
 	data = []
 	for item in serial_item_list:
-		total_serial_no = frappe.db.count("Serial No",
-			filters={"item_code": item.item_code, "status": ("in", status_list), "warehouse": warehouse})
+		total_serial_no = frappe.db.count(
+			"Serial No",
+			filters={"item_code": item.item_code, "status": ("in", status_list), "warehouse": warehouse},
+		)
 
-		actual_qty = frappe.db.get_value('Bin', fieldname=['actual_qty'],
-			filters={"warehouse": warehouse, "item_code": item.item_code})
+		actual_qty = frappe.db.get_value(
+			"Bin", fieldname=["actual_qty"], filters={"warehouse": warehouse, "item_code": item.item_code}
+		)
 
 		# frappe.db.get_value returns null if no record exist.
 		if not actual_qty:
diff --git a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py
index d1748ed..5430fe6 100644
--- a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py
+++ b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py
@@ -20,11 +20,11 @@
 		if consumed_details.get(item_code):
 			for cd in consumed_details.get(item_code):
 
-				if (cd.voucher_no not in material_transfer_vouchers):
+				if cd.voucher_no not in material_transfer_vouchers:
 					if cd.voucher_type in ["Delivery Note", "Sales Invoice"]:
 						delivered_qty += abs(flt(cd.actual_qty))
 						delivered_amount += abs(flt(cd.stock_value_difference))
-					elif cd.voucher_type!="Delivery Note":
+					elif cd.voucher_type != "Delivery Note":
 						consumed_qty += abs(flt(cd.actual_qty))
 						consumed_amount += abs(flt(cd.stock_value_difference))
 
@@ -32,66 +32,98 @@
 				total_qty += delivered_qty + consumed_qty
 				total_amount += delivered_amount + consumed_amount
 
-				row = [cd.item_code, cd.item_name, cd.description, cd.stock_uom, \
-					consumed_qty, consumed_amount, delivered_qty, delivered_amount, \
-					total_qty, total_amount, ','.join(list(set(suppliers)))]
+				row = [
+					cd.item_code,
+					cd.item_name,
+					cd.description,
+					cd.stock_uom,
+					consumed_qty,
+					consumed_amount,
+					delivered_qty,
+					delivered_amount,
+					total_qty,
+					total_amount,
+					",".join(list(set(suppliers))),
+				]
 				data.append(row)
 
 	return columns, data
 
+
 def get_columns(filters):
 	"""return columns based on filters"""
 
-	columns = [_("Item") + ":Link/Item:100"] + [_("Item Name") + "::100"] + \
-	[_("Description") + "::150"] + [_("UOM") + ":Link/UOM:90"] + \
-	[_("Consumed Qty") + ":Float:110"] + [_("Consumed Amount") + ":Currency:130"] + \
-	[_("Delivered Qty") + ":Float:110"] + [_("Delivered Amount") + ":Currency:130"] + \
-	[_("Total Qty") + ":Float:110"] + [_("Total Amount") + ":Currency:130"] + \
-	[_("Supplier(s)") + "::250"]
+	columns = (
+		[_("Item") + ":Link/Item:100"]
+		+ [_("Item Name") + "::100"]
+		+ [_("Description") + "::150"]
+		+ [_("UOM") + ":Link/UOM:90"]
+		+ [_("Consumed Qty") + ":Float:110"]
+		+ [_("Consumed Amount") + ":Currency:130"]
+		+ [_("Delivered Qty") + ":Float:110"]
+		+ [_("Delivered Amount") + ":Currency:130"]
+		+ [_("Total Qty") + ":Float:110"]
+		+ [_("Total Amount") + ":Currency:130"]
+		+ [_("Supplier(s)") + "::250"]
+	)
 
 	return columns
 
+
 def get_conditions(filters):
 	conditions = ""
 	values = []
 
-	if filters.get('from_date') and filters.get('to_date'):
+	if filters.get("from_date") and filters.get("to_date"):
 		conditions = "and sle.posting_date>=%s and sle.posting_date<=%s"
-		values = [filters.get('from_date'), filters.get('to_date')]
+		values = [filters.get("from_date"), filters.get("to_date")]
 
 	return conditions, values
 
+
 def get_consumed_details(filters):
 	conditions, values = get_conditions(filters)
 	consumed_details = {}
 
-	for d in frappe.db.sql("""select sle.item_code, i.item_name, i.description,
+	for d in frappe.db.sql(
+		"""select sle.item_code, i.item_name, i.description,
 		i.stock_uom, sle.actual_qty, sle.stock_value_difference,
 		sle.voucher_no, sle.voucher_type
 		from `tabStock Ledger Entry` sle, `tabItem` i
-		where sle.is_cancelled = 0 and sle.item_code=i.name and sle.actual_qty < 0 %s""" % conditions, values, as_dict=1):
-			consumed_details.setdefault(d.item_code, []).append(d)
+		where sle.is_cancelled = 0 and sle.item_code=i.name and sle.actual_qty < 0 %s"""
+		% conditions,
+		values,
+		as_dict=1,
+	):
+		consumed_details.setdefault(d.item_code, []).append(d)
 
 	return consumed_details
 
+
 def get_suppliers_details(filters):
 	item_supplier_map = {}
-	supplier = filters.get('supplier')
+	supplier = filters.get("supplier")
 
-	for d in frappe.db.sql("""select pr.supplier, pri.item_code from
+	for d in frappe.db.sql(
+		"""select pr.supplier, pri.item_code from
 		`tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri
 		where pr.name=pri.parent and pr.docstatus=1 and
 		pri.item_code=(select name from `tabItem` where
-			is_stock_item=1 and name=pri.item_code)""", as_dict=1):
-			item_supplier_map.setdefault(d.item_code, []).append(d.supplier)
+			is_stock_item=1 and name=pri.item_code)""",
+		as_dict=1,
+	):
+		item_supplier_map.setdefault(d.item_code, []).append(d.supplier)
 
-	for d in frappe.db.sql("""select pr.supplier, pri.item_code from
+	for d in frappe.db.sql(
+		"""select pr.supplier, pri.item_code from
 		`tabPurchase Invoice` pr, `tabPurchase Invoice Item` pri
 		where pr.name=pri.parent and pr.docstatus=1 and
 		ifnull(pr.update_stock, 0) = 1 and pri.item_code=(select name from `tabItem`
-			where is_stock_item=1 and name=pri.item_code)""", as_dict=1):
-			if d.item_code not in item_supplier_map:
-				item_supplier_map.setdefault(d.item_code, []).append(d.supplier)
+			where is_stock_item=1 and name=pri.item_code)""",
+		as_dict=1,
+	):
+		if d.item_code not in item_supplier_map:
+			item_supplier_map.setdefault(d.item_code, []).append(d.supplier)
 
 	if supplier:
 		invalid_items = []
@@ -104,6 +136,9 @@
 
 	return item_supplier_map
 
+
 def get_material_transfer_vouchers():
-	return frappe.db.sql_list("""select name from `tabStock Entry` where
-		purpose='Material Transfer' and docstatus=1""")
+	return frappe.db.sql_list(
+		"""select name from `tabStock Entry` where
+		purpose='Material Transfer' and docstatus=1"""
+	)
diff --git a/erpnext/stock/report/test_reports.py b/erpnext/stock/report/test_reports.py
index 76c2079..d118d8e 100644
--- a/erpnext/stock/report/test_reports.py
+++ b/erpnext/stock/report/test_reports.py
@@ -43,8 +43,18 @@
 		},
 	),
 	("Warehouse wise Item Balance Age and Value", {"_optional": True}),
-	("Item Variant Details", {"item": "_Test Variant Item",}),
-	("Total Stock Summary", {"group_by": "warehouse",}),
+	(
+		"Item Variant Details",
+		{
+			"item": "_Test Variant Item",
+		},
+	),
+	(
+		"Total Stock Summary",
+		{
+			"group_by": "warehouse",
+		},
+	),
 	("Batch Item Expiry Status", {}),
 	("Incorrect Stock Value Report", {"company": "_Test Company with perpetual inventory"}),
 	("Incorrect Serial No Valuation", {}),
@@ -54,12 +64,9 @@
 	("Delayed Item Report", {"based_on": "Sales Invoice"}),
 	("Delayed Item Report", {"based_on": "Delivery Note"}),
 	("Stock Ageing", {"range1": 30, "range2": 60, "range3": 90, "_optional": True}),
-	("Stock Ledger Invariant Check",
-		{
-			"warehouse": "_Test Warehouse - _TC",
-			"item": "_Test Item"
-		}
-	),
+	("Stock Ledger Invariant Check", {"warehouse": "_Test Warehouse - _TC", "item": "_Test Item"}),
+	("FIFO Queue vs Qty After Transaction Comparison", {"warehouse": "_Test Warehouse - _TC"}),
+	("FIFO Queue vs Qty After Transaction Comparison", {"item_group": "All Item Groups"}),
 ]
 
 OPTIONAL_FILTERS = {
diff --git a/erpnext/stock/report/total_stock_summary/total_stock_summary.py b/erpnext/stock/report/total_stock_summary/total_stock_summary.py
index 6f27558..21529da 100644
--- a/erpnext/stock/report/total_stock_summary/total_stock_summary.py
+++ b/erpnext/stock/report/total_stock_summary/total_stock_summary.py
@@ -15,6 +15,7 @@
 
 	return columns, stock
 
+
 def get_columns():
 	columns = [
 		_("Company") + ":Link/Company:250",
@@ -26,13 +27,16 @@
 
 	return columns
 
+
 def get_total_stock(filters):
 	conditions = ""
 	columns = ""
 
 	if filters.get("group_by") == "Warehouse":
 		if filters.get("company"):
-			conditions += " AND warehouse.company = %s" % frappe.db.escape(filters.get("company"), percent=False)
+			conditions += " AND warehouse.company = %s" % frappe.db.escape(
+				filters.get("company"), percent=False
+			)
 
 		conditions += " GROUP BY ledger.warehouse, item.item_code"
 		columns += "'' as company, ledger.warehouse"
@@ -40,7 +44,8 @@
 		conditions += " GROUP BY warehouse.company, item.item_code"
 		columns += " warehouse.company, '' as warehouse"
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 			SELECT
 				%s,
 				item.item_code,
@@ -53,4 +58,6 @@
 			INNER JOIN `tabWarehouse` warehouse
 				ON warehouse.name = ledger.warehouse
 			WHERE
-				ledger.actual_qty != 0 %s""" % (columns, conditions))
+				ledger.actual_qty != 0 %s"""
+		% (columns, conditions)
+	)
diff --git a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py
index 22bdb89..a54373f 100644
--- a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py
+++ b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py
@@ -21,7 +21,8 @@
 
 def execute(filters=None):
 	is_reposting_item_valuation_in_progress()
-	if not filters: filters = {}
+	if not filters:
+		filters = {}
 
 	validate_filters(filters)
 
@@ -39,7 +40,8 @@
 	item_value = {}
 
 	for (company, item, warehouse) in sorted(iwb_map):
-		if not item_map.get(item):  continue
+		if not item_map.get(item):
+			continue
 
 		row = []
 		qty_dict = iwb_map[(company, item, warehouse)]
@@ -50,13 +52,13 @@
 			total_stock_value += qty_dict.bal_val if wh.name == warehouse else 0.00
 
 		item_balance[(item, item_map[item]["item_group"])].append(row)
-		item_value.setdefault((item, item_map[item]["item_group"]),[])
+		item_value.setdefault((item, item_map[item]["item_group"]), [])
 		item_value[(item, item_map[item]["item_group"])].append(total_stock_value)
 
-
 	# sum bal_qty by item
 	for (item, item_group), wh_balance in item_balance.items():
-		if not item_ageing.get(item):  continue
+		if not item_ageing.get(item):
+			continue
 
 		total_stock_value = sum(item_value[(item, item_group)])
 		row = [item, item_group, total_stock_value]
@@ -81,17 +83,19 @@
 	add_warehouse_column(columns, warehouse_list)
 	return columns, data
 
+
 def get_columns(filters):
 	"""return columns"""
 
 	columns = [
-		_("Item")+":Link/Item:180",
-		_("Item Group")+"::100",
-		_("Value")+":Currency:100",
-		_("Age")+":Float:60",
+		_("Item") + ":Link/Item:180",
+		_("Item Group") + "::100",
+		_("Value") + ":Currency:100",
+		_("Age") + ":Float:60",
 	]
 	return columns
 
+
 def validate_filters(filters):
 	if not (filters.get("item_code") or filters.get("warehouse")):
 		sle_count = flt(frappe.db.sql("""select count(name) from `tabStock Ledger Entry`""")[0][0])
@@ -100,11 +104,12 @@
 	if not filters.get("company"):
 		filters["company"] = frappe.defaults.get_user_default("Company")
 
+
 def get_warehouse_list(filters):
 	from frappe.core.doctype.user_permission.user_permission import get_permitted_documents
 
-	condition = ''
-	user_permitted_warehouse = get_permitted_documents('Warehouse')
+	condition = ""
+	user_permitted_warehouse = get_permitted_documents("Warehouse")
 	value = ()
 	if user_permitted_warehouse:
 		condition = "and name in %s"
@@ -113,13 +118,20 @@
 		condition = "and name = %s"
 		value = filters.get("warehouse")
 
-	return frappe.db.sql("""select name
+	return frappe.db.sql(
+		"""select name
 		from `tabWarehouse` where is_group = 0
-		{condition}""".format(condition=condition), value, as_dict=1)
+		{condition}""".format(
+			condition=condition
+		),
+		value,
+		as_dict=1,
+	)
+
 
 def add_warehouse_column(columns, warehouse_list):
 	if len(warehouse_list) > 1:
-		columns += [_("Total Qty")+":Int:50"]
+		columns += [_("Total Qty") + ":Int:50"]
 
 	for wh in warehouse_list:
-		columns += [_(wh.name)+":Int:54"]
+		columns += [_(wh.name) + ":Int:54"]
diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py
index 62017e4..14cedd2 100644
--- a/erpnext/stock/stock_balance.py
+++ b/erpnext/stock/stock_balance.py
@@ -15,16 +15,20 @@
 	frappe.db.auto_commit_on_many_writes = 1
 
 	if allow_negative_stock:
-		existing_allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
+		existing_allow_negative_stock = frappe.db.get_value(
+			"Stock Settings", None, "allow_negative_stock"
+		)
 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
 
-	item_warehouses = frappe.db.sql("""
+	item_warehouses = frappe.db.sql(
+		"""
 		select distinct item_code, warehouse
 		from
 			(select item_code, warehouse from tabBin
 			union
 			select item_code, warehouse from `tabStock Ledger Entry`) a
-	""")
+	"""
+	)
 	for d in item_warehouses:
 		try:
 			repost_stock(d[0], d[1], allow_zero_rate, only_actual, only_bin, allow_negative_stock)
@@ -33,11 +37,20 @@
 			frappe.db.rollback()
 
 	if allow_negative_stock:
-		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock)
+		frappe.db.set_value(
+			"Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock
+		)
 	frappe.db.auto_commit_on_many_writes = 0
 
-def repost_stock(item_code, warehouse, allow_zero_rate=False,
-	only_actual=False, only_bin=False, allow_negative_stock=False):
+
+def repost_stock(
+	item_code,
+	warehouse,
+	allow_zero_rate=False,
+	only_actual=False,
+	only_bin=False,
+	allow_negative_stock=False,
+):
 
 	if not only_bin:
 		repost_actual_qty(item_code, warehouse, allow_zero_rate, allow_negative_stock)
@@ -47,35 +60,42 @@
 			"reserved_qty": get_reserved_qty(item_code, warehouse),
 			"indented_qty": get_indented_qty(item_code, warehouse),
 			"ordered_qty": get_ordered_qty(item_code, warehouse),
-			"planned_qty": get_planned_qty(item_code, warehouse)
+			"planned_qty": get_planned_qty(item_code, warehouse),
 		}
 		if only_bin:
-			qty_dict.update({
-				"actual_qty": get_balance_qty_from_sle(item_code, warehouse)
-			})
+			qty_dict.update({"actual_qty": get_balance_qty_from_sle(item_code, warehouse)})
 
 		update_bin_qty(item_code, warehouse, qty_dict)
 
+
 def repost_actual_qty(item_code, warehouse, allow_zero_rate=False, allow_negative_stock=False):
-	create_repost_item_valuation_entry({
-		"item_code": item_code,
-		"warehouse": warehouse,
-		"posting_date": "1900-01-01",
-		"posting_time": "00:01",
-		"allow_negative_stock": allow_negative_stock,
-		"allow_zero_rate": allow_zero_rate
-	})
+	create_repost_item_valuation_entry(
+		{
+			"item_code": item_code,
+			"warehouse": warehouse,
+			"posting_date": "1900-01-01",
+			"posting_time": "00:01",
+			"allow_negative_stock": allow_negative_stock,
+			"allow_zero_rate": allow_zero_rate,
+		}
+	)
+
 
 def get_balance_qty_from_sle(item_code, warehouse):
-	balance_qty = frappe.db.sql("""select qty_after_transaction from `tabStock Ledger Entry`
+	balance_qty = frappe.db.sql(
+		"""select qty_after_transaction from `tabStock Ledger Entry`
 		where item_code=%s and warehouse=%s and is_cancelled=0
 		order by posting_date desc, posting_time desc, creation desc
-		limit 1""", (item_code, warehouse))
+		limit 1""",
+		(item_code, warehouse),
+	)
 
 	return flt(balance_qty[0][0]) if balance_qty else 0.0
 
+
 def get_reserved_qty(item_code, warehouse):
-	reserved_qty = frappe.db.sql("""
+	reserved_qty = frappe.db.sql(
+		"""
 		select
 			sum(dnpi_qty * ((so_item_qty - so_item_delivered_qty) / so_item_qty))
 		from
@@ -98,7 +118,7 @@
 					select qty, parent_detail_docname, parent, name
 					from `tabPacked Item` dnpi_in
 					where item_code = %s and warehouse = %s
-					and parenttype="Sales Order"
+					and parenttype='Sales Order'
 					and item_code != parent_item
 					and exists (select * from `tabSales Order` so
 					where name = dnpi_in.parent and docstatus = 1 and status != 'Closed')
@@ -115,58 +135,76 @@
 			) tab
 		where
 			so_item_qty >= so_item_delivered_qty
-	""", (item_code, warehouse, item_code, warehouse))
+	""",
+		(item_code, warehouse, item_code, warehouse),
+	)
 
 	return flt(reserved_qty[0][0]) if reserved_qty else 0
 
+
 def get_indented_qty(item_code, warehouse):
 	# Ordered Qty is always maintained in stock UOM
-	inward_qty = frappe.db.sql("""
+	inward_qty = frappe.db.sql(
+		"""
 		select sum(mr_item.stock_qty - mr_item.ordered_qty)
 		from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr
 		where mr_item.item_code=%s and mr_item.warehouse=%s
 			and mr.material_request_type in ('Purchase', 'Manufacture', 'Customer Provided', 'Material Transfer')
 			and mr_item.stock_qty > mr_item.ordered_qty and mr_item.parent=mr.name
 			and mr.status!='Stopped' and mr.docstatus=1
-	""", (item_code, warehouse))
+	""",
+		(item_code, warehouse),
+	)
 	inward_qty = flt(inward_qty[0][0]) if inward_qty else 0
 
-	outward_qty = frappe.db.sql("""
+	outward_qty = frappe.db.sql(
+		"""
 		select sum(mr_item.stock_qty - mr_item.ordered_qty)
 		from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr
 		where mr_item.item_code=%s and mr_item.warehouse=%s
 			and mr.material_request_type = 'Material Issue'
 			and mr_item.stock_qty > mr_item.ordered_qty and mr_item.parent=mr.name
 			and mr.status!='Stopped' and mr.docstatus=1
-	""", (item_code, warehouse))
+	""",
+		(item_code, warehouse),
+	)
 	outward_qty = flt(outward_qty[0][0]) if outward_qty else 0
 
 	requested_qty = inward_qty - outward_qty
 
 	return requested_qty
 
+
 def get_ordered_qty(item_code, warehouse):
-	ordered_qty = frappe.db.sql("""
+	ordered_qty = frappe.db.sql(
+		"""
 		select sum((po_item.qty - po_item.received_qty)*po_item.conversion_factor)
 		from `tabPurchase Order Item` po_item, `tabPurchase Order` po
 		where po_item.item_code=%s and po_item.warehouse=%s
 		and po_item.qty > po_item.received_qty and po_item.parent=po.name
 		and po.status not in ('Closed', 'Delivered') and po.docstatus=1
-		and po_item.delivered_by_supplier = 0""", (item_code, warehouse))
+		and po_item.delivered_by_supplier = 0""",
+		(item_code, warehouse),
+	)
 
 	return flt(ordered_qty[0][0]) if ordered_qty else 0
 
+
 def get_planned_qty(item_code, warehouse):
-	planned_qty = frappe.db.sql("""
+	planned_qty = frappe.db.sql(
+		"""
 		select sum(qty - produced_qty) from `tabWork Order`
-		where production_item = %s and fg_warehouse = %s and status not in ("Stopped", "Completed", "Closed")
-		and docstatus=1 and qty > produced_qty""", (item_code, warehouse))
+		where production_item = %s and fg_warehouse = %s and status not in ('Stopped', 'Completed', 'Closed')
+		and docstatus=1 and qty > produced_qty""",
+		(item_code, warehouse),
+	)
 
 	return flt(planned_qty[0][0]) if planned_qty else 0
 
 
 def update_bin_qty(item_code, warehouse, qty_dict=None):
 	from erpnext.stock.utils import get_bin
+
 	bin = get_bin(item_code, warehouse)
 	mismatch = False
 	for field, value in qty_dict.items():
@@ -180,41 +218,54 @@
 		bin.db_update()
 		bin.clear_cache()
 
-def set_stock_balance_as_per_serial_no(item_code=None, posting_date=None, posting_time=None,
-	 	fiscal_year=None):
-	if not posting_date: posting_date = nowdate()
-	if not posting_time: posting_time = nowtime()
 
-	condition = " and item.name='%s'" % item_code.replace("'", "\'") if item_code else ""
+def set_stock_balance_as_per_serial_no(
+	item_code=None, posting_date=None, posting_time=None, fiscal_year=None
+):
+	if not posting_date:
+		posting_date = nowdate()
+	if not posting_time:
+		posting_time = nowtime()
 
-	bin = frappe.db.sql("""select bin.item_code, bin.warehouse, bin.actual_qty, item.stock_uom
+	condition = " and item.name='%s'" % item_code.replace("'", "'") if item_code else ""
+
+	bin = frappe.db.sql(
+		"""select bin.item_code, bin.warehouse, bin.actual_qty, item.stock_uom
 		from `tabBin` bin, tabItem item
-		where bin.item_code = item.name and item.has_serial_no = 1 %s""" % condition)
+		where bin.item_code = item.name and item.has_serial_no = 1 %s"""
+		% condition
+	)
 
 	for d in bin:
-		serial_nos = frappe.db.sql("""select count(name) from `tabSerial No`
-			where item_code=%s and warehouse=%s and docstatus < 2""", (d[0], d[1]))
+		serial_nos = frappe.db.sql(
+			"""select count(name) from `tabSerial No`
+			where item_code=%s and warehouse=%s and docstatus < 2""",
+			(d[0], d[1]),
+		)
 
-		sle = frappe.db.sql("""select valuation_rate, company from `tabStock Ledger Entry`
+		sle = frappe.db.sql(
+			"""select valuation_rate, company from `tabStock Ledger Entry`
 			where item_code = %s and warehouse = %s and is_cancelled = 0
-			order by posting_date desc limit 1""", (d[0], d[1]))
+			order by posting_date desc limit 1""",
+			(d[0], d[1]),
+		)
 
 		sle_dict = {
-			'doctype'					: 'Stock Ledger Entry',
-			'item_code'					: d[0],
-			'warehouse'					: d[1],
-			'transaction_date'	 		: nowdate(),
-			'posting_date'				: posting_date,
-			'posting_time'			 	: posting_time,
-			'voucher_type'			 	: 'Stock Reconciliation (Manual)',
-			'voucher_no'				: '',
-			'voucher_detail_no'			: '',
-			'actual_qty'				: flt(serial_nos[0][0]) - flt(d[2]),
-			'stock_uom'					: d[3],
-			'incoming_rate'				: sle and flt(serial_nos[0][0]) > flt(d[2]) and flt(sle[0][0]) or 0,
-			'company'					: sle and cstr(sle[0][1]) or 0,
-			'batch_no'					: '',
-			'serial_no'					: ''
+			"doctype": "Stock Ledger Entry",
+			"item_code": d[0],
+			"warehouse": d[1],
+			"transaction_date": nowdate(),
+			"posting_date": posting_date,
+			"posting_time": posting_time,
+			"voucher_type": "Stock Reconciliation (Manual)",
+			"voucher_no": "",
+			"voucher_detail_no": "",
+			"actual_qty": flt(serial_nos[0][0]) - flt(d[2]),
+			"stock_uom": d[3],
+			"incoming_rate": sle and flt(serial_nos[0][0]) > flt(d[2]) and flt(sle[0][0]) or 0,
+			"company": sle and cstr(sle[0][1]) or 0,
+			"batch_no": "",
+			"serial_no": "",
 		}
 
 		sle_doc = frappe.get_doc(sle_dict)
@@ -223,16 +274,17 @@
 		sle_doc.insert()
 
 		args = sle_dict.copy()
-		args.update({
-			"sle_id": sle_doc.name
-		})
+		args.update({"sle_id": sle_doc.name})
 
-		create_repost_item_valuation_entry({
-			"item_code": d[0],
-			"warehouse": d[1],
-			"posting_date": posting_date,
-			"posting_time": posting_time
-		})
+		create_repost_item_valuation_entry(
+			{
+				"item_code": d[0],
+				"warehouse": d[1],
+				"posting_date": posting_date,
+				"posting_time": posting_time,
+			}
+		)
+
 
 def reset_serial_no_status_and_warehouse(serial_nos=None):
 	if not serial_nos:
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 6975552..ba2d3c1 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -1,16 +1,15 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 
 import copy
 import json
-from typing import Optional
+from typing import Optional, Set, Tuple
 
 import frappe
 from frappe import _
 from frappe.model.meta import get_field_precision
-from frappe.query_builder.functions import Sum
+from frappe.query_builder.functions import CombineDatetime, Sum
 from frappe.utils import cint, cstr, flt, get_link_to_form, getdate, now, nowdate
-from pypika import CustomFunction
 
 import erpnext
 from erpnext.stock.doctype.bin.bin import update_qty as update_bin_qty
@@ -22,28 +21,32 @@
 from erpnext.stock.valuation import FIFOValuation, LIFOValuation, round_off_if_near_zero
 
 
-class NegativeStockError(frappe.ValidationError): pass
+class NegativeStockError(frappe.ValidationError):
+	pass
+
+
 class SerialNoExistsInFutureTransaction(frappe.ValidationError):
 	pass
 
 
 def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_voucher=False):
-	""" Create SL entries from SL entry dicts
+	"""Create SL entries from SL entry dicts
 
-		args:
-			- allow_negative_stock: disable negative stock valiations if true
-			- via_landed_cost_voucher: landed cost voucher cancels and reposts
-			entries of purchase document. This flag is used to identify if
-			cancellation and repost is happening via landed cost voucher, in
-			such cases certain validations need to be ignored (like negative
-					stock)
+	args:
+	        - allow_negative_stock: disable negative stock valiations if true
+	        - via_landed_cost_voucher: landed cost voucher cancels and reposts
+	        entries of purchase document. This flag is used to identify if
+	        cancellation and repost is happening via landed cost voucher, in
+	        such cases certain validations need to be ignored (like negative
+	                        stock)
 	"""
 	from erpnext.controllers.stock_controller import future_sle_exists
+
 	if sl_entries:
 		cancel = sl_entries[0].get("is_cancelled")
 		if cancel:
 			validate_cancellation(sl_entries)
-			set_as_cancel(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
+			set_as_cancel(sl_entries[0].get("voucher_type"), sl_entries[0].get("voucher_no"))
 
 		args = get_args_for_future_sle(sl_entries[0])
 		future_sle_exists(args, sl_entries)
@@ -53,19 +56,21 @@
 				validate_serial_no(sle)
 
 			if cancel:
-				sle['actual_qty'] = -flt(sle.get('actual_qty'))
+				sle["actual_qty"] = -flt(sle.get("actual_qty"))
 
-				if sle['actual_qty'] < 0 and not sle.get('outgoing_rate'):
-					sle['outgoing_rate'] = get_incoming_outgoing_rate_for_cancel(sle.item_code,
-						sle.voucher_type, sle.voucher_no, sle.voucher_detail_no)
-					sle['incoming_rate'] = 0.0
+				if sle["actual_qty"] < 0 and not sle.get("outgoing_rate"):
+					sle["outgoing_rate"] = get_incoming_outgoing_rate_for_cancel(
+						sle.item_code, sle.voucher_type, sle.voucher_no, sle.voucher_detail_no
+					)
+					sle["incoming_rate"] = 0.0
 
-				if sle['actual_qty'] > 0 and not sle.get('incoming_rate'):
-					sle['incoming_rate'] = get_incoming_outgoing_rate_for_cancel(sle.item_code,
-						sle.voucher_type, sle.voucher_no, sle.voucher_detail_no)
-					sle['outgoing_rate'] = 0.0
+				if sle["actual_qty"] > 0 and not sle.get("incoming_rate"):
+					sle["incoming_rate"] = get_incoming_outgoing_rate_for_cancel(
+						sle.item_code, sle.voucher_type, sle.voucher_no, sle.voucher_detail_no
+					)
+					sle["outgoing_rate"] = 0.0
 
-			if sle.get("actual_qty") or sle.get("voucher_type")=="Stock Reconciliation":
+			if sle.get("actual_qty") or sle.get("voucher_type") == "Stock Reconciliation":
 				sle_doc = make_entry(sle, allow_negative_stock, via_landed_cost_voucher)
 
 			args = sle_doc.as_dict()
@@ -74,13 +79,16 @@
 				# preserve previous_qty_after_transaction for qty reposting
 				args.previous_qty_after_transaction = sle.get("previous_qty_after_transaction")
 
-			is_stock_item = frappe.get_cached_value('Item', args.get("item_code"), 'is_stock_item')
+			is_stock_item = frappe.get_cached_value("Item", args.get("item_code"), "is_stock_item")
 			if is_stock_item:
 				bin_name = get_or_make_bin(args.get("item_code"), args.get("warehouse"))
 				repost_current_voucher(args, allow_negative_stock, via_landed_cost_voucher)
 				update_bin_qty(bin_name, args)
 			else:
-				frappe.msgprint(_("Item {0} ignored since it is not a stock item").format(args.get("item_code")))
+				frappe.msgprint(
+					_("Item {0} ignored since it is not a stock item").format(args.get("item_code"))
+				)
+
 
 def repost_current_voucher(args, allow_negative_stock=False, via_landed_cost_voucher=False):
 	if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
@@ -92,28 +100,35 @@
 
 		# Reposts only current voucher SL Entries
 		# Updates valuation rate, stock value, stock queue for current transaction
-		update_entries_after({
-			"item_code": args.get('item_code'),
-			"warehouse": args.get('warehouse'),
-			"posting_date": args.get("posting_date"),
-			"posting_time": args.get("posting_time"),
-			"voucher_type": args.get("voucher_type"),
-			"voucher_no": args.get("voucher_no"),
-			"sle_id": args.get('name'),
-			"creation": args.get('creation')
-		}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
+		update_entries_after(
+			{
+				"item_code": args.get("item_code"),
+				"warehouse": args.get("warehouse"),
+				"posting_date": args.get("posting_date"),
+				"posting_time": args.get("posting_time"),
+				"voucher_type": args.get("voucher_type"),
+				"voucher_no": args.get("voucher_no"),
+				"sle_id": args.get("name"),
+				"creation": args.get("creation"),
+			},
+			allow_negative_stock=allow_negative_stock,
+			via_landed_cost_voucher=via_landed_cost_voucher,
+		)
 
 		# update qty in future sle and Validate negative qty
 		update_qty_in_future_sle(args, allow_negative_stock)
 
 
 def get_args_for_future_sle(row):
-	return frappe._dict({
-		'voucher_type': row.get('voucher_type'),
-		'voucher_no': row.get('voucher_no'),
-		'posting_date': row.get('posting_date'),
-		'posting_time': row.get('posting_time')
-	})
+	return frappe._dict(
+		{
+			"voucher_type": row.get("voucher_type"),
+			"voucher_no": row.get("voucher_no"),
+			"posting_date": row.get("posting_date"),
+			"posting_time": row.get("posting_time"),
+		}
+	)
+
 
 def validate_serial_no(sle):
 	from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@@ -121,81 +136,112 @@
 	for sn in get_serial_nos(sle.serial_no):
 		args = copy.deepcopy(sle)
 		args.serial_no = sn
-		args.warehouse = ''
+		args.warehouse = ""
 
 		vouchers = []
-		for row in get_stock_ledger_entries(args, '>'):
+		for row in get_stock_ledger_entries(args, ">"):
 			voucher_type = frappe.bold(row.voucher_type)
 			voucher_no = frappe.bold(get_link_to_form(row.voucher_type, row.voucher_no))
-			vouchers.append(f'{voucher_type} {voucher_no}')
+			vouchers.append(f"{voucher_type} {voucher_no}")
 
 		if vouchers:
 			serial_no = frappe.bold(sn)
-			msg = (f'''The serial no {serial_no} has been used in the future transactions so you need to cancel them first.
-				The list of the transactions are as below.''' + '<br><br><ul><li>')
+			msg = (
+				f"""The serial no {serial_no} has been used in the future transactions so you need to cancel them first.
+				The list of the transactions are as below."""
+				+ "<br><br><ul><li>"
+			)
 
-			msg += '</li><li>'.join(vouchers)
-			msg += '</li></ul>'
+			msg += "</li><li>".join(vouchers)
+			msg += "</li></ul>"
 
-			title = 'Cannot Submit' if not sle.get('is_cancelled') else 'Cannot Cancel'
+			title = "Cannot Submit" if not sle.get("is_cancelled") else "Cannot Cancel"
 			frappe.throw(_(msg), title=_(title), exc=SerialNoExistsInFutureTransaction)
 
+
 def validate_cancellation(args):
 	if args[0].get("is_cancelled"):
-		repost_entry = frappe.db.get_value("Repost Item Valuation", {
-			'voucher_type': args[0].voucher_type,
-			'voucher_no': args[0].voucher_no,
-			'docstatus': 1
-		}, ['name', 'status'], as_dict=1)
+		repost_entry = frappe.db.get_value(
+			"Repost Item Valuation",
+			{"voucher_type": args[0].voucher_type, "voucher_no": args[0].voucher_no, "docstatus": 1},
+			["name", "status"],
+			as_dict=1,
+		)
 
 		if repost_entry:
-			if repost_entry.status == 'In Progress':
-				frappe.throw(_("Cannot cancel the transaction. Reposting of item valuation on submission is not completed yet."))
-			if repost_entry.status == 'Queued':
+			if repost_entry.status == "In Progress":
+				frappe.throw(
+					_(
+						"Cannot cancel the transaction. Reposting of item valuation on submission is not completed yet."
+					)
+				)
+			if repost_entry.status == "Queued":
 				doc = frappe.get_doc("Repost Item Valuation", repost_entry.name)
+				doc.status = "Skipped"
 				doc.flags.ignore_permissions = True
 				doc.cancel()
-				doc.delete()
+
 
 def set_as_cancel(voucher_type, voucher_no):
-	frappe.db.sql("""update `tabStock Ledger Entry` set is_cancelled=1,
+	frappe.db.sql(
+		"""update `tabStock Ledger Entry` set is_cancelled=1,
 		modified=%s, modified_by=%s
 		where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
-		(now(), frappe.session.user, voucher_type, voucher_no))
+		(now(), frappe.session.user, voucher_type, voucher_no),
+	)
+
 
 def make_entry(args, allow_negative_stock=False, via_landed_cost_voucher=False):
 	args["doctype"] = "Stock Ledger Entry"
 	sle = frappe.get_doc(args)
 	sle.flags.ignore_permissions = 1
-	sle.allow_negative_stock=allow_negative_stock
+	sle.allow_negative_stock = allow_negative_stock
 	sle.via_landed_cost_voucher = via_landed_cost_voucher
 	sle.submit()
 	return sle
 
-def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negative_stock=None, via_landed_cost_voucher=False, doc=None):
+
+def repost_future_sle(
+	args=None,
+	voucher_type=None,
+	voucher_no=None,
+	allow_negative_stock=None,
+	via_landed_cost_voucher=False,
+	doc=None,
+):
 	if not args and voucher_type and voucher_no:
 		args = get_items_to_be_repost(voucher_type, voucher_no, doc)
 
 	distinct_item_warehouses = get_distinct_item_warehouse(args, doc)
+	affected_transactions = get_affected_transactions(doc)
 
 	i = get_current_index(doc) or 0
 	while i < len(args):
 		validate_item_warehouse(args[i])
 
-		obj = update_entries_after({
-			'item_code': args[i].get('item_code'),
-			'warehouse': args[i].get('warehouse'),
-			'posting_date': args[i].get('posting_date'),
-			'posting_time': args[i].get('posting_time'),
-			'creation': args[i].get('creation'),
-			'distinct_item_warehouses': distinct_item_warehouses
-		}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
+		obj = update_entries_after(
+			{
+				"item_code": args[i].get("item_code"),
+				"warehouse": args[i].get("warehouse"),
+				"posting_date": args[i].get("posting_date"),
+				"posting_time": args[i].get("posting_time"),
+				"creation": args[i].get("creation"),
+				"distinct_item_warehouses": distinct_item_warehouses,
+			},
+			allow_negative_stock=allow_negative_stock,
+			via_landed_cost_voucher=via_landed_cost_voucher,
+		)
+		affected_transactions.update(obj.affected_transactions)
 
-		distinct_item_warehouses[(args[i].get('item_code'), args[i].get('warehouse'))].reposting_status = True
+		distinct_item_warehouses[
+			(args[i].get("item_code"), args[i].get("warehouse"))
+		].reposting_status = True
 
 		if obj.new_items_found:
 			for item_wh, data in distinct_item_warehouses.items():
-				if ('args_idx' not in data and not data.reposting_status) or (data.sle_changed and data.reposting_status):
+				if ("args_idx" not in data and not data.reposting_status) or (
+					data.sle_changed and data.reposting_status
+				):
 					data.args_idx = len(args)
 					args.append(data.sle)
 				elif data.sle_changed and not data.reposting_status:
@@ -205,122 +251,160 @@
 		i += 1
 
 		if doc and i % 2 == 0:
-			update_args_in_repost_item_valuation(doc, i, args, distinct_item_warehouses)
+			update_args_in_repost_item_valuation(
+				doc, i, args, distinct_item_warehouses, affected_transactions
+			)
 
 	if doc and args:
-		update_args_in_repost_item_valuation(doc, i, args, distinct_item_warehouses)
+		update_args_in_repost_item_valuation(
+			doc, i, args, distinct_item_warehouses, affected_transactions
+		)
+
 
 def validate_item_warehouse(args):
-	for field in ['item_code', 'warehouse', 'posting_date', 'posting_time']:
+	for field in ["item_code", "warehouse", "posting_date", "posting_time"]:
 		if not args.get(field):
-			validation_msg = f'The field {frappe.unscrub(args.get(field))} is required for the reposting'
+			validation_msg = f"The field {frappe.unscrub(args.get(field))} is required for the reposting"
 			frappe.throw(_(validation_msg))
 
-def update_args_in_repost_item_valuation(doc, index, args, distinct_item_warehouses):
-	frappe.db.set_value(doc.doctype, doc.name, {
-		'items_to_be_repost': json.dumps(args, default=str),
-		'distinct_item_and_warehouse': json.dumps({str(k): v for k,v in distinct_item_warehouses.items()}, default=str),
-		'current_index': index
-	})
 
-	frappe.db.commit()
+def update_args_in_repost_item_valuation(
+	doc, index, args, distinct_item_warehouses, affected_transactions
+):
+	doc.db_set(
+		{
+			"items_to_be_repost": json.dumps(args, default=str),
+			"distinct_item_and_warehouse": json.dumps(
+				{str(k): v for k, v in distinct_item_warehouses.items()}, default=str
+			),
+			"current_index": index,
+			"affected_transactions": frappe.as_json(affected_transactions),
+		}
+	)
 
-	frappe.publish_realtime('item_reposting_progress', {
-		'name': doc.name,
-		'items_to_be_repost': json.dumps(args, default=str),
-		'current_index': index
-	})
+	if not frappe.flags.in_test:
+		frappe.db.commit()
+
+	frappe.publish_realtime(
+		"item_reposting_progress",
+		{"name": doc.name, "items_to_be_repost": json.dumps(args, default=str), "current_index": index},
+	)
+
 
 def get_items_to_be_repost(voucher_type, voucher_no, doc=None):
 	if doc and doc.items_to_be_repost:
 		return json.loads(doc.items_to_be_repost) or []
 
-	return frappe.db.get_all("Stock Ledger Entry",
+	return frappe.db.get_all(
+		"Stock Ledger Entry",
 		filters={"voucher_type": voucher_type, "voucher_no": voucher_no},
 		fields=["item_code", "warehouse", "posting_date", "posting_time", "creation"],
 		order_by="creation asc",
-		group_by="item_code, warehouse"
+		group_by="item_code, warehouse",
 	)
 
+
 def get_distinct_item_warehouse(args=None, doc=None):
 	distinct_item_warehouses = {}
 	if doc and doc.distinct_item_and_warehouse:
 		distinct_item_warehouses = json.loads(doc.distinct_item_and_warehouse)
-		distinct_item_warehouses = {frappe.safe_eval(k): frappe._dict(v) for k, v in distinct_item_warehouses.items()}
+		distinct_item_warehouses = {
+			frappe.safe_eval(k): frappe._dict(v) for k, v in distinct_item_warehouses.items()
+		}
 	else:
 		for i, d in enumerate(args):
-			distinct_item_warehouses.setdefault((d.item_code, d.warehouse), frappe._dict({
-				"reposting_status": False,
-				"sle": d,
-				"args_idx": i
-			}))
+			distinct_item_warehouses.setdefault(
+				(d.item_code, d.warehouse), frappe._dict({"reposting_status": False, "sle": d, "args_idx": i})
+			)
 
 	return distinct_item_warehouses
 
+
+def get_affected_transactions(doc) -> Set[Tuple[str, str]]:
+	if not doc.affected_transactions:
+		return set()
+
+	transactions = frappe.parse_json(doc.affected_transactions)
+	return {tuple(transaction) for transaction in transactions}
+
+
 def get_current_index(doc=None):
 	if doc and doc.current_index:
 		return doc.current_index
 
+
 class update_entries_after(object):
 	"""
-		update valution rate and qty after transaction
-		from the current time-bucket onwards
+	update valution rate and qty after transaction
+	from the current time-bucket onwards
 
-		:param args: args as dict
+	:param args: args as dict
 
-			args = {
-				"item_code": "ABC",
-				"warehouse": "XYZ",
-				"posting_date": "2012-12-12",
-				"posting_time": "12:00"
-			}
+	        args = {
+	                "item_code": "ABC",
+	                "warehouse": "XYZ",
+	                "posting_date": "2012-12-12",
+	                "posting_time": "12:00"
+	        }
 	"""
-	def __init__(self, args, allow_zero_rate=False, allow_negative_stock=None, via_landed_cost_voucher=False, verbose=1):
+
+	def __init__(
+		self,
+		args,
+		allow_zero_rate=False,
+		allow_negative_stock=None,
+		via_landed_cost_voucher=False,
+		verbose=1,
+	):
 		self.exceptions = {}
 		self.verbose = verbose
 		self.allow_zero_rate = allow_zero_rate
 		self.via_landed_cost_voucher = via_landed_cost_voucher
 		self.item_code = args.get("item_code")
-		self.allow_negative_stock = allow_negative_stock or is_negative_stock_allowed(item_code=self.item_code)
+		self.allow_negative_stock = allow_negative_stock or is_negative_stock_allowed(
+			item_code=self.item_code
+		)
 
 		self.args = frappe._dict(args)
 		if self.args.sle_id:
-			self.args['name'] = self.args.sle_id
+			self.args["name"] = self.args.sle_id
 
 		self.company = frappe.get_cached_value("Warehouse", self.args.warehouse, "company")
-		self.get_precision()
+		self.set_precision()
 		self.valuation_method = get_valuation_method(self.item_code)
 
 		self.new_items_found = False
 		self.distinct_item_warehouses = args.get("distinct_item_warehouses", frappe._dict())
+		self.affected_transactions: Set[Tuple[str, str]] = set()
 
 		self.data = frappe._dict()
 		self.initialize_previous_data(self.args)
 		self.build()
 
-	def get_precision(self):
-		company_base_currency = frappe.get_cached_value('Company',  self.company,  "default_currency")
-		self.precision = get_field_precision(frappe.get_meta("Stock Ledger Entry").get_field("stock_value"),
-			currency=company_base_currency)
+	def set_precision(self):
+		self.flt_precision = cint(frappe.db.get_default("float_precision")) or 2
+		self.currency_precision = get_field_precision(
+			frappe.get_meta("Stock Ledger Entry").get_field("stock_value")
+		)
 
 	def initialize_previous_data(self, args):
 		"""
-			Get previous sl entries for current item for each related warehouse
-			and assigns into self.data dict
+		Get previous sl entries for current item for each related warehouse
+		and assigns into self.data dict
 
-			:Data Structure:
+		:Data Structure:
 
-			self.data = {
-				warehouse1: {
-					'previus_sle': {},
-					'qty_after_transaction': 10,
-					'valuation_rate': 100,
-					'stock_value': 1000,
-					'prev_stock_value': 1000,
-					'stock_queue': '[[10, 100]]',
-					'stock_value_difference': 1000
-				}
-			}
+		self.data = {
+		        warehouse1: {
+		                'previus_sle': {},
+		                'qty_after_transaction': 10,
+		                'valuation_rate': 100,
+		                'stock_value': 1000,
+		                'prev_stock_value': 1000,
+		                'stock_queue': '[[10, 100]]',
+		                'stock_value_difference': 1000
+		        }
+		}
 
 		"""
 		self.data.setdefault(args.warehouse, frappe._dict())
@@ -331,11 +415,13 @@
 		for key in ("qty_after_transaction", "valuation_rate", "stock_value"):
 			setattr(warehouse_dict, key, flt(previous_sle.get(key)))
 
-		warehouse_dict.update({
-			"prev_stock_value": previous_sle.stock_value or 0.0,
-			"stock_queue": json.loads(previous_sle.stock_queue or "[]"),
-			"stock_value_difference": 0.0
-		})
+		warehouse_dict.update(
+			{
+				"prev_stock_value": previous_sle.stock_value or 0.0,
+				"stock_queue": json.loads(previous_sle.stock_queue or "[]"),
+				"stock_value_difference": 0.0,
+			}
+		)
 
 	def build(self):
 		from erpnext.controllers.stock_controller import future_sle_exists
@@ -368,9 +454,10 @@
 			self.process_sle(sle)
 
 	def get_sle_against_current_voucher(self):
-		self.args['time_format'] = '%H:%i:%s'
+		self.args["time_format"] = "%H:%i:%s"
 
-		return frappe.db.sql("""
+		return frappe.db.sql(
+			"""
 			select
 				*, timestamp(posting_date, posting_time) as "timestamp"
 			from
@@ -384,22 +471,29 @@
 			order by
 				creation ASC
 			for update
-		""", self.args, as_dict=1)
+		""",
+			self.args,
+			as_dict=1,
+		)
 
 	def get_future_entries_to_fix(self):
 		# includes current entry!
-		args = self.data[self.args.warehouse].previous_sle \
-			or frappe._dict({"item_code": self.item_code, "warehouse": self.args.warehouse})
+		args = self.data[self.args.warehouse].previous_sle or frappe._dict(
+			{"item_code": self.item_code, "warehouse": self.args.warehouse}
+		)
 
 		return list(self.get_sle_after_datetime(args))
 
 	def get_dependent_entries_to_fix(self, entries_to_fix, sle):
-		dependant_sle = get_sle_by_voucher_detail_no(sle.dependant_sle_voucher_detail_no,
-			excluded_sle=sle.name)
+		dependant_sle = get_sle_by_voucher_detail_no(
+			sle.dependant_sle_voucher_detail_no, excluded_sle=sle.name
+		)
 
 		if not dependant_sle:
 			return entries_to_fix
-		elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse == self.args.warehouse:
+		elif (
+			dependant_sle.item_code == self.item_code and dependant_sle.warehouse == self.args.warehouse
+		):
 			return entries_to_fix
 		elif dependant_sle.item_code != self.item_code:
 			self.update_distinct_item_warehouses(dependant_sle)
@@ -411,14 +505,14 @@
 
 	def update_distinct_item_warehouses(self, dependant_sle):
 		key = (dependant_sle.item_code, dependant_sle.warehouse)
-		val = frappe._dict({
-			"sle": dependant_sle
-		})
+		val = frappe._dict({"sle": dependant_sle})
 		if key not in self.distinct_item_warehouses:
 			self.distinct_item_warehouses[key] = val
 			self.new_items_found = True
 		else:
-			existing_sle_posting_date = self.distinct_item_warehouses[key].get("sle", {}).get("posting_date")
+			existing_sle_posting_date = (
+				self.distinct_item_warehouses[key].get("sle", {}).get("posting_date")
+			)
 			if getdate(dependant_sle.posting_date) < getdate(existing_sle_posting_date):
 				val.sle_changed = True
 				self.distinct_item_warehouses[key] = val
@@ -427,18 +521,20 @@
 	def append_future_sle_for_dependant(self, dependant_sle, entries_to_fix):
 		self.initialize_previous_data(dependant_sle)
 
-		args = self.data[dependant_sle.warehouse].previous_sle \
-			or frappe._dict({"item_code": self.item_code, "warehouse": dependant_sle.warehouse})
+		args = self.data[dependant_sle.warehouse].previous_sle or frappe._dict(
+			{"item_code": self.item_code, "warehouse": dependant_sle.warehouse}
+		)
 		future_sle_for_dependant = list(self.get_sle_after_datetime(args))
 
 		entries_to_fix.extend(future_sle_for_dependant)
-		return sorted(entries_to_fix, key=lambda k: k['timestamp'])
+		return sorted(entries_to_fix, key=lambda k: k["timestamp"])
 
 	def process_sle(self, sle):
 		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 
 		# previous sle data for this warehouse
 		self.wh_data = self.data[sle.warehouse]
+		self.affected_transactions.add((sle.voucher_type, sle.voucher_no))
 
 		if (sle.serial_no and not self.via_landed_cost_voucher) or not cint(self.allow_negative_stock):
 			# validate negative stock for serialized items, fifo valuation
@@ -457,27 +553,35 @@
 			if sle.voucher_type == "Stock Reconciliation":
 				self.wh_data.qty_after_transaction = sle.qty_after_transaction
 
-			self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt(self.wh_data.valuation_rate)
-		elif sle.batch_no and frappe.db.get_value("Batch", sle.batch_no, "use_batchwise_valuation", cache=True):
+			self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt(
+				self.wh_data.valuation_rate
+			)
+		elif sle.batch_no and frappe.db.get_value(
+			"Batch", sle.batch_no, "use_batchwise_valuation", cache=True
+		):
 			self.update_batched_values(sle)
 		else:
-			if sle.voucher_type=="Stock Reconciliation" and not sle.batch_no:
+			if sle.voucher_type == "Stock Reconciliation" and not sle.batch_no:
 				# assert
 				self.wh_data.valuation_rate = sle.valuation_rate
 				self.wh_data.qty_after_transaction = sle.qty_after_transaction
-				self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt(self.wh_data.valuation_rate)
+				self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt(
+					self.wh_data.valuation_rate
+				)
 				if self.valuation_method != "Moving Average":
 					self.wh_data.stock_queue = [[self.wh_data.qty_after_transaction, self.wh_data.valuation_rate]]
 			else:
 				if self.valuation_method == "Moving Average":
 					self.get_moving_average_values(sle)
 					self.wh_data.qty_after_transaction += flt(sle.actual_qty)
-					self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt(self.wh_data.valuation_rate)
+					self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt(
+						self.wh_data.valuation_rate
+					)
 				else:
 					self.update_queue_values(sle)
 
 		# rounding as per precision
-		self.wh_data.stock_value = flt(self.wh_data.stock_value, self.precision)
+		self.wh_data.stock_value = flt(self.wh_data.stock_value, self.currency_precision)
 		if not self.wh_data.qty_after_transaction:
 			self.wh_data.stock_value = 0.0
 		stock_value_difference = self.wh_data.stock_value - self.wh_data.prev_stock_value
@@ -489,19 +593,19 @@
 		sle.stock_value = self.wh_data.stock_value
 		sle.stock_queue = json.dumps(self.wh_data.stock_queue)
 		sle.stock_value_difference = stock_value_difference
-		sle.doctype="Stock Ledger Entry"
+		sle.doctype = "Stock Ledger Entry"
 		frappe.get_doc(sle).db_update()
 
 		if not self.args.get("sle_id"):
 			self.update_outgoing_rate_on_transaction(sle)
 
-
 	def validate_negative_stock(self, sle):
 		"""
-			validate negative stock for entries current datetime onwards
-			will not consider cancelled entries
+		validate negative stock for entries current datetime onwards
+		will not consider cancelled entries
 		"""
 		diff = self.wh_data.qty_after_transaction + flt(sle.actual_qty)
+		diff = flt(diff, self.flt_precision)  # respect system precision
 
 		if diff < 0 and abs(diff) > 0.0001:
 			# negative stock!
@@ -528,13 +632,24 @@
 			self.recalculate_amounts_in_stock_entry(sle.voucher_no)
 			rate = frappe.db.get_value("Stock Entry Detail", sle.voucher_detail_no, "valuation_rate")
 		# Sales and Purchase Return
-		elif sle.voucher_type in ("Purchase Receipt", "Purchase Invoice", "Delivery Note", "Sales Invoice"):
+		elif sle.voucher_type in (
+			"Purchase Receipt",
+			"Purchase Invoice",
+			"Delivery Note",
+			"Sales Invoice",
+		):
 			if frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_return"):
 				from erpnext.controllers.sales_and_purchase_return import (
 					get_rate_for_return,  # don't move this import to top
 				)
-				rate = get_rate_for_return(sle.voucher_type, sle.voucher_no, sle.item_code,
-					voucher_detail_no=sle.voucher_detail_no, sle = sle)
+
+				rate = get_rate_for_return(
+					sle.voucher_type,
+					sle.voucher_no,
+					sle.item_code,
+					voucher_detail_no=sle.voucher_detail_no,
+					sle=sle,
+				)
 			else:
 				if sle.voucher_type in ("Purchase Receipt", "Purchase Invoice"):
 					rate_field = "valuation_rate"
@@ -542,8 +657,9 @@
 					rate_field = "incoming_rate"
 
 				# check in item table
-				item_code, incoming_rate = frappe.db.get_value(sle.voucher_type + " Item",
-					sle.voucher_detail_no, ["item_code", rate_field])
+				item_code, incoming_rate = frappe.db.get_value(
+					sle.voucher_type + " Item", sle.voucher_detail_no, ["item_code", rate_field]
+				)
 
 				if item_code == sle.item_code:
 					rate = incoming_rate
@@ -553,15 +669,18 @@
 					else:
 						ref_doctype = "Purchase Receipt Item Supplied"
 
-					rate = frappe.db.get_value(ref_doctype, {"parent_detail_docname": sle.voucher_detail_no,
-						"item_code": sle.item_code}, rate_field)
+					rate = frappe.db.get_value(
+						ref_doctype,
+						{"parent_detail_docname": sle.voucher_detail_no, "item_code": sle.item_code},
+						rate_field,
+					)
 
 		return rate
 
 	def update_outgoing_rate_on_transaction(self, sle):
 		"""
-			Update outgoing rate in Stock Entry, Delivery Note, Sales Invoice and Sales Return
-			In case of Stock Entry, also calculate FG Item rate and total incoming/outgoing amount
+		Update outgoing rate in Stock Entry, Delivery Note, Sales Invoice and Sales Return
+		In case of Stock Entry, also calculate FG Item rate and total incoming/outgoing amount
 		"""
 		if sle.actual_qty and sle.voucher_detail_no:
 			outgoing_rate = abs(flt(sle.stock_value_difference)) / abs(sle.actual_qty)
@@ -591,24 +710,33 @@
 		# Update item's incoming rate on transaction
 		item_code = frappe.db.get_value(sle.voucher_type + " Item", sle.voucher_detail_no, "item_code")
 		if item_code == sle.item_code:
-			frappe.db.set_value(sle.voucher_type + " Item", sle.voucher_detail_no, "incoming_rate", outgoing_rate)
+			frappe.db.set_value(
+				sle.voucher_type + " Item", sle.voucher_detail_no, "incoming_rate", outgoing_rate
+			)
 		else:
 			# packed item
-			frappe.db.set_value("Packed Item",
+			frappe.db.set_value(
+				"Packed Item",
 				{"parent_detail_docname": sle.voucher_detail_no, "item_code": sle.item_code},
-				"incoming_rate", outgoing_rate)
+				"incoming_rate",
+				outgoing_rate,
+			)
 
 	def update_rate_on_purchase_receipt(self, sle, outgoing_rate):
 		if frappe.db.exists(sle.voucher_type + " Item", sle.voucher_detail_no):
-			frappe.db.set_value(sle.voucher_type + " Item", sle.voucher_detail_no, "base_net_rate", outgoing_rate)
+			frappe.db.set_value(
+				sle.voucher_type + " Item", sle.voucher_detail_no, "base_net_rate", outgoing_rate
+			)
 		else:
-			frappe.db.set_value("Purchase Receipt Item Supplied", sle.voucher_detail_no, "rate", outgoing_rate)
+			frappe.db.set_value(
+				"Purchase Receipt Item Supplied", sle.voucher_detail_no, "rate", outgoing_rate
+			)
 
 		# Recalculate subcontracted item's rate in case of subcontracted purchase receipt/invoice
-		if frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_subcontracted") == 'Yes':
+		if frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_subcontracted"):
 			doc = frappe.get_doc(sle.voucher_type, sle.voucher_no)
 			doc.update_valuation_rate(reset_outgoing_rate=False)
-			for d in (doc.items + doc.supplied_items):
+			for d in doc.items + doc.supplied_items:
 				d.db_update()
 
 	def get_serialized_values(self, sle):
@@ -635,29 +763,34 @@
 		new_stock_qty = self.wh_data.qty_after_transaction + actual_qty
 
 		if new_stock_qty > 0:
-			new_stock_value = (self.wh_data.qty_after_transaction * self.wh_data.valuation_rate) + stock_value_change
+			new_stock_value = (
+				self.wh_data.qty_after_transaction * self.wh_data.valuation_rate
+			) + stock_value_change
 			if new_stock_value >= 0:
 				# calculate new valuation rate only if stock value is positive
 				# else it remains the same as that of previous entry
 				self.wh_data.valuation_rate = new_stock_value / new_stock_qty
 
 		if not self.wh_data.valuation_rate and sle.voucher_detail_no:
-			allow_zero_rate = self.check_if_allow_zero_valuation_rate(sle.voucher_type, sle.voucher_detail_no)
+			allow_zero_rate = self.check_if_allow_zero_valuation_rate(
+				sle.voucher_type, sle.voucher_detail_no
+			)
 			if not allow_zero_rate:
 				self.wh_data.valuation_rate = self.get_fallback_rate(sle)
 
 	def get_incoming_value_for_serial_nos(self, sle, serial_nos):
 		# get rate from serial nos within same company
-		all_serial_nos = frappe.get_all("Serial No",
-			fields=["purchase_rate", "name", "company"],
-			filters = {'name': ('in', serial_nos)})
+		all_serial_nos = frappe.get_all(
+			"Serial No", fields=["purchase_rate", "name", "company"], filters={"name": ("in", serial_nos)}
+		)
 
-		incoming_values = sum(flt(d.purchase_rate) for d in all_serial_nos if d.company==sle.company)
+		incoming_values = sum(flt(d.purchase_rate) for d in all_serial_nos if d.company == sle.company)
 
 		# Get rate for serial nos which has been transferred to other company
-		invalid_serial_nos = [d.name for d in all_serial_nos if d.company!=sle.company]
+		invalid_serial_nos = [d.name for d in all_serial_nos if d.company != sle.company]
 		for serial_no in invalid_serial_nos:
-			incoming_rate = frappe.db.sql("""
+			incoming_rate = frappe.db.sql(
+				"""
 				select incoming_rate
 				from `tabStock Ledger Entry`
 				where
@@ -671,7 +804,9 @@
 					)
 				order by posting_date desc
 				limit 1
-			""", (sle.company, serial_no, serial_no+'\n%', '%\n'+serial_no, '%\n'+serial_no+'\n%'))
+			""",
+				(sle.company, serial_no, serial_no + "\n%", "%\n" + serial_no, "%\n" + serial_no + "\n%"),
+			)
 
 			incoming_values += flt(incoming_rate[0][0]) if incoming_rate else 0
 
@@ -685,15 +820,17 @@
 				if flt(self.wh_data.qty_after_transaction) <= 0:
 					self.wh_data.valuation_rate = sle.incoming_rate
 				else:
-					new_stock_value = (self.wh_data.qty_after_transaction * self.wh_data.valuation_rate) + \
-						(actual_qty * sle.incoming_rate)
+					new_stock_value = (self.wh_data.qty_after_transaction * self.wh_data.valuation_rate) + (
+						actual_qty * sle.incoming_rate
+					)
 
 					self.wh_data.valuation_rate = new_stock_value / new_stock_qty
 
 			elif sle.outgoing_rate:
 				if new_stock_qty:
-					new_stock_value = (self.wh_data.qty_after_transaction * self.wh_data.valuation_rate) + \
-						(actual_qty * sle.outgoing_rate)
+					new_stock_value = (self.wh_data.qty_after_transaction * self.wh_data.valuation_rate) + (
+						actual_qty * sle.outgoing_rate
+					)
 
 					self.wh_data.valuation_rate = new_stock_value / new_stock_qty
 				else:
@@ -708,7 +845,9 @@
 			# Get valuation rate from previous SLE or Item master, if item does not have the
 			# allow zero valuration rate flag set
 			if not self.wh_data.valuation_rate and sle.voucher_detail_no:
-				allow_zero_valuation_rate = self.check_if_allow_zero_valuation_rate(sle.voucher_type, sle.voucher_detail_no)
+				allow_zero_valuation_rate = self.check_if_allow_zero_valuation_rate(
+					sle.voucher_type, sle.voucher_detail_no
+				)
 				if not allow_zero_valuation_rate:
 					self.wh_data.valuation_rate = self.get_fallback_rate(sle)
 
@@ -717,7 +856,9 @@
 		actual_qty = flt(sle.actual_qty)
 		outgoing_rate = flt(sle.outgoing_rate)
 
-		self.wh_data.qty_after_transaction = round_off_if_near_zero(self.wh_data.qty_after_transaction + actual_qty)
+		self.wh_data.qty_after_transaction = round_off_if_near_zero(
+			self.wh_data.qty_after_transaction + actual_qty
+		)
 
 		if self.valuation_method == "LIFO":
 			stock_queue = LIFOValuation(self.wh_data.stock_queue)
@@ -729,24 +870,33 @@
 		if actual_qty > 0:
 			stock_queue.add_stock(qty=actual_qty, rate=incoming_rate)
 		else:
+
 			def rate_generator() -> float:
-				allow_zero_valuation_rate = self.check_if_allow_zero_valuation_rate(sle.voucher_type, sle.voucher_detail_no)
+				allow_zero_valuation_rate = self.check_if_allow_zero_valuation_rate(
+					sle.voucher_type, sle.voucher_detail_no
+				)
 				if not allow_zero_valuation_rate:
 					return self.get_fallback_rate(sle)
 				else:
 					return 0.0
 
-			stock_queue.remove_stock(qty=abs(actual_qty), outgoing_rate=outgoing_rate, rate_generator=rate_generator)
+			stock_queue.remove_stock(
+				qty=abs(actual_qty), outgoing_rate=outgoing_rate, rate_generator=rate_generator
+			)
 
 		_qty, stock_value = stock_queue.get_total_stock_and_value()
 
 		stock_value_difference = stock_value - prev_stock_value
 
 		self.wh_data.stock_queue = stock_queue.state
-		self.wh_data.stock_value = round_off_if_near_zero(self.wh_data.stock_value + stock_value_difference)
+		self.wh_data.stock_value = round_off_if_near_zero(
+			self.wh_data.stock_value + stock_value_difference
+		)
 
 		if not self.wh_data.stock_queue:
-			self.wh_data.stock_queue.append([0, sle.incoming_rate or sle.outgoing_rate or self.wh_data.valuation_rate])
+			self.wh_data.stock_queue.append(
+				[0, sle.incoming_rate or sle.outgoing_rate or self.wh_data.valuation_rate]
+			)
 
 		if self.wh_data.qty_after_transaction:
 			self.wh_data.valuation_rate = self.wh_data.stock_value / self.wh_data.qty_after_transaction
@@ -755,14 +905,21 @@
 		incoming_rate = flt(sle.incoming_rate)
 		actual_qty = flt(sle.actual_qty)
 
-		self.wh_data.qty_after_transaction = round_off_if_near_zero(self.wh_data.qty_after_transaction + actual_qty)
+		self.wh_data.qty_after_transaction = round_off_if_near_zero(
+			self.wh_data.qty_after_transaction + actual_qty
+		)
 
 		if actual_qty > 0:
 			stock_value_difference = incoming_rate * actual_qty
 		else:
-			outgoing_rate = get_batch_incoming_rate(item_code=sle.item_code,
-					warehouse=sle.warehouse, batch_no=sle.batch_no, posting_date=sle.posting_date,
-					posting_time=sle.posting_time, creation=sle.creation)
+			outgoing_rate = get_batch_incoming_rate(
+				item_code=sle.item_code,
+				warehouse=sle.warehouse,
+				batch_no=sle.batch_no,
+				posting_date=sle.posting_date,
+				posting_time=sle.posting_time,
+				creation=sle.creation,
+			)
 			if outgoing_rate is None:
 				# This can *only* happen if qty available for the batch is zero.
 				# in such case fall back various other rates.
@@ -771,7 +928,9 @@
 				outgoing_rate = self.get_fallback_rate(sle)
 			stock_value_difference = outgoing_rate * actual_qty
 
-		self.wh_data.stock_value = round_off_if_near_zero(self.wh_data.stock_value + stock_value_difference)
+		self.wh_data.stock_value = round_off_if_near_zero(
+			self.wh_data.stock_value + stock_value_difference
+		)
 		if self.wh_data.qty_after_transaction:
 			self.wh_data.valuation_rate = self.wh_data.stock_value / self.wh_data.qty_after_transaction
 
@@ -790,10 +949,17 @@
 
 	def get_fallback_rate(self, sle) -> float:
 		"""When exact incoming rate isn't available use any of other "average" rates as fallback.
-			This should only get used for negative stock."""
-		return get_valuation_rate(sle.item_code, sle.warehouse,
-			sle.voucher_type, sle.voucher_no, self.allow_zero_rate,
-			currency=erpnext.get_company_currency(sle.company), company=sle.company, batch_no=sle.batch_no)
+		This should only get used for negative stock."""
+		return get_valuation_rate(
+			sle.item_code,
+			sle.warehouse,
+			sle.voucher_type,
+			sle.voucher_no,
+			self.allow_zero_rate,
+			currency=erpnext.get_company_currency(sle.company),
+			company=sle.company,
+			batch_no=sle.batch_no,
+		)
 
 	def get_sle_before_datetime(self, args):
 		"""get previous stock ledger entry before current time-bucket"""
@@ -810,18 +976,27 @@
 		for warehouse, exceptions in self.exceptions.items():
 			deficiency = min(e["diff"] for e in exceptions)
 
-			if ((exceptions[0]["voucher_type"], exceptions[0]["voucher_no"]) in
-				frappe.local.flags.currently_saving):
+			if (
+				exceptions[0]["voucher_type"],
+				exceptions[0]["voucher_no"],
+			) in frappe.local.flags.currently_saving:
 
 				msg = _("{0} units of {1} needed in {2} to complete this transaction.").format(
-					abs(deficiency), frappe.get_desk_link('Item', exceptions[0]["item_code"]),
-					frappe.get_desk_link('Warehouse', warehouse))
+					abs(deficiency),
+					frappe.get_desk_link("Item", exceptions[0]["item_code"]),
+					frappe.get_desk_link("Warehouse", warehouse),
+				)
 			else:
-				msg = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format(
-					abs(deficiency), frappe.get_desk_link('Item', exceptions[0]["item_code"]),
-					frappe.get_desk_link('Warehouse', warehouse),
-					exceptions[0]["posting_date"], exceptions[0]["posting_time"],
-					frappe.get_desk_link(exceptions[0]["voucher_type"], exceptions[0]["voucher_no"]))
+				msg = _(
+					"{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction."
+				).format(
+					abs(deficiency),
+					frappe.get_desk_link("Item", exceptions[0]["item_code"]),
+					frappe.get_desk_link("Warehouse", warehouse),
+					exceptions[0]["posting_date"],
+					exceptions[0]["posting_time"],
+					frappe.get_desk_link(exceptions[0]["voucher_type"], exceptions[0]["voucher_no"]),
+				)
 
 			if msg:
 				msg_list.append(msg)
@@ -829,7 +1004,7 @@
 		if msg_list:
 			message = "\n\n".join(msg_list)
 			if self.verbose:
-				frappe.throw(message, NegativeStockError, title='Insufficient Stock')
+				frappe.throw(message, NegativeStockError, title=_("Insufficient Stock"))
 			else:
 				raise NegativeStockError(message)
 
@@ -838,17 +1013,16 @@
 		for warehouse, data in self.data.items():
 			bin_name = get_or_make_bin(self.item_code, warehouse)
 
-			frappe.db.set_value('Bin', bin_name, {
-				"valuation_rate": data.valuation_rate,
-				"actual_qty": data.qty_after_transaction,
-				"stock_value": data.stock_value
-			})
+			updated_values = {"actual_qty": data.qty_after_transaction, "stock_value": data.stock_value}
+			if data.valuation_rate is not None:
+				updated_values["valuation_rate"] = data.valuation_rate
+			frappe.db.set_value("Bin", bin_name, updated_values)
 
 
 def get_previous_sle_of_current_voucher(args, exclude_current_voucher=False):
 	"""get stock ledger entries filtered by specific posting datetime conditions"""
 
-	args['time_format'] = '%H:%i:%s'
+	args["time_format"] = "%H:%i:%s"
 	if not args.get("posting_date"):
 		args["posting_date"] = "1900-01-01"
 	if not args.get("posting_time"):
@@ -859,7 +1033,8 @@
 		voucher_no = args.get("voucher_no")
 		voucher_condition = f"and voucher_no != '{voucher_no}'"
 
-	sle = frappe.db.sql("""
+	sle = frappe.db.sql(
+		"""
 		select *, timestamp(posting_date, posting_time) as "timestamp"
 		from `tabStock Ledger Entry`
 		where item_code = %(item_code)s
@@ -869,32 +1044,48 @@
 			and timestamp(posting_date, time_format(posting_time, %(time_format)s)) < timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s))
 		order by timestamp(posting_date, posting_time) desc, creation desc
 		limit 1
-		for update""".format(voucher_condition=voucher_condition), args, as_dict=1)
+		for update""".format(
+			voucher_condition=voucher_condition
+		),
+		args,
+		as_dict=1,
+	)
 
 	return sle[0] if sle else frappe._dict()
 
+
 def get_previous_sle(args, for_update=False):
 	"""
-		get the last sle on or before the current time-bucket,
-		to get actual qty before transaction, this function
-		is called from various transaction like stock entry, reco etc
+	get the last sle on or before the current time-bucket,
+	to get actual qty before transaction, this function
+	is called from various transaction like stock entry, reco etc
 
-		args = {
-			"item_code": "ABC",
-			"warehouse": "XYZ",
-			"posting_date": "2012-12-12",
-			"posting_time": "12:00",
-			"sle": "name of reference Stock Ledger Entry"
-		}
+	args = {
+	        "item_code": "ABC",
+	        "warehouse": "XYZ",
+	        "posting_date": "2012-12-12",
+	        "posting_time": "12:00",
+	        "sle": "name of reference Stock Ledger Entry"
+	}
 	"""
 	args["name"] = args.get("sle", None) or ""
 	sle = get_stock_ledger_entries(args, "<=", "desc", "limit 1", for_update=for_update)
 	return sle and sle[0] or {}
 
-def get_stock_ledger_entries(previous_sle, operator=None,
-	order="desc", limit=None, for_update=False, debug=False, check_serial_no=True):
+
+def get_stock_ledger_entries(
+	previous_sle,
+	operator=None,
+	order="desc",
+	limit=None,
+	for_update=False,
+	debug=False,
+	check_serial_no=True,
+):
 	"""get stock ledger entries filtered by specific posting datetime conditions"""
-	conditions = " and timestamp(posting_date, posting_time) {0} timestamp(%(posting_date)s, %(posting_time)s)".format(operator)
+	conditions = " and timestamp(posting_date, posting_time) {0} timestamp(%(posting_date)s, %(posting_time)s)".format(
+		operator
+	)
 	if previous_sle.get("warehouse"):
 		conditions += " and warehouse = %(warehouse)s"
 	elif previous_sle.get("warehouse_condition"):
@@ -903,15 +1094,21 @@
 	if check_serial_no and previous_sle.get("serial_no"):
 		# conditions += " and serial_no like {}".format(frappe.db.escape('%{0}%'.format(previous_sle.get("serial_no"))))
 		serial_no = previous_sle.get("serial_no")
-		conditions += (""" and
+		conditions += (
+			""" and
 			(
 				serial_no = {0}
 				or serial_no like {1}
 				or serial_no like {2}
 				or serial_no like {3}
 			)
-		""").format(frappe.db.escape(serial_no), frappe.db.escape('{}\n%'.format(serial_no)),
-			frappe.db.escape('%\n{}'.format(serial_no)), frappe.db.escape('%\n{}\n%'.format(serial_no)))
+		"""
+		).format(
+			frappe.db.escape(serial_no),
+			frappe.db.escape("{}\n%".format(serial_no)),
+			frappe.db.escape("%\n{}".format(serial_no)),
+			frappe.db.escape("%\n{}\n%".format(serial_no)),
+		)
 
 	if not previous_sle.get("posting_date"):
 		previous_sle["posting_date"] = "1900-01-01"
@@ -921,70 +1118,94 @@
 	if operator in (">", "<=") and previous_sle.get("name"):
 		conditions += " and name!=%(name)s"
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select *, timestamp(posting_date, posting_time) as "timestamp"
 		from `tabStock Ledger Entry`
 		where item_code = %%(item_code)s
 		and is_cancelled = 0
 		%(conditions)s
 		order by timestamp(posting_date, posting_time) %(order)s, creation %(order)s
-		%(limit)s %(for_update)s""" % {
+		%(limit)s %(for_update)s"""
+		% {
 			"conditions": conditions,
 			"limit": limit or "",
 			"for_update": for_update and "for update" or "",
-			"order": order
-		}, previous_sle, as_dict=1, debug=debug)
+			"order": order,
+		},
+		previous_sle,
+		as_dict=1,
+		debug=debug,
+	)
+
 
 def get_sle_by_voucher_detail_no(voucher_detail_no, excluded_sle=None):
-	return frappe.db.get_value('Stock Ledger Entry',
-		{'voucher_detail_no': voucher_detail_no, 'name': ['!=', excluded_sle]},
-		['item_code', 'warehouse', 'posting_date', 'posting_time', 'timestamp(posting_date, posting_time) as timestamp'],
-		as_dict=1)
+	return frappe.db.get_value(
+		"Stock Ledger Entry",
+		{"voucher_detail_no": voucher_detail_no, "name": ["!=", excluded_sle]},
+		[
+			"item_code",
+			"warehouse",
+			"posting_date",
+			"posting_time",
+			"timestamp(posting_date, posting_time) as timestamp",
+		],
+		as_dict=1,
+	)
 
-def get_batch_incoming_rate(item_code, warehouse, batch_no, posting_date, posting_time, creation=None):
 
-	Timestamp = CustomFunction('timestamp', ['date', 'time'])
+def get_batch_incoming_rate(
+	item_code, warehouse, batch_no, posting_date, posting_time, creation=None
+):
 
 	sle = frappe.qb.DocType("Stock Ledger Entry")
 
-	timestamp_condition = (Timestamp(sle.posting_date, sle.posting_time) < Timestamp(posting_date, posting_time))
+	timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime(
+		posting_date, posting_time
+	)
 	if creation:
 		timestamp_condition |= (
-				(Timestamp(sle.posting_date, sle.posting_time) == Timestamp(posting_date, posting_time))
-				& (sle.creation < creation)
-			)
+			CombineDatetime(sle.posting_date, sle.posting_time)
+			== CombineDatetime(posting_date, posting_time)
+		) & (sle.creation < creation)
 
 	batch_details = (
-		frappe.qb
-			.from_(sle)
-			.select(
-				Sum(sle.stock_value_difference).as_("batch_value"),
-				Sum(sle.actual_qty).as_("batch_qty")
-			)
-			.where(
-				(sle.item_code == item_code)
-				& (sle.warehouse == warehouse)
-				& (sle.batch_no == batch_no)
-				& (sle.is_cancelled == 0)
-			)
-			.where(timestamp_condition)
+		frappe.qb.from_(sle)
+		.select(Sum(sle.stock_value_difference).as_("batch_value"), Sum(sle.actual_qty).as_("batch_qty"))
+		.where(
+			(sle.item_code == item_code)
+			& (sle.warehouse == warehouse)
+			& (sle.batch_no == batch_no)
+			& (sle.is_cancelled == 0)
+		)
+		.where(timestamp_condition)
 	).run(as_dict=True)
 
 	if batch_details and batch_details[0].batch_qty:
 		return batch_details[0].batch_value / batch_details[0].batch_qty
 
 
-def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no,
-	allow_zero_rate=False, currency=None, company=None, raise_error_if_no_rate=True, batch_no=None):
+def get_valuation_rate(
+	item_code,
+	warehouse,
+	voucher_type,
+	voucher_no,
+	allow_zero_rate=False,
+	currency=None,
+	company=None,
+	raise_error_if_no_rate=True,
+	batch_no=None,
+):
 
 	if not company:
-		company =  frappe.get_cached_value("Warehouse", warehouse, "company")
+		company = frappe.get_cached_value("Warehouse", warehouse, "company")
 
 	last_valuation_rate = None
 
 	# Get moving average rate of a specific batch number
 	if warehouse and batch_no and frappe.db.get_value("Batch", batch_no, "use_batchwise_valuation"):
-		last_valuation_rate = frappe.db.sql("""
+		last_valuation_rate = frappe.db.sql(
+			"""
 			select sum(stock_value_difference) / sum(actual_qty)
 			from `tabStock Ledger Entry`
 			where
@@ -994,11 +1215,13 @@
 				AND is_cancelled = 0
 				AND NOT (voucher_no = %s AND voucher_type = %s)
 			""",
-			(item_code, warehouse, batch_no, voucher_no, voucher_type))
+			(item_code, warehouse, batch_no, voucher_no, voucher_type),
+		)
 
 	# Get valuation rate from last sle for the same item and warehouse
 	if not last_valuation_rate or last_valuation_rate[0][0] is None:
-		last_valuation_rate = frappe.db.sql("""select valuation_rate
+		last_valuation_rate = frappe.db.sql(
+			"""select valuation_rate
 			from `tabStock Ledger Entry` force index (item_warehouse)
 			where
 				item_code = %s
@@ -1006,18 +1229,23 @@
 				AND valuation_rate >= 0
 				AND is_cancelled = 0
 				AND NOT (voucher_no = %s AND voucher_type = %s)
-			order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, warehouse, voucher_no, voucher_type))
+			order by posting_date desc, posting_time desc, name desc limit 1""",
+			(item_code, warehouse, voucher_no, voucher_type),
+		)
 
 	if not last_valuation_rate:
 		# Get valuation rate from last sle for the item against any warehouse
-		last_valuation_rate = frappe.db.sql("""select valuation_rate
+		last_valuation_rate = frappe.db.sql(
+			"""select valuation_rate
 			from `tabStock Ledger Entry` force index (item_code)
 			where
 				item_code = %s
 				AND valuation_rate > 0
 				AND is_cancelled = 0
 				AND NOT(voucher_no = %s AND voucher_type = %s)
-			order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, voucher_no, voucher_type))
+			order by posting_date desc, posting_time desc, name desc limit 1""",
+			(item_code, voucher_no, voucher_type),
+		)
 
 	if last_valuation_rate:
 		return flt(last_valuation_rate[0][0])
@@ -1032,19 +1260,36 @@
 
 		if not valuation_rate:
 			# try in price list
-			valuation_rate = frappe.db.get_value('Item Price',
-				dict(item_code=item_code, buying=1, currency=currency),
-				'price_list_rate')
+			valuation_rate = frappe.db.get_value(
+				"Item Price", dict(item_code=item_code, buying=1, currency=currency), "price_list_rate"
+			)
 
-	if not allow_zero_rate and not valuation_rate and raise_error_if_no_rate \
-			and cint(erpnext.is_perpetual_inventory_enabled(company)):
-		frappe.local.message_log = []
+	if (
+		not allow_zero_rate
+		and not valuation_rate
+		and raise_error_if_no_rate
+		and cint(erpnext.is_perpetual_inventory_enabled(company))
+	):
 		form_link = get_link_to_form("Item", item_code)
 
-		message = _("Valuation Rate for the Item {0}, is required to do accounting entries for {1} {2}.").format(form_link, voucher_type, voucher_no)
+		message = _(
+			"Valuation Rate for the Item {0}, is required to do accounting entries for {1} {2}."
+		).format(form_link, voucher_type, voucher_no)
 		message += "<br><br>" + _("Here are the options to proceed:")
-		solutions = "<li>" + _("If the item is transacting as a Zero Valuation Rate item in this entry, please enable 'Allow Zero Valuation Rate' in the {0} Item table.").format(voucher_type) + "</li>"
-		solutions += "<li>" + _("If not, you can Cancel / Submit this entry") + " {0} ".format(frappe.bold("after")) + _("performing either one below:") + "</li>"
+		solutions = (
+			"<li>"
+			+ _(
+				"If the item is transacting as a Zero Valuation Rate item in this entry, please enable 'Allow Zero Valuation Rate' in the {0} Item table."
+			).format(voucher_type)
+			+ "</li>"
+		)
+		solutions += (
+			"<li>"
+			+ _("If not, you can Cancel / Submit this entry")
+			+ " {0} ".format(frappe.bold("after"))
+			+ _("performing either one below:")
+			+ "</li>"
+		)
 		sub_solutions = "<ul><li>" + _("Create an incoming stock transaction for the Item.") + "</li>"
 		sub_solutions += "<li>" + _("Mention Valuation Rate in the Item master.") + "</li></ul>"
 		msg = message + solutions + sub_solutions + "</li>"
@@ -1053,11 +1298,14 @@
 
 	return valuation_rate
 
+
 def update_qty_in_future_sle(args, allow_negative_stock=False):
 	"""Recalculate Qty after Transaction in future SLEs based on current SLE."""
 	datetime_limit_condition = ""
 	qty_shift = args.actual_qty
 
+	args["time_format"] = "%H:%i:%s"
+
 	# find difference/shift in qty caused by stock reconciliation
 	if args.voucher_type == "Stock Reconciliation":
 		qty_shift = get_stock_reco_qty_shift(args)
@@ -1069,7 +1317,8 @@
 		# add condition to update SLEs before this date & time
 		datetime_limit_condition = get_datetime_limit_condition(detail)
 
-	frappe.db.sql("""
+	frappe.db.sql(
+		f"""
 		update `tabStock Ledger Entry`
 		set qty_after_transaction = qty_after_transaction + {qty_shift}
 		where
@@ -1077,17 +1326,16 @@
 			and warehouse = %(warehouse)s
 			and voucher_no != %(voucher_no)s
 			and is_cancelled = 0
-			and (timestamp(posting_date, posting_time) > timestamp(%(posting_date)s, %(posting_time)s)
-				or (
-					timestamp(posting_date, posting_time) = timestamp(%(posting_date)s, %(posting_time)s)
-					and creation > %(creation)s
-				)
-			)
+			and timestamp(posting_date, time_format(posting_time, %(time_format)s))
+				> timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s))
 		{datetime_limit_condition}
-		""".format(qty_shift=qty_shift, datetime_limit_condition=datetime_limit_condition), args)
+		""",
+		args,
+	)
 
 	validate_negative_qty_in_future_sle(args, allow_negative_stock)
 
+
 def get_stock_reco_qty_shift(args):
 	stock_reco_qty_shift = 0
 	if args.get("is_cancelled"):
@@ -1099,8 +1347,9 @@
 			stock_reco_qty_shift = flt(args.actual_qty)
 	else:
 		# reco is being submitted
-		last_balance = get_previous_sle_of_current_voucher(args,
-			exclude_current_voucher=True).get("qty_after_transaction")
+		last_balance = get_previous_sle_of_current_voucher(args, exclude_current_voucher=True).get(
+			"qty_after_transaction"
+		)
 
 		if last_balance is not None:
 			stock_reco_qty_shift = flt(args.qty_after_transaction) - flt(last_balance)
@@ -1109,10 +1358,12 @@
 
 	return stock_reco_qty_shift
 
+
 def get_next_stock_reco(args):
 	"""Returns next nearest stock reconciliaton's details."""
 
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select
 			name, posting_date, posting_time, creation, voucher_no
 		from
@@ -1129,8 +1380,13 @@
 					and creation > %(creation)s
 				)
 			)
+		order by timestamp(posting_date, posting_time) asc, creation asc
 		limit 1
-	""", args, as_dict=1)
+	""",
+		args,
+		as_dict=1,
+	)
+
 
 def get_datetime_limit_condition(detail):
 	return f"""
@@ -1142,6 +1398,7 @@
 			)
 		)"""
 
+
 def validate_negative_qty_in_future_sle(args, allow_negative_stock=False):
 	if allow_negative_stock or is_negative_stock_allowed(item_code=args.item_code):
 		return
@@ -1149,33 +1406,58 @@
 		return
 
 	neg_sle = get_future_sle_with_negative_qty(args)
-	if neg_sle:
-		message = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format(
+
+	if is_negative_with_precision(neg_sle):
+		message = _(
+			"{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction."
+		).format(
 			abs(neg_sle[0]["qty_after_transaction"]),
-			frappe.get_desk_link('Item', args.item_code),
-			frappe.get_desk_link('Warehouse', args.warehouse),
-			neg_sle[0]["posting_date"], neg_sle[0]["posting_time"],
-			frappe.get_desk_link(neg_sle[0]["voucher_type"], neg_sle[0]["voucher_no"]))
+			frappe.get_desk_link("Item", args.item_code),
+			frappe.get_desk_link("Warehouse", args.warehouse),
+			neg_sle[0]["posting_date"],
+			neg_sle[0]["posting_time"],
+			frappe.get_desk_link(neg_sle[0]["voucher_type"], neg_sle[0]["voucher_no"]),
+		)
 
-		frappe.throw(message, NegativeStockError, title='Insufficient Stock')
-
+		frappe.throw(message, NegativeStockError, title=_("Insufficient Stock"))
 
 	if not args.batch_no:
 		return
 
 	neg_batch_sle = get_future_sle_with_negative_batch_qty(args)
-	if neg_batch_sle:
-		message = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format(
+	if is_negative_with_precision(neg_batch_sle, is_batch=True):
+		message = _(
+			"{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction."
+		).format(
 			abs(neg_batch_sle[0]["cumulative_total"]),
-			frappe.get_desk_link('Batch', args.batch_no),
-			frappe.get_desk_link('Warehouse', args.warehouse),
-			neg_batch_sle[0]["posting_date"], neg_batch_sle[0]["posting_time"],
-			frappe.get_desk_link(neg_batch_sle[0]["voucher_type"], neg_batch_sle[0]["voucher_no"]))
-		frappe.throw(message, NegativeStockError, title="Insufficient Stock for Batch")
+			frappe.get_desk_link("Batch", args.batch_no),
+			frappe.get_desk_link("Warehouse", args.warehouse),
+			neg_batch_sle[0]["posting_date"],
+			neg_batch_sle[0]["posting_time"],
+			frappe.get_desk_link(neg_batch_sle[0]["voucher_type"], neg_batch_sle[0]["voucher_no"]),
+		)
+		frappe.throw(message, NegativeStockError, title=_("Insufficient Stock for Batch"))
+
+
+def is_negative_with_precision(neg_sle, is_batch=False):
+	"""
+	Returns whether system precision rounded qty is insufficient.
+	E.g: -0.0003 in precision 3 (0.000) is sufficient for the user.
+	"""
+
+	if not neg_sle:
+		return False
+
+	field = "cumulative_total" if is_batch else "qty_after_transaction"
+	precision = cint(frappe.db.get_default("float_precision")) or 2
+	qty_deficit = flt(neg_sle[0][field], precision)
+
+	return qty_deficit < 0 and abs(qty_deficit) > 0.0001
 
 
 def get_future_sle_with_negative_qty(args):
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		select
 			qty_after_transaction, posting_date, posting_time,
 			voucher_type, voucher_no
@@ -1189,11 +1471,15 @@
 			and qty_after_transaction < 0
 		order by timestamp(posting_date, posting_time) asc
 		limit 1
-	""", args, as_dict=1)
+	""",
+		args,
+		as_dict=1,
+	)
 
 
 def get_future_sle_with_negative_batch_qty(args):
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		with batch_ledger as (
 			select
 				posting_date, posting_time, voucher_type, voucher_no,
@@ -1211,7 +1497,10 @@
 			cumulative_total < 0.0
 			and timestamp(posting_date, posting_time) >= timestamp(%(posting_date)s, %(posting_time)s)
 		limit 1
-	""", args, as_dict=1)
+	""",
+		args,
+		as_dict=1,
+	)
 
 
 def is_negative_stock_allowed(*, item_code: Optional[str] = None) -> bool:
diff --git a/erpnext/stock/tests/test_utils.py b/erpnext/stock/tests/test_utils.py
new file mode 100644
index 0000000..4e93ac9
--- /dev/null
+++ b/erpnext/stock/tests/test_utils.py
@@ -0,0 +1,83 @@
+import json
+
+import frappe
+from frappe.tests.utils import FrappeTestCase
+
+from erpnext.stock.utils import scan_barcode
+
+
+class StockTestMixin:
+	"""Mixin to simplfy stock ledger tests, useful for all stock transactions."""
+
+	def make_item(self, item_code=None, properties=None, *args, **kwargs):
+		from erpnext.stock.doctype.item.test_item import make_item
+
+		return make_item(item_code, properties, *args, **kwargs)
+
+	def assertSLEs(self, doc, expected_sles, sle_filters=None):
+		"""Compare sorted SLEs, useful for vouchers that create multiple SLEs for same line"""
+
+		filters = {"voucher_no": doc.name, "voucher_type": doc.doctype, "is_cancelled": 0}
+		if sle_filters:
+			filters.update(sle_filters)
+		sles = frappe.get_all(
+			"Stock Ledger Entry",
+			fields=["*"],
+			filters=filters,
+			order_by="timestamp(posting_date, posting_time), creation",
+		)
+		self.assertGreaterEqual(len(sles), len(expected_sles))
+
+		for exp_sle, act_sle in zip(expected_sles, sles):
+			for k, v in exp_sle.items():
+				act_value = act_sle[k]
+				if k == "stock_queue":
+					act_value = json.loads(act_value)
+					if act_value and act_value[0][0] == 0:
+						# ignore empty fifo bins
+						continue
+
+				self.assertEqual(v, act_value, msg=f"{k} doesn't match \n{exp_sle}\n{act_sle}")
+
+	def assertGLEs(self, doc, expected_gles, gle_filters=None, order_by=None):
+		filters = {"voucher_no": doc.name, "voucher_type": doc.doctype, "is_cancelled": 0}
+
+		if gle_filters:
+			filters.update(gle_filters)
+		actual_gles = frappe.get_all(
+			"GL Entry",
+			fields=["*"],
+			filters=filters,
+			order_by=order_by or "posting_date, creation",
+		)
+		self.assertGreaterEqual(len(actual_gles), len(expected_gles))
+		for exp_gle, act_gle in zip(expected_gles, actual_gles):
+			for k, exp_value in exp_gle.items():
+				act_value = act_gle[k]
+				self.assertEqual(exp_value, act_value, msg=f"{k} doesn't match \n{exp_gle}\n{act_gle}")
+
+
+class TestStockUtilities(FrappeTestCase, StockTestMixin):
+	def test_barcode_scanning(self):
+		simple_item = self.make_item(properties={"barcodes": [{"barcode": "12399"}]})
+		self.assertEqual(scan_barcode("12399")["item_code"], simple_item.name)
+
+		batch_item = self.make_item(properties={"has_batch_no": 1, "create_new_batch": 1})
+		batch = frappe.get_doc(doctype="Batch", item=batch_item.name).insert()
+
+		batch_scan = scan_barcode(batch.name)
+		self.assertEqual(batch_scan["item_code"], batch_item.name)
+		self.assertEqual(batch_scan["batch_no"], batch.name)
+		self.assertEqual(batch_scan["has_batch_no"], 1)
+		self.assertEqual(batch_scan["has_serial_no"], 0)
+
+		serial_item = self.make_item(properties={"has_serial_no": 1})
+		serial = frappe.get_doc(
+			doctype="Serial No", item_code=serial_item.name, serial_no=frappe.generate_hash()
+		).insert()
+
+		serial_scan = scan_barcode(serial.name)
+		self.assertEqual(serial_scan["item_code"], serial_item.name)
+		self.assertEqual(serial_scan["serial_no"], serial.name)
+		self.assertEqual(serial_scan["has_batch_no"], 0)
+		self.assertEqual(serial_scan["has_serial_no"], 1)
diff --git a/erpnext/stock/tests/test_valuation.py b/erpnext/stock/tests/test_valuation.py
index b64ff8e..e60c1ca 100644
--- a/erpnext/stock/tests/test_valuation.py
+++ b/erpnext/stock/tests/test_valuation.py
@@ -16,7 +16,6 @@
 
 
 class TestFIFOValuation(unittest.TestCase):
-
 	def setUp(self):
 		self.queue = FIFOValuation([])
 
@@ -29,7 +28,9 @@
 		self.assertAlmostEqual(sum(q for q, _ in self.queue), qty, msg=f"queue: {self.queue}", places=4)
 
 	def assertTotalValue(self, value):
-		self.assertAlmostEqual(sum(q * r for q, r in self.queue), value, msg=f"queue: {self.queue}", places=2)
+		self.assertAlmostEqual(
+			sum(q * r for q, r in self.queue), value, msg=f"queue: {self.queue}", places=2
+		)
 
 	def test_simple_addition(self):
 		self.queue.add_stock(1, 10)
@@ -55,14 +56,13 @@
 		self.queue.add_stock(6, 10)
 		self.assertEqual(self.queue, [[1, 10]])
 
-
 	def test_negative_stock(self):
 		self.queue.remove_stock(1, 5)
 		self.assertEqual(self.queue, [[-1, 5]])
 
-		# XXX
-		self.queue.remove_stock(1, 10)
+		self.queue.remove_stock(1)
 		self.assertTotalQty(-2)
+		self.assertEqual(self.queue, [[-2, 5]])
 
 		self.queue.add_stock(2, 10)
 		self.assertTotalQty(0)
@@ -75,7 +75,6 @@
 		self.queue.remove_stock(1, 20)
 		self.assertEqual(self.queue, [[1, 10]])
 
-
 	def test_remove_multiple_bins(self):
 		self.queue.add_stock(1, 10)
 		self.queue.add_stock(2, 20)
@@ -85,7 +84,6 @@
 		self.queue.remove_stock(4)
 		self.assertEqual(self.queue, [[5, 20]])
 
-
 	def test_remove_multiple_bins_with_rate(self):
 		self.queue.add_stock(1, 10)
 		self.queue.add_stock(2, 20)
@@ -95,7 +93,7 @@
 		self.queue.remove_stock(3, 20)
 		self.assertEqual(self.queue, [[1, 10], [5, 20]])
 
-	def test_collapsing_of_queue(self):
+	def test_queue_with_unknown_rate(self):
 		self.queue.add_stock(1, 1)
 		self.queue.add_stock(1, 2)
 		self.queue.add_stock(1, 3)
@@ -104,8 +102,7 @@
 		self.assertTotalValue(10)
 
 		self.queue.remove_stock(3, 1)
-		# XXX
-		self.assertEqual(self.queue, [[1, 7]])
+		self.assertEqual(self.queue, [[1, 4]])
 
 	def test_rounding_off(self):
 		self.queue.add_stock(1.0, 1.0)
@@ -143,7 +140,9 @@
 			else:
 				qty = abs(qty)
 				consumed = self.queue.remove_stock(qty)
-				self.assertAlmostEqual(qty, sum(q for q, _ in consumed), msg=f"incorrect consumption {consumed}")
+				self.assertAlmostEqual(
+					qty, sum(q for q, _ in consumed), msg=f"incorrect consumption {consumed}"
+				)
 				total_qty -= qty
 			self.assertTotalQty(total_qty)
 
@@ -164,15 +163,42 @@
 			else:
 				qty = abs(qty)
 				consumed = self.queue.remove_stock(qty)
-				self.assertAlmostEqual(qty, sum(q for q, _ in consumed), msg=f"incorrect consumption {consumed}")
+				self.assertAlmostEqual(
+					qty, sum(q for q, _ in consumed), msg=f"incorrect consumption {consumed}"
+				)
 				total_qty -= qty
 				total_value -= sum(q * r for q, r in consumed)
 			self.assertTotalQty(total_qty)
 			self.assertTotalValue(total_value)
 
+	@given(stock_queue_generator, st.floats(min_value=0.1, max_value=1e6))
+	def test_fifo_qty_value_nonneg_hypothesis_with_outgoing_rate(self, stock_queue, outgoing_rate):
+		self.queue = FIFOValuation([])
+		total_qty = 0.0
+		total_value = 0.0
+
+		for qty, rate in stock_queue:
+			# don't allow negative stock
+			if qty == 0 or total_qty + qty < 0 or abs(qty) < 0.1:
+				continue
+			if qty > 0:
+				self.queue.add_stock(qty, rate)
+				total_qty += qty
+				total_value += qty * rate
+			else:
+				qty = abs(qty)
+				consumed = self.queue.remove_stock(qty, outgoing_rate)
+				self.assertAlmostEqual(
+					qty, sum(q for q, _ in consumed), msg=f"incorrect consumption {consumed}"
+				)
+				total_qty -= qty
+				total_value -= sum(q * r for q, r in consumed)
+			self.assertTotalQty(total_qty)
+			self.assertTotalValue(total_value)
+			self.assertGreaterEqual(total_value, 0)
+
 
 class TestLIFOValuation(unittest.TestCase):
-
 	def setUp(self):
 		self.stack = LIFOValuation([])
 
@@ -185,7 +211,9 @@
 		self.assertAlmostEqual(sum(q for q, _ in self.stack), qty, msg=f"stack: {self.stack}", places=4)
 
 	def assertTotalValue(self, value):
-		self.assertAlmostEqual(sum(q * r for q, r in self.stack), value, msg=f"stack: {self.stack}", places=2)
+		self.assertAlmostEqual(
+			sum(q * r for q, r in self.stack), value, msg=f"stack: {self.stack}", places=2
+		)
 
 	def test_simple_addition(self):
 		self.stack.add_stock(1, 10)
@@ -248,7 +276,6 @@
 		consumed = self.stack.remove_stock(5)
 		self.assertEqual(consumed, [[5, 5]])
 
-
 	@given(stock_queue_generator)
 	def test_lifo_qty_hypothesis(self, stock_stack):
 		self.stack = LIFOValuation([])
@@ -263,7 +290,9 @@
 			else:
 				qty = abs(qty)
 				consumed = self.stack.remove_stock(qty)
-				self.assertAlmostEqual(qty, sum(q for q, _ in consumed), msg=f"incorrect consumption {consumed}")
+				self.assertAlmostEqual(
+					qty, sum(q for q, _ in consumed), msg=f"incorrect consumption {consumed}"
+				)
 				total_qty -= qty
 			self.assertTotalQty(total_qty)
 
@@ -284,12 +313,15 @@
 			else:
 				qty = abs(qty)
 				consumed = self.stack.remove_stock(qty)
-				self.assertAlmostEqual(qty, sum(q for q, _ in consumed), msg=f"incorrect consumption {consumed}")
+				self.assertAlmostEqual(
+					qty, sum(q for q, _ in consumed), msg=f"incorrect consumption {consumed}"
+				)
 				total_qty -= qty
 				total_value -= sum(q * r for q, r in consumed)
 			self.assertTotalQty(total_qty)
 			self.assertTotalValue(total_value)
 
+
 class TestLIFOValuationSLE(FrappeTestCase):
 	ITEM_CODE = "_Test LIFO item"
 	WAREHOUSE = "_Test Warehouse - _TC"
@@ -309,7 +341,9 @@
 		return make_stock_entry(**kwargs)
 
 	def assertStockQueue(self, se, expected_queue):
-		sle_name = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": se.name, "is_cancelled": 0, "voucher_type": "Stock Entry"})
+		sle_name = frappe.db.get_value(
+			"Stock Ledger Entry", {"voucher_no": se.name, "is_cancelled": 0, "voucher_type": "Stock Entry"}
+		)
 		sle = frappe.get_doc("Stock Ledger Entry", sle_name)
 
 		stock_queue = json.loads(sle.stock_queue)
@@ -321,7 +355,6 @@
 		if total_qty > 0:
 			self.assertEqual(stock_queue, expected_queue)
 
-
 	def test_lifo_values(self):
 
 		in1 = self._make_stock_entry(1, 1)
@@ -340,7 +373,7 @@
 		self.assertStockQueue(out2, [[1, 1]])
 
 		in4 = self._make_stock_entry(4, 4)
-		self.assertStockQueue(in4, [[1, 1], [4,4]])
+		self.assertStockQueue(in4, [[1, 1], [4, 4]])
 
 		out3 = self._make_stock_entry(-5)
 		self.assertStockQueue(out3, [])
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index f85a04f..9fb3be5 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -3,17 +3,24 @@
 
 
 import json
+from typing import Dict, Optional
 
 import frappe
 from frappe import _
+from frappe.query_builder.functions import CombineDatetime
 from frappe.utils import cstr, flt, get_link_to_form, nowdate, nowtime
 
 import erpnext
 from erpnext.stock.valuation import FIFOValuation, LIFOValuation
 
 
-class InvalidWarehouseCompany(frappe.ValidationError): pass
-class PendingRepostingError(frappe.ValidationError): pass
+class InvalidWarehouseCompany(frappe.ValidationError):
+	pass
+
+
+class PendingRepostingError(frappe.ValidationError):
+	pass
+
 
 def get_stock_value_from_bin(warehouse=None, item_code=None):
 	values = {}
@@ -26,22 +33,27 @@
 						and w2.lft between w1.lft and w1.rgt
 						) """
 
-		values['warehouse'] = warehouse
+		values["warehouse"] = warehouse
 
 	if item_code:
 		conditions += " and `tabBin`.item_code = %(item_code)s"
 
-		values['item_code'] = item_code
+		values["item_code"] = item_code
 
-	query = """select sum(stock_value) from `tabBin`, `tabItem` where 1 = 1
-		and `tabItem`.name = `tabBin`.item_code and ifnull(`tabItem`.disabled, 0) = 0 %s""" % conditions
+	query = (
+		"""select sum(stock_value) from `tabBin`, `tabItem` where 1 = 1
+		and `tabItem`.name = `tabBin`.item_code and ifnull(`tabItem`.disabled, 0) = 0 %s"""
+		% conditions
+	)
 
 	stock_value = frappe.db.sql(query, values)
 
 	return stock_value
 
+
 def get_stock_value_on(warehouse=None, posting_date=None, item_code=None):
-	if not posting_date: posting_date = nowdate()
+	if not posting_date:
+		posting_date = nowdate()
 
 	values, condition = [posting_date], ""
 
@@ -63,13 +75,19 @@
 		values.append(item_code)
 		condition += " AND item_code = %s"
 
-	stock_ledger_entries = frappe.db.sql("""
+	stock_ledger_entries = frappe.db.sql(
+		"""
 		SELECT item_code, stock_value, name, warehouse
 		FROM `tabStock Ledger Entry` sle
 		WHERE posting_date <= %s {0}
 			and is_cancelled = 0
 		ORDER BY timestamp(posting_date, posting_time) DESC, creation DESC
-	""".format(condition), values, as_dict=1)
+	""".format(
+			condition
+		),
+		values,
+		as_dict=1,
+	)
 
 	sle_map = {}
 	for sle in stock_ledger_entries:
@@ -78,23 +96,32 @@
 
 	return sum(sle_map.values())
 
+
 @frappe.whitelist()
-def get_stock_balance(item_code, warehouse, posting_date=None, posting_time=None,
-	with_valuation_rate=False, with_serial_no=False):
+def get_stock_balance(
+	item_code,
+	warehouse,
+	posting_date=None,
+	posting_time=None,
+	with_valuation_rate=False,
+	with_serial_no=False,
+):
 	"""Returns stock balance quantity at given warehouse on given posting date or current date.
 
 	If `with_valuation_rate` is True, will return tuple (qty, rate)"""
 
 	from erpnext.stock.stock_ledger import get_previous_sle
 
-	if posting_date is None: posting_date = nowdate()
-	if posting_time is None: posting_time = nowtime()
+	if posting_date is None:
+		posting_date = nowdate()
+	if posting_time is None:
+		posting_time = nowtime()
 
 	args = {
 		"item_code": item_code,
-		"warehouse":warehouse,
+		"warehouse": warehouse,
 		"posting_date": posting_date,
-		"posting_time": posting_time
+		"posting_time": posting_time,
 	}
 
 	last_entry = get_previous_sle(args)
@@ -103,33 +130,40 @@
 		if with_serial_no:
 			serial_nos = get_serial_nos_data_after_transactions(args)
 
-			return ((last_entry.qty_after_transaction, last_entry.valuation_rate, serial_nos)
-				if last_entry else (0.0, 0.0, None))
+			return (
+				(last_entry.qty_after_transaction, last_entry.valuation_rate, serial_nos)
+				if last_entry
+				else (0.0, 0.0, None)
+			)
 		else:
-			return (last_entry.qty_after_transaction, last_entry.valuation_rate) if last_entry else (0.0, 0.0)
+			return (
+				(last_entry.qty_after_transaction, last_entry.valuation_rate) if last_entry else (0.0, 0.0)
+			)
 	else:
 		return last_entry.qty_after_transaction if last_entry else 0.0
 
+
 def get_serial_nos_data_after_transactions(args):
-	from pypika import CustomFunction
 
 	serial_nos = set()
 	args = frappe._dict(args)
-	sle = frappe.qb.DocType('Stock Ledger Entry')
-	Timestamp = CustomFunction('timestamp', ['date', 'time'])
+	sle = frappe.qb.DocType("Stock Ledger Entry")
 
-	stock_ledger_entries = frappe.qb.from_(
-		sle
-	).select(
-		'serial_no','actual_qty'
-	).where(
-		(sle.item_code == args.item_code)
-		& (sle.warehouse == args.warehouse)
-		& (Timestamp(sle.posting_date, sle.posting_time) < Timestamp(args.posting_date, args.posting_time))
-		& (sle.is_cancelled == 0)
-	).orderby(
-		sle.posting_date, sle.posting_time, sle.creation
-	).run(as_dict=1)
+	stock_ledger_entries = (
+		frappe.qb.from_(sle)
+		.select("serial_no", "actual_qty")
+		.where(
+			(sle.item_code == args.item_code)
+			& (sle.warehouse == args.warehouse)
+			& (
+				CombineDatetime(sle.posting_date, sle.posting_time)
+				< CombineDatetime(args.posting_date, args.posting_time)
+			)
+			& (sle.is_cancelled == 0)
+		)
+		.orderby(sle.posting_date, sle.posting_time, sle.creation)
+		.run(as_dict=1)
+	)
 
 	for stock_ledger_entry in stock_ledger_entries:
 		changed_serial_no = get_serial_nos_data(stock_ledger_entry.serial_no)
@@ -138,12 +172,15 @@
 		else:
 			serial_nos.difference_update(changed_serial_no)
 
-	return '\n'.join(serial_nos)
+	return "\n".join(serial_nos)
+
 
 def get_serial_nos_data(serial_nos):
 	from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
 	return get_serial_nos(serial_nos)
 
+
 @frappe.whitelist()
 def get_latest_stock_qty(item_code, warehouse=None):
 	values, condition = [item_code], ""
@@ -160,37 +197,48 @@
 			values.append(warehouse)
 			condition += " AND warehouse = %s"
 
-	actual_qty = frappe.db.sql("""select sum(actual_qty) from tabBin
-		where item_code=%s {0}""".format(condition), values)[0][0]
+	actual_qty = frappe.db.sql(
+		"""select sum(actual_qty) from tabBin
+		where item_code=%s {0}""".format(
+			condition
+		),
+		values,
+	)[0][0]
 
 	return actual_qty
 
 
 def get_latest_stock_balance():
 	bin_map = {}
-	for d in frappe.db.sql("""SELECT item_code, warehouse, stock_value as stock_value
-		FROM tabBin""", as_dict=1):
-			bin_map.setdefault(d.warehouse, {}).setdefault(d.item_code, flt(d.stock_value))
+	for d in frappe.db.sql(
+		"""SELECT item_code, warehouse, stock_value as stock_value
+		FROM tabBin""",
+		as_dict=1,
+	):
+		bin_map.setdefault(d.warehouse, {}).setdefault(d.item_code, flt(d.stock_value))
 
 	return bin_map
 
+
 def get_bin(item_code, warehouse):
 	bin = frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse})
 	if not bin:
 		bin_obj = _create_bin(item_code, warehouse)
 	else:
-		bin_obj = frappe.get_doc('Bin', bin, for_update=True)
+		bin_obj = frappe.get_doc("Bin", bin, for_update=True)
 	bin_obj.flags.ignore_permissions = True
 	return bin_obj
 
-def get_or_make_bin(item_code: str , warehouse: str) -> str:
-	bin_record = frappe.db.get_value('Bin', {'item_code': item_code, 'warehouse': warehouse})
+
+def get_or_make_bin(item_code: str, warehouse: str) -> str:
+	bin_record = frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse})
 
 	if not bin_record:
 		bin_obj = _create_bin(item_code, warehouse)
 		bin_record = bin_obj.name
 	return bin_record
 
+
 def _create_bin(item_code, warehouse):
 	"""Create a bin and take care of concurrent inserts."""
 
@@ -206,6 +254,7 @@
 
 	return bin_obj
 
+
 @frappe.whitelist()
 def get_incoming_rate(args, raise_error_if_no_rate=True):
 	"""Get Incoming Rate based on valuation method"""
@@ -214,19 +263,21 @@
 		get_previous_sle,
 		get_valuation_rate,
 	)
+
 	if isinstance(args, str):
 		args = json.loads(args)
 
-	voucher_no = args.get('voucher_no') or args.get('name')
+	voucher_no = args.get("voucher_no") or args.get("name")
 
 	in_rate = None
 	if (args.get("serial_no") or "").strip():
 		in_rate = get_avg_purchase_rate(args.get("serial_no"))
-	elif args.get("batch_no") and \
-			frappe.db.get_value("Batch", args.get("batch_no"), "use_batchwise_valuation", cache=True):
+	elif args.get("batch_no") and frappe.db.get_value(
+		"Batch", args.get("batch_no"), "use_batchwise_valuation", cache=True
+	):
 		in_rate = get_batch_incoming_rate(
-			item_code=args.get('item_code'),
-			warehouse=args.get('warehouse'),
+			item_code=args.get("item_code"),
+			warehouse=args.get("warehouse"),
 			batch_no=args.get("batch_no"),
 			posting_date=args.get("posting_date"),
 			posting_time=args.get("posting_time"),
@@ -234,40 +285,62 @@
 	else:
 		valuation_method = get_valuation_method(args.get("item_code"))
 		previous_sle = get_previous_sle(args)
-		if valuation_method in ('FIFO', 'LIFO'):
+		if valuation_method in ("FIFO", "LIFO"):
 			if previous_sle:
-				previous_stock_queue = json.loads(previous_sle.get('stock_queue', '[]') or '[]')
-				in_rate = _get_fifo_lifo_rate(previous_stock_queue, args.get("qty") or 0, valuation_method) if previous_stock_queue else 0
-		elif valuation_method == 'Moving Average':
-			in_rate = previous_sle.get('valuation_rate') or 0
+				previous_stock_queue = json.loads(previous_sle.get("stock_queue", "[]") or "[]")
+				in_rate = (
+					_get_fifo_lifo_rate(previous_stock_queue, args.get("qty") or 0, valuation_method)
+					if previous_stock_queue
+					else 0
+				)
+		elif valuation_method == "Moving Average":
+			in_rate = previous_sle.get("valuation_rate") or 0
 
 	if in_rate is None:
-		in_rate = get_valuation_rate(args.get('item_code'), args.get('warehouse'),
-			args.get('voucher_type'), voucher_no, args.get('allow_zero_valuation'),
-			currency=erpnext.get_company_currency(args.get('company')), company=args.get('company'),
-			raise_error_if_no_rate=raise_error_if_no_rate, batch_no=args.get("batch_no"))
+		in_rate = get_valuation_rate(
+			args.get("item_code"),
+			args.get("warehouse"),
+			args.get("voucher_type"),
+			voucher_no,
+			args.get("allow_zero_valuation"),
+			currency=erpnext.get_company_currency(args.get("company")),
+			company=args.get("company"),
+			raise_error_if_no_rate=raise_error_if_no_rate,
+			batch_no=args.get("batch_no"),
+		)
 
 	return flt(in_rate)
 
+
 def get_avg_purchase_rate(serial_nos):
 	"""get average value of serial numbers"""
 
 	serial_nos = get_valid_serial_nos(serial_nos)
-	return flt(frappe.db.sql("""select avg(purchase_rate) from `tabSerial No`
-		where name in (%s)""" % ", ".join(["%s"] * len(serial_nos)),
-		tuple(serial_nos))[0][0])
+	return flt(
+		frappe.db.sql(
+			"""select avg(purchase_rate) from `tabSerial No`
+		where name in (%s)"""
+			% ", ".join(["%s"] * len(serial_nos)),
+			tuple(serial_nos),
+		)[0][0]
+	)
+
 
 def get_valuation_method(item_code):
 	"""get valuation method from item or default"""
-	val_method = frappe.db.get_value('Item', item_code, 'valuation_method', cache=True)
+	val_method = frappe.db.get_value("Item", item_code, "valuation_method", cache=True)
 	if not val_method:
-		val_method = frappe.db.get_value("Stock Settings", None, "valuation_method", cache=True) or "FIFO"
+		val_method = (
+			frappe.db.get_value("Stock Settings", None, "valuation_method", cache=True) or "FIFO"
+		)
 	return val_method
 
+
 def get_fifo_rate(previous_stock_queue, qty):
 	"""get FIFO (average) Rate from Queue"""
 	return _get_fifo_lifo_rate(previous_stock_queue, qty, "FIFO")
 
+
 def get_lifo_rate(previous_stock_queue, qty):
 	"""get LIFO (average) Rate from Queue"""
 	return _get_fifo_lifo_rate(previous_stock_queue, qty, "LIFO")
@@ -286,10 +359,11 @@
 		total_qty, total_value = ValuationKlass(popped_bins).get_total_stock_and_value()
 		return total_value / total_qty if total_qty else 0.0
 
-def get_valid_serial_nos(sr_nos, qty=0, item_code=''):
+
+def get_valid_serial_nos(sr_nos, qty=0, item_code=""):
 	"""split serial nos, validate and return list of valid serial nos"""
 	# TODO: remove duplicates in client side
-	serial_nos = cstr(sr_nos).strip().replace(',', '\n').split('\n')
+	serial_nos = cstr(sr_nos).strip().replace(",", "\n").split("\n")
 
 	valid_serial_nos = []
 	for val in serial_nos:
@@ -305,19 +379,29 @@
 
 	return valid_serial_nos
 
+
 def validate_warehouse_company(warehouse, company):
 	warehouse_company = frappe.db.get_value("Warehouse", warehouse, "company", cache=True)
 	if warehouse_company and warehouse_company != company:
-		frappe.throw(_("Warehouse {0} does not belong to company {1}").format(warehouse, company),
-			InvalidWarehouseCompany)
+		frappe.throw(
+			_("Warehouse {0} does not belong to company {1}").format(warehouse, company),
+			InvalidWarehouseCompany,
+		)
+
 
 def is_group_warehouse(warehouse):
 	if frappe.db.get_value("Warehouse", warehouse, "is_group", cache=True):
 		frappe.throw(_("Group node warehouse is not allowed to select for transactions"))
 
+
 def validate_disabled_warehouse(warehouse):
 	if frappe.db.get_value("Warehouse", warehouse, "disabled", cache=True):
-		frappe.throw(_("Disabled Warehouse {0} cannot be used for this transaction.").format(get_link_to_form('Warehouse', warehouse)))
+		frappe.throw(
+			_("Disabled Warehouse {0} cannot be used for this transaction.").format(
+				get_link_to_form("Warehouse", warehouse)
+			)
+		)
+
 
 def update_included_uom_in_report(columns, result, include_uom, conversion_factors):
 	if not include_uom or not conversion_factors:
@@ -335,11 +419,14 @@
 			convertible_columns.setdefault(key, d.get("convertible"))
 
 			# Add new column to show qty/rate as per the selected UOM
-			columns.insert(idx+1, {
-				'label': "{0} (per {1})".format(d.get("label"), include_uom),
-				'fieldname': "{0}_{1}".format(d.get("fieldname"), frappe.scrub(include_uom)),
-				'fieldtype': 'Currency' if d.get("convertible") == 'rate' else 'Float'
-			})
+			columns.insert(
+				idx + 1,
+				{
+					"label": "{0} (per {1})".format(d.get("label"), include_uom),
+					"fieldname": "{0}_{1}".format(d.get("fieldname"), frappe.scrub(include_uom)),
+					"fieldtype": "Currency" if d.get("convertible") == "rate" else "Float",
+				},
+			)
 
 	update_dict_values = []
 	for row_idx, row in enumerate(result):
@@ -351,13 +438,13 @@
 			if not conversion_factors[row_idx]:
 				conversion_factors[row_idx] = 1
 
-			if convertible_columns.get(key) == 'rate':
+			if convertible_columns.get(key) == "rate":
 				new_value = flt(value) * conversion_factors[row_idx]
 			else:
 				new_value = flt(value) / conversion_factors[row_idx]
 
 			if not is_dict_obj:
-				row.insert(key+1, new_value)
+				row.insert(key + 1, new_value)
 			else:
 				new_key = "{0}_{1}".format(key, frappe.scrub(include_uom))
 				update_dict_values.append([row, new_key, new_value])
@@ -366,11 +453,17 @@
 		row, key, value = data
 		row[key] = value
 
+
 def get_available_serial_nos(args):
-	return frappe.db.sql(""" SELECT name from `tabSerial No`
+	return frappe.db.sql(
+		""" SELECT name from `tabSerial No`
 		WHERE item_code = %(item_code)s and warehouse = %(warehouse)s
 		 and timestamp(purchase_date, purchase_time) <= timestamp(%(posting_date)s, %(posting_time)s)
-	""", args, as_dict=1)
+	""",
+		args,
+		as_dict=1,
+	)
+
 
 def add_additional_uom_columns(columns, result, include_uom, conversion_factors):
 	if not include_uom or not conversion_factors:
@@ -379,60 +472,54 @@
 	convertible_column_map = {}
 	for col_idx in list(reversed(range(0, len(columns)))):
 		col = columns[col_idx]
-		if isinstance(col, dict) and col.get('convertible') in ['rate', 'qty']:
+		if isinstance(col, dict) and col.get("convertible") in ["rate", "qty"]:
 			next_col = col_idx + 1
 			columns.insert(next_col, col.copy())
-			columns[next_col]['fieldname'] += '_alt'
-			convertible_column_map[col.get('fieldname')] = frappe._dict({
-				'converted_col': columns[next_col]['fieldname'],
-				'for_type': col.get('convertible')
-			})
-			if col.get('convertible') == 'rate':
-				columns[next_col]['label'] += ' (per {})'.format(include_uom)
+			columns[next_col]["fieldname"] += "_alt"
+			convertible_column_map[col.get("fieldname")] = frappe._dict(
+				{"converted_col": columns[next_col]["fieldname"], "for_type": col.get("convertible")}
+			)
+			if col.get("convertible") == "rate":
+				columns[next_col]["label"] += " (per {})".format(include_uom)
 			else:
-				columns[next_col]['label'] += ' ({})'.format(include_uom)
+				columns[next_col]["label"] += " ({})".format(include_uom)
 
 	for row_idx, row in enumerate(result):
 		for convertible_col, data in convertible_column_map.items():
-			conversion_factor = conversion_factors[row.get('item_code')] or 1
+			conversion_factor = conversion_factors[row.get("item_code")] or 1
 			for_type = data.for_type
 			value_before_conversion = row.get(convertible_col)
-			if for_type == 'rate':
+			if for_type == "rate":
 				row[data.converted_col] = flt(value_before_conversion) * conversion_factor
 			else:
 				row[data.converted_col] = flt(value_before_conversion) / conversion_factor
 
 		result[row_idx] = row
 
+
 def get_incoming_outgoing_rate_for_cancel(item_code, voucher_type, voucher_no, voucher_detail_no):
-	outgoing_rate = frappe.db.sql("""SELECT abs(stock_value_difference / actual_qty)
+	outgoing_rate = frappe.db.sql(
+		"""SELECT CASE WHEN actual_qty = 0 THEN 0 ELSE abs(stock_value_difference / actual_qty) END
 		FROM `tabStock Ledger Entry`
 		WHERE voucher_type = %s and voucher_no = %s
 			and item_code = %s and voucher_detail_no = %s
 			ORDER BY CREATION DESC limit 1""",
-		(voucher_type, voucher_no, item_code, voucher_detail_no))
+		(voucher_type, voucher_no, item_code, voucher_detail_no),
+	)
 
 	outgoing_rate = outgoing_rate[0][0] if outgoing_rate else 0.0
 
 	return outgoing_rate
 
+
 def is_reposting_item_valuation_in_progress():
-	reposting_in_progress = frappe.db.exists("Repost Item Valuation",
-		{'docstatus': 1, 'status': ['in', ['Queued','In Progress']]})
+	reposting_in_progress = frappe.db.exists(
+		"Repost Item Valuation", {"docstatus": 1, "status": ["in", ["Queued", "In Progress"]]}
+	)
 	if reposting_in_progress:
-		frappe.msgprint(_("Item valuation reposting in progress. Report might show incorrect item valuation."), alert=1)
-
-
-def calculate_mapped_packed_items_return(return_doc):
-	parent_items = set([item.parent_item for item in return_doc.packed_items])
-	against_doc = frappe.get_doc(return_doc.doctype, return_doc.return_against)
-
-	for original_bundle, returned_bundle in zip(against_doc.items, return_doc.items):
-		if original_bundle.item_code in parent_items:
-			for returned_packed_item, original_packed_item in zip(return_doc.packed_items, against_doc.packed_items):
-				if returned_packed_item.parent_item == original_bundle.item_code:
-					returned_packed_item.parent_detail_docname = returned_bundle.name
-					returned_packed_item.qty = (original_packed_item.qty / original_bundle.qty) * returned_bundle.qty
+		frappe.msgprint(
+			_("Item valuation reposting in progress. Report might show incorrect item valuation."), alert=1
+		)
 
 
 def check_pending_reposting(posting_date: str, throw_error: bool = True) -> bool:
@@ -440,22 +527,73 @@
 
 	filters = {
 		"docstatus": 1,
-		"status": ["in", ["Queued","In Progress", "Failed"]],
+		"status": ["in", ["Queued", "In Progress"]],
 		"posting_date": ["<=", posting_date],
 	}
 
-	reposting_pending =  frappe.db.exists("Repost Item Valuation", filters)
+	reposting_pending = frappe.db.exists("Repost Item Valuation", filters)
 	if reposting_pending and throw_error:
-		msg = _("Stock/Accounts can not be frozen as processing of backdated entries is going on. Please try again later.")
-		frappe.msgprint(msg,
-				raise_exception=PendingRepostingError,
-				title="Stock Reposting Ongoing",
-				indicator="red",
-				primary_action={
-					"label": _("Show pending entries"),
-					"client_action": "erpnext.route_to_pending_reposts",
-					"args": filters,
-				}
-			)
+		msg = _(
+			"Stock/Accounts can not be frozen as processing of backdated entries is going on. Please try again later."
+		)
+		frappe.msgprint(
+			msg,
+			raise_exception=PendingRepostingError,
+			title="Stock Reposting Ongoing",
+			indicator="red",
+			primary_action={
+				"label": _("Show pending entries"),
+				"client_action": "erpnext.route_to_pending_reposts",
+				"args": filters,
+			},
+		)
 
 	return bool(reposting_pending)
+
+
+@frappe.whitelist()
+def scan_barcode(search_value: str) -> Dict[str, Optional[str]]:
+
+	# search barcode no
+	barcode_data = frappe.db.get_value(
+		"Item Barcode",
+		{"barcode": search_value},
+		["barcode", "parent as item_code", "uom"],
+		as_dict=True,
+	)
+	if barcode_data:
+		return _update_item_info(barcode_data)
+
+	# search serial no
+	serial_no_data = frappe.db.get_value(
+		"Serial No",
+		search_value,
+		["name as serial_no", "item_code", "batch_no"],
+		as_dict=True,
+	)
+	if serial_no_data:
+		return _update_item_info(serial_no_data)
+
+	# search batch no
+	batch_no_data = frappe.db.get_value(
+		"Batch",
+		search_value,
+		["name as batch_no", "item as item_code"],
+		as_dict=True,
+	)
+	if batch_no_data:
+		return _update_item_info(batch_no_data)
+
+	return {}
+
+
+def _update_item_info(scan_result: Dict[str, Optional[str]]) -> Dict[str, Optional[str]]:
+	if item_code := scan_result.get("item_code"):
+		if item_info := frappe.get_cached_value(
+			"Item",
+			item_code,
+			["has_batch_no", "has_serial_no"],
+			as_dict=True,
+		):
+			scan_result.update(item_info)
+	return scan_result
diff --git a/erpnext/stock/valuation.py b/erpnext/stock/valuation.py
index e2bd1ad..35f4f12 100644
--- a/erpnext/stock/valuation.py
+++ b/erpnext/stock/valuation.py
@@ -11,7 +11,6 @@
 
 
 class BinWiseValuation(ABC):
-
 	@abstractmethod
 	def add_stock(self, qty: float, rate: float) -> None:
 		pass
@@ -61,7 +60,7 @@
 
 	# specifying the attributes to save resources
 	# ref: https://docs.python.org/3/reference/datamodel.html#slots
-	__slots__ = ["queue",]
+	__slots__ = ["queue"]
 
 	def __init__(self, state: Optional[List[StockBin]]):
 		self.queue: List[StockBin] = state if state is not None else []
@@ -74,9 +73,9 @@
 	def add_stock(self, qty: float, rate: float) -> None:
 		"""Update fifo queue with new stock.
 
-			args:
-				qty: new quantity to add
-				rate: incoming rate of new quantity"""
+		args:
+		        qty: new quantity to add
+		        rate: incoming rate of new quantity"""
 
 		if not len(self.queue):
 			self.queue.append([0, 0])
@@ -101,12 +100,12 @@
 		"""Remove stock from the queue and return popped bins.
 
 		args:
-			qty: quantity to remove
-			rate: outgoing rate
-			rate_generator: function to be called if queue is not found and rate is required.
+		        qty: quantity to remove
+		        rate: outgoing rate
+		        rate_generator: function to be called if queue is not found and rate is required.
 		"""
 		if not rate_generator:
-			rate_generator = lambda : 0.0  # noqa
+			rate_generator = lambda: 0.0  # noqa
 
 		consumed_bins = []
 		while qty:
@@ -122,13 +121,9 @@
 						index = idx
 						break
 
-				# If no entry found with outgoing rate, collapse queue
+				# If no entry found with outgoing rate, consume as per FIFO
 				if index is None:  # nosemgrep
-					new_stock_value = sum(d[QTY] * d[RATE] for d in self.queue) - qty * outgoing_rate
-					new_stock_qty = sum(d[QTY] for d in self.queue) - qty
-					self.queue = [[new_stock_qty, new_stock_value / new_stock_qty if new_stock_qty > 0 else outgoing_rate]]
-					consumed_bins.append([qty, outgoing_rate])
-					break
+					index = 0
 			else:
 				index = 0
 
@@ -169,7 +164,7 @@
 
 	# specifying the attributes to save resources
 	# ref: https://docs.python.org/3/reference/datamodel.html#slots
-	__slots__ = ["stack",]
+	__slots__ = ["stack"]
 
 	def __init__(self, state: Optional[List[StockBin]]):
 		self.stack: List[StockBin] = state if state is not None else []
@@ -182,11 +177,11 @@
 	def add_stock(self, qty: float, rate: float) -> None:
 		"""Update lifo stack with new stock.
 
-			args:
-				qty: new quantity to add
-				rate: incoming rate of new quantity.
+		args:
+		        qty: new quantity to add
+		        rate: incoming rate of new quantity.
 
-			Behaviour of this is same as FIFO valuation.
+		Behaviour of this is same as FIFO valuation.
 		"""
 		if not len(self.stack):
 			self.stack.append([0, 0])
@@ -205,19 +200,18 @@
 				else:  # new balance qty is still negative, maintain same rate
 					self.stack[-1][QTY] = qty
 
-
 	def remove_stock(
 		self, qty: float, outgoing_rate: float = 0.0, rate_generator: Callable[[], float] = None
 	) -> List[StockBin]:
 		"""Remove stock from the stack and return popped bins.
 
 		args:
-			qty: quantity to remove
-			rate: outgoing rate - ignored. Kept for backwards compatibility.
-			rate_generator: function to be called if stack is not found and rate is required.
+		        qty: quantity to remove
+		        rate: outgoing rate - ignored. Kept for backwards compatibility.
+		        rate_generator: function to be called if stack is not found and rate is required.
 		"""
 		if not rate_generator:
-			rate_generator = lambda : 0.0  # noqa
+			rate_generator = lambda: 0.0  # noqa
 
 		consumed_bins = []
 		while qty:
@@ -254,7 +248,7 @@
 	"""Rounds off the number to zero only if number is close to zero for decimal
 	specified in precision. Precision defaults to 7.
 	"""
-	if abs(0.0 - flt(number)) < (1.0 / (10 ** precision)):
+	if abs(0.0 - flt(number)) < (1.0 / (10**precision)):
 		return 0.0
 
 	return flt(number)
diff --git a/erpnext/support/__init__.py b/erpnext/support/__init__.py
index ac23ede..7b6845d 100644
--- a/erpnext/support/__init__.py
+++ b/erpnext/support/__init__.py
@@ -1,5 +1,5 @@
 install_docs = [
-	{'doctype':'Role', 'role_name':'Support Team', 'name':'Support Team'},
-	{'doctype':'Role', 'role_name':'Maintenance User', 'name':'Maintenance User'},
-	{'doctype':'Role', 'role_name':'Maintenance Manager', 'name':'Maintenance Manager'}
+	{"doctype": "Role", "role_name": "Support Team", "name": "Support Team"},
+	{"doctype": "Role", "role_name": "Maintenance User", "name": "Maintenance User"},
+	{"doctype": "Role", "role_name": "Maintenance Manager", "name": "Maintenance Manager"},
 ]
diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py
index e211e24..7f3e0cf 100644
--- a/erpnext/support/doctype/issue/issue.py
+++ b/erpnext/support/doctype/issue/issue.py
@@ -11,6 +11,8 @@
 from frappe.email.inbox import link_communication_to_document
 from frappe.model.document import Document
 from frappe.model.mapper import get_mapped_doc
+from frappe.query_builder import Interval
+from frappe.query_builder.functions import Now
 from frappe.utils import date_diff, get_datetime, now_datetime, time_diff_in_seconds
 from frappe.utils.user import is_website_user
 
@@ -50,23 +52,26 @@
 					self.customer = contact.get_link_for("Customer")
 
 			if not self.company:
-				self.company = frappe.db.get_value("Lead", self.lead, "company") or \
-					frappe.db.get_default("Company")
+				self.company = frappe.db.get_value("Lead", self.lead, "company") or frappe.db.get_default(
+					"Company"
+				)
 
 	def create_communication(self):
 		communication = frappe.new_doc("Communication")
-		communication.update({
-			"communication_type": "Communication",
-			"communication_medium": "Email",
-			"sent_or_received": "Received",
-			"email_status": "Open",
-			"subject": self.subject,
-			"sender": self.raised_by,
-			"content": self.description,
-			"status": "Linked",
-			"reference_doctype": "Issue",
-			"reference_name": self.name
-		})
+		communication.update(
+			{
+				"communication_type": "Communication",
+				"communication_medium": "Email",
+				"sent_or_received": "Received",
+				"email_status": "Open",
+				"subject": self.subject,
+				"sender": self.raised_by,
+				"content": self.description,
+				"status": "Linked",
+				"reference_doctype": "Issue",
+				"reference_name": self.name,
+			}
+		)
 		communication.ignore_permissions = True
 		communication.ignore_mandatory = True
 		communication.save()
@@ -97,23 +102,31 @@
 		# Replicate linked Communications
 		# TODO: get all communications in timeline before this, and modify them to append them to new doc
 		comm_to_split_from = frappe.get_doc("Communication", communication_id)
-		communications = frappe.get_all("Communication",
-			filters={"reference_doctype": "Issue",
+		communications = frappe.get_all(
+			"Communication",
+			filters={
+				"reference_doctype": "Issue",
 				"reference_name": comm_to_split_from.reference_name,
-				"creation": (">=", comm_to_split_from.creation)})
+				"creation": (">=", comm_to_split_from.creation),
+			},
+		)
 
 		for communication in communications:
 			doc = frappe.get_doc("Communication", communication.name)
 			doc.reference_name = replicated_issue.name
 			doc.save(ignore_permissions=True)
 
-		frappe.get_doc({
-			"doctype": "Comment",
-			"comment_type": "Info",
-			"reference_doctype": "Issue",
-			"reference_name": replicated_issue.name,
-			"content": " - Split the Issue from <a href='/app/Form/Issue/{0}'>{1}</a>".format(self.name, frappe.bold(self.name)),
-		}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "Comment",
+				"comment_type": "Info",
+				"reference_doctype": "Issue",
+				"reference_name": replicated_issue.name,
+				"content": " - Split the Issue from <a href='/app/Form/Issue/{0}'>{1}</a>".format(
+					self.name, frappe.bold(self.name)
+				),
+			}
+		).insert(ignore_permissions=True)
 
 		return replicated_issue.name
 
@@ -121,6 +134,7 @@
 		self.db_set("resolution_time", None)
 		self.db_set("user_resolution_time", None)
 
+
 def get_list_context(context=None):
 	return {
 		"title": _("Issues"),
@@ -128,7 +142,7 @@
 		"row_template": "templates/includes/issue_row.html",
 		"show_sidebar": True,
 		"show_search": True,
-		"no_breadcrumbs": True
+		"no_breadcrumbs": True,
 	}
 
 
@@ -145,7 +159,8 @@
 
 	ignore_permissions = False
 	if is_website_user():
-		if not filters: filters = {}
+		if not filters:
+			filters = {}
 
 		if customer:
 			filters["customer"] = customer
@@ -154,7 +169,9 @@
 
 		ignore_permissions = True
 
-	return get_list(doctype, txt, filters, limit_start, limit_page_length, ignore_permissions=ignore_permissions)
+	return get_list(
+		doctype, txt, filters, limit_start, limit_page_length, ignore_permissions=ignore_permissions
+	)
 
 
 @frappe.whitelist()
@@ -163,90 +180,115 @@
 	for name in json.loads(names):
 		frappe.db.set_value("Issue", name, "status", status)
 
+
 @frappe.whitelist()
 def set_status(name, status):
 	frappe.db.set_value("Issue", name, "status", status)
 
+
 def auto_close_tickets():
 	"""Auto-close replied support tickets after 7 days"""
-	auto_close_after_days = frappe.db.get_value("Support Settings", "Support Settings", "close_issue_after_days") or 7
+	auto_close_after_days = (
+		frappe.db.get_value("Support Settings", "Support Settings", "close_issue_after_days") or 7
+	)
 
-	issues = frappe.db.sql(""" select name from tabIssue where status='Replied' and
-		modified<DATE_SUB(CURDATE(), INTERVAL %s DAY) """, (auto_close_after_days), as_dict=True)
+	table = frappe.qb.DocType("Issue")
+	issues = (
+		frappe.qb.from_(table)
+		.select(table.name)
+		.where(
+			(table.modified < (Now() - Interval(days=auto_close_after_days))) & (table.status == "Replied")
+		)
+	).run(pluck=True)
 
 	for issue in issues:
-		doc = frappe.get_doc("Issue", issue.get("name"))
+		doc = frappe.get_doc("Issue", issue)
 		doc.status = "Closed"
 		doc.flags.ignore_permissions = True
 		doc.flags.ignore_mandatory = True
 		doc.save()
 
+
 def has_website_permission(doc, ptype, user, verbose=False):
 	from erpnext.controllers.website_list_for_contact import has_website_permission
+
 	permission_based_on_customer = has_website_permission(doc, ptype, user, verbose)
 
-	return permission_based_on_customer or doc.raised_by==user
+	return permission_based_on_customer or doc.raised_by == user
+
 
 def update_issue(contact, method):
 	"""Called when Contact is deleted"""
 	frappe.db.sql("""UPDATE `tabIssue` set contact='' where contact=%s""", contact.name)
 
+
 @frappe.whitelist()
 def make_task(source_name, target_doc=None):
-	return get_mapped_doc("Issue", source_name, {
-		"Issue": {
-			"doctype": "Task"
-		}
-	}, target_doc)
+	return get_mapped_doc("Issue", source_name, {"Issue": {"doctype": "Task"}}, target_doc)
+
 
 @frappe.whitelist()
 def make_issue_from_communication(communication, ignore_communication_links=False):
-	""" raise a issue from email """
+	"""raise a issue from email"""
 
 	doc = frappe.get_doc("Communication", communication)
-	issue = frappe.get_doc({
-		"doctype": "Issue",
-		"subject": doc.subject,
-		"communication_medium": doc.communication_medium,
-		"raised_by": doc.sender or "",
-		"raised_by_phone": doc.phone_no or ""
-	}).insert(ignore_permissions=True)
+	issue = frappe.get_doc(
+		{
+			"doctype": "Issue",
+			"subject": doc.subject,
+			"communication_medium": doc.communication_medium,
+			"raised_by": doc.sender or "",
+			"raised_by_phone": doc.phone_no or "",
+		}
+	).insert(ignore_permissions=True)
 
 	link_communication_to_document(doc, "Issue", issue.name, ignore_communication_links)
 
 	return issue.name
 
+
 def get_time_in_timedelta(time):
 	"""
-		Converts datetime.time(10, 36, 55, 961454) to datetime.timedelta(seconds=38215)
+	Converts datetime.time(10, 36, 55, 961454) to datetime.timedelta(seconds=38215)
 	"""
 	return timedelta(hours=time.hour, minutes=time.minute, seconds=time.second)
 
+
 def set_first_response_time(communication, method):
-	if communication.get('reference_doctype') == "Issue":
+	if communication.get("reference_doctype") == "Issue":
 		issue = get_parent_doc(communication)
 		if is_first_response(issue) and issue.service_level_agreement:
-			first_response_time = calculate_first_response_time(issue, get_datetime(issue.first_responded_on))
+			first_response_time = calculate_first_response_time(
+				issue, get_datetime(issue.first_responded_on)
+			)
 			issue.db_set("first_response_time", first_response_time)
 
+
 def is_first_response(issue):
-	responses = frappe.get_all('Communication', filters = {'reference_name': issue.name, 'sent_or_received': 'Sent'})
+	responses = frappe.get_all(
+		"Communication", filters={"reference_name": issue.name, "sent_or_received": "Sent"}
+	)
 	if len(responses) == 1:
 		return True
 	return False
 
+
 def calculate_first_response_time(issue, first_responded_on):
 	issue_creation_date = issue.service_level_agreement_creation or issue.creation
 	issue_creation_time = get_time_in_seconds(issue_creation_date)
 	first_responded_on_in_seconds = get_time_in_seconds(first_responded_on)
-	support_hours = frappe.get_cached_doc("Service Level Agreement", issue.service_level_agreement).support_and_resolution
+	support_hours = frappe.get_cached_doc(
+		"Service Level Agreement", issue.service_level_agreement
+	).support_and_resolution
 
 	if issue_creation_date.day == first_responded_on.day:
 		if is_work_day(issue_creation_date, support_hours):
 			start_time, end_time = get_working_hours(issue_creation_date, support_hours)
 
 			# issue creation and response on the same day during working hours
-			if is_during_working_hours(issue_creation_date, support_hours) and is_during_working_hours(first_responded_on, support_hours):
+			if is_during_working_hours(issue_creation_date, support_hours) and is_during_working_hours(
+				first_responded_on, support_hours
+			):
 				return get_elapsed_time(issue_creation_date, first_responded_on)
 
 			# issue creation is during working hours, but first response was after working hours
@@ -259,7 +301,7 @@
 
 			# both issue creation and first response were after working hours
 			else:
-				return 1.0		# this should ideally be zero, but it gets reset when the next response is sent if the value is zero
+				return 1.0  # this should ideally be zero, but it gets reset when the next response is sent if the value is zero
 
 		else:
 			return 1.0
@@ -269,7 +311,9 @@
 		if date_diff(first_responded_on, issue_creation_date) == 1:
 			first_response_time = 0
 		else:
-			first_response_time = calculate_initial_frt(issue_creation_date, date_diff(first_responded_on, issue_creation_date)- 1, support_hours)
+			first_response_time = calculate_initial_frt(
+				issue_creation_date, date_diff(first_responded_on, issue_creation_date) - 1, support_hours
+			)
 
 		# time taken on day of issue creation
 		if is_work_day(issue_creation_date, support_hours):
@@ -294,9 +338,11 @@
 		else:
 			return 1.0
 
+
 def get_time_in_seconds(date):
 	return timedelta(hours=date.hour, minutes=date.minute, seconds=date.second)
 
+
 def get_working_hours(date, support_hours):
 	if is_work_day(date, support_hours):
 		weekday = frappe.utils.get_weekday(date)
@@ -304,6 +350,7 @@
 			if day.workday == weekday:
 				return day.start_time, day.end_time
 
+
 def is_work_day(date, support_hours):
 	weekday = frappe.utils.get_weekday(date)
 	for day in support_hours:
@@ -311,6 +358,7 @@
 			return True
 	return False
 
+
 def is_during_working_hours(date, support_hours):
 	start_time, end_time = get_working_hours(date, support_hours)
 	time = get_time_in_seconds(date)
@@ -318,19 +366,22 @@
 		return True
 	return False
 
+
 def get_elapsed_time(start_time, end_time):
 	return round(time_diff_in_seconds(end_time, start_time), 2)
 
+
 def calculate_initial_frt(issue_creation_date, days_in_between, support_hours):
 	initial_frt = 0
 	for i in range(days_in_between):
-		date = issue_creation_date + timedelta(days = (i+1))
+		date = issue_creation_date + timedelta(days=(i + 1))
 		if is_work_day(date, support_hours):
 			start_time, end_time = get_working_hours(date, support_hours)
 			initial_frt += get_elapsed_time(start_time, end_time)
 
 	return initial_frt
 
+
 def is_before_working_hours(date, support_hours):
 	start_time, end_time = get_working_hours(date, support_hours)
 	time = get_time_in_seconds(date)
@@ -338,6 +389,7 @@
 		return True
 	return False
 
+
 def get_holidays(holiday_list_name):
 	holiday_list = frappe.get_cached_doc("Holiday List", holiday_list_name)
 	holidays = [holiday.holiday_date for holiday in holiday_list.holidays]
diff --git a/erpnext/support/doctype/issue/issue_dashboard.py b/erpnext/support/doctype/issue/issue_dashboard.py
index 7ab358a..1a53be9 100644
--- a/erpnext/support/doctype/issue/issue_dashboard.py
+++ b/erpnext/support/doctype/issue/issue_dashboard.py
@@ -2,12 +2,4 @@
 
 
 def get_data():
-	return {
-		'fieldname': 'issue',
-		'transactions': [
-			{
-				'label': _('Activity'),
-				'items': ['Task']
-			}
-		]
-	}
+	return {"fieldname": "issue", "transactions": [{"label": _("Activity"), "items": ["Task"]}]}
diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py
index 7a0a5e5..a440124 100644
--- a/erpnext/support/doctype/issue/test_issue.py
+++ b/erpnext/support/doctype/issue/test_issue.py
@@ -23,6 +23,7 @@
 		frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1)
 		create_service_level_agreements_for_issues()
 
+
 class TestIssue(TestSetUp):
 	def test_response_time_and_resolution_time_based_on_different_sla(self):
 		creation = get_datetime("2019-03-04 12:00")
@@ -41,9 +42,10 @@
 		self.assertEqual(issue.response_by, get_datetime("2019-03-04 14:00"))
 		self.assertEqual(issue.resolution_by, get_datetime("2019-03-04 15:00"))
 
-
 		# make issue with territory specific SLA
-		customer = create_customer("___Test Customer", "__Test SLA Customer Group", "_Test SLA Territory")
+		customer = create_customer(
+			"___Test Customer", "__Test SLA Customer Group", "_Test SLA Territory"
+		)
 		issue = make_issue(creation, "___Test Customer", 3)
 
 		self.assertEqual(issue.response_by, get_datetime("2019-03-04 14:00"))
@@ -78,10 +80,10 @@
 
 		frappe.flags.current_time = get_datetime("2019-03-04 15:00")
 		issue.reload()
-		issue.status = 'Closed'
+		issue.status = "Closed"
 		issue.save()
 
-		self.assertEqual(issue.agreement_status, 'Fulfilled')
+		self.assertEqual(issue.agreement_status, "Fulfilled")
 
 	def test_hold_time_on_replied(self):
 		creation = get_datetime("2020-03-04 4:00")
@@ -94,7 +96,7 @@
 
 		frappe.flags.current_time = get_datetime("2020-03-04 4:15")
 		issue.reload()
-		issue.status = 'Replied'
+		issue.status = "Replied"
 		issue.save()
 
 		self.assertEqual(issue.on_hold_since, frappe.flags.current_time)
@@ -113,7 +115,7 @@
 
 		frappe.flags.current_time = get_datetime("2020-03-04 5:05")
 		issue.reload()
-		issue.status = 'Closed'
+		issue.status = "Closed"
 		issue.save()
 
 		issue.reload()
@@ -130,27 +132,29 @@
 		create_communication(issue.name, "test@admin.com", "Sent", frappe.flags.current_time)
 
 		issue.reload()
-		issue.status = 'Replied'
+		issue.status = "Replied"
 		issue.save()
 
 		self.assertEqual(issue.on_hold_since, frappe.flags.current_time)
 
 		# close the issue after being on hold for 20 days
 		frappe.flags.current_time = get_datetime("2021-11-22 01:00")
-		issue.status = 'Closed'
+		issue.status = "Closed"
 		issue.save()
 
-		self.assertEqual(issue.resolution_by, get_datetime('2021-11-22 06:00:00'))
-		self.assertEqual(issue.resolution_date, get_datetime('2021-11-22 01:00:00'))
-		self.assertEqual(issue.agreement_status, 'Fulfilled')
+		self.assertEqual(issue.resolution_by, get_datetime("2021-11-22 06:00:00"))
+		self.assertEqual(issue.resolution_date, get_datetime("2021-11-22 01:00:00"))
+		self.assertEqual(issue.agreement_status, "Fulfilled")
 
 	def test_issue_open_after_closed(self):
 
 		# Created on -> 1 pm, Response Time -> 4 hrs, Resolution Time -> 6 hrs
 		frappe.flags.current_time = get_datetime("2021-11-01 13:00")
-		issue = make_issue(frappe.flags.current_time, index=1, issue_type='Critical') # Applies 24hr working time SLA
+		issue = make_issue(
+			frappe.flags.current_time, index=1, issue_type="Critical"
+		)  # Applies 24hr working time SLA
 		create_communication(issue.name, "test@example.com", "Received", frappe.flags.current_time)
-		self.assertEquals(issue.agreement_status, 'First Response Due')
+		self.assertEquals(issue.agreement_status, "First Response Due")
 		self.assertEquals(issue.response_by, get_datetime("2021-11-01 17:00"))
 		self.assertEquals(issue.resolution_by, get_datetime("2021-11-01 19:00"))
 
@@ -158,9 +162,9 @@
 		frappe.flags.current_time = get_datetime("2021-11-01 14:00")
 		create_communication(issue.name, "test@admin.com", "Sent", frappe.flags.current_time)
 		issue.reload()
-		issue.status = 'Replied'
+		issue.status = "Replied"
 		issue.save()
-		self.assertEquals(issue.agreement_status, 'Resolution Due')
+		self.assertEquals(issue.agreement_status, "Resolution Due")
 		self.assertEquals(issue.on_hold_since, frappe.flags.current_time)
 		self.assertEquals(issue.first_responded_on, frappe.flags.current_time)
 
@@ -168,7 +172,7 @@
 		frappe.flags.current_time = get_datetime("2021-11-01 15:00")
 		create_communication(issue.name, "test@example.com", "Received", frappe.flags.current_time)
 		issue.reload()
-		self.assertEquals(issue.status, 'Open')
+		self.assertEquals(issue.status, "Open")
 		# Hold Time + 1 Hrs
 		self.assertEquals(issue.total_hold_time, 3600)
 		# Resolution By should increase by one hrs
@@ -178,19 +182,19 @@
 		frappe.flags.current_time = get_datetime("2021-11-01 16:00")
 		create_communication(issue.name, "test@admin.com", "Sent", frappe.flags.current_time)
 		issue.reload()
-		issue.status = 'Replied'
+		issue.status = "Replied"
 		issue.save()
-		self.assertEquals(issue.agreement_status, 'Resolution Due')
+		self.assertEquals(issue.agreement_status, "Resolution Due")
 
 		# Customer Closed → 10 pm
 		frappe.flags.current_time = get_datetime("2021-11-01 22:00")
-		issue.status = 'Closed'
+		issue.status = "Closed"
 		issue.save()
 		# Hold Time + 6 Hrs
 		self.assertEquals(issue.total_hold_time, 3600 + 21600)
 		# Resolution By should increase by 6 hrs
 		self.assertEquals(issue.resolution_by, get_datetime("2021-11-02 02:00"))
-		self.assertEquals(issue.agreement_status, 'Fulfilled')
+		self.assertEquals(issue.agreement_status, "Fulfilled")
 		self.assertEquals(issue.resolution_date, frappe.flags.current_time)
 
 		# Customer Open → 3 am i.e after resolution by is crossed
@@ -201,15 +205,15 @@
 		self.assertEquals(issue.total_hold_time, 3600 + 21600 + 18000)
 		# Resolution By should increase by 5 hrs
 		self.assertEquals(issue.resolution_by, get_datetime("2021-11-02 07:00"))
-		self.assertEquals(issue.agreement_status, 'Resolution Due')
+		self.assertEquals(issue.agreement_status, "Resolution Due")
 		self.assertFalse(issue.resolution_date)
 
 		# We Closed → 4 am, SLA should be Fulfilled
 		frappe.flags.current_time = get_datetime("2021-11-02 04:00")
-		issue.status = 'Closed'
+		issue.status = "Closed"
 		issue.save()
 		self.assertEquals(issue.resolution_by, get_datetime("2021-11-02 07:00"))
-		self.assertEquals(issue.agreement_status, 'Fulfilled')
+		self.assertEquals(issue.agreement_status, "Fulfilled")
 		self.assertEquals(issue.resolution_date, frappe.flags.current_time)
 
 	def test_recording_of_assignment_on_first_reponse_failure(self):
@@ -219,11 +223,7 @@
 
 		issue = make_issue(frappe.flags.current_time, index=1)
 		create_communication(issue.name, "test@example.com", "Received", frappe.flags.current_time)
-		add_assignment({
-			'doctype': issue.doctype,
-			'name': issue.name,
-			'assign_to': ['test@admin.com']
-		})
+		add_assignment({"doctype": issue.doctype, "name": issue.name, "assign_to": ["test@admin.com"]})
 		issue.reload()
 
 		# send a reply failing response SLA
@@ -232,12 +232,15 @@
 
 		# assert if a new timeline item has been added
 		# to record the assignment
-		comment = frappe.db.exists('Comment', {
-			'reference_doctype': 'Issue',
-			'reference_name': issue.name,
-			'comment_type': 'Assigned',
-			'content': _('First Response SLA Failed by {}').format('test')
-		})
+		comment = frappe.db.exists(
+			"Comment",
+			{
+				"reference_doctype": "Issue",
+				"reference_name": issue.name,
+				"comment_type": "Assigned",
+				"content": _("First Response SLA Failed by {}").format("test"),
+			},
+		)
 		self.assertTrue(comment)
 
 	def test_agreement_status_on_response(self):
@@ -245,7 +248,7 @@
 
 		issue = make_issue(frappe.flags.current_time, index=1)
 		create_communication(issue.name, "test@example.com", "Received", frappe.flags.current_time)
-		self.assertTrue(issue.status == 'Open')
+		self.assertTrue(issue.status == "Open")
 
 		# send a reply within response SLA
 		frappe.flags.current_time = get_datetime("2021-11-02 11:00")
@@ -253,7 +256,8 @@
 
 		issue.reload()
 		self.assertEquals(issue.first_responded_on, frappe.flags.current_time)
-		self.assertEquals(issue.agreement_status, 'Resolution Due')
+		self.assertEquals(issue.agreement_status, "Resolution Due")
+
 
 class TestFirstResponseTime(TestSetUp):
 	# working hours used in all cases: Mon-Fri, 10am to 6pm
@@ -262,209 +266,268 @@
 	# issue creation and first response are on the same day
 	def test_first_response_time_case1(self):
 		"""
-			Test frt when issue creation and first response are during working hours on the same day.
+		Test frt when issue creation and first response are during working hours on the same day.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 11:00"), get_datetime("06-28-2021 12:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 11:00"), get_datetime("06-28-2021 12:00")
+		)
 		self.assertEqual(issue.first_response_time, 3600.0)
 
 	def test_first_response_time_case2(self):
 		"""
-			Test frt when issue creation was during working hours, but first response is sent after working hours on the same day.
+		Test frt when issue creation was during working hours, but first response is sent after working hours on the same day.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 12:00"), get_datetime("06-28-2021 20:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 12:00"), get_datetime("06-28-2021 20:00")
+		)
 		self.assertEqual(issue.first_response_time, 21600.0)
 
 	def test_first_response_time_case3(self):
 		"""
-			Test frt when issue creation was before working hours but first response is sent during working hours on the same day.
+		Test frt when issue creation was before working hours but first response is sent during working hours on the same day.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 6:00"), get_datetime("06-28-2021 12:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 6:00"), get_datetime("06-28-2021 12:00")
+		)
 		self.assertEqual(issue.first_response_time, 7200.0)
 
 	def test_first_response_time_case4(self):
 		"""
-			Test frt when both issue creation and first response were after working hours on the same day.
+		Test frt when both issue creation and first response were after working hours on the same day.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 19:00"), get_datetime("06-28-2021 20:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 19:00"), get_datetime("06-28-2021 20:00")
+		)
 		self.assertEqual(issue.first_response_time, 1.0)
 
 	def test_first_response_time_case5(self):
 		"""
-			Test frt when both issue creation and first response are on the same day, but it's not a work day.
+		Test frt when both issue creation and first response are on the same day, but it's not a work day.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-27-2021 10:00"), get_datetime("06-27-2021 11:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-27-2021 10:00"), get_datetime("06-27-2021 11:00")
+		)
 		self.assertEqual(issue.first_response_time, 1.0)
 
 	# issue creation and first response are on consecutive days
 	def test_first_response_time_case6(self):
 		"""
-			Test frt when the issue was created before working hours and the first response is also sent before working hours, but on the next day.
+		Test frt when the issue was created before working hours and the first response is also sent before working hours, but on the next day.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 6:00"), get_datetime("06-29-2021 6:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 6:00"), get_datetime("06-29-2021 6:00")
+		)
 		self.assertEqual(issue.first_response_time, 28800.0)
 
 	def test_first_response_time_case7(self):
 		"""
-			Test frt when the issue was created before working hours and the first response is sent during working hours, but on the next day.
+		Test frt when the issue was created before working hours and the first response is sent during working hours, but on the next day.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 6:00"), get_datetime("06-29-2021 11:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 6:00"), get_datetime("06-29-2021 11:00")
+		)
 		self.assertEqual(issue.first_response_time, 32400.0)
 
 	def test_first_response_time_case8(self):
 		"""
-			Test frt when the issue was created before working hours and the first response is sent after working hours, but on the next day.
+		Test frt when the issue was created before working hours and the first response is sent after working hours, but on the next day.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 6:00"), get_datetime("06-29-2021 20:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 6:00"), get_datetime("06-29-2021 20:00")
+		)
 		self.assertEqual(issue.first_response_time, 57600.0)
 
 	def test_first_response_time_case9(self):
 		"""
-			Test frt when the issue was created before working hours and the first response is sent on the next day, which is not a work day.
+		Test frt when the issue was created before working hours and the first response is sent on the next day, which is not a work day.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-25-2021 6:00"), get_datetime("06-26-2021 11:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-25-2021 6:00"), get_datetime("06-26-2021 11:00")
+		)
 		self.assertEqual(issue.first_response_time, 28800.0)
 
 	def test_first_response_time_case10(self):
 		"""
-			Test frt when the issue was created during working hours and the first response is sent before working hours, but on the next day.
+		Test frt when the issue was created during working hours and the first response is sent before working hours, but on the next day.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 12:00"), get_datetime("06-29-2021 6:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 12:00"), get_datetime("06-29-2021 6:00")
+		)
 		self.assertEqual(issue.first_response_time, 21600.0)
 
 	def test_first_response_time_case11(self):
 		"""
-			Test frt when the issue was created during working hours and the first response is also sent during working hours, but on the next day.
+		Test frt when the issue was created during working hours and the first response is also sent during working hours, but on the next day.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 12:00"), get_datetime("06-29-2021 11:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 12:00"), get_datetime("06-29-2021 11:00")
+		)
 		self.assertEqual(issue.first_response_time, 25200.0)
 
 	def test_first_response_time_case12(self):
 		"""
-			Test frt when the issue was created during working hours and the first response is sent after working hours, but on the next day.
+		Test frt when the issue was created during working hours and the first response is sent after working hours, but on the next day.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 12:00"), get_datetime("06-29-2021 20:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 12:00"), get_datetime("06-29-2021 20:00")
+		)
 		self.assertEqual(issue.first_response_time, 50400.0)
 
 	def test_first_response_time_case13(self):
 		"""
-			Test frt when the issue was created during working hours and the first response is sent on the next day, which is not a work day.
+		Test frt when the issue was created during working hours and the first response is sent on the next day, which is not a work day.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-25-2021 12:00"), get_datetime("06-26-2021 11:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-25-2021 12:00"), get_datetime("06-26-2021 11:00")
+		)
 		self.assertEqual(issue.first_response_time, 21600.0)
 
 	def test_first_response_time_case14(self):
 		"""
-			Test frt when the issue was created after working hours and the first response is sent before working hours, but on the next day.
+		Test frt when the issue was created after working hours and the first response is sent before working hours, but on the next day.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 20:00"), get_datetime("06-29-2021 6:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 20:00"), get_datetime("06-29-2021 6:00")
+		)
 		self.assertEqual(issue.first_response_time, 1.0)
 
 	def test_first_response_time_case15(self):
 		"""
-			Test frt when the issue was created after working hours and the first response is sent during working hours, but on the next day.
+		Test frt when the issue was created after working hours and the first response is sent during working hours, but on the next day.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 20:00"), get_datetime("06-29-2021 11:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 20:00"), get_datetime("06-29-2021 11:00")
+		)
 		self.assertEqual(issue.first_response_time, 3600.0)
 
 	def test_first_response_time_case16(self):
 		"""
-			Test frt when the issue was created after working hours and the first response is also sent after working hours, but on the next day.
+		Test frt when the issue was created after working hours and the first response is also sent after working hours, but on the next day.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 20:00"), get_datetime("06-29-2021 20:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 20:00"), get_datetime("06-29-2021 20:00")
+		)
 		self.assertEqual(issue.first_response_time, 28800.0)
 
 	def test_first_response_time_case17(self):
 		"""
-			Test frt when the issue was created after working hours and the first response is sent on the next day, which is not a work day.
+		Test frt when the issue was created after working hours and the first response is sent on the next day, which is not a work day.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-25-2021 20:00"), get_datetime("06-26-2021 11:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-25-2021 20:00"), get_datetime("06-26-2021 11:00")
+		)
 		self.assertEqual(issue.first_response_time, 1.0)
 
 	# issue creation and first response are a few days apart
 	def test_first_response_time_case18(self):
 		"""
-			Test frt when the issue was created before working hours and the first response is also sent before working hours, but after a few days.
+		Test frt when the issue was created before working hours and the first response is also sent before working hours, but after a few days.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 6:00"), get_datetime("07-01-2021 6:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 6:00"), get_datetime("07-01-2021 6:00")
+		)
 		self.assertEqual(issue.first_response_time, 86400.0)
 
 	def test_first_response_time_case19(self):
 		"""
-			Test frt when the issue was created before working hours and the first response is sent during working hours, but after a few days.
+		Test frt when the issue was created before working hours and the first response is sent during working hours, but after a few days.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 6:00"), get_datetime("07-01-2021 11:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 6:00"), get_datetime("07-01-2021 11:00")
+		)
 		self.assertEqual(issue.first_response_time, 90000.0)
 
 	def test_first_response_time_case20(self):
 		"""
-			Test frt when the issue was created before working hours and the first response is sent after working hours, but after a few days.
+		Test frt when the issue was created before working hours and the first response is sent after working hours, but after a few days.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 6:00"), get_datetime("07-01-2021 20:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 6:00"), get_datetime("07-01-2021 20:00")
+		)
 		self.assertEqual(issue.first_response_time, 115200.0)
 
 	def test_first_response_time_case21(self):
 		"""
-			Test frt when the issue was created before working hours and the first response is sent after a few days, on a holiday.
+		Test frt when the issue was created before working hours and the first response is sent after a few days, on a holiday.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-25-2021 6:00"), get_datetime("06-27-2021 11:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-25-2021 6:00"), get_datetime("06-27-2021 11:00")
+		)
 		self.assertEqual(issue.first_response_time, 28800.0)
 
 	def test_first_response_time_case22(self):
 		"""
-			Test frt when the issue was created during working hours and the first response is sent before working hours, but after a few days.
+		Test frt when the issue was created during working hours and the first response is sent before working hours, but after a few days.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 12:00"), get_datetime("07-01-2021 6:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 12:00"), get_datetime("07-01-2021 6:00")
+		)
 		self.assertEqual(issue.first_response_time, 79200.0)
 
 	def test_first_response_time_case23(self):
 		"""
-			Test frt when the issue was created during working hours and the first response is also sent during working hours, but after a few days.
+		Test frt when the issue was created during working hours and the first response is also sent during working hours, but after a few days.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 12:00"), get_datetime("07-01-2021 11:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 12:00"), get_datetime("07-01-2021 11:00")
+		)
 		self.assertEqual(issue.first_response_time, 82800.0)
 
 	def test_first_response_time_case24(self):
 		"""
-			Test frt when the issue was created during working hours and the first response is sent after working hours, but after a few days.
+		Test frt when the issue was created during working hours and the first response is sent after working hours, but after a few days.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 12:00"), get_datetime("07-01-2021 20:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 12:00"), get_datetime("07-01-2021 20:00")
+		)
 		self.assertEqual(issue.first_response_time, 108000.0)
 
 	def test_first_response_time_case25(self):
 		"""
-			Test frt when the issue was created during working hours and the first response is sent after a few days, on a holiday.
+		Test frt when the issue was created during working hours and the first response is sent after a few days, on a holiday.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-25-2021 12:00"), get_datetime("06-27-2021 11:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-25-2021 12:00"), get_datetime("06-27-2021 11:00")
+		)
 		self.assertEqual(issue.first_response_time, 21600.0)
 
 	def test_first_response_time_case26(self):
 		"""
-			Test frt when the issue was created after working hours and the first response is sent before working hours, but after a few days.
+		Test frt when the issue was created after working hours and the first response is sent before working hours, but after a few days.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 20:00"), get_datetime("07-01-2021 6:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 20:00"), get_datetime("07-01-2021 6:00")
+		)
 		self.assertEqual(issue.first_response_time, 57600.0)
 
 	def test_first_response_time_case27(self):
 		"""
-			Test frt when the issue was created after working hours and the first response is sent during working hours, but after a few days.
+		Test frt when the issue was created after working hours and the first response is sent during working hours, but after a few days.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 20:00"), get_datetime("07-01-2021 11:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 20:00"), get_datetime("07-01-2021 11:00")
+		)
 		self.assertEqual(issue.first_response_time, 61200.0)
 
 	def test_first_response_time_case28(self):
 		"""
-			Test frt when the issue was created after working hours and the first response is also sent after working hours, but after a few days.
+		Test frt when the issue was created after working hours and the first response is also sent after working hours, but after a few days.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-28-2021 20:00"), get_datetime("07-01-2021 20:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-28-2021 20:00"), get_datetime("07-01-2021 20:00")
+		)
 		self.assertEqual(issue.first_response_time, 86400.0)
 
 	def test_first_response_time_case29(self):
 		"""
-			Test frt when the issue was created after working hours and the first response is sent after a few days, on a holiday.
+		Test frt when the issue was created after working hours and the first response is sent after a few days, on a holiday.
 		"""
-		issue = create_issue_and_communication(get_datetime("06-25-2021 20:00"), get_datetime("06-27-2021 11:00"))
+		issue = create_issue_and_communication(
+			get_datetime("06-25-2021 20:00"), get_datetime("06-27-2021 11:00")
+		)
 		self.assertEqual(issue.first_response_time, 1.0)
 
+
 def create_issue_and_communication(issue_creation, first_responded_on):
 	issue = make_issue(issue_creation, index=1)
 	sender = create_user("test@admin.com")
@@ -474,25 +537,28 @@
 
 	return issue
 
+
 def make_issue(creation=None, customer=None, index=0, priority=None, issue_type=None):
-	if issue_type and not frappe.db.exists('Issue Type', issue_type):
-		doc = frappe.new_doc('Issue Type')
+	if issue_type and not frappe.db.exists("Issue Type", issue_type):
+		doc = frappe.new_doc("Issue Type")
 		doc.name = issue_type
 		doc.insert()
 
-	issue = frappe.get_doc({
-		"doctype": "Issue",
-		"subject": "Service Level Agreement Issue {0}".format(index),
-		"customer": customer,
-		"raised_by": "test@example.com",
-		"description": "Service Level Agreement Issue",
-		"issue_type": issue_type,
-		"priority": priority,
-		"creation": creation,
-		"opening_date": creation,
-		"service_level_agreement_creation": creation,
-		"company": "_Test Company"
-	}).insert(ignore_permissions=True)
+	issue = frappe.get_doc(
+		{
+			"doctype": "Issue",
+			"subject": "Service Level Agreement Issue {0}".format(index),
+			"customer": customer,
+			"raised_by": "test@example.com",
+			"description": "Service Level Agreement Issue",
+			"issue_type": issue_type,
+			"priority": priority,
+			"creation": creation,
+			"opening_date": creation,
+			"service_level_agreement_creation": creation,
+			"company": "_Test Company",
+		}
+	).insert(ignore_permissions=True)
 
 	return issue
 
@@ -503,45 +569,50 @@
 	create_territory(territory)
 
 	if not frappe.db.exists("Customer", {"customer_name": name}):
-		frappe.get_doc({
-			"doctype": "Customer",
-			"customer_name": name,
-			"customer_group": customer_group,
-			"territory": territory
-		}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "Customer",
+				"customer_name": name,
+				"customer_group": customer_group,
+				"territory": territory,
+			}
+		).insert(ignore_permissions=True)
 
 
 def create_customer_group(customer_group):
 
 	if not frappe.db.exists("Customer Group", {"customer_group_name": customer_group}):
-		frappe.get_doc({
-			"doctype": "Customer Group",
-			"customer_group_name": customer_group
-		}).insert(ignore_permissions=True)
+		frappe.get_doc({"doctype": "Customer Group", "customer_group_name": customer_group}).insert(
+			ignore_permissions=True
+		)
 
 
 def create_territory(territory):
 
 	if not frappe.db.exists("Territory", {"territory_name": territory}):
-		frappe.get_doc({
-			"doctype": "Territory",
-			"territory_name": territory,
-		}).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{
+				"doctype": "Territory",
+				"territory_name": territory,
+			}
+		).insert(ignore_permissions=True)
 
 
 def create_communication(reference_name, sender, sent_or_received, creation):
-	communication = frappe.get_doc({
-		"doctype": "Communication",
-		"communication_type": "Communication",
-		"communication_medium": "Email",
-		"sent_or_received": sent_or_received,
-		"email_status": "Open",
-		"subject": "Test Issue",
-		"sender": sender,
-		"content": "Test",
-		"status": "Linked",
-		"reference_doctype": "Issue",
-		"creation": creation,
-		"reference_name": reference_name
-	})
+	communication = frappe.get_doc(
+		{
+			"doctype": "Communication",
+			"communication_type": "Communication",
+			"communication_medium": "Email",
+			"sent_or_received": sent_or_received,
+			"email_status": "Open",
+			"subject": "Test Issue",
+			"sender": sender,
+			"content": "Test",
+			"status": "Linked",
+			"reference_doctype": "Issue",
+			"creation": creation,
+			"reference_name": reference_name,
+		}
+	)
 	communication.save()
diff --git a/erpnext/support/doctype/issue_priority/test_issue_priority.py b/erpnext/support/doctype/issue_priority/test_issue_priority.py
index d2b1415..c30540b 100644
--- a/erpnext/support/doctype/issue_priority/test_issue_priority.py
+++ b/erpnext/support/doctype/issue_priority/test_issue_priority.py
@@ -7,7 +7,6 @@
 
 
 class TestIssuePriority(unittest.TestCase):
-
 	def test_priorities(self):
 		make_priorities()
 		priorities = frappe.get_list("Issue Priority")
@@ -15,14 +14,13 @@
 		for priority in priorities:
 			self.assertIn(priority.name, ["Low", "Medium", "High"])
 
+
 def make_priorities():
 	insert_priority("Low")
 	insert_priority("Medium")
 	insert_priority("High")
 
+
 def insert_priority(name):
 	if not frappe.db.exists("Issue Priority", name):
-		frappe.get_doc({
-			"doctype": "Issue Priority",
-			"name": name
-		}).insert(ignore_permissions=True)
+		frappe.get_doc({"doctype": "Issue Priority", "name": name}).insert(ignore_permissions=True)
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
index 526b6aa..e49f212 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
@@ -42,16 +42,24 @@
 		for priority in self.priorities:
 			# Check if response and resolution time is set for every priority
 			if not priority.response_time:
-				frappe.throw(_("Set Response Time for Priority {0} in row {1}.").format(priority.priority, priority.idx))
+				frappe.throw(
+					_("Set Response Time for Priority {0} in row {1}.").format(priority.priority, priority.idx)
+				)
 
 			if self.apply_sla_for_resolution:
 				if not priority.resolution_time:
-					frappe.throw(_("Set Response Time for Priority {0} in row {1}.").format(priority.priority, priority.idx))
+					frappe.throw(
+						_("Set Response Time for Priority {0} in row {1}.").format(priority.priority, priority.idx)
+					)
 
 				response = priority.response_time
 				resolution = priority.resolution_time
 				if response > resolution:
-					frappe.throw(_("Response Time for {0} priority in row {1} can't be greater than Resolution Time.").format(priority.priority, priority.idx))
+					frappe.throw(
+						_("Response Time for {0} priority in row {1} can't be greater than Resolution Time.").format(
+							priority.priority, priority.idx
+						)
+					)
 
 			priorities.append(priority.priority)
 
@@ -74,9 +82,14 @@
 			support_days.append(support_and_resolution.workday)
 			support_and_resolution.idx = week.index(support_and_resolution.workday) + 1
 
-			if to_timedelta(support_and_resolution.start_time) >= to_timedelta(support_and_resolution.end_time):
-				frappe.throw(_("Start Time can't be greater than or equal to End Time for {0}.").format(
-					support_and_resolution.workday))
+			if to_timedelta(support_and_resolution.start_time) >= to_timedelta(
+				support_and_resolution.end_time
+			):
+				frappe.throw(
+					_("Start Time can't be greater than or equal to End Time for {0}.").format(
+						support_and_resolution.workday
+					)
+				)
 
 		# Check for repeated workday
 		if not len(set(support_days)) == len(support_days):
@@ -84,51 +97,76 @@
 			frappe.throw(_("Workday {0} has been repeated.").format(repeated_days))
 
 	def validate_doc(self):
-		if self.enabled and self.document_type == "Issue" \
-			and not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"):
-			frappe.throw(_("{0} is not enabled in {1}").format(frappe.bold("Track Service Level Agreement"),
-				get_link_to_form("Support Settings", "Support Settings")))
+		if (
+			self.enabled
+			and self.document_type == "Issue"
+			and not frappe.db.get_single_value("Support Settings", "track_service_level_agreement")
+		):
+			frappe.throw(
+				_("{0} is not enabled in {1}").format(
+					frappe.bold("Track Service Level Agreement"),
+					get_link_to_form("Support Settings", "Support Settings"),
+				)
+			)
 
-		if self.default_service_level_agreement and frappe.db.exists("Service Level Agreement", {
-			"document_type": self.document_type,
-			"default_service_level_agreement": "1",
-			"name": ["!=", self.name]
-		}):
-			frappe.throw(_("Default Service Level Agreement for {0} already exists.").format(self.document_type))
+		if self.default_service_level_agreement and frappe.db.exists(
+			"Service Level Agreement",
+			{
+				"document_type": self.document_type,
+				"default_service_level_agreement": "1",
+				"name": ["!=", self.name],
+			},
+		):
+			frappe.throw(
+				_("Default Service Level Agreement for {0} already exists.").format(self.document_type)
+			)
 
 		if self.start_date and self.end_date:
 			self.validate_from_to_dates(self.start_date, self.end_date)
 
-		if self.entity_type and self.entity and frappe.db.exists("Service Level Agreement", {
-			"entity_type": self.entity_type,
-			"entity": self.entity,
-			"name": ["!=", self.name]
-		}):
-			frappe.throw(_("Service Level Agreement for {0} {1} already exists.").format(
-				frappe.bold(self.entity_type), frappe.bold(self.entity)))
+		if (
+			self.entity_type
+			and self.entity
+			and frappe.db.exists(
+				"Service Level Agreement",
+				{"entity_type": self.entity_type, "entity": self.entity, "name": ["!=", self.name]},
+			)
+		):
+			frappe.throw(
+				_("Service Level Agreement for {0} {1} already exists.").format(
+					frappe.bold(self.entity_type), frappe.bold(self.entity)
+				)
+			)
 
 	def validate_selected_doctype(self):
 		invalid_doctypes = list(frappe.model.core_doctypes_list)
-		invalid_doctypes.extend(['Cost Center', 'Company'])
-		valid_document_types = frappe.get_all('DocType', {
-			'issingle': 0,
-			'istable': 0,
-			'is_submittable': 0,
-			'name': ['not in', invalid_doctypes],
-			'module': ['not in', ["Email", "Core", "Custom", "Event Streaming", "Social", "Data Migration", "Geo", "Desk"]]
-		}, pluck="name")
+		invalid_doctypes.extend(["Cost Center", "Company"])
+		valid_document_types = frappe.get_all(
+			"DocType",
+			{
+				"issingle": 0,
+				"istable": 0,
+				"is_submittable": 0,
+				"name": ["not in", invalid_doctypes],
+				"module": [
+					"not in",
+					["Email", "Core", "Custom", "Event Streaming", "Social", "Data Migration", "Geo", "Desk"],
+				],
+			},
+			pluck="name",
+		)
 
 		if self.document_type not in valid_document_types:
-			frappe.throw(
-				msg=_("Please select valid document type."),
-				title=_("Invalid Document Type")
-			)
+			frappe.throw(msg=_("Please select valid document type."), title=_("Invalid Document Type"))
 
 	def validate_status_field(self):
 		meta = frappe.get_meta(self.document_type)
 		if not meta.get_field("status"):
-			frappe.throw(_("The Document Type {0} must have a Status field to configure Service Level Agreement").format(
-				frappe.bold(self.document_type)))
+			frappe.throw(
+				_(
+					"The Document Type {0} must have a Status field to configure Service Level Agreement"
+				).format(frappe.bold(self.document_type))
+			)
 
 	def validate_condition(self):
 		temp_doc = frappe.new_doc(self.document_type)
@@ -141,11 +179,13 @@
 	def get_service_level_agreement_priority(self, priority):
 		priority = frappe.get_doc("Service Level Priority", {"priority": priority, "parent": self.name})
 
-		return frappe._dict({
-			"priority": priority.priority,
-			"response_time": priority.response_time,
-			"resolution_time": priority.resolution_time
-		})
+		return frappe._dict(
+			{
+				"priority": priority.priority,
+				"response_time": priority.response_time,
+				"resolution_time": priority.resolution_time,
+			}
+		)
 
 	def before_insert(self):
 		# no need to set up SLA fields for Issue dt as they are standard fields in Issue
@@ -176,46 +216,50 @@
 			if not meta.has_field(field.get("fieldname")):
 				last_index += 1
 
-				frappe.get_doc({
-					"doctype": "DocField",
-					"idx": last_index,
-					"parenttype": "DocType",
-					"parentfield": "fields",
-					"parent": self.document_type,
-					"label": field.get("label"),
-					"fieldname": field.get("fieldname"),
-					"fieldtype": field.get("fieldtype"),
-					"collapsible": field.get("collapsible"),
-					"options": field.get("options"),
-					"read_only": field.get("read_only"),
-					"hidden": field.get("hidden"),
-					"description": field.get("description"),
-					"default": field.get("default"),
-				}).insert(ignore_permissions=True)
+				frappe.get_doc(
+					{
+						"doctype": "DocField",
+						"idx": last_index,
+						"parenttype": "DocType",
+						"parentfield": "fields",
+						"parent": self.document_type,
+						"label": field.get("label"),
+						"fieldname": field.get("fieldname"),
+						"fieldtype": field.get("fieldtype"),
+						"collapsible": field.get("collapsible"),
+						"options": field.get("options"),
+						"read_only": field.get("read_only"),
+						"hidden": field.get("hidden"),
+						"description": field.get("description"),
+						"default": field.get("default"),
+					}
+				).insert(ignore_permissions=True)
 			else:
 				existing_field = meta.get_field(field.get("fieldname"))
 				self.reset_field_properties(existing_field, "DocField", field)
 
 		# to update meta and modified timestamp
-		frappe.get_doc('DocType', self.document_type).save(ignore_permissions=True)
+		frappe.get_doc("DocType", self.document_type).save(ignore_permissions=True)
 
 	def create_custom_fields(self, meta, service_level_agreement_fields):
 		for field in service_level_agreement_fields:
 			if not meta.has_field(field.get("fieldname")):
-				frappe.get_doc({
-					"doctype": "Custom Field",
-					"dt": self.document_type,
-					"label": field.get("label"),
-					"fieldname": field.get("fieldname"),
-					"fieldtype": field.get("fieldtype"),
-					"insert_after": "append",
-					"collapsible": field.get("collapsible"),
-					"options": field.get("options"),
-					"read_only": field.get("read_only"),
-					"hidden": field.get("hidden"),
-					"description": field.get("description"),
-					"default": field.get("default"),
-				}).insert(ignore_permissions=True)
+				frappe.get_doc(
+					{
+						"doctype": "Custom Field",
+						"dt": self.document_type,
+						"label": field.get("label"),
+						"fieldname": field.get("fieldname"),
+						"fieldtype": field.get("fieldtype"),
+						"insert_after": "append",
+						"collapsible": field.get("collapsible"),
+						"options": field.get("options"),
+						"read_only": field.get("read_only"),
+						"hidden": field.get("hidden"),
+						"description": field.get("description"),
+						"default": field.get("default"),
+					}
+				).insert(ignore_permissions=True)
 			else:
 				existing_field = meta.get_field(field.get("fieldname"))
 				self.reset_field_properties(existing_field, "Custom Field", field)
@@ -236,57 +280,73 @@
 
 
 def check_agreement_status():
-	service_level_agreements = frappe.get_all("Service Level Agreement", filters=[
-		{"enabled": 1},
-		{"default_service_level_agreement": 0}
-	], fields=["name"])
+	service_level_agreements = frappe.get_all(
+		"Service Level Agreement",
+		filters=[{"enabled": 1}, {"default_service_level_agreement": 0}],
+		fields=["name"],
+	)
 
 	for service_level_agreement in service_level_agreements:
 		doc = frappe.get_doc("Service Level Agreement", service_level_agreement.name)
 		if doc.end_date and getdate(doc.end_date) < getdate(frappe.utils.getdate()):
 			frappe.db.set_value("Service Level Agreement", service_level_agreement.name, "enabled", 0)
 
+
 def get_active_service_level_agreement_for(doc):
 	if not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"):
 		return
 
 	filters = [
-		["Service Level Agreement", "document_type", "=", doc.get('doctype')],
-		["Service Level Agreement", "enabled", "=", 1]
+		["Service Level Agreement", "document_type", "=", doc.get("doctype")],
+		["Service Level Agreement", "enabled", "=", 1],
 	]
 
-	if doc.get('priority'):
-		filters.append(["Service Level Priority", "priority", "=", doc.get('priority')])
+	if doc.get("priority"):
+		filters.append(["Service Level Priority", "priority", "=", doc.get("priority")])
 
 	or_filters = []
-	if doc.get('service_level_agreement'):
+	if doc.get("service_level_agreement"):
 		or_filters = [
-			["Service Level Agreement", "name", "=", doc.get('service_level_agreement')],
+			["Service Level Agreement", "name", "=", doc.get("service_level_agreement")],
 		]
 
-	customer = doc.get('customer')
+	customer = doc.get("customer")
 	if customer:
-		or_filters.extend([
-			["Service Level Agreement", "entity", "in", [customer] + get_customer_group(customer) + get_customer_territory(customer)],
-			["Service Level Agreement", "entity_type", "is", "not set"]
-		])
-	else:
-		or_filters.append(
-			["Service Level Agreement", "entity_type", "is", "not set"]
+		or_filters.extend(
+			[
+				[
+					"Service Level Agreement",
+					"entity",
+					"in",
+					[customer] + get_customer_group(customer) + get_customer_territory(customer),
+				],
+				["Service Level Agreement", "entity_type", "is", "not set"],
+			]
 		)
+	else:
+		or_filters.append(["Service Level Agreement", "entity_type", "is", "not set"])
 
-	default_sla_filter = filters + [["Service Level Agreement", "default_service_level_agreement", "=", 1]]
-	default_sla = frappe.get_all("Service Level Agreement", filters=default_sla_filter,
-		fields=["name", "default_priority", "apply_sla_for_resolution", "condition"])
+	default_sla_filter = filters + [
+		["Service Level Agreement", "default_service_level_agreement", "=", 1]
+	]
+	default_sla = frappe.get_all(
+		"Service Level Agreement",
+		filters=default_sla_filter,
+		fields=["name", "default_priority", "apply_sla_for_resolution", "condition"],
+	)
 
 	filters += [["Service Level Agreement", "default_service_level_agreement", "=", 0]]
-	agreements = frappe.get_all("Service Level Agreement", filters=filters, or_filters=or_filters,
-		fields=["name", "default_priority", "apply_sla_for_resolution", "condition"])
+	agreements = frappe.get_all(
+		"Service Level Agreement",
+		filters=filters,
+		or_filters=or_filters,
+		fields=["name", "default_priority", "apply_sla_for_resolution", "condition"],
+	)
 
 	# check if the current document on which SLA is to be applied fulfills all the conditions
 	filtered_agreements = []
 	for agreement in agreements:
-		condition = agreement.get('condition')
+		condition = agreement.get("condition")
 		if not condition or (condition and frappe.safe_eval(condition, None, get_context(doc))):
 			filtered_agreements.append(agreement)
 
@@ -295,8 +355,14 @@
 
 	return filtered_agreements[0] if filtered_agreements else None
 
+
 def get_context(doc):
-	return {"doc": doc.as_dict(), "nowdate": nowdate, "frappe": frappe._dict(utils=get_safe_globals().get("frappe").get("utils"))}
+	return {
+		"doc": doc.as_dict(),
+		"nowdate": nowdate,
+		"frappe": frappe._dict(utils=get_safe_globals().get("frappe").get("utils")),
+	}
+
 
 def get_customer_group(customer):
 	customer_groups = []
@@ -325,22 +391,33 @@
 
 	filters = [
 		["Service Level Agreement", "document_type", "=", doctype],
-		["Service Level Agreement", "enabled", "=", 1]
+		["Service Level Agreement", "enabled", "=", 1],
 	]
 
-	or_filters = [
-		["Service Level Agreement", "default_service_level_agreement", "=", 1]
-	]
+	or_filters = [["Service Level Agreement", "default_service_level_agreement", "=", 1]]
 
 	if customer:
 		# Include SLA with No Entity and Entity Type
 		or_filters.append(
-			["Service Level Agreement", "entity", "in", [""] + [customer] + get_customer_group(customer) + get_customer_territory(customer)]
+			[
+				"Service Level Agreement",
+				"entity",
+				"in",
+				[""] + [customer] + get_customer_group(customer) + get_customer_territory(customer),
+			]
 		)
 
 	return {
-		"priority": [priority.priority for priority in frappe.get_all("Service Level Priority", filters={"parent": name}, fields=["priority"])],
-		"service_level_agreements": [d.name for d in frappe.get_all("Service Level Agreement", filters=filters, or_filters=or_filters)]
+		"priority": [
+			priority.priority
+			for priority in frappe.get_all(
+				"Service Level Priority", filters={"parent": name}, fields=["priority"]
+			)
+		],
+		"service_level_agreements": [
+			d.name
+			for d in frappe.get_all("Service Level Agreement", filters=filters, or_filters=or_filters)
+		],
 	}
 
 
@@ -366,7 +443,9 @@
 
 
 def set_documents_with_active_service_level_agreement():
-	active = [sla.document_type for sla in frappe.get_all("Service Level Agreement", fields=["document_type"])]
+	active = [
+		sla.document_type for sla in frappe.get_all("Service Level Agreement", fields=["document_type"])
+	]
 	frappe.cache().hset("service_level_agreement", "active", active)
 	return active
 
@@ -414,7 +493,7 @@
 
 def handle_status_change(doc, apply_sla_for_resolution):
 	now_time = frappe.flags.current_time or now_datetime(doc.get("owner"))
-	prev_status = frappe.db.get_value(doc.doctype, doc.name, 'status')
+	prev_status = frappe.db.get_value(doc.doctype, doc.name, "status")
 
 	hold_statuses = get_hold_statuses(doc.service_level_agreement)
 	fulfillment_statuses = get_fulfillment_statuses(doc.service_level_agreement)
@@ -429,9 +508,9 @@
 		return status not in hold_statuses and status not in fulfillment_statuses
 
 	def set_first_response():
-		if doc.meta.has_field("first_responded_on") and not doc.get('first_responded_on'):
+		if doc.meta.has_field("first_responded_on") and not doc.get("first_responded_on"):
 			doc.first_responded_on = now_time
-			if get_datetime(doc.get('first_responded_on')) > get_datetime(doc.get('response_by')):
+			if get_datetime(doc.get("first_responded_on")) > get_datetime(doc.get("response_by")):
 				record_assigned_users_on_failure(doc)
 
 	def calculate_hold_hours():
@@ -444,7 +523,7 @@
 			doc.total_hold_time = (doc.total_hold_time or 0) + current_hold_hours
 		doc.on_hold_since = None
 
-	if ((is_open_status(prev_status) and not is_open_status(doc.status)) or doc.flags.on_first_reply):
+	if (is_open_status(prev_status) and not is_open_status(doc.status)) or doc.flags.on_first_reply:
 		set_first_response()
 
 	# Open to Replied
@@ -492,22 +571,28 @@
 
 
 def get_fulfillment_statuses(service_level_agreement):
-	return [entry.status for entry in frappe.db.get_all("SLA Fulfilled On Status", filters={
-		"parent": service_level_agreement
-	}, fields=["status"])]
+	return [
+		entry.status
+		for entry in frappe.db.get_all(
+			"SLA Fulfilled On Status", filters={"parent": service_level_agreement}, fields=["status"]
+		)
+	]
 
 
 def get_hold_statuses(service_level_agreement):
-	return [entry.status for entry in frappe.db.get_all("Pause SLA On Status", filters={
-		"parent": service_level_agreement
-	}, fields=["status"])]
+	return [
+		entry.status
+		for entry in frappe.db.get_all(
+			"Pause SLA On Status", filters={"parent": service_level_agreement}, fields=["status"]
+		)
+	]
 
 
 def update_response_and_resolution_metrics(doc, apply_sla_for_resolution):
 	priority = get_response_and_resolution_duration(doc)
 	start_date_time = get_datetime(doc.get("service_level_agreement_creation") or doc.creation)
 	set_response_by(doc, start_date_time, priority)
-	if apply_sla_for_resolution and not doc.get('on_hold_since'): # resolution_by is reset if on hold
+	if apply_sla_for_resolution and not doc.get("on_hold_since"):  # resolution_by is reset if on hold
 		set_resolution_by(doc, start_date_time, priority)
 
 
@@ -526,9 +611,13 @@
 		current_weekday = weekdays[current_date_time.weekday()]
 
 		if not is_holiday(current_date_time, holidays) and current_weekday in support_days:
-			if getdate(current_date_time) == getdate(start_date_time) \
-				and get_time_in_timedelta(current_date_time.time()) > support_days[current_weekday].start_time:
-				start_time = current_date_time - datetime(current_date_time.year, current_date_time.month, current_date_time.day)
+			if (
+				getdate(current_date_time) == getdate(start_date_time)
+				and get_time_in_timedelta(current_date_time.time()) > support_days[current_weekday].start_time
+			):
+				start_time = current_date_time - datetime(
+					current_date_time.year, current_date_time.month, current_date_time.day
+				)
 			else:
 				start_time = support_days[current_weekday].start_time
 
@@ -572,10 +661,12 @@
 def get_support_days(service_level):
 	support_days = {}
 	for service in service_level.get("support_and_resolution"):
-		support_days[service.workday] = frappe._dict({
-			"start_time": service.start_time,
-			"end_time": service.end_time,
-		})
+		support_days[service.workday] = frappe._dict(
+			{
+				"start_time": service.start_time,
+				"end_time": service.end_time,
+			}
+		)
 	return support_days
 
 
@@ -588,15 +679,20 @@
 	if not doc.meta.has_field("user_resolution_time"):
 		return
 
-	communications = frappe.get_all("Communication", filters={
-			"reference_doctype": doc.doctype,
-			"reference_name": doc.name
-		}, fields=["sent_or_received", "name", "creation"], order_by="creation")
+	communications = frappe.get_all(
+		"Communication",
+		filters={"reference_doctype": doc.doctype, "reference_name": doc.name},
+		fields=["sent_or_received", "name", "creation"],
+		order_by="creation",
+	)
 
 	pending_time = []
 	for i in range(len(communications)):
-		if communications[i].sent_or_received == "Received" and communications[i-1].sent_or_received == "Sent":
-			wait_time = time_diff_in_seconds(communications[i].creation, communications[i-1].creation)
+		if (
+			communications[i].sent_or_received == "Received"
+			and communications[i - 1].sent_or_received == "Sent"
+		):
+			wait_time = time_diff_in_seconds(communications[i].creation, communications[i - 1].creation)
 			if wait_time > 0:
 				pending_time.append(wait_time)
 
@@ -606,25 +702,35 @@
 
 
 def change_service_level_agreement_and_priority(self):
-	if self.service_level_agreement and frappe.db.exists("Issue", self.name) and \
-		frappe.db.get_single_value("Support Settings", "track_service_level_agreement"):
+	if (
+		self.service_level_agreement
+		and frappe.db.exists("Issue", self.name)
+		and frappe.db.get_single_value("Support Settings", "track_service_level_agreement")
+	):
 
 		if not self.priority == frappe.db.get_value("Issue", self.name, "priority"):
-			self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement)
+			self.set_response_and_resolution_time(
+				priority=self.priority, service_level_agreement=self.service_level_agreement
+			)
 			frappe.msgprint(_("Priority has been changed to {0}.").format(self.priority))
 
-		if not self.service_level_agreement == frappe.db.get_value("Issue", self.name, "service_level_agreement"):
-			self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement)
-			frappe.msgprint(_("Service Level Agreement has been changed to {0}.").format(self.service_level_agreement))
+		if not self.service_level_agreement == frappe.db.get_value(
+			"Issue", self.name, "service_level_agreement"
+		):
+			self.set_response_and_resolution_time(
+				priority=self.priority, service_level_agreement=self.service_level_agreement
+			)
+			frappe.msgprint(
+				_("Service Level Agreement has been changed to {0}.").format(self.service_level_agreement)
+			)
 
 
 def get_response_and_resolution_duration(doc):
 	sla = frappe.get_doc("Service Level Agreement", doc.service_level_agreement)
 	priority = sla.get_service_level_agreement_priority(doc.priority)
-	priority.update({
-		"support_and_resolution": sla.support_and_resolution,
-		"holiday_list": sla.holiday_list
-	})
+	priority.update(
+		{"support_and_resolution": sla.support_and_resolution, "holiday_list": sla.holiday_list}
+	)
 	return priority
 
 
@@ -632,14 +738,16 @@
 	if not frappe.db.get_single_value("Support Settings", "allow_resetting_service_level_agreement"):
 		frappe.throw(_("Allow Resetting Service Level Agreement from Support Settings."))
 
-	frappe.get_doc({
-		"doctype": "Comment",
-		"comment_type": "Info",
-		"reference_doctype": doc.doctype,
-		"reference_name": doc.name,
-		"comment_email": user,
-		"content": " resetted Service Level Agreement - {0}".format(_(reason)),
-	}).insert(ignore_permissions=True)
+	frappe.get_doc(
+		{
+			"doctype": "Comment",
+			"comment_type": "Info",
+			"reference_doctype": doc.doctype,
+			"reference_name": doc.name,
+			"comment_email": user,
+			"content": " resetted Service Level Agreement - {0}".format(_(reason)),
+		}
+	).insert(ignore_permissions=True)
 
 	doc.service_level_agreement_creation = now_datetime(doc.get("owner"))
 	doc.save()
@@ -665,28 +773,30 @@
 	if not parent:
 		return
 
-	if not parent.meta.has_field('service_level_agreement'):
+	if not parent.meta.has_field("service_level_agreement"):
 		return
 
 	if (
-		doc.sent_or_received == "Received" # a reply is received
-		and parent.get('status') == 'Open' # issue status is set as open from communication.py
+		doc.sent_or_received == "Received"  # a reply is received
+		and parent.get("status") == "Open"  # issue status is set as open from communication.py
 		and parent.get_doc_before_save()
-		and parent.get('status') != parent._doc_before_save.get('status') # status changed
+		and parent.get("status") != parent._doc_before_save.get("status")  # status changed
 	):
 		# undo the status change in db
 		# since prev status is fetched from db
 		frappe.db.set_value(
-			parent.doctype, parent.name,
-			'status', parent._doc_before_save.get('status'),
-			update_modified=False
+			parent.doctype,
+			parent.name,
+			"status",
+			parent._doc_before_save.get("status"),
+			update_modified=False,
 		)
 
 	elif (
-		doc.sent_or_received == "Sent" # a reply is sent
-		and parent.get('first_responded_on') # first_responded_on is set from communication.py
+		doc.sent_or_received == "Sent"  # a reply is sent
+		and parent.get("first_responded_on")  # first_responded_on is set from communication.py
 		and parent.get_doc_before_save()
-		and not parent._doc_before_save.get('first_responded_on') # first_responded_on was not set
+		and not parent._doc_before_save.get("first_responded_on")  # first_responded_on was not set
 	):
 		# reset first_responded_on since it will be handled/set later on
 		parent.first_responded_on = None
@@ -695,7 +805,9 @@
 	else:
 		return
 
-	for_resolution = frappe.db.get_value('Service Level Agreement', parent.service_level_agreement, 'apply_sla_for_resolution')
+	for_resolution = frappe.db.get_value(
+		"Service Level Agreement", parent.service_level_agreement, "apply_sla_for_resolution"
+	)
 
 	handle_status_change(parent, for_resolution)
 	update_response_and_resolution_metrics(parent, for_resolution)
@@ -705,36 +817,42 @@
 
 
 def reset_expected_response_and_resolution(doc):
-	if doc.meta.has_field("first_responded_on") and not doc.get('first_responded_on'):
+	if doc.meta.has_field("first_responded_on") and not doc.get("first_responded_on"):
 		doc.response_by = None
-	if doc.meta.has_field("resolution_by") and not doc.get('resolution_date'):
+	if doc.meta.has_field("resolution_by") and not doc.get("resolution_date"):
 		doc.resolution_by = None
 
 
 def set_response_by(doc, start_date_time, priority):
 	if doc.meta.has_field("response_by"):
-		doc.response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time)
-		if doc.meta.has_field("total_hold_time") and doc.get('total_hold_time') and not doc.get('first_responded_on'):
-			doc.response_by = add_to_date(doc.response_by, seconds=round(doc.get('total_hold_time')))
+		doc.response_by = get_expected_time_for(
+			parameter="response", service_level=priority, start_date_time=start_date_time
+		)
+		if (
+			doc.meta.has_field("total_hold_time")
+			and doc.get("total_hold_time")
+			and not doc.get("first_responded_on")
+		):
+			doc.response_by = add_to_date(doc.response_by, seconds=round(doc.get("total_hold_time")))
 
 
 def set_resolution_by(doc, start_date_time, priority):
 	if doc.meta.has_field("resolution_by"):
-		doc.resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time)
-		if doc.meta.has_field("total_hold_time") and doc.get('total_hold_time'):
-			doc.resolution_by = add_to_date(doc.resolution_by, seconds=round(doc.get('total_hold_time')))
+		doc.resolution_by = get_expected_time_for(
+			parameter="resolution", service_level=priority, start_date_time=start_date_time
+		)
+		if doc.meta.has_field("total_hold_time") and doc.get("total_hold_time"):
+			doc.resolution_by = add_to_date(doc.resolution_by, seconds=round(doc.get("total_hold_time")))
 
 
 def record_assigned_users_on_failure(doc):
 	assigned_users = doc.get_assigned_users()
 	if assigned_users:
 		from frappe.utils import get_fullname
-		assigned_users = ', '.join((get_fullname(user) for user in assigned_users))
-		message = _('First Response SLA Failed by {}').format(assigned_users)
-		doc.add_comment(
-			comment_type='Assigned',
-			text=message
-		)
+
+		assigned_users = ", ".join((get_fullname(user) for user in assigned_users))
+		message = _("First Response SLA Failed by {}").format(assigned_users)
+		doc.add_comment(comment_type="Assigned", text=message)
 
 
 def get_service_level_agreement_fields():
@@ -743,71 +861,57 @@
 			"collapsible": 1,
 			"fieldname": "service_level_section",
 			"fieldtype": "Section Break",
-			"label": "Service Level"
+			"label": "Service Level",
 		},
 		{
 			"fieldname": "service_level_agreement",
 			"fieldtype": "Link",
 			"label": "Service Level Agreement",
-			"options": "Service Level Agreement"
+			"options": "Service Level Agreement",
 		},
-		{
-			"fieldname": "priority",
-			"fieldtype": "Link",
-			"label": "Priority",
-			"options": "Issue Priority"
-		},
-		{
-			"fieldname": "response_by",
-			"fieldtype": "Datetime",
-			"label": "Response By",
-			"read_only": 1
-		},
+		{"fieldname": "priority", "fieldtype": "Link", "label": "Priority", "options": "Issue Priority"},
+		{"fieldname": "response_by", "fieldtype": "Datetime", "label": "Response By", "read_only": 1},
 		{
 			"fieldname": "first_responded_on",
 			"fieldtype": "Datetime",
 			"label": "First Responded On",
 			"no_copy": 1,
-			"read_only": 1
+			"read_only": 1,
 		},
 		{
 			"fieldname": "on_hold_since",
 			"fieldtype": "Datetime",
 			"hidden": 1,
 			"label": "On Hold Since",
-			"read_only": 1
+			"read_only": 1,
 		},
 		{
 			"fieldname": "total_hold_time",
 			"fieldtype": "Duration",
 			"label": "Total Hold Time",
-			"read_only": 1
+			"read_only": 1,
 		},
-		{
-			"fieldname": "cb",
-			"fieldtype": "Column Break",
-			"read_only": 1
-		},
+		{"fieldname": "cb", "fieldtype": "Column Break", "read_only": 1},
 		{
 			"default": "First Response Due",
 			"fieldname": "agreement_status",
 			"fieldtype": "Select",
 			"label": "Service Level Agreement Status",
 			"options": "First Response Due\nResolution Due\nFulfilled\nFailed",
-			"read_only": 1
+			"read_only": 1,
 		},
 		{
 			"fieldname": "resolution_by",
 			"fieldtype": "Datetime",
 			"label": "Resolution By",
-			"read_only": 1
+			"read_only": 1,
 		},
 		{
 			"fieldname": "service_level_agreement_creation",
 			"fieldtype": "Datetime",
 			"hidden": 1,
 			"label": "Service Level Agreement Creation",
-			"read_only": 1
+			"read_only": 1,
 		},
 		{
 			"depends_on": "eval:!doc.__islocal",
@@ -815,8 +919,8 @@
 			"fieldtype": "Datetime",
 			"label": "Resolution Date",
 			"no_copy": 1,
-			"read_only": 1
-		}
+			"read_only": 1,
+		},
 	]
 
 
@@ -826,21 +930,21 @@
 
 
 def update_agreement_status(doc, apply_sla_for_resolution):
-	if (doc.meta.has_field("agreement_status")):
+	if doc.meta.has_field("agreement_status"):
 		# if SLA is applied for resolution check for response and resolution, else only response
 		if apply_sla_for_resolution:
-			if doc.meta.has_field("first_responded_on") and not doc.get('first_responded_on'):
+			if doc.meta.has_field("first_responded_on") and not doc.get("first_responded_on"):
 				doc.agreement_status = "First Response Due"
-			elif doc.meta.has_field("resolution_date") and not doc.get('resolution_date'):
+			elif doc.meta.has_field("resolution_date") and not doc.get("resolution_date"):
 				doc.agreement_status = "Resolution Due"
-			elif get_datetime(doc.get('resolution_date')) <= get_datetime(doc.get('resolution_by')):
+			elif get_datetime(doc.get("resolution_date")) <= get_datetime(doc.get("resolution_by")):
 				doc.agreement_status = "Fulfilled"
 			else:
 				doc.agreement_status = "Failed"
 		else:
-			if doc.meta.has_field("first_responded_on") and not doc.get('first_responded_on'):
+			if doc.meta.has_field("first_responded_on") and not doc.get("first_responded_on"):
 				doc.agreement_status = "First Response Due"
-			elif get_datetime(doc.get('first_responded_on')) <= get_datetime(doc.get('response_by')):
+			elif get_datetime(doc.get("first_responded_on")) <= get_datetime(doc.get("response_by")):
 				doc.agreement_status = "Fulfilled"
 			else:
 				doc.agreement_status = "Failed"
@@ -853,6 +957,7 @@
 def get_time_in_timedelta(time):
 	"""Converts datetime.time(10, 36, 55, 961454) to datetime.timedelta(seconds=38215)."""
 	import datetime
+
 	return datetime.timedelta(hours=time.hour, minutes=time.minute, seconds=time.second)
 
 
@@ -865,7 +970,7 @@
 	from pytz import UnknownTimeZoneError, timezone
 
 	user_tz = get_tz(user)
-	utcnow = timezone('UTC').localize(utc_timestamp)
+	utcnow = timezone("UTC").localize(utc_timestamp)
 	try:
 		return utcnow.astimezone(timezone(user_tz))
 	except UnknownTimeZoneError:
@@ -884,11 +989,7 @@
 @frappe.whitelist()
 def get_sla_doctypes():
 	doctypes = []
-	data = frappe.get_all('Service Level Agreement',
-		{'enabled': 1},
-		['document_type'],
-		distinct=1
-	)
+	data = frappe.get_all("Service Level Agreement", {"enabled": 1}, ["document_type"], distinct=1)
 
 	for entry in data:
 		doctypes.append(entry.document_type)
diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
index a34124f..4e00138 100644
--- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
@@ -20,51 +20,122 @@
 
 	def test_service_level_agreement(self):
 		# Default Service Level Agreement
-		create_default_service_level_agreement = create_service_level_agreement(default_service_level_agreement=1,
-			holiday_list="__Test Holiday List", entity_type=None, entity=None, response_time=14400, resolution_time=21600)
+		create_default_service_level_agreement = create_service_level_agreement(
+			default_service_level_agreement=1,
+			holiday_list="__Test Holiday List",
+			entity_type=None,
+			entity=None,
+			response_time=14400,
+			resolution_time=21600,
+		)
 
-		get_default_service_level_agreement = get_service_level_agreement(default_service_level_agreement=1)
+		get_default_service_level_agreement = get_service_level_agreement(
+			default_service_level_agreement=1
+		)
 
-		self.assertEqual(create_default_service_level_agreement.name, get_default_service_level_agreement.name)
-		self.assertEqual(create_default_service_level_agreement.entity_type, get_default_service_level_agreement.entity_type)
-		self.assertEqual(create_default_service_level_agreement.entity, get_default_service_level_agreement.entity)
-		self.assertEqual(create_default_service_level_agreement.default_service_level_agreement, get_default_service_level_agreement.default_service_level_agreement)
+		self.assertEqual(
+			create_default_service_level_agreement.name, get_default_service_level_agreement.name
+		)
+		self.assertEqual(
+			create_default_service_level_agreement.entity_type,
+			get_default_service_level_agreement.entity_type,
+		)
+		self.assertEqual(
+			create_default_service_level_agreement.entity, get_default_service_level_agreement.entity
+		)
+		self.assertEqual(
+			create_default_service_level_agreement.default_service_level_agreement,
+			get_default_service_level_agreement.default_service_level_agreement,
+		)
 
 		# Service Level Agreement for Customer
 		customer = create_customer()
-		create_customer_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0,
-			holiday_list="__Test Holiday List", entity_type="Customer", entity=customer,
-			response_time=7200, resolution_time=10800)
-		get_customer_service_level_agreement = get_service_level_agreement(entity_type="Customer", entity=customer)
+		create_customer_service_level_agreement = create_service_level_agreement(
+			default_service_level_agreement=0,
+			holiday_list="__Test Holiday List",
+			entity_type="Customer",
+			entity=customer,
+			response_time=7200,
+			resolution_time=10800,
+		)
+		get_customer_service_level_agreement = get_service_level_agreement(
+			entity_type="Customer", entity=customer
+		)
 
-		self.assertEqual(create_customer_service_level_agreement.name, get_customer_service_level_agreement.name)
-		self.assertEqual(create_customer_service_level_agreement.entity_type, get_customer_service_level_agreement.entity_type)
-		self.assertEqual(create_customer_service_level_agreement.entity, get_customer_service_level_agreement.entity)
-		self.assertEqual(create_customer_service_level_agreement.default_service_level_agreement, get_customer_service_level_agreement.default_service_level_agreement)
+		self.assertEqual(
+			create_customer_service_level_agreement.name, get_customer_service_level_agreement.name
+		)
+		self.assertEqual(
+			create_customer_service_level_agreement.entity_type,
+			get_customer_service_level_agreement.entity_type,
+		)
+		self.assertEqual(
+			create_customer_service_level_agreement.entity, get_customer_service_level_agreement.entity
+		)
+		self.assertEqual(
+			create_customer_service_level_agreement.default_service_level_agreement,
+			get_customer_service_level_agreement.default_service_level_agreement,
+		)
 
 		# Service Level Agreement for Customer Group
 		customer_group = create_customer_group()
-		create_customer_group_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0,
-			holiday_list="__Test Holiday List", entity_type="Customer Group", entity=customer_group,
-			response_time=7200, resolution_time=10800)
-		get_customer_group_service_level_agreement = get_service_level_agreement(entity_type="Customer Group", entity=customer_group)
+		create_customer_group_service_level_agreement = create_service_level_agreement(
+			default_service_level_agreement=0,
+			holiday_list="__Test Holiday List",
+			entity_type="Customer Group",
+			entity=customer_group,
+			response_time=7200,
+			resolution_time=10800,
+		)
+		get_customer_group_service_level_agreement = get_service_level_agreement(
+			entity_type="Customer Group", entity=customer_group
+		)
 
-		self.assertEqual(create_customer_group_service_level_agreement.name, get_customer_group_service_level_agreement.name)
-		self.assertEqual(create_customer_group_service_level_agreement.entity_type, get_customer_group_service_level_agreement.entity_type)
-		self.assertEqual(create_customer_group_service_level_agreement.entity, get_customer_group_service_level_agreement.entity)
-		self.assertEqual(create_customer_group_service_level_agreement.default_service_level_agreement, get_customer_group_service_level_agreement.default_service_level_agreement)
+		self.assertEqual(
+			create_customer_group_service_level_agreement.name,
+			get_customer_group_service_level_agreement.name,
+		)
+		self.assertEqual(
+			create_customer_group_service_level_agreement.entity_type,
+			get_customer_group_service_level_agreement.entity_type,
+		)
+		self.assertEqual(
+			create_customer_group_service_level_agreement.entity,
+			get_customer_group_service_level_agreement.entity,
+		)
+		self.assertEqual(
+			create_customer_group_service_level_agreement.default_service_level_agreement,
+			get_customer_group_service_level_agreement.default_service_level_agreement,
+		)
 
 		# Service Level Agreement for Territory
 		territory = create_territory()
-		create_territory_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0,
+		create_territory_service_level_agreement = create_service_level_agreement(
+			default_service_level_agreement=0,
 			holiday_list="__Test Holiday List",
-			entity_type="Territory", entity=territory, response_time=7200, resolution_time=10800)
-		get_territory_service_level_agreement = get_service_level_agreement(entity_type="Territory", entity=territory)
+			entity_type="Territory",
+			entity=territory,
+			response_time=7200,
+			resolution_time=10800,
+		)
+		get_territory_service_level_agreement = get_service_level_agreement(
+			entity_type="Territory", entity=territory
+		)
 
-		self.assertEqual(create_territory_service_level_agreement.name, get_territory_service_level_agreement.name)
-		self.assertEqual(create_territory_service_level_agreement.entity_type, get_territory_service_level_agreement.entity_type)
-		self.assertEqual(create_territory_service_level_agreement.entity, get_territory_service_level_agreement.entity)
-		self.assertEqual(create_territory_service_level_agreement.default_service_level_agreement, get_territory_service_level_agreement.default_service_level_agreement)
+		self.assertEqual(
+			create_territory_service_level_agreement.name, get_territory_service_level_agreement.name
+		)
+		self.assertEqual(
+			create_territory_service_level_agreement.entity_type,
+			get_territory_service_level_agreement.entity_type,
+		)
+		self.assertEqual(
+			create_territory_service_level_agreement.entity, get_territory_service_level_agreement.entity
+		)
+		self.assertEqual(
+			create_territory_service_level_agreement.default_service_level_agreement,
+			get_territory_service_level_agreement.default_service_level_agreement,
+		)
 
 	def test_custom_field_creation_for_sla_on_standard_dt(self):
 		# Default Service Level Agreement
@@ -72,10 +143,12 @@
 		lead_sla = create_service_level_agreement(
 			default_service_level_agreement=1,
 			holiday_list="__Test Holiday List",
-			entity_type=None, entity=None,
-			response_time=14400, resolution_time=21600,
+			entity_type=None,
+			entity=None,
+			response_time=14400,
+			resolution_time=21600,
 			doctype=doctype,
-			sla_fulfilled_on=[{"status": "Converted"}]
+			sla_fulfilled_on=[{"status": "Converted"}],
 		)
 
 		# check default SLA for lead
@@ -86,27 +159,35 @@
 		sla_fields = get_service_level_agreement_fields()
 
 		for field in sla_fields:
-			self.assertTrue(frappe.db.exists("Custom Field", {"dt": doctype, "fieldname": field.get("fieldname")}))
+			self.assertTrue(
+				frappe.db.exists("Custom Field", {"dt": doctype, "fieldname": field.get("fieldname")})
+			)
 
 	def test_docfield_creation_for_sla_on_custom_dt(self):
 		doctype = create_custom_doctype()
 		sla = create_service_level_agreement(
 			default_service_level_agreement=1,
 			holiday_list="__Test Holiday List",
-			entity_type=None, entity=None,
-			response_time=14400, resolution_time=21600,
-			doctype=doctype.name
+			entity_type=None,
+			entity=None,
+			response_time=14400,
+			resolution_time=21600,
+			doctype=doctype.name,
 		)
 
 		# check default SLA for custom dt
-		default_sla = get_service_level_agreement(default_service_level_agreement=1, doctype=doctype.name)
+		default_sla = get_service_level_agreement(
+			default_service_level_agreement=1, doctype=doctype.name
+		)
 		self.assertEqual(sla.name, default_sla.name)
 
 		# check SLA docfields created
 		sla_fields = get_service_level_agreement_fields()
 
 		for field in sla_fields:
-			self.assertTrue(frappe.db.exists("DocField", {"fieldname": field.get("fieldname"), "parent": doctype.name}))
+			self.assertTrue(
+				frappe.db.exists("DocField", {"fieldname": field.get("fieldname"), "parent": doctype.name})
+			)
 
 	def test_sla_application(self):
 		# Default Service Level Agreement
@@ -114,10 +195,12 @@
 		lead_sla = create_service_level_agreement(
 			default_service_level_agreement=1,
 			holiday_list="__Test Holiday List",
-			entity_type=None, entity=None,
-			response_time=14400, resolution_time=21600,
+			entity_type=None,
+			entity=None,
+			response_time=14400,
+			resolution_time=21600,
 			doctype=doctype,
-			sla_fulfilled_on=[{"status": "Converted"}]
+			sla_fulfilled_on=[{"status": "Converted"}],
 		)
 
 		# make lead with default SLA
@@ -130,21 +213,23 @@
 
 		frappe.flags.current_time = datetime.datetime(2019, 3, 4, 15, 0)
 		lead.reload()
-		lead.status = 'Converted'
+		lead.status = "Converted"
 		lead.save()
 
-		self.assertEqual(lead.agreement_status, 'Fulfilled')
+		self.assertEqual(lead.agreement_status, "Fulfilled")
 
 	def test_hold_time(self):
 		doctype = "Lead"
 		create_service_level_agreement(
 			default_service_level_agreement=1,
 			holiday_list="__Test Holiday List",
-			entity_type=None, entity=None,
-			response_time=14400, resolution_time=21600,
+			entity_type=None,
+			entity=None,
+			response_time=14400,
+			resolution_time=21600,
 			doctype=doctype,
 			sla_fulfilled_on=[{"status": "Converted"}],
-			pause_sla_on=[{"status": "Replied"}]
+			pause_sla_on=[{"status": "Replied"}],
 		)
 
 		creation = datetime.datetime(2020, 3, 4, 4, 0)
@@ -152,7 +237,7 @@
 
 		frappe.flags.current_time = datetime.datetime(2020, 3, 4, 4, 15)
 		lead.reload()
-		lead.status = 'Replied'
+		lead.status = "Replied"
 		lead.save()
 
 		lead.reload()
@@ -160,7 +245,7 @@
 
 		frappe.flags.current_time = datetime.datetime(2020, 3, 4, 5, 5)
 		lead.reload()
-		lead.status = 'Converted'
+		lead.status = "Converted"
 		lead.save()
 
 		lead.reload()
@@ -172,12 +257,13 @@
 		create_service_level_agreement(
 			default_service_level_agreement=1,
 			holiday_list="__Test Holiday List",
-			entity_type=None, entity=None,
+			entity_type=None,
+			entity=None,
 			response_time=14400,
 			doctype=doctype,
 			sla_fulfilled_on=[{"status": "Replied"}],
 			pause_sla_on=[],
-			apply_sla_for_resolution=0
+			apply_sla_for_resolution=0,
 		)
 
 		creation = datetime.datetime(2019, 3, 4, 12, 0)
@@ -187,22 +273,23 @@
 		# failed with response time only
 		frappe.flags.current_time = datetime.datetime(2019, 3, 4, 16, 5)
 		lead.reload()
-		lead.status = 'Replied'
+		lead.status = "Replied"
 		lead.save()
 
 		lead.reload()
-		self.assertEqual(lead.agreement_status, 'Failed')
+		self.assertEqual(lead.agreement_status, "Failed")
 
 	def test_fulfilled_sla_for_response_only(self):
 		doctype = "Lead"
 		lead_sla = create_service_level_agreement(
 			default_service_level_agreement=1,
 			holiday_list="__Test Holiday List",
-			entity_type=None, entity=None,
+			entity_type=None,
+			entity=None,
 			response_time=14400,
 			doctype=doctype,
 			sla_fulfilled_on=[{"status": "Replied"}],
-			apply_sla_for_resolution=0
+			apply_sla_for_resolution=0,
 		)
 
 		# fulfilled with response time only
@@ -214,11 +301,11 @@
 
 		frappe.flags.current_time = datetime.datetime(2019, 3, 4, 15, 30)
 		lead.reload()
-		lead.status = 'Replied'
+		lead.status = "Replied"
 		lead.save()
 
 		lead.reload()
-		self.assertEqual(lead.agreement_status, 'Fulfilled')
+		self.assertEqual(lead.agreement_status, "Fulfilled")
 
 	def test_service_level_agreement_filters(self):
 		doctype = "Lead"
@@ -226,29 +313,30 @@
 			default_service_level_agreement=0,
 			doctype=doctype,
 			holiday_list="__Test Holiday List",
-			entity_type=None, entity=None,
+			entity_type=None,
+			entity=None,
 			condition='doc.source == "Test Source"',
 			response_time=14400,
 			sla_fulfilled_on=[{"status": "Replied"}],
-			apply_sla_for_resolution=0
+			apply_sla_for_resolution=0,
 		)
 		creation = datetime.datetime(2019, 3, 4, 12, 0)
 		lead = make_lead(creation=creation, index=4)
-		applied_sla = frappe.db.get_value('Lead', lead.name, 'service_level_agreement')
+		applied_sla = frappe.db.get_value("Lead", lead.name, "service_level_agreement")
 		self.assertFalse(applied_sla)
 
-		source = frappe.get_doc(doctype='Lead Source', source_name='Test Source')
+		source = frappe.get_doc(doctype="Lead Source", source_name="Test Source")
 		source.insert(ignore_if_duplicate=True)
 		lead.source = "Test Source"
 		lead.save()
-		applied_sla = frappe.db.get_value('Lead', lead.name, 'service_level_agreement')
+		applied_sla = frappe.db.get_value("Lead", lead.name, "service_level_agreement")
 		self.assertEqual(applied_sla, lead_sla.name)
 
 		# check if SLA is removed if condition fails
 		lead.reload()
 		lead.source = None
 		lead.save()
-		applied_sla = frappe.db.get_value('Lead', lead.name, 'service_level_agreement')
+		applied_sla = frappe.db.get_value("Lead", lead.name, "service_level_agreement")
 		self.assertFalse(applied_sla)
 
 	def tearDown(self):
@@ -256,130 +344,150 @@
 			frappe.delete_doc("Service Level Agreement", d.name, force=1)
 
 
-def get_service_level_agreement(default_service_level_agreement=None, entity_type=None, entity=None, doctype="Issue"):
+def get_service_level_agreement(
+	default_service_level_agreement=None, entity_type=None, entity=None, doctype="Issue"
+):
 	if default_service_level_agreement:
-		filters = {"default_service_level_agreement": default_service_level_agreement, "document_type": doctype}
+		filters = {
+			"default_service_level_agreement": default_service_level_agreement,
+			"document_type": doctype,
+		}
 	else:
 		filters = {"entity_type": entity_type, "entity": entity}
 
 	service_level_agreement = frappe.get_doc("Service Level Agreement", filters)
 	return service_level_agreement
 
-def create_service_level_agreement(default_service_level_agreement, holiday_list, response_time, entity_type,
-	entity, resolution_time=0, doctype="Issue", condition="", sla_fulfilled_on=[], pause_sla_on=[], apply_sla_for_resolution=1,
-	service_level=None, start_time="10:00:00", end_time="18:00:00"):
+
+def create_service_level_agreement(
+	default_service_level_agreement,
+	holiday_list,
+	response_time,
+	entity_type,
+	entity,
+	resolution_time=0,
+	doctype="Issue",
+	condition="",
+	sla_fulfilled_on=[],
+	pause_sla_on=[],
+	apply_sla_for_resolution=1,
+	service_level=None,
+	start_time="10:00:00",
+	end_time="18:00:00",
+):
 
 	make_holiday_list()
 	make_priorities()
 
 	if not sla_fulfilled_on:
-		sla_fulfilled_on = [
-			{"status": "Resolved"},
-			{"status": "Closed"}
-		]
+		sla_fulfilled_on = [{"status": "Resolved"}, {"status": "Closed"}]
 
 	pause_sla_on = [{"status": "Replied"}] if doctype == "Issue" else pause_sla_on
 
-	service_level_agreement = frappe._dict({
-		"doctype": "Service Level Agreement",
-		"enabled": 1,
-		"document_type": doctype,
-		"service_level": service_level or "__Test {} SLA".format(entity_type if entity_type else "Default"),
-		"default_service_level_agreement": default_service_level_agreement,
-		"condition": condition,
-		"default_priority": "Medium",
-		"holiday_list": holiday_list,
-		"entity_type": entity_type,
-		"entity": entity,
-		"start_date": frappe.utils.getdate(),
-		"end_date": frappe.utils.add_to_date(frappe.utils.getdate(), days=100),
-		"apply_sla_for_resolution": apply_sla_for_resolution,
-		"priorities": [
-			{
-				"priority": "Low",
-				"response_time": response_time,
-				"resolution_time": resolution_time,
-			},
-			{
-				"priority": "Medium",
-				"response_time": response_time,
-				"default_priority": 1,
-				"resolution_time": resolution_time,
-			},
-			{
-				"priority": "High",
-				"response_time": response_time,
-				"resolution_time": resolution_time,
-			}
-		],
-		"sla_fulfilled_on": sla_fulfilled_on,
-		"pause_sla_on": pause_sla_on,
-		"support_and_resolution": [
-			{
-				"workday": "Monday",
-				"start_time": start_time,
-				"end_time": end_time,
-			},
-			{
-				"workday": "Tuesday",
-				"start_time": start_time,
-				"end_time": end_time,
-			},
-			{
-				"workday": "Wednesday",
-				"start_time": start_time,
-				"end_time": end_time,
-			},
-			{
-				"workday": "Thursday",
-				"start_time": start_time,
-				"end_time": end_time,
-			},
-			{
-				"workday": "Friday",
-				"start_time": start_time,
-				"end_time": end_time,
-			}
-		]
-	})
+	service_level_agreement = frappe._dict(
+		{
+			"doctype": "Service Level Agreement",
+			"enabled": 1,
+			"document_type": doctype,
+			"service_level": service_level
+			or "__Test {} SLA".format(entity_type if entity_type else "Default"),
+			"default_service_level_agreement": default_service_level_agreement,
+			"condition": condition,
+			"default_priority": "Medium",
+			"holiday_list": holiday_list,
+			"entity_type": entity_type,
+			"entity": entity,
+			"start_date": frappe.utils.getdate(),
+			"end_date": frappe.utils.add_to_date(frappe.utils.getdate(), days=100),
+			"apply_sla_for_resolution": apply_sla_for_resolution,
+			"priorities": [
+				{
+					"priority": "Low",
+					"response_time": response_time,
+					"resolution_time": resolution_time,
+				},
+				{
+					"priority": "Medium",
+					"response_time": response_time,
+					"default_priority": 1,
+					"resolution_time": resolution_time,
+				},
+				{
+					"priority": "High",
+					"response_time": response_time,
+					"resolution_time": resolution_time,
+				},
+			],
+			"sla_fulfilled_on": sla_fulfilled_on,
+			"pause_sla_on": pause_sla_on,
+			"support_and_resolution": [
+				{
+					"workday": "Monday",
+					"start_time": start_time,
+					"end_time": end_time,
+				},
+				{
+					"workday": "Tuesday",
+					"start_time": start_time,
+					"end_time": end_time,
+				},
+				{
+					"workday": "Wednesday",
+					"start_time": start_time,
+					"end_time": end_time,
+				},
+				{
+					"workday": "Thursday",
+					"start_time": start_time,
+					"end_time": end_time,
+				},
+				{
+					"workday": "Friday",
+					"start_time": start_time,
+					"end_time": end_time,
+				},
+			],
+		}
+	)
 
 	filters = {
 		"default_service_level_agreement": service_level_agreement.default_service_level_agreement,
-		"service_level": service_level_agreement.service_level
+		"service_level": service_level_agreement.service_level,
 	}
 
 	if not default_service_level_agreement:
-		filters.update({
-			"entity_type": entity_type,
-			"entity": entity
-		})
+		filters.update({"entity_type": entity_type, "entity": entity})
 
 	sla = frappe.db.exists("Service Level Agreement", filters)
 	if sla:
 		frappe.delete_doc("Service Level Agreement", sla, force=1)
 
-	return frappe.get_doc(service_level_agreement).insert(ignore_permissions=True, ignore_if_duplicate=True)
+	return frappe.get_doc(service_level_agreement).insert(
+		ignore_permissions=True, ignore_if_duplicate=True
+	)
 
 
 def create_customer():
-	customer = frappe.get_doc({
-		"doctype": "Customer",
-		"customer_name": "_Test Customer",
-		"customer_group": "Commercial",
-		"customer_type": "Individual",
-		"territory": "Rest Of The World"
-	})
+	customer = frappe.get_doc(
+		{
+			"doctype": "Customer",
+			"customer_name": "_Test Customer",
+			"customer_group": "Commercial",
+			"customer_type": "Individual",
+			"territory": "Rest Of The World",
+		}
+	)
 	if not frappe.db.exists("Customer", "_Test Customer"):
 		customer.insert(ignore_permissions=True)
 		return customer.name
 	else:
 		return frappe.db.exists("Customer", "_Test Customer")
 
+
 def create_customer_group():
-	customer_group = frappe.get_doc({
-		"doctype": "Customer Group",
-		"customer_group_name": "_Test SLA Customer Group"
-	})
+	customer_group = frappe.get_doc(
+		{"doctype": "Customer Group", "customer_group_name": "_Test SLA Customer Group"}
+	)
 
 	if not frappe.db.exists("Customer Group", {"customer_group_name": "_Test SLA Customer Group"}):
 		customer_group.insert()
@@ -387,11 +495,14 @@
 	else:
 		return frappe.db.exists("Customer Group", {"customer_group_name": "_Test SLA Customer Group"})
 
+
 def create_territory():
-	territory = frappe.get_doc({
-		"doctype": "Territory",
-		"territory_name": "_Test SLA Territory",
-	})
+	territory = frappe.get_doc(
+		{
+			"doctype": "Territory",
+			"territory_name": "_Test SLA Territory",
+		}
+	)
 
 	if not frappe.db.exists("Territory", {"territory_name": "_Test SLA Territory"}):
 		territory.insert()
@@ -399,102 +510,116 @@
 	else:
 		return frappe.db.exists("Territory", {"territory_name": "_Test SLA Territory"})
 
+
 def create_service_level_agreements_for_issues():
-	create_service_level_agreement(default_service_level_agreement=1, holiday_list="__Test Holiday List",
-		entity_type=None, entity=None, response_time=14400, resolution_time=21600)
+	create_service_level_agreement(
+		default_service_level_agreement=1,
+		holiday_list="__Test Holiday List",
+		entity_type=None,
+		entity=None,
+		response_time=14400,
+		resolution_time=21600,
+	)
 
 	create_customer()
-	create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List",
-		entity_type="Customer", entity="_Test Customer", response_time=7200, resolution_time=10800)
+	create_service_level_agreement(
+		default_service_level_agreement=0,
+		holiday_list="__Test Holiday List",
+		entity_type="Customer",
+		entity="_Test Customer",
+		response_time=7200,
+		resolution_time=10800,
+	)
 
 	create_customer_group()
-	create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List",
-		entity_type="Customer Group", entity="_Test SLA Customer Group", response_time=7200, resolution_time=10800)
+	create_service_level_agreement(
+		default_service_level_agreement=0,
+		holiday_list="__Test Holiday List",
+		entity_type="Customer Group",
+		entity="_Test SLA Customer Group",
+		response_time=7200,
+		resolution_time=10800,
+	)
 
 	create_territory()
-	create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List",
-		entity_type="Territory", entity="_Test SLA Territory", response_time=7200, resolution_time=10800)
+	create_service_level_agreement(
+		default_service_level_agreement=0,
+		holiday_list="__Test Holiday List",
+		entity_type="Territory",
+		entity="_Test SLA Territory",
+		response_time=7200,
+		resolution_time=10800,
+	)
 
 	create_service_level_agreement(
-		default_service_level_agreement=0, holiday_list="__Test Holiday List",
-		entity_type=None, entity=None, response_time=14400, resolution_time=21600,
-		service_level="24-hour-SLA", start_time="00:00:00", end_time="23:59:59",
-		condition="doc.issue_type == 'Critical'"
+		default_service_level_agreement=0,
+		holiday_list="__Test Holiday List",
+		entity_type=None,
+		entity=None,
+		response_time=14400,
+		resolution_time=21600,
+		service_level="24-hour-SLA",
+		start_time="00:00:00",
+		end_time="23:59:59",
+		condition="doc.issue_type == 'Critical'",
 	)
 
+
 def make_holiday_list():
 	holiday_list = frappe.db.exists("Holiday List", "__Test Holiday List")
 	if not holiday_list:
-		holiday_list = frappe.get_doc({
-			"doctype": "Holiday List",
-			"holiday_list_name": "__Test Holiday List",
-			"from_date": "2019-01-01",
-			"to_date": "2019-12-31",
-			"holidays": [
-				{
-					"description": "Test Holiday 1",
-					"holiday_date": "2019-03-05"
-				},
-				{
-					"description": "Test Holiday 2",
-					"holiday_date": "2019-03-07"
-				},
-				{
-					"description": "Test Holiday 3",
-					"holiday_date": "2019-02-11"
-				},
-			]
-		}).insert()
+		holiday_list = frappe.get_doc(
+			{
+				"doctype": "Holiday List",
+				"holiday_list_name": "__Test Holiday List",
+				"from_date": "2019-01-01",
+				"to_date": "2019-12-31",
+				"holidays": [
+					{"description": "Test Holiday 1", "holiday_date": "2019-03-05"},
+					{"description": "Test Holiday 2", "holiday_date": "2019-03-07"},
+					{"description": "Test Holiday 3", "holiday_date": "2019-02-11"},
+				],
+			}
+		).insert()
+
 
 def create_custom_doctype():
 	if not frappe.db.exists("DocType", "Test SLA on Custom Dt"):
-		doc = frappe.get_doc({
+		doc = frappe.get_doc(
+			{
 				"doctype": "DocType",
 				"module": "Support",
 				"custom": 1,
 				"fields": [
-					{
-						"label": "Date",
-						"fieldname": "date",
-						"fieldtype": "Date"
-					},
-					{
-						"label": "Description",
-						"fieldname": "desc",
-						"fieldtype": "Long Text"
-					},
-					{
-						"label": "Email ID",
-						"fieldname": "email_id",
-						"fieldtype": "Link",
-						"options": "Customer"
-					},
+					{"label": "Date", "fieldname": "date", "fieldtype": "Date"},
+					{"label": "Description", "fieldname": "desc", "fieldtype": "Long Text"},
+					{"label": "Email ID", "fieldname": "email_id", "fieldtype": "Link", "options": "Customer"},
 					{
 						"label": "Status",
 						"fieldname": "status",
 						"fieldtype": "Select",
-						"options": "Open\nReplied\nClosed"
-					}
+						"options": "Open\nReplied\nClosed",
+					},
 				],
-				"permissions": [{
-					"role": "System Manager",
-					"read": 1,
-					"write": 1
-				}],
+				"permissions": [{"role": "System Manager", "read": 1, "write": 1}],
 				"name": "Test SLA on Custom Dt",
-			})
+			}
+		)
 		doc.insert()
 		return doc
 	else:
 		return frappe.get_doc("DocType", "Test SLA on Custom Dt")
 
+
 def make_lead(creation=None, index=0):
-	return frappe.get_doc({
-		"doctype": "Lead",
-		"email_id": "test_lead1@example{0}.com".format(index),
-		"lead_name": "_Test Lead {0}".format(index),
-		"status": "Open",
-		"creation": creation,
-		"service_level_agreement_creation": creation,
-		"priority": "Medium"
-	}).insert(ignore_permissions=True)
+	return frappe.get_doc(
+		{
+			"doctype": "Lead",
+			"email_id": "test_lead1@example{0}.com".format(index),
+			"lead_name": "_Test Lead {0}".format(index),
+			"status": "Open",
+			"creation": creation,
+			"service_level_agreement_creation": creation,
+			"priority": "Medium",
+		}
+	).insert(ignore_permissions=True)
diff --git a/erpnext/support/doctype/warranty_claim/test_warranty_claim.py b/erpnext/support/doctype/warranty_claim/test_warranty_claim.py
index f022d55..19e2349 100644
--- a/erpnext/support/doctype/warranty_claim/test_warranty_claim.py
+++ b/erpnext/support/doctype/warranty_claim/test_warranty_claim.py
@@ -5,7 +5,8 @@
 
 import frappe
 
-test_records = frappe.get_test_records('Warranty Claim')
+test_records = frappe.get_test_records("Warranty Claim")
+
 
 class TestWarrantyClaim(unittest.TestCase):
 	pass
diff --git a/erpnext/support/doctype/warranty_claim/warranty_claim.py b/erpnext/support/doctype/warranty_claim/warranty_claim.py
index 87e9541..5e2ea06 100644
--- a/erpnext/support/doctype/warranty_claim/warranty_claim.py
+++ b/erpnext/support/doctype/warranty_claim/warranty_claim.py
@@ -2,7 +2,6 @@
 # License: GNU General Public License v3. See license.txt
 
 
-
 import frappe
 from frappe import _, session
 from frappe.utils import now_datetime
@@ -15,27 +14,33 @@
 		return _("{0}: From {1}").format(self.status, self.customer_name)
 
 	def validate(self):
-		if session['user'] != 'Guest' and not self.customer:
+		if session["user"] != "Guest" and not self.customer:
 			frappe.throw(_("Customer is required"))
 
-		if self.status=="Closed" and not self.resolution_date and \
-			frappe.db.get_value("Warranty Claim", self.name, "status")!="Closed":
+		if (
+			self.status == "Closed"
+			and not self.resolution_date
+			and frappe.db.get_value("Warranty Claim", self.name, "status") != "Closed"
+		):
 			self.resolution_date = now_datetime()
 
 	def on_cancel(self):
-		lst = frappe.db.sql("""select t1.name
+		lst = frappe.db.sql(
+			"""select t1.name
 			from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2
 			where t2.parent = t1.name and t2.prevdoc_docname = %s and	t1.docstatus!=2""",
-			(self.name))
+			(self.name),
+		)
 		if lst:
-			lst1 = ','.join(x[0] for x in lst)
+			lst1 = ",".join(x[0] for x in lst)
 			frappe.throw(_("Cancel Material Visit {0} before cancelling this Warranty Claim").format(lst1))
 		else:
-			frappe.db.set(self, 'status', 'Cancelled')
+			frappe.db.set(self, "status", "Cancelled")
 
 	def on_update(self):
 		pass
 
+
 @frappe.whitelist()
 def make_maintenance_visit(source_name, target_doc=None):
 	from frappe.model.mapper import get_mapped_doc, map_child_doc
@@ -44,25 +49,25 @@
 		target_doc.prevdoc_doctype = source_parent.doctype
 		target_doc.prevdoc_docname = source_parent.name
 
-	visit = frappe.db.sql("""select t1.name
+	visit = frappe.db.sql(
+		"""select t1.name
 		from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2
 		where t2.parent=t1.name and t2.prevdoc_docname=%s
-		and t1.docstatus=1 and t1.completion_status='Fully Completed'""", source_name)
+		and t1.docstatus=1 and t1.completion_status='Fully Completed'""",
+		source_name,
+	)
 
 	if not visit:
-		target_doc = get_mapped_doc("Warranty Claim", source_name, {
-			"Warranty Claim": {
-				"doctype": "Maintenance Visit",
-				"field_map": {}
-			}
-		}, target_doc)
+		target_doc = get_mapped_doc(
+			"Warranty Claim",
+			source_name,
+			{"Warranty Claim": {"doctype": "Maintenance Visit", "field_map": {}}},
+			target_doc,
+		)
 
 		source_doc = frappe.get_doc("Warranty Claim", source_name)
 		if source_doc.get("item_code"):
-			table_map = {
-				"doctype": "Maintenance Visit Purpose",
-				"postprocess": _update_links
-			}
+			table_map = {"doctype": "Maintenance Visit Purpose", "postprocess": _update_links}
 			map_child_doc(source_doc, target_doc, table_map, source_doc)
 
 		return target_doc
diff --git a/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.py b/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.py
index 2ab0fb8..57fa7bf 100644
--- a/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.py
+++ b/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.py
@@ -3,25 +3,22 @@
 
 
 import frappe
+from frappe import _
 
 
 def execute(filters=None):
 	columns = [
+		{"fieldname": "creation_date", "label": _("Date"), "fieldtype": "Date", "width": 300},
 		{
-			'fieldname': 'creation_date',
-			'label': 'Date',
-			'fieldtype': 'Date',
-			'width': 300
-		},
-		{
-			'fieldname': 'first_response_time',
-			'fieldtype': 'Duration',
-			'label': 'First Response Time',
-			'width': 300
+			"fieldname": "first_response_time",
+			"fieldtype": "Duration",
+			"label": _("First Response Time"),
+			"width": 300,
 		},
 	]
 
-	data = frappe.db.sql('''
+	data = frappe.db.sql(
+		"""
 		SELECT
 			date(creation) as creation_date,
 			avg(first_response_time) as avg_response_time
@@ -31,6 +28,8 @@
 			and first_response_time > 0
 		GROUP BY creation_date
 		ORDER BY creation_date desc
-	''', (filters.from_date, filters.to_date))
+	""",
+		(filters.from_date, filters.to_date),
+	)
 
 	return columns, data
diff --git a/erpnext/support/report/issue_analytics/issue_analytics.py b/erpnext/support/report/issue_analytics/issue_analytics.py
index 056f2e0..00ba25a 100644
--- a/erpnext/support/report/issue_analytics/issue_analytics.py
+++ b/erpnext/support/report/issue_analytics/issue_analytics.py
@@ -14,6 +14,7 @@
 def execute(filters=None):
 	return IssueAnalytics(filters).run()
 
+
 class IssueAnalytics(object):
 	def __init__(self, filters=None):
 		"""Issue Analytics Report"""
@@ -30,101 +31,98 @@
 	def get_columns(self):
 		self.columns = []
 
-		if self.filters.based_on == 'Customer':
-			self.columns.append({
-				'label': _('Customer'),
-				'options': 'Customer',
-				'fieldname': 'customer',
-				'fieldtype': 'Link',
-				'width': 200
-			})
+		if self.filters.based_on == "Customer":
+			self.columns.append(
+				{
+					"label": _("Customer"),
+					"options": "Customer",
+					"fieldname": "customer",
+					"fieldtype": "Link",
+					"width": 200,
+				}
+			)
 
-		elif self.filters.based_on == 'Assigned To':
-			self.columns.append({
-				'label': _('User'),
-				'fieldname': 'user',
-				'fieldtype': 'Link',
-				'options': 'User',
-				'width': 200
-			})
+		elif self.filters.based_on == "Assigned To":
+			self.columns.append(
+				{"label": _("User"), "fieldname": "user", "fieldtype": "Link", "options": "User", "width": 200}
+			)
 
-		elif self.filters.based_on == 'Issue Type':
-			self.columns.append({
-				'label': _('Issue Type'),
-				'fieldname': 'issue_type',
-				'fieldtype': 'Link',
-				'options': 'Issue Type',
-				'width': 200
-			})
+		elif self.filters.based_on == "Issue Type":
+			self.columns.append(
+				{
+					"label": _("Issue Type"),
+					"fieldname": "issue_type",
+					"fieldtype": "Link",
+					"options": "Issue Type",
+					"width": 200,
+				}
+			)
 
-		elif self.filters.based_on == 'Issue Priority':
-			self.columns.append({
-				'label': _('Issue Priority'),
-				'fieldname': 'priority',
-				'fieldtype': 'Link',
-				'options': 'Issue Priority',
-				'width': 200
-			})
+		elif self.filters.based_on == "Issue Priority":
+			self.columns.append(
+				{
+					"label": _("Issue Priority"),
+					"fieldname": "priority",
+					"fieldtype": "Link",
+					"options": "Issue Priority",
+					"width": 200,
+				}
+			)
 
 		for end_date in self.periodic_daterange:
 			period = self.get_period(end_date)
-			self.columns.append({
-				'label': _(period),
-				'fieldname': scrub(period),
-				'fieldtype': 'Int',
-				'width': 120
-			})
+			self.columns.append(
+				{"label": _(period), "fieldname": scrub(period), "fieldtype": "Int", "width": 120}
+			)
 
-		self.columns.append({
-			'label': _('Total'),
-			'fieldname': 'total',
-			'fieldtype': 'Int',
-			'width': 120
-		})
+		self.columns.append(
+			{"label": _("Total"), "fieldname": "total", "fieldtype": "Int", "width": 120}
+		)
 
 	def get_data(self):
 		self.get_issues()
 		self.get_rows()
 
 	def get_period(self, date):
-		months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+		months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
 
-		if self.filters.range == 'Weekly':
-			period = 'Week ' + str(date.isocalendar()[1])
-		elif self.filters.range == 'Monthly':
+		if self.filters.range == "Weekly":
+			period = "Week " + str(date.isocalendar()[1])
+		elif self.filters.range == "Monthly":
 			period = str(months[date.month - 1])
-		elif self.filters.range == 'Quarterly':
-			period = 'Quarter ' + str(((date.month - 1) // 3) + 1)
+		elif self.filters.range == "Quarterly":
+			period = "Quarter " + str(((date.month - 1) // 3) + 1)
 		else:
 			year = get_fiscal_year(date, self.filters.company)
 			period = str(year[0])
 
-		if getdate(self.filters.from_date).year != getdate(self.filters.to_date).year and self.filters.range != 'Yearly':
-			period += ' ' + str(date.year)
+		if (
+			getdate(self.filters.from_date).year != getdate(self.filters.to_date).year
+			and self.filters.range != "Yearly"
+		):
+			period += " " + str(date.year)
 
 		return period
 
 	def get_period_date_ranges(self):
 		from dateutil.relativedelta import MO, relativedelta
+
 		from_date, to_date = getdate(self.filters.from_date), getdate(self.filters.to_date)
 
-		increment = {
-			'Monthly': 1,
-			'Quarterly': 3,
-			'Half-Yearly': 6,
-			'Yearly': 12
-		}.get(self.filters.range, 1)
+		increment = {"Monthly": 1, "Quarterly": 3, "Half-Yearly": 6, "Yearly": 12}.get(
+			self.filters.range, 1
+		)
 
-		if self.filters.range in ['Monthly', 'Quarterly']:
+		if self.filters.range in ["Monthly", "Quarterly"]:
 			from_date = from_date.replace(day=1)
-		elif self.filters.range == 'Yearly':
+		elif self.filters.range == "Yearly":
 			from_date = get_fiscal_year(from_date)[1]
 		else:
 			from_date = from_date + relativedelta(from_date, weekday=MO(-1))
 
 		self.periodic_daterange = []
 		for dummy in range(1, 53):
-			if self.filters.range == 'Weekly':
+			if self.filters.range == "Weekly":
 				period_end_date = add_days(from_date, 6)
 			else:
 				period_end_date = add_to_date(from_date, months=increment, days=-1)
@@ -141,25 +139,26 @@
 	def get_issues(self):
 		filters = self.get_common_filters()
 		self.field_map = {
-			'Customer': 'customer',
-			'Issue Type': 'issue_type',
-			'Issue Priority': 'priority',
-			'Assigned To': '_assign'
+			"Customer": "customer",
+			"Issue Type": "issue_type",
+			"Issue Priority": "priority",
+			"Assigned To": "_assign",
 		}
 
-		self.entries = frappe.db.get_all('Issue',
-			fields=[self.field_map.get(self.filters.based_on), 'name', 'opening_date'],
-			filters=filters
+		self.entries = frappe.db.get_all(
+			"Issue",
+			fields=[self.field_map.get(self.filters.based_on), "name", "opening_date"],
+			filters=filters,
 		)
 
 	def get_common_filters(self):
 		filters = {}
-		filters['opening_date'] = ('between', [self.filters.from_date, self.filters.to_date])
+		filters["opening_date"] = ("between", [self.filters.from_date, self.filters.to_date])
 
-		if self.filters.get('assigned_to'):
-			filters['_assign'] = ('like', '%' + self.filters.get('assigned_to') + '%')
+		if self.filters.get("assigned_to"):
+			filters["_assign"] = ("like", "%" + self.filters.get("assigned_to") + "%")
 
-		for entry in ['company', 'status', 'priority', 'customer', 'project']:
+		for entry in ["company", "status", "priority", "customer", "project"]:
 			if self.filters.get(entry):
 				filters[entry] = self.filters.get(entry)
 
@@ -170,14 +169,14 @@
 		self.get_periodic_data()
 
 		for entity, period_data in self.issue_periodic_data.items():
-			if self.filters.based_on == 'Customer':
-				row = {'customer': entity}
-			elif self.filters.based_on == 'Assigned To':
-				row = {'user': entity}
-			elif self.filters.based_on == 'Issue Type':
-				row = {'issue_type': entity}
-			elif self.filters.based_on == 'Issue Priority':
-				row = {'priority': entity}
+			if self.filters.based_on == "Customer":
+				row = {"customer": entity}
+			elif self.filters.based_on == "Assigned To":
+				row = {"user": entity}
+			elif self.filters.based_on == "Issue Type":
+				row = {"issue_type": entity}
+			elif self.filters.based_on == "Issue Priority":
+				row = {"priority": entity}
 
 			total = 0
 			for end_date in self.periodic_daterange:
@@ -186,7 +185,7 @@
 				row[scrub(period)] = amount
 				total += amount
 
-			row['total'] = total
+			row["total"] = total
 
 			self.data.append(row)
 
@@ -194,9 +193,9 @@
 		self.issue_periodic_data = frappe._dict()
 
 		for d in self.entries:
-			period = self.get_period(d.get('opening_date'))
+			period = self.get_period(d.get("opening_date"))
 
-			if self.filters.based_on == 'Assigned To':
+			if self.filters.based_on == "Assigned To":
 				if d._assign:
 					for entry in json.loads(d._assign):
 						self.issue_periodic_data.setdefault(entry, frappe._dict()).setdefault(period, 0.0)
@@ -206,18 +205,12 @@
 				field = self.field_map.get(self.filters.based_on)
 				value = d.get(field)
 				if not value:
-					value = _('Not Specified')
+					value = _("Not Specified")
 
 				self.issue_periodic_data.setdefault(value, frappe._dict()).setdefault(period, 0.0)
 				self.issue_periodic_data[value][period] += 1
 
 	def get_chart_data(self):
 		length = len(self.columns)
-		labels = [d.get('label') for d in self.columns[1:length-1]]
-		self.chart = {
-			'data': {
-				'labels': labels,
-				'datasets': []
-			},
-			'type': 'line'
-		}
+		labels = [d.get("label") for d in self.columns[1 : length - 1]]
+		self.chart = {"data": {"labels": labels, "datasets": []}, "type": "line"}
diff --git a/erpnext/support/report/issue_analytics/test_issue_analytics.py b/erpnext/support/report/issue_analytics/test_issue_analytics.py
index ba4dc54..169392e 100644
--- a/erpnext/support/report/issue_analytics/test_issue_analytics.py
+++ b/erpnext/support/report/issue_analytics/test_issue_analytics.py
@@ -10,7 +10,8 @@
 )
 from erpnext.support.report.issue_analytics.issue_analytics import execute
 
-months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+
 
 class TestIssueAnalytics(unittest.TestCase):
 	@classmethod
@@ -23,8 +24,8 @@
 		self.current_month = str(months[current_month_date.month - 1]).lower()
 		self.last_month = str(months[last_month_date.month - 1]).lower()
 		if current_month_date.year != last_month_date.year:
-			self.current_month += '_' + str(current_month_date.year)
-			self.last_month += '_' + str(last_month_date.year)
+			self.current_month += "_" + str(current_month_date.year)
+			self.last_month += "_" + str(last_month_date.year)
 
 	def test_issue_analytics(self):
 		create_service_level_agreements_for_issues()
@@ -38,146 +39,88 @@
 
 	def compare_result_for_customer(self):
 		filters = {
-			'company': '_Test Company',
-			'based_on': 'Customer',
-			'from_date': add_months(getdate(), -1),
-			'to_date': getdate(),
-			'range': 'Monthly'
+			"company": "_Test Company",
+			"based_on": "Customer",
+			"from_date": add_months(getdate(), -1),
+			"to_date": getdate(),
+			"range": "Monthly",
 		}
 
 		report = execute(filters)
 
 		expected_data = [
-			{
-				'customer': '__Test Customer 2',
-				self.last_month: 1.0,
-				self.current_month: 0.0,
-				'total': 1.0
-			},
-			{
-				'customer': '__Test Customer 1',
-				self.last_month: 0.0,
-				self.current_month: 1.0,
-				'total': 1.0
-			},
-			{
-				'customer': '__Test Customer',
-				self.last_month: 1.0,
-				self.current_month: 1.0,
-				'total': 2.0
-			}
+			{"customer": "__Test Customer 2", self.last_month: 1.0, self.current_month: 0.0, "total": 1.0},
+			{"customer": "__Test Customer 1", self.last_month: 0.0, self.current_month: 1.0, "total": 1.0},
+			{"customer": "__Test Customer", self.last_month: 1.0, self.current_month: 1.0, "total": 2.0},
 		]
 
-		self.assertEqual(expected_data, report[1]) # rows
-		self.assertEqual(len(report[0]), 4) # cols
+		self.assertEqual(expected_data, report[1])  # rows
+		self.assertEqual(len(report[0]), 4)  # cols
 
 	def compare_result_for_issue_type(self):
 		filters = {
-			'company': '_Test Company',
-			'based_on': 'Issue Type',
-			'from_date': add_months(getdate(), -1),
-			'to_date': getdate(),
-			'range': 'Monthly'
+			"company": "_Test Company",
+			"based_on": "Issue Type",
+			"from_date": add_months(getdate(), -1),
+			"to_date": getdate(),
+			"range": "Monthly",
 		}
 
 		report = execute(filters)
 
 		expected_data = [
-			{
-				'issue_type': 'Discomfort',
-				self.last_month: 1.0,
-				self.current_month: 0.0,
-				'total': 1.0
-			},
-			{
-				'issue_type': 'Service Request',
-				self.last_month: 0.0,
-				self.current_month: 1.0,
-				'total': 1.0
-			},
-			{
-				'issue_type': 'Bug',
-				self.last_month: 1.0,
-				self.current_month: 1.0,
-				'total': 2.0
-			}
+			{"issue_type": "Discomfort", self.last_month: 1.0, self.current_month: 0.0, "total": 1.0},
+			{"issue_type": "Service Request", self.last_month: 0.0, self.current_month: 1.0, "total": 1.0},
+			{"issue_type": "Bug", self.last_month: 1.0, self.current_month: 1.0, "total": 2.0},
 		]
 
-		self.assertEqual(expected_data, report[1]) # rows
-		self.assertEqual(len(report[0]), 4) # cols
+		self.assertEqual(expected_data, report[1])  # rows
+		self.assertEqual(len(report[0]), 4)  # cols
 
 	def compare_result_for_issue_priority(self):
 		filters = {
-			'company': '_Test Company',
-			'based_on': 'Issue Priority',
-			'from_date': add_months(getdate(), -1),
-			'to_date': getdate(),
-			'range': 'Monthly'
+			"company": "_Test Company",
+			"based_on": "Issue Priority",
+			"from_date": add_months(getdate(), -1),
+			"to_date": getdate(),
+			"range": "Monthly",
 		}
 
 		report = execute(filters)
 
 		expected_data = [
-			{
-				'priority': 'Medium',
-				self.last_month: 1.0,
-				self.current_month: 1.0,
-				'total': 2.0
-			},
-			{
-				'priority': 'Low',
-				self.last_month: 1.0,
-				self.current_month: 0.0,
-				'total': 1.0
-			},
-			{
-				'priority': 'High',
-				self.last_month: 0.0,
-				self.current_month: 1.0,
-				'total': 1.0
-			}
+			{"priority": "Medium", self.last_month: 1.0, self.current_month: 1.0, "total": 2.0},
+			{"priority": "Low", self.last_month: 1.0, self.current_month: 0.0, "total": 1.0},
+			{"priority": "High", self.last_month: 0.0, self.current_month: 1.0, "total": 1.0},
 		]
 
-		self.assertEqual(expected_data, report[1]) # rows
-		self.assertEqual(len(report[0]), 4) # cols
+		self.assertEqual(expected_data, report[1])  # rows
+		self.assertEqual(len(report[0]), 4)  # cols
 
 	def compare_result_for_assignment(self):
 		filters = {
-			'company': '_Test Company',
-			'based_on': 'Assigned To',
-			'from_date': add_months(getdate(), -1),
-			'to_date': getdate(),
-			'range': 'Monthly'
+			"company": "_Test Company",
+			"based_on": "Assigned To",
+			"from_date": add_months(getdate(), -1),
+			"to_date": getdate(),
+			"range": "Monthly",
 		}
 
 		report = execute(filters)
 
 		expected_data = [
-			{
-				'user': 'test@example.com',
-				self.last_month: 1.0,
-				self.current_month: 1.0,
-				'total': 2.0
-			},
-			{
-				'user': 'test1@example.com',
-				self.last_month: 2.0,
-				self.current_month: 1.0,
-				'total': 3.0
-			}
+			{"user": "test@example.com", self.last_month: 1.0, self.current_month: 1.0, "total": 2.0},
+			{"user": "test1@example.com", self.last_month: 2.0, self.current_month: 1.0, "total": 3.0},
 		]
 
-		self.assertEqual(expected_data, report[1]) # rows
-		self.assertEqual(len(report[0]), 4) # cols
+		self.assertEqual(expected_data, report[1])  # rows
+		self.assertEqual(len(report[0]), 4)  # cols
 
 
 def create_issue_types():
-	for entry in ['Bug', 'Service Request', 'Discomfort']:
-		if not frappe.db.exists('Issue Type', entry):
-			frappe.get_doc({
-				'doctype': 'Issue Type',
-				'__newname': entry
-			}).insert()
+	for entry in ["Bug", "Service Request", "Discomfort"]:
+		if not frappe.db.exists("Issue Type", entry):
+			frappe.get_doc({"doctype": "Issue Type", "__newname": entry}).insert()
 
 
 def create_records():
@@ -189,29 +132,15 @@
 	last_month_date = add_months(current_month_date, -1)
 
 	issue = make_issue(current_month_date, "__Test Customer", 2, "High", "Bug")
-	add_assignment({
-		"assign_to": ["test@example.com"],
-		"doctype": "Issue",
-		"name": issue.name
-	})
+	add_assignment({"assign_to": ["test@example.com"], "doctype": "Issue", "name": issue.name})
 
 	issue = make_issue(last_month_date, "__Test Customer", 2, "Low", "Bug")
-	add_assignment({
-		"assign_to": ["test1@example.com"],
-		"doctype": "Issue",
-		"name": issue.name
-	})
+	add_assignment({"assign_to": ["test1@example.com"], "doctype": "Issue", "name": issue.name})
 
 	issue = make_issue(current_month_date, "__Test Customer 1", 2, "Medium", "Service Request")
-	add_assignment({
-		"assign_to": ["test1@example.com"],
-		"doctype": "Issue",
-		"name": issue.name
-	})
+	add_assignment({"assign_to": ["test1@example.com"], "doctype": "Issue", "name": issue.name})
 
 	issue = make_issue(last_month_date, "__Test Customer 2", 2, "Medium", "Discomfort")
-	add_assignment({
-		"assign_to": ["test@example.com", "test1@example.com"],
-		"doctype": "Issue",
-		"name": issue.name
-	})
+	add_assignment(
+		{"assign_to": ["test@example.com", "test1@example.com"], "doctype": "Issue", "name": issue.name}
+	)
diff --git a/erpnext/support/report/issue_summary/issue_summary.py b/erpnext/support/report/issue_summary/issue_summary.py
index 67fe345..c80ce88 100644
--- a/erpnext/support/report/issue_summary/issue_summary.py
+++ b/erpnext/support/report/issue_summary/issue_summary.py
@@ -12,6 +12,7 @@
 def execute(filters=None):
 	return IssueSummary(filters).run()
 
+
 class IssueSummary(object):
 	def __init__(self, filters=None):
 		self.filters = frappe._dict(filters or {})
@@ -27,83 +28,78 @@
 	def get_columns(self):
 		self.columns = []
 
-		if self.filters.based_on == 'Customer':
-			self.columns.append({
-				'label': _('Customer'),
-				'options': 'Customer',
-				'fieldname': 'customer',
-				'fieldtype': 'Link',
-				'width': 200
-			})
+		if self.filters.based_on == "Customer":
+			self.columns.append(
+				{
+					"label": _("Customer"),
+					"options": "Customer",
+					"fieldname": "customer",
+					"fieldtype": "Link",
+					"width": 200,
+				}
+			)
 
-		elif self.filters.based_on == 'Assigned To':
-			self.columns.append({
-				'label': _('User'),
-				'fieldname': 'user',
-				'fieldtype': 'Link',
-				'options': 'User',
-				'width': 200
-			})
+		elif self.filters.based_on == "Assigned To":
+			self.columns.append(
+				{"label": _("User"), "fieldname": "user", "fieldtype": "Link", "options": "User", "width": 200}
+			)
 
-		elif self.filters.based_on == 'Issue Type':
-			self.columns.append({
-				'label': _('Issue Type'),
-				'fieldname': 'issue_type',
-				'fieldtype': 'Link',
-				'options': 'Issue Type',
-				'width': 200
-			})
+		elif self.filters.based_on == "Issue Type":
+			self.columns.append(
+				{
+					"label": _("Issue Type"),
+					"fieldname": "issue_type",
+					"fieldtype": "Link",
+					"options": "Issue Type",
+					"width": 200,
+				}
+			)
 
-		elif self.filters.based_on == 'Issue Priority':
-			self.columns.append({
-				'label': _('Issue Priority'),
-				'fieldname': 'priority',
-				'fieldtype': 'Link',
-				'options': 'Issue Priority',
-				'width': 200
-			})
+		elif self.filters.based_on == "Issue Priority":
+			self.columns.append(
+				{
+					"label": _("Issue Priority"),
+					"fieldname": "priority",
+					"fieldtype": "Link",
+					"options": "Issue Priority",
+					"width": 200,
+				}
+			)
 
-		self.statuses = ['Open', 'Replied', 'On Hold', 'Resolved', 'Closed']
+		self.statuses = ["Open", "Replied", "On Hold", "Resolved", "Closed"]
 		for status in self.statuses:
-			self.columns.append({
-				'label': _(status),
-				'fieldname': scrub(status),
-				'fieldtype': 'Int',
-				'width': 80
-			})
+			self.columns.append(
+				{"label": _(status), "fieldname": scrub(status), "fieldtype": "Int", "width": 80}
+			)
 
-		self.columns.append({
-			'label': _('Total Issues'),
-			'fieldname': 'total_issues',
-			'fieldtype': 'Int',
-			'width': 100
-		})
+		self.columns.append(
+			{"label": _("Total Issues"), "fieldname": "total_issues", "fieldtype": "Int", "width": 100}
+		)
 
 		self.sla_status_map = {
-			'SLA Failed': 'failed',
-			'SLA Fulfilled': 'fulfilled',
-			'First Response Due': 'first_response_due',
-			'Resolution Due': 'resolution_due'
+			"SLA Failed": "failed",
+			"SLA Fulfilled": "fulfilled",
+			"First Response Due": "first_response_due",
+			"Resolution Due": "resolution_due",
 		}
 
 		for label, fieldname in self.sla_status_map.items():
-			self.columns.append({
-				'label': _(label),
-				'fieldname': fieldname,
-				'fieldtype': 'Int',
-				'width': 100
-			})
+			self.columns.append(
+				{"label": _(label), "fieldname": fieldname, "fieldtype": "Int", "width": 100}
+			)
 
-		self.metrics = ['Avg First Response Time', 'Avg Response Time', 'Avg Hold Time',
-			'Avg Resolution Time', 'Avg User Resolution Time']
+		self.metrics = [
+			"Avg First Response Time",
+			"Avg Response Time",
+			"Avg Hold Time",
+			"Avg Resolution Time",
+			"Avg User Resolution Time",
+		]
 
 		for metric in self.metrics:
-			self.columns.append({
-				'label': _(metric),
-				'fieldname': scrub(metric),
-				'fieldtype': 'Duration',
-				'width': 170
-			})
+			self.columns.append(
+				{"label": _(metric), "fieldname": scrub(metric), "fieldtype": "Duration", "width": 170}
+			)
 
 	def get_data(self):
 		self.get_issues()
@@ -112,26 +108,37 @@
 	def get_issues(self):
 		filters = self.get_common_filters()
 		self.field_map = {
-			'Customer': 'customer',
-			'Issue Type': 'issue_type',
-			'Issue Priority': 'priority',
-			'Assigned To': '_assign'
+			"Customer": "customer",
+			"Issue Type": "issue_type",
+			"Issue Priority": "priority",
+			"Assigned To": "_assign",
 		}
 
-		self.entries = frappe.db.get_all('Issue',
-			fields=[self.field_map.get(self.filters.based_on), 'name', 'opening_date', 'status', 'avg_response_time',
-				'first_response_time', 'total_hold_time', 'user_resolution_time', 'resolution_time', 'agreement_status'],
-			filters=filters
+		self.entries = frappe.db.get_all(
+			"Issue",
+			fields=[
+				self.field_map.get(self.filters.based_on),
+				"name",
+				"opening_date",
+				"status",
+				"avg_response_time",
+				"first_response_time",
+				"total_hold_time",
+				"user_resolution_time",
+				"resolution_time",
+				"agreement_status",
+			],
+			filters=filters,
 		)
 
 	def get_common_filters(self):
 		filters = {}
-		filters['opening_date'] = ('between', [self.filters.from_date, self.filters.to_date])
+		filters["opening_date"] = ("between", [self.filters.from_date, self.filters.to_date])
 
-		if self.filters.get('assigned_to'):
-			filters['_assign'] = ('like', '%' + self.filters.get('assigned_to') + '%')
+		if self.filters.get("assigned_to"):
+			filters["_assign"] = ("like", "%" + self.filters.get("assigned_to") + "%")
 
-		for entry in ['company', 'status', 'priority', 'customer', 'project']:
+		for entry in ["company", "status", "priority", "customer", "project"]:
 			if self.filters.get(entry):
 				filters[entry] = self.filters.get(entry)
 
@@ -142,20 +149,20 @@
 		self.get_summary_data()
 
 		for entity, data in self.issue_summary_data.items():
-			if self.filters.based_on == 'Customer':
-				row = {'customer': entity}
-			elif self.filters.based_on == 'Assigned To':
-				row = {'user': entity}
-			elif self.filters.based_on == 'Issue Type':
-				row = {'issue_type': entity}
-			elif self.filters.based_on == 'Issue Priority':
-				row = {'priority': entity}
+			if self.filters.based_on == "Customer":
+				row = {"customer": entity}
+			elif self.filters.based_on == "Assigned To":
+				row = {"user": entity}
+			elif self.filters.based_on == "Issue Type":
+				row = {"issue_type": entity}
+			elif self.filters.based_on == "Issue Priority":
+				row = {"priority": entity}
 
 			for status in self.statuses:
 				count = flt(data.get(status, 0.0))
 				row[scrub(status)] = count
 
-			row['total_issues'] = data.get('total_issues', 0.0)
+			row["total_issues"] = data.get("total_issues", 0.0)
 
 			for sla_status in self.sla_status_map.values():
 				value = flt(data.get(sla_status), 0.0)
@@ -174,36 +181,41 @@
 			status = d.status
 			agreement_status = scrub(d.agreement_status)
 
-			if self.filters.based_on == 'Assigned To':
+			if self.filters.based_on == "Assigned To":
 				if d._assign:
 					for entry in json.loads(d._assign):
 						self.issue_summary_data.setdefault(entry, frappe._dict()).setdefault(status, 0.0)
 						self.issue_summary_data.setdefault(entry, frappe._dict()).setdefault(agreement_status, 0.0)
-						self.issue_summary_data.setdefault(entry, frappe._dict()).setdefault('total_issues', 0.0)
+						self.issue_summary_data.setdefault(entry, frappe._dict()).setdefault("total_issues", 0.0)
 						self.issue_summary_data[entry][status] += 1
 						self.issue_summary_data[entry][agreement_status] += 1
-						self.issue_summary_data[entry]['total_issues'] += 1
+						self.issue_summary_data[entry]["total_issues"] += 1
 
 			else:
 				field = self.field_map.get(self.filters.based_on)
 				value = d.get(field)
 				if not value:
-					value = _('Not Specified')
+					value = _("Not Specified")
 
 				self.issue_summary_data.setdefault(value, frappe._dict()).setdefault(status, 0.0)
 				self.issue_summary_data.setdefault(value, frappe._dict()).setdefault(agreement_status, 0.0)
-				self.issue_summary_data.setdefault(value, frappe._dict()).setdefault('total_issues', 0.0)
+				self.issue_summary_data.setdefault(value, frappe._dict()).setdefault("total_issues", 0.0)
 				self.issue_summary_data[value][status] += 1
 				self.issue_summary_data[value][agreement_status] += 1
-				self.issue_summary_data[value]['total_issues'] += 1
+				self.issue_summary_data[value]["total_issues"] += 1
 
 		self.get_metrics_data()
 
 	def get_metrics_data(self):
 		issues = []
 
-		metrics_list = ['avg_response_time', 'avg_first_response_time', 'avg_hold_time',
-			'avg_resolution_time', 'avg_user_resolution_time']
+		metrics_list = [
+			"avg_response_time",
+			"avg_first_response_time",
+			"avg_hold_time",
+			"avg_resolution_time",
+			"avg_user_resolution_time",
+		]
 
 		for entry in self.entries:
 			issues.append(entry.name)
@@ -211,7 +223,7 @@
 		field = self.field_map.get(self.filters.based_on)
 
 		if issues:
-			if self.filters.based_on == 'Assigned To':
+			if self.filters.based_on == "Assigned To":
 				assignment_map = frappe._dict()
 				for d in self.entries:
 					if d._assign:
@@ -219,11 +231,15 @@
 							for metric in metrics_list:
 								self.issue_summary_data.setdefault(entry, frappe._dict()).setdefault(metric, 0.0)
 
-							self.issue_summary_data[entry]['avg_response_time'] += d.get('avg_response_time') or 0.0
-							self.issue_summary_data[entry]['avg_first_response_time'] += d.get('first_response_time') or 0.0
-							self.issue_summary_data[entry]['avg_hold_time'] += d.get('total_hold_time') or 0.0
-							self.issue_summary_data[entry]['avg_resolution_time'] += d.get('resolution_time') or 0.0
-							self.issue_summary_data[entry]['avg_user_resolution_time'] += d.get('user_resolution_time') or 0.0
+							self.issue_summary_data[entry]["avg_response_time"] += d.get("avg_response_time") or 0.0
+							self.issue_summary_data[entry]["avg_first_response_time"] += (
+								d.get("first_response_time") or 0.0
+							)
+							self.issue_summary_data[entry]["avg_hold_time"] += d.get("total_hold_time") or 0.0
+							self.issue_summary_data[entry]["avg_resolution_time"] += d.get("resolution_time") or 0.0
+							self.issue_summary_data[entry]["avg_user_resolution_time"] += (
+								d.get("user_resolution_time") or 0.0
+							)
 
 							if not assignment_map.get(entry):
 								assignment_map[entry] = 0
@@ -234,7 +250,8 @@
 						self.issue_summary_data[entry][metric] /= flt(assignment_map.get(entry))
 
 			else:
-				data = frappe.db.sql("""
+				data = frappe.db.sql(
+					"""
 					SELECT
 						{0}, AVG(first_response_time) as avg_frt,
 						AVG(avg_response_time) as avg_resp_time,
@@ -245,21 +262,30 @@
 					WHERE
 						name IN %(issues)s
 					GROUP BY {0}
-				""".format(field), {'issues': issues}, as_dict=1)
+				""".format(
+						field
+					),
+					{"issues": issues},
+					as_dict=1,
+				)
 
 				for entry in data:
 					value = entry.get(field)
 					if not value:
-						value = _('Not Specified')
+						value = _("Not Specified")
 
 					for metric in metrics_list:
 						self.issue_summary_data.setdefault(value, frappe._dict()).setdefault(metric, 0.0)
 
-					self.issue_summary_data[value]['avg_response_time'] = entry.get('avg_resp_time') or 0.0
-					self.issue_summary_data[value]['avg_first_response_time'] = entry.get('avg_frt') or 0.0
-					self.issue_summary_data[value]['avg_hold_time'] = entry.get('avg_hold_time') or 0.0
-					self.issue_summary_data[value]['avg_resolution_time'] = entry.get('avg_resolution_time') or 0.0
-					self.issue_summary_data[value]['avg_user_resolution_time'] = entry.get('avg_user_resolution_time') or 0.0
+					self.issue_summary_data[value]["avg_response_time"] = entry.get("avg_resp_time") or 0.0
+					self.issue_summary_data[value]["avg_first_response_time"] = entry.get("avg_frt") or 0.0
+					self.issue_summary_data[value]["avg_hold_time"] = entry.get("avg_hold_time") or 0.0
+					self.issue_summary_data[value]["avg_resolution_time"] = (
+						entry.get("avg_resolution_time") or 0.0
+					)
+					self.issue_summary_data[value]["avg_user_resolution_time"] = (
+						entry.get("avg_user_resolution_time") or 0.0
+					)
 
 	def get_chart_data(self):
 		self.chart = []
@@ -273,47 +299,30 @@
 
 		entity = self.filters.based_on
 		entity_field = self.field_map.get(entity)
-		if entity == 'Assigned To':
-			entity_field = 'user'
+		if entity == "Assigned To":
+			entity_field = "user"
 
 		for entry in self.data:
 			labels.append(entry.get(entity_field))
-			open_issues.append(entry.get('open'))
-			replied_issues.append(entry.get('replied'))
-			on_hold_issues.append(entry.get('on_hold'))
-			resolved_issues.append(entry.get('resolved'))
-			closed_issues.append(entry.get('closed'))
+			open_issues.append(entry.get("open"))
+			replied_issues.append(entry.get("replied"))
+			on_hold_issues.append(entry.get("on_hold"))
+			resolved_issues.append(entry.get("resolved"))
+			closed_issues.append(entry.get("closed"))
 
 		self.chart = {
-			'data': {
-				'labels': labels[:30],
-				'datasets': [
-					{
-						'name': 'Open',
-						'values': open_issues[:30]
-					},
-					{
-						'name': 'Replied',
-						'values': replied_issues[:30]
-					},
-					{
-						'name': 'On Hold',
-						'values': on_hold_issues[:30]
-					},
-					{
-						'name': 'Resolved',
-						'values': resolved_issues[:30]
-					},
-					{
-						'name': 'Closed',
-						'values': closed_issues[:30]
-					}
-				]
+			"data": {
+				"labels": labels[:30],
+				"datasets": [
+					{"name": "Open", "values": open_issues[:30]},
+					{"name": "Replied", "values": replied_issues[:30]},
+					{"name": "On Hold", "values": on_hold_issues[:30]},
+					{"name": "Resolved", "values": resolved_issues[:30]},
+					{"name": "Closed", "values": closed_issues[:30]},
+				],
 			},
-			'type': 'bar',
-			'barOptions': {
-				'stacked': True
-			}
+			"type": "bar",
+			"barOptions": {"stacked": True},
 		}
 
 	def get_report_summary(self):
@@ -326,41 +335,41 @@
 		closed = 0
 
 		for entry in self.data:
-			open_issues += entry.get('open')
-			replied += entry.get('replied')
-			on_hold += entry.get('on_hold')
-			resolved += entry.get('resolved')
-			closed += entry.get('closed')
+			open_issues += entry.get("open")
+			replied += entry.get("replied")
+			on_hold += entry.get("on_hold")
+			resolved += entry.get("resolved")
+			closed += entry.get("closed")
 
 		self.report_summary = [
 			{
-				'value': open_issues,
-				'indicator': 'Red',
-				'label': _('Open'),
-				'datatype': 'Int',
+				"value": open_issues,
+				"indicator": "Red",
+				"label": _("Open"),
+				"datatype": "Int",
 			},
 			{
-				'value': replied,
-				'indicator': 'Grey',
-				'label': _('Replied'),
-				'datatype': 'Int',
+				"value": replied,
+				"indicator": "Grey",
+				"label": _("Replied"),
+				"datatype": "Int",
 			},
 			{
-				'value': on_hold,
-				'indicator': 'Grey',
-				'label': _('On Hold'),
-				'datatype': 'Int',
+				"value": on_hold,
+				"indicator": "Grey",
+				"label": _("On Hold"),
+				"datatype": "Int",
 			},
 			{
-				'value': resolved,
-				'indicator': 'Green',
-				'label': _('Resolved'),
-				'datatype': 'Int',
+				"value": resolved,
+				"indicator": "Green",
+				"label": _("Resolved"),
+				"datatype": "Int",
 			},
 			{
-				'value': closed,
-				'indicator': 'Green',
-				'label': _('Closed'),
-				'datatype': 'Int',
-			}
+				"value": closed,
+				"indicator": "Green",
+				"label": _("Closed"),
+				"datatype": "Int",
+			},
 		]
diff --git a/erpnext/support/report/support_hour_distribution/support_hour_distribution.py b/erpnext/support/report/support_hour_distribution/support_hour_distribution.py
index 6b2098f..5496721 100644
--- a/erpnext/support/report/support_hour_distribution/support_hour_distribution.py
+++ b/erpnext/support/report/support_hour_distribution/support_hour_distribution.py
@@ -7,34 +7,36 @@
 from frappe.utils import add_to_date, get_datetime, getdate
 
 time_slots = {
-	'12AM - 3AM': '00:00:00-03:00:00',
-	'3AM - 6AM': '03:00:00-06:00:00',
-	'6AM - 9AM': '06:00:00-09:00:00',
-	'9AM - 12PM': '09:00:00-12:00:00',
-	'12PM - 3PM': '12:00:00-15:00:00',
-	'3PM - 6PM': '15:00:00-18:00:00',
-	'6PM - 9PM': '18:00:00-21:00:00',
-	'9PM - 12AM': '21:00:00-23:00:00'
+	"12AM - 3AM": "00:00:00-03:00:00",
+	"3AM - 6AM": "03:00:00-06:00:00",
+	"6AM - 9AM": "06:00:00-09:00:00",
+	"9AM - 12PM": "09:00:00-12:00:00",
+	"12PM - 3PM": "12:00:00-15:00:00",
+	"3PM - 6PM": "15:00:00-18:00:00",
+	"6PM - 9PM": "18:00:00-21:00:00",
+	"9PM - 12AM": "21:00:00-23:00:00",
 }
 
+
 def execute(filters=None):
 	columns, data = [], []
-	if not filters.get('periodicity'):
-		filters['periodicity'] = 'Daily'
+	if not filters.get("periodicity"):
+		filters["periodicity"] = "Daily"
 
 	columns = get_columns()
 	data, timeslot_wise_count = get_data(filters)
 	chart = get_chart_data(timeslot_wise_count)
 	return columns, data, None, chart
 
+
 def get_data(filters):
 	start_date = getdate(filters.from_date)
 	data = []
 	time_slot_wise_total_count = {}
-	while(start_date <= getdate(filters.to_date)):
-		hours_count = {'date': start_date}
+	while start_date <= getdate(filters.to_date):
+		hours_count = {"date": start_date}
 		for key, value in time_slots.items():
-			start_time, end_time = value.split('-')
+			start_time, end_time = value.split("-")
 			start_time = get_datetime("{0} {1}".format(start_date.strftime("%Y-%m-%d"), start_time))
 			end_time = get_datetime("{0} {1}".format(start_date.strftime("%Y-%m-%d"), end_time))
 			hours_count[key] = get_hours_count(start_time, end_time)
@@ -47,49 +49,57 @@
 
 	return data, time_slot_wise_total_count
 
+
 def get_hours_count(start_time, end_time):
-	data = frappe.db.sql(""" select count(*) from `tabIssue` where creation
-		between %(start_time)s and %(end_time)s""", {
-			'start_time': start_time,
-			'end_time': end_time
-		}, as_list=1) or []
+	data = (
+		frappe.db.sql(
+			""" select count(*) from `tabIssue` where creation
+		between %(start_time)s and %(end_time)s""",
+			{"start_time": start_time, "end_time": end_time},
+			as_list=1,
+		)
+		or []
+	)
 
 	return data[0][0] if len(data) > 0 else 0
 
-def get_columns():
-	columns = [{
-		"fieldname": "date",
-		"label": _("Date"),
-		"fieldtype": "Date",
-		"width": 100
-	}]
 
-	for label in ['12AM - 3AM', '3AM - 6AM', '6AM - 9AM',
-		'9AM - 12PM', '12PM - 3PM', '3PM - 6PM', '6PM - 9PM', '9PM - 12AM']:
-		columns.append({
-			"fieldname": label,
-			"label": _(label),
-			"fieldtype": "Data",
-			"width": 120
-		})
+def get_columns():
+	columns = [{"fieldname": "date", "label": _("Date"), "fieldtype": "Date", "width": 100}]
+
+	for label in [
+		"12AM - 3AM",
+		"3AM - 6AM",
+		"6AM - 9AM",
+		"9AM - 12PM",
+		"12PM - 3PM",
+		"3PM - 6PM",
+		"6PM - 9PM",
+		"9PM - 12AM",
+	]:
+		columns.append({"fieldname": label, "label": _(label), "fieldtype": "Data", "width": 120})
 
 	return columns
 
+
 def get_chart_data(timeslot_wise_count):
 	total_count = []
-	timeslots = ['12AM - 3AM', '3AM - 6AM', '6AM - 9AM',
-		'9AM - 12PM', '12PM - 3PM', '3PM - 6PM', '6PM - 9PM', '9PM - 12AM']
+	timeslots = [
+		"12AM - 3AM",
+		"3AM - 6AM",
+		"6AM - 9AM",
+		"9AM - 12PM",
+		"12PM - 3PM",
+		"3PM - 6PM",
+		"6PM - 9PM",
+		"9PM - 12AM",
+	]
 
 	datasets = []
 	for data in timeslots:
 		total_count.append(timeslot_wise_count.get(data, 0))
-	datasets.append({'values': total_count})
+	datasets.append({"values": total_count})
 
-	chart = {
-		"data": {
-			'labels': timeslots,
-			'datasets': datasets
-		}
-	}
+	chart = {"data": {"labels": timeslots, "datasets": datasets}}
 	chart["type"] = "line"
 	return chart
diff --git a/erpnext/telephony/doctype/call_log/call_log.json b/erpnext/telephony/doctype/call_log/call_log.json
index 1d6c39e..a41ddb1 100644
--- a/erpnext/telephony/doctype/call_log/call_log.json
+++ b/erpnext/telephony/doctype/call_log/call_log.json
@@ -1,7 +1,7 @@
 {
  "actions": [],
  "autoname": "field:id",
- "creation": "2019-06-05 12:07:02.634534",
+ "creation": "2022-02-21 11:54:58.414784",
  "doctype": "DocType",
  "engine": "InnoDB",
  "field_order": [
@@ -9,6 +9,8 @@
   "id",
   "from",
   "to",
+  "call_received_by",
+  "employee_user_id",
   "medium",
   "start_time",
   "end_time",
@@ -20,6 +22,7 @@
   "recording_url",
   "recording_html",
   "section_break_11",
+  "type_of_call",
   "summary",
   "section_break_19",
   "links"
@@ -103,7 +106,8 @@
   },
   {
    "fieldname": "summary",
-   "fieldtype": "Small Text"
+   "fieldtype": "Small Text",
+   "label": "Summary"
   },
   {
    "fieldname": "section_break_11",
@@ -134,15 +138,37 @@
    "fieldname": "call_details_section",
    "fieldtype": "Section Break",
    "label": "Call Details"
+  },
+  {
+   "fieldname": "employee_user_id",
+   "fieldtype": "Link",
+   "hidden": 1,
+   "label": "Employee User Id",
+   "options": "User"
+  },
+  {
+   "fieldname": "type_of_call",
+   "fieldtype": "Link",
+   "label": "Type Of Call",
+   "options": "Telephony Call Type"
+  },
+  {
+   "depends_on": "to",
+   "fieldname": "call_received_by",
+   "fieldtype": "Link",
+   "label": "Call Received By",
+   "options": "Employee",
+   "read_only": 1
   }
  ],
  "in_create": 1,
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2021-02-08 14:23:28.744844",
+ "modified": "2022-04-14 02:59:22.503202",
  "modified_by": "Administrator",
  "module": "Telephony",
  "name": "Call Log",
+ "naming_rule": "By fieldname",
  "owner": "Administrator",
  "permissions": [
   {
@@ -164,6 +190,7 @@
  ],
  "sort_field": "creation",
  "sort_order": "DESC",
+ "states": [],
  "title_field": "from",
  "track_changes": 1,
  "track_views": 1
diff --git a/erpnext/telephony/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py
index 0c24484..7725e71 100644
--- a/erpnext/telephony/doctype/call_log/call_log.py
+++ b/erpnext/telephony/doctype/call_log/call_log.py
@@ -11,8 +11,8 @@
 from erpnext.crm.doctype.lead.lead import get_lead_with_phone_number
 from erpnext.crm.doctype.utils import get_scheduled_employees_for_popup, strip_number
 
-END_CALL_STATUSES = ['No Answer', 'Completed', 'Busy', 'Failed']
-ONGOING_CALL_STATUSES = ['Ringing', 'In Progress']
+END_CALL_STATUSES = ["No Answer", "Completed", "Busy", "Failed"]
+ONGOING_CALL_STATUSES = ["Ringing", "In Progress"]
 
 
 class CallLog(Document):
@@ -20,18 +20,21 @@
 		deduplicate_dynamic_links(self)
 
 	def before_insert(self):
-		"""Add lead(third party person) links to the document.
-		"""
-		lead_number = self.get('from') if self.is_incoming_call() else self.get('to')
+		"""Add lead(third party person) links to the document."""
+		lead_number = self.get("from") if self.is_incoming_call() else self.get("to")
 		lead_number = strip_number(lead_number)
 
 		contact = get_contact_with_phone_number(strip_number(lead_number))
 		if contact:
-			self.add_link(link_type='Contact', link_name=contact)
+			self.add_link(link_type="Contact", link_name=contact)
 
 		lead = get_lead_with_phone_number(lead_number)
 		if lead:
-			self.add_link(link_type='Lead', link_name=lead)
+			self.add_link(link_type="Lead", link_name=lead)
+
+		# Add Employee Name
+		if self.is_incoming_call():
+			self.update_received_by()
 
 	def after_insert(self):
 		self.trigger_call_popup()
@@ -39,86 +42,106 @@
 	def on_update(self):
 		def _is_call_missed(doc_before_save, doc_after_save):
 			# FIXME: This works for Exotel but not for all telepony providers
-			return doc_before_save.to != doc_after_save.to and doc_after_save.status not in END_CALL_STATUSES
+			return (
+				doc_before_save.to != doc_after_save.to and doc_after_save.status not in END_CALL_STATUSES
+			)
 
 		def _is_call_ended(doc_before_save, doc_after_save):
 			return doc_before_save.status not in END_CALL_STATUSES and self.status in END_CALL_STATUSES
 
 		doc_before_save = self.get_doc_before_save()
-		if not doc_before_save: return
+		if not doc_before_save:
+			return
+
+		if self.is_incoming_call() and self.has_value_changed("to"):
+			self.update_received_by()
 
 		if _is_call_missed(doc_before_save, self):
-			frappe.publish_realtime('call_{id}_missed'.format(id=self.id), self)
+			frappe.publish_realtime("call_{id}_missed".format(id=self.id), self)
 			self.trigger_call_popup()
 
 		if _is_call_ended(doc_before_save, self):
-			frappe.publish_realtime('call_{id}_ended'.format(id=self.id), self)
+			frappe.publish_realtime("call_{id}_ended".format(id=self.id), self)
 
 	def is_incoming_call(self):
-		return self.type == 'Incoming'
+		return self.type == "Incoming"
 
 	def add_link(self, link_type, link_name):
-		self.append('links', {
-			'link_doctype': link_type,
-			'link_name': link_name
-		})
+		self.append("links", {"link_doctype": link_type, "link_name": link_name})
 
 	def trigger_call_popup(self):
 		if self.is_incoming_call():
 			scheduled_employees = get_scheduled_employees_for_popup(self.medium)
-			employee_emails = get_employees_with_number(self.to)
+			employees = get_employees_with_number(self.to)
+			employee_emails = [employee.get("user_id") for employee in employees]
 
 			# check if employees with matched number are scheduled to receive popup
 			emails = set(scheduled_employees).intersection(employee_emails)
 
 			if frappe.conf.developer_mode:
-				self.add_comment(text=f"""
+				self.add_comment(
+					text=f"""
 					Scheduled Employees: {scheduled_employees}
 					Matching Employee: {employee_emails}
 					Show Popup To: {emails}
-				""")
+				"""
+				)
 
 			if employee_emails and not emails:
 				self.add_comment(text=_("No employee was scheduled for call popup"))
 
 			for email in emails:
-				frappe.publish_realtime('show_call_popup', self, user=email)
+				frappe.publish_realtime("show_call_popup", self, user=email)
+
+	def update_received_by(self):
+		if employees := get_employees_with_number(self.get("to")):
+			self.call_received_by = employees[0].get("name")
+			self.employee_user_id = employees[0].get("user_id")
 
 
 @frappe.whitelist()
-def add_call_summary(call_log, summary):
-	doc = frappe.get_doc('Call Log', call_log)
-	doc.add_comment('Comment', frappe.bold(_('Call Summary')) + '<br><br>' + summary)
+def add_call_summary_and_call_type(call_log, summary, call_type):
+	doc = frappe.get_doc("Call Log", call_log)
+	doc.type_of_call = call_type
+	doc.save()
+	doc.add_comment("Comment", frappe.bold(_("Call Summary")) + "<br><br>" + summary)
+
 
 def get_employees_with_number(number):
 	number = strip_number(number)
-	if not number: return []
+	if not number:
+		return []
 
-	employee_emails = frappe.cache().hget('employees_with_number', number)
-	if employee_emails: return employee_emails
+	employee_doc_name_and_emails = frappe.cache().hget("employees_with_number", number)
+	if employee_doc_name_and_emails:
+		return employee_doc_name_and_emails
 
-	employees = frappe.get_all('Employee', filters={
-		'cell_number': ['like', '%{}%'.format(number)],
-		'user_id': ['!=', '']
-	}, fields=['user_id'])
+	employee_doc_name_and_emails = frappe.get_all(
+		"Employee",
+		filters={"cell_number": ["like", f"%{number}%"], "user_id": ["!=", ""]},
+		fields=["name", "user_id"],
+	)
 
-	employee_emails = [employee.user_id for employee in employees]
-	frappe.cache().hset('employees_with_number', number, employee_emails)
+	frappe.cache().hset("employees_with_number", number, employee_doc_name_and_emails)
 
-	return employee_emails
+	return employee_doc_name_and_emails
+
 
 def link_existing_conversations(doc, state):
 	"""
 	Called from hooks on creation of Contact or Lead to link all the existing conversations.
 	"""
-	if doc.doctype != 'Contact': return
+	if doc.doctype != "Contact":
+		return
 	try:
 		numbers = [d.phone for d in doc.phone_nos]
 
 		for number in numbers:
 			number = strip_number(number)
-			if not number: continue
-			logs = frappe.db.sql_list("""
+			if not number:
+				continue
+			logs = frappe.db.sql_list(
+				"""
 				SELECT cl.name FROM `tabCall Log` cl
 				LEFT JOIN `tabDynamic Link` dl
 				ON cl.name = dl.parent
@@ -131,44 +154,42 @@
 						ELSE 0
 					END
 				)=0
-			""", dict(
-				phone_number='%{}'.format(number),
-				docname=doc.name,
-				doctype = doc.doctype
-				)
+			""",
+				dict(phone_number="%{}".format(number), docname=doc.name, doctype=doc.doctype),
 			)
 
 			for log in logs:
-				call_log = frappe.get_doc('Call Log', log)
+				call_log = frappe.get_doc("Call Log", log)
 				call_log.add_link(link_type=doc.doctype, link_name=doc.name)
 				call_log.save(ignore_permissions=True)
 			frappe.db.commit()
 	except Exception:
-		frappe.log_error(title=_('Error during caller information update'))
+		frappe.log_error(title=_("Error during caller information update"))
+
 
 def get_linked_call_logs(doctype, docname):
 	# content will be shown in timeline
-	logs = frappe.get_all('Dynamic Link', fields=['parent'], filters={
-		'parenttype': 'Call Log',
-		'link_doctype': doctype,
-		'link_name': docname
-	})
+	logs = frappe.get_all(
+		"Dynamic Link",
+		fields=["parent"],
+		filters={"parenttype": "Call Log", "link_doctype": doctype, "link_name": docname},
+	)
 
 	logs = set([log.parent for log in logs])
 
-	logs = frappe.get_all('Call Log', fields=['*'], filters={
-		'name': ['in', logs]
-	})
+	logs = frappe.get_all("Call Log", fields=["*"], filters={"name": ["in", logs]})
 
 	timeline_contents = []
 	for log in logs:
 		log.show_call_button = 0
-		timeline_contents.append({
-			'icon': 'call',
-			'is_card': True,
-			'creation': log.creation,
-			'template': 'call_link',
-			'template_data': log
-		})
+		timeline_contents.append(
+			{
+				"icon": "call",
+				"is_card": True,
+				"creation": log.creation,
+				"template": "call_link",
+				"template_data": log,
+			}
+		)
 
 	return timeline_contents
diff --git a/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.py b/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.py
index 08e244d..5edf81d 100644
--- a/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.py
+++ b/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.py
@@ -20,35 +20,38 @@
 		self.validate_call_schedule_overlaps(self.call_handling_schedule)
 
 	def validate_call_schedule_timeslot(self, schedule: list):
-		"""	Make sure that to time slot is ahead of from time slot.
-		"""
+		"""Make sure that to time slot is ahead of from time slot."""
 		errors = []
 		for record in schedule:
 			from_time = self.time_to_seconds(record.from_time)
 			to_time = self.time_to_seconds(record.to_time)
 			if from_time >= to_time:
 				errors.append(
-					_('Call Schedule Row {0}: To time slot should always be ahead of From time slot.').format(record.idx)
+					_("Call Schedule Row {0}: To time slot should always be ahead of From time slot.").format(
+						record.idx
+					)
 				)
 
 		if errors:
-			frappe.throw('<br/>'.join(errors))
+			frappe.throw("<br/>".join(errors))
 
 	def validate_call_schedule_overlaps(self, schedule: list):
-		"""Check if any time slots are overlapped in a day schedule.
-		"""
+		"""Check if any time slots are overlapped in a day schedule."""
 		week_days = set([each.day_of_week for each in schedule])
 
 		for day in week_days:
-			timeslots = [(record.from_time, record.to_time) for record in schedule if record.day_of_week==day]
+			timeslots = [
+				(record.from_time, record.to_time) for record in schedule if record.day_of_week == day
+			]
 
 			# convert time in timeslot into an integer represents number of seconds
 			timeslots = sorted(map(lambda seq: tuple(map(self.time_to_seconds, seq)), timeslots))
-			if len(timeslots) < 2: continue
+			if len(timeslots) < 2:
+				continue
 
 			for i in range(1, len(timeslots)):
-				if self.check_timeslots_overlap(timeslots[i-1], timeslots[i]):
-					frappe.throw(_('Please fix overlapping time slots for {0}.').format(day))
+				if self.check_timeslots_overlap(timeslots[i - 1], timeslots[i]):
+					frappe.throw(_("Please fix overlapping time slots for {0}.").format(day))
 
 	@staticmethod
 	def check_timeslots_overlap(ts1: Tuple[int, int], ts2: Tuple[int, int]) -> bool:
@@ -58,7 +61,6 @@
 
 	@staticmethod
 	def time_to_seconds(time: str) -> int:
-		"""Convert time string of format HH:MM:SS into seconds
-		"""
+		"""Convert time string of format HH:MM:SS into seconds"""
 		date_time = datetime.strptime(time, "%H:%M:%S")
 		return date_time - datetime(1900, 1, 1)
diff --git a/erpnext/education/doctype/__init__.py b/erpnext/telephony/doctype/telephony_call_type/__init__.py
similarity index 100%
rename from erpnext/education/doctype/__init__.py
rename to erpnext/telephony/doctype/telephony_call_type/__init__.py
diff --git a/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.js b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.js
new file mode 100644
index 0000000..efba2b8
--- /dev/null
+++ b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Telephony Call Type', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.json b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.json
new file mode 100644
index 0000000..603709e
--- /dev/null
+++ b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.json
@@ -0,0 +1,58 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "field:call_type",
+ "creation": "2022-02-25 16:13:37.321312",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "call_type",
+  "amended_from"
+ ],
+ "fields": [
+  {
+   "fieldname": "call_type",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Call Type",
+   "reqd": 1,
+   "unique": 1
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Telephony Call Type",
+   "print_hide": 1,
+   "read_only": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2022-02-25 16:14:07.087461",
+ "modified_by": "Administrator",
+ "module": "Telephony",
+ "name": "Telephony Call Type",
+ "naming_rule": "By fieldname",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.py b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.py
new file mode 100644
index 0000000..944ffef
--- /dev/null
+++ b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class TelephonyCallType(Document):
+	pass
diff --git a/erpnext/telephony/doctype/telephony_call_type/test_telephony_call_type.py b/erpnext/telephony/doctype/telephony_call_type/test_telephony_call_type.py
new file mode 100644
index 0000000..b3c19c3
--- /dev/null
+++ b/erpnext/telephony/doctype/telephony_call_type/test_telephony_call_type.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+
+class TestTelephonyCallType(unittest.TestCase):
+	pass
diff --git a/erpnext/templates/emails/request_for_quotation.html b/erpnext/templates/emails/request_for_quotation.html
index 3283987..5b073e6 100644
--- a/erpnext/templates/emails/request_for_quotation.html
+++ b/erpnext/templates/emails/request_for_quotation.html
@@ -1,24 +1,29 @@
 <h4>{{_("Request for Quotation")}}</h4>
 <p>{{ supplier_salutation if supplier_salutation else ''}} {{ supplier_name }},</p>
 <p>{{ message }}</p>
-
 <p>{{_("The Request for Quotation can be accessed by clicking on the following button")}}:</p>
-<p>
-	<button style="border: 1px solid #15c; padding: 6px; border-radius: 5px; background-color: white;">
-		<a href="{{ rfq_link }}" style="color: #15c; text-decoration:none;" target="_blank">Submit your Quotation</a>
-	</button>
-</p><br>
-
-<p>{{_("Regards")}},<br>
-{{ user_fullname }}</p><br>
-
+<br>
+<a
+	href="{{ rfq_link }}"
+	class="btn btn-default btn-sm"
+	target="_blank">
+	{{ _("Submit your Quotation") }}
+</a>
+<br>
+<br>
 {% if update_password_link %}
-
+<br>
 <p>{{_("Please click on the following button to set your new password")}}:</p>
-<p>
-	<button style="border: 1px solid #15c; padding: 4px; border-radius: 5px; background-color: white;">
-		<a href="{{ update_password_link }}" style="color: #15c; font-size: 12px; text-decoration:none;" target="_blank">{{_("Update Password") }}</a>
-	</button>
-</p>
-
+<a
+	href="{{ update_password_link }}"
+	class="btn btn-default btn-xs"
+	target="_blank">
+	{{_("Set Password") }}
+</a>
+<br>
+<br>
 {% endif %}
+<p>
+	{{_("Regards")}},<br>
+	{{ user_fullname }}
+</p>
diff --git a/erpnext/templates/generators/item_group.html b/erpnext/templates/generators/item_group.html
index e099cdd..956c3c51 100644
--- a/erpnext/templates/generators/item_group.html
+++ b/erpnext/templates/generators/item_group.html
@@ -52,24 +52,6 @@
 
 			</div>
 
-			<script>
-				frappe.ready(() => {
-					$('.product-filter-filter').on('keydown', frappe.utils.debounce((e) => {
-						const $input = $(e.target);
-						const keyword = ($input.val() || '').toLowerCase();
-						const $filter_options = $input.next('.filter-options');
-
-						$filter_options.find('.custom-control').show();
-						$filter_options.find('.custom-control').each((i, el) => {
-							const $el = $(el);
-							const value = $el.data('value').toLowerCase();
-							if (!value.includes(keyword)) {
-								$el.hide();
-							}
-						});
-					}, 300));
-				})
-			</script>
 		</div>
 	</div>
 </div>
diff --git a/erpnext/templates/generators/student_admission.html b/erpnext/templates/generators/student_admission.html
deleted file mode 100644
index 8cc58a0..0000000
--- a/erpnext/templates/generators/student_admission.html
+++ /dev/null
@@ -1,27 +0,0 @@
-
-{% extends "templates/web.html" %}
-
-{% block breadcrumbs %}
-	{% include "templates/includes/breadcrumbs.html" %}
-{% endblock %}
-
-{% block header %}
-
-<h1>{{ title }}</h1>
-{% endblock %}
-
-{% block page_content %}
-
-{%- if introduction -%}
-<div>{{ introduction }}</div>
-{% endif %}
-
-{%- if doc.enable_admission_application -%}
-<p>
-	<a class='btn btn-primary'
-	href='/student-applicant'>
-	{{ _("Apply Now") }}</a>
-</p>
-{% endif %}
-
-{% endblock %}
diff --git a/erpnext/templates/includes/assessment/assessment_row.html b/erpnext/templates/includes/assessment/assessment_row.html
deleted file mode 100644
index a33ccff..0000000
--- a/erpnext/templates/includes/assessment/assessment_row.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<div class="web-list-item">
-	<div class="row">
-		<div class="col-xs-4">
-			{{ doc.course }}
-		</div>
-		<div class="col-xs-2">
-			{{ doc.room }}
-		</div>
-		<div class="col-xs-2">
-			{{doc.schedule_date }}
-		</div>
-		<div class="col-xs-2">
-			{{ doc.from_time }}
-		</div>
-		<div class="col-xs-2">
-			{{ doc.to_time }}
-		</div>
-	</div>
-</div>
diff --git a/erpnext/templates/includes/course/course_row.html b/erpnext/templates/includes/course/course_row.html
deleted file mode 100644
index fddfc3c..0000000
--- a/erpnext/templates/includes/course/course_row.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<div class="web-list-item">
-	<a href="/courses?course={{ doc.course }}">
-		<div class="row">
-			<div class="col-xs-5">
-				{{ doc.course }}
-			</div>
-			<div class="col-xs-3">
-				{{ doc.name }}
-			</div>
-			<div class="col-xs-2">
-				{{doc.academic_term }}
-			</div>
-			<div class="col-xs-2">
-				{{ doc.academic_year }}
-			</div>
-		</div>
-	</a>
-</div>
diff --git a/erpnext/templates/includes/course/macros.html b/erpnext/templates/includes/course/macros.html
deleted file mode 100644
index 334b5ea..0000000
--- a/erpnext/templates/includes/course/macros.html
+++ /dev/null
@@ -1 +0,0 @@
-{% macro back_link(doc) %}&back-to=/courses?course={{ doc.name }}&back-to-title={{ doc.course_name }}{% endmacro %}
diff --git a/erpnext/templates/includes/macros.html b/erpnext/templates/includes/macros.html
index 4741307..fb4cecf 100644
--- a/erpnext/templates/includes/macros.html
+++ b/erpnext/templates/includes/macros.html
@@ -300,13 +300,13 @@
 
 		{% if values | len > 20 %}
 		<!-- show inline filter if values more than 20 -->
-		<input type="text" class="form-control form-control-sm mb-2 product-filter-filter"/>
+		<input type="text" class="form-control form-control-sm mb-2 filter-lookup-input" placeholder="Search {{ item_field.label + 's' }}"/>
 		{% endif %}
 
 		{% if values %}
 		<div class="filter-options">
 			{% for value in values %}
-			<div class="checkbox" data-value="{{ value }}">
+			<div class="filter-lookup-wrapper checkbox" data-value="{{ value }}">
 				<label for="{{value}}">
 					<input type="checkbox"
 						class="product-filter field-filter"
@@ -329,16 +329,16 @@
 {%- macro attribute_filter_section(filters)-%}
 {% for attribute in filters %}
 	<div class="mb-4 filter-block pb-5">
-		<div class="filter-label mb-3">{{ attribute.name}}</div>
-		{% if values | len > 20 %}
+		<div class="filter-label mb-3">{{ attribute.name }}</div>
+		{% if attribute.item_attribute_values | len > 20 %}
 		<!-- show inline filter if values more than 20 -->
-		<input type="text" class="form-control form-control-sm mb-2 product-filter-filter"/>
+		<input type="text" class="form-control form-control-sm mb-2 filter-lookup-input" placeholder="Search {{ attribute.name + 's' }}"/>
 		{% endif %}
 
 		{% if attribute.item_attribute_values %}
 		<div class="filter-options">
 			{% for attr_value in attribute.item_attribute_values %}
-			<div class="checkbox">
+			<div class="filter-lookup-wrapper checkbox" data-value="{{ attr_value }}">
 				<label data-value="{{ attr_value }}">
 					<input type="checkbox"
 						class="product-filter attribute-filter"
diff --git a/erpnext/templates/includes/projects/project_row.html b/erpnext/templates/includes/projects/project_row.html
index a256fbd..686637a 100644
--- a/erpnext/templates/includes/projects/project_row.html
+++ b/erpnext/templates/includes/projects/project_row.html
@@ -1,11 +1,11 @@
 {% if doc.status == "Open" %}
   <div class="web-list-item transaction-list-item">
     <div class="row">
-      <div class="col-xs-2">
+      <div class="col-xs-2 project-link">
         <a class="transaction-item-link" href="/projects?project={{ doc.name | urlencode }}">Link</a>
         {{ doc.name }}
       </div>
-      <div class="col-xs-2">
+      <div class="col-xs-2 project-name">
         {{ doc.project_name }}
       </div>
       <div class="col-xs-3 text-center">
@@ -25,7 +25,7 @@
       </div>
       {% if doc["_assign"] %}
         {% set assigned_users = json.loads(doc["_assign"])%}
-        <div class="col-xs-2">
+        <div class="col-xs-2 project-users">
           {% for user in assigned_users %}
             {% set user_details = frappe
               .db
@@ -46,7 +46,7 @@
           {% endfor %}
         </div>
       {% endif %}
-      <div class="col-xs-3 text-right small text-muted">
+      <div class="col-xs-3 text-right small text-muted project-modified-on">
         {{ frappe.utils.pretty_date(doc.modified) }}
       </div>
     </div>
diff --git a/erpnext/templates/pages/courses.html b/erpnext/templates/pages/courses.html
deleted file mode 100644
index 6592f7a..0000000
--- a/erpnext/templates/pages/courses.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% extends "templates/web.html" %}
-
-{% block header %}
-	<h1> About </h1>
-{% endblock %}
-
-{% block page_content %}
-
-<p class="post-description"> {{ intro }} </p>
-
-{% endblock %}
diff --git a/erpnext/templates/pages/courses.py b/erpnext/templates/pages/courses.py
deleted file mode 100644
index 6051e60..0000000
--- a/erpnext/templates/pages/courses.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-
-import frappe
-
-
-def get_context(context):
-	course = frappe.get_doc('Course', frappe.form_dict.course)
-	sidebar_title = course.name
-
-	context.no_cache = 1
-	context.show_sidebar = True
-	course = frappe.get_doc('Course', frappe.form_dict.course)
-	course.has_permission('read')
-	context.doc = course
-	context.sidebar_title = sidebar_title
-	context.intro = course.course_intro
diff --git a/erpnext/templates/pages/help.py b/erpnext/templates/pages/help.py
index 6a83fc8..19993ee 100644
--- a/erpnext/templates/pages/help.py
+++ b/erpnext/templates/pages/help.py
@@ -25,21 +25,19 @@
 	else:
 		context.issues = []
 
+
 def get_forum_posts(s):
-	response = requests.get(s.forum_url + '/' + s.get_latest_query)
+	response = requests.get(s.forum_url + "/" + s.get_latest_query)
 	response.raise_for_status()
 	response_json = response.json()
 
-	topics_data = {} # it will actually be an array
-	key_list = s.response_key_list.split(',')
+	topics_data = {}  # it will actually be an array
+	key_list = s.response_key_list.split(",")
 	for key in key_list:
 		topics_data = response_json.get(key) if not topics_data else topics_data.get(key)
 
 	for topic in topics_data:
-		topic["link"] = s.forum_url + '/' + s.post_route_string + '/' + str(topic.get(s.post_route_key))
+		topic["link"] = s.forum_url + "/" + s.post_route_string + "/" + str(topic.get(s.post_route_key))
 
-	post_params = {
-		"title": s.post_title_key,
-		"description": s.post_description_key
-	}
+	post_params = {"title": s.post_title_key, "description": s.post_description_key}
 	return topics_data, post_params
diff --git a/erpnext/templates/pages/home.py b/erpnext/templates/pages/home.py
index d08e81b..47fb89d 100644
--- a/erpnext/templates/pages/home.py
+++ b/erpnext/templates/pages/home.py
@@ -6,46 +6,51 @@
 
 no_cache = 1
 
+
 def get_context(context):
-	homepage = frappe.get_doc('Homepage')
+	homepage = frappe.get_cached_doc("Homepage")
 
 	for item in homepage.products:
-		route = frappe.db.get_value('Website Item', {"item_code": item.item_code}, 'route')
+		route = frappe.db.get_value("Website Item", {"item_code": item.item_code}, "route")
 		if route:
-			item.route = '/' + route
+			item.route = "/" + route
 
 	homepage.title = homepage.title or homepage.company
 	context.title = homepage.title
 	context.homepage = homepage
 
-	if homepage.hero_section_based_on == 'Homepage Section' and homepage.hero_section:
-		homepage.hero_section_doc = frappe.get_doc('Homepage Section', homepage.hero_section)
+	if homepage.hero_section_based_on == "Homepage Section" and homepage.hero_section:
+		homepage.hero_section_doc = frappe.get_cached_doc("Homepage Section", homepage.hero_section)
 
 	if homepage.slideshow:
-		doc = frappe.get_doc('Website Slideshow', homepage.slideshow)
+		doc = frappe.get_cached_doc("Website Slideshow", homepage.slideshow)
 		context.slideshow = homepage.slideshow
 		context.slideshow_header = doc.header
 		context.slides = doc.slideshow_items
 
-	context.blogs = frappe.get_all('Blog Post',
-		fields=['title', 'blogger', 'blog_intro', 'route'],
-		filters={
-			'published': 1
-		},
-		order_by='modified desc',
-		limit=3
+	context.blogs = frappe.get_all(
+		"Blog Post",
+		fields=["title", "blogger", "blog_intro", "route"],
+		filters={"published": 1},
+		order_by="modified desc",
+		limit=3,
 	)
 
 	# filter out homepage section which is used as hero section
-	homepage_hero_section = homepage.hero_section_based_on == 'Homepage Section' and homepage.hero_section
-	homepage_sections = frappe.get_all('Homepage Section',
-		filters=[['name', '!=', homepage_hero_section]] if homepage_hero_section else None,
-		order_by='section_order asc'
+	homepage_hero_section = (
+		homepage.hero_section_based_on == "Homepage Section" and homepage.hero_section
 	)
-	context.homepage_sections = [frappe.get_doc('Homepage Section', name) for name in homepage_sections]
+	homepage_sections = frappe.get_all(
+		"Homepage Section",
+		filters=[["name", "!=", homepage_hero_section]] if homepage_hero_section else None,
+		order_by="section_order asc",
+	)
+	context.homepage_sections = [
+		frappe.get_cached_doc("Homepage Section", name) for name in homepage_sections
+	]
 
 	context.metatags = context.metatags or frappe._dict({})
 	context.metatags.image = homepage.hero_image or None
 	context.metatags.description = homepage.description or None
 
-	context.explore_link = '/all-products'
+	context.explore_link = "/all-products"
diff --git a/erpnext/templates/pages/integrations/gocardless_checkout.py b/erpnext/templates/pages/integrations/gocardless_checkout.py
index bbdbf1d..655be52 100644
--- a/erpnext/templates/pages/integrations/gocardless_checkout.py
+++ b/erpnext/templates/pages/integrations/gocardless_checkout.py
@@ -14,8 +14,18 @@
 
 no_cache = 1
 
-expected_keys = ('amount', 'title', 'description', 'reference_doctype', 'reference_docname',
-	'payer_name', 'payer_email', 'order_id', 'currency')
+expected_keys = (
+	"amount",
+	"title",
+	"description",
+	"reference_doctype",
+	"reference_docname",
+	"payer_name",
+	"payer_email",
+	"order_id",
+	"currency",
+)
+
 
 def get_context(context):
 	context.no_cache = 1
@@ -25,17 +35,22 @@
 		for key in expected_keys:
 			context[key] = frappe.form_dict[key]
 
-		context['amount'] = flt(context['amount'])
+		context["amount"] = flt(context["amount"])
 
 		gateway_controller = get_gateway_controller(context.reference_docname)
-		context['header_img'] = frappe.db.get_value("GoCardless Settings", gateway_controller, "header_img")
+		context["header_img"] = frappe.db.get_value(
+			"GoCardless Settings", gateway_controller, "header_img"
+		)
 
 	else:
-		frappe.redirect_to_message(_('Some information is missing'),
-			_('Looks like someone sent you to an incomplete URL. Please ask them to look into it.'))
+		frappe.redirect_to_message(
+			_("Some information is missing"),
+			_("Looks like someone sent you to an incomplete URL. Please ask them to look into it."),
+		)
 		frappe.local.flags.redirect_location = frappe.local.response.location
 		raise frappe.Redirect
 
+
 @frappe.whitelist(allow_guest=True)
 def check_mandate(data, reference_doctype, reference_docname):
 	data = json.loads(data)
@@ -59,23 +74,27 @@
 			prefilled_customer.update({"email": frappe.session.user})
 
 	else:
-		prefilled_customer = {
-			"company_name": payer.name,
-			"email": frappe.session.user
-		}
+		prefilled_customer = {"company_name": payer.name, "email": frappe.session.user}
 
-	success_url = get_url("./integrations/gocardless_confirmation?reference_doctype=" + reference_doctype + "&reference_docname=" + reference_docname)
+	success_url = get_url(
+		"./integrations/gocardless_confirmation?reference_doctype="
+		+ reference_doctype
+		+ "&reference_docname="
+		+ reference_docname
+	)
 
 	try:
-		redirect_flow = client.redirect_flows.create(params={
-							"description": _("Pay {0} {1}").format(data['amount'], data['currency']),
-							"session_token": frappe.session.user,
-							"success_redirect_url": success_url,
-							"prefilled_customer": prefilled_customer
-						})
+		redirect_flow = client.redirect_flows.create(
+			params={
+				"description": _("Pay {0} {1}").format(data["amount"], data["currency"]),
+				"session_token": frappe.session.user,
+				"success_redirect_url": success_url,
+				"prefilled_customer": prefilled_customer,
+			}
+		)
 
 		return {"redirect_to": redirect_flow.redirect_url}
 
 	except Exception as e:
-		frappe.log_error(e, "GoCardless Payment Error")
-		return {"redirect_to": '/integrations/payment-failed'}
+		frappe.log_error("GoCardless Payment Error")
+		return {"redirect_to": "/integrations/payment-failed"}
diff --git a/erpnext/templates/pages/integrations/gocardless_confirmation.py b/erpnext/templates/pages/integrations/gocardless_confirmation.py
index a6c3e71..559aa48 100644
--- a/erpnext/templates/pages/integrations/gocardless_confirmation.py
+++ b/erpnext/templates/pages/integrations/gocardless_confirmation.py
@@ -11,7 +11,8 @@
 
 no_cache = 1
 
-expected_keys = ('redirect_flow_id', 'reference_doctype', 'reference_docname')
+expected_keys = ("redirect_flow_id", "reference_doctype", "reference_docname")
+
 
 def get_context(context):
 	context.no_cache = 1
@@ -22,11 +23,14 @@
 			context[key] = frappe.form_dict[key]
 
 	else:
-		frappe.redirect_to_message(_('Some information is missing'),
-			_('Looks like someone sent you to an incomplete URL. Please ask them to look into it.'))
+		frappe.redirect_to_message(
+			_("Some information is missing"),
+			_("Looks like someone sent you to an incomplete URL. Please ask them to look into it."),
+		)
 		frappe.local.flags.redirect_location = frappe.local.response.location
 		raise frappe.Redirect
 
+
 @frappe.whitelist(allow_guest=True)
 def confirm_payment(redirect_flow_id, reference_doctype, reference_docname):
 
@@ -34,15 +38,15 @@
 
 	try:
 		redirect_flow = client.redirect_flows.complete(
-			redirect_flow_id,
-			params={
-				"session_token": frappe.session.user
-		})
+			redirect_flow_id, params={"session_token": frappe.session.user}
+		)
 
 		confirmation_url = redirect_flow.confirmation_url
-		gocardless_success_page = frappe.get_hooks('gocardless_success_page')
+		gocardless_success_page = frappe.get_hooks("gocardless_success_page")
 		if gocardless_success_page:
-			confirmation_url = frappe.get_attr(gocardless_success_page[-1])(reference_doctype, reference_docname)
+			confirmation_url = frappe.get_attr(gocardless_success_page[-1])(
+				reference_doctype, reference_docname
+			)
 
 		data = {
 			"mandate": redirect_flow.links.mandate,
@@ -50,13 +54,13 @@
 			"redirect_to": confirmation_url,
 			"redirect_message": "Mandate successfully created",
 			"reference_doctype": reference_doctype,
-			"reference_docname": reference_docname
+			"reference_docname": reference_docname,
 		}
 
 		try:
 			create_mandate(data)
 		except Exception as e:
-			frappe.log_error(e, "GoCardless Mandate Registration Error")
+			frappe.log_error("GoCardless Mandate Registration Error")
 
 		gateway_controller = get_gateway_controller(reference_docname)
 		frappe.get_doc("GoCardless Settings", gateway_controller).create_payment_request(data)
@@ -64,30 +68,39 @@
 		return {"redirect_to": confirmation_url}
 
 	except Exception as e:
-		frappe.log_error(e, "GoCardless Payment Error")
-		return {"redirect_to": '/integrations/payment-failed'}
+		frappe.log_error("GoCardless Payment Error")
+		return {"redirect_to": "/integrations/payment-failed"}
 
 
 def create_mandate(data):
 	data = frappe._dict(data)
 	frappe.logger().debug(data)
 
-	mandate = data.get('mandate')
+	mandate = data.get("mandate")
 
 	if frappe.db.exists("GoCardless Mandate", mandate):
 		return
 
 	else:
-		reference_doc = frappe.db.get_value(data.get('reference_doctype'), data.get('reference_docname'), ["reference_doctype", "reference_name"], as_dict=1)
-		erpnext_customer = frappe.db.get_value(reference_doc.reference_doctype, reference_doc.reference_name, ["customer_name"], as_dict=1)
+		reference_doc = frappe.db.get_value(
+			data.get("reference_doctype"),
+			data.get("reference_docname"),
+			["reference_doctype", "reference_name"],
+			as_dict=1,
+		)
+		erpnext_customer = frappe.db.get_value(
+			reference_doc.reference_doctype, reference_doc.reference_name, ["customer_name"], as_dict=1
+		)
 
 		try:
-			frappe.get_doc({
-			"doctype": "GoCardless Mandate",
-			"mandate": mandate,
-			"customer": erpnext_customer.customer_name,
-			"gocardless_customer": data.get('customer')
-			}).insert(ignore_permissions=True)
+			frappe.get_doc(
+				{
+					"doctype": "GoCardless Mandate",
+					"mandate": mandate,
+					"customer": erpnext_customer.customer_name,
+					"gocardless_customer": data.get("customer"),
+				}
+			).insert(ignore_permissions=True)
 
 		except Exception:
-			frappe.log_error(frappe.get_traceback())
+			frappe.log_error("Gocardless: Unable to create mandate")
diff --git a/erpnext/templates/pages/material_request_info.py b/erpnext/templates/pages/material_request_info.py
index 65d4427..301ca01 100644
--- a/erpnext/templates/pages/material_request_info.py
+++ b/erpnext/templates/pages/material_request_info.py
@@ -20,17 +20,23 @@
 	if not frappe.has_website_permission(context.doc):
 		frappe.throw(_("Not Permitted"), frappe.PermissionError)
 
-	default_print_format = frappe.db.get_value('Property Setter', dict(property='default_print_format', doc_type=frappe.form_dict.doctype), "value")
+	default_print_format = frappe.db.get_value(
+		"Property Setter",
+		dict(property="default_print_format", doc_type=frappe.form_dict.doctype),
+		"value",
+	)
 	if default_print_format:
 		context.print_format = default_print_format
 	else:
 		context.print_format = "Standard"
 	context.doc.items = get_more_items_info(context.doc.items, context.doc.name)
 
+
 def get_more_items_info(items, material_request):
 	for item in items:
-		item.customer_provided = frappe.get_value('Item', item.item_code, 'is_customer_provided_item')
-		item.work_orders = frappe.db.sql("""
+		item.customer_provided = frappe.get_value("Item", item.item_code, "is_customer_provided_item")
+		item.work_orders = frappe.db.sql(
+			"""
 			select
 				wo.name, wo.status, wo_item.consumed_qty
 			from
@@ -41,9 +47,16 @@
 				and wo_item.parent=wo.name
 				and wo.status not in ('Completed', 'Cancelled', 'Stopped')
 			order by
-				wo.name asc""", item.item_code, as_dict=1)
-		item.delivered_qty = flt(frappe.db.sql("""select sum(transfer_qty)
+				wo.name asc""",
+			item.item_code,
+			as_dict=1,
+		)
+		item.delivered_qty = flt(
+			frappe.db.sql(
+				"""select sum(transfer_qty)
 						from `tabStock Entry Detail` where material_request = %s
 						and item_code = %s and docstatus = 1""",
-						(material_request, item.item_code))[0][0])
+				(material_request, item.item_code),
+			)[0][0]
+		)
 	return items
diff --git a/erpnext/education/doctype/__init__.py b/erpnext/templates/pages/non_profit/__init__.py
similarity index 100%
copy from erpnext/education/doctype/__init__.py
copy to erpnext/templates/pages/non_profit/__init__.py
diff --git a/erpnext/templates/pages/order.py b/erpnext/templates/pages/order.py
index 712b141..3e6d57a 100644
--- a/erpnext/templates/pages/order.py
+++ b/erpnext/templates/pages/order.py
@@ -19,12 +19,17 @@
 
 	context.parents = frappe.form_dict.parents
 	context.title = frappe.form_dict.name
-	context.payment_ref = frappe.db.get_value("Payment Request",
-		{"reference_name": frappe.form_dict.name}, "name")
+	context.payment_ref = frappe.db.get_value(
+		"Payment Request", {"reference_name": frappe.form_dict.name}, "name"
+	)
 
 	context.enabled_checkout = frappe.get_doc("E Commerce Settings").enable_checkout
 
-	default_print_format = frappe.db.get_value('Property Setter', dict(property='default_print_format', doc_type=frappe.form_dict.doctype), "value")
+	default_print_format = frappe.db.get_value(
+		"Property Setter",
+		dict(property="default_print_format", doc_type=frappe.form_dict.doctype),
+		"value",
+	)
 	if default_print_format:
 		context.print_format = default_print_format
 	else:
@@ -34,15 +39,23 @@
 		frappe.throw(_("Not Permitted"), frappe.PermissionError)
 
 	# check for the loyalty program of the customer
-	customer_loyalty_program = frappe.db.get_value("Customer", context.doc.customer, "loyalty_program")
+	customer_loyalty_program = frappe.db.get_value(
+		"Customer", context.doc.customer, "loyalty_program"
+	)
 	if customer_loyalty_program:
 		from erpnext.accounts.doctype.loyalty_program.loyalty_program import (
 			get_loyalty_program_details_with_points,
 		)
-		loyalty_program_details = get_loyalty_program_details_with_points(context.doc.customer, customer_loyalty_program)
+
+		loyalty_program_details = get_loyalty_program_details_with_points(
+			context.doc.customer, customer_loyalty_program
+		)
 		context.available_loyalty_points = int(loyalty_program_details.get("loyalty_points"))
 
+
 def get_attachments(dt, dn):
-        return frappe.get_all("File",
-			fields=["name", "file_name", "file_url", "is_private"],
-			filters = {"attached_to_name": dn, "attached_to_doctype": dt, "is_private":0})
+	return frappe.get_all(
+		"File",
+		fields=["name", "file_name", "file_url", "is_private"],
+		filters={"attached_to_name": dn, "attached_to_doctype": dt, "is_private": 0},
+	)
diff --git a/erpnext/templates/pages/partners.py b/erpnext/templates/pages/partners.py
index e4043ea..8a49504 100644
--- a/erpnext/templates/pages/partners.py
+++ b/erpnext/templates/pages/partners.py
@@ -6,11 +6,12 @@
 
 page_title = "Partners"
 
-def get_context(context):
-	partners = frappe.db.sql("""select * from `tabSales Partner`
-			where show_in_website=1 order by name asc""", as_dict=True)
 
-	return {
-		"partners": partners,
-		"title": page_title
-	}
+def get_context(context):
+	partners = frappe.db.sql(
+		"""select * from `tabSales Partner`
+			where show_in_website=1 order by name asc""",
+		as_dict=True,
+	)
+
+	return {"partners": partners, "title": page_title}
diff --git a/erpnext/templates/pages/product_search.py b/erpnext/templates/pages/product_search.py
index 237adf9..0768cc3 100644
--- a/erpnext/templates/pages/product_search.py
+++ b/erpnext/templates/pages/product_search.py
@@ -1,6 +1,8 @@
 # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 
+import json
+
 import frappe
 from frappe.utils import cint, cstr
 from redisearch import AutoCompleter, Client, Query
@@ -9,7 +11,7 @@
 	WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE,
 	WEBSITE_ITEM_INDEX,
 	WEBSITE_ITEM_NAME_AUTOCOMPLETE,
-	is_search_module_loaded,
+	is_redisearch_enabled,
 	make_key,
 )
 from erpnext.e_commerce.shopping_cart.product_info import set_product_info_for_website
@@ -17,9 +19,11 @@
 
 no_cache = 1
 
+
 def get_context(context):
 	context.show_search = True
 
+
 @frappe.whitelist(allow_guest=True)
 def get_product_list(search=None, start=0, limit=12):
 	data = get_product_data(search, start, limit)
@@ -29,6 +33,7 @@
 
 	return [get_item_for_list_in_html(r) for r in data]
 
+
 def get_product_data(search=None, start=0, limit=12):
 	# limit = 12 because we show 12 items in the grid view
 	# base query
@@ -51,9 +56,13 @@
 		search = "%" + cstr(search) + "%"
 
 	# order by
-	query += """ ORDER BY ranking desc, modified desc limit %s, %s""" % (cint(start), cint(limit))
+	query += """ ORDER BY ranking desc, modified desc limit %s offset %s""" % (
+		cint(limit),
+		cint(start),
+	)
 
-	return frappe.db.sql(query, {"search": search}, as_dict=1) # nosemgrep
+	return frappe.db.sql(query, {"search": search}, as_dict=1)  # nosemgrep
+
 
 @frappe.whitelist(allow_guest=True)
 def search(query):
@@ -62,15 +71,16 @@
 
 	return {
 		"product_results": product_results.get("results") or [],
-		"category_results": category_results.get("results") or []
+		"category_results": category_results.get("results") or [],
 	}
 
+
 @frappe.whitelist(allow_guest=True)
 def product_search(query, limit=10, fuzzy_search=True):
 	search_results = {"from_redisearch": True, "results": []}
 
-	if not is_search_module_loaded():
-		# Redisearch module not loaded
+	if not is_redisearch_enabled():
+		# Redisearch module not enabled
 		search_results["from_redisearch"] = False
 		search_results["results"] = get_product_data(query, 0, limit)
 		return search_results
@@ -81,12 +91,12 @@
 	red = frappe.cache()
 	query = clean_up_query(query)
 
+	# TODO: Check perf/correctness with Suggestions & Query vs only Query
+	# TODO: Use Levenshtein Distance in Query (max=3)
 	ac = AutoCompleter(make_key(WEBSITE_ITEM_NAME_AUTOCOMPLETE), conn=red)
 	client = Client(make_key(WEBSITE_ITEM_INDEX), conn=red)
 	suggestions = ac.get_suggestions(
-		query,
-		num=limit,
-		fuzzy= fuzzy_search and len(query) > 3 # Fuzzy on length < 3 can be real slow
+		query, num=limit, fuzzy=fuzzy_search and len(query) > 3  # Fuzzy on length < 3 can be real slow
 	)
 
 	# Build a query
@@ -98,40 +108,44 @@
 	q = Query(query_string)
 
 	results = client.search(q)
-	search_results['results'] = list(map(convert_to_dict, results.docs))
-	search_results['results'] = sorted(search_results['results'], key=lambda k: frappe.utils.cint(k['ranking']), reverse=True)
+	search_results["results"] = list(map(convert_to_dict, results.docs))
+	search_results["results"] = sorted(
+		search_results["results"], key=lambda k: frappe.utils.cint(k["ranking"]), reverse=True
+	)
 
 	return search_results
 
+
 def clean_up_query(query):
-	return ''.join(c for c in query if c.isalnum() or c.isspace())
+	return "".join(c for c in query if c.isalnum() or c.isspace())
+
 
 def convert_to_dict(redis_search_doc):
 	return redis_search_doc.__dict__
 
+
 @frappe.whitelist(allow_guest=True)
 def get_category_suggestions(query):
 	search_results = {"results": []}
 
-	if not is_search_module_loaded():
-		# Redisearch module not loaded, query db
+	if not is_redisearch_enabled():
+		# Redisearch module not enabled, query db
 		categories = frappe.db.get_all(
 			"Item Group",
-			filters={
-				"name": ["like", "%{0}%".format(query)],
-				"show_in_website": 1
-			},
-			fields=["name", "route"]
+			filters={"name": ["like", "%{0}%".format(query)], "show_in_website": 1},
+			fields=["name", "route"],
 		)
-		search_results['results'] = categories
+		search_results["results"] = categories
 		return search_results
 
 	if not query:
 		return search_results
 
 	ac = AutoCompleter(make_key(WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE), conn=frappe.cache())
-	suggestions = ac.get_suggestions(query, num=10)
+	suggestions = ac.get_suggestions(query, num=10, with_payloads=True)
 
-	search_results['results'] = [s.string for s in suggestions]
+	results = [json.loads(s.payload) for s in suggestions]
 
-	return search_results
\ No newline at end of file
+	search_results["results"] = results
+
+	return search_results
diff --git a/erpnext/templates/pages/projects.py b/erpnext/templates/pages/projects.py
index 16aa439..4b3089b 100644
--- a/erpnext/templates/pages/projects.py
+++ b/erpnext/templates/pages/projects.py
@@ -6,21 +6,28 @@
 
 
 def get_context(context):
-	project_user = frappe.db.get_value("Project User", {"parent": frappe.form_dict.project, "user": frappe.session.user} , ["user", "view_attachments"], as_dict= True)
-	if frappe.session.user != 'Administrator' and (not project_user or frappe.session.user == 'Guest'):
+	project_user = frappe.db.get_value(
+		"Project User",
+		{"parent": frappe.form_dict.project, "user": frappe.session.user},
+		["user", "view_attachments"],
+		as_dict=True,
+	)
+	if frappe.session.user != "Administrator" and (
+		not project_user or frappe.session.user == "Guest"
+	):
 		raise frappe.PermissionError
 
 	context.no_cache = 1
 	context.show_sidebar = True
-	project = frappe.get_doc('Project', frappe.form_dict.project)
+	project = frappe.get_doc("Project", frappe.form_dict.project)
 
-	project.has_permission('read')
+	project.has_permission("read")
 
-	project.tasks = get_tasks(project.name, start=0, item_status='open',
-		search=frappe.form_dict.get("search"))
+	project.tasks = get_tasks(
+		project.name, start=0, item_status="open", search=frappe.form_dict.get("search")
+	)
 
-	project.timesheets = get_timesheets(project.name, start=0,
-		search=frappe.form_dict.get("search"))
+	project.timesheets = get_timesheets(project.name, start=0, search=frappe.form_dict.get("search"))
 
 	if project_user and project_user.view_attachments:
 		project.attachments = get_attachments(project.name)
@@ -32,9 +39,22 @@
 	filters = {"project": project}
 	if search:
 		filters["subject"] = ("like", "%{0}%".format(search))
-	tasks = frappe.get_all("Task", filters=filters,
-		fields=["name", "subject", "status", "modified", "_assign", "exp_end_date", "is_group", "parent_task"],
-		limit_start=start, limit_page_length=10)
+	tasks = frappe.get_all(
+		"Task",
+		filters=filters,
+		fields=[
+			"name",
+			"subject",
+			"status",
+			"modified",
+			"_assign",
+			"exp_end_date",
+			"is_group",
+			"parent_task",
+		],
+		limit_start=start,
+		limit_page_length=10,
+	)
 	task_nest = []
 	for task in tasks:
 		if task.is_group:
@@ -44,36 +64,59 @@
 		task_nest.append(task)
 	return list(filter(lambda x: not x.parent_task, tasks))
 
+
 @frappe.whitelist()
 def get_task_html(project, start=0, item_status=None):
-	return frappe.render_template("erpnext/templates/includes/projects/project_tasks.html",
-		{"doc": {
-			"name": project,
-			"project_name": project,
-			"tasks": get_tasks(project, start, item_status=item_status)}
-		}, is_path=True)
+	return frappe.render_template(
+		"erpnext/templates/includes/projects/project_tasks.html",
+		{
+			"doc": {
+				"name": project,
+				"project_name": project,
+				"tasks": get_tasks(project, start, item_status=item_status),
+			}
+		},
+		is_path=True,
+	)
+
 
 def get_timesheets(project, start=0, search=None):
 	filters = {"project": project}
 	if search:
 		filters["activity_type"] = ("like", "%{0}%".format(search))
 
-	timesheets = frappe.get_all('Timesheet Detail', filters=filters,
-	fields=['project','activity_type','from_time','to_time','parent'],
-	limit_start=start, limit_page_length=10)
+	timesheets = frappe.get_all(
+		"Timesheet Detail",
+		filters=filters,
+		fields=["project", "activity_type", "from_time", "to_time", "parent"],
+		limit_start=start,
+		limit_page_length=10,
+	)
 	for timesheet in timesheets:
-		info = frappe.get_all('Timesheet', filters={"name": timesheet.parent},
-			fields=['name','status','modified','modified_by'],
-			limit_start=start, limit_page_length=10)
+		info = frappe.get_all(
+			"Timesheet",
+			filters={"name": timesheet.parent},
+			fields=["name", "status", "modified", "modified_by"],
+			limit_start=start,
+			limit_page_length=10,
+		)
 		if len(info):
 			timesheet.update(info[0])
 	return timesheets
 
+
 @frappe.whitelist()
 def get_timesheet_html(project, start=0):
-	return frappe.render_template("erpnext/templates/includes/projects/project_timesheets.html",
-		{"doc": {"timesheets": get_timesheets(project, start)}}, is_path=True)
+	return frappe.render_template(
+		"erpnext/templates/includes/projects/project_timesheets.html",
+		{"doc": {"timesheets": get_timesheets(project, start)}},
+		is_path=True,
+	)
+
 
 def get_attachments(project):
-	return frappe.get_all('File', filters= {"attached_to_name": project, "attached_to_doctype": 'Project', "is_private":0},
-		fields=['file_name','file_url', 'file_size'])
+	return frappe.get_all(
+		"File",
+		filters={"attached_to_name": project, "attached_to_doctype": "Project", "is_private": 0},
+		fields=["file_name", "file_url", "file_size"],
+	)
diff --git a/erpnext/templates/pages/rfq.py b/erpnext/templates/pages/rfq.py
index 0afd46c..4b83642 100644
--- a/erpnext/templates/pages/rfq.py
+++ b/erpnext/templates/pages/rfq.py
@@ -20,40 +20,59 @@
 	update_supplier_details(context)
 	context["title"] = frappe.form_dict.name
 
+
 def get_supplier():
 	doctype = frappe.form_dict.doctype
-	parties_doctype = 'Request for Quotation Supplier' if doctype == 'Request for Quotation' else doctype
+	parties_doctype = (
+		"Request for Quotation Supplier" if doctype == "Request for Quotation" else doctype
+	)
 	customers, suppliers = get_customers_suppliers(parties_doctype, frappe.session.user)
 
-	return suppliers[0] if suppliers else ''
+	return suppliers[0] if suppliers else ""
+
 
 def check_supplier_has_docname_access(supplier):
 	status = True
-	if frappe.form_dict.name not in frappe.db.sql_list("""select parent from `tabRequest for Quotation Supplier`
-		where supplier = %s""", (supplier,)):
+	if frappe.form_dict.name not in frappe.db.sql_list(
+		"""select parent from `tabRequest for Quotation Supplier`
+		where supplier = %s""",
+		(supplier,),
+	):
 		status = False
 	return status
 
+
 def unauthorized_user(supplier):
 	status = check_supplier_has_docname_access(supplier) or False
 	if status == False:
 		frappe.throw(_("Not Permitted"), frappe.PermissionError)
 
+
 def update_supplier_details(context):
 	supplier_doc = frappe.get_doc("Supplier", context.doc.supplier)
-	context.doc.currency = supplier_doc.default_currency or frappe.get_cached_value('Company',  context.doc.company,  "default_currency")
-	context.doc.currency_symbol = frappe.db.get_value("Currency", context.doc.currency, "symbol", cache=True)
-	context.doc.number_format = frappe.db.get_value("Currency", context.doc.currency, "number_format", cache=True)
-	context.doc.buying_price_list = supplier_doc.default_price_list or ''
+	context.doc.currency = supplier_doc.default_currency or frappe.get_cached_value(
+		"Company", context.doc.company, "default_currency"
+	)
+	context.doc.currency_symbol = frappe.db.get_value(
+		"Currency", context.doc.currency, "symbol", cache=True
+	)
+	context.doc.number_format = frappe.db.get_value(
+		"Currency", context.doc.currency, "number_format", cache=True
+	)
+	context.doc.buying_price_list = supplier_doc.default_price_list or ""
+
 
 def get_link_quotation(supplier, rfq):
-	quotation = frappe.db.sql(""" select distinct `tabSupplier Quotation Item`.parent as name,
+	quotation = frappe.db.sql(
+		""" select distinct `tabSupplier Quotation Item`.parent as name,
 		`tabSupplier Quotation`.status, `tabSupplier Quotation`.transaction_date from
 		`tabSupplier Quotation Item`, `tabSupplier Quotation` where `tabSupplier Quotation`.docstatus < 2 and
 		`tabSupplier Quotation Item`.request_for_quotation =%(name)s and
 		`tabSupplier Quotation Item`.parent = `tabSupplier Quotation`.name and
 		`tabSupplier Quotation`.supplier = %(supplier)s order by `tabSupplier Quotation`.creation desc""",
-		{'name': rfq, 'supplier': supplier}, as_dict=1)
+		{"name": rfq, "supplier": supplier},
+		as_dict=1,
+	)
 
 	for data in quotation:
 		data.transaction_date = formatdate(data.transaction_date)
diff --git a/erpnext/templates/pages/search_help.py b/erpnext/templates/pages/search_help.py
index 1ef3942..a6877ce 100644
--- a/erpnext/templates/pages/search_help.py
+++ b/erpnext/templates/pages/search_help.py
@@ -11,17 +11,18 @@
 	context.no_cache = 1
 	if frappe.form_dict.q:
 		query = str(utils.escape(sanitize_html(frappe.form_dict.q)))
-		context.title = _('Help Results for')
+		context.title = _("Help Results for")
 		context.query = query
 
-		context.route = '/search_help'
+		context.route = "/search_help"
 		d = frappe._dict()
 		d.results_sections = get_help_results_sections(query)
 		context.update(d)
 	else:
-		context.title = _('Docs Search')
+		context.title = _("Docs Search")
 
-@frappe.whitelist(allow_guest = True)
+
+@frappe.whitelist(allow_guest=True)
 def get_help_results_sections(text):
 	out = []
 	settings = frappe.get_doc("Support Settings", "Support Settings")
@@ -40,63 +41,72 @@
 
 		if results:
 			# Add section
-			out.append({
-				"title": api.source_name,
-				"results": results
-			})
+			out.append({"title": api.source_name, "results": results})
 
 	return out
 
+
 def get_response(api, text):
-	response = requests.get(api.base_url + '/' + api.query_route, data={
-		api.search_term_param_name: text
-	})
+	response = requests.get(
+		api.base_url + "/" + api.query_route, data={api.search_term_param_name: text}
+	)
 
 	response.raise_for_status()
 	return response.json()
 
+
 def get_topics_data(api, response_json):
 	if not response_json:
 		response_json = {}
-	topics_data = {} # it will actually be an array
-	key_list = api.response_result_key_path.split(',')
+	topics_data = {}  # it will actually be an array
+	key_list = api.response_result_key_path.split(",")
 
 	for key in key_list:
 		topics_data = response_json.get(key) if not topics_data else topics_data.get(key)
 
 	return topics_data or []
 
+
 def prepare_api_results(api, topics_data):
 	if not topics_data:
 		topics_data = []
 
 	results = []
 	for topic in topics_data:
-		route = api.base_url + '/' + (api.post_route  + '/' if api.post_route else "")
-		for key in api.post_route_key_list.split(','):
+		route = api.base_url + "/" + (api.post_route + "/" if api.post_route else "")
+		for key in api.post_route_key_list.split(","):
 			route += str(topic[key])
 
-		results.append(frappe._dict({
-			'title': topic[api.post_title_key],
-			'preview': html2text(topic[api.post_description_key]),
-			'route': route
-		}))
+		results.append(
+			frappe._dict(
+				{
+					"title": topic[api.post_title_key],
+					"preview": html2text(topic[api.post_description_key]),
+					"route": route,
+				}
+			)
+		)
 	return results[:5]
 
+
 def prepare_doctype_results(api, raw):
 	results = []
 	for r in raw:
 		prepared_result = {}
-		parts = r["content"].split(' ||| ')
+		parts = r["content"].split(" ||| ")
 
 		for part in parts:
-			pair = part.split(' : ', 1)
+			pair = part.split(" : ", 1)
 			prepared_result[pair[0]] = pair[1]
 
-		results.append(frappe._dict({
-			'title': prepared_result[api.result_title_field],
-			'preview': prepared_result[api.result_preview_field],
-			'route': prepared_result[api.result_route_field]
-		}))
+		results.append(
+			frappe._dict(
+				{
+					"title": prepared_result[api.result_title_field],
+					"preview": prepared_result[api.result_preview_field],
+					"route": prepared_result[api.result_route_field],
+				}
+			)
+		)
 
 	return results
diff --git a/erpnext/templates/pages/task_info.py b/erpnext/templates/pages/task_info.py
index d1a70e1..66b775a 100644
--- a/erpnext/templates/pages/task_info.py
+++ b/erpnext/templates/pages/task_info.py
@@ -4,9 +4,12 @@
 def get_context(context):
 	context.no_cache = 1
 
-	task = frappe.get_doc('Task', frappe.form_dict.task)
+	task = frappe.get_doc("Task", frappe.form_dict.task)
 
-	context.comments = frappe.get_all('Communication', filters={'reference_name': task.name, 'comment_type': 'comment'},
-	fields=['subject', 'sender_full_name', 'communication_date'])
+	context.comments = frappe.get_all(
+		"Communication",
+		filters={"reference_name": task.name, "comment_type": "comment"},
+		fields=["subject", "sender_full_name", "communication_date"],
+	)
 
 	context.doc = task
diff --git a/erpnext/templates/pages/timelog_info.py b/erpnext/templates/pages/timelog_info.py
index db61e7e..3f0ec37 100644
--- a/erpnext/templates/pages/timelog_info.py
+++ b/erpnext/templates/pages/timelog_info.py
@@ -4,6 +4,6 @@
 def get_context(context):
 	context.no_cache = 1
 
-	timelog = frappe.get_doc('Time Log', frappe.form_dict.timelog)
+	timelog = frappe.get_doc("Time Log", frappe.form_dict.timelog)
 
 	context.doc = timelog
diff --git a/erpnext/templates/pages/wishlist.py b/erpnext/templates/pages/wishlist.py
index 72ee34e..d70f27c 100644
--- a/erpnext/templates/pages/wishlist.py
+++ b/erpnext/templates/pages/wishlist.py
@@ -23,31 +23,33 @@
 	context.settings = settings
 	context.no_cache = 1
 
+
 def get_stock_availability(item_code, warehouse):
 	stock_qty = frappe.utils.flt(
-		frappe.db.get_value("Bin",
-			{
-				"item_code": item_code,
-				"warehouse": warehouse
-			},
-			"actual_qty")
+		frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "actual_qty")
 	)
 	return bool(stock_qty)
 
+
 def get_wishlist_items():
 	if not frappe.db.exists("Wishlist", frappe.session.user):
 		return []
 
 	return frappe.db.get_all(
 		"Wishlist Item",
-		filters={
-			"parent": frappe.session.user
-		},
+		filters={"parent": frappe.session.user},
 		fields=[
-			"web_item_name", "item_code", "item_name",
-			"website_item", "warehouse",
-			"image", "item_group", "route"
-		])
+			"web_item_name",
+			"item_code",
+			"item_name",
+			"website_item",
+			"warehouse",
+			"image",
+			"item_group",
+			"route",
+		],
+	)
+
 
 def set_stock_price_details(items, settings, selling_price_list):
 	for item in items:
@@ -55,17 +57,15 @@
 			item.available = get_stock_availability(item.item_code, item.get("warehouse"))
 
 		price_details = get_price(
-			item.item_code,
-			selling_price_list,
-			settings.default_customer_group,
-			settings.company
+			item.item_code, selling_price_list, settings.default_customer_group, settings.company
 		)
 
 		if price_details:
-			item.formatted_price = price_details.get('formatted_price')
-			item.formatted_mrp = price_details.get('formatted_mrp')
+			item.formatted_price = price_details.get("formatted_price")
+			item.formatted_mrp = price_details.get("formatted_mrp")
 			if item.formatted_mrp:
-				item.discount = price_details.get('formatted_discount_percent') or \
-					price_details.get('formatted_discount_rate')
+				item.discount = price_details.get("formatted_discount_percent") or price_details.get(
+					"formatted_discount_rate"
+				)
 
-	return items
\ No newline at end of file
+	return items
diff --git a/erpnext/templates/utils.py b/erpnext/templates/utils.py
index 9f46e6a..4295188 100644
--- a/erpnext/templates/utils.py
+++ b/erpnext/templates/utils.py
@@ -8,31 +8,35 @@
 @frappe.whitelist(allow_guest=True)
 def send_message(subject="Website Query", message="", sender="", status="Open"):
 	from frappe.www.contact import send_message as website_send_message
+
 	lead = customer = None
 
 	website_send_message(subject, message, sender)
 
-	customer = frappe.db.sql("""select distinct dl.link_name from `tabDynamic Link` dl
+	customer = frappe.db.sql(
+		"""select distinct dl.link_name from `tabDynamic Link` dl
 		left join `tabContact` c on dl.parent=c.name where dl.link_doctype='Customer'
-		and c.email_id = %s""", sender)
+		and c.email_id = %s""",
+		sender,
+	)
 
 	if not customer:
-		lead = frappe.db.get_value('Lead', dict(email_id=sender))
+		lead = frappe.db.get_value("Lead", dict(email_id=sender))
 		if not lead:
-			new_lead = frappe.get_doc(dict(
-				doctype='Lead',
-				email_id = sender,
-				lead_name = sender.split('@')[0].title()
-			)).insert(ignore_permissions=True)
+			new_lead = frappe.get_doc(
+				dict(doctype="Lead", email_id=sender, lead_name=sender.split("@")[0].title())
+			).insert(ignore_permissions=True)
 
-	opportunity = frappe.get_doc(dict(
-		doctype ='Opportunity',
-		opportunity_from = 'Customer' if customer else 'Lead',
-		status = 'Open',
-		title = subject,
-		contact_email = sender,
-		to_discuss = message
-	))
+	opportunity = frappe.get_doc(
+		dict(
+			doctype="Opportunity",
+			opportunity_from="Customer" if customer else "Lead",
+			status="Open",
+			title=subject,
+			contact_email=sender,
+			to_discuss=message,
+		)
+	)
 
 	if customer:
 		opportunity.party_name = customer[0][0]
@@ -43,15 +47,17 @@
 
 	opportunity.insert(ignore_permissions=True)
 
-	comm = frappe.get_doc({
-		"doctype":"Communication",
-		"subject": subject,
-		"content": message,
-		"sender": sender,
-		"sent_or_received": "Received",
-		'reference_doctype': 'Opportunity',
-		'reference_name': opportunity.name
-	})
+	comm = frappe.get_doc(
+		{
+			"doctype": "Communication",
+			"subject": subject,
+			"content": message,
+			"sender": sender,
+			"sent_or_received": "Received",
+			"reference_doctype": "Opportunity",
+			"reference_name": opportunity.name,
+		}
+	)
 	comm.insert(ignore_permissions=True)
 
 	return "okay"
diff --git a/erpnext/tests/__init__.py b/erpnext/tests/__init__.py
index a504340..dc37472 100644
--- a/erpnext/tests/__init__.py
+++ b/erpnext/tests/__init__.py
@@ -1 +1 @@
-global_test_dependencies = ['User', 'Company', 'Item']
+global_test_dependencies = ["User", "Company", "Item"]
diff --git a/erpnext/tests/exotel_test_data.py b/erpnext/tests/exotel_test_data.py
new file mode 100644
index 0000000..3ad2575
--- /dev/null
+++ b/erpnext/tests/exotel_test_data.py
@@ -0,0 +1,122 @@
+import frappe
+
+call_initiation_data = frappe._dict(
+	{
+		"CallSid": "23c162077629863c1a2d7f29263a162m",
+		"CallFrom": "09999999991",
+		"CallTo": "09999999980",
+		"Direction": "incoming",
+		"Created": "Wed, 23 Feb 2022 12:31:59",
+		"From": "09999999991",
+		"To": "09999999988",
+		"CurrentTime": "2022-02-23 12:32:02",
+		"DialWhomNumber": "09999999999",
+		"Status": "busy",
+		"EventType": "Dial",
+		"AgentEmail": "test_employee_exotel@company.com",
+	}
+)
+
+call_end_data = frappe._dict(
+	{
+		"CallSid": "23c162077629863c1a2d7f29263a162m",
+		"CallFrom": "09999999991",
+		"CallTo": "09999999980",
+		"Direction": "incoming",
+		"ForwardedFrom": "null",
+		"Created": "Wed, 23 Feb 2022 12:31:59",
+		"DialCallDuration": "17",
+		"RecordingUrl": "https://s3-ap-southeast-1.amazonaws.com/random.mp3",
+		"StartTime": "2022-02-23 12:31:58",
+		"EndTime": "1970-01-01 05:30:00",
+		"DialCallStatus": "completed",
+		"CallType": "completed",
+		"DialWhomNumber": "09999999999",
+		"ProcessStatus": "null",
+		"flow_id": "228040",
+		"tenant_id": "67291",
+		"From": "09999999991",
+		"To": "09999999988",
+		"RecordingAvailableBy": "Wed, 23 Feb 2022 12:37:25",
+		"CurrentTime": "2022-02-23 12:32:25",
+		"OutgoingPhoneNumber": "09999999988",
+		"Legs": [
+			{
+				"Number": "09999999999",
+				"Type": "single",
+				"OnCallDuration": "10",
+				"CallerId": "09999999980",
+				"CauseCode": "NORMAL_CLEARING",
+				"Cause": "16",
+			}
+		],
+	}
+)
+
+call_disconnected_data = frappe._dict(
+	{
+		"CallSid": "d96421addce69e24bdc7ce5880d1162l",
+		"CallFrom": "09999999991",
+		"CallTo": "09999999980",
+		"Direction": "incoming",
+		"ForwardedFrom": "null",
+		"Created": "Mon, 21 Feb 2022 15:58:12",
+		"DialCallDuration": "0",
+		"StartTime": "2022-02-21 15:58:12",
+		"EndTime": "1970-01-01 05:30:00",
+		"DialCallStatus": "canceled",
+		"CallType": "client-hangup",
+		"DialWhomNumber": "09999999999",
+		"ProcessStatus": "null",
+		"flow_id": "228040",
+		"tenant_id": "67291",
+		"From": "09999999991",
+		"To": "09999999988",
+		"CurrentTime": "2022-02-21 15:58:47",
+		"OutgoingPhoneNumber": "09999999988",
+		"Legs": [
+			{
+				"Number": "09999999999",
+				"Type": "single",
+				"OnCallDuration": "0",
+				"CallerId": "09999999980",
+				"CauseCode": "RING_TIMEOUT",
+				"Cause": "1003",
+			}
+		],
+	}
+)
+
+call_not_answered_data = frappe._dict(
+	{
+		"CallSid": "fdb67a2b4b2d057b610a52ef43f81622",
+		"CallFrom": "09999999991",
+		"CallTo": "09999999980",
+		"Direction": "incoming",
+		"ForwardedFrom": "null",
+		"Created": "Mon, 21 Feb 2022 15:47:02",
+		"DialCallDuration": "0",
+		"StartTime": "2022-02-21 15:47:02",
+		"EndTime": "1970-01-01 05:30:00",
+		"DialCallStatus": "no-answer",
+		"CallType": "incomplete",
+		"DialWhomNumber": "09999999999",
+		"ProcessStatus": "null",
+		"flow_id": "228040",
+		"tenant_id": "67291",
+		"From": "09999999991",
+		"To": "09999999988",
+		"CurrentTime": "2022-02-21 15:47:40",
+		"OutgoingPhoneNumber": "09999999988",
+		"Legs": [
+			{
+				"Number": "09999999999",
+				"Type": "single",
+				"OnCallDuration": "0",
+				"CallerId": "09999999980",
+				"CauseCode": "RING_TIMEOUT",
+				"Cause": "1003",
+			}
+		],
+	}
+)
diff --git a/erpnext/tests/server/agriculture.txt b/erpnext/tests/server/agriculture.txt
deleted file mode 100644
index 29dc9ab..0000000
--- a/erpnext/tests/server/agriculture.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Disease
-Crop
-Crop Cycle
-Soil Texture
\ No newline at end of file
diff --git a/erpnext/tests/test_activation.py b/erpnext/tests/test_activation.py
new file mode 100644
index 0000000..b56e233
--- /dev/null
+++ b/erpnext/tests/test_activation.py
@@ -0,0 +1,9 @@
+from frappe.tests.utils import FrappeTestCase
+
+from erpnext.utilities.activation import get_level
+
+
+class TestActivation(FrappeTestCase):
+	def test_activation(self):
+		levels = get_level()
+		self.assertTrue(levels)
diff --git a/erpnext/tests/test_exotel.py b/erpnext/tests/test_exotel.py
new file mode 100644
index 0000000..31baf75
--- /dev/null
+++ b/erpnext/tests/test_exotel.py
@@ -0,0 +1,68 @@
+import frappe
+from frappe.contacts.doctype.contact.test_contact import create_contact
+from frappe.tests.test_api import FrappeAPITestCase
+
+from erpnext.hr.doctype.employee.test_employee import make_employee
+
+
+class TestExotel(FrappeAPITestCase):
+	@classmethod
+	def setUpClass(cls):
+		cls.CURRENT_DB_CONNECTION = frappe.db
+		cls.test_employee_name = make_employee(
+			user="test_employee_exotel@company.com", cell_number="9999999999"
+		)
+		frappe.db.set_value("Exotel Settings", "Exotel Settings", "enabled", 1)
+		phones = [{"phone": "+91 9999999991", "is_primary_phone": 0, "is_primary_mobile_no": 1}]
+		create_contact(name="Test Contact", salutation="Mr", phones=phones)
+		frappe.db.commit()
+
+	def test_for_successful_call(self):
+		from .exotel_test_data import call_end_data, call_initiation_data
+
+		api_method = "handle_incoming_call"
+		end_call_api_method = "handle_end_call"
+
+		self.emulate_api_call_from_exotel(api_method, call_initiation_data)
+		self.emulate_api_call_from_exotel(end_call_api_method, call_end_data)
+		call_log = frappe.get_doc("Call Log", call_initiation_data.CallSid)
+
+		self.assertEqual(call_log.get("from"), call_initiation_data.CallFrom)
+		self.assertEqual(call_log.get("to"), call_initiation_data.DialWhomNumber)
+		self.assertEqual(call_log.get("call_received_by"), self.test_employee_name)
+		self.assertEqual(call_log.get("status"), "Completed")
+
+	def test_for_disconnected_call(self):
+		from .exotel_test_data import call_disconnected_data
+
+		api_method = "handle_missed_call"
+		self.emulate_api_call_from_exotel(api_method, call_disconnected_data)
+		call_log = frappe.get_doc("Call Log", call_disconnected_data.CallSid)
+		self.assertEqual(call_log.get("from"), call_disconnected_data.CallFrom)
+		self.assertEqual(call_log.get("to"), call_disconnected_data.DialWhomNumber)
+		self.assertEqual(call_log.get("call_received_by"), self.test_employee_name)
+		self.assertEqual(call_log.get("status"), "Canceled")
+
+	def test_for_call_not_answered(self):
+		from .exotel_test_data import call_not_answered_data
+
+		api_method = "handle_missed_call"
+		self.emulate_api_call_from_exotel(api_method, call_not_answered_data)
+		call_log = frappe.get_doc("Call Log", call_not_answered_data.CallSid)
+		self.assertEqual(call_log.get("from"), call_not_answered_data.CallFrom)
+		self.assertEqual(call_log.get("to"), call_not_answered_data.DialWhomNumber)
+		self.assertEqual(call_log.get("call_received_by"), self.test_employee_name)
+		self.assertEqual(call_log.get("status"), "No Answer")
+
+	def emulate_api_call_from_exotel(self, api_method, data):
+		self.post(
+			f"/api/method/erpnext.erpnext_integrations.exotel_integration.{api_method}",
+			data=frappe.as_json(data),
+			content_type="application/json",
+		)
+		# restart db connection to get latest data
+		frappe.connect()
+
+	@classmethod
+	def tearDownClass(cls):
+		frappe.db = cls.CURRENT_DB_CONNECTION
diff --git a/erpnext/tests/test_init.py b/erpnext/tests/test_init.py
index 6184972..18ce93a 100644
--- a/erpnext/tests/test_init.py
+++ b/erpnext/tests/test_init.py
@@ -4,7 +4,8 @@
 
 from erpnext import encode_company_abbr
 
-test_records = frappe.get_test_records('Company')
+test_records = frappe.get_test_records("Company")
+
 
 class TestInit(unittest.TestCase):
 	def test_encode_company_abbr(self):
@@ -12,23 +13,40 @@
 		abbr = "NFECT"
 
 		names = [
-			"Warehouse Name", "ERPNext Foundation India", "Gold - Member - {a}".format(a=abbr),
-			" - {a}".format(a=abbr), "ERPNext - Foundation - India",
+			"Warehouse Name",
+			"ERPNext Foundation India",
+			"Gold - Member - {a}".format(a=abbr),
+			" - {a}".format(a=abbr),
+			"ERPNext - Foundation - India",
 			"ERPNext Foundation India - {a}".format(a=abbr),
-			"No-Space-{a}".format(a=abbr), "- Warehouse"
+			"No-Space-{a}".format(a=abbr),
+			"- Warehouse",
 		]
 
 		expected_names = [
-			"Warehouse Name - {a}".format(a=abbr), "ERPNext Foundation India - {a}".format(a=abbr),
-			"Gold - Member - {a}".format(a=abbr), " - {a}".format(a=abbr),
+			"Warehouse Name - {a}".format(a=abbr),
+			"ERPNext Foundation India - {a}".format(a=abbr),
+			"Gold - Member - {a}".format(a=abbr),
+			" - {a}".format(a=abbr),
 			"ERPNext - Foundation - India - {a}".format(a=abbr),
-			"ERPNext Foundation India - {a}".format(a=abbr), "No-Space-{a} - {a}".format(a=abbr),
-			"- Warehouse - {a}".format(a=abbr)
+			"ERPNext Foundation India - {a}".format(a=abbr),
+			"No-Space-{a} - {a}".format(a=abbr),
+			"- Warehouse - {a}".format(a=abbr),
 		]
 
 		for i in range(len(names)):
 			enc_name = encode_company_abbr(names[i], abbr=abbr)
 			self.assertTrue(
 				enc_name == expected_names[i],
-				"{enc} is not same as {exp}".format(enc=enc_name, exp=expected_names[i])
+				"{enc} is not same as {exp}".format(enc=enc_name, exp=expected_names[i]),
 			)
+
+	def test_translation_files(self):
+		from frappe.tests.test_translate import verify_translation_files
+
+		verify_translation_files("erpnext")
+
+	def test_patches(self):
+		from frappe.tests.test_patches import check_patch_files
+
+		check_patch_files("erpnext")
diff --git a/erpnext/tests/test_notifications.py b/erpnext/tests/test_notifications.py
index 669bf6f..0f39195 100644
--- a/erpnext/tests/test_notifications.py
+++ b/erpnext/tests/test_notifications.py
@@ -10,9 +10,9 @@
 
 class TestNotifications(unittest.TestCase):
 	def test_get_notifications_for_targets(self):
-		'''
-			Test notification config entries for targets as percentages
-		'''
+		"""
+		Test notification config entries for targets as percentages
+		"""
 
 		company = frappe.get_all("Company")[0]
 		frappe.db.set_value("Company", company.name, "monthly_sales_target", 10000)
@@ -21,7 +21,7 @@
 		config = notifications.get_notification_config()
 		doc_target_percents = notifications.get_notifications_for_targets(config, {})
 
-		self.assertEqual(doc_target_percents['Company'][company.name], 10)
+		self.assertEqual(doc_target_percents["Company"][company.name], 10)
 
 		frappe.db.set_value("Company", company.name, "monthly_sales_target", 2000)
 		frappe.db.set_value("Company", company.name, "total_monthly_sales", 0)
@@ -29,4 +29,4 @@
 		config = notifications.get_notification_config()
 		doc_target_percents = notifications.get_notifications_for_targets(config, {})
 
-		self.assertEqual(doc_target_percents['Company'][company.name], 0)
+		self.assertEqual(doc_target_percents["Company"][company.name], 0)
diff --git a/erpnext/tests/test_point_of_sale.py b/erpnext/tests/test_point_of_sale.py
index 38f2c16..7267d4a 100644
--- a/erpnext/tests/test_point_of_sale.py
+++ b/erpnext/tests/test_point_of_sale.py
@@ -14,11 +14,11 @@
 class TestPointOfSale(unittest.TestCase):
 	@classmethod
 	def setUpClass(cls) -> None:
-		frappe.db.savepoint('before_test_point_of_sale')
+		frappe.db.savepoint("before_test_point_of_sale")
 
 	@classmethod
 	def tearDownClass(cls) -> None:
-		frappe.db.rollback(save_point='before_test_point_of_sale')
+		frappe.db.rollback(save_point="before_test_point_of_sale")
 
 	def test_item_search(self):
 		"""
diff --git a/erpnext/tests/test_regional.py b/erpnext/tests/test_regional.py
index 0855bbb..2c16def 100644
--- a/erpnext/tests/test_regional.py
+++ b/erpnext/tests/test_regional.py
@@ -7,12 +7,13 @@
 
 @erpnext.allow_regional
 def test_method():
-	return 'original'
+	return "original"
+
 
 class TestInit(unittest.TestCase):
 	def test_regional_overrides(self):
-		frappe.flags.country = 'Maldives'
-		self.assertEqual(test_method(), 'original')
+		frappe.flags.country = "Maldives"
+		self.assertEqual(test_method(), "original")
 
-		frappe.flags.country = 'France'
-		self.assertEqual(test_method(), 'overridden')
+		frappe.flags.country = "France"
+		self.assertEqual(test_method(), "overridden")
diff --git a/erpnext/tests/test_search.py b/erpnext/tests/test_search.py
deleted file mode 100644
index c169458..0000000
--- a/erpnext/tests/test_search.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import unittest
-
-import frappe
-from frappe.contacts.address_and_contact import filter_dynamic_link_doctypes
-
-
-class TestSearch(unittest.TestCase):
-	# Search for the word "cond", part of the word "conduire" (Lead) in french.
-	def test_contact_search_in_foreign_language(self):
-		try:
-			frappe.local.lang = 'fr'
-			output = filter_dynamic_link_doctypes("DocType", "cond", "name", 0, 20, {
-				'fieldtype': 'HTML',
-				'fieldname': 'contact_html'
-			})
-			result = [['found' for x in y if x=="Lead"] for y in output]
-			self.assertTrue(['found'] in result)
-		finally:
-			frappe.local.lang = 'en'
diff --git a/erpnext/tests/test_subcontracting.py b/erpnext/tests/test_subcontracting.py
index fccfd0d..93d1c8e 100644
--- a/erpnext/tests/test_subcontracting.py
+++ b/erpnext/tests/test_subcontracting.py
@@ -25,35 +25,43 @@
 		make_bom_for_subcontracted_items()
 
 	def test_po_with_bom(self):
-		'''
-			- Set backflush based on BOM
-			- Create subcontracted PO for the item Subcontracted Item SA1 and add same item two times.
-			- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
-			- Create purchase receipt against the PO and check serial nos and batch no.
-		'''
+		"""
+		- Set backflush based on BOM
+		- Create subcontracted PO for the item Subcontracted Item SA1 and add same item two times.
+		- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
+		- Create purchase receipt against the PO and check serial nos and batch no.
+		"""
 
-		set_backflush_based_on('BOM')
-		item_code = 'Subcontracted Item SA1'
-		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 5, 'rate': 100},
-			{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 6, 'rate': 100}]
+		set_backflush_based_on("BOM")
+		item_code = "Subcontracted Item SA1"
+		items = [
+			{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 5, "rate": 100},
+			{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 6, "rate": 100},
+		]
 
-		rm_items = [{'item_code': 'Subcontracted SRM Item 1', 'qty': 5},
-			{'item_code': 'Subcontracted SRM Item 2', 'qty': 5},
-			{'item_code': 'Subcontracted SRM Item 3', 'qty': 5},
-			{'item_code': 'Subcontracted SRM Item 1', 'qty': 6},
-			{'item_code': 'Subcontracted SRM Item 2', 'qty': 6},
-			{'item_code': 'Subcontracted SRM Item 3', 'qty': 6}
+		rm_items = [
+			{"item_code": "Subcontracted SRM Item 1", "qty": 5},
+			{"item_code": "Subcontracted SRM Item 2", "qty": 5},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 5},
+			{"item_code": "Subcontracted SRM Item 1", "qty": 6},
+			{"item_code": "Subcontracted SRM Item 2", "qty": 6},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 6},
 		]
 
 		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
-			supplier_warehouse="_Test Warehouse 1 - _TC")
+		po = create_purchase_order(
+			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
+		)
 
 		for d in rm_items:
-			d['po_detail'] = po.items[0].name if d.get('qty') == 5 else po.items[1].name
+			d["po_detail"] = po.items[0].name if d.get("qty") == 5 else po.items[1].name
 
-		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
-			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+		make_stock_transfer_entry(
+			po_no=po.name,
+			main_item_code=item_code,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
 
 		pr1 = make_purchase_receipt(po.name)
 		pr1.submit()
@@ -61,43 +69,58 @@
 		for key, value in get_supplied_items(pr1).items():
 			transferred_detais = itemwise_details.get(key)
 
-			for field in ['qty', 'serial_no', 'batch_no']:
+			for field in ["qty", "serial_no", "batch_no"]:
 				if value.get(field):
 					transfer, consumed = (transferred_detais.get(field), value.get(field))
-					if field == 'serial_no':
+					if field == "serial_no":
 						transfer, consumed = (sorted(transfer), sorted(consumed))
 
 					self.assertEqual(transfer, consumed)
 
 	def test_po_with_material_transfer(self):
-		'''
-			- Set backflush based on Material Transfer
-			- Create subcontracted PO for the item Subcontracted Item SA1 and Subcontracted Item SA5.
-			- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
-			- Transfer extra item Subcontracted SRM Item 4 for the subcontract item Subcontracted Item SA5.
-			- Create partial purchase receipt against the PO and check serial nos and batch no.
-		'''
+		"""
+		- Set backflush based on Material Transfer
+		- Create subcontracted PO for the item Subcontracted Item SA1 and Subcontracted Item SA5.
+		- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
+		- Transfer extra item Subcontracted SRM Item 4 for the subcontract item Subcontracted Item SA5.
+		- Create partial purchase receipt against the PO and check serial nos and batch no.
+		"""
 
-		set_backflush_based_on('Material Transferred for Subcontract')
-		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': 'Subcontracted Item SA1', 'qty': 5, 'rate': 100},
-			{'warehouse': '_Test Warehouse - _TC', 'item_code': 'Subcontracted Item SA5', 'qty': 6, 'rate': 100}]
+		set_backflush_based_on("Material Transferred for Subcontract")
+		items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Item SA1",
+				"qty": 5,
+				"rate": 100,
+			},
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Item SA5",
+				"qty": 6,
+				"rate": 100,
+			},
+		]
 
-		rm_items = [{'item_code': 'Subcontracted SRM Item 1', 'qty': 5, 'main_item_code': 'Subcontracted Item SA1'},
-			{'item_code': 'Subcontracted SRM Item 2', 'qty': 5, 'main_item_code': 'Subcontracted Item SA1'},
-			{'item_code': 'Subcontracted SRM Item 3', 'qty': 5, 'main_item_code': 'Subcontracted Item SA1'},
-			{'item_code': 'Subcontracted SRM Item 5', 'qty': 6, 'main_item_code': 'Subcontracted Item SA5'},
-			{'item_code': 'Subcontracted SRM Item 4', 'qty': 6, 'main_item_code': 'Subcontracted Item SA5'}
+		rm_items = [
+			{"item_code": "Subcontracted SRM Item 1", "qty": 5, "main_item_code": "Subcontracted Item SA1"},
+			{"item_code": "Subcontracted SRM Item 2", "qty": 5, "main_item_code": "Subcontracted Item SA1"},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 5, "main_item_code": "Subcontracted Item SA1"},
+			{"item_code": "Subcontracted SRM Item 5", "qty": 6, "main_item_code": "Subcontracted Item SA5"},
+			{"item_code": "Subcontracted SRM Item 4", "qty": 6, "main_item_code": "Subcontracted Item SA5"},
 		]
 
 		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
-			supplier_warehouse="_Test Warehouse 1 - _TC")
+		po = create_purchase_order(
+			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
+		)
 
 		for d in rm_items:
-			d['po_detail'] = po.items[0].name if d.get('qty') == 5 else po.items[1].name
+			d["po_detail"] = po.items[0].name if d.get("qty") == 5 else po.items[1].name
 
-		make_stock_transfer_entry(po_no = po.name,
-			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+		make_stock_transfer_entry(
+			po_no=po.name, rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)
+		)
 
 		pr1 = make_purchase_receipt(po.name)
 		pr1.remove(pr1.items[1])
@@ -106,7 +129,7 @@
 		for key, value in get_supplied_items(pr1).items():
 			transferred_detais = itemwise_details.get(key)
 
-			for field in ['qty', 'serial_no', 'batch_no']:
+			for field in ["qty", "serial_no", "batch_no"]:
 				if value.get(field):
 					self.assertEqual(value.get(field), transferred_detais.get(field))
 
@@ -116,36 +139,51 @@
 		for key, value in get_supplied_items(pr2).items():
 			transferred_detais = itemwise_details.get(key)
 
-			for field in ['qty', 'serial_no', 'batch_no']:
+			for field in ["qty", "serial_no", "batch_no"]:
 				if value.get(field):
 					self.assertEqual(value.get(field), transferred_detais.get(field))
 
 	def test_subcontract_with_same_components_different_fg(self):
-		'''
-			- Set backflush based on Material Transfer
-			- Create subcontracted PO for the item Subcontracted Item SA2 and Subcontracted Item SA3.
-			- Transfer the components from Stores to Supplier warehouse with serial nos.
-			- Transfer extra qty of components for the item Subcontracted Item SA2.
-			- Create partial purchase receipt against the PO and check serial nos and batch no.
-		'''
+		"""
+		- Set backflush based on Material Transfer
+		- Create subcontracted PO for the item Subcontracted Item SA2 and Subcontracted Item SA3.
+		- Transfer the components from Stores to Supplier warehouse with serial nos.
+		- Transfer extra qty of components for the item Subcontracted Item SA2.
+		- Create partial purchase receipt against the PO and check serial nos and batch no.
+		"""
 
-		set_backflush_based_on('Material Transferred for Subcontract')
-		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': 'Subcontracted Item SA2', 'qty': 5, 'rate': 100},
-			{'warehouse': '_Test Warehouse - _TC', 'item_code': 'Subcontracted Item SA3', 'qty': 6, 'rate': 100}]
+		set_backflush_based_on("Material Transferred for Subcontract")
+		items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Item SA2",
+				"qty": 5,
+				"rate": 100,
+			},
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Item SA3",
+				"qty": 6,
+				"rate": 100,
+			},
+		]
 
-		rm_items = [{'item_code': 'Subcontracted SRM Item 2', 'qty': 6, 'main_item_code': 'Subcontracted Item SA2'},
-			{'item_code': 'Subcontracted SRM Item 2', 'qty': 6, 'main_item_code': 'Subcontracted Item SA3'}
+		rm_items = [
+			{"item_code": "Subcontracted SRM Item 2", "qty": 6, "main_item_code": "Subcontracted Item SA2"},
+			{"item_code": "Subcontracted SRM Item 2", "qty": 6, "main_item_code": "Subcontracted Item SA3"},
 		]
 
 		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
-			supplier_warehouse="_Test Warehouse 1 - _TC")
+		po = create_purchase_order(
+			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
+		)
 
 		for d in rm_items:
-			d['po_detail'] = po.items[0].name if d.get('qty') == 5 else po.items[1].name
+			d["po_detail"] = po.items[0].name if d.get("qty") == 5 else po.items[1].name
 
-		make_stock_transfer_entry(po_no = po.name,
-			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+		make_stock_transfer_entry(
+			po_no=po.name, rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)
+		)
 
 		pr1 = make_purchase_receipt(po.name)
 		pr1.items[0].qty = 3
@@ -155,7 +193,7 @@
 		for key, value in get_supplied_items(pr1).items():
 			transferred_detais = itemwise_details.get(key)
 			self.assertEqual(value.qty, 4)
-			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get('serial_no')[0:4]))
+			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[0:4]))
 
 		pr2 = make_purchase_receipt(po.name)
 		pr2.items[0].qty = 2
@@ -166,7 +204,7 @@
 			transferred_detais = itemwise_details.get(key)
 
 			self.assertEqual(value.qty, 2)
-			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get('serial_no')[4:6]))
+			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[4:6]))
 
 		pr3 = make_purchase_receipt(po.name)
 		pr3.submit()
@@ -174,85 +212,104 @@
 			transferred_detais = itemwise_details.get(key)
 
 			self.assertEqual(value.qty, 6)
-			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get('serial_no')[6:12]))
+			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[6:12]))
 
 	def test_return_non_consumed_materials(self):
-		'''
-			- Set backflush based on Material Transfer
-			- Create subcontracted PO for the item Subcontracted Item SA2.
-			- Transfer the components from Stores to Supplier warehouse with serial nos.
-			- Transfer extra qty of component for the subcontracted item Subcontracted Item SA2.
-			- Create purchase receipt for full qty against the PO and change the qty of raw material.
-			- After that return the non consumed material back to the store from supplier's warehouse.
-		'''
+		"""
+		- Set backflush based on Material Transfer
+		- Create subcontracted PO for the item Subcontracted Item SA2.
+		- Transfer the components from Stores to Supplier warehouse with serial nos.
+		- Transfer extra qty of component for the subcontracted item Subcontracted Item SA2.
+		- Create purchase receipt for full qty against the PO and change the qty of raw material.
+		- After that return the non consumed material back to the store from supplier's warehouse.
+		"""
 
-		set_backflush_based_on('Material Transferred for Subcontract')
-		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': 'Subcontracted Item SA2', 'qty': 5, 'rate': 100}]
-		rm_items = [{'item_code': 'Subcontracted SRM Item 2', 'qty': 6, 'main_item_code': 'Subcontracted Item SA2'}]
+		set_backflush_based_on("Material Transferred for Subcontract")
+		items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Item SA2",
+				"qty": 5,
+				"rate": 100,
+			}
+		]
+		rm_items = [
+			{"item_code": "Subcontracted SRM Item 2", "qty": 6, "main_item_code": "Subcontracted Item SA2"}
+		]
 
 		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
-			supplier_warehouse="_Test Warehouse 1 - _TC")
+		po = create_purchase_order(
+			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
+		)
 
 		for d in rm_items:
-			d['po_detail'] = po.items[0].name
+			d["po_detail"] = po.items[0].name
 
-		make_stock_transfer_entry(po_no = po.name,
-			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+		make_stock_transfer_entry(
+			po_no=po.name, rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)
+		)
 
 		pr1 = make_purchase_receipt(po.name)
 		pr1.save()
 		pr1.supplied_items[0].consumed_qty = 5
-		pr1.supplied_items[0].serial_no = '\n'.join(sorted(
-			itemwise_details.get('Subcontracted SRM Item 2').get('serial_no')[0:5]
-		))
+		pr1.supplied_items[0].serial_no = "\n".join(
+			sorted(itemwise_details.get("Subcontracted SRM Item 2").get("serial_no")[0:5])
+		)
 		pr1.submit()
 
 		for key, value in get_supplied_items(pr1).items():
 			transferred_detais = itemwise_details.get(key)
 			self.assertEqual(value.qty, 5)
-			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get('serial_no')[0:5]))
+			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[0:5]))
 
 		po.load_from_db()
 		self.assertEqual(po.supplied_items[0].consumed_qty, 5)
 		doc = get_materials_from_supplier(po.name, [d.name for d in po.supplied_items])
 		self.assertEqual(doc.items[0].qty, 1)
-		self.assertEqual(doc.items[0].s_warehouse, '_Test Warehouse 1 - _TC')
-		self.assertEqual(doc.items[0].t_warehouse, '_Test Warehouse - _TC')
-		self.assertEqual(get_serial_nos(doc.items[0].serial_no),
-			itemwise_details.get(doc.items[0].item_code)['serial_no'][5:6])
+		self.assertEqual(doc.items[0].s_warehouse, "_Test Warehouse 1 - _TC")
+		self.assertEqual(doc.items[0].t_warehouse, "_Test Warehouse - _TC")
+		self.assertEqual(
+			get_serial_nos(doc.items[0].serial_no),
+			itemwise_details.get(doc.items[0].item_code)["serial_no"][5:6],
+		)
 
 	def test_item_with_batch_based_on_bom(self):
-		'''
-			- Set backflush based on BOM
-			- Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
-			- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
-			- Transfer the components in multiple batches.
-			- Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
-			- Keep the qty as 2 for Subcontracted Item in the purchase receipt.
-		'''
+		"""
+		- Set backflush based on BOM
+		- Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
+		- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
+		- Transfer the components in multiple batches.
+		- Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
+		- Keep the qty as 2 for Subcontracted Item in the purchase receipt.
+		"""
 
-		set_backflush_based_on('BOM')
-		item_code = 'Subcontracted Item SA4'
-		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}]
+		set_backflush_based_on("BOM")
+		item_code = "Subcontracted Item SA4"
+		items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
 
-		rm_items = [{'item_code': 'Subcontracted SRM Item 1', 'qty': 10},
-			{'item_code': 'Subcontracted SRM Item 2', 'qty': 10},
-			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
-			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
-			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
-			{'item_code': 'Subcontracted SRM Item 3', 'qty': 1}
+		rm_items = [
+			{"item_code": "Subcontracted SRM Item 1", "qty": 10},
+			{"item_code": "Subcontracted SRM Item 2", "qty": 10},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 1},
 		]
 
 		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
-			supplier_warehouse="_Test Warehouse 1 - _TC")
+		po = create_purchase_order(
+			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
+		)
 
 		for d in rm_items:
-			d['po_detail'] = po.items[0].name
+			d["po_detail"] = po.items[0].name
 
-		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
-			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+		make_stock_transfer_entry(
+			po_no=po.name,
+			main_item_code=item_code,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
 
 		pr1 = make_purchase_receipt(po.name)
 		pr1.items[0].qty = 2
@@ -281,37 +338,43 @@
 			self.assertEqual(value.qty, 2)
 
 	def test_item_with_batch_based_on_material_transfer(self):
-		'''
-			- Set backflush based on Material Transferred for Subcontract
-			- Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
-			- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
-			- Transfer the components in multiple batches with extra 2 qty for the batched item.
-			- Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
-			- Keep the qty as 2 for Subcontracted Item in the purchase receipt.
-			- In the first purchase receipt the batched raw materials will be consumed 2 extra qty.
-		'''
+		"""
+		- Set backflush based on Material Transferred for Subcontract
+		- Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
+		- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
+		- Transfer the components in multiple batches with extra 2 qty for the batched item.
+		- Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
+		- Keep the qty as 2 for Subcontracted Item in the purchase receipt.
+		- In the first purchase receipt the batched raw materials will be consumed 2 extra qty.
+		"""
 
-		set_backflush_based_on('Material Transferred for Subcontract')
-		item_code = 'Subcontracted Item SA4'
-		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}]
+		set_backflush_based_on("Material Transferred for Subcontract")
+		item_code = "Subcontracted Item SA4"
+		items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
 
-		rm_items = [{'item_code': 'Subcontracted SRM Item 1', 'qty': 10},
-			{'item_code': 'Subcontracted SRM Item 2', 'qty': 10},
-			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
-			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
-			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
-			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3}
+		rm_items = [
+			{"item_code": "Subcontracted SRM Item 1", "qty": 10},
+			{"item_code": "Subcontracted SRM Item 2", "qty": 10},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
 		]
 
 		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
-			supplier_warehouse="_Test Warehouse 1 - _TC")
+		po = create_purchase_order(
+			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
+		)
 
 		for d in rm_items:
-			d['po_detail'] = po.items[0].name
+			d["po_detail"] = po.items[0].name
 
-		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
-			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+		make_stock_transfer_entry(
+			po_no=po.name,
+			main_item_code=item_code,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
 
 		pr1 = make_purchase_receipt(po.name)
 		pr1.items[0].qty = 2
@@ -320,7 +383,7 @@
 		pr1.submit()
 
 		for key, value in get_supplied_items(pr1).items():
-			qty = 4 if key != 'Subcontracted SRM Item 3' else 6
+			qty = 4 if key != "Subcontracted SRM Item 3" else 6
 			self.assertEqual(value.qty, qty)
 
 		pr1 = make_purchase_receipt(po.name)
@@ -341,30 +404,35 @@
 			self.assertEqual(value.qty, 2)
 
 	def test_partial_transfer_serial_no_components_based_on_material_transfer(self):
-		'''
-			- Set backflush based on Material Transferred for Subcontract
-			- Create subcontracted PO for the item Subcontracted Item SA2.
-			- Transfer the partial components from Stores to Supplier warehouse with serial nos.
-			- Create partial purchase receipt against the PO and change the qty manually.
-			- Transfer the remaining components from Stores to Supplier warehouse with serial nos.
-			- Create purchase receipt for remaining qty against the PO and change the qty manually.
-		'''
+		"""
+		- Set backflush based on Material Transferred for Subcontract
+		- Create subcontracted PO for the item Subcontracted Item SA2.
+		- Transfer the partial components from Stores to Supplier warehouse with serial nos.
+		- Create partial purchase receipt against the PO and change the qty manually.
+		- Transfer the remaining components from Stores to Supplier warehouse with serial nos.
+		- Create purchase receipt for remaining qty against the PO and change the qty manually.
+		"""
 
-		set_backflush_based_on('Material Transferred for Subcontract')
-		item_code = 'Subcontracted Item SA2'
-		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}]
+		set_backflush_based_on("Material Transferred for Subcontract")
+		item_code = "Subcontracted Item SA2"
+		items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
 
-		rm_items = [{'item_code': 'Subcontracted SRM Item 2', 'qty': 5}]
+		rm_items = [{"item_code": "Subcontracted SRM Item 2", "qty": 5}]
 
 		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
-			supplier_warehouse="_Test Warehouse 1 - _TC")
+		po = create_purchase_order(
+			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
+		)
 
 		for d in rm_items:
-			d['po_detail'] = po.items[0].name
+			d["po_detail"] = po.items[0].name
 
-		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
-			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+		make_stock_transfer_entry(
+			po_no=po.name,
+			main_item_code=item_code,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
 
 		pr1 = make_purchase_receipt(po.name)
 		pr1.items[0].qty = 5
@@ -377,7 +445,9 @@
 
 		pr1.load_from_db()
 		pr1.supplied_items[0].consumed_qty = 5
-		pr1.supplied_items[0].serial_no = '\n'.join(itemwise_details[pr1.supplied_items[0].rm_item_code]['serial_no'])
+		pr1.supplied_items[0].serial_no = "\n".join(
+			itemwise_details[pr1.supplied_items[0].rm_item_code]["serial_no"]
+		)
 		pr1.save()
 		pr1.submit()
 
@@ -388,10 +458,14 @@
 
 		itemwise_details = make_stock_in_entry(rm_items=rm_items)
 		for d in rm_items:
-			d['po_detail'] = po.items[0].name
+			d["po_detail"] = po.items[0].name
 
-		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
-			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+		make_stock_transfer_entry(
+			po_no=po.name,
+			main_item_code=item_code,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
 
 		pr1 = make_purchase_receipt(po.name)
 		pr1.submit()
@@ -402,67 +476,77 @@
 			self.assertEqual(sorted(value.serial_no), sorted(details.serial_no))
 
 	def test_incorrect_serial_no_components_based_on_material_transfer(self):
-		'''
-			- Set backflush based on Material Transferred for Subcontract
-			- Create subcontracted PO for the item Subcontracted Item SA2.
-			- Transfer the serialized componenets to the supplier.
-			- Create purchase receipt and change the serial no which is not transferred.
-			- System should throw the error and not allowed to save the purchase receipt.
-		'''
+		"""
+		- Set backflush based on Material Transferred for Subcontract
+		- Create subcontracted PO for the item Subcontracted Item SA2.
+		- Transfer the serialized componenets to the supplier.
+		- Create purchase receipt and change the serial no which is not transferred.
+		- System should throw the error and not allowed to save the purchase receipt.
+		"""
 
-		set_backflush_based_on('Material Transferred for Subcontract')
-		item_code = 'Subcontracted Item SA2'
-		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}]
+		set_backflush_based_on("Material Transferred for Subcontract")
+		item_code = "Subcontracted Item SA2"
+		items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
 
-		rm_items = [{'item_code': 'Subcontracted SRM Item 2', 'qty': 10}]
+		rm_items = [{"item_code": "Subcontracted SRM Item 2", "qty": 10}]
 
 		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
-			supplier_warehouse="_Test Warehouse 1 - _TC")
+		po = create_purchase_order(
+			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
+		)
 
 		for d in rm_items:
-			d['po_detail'] = po.items[0].name
+			d["po_detail"] = po.items[0].name
 
-		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
-			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+		make_stock_transfer_entry(
+			po_no=po.name,
+			main_item_code=item_code,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
 
 		pr1 = make_purchase_receipt(po.name)
 		pr1.save()
-		pr1.supplied_items[0].serial_no = 'ABCD'
+		pr1.supplied_items[0].serial_no = "ABCD"
 		self.assertRaises(frappe.ValidationError, pr1.save)
 		pr1.delete()
 
 	def test_partial_transfer_batch_based_on_material_transfer(self):
-		'''
-			- Set backflush based on Material Transferred for Subcontract
-			- Create subcontracted PO for the item Subcontracted Item SA6.
-			- Transfer the partial components from Stores to Supplier warehouse with batch.
-			- Create partial purchase receipt against the PO and change the qty manually.
-			- Transfer the remaining components from Stores to Supplier warehouse with batch.
-			- Create purchase receipt for remaining qty against the PO and change the qty manually.
-		'''
+		"""
+		- Set backflush based on Material Transferred for Subcontract
+		- Create subcontracted PO for the item Subcontracted Item SA6.
+		- Transfer the partial components from Stores to Supplier warehouse with batch.
+		- Create partial purchase receipt against the PO and change the qty manually.
+		- Transfer the remaining components from Stores to Supplier warehouse with batch.
+		- Create purchase receipt for remaining qty against the PO and change the qty manually.
+		"""
 
-		set_backflush_based_on('Material Transferred for Subcontract')
-		item_code = 'Subcontracted Item SA6'
-		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}]
+		set_backflush_based_on("Material Transferred for Subcontract")
+		item_code = "Subcontracted Item SA6"
+		items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
 
-		rm_items = [{'item_code': 'Subcontracted SRM Item 3', 'qty': 5}]
+		rm_items = [{"item_code": "Subcontracted SRM Item 3", "qty": 5}]
 
 		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
-			supplier_warehouse="_Test Warehouse 1 - _TC")
+		po = create_purchase_order(
+			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
+		)
 
 		for d in rm_items:
-			d['po_detail'] = po.items[0].name
+			d["po_detail"] = po.items[0].name
 
-		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
-			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+		make_stock_transfer_entry(
+			po_no=po.name,
+			main_item_code=item_code,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
 
 		pr1 = make_purchase_receipt(po.name)
 		pr1.items[0].qty = 5
 		pr1.save()
 
-		transferred_batch_no = ''
+		transferred_batch_no = ""
 		for key, value in get_supplied_items(pr1).items():
 			details = itemwise_details.get(key)
 			self.assertEqual(value.qty, 3)
@@ -482,10 +566,14 @@
 
 		itemwise_details = make_stock_in_entry(rm_items=rm_items)
 		for d in rm_items:
-			d['po_detail'] = po.items[0].name
+			d["po_detail"] = po.items[0].name
 
-		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
-			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+		make_stock_transfer_entry(
+			po_no=po.name,
+			main_item_code=item_code,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
 
 		pr1 = make_purchase_receipt(po.name)
 		pr1.submit()
@@ -495,55 +583,60 @@
 			self.assertEqual(value.qty, details.qty)
 			self.assertEqual(value.batch_no, details.batch_no)
 
-
 	def test_item_with_batch_based_on_material_transfer_for_purchase_invoice(self):
-		'''
-			- Set backflush based on Material Transferred for Subcontract
-			- Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
-			- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
-			- Transfer the components in multiple batches with extra 2 qty for the batched item.
-			- Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
-			- Keep the qty as 2 for Subcontracted Item in the purchase receipt.
-			- In the first purchase receipt the batched raw materials will be consumed 2 extra qty.
-		'''
+		"""
+		- Set backflush based on Material Transferred for Subcontract
+		- Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
+		- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
+		- Transfer the components in multiple batches with extra 2 qty for the batched item.
+		- Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
+		- Keep the qty as 2 for Subcontracted Item in the purchase receipt.
+		- In the first purchase receipt the batched raw materials will be consumed 2 extra qty.
+		"""
 
-		set_backflush_based_on('Material Transferred for Subcontract')
-		item_code = 'Subcontracted Item SA4'
-		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}]
+		set_backflush_based_on("Material Transferred for Subcontract")
+		item_code = "Subcontracted Item SA4"
+		items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
 
-		rm_items = [{'item_code': 'Subcontracted SRM Item 1', 'qty': 10},
-			{'item_code': 'Subcontracted SRM Item 2', 'qty': 10},
-			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
-			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
-			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
-			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3}
+		rm_items = [
+			{"item_code": "Subcontracted SRM Item 1", "qty": 10},
+			{"item_code": "Subcontracted SRM Item 2", "qty": 10},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
 		]
 
 		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
-			supplier_warehouse="_Test Warehouse 1 - _TC")
+		po = create_purchase_order(
+			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
+		)
 
 		for d in rm_items:
-			d['po_detail'] = po.items[0].name
+			d["po_detail"] = po.items[0].name
 
-		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
-			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+		make_stock_transfer_entry(
+			po_no=po.name,
+			main_item_code=item_code,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
 
 		pr1 = make_purchase_invoice(po.name)
 		pr1.update_stock = 1
 		pr1.items[0].qty = 2
-		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		pr1.items[0].expense_account = "Stock Adjustment - _TC"
 		add_second_row_in_pr(pr1)
 		pr1.save()
 		pr1.submit()
 
 		for key, value in get_supplied_items(pr1).items():
-			qty = 4 if key != 'Subcontracted SRM Item 3' else 6
+			qty = 4 if key != "Subcontracted SRM Item 3" else 6
 			self.assertEqual(value.qty, qty)
 
 		pr1 = make_purchase_invoice(po.name)
 		pr1.update_stock = 1
-		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		pr1.items[0].expense_account = "Stock Adjustment - _TC"
 		pr1.items[0].qty = 2
 		add_second_row_in_pr(pr1)
 		pr1.save()
@@ -555,43 +648,50 @@
 		pr1 = make_purchase_invoice(po.name)
 		pr1.update_stock = 1
 		pr1.items[0].qty = 2
-		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		pr1.items[0].expense_account = "Stock Adjustment - _TC"
 		pr1.save()
 		pr1.submit()
 
 		for key, value in get_supplied_items(pr1).items():
 			self.assertEqual(value.qty, 2)
 
-	def test_partial_transfer_serial_no_components_based_on_material_transfer_for_purchase_invoice(self):
-		'''
-			- Set backflush based on Material Transferred for Subcontract
-			- Create subcontracted PO for the item Subcontracted Item SA2.
-			- Transfer the partial components from Stores to Supplier warehouse with serial nos.
-			- Create partial purchase receipt against the PO and change the qty manually.
-			- Transfer the remaining components from Stores to Supplier warehouse with serial nos.
-			- Create purchase receipt for remaining qty against the PO and change the qty manually.
-		'''
+	def test_partial_transfer_serial_no_components_based_on_material_transfer_for_purchase_invoice(
+		self,
+	):
+		"""
+		- Set backflush based on Material Transferred for Subcontract
+		- Create subcontracted PO for the item Subcontracted Item SA2.
+		- Transfer the partial components from Stores to Supplier warehouse with serial nos.
+		- Create partial purchase receipt against the PO and change the qty manually.
+		- Transfer the remaining components from Stores to Supplier warehouse with serial nos.
+		- Create purchase receipt for remaining qty against the PO and change the qty manually.
+		"""
 
-		set_backflush_based_on('Material Transferred for Subcontract')
-		item_code = 'Subcontracted Item SA2'
-		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}]
+		set_backflush_based_on("Material Transferred for Subcontract")
+		item_code = "Subcontracted Item SA2"
+		items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
 
-		rm_items = [{'item_code': 'Subcontracted SRM Item 2', 'qty': 5}]
+		rm_items = [{"item_code": "Subcontracted SRM Item 2", "qty": 5}]
 
 		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
-			supplier_warehouse="_Test Warehouse 1 - _TC")
+		po = create_purchase_order(
+			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
+		)
 
 		for d in rm_items:
-			d['po_detail'] = po.items[0].name
+			d["po_detail"] = po.items[0].name
 
-		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
-			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+		make_stock_transfer_entry(
+			po_no=po.name,
+			main_item_code=item_code,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
 
 		pr1 = make_purchase_invoice(po.name)
 		pr1.update_stock = 1
 		pr1.items[0].qty = 5
-		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		pr1.items[0].expense_account = "Stock Adjustment - _TC"
 		pr1.save()
 
 		for key, value in get_supplied_items(pr1).items():
@@ -601,7 +701,9 @@
 
 		pr1.load_from_db()
 		pr1.supplied_items[0].consumed_qty = 5
-		pr1.supplied_items[0].serial_no = '\n'.join(itemwise_details[pr1.supplied_items[0].rm_item_code]['serial_no'])
+		pr1.supplied_items[0].serial_no = "\n".join(
+			itemwise_details[pr1.supplied_items[0].rm_item_code]["serial_no"]
+		)
 		pr1.save()
 		pr1.submit()
 
@@ -612,14 +714,18 @@
 
 		itemwise_details = make_stock_in_entry(rm_items=rm_items)
 		for d in rm_items:
-			d['po_detail'] = po.items[0].name
+			d["po_detail"] = po.items[0].name
 
-		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
-			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+		make_stock_transfer_entry(
+			po_no=po.name,
+			main_item_code=item_code,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
 
 		pr1 = make_purchase_invoice(po.name)
 		pr1.update_stock = 1
-		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		pr1.items[0].expense_account = "Stock Adjustment - _TC"
 		pr1.submit()
 
 		for key, value in get_supplied_items(pr1).items():
@@ -628,38 +734,43 @@
 			self.assertEqual(sorted(value.serial_no), sorted(details.serial_no))
 
 	def test_partial_transfer_batch_based_on_material_transfer_for_purchase_invoice(self):
-		'''
-			- Set backflush based on Material Transferred for Subcontract
-			- Create subcontracted PO for the item Subcontracted Item SA6.
-			- Transfer the partial components from Stores to Supplier warehouse with batch.
-			- Create partial purchase receipt against the PO and change the qty manually.
-			- Transfer the remaining components from Stores to Supplier warehouse with batch.
-			- Create purchase receipt for remaining qty against the PO and change the qty manually.
-		'''
+		"""
+		- Set backflush based on Material Transferred for Subcontract
+		- Create subcontracted PO for the item Subcontracted Item SA6.
+		- Transfer the partial components from Stores to Supplier warehouse with batch.
+		- Create partial purchase receipt against the PO and change the qty manually.
+		- Transfer the remaining components from Stores to Supplier warehouse with batch.
+		- Create purchase receipt for remaining qty against the PO and change the qty manually.
+		"""
 
-		set_backflush_based_on('Material Transferred for Subcontract')
-		item_code = 'Subcontracted Item SA6'
-		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}]
+		set_backflush_based_on("Material Transferred for Subcontract")
+		item_code = "Subcontracted Item SA6"
+		items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
 
-		rm_items = [{'item_code': 'Subcontracted SRM Item 3', 'qty': 5}]
+		rm_items = [{"item_code": "Subcontracted SRM Item 3", "qty": 5}]
 
 		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
-			supplier_warehouse="_Test Warehouse 1 - _TC")
+		po = create_purchase_order(
+			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
+		)
 
 		for d in rm_items:
-			d['po_detail'] = po.items[0].name
+			d["po_detail"] = po.items[0].name
 
-		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
-			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+		make_stock_transfer_entry(
+			po_no=po.name,
+			main_item_code=item_code,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
 
 		pr1 = make_purchase_invoice(po.name)
 		pr1.update_stock = 1
 		pr1.items[0].qty = 5
-		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		pr1.items[0].expense_account = "Stock Adjustment - _TC"
 		pr1.save()
 
-		transferred_batch_no = ''
+		transferred_batch_no = ""
 		for key, value in get_supplied_items(pr1).items():
 			details = itemwise_details.get(key)
 			self.assertEqual(value.qty, 3)
@@ -679,14 +790,18 @@
 
 		itemwise_details = make_stock_in_entry(rm_items=rm_items)
 		for d in rm_items:
-			d['po_detail'] = po.items[0].name
+			d["po_detail"] = po.items[0].name
 
-		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
-			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+		make_stock_transfer_entry(
+			po_no=po.name,
+			main_item_code=item_code,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
 
 		pr1 = make_purchase_invoice(po.name)
 		pr1.update_stock = 1
-		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		pr1.items[0].expense_account = "Stock Adjustment - _TC"
 		pr1.submit()
 
 		for key, value in get_supplied_items(pr1).items():
@@ -695,41 +810,47 @@
 			self.assertEqual(value.batch_no, details.batch_no)
 
 	def test_item_with_batch_based_on_bom_for_purchase_invoice(self):
-		'''
-			- Set backflush based on BOM
-			- Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
-			- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
-			- Transfer the components in multiple batches.
-			- Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
-			- Keep the qty as 2 for Subcontracted Item in the purchase receipt.
-		'''
+		"""
+		- Set backflush based on BOM
+		- Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
+		- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
+		- Transfer the components in multiple batches.
+		- Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
+		- Keep the qty as 2 for Subcontracted Item in the purchase receipt.
+		"""
 
-		set_backflush_based_on('BOM')
-		item_code = 'Subcontracted Item SA4'
-		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}]
+		set_backflush_based_on("BOM")
+		item_code = "Subcontracted Item SA4"
+		items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
 
-		rm_items = [{'item_code': 'Subcontracted SRM Item 1', 'qty': 10},
-			{'item_code': 'Subcontracted SRM Item 2', 'qty': 10},
-			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
-			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
-			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
-			{'item_code': 'Subcontracted SRM Item 3', 'qty': 1}
+		rm_items = [
+			{"item_code": "Subcontracted SRM Item 1", "qty": 10},
+			{"item_code": "Subcontracted SRM Item 2", "qty": 10},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 1},
 		]
 
 		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
-			supplier_warehouse="_Test Warehouse 1 - _TC")
+		po = create_purchase_order(
+			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
+		)
 
 		for d in rm_items:
-			d['po_detail'] = po.items[0].name
+			d["po_detail"] = po.items[0].name
 
-		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
-			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+		make_stock_transfer_entry(
+			po_no=po.name,
+			main_item_code=item_code,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
 
 		pr1 = make_purchase_invoice(po.name)
 		pr1.update_stock = 1
 		pr1.items[0].qty = 2
-		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		pr1.items[0].expense_account = "Stock Adjustment - _TC"
 		add_second_row_in_pr(pr1)
 		pr1.save()
 		pr1.submit()
@@ -740,7 +861,7 @@
 		pr1 = make_purchase_invoice(po.name)
 		pr1.update_stock = 1
 		pr1.items[0].qty = 2
-		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		pr1.items[0].expense_account = "Stock Adjustment - _TC"
 		add_second_row_in_pr(pr1)
 		pr1.save()
 		pr1.submit()
@@ -751,34 +872,99 @@
 		pr1 = make_purchase_invoice(po.name)
 		pr1.update_stock = 1
 		pr1.items[0].qty = 2
-		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		pr1.items[0].expense_account = "Stock Adjustment - _TC"
 		pr1.save()
 		pr1.submit()
 
 		for key, value in get_supplied_items(pr1).items():
 			self.assertEqual(value.qty, 2)
 
+	def test_po_supplied_qty(self):
+		"""
+		Check if 'Supplied Qty' in PO's Supplied Items table is reset on submit/cancel.
+		"""
+		set_backflush_based_on("Material Transferred for Subcontract")
+		items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Item SA1",
+				"qty": 5,
+				"rate": 100,
+			},
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Item SA5",
+				"qty": 6,
+				"rate": 100,
+			},
+		]
+
+		rm_items = [
+			{"item_code": "Subcontracted SRM Item 1", "qty": 5, "main_item_code": "Subcontracted Item SA1"},
+			{"item_code": "Subcontracted SRM Item 2", "qty": 5, "main_item_code": "Subcontracted Item SA1"},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 5, "main_item_code": "Subcontracted Item SA1"},
+			{"item_code": "Subcontracted SRM Item 5", "qty": 6, "main_item_code": "Subcontracted Item SA5"},
+			{"item_code": "Subcontracted SRM Item 4", "qty": 6, "main_item_code": "Subcontracted Item SA5"},
+		]
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		po = create_purchase_order(
+			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
+		)
+
+		for d in rm_items:
+			d["po_detail"] = po.items[0].name if d.get("qty") == 5 else po.items[1].name
+
+		se = make_stock_transfer_entry(
+			po_no=po.name, rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)
+		)
+
+		po.reload()
+		for row in po.get("supplied_items"):
+			self.assertIn(row.supplied_qty, [5.0, 6.0])
+
+		se.cancel()
+		po.reload()
+		for row in po.get("supplied_items"):
+			self.assertEqual(row.supplied_qty, 0.0)
+
+
 def add_second_row_in_pr(pr):
 	item_dict = {}
-	for column in ['item_code', 'item_name', 'qty', 'uom', 'warehouse', 'stock_uom',
-		'purchase_order', 'purchase_order_item', 'conversion_factor', 'rate', 'expense_account', 'po_detail']:
+	for column in [
+		"item_code",
+		"item_name",
+		"qty",
+		"uom",
+		"warehouse",
+		"stock_uom",
+		"purchase_order",
+		"purchase_order_item",
+		"conversion_factor",
+		"rate",
+		"expense_account",
+		"po_detail",
+	]:
 		item_dict[column] = pr.items[0].get(column)
 
-	pr.append('items', item_dict)
+	pr.append("items", item_dict)
 	pr.set_missing_values()
 
+
 def get_supplied_items(pr_doc):
 	supplied_items = {}
-	for row in pr_doc.get('supplied_items'):
+	for row in pr_doc.get("supplied_items"):
 		if row.rm_item_code not in supplied_items:
-			supplied_items.setdefault(row.rm_item_code,
-				frappe._dict({'qty': 0, 'serial_no': [], 'batch_no': defaultdict(float)}))
+			supplied_items.setdefault(
+				row.rm_item_code, frappe._dict({"qty": 0, "serial_no": [], "batch_no": defaultdict(float)})
+			)
 
 		details = supplied_items[row.rm_item_code]
 		update_item_details(row, details)
 
 	return supplied_items
 
+
 def make_stock_in_entry(**args):
 	args = frappe._dict(args)
 
@@ -786,11 +972,17 @@
 	for row in args.rm_items:
 		row = frappe._dict(row)
 
-		doc = make_stock_entry(target=row.warehouse or '_Test Warehouse - _TC',
-			item_code=row.item_code, qty=row.qty or 1, basic_rate=row.rate or 100)
+		doc = make_stock_entry(
+			target=row.warehouse or "_Test Warehouse - _TC",
+			item_code=row.item_code,
+			qty=row.qty or 1,
+			basic_rate=row.rate or 100,
+		)
 
 		if row.item_code not in items:
-			items.setdefault(row.item_code, frappe._dict({'qty': 0, 'serial_no': [], 'batch_no': defaultdict(float)}))
+			items.setdefault(
+				row.item_code, frappe._dict({"qty": 0, "serial_no": [], "batch_no": defaultdict(float)})
+			)
 
 		child_row = doc.items[0]
 		details = items[child_row.item_code]
@@ -798,15 +990,20 @@
 
 	return items
 
+
 def update_item_details(child_row, details):
-	details.qty += (child_row.get('qty') if child_row.doctype == 'Stock Entry Detail'
-		else child_row.get('consumed_qty'))
+	details.qty += (
+		child_row.get("qty")
+		if child_row.doctype == "Stock Entry Detail"
+		else child_row.get("consumed_qty")
+	)
 
 	if child_row.serial_no:
 		details.serial_no.extend(get_serial_nos(child_row.serial_no))
 
 	if child_row.batch_no:
-		details.batch_no[child_row.batch_no] += (child_row.get('qty') or child_row.get('consumed_qty'))
+		details.batch_no[child_row.batch_no] += child_row.get("qty") or child_row.get("consumed_qty")
+
 
 def make_stock_transfer_entry(**args):
 	args = frappe._dict(args)
@@ -815,21 +1012,27 @@
 	for row in args.rm_items:
 		row = frappe._dict(row)
 
-		item = {'item_code': row.main_item_code or args.main_item_code, 'rm_item_code': row.item_code,
-			'qty': row.qty or 1, 'item_name': row.item_code, 'rate': row.rate or 100,
-			'stock_uom': row.stock_uom or 'Nos', 'warehouse': row.warehuose or '_Test Warehouse - _TC'}
+		item = {
+			"item_code": row.main_item_code or args.main_item_code,
+			"rm_item_code": row.item_code,
+			"qty": row.qty or 1,
+			"item_name": row.item_code,
+			"rate": row.rate or 100,
+			"stock_uom": row.stock_uom or "Nos",
+			"warehouse": row.warehuose or "_Test Warehouse - _TC",
+		}
 
 		item_details = args.itemwise_details.get(row.item_code)
 
 		if item_details and item_details.serial_no:
-			serial_nos = item_details.serial_no[0:cint(row.qty)]
-			item['serial_no'] = '\n'.join(serial_nos)
+			serial_nos = item_details.serial_no[0 : cint(row.qty)]
+			item["serial_no"] = "\n".join(serial_nos)
 			item_details.serial_no = list(set(item_details.serial_no) - set(serial_nos))
 
 		if item_details and item_details.batch_no:
 			for batch_no, batch_qty in item_details.batch_no.items():
 				if batch_qty >= row.qty:
-					item['batch_no'] = batch_no
+					item["batch_no"] = batch_no
 					item_details.batch_no[batch_no] -= row.qty
 					break
 
@@ -842,42 +1045,70 @@
 
 	return doc
 
+
 def make_subcontract_items():
-	sub_contracted_items = {'Subcontracted Item SA1': {}, 'Subcontracted Item SA2': {}, 'Subcontracted Item SA3': {},
-		'Subcontracted Item SA4': {'has_batch_no': 1, 'create_new_batch': 1, 'batch_number_series': 'SBAT.####'},
-		'Subcontracted Item SA5': {}, 'Subcontracted Item SA6': {}}
+	sub_contracted_items = {
+		"Subcontracted Item SA1": {},
+		"Subcontracted Item SA2": {},
+		"Subcontracted Item SA3": {},
+		"Subcontracted Item SA4": {
+			"has_batch_no": 1,
+			"create_new_batch": 1,
+			"batch_number_series": "SBAT.####",
+		},
+		"Subcontracted Item SA5": {},
+		"Subcontracted Item SA6": {},
+	}
 
 	for item, properties in sub_contracted_items.items():
-		if not frappe.db.exists('Item', item):
-			properties.update({'is_stock_item': 1, 'is_sub_contracted_item': 1})
+		if not frappe.db.exists("Item", item):
+			properties.update({"is_stock_item": 1, "is_sub_contracted_item": 1})
 			make_item(item, properties)
 
+
 def make_raw_materials():
-	raw_materials = {'Subcontracted SRM Item 1': {},
-		'Subcontracted SRM Item 2': {'has_serial_no': 1, 'serial_no_series': 'SRI.####'},
-		'Subcontracted SRM Item 3': {'has_batch_no': 1, 'create_new_batch': 1, 'batch_number_series': 'BAT.####'},
-		'Subcontracted SRM Item 4': {'has_serial_no': 1, 'serial_no_series': 'SRII.####'},
-		'Subcontracted SRM Item 5': {'has_serial_no': 1, 'serial_no_series': 'SRII.####'}}
+	raw_materials = {
+		"Subcontracted SRM Item 1": {},
+		"Subcontracted SRM Item 2": {"has_serial_no": 1, "serial_no_series": "SRI.####"},
+		"Subcontracted SRM Item 3": {
+			"has_batch_no": 1,
+			"create_new_batch": 1,
+			"batch_number_series": "BAT.####",
+		},
+		"Subcontracted SRM Item 4": {"has_serial_no": 1, "serial_no_series": "SRII.####"},
+		"Subcontracted SRM Item 5": {"has_serial_no": 1, "serial_no_series": "SRII.####"},
+	}
 
 	for item, properties in raw_materials.items():
-		if not frappe.db.exists('Item', item):
-			properties.update({'is_stock_item': 1})
+		if not frappe.db.exists("Item", item):
+			properties.update({"is_stock_item": 1})
 			make_item(item, properties)
 
+
 def make_bom_for_subcontracted_items():
 	boms = {
-		'Subcontracted Item SA1': ['Subcontracted SRM Item 1', 'Subcontracted SRM Item 2', 'Subcontracted SRM Item 3'],
-		'Subcontracted Item SA2': ['Subcontracted SRM Item 2'],
-		'Subcontracted Item SA3': ['Subcontracted SRM Item 2'],
-		'Subcontracted Item SA4': ['Subcontracted SRM Item 1', 'Subcontracted SRM Item 2', 'Subcontracted SRM Item 3'],
-		'Subcontracted Item SA5': ['Subcontracted SRM Item 5'],
-		'Subcontracted Item SA6': ['Subcontracted SRM Item 3']
+		"Subcontracted Item SA1": [
+			"Subcontracted SRM Item 1",
+			"Subcontracted SRM Item 2",
+			"Subcontracted SRM Item 3",
+		],
+		"Subcontracted Item SA2": ["Subcontracted SRM Item 2"],
+		"Subcontracted Item SA3": ["Subcontracted SRM Item 2"],
+		"Subcontracted Item SA4": [
+			"Subcontracted SRM Item 1",
+			"Subcontracted SRM Item 2",
+			"Subcontracted SRM Item 3",
+		],
+		"Subcontracted Item SA5": ["Subcontracted SRM Item 5"],
+		"Subcontracted Item SA6": ["Subcontracted SRM Item 3"],
 	}
 
 	for item_code, raw_materials in boms.items():
-		if not frappe.db.exists('BOM', {'item': item_code}):
+		if not frappe.db.exists("BOM", {"item": item_code}):
 			make_bom(item=item_code, raw_materials=raw_materials, rate=100)
 
+
 def set_backflush_based_on(based_on):
-	frappe.db.set_value('Buying Settings', None,
-		'backflush_raw_materials_of_subcontract_based_on', based_on)
+	frappe.db.set_value(
+		"Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", based_on
+	)
diff --git a/erpnext/tests/test_webform.py b/erpnext/tests/test_webform.py
index 19255db..202467b 100644
--- a/erpnext/tests/test_webform.py
+++ b/erpnext/tests/test_webform.py
@@ -7,132 +7,143 @@
 
 class TestWebsite(unittest.TestCase):
 	def test_permission_for_custom_doctype(self):
-		create_user('Supplier 1', 'supplier1@gmail.com')
-		create_user('Supplier 2', 'supplier2@gmail.com')
-		create_supplier_with_contact('Supplier1', 'All Supplier Groups', 'Supplier 1', 'supplier1@gmail.com')
-		create_supplier_with_contact('Supplier2', 'All Supplier Groups', 'Supplier 2', 'supplier2@gmail.com')
-		po1 = create_purchase_order(supplier='Supplier1')
-		po2 = create_purchase_order(supplier='Supplier2')
+		create_user("Supplier 1", "supplier1@gmail.com")
+		create_user("Supplier 2", "supplier2@gmail.com")
+		create_supplier_with_contact(
+			"Supplier1", "All Supplier Groups", "Supplier 1", "supplier1@gmail.com"
+		)
+		create_supplier_with_contact(
+			"Supplier2", "All Supplier Groups", "Supplier 2", "supplier2@gmail.com"
+		)
+		po1 = create_purchase_order(supplier="Supplier1")
+		po2 = create_purchase_order(supplier="Supplier2")
 
 		create_custom_doctype()
 		create_webform()
-		create_order_assignment(supplier='Supplier1', po = po1.name)
-		create_order_assignment(supplier='Supplier2', po = po2.name)
+		create_order_assignment(supplier="Supplier1", po=po1.name)
+		create_order_assignment(supplier="Supplier2", po=po2.name)
 
 		frappe.set_user("Administrator")
 		# checking if data consist of all order assignment of Supplier1 and Supplier2
-		self.assertTrue('Supplier1' and 'Supplier2' in [data.supplier for data in get_data()])
+		self.assertTrue("Supplier1" and "Supplier2" in [data.supplier for data in get_data()])
 
 		frappe.set_user("supplier1@gmail.com")
 		# checking if data only consist of order assignment of Supplier1
-		self.assertTrue('Supplier1' in [data.supplier for data in get_data()])
-		self.assertFalse([data.supplier for data in get_data() if data.supplier != 'Supplier1'])
+		self.assertTrue("Supplier1" in [data.supplier for data in get_data()])
+		self.assertFalse([data.supplier for data in get_data() if data.supplier != "Supplier1"])
 
 		frappe.set_user("supplier2@gmail.com")
 		# checking if data only consist of order assignment of Supplier2
-		self.assertTrue('Supplier2' in [data.supplier for data in get_data()])
-		self.assertFalse([data.supplier for data in get_data() if data.supplier != 'Supplier2'])
+		self.assertTrue("Supplier2" in [data.supplier for data in get_data()])
+		self.assertFalse([data.supplier for data in get_data() if data.supplier != "Supplier2"])
 
 		frappe.set_user("Administrator")
 
+
 def get_data():
-	webform_list_contexts = frappe.get_hooks('webform_list_context')
+	webform_list_contexts = frappe.get_hooks("webform_list_context")
 	if webform_list_contexts:
-		context = frappe._dict(frappe.get_attr(webform_list_contexts[0])('Buying') or {})
-	kwargs = dict(doctype='Order Assignment', order_by = 'modified desc')
+		context = frappe._dict(frappe.get_attr(webform_list_contexts[0])("Buying") or {})
+	kwargs = dict(doctype="Order Assignment", order_by="modified desc")
 	return context.get_list(**kwargs)
 
+
 def create_user(name, email):
-	frappe.get_doc({
-		'doctype': 'User',
-		'send_welcome_email': 0,
-		'user_type': 'Website User',
-		'first_name': name,
-		'email': email,
-		'roles': [{"doctype": "Has Role", "role": "Supplier"}]
-	}).insert(ignore_if_duplicate = True)
+	frappe.get_doc(
+		{
+			"doctype": "User",
+			"send_welcome_email": 0,
+			"user_type": "Website User",
+			"first_name": name,
+			"email": email,
+			"roles": [{"doctype": "Has Role", "role": "Supplier"}],
+		}
+	).insert(ignore_if_duplicate=True)
+
 
 def create_supplier_with_contact(name, group, contact_name, contact_email):
-	supplier = frappe.get_doc({
-		'doctype': 'Supplier',
-		'supplier_name': name,
-		'supplier_group': group
-	}).insert(ignore_if_duplicate = True)
+	supplier = frappe.get_doc(
+		{"doctype": "Supplier", "supplier_name": name, "supplier_group": group}
+	).insert(ignore_if_duplicate=True)
 
-	if not frappe.db.exists('Contact', contact_name+'-1-'+name):
+	if not frappe.db.exists("Contact", contact_name + "-1-" + name):
 		new_contact = frappe.new_doc("Contact")
 		new_contact.first_name = contact_name
-		new_contact.is_primary_contact = True,
-		new_contact.append('links', {
-			"link_doctype": "Supplier",
-			"link_name": supplier.name
-		})
-		new_contact.append('email_ids', {
-			"email_id": contact_email,
-			"is_primary": 1
-		})
+		new_contact.is_primary_contact = (True,)
+		new_contact.append("links", {"link_doctype": "Supplier", "link_name": supplier.name})
+		new_contact.append("email_ids", {"email_id": contact_email, "is_primary": 1})
 
 		new_contact.insert(ignore_mandatory=True)
 
+
 def create_custom_doctype():
-	frappe.get_doc({
-		'doctype': 'DocType',
-		'name': 'Order Assignment',
-		'module': 'Buying',
-		'custom': 1,
-		'autoname': 'field:po',
-		'fields': [
-			{'label': 'PO', 'fieldname': 'po', 'fieldtype': 'Link', 'options': 'Purchase Order'},
-			{'label': 'Supplier', 'fieldname': 'supplier', 'fieldtype': 'Data', "fetch_from": "po.supplier"}
-		],
-		'permissions': [
-			{
-				"create": 1,
-				"delete": 1,
-				"email": 1,
-				"export": 1,
-				"print": 1,
-				"read": 1,
-				"report": 1,
-				"role": "System Manager",
-				"share": 1,
-				"write": 1
-			},
-			{
-				"read": 1,
-				"role": "Supplier"
-			}
-		]
-	}).insert(ignore_if_duplicate = True)
+	frappe.get_doc(
+		{
+			"doctype": "DocType",
+			"name": "Order Assignment",
+			"module": "Buying",
+			"custom": 1,
+			"autoname": "field:po",
+			"fields": [
+				{"label": "PO", "fieldname": "po", "fieldtype": "Link", "options": "Purchase Order"},
+				{
+					"label": "Supplier",
+					"fieldname": "supplier",
+					"fieldtype": "Data",
+					"fetch_from": "po.supplier",
+				},
+			],
+			"permissions": [
+				{
+					"create": 1,
+					"delete": 1,
+					"email": 1,
+					"export": 1,
+					"print": 1,
+					"read": 1,
+					"report": 1,
+					"role": "System Manager",
+					"share": 1,
+					"write": 1,
+				},
+				{"read": 1, "role": "Supplier"},
+			],
+		}
+	).insert(ignore_if_duplicate=True)
+
 
 def create_webform():
-	frappe.get_doc({
-		'doctype': 'Web Form',
-		'module': 'Buying',
-		'title': 'SO Schedule',
-		'route': 'so-schedule',
-		'doc_type': 'Order Assignment',
-		'web_form_fields': [
-			{
-				'doctype': 'Web Form Field',
-				'fieldname': 'po',
-				'fieldtype': 'Link',
-				'options': 'Purchase Order',
-				'label': 'PO'
-			},
-			{
-				'doctype': 'Web Form Field',
-				'fieldname': 'supplier',
-				'fieldtype': 'Data',
-				'label': 'Supplier'
-			}
-		]
+	frappe.get_doc(
+		{
+			"doctype": "Web Form",
+			"module": "Buying",
+			"title": "SO Schedule",
+			"route": "so-schedule",
+			"doc_type": "Order Assignment",
+			"web_form_fields": [
+				{
+					"doctype": "Web Form Field",
+					"fieldname": "po",
+					"fieldtype": "Link",
+					"options": "Purchase Order",
+					"label": "PO",
+				},
+				{
+					"doctype": "Web Form Field",
+					"fieldname": "supplier",
+					"fieldtype": "Data",
+					"label": "Supplier",
+				},
+			],
+		}
+	).insert(ignore_if_duplicate=True)
 
-	}).insert(ignore_if_duplicate = True)
 
 def create_order_assignment(supplier, po):
-	frappe.get_doc({
-		'doctype': 'Order Assignment',
-		'po': po,
-		'supplier': supplier,
-	}).insert(ignore_if_duplicate = True)
\ No newline at end of file
+	frappe.get_doc(
+		{
+			"doctype": "Order Assignment",
+			"po": po,
+			"supplier": supplier,
+		}
+	).insert(ignore_if_duplicate=True)
diff --git a/erpnext/tests/test_woocommerce.py b/erpnext/tests/test_woocommerce.py
deleted file mode 100644
index 4a451ab..0000000
--- a/erpnext/tests/test_woocommerce.py
+++ /dev/null
@@ -1,58 +0,0 @@
-import os
-import time
-import unittest
-
-import frappe
-import requests
-
-from erpnext.erpnext_integrations.connectors.woocommerce_connection import order
-
-
-class TestWoocommerce(unittest.TestCase):
-	def setUp(self):
-
-		woo_settings = frappe.get_doc("Woocommerce Settings")
-		if not woo_settings.secret:
-			woo_settings.secret = "ec434676aa1de0e502389f515c38f89f653119ab35e9117c7a79e576"
-			woo_settings.woocommerce_server_url = "https://woocommerce.mntechnique.com/"
-			woo_settings.api_consumer_key = "ck_fd43ff5756a6abafd95fadb6677100ce95a758a1"
-			woo_settings.api_consumer_secret = "cs_94360a1ad7bef7fa420a40cf284f7b3e0788454e"
-			woo_settings.enable_sync = 1
-			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":"_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"}))
-		self.assertTrue(frappe.get_value("Item",{"woocommerce_id": 56}))
-		self.assertTrue(frappe.get_value("Sales Order",{"woocommerce_id":75}))
-		frappe.flags.woocomm_test_order_data = {}
-
-def emulate_request():
-	# Emulate Woocommerce Request
-	headers = {
-		"X-Wc-Webhook-Event":"created",
-		"X-Wc-Webhook-Signature":"h1SjzQMPwd68MF5bficeFq20/RkQeRLsb9AVCUz/rLs="
-	}
-	# Emulate Request Data
-	data = """{"id":74,"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":null,"date_paid_gmt":null,"date_completed":null,"date_completed_gmt":null,"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":[]}"""
-
-	# Build URL
-	port = frappe.get_site_config().webserver_port or '8000'
-
-	if os.environ.get('CI'):
-		host = 'localhost'
-	else:
-		host = frappe.local.site
-
-	url = "http://{site}:{port}/api/method/erpnext.erpnext_integrations.connectors.woocommerce_connection.order".format(site=host, port=port)
-
-	r = requests.post(url=url, headers=headers, data=data)
-
-	time.sleep(5)
-	return r
diff --git a/erpnext/tests/test_zform_loads.py b/erpnext/tests/test_zform_loads.py
index b6fb636..26e60c0 100644
--- a/erpnext/tests/test_zform_loads.py
+++ b/erpnext/tests/test_zform_loads.py
@@ -1,30 +1,58 @@
-""" dumb test to check all function calls on known form loads """
-
-import unittest
+""" smoak tests to check basic functionality calls on known form loads."""
 
 import frappe
 from frappe.desk.form.load import getdoc
+from frappe.tests.utils import FrappeTestCase, change_settings
+from frappe.www.printview import get_html_and_style
 
 
-class TestFormLoads(unittest.TestCase):
-
+class TestFormLoads(FrappeTestCase):
+	@change_settings("Print Settings", {"allow_print_for_cancelled": 1})
 	def test_load(self):
 		erpnext_modules = frappe.get_all("Module Def", filters={"app_name": "erpnext"}, pluck="name")
-		doctypes = frappe.get_all("DocType", {"istable": 0, "issingle": 0, "is_virtual": 0, "module": ("in", erpnext_modules)}, pluck="name")
+		doctypes = frappe.get_all(
+			"DocType",
+			{"istable": 0, "issingle": 0, "is_virtual": 0, "module": ("in", erpnext_modules)},
+			pluck="name",
+		)
 
 		for doctype in doctypes:
 			last_doc = frappe.db.get_value(doctype, {}, "name", order_by="modified desc")
 			if not last_doc:
 				continue
 			with self.subTest(msg=f"Loading {doctype} - {last_doc}", doctype=doctype, last_doc=last_doc):
-				try:
-					# reset previous response
-					frappe.response = frappe._dict({"docs":[]})
-					frappe.response.docinfo = None
+				self.assertFormLoad(doctype, last_doc)
+				self.assertDocPrint(doctype, last_doc)
 
-					getdoc(doctype, last_doc)
-				except Exception as e:
-					self.fail(f"Failed to load {doctype} - {last_doc}: {e}")
+	def assertFormLoad(self, doctype, docname):
+		# reset previous response
+		frappe.response = frappe._dict({"docs": []})
+		frappe.response.docinfo = None
 
-				self.assertTrue(frappe.response.docs, msg=f"expected document in reponse, found: {frappe.response.docs}")
-				self.assertTrue(frappe.response.docinfo, msg=f"expected docinfo in reponse, found: {frappe.response.docinfo}")
+		try:
+			getdoc(doctype, docname)
+		except Exception as e:
+			self.fail(f"Failed to load {doctype}-{docname}: {e}")
+
+		self.assertTrue(
+			frappe.response.docs, msg=f"expected document in reponse, found: {frappe.response.docs}"
+		)
+		self.assertTrue(
+			frappe.response.docinfo, msg=f"expected docinfo in reponse, found: {frappe.response.docinfo}"
+		)
+
+	def assertDocPrint(self, doctype, docname):
+		doc = frappe.get_doc(doctype, docname)
+		doc.set("__onload", frappe._dict())
+		doc.run_method("onload")
+
+		messages_before = frappe.get_message_log()
+		ret = get_html_and_style(doc=doc.as_json(), print_format="Standard", no_letterhead=1)
+		messages_after = frappe.get_message_log()
+
+		if len(messages_after) > len(messages_before):
+			new_messages = messages_after[len(messages_before) :]
+			self.fail("Print view showing error/warnings: \n" + "\n".join(str(msg) for msg in new_messages))
+
+		# html should exist
+		self.assertTrue(bool(ret["html"]))
diff --git a/erpnext/tests/ui/agriculture.txt b/erpnext/tests/ui/agriculture.txt
deleted file mode 100644
index 4622bc0..0000000
--- a/erpnext/tests/ui/agriculture.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-erpnext/agriculture/doctype/land_unit/test_land_unit.js
-erpnext/agriculture/doctype/fertilizer/test_fertilizer.js
-erpnext/agriculture/doctype/water_analysis/test_water_analysis.js
-erpnext/agriculture/doctype/disease/test_disease.js
-erpnext/agriculture/doctype/soil_texture/test_soil_texture.js
-erpnext/agriculture/doctype/crop/test_crop.js
-erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.js
\ No newline at end of file
diff --git a/erpnext/tests/ui/make_fixtures.js b/erpnext/tests/ui/make_fixtures.js
deleted file mode 100644
index 8c9e508..0000000
--- a/erpnext/tests/ui/make_fixtures.js
+++ /dev/null
@@ -1,258 +0,0 @@
-$.extend(frappe.test_data, {
-	// "Fiscal Year": {
-	// 	"2017-18": [
-	// 		{"year": "2017-18"},
-	// 		{"year_start_date": "2017-04-01"},
-	// 		{"year_end_date": "2018-03-31"},
-	// 	]
-	// },
-	"Customer": {
-		"Test Customer 1": [
-			{customer_name: "Test Customer 1"}
-		],
-		"Test Customer 2": [
-			{customer_name: "Test Customer 2"}
-		],
-		"Test Customer 3": [
-			{customer_name: "Test Customer 3"}
-		],
-	},
-	"Item": {
-		"Test Product 1": [
-			{item_code: "Test Product 1"},
-			{item_group: "Products"},
-			{is_stock_item: 1},
-			{standard_rate: 100},
-			{opening_stock: 100},
-		],
-		"Test Product 2": [
-			{item_code: "Test Product 2"},
-			{item_group: "Products"},
-			{is_stock_item: 1},
-			{standard_rate: 150},
-			{opening_stock: 200},
-		],
-		"Test Product 3": [
-			{item_code: "Test Product 3"},
-			{item_group: "Products"},
-			{is_stock_item: 1},
-			{standard_rate: 250},
-			{opening_stock: 100},
-			{stock_uom:'Kg'}
-		],
-		"Test Service 1": [
-			{item_code: "Test Service 1"},
-			{item_group: "Services"},
-			{is_stock_item: 0},
-			{standard_rate: 200}
-		],
-		"Test Service 2": [
-			{item_code: "Test Service 2"},
-			{item_group: "Services"},
-			{is_stock_item: 0},
-			{standard_rate: 300}
-		]
-	},
-	"Lead": {
-		"LEAD-00001": [
-			{lead_name: "Test Lead 1"}
-		],
-		"LEAD-00002": [
-			{lead_name: "Test Lead 2"}
-		],
-		"LEAD-00003": [
-			{lead_name: "Test Lead 3"}
-		]
-	},
-	"Address": {
-		"Test1-Billing": [
-			{address_title:"Test1"},
-			{address_type: "Billing"},
-			{address_line1: "Billing Street 1"},
-			{city: "Billing City 1"},
-			{links: [
-				[
-					{link_doctype: "Customer"},
-					{link_name: "Test Customer 1"}
-				]
-			]}
-		],
-		"Test1-Shipping": [
-			{address_title:"Test1"},
-			{address_type: "Shipping"},
-			{address_line1: "Shipping Street 1"},
-			{city: "Shipping City 1"},
-			{links: [
-				[
-					{link_doctype: "Customer"},
-					{link_name: "Test Customer 1"}
-				]
-			]}
-		],
-		"Test1-Warehouse": [
-			{address_title:"Test1"},
-			{address_type: "Warehouse"},
-			{address_line1: "Warehouse Street 1"},
-			{city: "Warehouse City 1"},
-			{links: [
-				[
-					{link_doctype: "Customer"},
-					{link_name: "Test Customer 1"}
-				]
-			]}
-		],
-		"Test2-Billing": [
-			{address_title:"Test2"},
-			{address_type: "Billing"},
-			{address_line1: "Billing Street 2"},
-			{city: "Billing City 2"},
-			{links: [
-				[
-					{link_doctype: "Customer"},
-					{link_name: "Test Customer 2"}
-				]
-			]}
-		],
-		"Test2-Shipping": [
-			{address_title:"Test2"},
-			{address_type: "Shipping"},
-			{address_line1: "Shipping Street 2"},
-			{city: "Shipping City 2"},
-			{links: [
-				[
-					{link_doctype: "Customer"},
-					{link_name: "Test Customer 2"}
-				]
-			]}
-		],
-		"Test2-Warehouse": [
-			{address_title:"Test2"},
-			{address_type: "Warehouse"},
-			{address_line1: "Warehouse Street 2"},
-			{city: "Warehouse City 2"},
-			{links: [
-				[
-					{link_doctype: "Customer"},
-					{link_name: "Test Customer 2"}
-				]
-			]}
-		]
-	},
-	"Contact": {
-		"Contact 1-Test Customer 1": [
-			{first_name: "Contact 1"},
-			{links: [
-				[
-					{link_doctype: "Customer"},
-					{link_name: "Test Customer 1"}
-				]
-			]}
-		],
-		"Contact 2-Test Customer 1": [
-			{first_name: "Contact 2"},
-			{links: [
-				[
-					{link_doctype: "Customer"},
-					{link_name: "Test Customer 1"}
-				]
-			]}
-		],
-		"Contact 1-Test Customer 2": [
-			{first_name: "Contact 1"},
-			{links: [
-				[
-					{link_doctype: "Customer"},
-					{link_name: "Test Customer 2"}
-				]
-			]}
-		],
-		"Contact 2-Test Customer 2": [
-			{first_name: "Contact 2"},
-			{links: [
-				[
-					{link_doctype: "Customer"},
-					{link_name: "Test Customer 2"}
-				]
-			]}
-		],
-	},
-	"Price List": {
-		"Test-Buying-USD": [
-			{price_list_name: "Test-Buying-USD"},
-			{currency: "USD"},
-			{buying: "1"}
-		],
-		"Test-Buying-EUR": [
-			{price_list_name: "Test-Buying-EUR"},
-			{currency: "EUR"},
-			{buying: "1"}
-		],
-		"Test-Selling-USD": [
-			{price_list_name: "Test-Selling-USD"},
-			{currency: "USD"},
-			{selling: "1"}
-		],
-		"Test-Selling-EUR": [
-			{price_list_name: "Test-Selling-EUR"},
-			{currency: "EUR"},
-			{selling: "1"}
-		],
-	},
-	"Terms and Conditions": {
-		"Test Term 1": [
-			{title: "Test Term 1"}
-		],
-		"Test Term 2": [
-			{title: "Test Term 2"}
-		]
-	},
-	"Item Price": {
-		"ITEM-PRICE-00001": [
-			{item_code: 'Test Product 1'},
-			{price_list: '_Test Price List'},
-			{price_list_rate: 100}
-		],
-		"ITEM-PRICE-00002": [
-			{item_code: 'Test Product 2'},
-			{price_list: '_Test Price List'},
-			{price_list_rate: 200}
-		]
-	},
-	"Payment Term": {
-		"_Test Payment Term": [
-			{payment_term_name: '_Test Payment Term'},
-			{due_date_based_on: 'Day(s) after invoice date'},
-			{invoice_portion: 100},
-			{credit_days: 0}
-		]
-	},
-	"Payment Terms Template": {
-		"_Test Payment Term Template UI": [
-			{template_name: "_Test Payment Term Template UI"},
-			{terms: [
-				[
-					{payment_term: '_Test Payment Term'},
-					{invoice_portion: 100}
-				]
-			]}
-		]
-	}
-});
-
-
-// this is a script that creates all fixtures
-// called as a test
-QUnit.module('fixture');
-
-QUnit.test('Make fixtures', assert => {
-	// create all fixtures first
-	assert.expect(0);
-	let done = assert.async();
-	let tasks = [];
-	Object.keys(frappe.test_data).forEach(function(doctype) {
-		tasks.push(function() {
-			return frappe.tests.setup_doctype(doctype, frappe.test_data[doctype]);
-		});
-	});
-	frappe.run_serially(tasks).then(() => done());
-});
diff --git a/erpnext/tests/ui/setup_wizard.js b/erpnext/tests/ui/setup_wizard.js
deleted file mode 100644
index ccff785..0000000
--- a/erpnext/tests/ui/setup_wizard.js
+++ /dev/null
@@ -1,47 +0,0 @@
-const path = require('path');
-const path_join = path.resolve;
-const apps_path = path_join(__dirname, '..', '..', '..', '..');
-const frappe_ui_tests_path = path_join(apps_path, 'frappe', 'frappe', 'tests', 'ui');
-
-const login = require(frappe_ui_tests_path + "/login.js")['Login'];
-const welcome = require(frappe_ui_tests_path + "/setup_wizard.js")['Welcome'];
-const region = require(frappe_ui_tests_path + "/setup_wizard.js")['Region'];
-const user = require(frappe_ui_tests_path + "/setup_wizard.js")['User'];
-
-module.exports = {
-	before: browser => {
-		browser
-			.url(browser.launch_url + '/login')
-			.waitForElementVisible('body', 5000);
-	},
-	'Login': login,
-	'Welcome': welcome,
-	'Region': region,
-	'User': user,
-	'Domain': browser => {
-		let slide_selector = '[data-slide-name="domain"]';
-		browser
-			.waitForElementVisible(slide_selector, 2000)
-			.setValue('select[data-fieldname="domain"]', "Manufacturing")
-			.click(slide_selector + ' .next-btn');
-	},
-	'Brand': browser => {
-		let slide_selector = '[data-slide-name="brand"]';
-		browser
-			.waitForElementVisible(slide_selector, 2000)
-			.setValue('input[data-fieldname="company_name"]', "Acme")
-			.click(slide_selector + " .next-btn");
-	},
-	'Organisation': browser => {
-		let slide_selector = '[data-slide-name="organisation"]';
-		browser
-			.waitForElementVisible(slide_selector, 2000)
-			.setValue('input[data-fieldname="company_tagline"]', "Build tools for Builders")
-			.setValue('input[data-fieldname="bank_account"]', "YNG")
-			.click(slide_selector + " .next-btn");
-	},
-
-	after: browser => {
-		browser.end();
-	},
-};
diff --git a/erpnext/tests/ui/tests.txt b/erpnext/tests/ui/tests.txt
deleted file mode 100644
index 5e238e4..0000000
--- a/erpnext/tests/ui/tests.txt
+++ /dev/null
@@ -1,110 +0,0 @@
-erpnext/tests/ui/make_fixtures.js #long
-erpnext/accounts/doctype/account/tests/test_account.js
-erpnext/accounts/doctype/account/tests/test_make_tax_account.js
-erpnext/accounts/doctype/account/tests/test_account_with_number.js
-erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule.js
-erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.js
-erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.js
-erpnext/accounts/doctype/shipping_rule/test_shipping_rule.js
-erpnext/crm/doctype/opportunity/test_opportunity.js
-erpnext/stock/doctype/item/tests/test_item.js
-erpnext/selling/doctype/quotation/tests/test_quotation_with_discount_on_grand_total.js
-erpnext/selling/doctype/quotation/tests/test_quotation_with_item_wise_discount.js
-erpnext/selling/doctype/quotation/tests/test_quotation_with_multi_uom.js
-erpnext/selling/doctype/quotation/tests/test_quotation_with_taxes_and_charges.js
-erpnext/selling/doctype/quotation/tests/test_quotation_with_shipping_rule.js
-erpnext/selling/doctype/quotation/tests/test_quotation.js
-erpnext/selling/doctype/sales_order/tests/test_sales_order.js
-erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multiple_delivery_date.js
-erpnext/selling/doctype/sales_order/tests/test_sales_order_with_item_wise_discount.js
-erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multi_uom.js
-erpnext/selling/doctype/sales_order/tests/test_sales_order_with_discount_on_grand_total.js
-erpnext/selling/doctype/sales_order/tests/test_sales_order_with_taxes_and_charges.js
-erpnext/selling/doctype/sales_order/tests/test_sales_order_with_shipping_rule.js
-erpnext/selling/doctype/sales_order/tests/test_sales_order_with_pricing_rule.js
-erpnext/manufacturing/doctype/workstation/test_workstation.js
-erpnext/manufacturing/doctype/operation/test_operation.js
-erpnext/manufacturing/doctype/bom/test_bom.js
-erpnext/projects/doctype/project/project_timesheet.js
-erpnext/hr/doctype/holiday_list/test_holiday_list.js
-erpnext/hr/doctype/branch/test_branch.js
-erpnext/hr/doctype/leave_block_list/test_leave_block_list.js
-erpnext/hr/doctype/department/test_department.js
-erpnext/hr/doctype/designation/test_designation.js
-erpnext/hr/doctype/employment_type/test_employment_type.js
-erpnext/hr/doctype/employee/test_employee.js
-erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js
-erpnext/hr/doctype/attendance/test_attendance.js
-erpnext/hr/doctype/leave_type/test_leave_type.js
-erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js
-erpnext/hr/doctype/leave_allocation/test_leave_allocation.js
-erpnext/hr/doctype/leave_application/test_leave_application.js
-erpnext/stock/doctype/warehouse/test_warehouse.js
-erpnext/manufacturing/doctype/work_order/test_work_order.js #long
-erpnext/accounts/page/pos/test_pos.js
-erpnext/selling/page/point_of_sale/tests/test_point_of_sale.js
-erpnext/selling/doctype/product_bundle/test_product_bundle.js
-erpnext/stock/doctype/delivery_note/test_delivery_note.js
-erpnext/stock/doctype/material_request/tests/test_material_request.js
-erpnext/stock/doctype/material_request/tests/test_material_request_type_material_issue.js
-erpnext/stock/doctype/material_request/tests/test_material_request_type_material_transfer.js
-erpnext/stock/doctype/material_request/tests/test_material_request_type_manufacture.js
-erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue.js
-erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt.js
-erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer.js
-erpnext/hr/doctype/salary_structure/test_salary_structure.js
-erpnext/hr/doctype/salary_slip/test_salary_slip.js
-erpnext/hr/doctype/job_opening/test_job_opening.js
-erpnext/hr/doctype/job_applicant/test_job_applicant.js
-erpnext/hr/doctype/job_offer/test_job_offer.js
-erpnext/hr/doctype/appraisal_template/test_appraisal_template.js
-erpnext/hr/doctype/appraisal/test_appraisal.js
-erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js
-erpnext/hr/doctype/expense_claim/test_expense_claim.js
-erpnext/hr/doctype/training_event/tests/test_training_event.js
-erpnext/hr/doctype/training_result_employee/test_training_result.js
-erpnext/hr/doctype/training_feedback/test_training_feedback.js
-erpnext/hr/doctype/loan_type/test_loan_type.js
-erpnext/hr/doctype/loan_application/test_loan_application.js
-erpnext/hr/doctype/loan/test_loan.js
-erpnext/buying/doctype/supplier/test_supplier.js
-erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js
-erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation.js
-erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_taxes_and_charges.js
-erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice.js
-erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment.js
-erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment_request.js
-erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.js
-erpnext/accounts/doctype/payment_entry/tests/test_payment_against_purchase_invoice.js
-erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js
-erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js
-erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_multi_uom.js
-erpnext/buying/doctype/purchase_order/tests/test_purchase_order_get_items.js
-erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_discount_on_grand_total.js
-erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_item_wise_discount.js
-erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js
-erpnext/buying/doctype/purchase_order/tests/test_purchase_order_receipt.js
-erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.js
-erpnext/accounts/doctype/journal_entry/test_journal_entry.js
-erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js
-erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js
-erpnext/selling/doctype/quotation/tests/test_quotation_submit_cancel_amend.js
-erpnext/stock/doctype/batch/test_batch.js
-erpnext/accounts/doctype/bank_reconciliation/test_bank_reconciliation.js
-erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt_for_serialize_item.js
-erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer_for_manufacture.js
-erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_subcontract.js
-erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with_serialize_item.js
-erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js
-erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js
-erpnext/accounts/doctype/payment_entry/tests/test_payment_against_invoice.js
-erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_last_purchase_rate.js
-erpnext/stock/doctype/item_price/test_item_price.js
-erpnext/stock/doctype/delivery_note/test_delivery_note_with_margin.js
-erpnext/selling/doctype/sales_order/tests/test_sales_order_with_margin.js
-erpnext/selling/doctype/quotation/tests/test_quotation_with_margin.js
-erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_margin.js
-erpnext/hr/doctype/payroll_entry/test_set_salary_components.js
-erpnext/hr/doctype/payroll_entry/test_payroll_entry.js
-erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule_with_same_currency.js
-erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule_with_different_currency.js
\ No newline at end of file
diff --git a/erpnext/tests/ui/tests2.txt b/erpnext/tests/ui/tests2.txt
deleted file mode 100644
index e410a83..0000000
--- a/erpnext/tests/ui/tests2.txt
+++ /dev/null
@@ -1,46 +0,0 @@
-erpnext/setup/doctype/company/tests/test_company.js
-erpnext/crm/doctype/lead/tests/test_lead_individual.js
-erpnext/crm/doctype/lead/tests/test_lead_organization.js
-erpnext/setup/doctype/company/tests/test_company_production.js
-erpnext/hr/doctype/leave_block_list/test_leave_block_list.js
-erpnext/hr/doctype/department/test_department.js
-erpnext/hr/doctype/designation/test_designation.js
-erpnext/education/doctype/academic_year/test_academic_year.js
-erpnext/education/doctype/academic_term/test_academic_term.js
-erpnext/education/doctype/education_settings/test_education_settings.js
-erpnext/education/doctype/student_batch_name/test_student_batch_name.js
-erpnext/education/doctype/student_category/test_student_category.js
-erpnext/education/doctype/room/test_room.js
-erpnext/education/doctype/instructor/test_instructor.js
-erpnext/education/doctype/grading_scale/test_grading_scale.js
-erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.js
-erpnext/education/doctype/assessment_criteria/test_assessment_criteria.js
-erpnext/education/doctype/course/test_course.js
-erpnext/education/doctype/program/test_program.js
-erpnext/education/doctype/guardian/test_guardian.js
-erpnext/education/doctype/student_admission/test_student_admission.js
-erpnext/education/doctype/student_applicant/tests/test_student_applicant_dummy_data.js
-erpnext/education/doctype/student_applicant/tests/test_student_applicant.js
-erpnext/education/doctype/student_applicant/tests/test_student_applicant_options.js
-erpnext/education/doctype/student_log/test_student_log.js
-erpnext/education/doctype/student_group/test_student_group.js
-erpnext/education/doctype/student_group_creation_tool/test_student_group_creation_tool.js
-erpnext/education/doctype/student_leave_application/test_student_leave_application.js
-erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.js
-erpnext/education/doctype/student_attendance/test_student_attendance.js
-erpnext/education/doctype/assessment_group/test_assessment_group.js
-erpnext/education/doctype/assessment_plan/test_assessment_plan.js
-erpnext/education/doctype/assessment_result/test_assessment_result.js
-erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.js
-erpnext/projects/doctype/task/tests/test_task_tree.js
-erpnext/restaurant/doctype/restaurant/test_restaurant.js
-erpnext/restaurant/doctype/restaurant_table/test_restaurant_table.js
-erpnext/restaurant/doctype/restaurant_menu/test_restaurant_menu.js
-erpnext/restaurant/doctype/restaurant_order_entry/restaurant_order_entry.js
-erpnext/non_profit/doctype/membership_type/test_membership_type.js
-erpnext/non_profit/doctype/member/test_member.js
-erpnext/non_profit/doctype/volunteer_type/test_volunteer_type.js
-erpnext/non_profit/doctype/volunteer/test_volunteer.js
-erpnext/non_profit/doctype/donor_type/test_donor_type.js
-erpnext/non_profit/doctype/donor/test_donor.js
-erpnext/non_profit/doctype/grant_application/test_grant_application.js
\ No newline at end of file
diff --git a/erpnext/tests/ui_test_bulk_transaction_processing.py b/erpnext/tests/ui_test_bulk_transaction_processing.py
deleted file mode 100644
index d78689e..0000000
--- a/erpnext/tests/ui_test_bulk_transaction_processing.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import frappe
-
-from erpnext.bulk_transaction.doctype.bulk_transaction_logger.test_bulk_transaction_logger import (
-	create_company,
-	create_customer,
-	create_item,
-	create_so,
-)
-
-
-@frappe.whitelist()
-def create_records():
-	create_company()
-	create_customer()
-	create_item()
-
-	gd = frappe.get_doc("Global Defaults")
-	gd.set("default_company", "Test Bulk")
-	gd.save()
-	frappe.clear_cache()
-	create_so()
\ No newline at end of file
diff --git a/erpnext/tests/ui_test_helpers.py b/erpnext/tests/ui_test_helpers.py
deleted file mode 100644
index 9c8c371..0000000
--- a/erpnext/tests/ui_test_helpers.py
+++ /dev/null
@@ -1,62 +0,0 @@
-import frappe
-from frappe.utils import getdate
-
-
-@frappe.whitelist()
-def create_employee_records():
-	create_company()
-	create_missing_designation()
-
-	frappe.db.sql("DELETE FROM tabEmployee WHERE company='Test Org Chart'")
-
-	emp1 = create_employee('Test Employee 1', 'CEO')
-	emp2 = create_employee('Test Employee 2', 'CTO')
-	emp3 = create_employee('Test Employee 3', 'Head of Marketing and Sales', emp1)
-	emp4 = create_employee('Test Employee 4', 'Project Manager', emp2)
-	emp5 = create_employee('Test Employee 5', 'Engineer', emp2)
-	emp6 = create_employee('Test Employee 6', 'Analyst', emp3)
-	emp7 = create_employee('Test Employee 7', 'Software Developer', emp4)
-
-	employees = [emp1, emp2, emp3, emp4, emp5, emp6, emp7]
-	return employees
-
-@frappe.whitelist()
-def get_employee_records():
-	return frappe.db.get_list('Employee', filters={
-		'company': 'Test Org Chart'
-	}, pluck='name', order_by='name')
-
-def create_company():
-	company = frappe.db.exists('Company', 'Test Org Chart')
-	if not company:
-		company = frappe.get_doc({
-			'doctype': 'Company',
-			'company_name': 'Test Org Chart',
-			'country': 'India',
-			'default_currency': 'INR'
-		}).insert().name
-
-	return company
-
-def create_employee(first_name, designation, reports_to=None):
-	employee = frappe.db.exists('Employee', {'first_name': first_name, 'designation': designation})
-	if not employee:
-		employee = frappe.get_doc({
-			'doctype': 'Employee',
-			'first_name': first_name,
-			'company': 'Test Org Chart',
-			'gender': 'Female',
-			'date_of_birth': getdate('08-12-1998'),
-			'date_of_joining': getdate('01-01-2021'),
-			'designation': designation,
-			'reports_to': reports_to
-		}).insert().name
-
-	return employee
-
-def create_missing_designation():
-	if not frappe.db.exists('Designation', 'CTO'):
-		frappe.get_doc({
-			'doctype': 'Designation',
-			'designation_name': 'CTO'
-		}).insert()
diff --git a/erpnext/tests/utils.py b/erpnext/tests/utils.py
index d795253..159ce70 100644
--- a/erpnext/tests/utils.py
+++ b/erpnext/tests/utils.py
@@ -9,83 +9,77 @@
 ReportFilters = Dict[str, Any]
 ReportName = NewType("ReportName", str)
 
+
 def create_test_contact_and_address():
-	frappe.db.sql('delete from tabContact')
-	frappe.db.sql('delete from `tabContact Email`')
-	frappe.db.sql('delete from `tabContact Phone`')
-	frappe.db.sql('delete from tabAddress')
-	frappe.db.sql('delete from `tabDynamic Link`')
+	frappe.db.sql("delete from tabContact")
+	frappe.db.sql("delete from `tabContact Email`")
+	frappe.db.sql("delete from `tabContact Phone`")
+	frappe.db.sql("delete from tabAddress")
+	frappe.db.sql("delete from `tabDynamic Link`")
 
-	frappe.get_doc({
-		"doctype": "Address",
-		"address_title": "_Test Address for Customer",
-		"address_type": "Office",
-		"address_line1": "Station Road",
-		"city": "_Test City",
-		"state": "Test State",
-		"country": "India",
-		"links": [
-			{
-				"link_doctype": "Customer",
-				"link_name": "_Test Customer"
-			}
-		]
-	}).insert()
+	frappe.get_doc(
+		{
+			"doctype": "Address",
+			"address_title": "_Test Address for Customer",
+			"address_type": "Office",
+			"address_line1": "Station Road",
+			"city": "_Test City",
+			"state": "Test State",
+			"country": "India",
+			"links": [{"link_doctype": "Customer", "link_name": "_Test Customer"}],
+		}
+	).insert()
 
-	contact = frappe.get_doc({
-		"doctype": 'Contact',
-		"first_name": "_Test Contact for _Test Customer",
-		"links": [
-			{
-				"link_doctype": "Customer",
-				"link_name": "_Test Customer"
-			}
-		]
-	})
+	contact = frappe.get_doc(
+		{
+			"doctype": "Contact",
+			"first_name": "_Test Contact for _Test Customer",
+			"links": [{"link_doctype": "Customer", "link_name": "_Test Customer"}],
+		}
+	)
 	contact.add_email("test_contact_customer@example.com", is_primary=True)
 	contact.add_phone("+91 0000000000", is_primary_phone=True)
 	contact.insert()
 
-	contact_two = frappe.get_doc({
-		"doctype": 'Contact',
-		"first_name": "_Test Contact 2 for _Test Customer",
-		"links": [
-			{
-				"link_doctype": "Customer",
-				"link_name": "_Test Customer"
-			}
-		]
-	})
+	contact_two = frappe.get_doc(
+		{
+			"doctype": "Contact",
+			"first_name": "_Test Contact 2 for _Test Customer",
+			"links": [{"link_doctype": "Customer", "link_name": "_Test Customer"}],
+		}
+	)
 	contact_two.add_email("test_contact_two_customer@example.com", is_primary=True)
 	contact_two.add_phone("+92 0000000000", is_primary_phone=True)
 	contact_two.insert()
 
 
 def execute_script_report(
-		report_name: ReportName,
-		module: str,
-		filters: ReportFilters,
-		default_filters: Optional[ReportFilters] = None,
-		optional_filters: Optional[ReportFilters] = None
-	):
+	report_name: ReportName,
+	module: str,
+	filters: ReportFilters,
+	default_filters: Optional[ReportFilters] = None,
+	optional_filters: Optional[ReportFilters] = None,
+):
 	"""Util for testing execution of a report with specified filters.
 
 	Tests the execution of report with default_filters + filters.
 	Tests the execution using optional_filters one at a time.
 
 	Args:
-		report_name: Human readable name of report (unscrubbed)
-		module: module to which report belongs to
-		filters: specific values for filters
-		default_filters: default values for filters such as company name.
-		optional_filters: filters which should be tested one at a time in addition to default filters.
+	        report_name: Human readable name of report (unscrubbed)
+	        module: module to which report belongs to
+	        filters: specific values for filters
+	        default_filters: default values for filters such as company name.
+	        optional_filters: filters which should be tested one at a time in addition to default filters.
 	"""
 
 	if default_filters is None:
 		default_filters = {}
 
 	test_filters = []
-	report_execute_fn = frappe.get_attr(get_report_module_dotted_path(module, report_name) + ".execute")
+	report_execute_fn = frappe.get_attr(
+		get_report_module_dotted_path(module, report_name) + ".execute"
+	)
 	report_filters = frappe._dict(default_filters).copy().update(filters)
 
 	test_filters.append(report_filters)
diff --git a/erpnext/translations/ar.csv b/erpnext/translations/ar.csv
index 91a9da9..e62f61a 100644
--- a/erpnext/translations/ar.csv
+++ b/erpnext/translations/ar.csv
@@ -4297,7 +4297,7 @@
 "To allow different rates, disable the {0} checkbox in {1}.",للسماح بمعدلات مختلفة ، قم بتعطيل مربع الاختيار {0} في {1}.,
 Current Odometer Value should be greater than Last Odometer Value {0},يجب أن تكون قيمة عداد المسافات الحالية أكبر من قيمة آخر عداد المسافات {0},
 No additional expenses has been added,لم يتم إضافة مصاريف إضافية,
-Asset{} {assets_link} created for {},الأصل {} {asset_link} الذي تم إنشاؤه لـ {},
+Asset{} {assets_link} created for {},الأصل {} {assets_link} الذي تم إنشاؤه لـ {},
 Row {}: Asset Naming Series is mandatory for the auto creation for item {},الصف {}: سلسلة تسمية الأصول إلزامية للإنشاء التلقائي للعنصر {},
 Assets not created for {0}. You will have to create asset manually.,لم يتم إنشاء الأصول لـ {0}. سيكون عليك إنشاء الأصل يدويًا.,
 {0} {1} has accounting entries in currency {2} for company {3}. Please select a receivable or payable account with currency {2}.,{0} يحتوي {1} على إدخالات محاسبية بالعملة {2} للشركة {3}. الرجاء تحديد حساب مستحق أو دائن بالعملة {2}.,
diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv
index b882b9d..45bc6c2 100644
--- a/erpnext/translations/de.csv
+++ b/erpnext/translations/de.csv
@@ -360,7 +360,7 @@
 Bank Statement Settings,Kontoauszug Einstellungen,
 Bank Statement balance as per General Ledger,Kontoauszug Bilanz nach Hauptbuch,
 Bank account cannot be named as {0},Bankname {0} ungültig,
-Bank/Cash transactions against party or for internal transfer,Bank / Geldgeschäfte gegen Partei oder für die interne Übertragung,
+Bank/Cash transactions against party or for internal transfer,Bank-/Bargeldtransaktionen mit einer Partei oder intern,
 Banking,Bankwesen,
 Banking and Payments,Bank- und Zahlungsverkehr,
 Barcode {0} already used in Item {1},Barcode {0} wird bereits für Artikel {1} verwendet,
@@ -783,7 +783,7 @@
 Default BOM ({0}) must be active for this item or its template,Standardstückliste ({0}) muss für diesen Artikel oder dessen Vorlage aktiv sein,
 Default BOM for {0} not found,Standardstückliste für {0} nicht gefunden,
 Default BOM not found for Item {0} and Project {1},Standard-Stückliste nicht gefunden für Position {0} und Projekt {1},
-Default In-Transit Warehouse, Standardlager für Waren im Transit,
+Default In-Transit Warehouse,Standard-Durchgangslager,
 Default Letter Head,Standardbriefkopf,
 Default Tax Template,Standardsteuervorlage,
 Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM.,"Die Standard-Maßeinheit für Artikel {0} kann nicht direkt geändert werden, weil Sie bereits einige Transaktionen mit einer anderen Maßeinheit durchgeführt haben. Sie müssen einen neuen Artikel erstellen, um eine andere Standard-Maßeinheit verwenden zukönnen.",
@@ -987,15 +987,15 @@
 Expected End Date,Voraussichtliches Enddatum,
 Expected Hrs,Erwartete Stunden,
 Expected Start Date,Voraussichtliches Startdatum,
-Expense,Auslage,
+Expense,Aufwand,
 Expense / Difference account ({0}) must be a 'Profit or Loss' account,"Aufwands-/Differenz-Konto ({0}) muss ein ""Gewinn oder Verlust""-Konto sein",
 Expense Account,Aufwandskonto,
-Expense Claim,Aufwandsabrechnung,
+Expense Claim,Auslagenabrechnung,
 Expense Claim for Vehicle Log {0},Auslagenabrechnung für Fahrtenbuch {0},
 Expense Claim {0} already exists for the Vehicle Log,Auslagenabrechnung {0} existiert bereits für das Fahrzeug Log,
 Expense Claims,Aufwandsabrechnungen,
 Expense account is mandatory for item {0},Aufwandskonto ist zwingend für Artikel {0},
-Expenses,Ausgaben,
+Expenses,Aufwendungen,
 Expenses Included In Asset Valuation,"Aufwendungen, die in der Vermögensbewertung enthalten sind",
 Expenses Included In Valuation,In der Bewertung enthaltene Aufwendungen,
 Expired Batches,Abgelaufene Chargen,
@@ -1098,7 +1098,7 @@
 From Delivery Note,Von Lieferschein,
 From Fiscal Year,Ab dem Geschäftsjahr,
 From GSTIN,Von GSTIN,
-From Party Name,Von Party Name,
+From Party Name,Name des Absenders,
 From Pin Code,Von Pin-Code,
 From Place,Von Ort,
 From Range has to be less than To Range,Von-Bereich muss kleiner sein als Bis-Bereich,
@@ -1163,7 +1163,8 @@
 Grand Total,Gesamtbetrag,
 Grant,Gewähren,
 Grant Application,Antrag bewilligen,
-Grant Leaves,Grant Blätter,
+Grant Commission,Provision gewähren,
+Grant Leaves,Meldungen gewähren,
 Grant information.,Gewähren Sie Informationen.,
 Grocery,Lebensmittelgeschäft,
 Gross Pay,Bruttolohn,
@@ -1173,11 +1174,11 @@
 Gross Purchase Amount,Bruttokaufbetrag,
 Gross Purchase Amount is mandatory,Bruttokaufbetrag ist erforderlich,
 Group by Account,Gruppieren nach Konto,
-Group by Party,Gruppieren nach Parteien,
+Group by Party,Gruppieren nach Partei,
 Group by Voucher,Gruppieren nach Beleg,
 Group by Voucher (Consolidated),Gruppieren nach Beleg (konsolidiert),
 Group node warehouse is not allowed to select for transactions,Gruppenknoten Lager ist nicht für Transaktionen zu wählen erlaubt,
-Group to Non-Group,Gruppe an konzernfremde,
+Convert to Ledger,In Lagerbuch umwandeln,Warehouse
 Group your students in batches,Gruppieren Sie Ihre Schüler in den Reihen,
 Groups,Gruppen,
 Guardian1 Email ID,Guardian1 E-Mail-ID,
@@ -1332,6 +1333,7 @@
 Inward supplies from ISD,Nachschub von ISD,
 Inward supplies liable to reverse charge (other than 1 & 2 above),Rückbelastungspflichtige Lieferungen (außer 1 &amp; 2 oben),
 Is Active,Ist aktiv(iert),
+Is Debit Note,Ist Lastschrift,
 Is Default,Ist Standard,
 Is Existing Asset,Vermögenswert existiert bereits.,
 Is Frozen,Ist gesperrt,
@@ -1541,7 +1543,7 @@
 Marketing Expenses,Marketingkosten,
 Marketplace,Marktplatz,
 Marketplace Error,Marktplatzfehler,
-Masters,Stämme,
+Masters,Stammdaten,
 Match Payments with Invoices,Zahlungen und Rechnungen abgleichen,
 Match non-linked Invoices and Payments.,Nicht verknüpfte Rechnungen und Zahlungen verknüpfen,
 Material,Material,
@@ -1621,7 +1623,7 @@
 More than one selection for {0} not allowed,Mehr als eine Auswahl für {0} ist nicht zulässig,
 More...,Mehr...,
 Motion Picture & Video,Film & Fernsehen,
-Move,Bewegen,
+Move,Verschieben,
 Move Item,Element verschieben,
 Multi Currency,Unterschiedliche Währungen,
 Multiple Item prices.,Mehrere verschiedene Artikelpreise,
@@ -1699,7 +1701,7 @@
 No Remarks,Keine Anmerkungen,
 No Result to submit,Kein Ergebnis zur Einreichung,
 No Salary Structure assigned for Employee {0} on given date {1},Keine Gehaltsstruktur für Mitarbeiter {0} am angegebenen Datum {1} zugewiesen,
-No Staffing Plans found for this Designation,Für diese Bezeichnung wurden keine Stellenpläne gefunden,
+No Staffing Plans found for this Designation,Für diese Position wurden keine Stellenpläne gefunden,
 No Student Groups created.,Keine Studentengruppen erstellt.,
 No Students in,Keine Studenten in,
 No Tax Withholding data found for the current Fiscal Year.,Keine Steuerverweigerungsdaten für das aktuelle Geschäftsjahr gefunden.,
@@ -1733,7 +1735,6 @@
 Non Profit,Gemeinnützig,
 Non Profit (beta),Non-Profit (Beta),
 Non-GST outward supplies,Nicht-GST-Lieferungen nach außen,
-Non-Group to Group,Non-Group-Gruppe,
 None,Keiner,
 None of the items have any change in quantity or value.,Keiner der Artikel hat irgendeine Änderung bei Mengen oder Kosten.,
 Nos,Stk,
@@ -1871,12 +1872,12 @@
 Part-time,Teilzeit,
 Partially Depreciated,Teilweise abgeschrieben,
 Partially Received,Teilweise erhalten,
-Party,Gruppe,
-Party Name,Name,
-Party Type,Gruppen-Typ,
-Party Type and Party is mandatory for {0} account,Party Type und Party ist für das Konto {0} obligatorisch,
-Party Type is mandatory,Party-Typ ist Pflicht,
-Party is mandatory,Partei ist obligatorisch,
+Party,Partei,
+Party Name,Name der Partei,
+Party Type,Partei-Typ,
+Party Type and Party is mandatory for {0} account,Partei-Typ und Partei sind Pflichtfelder für Konto {0},
+Party Type is mandatory,Partei-Typ ist ein Pflichtfeld,
+Party is mandatory,Partei ist ein Pflichtfeld,
 Password,Passwort,
 Password policy for Salary Slips is not set,Die Kennwortrichtlinie für Gehaltsabrechnungen ist nicht festgelegt,
 Past Due Date,Fälligkeitsdatum,
@@ -1943,7 +1944,7 @@
 Pharmaceuticals,Pharmaprodukte,
 Physician,Arzt,
 Piecework,Akkordarbeit,
-Pincode,Postleitzahl (PLZ),
+Pincode,Postleitzahl,
 Place Of Supply (State/UT),Ort der Lieferung (Staat / UT),
 Place Order,Bestellung aufgeben,
 Plan Name,Planname,
@@ -2025,7 +2026,7 @@
 Please select Category first,Bitte zuerst Kategorie auswählen,
 Please select Charge Type first,Bitte zuerst Chargentyp auswählen,
 Please select Company,Bitte Unternehmen auswählen,
-Please select Company and Designation,Bitte wählen Sie Unternehmen und Stelle,
+Please select Company and Designation,Bitte wählen Sie Unternehmen und Position,
 Please select Company and Posting Date to getting entries,"Bitte wählen Sie Unternehmen und Buchungsdatum, um Einträge zu erhalten",
 Please select Company first,Bitte zuerst Unternehmen auswählen,
 Please select Completion Date for Completed Asset Maintenance Log,Bitte wählen Sie Fertigstellungsdatum für das abgeschlossene Wartungsprotokoll für den Vermögenswert,
@@ -2037,10 +2038,10 @@
 Please select Healthcare Service,Bitte wählen Sie Gesundheitsdienst,
 "Please select Item where ""Is Stock Item"" is ""No"" and ""Is Sales Item"" is ""Yes"" and there is no other Product Bundle","Bitte einen Artikel auswählen, bei dem ""Ist Lagerartikel"" mit ""Nein"" und ""Ist Verkaufsartikel"" mit ""Ja"" bezeichnet ist, und es kein anderes Produkt-Bundle gibt",
 Please select Maintenance Status as Completed or remove Completion Date,Bitte wählen Sie Wartungsstatus als erledigt oder entfernen Sie das Abschlussdatum,
-Please select Party Type first,Bitte zuerst Gruppentyp auswählen,
+Please select Party Type first,Bitte zuerst Partei-Typ auswählen,
 Please select Patient,Bitte einen Patienten auswählen,
 Please select Patient to get Lab Tests,"Bitte wählen Sie Patient, um Labortests zu erhalten",
-Please select Posting Date before selecting Party,Bitte wählen Sie Buchungsdatum vor dem Party-Auswahl,
+Please select Posting Date before selecting Party,Bitte erst Buchungsdatum und dann die Partei auswählen,
 Please select Posting Date first,Bitte zuerst ein Buchungsdatum auswählen,
 Please select Price List,Bitte eine Preisliste auswählen,
 Please select Program,Bitte wählen Sie Programm,
@@ -2182,7 +2183,7 @@
 Process Master Data,Stammdaten bearbeiten,
 Processing Chart of Accounts and Parties,Verarbeiten des Kontenplans und der Parteien,
 Processing Items and UOMs,Verarbeiten von Artikeln und Mengeneinheiten,
-Processing Party Addresses,Bearbeiteradressen,
+Processing Party Addresses,Verarbeitung der Adressen der Parteien,
 Processing Vouchers,Bearbeitung von Gutscheinen,
 Procurement,Beschaffung,
 Produced Qty,Produzierte Menge,
@@ -2486,8 +2487,8 @@
 Row {0}: From time must be less than to time,Zeile {0}: Von Zeit zu Zeit muss kleiner sein,
 Row {0}: Hours value must be greater than zero.,Row {0}: Stunden-Wert muss größer als Null sein.,
 Row {0}: Invalid reference {1},Zeile {0}: Ungültige Referenz {1},
-Row {0}: Party / Account does not match with {1} / {2} in {3} {4},Zeile {0}: Gruppe / Konto stimmt nicht mit {1} / {2} in {3} {4} überein,
-Row {0}: Party Type and Party is required for Receivable / Payable account {1},Zeile {0}: Gruppen-Typ und Gruppe sind für Forderungen-/Verbindlichkeiten-Konto {1} zwingend erforderlich,
+Row {0}: Party / Account does not match with {1} / {2} in {3} {4},Zeile {0}: Partei / Konto stimmt nicht mit {1} / {2} in {3} {4} überein,
+Row {0}: Party Type and Party is required for Receivable / Payable account {1},Zeile {0}: Partei-Typ und Partei sind für Forderungen-/Verbindlichkeiten-Konto {1} zwingend erforderlich,
 Row {0}: Payment against Sales/Purchase Order should always be marked as advance,"Zeile {0}: ""Zahlung zu Auftrag bzw. Bestellung"" sollte immer als ""Vorkasse"" eingestellt werden",
 Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.,"Zeile {0}: Wenn es sich um eine Vorkasse-Buchung handelt, bitte ""Ist Vorkasse"" zu Konto {1} anklicken, .",
 Row {0}: Please set at Tax Exemption Reason in Sales Taxes and Charges,Zeile {0}: Bitte setzen Sie den Steuerbefreiungsgrund in den Umsatzsteuern und -gebühren,
@@ -2539,7 +2540,7 @@
 Sales Partner,Vertriebspartner,
 Sales Pipeline,Vertriebspipeline,
 Sales Price List,Verkaufspreisliste,
-Sales Return,Rücklieferung,
+Sales Return,Retoure,
 Sales Summary,Verkaufszusammenfassung,
 Sales Tax Template,Umsatzsteuer-Vorlage,
 Sales Team,Verkaufsteam,
@@ -2770,7 +2771,7 @@
 Split Batch,Split Batch,
 Split Issue,Split-Problem,
 Sports,Sport,
-Staffing Plan {0} already exist for designation {1},Personalplan {0} existiert bereits für Bezeichnung {1},
+Staffing Plan {0} already exist for designation {1},Personalplan {0} existiert bereits für Position {1},
 Standard,Standard,
 Standard Buying,Standard-Kauf,
 Standard Selling,Standard-Vertrieb,
@@ -3045,7 +3046,7 @@
 To Deliver and Bill,Auszuliefern und Abzurechnen,
 To Fiscal Year,Bis zum Geschäftsjahr,
 To GSTIN,Zu GSTIN,
-To Party Name,Zum Party-Namen,
+To Party Name,Name des Empfängers,
 To Pin Code,PIN-Code,
 To Place,Hinstellen,
 To Receive,Zu empfangen,
@@ -3056,7 +3057,7 @@
 To date can not be equal or less than from date,Bis heute kann nicht gleich oder weniger als von Datum sein,
 To date can not be less than from date,Bis heute kann nicht weniger als von Datum sein,
 To date can not greater than employee's relieving date,Bis heute kann nicht mehr als Entlastungsdatum des Mitarbeiters sein,
-"To filter based on Party, select Party Type first","Um auf der Grundlage von Gruppen zu filtern, bitte zuerst den Gruppentyp wählen",
+"To filter based on Party, select Party Type first","Bitte Partei-Typ wählen um nach Partei zu filtern",
 "To get the best out of ERPNext, we recommend that you take some time and watch these help videos.","Um ERPNext bestmöglich zu nutzen, empfehlen wir Ihnen, sich die Zeit zu nehmen diese Hilfevideos anzusehen.",
 "To include tax in row {0} in Item rate, taxes in rows {1} must also be included","Um Steuern im Artikelpreis in Zeile {0} einzubeziehen, müssen Steuern in den Zeilen {1} ebenfalls einbezogen sein",
 To make Customer based incentive schemes.,Um Kunden basierte Anreizsysteme zu machen.,
@@ -3708,7 +3709,7 @@
 Delivery Notes,Lieferscheine,
 Depreciated Amount,Abschreibungsbetrag,
 Description,Beschreibung,
-Designation,Bezeichnung,
+Designation,Position,
 Difference Value,Differenzwert,
 Dimension Filter,Dimensionsfilter,
 Disabled,Deaktiviert,
@@ -3918,7 +3919,7 @@
 Please enter GSTIN and state for the Company Address {0},Bitte geben Sie GSTIN ein und geben Sie die Firmenadresse {0} an.,
 Please enter Item Code to get item taxes,"Bitte geben Sie den Artikelcode ein, um die Artikelsteuern zu erhalten",
 Please enter Warehouse and Date,Bitte geben Sie Lager und Datum ein,
-Please enter the designation,Bitte geben Sie die Bezeichnung ein,
+Please enter the designation,Bitte geben Sie die Position ein,
 Please login as a Marketplace User to edit this item.,"Bitte melden Sie sich als Marketplace-Benutzer an, um diesen Artikel zu bearbeiten.",
 Please login as a Marketplace User to report this item.,"Bitte melden Sie sich als Marketplace-Benutzer an, um diesen Artikel zu melden.",
 Please select <b>Template Type</b> to download template,"Bitte wählen Sie <b>Vorlagentyp</b> , um die Vorlage herunterzuladen",
@@ -4548,7 +4549,7 @@
 Account Subtype,Kontosubtyp,
 Is Default Account,Ist Standardkonto,
 Is Company Account,Ist Unternehmenskonto,
-Party Details,Party Details,
+Party Details,Details der Partei,
 Account Details,Kontendaten,
 IBAN,IBAN,
 Bank Account No,Bankkonto Nr,
@@ -4784,9 +4785,9 @@
 Subscription Section,Abonnementbereich,
 Journal Entry Account,Journalbuchungskonto,
 Account Balance,Kontostand,
-Party Balance,Gruppen-Saldo,
+Party Balance,Saldo der Partei,
 Accounting Dimensions,Abrechnungsdimensionen,
-If Income or Expense,Wenn Ertrag oder Aufwand,
+If Income or Expense,Wenn Ertrag oder Aufwand,
 Exchange Rate,Wechselkurs,
 Debit in Company Currency,Soll in Unternehmenswährung,
 Credit in Company Currency,(Gut)Haben in Unternehmenswährung,
@@ -4827,11 +4828,11 @@
 Monthly Distribution Percentages,Prozentuale Aufteilungen der monatsweisen Verteilung,
 Monthly Distribution Percentage,Prozentuale Aufteilung der monatsweisen Verteilung,
 Percentage Allocation,Prozentuale Aufteilung,
-Create Missing Party,Erstelle fehlende Partei,
+Create Missing Party,Fehlende Partei erstellen,
 Create missing customer or supplier.,Erstelle einen fehlenden Kunden oder Lieferanten.,
 Opening Invoice Creation Tool Item,Eröffnen des Rechnungserstellungswerkzeugs,
 Temporary Opening Account,Temporäres Eröffnungskonto,
-Party Account,Gruppenkonto,
+Party Account,Konto der Partei,
 Type of Payment,Zahlungsart,
 ACC-PAY-.YYYY.-,ACC-PAY-.JJJJ.-,
 Receive,Empfangen,
@@ -4840,7 +4841,7 @@
 Payment Ordered,Zahlung bestellt,
 Payment From / To,Zahlung von / an,
 Company Bank Account,Firmenkonto,
-Party Bank Account,Party-Bankkonto,
+Party Bank Account,Bankkonto der Partei,
 Account Paid From,Ausgangskonto,
 Account Paid To,Eingangskonto,
 Paid Amount (Company Currency),Gezahlter Betrag (Unternehmenswährung),
@@ -4944,7 +4945,7 @@
 Coupon Code Based,Gutscheincode basiert,
 Discount on Other Item,Rabatt auf andere Artikel,
 Apply Rule On Other,Regel auf andere anwenden,
-Party Information,Party Informationen,
+Party Information,Informationen zur Partei,
 Quantity and Amount,Menge und Menge,
 Min Qty,Mindestmenge,
 Max Qty,Maximalmenge,
@@ -5046,7 +5047,7 @@
 Print Language,Drucksprache,
 "Once set, this invoice will be on hold till the set date","Einmal eingestellt, wird diese Rechnung bis zum festgelegten Datum gehalten",
 Credit To,Gutschreiben auf,
-Party Account Currency,Gruppenkonten-Währung,
+Party Account Currency,Währung des Kontos der Partei,
 Against Expense Account,Zu Aufwandskonto,
 Inter Company Invoice Reference,Unternehmensübergreifende Rechnungsreferenz,
 Is Internal Supplier,Ist interner Lieferant,
@@ -5061,7 +5062,7 @@
 Rejected Qty,Abgelehnt Menge,
 UOM Conversion Factor,Maßeinheit-Umrechnungsfaktor,
 Discount on Price List Rate (%),Rabatt auf die Preisliste (%),
-Price List Rate (Company Currency),Preisliste (Unternehmenswährung),
+Price List Rate (Company Currency),Preisliste (Unternehmenswährung),
 Rate ,Preis,
 Rate (Company Currency),Preis (Unternehmenswährung),
 Amount (Company Currency),Betrag (Unternehmenswährung),
@@ -5648,7 +5649,7 @@
 Campaign Email Schedule,Kampagnen-E-Mail-Zeitplan,
 Send After (days),Senden nach (Tage),
 Signed,Unterzeichnet,
-Party User,Party Benutzer,
+Party User,Benutzer der Partei,
 Unsigned,Nicht unterzeichnet,
 Fulfilment Status,Erfüllungsstatus,
 N/A,nicht verfügbar,
@@ -6241,7 +6242,7 @@
 Create Sample Collection document for Lab Test,Erstellen Sie ein Probensammeldokument für den Labortest,
 Checking this will create a Sample Collection document  every time you create a Lab Test,"Wenn Sie dies aktivieren, wird jedes Mal, wenn Sie einen Labortest erstellen, ein Probensammeldokument erstellt",
 Employee name and designation in print,Name und Bezeichnung des Mitarbeiters im Druck,
-Check this if you want the Name and Designation of the Employee associated with the User who submits the document to be printed in the Lab Test Report.,"Aktivieren Sie diese Option, wenn Sie möchten, dass der Name und die Bezeichnung des Mitarbeiters, der dem Benutzer zugeordnet ist, der das Dokument einreicht, im Labortestbericht gedruckt werden.",
+Check this if you want the Name and Designation of the Employee associated with the User who submits the document to be printed in the Lab Test Report.,"Aktivieren Sie diese Option, wenn Sie möchten, dass der Name und die Position des Mitarbeiters, der dem Benutzer zugeordnet ist, der das Dokument einreicht, im Labortestbericht gedruckt werden.",
 Do not print or email Lab Tests without Approval,Drucken oder senden Sie Labortests nicht ohne Genehmigung per E-Mail,
 Checking this will restrict printing and emailing of Lab Test documents unless they have the status as Approved.,"Wenn Sie dies aktivieren, wird das Drucken und E-Mailen von Labortestdokumenten eingeschränkt, sofern diese nicht den Status &quot;Genehmigt&quot; haben.",
 Custom Signature in Print,Kundenspezifische Unterschrift im Druck,
@@ -6497,7 +6498,7 @@
 Approver,Genehmiger,
 Required Skills,Benötigte Fähigkeiten,
 Skills,Kompetenzen,
-Designation Skill,Bezeichnung Fähigkeit,
+Designation Skill,Positions Fähigkeit,
 Skill,Fertigkeit,
 Driver,Fahrer/-in,
 HR-DRI-.YYYY.-,HR-DRI-.YYYY.-,
@@ -6515,20 +6516,20 @@
 HR-EMP-,HR-EMP-,
 Employment Type,Art der Beschäftigung,
 Emergency Contact,Notfallkontakt,
-Emergency Contact Name,Notfall Kontaktname,
-Emergency Phone,Notruf,
+Emergency Contact Name,Name des Notfallkontakts,
+Emergency Phone,Telefonnummer des Notfallkontakts,
 ERPNext User,ERPNext Benutzer,
 "System User (login) ID. If set, it will become default for all HR forms.","Systembenutzer-ID (Anmeldung). Wenn gesetzt, wird sie standardmäßig für alle HR-Formulare verwendet.",
 Create User Permission,Benutzerberechtigung Erstellen,
 This will restrict user access to other employee records,Dies schränkt den Benutzerzugriff auf andere Mitarbeiterdatensätze ein,
 Joining Details,Details des Beitritts,
 Offer Date,Angebotsdatum,
-Confirmation Date,Datum bestätigen,
+Confirmation Date,Bestätigungsdatum,
 Contract End Date,Vertragsende,
-Notice (days),Meldung(s)(-Tage),
+Notice (days),Kündigungsfrist (Tage),
 Date Of Retirement,Zeitpunkt der Pensionierung,
 Department and Grade,Abteilung und Klasse,
-Reports to,Berichte an,
+Reports to,Vorgesetzter,
 Attendance and Leave Details,Anwesenheits- und Urlaubsdetails,
 Leave Policy,Urlaubsrichtlinie,
 Attendance Device ID (Biometric/RF tag ID),Anwesenheitsgeräte-ID (biometrische / RF-Tag-ID),
@@ -6551,8 +6552,8 @@
 Provide Email Address registered in company,Geben Sie E-Mail-Adresse in Unternehmen registriert,
 Current Address Is,Aktuelle Adresse ist,
 Current Address,Aktuelle Adresse,
-Personal Bio,Persönliches Bio,
-Bio / Cover Letter,Bio / Anschreiben,
+Personal Bio,Lebenslauf,
+Bio / Cover Letter,Lebenslauf / Anschreiben,
 Short biography for website and other publications.,Kurzbiographie für die Webseite und andere Publikationen.,
 Passport Number,Passnummer,
 Date of Issue,Ausstellungsdatum,
@@ -6796,7 +6797,7 @@
 Employment Type (optional),Anstellungsart (optional),
 Branch (optional),Zweigstelle (optional),
 Department (optional),Abteilung (optional),
-Designation (optional),Bezeichnung (optional),
+Designation (optional),Position (optional),
 Employee Grade (optional),Dienstgrad (optional),
 Employee (optional),Mitarbeiter (optional),
 Allocate Leaves,Blätter zuweisen,
@@ -7651,7 +7652,7 @@
 Buyer of Goods and Services.,Käufer von Waren und Dienstleistungen.,
 CUST-.YYYY.-,CUST-.YYYY.-,
 Default Company Bank Account,Standard-Bankkonto des Unternehmens,
-From Lead,Von Lead,
+From Lead,Aus Lead,
 Account Manager,Buchhalter,
 Allow Sales Invoice Creation Without Sales Order,Ermöglichen Sie die Erstellung von Kundenrechnungen ohne Auftrag,
 Allow Sales Invoice Creation Without Delivery Note,Ermöglichen Sie die Erstellung einer Ausgangsrechnung ohne Lieferschein,
@@ -7767,7 +7768,7 @@
 Applicable To (Role),Anwenden auf (Rolle),
 Applicable To (Employee),Anwenden auf (Mitarbeiter),
 Applicable To (User),Anwenden auf (Benutzer),
-Applicable To (Designation),Anwenden auf (Bezeichnung),
+Applicable To (Designation),Anwenden auf (Position),
 Approving Role (above authorized value),Genehmigende Rolle (über dem autorisierten Wert),
 Approving User  (above authorized value),Genehmigender Benutzer (über dem autorisierten Wert),
 Brand Defaults,Markenstandards,
@@ -8596,7 +8597,7 @@
 Total Stock Summary,Gesamt Stock Zusammenfassung,
 Trial Balance,Probebilanz,
 Trial Balance (Simple),Probebilanz (einfach),
-Trial Balance for Party,Summen- und Saldenliste für Gruppe,
+Trial Balance for Party,Summen- und Saldenliste für Partei,
 Unpaid Expense Claim,Ungezahlte Spesenabrechnung,
 Warehouse wise Item Balance Age and Value,Lagerweise Item Balance Alter und Wert,
 Work Order Stock Report,Arbeitsauftragsbericht,
@@ -8944,7 +8945,7 @@
 Requesting Department,Abteilung anfordern,
 Employee (Lab Technician),Mitarbeiter (Labortechniker),
 Lab Technician Name,Name des Labortechnikers,
-Lab Technician Designation,Bezeichnung des Labortechnikers,
+Lab Technician Designation,Position des Labortechnikers,
 Compound Test Result,Zusammengesetztes Testergebnis,
 Organism Test Result,Organismustestergebnis,
 Sensitivity Test Result,Empfindlichkeitstestergebnis,
@@ -9222,7 +9223,7 @@
 Unmarked Days,Nicht markierte Tage,
 Jan,Jan.,
 Feb,Feb.,
-Mar,Beschädigen,
+Mar,Mrz.,
 Apr,Apr.,
 Aug,Aug.,
 Sep,Sep.,
@@ -9531,7 +9532,7 @@
 "To overrule this, enable '{0}' in company {1}","Um dies zu überschreiben, aktivieren Sie &#39;{0}&#39; in Firma {1}",
 Invalid condition expression,Ungültiger Bedingungsausdruck,
 Please Select a Company First,Bitte wählen Sie zuerst eine Firma aus,
-Please Select Both Company and Party Type First,Bitte wählen Sie zuerst Firmen- und Partytyp aus,
+Please Select Both Company and Party Type First,Bitte zuerst Unternehmen und Partei-Typ auswählen,
 Provide the invoice portion in percent,Geben Sie den Rechnungsanteil in Prozent an,
 Give number of days according to prior selection,Geben Sie die Anzahl der Tage gemäß vorheriger Auswahl an,
 Email Details,E-Mail-Details,
@@ -9540,7 +9541,7 @@
 Please select a Supplier,Bitte wählen Sie einen Lieferanten aus,
 Supplier Lead Time (days),Vorlaufzeit des Lieferanten (Tage),
 "Home, Work, etc.","Zuhause, Arbeit usw.",
-Exit Interview Held On,Beenden Sie das Interview,
+Exit Interview Held On,Entlassungsgespräch am,
 Condition and formula,Zustand und Formel,
 Sets 'Target Warehouse' in each row of the Items table.,Legt &#39;Ziellager&#39; in jeder Zeile der Elementtabelle fest.,
 Sets 'Source Warehouse' in each row of the Items table.,Legt &#39;Source Warehouse&#39; in jeder Zeile der Items-Tabelle fest.,
@@ -9845,3 +9846,29 @@
 Creating Purchase Order ...,Bestellung anlegen ...,
 "Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Wählen Sie einen Lieferanten aus den Standardlieferanten der folgenden Artikel aus. Bei der Auswahl erfolgt eine Bestellung nur für Artikel, die dem ausgewählten Lieferanten gehören.",
 Row #{}: You must select {} serial numbers for item {}.,Zeile # {}: Sie müssen {} Seriennummern für Artikel {} auswählen.,
+{}  To Deliver,{} Zu liefern,
+{}  To Receive,{} Zu erhalten,
+{}  Available,{} Verfügbar,
+Report an Issue,Ein Problem melden,
+User Forum,Anwenderforum,
+Get Customer Group Details,Einstellungen aus Kundengruppe übernehmen,
+Is Rate Adjustment Entry (Debit Note),Ist Preisanpassung (Belastungsanzeige),
+Fetch Timesheet,Zeiterfassung laden,
+Company Tax ID,Eigene Steuernummer,
+Quotation Number,Angebotsnummer,
+Company Shipping Address,Eigene Lieferadresse,
+Company Billing Address,Eigene Rechnungsadresse,
+Billing Address Details,Vorschau Rechnungsadresse,
+Supplier Contact,Lieferantenkontakt,
+Order Status,Bestellstatus,
+Invoice Portion (%),Rechnungsanteil (%),
+Discount Settings,Rabatt-Einstellungen,
+Payment Amount (Company Currency),Zahlungsbetrag (Unternehmenswährung),
+Putaway Rule,Einlagerungsregel,
+Apply Putaway Rule,Einlagerungsregel anwenden,
+Default Discount Account,Standard-Rabattkonto,
+Default Provisional Account,Standard Provisorisches Konto,
+Leave Type Allocation,Zuordnung Abwesenheitsarten,
+From Lead,Aus Lead,
+From Opportunity,Aus Chance,
+Publish in Website,Auf Webseite veröffentlichen,
diff --git a/erpnext/translations/fr.csv b/erpnext/translations/fr.csv
index 3cdae45..ffc46d2 100644
--- a/erpnext/translations/fr.csv
+++ b/erpnext/translations/fr.csv
@@ -175,7 +175,7 @@
 All Accounts,Tous les comptes,
 All Addresses.,Toutes les adresses.,
 All Assessment Groups,Tous les Groupes d'Évaluation,
-All BOMs,Toutes les LDM,
+All BOMs,Toutes les nomenclatures,
 All Contacts.,Tous les contacts.,
 All Customer Groups,Tous les Groupes Client,
 All Day,Toute la Journée,
@@ -271,7 +271,7 @@
 Assessment Reports,Rapports d'évaluation,
 Assessment Result,Résultat de l'Évaluation,
 Assessment Result record {0} already exists.,Le Résultat d'Évaluation {0} existe déjà.,
-Asset,Atout,
+Asset,Actif - Immo.,
 Asset Category,Catégorie d'Actif,
 Asset Category is mandatory for Fixed Asset item,Catégorie d'Actif est obligatoire pour l'article Immobilisé,
 Asset Maintenance,Maintenance des actifs,
@@ -285,7 +285,7 @@
 "Asset {0} cannot be scrapped, as it is already {1}","L'actif {0} ne peut pas être mis au rebut, car il est déjà {1}",
 Asset {0} does not belong to company {1},L'actif {0} ne fait pas partie à la société {1},
 Asset {0} must be submitted,L'actif {0} doit être soumis,
-Assets,Les atouts,
+Assets,Actifs - Immo.,
 Assign,Assigner,
 Assign Salary Structure,Affecter la structure salariale,
 Assign To,Attribuer À,
@@ -330,16 +330,16 @@
 Avg. Buying Price List Rate,Moyenne de la liste de prix d'achat,
 Avg. Selling Price List Rate,Prix moyen de la liste de prix de vente,
 Avg. Selling Rate,Moy. Taux de vente,
-BOM,LDM (Liste de Matériaux),
-BOM Browser,Explorateur LDM,
-BOM No,N° LDM,
-BOM Rate,Taux LDM,
-BOM Stock Report,Rapport de Stock de LDM,
-BOM and Manufacturing Quantity are required,LDM et quantité de production sont nécessaires,
-BOM does not contain any stock item,LDM ne contient aucun article en stock,
-BOM {0} does not belong to Item {1},LDM {0} n’appartient pas à l'article {1},
-BOM {0} must be active,LDM {0} doit être active,
-BOM {0} must be submitted,LDM {0} doit être soumise,
+BOM,Nomenclature,
+BOM Browser,Explorateur Nomenclature,
+BOM No,N° Nomenclature,
+BOM Rate,Valeur nomenclature,
+BOM Stock Report,Rapport de Stock des nomenclatures,
+BOM and Manufacturing Quantity are required,Nomenclature et quantité de production sont nécessaires,
+BOM does not contain any stock item,Nomenclature ne contient aucun article en stock,
+BOM {0} does not belong to Item {1},Nomenclature {0} n’appartient pas à l'article {1},
+BOM {0} must be active,Nomenclature {0} doit être active,
+BOM {0} must be submitted,Nomenclature {0} doit être soumise,
 Balance,Solde,
 Balance (Dr - Cr),Balance (Dr - Cr),
 Balance ({0}),Solde ({0}),
@@ -386,8 +386,8 @@
 Bill,Facture,
 Bill Date,Date de la Facture,
 Bill No,Numéro de facture,
-Bill of Materials,Liste de Matériaux,
-Bill of Materials (BOM),Liste de Matériaux (LDM),
+Bill of Materials,Nomenclatures,
+Bill of Materials (BOM),Nomenclature,
 Billable Hours,Heures facturables,
 Billed,Facturé,
 Billed Amount,Montant facturé,
@@ -404,14 +404,14 @@
 Black,Noir,
 Blanket Orders from Costumers.,Commandes provisoires de clients.,
 Block Invoice,Bloquer la facture,
-Boms,Listes de Matériaux,
+Boms,Nomenclatures,
 Bonus Payment Date cannot be a past date,La date de paiement du bonus ne peut pas être une date passée,
 Both Trial Period Start Date and Trial Period End Date must be set,La date de début de la période d&#39;essai et la date de fin de la période d&#39;essai doivent être définies,
 Both Warehouse must belong to same Company,Les deux Entrepôt doivent appartenir à la même Société,
 Branch,Branche,
 Broadcasting,Radio/Télévision,
 Brokerage,Courtage,
-Browse BOM,Parcourir la LDM,
+Browse BOM,Parcourir la nomenclature,
 Budget Against,Budget Pour,
 Budget List,Liste budgétaire,
 Budget Variance Report,Rapport d’Écarts de Budget,
@@ -467,7 +467,7 @@
 Cannot covert to Group because Account Type is selected.,Conversion impossible en Groupe car le Type de Compte est sélectionné.,
 Cannot create Retention Bonus for left Employees,Impossible de créer une prime de fidélisation pour les employés ayant quitté l'entreprise,
 Cannot create a Delivery Trip from Draft documents.,Impossible de créer un voyage de livraison à partir de documents brouillons.,
-Cannot deactivate or cancel BOM as it is linked with other BOMs,Désactivation ou annulation de la LDM impossible car elle est liée avec d'autres LDMs,
+Cannot deactivate or cancel BOM as it is linked with other BOMs,Désactivation ou annulation de la nomenclature impossible car elle est liée avec d'autres nomenclatures,
 "Cannot declare as lost, because Quotation has been made.","Impossible de déclarer comme perdu, parce que le Devis a été fait.",
 Cannot deduct when category is for 'Valuation' or 'Valuation and Total',Déduction impossible lorsque la catégorie est pour 'Évaluation' ou 'Vaulation et Total',
 Cannot deduct when category is for 'Valuation' or 'Vaulation and Total',Vous ne pouvez pas déduire lorsqu'une catégorie est pour 'Évaluation' ou 'Évaluation et Total',
@@ -722,7 +722,7 @@
 Currency should be same as Price List Currency: {0},La devise doit être la même que la devise de la liste de prix: {0},
 Current,Actuel,
 Current Assets,Actifs Actuels,
-Current BOM and New BOM can not be same,La LDM actuelle et la nouvelle LDM ne peuvent être pareilles,
+Current BOM and New BOM can not be same,La nomenclature actuelle et la nouvelle nomenclature ne peuvent être pareilles,
 Current Job Openings,Offres d'Emploi Actuelles,
 Current Liabilities,Dettes Actuelles,
 Current Qty,Qté actuelle,
@@ -780,9 +780,9 @@
 Declare Lost,Déclarer perdu,
 Deduction,Déduction,
 Default Activity Cost exists for Activity Type - {0},Un Coût d’Activité par défault existe pour le Type d’Activité {0},
-Default BOM ({0}) must be active for this item or its template,LDM par défaut ({0}) doit être actif pour ce produit ou son modèle,
-Default BOM for {0} not found,LDM par défaut {0} introuvable,
-Default BOM not found for Item {0} and Project {1},La LDM par défaut n'a pas été trouvée pour l'Article {0} et le Projet {1},
+Default BOM ({0}) must be active for this item or its template,Nomenclature par défaut ({0}) doit être actif pour ce produit ou son modèle,
+Default BOM for {0} not found,Nomenclature par défaut {0} introuvable,
+Default BOM not found for Item {0} and Project {1},La nomenclature par défaut n'a pas été trouvée pour l'Article {0} et le Projet {1},
 Default Letter Head,En-Tête de Courrier par Défaut,
 Default Tax Template,Modèle de Taxes par Défaut,
 Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM.,L’Unité de Mesure par Défaut pour l’Article {0} ne peut pas être modifiée directement parce que vous avez déjà fait une (des) transaction (s) avec une autre unité de mesure. Vous devez créer un nouvel article pour utiliser une UDM par défaut différente.,
@@ -951,14 +951,14 @@
 Ends On date cannot be before Next Contact Date.,La date de fin ne peut pas être avant la prochaine date de contact,
 Energy,Énergie,
 Engineer,Ingénieur,
-Enough Parts to Build,Pièces Suffisantes pour Construire,
+Enough Parts to Build,Pièces Suffisantes pour Construire
 Enroll,Inscrire,
 Enrolling student,Inscrire un étudiant,
 Enrolling students,Inscription des étudiants,
 Enter depreciation details,Veuillez entrer les détails de l'amortissement,
-Enter the Bank Guarantee Number before submittting.,Entrez le numéro de garantie bancaire avant de soumettre.,
-Enter the name of the Beneficiary before submittting.,Entrez le nom du bénéficiaire avant de soumettre.,
-Enter the name of the bank or lending institution before submittting.,Entrez le nom de la banque ou de l'institution de prêt avant de soumettre.,
+Enter the Bank Guarantee Number before submittting.,Entrez le numéro de garantie bancaire avant de valider.
+Enter the name of the Beneficiary before submittting.,Entrez le nom du bénéficiaire avant de valider.
+Enter the name of the bank or lending institution before submittting.,Entrez le nom de la banque ou de l'institution de prêt avant de valider.,
 Enter value betweeen {0} and {1},Entrez une valeur entre {0} et {1},
 Entertainment & Leisure,Divertissement et Loisir,
 Entertainment Expenses,Charges de Représentation,
@@ -1023,7 +1023,7 @@
 Female,Féminin,
 Fetch Data,Récupérer des données,
 Fetch Subscription Updates,Vérifier les mises à jour des abonnements,
-Fetch exploded BOM (including sub-assemblies),Récupérer la LDM éclatée (y compris les sous-ensembles),
+Fetch exploded BOM (including sub-assemblies),Récupérer la nomenclature éclatée (y compris les sous-ensembles),
 Fetching records......,Récupération des enregistrements ......,
 Field Name,Nom du Champ,
 Fieldname,Nom du Champ,
@@ -1068,7 +1068,7 @@
 For Quantity (Manufactured Qty) is mandatory,Pour Quantité (Qté Produite) est obligatoire,
 For Supplier,Pour Fournisseur,
 For Warehouse,Pour l’Entrepôt,
-For Warehouse is required before Submit,Pour l’Entrepôt est requis avant de Soumettre,
+For Warehouse is required before Submit,Pour l’Entrepôt est requis avant de Valider,
 "For an item {0}, quantity must be negative number","Pour l'article {0}, la quantité doit être un nombre négatif",
 "For an item {0}, quantity must be positive number","Pour un article {0}, la quantité doit être un nombre positif",
 "For job card {0}, you can only make the 'Material Transfer for Manufacture' type stock entry","Pour la carte de travail {0}, vous pouvez uniquement saisir une entrée de stock de type &quot;Transfert d'article pour fabrication&quot;.",
@@ -1135,7 +1135,7 @@
 Get Invocies,Obtenir des invocies,
 Get Invoices,Obtenir des factures,
 Get Invoices based on Filters,Obtenir les factures en fonction des filtres,
-Get Items from BOM,Obtenir les Articles depuis LDM,
+Get Items from BOM,Obtenir les Articles depuis nomenclature,
 Get Items from Healthcare Services,Obtenir des articles des services de santé,
 Get Items from Prescriptions,Obtenir des articles des prescriptions,
 Get Items from Product Bundle,Obtenir les Articles du Produit Groupé,
@@ -1211,7 +1211,7 @@
 Help Results for,Aide Résultats pour,
 High,Haut,
 High Sensitivity,Haute sensibilité,
-Hold,Tenir,
+Hold,Mettre en attente,
 Hold Invoice,Facture en attente,
 Holiday,Vacances,
 Holiday List,Liste de vacances,
@@ -1352,11 +1352,11 @@
 Item Group,Groupe d'Article,
 Item Group Tree,Arborescence de Groupe d'Article,
 Item Group not mentioned in item master for item {0},Le Groupe d'Articles n'est pas mentionné dans la fiche de l'article pour l'article {0},
-Item Name,Nom de l&#39;article,
+Item Name,Nom de l'article,
 Item Price added for {0} in Price List {1},Prix de l'Article ajouté pour {0} dans la Liste de Prix {1},
-"Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty and Dates.","Le prix de l&#39;article apparaît plusieurs fois en fonction de la liste de prix, du fournisseur / client, de la devise, de l&#39;article, de l&#39;unité de mesure, de la quantité et des dates.",
+"Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty and Dates.","Le prix de l'article apparaît plusieurs fois en fonction de la liste de prix, du fournisseur / client, de la devise, de l'article, de l'unité de mesure, de la quantité et des dates.",
 Item Price updated for {0} in Price List {1},Prix de l'Article mis à jour pour {0} dans la Liste des Prix {1},
-Item Row {0}: {1} {2} does not exist in above '{1}' table,Ligne d&#39;objet {0}: {1} {2} n&#39;existe pas dans la table &#39;{1}&#39; ci-dessus,
+Item Row {0}: {1} {2} does not exist in above '{1}' table,Ligne d'objet {0}: {1} {2} n'existe pas dans la table '{1}' ci-dessus,
 Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable,La Ligne de Taxe d'Article {0} doit indiquer un compte de type Taxes ou Produit ou Charge ou Facturable,
 Item Template,Modèle d'article,
 Item Variant Settings,Paramètres de Variante d'Article,
@@ -1425,8 +1425,8 @@
 Last Purchase Price,Dernier prix d'achat,
 Last Purchase Rate,Dernier Prix d'Achat,
 Latest,Dernier,
-Latest price updated in all BOMs,Prix les plus récents mis à jour dans toutes les LDMs,
-Lead,Conduire,
+Latest price updated in all BOMs,Prix les plus récents mis à jour dans toutes les nomenclatures,
+Lead,Prospect,
 Lead Count,Nombre de Prospects,
 Lead Owner,Responsable du Prospect,
 Lead Owner cannot be same as the Lead,Le Responsable du Prospect ne peut pas être identique au Prospect,
@@ -1655,7 +1655,7 @@
 Net pay cannot be negative,Salaire Net ne peut pas être négatif,
 New Account Name,Nouveau Nom de Compte,
 New Address,Nouvelle adresse,
-New BOM,Nouvelle LDM,
+New BOM,Nouvelle nomenclature,
 New Batch ID (Optional),Nouveau Numéro de Lot (Optionnel),
 New Batch Qty,Nouvelle Qté de Lot,
 New Company,Nouvelle Société,
@@ -1689,11 +1689,11 @@
 No Items available for transfer,Aucun article disponible pour le transfert,
 No Items selected for transfer,Aucun article sélectionné pour le transfert,
 No Items to pack,Pas d’Articles à emballer,
-No Items with Bill of Materials to Manufacture,Aucun Article avec une Liste de Matériel à Produire,
+No Items with Bill of Materials to Manufacture,Aucun Article avec une nomenclature à Produire,
 No Items with Bill of Materials.,Aucun article avec nomenclature.,
 No Permission,Aucune autorisation,
 No Remarks,Aucune Remarque,
-No Result to submit,Aucun résultat à soumettre,
+No Result to submit,Aucun résultat à valider,
 No Salary Structure assigned for Employee {0} on given date {1},Aucune structure de salaire attribuée à l&#39;employé {0} à la date donnée {1},
 No Staffing Plans found for this Designation,Aucun plan de dotation trouvé pour cette désignation,
 No Student Groups created.,Aucun Groupe d'Étudiants créé.,
@@ -1777,7 +1777,7 @@
 Only Leave Applications with status 'Approved' and 'Rejected' can be submitted,Seules les Demandes de Congés avec le statut 'Appouvée' ou 'Rejetée' peuvent être soumises,
 "Only the Student Applicant with the status ""Approved"" will be selected in the table below.",Seul les candidatures étudiantes avec le statut «Approuvé» seront sélectionnées dans le tableau ci-dessous.,
 Only users with {0} role can register on Marketplace,Seuls les utilisateurs ayant le rôle {0} peuvent s'inscrire sur Marketplace,
-Open BOM {0},Ouvrir LDM {0},
+Open BOM {0},Ouvrir nomenclature {0},
 Open Item {0},Ouvrir l'Article {0},
 Open Notifications,Notifications ouvertes,
 Open Orders,Commandes ouvertes,
@@ -2015,9 +2015,9 @@
 Please save the report again to rebuild or update,Veuillez enregistrer le rapport à nouveau pour reconstruire ou mettre à jour,
 "Please select Allocated Amount, Invoice Type and Invoice Number in atleast one row","Veuillez sélectionner le Montant Alloué, le Type de Facture et le Numéro de Facture dans au moins une ligne",
 Please select Apply Discount On,Veuillez sélectionnez Appliquer Remise Sur,
-Please select BOM against item {0},Veuillez sélectionner la liste de matériaux (LDM) pour l'article {0},
-Please select BOM for Item in Row {0},Veuillez sélectionnez une LDM pour l’Article à la Ligne {0},
-Please select BOM in BOM field for Item {0},Veuillez sélectionner une LDM dans le champ LDM pour l’Article {0},
+Please select BOM against item {0},Veuillez sélectionner la nomenclature pour l'article {0},
+Please select BOM for Item in Row {0},Veuillez sélectionnez une nomenclature pour l’Article à la Ligne {0},
+Please select BOM in BOM field for Item {0},Veuillez sélectionner une nomenclature dans le champ nomenclature pour l’Article {0},
 Please select Category first,Veuillez d’abord sélectionner une Catégorie,
 Please select Charge Type first,Veuillez d’abord sélectionner le Type de Facturation,
 Please select Company,Veuillez sélectionner une Société,
@@ -2044,7 +2044,7 @@
 Please select Sample Retention Warehouse in Stock Settings first,Veuillez d'abord définir un entrepôt de stockage des échantillons dans les paramètres de stock,
 Please select Start Date and End Date for Item {0},Veuillez sélectionner la Date de Début et Date de Fin pour l'Article {0},
 Please select Student Admission which is mandatory for the paid student applicant,Veuillez sélectionner obligatoirement une Admission d'Étudiant pour la candidature étudiante payée,
-Please select a BOM,Veuillez sélectionner une LDM,
+Please select a BOM,Veuillez sélectionner une nomenclature,
 Please select a Batch for Item {0}. Unable to find a single batch that fulfills this requirement,Veuillez sélectionner un Lot pour l'Article {0}. Impossible de trouver un seul lot satisfaisant à cette exigence,
 Please select a Company,Veuillez sélectionner une Société,
 Please select a batch,Veuillez sélectionner un lot,
@@ -2273,8 +2273,8 @@
 Quantity to Produce,Quantité à produire,
 Quantity to Produce can not be less than Zero,La quantité à produire ne peut être inférieure à zéro,
 Query Options,Options de Requête,
-Queued for replacing the BOM. It may take a few minutes.,En file d'attente pour remplacer la LDM. Cela peut prendre quelques minutes.,
-Queued for updating latest price in all Bill of Materials. It may take a few minutes.,Mise à jour des prix les plus récents dans toutes les Listes de Matériaux en file d'attente. Cela peut prendre quelques minutes.,
+Queued for replacing the BOM. It may take a few minutes.,En file d'attente pour remplacer la nomenclature. Cela peut prendre quelques minutes.,
+Queued for updating latest price in all Bill of Materials. It may take a few minutes.,Mise à jour des prix les plus récents dans toutes les nomenclatures en file d'attente. Cela peut prendre quelques minutes.,
 Quick Journal Entry,Écriture Rapide dans le Journal,
 Quot Count,Compte de Devis,
 Quot/Lead %,Devis / Prospects %,
@@ -2354,7 +2354,7 @@
 Reorder Qty,Qté de Réapprovisionnement,
 Repeat Customer Revenue,Revenus de Clients Récurrents,
 Repeat Customers,Clients Récurrents,
-Replace BOM and update latest price in all BOMs,Remplacer la LDM et actualiser les prix les plus récents dans toutes les LDMs,
+Replace BOM and update latest price in all BOMs,Remplacer la nomenclature et actualiser les prix les plus récents dans toutes les nomenclatures,
 Replied,Répondu,
 Replies,réponses,
 Report,Rapport,
@@ -2466,11 +2466,11 @@
 Row {0}: Allocated amount {1} must be less than or equals to Payment Entry amount {2},Ligne {0} : Le montant alloué {1} doit être inférieur ou égal au montant du Paiement {2},
 Row {0}: Allocated amount {1} must be less than or equals to invoice outstanding amount {2},Ligne {0} : Le montant alloué {1} doit être inférieur ou égal au montant restant sur la Facture {2},
 Row {0}: An Reorder entry already exists for this warehouse {1},Ligne {0} : Une écriture de Réapprovisionnement existe déjà pour cet entrepôt {1},
-Row {0}: Bill of Materials not found for the Item {1},Ligne {0} : Liste de Matériaux non trouvée pour l’Article {1},
+Row {0}: Bill of Materials not found for the Item {1},Ligne {0} : Nomenclature non trouvée pour l’Article {1},
 Row {0}: Conversion Factor is mandatory,Ligne {0} : Le Facteur de Conversion est obligatoire,
 Row {0}: Cost center is required for an item {1},Ligne {0}: le Centre de Coûts est requis pour un article {1},
 Row {0}: Credit entry can not be linked with a {1},Ligne {0} : L’Écriture de crédit ne peut pas être liée à un {1},
-Row {0}: Currency of the BOM #{1} should be equal to the selected currency {2},Ligne {0} : La devise de la LDM #{1} doit être égale à la devise sélectionnée {2},
+Row {0}: Currency of the BOM #{1} should be equal to the selected currency {2},Ligne {0} : La devise de la nomenclature #{1} doit être égale à la devise sélectionnée {2},
 Row {0}: Debit entry can not be linked with a {1},Ligne {0} : L’Écriture de Débit ne peut pas être lié à un {1},
 Row {0}: Depreciation Start Date is required,Ligne {0}: la date de début de l'amortissement est obligatoire,
 Row {0}: Enter location for the asset item {1},Ligne {0}: entrez la localisation de l'actif {1},
@@ -2490,7 +2490,7 @@
 Row {0}: Please set the correct code on Mode of Payment {1},Ligne {0}: définissez le code correct sur le mode de paiement {1}.,
 Row {0}: Qty is mandatory,Ligne {0} : Qté obligatoire,
 Row {0}: Quality Inspection rejected for item {1},Ligne {0}: le contrôle qualité a été rejeté pour l'élément {1}.,
-Row {0}: UOM Conversion Factor is mandatory,Ligne {0} : Facteur de Conversion LDM est obligatoire,
+Row {0}: UOM Conversion Factor is mandatory,Ligne {0} : Facteur de Conversion nomenclature est obligatoire,
 Row {0}: select the workstation against the operation {1},Ligne {0}: sélectionnez le poste de travail en fonction de l'opération {1},
 Row {0}: {1} Serial numbers required for Item {2}. You have provided {3}.,Ligne {0}: {1} Numéros de série requis pour l'article {2}. Vous en avez fourni {3}.,
 Row {0}: {1} must be greater than 0,Ligne {0}: {1} doit être supérieure à 0,
@@ -2587,8 +2587,8 @@
 Select,Sélectionner,
 Select Alternate Item,Sélectionnez un autre élément,
 Select Attribute Values,Sélectionner les valeurs d'attribut,
-Select BOM,Sélectionner LDM,
-Select BOM and Qty for Production,Sélectionner la LDM et la Qté pour la Production,
+Select BOM,Sélectionner une nomenclature,
+Select BOM and Qty for Production,Sélectionner la nomenclature et la Qté pour la Production,
 "Select BOM, Qty and For Warehouse","Sélectionner une nomenclature, une quantité et un entrepôt",
 Select Batch,Sélectionnez le Lot,
 Select Batch Numbers,Sélectionnez les Numéros de Lot,
@@ -2760,7 +2760,7 @@
 Source and target warehouse must be different,Entrepôt source et destination doivent être différents,
 Source of Funds (Liabilities),Source des Fonds (Passif),
 Source warehouse is mandatory for row {0},Entrepôt source est obligatoire à la ligne {0},
-Specified BOM {0} does not exist for Item {1},La LDM {0} spécifiée n'existe pas pour l'Article {1},
+Specified BOM {0} does not exist for Item {1},La nomenclature {0} spécifiée n'existe pas pour l'Article {1},
 Split,Fractionner,
 Split Batch,Lot Fractionné,
 Split Issue,Diviser le ticket,
@@ -2847,12 +2847,12 @@
 Sub-contracting,Sous-traitant,
 Subcontract,Sous-traiter,
 Subject,Sujet,
-Submit,Soumettre,
-Submit Proof,Soumettre une preuve,
-Submit Salary Slip,Soumettre la Fiche de Paie,
-Submit this Work Order for further processing.,Soumettre cet ordre de travail pour continuer son traitement.,
-Submit this to create the Employee record,Soumettre pour créer la fiche employé,
-Submitting Salary Slips...,Soumission des bulletins de salaire ...,
+Submit,Valider,
+Submit Proof,Valider une preuve,
+Submit Salary Slip,Valider la Fiche de Paie,
+Submit this Work Order for further processing.,Valider cet ordre de travail pour continuer son traitement.,
+Submit this to create the Employee record,Valider pour créer la fiche employé,
+Submitting Salary Slips...,Validation des bulletins de salaire ...,
 Subscription,Abonnement,
 Subscription Management,Gestion des abonnements,
 Subscriptions,Abonnements,
@@ -2888,11 +2888,11 @@
 Supplies made to Unregistered Persons,Fournitures faites à des personnes non inscrites,
 Suppliies made to Composition Taxable Persons,Suppleies à des personnes assujetties à la composition,
 Supply Type,Type d'approvisionnement,
-Support,Soutien,
-Support Analytics,Analyse du Support,
-Support Settings,Paramètres du Support,
-Support Tickets,Billets de Support,
-Support queries from customers.,Demande de support des clients,
+Support,"Assistance/Support",
+Support Analytics,Analyse de l'assistance,
+Support Settings,Paramètres du module Assistance,
+Support Tickets,Ticket d'assistance,
+Support queries from customers.,Demande d'assistance des clients,
 Susceptible,Sensible,
 Sync has been temporarily disabled because maximum retries have been exceeded,La synchronisation a été temporairement désactivée car les tentatives maximales ont été dépassées,
 Syntax error in condition: {0},Erreur de syntaxe dans la condition: {0},
@@ -2954,7 +2954,7 @@
 The Term End Date cannot be later than the Year End Date of the Academic Year to which the term is linked (Academic Year {}). Please correct the dates and try again.,La Date de Fin de Terme ne peut pas être postérieure à la Date de Fin de l'Année Académique à laquelle le terme est lié (Année Académique {}). Veuillez corriger les dates et essayer à nouveau.,
 The Term Start Date cannot be earlier than the Year Start Date of the Academic Year to which the term is linked (Academic Year {}). Please correct the dates and try again.,La Date de Début de Terme ne peut pas être antérieure à la Date de Début de l'Année Académique à laquelle le terme est lié (Année Académique {}). Veuillez corriger les dates et essayer à nouveau.,
 The Year End Date cannot be earlier than the Year Start Date. Please correct the dates and try again.,La Date de Fin d'Année ne peut pas être antérieure à la Date de Début d’Année. Veuillez corriger les dates et essayer à nouveau.,
-The amount of {0} set in this payment request is different from the calculated amount of all payment plans: {1}. Make sure this is correct before submitting the document.,Le montant {0} défini dans cette requête de paiement est différent du montant calculé de tous les plans de paiement: {1}.\nVeuillez vérifier que c'est correct avant de soumettre le document.,
+The amount of {0} set in this payment request is different from the calculated amount of all payment plans: {1}. Make sure this is correct before submitting the document.,Le montant {0} défini dans cette requête de paiement est différent du montant calculé de tous les plans de paiement: {1}.\nVeuillez vérifier que c'est correct avant de valider le document.,
 The day(s) on which you are applying for leave are holidays. You need not apply for leave.,Le(s) jour(s) pour le(s)quel(s) vous demandez un congé sont des jour(s) férié(s). Vous n’avez pas besoin d’effectuer de demande.,
 The field From Shareholder cannot be blank,Le champ 'De l'actionnaire' ne peut pas être vide,
 The field To Shareholder cannot be blank,Le champ 'A l'actionnaire' ne peut pas être vide,
@@ -2965,7 +2965,7 @@
 The name of your company for which you are setting up this system.,Le nom de l'entreprise pour laquelle vous configurez ce système.,
 The number of shares and the share numbers are inconsistent,Le nombre d'actions dans les transactions est incohérent avec le nombre total d'actions,
 The payment gateway account in plan {0} is different from the payment gateway account in this payment request,Le compte passerelle de paiement dans le plan {0} est différent du compte passerelle de paiement dans cette requête de paiement.,
-The selected BOMs are not for the same item,Les LDMs sélectionnées ne sont pas pour le même article,
+The selected BOMs are not for the same item,Les nomenclatures sélectionnées ne sont pas pour le même article,
 The selected item cannot have Batch,L’article sélectionné ne peut pas avoir de Lot,
 The seller and the buyer cannot be the same,Le vendeur et l'acheteur ne peuvent pas être les mêmes,
 The shareholder does not belong to this company,L'actionnaire n'appartient pas à cette société,
@@ -3011,7 +3011,7 @@
 This is based on transactions against this Patient. See timeline below for details,Ceci est basé sur les transactions de ce patient. Voir la chronologie ci-dessous pour plus de détails,
 This is based on transactions against this Sales Person. See timeline below for details,Ceci est basé sur les transactions contre ce vendeur. Voir la chronologie ci-dessous pour plus de détails,
 This is based on transactions against this Supplier. See timeline below for details,Basé sur les transactions avec ce fournisseur. Voir la chronologie ci-dessous pour plus de détails,
-This will submit Salary Slips and create accrual Journal Entry. Do you want to proceed?,Cela permettra de soumettre des bulletins de salaire et de créer une écriture de journal d&#39;accumulation. Voulez-vous poursuivre?,
+This will submit Salary Slips and create accrual Journal Entry. Do you want to proceed?,Cela permettra de valider des bulletins de salaire et de créer une écriture de journal d&#39;accumulation. Voulez-vous poursuivre?,
 This {0} conflicts with {1} for {2} {3},Ce {0} est en conflit avec {1} pour {2} {3},
 Time Sheet for manufacturing.,Feuille de Temps pour la production.,
 Time Tracking,Suivi du temps,
@@ -3037,6 +3037,7 @@
 To Date should be within the Fiscal Year. Assuming To Date = {0},La Date Finale doit être dans l'exercice. En supposant Date Finale = {0},
 To Datetime,À la Date,
 To Deliver,À Livrer,
+{}  To Deliver,{} à livrer
 To Deliver and Bill,À Livrer et Facturer,
 To Fiscal Year,À l'année fiscale,
 To GSTIN,GSTIN (Destination),
@@ -3149,7 +3150,7 @@
 Travel,Déplacement,
 Travel Expenses,Frais de Déplacement,
 Tree Type,Type d'Arbre,
-Tree of Bill of Materials,Arbre des Listes de Matériaux,
+Tree of Bill of Materials,Arbre des Nomenclatures,
 Tree of Item Groups.,Arbre de Groupes d’Articles .,
 Tree of Procedures,Arbre de procédures,
 Tree of Quality Procedures.,Arbre de la qualité des procédures.,
@@ -3304,7 +3305,7 @@
 WooCommerce Products,Produits WooCommerce,
 Work In Progress,Travaux en cours,
 Work Order,Ordre de travail,
-Work Order already created for all items with BOM,Ordre de travail déjà créé pour tous les articles avec une LDM,
+Work Order already created for all items with BOM,Ordre de travail déjà créé pour tous les articles avec une nomenclature,
 Work Order cannot be raised against a Item Template,Un ordre de travail ne peut pas être créé pour un modèle d'article,
 Work Order has been {0},L'ordre de travail a été {0},
 Work Order not created,Ordre de travail non créé,
@@ -3312,7 +3313,7 @@
 Work Order {0} must be submitted,L'ordre de travail {0} doit être soumis,
 Work Orders Created: {0},Ordres de travail créés: {0},
 Work Summary for {0},Résumé de travail de {0},
-Work-in-Progress Warehouse is required before Submit,L'entrepôt des Travaux en Cours est nécessaire avant de Soumettre,
+Work-in-Progress Warehouse is required before Submit,L'entrepôt des Travaux en Cours est nécessaire avant de Valider,
 Workflow,Flux de Travail,
 Working,Travail en cours,
 Working Hours,Heures de travail,
@@ -3325,13 +3326,13 @@
 You are not authorized to approve leaves on Block Dates,Vous n'êtes pas autorisé à approuver les congés sur les Dates Bloquées,
 You are not authorized to set Frozen value,Vous n'êtes pas autorisé à définir des valeurs gelées,
 You are not present all day(s) between compensatory leave request days,Vous n'êtes pas présent(e) tous les jours vos demandes de congé compensatoire,
-You can not change rate if BOM mentioned agianst any item,Vous ne pouvez pas modifier le taux si la LDM est mentionnée pour un article,
+You can not change rate if BOM mentioned agianst any item,Vous ne pouvez pas modifier le taux si la nomenclature est mentionnée pour un article,
 You can not enter current voucher in 'Against Journal Entry' column,Vous ne pouvez pas entrer le bon actuel dans la colonne 'Pour l'Écriture de Journal',
 You can only have Plans with the same billing cycle in a Subscription,Vous ne pouvez avoir que des plans ayant le même cycle de facturation dans le même abonnement,
 You can only redeem max {0} points in this order.,Vous pouvez uniquement échanger un maximum de {0} points dans cet commande.,
 You can only renew if your membership expires within 30 days,Vous ne pouvez renouveler que si votre abonnement expire dans les 30 jours,
 You can only select a maximum of one option from the list of check boxes.,Vous pouvez sélectionner au maximum une option dans la liste des cases à cocher.,
-You can only submit Leave Encashment for a valid encashment amount,Vous pouvez uniquement soumettre un encaissement de congé pour un montant d'encaissement valide,
+You can only submit Leave Encashment for a valid encashment amount,Vous pouvez uniquement valider un encaissement de congé pour un montant d'encaissement valide,
 You can't redeem Loyalty Points having more value than the Grand Total.,Vous ne pouvez pas échanger des points de fidélité ayant plus de valeur que le total général.,
 You cannot credit and debit same account at the same time,Vous ne pouvez pas créditer et débiter le même compte simultanément,
 You cannot delete Fiscal Year {0}. Fiscal Year {0} is set as default in Global Settings,Vous ne pouvez pas supprimer l'exercice fiscal {0}. L'exercice fiscal {0} est défini par défaut dans les Paramètres Globaux,
@@ -3660,7 +3661,7 @@
 Choose a corresponding payment,Choisissez un paiement correspondant,
 Click on the link below to verify your email and confirm the appointment,Cliquez sur le lien ci-dessous pour vérifier votre email et confirmer le rendez-vous,
 Close,Fermer,
-Communication,la communication,
+Communication,Communication,
 Compact Item Print,Impression de l'Article Compacté,
 Company,Société,
 Company of asset {0} and purchase document {1} doesn't matches.,La société de l'actif {0} et le document d'achat {1} ne correspondent pas.,
@@ -3684,8 +3685,8 @@
 Creating Accounts...,Création de comptes ...,
 Creating bank entries...,Création d'entrées bancaires ...,
 Credit limit is already defined for the Company {0},La limite de crédit est déjà définie pour la société {0}.,
-Ctrl + Enter to submit,Ctrl + Entrée pour soumettre,
-Ctrl+Enter to submit,Ctrl + Entrée pour soumettre,
+Ctrl + Enter to submit,Ctrl + Entrée pour valider,
+Ctrl+Enter to submit,Ctrl + Entrée pour valider,
 Currency,Devise,
 Current Status,Statut Actuel,
 Customer PO,Bon de commande client,
@@ -3709,7 +3710,7 @@
 Disabled,Desactivé,
 Disbursement and Repayment,Décaissement et remboursement,
 Distance cannot be greater than 4000 kms,La distance ne peut pas dépasser 4000 km,
-Do you want to submit the material request,Voulez-vous soumettre la demande de matériel,
+Do you want to submit the material request,Voulez-vous valider la demande de matériel,
 Doctype,Doctype,
 Document {0} successfully uncleared,Document {0} non effacé avec succès,
 Download Template,Télécharger le Modèle,
@@ -3968,7 +3969,7 @@
 Quarterly,Trimestriel,
 Queued,File d'Attente,
 Quick Entry,Écriture Rapide,
-Quiz {0} does not exist,Le questionnaire {0} n&#39;existe pas,
+Quiz {0} does not exist,Le questionnaire {0} n'existe pas,
 Quotation Amount,Montant du devis,
 Rate or Discount is required for the price discount.,Le taux ou la remise est requis pour la remise de prix.,
 Reason,Raison,
@@ -4070,7 +4071,7 @@
 Stores - {0},Magasins - {0},
 Student with email {0} does not exist,Étudiant avec le courrier électronique {0} n'existe pas,
 Submit Review,Poster un commentaire,
-Submitted,Soumis,
+Submitted,Valider,
 Supplier Addresses And Contacts,Adresses et contacts des fournisseurs,
 Synchronize this account,Synchroniser ce compte,
 Tag,Étiquette,
@@ -4240,7 +4241,7 @@
 From date cannot be greater than To date,La Date Initiale ne peut pas être postérieure à la Date Finale,
 Group by,Grouper Par,
 In stock,En stock,
-Item name,Nom de l'article,
+Item name,Libellé de l'article,
 Loan amount is mandatory,Le montant du prêt est obligatoire,
 Minimum Qty,Quantité minimum,
 More details,Plus de détails,
@@ -4309,7 +4310,7 @@
 Partially Paid,Partiellement payé,
 Invalid Account Currency,Devise de compte non valide,
 "Row {0}: The item {1}, quantity must be positive number","Ligne {0}: l&#39;article {1}, la quantité doit être un nombre positif",
-"Please set {0} for Batched Item {1}, which is used to set {2} on Submit.","Veuillez définir {0} pour l&#39;article par lots {1}, qui est utilisé pour définir {2} sur Soumettre.",
+"Please set {0} for Batched Item {1}, which is used to set {2} on Submit.","Veuillez définir {0} pour l&#39;article par lots {1}, qui est utilisé pour définir {2} sur Valider.",
 Expiry Date Mandatory,Date d&#39;expiration obligatoire,
 Variant Item,Élément de variante,
 BOM 1 {0} and BOM 2 {1} should not be same,La nomenclature 1 {0} et la nomenclature 2 {1} ne doivent pas être identiques,
@@ -4589,7 +4590,7 @@
 New Transactions,Nouvelles transactions,
 Match Transaction to Invoices,Faire correspondre la transaction aux factures,
 Create New Payment/Journal Entry,Créer un nouveau paiement / écriture de journal,
-Submit/Reconcile Payments,Soumettre / rapprocher les paiements,
+Submit/Reconcile Payments,Valider / rapprocher les paiements,
 Matching Invoices,Factures correspondantes,
 Payment Invoice Items,Articles de la facture de paiement,
 Reconciled Transactions,Transactions rapprochées,
@@ -5473,7 +5474,7 @@
 PUR-ORD-.YYYY.-,PUR-ORD-.YYYY.-,
 Get Items from Open Material Requests,Obtenir des Articles de Demandes Matérielles Ouvertes,
 Fetch items based on Default Supplier.,Récupérez les articles en fonction du fournisseur par défaut.,
-Required By,Requis Par,
+Required By,Requis pour le,
 Order Confirmation No,No de confirmation de commande,
 Order Confirmation Date,Date de confirmation de la commande,
 Customer Mobile No,N° de Portable du Client,
@@ -5501,7 +5502,7 @@
 Blanket Order Rate,Prix unitaire de commande avec limites,
 Returned Qty,Qté Retournée,
 Purchase Order Item Supplied,Article Fourni du Bon de Commande,
-BOM Detail No,N° de Détail LDM,
+BOM Detail No,N° de Détail de la nomenclature,
 Stock Uom,UDM du Stock,
 Raw Material Item Code,Code d’Article de Matière Première,
 Supplied Qty,Qté Fournie,
@@ -5599,7 +5600,6 @@
 Received By,Reçu par,
 Caller Information,Informations sur l&#39;appelant,
 Contact Name,Nom du Contact,
-Lead ,Conduire,
 Lead Name,Nom du Prospect,
 Ringing,Sonnerie,
 Missed,Manqué,
@@ -6208,7 +6208,7 @@
 Checking this will create new Patients with a Disabled status by default and will only be enabled after invoicing the Registration Fee.,Cochez cette case pour créer de nouveaux patients avec un statut Désactivé par défaut et ne seront activés qu&#39;après facturation des frais d&#39;inscription.,
 Registration Fee,Frais d'Inscription,
 Automate Appointment Invoicing,Automatiser la facturation des rendez-vous,
-Manage Appointment Invoice submit and cancel automatically for Patient Encounter,Gérer les factures de rendez-vous soumettre et annuler automatiquement pour la consultation des patients,
+Manage Appointment Invoice submit and cancel automatically for Patient Encounter,Gérer les factures de rendez-vous valider et annuler automatiquement pour la consultation des patients,
 Enable Free Follow-ups,Activer les suivis gratuits,
 Number of Patient Encounters in Valid Days,Nombre de rencontres de patients en jours valides,
 The number of free follow ups (Patient Encounters in valid days) allowed,Le nombre de suivis gratuits (rencontres de patients en jours valides) autorisés,
@@ -7182,7 +7182,7 @@
 Ordered Quantity,Quantité Commandée,
 Item to be manufactured or repacked,Article à produire ou à réemballer,
 Quantity of item obtained after manufacturing / repacking from given quantities of raw materials,Quantité d'article obtenue après production / reconditionnement des quantités données de matières premières,
-Set rate of sub-assembly item based on BOM,Définir le prix des articles de sous-assemblage en fonction de la LDM,
+Set rate of sub-assembly item based on BOM,Définir le prix des articles de sous-assemblage en fonction de la nomenclature,
 Allow Alternative Item,Autoriser un article alternatif,
 Item UOM,UDM de l'Article,
 Conversion Rate,Taux de Conversion,
@@ -7213,33 +7213,33 @@
 Show Items,Afficher les Articles,
 Show Operations,Afficher Opérations,
 Website Description,Description du Site Web,
-BOM Explosion Item,Article Eclaté LDM,
+BOM Explosion Item,Article Eclaté en nomenclature,
 Qty Consumed Per Unit,Qté Consommée Par Unité,
 Include Item In Manufacturing,Inclure l&#39;article dans la fabrication,
-BOM Item,Article LDM,
+BOM Item,Article de la nomenclature,
 Item operation,Opération de l'article,
 Rate & Amount,Taux et Montant,
 Basic Rate (Company Currency),Taux de Base (Devise de la Société ),
 Scrap %,% de Rebut,
 Original Item,Article original,
-BOM Operation,Opération LDM,
-Operation Time ,Moment de l&#39;opération,
-In minutes,En quelques minutes,
+BOM Operation,Opération de la nomenclature (gamme),
+Operation Time ,Durée de l&#39;opération,
+In minutes,En minutes,
 Batch Size,Taille du lot,
 Base Hour Rate(Company Currency),Taux Horaire de Base (Devise de la Société),
 Operating Cost(Company Currency),Coût d'Exploitation (Devise Société),
-BOM Scrap Item,Article Mis au Rebut LDM,
+BOM Scrap Item,Article Mis au Rebut dans la nomenclature,
 Basic Amount (Company Currency),Montant de Base (Devise de la Société),
-BOM Update Tool,Outil de mise à jour de LDM,
-"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate ""BOM Explosion Item"" table as per new BOM.\nIt also updates latest price in all the BOMs.","Remplacez une LDM particulière dans toutes les LDM où elles est utilisée. Cela remplacera le lien vers l'ancienne LDM, mettra à jour les coûts et régénérera le tableau ""Article Explosé de LDM"" selon la nouvelle LDM. Cela mettra également à jour les prix les plus récents dans toutes les LDMs.",
-Replace BOM,Remplacer la LDM,
-Current BOM,LDM Actuelle,
-The BOM which will be replaced,La LDM qui sera remplacée,
-The new BOM after replacement,La nouvelle LDM après remplacement,
+BOM Update Tool,Outil de mise à jour des Nomenclatures,
+"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate ""BOM Explosion Item"" table as per new BOM.\nIt also updates latest price in all the BOMs.","Remplacez une nomenclature particulière dans toutes les nomenclatures où elles est utilisée. Cela remplacera le lien vers l'ancienne nomenclature, mettra à jour les coûts et régénérera le tableau ""Article Explosé de nomenclature"" selon la nouvelle nomenclature. Cela mettra également à jour les prix les plus récents dans toutes les nomenclatures.",
+Replace BOM,Remplacer la nomenclature,
+Current BOM,nomenclature Actuelle,
+The BOM which will be replaced,La nomenclature qui sera remplacée,
+The new BOM after replacement,La nouvelle nomenclature après remplacement,
 Replace,Remplacer,
-Update latest price in all BOMs,Mettre à jour le prix le plus récent dans toutes les LDMs,
-BOM Website Item,Article de LDM du Site Internet,
-BOM Website Operation,Opération de LDM du Site Internet,
+Update latest price in all BOMs,Mettre à jour le prix le plus récent dans toutes les nomenclatures,
+BOM Website Item,Article de nomenclature du Site Internet,
+BOM Website Operation,Opération de nomenclature du Site Internet,
 Operation Time,Heure de l'Opération,
 PO-JOB.#####,PO-JOB. #####,
 Timing Detail,Détail du timing,
@@ -7271,7 +7271,7 @@
 Overproduction Percentage For Sales Order,Pourcentage de surproduction pour les commandes client,
 Overproduction Percentage For Work Order,Pourcentage de surproduction pour les ordres de travail,
 Other Settings,Autres Paramètres,
-Update BOM Cost Automatically,Mettre à jour automatiquement le coût de la LDM,
+Update BOM Cost Automatically,Mettre à jour automatiquement le coût de la nomenclature,
 Material Request Plan Item,Article du plan de demande de matériel,
 Material Request Type,Type de Demande de Matériel,
 Material Issue,Sortie de Matériel,
@@ -7311,7 +7311,7 @@
 Item To Manufacture,Article à produire,
 Material Transferred for Manufacturing,Matériel Transféré pour la Production,
 Manufactured Qty,Qté Produite,
-Use Multi-Level BOM,Utiliser LDM à Plusieurs Niveaux,
+Use Multi-Level BOM,Utiliser les nomenclatures à plusieurs niveaux,
 Plan material for sub-assemblies,Plan de matériaux pour les sous-ensembles,
 Skip Material Transfer to WIP Warehouse,Ignorer le transfert de matériel vers l'entrepôt WIP,
 Check if material transfer entry is not required,Vérifiez si une un transfert de matériel n'est pas requis,
@@ -7684,7 +7684,7 @@
 Expected Amount,Montant prévu,
 POS Closing Voucher Invoices,Factures du bon de clôture du PDV,
 Quantity of Items,Quantité d'articles,
-"Aggregate group of **Items** into another **Item**. This is useful if you are bundling a certain **Items** into a package and you maintain stock of the packed **Items** and not the aggregate **Item**. \n\nThe package **Item** will have ""Is Stock Item"" as ""No"" and ""Is Sales Item"" as ""Yes"".\n\nFor Example: If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item.\n\nNote: BOM = Bill of Materials","Regroupement d' **Articles** dans un autre **Article**. Ceci est utile si vous regroupez certains **Articles** dans un lot et que vous maintenez l'inventaire des **Articles** du lot et non de l'**Article** composé. L'**Article** composé aura ""Article En Stock"" à ""Non"" et ""Article À Vendre"" à ""Oui"". Exemple : Si vous vendez des Ordinateurs Portables et Sacs à Dos séparément et qu'il y a un prix spécial si le client achète les deux, alors l'Ordinateur Portable + le Sac à Dos sera un nouveau Produit Groupé. Remarque: LDM = Liste\nDes Matériaux",
+"Aggregate group of **Items** into another **Item**. This is useful if you are bundling a certain **Items** into a package and you maintain stock of the packed **Items** and not the aggregate **Item**. \n\nThe package **Item** will have ""Is Stock Item"" as ""No"" and ""Is Sales Item"" as ""Yes"".\n\nFor Example: If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item.\n\nNote: BOM = Bill of Materials","Regroupement d' **Articles** dans un autre **Article**. Ceci est utile si vous regroupez certains **Articles** dans un lot et que vous maintenez l'inventaire des **Articles** du lot et non de l'**Article** composé. L'**Article** composé aura ""Article En Stock"" à ""Non"" et ""Article À Vendre"" à ""Oui"". Exemple : Si vous vendez des Ordinateurs Portables et Sacs à Dos séparément et qu'il y a un prix spécial si le client achète les deux, alors l'Ordinateur Portable + le Sac à Dos sera un nouveau Produit Groupé.",
 Parent Item,Article Parent,
 List items that form the package.,Liste des articles qui composent le paquet.,
 SAL-QTN-.YYYY.-,SAL-QTN-. AAAA.-,
@@ -8088,7 +8088,7 @@
 Inspection Criteria,Critères d'Inspection,
 Inspection Required before Purchase,Inspection Requise avant Achat,
 Inspection Required before Delivery,Inspection Requise avant Livraison,
-Default BOM,LDM par Défaut,
+Default BOM,Nomenclature par Défaut,
 Supply Raw Materials for Purchase,Fournir les Matières Premières pour l'Achat,
 If subcontracted to a vendor,Si sous-traité à un fournisseur,
 Customer Code,Code Client,
@@ -8294,7 +8294,7 @@
 Sales Invoice No,N° de la Facture de Vente,
 Purchase Receipt No,N° du Reçu d'Achat,
 Inspection Required,Inspection obligatoire,
-From BOM,De LDM,
+From BOM,Depuis la nomenclature,
 For Quantity,Pour la Quantité,
 As per Stock UOM,Selon UDM du Stock,
 Including items for sub assemblies,Incluant les articles pour des sous-ensembles,
@@ -8315,7 +8315,7 @@
 Basic Amount,Montant de Base,
 Additional Cost,Frais Supplémentaire,
 Serial No / Batch,N° de Série / Lot,
-BOM No. for a Finished Good Item,N° d’Article Produit Fini LDM,
+BOM No. for a Finished Good Item,N° de nomenclature pour un d’Article (Produit Fini),
 Material Request used to make this Stock Entry,Demande de Matériel utilisée pour réaliser cette Écriture de Stock,
 Subcontracted Item,Article sous-traité,
 Against Stock Entry,Contre entrée de stock,
@@ -8455,9 +8455,9 @@
 Batch Item Expiry Status,Statut d'Expiration d'Article du Lot,
 Batch-Wise Balance History,Historique de Balance des Lots,
 BOM Explorer,Explorateur de nomenclature,
-BOM Search,Recherche LDM,
-BOM Stock Calculated,Stock calculé par liste de matériaux (LDM),
-BOM Variance Report,Rapport de variance par liste de matériaux (LDM),
+BOM Search,Recherche nomenclature,
+BOM Stock Calculated,Stock calculé par nomenclature,
+BOM Variance Report,Rapport de variance par nomenclature,
 Campaign Efficiency,Efficacité des Campagnes,
 Cash Flow,Flux de Trésorerie,
 Completed Work Orders,Ordres de travail terminés,
@@ -8679,7 +8679,7 @@
 Days,Journées,
 Months,Mois,
 Book Deferred Entries Via Journal Entry,Enregistrer les écritures différées via l&#39;écriture au journal,
-Submit Journal Entries,Soumettre les entrées de journal,
+Submit Journal Entries,Valider les entrées de journal,
 If this is unchecked Journal Entries will be saved in a Draft state and will have to be submitted manually,"Si cette case n&#39;est pas cochée, les entrées de journal seront enregistrées dans un état Brouillon et devront être soumises manuellement",
 Enable Distributed Cost Center,Activer le centre de coûts distribués,
 Distributed Cost Center,Centre de coûts distribués,
@@ -9065,7 +9065,7 @@
 Monthly Eligible Amount,Montant mensuel admissible,
 Total Eligible HRA Exemption,Exemption HRA totale éligible,
 Validating Employee Attendance...,Validation de la présence des employés ...,
-Submitting Salary Slips and creating Journal Entry...,Soumettre des fiches de salaire et créer une écriture au journal ...,
+Submitting Salary Slips and creating Journal Entry...,Validation des fiches de salaire et créer une écriture au journal ...,
 Calculate Payroll Working Days Based On,Calculer les jours ouvrables de paie en fonction de,
 Consider Unmarked Attendance As,Considérez la participation non marquée comme,
 Fraction of Daily Salary for Half Day,Fraction du salaire journalier pour une demi-journée,
@@ -9166,8 +9166,8 @@
 Customer contact updated successfully.,Contact client mis à jour avec succès.,
 Item will be removed since no serial / batch no selected.,L&#39;article sera supprimé car aucun numéro de série / lot sélectionné.,
 Discount (%),Remise (%),
-You cannot submit the order without payment.,Vous ne pouvez pas soumettre la commande sans paiement.,
-You cannot submit empty order.,Vous ne pouvez pas soumettre de commande vide.,
+You cannot submit the order without payment.,Vous ne pouvez pas valider la commande sans paiement.,
+You cannot submit empty order.,Vous ne pouvez pas valider de commande vide.,
 To Be Paid,Être payé,
 Create POS Opening Entry,Créer une entrée d&#39;ouverture de PDV,
 Please add Mode of payments and opening balance details.,Veuillez ajouter le mode de paiement et les détails du solde d&#39;ouverture.,
@@ -9267,7 +9267,7 @@
 Amount Delivered,Montant livré,
 Delay (in Days),Retard (en jours),
 Group by Sales Order,Regrouper par commande client,
- Sales Value,La valeur des ventes,
+Sales Value,La valeur des ventes,
 Stock Qty vs Serial No Count,Quantité de stock vs numéro de série,
 Serial No Count,Numéro de série,
 Work Order Summary,Résumé de l&#39;ordre de travail,
@@ -9305,7 +9305,7 @@
 {0} {1} has been added to all the selected topics successfully.,{0} {1} a bien été ajouté à tous les sujets sélectionnés.,
 Topics updated,Sujets mis à jour,
 Academic Term and Program,Terme académique et programme,
-Please remove this item and try to submit again or update the posting time.,Veuillez supprimer cet élément et réessayer de le soumettre ou mettre à jour l&#39;heure de publication.,
+Please remove this item and try to submit again or update the posting time.,Veuillez supprimer cet élément et réessayer de le valider ou mettre à jour l&#39;heure de publication.,
 Failed to Authenticate the API key.,Échec de l&#39;authentification de la clé API.,
 Invalid Credentials,Les informations d&#39;identification invalides,
 URL can only be a string,L&#39;URL ne peut être qu&#39;une chaîne,
@@ -9416,7 +9416,7 @@
 "Valuation Rate for the Item {0}, is required to do accounting entries for {1} {2}.",Le taux de valorisation de l&#39;article {0} est requis pour effectuer des écritures comptables pour {1} {2}.,
  Here are the options to proceed:,Voici les options pour continuer:,
 "If the item is transacting as a Zero Valuation Rate item in this entry, please enable 'Allow Zero Valuation Rate' in the {0} Item table.","Si l&#39;article est traité comme un article à taux de valorisation nul dans cette entrée, veuillez activer &quot;Autoriser le taux de valorisation nul&quot; dans le {0} tableau des articles.",
-"If not, you can Cancel / Submit this entry ","Sinon, vous pouvez annuler / soumettre cette entrée",
+"If not, you can Cancel / Submit this entry ","Sinon, vous pouvez annuler / valider cette entrée",
  performing either one below:,effectuer l&#39;un ou l&#39;autre ci-dessous:,
 Create an incoming stock transaction for the Item.,Créez une transaction de stock entrante pour l&#39;article.,
 Mention Valuation Rate in the Item master.,Mentionnez le taux de valorisation dans la fiche article.,
@@ -9573,7 +9573,7 @@
 Role Allowed to Set Frozen Accounts and Edit Frozen Entries,Rôle autorisé à définir des comptes gelés et à modifier les entrées gelées,
 Address used to determine Tax Category in transactions,Adresse utilisée pour déterminer la catégorie de taxe dans les transactions,
 "The percentage you are allowed to bill more against the amount ordered. For example, if the order value is $100 for an item and tolerance is set as 10%, then you are allowed to bill up to $110 ","Le pourcentage que vous êtes autorisé à facturer davantage par rapport au montant commandé. Par exemple, si la valeur de la commande est de 100 USD pour un article et que la tolérance est définie sur 10%, vous êtes autorisé à facturer jusqu&#39;à 110 USD.",
-This role is allowed to submit transactions that exceed credit limits,Ce rôle est autorisé à soumettre des transactions qui dépassent les limites de crédit,
+This role is allowed to submit transactions that exceed credit limits,Ce rôle est autorisé à valider des transactions qui dépassent les limites de crédit,
 "If ""Months"" is selected, a fixed amount will be booked as deferred revenue or expense for each month irrespective of the number of days in a month. It will be prorated if deferred revenue or expense is not booked for an entire month","Si «Mois» est sélectionné, un montant fixe sera comptabilisé en tant que revenus ou dépenses différés pour chaque mois, quel que soit le nombre de jours dans un mois. Il sera calculé au prorata si les revenus ou les dépenses différés ne sont pas comptabilisés pour un mois entier",
 "If this is unchecked, direct GL entries will be created to book deferred revenue or expense","Si cette case n&#39;est pas cochée, des entrées GL directes seront créées pour enregistrer les revenus ou les dépenses différés",
 Show Inclusive Tax in Print,Afficher la taxe incluse en version imprimée,
@@ -9647,7 +9647,7 @@
 Validate Selling Price for Item Against Purchase Rate or Valuation Rate,Valider le prix de vente de l&#39;article par rapport au taux d&#39;achat ou au taux de valorisation,
 Hide Customer's Tax ID from Sales Transactions,Masquer le numéro d&#39;identification fiscale du client dans les transactions de vente,
 "The percentage you are allowed to receive or deliver more against the quantity ordered. For example, if you have ordered 100 units, and your Allowance is 10%, then you are allowed to receive 110 units.","Le pourcentage que vous êtes autorisé à recevoir ou à livrer plus par rapport à la quantité commandée. Par exemple, si vous avez commandé 100 unités et que votre allocation est de 10%, vous êtes autorisé à recevoir 110 unités.",
-Action If Quality Inspection Is Not Submitted,Action si l&#39;inspection de la qualité n&#39;est pas soumise,
+Action If Quality Inspection Is Not Submitted,Action si l&#39;inspection qualité n&#39;est pas soumise,
 Auto Insert Price List Rate If Missing,Taux de liste de prix d&#39;insertion automatique s&#39;il est manquant,
 Automatically Set Serial Nos Based on FIFO,Définir automatiquement les numéros de série en fonction de FIFO,
 Set Qty in Transactions Based on Serial No Input,Définir la quantité dans les transactions en fonction du numéro de série,
@@ -9744,7 +9744,7 @@
 Edit Receipt,Modifier le reçu,
 Focus on search input,Focus sur l&#39;entrée de recherche,
 Focus on Item Group filter,Focus sur le filtre de groupe d&#39;articles,
-Checkout Order / Submit Order / New Order,Commander la commande / Soumettre la commande / Nouvelle commande,
+Checkout Order / Submit Order / New Order,Commander la commande / Valider la commande / Nouvelle commande,
 Add Order Discount,Ajouter une remise de commande,
 Item Code: {0} is not available under warehouse {1}.,Code d&#39;article: {0} n&#39;est pas disponible dans l&#39;entrepôt {1}.,
 Serial numbers unavailable for Item {0} under warehouse {1}. Please try changing warehouse.,Numéros de série non disponibles pour l&#39;article {0} sous l&#39;entrepôt {1}. Veuillez essayer de changer d’entrepôt.,
@@ -9787,11 +9787,11 @@
 as no Purchase Receipt is created against Item {}. ,car aucun reçu d&#39;achat n&#39;est créé pour l&#39;article {}.,
 This is done to handle accounting for cases when Purchase Receipt is created after Purchase Invoice,Ceci est fait pour gérer la comptabilité des cas où le reçu d&#39;achat est créé après la facture d&#39;achat,
 Purchase Order Required for item {},Bon de commande requis pour l&#39;article {},
-To submit the invoice without purchase order please set {} ,"Pour soumettre la facture sans bon de commande, veuillez définir {}",
+To submit the invoice without purchase order please set {} ,"Pour valider la facture sans bon de commande, veuillez définir {}",
 as {} in {},un péché {},
 Mandatory Purchase Order,Bon de commande obligatoire,
 Purchase Receipt Required for item {},Reçu d&#39;achat requis pour l&#39;article {},
-To submit the invoice without purchase receipt please set {} ,"Pour soumettre la facture sans reçu d&#39;achat, veuillez définir {}",
+To submit the invoice without purchase receipt please set {} ,"Pour valider la facture sans reçu d&#39;achat, veuillez définir {}",
 Mandatory Purchase Receipt,Reçu d&#39;achat obligatoire,
 POS Profile {} does not belongs to company {},Le profil PDV {} n&#39;appartient pas à l&#39;entreprise {},
 User {} is disabled. Please select valid user/cashier,L&#39;utilisateur {} est désactivé. Veuillez sélectionner un utilisateur / caissier valide,
@@ -9838,3 +9838,75 @@
 Creating Purchase Order ...,Création d&#39;une commande d&#39;achat ...,
 "Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Sélectionnez un fournisseur parmi les fournisseurs par défaut des articles ci-dessous. Lors de la sélection, un bon de commande sera effectué contre des articles appartenant uniquement au fournisseur sélectionné.",
 Row #{}: You must select {} serial numbers for item {}.,Ligne n ° {}: vous devez sélectionner {} numéros de série pour l&#39;article {}.,
+Update Rate as per Last Purchase,Mettre à jour avec les derniers prix d'achats
+Company Shipping Address,Adresse d&#39;expédition
+Shipping Address Details,Détail d&#39;adresse d&#39;expédition
+Company Billing Address,Adresse de la société de facturation
+Supplier Address Details,
+Bank Reconciliation Tool,Outil de réconcialiation d&#39;écritures bancaires
+Supplier Contact,Contact fournisseur
+Subcontracting,Sous traitance
+Order Status,Statut de la commande
+Build,Personnalisations avancées
+Dispatch Address Name,Adresse de livraison intermédiaire
+Amount Eligible for Commission,Montant éligible à comission
+Grant Commission,Eligible aux commissions
+Stock Transactions Settings, Paramétre des transactions
+Role Allowed to Over Deliver/Receive, Rôle autorisé à dépasser cette limite
+Users with this role are allowed to over deliver/receive against orders above the allowance percentage,Rôle Utilisateur qui sont autorisé à livrée/commandé au-delà de la limite
+Over Transfer Allowance,Autorisation de limite de transfert
+Quality Inspection Settings,Paramétre de l&#39;inspection qualité
+Action If Quality Inspection Is Rejected,Action si l'inspection qualité est rejetée
+Disable Serial No And Batch Selector,Désactiver le sélecteur de numéro de lot/série
+Is Rate Adjustment Entry (Debit Note),Est un justement du prix de la note de débit
+Issue a debit note with 0 qty against an existing Sales Invoice,Creer une note de débit avec une quatité à O pour la facture
+Control Historical Stock Transactions,Controle de l&#39;historique des stransaction de stock
+No stock transactions can be created or modified before this date.,Aucune transaction ne peux être créée ou modifié avant cette date.
+Stock transactions that are older than the mentioned days cannot be modified.,Les transactions de stock plus ancienne que le nombre de jours ci-dessus ne peuvent être modifiées
+Role Allowed to Create/Edit Back-dated Transactions,Rôle autorisé à créer et modifier des transactions anti-datée
+"If mentioned, the system will allow only the users with this Role to create or modify any stock transaction earlier than the latest stock transaction for a specific item and warehouse. If set as blank, it allows all users to create/edit back-dated transactions.","Les utilisateur de ce role pourront creer et modifier des transactions dans le passé. Si vide tout les utilisateurs pourrons le faire"
+Auto Insert Item Price If Missing,Création du prix de l'article dans les listes de prix si abscent
+Update Existing Price List Rate,Mise a jour automatique du prix dans les listes de prix
+Show Barcode Field in Stock Transactions,Afficher le champ Code Barre dans les transactions de stock
+Convert Item Description to Clean HTML in Transactions,Convertir les descriptions d'articles en HTML valide lors des transactions
+Have Default Naming Series for Batch ID?,Nom de série par défaut pour les Lots ou Séries
+"The percentage you are allowed to transfer more against the quantity ordered. For example, if you have ordered 100 units, and your Allowance is 10%, then you are allowed transfer 110 units","Le pourcentage de quantité que vous pourrez réceptionner en plus de la quantité commandée. Par exemple, vous avez commandé 100 unités, votre pourcentage de dépassement est de 10%, vous pourrez réceptionner 110 unités"
+Allowed Items,Articles autorisés
+Party Specific Item,Restriction d'article disponible
+Restrict Items Based On,Type de critére de restriction
+Based On Value,critére de restriction
+Unit of Measure (UOM),Unité de mesure (UDM),
+Unit Of Measure (UOM),Unité de mesure (UDM),
+CRM Settings,Paramètres CRM
+Do Not Explode,Ne pas décomposer
+Quick Access, Accés rapides
+{}  Available,{} Disponible.s
+{} Pending,{} En attente.s
+{} To Bill,{} à facturer
+{}  To Receive,{} A recevoir
+{} Active,{} Actif.ve(s)
+{} Open,{} Ouvert.e(s)
+Incorrect Data Report,Rapport de données incohérentes
+Incorrect Serial No Valuation,Valorisation inccorecte par Num. Série / Lots
+Incorrect Balance Qty After Transaction,Equilibre des quantités aprés une transaction
+Interview Type,Type d'entretien
+Interview Round,Cycle d'entretien
+Interview,Entretien
+Interview Feedback,Retour d'entretien
+Journal Energy Point,Historique des points d'énergies
+Billing Address Details,Adresse de facturation (détails)
+Supplier Address Details,Adresse Fournisseur (détails)
+Retail,Commerce
+Users,Utilisateurs
+Permission Manager,Gestion des permissions
+Fetch Timesheet,Récuprer les temps saisis
+Get Supplier Group Details,Appliquer les informations depuis le Groupe de fournisseur
+Quality Inspection(s),Inspection(s) Qualité
+Set Advances and Allocate (FIFO),Affecter les encours au réglement
+Apply Putaway Rule,Appliquer la régle de routage d'entrepot
+Delete Transactions,Supprimer les transactions
+Default Payment Discount Account,Compte par défaut des paiements de remise
+Unrealized Profit / Loss Account,Compte de perte
+Enable Provisional Accounting For Non Stock Items,Activer la provision pour les articles non stockés
+Publish in Website,Publier sur le Site Web
+List View,Vue en liste
diff --git a/erpnext/translations/ru.csv b/erpnext/translations/ru.csv
index 7fcb7b0..743b294 100644
--- a/erpnext/translations/ru.csv
+++ b/erpnext/translations/ru.csv
@@ -1,18 +1,18 @@
-"""Customer Provided Item"" cannot be Purchase Item also","«Товар, предоставленный клиентом» также не может быть предметом покупки",
-"""Customer Provided Item"" cannot have Valuation Rate",«Предоставленный клиентом товар» не может иметь оценку,
-"""Is Fixed Asset"" cannot be unchecked, as Asset record exists against the item","Нельзя отменить выбор ""Является основным средством"", поскольку по данному пункту имеется запись по активам",
-'Based On' and 'Group By' can not be same,"""На основании"" и ""Группировка по"" не могут быть одинаковыми",
-'Days Since Last Order' must be greater than or equal to zero,"""Дней с последнего Заказа"" должно быть больше или равно 0",
-'Entries' cannot be empty,"""Записи"" не могут быть пустыми",
-'From Date' is required,"Поле ""С даты"" является обязательным для заполнения",
-'From Date' must be after 'To Date',"Поле ""С даты"" должно быть после ""До даты""",
-'Has Serial No' can not be 'Yes' for non-stock item,«Имеет серийный номер» не может быть «Да» для нескладируемого продукта,
-'Opening',«Открывается»,
-'To Case No.' cannot be less than 'From Case No.',"«До дела №» не может быть меньше, чем «От дела №»",
-'To Date' is required,"Поле ""До Даты"" является обязательным для заполнения",
-'Total',«Итого»,
-'Update Stock' can not be checked because items are not delivered via {0},"Нельзя выбрать «Обновить запасы», так как продукты не поставляются через {0}",
-'Update Stock' cannot be checked for fixed asset sale,"""Обновить запасы"" нельзя выбрать при продаже основных средств",
+"""Customer Provided Item"" cannot be Purchase Item also","""Товар, предоставленный клиентом"" не может быть предметом покупки",
+"""Customer Provided Item"" cannot have Valuation Rate","""Предоставленный клиентом товар"" не может иметь оценку",
+"""Is Fixed Asset"" cannot be unchecked, as Asset record exists against the item","Нельзя убрать отметку ""Является основным средством"", поскольку по данному пункту имеется запись по активам",
+'Based On' and 'Group By' can not be same,'На основании' и 'Группировка по' не могут быть одинаковыми,
+'Days Since Last Order' must be greater than or equal to zero,'Дней с момента последнего заказа' должно быть больше или равно 0,
+'Entries' cannot be empty,'Записи' не могут быть пустыми,
+'From Date' is required,Поле 'С даты' является обязательным для заполнения,
+'From Date' must be after 'To Date',Значение 'С даты' должно быть после 'До даты',
+'Has Serial No' can not be 'Yes' for non-stock item,'Имеет серийный номер' не может быть 'Да' для нескладируемого продукта,
+'Opening','Открытие',
+'To Case No.' cannot be less than 'From Case No.',"'До дела №' не может быть меньше, чем 'От дела №'",
+'To Date' is required,Поле 'До Даты' является обязательным для заполнения,
+'Total','Итого',
+'Update Stock' can not be checked because items are not delivered via {0},"Нельзя выбрать 'Обновить запасы', так как продукты не поставляются через {0}",
+'Update Stock' cannot be checked for fixed asset sale,"'Обновить запасы' нельзя выбрать при продаже основных средств",
 ) for {0},) для {0},
 1 exact match.,1 точное совпадение.,
 90-Above,90-Над,
@@ -70,20 +70,20 @@
 Account {0}: You can not assign itself as parent account,Счёт {0}: Вы не можете  назначить самого себя родительским счётом,
 Account: {0} can only be updated via Stock Transactions,Счет: {0} можно обновить только через  перемещение по складу,
 Account: {0} with currency: {1} can not be selected,Счет: {0} с валютой: {1} не может быть выбран,
-Accountant,бухгалтер,
+Accountant,Бухгалтер,
 Accounting,Бухгалтерия,
 Accounting Entry for Asset,Учетная запись для активов,
 Accounting Entry for Stock,Бухгалтерская Проводка по Запасам,
 Accounting Entry for {0}: {1} can only be made in currency: {2},Бухгалтерская Проводка для {0}: {1} может быть сделана только в валюте: {2},
-Accounting Ledger,Главная книга,
+Accounting Ledger,Бухгалтерская книга,
 Accounting journal entries.,Журнал бухгалтерских записей.,
 Accounts,Счета,
-Accounts Manager,Диспетчер учетных записей,
+Accounts Manager,Диспетчер счетов,
 Accounts Payable,Счета к оплате,
 Accounts Payable Summary,Сводка кредиторской задолженности,
 Accounts Receivable,Дебиторская задолженность,
 Accounts Receivable Summary,Сводка дебиторской задолженности,
-Accounts User,Пользователь Учетных записей,
+Accounts User,Пользователь Счетов,
 Accounts table cannot be blank.,Таблица учета не может быть пустой.,
 Accrual Journal Entry for salaries from {0} to {1},Запись журнала начислений для зарплат от {0} до {1},
 Accumulated Depreciation,начисленной амортизации,
@@ -115,7 +115,7 @@
 Add Employees,Добавить сотрудников,
 Add Item,Добавить продукт,
 Add Items,Добавить продукты,
-Add Leads,Добавить Обращения,
+Add Leads,Добавить лид,
 Add Multiple Tasks,Добавить несколько задач,
 Add Row,Добавить ряд,
 Add Sales Partners,Добавить партнеров по продажам,
@@ -125,7 +125,7 @@
 Add Time Slots,Добавление временных интервалов,
 Add Timesheets,Добавить табели,
 Add Timeslots,Добавить таймслоты,
-Add Users to Marketplace,Добавить пользователей на рынок,
+Add Users to Marketplace,Добавить пользователей на торговую площадку,
 Add a new address,добавить новый адрес,
 Add cards or custom sections on homepage,Добавить карты или пользовательские разделы на главной странице,
 Add more items or open full form,Добавить ещё продукты или открыть полную форму,
@@ -144,8 +144,8 @@
 Address Type,Тип адреса,
 Administrative Expenses,Административные затраты,
 Administrative Officer,Администратор,
-Administrator,администратор,
-Admission,вход,
+Administrator,Администратор,
+Admission,Допуск,
 Admission and Enrollment,Прием и зачисление,
 Admissions for {0},Поступающим для {0},
 Admit,Допустить,
@@ -154,13 +154,13 @@
 Advance Payments,Авансовые платежи,
 Advance account currency should be same as company currency {0},"Валюта авансового счета должна быть такой же, как и валюта компании {0}",
 Advance amount cannot be greater than {0} {1},"Предварительная сумма не может быть больше, чем {0} {1}",
-Advertising,реклама,
-Aerospace,авиационно-космический,
-Against,против,
+Advertising,Реклама,
+Aerospace,Авиационно-космический,
+Against,Против,
 Against Account,Со счета,
 Against Journal Entry {0} does not have any unmatched {1} entry,Против Запись в журнале {0} не имеет никакого непревзойденную {1} запись,
 Against Journal Entry {0} is already adjusted against some other voucher,Против Запись в журнале {0} уже настроен против какой-либо другой ваучер,
-Against Supplier Invoice {0} dated {1},Против поставщика счет-фактура {0} от {1},
+Against Supplier Invoice {0} dated {1},Против поставщика счет {0} от {1},
 Against Voucher,Против ваучером,
 Against Voucher Type,Против Сертификаты Тип,
 Age,Возраст,
@@ -200,7 +200,7 @@
 Allocating leaves...,Выделенные разрешения,
 Already record exists for the item {0},Уже существует запись для элемента {0},
 "Already set default in pos profile {0} for user {1}, kindly disabled default","Уже задан по умолчанию в pos-профиле {0} для пользователя {1}, любезно отключен по умолчанию",
-Alternate Item,Альтернативный товар,
+Alternate Item,Альтернативный продукт,
 Alternative item must not be same as item code,"Альтернативный элемент не должен быть таким же, как код позиции",
 Amended From,Измененный С,
 Amount,Сумма,
@@ -208,7 +208,7 @@
 Amount of Integrated Tax,Сумма Интегрированного Налога,
 Amount of TDS Deducted,Количество вычитаемых TDS,
 Amount should not be less than zero.,Сумма не должна быть меньше нуля.,
-Amount to Bill,"Сумма, Биллу",
+Amount to Bill,"Сумма к оплате",
 Amount {0} {1} against {2} {3},Сумма {0} {1} против {2} {3},
 Amount {0} {1} deducted against {2},Сумма {0} {1} вычтены {2},
 Amount {0} {1} transferred from {2} to {3},Сумма {0} {1} переведен из {2} до {3},
@@ -232,7 +232,7 @@
 "Applicable if the company is SpA, SApA or SRL","Применимо, если компания SpA, SApA или SRL",
 Applicable if the company is a limited liability company,"Применимо, если компания является обществом с ограниченной ответственностью",
 Applicable if the company is an Individual or a Proprietorship,"Применимо, если компания является частным лицом или собственником",
-Applicant,заявитель,
+Applicant,Заявитель,
 Applicant Type,Тип заявителя,
 Application of Funds (Assets),Применение средств (активов),
 Application period cannot be across two allocation records,Период применения не может быть через две записи распределения,
@@ -242,7 +242,7 @@
 Appointment Confirmation,Подтверждение назначения,
 Appointment Duration (mins),Продолжительность встречи (мин.),
 Appointment Type,Тип Назначения,
-Appointment {0} and Sales Invoice {1} cancelled,Назначение {0} и счет-фактура продажи {1} отменены,
+Appointment {0} and Sales Invoice {1} cancelled,Назначение {0} и счет продажи {1} отменены,
 Appointments and Encounters,Встречи и Столкновения,
 Appointments and Patient Encounters,Назначения и встречи с пациентами,
 Appraisal {0} created for Employee {1} in the given date range,Оценка {0} создан Требуются {1} в указанный диапазон дат,
@@ -254,7 +254,7 @@
 Approving User cannot be same as user the rule is Applicable To,"Утвержденный Пользователь не может быть тем же пользователем, к которому применимо правило",
 "Apps using current key won't be able to access, are you sure?","Приложения, использующие текущий ключ, не смогут получить доступ, вы уверены?",
 Are you sure you want to cancel this appointment?,Вы действительно хотите отменить эту встречу?,
-Arrear,задолженность,
+Arrear,Задолженность,
 As Examiner,Как экзаменатор,
 As On Date,По состоянию на Дату,
 As Supervisor,Как супервизор,
@@ -288,18 +288,18 @@
 Assets,Активы,
 Assign,Назначить,
 Assign Salary Structure,Назначить структуру заработной платы,
-Assign To,Назначить в,
+Assign To,Назначить для,
 Assign to Employees,Назначить сотрудникам,
-Assigning Structures...,Назначение структур ...,
+Assigning Structures...,Назначение структур...,
 Associate,Помощник,
 At least one mode of payment is required for POS invoice.,По крайней мере один способ оплаты требуется для POS счета.,
 Atleast one item should be entered with negative quantity in return document,Как минимум один продукт должен быть введен с отрицательным количеством в возвратном документе,
 Atleast one of the Selling or Buying must be selected,По крайней мере один из продажи или покупки должен быть выбран,
-Atleast one warehouse is mandatory,"По крайней мере, один склад является обязательным",
+Atleast one warehouse is mandatory,"По крайней мере, один склад обязателен",
 Attach Logo,Прикрепить логотип,
 Attachment,Вложение,
 Attachments,Приложения,
-Attendance,посещаемость,
+Attendance,Посещаемость,
 Attendance From Date and Attendance To Date is mandatory,"""Начало учетного периода"" и ""Конец учетного периода"" обязательны к заполнению",
 Attendance can not be marked for future dates,Посещаемость не могут быть отмечены для будущих дат,
 Attendance date can not be less than employee's joining date,"Дата Посещаемость не может быть меньше, чем присоединение даты работника",
@@ -310,13 +310,13 @@
 Attendance not submitted for {0} as {1} on leave.,Посещаемость не была отправлена {0} как {1} в отпуске.,
 Attribute table is mandatory,Таблица атрибутов является обязательной,
 Attribute {0} selected multiple times in Attributes Table,Атрибут {0} выбран несколько раз в таблице атрибутов,
-Author,автор,
+Author,Автор,
 Authorized Signatory,Право подписи,
 Auto Material Requests Generated,"Запросы Авто материал, полученный",
 Auto Repeat,Автоматическое повторение,
 Auto repeat document updated,Автоматический повторный документ обновлен,
 Automotive,Автомобилестроение,
-Available,имеется,
+Available,Доступно,
 Available Leaves,Доступные листья,
 Available Qty,Доступное количество,
 Available Selling,Доступные продажи,
@@ -329,7 +329,7 @@
 Avg Daily Outgoing,Среднедневные исходящие,
 Avg. Buying Price List Rate,Avg. Цена прайс-листа,
 Avg. Selling Price List Rate,Avg. Цена прайс-листа,
-Avg. Selling Rate,Средняя Цена Продажи,
+Avg. Selling Rate,Средняя цена продажи,
 BOM,ВМ,
 BOM Browser,Браузер ВМ,
 BOM No,ВМ №,
@@ -347,7 +347,7 @@
 Balance Sheet,Балансовый отчет,
 Balance Value,Валюта баланса,
 Balance for Account {0} must always be {1},Весы для счета {0} должен быть всегда {1},
-Bank,Банк:,
+Bank,Банк,
 Bank Account,Банковский счет,
 Bank Accounts,Банковские счета,
 Bank Draft,Банковский счет,
@@ -358,7 +358,7 @@
 Bank Reconciliation Statement,Банковская сверка состояние,
 Bank Statement,Выписка из банка,
 Bank Statement Settings,Настройки банковских выписок,
-Bank Statement balance as per General Ledger,Банк балансовый отчет за Главную книгу,
+Bank Statement balance as per General Ledger,Баланс банковской выписки согласно бухгалтерской книге,
 Bank account cannot be named as {0},Банковский счет не может быть назван {0},
 Bank/Cash transactions against party or for internal transfer,Банк / Кассовые операции против партии или для внутренней передачи,
 Banking,Банковские операции,
@@ -369,39 +369,39 @@
 Base URL,Базовый URL,
 Based On,На основании,
 Based On Payment Terms,На основании условий оплаты,
-Basic,основной,
-Batch,партия,
+Basic,Основной,
+Batch,Партия,
 Batch Entries,Пакетные записи,
 Batch ID is mandatory,Идентификатор партии является обязательным,
 Batch Inventory,Пакетная Инвентарь,
 Batch Name,Наименование партии,
-Batch No,№ партии,
+Batch No,Партия №,
 Batch number is mandatory for Item {0},Номер партии является обязательным для продукта {0},
 Batch {0} of Item {1} has expired.,Партия {0} продукта {1} просрочена,
 Batch {0} of Item {1} is disabled.,Пакет {0} элемента {1} отключен.,
-Batch: ,Batch:,
-Batches,Порции,
+Batch: ,Партия: ,
+Batches,Партии,
 Become a Seller,Стать продавцом,
-Beginner,начинающий,
-Bill,Билл,
-Bill Date,Дата оплаты,
-Bill No,Номер накладной,
+Beginner,Начинающий,
+Bill,Счет,
+Bill Date,Дата выставления счета,
+Bill No,Номер счета,
 Bill of Materials,Ведомость материалов,
 Bill of Materials (BOM),Ведомость материалов (ВМ),
 Billable Hours,Оплачиваемые часы,
-Billed,Выдавать счета,
-Billed Amount,Счетов выдано количество,
+Billed,Выставлен счет,
+Billed Amount,Количество выставленных счетов,
 Billing,Выставление счетов,
 Billing Address,Адрес для выставления счетов,
 Billing Address is same as Shipping Address,Платежный адрес совпадает с адресом доставки,
-Billing Amount,Биллинг Сумма,
+Billing Amount,Количество счетов,
 Billing Status,Статус оплаты,
 Billing currency must be equal to either default company's currency or party account currency,Валюта платежа должна быть равна валюте валюты дефолта или валюте счета участника,
-Bills raised by Suppliers.,Платежи Поставщикам,
-Bills raised to Customers.,Платежи Заказчиков,
+Bills raised by Suppliers.,Счета выставленные поставщиками,
+Bills raised to Customers.,Счета выставленные клиентам,
 Biotechnology,Биотехнологии,
 Birthday Reminder,День рождения,
-Black,черный,
+Black,Черный,
 Blanket Orders from Costumers.,Заказы на одеяла от клиентов.,
 Block Invoice,Блок-счет,
 Boms,Boms,
@@ -414,14 +414,14 @@
 Browse BOM,Просмотр спецификации,
 Budget Against,Бюджет против,
 Budget List,Бюджетный список,
-Budget Variance Report,Бюджет Разница Сообщить,
+Budget Variance Report,Отчет об отклонении бюджета,
 Budget cannot be assigned against Group Account {0},Бюджет не может быть назначен на учетную запись группы {0},
 "Budget cannot be assigned against {0}, as it's not an Income or Expense account","Бюджет не может быть назначен на {0}, так как это не доход или расход счета",
-Buildings,здания,
+Buildings,Здания,
 Bundle items at time of sale.,Собирать продукты в момент продажи.,
 Business Development Manager,Менеджер по развитию бизнеса,
-Buy,купить,
-Buying,Покупки,
+Buy,Купить,
+Buying,Закупки,
 Buying Amount,Сумма покупки,
 Buying Price List,Ценовой список покупок,
 Buying Rate,Частота покупки,
@@ -437,7 +437,7 @@
 CWIP Account,CWIP-аккаунт,
 Calculated Bank Statement balance,Расчетный банк себе баланс,
 Calls,Звонки,
-Campaign,кампания,
+Campaign,Кампания,
 Can be approved by {0},Может быть одобрено {0},
 "Can not filter based on Account, if grouped by Account","Не можете фильтровать на основе счета, если сгруппированы по Счет",
 "Can not filter based on Voucher No, if grouped by Voucher","Не можете фильтровать на основе ваучером Нет, если сгруппированы по ваучером",
@@ -451,10 +451,10 @@
 Cancel Material Visits {0} before cancelling this Maintenance Visit,Отменить Материал просмотров {0} до отмены этого обслуживания визит,
 Cancel Subscription,Отменить подписку,
 Cancel the journal entry {0} first,Сначала отменить запись журнала {0},
-Canceled,отменен,
+Canceled,Отменен,
 "Cannot Submit, Employees left to mark attendance","Не могу отправить, Сотрудники оставили отмечать посещаемость",
 Cannot be a fixed asset item as Stock Ledger is created.,"Не может быть элементом фиксированного актива, так как создается складская книга.",
-Cannot cancel because submitted Stock Entry {0} exists,"Нельзя отменить, так как проведена учетная запись по Запасам {0}",
+Cannot cancel because submitted Stock Entry {0} exists,"Нельзя отменить, так как проведен счет по Запасам {0}",
 Cannot cancel transaction for Completed Work Order.,Невозможно отменить транзакцию для выполненного рабочего заказа.,
 Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3},"Невозможно отменить {0} {1}, поскольку Serial No {2} не относится к складу {3}",
 Cannot change Attributes after stock transaction. Make a new Item and transfer stock to the new Item,Невозможно изменить атрибуты после транзакции с акциями. Сделайте новый предмет и переведите запас на новый элемент,
@@ -489,33 +489,33 @@
 Capital Equipments,Капитальные оборудование,
 Capital Stock,Капитал,
 Capital Work in Progress,Капитальная работа в процессе,
-Cart,Тележка,
-Cart is Empty,Корзина Пусто,
+Cart,Корзина,
+Cart is Empty,Корзина пуста,
 Case No(s) already in use. Try from Case No {0},Случай Нет (ы) уже используется. Попробуйте из дела № {0},
 Cash,Наличные,
 Cash Flow Statement,О движении денежных средств,
 Cash Flow from Financing,Поток денежных средств от финансовой,
-Cash Flow from Investing,Поток денежных средств от инвестиционной,
+Cash Flow from Investing,Поток денежных средств от инвестиций,
 Cash Flow from Operations,Поток денежных средств от операций,
-Cash In Hand,Наличность кассы,
+Cash In Hand,Наличные на руках,
 Cash or Bank Account is mandatory for making payment entry,Наличными или банковский счет является обязательным для внесения записи платежей,
 Cashier Closing,Закрытие кассы,
 Casual Leave,Повседневная Оставить,
 Category,Категория,
 Category Name,Название категории,
-Caution,предосторожность,
+Caution,Предосторожность,
 Central Tax,Центральный налог,
-Certification,сертификация,
-Cess,налог,
-Change Amount,Изменение Сумма,
-Change Item Code,Изменить код товара,
+Certification,Сертификация,
+Cess,Налог,
+Change Amount,Изменить сумму,
+Change Item Code,Изменить код продукта,
 Change Release Date,Изменить дату выпуска,
 Change Template Code,Изменить шаблонный код,
 Changing Customer Group for the selected Customer is not allowed.,Изменение группы клиентов для выбранного Клиента запрещено.,
-Chapter,глава,
+Chapter,Глава,
 Chapter information.,Информация о главе.,
 Charge of type 'Actual' in row {0} cannot be included in Item Rate,Начисление типа «Актуальные 'в строке {0} не могут быть включены в пункт Оценить,
-Chargeble,Chargeble,
+Chargeble,Платный,
 Charges are updated in Purchase Receipt against each item,Расходы обновляются в приобретении получение против каждого пункта,
 "Charges will be distributed proportionately based on item qty or amount, as per your selection","Расходы будут распределяться пропорционально на основе количества или суммы продукта, согласно вашему выбору",
 Chart of Cost Centers,План МВЗ,
@@ -536,10 +536,10 @@
 Clay,глина,
 Clear filters,Очистить фильтры,
 Clear values,Очистить значения,
-Clearance Date,Клиренс Дата,
-Clearance Date not mentioned,Клиренс Дата не упоминается,
-Clearance Date updated,Зазор Дата обновления,
-Client,клиент,
+Clearance Date,Дата оформления,
+Clearance Date not mentioned,Дата оформления не упоминается,
+Clearance Date updated,Дата оформления обновлена,
+Client,Клиент,
 Client ID,ID клиента,
 Client Secret,Секрет клиента,
 Clinical Procedure,Клиническая процедура,
@@ -556,7 +556,7 @@
 Closing Balance,Конечное сальдо,
 Code,Код,
 Collapse All,Свернуть все,
-Color,цвет,
+Color,Цвет,
 Colour,Цвет,
 Combined invoice portion must equal 100%,Комбинированная часть счета должна равняться 100%,
 Commercial,Коммерческий сектор,
@@ -576,11 +576,11 @@
 Company {0} does not exist,Компания {0} не существует,
 Compensatory Off,Компенсационные Выкл,
 Compensatory leave request days not in valid holidays,Дни запроса на получение компенсационных отчислений не действительны,
-Complaint,жалоба,
+Complaint,Жалоба,
 Completion Date,Дата завершения,
-Computer,компьютер,
+Computer,Компьютер,
 Condition,Условия,
-Configure,конфигурировать,
+Configure,Конфигурировать,
 Configure {0},Настроить {0},
 Confirmed orders from Customers.,Подтвержденные заказы от клиентов.,
 Connect Amazon with ERPNext,Подключить Amazon к ERPNext,
@@ -589,13 +589,13 @@
 Connected to QuickBooks,Подключено к QuickBooks,
 Connecting to QuickBooks,Подключение к QuickBooks,
 Consultation,Консультация,
-Consultations,консультации,
-Consulting,консалтинг,
-Consumable,потребляемый,
+Consultations,Консультации,
+Consulting,Консалтинг,
+Consumable,Потребляемый,
 Consumed,Потребляемый,
 Consumed Amount,Израсходованное количество,
-Consumed Qty,Потребляемая Кол-во,
-Consumer Products,Потребительские товары,
+Consumed Qty,Потребляемое кол-во,
+Consumer Products,Потребительские продукты,
 Contact,Контакты,
 Contact Details,Контактная информация,
 Contact Number,Контактный номер,
@@ -604,8 +604,8 @@
 Content Masters,Мастера контента,
 Content Type,Тип контента,
 Continue Configuration,Продолжить настройку,
-Contract,контракт,
-Contract End Date must be greater than Date of Joining,"Конец контракта Дата должна быть больше, чем дата вступления",
+Contract,Договор,
+Contract End Date must be greater than Date of Joining,"Дата окончания договора должна быть позже, чем дата его заключения",
 Contribution %,Вклад%,
 Contribution Amount,Вклад Сумма,
 Conversion factor for default Unit of Measure must be 1 in row {0},Коэффициент пересчета для дефолтного Единица измерения должна быть 1 в строке {0},
@@ -623,7 +623,7 @@
 Cost Updated,Стоимость Обновлено,
 Cost as on,"Стоимость, как на",
 Cost of Delivered Items,Затраты по поставленным продуктам,
-Cost of Goods Sold,Себестоимость проданного товара,
+Cost of Goods Sold,Себестоимость проданных продуктов,
 Cost of Issued Items,Стоимость выпущенных продуктов,
 Cost of New Purchase,Стоимость новой покупки,
 Cost of Purchased Items,Стоимость поставленных продуктов,
@@ -631,19 +631,18 @@
 Cost of Sold Asset,Себестоимость проданных активов,
 Cost of various activities,Стоимость различных видов деятельности,
 "Could not create Credit Note automatically, please uncheck 'Issue Credit Note' and submit again","Не удалось создать кредитную ноту автоматически, снимите флажок «Выдавать кредитную ноту» и отправьте снова",
-Could not generate Secret,Не удалось создать секрет,
+Could not generate Secret,Не удалось сгенерировать секретный ключ,
 Could not retrieve information for {0}.,Не удалось получить информацию для {0}.,
 Could not solve criteria score function for {0}. Make sure the formula is valid.,"Не удалось решить функцию оценки критериев для {0}. Убедитесь, что формула действительна.",
 Could not solve weighted score function. Make sure the formula is valid.,"Не удалось решить функцию взвешенного балла. Убедитесь, что формула действительна.",
-Could not submit some Salary Slips,Не удалось подтвердить некоторые зарплатные листки,
-"Could not update stock, invoice contains drop shipping item.","Не удалось обновить запас, счет-фактура содержит падение пункт доставки.",
-Country wise default Address Templates,Шаблоны Страна мудрый адрес по умолчанию,
+Could not submit some Salary Slips,Не удалось отправить некоторые зарплатные ведомости,
+"Could not update stock, invoice contains drop shipping item.","Не удалось обновить запасы, счет содержит продукт прямой доставки.",
+Country wise default Address Templates,Шаблоны адресов по умолчанию для разных стран,
 Course,Курс,
-Course Code: ,Код курса:,
+Course Code: ,Код курса: ,
 Course Enrollment {0} does not exists,Зачисление на курс {0} не существует,
 Course Schedule,Расписание курса,
-Course: ,Курс:,
-Cr,Cr,
+Course: ,Курс: ,
 Create,Создать,
 Create BOM,Создать спецификацию,
 Create Delivery Trip,Создать маршрут доставки,
@@ -658,22 +657,22 @@
 Create Invoices,Создать счета,
 Create Job Card,Создать вакансию,
 Create Journal Entry,Создать запись в журнале,
-Create Lead,Создать лидерство,
-Create Leads,Создать Обращения,
+Create Lead,Создать обращение,
+Create Leads,Создать лид,
 Create Maintenance Visit,Создать техническое посещение,
 Create Material Request,Создать заявку на материал,
 Create Multiple,Создать несколько,
-Create Opening Sales and Purchase Invoices,Создание начальных счетов-фактур купли-продажи,
+Create Opening Sales and Purchase Invoices,Создание начальных счетов купли-продажи,
 Create Payment Entries,Создать платежные записи,
 Create Payment Entry,Создать платежную запись,
 Create Print Format,Создание Формат печати,
-Create Purchase Order,Создать заказ на поставку,
-Create Purchase Orders,Создание заказов на поставку,
-Create Quotation,Создание цитаты,
-Create Salary Slip,Создание Зарплата Слип,
-Create Salary Slips,Создать зарплатные листки,
+Create Purchase Order,Создать заявку на поставку,
+Create Purchase Orders,Создание заявки на поставку,
+Create Quotation,Создать предложение,
+Create Salary Slip,Создать зарплатную ведомость,
+Create Salary Slips,Создать зарплатные ведомости,
 Create Sales Invoice,Создать счет на продажу,
-Create Sales Order,Создать Сделку,
+Create Sales Order,Создать заявку на продажу,
 Create Sales Orders to help you plan your work and deliver on-time,"Создавайте заказы на продажу, чтобы помочь вам спланировать свою работу и выполнить ее в срок",
 Create Sample Retention Stock Entry,Создать образец записи для удержания запаса,
 Create Student,Создать ученика,
@@ -692,23 +691,23 @@
 Created {0} scorecards for {1} between: ,Созданы {0} оценочные карточки для {1} между:,
 Creating Company and Importing Chart of Accounts,Создание компании и импорт плана счетов,
 Creating Fees,Создание сборов,
-Creating Payment Entries......,Создание платежных записей ......,
+Creating Payment Entries......,Создание платежных записей......,
 Creating Salary Slips...,Создание зарплатных листков...,
 Creating student groups,Создание групп студентов,
-Creating {0} Invoice,Создание {0} счета-фактуры,
-Credit,кредит,
+Creating {0} Invoice,Создание {0} счета,
+Credit,Кредит,
 Credit ({0}),Кредит ({0}),
 Credit Account,Кредитный счет,
-Credit Balance,Остаток кредита,
+Credit Balance,Кредитный баланс,
 Credit Card,Кредитная карта,
 Credit Days cannot be a negative number,Кредитные дни не могут быть отрицательным числом,
-Credit Limit,{0}{/0} {1}Кредитный лимит {/1},
-Credit Note,Кредит-нота,
+Credit Limit,Кредитный лимит,
+Credit Note,Кредитная запись,
 Credit Note Amount,Сумма кредитной записи,
 Credit Note Issued,Кредит выдается справка,
-Credit Note {0} has been created automatically,Кредитная нота {0} создана автоматически,
-Credit limit has been crossed for customer {0} ({1}/{2}),Кредитный лимит был скрещен для клиента {0} ({1} / {2}),
-Creditors,кредиторы,
+Credit Note {0} has been created automatically,Кредитная запись {0} была создана автоматически,
+Credit limit has been crossed for customer {0} ({1}/{2}),Кредитный лимит был скрещен для клиента {0} ({1}/{2}),
+Creditors,Кредиторы,
 Criteria weights must add up to 100%,Критерии веса должны составлять до 100%,
 Crop Cycle,Цикл урожая,
 Crops & Lands,Сельскохозяйственные культуры и земли,
@@ -716,24 +715,24 @@
 Currency can not be changed after making entries using some other currency,"Валюта не может быть изменена после внесения записи, используя другой валюты",
 Currency exchange rate master.,Мастер Валютный курс.,
 Currency for {0} must be {1},Валюта для {0} должно быть {1},
-Currency is required for Price List {0},Валюта необходима для Прейскурантом {0},
+Currency is required for Price List {0},Валюта необходима для прайс-листа {0},
 Currency of the Closing Account must be {0},Валюта закрытии счета должны быть {0},
 Currency of the price list {0} must be {1} or {2},Валюта прейскуранта {0} должна быть {1} или {2},
-Currency should be same as Price List Currency: {0},"Валюта должна быть такой же, как и прейскурант Валюта: {0}",
+Currency should be same as Price List Currency: {0},"Валюта должна быть такой же, как и прайс-лист валюты: {0}",
 Current,Текущий,
 Current Assets,Оборотные активы,
 Current BOM and New BOM can not be same,"Текущий спецификации и Нью-BOM не может быть таким же,",
 Current Job Openings,Текущие вакансии Вакансии,
 Current Liabilities,Текущие обязательства,
-Current Qty,Текущий Кол-во,
-Current invoice {0} is missing,Текущий счет-фактура {0} отсутствует,
-Custom HTML,Особый HTML,
-Custom?,Пользовательские?,
+Current Qty,Текущее количество,
+Current invoice {0} is missing,Текущий счет {0} отсутствует,
+Custom HTML,Пользовательский HTML,
+Custom?,Пользовательский?,
 Customer,Клиент,
-Customer Addresses And Contacts,Адреса клиентов и Контакты,
+Customer Addresses And Contacts,Адреса клиентов и контакты,
 Customer Contact,Контакты с клиентами,
 Customer Database.,База данных клиентов.,
-Customer Group,Группа Клиентов,
+Customer Group,Группа клиентов,
 Customer LPO,Клиент LPO,
 Customer LPO No.,Номер клиента LPO,
 Customer Name,Имя клиента,
@@ -764,19 +763,19 @@
 Date of Joining,Дата вступления,
 Date of Joining must be greater than Date of Birth,Дата Присоединение должно быть больше Дата рождения,
 Date of Transaction,Дата транзакции,
-Datetime,Datetime,
+Datetime,Дата и время,
 Day,День,
 Debit,Дебет,
 Debit ({0}),Дебет ({0}),
 Debit A/C Number,Дебетовый номер кондиционера,
 Debit Account,Дебетовый счет,
-Debit Note,Дебет-нота,
-Debit Note Amount,Сумма дебетовой ноты,
-Debit Note Issued,Дебет Примечание Выпущенный,
+Debit Note,Дебетовая запись,
+Debit Note Amount,Сумма дебетовой записи,
+Debit Note Issued,Дата дебетовой записи,
 Debit To is required,Дебет требуется,
 Debit and Credit not equal for {0} #{1}. Difference is {2}.,Дебет и Кредит не равны для {0} # {1}. Разница {2}.,
-Debtors,Должники,
-Debtors ({0}),Должники ({0}),
+Debtors,Дебеторы,
+Debtors ({0}),Дебеторы ({0}),
 Declare Lost,Объявить потерянным,
 Deduction,Вычет,
 Default Activity Cost exists for Activity Type - {0},По умолчанию активность Стоимость существует для вида деятельности - {0},
@@ -790,17 +789,16 @@
 Default settings for buying transactions.,Настройки по умолчанию для покупки сделок.,
 Default settings for selling transactions.,Настройки по умолчанию для продажи сделок.,
 Default tax templates for sales and purchase are created.,Создаются шаблоны налогов по умолчанию для продаж и покупки.,
-Defaults,Значения по Умолчанию,
+Defaults,Значения по умолчанию,
 Defense,Оборона,
 Define Project type.,Установите тип проекта.,
 Define budget for a financial year.,Определить бюджет на финансовый год.,
 Define various loan types,Определение различных видов кредита,
-Del,Del,
 Delay in payment (Days),Задержка в оплате (дни),
 Delete all the Transactions for this Company,Удалить все транзакции этой компании,
 Deletion is not permitted for country {0},Для страны не разрешено удаление {0},
 Delivered,Доставлено,
-Delivered Amount,Поставляется Сумма,
+Delivered Amount,Доставленное количество,
 Delivered Qty,Поставляемое кол-во,
 Delivered: {0},Доставлено: {0},
 Delivery,Доставка,
@@ -816,97 +814,97 @@
 Department,Отдел,
 Department Stores,Универмаги,
 Depreciation,Амортизация,
-Depreciation Amount,Амортизация основных средств Сумма,
-Depreciation Amount during the period,Амортизация Сумма за период,
-Depreciation Date,Износ Дата,
+Depreciation Amount,Сумма амортизации основных средств,
+Depreciation Amount during the period,Сумма амортизации за период,
+Depreciation Date,Дата амортизации,
 Depreciation Eliminated due to disposal of assets,Амортизация Дошел вследствие выбытия активов,
 Depreciation Entry,Износ Вход,
-Depreciation Method,метод начисления износа,
+Depreciation Method,Метод начисления износа,
 Depreciation Row {0}: Depreciation Start Date is entered as past date,Строка амортизации {0}: дата начала амортизации вводится как прошедшая дата,
 Depreciation Row {0}: Expected value after useful life must be greater than or equal to {1},Строка амортизации {0}: ожидаемое значение после полезного срока службы должно быть больше или равно {1},
 Depreciation Row {0}: Next Depreciation Date cannot be before Available-for-use Date,"Строка амортизации {0}: следующая дата амортизации не может быть до даты, доступной для использования",
 Depreciation Row {0}: Next Depreciation Date cannot be before Purchase Date,Строка амортизации {0}: следующая дата амортизации не может быть до даты покупки,
-Designer,дизайнер,
+Designer,Дизайнер,
 Detailed Reason,Подробная причина,
 Details,Подробности,
 Details of Outward Supplies and inward supplies liable to reverse charge,"Сведения о расходных материалах и расходных материалах, подлежащих возврату",
 Details of the operations carried out.,Информация о выполненных операциях.,
-Diagnosis,диагностика,
+Diagnosis,Диагностика,
 Did not find any item called {0},Не нашли какой-либо пункт под названием {0},
 Diff Qty,Diff Qty,
-Difference Account,Учетная запись,
+Difference Account,Разница счета,
 "Difference Account must be a Asset/Liability type account, since this Stock Reconciliation is an Opening Entry","Разница аккаунт должен быть тип счета активов / пассивов, так как это со Примирение запись Открытие",
-Difference Amount,Разница Сумма,
-Difference Amount must be zero,Разница Сумма должна быть равна нулю,
+Difference Amount,Разница,
+Difference Amount must be zero,Разница должна быть равна нулю,
 Different UOM for items will lead to incorrect (Total) Net Weight value. Make sure that Net Weight of each item is in the same UOM.,"Различные единицы измерения (ЕИ) продуктов приведут к некорректному (общему) значению массы нетто. Убедитесь, что вес нетто каждого продукта находится в одной ЕИ.",
 Direct Expenses,Прямые расходы,
 Direct Income,Прямая прибыль,
 Disable,Отключить,
-Disabled template must not be default template,Шаблон для инвалидов не должно быть по умолчанию шаблон,
+Disabled template must not be default template,Отключенный шаблон не может быть шаблоном по умолчанию,
 Disburse Loan,Выдавать кредит,
 Disbursed,Освоено,
 Disc,диск,
 Discharge,разрядка,
-Discount,скидка,
+Discount,Скидка,
 Discount Percentage can be applied either against a Price List or for all Price List.,"Процент скидки может применяться либо к Прайс-листу, либо ко всем Прайс-листам.",
 Discount must be less than 100,Скидка должна быть меньше 100,
 Diseases & Fertilizers,Болезни и удобрения,
-Dispatch,отправка,
+Dispatch,Отправка,
 Dispatch Notification,Уведомление о рассылке,
 Dispatch State,Состояние отправки,
 Distance,Дистанция,
-Distribution,распределение,
-Distributor,дистрибьютор,
+Distribution,Дистрибьюция,
+Distributor,Дистрибьютор,
 Dividends Paid,Оплачено дивидендов,
-Do you really want to restore this scrapped asset?,Вы действительно хотите восстановить этот актив на слом?,
+Do you really want to restore this scrapped asset?,Вы действительно хотите восстановить этот списанный актив?,
 Do you really want to scrap this asset?,Вы действительно хотите отказаться от этого актива?,
 Do you want to notify all the customers by email?,Вы хотите уведомить всех клиентов по электронной почте?,
 Doc Date,Дата документа,
 Doc Name,Имя документа,
 Doc Type,Тип документа,
 Docs Search,Поиск документов,
-Document Name,название документа,
+Document Name,Название документа,
 Document Status,Статус документа,
-Document Type,тип документа,
-Domain,Домен,
+Document Type,Тип документа,
+Domain,Направление,
 Domains,Направление деятельности,
-Done,Сделать,
-Donor,даритель,
+Done,Готово,
+Donor,Донор,
 Donor Type information.,Информация о доноре.,
 Donor information.,Донорская информация.,
 Download JSON,Скачать JSON,
-Draft,Проект,
-Drop Ship,Корабль падения,
+Draft,Черновик,
+Drop Ship,Прямая поставка,
 Drug,Лекарство,
 Due / Reference Date cannot be after {0},Из-за / Reference Дата не может быть в течение {0},
 Due Date cannot be before Posting / Supplier Invoice Date,Срок оплаты не может быть раньше даты публикации / выставления счета поставщику,
 Due Date is mandatory,Благодаря Дата является обязательным,
-Duplicate Entry. Please check Authorization Rule {0},"Копия записи. Пожалуйста, проверьте Авторизация Правило {0}",
+Duplicate Entry. Please check Authorization Rule {0},"Копия записи. Пожалуйста, проверьте правила авторизации {0}",
 Duplicate Serial No entered for Item {0},Дубликат Серийный номер вводится для Пункт {0},
 Duplicate customer group found in the cutomer group table,Дубликат группа клиентов найти в таблице Cutomer группы,
 Duplicate entry,Дублировать запись,
-Duplicate item group found in the item group table,Повторяющаяся группа находке в таблице группы товаров,
+Duplicate item group found in the item group table,Дубликат группы продуктов в таблице групп продуктов,
 Duplicate roll number for student {0},Повторяющийся номер ролика для ученика {0},
 Duplicate row {0} with same {1},Дубликат строка {0} с же {1},
 Duplicate {0} found in the table,Дубликат {0} найден в таблице,
 Duration in Days,Продолжительность в днях,
 Duties and Taxes,Пошлины и налоги,
 E-Invoicing Information Missing,Отсутствует информация об инвойсировании,
-ERPNext Demo,ERPNext Demo,
+ERPNext Demo,ERPNext демо,
 ERPNext Settings,Настройки ERPNext,
-Earliest,Старейшие,
+Earliest,Самый ранний,
 Earnest Money,Задаток,
 Earning,Зарабатывание,
-Edit,редактировать,
+Edit,Редактировать,
 Edit Publishing Details,Редактировать информацию о публикации,
 "Edit in full page for more options like assets, serial nos, batches etc.","Редактируйте на полной странице дополнительные параметры, такие как активы, серийные номера, партии и т. Д.",
 Education,образование,
 Either location or employee must be required,"Требуется либо место, либо сотрудник",
 Either target qty or target amount is mandatory,Либо целевой Количество или целевое количество является обязательным,
 Either target qty or target amount is mandatory.,Либо целевой Количество или целевое количество является обязательным.,
-Electrical,электрический,
+Electrical,Электрический,
 Electronic Equipments,Электронные приборы,
-Electronics,электроника,
+Electronics,Электроника,
 Eligible ITC,Соответствующий ITC,
 Email Account,Электронная почта,
 Email Address,Адрес электронной почты,
@@ -918,18 +916,18 @@
 Email not found in default contact,Адрес электронной почты не найден в контакте по умолчанию,
 Email sent to {0},Письмо отправлено на адрес {0},
 Employee,Сотрудник,
-Employee A/C Number,Номер A / C сотрудника,
+Employee A/C Number,A/C номер сотрудника,
 Employee Advances,Достижения сотрудников,
 Employee Benefits,Вознаграждения работникам,
-Employee Grade,Уровень персонала,
+Employee Grade,Ранг персонала,
 Employee ID,ID сотрудника,
 Employee Lifecycle,Жизненный цикл сотрудников,
 Employee Name,Имя сотрудника,
-Employee Promotion cannot be submitted before Promotion Date ,Продвижение сотрудника не может быть отправлено до даты акции,
+Employee Promotion cannot be submitted before Promotion Date ,Повышение сотрудника не может быть выполнено до даты приступления к должности,
 Employee Referral,Перечень сотрудников,
 Employee Transfer cannot be submitted before Transfer Date ,Передача сотрудника не может быть отправлена до даты передачи,
-Employee cannot report to himself.,Сотрудник не может сообщить себе.,
-Employee relieved on {0} must be set as 'Left',"Сотрудник освобожден от {0} должен быть установлен как ""левые""",
+Employee cannot report to himself.,Сотрудник не может сообщить самому себе.,
+Employee relieved on {0} must be set as 'Left',"Сотрудник освобожден от {0} должен быть установлен как 'Покинул'",
 Employee {0} already submited an apllication {1} for the payroll period {2},Сотрудник {0} уже отправил заявку {1} для периода расчета {2},
 Employee {0} has already applied for {1} between {2} and {3} : ,Сотрудник {0} уже подал заявку на {1} между {2} и {3}:,
 Employee {0} has no maximum benefit amount,Сотрудник {0} не имеет максимальной суммы пособия,
@@ -937,9 +935,9 @@
 Employee {0} is on Leave on {1},Сотрудник {0} отправляется в {1},
 Employee {0} of grade {1} have no default leave policy,Сотрудник {0} класса {1} не имеет политики отпуска по умолчанию,
 Employee {0} on Half day on {1},Сотрудник {0} на полдня на {1},
-Enable,Автоматическое обновление,
-Enable / disable currencies.,Включение / отключение валюты.,
-Enabled,Включено,
+Enable, Разрешить,
+Enable / disable currencies.,Разрешить / запретить валюты.,
+Enabled,Разрешено,
 "Enabling 'Use for Shopping Cart', as Shopping Cart is enabled and there should be at least one Tax Rule for Shopping Cart","Включение &quot;Использовать для Корзине», как Корзина включена и должно быть по крайней мере один налог Правило Корзина",
 End Date,Дата окончания,
 End Date can not be less than Start Date,Дата окончания не может быть меньше даты начала,
@@ -950,8 +948,8 @@
 End time cannot be before start time,Время окончания не может быть раньше времени начала,
 Ends On date cannot be before Next Contact Date.,Конец дата не может быть до следующей даты контакта.,
 Energy,Энергоэффективность,
-Engineer,инженер,
-Enough Parts to Build,Достаточно части для сборки,
+Engineer,Инженер,
+Enough Parts to Build,Достаточно деталей для сборки,
 Enroll,зачислять,
 Enrolling student,поступив студент,
 Enrolling students,Регистрация студентов,
@@ -966,27 +964,27 @@
 Error Log,Журнал ошибок,
 Error evaluating the criteria formula,Ошибка оценки формулы критериев,
 Error in formula or condition: {0},Ошибка в формуле или условие: {0},
-Error: Not a valid id?,Ошибка: Не действует ID?,
+Error: Not a valid id?,Ошибка: Не действительный ID?,
 Estimated Cost,Ориентировочная стоимость,
-Evaluation,оценка,
+Evaluation,Оценка,
 "Even if there are multiple Pricing Rules with highest priority, then following internal priorities are applied:","Даже если существует несколько правил ценообразования с наивысшим приоритетом, применяются следующие внутренние приоритеты:",
 Event,Событие,
-Event Location,Место проведения мероприятия,
+Event Location,Место проведения,
 Event Name,Название события,
 Exchange Gain/Loss,Обмен Прибыль / Убыток,
 Exchange Rate Revaluation master.,Курс переоценки мастер.,
 Exchange Rate must be same as {0} {1} ({2}),"Курс должен быть таким же, как {0} {1} ({2})",
-Excise Invoice,Акцизный Счет,
+Excise Invoice,Акцизный счет,
 Execution,Реализация,
 Executive Search,Executive Search,
-Expand All,Расширить все,
+Expand All,Развернуть все,
 Expected Delivery Date,Ожидаемая дата доставки,
 Expected Delivery Date should be after Sales Order Date,Ожидаемая дата доставки должна быть после даты Сделки,
 Expected End Date,Ожидаемая дата завершения,
-Expected Hrs,Ожидаемые часы,
+Expected Hrs,Ожидаемая длительность,
 Expected Start Date,Ожидаемая дата начала,
-Expense,расходы,
-Expense / Difference account ({0}) must be a 'Profit or Loss' account,"Расходов / Разница счет ({0}) должен быть ""прибыль или убыток» счета",
+Expense,Расходы,
+Expense / Difference account ({0}) must be a 'Profit or Loss' account,Счет расходов / разницы ({0}) должен быть счетом "Прибыль или убыток",
 Expense Account,Расходов счета,
 Expense Claim,Заявка на возмещение,
 Expense Claim for Vehicle Log {0},Авансовый Отчет для для журнала автомобиля {0},
@@ -996,7 +994,7 @@
 Expenses,Расходы,
 Expenses Included In Asset Valuation,"Расходы, включенные в оценку активов",
 Expenses Included In Valuation,"Затрат, включаемых в оценке",
-Expired Batches,Истекшие партии,
+Expired Batches,Просроченные партии,
 Expires On,Годен до,
 Expiring On,Срок действия,
 Expiry (In Days),Срок действия (в днях),
@@ -1004,7 +1002,7 @@
 Export E-Invoices,Экспорт электронных счетов,
 Extra Large,Очень большой,
 Extra Small,Очень маленький,
-Fail,Потерпеть неудачу,
+Fail,Неудача,
 Failed,Не выполнено,
 Failed to create website,Не удалось создать веб-сайт,
 Failed to install presets,Не удалось установить пресеты,
@@ -1013,7 +1011,7 @@
 Failed to setup defaults,Не удалось установить значения по умолчанию,
 Failed to setup post company fixtures,Не удалось настроить оборудование для компании,
 Fax,Факс,
-Fee,плата,
+Fee,Оплата,
 Fee Created,Плата создана,
 Fee Creation Failed,Не удалось создать сбор,
 Fee Creation Pending,Платежное создание ожидается,
@@ -1023,8 +1021,8 @@
 Female,Женский,
 Fetch Data,Получение данных,
 Fetch Subscription Updates,Получение обновлений подписки,
-Fetch exploded BOM (including sub-assemblies),Fetch разобранном BOM (в том числе узлов),
-Fetching records......,Получение записей ......,
+Fetch exploded BOM (including sub-assemblies),Получить развернутую спецификацию (включая узлы),
+Fetching records......,Получение записей......,
 Field Name,Имя поля,
 Fieldname,Имя поля,
 Fields,Поля,
@@ -1039,8 +1037,8 @@
 Financial Year,Финансовый год,
 Finish,Завершить,
 Finished Good,Готово Хорошо,
-Finished Good Item Code,Готовый товарный код,
-Finished Goods,Готовая продукция,
+Finished Good Item Code,Код готовых продуктов,
+Finished Goods,Готовые продукты,
 Finished Item {0} must be entered for Manufacture type entry,Готовая единица {0} должна быть введена для Производственного типа записи,
 Finished product quantity <b>{0}</b> and For Quantity <b>{1}</b> cannot be different,Количество готового продукта <b>{0}</b> и для количества <b>{1}</b> не может быть разным,
 First Name,Имя,
@@ -1056,7 +1054,7 @@
 Fixed Asset Item must be a non-stock item.,Элемент основных средств не может быть элементом запасов.,
 Fixed Assets,Основные средства,
 Following Material Requests have been raised automatically based on Item's re-order level,Следующие запросы на материалы были созданы автоматически на основании минимального уровня запасов продукта,
-Following accounts might be selected in GST Settings:,В настройках GST можно выбрать следующие учетные записи:,
+Following accounts might be selected in GST Settings:,В настройках НДС можно выбрать следующие учетные записи:,
 Following course schedules were created,Были составлены графики курсов,
 Following item {0} is not marked as {1} item. You can enable them as {1} item from its Item master,Следующий элемент {0} не помечен как элемент {1}. Вы можете включить их как {1} из своего элемента,
 Following items {0} are not marked as {1} item. You can enable them as {1} item from its Item master,Следующие элементы {0} не помечены как {1}. Вы можете включить их как {1} из своего элемента,
@@ -1079,7 +1077,7 @@
 Forum Activity,Активность в форуме,
 Free item code is not selected,Бесплатный код товара не выбран,
 Freight and Forwarding Charges,Грузовые и экспедиторские Сборы,
-Frequency,частота,
+Frequency,Частота,
 Friday,Пятница,
 From,От,
 From Address 1,Из адреса 1,
@@ -1096,7 +1094,7 @@
 From Fiscal Year,Из финансового года,
 From GSTIN,От GSTIN,
 From Party Name,От имени партии,
-From Pin Code,Из штырькового кода,
+From Pin Code,Из PIN кода,
 From Place,С места,
 From Range has to be less than To Range,"С Диапазон должен быть меньше, чем диапазон",
 From State,Из штата,
@@ -1108,8 +1106,8 @@
 From date can not be less than employee's joining date,От даты не может быть меньше даты вступления в должность сотрудника,
 From value must be less than to value in row {0},"От значение должно быть меньше, чем значение в строке {0}",
 From {0} | {1} {2},С {0} | {1} {2},
-Fuel Price,Топливо Цена,
-Fuel Qty,Топливо Кол-во,
+Fuel Price,Стоимость топлива,
+Fuel Qty,Топливо кол-во,
 Fulfillment,свершение,
 Full,Полный,
 Full Name,Полное имя,
@@ -1132,7 +1130,7 @@
 Generate Secret,Создать секрет,
 Get Details From Declaration,Получить детали из декларации,
 Get Employees,Получить сотрудников,
-Get Invocies,Получить призывы,
+Get Invocies,Получить счета,
 Get Invoices,Получить счета,
 Get Invoices based on Filters,Получить счета на основе фильтров,
 Get Items from BOM,Получить продукты из спецификации,
@@ -1144,7 +1142,7 @@
 Get Updates,Получить обновления,
 Get customers from,Получить клиентов от,
 Get from Patient Encounter,Получите от Patient Encounter,
-Getting Started,Руководство,
+Getting Started,Приступая к работе,
 GitHub Sync ID,Идентификатор синхронизации GitHub,
 Global settings for all manufacturing processes.,Глобальные настройки для всех производственных процессов.,
 Go to the Desktop and start using ERPNext,Перейдите на рабочий стол и начать использовать ERPNext,
@@ -1153,8 +1151,8 @@
 Goal and Procedure,Цель и процедура,
 Goals cannot be empty,Цели не могут быть пустыми,
 Goods In Transit,Товары в пути,
-Goods Transferred,Товар перенесен,
-Goods and Services Tax (GST India),Налог на товары и услуги (GST India),
+Goods Transferred,Товар передан,
+Goods and Services Tax (GST India),Налог на товары и услуги (НДС Индия),
 Goods are already received against the outward entry {0},Товар уже получен против выездной записи {0},
 Government,Правительство,
 Grand Total,Общий итог,
@@ -1169,28 +1167,28 @@
 Gross Profit / Loss,Валовая прибыль / убыток,
 Gross Purchase Amount,Валовая сумма покупки,
 Gross Purchase Amount is mandatory,Валовая сумма покупки является обязательным,
-Group by Account,Группа по Счет,
-Group by Party,Группа по партии,
-Group by Voucher,Группа по ваучером,
-Group by Voucher (Consolidated),Группа по ваучеру (консолидировано),
-Group node warehouse is not allowed to select for transactions,"склад группы узлов не допускается, чтобы выбрать для сделок",
+Group by Account,Сгруппировать по счетам,
+Group by Party,Сгруппировать по партии,
+Group by Voucher,Сгруппировать по ваучеру,
+Group by Voucher (Consolidated),Сгруппировать по ваучеру (консолидировано),
+Group node warehouse is not allowed to select for transactions,Склад узла группы не может выбирать для транзакций,
 Group to Non-Group,Группа не-группы,
 Group your students in batches,Группа ваших студентов в партиях,
-Groups,группы,
+Groups,Группы,
 Guardian1 Email ID,Идентификатор электронной почты Guardian1,
-Guardian1 Mobile No,Guardian1 Mobile Нет,
+Guardian1 Mobile No,Guardian1 Mobile №,
 Guardian1 Name,Имя Guardian1,
 Guardian2 Email ID,Идентификатор электронной почты Guardian2,
-Guardian2 Mobile No,Guardian2 Mobile Нет,
+Guardian2 Mobile No,Guardian2 Mobile №,
 Guardian2 Name,Имя Guardian2,
 Guest,Гость,
 HR Manager,Менеджер отдела кадров,
-HSN,HSN,
+HSN,ГСК,
 HSN/SAC,HSN / SAC,
 Half Day,Полдня,
 Half Day Date is mandatory,Полдня Дата обязательна,
-Half Day Date should be between From Date and To Date,Поаяся Дата должна быть в пределах от даты и до настоящего времени,
-Half Day Date should be in between Work From Date and Work End Date,Половина дня должна находиться между Работой с даты и датой окончания работы,
+Half Day Date should be between From Date and To Date,Дата половины дня должна быть между датой начала и датой окончания.,
+Half Day Date should be in between Work From Date and Work End Date,Дата половины дня должна находиться между датой начала работы и датой окончания работы,
 Half Yearly,Половина года,
 Half day date should be in between from date and to date,Половина дня должна быть между датой и датой,
 Half-Yearly,Раз в полгода,
@@ -1212,13 +1210,13 @@
 High,Высокий,
 High Sensitivity,Высокая чувствительность,
 Hold,Удержание,
-Hold Invoice,Держать счет-фактуру,
+Hold Invoice,Держать счет,
 Holiday,Выходной,
 Holiday List,Список праздников,
 Hotel Rooms of type {0} are unavailable on {1},Номера отеля типа {0} недоступны в {1},
 Hotels,Отели,
-Hourly,почасовой,
-Hours,часов,
+Hourly,Почасовой,
+Hours,Часов,
 House rent paid days overlapping with {0},Аренда дома оплачивается по дням с перекрытием {0},
 House rented dates required for exemption calculation,"Даты аренды дома, необходимые для расчета освобождения",
 House rented dates should be atleast 15 days apart,Даты аренды дома должны быть как минимум на 15 дней друг от друга,
@@ -1229,7 +1227,7 @@
 Human Resources,Персонал,
 IFSC Code,Код IFSC,
 IGST Amount,Сумма IGST,
-IP Address,Айпи адрес,
+IP Address,IP адрес,
 ITC Available (whether in full op part),ITC Доступен (будь то в полной части оп),
 ITC Reversed,ITC наоборот,
 Identifying Decision Makers,"Определение лиц, принимающих решения",
@@ -1255,7 +1253,7 @@
 In Production,В производстве,
 In Qty,В кол-ве,
 In Stock Qty,В наличии Кол-во,
-In Stock: ,В наличии:,
+In Stock: ,В наличии: ,
 In Value,В цене,
 "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",В случае многоуровневой программы Клиенты будут автоматически назначены соответствующему уровню в соответствии с затраченными,
 Inactive,Неактивный,
@@ -1265,12 +1263,12 @@
 Include POS Transactions,Включить POS-транзакции,
 Include UOM,Включить UOM,
 Included in Gross Profit,Включено в валовую прибыль,
-Income,доход,
+Income,Доход,
 Income Account,Счет Доходов,
 Income Tax,Подоходный налог,
 Incoming,Входящий,
 Incoming Rate,Входящая цена,
-Incorrect number of General Ledger Entries found. You might have selected a wrong Account in the transaction.,"Неверное количество Главная книга найдено. Вы, возможно, выбран неправильный счет в сделке.",
+Incorrect number of General Ledger Entries found. You might have selected a wrong Account in the transaction.,"Обнаружено неверное количество записей в бухгалтерской книге. Возможно, вы выбрали неверный счет в транзакции.",
 Increment cannot be 0,Прирост не может быть 0,
 Increment for Attribute {0} cannot be 0,Прирост за атрибут {0} не может быть 0,
 Indirect Expenses,Косвенные расходы,
@@ -1286,13 +1284,13 @@
 Installing presets,Установка пресетов,
 Institute Abbreviation,институт Аббревиатура,
 Institute Name,Название института,
-Instructor,инструктор,
-Insufficient Stock,Недостаточный Stock,
-Insurance Start date should be less than Insurance End date,"Дата страхование начала должна быть меньше, чем дата страхование End",
+Instructor,Инструктор,
+Insufficient Stock,Недостаточный запас,
+Insurance Start date should be less than Insurance End date,"Дата начала страхования должна быть раньше, чем дата окончания",
 Integrated Tax,Интегрированный налог,
 Inter-State Supplies,Межгосударственные поставки,
-Interest Amount,Проценты Сумма,
-Interests,интересы,
+Interest Amount,Сумма процентов,
+Interests,Интересы,
 Intern,Стажер,
 Internet Publishing,Интернет издания,
 Intra-State Supplies,Внутригосударственные поставки,
@@ -1314,14 +1312,14 @@
 Investment Banking,Инвестиционно-банковская деятельность,
 Investments,Инвестиции,
 Invoice,Счет,
-Invoice Created,Создан счет-фактура,
+Invoice Created,Счет создан,
 Invoice Discounting,Дисконтирование счета,
-Invoice Patient Registration,Регистрация счета-фактуры,
+Invoice Patient Registration,Регистрация счета,
 Invoice Posting Date,Счет Дата размещения,
 Invoice Type,Тип счета,
 Invoice already created for all billing hours,"Счет, уже созданный для всех платежных часов",
 Invoice can't be made for zero billing hour,Счета не могут быть выставлены за нулевой расчетный час,
-Invoice {0} no longer exists,Счет-фактура {0} больше не существует,
+Invoice {0} no longer exists,Счет {0} больше не существует,
 Invoiced,Фактурная,
 Invoiced Amount,Сумма по счетам,
 Invoices,Счета,
@@ -1339,50 +1337,50 @@
 Issues,Вопросов,
 It is needed to fetch Item Details.,Это необходимо для отображения подробностей продукта.,
 Item,Продукт,
-Item 1,Пункт 1,
-Item 2,Пункт 2,
-Item 3,Пункт 3,
-Item 4,Пункт 4,
-Item 5,Пункт 5,
-Item Cart,Продуктовая корзина,
+Item 1,Продукт 1,
+Item 2,Продукт 2,
+Item 3,Продукт 3,
+Item 4,Продукт 4,
+Item 5,Продукт 5,
+Item Cart,Корзина,
 Item Code,Код продукта,
-Item Code cannot be changed for Serial No.,Код товара не может быть изменен для серийный номер,
-Item Code required at Row No {0},Код товара требуется на Row Нет {0},
+Item Code cannot be changed for Serial No.,Код продукта не может быть изменен для серийного номера,
+Item Code required at Row No {0},Требуется код продукта в строке № {0},
 Item Description,Описание продукта,
 Item Group,Продуктовая группа,
 Item Group Tree,Структура продуктовых групп,
 Item Group not mentioned in item master for item {0},Пункт Группа не упоминается в мастера пункт по пункту {0},
-Item Name,Имя элемента,
+Item Name,Название продукта,
 Item Price added for {0} in Price List {1},Цена продукта {0} добавлена в прайс-лист {1},
 "Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty and Dates.","Цена товара отображается несколько раз на основе Прайс-листа, Поставщика / Клиента, Валюты, Предмет, UOM, Кол-во и Даты.",
 Item Price updated for {0} in Price List {1},Цена продукта {0} обновлена в прайс-листе {1},
-Item Row {0}: {1} {2} does not exist in above '{1}' table,Элемент Row {0}: {1} {2} не существует в таблице «{1}»,
-Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable,Пункт Налоговый ряд {0} должен иметь учетную запись типа налога или доходов или расходов или платная,
+Item Row {0}: {1} {2} does not exist in above '{1}' table,Строка {0}: {1} {2} не существует в таблице «{1}»,
+Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable,"Строка налога {0} должен иметь счет типа Налога, Доход, Расходов или Облагаемый налогом",
 Item Template,Шаблон продукта,
-Item Variant Settings,Параметры модификации продкута,
+Item Variant Settings,Параметры модификации продукта,
 Item Variant {0} already exists with same attributes,Модификация продукта {0} с этими атрибутами уже существует,
 Item Variants,Варианты продукта,
 Item Variants updated,Обновлены варианты предметов,
 Item has variants.,Продукт имеет модификации,
 Item must be added using 'Get Items from Purchase Receipts' button,"Продукт должен быть добавлен с помощью кнопки ""Получить продукты из покупки '",
 Item valuation rate is recalculated considering landed cost voucher amount,Коэффициент оценки стоимости пересчитывается с учетом суммы  талонов с затратами,
-Item variant {0} exists with same attributes,Модификация продукта {0} с этими атрибутами существует,
+Item variant {0} exists with same attributes,Вариант продукта {0} с этими атрибутами уже существует,
 Item {0} does not exist,Продукт {0} не существует,
 Item {0} does not exist in the system or has expired,Продукт {0} не существует или просрочен,
 Item {0} has already been returned,Продукт {0} уже возвращен,
 Item {0} has been disabled,Продукт {0} не годен,
-Item {0} has reached its end of life on {1},Пункт {0} достигла своей жизни на {1},
-Item {0} ignored since it is not a stock item,"Пункт {0} игнорируется, так как это не складские позиции",
-"Item {0} is a template, please select one of its variants","Пункт {0} шаблона, выберите один из его вариантов",
-Item {0} is cancelled,Пункт {0} отменяется,
-Item {0} is disabled,Пункт {0} отключена,
-Item {0} is not a serialized Item,Пункт {0} не сериализованным Пункт,
+Item {0} has reached its end of life on {1},Продукт {0} достигокончания срока годности на {1},
+Item {0} ignored since it is not a stock item,"Продукт {0} игнорируется, так как это не складские позиции",
+"Item {0} is a template, please select one of its variants","Продукт {0} это шаблон, выберите один из его вариантов",
+Item {0} is cancelled,Продукт {0} отменен,
+Item {0} is disabled,Продукт {0} отключен,
+Item {0} is not a serialized Item,Продукт {0} не сериализованным продуктом,
 Item {0} is not a stock Item,Продукта {0} нет на складе,
-Item {0} is not active or end of life has been reached,Пункт {0} не является активным или конец жизни был достигнут,
-Item {0} is not setup for Serial Nos. Check Item master,Пункт {0} не установка для мастера серийные номера Проверить товара,
-Item {0} is not setup for Serial Nos. Column must be blank,Пункт {0} не установка для серийные номера колонке должно быть пустым,
-Item {0} must be a Fixed Asset Item,Пункт {0} должен быть Fixed Asset Item,
-Item {0} must be a Sub-contracted Item,Пункт {0} должен быть Субдоговорная Пункт,
+Item {0} is not active or end of life has been reached,Продукт {0} не активен или истек срок годности,
+Item {0} is not setup for Serial Nos. Check Item master,Продукт {0} не настроен для серийных номеров. Проверьте мастер продукта,
+Item {0} is not setup for Serial Nos. Column must be blank,Продукт {0} не настроен для серийных номеров. Колонка должны быть пуста,
+Item {0} must be a Fixed Asset Item,Продукт {0} должен быть объектом основных средств,
+Item {0} must be a Sub-contracted Item,Продукт {0} должен быть предметом субподряда,
 Item {0} must be a non-stock item,Продукт {0} должен отсутствовать на складе,
 Item {0} must be a stock Item,Продукт {0} должен быть в наличии,
 Item {0} not found,Продукт {0} не найден,
@@ -1397,10 +1395,10 @@
 Job Description,Описание работы,
 Job Offer,Предложение работы,
 Job card {0} created,Карта работы {0} создана,
-Jobs,работы,
+Jobs,Вакансии,
 Join,Присоединиться,
 Journal Entries {0} are un-linked,Записи в журнале {0} не-связаны,
-Journal Entry,Запись в дневнике,
+Journal Entry,Запись в журнале,
 Journal Entry {0} does not have account {1} or already matched against other voucher,Запись в журнале {0} не имеете учет {1} или уже сравнивается с другой ваучер,
 Kanban Board,Канбан-доска,
 Key Reports,Ключевые отчеты,
@@ -1413,12 +1411,12 @@
 Lab Tests and Vital Signs,Лабораторные тесты и жизненные знаки,
 Lab result datetime cannot be before testing datetime,Лабораторный результат datetime не может быть до тестирования даты и времени,
 Lab testing datetime cannot be before collection datetime,Лабораторное тестирование datetime не может быть до даты сбора данных,
-Label,Имя поля,
-Laboratory,лаборатория,
+Label,Ярлык,
+Laboratory,Лаборатория,
 Language Name,Название языка,
-Large,большой,
-Last Communication,Последнее сообщение,
-Last Communication Date,Дата последнего общения,
+Large,Большой,
+Last Communication,Последняя коммуникация,
+Last Communication Date,Дата последней коммуникации,
 Last Name,Фамилия,
 Last Order Amount,Последняя сумма заказа,
 Last Order Date,Последняя дата заказа,
@@ -1426,20 +1424,20 @@
 Last Purchase Rate,Последняя цена покупки,
 Latest,Последние,
 Latest price updated in all BOMs,Последняя цена обновлена во всех спецификациях,
-Lead,Обращение,
-Lead Count,Счет Обращения,
-Lead Owner,Ответственный,
-Lead Owner cannot be same as the Lead,Ответственным за Обращение не может быть сам обратившийся,
-Lead Time Days,Время выполнения,
-Lead to Quotation,Обращение в Предложение,
-"Leads help you get business, add all your contacts and more as your leads",Обращения необходимы бизнесу. Добавьте все свои контакты в качестве Обращений,
+Lead,Лид,
+Lead Count,Количество лидов,
+Lead Owner,Ответственный за лид,
+Lead Owner cannot be same as the Lead,Ответственным за лид не может быть сам лид,
+Lead Time Days,Время лида,
+Lead to Quotation,Лид в предложение,
+"Leads help you get business, add all your contacts and more as your leads",Лиды необходимы бизнесу. Добавьте все свои контакты в качестве лидов,
 Learn,Справка,
 Leave Approval Notification,Оставить уведомление об утверждении,
 Leave Blocked,Оставьте Заблокированные,
 Leave Encashment,Оставьте Инкассацию,
 Leave Management,Оставить управления,
 Leave Status Notification,Оставить уведомление о состоянии,
-Leave Type,Оставьте Тип,
+Leave Type,Тип отпуска,
 Leave Type is madatory,Оставить Тип сумасшедший,
 Leave Type {0} cannot be allocated since it is leave without pay,"Оставьте Тип {0} не может быть выделена, так как он неоплачиваемый отпуск",
 Leave Type {0} cannot be carry-forwarded,Оставьте Тип {0} не может быть перенос направлен,
@@ -1449,14 +1447,14 @@
 Leave application {0} already exists against the student {1},Оставить заявку {0} уже существует против ученика {1},
 "Leave cannot be allocated before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}","Оставить не могут быть выделены, прежде чем {0}, а отпуск баланс уже переноса направляются в будущем записи распределения отпуска {1}",
 "Leave cannot be applied/cancelled before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}","Оставьте не могут быть применены / отменены, прежде чем {0}, а отпуск баланс уже переноса направляются в будущем записи распределения отпуска {1}",
-Leave of type {0} cannot be longer than {1},"Оставить типа {0} не может быть больше, чем {1}",
-Leaves,Листья,
+Leave of type {0} cannot be longer than {1},"Отпусков типа {0} не может быть больше, чем {1}",
+Leaves,Отпуски,
 Leaves Allocated Successfully for {0},Отпуск успешно распределен для {0},
-Leaves has been granted sucessfully,Листья были успешно предоставлены,
-Leaves must be allocated in multiples of 0.5,"Листья должны быть выделены несколько 0,5",
-Leaves per Year,Листья в год,
+Leaves has been granted sucessfully,Отпуска были успешно предоставлены,
+Leaves must be allocated in multiples of 0.5,"Отпуска должны быть распределены кратно 0,5",
+Leaves per Year,Отпусков в год,
 Ledger,Регистр,
-Legal,легальный,
+Legal,Легальный,
 Legal Expenses,Судебные издержки,
 Letter Head,Печатный бланк,
 Letter Heads for print templates.,Письмо главы для шаблонов печати.,
@@ -1464,13 +1462,13 @@
 Liability,Ответственность сторон,
 License,Лицензия,
 Lifecycle,Жизненный цикл,
-Limit,предел,
+Limit,Предел,
 Limit Crossed,предел Скрещенные,
 Link to Material Request,Ссылка на запрос материала,
 List of all share transactions,Список всех сделок с акциями,
 List of available Shareholders with folio numbers,Список доступных акционеров с номерами фолио,
 Loading Payment System,Загрузка платежной системы,
-Loan,ссуда,
+Loan,Ссуда,
 Loan Amount cannot exceed Maximum Loan Amount of {0},Сумма кредита не может превышать максимальный Сумма кредита {0},
 Loan Application,Заявка на получение ссуды,
 Loan Management,Управление кредитами,
@@ -1480,9 +1478,9 @@
 Loans and Advances (Assets),Кредиты и авансы (активы),
 Local,Локальные,
 Log,Запись в журнале,
-Logs for maintaining sms delivery status,Журналы для просмотра статуса доставки СМС,
+Logs for maintaining sms delivery status,Журналы для просмотра статуса доставки SMS,
 Lost,Поражений,
-Lost Reasons,Потерянные Причины,
+Lost Reasons,Потерянные причины,
 Low,Низкий,
 Low Sensitivity,Низкая чувствительность,
 Lower Income,Низкий уровень дохода,
@@ -1502,9 +1500,9 @@
 Maintenance Schedule {0} must be cancelled before cancelling this Sales Order,График Обслуживания {0} должен быть отменен до отмены этой Сделки,
 Maintenance Status has to be Cancelled or Completed to Submit,Статус обслуживания должен быть отменен или завершен для отправки,
 Maintenance User,Сотрудник обслуживания,
-Maintenance Visit,Техническое обслуживание Посетить,
+Maintenance Visit,Заявки на техническое обслуживание,
 Maintenance Visit {0} must be cancelled before cancelling this Sales Order,Посещение по Обслуживанию {0} должно быть отменено до отмены этой Сделки,
-Maintenance start date can not be before delivery date for Serial No {0},Техническое обслуживание дата не может быть до даты доставки для Serial No {0},
+Maintenance start date can not be before delivery date for Serial No {0},Дата технического обслуживания не может быть раньше даты поставки {0},
 Make,Сделать,
 Make Payment,Создать платёж,
 Make project from a template.,Сделать проект из шаблона.,
@@ -1514,8 +1512,8 @@
 Manage Sales Partners.,Управление партнерами по сбыту.,
 Manage Sales Person Tree.,Управление деревом менеджеров по продажам.,
 Manage Territory Tree.,Управление деревом территорий.,
-Manage your orders,Управляйте свои заказы,
-Management,управление,
+Manage your orders,Управление вашими заказами,
+Management,Менеджмент,
 Manager,Менеджер,
 Managing Projects,Управление проектами,
 Managing Subcontracting,Управление субподрядом,
@@ -1524,30 +1522,30 @@
 Mandatory field - Get Students From,Обязательное поле — получить учащихся из,
 Mandatory field - Program,Обязательное поле — программа,
 Manufacture,Производство,
-Manufacturer,производитель,
+Manufacturer,Производитель,
 Manufacturer Part Number,Номер партии производителя,
 Manufacturing,Производство,
 Manufacturing Quantity is mandatory,Производство Количество является обязательным,
-Mapping,картографирование,
+Mapping,Картографирование,
 Mapping Type,Тип отображения,
 Mark Absent,Отметка отсутствует,
 Mark Attendance,Пометить посещаемость,
 Mark Half Day,Отметить Полдня,
-Mark Present,Марк Присутствует,
-Marketing,маркетинг,
+Mark Present,Отметить Присутствует,
+Marketing,Маркетинг,
 Marketing Expenses,Маркетинговые расходы,
 Marketplace,Торговая площадка,
-Marketplace Error,Ошибка рынка,
+Marketplace Error,Ошибка торговой площадки,
 Masters,Мастеры,
-Match Payments with Invoices,Соответствие Платежи с счетов-фактур,
-Match non-linked Invoices and Payments.,"Подходим, не связанных Счета и платежи.",
-Material,материал,
+Match Payments with Invoices,Соответствие платежей со счетами,
+Match non-linked Invoices and Payments.,Сопоставьте несвязанные счета и платежи.,
+Material,Материал,
 Material Consumption,Расход материала,
 Material Consumption is not set in Manufacturing Settings.,Потребление материала не задано в настройках производства.,
 Material Receipt,Материал Поступление,
 Material Request,Запрос материала,
 Material Request Date,Дата заявки на материал,
-Material Request No,Материал Запрос Нет,
+Material Request No,Запрос материалов №,
 "Material Request not created, as quantity for Raw Materials already available.","Запрос материала не создан, так как количество сырья уже доступно.",
 Material Request of maximum {0} can be made for Item {1} against Sales Order {2},Максимум {0} заявок на материал может быть сделано для продукта {1} по Сделке {2},
 Material Request to Purchase Order,Материал Заказать орденом,
@@ -1559,7 +1557,7 @@
 Max Exemption Amount cannot be greater than maximum exemption amount {0} of Tax Exemption Category {1},Максимальная сумма освобождения не может превышать максимальную сумму освобождения {0} для категории налоговых освобождений {1},
 Max benefits should be greater than zero to dispense benefits,"Максимальные преимущества должны быть больше нуля, чтобы распределять преимущества",
 Max discount allowed for item: {0} is {1}%,Макс скидка позволило пункта: {0} {1}%,
-Max: {0},Max: {0},
+Max: {0},Макс.: {0},
 Maximum Samples - {0} can be retained for Batch {1} and Item {2}.,Максимальные образцы - {0} могут сохраняться для Batch {1} и Item {2}.,
 Maximum Samples - {0} have already been retained for Batch {1} and Item {2} in Batch {3}.,Максимальные образцы - {0} уже сохранены для Batch {1} и Item {2} в пакете {3}.,
 Maximum amount eligible for the component {0} exceeds {1},"Максимальное количество, подходящее для компонента {0}, превышает {1}",
@@ -1577,14 +1575,14 @@
 Member Activity,Активность участника,
 Member ID,ID пользователя,
 Member Name,Имя участника,
-Member information.,Информация о членах.,
-Membership,членство,
+Member information.,Информация об участниках.,
+Membership,Членство,
 Membership Details,Сведения о членстве,
-Membership ID,Идентификатор членства,
+Membership ID,Идентификатор участника,
 Membership Type,Тип членства,
 Memebership Details,Меморандум,
 Memebership Type Details,Информация о типе памяти,
-Merge,сливаться,
+Merge,Объеденить,
 Merge Account,Объединить учетную запись,
 Merge with Existing Account,Слияние с существующей учетной записью,
 "Merging is only possible if following properties are same in both records. Is Group, Root Type, Company","Объединение возможно только, если следующие свойства такие же, как в отчетах. Есть группа, корневого типа, компания",
@@ -1596,7 +1594,7 @@
 Middle Name (Optional),Отчество (необязательно),
 Min Amt can not be greater than Max Amt,Min Amt не может быть больше Max Amt,
 Min Qty can not be greater than Max Qty,"Мин Кол-во не может быть больше, чем максимальное Кол-во",
-Minimum Lead Age (Days),Минимальный срок Обращения (в днях),
+Minimum Lead Age (Days),Минимальный срок лида (в днях),
 Miscellaneous Expenses,Прочие расходы,
 Missing Currency Exchange Rates for {0},Пропавших без вести Курсы валют на {0},
 Missing email template for dispatch. Please set one in Delivery Settings.,Отсутствует шаблон электронной почты для отправки. Установите его в настройках доставки.,
@@ -1606,7 +1604,7 @@
 Mode of Transport,Вид транспорта,
 Mode of Transportation,Способ транспортировки,
 Mode of payment is required to make a payment,Способ оплаты требуется произвести оплату,
-Model,модель,
+Model,Модель,
 Moderate Sensitivity,Умеренная Чувствительность,
 Monday,Понедельник,
 Monthly,Ежемесячно,
@@ -1617,7 +1615,7 @@
 More than one selection for {0} not allowed,Не допускается более одного выбора для {0},
 More...,Больше...,
 Motion Picture & Video,Кино- и видеостудия,
-Move,перемещение,
+Move,Переместить,
 Move Item,Переместить продукт,
 Multi Currency,Мультивалютность,
 Multiple Item prices.,Гибкая цена продукта,
@@ -1625,13 +1623,13 @@
 "Multiple Price Rules exists with same criteria, please resolve conflict by assigning priority. Price Rules: {0}","Несколько Цена Правила существует с теми же критериями, пожалуйста разрешить конфликт путем присвоения приоритета. Цена Правила: {0}",
 Multiple Variants,Несколько вариантов,
 Multiple fiscal years exist for the date {0}. Please set company in Fiscal Year,"Несколько финансовых лет существуют на дату {0}. Пожалуйста, установите компанию в финансовый год",
-Music,музыка,
+Music,Музыка,
 My Account,Мой аккаунт,
-Name error: {0},Ошибка Имя: {0},
+Name error: {0},Имя ошибки: {0},
 Name of new Account. Note: Please don't create accounts for Customers and Suppliers,"Название нового счёта. Примечание: Пожалуйста, не создавайте счета для клиентов и поставщиков",
 Name or Email is mandatory,Имя или адрес электронной почты является обязательным,
 Nature Of Supplies,Природа поставок,
-Navigating,навигационный,
+Navigating,Навигационный,
 Needs Analysis,Анализ потребностей,
 Negative Quantity is not allowed,Отрицательное количество недопустимо,
 Negative Valuation Rate is not allowed,Отрицательный Оценка курс не допускается,
@@ -1639,7 +1637,7 @@
 Net Asset value as on,Чистая стоимость активов на,
 Net Cash from Financing,Чистые денежные средства от финансовой деятельности,
 Net Cash from Investing,Чистые денежные средства от инвестиционной деятельности,
-Net Cash from Operations,Чистые денежные средства от операционной,
+Net Cash from Operations,Чистые денежные средства от операций,
 Net Change in Accounts Payable,Чистое изменение кредиторской задолженности,
 Net Change in Accounts Receivable,Чистое изменение дебиторской задолженности,
 Net Change in Cash,Чистое изменение денежных средств,
@@ -1648,20 +1646,20 @@
 Net Change in Inventory,Чистое изменение в запасах,
 Net ITC Available(A) - (B),Чистый ITC Доступен (A) - (B),
 Net Pay,Чистая оплата,
-Net Pay cannot be less than 0,"Net Pay не может быть меньше, чем 0",
+Net Pay cannot be less than 0,"Чистая оплата не может быть меньше, чем 0",
 Net Profit,Чистая прибыль,
 Net Salary Amount,Чистая сумма зарплаты,
-Net Total,Чистая Всего,
+Net Total,Чистая всего,
 Net pay cannot be negative,Чистая зарплата не может быть отрицательным,
 New Account Name,Новое название счёта,
 New Address,Новый адрес,
-New BOM,Новая ВМ,
+New BOM,Новая спецификация,
 New Batch ID (Optional),Новый идентификатор партии (необязательно),
 New Batch Qty,Новое количество партий,
-New Company,Новая Компания,
-New Cost Center Name,Новый Центр Стоимость Имя,
-New Customer Revenue,Новый Выручка клиентов,
-New Customers,новые клиенты,
+New Company,Новая компания,
+New Cost Center Name,Название нового центра затрат,
+New Customer Revenue,Доход от нового клиента,
+New Customers,Новые клиенты,
 New Department,Новый отдел,
 New Employee,Новый сотрудник,
 New Location,Новое место,
@@ -1676,10 +1674,10 @@
 Newsletters,Информационная рассылка,
 Newspaper Publishers,Информационное издательство,
 Next,Далее,
-Next Contact By cannot be same as the Lead Email Address,Следующий контакт не может совпадать с адресом электронной почты Обращения,
+Next Contact By cannot be same as the Lead Email Address,Следующий контакт не может совпадать с адресом электронной почты лида,
 Next Contact Date cannot be in the past,Дата следующего контакта не может быть в прошлом,
 Next Steps,Следующие шаги,
-No Action,Бездействие,
+No Action,Нет действий,
 No Customers yet!,Клиентов еще нет!,
 No Data,Нет данных,
 No Delivery Note selected for Customer {},Нет примечания о доставке для клиента {},
@@ -1692,7 +1690,7 @@
 No Items with Bill of Materials to Manufacture,Нет предметов с Биллом материалов не Manufacture,
 No Items with Bill of Materials.,Нет предметов с ведомостью материалов.,
 No Permission,Нет разрешения,
-No Remarks,Нет Замечания,
+No Remarks,Нет замечаний,
 No Result to submit,Нет результатов для отправки,
 No Salary Structure assigned for Employee {0} on given date {1},"Нет структуры заработной платы, назначенной для сотрудника {0} в данную дату {1}",
 No Staffing Plans found for this Designation,Никаких кадровых планов для этого обозначения,
@@ -1717,26 +1715,26 @@
 No products found,Продукты не найдены,
 No products found.,Не найдено продуктов.,
 No record found,Не запись не найдено,
-No records found in the Invoice table,Не записи не найдено в таблице счетов,
-No records found in the Payment table,Не записи не найдено в таблице оплаты,
+No records found in the Invoice table,В таблице счетов не найдено ни одной записи,
+No records found in the Payment table,В таблице платежей не найдено ни одной записи,
 No replies from,Нет ответов от,
 No salary slip found to submit for the above selected criteria OR salary slip already submitted,Никакой оговорки о зарплате не было найдено для вышеуказанных выбранных критериев.,
 No tasks,Нет задач,
 No time sheets,Нет табелей,
 No values,Нет значений,
 No {0} found for Inter Company Transactions.,Нет {0} найдено для транзакций Inter Company.,
-Non GST Inward Supplies,Не входящие в GST поставки,
+Non GST Inward Supplies,Не входящие в НДС поставки,
 Non Profit,Некоммерческое предприятие,
-Non Profit (beta),Непрофит (бета),
-Non-GST outward supplies,Внешние поставки без GST,
+Non Profit (beta),Некоммерческое предприятие (бета),
+Non-GST outward supplies,Внешние поставки без НДС,
 Non-Group to Group,Non-группы к группе,
 None,Никто,
 None of the items have any change in quantity or value.,Ни одному продукту не изменено количество или объём.,
-Nos,кол-во,
-Not Available,Не доступен,
-Not Marked,без маркировки,
+Nos,Кол-во,
+Not Available,Недоступен,
+Not Marked,Без маркировки,
 Not Paid and Not Delivered,Не оплачен и не доставлен,
-Not Permitted,Не Допустимая,
+Not Permitted,Нет допуска,
 Not Started,Не начато,
 Not active,Не действует,
 Not allow to set alternative item for the item {0},Не разрешить установку альтернативного элемента для элемента {0},
@@ -1746,7 +1744,7 @@
 Not permitted for {0},Не допускается для {0},
 "Not permitted, configure Lab Test Template as required","Не разрешено, настройте шаблон лабораторного тестирования по мере необходимости.",
 Not permitted. Please disable the Service Unit Type,Не разрешено. Отключите тип устройства обслуживания,
-Note: Due / Reference Date exceeds allowed customer credit days by {0} day(s),Примечание: Из-за / Reference Дата превышает разрешенный лимит клиент дня на {0} сутки (ы),
+Note: Due / Reference Date exceeds allowed customer credit days by {0} day(s),Примечание: Дата платежа / Исходная дата превышает допустимый кредитный день клиента на {0} дн.,
 Note: Item {0} entered multiple times,Примечание: Продукт {0} имеет несколько вхождений,
 Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified,"Примечание: Оплата Вступление не будет создана, так как ""Наличные или Банковский счет"" не был указан",
 Note: System will not check over-delivery and over-booking for Item {0} as quantity or amount is 0,Примечание: Система не будет проверять по-доставки и избыточного бронирования по пункту {0} как количестве 0,
@@ -1757,7 +1755,7 @@
 Nothing is included in gross,Ничто не входит в валовой,
 Nothing more to show.,Ничего больше не показывать.,
 Nothing to change,Нечего менять,
-Notice Period,Срок Уведомления,
+Notice Period,Период уведомления,
 Notify Customers via Email,Уведомлять клиентов по электронной почте,
 Number,Число,
 Number of Depreciations Booked cannot be greater than Total Number of Depreciations,"Количество не Забронированный отчислений на амортизацию может быть больше, чем Общее количество отчислений на амортизацию",
@@ -1766,23 +1764,23 @@
 "Number of new Account, it will be included in the account name as a prefix","Номер новой учетной записи, она будет включена в имя учетной записи в качестве префикса",
 "Number of new Cost Center, it will be included in the cost center name as a prefix","Количество нового МВЗ, оно будет включено в название МВЗ в качестве префикса",
 Number of root accounts cannot be less than 4,Количество корневых учетных записей не может быть меньше 4,
-Odometer,одометр,
+Odometer,Одометр,
 Office Equipments,Оборудование офиса,
 Office Maintenance Expenses,Эксплуатационные расходы на офис,
 Office Rent,Аренда площади для офиса,
 On Hold,На удерживании,
-On Net Total,On Net Всего,
+On Net Total,Чистая сумма,
 One customer can be part of only single Loyalty Program.,Один клиент может быть частью единой программы лояльности.,
 Online Auctions,Аукционы в Интернете,
 Only Leave Applications with status 'Approved' and 'Rejected' can be submitted,Только оставьте приложения со статусом «Одобрено» и «Отклонено» могут быть представлены,
 "Only the Student Applicant with the status ""Approved"" will be selected in the table below.",В таблице ниже будет выбран только кандидат-студент со статусом «Approved».,
 Only users with {0} role can register on Marketplace,Только пользователи с ролью {0} могут зарегистрироваться на Marketplace,
-Open BOM {0},Открыть ВОМ {0},
+Open BOM {0},Открыть спецификацию {0},
 Open Item {0},Открыть продукт {0},
 Open Notifications,Открытые уведомления,
 Open Orders,Открытые заказы,
 Open a new ticket,Открыть новый билет,
-Opening,открытие,
+Opening,Открытие,
 Opening (Cr),Начальное сальдо (кредит),
 Opening (Dr),Начальное сальдо (дебет),
 Opening Accounting Balance,Начальный бухгалтерский баланс,
@@ -1794,25 +1792,25 @@
 Opening Date should be before Closing Date,Дата открытия должна быть ранее Даты закрытия,
 Opening Entry Journal,Журнал открытия журнала,
 Opening Invoice Creation Tool,Инструмент для открытия счета,
-Opening Invoice Item,Открытие счета-фактуры,
-Opening Invoices,Открытие счетов-фактур,
-Opening Invoices Summary,Открытие счета-фактуры,
+Opening Invoice Item,Открытие счета,
+Opening Invoices,Открытие счетов,
+Opening Invoices Summary,Сводка по открытию счетов,
 Opening Qty,Открытое кол-во,
 Opening Stock,Начальный запас,
 Opening Stock Balance,Открытие баланса запасов,
 Opening Value,Начальное значение,
-Opening {0} Invoice created,Открытие {0} счет-фактуры,
+Opening {0} Invoice created,Открытие {0} счета создано,
 Operation,Операция,
 Operation Time must be greater than 0 for Operation {0},"Время работы должно быть больше, чем 0 для операции {0}",
-"Operation {0} longer than any available working hours in workstation {1}, break down the operation into multiple operations","Операция {0} больше, чем каких-либо имеющихся рабочих часов в рабочей станции {1}, сломать операции в несколько операций",
+"Operation {0} longer than any available working hours in workstation {1}, break down the operation into multiple operations","Операция {0} больше, чем имеющихся часов на рабочем месте{1}, разбить операции на более мелкие",
 Operations,Эксплуатация,
 Operations cannot be left blank,"Операции, не может быть оставлено пустым",
 Opp Count,Счетчик Opp,
-Opp/Lead %,"Выявления/Обращения, %",
+Opp/Lead %,"Выявления/Лиды, %",
 Opportunities,Возможности,
-Opportunities by lead source,Выявления из источника обращения,
-Opportunity,Выявление,
-Opportunity Amount,Количество Выявления,
+Opportunities by lead source,Возможность из источника лидов,
+Opportunity,Возможность,
+Opportunity Amount,Сумма возможности,
 Optional Holiday List not set for leave period {0},Необязательный список праздников не установлен для периода отпуска {0},
 "Optional. Sets company's default currency, if not specified.","Необязательный. Устанавливает по умолчанию валюту компании, если не указано.",
 Optional. This setting will be used to filter in various transactions.,Факультативно. Эта установка будет использоваться для фильтрации в различных сделок.,
@@ -1821,29 +1819,29 @@
 Order Entry,Порядок въезда,
 Order Value,Ценность заказа,
 Order rescheduled for sync,Заказ перенесен на синхронизацию,
-Order/Quot %,Заказ / Котировка%,
+Order/Quot %,Заказ/Котировка %,
 Ordered,В обработке,
 Ordered Qty,Заказал кол-во,
 "Ordered Qty: Quantity ordered for purchase, but not received.","Заказал Количество: Количество заказал для покупки, но не получил.",
-Orders,заказы,
+Orders,Заказы,
 Orders released for production.,"Заказы, выпущенные для производства.",
-Organization,организация,
+Organization,Организация,
 Organization Name,Название организации,
 Other,Другое,
 Other Reports,Другие отчеты,
 "Other outward supplies(Nil rated,Exempted)","Другие внешние поставки (Ноль оценили, освобождены)",
 Others,Другое,
 Out Qty,Из кол-ва,
-Out Value,аута,
+Out Value,Выходное значение,
 Out of Order,Вышел из строя,
-Outgoing,исходящий,
-Outstanding,выдающийся,
+Outgoing,Исходящие,
+Outstanding,Выдающийся,
 Outstanding Amount,Непогашенная сумма,
 Outstanding Amt,Выдающийся Amt,
 Outstanding Cheques and Deposits to clear,"Выдающиеся чеки и депозиты, чтобы очистить",
 Outstanding for {0} cannot be less than zero ({1}),Выдающийся для {0} не может быть меньше нуля ({1}),
 Outward taxable supplies(zero rated),Исходящие облагаемые налогом поставки (нулевой рейтинг),
-Overdue,просроченный,
+Overdue,Просроченные,
 Overlap in scoring between {0} and {1},Перекрытие при подсчете между {0} и {1},
 Overlapping conditions found between:,Перекрытие условия найдено между:,
 Owner,Владелец,
@@ -1855,7 +1853,7 @@
 POS Settings,Настройки POS,
 Packed quantity must equal quantity for Item {0} in row {1},Упакованное количество должно соответствовать количеству  продукта  {0} в строке {1},
 Packing Slip,Упаковочный лист,
-Packing Slip(s) cancelled,Упаковочный лист (ы) отменены,
+Packing Slip(s) cancelled,Упаковочный лист(ы) отменены,
 Paid,Оплачено,
 Paid Amount,Оплаченная сумма,
 Paid Amount cannot be greater than total negative outstanding amount {0},Уплаченная сумма не может быть больше суммарного отрицательного непогашенной {0},
@@ -1869,12 +1867,12 @@
 Partially Received,Частично получено,
 Party,Партия,
 Party Name,Название группы,
-Party Type,Тип Группы,
+Party Type,Тип группы,
 Party Type and Party is mandatory for {0} account,Тип и сторона партии обязательны для учетной записи {0},
 Party Type is mandatory,Тип партии является обязательным,
 Party is mandatory,Партия является обязательным,
 Password,Пароль,
-Password policy for Salary Slips is not set,Политика паролей для Salary Slips не установлена,
+Password policy for Salary Slips is not set,Политика паролей для зарплатных ведомостей не установлена,
 Past Due Date,Прошлая дата погашения,
 Patient,Пациент,
 Patient Appointment,Назначение пациента,
@@ -1882,7 +1880,7 @@
 Patient not found,Пациент не найден,
 Pay Remaining,Оплатить остаток,
 Pay {0} {1},Оплатить {0} {1},
-Payable,к оплате,
+Payable,К оплате,
 Payable Account,Счёт оплаты,
 Payable Amount,Сумма к оплате,
 Payment,Оплата,
@@ -1900,7 +1898,7 @@
 Payment Failed. Please check your GoCardless Account for more details,"Платеж не прошел. Пожалуйста, проверьте свою учетную запись GoCardless для получения более подробной информации.",
 Payment Gateway,Платежный шлюз,
 "Payment Gateway Account not created, please create one manually.","Payment Gateway Account не создан, создайте его вручную.",
-Payment Gateway Name,Имя платежного шлюза,
+Payment Gateway Name,Название платежного шлюза,
 Payment Mode,Режим платежа,
 Payment Receipt Note,Оплата Получение Примечание,
 Payment Request,Платежная заявка,
@@ -1919,43 +1917,43 @@
 Payroll,Начисление заработной платы,
 Payroll Number,Номер платежной ведомости,
 Payroll Payable,Расчет заработной платы оплачивается,
-Payslip,листка,
+Payslip,Расчетная ведомость,
 Pending Activities,В ожидании Деятельность,
 Pending Amount,В ожидании Сумма,
 Pending Leaves,Ожидающие листья,
 Pending Qty,В ожидании кол-во,
 Pending Quantity,Количество в ожидании,
-Pending Review,В ожидании отзыв,
+Pending Review,В ожидании отзыва,
 Pending activities for today,В ожидании деятельность на сегодняшний день,
 Pension Funds,Пенсионные фонды,
 Percentage Allocation should be equal to 100%,Процент Распределение должно быть равно 100%,
 Perception Analysis,Анализ восприятия,
-Period,Период обновления,
+Period,Период,
 Period Closing Entry,Период закрытия входа,
 Period Closing Voucher,Период Окончание Ваучер,
-Periodicity,периодичность,
-Personal Details,Личные Данные,
+Periodicity,Периодичность,
+Personal Details,Личные данные,
 Pharmaceutical,Фармацевтический,
 Pharmaceuticals,Фармацевтика,
-Physician,врач,
+Physician,Врач,
 Piecework,Сдельная работа,
-Pincode,Pincode,
+Pincode,PIN код,
 Place Of Supply (State/UT),Место поставки (штат / UT),
 Place Order,Разместить заказ,
 Plan Name,Название плана,
 Plan for maintenance visits.,Запланируйте для посещения технического обслуживания.,
 Planned Qty,Планируемое кол-во,
 "Planned Qty: Quantity, for which, Work Order has been raised, but is pending to be manufactured.","Запланированное кол-во: количество, для которого было задано рабочее задание, но ожидается его изготовление.",
-Planning,планирование,
+Planning,Планирование,
 Plants and Machineries,Растения и Механизмов,
 Please Set Supplier Group in Buying Settings.,Установите группу поставщиков в разделе «Настройки покупок».,
-Please add a Temporary Opening account in Chart of Accounts,"Пожалуйста, добавьте временный вступительный счет в План счетов",
-Please add the account to root level Company - ,"Пожалуйста, добавьте учетную запись на корневой уровень компании -",
+Please add a Temporary Opening account in Chart of Accounts,"Пожалуйста, добавьте временный вступительный счет в план счетов",
+Please add the account to root level Company - ,"Пожалуйста, добавьте счет на корневой уровень компании -",
 Please add the remaining benefits {0} to any of the existing component,Добавьте оставшиеся преимущества {0} к любому из существующих компонентов,
 Please check Multi Currency option to allow accounts with other currency,"Пожалуйста, проверьте мультивалютный вариант, позволяющий счета другой валюте",
-Please click on 'Generate Schedule',"Пожалуйста, нажмите на кнопку ""Generate Расписание""",
-Please click on 'Generate Schedule' to fetch Serial No added for Item {0},"Пожалуйста, нажмите на кнопку ""Generate Расписание"", чтобы принести Серийный номер добавлен для Пункт {0}",
-Please click on 'Generate Schedule' to get schedule,"Пожалуйста, нажмите на кнопку ""Generate Расписание"", чтобы получить график",
+Please click on 'Generate Schedule',"Пожалуйста, нажмите на кнопку 'Создать расписание'",
+Please click on 'Generate Schedule' to fetch Serial No added for Item {0},"Пожалуйста, нажмите на кнопку ""Создать расписание"", чтобы принести Серийный номер добавлен для Пункт {0}",
+Please click on 'Generate Schedule' to get schedule,"Пожалуйста, нажмите на кнопку ""Создать расписание"", чтобы получить график",
 Please confirm once you have completed your training,"Пожалуйста, подтвердите, как только вы закончили обучение",
 Please create purchase receipt or purchase invoice for the item {0},Создайте квитанцию о покупке или фактуру покупки для товара {0},
 Please define grade for Threshold 0%,"Пожалуйста, определите оценку для Threshold 0%",
@@ -1963,19 +1961,19 @@
 Please enable Applicable on Purchase Order and Applicable on Booking Actual Expenses,"Пожалуйста, включите Применимо по заказу на поставку и применимо при бронировании Фактические расходы",
 Please enable default incoming account before creating Daily Work Summary Group,"Включите учетную запись по умолчанию, прежде чем создавать сводную группу ежедневных работ",
 Please enable pop-ups,"Пожалуйста, включите всплывающие окна",
-Please enter 'Is Subcontracted' as Yes or No,"Пожалуйста, введите 'Является субподряду "", как Да или Нет",
+Please enter 'Is Subcontracted' as Yes or No,"Пожалуйста, введите 'Является субподрядом', как Да или Нет",
 Please enter API Consumer Key,Введите API-адрес потребителя,
 Please enter API Consumer Secret,"Пожалуйста, введите секретный раздел API",
 Please enter Account for Change Amount,"Пожалуйста, введите счет для изменения высоты",
 Please enter Approving Role or Approving User,"Пожалуйста, введите утверждении роли или утверждении Пользователь",
 Please enter Cost Center,"Пожалуйста, введите МВЗ",
 Please enter Delivery Date,Укажите дату поставки,
-Please enter Employee Id of this sales person,"Пожалуйста, введите Employee Id этого менеджера по продажам",
+Please enter Employee Id of this sales person,"Пожалуйста, введите идентификатор сотрудника этого продавца",
 Please enter Expense Account,"Пожалуйста, введите Expense счет",
 Please enter Item Code to get Batch Number,"Пожалуйста, введите код товара, чтобы получить номер партии",
 Please enter Item Code to get batch no,"Пожалуйста, введите Код товара, чтобы получить партию не",
 Please enter Item first,"Пожалуйста, введите сначала продукт",
-Please enter Maintaince Details first,"Пожалуйста, введите Maintaince Подробности",
+Please enter Maintaince Details first,"Пожалуйста, введите детали обслуживания",
 Please enter Planned Qty for Item {0} at row {1},"Пожалуйста, введите Запланированное Количество по пункту {0} в строке {1}",
 Please enter Preferred Contact Email,"Пожалуйста, введите предпочитаемый адрес электронной почты Контакта",
 Please enter Production Item first,"Пожалуйста, сначала введите производство продукта",
@@ -2007,7 +2005,7 @@
 Please mention Round Off Account in Company,"Пожалуйста, укажите округлить счет в компании",
 Please mention Round Off Cost Center in Company,"Пожалуйста, укажите округлить МВЗ в компании",
 Please mention no of visits required,"Пожалуйста, укажите кол-во посещений, необходимых",
-Please mention the Lead Name in Lead {0},"Пожалуйста, укажите Имя в Обращении {0}",
+Please mention the Lead Name in Lead {0},"Пожалуйста, укажите имя в лиде {0}",
 Please pull items from Delivery Note,Пожалуйста вытяните продукты из транспортной накладной,
 Please register the SIREN number in the company information file,"Пожалуйста, зарегистрируйте номер SIREN в файле информации о компании",
 Please remove this Invoice {0} from C-Form {1},"Пожалуйста, удалите этот счет {0} из C-Form {1}",
@@ -2023,14 +2021,14 @@
 Please select Company,"Пожалуйста, выберите компанию",
 Please select Company and Designation,Выберите компанию и обозначение,
 Please select Company and Posting Date to getting entries,Выберите компанию и дату проводки для получения записей.,
-Please select Company first,"Пожалуйста, выберите КОМПАНИЯ Первый",
-Please select Completion Date for Completed Asset Maintenance Log,Выберите Дата завершения для завершенного журнала обслуживания активов,
-Please select Completion Date for Completed Repair,"Пожалуйста, выберите Дата завершения для завершенного ремонта",
-Please select Course,Выберите курс,
-Please select Drug,Выберите лекарство,
-Please select Employee,Выберите Сотрудник,
+Please select Company first,"Пожалуйста, выберите первую компанию",
+Please select Completion Date for Completed Asset Maintenance Log,Выберите дата завершения для журнала обслуживания активов,
+Please select Completion Date for Completed Repair,"Пожалуйста, выберите дату завершения ремонта",
+Please select Course,"Пожалуйста, выберите курс",
+Please select Drug,"Пожалуйста, выберите лекарство",
+Please select Employee,"Пожалуйста, выберите сотрудника",
 Please select Existing Company for creating Chart of Accounts,"Пожалуйста, выберите Существующую компанию для создания плана счетов",
-Please select Healthcare Service,Выберите Healthcare Service,
+Please select Healthcare Service,Выберите услуги здравоохранение,
 "Please select Item where ""Is Stock Item"" is ""No"" and ""Is Sales Item"" is ""Yes"" and there is no other Product Bundle","Пожалуйста, выберите продукт со значениями ""Складируемый продукт"" - ""Нет"", ""Продаваемый продукт"" - ""Да"" и без связи с продуктовым набором.",
 Please select Maintenance Status as Completed or remove Completion Date,Выберите «Состояние обслуживания» как «Завершено» или «Дата завершения»,
 Please select Party Type first,"Пожалуйста, выберите партии первого типа",
@@ -2039,8 +2037,8 @@
 Please select Posting Date before selecting Party,"Пожалуйста, выберите Дата публикации, прежде чем выбрать партию",
 Please select Posting Date first,"Пожалуйста, выберите проводки Дата первого",
 Please select Price List,"Пожалуйста, выберите прайс-лист",
-Please select Program,Выберите программу,
-Please select Qty against item {0},Выберите кол-во продукта {0},
+Please select Program,"Пожалуйста, выберите программу",
+Please select Qty against item {0},"Пожалуйста, выберите количество продуктов {0}",
 Please select Sample Retention Warehouse in Stock Settings first,Сначала выберите «Хранилище хранения образцов» в разделе «Настройки запаса»,
 Please select Start Date and End Date for Item {0},"Пожалуйста, выберите дату начала и дату окончания Пункт {0}",
 Please select Student Admission which is mandatory for the paid student applicant,"Пожалуйста, выберите Вход для студентов, который является обязательным для оплачиваемого студента",
@@ -2048,36 +2046,36 @@
 Please select a Batch for Item {0}. Unable to find a single batch that fulfills this requirement,"Выберите партию для продукта {0}. Не удалось найти такую, которая удовлетворяет этому требованию.",
 Please select a Company,"Пожалуйста, выберите компанию",
 Please select a batch,Выберите пакет,
-Please select a csv file,Выберите файл CSV,
+Please select a csv file,"Пожалуйста, выберите файл CSV",
 Please select a field to edit from numpad,Выберите поле для редактирования из numpad,
-Please select a table,Выберите таблицу,
-Please select a valid Date,Пожалуйста выберите правильную дату,
+Please select a table,"Пожалуйста, выберите таблицу",
+Please select a valid Date,"Пожалуйста, выберите правильную дату",
 Please select a value for {0} quotation_to {1},"Пожалуйста, выберите значение для {0} предложение_для {1}",
-Please select a warehouse,Выберите склад,
+Please select a warehouse,"Пожалуйста, выберите склад",
 Please select at least one domain.,Выберите хотя бы один домен.,
 Please select correct account,"Пожалуйста, выберите правильный счет",
 Please select date,"Пожалуйста, выберите даты",
 Please select item code,"Пожалуйста, выберите код продукта",
 Please select month and year,"Пожалуйста, выберите месяц и год",
 Please select prefix first,"Пожалуйста, выберите префикс первым",
-Please select the Company,Выберите компанию,
+Please select the Company,"Пожалуйста, выберите компанию",
 Please select the Multiple Tier Program type for more than one collection rules.,Выберите несколько типов программ для нескольких правил сбора.,
 Please select the assessment group other than 'All Assessment Groups',"Выберите группу оценки, отличную от «Все группы оценки»,",
 Please select the document type first,"Пожалуйста, выберите тип документа сначала",
 Please select weekly off day,"Пожалуйста, выберите в неделю выходной",
 Please select {0},"Пожалуйста, выберите {0}",
 Please select {0} first,"Пожалуйста, выберите {0} первый",
-Please set 'Apply Additional Discount On',"Пожалуйста, установите &quot;Применить Дополнительная Скидка On &#39;",
+Please set 'Apply Additional Discount On',"Пожалуйста, установите &quot;Применить дополнительную скидку на &#39;",
 Please set 'Asset Depreciation Cost Center' in Company {0},"Пожалуйста, установите &quot;активов Амортизация затрат по МВЗ&quot; в компании {0}",
-Please set 'Gain/Loss Account on Asset Disposal' in Company {0},"Пожалуйста, установите &quot;прибыль / убыток Счет по обращению с отходами актива в компании {0}",
+Please set 'Gain/Loss Account on Asset Disposal' in Company {0},"Пожалуйста, установите &quot;прибыль / убыток Счет по лиду с отходами актива в компании {0}",
 Please set Account in Warehouse {0} or Default Inventory Account in Company {1},Укажите учетную запись в хранилище {0} или учетную запись инвентаризации по умолчанию в компании {1},
-Please set B2C Limit in GST Settings.,Установите B2C Limit в настройках GST.,
+Please set B2C Limit in GST Settings.,Установите B2C ограничение в настройках НДС.,
 Please set Company,Укажите компанию,
 Please set Company filter blank if Group By is 'Company',"Пожалуйста, установите фильтр компании blank, если Group By является «Company»",
 Please set Default Payroll Payable Account in Company {0},"Пожалуйста, установите по умолчанию Payroll расчётный счёт в компании {0}",
 Please set Depreciation related Accounts in Asset Category {0} or Company {1},"Пожалуйста, установите Амортизация соответствующих счетов в Asset Категория {0} или компании {1}",
 Please set Email Address,"Пожалуйста, установите адрес электронной почты",
-Please set GST Accounts in GST Settings,"Пожалуйста, установите учетные записи GST в настройках GST",
+Please set GST Accounts in GST Settings,"Пожалуйста, установите счета НДС в настройках НДС",
 Please set Hotel Room Rate on {},"Пожалуйста, установите рейтинг номера в отеле {}",
 Please set Number of Depreciations Booked,"Пожалуйста, установите Количество отчислений на амортизацию бронирования",
 Please set Unrealized Exchange Gain/Loss Account in Company {0},Установите Unrealized Exchange Gain / Loss Account в компании {0},
@@ -2122,7 +2120,7 @@
 Point-of-Sale,Торговая точка,
 Point-of-Sale Profile,Точка-в-продажи профиля,
 Portal,Портал,
-Portal Settings,портал Настройки,
+Portal Settings,Настройки портала,
 Possible Supplier,Возможный поставщик,
 Postal Expenses,Почтовые расходы,
 Posting Date,Дата публикации,
@@ -2133,9 +2131,9 @@
 Potential opportunities for selling.,Потенциальные возможности для продажи.,
 Practitioner Schedule,Расписание практикующих,
 Pre Sales,Предпродажа,
-Preference,предпочтение,
+Preference,Предпочтение,
 Prescribed Procedures,Предписанные процедуры,
-Prescription,давность,
+Prescription,Рецепт,
 Prescription Dosage,Дозировка по рецепту,
 Prescription Duration,Продолжительность рецепта,
 Prescriptions,Предписания,
@@ -2146,14 +2144,14 @@
 Previous Financial Year is not closed,Предыдущий финансовый год не закрыт,
 Price,Цена,
 Price List,Прайс-лист,
-Price List Currency not selected,Прайс-лист Обмен не выбран,
+Price List Currency not selected,Валюта прайс-листа не выбрана,
 Price List Rate,Прайс-лист Оценить,
 Price List master.,Мастер Прайс-лист.,
 Price List must be applicable for Buying or Selling,Прайс-лист должен быть применим для покупки или продажи,
 Price List {0} is disabled or does not exist,Прайс-лист {0} отключен или не существует,
 Price or product discount slabs are required,Требуется цена или скидка на продукцию,
-Pricing,ценообразование,
-Pricing Rule,Цены Правило,
+Pricing,Ценообразование,
+Pricing Rule,Правила ценообразования,
 "Pricing Rule is first selected based on 'Apply On' field, which can be Item, Item Group or Brand.","Правило ценообразования сначала выбирается на основе поля «Применить на», значением которого может быть Позиция, Группа Позиций, Торговая Марка.",
 "Pricing Rule is made to overwrite Price List / define discount percentage, based on some criteria.","Правило ценообразования делается для того, чтобы изменить Прайс-лист / определить процент скидки, основанный на некоторых критериях.",
 Pricing Rule {0} is updated,Правило ценообразования {0} обновлено,
@@ -2171,8 +2169,8 @@
 Printing and Branding,Печать и брендинг,
 Private Equity,Частные капиталовложения,
 Privilege Leave,Привилегированный Оставить,
-Probation,испытательный срок,
-Probationary Period,Испытательный срок,
+Probation,Испытательный срок,
+Probationary Period,Испытательный период,
 Procedure,Процедура,
 Process Day Book Data,Обработка данных дневника,
 Process Master Data,Обработка основных данных,
@@ -2185,20 +2183,20 @@
 Product,Продукт,
 Product Bundle,Продуктовый набор,
 Product Search,Поиск продукта,
-Production,производство,
+Production,Производство,
 Production Item,Производство товара,
 Products,Продукты,
 Profit and Loss,Прибыль и убытки,
 Profit for the year,Прибыль за год,
-Program,программа,
+Program,Программа,
 Program in the Fee Structure and Student Group {0} are different.,Программа в структуре вознаграждения и студенческой группе {0} отличается.,
 Program {0} does not exist.,Программа {0} не существует.,
-Program: ,Программа:,
+Program: ,Программа: ,
 Progress % for a task cannot be more than 100.,Готовность задачи не может превышать 100%.,
-Project Collaboration Invitation,Сотрудничество Приглашение проекта,
+Project Collaboration Invitation,Приглашение к сотрудничеству в проекте,
 Project Id,Идентификатор проекта,
 Project Manager,Менеджер проектов,
-Project Name,название проекта,
+Project Name,Название проекта,
 Project Start Date,Дата начала проекта,
 Project Status,Статус проекта,
 Project Summary for {0},Краткое описание проекта для {0},
@@ -2206,7 +2204,7 @@
 Project Value,Значимость проекта,
 Project activity / task.,Проектная деятельность / задачи.,
 Project master.,Мастер проекта.,
-Project-wise data is not available for Quotation,Данные Проекта не доступны для Предложения,
+Project-wise data is not available for Quotation,Данные проекта не доступны для предложения,
 Projected,Проектированный,
 Projected Qty,Прогнозируемое кол-во,
 Projected Quantity Formula,Формула прогнозируемого количества,
@@ -2220,35 +2218,35 @@
 Publications,Публикации,
 Publish Items on Website,Опубликовать товары на сайте,
 Published,Опубликовано,
-Publishing,Издательское дело,
+Publishing,Публикация,
 Purchase,Купить,
 Purchase Amount,Сумма покупки,
 Purchase Date,Дата покупки,
-Purchase Invoice,Покупка Счет,
+Purchase Invoice,Счет на покупку,
 Purchase Invoice {0} is already submitted,Счет на закупку {0} уже проведен,
 Purchase Manager,Менеджер поставок,
 Purchase Master Manager,Руководитель поставок,
 Purchase Order,Заказ на покупку,
 Purchase Order Amount,Сумма заказа на покупку,
-Purchase Order Amount(Company Currency),Сумма заказа на покупку (валюта компании),
+Purchase Order Amount(Company Currency),Сумма заказа на покупку (в валюте компании),
 Purchase Order Date,Дата заказа на покупку,
 Purchase Order Items not received on time,Элементы заказа на поставку не принимаются вовремя,
-Purchase Order number required for Item {0},Число Заказ требуется для Пункт {0},
-Purchase Order to Payment,Заказ на Оплата,
+Purchase Order number required for Item {0},Число Заказ требуется для продукта {0},
+Purchase Order to Payment,Заказ на покупку к оплата,
 Purchase Order {0} is not submitted,Заказ на закупку {0} не проведен,
 Purchase Orders are not allowed for {0} due to a scorecard standing of {1}.,"Заказы на поставку не допускаются для {0} из-за того, что система показателей имеет значение {1}.",
 Purchase Orders given to Suppliers.,"Заказы, выданные поставщикам.",
 Purchase Price List,Прайс-лист закупки,
-Purchase Receipt,Товарный чек,
+Purchase Receipt,Квитанция о покупке,
 Purchase Receipt {0} is not submitted,Приход закупки {0} не проведен,
 Purchase Tax Template,Налог на покупку шаблон,
 Purchase User,Специалист поставок,
 Purchase orders help you plan and follow up on your purchases,Заказы помогут вам планировать и следить за ваши покупки,
-Purchasing,покупка,
+Purchasing,Покупка,
 Purpose must be one of {0},Цель должна быть одна из {0},
 Qty,Кол-во,
 Qty To Manufacture,Кол-во для производства,
-Qty Total,Количество Всего,
+Qty Total,Общее количество,
 Qty for {0},Кол-во для {0},
 Qualification,Квалификаци,
 Quality,Качество,
@@ -2276,8 +2274,8 @@
 Queued for replacing the BOM. It may take a few minutes.,Очередь на замену спецификации. Это может занять несколько минут.,
 Queued for updating latest price in all Bill of Materials. It may take a few minutes.,Очередь для обновления последней цены во всех Биллях материалов. Это может занять несколько минут.,
 Quick Journal Entry,Быстрый журнал запись,
-Quot Count,Количество котировок,
-Quot/Lead %,"Предложения/Обращения, %",
+Quot Count,Количество предложений,
+Quot/Lead %,"Предложения/Лиды, %",
 Quotation,Предложение,
 Quotation {0} is cancelled,Предложение {0} отменено,
 Quotation {0} not of type {1},Предложение {0} не типа {1},
@@ -2285,7 +2283,7 @@
 "Quotations are proposals, bids you have sent to your customers","Предложения - это коммерческие предложения, которые вы отправили своим клиентам",
 Quotations received from Suppliers.,"Предложения, полученные от Поставщиков.",
 Quotations: ,Предложения:,
-Quotes to Leads or Customers.,Предложения Обращениям или Клиентам.,
+Quotes to Leads or Customers.,Предложения в Лиды или Клиентов.,
 RFQs are not allowed for {0} due to a scorecard standing of {1},"Запросы не допускаются для {0} из-за того, что значение показателя {1}",
 Range,Диапазон,
 Rate,Цена,
@@ -2296,14 +2294,14 @@
 Raw Materials cannot be blank.,Сырье не может быть пустым.,
 Re-open,Снова откройте,
 Read blog,Читать блог,
-Read the ERPNext Manual,Прочитайте Руководство ERPNext,
+Read the ERPNext Manual,Прочитайте руководство ERPNext,
 Reading Uploaded File,Чтение загруженного файла,
 Real Estate,Недвижимость,
 Reason For Putting On Hold,Причина удержания,
 Reason for Hold,Причина удержания,
-Reason for hold: ,Причина удержания:,
+Reason for hold: ,Причина удержания: ,
 Receipt,Квитанция,
-Receipt document must be submitted,Документ о получении должен быть проведен,
+Receipt document must be submitted,Документ о получении должен быть исполнен,
 Receivable,Дебиторская задолженность,
 Receivable Account,Счет Дебиторской задолженности,
 Received,Получено,
@@ -2316,7 +2314,7 @@
 "Record of all communications of type email, phone, chat, visit, etc.","Запись всех способов коммуникации — электронной почты, звонков, чатов, посещений и т. п.",
 Records,Записи,
 Redirect URL,Перенаправление URL,
-Ref,ссылка,
+Ref,Ссылка,
 Ref Date,Дата ссылки,
 Reference,Справка,
 Reference #{0} dated {1},Ссылка №{0} от {1},
@@ -2333,21 +2331,21 @@
 Reference Type,Тип ссылки,
 "Reference: {0}, Item Code: {1} and Customer: {2}","Ссылка: {0}, Код товара: {1} и Заказчик: {2}",
 References,Рекомендации,
-Refresh Token,токен обновления,
+Refresh Token,Токен обновления,
 Region,Область,
-Register,регистр,
-Reject,отклонять,
+Register,Регистр,
+Reject,Отклонить,
 Rejected,Отклоненные,
 Related,Связанный,
 Relation with Guardian1,Связь с Guardian1,
 Relation with Guardian2,Связь с Guardian2,
 Release Date,Дата выпуска,
 Reload Linked Analysis,Обновить связанный анализ,
-Remaining,осталось,
+Remaining,Осталось,
 Remaining Balance,Остаток средств,
 Remarks,Примечания,
 Reminder to update GSTIN Sent,Напоминание об обновлении отправленного GSTIN,
-Remove item if charges is not applicable to that item,"Удалить продукт, если сборы не применимы к этому продукту",
+Remove item if charges is not applicable to that item,"Удалить объект, если к нему не применяются сборы",
 Removed items with no change in quantity or value.,Удалены пункты без изменения в количестве или стоимости.,
 Reopen,Возобновить,
 Reorder Level,Уровень переупорядочения,
@@ -2373,9 +2371,9 @@
 "Requested Qty: Quantity requested for purchase, but not ordered.","Запрашиваемые Кол-во: Количество просил для покупки, но не заказали.",
 Requesting Site,Запрашивающий сайт,
 Requesting payment against {0} {1} for amount {2},Запрос платеж против {0} {1} на сумму {2},
-Requestor,Requestor,
-Required On,Обязательно На,
-Required Qty,Обязательные Кол-во,
+Requestor,Заявитель,
+Required On,Требуется на,
+Required Qty,Требуемое количество,
 Required Quantity,Необходимое количество,
 Reschedule,Перепланирование,
 Research,Исследования,
@@ -2391,9 +2389,9 @@
 Reserved for manufacturing,Зарезервировано для изготовления,
 Reserved for sale,Зарезервировано для продажи,
 Reserved for sub contracting,Зарезервировано для субподряда,
-Resistant,резистентный,
+Resistant,Резистентный,
 Resolve error and upload again.,Устраните ошибку и загрузите снова.,
-Responsibilities,обязанности,
+Responsibilities,Обязанности,
 Rest Of The World,Остальной мир,
 Restart Subscription,Перезапустить подписку,
 Restaurant,Ресторан,
@@ -2411,91 +2409,91 @@
 Return / Debit Note,Возврат / дебетовые Примечание,
 Returns,Возвращает,
 Reverse Journal Entry,Обратная запись журнала,
-Review Invitation Sent,Отправлено приглашение на просмотр,
+Review Invitation Sent,Отправлено приглашение на рассмотрение,
 Review and Action,Обзор и действие,
 Role,Роль,
 Rooms Booked,Забронированные номера,
-Root Company,Корневая Компания,
+Root Company,Родительская компания,
 Root Type,Корневая Тип,
 Root Type is mandatory,Корневая Тип является обязательным,
 Root cannot be edited.,Корневая не могут быть изменены.,
 Root cannot have a parent cost center,Корневая не может иметь родителей МВЗ,
 Round Off,Округлять,
-Rounded Total,Округлые Всего,
-Route,маршрут,
-Row # {0}: ,Ряд # {0}:,
-Row # {0}: Batch No must be same as {1} {2},"Ряд # {0}: Пакетное Нет должно быть таким же, как {1} {2}",
-Row # {0}: Cannot return more than {1} for Item {2},Ряд # {0}: Невозможно вернуть более {1} для п {2},
+Rounded Total,Итого с округлением,
+Route,Маршрут,
+Row # {0}: ,Строка # {0}: ,
+Row # {0}: Batch No must be same as {1} {2},"Строка # {0}: Номер партии не должен быть таким же, как {1} {2}",
+Row # {0}: Cannot return more than {1} for Item {2},Строка # {0}: Невозможно вернуть более {1} для {2},
 Row # {0}: Rate cannot be greater than the rate used in {1} {2},"Строка # {0}: ставка не может быть больше ставки, используемой в {1} {2}",
-Row # {0}: Serial No is mandatory,Ряд # {0}: Серийный номер является обязательным,
-Row # {0}: Serial No {1} does not match with {2} {3},"Ряд # {0}: Серийный номер {1}, не соответствует {2} {3}",
-Row #{0} (Payment Table): Amount must be negative,Строка # {0} (таблица платежей): сумма должна быть отрицательной,
-Row #{0} (Payment Table): Amount must be positive,Строка # {0} (таблица платежей): сумма должна быть положительной,
-Row #{0}: Account {1} does not belong to company {2},Строка # {0}: учетная запись {1} не принадлежит компании {2},
-Row #{0}: Allocated Amount cannot be greater than outstanding amount.,Строка # {0}: выделенная сумма не может превышать невыплаченную сумму.,
-"Row #{0}: Asset {1} cannot be submitted, it is already {2}","Строка # {0}: Актив {1} не может быть проведен, он уже {2}",
-Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.,"Строка # {0}: не может установить значение скорости, если сумма превышает сумму выставленного счета за элемент {1}.",
-Row #{0}: Clearance date {1} cannot be before Cheque Date {2},Строка # {0}: дате зазора {1} не может быть до того Cheque Дата {2},
-Row #{0}: Duplicate entry in References {1} {2},Строка # {0}: Дублирующая запись в ссылках {1} {2},
-Row #{0}: Expected Delivery Date cannot be before Purchase Order Date,Строка # {0}: ожидаемая дата поставки не может быть до даты заказа на поставку,
-Row #{0}: Item added,Строка № {0}: пункт добавлен,
-Row #{0}: Journal Entry {1} does not have account {2} or already matched against another voucher,Строка # {0}: Запись в журнале {1} не имеет учетной записи {2} или уже сопоставляется с другой купон,
-Row #{0}: Not allowed to change Supplier as Purchase Order already exists,Ряд # {0}: Не разрешено изменять Поставщик как уже существует заказа,
-Row #{0}: Please set reorder quantity,"Ряд # {0}: Пожалуйста, установите количество тональный",
-Row #{0}: Please specify Serial No for Item {1},"Ряд # {0}: Пожалуйста, сформулируйте Серийный номер для Пункт {1}",
-Row #{0}: Qty increased by 1,Ряд № {0}: кол-во увеличено на 1,
-Row #{0}: Rate must be same as {1}: {2} ({3} / {4}) ,"Ряд # {0}: цена должна быть такой же, как {1}: {2} ({3} / {4})",
-Row #{0}: Reference Document Type must be one of Expense Claim or Journal Entry,Строка # {0}: Тип ссылочного документа должен быть одним из заголовка расхода или записи журнала,
-"Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry","Строка # {0}: Ссылка Тип документа должен быть одним из заказа на поставку, счета-фактуры Покупка или журнал запись",
-Row #{0}: Rejected Qty can not be entered in Purchase Return,Ряд # {0}: Отклонено Кол-во не может быть введен в приобретении Вернуться,
-Row #{0}: Rejected Warehouse is mandatory against rejected Item {1},Ряд # {0}: Отклонено Склад является обязательным в отношении отклонил Пункт {1},
-Row #{0}: Reqd by Date cannot be before Transaction Date,Строка # {0}: Reqd by Date не может быть до даты транзакции,
-Row #{0}: Set Supplier for item {1},Ряд # {0}: Установить Поставщик по пункту {1},
-Row #{0}: Status must be {1} for Invoice Discounting {2},Строка # {0}: статус должен быть {1} для дисконтирования счета-фактуры {2},
+Row # {0}: Serial No is mandatory,Строка # {0}: Серийный номер является обязательным,
+Row # {0}: Serial No {1} does not match with {2} {3},"Строка # {0}: Серийный номер {1}, не соответствует {2} {3}",
+Row #{0} (Payment Table): Amount must be negative,Строка #{0} (таблица платежей): сумма должна быть отрицательной,
+Row #{0} (Payment Table): Amount must be positive,Строка #{0} (таблица платежей): сумма должна быть положительной,
+Row #{0}: Account {1} does not belong to company {2},Строка #{0}: Счет {1} не принадлежит компании {2},
+Row #{0}: Allocated Amount cannot be greater than outstanding amount.,Строка #{0}: выделенная сумма не может превышать невыплаченную сумму.,
+"Row #{0}: Asset {1} cannot be submitted, it is already {2}","Строка #{0}: Актив {1} не может быть проведен, он уже {2}",
+Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.,"Строка #{0}: не может установить значение скорости, если сумма превышает сумму выставленного счета за элемент {1}.",
+Row #{0}: Clearance date {1} cannot be before Cheque Date {2},Строка #{0}: дате зазора {1} не может быть до того Cheque Дата {2},
+Row #{0}: Duplicate entry in References {1} {2},Строка #{0}: Дублирующая запись в ссылках {1} {2},
+Row #{0}: Expected Delivery Date cannot be before Purchase Order Date,Строка #{0}: ожидаемая дата поставки не может быть до даты заказа на поставку,
+Row #{0}: Item added,Строка #{0}: пункт добавлен,
+Row #{0}: Journal Entry {1} does not have account {2} or already matched against another voucher,Строка #{0}: Запись в журнале {1} не имеет учетной записи {2} или уже сопоставляется с другой купон,
+Row #{0}: Not allowed to change Supplier as Purchase Order already exists,Строка #{0}: Не разрешено изменять поставщика когда уже существует заказ,
+Row #{0}: Please set reorder quantity,"Строка #{0}: Пожалуйста, укажите количество повторных заказов",
+Row #{0}: Please specify Serial No for Item {1},"Строка #{0}: Пожалуйста, сформулируйте серийный номер для продукта {1}",
+Row #{0}: Qty increased by 1,Строка #{0}: кол-во увеличено на 1,
+Row #{0}: Rate must be same as {1}: {2} ({3} / {4}) ,"Строка #{0}: цена должна быть такой же, как {1}: {2} ({3} / {4})",
+Row #{0}: Reference Document Type must be one of Expense Claim or Journal Entry,Строка #{0}: Тип ссылочного документа должен быть одним из заголовка расхода или записи журнала,
+"Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry","Строка #{0}: Тип справочного документа должен быть одним из следующих: Заказ на покупку, Счет-фактура на покупку или Запись в журнале",
+Row #{0}: Rejected Qty can not be entered in Purchase Return,Строка #{0}: Отклоненное количество не может быть введено в возврат покупки,
+Row #{0}: Rejected Warehouse is mandatory against rejected Item {1},Строка #{0}: Отклоненный склад является обязательным для отклоненного продукта {1},
+Row #{0}: Reqd by Date cannot be before Transaction Date,Строка #{0}: Reqd by Date не может быть до даты транзакции,
+Row #{0}: Set Supplier for item {1},Строка #{0}: Установить поставщика для {1},
+Row #{0}: Status must be {1} for Invoice Discounting {2},Строка #{0}: статус должен быть {1} для дисконтирования счета-фактуры {2},
 "Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches","Строка # {0}: партия {1} имеет только {2} qty. Выберите другой пакет, в котором имеется {3} qty, или разбейте строку на несколько строк, чтобы доставлять / выпускать из нескольких партий",
-Row #{0}: Timings conflicts with row {1},Ряд # {0}: тайминги конфликты с рядом {1},
-Row #{0}: {1} can not be negative for item {2},Строка # {0}: {1} не может быть отрицательным по пункту {2},
-Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2},"Ряд № {0}: Сумма не может быть больше, чем указанная в Авансовом Отчете {1}. Указанная сумма {2}",
+Row #{0}: Timings conflicts with row {1},Строка #{0}: Тайминги конфликтуют со строкой {1},
+Row #{0}: {1} can not be negative for item {2},Строка #{0}: {1} не может быть отрицательным для {2},
+Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2},"Строка #{0}: Сумма не может быть больше, чем указанная в Авансовом Отчете {1}. Указанная сумма {2}",
 Row {0} : Operation is required against the raw material item {1},Строка {0}: требуется операция против элемента исходного материала {1},
-Row {0}# Allocated amount {1} cannot be greater than unclaimed amount {2},"Строка {0} # Выделенная сумма {1} не может быть больше, чем невостребованная сумма {2}",
-Row {0}# Item {1} cannot be transferred more than {2} against Purchase Order {3},Строка {0} # Элемент {1} не может быть передан более {2} в отношении заказа на поставку {3},
-Row {0}# Paid Amount cannot be greater than requested advance amount,Строка {0} # Платная сумма не может быть больше запрашиваемой суммы аванса,
+Row {0}# Allocated amount {1} cannot be greater than unclaimed amount {2},"Строка {0}# Выделенная сумма {1} не может быть больше, чем невостребованная сумма {2}",
+Row {0}# Item {1} cannot be transferred more than {2} against Purchase Order {3},Строка {0}# Продукт {1} не может быть передан более {2} в отношении заказа на поставку {3},
+Row {0}# Paid Amount cannot be greater than requested advance amount,Строка {0}# Оплаченная сумма не может быть больше запрошенной суммы аванса,
 Row {0}: Activity Type is mandatory.,Строка {0}: Вид деятельности является обязательным.,
-Row {0}: Advance against Customer must be credit,Ряд {0}: Предварительная отношении Клиента должен быть кредит,
-Row {0}: Advance against Supplier must be debit,Ряд {0}: Предварительная против Поставщика должны быть дебет,
+Row {0}: Advance against Customer must be credit,Строка {0}: Аванс в отношении клиента должен быть кредитом,
+Row {0}: Advance against Supplier must be debit,Строка {0}: Аванс в отношении поставщика должны быть дебетом,
 Row {0}: Allocated amount {1} must be less than or equals to Payment Entry amount {2},Строка {0}: Выделенная сумма {1} должна быть меньше или равна сумме платежа ввода {2},
-Row {0}: Allocated amount {1} must be less than or equals to invoice outstanding amount {2},"Ряд {0}: суммы, выделенной {1} должен быть меньше или равен счета-фактуры сумма задолженности {2}",
-Row {0}: An Reorder entry already exists for this warehouse {1},Ряд {0}: запись Изменить порядок уже существует для этого склада {1},
+Row {0}: Allocated amount {1} must be less than or equals to invoice outstanding amount {2},"Строка {0}: суммы, выделенной {1} должен быть меньше или равен счета-фактуры сумма задолженности {2}",
+Row {0}: An Reorder entry already exists for this warehouse {1},Строка {0}: Запись о повторном заказе уже существует для этого склада {1},
 Row {0}: Bill of Materials not found for the Item {1},Строка {0}: Для продукта {1} не найдена ведомость материалов,
-Row {0}: Conversion Factor is mandatory,Ряд {0}: Коэффициент преобразования является обязательным,
+Row {0}: Conversion Factor is mandatory,Строка {0}: Коэффициент преобразования является обязательным,
 Row {0}: Cost center is required for an item {1},Строка {0}: МВЗ требуется для элемента {1},
-Row {0}: Credit entry can not be linked with a {1},Ряд {0}: Кредитная запись не может быть связан с {1},
-Row {0}: Currency of the BOM #{1} should be equal to the selected currency {2},Строка {0}: Валюта BOM # {1} должен быть равен выбранной валюте {2},
-Row {0}: Debit entry can not be linked with a {1},Ряд {0}: Дебет запись не может быть связан с {1},
+Row {0}: Credit entry can not be linked with a {1},Строка {0}: Кредитная запись не может быть связана с {1},
+Row {0}: Currency of the BOM #{1} should be equal to the selected currency {2},Строка {0}: Валюта спецификации # {1} должен быть равен выбранной валюте {2},
+Row {0}: Debit entry can not be linked with a {1},Строка {0}: Дебет запись не может быть связан с {1},
 Row {0}: Depreciation Start Date is required,Строка {0}: требуется дата начала амортизации,
 Row {0}: Enter location for the asset item {1},Строка {0}: введите местоположение для объекта актива {1},
-Row {0}: Exchange Rate is mandatory,Ряд {0}: Курс является обязательным,
-Row {0}: Expected Value After Useful Life must be less than Gross Purchase Amount,Ряд {0}: ожидаемое значение после полезной жизни должно быть меньше валовой суммы покупки,
+Row {0}: Exchange Rate is mandatory,Строка {0}: Курс является обязательным,
+Row {0}: Expected Value After Useful Life must be less than Gross Purchase Amount,Строка {0}: Ожидаемая стоимость после срока полезного использования должна быть меньше общей суммы покупки,
 Row {0}: From Time and To Time is mandatory.,Строка {0}: От времени и времени является обязательным.,
 Row {0}: From Time and To Time of {1} is overlapping with {2},Строка {0}: От времени и времени {1} перекрывается с {2},
 Row {0}: From time must be less than to time,Строка {0}: время должно быть меньше времени,
 Row {0}: Hours value must be greater than zero.,Строка {0}: значение часов должно быть больше нуля.,
-Row {0}: Invalid reference {1},Ряд {0}: Недопустимая ссылка {1},
-Row {0}: Party / Account does not match with {1} / {2} in {3} {4},Ряд {0}: Партия / счета не соответствует {1} / {2} в {3} {4},
-Row {0}: Party Type and Party is required for Receivable / Payable account {1},Ряд {0}: Партия Тип и партия необходима для / дебиторская задолженность внимание {1},
-Row {0}: Payment against Sales/Purchase Order should always be marked as advance,Ряд {0}: Платеж по покупке / продаже порядок должен всегда быть помечены как заранее,
-Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.,"Ряд {0}: Пожалуйста, проверьте 'Как Advance ""против счета {1}, если это заранее запись.",
-Row {0}: Please set at Tax Exemption Reason in Sales Taxes and Charges,Строка {0}: укажите причину освобождения от налогов в налогах с продаж и сборах,
+Row {0}: Invalid reference {1},Строка {0}: Недопустимая ссылка {1},
+Row {0}: Party / Account does not match with {1} / {2} in {3} {4},Строка {0}: Партия / счета не соответствует {1} / {2} в {3} {4},
+Row {0}: Party Type and Party is required for Receivable / Payable account {1},Строка {0}: Для счета дебиторской/кредиторской задолженности требуется тип и сторона стороны {1},
+Row {0}: Payment against Sales/Purchase Order should always be marked as advance,Строка {0}: Платеж по покупке / продаже порядок должен всегда быть помечены как заранее,
+Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.,"Строка {0}: Пожалуйста, проверьте 'Как Advance ""против счета {1}, если это заранее запись.",
+Row {0}: Please set at Tax Exemption Reason in Sales Taxes and Charges,Строка {0}: Укажите причину освобождения от уплаты налогов в разделе Налоги и сборы,
 Row {0}: Please set the Mode of Payment in Payment Schedule,"Строка {0}: пожалуйста, установите способ оплаты в графике платежей",
 Row {0}: Please set the correct code on Mode of Payment {1},Строка {0}: установите правильный код в способе оплаты {1},
-Row {0}: Qty is mandatory,Ряд {0}: Кол-во является обязательным,
+Row {0}: Qty is mandatory,Строка {0}: Кол-во является обязательным,
 Row {0}: Quality Inspection rejected for item {1},Строка {0}: проверка качества отклонена для элемента {1},
-Row {0}: UOM Conversion Factor is mandatory,Ряд {0}: Коэффициент преобразования Единица измерения является обязательным,
-Row {0}: select the workstation against the operation {1},Строка {0}: выберите рабочую станцию против операции {1},
+Row {0}: UOM Conversion Factor is mandatory,Строка {0}: Коэффициент преобразования единиц измерения является обязательным,
+Row {0}: select the workstation against the operation {1},Строка {0}: выберите рабочее место для операции {1},
 Row {0}: {1} Serial numbers required for Item {2}. You have provided {3}.,Строка {0}: {1} Необходимы серийные номера продукта {2}. Вы предоставили {3}.,
 Row {0}: {1} must be greater than 0,Строка {0}: {1} должна быть больше 0,
-Row {0}: {1} {2} does not match with {3},Ряд {0}: {1} {2} не соответствует {3},
-Row {0}:Start Date must be before End Date,Ряд {0}: Дата начала должна быть раньше даты окончания,
+Row {0}: {1} {2} does not match with {3},Строка {0}: {1} {2} не соответствует {3},
+Row {0}:Start Date must be before End Date,Строка {0}: Дата начала должна быть раньше даты окончания,
 Rows with duplicate due dates in other rows were found: {0},Были найдены строки с повторяющимися датами в других строках: {0},
 Rules for adding shipping costs.,Правила для добавления стоимости доставки.,
 Rules for applying pricing and discount.,Правила применения цен и скидки.,
@@ -2507,9 +2505,9 @@
 Salary Slip ID,Зарплата скольжения ID,
 Salary Slip of employee {0} already created for this period,Зарплата Скольжение работника {0} уже создано за этот период,
 Salary Slip of employee {0} already created for time sheet {1},Зарплата Скольжение работника {0} уже создан для табеля {1},
-Salary Slip submitted for period from {0} to {1},"Зарплатный сальс, представленный на период от {0} до {1}",
+Salary Slip submitted for period from {0} to {1},"Зарплатная ведомость отправлена за период с {0} по {1}",
 Salary Structure Assignment for Employee already exists,Присвоение структуры зарплаты сотруднику уже существует,
-Salary Structure Missing,Структура заработной платы Отсутствующий,
+Salary Structure Missing,Структура заработной платы отсутствует,
 Salary Structure must be submitted before submission of Tax Ememption Declaration,Структура заработной платы должна быть представлена до подачи декларации об освобождении от налогов,
 Salary Structure not found for employee {0} and date {1},Структура зарплаты не найдена для сотрудника {0} и даты {1},
 Salary Structure should have flexible benefit component(s) to dispense benefit amount,Структура заработной платы должна иметь гибкий компонент (ы) выгоды для распределения суммы пособия,
@@ -2532,7 +2530,7 @@
 Sales Order {0} is {1},Сделка {0} это {1},
 Sales Orders,Сделки,
 Sales Partner,Партнер по продажам,
-Sales Pipeline,Воронка продаж,
+Sales Pipeline,Пайплайн продаж,
 Sales Price List,Прайс-лист продажи,
 Sales Return,Возвраты с продаж,
 Sales Summary,Резюме продаж,
@@ -2567,12 +2565,12 @@
 "Schedules for {0} overlaps, do you want to proceed after skiping overlaped slots ?","Расписания для {0} перекрываются, вы хотите продолжить после пропусков перекрытых слотов?",
 Score cannot be greater than Maximum Score,"Оценка не может быть больше, чем максимальный балл",
 Score must be less than or equal to 5,Оценка должна быть меньше или равна 5,
-Scorecards,Scorecards,
+Scorecards,Оценочные карты,
 Scrapped,Уничтоженный,
 Search,Поиск,
 Search Results,результаты поиска,
 Search Sub Assemblies,Поиск Sub сборки,
-"Search by item code, serial number, batch no or barcode","Поиск по коду товара, серийному номеру, номеру партии или штрих-коду",
+"Search by item code, serial number, batch no or barcode","Поиск по коду продукта, серийному номеру, номеру партии или штрих-коду",
 "Seasonality for setting budgets, targets etc.","Сезонность для установки бюджеты, целевые и т.п.",
 Secret Key,Секретный ключ,
 Secretary,Секретарь,
@@ -2583,23 +2581,23 @@
 See All Articles,Просмотреть все статьи,
 See all open tickets,Просмотреть все открытые билеты,
 See past orders,Посмотреть прошлые заказы,
-See past quotations,Посмотреть прошлые цитаты,
+See past quotations,Посмотреть прошлые предложения,
 Select,Выбрать,
-Select Alternate Item,Выбрать альтернативный элемент,
+Select Alternate Item,Выбрать альтернативный продукт,
 Select Attribute Values,Выберите значения атрибута,
-Select BOM,Выберите BOM,
-Select BOM and Qty for Production,Выберите BOM и Кол-во для производства,
+Select BOM,Выберите спецификацию,
+Select BOM and Qty for Production,Выберите спецификацию и кол-во для производства,
 "Select BOM, Qty and For Warehouse","Выберите спецификацию, кол-во и для склада",
 Select Batch,Выбрать пакет,
 Select Batch Numbers,Выберите пакетные номера,
-Select Brand...,Выберите бренд ...,
+Select Brand...,Выберите бренд...,
 Select Company,Выберите компанию,
-Select Company...,Выберите компанию ...,
+Select Company...,Выберите компанию...,
 Select Customer,Выбрать клиента,
 Select Days,Выберите дни,
-Select Default Supplier,Выберите По умолчанию Поставщик,
+Select Default Supplier,Выберите поставщика по умолчанию,
 Select DocType,Выберите тип документа,
-Select Fiscal Year...,Выберите финансовый год ...,
+Select Fiscal Year...,Выберите финансовый год...,
 Select Item (optional),Выберите продукт (необязательно),
 Select Items based on Delivery Date,Выбрать продукты по дате поставки,
 Select Items to Manufacture,Выберите продукты для производства,
@@ -2610,7 +2608,7 @@
 Select Quantity,Выберите количество,
 Select Serial Numbers,Выберите серийные номера,
 Select Target Warehouse,Выберите целевое хранилище,
-Select Warehouse...,Выберите Склад ...,
+Select Warehouse...,Выберите cклад...,
 Select an account to print in account currency,Выберите учетную запись для печати в валюте счета.,
 Select an employee to get the employee advance.,"Выберите сотрудника, чтобы получить работу сотрудника.",
 Select at least one value from each of the attributes.,Выберите по крайней мере одно значение из каждого из атрибутов.,
@@ -2621,47 +2619,47 @@
 Select the nature of your business.,Выберите характер вашего бизнеса.,
 Select the program first,Сначала выберите программу,
 Select to add Serial Number.,"Выберите, чтобы добавить серийный номер.",
-Select your Domains,Выберите свои домены,
+Select your Domains,Выберите ваши сферы деятельности,
 Selected Price List should have buying and selling fields checked.,Выбранный прейскурант должен иметь поля для покупки и продажи.,
 Sell,Продажа,
-Selling,продажа,
-Selling Amount,Продажа Сумма,
+Selling,Продажа,
+Selling Amount,Сумма продажа,
 Selling Price List,Продажа прайс-листа,
 Selling Rate,Стоимость продажи,
 "Selling must be checked, if Applicable For is selected as {0}","Продажа должна быть проверена, если выбран Применимо для как {0}",
 Send Grant Review Email,Отправить отзыв по электронной почте,
-Send Now,Отправить Сейчас,
-Send SMS,Отправить смс,
-Send mass SMS to your contacts,Отправить массовое СМС по списку контактов,
-Sensitivity,чувствительность,
+Send Now,Отправить сейчас,
+Send SMS,Отправить SMS,
+Send mass SMS to your contacts,Отправить SMS по списку контактов,
+Sensitivity,Чувствительность,
 Sent,Отправлено,
 Serial No and Batch,Серийный номер и партия,
-Serial No is mandatory for Item {0},Серийный номер является обязательным для п. {0},
+Serial No is mandatory for Item {0},Серийный номер является обязательным для продукта {0},
 Serial No {0} does not belong to Batch {1},Серийный номер {0} не принадлежит к пакету {1},
 Serial No {0} does not belong to Delivery Note {1},Серийный номер {0} не принадлежит накладной {1},
-Serial No {0} does not belong to Item {1},Серийный номер {0} не принадлежит Пункт {1},
-Serial No {0} does not belong to Warehouse {1},Серийный номер {0} не принадлежит Склад {1},
-Serial No {0} does not belong to any Warehouse,Серийный номер {0} не принадлежит ни к одной Склад,
+Serial No {0} does not belong to Item {1},Серийный номер {0} не принадлежит продукту {1},
+Serial No {0} does not belong to Warehouse {1},Серийный номер {0} не принадлежит складу {1},
+Serial No {0} does not belong to any Warehouse,Серийный номер {0} не принадлежит ни к одному складу,
 Serial No {0} does not exist,Серийный номер {0} не существует,
 Serial No {0} has already been received,Серийный номер {0} уже существует,
-Serial No {0} is under maintenance contract upto {1},Серийный номер {0} находится под контрактом на техническое обслуживание ДО {1},
+Serial No {0} is under maintenance contract upto {1},Серийный номер {0} находится под контрактом на техническое обслуживание до {1},
 Serial No {0} is under warranty upto {1},Серийный номер {0} находится на гарантии до {1},
 Serial No {0} not found,Серийный номер {0} не найден,
-Serial No {0} not in stock,Серийный номер {0} не в наличии,
-Serial No {0} quantity {1} cannot be a fraction,Серийный номер {0} количество {1} не может быть фракция,
+Serial No {0} not in stock,Серийного номера {0} нет в наличии,
+Serial No {0} quantity {1} cannot be a fraction,Серийный номер {0} количество {1} не может быть дробным,
 Serial Nos Required for Serialized Item {0},Требуются серийные номера для серийного продукта {0},
 Serial Number: {0} is already referenced in Sales Invoice: {1},Серийный номер: {0} уже указан в счете продаж: {1},
 Serial Numbers,Серийные номера,
 Serial Numbers in row {0} does not match with Delivery Note,Серийные номера в строке {0} не совпадают с полем «Поставка»,
 Serial no {0} has been already returned,Серийный номер {0} уже возвращен,
-Serial number {0} entered more than once,Серийный номер {0} вошли более одного раза,
+Serial number {0} entered more than once,Серийный номер {0} используется больше одного раза,
 Serialized Inventory,Учет сериями,
 Series Updated,Идентификатор обновлен,
 Series Updated Successfully,Идентификатор успешно обновлен,
 Series is mandatory,Идентификатор является обязательным,
 Series {0} already used in {1},Идентификатор {0} уже используется в {1},
-Service,Услуги,
-Service Expense,Услуги Expense,
+Service,Услуга,
+Service Expense,Стоимость услуги,
 Service Level Agreement,Соглашение об уровне обслуживания,
 Service Level Agreement.,Соглашение об уровне обслуживания.,
 Service Level.,Уровень обслуживания.,
@@ -2673,12 +2671,12 @@
 Set New Release Date,Установите новую дату выпуска,
 Set Project and all Tasks to status {0}?,Установить проект и все задачи в статус {0}?,
 Set Status,Установить статус,
-Set Tax Rule for shopping cart,Установите Налоговый Правило корзине,
-Set as Closed,Установить как Закрыт,
-Set as Completed,Сделать завершенным,
-Set as Default,Установить по умолчанию,
-Set as Lost,Установить как Остаться в живых,
-Set as Open,Установить как Open,
+Set Tax Rule for shopping cart,Установить налоговое правило для корзины,
+Set as Closed,Установить как "Закрыт",
+Set as Completed,Установить как "Завершен",
+Set as Default,Установить "По умолчанию",
+Set as Lost,Установить как "Потерянный",
+Set as Open,Установить как "Открытый",
 Set default inventory account for perpetual inventory,Установить учетную запись по умолчанию для вечной инвентаризации,
 Set this if the customer is a Public Administration company.,"Установите это, если клиент является компанией государственного управления.",
 Set {0} in asset category {1} or company {2},Установите {0} в категории активов {1} или компании {2},
@@ -2687,25 +2685,25 @@
 Setting up Email,Настройка электронной почты,
 Setting up Email Account,Настройка учетной записи электронной почты,
 Setting up Employees,Настройка сотрудников,
-Setting up Taxes,Настройка Налоги,
-Setting up company,Создание компании,
-Settings,настройки,
+Setting up Taxes,Настройка налога,
+Setting up company,Настройка компании,
+Settings,Настройки,
 "Settings for online shopping cart such as shipping rules, price list etc.","Настройки для онлайн корзины, такие как правилами перевозок, прайс-лист и т.д.",
 Settings for website homepage,Настройки для сайта домашнюю страницу,
 Settings for website product listing,Настройки для списка товаров на сайте,
 Settled,Установившаяся,
 Setup Gateway accounts.,Настройка шлюза счета.,
-Setup SMS gateway settings,Указать настройки СМС-шлюза,
+Setup SMS gateway settings,Указать настройки SMS-шлюза,
 Setup cheque dimensions for printing,Размеры Проверьте настройки для печати,
-Setup default values for POS Invoices,Настройка значений по умолчанию для счетов-фактур POS,
+Setup default values for POS Invoices,Настройка значений по умолчанию для счетов POS,
 Setup mode of POS (Online / Offline),Режим настройки POS (Online / Offline),
 Setup your Institute in ERPNext,Установите свой институт в ERPNext,
 Share Balance,Баланс акций,
-Share Ledger,Share Ledger,
+Share Ledger,Записи по акциям,
 Share Management,Управление долями,
 Share Transfer,Передача акций,
-Share Type,Share Тип,
-Shareholder,акционер,
+Share Type,Тип акций,
+Shareholder,Акционер,
 Ship To State,Корабль в штат,
 Shipments,Поставки,
 Shipping,Доставка,
@@ -2715,17 +2713,17 @@
 Shipping rule only applicable for Selling,Правило доставки применимо только для продажи,
 Shopify Supplier,Покупатель,
 Shopping Cart,Корзина,
-Shopping Cart Settings,Корзина Настройки,
-Short Name,Короткое Имя,
+Shopping Cart Settings,Настройки корзины,
+Short Name,Короткое имя,
 Shortage Qty,Нехватка Кол-во,
-Show Completed,Показать выполнено,
+Show Completed,Показать завершенные,
 Show Cumulative Amount,Показать суммарную сумму,
 Show Employee,Показать сотрудника,
-Show Open,Показать открыт,
+Show Open,Показать открытые,
 Show Opening Entries,Показать вступительные записи,
 Show Payment Details,Показать данные платежа,
 Show Return Entries,Показать возвращенные записи,
-Show Salary Slip,Показать Зарплата скольжению,
+Show Salary Slip,Показать зарплатную ведомость,
 Show Variant Attributes,Показать атрибуты варианта,
 Show Variants,Показать варианты,
 Show closed,Показать закрыто,
@@ -2745,7 +2743,7 @@
 Soap & Detergent,Моющие средства,
 Software,Программное обеспечение,
 Software Developer,Разработчик программного обеспечения,
-Softwares,Softwares,
+Softwares,Программное обеспечение,
 Soil compositions do not add up to 100,Композиции почвы не составляют до 100,
 Sold,Продан,
 Some emails are invalid,Некоторые электронные письма недействительны,
@@ -2754,13 +2752,13 @@
 "Sorry, Serial Nos cannot be merged","К сожалению, серийные номера не могут быть объединены",
 Source,Источник,
 Source Name,Имя источника,
-Source Warehouse,Источник Склад,
+Source Warehouse,Склад источник,
 Source and Target Location cannot be same,Источник и целевое местоположение не могут быть одинаковыми,
 Source and target warehouse cannot be same for row {0},Источник и цель склад не может быть одинаковым для ряда {0},
 Source and target warehouse must be different,Исходный и целевой склад должны быть разными,
 Source of Funds (Liabilities),Источник финансирования (обязательства),
 Source warehouse is mandatory for row {0},Источник склад является обязательным для ряда {0},
-Specified BOM {0} does not exist for Item {1},Указанная ВМ {0} для продукта {1} не существует,
+Specified BOM {0} does not exist for Item {1},Указанная спецификация {0} для продукта {1} не существует,
 Split,Трещина,
 Split Batch,Сплит-пакет,
 Split Issue,Сплит-выпуск,
@@ -2770,7 +2768,7 @@
 Standard Buying,Стандартный Покупка,
 Standard Selling,Стандартный Продажа,
 Standard contract terms for Sales or Purchase.,Стандартные условия договора для продажи или покупки.,
-Start Date,Дата Начала,
+Start Date,Дата начала,
 Start Date of Agreement can't be greater than or equal to End Date.,Дата начала соглашения не может быть больше или равна дате окончания.,
 Start Year,Год начала,
 "Start and end dates not in a valid Payroll Period, cannot calculate {0}","Даты начала и окончания не в допустимом Периоде платежной ведомости, не может вычислить {0}",
@@ -2787,17 +2785,17 @@
 Stock Adjustment,Регулирование запасов,
 Stock Analytics,Аналитика запасов,
 Stock Assets,Капитал запасов,
-Stock Available,Имеется в наличии,
+Stock Available,Есть в наличии,
 Stock Balance,Баланс запасов,
 Stock Entries already created for Work Order ,"Записи запаса, уже созданные для рабочего заказа",
 Stock Entry,Движения на складе,
 Stock Entry {0} created,Создана складская запись {0},
 Stock Entry {0} is not submitted,Складской акт {0} не проведен,
 Stock Expenses,Расходы по Запасам,
-Stock In Hand,Товарная наличность,
+Stock In Hand,Запасы на руках,
 Stock Items,Позиции на складе,
-Stock Ledger,Книга учета Запасов,
-Stock Ledger Entries and GL Entries are reposted for the selected Purchase Receipts,Записи складской книги и записи GL запасов отправляются для выбранных покупок,
+Stock Ledger,Книга учета запасов,
+Stock Ledger Entries and GL Entries are reposted for the selected Purchase Receipts,Записи книги учета запасов и записи GL повторно публикуются для выбранных квитанций о покупках,
 Stock Levels,Уровень запасов,
 Stock Liabilities,Обязательства по запасам,
 Stock Options,Опционы,
@@ -2816,12 +2814,12 @@
 Stop,Стоп,
 Stopped,Приостановлено,
 "Stopped Work Order cannot be cancelled, Unstop it first to cancel","Прекращенный рабочий заказ не может быть отменен, отмените его сначала, чтобы отменить",
-Stores,магазины,
+Stores,Магазины,
 Structures have been assigned successfully,Структуры были успешно назначены,
 Student,Студент,
 Student Activity,Студенческая деятельность,
 Student Address,Адрес студента,
-Student Admissions,зачисляемых студентов,
+Student Admissions,Зачисление студентов,
 Student Attendance,Посещаемость студента,
 "Student Batches help you track attendance, assessments and fees for students","Студенческие Порции помогают отслеживать посещаемость, оценки и сборы для студентов",
 Student Email Address,Адрес электронной почты студента,
@@ -2829,26 +2827,26 @@
 Student Group,Учебная группа,
 Student Group Strength,Сила студенческой группы,
 Student Group is already updated.,Студенческая группа уже обновлена.,
-Student Group: ,Студенческая группа:,
+Student Group: ,Студенческая группа: ,
 Student ID,Студенческий билет,
-Student ID: ,Студенческий билет:,
+Student ID: ,Студенческий билет: ,
 Student LMS Activity,Студенческая LMS Активность,
-Student Mobile No.,Student Mobile No.,
+Student Mobile No.,Мобильный номер студента,
 Student Name,Имя ученика,
-Student Name: ,Имя ученика:,
+Student Name: ,Имя ученика: ,
 Student Report Card,Студенческая отчетная карточка,
 Student is already enrolled.,Студент уже поступил.,
 Student {0} - {1} appears Multiple times in row {2} & {3},Student {0} - {1} несколько раз появляется в строке {2} и {3},
 Student {0} does not belong to group {1},Студент {0} не принадлежит группе {1},
 Student {0} exist against student applicant {1},Student {0} существует против студента заявителя {1},
 "Students are at the heart of the system, add all your students","Студенты в центре системы, добавьте все студенты",
-Sub Assemblies,Sub сборки,
+Sub Assemblies,Подсборки,
 Sub Type,Подтип,
-Sub-contracting,Суб-сжимания,
-Subcontract,субподряд,
+Sub-contracting,Суб-контракты,
+Subcontract,Субподряд,
 Subject,Тема,
 Submit,Провести,
-Submit Proof,Подтвердить,
+Submit Proof,Отправить подтверждение,
 Submit Salary Slip,Провести Зарплатную ведомость,
 Submit this Work Order for further processing.,Отправьте этот рабочий заказ для дальнейшей обработки.,
 Submit this to create the Employee record,"Отправьте это, чтобы создать запись сотрудника",
@@ -2857,16 +2855,16 @@
 Subscription Management,Управление подпиской,
 Subscriptions,Подписки,
 Subtotal,Промежуточный итог,
-Successful,успешный,
-Successfully Reconciled,Успешно Примирение,
-Successfully Set Supplier,Успешно установить поставщика,
+Successful,Успешно,
+Successfully Reconciled,Успешно согласовано,
+Successfully Set Supplier,Поставщик успешно установлен,
 Successfully created payment entries,Успешно созданные платежные записи,
 Successfully deleted all transactions related to this company!,"Успешно удален все сделки, связанные с этой компанией!",
 Sum of Scores of Assessment Criteria needs to be {0}.,Сумма десятков критериев оценки должно быть {0}.,
 Sum of points for all goals should be 100. It is {0},Сумма баллов за все цели должны быть 100. Это {0},
-Summary,Резюме,
-Summary for this month and pending activities,Резюме для этого месяца и в ожидании деятельности,
-Summary for this week and pending activities,Резюме на этой неделе и в ожидании деятельности,
+Summary,Сводка,
+Summary for this month and pending activities,Сводка за этот месяц и предстоящие мероприятия,
+Summary for this week and pending activities,Сводка за эту неделю и предстоящие мероприятия,
 Sunday,Воскресенье,
 Suplier,Поставщик,
 Supplier,Поставщик,
@@ -2875,15 +2873,15 @@
 Supplier Id,Id поставщика,
 Supplier Invoice Date cannot be greater than Posting Date,"Дата Поставщик Счет не может быть больше, чем Дата публикации",
 Supplier Invoice No,Поставщик Счет №,
-Supplier Invoice No exists in Purchase Invoice {0},Поставщик Счет-фактура не существует в счете-фактуре {0},
+Supplier Invoice No exists in Purchase Invoice {0},Номер счета поставщика отсутствует в счете на покупку {0},
 Supplier Name,наименование поставщика,
 Supplier Part No,Деталь поставщика №,
 Supplier Quotation,Предложение поставщика,
-Supplier Scorecard,Поставщик Scorecard,
-Supplier Warehouse mandatory for sub-contracted Purchase Receipt,Поставщик Склад обязательным для субподрядчиком ТОВАРНЫЙ ЧЕК,
+Supplier Scorecard,Оценочная карта поставщика,
+Supplier Warehouse mandatory for sub-contracted Purchase Receipt,Наличие склада поставщика обязательно для субподрядной квитанции о покупке,
 Supplier database.,База данных поставщиков.,
 Supplier {0} not found in {1},Поставщик {0} не найден в {1},
-Supplier(s),Поставщик (и),
+Supplier(s),Поставщик(и),
 Supplies made to UIN holders,Поставки для держателей UIN,
 Supplies made to Unregistered Persons,"Поставки, сделанные незарегистрированным лицам",
 Suppliies made to Composition Taxable Persons,"Поставки, сделанные в состав облагаемых лиц",
@@ -2891,7 +2889,7 @@
 Support,Поддержка,
 Support Analytics,Аналитика поддержки,
 Support Settings,Настройки поддержки,
-Support Tickets,Билеты на поддержку,
+Support Tickets,Заявки на поддержку,
 Support queries from customers.,Поддержка запросов от клиентов.,
 Susceptible,восприимчивый,
 Sync has been temporarily disabled because maximum retries have been exceeded,"Синхронизация временно отключена, поскольку превышены максимальные повторные попытки",
@@ -2900,43 +2898,43 @@
 System Manager,Менеджер системы,
 TDS Rate %,TDS Rate%,
 Tap items to add them here,"Выберите продукты, чтобы добавить их",
-Target,цель,
+Target,Цель,
 Target ({}),Цель ({}),
 Target On,Целевая На,
 Target Warehouse,Склад готовой продукции,
 Target warehouse is mandatory for row {0},Целевая склад является обязательным для ряда {0},
-Task,задача,
+Task,Задача,
 Tasks,Задачи,
 Tasks have been created for managing the {0} disease (on row {1}),Задачи были созданы для управления {0} болезнью (в строке {1}),
-Tax,налог,
+Tax,Налог,
 Tax Assets,Налоговые активы,
 Tax Category,Налоговая категория,
 Tax Category for overriding tax rates.,Налоговая категория для переопределения налоговых ставок.,
 "Tax Category has been changed to ""Total"" because all the Items are non-stock items","Налоговая категория была изменена на «Итого», потому что все элементы не являются складскими запасами",
 Tax ID,ИНН,
-Tax Id: ,Идентификатор налога:,
+Tax Id: ,Идентификатор налога: ,
 Tax Rate,Размер налога,
-Tax Rule Conflicts with {0},Налоговый Правило конфликты с {0},
-Tax Rule for transactions.,Налоговый Правило для сделок.,
+Tax Rule Conflicts with {0},Налоговое правило конфликтует с {0},
+Tax Rule for transactions.,Налоговое правило для сделок.,
 Tax Template is mandatory.,Налоговый шаблона является обязательным.,
 Tax Withholding rates to be applied on transactions.,"Ставки налога на удержание, применяемые к сделкам.",
 Tax template for buying transactions.,Налоговый шаблон для покупки сделок.,
 Tax template for item tax rates.,Налоговый шаблон для налоговых ставок.,
 Tax template for selling transactions.,Налоговый шаблон для продажи сделок.,
 Taxable Amount,Налогооблагаемая сумма,
-Taxes,налоги,
+Taxes,Налоги,
 Team Updates,Команда обновления,
 Technology,Технология,
 Telecommunications,Телекоммуникации,
-Telephone Expenses,Телефон Расходы,
-Television,телевидение,
-Template Name,Имя Шаблона,
+Telephone Expenses,Телефонные расходы,
+Television,Телевидение,
+Template Name,Название шаблона,
 Template of terms or contract.,Шаблон терминов или договором.,
 Templates of supplier scorecard criteria.,Шаблоны критериев оценки поставщиков.,
 Templates of supplier scorecard variables.,Шаблоны переменных показателей поставщика.,
 Templates of supplier standings.,Шаблоны позиций поставщиков.,
 Temporarily on Hold,Временно в режиме удержания,
-Temporary,временный,
+Temporary,Временный,
 Temporary Accounts,Временные счета,
 Temporary Opening,Временное открытие,
 Terms and Conditions,Правила и условия,
@@ -2965,7 +2963,7 @@
 The name of your company for which you are setting up this system.,"Название вашей компании, для которой вы настраиваете эту систему.",
 The number of shares and the share numbers are inconsistent,Количество акций и номеров акций несовместимы,
 The payment gateway account in plan {0} is different from the payment gateway account in this payment request,Учетная запись платежного шлюза в плане {0} отличается от учетной записи платежного шлюза в этом платежном запросе,
-The selected BOMs are not for the same item,Выбранные ВМ не для одного продукта,
+The selected BOMs are not for the same item,Выбранные спецификации не для одного продукта,
 The selected item cannot have Batch,Выбранный продукт не может иметь партию,
 The seller and the buyer cannot be the same,Продавец и покупатель не могут быть одинаковыми,
 The shareholder does not belong to this company,Акционер не принадлежит к этой компании,
@@ -2974,7 +2972,7 @@
 "The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Draft stage",Задача была поставлена в качестве фонового задания. В случае возникновения каких-либо проблем с обработкой в фоновом режиме система добавит комментарий об ошибке в этой сверке запасов и вернется к этапу черновика.,
 "Then Pricing Rules are filtered out based on Customer, Customer Group, Territory, Supplier, Supplier Type, Campaign, Sales Partner etc.","Затем Правила ценообразования отфильтровываются на основе Клиента, Группы клиентов, Территории, Поставщика, Типа поставщика, Кампании, Партнера по продажам и т. д.",
 "There are inconsistencies between the rate, no of shares and the amount calculated","Существуют несоответствия между ставкой, количеством акций и рассчитанной суммой",
-There are more holidays than working days this month.,"Есть больше праздников, чем рабочих дней в этом месяце.",
+There are more holidays than working days this month.,"В этом месяце праздников больше, чем рабочих дней.",
 There can be multiple tiered collection factor based on the total spent. But the conversion factor for redemption will always be same for all the tier.,"Может быть многоуровневый коэффициент сбора, основанный на общей затрате. Но коэффициент пересчета для погашения всегда будет одинаковым для всех уровней.",
 There can only be 1 Account per Company in {0} {1},Там может быть только 1 аккаунт на компанию в {0} {1},
 "There can only be one Shipping Rule Condition with 0 or blank value for ""To Value""","Там может быть только один Правило Начальные с 0 или пустое значение для ""To Размер""",
@@ -2992,18 +2990,18 @@
 This action will stop future billing. Are you sure you want to cancel this subscription?,Это действие остановит будущий биллинг. Вы действительно хотите отменить эту подписку?,
 This covers all scorecards tied to this Setup,"Это охватывает все оценочные карточки, привязанные к этой настройке",
 This document is over limit by {0} {1} for item {4}. Are you making another {3} against the same {2}?,Этот документ находится над пределом {0} {1} для элемента {4}. Вы делаете другой {3} против того же {2}?,
-This is a root account and cannot be edited.,Это корень счета и не могут быть изменены.,
-This is a root customer group and cannot be edited.,Это корневая группа клиентов и не могут быть изменены.,
-This is a root department and cannot be edited.,Это корневой отдел и не может быть отредактирован.,
+This is a root account and cannot be edited.,Это кореневой счет и он не может быть изменен.,
+This is a root customer group and cannot be edited.,Это корневая группа клиентов и она не может быть изменена.,
+This is a root department and cannot be edited.,Это корневой отдел и он не может быть отредактирован.,
 This is a root healthcare service unit and cannot be edited.,Это корневая служба здравоохранения и не может быть отредактирована.,
-This is a root item group and cannot be edited.,Это корень группу товаров и не могут быть изменены.,
-This is a root sales person and cannot be edited.,Это корень продавец и не могут быть изменены.,
-This is a root supplier group and cannot be edited.,Это группа поставщиков корней и не может быть отредактирована.,
-This is a root territory and cannot be edited.,Это корень территории и не могут быть изменены.,
-This is an example website auto-generated from ERPNext,Это пример сайт автоматически сгенерированный из ERPNext,
-This is based on logs against this Vehicle. See timeline below for details,Это основано на бревнах против этого транспортного средства. См график ниже для получения подробной информации,
-This is based on stock movement. See {0} for details,Это основано на фондовом движении. См {0} для получения более подробной,
-This is based on the Time Sheets created against this project,"Это основано на табелей учета рабочего времени, созданных против этого проекта",
+This is a root item group and cannot be edited.,Это корень группы продуктов и не может быть изменен.,
+This is a root sales person and cannot be edited.,Это корневой продавец и не может быть изменен.,
+This is a root supplier group and cannot be edited.,Это корневая группа поставщиков и она не может быть отредактирована.,
+This is a root territory and cannot be edited.,Это корневая территория и не может быть изменена.,
+This is an example website auto-generated from ERPNext,Это пример сайта автоматически сгенерированного из ERPNext,
+This is based on logs against this Vehicle. See timeline below for details,Это основано на журналах для этого транспортного средства. Смотрите график ниже для деталей,
+This is based on stock movement. See {0} for details,Это основано на движении остатков. См {0} для получения более подробной,
+This is based on the Time Sheets created against this project,"Это основано на табелях учета рабочего времени, созданных по этому проекту",
 This is based on the attendance of this Employee,Это основано на посещаемости этого сотрудника,
 This is based on the attendance of this Student,Это основано на посещаемости этого студента,
 This is based on transactions against this Customer. See timeline below for details,Это основано на операциях против этого клиента. См график ниже для получения подробной информации,
@@ -3018,15 +3016,15 @@
 "Time slot skiped, the slot {0} to {1} overlap exisiting slot {2} to {3}","Временной интервал пропущен, слот {0} - {1} перекрывает существующий слот {2} до {3}",
 Time slots added,Добавлены временные интервалы,
 Time(in mins),Время (в мин),
-Timer,таймер,
+Timer,Таймер,
 Timer exceeded the given hours.,Таймер превысил указанные часы.,
-Timesheet,табель,
+Timesheet,Табель,
 Timesheet for tasks.,Табель для задач.,
 Timesheet {0} is already completed or cancelled,Табель {0} уже заполнен или отменен,
 Timesheets,Табели,
-"Timesheets help keep track of time, cost and billing for activites done by your team","Timesheets поможет отслеживать время, стоимость и выставление счетов для Активности сделанной вашей команды",
+"Timesheets help keep track of time, cost and billing for activites done by your team","Табели учета рабочего времени помогают отслеживать время, стоимость и выставлять счета за работу, проделанную вашей командой.",
 Titles for print templates e.g. Proforma Invoice.,"Титулы для шаблонов печати, например, счет-проформа.",
-To,к,
+To,К,
 To Address 1,Адрес 1,
 To Address 2,Адрес 2,
 To Bill,Укомплектован,
@@ -3034,8 +3032,8 @@
 To Date cannot be before From Date,На сегодняшний день не может быть раньше от даты,
 To Date cannot be less than From Date,"Дата не может быть меньше, чем с даты",
 To Date must be greater than From Date,"До даты должно быть больше, чем с даты",
-To Date should be within the Fiscal Year. Assuming To Date = {0},Чтобы Дата должна быть в пределах финансового года. Предполагая To Date = {0},
-To Datetime,Для DateTime,
+"To Date should be within the Fiscal Year. Assuming To Date = {0}","Дата должна быть в пределах финансового года. Предположим, до даты = {0}",
+To Datetime,Ко времени,
 To Deliver,Для доставки,
 To Deliver and Bill,Для доставки и оплаты,
 To Fiscal Year,К финансовому году,
@@ -3043,10 +3041,10 @@
 To Party Name,Название партии,
 To Pin Code,К PIN-коду,
 To Place,Положить,
-To Receive,Получить,
+To Receive,К получению,
 To Receive and Bill,Для приема и Билл,
 To State,Государство,
-To Warehouse,Для Склад,
+To Warehouse,Для склада,
 To create a Payment Request reference document is required,Для создания ссылочного документа запроса платежа требуется,
 To date can not be equal or less than from date,"На сегодняшний день не может быть равным или меньше, чем с даты",
 To date can not be less than from date,"На сегодняшний день не может быть меньше, чем с даты",
@@ -3061,13 +3059,13 @@
 To view logs of Loyalty Points assigned to a Customer.,"Просмотр журналов лояльности, назначенных Клиенту.",
 To {0},Для {0},
 To {0} | {1} {2},Для {0} | {1} {2},
-Toggle Filters,Toggle Filters,
+Toggle Filters,Изменить фильтры,
 Too many columns. Export the report and print it using a spreadsheet application.,Слишком много столбцов. Экспортируйте отчет и распечатайте его с помощью приложения для электронных таблиц.,
 Tools,Инструменты,
 Total (Credit),Итого (кредит),
 Total (Without Tax),Всего (без налога),
-Total Absent,Всего Отсутствует,
-Total Achieved,Всего Выполнено,
+Total Absent,Всего отсутствует,
+Total Achieved,Всего выполнено,
 Total Actual,Общий фактический,
 Total Allocated Leaves,Всего выделенных листов,
 Total Amount,Общая сумма,
@@ -3079,10 +3077,10 @@
 Total Contribution Amount: {0},Общая сумма вклада: {0},
 Total Credit/ Debit Amount should be same as linked Journal Entry,"Общая сумма кредита / дебетовой суммы должна быть такой же, как связанная запись журнала",
 Total Debit must be equal to Total Credit. The difference is {0},"Всего Дебет должна быть равна общей выработке. Разница в том, {0}",
-Total Deduction,Всего Вычет,
-Total Invoiced Amount,Всего Сумма по счетам,
-Total Leaves,Всего Листья,
-Total Order Considered,Итоговый заказ считается,
+Total Deduction,Общий вычет,
+Total Invoiced Amount,Общая сумма по счетам,
+Total Leaves,Всего отпусков,
+Total Order Considered,Всего рассмотренных заказов,
 Total Order Value,Общая стоимость заказа,
 Total Outgoing,Всего исходящих,
 Total Outstanding,Всего выдающихся,
@@ -3092,12 +3090,12 @@
 Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total,Общая сумма платежа в Графе платежей должна быть равна Grand / Rounded Total,
 Total Payments,Всего платежей,
 Total Present,Итого Текущая,
-Total Qty,Всего кол-во,
+Total Qty,Общее количество,
 Total Quantity,Общая численность,
 Total Revenue,Общий доход,
 Total Student,Всего учеников,
-Total Target,Всего Target,
-Total Tax,Совокупная налоговая,
+Total Target,Всего целей,
+Total Tax,Совокупный налог,
 Total Taxable Amount,Общая сумма налогооблагаемой суммы,
 Total Taxable Value,Общая налогооблагаемая стоимость,
 Total Unpaid: {0},Общая сумма невыплаченных: {0},
@@ -3119,36 +3117,36 @@
 "Total {0} for all items is zero, may be you should change 'Distribute Charges Based On'","Всего {0} для всех элементов равно нулю, может быть, вы должны изменить «Распределить плату на основе»",
 Total(Amt),Всего (сумма),
 Total(Qty),Всего (кол-во),
-Traceability,прослеживаемость,
+Traceability,Прослеживаемость,
 Traceback,Диагностика,
-Track Leads by Lead Source.,Отслеживать Обращения по Источнику.,
+Track Leads by Lead Source.,Отслеживать лидов по источнику.,
 Training,Обучение,
 Training Event,Учебное мероприятие,
 Training Events,Учебные мероприятия,
-Training Feedback,Обучение Обратная связь,
+Training Feedback,Обучение обратная связь,
 Training Result,Результат обучения,
 Transaction,Транзакция,
-Transaction Date,Сделка Дата,
+Transaction Date,Дата транзакции,
 Transaction Type,Тип операции,
 Transaction currency must be same as Payment Gateway currency,"Валюта сделки должна быть такой же, как платежный шлюз валюты",
 Transaction not allowed against stopped Work Order {0},Транзакция не разрешена против прекращенного рабочего заказа {0},
 Transaction reference no {0} dated {1},Референция сделка не {0} от {1},
-Transactions,операции,
+Transactions,Транзакции,
 Transactions can only be deleted by the creator of the Company,Сделки могут быть удалены только создателем компании,
-Transfer,Переложить,
+Transfer,Передача,
 Transfer Material,О передаче материала,
 Transfer Type,Тип передачи,
 Transfer an asset from one warehouse to another,Передача актива с одного склада на другой,
 Transfered,Все передаваемые,
 Transferred Quantity,Переданное количество,
 Transport Receipt Date,Дата получения транспортного сообщения,
-Transport Receipt No,Транспортная квитанция Нет,
+Transport Receipt No,Транспортная квитанция №,
 Transportation,Транспортировка,
 Transporter ID,Идентификатор транспортника,
 Transporter Name,Название транспорта,
 Travel,Путешествия,
-Travel Expenses,Командировочные Pасходы,
-Tree Type,Дерево Тип,
+Travel Expenses,Командировочные расходы,
+Tree Type,Дерево тип,
 Tree of Bill of Materials,Дерево Билла материалов,
 Tree of Item Groups.,Структура продуктовых групп,
 Tree of Procedures,Дерево процедур,
@@ -3159,7 +3157,7 @@
 Trial Period End Date Cannot be before Trial Period Start Date,Дата окончания пробного периода Не может быть до начала периода пробного периода,
 Trialling,возможность проверки,
 Type of Business,Тип бизнеса,
-Types of activities for Time Logs,Виды деятельности для Время Журналы,
+Types of activities for Time Logs,Типы действий для учета времени,
 UOM,Единица измерения,
 UOM Conversion factor is required in row {0},Фактор Единица измерения преобразования требуется в строке {0},
 UOM coversion factor required for UOM: {0} in Item: {1},Единица измерения фактором Конверсия требуется для UOM: {0} в пункте: {1},
@@ -3167,39 +3165,39 @@
 Unable to find DocType {0},Не удалось найти DocType {0},
 Unable to find exchange rate for {0} to {1} for key date {2}. Please create a Currency Exchange record manually,Невозможно найти обменный курс {0} до {1} за контрольную дату {2}. Создайте запись обмена валюты вручную.,
 Unable to find score starting at {0}. You need to have standing scores covering 0 to 100,"Не удалось найти результат, начинающийся с {0}. Вы должны иметь постоянные баллы, покрывающие 0 до 100",
-Unable to find variable: ,Не удалось найти переменную:,
-Unblock Invoice,Разблокировать счет-фактуру,
+Unable to find variable: ,Не удалось найти переменную: ,
+Unblock Invoice,Разблокировать счет,
 Uncheck all,Снять все,
-Unclosed Fiscal Years Profit / Loss (Credit),Unclosed финансовых лет Прибыль / убыток (Кредит),
+Unclosed Fiscal Years Profit / Loss (Credit),Прибыль/убыток за незакрытые финансовые годы (кредит),
 Unit,Единица,
 Unit of Measure,Единица измерения,
 Unit of Measure {0} has been entered more than once in Conversion Factor Table,Единица измерения {0} был введен более чем один раз в таблицу преобразования Factor,
-Unknown,неизвестный,
+Unknown,Неизвестный,
 Unpaid,Неоплачено,
 Unsecured Loans,Необеспеченных кредитов,
 Unsubscribe from this Email Digest,Отписаться от этого дайджеста электронной почты,
 Unsubscribed,Отписался,
 Until,До,
 Unverified Webhook Data,Непроверенные данные Webhook,
-Update Account Name / Number,Обновить имя учетной записи / номер,
-Update Account Number / Name,Обновить номер / имя учетной записи,
-Update Cost,Обновление Стоимость,
+Update Account Name / Number,Обновить имя / номер счета,
+Update Account Number / Name,Обновить номер / имя счета,
+Update Cost,Обновить стоимость,
 Update Items,Обновить элементы,
-Update Print Format,Обновление Формат печати,
+Update Print Format,Обновить формат печати,
 Update Response,Обновить ответ,
 Update bank payment dates with journals.,Обновление банк платежные даты с журналов.,
 Update in progress. It might take a while.,Идет обновление. Это может занять некоторое время.,
 Update rate as per last purchase,Скорость обновления согласно последней покупке,
-Update stock must be enable for the purchase invoice {0},Для покупки счета-фактуры {0} необходимо включить обновление запасов,
-Updating Variants...,Обновление вариантов ...,
+Update stock must be enable for the purchase invoice {0},Обновление запасов должно быть включено для счета на покупку {0},
+Updating Variants...,Обновление вариантов...,
 Upload your letter head and logo. (you can edit them later).,Загрузить шапку фирменного бланка и логотип. (Вы можете отредактировать их позднее).,
 Upper Income,Высокий уровень дохода,
 Use Sandbox,Использовать «песочницу»,
 Used Leaves,Используемые листы,
-User,пользователь,
+User,Пользователь,
 User ID,ID пользователя,
 User ID not set for Employee {0},ID пользователя не установлен для сотрудника {0},
-User Remark,Примечание Пользователь,
+User Remark,Примечание пользователя,
 User has not applied rule on the invoice {0},Пользователь не применил правило к счету {0},
 User {0} already exists,Пользователь {0} уже существует,
 User {0} created,Пользователь {0} создан,
@@ -3215,7 +3213,7 @@
 Valid from date must be less than valid upto date,Срок действия с даты должен быть меньше срока действия до даты,
 Valid till date cannot be before transaction date,Действителен до даты не может быть до даты транзакции,
 Validity,Период действия,
-Validity period of this quotation has ended.,Срок действия этого Предложения истек,
+Validity period of this quotation has ended.,Срок действия этого предложения истек,
 Valuation Rate,Оценка,
 Valuation Rate is mandatory if Opening Stock entered,"Оценка Оцените является обязательным, если введен Открытие изображения",
 Valuation type charges can not marked as Inclusive,Обвинения типа Оценка не может отмечен как включено,
@@ -3224,38 +3222,38 @@
 Value for Attribute {0} must be within the range of {1} to {2} in the increments of {3} for Item {4},Значение атрибута {0} должно быть в диапазоне от {1} до {2} в приращений {3} для п {4},
 Value missing,Недостающее значение,
 Value must be between {0} and {1},Значение должно быть между {0} и {1},
-"Values of exempt, nil rated and non-GST inward supplies","Значения льготных, нулевых и не связанных с GST внутренних поставок",
-Variable,переменная,
+"Values of exempt, nil rated and non-GST inward supplies","Значения льготных, нулевых и не связанных с НДС внутренних поставок",
+Variable,Переменная,
 Variance,Дисперсия,
 Variance ({}),Дисперсия ({}),
 Variant,Вариант,
-Variant Attributes,Вариант Атрибуты,
+Variant Attributes,Атрибуты варианта,
 Variant Based On cannot be changed,Вариант на основе не может быть изменен,
 Variant Details Report,Подробный отчет о вариантах,
 Variant creation has been queued.,Создание вариантов было поставлено в очередь.,
 Vehicle Expenses,Расходы транспортных средств,
 Vehicle No,Автомобиль №,
-Vehicle Type,тип машины,
+Vehicle Type,Тип автомобиля,
 Vehicle/Bus Number,Номер транспортного средства / автобуса,
 Venture Capital,Инвестиции,
 View Chart of Accounts,Просмотр схемы счетов,
 View Fees Records,Посмотреть рекорды,
 View Form,Посмотреть форму,
 View Lab Tests,Просмотр лабораторных тестов,
-View Leads,Посмотреть Обращения,
-View Ledger,Посмотреть Леджер,
+View Leads,Посмотреть лиды,
+View Ledger,Посмотреть записи,
 View Now,Просмотр сейчас,
 View a list of all the help videos,Просмотреть список всех справочных видео,
 View in Cart,Смотрите в корзину,
 Visit report for maintenance call.,Посетите отчет за призыв обслуживания.,
 Visit the forums,Посетите форумы,
 Vital Signs,Жизненно важные признаки,
-Volunteer,доброволец,
+Volunteer,Волонтер,
 Volunteer Type information.,Информация о волонтере.,
 Volunteer information.,Информация о волонтерах.,
 Voucher #,Ваучер #,
 Voucher No,Ваучер №,
-Voucher Type,Ваучер Тип,
+Voucher Type,Тип ваучера,
 WIP Warehouse,WIP Склад,
 Walk In,Прогулка в,
 Warehouse can not be deleted as stock ledger entry exists for this warehouse.,"Склад не может быть удалён, так как существует запись в складкой книге  этого склада.",
@@ -3263,7 +3261,7 @@
 Warehouse is mandatory,Склад является обязательным,
 Warehouse is mandatory for stock Item {0} in row {1},Склад является обязательным для Запаса {0} в строке {1},
 Warehouse not found in the system,Склад не найден в системе,
-"Warehouse required at Row No {0}, please set default warehouse for the item {1} for the company {2}","Требуется хранилище в строке «Нет» {0}, пожалуйста, установите для хранилища по умолчанию для товара {1} для компании {2}",
+"Warehouse required at Row No {0}, please set default warehouse for the item {1} for the company {2}","Требуется хранилище в строке № {0}, пожалуйста, установите для хранилища по умолчанию для товара {1} для компании {2}",
 Warehouse required for stock Item {0},Требуется Склад для Запаса {0},
 Warehouse {0} can not be deleted as quantity exists for Item {1},Склад {0} не может быть удален как существует количество для Пункт {1},
 Warehouse {0} does not belong to company {1},Склад {0} не принадлежит компания {1},
@@ -3299,7 +3297,7 @@
 What do you need help with?,Как я могу вам помочь?,
 What does it do?,Что оно делает?,
 Where manufacturing operations are carried.,Где производственные операции проводятся.,
-White,белый,
+White,Белый,
 Wire Transfer,Банковский перевод,
 WooCommerce Products,Продукты WooCommerce,
 Work In Progress,Незавершенная работа,
@@ -3313,11 +3311,11 @@
 Work Orders Created: {0},Созданы рабочие задания: {0},
 Work Summary for {0},Резюме работы для {0},
 Work-in-Progress Warehouse is required before Submit,Работа-в-Прогресс Склад требуется перед Отправить,
-Workflow,Поток,
-Working,Работающий,
+Workflow,Рабочий процесс,
+Working,В работе,
 Working Hours,Часы работы,
-Workstation,рабочая станция,
-Workstation is closed on the following dates as per Holiday List: {0},Рабочая станция закрыта в следующие сроки согласно Список праздников: {0},
+Workstation,Рабочее место,
+Workstation is closed on the following dates as per Holiday List: {0},Рабочая место закрыто в следующие даты согласно списка праздников: {0},
 Wrapping up,Завершение,
 Wrong Password,Неправильный пароль,
 Year start date or end date is overlapping with {0}. To avoid please set company,"Год дата начала или дата окончания перекрывается с {0}. Чтобы избежать, пожалуйста, установите компанию",
@@ -3325,7 +3323,7 @@
 You are not authorized to approve leaves on Block Dates,Вы не уполномочен утверждать листья на Блок Сроки,
 You are not authorized to set Frozen value,Ваши настройки доступа не позволяют замораживать значения,
 You are not present all day(s) between compensatory leave request days,Вы не присутствуете весь день (ы) между днями запроса компенсационного отпуска,
-You can not change rate if BOM mentioned agianst any item,"Вы не можете изменить рейтинг, если ВМ упоминается agianst любого продукта",
+You can not change rate if BOM mentioned agianst any item,"Вы не можете изменить оценку спецификации, упомянутой для любого элемента",
 You can not enter current voucher in 'Against Journal Entry' column,Вы не можете ввести текущий ваучер в «Против Запись в журнале 'колонке,
 You can only have Plans with the same billing cycle in a Subscription,У вас могут быть только планы с одинаковым биллинговым циклом в подписке,
 You can only redeem max {0} points in this order.,Вы можете выкупить только max {0} очков в этом порядке.,
@@ -3335,7 +3333,7 @@
 You can't redeem Loyalty Points having more value than the Grand Total.,"Вы не можете выкупить очки лояльности, имеющие большую ценность, чем Grand Total.",
 You cannot credit and debit same account at the same time,Нельзя кредитовать и дебетовать один счёт за один раз,
 You cannot delete Fiscal Year {0}. Fiscal Year {0} is set as default in Global Settings,Нельзя удалить {0} финансовый год. Финансовый год {0} установлен  основным в глобальных параметрах,
-You cannot delete Project Type 'External',Вы не можете удалить Project Type &#39;External&#39;,
+You cannot delete Project Type 'External',Вы не можете удалить проект типа "Внешний",
 You cannot edit root node.,Вы не можете редактировать корневой узел.,
 You cannot restart a Subscription that is not cancelled.,"Вы не можете перезапустить подписку, которая не отменена.",
 You don't have enought Loyalty Points to redeem,У вас недостаточно очков лояльности для выкупа,
@@ -3352,8 +3350,8 @@
 Your Organization,Ваша организация,
 Your cart is Empty,Ваша корзина пуста,
 Your email address...,Ваш адрес электронной почты...,
-Your order is out for delivery!,Ваш заказ для доставки!,
-Your tickets,Ваши билеты,
+Your order is out for delivery!,Ваш заказ готов к доставке!,
+Your tickets,Ваши заявки,
 ZIP Code,Почтовый индекс,
 [Error],[Ошибка],
 [{0}](#Form/Item/{0}) is out of stock,[{0}](#Form/Item/{0}) нет в наличии,
@@ -3364,10 +3362,10 @@
 "e.g. ""Build tools for builders""","например ""Построить инструменты для строителей """,
 "e.g. ""Primary School"" or ""University""","например, &quot;Начальная школа&quot; или &quot;Университет&quot;",
 "e.g. Bank, Cash, Credit Card","например банк, наличные, кредитная карта",
-hidden,Скрытый,
+hidden,скрытый,
 modified,модифицированный,
 old_parent,old_parent,
-on,Вкл,
+on,вкл,
 {0} '{1}' is disabled,{0} '{1}' отключен,
 {0} '{1}' not in Fiscal Year {2},{0} '{1}' не в {2} Финансовом году,
 {0} ({1}) cannot be greater than planned quantity ({2}) in Work Order {3},{0} ({1}) не может быть больше запланированного количества ({2}) в рабочем порядке {3},
@@ -3389,7 +3387,7 @@
 {0} applicable after {1} working days,{0} применимо после {1} рабочих дней,
 {0} asset cannot be transferred,{0} актив не может быть перемещён,
 {0} can not be negative,{0} не может быть отрицательным,
-{0} created,Создано {0},
+{0} created,{0} создано,
 "{0} currently has a {1} Supplier Scorecard standing, and Purchase Orders to this supplier should be issued with caution.","{0} в настоящее время имеет {1} систему показателей поставщика, и Заказы на поставку этому поставщику должны выдаваться с осторожностью.",
 "{0} currently has a {1} Supplier Scorecard standing, and RFQs to this supplier should be issued with caution.","{0} в настоящее время имеет {1} систему показателей поставщика, и RFQ для этого поставщика должны выдаваться с осторожностью.",
 {0} does not belong to Company {1},{0} не принадлежит компании {1},
@@ -3446,10 +3444,10 @@
 {0} {1} is not submitted,{0} {1} не проведен,
 {0} {1} is {2},{0} {1} - это {2},
 {0} {1} must be submitted,{0} {1} должен быть проведен,
-{0} {1} not in any active Fiscal Year.,{0} {1} не в каком-либо активном финансовом годе.,
+{0} {1} not in any active Fiscal Year.,{0} {1} нет не в одном активном финансовом годе.,
 {0} {1} status is {2},{0} {1} статус — {2},
 {0} {1}: 'Profit and Loss' type account {2} not allowed in Opening Entry,"{0} {1}: тип счета {2} ""Прибыли и убытки"" не допускается в качестве начальной проводки",
-{0} {1}: Account {2} does not belong to Company {3},{0} {1}: Счет {2} не принадлежит Компании {3},
+{0} {1}: Account {2} does not belong to Company {3},{0} {1}: Счет {2} не принадлежит компании {3},
 {0} {1}: Account {2} is inactive,{0} {1}: Счет {2} неактивен,
 {0} {1}: Accounting Entry for {2} can only be made in currency: {3},{0} {1}: Бухгалтерская запись для {2} может быть сделана только в валюте: {3},
 {0} {1}: Cost Center is mandatory for Item {2},{0} {1}: МВЗ является обязательным для позиции {2},
@@ -3458,7 +3456,7 @@
 {0} {1}: Customer is required against Receivable account {2},{0} {1}: Наименование клиента обязательно для Дебиторской задолженности {2},
 {0} {1}: Either debit or credit amount is required for {2},{0} {1}: Требуется указать сумму дебета или кредита для {2},
 {0} {1}: Supplier is required against Payable account {2},{0} {1}: Наименование поставщика обязательно для кредиторской задолженности {2},
-{0}% Billed,{0} % оплачено,
+{0}% Billed,{0}% оплачено,
 {0}% Delivered,{0}% доставлено,
 "{0}: Employee email not found, hence email not sent","{0}: Адрес электронной почты сотрудника не найден, поэтому письмо не отправлено",
 {0}: From {0} of type {1},{0}: От {0} типа {1},
@@ -3469,13 +3467,13 @@
 Assigned To,Назначено для,
 Chat,Чат,
 Completed By,Завершено,
-Conditions,условия,
-County,округ,
+Conditions,Условия,
+County,Округ,
 Day of Week,День недели,
-"Dear System Manager,","Уважаемый Менеджер системы,",
+"Dear System Manager,","Уважаемый менеджер системы,",
 Default Value,Значение по умолчанию,
 Email Group,Группа электронной почты,
-Email Settings,Настройки Электронной Почты,
+Email Settings,Настройки электронной почты,
 Email not sent to {0} (unsubscribed / disabled),Письмо не отправлено {0} (отписан / отключено),
 Error Message,Сообщение об ошибке,
 Fieldtype,Тип поля,
@@ -3483,11 +3481,11 @@
 ID,ID,
 Images,Изображении,
 Import,Импорт,
-Language,язык,
-Likes,Понравившееся,
+Language,Язык,
+Likes,Лайки,
 Merge with existing,Слияние с существующими,
 Office,Офис,
-Orientation,ориентация,
+Orientation,Ориентация,
 Parent,Родитель,
 Passive,Пассивный,
 Payment Failed,Платеж не прошел,
@@ -3499,25 +3497,25 @@
 Postal,Почтовый,
 Postal Code,Почтовый индекс,
 Previous,Предыдущая,
-Provider,поставщик,
+Provider,Поставщик,
 Read Only,Только чтения,
 Recipient,Сторона-реципиент,
 Reviews,Отзывы,
 Sender,Отправитель,
 Shop,Магазин,
-Sign Up,Подписаться,
+Sign Up,Регистрация,
 Subsidiary,Филиал,
 There is some problem with the file url: {0},Существует некоторая проблема с файловой URL: {0},
 There were errors while sending email. Please try again.,"При отправке электронной почты возникли ошибки. Пожалуйста, попробуйте ещё раз.",
-Values Changed,Значения Изменено,
+Values Changed,Значения изменено,
 or,или,
 Ageing Range 4,Диапазон старения 4,
 Allocated amount cannot be greater than unadjusted amount,Выделенная сумма не может быть больше нескорректированной,
 Allocated amount cannot be negative,Выделенная сумма не может быть отрицательной,
 "Difference Account must be a Asset/Liability type account, since this Stock Entry is an Opening Entry","Разница счета должна быть учетной записью типа актива / пассива, так как эта запись акции является вводной",
 Error in some rows,Ошибка в некоторых строках,
-Import Successful,Импорт успешен,
-Please save first,"Пожалуйста, сохраните сначала",
+Import Successful,Импорт успешно завершен,
+Please save first,"Пожалуйста, сначала сохраните",
 Price not found for item {0} in price list {1},Цена не найдена для товара {0} в прайс-листе {1},
 Warehouse Type,Тип склада,
 'Date' is required,Требуется дата,
@@ -3526,18 +3524,18 @@
 Bundle Qty,Комплект кол-во,
 Company GSTIN,Компания GSTIN,
 Company field is required,Поле компании обязательно для заполнения,
-Creating Dimensions...,Создание размеров ...,
+Creating Dimensions...,Создание размеров...,
 Duplicate entry against the item code {0} and manufacturer {1},Повторяющаяся запись с кодом товара {0} и производителем {1},
 Invalid GSTIN! The input you've entered doesn't match the GSTIN format for UIN Holders or Non-Resident OIDAR Service Providers,Неверный GSTIN! Введенный вами ввод не соответствует формату GSTIN для владельцев UIN или нерезидентов OIDAR.,
-Invoice Grand Total,Счет-фактура Grand Total,
+Invoice Grand Total,Общая сумма счета,
 Last carbon check date cannot be a future date,Дата последней проверки углерода не может быть датой в будущем,
 Make Stock Entry,Сделать складской запас,
 Quality Feedback,Отзыв о качестве,
 Quality Feedback Template,Шаблон обратной связи по качеству,
 Rules for applying different promotional schemes.,Правила применения разных рекламных схем.,
-Shift,сдвиг,
+Shift,Сдвиг,
 Show {0},Показать {0},
-"Special Characters except ""-"", ""#"", ""."", ""/"", ""{"" and ""}"" not allowed in naming series","Специальные символы, кроме &quot;-&quot;, &quot;#&quot;, &quot;.&quot;, &quot;/&quot;, &quot;{&quot; И &quot;}&quot;, не допускаются в именных сериях",
+"Special Characters except ""-"", ""#"", ""."", ""/"", ""{"" and ""}"" not allowed in naming series","Специальные символы, кроме ""-"", ""#"", ""."", ""/"", ""{"" и ""}"", не допускаются в серийных номерах",
 Target Details,Детали цели,
 {0} already has a Parent Procedure {1}.,{0} уже имеет родительскую процедуру {1}.,
 API,API,
@@ -3546,36 +3544,36 @@
 Change,Изменение,
 Contact Email,Эл.почта для связи,
 Export Type,Тип экспорта,
-From Date,С,
+From Date,С даты,
 Group By,Группа по,
 Importing {0} of {1},Импорт {0} из {1},
-Invalid URL,неправильный адрес,
-Landscape,Пейзаж,
+Invalid URL,Неправильный URL,
+Landscape,Альбомный,
 Last Sync On,Последняя синхронизация,
-Naming Series,Идентификация по Имени,
+Naming Series,Идентификация по имени,
 No data to export,Нет данных для экспорта,
-Portrait,Портрет,
-Print Heading,Распечатать Заголовок,
+Portrait,Портретный,
+Print Heading,Распечатать заголовок,
 Scheduler Inactive,Планировщик неактивен,
 Scheduler is inactive. Cannot import data.,Планировщик неактивен. Невозможно импортировать данные.,
 Show Document,Показать документ,
 Show Traceback,Показать трассировку,
-Video,видео,
+Video,Видео,
 Webhook Secret,Webhook Secret,
 % Of Grand Total,% От общего итога,
-'employee_field_value' and 'timestamp' are required.,&#39;employee_field_value&#39; и &#39;timestamp&#39; являются обязательными.,
+'employee_field_value' and 'timestamp' are required.,"employee_field_value" и "timestamp" являются обязательными.,
 <b>Company</b> is a mandatory filter.,<b>Компания</b> является обязательным фильтром.,
 <b>From Date</b> is a mandatory filter.,<b>С даты</b> является обязательным фильтром.,
-<b>From Time</b> cannot be later than <b>To Time</b> for {0},"<b>Время</b> не может быть позже, чем <b>время</b> для {0}",
+<b>From Time</b> cannot be later than <b>To Time</b> for {0},"<b>Начально время</b> не может быть позже, чем <b>конечное время</b> для {0}",
 <b>To Date</b> is a mandatory filter.,<b>На сегодняшний день</b> это обязательный фильтр.,
 A new appointment has been created for you with {0},Для вас создана новая встреча с {0},
-Account Value,Стоимость аккаунта,
-Account is mandatory to get payment entries,Аккаунт обязателен для получения платежных записей,
-Account is not set for the dashboard chart {0},Учетная запись не настроена для диаграммы панели мониторинга {0},
-Account {0} does not belong to company {1},Аккаунт {0} не принадлежит компании {1},
-Account {0} does not exists in the dashboard chart {1},Аккаунт {0} не существует в диаграмме панели {1},
-Account: <b>{0}</b> is capital Work in progress and can not be updated by Journal Entry,Аккаунт: <b>{0}</b> является капитальным незавершенным и не может быть обновлен в журнале,
-Account: {0} is not permitted under Payment Entry,Аккаунт: {0} не разрешен при вводе платежа,
+Account Value,Стоимость счета,
+Account is mandatory to get payment entries,Счет обязателен для получения платежных записей,
+Account is not set for the dashboard chart {0},Счет не настроен для диаграммы панели мониторинга {0},
+Account {0} does not belong to company {1},Счет {0} не принадлежит компании {1},
+Account {0} does not exists in the dashboard chart {1},Счет {0} не существует в диаграмме панели {1},
+Account: <b>{0}</b> is capital Work in progress and can not be updated by Journal Entry,Счет: <b>{0}</b> является незавершенным и не может быть обновлен в журнале,
+Account: {0} is not permitted under Payment Entry,Счет: {0} не разрешен при вводе платежа,
 Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}.,Параметр учета <b>{0}</b> требуется для счета «Баланс» {1}.,
 Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}.,Параметр учета <b>{0}</b> требуется для счета «Прибыли и убытки» {1}.,
 Accounting Masters,Бухгалтерские мастера,
@@ -3583,7 +3581,7 @@
 Activity,Активность,
 Add / Manage Email Accounts.,Добавление / Управление учетными записями электронной почты,
 Add Child,Добавить потомка,
-Add Loan Security,Добавить кредит безопасности,
+Add Loan Security,Добавить обеспечение по кредиту,
 Add Multiple,Добавить несколько,
 Add Participants,Добавить участников,
 Add to Featured Item,Добавить в избранное,
@@ -3641,48 +3639,48 @@
 Book Appointment,Назначение книги,
 Brand,Бренд,
 Browse,Обзор,
-Call Connected,Call Connected,
+Call Connected,Вызов подключен,
 Call Disconnected,Вызов отключен,
 Call Missed,Звонок пропущен,
 Call Summary,Сводка вызовов,
 Call Summary Saved,Сводка вызовов сохранена,
-Cancelled,отменен,
+Cancelled,Отменен,
 Cannot Calculate Arrival Time as Driver Address is Missing.,"Невозможно рассчитать время прибытия, так как отсутствует адрес водителя.",
-Cannot Optimize Route as Driver Address is Missing.,"Не удается оптимизировать маршрут, так как отсутствует адрес драйвера.",
+Cannot Optimize Route as Driver Address is Missing.,"Не удается оптимизировать маршрут, так как отсутствует адрес водителя.",
 Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.,"Невозможно выполнить задачу {0}, поскольку ее зависимая задача {1} не завершена / не отменена.",
 Cannot create loan until application is approved,"Невозможно создать кредит, пока заявка не будет утверждена",
 Cannot find a matching Item. Please select some other value for {0}.,"Нет столько продуктов. Пожалуйста, выберите другое количество для {0}.",
 "Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings","Невозможно переплатить за элемент {0} в строке {1} более чем {2}. Чтобы разрешить перерасчет, пожалуйста, установите скидку в настройках аккаунта",
-"Capacity Planning Error, planned start time can not be same as end time","Ошибка планирования мощности, запланированное время начала не может совпадать с временем окончания",
-Categories,категории,
+"Capacity Planning Error, planned start time can not be same as end time","Ошибка планирования емкости, запланированное время начала не может совпадать со временем окончания",
+Categories,Категории,
 Changes in {0},Изменения в {0},
 Chart,Диаграмма,
 Choose a corresponding payment,Выберите соответствующий платеж,
 Click on the link below to verify your email and confirm the appointment,"Нажмите на ссылку ниже, чтобы подтвердить свою электронную почту и подтвердить встречу",
 Close,Закрыть,
-Communication,Общение,
+Communication,Коммуникация,
 Compact Item Print,Компактный товара печати,
 Company,Организация,
 Company of asset {0} and purchase document {1} doesn't matches.,Компания актива {0} и документ покупки {1} не совпадают.,
 Compare BOMs for changes in Raw Materials and Operations,Сравните спецификации для изменений в сырье и операциях,
 Compare List function takes on list arguments,Функция сравнения списка принимает аргументы списка,
-Complete,полный,
-Completed,Завершено,
-Completed Quantity,Завершенное количество,
+Complete,Завершенно,
+Completed,Завершенный,
+Completed Quantity,Количество завершенных,
 Connect your Exotel Account to ERPNext and track call logs,Подключите свою учетную запись Exotel к ERPNext и отслеживайте журналы вызовов,
 Connect your bank accounts to ERPNext,Подключите свои банковские счета к ERPNext,
 Contact Seller,Связаться с продавцом,
-Continue,Продолжать,
-Cost Center: {0} does not exist,МВЗ: {0} не существует,
+Continue,Продолжить,
+Cost Center: {0} does not exist,Центр затрат: {0} не существует,
 Couldn't Set Service Level Agreement {0}.,Не удалось установить соглашение об уровне обслуживания {0}.,
 Country,Страна,
 Country Code in File does not match with country code set up in the system,"Код страны в файле не совпадает с кодом страны, установленным в системе",
 Create New Contact,Создать новый контакт,
-Create New Lead,Создать новое руководство,
+Create New Lead,Создать новый лид,
 Create Pick List,Создать список выбора,
 Create Quality Inspection for Item {0},Создать проверку качества для позиции {0},
-Creating Accounts...,Создание аккаунтов ...,
-Creating bank entries...,Создание банковских записей ...,
+Creating Accounts...,Создание счетов...,
+Creating bank entries...,Создание банковских записей...,
 Credit limit is already defined for the Company {0},Кредитный лимит уже определен для Компании {0},
 Ctrl + Enter to submit,Ctrl + Enter для отправки,
 Ctrl+Enter to submit,Ctrl + Enter для отправки,
@@ -3694,7 +3692,7 @@
 Date,Дата,
 Date Range,Диапазон дат,
 Date of Birth cannot be greater than Joining Date.,Дата рождения не может быть больше даты присоединения.,
-Dear,Уважаемый (ая),
+Dear,Уважаемый(ая),
 Default,По умолчанию,
 Define coupon codes.,Определить коды купонов.,
 Delayed Days,Задержанные дни,
@@ -3716,7 +3714,7 @@
 Dr,Доктор,
 Due Date,Дата выполнения,
 Duplicate,Дублировать,
-Duplicate Project with Tasks,Дублирующий проект с задачами,
+Duplicate Project with Tasks,Дублировать проект с задачами,
 Duplicate project has been created,Дублированный проект создан,
 E-Way Bill JSON can only be generated from a submitted document,E-Way Bill JSON может быть создан только из представленного документа,
 E-Way Bill JSON can only be generated from submitted document,E-Way Bill JSON может быть создан только из представленного документа,
@@ -3735,7 +3733,7 @@
 Enable Auto Re-Order,Включить автоматический повторный заказ,
 End Date of Agreement can't be less than today.,"Дата окончания соглашения не может быть меньше, чем сегодня.",
 End Time,Время окончания,
-Energy Point Leaderboard,Таблица лидеров Energy Point,
+Energy Point Leaderboard,Таблица лидеров согласно баллам активности,
 Enter API key in Google Settings.,Введите ключ API в настройках Google.,
 Enter Supplier,Введите поставщика,
 Enter Value,Введите значение,
@@ -3746,18 +3744,18 @@
 Event Link,Ссылка на событие,
 Exception occurred while reconciling {0},Исключительная ситуация при согласовании {0},
 Expected and Discharge dates cannot be less than Admission Schedule date,Ожидаемые и даты выписки не могут быть меньше даты приема,
-Expire Allocation,Expire Allocation,
+Expire Allocation,Истечение срока действия,
 Expired,Истек срок действия,
 Export,Экспорт,
-Export not allowed. You need {0} role to export.,Экспорт не допускается. Вам нужно {0} роль для экспорта.,
-Failed to add Domain,Не удалось добавить домен,
+Export not allowed. You need {0} role to export.,Экспорт не допускается. Вам нужна роль {0} для экспорта.,
+Failed to add Domain,Не удалось добавить вид деятельности,
 Fetch Items from Warehouse,Получить товары со склада,
-Fetching...,Fetching ...,
-Field,поле,
+Fetching...,Получение...,
+Field,Поле,
 File Manager,Файловый менеджер,
 Filters,Фильтры,
 Finding linked payments,Поиск связанных платежей,
-Fleet Management,Управление флотом,
+Fleet Management,Автопарк,
 Following fields are mandatory to create address:,Следующие поля обязательны для создания адреса:,
 For Month,На месяц,
 "For item {0} at row {1}, count of serial numbers does not match with the picked quantity",Для позиции {0} в строке {1} количество серийных номеров не совпадает с выбранным количеством,
@@ -3770,14 +3768,14 @@
 Future Payment Amount,Сумма будущего платежа,
 Future Payment Ref,Будущий платеж Ref,
 Future Payments,Будущие платежи,
-GST HSN Code does not exist for one or more items,GST Код HSN не существует для одного или нескольких пунктов,
+GST HSN Code does not exist for one or more items,НДС ГС-код не существует для одного или нескольких товаров,
 Generate E-Way Bill JSON,Создать электронный билл Билл JSON,
 Get Items,Получить продукты,
 Get Outstanding Documents,Получить выдающиеся документы,
 Goal,Цель,
 Greater Than Amount,"Больше, чем сумма",
 Green,Зеленый,
-Group,группа,
+Group,Группа,
 Group By Customer,Группировать по клиенту,
 Group By Supplier,Группа по поставщикам,
 Group Node,Узел Группа,
@@ -3785,14 +3783,14 @@
 Help,Помощь,
 Help Article,Статья помощи,
 "Helps you keep tracks of Contracts based on Supplier, Customer and Employee","Помогает вам отслеживать контракты на основе поставщика, клиента и сотрудника",
-Helps you manage appointments with your leads,Помогает вам управлять назначениями с вашими лидами,
+Helps you manage appointments with your leads,Помогает вам управлять назначениями с вашими обращениями,
 Home,Главная,
 IBAN is not valid,IBAN недействителен,
 Import Data from CSV / Excel files.,Импорт данных из файлов CSV / Excel.,
 In Progress,Выполняется,
 Incoming call from {0},Входящий звонок от {0},
 Incorrect Warehouse,Неправильный склад,
-Intermediate,промежуточный,
+Intermediate,Промежуточный,
 Invalid Barcode. There is no Item attached to this barcode.,Неверный штрих-код. К этому штрих-коду не прикреплено ни одного предмета.,
 Invalid credentials,Неверные учетные данные,
 Invite as User,Пригласить в пользователя,
@@ -3803,7 +3801,7 @@
 Item listing removed,Список товаров удален,
 Item quantity can not be zero,Количество товара не может быть нулевым,
 Item taxes updated,Товарные налоги обновлены,
-Item {0}: {1} qty produced. ,Элемент {0}: произведено {1} кол-во.,
+Item {0}: {1} qty produced. ,Элемент {0}: произведено {1} кол-во. ,
 Joining Date can not be greater than Leaving Date,"Дата вступления не может быть больше, чем Дата отъезда",
 Lab Test Item {0} already exist,Элемент лабораторного теста {0} уже существует,
 Last Issue,Последний выпуск,
@@ -3811,7 +3809,7 @@
 Leave application is linked with leave allocations {0}. Leave application cannot be set as leave without pay,Заявка на отпуск связана с распределением отпуска {0}. Заявка на отпуск не может быть установлена как отпуск без оплаты,
 Leaves Taken,Листья взяты,
 Less Than Amount,Меньше чем сумма,
-Liabilities,пассивы,
+Liabilities,Обязательства,
 Loading...,Загрузка...,
 Loan Amount exceeds maximum loan amount of {0} as per proposed securities,Сумма кредита превышает максимальную сумму кредита {0} в соответствии с предлагаемыми ценными бумагами,
 Loan Applications from customers and employees.,Кредитные заявки от клиентов и сотрудников.,
@@ -3827,16 +3825,16 @@
 Loan Type for interest and penalty rates,Тип кредита для процентов и пеней,
 Loan amount cannot be greater than {0},Сумма кредита не может превышать {0},
 Loan is mandatory,Кредит обязателен,
-Loans,кредитование,
+Loans,Кредиты,
 Loans provided to customers and employees.,"Кредиты, предоставленные клиентам и сотрудникам.",
 Location,Местоположение,
 Log Type is required for check-ins falling in the shift: {0}.,Тип регистрации необходим для регистрации заезда в смену: {0}.,
 Looks like someone sent you to an incomplete URL. Please ask them to look into it.,"Похоже, кто-то послал вас к неполному URL. Пожалуйста, попросите их посмотреть в нее.",
-Make Journal Entry,Сделать запись журнала,
-Make Purchase Invoice,Сделать счете-фактуре,
+Make Journal Entry,Сделать запись в журнале,
+Make Purchase Invoice,Сделать счет на покупку,
 Manufactured,Изготовлено,
 Mark Work From Home,Пометить работу из дома,
-Master,Магистр,
+Master,Мастер,
 Max strength cannot be less than zero.,Максимальная сила не может быть меньше нуля.,
 Maximum attempts for this quiz reached!,Максимальное количество попыток для этого теста достигнуто!,
 Message,Сообщение,
@@ -3844,11 +3842,11 @@
 Mobile No,Мобильный номер,
 Mobile Number,Мобильный номер,
 Month,Mесяц,
-Name,имя,
+Name,Имя,
 Near you,Возле тебя,
 Net Profit/Loss,Чистая прибыль / убыток,
-New Expense,Новый Расход,
-New Invoice,Новый Счет,
+New Expense,Новый расход,
+New Invoice,Новый счет,
 New Payment,Новый платеж,
 New release date should be in the future,Дата нового релиза должна быть в будущем,
 Newsletter,Рассылка новостей,
@@ -3857,7 +3855,7 @@
 No Leaves Allocated to Employee: {0} for Leave Type: {1},Сотрудникам не выделено ни одного листа: {0} для типа отпуска: {1},
 No communication found.,Связь не найдена.,
 No correct answer is set for {0},Не указан правильный ответ для {0},
-No description,без описания,
+No description,Без описания,
 No issue has been raised by the caller.,Никакая проблема не была поднята вызывающим абонентом.,
 No items to publish,Нет материалов для публикации,
 No outstanding invoices found,Не найдено неоплаченных счетов,
@@ -3869,19 +3867,23 @@
 Not Allowed,Не разрешено,
 Not allowed to create accounting dimension for {0},Не разрешено создавать учетное измерение для {0},
 Not permitted. Please disable the Lab Test Template,"Не разрешено Пожалуйста, отключите шаблон лабораторного теста",
-Note,Заметки,
-Notes: ,Заметки:,
-On Converting Opportunity,О возможности конвертации,
-On Purchase Order Submission,При подаче заказа на поставку,
-On Sales Order Submission,На подаче заказа клиента,
-On Task Completion,По завершении задачи,
+Note,Заметка,
+Notes: ,Заметки: ,
+On Converting Opportunity,Конвертацию возможности,
+On Purchase Order Submission,Офомление заказа на закупку,
+On Sales Order Submission,Оформление заказа на продажу,
+On Task Completion,Завершении задачи,
 On {0} Creation,На {0} создании,
+On Item Creation,Создание продукта,
+On Lead Creation,Создание лида,
+On Supplier Creation,Создание поставщика,
+On Customer Creation,Создание клиента,
 Only .csv and .xlsx files are supported currently,В настоящее время поддерживаются только файлы .csv и .xlsx,
 Only expired allocation can be cancelled,Только истекшее распределение может быть отменено,
 Only users with the {0} role can create backdated leave applications,Только пользователи с ролью {0} могут создавать оставленные приложения с задним сроком действия,
-Open,Создано,
+Open,Открыт,
 Open Contact,Открытый контакт,
-Open Lead,Открытое руководство,
+Open Lead,Открытое обращение,
 Opening and Closing,Открытие и Закрытие,
 Operating Cost as per Work Order / BOM,Эксплуатационные расходы согласно заказу на работу / спецификации,
 Order Amount,Сумма заказа,
@@ -3891,15 +3893,15 @@
 Passing Score value should be between 0 and 100,Проходной балл должен быть от 0 до 100,
 Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically,Политика паролей не может содержать пробелов или дефисов одновременно. Формат будет реструктурирован автоматически,
 Patient History,История пациента,
-Pause,пауза,
-Pay,Платить,
+Pause,Пауза,
+Pay,Оплатить,
 Payment Document Type,Тип платежного документа,
 Payment Name,Название платежа,
 Penalty Amount,Сумма штрафа,
 Pending,В ожидании,
-Performance,Спектакль,
+Performance,Производительность,
 Period based On,Период на основе,
-Perpetual inventory required for the company {0} to view this report.,"Постоянная инвентаризация требуется для компании {0}, чтобы просмотреть этот отчет.",
+Perpetual inventory required for the company {0} to view this report.,"Чтобы посмотреть этот отчет, требуется постоянная инвентаризация для комнаии {0}",
 Phone,Телефон,
 Pick List,Список выбора,
 Plaid authentication error,Ошибка аутентификации пледа,
@@ -3912,25 +3914,25 @@
 Please enter <b>Difference Account</b> or set default <b>Stock Adjustment Account</b> for company {0},"Пожалуйста, введите <b>разницу счета</b> или установить <b>учетную запись</b> по умолчанию для компании {0}",
 Please enter GSTIN and state for the Company Address {0},"Пожалуйста, введите GSTIN и укажите адрес компании {0}",
 Please enter Item Code to get item taxes,"Пожалуйста, введите код товара, чтобы получить налоги",
-Please enter Warehouse and Date,"Пожалуйста, введите Склад и Дата",
+Please enter Warehouse and Date,"Пожалуйста, укажите склад и дату",
 Please enter the designation,"Пожалуйста, введите обозначение",
 Please login as a Marketplace User to edit this item.,"Пожалуйста, войдите как пользователь Marketplace, чтобы редактировать этот элемент.",
 Please login as a Marketplace User to report this item.,"Пожалуйста, войдите как пользователь Marketplace, чтобы сообщить об этом товаре.",
 Please select <b>Template Type</b> to download template,"Пожалуйста, выберите <b>Тип шаблона,</b> чтобы скачать шаблон",
 Please select Applicant Type first,"Пожалуйста, сначала выберите Тип заявителя",
 Please select Customer first,"Пожалуйста, сначала выберите клиента",
-Please select Item Code first,"Пожалуйста, сначала выберите код товара",
-Please select Loan Type for company {0},"Пожалуйста, выберите Тип кредита для компании {0}",
+Please select Item Code first,"Пожалуйста, сначала выберите код продукта",
+Please select Loan Type for company {0},"Пожалуйста, выберите тип кредита для компании {0}",
 Please select a Delivery Note,"Пожалуйста, выберите накладную",
 Please select a Sales Person for item: {0},"Пожалуйста, выберите продавца для позиции: {0}",
 Please select another payment method. Stripe does not support transactions in currency '{0}',Выберите другой способ оплаты. Полоса не поддерживает транзакции в валюте &#39;{0}&#39;,
 Please select the customer.,"Пожалуйста, выберите клиента.",
 Please set a Supplier against the Items to be considered in the Purchase Order.,"Пожалуйста, установите Поставщика в отношении Товаров, которые должны быть учтены в Заказе на поставку.",
-Please set account heads in GST Settings for Compnay {0},"Пожалуйста, установите заголовки учетных записей в настройках GST для Compnay {0}",
+Please set account heads in GST Settings for Compnay {0},"Пожалуйста, установите заголовки счетов в настройках НДС для комании {0}",
 Please set an email id for the Lead {0},"Пожалуйста, установите идентификатор электронной почты для отведения {0}",
 Please set default UOM in Stock Settings,"Пожалуйста, установите UOM по умолчанию в настройках акций",
 Please set filter based on Item or Warehouse due to a large amount of entries.,"Пожалуйста, установите фильтр на основе товара или склада из-за большого количества записей.",
-Please set up the Campaign Schedule in the Campaign {0},"Пожалуйста, настройте Расписание Кампании в Кампании {0}",
+Please set up the Campaign Schedule in the Campaign {0},"Пожалуйста, настройте расписание кампании в настройках кампании {0}",
 Please set valid GSTIN No. in Company Address for company {0},"Пожалуйста, установите действительный номер GSTIN в адресе компании для компании {0}",
 Please set {0},"Пожалуйста, установите {0}",customer
 Please setup a default bank account for company {0},"Пожалуйста, настройте банковский счет по умолчанию для компании {0}",
@@ -3943,23 +3945,23 @@
 Priority has been changed to {0}.,Приоритет был изменен на {0}.,
 Priority {0} has been repeated.,Приоритет {0} был повторен.,
 Processing XML Files,Обработка файлов XML,
-Profitability,рентабельность,
-Project,проект,
+Profitability,Рентабельность,
+Project,Проект,
 Proposed Pledges are mandatory for secured Loans,Предлагаемые залоги являются обязательными для обеспеченных займов,
 Provide the academic year and set the starting and ending date.,Укажите учебный год и установите дату начала и окончания.,
 Public token is missing for this bank,Публичный токен отсутствует для этого банка,
 Publish,Публиковать,
 Publish 1 Item,Опубликовать 1 пункт,
-Publish Items,Публикация товаров,
-Publish More Items,Опубликовать больше предметов,
-Publish Your First Items,Опубликуйте свои первые предметы,
-Publish {0} Items,Опубликовать {0} товаров,
-Published Items,Опубликованные предметы,
-Purchase Invoice cannot be made against an existing asset {0},Счет-фактура покупки не может быть сделан против существующего актива {0},
+Publish Items,Публикация продуктов,
+Publish More Items,Опубликовать больше продуктов,
+Publish Your First Items,Опубликуйте свои первые продукты,
+Publish {0} Items,Опубликовать {0} продуктов,
+Published Items,Опубликованные продукты,
+Purchase Invoice cannot be made against an existing asset {0},Счет покупки не может быть сделан против существующего актива {0},
 Purchase Invoices,Счета на покупку,
 Purchase Orders,Заказы,
 Purchase Receipt doesn't have any Item for which Retain Sample is enabled.,"В квитанции о покупке нет ни одного предмета, для которого включена функция сохранения образца.",
-Purchase Return,Покупка Вернуться,
+Purchase Return,Возврат покупки,
 Qty of Finished Goods Item,Кол-во готовых товаров,
 Qty or Amount is mandatroy for loan security,Кол-во или сумма является мандатрой для обеспечения кредита,
 Quality Inspection required for Item {0} to submit,Инспекция по качеству требуется для отправки элемента {0},
@@ -3977,11 +3979,11 @@
 Reconciled,Примирение,
 Recruitment,Набор персонала,
 Red,Красный,
-Refreshing,освежение,
+Refreshing,Обновление,
 Release date must be in the future,Дата релиза должна быть в будущем,
 Relieving Date must be greater than or equal to Date of Joining,Дата освобождения должна быть больше или равна дате присоединения,
 Rename,Переименовать,
-Rename Not Allowed,Переименовать не разрешено,
+Rename Not Allowed,Переименовывать запрещено,
 Repayment Method is mandatory for term loans,Метод погашения обязателен для срочных кредитов,
 Repayment Start Date is mandatory for term loans,Дата начала погашения обязательна для срочных кредитов,
 Report Item,Элемент отчета,
@@ -3993,27 +3995,27 @@
 Return amount cannot be greater unclaimed amount,Возврат суммы не может быть больше невостребованной суммы,
 Review,Обзор,
 Room,Комната,
-Room Type,Тип номера,
-Row # ,Ряд #,
-Row #{0}: Accepted Warehouse and Supplier Warehouse cannot be same,Строка # {0}: принятый склад и склад поставщика не могут быть одинаковыми,
-Row #{0}: Cannot delete item {1} which has already been billed.,"Строка # {0}: невозможно удалить элемент {1}, для которого уже выставлен счет.",
-Row #{0}: Cannot delete item {1} which has already been delivered,"Строка # {0}: невозможно удалить элемент {1}, который уже был доставлен",
-Row #{0}: Cannot delete item {1} which has already been received,"Строка # {0}: невозможно удалить элемент {1}, который уже был получен",
-Row #{0}: Cannot delete item {1} which has work order assigned to it.,"Строка # {0}: невозможно удалить элемент {1}, которому назначено рабочее задание.",
-Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.,"Строка # {0}: невозможно удалить элемент {1}, который назначен заказу клиента на покупку.",
-Row #{0}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor,Строка № {0}: невозможно выбрать склад поставщика при подаче сырья субподрядчику,
-Row #{0}: Cost Center {1} does not belong to company {2},Строка # {0}: МВЗ {1} не принадлежит компании {2},
-Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}.,"Строка # {0}: операция {1} не завершена для {2} количества готовой продукции в рабочем задании {3}. Пожалуйста, обновите статус операции с помощью Job Card {4}.",
-Row #{0}: Payment document is required to complete the transaction,Строка # {0}: для завершения транзакции требуется платежный документ,
-Row #{0}: Serial No {1} does not belong to Batch {2},Строка # {0}: серийный номер {1} не принадлежит партии {2},
-Row #{0}: Service End Date cannot be before Invoice Posting Date,Строка # {0}: дата окончания обслуживания не может быть раньше даты проводки счета,
-Row #{0}: Service Start Date cannot be greater than Service End Date,Строка # {0}: дата начала обслуживания не может быть больше даты окончания обслуживания,
-Row #{0}: Service Start and End Date is required for deferred accounting,Строка # {0}: дата начала и окончания обслуживания требуется для отложенного учета,
+Room Type,Тип комнаты,
+Row # ,Строка # ,
+Row #{0}: Accepted Warehouse and Supplier Warehouse cannot be same,Строка #{0}: склад для получения и склад поставщика не могут быть одинаковыми,
+Row #{0}: Cannot delete item {1} which has already been billed.,"Строка #{0}: невозможно удалить продукт {1}, для которого уже выставлен счет.",
+Row #{0}: Cannot delete item {1} which has already been delivered,"Строка #{0}: невозможно удалить продукт {1}, который уже был доставлен",
+Row #{0}: Cannot delete item {1} which has already been received,"Строка #{0}: невозможно удалить продукт {1}, который уже был получен",
+Row #{0}: Cannot delete item {1} which has work order assigned to it.,"Строка #{0}: невозможно удалить продукт {1}, которому назначено рабочее задание.",
+Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.,"Строка #{0}: невозможно удалить продукт {1}, который есть в заказе клиента на покупку.",
+Row #{0}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor,Строка #{0}: невозможно выбрать склад поставщика при подаче сырья субподрядчику,
+Row #{0}: Cost Center {1} does not belong to company {2},Строка #{0}: МВЗ {1} не принадлежит компании {2},
+Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}.,"Строка #{0}: операция {1} не завершена для {2} количества готовой продукции в рабочем задании {3}. Пожалуйста, обновите статус операции с помощью Карточки работ {4}.",
+Row #{0}: Payment document is required to complete the transaction,Строка #{0}: для завершения транзакции требуется платежный документ,
+Row #{0}: Serial No {1} does not belong to Batch {2},Строка #{0}: серийный номер {1} не принадлежит партии {2},
+Row #{0}: Service End Date cannot be before Invoice Posting Date,Строка #{0}: дата окончания обслуживания не может быть раньше даты проводки счета,
+Row #{0}: Service Start Date cannot be greater than Service End Date,Строка #{0}: дата начала обслуживания не может быть больше даты окончания обслуживания,
+Row #{0}: Service Start and End Date is required for deferred accounting,Строка #{0}: дата начала и окончания обслуживания требуется для отложенного учета,
 Row {0}: Invalid Item Tax Template for item {1},Строка {0}: неверный шаблон налога на товар для товара {1},
 Row {0}: Quantity not available for {4} in warehouse {1} at posting time of the entry ({2} {3}),Строка {0}: количество недоступно для {4} на складе {1} во время проводки записи ({2} {3}),
 Row {0}: user has not applied the rule {1} on the item {2},Строка {0}: пользователь не применил правило {1} к элементу {2},
 Row {0}:Sibling Date of Birth cannot be greater than today.,"Строка {0}: дата рождения родного брата не может быть больше, чем сегодня.",
-Row({0}): {1} is already discounted in {2},Строка ({0}): {1} уже дисконтирован в {2},
+Row({0}): {1} is already discounted in {2},Строка({0}): {1} уже дисконтирован в {2},
 Rows Added in {0},Строки добавлены в {0},
 Rows Removed in {0},Строки удалены в {0},
 Sanctioned Amount limit crossed for {0} {1},Предел санкционированной суммы для {0} {1},
@@ -4023,9 +4025,9 @@
 Saved Items,Сохраненные предметы,
 Search Items ...,Поиск предметов ...,
 Search for a payment,Поиск платежа,
-Search for anything ...,Ищите что-нибудь ...,
+Search for anything ...,Искать что угодно ...,
 Search results for,Результаты поиска,
-Select All,Выбрать Все,
+Select All,Выбрать все,
 Select Difference Account,Выберите учетную запись разницы,
 Select a Default Priority.,Выберите приоритет по умолчанию.,
 Select a company,Выберите компанию,
@@ -4060,7 +4062,7 @@
 Sr,Sr,
 Start,Начать,
 Start Date cannot be before the current date,Дата начала не может быть раньше текущей даты,
-Start Time,Время,
+Start Time,Время начала,
 Status,Статус,
 Status must be Cancelled or Completed,Статус должен быть отменен или завершен,
 Stock Balance Report,Отчет об остатках на складе,
@@ -4069,7 +4071,7 @@
 Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses.,Стоимость запаса ({0}) и остаток на счете ({1}) не синхронизированы для счета {2} и связанных хранилищ.,
 Stores - {0},Магазины - {0},
 Student with email {0} does not exist,Студент с электронной почтой {0} не существует,
-Submit Review,добавить отзыв,
+Submit Review,Добавить отзыв,
 Submitted,Проведенный,
 Supplier Addresses And Contacts,Адреса и контакты поставщика,
 Synchronize this account,Синхронизировать этот аккаунт,
@@ -4101,7 +4103,7 @@
 This page keeps track of items you want to buy from sellers.,"На этой странице отслеживаются товары, которые вы хотите купить у продавцов.",
 This page keeps track of your items in which buyers have showed some interest.,"Эта страница отслеживает ваши товары, к которым покупатели проявили определенный интерес.",
 Thursday,Четверг,
-Timing,тайминг,
+Timing,Сроки,
 Title,Заголовок,
 "To allow over billing, update ""Over Billing Allowance"" in Accounts Settings or the Item.","Чтобы разрешить чрезмерную оплату, обновите «Разрешение на чрезмерную оплату» в настройках учетных записей или элемента.",
 "To allow over receipt / delivery, update ""Over Receipt/Delivery Allowance"" in Stock Settings or the Item.","Чтобы разрешить перерасход / доставку, обновите параметр «Сверх квитанция / доставка» в настройках запаса или позиции.",
@@ -4125,14 +4127,14 @@
 Unlink external integrations,Отключить внешние интеграции,
 Unmarked Attendance for days,Посещаемость без опознавательных знаков в течение нескольких дней,
 Unpublish Item,Отменить публикацию,
-Unreconciled,несверенный,
-Unsupported GST Category for E-Way Bill JSON generation,Неподдерживаемая категория GST для генерации E-Way Bill JSON,
+Unreconciled,Несверенный,
+Unsupported GST Category for E-Way Bill JSON generation,Неподдерживаемая категория НДС для генерации E-Way Bill JSON,
 Update,Обновить,
 Update Details,Обновить данные,
 Update Taxes for Items,Обновить налоги на товары,
 "Upload a bank statement, link or reconcile a bank account","Загрузить выписку из банковского счета, связать или сверить банковский счет",
 Upload a statement,Загрузить заявление,
-Use a name that is different from previous project name,"Используйте имя, которое отличается от предыдущего названия проекта",
+Use a name that is different from previous project name,"Используйте название, которое отличается от предыдущего названия проекта",
 User {0} is disabled,Пользователь {0} отключен,
 Users and Permissions,Пользователи и Права,
 Vacancies cannot be lower than the current openings,Вакансии не могут быть ниже текущих вакансий,
@@ -4141,7 +4143,7 @@
 Values Out Of Sync,Значения не синхронизированы,
 Vehicle Type is required if Mode of Transport is Road,"Тип транспортного средства требуется, если вид транспорта - дорога",
 Vendor Name,Имя продавца,
-Verify Email,подтвердить электронную почту,
+Verify Email,Подтвердить Email,
 View,Посмотреть,
 View all issues from {0},Просмотреть все проблемы от {0},
 View call log,Просмотр журнала звонков,
@@ -4157,18 +4159,18 @@
 You,Вы,
 You are not allowed to enroll for this course,Вы не можете записаться на этот курс,
 You are not enrolled in program {0},Вы не зарегистрированы в программе {0},
-You can Feature upto 8 items.,Вы можете добавить до 8 предметов.,
+You can Feature upto 8 items.,Вы можете добавить до 8 элементов.,
 You can also copy-paste this link in your browser,Ещё можно скопировать эту ссылку в браузер,
-You can publish upto 200 items.,Вы можете опубликовать до 200 пунктов.,
+You can publish upto 200 items.,Вы можете опубликовать до 200 элементов.,
 You have to enable auto re-order in Stock Settings to maintain re-order levels.,"Вы должны включить автоматический повторный заказ в настройках запаса, чтобы поддерживать уровни повторного заказа.",
 You must be a registered supplier to generate e-Way Bill,Вы должны быть зарегистрированным поставщиком для создания электронного билля,
 You need to login as a Marketplace User before you can add any reviews.,"Вам необходимо войти в систему как пользователь Marketplace, чтобы добавить какие-либо отзывы.",
 Your Featured Items,Ваши избранные товары,
 Your Items,Ваши товары,
-Your Profile,Твой профиль,
+Your Profile,Ваш профиль,
 Your rating:,Ваш рейтинг:,
 and,и,
-e-Way Bill already exists for this document,e-Way Bill уже существует для этого документа,
+e-Way Bill already exists for this document,e-Way платеж уже существует для этого документа,
 woocommerce - {0},woocommerce - {0},
 {0} Coupon used are {1}. Allowed quantity is exhausted,Использован {0} купон: {1}. Допустимое количество исчерпано,
 {0} Name,{0} Имя,
@@ -4193,22 +4195,22 @@
 Barcode,Штрих-код,
 Bold,Жирный,
 Center,Центр,
-Clear,ясно,
+Clear,Отчистить,
 Comment,Комментарий,
 Comments,Комментарии,
 DocType,DocType,
 Download,Скачать,
 Left,Слева,
-Link,Ссылка на сайт,
-New,новый,
+Link,Ссылка,
+New,Новый,
 Not Found,Не найдено,
 Print,Распечатать,
 Reference Name,Имя ссылки,
 Refresh,Обновить,
-Success,успех,
+Success,Успешно,
 Time,Время,
 Value,Значение,
-Actual,фактический,
+Actual,Актуальность,
 Add to Cart,добавить в корзину,
 Days Since Last Order,Дней с последнего заказа,
 In Stock,В наличии,
@@ -4217,20 +4219,20 @@
 No students Found,Студенты не найдены,
 Not in Stock,Нет в наличии,
 Please select a Customer,Выберите клиента,
-Printed On,Отпечатано на,
+Printed On,Напечатано на,
 Received From,Получено от,
 Sales Person,Продавец,
 To date cannot be before From date,На сегодняшний день не может быть раньше От даты,
 Write Off,Списать,
-{0} Created,Создано {0},
+{0} Created,{0} Создано,
 Email Id,Email ID,
-No,нет,
+No,Нет,
 Reference Doctype,Ссылка DocType,
-User Id,Идентификатор пользователя,
-Yes,да,
-Actual ,Фактически,
+User Id,ID пользователя,
+Yes,Да,
+Actual ,Актуальный ,
 Add to cart,Добавить в корзину,
-Budget,бюджет,
+Budget,Бюджет,
 Chart of Accounts,План счетов,
 Customer database.,База данных клиентов.,
 Days Since Last order,Дни с последнего Заказать,
@@ -4238,26 +4240,26 @@
 End date can not be less than start date,"Дата окончания не может быть меньше, чем Дата начала",
 For Default Supplier (Optional),Поставщик по умолчанию (необязательно),
 From date cannot be greater than To date,"С даты не может быть больше, чем к дате",
-Group by,Group By,
+Group by,Группировать по,
 In stock,В наличии,
 Item name,Название продукта,
 Loan amount is mandatory,Сумма кредита обязательна,
 Minimum Qty,Минимальное количество,
 More details,Больше параметров,
-Nature of Supplies,Природа поставок,
+Nature of Supplies,Характер поставок,
 No Items found.,Ничего не найдено.,
 No employee found,Сотрудник не найден,
 No students found,Нет студентов не найдено,
 Not in stock,Нет в наличии,
 Not permitted,Не разрешено,
-Open Issues ,Открыть вопросы,
-Open Projects ,Открыть проекты,
-Open To Do ,Открыть список дел,
+Open Issues ,Открытые вопросы ,
+Open Projects ,Открытые проекты,
+Open To Do ,Открыть список задач ,
 Operation Id,Код операции,
 Partially ordered,Частично заказанно,
-Please select company first,"Пожалуйста, выберите КОМПАНИЯ Первый",
+Please select company first,Сначала выберите компанию,
 Please select patient,Выберите пациента,
-Printed On ,Напечатано на,
+Printed On ,Напечатано на ,
 Projected qty,Прогнозируемое кол-во,
 Sales person,Продавец,
 Serial No {0} Created,Серийный номер {0} создан,
@@ -4268,28 +4270,28 @@
 Total Taxable value,Общая налогооблагаемая стоимость,
 Upcoming Calendar Events ,Предстоящие события календаря,
 Value or Qty,Значение или кол-во,
-Variance ,отклонение,
+Variance ,Расхождение ,
 Variant of,Вариант,
 Write off,Списать,
 hours,часов,
 received from,получено от,
 to,для,
 Cards,Карты,
-Percentage,процент,
+Percentage,Процент,
 Failed to setup defaults for country {0}. Please contact support@erpnext.com,"Не удалось установить значения по умолчанию для страны {0}. Пожалуйста, свяжитесь с support@erpnext.com",
 Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it.,Строка # {0}: элемент {1} не является сериализованным / пакетным элементом. Он не может иметь серийный номер / пакетный номер против него.,
 Please set {0},"Пожалуйста, установите {0}",
 Please set {0},"Пожалуйста, установите {0}",supplier
 Draft,Черновой вариант,"docstatus,=,0"
-Cancelled,отменен,"docstatus,=,2"
+Cancelled,Отменен,"docstatus,=,2"
 Please setup Instructor Naming System in Education > Education Settings,"Пожалуйста, настройте систему именования инструкторов в «Образование»",
-Please set Naming Series for {0} via Setup > Settings > Naming Series,"Пожалуйста, установите серию имен для {0} через Настройка&gt; Настройки&gt; Серия имен",
+Please set Naming Series for {0} via Setup > Settings > Naming Series,"Пожалуйста, установите серию имен для {0} через Настройка > Настройки > Серия имен",
 UOM Conversion factor ({0} -> {1}) not found for item: {2},Коэффициент преобразования UOM ({0} -&gt; {1}) не найден для элемента: {2},
-Item Code > Item Group > Brand,Код товара&gt; Группа товаров&gt; Марка,
-Customer > Customer Group > Territory,Клиент&gt; Группа клиентов&gt; Территория,
-Supplier > Supplier Type,Поставщик&gt; Тип поставщика,
-Please setup Employee Naming System in Human Resource > HR Settings,"Пожалуйста, настройте систему имен сотрудников в разделе «Управление персоналом»&gt; «Настройки HR»",
-Please setup numbering series for Attendance via Setup > Numbering Series,Настройте серию нумерации для Посещаемости через Настройка&gt; Серия нумерации,
+Item Code > Item Group > Brand,Код товара > Группа товаров > Бренд,
+Customer > Customer Group > Territory,Клиент > Группа клиентов > Территория,
+Supplier > Supplier Type,Поставщик > Тип поставщика,
+Please setup Employee Naming System in Human Resource > HR Settings,"Пожалуйста, настройте систему имен сотрудников в разделе Управление персоналом > Настройки HR",
+Please setup numbering series for Attendance via Setup > Numbering Series,Настройте серию нумерации для Посещаемости через Настройка > Серия нумерации,
 The value of {0} differs between Items {1} and {2},Значение {0} различается между элементами {1} и {2}.,
 Auto Fetch,Автозагрузка,
 Fetch Serial Numbers based on FIFO,Получение серийных номеров на основе FIFO,
@@ -4319,7 +4321,7 @@
 Publish Date,Дата публикации,
 Duration,Продолжительность,
 Advanced Settings,Расширенные настройки,
-Path,Дорожка,
+Path,Путь,
 Components,Компоненты,
 Verified By,Утверждено,
 Invalid naming series (. missing) for {0},Недопустимая серия имен (. Отсутствует) для {0},
@@ -4338,7 +4340,7 @@
 Invalid {0} or {1},Недействительный {0} или {1},
 Error! Failed to get access token.,Ошибка! Не удалось получить токен доступа.,
 Invalid Consumer Key or Consumer Secret Key,Недействительный ключ потребителя или секретный ключ потребителя,
-Your Session will be expire in ,Ваша сессия истечет через,
+Your Session will be expire in ,Ваша сессия истечет через ,
  days.,дней.,
 Session is expired. Save doc to login.,Сессия истекла. Сохраните документ для входа в систему.,
 Error While Uploading Image,Ошибка при загрузке изображения,
@@ -4363,10 +4365,10 @@
 Row {0}: {1} is required in the expenses table to book an expense claim.,Строка {0}: {1} требуется в таблице расходов для регистрации претензии по расходам.,
 Set the default account for the {0} {1},Установите учетную запись по умолчанию для {0} {1},
 (Half Day),(Полдня),
-Income Tax Slab,Плита подоходного налога,
-Row #{0}: Cannot set amount or formula for Salary Component {1} with Variable Based On Taxable Salary,Строка № {0}: невозможно установить сумму или формулу для компонента заработной платы {1} с переменной на основе налогооблагаемой заработной платы.,
-Row #{}: {} of {} should be {}. Please modify the account or select a different account.,Строка № {}: {} из {} должно быть {}. Измените учетную запись или выберите другую учетную запись.,
-Row #{}: Please asign task to a member.,Строка № {}: назначьте задачу участнику.,
+Income Tax Slab,Подоходный налог,
+Row #{0}: Cannot set amount or formula for Salary Component {1} with Variable Based On Taxable Salary,Строка #{0}: невозможно установить сумму или формулу для компонента заработной платы {1} с переменной на основе налогооблагаемой заработной платы.,
+Row #{}: {} of {} should be {}. Please modify the account or select a different account.,Строка #{}: {} из {} должно быть {}. Измените учетную запись или выберите другую учетную запись.,
+Row #{}: Please asign task to a member.,Строка #{}: назначьте задачу участнику.,
 Process Failed,Ошибка процесса,
 Tally Migration Error,Ошибка миграции Tally,
 Please set Warehouse in Woocommerce Settings,"Пожалуйста, установите склад в настройках Woocommerce",
@@ -4375,7 +4377,7 @@
 Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.,Не удается найти {} для элемента {}. Установите то же самое в Мастер предметов или Настройки запасов.,
 Row #{0}: The batch {1} has already expired.,Строка № {0}: срок действия пакета {1} уже истек.,
 Start Year and End Year are mandatory,Год начала и год окончания являются обязательными,
-GL Entry,GL Вступление,
+GL Entry,БК запись,
 Cannot allocate more than {0} against payment term {1},Невозможно выделить более {0} на срок платежа {1},
 The root account {0} must be a group,Корневая учетная запись {0} должна быть группой,
 Shipping rule not applicable for country {0} in Shipping Address,Правило доставки неприменимо для страны {0} в адресе доставки,
@@ -4405,7 +4407,7 @@
 "{0} Retain Sample is based on batch, please check Has Batch No to retain sample of item","{0} Сохранить образец основан на партии. Установите флажок &quot;Имеет номер партии&quot;, чтобы сохранить образец товара.",
 Empty,Пустой,
 Currently no stock available in any warehouse,В настоящее время нет в наличии ни на одном складе,
-BOM Qty,BOM Qty,
+BOM Qty,Кол-во спецификаций,
 Time logs are required for {0} {1},Журналы времени необходимы для {0} {1},
 Total Completed Qty,Всего завершено кол-во,
 Qty to Manufacture,Кол-во для производства,
@@ -4424,8 +4426,8 @@
 OP Consulting Charge,OP Consulting Charge,
 Inpatient Visit Charge,Стационарное посещение,
 Appointment Status,Статус встречи,
-Test: ,Контрольная работа:,
-Collection Details: ,Детали коллекции:,
+Test: ,Тест: ,
+Collection Details: ,Детали коллекции: ,
 {0} out of {1},{0} из {1},
 Select Therapy Type,Выберите тип терапии,
 {0} sessions completed,{0} сеансов завершено,
@@ -4438,12 +4440,12 @@
 Item with Item Code {0} already exists,Товар с кодом товара {0} уже существует,
 Registration Fee cannot be negative or zero,Регистрационный взнос не может быть отрицательным или нулевым.,
 Configure a service Item for {0},Настроить сервисный элемент для {0},
-Temperature: ,Температура:,
-Pulse: ,Пульс:,
-Respiratory Rate: ,Частота дыхания:,
-BP: ,АД:,
-BMI: ,ИМТ:,
-Note: ,Примечание:,
+Temperature: ,Температура: ,
+Pulse: ,Пульс: ,
+Respiratory Rate: ,Частота дыхания: ,
+BP: ,АД :,
+BMI: ,ИМТ: ,
+Note: ,Примечание: ,
 Check Availability,Проверить наличие свободных мест,
 Please select Patient first,"Пожалуйста, сначала выберите пациента",
 Please select a Mode of Payment first,"Пожалуйста, сначала выберите способ оплаты",
@@ -4476,9 +4478,9 @@
 Please set Customer in Patient {0},Установите клиента в пациенте {0},
 Item {0} is not active,Пункт {0} не активен,
 Therapy Plan {0} created successfully.,План терапии {0} успешно создан.,
-Symptoms: ,Симптомы:,
+Symptoms: ,Симптомы: ,
 No Symptoms,Нет симптомов,
-Diagnosis: ,Диагноз:,
+Diagnosis: ,Диагноз: ,
 No Diagnosis,Нет диагноза,
 Drug(s) Prescribed.,Выписанные лекарства.,
 Test(s) Prescribed.,Предписанные испытания.,
@@ -4493,7 +4495,7 @@
 Setting Account Type helps in selecting this Account in transactions.,Установка Тип аккаунта помогает в выборе этого счет в сделках.,
 Chargeable,Ответственный,
 Rate at which this tax is applied,Курс по которому этот налог применяется,
-Frozen,замороженные,
+Frozen,Замороженные,
 "If the account is frozen, entries are allowed to restricted users.","Если счет замораживается, записи разрешается ограниченных пользователей.",
 Balance must be,Баланс должен быть,
 Lft,Lft,
@@ -4518,9 +4520,9 @@
 Determine Address Tax Category From,Определить адрес налоговой категории от,
 Over Billing Allowance (%),Превышение надбавки (%),
 Credit Controller,Кредитная контроллер,
-Check Supplier Invoice Number Uniqueness,Проверять Уникальность Номера Счетов получаемых от Поставщика,
-Make Payment via Journal Entry,Платежи через журнал Вход,
-Unlink Payment on Cancellation of Invoice,Unlink Оплата об аннулировании счета-фактуры,
+Check Supplier Invoice Number Uniqueness,Проверять уникальность номеров счетов получаемых от поставщика,
+Make Payment via Journal Entry,Произвести оплату через журнальную запись,
+Unlink Payment on Cancellation of Invoice,Отменить привязку платежа при аннулировании счета,
 Book Asset Depreciation Entry Automatically,Автоматическое внесение амортизации в книгу,
 Automatically Add Taxes and Charges from Item Tax Template,Автоматически добавлять налоги и сборы из шаблона налога на товар,
 Automatically Fetch Payment Terms,Автоматически получать условия оплаты,
@@ -4535,7 +4537,7 @@
 Branch Code,Код филиала,
 Address and Contact,Адрес и контакт,
 Address HTML,Адрес HTML,
-Contact HTML,Связаться с HTML,
+Contact HTML,Контакт HTML,
 Data Import Configuration,Конфигурация импорта данных,
 Bank Transaction Mapping,Отображение банковских транзакций,
 Plaid Access Token,Жетон доступа к пледу,
@@ -4556,8 +4558,8 @@
 Bank Account Type,Тип банковского счета,
 Bank Guarantee,Банковская гарантия,
 Bank Guarantee Type,Тип банковской гарантии,
-Receiving,получающий,
-Providing,обеспечение,
+Receiving,Получение,
+Providing,Обеспечение,
 Reference Document Name,Название ссылочного документа,
 Validity in Days,Срок действия в днях,
 Bank Account Info,Информация о банковском счете,
@@ -4575,28 +4577,28 @@
 Payment Entries,Записи оплаты,
 Update Clearance Date,Обновление просвет Дата,
 Bank Reconciliation Detail,Подробности банковской сверки,
-Cheque Number,Чек Количество,
-Cheque Date,Чек Дата,
+Cheque Number,Номер чека,
+Cheque Date,Дата чека,
 Statement Header Mapping,Сопоставление заголовков операторов,
 Statement Headers,Заголовки операторов,
 Transaction Data Mapping,Сопоставление данных транзакций,
 Mapped Items,Отображаемые объекты,
 Bank Statement Settings Item,Элемент настройки выписки по банку,
-Mapped Header,Mapped Header,
+Mapped Header,Сопоставленный заголовок,
 Bank Header,Заголовок банка,
 Bank Statement Transaction Entry,Ввод транзакции с банковским выпиской,
 Bank Transaction Entries,Записи банковских транзакций,
 New Transactions,Новые транзакции,
-Match Transaction to Invoices,Сопоставление транзакций с счетами-фактурами,
+Match Transaction to Invoices,Сопоставление транзакций со счетами,
 Create New Payment/Journal Entry,Создать новую запись о платеже / журнале,
-Submit/Reconcile Payments,Отправить / Согласовать платежи,
-Matching Invoices,Сопоставление счетов-фактур,
+Submit/Reconcile Payments,Утвердить/Согласовать платежи,
+Matching Invoices,Сопоставление счетов,
 Payment Invoice Items,Платежные счета,
 Reconciled Transactions,Согласованные транзакции,
 Bank Statement Transaction Invoice Item,Элемент счета транзакции банковского выписки,
 Payment Description,Описание платежа,
-Invoice Date,Дата Счета,
-invoice,счет-фактура,
+Invoice Date,Дата счета,
+invoice,счет,
 Bank Statement Transaction Payment Item,Элемент оплаты транзакции по банковскому счету,
 outstanding_amount,outstanding_amount,
 Payment Reference,Ссылка на платеж,
@@ -4605,9 +4607,9 @@
 Mapped Data Type,Тип данных сопоставления,
 Mapped Data,Отображаемые данные,
 Bank Transaction,Банковская операция,
-ACC-BTN-.YYYY.-,ACC-БТН-.YYYY.-,
+ACC-BTN-.YYYY.-,ACC-BTN-.YYYY.-,
 Transaction ID,ID транзакции,
-Unallocated Amount,Нераспределенные Сумма,
+Unallocated Amount,Нераспределенная сумма,
 Field in Bank Transaction,Поле в банковской операции,
 Column in Bank File,Столбец в банковском файле,
 Bank Transaction Payments,Платежи по банковским операциям,
@@ -4631,12 +4633,12 @@
 C-Form No,C-образный Нет,
 Received Date,Дата получения,
 Quarter,Квартал,
-I,Я,
+I,I,
 II,II,
 III,III,
 IV,IV,
 C-Form Invoice Detail,C-образный Счет Подробно,
-Invoice No,Номер Счета,
+Invoice No,Номер cчета,
 Cash Flow Mapper,Диспетчер денежных потоков,
 Section Name,Название раздела,
 Section Header,Заголовок раздела,
@@ -4644,7 +4646,7 @@
 e.g Adjustments for:,"например, корректировки для:",
 Section Subtotal,Раздел Итого,
 Section Footer,Нижний колонтитул,
-Position,Должность,
+Position,Позиция,
 Cash Flow Mapping,Отображение денежных потоков,
 Select Maximum Of 1,Выберите Максимум 1,
 Is Finance Cost,Стоимость финансирования,
@@ -4652,15 +4654,15 @@
 Is Finance Cost Adjustment,Корректировка финансовых расходов,
 Is Income Tax Liability,Ответственность подоходного налога,
 Is Income Tax Expense,Расходы на подоходный налог,
-Cash Flow Mapping Accounts,Учетные записи денежных потоков,
-account,Аккаунт,
+Cash Flow Mapping Accounts,Счета денежных потоков,
+account,счет,
 Cash Flow Mapping Template,Шаблон сопоставления денежных потоков,
 Cash Flow Mapping Template Details,Подробное описание шаблонов движения денежных средств,
-POS-CLO-,POS-ClO-,
+POS-CLO-,POS-CLO-,
 Custody,Опека,
 Net Amount,Чистая сумма,
 Cashier Closing Payments,Кассовые платежи,
-Chart of Accounts Importer,План счетов импортера,
+Chart of Accounts Importer,Импорт плана счетов,
 Import Chart of Accounts from a csv file,Импортировать план счетов из CSV-файла,
 Attach custom Chart of Accounts file,Прикрепить пользовательский файл плана счетов,
 Chart Preview,Предварительный просмотр диаграммы,
@@ -4668,12 +4670,12 @@
 Cheque Print Template,Чеками печати шаблона,
 Has Print Format,Имеет формат печати,
 Primary Settings,Основные настройки,
-Cheque Size,Cheque Размер,
-Regular,регулярное,
+Cheque Size,Размер чека,
+Regular,Обычный,
 Starting position from top edge,Исходное положение от верхнего края,
-Cheque Width,Cheque Ширина,
-Cheque Height,Cheque Высота,
-Scanned Cheque,Сканированные чеками,
+Cheque Width,Ширина чека,
+Cheque Height,Высота чека,
+Scanned Cheque,Отсканированный чек,
 Is Account Payable,Является ли кредиторская задолженность,
 Distance from top edge,Расстояние от верхнего края,
 Distance from left edge,Расстояние от левого края,
@@ -4689,13 +4691,13 @@
 Track separate Income and Expense for product verticals or divisions.,Подписка отдельный доходы и расходы за вертикалей продукции или подразделений.,
 Cost Center Name,Название учетного отдела,
 Parent Cost Center,Родитель МВЗ,
-lft,LFT,
-rgt,РТГ,
-Coupon Code,код купона,
+lft,лев,
+rgt,прав,
+Coupon Code,Код купона,
 Coupon Name,Название купона,
-"e.g. ""Summer Holiday 2019 Offer 20""","например, &quot;Летние каникулы 2019 Предложение 20&quot;",
+"e.g. ""Summer Holiday 2019 Offer 20""","например, ""Летние каникулы 2019 Предложение 20""",
 Coupon Type,Тип купона,
-Promotional,рекламный,
+Promotional,Рекламный,
 Gift Card,Подарочная карта,
 unique e.g. SAVE20  To be used to get discount,"уникальный, например, SAVE20 для получения скидки",
 Validity and Usage,Срок действия и использование,
@@ -4715,17 +4717,17 @@
 Balance In Base Currency,Баланс в базовой валюте,
 New Exchange Rate,Новый обменный курс,
 New Balance In Base Currency,Новый баланс в базовой валюте,
-Gain/Loss,Прибыль / убыток,
+Gain/Loss,Прибыль/убыток,
 **Fiscal Year** represents a Financial Year. All accounting entries and other major transactions are tracked against **Fiscal Year**.,**Фискальный год** представляет собой финансовый год. Все бухгалтерские записи и другие крупные сделки отслеживаются по **Фискальному году**.,
 Year Name,Год,
 "For e.g. 2012, 2012-13","Например, 2012, 2012-13",
 Year Start Date,Дата начала года,
 Year End Date,Дата окончания года,
 Companies,Компании,
-Auto Created,Автосоздан,
+Auto Created,Создан автоматически,
 Stock User,Пользователь склада,
 Fiscal Year Company,Финансовый год компании,
-Debit Amount,Дебет Сумма,
+Debit Amount,Сумма дебета,
 Credit Amount,Сумма кредита,
 Debit Amount in Account Currency,Дебет Сумма в валюте счета,
 Credit Amount in Account Currency,Сумма кредита в валюте счета,
@@ -4733,10 +4735,10 @@
 Is Opening,Открывает,
 Is Advance,Является Advance,
 To Rename,Переименовать,
-GST Account,Учетная запись GST,
-CGST Account,Учетная запись CGST,
-SGST Account,Учетная запись SGST,
-IGST Account,Учет IGST,
+GST Account,НДС счет,
+CGST Account,CGST счет,
+SGST Account,SGST счет,
+IGST Account,IGST счет,
 CESS Account,CESS-аккаунт,
 Loan Start Date,Дата начала займа,
 Loan Period (Days),Срок кредитования (дни),
@@ -4759,11 +4761,11 @@
 Excise Entry,Акцизный запись,
 Write Off Entry,Списание запись,
 Opening Entry,Начальная запись,
-ACC-JV-.YYYY.-,ACC-СП-.YYYY.-,
-Accounting Entries,Бухгалтерские Проводки,
-Total Debit,Всего Дебет,
-Total Credit,Всего очков,
-Difference (Dr - Cr),Отличия (д-р - Cr),
+ACC-JV-.YYYY.-,ACC-JV-.YYYY.-,
+Accounting Entries,Бухгалтерские проводки,
+Total Debit,Общий дебет,
+Total Credit,Общий кредит,
+Difference (Dr - Cr),Разница (Деб - Кред),
 Make Difference Entry,Сделать Разница запись,
 Total Amount Currency,Общая сумма валюты,
 Total Amount in Words,Общая сумма в словах,
@@ -4771,7 +4773,7 @@
 Paid Loan,Платный кредит,
 Inter Company Journal Entry Reference,Ссылка на журнал Inter Company Journal,
 Write Off Based On,Списание на основе,
-Get Outstanding Invoices,Получить неоплаченных счетов-фактур,
+Get Outstanding Invoices,Получить неоплаченные счетов,
 Write Off Amount,Сумма списания,
 Printing Settings,Настройки печати,
 Pay To / Recd From,Оплачено кем/получено от,
@@ -4790,8 +4792,8 @@
 Reference Due Date,Справочная дата,
 Loyalty Program Tier,Уровень программы лояльности,
 Redeem Against,Погасить Против,
-Expiry Date,Срок годности:,
-Loyalty Point Entry Redemption,Возврат к лояльности,
+Expiry Date,Срок действия,
+Loyalty Point Entry Redemption,Активация баллов лояльности,
 Redemption Date,Дата погашения,
 Redeemed Points,Погашенные очки,
 Loyalty Program Name,Название программы лояльности,
@@ -4802,7 +4804,7 @@
 Auto Opt In (For all customers),Auto Opt In (для всех клиентов),
 Collection Tier,Уровень сбора,
 Collection Rules,Правила сбора,
-Redemption,Выкуп,
+Redemption,Выплата,
 Conversion Factor,Коэффициент конверсии,
 1 Loyalty Points = How much base currency?,1 Бонусные баллы = Сколько базовой валюты?,
 Expiry Duration (in days),Продолжительность действия (в днях),
@@ -4811,8 +4813,8 @@
 Loyalty Program Collection,Коллекция программы лояльности,
 Tier Name,Название уровня,
 Minimum Total Spent,Минимальные общие затраты,
-Collection Factor (=1 LP),Коэффициент сбора (= 1 LP),
-For how much spent = 1 Loyalty Point,За сколько потраченных = 1 Точка лояльности,
+Collection Factor (=1 LP),Коэффициент сбора (=1 Балл),
+For how much spent = 1 Loyalty Point,За сколько потраченных = 1 Балл лояльности,
 Mode of Payment Account,Форма оплаты счета,
 Default Account,По умолчанию учетная запись,
 Default account will be automatically updated in POS Invoice when this mode is selected.,"Учетная запись по умолчанию будет автоматически обновляться в POS-счете, если выбран этот режим.",
@@ -4824,7 +4826,7 @@
 Percentage Allocation,Процент Распределение,
 Create Missing Party,Создать отсутствующую партию,
 Create missing customer or supplier.,Создайте отсутствующего клиента или поставщика.,
-Opening Invoice Creation Tool Item,Открытие инструмента для создания счета-фактуры,
+Opening Invoice Creation Tool Item,Открытие инструмента для создания счета,
 Temporary Opening Account,Временный вступительный счет,
 Party Account,Партия аккаунт,
 Type of Payment,Тип платежа,
@@ -4840,14 +4842,14 @@
 Account Paid To,Счет оплачены до,
 Paid Amount (Company Currency),Оплаченная сумма (в валюте компании),
 Received Amount,Полученная сумма,
-Received Amount (Company Currency),Полученная сумма (валюта компании),
+Received Amount (Company Currency),Полученная сумма (в валюте компании),
 Get Outstanding Invoice,Получить выдающийся счет,
 Payment References,Ссылки оплаты,
 Writeoff,Списать,
-Total Allocated Amount,Общая сумма Обозначенная,
-Total Allocated Amount (Company Currency),Общая Выделенная сумма (Компания Валюта),
+Total Allocated Amount,Общая выделенная сумма,
+Total Allocated Amount (Company Currency),Общая выделенная сумма (в валюте компании),
 Set Exchange Gain / Loss,Установить Курсовая прибыль / убыток,
-Difference Amount (Company Currency),Разница Сумма (Компания Валюта),
+Difference Amount (Company Currency),Разница (в валюте компании),
 Write Off Difference Amount,Списание разница в,
 Deductions or Loss,Отчисления или убыток,
 Payment Deductions or Loss,Отчисления оплаты или убыток,
@@ -4857,7 +4859,7 @@
 Allocated,Выделенные,
 Payment Gateway Account,Аккаунт платежного шлюза,
 Payment Account,Счёт оплаты,
-Default Payment Request Message,По умолчанию Оплата Сообщение запроса,
+Default Payment Request Message,Сообщение запроса платежа по умолчанию,
 PMO-,PMO-,
 Payment Order Type,Тип платежного поручения,
 Payment Order Reference,Ссылка на платежное поручение,
@@ -4866,21 +4868,21 @@
 Receivable / Payable Account,Счет Дебиторской / Кредиторской задолженности,
 Bank / Cash Account,Банк / Расчетный счет,
 From Invoice Date,От Дата Счета,
-To Invoice Date,Счета-фактуры Дата,
-Minimum Invoice Amount,Минимальная Сумма счета,
-Maximum Invoice Amount,Максимальная Сумма счета,
+To Invoice Date,К дате выставления счета,
+Minimum Invoice Amount,Минимальная сумма счета,
+Maximum Invoice Amount,Максимальная сумма счета,
 System will fetch all the entries if limit value is zero.,"Система извлечет все записи, если предельное значение равно нулю.",
 Get Unreconciled Entries,Получить несверенные записи,
 Unreconciled Payment Details,Несогласованные Детали компенсации,
 Invoice/Journal Entry Details,Счет / Журнал вступления подробнее,
 Payment Reconciliation Invoice,Оплата Примирение Счет,
-Invoice Number,Номер Счета,
-Payment Reconciliation Payment,Оплата Примирение Оплата,
-Reference Row,Ссылка Row,
+Invoice Number,Номер cчета,
+Payment Reconciliation Payment,Сверка платежей об оплате,
+Reference Row,Справочная строка,
 Allocated amount,Выделенная сумма,
 Payment Request Type,Тип платежного запроса,
-Outward,внешний,
-Inward,внутрь,
+Outward,Внешний,
+Inward,Внутренний,
 ACC-PRQ-.YYYY.-,ACC-PRQ-.YYYY.-,
 Transaction Details,Детали транзакции,
 Amount in customer's currency,Сумма в валюте клиента,
@@ -4892,15 +4894,15 @@
 Make Sales Invoice,Создать счёт,
 Mute Email,Отключить электронную почту,
 payment_url,payment_url,
-Payment Gateway Details,Компенсация Детали шлюза,
+Payment Gateway Details,Детали платежного шлюза,
 Payment Schedule,График оплаты,
 Invoice Portion,Часть счета,
 Payment Amount,Сумма платежа,
 Payment Term Name,Название условия платежа,
 Due Date Based On,Дата составления финансовой отчетности,
 Day(s) after invoice date,День (ы) после даты выставления счета,
-Day(s) after the end of the invoice month,День (ы) после окончания месяца счета-фактуры,
-Month(s) after the end of the invoice month,Месяц (ы) после окончания месяца счета-фактуры,
+Day(s) after the end of the invoice month,День (дни) после окончания месяца выставления счета,
+Month(s) after the end of the invoice month,Месяц(а) после окончания месяца выставления счета,
 Credit Days,Кредитных дней,
 Credit Months,Кредитные месяцы,
 Allocate Payment Based On Payment Terms,Распределить платеж на основе условий платежа,
@@ -4910,13 +4912,13 @@
 Closing Account Head,Закрытие счета руководитель,
 "The account head under Liability or Equity, in which Profit/Loss will be booked","Глава счета под обязательство или долевой, в котором прибыль / убыток будет забронирован",
 POS Customer Group,POS Группа клиентов,
-POS Field,POS Field,
-POS Item Group,POS Item Group,
+POS Field,POS поле,
+POS Item Group,POS продуктовая группа,
 Company Address,Адрес компании,
-Update Stock,Обновить склад,
+Update Stock,Обновить остатки,
 Ignore Pricing Rule,Игнорировать правило ценообразования,
 Applicable for Users,Применимо для пользователей,
-Sales Invoice Payment,Накладная Оплата,
+Sales Invoice Payment,Оплата счета продажи,
 Item Groups,Продуктовые группы,
 Only show Items from these Item Groups,Показывать только предметы из этих групп товаров,
 Customer Groups,Группы клиентов,
@@ -4940,44 +4942,44 @@
 Apply Rule On Other,Применить правило на других,
 Party Information,Информация о вечеринке,
 Quantity and Amount,Количество и сумма,
-Min Qty,Мин Кол-во,
-Max Qty,Макс Кол-во,
+Min Qty,Мин. кол-во,
+Max Qty,Макс. кол-во,
 Min Amt,Мин Amt,
 Max Amt,Макс Амт,
 Period Settings,Настройки периода,
-Margin,Разница,
+Margin,Маржа,
 Margin Type,Тип маржа,
 Margin Rate or Amount,Маржинальная ставка или сумма,
 Price Discount Scheme,Схема скидок,
 Rate or Discount,Стоимость или скидка,
 Discount Percentage,Скидка в процентах,
 Discount Amount,Сумма скидки,
-For Price List,Для Прейскурантом,
+For Price List,Для прайс-листа,
 Product Discount Scheme,Схема скидок на товары,
 Same Item,Тот же пункт,
 Free Item,Бесплатный товар,
 Threshold for Suggestion,Порог для предложения,
-System will notify to increase or decrease quantity or amount ,Система сообщит об увеличении или уменьшении количества или суммы,
+System will notify to increase or decrease quantity or amount ,Система сообщит об увеличении или уменьшении количества или суммы ,
 "Higher the number, higher the priority","Чем выше число, тем выше приоритет",
 Apply Multiple Pricing Rules,Применить несколько правил ценообразования,
 Apply Discount on Rate,Применить скидку на ставку,
 Validate Applied Rule,Утвердить примененное правило,
 Rule Description,Описание правила,
-Pricing Rule Help,Цены Правило Помощь,
+Pricing Rule Help,Справка по правилу ценообразования,
 Promotional Scheme Id,Идентификатор рекламной схемы,
 Promotional Scheme,Схема продвижения,
-Pricing Rule Brand,Ценовое правило Бренд,
+Pricing Rule Brand,Правило ценообразования для бренда,
 Pricing Rule Detail,Детализация правила ценообразования,
 Child Docname,Детское имя,
 Rule Applied,Правило применяется,
-Pricing Rule Item Code,Правило ценообразования Код товара,
-Pricing Rule Item Group,Группа правил правила ценообразования,
+Pricing Rule Item Code,Правило ценообразования по коду продукта,
+Pricing Rule Item Group,Правило ценообразования для группы,
 Price Discount Slabs,Цена Скидка Плиты,
 Promotional Scheme Price Discount,Рекламная схема Цена со скидкой,
 Product Discount Slabs,Дисконтные плиты продукта,
 Promotional Scheme Product Discount,Рекламная схема товара со скидкой,
-Min Amount,Минимальная сумма,
-Max Amount,Макс. Сумма,
+Min Amount,Мин. сумма,
+Max Amount,Макс. сумма,
 Discount Type,Тип скидки,
 ACC-PINV-.YYYY.-,ACC-PINV-.YYYY.-,
 Tax Withholding Category,Категория удержания налогов,
@@ -4985,12 +4987,12 @@
 Is Paid,Оплачено,
 Is Return (Debit Note),Возврат (дебетовая заметка),
 Apply Tax Withholding Amount,Применять сумму удержания налога,
-Accounting Dimensions ,Бухгалтерские размеры,
+Accounting Dimensions ,Бухгалтерские измерения ,
 Supplier Invoice Details,Подробная информация о поставщике счета,
 Supplier Invoice Date,Дата выставления счета поставщиком,
 Return Against Purchase Invoice,Вернуться против счет покупки,
 Select Supplier Address,Выбрать адрес поставщика,
-Contact Person,Контактное Лицо,
+Contact Person,Контактное лицо,
 Select Shipping Address,Выберите адрес доставки,
 Currency and Price List,Валюта и прайс-лист,
 Price List Currency,Прайс-лист валют,
@@ -5002,75 +5004,75 @@
 Supplier Warehouse,Склад поставщика,
 Pricing Rules,Правила ценообразования,
 Supplied Items,Поставляемые продукты,
-Total (Company Currency),Всего (Компания валют),
-Net Total (Company Currency),Чистая Всего (Компания Валюта),
+Total (Company Currency),Всего (в валюте компании),
+Net Total (Company Currency),Чистая Всего (в валюте компании),
 Total Net Weight,Общий вес нетто,
 Shipping Rule,Правило доставки,
 Purchase Taxes and Charges Template,Купить налоги и сборы шаблон,
 Purchase Taxes and Charges,Покупка Налоги и сборы,
 Tax Breakup,Распределение налогов,
 Taxes and Charges Calculation,Налоги и сборы Расчет,
-Taxes and Charges Added (Company Currency),Налоги и сборы Добавил (Компания Валюта),
-Taxes and Charges Deducted (Company Currency),"Налоги, которые вычитаются (Компания Валюта)",
-Total Taxes and Charges (Company Currency),Всего Налоги и сборы (Компания Валюты),
+Taxes and Charges Added (Company Currency),Добавленные налоги и сборы (в валюте компании),
+Taxes and Charges Deducted (Company Currency),"Налоги, которые вычитаются (в валюте компании)",
+Total Taxes and Charges (Company Currency),Всего налогов и сборов (в валюте компании),
 Taxes and Charges Added,Налоги и сборы добавлены,
 Taxes and Charges Deducted,"Налоги и сборы, вычитаемые",
 Total Taxes and Charges,Общие налоги и сборы,
 Additional Discount,Дополнительная скидка,
-Apply Additional Discount On,Применить Дополнительную Скидку на,
-Additional Discount Amount (Company Currency),Дополнительная скидка Сумма (валюта компании),
+Apply Additional Discount On,Применить дополнительную скидку на,
+Additional Discount Amount (Company Currency),Сумма дополнительных скидок (в валюте компании),
 Additional Discount Percentage,Дополнительная скидка в процентах,
 Additional Discount Amount,Сумма дополнительной скидки,
-Grand Total (Company Currency),Общий итог (Компания Валюта),
-Rounding Adjustment (Company Currency),Коррекция округления (валюта компании),
-Rounded Total (Company Currency),Округлые Всего (Компания Валюта),
-In Words (Company Currency),В Слов (Компания валюте),
+Grand Total (Company Currency),Общий итог (в валюте компании),
+Rounding Adjustment (Company Currency),Коррекция округления (в валюте компании),
+Rounded Total (Company Currency),Всего округленно (в валюте компании),
+In Words (Company Currency),Словами (в валюте компании),
 Rounding Adjustment,Коррекция округления,
 In Words,Прописью,
-Total Advance,Всего Advance,
+Total Advance,Общий аванс,
 Disable Rounded Total,Отключение закругленными Итого,
 Cash/Bank Account,Наличные / Банковский счет,
-Write Off Amount (Company Currency),Списание Сумма (Компания валют),
-Set Advances and Allocate (FIFO),Set Advances and Allocate (FIFO),
+Write Off Amount (Company Currency),Сумма списаний (в валюте компании),
+Set Advances and Allocate (FIFO),Установите авансы и распределите (FIFO),
 Get Advances Paid,Получить авансы выданные,
 Advances,Авансы,
-Terms,Термины,
+Terms,Условия,
 Terms and Conditions1,Сроки и условиях1,
 Group same items,Сгруппировать похожие продукты,
-Print Language,Язык печати,
+Print Language,Язык для печати,
 "Once set, this invoice will be on hold till the set date",После этого этот счет будет приостановлен до установленной даты,
-Credit To,Кредитная Для,
+Credit To,Кредит для,
 Party Account Currency,Партия Валюта счета,
 Against Expense Account,Со счета расходов,
 Inter Company Invoice Reference,Справочная информация для Inter Company,
 Is Internal Supplier,Внутренний поставщик,
-Start date of current invoice's period,Дату периода текущего счета-фактуры начнем,
-End date of current invoice's period,Дата и время окончания периода текущего счета-фактуры в,
+Start date of current invoice's period,Дата начала периода текущего счета,
+End date of current invoice's period,Дата окончания периода текущего счета,
 Update Auto Repeat Reference,Обновить ссылку на автоматический повтор,
-Purchase Invoice Advance,Счета-фактуры Advance,
-Purchase Invoice Item,Покупка Счет Пункт,
-Quantity and Rate,Количество и курс,
+Purchase Invoice Advance,Авансовый счет на покупку,
+Purchase Invoice Item,Счет на покупку продукта,
+Quantity and Rate,Количество и стоимость,
 Received Qty,Поступившее кол-во,
-Accepted Qty,Принятый Кол-во,
-Rejected Qty,Отклонено Кол-во,
+Accepted Qty,Принятое кол-во,
+Rejected Qty,Отклоненое кол-во,
 UOM Conversion Factor,Коэффициент пересчета единицы измерения,
-Discount on Price List Rate (%),Скидка на Прайс-лист ставка (%),
-Price List Rate (Company Currency),Прайс-лист Тариф (Компания Валюта),
-Rate ,Цена,
-Rate (Company Currency),Тариф (Компания Валюта),
-Amount (Company Currency),Сумма (Компания Валюта),
+Discount on Price List Rate (%),Скидка от прайс-листа (%),
+Price List Rate (Company Currency),Прайс-лист Тариф (в валюте компании),
+Rate ,Цена ,
+Rate (Company Currency),Тариф (в валюте компании),
+Amount (Company Currency),Сумма (в валюте компании),
 Is Free Item,Это бесплатный товар,
 Net Rate,Нетто-ставка,
-Net Rate (Company Currency),Чистая скорость (Компания валют),
-Net Amount (Company Currency),Чистая сумма (валюта Компании),
+Net Rate (Company Currency),Чистая стоимость (в валюте компании),
+Net Amount (Company Currency),Чистая сумма (в валюте компании),
 Item Tax Amount Included in Value,"Сумма налога на имущество, включенная в стоимость",
 Landed Cost Voucher Amount,Земельные стоимости путевки сумма,
 Raw Materials Supplied Cost,Стоимость поставленного сырья,
 Accepted Warehouse,Принимающий склад,
 Serial No,Серийный номер,
-Rejected Serial No,Отклонен Серийный номер,
-Expense Head,Расходов Глава,
-Is Fixed Asset,Фиксирована Asset,
+Rejected Serial No,Отклоненный Серийный номер,
+Expense Head,Глава расходов,
+Is Fixed Asset,Является основным средством,
 Asset Location,Месторасположение активов,
 Deferred Expense,Отложенные расходы,
 Deferred Expense Account,Отсроченная учетная запись,
@@ -5092,7 +5094,7 @@
 Valuation and Total,Оценка и Всего,
 Valuation,Оценка,
 Add or Deduct,Добавить или вычесть,
-Deduct,Вычеты €,
+Deduct,Вычеты,
 On Previous Row Amount,На Сумму предыдущей строки,
 On Previous Row Total,На Итого предыдущей строки,
 On Item Quantity,На количество товара,
@@ -5103,10 +5105,10 @@
 Tax Amount After Discount Amount,Сумма налога После скидка сумма,
 Item Wise Tax Detail ,Пункт Мудрые налоговые данные,
 "Standard tax template that can be applied to all Purchase Transactions. This template can contain list of tax heads and also other expense heads like ""Shipping"", ""Insurance"", ""Handling"" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n    - This can be on **Net Total** (that is the sum of basic amount).\n    - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n    - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on ""Previous Row Total"" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Consider Tax or Charge for: In this section you can specify if the tax / charge is only for valuation (not a part of total) or only for total (does not add value to the item) or for both.\n10. Add or Deduct: Whether you want to add or deduct the tax.","Стандартный шаблон налог, который может быть применен ко всем операциям купли-. Этот шаблон может содержать перечень налоговых руководителей, а также других расходов руководителей как ""Shipping"", ""Insurance"", ""Обращение"" и т.д. \n\n #### Примечание \n\n ставка налога на которые вы указали здесь будет стандартная ставка налога на прибыль для всех ** деталей **. Если есть ** товары **, которые имеют различные цены, они должны быть добавлены в ** деталь налога ** стол в ** деталь ** мастера.\n\n #### Описание колонок \n\n 1. Расчет Тип: \n - Это может быть ** Чистый Всего ** (то есть сумма основной суммы).\n - ** На предыдущей строке Total / сумма ** (по совокупности налогов и сборов). Если вы выбираете эту опцию, налог будет применяться в процентах от предыдущего ряда (в налоговом таблицы) суммы или объема.\n - ** ** Фактический (как уже упоминалось).\n 2. Счет Руководитель: лицевому счету, при которых этот налог будут забронированы \n 3. Центр Стоимость: Если налог / налог на заряд доход (как перевозка груза) или расходов это должен быть забронирован на МВЗ.\n 4. Описание: Описание налога (которые будут напечатаны в счетах-фактурах / кавычек).\n 5. Оценить: Налоговая ставка.\n 6. Количество: Сумма налога.\n 7. Всего: Суммарное к этой точке.\n 8. Введите Row: Если на базе ""Предыдущая сумма по строке"" вы можете выбрать номер строки которой будет приниматься в качестве основы для такого расчета (по умолчанию предыдущего ряда).\n 9. Рассмотрим налог или сбор для: В этом разделе вы можете указать, будет ли налог / налог на сбор только для оценки (не часть от общей суммы) или только для общей (не добавляет ценности объект) или для обоих.\n 10. Добавить или вычесть: Если вы хотите, чтобы добавить или вычесть налог.",
-Salary Component Account,Зарплатный Компонент,
+Salary Component Account,Счет компонента заработной платы,
 Default Bank / Cash account will be automatically updated in Salary Journal Entry when this mode is selected.,"По умолчанию банк / Наличный счет будет автоматически обновляться в Зарплатный Запись в журнале, когда выбран этот режим.",
 ACC-SINV-.YYYY.-,ACC-SINV-.YYYY.-,
-Include Payment (POS),Включите Оплату (POS),
+Include Payment (POS),Включая оплату (POS),
 Offline POS Name,Offline POS Имя,
 Is Return (Credit Note),Возврат (кредитная нота),
 Return Against Sales Invoice,Вернуться против накладная,
@@ -5114,8 +5116,8 @@
 Customer PO Details,Детали заказа клиента,
 Customer's Purchase Order,Заказ клиента,
 Customer's Purchase Order Date,Клиентам Дата Заказ,
-Customer Address,Клиент Адрес,
-Shipping Address Name,Адрес доставки Имя,
+Customer Address,Адрес клиента,
+Shipping Address Name,Название адрес доставки,
 Company Address Name,Название компании,
 Rate at which Customer Currency is converted to customer's base currency,Курс по которому валюта Покупателя конвертируется в базовую валюту покупателя,
 Rate at which Price list currency is converted to customer's base currency,Курс по которому валюта Прайс листа конвертируется в базовую валюту покупателя,
@@ -5123,21 +5125,21 @@
 Packing List,Список упаковки,
 Packed Items,Упакованные продукты,
 Product Bundle Help,Продукт Связка Помощь,
-Time Sheet List,Список времени лист,
-Time Sheets,Time Sheets,
-Total Billing Amount,Всего счетов Сумма,
-Sales Taxes and Charges Template,Продажи Налоги и сборы шаблона,
+Time Sheet List,Список табелей учета рабочего времени,
+Time Sheets,Табель учета рабочего времени,
+Total Billing Amount,Общая сумма счета,
+Sales Taxes and Charges Template,Шаблон налогов и сборов с продаж,
 Sales Taxes and Charges,Налоги и сборы с продаж,
 Loyalty Points Redemption,Выкуп лояльности очков,
 Redeem Loyalty Points,Погасить очки лояльности,
 Redemption Account,Счет погашения,
 Redemption Cost Center,Центр выкупа,
-In Words will be visible once you save the Sales Invoice.,По словам будет виден только вы сохраните Расходная накладная.,
+In Words will be visible once you save the Sales Invoice.,В записях будет видно как только вы сохраните счет продажи.,
 Allocate Advances Automatically (FIFO),Автоматическое выделение авансов (FIFO),
 Get Advances Received,Получить авансы полученные,
-Base Change Amount (Company Currency),Базовая Изменение Сумма (Компания Валюта),
+Base Change Amount (Company Currency),Базовая Изменение Сумма (в валюте компании),
 Write Off Outstanding Amount,Списание суммы задолженности,
-Terms and Conditions Details,Условия Подробности,
+Terms and Conditions Details,Дополнительные условия,
 Is Internal Customer,Внутренний клиент,
 Is Discounted,Со скидкой,
 Unpaid and Discounted,Неоплачиваемый и со скидкой,
@@ -5149,7 +5151,7 @@
 Commission Rate (%),Комиссия ставка (%),
 Sales Team1,Продажи Команда1,
 Against Income Account,Со счета доходов,
-Sales Invoice Advance,Счет Продажи предварительный,
+Sales Invoice Advance,Предварительный счет продажи,
 Advance amount,Предварительная сумма,
 Sales Invoice Item,Счет на продажу продукта,
 Customer's Item Code,Клиентский код продукта,
@@ -5158,40 +5160,40 @@
 Discount and Margin,Скидка и маржа,
 Rate With Margin,Оценить с маржой,
 Discount (%) on Price List Rate with Margin,Скидка (%) на цену Прейскурант с маржой,
-Rate With Margin (Company Currency),Ставка с маржей (валюта компании),
+Rate With Margin (Company Currency),Ставка с маржей (в валюте компании),
 Delivered By Supplier,Доставлено поставщиком,
 Deferred Revenue,Отложенный доход,
 Deferred Revenue Account,Отложенный счет доходов,
 Enable Deferred Revenue,Включить отложенный доход,
-Stock Details,Подробности Запасов,
+Stock Details,Подробности запасов,
 Customer Warehouse (Optional),Склад Клиент (Необязательно),
-Available Batch Qty at Warehouse,Доступные Пакетная Кол-во на складе,
-Available Qty at Warehouse,Доступное Кол-во на складе,
+Available Batch Qty at Warehouse,Доступное кол-во пакетов на складе,
+Available Qty at Warehouse,Доступное кол-во на складе,
 Delivery Note Item,Доставляемый продукт,
-Base Amount (Company Currency),Базовая сумма (Компания Валюта),
+Base Amount (Company Currency),Базовая сумма (в валюте компании),
 Sales Invoice Timesheet,Счет по табелю,
-Time Sheet,Время Sheet,
-Billing Hours,Платежная часы,
-Timesheet Detail,Timesheet Деталь,
-Tax Amount After Discount Amount (Company Currency),Сумма налога после скидки Сумма (Компания валют),
-Item Wise Tax Detail,Пункт Мудрый Налоговый Подробно,
+Time Sheet,Табель учета рабочего времени,
+Billing Hours,Оплачеваемые часы,
+Timesheet Detail,Сведения о расписании,
+Tax Amount After Discount Amount (Company Currency),Сумма налога после суммы скидки (в валюте компании),
+Item Wise Tax Detail,Подробная информация о налоге на товар,
 Parenttype,ParentType,
 "Standard tax template that can be applied to all Sales Transactions. This template can contain list of tax heads and also other expense / income heads like ""Shipping"", ""Insurance"", ""Handling"" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n    - This can be on **Net Total** (that is the sum of basic amount).\n    - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n    - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on ""Previous Row Total"" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Is this Tax included in Basic Rate?: If you check this, it means that this tax will not be shown below the item table, but will be included in the Basic Rate in your main item table. This is useful where you want give a flat price (inclusive of all taxes) price to customers.","Стандартный шаблон налог, который может быть применен ко всем сделок купли-продажи. Этот шаблон может содержать перечень налоговых руководителей, а также других глав расходы / доходы, как ""Shipping"", ""Insurance"", ""Обращение"" и т.д. \n\n #### Примечание \n\n ставка налога на Вы Определить здесь будет стандартная ставка налога на прибыль для всех ** деталей **. Если есть ** товары **, которые имеют различные цены, они должны быть добавлены в ** деталь налога ** стол в ** деталь ** мастера.\n\n #### Описание колонок \n\n 1. Расчет Тип: \n - Это может быть ** Чистый Всего ** (то есть сумма основной суммы).\n - ** На предыдущей строке Total / сумма ** (по совокупности налогов и сборов). Если вы выбираете эту опцию, налог будет применяться в процентах от предыдущего ряда (в налоговом таблицы) суммы или объема.\n - ** ** Фактический (как уже упоминалось).\n 2. Счет Руководитель: лицевому счету, при которых этот налог будут забронированы \n 3. Центр Стоимость: Если налог / налог на заряд доход (как перевозка груза) или расходов это должен быть забронирован на МВЗ.\n 4. Описание: Описание налога (которые будут напечатаны в счетах-фактурах / кавычек).\n 5. Оценить: Налоговая ставка.\n 6. Количество: Сумма налога.\n 7. Всего: Суммарное к этой точке.\n 8. Введите Row: Если на базе ""Предыдущая сумма по строке"" вы можете выбрать номер строки которой будет приниматься в качестве основы для такого расчета (по умолчанию предыдущего ряда).\n 9. Это налог Включено в основной ставке ?: Если вы посмотрите, это значит, что этот налог не будет показано ниже в таблице элементов, но будет включен в основной ставке в основной таблице элементов. Это полезно, если вы хотите дать квартира Цена (включая все налоги) цену к клиентам.",
 * Will be calculated in the transaction.,* Будет рассчитана в сделке.,
-From No,От Нет,
-To No,Нет,
-Is Company,Является ли компания,
+From No,От №,
+To No,К №,
+Is Company,Это компания,
 Current State,Текущее состояние,
-Purchased,купленный,
+Purchased,Купленный,
 From Shareholder,От акционеров,
-From Folio No,Из Folio No,
+From Folio No,Из фолио №,
 To Shareholder,Акционеру,
-To Folio No,В Folio No,
+To Folio No,В фолио №,
 Equity/Liability Account,Акционерный / Обязательный счет,
 Asset Account,Аккаунт активов,
 (including),(в том числе),
 ACC-SH-.YYYY.-,ACC-SH-.YYYY.-,
-Folio no.,Folio no.,
+Folio no.,Фолио №,
 Address and Contacts,Адрес и контакты,
 Contact List,Список контактов,
 Hidden list maintaining the list of contacts linked to Shareholder,"Скрытый список, поддерживающий список контактов, связанных с Акционером",
@@ -5201,7 +5203,7 @@
 Shipping Rule Type,Тип правила доставки,
 Shipping Account,Счет доставки,
 Calculate Based On,Рассчитать на основе,
-Fixed,Исправлена,
+Fixed,Фиксированно,
 Net Weight,Вес нетто,
 Shipping Amount,Сумма доставки,
 Shipping Rule Conditions,Правило перевозки груза,
@@ -5223,11 +5225,11 @@
 Number of days that the subscriber has to pay invoices generated by this subscription,"Количество дней, в течение которых абонент должен оплатить счета, сгенерированные этой подпиской",
 Cancel At End Of Period,Отмена на конец периода,
 Generate Invoice At Beginning Of Period,Сформировать счет в начале периода,
-Plans,планы,
+Plans,Планы,
 Discounts,Скидки,
 Additional DIscount Percentage,Процент Дополнительной Скидки,
 Additional DIscount Amount,Сумма Дополнительной Скидки,
-Subscription Invoice,Счет-фактура,
+Subscription Invoice,Счет за подписку,
 Subscription Plan,План подписки,
 Cost,Стоимость,
 Billing Interval,Интервал выставления счетов,
@@ -5241,18 +5243,18 @@
 Number of days after invoice date has elapsed before canceling subscription or marking subscription as unpaid,Количество дней после истечения срока выставления счета перед отменой подписки или подпиской по подписке как неоплаченной,
 Prorate,пропорциональная доля,
 Tax Rule,Налоговое положение,
-Tax Type,Налоги Тип,
+Tax Type,Тип налога,
 Use for Shopping Cart,Используйте корзину для,
 Billing City,Город платильщика,
 Billing County,Платежный County,
 Billing State,Государственный счетов,
 Billing Zipcode,"Индекс по банковскому переводу, по счету",
 Billing Country,Страна плательщика,
-Shipping City,Доставка Город,
-Shipping County,графство Доставка,
-Shipping State,Государственный Доставка,
-Shipping Zipcode,Доставка Zipcode,
-Shipping Country,Доставка Страна,
+Shipping City,Доставка город,
+Shipping County,Доставка округ,
+Shipping State,Доставка регион,
+Shipping Zipcode,Доставка индекс,
+Shipping Country,Доставка страна,
 Tax Withholding Account,Удержание налога,
 Tax Withholding Rates,Ставки удержания налогов,
 Rates,Ставки,
@@ -5264,7 +5266,7 @@
 Water Analysis,Анализ воды,
 Soil Analysis,Анализ почвы,
 Plant Analysis,Анализ растений,
-Fertilizer,удобрение,
+Fertilizer,Удобрение,
 Soil Texture,Текстура почвы,
 Weather,Погода,
 Agriculture Manager,Менеджер по развитию,
@@ -5278,7 +5280,7 @@
 Previous Business Day,Предыдущий рабочий день,
 Next Business Day,Следующий рабочий день,
 Urgent,Важно,
-Crop,урожай,
+Crop,Урожай,
 Crop Name,Название урожая,
 Scientific Name,Научное название,
 "You can define all the tasks which need to carried out for this crop here. The day field is used to mention the day on which the task needs to be carried out, 1 being the 1st day, etc.. ","Здесь вы можете определить все задачи, которые необходимо выполнить для этого урожая. Поле дня используется для обозначения дня, когда задача должна быть выполнена, 1 - 1-й день и т. Д.",
@@ -5286,7 +5288,7 @@
 Crop Spacing UOM,Интервал между кадрами UOM,
 Row Spacing,Интервал между рядами,
 Row Spacing UOM,Интервал строк,
-Perennial,круглогодичный,
+Perennial,Круглогодичный,
 Biennial,двухгодичный,
 Planting UOM,Посадка UOM,
 Planting Area,Площадь посадки,
@@ -5296,7 +5298,7 @@
 Produce,Производить,
 Byproducts,Субпродукты,
 Linked Location,Связанное местоположение,
-A link to all the Locations in which the Crop is growing,Ссылка на все Местоположения в который растет Урожай,
+A link to all the Locations in which the Crop is growing,Ссылка на все местоположения в которых есть урожай,
 This will be day 1 of the crop cycle,Это будет первый день цикла урожая,
 ISO 8601 standard,Стандарт ISO 8601,
 Cycle Type,Тип цикла,
@@ -5307,7 +5309,7 @@
 List of diseases detected on the field. When selected it'll automatically add a list of tasks to deal with the disease ,"Список заболеваний, обнаруженных на поле. При выборе он автоматически добавит список задач для борьбы с болезнью",
 Detected Disease,Обнаруженная болезнь,
 LInked Analysis,Анализ LInked,
-Disease,болезнь,
+Disease,Болезнь,
 Tasks Created,Созданные задачи,
 Common Name,Распространенное имя,
 Treatment Task,Лечебная задача,
@@ -5326,10 +5328,10 @@
 Plant Analysis Criteria,Критерии анализа производства,
 Minimum Permissible Value,Минимальное допустимое значение,
 Maximum Permissible Value,Максимально допустимое значение,
-Ca/K,Са / К,
-Ca/Mg,Са / Mg,
-Mg/K,Мг / К,
-(Ca+Mg)/K,(Са + Mg) / К,
+Ca/K,Са/К,
+Ca/Mg,Са/Mg,
+Mg/K,Мг/К,
+(Ca+Mg)/K,(Са+Mg)/К,
 Ca/(K+Ca+Mg),Са / (К + Са + Mg),
 Soil Analysis Criterias,Критерий анализа почвы,
 Soil Analysis Criteria,Критерии оценки почвы,
@@ -5357,11 +5359,11 @@
 Person Responsible,Ответственное лицо,
 Water Analysis Criteria,Критерии анализа воды,
 Weather Parameter,Параметры погоды,
-ACC-ASS-.YYYY.-,ACC-АСС-.YYYY.-,
+ACC-ASS-.YYYY.-,ACC-ASS-.YYYY.-,
 Asset Owner,Владелец актива,
 Asset Owner Company,Компания по управлению активами,
-Custodian,попечитель,
-Disposal Date,Утилизация Дата,
+Custodian,Попечитель,
+Disposal Date,Дата утилизации,
 Journal Entry for Scrap,Запись в журнале для лома,
 Available-for-use Date,Доступная для использования дата,
 Calculate Depreciation,Рассчитать амортизацию,
@@ -5374,29 +5376,29 @@
 Value After Depreciation,Значение после амортизации,
 Total Number of Depreciations,Общее количество отчислений на амортизацию,
 Frequency of Depreciation (Months),Частота амортизации (месяцев),
-Next Depreciation Date,Следующий Износ Дата,
-Depreciation Schedule,Амортизация Расписание,
-Depreciation Schedules,Амортизационные Расписания,
+Next Depreciation Date,Следующая дата амортизации,
+Depreciation Schedule,Амортизация расписание,
+Depreciation Schedules,Амортизационные расписания,
 Insurance details,Детали страхования,
 Policy number,Номер полиса,
-Insurer,страхователь,
+Insurer,Страхователь,
 Insured value,Страховое значение,
 Insurance Start Date,Дата начала страхования,
 Insurance End Date,Дата окончания страхования,
 Comprehensive Insurance,Комплексное страхование,
 Maintenance Required,Требуется техническое обслуживание,
-Check if Asset requires Preventive Maintenance or Calibration,"Проверьте, требуется ли Asset профилактическое обслуживание или калибровка",
+Check if Asset requires Preventive Maintenance or Calibration,"Проверьте, требует ли актив профилактического обслуживания или калибровки",
 Booked Fixed Asset,Забронированные основные средства,
 Purchase Receipt Amount,Сумма покупки,
 Default Finance Book,Финансовая книга по умолчанию,
 Quality Manager,Менеджер по качеству,
-Asset Category Name,Asset Категория Название,
+Asset Category Name,Название категории активов,
 Depreciation Options,Варианты амортизации,
 Enable Capital Work in Progress Accounting,Включить капитальную работу в процессе учета,
 Finance Book Detail,Финансовая книга,
-Asset Category Account,Категория активов Счет,
+Asset Category Account,Счет категории активов,
 Fixed Asset Account,Счет учета основных средств,
-Accumulated Depreciation Account,Начисленной амортизации Счет,
+Accumulated Depreciation Account,Счет накопленной амортизации,
 Depreciation Expense Account,Износ счет расходов,
 Capital Work In Progress Account,Счет капитальной работы,
 Asset Finance Book,Финансовая книга по активам,
@@ -5411,15 +5413,15 @@
 Asset Maintenance Log,Журнал обслуживания активов,
 ACC-AML-.YYYY.-,ACC-AML-.YYYY.-,
 Maintenance Type,Тип технического обслуживания,
-Maintenance Status,Техническое обслуживание Статус,
-Planned,планируемый,
+Maintenance Status,Статус технического обслуживания,
+Planned,Планируемый,
 Has Certificate ,Имеет сертификат,
 Certificate,Сертификат,
 Actions performed,Выполненные действия,
 Asset Maintenance Task,Задача по обслуживанию активов,
 Maintenance Task,Задача обслуживания,
 Preventive Maintenance,Профилактика,
-Calibration,калибровка,
+Calibration,Калибровка,
 2 Yearly,2 года,
 Certificate Required,Требуется сертификат,
 Assign to Name,Назначить имя,
@@ -5441,7 +5443,7 @@
 Assign To Name,Назначить имя,
 Repair Status,Статус ремонта,
 Error Description,Описание ошибки,
-Downtime,время простоя,
+Downtime,Время простоя,
 Repair Cost,Стоимость ремонта,
 Manufacturing Manager,Менеджер производства,
 Current Asset Value,Текущая стоимость актива,
@@ -5453,19 +5455,19 @@
 Is Container,Контейнер,
 Check if it is a hydroponic unit,"Проверьте, является ли это гидропонной единицей",
 Location Details,Информация о местоположении,
-Latitude,широта,
-Longitude,долгота,
+Latitude,Широта,
+Longitude,Долгота,
 Area,Площадь,
 Area UOM,Область UOM,
 Tree Details,Детали Дерево,
 Maintenance Team Member,Член технической службы,
 Team Member,Участник команды,
 Maintenance Role,Роль обслуживания,
-Buying Settings,Настройка покупки,
-Settings for Buying Module,Настройки для покупки модуля,
-Supplier Naming By,Поставщик Именование По,
+Buying Settings,Настройка закупок,
+Settings for Buying Module,Настройки модуля закупок,
+Supplier Naming By,Поставщик именование по,
 Default Supplier Group,Группа поставщиков по умолчанию,
-Default Buying Price List,По умолчанию Покупка Прайс-лист,
+Default Buying Price List,По умолчанию прайс-лист покупки,
 Backflush Raw Materials of Subcontract Based On,"Исходные материалы субподряда, основанные на",
 Material Transferred for Subcontract,"Материал, переданный для субподряда",
 Over Transfer Allowance (%),Превышение Пособия (%),
@@ -5473,13 +5475,13 @@
 PUR-ORD-.YYYY.-,PUR-ORD-.YYYY.-,
 Get Items from Open Material Requests,Получить товары из Открыть Запросов Материала,
 Fetch items based on Default Supplier.,Получать элементы на основе поставщика по умолчанию.,
-Required By,Требуется По,
-Order Confirmation No,Подтверждение заказа Нет,
+Required By,Требуется по,
+Order Confirmation No,Подтверждение заказа №,
 Order Confirmation Date,Дата подтверждения заказа,
-Customer Mobile No,Заказчик Мобильная Нет,
-Customer Contact Email,Контактный адрес электронной почты Клиента,
+Customer Mobile No,Заказчик номер мобильного,
+Customer Contact Email,Контактный адрес электронной почты клиента,
 Set Target Warehouse,Установить целевой склад,
-Sets 'Warehouse' in each row of the Items table.,Устанавливает «Склад» в каждой строке таблицы «Предметы».,
+Sets 'Warehouse' in each row of the Items table.,Устанавливает «Склад» в каждой строке таблицы «Продукты».,
 Supply Raw Materials,Поставка сырья,
 Purchase Order Pricing Rule,Правило ценообразования заказа на покупку,
 Set Reserve Warehouse,Установить резервный склад,
@@ -5501,13 +5503,13 @@
 Blanket Order Rate,Стоимость заказа на одеяло,
 Returned Qty,Вернулся Кол-во,
 Purchase Order Item Supplied,Заказ товара Поставляется,
-BOM Detail No,ВМ детали №,
+BOM Detail No,Подробности спецификации №,
 Stock Uom,Единица измерения запасов,
 Raw Material Item Code,Код сырьевой  позиции,
 Supplied Qty,Поставляемое кол-во,
 Purchase Receipt Item Supplied,Покупка Получение товара Поставляется,
 Current Stock,Наличие на складе,
-PUR-RFQ-.YYYY.-,PUR-предложения-.YYYY.-,
+PUR-RFQ-.YYYY.-,PUR-RFQ-.YYYY.-,
 For individual supplier,Для индивидуального поставщика,
 Link to Material Requests,Ссылка на запросы материалов,
 Message for Supplier,Сообщение для Поставщика,
@@ -5515,19 +5517,19 @@
 Required Date,Требуемая дата,
 Request for Quotation Supplier,Запрос на предложение поставщика,
 Send Email,Отправить письмо,
-Quote Status,Статус цитаты,
+Quote Status,Статус предложения,
 Download PDF,Скачать PDF,
 Supplier of Goods or Services.,Поставщик товаров или услуг.,
 Name and Type,Наименование и тип,
 SUP-.YYYY.-,SUP-.YYYY.-,
 Default Bank Account,По умолчанию Банковский счет,
-Is Transporter,Является Transporter,
+Is Transporter,Является перевозчиком,
 Represents Company,Представляет компанию,
 Supplier Type,Тип поставщика,
-Allow Purchase Invoice Creation Without Purchase Order,Разрешить создание счета-фактуры без заказа на покупку,
-Allow Purchase Invoice Creation Without Purchase Receipt,Разрешить создание счета-фактуры без квитанции о покупке,
+Allow Purchase Invoice Creation Without Purchase Order,Разрешить создание счета без заказа на покупку,
+Allow Purchase Invoice Creation Without Purchase Receipt,Разрешить создание счета без квитанции о покупке,
 Warn RFQs,Предупреждать о RFQ,
-Warn POs,Предупредить ПО,
+Warn POs,Предупреждать ПО,
 Prevent RFQs,Предотвращение запросов,
 Prevent POs,Предотвращение PO,
 Billing Currency,Платежная валюта,
@@ -5543,9 +5545,9 @@
 PUR-SQTN-.YYYY.-,PUR-SQTN-.YYYY.-,
 Supplier Address,Адрес поставщика,
 Link to material requests,Ссылка на заявки на материалы,
-Rounding Adjustment (Company Currency,Корректировка округления (Валюта компании,
+Rounding Adjustment (Company Currency,Корректировка округления (в валюте компании,
 Auto Repeat Section,Секция автоматического повтора,
-Is Subcontracted,Является субподряду,
+Is Subcontracted,Является субподрядом,
 Lead Time in days,Время выполнения в днях,
 Supplier Score,Оценка поставщика,
 Indicator Color,Цвет индикатора,
@@ -5560,7 +5562,7 @@
 Criteria Setup,Настройка критериев,
 Load All Criteria,Загрузить все критерии,
 Scoring Criteria,Критерии оценки,
-Scorecard Actions,Действия в Scorecard,
+Scorecard Actions,Действия в оценочной карте,
 Warn for new Request for Quotations,Предупреждать о новых запросах на предложение,
 Warn for new Purchase Orders,Предупреждать о новых заказах на поставку,
 Notify Supplier,Сообщите поставщику,
@@ -5573,22 +5575,22 @@
 Supplier Scorecard Period,Период оценки поставщика,
 PU-SSP-.YYYY.-,PU-SSP-.YYYY.-,
 Period Score,Период,
-Calculations,вычисления,
-Criteria,критерии,
-Variables,переменные,
-Supplier Scorecard Setup,Поставщик Scorecard Setup,
+Calculations,Вычисления,
+Criteria,Критерии,
+Variables,Переменные,
+Supplier Scorecard Setup,Настройка оценочной карты поставщика,
 Supplier Scorecard Scoring Criteria,Критерии оценки поставщика,
 Score,Балл,
-Supplier Scorecard Scoring Standing,Поставщик Scorecard Scoring Standing,
+Supplier Scorecard Scoring Standing,Статус оценочной карты поставщика,
 Standing Name,Постоянное имя,
 Purple,Пурпурный,
-Yellow,жёлтый,
-Orange,оранжевый,
-Min Grade,Min Grade,
-Max Grade,Макс. Класс,
+Yellow,Жёлтый,
+Orange,Оранжевый,
+Min Grade,Мин. класс,
+Max Grade,Макс. класс,
 Warn Purchase Orders,Предупреждать заказы на поставку,
 Prevent Purchase Orders,Запретить заказы на поставку,
-Employee ,Сотрудник,
+Employee ,Сотрудник ,
 Supplier Scorecard Scoring Variable,Переменная переменной Scorecard поставщика,
 Variable Name,Имя переменной,
 Parameter Name,Имя параметра,
@@ -5598,27 +5600,27 @@
 Call Log,Журнал вызовов,
 Received By,Получено,
 Caller Information,Информация о звонящем,
-Contact Name,Имя Контакта,
-Lead ,вести,
-Lead Name,Имя,
-Ringing,звонкий,
+Contact Name,Имя контакта,
+Lead ,Лид ,
+Lead Name,Имя лида,
+Ringing,Звонок,
 Missed,Пропущенный,
 Call Duration in seconds,Продолжительность звонка в секундах,
 Recording URL,Запись URL,
-Communication Medium,Связь Средний,
-Communication Medium Type,Тип средств связи,
-Voice,голос,
+Communication Medium,Способ коммуникации,
+Communication Medium Type,Тип способа коммуникации,
+Voice,Голос,
 Catch All,Поймать все,
 "If there is no assigned timeslot, then communication will be handled by this group","Если нет назначенного временного интервала, то связь будет обрабатываться этой группой",
 Timeslots,Временные интервалы,
-Communication Medium Timeslot,Коммуникационный средний таймслот,
+Communication Medium Timeslot,Коммуникационный таймслот,
 Employee Group,Группа сотрудников,
 Appointment,"Деловое свидание, встреча",
 Scheduled Time,Назначенное время,
-Unverified,непроверенный,
+Unverified,Непроверенный,
 Customer Details,Данные клиента,
 Phone Number,Телефонный номер,
-Skype ID,Скайп ай-ди,
+Skype ID,Skype ID,
 Linked Documents,Связанные документы,
 Appointment With,Встреча с,
 Calendar Event,Календарь событий,
@@ -5638,22 +5640,22 @@
 "Leave blank for home.\nThis is relative to site URL, for example ""about"" will redirect to ""https://yoursitename.com/about""","Оставьте пустым для дома. Это относительно URL сайта, например, «about» будет перенаправлен на «https://yoursitename.com/about»",
 Appointment Booking Slots,Назначение Бронирование Слоты,
 Day Of Week,День недели,
-From Time ,С,
+From Time ,От времени ,
 Campaign Email Schedule,Расписание рассылки кампании,
 Send After (days),Отправить после (дней),
 Signed,подписанный,
 Party User,Пользователь Party,
-Unsigned,неподписанный,
+Unsigned,Неподписанный,
 Fulfilment Status,Статус выполнения,
-N/A,N / A,
-Unfulfilled,невыполненный,
+N/A,Н/Д,
+Unfulfilled,Невыполненный,
 Partially Fulfilled,Частично выполнено,
 Fulfilled,Исполненная,
 Lapsed,Просроченные,
 Contract Period,Контрактный период,
 Signee Details,Информация о подписчике,
-Signee,грузополучатель,
-Signed On,Подпись,
+Signee,Грузополучатель,
+Signed On,Подписано,
 Contract Details,Информация о контракте,
 Contract Template,Шаблон контракта,
 Contract Terms,Условия договора,
@@ -5662,7 +5664,7 @@
 Fulfilment Deadline,Срок выполнения,
 Fulfilment Terms,Условия выполнения,
 Contract Fulfilment Checklist,Контрольный список выполнения контракта,
-Requirement,требование,
+Requirement,Требование,
 Contract Terms and Conditions,Условия договора,
 Fulfilment Terms and Conditions,Сроки и условия выполнения,
 Contract Template Fulfilment Terms,Условия заключения контрактов,
@@ -5683,7 +5685,7 @@
 Ends On,Заканчивается,
 Address & Contact,Адрес и контакт,
 Mobile No.,Мобильный,
-Lead Type,Тип Обращения,
+Lead Type,Тип лидов,
 Channel Partner,Партнер,
 Consultant,Консультант,
 Market Segment,Сегмент рынка,
@@ -5699,15 +5701,15 @@
 Consumer Key,Потребительский ключ,
 Consumer Secret,Потребительский секрет,
 User Details,Детали пользователя,
-Person URN,Человек URN,
+Person URN,ИНН человека,
 Session Status,Статус сеанса,
 Lost Reason Detail,Потерянная причина подробно,
 Opportunity Lost Reason,Возможность потерянной причины,
 Potential Sales Deal,Сделка потенциальных продаж,
-CRM-OPP-.YYYY.-,CRM-ОПП-.YYYY.-,
-Opportunity From,Выявление из,
-Customer / Lead Name,Имя Клиента / Обращения,
-Opportunity Type,Тип Выявления,
+CRM-OPP-.YYYY.-,CRM-OPP-.YYYY.-,
+Opportunity From,Возможность из,
+Customer / Lead Name,Имя Клиента / Лида,
+Opportunity Type,Тип возможности,
 Converted By,Преобразовано,
 Sales Stage,Этап продажи,
 Lost Reason,Забыли Причина,
@@ -5716,17 +5718,17 @@
 With Items,С продуктами,
 Probability (%),Вероятность (%),
 Contact Info,Контактная информация,
-Customer / Lead Address,Адрес Клиента / Обращения,
+Customer / Lead Address,Адрес Клиента / Лида,
 Contact Mobile No,Связаться Мобильный Нет,
 Enter name of campaign if source of enquiry is campaign,"Введите имя кампании, если источником исследования является кампания",
-Opportunity Date,Дата Выявления,
-Opportunity Item,Продукт Выявления,
+Opportunity Date,Дата возможности,
+Opportunity Item,Возможность по продукту,
 Basic Rate,Основная ставка,
 Stage Name,Сценический псевдоним,
 Social Media Post,Сообщение в социальных сетях,
 Post Status,Статус сообщения,
 Posted,Опубликовано,
-Share On,Поделись,
+Share On,Поделиться,
 Twitter,Twitter,
 LinkedIn,LinkedIn,
 Twitter Post Id,Идентификатор сообщения Twitter,
@@ -5747,7 +5749,7 @@
 Assessment Name,Название оценки,
 Grading Scale,Оценочная шкала,
 Examiner,экзаменатор,
-Examiner Name,Имя Examiner,
+Examiner Name,Имя экзаменатора,
 Supervisor,Руководитель,
 Supervisor Name,Имя супервизора,
 Evaluate,оценивать,
@@ -5756,7 +5758,7 @@
 Maximum Score,Максимальный балл,
 Result,Результат,
 Total Score,Общий счет,
-Grade,Класс,
+Grade,Ранг,
 Assessment Result Detail,Оценка результата Detail,
 Assessment Result Tool,Оценка результата инструмент,
 Result HTML,Результат HTML,
@@ -5765,7 +5767,7 @@
 Content Question,Содержание вопроса,
 Question Link,Ссылка на вопрос,
 Course Name,Название курса,
-Topics,темы,
+Topics,Темы,
 Hero Image,Образ героя,
 Default Grading Scale,Шкала оценок,
 Education Manager,Менеджер по образованию,
@@ -5775,7 +5777,7 @@
 Course Assessment Criteria,Критерии оценки курса,
 Weightage,Добавка,
 Course Content,Содержание курса,
-Quiz,викторина,
+Quiz,Викторина,
 Program Enrollment,Программа подачи заявок,
 Enrollment Date,Дата поступления,
 Instructor Name,Имя инструктора,
@@ -5785,7 +5787,7 @@
 To TIme,До,
 Course End Date,Дата окончания курса,
 Course Topic,Тема курса,
-Topic,тема,
+Topic,Тема,
 Topic Name,Название темы,
 Education Settings,Настройки образования,
 Current Academic Year,Текущий академический год,
@@ -5818,7 +5820,7 @@
 Student Batch,Student Batch,
 Total Students,Всего студентов,
 Fee Schedule Student Group,Плата за обучение Студенческая группа,
-EDU-FST-.YYYY.-,EDU-FST .YYYY.-,
+EDU-FST-.YYYY.-,EDU-FST-.YYYY.-,
 EDU-FEE-.YYYY.-,EDU-FEE-.YYYY.-,
 Include Payment,Включить оплату,
 Send Payment Request,Отправить запрос на оплату,
@@ -5827,7 +5829,7 @@
 Grading Scale Name,Название шкалы оценок,
 Grading Scale Intervals,Интервалы Оценочная шкала,
 Intervals,Интервалы,
-Grading Scale Interval,Интервал Градация шкалы,
+Grading Scale Interval,Интервал градации шкалы,
 Grade Code,Код оценки,
 Threshold,Предельное значение,
 Grade Description,Описание класса,
@@ -5845,18 +5847,18 @@
 EDU-INS-.YYYY.-,EDU-INS-.YYYY.-,
 Instructor Log,Журнал инструктора,
 Other details,Другие детали,
-Option,вариант,
+Option,Вариант,
 Is Correct,Верно,
 Program Name,Название программы,
 Program Abbreviation,Программа Аббревиатура,
-Courses,курсы,
+Courses,Курсы,
 Is Published,Опубликовано,
 Allow Self Enroll,Разрешить самостоятельную регистрацию,
 Is Featured,Показано,
 Intro Video,Вступительное видео,
 Program Course,Программа курса,
 School House,Общежитие,
-Boarding Student,Студент-пансионер,
+Boarding Student,Студент-интернат,
 Check this if the Student is residing at the Institute's Hostel.,"Поставьте галочку, если студент проживает в общежитии института",
 Walking,Пешком,
 Institute's Bus,Автобус института,
@@ -5865,8 +5867,8 @@
 Pick/Drop by Guardian,Доставляет и забирает родитель/попечитель,
 Enrolled courses,Зачисленные курсы,
 Program Enrollment Course,Курсы по зачислению в программу,
-Program Enrollment Fee,Программа Зачисление Плата,
-Program Enrollment Tool,Программа Зачисление Tool,
+Program Enrollment Fee,Плата за участие в программе,
+Program Enrollment Tool,Инструмент регистрации в программе,
 Get Students From,Получить студентов из,
 Student Applicant,Абитуриент,
 Get Students,Получить Студенты,
@@ -5885,13 +5887,13 @@
 Quiz Configuration,Конфигурация викторины,
 Passing Score,Проходной балл,
 Score out of 100,Оценка из 100,
-Max Attempts,Макс Попытки,
+Max Attempts,Макс. попыток,
 Enter 0 to waive limit,"Введите 0, чтобы отказаться от лимита",
 Grading Basis,Оценка основ,
 Latest Highest Score,Последний наивысший балл,
 Latest Attempt,Последняя попытка,
 Quiz Activity,Викторина,
-Enrollment,регистрация,
+Enrollment,Регистрация,
 Pass,Проходить,
 Quiz Question,Контрольный вопрос,
 Quiz Result,Результат теста,
@@ -5910,7 +5912,7 @@
 A-,A-,
 B+,B+,
 B-,B-,
-O+,O +,
+O+,O+,
 O-,О-,
 AB+,AB+,
 AB-,AB-,
@@ -5921,7 +5923,7 @@
 Sibling Details,подробности Родственные,
 Siblings,Братья и сестры,
 Exit,Выход,
-Date of Leaving,Дата Покидая,
+Date of Leaving,Дата ухода,
 Leaving Certificate Number,Оставив номер сертификата,
 Reason For Leaving,Причина ухода,
 Student Admission,приёму студентов,
@@ -5936,9 +5938,9 @@
 Naming Series (for Student Applicant),Идентификация по Имени (для заявителей-студентов),
 LMS Only,Только LMS,
 EDU-APP-.YYYY.-,EDU-APP-.YYYY.-,
-Application Status,Статус приложения,
+Application Status,Статус подачи документов,
 Application Date,Дата подачи документов,
-Student Attendance Tool,Student Участники Инструмент,
+Student Attendance Tool,Инструмент посещаемости учащихся,
 Group Based On,Группа на основе,
 Students HTML,Студенты HTML,
 Group Based on,Группа основана на,
@@ -5958,9 +5960,9 @@
 Group Roll Number,Номер группы в реестре учреждения,
 Student Guardian,Студент-хранитель,
 Relation,Отношение,
-Mother,Мама,
+Mother,Мать,
 Father,Отец,
-Student Language,Student Язык,
+Student Language,Язык студента,
 Student Leave Application,Заявление на отпуск обучаемого,
 Mark as Present,Отметить как “Присутствует”,
 Student Log,Журнал студента,
@@ -5991,17 +5993,17 @@
 AE,AE,
 AU,AU,
 BR,BR,
-CA,Калифорния,
+CA,CA,
 CN,CN,
-DE,Делавэр,
+DE,DE,
 ES,ES,
 FR,FR,
-IN,В,
+IN,IN,
 JP,JP,
-IT,ЭТО,
+IT,IT,
 MX,MX,
-UK,Великобритания,
-US,НАС,
+UK,UK,
+US,US,
 Customer Type,Тип клиента,
 Market Place Account Group,Группа учета рынка,
 After Date,После даты,
@@ -6018,19 +6020,19 @@
 Exotel Settings,Настройки Exotel,
 Account SID,SID аккаунта,
 API Token,API-токен,
-GoCardless Mandate,Безрукий мандат,
-Mandate,мандат,
-GoCardless Customer,Без комиссии,
-GoCardless Settings,Настройки бездорожья,
-Webhooks Secret,Секретные клипы,
-Plaid Settings,Настройки пледа,
+GoCardless Mandate,GoCardless мандат,
+Mandate,Мандат,
+GoCardless Customer,Клиент GoCardless,
+GoCardless Settings,Настройки GoCardless,
+Webhooks Secret,Webhooks Secret,
+Plaid Settings,Настройки Plaid,
 Synchronize all accounts every hour,Синхронизировать все учетные записи каждый час,
 Plaid Client ID,Идентификатор клиента,
-Plaid Secret,Плед секрет,
-Plaid Environment,Плед среды,
+Plaid Secret,Plaid Secret,
+Plaid Environment,Plaid Environment,
 sandbox,песочница,
-development,развитие,
-production,производство,
+development,development,
+production,production,
 QuickBooks Migrator,QuickBooks Migrator,
 Application Settings,Настройки приложения,
 Token Endpoint,Конечная точка маркера,
@@ -6042,15 +6044,15 @@
 Company Settings,Настройки компании,
 Default Shipping Account,Учетная запись по умолчанию,
 Default Warehouse,Склад по умолчанию,
-Default Cost Center,По умолчанию Центр Стоимость,
+Default Cost Center,Центр затрат по умолчанию,
 Undeposited Funds Account,Учет нераспределенных средств,
-Shopify Log,Shopify Вход,
+Shopify Log,Логи Shopify,
 Request Data,Запросить данные,
-Shopify Settings,Изменить настройки,
+Shopify Settings,Shopify настройки,
 status html,статус html,
 Enable Shopify,Включить Shopify,
 App Type,Тип приложения,
-Last Sync Datetime,Последнее время синхронизации,
+Last Sync Datetime,Последняя дата синхронизации,
 Shop URL,URL магазина,
 eg: frappe.myshopify.com,например: frappe.myshopify.com,
 Shared secret,Общий секрет,
@@ -6060,20 +6062,20 @@
 Default Customer,Клиент по умолчанию,
 Customer Group will set to selected group while syncing customers from Shopify,"Группа клиентов настроится на выбранную группу, синхронизируя клиентов с Shopify",
 For Company,Для Компании,
-Cash Account will used for Sales Invoice creation,Денежный счет будет использоваться для создания счета-фактуры,
+Cash Account will used for Sales Invoice creation,Денежный счет будет использоваться для создания счета продажи,
 Update Price from Shopify To ERPNext Price List,Обновить цену от Shopify до прайс-листа ERPNext,
 Default Warehouse to to create Sales Order and Delivery Note,Склад по умолчанию для формирования Сделки и Доставки,
-Sales Order Series,Идентификаторы Сделок,
+Sales Order Series,Идентификаторы сделок,
 Import Delivery Notes from Shopify on Shipment,Импорт уведомлений о доставке из Shopify on Shipment,
 Delivery Note Series,Идентификаторы документов доставки,
-Import Sales Invoice from Shopify if Payment is marked,"Импортировать счет-фактуру продавца, чтобы узнать, отмечен ли платеж",
+Import Sales Invoice from Shopify if Payment is marked,"Импортировать счет продавца, чтобы узнать, отмечен ли платеж",
 Sales Invoice Series,Идентификаторы счетов продаж,
-Shopify Tax Account,Уточнить налоговый счет,
+Shopify Tax Account,Shopify налоговый счет,
 Shopify Tax/Shipping Title,Изменить название налога / доставки,
 ERPNext Account,Учетная запись ERPNext,
 Shopify Webhook Detail,Узнайте подробности веб-камеры,
 Webhook ID,Идентификатор Webhook,
-Tally Migration,Tally Migration,
+Tally Migration,Tally миграция,
 Master Data,Основные данные,
 "Data exported from Tally that consists of the Chart of Accounts, Customers, Suppliers, Addresses, Items and UOMs","Данные, экспортированные из Tally, которые состоят из плана счетов, клиентов, поставщиков, адресов, позиций и единиц измерения",
 Is Master Data Processed,Обработка основных данных,
@@ -6082,7 +6084,7 @@
 Creditors Account set in Tally,Счет кредиторов установлен в Tally,
 Tally Debtors Account,Счет Tally должников,
 Debtors Account set in Tally,Счет дебитора установлен в Tally,
-Tally Company,Талли Компания,
+Tally Company,Tally Компания,
 Company Name as per Imported Tally Data,Название компании согласно импортированным данным подсчета,
 Default UOM,Единица измерения по умолчанию,
 UOM in case unspecified in imported data,"Единицы измерения, если они не указаны в импортированных данных",
@@ -6092,7 +6094,7 @@
 Parties,Стороны,
 UOMs,Единицы измерения,
 Vouchers,Ваучеры,
-Round Off Account,Округление аккаунт,
+Round Off Account,Округлить счет,
 Day Book Data,Данные Дневной Книги,
 Day Book Data exported from Tally that consists of all historic transactions,"Данные дневной книги, экспортированные из Tally, которые включают все исторические транзакции",
 Is Day Book Data Processed,Обработаны ли данные дневника,
@@ -6100,7 +6102,7 @@
 Woocommerce Settings,Настройки Woocommerce,
 Enable Sync,Включить синхронизацию,
 Woocommerce Server URL,URL-адрес сервера Woocommerce,
-Secret,секрет,
+Secret,Секрет,
 API consumer key,Пользовательский ключ API,
 API consumer secret,Секрет потребителя API,
 Tax Account,Налоговый счет,
@@ -6108,7 +6110,7 @@
 Creation User,Создание пользователя,
 "The user that will be used to create Customers, Items and Sales Orders. This user should have the relevant permissions.","Пользователь, который будет использоваться для создания клиентов, товаров и заказов на продажу. Этот пользователь должен иметь соответствующие разрешения.",
 "This warehouse will be used to create Sales Orders. The fallback warehouse is ""Stores"".",Этот склад будет использоваться для создания заказов на продажу. Резервный склад &quot;Магазины&quot;.,
-"The fallback series is ""SO-WOO-"".",Аварийная серия &quot;SO-WOO-&quot;.,
+"The fallback series is ""SO-WOO-"".","Аварийная серия ""SO-WOO-"".",
 This company will be used to create Sales Orders.,Эта компания будет использоваться для создания заказов на продажу.,
 Delivery After (Days),Доставка после (дней),
 This is the default offset (days) for the Delivery Date in Sales Orders. The fallback offset is 7 days from the order placement date.,Это смещение по умолчанию (дни) для даты поставки в заказах на продажу. Смещение отступления составляет 7 дней с даты размещения заказа.,
@@ -6126,7 +6128,7 @@
 Procedure Template,Шаблон процедуры,
 Procedure Prescription,Процедура рецепта,
 Service Unit,Сервисный блок,
-Consumables,расходные материалы,
+Consumables,Расходные материалы,
 Consume Stock,Потребляемый запас,
 Invoice Consumables Separately,Отдельно выставляйте счета на расходные материалы,
 Consumption Invoiced,Счет за потребление,
@@ -6143,14 +6145,14 @@
 Collection Details,Сведения о собрании,
 Change In Item,Изменить элемент,
 Codification Table,Таблица кодирования,
-Complaints,жалобы,
+Complaints,Жалобы,
 Dosage Strength,Дозировка,
 Strength,Прочность,
 Drug Prescription,Рецепт лекарств,
 Drug Name / Description,Название / описание препарата,
-Dosage,дозировка,
+Dosage,Дозировка,
 Dosage by Time Interval,Дозировка по временному интервалу,
-Interval,интервал,
+Interval,Интервал,
 Interval UOM,Интервал UOM,
 Hour,Час,
 Update Schedule,Расписание обновлений,
@@ -6179,10 +6181,10 @@
 Phone (R),Телефон (R),
 Phone (Office),Телефон(офисный),
 Employee and User Details,Сведения о сотруднике и пользователе,
-Hospital,больница,
+Hospital,Больница,
 Appointments,Назначения,
 Practitioner Schedules,Расписание практикующих,
-Charges,расходы,
+Charges,Расходы,
 Out Patient Consulting Charge,Плата за консультацию пациента,
 Default Currency,Базовая валюта,
 Healthcare Schedule Time Slot,Расписание занятий,
@@ -6193,7 +6195,7 @@
 Inpatient Occupancy,Стационарное размещение,
 Occupancy Status,Статус занятости,
 Vacant,Вакантно,
-Occupied,занятый,
+Occupied,Занято,
 Item Details,Детальная информация о товаре,
 UOM Conversion in Hours,Преобразование UOM в часы,
 Rate / UOM,Скорость / UOM,
@@ -6217,7 +6219,7 @@
 Default Healthcare Service Items,Предметы медицинского обслуживания по умолчанию,
 "You can configure default Items for billing consultation charges, procedure consumption items and inpatient visits","Вы можете настроить элементы по умолчанию для выставления счетов за консультации, элементы потребления процедур и посещения стационара.",
 Clinical Procedure Consumable Item,Расходный материал для клинической процедуры,
-Default Accounts,Учетные записи по умолчанию,
+Default Accounts,Счета по умолчанию,
 Default income accounts to be used if not set in Healthcare Practitioner to book Appointment charges.,"Учетные записи будут использоваться по умолчанию, если они не установлены в Практикуме здравоохранения, чтобы забронировать плату за назначение.",
 Default receivable accounts to be used to book Appointment charges.,"Счета дебиторской задолженности по умолчанию, которые будут использоваться для учета расходов на встречу.",
 Out Patient SMS Alerts,Оповещения SMS для пациентов,
@@ -6230,8 +6232,8 @@
 Reminder Message,Сообщение напоминания,
 Remind Before,Напомнить,
 Laboratory Settings,Настройки лаборатории,
-Create Lab Test(s) on Sales Invoice Submission,Создание лабораторных тестов для отправки счета-фактуры,
-Checking this will create Lab Test(s) specified in the Sales Invoice on submission.,"При установке этого флажка будут созданы лабораторные тесты, указанные в счете-фактуре продажи при отправке.",
+Create Lab Test(s) on Sales Invoice Submission,Создание лабораторных тестов на основе счета продажи,
+Checking this will create Lab Test(s) specified in the Sales Invoice on submission.,"При установке этого флажка будут созданы лабораторные тесты, указанные в счете продажи при отправке.",
 Create Sample Collection document for Lab Test,Создать образец документа для лабораторного тестирования,
 Checking this will create a Sample Collection document  every time you create a Lab Test,"При проверке этого будет создаваться документ Sample Collection каждый раз, когда вы создаете лабораторный тест.",
 Employee name and designation in print,Имя и обозначение сотрудника в печати,
@@ -6258,7 +6260,7 @@
 Discharge Scheduled,Расписание выписок,
 Discharged,Выписанный,
 Admission Schedule Date,Дата приема,
-Admitted Datetime,Допустимое значение Datetime,
+Admitted Datetime,Допустимое значение даты и времени,
 Expected Discharge,Ожидаемый выпуск,
 Discharge Date,Дата выписки,
 Lab Prescription,Лабораторный рецепт,
@@ -6296,10 +6298,10 @@
 Marital Status,Семейное положение,
 Married,Замужем,
 Divorced,Разведенный,
-Widow,вдова,
+Widow,Вдова,
 Patient Relation,Отношение пациентов,
 "Allergies, Medical and Surgical History","Аллергии, медицинская и хирургическая история",
-Allergies,аллергии,
+Allergies,Аллергии,
 Medication,медикаментозное лечение,
 Medical History,История болезни,
 Surgical History,Хирургическая история,
@@ -6315,10 +6317,10 @@
 Get Prescribed Therapies,Получите прописанную терапию,
 Appointment Datetime,Дата и время встречи,
 Duration (In Minutes),Продолжительность (в минутах),
-Reference Sales Invoice,Ссылка на счет-фактуру,
+Reference Sales Invoice,Ссылка на счет продажи,
 More Info,Подробнее,
 Referring Practitioner,Справляющий практик,
-Reminded,напомнил,
+Reminded,Напомнил,
 HLC-PA-.YYYY.-,HLC-PA-.YYYY.-,
 Assessment Template,Шаблон оценки,
 Assessment Datetime,Дата и время оценки,
@@ -6333,18 +6335,18 @@
 Patient Assessment Sheet,Лист оценки пациента,
 Patient Assessment Template,Шаблон оценки пациента,
 Assessment Parameters,Параметры оценки,
-Parameters,параметры,
+Parameters,Параметры,
 Assessment Scale,Шкала оценки,
 Scale Minimum,Минимальный масштаб,
 Scale Maximum,Максимальный масштаб,
 HLC-ENC-.YYYY.-,HLC-ENC-.YYYY.-,
 Encounter Date,Дата встречи,
 Encounter Time,Время встречи,
-Encounter Impression,Впечатление от Encounter,
+Encounter Impression,Впечатление от встречи,
 Symptoms,Симптомы,
 In print,В печати,
 Medical Coding,Медицинское кодирование,
-Procedures,процедуры,
+Procedures,Поцедуры,
 Therapies,Терапии,
 Review Details,Обзорная информация,
 Patient Encounter Diagnosis,Диагностика встречи с пациентом,
@@ -6352,8 +6354,8 @@
 HLC-PMR-.YYYY.-,HLC-PMR-.YYYY.-,
 Attach Medical Record,Прикрепите медицинскую карту,
 Reference DocType,Ссылка DocType,
-Spouse,супруга,
-Family,семья,
+Spouse,Супруга,
+Family,Семья,
 Schedule Details,Детали расписания,
 Schedule Name,Название расписания,
 Time Slots,Временные интервалы,
@@ -6386,13 +6388,13 @@
 Coated,Покрытый,
 Very Coated,Очень хорошо,
 Normal,Нормальный,
-Furry,пушистый,
+Furry,Пушистый,
 Cuts,Порезы,
 Abdomen,Брюшная полость,
 Bloated,Раздутый,
-Fluid,жидкость,
+Fluid,Жидкость,
 Constipated,Запор,
-Reflexes,рефлексы,
+Reflexes,Рефлексы,
 Hyper,Hyper,
 Very Hyper,Очень Hyper,
 One Sided,Односторонняя,
@@ -6419,7 +6421,7 @@
 Hotel Room Reservation,Бронирование номеров в гостинице,
 Guest Name,Имя гостя,
 Late Checkin,Поздняя регистрация,
-Booked,бронирования,
+Booked,Забронировано,
 Hotel Reservation User,Бронирование отеля,
 Hotel Room Reservation Item,Бронирование номера в гостинице,
 Hotel Settings,Отель,
@@ -6427,13 +6429,13 @@
 Default Invoice Naming Series,Идентификаторы по имени для счета  по умолчанию,
 Additional Salary,Дополнительная зарплата,
 HR,HR,
-HR-ADS-.YY.-.MM.-,HR-ADS-.YY .-. MM.-,
-Salary Component,Зарплата Компонент,
+HR-ADS-.YY.-.MM.-,HR-ADS-.YY.-.MM.-,
+Salary Component,Компонент зарплаты,
 Overwrite Salary Structure Amount,Перезаписать сумму заработной платы,
 Deduct Full Tax on Selected Payroll Date,Вычесть полный налог на выбранную дату расчета,
 Payroll Date,Дата расчета заработной платы,
 Date on which this component is applied,Дата применения этого компонента,
-Salary Slip,Зарплата скольжения,
+Salary Slip,Справка о заработной плате,
 Salary Component Type,Тип залогового имущества,
 HR User,Сотрудник отдела кадров,
 Appointment Letter,Назначение письмо,
@@ -6441,7 +6443,7 @@
 Applicant Name,Имя заявителя,
 Appointment Date,Назначенная дата,
 Appointment Letter Template,Шаблон письма о назначении,
-Body,тело,
+Body,Содержимое,
 Closing Notes,Заметки,
 Appointment Letter content,Письмо о назначении,
 Appraisal,Оценка,
@@ -6451,35 +6453,35 @@
 Goals,Цели,
 Total Score (Out of 5),Всего рейтинг (из 5),
 "Any other remarks, noteworthy effort that should go in the records.","Любые другие замечания, отметить усилия, которые должны идти в записях.",
-Appraisal Goal,Оценка Гол,
-Key Responsibility Area,Ключ Ответственность Площадь,
-Weightage (%),Weightage (%),
+Appraisal Goal,Цель оценки,
+Key Responsibility Area,Основная зона ответственности,
+Weightage (%),Весовая нагрузка (%),
 Score (0-5),Оценка (0-5),
-Score Earned,Оценка Заработано,
-Appraisal Template Title,Оценка шаблона Название,
-Appraisal Template Goal,Оценка шаблона Гол,
+Score Earned,Оценка получена,
+Appraisal Template Title,Название шаблона оценки,
+Appraisal Template Goal,Цель шаблона оценки,
 KRA,КРА,
-Key Performance Area,Ключ Площадь Производительность,
+Key Performance Area,Ключевая область производительности,
 HR-ATT-.YYYY.-,HR-ATT-.YYYY.-,
-On Leave,в отпуске,
+On Leave,В отпуске,
 Work From Home,Работа из дома,
 Leave Application,Заявление на отпуск,
-Attendance Date,Посещаемость Дата,
+Attendance Date,Дата посещения,
 Attendance Request,Запрос на посещение,
-Late Entry,Поздний вход,
-Early Exit,Ранний выход,
+Late Entry,Позднее начало,
+Early Exit,Ранний уход,
 Half Day Date,Полдня Дата,
 On Duty,На службе,
-Explanation,объяснение,
+Explanation,Объяснение,
 Compensatory Leave Request,Компенсационный отпуск,
 Leave Allocation,Распределение отпусков,
 Worked On Holiday,Работал на отдыхе,
 Work From Date,Работа с даты,
 Work End Date,Дата окончания работы,
-Email Sent To,Е-мейл отправлен,
+Email Sent To,Email отправлен,
 Select Users,Выберите пользователей,
 Send Emails At,Отправить электронные письма на,
-Reminder,напоминание,
+Reminder,Напоминание,
 Daily Work Summary Group User,Ежедневная рабочая группа,
 email,Эл. адрес,
 Parent Department,Родительский отдел,
@@ -6495,13 +6497,13 @@
 Skill,Умение,
 Driver,Водитель,
 HR-DRI-.YYYY.-,HR-DRI-.YYYY.-,
-Suspended,подвешенный,
+Suspended,Приостановленный,
 Transporter,Транспортер,
-Applicable for external driver,Применимо для внешнего драйвера,
-Cellphone Number,номер мобильного телефона,
+Applicable for external driver,Применимо для внештатного курьера,
+Cellphone Number,Номер мобильного телефона,
 License Details,Сведения о лицензии,
 License Number,Лицензионный номер,
-Issuing Date,Дата выпуска ценных бумаг,
+Issuing Date,Дата получения,
 Driving License Categories,Категории водительских прав,
 Driving License Category,Категория водительских удостоверений,
 Fleet Manager,Менеджер автопарка,
@@ -6509,7 +6511,7 @@
 HR-EMP-,HR-EMP-,
 Employment Type,Вид занятости,
 Emergency Contact,Экстренная связь,
-Emergency Contact Name,контакт для чрезвычайных ситуаций,
+Emergency Contact Name,Контакт для чрезвычайных ситуаций,
 Emergency Phone,В случае чрезвычайных ситуаций,
 ERPNext User,Пользователь ERPNext,
 "System User (login) ID. If set, it will become default for all HR forms.","Система Пользователь (Войти) ID. Если установлено, то это станет по умолчанию для всех форм HR.",
@@ -6518,18 +6520,18 @@
 Joining Details,Детали соединения,
 Offer Date,Дата предложения,
 Confirmation Date,Дата подтверждения,
-Contract End Date,Конец контракта Дата,
+Contract End Date,Дата окончания контракта,
 Notice (days),Уведомление (дней),
 Date Of Retirement,Дата выбытия,
 Department and Grade,Отдел и класс,
 Reports to,Доклады,
 Attendance and Leave Details,Посещаемость и детали отпуска,
-Leave Policy,Оставить политику,
+Leave Policy,Политика отпусков,
 Attendance Device ID (Biometric/RF tag ID),Идентификатор устройства посещаемости (биометрический идентификатор / идентификатор радиочастотной метки),
 Applicable Holiday List,Применимо Список праздников,
-Default Shift,Сдвиг по умолчанию,
+Default Shift,Смена по умолчанию,
 Salary Details,Сведения о зарплате,
-Salary Mode,Режим Зарплата,
+Salary Mode,Способ оплаты,
 Bank A/C No.,Банк Сч/Тек №,
 Health Insurance,Медицинская страховка,
 Health Insurance Provider,Поставщик медицинского страхования,
@@ -6538,7 +6540,7 @@
 Personal Email,Личная электронная почта,
 Permanent Address Is,Постоянный адрес Является,
 Rented,Арендованный,
-Owned,Присвоено,
+Owned,Собственность,
 Permanent Address,Постоянный адрес,
 Prefered Contact Email,Предпочитаемый контактный адрес электронной почты,
 Company Email,Электронная почта компании,
@@ -6549,23 +6551,23 @@
 Bio / Cover Letter,Био / сопроводительное письмо,
 Short biography for website and other publications.,Краткая биография для веб-сайта и других изданий.,
 Passport Number,Номер паспорта,
-Date of Issue,Дата вопроса,
-Place of Issue,Место вопроса,
-Widowed,Овдовевший,
-Family Background,Семья Фон,
+Date of Issue,Дата выдачи,
+Place of Issue,Место выдачи,
+Widowed,Овдовевший(ая),
+Family Background,О семье,
 "Here you can maintain family details like name and occupation of parent, spouse and children","Здесь Вы можете сохранить описание семьи — имя и профессия родителей, супруга и детей",
 Health Details,Подробности здоровья,
 "Here you can maintain height, weight, allergies, medical concerns etc","Здесь вы можете записывать рост, вес, аллергии, медицинские проблемы и т. п.",
 Educational Qualification,Образовательный ценз,
 Previous Work Experience,Предыдущий опыт работы,
-External Work History,Внешний Работа История,
+External Work History,Внешний история работы,
 History In Company,История в компании,
-Internal Work History,Внутренняя история Работа,
-Resignation Letter Date,Отставка Письмо Дата,
-Relieving Date,Освобождение Дата,
+Internal Work History,Внутренняя история работ,
+Resignation Letter Date,Дата заявления об уходе,
+Relieving Date,Дата увольнения,
 Reason for Leaving,Причина увольнения,
 Leave Encashed?,Оставьте инкассированы?,
-Encashment Date,Инкассация Дата,
+Encashment Date,Дата инкассации,
 New Workplace,Новый рабочий участок,
 HR-EAD-.YYYY.-,HR-EAD-.YYYY.-,
 Returned Amount,Возвращенная сумма,
@@ -6602,10 +6604,10 @@
 OUT,ИЗ,
 Location / Device ID,Расположение / ID устройства,
 Skip Auto Attendance,Пропустить автоматическое посещение,
-Shift Start,Shift Start,
-Shift End,Shift End,
-Shift Actual Start,Сдвиг фактического начала,
-Shift Actual End,Сдвиг фактического конца,
+Shift Start,Начало смены,
+Shift End,Конец смены,
+Shift Actual Start,Фактическое начало смены,
+Shift Actual End,Фактическое окончание смены,
 Employee Education,Сотрудник Образование,
 School/University,Школа / университет,
 Graduate,Выпускник,
@@ -6614,7 +6616,7 @@
 Year of Passing,Год прохождения,
 Class / Percentage,Класс / в процентах,
 Major/Optional Subjects,Основные / факультативных предметов,
-Employee External Work History,Сотрудник Внешний Работа История,
+Employee External Work History,История внешней работы сотрудника,
 Total Experience,Суммарный опыт,
 Default Leave Policy,Политика по умолчанию,
 Default Salary Structure,Структура заработной платы по умолчанию,
@@ -6628,7 +6630,7 @@
 Employee Onboarding,Сотрудник по бортовому,
 Notify users by email,Уведомить пользователей по электронной почте,
 Employee Onboarding Template,Шаблон рабочего стола,
-Activities,мероприятия,
+Activities,Мероприятия,
 Employee Onboarding Activity,Деятельность бортового персонала,
 Employee Other Income,Другой доход сотрудника,
 Employee Promotion,Продвижение сотрудников,
@@ -6640,7 +6642,7 @@
 Employee Separation Template,Шаблон разделения сотрудников,
 Exit Interview Summary,Выйти из интервью,
 Employee Skill,Навыки сотрудников,
-Proficiency,умение,
+Proficiency,Умения,
 Evaluation Date,Дата оценки,
 Employee Skill Map,Карта навыков сотрудников,
 Employee Skills,Навыки сотрудников,
@@ -6678,30 +6680,30 @@
 Employee Transfer Property,Свойство переноса персонала,
 HR-EXP-.YYYY.-,HR-EXP-.YYYY.-,
 Expense Taxes and Charges,Расходные налоги и сборы,
-Total Sanctioned Amount,Всего Санкционированный Количество,
+Total Sanctioned Amount,Общая санкционированная сумма,
 Total Advance Amount,Общая сумма аванса,
 Total Claimed Amount,Всего заявленной суммы,
 Total Amount Reimbursed,Общая сумма возмещаются,
-Vehicle Log,Автомобиль Вход,
-Employees Email Id,Идентификаторы Электронных почт сотрудников,
+Vehicle Log,Журнал автомобиля,
+Employees Email Id,Идентификаторы почты сотрудников,
 More Details,Подробнее,
-Expense Claim Account,Счет Авансового Отчета,
-Expense Claim Advance,Претензия на увеличение расходов,
+Expense Claim Account,Счет требований о расходах,
+Expense Claim Advance,Предварительное требование о возмещении расходов,
 Unclaimed amount,Невостребованная сумма,
 Expense Claim Detail,Детали Авансового Отчета,
 Expense Date,Дата расхода,
-Expense Claim Type,Тип Авансового Отчета,
+Expense Claim Type,Тип требования о расходах,
 Holiday List Name,Название списка выходных,
 Total Holidays,Всего праздников,
 Add Weekly Holidays,Добавить еженедельные каникулы,
-Weekly Off,Еженедельный Выкл,
+Weekly Off,Еженедельный выходной,
 Add to Holidays,Добавить в праздники,
 Holidays,Праздники,
 Clear Table,Очистить таблицу,
 HR Settings,Настройки HR,
-Employee Settings,Работники Настройки,
+Employee Settings,Настройки сотрудников,
 Retirement Age,Пенсионный возраст,
-Enter retirement age in years,Введите возраст выхода на пенсию в ближайшие годы,
+Enter retirement age in years,Введите возраст выхода на пенсию,
 Stop Birthday Reminders,Стоп День рождения Напоминания,
 Expense Approver Mandatory In Expense Claim,"Утверждение о расходах, обязательный для покрытия расходов",
 Payroll Settings,Настройки по заработной плате,
@@ -6717,13 +6719,13 @@
 "The salary slip emailed to the employee will be password protected, the password will be generated based on the password policy.","Письмо с зарплатой сотруднику будет защищено паролем, пароль будет сгенерирован на основе политики паролей.",
 Password Policy,Политика паролей,
 <b>Example:</b> SAL-{first_name}-{date_of_birth.year} <br>This will generate a password like SAL-Jane-1972,<b>Пример:</b> SAL- {first_name} - {date_of_birth.year} <br> Это сгенерирует пароль как SAL-Jane-1972,
-Leave Settings,Оставить настройки,
-Leave Approval Notification Template,Оставить шаблон уведомления об уведомлении,
-Leave Status Notification Template,Оставить шаблон уведомления о состоянии,
+Leave Settings,Настройки отпуска,
+Leave Approval Notification Template,Шаблон уведомления об утверждении отпуска,
+Leave Status Notification Template,Шаблон уведомления о состоянии отпуска,
 Role Allowed to Create Backdated Leave Application,"Роль, разрешенная для создания приложения с задним сроком выхода",
-Leave Approver Mandatory In Leave Application,Утвердить заявление на отпуск,
-Show Leaves Of All Department Members In Calendar,Показать листы всех членов Департамента в календаре,
-Auto Leave Encashment,Авто оставить инкассо,
+Leave Approver Mandatory In Leave Application,Утверждать заявление на отпуск,
+Show Leaves Of All Department Members In Calendar,Показать отпуска всех сотрудников отдела в календаре,
+Auto Leave Encashment,Автоматический выход из инкассации,
 Hiring Settings,Настройки найма,
 Check Vacancies On Job Offer Creation,Проверьте вакансии на создание предложения о работе,
 Identification Document Type,Тип идентификационного документа,
@@ -6747,7 +6749,7 @@
 Awaiting Response,В ожидании ответа,
 Job Offer Terms,Условия работы,
 Select Terms and Conditions,Выберите Сроки и условия,
-Printing Details,Печатать Подробности,
+Printing Details,Подробности печати,
 Job Offer Term,Срок действия предложения,
 Offer Term,Условие предложения,
 Value / Description,Значение / Описание,
@@ -6757,7 +6759,7 @@
 Planned number of Positions,Планируемое количество позиций,
 "Job profile, qualifications required etc.","Профиль работы, необходимая квалификация и т.д.",
 HR-LAL-.YYYY.-,HR-LAL-.YYYY.-,
-Allocation,распределение,
+Allocation,Распределение,
 New Leaves Allocated,Новые листья Выделенные,
 Add unused leaves from previous allocations,Добавить неиспользованные отпуска с прошлых периодов,
 Unused leaves,Неиспользованные листья,
@@ -6785,15 +6787,15 @@
 Allow User,Разрешить пользователю,
 Leave Block List Date,Оставьте Блок-лист Дата,
 Block Date,Блок Дата,
-Leave Control Panel,Оставьте панели управления,
-Select Employees,Выберите Сотрудников,
+Leave Control Panel,Выйти из панели управления,
+Select Employees,Выберите сотрудников,
 Employment Type (optional),Тип занятости (необязательно),
 Branch (optional),Филиал (необязательно),
 Department (optional),Отдел (необязательно),
 Designation (optional),Обозначение (необязательно),
 Employee Grade (optional),Оценка сотрудника (необязательно),
 Employee (optional),Сотрудник (необязательно),
-Allocate Leaves,Выделить листья,
+Allocate Leaves,Распределить записи,
 Carry Forward,Переносить,
 Please select Carry Forward if you also want to include previous fiscal year's balance leaves to this fiscal year,"Пожалуйста, выберите переносить, если вы также хотите включить баланс предыдущего финансового года оставляет в этом финансовом году",
 New Leaves Allocated (In Days),Новые листья Выделенные (в днях),
@@ -6805,13 +6807,13 @@
 Transaction Name,Название транзакции,
 Is Carry Forward,Является ли переносить,
 Is Expired,Истек,
-Is Leave Without Pay,Является отпуск без,
+Is Leave Without Pay,Отпуск без содержания,
 Holiday List for Optional Leave,Список праздников для дополнительного отпуска,
 Leave Allocations,Оставить выделение,
 Leave Policy Details,Оставьте сведения о политике,
 Leave Policy Detail,Оставить информацию о политике,
 Annual Allocation,Ежегодное распределение,
-Leave Type Name,Оставьте Тип Название,
+Leave Type Name,Оставить имя типа,
 Max Leaves Allowed,Максимальные листья разрешены,
 Applicable After (Working Days),Применимые после (рабочие дни),
 Maximum Continuous Days Applicable,Максимальные непрерывные дни,
@@ -6822,28 +6824,28 @@
 Maximum Carry Forwarded Leaves,Максимум несут переадресованные листья,
 Expire Carry Forwarded Leaves (Days),Expire Carry Forwarded Leaves (Days),
 Calculated in days,Рассчитано в днях,
-Encashment,инкассация,
+Encashment,Инкассация,
 Allow Encashment,Разрешить инкассацию,
 Encashment Threshold Days,Дни порога инкассации,
 Earned Leave,Заработано,
 Is Earned Leave,Заработано,
 Earned Leave Frequency,Заработок,
-Rounding,округление,
-Payroll Employee Detail,Сведения о сотрудниках по расчетам,
-Payroll Frequency,Расчет заработной платы Частота,
+Rounding,Округление,
+Payroll Employee Detail,Сведения о сотруднике по заработной плате,
+Payroll Frequency,Частота расчета заработной платы,
 Fortnightly,раз в две недели,
 Bimonthly,Раз в два месяца,
 Employees,Сотрудники,
 Number Of Employees,Количество работников,
 Employee Details,Сотрудник Подробнее,
 Validate Attendance,Подтвердить посещаемость,
-Salary Slip Based on Timesheet,Зарплата скольжения на основе Timesheet,
+Salary Slip Based on Timesheet,Зарплатная ведомость на основе карточки учета рабочего времени,
 Select Payroll Period,Выберите Период начисления заработной платы,
 Deduct Tax For Unclaimed Employee Benefits,Вычет налога для невостребованных сотрудников,
 Deduct Tax For Unsubmitted Tax Exemption Proof,Доход от вычета налога за отказ в освобождении от налогов,
 Select Payment Account to make Bank Entry,Выберите Учетная запись Оплата сделать Банк Стажер,
-Salary Slips Created,Созданы зарплатные слайды,
-Salary Slips Submitted,Заявки на зарплату,
+Salary Slips Created,Зарплатные ведомости созданы,
+Salary Slips Submitted,Утвержденные зарплатные ведомости,
 Payroll Periods,Периоды начисления заработной платы,
 Payroll Period Date,Дата периода расчета заработной платы,
 Purpose of Travel,Цель поездки,
@@ -6876,7 +6878,7 @@
 Tax on additional salary,Налог на дополнительную зарплату,
 Salary Structure,Зарплата Структура,
 Working Days,В рабочие дни,
-Salary Slip Timesheet,Зарплата скольжению Timesheet,
+Salary Slip Timesheet,Табель учета рабочего времени,
 Total Working Hours,Всего часов работы,
 Hour Rate,Часовой разряд,
 Bank Account No.,Счет №,
@@ -6891,16 +6893,16 @@
 net pay info,Чистая информация платить,
 Gross Pay - Total Deduction - Loan Repayment,Gross Pay - Итого Вычет - Погашение кредита,
 Total in words,Всего в словах,
-Net Pay (in words) will be visible once you save the Salary Slip.,"Чистая Платное (прописью) будут видны, как только вы сохраните Зарплата Слип.",
-Salary Component for timesheet based payroll.,Заработная плата Компонент для расчета заработной платы на основе расписания.,
+Net Pay (in words) will be visible once you save the Salary Slip.,"Чистая плата (прописью) будет видна, как только вы сохраните зарплатную ведомость.",
+Salary Component for timesheet based payroll.,Компонент заработной платы для расчета зарплаты на основе расписания.,
 Leave Encashment Amount Per Day,Оставить количество инкассации в день,
 Max Benefits (Amount),Максимальные выгоды (сумма),
 Salary breakup based on Earning and Deduction.,Зарплата распада на основе Заработок и дедукции.,
-Total Earning,Всего Заработок,
+Total Earning,Всего заработок,
 Salary Structure Assignment,Назначение структуры заработной платы,
-Shift Assignment,Назначение сдвига,
-Shift Type,Тип сдвига,
-Shift Request,Запрос на сдвиг,
+Shift Assignment,Назначение смены,
+Shift Type,Тип смены,
+Shift Request,Запрос на смену,
 Enable Auto Attendance,Включить автоматическое посещение,
 Mark attendance based on 'Employee Checkin' for Employees assigned to this shift.,"Отметьте посещаемость на основе «Проверка сотрудников» для сотрудников, назначенных для этой смены.",
 Auto Attendance Settings,Настройки автоматической посещаемости,
@@ -6908,8 +6910,8 @@
 Alternating entries as IN and OUT during the same shift,Чередование записей как IN и OUT в течение одной смены,
 Strictly based on Log Type in Employee Checkin,Строго на основе типа журнала в регистрации сотрудников,
 Working Hours Calculation Based On,Расчет рабочего времени на основе,
-First Check-in and Last Check-out,Первый заезд и Последний выезд,
-Every Valid Check-in and Check-out,Каждый действительный заезд и выезд,
+First Check-in and Last Check-out,Первый заезд и последний выезд,
+Every Valid Check-in and Check-out,Каждый проверенный заезд и выезд,
 Begin check-in before shift start time (in minutes),Начать регистрацию до начала смены (в минутах),
 The time before the shift start time during which Employee Check-in is considered for attendance.,"Время до начала смены, в течение которого регистрация сотрудников рассматривается для посещения.",
 Allow check-out after shift end time (in minutes),Разрешить выезд после окончания смены (в минутах),
@@ -6917,7 +6919,7 @@
 Working Hours Threshold for Half Day,Порог рабочего времени на полдня,
 Working hours below which Half Day is marked. (Zero to disable),"Рабочее время, ниже которого отмечается полдня. (Ноль отключить)",
 Working Hours Threshold for Absent,Порог рабочего времени для отсутствующих,
-Working hours below which Absent is marked. (Zero to disable),"Рабочее время, ниже которого отмечается отсутствие. (Ноль отключить)",
+"Working hours below which Absent is marked. (Zero to disable)","Порог рабочего времени, ниже которого устанавливается отметка «Отсутствует». (Ноль для отключения)",
 Process Attendance After,Посещаемость процесса после,
 Attendance will be marked automatically only after this date.,Посещаемость будет отмечена автоматически только после этой даты.,
 Last Sync of Checkin,Последняя синхронизация регистрации,
@@ -6947,20 +6949,20 @@
 Event Status,Состояние события,
 Has Certificate,Имеет сертификат,
 Seminar,Семинар,
-Theory,теория,
-Workshop,мастерская,
+Theory,Теория,
+Workshop,Мастерская,
 Conference,Конференция,
 Exam,Экзамен,
-Internet,интернет,
+Internet,Интернет,
 Self-Study,Самообучения,
-Advance,авансировать,
+Advance,Аванс,
 Trainer Name,Имя тренера,
-Trainer Email,Электронная почта Тренера,
+Trainer Email,Электронная почта тренера,
 Attendees,Присутствующие,
 Employee Emails,Электронные почты сотрудников,
 Training Event Employee,Обучение сотрудников Событие,
 Invited,приглашенный,
-Feedback Submitted,Обратная связь Представлено,
+Feedback Submitted,Отзыв отправлен,
 Optional,Необязательный,
 Training Result Employee,Результат обучения сотрудника,
 Travel Itinerary,Маршрут путешествия,
@@ -6972,7 +6974,7 @@
 Taxi,Такси,
 Rented Car,Прокат автомобилей,
 Meal Preference,Предпочитаемая еда,
-Vegetarian,вегетарианец,
+Vegetarian,Вегетарианец,
 Non-Vegetarian,Не вегетарианский,
 Gluten Free,Не содержит глютен,
 Non Diary,Не дневник,
@@ -6983,15 +6985,15 @@
 Preferred Area for Lodging,Предпочтительная зона для проживания,
 Check-in Date,Дата заезда,
 Check-out Date,Проверить дату,
-Travel Request,Запрос на поездку,
-Travel Type,Тип путешествия,
-Domestic,внутренний,
-International,Международный,
-Travel Funding,Финансирование путешествия,
-Require Full Funding,Требовать полного финансирования,
-Fully Sponsored,Полностью спонсируемый,
+Travel Request,Запрос на командировку,
+Travel Type,Тип командировки,
+Domestic,Внутренняя,
+International,Международная,
+Travel Funding,Финансирование командировки,
+Require Full Funding,Требуется полное финансирование,
+Fully Sponsored,Полностью спонсируемое,
 "Partially Sponsored, Require Partial Funding","Частично спонсируется, требует частичного финансирования",
-Copy of Invitation/Announcement,Копия приглашения / объявление,
+Copy of Invitation/Announcement,Копия приглашения / объявления,
 "Details of Sponsor (Name, Location)","Подробная информация о спонсоре (название, местоположение)",
 Identification Document Number,Идентификационный номер документа,
 Any other details,Любые другие детали,
@@ -7014,29 +7016,29 @@
 License Plate,Номерной знак,
 Odometer Value (Last),Значение одометра (последнее),
 Acquisition Date,Дата приобретения,
-Chassis No,Шасси Нет,
+Chassis No,VIN код,
 Vehicle Value,Значение автомобиля,
 Insurance Details,Страхование Подробнее,
 Insurance Company,Страховая компания,
-Policy No,Политика Нет,
+Policy No,Полис №,
 Additional Details,дополнительные детали,
 Fuel Type,Тип топлива,
 Petrol,Бензин,
-Diesel,дизель,
+Diesel,Дизель,
 Natural Gas,Природный газ,
-Electric,электрический,
+Electric,Электрический,
 Fuel UOM,Топливо UOM,
-Last Carbon Check,Последний Carbon Проверить,
+Last Carbon Check,Последняя проверка на углерод,
 Wheels,Колеса,
-Doors,двери,
-HR-VLOG-.YYYY.-,HR-Видеоблог-.YYYY.-,
+Doors,Двери,
+HR-VLOG-.YYYY.-,HR-VLOG-.YYYY.-,
 Odometer Reading,Показания одометра,
 Current Odometer value ,Текущее значение одометра,
-last Odometer Value ,Значение последнего одометра,
+last Odometer Value ,последнее значение одометра,
 Refuelling Details,Заправочные Подробнее,
-Invoice Ref,Счет-фактура Ссылка,
+Invoice Ref,Счет ссылка,
 Service Details,Сведения о службе,
-Service Detail,Деталь обслуживания,
+Service Detail,Сведения об услуге,
 Vehicle Service,Обслуживание автомобиля,
 Service Item,Продукт-услуга,
 Brake Oil,Тормозные масла,
@@ -7044,8 +7046,8 @@
 Clutch Plate,Диск сцепления,
 Engine Oil,Машинное масло,
 Oil Change,Замена масла,
-Inspection,осмотр,
-Mileage,пробег,
+Inspection,Осмотр,
+Mileage,Пробег,
 Hub Tracked Item,Отслеживаемый элемент концентратора,
 Hub Node,Узел хаба,
 Image List,Список изображений,
@@ -7098,7 +7100,7 @@
 Total Payable Interest,Общая задолженность по процентам,
 Against Loan ,Против ссуды,
 Loan Interest Accrual,Начисление процентов по кредитам,
-Amounts,суммы,
+Amounts,Суммы,
 Pending Principal Amount,Ожидающая основная сумма,
 Payable Principal Amount,Основная сумма к оплате,
 Paid Principal Amount,Выплаченная основная сумма,
@@ -7129,11 +7131,11 @@
 Shortfall Time,Время нехватки,
 America/New_York,Америка / Триатлон,
 Shortfall Amount,Сумма дефицита,
-Security Value ,Значение безопасности,
+Security Value ,Значение безопасности ,
 Process Loan Security Shortfall,Недостаток безопасности процесса займа,
 Loan To Value Ratio,Соотношение займа к стоимости,
 Unpledge Time,Время невыплаты,
-Loan Name,Кредит Имя,
+Loan Name,Название кредита,
 Rate of Interest (%) Yearly,Процентная ставка (%) Годовой,
 Penalty Interest Rate (%) Per Day,Процентная ставка штрафа (%) в день,
 Penalty Interest Rate is levied on the pending interest amount on a daily basis in case of delayed repayment ,Штрафная процентная ставка взимается на сумму отложенного процента на ежедневной основе в случае задержки выплаты,
@@ -7147,11 +7149,11 @@
 Total Payment,Всего к оплате,
 Balance Loan Amount,Баланс Сумма кредита,
 Is Accrued,Начислено,
-Salary Slip Loan,Зарплатный ссудный кредит,
+Salary Slip Loan,Ссуда на зарплату,
 Loan Repayment Entry,Запись о погашении кредита,
 Sanctioned Loan Amount,Санкционированная сумма кредита,
 Sanctioned Amount Limit,Утвержденный лимит суммы,
-Unpledge,Unpledge,
+Unpledge,Отменить залог,
 Haircut,Стрижка волос,
 MAT-MSH-.YYYY.-,MAT-MSH-.YYYY.-,
 Generate Schedule,Создать расписание,
@@ -7164,7 +7166,7 @@
 No of Visits,Кол-во посещений,
 MAT-MVS-.YYYY.-,MAT-MVS-.YYYY.-,
 Maintenance Date,Дата технического обслуживания,
-Maintenance Time,Техническое обслуживание Время,
+Maintenance Time,Время технического обслуживания,
 Completion Status,Статус завершения,
 Partially Completed,Частично завершено,
 Fully Completed,Полностью завершен,
@@ -7172,38 +7174,38 @@
 Breakdown,Разбивка,
 Purposes,Цели,
 Customer Feedback,Обратная связь с клиентами,
-Maintenance Visit Purpose,Техническое обслуживание Посетить Цель,
-Work Done,Сделано,
+Maintenance Visit Purpose,Цель технического обслуживания,
+Work Done,Работа выполнена,
 Against Document No,Против Документ №,
-Against Document Detail No,Против деталях документа Нет,
+Against Document Detail No,Против деталях документа №,
 MFG-BLR-.YYYY.-,MFG-BLR-.YYYY.-,
 Order Type,Тип заказа,
 Blanket Order Item,Элемент заказа одеяла,
 Ordered Quantity,Заказанное количество,
 Item to be manufactured or repacked,Продукт должен быть произведен или переупакован,
 Quantity of item obtained after manufacturing / repacking from given quantities of raw materials,Количество пункта получены после изготовления / переупаковка от заданных величин сырья,
-Set rate of sub-assembly item based on BOM,Установить скорость элемента подзаголовки на основе спецификации,
+Set rate of sub-assembly item based on BOM,Установить скорость сборки на основе спецификации,
 Allow Alternative Item,Разрешить альтернативный элемент,
 Item UOM,Единиц продукта,
 Conversion Rate,Коэффициент конверсии,
 Rate Of Materials Based On,Оценить материалов на основе,
-With Operations,С операций,
+With Operations,С операциями,
 Manage cost of operations,Управление стоимостью операций,
 Transfer Material Against,Передача материала против,
 Routing,Маршрутизация,
 Materials,Материалы,
 Quality Inspection Required,Требуется проверка качества,
-Quality Inspection Template,Качественный контрольный шаблон,
-Scrap,лом,
+Quality Inspection Template,Шаблон контроля качества,
+Scrap,Брак,
 Scrap Items,Утилизированные продукты,
 Operating Cost,Эксплуатационные затраты,
 Raw Material Cost,Стоимость сырья,
 Scrap Material Cost,Лом Материал Стоимость,
-Operating Cost (Company Currency),Эксплуатационные расходы (Компания Валюта),
-Raw Material Cost (Company Currency),Стоимость сырья (валюта компании),
-Scrap Material Cost(Company Currency),Скрапа Стоимость (Компания Валюта),
+Operating Cost (Company Currency),Эксплуатационные расходы (в валюте компании),
+Raw Material Cost (Company Currency),Стоимость сырья (в валюте компании),
+Scrap Material Cost(Company Currency),Стоимость отходов (в валюте компании),
 Total Cost,Общая стоимость,
-Total Cost (Company Currency),Общая стоимость (валюта компании),
+Total Cost (Company Currency),Общая стоимость (в валюте компании),
 Materials Required (Exploded),Необходимые материалы (в разобранном),
 Exploded Items,Взорванные предметы,
 Show in Website,Показать на веб-сайте,
@@ -7213,37 +7215,37 @@
 Show Items,Показать продукты,
 Show Operations,Показать операции,
 Website Description,Описание,
-BOM Explosion Item,Дерево ВМ продукта,
+BOM Explosion Item,Дерево спецификации продукта,
 Qty Consumed Per Unit,"Кол-во,  потребляемое за единицу",
 Include Item In Manufacturing,Включить товар в производство,
-BOM Item,ВМ продукта,
+BOM Item,Спецификация продукта,
 Item operation,Работа с элементами,
 Rate & Amount,Стоимость и сумма,
-Basic Rate (Company Currency),Основная ставка (валюта компании),
-Scrap %,Лом%,
+Basic Rate (Company Currency),Основная ставка (в валюте компании),
+Scrap %,Брак %,
 Original Item,Оригинальный товар,
-BOM Operation,ВМ Операция,
+BOM Operation,Операция спецификации,
 Operation Time ,Время операции,
 In minutes,В считанные минуты,
 Batch Size,Размер партии,
-Base Hour Rate(Company Currency),Базовый час Rate (Компания Валюта),
-Operating Cost(Company Currency),Эксплуатационные расходы (Компания Валюта),
+Base Hour Rate(Company Currency),Базовый час Rate (в валюте компании),
+Operating Cost(Company Currency),Эксплуатационные расходы (в валюте компании),
 BOM Scrap Item,Спецификация отходов продукта,
-Basic Amount (Company Currency),Базовая сумма (Компания Валюта),
+Basic Amount (Company Currency),Базовая сумма (в валюте компании),
 BOM Update Tool,Инструмент обновления спецификации,
 "Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate ""BOM Explosion Item"" table as per new BOM.\nIt also updates latest price in all the BOMs.","Замените конкретную спецификацию во всех других спецификациях, где она используется. Он заменит старую ссылку BOM, обновит стоимость и восстановит таблицу «BOM Explosion Item» в соответствии с новой спецификацией. Он также обновляет последнюю цену во всех спецификациях.",
 Replace BOM,Заменить спецификацию,
-Current BOM,Текущий BOM,
+Current BOM,Текущая спецификация,
 The BOM which will be replaced,"В спецификации, которые будут заменены",
 The new BOM after replacement,Новая спецификация после замены,
 Replace,Заменить,
 Update latest price in all BOMs,Обновление последней цены во всех спецификациях,
 BOM Website Item,Спецификация продукта на сайте,
-BOM Website Operation,BOM Операция Сайт,
+BOM Website Operation,Операция спецификации на сайте,
 Operation Time,Время работы,
-PO-JOB.#####,ПО-РАБОТА. #####,
+PO-JOB.#####,PO-JOB.#####,
 Timing Detail,Сроки,
-Time Logs,Журналы Время,
+Time Logs,Журналы времени,
 Total Time in Mins,Общее время в минутах,
 Operation ID,ID операции,
 Transferred Qty,Передано кол-во,
@@ -7252,9 +7254,9 @@
 Current Time,Текущее время,
 Job Card Item,Номер карты заданий,
 Job Card Time Log,Журнал учета рабочего времени,
-Time In Mins,Время в Мин,
-Completed Qty,Завершено Кол-во,
-Manufacturing Settings,Настройки Производство,
+Time In Mins,Время в мин.,
+Completed Qty,Завершено кол-во,
+Manufacturing Settings,Настройки производства,
 Raw Materials Consumption,Потребление сырья,
 Allow Multiple Material Consumption,Разрешить потребление нескольких материалов,
 Backflush Raw Materials Based On,На основе обратного отнесения затрат на сырье и материалы,
@@ -7277,7 +7279,7 @@
 Material Issue,Вопрос по материалу,
 Customer Provided,Клиент предоставлен,
 Minimum Order Quantity,Минимум для заказа,
-Default Workstation,По умолчанию Workstation,
+Default Workstation,Рабочее место по умолчанию,
 Production Plan,План производства,
 MFG-PP-.YYYY.-,MFG-PP-.YYYY.-,
 Get Items From,Получить продукты от,
@@ -7302,17 +7304,17 @@
 Planned Start Date,Планируемая дата начала,
 Quantity and Description,Количество и описание,
 material_request_item,material_request_item,
-Product Bundle Item,Продукт Связка товара,
-Production Plan Material Request,Производство План Материал Запрос,
-Production Plan Sales Order,Производственный План по Сделкам,
-Sales Order Date,Дата Сделки,
+Product Bundle Item,Связка продуктов,
+Production Plan Material Request,Производственный план по запросам материалов,
+Production Plan Sales Order,Производственный план по сделкам,
+Sales Order Date,Дата сделки,
 Routing Name,Название маршрутизации,
 MFG-WO-.YYYY.-,MFG-WO-.YYYY.-,
-Item To Manufacture,Продукт в производство,
-Material Transferred for Manufacturing,Материал переведен на Производство,
-Manufactured Qty,Изготовлено Кол-во,
-Use Multi-Level BOM,Использование Multi-Level BOM,
-Plan material for sub-assemblies,План материал для Субсборки,
+Item To Manufacture,Продукт для производства,
+Material Transferred for Manufacturing,Материал отправлен на производство,
+Manufactured Qty,Изготовлено кол-во,
+Use Multi-Level BOM,Использование многоуровневой специфифкации,
+Plan material for sub-assemblies,План материал для субсборки,
 Skip Material Transfer to WIP Warehouse,Пропустить передачу материала на склад WIP,
 Check if material transfer entry is not required,"Проверьте, не требуется ли запись материала",
 Backflush Raw Materials From Work-in-Progress Warehouse,Сбросить сырье с незавершенного производства,
@@ -7322,11 +7324,11 @@
 Work-in-Progress Warehouse,Работа-в-Прогресс Склад,
 This is a location where operations are executed.,"Это место, где выполняются операции.",
 This is a location where final product stored.,"Это место, где хранится конечный продукт.",
-Scrap Warehouse,Лом Склад,
+Scrap Warehouse,Склад брака,
 This is a location where scraped materials are stored.,"Это место, где хранятся скребки.",
 Required Items,Требуемые товары,
-Actual Start Date,Фактическая Дата начала,
-Planned End Date,Планируемая Дата завершения,
+Actual Start Date,Фактическая дата начала,
+Planned End Date,Планируемая дата завершения,
 Actual End Date,Факт. дата окончания,
 Operation Cost,Стоимость эксплуатации,
 Planned Operating Cost,Планируемые Эксплуатационные расходы,
@@ -7342,34 +7344,34 @@
 Operation completed for how many finished goods?,Операция выполнена На сколько готовой продукции?,
 Work in Progress,Незавершенная работа,
 Estimated Time and Cost,Расчетное время и стоимость,
-Planned Start Time,Планируемые Время,
-Planned End Time,Планируемые Время окончания,
+Planned Start Time,Планируемые время,
+Planned End Time,Планируемые время окончания,
 in Minutes,Через несколько минут,
 Actual Time and Cost,Фактическое время и стоимость,
-Actual Start Time,Фактическое начало Время,
-Actual End Time,Фактическое Время окончания,
-Updated via 'Time Log',"Обновлено помощью ""Time Вход""",
-Actual Operation Time,Фактическая Время работы,
-in Minutes\nUpdated via 'Time Log',"в минутах \n Обновлено помощью ""Time Вход""",
+Actual Start Time,Фактическое время начала,
+Actual End Time,Фактическое время окончания,
+Updated via 'Time Log',"Обновлено через 'Журнал времени'",
+Actual Operation Time,Фактическая время работы,
+in Minutes\nUpdated via 'Time Log',"в минутах \n Обновлено через 'Журнал времени'",
 (Hour Rate / 60) * Actual Operation Time,(часовая ставка ÷ 60) × фактическое время работы,
-Workstation Name,Имя рабочей станции,
+Workstation Name,Название рабочего места,
 Production Capacity,Производственная мощность,
 Operating Costs,Операционные расходы,
 Electricity Cost,Стоимость электроэнергии,
 per hour,в час,
-Consumable Cost,Расходные Стоимость,
+Consumable Cost,Расходная стоимость,
 Rent Cost,Стоимость аренды,
 Wages,Заработная плата,
 Wages per hour,Заработная плата в час,
 Net Hour Rate,Чистая стоимость часа,
-Workstation Working Hour,Рабочая станция Рабочие часы,
+Workstation Working Hour,Рабочие часы на рабочем месте,
 Certification Application,Приложение для сертификации,
 Name of Applicant,Имя заявителя,
 Certification Status,Статус сертификации,
 Yet to appear,Но чтобы появиться,
 Certified,Проверенный,
 Not Certified,Не сертифицирован,
-USD,доллар США,
+USD,USD,
 INR,INR,
 Certified Consultant,Сертифицированный консультант,
 Name of Consultant,Имя консультанта,
@@ -7377,13 +7379,13 @@
 Discuss ID,Обсудить ID,
 GitHub ID,Идентификатор GitHub,
 Non Profit Manager,Менеджер некоммерческих организаций,
-Chapter Head,Глава главы,
+Chapter Head,Заголовок главы,
 Meetup Embed HTML,Вставить HTML-код,
 chapters/chapter_name\nleave blank automatically set after saving chapter.,главы / chapter_name оставить пустым автоматически после сохранения главы.,
 Chapter Members,Члены группы,
-Members,члены,
+Members,Участники,
 Chapter Member,Участник,
-Website URL,URL веб-сайта,
+Website URL,URL сайта,
 Leave Reason,Оставить разум,
 Donor Name,Имя донора,
 Donor Type,Тип донора,
@@ -7391,16 +7393,16 @@
 Grant Application Details ,Сведения о предоставлении гранта,
 Grant Description,Описание гранта,
 Requested Amount,Запрошенная сумма,
-Has any past Grant Record,Имеет ли какая-либо прошлая грантовая запись,
+Has any past Grant Record,Имеет предидущую связь в диаграмме,
 Show on Website,Показать на сайте,
-Assessment  Mark (Out of 10),Оценка Оценка (из 10),
+Assessment  Mark (Out of 10),Оценка (из 10),
 Assessment  Manager,Менеджер по оценке,
-Email Notification Sent,Уведомление отправлено по электронной почте,
-NPO-MEM-.YYYY.-,НПО-MEM-.YYYY.-,
+Email Notification Sent,Отправлено уведомление по электронной почте,
+NPO-MEM-.YYYY.-,NPO-MEM-.YYYY.-,
 Membership Expiry Date,Дата истечения срока членства,
 Razorpay Details,Детали Razorpay,
 Subscription ID,ID подписки,
-Customer ID,Пользовательский ИД,
+Customer ID,Пользовательский ID,
 Subscription Activated,Подписка активирована,
 Subscription Start ,Подписка Старт,
 Subscription End,Конец подписки,
@@ -7420,8 +7422,8 @@
 Availability and Skills,Доступность и навыки,
 Availability,Доступность,
 Weekends,Выходные дни,
-Availability Timeslot,Доступность Timeslot,
-Morning,утро,
+Availability Timeslot,Временной интервал доступности,
+Morning,Утро,
 Afternoon,После полудня,
 Evening,Вечер,
 Anytime,В любой момент,
@@ -7430,33 +7432,33 @@
 Homepage,Главная страница,
 Hero Section Based On,Раздел героя на основе,
 Homepage Section,Раздел домашней страницы,
-Hero Section,Раздел героя,
-Tag Line,Tag Line,
-Company Tagline for website homepage,Компания Слоган на главную страницу сайта,
-Company Description for website homepage,Описание компании на главную страницу сайта,
-Homepage Slideshow,Домашняя страница Слайдшоу,
-"URL for ""All Products""",URL для &quot;Все продукты&quot;,
-Products to be shown on website homepage,Продукты будут показаны на главной страницы сайта,
-Homepage Featured Product,Главная рекомендуемых продуктов,
+Hero Section,Раздел героев,
+Tag Line,Тег линии,
+Company Tagline for website homepage,Слоган компании на главной странице сайта,
+Company Description for website homepage,Описание компании на главной странице сайта,
+Homepage Slideshow,Слайдшоу на домашней странице,
+"URL for ""All Products""",URL для ""Все продукты""",
+Products to be shown on website homepage,Продукты будут показаны на главной странице сайта,
+Homepage Featured Product,Рекомендуемые продукты на главной страницу,
 route,маршрут,
 Section Based On,Раздел на основе,
-Section Cards,Раздел Карты,
+Section Cards,Раздел Карточки,
 Number of Columns,Число столбцов,
 Number of columns for this section. 3 cards will be shown per row if you select 3 columns.,"Количество столбцов для этого раздела. 3 карты будут показаны в строке, если вы выберете 3 столбца.",
 Section HTML,Раздел HTML,
 Use this field to render any custom HTML in the section.,Используйте это поле для визуализации любого пользовательского HTML в разделе.,
-Section Order,Раздел Заказ,
+Section Order,Раздел заказов,
 "Order in which sections should appear. 0 is first, 1 is second and so on.","Порядок, в котором должны появляться разделы. 0 - первое, 1 - второе и т. Д.",
-Homepage Section Card,Домашняя страница Раздел Карта,
-Subtitle,подзаголовок,
-Products Settings,Настройки Продукты,
+Homepage Section Card,Домашняя страница раздел карт,
+Subtitle,Подзаголовок,
+Products Settings,Настройки продуктов,
 Home Page is Products,Главная — Продукты,
 "If checked, the Home page will be the default Item Group for the website","Если этот флажок установлен, главная страница будет по умолчанию Item Group для веб-сайте",
 Show Availability Status,Показать статус доступности,
 Product Page,Страница продукта,
 Products per Page,Продукты на страницу,
-Enable Field Filters,Включить полевые фильтры,
-Item Fields,Поля предметов,
+Enable Field Filters,Включить поле фильтров,
+Item Fields,Поля продуктов,
 Enable Attribute Filters,Включить фильтры атрибутов,
 Attributes,Атрибуты,
 Hide Variants,Скрыть варианты,
@@ -7464,31 +7466,31 @@
 Attribute,Атрибут,
 Website Filter Field,Поле фильтра веб-сайта,
 Activity Cost,Стоимость деятельности,
-Billing Rate,Платежная Оценить,
-Costing Rate,Калькуляция Оценить,
-title,заглавие,
+Billing Rate,Платежная оценка,
+Costing Rate,Стоимость,
+title,название,
 Projects User,Исполнитель проектов,
-Default Costing Rate,По умолчанию Калькуляция Оценить,
-Default Billing Rate,По умолчанию Платежная Оценить,
+Default Costing Rate,Ставка стоимости по умолчанию,
+Default Billing Rate,Платежная ставка по умолчанию,
 Dependent Task,Зависимая задача,
 Project Type,Тип проекта,
-% Complete Method,% Полный метод,
-Task Completion,Завершение задачи,
-Task Progress,Готовность задачи,
+% Complete Method,% Метод оценки готовности,
+Task Completion,Завершеные задачи,
+Task Progress,Ход выполнения задачи,
 % Completed,% Завершено,
 From Template,Из шаблона,
 Project will be accessible on the website to these users,Проект будет доступен на веб-сайте для этих пользователей,
 Copied From,Скопировано из,
 Start and End Dates,Даты начала и окончания,
 Actual Time (in Hours),Фактическое время (в часах),
-Costing and Billing,Калькуляция и биллинг,
-Total Costing Amount (via Timesheets),Общая сумма калькуляции (через расписания),
-Total Expense Claim (via Expense Claims),Итоговая сумма аванса (через Авансовые Отчеты),
-Total Purchase Cost (via Purchase Invoice),Общая стоимость покупки (через счет покупки),
-Total Sales Amount (via Sales Order),Общая Сумма Продаж (по Сделке),
-Total Billable Amount (via Timesheets),Общая сумма платежа (через расписания),
-Total Billed Amount (via Sales Invoices),Общая сумма выставленных счетов (через счета-фактуры),
-Total Consumed Material Cost  (via Stock Entry),Общая потребляемая материальная стоимость (через вход запаса),
+Costing and Billing,Стоимость и платежи,
+Total Costing Amount (via Timesheets),Общая стоимость (по табелю учета рабочего времени),
+Total Expense Claim (via Expense Claims),Итоговая сумма аванса (через возмещение расходов),
+Total Purchase Cost (via Purchase Invoice),Общая стоимость покупки (по счетам закупок),
+Total Sales Amount (via Sales Order),Общая сумма продажи (по сделке),
+Total Billable Amount (via Timesheets),Общая сумма платежа (по табелю учета рабочего времени),
+Total Billed Amount (via Sales Invoices),Общая сумма выставленных счетов (по счетам продаж),
+Total Consumed Material Cost  (via Stock Entry),Общая потребляемая материальная стоимость (через записи на складе),
 Gross Margin,Валовая прибыль,
 Gross Margin %,Валовая маржа %,
 Monitor Progress,Мониторинг готовности,
@@ -7509,43 +7511,43 @@
 Project User,Пользователь проекта,
 View attachments,Просмотр вложений,
 Projects Settings,Настройки проектов,
-Ignore Workstation Time Overlap,Игнорировать перекрытие рабочей станции,
+Ignore Workstation Time Overlap,Игнорировать перекрытие времени на рабочем месте,
 Ignore User Time Overlap,Игнорировать перекрытие пользовательского времени,
-Ignore Employee Time Overlap,Игнорировать перекрытие сотрудников,
+Ignore Employee Time Overlap,Игнорировать перекрытие времени сотрудников,
 Weight,Вес,
 Parent Task,Родительская задача,
-Timeline,График,
+Timeline,Хронология,
 Expected Time (in hours),Ожидаемое время (в часах),
 % Progress,% Прогресс,
-Is Milestone,Это этап,
+Is Milestone,Является этапом,
 Task Description,Описание задания,
-Dependencies,зависимости,
-Dependent Tasks,Зависимые задачи,
+Dependencies,Зависимости,
+Dependent Tasks,Зависит от задач,
 Depends on Tasks,Зависит от задач,
-Actual Start Date (via Time Sheet),Фактическая дата начала (с помощью Time Sheet),
+Actual Start Date (via Time Sheet),Фактическая дата начала (по табелю учета рабочего времени),
 Actual Time (in hours),Фактическое время (в часах),
-Actual End Date (via Time Sheet),Фактическая дата окончания (с помощью табеля рабочего времени),
-Total Costing Amount (via Time Sheet),Общая калькуляция Сумма (с помощью Time Sheet),
+Actual End Date (via Time Sheet),Фактическая дата окончания (по табелю учета рабочего времени),
+Total Costing Amount (via Time Sheet),Общая калькуляция Сумма (по табелю учета рабочего времени),
 Total Expense Claim (via Expense Claim),Итоговая сумма аванса (через Авансовый Отчет),
-Total Billing Amount (via Time Sheet),Общая сумма Billing (через табель),
-Review Date,Дата пересмотра,
+Total Billing Amount (via Time Sheet),Общая сумма Billing (по табелю учета рабочего времени),
+Review Date,Дата проверки,
 Closing Date,Дата закрытия,
 Task Depends On,Задача зависит от,
 Task Type,Тип задачи,
-TS-.YYYY.-,ТС-.ГГГГ.-,
-Employee Detail,Сотрудник Деталь,
+TS-.YYYY.-,TS-.YYYY.-,
+Employee Detail,Подробности о сотруднике,
 Billing Details,Платежные реквизиты,
 Total Billable Hours,Всего человеко-часов,
-Total Billed Hours,Всего Оплачиваемые Часы,
-Total Costing Amount,Общая сумма Стоимостью,
-Total Billable Amount,Общая сумма Выплачиваемый,
-Total Billed Amount,Общая сумма Объявленный,
+Total Billed Hours,Всего оплачиваемые часы,
+Total Costing Amount,Общая стоимость,
+Total Billable Amount,Общая сумма выплат,
+Total Billed Amount,Объявленная стоимость,
 % Amount Billed,% Сумма счета,
-Hrs,часов,
+Hrs,Часов,
 Costing Amount,Калькуляция Сумма,
-Corrective/Preventive,Корректирующее / Профилактическая,
-Corrective,корректив,
-Preventive,превентивный,
+Corrective/Preventive,Корректирующее / Превентивная,
+Corrective,Корректив,
+Preventive,Превентивный,
 Resolution,Разрешение,
 Resolutions,Решения,
 Quality Action Resolution,Решение по качеству действий,
@@ -7553,45 +7555,45 @@
 Quality Feedback Template Parameter,Параметр шаблона обратной связи по качеству,
 Quality Goal,Цель качества,
 Monitoring Frequency,Частота мониторинга,
-Weekday,будний день,
+Weekday,Будний день,
 Objectives,Цели,
 Quality Goal Objective,Цель качества,
 Objective,Задача,
 Agenda,Повестка дня,
-Minutes,минут,
-Quality Meeting Agenda,Повестка дня встречи качества,
-Quality Meeting Minutes,Протокол встречи качества,
+Minutes,Минут,
+Quality Meeting Agenda,Встреча для оценки работ,
+Quality Meeting Minutes,Протокол встречи для оценки работ,
 Minute,Минута,
 Parent Procedure,Родительская процедура,
 Processes,Процессы,
-Quality Procedure Process,Процесс качественного процесса,
+Quality Procedure Process,Процедура контроля качества,
 Process Description,Описание процесса,
 Link existing Quality Procedure.,Ссылка существующей процедуры качества.,
 Additional Information,Дополнительная информация,
 Quality Review Objective,Цель проверки качества,
 DATEV Settings,Настройки DATEV,
-Regional,региональный,
+Regional,Региональный,
 Consultant ID,Идентификатор консультанта,
-GST HSN Code,Код GST HSN,
-HSN Code,Код HSN,
-GST Settings,Настройки GST,
-GST Summary,Обзор GST,
+GST HSN Code,НДС ГС код,
+HSN Code,ГС код,
+GST Settings,Настройки НДС,
+GST Summary,Сумма НДС,
 GSTIN Email Sent On,GSTIN электронная почта отправлена,
-GST Accounts,Учетные записи GST,
+GST Accounts,Счета НДС,
 B2C Limit,Ограничение B2C,
 Set Invoice Value for B2C. B2CL and B2CS calculated based on this invoice value.,"Установите значение счета для B2C. B2CL и B2CS, рассчитанные на основе этой стоимости счета.",
 GSTR 3B Report,Отчет GSTR 3B,
-January,январь,
-February,февраль,
-March,марш,
-April,апрель,
+January,Январь,
+February,Февраль,
+March,Март,
+April,Апрель,
 May,Мая,
-June,июнь,
-July,июль,
-August,августейший,
-September,сентябрь,
-October,октября,
-November,ноябрь,
+June,Июнь,
+July,Июль,
+August,Август,
+September,Сентябрь,
+October,Октября,
+November,Ноябрь,
 December,Декабрь,
 JSON Output,Выход JSON,
 Invoices with no Place Of Supply,Счета без места поставки,
@@ -7612,9 +7614,9 @@
 194LA,194LA,
 194LBB,194LBB,
 194LBC,194LBC,
-Certificate No,Сертификат номер,
+Certificate No,Сертификат №,
 Deductee Details,Детали франшизы,
-PAN No,PAN Нет,
+PAN No,PAN №,
 Validity Details,Сведения о сроке действия,
 Rate Of TDS As Per Certificate,Ставка TDS согласно сертификату,
 Certificate Limit,Лимит сертификата,
@@ -7639,16 +7641,16 @@
 Reservation End Time,Время окончания бронирования,
 No of Seats,Количество мест,
 Minimum Seating,Минимальное размещение,
-"Keep Track of Sales Campaigns. Keep track of Leads, Quotations, Sales Order etc from Campaigns to gauge Return on Investment. ","Следите за Кампаниями по продажам. Отслеживайте Обращения, Предложения, Сделки и т.п. от Кампаний и оцените отдачу от инвестиций.",
+"Keep Track of Sales Campaigns. Keep track of Leads, Quotations, Sales Order etc from Campaigns to gauge Return on Investment. ","Следите за кампаниями по продажам. Отслеживайте лиды, предложения, сделки и т.п. от кампаний и оцените отдачу от инвестиций.",
 SAL-CAM-.YYYY.-,SAL-CAM-.YYYY.-,
 Campaign Schedules,Расписание кампаний,
 Buyer of Goods and Services.,Покупатель товаров и услуг.,
-CUST-.YYYY.-,КЛИЕНТ-.YYYY.-,
+CUST-.YYYY.-,CUST-.YYYY.-,
 Default Company Bank Account,Стандартный банковский счет компании,
-From Lead,Из Обращения,
+From Lead,Из лида,
 Account Manager,Менеджер по работе с клиентами,
-Allow Sales Invoice Creation Without Sales Order,Разрешить создание счета-фактуры без заказа на продажу,
-Allow Sales Invoice Creation Without Delivery Note,Разрешить создание счета-фактуры без накладной,
+Allow Sales Invoice Creation Without Sales Order,Разрешить создание счета без заказа на продажу,
+Allow Sales Invoice Creation Without Delivery Note,Разрешить создание счета без накладной,
 Default Price List,По умолчанию Прайс-лист,
 Primary Address and Contact Detail,Первичный адрес и контактная информация,
 "Select, to make the customer searchable with these fields","Выберите, чтобы сделать поиск клиента с этими полями",
@@ -7670,21 +7672,21 @@
 MAT-INS-.YYYY.-,MAT-INS-.YYYY.-,
 Installation Date,Дата установки,
 Installation Time,Время установки,
-Installation Note Item,Установка Примечание Пункт,
+Installation Note Item,Установка примечаний к продукту,
 Installed Qty,Установленное количество,
-Lead Source,Источник Обращения,
+Lead Source,Источник лида,
 Period Start Date,Дата начала периода,
 Period End Date,Дата окончания периода,
 Cashier,Касса,
-Difference,разница,
+Difference,Разница,
 Modes of Payment,Способы оплаты,
-Linked Invoices,Связанные счета-фактуры,
+Linked Invoices,Связанные счета,
 POS Closing Voucher Details,Информация о закрытии ваучера POS,
 Collected Amount,Собранная сумма,
 Expected Amount,Ожидаемая сумма,
-POS Closing Voucher Invoices,Счета-фактуры закрытых ваучеров,
+POS Closing Voucher Invoices,Счета закрытых ваучеров,
 Quantity of Items,Количество позиций,
-"Aggregate group of **Items** into another **Item**. This is useful if you are bundling a certain **Items** into a package and you maintain stock of the packed **Items** and not the aggregate **Item**. \n\nThe package **Item** will have ""Is Stock Item"" as ""No"" and ""Is Sales Item"" as ""Yes"".\n\nFor Example: If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item.\n\nNote: BOM = Bill of Materials","Собрать группу продуктов в третий продукт. Это полезно когда Вы комплектуете определённые продукты в упаковку и ведёте учёт не упаковок, а самих продуктов. Пакетному продукту указывают «Складируемый продукт» — «Нет» и «Продаваемый продукт» — «Да». Для примера: если вы продаете ноутбуки и рюкзаки отдельно, но они имеют специальную цену когда клиент покупает их вместе, тогда «ноутбук+рюкзак» будет пакетным продуктом. Примечание: ВМ = Ведомость материалов (спецификация)",
+"Aggregate group of **Items** into another **Item**. This is useful if you are bundling a certain **Items** into a package and you maintain stock of the packed **Items** and not the aggregate **Item**. \n\nThe package **Item** will have ""Is Stock Item"" as ""No"" and ""Is Sales Item"" as ""Yes"".\n\nFor Example: If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item.\n\nNote: BOM = Bill of Materials","Собрать группу продуктов в третий продукт. Это полезно когда вы комплектуете определённые продукты в упаковку и ведёте учёт не упаковок, а самих продуктов. Пакетному продукту указывают «Складируемый продукт» — «Нет» и «Продаваемый продукт» — «Да». Для примера: если вы продаете ноутбуки и рюкзаки отдельно, но они имеют специальную цену когда клиент покупает их вместе, тогда «ноутбук+рюкзак» будет пакетным продуктом. Примечание: ВМ = Ведомость материалов (спецификация)",
 Parent Item,Родительский продукт,
 List items that form the package.,"Список продуктов, которые формируют пакет",
 SAL-QTN-.YYYY.-,SAL-QTN-.YYYY.-,
@@ -7716,8 +7718,8 @@
 Partly Billed,Выставлены счет(а) частично,
 Ensure Delivery Based on Produced Serial No,Обеспечить доставку на основе серийного номера,
 Supplier delivers to Customer,Поставщик поставляет Покупателю,
-Delivery Warehouse,Доставка Склад,
-Planned Quantity,Планируемый Количество,
+Delivery Warehouse,Доставка на склад,
+Planned Quantity,Планируемое количество,
 For Production,Для производства,
 Work Order Qty,Кол-во заказа,
 Produced Quantity,Добытое количество,
@@ -7726,7 +7728,7 @@
 Contact No.,Контактный номер,
 Contribution (%),Вклад (%),
 Contribution to Net Total,Вклад в Сумму,
-Selling Settings,Продажа Настройки,
+Selling Settings,Настройки продаж,
 Settings for Selling Module,Настройки по продаже модуля,
 Customer Naming By,Именование клиентов По,
 Campaign Naming By,Кампания Именование По,
@@ -7736,15 +7738,15 @@
 Default Quotation Validity Days,"Число дней по умолчанию, в течение которых Предложение действительно",
 Sales Update Frequency,Частота обновления продаж,
 Each Transaction,Каждая транзакция,
-SMS Center,СМС-центр,
+SMS Center,SMS-центр,
 Send To,Отправить,
-All Contact,Всем Контактам,
+All Contact,Всем контактам,
 All Customer Contact,Контакты всех клиентов,
-All Supplier Contact,Всем Контактам Поставщиков,
-All Sales Partner Contact,Всем Контактам Торговых Партнеров,
-All Lead (Open),Всем Обращениям (Созданным),
-All Employee (Active),Всем Сотрудникам (Активным),
-All Sales Person,Всем Продавцам,
+All Supplier Contact,Всем контактам поставщиков,
+All Sales Partner Contact,Всем контактам торговых партнеров,
+All Lead (Open),Всем лидам (Открыт),
+All Employee (Active),Всем сотрудникам (Активный),
+All Sales Person,Всем продавцам,
 Create Receiver List,Создать список получателей,
 Receiver List,Список получателей,
 Messages greater than 160 characters will be split into multiple messages,"Сообщения с более, чем 160 символами, будут разделены на несколько",
@@ -7766,9 +7768,9 @@
 Approving User  (above authorized value),Утверждении пользователь (выше уставного стоимости),
 Brand Defaults,Марка по умолчанию,
 Legal Entity / Subsidiary with a separate Chart of Accounts belonging to the Organization.,"Юридическое лицо / Вспомогательный с отдельным Планом счетов бухгалтерского учета, принадлежащего Организации.",
-Change Abbreviation,Изменить Аббревиатура,
+Change Abbreviation,Изменить аббревиатуру,
 Parent Company,Родительская компания,
-Default Values,Значения По Умолчанию,
+Default Values,Значения по умолчанию,
 Default Holiday List,По умолчанию Список праздников,
 Default Selling Terms,Условия продажи по умолчанию,
 Default Buying Terms,Условия покупки по умолчанию,
@@ -7792,14 +7794,14 @@
 Unrealized Exchange Gain/Loss Account,Нереализованная учетная ставка по обмену / убытку,
 Allow Account Creation Against Child Company,Разрешить создание аккаунта против дочерней компании,
 Default Payable Account,По умолчанию оплачивается аккаунт,
-Default Employee Advance Account,Default Advance Account,
+Default Employee Advance Account,Авансовый счет сотрудника по умолчанию,
 Default Cost of Goods Sold Account,По умолчанию Себестоимость проданных товаров счет,
 Default Income Account,Счет дохода по умолчанию,
 Default Deferred Revenue Account,По умолчанию отложенная учетная запись,
 Default Deferred Expense Account,Учетная запись по отложенному счету по умолчанию,
 Default Payroll Payable Account,По умолчанию Payroll оплаты счетов,
 Default Expense Claim Payable Account,Платежная учетная запись по умолчанию,
-Stock Settings,Настройки Запасов,
+Stock Settings,Настройки запасов,
 Enable Perpetual Inventory,Включить вечный инвентарь,
 Default Inventory Account,Учетная запись по умолчанию,
 Stock Adjustment Account,Регулирование счета запасов,
@@ -7815,17 +7817,17 @@
 Date of Incorporation,Дата включения,
 Date of Commencement,Дата начала,
 Phone No,Номер телефона,
-Company Description,Описание Компании,
+Company Description,Описание компании,
 Registration Details,Регистрационные данные,
 Company registration numbers for your reference. Tax numbers etc.,Регистрационные номера компании для вашей справки. Налоговые числа и т.д.,
-Delete Company Transactions,Удалить Сделки Компания,
+Delete Company Transactions,Удалить транзакции компании,
 Currency Exchange,Курс обмена валюты,
 Specify Exchange Rate to convert one currency into another,Укажите Курс конвертировать одну валюту в другую,
 From Currency,Из валюты,
 To Currency,В валюту,
 For Buying,Для покупки,
 For Selling,Для продажи,
-Customer Group Name,Группа Имя клиента,
+Customer Group Name,Название группы клиентов,
 Parent Customer Group,Родительская группа клиента,
 Only leaf nodes are allowed in transaction,Только листовые узлы допускаются в сделке,
 Mention if non-standard receivable account applicable,Упоминание если нестандартная задолженность счет применимо,
@@ -7859,7 +7861,7 @@
 Purchase Orders Items Overdue,Элементы заказа на поставку просрочены,
 Upcoming Calendar Events,Предстоящие события календаря,
 Open To Do,Открыто делать,
-Add Quote,Добавить Цитата,
+Add Quote,Добавить предложение,
 Global Defaults,Глобальные вводные по умолчанию,
 Default Company,Компания по умолчанию,
 Current Fiscal Year,Текущий финансовый год,
@@ -7893,7 +7895,7 @@
 Update Series Number,Обновить Идентификаторы по Номеру,
 Quotation Lost Reason,Причина Отказа от Предложения,
 A third party distributor / dealer / commission agent / affiliate / reseller who sells the companies products for a commission.,"Сторонний дистрибьютер, дилер, агент, филиал или реселлер, который продаёт продукты компании за комиссионное вознаграждение.",
-Sales Partner Name,Имя Партнера по продажам,
+Sales Partner Name,Имя партнера по продажам,
 Partner Type,Тип партнера,
 Address & Contacts,Адрес и контакты,
 Address Desc,Адрес по убыванию,
@@ -7908,13 +7910,13 @@
 All Sales Transactions can be tagged against multiple **Sales Persons** so that you can set and monitor targets.,Все сделок купли-продажи могут быть помечены против нескольких ** продавцы ** так что вы можете устанавливать и контролировать цели.,
 Name and Employee ID,Имя и ID сотрудника,
 Sales Person Name,Имя продавца,
-Parent Sales Person,Головная группа продаж,
-Select company name first.,Выберите название компании в первую очередь.,
+Parent Sales Person,Выше стоящий продавец,
+Select company name first.,В первую очередь выберите название компании.,
 Sales Person Targets,Цели продавца,
 Set targets Item Group-wise for this Sales Person.,Задайте цели Продуктовых Групп для Продавца,
 Supplier Group Name,Название группы поставщиков,
 Parent Supplier Group,Родительская группа поставщиков,
-Target Detail,Цель Подробности,
+Target Detail,Подробности цели,
 Target Qty,Целевое количество,
 Target  Amount,Целевая сумма,
 Target Distribution,Распределение цели,
@@ -7922,9 +7924,9 @@
 Applicable Modules,Применимые модули,
 Terms and Conditions Help,Правила и условия Помощь,
 Classification of Customers by region,Классификация клиентов по регионам,
-Territory Name,Территория Имя,
-Parent Territory,Родитель Территория,
-Territory Manager,Territory Manager,
+Territory Name,Название региона,
+Parent Territory,Родительский регион,
+Territory Manager,Региональный менеджер,
 For reference,Для справки,
 Territory Targets,Территория Цели,
 Set Item Group-wise budgets on this Territory. You can also include seasonality by setting the Distribution.,"Установите группу товаров стрелке бюджеты на этой территории. Вы можете также включить сезонность, установив распределение.",
@@ -7937,16 +7939,16 @@
 Display Settings,Настройки отображения,
 Show Public Attachments,Показать общедоступные приложения,
 Show Price,Показать цену,
-Show Stock Availability,Доступность,
+Show Stock Availability,Показать наличие на складе,
 Show Contact Us Button,Кнопка «Связаться с нами»,
 Show Stock Quantity,Показывать количество запаса,
-Show Apply Coupon Code,Показать Применить код купона,
+Show Apply Coupon Code,Показать примененные коды купона,
 Allow items not in stock to be added to cart,"Разрешить добавление в корзину товаров, которых нет в наличии",
 Prices will not be shown if Price List is not set,"Цены не будут показаны, если прайс-лист не установлен",
 Quotation Series,Идентификаторы Предложений,
 Checkout Settings,Checkout Настройки,
 Enable Checkout,Включить Checkout,
-Payment Success Url,Успех Оплата URL,
+Payment Success Url,URL успешного платежа,
 After payment completion redirect user to selected page.,После завершения оплаты перенаправить пользователя на выбранную страницу.,
 Batch Details,Детали партии,
 Batch ID,ID партии,
@@ -7973,13 +7975,13 @@
 Issue Credit Note,Кредитная кредитная карта,
 Return Against Delivery Note,Вернуться На накладной,
 Customer's Purchase Order No,Клиентам Заказ Нет,
-Billing Address Name,Адрес для выставления счета Имя,
+Billing Address Name,Название адреса для выставления счета,
 Required only for sample item.,Требуется только для образца пункта.,
 "If you have created a standard template in Sales Taxes and Charges Template, select one and click on the button below.","Если вы создали стандартный шаблон в шаблонах Налоги с налогами и сбором платежей, выберите его и нажмите кнопку ниже.",
 In Words will be visible once you save the Delivery Note.,По словам будет виден только вы сохраните накладной.,
 In Words (Export) will be visible once you save the Delivery Note.,В Слов (Экспорт) будут видны только вы сохраните накладной.,
-Transporter Info,Transporter информация,
-Driver Name,Имя драйвера,
+Transporter Info,Информация для транспортировки,
+Driver Name,Имя водителя,
 Track this Delivery Note against any Project,Подписка на Delivery Note против любого проекта,
 Inter Company Reference,Справочник Интер,
 Print Without Amount,Распечатать без суммы,
@@ -7988,7 +7990,7 @@
 Installation Status,Состояние установки,
 Excise Page Number,Количество Акцизный Страница,
 Instructions,Инструкции,
-From Warehouse,От Склад,
+From Warehouse,Со склада,
 Against Sales Order,По Сделке,
 Against Sales Order Item,По Продукту Сделки,
 Against Sales Invoice,Повторная накладная,
@@ -8003,18 +8005,18 @@
 Send with Attachment,Отправить с прикрепленным файлом,
 Delay between Delivery Stops,Задержка между поставками,
 Delivery Stop,Остановить доставку,
-Lock,Замок,
+Lock,Заблокировано,
 Visited,Посещен,
 Order Information,запросить информацию,
 Contact Information,Контакты,
 Email sent to,Письмо отправлено,
 Dispatch Information,Информация о доставке,
-Estimated Arrival,ожидаемое прибытие,
+Estimated Arrival,Ожидаемое прибытие,
 MAT-DT-.YYYY.-,MAT-DT-.YYYY.-,
 Initial Email Notification Sent,Исходящее уведомление по электронной почте отправлено,
 Delivery Details,Подробности доставки,
 Driver Email,Электронная почта водителя,
-Driver Address,Адрес драйвера,
+Driver Address,Адрес водителя,
 Total Estimated Distance,Общее расчетное расстояние,
 Distance UOM,Расстояние UOM,
 Departure Time,Время отправления,
@@ -8049,7 +8051,7 @@
 Will also apply for variants unless overrridden,"Будет также применяться для модификаций, если не отменено",
 Units of Measure,Единицы измерения,
 Will also apply for variants,Также применять к модификациям,
-Serial Nos and Batches,Серийные номера  и партии,
+Serial Nos and Batches,Серийные номера и партии,
 Has Batch No,Имеет номер партии,
 Automatically Create New Batch,Автоматически создавать новую группу,
 Batch Number Series,Партия Идентификаторов по номеру,
@@ -8079,16 +8081,16 @@
 Supplier Items,Продукты поставщика,
 Foreign Trade Details,Сведения о внешней торговле,
 Country of Origin,Страна происхождения,
-Sales Details,Продажи Подробности,
+Sales Details,Детали продажи,
 Default Sales Unit of Measure,Единица измерения продаж по умолчанию,
 Is Sales Item,Продаваемый продукт,
-Max Discount (%),Макс Скидка (%),
+Max Discount (%),Макс. скидка (%),
 No of Months,Число месяцев,
 Customer Items,Продукты для клиентов,
 Inspection Criteria,Критерии проверки,
 Inspection Required before Purchase,Перед приходованием необходима проверка,
 Inspection Required before Delivery,Перед отправкой необходима проверка,
-Default BOM,По умолчанию BOM,
+Default BOM,Спецификация по умолчанию,
 Supply Raw Materials for Purchase,Поставка сырья для покупки,
 If subcontracted to a vendor,Если по субподряду поставщика,
 Customer Code,Код клиента,
@@ -8098,7 +8100,7 @@
 Items with higher weightage will be shown higher,Продукты с более высоким весом будут показаны выше,
 Show a slideshow at the top of the page,Показывать слайд-шоу в верхней части страницы,
 Website Image,Изображение сайта,
-Website Warehouse,Сайт Склад,
+Website Warehouse,Сайт склад,
 "Show ""In Stock"" or ""Not in Stock"" based on stock available in this warehouse.",Покажите «На складе» или «Не на складе» на основе имеющихся на складе.,
 Website Item Groups,Продуктовые группы на сайте,
 List this Item in multiple groups on the website.,Укажите этот продукт в нескольких группах на сайте,
@@ -8108,16 +8110,16 @@
 Total Projected Qty,Общая запланированная Кол-во,
 Hub Publishing Details,Сведения о публикации концентратора,
 Publish in Hub,Опубликовать в Hub,
-Publish Item to hub.erpnext.com,Опубликовать деталь к hub.erpnext.com,
+Publish Item to hub.erpnext.com,Опубликовать продукт в hub.erpnext.com,
 Hub Category to Publish,Категория концентратора для публикации,
 Hub Warehouse,Склад хабов,
 "Publish ""In Stock"" or ""Not in Stock"" on Hub based on stock available in this warehouse.",Опубликуйте «На складе» или «Нет на складе» на концентраторе на основе имеющихся в наличии на складе.,
-Synced With Hub,Синхронизированные со ступицей,
-Item Alternative,Пункт Альтернатива,
-Alternative Item Code,Альтернативный код товара,
+Synced With Hub,Синхронизированные с хабом,
+Item Alternative,Альтернативный продукт,
+Alternative Item Code,Альтернативный код продукта,
 Two-way,Двусторонний,
-Alternative Item Name,Альтернативное название товара,
-Attribute Name,Имя атрибута,
+Alternative Item Name,Альтернативное название продукта,
+Attribute Name,Название атрибута,
 Numeric Values,Числовые значения,
 From Range,От хребта,
 Increment,Приращение,
@@ -8134,7 +8136,7 @@
 Item Customer Detail,Пункт Детальное клиентов,
 "For the convenience of customers, these codes can be used in print formats like Invoices and Delivery Notes","Для удобства клиентов, эти коды могут быть использованы в печатных формах документов, таких как Счета и Документы  Отгрузки",
 Ref Code,Код ссылки,
-Item Default,Пункт По умолчанию,
+Item Default,Продукт по умолчанию,
 Purchase Defaults,Покупки по умолчанию,
 Default Buying Cost Center,Центр затрат продажи по умолчанию,
 Default Supplier,Поставщик по умолчанию,
@@ -8184,7 +8186,7 @@
 % Ordered,% заказано,
 Terms and Conditions Content,Условия Содержимое,
 Quantity and Warehouse,Количество и Склад,
-Lead Time Date,Время и Дата Обращения,
+Lead Time Date,Время и Дата Лида,
 Min Order Qty,Минимальный заказ Кол-во,
 Packed Item,Упаковано,
 To Warehouse (Optional),На склад (Необязательно),
@@ -8216,10 +8218,10 @@
 Pick List Item,Элемент списка выбора,
 Picked Qty,Выбрал кол-во,
 Price List Master,Прайс-лист Мастер,
-Price List Name,Цена Имя,
+Price List Name,Название прайс-листа,
 Price Not UOM Dependent,Цена не зависит от UOM,
 Applicable for Countries,Применимо для стран,
-Price List Country,Цены Страна,
+Price List Country,Прайс лист страны,
 MAT-PRE-.YYYY.-,MAT-PRE-.YYYY.-,
 Supplier Delivery Note,Доставочный лист,
 Time at which materials were received,Время получения материалов,
@@ -8236,8 +8238,8 @@
 Vehicle Number,Номер транспортного средства,
 Vehicle Date,Дата транспортного средства,
 Received and Accepted,Получил и принял,
-Accepted Quantity,Принято Количество,
-Rejected Quantity,Отклонен Количество,
+Accepted Quantity,Количество принятых,
+Rejected Quantity,Количество отклоненных,
 Accepted Qty as per Stock UOM,Принятое количество в соответствии с единицами измерения запаса,
 Sample Quantity,Количество образцов,
 Rate and Amount,Ставку и сумму,
@@ -8278,21 +8280,21 @@
 Warranty / AMC Details,Гарантия / подробная информация,
 Warranty Expiry Date,Срок действия гарантии,
 AMC Expiry Date,Срок действия AMC,
-Under Warranty,Под гарантии,
+Under Warranty,На гарантии,
 Out of Warranty,По истечении гарантийного срока,
 Under AMC,Под КУА,
 Out of AMC,Из КУА,
 Warranty Period (Days),Гарантийный срок (дней),
 Serial No Details,Серийный номер подробнее,
 MAT-STE-.YYYY.-,MAT-STE-.YYYY.-,
-Stock Entry Type,Тип входа,
+Stock Entry Type,Тип складской записи,
 Stock Entry (Outward GIT),Вход в акции (внешний GIT),
 Material Consumption for Manufacture,Потребление материала для производства,
 Repack,Перепаковать,
 Send to Subcontractor,Отправить субподрядчику,
-Delivery Note No,Документ  Отгрузки №,
-Sales Invoice No,№ Счета на продажу,
-Purchase Receipt No,Покупка Получение Нет,
+Delivery Note No,Накладная о доставке №,
+Sales Invoice No,Номер счета на продажу,
+Purchase Receipt No,Квитанция о покупке №,
 Inspection Required,Инспекция Обязательные,
 From BOM,Из спецификации,
 For Quantity,Для Количество,
@@ -8310,34 +8312,34 @@
 Total Additional Costs,Всего Дополнительные расходы,
 Customer or Supplier Details,Детали по Заказчику или Поставщику,
 Per Transferred,За переданный,
-Stock Entry Detail,Подробности Складского Акта,
+Stock Entry Detail,Подробности складских записей,
 Basic Rate (as per Stock UOM),Базовая ставка (согласно ед.измерения запасов на складе),
 Basic Amount,Основное количество,
 Additional Cost,Дополнительная стоимость,
 Serial No / Batch,Серийный номер / Партия,
-BOM No. for a Finished Good Item,Номер ВМ для готового продукта,
-Material Request used to make this Stock Entry,"Материал Запрос используется, чтобы сделать эту Stock запись",
+BOM No. for a Finished Good Item,Номер спецификации для готового продукта,
+Material Request used to make this Stock Entry,"Запрос материала, использованный для создания этой записи о запасах,",
 Subcontracted Item,Субподрядный товар,
 Against Stock Entry,Против входа в акции,
 Stock Entry Child,Фондовый вход ребенка,
 PO Supplied Item,PO поставленный пункт,
 Reference Purchase Receipt,Ссылка на покупку,
-Stock Ledger Entry,Фото со Ledger Entry,
+Stock Ledger Entry,Записи в остатках,
 Outgoing Rate,Исходящие Оценить,
 Actual Qty After Transaction,Остаток после проведения,
-Stock Value Difference,Расхождение Стоимости Запасов,
+Stock Value Difference,Расхождение стоимости запасов,
 Stock Queue (FIFO),Фото со Очередь (FIFO),
-Is Cancelled,Является Отмененные,
+Is Cancelled,Является отмененным,
 Stock Reconciliation,Инвентаризация запасов,
 This tool helps you to update or fix the quantity and valuation of stock in the system. It is typically used to synchronise the system values and what actually exists in your warehouses.,"Этот инструмент поможет вам обновить или исправить количество и оценку запасов в системе. Это, как правило, используется для синхронизации системных значений и то, что на самом деле существует в ваших складах.",
-MAT-RECO-.YYYY.-,MAT-РЕКО-.YYYY.-,
+MAT-RECO-.YYYY.-,MAT-RECO-.YYYY.-,
 Reconciliation JSON,Примирение JSON,
 Stock Reconciliation Item,Товар с Сверки Запасов,
 Before reconciliation,Перед примирения,
 Current Serial No,Текущий серийный номер,
 Current Valuation Rate,Текущая оценка,
 Current Amount,Текущий объем,
-Quantity Difference,Количество Разница,
+Quantity Difference,Количественная разница,
 Amount Difference,Сумма разница,
 Item Naming By,Наименование продукта по,
 Default Item Group,Продуктовая группа по умолчанию,
@@ -8351,19 +8353,19 @@
 Auto Material Request,Автоматический запрос материалов,
 Inter Warehouse Transfer Settings,Настройки передачи между складами,
 Freeze Stock Entries,Замораживание поступления запасов,
-Stock Frozen Upto,Фото Замороженные До,
+Stock Frozen Upto,остатки заморожены до,
 Batch Identification,Идентификация партии,
-Use Naming Series,Использовать Идентификаторы по Имени,
+Use Naming Series,Использовать серийный номер,
 Naming Series Prefix,Префикс Идентификации по Имени,
-UOM Category,Категория UOM,
-UOM Conversion Detail,Единица измерения Преобразование Подробно,
+UOM Category,Категория единиц измерения,
+UOM Conversion Detail,Подробно о преобразовании единиц измерения,
 Variant Field,Поле вариантов,
 A logical Warehouse against which stock entries are made.,"Логический Склад, по которому сделаны складские записи",
 Warehouse Detail,Подробности склада,
 Warehouse Name,Название склада,
 Warehouse Contact Info,Контактная информация склада,
-PIN,ШТЫРЬ,
-ISS-.YYYY.-,МКС-.ГГГГ.-,
+PIN,PIN,
+ISS-.YYYY.-,ISS-.YYYY.-,
 Raised By (Email),Создал (Email),
 Issue Type,Тип вопроса,
 Issue Split From,Выпуск Сплит От,
@@ -8433,21 +8435,21 @@
 Raised By,Создал,
 From Company,От компании,
 Rename Tool,Переименование файлов,
-Utilities,Инженерное оборудование,
+Utilities,Утилиты,
 Type of document to rename.,"Вид документа, переименовать.",
 File to Rename,Файл Переименовать,
 "Attach .csv file with two columns, one for the old name and one for the new name","Прикрепите файл .csv с двумя колоннами, одна для старого имени и одина для нового названия",
 Rename Log,Переименовать Входить,
-SMS Log,СМС-журнал,
+SMS Log,SMS-журнал,
 Sender Name,Имя отправителя,
-Sent On,Направлено на,
-No of Requested SMS,Кол-во запрошенных СМС,
+Sent On,Отправлено на,
+No of Requested SMS,Кол-во запрошенных SMS,
 Requested Numbers,Запрошенные номера,
-No of Sent SMS,Кол-во отправленных СМС,
+No of Sent SMS,Кол-во отправленных SMS,
 Sent To,Отправить,
 Absent Student Report,Отчет о пропуске занятия,
 Assessment Plan Status,Статус плана оценки,
-Asset Depreciation Ledger,Износ Леджер активов,
+Asset Depreciation Ledger,Книга амортизации основных средств,
 Asset Depreciations and Balances,Активов Амортизация и противовесов,
 Available Stock for Packing Items,Доступные Запасы для Комплектации Продуктов,
 Bank Clearance Summary,Банк уплата по счетам итого,
@@ -8491,40 +8493,40 @@
 Final Assessment Grades,Итоговые оценки,
 Fixed Asset Register,Регистр фиксированных активов,
 Gross and Net Profit Report,Отчет о валовой и чистой прибыли,
-GST Itemised Purchase Register,Регистр покупки в GST,
-GST Itemised Sales Register,Регистр продаж GST,
-GST Purchase Register,Регистр закупок GST,
-GST Sales Register,Реестр продаж GST,
+GST Itemised Purchase Register,Подробный реестр покупок с НДС,
+GST Itemised Sales Register,Подробный реестр продаж с НДС,
+GST Purchase Register,Реестр закупок с НДС,
+GST Sales Register,Реестр продаж с НДС,
 GSTR-1,GSTR-1,
 GSTR-2,GSTR-2,
 Hotel Room Occupancy,Гостиничный номер,
-HSN-wise-summary of outward supplies,HSN-краткая сводка внешних поставок,
+HSN-wise-summary of outward supplies,ГСК-краткая сводка внешних поставок,
 Inactive Customers,Неактивные клиенты,
 Inactive Sales Items,Неактивные позиции продаж,
 IRS 1099,IRS 1099,
 Issued Items Against Work Order,Продукты выпущенные под заказ,
 Projected Quantity as Source,Планируемое количество как источник,
 Item Balance (Simple),Остаток продукта (простой),
-Item Price Stock,Цена товара,
+Item Price Stock,Стоимость продукта на складе,
 Item Prices,Цены продукта,
 Item Shortage Report,Отчет о нехватке продуктов,
 Item Variant Details,Подробности модификации продукта,
 Item-wise Price List Rate,Цена продукта в прайс-листе,
-Item-wise Purchase History,Пункт мудрый История покупок,
-Item-wise Purchase Register,Пункт мудрый Покупка Зарегистрироваться,
-Item-wise Sales History,История продаж продуктов,
-Item-wise Sales Register,Пункт мудрый Продажи Зарегистрироваться,
+Item-wise Purchase History,История покупок по продуктам,
+Item-wise Purchase Register,Реестр покупок по продуктам,
+Item-wise Sales History,История продаж по продуктам,
+Item-wise Sales Register,Реестр продаж по продуктам,
 Items To Be Requested,Запрашиваемые продукты,
 Reserved,Зарезервировано,
 Itemwise Recommended Reorder Level,Рекомендация пополнения уровня продукта,
-Lead Details,Подробнее об Обращении,
-Lead Owner Efficiency,Эффективность Ответственного за Обращения,
+Lead Details,Подробнее об лиде,
+Lead Owner Efficiency,Эффективность ответственного за лид,
 Loan Repayment and Closure,Погашение и закрытие кредита,
 Loan Security Status,Состояние безопасности ссуды,
 Lost Opportunity,Потерянная возможность,
 Maintenance Schedules,Графики технического обслуживания,
 Material Requests for which Supplier Quotations are not created,"Запросы на Материалы, для которых не создаются Предложения Поставщика",
-Monthly Attendance Sheet,Ежемесячная посещаемость Лист,
+Monthly Attendance Sheet,Табель ежемесячной посещаемости,
 Open Work Orders,Открытые рабочие задания,
 Qty to Deliver,Кол-во для доставки,
 Patient Appointment Analytics,Аналитика записи пациентов,
@@ -8540,11 +8542,11 @@
 Project wise Stock Tracking ,Отслеживание затрат по проектам,
 Prospects Engaged But Not Converted,"Перспективные, но не работающие",
 Purchase Analytics,Аналитика поставок,
-Purchase Invoice Trends,Счета-фактуры Тенденции,
+Purchase Invoice Trends,Тенденции на закупки,
 Qty to Receive,Кол-во на получение,
 Received Qty Amount,Полученная Кол-во Сумма,
 Billed Qty,Кол-во,
-Purchase Order Trends,Заказ на покупку Тенденции,
+Purchase Order Trends,Тенденции закупок,
 Purchase Receipt Trends,Динамика Получения Поставок,
 Purchase Register,Покупка Становиться на учет,
 Quotation Trends,Динамика предложений,
@@ -8554,14 +8556,14 @@
 Qty to Transfer,Кол-во для передачи,
 Salary Register,Доход Регистрация,
 Sales Analytics,Аналитика продаж,
-Sales Invoice Trends,Расходная накладная тенденции,
-Sales Order Trends,Динамика по Сделкам,
+Sales Invoice Trends,Тенденции по расходам,
+Sales Order Trends,Динамика по сделкам,
 Sales Partner Commission Summary,Сводка комиссий партнеров по продажам,
 Sales Partner Target Variance based on Item Group,Целевое отклонение партнера по продажам на основе группы товаров,
 Sales Partner Transaction Summary,Сводка по сделкам с партнерами по продажам,
-Sales Partners Commission,Комиссионные Партнеров по продажам,
+Sales Partners Commission,Комиссия партнеров по продажам,
 Invoiced Amount (Exclusive Tax),Сумма счета (без учета налога),
-Average Commission Rate,Средний Уровень Комиссии,
+Average Commission Rate,Средний уровень комиссии,
 Sales Payment Summary,Сводка по продажам,
 Sales Person Commission Summary,Резюме комиссии по продажам,
 Sales Person Target Variance Based On Item Group,"Целевое отклонение продавца, основанное на группе товаров",
@@ -8574,15 +8576,15 @@
 Stock and Account Value Comparison,Сравнение стоимости акций и счетов,
 Stock Projected Qty,Прогнозируемое количество запасов,
 Student and Guardian Contact Details,Контактная информация студента и родителя/попечителя,
-Student Batch-Wise Attendance,Student порционно Посещаемость,
-Student Fee Collection,Student Fee Collection,
+Student Batch-Wise Attendance,Посещаемость студентов,
+Student Fee Collection,Сбор студенческой платы,
 Student Monthly Attendance Sheet,Ежемесячная посещаемость студентов,
 Subcontracted Item To Be Received,"Субподрядный предмет, подлежащий получению",
 Subcontracted Raw Materials To Be Transferred,Субподрядное сырье для передачи,
-Supplier Ledger Summary,Список поставщиков,
+Supplier Ledger Summary,Сводка книги поставщиков,
 Supplier-Wise Sales Analytics,Аналитика продаж в разрезе поставщиков,
 Support Hour Distribution,Распределение поддержки,
-TDS Computation Summary,Резюме вычислений TDS,
+TDS Computation Summary,Сводка расчетов TDS,
 TDS Payable Monthly,TDS Payable Monthly,
 Territory Target Variance Based On Item Group,Целевое отклонение территории на основе группы товаров,
 Territory-wise Sales,Продажи по территории,
@@ -8624,13 +8626,13 @@
 {} Assets created for {},"{} Активы, созданные для {}",
 {0} Number {1} is already used in {2} {3},{0} Номер {1} уже используется в {2} {3},
 Update Bank Clearance Dates,Обновить даты оформления банка,
-Healthcare Practitioner: ,Практикующий врач:,
-Lab Test Conducted: ,Проведены лабораторные испытания:,
-Lab Test Event: ,Событие лабораторных испытаний:,
-Lab Test Result: ,Результат лабораторного тестирования:,
-Clinical Procedure conducted: ,Проведенная клиническая процедура:,
+Healthcare Practitioner: ,Практикующий врач: ,
+Lab Test Conducted: ,Проведены лабораторные испытания: ,
+Lab Test Event: ,Событие лабораторных испытаний: ,
+Lab Test Result: ,Результат лабораторного тестирования: ,
+Clinical Procedure conducted: ,Проведенная клиническая процедура: ,
 Therapy Session Charges: {0},Стоимость сеанса терапии: {0},
-Therapy: ,Терапия:,
+Therapy: ,Терапия: ,
 Therapy Plan: ,План терапии:,
 Total Counts Targeted: ,Общее количество целевых:,
 Total Counts Completed: ,Всего завершено подсчетов:,
@@ -8679,16 +8681,16 @@
 Days,Дней,
 Months,Месяцы,
 Book Deferred Entries Via Journal Entry,Книга отложенных записей через запись в журнале,
-Submit Journal Entries,Отправить записи журнала,
+Submit Journal Entries,Утвердить журнальные записи,
 If this is unchecked Journal Entries will be saved in a Draft state and will have to be submitted manually,"Если этот флажок не установлен, записи журнала будут сохранены в состоянии черновик, и их придется отправлять вручную.",
 Enable Distributed Cost Center,Включить центр распределенных затрат,
 Distributed Cost Center,Центр распределенных затрат,
 Dunning,Даннинг,
-DUNN-.MM.-.YY.-,DUNN-.MM .-. YY.-,
+DUNN-.MM.-.YY.-,DUNN-.MM.-.YY.-,
 Overdue Days,Просроченные дни,
 Dunning Type,Тип напоминания,
 Dunning Fee,Плата за напоминание,
-Dunning Amount,Сумма напоминаний,
+Dunning Amount,Количество напоминаний,
 Resolved,Решено,
 Unresolved,Нерешенный,
 Printing Setting,Настройка печати,
@@ -8714,12 +8716,12 @@
 Opening Amount,Начальная сумма,
 Closing Amount,Заключительная сумма,
 POS Closing Entry Taxes,Налоги на вход при закрытии торговых точек,
-POS Invoice,Счет-фактура POS,
+POS Invoice,Счет POS,
 ACC-PSINV-.YYYY.-,ACC-PSINV-.YYYY.-,
-Consolidated Sales Invoice,Консолидированный счет-фактура продажи,
+Consolidated Sales Invoice,Консолидированный счет продажи,
 Return Against POS Invoice,Возврат по счету POS,
 Consolidated,Консолидированный,
-POS Invoice Item,Позиция счета-фактуры POS,
+POS Invoice Item,Позиция счета POS,
 POS Invoice Merge Log,Журнал слияния счетов POS,
 POS Invoices,Счета POS,
 Consolidated Credit Note,Консолидированная кредитная нота,
@@ -8730,7 +8732,7 @@
 POS Payment Method,Способ оплаты POS,
 Payment Methods,Способы оплаты,
 Process Statement Of Accounts,Выписка по счетам,
-General Ledger Filters,Фильтры Главной книги,
+General Ledger Filters,Фильтры бухгалтерской книги,
 Customers,Клиенты,
 Select Customers By,Выбрать клиентов по,
 Fetch Customers,Привлечь клиентов,
@@ -8739,7 +8741,7 @@
 Include Ageing Summary,Включить сводку по старению,
 Enable Auto Email,Включить автоматическую электронную почту,
 Filter Duration (Months),Продолжительность фильтра (мес.),
-CC To,CC To,
+CC To,Копия,
 Help Text,Текст справки,
 Emails Queued,Электронные письма в очереди,
 Process Statement Of Accounts Customer,Выписка по счетам клиента,
@@ -8756,7 +8758,7 @@
 Registered Regular,Зарегистрированный Обычный,
 Registered Composition,Зарегистрированный состав,
 Unregistered,Незарегистрированный,
-SEZ,ОЭЗ,
+SEZ,ОЭС,
 Overseas,За границей,
 UIN Holders,Держатели UIN,
 With Payment of Tax,С уплатой налога,
@@ -8774,7 +8776,7 @@
 01-Sales Return,01-Возврат продажи,
 02-Post Sale Discount,02-Скидка после продажи,
 03-Deficiency in services,03-Дефицит услуг,
-04-Correction in Invoice,04-Исправление в счете-фактуре,
+04-Correction in Invoice,04-Исправление в счете,
 05-Change in POS,05-Изменение в POS,
 06-Finalization of Provisional assessment,06-Завершение предварительной оценки,
 07-Others,07-другие,
@@ -8797,7 +8799,7 @@
 Customer GSTIN,Клиент GSTIN,
 GST Transporter ID,GST Transporter ID,
 Distance (in km),Расстояние (в км),
-Road,дорога,
+Road,Дорога,
 Air,Воздух,
 Rail,Железнодорожный,
 Ship,Корабль,
@@ -8811,9 +8813,9 @@
 Subscription End Date,Дата окончания подписки,
 Follow Calendar Months,Следите за календарными месяцами,
 If this is checked subsequent new invoices will be created on calendar  month and quarter start dates irrespective of current invoice start date,"Если этот флажок установлен, последующие новые счета будут создаваться в даты начала календарного месяца и квартала независимо от даты начала текущего счета.",
-Generate New Invoices Past Due Date,Создание просроченных новых счетов-фактур,
+Generate New Invoices Past Due Date,Создание просроченных новых счетов,
 New invoices will be generated as per schedule even if current invoices are unpaid or past due date,"Новые счета будут генерироваться в соответствии с графиком, даже если текущие счета не оплачены или просрочены.",
-Document Type ,тип документа,
+Document Type ,Тип документа ,
 Subscription Price Based On,Цена подписки на основе,
 Fixed Rate,Фиксированная ставка,
 Based On Price List,На основании прайс-листа,
@@ -8838,18 +8840,18 @@
 Opportunity Lost Reason Detail,Подробная информация о причине потери возможности,
 Access Token Secret,Секрет токена доступа,
 Add to Topics,Добавить в темы,
-...Adding Article to Topics,... Добавление статьи в темы,
+...Adding Article to Topics,...Добавление статьи в темы,
 Add Article to Topics,Добавить статью в темы,
 This article is already added to the existing topics,Эта статья уже добавлена в существующие темы,
 Add to Programs,Добавить в программы,
-Programs,Программ,
-...Adding Course to Programs,... Добавление курса в программы,
+Programs,Программы,
+...Adding Course to Programs,...Добавление курса в программы,
 Add Course to Programs,Добавить курс в программы,
 This course is already added to the existing programs,Этот курс уже добавлен в существующие программы,
 Learning Management System Settings,Настройки системы управления обучением,
 Enable Learning Management System,Включить систему управления обучением,
 Learning Management System Title,Название системы управления обучением,
-...Adding Quiz to Topics,... Добавление викторины к темам,
+...Adding Quiz to Topics,...Добавление викторины к темам,
 Add Quiz to Topics,Добавить тест к темам,
 This quiz is already added to the existing topics,Этот тест уже добавлен в существующие темы,
 Enable Admission Application,Включить заявку на поступление,
@@ -8859,7 +8861,7 @@
 Attendance Based On,Посещаемость на основе,
 Check this to mark the student as present in case the student is not attending the institute to participate or represent the institute in any event.\n\n,"Установите этот флажок, чтобы отметить студента как присутствующего на случай, если он не посещает институт для участия или представления института в любом случае.",
 Add to Courses,Добавить в курсы,
-...Adding Topic to Courses,... Добавление темы в курсы,
+...Adding Topic to Courses,...Добавление темы в курсы,
 Add Topic to Courses,Добавить тему в курсы,
 This topic is already added to the existing courses,Эта тема уже добавлена в существующие курсы,
 "If Shopify does not have a customer in the order, then while syncing the orders, the system will consider the default customer for the order","Если Shopify не имеет клиента в заказе, то при синхронизации заказов система будет рассматривать клиента по умолчанию для заказа.",
@@ -8889,7 +8891,7 @@
 Clinical Procedure Rate,Скорость клинической процедуры,
 Check this if the Clinical Procedure is billable and also set the rate.,"Отметьте это, если клиническая процедура оплачивается, а также установите ставку.",
 Check this if the Clinical Procedure utilises consumables. Click ,"Проверьте это, если в клинической процедуре используются расходные материалы. Нажмите",
- to know more,знать больше,
+ to know more,узнать больше,
 "You can also set the Medical Department for the template. After saving the document, an Item will automatically be created for billing this Clinical Procedure. You can then use this template while creating Clinical Procedures for Patients. Templates save you from filling up redundant data every single time. You can also create templates for other operations like Lab Tests, Therapy Sessions, etc.","Вы также можете указать для шаблона Медицинский отдел. После сохранения документа будет автоматически создана Позиция для выставления счета за эту Клиническую процедуру. Затем вы можете использовать этот шаблон при создании клинических процедур для пациентов. Шаблоны избавляют вас от постоянного заполнения избыточными данными. Вы также можете создавать шаблоны для других операций, таких как лабораторные тесты, сеансы терапии и т. Д.",
 Descriptive Test Result,Описательный результат теста,
 Allow Blank,Разрешить пустой,
@@ -8945,21 +8947,21 @@
 Worksheet Instructions,Инструкции по рабочему листу,
 Result Legend Print,Печать легенды результатов,
 Print Position,Позиция печати,
-Bottom,Дно,
-Top,верхний,
-Both,Обе,
+Bottom,Низ,
+Top,Верх,
+Both,Оба,
 Result Legend,Легенда результата,
 Lab Tests,Лабораторные тесты,
 No Lab Tests found for the Patient {0},Для пациента не найдено лабораторных тестов {0},
-"Did not send SMS, missing patient mobile number or message content.","Не отправлено SMS, отсутствует номер мобильного телефона пациента или содержание сообщения.",
+"Did not send SMS, missing patient mobile number or message content.","SMS не отправлено, отсутствует номер мобильного телефона пациента или содержание сообщения.",
 No Lab Tests created,Лабораторные тесты не созданы,
-Creating Lab Tests...,Создание лабораторных тестов ...,
+Creating Lab Tests...,Создание лабораторных тестов...,
 Lab Test Group Template,Шаблон группы лабораторных тестов,
 Add New Line,Добавить новую строку,
 Secondary UOM,Вторичная единица измерения,
 "<b>Single</b>: Results which require only a single input.\n<br>\n<b>Compound</b>: Results which require multiple event inputs.\n<br>\n<b>Descriptive</b>: Tests which have multiple result components with manual result entry.\n<br>\n<b>Grouped</b>: Test templates which are a group of other test templates.\n<br>\n<b>No Result</b>: Tests with no results, can be ordered and billed but no Lab Test will be created. e.g.. Sub Tests for Grouped results","<b>Одиночный</b> : результаты, требующие только одного ввода.<br> <b>Составные</b> : результаты, требующие ввода нескольких событий.<br> <b>Описательный</b> : тесты с несколькими компонентами результатов с вводом результатов вручную.<br> <b>Сгруппированы</b> : тестовые шаблоны, которые представляют собой группу других тестовых шаблонов.<br> <b>Нет результата</b> : тесты, не получившие результатов, можно заказать и выставить счет, но лабораторный тест не будет создан. например. Подтесты для сгруппированных результатов",
 "If unchecked, the item will not be available in Sales Invoices for billing but can be used in group test creation. ","Если этот флажок не установлен, элемент не будет доступен в счетах-фактурах для выставления счетов, но его можно будет использовать при создании группового теста.",
-Description ,Описание,
+Description ,Описание ,
 Descriptive Test,Описательный тест,
 Group Tests,Групповые тесты,
 Instructions to be printed on the worksheet,Инструкции для печати на рабочем листе,
@@ -9013,7 +9015,7 @@
 Select variant item code for the template item {0},Выберите вариант кода товара для шаблона товара {0},
 Downtime Entry,Запись о простоях,
 DT-,DT-,
-Workstation / Machine,Рабочая станция / машина,
+Workstation / Machine,Рабочее место / машина,
 Operator,Оператор,
 In Mins,В минутах,
 Downtime Reason,Причина простоя,
@@ -9045,7 +9047,7 @@
 Send Invoice with Email,Отправить счет по электронной почте,
 Membership Print Format,Формат печати членства,
 Invoice Print Format,Формат печати счета,
-Revoke <Key></Key>,Отозвать&lt;Key&gt;&lt;/Key&gt;,
+Revoke <Key></Key>,Отозвать <Key></Key>,
 You can learn more about memberships in the manual. ,Вы можете узнать больше о членстве в руководстве.,
 ERPNext Docs,ERPСледующие документы,
 Regenerate Webhook Secret,Восстановить секрет веб-перехватчика,
@@ -9064,23 +9066,23 @@
 Rented To Date,Сдано на дату,
 Monthly Eligible Amount,Ежемесячная приемлемая сумма,
 Total Eligible HRA Exemption,Полное соответствие требованиям HRA,
-Validating Employee Attendance...,Проверка явки сотрудников ...,
+Validating Employee Attendance...,Проверка явки сотрудников...,
 Submitting Salary Slips and creating Journal Entry...,Отправка ведомостей о заработной плате и создание записи в журнале ...,
 Calculate Payroll Working Days Based On,Расчет рабочих дней для расчета заработной платы на основе,
 Consider Unmarked Attendance As,Считайте неотмеченную посещаемость как,
 Fraction of Daily Salary for Half Day,Доля дневной заработной платы за полдня,
 Component Type,Тип компонента,
-Provident Fund,резервный фонд,
+Provident Fund,Резервный фонд,
 Additional Provident Fund,Дополнительный резервный фонд,
 Provident Fund Loan,Ссуда из фонда обеспечения персонала,
 Professional Tax,Профессиональный налог,
 Is Income Tax Component,Компонент подоходного налога,
 Component properties and references ,Свойства компонентов и ссылки,
-Additional Salary ,Дополнительная зарплата,
+Additional Salary ,Дополнительная зарплата ,
 Unmarked days,Неотмеченные дни,
 Absent Days,Отсутствующие дни,
 Conditions and Formula variable and example,"Условия и формула, переменная и пример",
-Feedback By,Отзыв Автор,
+Feedback By,Отзыв от,
 Manufacturing Section,Производственный отдел,
 "By default, the Customer Name is set as per the Full Name entered. If you want Customers to be named by a ","По умолчанию имя клиента устанавливается в соответствии с введенным полным именем. Если вы хотите, чтобы имена клиентов",
 Configure the default Price List when creating a new Sales transaction. Item prices will be fetched from this Price List.,Настройте прайс-лист по умолчанию при создании новой транзакции продаж. Цены на товары будут взяты из этого прейскуранта.,
@@ -9097,13 +9099,13 @@
 Quotation Lost Reason Detail,Расценки на потерю причины,
 Enable Variants,Включить варианты,
 Save Quotations as Draft,Сохранить предложения как черновик,
-MAT-DN-RET-.YYYY.-,МАТ-ДН-РЕТ-.ГГГГ.-,
+MAT-DN-RET-.YYYY.-,MAT-DN-RET-.YYYY.-,
 Please Select a Customer,"Пожалуйста, выберите клиента",
 Against Delivery Note Item,Против позиции накладной,
-Is Non GST ,Без НДС,
+Is Non GST ,Без НДС ,
 Image Description,Описание изображения,
 Transfer Status,Статус передачи,
-MAT-PR-RET-.YYYY.-,МАТ-ПР-РЕТ-.ГГГГ.-,
+MAT-PR-RET-.YYYY.-,MAT-PR-RET-.YYYY.-,
 Track this Purchase Receipt against any Project,Отслеживайте эту квитанцию о покупке для любого проекта,
 Please Select a Supplier,"Пожалуйста, выберите поставщика",
 Add to Transit,Добавить в общественный транспорт,
@@ -9146,8 +9148,8 @@
 Select Assessment Template,Выберите шаблон экзамена,
  out of ,снаружи,
 Select Assessment Parameter,Выберите параметр оценки,
-Gender: ,Пол:,
-Contact: ,Контакты:,
+Gender: ,Пол: ,
+Contact: ,Контакты: ,
 Total Therapy Sessions: ,Всего сеансов терапии:,
 Monthly Therapy Sessions: ,Ежемесячные сеансы терапии:,
 Patient Profile,Профиль пациента,
@@ -9177,7 +9179,7 @@
 There was an error saving the document.,При сохранении документа произошла ошибка.,
 You must select a customer before adding an item.,Перед добавлением товара необходимо выбрать клиента.,
 Please Select a Company,"Пожалуйста, выберите компанию",
-Active Leads,Активные лиды,
+Active Leads,Активные обращения,
 Please Select a Company.,"Пожалуйста, выберите компанию.",
 BOM Operations Time,Время операций по спецификации,
 BOM ID,Идентификатор спецификации,
@@ -9206,12 +9208,12 @@
 Projected Quantity,Прогнозируемое количество,
  Total Sales Amount,Общая сумма продаж,
 Job Card Summary,Сводка карточки вакансии,
-Id,Я бы,
+Id,Id,
 Time Required (In Mins),Требуемое время (в минутах),
 From Posting Date,С даты публикации,
 To Posting Date,До даты публикации,
-No records found,записей не найдено,
-Customer/Lead Name,Имя клиента / лида,
+No records found,Записей не найдено,
+Customer/Lead Name,Имя клиента / обратившегося,
 Unmarked Days,Дни без отметок,
 Jan,Янв,
 Feb,Фев,
@@ -9219,9 +9221,9 @@
 Apr,Апр,
 Aug,Авг,
 Sep,Сен,
-Oct,Октябрь,
+Oct,Окт,
 Nov,Ноя,
-Dec,Декабрь,
+Dec,Дек,
 Summarized View,Обобщенный вид,
 Production Planning Report,Отчет о производственном планировании,
 Order Qty,Кол-во заказа,
@@ -9242,7 +9244,7 @@
 Tasks Overdue,Просроченные задачи,
 Completion,Завершение,
 Provident Fund Deductions,Отчисления в резервный фонд,
-Purchase Order Analysis,Анализ заказа на закупку,
+Purchase Order Analysis,Анализ заказов на закупку,
 From and To Dates are required.,Укажите даты от и до.,
 To Date cannot be before From Date.,Дата не может быть раньше даты начала.,
 Qty to Bill,Кол-во к счету,
@@ -9267,7 +9269,7 @@
 Amount Delivered,Сумма доставки,
 Delay (in Days),Задержка (в днях),
 Group by Sales Order,Группировать по заказу на продажу,
- Sales Value,Объем продаж,
+ Sales Value, Объем продаж,
 Stock Qty vs Serial No Count,Кол-во на складе по сравнению с серийным номером,
 Serial No Count,Серийный номер,
 Work Order Summary,Сводка заказа на работу,
@@ -9311,7 +9313,7 @@
 URL can only be a string,URL может быть только строкой,
 "Here is your webhook secret, this will be shown to you only once.","Вот ваш секрет веб-перехватчика, он будет показан вам только один раз.",
 The payment for this membership is not paid. To generate invoice fill the payment details,Плата за это членство не взимается. Для создания счета заполните платежные реквизиты,
-An invoice is already linked to this document,Счет-фактура уже привязана к этому документу,
+An invoice is already linked to this document,Счет уже привязан к этому документу,
 No customer linked to member {},Ни один клиент не связан с участником {},
 You need to set <b>Debit Account</b> in Membership Settings,Вам необходимо установить <b>дебетовую учетную запись</b> в настройках членства,
 You need to set <b>Default Company</b> for invoicing in Membership Settings,Вам необходимо указать <b>компанию</b> по <b>умолчанию</b> для выставления счетов в настройках членства.,
@@ -9320,9 +9322,9 @@
 A customer is already linked to this Member,Клиент уже привязан к этому участнику,
 End Date must not be lesser than Start Date,Дата окончания не должна быть меньше даты начала.,
 Employee {0} already has Active Shift {1}: {2},Сотрудник {0} уже имеет активную смену {1}: {2},
- from {0},от {0},
- to {0},в {0},
-Please select Employee first.,"Пожалуйста, сначала выберите Сотрудник.",
+ from {0}, от {0},
+ to {0}, в {0},
+Please select Employee first.,"Пожалуйста, сначала выберите сотрудника.",
 Please set {0} for the Employee or for Department: {1},Установите {0} для сотрудника или отдела: {1},
 To Date should be greater than From Date,"Дата до должна быть больше, чем Дата",
 Employee Onboarding: {0} is already for Job Applicant: {1},Прием на работу сотрудника: {0} уже для соискателя: {1},
@@ -9335,7 +9337,7 @@
 Category-wise Asset Value,Стоимость актива по категориям,
 Total Assets,Итого активы,
 New Assets (This Year),Новые активы (в этом году),
-Row #{}: Depreciation Posting Date should not be equal to Available for Use Date.,Строка № {}: Дата проводки амортизации не должна совпадать с датой доступности для использования.,
+Row #{}: Depreciation Posting Date should not be equal to Available for Use Date.,Строка №{}: Дата проводки амортизации не должна совпадать с датой доступности для использования.,
 Incorrect Date,Неправильная дата,
 Invalid Gross Purchase Amount,Неверная сумма покупки брутто,
 There are active maintenance or repairs against the asset. You must complete all of them before cancelling the asset.,"Активно проводится техническое обслуживание или ремонт актива. Вы должны выполнить их все, прежде чем аннулировать актив.",
@@ -9345,7 +9347,7 @@
 Mins,Мин,
 by,по,
 Back to,Вернуться к,
-Enrolling...,Регистрация ...,
+Enrolling...,Регистрация...,
 You have successfully enrolled for the program ,Вы успешно записались на программу,
 Enrolled,Зарегистрирован,
 Watch Intro,Смотреть вступление,
@@ -9376,14 +9378,14 @@
 Invalid Item,Недействительный товар,
 Row #{}: You cannot add postive quantities in a return invoice. Please remove item {} to complete the return.,"Строка № {}: нельзя добавлять положительные количества в счет-фактуру на возврат. Удалите товар {}, чтобы завершить возврат.",
 The selected change account {} doesn't belongs to Company {}.,Выбранный аккаунт изменения {} не принадлежит Компании {}.,
-Atleast one invoice has to be selected.,Необходимо выбрать хотя бы один счет-фактуру.,
+Atleast one invoice has to be selected.,Необходимо выбрать хотя бы один счет.,
 Payment methods are mandatory. Please add at least one payment method.,"Способы оплаты обязательны. Пожалуйста, добавьте хотя бы один способ оплаты.",
 Please select a default mode of payment,"Пожалуйста, выберите способ оплаты по умолчанию",
 You can only select one mode of payment as default,По умолчанию вы можете выбрать только один способ оплаты.,
 Missing Account,Отсутствует аккаунт,
 Customers not selected.,Клиенты не выбраны.,
 Statement of Accounts,Выписка со счетов,
-Ageing Report Based On ,Отчет о старении на основе,
+Ageing Report Based On ,Отчет о старении на основе ,
 Please enter distributed cost center,"Пожалуйста, введите распределенное МВЗ",
 Total percentage allocation for distributed cost center should be equal to 100,Общее процентное распределение для распределенного МВЗ должно быть равно 100.,
 Cannot enable Distributed Cost Center for a Cost Center already allocated in another Distributed Cost Center,"Невозможно включить центр распределенных затрат для центра затрат, уже выделенного в другом центре распределенных затрат",
@@ -9394,7 +9396,7 @@
 Trial Period Start date cannot be after Subscription Start Date,Дата начала пробного периода не может быть позже даты начала подписки,
 Subscription End Date must be after {0} as per the subscription plan,Дата окончания подписки должна быть позже {0} в соответствии с планом подписки.,
 Subscription End Date is mandatory to follow calendar months,Дата окончания подписки обязательна после календарных месяцев.,
-Row #{}: POS Invoice {} is not against customer {},Строка № {}: счет-фактура торговой точки {} не выставлен клиенту {},
+Row #{}: POS Invoice {} is not against customer {},Строка № {}: счет торговой точки {} не выставлен клиенту {},
 Row #{}: POS Invoice {} is not submitted yet,Строка № {}: Счет POS {} еще не отправлен,
 Row #{}: POS Invoice {} has been {},Строка № {}: Счет POS {} был {},
 No Supplier found for Inter Company Transactions which represents company {0},"Для транзакций между компаниями не найден поставщик, представляющий компанию {0}",
@@ -9412,11 +9414,11 @@
 Please enter a coupon code,"Пожалуйста, введите код купона",
 Please enter a valid coupon code,"Пожалуйста, введите действительный код купона",
 Invalid Child Procedure,Недействительная детская процедура,
-Import Italian Supplier Invoice.,Импортировать счет-фактуру итальянского поставщика.,
+Import Italian Supplier Invoice.,Импортировать счет итальянского поставщика.,
 "Valuation Rate for the Item {0}, is required to do accounting entries for {1} {2}.","Курс оценки для Предмета {0}, необходим для ведения бухгалтерских записей для {1} {2}.",
  Here are the options to proceed:,"Вот варианты, чтобы продолжить:",
 "If the item is transacting as a Zero Valuation Rate item in this entry, please enable 'Allow Zero Valuation Rate' in the {0} Item table.","Если в этой записи предмет используется как предмет с нулевой оценкой, включите параметр «Разрешить нулевую ставку оценки» в таблице предметов {0}.",
-"If not, you can Cancel / Submit this entry ","Если нет, вы можете отменить / отправить эту запись",
+"If not, you can Cancel / Submit this entry ","Если нет, вы можете отменить / утвердить эту запись",
  performing either one below:,выполнение одного из следующих:,
 Create an incoming stock transaction for the Item.,Создайте проводку входящего запаса для Товара.,
 Mention Valuation Rate in the Item master.,Упомяните коэффициент оценки в мастере предметов.,
@@ -9475,7 +9477,7 @@
 Row #{0}: Child Item should not be a Product Bundle. Please remove Item {1} and Save,Строка № {0}: дочерний элемент не должен быть набором продукта. Удалите элемент {1} и сохраните,
 Credit limit reached for customer {0},Достигнут кредитный лимит для клиента {0},
 Could not auto create Customer due to the following missing mandatory field(s):,Не удалось автоматически создать клиента из-за отсутствия следующих обязательных полей:,
-Please create Customer from Lead {0}.,Создайте клиента из лида {0}.,
+Please create Customer from Lead {0}.,Создайте клиента из обращения {0}.,
 Mandatory Missing,Обязательно отсутствует,
 Please set Payroll based on in Payroll settings,"Пожалуйста, установите Расчет заработной платы на основе в настройках Заработной платы",
 Additional Salary: {0} already exist for Salary Component: {1} for period {2} and {3},Дополнительная зарплата: {0} уже существует для компонента зарплаты: {1} за период {2} и {3},
@@ -9486,10 +9488,10 @@
 Payroll date can not be greater than employee's relieving date.,Дата расчета не может быть больше даты увольнения сотрудника.,
 Row #{0}: Please enter the result value for {1},Строка № {0}: введите значение результата для {1},
 Mandatory Results,Обязательные результаты,
-Sales Invoice or Patient Encounter is required to create Lab Tests,Счет-фактура продажи или встреча с пациентом необходимы для создания лабораторных тестов,
+Sales Invoice or Patient Encounter is required to create Lab Tests,Счет продажи или встреча с пациентом необходимы для создания лабораторных тестов,
 Insufficient Data,Недостаточные данные,
 Lab Test(s) {0} created successfully,Лабораторные тесты {0} успешно созданы,
-Test :,Контрольная работа :,
+Test :,Тест :,
 Sample Collection {0} has been created,Коллекция образцов {0} создана,
 Normal Range: ,Нормальный диапазон:,
 Row #{0}: Check Out datetime cannot be less than Check In datetime,Строка № {0}: Дата и время выезда не может быть меньше даты и времени выезда.,
@@ -9504,7 +9506,7 @@
 {0} on {1},{0} в {1},
 {0} with {1},{0} с {1},
 Appointment Confirmation Message Not Sent,Сообщение с подтверждением встречи не отправлено,
-"SMS not sent, please check SMS Settings","СМС не отправлено, проверьте настройки СМС",
+"SMS not sent, please check SMS Settings","SMS не отправлено, проверьте настройки SMS",
 Healthcare Service Unit Type cannot have both {0} and {1},Тип единицы медицинского обслуживания не может содержать одновременно {0} и {1},
 Healthcare Service Unit Type must allow atleast one among {0} and {1},Тип единицы медицинского обслуживания должен допускать хотя бы одно из {0} и {1},
 Set Response Time and Resolution Time for Priority {0} in row {1}.,Задайте время ответа и время разрешения для приоритета {0} в строке {1}.,
@@ -9532,7 +9534,7 @@
 Preview Email,Предварительный просмотр электронной почты,
 Please select a Supplier,"Пожалуйста, выберите поставщика",
 Supplier Lead Time (days),Срок поставки поставщика (дни),
-"Home, Work, etc.","Дом, работа и т. Д.",
+"Home, Work, etc.","Дом, Работа и т.д.",
 Exit Interview Held On,Завершить собеседование,
 Condition and formula,Условие и формула,
 Sets 'Target Warehouse' in each row of the Items table.,Устанавливает «Целевой склад» в каждой строке таблицы «Предметы».,
@@ -9579,8 +9581,8 @@
 Show Inclusive Tax in Print,Показать включенный налог в печати,
 Only select this if you have set up the Cash Flow Mapper documents,"Выбирайте это только в том случае, если вы настроили документы Cash Flow Mapper.",
 Payment Channel,Платежный канал,
-Is Purchase Order Required for Purchase Invoice & Receipt Creation?,Требуется ли заказ на покупку для создания счета-фактуры и квитанции?,
-Is Purchase Receipt Required for Purchase Invoice Creation?,Требуется ли квитанция о покупке для создания счета-фактуры?,
+Is Purchase Order Required for Purchase Invoice & Receipt Creation?,Требуется ли заказ на покупку для создания счета и квитанции?,
+Is Purchase Receipt Required for Purchase Invoice Creation?,Требуется ли квитанция о покупке для создания счета?,
 Maintain Same Rate Throughout the Purchase Cycle,Поддерживайте одинаковую ставку на протяжении всего цикла покупки,
 Allow Item To Be Added Multiple Times in a Transaction,Разрешить добавление элемента несколько раз в транзакцию,
 Suppliers,Поставщики,
@@ -9592,7 +9594,7 @@
 Initiator Name,Имя инициатора,
 Till Number,До номера,
 Sandbox,Песочница,
- Online PassKey,Интернет-пароль,
+ Online PassKey, Интернет-пароль,
 Security Credential,Учетные данные безопасности,
 Get Account Balance,Получить остаток на счете,
 Please set the initiator name and the security credential,Установите имя инициатора и учетные данные безопасности,
@@ -9623,12 +9625,12 @@
 Is Order Completed,Заказ выполнен,
 Employee Records to Be Created By,"Записи о сотрудниках, которые будут создавать",
 Employee records are created using the selected field,Записи о сотрудниках создаются с использованием выбранного поля,
-Don't send employee birthday reminders,Не отправляйте сотрудникам напоминания о днях рождения,
+Don't send employee birthday reminders,Не отправлять сотрудникам напоминания о днях рождения,
 Restrict Backdated Leave Applications,Ограничение подачи заявлений на отпуск задним числом,
 Sequence ID,Идентификатор последовательности,
 Sequence Id,Идентификатор последовательности,
 Allow multiple material consumptions against a Work Order,Разрешить многократное потребление материала для рабочего задания,
-Plan time logs outside Workstation working hours,Планирование журналов рабочего времени вне рабочего времени рабочей станции,
+Plan time logs outside Workstation working hours,Планирование журналов рабочего времени вне рабочего времени рабочего места,
 Plan operations X days in advance,Планируйте операции на Х дней вперед,
 Time Between Operations (Mins),Время между операциями (мин),
 Default: 10 mins,По умолчанию: 10 минут,
@@ -9638,8 +9640,8 @@
 Select Items,Выбрать элементы,
 Against Default Supplier,Против дефолтного поставщика,
 Auto close Opportunity after the no. of days mentioned above,"Автоматическое закрытие Возможность после нет. дней, упомянутых выше",
-Is Sales Order Required for Sales Invoice & Delivery Note Creation?,Требуется ли заказ на продажу для создания счета-фактуры и накладной?,
-Is Delivery Note Required for Sales Invoice Creation?,Требуется ли накладная для создания счета-фактуры?,
+Is Sales Order Required for Sales Invoice & Delivery Note Creation?,Требуется ли заказ на продажу для создания счета и накладной?,
+Is Delivery Note Required for Sales Invoice Creation?,Требуется ли накладная для создания счета?,
 How often should Project and Company be updated based on Sales Transactions?,Как часто следует обновлять проект и компанию на основе сделок купли-продажи?,
 Allow User to Edit Price List Rate in Transactions,Разрешить пользователю изменять ставку прайс-листа в транзакциях,
 Allow Item to Be Added Multiple Times in a Transaction,Разрешить добавление элемента несколько раз в транзакцию,
@@ -9653,8 +9655,8 @@
 Set Qty in Transactions Based on Serial No Input,Установить количество транзакций на основе серийного номера ввода,
 Raise Material Request When Stock Reaches Re-order Level,"Поднимите запрос на материалы, когда запас достигает уровня повторного заказа",
 Notify by Email on Creation of Automatic Material Request,Уведомление по электронной почте о создании автоматического запроса материалов,
-Allow Material Transfer from Delivery Note to Sales Invoice,Разрешить перенос материала из накладной в счет-фактуру,
-Allow Material Transfer from Purchase Receipt to Purchase Invoice,Разрешить перенос материала из квитанции о покупке в счет-фактуру,
+Allow Material Transfer from Delivery Note to Sales Invoice,Разрешить перенос материала из накладной в счет,
+Allow Material Transfer from Purchase Receipt to Purchase Invoice,Разрешить перенос материала из квитанции о покупке в счет,
 Freeze Stocks Older Than (Days),Заморозить запасы старше (дней),
 Role Allowed to Edit Frozen Stock,"Роль, разрешенная для редактирования замороженных запасов",
 The unallocated amount of Payment Entry {0} is greater than the Bank Transaction's unallocated amount,Нераспределенная сумма платежной записи {0} превышает нераспределенную сумму банковской операции.,
@@ -9677,7 +9679,7 @@
 Mpesa Account Balance Processing Error,Ошибка обработки остатка на счете Mpesa,
 Balance Details,Детали баланса,
 Current Balance,Текущий баланс,
-Available Balance,доступные средства,
+Available Balance,Доступные средства,
 Reserved Balance,Зарезервированный баланс,
 Uncleared Balance,Неочищенный баланс,
 Payment related to {0} is not completed,"Платеж, связанный с {0}, не завершен",
@@ -9689,10 +9691,10 @@
 Payment amount cannot be less than or equal to 0,Сумма платежа не может быть меньше или равна 0,
 Please enter the phone number first,"Пожалуйста, сначала введите номер телефона",
 Row #{}: {} {} does not exist.,Строка № {}: {} {} не существует.,
-Row #{0}: {1} is required to create the Opening {2} Invoices,Строка № {0}: {1} требуется для создания начальных {2} счетов-фактур,
+Row #{0}: {1} is required to create the Opening {2} Invoices,Строка № {0}: {1} требуется для создания начальных {2} счетов,
 You had {} errors while creating opening invoices. Check {} for more details,При создании начальных счетов у вас было {} ошибок. Проверьте {} для получения дополнительной информации,
 Error Occured,Произошла ошибка,
-Opening Invoice Creation In Progress,Открытие счета-фактуры в процессе создания,
+Opening Invoice Creation In Progress,Открытие счета в процессе создания,
 Creating {} out of {} {},Создание {} из {} {},
 (Serial No: {0}) cannot be consumed as it's reserverd to fullfill Sales Order {1}.,"(Серийный номер: {0}) нельзя использовать, поскольку он зарезервирован для выполнения заказа на продажу {1}.",
 Item {0} {1},Пункт {0} {1},
@@ -9712,7 +9714,7 @@
 To Date cannot be after the current date.,Дата не может быть позже текущей даты.,
 From Time cannot be after the current time.,From Time не может быть позже текущего времени.,
 To Time cannot be after the current time.,Время не может быть позже текущего времени.,
-Stock Entry {0} created and ,Публикация акций {0} создана и,
+Stock Entry {0} created and ,Публикация акций {0} создана и ,
 Inpatient Medication Orders updated successfully,Заказы на лечение в стационаре успешно обновлены,
 Row {0}: Cannot create Inpatient Medication Entry against cancelled Inpatient Medication Order {1},Строка {0}: Невозможно создать запись о стационарном лечении для отмененного заказа на лечение в стационаре {1},
 Row {0}: This Medication Order is already marked as completed,Строка {0}: этот заказ на лекарства уже отмечен как выполненный.,
@@ -9744,7 +9746,7 @@
 Edit Receipt,Редактировать квитанцию,
 Focus on search input,Сосредоточьтесь на вводе поиска,
 Focus on Item Group filter,Фильтр по группам товаров,
-Checkout Order / Submit Order / New Order,Заказ оформления заказа / Отправить заказ / Новый заказ,
+Checkout Order / Submit Order / New Order,Оформление заказа / Утвердить заказ / Новый заказ,
 Add Order Discount,Добавить скидку на заказ,
 Item Code: {0} is not available under warehouse {1}.,Код товара: {0} недоступен на складе {1}.,
 Serial numbers unavailable for Item {0} under warehouse {1}. Please try changing warehouse.,Отсутствуют серийные номера для позиции {0} на складе {1}. Попробуйте сменить склад.,
@@ -9756,9 +9758,9 @@
 Enter amount to be redeemed.,Введите сумму к выкупу.,
 You cannot redeem more than {0}.,Вы не можете обменять более {0}.,
 Open Form View,Открыть просмотр формы,
-POS invoice {0} created succesfully,Счет-фактура POS {0} успешно создана,
+POS invoice {0} created succesfully,Счет POS {0} успешно создана,
 Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2}.,Недостаточно количества на складе для кода товара: {0} на складе {1}. Доступное количество {2}.,
-Serial No: {0} has already been transacted into another POS Invoice.,Серийный номер: {0} уже был переведен в другой счет-фактуру торговой точки.,
+Serial No: {0} has already been transacted into another POS Invoice.,Серийный номер: {0} уже был переведен в другой счет торговой точки.,
 Balance Serial No,Серийный номер весов,
 Warehouse: {0} does not belong to {1},Склад: {0} не принадлежит {1},
 Please select batches for batched item {0},Выберите партии для товара {0},
@@ -9771,10 +9773,10 @@
 {0} is mandatory. Maybe Currency Exchange record is not created for {1} to {2},"{0} является обязательным. Возможно, запись обмена валют не создана для {1} - {2}",
 {} has submitted assets linked to it. You need to cancel the assets to create purchase return.,"{} отправил связанные с ним активы. Вам необходимо отменить активы, чтобы создать возврат покупки.",
 Cannot cancel this document as it is linked with submitted asset {0}. Please cancel it to continue.,"Невозможно отменить этот документ, поскольку он связан с отправленным объектом {0}. Пожалуйста, отмените его, чтобы продолжить.",
-Row #{}: Serial No. {} has already been transacted into another POS Invoice. Please select valid serial no.,"Строка № {}: серийный номер {} уже переведен в другой счет-фактуру торговой точки. Пожалуйста, выберите действительный серийный номер.",
-Row #{}: Serial Nos. {} has already been transacted into another POS Invoice. Please select valid serial no.,"Строка № {}: Серийные номера {} уже были переведены в другой счет-фактуру торговой точки. Пожалуйста, выберите действительный серийный номер.",
+Row #{}: Serial No. {} has already been transacted into another POS Invoice. Please select valid serial no.,"Строка № {}: серийный номер {} уже переведен в другой счет торговой точки. Пожалуйста, выберите действительный серийный номер.",
+Row #{}: Serial Nos. {} has already been transacted into another POS Invoice. Please select valid serial no.,"Строка № {}: Серийные номера {} уже были переведены в другой счет торговой точки. Пожалуйста, выберите действительный серийный номер.",
 Item Unavailable,Товар недоступен,
-Row #{}: Serial No {} cannot be returned since it was not transacted in original invoice {},"Строка № {}: Серийный номер {} не может быть возвращен, поскольку он не был указан в исходном счете-фактуре {}",
+Row #{}: Serial No {} cannot be returned since it was not transacted in original invoice {},"Строка № {}: Серийный номер {} не может быть возвращен, поскольку он не был указан в исходном счете {}",
 Please set default Cash or Bank account in Mode of Payment {},Установите по умолчанию наличный или банковский счет в режиме оплаты {},
 Please set default Cash or Bank account in Mode of Payments {},Установите по умолчанию наличный или банковский счет в режиме оплаты {},
 Please ensure {} account is a Balance Sheet account. You can change the parent account to a Balance Sheet account or select a different account.,"Убедитесь, что счет {} является балансом. Вы можете изменить родительский счет на счет баланса или выбрать другой счет.",
@@ -9785,7 +9787,7 @@
 Expense Head Changed,Расходная часть изменена,
 because expense is booked against this account in Purchase Receipt {},поскольку расходы регистрируются по этому счету в квитанции о покупке {},
 as no Purchase Receipt is created against Item {}. ,поскольку для элемента {} не создается квитанция о покупке.,
-This is done to handle accounting for cases when Purchase Receipt is created after Purchase Invoice,"Это сделано для обработки учета в тех случаях, когда квитанция о покупке создается после счета-фактуры.",
+This is done to handle accounting for cases when Purchase Receipt is created after Purchase Invoice,"Это сделано для обработки учета в тех случаях, когда квитанция о покупке создается после счета.",
 Purchase Order Required for item {},Требуется заказ на покупку для товара {},
 To submit the invoice without purchase order please set {} ,"Чтобы отправить счет без заказа на покупку, установите {}",
 as {} in {},как в {},
@@ -9795,8 +9797,8 @@
 Mandatory Purchase Receipt,Квитанция об обязательной покупке,
 POS Profile {} does not belongs to company {},Профиль POS {} не принадлежит компании {},
 User {} is disabled. Please select valid user/cashier,Пользователь {} отключен. Выберите действующего пользователя / кассира,
-Row #{}: Original Invoice {} of return invoice {} is {}. ,Строка № {}: Исходный счет-фактура {} обратного счета-фактуры {}: {}.,
-Original invoice should be consolidated before or along with the return invoice.,Оригинальный счет-фактура должен быть объединен до или вместе с обратным счетом-фактурой.,
+Row #{}: Original Invoice {} of return invoice {} is {}. ,Строка № {}: Исходный счет {} обратного счета {}: {}.,
+Original invoice should be consolidated before or along with the return invoice.,Оригинальный счет должен быть объединен до или вместе с обратным счетом.,
 You can add original invoice {} manually to proceed.,"Чтобы продолжить, вы можете добавить исходный счет {} вручную.",
 Please ensure {} account is a Balance Sheet account. ,"Убедитесь, что счет {} является балансом.",
 You can change the parent account to a Balance Sheet account or select a different account.,Вы можете изменить родительский счет на счет баланса или выбрать другой счет.,
@@ -9805,18 +9807,18 @@
 {} can't be cancelled since the Loyalty Points earned has been redeemed. First cancel the {} No {},"{} не может быть отменен, так как заработанные очки лояльности были погашены. Сначала отмените {} Нет {}",
 already exists,уже существует,
 POS Closing Entry {} against {} between selected period,Закрытие торговой точки {} против {} между выбранным периодом,
-POS Invoice is {},Счет-фактура POS: {},
+POS Invoice is {},Счет POS {},
 POS Profile doesn't matches {},Профиль POS не соответствует {},
-POS Invoice is not {},Счет-фактура POS не {},
-POS Invoice isn't created by user {},Счет-фактура POS не создается пользователем {},
+POS Invoice is not {},Счет POS не {},
+POS Invoice isn't created by user {},Счет POS не создается пользователем {},
 Row #{}: {},Строка #{}: {},
-Invalid POS Invoices,Недействительные счета-фактуры POS,
+Invalid POS Invoices,Недействительные счета POS,
 Please add the account to root level Company - {},"Пожалуйста, добавьте аккаунт в компанию корневого уровня - {}",
 "While creating account for Child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA","При создании аккаунта для дочерней компании {0} родительский аккаунт {1} не найден. Пожалуйста, создайте родительский аккаунт в соответствующем сертификате подлинности",
-Account Not Found,Аккаунт не найден,
+Account Not Found,Счет не найден,
 "While creating account for Child Company {0}, parent account {1} found as a ledger account.",При создании аккаунта для дочерней компании {0} родительский аккаунт {1} обнаружен как счет главной книги.,
 Please convert the parent account in corresponding child company to a group account.,Преобразуйте родительскую учетную запись в соответствующей дочерней компании в групповую.,
-Invalid Parent Account,Неверный родительский аккаунт,
+Invalid Parent Account,Неверный родительский счет,
 "Renaming it is only allowed via parent company {0}, to avoid mismatch.","Переименование разрешено только через головную компанию {0}, чтобы избежать несоответствия.",
 "If you {0} {1} quantities of the item {2}, the scheme {3} will be applied on the item.","Если вы {0} {1} количество товара {2}, схема {3} будет применена к товару.",
 "If you {0} {1} worth item {2}, the scheme {3} will be applied on the item.","Если вы {0} {1} оцениваете предмет {2}, к нему будет применена схема {3}.",
@@ -9838,3 +9840,8 @@
 Creating Purchase Order ...,Создание заказа на поставку ...,
 "Select a Supplier from the Default Suppliers of the items below. On selection, a Purchase Order will be made against items belonging to the selected Supplier only.","Выберите поставщика из списка поставщиков по умолчанию для позиций ниже. При выборе Заказ на поставку будет сделан в отношении товаров, принадлежащих только выбранному Поставщику.",
 Row #{}: You must select {} serial numbers for item {}.,Строка № {}: необходимо выбрать {} серийных номеров для позиции {}.,
+Items & Pricing,Продукты и цены,
+Overdue,Просрочено,
+Completed,Завершенно,
+Total Tasks,Всего задач,
+Build,Конструктор,
diff --git a/erpnext/utilities/__init__.py b/erpnext/utilities/__init__.py
index 3749cde..c2b4229 100644
--- a/erpnext/utilities/__init__.py
+++ b/erpnext/utilities/__init__.py
@@ -7,9 +7,12 @@
 
 
 def update_doctypes():
-	for d in frappe.db.sql("""select df.parent, df.fieldname
+	for d in frappe.db.sql(
+		"""select df.parent, df.fieldname
 		from tabDocField df, tabDocType dt where df.fieldname
-		like "%description%" and df.parent = dt.name and dt.istable = 1""", as_dict=1):
+		like "%description%" and df.parent = dt.name and dt.istable = 1""",
+		as_dict=1,
+	):
 		dt = frappe.get_doc("DocType", d.parent)
 
 		for f in dt.fields:
@@ -18,20 +21,17 @@
 				dt.save()
 				break
 
+
 def get_site_info(site_info):
 	# called via hook
-	company = frappe.db.get_single_value('Global Defaults', 'default_company')
+	company = frappe.db.get_single_value("Global Defaults", "default_company")
 	domain = None
 
 	if not company:
-		company = frappe.db.sql('select name from `tabCompany` order by creation asc')
+		company = frappe.db.sql("select name from `tabCompany` order by creation asc")
 		company = company[0][0] if company else None
 
 	if company:
-		domain = frappe.get_cached_value('Company',  cstr(company),  'domain')
+		domain = frappe.get_cached_value("Company", cstr(company), "domain")
 
-	return {
-		'company': company,
-		'domain': domain,
-		'activation': get_level()
-	}
+	return {"company": company, "domain": domain, "activation": get_level()}
diff --git a/erpnext/utilities/activation.py b/erpnext/utilities/activation.py
index faf3fd4..7b2df5e 100644
--- a/erpnext/utilities/activation.py
+++ b/erpnext/utilities/activation.py
@@ -18,7 +18,6 @@
 		"Customer": 5,
 		"Delivery Note": 5,
 		"Employee": 3,
-		"Instructor": 5,
 		"Issue": 5,
 		"Item": 5,
 		"Journal Entry": 3,
@@ -37,11 +36,10 @@
 		"Sales Order": 2,
 		"Sales Invoice": 2,
 		"Stock Entry": 3,
-		"Student": 5,
 		"Supplier": 5,
 		"Task": 5,
 		"User": 5,
-		"Work Order": 5
+		"Work Order": 5,
 	}
 
 	for doctype, min_count in doctypes.items():
@@ -50,111 +48,100 @@
 			activation_level += 1
 		sales_data.append({doctype: count})
 
-	if frappe.db.get_single_value('System Settings', 'setup_complete'):
+	if frappe.db.get_single_value("System Settings", "setup_complete"):
 		activation_level += 1
 
-	communication_number = frappe.db.count('Communication', dict(communication_medium='Email'))
+	communication_number = frappe.db.count("Communication", dict(communication_medium="Email"))
 	if communication_number > 10:
 		activation_level += 1
 	sales_data.append({"Communication": communication_number})
 
 	# recent login
-	if frappe.db.sql('select name from tabUser where last_login > date_sub(now(), interval 2 day) limit 1'):
+	if frappe.db.sql(
+		"select name from tabUser where last_login > date_sub(now(), interval 2 day) limit 1"
+	):
 		activation_level += 1
 
 	level = {"activation_level": activation_level, "sales_data": sales_data}
 
 	return level
 
+
 def get_help_messages():
-	'''Returns help messages to be shown on Desktop'''
+	"""Returns help messages to be shown on Desktop"""
 	if get_level() > 6:
 		return []
 
-	domain = frappe.get_cached_value('Company',  erpnext.get_default_company(),  'domain')
+	domain = frappe.get_cached_value("Company", erpnext.get_default_company(), "domain")
 	messages = []
 
 	message_settings = [
 		frappe._dict(
-			doctype='Lead',
-			title=_('Create Leads'),
-			description=_('Leads help you get business, add all your contacts and more as your leads'),
-			action=_('Create Lead'),
-			route='List/Lead',
-			domain=('Manufacturing', 'Retail', 'Services', 'Distribution'),
-			target=3
+			doctype="Lead",
+			title=_("Create Leads"),
+			description=_("Leads help you get business, add all your contacts and more as your leads"),
+			action=_("Create Lead"),
+			route="List/Lead",
+			domain=("Manufacturing", "Retail", "Services", "Distribution"),
+			target=3,
 		),
 		frappe._dict(
-			doctype='Quotation',
-			title=_('Create customer quotes'),
-			description=_('Quotations are proposals, bids you have sent to your customers'),
-			action=_('Create Quotation'),
-			route='List/Quotation',
-			domain=('Manufacturing', 'Retail', 'Services', 'Distribution'),
-			target=3
+			doctype="Quotation",
+			title=_("Create customer quotes"),
+			description=_("Quotations are proposals, bids you have sent to your customers"),
+			action=_("Create Quotation"),
+			route="List/Quotation",
+			domain=("Manufacturing", "Retail", "Services", "Distribution"),
+			target=3,
 		),
 		frappe._dict(
-			doctype='Sales Order',
-			title=_('Manage your orders'),
-			description=_('Create Sales Orders to help you plan your work and deliver on-time'),
-			action=_('Create Sales Order'),
-			route='List/Sales Order',
-			domain=('Manufacturing', 'Retail', 'Services', 'Distribution'),
-			target=3
+			doctype="Sales Order",
+			title=_("Manage your orders"),
+			description=_("Create Sales Orders to help you plan your work and deliver on-time"),
+			action=_("Create Sales Order"),
+			route="List/Sales Order",
+			domain=("Manufacturing", "Retail", "Services", "Distribution"),
+			target=3,
 		),
 		frappe._dict(
-			doctype='Purchase Order',
-			title=_('Create Purchase Orders'),
-			description=_('Purchase orders help you plan and follow up on your purchases'),
-			action=_('Create Purchase Order'),
-			route='List/Purchase Order',
-			domain=('Manufacturing', 'Retail', 'Services', 'Distribution'),
-			target=3
+			doctype="Purchase Order",
+			title=_("Create Purchase Orders"),
+			description=_("Purchase orders help you plan and follow up on your purchases"),
+			action=_("Create Purchase Order"),
+			route="List/Purchase Order",
+			domain=("Manufacturing", "Retail", "Services", "Distribution"),
+			target=3,
 		),
 		frappe._dict(
-			doctype='User',
-			title=_('Create Users'),
-			description=_('Add the rest of your organization as your users. You can also add invite Customers to your portal by adding them from Contacts'),
-			action=_('Create User'),
-			route='List/User',
-			domain=('Manufacturing', 'Retail', 'Services', 'Distribution'),
-			target=3
+			doctype="User",
+			title=_("Create Users"),
+			description=_(
+				"Add the rest of your organization as your users. You can also add invite Customers to your portal by adding them from Contacts"
+			),
+			action=_("Create User"),
+			route="List/User",
+			domain=("Manufacturing", "Retail", "Services", "Distribution"),
+			target=3,
 		),
 		frappe._dict(
-			doctype='Timesheet',
-			title=_('Add Timesheets'),
-			description=_('Timesheets help keep track of time, cost and billing for activites done by your team'),
-			action=_('Create Timesheet'),
-			route='List/Timesheet',
-			domain=('Services',),
-			target=5
+			doctype="Timesheet",
+			title=_("Add Timesheets"),
+			description=_(
+				"Timesheets help keep track of time, cost and billing for activites done by your team"
+			),
+			action=_("Create Timesheet"),
+			route="List/Timesheet",
+			domain=("Services",),
+			target=5,
 		),
 		frappe._dict(
-			doctype='Student',
-			title=_('Add Students'),
-			description=_('Students are at the heart of the system, add all your students'),
-			action=_('Create Student'),
-			route='List/Student',
-			domain=('Education',),
-			target=5
+			doctype="Employee",
+			title=_("Create Employee Records"),
+			description=_("Create Employee records to manage leaves, expense claims and payroll"),
+			action=_("Create Employee"),
+			route="List/Employee",
+			target=3,
 		),
-		frappe._dict(
-			doctype='Student Batch',
-			title=_('Group your students in batches'),
-			description=_('Student Batches help you track attendance, assessments and fees for students'),
-			action=_('Create Student Batch'),
-			route='List/Student Batch',
-			domain=('Education',),
-			target=3
-		),
-		frappe._dict(
-			doctype='Employee',
-			title=_('Create Employee Records'),
-			description=_('Create Employee records to manage leaves, expense claims and payroll'),
-			action=_('Create Employee'),
-			route='List/Employee',
-			target=3
-		)
 	]
 
 	for m in message_settings:
diff --git a/erpnext/utilities/bot.py b/erpnext/utilities/bot.py
deleted file mode 100644
index 87a3508..0000000
--- a/erpnext/utilities/bot.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-
-import frappe
-from frappe import _
-from frappe.utils.bot import BotParser
-
-
-class FindItemBot(BotParser):
-	def get_reply(self):
-		if self.startswith('where is', 'find item', 'locate'):
-			if not frappe.has_permission('Warehouse'):
-				raise frappe.PermissionError
-
-			item = '%{0}%'.format(self.strip_words(self.query, 'where is', 'find item', 'locate'))
-			items = frappe.db.sql('''select name from `tabItem` where item_code like %(txt)s
-				or item_name like %(txt)s or description like %(txt)s''', dict(txt=item))
-
-			if items:
-				out = []
-				warehouses = frappe.get_all("Warehouse")
-				for item in items:
-					found = False
-					for warehouse in warehouses:
-						qty = frappe.db.get_value("Bin", {'item_code': item[0], 'warehouse': warehouse.name}, 'actual_qty')
-						if qty:
-							out.append(_('{0} units of [{1}](/app/Form/Item/{1}) found in [{2}](/app/Form/Warehouse/{2})').format(qty,
-								item[0], warehouse.name))
-							found = True
-
-					if not found:
-						out.append(_('[{0}](/app/Form/Item/{0}) is out of stock').format(item[0]))
-
-				return "\n\n".join(out)
-
-			else:
-				return _("Did not find any item called {0}").format(item)
diff --git a/erpnext/utilities/bulk_transaction.py b/erpnext/utilities/bulk_transaction.py
index 64e2ff4..bfcba07 100644
--- a/erpnext/utilities/bulk_transaction.py
+++ b/erpnext/utilities/bulk_transaction.py
@@ -45,9 +45,13 @@
 			frappe.db.rollback(save_point="before_creation_state")
 			failed_history.append(e)
 			failed.append(e)
-			update_logger(doc_name, e, from_doctype, to_doctype, status="Failed", log_date=str(date.today()))
+			update_logger(
+				doc_name, e, from_doctype, to_doctype, status="Failed", log_date=str(date.today())
+			)
 		if not failed:
-			update_logger(doc_name, None, from_doctype, to_doctype, status="Success", log_date=str(date.today()))
+			update_logger(
+				doc_name, None, from_doctype, to_doctype, status="Success", log_date=str(date.today())
+			)
 
 	show_job_status(failed_history, deserialized_data, to_doctype)
 
@@ -96,7 +100,7 @@
 		},
 		"Purchase Receipt": {"Purchase Invoice": purchase_receipt.make_purchase_invoice},
 	}
-	if to_doctype in ['Advance Payment', 'Payment']:
+	if to_doctype in ["Advance Payment", "Payment"]:
 		obj = mapper[from_doctype][to_doctype](from_doctype, doc_name)
 	else:
 		obj = mapper[from_doctype][to_doctype](doc_name)
@@ -142,9 +146,7 @@
 	else:
 		log_doc = get_logger_doc(log_date)
 		if record_exists(log_doc, doc_name, status):
-			append_data_to_logger(
-				log_doc, doc_name, e, from_doctype, to_doctype, status, restarted
-			)
+			append_data_to_logger(log_doc, doc_name, e, from_doctype, to_doctype, status, restarted)
 			log_doc.save()
 
 
@@ -158,20 +160,20 @@
 
 	if len(failed_history) != 0 and len(failed_history) < len(deserialized_data):
 		frappe.msgprint(
-			_("""Creation of {0} partially successful.
-				Check <b><a href="/app/bulk-transaction-log">Bulk Transaction Log</a></b>""").format(
-				to_doctype
-			),
+			_(
+				"""Creation of {0} partially successful.
+				Check <b><a href="/app/bulk-transaction-log">Bulk Transaction Log</a></b>"""
+			).format(to_doctype),
 			title="Partially successful",
 			indicator="orange",
 		)
 
 	if len(failed_history) == len(deserialized_data):
 		frappe.msgprint(
-			_("""Creation of {0} failed.
-				Check <b><a href="/app/bulk-transaction-log">Bulk Transaction Log</a></b>""").format(
-				to_doctype
-			),
+			_(
+				"""Creation of {0} failed.
+				Check <b><a href="/app/bulk-transaction-log">Bulk Transaction Log</a></b>"""
+			).format(to_doctype),
 			title="Failed",
 			indicator="red",
 		)
@@ -198,4 +200,4 @@
 
 	log_doc.save()
 
-	return record
\ No newline at end of file
+	return record
diff --git a/erpnext/utilities/doctype/rename_tool/rename_tool.js b/erpnext/utilities/doctype/rename_tool/rename_tool.js
index 5553e44..b8203f4 100644
--- a/erpnext/utilities/doctype/rename_tool/rename_tool.js
+++ b/erpnext/utilities/doctype/rename_tool/rename_tool.js
@@ -30,7 +30,16 @@
 					select_doctype: frm.doc.select_doctype
 				},
 				callback: function(r) {
-					frm.get_field("rename_log").$wrapper.html(r.message.join("<br>"));
+					let html = r.message.join("<br>");
+
+					if (r.exc) {
+						r.exc = frappe.utils.parse_json(r.exc);
+						if (Array.isArray(r.exc)) {
+							html += "<br>" + r.exc.join("<br>");
+						}
+					}
+
+					frm.get_field("rename_log").$wrapper.html(html);
 				}
 			});
 		});
diff --git a/erpnext/utilities/doctype/rename_tool/rename_tool.py b/erpnext/utilities/doctype/rename_tool/rename_tool.py
index 74de54a..b31574c 100644
--- a/erpnext/utilities/doctype/rename_tool/rename_tool.py
+++ b/erpnext/utilities/doctype/rename_tool/rename_tool.py
@@ -12,14 +12,19 @@
 class RenameTool(Document):
 	pass
 
+
 @frappe.whitelist()
 def get_doctypes():
-	return frappe.db.sql_list("""select name from tabDocType
-		where allow_rename=1 and module!='Core' order by name""")
+	return frappe.db.sql_list(
+		"""select name from tabDocType
+		where allow_rename=1 and module!='Core' order by name"""
+	)
+
 
 @frappe.whitelist()
 def upload(select_doctype=None, rows=None):
 	from frappe.utils.csvutils import read_csv_content_from_attached_file
+
 	if not select_doctype:
 		select_doctype = frappe.form_dict.select_doctype
 
diff --git a/erpnext/utilities/doctype/sms_log/test_sms_log.py b/erpnext/utilities/doctype/sms_log/test_sms_log.py
index 5f7abdc..3ff0202 100644
--- a/erpnext/utilities/doctype/sms_log/test_sms_log.py
+++ b/erpnext/utilities/doctype/sms_log/test_sms_log.py
@@ -5,5 +5,6 @@
 
 # test_records = frappe.get_test_records('SMS Log')
 
+
 class TestSMSLog(unittest.TestCase):
 	pass
diff --git a/erpnext/utilities/doctype/video/video.js b/erpnext/utilities/doctype/video/video.js
index 9cb5a15..e6c6efb 100644
--- a/erpnext/utilities/doctype/video/video.js
+++ b/erpnext/utilities/doctype/video/video.js
@@ -4,7 +4,7 @@
 frappe.ui.form.on('Video', {
 	refresh: function (frm) {
 		frm.events.toggle_youtube_statistics_section(frm);
-		frm.add_custom_button("Watch Video", () => frappe.help.show_video(frm.doc.url, frm.doc.title));
+		frm.add_custom_button(__("Watch Video"), () => frappe.help.show_video(frm.doc.url, frm.doc.title));
 	},
 
 	toggle_youtube_statistics_section: (frm) => {
diff --git a/erpnext/utilities/doctype/video/video.py b/erpnext/utilities/doctype/video/video.py
index ae13952..a39d0a9 100644
--- a/erpnext/utilities/doctype/video/video.py
+++ b/erpnext/utilities/doctype/video/video.py
@@ -28,16 +28,16 @@
 
 		try:
 			video = api.get_video_by_id(video_id=self.youtube_video_id)
-			video_stats = video.items[0].to_dict().get('statistics')
+			video_stats = video.items[0].to_dict().get("statistics")
 
-			self.like_count = video_stats.get('likeCount')
-			self.view_count = video_stats.get('viewCount')
-			self.dislike_count = video_stats.get('dislikeCount')
-			self.comment_count = video_stats.get('commentCount')
+			self.like_count = video_stats.get("likeCount")
+			self.view_count = video_stats.get("viewCount")
+			self.dislike_count = video_stats.get("dislikeCount")
+			self.comment_count = video_stats.get("commentCount")
 
 		except Exception:
-			title = "Failed to Update YouTube Statistics for Video: {0}".format(self.name)
-			frappe.log_error(title + "\n\n" +  frappe.get_traceback(), title=title)
+			self.log_error("Unable to update YouTube statistics")
+
 
 def is_tracking_enabled():
 	return frappe.db.get_single_value("Video Settings", "enable_youtube_tracking")
@@ -54,7 +54,9 @@
 
 def update_youtube_data():
 	# Called every 30 minutes via hooks
-	enable_youtube_tracking, frequency = frappe.db.get_value("Video Settings", "Video Settings", ["enable_youtube_tracking", "frequency"])
+	enable_youtube_tracking, frequency = frappe.db.get_value(
+		"Video Settings", "Video Settings", ["enable_youtube_tracking", "frequency"]
+	)
 
 	if not enable_youtube_tracking:
 		return
@@ -77,19 +79,21 @@
 	for video in video_list:
 		ids.append(video.youtube_video_id)
 
-	return ','.join(ids)
+	return ",".join(ids)
 
 
 @frappe.whitelist()
 def get_id_from_url(url):
 	"""
-		Returns video id from url
-		:param youtube url: String URL
+	Returns video id from url
+	:param youtube url: String URL
 	"""
 	if not isinstance(url, str):
 		frappe.throw(_("URL can only be a string"), title=_("Invalid URL"))
 
-	pattern = re.compile(r'[a-z\:\//\.]+(youtube|youtu)\.(com|be)/(watch\?v=|embed/|.+\?v=)?([^"&?\s]{11})?')
+	pattern = re.compile(
+		r'[a-z\:\//\.]+(youtube|youtu)\.(com|be)/(watch\?v=|embed/|.+\?v=)?([^"&?\s]{11})?'
+	)
 	id = pattern.match(url)
 	return id.groups()[-1]
 
@@ -104,8 +108,7 @@
 			video_stats = video.items
 			return video_stats
 		except Exception:
-			title = "Failed to Update YouTube Statistics"
-			frappe.log_error(title + "\n\n" +  frappe.get_traceback(), title=title)
+			frappe.log_error("Unable to update YouTube statistics")
 
 	def prepare_and_set_data(video_list):
 		video_ids = get_formatted_ids(video_list)
@@ -114,24 +117,27 @@
 
 	def set_youtube_data(entries):
 		for entry in entries:
-			video_stats = entry.to_dict().get('statistics')
-			video_id = entry.to_dict().get('id')
+			video_stats = entry.to_dict().get("statistics")
+			video_id = entry.to_dict().get("id")
 			stats = {
-				'like_count' : video_stats.get('likeCount'),
-				'view_count' : video_stats.get('viewCount'),
-				'dislike_count' : video_stats.get('dislikeCount'),
-				'comment_count' : video_stats.get('commentCount'),
-				'video_id': video_id
+				"like_count": video_stats.get("likeCount"),
+				"view_count": video_stats.get("viewCount"),
+				"dislike_count": video_stats.get("dislikeCount"),
+				"comment_count": video_stats.get("commentCount"),
+				"video_id": video_id,
 			}
 
-			frappe.db.sql("""
+			frappe.db.sql(
+				"""
 				UPDATE `tabVideo`
 				SET
 					like_count  = %(like_count)s,
 					view_count = %(view_count)s,
 					dislike_count = %(dislike_count)s,
 					comment_count = %(comment_count)s
-				WHERE youtube_video_id = %(video_id)s""", stats)
+				WHERE youtube_video_id = %(video_id)s""",
+				stats,
+			)
 
 	video_list = frappe.get_all("Video", fields=["youtube_video_id"])
 	if len(video_list) > 50:
diff --git a/erpnext/utilities/doctype/video_settings/video_settings.py b/erpnext/utilities/doctype/video_settings/video_settings.py
index 6f1e2bb..9bc7972 100644
--- a/erpnext/utilities/doctype/video_settings/video_settings.py
+++ b/erpnext/utilities/doctype/video_settings/video_settings.py
@@ -18,5 +18,5 @@
 				build("youtube", "v3", developerKey=self.api_key)
 			except Exception:
 				title = _("Failed to Authenticate the API key.")
-				frappe.log_error(title + "\n\n" +  frappe.get_traceback(), title=title)
+				self.log_error("Failed to authenticate API key")
 				frappe.throw(title + " Please check the error logs.", title=_("Invalid Credentials"))
diff --git a/erpnext/utilities/hierarchy_chart.py b/erpnext/utilities/hierarchy_chart.py
index c18ce10..4bf4353 100644
--- a/erpnext/utilities/hierarchy_chart.py
+++ b/erpnext/utilities/hierarchy_chart.py
@@ -8,11 +8,11 @@
 
 @frappe.whitelist()
 def get_all_nodes(method, company):
-	'''Recursively gets all data from nodes'''
+	"""Recursively gets all data from nodes"""
 	method = frappe.get_attr(method)
 
 	if method not in frappe.whitelisted:
-		frappe.throw(_('Not Permitted'), frappe.PermissionError)
+		frappe.throw(_("Not Permitted"), frappe.PermissionError)
 
 	root_nodes = method(company=company)
 	result = []
@@ -21,14 +21,16 @@
 	for root in root_nodes:
 		data = method(root.id, company)
 		result.append(dict(parent=root.id, parent_name=root.name, data=data))
-		nodes_to_expand.extend([{'id': d.get('id'), 'name': d.get('name')} for d in data if d.get('expandable')])
+		nodes_to_expand.extend(
+			[{"id": d.get("id"), "name": d.get("name")} for d in data if d.get("expandable")]
+		)
 
 	while nodes_to_expand:
 		parent = nodes_to_expand.pop(0)
-		data = method(parent.get('id'), company)
-		result.append(dict(parent=parent.get('id'), parent_name=parent.get('name'), data=data))
+		data = method(parent.get("id"), company)
+		result.append(dict(parent=parent.get("id"), parent_name=parent.get("name"), data=data))
 		for d in data:
-			if d.get('expandable'):
-				nodes_to_expand.append({'id': d.get('id'), 'name': d.get('name')})
+			if d.get("expandable"):
+				nodes_to_expand.append({"id": d.get("id"), "name": d.get("name")})
 
 	return result
diff --git a/erpnext/utilities/naming.py b/erpnext/utilities/naming.py
new file mode 100644
index 0000000..52bbade
--- /dev/null
+++ b/erpnext/utilities/naming.py
@@ -0,0 +1,60 @@
+import frappe
+from frappe.model.naming import get_default_naming_series
+
+
+class NamingSeriesNotSetError(frappe.ValidationError):
+	pass
+
+
+def set_by_naming_series(
+	doctype, fieldname, naming_series, hide_name_field=True, make_mandatory=1
+):
+	"""Change a doctype's naming to user naming series"""
+	from frappe.custom.doctype.property_setter.property_setter import make_property_setter
+
+	if naming_series:
+		make_property_setter(
+			doctype, "naming_series", "hidden", 0, "Check", validate_fields_for_doctype=False
+		)
+		make_property_setter(
+			doctype, "naming_series", "reqd", make_mandatory, "Check", validate_fields_for_doctype=False
+		)
+
+		# set values for mandatory
+		try:
+			frappe.db.sql(
+				"""update `tab{doctype}` set naming_series={s} where
+				ifnull(naming_series, '')=''""".format(
+					doctype=doctype, s="%s"
+				),
+				get_default_naming_series(doctype),
+			)
+		except NamingSeriesNotSetError:
+			pass
+
+		if hide_name_field:
+			make_property_setter(doctype, fieldname, "reqd", 0, "Check", validate_fields_for_doctype=False)
+			make_property_setter(
+				doctype, fieldname, "hidden", 1, "Check", validate_fields_for_doctype=False
+			)
+	else:
+		make_property_setter(
+			doctype, "naming_series", "reqd", 0, "Check", validate_fields_for_doctype=False
+		)
+		make_property_setter(
+			doctype, "naming_series", "hidden", 1, "Check", validate_fields_for_doctype=False
+		)
+
+		if hide_name_field:
+			make_property_setter(
+				doctype, fieldname, "hidden", 0, "Check", validate_fields_for_doctype=False
+			)
+			make_property_setter(doctype, fieldname, "reqd", 1, "Check", validate_fields_for_doctype=False)
+
+			# set values for mandatory
+			frappe.db.sql(
+				"""update `tab{doctype}` set `{fieldname}`=`name` where
+				ifnull({fieldname}, '')=''""".format(
+					doctype=doctype, fieldname=fieldname
+				)
+			)
diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py
index 0a45002..04ee0b3 100644
--- a/erpnext/utilities/product.py
+++ b/erpnext/utilities/product.py
@@ -9,32 +9,41 @@
 
 
 def get_web_item_qty_in_stock(item_code, item_warehouse_field, warehouse=None):
-	in_stock, stock_qty = 0, ''
-	template_item_code, is_stock_item = frappe.db.get_value("Item", item_code, ["variant_of", "is_stock_item"])
+	in_stock, stock_qty = 0, ""
+	template_item_code, is_stock_item = frappe.db.get_value(
+		"Item", item_code, ["variant_of", "is_stock_item"]
+	)
 
 	if not warehouse:
 		warehouse = frappe.db.get_value("Website Item", {"item_code": item_code}, item_warehouse_field)
 
 	if not warehouse and template_item_code and template_item_code != item_code:
-		warehouse = frappe.db.get_value("Website Item", {"item_code": template_item_code}, item_warehouse_field)
+		warehouse = frappe.db.get_value(
+			"Website Item", {"item_code": template_item_code}, item_warehouse_field
+		)
 
 	if warehouse:
-		stock_qty = frappe.db.sql("""
+		stock_qty = frappe.db.sql(
+			"""
 			select GREATEST(S.actual_qty - S.reserved_qty - S.reserved_qty_for_production - S.reserved_qty_for_sub_contract, 0) / IFNULL(C.conversion_factor, 1)
 			from tabBin S
 			inner join `tabItem` I on S.item_code = I.Item_code
 			left join `tabUOM Conversion Detail` C on I.sales_uom = C.uom and C.parent = I.Item_code
-			where S.item_code=%s and S.warehouse=%s""", (item_code, warehouse))
+			where S.item_code=%s and S.warehouse=%s""",
+			(item_code, warehouse),
+		)
 
 		if stock_qty:
 			stock_qty = adjust_qty_for_expired_items(item_code, stock_qty, warehouse)
 			in_stock = stock_qty[0][0] > 0 and 1 or 0
 
-	return frappe._dict({"in_stock": in_stock, "stock_qty": stock_qty, "is_stock_item": is_stock_item})
+	return frappe._dict(
+		{"in_stock": in_stock, "stock_qty": stock_qty, "is_stock_item": is_stock_item}
+	)
 
 
 def adjust_qty_for_expired_items(item_code, stock_qty, warehouse):
-	batches = frappe.get_all('Batch', filters=[{'item': item_code}], fields=['expiry_date', 'name'])
+	batches = frappe.get_all("Batch", filters=[{"item": item_code}], fields=["expiry_date", "name"])
 	expired_batches = get_expired_batches(batches)
 	stock_qty = [list(item) for item in stock_qty]
 
@@ -67,33 +76,42 @@
 
 	return qty
 
+
 def get_price(item_code, price_list, customer_group, company, qty=1):
 	from erpnext.e_commerce.shopping_cart.cart import get_party
 
 	template_item_code = frappe.db.get_value("Item", item_code, "variant_of")
 
 	if price_list:
-		price = frappe.get_all("Item Price", fields=["price_list_rate", "currency"],
-			filters={"price_list": price_list, "item_code": item_code})
+		price = frappe.get_all(
+			"Item Price",
+			fields=["price_list_rate", "currency"],
+			filters={"price_list": price_list, "item_code": item_code},
+		)
 
 		if template_item_code and not price:
-			price = frappe.get_all("Item Price", fields=["price_list_rate", "currency"],
-				filters={"price_list": price_list, "item_code": template_item_code})
+			price = frappe.get_all(
+				"Item Price",
+				fields=["price_list_rate", "currency"],
+				filters={"price_list": price_list, "item_code": template_item_code},
+			)
 
 		if price:
 			party = get_party()
-			pricing_rule_dict = frappe._dict({
-				"item_code": item_code,
-				"qty": qty,
-				"stock_qty": qty,
-				"transaction_type": "selling",
-				"price_list": price_list,
-				"customer_group": customer_group,
-				"company": company,
-				"conversion_rate": 1,
-				"for_shopping_cart": True,
-				"currency": frappe.db.get_value("Price List", price_list, "currency")
-			})
+			pricing_rule_dict = frappe._dict(
+				{
+					"item_code": item_code,
+					"qty": qty,
+					"stock_qty": qty,
+					"transaction_type": "selling",
+					"price_list": price_list,
+					"customer_group": customer_group,
+					"company": company,
+					"conversion_rate": 1,
+					"for_shopping_cart": True,
+					"currency": frappe.db.get_value("Price List", price_list, "currency"),
+				}
+			)
 
 			if party and party.doctype == "Customer":
 				pricing_rule_dict.update({"customer": party.name})
@@ -108,7 +126,9 @@
 				if pricing_rule.pricing_rule_for == "Discount Percentage":
 					price_obj.discount_percent = pricing_rule.discount_percentage
 					price_obj.formatted_discount_percent = str(flt(pricing_rule.discount_percentage, 0)) + "%"
-					price_obj.price_list_rate = flt(price_obj.price_list_rate * (1.0 - (flt(pricing_rule.discount_percentage) / 100.0)))
+					price_obj.price_list_rate = flt(
+						price_obj.price_list_rate * (1.0 - (flt(pricing_rule.discount_percentage) / 100.0))
+					)
 
 				if pricing_rule.pricing_rule_for == "Rate":
 					rate_discount = flt(mrp) - flt(pricing_rule.price_list_rate)
@@ -117,21 +137,33 @@
 					price_obj.price_list_rate = pricing_rule.price_list_rate or 0
 
 			if price_obj:
-				price_obj["formatted_price"] = fmt_money(price_obj["price_list_rate"], currency=price_obj["currency"])
+				price_obj["formatted_price"] = fmt_money(
+					price_obj["price_list_rate"], currency=price_obj["currency"]
+				)
 				if mrp != price_obj["price_list_rate"]:
 					price_obj["formatted_mrp"] = fmt_money(mrp, currency=price_obj["currency"])
 
-				price_obj["currency_symbol"] = not cint(frappe.db.get_default("hide_currency_symbol")) \
-					and (frappe.db.get_value("Currency", price_obj.currency, "symbol", cache=True) or price_obj.currency) \
+				price_obj["currency_symbol"] = (
+					not cint(frappe.db.get_default("hide_currency_symbol"))
+					and (
+						frappe.db.get_value("Currency", price_obj.currency, "symbol", cache=True)
+						or price_obj.currency
+					)
 					or ""
+				)
 
-				uom_conversion_factor = frappe.db.sql("""select	C.conversion_factor
+				uom_conversion_factor = frappe.db.sql(
+					"""select	C.conversion_factor
 					from `tabUOM Conversion Detail` C
 					inner join `tabItem` I on C.parent = I.name and C.uom = I.sales_uom
-					where I.name = %s""", item_code)
+					where I.name = %s""",
+					item_code,
+				)
 
 				uom_conversion_factor = uom_conversion_factor[0][0] if uom_conversion_factor else 1
-				price_obj["formatted_price_sales_uom"] = fmt_money(price_obj["price_list_rate"] * uom_conversion_factor, currency=price_obj["currency"])
+				price_obj["formatted_price_sales_uom"] = fmt_money(
+					price_obj["price_list_rate"] * uom_conversion_factor, currency=price_obj["currency"]
+				)
 
 				if not price_obj["price_list_rate"]:
 					price_obj["price_list_rate"] = 0
@@ -144,11 +176,17 @@
 
 			return price_obj
 
+
 def get_non_stock_item_status(item_code, item_warehouse_field):
 	# if item is a product bundle, check if its bundle items are in stock
 	if frappe.db.exists("Product Bundle", item_code):
 		items = frappe.get_doc("Product Bundle", item_code).get_all_children()
-		bundle_warehouse = frappe.db.get_value("Website Item", {"item_code": item_code}, item_warehouse_field)
-		return all(get_web_item_qty_in_stock(d.item_code, item_warehouse_field, bundle_warehouse).in_stock for d in items)
+		bundle_warehouse = frappe.db.get_value(
+			"Website Item", {"item_code": item_code}, item_warehouse_field
+		)
+		return all(
+			get_web_item_qty_in_stock(d.item_code, item_warehouse_field, bundle_warehouse).in_stock
+			for d in items
+		)
 	else:
 		return 1
diff --git a/erpnext/utilities/report/youtube_interactions/youtube_interactions.py b/erpnext/utilities/report/youtube_interactions/youtube_interactions.py
index a185a70..a2cb4e8 100644
--- a/erpnext/utilities/report/youtube_interactions/youtube_interactions.py
+++ b/erpnext/utilities/report/youtube_interactions/youtube_interactions.py
@@ -16,98 +16,58 @@
 	chart_data, summary = get_chart_summary_data(data)
 	return columns, data, None, chart_data, summary
 
+
 def get_columns():
 	return [
-		{
-			"label": _("Published Date"),
-			"fieldname": "publish_date",
-			"fieldtype": "Date",
-			"width": 100
-		},
-		{
-			"label": _("Title"),
-			"fieldname": "title",
-			"fieldtype": "Data",
-			"width": 200
-		},
-		{
-			"label": _("Duration"),
-			"fieldname": "duration",
-			"fieldtype": "Duration",
-			"width": 100
-		},
-		{
-			"label": _("Views"),
-			"fieldname": "view_count",
-			"fieldtype": "Float",
-			"width": 200
-		},
-		{
-			"label": _("Likes"),
-			"fieldname": "like_count",
-			"fieldtype": "Float",
-			"width": 200
-		},
-		{
-			"label": _("Dislikes"),
-			"fieldname": "dislike_count",
-			"fieldtype": "Float",
-			"width": 100
-		},
-		{
-			"label": _("Comments"),
-			"fieldname": "comment_count",
-			"fieldtype": "Float",
-			"width": 100
-		}
+		{"label": _("Published Date"), "fieldname": "publish_date", "fieldtype": "Date", "width": 100},
+		{"label": _("Title"), "fieldname": "title", "fieldtype": "Data", "width": 200},
+		{"label": _("Duration"), "fieldname": "duration", "fieldtype": "Duration", "width": 100},
+		{"label": _("Views"), "fieldname": "view_count", "fieldtype": "Float", "width": 200},
+		{"label": _("Likes"), "fieldname": "like_count", "fieldtype": "Float", "width": 200},
+		{"label": _("Dislikes"), "fieldname": "dislike_count", "fieldtype": "Float", "width": 100},
+		{"label": _("Comments"), "fieldname": "comment_count", "fieldtype": "Float", "width": 100},
 	]
 
+
 def get_data(filters):
-	return frappe.db.sql("""
+	return frappe.db.sql(
+		"""
 		SELECT
 			publish_date, title, provider, duration,
 			view_count, like_count, dislike_count, comment_count
 		FROM `tabVideo`
 		WHERE view_count is not null
 			and publish_date between %(from_date)s and %(to_date)s
-		ORDER BY view_count desc""", filters, as_dict=1)
+		ORDER BY view_count desc""",
+		filters,
+		as_dict=1,
+	)
+
 
 def get_chart_summary_data(data):
 	labels, likes, views = [], [], []
 	total_views = 0
 
 	for row in data:
-		labels.append(row.get('title'))
-		likes.append(row.get('like_count'))
-		views.append(row.get('view_count'))
-		total_views += flt(row.get('view_count'))
-
+		labels.append(row.get("title"))
+		likes.append(row.get("like_count"))
+		views.append(row.get("view_count"))
+		total_views += flt(row.get("view_count"))
 
 	chart_data = {
-		"data" : {
-			"labels" : labels,
-			"datasets" : [
-				{
-					"name" : "Likes",
-					"values" : likes
-				},
-				{
-					"name" : "Views",
-					"values" : views
-				}
-			]
+		"data": {
+			"labels": labels,
+			"datasets": [{"name": "Likes", "values": likes}, {"name": "Views", "values": views}],
 		},
 		"type": "bar",
-		"barOptions": {
-			"stacked": 1
-		},
+		"barOptions": {"stacked": 1},
 	}
 
 	summary = [
 		{
 			"value": total_views,
 			"indicator": "Blue",
-			"label": "Total Views",
+			"label": _("Total Views"),
 			"datatype": "Float",
 		}
 	]
diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py
index feea228..73cbcd4 100644
--- a/erpnext/utilities/transaction_base.py
+++ b/erpnext/utilities/transaction_base.py
@@ -10,7 +10,9 @@
 from erpnext.controllers.status_updater import StatusUpdater
 
 
-class UOMMustBeIntegerError(frappe.ValidationError): pass
+class UOMMustBeIntegerError(frappe.ValidationError):
+	pass
+
 
 class TransactionBase(StatusUpdater):
 	def validate_posting_time(self):
@@ -18,69 +20,79 @@
 		if frappe.flags.in_import and self.posting_date:
 			self.set_posting_time = 1
 
-		if not getattr(self, 'set_posting_time', None):
+		if not getattr(self, "set_posting_time", None):
 			now = now_datetime()
-			self.posting_date = now.strftime('%Y-%m-%d')
-			self.posting_time = now.strftime('%H:%M:%S.%f')
+			self.posting_date = now.strftime("%Y-%m-%d")
+			self.posting_time = now.strftime("%H:%M:%S.%f")
 		elif self.posting_time:
 			try:
 				get_time(self.posting_time)
 			except ValueError:
-				frappe.throw(_('Invalid Posting Time'))
+				frappe.throw(_("Invalid Posting Time"))
 
 	def add_calendar_event(self, opts, force=False):
-		if cstr(self.contact_by) != cstr(self._prev.contact_by) or \
-				cstr(self.contact_date) != cstr(self._prev.contact_date) or force or \
-				(hasattr(self, "ends_on") and cstr(self.ends_on) != cstr(self._prev.ends_on)):
+		if (
+			cstr(self.contact_by) != cstr(self._prev.contact_by)
+			or cstr(self.contact_date) != cstr(self._prev.contact_date)
+			or force
+			or (hasattr(self, "ends_on") and cstr(self.ends_on) != cstr(self._prev.ends_on))
+		):
 
 			self.delete_events()
 			self._add_calendar_event(opts)
 
 	def delete_events(self):
-		participations = frappe.get_all("Event Participants", filters={"reference_doctype": self.doctype, "reference_docname": self.name,
-			"parenttype": "Event"}, fields=["name", "parent"])
+		participations = frappe.get_all(
+			"Event Participants",
+			filters={
+				"reference_doctype": self.doctype,
+				"reference_docname": self.name,
+				"parenttype": "Event",
+			},
+			fields=["name", "parent"],
+		)
 
 		if participations:
 			for participation in participations:
-				total_participants = frappe.get_all("Event Participants", filters={"parenttype": "Event", "parent": participation.parent})
+				total_participants = frappe.get_all(
+					"Event Participants", filters={"parenttype": "Event", "parent": participation.parent}
+				)
 
 				if len(total_participants) <= 1:
 					frappe.db.sql("delete from `tabEvent` where name='%s'" % participation.parent)
 
 				frappe.db.sql("delete from `tabEvent Participants` where name='%s'" % participation.name)
 
-
 	def _add_calendar_event(self, opts):
 		opts = frappe._dict(opts)
 
 		if self.contact_date:
-			event = frappe.get_doc({
-				"doctype": "Event",
-				"owner": opts.owner or self.owner,
-				"subject": opts.subject,
-				"description": opts.description,
-				"starts_on":  self.contact_date,
-				"ends_on": opts.ends_on,
-				"event_type": "Private"
-			})
-
-			event.append('event_participants', {
-				"reference_doctype": self.doctype,
-				"reference_docname": self.name
+			event = frappe.get_doc(
+				{
+					"doctype": "Event",
+					"owner": opts.owner or self.owner,
+					"subject": opts.subject,
+					"description": opts.description,
+					"starts_on": self.contact_date,
+					"ends_on": opts.ends_on,
+					"event_type": "Private",
 				}
 			)
 
+			event.append(
+				"event_participants", {"reference_doctype": self.doctype, "reference_docname": self.name}
+			)
+
 			event.insert(ignore_permissions=True)
 
 			if frappe.db.exists("User", self.contact_by):
-				frappe.share.add("Event", event.name, self.contact_by,
-					flags={"ignore_share_permission": True})
+				frappe.share.add("Event", event.name, self.contact_by, flags={"ignore_share_permission": True})
 
 	def validate_uom_is_integer(self, uom_field, qty_fields):
 		validate_uom_is_integer(self, uom_field, qty_fields)
 
 	def validate_with_previous_doc(self, ref):
-		self.exclude_fields = ["conversion_factor", "uom"] if self.get('is_return') else []
+		self.exclude_fields = ["conversion_factor", "uom"] if self.get("is_return") else []
 
 		for key, val in ref.items():
 			is_child = val.get("is_child_table")
@@ -105,8 +117,9 @@
 	def compare_values(self, ref_doc, fields, doc=None):
 		for reference_doctype, ref_dn_list in ref_doc.items():
 			for reference_name in ref_dn_list:
-				prevdoc_values = frappe.db.get_value(reference_doctype, reference_name,
-					[d[0] for d in fields], as_dict=1)
+				prevdoc_values = frappe.db.get_value(
+					reference_doctype, reference_name, [d[0] for d in fields], as_dict=1
+				)
 
 				if not prevdoc_values:
 					frappe.throw(_("Invalid reference {0} {1}").format(reference_doctype, reference_name))
@@ -115,7 +128,6 @@
 					if prevdoc_values[field] is not None and field not in self.exclude_fields:
 						self.validate_value(field, condition, prevdoc_values[field], doc)
 
-
 	def validate_rate_with_reference_doc(self, ref_details):
 		buying_doctypes = ["Purchase Order", "Purchase Invoice", "Purchase Receipt"]
 
@@ -131,17 +143,26 @@
 				if d.get(ref_link_field):
 					ref_rate = frappe.db.get_value(ref_dt + " Item", d.get(ref_link_field), "rate")
 
-					if abs(flt(d.rate - ref_rate, d.precision("rate"))) >= .01:
+					if abs(flt(d.rate - ref_rate, d.precision("rate"))) >= 0.01:
 						if action == "Stop":
-							role_allowed_to_override = frappe.db.get_single_value(settings_doc, 'role_to_override_stop_action')
+							role_allowed_to_override = frappe.db.get_single_value(
+								settings_doc, "role_to_override_stop_action"
+							)
 
 							if role_allowed_to_override not in frappe.get_roles():
-								frappe.throw(_("Row #{0}: Rate must be same as {1}: {2} ({3} / {4})").format(
-									d.idx, ref_dt, d.get(ref_dn_field), d.rate, ref_rate))
+								frappe.throw(
+									_("Row #{0}: Rate must be same as {1}: {2} ({3} / {4})").format(
+										d.idx, ref_dt, d.get(ref_dn_field), d.rate, ref_rate
+									)
+								)
 						else:
-							frappe.msgprint(_("Row #{0}: Rate must be same as {1}: {2} ({3} / {4})").format(
-								d.idx, ref_dt, d.get(ref_dn_field), d.rate, ref_rate), title=_("Warning"), indicator="orange")
-
+							frappe.msgprint(
+								_("Row #{0}: Rate must be same as {1}: {2} ({3} / {4})").format(
+									d.idx, ref_dt, d.get(ref_dn_field), d.rate, ref_rate
+								),
+								title=_("Warning"),
+								indicator="orange",
+							)
 
 	def get_link_filters(self, for_doctype):
 		if hasattr(self, "prev_link_mapper") and self.prev_link_mapper.get(for_doctype):
@@ -150,11 +171,7 @@
 			values = filter(None, tuple(item.as_dict()[fieldname] for item in self.items))
 
 			if values:
-				ret = {
-					for_doctype : {
-						"filters": [[for_doctype, "name", "in", values]]
-					}
-				}
+				ret = {for_doctype: {"filters": [[for_doctype, "name", "in", values]]}}
 			else:
 				ret = None
 		else:
@@ -163,17 +180,17 @@
 		return ret
 
 	def reset_default_field_value(self, default_field: str, child_table: str, child_table_field: str):
-		""" Reset "Set default X" fields on forms to avoid confusion.
+		"""Reset "Set default X" fields on forms to avoid confusion.
 
-			example:
-				doc = {
-					"set_from_warehouse": "Warehouse A",
-					"items": [{"from_warehouse": "warehouse B"}, {"from_warehouse": "warehouse A"}],
-				}
-				Since this has dissimilar values in child table, the default field will be erased.
+		example:
+		        doc = {
+		                "set_from_warehouse": "Warehouse A",
+		                "items": [{"from_warehouse": "warehouse B"}, {"from_warehouse": "warehouse A"}],
+		        }
+		        Since this has dissimilar values in child table, the default field will be erased.
 
-				doc.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
-			"""
+		        doc.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
+		"""
 		child_table_values = set()
 
 		for row in self.get(child_table):
@@ -182,8 +199,11 @@
 		if len(child_table_values) > 1:
 			self.set(default_field, None)
 
+
 def delete_events(ref_type, ref_name):
-	events = frappe.db.sql_list(""" SELECT
+	events = (
+		frappe.db.sql_list(
+			""" SELECT
 			distinct `tabEvent`.name
 		from
 			`tabEvent`, `tabEvent Participants`
@@ -191,18 +211,27 @@
 			`tabEvent`.name = `tabEvent Participants`.parent
 			and `tabEvent Participants`.reference_doctype = %s
 			and `tabEvent Participants`.reference_docname = %s
-		""", (ref_type, ref_name)) or []
+		""",
+			(ref_type, ref_name),
+		)
+		or []
+	)
 
 	if events:
 		frappe.delete_doc("Event", events, for_reload=True)
 
+
 def validate_uom_is_integer(doc, uom_field, qty_fields, child_dt=None):
 	if isinstance(qty_fields, str):
 		qty_fields = [qty_fields]
 
 	distinct_uoms = list(set(d.get(uom_field) for d in doc.get_all_children()))
-	integer_uoms = list(filter(lambda uom: frappe.db.get_value("UOM", uom,
-		"must_be_whole_number", cache=True) or None, distinct_uoms))
+	integer_uoms = list(
+		filter(
+			lambda uom: frappe.db.get_value("UOM", uom, "must_be_whole_number", cache=True) or None,
+			distinct_uoms,
+		)
+	)
 
 	if not integer_uoms:
 		return
@@ -213,6 +242,11 @@
 				qty = d.get(f)
 				if qty:
 					if abs(cint(qty) - flt(qty)) > 0.0000001:
-						frappe.throw(_("Row {1}: Quantity ({0}) cannot be a fraction. To allow this, disable '{2}' in UOM {3}.") \
-							.format(qty, d.idx, frappe.bold(_("Must be Whole Number")), frappe.bold(d.get(uom_field))),
-								UOMMustBeIntegerError)
+						frappe.throw(
+							_(
+								"Row {1}: Quantity ({0}) cannot be a fraction. To allow this, disable '{2}' in UOM {3}."
+							).format(
+								qty, d.idx, frappe.bold(_("Must be Whole Number")), frappe.bold(d.get(uom_field))
+							),
+							UOMMustBeIntegerError,
+						)
diff --git a/erpnext/www/all-products/index.html b/erpnext/www/all-products/index.html
index 3d5517c..04fc74c 100644
--- a/erpnext/www/all-products/index.html
+++ b/erpnext/www/all-products/index.html
@@ -31,24 +31,6 @@
 			{% endif %}
 		</div>
 
-		<script>
-			frappe.ready(() => {
-				$('.product-filter-filter').on('keydown', frappe.utils.debounce((e) => {
-					const $input = $(e.target);
-					const keyword = ($input.val() || '').toLowerCase();
-					const $filter_options = $input.next('.filter-options');
-
-					$filter_options.find('.custom-control').show();
-					$filter_options.find('.custom-control').each((i, el) => {
-						const $el = $(el);
-						const value = $el.data('value').toLowerCase();
-						if (!value.includes(keyword)) {
-							$el.hide();
-						}
-					});
-				}, 300));
-			})
-		</script>
 	</div>
 </div>
 
diff --git a/erpnext/www/all-products/index.py b/erpnext/www/all-products/index.py
index ffaead6..fbf0dce 100644
--- a/erpnext/www/all-products/index.py
+++ b/erpnext/www/all-products/index.py
@@ -5,15 +5,18 @@
 
 sitemap = 1
 
+
 def get_context(context):
 	# Add homepage as parent
 	context.body_class = "product-page"
-	context.parents = [{"name": frappe._("Home"), "route":"/"}]
+	context.parents = [{"name": frappe._("Home"), "route": "/"}]
 
 	filter_engine = ProductFiltersBuilder()
 	context.field_filters = filter_engine.get_field_filters()
 	context.attribute_filters = filter_engine.get_attribute_filters()
 
-	context.page_length = cint(frappe.db.get_single_value('E Commerce Settings', 'products_per_page'))or 20
+	context.page_length = (
+		cint(frappe.db.get_single_value("E Commerce Settings", "products_per_page")) or 20
+	)
 
-	context.no_cache = 1
\ No newline at end of file
+	context.no_cache = 1
diff --git a/erpnext/www/book_appointment/index.py b/erpnext/www/book_appointment/index.py
index 8cda3c1..06e99da 100644
--- a/erpnext/www/book_appointment/index.py
+++ b/erpnext/www/book_appointment/index.py
@@ -11,38 +11,46 @@
 
 
 def get_context(context):
-	is_enabled = frappe.db.get_single_value('Appointment Booking Settings', 'enable_scheduling')
+	is_enabled = frappe.db.get_single_value("Appointment Booking Settings", "enable_scheduling")
 	if is_enabled:
 		return context
 	else:
-		frappe.redirect_to_message(_("Appointment Scheduling Disabled"), _("Appointment Scheduling has been disabled for this site"),
-			http_status_code=302, indicator_color="red")
+		frappe.redirect_to_message(
+			_("Appointment Scheduling Disabled"),
+			_("Appointment Scheduling has been disabled for this site"),
+			http_status_code=302,
+			indicator_color="red",
+		)
 		raise frappe.Redirect
 
+
 @frappe.whitelist(allow_guest=True)
 def get_appointment_settings():
-	settings = frappe.get_doc('Appointment Booking Settings')
-	settings.holiday_list = frappe.get_doc('Holiday List', settings.holiday_list)
+	settings = frappe.get_doc("Appointment Booking Settings")
+	settings.holiday_list = frappe.get_doc("Holiday List", settings.holiday_list)
 	return settings
 
+
 @frappe.whitelist(allow_guest=True)
 def get_timezones():
 	import pytz
+
 	return pytz.all_timezones
 
+
 @frappe.whitelist(allow_guest=True)
 def get_appointment_slots(date, timezone):
 	# Convert query to local timezones
-	format_string = '%Y-%m-%d %H:%M:%S'
-	query_start_time = datetime.datetime.strptime(date + ' 00:00:00', format_string)
-	query_end_time = datetime.datetime.strptime(date + ' 23:59:59', format_string)
+	format_string = "%Y-%m-%d %H:%M:%S"
+	query_start_time = datetime.datetime.strptime(date + " 00:00:00", format_string)
+	query_end_time = datetime.datetime.strptime(date + " 23:59:59", format_string)
 	query_start_time = convert_to_system_timezone(timezone, query_start_time)
 	query_end_time = convert_to_system_timezone(timezone, query_end_time)
 	now = convert_to_guest_timezone(timezone, datetime.datetime.now())
 
 	# Database queries
-	settings = frappe.get_doc('Appointment Booking Settings')
-	holiday_list = frappe.get_doc('Holiday List', settings.holiday_list)
+	settings = frappe.get_doc("Appointment Booking Settings")
+	holiday_list = frappe.get_doc("Holiday List", settings.holiday_list)
 	timeslots = get_available_slots_between(query_start_time, query_end_time, settings)
 
 	# Filter and convert timeslots
@@ -58,15 +66,15 @@
 			converted_timeslots.append(dict(time=converted_timeslot, availability=True))
 		else:
 			converted_timeslots.append(dict(time=converted_timeslot, availability=False))
-	date_required = datetime.datetime.strptime(date + ' 00:00:00', format_string).date()
+	date_required = datetime.datetime.strptime(date + " 00:00:00", format_string).date()
 	converted_timeslots = filter_timeslots(date_required, converted_timeslots)
 	return converted_timeslots
 
+
 def get_available_slots_between(query_start_time, query_end_time, settings):
 	records = _get_records(query_start_time, query_end_time, settings)
 	timeslots = []
-	appointment_duration = datetime.timedelta(
-		minutes=settings.appointment_duration)
+	appointment_duration = datetime.timedelta(minutes=settings.appointment_duration)
 	for record in records:
 		if record.day_of_week == WEEKDAYS[query_start_time.weekday()]:
 			current_time = _deltatime_to_datetime(query_start_time, record.from_time)
@@ -82,33 +90,35 @@
 
 @frappe.whitelist(allow_guest=True)
 def create_appointment(date, time, tz, contact):
-	format_string = '%Y-%m-%d %H:%M:%S'
+	format_string = "%Y-%m-%d %H:%M:%S"
 	scheduled_time = datetime.datetime.strptime(date + " " + time, format_string)
 	# Strip tzinfo from datetime objects since it's handled by the doctype
-	scheduled_time = scheduled_time.replace(tzinfo = None)
+	scheduled_time = scheduled_time.replace(tzinfo=None)
 	scheduled_time = convert_to_system_timezone(tz, scheduled_time)
-	scheduled_time = scheduled_time.replace(tzinfo = None)
+	scheduled_time = scheduled_time.replace(tzinfo=None)
 	# Create a appointment document from form
-	appointment = frappe.new_doc('Appointment')
+	appointment = frappe.new_doc("Appointment")
 	appointment.scheduled_time = scheduled_time
 	contact = json.loads(contact)
-	appointment.customer_name = contact.get('name', None)
-	appointment.customer_phone_number = contact.get('number', None)
-	appointment.customer_skype = contact.get('skype', None)
-	appointment.customer_details = contact.get('notes', None)
-	appointment.customer_email = contact.get('email', None)
-	appointment.status = 'Open'
+	appointment.customer_name = contact.get("name", None)
+	appointment.customer_phone_number = contact.get("number", None)
+	appointment.customer_skype = contact.get("skype", None)
+	appointment.customer_details = contact.get("notes", None)
+	appointment.customer_email = contact.get("email", None)
+	appointment.status = "Open"
 	appointment.insert()
 	return appointment
 
+
 # Helper Functions
 def filter_timeslots(date, timeslots):
 	filtered_timeslots = []
 	for timeslot in timeslots:
-		if(timeslot['time'].date() == date):
+		if timeslot["time"].date() == date:
 			filtered_timeslots.append(timeslot)
 	return filtered_timeslots
 
+
 def convert_to_guest_timezone(guest_tz, datetimeobject):
 	guest_tz = pytz.timezone(guest_tz)
 	local_timezone = pytz.timezone(frappe.utils.get_time_zone())
@@ -116,15 +126,18 @@
 	datetimeobject = datetimeobject.astimezone(guest_tz)
 	return datetimeobject
 
-def convert_to_system_timezone(guest_tz,datetimeobject):
+
+def convert_to_system_timezone(guest_tz, datetimeobject):
 	guest_tz = pytz.timezone(guest_tz)
 	datetimeobject = guest_tz.localize(datetimeobject)
 	system_tz = pytz.timezone(frappe.utils.get_time_zone())
 	datetimeobject = datetimeobject.astimezone(system_tz)
 	return datetimeobject
 
+
 def check_availabilty(timeslot, settings):
-	return frappe.db.count('Appointment', {'scheduled_time': timeslot}) < settings.number_of_agents
+	return frappe.db.count("Appointment", {"scheduled_time": timeslot}) < settings.number_of_agents
+
 
 def _is_holiday(date, holiday_list):
 	for holiday in holiday_list.holidays:
@@ -136,7 +149,10 @@
 def _get_records(start_time, end_time, settings):
 	records = []
 	for record in settings.availability_of_slots:
-		if record.day_of_week == WEEKDAYS[start_time.weekday()] or record.day_of_week == WEEKDAYS[end_time.weekday()]:
+		if (
+			record.day_of_week == WEEKDAYS[start_time.weekday()]
+			or record.day_of_week == WEEKDAYS[end_time.weekday()]
+		):
 			records.append(record)
 	return records
 
@@ -148,4 +164,4 @@
 
 def _datetime_to_deltatime(date_time):
 	midnight = datetime.datetime.combine(date_time.date(), datetime.time.min)
-	return (date_time-midnight)
+	return date_time - midnight
diff --git a/erpnext/www/book_appointment/verify/index.py b/erpnext/www/book_appointment/verify/index.py
index dc36f4f..1a5ba9d 100644
--- a/erpnext/www/book_appointment/verify/index.py
+++ b/erpnext/www/book_appointment/verify/index.py
@@ -8,11 +8,11 @@
 		context.success = False
 		return context
 
-	email = frappe.form_dict['email']
-	appointment_name = frappe.form_dict['appointment']
+	email = frappe.form_dict["email"]
+	appointment_name = frappe.form_dict["appointment"]
 
 	if email and appointment_name:
-		appointment = frappe.get_doc('Appointment',appointment_name)
+		appointment = frappe.get_doc("Appointment", appointment_name)
 		appointment.set_verified(email)
 		context.success = True
 		return context
diff --git a/erpnext/www/lms/__init__.py b/erpnext/www/lms/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/www/lms/__init__.py
+++ /dev/null
diff --git a/erpnext/www/lms/content.html b/erpnext/www/lms/content.html
deleted file mode 100644
index d22ef66..0000000
--- a/erpnext/www/lms/content.html
+++ /dev/null
@@ -1,246 +0,0 @@
-{% extends "templates/base.html" %}
-{% block title %}{{ content.name or 'Content Page' }}{% endblock %}
-
-{% block head_include %}
-	<style>
-		.lms-content {
-			line-height: 1.8em;
-		}
-
-		.lms-content h1 {
-			margin-top: 1em;
-		}
-
-		.lms-content h2 {
-			margin-top: 1em;
-		}
-
-		.lms-content h3 {
-			margin-top: 0.8em;
-		}
-
-		.lms-content h4 {
-			margin-top: 0.6em;
-		}
-
-		section {
-			padding: 5rem 0 5rem 0;
-		}
-		.plyr--video .plyr__control.plyr__tab-focus,
-		.plyr--video .plyr__control:hover,
-		.plyr--video .plyr__control[aria-expanded='true'] {
-			 background: #5e64ff !important;
-		}
-
-		.plyr__control--overlaid:focus,
-		.plyr__control--overlaid:hover {
-		  background: #5e64ff !important;
-		}
-
-		.plyr__menu__container .plyr__control[role=menuitemradio][aria-checked=true]::before {
-    		background: #5e64ff !important;
-		}
-
-		.plyr__menu__container
-		.plyr__control[role='menuitemradio'][aria-checked='true']::before {
-		  background: #5e64ff;
-		}
-		.plyr--full-ui input[type='range'] {
-		  color: #5e64ff !important;
-		}
-
-		.plyr__control--overlaid {
-			background: rgba(94, 100, 255, 0.8) !important;
-		}
-	</style>
-	<link rel="stylesheet" href="https://cdn.plyr.io/3.5.3/plyr.css" />
-{% endblock %}
-
-{% macro title() %}
-	<div class="mb-3">
-		<a href="/lms/course?name={{ course }}&program={{ program }}" class="text-muted">
-			{{_('Back to Course')}}
-		</a>
-	</div>
-	<div class="lms-title">
-		<h2>{{ content.name }} <span class="small text-muted">({{ position + 1 }}/{{length}})</span></h2>
-		<div class="lms-timer float-right fond-weight-bold hide"></div>
-	</div>
-{% endmacro %}
-
-{% macro navigation() %}
-		{% if previous %}
-			<a href="/lms/content?program={{ program }}&course={{ course }}&topic={{ topic }}&type={{ previous.content_type }}&content={{ previous.content }}" class='btn text-muted' style="box-shadow: none;">{{_('Previous')}}</a>
-		{% else %}
-			<a href="/lms/course?name={{ course }}&program={{ program }}" class='btn text-muted' style="box-shadow: none;">{{ _('Back to Course') }}</a>
-		{% endif %}
-
-		{% if next %}
-			<button id="nextButton" onclick="handle('/lms/content?program={{ program }}&course={{ course }}&topic={{ topic }}&type={{ next.content_type }}&content={{ next.content }}')" class='btn btn-primary' disabled="true">{{_('Next')}}</button>
-		{% else %}
-			<button id="nextButton" onclick="handle('/lms/course?name={{ course }}&program={{ program }}')" class='btn btn-primary' disabled="true">{{_('Finish Topic')}}</button>
-		{% endif %}
-{% endmacro %}
-
-{% macro video() %}
-<div class="mb-5">
-	{{ title() }}
-	<div class="text-muted">
-		{% if content.duration %}
-			{{ content.duration }} {{_('Mins')}}
-		{% endif %}
-
-		{% if content.publish_date and content.duration%}
-			-
-		{% endif %}
-
-		{% if content.publish_date %}
-			{{_('Published on')}} {{ content.publish_date.strftime('%d, %b %Y') }}
-		{% endif %}
-	</div>
-</div>
-<div id="player" data-plyr-provider="{{ content.provider|lower }}" data-plyr-embed-id="{{ content.url }}"></div>
-<div class="my-5 lms-content">
-	{{ content.description }}
-</div>
-{% endmacro %}
-
-{% macro article() %}
-<div class="mb-5">
-	{{ title() }}
-	<div class="text-muted">
-		{% if content.author or content.publish_date %}
-			{{_('Published')}}
-		{% endif %}
-		{% if content.author %}
-			{{_('by')}} {{ content.author }}
-		{% endif %}
-		{% if content.publish_date %}
-			{{_('on')}} {{ content.publish_date.strftime('%d, %b %Y') }}
-		{% endif %}
-	</div>
-</div>
-<div class="lms-content">
-	{{ content.content }}
-</div>
-{% endmacro %}
-
-{% macro quiz() %}
-<div class="mb-5">
-	{{ title() }}
-</div>
-<div id="quiz-wrapper">
-</div>
-{% endmacro %}
-
-{% block content %}
-<section class="section">
-	<div>
-		<div class='container pb-5'>
-			{% if content_type=='Video' %}
-				{{ video() }}
-			{% elif content_type=='Article'%}
-				{{ article() }}
-			{% elif content_type=='Quiz' %}
-				{{ quiz() }}
-			{% endif %}
-			<div class="pull-right" {{ 'hidden' if content_type=='Quiz'}}>
-				{{ navigation() }}
-			</div>
-		</div>
-	</div>
-</section>
-{% endblock %}
-
-{% block script %}
-	{% if content_type=='Video' %}
-		<script src="https://cdn.plyr.io/3.5.3/plyr.js"></script>
-	{% elif content_type == 'Quiz' %}
-		<script src='/assets/erpnext/js/education/lms/quiz.js'></script>
-	{% endif  %}
-	<script>
-		{% if content_type == 'Video' %}
-			const player = new Plyr('#player');
-		{% elif content_type == 'Quiz' %}
-			{% if next %}
-			const quiz_exit_button = 'Next'
-			const next_url = '/lms/content?program={{ program }}&course={{ course }}&topic={{ topic }}&type={{ next.content_type }}&content={{ next.content }}'
-			{% else %}
-			const quiz_exit_button = 'Finish Course'
-			const next_url = '/lms/course?name={{ course }}&program={{ program }}'
-			{% endif %}
-			frappe.ready(() => {
-				{% if content.is_time_bound %}
-					var duration = get_duration("{{content.duration}}")
-					var d = frappe.msgprint({
-						title: __('Important Notice'),
-						indicator: "red",
-						message: __(`This is a Time-Bound Quiz. <br><br>
-						A timer for <b>${duration}</b> will start, once you click on <b>Proceed</b>. <br><br>
-						If you fail to submit before the time is up, the Quiz will be submitted automatically.`),
-						primary_action: {
-							label: __("Proceed"),
-							action: () => {
-								create_quiz();
-								d.hide();
-        					}
-						},
-						secondary_action: {
-							action: () => {
-								d.hide();
-								window.location.href = "/lms/course?name={{ course }}&program={{ program }}";
-							},
-							label: __("Go Back"),
-						}
-					});
-				{% else %}
-					create_quiz();
-				{% endif %}
-				function create_quiz() {
-					const quiz = new Quiz(document.getElementById('quiz-wrapper'), {
-						name: '{{ content.name }}',
-						course: '{{ course }}',
-						program: '{{ program }}',
-						quiz_exit_button: quiz_exit_button,
-						next_url: next_url
-					})
-					window.quiz = quiz;
-				}
-				function get_duration(seconds){
-					var hours = append_zero(Math.floor(seconds / 3600));
-					var minutes = append_zero(Math.floor(seconds % 3600 / 60));
-					var seconds = append_zero(Math.floor(seconds % 3600 % 60));
-					return `${hours}:${minutes}:${seconds}`;
-				}
-				function append_zero(time) {
-					return time > 9 ? time : "0" + time;
-				}
-			})
-		{% endif  %}
-
-		{% if content_type != 'Quiz' %}
-
-		frappe.ready(() => {
-			next = document.getElementById('nextButton')
-			next.disabled = false;
-		})
-
-
-		function handle(url) {
-			opts = {
-				method: "erpnext.education.utils.add_activity",
-				args: {
-					course: "{{ course }}",
-					content_type: "{{ content_type }}",
-					content: "{{ content.name }}",
-					program: "{{ program }}"
-				}
-			}
-			frappe.call(opts).then(res => {
-				window.location.href = url;
-			})
-		}
-
-		{% endif %}
-	</script>
-{% endblock %}
diff --git a/erpnext/www/lms/content.py b/erpnext/www/lms/content.py
deleted file mode 100644
index b187a78..0000000
--- a/erpnext/www/lms/content.py
+++ /dev/null
@@ -1,68 +0,0 @@
-import frappe
-
-import erpnext.education.utils as utils
-
-no_cache = 1
-
-def get_context(context):
-	# Load Query Parameters
-	try:
-		program = frappe.form_dict['program']
-		content = frappe.form_dict['content']
-		content_type = frappe.form_dict['type']
-		course = frappe.form_dict['course']
-		topic = frappe.form_dict['topic']
-	except KeyError:
-		frappe.local.flags.redirect_location = '/lms'
-		raise frappe.Redirect
-
-
-	# Check if user has access to the content
-	has_program_access = utils.allowed_program_access(program)
-	has_content_access = allowed_content_access(program, content, content_type)
-
-	if frappe.session.user == "Guest" or not has_program_access or not has_content_access:
-		frappe.local.flags.redirect_location = '/lms'
-		raise frappe.Redirect
-
-
-	# Set context for content to be displayer
-	context.content = frappe.get_doc(content_type, content).as_dict()
-	context.content_type = content_type
-	context.program = program
-	context.course = course
-	context.topic = topic
-
-	topic = frappe.get_doc("Topic", topic)
-	content_list = [{'content_type':item.content_type, 'content':item.content} for item in topic.topic_content]
-
-	# Set context for progress numbers
-	context.position = content_list.index({'content': content, 'content_type': content_type})
-	context.length = len(content_list)
-
-	# Set context for navigation
-	context.previous = get_previous_content(content_list, context.position)
-	context.next = get_next_content(content_list, context.position)
-
-def get_next_content(content_list, current_index):
-	try:
-		return content_list[current_index + 1]
-	except IndexError:
-		return None
-
-def get_previous_content(content_list, current_index):
-	if current_index == 0:
-		return None
-	else:
-		return content_list[current_index - 1]
-
-def allowed_content_access(program, content, content_type):
-	contents_of_program = frappe.db.sql("""select `tabTopic Content`.content, `tabTopic Content`.content_type
-	from `tabCourse Topic`,
-		 `tabProgram Course`,
-		 `tabTopic Content`
-	where `tabCourse Topic`.parent = `tabProgram Course`.course
-			and `tabTopic Content`.parent = `tabCourse Topic`.topic
-			and `tabProgram Course`.parent = %(program)s""", {'program': program})
-
-	return (content, content_type) in contents_of_program
diff --git a/erpnext/www/lms/course.html b/erpnext/www/lms/course.html
deleted file mode 100644
index c07b940..0000000
--- a/erpnext/www/lms/course.html
+++ /dev/null
@@ -1,106 +0,0 @@
-{% extends "templates/base.html" %}
-{% block title %}{{ course.course_name }}{% endblock %}
-{% from "www/lms/macros/hero.html" import hero %}
-{% from "www/lms/macros/card.html" import null_card %}
-
-{% block head_include %}
-	<style>
-		div.card-hero-img {
-			height: 220px;
-			background-size: cover;
-			background-repeat: no-repeat;
-			background-position: center;
-			background-color: rgb(250, 251, 252);
-		}
-
-		.card-image-wrapper {
-			display: flex;
-			overflow: hidden;
-			height: 220px;
-			background-color: rgb(250, 251, 252);
-			justify-content: center;
-		}
-
-		.image-body {
-			align-self: center;
-			color: #d1d8dd;
-			font-size: 24px;
-			font-weight: 600;
-			line-height: 1;
-			padding: 20px;
-		}
-		section {
-			padding: 5rem 0 5rem 0;
-		}
-	</style>
-{% endblock %}
-
-
-{% macro card(topic) %}
-<div class="col-sm-4 mb-4 text-left">
-	<div class="card h-100">
-		{% if has_access %}
-			<a href="/lms/topic?program={{ program }}&course={{ course.name }}&topic={{ topic.name }}" class="no-decoration no-underline">
-		{% else %}
-			<a href="/login#login">
-		{% endif %}
-			{% if topic.hero_image %}
-			<div class="card-hero-img" style="background-image: url('{{ topic.hero_image }}')"></div>
-			{% else %}
-			<div class="card-image-wrapper text-center">
-				<div class="image-body"><i class="fa fa-picture-o" aria-hidden="true"></i></div>
-			</div>
-			{% endif %}
-			<div class='card-body'>
-				<h5 class='card-title'>{{ topic.topic_name }}</h5>
-				<div>
-					<ol class="list-unstyled">
-					{% for content in topic.topic_content %}
-						<li>
-							{% if has_access %}
-								<a class="text-muted" href="/lms/content?program={{ program }}&course={{ course.name }}&topic={{ topic.name }}&type={{ content.content_type }}&content={{ content.content }}">
-									{{ content.content }}
-								</a>
-							{% else %}
-								<span class="text-muted">{{ content.content }}</span>
-							{% endif %}
-						</li>
-					{% endfor %}
-					</ol>
-				</div>
-			</div>
-		{% if has_access %}
-			<div class='card-footer'>
-				{% if progress[topic.name].completed %}
-					<span class="indicator green">{{_('Completed')}}</span>
-				{% elif progress[topic.name].started %}
-					<span class="indicator orange">{{_('In Progress')}}</span>
-				{% else %}
-					<span class="indicator blue">{{_('Start')}}</span>
-				{% endif %}
-			</div>
-			</a>
-		{% else %}
-			</a>
-		{% endif %}
-	</div>
-</div>
-{% endmacro %}
-
-{% block content %}
-<section class="section">
-	{{ hero(course.course_name, course.description, has_access, {'name': 'Program', 'url': '/lms/program?program=' + program }) }}
-	<div class='container'>
-		<div class="row mt-5">
-			{% for topic in topics %}
-				{{ card(topic) }}
-			{% endfor %}
-			{% if topics %}
-				{% for n in range( (3 - (topics|length)) %3) %}
-					{{ null_card() }}
-				{% endfor %}
-			{% endif %}
-		</div>
-	</div>
-</section>
-{% endblock %}
diff --git a/erpnext/www/lms/course.py b/erpnext/www/lms/course.py
deleted file mode 100644
index 012e25c..0000000
--- a/erpnext/www/lms/course.py
+++ /dev/null
@@ -1,26 +0,0 @@
-import frappe
-
-import erpnext.education.utils as utils
-
-no_cache = 1
-
-def get_context(context):
-	try:
-		program = frappe.form_dict['program']
-		course_name = frappe.form_dict['name']
-	except KeyError:
-		frappe.local.flags.redirect_location = '/lms'
-		raise frappe.Redirect
-
-	context.education_settings = frappe.get_single("Education Settings")
-	course = frappe.get_doc('Course', course_name)
-	context.program = program
-	context.course = course
-
-	context.topics = course.get_topics()
-	context.has_access =  utils.allowed_program_access(context.program)
-	context.progress = get_topic_progress(context.topics, course, context.program)
-
-def get_topic_progress(topics, course, program):
-	progress = {topic.name: utils.get_topic_progress(topic, course.name, program) for topic in topics}
-	return progress
diff --git a/erpnext/www/lms/index.html b/erpnext/www/lms/index.html
deleted file mode 100644
index c1e9620..0000000
--- a/erpnext/www/lms/index.html
+++ /dev/null
@@ -1,69 +0,0 @@
-{% extends "templates/base.html" %}
-{% block title %}{{ education_settings.portal_title }}{% endblock %}
-{% from "www/lms/macros/card.html" import program_card %}
-{% from "www/lms/macros/card.html" import null_card %}
-
-{% block head_include %}
-	<meta name="description" content="{{ education_settings.description }}" />
-	<meta name="keywords" content="ERP Software, Cloud ERP, Open Source ERP, Accounting Software, Online ERP, Online Accounting, ERP for small business" />
-	<style>
-		div.card-hero-img {
-			height: 220px;
-			background-size: cover;
-			background-repeat: no-repeat;
-			background-position: center;
-			background-color: rgb(250, 251, 252);
-		}
-
-		.card-image-wrapper {
-			display: flex;
-			overflow: hidden;
-			height: 220px;
-			background-color: rgb(250, 251, 252);
-			justify-content: center;
-		}
-
-		.image-body {
-			align-self: center;
-			color: #d1d8dd;
-			font-size: 24px;
-			font-weight: 600;
-			line-height: 1;
-			padding: 20px;
-		}
-
-		section {
-			padding: 5rem 0 5rem 0;
-		}
-	</style>
-{% endblock %}
-
-{% block content %}
-<section class="top-section" style="padding: 6rem 0rem;">
-	<div class='container pb-5'>
-		<h1>{{ education_settings.portal_title }}</h1>
-		{% if education_settings.description %}
-			<p class='lead'>{{ education_settings.description }}</p>
-		{% endif %}
-		<p class="mt-4">
-			{% if frappe.session.user == 'Guest' %}
-				<a class="btn btn-primary btn-lg" href="/login#signup">{{_('Sign Up')}}</a>
-			{% endif %}
-		</p>
-	</div>
-	<div class='container'>
-		<div class="row mt-5">
-			{% if featured_programs %}
-				{% for program in featured_programs %}
-					{{ program_card(program.program, program.has_access) }}
-				{% endfor %}
-				{% for n in range( (3 - (featured_programs|length)) %3) %}
-					{{ null_card() }}
-				{% endfor %}
-			{% else %}
-				<p class="lead">You have not enrolled in any program. Contact your Instructor.</p>
-			{% endif %}
-		</div>
-	</div>
-</section>
-{% endblock %}
diff --git a/erpnext/www/lms/index.py b/erpnext/www/lms/index.py
deleted file mode 100644
index 035f7d9..0000000
--- a/erpnext/www/lms/index.py
+++ /dev/null
@@ -1,16 +0,0 @@
-import frappe
-
-import erpnext.education.utils as utils
-
-no_cache = 1
-
-def get_context(context):
-	context.education_settings = frappe.get_single("Education Settings")
-	if not context.education_settings.enable_lms:
-		frappe.local.flags.redirect_location = '/'
-		raise frappe.Redirect
-	context.featured_programs = get_featured_programs()
-
-
-def get_featured_programs():
-	return utils.get_portal_programs() or []
diff --git a/erpnext/www/lms/macros/__init__.py b/erpnext/www/lms/macros/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/www/lms/macros/__init__.py
+++ /dev/null
diff --git a/erpnext/www/lms/macros/card.html b/erpnext/www/lms/macros/card.html
deleted file mode 100644
index 3cbdec6..0000000
--- a/erpnext/www/lms/macros/card.html
+++ /dev/null
@@ -1,34 +0,0 @@
-{% macro program_card(program, has_access) %}
-<div class="col-sm-4 mb-4 text-left">
-	<a href="/lms/program?program={{ program.name }}" class="no-decoration no-underline">
-	<div class="card h-100">
-		{% if program.hero_image %}
-		<div class="card-hero-img" style="background-image: url('{{ program.hero_image }}')"></div>
-		{% else %}
-		<div class="card-image-wrapper text-center">
-			<div class="image-body"><i class="fa fa-picture-o" aria-hidden="true"></i></div>
-		</div>
-		{% endif %}
-		<div class='card-body'>
-			<h5 class='card-title'>{{ program.program_name }}</h5>
-			<div class="text-muted">{{ program.description[:110] + '...' if program.description else '' }}</div>
-		</div>
-		{% if has_access or program.intro_video%}
-		<div class='card-footer'>
-			{% if has_access %} <span class="indicator green">{{_('Enrolled')}}</span>
-			{% elif program.intro_video %} <span><a href="{{ program.intro_video }}" target="blank">{{_('Watch Intro')}}</a></span>
-			{% endif %}
-		</div>
-		{% endif %}
-	</div>
-	</a>
-</div>
-{% endmacro %}
-
-
-{% macro null_card() %}
-<div class="col-sm-4 mb-4 text-left">
-	<div class="h-100 d-none d-sm-block" style="border: 1px solid rgba(209,216,221,0.5);border-radius: 0.25rem;background-color: rgb(250, 251, 252);">
-	</div>
-</div>
-{% endmacro %}
diff --git a/erpnext/www/lms/macros/hero.html b/erpnext/www/lms/macros/hero.html
deleted file mode 100644
index 95ba8f7..0000000
--- a/erpnext/www/lms/macros/hero.html
+++ /dev/null
@@ -1,56 +0,0 @@
-{% macro hero(title, description, has_access, back) %}
-	<div class='container pb-5'>
-		<div class="mb-3">
-			<a href="{{ back.url }}" class="text-muted">
-				{{_('Back to')}} {{ _(back.name) }}
-			</a>
-		</div>
-		<h1>{{ title }}</h1>
-		<p class='lead' style="max-width: 100%;">{{ description or ''}}</p>
-		<p class="mt-4">
-			{% if frappe.session.user == 'Guest' %}
-			<a id="signup" class="btn btn-primary btn-lg" href="/login#signup">{{_('Sign Up')}}</a>
-			{% elif not has_access %}
-			<button id="enroll" class="btn btn-primary btn-lg" onclick="enroll()">{{_('Enroll')}}</button>
-			{% endif %}
-		</p>
-	</div>
-
-{% block script %}
-<script type="text/javascript">
-	frappe.ready(() => {
-		btn = document.getElementById('enroll');
-	})
-
-	function enroll() {
-		let params = frappe.utils.get_query_params()
-
-		let btn = document.getElementById('enroll');
-
-		let opts = {
-			method: 'erpnext.education.utils.enroll_in_program',
-			args: {
-				program_name: params.program
-			},
-			freeze: true,
-			freeze_message: __('Enrolling...')
-		}
-
-		frappe.call(opts).then(res => {
-			let success_dialog = new frappe.ui.Dialog({
-				title: __('Success'),
-				primary_action_label: __('View Program Content'),
-				primary_action: function() {
-					window.location.reload();
-				},
-				secondary_action: function() {
-					window.location.reload();
-				}
-			})
-			success_dialog.show();
-			success_dialog.set_message(__('You have successfully enrolled for the program '));
-		})
-	}
-</script>
-{% endblock %}
-{% endmacro %}
diff --git a/erpnext/www/lms/profile.html b/erpnext/www/lms/profile.html
deleted file mode 100644
index 5755dfe..0000000
--- a/erpnext/www/lms/profile.html
+++ /dev/null
@@ -1,64 +0,0 @@
-{% extends "templates/base.html" %}
-{% block title %}Profile{% endblock %}
-{% from "www/lms/macros/hero.html" import hero %}
-
-{% block head_include %}
-	<style>
-		section {
-			padding: 5rem 0 5rem 0;
-		}
-	</style>
-{% endblock %}
-
-{% macro card(program) %}
-<div class="col-sm-4 mb-4 text-left">
-	<a href="/lms/program?program={{ program.name }}" class="no-decoration no-underline">
-	<div class="card h-100">
-		<div class='card-body'>
-			<h5 class='card-title'>{{ program.program }}</h5>
-			<ul class="list-unstyled text-muted">
-				{% for course in program.progress %}
-				<li>
-					{% if course.completed %} <span class="indicator green">
-					{% elif course.started %} <span class="indicator orange">
-					{% else %} <span class="indicator blue">
-					{% endif %}
-						<a class="text-muted" href="/lms/course?name={{ course.name }}&program={{ program.name }}">{{ course.course }}</a>
-					</span>
-				</li>
-				{% endfor %}
-			</ul>
-		</div>
-		<div class='card-footer'>
-			<span class="small">{{ program.completion }}{{_('% Complete')}}</span>
-		</div>
-	</div>
-	</a>
-</div>
-{% endmacro %}
-
-{% block content %}
-<section class="section">
-	<div class='container pb-5'>
-		<div class="mb-3 row">
-			<div class="col-md-7">
-				<a href="/lms" class="text-muted">
-					{{_('Back to Home')}}
-				</a>
-			</div>
-			<div class="col-md-5 text-right">
-				<a href="/update-profile?name={{ frappe.session.user }}" target="blank" class="mt-0 text-muted">{{_('Edit Profile')}}</a>
-			</div>
-		</div>
-		<h1>{{ student.first_name }} {{ student.last_name or '' }}</h1>
-		<p class="lead" style="max-width: 100%;">{{ student.name }}</p>
-	</div>
-	<div class='container'>
-		<div class="row mt-5">
-			{% for program in progress %}
-				{{ card(program) }}
-			{% endfor %}
-		</div>
-	</div>
-</section>
-{% endblock %}
diff --git a/erpnext/www/lms/profile.py b/erpnext/www/lms/profile.py
deleted file mode 100644
index 8cd2f24..0000000
--- a/erpnext/www/lms/profile.py
+++ /dev/null
@@ -1,26 +0,0 @@
-import frappe
-
-import erpnext.education.utils as utils
-
-no_cache = 1
-
-def get_context(context):
-	if frappe.session.user == "Guest":
-		frappe.local.flags.redirect_location = '/lms'
-		raise frappe.Redirect
-
-	context.student = utils.get_current_student()
-	if not context.student:
-		context.student = frappe.get_doc('User', frappe.session.user)
-	context.progress = get_program_progress(context.student.name)
-
-def get_program_progress(student):
-	enrolled_programs = frappe.get_all("Program Enrollment", filters={'student':student}, fields=['program'])
-	student_progress = []
-	for list_item in enrolled_programs:
-		program = frappe.get_doc("Program", list_item.program)
-		progress = utils.get_program_progress(program)
-		completion = utils.get_program_completion(program)
-		student_progress.append({'program': program.program_name, 'name': program.name, 'progress':progress, 'completion': completion})
-
-	return student_progress
diff --git a/erpnext/www/lms/program.html b/erpnext/www/lms/program.html
deleted file mode 100644
index 30528c6..0000000
--- a/erpnext/www/lms/program.html
+++ /dev/null
@@ -1,87 +0,0 @@
-{% extends "templates/base.html" %}
-{% block title %}{{ program.program_name }}{% endblock %}
-{% from "www/lms/macros/hero.html" import hero %}
-{% from "www/lms/macros/card.html" import null_card %}
-
-{% block head_include %}
-	<style>
-		div.card-hero-img {
-			height: 220px;
-			background-size: cover;
-			background-repeat: no-repeat;
-			background-position: center;
-			background-color: rgb(250, 251, 252);
-		}
-
-		.card-image-wrapper {
-			display: flex;
-			overflow: hidden;
-			height: 220px;
-			background-color: rgb(250, 251, 252);
-			justify-content: center;
-		}
-
-		.image-body {
-			align-self: center;
-			color: #d1d8dd;
-			font-size: 24px;
-			font-weight: 600;
-			line-height: 1;
-			padding: 20px;
-		}
-
-		section {
-			padding: 5rem 0 5rem 0;
-		}
-	</style>
-{% endblock %}
-
-
-{% macro card(course) %}
-<div class="col-sm-4 mb-4 text-left">
-	<a href="/lms/course?name={{ course.name }}&program={{ program.name }}" class="no-decoration no-underline">
-	<div class="card h-100">
-		{% if course.hero_image %}
-		<div class="card-hero-img" style="background-image: url('{{ course.hero_image }}')"></div>
-		{% else %}
-		<div class="card-image-wrapper text-center">
-			<div class="image-body"><i class="fa fa-picture-o" aria-hidden="true"></i></div>
-		</div>
-		{% endif %}
-		<div class='card-body'>
-			<h5 class='card-title'>{{ course.course_name }}</h5>
-			<div class="text-muted">{{ course.description[:110] + '...' if course.description else '' }}</div>
-		</div>
-		{% if has_access and progress[course.name] %}
-		<div class='card-footer'>
-			{% if progress[course.name].completed %}
-			<span class="indicator green">{{_('Completed')}}</span>
-			{% elif progress[course.name].started %}
-			<span class="indicator orange">{{_('In Progress')}}</span>
-			{% else %}
-			<span class="indicator blue">{{_('Start')}}</span>
-			{% endif %}
-		</div>
-		{% endif %}
-	</div>
-	</a>
-</div>
-{% endmacro %}
-
-{% block content %}
-<section class="section">
-	{{ hero(program.program_name, program.description, has_access, {'name': 'Home', 'url': '/lms'}) }}
-	<div class='container'>
-		<div class="row mt-5">
-			{% for course in courses %}
-				{{ card(course) }}
-			{% endfor %}
-			{% if courses %}
-				{% for n in range( (3 - (courses|length)) %3) %}
-					{{ null_card() }}
-				{% endfor %}
-			{% endif %}
-		</div>
-	</div>
-</section>
-{% endblock %}
diff --git a/erpnext/www/lms/program.py b/erpnext/www/lms/program.py
deleted file mode 100644
index db2653a..0000000
--- a/erpnext/www/lms/program.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import frappe
-from frappe import _
-
-import erpnext.education.utils as utils
-
-no_cache = 1
-
-def get_context(context):
-	try:
-		program = frappe.form_dict['program']
-	except KeyError:
-		frappe.local.flags.redirect_location = '/lms'
-		raise frappe.Redirect
-
-	context.education_settings = frappe.get_single("Education Settings")
-	context.program = get_program(program)
-	context.courses = [frappe.get_doc("Course", course.course) for course in context.program.courses]
-	context.has_access = utils.allowed_program_access(program)
-	context.progress = get_course_progress(context.courses, context.program)
-
-def get_program(program_name):
-	try:
-		return frappe.get_doc('Program', program_name)
-	except frappe.DoesNotExistError:
-		frappe.throw(_("Program {0} does not exist.").format(program_name))
-
-def get_course_progress(courses, program):
-	progress = {course.name: utils.get_course_progress(course, program) for course in courses}
-	return progress or {}
diff --git a/erpnext/www/lms/topic.html b/erpnext/www/lms/topic.html
deleted file mode 100644
index dc69599..0000000
--- a/erpnext/www/lms/topic.html
+++ /dev/null
@@ -1,58 +0,0 @@
-{% extends "templates/base.html" %}
-{% block title %}{{ topic.name }}{% endblock %}
-{% from "www/lms/macros/hero.html" import hero %}
-{% from "www/lms/macros/card.html" import null_card %}
-
-{% block head_include %}
-	<style>
-		section {
-			padding: 5rem 0 5rem 0;
-		}
-	</style>
-{% endblock %}
-
-
-{% macro card(content, index, length) %}
-<div class="col-sm-4 mb-4 text-left">
-	<a href="/lms/content?program={{ program }}&course={{ course }}&topic={{ topic.name }}&type={{ content.content_type }}&content={{ content.content.name }}" class="no-decoration no-underline">
-	<div class="card h-100">
-		<div class='card-body'>
-			<div class="text-muted">{{ content.content_type or '' }}</div>
-			<h5 class='card-title'>{{ content.content.name }}</h5>
-		</div>
-		{% if has_access %}
-		<div class='card-footer'>
-			{% if content.content_type == 'Quiz' %}
-				{% if content.result == 'Fail' %} <span class="indicator red">{{_('Fail')}} <span class="text-muted">({{ content.score }}/100)</span></span>
-				{% elif content.result == 'Pass' %} <span class="indicator green">{{_('Pass')}} <span class="text-muted">({{ content.score }}/100)</span>
-				{% else %} <span class="indicator blue">{{_('Start')}}</span>
-				{% endif %}
-			{% else %}
-				{% if content.completed %} <span class="indicator green">{{_('Completed')}}</span>
-				{% else %} <span class="indicator blue">{{_('Start')}}</span>
-				{% endif %}
-			{% endif %}
-		</div>
-		{% endif %}
-	</div>
-	</a>
-</div>
-{% endmacro %}
-
-{% block content %}
-<section class="section">
-	{{ hero(topic.topic_name, topic.description, has_access, {'name': 'Course', 'url': '/lms/course?name=' + course +'&program=' + program}) }}
-	<div class='container'>
-		<div class="row mt-5">
-			{% for content in contents %}
-				{{ card(content, loop.index, topic.contents|length) }}
-			{% endfor %}
-			{% if contents %}
-				{% for n in range( (3 - (contents|length)) %3) %}
-					{{ null_card() }}
-				{% endfor %}
-			{% endif %}
-		</div>
-	</div>
-</section>
-{% endblock %}
diff --git a/erpnext/www/lms/topic.py b/erpnext/www/lms/topic.py
deleted file mode 100644
index 17fc8f7..0000000
--- a/erpnext/www/lms/topic.py
+++ /dev/null
@@ -1,45 +0,0 @@
-import frappe
-
-import erpnext.education.utils as utils
-
-no_cache = 1
-
-def get_context(context):
-	try:
-		course = frappe.form_dict['course']
-		program = frappe.form_dict['program']
-		topic = frappe.form_dict['topic']
-	except KeyError:
-		frappe.local.flags.redirect_location = '/lms'
-		raise frappe.Redirect
-
-	context.program = program
-	context.course = course
-	context.topic = frappe.get_doc("Topic", topic)
-	context.contents = get_contents(context.topic, course, program)
-	context.has_access =  utils.allowed_program_access(program)
-
-def get_contents(topic, course, program):
-	student = utils.get_current_student()
-	if student:
-		course_enrollment = utils.get_or_create_course_enrollment(course, program)
-	contents = topic.get_contents()
-	progress = []
-	if contents:
-		for content in contents:
-			if content.doctype in ('Article', 'Video'):
-				if student:
-					status = utils.check_content_completion(content.name, content.doctype, course_enrollment.name)
-				else:
-					status = True
-				progress.append({'content': content, 'content_type': content.doctype, 'completed': status})
-			elif content.doctype == 'Quiz':
-				if student:
-					status, score, result, time_taken = utils.check_quiz_completion(content, course_enrollment.name)
-				else:
-					status = False
-					score = None
-					result = None
-				progress.append({'content': content, 'content_type': content.doctype, 'completed': status, 'score': score, 'result': result})
-
-	return progress
diff --git a/erpnext/www/payment_setup_certification.py b/erpnext/www/payment_setup_certification.py
index c65cddb..5d62d60 100644
--- a/erpnext/www/payment_setup_certification.py
+++ b/erpnext/www/payment_setup_certification.py
@@ -2,18 +2,23 @@
 
 no_cache = 1
 
+
 def get_context(context):
-	if frappe.session.user != 'Guest':
+	if frappe.session.user != "Guest":
 		context.all_certifications = get_all_certifications_of_a_member()
 		context.show_sidebar = True
 
 
 def get_all_certifications_of_a_member():
-	'''Returns all certifications'''
+	"""Returns all certifications"""
 	all_certifications = []
-	all_certifications = frappe.db.sql(""" select cc.name,cc.from_date,cc.to_date,ca.amount,ca.currency
+	all_certifications = frappe.db.sql(
+		""" select cc.name,cc.from_date,cc.to_date,ca.amount,ca.currency
 		from `tabCertified Consultant` cc
 		inner join `tabCertification Application` ca
 		on cc.certification_application = ca.name
-		where paid = 1 and email = %(user)s order by cc.to_date desc""" ,{'user': frappe.session.user},as_dict=True)
+		where paid = 1 and email = %(user)s order by cc.to_date desc""",
+		{"user": frappe.session.user},
+		as_dict=True,
+	)
 	return all_certifications
diff --git a/erpnext/www/shop-by-category/index.py b/erpnext/www/shop-by-category/index.py
index 09f97ba..8a92418 100644
--- a/erpnext/www/shop-by-category/index.py
+++ b/erpnext/www/shop-by-category/index.py
@@ -3,6 +3,7 @@
 
 sitemap = 1
 
+
 def get_context(context):
 	context.body_class = "product-page"
 
@@ -18,13 +19,9 @@
 
 	context.no_cache = 1
 
+
 def get_slideshow(slideshow):
-	values = {
-		'show_indicators': 1,
-		'show_controls': 1,
-		'rounded': 1,
-		'slider_name': "Categories"
-	}
+	values = {"show_indicators": 1, "show_controls": 1, "rounded": 1, "slider_name": "Categories"}
 	slideshow = frappe.get_cached_doc("Website Slideshow", slideshow)
 	slides = slideshow.get({"doctype": "Website Slideshow Item"})
 	for index, slide in enumerate(slides, start=1):
@@ -37,9 +34,10 @@
 
 	return values
 
+
 def get_tabs(categories):
 	tab_values = {
-		'title': _("Shop by Category"),
+		"title": _("Shop by Category"),
 	}
 
 	categorical_data = get_category_records(categories)
@@ -48,21 +46,19 @@
 		# pre-render cards for each tab
 		tab_values[f"tab_{index + 1}_content"] = frappe.render_template(
 			"erpnext/www/shop-by-category/category_card_section.html",
-			{"data": categorical_data[tab], "type": tab}
+			{"data": categorical_data[tab], "type": tab},
 		)
 	return tab_values
 
+
 def get_category_records(categories):
 	categorical_data = {}
 	for category in categories:
 		if category == "item_group":
 			categorical_data["item_group"] = frappe.db.get_all(
 				"Item Group",
-				filters={
-					"parent_item_group": "All Item Groups",
-					"show_in_website": 1
-				},
-				fields=["name", "parent_item_group", "is_group", "image", "route"]
+				filters={"parent_item_group": "All Item Groups", "show_in_website": 1},
+				fields=["name", "parent_item_group", "is_group", "image", "route"],
 			)
 		else:
 			doctype = frappe.unscrub(category)
@@ -73,4 +69,3 @@
 			categorical_data[category] = frappe.db.get_all(doctype, fields=fields)
 
 	return categorical_data
-
diff --git a/erpnext/www/support/index.py b/erpnext/www/support/index.py
index 408ddf4..aa00e92 100644
--- a/erpnext/www/support/index.py
+++ b/erpnext/www/support/index.py
@@ -3,7 +3,7 @@
 
 def get_context(context):
 	context.no_cache = 1
-	context.align_greeting = ''
+	context.align_greeting = ""
 	setting = frappe.get_doc("Support Settings")
 
 	context.greeting_title = setting.greeting_title
@@ -16,18 +16,22 @@
 		if favorite_articles:
 			for article in favorite_articles:
 				name_list.append(article.name)
-		for record in (frappe.get_all("Help Article",
+		for record in frappe.get_all(
+			"Help Article",
 			fields=["title", "content", "route", "category"],
-			filters={"name": ['not in', tuple(name_list)], "published": 1},
-			order_by="creation desc", limit=(6-len(favorite_articles)))):
+			filters={"name": ["not in", tuple(name_list)], "published": 1},
+			order_by="creation desc",
+			limit=(6 - len(favorite_articles)),
+		):
 			favorite_articles.append(record)
 
 	context.favorite_article_list = get_favorite_articles(favorite_articles)
 	context.help_article_list = get_help_article_list()
 
+
 def get_favorite_articles_by_page_view():
 	return frappe.db.sql(
-			"""
+		"""
 			SELECT
 				t1.name as name,
 				t1.title as title,
@@ -43,32 +47,42 @@
 			GROUP BY route
 			ORDER BY count DESC
 			LIMIT 6;
-			""", as_dict=True)
+			""",
+		as_dict=True,
+	)
+
 
 def get_favorite_articles(favorite_articles):
-	favorite_article_list=[]
+	favorite_article_list = []
 	for article in favorite_articles:
 		description = frappe.utils.strip_html(article.content)
 		if len(description) > 120:
-			description = description[:120] + '...'
+			description = description[:120] + "..."
 		favorite_article_dict = {
-			'title': article.title,
-			'description': description,
-			'route': article.route,
-			'category': article.category,
+			"title": article.title,
+			"description": description,
+			"route": article.route,
+			"category": article.category,
 		}
 		favorite_article_list.append(favorite_article_dict)
 	return favorite_article_list
 
+
 def get_help_article_list():
-	help_article_list=[]
+	help_article_list = []
 	category_list = frappe.get_all("Help Category", fields="name")
 	for category in category_list:
-		help_articles = frappe.get_all("Help Article", fields="*", filters={"category": category.name, "published": 1}, order_by="modified desc", limit=5)
+		help_articles = frappe.get_all(
+			"Help Article",
+			fields="*",
+			filters={"category": category.name, "published": 1},
+			order_by="modified desc",
+			limit=5,
+		)
 		if help_articles:
 			help_aricles_per_caetgory = {
-				'category': category,
-				'articles': help_articles,
+				"category": category,
+				"articles": help_articles,
 			}
 			help_article_list.append(help_aricles_per_caetgory)
 	return help_article_list
diff --git a/requirements.txt b/requirements.txt
index 39591ca..83e5375 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,13 +1,11 @@
 # frappe   # https://github.com/frappe/frappe is installed during bench-init
 gocardless-pro~=1.22.0
 googlemaps
-pandas~=1.1.5
 plaid-python~=7.2.1
 pycountry~=20.7.3
-PyGithub~=1.55
 python-stdnum~=1.16
 python-youtube~=0.8.0
 taxjar~=1.9.2
 tweepy~=3.10.0
 Unidecode~=1.2.0
-redisearch==2.0.0
\ No newline at end of file
+redisearch~=2.1.0
diff --git a/setup.py b/setup.py
index 8140700..1faff04 100644
--- a/setup.py
+++ b/setup.py
@@ -2,23 +2,22 @@
 import re, ast
 
 # get version from __version__ variable in erpnext/__init__.py
-_version_re = re.compile(r'__version__\s+=\s+(.*)')
+_version_re = re.compile(r"__version__\s+=\s+(.*)")
 
-with open('requirements.txt') as f:
-	install_requires = f.read().strip().split('\n')
+with open("requirements.txt") as f:
+	install_requires = f.read().strip().split("\n")
 
-with open('erpnext/__init__.py', 'rb') as f:
-	version = str(ast.literal_eval(_version_re.search(
-		f.read().decode('utf-8')).group(1)))
+with open("erpnext/__init__.py", "rb") as f:
+	version = str(ast.literal_eval(_version_re.search(f.read().decode("utf-8")).group(1)))
 
 setup(
-	name='erpnext',
+	name="erpnext",
 	version=version,
-	description='Open Source ERP',
-	author='Frappe Technologies',
-	author_email='info@erpnext.com',
+	description="Open Source ERP",
+	author="Frappe Technologies",
+	author_email="info@erpnext.com",
 	packages=find_packages(),
 	zip_safe=False,
 	include_package_data=True,
-	install_requires=install_requires
+	install_requires=install_requires,
 )
diff --git a/sponsors.md b/sponsors.md
index 125b358..57adc8d 100644
--- a/sponsors.md
+++ b/sponsors.md
@@ -61,5 +61,13 @@
 				Bulk edit via export-import in Bank Reconciliation <a href="https://github.com/frappe/erpnext/issues/1938">#4356</a>
 			</td>
 		</tr>
+		<tr>
+			<td style="width: 30%">
+				<a href="https://www.sapconinstruments.com/">Sapcon Instruments Pvt Ltd</a>
+			</td>
+			<td>
+				Level wise BOM Cost Updation and Performance Enhancement <a href="https://github.com/frappe/erpnext/pull/31072">#31072</a>
+			</td>
+		</tr>
 	</tbody>
 </table>